diff -Nru a/CREDITS b/CREDITS --- a/CREDITS Tue Feb 19 18:08:58 2002 +++ b/CREDITS Tue Feb 19 18:08:58 2002 @@ -1657,6 +1657,13 @@ S: San Antonio, Texas 78269-1886 S: USA +N: Denis O. Kropp +E: dok@directfb.org +D: NeoMagic framebuffer driver +S: Badensche Str. 46 +S: 10715 Berlin +S: Germany + N: Andrzej M. Krzysztofowicz E: ankry@mif.pg.gda.pl D: Some 8-bit XT disk driver and devfs hacking diff -Nru a/Documentation/cachetlb.txt b/Documentation/cachetlb.txt --- a/Documentation/cachetlb.txt Tue Feb 19 18:08:59 2002 +++ b/Documentation/cachetlb.txt Tue Feb 19 18:08:59 2002 @@ -341,6 +341,17 @@ If the icache does not snoop stores then this routine will need to flush it. + void flush_icache_user_range(struct vm_area_struct *vma, + struct page *page, unsigned long addr, int len) + This is called when the kernel stores into addresses that are + part of the address space of a user process (which may be some + other process than the current process). The addr argument + gives the virtual address in that process's address space, + page is the page which is being modified, and len indicates + how many bytes have been modified. The modified region must + not cross a page boundary. Currently this is only called from + kernel/ptrace.c. + void flush_icache_page(struct vm_area_struct *vma, struct page *page) All the functionality of flush_icache_page can be implemented in flush_dcache_page and update_mmu_cache. In 2.5 the hope is to diff -Nru a/Documentation/cdrom/sbpcd b/Documentation/cdrom/sbpcd --- a/Documentation/cdrom/sbpcd Tue Feb 19 18:08:58 2002 +++ b/Documentation/cdrom/sbpcd Tue Feb 19 18:08:58 2002 @@ -613,8 +613,8 @@ printf("READ d READ RAW w READ AUDIO A\n"); printf("MS-INFO M TOC T START S\n"); printf("SET EJECTSW X DEVICE D DEBUG Y\n"); - printf("AUDIO_BUFSIZ Z RESET R BLKRASET B\n"); - printf("SET VOLUME v GET VOLUME V\n"); + printf("AUDIO_BUFSIZ Z RESET R SET VOLUME v\n"); + printf("GET VOLUME V\n"); } /* @@ -881,12 +881,6 @@ case 'R': rc=ioctl(drive,CDROMRESET); if (rc<0) printf("CDROMRESET: rc=%d.\n",rc); - break; - case 'B': /* set the driver's (?) read ahead value */ - printf("enter read-ahead size: ? "); - scanf("%d",&i); - rc=ioctl(drive,BLKRASET,i); - if (rc<0) printf("BLKRASET: rc=%d.\n",rc); break; #ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/ case 'd': diff -Nru a/Documentation/fb/matroxfb.txt b/Documentation/fb/matroxfb.txt --- a/Documentation/fb/matroxfb.txt Tue Feb 19 18:08:58 2002 +++ b/Documentation/fb/matroxfb.txt Tue Feb 19 18:08:58 2002 @@ -10,7 +10,7 @@ * It provides a nice large console (128 cols + 48 lines with 1024x768) without using tiny, unreadable fonts. - * You can run XF68_FBDev on top of /dev/fb0 + * You can run XF{68,86}_FBDev or XFree86 fbdev driver on top of /dev/fb0 * Most important: boot logo :-) Disadvantages: @@ -27,9 +27,6 @@ If you want, for example, enable a resolution of 1280x1024x24bpp you should pass to the kernel this command line: "video=matrox:vesa:0x1BB". -Note that the same line, if 'appended' as a lilo parameter in lilo.conf will -read "video=matrox:vesa:443" because lilo pass integer parameters as decimal -numbers to the kernel. You should compile in both vgacon (to boot if you remove you Matrox from box) and matroxfb (for graphics mode). You should not compile-in vesafb @@ -82,13 +79,16 @@ X11 === -XF68_FBDev should work just fine, but it is non-accelerated. On non-intel +XF{68,86}_FBDev should work just fine, but it is non-accelerated. On non-intel architectures there are some glitches for 24bpp videomodes. 8, 16 and 32bpp works fine. Running another (accelerated) X-Server like XF86_SVGA works too. But (at least) XFree servers have big troubles in multihead configurations (even on first -head, not even talking about second). +head, not even talking about second). Running XFree86 4.x accelerated mga +driver is possible, but you must not enable DRI - if you do, resolution and +color depth of your X desktop must match resolution and color depths of your +virtual consoles, otherwise X will corrupt accelerator settings. SVGALib diff -Nru a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking --- a/Documentation/filesystems/Locking Tue Feb 19 18:08:59 2002 +++ b/Documentation/filesystems/Locking Tue Feb 19 18:08:59 2002 @@ -45,37 +45,48 @@ int (*revalidate) (struct dentry *); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct dentry *, struct iattr *); + int (*setxattr) (struct dentry *, const char *, void *, size_t, int); + ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); + ssize_t (*listxattr) (struct dentry *, char *, size_t); + int (*removexattr) (struct dentry *, const char *); locking rules: all may block - BKL i_sem(inode) i_zombie(inode) -lookup: yes yes no -create: yes yes yes -link: yes yes yes -mknod: yes yes yes -mkdir: yes yes yes -unlink: yes yes yes -rmdir: yes yes yes (see below) -rename: yes yes (both) yes (both) (see below) -readlink: no no no -follow_link: no no no -truncate: yes yes no (see below) -setattr: yes if ATTR_SIZE no -permssion: yes no no -getattr: (see below) -revalidate: no (see below) - Additionally, ->rmdir() has i_zombie on victim and so does ->rename() -in case when target exists and is a directory. - ->rename() on directories has (per-superblock) ->s_vfs_rename_sem. + BKL i_sem(inode) +lookup: no yes +create: no yes +link: no yes (both) +mknod: no yes +symlink: no yes +mkdir: no yes +unlink: no yes (both) +rmdir: no yes (both) (see below) +rename: no yes (all) (see below) +readlink: no no +follow_link: no no +truncate: no yes (see below) +setattr: yes if ATTR_SIZE +permission: yes no +getattr: (see below) +revalidate: no (see below) +setxattr: yes no +getxattr: yes no +listxattr: yes no +removexattr: yes no + Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_sem on +victim. + cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem. ->revalidate(), it may be called both with and without the i_sem -on dentry->d_inode. VFS never calls it with i_zombie on dentry->d_inode, -but watch for other methods directly calling this one... +on dentry->d_inode. ->truncate() is never called directly - it's a callback, not a method. It's called by vmtruncate() - library function normally used by ->setattr(). Locking information above applies to that call (i.e. is inherited from ->setattr() - vmtruncate() is used when ATTR_SIZE had been passed). ->getattr() is currently unused. + +See Documentation/filesystems/directory-locking for more detailed discussion +of the locking scheme for directory operations. --------------------------- super_operations --------------------------- prototypes: diff -Nru a/Documentation/filesystems/directory-locking b/Documentation/filesystems/directory-locking --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/filesystems/directory-locking Tue Feb 19 18:09:01 2002 @@ -0,0 +1,113 @@ + Locking scheme used for directory operations is based on two +kinds of locks - per-inode (->i_sem) and per-filesystem (->s_vfs_rename_sem). + + For our purposes all operations fall in 5 classes: + +1) read access. Locking rules: caller locks directory we are accessing. + +2) object creation. Locking rules: same as above. + +3) object removal. Locking rules: caller locks parent, finds victim, +locks victim and calls the method. + +4) rename() that is _not_ cross-directory. Locking rules: caller locks +the parent, finds source and target, if target already exists - locks it +and then calls the method. + +5) link creation. Locking rules: + * lock parent + * check that source is not a directory + * lock source + * call the method. + +6) cross-directory rename. The trickiest in the whole bunch. Locking +rules: + * lock the filesystem + * lock parents in "ancestors first" order. + * find source and target. + * if old parent is equal to or is a descendent of target + fail with -ENOTEMPTY + * if new parent is equal to or is a descendent of source + fail with -ELOOP + * if target exists - lock it. + * call the method. + + +The rules above obviously guarantee that all directories that are going to be +read, modified or removed by method will be locked by caller. + + +If no directory is its own ancestor, the scheme above is deadlock-free. +Proof: + + First of all, at any moment we have a partial ordering of the +objects - A < B iff A is an ancestor of B. + + That ordering can change. However, the following is true: + +(1) if object removal or non-cross-directory rename holds lock on A and + attempts to acquire lock on B, A will remain the parent of B until we + acquire the lock on B. (Proof: only cross-directory rename can change + the parent of object and it would have to lock the parent). + +(2) if cross-directory rename holds the lock on filesystem, order will not + change until rename acquires all locks. (Proof: other cross-directory + renames will be blocked on filesystem lock and we don't start changing + the order until we had acquired all locks). + +(3) any operation holds at most one lock on non-directory object and + that lock is acquired after all other locks. (Proof: see descriptions + of operations). + + Now consider the minimal deadlock. Each process is blocked on +attempt to acquire some lock and already holds at least one lock. Let's +consider the set of contended locks. First of all, filesystem lock is +not contended, since any process blocked on it is not holding any locks. +Thus all processes are blocked on ->i_sem. + + Non-directory objects are not contended due to (3). Thus link +creation can't be a part of deadlock - it can't be blocked on source +and it means that it doesn't hold any locks. + + Any contended object is either held by cross-directory rename or +has a child that is also contended. Indeed, suppose that it is held by +operation other than cross-directory rename. Then the lock this operation +is blocked on belongs to child of that object due to (1). + + It means that one of the operations is cross-directory rename. +Otherwise the set of contended objects would be infinite - each of them +would have a contended child and we had assumed that no object is its +own descendent. Moreover, there is exactly one cross-directory rename +(see above). + + Consider the object blocking the cross-directory rename. One of +its descendents is locked by cross-directory rename (otherwise we would again +have an infinite set of of contended objects). But that means that means +that cross-directory rename is taking locks out of order. Due to (2) the +order hadn't changed since we had acquired filesystem lock. But locking +rules for cross-directory rename guarantee that we do not try to acquire +lock on descendent before the lock on ancestor. Contradiction. I.e. +deadlock is impossible. Q.E.D. + + + These operations are guaranteed to avoid loop creation. Indeed, +the only operation that could introduce loops is cross-directory rename. +Since the only new (parent, child) pair added by rename() is (new parent, +source), such loop would have to contain these objects and the rest of it +would have to exist before rename(). I.e. at the moment of loop creation +rename() responsible for that would be holding filesystem lock and new parent +would have to be equal to or a descendent of source. But that means that +new parent had been equal to or a descendent of source since the moment when +we had acquired filesystem lock and rename() would fail with -ELOOP in that +case. + + While this locking scheme works for arbitrary DAGs, it relies on +ability to check that directory is a descendent of another object. Current +implementation assumes that directory graph is a tree. This assumption is +also preserved by all operations (cross-directory rename on a tree that would +not introduce a cycle will leave it a tree and link() fails for directories). + + Notice that "directory" in the above == "anything that might have +children", so if we are going to introduce hybrid objects we will need +either to make sure that link(2) doesn't work for them or to make changes +in is_subdir() that would make it work even in presense of such beasts. diff -Nru a/Documentation/filesystems/porting b/Documentation/filesystems/porting --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/filesystems/porting Tue Feb 19 18:09:00 2002 @@ -0,0 +1,100 @@ +Changes since 2.5.0: + +--- +[recommeneded] + +New helpers: sb_bread(), sb_getblk(), sb_get_hash_table(), set_bh(), + sb_set_blocksize() and sb_min_blocksize(). + +Use them. + +--- +[recommeneded] + +New methods: ->alloc_inode() and ->destroy_inode(). + +Remove inode->u.foo_inode_i +Declare + struct foo_inode_info { + /* fs-private stuff */ + struct inode vfs_inode; + }; + static inline struct foo_inode_info *FOO_I(struct inode *inode) + { + return list_entry(inode, struct foo_inode_info, vfs_inode); + } + +Use FOO_I(inode) instead of &inode->u.foo_inode_i; + +Add foo_alloc_inode() and foo_destory_inode() - the former should allocate +foo_inode_info and return the address of ->vfs_inode, the latter should free +FOO_I(inode) (see in-tree filesystems for examples). + +Make them ->alloc_inode and ->destroy_inode in your super_operations. + +Keep in mind that now you need explicit initialization of private data - +typically in ->read_inode() and after getting an inode from new_inode(). + +At some point that will become mandatory. + +--- +[mandatory] + +Change of file_system_type method (->read_super to ->get_sb) + +->read_super() is no more. Ditto for DECLARE_FSTYPE and DECLARE_FSTYPE_DEV. + +Turn your foo_read_super() into a function that would return 0 in case of +success and negative number in case of error (-EINVAL unless you have more +informative error value to report). Call it foo_fill_super(). Now declare + +struct super_block foo_get_sb(struct file_system_type *fs_type, + int flags, char *dev_name, void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, ext2_fill_super); +} + +(or similar with s/bdev/nodev/ or s/bdev/single/, depending on the kind of +filesystem). + +Replace DECLARE_FSTYPE... with explicit initializer and have ->get_sb set as +foo_get_sb. + +--- +[mandatory] + +Locking change: ->s_vfs_rename_sem is taken only by cross-directory renames. +Most likely there is no need to change anything, but if you relied on +global exclusion between renames for some internal purpose - you need to +change your internal locking. Otherwise exclusion warranties remain the +same (i.e. parents are victim are locked, etc.). + +--- +[informational] + +Now we have the exclusion between ->lookup() and directory removal (by +->rmdir() and ->rename()). If you used to need that exclusion and do +it by internal locking (most of filesystems couldn't care less) - you +can relax your locking. + +--- +[mandatory] + +->lookup(), ->truncate(), ->create(), ->unlink(), ->mknod(), ->mkdir(), +->rmdir(), ->link(), ->symlink() and ->rename() are called without BKL now. +Grab it on the entry, drop upon return - that will guarantee the same +locking you used to have. If your method or its parts do not need BKL - +better yet, now you can shift lock_kernel() / unlock_kernel() so that +they would protect exactly what needs to be protected. + +--- +[informational] + +check for ->link() target not being a directory is done by callers. Feel +free to drop it... + +--- +[informational] + +->link() callers hold ->i_sem on the object we are linking to. Some of your +problems might be over... diff -Nru a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt --- a/Documentation/usb/error-codes.txt Tue Feb 19 18:08:57 2002 +++ b/Documentation/usb/error-codes.txt Tue Feb 19 18:08:57 2002 @@ -1,8 +1,11 @@ -Revised: 2001-Dec-06. +Revised: 2002-Feb-09. This is the documentation of (hopefully) all possible error codes (and -their interpretation) that can be returned from the host controller drivers -and from usbcore. +their interpretation) that can be returned from usbcore. + +Some of them are returned by the Host Controller Drivers (HCDs), which +device drivers only see through usbcore. As a rule, all the HCDs should +behave the same except for transfer speed dependent behaviors. ************************************************************************** @@ -19,9 +22,9 @@ -ENODEV specified USB-device or bus doesn't exist --ENXIO a control or interrupt URB is already queued to this endpoint; or - a bulk URB is already queued to this endpoint and - USB_QUEUE_BULK wasn't used (UHCI HCDs only) +-ENXIO a control or interrupt URB is already queued to this endpoint; + or (UHCI only) a bulk URB is already queued to this endpoint + and USB_QUEUE_BULK wasn't used -EINVAL a) Invalid transfer type specified (or not supported) b) Invalid interrupt interval (0<=n<256) @@ -34,7 +37,8 @@ -EFBIG too much ISO frames requested (currently uhci>900) --EPIPE specified pipe-handle is already stalled +-EPIPE Specified endpoint is stalled. For non-control endpoints, + reset this status with usb_clear_halt(). -EMSGSIZE endpoint message size is zero, do interface/alternate setting @@ -50,19 +54,29 @@ * or in iso_frame_desc[n].status (for ISO) * ************************************************************************** +USB device drivers may only test urb status values in completion handlers. +This is because otherwise there would be a race between HCDs updating +these values on one CPU, and device drivers testing them on another CPU. + +A transfer's actual_length may be positive even when an error has been +reported. That's because transfers often involve several packets, so that +one or more packets could finish before an error stops further endpoint I/O. + + 0 Transfer completed successfully --ENOENT URB was canceled by usb_unlink_urb +-ENOENT URB was synchronously unlinked by usb_unlink_urb -EINPROGRESS URB still pending, no results yet - (actually no error until now;-) + (That is, if drivers see this it's a bug.) --EPROTO a) bitstuff error +-EPROTO (*) a) bitstuff error b) unknown USB error --EILSEQ CRC mismatch +-EILSEQ (*) CRC mismatch --EPIPE endpoint stalled +-EPIPE Endpoint stalled. For non-control endpoints, + reset this status with usb_clear_halt(). -ECOMM During an IN transfer, the host controller received data from an endpoint faster than it @@ -72,24 +86,35 @@ could not retrieve data from system memory fast enough to keep up with the USB data rate --EOVERFLOW The amount of data returned by the endpoint was +-EOVERFLOW (*) The amount of data returned by the endpoint was greater than either the max packet size of the endpoint or the remaining buffer size. "Babble". -EREMOTEIO The endpoint returned less than max packet size and that amount did not fill the specified buffer --ETIMEDOUT transfer timed out, NAK + (and USB_DISBLE_SPD was not set in transfer_flags) --ENODEV device was removed +-ETIMEDOUT transfer timed out, NAK --EREMOTEIO short packet detected +-ENODEV Device was removed. Often preceded by a burst of + other errors, since the hub driver does't detect + device removal events immediately. -EXDEV ISO transfer only partially completed look at individual frame status for details -EINVAL ISO madness, if this happens: Log off and go home --ECONNRESET the URB is being unlinked asynchronously +-ECONNRESET URB was asynchronously unlinked by usb_unlink_urb + +-ESHUTDOWN The host controller has been disabled due to some + problem that could not be worked around. + + +(*) Error codes like -EPROTO, -EILSEQ and -EOVERFLOW normally indicate +hardware problems such as bad devices (including firmware) or cables. + + ************************************************************************** * Error codes returned by usbcore-functions * @@ -100,4 +125,6 @@ -EINVAL error during registering new driver usb_get_*/usb_set_*(): +usb_control_msg(): +usb_bulk_msg(): All USB errors (submit/status) can occur diff -Nru a/Documentation/x86_64/mm.txt b/Documentation/x86_64/mm.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/x86_64/mm.txt Tue Feb 19 18:09:00 2002 @@ -0,0 +1,148 @@ +The paging design used on the x86-64 linux kernel port in 2.4.x provides: + +o per process virtual address space limit of 512 Gigabytes +o top of userspace stack located at address 0x0000007fffffffff +o PAGE_OFFSET = 0xffff800000000000 +o start of the kernel = 0xffffffff800000000 +o global RAM per system 2^64-PAGE_OFFSET-sizeof(kernel) = 128 Terabytes - 2 Gigabytes +o no need of any common code change +o no need to use highmem to handle the 128 Terabytes of RAM + +Description: + + Userspace is able to modify and it sees only the 3rd/2nd/1st level + pagetables (pgd_offset() implicitly walks the 1st slot of the 4th + level pagetable and it returns an entry into the 3rd level pagetable). + This is where the per-process 512 Gigabytes limit cames from. + + The common code pgd is the PDPE, the pmd is the PDE, the + pte is the PTE. The PML4E remains invisible to the common + code. + + The kernel uses all the first 47 bits of the negative half + of the virtual address space to build the direct mapping using + 2 Mbytes page size. The kernel virtual addresses have bit number + 47 always set to 1 (and in turn also bits 48-63 are set to 1 too, + due the sign extension). This is where the 128 Terabytes - 2 Gigabytes global + limit of RAM cames from. + + Since the per-process limit is 512 Gigabytes (due to kernel common + code 3 level pagetable limitation), the higher virtual address mapped + into userspace is 0x7fffffffff and it makes sense to use it + as the top of the userspace stack to allow the stack to grow as + much as possible. + + Setting the PAGE_OFFSET to 2^39 (after the last userspace + virtual address) wouldn't make much difference compared to + setting PAGE_OFFSET to 0xffff800000000000 because we have an + hole into the virtual address space. The last byte mapped by the + 255th slot in the 4th level pagetable is at virtual address + 0x00007fffffffffff and the first byte mapped by the 256th slot in the + 4th level pagetable is at address 0xffff800000000000. Due to this + hole we can't trivially build a direct mapping across all the + 512 slots of the 4th level pagetable, so we simply use only the + second (negative) half of the 4th level pagetable for that purpose + (that provides us 128 Terabytes of contigous virtual addresses). + Strictly speaking we could build a direct mapping also across the hole + using some DISCONTIGMEM trick, but we don't need such a large + direct mapping right now. + +Future: + + During 2.5.x we can break the 512 Gigabytes per-process limit + possibly by removing from the common code any knowledge about the + architectural dependent physical layout of the virtual to physical + mapping. + + Once the 512 Gigabytes limit will be removed the kernel stack will + be moved (most probably to virtual address 0x00007fffffffffff). + Nothing will break in userspace due that move, as nothing breaks + in IA32 compiling the kernel with CONFIG_2G. + +Linus agreed on not breaking common code and to live with the 512 Gigabytes +per-process limitation for the 2.4.x timeframe and he has given me and Andi +some very useful hints... (thanks! :) + +Thanks also to H. Peter Anvin for his interesting and useful suggestions on +the x86-64-discuss lists! + +Other memory management related issues follows: + +PAGE_SIZE: + + If somebody is wondering why these days we still have a so small + 4k pagesize (16 or 32 kbytes would be much better for performance + of course), the PAGE_SIZE have to remain 4k for 32bit apps to + provide 100% backwards compatible IA32 API (we can't allow silent + fs corruption or as best a loss of coherency with the page cache + by allocating MAP_SHARED areas in MAP_ANONYMOUS memory with a + do_mmap_fake). I think it could be possible to have a dynamic page + size between 32bit and 64bit apps but it would need extremely + intrusive changes in the common code as first for page cache and + we sure don't want to depend on them right now even if the + hardware would support that. + +PAGETABLE SIZE: + + In turn we can't afford to have pagetables larger than 4k because + we could not be able to allocate them due physical memory + fragmentation, and failing to allocate the kernel stack is a minor + issue compared to failing the allocation of a pagetable. If we + fail the allocation of a pagetable the only thing we can do is to + sched_yield polling the freelist (deadlock prone) or to segfault + the task (not even the sighandler would be sure to run). + +KERNEL STACK: + + 1st stage: + + The kernel stack will be at first allocated with an order 2 allocation + (16k) (the utilization of the stack for a 64bit platform really + isn't exactly the double of a 32bit platform because the local + variables may not be all 64bit wide, but not much less). This will + make things even worse than they are right now on IA32 with + respect of failing fork/clone due memory fragmentation. + + 2nd stage: + + We'll benchmark if reserving one register as task_struct + pointer will improve performance of the kernel (instead of + recalculating the task_struct pointer starting from the stack + pointer each time). My guess is that recalculating will be faster + but it worth a try. + + If reserving one register for the task_struct pointer + will be faster we can as well split task_struct and kernel + stack. task_struct can be a slab allocation or a + PAGE_SIZEd allocation, and the kernel stack can then be + allocated in a order 1 allocation. Really this is risky, + since 8k on a 64bit platform is going to be less than 7k + on a 32bit platform but we could try it out. This would + reduce the fragmentation problem of an order of magnitude + making it equal to the current IA32. + + We must also consider the x86-64 seems to provide in hardware a + per-irq stack that could allow us to remove the irq handler + footprint from the regular per-process-stack, so it could allow + us to live with a smaller kernel stack compared to the other + linux architectures. + + 3rd stage: + + Before going into production if we still have the order 2 + allocation we can add a sysctl that allows the kernel stack to be + allocated with vmalloc during memory fragmentation. This have to + remain turned off during benchmarks :) but it should be ok in real + life. + +Order of PAGE_CACHE_SIZE and other allocations: + + On the long run we can increase the PAGE_CACHE_SIZE to be + an order 2 allocations and also the slab/buffercache etc.ec.. + could be all done with order 2 allocations. To make the above + to work we should change lots of common code thus it can be done + only once the basic port will be in a production state. Having + a working PAGE_CACHE_SIZE would be a benefit also for + IA32 and other architectures of course. + +Andrea SuSE diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Tue Feb 19 18:08:57 2002 +++ b/MAINTAINERS Tue Feb 19 18:08:57 2002 @@ -1042,9 +1042,10 @@ M: jamesm@intercode.com.au P: Harald Welte M: laforge@gnumonks.org -W: http://netfilter.samba.org -W: http://netfilter.kernelnotes.org -W: http://netfilter.filewatcher.org +P: Jozsef Kadlecsik +M: kadlec@blackhole.kfki.hu +W: http://www.netfilter.org/ +W: http://www.iptables.org/ L: netfilter@lists.samba.org S: Supported @@ -1391,9 +1392,10 @@ S: Maintained SOUND -P: Alan Cox -M: alan@redhat.com -S: Maintained for 2.2 only +P: Jaroslav Kysela +M: perex@suse.cz +L: alsa-devel@alsa-project.org +S: Maintained SPARC: P: David S. Miller @@ -1759,6 +1761,13 @@ X86 3-LEVEL PAGING (PAE) SUPPORT P: Ingo Molnar M: mingo@redhat.com +S: Maintained + +X86-64 port +P: Andi Kleen +M: ak@suse.de +L: discuss@x86-64.org +W: http://www.x86-64.org S: Maintained YAM DRIVER FOR AX.25 diff -Nru a/Makefile b/Makefile --- a/Makefile Tue Feb 19 18:08:57 2002 +++ b/Makefile Tue Feb 19 18:08:57 2002 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 5 -SUBLEVEL = 4 +SUBLEVEL = 5 EXTRAVERSION = KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -122,7 +122,7 @@ NETWORKS =net/network.o LIBS =$(TOPDIR)/lib/lib.a -SUBDIRS =kernel lib drivers mm fs net ipc +SUBDIRS =kernel lib drivers mm fs net ipc sound DRIVERS-n := DRIVERS-y := @@ -156,7 +156,7 @@ DRIVERS-y += drivers/cdrom/driver.o endif -DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o +DRIVERS-$(CONFIG_SOUND) += sound/sound.o DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtdlink.o DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o @@ -201,7 +201,7 @@ drivers/char/drm/*-mod.c \ drivers/pci/devlist.h drivers/pci/classlist.h drivers/pci/gen-devlist \ drivers/zorro/devlist.h drivers/zorro/gen-devlist \ - drivers/sound/bin2hex drivers/sound/hex2hex \ + sound/oss/bin2hex sound/oss/hex2hex \ drivers/atm/fore200e_mkfirm drivers/atm/{pca,sba}*{.bin,.bin1,.bin2} \ drivers/scsi/aic7xxx/aicasm/aicasm_gram.c \ drivers/scsi/aic7xxx/aicasm/aicasm_scan.c \ @@ -222,11 +222,11 @@ drivers/net/hamradio/soundmodem/sm_tbl_{hapn4800,psk4800}.h \ drivers/net/hamradio/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h \ drivers/net/hamradio/soundmodem/gentbl \ - drivers/sound/*_boot.h drivers/sound/.*.boot \ - drivers/sound/msndinit.c \ - drivers/sound/msndperm.c \ - drivers/sound/pndsperm.c \ - drivers/sound/pndspini.c \ + sound/oss/*_boot.h sound/oss/.*.boot \ + sound/oss/msndinit.c \ + sound/oss/msndperm.c \ + sound/oss/pndsperm.c \ + sound/oss/pndspini.c \ drivers/atm/fore200e_*_fw.c drivers/atm/.fore200e_*.fw \ .version .config* config.in config.old \ scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp \ @@ -343,7 +343,7 @@ init/do_mounts.o: init/do_mounts.c include/config/MARKER $(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -c -o $*.o $< -fs lib mm ipc kernel drivers net: dummy +fs lib mm ipc kernel drivers net sound: dummy $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@) TAGS: dummy diff -Nru a/Rules.make b/Rules.make --- a/Rules.make Tue Feb 19 18:08:59 2002 +++ b/Rules.make Tue Feb 19 18:08:59 2002 @@ -208,6 +208,8 @@ ifneq "$(strip $(export-objs))" "" MODINCL = $(TOPDIR)/include/linux/modules +MODCURDIR = $(subst $(TOPDIR)/,,$(shell /bin/pwd)) +MODPREFIX = $(subst /,-,$(MODCURDIR))__ # The -w option (enable warnings) for genksyms will return here in 2.1 # So where has it gone? @@ -222,20 +224,20 @@ genksyms_smp_prefix := endif -$(MODINCL)/%.ver: %.c - @if [ ! -r $(MODINCL)/$*.stamp -o $(MODINCL)/$*.stamp -ot $< ]; then \ +$(MODINCL)/$(MODPREFIX)%.ver: %.c + @if [ ! -r $(MODINCL)/$(MODPREFIX)$*.stamp -o $(MODINCL)/$(MODPREFIX)$*.stamp -ot $< ]; then \ echo '$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -E -D__GENKSYMS__ $<'; \ echo '| $(GENKSYMS) $(genksyms_smp_prefix) -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) > $@.tmp'; \ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -E -D__GENKSYMS__ $< \ | $(GENKSYMS) $(genksyms_smp_prefix) -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) > $@.tmp; \ if [ -r $@ ] && cmp -s $@ $@.tmp; then echo $@ is unchanged; rm -f $@.tmp; \ else echo mv $@.tmp $@; mv -f $@.tmp $@; fi; \ - fi; touch $(MODINCL)/$*.stamp + fi; touch $(MODINCL)/$(MODPREFIX)$*.stamp -$(addprefix $(MODINCL)/,$(export-objs:.o=.ver)): $(TOPDIR)/include/linux/autoconf.h +$(addprefix $(MODINCL)/$(MODPREFIX),$(export-objs:.o=.ver)): $(TOPDIR)/include/linux/autoconf.h # updates .ver files but not modversions.h -fastdep: $(addprefix $(MODINCL)/,$(export-objs:.o=.ver)) +fastdep: $(addprefix $(MODINCL)/$(MODPREFIX),$(export-objs:.o=.ver)) # updates .ver files and modversions.h like before (is this needed?) dep: fastdep update-modverfile diff -Nru a/arch/alpha/Makefile b/arch/alpha/Makefile --- a/arch/alpha/Makefile Tue Feb 19 18:08:57 2002 +++ b/arch/alpha/Makefile Tue Feb 19 18:08:57 2002 @@ -117,11 +117,13 @@ archclean: @$(MAKE) -C arch/alpha/kernel clean @$(MAKEBOOT) clean - rm -f arch/alpha/vmlinux.lds archmrproper: + rm -f arch/alpha/vmlinux.lds + rm -f include/asm-alpha/asm_offsets.h archdep: + $(MAKE) -C arch/alpha/kernel asm_offsets @$(MAKEBOOT) dep vmlinux: arch/alpha/vmlinux.lds diff -Nru a/arch/alpha/config.in b/arch/alpha/config.in --- a/arch/alpha/config.in Tue Feb 19 18:08:58 2002 +++ b/arch/alpha/config.in Tue Feb 19 18:08:58 2002 @@ -346,7 +346,7 @@ tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in + source sound/Config.in fi endmenu diff -Nru a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile --- a/arch/alpha/kernel/Makefile Tue Feb 19 18:08:59 2002 +++ b/arch/alpha/kernel/Makefile Tue Feb 19 18:08:59 2002 @@ -8,15 +8,15 @@ # Note 2! The CFLAGS definitions are now in the main makefile... .S.s: - $(CPP) $(AFLAGS) -o $*.s $< + $(CPP) $(CFLAGS) $(AFLAGS) -o $*.s $< .S.o: - $(CC) $(AFLAGS) -c -o $*.o $< + $(CC) $(CFLAGS) $(AFLAGS) -c -o $*.o $< O_TARGET := kernel.o export-objs := alpha_ksyms.o -obj-y := entry.o traps.o process.o osf_sys.o irq.o irq_alpha.o \ +obj-y := entry.o traps.o process.o init_task.o osf_sys.o irq.o irq_alpha.o \ signal.o setup.o ptrace.o time.o semaphore.o alpha_ksyms.o # @@ -102,11 +102,15 @@ all: kernel.o head.o -asm_offsets: check_asm - ./check_asm > $(TOPDIR)/include/asm-alpha/asm_offsets.h - -check_asm: check_asm.c - $(HOSTCC) -o $@ $< $(CPPFLAGS) -ffixed-8 +ASM_OFFSETS_H = $(TOPDIR)/include/asm-alpha/asm_offsets.h +asm_offsets: + $(CC) $(CFLAGS) -S -o - check_asm.c | \ + sed -e '/xyzzy/ { s/xyzzy //; p; }; d;' > asm_offsets.tmp + @if cmp -s asm_offsets.tmp $(ASM_OFFSETS_H); then \ + set -x; rm asm_offsets.tmp; \ + else \ + set -x; mv asm_offsets.tmp $(ASM_OFFSETS_H); \ + fi clean:: rm -f check_asm diff -Nru a/arch/alpha/kernel/check_asm.c b/arch/alpha/kernel/check_asm.c --- a/arch/alpha/kernel/check_asm.c Tue Feb 19 18:09:00 2002 +++ b/arch/alpha/kernel/check_asm.c Tue Feb 19 18:09:00 2002 @@ -3,30 +3,28 @@ #include #include -int main() +#define OUT(x) \ + asm ("\nxyzzy " x) +#define DEF(name, val) \ + asm volatile ("\nxyzzy #define " name " %0" : : "i"(val)) + +void foo(void) { - printf("#ifndef __ASM_OFFSETS_H__\n#define __ASM_OFFSETS_H__\n"); + OUT("#ifndef __ASM_OFFSETS_H__"); + OUT("#define __ASM_OFFSETS_H__"); + OUT(""); + + DEF("TI_TASK", offsetof(struct thread_info, task)); + DEF("TI_FLAGS", offsetof(struct thread_info, flags)); + DEF("TI_CPU", offsetof(struct thread_info, cpu)); - printf("#define TASK_STATE %ld\n", - (long)offsetof(struct task_struct, state)); - printf("#define TASK_FLAGS %ld\n", - (long)offsetof(struct task_struct, flags)); - printf("#define TASK_SIGPENDING %ld\n", -#error (long)offsetof(struct task_struct, sigpending)); - printf("#define TASK_ADDR_LIMIT %ld\n", - (long)offsetof(struct task_struct, addr_limit)); - printf("#define TASK_EXEC_DOMAIN %ld\n", - (long)offsetof(struct task_struct, exec_domain)); - printf("#define TASK_NEED_RESCHED %ld\n", -#error (long)offsetof(struct task_struct, work.need_resched)); - printf("#define TASK_SIZE %ld\n", sizeof(struct task_struct)); - printf("#define STACK_SIZE %ld\n", sizeof(union task_union)); + DEF("PT_PTRACED", PT_PTRACED); + DEF("CLONE_VM", CLONE_VM); + DEF("SIGCHLD", SIGCHLD); - printf("#define HAE_CACHE %ld\n", - (long)offsetof(struct alpha_machine_vector, hae_cache)); - printf("#define HAE_REG %ld\n", - (long)offsetof(struct alpha_machine_vector, hae_register)); + DEF("HAE_CACHE", offsetof(struct alpha_machine_vector, hae_cache)); + DEF("HAE_REG", offsetof(struct alpha_machine_vector, hae_register)); - printf("#endif /* __ASM_OFFSETS_H__ */\n"); - return 0; + OUT(""); + OUT("#endif /* __ASM_OFFSETS_H__ */"); } diff -Nru a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S --- a/arch/alpha/kernel/entry.S Tue Feb 19 18:08:56 2002 +++ b/arch/alpha/kernel/entry.S Tue Feb 19 18:08:56 2002 @@ -7,42 +7,16 @@ #include #include #include +#include +#include -#define SIGCHLD 20 - -#define NR_SYSCALLS 378 - -/* - * These offsets must match with alpha_mv in . - */ -#define HAE_CACHE 0 -#define HAE_REG 8 +#define NR_SYSCALLS 381 /* * stack offsets */ -#define SP_OFF 184 - -#define SWITCH_STACK_SIZE 320 - -/* - * task structure offsets - */ -#define TASK_STATE 0 -#define TASK_FLAGS 8 -#error #define TASK_SIGPENDING 16 -#define TASK_ADDR_LIMIT 24 -#define TASK_EXEC_DOMAIN 32 -#error #define TASK_NEED_RESCHED 40 -#error #define TASK_PTRACE 48 -#define TASK_PROCESSOR 100 - -/* - * task flags (must match include/linux/sched.h): - */ -#define PT_PTRACED 0x00000001 - -#define CLONE_VM 0x00000100 +#define SP_OFF 184 +#define SWITCH_STACK_SIZE 320 /* * This defines the normal kernel pt-regs layout. @@ -55,7 +29,7 @@ */ #define SAVE_ALL \ - subq $30,184,$30; \ + subq $30,SP_OFF,$30; \ stq $0,0($30); \ stq $1,8($30); \ stq $2,16($30); \ @@ -98,12 +72,8 @@ ldq $8,64($30); \ beq $20,99f; \ ldq $20,HAE_REG($19); \ - addq $31,7,$16; \ - call_pal PAL_swpipl; \ stq $21,HAE_CACHE($19); \ stq $21,0($20); \ - mov $0,$16; \ - call_pal PAL_swpipl; \ ldq $0,0($30); \ ldq $1,8($30); \ 99:; \ @@ -117,13 +87,10 @@ ldq $26,128($30); \ ldq $27,136($30); \ ldq $28,144($30); \ - addq $30,184,$30 + addq $30,SP_OFF,$30 .text .set noat -#if defined(__linux__) && !defined(__ELF__) - .set singlegp -#endif .align 3 .globl entInt @@ -203,68 +170,57 @@ /* - * Fork() is one of the special system calls: it needs to - * save the callee-saved regs so that the regs can be found - * for the new process.. We save them in the "context switch" - * stack format (see arch/alpha/kernel/process.c). - * - * Also, for the kernel fork, we need to fake the system call - * stack buildup, as we can't do system calls from kernel space. - */ -.align 3 -.ent kernel_clone -kernel_clone: - .frame $30, 0, $26 - .prologue 0 - subq $30,6*8,$30 - stq $31,0($30) - stq $26,8($30) - stq $29,16($30) - stq $16,24($30) - stq $17,32($30) - stq $18,40($30) - bis $31,2,$0 /* Register v0: syscall nr for fork() */ - SAVE_ALL - bsr $26,sys_clone - stq $0,0($30) - br ret_from_sys_call -.end kernel_clone - -/* * kernel_thread(fn, arg, clone_flags) */ -.align 3 -.globl kernel_thread -.ent kernel_thread + .align 4 + .globl kernel_thread + .ent kernel_thread kernel_thread: ldgp $29,0($27) /* we can be called from a module */ - .frame $30, 4*8, $26 - subq $30,4*8,$30 - stq $10,16($30) - stq $9,8($30) - lda $0,CLONE_VM - stq $26,0($30) .prologue 1 - mov $16,$9 /* save fn */ - mov $17,$10 /* save arg */ - or $18,$0,$16 /* shuffle flags to front; add CLONE_VM. */ - bsr $26,kernel_clone - bne $20,1f /* $20 is non-zero in child */ - ldq $26,0($30) - ldq $9,8($30) - ldq $10,16($30) - addq $30,4*8,$30 - ret $31,($26),1 -/* this is in child: look out as we don't have any stack here.. */ -1: mov $9,$27 /* get fn */ - lda $8,0x3fff - mov $10,$16 /* get arg */ - bic $30,$8,$8 /* get current */ - jsr $26,($27) - ldgp $29,0($26) - mov $0,$16 - mov $31,$26 - jsr $31,sys_exit + subq $30,SP_OFF+6*8,$30 + br $1, 2f /* load start address */ + + /* We've now "returned" from a fake system call. */ + unop + blt $0, 1f /* error? */ + ldi $1, 0x3fff + beq $20, 1f /* parent or child? */ + + bic $30, $1, $8 /* in child. */ + jsr $26, ($27) + ldgp $29, 0($26) + mov $0, $16 + mov $31, $26 + jmp $31, sys_exit + +1: ret /* in parent. */ + + .align 4 +2: /* Fake a system call stack frame, as we can't do system calls + from kernel space. Note that we store FN and ARG as they + need to be set up in the child for the call. Also store $8 + and $26 for use in the parent. */ + stq $31, SP_OFF($30) /* ps */ + stq $1, SP_OFF+8($30) /* pc */ + stq $29, SP_OFF+16($30) /* gp */ + stq $16, 136($30) /* $27; FN for child */ + stq $17, SP_OFF+24($30) /* $16; ARG for child */ + stq $8, 64($30) /* $8 */ + stq $26, 128($30) /* $26 */ + /* Avoid the HAE being gratuitously wrong, to avoid restoring it. */ + ldq $2, alpha_mv+HAE_CACHE + stq $2, 152($30) /* HAE */ + + /* Shuffle FLAGS to the front; add CLONE_VM. */ + ldi $1, CLONE_VM + or $18, $1, $16 + bsr $26, sys_clone + + /* We don't actually care for a3 success widgetry in the kernel. + Not for positive errno values. */ + stq $0, 0($30) /* $0 */ + br restore_all .end kernel_thread /* @@ -535,10 +491,21 @@ call_pal PAL_swpctx unop bsr $1,undo_switch_stack + lda $8,0x3fff mov $17,$0 + bic $30,$8,$8 ret $31,($26),1 .end alpha_switch_to +.globl ret_from_fork +.align 3 +.ent ret_from_fork +ret_from_fork: + lda $26,ret_from_sys_call + mov $0,$16 + jmp $31,schedule_tail +.end ret_from_fork + /* * Oh, well.. Disassembling OSF/1 binaries to find out how the * system calls work isn't much fun. @@ -559,12 +526,11 @@ lda $5,sys_call_table lda $27,sys_ni_syscall cmpult $0,$4,$4 - ldq $3,TASK_PTRACE($8) + ldl $3,TI_FLAGS($8) stq $17,SP_OFF+32($30) s8addq $0,$5,$5 - and $3,PT_PTRACED,$3 stq $18,SP_OFF+40($30) - bne $3,strace + blbs $3,strace beq $4,1f ldq $27,0($5) 1: jsr $26,($27),alpha_ni_syscall @@ -580,34 +546,65 @@ and $0,8,$0 beq $0,restore_all ret_from_reschedule: -#error ldq $2,TASK_NEED_RESCHED($8) - lda $4,init_task_union - bne $2,reschedule - xor $4,$8,$4 -#error ldl $5,TASK_SIGPENDING($8) - beq $4,restore_all - bne $5,signal_return + /* Make sure need_resched and sigpending don't change between + sampling and the rti. */ + lda $16,7 + call_pal PAL_swpipl + ldl $5,TI_FLAGS($8) + and $5,_TIF_WORK_MASK,$2 + bne $5,work_pending restore_all: RESTORE_ALL call_pal PAL_rti +work_pending: + and $5,_TIF_NEED_RESCHED,$2 + beq $2,work_notifysig + +work_resched: + subq $30,16,$30 + stq $19,0($30) /* save syscall nr */ + stq $20,8($30) /* and error indication (a3) */ + jsr $26,schedule + ldq $19,0($30) + ldq $20,8($30) + addq $30,16,$30 + /* Make sure need_resched and sigpending don't change between + sampling and the rti. */ + lda $16,7 + call_pal PAL_swpipl + ldl $5,TI_FLAGS($8) + and $5,_TIF_WORK_MASK,$2 + beq $2,restore_all + and $5,_TIF_NEED_RESCHED,$2 + bne $2,work_resched + +work_notifysig: + mov $30,$17 + br $1,do_switch_stack + mov $5,$21 + mov $30,$18 + mov $31,$16 + jsr $26,do_notify_resume + bsr $1,undo_switch_stack + br restore_all /* PTRACE syscall handler */ .align 3 strace: /* set up signal stack, call syscall_trace */ bsr $1,do_switch_stack - jsr $26,syscall_trace + jsr $26,syscall_trace bsr $1,undo_switch_stack /* get the system call number and the arguments back.. */ - ldq $0,0($30) - ldq $16,SP_OFF+24($30) - ldq $17,SP_OFF+32($30) - ldq $18,SP_OFF+40($30) - ldq $19,72($30) - ldq $20,80($30) - ldq $21,88($30) + ldq $0,0($30) + ldq $16,SP_OFF+24($30) + ldq $17,SP_OFF+32($30) + ldq $18,SP_OFF+40($30) + ldq $19,72($30) + ldq $20,80($30) + ldq $21,88($30) /* get the system call pointer.. */ lda $1,NR_SYSCALLS($31) @@ -627,11 +624,11 @@ stq $0,0($30) /* save return value */ bsr $1,do_switch_stack - jsr $26,syscall_trace + jsr $26,syscall_trace bsr $1,undo_switch_stack br $31,ret_from_sys_call - .align 3 + .align 3 strace_error: ldq $19,0($30) /* old syscall nr (zero if success) */ beq $19,strace_success @@ -645,7 +642,7 @@ bsr $1,do_switch_stack mov $19,$9 /* save old syscall number */ mov $20,$10 /* save old a3 */ - jsr $26,syscall_trace + jsr $26,syscall_trace mov $9,$19 mov $10,$20 bsr $1,undo_switch_stack @@ -677,39 +674,7 @@ stq $0,0($30) stq $31,72($30) /* a3=0 => no error */ br ret_from_sys_call - -.align 3 -signal_return: - mov $30,$17 - br $1,do_switch_stack - mov $30,$18 - mov $31,$16 - jsr $26,do_signal - bsr $1,undo_switch_stack - br restore_all .end entSys - - .globl ret_from_fork -.align 3 -.ent ret_from_fork -ret_from_fork: - lda $26,ret_from_sys_call - mov $17,$16 - jsr $31,schedule_tail -.end ret_from_fork - -.align 3 -.ent reschedule -reschedule: - subq $30,16,$30 - stq $19,0($30) /* save syscall nr */ - stq $20,8($30) /* and error indication (a3) */ - jsr $26,schedule - ldq $19,0($30) - ldq $20,8($30) - addq $30,16,$30 - br ret_from_reschedule -.end reschedule .align 3 .ent sys_sigreturn diff -Nru a/arch/alpha/kernel/head.S b/arch/alpha/kernel/head.S --- a/arch/alpha/kernel/head.S Tue Feb 19 18:08:57 2002 +++ b/arch/alpha/kernel/head.S Tue Feb 19 18:08:57 2002 @@ -22,8 +22,8 @@ .prologue 0 br $27,1f 1: ldgp $29,0($27) - /* We need to get current loaded up with our first task... */ - lda $8,init_task_union + /* We need to get current_task_info loaded up... */ + lda $8,init_thread_union /* ... and find our stack ... */ lda $30,0x4000($8) /* ... and then we can start the kernel. */ diff -Nru a/arch/alpha/kernel/init_task.c b/arch/alpha/kernel/init_task.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/alpha/kernel/init_task.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include +#include + + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); +struct task_struct init_task = INIT_TASK(init_task); + +union thread_union init_thread_union + __attribute__((section(".data.init_thread"))) + = { INIT_THREAD_INFO(init_task) }; diff -Nru a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c --- a/arch/alpha/kernel/osf_sys.c Tue Feb 19 18:08:56 2002 +++ b/arch/alpha/kernel/osf_sys.c Tue Feb 19 18:08:56 2002 @@ -776,7 +776,7 @@ /* Return current software fp control & status bits. */ /* Note that DU doesn't verify available space here. */ - w = current->thread.flags & IEEE_SW_MASK; + w = current_thread_info()->ieee_state & IEEE_SW_MASK; w = swcr_update_status(w, rdfpcr()); if (put_user(w, (unsigned long *) buffer)) return -EFAULT; @@ -793,7 +793,7 @@ case GSI_UACPROC: if (nbytes < sizeof(unsigned int)) return -EINVAL; - w = (current->thread.flags >> UAC_SHIFT) & UAC_BITMASK; + w = (current_thread_info()->flags >> UAC_SHIFT) & UAC_BITMASK; if (put_user(w, (unsigned int *)buffer)) return -EFAULT; return 1; @@ -840,8 +840,9 @@ /* Update softare trap enable bits. */ if (get_user(swcr, (unsigned long *)buffer)) return -EFAULT; - current->thread.flags &= ~IEEE_SW_MASK; - current->thread.flags |= swcr & IEEE_SW_MASK; + current_thread_info()->ieee_state + = ((current_thread_info()->ieee_state & ~IEEE_SW_MASK) + | (swcr & IEEE_SW_MASK)); /* Update the real fpcr. */ fpcr = rdfpcr(); @@ -869,18 +870,23 @@ case SSI_NVPAIRS: { unsigned long v, w, i; + unsigned int old, new; for (i = 0; i < nbytes; ++i) { + if (get_user(v, 2*i + (unsigned int *)buffer)) return -EFAULT; if (get_user(w, 2*i + 1 + (unsigned int *)buffer)) return -EFAULT; switch (v) { case SSIN_UACPROC: - current->thread.flags &= - ~(UAC_BITMASK << UAC_SHIFT); - current->thread.flags |= - (w & UAC_BITMASK) << UAC_SHIFT; + again: + old = current_thread_info()->flags; + new = old & ~(UAC_BITMASK << UAC_SHIFT); + new = new | (w & UAC_BITMASK) << UAC_SHIFT; + if (cmpxchg(¤t_thread_info()->flags, + old, new) != old) + goto again; break; default: diff -Nru a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c --- a/arch/alpha/kernel/pci_iommu.c Tue Feb 19 18:08:59 2002 +++ b/arch/alpha/kernel/pci_iommu.c Tue Feb 19 18:08:59 2002 @@ -411,13 +411,8 @@ Write dma_length of each leader with the combined lengths of the mergable followers. */ -#define SG_ENT_VIRT_ADDRESS(SG) \ - ((SG)->address \ - ? (SG)->address \ - : page_address((SG)->page) + (SG)->offset) - -#define SG_ENT_PHYS_ADDRESS(SG) \ - __pa(SG_ENT_VIRT_ADDRESS(SG)) +#define SG_ENT_VIRT_ADDRESS(SG) (page_address((SG)->page) + (SG)->offset) +#define SG_ENT_PHYS_ADDRESS(SG) __pa(SG_ENT_VIRT_ADDRESS(SG)) static void sg_classify(struct scatterlist *sg, struct scatterlist *end, int virt_ok) diff -Nru a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c --- a/arch/alpha/kernel/process.c Tue Feb 19 18:08:59 2002 +++ b/arch/alpha/kernel/process.c Tue Feb 19 18:08:59 2002 @@ -43,22 +43,6 @@ #include "pci_impl.h" /* - * Initial task structure. Make this a per-architecture thing, - * because different architectures tend to have different - * alignment requirements and potentially different initial - * setup. - */ - -unsigned long init_user_stack[1024] = { STACK_MAGIC, }; -static struct fs_struct init_fs = INIT_FS; -static struct files_struct init_files = INIT_FILES; -static struct signal_struct init_signals = INIT_SIGNALS; -struct mm_struct init_mm = INIT_MM(init_mm); - -union task_union init_task_union __attribute__((section("init_task"))) - = { task: INIT_TASK(init_task_union.task) }; - -/* * No need to acquire the kernel lock, we're entirely local.. */ asmlinkage int @@ -73,18 +57,12 @@ void cpu_idle(void) { - /* An endless idle loop with no priority at all. */ - current->nice = 20; - while (1) { /* FIXME -- EV6 and LCA45 know how to power down the CPU. */ - /* Although we are an idle CPU, we do not want to - get into the scheduler unnecessarily. */ - long oldval = xchg(¤t->work.need_resched, -1UL); - if (!oldval) - while (current->work.need_resched < 0); + while (!need_resched()) + barrier(); schedule(); check_pgt_cache(); } @@ -152,7 +130,7 @@ barrier(); #endif - /* If booted from SRM, reset some of the original environment. */ + /* If booted from SRM, reset some of the original environment. */ if (alpha_using_srm) { #ifdef CONFIG_DUMMY_CONSOLE /* This has the effect of resetting the VGA video origin. */ @@ -255,7 +233,7 @@ { /* Arrange for each exec'ed process to start off with a clean slate with respect to the FPU. This is all exceptions disabled. */ - current->thread.flags &= ~IEEE_SW_MASK; + current_thread_info()->ieee_state = 0; wrfpcr(FPCR_DYN_NORMAL | ieee_swcr_to_fpcr(0)); } @@ -294,8 +272,8 @@ * * Note the "stack_offset" stuff: when returning to kernel mode, we need * to have some extra stack-space for the kernel stack that still exists - * after the "ret_from_sys_call". When returning to user mode, we only - * want the space needed by the syscall stack frame (ie "struct pt_regs"). + * after the "ret_from_fork". When returning to user mode, we only want + * the space needed by the syscall stack frame (ie "struct pt_regs"). * Use the passed "regs" pointer to determine how much space we need * for a kernel fork(). */ @@ -305,9 +283,9 @@ unsigned long unused, struct task_struct * p, struct pt_regs * regs) { - extern void ret_from_sys_call(void); extern void ret_from_fork(void); + struct thread_info *childti = p->thread_info; struct pt_regs * childregs; struct switch_stack * childstack, *stack; unsigned long stack_offset; @@ -315,7 +293,8 @@ stack_offset = PAGE_SIZE - sizeof(struct pt_regs); if (!(regs->ps & 8)) stack_offset = (PAGE_SIZE-1) & (unsigned long) regs; - childregs = (struct pt_regs *) (stack_offset + PAGE_SIZE + (long)p); + childregs = (struct pt_regs *) + (stack_offset + PAGE_SIZE + (long) childti); *childregs = *regs; childregs->r0 = 0; @@ -326,10 +305,9 @@ childstack = ((struct switch_stack *) childregs) - 1; *childstack = *stack; childstack->r26 = (unsigned long) ret_from_fork; - p->thread.usp = usp; - p->thread.ksp = (unsigned long) childstack; - p->thread.pal_flags = 1; /* set FEN, clear everything else */ - p->thread.flags = current->thread.flags; + childti->pcb.usp = usp; + childti->pcb.ksp = (unsigned long) childstack; + childti->pcb.flags = 1; /* set FEN, clear everything else */ return 0; } @@ -433,6 +411,35 @@ } /* + * Return saved PC of a blocked thread. This assumes the frame + * pointer is the 6th saved long on the kernel stack and that the + * saved return address is the first long in the frame. This all + * holds provided the thread blocked through a call to schedule() ($15 + * is the frame pointer in schedule() and $15 is saved at offset 48 by + * entry.S:do_switch_stack). + * + * Under heavy swap load I've seen this lose in an ugly way. So do + * some extra sanity checking on the ranges we expect these pointers + * to be in so that we can fail gracefully. This is just for ps after + * all. -- r~ + */ + +unsigned long +thread_saved_pc(task_t *t) +{ + unsigned long base = (unsigned long)t->thread_info; + unsigned long fp, sp = t->thread_info->pcb.ksp; + + if (sp > base && sp+6*8 < base + 16*1024) { + fp = ((unsigned long*)sp)[6]; + if (fp > sp && fp < base + 16*1024) + return *(unsigned long *)fp; + } + + return 0; +} + +/* * These bracket the sleeping functions.. */ extern void scheduling_functions_start_here(void); @@ -457,9 +464,9 @@ * after all... */ - pc = thread_saved_pc(&p->thread); + pc = thread_saved_pc(p); if (pc >= first_sched && pc < last_sched) { - schedule_frame = ((unsigned long *)p->thread.ksp)[6]; + schedule_frame = ((unsigned long *)p->thread_info->pcb.ksp)[6]; return ((unsigned long *)schedule_frame)[12]; } return pc; diff -Nru a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c --- a/arch/alpha/kernel/ptrace.c Tue Feb 19 18:09:00 2002 +++ b/arch/alpha/kernel/ptrace.c Tue Feb 19 18:09:00 2002 @@ -101,7 +101,7 @@ long *addr; if (regno == 30) { - addr = &task->thread.usp; + addr = &task->thread_info->pcb.usp; } else if (regno == 31 || regno > 64) { zero = 0; addr = &zero; @@ -120,7 +120,8 @@ /* Special hack for fpcr -- combine hardware and software bits. */ if (regno == 63) { unsigned long fpcr = *get_reg_addr(task, regno); - unsigned long swcr = task->thread.flags & IEEE_SW_MASK; + unsigned long swcr + = task->thread_info->ieee_state & IEEE_SW_MASK; swcr = swcr_update_status(swcr, fpcr); return fpcr | swcr; } @@ -134,8 +135,9 @@ put_reg(struct task_struct *task, unsigned long regno, long data) { if (regno == 63) { - task->thread.flags = ((task->thread.flags & ~IEEE_SW_MASK) - | (data & IEEE_SW_MASK)); + task->thread_info->ieee_state + = ((task->thread_info->ieee_state & ~IEEE_SW_MASK) + | (data & IEEE_SW_MASK)); data = (data & FPCR_DYN_MASK) | ieee_swcr_to_fpcr(data); } *get_reg_addr(task, regno) = data; @@ -182,31 +184,34 @@ * branch (emulation can be tricky for fp branches). */ displ = ((s32)(insn << 11)) >> 9; - child->thread.bpt_addr[nsaved++] = pc + 4; + child->thread_info->bpt_addr[nsaved++] = pc + 4; if (displ) /* guard against unoptimized code */ - child->thread.bpt_addr[nsaved++] = pc + 4 + displ; + child->thread_info->bpt_addr[nsaved++] + = pc + 4 + displ; DBG(DBG_BPT, ("execing branch\n")); } else if (op_code == 0x1a) { reg_b = (insn >> 16) & 0x1f; - child->thread.bpt_addr[nsaved++] = get_reg(child, reg_b); + child->thread_info->bpt_addr[nsaved++] = get_reg(child, reg_b); DBG(DBG_BPT, ("execing jump\n")); } else { - child->thread.bpt_addr[nsaved++] = pc + 4; + child->thread_info->bpt_addr[nsaved++] = pc + 4; DBG(DBG_BPT, ("execing normal insn\n")); } /* install breakpoints: */ for (i = 0; i < nsaved; ++i) { - res = read_int(child, child->thread.bpt_addr[i], &insn); + res = read_int(child, child->thread_info->bpt_addr[i], &insn); if (res < 0) return res; - child->thread.bpt_insn[i] = insn; - DBG(DBG_BPT, (" -> next_pc=%lx\n", child->thread.bpt_addr[i])); - res = write_int(child, child->thread.bpt_addr[i], BREAKINST); + child->thread_info->bpt_insn[i] = insn; + DBG(DBG_BPT, (" -> next_pc=%lx\n", + child->thread_info->bpt_addr[i])); + res = write_int(child, child->thread_info->bpt_addr[i], + BREAKINST); if (res < 0) return res; } - child->thread.bpt_nsaved = nsaved; + child->thread_info->bpt_nsaved = nsaved; return 0; } @@ -217,9 +222,9 @@ int ptrace_cancel_bpt(struct task_struct * child) { - int i, nsaved = child->thread.bpt_nsaved; + int i, nsaved = child->thread_info->bpt_nsaved; - child->thread.bpt_nsaved = 0; + child->thread_info->bpt_nsaved = 0; if (nsaved > 2) { printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved); @@ -227,8 +232,8 @@ } for (i = 0; i < nsaved; ++i) { - write_int(child, child->thread.bpt_addr[i], - child->thread.bpt_insn[i]); + write_int(child, child->thread_info->bpt_addr[i], + child->thread_info->bpt_insn[i]); } return (nsaved != 0); } @@ -335,9 +340,9 @@ if ((unsigned long) data > _NSIG) goto out; if (request == PTRACE_SYSCALL) - child->ptrace |= PT_TRACESYS; + set_thread_flag(TIF_SYSCALL_TRACE); else - child->ptrace &= ~PT_TRACESYS; + clear_thread_flag(TIF_SYSCALL_TRACE); child->exit_code = data; wake_up_process(child); /* make sure single-step breakpoint is gone. */ @@ -364,8 +369,9 @@ ret = -EIO; if ((unsigned long) data > _NSIG) goto out; - child->thread.bpt_nsaved = -1; /* mark single-stepping */ - child->ptrace &= ~PT_TRACESYS; + /* Mark single stepping. */ + child->thread_info->bpt_nsaved = -1; + clear_thread_flag(TIF_SYSCALL_TRACE); wake_up_process(child); child->exit_code = data; /* give it a chance to run. */ @@ -381,7 +387,7 @@ goto out; } out: - free_task_struct(child); + put_task_struct(child); out_notsk: unlock_kernel(); return ret; @@ -390,8 +396,9 @@ asmlinkage void syscall_trace(void) { - if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) - != (PT_PTRACED|PT_TRACESYS)) + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) return; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; diff -Nru a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c --- a/arch/alpha/kernel/signal.c Tue Feb 19 18:09:00 2002 +++ b/arch/alpha/kernel/signal.c Tue Feb 19 18:09:00 2002 @@ -32,8 +32,8 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) asmlinkage void ret_from_sys_call(void); -asmlinkage int do_signal(sigset_t *, struct pt_regs *, - struct switch_stack *, unsigned long, unsigned long); +static int do_signal(sigset_t *, struct pt_regs *, struct switch_stack *, + unsigned long, unsigned long); int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) @@ -104,7 +104,7 @@ if (_NSIG_WORDS > 1 && sign > 0) sigemptyset(¤t->blocked); current->blocked.sig[0] = newmask; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); (®s)->r0 = 0; /* special no error return */ @@ -182,7 +182,7 @@ spin_lock_irq(¤t->sigmask_lock); oldset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); while (1) { @@ -209,7 +209,7 @@ spin_lock_irq(¤t->sigmask_lock); oldset = current->blocked; current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); while (1) { @@ -316,7 +316,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(&frame->sc, regs, sw)) @@ -347,7 +347,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(&frame->uc.uc_mcontext, regs, sw)) @@ -579,7 +579,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } @@ -618,7 +618,7 @@ * restart. "r0" is also used as an indicator whether we can restart at * all (if we get here from anything but a syscall return, it will be 0) */ -asmlinkage int +static int do_signal(sigset_t *oldset, struct pt_regs * regs, struct switch_stack * sw, unsigned long r0, unsigned long r19) { @@ -743,4 +743,13 @@ ptrace_set_bpt(current); /* re-set breakpoint */ return 0; +} + +void +do_notify_resume(sigset_t *oldset, struct pt_regs *regs, + struct switch_stack *sw, unsigned long r0, + unsigned long r19, unsigned long thread_info_flags) +{ + if (thread_info_flags & _TIF_SIGPENDING) + do_signal(oldset, regs, sw, r0, r19); } diff -Nru a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c --- a/arch/alpha/kernel/smp.c Tue Feb 19 18:08:58 2002 +++ b/arch/alpha/kernel/smp.c Tue Feb 19 18:08:58 2002 @@ -62,6 +62,7 @@ enum ipi_message_type { IPI_RESCHEDULE, + IPI_MIGRATION, IPI_CALL_FUNC, IPI_CPU_STOP, }; @@ -69,7 +70,7 @@ spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; /* Set to a secondary's cpuid when it comes online. */ -static unsigned long smp_secondary_alive; +static int smp_secondary_alive __initdata = 0; /* Which cpus ids came online. */ unsigned long cpu_present_mask; @@ -82,6 +83,7 @@ int smp_num_cpus = 1; /* Number that came online. */ int smp_threads_ready; /* True once the per process idle is forked. */ cycles_t cacheflush_time; +unsigned long cache_decay_ticks; int __cpu_number_map[NR_CPUS]; int __cpu_logical_map[NR_CPUS]; @@ -156,13 +158,6 @@ { int cpuid = hard_smp_processor_id(); - if (current != init_tasks[cpu_number_map(cpuid)]) { - printk("BUG: smp_calling: cpu %d current %p init_tasks[cpu_number_map(cpuid)] %p\n", - cpuid, current, init_tasks[cpu_number_map(cpuid)]); - } - - DBGS(("CALLIN %d state 0x%lx\n", cpuid, current->state)); - /* Turn on machine checks. */ wrmces(7); @@ -175,22 +170,21 @@ /* Get our local ticker going. */ smp_setup_percpu_timer(cpuid); + /* All kernel threads share the same mm context. */ + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + /* Must have completely accurate bogos. */ __sti(); - /* - * Wait boot CPU to stop with irq enabled before - * running calibrate_delay(). - */ + /* Wait boot CPU to stop with irq enabled before running + calibrate_delay. */ wait_boot_cpu_to_stop(cpuid); mb(); calibrate_delay(); smp_store_cpu_info(cpuid); - /* - * Allow master to continue only after we written - * the loops_per_jiffy. - */ + /* Allow master to continue only after we written loops_per_jiffy. */ wmb(); smp_secondary_alive = 1; @@ -198,15 +192,9 @@ while (!smp_threads_ready) barrier(); - DBGS(("smp_callin: commencing CPU %d current %p\n", - cpuid, current)); - - /* Setup the scheduler for this processor. */ - init_idle(); + DBGS(("smp_callin: commencing CPU %d current %p active_mm %p\n", + cpuid, current, current->active_mm)); - /* ??? This should be in init_idle. */ - atomic_inc(&init_mm.mm_count); - current->active_mm = &init_mm; /* Do nothing. */ cpu_idle(); } @@ -222,8 +210,9 @@ smp_tune_scheduling (int cpuid) { struct percpu_struct *cpu; - unsigned long on_chip_cache; - unsigned long freq; + unsigned long on_chip_cache; /* kB */ + unsigned long freq; /* Hz */ + unsigned long bandwidth = 350; /* MB/s */ cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset + cpuid * hwrpb->processor_size); @@ -244,43 +233,54 @@ case EV6_CPU: case EV67_CPU: - on_chip_cache = 64 + 64; - break; - default: - on_chip_cache = 8 + 8; + on_chip_cache = 64 + 64; break; } freq = hwrpb->cycle_freq ? : est_cycle_freq; -#if 0 - /* Magic estimation stolen from x86 port. */ - cacheflush_time = freq / 1024L * on_chip_cache / 5000L; + cacheflush_time = (freq / 1000000) * (on_chip_cache << 10) / bandwidth; + cache_decay_ticks = cacheflush_time / (freq / 1000) * HZ / 1000; - printk("Using heuristic of %d cycles.\n", - cacheflush_time); -#else - /* Magic value to force potential preemption of other CPUs. */ - cacheflush_time = INT_MAX; + printk("per-CPU timeslice cutoff: %ld.%02ld usecs.\n", + cacheflush_time/(freq/1000000), + (cacheflush_time*100/(freq/1000000)) % 100); + printk("task migration cache decay timeout: %ld msecs.\n", + (cache_decay_ticks + 1) * 1000 / HZ); +} - printk("Using heuristic of %d cycles.\n", - cacheflush_time); -#endif +/* Wait until hwrpb->txrdy is clear for cpu. Return -1 on timeout. */ +static int __init +wait_for_txrdy (unsigned long cpumask) +{ + unsigned long timeout; + + if (!(hwrpb->txrdy & cpumask)) + return 0; + + timeout = jiffies + 10*HZ; + while (time_before(jiffies, timeout)) { + if (!(hwrpb->txrdy & cpumask)) + return 0; + udelay(10); + barrier(); + } + + return -1; } /* * Send a message to a secondary's console. "START" is one such * interesting message. ;-) */ -static void +static void __init send_secondary_console_msg(char *str, int cpuid) { struct percpu_struct *cpu; register char *cp1, *cp2; unsigned long cpumask; size_t len; - long timeout; cpu = (struct percpu_struct *) ((char*)hwrpb @@ -288,9 +288,8 @@ + cpuid * hwrpb->processor_size); cpumask = (1UL << cpuid); - if (hwrpb->txrdy & cpumask) - goto delay1; - ready1: + if (wait_for_txrdy(cpumask)) + goto timeout; cp2 = str; len = strlen(cp2); @@ -302,34 +301,12 @@ wmb(); set_bit(cpuid, &hwrpb->rxrdy); - if (hwrpb->txrdy & cpumask) - goto delay2; - ready2: + if (wait_for_txrdy(cpumask)) + goto timeout; return; -delay1: - /* Wait 10 seconds. Note that jiffies aren't ticking yet. */ - for (timeout = 1000000; timeout > 0; --timeout) { - if (!(hwrpb->txrdy & cpumask)) - goto ready1; - udelay(10); - barrier(); - } - goto timeout; - -delay2: - /* Wait 10 seconds. */ - for (timeout = 1000000; timeout > 0; --timeout) { - if (!(hwrpb->txrdy & cpumask)) - goto ready2; - udelay(10); - barrier(); - } - goto timeout; - -timeout: + timeout: printk("Processor %x not ready\n", cpuid); - return; } /* @@ -392,7 +369,7 @@ secondary_cpu_start(int cpuid, struct task_struct *idle) { struct percpu_struct *cpu; - struct pcb_struct *hwpcb; + struct pcb_struct *hwpcb, *ipcb; long timeout; cpu = (struct percpu_struct *) @@ -400,18 +377,19 @@ + hwrpb->processor_offset + cpuid * hwrpb->processor_size); hwpcb = (struct pcb_struct *) cpu->hwpcb; + ipcb = &idle->thread_info->pcb; /* Initialize the CPU's HWPCB to something just good enough for us to get started. Immediately after starting, we'll swpctx - to the target idle task's ptb. Reuse the stack in the mean + to the target idle task's pcb. Reuse the stack in the mean time. Precalculate the target PCBB. */ - hwpcb->ksp = (unsigned long) idle + sizeof(union task_union) - 16; + hwpcb->ksp = (unsigned long)ipcb + sizeof(union thread_union) - 16; hwpcb->usp = 0; - hwpcb->ptbr = idle->thread.ptbr; + hwpcb->ptbr = ipcb->ptbr; hwpcb->pcc = 0; hwpcb->asn = 0; - hwpcb->unique = virt_to_phys(&idle->thread); - hwpcb->flags = idle->thread.pal_flags; + hwpcb->unique = virt_to_phys(ipcb); + hwpcb->flags = ipcb->flags; hwpcb->res1 = hwpcb->res2 = 0; #if 0 @@ -419,7 +397,7 @@ hwpcb->ksp, hwpcb->ptbr, hwrpb->vptb, hwpcb->unique)); #endif DBGS(("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n", - cpuid, idle->state, idle->thread.pal_flags)); + cpuid, idle->state, ipcb->flags)); /* Setup HWRPB fields that SRM uses to activate secondary CPU */ hwrpb->CPU_restart = __smp_callin; @@ -439,9 +417,9 @@ send_secondary_console_msg("START\r\n", cpuid); - /* Wait 10 seconds for an ACK from the console. Note that jiffies - aren't ticking yet. */ - for (timeout = 1000000; timeout > 0; timeout--) { + /* Wait 10 seconds for an ACK from the console. */ + timeout = jiffies + 10*HZ; + while (time_before(jiffies, timeout)) { if (cpu->flags & 1) goto started; udelay(10); @@ -450,18 +428,17 @@ printk(KERN_ERR "SMP: Processor %d failed to start.\n", cpuid); return -1; -started: + started: DBGS(("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid)); return 0; } -static int __init fork_by_hand(void) +static int __init +fork_by_hand(void) { + /* Don't care about the contents of regs since we'll never + reschedule the forked task. */ struct pt_regs regs; - /* - * don't care about the regs settings since - * we'll never reschedule the forked task. - */ return do_fork(CLONE_VM|CLONE_PID, 0, ®s, 0); } @@ -474,67 +451,57 @@ struct task_struct *idle; long timeout; - /* Cook up an idler for this guy. Note that the address we give - to kernel_thread is irrelevant -- it's going to start where - HWRPB.CPU_restart says to start. But this gets all the other - task-y sort of data structures set up like we wish. */ - /* - * We can't use kernel_thread since we must avoid to - * reschedule the child. - */ + /* Cook up an idler for this guy. Note that the address we + give to kernel_thread is irrelevant -- it's going to start + where HWRPB.CPU_restart says to start. But this gets all + the other task-y sort of data structures set up like we + wish. We can't use kernel_thread since we must avoid + rescheduling the child. */ if (fork_by_hand() < 0) panic("failed fork for CPU %d", cpuid); idle = init_task.prev_task; if (!idle) panic("No idle process for CPU %d", cpuid); - if (idle == &init_task) - panic("idle process is init_task for CPU %d", cpuid); - idle->processor = cpuid; - idle->cpus_runnable = 1 << cpuid; /* we schedule the first task manually */ + init_idle(idle, cpuid); + unhash_process(idle); + __cpu_logical_map[cpunum] = cpuid; __cpu_number_map[cpuid] = cpunum; - - del_from_runqueue(idle); - unhash_process(idle); - init_tasks[cpunum] = idle; DBGS(("smp_boot_one_cpu: CPU %d state 0x%lx flags 0x%lx\n", cpuid, idle->state, idle->flags)); - /* The secondary will change this once it is happy. Note that - secondary_cpu_start contains the necessary memory barrier. */ + /* Signal the secondary to wait a moment. */ smp_secondary_alive = -1; /* Whirrr, whirrr, whirrrrrrrrr... */ if (secondary_cpu_start(cpuid, idle)) return -1; + /* Notify the secondary CPU it can run calibrate_delay. */ mb(); - /* Notify the secondary CPU it can run calibrate_delay() */ smp_secondary_alive = 0; - /* We've been acked by the console; wait one second for the task - to start up for real. Note that jiffies aren't ticking yet. */ - for (timeout = 0; timeout < 1000000; timeout++) { + /* We've been acked by the console; wait one second for + the task to start up for real. */ + timeout = jiffies + 1*HZ; + while (time_before(jiffies, timeout)) { if (smp_secondary_alive == 1) goto alive; udelay(10); barrier(); } - /* we must invalidate our stuff as we failed to boot the CPU */ + /* We must invalidate our stuff as we failed to boot the CPU. */ __cpu_logical_map[cpunum] = -1; __cpu_number_map[cpuid] = -1; - /* the idle task is local to us so free it as we don't use it */ - free_task_struct(idle); - printk(KERN_ERR "SMP: Processor %d is stuck.\n", cpuid); return -1; -alive: + alive: /* Another "Red Snapper". */ return 0; } @@ -605,20 +572,15 @@ __cpu_number_map[boot_cpuid] = 0; __cpu_logical_map[0] = boot_cpuid; - current->processor = boot_cpuid; + current_thread_info()->cpu = boot_cpuid; smp_store_cpu_info(boot_cpuid); smp_tune_scheduling(boot_cpuid); smp_setup_percpu_timer(boot_cpuid); - init_idle(); - - /* ??? This should be in init_idle. */ - atomic_inc(&init_mm.mm_count); - current->active_mm = &init_mm; - /* Nothing to do on a UP box, or when told not to. */ if (smp_num_probed == 1 || max_cpus == 0) { + cpu_present_mask = 1UL << boot_cpuid; printk(KERN_INFO "SMP mode deactivated.\n"); return; } @@ -707,26 +669,35 @@ static void send_ipi_message(unsigned long to_whom, enum ipi_message_type operation) { - long i, j; + unsigned long i, set, n; - /* Reduce the number of memory barriers by doing two loops, - one to set the bits, one to invoke the interrupts. */ - - mb(); /* Order out-of-band data and bit setting. */ - - for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { - if (to_whom & j) - set_bit(operation, &ipi_data[i].bits); - } - - mb(); /* Order bit setting and interrupt. */ + set = to_whom & -to_whom; + if (to_whom == set) { + n = __ffs(set); + mb(); + set_bit(operation, &ipi_data[n].bits); + mb(); + wripir(n); + } else { + mb(); + for (i = to_whom; i ; i &= ~set) { + set = i & -i; + n = __ffs(set); + set_bit(operation, &ipi_data[n].bits); + } - for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { - if (to_whom & j) - wripir(i); + mb(); + for (i = to_whom; i ; i &= ~set) { + set = i & -i; + n = __ffs(set); + wripir(n); + } } } +/* Data for IPI_MIGRATION. */ +static task_t *migration_task; + /* Structure and data for smp_call_function. This is designed to minimize static memory requirements. Plus it looks cleaner. */ @@ -743,13 +714,13 @@ /* Atomicly drop data into a shared pointer. The pointer is free if it is initially locked. If retry, spin until free. */ -static inline int +static int pointer_lock (void *lock, void *data, int retry) { void *old, *tmp; mb(); -again: + again: /* Compare and swap with zero. */ asm volatile ( "1: ldq_l %0,%1\n" @@ -792,13 +763,25 @@ which = ops & -ops; ops &= ~which; - which = ffz(~which); + which = __ffs(which); - if (which == IPI_RESCHEDULE) { + switch (which) { + case IPI_RESCHEDULE: /* Reschedule callback. Everything to be done is done by the interrupt return path. */ - } - else if (which == IPI_CALL_FUNC) { + break; + + case IPI_MIGRATION: + { + task_t *t = migration_task; + mb(); + migration_task = 0; + sched_task_migrated(t); + break; + } + + case IPI_CALL_FUNC: + { struct smp_call_struct *data; void (*func)(void *info); void *info; @@ -821,13 +804,16 @@ /* Notify the sending CPU that the task is done. */ mb(); if (wait) atomic_dec (&data->unfinished_count); - } - else if (which == IPI_CPU_STOP) { + break; + } + + case IPI_CPU_STOP: halt(); - } - else { + + default: printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which); + break; } } while (ops); @@ -852,9 +838,22 @@ } void +smp_migrate_task(int cpu, task_t *t) +{ +#if DEBUG_IPI_MSG + if (cpu == hard_smp_processor_id()) + printk(KERN_WARNING + "smp_migrate_task: Sending IPI to self.\n"); +#endif + /* Acquire the migration_task mutex. */ + pointer_lock(&migration_task, t, 1); + send_ipi_message(1UL << cpu, IPI_MIGRATION); +} + +void smp_send_stop(void) { - unsigned long to_whom = cpu_present_mask ^ (1UL << smp_processor_id()); + unsigned long to_whom = cpu_present_mask & ~(1UL << smp_processor_id()); #if DEBUG_IPI_MSG if (hard_smp_processor_id() != boot_cpu_id) printk(KERN_WARNING "smp_send_stop: Not on boot cpu.\n"); @@ -881,16 +880,13 @@ struct smp_call_struct data; long timeout; int num_cpus_to_call; - long i,j; data.func = func; data.info = info; data.wait = wait; to_whom &= ~(1L << smp_processor_id()); - for (i = 0, j = 1, num_cpus_to_call = 0; i < NR_CPUS; ++i, j <<= 1) - if (to_whom & j) - num_cpus_to_call++; + num_cpus_to_call = hweight64(to_whom); atomic_set(&data.unstarted_count, num_cpus_to_call); atomic_set(&data.unfinished_count, num_cpus_to_call); @@ -1065,7 +1061,8 @@ } void -flush_icache_page(struct vm_area_struct *vma, struct page *page) +flush_icache_user_range(struct vm_area_struct *vma, struct page *page, + unsigned long addr, int len) { struct mm_struct *mm = vma->vm_mm; @@ -1094,7 +1091,7 @@ #ifdef CONFIG_DEBUG_SPINLOCK void -spin_unlock(spinlock_t * lock) +_raw_spin_unlock(spinlock_t * lock) { mb(); lock->lock = 0; @@ -1185,7 +1182,7 @@ #endif /* CONFIG_DEBUG_SPINLOCK */ #ifdef CONFIG_DEBUG_RWLOCK -void write_lock(rwlock_t * lock) +void _raw_write_lock(rwlock_t * lock) { long regx, regy; int stuck_lock, stuck_reader; @@ -1230,7 +1227,7 @@ } } -void read_lock(rwlock_t * lock) +void _raw_read_lock(rwlock_t * lock) { long regx; int stuck_lock; diff -Nru a/arch/alpha/kernel/srm_env.c b/arch/alpha/kernel/srm_env.c --- a/arch/alpha/kernel/srm_env.c Tue Feb 19 18:08:58 2002 +++ b/arch/alpha/kernel/srm_env.c Tue Feb 19 18:08:58 2002 @@ -43,6 +43,7 @@ #include #include #include +#include #define DIRNAME "srm_environment" /* Subdir in /proc/ */ #define VERSION "0.0.2" /* Module version */ diff -Nru a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c --- a/arch/alpha/kernel/traps.c Tue Feb 19 18:08:58 2002 +++ b/arch/alpha/kernel/traps.c Tue Feb 19 18:08:58 2002 @@ -131,8 +131,8 @@ void show_trace_task(struct task_struct * tsk) { - struct thread_struct * thread = &tsk->thread; - unsigned long fp, sp = thread->ksp, base = (unsigned long) thread; + struct thread_info *ti = tsk->thread_info; + unsigned long fp, sp = ti->pcb.ksp, base = (unsigned long) ti; if (sp > base && sp+6*8 < base + 16*1024) { fp = ((unsigned long*)sp)[6]; @@ -180,12 +180,11 @@ dik_show_trace((unsigned long *)(regs+1)); dik_show_code((unsigned int *)regs->pc); - if (current->thread.flags & (1UL << 63)) { + if (test_and_set_thread_flag (TIF_DIE_IF_KERNEL)) { printk("die_if_kernel recursion detected.\n"); sti(); while (1); } - current->thread.flags |= (1UL << 63); do_exit(SIGSEGV); } @@ -232,6 +231,13 @@ unsigned long a5, struct pt_regs regs) { if (!opDEC_testing || type != 4) { + if (type == 1) { + const unsigned int *data + = (const unsigned int *) regs.pc; + printk("Kernel bug at %s:%d\n", + (const char *)(data[1] | (long)data[2] << 32), + data[0]); + } die_if_kernel((type == 1 ? "Kernel Bug" : "Instruction fault"), ®s, type, 0); } @@ -324,8 +330,8 @@ FP registers, PAL_clrfen is not useful except for DoS attacks. So turn the bleeding FPU back on and be done with it. */ - current->thread.pal_flags |= 1; - __reload_thread(¤t->thread); + current_thread_info()->pcb.flags |= 1; + __reload_thread(¤t_thread_info()->pcb); return; case 5: /* illoc */ @@ -605,12 +611,11 @@ dik_show_code((unsigned int *)pc); dik_show_trace((unsigned long *)(®s+1)); - if (current->thread.flags & (1UL << 63)) { + if (test_and_set_thread_flag (TIF_DIE_IF_KERNEL)) { printk("die_if_kernel recursion detected.\n"); sti(); while (1); } - current->thread.flags |= (1UL << 63); do_exit(SIGSEGV); } @@ -706,14 +711,12 @@ unsigned long tmp1, tmp2, tmp3, tmp4; unsigned long fake_reg, *reg_addr = &fake_reg; - unsigned long uac_bits; long error; /* Check the UAC bits to decide what the user wants us to do with the unaliged access. */ - uac_bits = (current->thread.flags >> UAC_SHIFT) & UAC_BITMASK; - if (!(uac_bits & UAC_NOPRINT)) { + if (!test_thread_flag (TIF_UAC_NOPRINT)) { if (cnt >= 5 && jiffies - last_time > 5*HZ) { cnt = 0; } @@ -724,13 +727,11 @@ } last_time = jiffies; } - if (uac_bits & UAC_SIGBUS) { + if (test_thread_flag (TIF_UAC_SIGBUS)) goto give_sigbus; - } - if (uac_bits & UAC_NOFIX) { - /* Not sure why you'd want to use this, but... */ + /* Not sure why you'd want to use this, but... */ + if (test_thread_flag (TIF_UAC_NOFIX)) return; - } /* Don't bother reading ds in the access check since we already know that this came from the user. Also rely on the fact that diff -Nru a/arch/alpha/lib/ev6-memset.S b/arch/alpha/lib/ev6-memset.S --- a/arch/alpha/lib/ev6-memset.S Tue Feb 19 18:08:57 2002 +++ b/arch/alpha/lib/ev6-memset.S Tue Feb 19 18:08:57 2002 @@ -236,7 +236,7 @@ * entry point. */ .align 4 - .ent __memset + .ent __constant_c_memset __constant_c_memset: .frame $30,0,$26,0 .prologue 0 diff -Nru a/arch/alpha/math-emu/math.c b/arch/alpha/math-emu/math.c --- a/arch/alpha/math-emu/math.c Tue Feb 19 18:08:59 2002 +++ b/arch/alpha/math-emu/math.c Tue Feb 19 18:08:59 2002 @@ -114,7 +114,7 @@ mode = (insn >> 11) & 0x3; fpcr = rdfpcr(); - swcr = swcr_update_status(current->thread.flags, fpcr); + swcr = swcr_update_status(current_thread_info()->ieee_state, fpcr); if (mode == 3) { /* Dynamic -- get rounding mode from fpcr. */ @@ -297,7 +297,8 @@ if (_fex) { /* Record exceptions in software control word. */ swcr |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT); - current->thread.flags |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT); + current_thread_info()->ieee_state + |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT); /* Update hardware control register. */ fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); diff -Nru a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c --- a/arch/alpha/mm/fault.c Tue Feb 19 18:08:58 2002 +++ b/arch/alpha/mm/fault.c Tue Feb 19 18:08:58 2002 @@ -43,14 +43,16 @@ __load_new_mm_context(struct mm_struct *next_mm) { unsigned long mmc; + struct pcb_struct *pcb; mmc = __get_new_mm_context(next_mm, smp_processor_id()); next_mm->context[smp_processor_id()] = mmc; - current->thread.asn = mmc & HARDWARE_ASN_MASK; - current->thread.ptbr - = ((unsigned long) next_mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; - __reload_thread(¤t->thread); + pcb = ¤t_thread_info()->pcb; + pcb->asn = mmc & HARDWARE_ASN_MASK; + pcb->ptbr = ((unsigned long) next_mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; + + __reload_thread(pcb); } diff -Nru a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c --- a/arch/alpha/mm/init.c Tue Feb 19 18:08:59 2002 +++ b/arch/alpha/mm/init.c Tue Feb 19 18:08:59 2002 @@ -40,7 +40,7 @@ extern void die_if_kernel(char *,struct pt_regs *,long); -struct thread_struct original_pcb; +static struct pcb_struct original_pcb; #ifndef CONFIG_SMP struct pgtable_cache_struct quicklists; @@ -151,7 +151,7 @@ #endif static inline unsigned long -load_PCB(struct thread_struct * pcb) +load_PCB(struct pcb_struct *pcb) { register unsigned long sp __asm__("$30"); pcb->ksp = sp; @@ -182,10 +182,9 @@ } /* Also set up the real kernel PCB while we're at it. */ - init_task.thread.ptbr = newptbr; - init_task.thread.pal_flags = 1; /* set FEN, clear everything else */ - init_task.thread.flags = 0; - original_pcb_ptr = load_PCB(&init_task.thread); + init_thread_info.pcb.ptbr = newptbr; + init_thread_info.pcb.flags = 1; /* set FEN, clear everything else */ + original_pcb_ptr = load_PCB(&init_thread_info.pcb); tbia(); /* Save off the contents of the original PCB so that we can @@ -199,7 +198,7 @@ original_pcb_ptr = (unsigned long) phys_to_virt(original_pcb_ptr); } - original_pcb = *(struct thread_struct *) original_pcb_ptr; + original_pcb = *(struct pcb_struct *) original_pcb_ptr; } int callback_init_done; @@ -270,7 +269,7 @@ /* Let vmalloc know that we've allocated some space. */ console_remap_vm.flags = VM_ALLOC; - console_remap_vm.addr = VMALLOC_START; + console_remap_vm.addr = (void *) VMALLOC_START; console_remap_vm.size = vaddr - VMALLOC_START; vmlist = &console_remap_vm; } diff -Nru a/arch/alpha/vmlinux.lds.in b/arch/alpha/vmlinux.lds.in --- a/arch/alpha/vmlinux.lds.in Tue Feb 19 18:08:56 2002 +++ b/arch/alpha/vmlinux.lds.in Tue Feb 19 18:08:56 2002 @@ -11,37 +11,42 @@ . = 0xfffffc0000810000; #endif - _text = .; - .text : { *(.text) } :kernel - _etext = .; + .text : { + _text = .; + *(.text) + _etext = .; + } :kernel /* Exception table */ - . = ALIGN(16); - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; + __ex_table ALIGN(16) : { + __start___ex_table = .; + *(__ex_table) + __stop___ex_table = .; + } /* Kernel symbol table */ - . = ALIGN(8); - __start___ksymtab = .; - __ksymtab : { *(__ksymtab) } - __stop___ksymtab = .; + __ksymtab ALIGN(8) : { + __start___ksymtab = .; + *(__ksymtab) + __stop___ksymtab = .; + } .kstrtab : { *(.kstrtab) } /* Startup code */ - . = ALIGN(8192); - __init_begin = .; - .text.init : { *(.text.init) } + .text.init ALIGN(8192) : { + __init_begin = .; + *(.text.init) + } .data.init : { *(.data.init) } - . = ALIGN(16); - __setup_start = .; - .setup.init : { *(.setup.init) } - __setup_end = .; - - . = ALIGN(8); - __initcall_start = .; - .initcall.init : { + .setup.init ALIGN(16): { + __setup_start = .; + *(.setup.init) + __setup_end = .; + } + + .initcall.init ALIGN(8): { + __initcall_start = .; *(.initcall1.init) *(.initcall2.init) *(.initcall3.init) @@ -49,29 +54,37 @@ *(.initcall5.init) *(.initcall6.init) *(.initcall7.init) + __initcall_end = .; } - __initcall_end = .; - - . = ALIGN(2*8192); /* Align double page for init_task_union */ - __init_end = .; /* The initial task and kernel stack */ - init_task : { *(init_task) } + .data.init_thread ALIGN(2*8192) : { + __init_end = .; + *(.data.init_thread) + } /* Global data */ - _data = .; - .data.cacheline_aligned : { *(.data.cacheline_aligned) } + .data.cacheline_aligned : { + _data = .; + *(.data.cacheline_aligned) + } .rodata : { *(.rodata) *(.rodata.*) } .data : { *(.data) CONSTRUCTORS } .got : { *(.got) } - .sdata : { *(.sdata) } - _edata = .; + .sdata : { + *(.sdata) + _edata = .; + } - __bss_start = .; - .sbss : { *(.sbss) *(.scommon) } - .bss : { *(.bss) *(COMMON) } - __bss_stop = .; - _end = .; + .sbss : { + __bss_start = .; + *(.sbss) *(.scommon) + } + .bss : { + *(.bss) *(COMMON) + __bss_stop = .; + _end = .; + } .mdebug 0 : { *(.mdebug) } .note 0 : { *(.note) } diff -Nru a/arch/arm/config.in b/arch/arm/config.in --- a/arch/arm/config.in Tue Feb 19 18:08:58 2002 +++ b/arch/arm/config.in Tue Feb 19 18:08:58 2002 @@ -656,7 +656,7 @@ tristate 'Sound support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in + source sound/Config.in fi endmenu fi diff -Nru a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c --- a/arch/arm/kernel/ecard.c Tue Feb 19 18:09:00 2002 +++ b/arch/arm/kernel/ecard.c Tue Feb 19 18:09:00 2002 @@ -308,7 +308,7 @@ */ sigfillset(&tsk->blocked); sigemptyset(&tsk->pending.signal); - recalc_sigpending(tsk); + recalc_sigpending(); strcpy(tsk->comm, "kecardd"); daemonize(); diff -Nru a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c --- a/arch/arm/kernel/signal.c Tue Feb 19 18:08:57 2002 +++ b/arch/arm/kernel/signal.c Tue Feb 19 18:08:57 2002 @@ -98,7 +98,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->ARM_r0 = -EINTR; @@ -126,7 +126,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->ARM_r0 = -EINTR; @@ -245,7 +245,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(regs, &frame->sc)) @@ -285,7 +285,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) @@ -512,7 +512,7 @@ sigorsets(&tsk->blocked, &tsk->blocked, &ka->sa.sa_mask); sigaddset(&tsk->blocked, sig); - recalc_sigpending(tsk); + recalc_sigpending(); spin_unlock_irq(&tsk->sigmask_lock); } return; diff -Nru a/arch/cris/config.in b/arch/cris/config.in --- a/arch/cris/config.in Tue Feb 19 18:08:57 2002 +++ b/arch/cris/config.in Tue Feb 19 18:08:57 2002 @@ -222,7 +222,7 @@ tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in + source sound/Config.in fi endmenu diff -Nru a/arch/cris/drivers/eeprom.c b/arch/cris/drivers/eeprom.c --- a/arch/cris/drivers/eeprom.c Tue Feb 19 18:08:59 2002 +++ b/arch/cris/drivers/eeprom.c Tue Feb 19 18:08:59 2002 @@ -470,17 +470,17 @@ /* truncate position */ if (file->f_pos < 0) { - file->f_pos = 0; - unlock_kernel(); + file->f_pos = 0; ret = -EOVERFLOW; } - + if (file->f_pos >= eeprom.size) { file->f_pos = eeprom.size - 1; ret = -EOVERFLOW; } + unlock_kernel(); return ( ret ); } diff -Nru a/arch/cris/drivers/ide.c b/arch/cris/drivers/ide.c --- a/arch/cris/drivers/ide.c Tue Feb 19 18:08:58 2002 +++ b/arch/cris/drivers/ide.c Tue Feb 19 18:08:58 2002 @@ -727,7 +727,7 @@ rq = HWGROUP(drive)->rq; for (i = rq->nr_sectors; i > 0;) { i -= rq->current_nr_sectors; - ide_end_request(1, HWGROUP(drive)); + ide_end_request(drive, 1); } return ide_stopped; } diff -Nru a/arch/cris/kernel/signal.c b/arch/cris/kernel/signal.c --- a/arch/cris/kernel/signal.c Tue Feb 19 18:08:57 2002 +++ b/arch/cris/kernel/signal.c Tue Feb 19 18:08:57 2002 @@ -94,7 +94,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->r10 = -EINTR; @@ -133,7 +133,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->r10 = -EINTR; @@ -275,7 +275,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(regs, &frame->sc)) @@ -315,7 +315,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) @@ -560,7 +560,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/i386/config.in b/arch/i386/config.in --- a/arch/i386/config.in Tue Feb 19 18:08:59 2002 +++ b/arch/i386/config.in Tue Feb 19 18:08:59 2002 @@ -153,16 +153,27 @@ tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID choice 'High Memory Support' \ - "off CONFIG_NOHIGHMEM \ - 4GB CONFIG_HIGHMEM4G \ - 64GB CONFIG_HIGHMEM64G" off + "off CONFIG_NOHIGHMEM \ + 4GB CONFIG_HIGHMEM4G \ + 4GB-highpte CONFIG_HIGHMEM4G_HIGHPTE \ + 64GB CONFIG_HIGHMEM64G \ + 64GB-highpte CONFIG_HIGHMEM64G_HIGHPTE" off if [ "$CONFIG_HIGHMEM4G" = "y" ]; then define_bool CONFIG_HIGHMEM y fi +if [ "$CONFIG_HIGHMEM4G_HIGHPTE" = "y" ]; then + define_bool CONFIG_HIGHMEM y + define_bool CONFIG_HIGHPTE y +fi if [ "$CONFIG_HIGHMEM64G" = "y" ]; then define_bool CONFIG_HIGHMEM y define_bool CONFIG_X86_PAE y fi +if [ "$CONFIG_HIGHMEM64G_HIGHPTE" = "y" ]; then + define_bool CONFIG_HIGHMEM y + define_bool CONFIG_HIGHPTE y + define_bool CONFIG_X86_PAE y +fi bool 'Math emulation' CONFIG_MATH_EMULATION bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR @@ -382,7 +393,7 @@ tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in + source sound/Config.in fi endmenu @@ -397,12 +408,13 @@ bool 'Kernel debugging' CONFIG_DEBUG_KERNEL if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then - bool ' Debug high memory support' CONFIG_DEBUG_HIGHMEM bool ' Debug memory allocations' CONFIG_DEBUG_SLAB bool ' Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK - bool ' Verbose BUG() reporting (adds 70K)' CONFIG_DEBUG_BUGVERBOSE + if [ "$CONFIG_HIGHMEM" = "y" ]; then + bool ' Highmem debugging' CONFIG_DEBUG_HIGHMEM + fi fi endmenu diff -Nru a/arch/i386/defconfig b/arch/i386/defconfig --- a/arch/i386/defconfig Tue Feb 19 18:08:58 2002 +++ b/arch/i386/defconfig Tue Feb 19 18:08:58 2002 @@ -64,7 +64,9 @@ # CONFIG_X86_CPUID is not set CONFIG_NOHIGHMEM=y # CONFIG_HIGHMEM4G is not set +# CONFIG_HIGHMEM4G_HIGHPTE is not set # CONFIG_HIGHMEM64G is not set +# CONFIG_HIGHMEM64G_HIGHPTE is not set # CONFIG_MATH_EMULATION is not set # CONFIG_MTRR is not set CONFIG_SMP=y @@ -698,28 +700,77 @@ # Sound # CONFIG_SOUND=y -# CONFIG_SOUND_BT878 is not set -# CONFIG_SOUND_CMPCI is not set -# CONFIG_SOUND_EMU10K1 is not set -# CONFIG_MIDI_EMU10K1 is not set -# CONFIG_SOUND_FUSION is not set -# CONFIG_SOUND_CS4281 is not set -# CONFIG_SOUND_ES1370 is not set -CONFIG_SOUND_ES1371=y -# CONFIG_SOUND_ESSSOLO1 is not set -# CONFIG_SOUND_MAESTRO is not set -# CONFIG_SOUND_MAESTRO3 is not set -# CONFIG_SOUND_ICH is not set -# CONFIG_SOUND_RME96XX is not set -# CONFIG_SOUND_SONICVIBES is not set -# CONFIG_SOUND_TRIDENT is not set -# CONFIG_SOUND_MSNDCLAS is not set -# CONFIG_SOUND_MSNDPIN is not set -# CONFIG_SOUND_VIA82CXXX is not set -# CONFIG_MIDI_VIA82CXXX is not set -# CONFIG_SOUND_OSS is not set -# CONFIG_SOUND_TVMIXER is not set -CONFIG_INPUT_GAMEPORT=y + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=y +# CONFIG_SND_RTCTIMER is not set +CONFIG_SND_SEQUENCER=y +# CONFIG_SND_SEQ_DUMMY is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_SEQUENCER_OSS=y +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_VIRMIDI is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_AD1816A is not set +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4232 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_ES968 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_WAVEFRONT is not set +# CONFIG_SND_ALS100 is not set +# CONFIG_SND_AZT2320 is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_DT0197H is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_SGALAXY is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_ENS1370 is not set +CONFIG_SND_ENS1371=y +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_VIA686 is not set +# CONFIG_SND_VIA8233 is not set # # USB support diff -Nru a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c --- a/arch/i386/kernel/apm.c Tue Feb 19 18:08:57 2002 +++ b/arch/i386/kernel/apm.c Tue Feb 19 18:08:57 2002 @@ -39,6 +39,7 @@ * Feb 2000, Version 1.13 * Nov 2000, Version 1.14 * Oct 2001, Version 1.15 + * Jan 2002, Version 1.16 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -85,7 +86,7 @@ * change APM_NOINTS to CONFIG_APM_ALLOW_INTS * remove dependency on CONFIG_PROC_FS * Stephen Rothwell - * 1.9: Fix small typo. + * 1.9: Fix small typo. * Try to cope with BIOS's that need to have all display * devices blanked and not just the first one. * Ross Paterson @@ -164,8 +165,18 @@ * If an APM idle fails log it and idle sensibly * 1.15: Don't queue events to clients who open the device O_WRONLY. * Don't expect replies from clients who open the device O_RDONLY. - * (Idea from Thomas Hood ) - * Minor waitqueue cleanups.(John Fremlin ) + * (Idea from Thomas Hood ) + * Minor waitqueue cleanups. (John Fremlin ) + * 1.16: Fix idle calling. (Andreas Steinmetz et al.) + * Notify listeners of standby or suspend events before notifying + * drivers. Return EBUSY to ioctl() if suspend is rejected. + * (Russell King and Thomas Hood) + * Ignore first resume after we generate our own resume event + * after a suspend (Thomas Hood ) + * Daemonize now gets rid of our controlling terminal (sfr). + * CONFIG_APM_CPU_IDLE now just affects the default value of + * idle_threshold (sfr). + * Change name of kernel apm daemon (as it no longer idles) (sfr). * * APM 1.1 Reference: * @@ -238,6 +249,12 @@ * [no-]power[-_]off power off on shutdown * bounce[-_]interval= number of ticks to ignore suspend * bounces + * idle[-_]threshold= System idle percentage above which to + * make APM BIOS idle calls. Set it to + * 100 to disable. + * idle[-_]period= Period (in 1/100s of a second) over + * which the idle percentage is + * calculated. */ /* KNOWN PROBLEM MACHINES: @@ -341,15 +358,26 @@ #define APM_BIOS_MAGIC 0x4101 /* + * idle percentage above which bios idle calls are done + */ +#ifdef CONFIG_APM_CPU_IDLE +#define DEFAULT_IDLE_THRESHOLD 95 +#else +#define DEFAULT_IDLE_THRESHOLD 100 +#endif +#define DEFAULT_IDLE_PERIOD (100 / 3) + +/* * Local variables */ static struct { unsigned long offset; unsigned short segment; } apm_bios_entry; -#ifdef CONFIG_APM_CPU_IDLE static int clock_slowed; -#endif +static int idle_threshold = DEFAULT_IDLE_THRESHOLD; +static int idle_period = DEFAULT_IDLE_PERIOD; +static int set_pm_idle; static int suspends_pending; static int standbys_pending; static int waiting_for_resume; @@ -389,7 +417,7 @@ static struct apm_user * user_list; static spinlock_t user_list_lock = SPIN_LOCK_UNLOCKED; -static char driver_version[] = "1.15"; /* no spaces */ +static char driver_version[] = "1.16"; /* no spaces */ /* * APM event names taken from the APM 1.2 specification. These are @@ -685,8 +713,6 @@ return set_power_state(APM_DEVICE_ALL, state); } -#ifdef CONFIG_APM_CPU_IDLE - /** * apm_do_idle - perform power saving * @@ -736,63 +762,89 @@ } } -#if 0 -extern int hlt_counter; - /* - * If no process has been interested in this - * CPU for some time, we want to wake up the - * power management thread - we probably want + * If no process has really been interested in + * the CPU for some time, we want to call BIOS + * power management - we probably want * to conserve power. */ -#define HARD_IDLE_TIMEOUT (HZ/3) +#define IDLE_CALC_LIMIT (HZ * 100) +#define IDLE_LEAKY_MAX 16 -/* This should wake up kapmd and ask it to slow the CPU */ -#define powermanagement_idle() do { } while (0) +static void (*sys_idle)(void); + +extern void default_idle(void); /** * apm_cpu_idle - cpu idling for APM capable Linux * * This is the idling function the kernel executes when APM is available. It - * tries to save processor time directly by using hlt instructions. A - * separate apm thread tries to do the BIOS power management. - * - * N.B. This is curently not used for kernels 2.4.x. + * tries to do BIOS powermanagement based on the average system idle time. + * Furthermore it calls the system default idle routine. */ static void apm_cpu_idle(void) { - unsigned int start_idle; + static int use_apm_idle = 0; + static unsigned int last_jiffies = 0; + static unsigned int last_stime = 0; + + int apm_is_idle = 0; + unsigned int jiffies_since_last_check = jiffies - last_jiffies; + unsigned int t1; + + +recalc: + if (jiffies_since_last_check > IDLE_CALC_LIMIT) { + use_apm_idle = 0; + last_jiffies = jiffies; + last_stime = current->times.tms_stime; + } else if (jiffies_since_last_check > idle_period) { + unsigned int idle_percentage; + + idle_percentage = current->times.tms_stime - last_stime; + idle_percentage *= 100; + idle_percentage /= jiffies_since_last_check; + use_apm_idle = (idle_percentage > idle_threshold); + last_jiffies = jiffies; + last_stime = current->times.tms_stime; + } + + t1 = IDLE_LEAKY_MAX; + + while (need_resched()) { + if (use_apm_idle) { + unsigned int t; - start_idle = jiffies; - while (1) { - if (!need_resched()) { - if (jiffies - start_idle < HARD_IDLE_TIMEOUT) { - if (!current_cpu_data.hlt_works_ok) - continue; - if (hlt_counter) + t = jiffies; + switch (apm_do_idle()) { + case 0: apm_is_idle = 1; + if (t != jiffies) { + if (t1) { + t1 = IDLE_LEAKY_MAX; + continue; + } + } else if (t1) { + t1--; continue; - __cli(); - if (!need_resched()) - safe_halt(); - else - __sti(); - continue; + } + break; + case 1: apm_is_idle = 1; + break; } - - /* - * Ok, do some power management - we've been idle for too long - */ - powermanagement_idle(); } - - schedule(); - check_pgt_cache(); - start_idle = jiffies; + if (sys_idle) + sys_idle(); + else + default_idle(); + jiffies_since_last_check = jiffies - last_jiffies; + if (jiffies_since_last_check > idle_period) + goto recalc; } + + if (apm_is_idle) + apm_do_busy(); } -#endif -#endif #ifdef CONFIG_SMP static int apm_magic(void * unused) @@ -1137,59 +1189,44 @@ #endif } -static int send_event(apm_event_t event) +static int suspend(int vetoable) { - switch (event) { - case APM_SYS_SUSPEND: - case APM_CRITICAL_SUSPEND: - case APM_USER_SUSPEND: - /* map all suspends to ACPI D3 */ - if (pm_send_all(PM_SUSPEND, (void *)3)) { - if (event == APM_CRITICAL_SUSPEND) { - printk(KERN_CRIT - "apm: Critical suspend was vetoed, " - "expect armageddon\n" ); - return 0; - } + int err; + struct apm_user *as; + + if (pm_send_all(PM_SUSPEND, (void *)3)) { + /* Vetoed */ + if (vetoable) { if (apm_info.connection_version > 0x100) apm_set_power_state(APM_STATE_REJECT); - return 0; + err = -EBUSY; + waiting_for_resume = 0; + printk(KERN_WARNING "apm: suspend was vetoed.\n"); + goto out; } - break; - case APM_NORMAL_RESUME: - case APM_CRITICAL_RESUME: - /* map all resumes to ACPI D0 */ - (void) pm_send_all(PM_RESUME, (void *)0); - break; + printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n"); } - - return 1; -} - -static int suspend(void) -{ - int err; - struct apm_user *as; - get_time_diff(); cli(); err = apm_set_power_state(APM_STATE_SUSPEND); reinit_timer(); set_time(); + sti(); if (err == APM_NO_ERROR) err = APM_SUCCESS; if (err != APM_SUCCESS) apm_error("suspend", err); - send_event(APM_NORMAL_RESUME); - sti(); + err = (err == APM_SUCCESS) ? 0 : -EIO; + pm_send_all(PM_RESUME, (void *)0); queue_event(APM_NORMAL_RESUME, NULL); + ignore_normal_resume = 1; + out: spin_lock(&user_list_lock); for (as = user_list; as != NULL; as = as->next) { as->suspend_wait = 0; - as->suspend_result = ((err == APM_SUCCESS) ? 0 : -EIO); + as->suspend_result = err; } spin_unlock(&user_list_lock); - ignore_normal_resume = 1; wake_up_interruptible(&apm_suspend_waitqueue); return err; } @@ -1198,6 +1235,7 @@ { int err; + /* If needed, notify drivers here */ get_time_diff(); err = apm_set_power_state(APM_STATE_STANDBY); if ((err != APM_SUCCESS) && (err != APM_NO_ERROR)) @@ -1241,17 +1279,13 @@ if (ignore_bounce && ((jiffies - last_resume) > bounce_interval)) ignore_bounce = 0; - if (ignore_normal_resume && (event != APM_NORMAL_RESUME)) - ignore_normal_resume = 0; switch (event) { case APM_SYS_STANDBY: case APM_USER_STANDBY: - if (send_event(event)) { - queue_event(event, NULL); - if (standbys_pending <= 0) - standby(); - } + queue_event(event, NULL); + if (standbys_pending <= 0) + standby(); break; case APM_USER_SUSPEND: @@ -1276,12 +1310,10 @@ */ if (waiting_for_resume) return; - if (send_event(event)) { - queue_event(event, NULL); - waiting_for_resume = 1; - if (suspends_pending <= 0) - (void) suspend(); - } + waiting_for_resume = 1; + queue_event(event, NULL); + if (suspends_pending <= 0) + (void) suspend(1); break; case APM_NORMAL_RESUME: @@ -1293,16 +1325,17 @@ if ((event != APM_NORMAL_RESUME) || (ignore_normal_resume == 0)) { set_time(); - send_event(event); + pm_send_all(PM_RESUME, (void *)0); queue_event(event, NULL); } + ignore_normal_resume = 0; break; case APM_CAPABILITY_CHANGE: case APM_LOW_BATTERY: case APM_POWER_STATUS_CHANGE: - send_event(event); queue_event(event, NULL); + /* If needed, notify drivers here */ break; case APM_UPDATE_TIME: @@ -1310,12 +1343,10 @@ break; case APM_CRITICAL_SUSPEND: - send_event(event); /* - * We can only hope it worked - we are not allowed - * to reject a critical suspend. + * We are not allowed to reject a critical suspend. */ - (void) suspend(); + (void) suspend(0); break; } } @@ -1343,63 +1374,24 @@ /* * This is the APM thread main loop. - * - * Check whether we're the only running process to - * decide if we should just power down. - * */ -#define system_idle() (nr_running == 1) static void apm_mainloop(void) { - int timeout = HZ; DECLARE_WAITQUEUE(wait, current); add_wait_queue(&apm_waitqueue, &wait); set_current_state(TASK_INTERRUPTIBLE); for (;;) { - /* Nothing to do, just sleep for the timeout */ - timeout = 2 * timeout; - if (timeout > APM_CHECK_TIMEOUT) - timeout = APM_CHECK_TIMEOUT; - schedule_timeout(timeout); + schedule_timeout(APM_CHECK_TIMEOUT); if (exit_kapmd) break; - /* * Ok, check all events, check for idle (and mark us sleeping * so as not to count towards the load average).. */ set_current_state(TASK_INTERRUPTIBLE); apm_event_handler(); -#ifdef CONFIG_APM_CPU_IDLE - if (!system_idle()) - continue; - - /* - * If we can idle... - */ - if (apm_do_idle() != -1) { - unsigned long start = jiffies; - while ((!exit_kapmd) && system_idle()) { - if (apm_do_idle()) { - set_current_state(TASK_INTERRUPTIBLE); - /* APM needs us to snooze .. either - the BIOS call failed (-1) or it - slowed the clock (1). We sleep - until it talks to us again */ - schedule_timeout(1); - } - if ((jiffies - start) > APM_CHECK_TIMEOUT) { - apm_event_handler(); - start = jiffies; - } - } - apm_do_busy(); - apm_event_handler(); - timeout = 1; - } -#endif } remove_wait_queue(&apm_waitqueue, &wait); } @@ -1485,9 +1477,7 @@ as->standbys_read--; as->standbys_pending--; standbys_pending--; - } else if (!send_event(APM_USER_STANDBY)) - return -EAGAIN; - else + } else queue_event(APM_USER_STANDBY, as); if (standbys_pending <= 0) standby(); @@ -1497,13 +1487,10 @@ as->suspends_read--; as->suspends_pending--; suspends_pending--; - } else if (!send_event(APM_USER_SUSPEND)) - return -EAGAIN; - else + } else queue_event(APM_USER_SUSPEND, as); if (suspends_pending <= 0) { - if (suspend() != APM_SUCCESS) - return -EIO; + return suspend(1); } else { as->suspend_wait = 1; wait_event_interruptible(apm_suspend_waitqueue, @@ -1533,7 +1520,7 @@ if (as->suspends_pending > 0) { suspends_pending -= as->suspends_pending; if (suspends_pending <= 0) - (void) suspend(); + (void) suspend(1); } spin_lock(&user_list_lock); if (user_list == as) @@ -1684,9 +1671,8 @@ daemonize(); - strcpy(current->comm, "kapm-idled"); + strcpy(current->comm, "kapmd"); sigfillset(¤t->blocked); - current->tty = NULL; /* get rid of controlling tty */ if (apm_info.connection_version == 0) { apm_info.connection_version = apm_info.bios.version; @@ -1805,7 +1791,14 @@ if ((strncmp(str, "bounce-interval=", 16) == 0) || (strncmp(str, "bounce_interval=", 16) == 0)) bounce_interval = simple_strtol(str + 16, NULL, 0); - invert = (strncmp(str, "no-", 3) == 0); + if ((strncmp(str, "idle-threshold=", 15) == 0) || + (strncmp(str, "idle_threshold=", 15) == 0)) + idle_threshold = simple_strtol(str + 15, NULL, 0); + if ((strncmp(str, "idle-period=", 12) == 0) || + (strncmp(str, "idle_period=", 12) == 0)) + idle_threshold = simple_strtol(str + 15, NULL, 0); + invert = (strncmp(str, "no-", 3) == 0) || + (strncmp(str, "no_", 3) == 0); if (invert) str += 3; if (strncmp(str, "debug", 5) == 0) @@ -1976,6 +1969,14 @@ misc_register(&apm_device); + if (HZ != 100) + idle_period = (idle_period * HZ) / 100; + if (idle_threshold < 100) { + sys_idle = pm_idle; + pm_idle = apm_cpu_idle; + set_pm_idle = 1; + } + return 0; } @@ -1983,6 +1984,8 @@ { int error; + if (set_pm_idle) + pm_idle = sys_idle; if (((apm_info.bios.flags & APM_BIOS_DISENGAGED) == 0) && (apm_info.connection_version > 0x0100)) { error = apm_engage_power_management(APM_DEVICE_ALL, 0); @@ -2020,5 +2023,11 @@ MODULE_PARM(realmode_power_off, "i"); MODULE_PARM_DESC(realmode_power_off, "Switch to real mode before powering off"); +MODULE_PARM(idle_threshold, "i"); +MODULE_PARM_DESC(idle_threshold, + "System idle percentage above which to make APM BIOS idle calls"); +MODULE_PARM(idle_period, "i"); +MODULE_PARM_DESC(idle_period, + "Period (in sec/100) over which to caculate the idle percentage"); EXPORT_NO_SYMBOLS; diff -Nru a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S --- a/arch/i386/kernel/entry.S Tue Feb 19 18:08:58 2002 +++ b/arch/i386/kernel/entry.S Tue Feb 19 18:08:58 2002 @@ -69,24 +69,27 @@ NT_MASK = 0x00004000 VM_MASK = 0x00020000 -/* These are offsets into the irq_stat structure +/* + * These are offsets into the irq_stat structure * There is one per cpu and it is aligned to 32 * byte boundry (we put that here as a shift count) */ -irq_array_shift = CONFIG_X86_L1_CACHE_SHIFT -irq_stat_local_irq_count = 4 -irq_stat_local_bh_count = 8 +irq_array_shift = CONFIG_X86_L1_CACHE_SHIFT +local_irq_count = 4 +local_bh_count = 8 #ifdef CONFIG_SMP -#define GET_CPU_INDX movl TI_CPU(%ebx),%eax; \ - shll $irq_array_shift,%eax -#define GET_CURRENT_CPU_INDX GET_THREAD_INFO(%ebx); \ - GET_CPU_INDX -#define CPU_INDX (,%eax) +#define GET_CPU_IDX \ + movl TI_CPU(%ebx), %eax; \ + shll $irq_array_shift, %eax +#define GET_CURRENT_CPU_IDX \ + GET_THREAD_INFO(%ebx); \ + GET_CPU_IDX +#define CPU_IDX (,%eax) #else -#define GET_CPU_INDX -#define GET_CURRENT_CPU_INDX GET_THREAD_INFO(%ebx) -#define CPU_INDX +#define GET_CPU_IDX +#define GET_CURRENT_CPU_IDX GET_THREAD_INFO(%ebx) +#define CPU_IDX #endif #ifdef CONFIG_PREEMPT @@ -111,9 +114,9 @@ pushl %edx; \ pushl %ecx; \ pushl %ebx; \ - movl $(__KERNEL_DS),%edx; \ - movl %edx,%ds; \ - movl %edx,%es; + movl $(__KERNEL_DS), %edx; \ + movl %edx, %ds; \ + movl %edx, %es; #define RESTORE_ALL \ popl %ebx; \ @@ -125,7 +128,7 @@ popl %eax; \ 1: popl %ds; \ 2: popl %es; \ - addl $4,%esp; \ + addl $4, %esp; \ 3: iret; \ .section .fixup,"ax"; \ 4: movl $0,(%esp); \ @@ -147,20 +150,21 @@ .previous ENTRY(lcall7) - pushfl # We get a different stack layout with call gates, - pushl %eax # which has to be cleaned up later.. + pushfl # We get a different stack layout with call + # gates, which has to be cleaned up later.. + pushl %eax SAVE_ALL - movl EIP(%esp),%eax # due to call gates, this is eflags, not eip.. - movl CS(%esp),%edx # this is eip.. - movl EFLAGS(%esp),%ecx # and this is cs.. + movl EIP(%esp), %eax # due to call gates, this is eflags, not eip.. + movl CS(%esp), %edx # this is eip.. + movl EFLAGS(%esp), %ecx # and this is cs.. movl %eax,EFLAGS(%esp) # movl %edx,EIP(%esp) # Now we move them to their "normal" places movl %ecx,CS(%esp) # - movl %esp,%ebx + movl %esp, %ebx pushl %ebx - andl $-8192,%ebx # GET_THREAD_INFO - movl TI_EXEC_DOMAIN(%ebx),%edx # Get the execution domain - movl 4(%edx),%edx # Get the lcall7 handler for the domain + andl $-8192, %ebx # GET_THREAD_INFO + movl TI_EXEC_DOMAIN(%ebx), %edx # Get the execution domain + movl 4(%edx), %edx # Get the lcall7 handler for the domain pushl $0x7 call *%edx addl $4, %esp @@ -168,20 +172,21 @@ jmp resume_userspace ENTRY(lcall27) - pushfl # We get a different stack layout with call gates, - pushl %eax # which has to be cleaned up later.. + pushfl # We get a different stack layout with call + # gates, which has to be cleaned up later.. + pushl %eax SAVE_ALL - movl EIP(%esp),%eax # due to call gates, this is eflags, not eip.. - movl CS(%esp),%edx # this is eip.. - movl EFLAGS(%esp),%ecx # and this is cs.. + movl EIP(%esp), %eax # due to call gates, this is eflags, not eip.. + movl CS(%esp), %edx # this is eip.. + movl EFLAGS(%esp), %ecx # and this is cs.. movl %eax,EFLAGS(%esp) # movl %edx,EIP(%esp) # Now we move them to their "normal" places movl %ecx,CS(%esp) # - movl %esp,%ebx + movl %esp, %ebx pushl %ebx - andl $-8192,%ebx # GET_THREAD_INFO - movl TI_EXEC_DOMAIN(%ebx),%edx # Get the execution domain - movl 4(%edx),%edx # Get the lcall7 handler for the domain + andl $-8192, %ebx # GET_THREAD_INFO + movl TI_EXEC_DOMAIN(%ebx), %edx # Get the execution domain + movl 4(%edx), %edx # Get the lcall7 handler for the domain pushl $0x27 call *%edx addl $4, %esp @@ -190,9 +195,9 @@ ENTRY(ret_from_fork) - pushl %ebx +#if CONFIG_SMP call SYMBOL_NAME(schedule_tail) - addl $4, %esp +#endif GET_THREAD_INFO(%ebx) jmp syscall_exit @@ -209,15 +214,17 @@ GET_THREAD_INFO(%ebx) init_ret_intr ret_from_exception: - movl EFLAGS(%esp),%eax # mix EFLAGS and CS - movb CS(%esp),%al - testl $(VM_MASK | 3),%eax + movl EFLAGS(%esp), %eax # mix EFLAGS and CS + movb CS(%esp), %al + testl $(VM_MASK | 3), %eax jz resume_kernel # returning to kernel or vm86-space ENTRY(resume_userspace) - cli # make sure we don't miss an interrupt setting need_resched - # or sigpending between sampling and the iret - movl TI_FLAGS(%ebx),%ecx - andl $_TIF_WORK_MASK,%ecx # is there any work to be done on int/excp return? + cli # make sure we don't miss an interrupt + # setting need_resched or sigpending + # between sampling and the iret + movl TI_FLAGS(%ebx), %ecx + andl $_TIF_WORK_MASK, %ecx # is there any work to be done on + # int/exception return? jne work_pending jmp restore_all @@ -225,15 +232,17 @@ ENTRY(resume_kernel) cmpl $0,TI_PRE_COUNT(%ebx) jnz restore_all - movl TI_FLAGS(%ebx),%ecx - testb $_TIF_NEED_RESCHED,%cl + movl TI_FLAGS(%ebx), %ecx + testb $_TIF_NEED_RESCHED, %cl jz restore_all - movl SYMBOL_NAME(irq_stat)+irq_stat_local_bh_count CPU_INDX,%ecx - addl SYMBOL_NAME(irq_stat)+irq_stat_local_irq_count CPU_INDX,%ecx + movl SYMBOL_NAME(irq_stat)+local_bh_count CPU_IDX, %ecx + addl SYMBOL_NAME(irq_stat)+local_irq_count CPU_IDX, %ecx jnz restore_all incl TI_PRE_COUNT(%ebx) sti - call SYMBOL_NAME(preempt_schedule) + movl TI_TASK(%ebx), %ecx # ti->task + movl $0,(%ecx) # current->state = TASK_RUNNING + call SYMBOL_NAME(schedule) jmp ret_from_intr #endif @@ -243,18 +252,20 @@ pushl %eax # save orig_eax SAVE_ALL GET_THREAD_INFO(%ebx) - cmpl $(NR_syscalls),%eax + cmpl $(NR_syscalls), %eax jae syscall_badsys - testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebx) # system call tracing in operation + # system call tracing in operation + testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebx) jnz syscall_trace_entry syscall_call: call *SYMBOL_NAME(sys_call_table)(,%eax,4) movl %eax,EAX(%esp) # store the return value syscall_exit: - cli # make sure we don't miss an interrupt setting need_resched - # or sigpending between sampling and the iret - movl TI_FLAGS(%ebx),%ecx - testw $_TIF_ALLWORK_MASK,%cx # current->work + cli # make sure we don't miss an interrupt + # setting need_resched or sigpending + # between sampling and the iret + movl TI_FLAGS(%ebx), %ecx + testw $_TIF_ALLWORK_MASK, %cx # current->work jne syscall_exit_work restore_all: RESTORE_ALL @@ -262,23 +273,27 @@ # perform work that needs to be done immediately before resumption ALIGN work_pending: - testb $_TIF_NEED_RESCHED,%cl + testb $_TIF_NEED_RESCHED, %cl jz work_notifysig work_resched: call SYMBOL_NAME(schedule) - cli # make sure we don't miss an interrupt setting need_resched - # or sigpending between sampling and the iret - movl TI_FLAGS(%ebx),%ecx - andl $_TIF_WORK_MASK,%ecx # is there any work to be done other than syscall tracing? + cli # make sure we don't miss an interrupt + # setting need_resched or sigpending + # between sampling and the iret + movl TI_FLAGS(%ebx), %ecx + andl $_TIF_WORK_MASK, %ecx # is there any work to be done other + # than syscall tracing? jz restore_all - testb $_TIF_NEED_RESCHED,%cl + testb $_TIF_NEED_RESCHED, %cl jnz work_resched -work_notifysig: # deal with pending signals and notify-resume requests +work_notifysig: # deal with pending signals and + # notify-resume requests testl $(VM_MASK),EFLAGS(%esp) - movl %esp,%eax - jne work_notifysig_v86 # returning to kernel-space or vm86-space - xorl %edx,%edx + movl %esp, %eax + jne work_notifysig_v86 # returning to kernel-space or + # vm86-space + xorl %edx, %edx call SYMBOL_NAME(do_notify_resume) jmp restore_all @@ -287,8 +302,8 @@ pushl %ecx call SYMBOL_NAME(save_v86_state) popl %ecx - movl %eax,%esp - xorl %edx,%edx + movl %eax, %esp + xorl %edx, %edx call SYMBOL_NAME(do_notify_resume) jmp restore_all @@ -296,22 +311,23 @@ ALIGN syscall_trace_entry: movl $-ENOSYS,EAX(%esp) - movl %esp,%eax + movl %esp, %eax xorl %edx,%edx call SYMBOL_NAME(do_syscall_trace) - movl ORIG_EAX(%esp),%eax - cmpl $(NR_syscalls),%eax + movl ORIG_EAX(%esp), %eax + cmpl $(NR_syscalls), %eax jnae syscall_call jmp syscall_exit # perform syscall exit tracing ALIGN syscall_exit_work: - testb $_TIF_SYSCALL_TRACE,%cl + testb $_TIF_SYSCALL_TRACE, %cl jz work_pending - sti # could let do_syscall_trace() call schedule() instead - movl %esp,%eax - movl $1,%edx + sti # could let do_syscall_trace() call + # schedule() instead + movl %esp, %eax + movl $1, %edx call SYMBOL_NAME(do_syscall_trace) jmp resume_userspace @@ -321,13 +337,13 @@ jmp resume_userspace ENTRY(divide_error) - pushl $0 # no error code + pushl $0 # no error code pushl $ SYMBOL_NAME(do_divide_error) ALIGN error_code: pushl %ds pushl %eax - xorl %eax,%eax + xorl %eax, %eax pushl %ebp pushl %edi pushl %esi @@ -336,20 +352,20 @@ pushl %ecx pushl %ebx cld - movl %es,%ecx + movl %es, %ecx movl ORIG_EAX(%esp), %esi # get the error code movl ES(%esp), %edi # get the function address movl %eax, ORIG_EAX(%esp) movl %ecx, ES(%esp) - movl %esp,%edx + movl %esp, %edx pushl %esi # push the error code pushl %edx # push the pt_regs pointer - movl $(__KERNEL_DS),%edx - movl %edx,%ds - movl %edx,%es + movl $(__KERNEL_DS), %edx + movl %edx, %ds + movl %edx, %es GET_THREAD_INFO(%ebx) call *%edi - addl $8,%esp + addl $8, %esp preempt_stop jmp ret_from_exception @@ -364,19 +380,19 @@ jmp error_code ENTRY(device_not_available) - pushl $-1 # mark this as an int + pushl $-1 # mark this as an int SAVE_ALL GET_THREAD_INFO(%ebx) - movl %cr0,%eax - testl $0x4,%eax # EM (math emulation bit) + movl %cr0, %eax + testl $0x4, %eax # EM (math emulation bit) jne device_not_available_emulate preempt_stop call SYMBOL_NAME(math_state_restore) jmp ret_from_exception device_not_available_emulate: - pushl $0 # temporary storage for ORIG_EIP + pushl $0 # temporary storage for ORIG_EIP call SYMBOL_NAME(math_emulate) - addl $4,%esp + addl $4, %esp preempt_stop jmp ret_from_exception @@ -388,11 +404,11 @@ ENTRY(nmi) pushl %eax SAVE_ALL - movl %esp,%edx + movl %esp, %edx pushl $0 pushl %edx call SYMBOL_NAME(do_nmi) - addl $8,%esp + addl $8, %esp RESTORE_ALL ENTRY(int3) @@ -477,7 +493,7 @@ .long SYMBOL_NAME(sys_mknod) .long SYMBOL_NAME(sys_chmod) /* 15 */ .long SYMBOL_NAME(sys_lchown16) - .long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */ .long SYMBOL_NAME(sys_stat) .long SYMBOL_NAME(sys_lseek) .long SYMBOL_NAME(sys_getpid) /* 20 */ @@ -491,11 +507,12 @@ .long SYMBOL_NAME(sys_fstat) .long SYMBOL_NAME(sys_pause) .long SYMBOL_NAME(sys_utime) /* 30 */ - .long SYMBOL_NAME(sys_ni_syscall) /* old stty syscall holder */ - .long SYMBOL_NAME(sys_ni_syscall) /* old gtty syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old stty syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old gtty syscall holder */ .long SYMBOL_NAME(sys_access) .long SYMBOL_NAME(sys_nice) - .long SYMBOL_NAME(sys_ni_syscall) /* 35 */ /* old ftime syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* 35 */ + /* old ftime syscall holder */ .long SYMBOL_NAME(sys_sync) .long SYMBOL_NAME(sys_kill) .long SYMBOL_NAME(sys_rename) @@ -504,7 +521,7 @@ .long SYMBOL_NAME(sys_dup) .long SYMBOL_NAME(sys_pipe) .long SYMBOL_NAME(sys_times) - .long SYMBOL_NAME(sys_ni_syscall) /* old prof syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old prof syscall holder */ .long SYMBOL_NAME(sys_brk) /* 45 */ .long SYMBOL_NAME(sys_setgid16) .long SYMBOL_NAME(sys_getgid16) @@ -512,13 +529,13 @@ .long SYMBOL_NAME(sys_geteuid16) .long SYMBOL_NAME(sys_getegid16) /* 50 */ .long SYMBOL_NAME(sys_acct) - .long SYMBOL_NAME(sys_umount) /* recycled never used phys() */ - .long SYMBOL_NAME(sys_ni_syscall) /* old lock syscall holder */ + .long SYMBOL_NAME(sys_umount) /* recycled never used phys() */ + .long SYMBOL_NAME(sys_ni_syscall) /* old lock syscall holder */ .long SYMBOL_NAME(sys_ioctl) .long SYMBOL_NAME(sys_fcntl) /* 55 */ - .long SYMBOL_NAME(sys_ni_syscall) /* old mpx syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old mpx syscall holder */ .long SYMBOL_NAME(sys_setpgid) - .long SYMBOL_NAME(sys_ni_syscall) /* old ulimit syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old ulimit syscall holder */ .long SYMBOL_NAME(sys_olduname) .long SYMBOL_NAME(sys_umask) /* 60 */ .long SYMBOL_NAME(sys_chroot) @@ -558,7 +575,7 @@ .long SYMBOL_NAME(sys_fchown16) /* 95 */ .long SYMBOL_NAME(sys_getpriority) .long SYMBOL_NAME(sys_setpriority) - .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */ .long SYMBOL_NAME(sys_statfs) .long SYMBOL_NAME(sys_fstatfs) /* 100 */ .long SYMBOL_NAME(sys_ioperm) @@ -648,8 +665,8 @@ .long SYMBOL_NAME(sys_capset) /* 185 */ .long SYMBOL_NAME(sys_sigaltstack) .long SYMBOL_NAME(sys_sendfile) - .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ - .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ + .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ + .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ .long SYMBOL_NAME(sys_vfork) /* 190 */ .long SYMBOL_NAME(sys_getrlimit) .long SYMBOL_NAME(sys_mmap2) @@ -698,7 +715,7 @@ .long SYMBOL_NAME(sys_removexattr) /* 235 */ .long SYMBOL_NAME(sys_lremovexattr) .long SYMBOL_NAME(sys_fremovexattr) - .long SYMBOL_NAME(sys_tkill) + .long SYMBOL_NAME(sys_tkill) .rept NR_syscalls-(.-sys_call_table)/4 .long SYMBOL_NAME(sys_ni_syscall) diff -Nru a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c --- a/arch/i386/kernel/i386_ksyms.c Tue Feb 19 18:09:00 2002 +++ b/arch/i386/kernel/i386_ksyms.c Tue Feb 19 18:09:00 2002 @@ -32,9 +32,11 @@ extern void dump_thread(struct pt_regs *, struct user *); extern spinlock_t rtc_lock; -#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) +#if defined(CONFIG_APM_MODULE) extern void machine_real_restart(unsigned char *, int); EXPORT_SYMBOL(machine_real_restart); +extern void default_idle(void); +EXPORT_SYMBOL(default_idle); #endif #ifdef CONFIG_SMP @@ -93,7 +95,6 @@ EXPORT_SYMBOL(strtok); EXPORT_SYMBOL(strpbrk); -EXPORT_SYMBOL(simple_strtol); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strncpy_from_user); @@ -165,10 +166,6 @@ #ifdef CONFIG_HAVE_DEC_LOCK EXPORT_SYMBOL(atomic_dec_and_lock); -#endif - -#ifdef CONFIG_DEBUG_BUGVERBOSE -EXPORT_SYMBOL(do_BUG); #endif extern int is_sony_vaio_laptop; diff -Nru a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c --- a/arch/i386/kernel/process.c Tue Feb 19 18:08:56 2002 +++ b/arch/i386/kernel/process.c Tue Feb 19 18:08:56 2002 @@ -55,6 +55,14 @@ int hlt_counter; /* + * Return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + return ((unsigned long *)tsk->thread.esp)[3]; +} + +/* * Powermanagement idle function, if any.. */ void (*pm_idle)(void); @@ -78,7 +86,7 @@ * We use this if we don't have any better * idle routine.. */ -static void default_idle(void) +void default_idle(void) { if (current_cpu_data.hlt_works_ok && !hlt_counter) { __cli(); @@ -137,7 +145,6 @@ while (!need_resched()) idle(); schedule(); - check_pgt_cache(); } } diff -Nru a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c --- a/arch/i386/kernel/ptrace.c Tue Feb 19 18:08:58 2002 +++ b/arch/i386/kernel/ptrace.c Tue Feb 19 18:08:58 2002 @@ -455,9 +455,11 @@ between a syscall stop and SIGTRAP delivery */ current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0); + preempt_disable(); current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); + preempt_enable(); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff -Nru a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c --- a/arch/i386/kernel/setup.c Tue Feb 19 18:09:00 2002 +++ b/arch/i386/kernel/setup.c Tue Feb 19 18:09:00 2002 @@ -139,6 +139,9 @@ /* For PCI or other memory-mapped resources */ unsigned long pci_mem_start = 0x10000000; +/* user-defined highmem size */ +static unsigned int highmem_pages = -1; + /* * Setup options */ @@ -638,8 +641,15 @@ } } /* acpismp=force forces parsing and use of the ACPI SMP table */ - if (c == ' ' && !memcmp(from, "acpismp=force", 13)) + if (c == ' ' && !memcmp(from, "acpismp=force", 13)) enable_acpi_smp_table = 1; + /* + * highmem=size forces highmem to be exactly 'size' bytes. + * This works even on boxes that have no highmem otherwise. + * This also works to reduce highmem size on bigger boxes. + */ + if (c == ' ' && !memcmp(from, "highmem=", 8)) + highmem_pages = memparse(from+8, &from) >> PAGE_SHIFT; c = *(from++); if (!c) @@ -737,6 +747,14 @@ */ max_low_pfn = max_pfn; if (max_low_pfn > MAXMEM_PFN) { + if (highmem_pages == -1) + highmem_pages = max_pfn - MAXMEM_PFN; + if (highmem_pages + MAXMEM_PFN < max_pfn) + max_pfn = MAXMEM_PFN + highmem_pages; + if (highmem_pages + MAXMEM_PFN > max_pfn) { + printk("only %luMB highmem pages available, ignoring highmem size of %uMB.\n", pages_to_mb(max_pfn - MAXMEM_PFN), pages_to_mb(highmem_pages)); + highmem_pages = 0; + } max_low_pfn = MAXMEM_PFN; #ifndef CONFIG_HIGHMEM /* Maximum memory usable is what is directly addressable */ @@ -756,16 +774,37 @@ } #endif /* !CONFIG_X86_PAE */ #endif /* !CONFIG_HIGHMEM */ + } else { + if (highmem_pages == -1) + highmem_pages = 0; +#if CONFIG_HIGHMEM + if (highmem_pages >= max_pfn) { + printk(KERN_ERR "highmem size specified (%uMB) is bigger than pages available (%luMB)!.\n", pages_to_mb(highmem_pages), pages_to_mb(max_pfn)); + highmem_pages = 0; + } + if (highmem_pages) { + if (max_low_pfn-highmem_pages < 64*1024*1024/PAGE_SIZE){ + printk(KERN_ERR "highmem size %uMB results in smaller than 64MB lowmem, ignoring it.\n", pages_to_mb(highmem_pages)); + highmem_pages = 0; + } + max_low_pfn -= highmem_pages; + } +#else + if (highmem_pages) + printk(KERN_ERR "ignoring highmem size on non-highmem kernel!\n"); +#endif } #ifdef CONFIG_HIGHMEM highstart_pfn = highend_pfn = max_pfn; - if (max_pfn > MAXMEM_PFN) { - highstart_pfn = MAXMEM_PFN; - printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", - pages_to_mb(highend_pfn - highstart_pfn)); + if (max_pfn > max_low_pfn) { + highstart_pfn = max_low_pfn; } + printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", + pages_to_mb(highend_pfn - highstart_pfn)); #endif + printk(KERN_NOTICE "%ldMB LOWMEM available.\n", + pages_to_mb(max_low_pfn)); /* * Initialize the boot-time allocator (with low memory only): */ diff -Nru a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c --- a/arch/i386/kernel/signal.c Tue Feb 19 18:08:59 2002 +++ b/arch/i386/kernel/signal.c Tue Feb 19 18:08:59 2002 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->eax = -EINTR; @@ -105,7 +106,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->eax = -EINTR; @@ -262,7 +263,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(regs, &frame->sc, &eax)) @@ -290,7 +291,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &eax)) @@ -569,7 +570,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } @@ -609,9 +610,11 @@ if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { /* Let the debugger run. */ current->exit_code = signr; + preempt_disable(); current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); + preempt_enable(); /* We're back. Did the debugger cancel the sig? */ if (!(signr = current->exit_code)) @@ -666,12 +669,14 @@ case SIGSTOP: { struct signal_struct *sig; - current->state = TASK_STOPPED; current->exit_code = signr; sig = current->p_pptr->sig; + preempt_disable(); + current->state = TASK_STOPPED; if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); + preempt_enable(); continue; } diff -Nru a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c --- a/arch/i386/kernel/smpboot.c Tue Feb 19 18:09:00 2002 +++ b/arch/i386/kernel/smpboot.c Tue Feb 19 18:09:00 2002 @@ -146,10 +146,6 @@ struct cpuinfo_x86 *c = cpu_data + id; *c = boot_cpu_data; - c->pte_quick = 0; - c->pmd_quick = 0; - c->pgd_quick = 0; - c->pgtable_cache_sz = 0; identify_cpu(c); /* * Mask B, Pentium, but not Pentium MMX diff -Nru a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c --- a/arch/i386/kernel/traps.c Tue Feb 19 18:08:58 2002 +++ b/arch/i386/kernel/traps.c Tue Feb 19 18:08:58 2002 @@ -237,6 +237,41 @@ printk("\n"); } +static void handle_BUG(struct pt_regs *regs) +{ + unsigned short ud2; + unsigned short line; + char *file; + char c; + unsigned long eip; + + if (regs->xcs & 3) + goto no_bug; /* Not in kernel */ + + eip = regs->eip; + + if (eip < PAGE_OFFSET) + goto no_bug; + if (__get_user(ud2, (unsigned short *)eip)) + goto no_bug; + if (ud2 != 0x0b0f) + goto no_bug; + if (__get_user(line, (unsigned short *)(eip + 2))) + goto bug; + if (__get_user(file, (char **)(eip + 4)) || + (unsigned long)file < PAGE_OFFSET || __get_user(c, file)) + file = ""; + + printk("kernel BUG at %s:%d!\n", file, line); + +no_bug: + return; + + /* Here we know it was a BUG but file-n-line is unavailable */ +bug: + printk("Kernel BUG\n"); +} + spinlock_t die_lock = SPIN_LOCK_UNLOCKED; void die(const char * str, struct pt_regs * regs, long err) @@ -244,6 +279,7 @@ console_verbose(); spin_lock_irq(&die_lock); bust_spinlocks(1); + handle_BUG(regs); printk("%s: %04lx\n", str, err & 0xffff); show_registers(regs); bust_spinlocks(0); @@ -752,7 +788,7 @@ page = (unsigned long) vmalloc(PAGE_SIZE); pgd = pgd_offset(&init_mm, page); pmd = pmd_offset(pgd, page); - pte = pte_offset(pmd, page); + pte = pte_offset_kernel(pmd, page); __free_page(pte_page(*pte)); *pte = mk_pte_phys(__pa(&idt_table), PAGE_KERNEL_RO); /* diff -Nru a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c --- a/arch/i386/kernel/vm86.c Tue Feb 19 18:08:57 2002 +++ b/arch/i386/kernel/vm86.c Tue Feb 19 18:08:57 2002 @@ -93,7 +93,7 @@ { pgd_t *pgd; pmd_t *pmd; - pte_t *pte; + pte_t *pte, *mapped; int i; pgd = pgd_offset(tsk->mm, 0xA0000); @@ -112,12 +112,15 @@ pmd_clear(pmd); return; } - pte = pte_offset(pmd, 0xA0000); + preempt_disable(); + pte = mapped = pte_offset_map(pmd, 0xA0000); for (i = 0; i < 32; i++) { if (pte_present(*pte)) set_pte(pte, pte_wrprotect(*pte)); pte++; } + pte_unmap(mapped); + preempt_enable(); flush_tlb(); } @@ -438,7 +441,7 @@ unsigned long flags; spin_lock_irqsave(¤t->sigmask_lock, flags); sigdelset(¤t->blocked, SIGTRAP); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } send_sig(SIGTRAP, current, 1); diff -Nru a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c --- a/arch/i386/mm/fault.c Tue Feb 19 18:08:57 2002 +++ b/arch/i386/mm/fault.c Tue Feb 19 18:08:57 2002 @@ -126,12 +126,6 @@ } } -void do_BUG(const char *file, int line) -{ - bust_spinlocks(1); - printk("kernel BUG at %s:%d!\n", file, line); -} - asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); extern unsigned long idt; @@ -326,12 +320,20 @@ asm("movl %%cr3,%0":"=r" (page)); page = ((unsigned long *) __va(page))[address >> 22]; printk(KERN_ALERT "*pde = %08lx\n", page); + /* + * We must not directly access the pte in the highpte + * case, the page table might be allocated in highmem. + * And lets rather not kmap-atomic the pte, just in case + * it's allocated already. + */ +#ifndef CONFIG_HIGHPTE if (page & 1) { page &= PAGE_MASK; address &= 0x003ff000; page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; printk(KERN_ALERT "*pte = %08lx\n", page); } +#endif die("Oops", regs, error_code); bust_spinlocks(0); do_exit(SIGKILL); @@ -401,7 +403,7 @@ goto no_context; set_pmd(pmd, *pmd_k); - pte_k = pte_offset(pmd_k, address); + pte_k = pte_offset_kernel(pmd_k, address); if (!pte_present(*pte_k)) goto no_context; return; diff -Nru a/arch/i386/mm/init.c b/arch/i386/mm/init.c --- a/arch/i386/mm/init.c Tue Feb 19 18:08:58 2002 +++ b/arch/i386/mm/init.c Tue Feb 19 18:08:58 2002 @@ -43,28 +43,6 @@ static unsigned long totalram_pages; static unsigned long totalhigh_pages; -int do_check_pgt_cache(int low, int high) -{ - int freed = 0; - if(pgtable_cache_size > high) { - do { - if (pgd_quicklist) { - free_pgd_slow(get_pgd_fast()); - freed++; - } - if (pmd_quicklist) { - pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); - freed++; - } - if (pte_quicklist) { - pte_free_slow(pte_alloc_one_fast(NULL, 0)); - freed++; - } - } while(pgtable_cache_size > low); - } - return freed; -} - /* * NOTE: pagetable_init alloc all the fixmap pagetables contiguous on the * physical space so we can cache the place of the first one and move @@ -76,7 +54,7 @@ pgprot_t kmap_prot; #define kmap_get_fixmap_pte(vaddr) \ - pte_offset(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)) + pte_offset_kernel(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)) void __init kmap_init(void) { @@ -116,7 +94,6 @@ printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); - printk("%ld pages in page table cache\n",pgtable_cache_size); show_buffers(); } @@ -143,7 +120,7 @@ printk("PAE BUG #01!\n"); return; } - pte = pte_offset(pmd, vaddr); + pte = pte_offset_kernel(pmd, vaddr); if (pte_val(*pte)) pte_ERROR(*pte); pgprot_val(prot) = pgprot_val(PAGE_KERNEL) | pgprot_val(flags); @@ -196,7 +173,7 @@ if (pmd_none(*pmd)) { pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte))); - if (pte != pte_offset(pmd, 0)) + if (pte != pte_offset_kernel(pmd, 0)) BUG(); } vaddr += PMD_SIZE; @@ -267,7 +244,7 @@ *pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL); } set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte_base))); - if (pte_base != pte_offset(pmd, 0)) + if (pte_base != pte_offset_kernel(pmd, 0)) BUG(); } @@ -289,7 +266,7 @@ pgd = swapper_pg_dir + __pgd_offset(vaddr); pmd = pmd_offset(pgd, vaddr); - pte = pte_offset(pmd, vaddr); + pte = pte_offset_kernel(pmd, vaddr); pkmap_page_table = pte; #endif @@ -398,7 +375,7 @@ pgd = swapper_pg_dir + __pgd_offset(vaddr); pmd = pmd_offset(pgd, vaddr); - pte = pte_offset(pmd, vaddr); + pte = pte_offset_kernel(pmd, vaddr); old_pte = *pte; *pte = mk_pte_phys(0, PAGE_READONLY); local_flush_tlb(); diff -Nru a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c --- a/arch/i386/mm/ioremap.c Tue Feb 19 18:08:58 2002 +++ b/arch/i386/mm/ioremap.c Tue Feb 19 18:08:58 2002 @@ -49,7 +49,7 @@ if (address >= end) BUG(); do { - pte_t * pte = pte_alloc(&init_mm, pmd, address); + pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; remap_area_pte(pte, address, end - address, address + phys_addr, flags); diff -Nru a/arch/ia64/config.in b/arch/ia64/config.in --- a/arch/ia64/config.in Tue Feb 19 18:09:00 2002 +++ b/arch/ia64/config.in Tue Feb 19 18:09:00 2002 @@ -212,7 +212,7 @@ tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in + source sound/Config.in fi endmenu diff -Nru a/arch/ia64/ia32/ia32_signal.c b/arch/ia64/ia32/ia32_signal.c --- a/arch/ia64/ia32/ia32_signal.c Tue Feb 19 18:08:57 2002 +++ b/arch/ia64/ia32/ia32_signal.c Tue Feb 19 18:08:57 2002 @@ -174,7 +174,7 @@ { oldset = current->blocked; current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); } spin_unlock_irq(¤t->sigmask_lock); @@ -694,7 +694,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = (sigset_t) set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext_ia32(regs, &frame->sc, &eax)) @@ -725,7 +725,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext_ia32(regs, &frame->uc.uc_mcontext, &eax)) diff -Nru a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c --- a/arch/ia64/kernel/signal.c Tue Feb 19 18:08:57 2002 +++ b/arch/ia64/kernel/signal.c Tue Feb 19 18:08:57 2002 @@ -62,7 +62,7 @@ { oldset = current->blocked; current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); } spin_unlock_irq(¤t->sigmask_lock); @@ -259,7 +259,7 @@ spin_lock_irq(¤t->sigmask_lock); { current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); } spin_unlock_irq(¤t->sigmask_lock); @@ -438,7 +438,7 @@ { sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); sigaddset(¤t->blocked, sig); - recalc_sigpending(current); + recalc_sigpending(); } spin_unlock_irq(¤t->sigmask_lock); } diff -Nru a/arch/m68k/config.in b/arch/m68k/config.in --- a/arch/m68k/config.in Tue Feb 19 18:08:58 2002 +++ b/arch/m68k/config.in Tue Feb 19 18:08:58 2002 @@ -511,7 +511,7 @@ tristate 'Sound support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/dmasound/Config.in + source sound/oss/dmasound/Config.in fi endmenu diff -Nru a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c --- a/arch/m68k/kernel/signal.c Tue Feb 19 18:08:58 2002 +++ b/arch/m68k/kernel/signal.c Tue Feb 19 18:08:58 2002 @@ -79,7 +79,7 @@ mask &= _BLOCKABLE; saveset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); regs->d0 = -EINTR; while (1) { @@ -107,7 +107,7 @@ saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); regs->d0 = -EINTR; while (1) { @@ -552,7 +552,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); if (restore_sigcontext(regs, &frame->sc, frame + 1, &d0)) goto badframe; @@ -579,7 +579,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); if (rt_restore_ucontext(regs, sw, &frame->uc, &d0)) goto badframe; @@ -1011,7 +1011,7 @@ sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); if (!(ka->sa.sa_flags & SA_NODEFER)) sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); } /* diff -Nru a/arch/mips/config.in b/arch/mips/config.in --- a/arch/mips/config.in Tue Feb 19 18:08:57 2002 +++ b/arch/mips/config.in Tue Feb 19 18:08:57 2002 @@ -483,7 +483,7 @@ tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in + source sound/Config.in fi endmenu fi diff -Nru a/arch/mips/kernel/irixsig.c b/arch/mips/kernel/irixsig.c --- a/arch/mips/kernel/irixsig.c Tue Feb 19 18:08:59 2002 +++ b/arch/mips/kernel/irixsig.c Tue Feb 19 18:08:59 2002 @@ -147,7 +147,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } @@ -263,7 +263,7 @@ default: sigaddset(¤t->pending.signal, signr); - recalc_sigpending(current); + recalc_sigpending(); current->flags |= PF_SIGNALED; do_exit(exit_code); /* NOTREACHED */ @@ -344,7 +344,7 @@ sigdelsetmask(&blocked, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = blocked; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); /* @@ -464,7 +464,7 @@ default: return -EINVAL; } - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } if(old) { @@ -489,7 +489,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->regs[2] = -EINTR; diff -Nru a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c --- a/arch/mips/kernel/signal.c Tue Feb 19 18:09:00 2002 +++ b/arch/mips/kernel/signal.c Tue Feb 19 18:09:00 2002 @@ -89,7 +89,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs.regs[2] = EINTR; @@ -123,7 +123,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs.regs[2] = EINTR; @@ -256,7 +256,7 @@ sigdelsetmask(&blocked, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = blocked; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->sf_sc)) @@ -294,7 +294,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->rs_uc.uc_mcontext)) @@ -536,7 +536,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/mips64/config.in b/arch/mips64/config.in --- a/arch/mips64/config.in Tue Feb 19 18:08:58 2002 +++ b/arch/mips64/config.in Tue Feb 19 18:08:58 2002 @@ -234,7 +234,7 @@ tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in + source sound/Config.in fi endmenu diff -Nru a/arch/mips64/kernel/ioctl32.c b/arch/mips64/kernel/ioctl32.c --- a/arch/mips64/kernel/ioctl32.c Tue Feb 19 18:09:00 2002 +++ b/arch/mips64/kernel/ioctl32.c Tue Feb 19 18:09:00 2002 @@ -760,10 +760,6 @@ IOCTL32_HANDLER(BLKGETSIZE, w_long), IOCTL32_DEFAULT(BLKFLSBUF), - IOCTL32_DEFAULT(BLKRASET), - IOCTL32_HANDLER(BLKRAGET, w_long), - IOCTL32_DEFAULT(BLKFRASET), - IOCTL32_HANDLER(BLKFRAGET, w_long), IOCTL32_DEFAULT(BLKSECTSET), IOCTL32_HANDLER(BLKSECTGET, w_long), IOCTL32_DEFAULT(BLKSSZGET), diff -Nru a/arch/mips64/kernel/linux32.c b/arch/mips64/kernel/linux32.c --- a/arch/mips64/kernel/linux32.c Tue Feb 19 18:08:57 2002 +++ b/arch/mips64/kernel/linux32.c Tue Feb 19 18:08:57 2002 @@ -1000,7 +1000,7 @@ goto out; ret = read(file, buf, count, &pos); if (ret > 0) - inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_ACCESS); + dnotify_parent(file->f_dentry, DN_ACCESS); out: fput(file); bad_file: @@ -1032,7 +1032,7 @@ ret = write(file, buf, count, &pos); if (ret > 0) - inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_MODIFY); + dnotify_parent(file->f_dentry, DN_MODIFY); out: fput(file); bad_file: diff -Nru a/arch/mips64/kernel/signal.c b/arch/mips64/kernel/signal.c --- a/arch/mips64/kernel/signal.c Tue Feb 19 18:08:58 2002 +++ b/arch/mips64/kernel/signal.c Tue Feb 19 18:08:58 2002 @@ -118,7 +118,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs.regs[2] = EINTR; @@ -152,7 +152,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs.regs[2] = EINTR; @@ -281,7 +281,7 @@ sigdelsetmask(&blocked, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = blocked; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->sf_sc)) @@ -318,7 +318,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->rs_uc.uc_mcontext)) @@ -558,7 +558,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/mips64/kernel/signal32.c b/arch/mips64/kernel/signal32.c --- a/arch/mips64/kernel/signal32.c Tue Feb 19 18:09:00 2002 +++ b/arch/mips64/kernel/signal32.c Tue Feb 19 18:09:00 2002 @@ -161,7 +161,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs.regs[2] = EINTR; @@ -195,7 +195,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs.regs[2] = EINTR; @@ -360,7 +360,7 @@ sigdelsetmask(&blocked, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = blocked; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->sf_sc)) @@ -398,7 +398,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->rs_uc.uc_mcontext)) @@ -639,7 +639,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/parisc/config.in b/arch/parisc/config.in --- a/arch/parisc/config.in Tue Feb 19 18:09:00 2002 +++ b/arch/parisc/config.in Tue Feb 19 18:09:00 2002 @@ -170,7 +170,7 @@ comment 'Sound Drivers' tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in + source sound/Config.in fi endmenu diff -Nru a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c --- a/arch/parisc/kernel/signal.c Tue Feb 19 18:08:59 2002 +++ b/arch/parisc/kernel/signal.c Tue Feb 19 18:08:59 2002 @@ -112,7 +112,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->gr[28] = -EINTR; @@ -193,7 +193,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); /* Good thing we saved the old gr[30], eh? */ @@ -451,7 +451,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } return 1; diff -Nru a/arch/ppc/4xx_io/Makefile b/arch/ppc/4xx_io/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/4xx_io/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,12 @@ +# +# Makefile for the linux MPC4xx ppc-specific parts +# + +O_TARGET := 4xx_io.o + +#obj-y := + +obj-$(CONFIG_STB_KB) += stb_kb.o +obj-$(CONFIG_SERIAL_SICC) += serial_sicc.o + +include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc/4xx_io/serial_sicc.c b/arch/ppc/4xx_io/serial_sicc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/4xx_io/serial_sicc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2133 @@ +/* + * arch/ppc/4xx_io/serial_sicc.c + * + * Driver for IBM STB3xxx SICC serial port + * + * Based on drivers/char/serial_amba.c, by ARM Ltd. + * + * Copyright 2001 IBM Crop. + * Author: IBM China Research Lab + * Yudong Yang + * Yi Ge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This is a driver for SICC serial port on IBM Redwood 4 evaluation board. + * The driver support both as a console device and normal serial device and + * is compatible with normal ttyS* devices. + */ + +#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 + + +/* ----------------------------------------------------------------------------- + * From STB03xxx SICC UART Specification + * ----------------------------------------------------------------------------- + * UART Register Offsets. + */ + +#define BL_SICC_LSR 0x0000000 /* line status register read/clear */ +#define BL_SICC_LSRS 0x0000001 /* set line status register read/set */ +#define BL_SICC_HSR 0x0000002 /* handshake status register r/clear */ +#define BL_SICC_HSRS 0x0000003 /* set handshake status register r/set */ +#define BL_SICC_BRDH 0x0000004 /* baudrate divisor high reg r/w */ +#define BL_SICC_BRDL 0x0000005 /* baudrate divisor low reg r/w */ +#define BL_SICC_LCR 0x0000006 /* control register r/w */ +#define BL_SICC_RCR 0x0000007 /* receiver command register r/w */ +#define BL_SICC_TxCR 0x0000008 /* transmitter command register r/w */ +#define BL_SICC_RBR 0x0000009 /* receive buffer r */ +#define BL_SICC_TBR 0x0000009 /* transmit buffer w */ +#define BL_SICC_CTL2 0x000000A /* added for Vesta */ +#define BL_SICC_IrCR 0x000000B /* added for Vesta IR */ + +/* masks and definitions for serial port control register */ + +#define _LCR_LM_MASK 0xc0 /* loop back modes */ +#define _LCR_DTR_MASK 0x20 /* data terminal ready 0-inactive */ +#define _LCR_RTS_MASK 0x10 /* request to send 0-inactive */ +#define _LCR_DB_MASK 0x08 /* data bits mask */ +#define _LCR_PE_MASK 0x04 /* parity enable */ +#define _LCR_PTY_MASK 0x02 /* parity */ +#define _LCR_SB_MASK 0x01 /* stop bit mask */ + +#define _LCR_LM_NORM 0x00 /* normal operation */ +#define _LCR_LM_LOOP 0x40 /* internal loopback mode */ +#define _LCR_LM_ECHO 0x80 /* automatic echo mode */ +#define _LCR_LM_RES 0xc0 /* reserved */ + +#define _LCR_DTR_ACTIVE _LCR_DTR_MASK /* DTR is active */ +#define _LCR_RTS_ACTIVE _LCR_RTS_MASK /* RTS is active */ +#define _LCR_DB_8_BITS _LCR_DB_MASK /* 8 data bits */ +#define _LCR_DB_7_BITS 0x00 /* 7 data bits */ +#define _LCR_PE_ENABLE _LCR_PE_MASK /* parity enabled */ +#define _LCR_PE_DISABLE 0x00 /* parity disabled */ +#define _LCR_PTY_EVEN 0x00 /* even parity */ +#define _LCR_PTY_ODD _LCR_PTY_MASK /* odd parity */ +#define _LCR_SB_1_BIT 0x00 /* one stop bit */ +#define _LCR_SB_2_BIT _LCR_SB_MASK /* two stop bit */ + +/* serial port handshake register */ + +#define _HSR_DIS_MASK 0x80 /* DSR input inactive error mask */ +#define _HSR_CS_MASK 0x40 /* CTS input inactive error mask */ +#define _HSR_DIS_ACT 0x00 /* dsr input is active */ +#define _HSR_DIS_INACT _HSR_DIS_MASK /* dsr input is inactive */ +#define _HSR_CS_ACT 0x00 /* cts input is active */ +#define _HSR_CS_INACT _HSR_CS_MASK /* cts input is active */ + +/* serial port line status register */ + +#define _LSR_RBR_MASK 0x80 /* receive buffer ready mask */ +#define _LSR_FE_MASK 0x40 /* framing error */ +#define _LSR_OE_MASK 0x20 /* overrun error */ +#define _LSR_PE_MASK 0x10 /* parity error */ +#define _LSR_LB_MASK 0x08 /* line break */ +#define _LSR_TBR_MASK 0x04 /* transmit buffer ready */ +#define _LSR_TSR_MASK 0x02 /* transmit shift register ready */ + +#define _LSR_RBR_FULL _LSR_RBR_MASK /* receive buffer is full */ +#define _LSR_FE_ERROR _LSR_FE_MASK /* framing error detected */ +#define _LSR_OE_ERROR _LSR_OE_MASK /* overrun error detected */ +#define _LSR_PE_ERROR _LSR_PE_MASK /* parity error detected */ +#define _LSR_LB_BREAK _LSR_LB_MASK /* line break detected */ +#define _LSR_TBR_EMPTY _LSR_TBR_MASK /* transmit buffer is ready */ +#define _LSR_TSR_EMPTY _LSR_TSR_MASK /* transmit shift register is empty */ +#define _LSR_TX_ALL 0x06 /* all physical transmit is done */ + +#define _LSR_RX_ERR (_LSR_LB_BREAK | _LSR_FE_MASK | _LSR_OE_MASK | \ + _LSR_PE_MASK ) + +/* serial port reciever command register */ + +#define _RCR_ER_MASK 0x80 /* enable receiver mask */ +#define _RCR_DME_MASK 0x60 /* dma mode */ +#define _RCR_EIE_MASK 0x10 /* error interrupt enable mask */ +#define _RCR_PME_MASK 0x08 /* pause mode mask */ + +#define _RCR_ER_ENABLE _RCR_ER_MASK /* receiver enabled */ +#define _RCR_DME_DISABLE 0x00 /* dma disabled */ +#define _RCR_DME_RXRDY 0x20 /* dma disabled, RxRDY interrupt enabled*/ +#define _RCR_DME_ENABLE2 0x40 /* dma enabled,receiver src channel 2 */ +#define _RCR_DME_ENABLE3 0x60 /* dma enabled,receiver src channel 3 */ +#define _RCR_PME_HARD _RCR_PME_MASK /* RTS controlled by hardware */ +#define _RCR_PME_SOFT 0x00 /* RTS controlled by software */ + +/* serial port transmit command register */ + +#define _TxCR_ET_MASK 0x80 /* transmiter enable mask */ +#define _TxCR_DME_MASK 0x60 /* dma mode mask */ +#define _TxCR_TIE_MASK 0x10 /* empty interrupt enable mask */ +#define _TxCR_EIE_MASK 0x08 /* error interrupt enable mask */ +#define _TxCR_SPE_MASK 0x04 /* stop/pause mask */ +#define _TxCR_TB_MASK 0x02 /* transmit break mask */ + +#define _TxCR_ET_ENABLE _TxCR_ET_MASK /* transmiter enabled */ +#define _TxCR_DME_DISABLE 0x00 /* transmiter disabled, TBR intr disabled */ +#define _TxCR_DME_TBR 0x20 /* transmiter disabled, TBR intr enabled */ +#define _TxCR_DME_CHAN_2 0x40 /* dma enabled, destination chann 2 */ +#define _TxCR_DME_CHAN_3 0x60 /* dma enabled, destination chann 3 */ + +/* serial ctl reg 2 - added for Vesta */ + +#define _CTL2_EXTERN 0x80 /* */ +#define _CTL2_USEFIFO 0x40 /* */ +#define _CTL2_RESETRF 0x08 /* */ +#define _CTL2_RESETTF 0x04 /* */ + + + +#define SERIAL_SICC_NAME "ttySICC" +#define SERIAL_SICC_MAJOR 150 +#define SERIAL_SICC_MINOR 1 +#define SERIAL_SICC_NR 1 + +#define CALLOUT_SICC_NAME "cuasicc" +#define CALLOUT_SICC_MAJOR 151 +#define CALLOUT_SICC_MINOR 1 +#define CALLOUT_SICC_NR SERIAL_SICC_NR + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define DEBUG 0 + + + +/* + * Things needed by tty driver + */ +static struct tty_driver siccnormal_driver, sicccallout_driver; +static int siccuart_refcount; +static struct tty_struct *siccuart_table[SERIAL_SICC_NR]; +static struct termios *siccuart_termios[SERIAL_SICC_NR]; +static struct termios *siccuart_termios_locked[SERIAL_SICC_NR]; + +#if defined(CONFIG_SERIAL_SICC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +/* + * Things needed internally to this driver + */ + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static u_char *tmp_buf; +static DECLARE_MUTEX(tmp_buf_sem); + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 +#define SICC_ISR_PASS_LIMIT 256 + +#define EVT_WRITE_WAKEUP 0 + +struct SICC_icount { + __u32 cts; + __u32 dsr; + __u32 rng; + __u32 dcd; + __u32 rx; + __u32 tx; + __u32 frame; + __u32 overrun; + __u32 parity; + __u32 brk; + __u32 buf_overrun; +}; + +/* + * Static information about the port + */ +struct SICC_port { + unsigned int uart_base; + unsigned int uart_base_phys; + unsigned int irqrx; + unsigned int irqtx; + unsigned int uartclk; + unsigned int fifosize; + unsigned int tiocm_support; + void (*set_mctrl)(struct SICC_port *, u_int mctrl); +}; + +/* + * This is the state information which is persistent across opens + */ +struct SICC_state { + struct SICC_icount icount; + unsigned int line; + unsigned int close_delay; + unsigned int closing_wait; + unsigned int custom_divisor; + unsigned int flags; + struct termios normal_termios; + struct termios callout_termios; + + int count; + struct SICC_info *info; +}; + +#define SICC_XMIT_SIZE 1024 +/* + * This is the state information which is only valid when the port is open. + */ +struct SICC_info { + struct SICC_port *port; + struct SICC_state *state; + struct tty_struct *tty; + unsigned char x_char; + unsigned char old_status; + unsigned char read_status_mask; + unsigned char ignore_status_mask; + struct circ_buf xmit; + unsigned int flags; +#ifdef SUPPORT_SYSRQ + unsigned long sysrq; +#endif + + unsigned int event; + unsigned int timeout; + unsigned int lcr_h; + unsigned int mctrl; + int blocked_open; + pid_t session; + pid_t pgrp; + + struct tasklet_struct tlet; + + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + wait_queue_head_t delta_msr_wait; +}; + +#ifdef CONFIG_SERIAL_SICC_CONSOLE +static struct console siccuart_cons; +#endif +static void siccuart_change_speed(struct SICC_info *info, struct termios *old_termios); +static void siccuart_wait_until_sent(struct tty_struct *tty, int timeout); + + + +static void powerpcMtcic_cr(unsigned long value) +{ + mtdcr(DCRN_CICCR, value); +} + +static unsigned long powerpcMfcic_cr(void) +{ + return mfdcr(DCRN_CICCR); +} + +static unsigned long powerpcMfclkgpcr(void) +{ + return mfdcr(DCRN_SCCR); +} + +static void sicc_set_mctrl_null(struct SICC_port *port, u_int mctrl) +{ +} + +static struct SICC_port sicc_ports[SERIAL_SICC_NR] = { + { + uart_base: 0, + uart_base_phys: SICC0_IO_BASE, + irqrx: SICC0_INTRX, + irqtx: SICC0_INTTX, +// uartclk: 0, + fifosize: 1, + set_mctrl: sicc_set_mctrl_null, + } +}; + +static struct SICC_state sicc_state[SERIAL_SICC_NR]; + +static void siccuart_enable_rx_interrupt(struct SICC_info *info) +{ + unsigned char cr; + + cr = readb(info->port->uart_base+BL_SICC_RCR); + cr &= ~_RCR_DME_MASK; + cr |= _RCR_DME_RXRDY; + writeb(cr, info->port->uart_base+BL_SICC_RCR); +} + +static void siccuart_disable_rx_interrupt(struct SICC_info *info) +{ + unsigned char cr; + + cr = readb(info->port->uart_base+BL_SICC_RCR); + cr &= ~_RCR_DME_MASK; + cr |= _RCR_DME_DISABLE; + writeb(cr, info->port->uart_base+BL_SICC_RCR); +} + + +static void siccuart_enable_tx_interrupt(struct SICC_info *info) +{ + unsigned char cr; + + cr = readb(info->port->uart_base+BL_SICC_TxCR); + cr &= ~_TxCR_DME_MASK; + cr |= _TxCR_DME_TBR; + writeb(cr, info->port->uart_base+BL_SICC_TxCR); +} + +static void siccuart_disable_tx_interrupt(struct SICC_info *info) +{ + unsigned char cr; + + cr = readb(info->port->uart_base+BL_SICC_TxCR); + cr &= ~_TxCR_DME_MASK; + cr |= _TxCR_DME_DISABLE; + writeb(cr, info->port->uart_base+BL_SICC_TxCR); +} + + +static void siccuart_stop(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + save_flags(flags); cli(); + siccuart_disable_tx_interrupt(info); + restore_flags(flags); +} + +static void siccuart_start(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + save_flags(flags); cli(); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf) + siccuart_enable_tx_interrupt(info); + restore_flags(flags); +} + + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static void siccuart_event(struct SICC_info *info, int event) +{ + info->event |= 1 << event; + tasklet_schedule(&info->tlet); +} + +static void +#ifdef SUPPORT_SYSRQ +siccuart_rx_chars(struct SICC_info *info, struct pt_regs *regs) +#else +siccuart_rx_chars(struct SICC_info *info) +#endif +{ + struct tty_struct *tty = info->tty; + unsigned int status, ch, rsr, flg, ignored = 0; + struct SICC_icount *icount = &info->state->icount; + struct SICC_port *port = info->port; + + status = readb(port->uart_base+BL_SICC_LSR ); + while (status & _LSR_RBR_FULL) { + ch = readb(port->uart_base+BL_SICC_RBR); + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + icount->rx++; + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + rsr = readb(port->uart_base+BL_SICC_LSR); + if (rsr & _LSR_RX_ERR) + goto handle_error; +#ifdef SUPPORT_SYSRQ + if (info->sysrq) { + if (ch && time_before(jiffies, info->sysrq)) { + handle_sysrq(ch, regs, NULL, NULL); + info->sysrq = 0; + goto ignore_char; + } + info->sysrq = 0; + } +#endif + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = readb(port->uart_base+BL_SICC_LSR ); + } +out: + tty_flip_buffer_push(tty); + return; + +handle_error: + if (rsr & _LSR_LB_BREAK) { + rsr &= ~(_LSR_FE_MASK | _LSR_PE_MASK); + icount->brk++; + +#ifdef SUPPORT_SYSRQ + if (info->state->line == siccuart_cons.index) { + if (!info->sysrq) { + info->sysrq = jiffies + HZ*5; + goto ignore_char; + } + } +#endif + } else if (rsr & _LSR_PE_MASK) + icount->parity++; + else if (rsr & _LSR_FE_MASK) + icount->frame++; + if (rsr & _LSR_OE_MASK) + icount->overrun++; + + if (rsr & info->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + rsr &= info->read_status_mask; + + if (rsr & _LSR_LB_BREAK) + flg = TTY_BREAK; + else if (rsr & _LSR_PE_MASK) + flg = TTY_PARITY; + else if (rsr & _LSR_FE_MASK) + flg = TTY_FRAME; + + if (rsr & _LSR_OE_MASK) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + info->sysrq = 0; +#endif + goto error_return; +} + +static void siccuart_tx_chars(struct SICC_info *info) +{ + struct SICC_port *port = info->port; + int count; + unsigned char status; + + + if (info->x_char) { + writeb(info->x_char, port->uart_base+ BL_SICC_TBR); + info->state->icount.tx++; + info->x_char = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + siccuart_disable_tx_interrupt(info); + writeb(status&(~_LSR_RBR_MASK),port->uart_base+BL_SICC_LSR); + return; + } + + count = port->fifosize; + do { + writeb(info->xmit.buf[info->xmit.tail], port->uart_base+ BL_SICC_TBR); + info->xmit.tail = (info->xmit.tail + 1) & (SICC_XMIT_SIZE - 1); + info->state->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (--count > 0); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SICC_XMIT_SIZE) < WAKEUP_CHARS) + siccuart_event(info, EVT_WRITE_WAKEUP); + + if (info->xmit.head == info->xmit.tail) { + siccuart_disable_tx_interrupt(info); + } +} + + +static void siccuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct SICC_info *info = dev_id; + +#ifdef SUPPORT_SYSRQ + siccuart_rx_chars(info, regs); +#else + siccuart_rx_chars(info); +#endif + + //powerpcClearUicsrBits(0x00000400); +} + + +static void siccuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct SICC_info *info = dev_id; + siccuart_tx_chars(info); + +} + +static void siccuart_tasklet_action(unsigned long data) +{ + struct SICC_info *info = (struct SICC_info *)data; + struct tty_struct *tty; + + tty = info->tty; + if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event)) + return; + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); +} + +static int siccuart_startup(struct SICC_info *info) +{ + unsigned long flags; + unsigned long page; + int retval = 0; + + if (info->flags & ASYNC_INITIALIZED) { + return 0; + } + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + if (info->port->uart_base == 0) + info->port->uart_base = (int)ioremap(info->port->uart_base_phys, PAGE_SIZE); + if (info->port->uart_base == 0) { + free_page(page); + return -ENOMEM; + } + + save_flags(flags); cli(); + + if (info->xmit.buf) + free_page(page); + else + info->xmit.buf = (unsigned char *) page; + + + info->mctrl = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->mctrl = TIOCM_RTS | TIOCM_DTR; + info->port->set_mctrl(info->port, info->mctrl); + + /* + * initialise the old status of the modem signals + */ + info->old_status = 0; // UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY; + + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit.head = info->xmit.tail = 0; + + /* + * Set up the tty->alt_speed kludge + */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } + + + writeb( 0x00, info->port->uart_base + BL_SICC_IrCR ); // disable IrDA + + + /* + * and set the speed of the serial port + */ + siccuart_change_speed(info, 0); + + // enable rx/tx ports + writeb(_RCR_ER_ENABLE /*| _RCR_PME_HARD*/, info->port->uart_base + BL_SICC_RCR); + writeb(_TxCR_ET_ENABLE , info->port->uart_base + BL_SICC_TxCR); + + readb(info->port->uart_base + BL_SICC_RBR); // clear rx port + + writeb(0xf8, info->port->uart_base + BL_SICC_LSR); /* reset bits 0-4 of LSR */ + + /* + * Finally, enable interrupts + */ + + /* + * Allocate the IRQ + */ + retval = request_irq(info->port->irqrx, siccuart_int_rx, 0, "SICC rx", info); + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + retval = 0; + } + goto errout; + } + retval = request_irq(info->port->irqtx, siccuart_int_tx, 0, "SICC tx", info); + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + retval = 0; + } + free_irq(info->port->irqrx, info); + goto errout; + } + + siccuart_enable_rx_interrupt(info); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + + +errout: + restore_flags(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void siccuart_shutdown(struct SICC_info *info) +{ + unsigned long flags; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + save_flags(flags); cli(); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be woken up + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * disable all interrupts, disable the port + */ + siccuart_disable_rx_interrupt(info); + siccuart_disable_tx_interrupt(info); + + /* + * Free the IRQ + */ + free_irq(info->port->irqtx, info); + free_irq(info->port->irqrx, info); + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = NULL; + free_page(pg); + } + + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS); + info->port->set_mctrl(info->port, info->mctrl); + + /* kill off our tasklet */ + tasklet_kill(&info->tlet); + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + + restore_flags(flags); +} + + +static void siccuart_change_speed(struct SICC_info *info, struct termios *old_termios) +{ + unsigned int lcr_h, baud, quot, cflag, old_rcr, old_tcr, bits; + unsigned long flags; + + if (!info->tty || !info->tty->termios) + return; + + cflag = info->tty->termios->c_cflag; + +#if DEBUG + printk("siccuart_set_cflag(0x%x) called\n", cflag); +#endif + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS7: lcr_h = _LCR_PE_DISABLE | _LCR_DB_7_BITS | _LCR_SB_1_BIT; bits = 9; break; + default: lcr_h = _LCR_PE_DISABLE | _LCR_DB_8_BITS | _LCR_SB_1_BIT; bits = 10; break; // CS8 + } + if (cflag & CSTOPB) { + lcr_h |= _LCR_SB_2_BIT; + bits ++; + } + if (cflag & PARENB) { + lcr_h |= _LCR_PE_ENABLE; + bits++; + if (!(cflag & PARODD)) + lcr_h |= _LCR_PTY_ODD; + else + lcr_h |= _LCR_PTY_EVEN; + } + + do { + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; + + + { + // here is ppc403SetBaud(com_port, baud); + unsigned long divisor, clockSource, temp; + + /* Ensure CICCR[7] is 0 to select Internal Baud Clock */ + powerpcMtcic_cr((unsigned long)(powerpcMfcic_cr() & 0xFEFFFFFF)); + + /* Determine Internal Baud Clock Frequency */ + /* powerpcMfclkgpcr() reads DCR 0x120 - the*/ + /* SCCR (Serial Clock Control Register) on Vesta */ + temp = powerpcMfclkgpcr(); + + if(temp & 0x00000080) { + clockSource = 324000000; + } + else { + clockSource = 216000000; + } + clockSource = clockSource/(unsigned long)((temp&0x00FC0000)>>18); + divisor = clockSource/(16*baud) - 1; + /* divisor has only 12 bits of resolution */ + if(divisor>0x00000FFF){ + divisor=0x00000FFF; + } + + quot = divisor; + } + + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + old_termios = NULL; + } + } while (quot == 0 && old_termios); + + /* As a last resort, if the quotient is zero, default to 9600 bps */ + if (!quot) + quot = (info->port->uartclk / (16 * 9600)) - 1; + + info->timeout = info->port->fifosize * HZ * bits / baud; + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; + else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /* + * Set up parity check flag + */ +#define RELEVENT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + info->read_status_mask = _LSR_OE_MASK; + if (I_INPCK(info->tty)) + info->read_status_mask |= _LSR_FE_MASK | _LSR_PE_MASK; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= _LSR_LB_MASK; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= _LSR_FE_MASK | _LSR_PE_MASK; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= _LSR_LB_MASK; + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= _LSR_OE_MASK; + } + + /* first, disable everything */ + save_flags(flags); cli(); + + old_rcr = readb(info->port->uart_base + BL_SICC_RCR); + old_tcr = readb(info->port->uart_base + BL_SICC_TxCR); + + + writeb(0, info->port->uart_base + BL_SICC_RCR); + writeb(0, info->port->uart_base + BL_SICC_TxCR); + + /*RLBtrace (&ppc403Chan0, 0x2000000c, 0, 0);*/ + + + restore_flags(flags); + + + /* Set baud rate */ + writeb((quot & 0x00000F00)>>8, info->port->uart_base + BL_SICC_BRDH ); + writeb( quot & 0x00000FF, info->port->uart_base + BL_SICC_BRDL ); + + /* Set CTL2 reg to use external clock (ExtClk) and enable FIFOs. */ + /* For now, do NOT use FIFOs since 403 UART did not have this */ + /* capability and this driver was inherited from 403UART. */ + writeb(_CTL2_EXTERN, info->port->uart_base + BL_SICC_CTL2); + + writeb(lcr_h, info->port->uart_base + BL_SICC_LCR); + + writeb(old_rcr, info->port->uart_base + BL_SICC_RCR); // restore rcr + writeb(old_tcr, info->port->uart_base + BL_SICC_TxCR); // restore txcr + +} + + +static void siccuart_put_char(struct tty_struct *tty, u_char ch) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + if (!tty || !info->xmit.buf) + return; + + save_flags(flags); cli(); + if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE) != 0) { + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SICC_XMIT_SIZE - 1); + } + restore_flags(flags); +} + +static void siccuart_flush_chars(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + if (info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + + save_flags(flags); cli(); + siccuart_enable_tx_interrupt(info); + restore_flags(flags); +} + +static int siccuart_write(struct tty_struct *tty, int from_user, + const u_char * buf, int count) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + int c, ret = 0; + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SICC_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SICC_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = (info->xmit.head + c) & + (SICC_XMIT_SIZE - 1); + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + cli(); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SICC_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = (info->xmit.head + c) & + (SICC_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; + } + restore_flags(flags); + } + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped) + siccuart_enable_tx_interrupt(info); + return ret; +} + +static int siccuart_write_room(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE); +} + +static int siccuart_chars_in_buffer(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + + return CIRC_CNT(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE); +} + +static void siccuart_flush_buffer(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + +#if DEBUG + printk("siccuart_flush_buffer(%d) called\n", + MINOR(tty->device) - tty->driver.minor_start); +#endif + save_flags(flags); cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void siccuart_send_xchar(struct tty_struct *tty, char ch) +{ + struct SICC_info *info = tty->driver_data; + + info->x_char = ch; + if (ch) + siccuart_enable_tx_interrupt(info); +} + +static void siccuart_throttle(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + if (I_IXOFF(tty)) + siccuart_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) { + save_flags(flags); cli(); + info->mctrl &= ~TIOCM_RTS; + info->port->set_mctrl(info->port, info->mctrl); + restore_flags(flags); + } +} + +static void siccuart_unthrottle(struct tty_struct *tty) +{ + struct SICC_info *info = (struct SICC_info *) tty->driver_data; + unsigned long flags; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + siccuart_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) { + save_flags(flags); cli(); + info->mctrl |= TIOCM_RTS; + info->port->set_mctrl(info->port, info->mctrl); + restore_flags(flags); + } +} + +static int get_serial_info(struct SICC_info *info, struct serial_struct *retinfo) +{ + struct SICC_state *state = info->state; + struct SICC_port *port = info->port; + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = 0; + tmp.line = state->line; + tmp.port = port->uart_base; + if (HIGH_BITS_OFFSET) + tmp.port_high = port->uart_base >> HIGH_BITS_OFFSET; + tmp.irq = port->irqrx; + tmp.flags = 0; + tmp.xmit_fifo_size = port->fifosize; + tmp.baud_base = port->uartclk / 16; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct SICC_info *info, + struct serial_struct *newinfo) +{ + struct serial_struct new_serial; + struct SICC_state *state, old_state; + struct SICC_port *port; + unsigned long new_port; + unsigned int i, change_irq, change_port; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + state = info->state; + old_state = *state; + port = info->port; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + change_irq = new_serial.irq != port->irqrx; + change_port = new_port != port->uart_base; + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || + (new_serial.baud_base != port->uartclk / 16) || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != port->fifosize) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (state->flags & ~ASYNC_USR_MASK))) + return -EPERM; + state->flags = ((state->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)) + return -EINVAL; + + if (new_serial.type && change_port) { + for (i = 0; i < SERIAL_SICC_NR; i++) + if ((port != sicc_ports + i) && + sicc_ports[i].uart_base != new_port) + return -EADDRINUSE; + } + + if ((change_port || change_irq) && (state->count > 1)) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + port->uartclk = new_serial.baud_base * 16; + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ / 100; + state->closing_wait = new_serial.closing_wait * HZ / 100; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + port->fifosize = new_serial.xmit_fifo_size; + + if (change_port || change_irq) { + /* + * We need to shutdown the serial port at the old + * port/irq combination. + */ + siccuart_shutdown(info); + port->irqrx = new_serial.irq; + port->uart_base = new_port; + } + +check_and_exit: + if (!port->uart_base) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if ((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK) || + (old_state.custom_divisor != state->custom_divisor)) { + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + siccuart_change_speed(info, NULL); + } + } else + retval = siccuart_startup(info); + return retval; +} + + +/* + * get_lsr_info - get line status register info + */ +static int get_lsr_info(struct SICC_info *info, unsigned int *value) +{ + unsigned int result, status; + unsigned long flags; + + save_flags(flags); cli(); + status = readb(info->port->uart_base + BL_SICC_LSR); + restore_flags(flags); + result = status & _LSR_TSR_EMPTY ? TIOCSER_TEMT : 0; + + /* + * If we're about to load something into the transmit + * register, we'll pretend the transmitter isn't empty to + * avoid a race condition (depending on when the transmit + * interrupt happens). + */ + if (info->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + SICC_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= TIOCSER_TEMT; + + return put_user(result, value); +} + +static int get_modem_info(struct SICC_info *info, unsigned int *value) +{ + unsigned int result = info->mctrl; + + return put_user(result, value); +} + +static int set_modem_info(struct SICC_info *info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg, old; + unsigned long flags; + + if (get_user(arg, value)) + return -EFAULT; + + old = info->mctrl; + switch (cmd) { + case TIOCMBIS: + info->mctrl |= arg; + break; + + case TIOCMBIC: + info->mctrl &= ~arg; + break; + + case TIOCMSET: + info->mctrl = arg; + break; + + default: + return -EINVAL; + } + save_flags(flags); cli(); + if (old != info->mctrl) + info->port->set_mctrl(info->port, info->mctrl); + restore_flags(flags); + return 0; +} + +static void siccuart_break_ctl(struct tty_struct *tty, int break_state) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + unsigned int lcr_h; + + + save_flags(flags); cli(); + lcr_h = readb(info->port + BL_SICC_LSR); + if (break_state == -1) + lcr_h |= _LSR_LB_MASK; + else + lcr_h &= ~_LSR_LB_MASK; + writeb(lcr_h, info->port + BL_SICC_LSRS); + restore_flags(flags); +} + +static int siccuart_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct SICC_info *info = tty->driver_data; + struct SICC_icount cnow; + struct serial_icounter_struct icount; + unsigned long flags; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCMGET: + return get_modem_info(info, (unsigned int *)arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *)arg); + case TIOCGSERIAL: + return get_serial_info(info, + (struct serial_struct *)arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *)arg); + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *)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 + */ + case TIOCMIWAIT: + return 0; + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + save_flags(flags); cli(); + cnow = info->state->icount; + restore_flags(flags); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user((void *)arg, &icount, sizeof(icount)) + ? -EFAULT : 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void siccuart_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + if ((cflag ^ old_termios->c_cflag) == 0 && + RELEVENT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) + return; + + siccuart_change_speed(info, old_termios); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + save_flags(flags); cli(); + info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR); + info->port->set_mctrl(info->port, info->mctrl); + restore_flags(flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + save_flags(flags); cli(); + info->mctrl |= TIOCM_DTR; + if (!(cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) + info->mctrl |= TIOCM_RTS; + info->port->set_mctrl(info->port, info->mctrl); + restore_flags(flags); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(cflag & CRTSCTS)) { + tty->hw_stopped = 0; + siccuart_start(tty); + } + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} + +static void siccuart_close(struct tty_struct *tty, struct file *filp) +{ + struct SICC_info *info = tty->driver_data; + struct SICC_state *state; + unsigned long flags; + + if (!info) + return; + + state = info->state; + +#if DEBUG + //printk("siccuart_close() called\n"); +#endif + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("siccuart_close: bad serial port count; tty->count is 1, state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for %s%d: %d\n", tty->driver.name, info->state->line, state->count); + state->count = 0; + } + if (state->count) { + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + restore_flags(flags); + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->state->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->state->closing_wait); + /* + * At this point, we stop accepting input. To do this, we + * disable the receive line status interrupts. + */ + if (info->flags & ASYNC_INITIALIZED) { + siccuart_disable_rx_interrupt(info); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + siccuart_wait_until_sent(tty, info->timeout); + } + siccuart_shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = NULL; + if (info->blocked_open) { + if (info->state->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->state->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; +} + +static void siccuart_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct SICC_info *info = (struct SICC_info *) tty->driver_data; + unsigned long char_time, expire; + + if (info->port->fifosize == 0) + return; + + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (info->timeout - HZ/50) / info->port->fifosize; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + + // Crazy!! sometimes the input arg 'timeout' can be negtive numbers :-( + if (timeout >= 0 && timeout < char_time) + char_time = timeout; + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than info->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*info->timeout. + */ + if (!timeout || timeout > 2 * info->timeout) + timeout = 2 * info->timeout; + + expire = jiffies + timeout; +#if DEBUG + printk("siccuart_wait_until_sent(%d), jiff=%lu, expire=%lu char_time=%lu...\n", + MINOR(tty->device) - tty->driver.minor_start, jiffies, + expire, char_time); +#endif + while ((readb(info->port->uart_base + BL_SICC_LSR) & _LSR_TX_ALL) != _LSR_TX_ALL) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); +} + +static void siccuart_hangup(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + struct SICC_state *state = info->state; + + siccuart_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + siccuart_shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = NULL; + wake_up_interruptible(&info->open_wait); +} + +static int block_til_ready(struct tty_struct *tty, struct file *filp, + struct SICC_info *info) +{ + DECLARE_WAITQUEUE(wait, current); + struct SICC_state *state = info->state; + unsigned long flags; + int do_clocal = 0, extra_count = 0, retval; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + return (info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (state->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, state->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) { + info->mctrl = TIOCM_DTR | TIOCM_RTS; + info->port->set_mctrl(info->port, info->mctrl); + } + restore_flags(flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal /*|| (UART_GET_FR(info->port) & SICC_UARTFR_DCD)*/)) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static struct SICC_info *siccuart_get(int line) +{ + struct SICC_info *info; + struct SICC_state *state = sicc_state + line; + + state->count++; + if (state->info) + return state->info; + info = kmalloc(sizeof(struct SICC_info), GFP_KERNEL); + if (info) { + memset(info, 0, sizeof(struct SICC_info)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->flags = state->flags; + info->state = state; + info->port = sicc_ports + line; + tasklet_init(&info->tlet, siccuart_tasklet_action, + (unsigned long)info); + } + if (state->info) { + kfree(info); + return state->info; + } + state->info = info; + return info; +} + +static int siccuart_open(struct tty_struct *tty, struct file *filp) +{ + struct SICC_info *info; + int retval, line = MINOR(tty->device) - tty->driver.minor_start; + + + // is this a line that we've got? + MOD_INC_USE_COUNT; + if (line >= SERIAL_SICC_NR) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + info = siccuart_get(line); + if (!info) + return -ENOMEM; + + tty->driver_data = info; + info->tty = tty; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + /* + * Make sure we have the temporary buffer allocated + */ + if (!tmp_buf) { + unsigned long page = get_zeroed_page(GFP_KERNEL); + if (tmp_buf) + free_page(page); + else if (!page) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + tmp_buf = (u_char *)page; + } + + /* + * If the port is in the middle of closing, bail out now. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + MOD_DEC_USE_COUNT; + return -EAGAIN; + } + + /* + * Start up the serial port + */ + retval = siccuart_startup(info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + + if ((info->state->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) { + *tty->termios = info->state->normal_termios; + } + else { + *tty->termios = info->state->callout_termios; + } + } +#ifdef CONFIG_SERIAL_SICC_CONSOLE + if (siccuart_cons.cflag && siccuart_cons.index == line) { + tty->termios->c_cflag = siccuart_cons.cflag; + siccuart_cons.cflag = 0; + siccuart_change_speed(info, NULL); + } +#endif + info->session = current->session; + info->pgrp = current->pgrp; + return 0; +} + +int __init siccuart_init(void) +{ + int i; + printk("IBM Vesta SICC serial port driver V 0.1 by Yudong Yang and Yi Ge / IBM CRL .\n"); + siccnormal_driver.magic = TTY_DRIVER_MAGIC; + siccnormal_driver.driver_name = "serial_sicc"; + siccnormal_driver.name = SERIAL_SICC_NAME; + siccnormal_driver.major = SERIAL_SICC_MAJOR; + siccnormal_driver.minor_start = SERIAL_SICC_MINOR; + siccnormal_driver.num = SERIAL_SICC_NR; + siccnormal_driver.type = TTY_DRIVER_TYPE_SERIAL; + siccnormal_driver.subtype = SERIAL_TYPE_NORMAL; + siccnormal_driver.init_termios = tty_std_termios; + siccnormal_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + siccnormal_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + siccnormal_driver.refcount = &siccuart_refcount; + siccnormal_driver.table = siccuart_table; + siccnormal_driver.termios = siccuart_termios; + siccnormal_driver.termios_locked = siccuart_termios_locked; + + siccnormal_driver.open = siccuart_open; + siccnormal_driver.close = siccuart_close; + siccnormal_driver.write = siccuart_write; + siccnormal_driver.put_char = siccuart_put_char; + siccnormal_driver.flush_chars = siccuart_flush_chars; + siccnormal_driver.write_room = siccuart_write_room; + siccnormal_driver.chars_in_buffer = siccuart_chars_in_buffer; + siccnormal_driver.flush_buffer = siccuart_flush_buffer; + siccnormal_driver.ioctl = siccuart_ioctl; + siccnormal_driver.throttle = siccuart_throttle; + siccnormal_driver.unthrottle = siccuart_unthrottle; + siccnormal_driver.send_xchar = siccuart_send_xchar; + siccnormal_driver.set_termios = siccuart_set_termios; + siccnormal_driver.stop = siccuart_stop; + siccnormal_driver.start = siccuart_start; + siccnormal_driver.hangup = siccuart_hangup; + siccnormal_driver.break_ctl = siccuart_break_ctl; + siccnormal_driver.wait_until_sent = siccuart_wait_until_sent; + siccnormal_driver.read_proc = NULL; + + /* + * The callout device is just like the normal device except for + * the major number and the subtype code. + */ + sicccallout_driver = siccnormal_driver; + sicccallout_driver.name = CALLOUT_SICC_NAME; + sicccallout_driver.major = CALLOUT_SICC_MAJOR; + sicccallout_driver.subtype = SERIAL_TYPE_CALLOUT; + sicccallout_driver.read_proc = NULL; + sicccallout_driver.proc_entry = NULL; + + if (tty_register_driver(&siccnormal_driver)) + panic("Couldn't register SICC serial driver\n"); + if (tty_register_driver(&sicccallout_driver)) + panic("Couldn't register SICC callout driver\n"); + + for (i = 0; i < SERIAL_SICC_NR; i++) { + struct SICC_state *state = sicc_state + i; + state->line = i; + state->close_delay = 5 * HZ / 10; + state->closing_wait = 30 * HZ; + state->callout_termios = sicccallout_driver.init_termios; + state->normal_termios = siccnormal_driver.init_termios; + } + + + return 0; +} + +__initcall(siccuart_init); + +#ifdef CONFIG_SERIAL_SICC_CONSOLE +/************** console driver *****************/ + +/* + * This code is currently never used; console->read is never called. + * Therefore, although we have an implementation, we don't use it. + * FIXME: the "const char *s" should be fixed to "char *s" some day. + * (when the definition in include/linux/console.h is also fixed) + */ +#ifdef used_and_not_const_char_pointer +static int siccuart_console_read(struct console *co, const char *s, u_int count) +{ + struct SICC_port *port = &sicc_ports[co->index]; + unsigned int status; + char *w; + int c; +#if DEBUG + printk("siccuart_console_read() called\n"); +#endif + + c = 0; + w = s; + while (c < count) { + if(readb(port->uart_base + BL_SICC_LSR) & _LSR_RBR_FULL) { + *w++ = readb(port->uart_base + BL_SICC_RBR); + c++; + } else { + // nothing more to get, return + return c; + } + } + // return the count + return c; +} +#endif + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void siccuart_console_write(struct console *co, const char *s, u_int count) +{ + struct SICC_port *port = &sicc_ports[co->index]; + unsigned int old_cr; + int i; + + /* + * First save the CR then disable the interrupts + */ + old_cr = readb(port->uart_base + BL_SICC_TxCR); + writeb(old_cr & ~_TxCR_DME_MASK, port->uart_base + BL_SICC_TxCR); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + while ((readb(port->uart_base + BL_SICC_LSR)&_LSR_TX_ALL) != _LSR_TX_ALL); + writeb(s[i], port->uart_base + BL_SICC_TBR); + if (s[i] == '\n') { + while ((readb(port->uart_base + BL_SICC_LSR)&_LSR_TX_ALL) != _LSR_TX_ALL); + writeb('\r', port->uart_base + BL_SICC_TBR); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + while ((readb(port->uart_base + BL_SICC_LSR)&_LSR_TX_ALL) != _LSR_TX_ALL); + writeb(old_cr, port->uart_base + BL_SICC_TxCR); +} + +/* + * Receive character from the serial port + */ +static int siccuart_console_wait_key(struct console *co) +{ + struct SICC_port *port = &sicc_ports[co->index]; + int c; + + while(!(readb(port->uart_base + BL_SICC_LSR) & _LSR_RBR_FULL)); + c = readb(port->uart_base + BL_SICC_RBR); + return c; +} + +static kdev_t siccuart_console_device(struct console *c) +{ + return MKDEV(SERIAL_SICC_MAJOR, SERIAL_SICC_MINOR + c->index); +} + + +static int __init siccuart_console_setup(struct console *co, char *options) +{ + struct SICC_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + u_int cflag = CREAD | HUPCL | CLOCAL; + u_int lcr_h, quot; + + + if (co->index >= SERIAL_SICC_NR) + co->index = 0; + + port = &sicc_ports[co->index]; + + if (port->uart_base == 0) + port->uart_base = (int)ioremap(port->uart_base_phys, PAGE_SIZE); + + if (options) { + char *s = options; + baud = simple_strtoul(s, NULL, 10); + while (*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s - '0'; + } + + /* + * Now construct a cflag setting. + */ + switch (baud) { + case 1200: cflag |= B1200; break; + case 2400: cflag |= B2400; break; + case 4800: cflag |= B4800; break; + default: cflag |= B9600; baud = 9600; break; + case 19200: cflag |= B19200; break; + case 38400: cflag |= B38400; break; + case 57600: cflag |= B57600; break; + case 115200: cflag |= B115200; break; + } + switch (bits) { + case 7: cflag |= CS7; lcr_h = _LCR_PE_DISABLE | _LCR_DB_7_BITS | _LCR_SB_1_BIT; break; + default: cflag |= CS8; lcr_h = _LCR_PE_DISABLE | _LCR_DB_8_BITS | _LCR_SB_1_BIT; break; + } + switch (parity) { + case 'o': + case 'O': cflag |= PARODD; lcr_h |= _LCR_PTY_ODD; break; + case 'e': + case 'E': cflag |= PARENB; lcr_h |= _LCR_PE_ENABLE | _LCR_PTY_ODD; break; + } + + co->cflag = cflag; + + + { + // a copy of is inserted here ppc403SetBaud(com_port, (int)9600); + unsigned long divisor, clockSource, temp; + unsigned int rate = baud; + + /* Ensure CICCR[7] is 0 to select Internal Baud Clock */ + powerpcMtcic_cr((unsigned long)(powerpcMfcic_cr() & 0xFEFFFFFF)); + + /* Determine Internal Baud Clock Frequency */ + /* powerpcMfclkgpcr() reads DCR 0x120 - the*/ + /* SCCR (Serial Clock Control Register) on Vesta */ + temp = powerpcMfclkgpcr(); + + if(temp & 0x00000080) { + clockSource = 324000000; + } + else { + clockSource = 216000000; + } + clockSource = clockSource/(unsigned long)((temp&0x00FC0000)>>18); + divisor = clockSource/(16*rate) - 1; + /* divisor has only 12 bits of resolution */ + if(divisor>0x00000FFF){ + divisor=0x00000FFF; + } + + quot = divisor; + } + + writeb((quot & 0x00000F00)>>8, port->uart_base + BL_SICC_BRDH ); + writeb( quot & 0x00000FF, port->uart_base + BL_SICC_BRDL ); + + /* Set CTL2 reg to use external clock (ExtClk) and enable FIFOs. */ + /* For now, do NOT use FIFOs since 403 UART did not have this */ + /* capability and this driver was inherited from 403UART. */ + writeb(_CTL2_EXTERN, port->uart_base + BL_SICC_CTL2); + + writeb(lcr_h, port->uart_base + BL_SICC_LCR); + writeb(_RCR_ER_ENABLE | _RCR_PME_HARD, port->uart_base + BL_SICC_RCR); + writeb( _TxCR_ET_ENABLE , port->uart_base + BL_SICC_TxCR); + + // writeb(, info->port->uart_base + BL_SICC_RCR ); + /* + * Transmitter Command Register: Transmitter enabled & DMA + TBR interrupt + * + Transmitter Empty interrupt + Transmitter error interrupt disabled & + * Stop mode when CTS active enabled & Transmit Break + Pattern Generation + * mode disabled. + */ + + writeb( 0x00, port->uart_base + BL_SICC_IrCR ); // disable IrDA + + readb(port->uart_base + BL_SICC_RBR); + + writeb(0xf8, port->uart_base + BL_SICC_LSR); /* reset bits 0-4 of LSR */ + + /* we will enable the port as we need it */ + + return 0; +} + +static struct console siccuart_cons = +{ + name: SERIAL_SICC_NAME, + write: siccuart_console_write, +#ifdef used_and_not_const_char_pointer + read: siccuart_console_read, +#endif + device: siccuart_console_device, + wait_key: siccuart_console_wait_key, + setup: siccuart_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init sicc_console_init(void) +{ + register_console(&siccuart_cons); +} + +#endif /* CONFIG_SERIAL_SICC_CONSOLE */ diff -Nru a/arch/ppc/4xx_io/stb_kb.c b/arch/ppc/4xx_io/stb_kb.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/4xx_io/stb_kb.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,293 @@ +/* + * arch/ppc/4xx_io/stb_kb.c + * + * 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 archive for + * more details. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* the following are borrowed from pc_keyb.c, thanks to those involved! */ +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* + * The keycodes below are randomly located in 89-95,112-118,120-127. + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* tfj@olivia.ping.dk: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ +}; + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +void __init rawirkbd_init_hw(void) { + +} +/* +int rawirkbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + return -EINVAL; +} + +int rawirkbd_getkeycode(unsigned int scancode) +{ + return -EINVAL; +} +*/ + +int rawirkbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 127) + return -EINVAL; + if (scancode < 128) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 128] = keycode; + return 0; +} + +int rawirkbd_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < 128) ? high_keys[scancode - SC_LIM] : + e0_keys[scancode - 128]; +} + +int rawirkbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + static int prev_scancode = 0; + + /* special prefix scancodes.. */ + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */ + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + + scancode &= 0x7f; + + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else if (scancode >= SC_LIM) { + /* This happens with the FOCUS 9000 keyboard + Its keys PF1..PF12 are reported to generate + 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f + Moreover, unless repeated, they do not generate + key-down events, so we have to zero up_flag below */ + /* Also, Japanese 86/106 keyboards are reported to + generate 0x73 and 0x7d for \ - and \ | respectively. */ + /* Also, some Brazilian keyboard is reported to produce + 0x73 and 0x7e for \ ? and KP-dot, respectively. */ + + *keycode = high_keys[scancode - SC_LIM]; + + if (!*keycode) { + if (!raw_mode) { +#ifdef KBD_REPORT_UNKN + printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" + " - ignored\n", scancode); +#endif + } + return 0; + } + } else + *keycode = scancode; + return 1; +} + +char rawirkbd_unexpected_up(unsigned char keycode) +{ + /* unexpected, but this can happen: maybe this was a key release for a + FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */ + if (keycode >= SC_LIM || keycode == 85) + return 0; + else + return 0200; +} + +#include + +void redwood_irkb_init(void) +{ + extern struct machdep_calls ppc_md; + + ppc_md.kbd_setkeycode = rawirkbd_setkeycode; + ppc_md.kbd_getkeycode = rawirkbd_getkeycode; + ppc_md.kbd_translate = rawirkbd_translate; + ppc_md.kbd_unexpected_up = rawirkbd_unexpected_up; + ppc_md.kbd_leds = NULL; /*rawirkbd_leds;*/ + ppc_md.kbd_init_hw = rawirkbd_init_hw; +} + diff -Nru a/arch/ppc/8260_io/Config.in b/arch/ppc/8260_io/Config.in --- a/arch/ppc/8260_io/Config.in Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/8260_io/Config.in Tue Feb 19 18:08:57 2002 @@ -1,6 +1,9 @@ # # MPC8260 Communication options # +mainmenu_option next_comment +comment 'MPC8260 Communication Options' +bool 'Enable SCC Console' CONFIG_SCC_CONSOLE if [ "$CONFIG_NET_ETHERNET" = "y" ]; then mainmenu_option next_comment comment 'MPC8260 Communication Options' @@ -20,6 +23,13 @@ bool 'Ethernet on FCC1' CONFIG_FCC1_ENET bool 'Ethernet on FCC2' CONFIG_FCC2_ENET bool 'Ethernet on FCC3' CONFIG_FCC3_ENET + bool 'Use MDIO for PHY configuration' CONFIG_USE_MDIO + if [ "$CONFIG_USE_MDIO" = "y" ]; then + choice 'Type of PHY' \ + "LXT970 CONFIG_FCC_LXT970 \ + LXT971 CONFIG_FCC_LXT971 \ + QS6612 CONFIG_FCC_QS6612" CONFIG_FCC_LXT971 + fi fi - endmenu fi +endmenu diff -Nru a/arch/ppc/8260_io/fcc_enet.c b/arch/ppc/8260_io/fcc_enet.c --- a/arch/ppc/8260_io/fcc_enet.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/8260_io/fcc_enet.c Tue Feb 19 18:08:59 2002 @@ -1,21 +1,23 @@ /* - * BK Id: SCCS/s.fcc_enet.c 1.7 05/17/01 18:14:20 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * Fast Ethernet Controller (FCC) driver for Motorola MPC8260. * Copyright (c) 2000 MontaVista Software, Inc. Dan Malek (dmalek@jlc.net) * * This version of the driver is a combination of the 8xx fec and - * 8260 SCC Ethernet drivers. People seem to be choosing common I/O - * configurations, so this driver will work on the EST8260 boards and - * others yet to be announced. + * 8260 SCC Ethernet drivers. This version has some additional + * configuration options, which should probably be moved out of + * here. This driver currently works for the EST SBC8260, + * SBS Diablo/BCM, Embedded Planet RPX6, TQM8260, and others. * * Right now, I am very watseful with the buffers. I allocate memory * pages and then divide them into 2K frame buffers. This way I know I * have buffers large enough to hold one frame within one buffer descriptor. * Once I get this working, I will use 64 or 128 byte CPM buffers, which * will be much more memory efficient and will easily handle lots of - * small packets. + * small packets. Since this is a cache coherent processor and CPM, + * I could also preallocate SKB's and use them directly on the interface. * */ @@ -48,6 +50,56 @@ */ #define TX_TIMEOUT (2*HZ) +#ifdef CONFIG_USE_MDIO +/* Forward declarations of some structures to support different PHYs */ + +typedef struct { + uint mii_data; + void (*funct)(uint mii_reg, struct net_device *dev); +} phy_cmd_t; + +typedef struct { + uint id; + char *name; + + const phy_cmd_t *config; + const phy_cmd_t *startup; + const phy_cmd_t *ack_int; + const phy_cmd_t *shutdown; +} phy_info_t; + +/* Register definitions for the PHY. */ + +#define MII_REG_CR 0 /* Control Register */ +#define MII_REG_SR 1 /* Status Register */ +#define MII_REG_PHYIR1 2 /* PHY Identification Register 1 */ +#define MII_REG_PHYIR2 3 /* PHY Identification Register 2 */ +#define MII_REG_ANAR 4 /* A-N Advertisement Register */ +#define MII_REG_ANLPAR 5 /* A-N Link Partner Ability Register */ +#define MII_REG_ANER 6 /* A-N Expansion Register */ +#define MII_REG_ANNPTR 7 /* A-N Next Page Transmit Register */ +#define MII_REG_ANLPRNPR 8 /* A-N Link Partner Received Next Page Reg. */ + +/* values for phy_status */ + +#define PHY_CONF_ANE 0x0001 /* 1 auto-negotiation enabled */ +#define PHY_CONF_LOOP 0x0002 /* 1 loopback mode enabled */ +#define PHY_CONF_SPMASK 0x00f0 /* mask for speed */ +#define PHY_CONF_10HDX 0x0010 /* 10 Mbit half duplex supported */ +#define PHY_CONF_10FDX 0x0020 /* 10 Mbit full duplex supported */ +#define PHY_CONF_100HDX 0x0040 /* 100 Mbit half duplex supported */ +#define PHY_CONF_100FDX 0x0080 /* 100 Mbit full duplex supported */ + +#define PHY_STAT_LINK 0x0100 /* 1 up - 0 down */ +#define PHY_STAT_FAULT 0x0200 /* 1 remote fault */ +#define PHY_STAT_ANC 0x0400 /* 1 auto-negotiation complete */ +#define PHY_STAT_SPMASK 0xf000 /* mask for speed */ +#define PHY_STAT_10HDX 0x1000 /* 10 Mbit half duplex selected */ +#define PHY_STAT_10FDX 0x2000 /* 10 Mbit full duplex selected */ +#define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */ +#define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */ +#endif /* CONFIG_USE_MDIO */ + /* The number of Tx and Rx buffers. These are allocated from the page * pool. The code may assume these are power of two, so it is best * to keep them that size. @@ -77,12 +129,12 @@ static int fcc_enet_open(struct net_device *dev); static int fcc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev); static int fcc_enet_rx(struct net_device *dev); -static void fcc_enet_mii(struct net_device *dev); static void fcc_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs); static int fcc_enet_close(struct net_device *dev); static struct net_device_stats *fcc_enet_get_stats(struct net_device *dev); static void set_multicast_list(struct net_device *dev); -static void restart_fcc(struct net_device *dev); +static void fcc_restart(struct net_device *dev, int duplex); +static int fcc_enet_set_mac_address(struct net_device *dev, void *addr); /* These will be configurable for the FCC choice. * Multiple ports can be configured. There is little choice among the @@ -166,8 +218,14 @@ /* MII status/control serial interface. */ -#define PC_MDIO ((uint)0x00400000) -#define PC_MDCK ((uint)0x00200000) +#ifdef CONFIG_TQM8260 +/* TQM8260 has MDIO and MDCK on PC30 and PC31 respectively */ +#define PC_MDIO ((uint)0x00000002) +#define PC_MDCK ((uint)0x00000001) +#else +#define PC_MDIO ((uint)0x00000004) +#define PC_MDCK ((uint)0x00000020) +#endif /* A table of information for supporting FCCs. This does two things. * First, we know how many FCCs we have and they are always externally @@ -191,17 +249,31 @@ #ifdef CONFIG_FCC1_ENET { 0, CPM_CR_FCC1_SBLOCK, CPM_CR_FCC1_PAGE, PROFF_FCC1, SIU_INT_FCC1, (PC_F1RXCLK | PC_F1TXCLK), CMX1_CLK_ROUTE, CMX1_CLK_MASK, +# if defined(CONFIG_TQM8260) PC_MDIO, PC_MDCK }, +# else + 0x00000004, 0x00000100 }, +# endif #endif #ifdef CONFIG_FCC2_ENET { 1, CPM_CR_FCC2_SBLOCK, CPM_CR_FCC2_PAGE, PROFF_FCC2, SIU_INT_FCC2, (PC_F2RXCLK | PC_F2TXCLK), CMX2_CLK_ROUTE, CMX2_CLK_MASK, +# if defined(CONFIG_TQM8260) PC_MDIO, PC_MDCK }, +# elif defined(CONFIG_EST8260) || defined(CONFIG_ADS8260) + 0x00400000, 0x00200000 }, +# else + 0x00000002, 0x00000080 }, +# endif #endif #ifdef CONFIG_FCC3_ENET { 2, CPM_CR_FCC3_SBLOCK, CPM_CR_FCC3_PAGE, PROFF_FCC3, SIU_INT_FCC3, (PC_F3RXCLK | PC_F3TXCLK), CMX3_CLK_ROUTE, CMX3_CLK_MASK, +# if defined(CONFIG_TQM8260) PC_MDIO, PC_MDCK }, +# else + 0x00000001, 0x00000040 }, +# endif #endif }; @@ -230,9 +302,23 @@ struct net_device_stats stats; uint tx_full; spinlock_t lock; - uint phy_address; - uint phy_type; - uint phy_duplex; + +#ifdef CONFIG_USE_MDIO + uint phy_id; + uint phy_id_done; + uint phy_status; + phy_info_t *phy; + struct tq_struct phy_task; + + uint sequence_done; + + uint phy_addr; +#endif /* CONFIG_USE_MDIO */ + + int link; + int old_link; + int full_duplex; + fcc_info_t *fip; }; @@ -244,55 +330,31 @@ static void init_fcc_param(fcc_info_t *fip, struct net_device *dev, volatile immap_t *immap); -/* MII processing. We keep this as simple as possible. Requests are - * placed on the list (if there is room). When the request is finished - * by the MII, an optional function may be called. - */ -typedef struct mii_list { - uint mii_regval; - void (*mii_func)(uint val, struct net_device *dev); - struct mii_list *mii_next; -} mii_list_t; - -#define NMII 20 -mii_list_t mii_cmds[NMII]; -mii_list_t *mii_free; -mii_list_t *mii_head; -mii_list_t *mii_tail; - -static int phyaddr; -static uint phytype; - -static int mii_queue(int request, void (*func)(uint, struct net_device *)); -static void mii_startup_cmds(void); +#ifdef CONFIG_USE_MDIO +static int mii_queue(struct net_device *dev, int request, void (*func)(uint, struct net_device *)); static uint mii_send_receive(fcc_info_t *fip, uint cmd); +static void fcc_stop(struct net_device *dev); + /* Make MII read/write commands for the FCC. */ - -#define mk_mii_phyaddr(ADDR) (0x60020000 | ((ADDR) << 23) | (2 << 18)) - -#define mk_mii_read(REG) (0x60020000 | ((phyaddr << 23) | \ - (REG & 0x1f) << 18)) - -#define mk_mii_write(REG, VAL) (0x50020000 | ((phyaddr << 23) | \ - (REG & 0x1f) << 18) | \ +#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18)) +#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \ (VAL & 0xffff)) +#define mk_mii_end 0 +#endif /* CONFIG_USE_MDIO */ static int -fcc_enet_open(struct net_device *dev) -{ - netif_start_queue(dev); - return 0; /* Always succeed */ -} - -static int fcc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct fcc_enet_private *cep = (struct fcc_enet_private *)dev->priv; volatile cbd_t *bdp; + if (!cep->link) { + /* Link is down or autonegotiation is in progress. */ + return 1; + } /* Fill in a Tx ring entry */ bdp = cep->cur_tx; @@ -307,24 +369,20 @@ } #endif - /* Clear all of the status flags. - */ + /* Clear all of the status flags. */ bdp->cbd_sc &= ~BD_ENET_TX_STATS; - /* If the frame is short, tell CPM to pad it. - */ + /* If the frame is short, tell CPM to pad it. */ if (skb->len <= ETH_ZLEN) bdp->cbd_sc |= BD_ENET_TX_PAD; else bdp->cbd_sc &= ~BD_ENET_TX_PAD; - /* Set buffer length and buffer pointer. - */ + /* Set buffer length and buffer pointer. */ bdp->cbd_datlen = skb->len; bdp->cbd_bufaddr = __pa(skb->data); - /* Save skb pointer. - */ + /* Save skb pointer. */ cep->tx_skbuff[cep->skb_cur] = skb; cep->stats.tx_bytes += skb->len; @@ -338,14 +396,12 @@ bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC); #if 0 - /* Errata says don't do this. - */ + /* Errata says don't do this. */ cep->fccp->fcc_ftodr = 0x8000; #endif dev->trans_start = jiffies; - /* If this was the last BD in the ring, start at the beginning again. - */ + /* If this was the last BD in the ring, start at the beginning again. */ if (bdp->cbd_sc & BD_ENET_TX_WRAP) bdp = cep->tx_bd_base; else @@ -398,8 +454,7 @@ netif_wake_queue(dev); } -/* The interrupt handler. - */ +/* The interrupt handler. */ static void fcc_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs) { @@ -469,13 +524,11 @@ if (bdp->cbd_sc & BD_ENET_TX_DEF) cep->stats.collisions++; - /* Free the sk buffer associated with this last transmit. - */ + /* Free the sk buffer associated with this last transmit. */ dev_kfree_skb_irq(cep->tx_skbuff[cep->skb_dirty]); cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK; - /* Update pointer to next buffer descriptor to be transmitted. - */ + /* Update pointer to next buffer descriptor to be transmitted. */ if (bdp->cbd_sc & BD_ENET_TX_WRAP) bdp = cep->tx_bd_base; else @@ -510,8 +563,13 @@ * down. We now issue a restart transmit. Since the * errors close the BD and update the pointers, the restart * _should_ pick up without having to reset any of our - * pointers either. + * pointers either. Also, To workaround 8260 device erratum + * CPM37, we must disable and then re-enable the transmitter + * following a Late Collision, Underrun, or Retry Limit error. */ + cep->fccp->fcc_gfmr &= ~FCC_GFMR_ENT; + udelay(10); /* wait a few microseconds just on principle */ + cep->fccp->fcc_gfmr |= FCC_GFMR_ENT; cp = cpmp; cp->cp_cpcr = @@ -564,8 +622,7 @@ printk("CPM ENET: rcv is not first+last\n"); #endif - /* Frame too long or too short. - */ + /* Frame too long or too short. */ if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) cep->stats.rx_length_errors++; if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ @@ -574,28 +631,22 @@ cep->stats.rx_crc_errors++; if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ cep->stats.rx_crc_errors++; - - /* Report late collisions as a frame error. - * On this error, the BD is closed, but we don't know what we - * have in the buffer. So, just drop this frame on the floor. - */ - if (bdp->cbd_sc & BD_ENET_RX_CL) { + if (bdp->cbd_sc & BD_ENET_RX_CL) /* Late Collision */ cep->stats.rx_frame_errors++; - } - else { - /* Process the incoming frame. - */ + if (!(bdp->cbd_sc & + (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | BD_ENET_RX_CR + | BD_ENET_RX_OV | BD_ENET_RX_CL))) + { + /* Process the incoming frame. */ cep->stats.rx_packets++; - pkt_len = bdp->cbd_datlen; + + /* Remove the FCS from the packet length. */ + pkt_len = bdp->cbd_datlen - 4; cep->stats.rx_bytes += pkt_len; - /* This does 16 byte alignment, much more than we need. - * The packet length includes FCS, but we don't want to - * include that when passing upstream as it messes up - * bridging applications. - */ - skb = dev_alloc_skb(pkt_len-4); + /* This does 16 byte alignment, much more than we need. */ + skb = dev_alloc_skb(pkt_len); if (skb == NULL) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); @@ -603,25 +654,22 @@ } else { skb->dev = dev; - skb_put(skb,pkt_len-4); /* Make room */ + skb_put(skb,pkt_len); /* Make room */ eth_copy_and_sum(skb, (unsigned char *)__va(bdp->cbd_bufaddr), - pkt_len-4, 0); + pkt_len, 0); skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); } } - /* Clear the status flags for this buffer. - */ + /* Clear the status flags for this buffer. */ bdp->cbd_sc &= ~BD_ENET_RX_STATS; - /* Mark the buffer empty. - */ + /* Mark the buffer empty. */ bdp->cbd_sc |= BD_ENET_RX_EMPTY; - /* Update BD pointer to next entry. - */ + /* Update BD pointer to next entry. */ if (bdp->cbd_sc & BD_ENET_RX_WRAP) bdp = cep->rx_bd_base; else @@ -636,8 +684,7 @@ static int fcc_enet_close(struct net_device *dev) { - /* Don't know what to do yet. - */ + /* Don't know what to do yet. */ netif_stop_queue(dev); return 0; @@ -650,340 +697,527 @@ return &cep->stats; } -/* The MII is simulated from the 8xx FEC implementation. The FCC - * is not responsible for the MII control/status interface. +#ifdef CONFIG_USE_MDIO + +/* NOTE: Most of the following comes from the FEC driver for 860. The + * overall structure of MII code has been retained (as it's proved stable + * and well-tested), but actual transfer requests are processed "at once" + * instead of being queued (there's no interrupt-driven MII transfer + * mechanism, one has to toggle the data/clock bits manually). */ -static void -fcc_enet_mii(struct net_device *dev) +static int +mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *)) { - struct fcc_enet_private *fep; - mii_list_t *mip; - uint mii_reg; + struct fcc_enet_private *fep; + int retval, tmp; - fep = (struct fcc_enet_private *)dev->priv; -#if 0 - ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); - mii_reg = ep->fec_mii_data; -#endif - - if ((mip = mii_head) == NULL) { - printk("MII and no head!\n"); + /* Add PHY address to register command. */ + fep = dev->priv; + regval |= fep->phy_addr << 23; + + retval = 0; + + tmp = mii_send_receive(fep->fip, regval); + if (func) + func(tmp, dev); + + return retval; +} + +static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c) +{ + int k; + + if(!c) return; - } - if (mip->mii_func != NULL) - (*(mip->mii_func))(mii_reg, dev); + for(k = 0; (c+k)->mii_data != mk_mii_end; k++) + mii_queue(dev, (c+k)->mii_data, (c+k)->funct); +} - mii_head = mip->mii_next; - mip->mii_next = mii_free; - mii_free = mip; +static void mii_parse_sr(uint mii_reg, struct net_device *dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; -#if 0 - if ((mip = mii_head) != NULL) - ep->fec_mii_data = mip->mii_regval; -#endif + s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); + + if (mii_reg & 0x0004) + s |= PHY_STAT_LINK; + if (mii_reg & 0x0010) + s |= PHY_STAT_FAULT; + if (mii_reg & 0x0020) + s |= PHY_STAT_ANC; + + fep->phy_status = s; + fep->link = (s & PHY_STAT_LINK) ? 1 : 0; } -static int -mii_queue(int regval, void (*func)(uint, struct net_device *)) +static void mii_parse_cr(uint mii_reg, struct net_device *dev) { - unsigned long flags; - mii_list_t *mip; - int retval; + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; - retval = 0; + s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP); - save_flags(flags); - cli(); + if (mii_reg & 0x1000) + s |= PHY_CONF_ANE; + if (mii_reg & 0x4000) + s |= PHY_CONF_LOOP; - if ((mip = mii_free) != NULL) { - mii_free = mip->mii_next; - mip->mii_regval = regval; - mip->mii_func = func; - mip->mii_next = NULL; - if (mii_head) { - mii_tail->mii_next = mip; - mii_tail = mip; - } - else { - mii_head = mii_tail = mip; -#if 0 - (&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec))->fec_mii_data = regval; -#endif - } - } - else { - retval = 1; - } + fep->phy_status = s; +} - restore_flags(flags); +static void mii_parse_anar(uint mii_reg, struct net_device *dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_CONF_SPMASK); + + if (mii_reg & 0x0020) + s |= PHY_CONF_10HDX; + if (mii_reg & 0x0040) + s |= PHY_CONF_10FDX; + if (mii_reg & 0x0080) + s |= PHY_CONF_100HDX; + if (mii_reg & 0x00100) + s |= PHY_CONF_100FDX; - return(retval); + fep->phy_status = s; } +/* ------------------------------------------------------------------------- */ +/* The Level one LXT970 is used by many boards */ -static volatile uint full_duplex; +#ifdef CONFIG_FCC_LXT970 -static void -mii_status(uint mii_reg, struct net_device *dev) +#define MII_LXT970_MIRROR 16 /* Mirror register */ +#define MII_LXT970_IER 17 /* Interrupt Enable Register */ +#define MII_LXT970_ISR 18 /* Interrupt Status Register */ +#define MII_LXT970_CONFIG 19 /* Configuration Register */ +#define MII_LXT970_CSR 20 /* Chip Status Register */ + +static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev) { - volatile uint prev_duplex; + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; - if (((mii_reg >> 18) & 0x1f) == 1) { - /* status register. - */ - printk("fec: "); - if (mii_reg & 0x0004) - printk("link up"); - else - printk("link down"); + s &= ~(PHY_STAT_SPMASK); - if (mii_reg & 0x0010) - printk(",remote fault"); - if (mii_reg & 0x0020) - printk(",auto complete"); - printk("\n"); - } - if (((mii_reg >> 18) & 0x1f) == 0x14) { - /* Extended chip status register. - */ - prev_duplex = full_duplex; - printk("fec: "); - if (mii_reg & 0x0800) - printk("100 Mbps"); + if (mii_reg & 0x0800) { + if (mii_reg & 0x1000) + s |= PHY_STAT_100FDX; else - printk("10 Mbps"); + s |= PHY_STAT_100HDX; + } else { + if (mii_reg & 0x1000) + s |= PHY_STAT_10FDX; + else + s |= PHY_STAT_10HDX; + } - if (mii_reg & 0x1000) { - printk(", Full-Duplex\n"); - full_duplex = 1; - } - else { - printk(", Half-Duplex\n"); - full_duplex = 0; - } + fep->phy_status = s; +} + +static phy_info_t phy_info_lxt970 = { + 0x07810000, + "LXT970", + + (const phy_cmd_t []) { /* config */ #if 0 - if (prev_duplex != full_duplex) - restart_fec(dev); -#endif - } - if (((mii_reg >> 18) & 0x1f) == 31) { - /* QS6612 PHY Control/Status. - * OK, now we have it all, so figure out what is going on. +// { mk_mii_write(MII_REG_ANAR, 0x0021), NULL }, + + /* Set default operation of 100-TX....for some reason + * some of these bits are set on power up, which is wrong. */ - prev_duplex = full_duplex; - printk("fec: "); + { mk_mii_write(MII_LXT970_CONFIG, 0), NULL }, +#endif + { mk_mii_read(MII_REG_CR), mii_parse_cr }, + { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_LXT970_IER, 0x0002), NULL }, + { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + /* read SR and ISR to acknowledge */ + + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_read(MII_LXT970_ISR), NULL }, + + /* find out the current status */ + + { mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_LXT970_IER, 0x0000), NULL }, + { mk_mii_end, } + }, +}; - mii_reg = (mii_reg >> 2) & 7; +#endif /* CONFIG_FEC_LXT970 */ - if (mii_reg & 1) - printk("10 Mbps"); - else - printk("100 Mbps"); +/* ------------------------------------------------------------------------- */ +/* The Level one LXT971 is used on some of my custom boards */ - if (mii_reg > 4) { - printk(", Full-Duplex\n"); - full_duplex = 1; - } - else { - printk(", Half-Duplex\n"); - full_duplex = 0; - } +#ifdef CONFIG_FCC_LXT971 -#if 0 - if (prev_duplex != full_duplex) - restart_fec(dev); -#endif - } -} +/* register definitions for the 971 */ -static uint phyno; +#define MII_LXT971_PCR 16 /* Port Control Register */ +#define MII_LXT971_SR2 17 /* Status Register 2 */ +#define MII_LXT971_IER 18 /* Interrupt Enable Register */ +#define MII_LXT971_ISR 19 /* Interrupt Status Register */ +#define MII_LXT971_LCR 20 /* LED Control Register */ +#define MII_LXT971_TCR 30 /* Transmit Control Register */ -static void -mii_discover_phy3(uint mii_reg, struct net_device *dev) +/* + * I had some nice ideas of running the MDIO faster... + * The 971 should support 8MHz and I tried it, but things acted really + * weird, so 2.5 MHz ought to be enough for anyone... + */ + +static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev) { - phytype <<= 16; - phytype |= (mii_reg & 0xffff); - printk("fec: Phy @ 0x%x, type 0x%08x\n", phyno, phytype); - mii_startup_cmds(); + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_STAT_SPMASK); + + if (mii_reg & 0x4000) { + if (mii_reg & 0x0200) + s |= PHY_STAT_100FDX; + else + s |= PHY_STAT_100HDX; + } else { + if (mii_reg & 0x0200) + s |= PHY_STAT_10FDX; + else + s |= PHY_STAT_10HDX; + } + if (mii_reg & 0x0008) + s |= PHY_STAT_FAULT; + + fep->phy_status = s; } -static void -mii_discover_phy(uint mii_reg, struct net_device *dev) +static phy_info_t phy_info_lxt971 = { + 0x0001378e, + "LXT971", + + (const phy_cmd_t []) { /* config */ +// { mk_mii_write(MII_REG_ANAR, 0x021), NULL }, /* 10 Mbps, HD */ + { mk_mii_read(MII_REG_CR), mii_parse_cr }, + { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL }, + { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ + + /* Somehow does the 971 tell me that the link is down + * the first read after power-up. + * read here to get a valid value in ack_int */ + + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + /* find out the current status */ + + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 }, + + /* we only need to read ISR to acknowledge */ + + { mk_mii_read(MII_LXT971_ISR), NULL }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_LXT971_IER, 0x0000), NULL }, + { mk_mii_end, } + }, +}; + +#endif /* CONFIG_FEC_LXT970 */ + + +/* ------------------------------------------------------------------------- */ +/* The Quality Semiconductor QS6612 is used on the RPX CLLF */ + +#ifdef CONFIG_FCC_QS6612 + +/* register definitions */ + +#define MII_QS6612_MCR 17 /* Mode Control Register */ +#define MII_QS6612_FTR 27 /* Factory Test Register */ +#define MII_QS6612_MCO 28 /* Misc. Control Register */ +#define MII_QS6612_ISR 29 /* Interrupt Source Register */ +#define MII_QS6612_IMR 30 /* Interrupt Mask Register */ +#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */ + +static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev) { - if (phyno < 32) { - if ((phytype = (mii_reg & 0xffff)) != 0xffff) { - phyaddr = phyno; - mii_queue(mk_mii_read(3), mii_discover_phy3); - } - else { - phyno++; - mii_queue(mk_mii_phyaddr(phyno), mii_discover_phy); - } - } - else { - printk("FEC: No PHY device found.\n"); + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_STAT_SPMASK); + + switch((mii_reg >> 2) & 7) { + case 1: s |= PHY_STAT_10HDX; break; + case 2: s |= PHY_STAT_100HDX; break; + case 5: s |= PHY_STAT_10FDX; break; + case 6: s |= PHY_STAT_100FDX; break; } + + fep->phy_status = s; } -static void -mii_discover_phy_poll(fcc_info_t *fip) +static phy_info_t phy_info_qs6612 = { + 0x00181440, + "QS6612", + + (const phy_cmd_t []) { /* config */ +// { mk_mii_write(MII_REG_ANAR, 0x061), NULL }, /* 10 Mbps */ + + /* The PHY powers up isolated on the RPX, + * so send a command to allow operation. + */ + + { mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL }, + + /* parse cr and anar to get some info */ + + { mk_mii_read(MII_REG_CR), mii_parse_cr }, + { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_QS6612_IMR, 0x003a), NULL }, + { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + + /* we need to read ISR, SR and ANER to acknowledge */ + + { mk_mii_read(MII_QS6612_ISR), NULL }, + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_read(MII_REG_ANER), NULL }, + + /* read pcr to get info */ + + { mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_QS6612_IMR, 0x0000), NULL }, + { mk_mii_end, } + }, +}; + + +#endif /* CONFIG_FEC_QS6612 */ + + +static phy_info_t *phy_info[] = { + +#ifdef CONFIG_FCC_LXT970 + &phy_info_lxt970, +#endif /* CONFIG_FEC_LXT970 */ + +#ifdef CONFIG_FCC_LXT971 + &phy_info_lxt971, +#endif /* CONFIG_FEC_LXT971 */ + +#ifdef CONFIG_FCC_QS6612 + &phy_info_qs6612, +#endif /* CONFIG_FEC_LXT971 */ + + NULL +}; + +static void mii_display_status(struct net_device *dev) { - uint rv; - int i; + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + if (!fep->link && !fep->old_link) { + /* Link is still down - don't print anything */ + return; + } + + printk("%s: status: ", dev->name); - for (i=0; i<32; i++) { - rv = mii_send_receive(fip, mk_mii_phyaddr(i)); - if ((phytype = (rv & 0xffff)) != 0xffff) { - phyaddr = i; - rv = mii_send_receive(fip, mk_mii_read(3)); - phytype <<= 16; - phytype |= (rv & 0xffff); - printk("fec: Phy @ 0x%x, type 0x%08x\n", phyaddr, phytype); + if (!fep->link) { + printk("link down"); + } else { + printk("link up"); + + switch(s & PHY_STAT_SPMASK) { + case PHY_STAT_100FDX: printk(", 100 Mbps Full Duplex"); break; + case PHY_STAT_100HDX: printk(", 100 Mbps Half Duplex"); break; + case PHY_STAT_10FDX: printk(", 10 Mbps Full Duplex"); break; + case PHY_STAT_10HDX: printk(", 10 Mbps Half Duplex"); break; + default: + printk(", Unknown speed/duplex"); } + + if (s & PHY_STAT_ANC) + printk(", auto-negotiation complete"); } + + if (s & PHY_STAT_FAULT) + printk(", remote fault"); + + printk(".\n"); } -static void -mii_startup_cmds(void) +static void mii_display_config(struct net_device *dev) { + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; -#if 1 - /* Level One PHY. - */ + printk("%s: config: auto-negotiation ", dev->name); - /* Read status registers to clear any pending interrupt. - */ - mii_queue(mk_mii_read(1), mii_status); - mii_queue(mk_mii_read(18), mii_status); + if (s & PHY_CONF_ANE) + printk("on"); + else + printk("off"); - /* Read extended chip status register. - */ - mii_queue(mk_mii_read(0x14), mii_status); + if (s & PHY_CONF_100FDX) + printk(", 100FDX"); + if (s & PHY_CONF_100HDX) + printk(", 100HDX"); + if (s & PHY_CONF_10FDX) + printk(", 10FDX"); + if (s & PHY_CONF_10HDX) + printk(", 10HDX"); + if (!(s & PHY_CONF_SPMASK)) + printk(", No speed/duplex selected?"); + + if (s & PHY_CONF_LOOP) + printk(", loopback enabled"); + + printk(".\n"); + + fep->sequence_done = 1; +} + +static void mii_relink(struct net_device *dev) +{ + struct fcc_enet_private *fep = dev->priv; + int duplex; + + fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0; + mii_display_status(dev); + fep->old_link = fep->link; + + if (fep->link) { + duplex = 0; + if (fep->phy_status + & (PHY_STAT_100FDX | PHY_STAT_10FDX)) + duplex = 1; + fcc_restart(dev, duplex); + } else { + fcc_stop(dev); + } +} - /* Set default operation of 100-TX....for some reason - * some of these bits are set on power up, which is wrong. - */ - mii_queue(mk_mii_write(0x13, 0), NULL); +static void mii_queue_relink(uint mii_reg, struct net_device *dev) +{ + struct fcc_enet_private *fep = dev->priv; - /* Enable Link status change interrupts. - */ - mii_queue(mk_mii_write(0x11, 0x0002), NULL); + fep->phy_task.routine = (void *)mii_relink; + fep->phy_task.data = dev; + schedule_task(&fep->phy_task); +} - /* Don't advertize Full duplex. - mii_queue(mk_mii_write(0x04, 0x0021), NULL); - */ -#endif +static void mii_queue_config(uint mii_reg, struct net_device *dev) +{ + struct fcc_enet_private *fep = dev->priv; + fep->phy_task.routine = (void *)mii_display_config; + fep->phy_task.data = dev; + schedule_task(&fep->phy_task); } -/* This supports the mii_link interrupt below. - * We should get called three times. Once for register 1, once for - * register 18, and once for register 20. - */ -static uint mii_saved_reg1; + +phy_cmd_t phy_cmd_relink[] = { { mk_mii_read(MII_REG_CR), mii_queue_relink }, + { mk_mii_end, } }; +phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config }, + { mk_mii_end, } }; + + +/* Read remainder of PHY ID. +*/ static void -mii_relink(uint mii_reg, struct net_device *dev) +mii_discover_phy3(uint mii_reg, struct net_device *dev) { - volatile uint prev_duplex; - unsigned long flags; + struct fcc_enet_private *fep; + int i; - if (((mii_reg >> 18) & 0x1f) == 1) { - /* Just save the status register and get out. - */ - mii_saved_reg1 = mii_reg; - return; - } - if (((mii_reg >> 18) & 0x1f) == 18) { - /* Not much here, but has to be read to clear the - * interrupt condition. - */ - if ((mii_reg & 0x8000) == 0) - printk("fec: re-link and no IRQ?\n"); - if ((mii_reg & 0x4000) == 0) - printk("fec: no PHY power?\n"); - } - if (((mii_reg >> 18) & 0x1f) == 20) { - /* Extended chip status register. - * OK, now we have it all, so figure out what is going on. - */ - prev_duplex = full_duplex; - printk("fec: "); - if (mii_saved_reg1 & 0x0004) - printk("link up"); - else - printk("link down"); + fep = dev->priv; + fep->phy_id |= (mii_reg & 0xffff); - if (mii_saved_reg1 & 0x0010) - printk(", remote fault"); - if (mii_saved_reg1 & 0x0020) - printk(", auto complete"); + for(i = 0; phy_info[i]; i++) + if(phy_info[i]->id == (fep->phy_id >> 4)) + break; - if (mii_reg & 0x0800) - printk(", 100 Mbps"); - else - printk(", 10 Mbps"); + if(!phy_info[i]) + panic("%s: PHY id 0x%08x is not supported!\n", + dev->name, fep->phy_id); - if (mii_reg & 0x1000) { - printk(", Full-Duplex\n"); - full_duplex = 1; - } - else { - printk(", Half-Duplex\n"); - full_duplex = 0; - } - if (prev_duplex != full_duplex) { - save_flags(flags); - cli(); -#if 0 - restart_fec(dev); -#endif - restore_flags(flags); - } - } - if (((mii_reg >> 18) & 0x1f) == 31) { - /* QS6612 PHY Control/Status. - * OK, now we have it all, so figure out what is going on. - */ - prev_duplex = full_duplex; - printk("fec: "); - if (mii_saved_reg1 & 0x0004) - printk("link up"); - else - printk("link down"); + fep->phy = phy_info[i]; - if (mii_saved_reg1 & 0x0010) - printk(", remote fault"); - if (mii_saved_reg1 & 0x0020) - printk(", auto complete"); + printk("%s: Phy @ 0x%x, type %s (0x%08x)\n", + dev->name, fep->phy_addr, fep->phy->name, fep->phy_id); +} - mii_reg = (mii_reg >> 2) & 7; +/* Scan all of the MII PHY addresses looking for someone to respond + * with a valid ID. This usually happens quickly. + */ +static void +mii_discover_phy(uint mii_reg, struct net_device *dev) +{ + struct fcc_enet_private *fep; + uint phytype; - if (mii_reg & 1) - printk(", 10 Mbps"); - else - printk(", 100 Mbps"); + fep = dev->priv; - if (mii_reg > 4) { - printk(", Full-Duplex\n"); - full_duplex = 1; - } - else { - printk(", Half-Duplex\n"); - full_duplex = 0; - } + if ((phytype = (mii_reg & 0xfff)) != 0xfff) { -#if 0 - if (prev_duplex != full_duplex) { - save_flags(flags); - cli(); - restart_fec(dev); - restore_flags(flags); + /* Got first part of ID, now get remainder. */ + fep->phy_id = phytype << 16; + mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3); + } else { + fep->phy_addr++; + if (fep->phy_addr < 32) { + mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), + mii_discover_phy); + } else { + printk("fec: No PHY device found.\n"); } -#endif } } +/* This interrupt occurs when the PHY detects a link change. */ +static void +mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + struct fcc_enet_private *fep = dev->priv; + + mii_do_cmd(dev, fep->phy->ack_int); + mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */ +} + +#endif /* CONFIG_USE_MDIO */ + /* Set or clear the multicast filter for this adaptor. * Skeleton taken from sunlance driver. * The CPM Ethernet implementation allows Multicast as well as individual @@ -1062,6 +1296,33 @@ } } + +/* Set the individual MAC address. + */ +int fcc_enet_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr= (struct sockaddr *) p; + struct fcc_enet_private *cep; + volatile fcc_enet_t *ep; + unsigned char *eap; + int i; + + cep = (struct fcc_enet_private *)(dev->priv); + ep = cep->ep; + + if (netif_running(dev)) + return -EBUSY; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + eap = (unsigned char *) &(ep->fen_paddrh); + for (i=5; i>=0; i--) + *eap++ = addr->sa_data[i]; + + return 0; +} + + /* Initialize the CPM Ethernet on FCC. */ int __init fec_enet_init(void) @@ -1113,19 +1374,22 @@ dev->stop = fcc_enet_close; dev->get_stats = fcc_enet_get_stats; dev->set_multicast_list = set_multicast_list; + dev->set_mac_address = fcc_enet_set_mac_address; init_fcc_startup(fip, dev); - printk("%s: FCC ENET Version 0.2, ", dev->name); + printk("%s: FCC ENET Version 0.3, ", dev->name); for (i=0; i<5; i++) printk("%02x:", dev->dev_addr[i]); printk("%02x\n", dev->dev_addr[5]); - /* This is just a hack for now that works only on the EST - * board, or anything else that has MDIO/CK configured. - * It is mainly to test the MII software clocking. - */ - mii_discover_phy_poll(fip); +#ifdef CONFIG_USE_MDIO + /* Queue up command to detect the PHY and initialize the + * remainder of the interface. + */ + cep->phy_addr = 0; + mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy); +#endif /* CONFIG_USE_MDIO */ fip++; } @@ -1200,12 +1464,14 @@ io->iop_pdirc &= ~(fip->fc_trxclocks); io->iop_pparc |= fip->fc_trxclocks; +#ifdef CONFIG_USE_MDIO /* ....and the MII serial clock/data. */ io->iop_pdatc |= (fip->fc_mdio | fip->fc_mdck); - io->iop_podrc |= fip->fc_mdio; + io->iop_podrc &= ~(fip->fc_mdio | fip->fc_mdck); io->iop_pdirc |= (fip->fc_mdio | fip->fc_mdck); io->iop_pparc &= ~(fip->fc_mdio | fip->fc_mdck); +#endif /* CONFIG_USE_MDIO */ /* Configure Serial Interface clock routing. * First, clear all FCC bits to zero, @@ -1446,6 +1712,12 @@ "fenet", dev) < 0) printk("Can't get FCC IRQ %d\n", fip->fc_interrupt); +#ifdef CONFIG_USE_MDIO + if (request_8xxirq(PHY_INTERRUPT, mii_link_interrupt, 0, + "mii", dev) < 0) + printk("Can't get MII IRQ %d\n", fip->fc_interrupt); +#endif /* CONFIG_USE_MDIO */ + /* Set GFMR to enable Ethernet operating mode. */ fccp->fcc_gfmr = (FCC_GFMR_TCI | FCC_GFMR_MODE_ENET); @@ -1460,11 +1732,25 @@ */ fccp->fcc_fpsmr = FCC_PSMR_ENCRC; - /* And last, enable the transmit and receive processing. +#ifdef CONFIG_ADS8260 + /* Enable the PHY. */ - fccp->fcc_gfmr |= (FCC_GFMR_ENR | FCC_GFMR_ENT); + ads_csr_addr[1] |= BCSR1_FETH_RST; /* Remove reset */ + ads_csr_addr[1] &= ~BCSR1_FETHIEN; /* Enable */ +#endif + +#if defined(CONFIG_USE_MDIO) || defined(CONFIG_TQM8260) + /* start in full duplex mode, and negotiate speed + */ + fcc_restart (dev, 1); +#else + /* start in half duplex mode + */ + fcc_restart (dev, 0); +#endif } +#ifdef CONFIG_USE_MDIO /* MII command/status interface. * I'm not going to describe all of the details. You can find the * protocol definition in many other places, including the data sheet @@ -1472,136 +1758,153 @@ * I wonder what "they" were thinking (maybe weren't) when they leave * the I2C in the CPM but I have to toggle these bits...... */ + +#define FCC_PDATC_MDIO(bit) \ + if (bit) \ + io->iop_pdatc |= fip->fc_mdio; \ + else \ + io->iop_pdatc &= ~fip->fc_mdio; + +#define FCC_PDATC_MDC(bit) \ + if (bit) \ + io->iop_pdatc |= fip->fc_mdck; \ + else \ + io->iop_pdatc &= ~fip->fc_mdck; + static uint mii_send_receive(fcc_info_t *fip, uint cmd) { - unsigned long flags; uint retval; - int read_op, i; + int read_op, i, off; volatile immap_t *immap; volatile iop8260_t *io; immap = (immap_t *)IMAP_ADDR; io = &immap->im_ioport; - /* When we get here, both clock and data are high, outputs. - * Output is open drain. - * Data transitions on high->low clock, is valid on low->high clock. - * Spec says edge transitions no closer than 160 nSec, minimum clock - * cycle 400 nSec. I could only manage about 500 nSec edges with - * an XOR loop, so I won't worry about delays yet. - * I disable interrupts during bit flipping to ensure atomic - * updates of the registers. I do lots of interrupt disable/enable - * to ensure we don't hang out too long with interrupts disabled. - */ - - /* First, crank out 32 1-bits as preamble. - * This is 64 transitions to clock the bits, with clock/data - * left high. - */ - save_flags(flags); - cli(); - for (i=0; i<64; i++) { - io->iop_pdatc ^= fip->fc_mdck; - udelay(0); - } - restore_flags(flags); + io->iop_pdirc |= (fip->fc_mdio | fip->fc_mdck); read_op = ((cmd & 0xf0000000) == 0x60000000); - /* We return the command word on a write op, or the command portion - * plus the new data on a read op. This is what the 8xx FEC does, - * and it allows the functions to simply look at the returned value - * and know the PHY/register as well. + /* Write preamble */ - if (read_op) - retval = cmd; - else - retval = (cmd >> 16); - - /* Clock out the first 16 MS bits of the command. - */ - save_flags(flags); - cli(); - for (i=0; i<16; i++) { - io->iop_pdatc &= ~(fip->fc_mdck); - if (cmd & 0x80000000) - io->iop_pdatc |= fip->fc_mdio; - else - io->iop_pdatc &= ~(fip->fc_mdio); - cmd <<= 1; - io->iop_pdatc |= fip->fc_mdck; - udelay(0); + for (i = 0; i < 32; i++) + { + FCC_PDATC_MDC(0); + FCC_PDATC_MDIO(1); + udelay(1); + FCC_PDATC_MDC(1); + udelay(1); } - /* Do the turn-around. If read op, we make the IO and input. - * If write op, do the 1/0 thing. + /* Write data */ - io->iop_pdatc &= ~(fip->fc_mdck); + for (i = 0, off = 31; i < (read_op ? 14 : 32); i++, --off) + { + FCC_PDATC_MDC(0); + FCC_PDATC_MDIO((cmd >> off) & 0x00000001); + udelay(1); + FCC_PDATC_MDC(1); + udelay(1); + } + + retval = cmd; + if (read_op) - io->iop_pdirc &= ~(fip->fc_mdio); - else - io->iop_pdatc |= fip->fc_mdio; - io->iop_pdatc |= fip->fc_mdck; + { + retval >>= 16; - /* I do this mainly to get just a little delay. - */ - restore_flags(flags); - save_flags(flags); - cli(); - io->iop_pdatc &= ~(fip->fc_mdck); - io->iop_pdirc &= ~(fip->fc_mdio); - io->iop_pdatc |= fip->fc_mdck; - - restore_flags(flags); - save_flags(flags); - cli(); - - /* For read, clock in 16 bits. For write, clock out - * rest of command. - */ - if (read_op) { - io->iop_pdatc &= ~(fip->fc_mdck); - udelay(0); - for (i=0; i<16; i++) { - io->iop_pdatc |= fip->fc_mdck; - udelay(0); + FCC_PDATC_MDC(0); + io->iop_pdirc &= ~fip->fc_mdio; + udelay(1); + FCC_PDATC_MDC(1); + udelay(1); + FCC_PDATC_MDC(0); + udelay(1); + + for (i = 0, off = 15; i < 16; i++, off--) + { + FCC_PDATC_MDC(1); retval <<= 1; if (io->iop_pdatc & fip->fc_mdio) - retval |= 1; - io->iop_pdatc &= ~(fip->fc_mdck); - udelay(0); + retval++; + udelay(1); + FCC_PDATC_MDC(0); + udelay(1); } } - else { - for (i=0; i<16; i++) { - io->iop_pdatc &= ~(fip->fc_mdck); - if (cmd & 0x80000000) - io->iop_pdatc |= fip->fc_mdio; - else - io->iop_pdatc &= ~(fip->fc_mdio); - cmd <<= 1; - io->iop_pdatc |= fip->fc_mdck; - udelay(0); - } - io->iop_pdatc &= ~(fip->fc_mdck); + + io->iop_pdirc |= (fip->fc_mdio | fip->fc_mdck); + + for (i = 0; i < 32; i++) + { + FCC_PDATC_MDC(0); + FCC_PDATC_MDIO(1); + udelay(1); + FCC_PDATC_MDC(1); + udelay(1); } - restore_flags(flags); - /* Some diagrams show two 1 bits for "idle". I don't know if - * this is really necessary or if it was just to indicate nothing - * is going to happen for a while. - * Make the data pin an output, set the data high, and clock it. - */ - save_flags(flags); - cli(); - io->iop_pdatc |= fip->fc_mdio; - io->iop_pdirc |= fip->fc_mdio; - for (i=0; i<3; i++) - io->iop_pdatc ^= fip->fc_mdck; - restore_flags(flags); + return retval; +} + +static void +fcc_stop(struct net_device *dev) +{ + volatile fcc_t *fccp; + struct fcc_enet_private *fcp; - /* We exit with the same conditions as entry. - */ - return(retval); + fcp = (struct fcc_enet_private *)(dev->priv); + fccp = fcp->fccp; + + /* Disable transmit/receive */ + fccp->fcc_gfmr &= ~(FCC_GFMR_ENR | FCC_GFMR_ENT); +} +#endif /* CONFIG_USE_MDIO */ + +static void +fcc_restart(struct net_device *dev, int duplex) +{ + volatile fcc_t *fccp; + struct fcc_enet_private *fcp; + + fcp = (struct fcc_enet_private *)(dev->priv); + fccp = fcp->fccp; + + if (duplex) + fccp->fcc_fpsmr |= FCC_PSMR_FDE; + else + fccp->fcc_fpsmr &= ~FCC_PSMR_FDE; + + /* Enable transmit/receive */ + fccp->fcc_gfmr |= FCC_GFMR_ENR | FCC_GFMR_ENT; +} + +static int +fcc_enet_open(struct net_device *dev) +{ + struct fcc_enet_private *fep = dev->priv; + +#ifdef CONFIG_USE_MDIO + fep->sequence_done = 0; + fep->link = 0; + + if (fep->phy) { + mii_do_cmd(dev, fep->phy->ack_int); + mii_do_cmd(dev, fep->phy->config); + mii_do_cmd(dev, phy_cmd_config); /* display configuration */ + while(!fep->sequence_done) + schedule(); + + mii_do_cmd(dev, fep->phy->startup); + netif_start_queue(dev); + return 0; /* Success */ + } + return -ENODEV; /* No PHY we understand */ +#else + fep->link = 1; + netif_start_queue(dev); + return 0; /* Always succeed */ +#endif /* CONFIG_USE_MDIO */ } + diff -Nru a/arch/ppc/8260_io/uart.c b/arch/ppc/8260_io/uart.c --- a/arch/ppc/8260_io/uart.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/8260_io/uart.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.uart.c 1.6 05/17/01 18:14:20 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * UART driver for MPC8260 CPM SCC or SMC @@ -53,12 +53,21 @@ #ifdef CONFIG_SERIAL_CONSOLE #include +/* SCC Console configuration. Not quite finished. The SCC_CONSOLE + * should be the number of the SCC to use, but only SCC1 will + * work at this time. + */ +#ifdef CONFIG_SCC_CONSOLE +#define SCC_CONSOLE 1 +#endif + /* this defines the index into rs_table for the port to use */ #ifndef CONFIG_SERIAL_CONSOLE_PORT #define CONFIG_SERIAL_CONSOLE_PORT 0 #endif #endif +#define CONFIG_SERIAL_CONSOLE_PORT 0 #define TX_WAKEUP ASYNC_SHARE_IRQ @@ -105,6 +114,18 @@ */ #define smc_scc_num hub6 +/* The choice of serial port to use for KGDB. If the system has + * two ports, you can use one for console and one for KGDB (which + * doesn't make sense to me, but people asked for it). + */ +#ifdef CONFIG_KGDB_TTYS1 +#define KGDB_SER_IDX 1 /* SCC2/SMC2 */ +#else +#define KGDB_SER_IDX 0 /* SCC1/SMC1 */ +#endif + +#ifndef SCC_CONSOLE + /* SMC2 is sometimes used for low performance TDM interfaces. Define * this as 1 if you want SMC2 as a serial port UART managed by this driver. * Define this as 0 if you wish to use SMC2 for something else. @@ -129,10 +150,26 @@ #if USE_SMC2 { 0, 0, PROFF_SMC2, SIU_INT_SMC2, 0, 1 }, /* SMC2 ttyS1 */ #endif - { 0, 0, PROFF_SCC2, SIU_INT_SCC2, 0, SCC_NUM_BASE}, /* SCC2 ttyS2 */ - { 0, 0, PROFF_SCC3, SIU_INT_SCC3, 0, SCC_NUM_BASE + 1}, /* SCC3 ttyS3 */ +#ifndef CONFIG_SCC1_ENET + { 0, 0, PROFF_SCC1, SIU_INT_SCC1, 0, SCC_NUM_BASE}, /* SCC1 ttyS2 */ +#endif +#ifndef CONFIG_SCC2_ENET + { 0, 0, PROFF_SCC2, SIU_INT_SCC2, 0, SCC_NUM_BASE + 1}, /* SCC2 ttyS3 */ +#endif }; +#else /* SCC_CONSOLE */ +#define SCC_NUM_BASE 0 /* SCC base tty "number" */ +#define SCC_IDX_BASE 0 /* table index */ +static struct serial_state rs_table[] = { + /* UART CLK PORT IRQ FLAGS NUM */ + { 0, 0, PROFF_SCC1, SIU_INT_SCC1, 0, SCC_NUM_BASE}, /* SCC1 ttyS2 */ + { 0, 0, PROFF_SCC2, SIU_INT_SCC2, 0, SCC_NUM_BASE + 1}, /* SCC2 ttyS3 */ +}; +#endif /* SCC_CONSOLE */ + +#define PORT_NUM(P) (((P) < (SCC_NUM_BASE)) ? (P) : (P)-(SCC_NUM_BASE)) + #define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) static struct tty_struct *serial_table[NR_PORTS]; @@ -349,6 +386,13 @@ i = bdp->cbd_datlen; cp = (unsigned char *)__va(bdp->cbd_bufaddr); status = bdp->cbd_sc; +#ifdef CONFIG_KGDB + if (info->state->smc_scc_num == KGDB_SER_IDX) { + if (*cp == 0x03 || *cp == '$') + breakpoint(); + return; + } +#endif /* Check to see if there is room in the tty buffer for * the characters in our BD buffer. If not, we exit @@ -775,7 +819,10 @@ else { sccp = &immr->im_scc[idx - SCC_IDX_BASE]; sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); - sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); +#ifdef CONFIG_SERIAL_CONSOLE + if (idx != CONFIG_SERIAL_CONSOLE_PORT) + sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); +#endif } if (info->tty) @@ -1736,7 +1783,7 @@ schedule_timeout(char_time); if (signal_pending(current)) break; - if (timeout && ((orig_jiffies + timeout) < jiffies)) + if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; bdp = info->tx_cur; } while (bdp->cbd_sc & BD_SC_READY); @@ -2006,7 +2053,7 @@ ret = sprintf(buf, "%d: uart:%s port:%X irq:%d", state->line, (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC", - state->port, state->irq); + (unsigned int)(state->port), state->irq); if (!state->port || (state->type == PORT_UNKNOWN)) { ret += sprintf(buf+ret, "\n"); @@ -2129,8 +2176,11 @@ /* * Print a string to the serial port trying not to disturb any possible * real use of the port... + * These funcitons work equally well for SCC, even though they are + * designed for SMC. Our only interests are the transmit/receive + * buffers, which are identically mapped for either the SCC or SMC. */ -static void serial_console_write(struct console *c, const char *s, +static void my_console_write(int idx, const char *s, unsigned count) { struct serial_state *ser; @@ -2140,7 +2190,7 @@ volatile smc_uart_t *up; volatile u_char *cp; - ser = rs_table + c->index; + ser = rs_table + idx; /* If the port has been initialized for general use, we have * to use the buffer descriptors allocated there. Otherwise, @@ -2177,8 +2227,15 @@ * that, not that it is ready for us to send. */ while (bdp->cbd_sc & BD_SC_READY); - /* Send the character out. */ - cp = __va(bdp->cbd_bufaddr); + + /* Send the character out. + * If the buffer address is in the CPM DPRAM, don't + * convert it. + */ + if ((uint)(bdp->cbd_bufaddr) > (uint)IMAP_ADDR) + cp = (u_char *)(bdp->cbd_bufaddr); + else + cp = __va(bdp->cbd_bufaddr); *cp = *s; bdp->cbd_datlen = 1; @@ -2216,9 +2273,196 @@ info->tx_cur = (cbd_t *)bdp; } +static void serial_console_write(struct console *c, const char *s, + unsigned count) +{ +#if defined(CONFIG_KGDB) && !defined(CONFIG_USE_SERIAL2_KGDB) + /* Try to let stub handle output. Returns true if it did. */ + if (kgdb_output_string(s, count)) + return; +#endif + my_console_write(c->index, s, count); +} + +#ifdef CONFIG_XMON +int +xmon_8xx_write(const char *s, unsigned count) +{ + my_console_write(KGDB_SER_IDX, s, count); + return(count); +} +#endif + +#ifdef CONFIG_KGDB +void +putDebugChar(char ch) +{ + my_console_write(KGDB_SER_IDX, &ch, 1); +} +#endif + +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) +/* + * Receive character from the serial port. This only works well + * before the port is initialize for real use. + */ +static int my_console_wait_key(int idx, int xmon, char *obuf) +{ + struct serial_state *ser; + u_char c, *cp; + ser_info_t *info; + volatile cbd_t *bdp; + volatile smc_uart_t *up; + int i; + + ser = rs_table + idx; + + /* Pointer to UART in parameter ram. + */ + up = (smc_uart_t *)&immr->im_dprambase[ser->port]; + + /* Get the address of the host memory buffer. + * If the port has been initialized for general use, we must + * use information from the port structure. + */ + if ((info = (ser_info_t *)ser->info)) + bdp = info->rx_cur; + else + bdp = (cbd_t *)&immr->im_dprambase[up->smc_rbase]; + + /* + * We need to gracefully shut down the receiver, disable + * interrupts, then read the input. + * XMON just wants a poll. If no character, return -1, else + * return the character. + */ + if (!xmon) { + while (bdp->cbd_sc & BD_SC_EMPTY); + } + else { + if (bdp->cbd_sc & BD_SC_EMPTY) + return -1; + } + + /* If the buffer address is in the CPM DPRAM, don't + * convert it. + */ + if ((uint)(bdp->cbd_bufaddr) > (uint)IMAP_ADDR) + cp = (u_char *)(bdp->cbd_bufaddr); + else + cp = __va(bdp->cbd_bufaddr); + + if (obuf) { + i = c = bdp->cbd_datlen; + while (i-- > 0) + *obuf++ = *cp++; + } + else { + c = *cp; + } + bdp->cbd_sc |= BD_SC_EMPTY; + + if (info) { + if (bdp->cbd_sc & BD_SC_WRAP) { + bdp = info->rx_bd_base; + } + else { + bdp++; + } + info->rx_cur = (cbd_t *)bdp; + } + + return((int)c); +} +#endif /* CONFIG_XMON || CONFIG_KGDB */ + +#ifdef CONFIG_XMON +int +xmon_8xx_read_poll(void) +{ + return(my_console_wait_key(KGDB_SER_IDX, 1, NULL)); +} + +int +xmon_8xx_read_char(void) +{ + return(my_console_wait_key(KGDB_SER_IDX, 0, NULL)); +} +#endif + +#ifdef CONFIG_KGDB +static char kgdb_buf[RX_BUF_SIZE], *kgdp; +static int kgdb_chars; + +char +getDebugChar(void) +{ + if (kgdb_chars <= 0) { + kgdb_chars = my_console_wait_key(KGDB_SER_IDX, 0, kgdb_buf); + kgdp = kgdb_buf; + } + kgdb_chars--; + + return(*kgdp++); +} + +void kgdb_interruptible(int yes) +{ + volatile smc_t *smcp; + + smcp = &immr->im_smc[KGDB_SER_IDX]; + + if (yes == 1) + smcp->smc_smcm |= SMCM_RX; + else + smcp->smc_smcm &= ~SMCM_RX; +} + +void kgdb_map_scc(void) +{ + ushort serbase; + uint mem_addr; + volatile cbd_t *bdp; + volatile smc_uart_t *up; + + /* The serial port has already been initialized before + * we get here. We have to assign some pointers needed by + * the kernel, and grab a memory location in the CPM that will + * work until the driver is really initialized. + */ + immr = (immap_t *)IMAP_ADDR; + + /* Right now, assume we are using SMCs. + */ +#ifdef USE_KGDB_SMC2 + *(ushort *)(&immr->im_dprambase[PROFF_SMC2_BASE]) = serbase = PROFF_SMC2; +#else + *(ushort *)(&immr->im_dprambase[PROFF_SMC1_BASE]) = serbase = PROFF_SMC1; +#endif + up = (smc_uart_t *)&immr->im_dprambase[serbase]; + + /* Allocate space for an input FIFO, plus a few bytes for output. + * Allocate bytes to maintain word alignment. + */ + mem_addr = (uint)(&immr->im_dprambase[0x1000]); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + bdp = (cbd_t *)&immr->im_dprambase[up->smc_rbase]; + bdp->cbd_bufaddr = mem_addr; + + bdp = (cbd_t *)&immr->im_dprambase[up->smc_tbase]; + bdp->cbd_bufaddr = mem_addr+RX_BUF_SIZE; + + up->smc_mrblr = RX_BUF_SIZE; /* receive buffer length */ + up->smc_maxidl = RX_BUF_SIZE; +} +#endif + static kdev_t serial_console_device(struct console *c) { - return MKDEV(TTYAUX_MAJOR, 64 + c->index); + return MKDEV(TTY_MAJOR, 64 + c->index); } @@ -2276,7 +2520,11 @@ __clear_user(&serial_driver,sizeof(struct tty_driver)); serial_driver.magic = TTY_DRIVER_MAGIC; serial_driver.driver_name = "serial"; +#ifdef CONFIG_DEVFS_FS + serial_driver.name = "tts/%d"; +#else serial_driver.name = "ttyS"; +#endif serial_driver.major = TTY_MAJOR; serial_driver.minor_start = 64; serial_driver.num = NR_PORTS; @@ -2314,7 +2562,11 @@ * major number and the subtype code. */ callout_driver = serial_driver; +#ifdef CONFIG_DEVFS_FS + callout_driver.name = "cua/%d"; +#else callout_driver.name = "cua"; +#endif callout_driver.major = TTYAUX_MAJOR; callout_driver.subtype = SERIAL_TYPE_CALLOUT; callout_driver.read_proc = 0; @@ -2340,6 +2592,7 @@ * Configure SMCs Tx/Rx. SMC1 is only on Port D, SMC2 is * only on Port A. You either pick 'em, or not. */ +#ifndef SCC_CONSOLE io->iop_ppard |= 0x00c00000; io->iop_pdird |= 0x00400000; io->iop_pdird &= ~0x00800000; @@ -2375,6 +2628,27 @@ */ immap->im_cpmux.cmx_scr &= ~0x00ffff00; immap->im_cpmux.cmx_scr |= 0x00121b00; +#else + io->iop_pparb |= 0x008b0000; + io->iop_pdirb |= 0x00880000; + io->iop_psorb |= 0x00880000; + io->iop_pdirb &= ~0x00030000; + io->iop_psorb &= ~0x00030000; + + /* Use Port D for SCC1 instead of other functions. + */ + io->iop_ppard |= 0x00000003; + io->iop_psord &= ~0x00000001; /* Rx */ + io->iop_psord |= 0x00000002; /* Tx */ + io->iop_pdird &= ~0x00000001; /* Rx */ + io->iop_pdird |= 0x00000002; /* Tx */ + + /* Connect SCC1, SCC2, SCC3 to NMSI. Connect BRG1 to SCC1, + * BRG2 to SCC2, BRG3 to SCC3. + */ + immap->im_cpmux.cmx_scr &= ~0xffffff00; + immap->im_cpmux.cmx_scr |= 0x00091200; +#endif for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { state->magic = SSTATE_MAGIC; @@ -2390,9 +2664,12 @@ state->icount.rx = state->icount.tx = 0; state->icount.frame = state->icount.parity = 0; state->icount.overrun = state->icount.brk = 0; - printk(KERN_INFO "ttyS%02d at 0x%04x is a %s\n", - i, state->port, - (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC"); + printk (KERN_INFO "ttyS%d on %s%d at 0x%04x, BRG%d\n", + i, + (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC", + PORT_NUM(state->smc_scc_num) + 1, + (unsigned int)(state->port), + state->smc_scc_num + 1); #ifdef CONFIG_SERIAL_CONSOLE /* If we just printed the message on the console port, and * we are about to initialize it for general use, we have @@ -2400,7 +2677,7 @@ * make it out of the transmit buffer. */ if (i == CONFIG_SERIAL_CONSOLE_PORT) - mdelay(2); + mdelay(300); #endif info = kmalloc(sizeof(ser_info_t), GFP_KERNEL); if (info) { @@ -2452,6 +2729,7 @@ else { scp = &immap->im_scc[idx - SCC_IDX_BASE]; sup = (scc_uart_t *)&immap->im_dprambase[state->port]; + scp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); sup->scc_genscc.scc_rbase = dp_addr; } @@ -2555,6 +2833,22 @@ /* Send the CPM an initialize command. */ +#ifdef SCC_CONSOLE + switch (state->smc_scc_num) { + case 0: + page = CPM_CR_SCC1_PAGE; + sblock = CPM_CR_SCC1_SBLOCK; + break; + case 1: + page = CPM_CR_SCC2_PAGE; + sblock = CPM_CR_SCC2_SBLOCK; + break; + case 2: + page = CPM_CR_SCC3_PAGE; + sblock = CPM_CR_SCC3_SBLOCK; + break; + } +#else if (state->smc_scc_num == 2) { page = CPM_CR_SCC2_PAGE; sblock = CPM_CR_SCC2_SBLOCK; @@ -2563,6 +2857,7 @@ page = CPM_CR_SCC3_PAGE; sblock = CPM_CR_SCC3_SBLOCK; } +#endif cp->cp_cpcr = mk_cr_cmd(page, sblock, 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; @@ -2596,8 +2891,12 @@ /* If the port is the console, enable Rx and Tx. */ #ifdef CONFIG_SERIAL_CONSOLE - if (i == CONFIG_SERIAL_CONSOLE_PORT) - sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + if (i == CONFIG_SERIAL_CONSOLE_PORT) { + if (idx < SCC_NUM_BASE) + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + else + scp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + } #endif } } @@ -2614,8 +2913,12 @@ volatile cbd_t *bdp; volatile cpm8260_t *cp; volatile immap_t *immap; +#ifndef SCC_CONSOLE volatile smc_t *sp; volatile smc_uart_t *up; +#endif + volatile scc_t *scp; + volatile scc_uart_t *sup; volatile iop8260_t *io; bd_t *bd; @@ -2630,11 +2933,25 @@ ser = rs_table + co->index; - immap = immr; cp = &immap->im_cpm; io = &immap->im_ioport; +#ifdef SCC_CONSOLE + scp = (scc_t *)&(immap->im_scc[SCC_CONSOLE-1]); + sup = (scc_uart_t *)&immap->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; + scp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); + scp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* Use Port D for SCC1 instead of other functions. + */ + io->iop_ppard |= 0x00000003; + io->iop_psord &= ~0x00000001; /* Rx */ + io->iop_psord |= 0x00000002; /* Tx */ + io->iop_pdird &= ~0x00000001; /* Rx */ + io->iop_pdird |= 0x00000002; /* Tx */ + +#else /* This should have been done long ago by the early boot code, * but do it again to make sure. */ @@ -2662,6 +2979,7 @@ io->iop_pdird |= 0x00400000; io->iop_pdird &= ~0x00800000; io->iop_psord &= ~0x00c00000; +#endif /* Allocate space for two buffer descriptors in the DP ram. */ @@ -2686,6 +3004,68 @@ /* Set up the uart parameters in the parameter ram. */ +#ifdef SCC_CONSOLE + sup->scc_genscc.scc_rbase = dp_addr; + sup->scc_genscc.scc_tbase = dp_addr + sizeof(cbd_t); + + /* Set up the uart parameters in the + * parameter ram. + */ + sup->scc_genscc.scc_rfcr = CPMFCR_GBL | CPMFCR_EB; + sup->scc_genscc.scc_tfcr = CPMFCR_GBL | CPMFCR_EB; + + sup->scc_genscc.scc_mrblr = 1; + sup->scc_maxidl = 0; + sup->scc_brkcr = 1; + sup->scc_parec = 0; + sup->scc_frmec = 0; + sup->scc_nosec = 0; + sup->scc_brkec = 0; + sup->scc_uaddr1 = 0; + sup->scc_uaddr2 = 0; + sup->scc_toseq = 0; + sup->scc_char1 = 0x8000; + sup->scc_char2 = 0x8000; + sup->scc_char3 = 0x8000; + sup->scc_char4 = 0x8000; + sup->scc_char5 = 0x8000; + sup->scc_char6 = 0x8000; + sup->scc_char7 = 0x8000; + sup->scc_char8 = 0x8000; + sup->scc_rccm = 0xc0ff; + + /* Send the CPM an initialize command. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SCC1_PAGE, CPM_CR_SCC1_SBLOCK, 0, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + scp->scc_gsmrh = 0; + scp->scc_gsmrl = + (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + + /* Disable all interrupts and clear all pending + * events. + */ + scp->scc_sccm = 0; + scp->scc_scce = 0xffff; + scp->scc_dsr = 0x7e7e; + scp->scc_pmsr = 0x3000; + + /* Wire BRG1 to SCC1. The serial init will take care of + * others. + */ + immap->im_cpmux.cmx_scr = 0; + + /* Set up the baud rate generator. + */ + m8260_cpm_setbrg(ser->smc_scc_num, bd->bi_baudrate); + + scp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); +#else up->smc_rbase = dp_addr; /* Base of receive buffer desc. */ up->smc_tbase = dp_addr+sizeof(cbd_t); /* Base of xmt buffer desc. */ up->smc_rfcr = CPMFCR_GBL | CPMFCR_EB; @@ -2714,6 +3094,7 @@ /* And finally, enable Rx and Tx. */ sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; +#endif return 0; } diff -Nru a/arch/ppc/8xx_io/Config.in b/arch/ppc/8xx_io/Config.in --- a/arch/ppc/8xx_io/Config.in Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/8xx_io/Config.in Tue Feb 19 18:08:58 2002 @@ -25,6 +25,10 @@ fi bool 'Enable SCC2 and SCC3 for UART' CONFIG_USE_SCC_IO +if [ "$CONFIG_SOUND" = "y" ]; then + bool 'Embedded Planet HIOX Audio' CONFIG_HTDMSOUND +fi + # This doesn't really belong here, but it is convenient to ask # 8xx specific questions. diff -Nru a/arch/ppc/8xx_io/Makefile b/arch/ppc/8xx_io/Makefile --- a/arch/ppc/8xx_io/Makefile Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/8xx_io/Makefile Tue Feb 19 18:08:59 2002 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.Makefile 1.6 08/30/01 09:33:48 trini +# BK Id: %F% %I% %G% %U% %#% # # # Makefile for the linux MPC8xx ppc-specific parts of comm processor @@ -16,5 +16,6 @@ obj-$(CONFIG_FEC_ENET) += fec.o obj-$(CONFIG_SCC_ENET) += enet.o obj-$(CONFIG_UCODE_PATCH) += micropatch.o +obj-$(CONFIG_HTDMSOUND) += cs4218_tdm.o include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc/8xx_io/commproc.c b/arch/ppc/8xx_io/commproc.c --- a/arch/ppc/8xx_io/commproc.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/8xx_io/commproc.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.commproc.c 1.15 10/16/01 16:21:52 trini + * BK Id: %F% %I% %G% %U% %#% */ /* @@ -55,7 +55,63 @@ static struct cpm_action cpm_vecs[CPMVEC_NR]; static void cpm_interrupt(int irq, void * dev, struct pt_regs * regs); static void cpm_error_interrupt(void *, struct pt_regs * regs); +static void alloc_host_memory(void); +#if 1 +void +m8xx_cpm_reset() +{ + volatile immap_t *imp; + volatile cpm8xx_t *commproc; + pte_t *pte; + + imp = (immap_t *)IMAP_ADDR; + commproc = (cpm8xx_t *)&imp->im_cpm; + +#ifdef CONFIG_UCODE_PATCH + /* Perform a reset. + */ + commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); + + /* Wait for it. + */ + while (commproc->cp_cpcr & CPM_CR_FLG); + + cpm_load_patch(imp); +#endif + + /* Set SDMA Bus Request priority 5. + * On 860T, this also enables FEC priority 6. I am not sure + * this is what we realy want for some applications, but the + * manual recommends it. + * Bit 25, FAM can also be set to use FEC aggressive mode (860T). + */ + imp->im_siu_conf.sc_sdcr = 1; + + /* Reclaim the DP memory for our use. + */ + dp_alloc_base = CPM_DATAONLY_BASE; + dp_alloc_top = dp_alloc_base + CPM_DATAONLY_SIZE; + + /* Tell everyone where the comm processor resides. + */ + cpmp = (cpm8xx_t *)commproc; +} + +/* We used to do this earlier, but have to postpone as long as possible + * to ensure the kernel VM is now running. + */ +static void +alloc_host_memory() +{ + uint physaddr; + + /* Set the host page for allocation. + */ + host_buffer = (uint)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &physaddr); + host_end = host_buffer + PAGE_SIZE; +} +#else void m8xx_cpm_reset(uint host_page_addr) { @@ -111,6 +167,7 @@ */ cpmp = (cpm8xx_t *)commproc; } +#endif /* This is called during init_IRQ. We used to do it above, but this * was too early since init_IRQ was not yet called. @@ -222,6 +279,12 @@ return(retloc); } +uint +m8xx_cpm_dpalloc_index(void) +{ + return dp_alloc_base; +} + /* We also own one page of host buffer space for the allocation of * UART "fifos" and the like. */ @@ -229,6 +292,11 @@ m8xx_cpm_hostalloc(uint size) { uint retloc; + +#if 1 + if (host_buffer == 0) + alloc_host_memory(); +#endif if ((host_buffer + size) >= host_end) return(0); diff -Nru a/arch/ppc/8xx_io/cs4218.h b/arch/ppc/8xx_io/cs4218.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/8xx_io/cs4218.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,167 @@ +#ifndef _cs4218_h_ +/* + * Hacked version of linux/drivers/sound/dmasound/dmasound.h + * + * + * Minor numbers for the sound driver. + * + * Unfortunately Creative called the codec chip of SB as a DSP. For this + * reason the /dev/dsp is reserved for digitized audio use. There is a + * device for true DSP processors but it will be called something else. + * In v3.0 it's /dev/sndproc but this could be a temporary solution. + */ +#define _cs4218_h_ + +#include +#include + +#define SND_NDEVS 256 /* Number of supported devices */ +#define SND_DEV_CTL 0 /* Control port /dev/mixer */ +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + synthesizer and MIDI output) */ +#define SND_DEV_MIDIN 2 /* Raw midi access */ +#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ +#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ +#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ +#define SND_DEV_STATUS 6 /* /dev/sndstat */ +/* #7 not in use now. Was in 2.4. Free for use after v3.0. */ +#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ +#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ +#define SND_DEV_PSS SND_DEV_SNDPROC + +/* switch on various prinks */ +#define DEBUG_DMASOUND 1 + +#define MAX_AUDIO_DEV 5 +#define MAX_MIXER_DEV 4 +#define MAX_SYNTH_DEV 3 +#define MAX_MIDI_DEV 6 +#define MAX_TIMER_DEV 3 + +#define MAX_CATCH_RADIUS 10 + +#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) +#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) + +#define IOCTL_IN(arg, ret) \ + do { int error = get_user(ret, (int *)(arg)); \ + if (error) return error; \ + } while (0) +#define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) + +static inline int ioctl_return(int *addr, int value) +{ + return value < 0 ? value : put_user(value, addr); +} + +#define HAS_RECORD + + /* + * Initialization + */ + +/* description of the set-up applies to either hard or soft settings */ + +typedef struct { + int format; /* AFMT_* */ + int stereo; /* 0 = mono, 1 = stereo */ + int size; /* 8/16 bit*/ + int speed; /* speed */ +} SETTINGS; + + /* + * Machine definitions + */ + +typedef struct { + const char *name; + const char *name2; + void (*open)(void); + void (*release)(void); + void *(*dma_alloc)(unsigned int, int); + void (*dma_free)(void *, unsigned int); + int (*irqinit)(void); +#ifdef MODULE + void (*irqcleanup)(void); +#endif + void (*init)(void); + void (*silence)(void); + int (*setFormat)(int); + int (*setVolume)(int); + int (*setBass)(int); + int (*setTreble)(int); + int (*setGain)(int); + void (*play)(void); + void (*record)(void); /* optional */ + void (*mixer_init)(void); /* optional */ + int (*mixer_ioctl)(u_int, u_long); /* optional */ + int (*write_sq_setup)(void); /* optional */ + int (*read_sq_setup)(void); /* optional */ + int (*sq_open)(mode_t); /* optional */ + int (*state_info)(char *, size_t); /* optional */ + void (*abort_read)(void); /* optional */ + int min_dsp_speed; + int max_dsp_speed; + int version ; + int hardware_afmts ; /* OSS says we only return h'ware info */ + /* when queried via SNDCTL_DSP_GETFMTS */ + int capabilities ; /* low-level reply to SNDCTL_DSP_GETCAPS */ + SETTINGS default_hard ; /* open() or init() should set something valid */ + SETTINGS default_soft ; /* you can make it look like old OSS, if you want to */ +} MACHINE; + + /* + * Low level stuff + */ + +typedef struct { + ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); +} TRANS; + + + /* + * Sound queue stuff, the heart of the driver + */ + +struct sound_queue { + /* buffers allocated for this queue */ + int numBufs; /* real limits on what the user can have */ + int bufSize; /* in bytes */ + char **buffers; + + /* current parameters */ + int locked ; /* params cannot be modified when != 0 */ + int user_frags ; /* user requests this many */ + int user_frag_size ; /* of this size */ + int max_count; /* actual # fragments <= numBufs */ + int block_size; /* internal block size in bytes */ + int max_active; /* in-use fragments <= max_count */ + + /* it shouldn't be necessary to declare any of these volatile */ + int front, rear, count; + int rear_size; + /* + * The use of the playing field depends on the hardware + * + * Atari, PMac: The number of frames that are loaded/playing + * + * Amiga: Bit 0 is set: a frame is loaded + * Bit 1 is set: a frame is playing + */ + int active; + wait_queue_head_t action_queue, open_queue, sync_queue; + int open_mode; + int busy, syncing, xruns, died; +}; + +#define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ) +#define WAKE_UP(queue) (wake_up_interruptible(&queue)) + +#endif /* _cs4218_h_ */ diff -Nru a/arch/ppc/8xx_io/cs4218_tdm.c b/arch/ppc/8xx_io/cs4218_tdm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/8xx_io/cs4218_tdm.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2849 @@ + +/* This is a modified version of linux/drivers/sound/dmasound.c to + * support the CS4218 codec on the 8xx TDM port. Thanks to everyone + * that contributed to the dmasound software (which includes me :-). + * + * The CS4218 is configured in Mode 4, sub-mode 0. This provides + * left/right data only on the TDM port, as a 32-bit word, per frame + * pulse. The control of the CS4218 is provided by some other means, + * like the SPI port. + * Dan Malek (dmalek@jlc.net) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Should probably do something different with this path name..... + * Actually, I should just stop using it... + */ +#include "cs4218.h" +#include + +#include +#include +#include + +#define DMASND_CS4218 5 + +#define MAX_CATCH_RADIUS 10 +#define MIN_BUFFERS 4 +#define MIN_BUFSIZE 4 +#define MAX_BUFSIZE 128 + +#define HAS_8BIT_TABLES + +static int sq_unit = -1; +static int mixer_unit = -1; +static int state_unit = -1; +static int irq_installed = 0; +static char **sound_buffers = NULL; +static char **sound_read_buffers = NULL; + +/* Local copies of things we put in the control register. Output + * volume, like most codecs is really attenuation. + */ +static int cs4218_rate_index; + +/* + * Stuff for outputting a beep. The values range from -327 to +327 + * so we can multiply by an amplitude in the range 0..100 to get a + * signed short value to put in the output buffer. + */ +static short beep_wform[256] = { + 0, 40, 79, 117, 153, 187, 218, 245, + 269, 288, 304, 316, 323, 327, 327, 324, + 318, 310, 299, 288, 275, 262, 249, 236, + 224, 213, 204, 196, 190, 186, 183, 182, + 182, 183, 186, 189, 192, 196, 200, 203, + 206, 208, 209, 209, 209, 207, 204, 201, + 197, 193, 188, 183, 179, 174, 170, 166, + 163, 161, 160, 159, 159, 160, 161, 162, + 164, 166, 168, 169, 171, 171, 171, 170, + 169, 167, 163, 159, 155, 150, 144, 139, + 133, 128, 122, 117, 113, 110, 107, 105, + 103, 103, 103, 103, 104, 104, 105, 105, + 105, 103, 101, 97, 92, 86, 78, 68, + 58, 45, 32, 18, 3, -11, -26, -41, + -55, -68, -79, -88, -95, -100, -102, -102, + -99, -93, -85, -75, -62, -48, -33, -16, + 0, 16, 33, 48, 62, 75, 85, 93, + 99, 102, 102, 100, 95, 88, 79, 68, + 55, 41, 26, 11, -3, -18, -32, -45, + -58, -68, -78, -86, -92, -97, -101, -103, + -105, -105, -105, -104, -104, -103, -103, -103, + -103, -105, -107, -110, -113, -117, -122, -128, + -133, -139, -144, -150, -155, -159, -163, -167, + -169, -170, -171, -171, -171, -169, -168, -166, + -164, -162, -161, -160, -159, -159, -160, -161, + -163, -166, -170, -174, -179, -183, -188, -193, + -197, -201, -204, -207, -209, -209, -209, -208, + -206, -203, -200, -196, -192, -189, -186, -183, + -182, -182, -183, -186, -190, -196, -204, -213, + -224, -236, -249, -262, -275, -288, -299, -310, + -318, -324, -327, -327, -323, -316, -304, -288, + -269, -245, -218, -187, -153, -117, -79, -40, +}; + +#define BEEP_SPEED 5 /* 22050 Hz sample rate */ +#define BEEP_BUFLEN 512 +#define BEEP_VOLUME 15 /* 0 - 100 */ + +static int beep_volume = BEEP_VOLUME; +static int beep_playing = 0; +static int beep_state = 0; +static short *beep_buf; +static void (*orig_mksound)(unsigned int, unsigned int); + +/* This is found someplace else......I guess in the keyboard driver + * we don't include. + */ +static void (*kd_mksound)(unsigned int, unsigned int); + +static int catchRadius = 0; +static int numBufs = 4, bufSize = 32; +static int numReadBufs = 4, readbufSize = 32; + + +/* TDM/Serial transmit and receive buffer descriptors. +*/ +static volatile cbd_t *rx_base, *rx_cur, *tx_base, *tx_cur; + +MODULE_PARM(catchRadius, "i"); +MODULE_PARM(numBufs, "i"); +MODULE_PARM(bufSize, "i"); +MODULE_PARM(numreadBufs, "i"); +MODULE_PARM(readbufSize, "i"); + +#define arraysize(x) (sizeof(x)/sizeof(*(x))) +#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) +#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) + +#define IOCTL_IN(arg, ret) \ + do { int error = get_user(ret, (int *)(arg)); \ + if (error) return error; \ + } while (0) +#define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) + +/* CS4218 serial port control in mode 4. +*/ +#define CS_INTMASK ((uint)0x40000000) +#define CS_DO1 ((uint)0x20000000) +#define CS_LATTEN ((uint)0x1f000000) +#define CS_RATTEN ((uint)0x00f80000) +#define CS_MUTE ((uint)0x00040000) +#define CS_ISL ((uint)0x00020000) +#define CS_ISR ((uint)0x00010000) +#define CS_LGAIN ((uint)0x0000f000) +#define CS_RGAIN ((uint)0x00000f00) + +#define CS_LATTEN_SET(X) (((X) & 0x1f) << 24) +#define CS_RATTEN_SET(X) (((X) & 0x1f) << 19) +#define CS_LGAIN_SET(X) (((X) & 0x0f) << 12) +#define CS_RGAIN_SET(X) (((X) & 0x0f) << 8) + +#define CS_LATTEN_GET(X) (((X) >> 24) & 0x1f) +#define CS_RATTEN_GET(X) (((X) >> 19) & 0x1f) +#define CS_LGAIN_GET(X) (((X) >> 12) & 0x0f) +#define CS_RGAIN_GET(X) (((X) >> 8) & 0x0f) + +/* The control register is effectively write only. We have to keep a copy + * of what we write. + */ +static uint cs4218_control; + +/* A place to store expanding information. +*/ +static int expand_bal; +static int expand_data; + +/* Since I can't make the microcode patch work for the SPI, I just + * clock the bits using software. + */ +static void sw_spi_init(void); +static void sw_spi_io(u_char *obuf, u_char *ibuf, uint bcnt); +static uint cs4218_ctl_write(uint ctlreg); + +/*** Some low level helpers **************************************************/ + +/* 16 bit mu-law */ + +static short ulaw2dma16[] = { + -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, + -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, + -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, + -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0, +}; + +/* 16 bit A-law */ + +static short alaw2dma16[] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, + -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, + -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, + -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848, +}; + + +/*** Translations ************************************************************/ + + +static ssize_t cs4218_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_s16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_u16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); + + +/*** Low level stuff *********************************************************/ + +struct cs_sound_settings { + MACHINE mach; /* machine dependent things */ + SETTINGS hard; /* hardware settings */ + SETTINGS soft; /* software settings */ + SETTINGS dsp; /* /dev/dsp default settings */ + TRANS *trans_write; /* supported translations for playback */ + TRANS *trans_read; /* supported translations for record */ + int volume_left; /* volume (range is machine dependent) */ + int volume_right; + int bass; /* tone (range is machine dependent) */ + int treble; + int gain; + int minDev; /* minor device number currently open */ +}; + +static struct cs_sound_settings sound; + +static void *CS_Alloc(unsigned int size, int flags); +static void CS_Free(void *ptr, unsigned int size); +static int CS_IrqInit(void); +#ifdef MODULE +static void CS_IrqCleanup(void); +#endif /* MODULE */ +static void CS_Silence(void); +static void CS_Init(void); +static void CS_Play(void); +static void CS_Record(void); +static int CS_SetFormat(int format); +static int CS_SetVolume(int volume); +static void cs4218_tdm_tx_intr(void *devid); +static void cs4218_tdm_rx_intr(void *devid); +static void cs4218_intr(void *devid, struct pt_regs *regs); +static int cs_get_volume(uint reg); +static int cs_volume_setter(int volume, int mute); +static int cs_get_gain(uint reg); +static int cs_set_gain(int gain); +static void cs_mksound(unsigned int hz, unsigned int ticks); +static void cs_nosound(unsigned long xx); + +/*** Mid level stuff *********************************************************/ + + +static void sound_silence(void); +static void sound_init(void); +static int sound_set_format(int format); +static int sound_set_speed(int speed); +static int sound_set_stereo(int stereo); +static int sound_set_volume(int volume); + +static ssize_t sound_copy_translate(const u_char *userPtr, + size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t sound_copy_translate_read(const u_char *userPtr, + size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); + + +/* + * /dev/mixer abstraction + */ + +struct sound_mixer { + int busy; + int modify_counter; +}; + +static struct sound_mixer mixer; + +static struct sound_queue sq; +static struct sound_queue read_sq; + +#define sq_block_address(i) (sq.buffers[i]) +#define SIGNAL_RECEIVED (signal_pending(current)) +#define NON_BLOCKING(open_mode) (open_mode & O_NONBLOCK) +#define ONE_SECOND HZ /* in jiffies (100ths of a second) */ +#define NO_TIME_LIMIT 0xffffffff + +/* + * /dev/sndstat + */ + +struct sound_state { + int busy; + char buf[512]; + int len, ptr; +}; + +static struct sound_state state; + +/*** Common stuff ********************************************************/ + +static long long sound_lseek(struct file *file, long long offset, int orig); + +/*** Config & Setup **********************************************************/ + +void dmasound_setup(char *str, int *ints); + +/*** Translations ************************************************************/ + + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + +static ssize_t cs4218_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + short *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16; + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t cs4218_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t cs4218_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +/* This is the default format of the codec. Signed, 16-bit stereo + * generated by an application shouldn't have to be copied at all. + * We should just get the phsical address of the buffers and update + * the TDM BDs directly. + */ +static ssize_t cs4218_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int stereo = sound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + if (get_user(data, up++)) + return -EFAULT; + *fp++ = data; + *fp++ = data; + count--; + } + } else { + if (copy_from_user(fp, userPtr, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static ssize_t cs4218_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = sound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + while (count > 0) { + int data; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + *fp++ = data; + if (stereo) { + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + } + *fp++ = data; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + + +static ssize_t cs4218_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned short *table = (unsigned short *) + (sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16); + unsigned int data = expand_data; + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + int bal = expand_bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int utotal, ftotal; + int stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + table[c]; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t cs4218_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + (c << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t cs4218_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = (c ^ 0x80) << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + ((c ^ 0x80) << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t cs4218_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + unsigned short *up = (unsigned short *) userPtr; + int bal = expand_bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + c; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + + +static ssize_t cs4218_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + unsigned short *up = (unsigned short *) userPtr; + int bal = expand_bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + (c ^ mask); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + +static ssize_t cs4218_ct_s8_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + + val = *p++; + data = val >> 8; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + if (stereo) { + val = *p; + data = val >> 8; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + } + p++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t cs4218_ct_u8_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + + val = *p++; + data = (val >> 8) ^ 0x80; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + if (stereo) { + val = *p; + data = (val >> 8) ^ 0x80; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + } + p++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t cs4218_ct_s16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int stereo = sound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + data = *fp; + if (put_user(data, up++)) + return -EFAULT; + fp+=2; + count--; + } + } else { + if (copy_to_user((u_char *)userPtr, fp, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static ssize_t cs4218_ct_u16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = sound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + while (count > 0) { + int data; + + data = *fp++; + data ^= mask; + if (put_user(data, up++)) + return -EFAULT; + if (stereo) { + data = *fp; + data ^= mask; + if (put_user(data, up++)) + return -EFAULT; + } + fp++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static TRANS transCSNormal = { + cs4218_ct_law, cs4218_ct_law, cs4218_ct_s8, cs4218_ct_u8, + cs4218_ct_s16, cs4218_ct_u16, cs4218_ct_s16, cs4218_ct_u16 +}; + +static TRANS transCSExpand = { + cs4218_ctx_law, cs4218_ctx_law, cs4218_ctx_s8, cs4218_ctx_u8, + cs4218_ctx_s16, cs4218_ctx_u16, cs4218_ctx_s16, cs4218_ctx_u16 +}; + +static TRANS transCSNormalRead = { + NULL, NULL, cs4218_ct_s8_read, cs4218_ct_u8_read, + cs4218_ct_s16_read, cs4218_ct_u16_read, + cs4218_ct_s16_read, cs4218_ct_u16_read +}; + +/*** Low level stuff *********************************************************/ + +static void *CS_Alloc(unsigned int size, int flags) +{ + int order; + + size >>= 13; + for (order=0; order < 5; order++) { + if (size == 0) + break; + size >>= 1; + } + return (void *)__get_free_pages(flags, order); +} + +static void CS_Free(void *ptr, unsigned int size) +{ + int order; + + size >>= 13; + for (order=0; order < 5; order++) { + if (size == 0) + break; + size >>= 1; + } + free_pages((ulong)ptr, order); +} + +static int __init CS_IrqInit(void) +{ + cpm_install_handler(CPMVEC_SMC2, cs4218_intr, NULL); + return 1; +} + +#ifdef MODULE +static void CS_IrqCleanup(void) +{ + volatile smc_t *sp; + volatile cpm8xx_t *cp; + + /* First disable transmitter and receiver. + */ + sp = &cpmp->cp_smc[1]; + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* And now shut down the SMC. + */ + cp = cpmp; /* Get pointer to Communication Processor */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, + CPM_CR_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Release the interrupt handler. + */ + cpm_free_handler(CPMVEC_SMC2); + + if (beep_buf) + kfree(beep_buf); + kd_mksound = orig_mksound; +} +#endif /* MODULE */ + +static void CS_Silence(void) +{ + volatile smc_t *sp; + + /* Disable transmitter. + */ + sp = &cpmp->cp_smc[1]; + sp->smc_smcmr &= ~SMCMR_TEN; +} + +/* Frequencies depend upon external oscillator. There are two + * choices, 12.288 and 11.2896 MHz. The RPCG audio supports both through + * and external control register selection bit. + */ +static int cs4218_freqs[] = { + /* 12.288 11.2896 */ + 48000, 44100, + 32000, 29400, + 24000, 22050, + 19200, 17640, + 16000, 14700, + 12000, 11025, + 9600, 8820, + 8000, 7350 +}; + +static void CS_Init(void) +{ + int i, tolerance; + + switch (sound.soft.format) { + case AFMT_S16_LE: + case AFMT_U16_LE: + sound.hard.format = AFMT_S16_LE; + break; + default: + sound.hard.format = AFMT_S16_BE; + break; + } + sound.hard.stereo = 1; + sound.hard.size = 16; + + /* + * If we have a sample rate which is within catchRadius percent + * of the requested value, we don't have to expand the samples. + * Otherwise choose the next higher rate. + */ + i = (sizeof(cs4218_freqs) / sizeof(int)); + do { + tolerance = catchRadius * cs4218_freqs[--i] / 100; + } while (sound.soft.speed > cs4218_freqs[i] + tolerance && i > 0); + if (sound.soft.speed >= cs4218_freqs[i] - tolerance) + sound.trans_write = &transCSNormal; + else + sound.trans_write = &transCSExpand; + sound.trans_read = &transCSNormalRead; + sound.hard.speed = cs4218_freqs[i]; + cs4218_rate_index = i; + + /* The CS4218 has seven selectable clock dividers for the sample + * clock. The HIOX then provides one of two external rates. + * An even numbered frequency table index uses the high external + * clock rate. + */ + *(uint *)HIOX_CSR4_ADDR &= ~(HIOX_CSR4_AUDCLKHI | HIOX_CSR4_AUDCLKSEL); + if ((i & 1) == 0) + *(uint *)HIOX_CSR4_ADDR |= HIOX_CSR4_AUDCLKHI; + i >>= 1; + *(uint *)HIOX_CSR4_ADDR |= (i & HIOX_CSR4_AUDCLKSEL); + + expand_bal = -sound.soft.speed; +} + +static int CS_SetFormat(int format) +{ + int size; + + switch (format) { + case AFMT_QUERY: + return sound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", + format); + size = 8; + format = AFMT_U8; + } + + sound.soft.format = format; + sound.soft.size = size; + if (sound.minDev == SND_DEV_DSP) { + sound.dsp.format = format; + sound.dsp.size = size; + } + + CS_Init(); + + return format; +} + +/* Volume is the amount of attenuation we tell the codec to impose + * on the outputs. There are 32 levels, with 0 the "loudest". + */ +#define CS_VOLUME_TO_MASK(x) (31 - ((((x) - 1) * 31) / 99)) +#define CS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 31)) + +static int cs_get_volume(uint reg) +{ + int volume; + + volume = CS_MASK_TO_VOLUME(CS_LATTEN_GET(reg)); + volume |= CS_MASK_TO_VOLUME(CS_RATTEN_GET(reg)) << 8; + return volume; +} + +static int cs_volume_setter(int volume, int mute) +{ + uint tempctl; + + if (mute && volume == 0) { + tempctl = cs4218_control | CS_MUTE; + } else { + tempctl = cs4218_control & ~CS_MUTE; + tempctl = tempctl & ~(CS_LATTEN | CS_RATTEN); + tempctl |= CS_LATTEN_SET(CS_VOLUME_TO_MASK(volume & 0xff)); + tempctl |= CS_RATTEN_SET(CS_VOLUME_TO_MASK((volume >> 8) & 0xff)); + volume = cs_get_volume(tempctl); + } + if (tempctl != cs4218_control) { + cs4218_ctl_write(tempctl); + } + return volume; +} + + +/* Gain has 16 steps from 0 to 15. These are in 1.5dB increments from + * 0 (no gain) to 22.5 dB. + */ +#define CS_RECLEVEL_TO_GAIN(v) \ + ((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20) +#define CS_GAIN_TO_RECLEVEL(v) (((v) * 20 + 2) / 3) + +static int cs_get_gain(uint reg) +{ + int gain; + + gain = CS_GAIN_TO_RECLEVEL(CS_LGAIN_GET(reg)); + gain |= CS_GAIN_TO_RECLEVEL(CS_RGAIN_GET(reg)) << 8; + return gain; +} + +static int cs_set_gain(int gain) +{ + uint tempctl; + + tempctl = cs4218_control & ~(CS_LGAIN | CS_RGAIN); + tempctl |= CS_LGAIN_SET(CS_RECLEVEL_TO_GAIN(gain & 0xff)); + tempctl |= CS_RGAIN_SET(CS_RECLEVEL_TO_GAIN((gain >> 8) & 0xff)); + gain = cs_get_gain(tempctl); + + if (tempctl != cs4218_control) { + cs4218_ctl_write(tempctl); + } + return gain; +} + +static int CS_SetVolume(int volume) +{ + return cs_volume_setter(volume, CS_MUTE); +} + +static void CS_Play(void) +{ + int i, count; + unsigned long flags; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + + save_flags(flags); cli(); +#if 0 + if (awacs_beep_state) { + /* sound takes precedence over beeps */ + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (awacs_rate_index << 8)); + out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(sq.front+sq.active) % sq.max_count]))); + + beep_playing = 0; + awacs_beep_state = 0; + } +#endif + i = sq.front + sq.active; + if (i >= sq.max_count) + i -= sq.max_count; + while (sq.active < 2 && sq.active < sq.count) { + count = (sq.count == sq.active + 1)?sq.rear_size:sq.block_size; + if (count < sq.block_size && !sq.syncing) + /* last block not yet filled, and we're not syncing. */ + break; + + bdp = &tx_base[i]; + bdp->cbd_datlen = count; + + flush_dcache_range((ulong)sound_buffers[i], + (ulong)(sound_buffers[i] + count)); + + if (++i >= sq.max_count) + i = 0; + + if (sq.active == 0) { + /* The SMC does not load its fifo until the first + * TDM frame pulse, so the transmit data gets shifted + * by one word. To compensate for this, we incorrectly + * transmit the first buffer and shorten it by one + * word. Subsequent buffers are then aligned properly. + */ + bdp->cbd_datlen -= 2; + + /* Start up the SMC Transmitter. + */ + cp = cpmp; + cp->cp_smc[1].smc_smcmr |= SMCMR_TEN; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, + CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } + + /* Buffer is ready now. + */ + bdp->cbd_sc |= BD_SC_READY; + + ++sq.active; + } + restore_flags(flags); +} + + +static void CS_Record(void) +{ + unsigned long flags; + volatile smc_t *sp; + + if (read_sq.active) + return; + + save_flags(flags); cli(); + + /* This is all we have to do......Just start it up. + */ + sp = &cpmp->cp_smc[1]; + sp->smc_smcmr |= SMCMR_REN; + + read_sq.active = 1; + + restore_flags(flags); +} + + +static void +cs4218_tdm_tx_intr(void *devid) +{ + int i = sq.front; + volatile cbd_t *bdp; + + while (sq.active > 0) { + bdp = &tx_base[i]; + if (bdp->cbd_sc & BD_SC_READY) + break; /* this frame is still going */ + --sq.count; + --sq.active; + if (++i >= sq.max_count) + i = 0; + } + if (i != sq.front) + WAKE_UP(sq.action_queue); + sq.front = i; + + CS_Play(); + + if (!sq.active) + WAKE_UP(sq.sync_queue); +} + + +static void +cs4218_tdm_rx_intr(void *devid) +{ + + /* We want to blow 'em off when shutting down. + */ + if (read_sq.active == 0) + return; + + /* Check multiple buffers in case we were held off from + * interrupt processing for a long time. Geeze, I really hope + * this doesn't happen. + */ + while ((rx_base[read_sq.rear].cbd_sc & BD_SC_EMPTY) == 0) { + + /* Invalidate the data cache range for this buffer. + */ + invalidate_dcache_range( + (uint)(sound_read_buffers[read_sq.rear]), + (uint)(sound_read_buffers[read_sq.rear] + read_sq.block_size)); + + /* Make buffer available again and move on. + */ + rx_base[read_sq.rear].cbd_sc |= BD_SC_EMPTY; + read_sq.rear++; + + /* Wrap the buffer ring. + */ + if (read_sq.rear >= read_sq.max_active) + read_sq.rear = 0; + + /* If we have caught up to the front buffer, bump it. + * This will cause weird (but not fatal) results if the + * read loop is currently using this buffer. The user is + * behind in this case anyway, so weird things are going + * to happen. + */ + if (read_sq.rear == read_sq.front) { + read_sq.front++; + if (read_sq.front >= read_sq.max_active) + read_sq.front = 0; + } + } + + WAKE_UP(read_sq.action_queue); +} + +static void cs_nosound(unsigned long xx) +{ + unsigned long flags; + + save_flags(flags); cli(); + if (beep_playing) { +#if 0 + st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); +#endif + beep_playing = 0; + } + restore_flags(flags); +} + +static struct timer_list beep_timer = { + function: cs_nosound +}; + +static void cs_mksound(unsigned int hz, unsigned int ticks) +{ + unsigned long flags; + int beep_speed = BEEP_SPEED; + int srate = cs4218_freqs[beep_speed]; + int period, ncycles, nsamples; + int i, j, f; + short *p; + static int beep_hz_cache; + static int beep_nsamples_cache; + static int beep_volume_cache; + + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { +#if 1 + /* this is a hack for broken X server code */ + hz = 750; + ticks = 12; +#else + /* cancel beep currently playing */ + awacs_nosound(0); + return; +#endif + } + save_flags(flags); cli(); + del_timer(&beep_timer); + if (ticks) { + beep_timer.expires = jiffies + ticks; + add_timer(&beep_timer); + } + if (beep_playing || sq.active || beep_buf == NULL) { + restore_flags(flags); + return; /* too hard, sorry :-( */ + } + beep_playing = 1; +#if 0 + st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); +#endif + restore_flags(flags); + + if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { + nsamples = beep_nsamples_cache; + } else { + period = srate * 256 / hz; /* fixed point */ + ncycles = BEEP_BUFLEN * 256 / period; + nsamples = (period * ncycles) >> 8; + f = ncycles * 65536 / nsamples; + j = 0; + p = beep_buf; + for (i = 0; i < nsamples; ++i, p += 2) { + p[0] = p[1] = beep_wform[j >> 8] * beep_volume; + j = (j + f) & 0xffff; + } + beep_hz_cache = hz; + beep_volume_cache = beep_volume; + beep_nsamples_cache = nsamples; + } + +#if 0 + st_le16(&beep_dbdma_cmd->req_count, nsamples*4); + st_le16(&beep_dbdma_cmd->xfer_status, 0); + st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); + st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); + awacs_beep_state = 1; + + save_flags(flags); cli(); + if (beep_playing) { /* i.e. haven't been terminated already */ + out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (beep_speed << 8)); + out_le32(&awacs->byteswap, 0); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); + out_le32(&awacs_txdma->control, RUN | (RUN << 16)); + } +#endif + restore_flags(flags); +} + +static void CS_open(void) +{ + MOD_INC_USE_COUNT; +} + +static void CS_release(void) +{ + MOD_DEC_USE_COUNT; +} + +static MACHINE mach_cs4218 = { + name: "HIOX CS4218", + name2: "Built-in Sound", + open: CS_open, + release: CS_release, + dma_alloc: CS_Alloc, + dma_free: CS_Free, + irqinit: CS_IrqInit, +#ifdef MODULE + irqcleanup: CS_IrqCleanup, +#endif /* MODULE */ + init: CS_Init, + silence: CS_Silence, + setFormat: CS_SetFormat, + setVolume: CS_SetVolume, + play: CS_Play +}; + + +/*** Mid level stuff *********************************************************/ + + +static void sound_silence(void) +{ + /* update hardware settings one more */ + (*sound.mach.init)(); + + (*sound.mach.silence)(); +} + + +static void sound_init(void) +{ + (*sound.mach.init)(); +} + + +static int sound_set_format(int format) +{ + return(*sound.mach.setFormat)(format); +} + + +static int sound_set_speed(int speed) +{ + if (speed < 0) + return(sound.soft.speed); + + sound.soft.speed = speed; + (*sound.mach.init)(); + if (sound.minDev == SND_DEV_DSP) + sound.dsp.speed = sound.soft.speed; + + return(sound.soft.speed); +} + + +static int sound_set_stereo(int stereo) +{ + if (stereo < 0) + return(sound.soft.stereo); + + stereo = !!stereo; /* should be 0 or 1 now */ + + sound.soft.stereo = stereo; + if (sound.minDev == SND_DEV_DSP) + sound.dsp.stereo = stereo; + (*sound.mach.init)(); + + return(stereo); +} + + +static int sound_set_volume(int volume) +{ + return(*sound.mach.setVolume)(volume); +} + +static ssize_t sound_copy_translate(const u_char *userPtr, + size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; + + switch (sound.soft.format) { + case AFMT_MU_LAW: + ct_func = sound.trans_write->ct_ulaw; + break; + case AFMT_A_LAW: + ct_func = sound.trans_write->ct_alaw; + break; + case AFMT_S8: + ct_func = sound.trans_write->ct_s8; + break; + case AFMT_U8: + ct_func = sound.trans_write->ct_u8; + break; + case AFMT_S16_BE: + ct_func = sound.trans_write->ct_s16be; + break; + case AFMT_U16_BE: + ct_func = sound.trans_write->ct_u16be; + break; + case AFMT_S16_LE: + ct_func = sound.trans_write->ct_s16le; + break; + case AFMT_U16_LE: + ct_func = sound.trans_write->ct_u16le; + break; + } + if (ct_func) + return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); + else + return 0; +} + +static ssize_t sound_copy_translate_read(const u_char *userPtr, + size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; + + switch (sound.soft.format) { + case AFMT_MU_LAW: + ct_func = sound.trans_read->ct_ulaw; + break; + case AFMT_A_LAW: + ct_func = sound.trans_read->ct_alaw; + break; + case AFMT_S8: + ct_func = sound.trans_read->ct_s8; + break; + case AFMT_U8: + ct_func = sound.trans_read->ct_u8; + break; + case AFMT_S16_BE: + ct_func = sound.trans_read->ct_s16be; + break; + case AFMT_U16_BE: + ct_func = sound.trans_read->ct_u16be; + break; + case AFMT_S16_LE: + ct_func = sound.trans_read->ct_s16le; + break; + case AFMT_U16_LE: + ct_func = sound.trans_read->ct_u16le; + break; + } + if (ct_func) + return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); + else + return 0; +} + + +/* + * /dev/mixer abstraction + */ + +static int mixer_open(struct inode *inode, struct file *file) +{ + MOD_INC_USE_COUNT; + mixer.busy = 1; + return 0; +} + + +static int mixer_release(struct inode *inode, struct file *file) +{ + mixer.busy = 0; + MOD_DEC_USE_COUNT; + return 0; +} + + +static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) +{ + int data; + uint tmpcs; + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + mixer.modify_counter++; + if (cmd == OSS_GETVERSION) + return IOCTL_OUT(arg, SOUND_VERSION); + switch (cmd) { + case SOUND_MIXER_INFO: { + mixer_info info; + strncpy(info.id, "CS4218_TDM", sizeof(info.id)); + strncpy(info.name, "CS4218_TDM", sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + info.modify_counter = mixer.modify_counter; + if (copy_to_user((int *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_RECLEV + | SOUND_MASK_ALTPCM; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + if (cs4218_control & CS_DO1) + data = SOUND_MASK_LINE; + else + data = SOUND_MASK_MIC; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE | SOUND_MASK_MIC); + if (data & SOUND_MASK_LINE) + tmpcs = cs4218_control | + (CS_ISL | CS_ISR | CS_DO1); + if (data & SOUND_MASK_MIC) + tmpcs = cs4218_control & + ~(CS_ISL | CS_ISR | CS_DO1); + if (tmpcs != cs4218_control) + cs4218_ctl_write(tmpcs); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_RECLEV; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_VOLUME: + data = (cs4218_control & CS_MUTE)? 0: + cs_get_volume(cs4218_control); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_volume(data)); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = cs_set_gain(data); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = cs_get_gain(cs4218_control); + return IOCTL_OUT(arg, data); + } + + return -EINVAL; +} + + +static struct file_operations mixer_fops = +{ + owner: THIS_MODULE, + llseek: sound_lseek, + ioctl: mixer_ioctl, + open: mixer_open, + release: mixer_release, +}; + + +static void __init mixer_init(void) +{ + mixer_unit = register_sound_mixer(&mixer_fops, -1); + if (mixer_unit < 0) + return; + + mixer.busy = 0; + sound.treble = 0; + sound.bass = 0; + + /* Set Line input, no gain, no attenuation. + */ + cs4218_control = CS_ISL | CS_ISR | CS_DO1; + cs4218_control |= CS_LGAIN_SET(0) | CS_RGAIN_SET(0); + cs4218_control |= CS_LATTEN_SET(0) | CS_RATTEN_SET(0); + cs4218_ctl_write(cs4218_control); +} + + +/* + * Sound queue stuff, the heart of the driver + */ + + +static int sq_allocate_buffers(void) +{ + int i; + + if (sound_buffers) + return 0; + sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL); + if (!sound_buffers) + return -ENOMEM; + for (i = 0; i < numBufs; i++) { + sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL); + if (!sound_buffers[i]) { + while (i--) + sound.mach.dma_free (sound_buffers[i], bufSize << 10); + kfree (sound_buffers); + sound_buffers = 0; + return -ENOMEM; + } + } + return 0; +} + + +static void sq_release_buffers(void) +{ + int i; + + if (sound_buffers) { + for (i = 0; i < numBufs; i++) + sound.mach.dma_free (sound_buffers[i], bufSize << 10); + kfree (sound_buffers); + sound_buffers = 0; + } +} + + +static int sq_allocate_read_buffers(void) +{ + int i; + + if (sound_read_buffers) + return 0; + sound_read_buffers = kmalloc(numReadBufs * sizeof(char *), GFP_KERNEL); + if (!sound_read_buffers) + return -ENOMEM; + for (i = 0; i < numBufs; i++) { + sound_read_buffers[i] = sound.mach.dma_alloc (readbufSize<<10, + GFP_KERNEL); + if (!sound_read_buffers[i]) { + while (i--) + sound.mach.dma_free (sound_read_buffers[i], + readbufSize << 10); + kfree (sound_read_buffers); + sound_read_buffers = 0; + return -ENOMEM; + } + } + return 0; +} + +static void sq_release_read_buffers(void) +{ + int i; + + if (sound_read_buffers) { + cpmp->cp_smc[1].smc_smcmr &= ~SMCMR_REN; + for (i = 0; i < numReadBufs; i++) + sound.mach.dma_free (sound_read_buffers[i], + bufSize << 10); + kfree (sound_read_buffers); + sound_read_buffers = 0; + } +} + + +static void sq_setup(int numBufs, int bufSize, char **write_buffers) +{ + int i; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile smc_t *sp; + + /* Make sure the SMC transmit is shut down. + */ + cp = cpmp; + sp = &cpmp->cp_smc[1]; + sp->smc_smcmr &= ~SMCMR_TEN; + + sq.max_count = numBufs; + sq.max_active = numBufs; + sq.block_size = bufSize; + sq.buffers = write_buffers; + + sq.front = sq.count = 0; + sq.rear = -1; + sq.syncing = 0; + sq.active = 0; + + bdp = tx_base; + for (i=0; icbd_bufaddr = virt_to_bus(write_buffers[i]); + bdp++; + } + + /* This causes the SMC to sync up with the first buffer again. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); +} + +static void read_sq_setup(int numBufs, int bufSize, char **read_buffers) +{ + int i; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile smc_t *sp; + + /* Make sure the SMC receive is shut down. + */ + cp = cpmp; + sp = &cpmp->cp_smc[1]; + sp->smc_smcmr &= ~SMCMR_REN; + + read_sq.max_count = numBufs; + read_sq.max_active = numBufs; + read_sq.block_size = bufSize; + read_sq.buffers = read_buffers; + + read_sq.front = read_sq.count = 0; + read_sq.rear = 0; + read_sq.rear_size = 0; + read_sq.syncing = 0; + read_sq.active = 0; + + bdp = rx_base; + for (i=0; icbd_bufaddr = virt_to_bus(read_buffers[i]); + bdp->cbd_datlen = read_sq.block_size; + bdp++; + } + + /* This causes the SMC to sync up with the first buffer again. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_RX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); +} + + +static void sq_play(void) +{ + (*sound.mach.play)(); +} + + +/* ++TeSche: radically changed this one too */ + +static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, + loff_t *ppos) +{ + ssize_t uWritten = 0; + u_char *dest; + ssize_t uUsed, bUsed, bLeft; + + /* ++TeSche: Is something like this necessary? + * Hey, that's an honest question! Or does any other part of the + * filesystem already checks this situation? I really don't know. + */ + if (uLeft == 0) + return 0; + + /* The interrupt doesn't start to play the last, incomplete frame. + * Thus we can append to it without disabling the interrupts! (Note + * also that sq.rear isn't affected by the interrupt.) + */ + + if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) { + dest = sq_block_address(sq.rear); + bUsed = sq.rear_size; + uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + sq.rear_size = bUsed; + } + + do { + while (sq.count == sq.max_active) { + sq_play(); + if (NON_BLOCKING(sq.open_mode)) + return uWritten > 0 ? uWritten : -EAGAIN; + SLEEP(sq.action_queue); + if (SIGNAL_RECEIVED) + return uWritten > 0 ? uWritten : -EINTR; + } + + /* Here, we can avoid disabling the interrupt by first + * copying and translating the data, and then updating + * the sq variables. Until this is done, the interrupt + * won't see the new frame and we can work on it + * undisturbed. + */ + + dest = sq_block_address((sq.rear+1) % sq.max_count); + bUsed = 0; + bLeft = sq.block_size; + uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); + if (uUsed <= 0) + break; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + if (bUsed) { + sq.rear = (sq.rear+1) % sq.max_count; + sq.rear_size = bUsed; + sq.count++; + } + } while (bUsed); /* uUsed may have been 0 */ + + sq_play(); + + return uUsed < 0? uUsed: uWritten; +} + + +/***********/ + +/* Here is how the values are used for reading. + * The value 'active' simply indicates the DMA is running. This is + * done so the driver semantics are DMA starts when the first read is + * posted. The value 'front' indicates the buffer we should next + * send to the user. The value 'rear' indicates the buffer the DMA is + * currently filling. When 'front' == 'rear' the buffer "ring" is + * empty (we always have an empty available). The 'rear_size' is used + * to track partial offsets into the current buffer. Right now, I just keep + * The DMA running. If the reader can't keep up, the interrupt tosses + * the oldest buffer. We could also shut down the DMA in this case. + */ +static ssize_t sq_read(struct file *file, char *dst, size_t uLeft, + loff_t *ppos) +{ + + ssize_t uRead, bLeft, bUsed, uUsed; + + if (uLeft == 0) + return 0; + + if (!read_sq.active) + CS_Record(); /* Kick off the record process. */ + + uRead = 0; + + /* Move what the user requests, depending upon other options. + */ + while (uLeft > 0) { + + /* When front == rear, the DMA is not done yet. + */ + while (read_sq.front == read_sq.rear) { + if (NON_BLOCKING(read_sq.open_mode)) { + return uRead > 0 ? uRead : -EAGAIN; + } + SLEEP(read_sq.action_queue); + if (SIGNAL_RECEIVED) + return uRead > 0 ? uRead : -EINTR; + } + + /* The amount we move is either what is left in the + * current buffer or what the user wants. + */ + bLeft = read_sq.block_size - read_sq.rear_size; + bUsed = read_sq.rear_size; + uUsed = sound_copy_translate_read(dst, uLeft, + read_sq.buffers[read_sq.front], &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + dst += uUsed; + uRead += uUsed; + uLeft -= uUsed; + read_sq.rear_size += bUsed; + if (read_sq.rear_size >= read_sq.block_size) { + read_sq.rear_size = 0; + read_sq.front++; + if (read_sq.front >= read_sq.max_active) + read_sq.front = 0; + } + } + return uRead; +} + +static int sq_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + MOD_INC_USE_COUNT; + if (file->f_mode & FMODE_WRITE) { + if (sq.busy) { + rc = -EBUSY; + if (NON_BLOCKING(file->f_flags)) + goto err_out; + rc = -EINTR; + while (sq.busy) { + SLEEP(sq.open_queue); + if (SIGNAL_RECEIVED) + goto err_out; + } + } + sq.busy = 1; /* Let's play spot-the-race-condition */ + + if (sq_allocate_buffers()) goto err_out_nobusy; + + sq_setup(numBufs, bufSize<<10,sound_buffers); + sq.open_mode = file->f_mode; + } + + + if (file->f_mode & FMODE_READ) { + if (read_sq.busy) { + rc = -EBUSY; + if (NON_BLOCKING(file->f_flags)) + goto err_out; + rc = -EINTR; + while (read_sq.busy) { + SLEEP(read_sq.open_queue); + if (SIGNAL_RECEIVED) + goto err_out; + } + rc = 0; + } + read_sq.busy = 1; + if (sq_allocate_read_buffers()) goto err_out_nobusy; + + read_sq_setup(numReadBufs,readbufSize<<10, sound_read_buffers); + read_sq.open_mode = file->f_mode; + } + + /* Start up the 4218 by: + * Reset. + * Enable, unreset. + */ + *((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_RSTAUDIO; + eieio(); + *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_ENAUDIO; + mdelay(50); + *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO; + + /* We need to send the current control word in case someone + * opened /dev/mixer and changed things while we were shut + * down. Chances are good the initialization that follows + * would have done this, but it is still possible it wouldn't. + */ + cs4218_ctl_write(cs4218_control); + + sound.minDev = MINOR(inode->i_rdev) & 0x0f; + sound.soft = sound.dsp; + sound.hard = sound.dsp; + sound_init(); + if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) { + sound_set_speed(8000); + sound_set_stereo(0); + sound_set_format(AFMT_MU_LAW); + } + + return 0; + +err_out_nobusy: + if (file->f_mode & FMODE_WRITE) { + sq.busy = 0; + WAKE_UP(sq.open_queue); + } + if (file->f_mode & FMODE_READ) { + read_sq.busy = 0; + WAKE_UP(read_sq.open_queue); + } +err_out: + MOD_DEC_USE_COUNT; + return rc; +} + + +static void sq_reset(void) +{ + sound_silence(); + sq.active = 0; + sq.count = 0; + sq.front = (sq.rear+1) % sq.max_count; +#if 0 + init_tdm_buffers(); +#endif +} + + +static int sq_fsync(struct file *filp, struct dentry *dentry) +{ + int rc = 0; + + sq.syncing = 1; + sq_play(); /* there may be an incomplete frame waiting */ + + while (sq.active) { + SLEEP(sq.sync_queue); + if (SIGNAL_RECEIVED) { + /* While waiting for audio output to drain, an + * interrupt occurred. Stop audio output immediately + * and clear the queue. */ + sq_reset(); + rc = -EINTR; + break; + } + } + + sq.syncing = 0; + return rc; +} + +static int sq_release(struct inode *inode, struct file *file) +{ + int rc = 0; + + if (sq.busy) + rc = sq_fsync(file, file->f_dentry); + sound.soft = sound.dsp; + sound.hard = sound.dsp; + sound_silence(); + + sq_release_read_buffers(); + sq_release_buffers(); + MOD_DEC_USE_COUNT; + + if (file->f_mode & FMODE_READ) { + read_sq.busy = 0; + WAKE_UP(read_sq.open_queue); + } + + if (file->f_mode & FMODE_WRITE) { + sq.busy = 0; + WAKE_UP(sq.open_queue); + } + + /* Shut down the SMC. + */ + cpmp->cp_smc[1].smc_smcmr &= ~(SMCMR_TEN | SMCMR_REN); + + /* Shut down the codec. + */ + *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO; + eieio(); + *((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_ENAUDIO; + + /* Wake up a process waiting for the queue being released. + * Note: There may be several processes waiting for a call + * to open() returning. */ + + return rc; +} + + +static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) +{ + u_long fmt; + int data; +#if 0 + int size, nbufs; +#else + int size; +#endif + + switch (cmd) { + case SNDCTL_DSP_RESET: + sq_reset(); + return 0; + case SNDCTL_DSP_POST: + case SNDCTL_DSP_SYNC: + return sq_fsync(file, file->f_dentry); + + /* ++TeSche: before changing any of these it's + * probably wise to wait until sound playing has + * settled down. */ + case SNDCTL_DSP_SPEED: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_speed(data)); + case SNDCTL_DSP_STEREO: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data)); + case SOUND_PCM_WRITE_CHANNELS: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); + case SNDCTL_DSP_SETFMT: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_format(data)); + case SNDCTL_DSP_GETFMTS: + fmt = 0; + if (sound.trans_write) { + if (sound.trans_write->ct_ulaw) + fmt |= AFMT_MU_LAW; + if (sound.trans_write->ct_alaw) + fmt |= AFMT_A_LAW; + if (sound.trans_write->ct_s8) + fmt |= AFMT_S8; + if (sound.trans_write->ct_u8) + fmt |= AFMT_U8; + if (sound.trans_write->ct_s16be) + fmt |= AFMT_S16_BE; + if (sound.trans_write->ct_u16be) + fmt |= AFMT_U16_BE; + if (sound.trans_write->ct_s16le) + fmt |= AFMT_S16_LE; + if (sound.trans_write->ct_u16le) + fmt |= AFMT_U16_LE; + } + return IOCTL_OUT(arg, fmt); + case SNDCTL_DSP_GETBLKSIZE: + size = sq.block_size + * sound.soft.size * (sound.soft.stereo + 1) + / (sound.hard.size * (sound.hard.stereo + 1)); + return IOCTL_OUT(arg, size); + case SNDCTL_DSP_SUBDIVIDE: + break; +#if 0 /* Sorry can't do this at the moment. The CPM allocated buffers + * long ago that can't be changed. + */ + case SNDCTL_DSP_SETFRAGMENT: + if (sq.count || sq.active || sq.syncing) + return -EINVAL; + IOCTL_IN(arg, size); + nbufs = size >> 16; + if (nbufs < 2 || nbufs > numBufs) + nbufs = numBufs; + size &= 0xffff; + if (size >= 8 && size <= 30) { + size = 1 << size; + size *= sound.hard.size * (sound.hard.stereo + 1); + size /= sound.soft.size * (sound.soft.stereo + 1); + if (size > (bufSize << 10)) + size = bufSize << 10; + } else + size = bufSize << 10; + sq_setup(numBufs, size, sound_buffers); + sq.max_active = nbufs; + return 0; +#endif + + default: + return mixer_ioctl(inode, file, cmd, arg); + } + return -EINVAL; +} + + + +static struct file_operations sq_fops = +{ + owner: THIS_MODULE, + llseek: sound_lseek, + read: sq_read, /* sq_read */ + write: sq_write, + ioctl: sq_ioctl, + open: sq_open, + release: sq_release, +}; + + +static void __init sq_init(void) +{ + sq_unit = register_sound_dsp(&sq_fops, -1); + if (sq_unit < 0) + return; + + init_waitqueue_head(&sq.action_queue); + init_waitqueue_head(&sq.open_queue); + init_waitqueue_head(&sq.sync_queue); + init_waitqueue_head(&read_sq.action_queue); + init_waitqueue_head(&read_sq.open_queue); + init_waitqueue_head(&read_sq.sync_queue); + + sq.busy = 0; + read_sq.busy = 0; + + /* whatever you like as startup mode for /dev/dsp, + * (/dev/audio hasn't got a startup mode). note that + * once changed a new open() will *not* restore these! + */ + sound.dsp.format = AFMT_S16_BE; + sound.dsp.stereo = 1; + sound.dsp.size = 16; + + /* set minimum rate possible without expanding */ + sound.dsp.speed = 8000; + + /* before the first open to /dev/dsp this wouldn't be set */ + sound.soft = sound.dsp; + sound.hard = sound.dsp; + + sound_silence(); +} + +/* + * /dev/sndstat + */ + + +/* state.buf should not overflow! */ + +static int state_open(struct inode *inode, struct file *file) +{ + char *buffer = state.buf, *mach = "", cs4218_buf[50]; + int len = 0; + + if (state.busy) + return -EBUSY; + + MOD_INC_USE_COUNT; + state.ptr = 0; + state.busy = 1; + + sprintf(cs4218_buf, "Crystal CS4218 on TDM, "); + mach = cs4218_buf; + + len += sprintf(buffer+len, "%sDMA sound driver:\n", mach); + + len += sprintf(buffer+len, "\tsound.format = 0x%x", sound.soft.format); + switch (sound.soft.format) { + case AFMT_MU_LAW: + len += sprintf(buffer+len, " (mu-law)"); + break; + case AFMT_A_LAW: + len += sprintf(buffer+len, " (A-law)"); + break; + case AFMT_U8: + len += sprintf(buffer+len, " (unsigned 8 bit)"); + break; + case AFMT_S8: + len += sprintf(buffer+len, " (signed 8 bit)"); + break; + case AFMT_S16_BE: + len += sprintf(buffer+len, " (signed 16 bit big)"); + break; + case AFMT_U16_BE: + len += sprintf(buffer+len, " (unsigned 16 bit big)"); + break; + case AFMT_S16_LE: + len += sprintf(buffer+len, " (signed 16 bit little)"); + break; + case AFMT_U16_LE: + len += sprintf(buffer+len, " (unsigned 16 bit little)"); + break; + } + len += sprintf(buffer+len, "\n"); + len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n", + sound.soft.speed, sound.hard.speed); + len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", + sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono"); + len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d" + " sq.max_active = %d\n", + sq.block_size, sq.max_count, sq.max_active); + len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count, + sq.rear_size); + len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n", + sq.active, sq.syncing); + state.len = len; + return 0; +} + + +static int state_release(struct inode *inode, struct file *file) +{ + state.busy = 0; + MOD_DEC_USE_COUNT; + return 0; +} + + +static ssize_t state_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int n = state.len - state.ptr; + if (n > count) + n = count; + if (n <= 0) + return 0; + if (copy_to_user(buf, &state.buf[state.ptr], n)) + return -EFAULT; + state.ptr += n; + return n; +} + + +static struct file_operations state_fops = +{ + owner: THIS_MODULE, + llseek: sound_lseek, + read: state_read, + open: state_open, + release: state_release, +}; + + +static void __init state_init(void) +{ + state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); + if (state_unit < 0) + return; + state.busy = 0; +} + + +/*** Common stuff ********************************************************/ + +static long long sound_lseek(struct file *file, long long offset, int orig) +{ + return -ESPIPE; +} + + +/*** Config & Setup **********************************************************/ + + +int __init tdm8xx_sound_init(void) +{ + int i, has_sound; + uint dp_addr; + volatile uint *sirp; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile immap_t *immap; + + has_sound = 0; + + /* Program the SI/TSA to use TDMa, connected to SMC2, for 4 bytes. + */ + cp = cpmp; /* Get pointer to Communication Processor */ + immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ + + /* Set all TDMa control bits to zero. This enables most features + * we want. + */ + cp->cp_simode &= ~0x00000fff; + + /* Enable common receive/transmit clock pins, use IDL format. + * Sync on falling edge, transmit rising clock, recieve falling + * clock, delay 1 bit on both Tx and Rx. Common Tx/Rx clocks and + * sync. + * Connect SMC2 to TSA. + */ + cp->cp_simode |= 0x80000141; + + /* Configure port A pins for TDMa operation. + * The RPX-Lite (MPC850/823) loses SMC2 when TDM is used. + */ + immap->im_ioport.iop_papar |= 0x01c0; /* Enable TDMa functions */ + immap->im_ioport.iop_padir |= 0x00c0; /* Enable TDMa Tx/Rx */ + immap->im_ioport.iop_padir &= ~0x0100; /* Enable L1RCLKa */ + + immap->im_ioport.iop_pcpar |= 0x0800; /* Enable L1RSYNCa */ + immap->im_ioport.iop_pcdir &= ~0x0800; + + /* Initialize the SI TDM routing table. We use TDMa only. + * The receive table and transmit table each have only one + * entry, to capture/send four bytes after each frame pulse. + * The 16-bit ram entry is 0000 0001 1000 1111. (SMC2) + */ + cp->cp_sigmr = 0; + sirp = (uint *)cp->cp_siram; + + *sirp = 0x018f0000; /* Receive entry */ + sirp += 64; + *sirp = 0x018f0000; /* Tramsmit entry */ + + /* Enable single TDMa routing. + */ + cp->cp_sigmr = 0x04; + + /* Initialize the SMC for transparent operation. + */ + sp = &cpmp->cp_smc[1]; + up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC2]; + + /* We need to allocate a transmit and receive buffer + * descriptors from dual port ram. + */ + dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * numReadBufs); + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; + up->smc_rbase = dp_addr; + rx_cur = rx_base = (cbd_t *)bdp; + + for (i=0; i<(numReadBufs-1); i++) { + bdp->cbd_bufaddr = 0; + bdp->cbd_datlen = 0; + bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; + bdp++; + } + bdp->cbd_bufaddr = 0; + bdp->cbd_datlen = 0; + bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; + + /* Now, do the same for the transmit buffers. + */ + dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * numBufs); + + bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; + up->smc_tbase = dp_addr; + tx_cur = tx_base = (cbd_t *)bdp; + + for (i=0; i<(numBufs-1); i++) { + bdp->cbd_bufaddr = 0; + bdp->cbd_datlen = 0; + bdp->cbd_sc = BD_SC_INTRPT; + bdp++; + } + bdp->cbd_bufaddr = 0; + bdp->cbd_datlen = 0; + bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT); + + /* Set transparent SMC mode. + * A few things are specific to our application. The codec interface + * is MSB first, hence the REVD selection. The CD/CTS pulse are + * used by the TSA to indicate the frame start to the SMC. + */ + up->smc_rfcr = SCC_EB; + up->smc_tfcr = SCC_EB; + up->smc_mrblr = readbufSize * 1024; + + /* Set 16-bit reversed data, transparent mode. + */ + sp->smc_smcmr = smcr_mk_clen(15) | + SMCMR_SM_TRANS | SMCMR_REVD | SMCMR_BS; + + /* Enable and clear events. + * Because of FIFO delays, all we need is the receive interrupt + * and we can process both the current receive and current + * transmit interrupt within a few microseconds of the transmit. + */ + sp->smc_smce = 0xff; + sp->smc_smcm = SMCM_TXE | SMCM_TX | SMCM_RX; + + /* Send the CPM an initialize command. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + sound.mach = mach_cs4218; + has_sound = 1; + + /* Initialize beep stuff */ + orig_mksound = kd_mksound; + kd_mksound = cs_mksound; + beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); + if (beep_buf == NULL) + printk(KERN_WARNING "dmasound: no memory for " + "beep buffer\n"); + + if (!has_sound) + return -ENODEV; + + /* Initialize the software SPI. + */ + sw_spi_init(); + + /* Set up sound queue, /dev/audio and /dev/dsp. */ + + /* Set default settings. */ + sq_init(); + + /* Set up /dev/sndstat. */ + state_init(); + + /* Set up /dev/mixer. */ + mixer_init(); + + if (!sound.mach.irqinit()) { + printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n"); + return -ENODEV; + } +#ifdef MODULE + irq_installed = 1; +#endif + + printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", + numBufs, bufSize); + + return 0; +} + +/* Due to FIFOs and bit delays, the transmit interrupt occurs a few + * microseconds ahead of the receive interrupt. + * When we get an interrupt, we service the transmit first, then + * check for a receive to prevent the overhead of returning through + * the interrupt handler only to get back here right away during + * full duplex operation. + */ +static void +cs4218_intr(void *dev_id, struct pt_regs *regs) +{ + volatile smc_t *sp; + volatile cpm8xx_t *cp; + + sp = &cpmp->cp_smc[1]; + + if (sp->smc_smce & SCCM_TX) { + sp->smc_smce = SCCM_TX; + cs4218_tdm_tx_intr((void *)sp); + } + + if (sp->smc_smce & SCCM_RX) { + sp->smc_smce = SCCM_RX; + cs4218_tdm_rx_intr((void *)sp); + } + + if (sp->smc_smce & SCCM_TXE) { + /* Transmit underrun. This happens with the application + * didn't keep up sending buffers. We tell the SMC to + * restart, which will cause it to poll the current (next) + * BD. If the user supplied data since this occurred, + * we just start running again. If they didn't, the SMC + * will poll the descriptor until data is placed there. + */ + sp->smc_smce = SCCM_TXE; + cp = cpmp; /* Get pointer to Communication Processor */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, + CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } +} + + +#define MAXARGS 8 /* Should be sufficient for now */ + +void __init dmasound_setup(char *str, int *ints) +{ + /* check the bootstrap parameter for "dmasound=" */ + + switch (ints[0]) { + case 3: + if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS)) + printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius); + else + catchRadius = ints[3]; + /* fall through */ + case 2: + if (ints[1] < MIN_BUFFERS) + printk("dmasound_setup: illegal number of buffers, using default = %d\n", numBufs); + else + numBufs = ints[1]; + if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE) + printk("dmasound_setup: illegal buffer size, using default = %d\n", bufSize); + else + bufSize = ints[2]; + break; + case 0: + break; + default: + printk("dmasound_setup: illegal number of arguments\n"); + } +} + +/* Software SPI functions. + * These are on Port B. + */ +#define PB_SPICLK ((uint)0x00000002) +#define PB_SPIMOSI ((uint)0x00000004) +#define PB_SPIMISO ((uint)0x00000008) + +static +void sw_spi_init(void) +{ + volatile cpm8xx_t *cp; + volatile uint *hcsr4; + + hcsr4 = (volatile uint *)HIOX_CSR4_ADDR; + cp = cpmp; /* Get pointer to Communication Processor */ + + *hcsr4 &= ~HIOX_CSR4_AUDSPISEL; /* Disable SPI select */ + + /* Make these Port B signals general purpose I/O. + * First, make sure the clock is low. + */ + cp->cp_pbdat &= ~PB_SPICLK; + cp->cp_pbpar &= ~(PB_SPICLK | PB_SPIMOSI | PB_SPIMISO); + + /* Clock and Master Output are outputs. + */ + cp->cp_pbdir |= (PB_SPICLK | PB_SPIMOSI); + + /* Master Input. + */ + cp->cp_pbdir &= ~PB_SPIMISO; + +} + +/* Write the CS4218 control word out the SPI port. While the + * the control word is going out, the status word is arriving. + */ +static +uint cs4218_ctl_write(uint ctlreg) +{ + uint status; + + sw_spi_io((u_char *)&ctlreg, (u_char *)&status, 4); + + /* Shadow the control register.....I guess we could do + * the same for the status, but for now we just return it + * and let the caller decide. + */ + cs4218_control = ctlreg; + return status; +} + +static +void sw_spi_io(u_char *obuf, u_char *ibuf, uint bcnt) +{ + int bits, i; + u_char outbyte, inbyte; + volatile cpm8xx_t *cp; + volatile uint *hcsr4; + + hcsr4 = (volatile uint *)HIOX_CSR4_ADDR; + cp = cpmp; /* Get pointer to Communication Processor */ + + /* The timing on the bus is pretty slow. Code inefficiency + * and eieio() is our friend here :-). + */ + cp->cp_pbdat &= ~PB_SPICLK; + *hcsr4 |= HIOX_CSR4_AUDSPISEL; /* Enable SPI select */ + eieio(); + + /* Clock in/out the bytes. Data is valid on the falling edge + * of the clock. Data is MSB first. + */ + for (i=0; icp_pbdat |= PB_SPICLK; + eieio(); + if (outbyte & 0x80) + cp->cp_pbdat |= PB_SPIMOSI; + else + cp->cp_pbdat &= ~PB_SPIMOSI; + eieio(); + cp->cp_pbdat &= ~PB_SPICLK; + eieio(); + outbyte <<= 1; + inbyte <<= 1; + if (cp->cp_pbdat & PB_SPIMISO) + inbyte |= 1; + } + *ibuf++ = inbyte; + } + + *hcsr4 &= ~HIOX_CSR4_AUDSPISEL; /* Disable SPI select */ + eieio(); +} + +void cleanup_module(void) +{ + if (irq_installed) { + sound_silence(); +#ifdef MODULE + sound.mach.irqcleanup(); +#endif + } + + sq_release_read_buffers(); + sq_release_buffers(); + + if (mixer_unit >= 0) + unregister_sound_mixer(mixer_unit); + if (state_unit >= 0) + unregister_sound_special(state_unit); + if (sq_unit >= 0) + unregister_sound_dsp(sq_unit); +} + +module_init(tdm8xx_sound_init); +module_exit(cleanup_module); + diff -Nru a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c --- a/arch/ppc/8xx_io/enet.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/8xx_io/enet.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.enet.c 1.17 10/11/01 11:55:47 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Ethernet driver for Motorola MPC8xx. @@ -139,6 +139,11 @@ cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ cbd_t *dirty_tx; /* The ring entries to be free()ed. */ scc_t *sccp; + + /* Virtual addresses for the receive buffers because we can't + * do a __va() on them anymore. + */ + unsigned char *rx_vaddr[RX_RING_SIZE]; struct net_device_stats stats; uint tx_full; spinlock_t lock; @@ -507,7 +512,7 @@ skb->dev = dev; skb_put(skb,pkt_len-4); /* Make room */ eth_copy_and_sum(skb, - (unsigned char *)__va(bdp->cbd_bufaddr), + cep->rx_vaddr[bdp - cep->rx_bd_base], pkt_len-4, 0); skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); @@ -641,10 +646,9 @@ { struct net_device *dev; struct scc_enet_private *cep; - int i, j; - unsigned char *eap; - unsigned long mem_addr; - pte_t *pte; + int i, j, k; + unsigned char *eap, *ba; + dma_addr_t mem_addr; bd_t *bd; volatile cbd_t *bdp; volatile cpm8xx_t *cp; @@ -834,24 +838,21 @@ bdp->cbd_sc |= BD_SC_WRAP; bdp = cep->rx_bd_base; + k = 0; for (i=0; icbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR; - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = mem_addr; + cep->rx_vaddr[k++] = ba; mem_addr += CPM_ENET_RX_FRSIZE; + ba += CPM_ENET_RX_FRSIZE; bdp++; } } diff -Nru a/arch/ppc/8xx_io/fec.c b/arch/ppc/8xx_io/fec.c --- a/arch/ppc/8xx_io/fec.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/8xx_io/fec.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.fec.c 1.20 10/11/01 11:55:47 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. @@ -163,7 +163,12 @@ cbd_t *tx_bd_base; cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ cbd_t *dirty_tx; /* The ring entries to be free()ed. */ - scc_t *sccp; + + /* Virtual addresses for the receive buffers because we can't + * do a __va() on them anymore. + */ + unsigned char *rx_vaddr[RX_RING_SIZE]; + struct net_device_stats stats; uint tx_full; spinlock_t lock; @@ -688,7 +693,7 @@ fep->stats.rx_packets++; pkt_len = bdp->cbd_datlen; fep->stats.rx_bytes += pkt_len; - data = (__u8*)__va(bdp->cbd_bufaddr); + data = fep->rx_vaddr[bdp - fep->rx_bd_base]; #ifdef CONFIG_FEC_PACKETHOOK /* Packet hook ... */ @@ -724,9 +729,7 @@ } else { skb->dev = dev; skb_put(skb,pkt_len-4); /* Make room */ - eth_copy_and_sum(skb, - (unsigned char *)__va(bdp->cbd_bufaddr), - pkt_len-4, 0); + eth_copy_and_sum(skb, data, pkt_len-4, 0); skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); } @@ -1504,10 +1507,9 @@ { struct net_device *dev; struct fec_enet_private *fep; - int i, j; - unsigned char *eap, *iap; + int i, j, k; + unsigned char *eap, *iap, *ba; unsigned long mem_addr; - pte_t *pte; volatile cbd_t *bdp; cbd_t *cbd_base; volatile immap_t *immap; @@ -1578,14 +1580,7 @@ printk("FEC initialization failed.\n"); return 1; } - mem_addr = __get_free_page(GFP_KERNEL); - cbd_base = (cbd_t *)mem_addr; - - /* Make it uncached. - */ - pte = va_to_pte(mem_addr); - pte_val(*pte) |= _PAGE_NO_CACHE; - flush_tlb_page(init_mm.mmap, mem_addr); + cbd_base = (cbd_t *)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &mem_addr); /* Set receive and transmit descriptor base. */ @@ -1597,24 +1592,21 @@ /* Initialize the receive buffer descriptors. */ bdp = fep->rx_bd_base; + k = 0; for (i=0; icbd_sc = BD_ENET_RX_EMPTY; - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = mem_addr; + fep->rx_vaddr[k++] = ba; mem_addr += FEC_ENET_RX_FRSIZE; + ba += FEC_ENET_RX_FRSIZE; bdp++; } } @@ -1776,8 +1768,8 @@ /* Set receive and transmit descriptor base. */ - fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base)); - fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base)); + fecp->fec_r_des_start = iopa((uint)(fep->rx_bd_base)); + fecp->fec_x_des_start = iopa((uint)(fep->tx_bd_base)); fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; fep->cur_rx = fep->rx_bd_base; diff -Nru a/arch/ppc/8xx_io/uart.c b/arch/ppc/8xx_io/uart.c --- a/arch/ppc/8xx_io/uart.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/8xx_io/uart.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.uart.c 1.19 10/26/01 09:59:32 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * UART driver for MPC860 CPM SCC or SMC @@ -49,9 +49,7 @@ #endif #ifdef CONFIG_KGDB -extern void breakpoint(void); -extern void set_debug_traps(void); -extern int kgdb_output_string (const char* s, unsigned int count); +#include #endif #ifdef CONFIG_SERIAL_CONSOLE @@ -135,6 +133,16 @@ #define NUM_IS_SCC ((int)0x00010000) #define PORT_NUM(P) ((P) & 0x0000ffff) +/* The choice of serial port to use for KGDB. If the system has + * two ports, you can use one for console and one for KGDB (which + * doesn't make sense to me, but people asked for it). + */ +#ifdef CONFIG_KGDB_TTYS1 +#define KGDB_SER_IDX 1 /* SCC2/SMC2 */ +#else +#define KGDB_SER_IDX 0 /* SCC1/SMC1 */ +#endif + /* Processors other than the 860 only get SMCs configured by default. * Either they don't have SCCs or they are allocated somewhere else. * Of course, there are now 860s without some SCCs, so we will need to @@ -211,6 +219,12 @@ cbd_t *rx_cur; cbd_t *tx_bd_base; cbd_t *tx_cur; + + /* Virtual addresses for the FIFOs because we can't __va() a + * physical address anymore. + */ + unsigned char *rx_va_base; + unsigned char *tx_va_base; } ser_info_t; static struct console sercons = { @@ -387,8 +401,15 @@ /* Get the number of characters and the buffer pointer. */ i = bdp->cbd_datlen; - cp = (unsigned char *)__va(bdp->cbd_bufaddr); + cp = info->rx_va_base + ((bdp - info->rx_bd_base) * RX_BUF_SIZE); status = bdp->cbd_sc; +#ifdef CONFIG_KGDB + if (info->state->smc_scc_num == KGDB_SER_IDX) { + if (*cp == 0x03 || *cp == '$') + breakpoint(); + return; + } +#endif /* Check to see if there is room in the tty buffer for * the characters in our BD buffer. If not, we exit @@ -1027,6 +1048,7 @@ { ser_info_t *info = (ser_info_t *)tty->driver_data; volatile cbd_t *bdp; + unsigned char *cp; if (serial_paranoia_check(info, tty->device, "rs_put_char")) return; @@ -1037,7 +1059,8 @@ bdp = info->tx_cur; while (bdp->cbd_sc & BD_SC_READY); - *((char *)__va(bdp->cbd_bufaddr)) = ch; + cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); + *cp = ch; bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; @@ -1058,6 +1081,7 @@ int c, ret = 0; ser_info_t *info = (ser_info_t *)tty->driver_data; volatile cbd_t *bdp; + unsigned char *cp; #ifdef CONFIG_KGDB /* Try to let stub handle output. Returns true if it did. */ @@ -1084,14 +1108,15 @@ break; } + cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); if (from_user) { - if (copy_from_user(__va(bdp->cbd_bufaddr), buf, c)) { + if (copy_from_user((void *)cp, buf, c)) { if (!ret) ret = -EFAULT; break; } } else { - memcpy(__va(bdp->cbd_bufaddr), buf, c); + memcpy((void *)cp, buf, c); } bdp->cbd_datlen = c; @@ -1166,6 +1191,7 @@ static void rs_8xx_send_xchar(struct tty_struct *tty, char ch) { volatile cbd_t *bdp; + unsigned char *cp; ser_info_t *info = (ser_info_t *)tty->driver_data; @@ -1175,7 +1201,8 @@ bdp = info->tx_cur; while (bdp->cbd_sc & BD_SC_READY); - *((char *)__va(bdp->cbd_bufaddr)) = ch; + cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); + *cp = ch; bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; @@ -1800,7 +1827,7 @@ schedule_timeout(char_time); if (signal_pending(current)) break; - if (timeout && ((orig_jiffies + timeout) < jiffies)) + if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; /* The 'tx_cur' is really the next buffer to send. We @@ -2202,6 +2229,11 @@ #ifdef CONFIG_SERIAL_CONSOLE +/* I need this just so I can store the virtual addresses and have + * common functions for the early console printing. + */ +static ser_info_t consinfo; + /* * Print a string to the serial port trying not to disturb any possible * real use of the port... @@ -2234,6 +2266,8 @@ /* Get the address of the host memory buffer. */ bdp = bdbase = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + + info = &consinfo; } /* @@ -2261,7 +2295,7 @@ if ((uint)(bdp->cbd_bufaddr) > (uint)IMAP_ADDR) cp = (u_char *)(bdp->cbd_bufaddr); else - cp = __va(bdp->cbd_bufaddr); + cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); *cp = *s; bdp->cbd_datlen = 1; @@ -2275,7 +2309,7 @@ /* if a LF, also do CR... */ if (*s == 10) { while (bdp->cbd_sc & BD_SC_READY); - cp = __va(bdp->cbd_bufaddr); + cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); *cp = 13; bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; @@ -2350,10 +2384,13 @@ * If the port has been initialized for general use, we must * use information from the port structure. */ - if ((info = (ser_info_t *)ser->info)) + if ((info = (ser_info_t *)ser->info)) { bdp = info->rx_cur; - else + } + else { bdp = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + info = &consinfo; + } /* * We need to gracefully shut down the receiver, disable @@ -2375,7 +2412,7 @@ if ((uint)(bdp->cbd_bufaddr) > (uint)IMAP_ADDR) cp = (u_char *)(bdp->cbd_bufaddr); else - cp = __va(bdp->cbd_bufaddr); + cp = info->rx_va_base + ((bdp - info->rx_bd_base) * RX_BUF_SIZE); if (obuf) { i = c = bdp->cbd_datlen; @@ -2418,7 +2455,7 @@ static char kgdb_buf[RX_BUF_SIZE], *kgdp; static int kgdb_chars; -unsigned char +char getDebugChar(void) { if (kgdb_chars <= 0) { @@ -2430,9 +2467,18 @@ return(*kgdp++); } -void kgdb_interruptible(int state) +void kgdb_interruptible(int yes) { + volatile smc_t *smcp; + + smcp = &cpmp->cp_smc[KGDB_SER_IDX]; + + if (yes == 1) + smcp->smc_smcm |= SMCM_RX; + else + smcp->smc_smcm &= ~SMCM_RX; } + void kgdb_map_scc(void) { struct serial_state *ser; @@ -2459,7 +2505,7 @@ /* Allocate space for an input FIFO, plus a few bytes for output. * Allocate bytes to maintain word alignment. */ - mem_addr = (uint)(&cpmp->cp_dpmem[0x1000]); + mem_addr = (uint)(&cpmp->cp_dpmem[0xa00]); /* Set the physical address of the host memory buffers in * the buffer descriptors. @@ -2522,7 +2568,11 @@ __clear_user(&serial_driver,sizeof(struct tty_driver)); serial_driver.magic = TTY_DRIVER_MAGIC; serial_driver.driver_name = "serial"; +#ifdef CONFIG_DEVFS_FS + serial_driver.name = "tts/%d"; +#else serial_driver.name = "ttyS"; +#endif serial_driver.major = TTY_MAJOR; serial_driver.minor_start = 64; serial_driver.num = NR_PORTS; @@ -2560,7 +2610,11 @@ * major number and the subtype code. */ callout_driver = serial_driver; +#ifdef CONFIG_DEVFS_FS + callout_driver.name = "cua/%d"; +#else callout_driver.name = "cua"; +#endif callout_driver.major = TTYAUX_MAJOR; callout_driver.subtype = SERIAL_TYPE_CALLOUT; callout_driver.read_proc = 0; @@ -2672,6 +2726,7 @@ /* Allocate space for FIFOs in the host memory. */ mem_addr = m8xx_cpm_hostalloc(RX_NUM_FIFO * RX_BUF_SIZE); + info->rx_va_base = (unsigned char *)mem_addr; /* Set the physical address of the host memory * buffers in the buffer descriptors, and the @@ -2681,12 +2736,12 @@ info->rx_cur = info->rx_bd_base = (cbd_t *)bdp; for (j=0; j<(RX_NUM_FIFO-1); j++) { - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = iopa(mem_addr); bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; mem_addr += RX_BUF_SIZE; bdp++; } - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = iopa(mem_addr); bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; idx = PORT_NUM(info->state->smc_scc_num); @@ -2706,6 +2761,7 @@ /* Allocate space for FIFOs in the host memory. */ mem_addr = m8xx_cpm_hostalloc(TX_NUM_FIFO * TX_BUF_SIZE); + info->tx_va_base = (unsigned char *)mem_addr; /* Set the physical address of the host memory * buffers in the buffer descriptors, and the @@ -2715,12 +2771,12 @@ info->tx_cur = info->tx_bd_base = (cbd_t *)bdp; for (j=0; j<(TX_NUM_FIFO-1); j++) { - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = iopa(mem_addr); bdp->cbd_sc = BD_SC_INTRPT; mem_addr += TX_BUF_SIZE; bdp++; } - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = iopa(mem_addr); bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT); if (info->state->smc_scc_num & NUM_IS_SCC) { @@ -2926,20 +2982,28 @@ * from dual port ram, and a character buffer area from host mem. */ + /* Allocate space for two FIFOs. We can't allocate from host + * memory yet because vm allocator isn't initialized + * during this early console init. + */ + dp_addr = m8xx_cpm_dpalloc(8); + mem_addr = (uint)(&cpmp->cp_dpmem[dp_addr]); + /* Allocate space for two buffer descriptors in the DP ram. */ dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * 2); - /* Allocate space for two 2 byte FIFOs in the host memory. - */ - mem_addr = m8xx_cpm_hostalloc(8); - /* Set the physical address of the host memory buffers in * the buffer descriptors. */ bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; - bdp->cbd_bufaddr = __pa(mem_addr); - (bdp+1)->cbd_bufaddr = __pa(mem_addr+4); + bdp->cbd_bufaddr = iopa(mem_addr); + (bdp+1)->cbd_bufaddr = iopa(mem_addr+4); + + consinfo.rx_va_base = mem_addr; + consinfo.rx_bd_base = bdp; + consinfo.tx_va_base = mem_addr + 4; + consinfo.tx_bd_base = bdp+1; /* For the receive, set empty and wrap. * For transmit, set wrap. @@ -3044,3 +3108,18 @@ return 0; } +#ifdef CONFIG_INPUT_KEYBDEV + +void handle_scancode(unsigned char scancode, int down) +{ + printk("handle_scancode(scancode=0x%x, down=%d)\n", scancode, down); +} + +static void kbd_bh(unsigned long dummy) +{ +} + +DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); +void (*kbd_ledfunc)(unsigned int led); + +#endif diff -Nru a/arch/ppc/Config.help b/arch/ppc/Config.help --- a/arch/ppc/Config.help Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/Config.help Tue Feb 19 18:08:58 2002 @@ -1061,3 +1061,45 @@ Include in-kernel hooks for the xmon kernel monitor/debugger supported by the PPC port. +CONFIG_ADVANCED_OPTIONS + This option will enable prompting for a variety of advanced kernel + configuration options. These options can cause the kernel to not + work if they are set incorrectly, but can be used to optimize certain + aspects of kernel memory management. + + Unless you know what you are doing you *should not* enable this option. + +CONFIG_HIGHMEM_START_BOOL + Unless you know what you are doing you *should not* set this option. + + It can be used to override the default PKMAP_BASE address which + is the location of the high memory pool. This can be useful in + optimizing virtual memory usage in a system. + +CONFIG_LOWMEM_SIZE_BOOL + Unless you know what you are doing you *should not* set this option. + + It can be used to override the standard calculated value of + MAX_LOW_MEM. This can be useful in optimizing virtual memory usage + in a system. + +CONFIG_KERNEL_START_BOOL + Unless you know what you are doing you *should not* set this option. + + It can be used to override the standard PAGE_OFFSET/KERNELBASE + value used by the kernel. This can be useful in controlling + amount of virtual address space available to the kernel. + +CONFIG_TASK_SIZE_BOOL + Unless you know what you are doing you *should not* set this option. + + It can be used to override the standard TASK_SIZE value used + by the kernel. This can be useful in controlling amount of + virtual address space available to user tasks. + +CONFIG_BOOT_LOAD_BOOL + Unless you know what you are doing you *should not* set this option. + + It can be used to change the initial load address of the zImage or + zImage.initrd file. This can be useful if you are on a board which has + a small ammount of memory. diff -Nru a/arch/ppc/Makefile b/arch/ppc/Makefile --- a/arch/ppc/Makefile Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/Makefile Tue Feb 19 18:08:57 2002 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.Makefile 1.23 09/18/01 11:19:05 paulus +# BK Id: %F% %I% %G% %U% %#% # # This file is included by the global makefile so that you can add your own # architecture-specific flags and dependencies. Remember to do have actions @@ -15,45 +15,51 @@ # # Be sure to change PAGE_OFFSET in include/asm-ppc/page.h to match +ifdef CONFIG_KERNEL_START_BOOL +KERNELLOAD =$(CONFIG_KERNEL_START) +else KERNELLOAD =0xc0000000 - -ifeq ($(shell uname -m),ppc) -CHECKS = checks endif -ASFLAGS = LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic -CPPFLAGS := $(CPPFLAGS) -D__powerpc__ -CFLAGS := $(CFLAGS) -D__powerpc__ -fsigned-char \ +CPPFLAGS := $(CPPFLAGS) -I$(TOPDIR)/arch/$(ARCH) +AFLAGS := $(AFLAGS) -I$(TOPDIR)/arch/$(ARCH) +CFLAGS := $(CFLAGS) -I$(TOPDIR)/arch/$(ARCH) -fsigned-char \ -msoft-float -pipe -ffixed-r2 -Wno-uninitialized \ -mmultiple -mstring CPP = $(CC) -E $(CFLAGS) ifdef CONFIG_4xx -CFLAGS := $(CFLAGS) -mcpu=403 -endif - -ifdef CONFIG_8xx -CFLAGS := $(CFLAGS) -mcpu=860 +CFLAGS := $(CFLAGS) -Wa,-m405 endif ifdef CONFIG_PPC64BRIDGE CFLAGS := $(CFLAGS) -Wa,-mppc64bridge endif +ifdef CONFIG_MORE_COMPILE_OPTIONS +# Use sed to remove the quotes. + CFLAGS += $(shell echo $(CONFIG_COMPILE_OPTIONS) | sed -e 's/"//g') +endif + ifdef CONFIG_4xx HEAD := arch/ppc/kernel/head_4xx.o else ifdef CONFIG_8xx HEAD := arch/ppc/kernel/head_8xx.o else - HEAD := arch/ppc/kernel/head.o + ifdef CONFIG_PPC_ISERIES + HEAD := arch/ppc/kernel/iSeries_head.o + else + HEAD := arch/ppc/kernel/head.o + endif endif endif -ARCH_SUBDIRS = arch/ppc/kernel arch/ppc/mm arch/ppc/lib +ARCH_SUBDIRS = arch/ppc/kernel arch/ppc/platforms arch/ppc/mm arch/ppc/lib SUBDIRS := $(SUBDIRS) $(ARCH_SUBDIRS) -CORE_FILES := arch/ppc/kernel/kernel.o arch/ppc/mm/mm.o arch/ppc/lib/lib.o $(CORE_FILES) +CORE_FILES := arch/ppc/kernel/kernel.o arch/ppc/platforms/platform.o \ + arch/ppc/mm/mm.o arch/ppc/lib/lib.o $(CORE_FILES) ifdef CONFIG_MATH_EMULATION SUBDIRS += arch/ppc/math-emu @@ -77,20 +83,27 @@ DRIVERS += arch/ppc/8260_io/8260_io.o endif +ifdef CONFIG_4xx +SUBDIRS += arch/ppc/4xx_io +DRIVERS += arch/ppc/4xx_io/4xx_io.o +endif + ifdef CONFIG_APUS SUBDIRS += arch/ppc/amiga CORE_FILES += arch/ppc/amiga/amiga.o endif -checks: - @$(MAKE) -C arch/$(ARCH)/kernel checks +ifdef CONFIG_PPC_ISERIES +SUBDIRS += arch/ppc/iSeries +CORE_FILES += arch/ppc/iSeries/iSeries.o +endif -BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd +BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd pImage vmlinux.sm # All the instructions talk about "make bzImage". bzImage: zImage -$(BOOT_TARGETS): $(CHECKS) vmlinux +$(BOOT_TARGETS): vmlinux @$(MAKEBOOT) $@ %_config: arch/ppc/configs/%_defconfig @@ -98,7 +111,8 @@ cp -f arch/ppc/configs/$(@:config=defconfig) arch/ppc/defconfig archclean: - rm -f arch/ppc/kernel/{mk_defs,ppc_defs.h,find_name,checks} + rm -f arch/ppc/kernel/{mk_defs,ppc_defs.h,find_name} + rm -f arch/ppc/iSeries/ReleaseData.h @$(MAKEBOOT) clean archmrproper: diff -Nru a/arch/ppc/amiga/amiints.c b/arch/ppc/amiga/amiints.c --- a/arch/ppc/amiga/amiints.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/amiga/amiints.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.amiints.c 1.8 05/21/01 00:48:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/arch/m68k/amiga/amiints.c -- Amiga Linux interrupt handling code @@ -42,9 +42,10 @@ #include #include #include +#include +#include #include #include -#include #include #include @@ -62,10 +63,6 @@ unsigned long flags, const char *devname, void *dev_id); extern void cia_free_irq(unsigned int irq, void *dev_id); extern void cia_init_IRQ(struct ciabase *base); -extern int cia_get_irq_list(struct ciabase *base, struct seq_file *p); - -/* irq node variables for amiga interrupt sources */ -static irq_node_t *ami_irq_list[AMI_STD_IRQS]; unsigned short ami_intena_vals[AMI_STD_IRQS] = { IF_VERTB, IF_COPER, IF_AUD0, IF_AUD1, IF_AUD2, IF_AUD3, IF_BLIT, @@ -79,7 +76,7 @@ static void ami_badint(int irq, void *dev_id, struct pt_regs *fp) { - num_spurious += 1; +/* num_spurious += 1;*/ } /* @@ -98,25 +95,12 @@ { int i; - /* initialize handlers */ - for (i = 0; i < AMI_STD_IRQS; i++) { - if (ami_servers[i]) { - ami_irq_list[i] = NULL; - } else { - ami_irq_list[i] = new_irq_node(); - ami_irq_list[i]->handler = ami_badint; - ami_irq_list[i]->flags = 0; - ami_irq_list[i]->dev_id = NULL; - ami_irq_list[i]->devname = NULL; - ami_irq_list[i]->next = NULL; - } - } for (i = 0; i < AMI_IRQS; i++) ami_ablecount[i] = 0; /* turn off PCMCIA interrupts */ if (AMIGAHW_PRESENT(PCMCIA)) - pcmcia_disable_irq(); + gayle.inten = GAYLE_IRQ_IDE; /* turn off all interrupts... */ custom.intena = 0x7fff; @@ -139,152 +123,6 @@ cia_init_IRQ(&ciab_base); } -static inline int amiga_insert_irq(irq_node_t **list, irq_node_t *node) -{ - unsigned long flags; - irq_node_t *cur; - - if (!node->dev_id) - printk("%s: Warning: dev_id of %s is zero\n", - __FUNCTION__, node->devname); - - save_flags(flags); - cli(); - - cur = *list; - - if (node->flags & SA_INTERRUPT) { - if (node->flags & SA_SHIRQ) - return -EBUSY; - /* - * There should never be more than one - */ - while (cur && cur->flags & SA_INTERRUPT) { - list = &cur->next; - cur = cur->next; - } - } else { - while (cur) { - list = &cur->next; - cur = cur->next; - } - } - - node->next = cur; - *list = node; - - restore_flags(flags); - return 0; -} - -static inline void amiga_delete_irq(irq_node_t **list, void *dev_id) -{ - unsigned long flags; - irq_node_t *node; - - save_flags(flags); - cli(); - - for (node = *list; node; list = &node->next, node = *list) { - if (node->dev_id == dev_id) { - *list = node->next; - /* Mark it as free. */ - node->handler = NULL; - restore_flags(flags); - return; - } - } - restore_flags(flags); - printk ("%s: tried to remove invalid irq\n", __FUNCTION__); -} - -/* - * amiga_request_irq : add an interrupt service routine for a particular - * machine specific interrupt source. - * If the addition was successful, it returns 0. - */ - -int amiga_request_irq(unsigned int irq, - void (*handler)(int, void *, struct pt_regs *), - unsigned long flags, const char *devname, void *dev_id) -{ - irq_node_t *node; - int error = 0; - - if (irq >= AMI_IRQS) { - printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__, - irq, devname); - return -ENXIO; - } - - if (irq >= IRQ_AMIGA_AUTO) - return sys_request_irq(irq - IRQ_AMIGA_AUTO, handler, - flags, devname, dev_id); - - if (irq >= IRQ_AMIGA_CIAA) - return cia_request_irq(irq, handler, flags, devname, dev_id); - - /* - * IRQ_AMIGA_PORTS & IRQ_AMIGA_EXTER defaults to shared, - * we could add a check here for the SA_SHIRQ flag but all drivers - * should be aware of sharing anyway. - */ - if (ami_servers[irq]) { - if (!(node = new_irq_node())) - return -ENOMEM; - node->handler = handler; - node->flags = flags; - node->dev_id = dev_id; - node->devname = devname; - node->next = NULL; - error = amiga_insert_irq(&ami_irq_list[irq], node); - } else { - ami_irq_list[irq]->handler = handler; - ami_irq_list[irq]->flags = flags; - ami_irq_list[irq]->dev_id = dev_id; - ami_irq_list[irq]->devname = devname; - } - - /* enable the interrupt */ - if (irq < IRQ_AMIGA_PORTS && !ami_ablecount[irq]) - custom.intena = IF_SETCLR | ami_intena_vals[irq]; - - return error; -} - -void amiga_free_irq(unsigned int irq, void *dev_id) -{ - if (irq >= AMI_IRQS) { - printk ("%s: Unknown IRQ %d\n", __FUNCTION__, irq); - return; - } - - if (irq >= IRQ_AMIGA_AUTO) { - sys_free_irq(irq - IRQ_AMIGA_AUTO, dev_id); - return; - } - if (irq >= IRQ_AMIGA_CIAA) { - cia_free_irq(irq, dev_id); - return; - } - - if (ami_servers[irq]) { - amiga_delete_irq(&ami_irq_list[irq], dev_id); - /* if server list empty, disable the interrupt */ - if (!ami_irq_list[irq] && irq < IRQ_AMIGA_PORTS) - custom.intena = ami_intena_vals[irq]; - } else { - if (ami_irq_list[irq]->dev_id != dev_id) - printk("%s: removing probably wrong IRQ %d from %s\n", - __FUNCTION__, irq, ami_irq_list[irq]->devname); - ami_irq_list[irq]->handler = ami_badint; - ami_irq_list[irq]->flags = 0; - ami_irq_list[irq]->dev_id = NULL; - ami_irq_list[irq]->devname = NULL; - custom.intena = ami_intena_vals[irq]; - } -} - /* * Enable/disable a particular machine specific interrupt source. * Note that this may affect other interrupts in case of a shared interrupt. @@ -351,20 +189,24 @@ inline void amiga_do_irq(int irq, struct pt_regs *fp) { - kstat.irqs[0][SYS_IRQS + irq]++; - ami_irq_list[irq]->handler(irq, ami_irq_list[irq]->dev_id, fp); + irq_desc_t *desc = irq_desc + irq; + struct irqaction *action = desc->action; + + kstat.irqs[0][irq]++; + action->handler(irq, action->dev_id, fp); } void amiga_do_irq_list(int irq, struct pt_regs *fp) { - irq_node_t *node; + irq_desc_t *desc = irq_desc + irq; + struct irqaction *action; - kstat.irqs[0][SYS_IRQS + irq]++; + kstat.irqs[0][irq]++; custom.intreq = ami_intena_vals[irq]; - for (node = ami_irq_list[irq]; node; node = node->next) - node->handler(irq, node->dev_id, fp); + for (action = desc->action; action; action = action->next) + action->handler(irq, action->dev_id, fp); } /* @@ -470,9 +312,15 @@ /* The PPC irq handling links all handlers requested on the same vector and executes them in a loop. Having ami_badint at the end of the chain is a bad idea. */ -void (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { - NULL, ami_int1, NULL, ami_int3, - ami_int4, ami_int5, NULL, ami_int7 +struct irqaction amiga_sys_irqaction[AUTO_IRQS] = { + { handler: ami_badint, name: "spurious int" }, + { handler: ami_int1, name: "int1 handler" }, + { 0, /* CIAA */ }, + { handler: ami_int3, name: "int3 handler" }, + { handler: ami_int4, name: "int4 handler" }, + { handler: ami_int5, name: "int5 handler" }, + { 0, /* CIAB */ }, + { handler: ami_int7, name: "int7 handler" }, }; #else void (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { @@ -480,29 +328,3 @@ ami_int4, ami_int5, ami_badint, ami_int7 }; #endif - -int show_amiga_intreeupts(struct seq_file *p, void *v) -{ - int i; - irq_node_t *node; - - for (i = 0; i < AMI_STD_IRQS; i++) { - if (!(node = ami_irq_list[i])) - continue; - seq_printf(p, "ami %2d: %10u ", i, - kstat.irqs[0][SYS_IRQS + i]); - do { - if (node->flags & SA_INTERRUPT) - seq_puts(p, "F "); - else - seq_puts(p, " "); - seq_printf(p, "%s\n", node->devname); - if ((node = node->next)) - seq_puts(p, " "); - } while (node); - } - - cia_get_irq_list(&ciaa_base, p); - cia_get_irq_list(&ciab_base, p); - return len; -} diff -Nru a/arch/ppc/amiga/cia.c b/arch/ppc/amiga/cia.c --- a/arch/ppc/amiga/cia.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/amiga/cia.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.cia.c 1.7 05/21/01 00:48:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/arch/m68k/amiga/cia.c - CIA support @@ -16,10 +16,10 @@ #include #include #include -#include +#include +#include #include #include -#include #include #include @@ -31,7 +31,6 @@ u_short int_mask; int handler_irq, cia_irq, server_irq; char *name; - irq_handler_t irq_list[CIA_IRQS]; } ciaa_base = { &ciaa, 0, 0, IF_PORTS, IRQ_AMIGA_AUTO_2, IRQ_AMIGA_CIAA, @@ -100,15 +99,13 @@ } /* - * Enable or disable CIA interrupts, return old interrupt mask, - * interrupts will only be enabled if a handler exists + * Enable or disable CIA interrupts, return old interrupt mask. */ static unsigned char cia_able_irq_private(struct ciabase *base, unsigned char mask) { - u_char old, tmp; - int i; + u_char old; old = base->icr_mask; base->icr_data |= base->cia->icr; @@ -118,12 +115,7 @@ else base->icr_mask &= ~mask; base->icr_mask &= CIA_ICR_ALL; - for (i = 0, tmp = 1; i < CIA_IRQS; i++, tmp <<= 1) { - if ((tmp & base->icr_mask) && !base->irq_list[i].handler) { - base->icr_mask &= ~tmp; - base->cia->icr = tmp; - } - } + if (base->icr_data & base->icr_mask) custom.intreq = IF_SETCLR | base->int_mask; return old; @@ -145,94 +137,45 @@ return cia_able_irq_private(base, mask); } -int cia_request_irq(unsigned int irq, - void (*handler)(int, void *, struct pt_regs *), - unsigned long flags, const char *devname, void *dev_id) -{ - u_char mask; - struct ciabase *base; - - CIA_SET_BASE_ADJUST_IRQ(base, irq); - - base->irq_list[irq].handler = handler; - base->irq_list[irq].flags = flags; - base->irq_list[irq].dev_id = dev_id; - base->irq_list[irq].devname = devname; - - /* enable the interrupt */ - mask = 1 << irq; - cia_set_irq_private(base, mask); - cia_able_irq_private(base, CIA_ICR_SETCLR | mask); - return 0; -} - -void cia_free_irq(unsigned int irq, void *dev_id) -{ - struct ciabase *base; - - CIA_SET_BASE_ADJUST_IRQ(base, irq); - - if (base->irq_list[irq].dev_id != dev_id) - printk("%s: removing probably wrong IRQ %i from %s\n", - __FUNCTION__, base->cia_irq + irq, - base->irq_list[irq].devname); - - base->irq_list[irq].handler = NULL; - base->irq_list[irq].flags = 0; - - cia_able_irq_private(base, 1 << irq); -} - static void cia_handler(int irq, void *dev_id, struct pt_regs *fp) { struct ciabase *base = (struct ciabase *)dev_id; - int mach_irq, i; + irq_desc_t *desc; + struct irqaction *action; + int i; unsigned char ints; - mach_irq = base->cia_irq; - irq = SYS_IRQS + mach_irq; + irq = base->cia_irq; + desc = irq_desc + irq; ints = cia_set_irq_private(base, CIA_ICR_ALL); custom.intreq = base->int_mask; - for (i = 0; i < CIA_IRQS; i++, irq++, mach_irq++) { + for (i = 0; i < CIA_IRQS; i++, irq++) { if (ints & 1) { kstat.irqs[0][irq]++; - base->irq_list[i].handler(mach_irq, base->irq_list[i].dev_id, fp); + action = desc->action; + action->handler(irq, action->dev_id, fp); } ints >>= 1; + desc++; } amiga_do_irq_list(base->server_irq, fp); } void __init cia_init_IRQ(struct ciabase *base) { - int i; - - /* init isr handlers */ - for (i = 0; i < CIA_IRQS; i++) { - base->irq_list[i].handler = NULL; - base->irq_list[i].flags = 0; - } + extern struct irqaction amiga_sys_irqaction[AUTO_IRQS]; + struct irqaction *action; /* clear any pending interrupt and turn off all interrupts */ cia_set_irq_private(base, CIA_ICR_ALL); cia_able_irq_private(base, CIA_ICR_ALL); /* install CIA handler */ - request_irq(base->handler_irq, cia_handler, 0, base->name, base); + action = &amiga_sys_irqaction[base->handler_irq-IRQ_AMIGA_AUTO]; + action->handler = cia_handler; + action->dev_id = base; + action->name = base->name; + setup_irq(base->handler_irq, &amiga_sys_irqaction[base->handler_irq-IRQ_AMIGA_AUTO]); custom.intena = IF_SETCLR | base->int_mask; -} - -int cia_get_irq_list(struct ciabase *base, struct seq_file *p) -{ - int i, j; - - j = base->cia_irq; - for (i = 0; i < CIA_IRQS; i++) { - seq_printf(p, "cia %2d: %10d ", j + i, - kstat.irqs[0][SYS_IRQS + j + i]); - seq_puts(p, " "); - seq_printf(p, "%s\n", base->irq_list[i].devname); - } - return 0; } diff -Nru a/arch/ppc/amiga/config.c b/arch/ppc/amiga/config.c --- a/arch/ppc/amiga/config.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/amiga/config.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.config.c 1.12 09/18/01 11:19:06 paulus + * BK Id: %F% %I% %G% %U% %#% */ #define m68k_debug_device debug_device @@ -28,7 +28,6 @@ #ifdef CONFIG_ZORRO #include #endif -#include #include #include @@ -92,7 +91,6 @@ extern void amiga_disable_irq (unsigned int); static void amiga_get_model(char *model); static int amiga_get_hardware_list(char *buffer); -extern int show_amiga_interrupts (struct seq_file *, void *); /* amiga specific timer functions */ static unsigned long amiga_gettimeoffset (void); static void a3000_gettod (int *, int *, int *, int *, int *, int *); @@ -412,8 +410,8 @@ mach_keyb_init = amiga_keyb_init; mach_kbdrate = amiga_kbdrate; mach_init_IRQ = amiga_init_IRQ; - mach_default_handler = &amiga_default_handler; #ifndef CONFIG_APUS + mach_default_handler = &amiga_default_handler; mach_request_irq = amiga_request_irq; mach_free_irq = amiga_free_irq; enable_irq = amiga_enable_irq; @@ -421,7 +419,6 @@ #endif mach_get_model = amiga_get_model; mach_get_hardware_list = amiga_get_hardware_list; - mach_get_irq_list = show_amiga_interrupts; mach_gettimeoffset = amiga_gettimeoffset; if (AMIGAHW_PRESENT(A3000_CLK)){ mach_gettod = a3000_gettod; diff -Nru a/arch/ppc/boot/Makefile b/arch/ppc/boot/Makefile --- a/arch/ppc/boot/Makefile Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/boot/Makefile Tue Feb 19 18:08:57 2002 @@ -17,29 +17,32 @@ AFLAGS += -D__BOOTER__ OBJCOPY_ARGS = -O elf32-powerpc -ifeq ($(CONFIG_SMP),y) -TFTPSIMAGE=/tftpboot/sImage.smp -else -TFTPSIMAGE=/tftpboot/sImage -endif +MKIMAGE := ./utils/mkimage.wrapper -lib/zlib.a: +lib/zlib.a: lib/zlib.c $(MAKE) -C lib images/vmlinux.gz: $(TOPDIR)/vmlinux $(MAKE) -C images vmlinux.gz -# Subdirs and tools needed for each. -subdir-y := lib images common -subdir-$(CONFIG_ALL_PPC) += chrp pmac prep -tools-$(CONFIG_ALL_PPC) := addnote piggyback mknote hack-coff mkprep -subdir-$(CONFIG_4xx) += tree -subdir-$(CONFIG_8xx) += mbx -subdir-$(CONFIG_8260) += mbx -tools-$(CONFIG_GEMINI) := mksimage - -# These are dirs we don't want to go into on BOOT_TARGETS -NONBOOT := lib images common +# Subdirs and tools needed for each. Assume we always need to go into +# 'simple' unless told otherwise. +subdir-y := lib common simple +subdir-$(CONFIG_ALL_PPC) := chrp pmac prep +tools-$(CONFIG_ALL_PPC) := addnote mknote hack-coff mkprep +tools-$(CONFIG_PPLUS) := mkbugboot mkprep +tools-$(CONFIG_4xx) := mktree +tools-$(CONFIG_LOPEC) := mkbugboot mkprep +tools-$(CONFIG_MCPN765) := mkbugboot mkprep +tools-$(CONFIG_MENF1) := mkprep +tools-$(CONFIG_MVME5100) := mkbugboot mkprep +tools-$(CONFIG_PRPMC750) := mkbugboot mkprep +tools-$(CONFIG_PRPMC800) := mkbugboot mkprep +tools-$(CONFIG_SPRUCE) := mktree + +# These are dirs we don't want to go into on BOOT_TARGETS. We have them for +# the 'depend' stage. +NONBOOT := lib common # These are the subdirs we want to use BOOTDIRS = $(filter-out $(NONBOOT), $(subdir-y)) @@ -50,37 +53,32 @@ $(MAKE) -C utils $(tools-y) # The targets all boards support for boot images. -BOOT_TARGETS = zImage -ifndef CONFIG_GEMINI -BOOT_TARGETS += zImage.initrd znetboot znetboot.initrd -endif +BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd -$(BOOT_TARGETS): sImage vmapus lib/zlib.a images/vmlinux.gz maketools +$(BOOT_TARGETS): vmapus lib/zlib.a images/vmlinux.gz maketools ifneq ($(BOOTDIRS),) for d in $(BOOTDIRS); do $(MAKE) -C $$d $@; done endif -sImage: $(TOPDIR)/vmlinux -ifdef CONFIG_GEMINI - $(OBJCOPY) -I elf32-powerpc -O binary $(TOPDIR)/vmlinux images/sImage -endif - vmapus: $(TOPDIR)/vmlinux ifdef CONFIG_APUS $(STRIP) $(TOPDIR)/vmlinux -o images/vmapus gzip $(GZIP_FLAGS) images/vmapus endif -ifdef CONFIG_GEMINI -znetboot : zImage - cp images/sImage $(TFTPSIMAGE) -endif +# Make an image for PPCBoot +pImage: images/vmlinux.gz + $(MKIMAGE) -A ppc -O linux -T kernel -C gzip -a 00000000 -e 00000000 \ + -n 'Linux-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)' \ + -d $< images/vmlinux.PPCBoot + ln -sf vmlinux.PPCBoot images/pImage + +vmlinux.sm: $(TOPDIR)/vmlinux utils/addSystemMap + ./utils/addSystemMap $(TOPDIR)/System.map $(TOPDIR)/vmlinux images/vmlinux.sm -# Clean up after ourselves. We have to do it like this since only some dirs -# need to be gone into. -- Tom +# These are subdirs with files not normally rm'ed. -- Tom clean: $(MAKE) -C images clean - $(MAKE) -C tree clean $(MAKE) -C utils clean include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc/boot/chrp/Makefile b/arch/ppc/boot/chrp/Makefile --- a/arch/ppc/boot/chrp/Makefile Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/boot/chrp/Makefile Tue Feb 19 18:08:58 2002 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.Makefile 1.13 07/27/01 20:24:17 trini +# BK Id: %F% %I% %G% %U% %#% # # Makefile for making ELF bootable images for booting on CHRP # using Open Firmware. @@ -7,19 +7,9 @@ # # Based on coffboot by Paul Mackerras -ifeq ($(CONFIG_PPC64BRIDGE),y) -MSIZE=.64 -AFLAGS += -Wa,-mppc64bridge -else -MSIZE= -endif - -.c.o: - $(CC) $(CFLAGS) -DKERNELBASE=$(KERNELBASE) -c -o $*.o $< -.S.o: - $(CC) $(AFLAGS) -traditional -c -o $*.o $< +USE_STANDARD_AS_RULE := true -LD_ARGS = -Ttext 0x00400000 +LD_ARGS = -T ../ld.script -Ttext 0x00400000 OBJS = ../common/crt0.o start.o main.o misc.o ../common/string.o image.o \ ../common/ofcommon.o @@ -27,23 +17,20 @@ ADDNOTE = ../utils/addnote PIGGYBACK = ../utils/piggyback +ifeq ($(CONFIG_PPC64BRIDGE),y) +END += .64 +AFLAGS += -Wa,-mppc64bridge +endif ifeq ($(CONFIG_SMP),y) -TFTPIMAGE=/tftpboot/zImage.chrp.smp$(MSIZE) -else -TFTPIMAGE=/tftpboot/zImage.chrp$(MSIZE) +END += .smp endif +TFTPIMAGE=/tftpboot/zImage.chrp$(END) + all: zImage znetboot: zImage -ifdef CONFIG_SMP - cp -f $(TOPDIR)/vmlinux /tftpboot/vmlinux.smp -else - cp -f $(TOPDIR)/vmlinux /tftpboot/vmlinux -endif -ifdef CONFIG_PPC64BRIDGE - cp -f $(TOPDIR)/vmlinux /tftpboot/vmlinux.64 -endif + cp -f $(TOPDIR)/vmlinux /tftpboot/vmlinux$(END) cp ../images/zImage.chrp $(TFTPIMAGE) znetboot.initrd: zImage.initrd @@ -52,22 +39,28 @@ floppy: zImage mcopy zImage a:zImage -image.o: $(PIGGYBACK) ../images/vmlinux.gz - $(PIGGYBACK) image < ../images/vmlinux.gz | $(AS) -o $@ - -sysmap.o: $(PIGGYBACK) $(TOPDIR)/System.map - $(PIGGYBACK) sysmap < $(TOPDIR)/System.map | $(AS) -o $@ - -initrd.o: ../images/ramdisk.image.gz $(PIGGYBACK) - $(PIGGYBACK) initrd < ../images/ramdisk.image.gz | $(AS) -o $@ +image.o: ../images/vmlinux.gz ../common/dummy.o + $(OBJCOPY) ../common/dummy.o $@ \ + --add-section=.image=../images/vmlinux.gz \ + --set-section-flags=.image=contents,alloc,load,readonly,data +ifdef CONFIG_XMON + $(OBJCOPY) $@ $@ \ + --add-section=.sysmap=$(TOPDIR)/System.map \ + --set-section-flags=.sysmap=contents,alloc,load,readonly,data +endif -zImage: $(OBJS) $(LIBS) ../common/no_initrd.o $(ADDNOTE) ../images/vmlinux.gz - $(LD) $(LD_ARGS) -o ../images/$@.chrp $(OBJS) ../common/no_initrd.o $(LIBS) +zImage: $(OBJS) $(LIBS) $(ADDNOTE) + $(LD) $(LD_ARGS) -o ../images/$@.chrp $(OBJS) $(LIBS) + $(OBJCOPY) ../images/$@.chrp ../images/$@.chrp -R .comment -R .ramdisk cp ../images/$@.chrp ../images/$@.chrp-rs6k $(ADDNOTE) ../images/$@.chrp-rs6k -zImage.initrd: $(OBJS) $(LIBS) initrd.o $(ADDNOTE) ../images/vmlinux.gz - $(LD) $(LD_ARGS) -o ../images/$@.chrp $(OBJS) initrd.o $(LIBS) +zImage.initrd: $(OBJS) $(LIBS) $(ADDNOTE) ../images/ramdisk.image.gz + $(OBJCOPY) image.o image.o \ + --add-section=.ramdisk=../images/ramdisk.image.gz \ + --set-section-flags=.ramdisk=contents,alloc,load,readonly,data + $(LD) $(LD_ARGS) -o ../images/$@.chrp $(OBJS) $(LIBS) + $(OBJCOPY) ../images/$@.chrp ../images/$@.chrp -R .comment cp ../images/$@.chrp ../images/$@.chrp-rs6k $(ADDNOTE) ../images/$@.chrp-rs6k diff -Nru a/arch/ppc/boot/chrp/main.c b/arch/ppc/boot/chrp/main.c --- a/arch/ppc/boot/chrp/main.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/boot/chrp/main.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.main.c 1.13 07/27/01 20:24:17 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Copyright (C) Paul Mackerras 1997. @@ -11,15 +11,14 @@ */ #include "nonstdio.h" #include +#include + +/* Passed from the linker */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin[], __ramdisk_end; +extern char _start, _end; -extern char _end[]; -extern char initrd_data[]; -extern char image_data[]; -extern char sysmap_data[]; extern int getprop(void *, const char *, void *, int); -extern int initrd_len; -extern int image_len; -extern int sysmap_len; extern unsigned int heap_max; extern void claim(unsigned int virt, unsigned int size, unsigned int align); extern void *finddevice(const char *); @@ -53,22 +52,27 @@ unsigned sa, len; void *dst; unsigned char *im; - unsigned initrd_start=0, initrd_size=0; - extern char _start; + unsigned int initrd_size, initrd_start; printf("chrpboot starting: loaded at 0x%p\n\r", &_start); - if (initrd_len) { - initrd_size = initrd_len; + initrd_size = (char *)(&__ramdisk_end) - (char *)(&__ramdisk_begin); + if (initrd_size) { initrd_start = (RAM_END - initrd_size) & ~0xFFF; + a1 = initrd_start; + a2 = initrd_size; claim(initrd_start, RAM_END - initrd_start, 0); printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n\r", - initrd_start, initrd_data, initrd_size); - memcpy((char *)initrd_start, initrd_data, initrd_size); + initrd_start, (char *)(&__ramdisk_begin), initrd_size); + memcpy((char *)initrd_start, (char *)(&__ramdisk_begin), initrd_size); + } else { + initrd_start = 0; + initrd_size = 0; + a2 = 0xdeadbeef; } - im = image_data; - len = image_len; + im = (char *)(&__image_begin); + len = (char *)(&__image_end) - (char *)(&__image_begin); /* claim 4MB starting at PROG_START */ claim(PROG_START, PROG_SIZE - PROG_START, 0); dst = (void *) PROG_START; diff -Nru a/arch/ppc/boot/common/Makefile b/arch/ppc/boot/common/Makefile --- a/arch/ppc/boot/common/Makefile Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/boot/common/Makefile Tue Feb 19 18:08:57 2002 @@ -8,18 +8,7 @@ # Tom Rini January 2001 # -.c.s: - $(CC) $(CFLAGS) -S -o $*.s $< -.s.o: - $(AS) -o $*.o $< -.c.o: - $(CC) $(CFLAGS) -c -o $*.o $< -.S.s: - $(CPP) $(AFLAGS) -traditional -o $*.o $< -.S.o: - $(CC) $(AFLAGS) -traditional -c -o $*.o $< - -OBJCOPY_ARGS = -O elf32-powerpc +USE_STANDARD_AS_RULE := true coffcrt0.o: $(CC) $(AFLAGS) -DXCOFF -traditional -c -o coffcrt0.o crt0.S diff -Nru a/arch/ppc/boot/common/crt0.S b/arch/ppc/boot/common/crt0.S --- a/arch/ppc/boot/common/crt0.S Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/boot/common/crt0.S Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.crt0.S 1.12 08/09/01 17:09:10 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* Copyright (c) 1997 Paul Mackerras * Initial Power Macintosh COFF version. @@ -22,7 +22,7 @@ */ #include -#include "../../kernel/ppc_asm.tmpl" +#include .text diff -Nru a/arch/ppc/boot/common/dummy.c b/arch/ppc/boot/common/dummy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/common/dummy.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,7 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +int main(void) +{ + return 0; +} diff -Nru a/arch/ppc/boot/common/misc-simple.c b/arch/ppc/boot/common/misc-simple.c --- a/arch/ppc/boot/common/misc-simple.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/boot/common/misc-simple.c Tue Feb 19 18:08:58 2002 @@ -3,10 +3,10 @@ * * Misc. bootloader code for many machines. This assumes you have are using * a 6xx/7xx/74xx CPU in your machine. This assumes the chunk of memory - * below 8MB is free. Finally, it assumes you have a NS16550-style uart for + * below 8MB is free. Finally, it assumes you have a NS16550-style uart for * your serial console. If a machine meets these requirements, it can quite * likely use this code during boot. - * + * * Author: Matt Porter * Derived from arch/ppc/boot/prep/misc.c * @@ -25,41 +25,59 @@ #include #include #include +#include #include "nonstdio.h" #include "zlib.h" -unsigned long com_port; - -char *avail_ram; -char *end_avail; -extern char _end[]; - +/* Default cmdline */ #ifdef CONFIG_CMDLINE #define CMDLINE CONFIG_CMDLINE #else #define CMDLINE "" #endif + +/* Keyboard (and VGA console)? */ +#ifdef CONFIG_VGA_CONSOLE +#define HAS_KEYB 1 +#else +#define HAS_KEYB 0 +#endif + +char *avail_ram; +char *end_avail; +char *zimage_start; char cmd_preset[] = CMDLINE; char cmd_buf[256]; char *cmd_line = cmd_buf; - -unsigned long initrd_start = 0, initrd_end = 0; -char *zimage_start; +int keyb_present = HAS_KEYB; int zimage_size; +unsigned long com_port; +unsigned long initrd_size = 0; + +/* The linker tells us various locations in the image */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin, __ramdisk_end; +extern char _end[]; +/* Original location */ +extern unsigned long start; + +extern int CRT_tstc(void); +extern unsigned long serial_init(int chan, void *ignored); +extern void serial_close(unsigned long com_port); extern void gunzip(void *, int, unsigned char *, int *); -extern unsigned long serial_init(int chan); +extern void setup_legacy(void); -void +struct bi_record * decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum) { - int timer = 0; - extern unsigned long start; char *cp, ch; + struct bi_record *rec, *birecs; - com_port = serial_init(0); + setup_legacy(); + com_port = serial_init(0, NULL); /* assume the chunk below 8M is free */ end_avail = (char *)0x00800000; @@ -69,7 +87,8 @@ * were relocated to. */ puts("loaded at: "); puthex(load_addr); - puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); + puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); + puts("\n"); if ( (unsigned long)load_addr != (unsigned long)&start ) { puts("relocated to: "); puthex((unsigned long)&start); @@ -78,49 +97,31 @@ puts("\n"); } - /* we have to subtract 0x10000 here to correct for objdump including - the size of the elf header which we strip -- Cort */ - zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET); - zimage_size = ZIMAGE_SIZE; - - if ( INITRD_OFFSET ) - initrd_start = load_addr - 0x10000 + INITRD_OFFSET; - else - initrd_start = 0; - initrd_end = INITRD_SIZE + initrd_start; + /* + * We link ourself to 0x00800000. When we run, we relocate + * ourselves there. So we just need __image_begin for the + * start. -- Tom + */ + zimage_start = (char *)(unsigned long)(&__image_begin); + zimage_size = (unsigned long)(&__image_end) - + (unsigned long)(&__image_begin); + + initrd_size = (unsigned long)(&__ramdisk_end) - + (unsigned long)(&__ramdisk_begin); /* - * Find a place to stick the zimage and initrd and - * relocate them if we have to. -- Cort + * The zImage and initrd will be between start and _end, so they've + * already been moved once. We're good to go now. -- Tom */ avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); puts("zimage at: "); puthex((unsigned long)zimage_start); - puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); - if ( (unsigned long)zimage_start <= 0x00800000 ) - { - memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size ); - zimage_start = (char *)avail_ram; - puts("relocated to: "); puthex((unsigned long)zimage_start); - puts(" "); - puthex((unsigned long)zimage_size+(unsigned long)zimage_start); - puts("\n"); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); + puts("\n"); - /* relocate initrd */ - if ( initrd_start ) - { - puts("initrd at: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); - avail_ram = (char *)PAGE_ALIGN( - (unsigned long)zimage_size+(unsigned long)zimage_start); - memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE ); - initrd_start = (unsigned long)avail_ram; - initrd_end = initrd_start + INITRD_SIZE; - puts("relocated to: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); - } - } else if ( initrd_start ) { - puts("initrd at: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); + if ( initrd_size ) { + puts("initrd at: "); + puthex((unsigned long)(&__ramdisk_begin)); + puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n"); } avail_ram = (char *)0x00400000; @@ -128,11 +129,28 @@ puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); puthex((unsigned long)end_avail); puts("\n"); + if (keyb_present) + CRT_tstc(); /* Forces keyboard to be initialized */ +#ifdef CONFIG_GEMINI + /* + * If cmd_line is empty and cmd_preset is not, copy cmd_preset + * to cmd_line. This way we can override cmd_preset with the + * command line from Smon. + */ + + if ( (cmd_line[0] == '\0') && (cmd_preset[0] != '\0')) + memcpy (cmd_line, cmd_preset, sizeof(cmd_preset)); +#endif + /* Display standard Linux/PPC boot prompt for kernel args */ puts("\nLinux/PPC load: "); cp = cmd_line; memcpy (cmd_line, cmd_preset, sizeof(cmd_preset)); while ( *cp ) putc(*cp++); + +#ifndef CONFIG_GEMINI + /* Val Henson has requested that Gemini doesn't wait for the + * user to edit the cmdline or not. */ while (timer++ < 5*1000) { if (tstc()) { while ((ch = getc()) != '\n' && ch != '\r') { @@ -158,18 +176,44 @@ udelay(1000); /* 1 msec */ } *cp = 0; +#endif puts("\n"); - /* mappings on early boot can only handle 16M */ - if ( (int)(cmd_line[0]) > (16<<20)) - puts("cmd_line located > 16M\n"); - if ( initrd_start > (16<<20)) - puts("initrd_start located > 16M\n"); - puts("Uncompressing Linux..."); - gunzip(0, 0x400000, zimage_start, &zimage_size); puts("done.\n"); + /* + * Create bi_recs for cmd_line and initrds + */ + rec = (struct bi_record *)_ALIGN((unsigned long)(zimage_size) + + (1 << 20) - 1, (1 << 20)); + birecs = rec; + + rec->tag = BI_FIRST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_CMD_LINE; + memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1); + rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1; + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + if ( initrd_size ) { + rec->tag = BI_INITRD; + rec->data[0] = (unsigned long)(&__ramdisk_begin); + rec->data[1] = initrd_size; + rec->size = sizeof(struct bi_record) + 2 * + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + + rec->size); + } + + rec->tag = BI_LAST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); puts("Now booting the kernel\n"); + serial_close(com_port); + + return birecs; } diff -Nru a/arch/ppc/boot/common/no_initrd.c b/arch/ppc/boot/common/no_initrd.c --- a/arch/ppc/boot/common/no_initrd.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,5 +0,0 @@ -/* - * BK Id: SCCS/s.no_initrd.c 1.7 05/18/01 15:17:23 cort - */ -char initrd_data[1]; -int initrd_len = 0; diff -Nru a/arch/ppc/boot/common/ns16550.c b/arch/ppc/boot/common/ns16550.c --- a/arch/ppc/boot/common/ns16550.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/boot/common/ns16550.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ns16550.c 1.9 07/30/01 17:19:40 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * COM1 NS16550 support @@ -10,6 +10,8 @@ #include #include +#define SERIAL_BAUD 9600 + extern void outb(int port, unsigned char val); extern unsigned char inb(int port); extern unsigned long ISA_io; @@ -20,8 +22,10 @@ static int shift; -unsigned long serial_init(int chan) { +unsigned long serial_init(int chan, void *ignored) +{ unsigned long com_port; + unsigned char lcr, dlm; /* We need to find out which type io we're expecting. If it's * 'SERIAL_IO_PORT', we get an offset from the isa_io_base. @@ -40,23 +44,33 @@ /* How far apart the registers are. */ shift = rs_table[chan].iomem_reg_shift; - - /* See if port is present */ - outb(com_port + (UART_LCR << shift), 0x00); - outb(com_port + (UART_IER << shift), 0x00); + + /* save the LCR */ + lcr = inb(com_port + (UART_LCR << shift)); /* Access baud rate */ outb(com_port + (UART_LCR << shift), 0x80); -#ifdef CONFIG_SERIAL_CONSOLE_NONSTD - /* Input clock. */ - outb(com_port + (UART_DLL << shift), - (BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD)); - outb(com_port + (UART_DLM << shift), - (BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD) >> 8); -#endif - /* 8 data, 1 stop, no parity */ - outb(com_port + (UART_LCR << shift), 0x03); - /* RTS/DTR */ - outb(com_port + (UART_MCR << shift), 0x03); + dlm = inb(com_port + (UART_DLM << shift)); + /* + * Test if serial port is unconfigured. + * We assume that no-one uses less than 110 baud or + * less than 7 bits per character these days. + * -- paulus. + */ + + if ((dlm <= 4) && (lcr & 2)) + /* port is configured, put the old LCR back */ + outb(com_port + (UART_LCR << shift), lcr); + else { + /* Input clock. */ + outb(com_port + (UART_DLL << shift), + (BASE_BAUD / SERIAL_BAUD)); + outb(com_port + (UART_DLM << shift), + (BASE_BAUD / SERIAL_BAUD) >> 8); + /* 8 data, 1 stop, no parity */ + outb(com_port + (UART_LCR << shift), 0x03); + /* RTS/DTR */ + outb(com_port + (UART_MCR << shift), 0x03); + } /* Clear & enable FIFOs */ outb(com_port + (UART_FCR << shift), 0x07); @@ -83,4 +97,9 @@ serial_tstc(unsigned long com_port) { return ((inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) != 0); +} + +void +serial_close(unsigned long com_port) +{ } diff -Nru a/arch/ppc/boot/common/ofcommon.c b/arch/ppc/boot/common/ofcommon.c --- a/arch/ppc/boot/common/ofcommon.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/boot/common/ofcommon.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ofcommon.c 1.1 07/27/01 20:24:18 trini + * BK Id: %F% %I% %G% %U% %#% * * Copyright (C) Paul Mackerras 1997. * @@ -14,6 +14,9 @@ #include #include +/* Information from the linker */ +extern char __sysmap_begin, __sysmap_end; + extern int strcmp(const char *s1, const char *s2); extern char *avail_ram, *avail_high; extern char *end_avail; @@ -126,15 +129,22 @@ inflateEnd(&s); } -/* Make a bi_rec in OF. We need to be passed a name for BI_BOOTLOADER_ID, +/* Make a bi_rec in OF. We need to be passed a name for BI_BOOTLOADER_ID, * a machine type for BI_MACHTYPE, and the location where the end of the * bootloader is (PROG_START + PROG_SIZE) */ void make_bi_recs(unsigned long addr, char *name, unsigned int mach, unsigned long progend) { + unsigned long sysmap_size; struct bi_record *rec; + /* FIgure out the size of a possible System.map we're going to + * pass along. + * */ + sysmap_size = (unsigned long)(&__sysmap_end) - + (unsigned long)(&__sysmap_begin); + /* leave a 1MB gap then align to the next 1MB boundary */ addr = _ALIGN(addr+ (1<<20) - 1, (1<<20)); /* oldworld machine seem very unhappy about this. -- Tom */ @@ -150,21 +160,22 @@ sprintf( (char *)rec->data, name); rec->size = sizeof(struct bi_record) + strlen(name) + 1; rec = (struct bi_record *)((unsigned long)rec + rec->size); - + rec->tag = BI_MACHTYPE; rec->data[0] = mach; rec->data[1] = 1; rec->size = sizeof(struct bi_record) + 2 * sizeof(unsigned long); rec = (struct bi_record *)((unsigned long)rec + rec->size); -#ifdef SYSMAP_OFFSET - rec->tag = BI_SYSMAP; - rec->data[0] = SYSMAP_OFFSET; - rec->data[1] = SYSMAP_SIZE; - rec->size = sizeof(struct bi_record) + 2 * sizeof(unsigned long); - rec = (struct bi_record *)((unsigned long)rec + rec->size); -#endif /* SYSMAP_OFFSET */ - + if (sysmap_size) { + rec->tag = BI_SYSMAP; + rec->data[0] = (unsigned long)(&__sysmap_begin); + rec->data[1] = sysmap_size; + rec->size = sizeof(struct bi_record) + 2 * + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + } + rec->tag = BI_LAST; rec->size = sizeof(struct bi_record); rec = (struct bi_record *)((unsigned long)rec + rec->size); diff -Nru a/arch/ppc/boot/common/relocate.S b/arch/ppc/boot/common/relocate.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/common/relocate.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,204 @@ +/* + * arch/ppc/boot/simple/relocate.S + * + * This is the common part of the loader relocation and initialization + * process. All of the board/processor specific initialization is + * done before we get here. + * + * Author: Tom Rini + * trini@mvista.com + * Derived from arch/ppc/boot/prep/head.S (Cort Dougan, many others). + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#define GETSYM(reg, sym) \ + lis reg, sym@h; ori reg, reg, sym@l + + .text + /* We get called from the early initialization code. + * Register 3 has the address where we were loaded, + * Register 4 contains any residual data passed from the + * boot rom. + */ + .globl relocate +relocate: + /* Save r3, r4 for later. + * The r8/r11 are legacy registers so I don't have to + * rewrite the code below :-). + */ + mr r8, r3 + mr r11, r4 + + /* compute the size of the whole image in words. */ + GETSYM(r4,start) + GETSYM(r5,end) + + addi r5,r5,3 /* round up */ + sub r5,r5,r4 /* end - start */ + srwi r5,r5,2 + mr r7,r5 /* Save for later use. */ + + /* + * Check if we need to relocate ourselves to the link addr or were + * we loaded there to begin with. + */ + cmp cr0,r3,r4 + beq start_ldr /* If 0, we don't need to relocate */ + + /* Move this code somewhere safe. This is max(load + size, end) + * r8 == load address + */ + GETSYM(r4, start) + GETSYM(r5, end) + + sub r6,r5,r4 + add r6,r8,r6 /* r6 == phys(load + size) */ + + cmpw r5,r6 + bgt 1f + b 2f +1: + mr r6, r5 +2: + /* dest is in r6 */ + /* Ensure alignment --- this code is precautionary */ + addi r6,r6,4 + li r5,0x0003 + andc r6,r6,r5 + + /* Find physical address and size of do_relocate */ + GETSYM(r5, __relocate_start) + GETSYM(r4, __relocate_end) + GETSYM(r3, start) + + /* Size to copy */ + sub r4,r4,r5 + srwi r4,r4,2 + + /* Src addr to copy (= __relocate_start - start + where_loaded) */ + sub r3,r5,r3 + add r5,r8,r3 + + /* Save dest */ + mr r3, r6 + + /* Do the copy */ + mtctr r4 +3: lwz r4,0(r5) + stw r4,0(r3) + addi r3,r3,4 + addi r5,r5,4 + bdnz 3b + + GETSYM(r4, __relocate_start) + GETSYM(r5, do_relocate) + + sub r4,r5,r4 /* Get entry point for do_relocate in + add r6,r6,r4 * relocated section */ + + /* This will return to the relocated do_relocate */ + mtlr r6 + b flush_instruction_cache + + .section ".relocate_code","xa" + +do_relocate: + /* We have 2 cases --- start < load, or start > load + * This determines whether we copy from the end, or the start. + * Its easier to have 2 loops than to have paramaterised + * loops. Sigh. + */ + li r6,0 /* Clear checksum */ + mtctr r7 /* Setup for a loop */ + + GETSYM(r4, start) + mr r3,r8 /* Get the load addr */ + + cmp cr0,r4,r3 /* If we need to copy from the end, do so */ + bgt do_relocate_from_end + +do_relocate_from_start: +1: lwz r5,0(r3) /* Load and decrement */ + stw r5,0(r4) /* Store and decrement */ + addi r3,r3,4 + addi r4,r4,4 + xor r6,r6,r5 /* Update checksum */ + bdnz 1b /* Are we done? */ + b do_relocate_out /* Finished */ + +do_relocate_from_end: + GETSYM(r3, end) + slwi r4,r7,2 + add r4,r8,r4 /* Get the physical end */ +1: lwzu r5,-4(r4) + stwu r5, -4(r3) + xor r6,r6,r5 + bdnz 1b + +do_relocate_out: + GETSYM(r3,start_ldr) + mtlr r3 /* Easiest way to do an absolute jump */ +/* Some boards don't boot up with the I-cache enabled. Do that + * now because the decompress runs much faster that way. + * As a side effect, we have to ensure the data cache is not enabled + * so we can access the serial I/O without trouble. + */ + b flush_instruction_cache + + .previous + +start_ldr: +/* Clear all of BSS and set up stack for C calls */ + lis r3,edata@h + ori r3,r3,edata@l + lis r4,end@h + ori r4,r4,end@l + subi r3,r3,4 + subi r4,r4,4 + li r0,0 +50: stwu r0,4(r3) + cmp cr0,r3,r4 + bne 50b +90: mr r9,r1 /* Save old stack pointer (in case it matters) */ + lis r1,.stack@h + ori r1,r1,.stack@l + addi r1,r1,4096*2 + subi r1,r1,256 + li r2,0x000F /* Mask pointer to 16-byte boundary */ + andc r1,r1,r2 + + /* + * Exec kernel loader + */ + mr r3,r8 /* Load point */ + mr r4,r7 /* Program length */ + mr r5,r6 /* Checksum */ + mr r6,r11 /* Residual data */ + bl decompress_kernel + + /* + * Make sure the kernel knows we don't have things set in + * registers. -- Tom + */ + li r4,0 + li r6,0 + + /* + * Start at the begining. + */ + li r9,0x0000 + mtlr r9 + blr + + .comm .stack,4096*2,4 diff -Nru a/arch/ppc/boot/common/util.S b/arch/ppc/boot/common/util.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/common/util.S Tue Feb 19 18:09:01 2002 @@ -0,0 +1,230 @@ +/* + * arch/ppc/boot/common/util.S + * + * Useful bootup functions, which are more easily done in asm than C. + * + * NOTE: Be very very careful about the registers you use here. + * We don't follow any ABI calling convention among the + * assembler functions that call each other, especially early + * in the initialization. Please preserve at least r3 and r4 + * for these early functions, as they often contain information + * passed from boot roms into the C decompress function. + * + * Author: Tom Rini + * trini@mvista.com + * Derived from arch/ppc/boot/prep/head.S (Cort Dougan, many others). + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include + + + .text + + .globl disable_6xx_mmu +disable_6xx_mmu: + /* Establish default MSR value, exception prefix 0xFFF. + * If necessary, this function must fix up the LR if we + * return to a different address space once the MMU is + * disabled. + */ + li r8,MSR_IP|MSR_FP + mtmsr r8 + + /* Clear BATs */ + li r8,0 + mtspr DBAT0U,r8 + mtspr DBAT0L,r8 + mtspr DBAT1U,r8 + mtspr DBAT1L,r8 + mtspr DBAT2U,r8 + mtspr DBAT2L,r8 + mtspr DBAT3U,r8 + mtspr DBAT3L,r8 + mtspr IBAT0U,r8 + mtspr IBAT0L,r8 + mtspr IBAT1U,r8 + mtspr IBAT1L,r8 + mtspr IBAT2U,r8 + mtspr IBAT2L,r8 + mtspr IBAT3U,r8 + mtspr IBAT3L,r8 + isync + sync + sync + + /* Set segment registers */ + li r8,16 /* load up segment register values */ + mtctr r8 /* for context 0 */ + lis r8,0x2000 /* Ku = 1, VSID = 0 */ + li r10,0 +3: mtsrin r8,r10 + addi r8,r8,0x111 /* increment VSID */ + addis r10,r10,0x1000 /* address of next segment */ + bdnz 3b + + .globl disable_6xx_l1cache +disable_6xx_l1cache: + /* Enable, invalidate and then disable the L1 icache/dcache. */ + li r8,0 + ori r8,r8,(HID0_ICE|HID0_DCE|HID0_ICFI|HID0_DCI) + mfspr r11,HID0 + or r11,r11,r8 + andc r10,r11,r8 + isync + mtspr HID0,r8 + sync + isync + mtspr HID0,r10 + sync + isync + blr + + .globl _setup_L2CR +_setup_L2CR: +/* + * We should be skipping this section on CPUs where this results in an + * illegal instruction. If not, please send trini@kernel.crashing.org + * the PVR of your CPU. + */ + /* Invalidate/disable L2 cache */ + sync + isync + mfspr r8,L2CR + rlwinm r8,r8,0,1,31 + oris r8,r8,0x0020 + sync + isync + mtspr L2CR,r8 + sync + isync + + /* Wait for the invalidation to complete */ +1: mfspr r8,L2CR + rlwinm. r9,r8,0,31,31 + bne 1b + + rlwinm r8,r8,0,11,9 /* Turn off L2I bit */ + sync + isync + mtspr L2CR,r8 + sync + isync + blr + + +/* + * Delay for a number of microseconds + * -- Use the BUS timer (assumes 66MHz) + */ + .globl udelay +udelay: + mfspr r4,PVR + srwi r4,r4,16 + cmpi 0,r4,1 /* 601 ? */ + bne .udelay_not_601 +00: li r0,86 /* Instructions / microsecond? */ + mtctr r0 +10: addi r0,r0,0 /* NOP */ + bdnz 10b + subic. r3,r3,1 + bne 00b + blr + +.udelay_not_601: + mulli r4,r3,1000 /* nanoseconds */ + addi r4,r4,59 + li r5,60 + divw r4,r4,r5 /* BUS ticks */ +1: mftbu r5 + mftb r6 + mftbu r7 + cmp 0,r5,r7 + bne 1b /* Get [synced] base time */ + addc r9,r6,r4 /* Compute end time */ + addze r8,r5 +2: mftbu r5 + cmp 0,r5,r8 + blt 2b + bgt 3f + mftb r6 + cmp 0,r6,r9 + blt 2b +3: blr + +.globl _put_MSR +_put_MSR: + mtmsr r3 + blr + + .section ".relocate_code","xa" +/* + * Flush and enable instruction cache + * First, flush the data cache in case it was enabled and may be + * holding instructions for copy back. + */ +_GLOBAL(flush_instruction_cache) + mflr r6 + bl flush_data_cache + +#ifdef CONFIG_8xx + lis r3, IDC_INVALL@h + mtspr IC_CST, r3 + lis r3, IDC_ENABLE@h + mtspr IC_CST, r3 + lis r3, IDC_DISABLE@h + mtspr DC_CST, r3 +#elif CONFIG_4xx + lis r3,start@h # r9 = &_start + lis r4,_etext@ha + addi r4,r4,_etext@l # r8 = &_etext +1: dcbf r0,r3 # Flush the data cache + icbi r0,r3 # Invalidate the instruction cache + addi r3,r3,0x10 # Increment by one cache line + cmplwi cr0,r3,r4 # Are we at the end yet? + blt 1b # No, keep flushing and invalidating +#else + /* Enable, invalidate and then disable the L1 icache/dcache. */ + li r3,0 + ori r3,r3,(HID0_ICE|HID0_DCE|HID0_ICFI|HID0_DCI) + mfspr r4,HID0 + or r5,r4,r3 + isync + mtspr HID0,r5 + sync + isync + ori r5,r4,HID0_ICE /* Enable cache */ + mtspr HID0,r5 + sync + isync +#endif + mtlr r6 + blr + +#define NUM_CACHE_LINES 128*8 +#define cache_flush_buffer 0x1000 + +/* + * Flush data cache + * Do this by just reading lots of stuff into the cache. + */ +_GLOBAL(flush_data_cache) + lis r3,cache_flush_buffer@h + ori r3,r3,cache_flush_buffer@l + li r4,NUM_CACHE_LINES + mtctr r4 +00: lwz r4,0(r3) + addi r3,r3,L1_CACHE_BYTES /* Next line, please */ + bdnz 00b +10: blr + + .previous + diff -Nru a/arch/ppc/boot/images/Makefile b/arch/ppc/boot/images/Makefile --- a/arch/ppc/boot/images/Makefile Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/boot/images/Makefile Tue Feb 19 18:08:57 2002 @@ -5,8 +5,8 @@ include $(TOPDIR)/Rules.make vmlinux.gz: $(TOPDIR)/vmlinux - $(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux + $(OBJCOPY) --strip-all -S -O binary $(TOPDIR)/vmlinux vmlinux gzip -vf9 vmlinux clean: - rm -f sImage vmapus vmlinux.* miboot.image* zImage* zvmlinux.* + rm -f sImage vmapus vmlinux* miboot* zImage* zvmlinux* diff -Nru a/arch/ppc/boot/ld.script b/arch/ppc/boot/ld.script --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/ld.script Tue Feb 19 18:09:00 2002 @@ -0,0 +1,81 @@ +OUTPUT_ARCH(powerpc) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .plt : { *(.plt) } + .text : + { + *(.text) + *(.fixup) + __relocate_start = .; + *(.relocate_code) + __relocate_end = .; + } + _etext = .; + PROVIDE (etext = .); + + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + *(.rodata) + *(.rodata.*) + *(.rodata1) + *(.got1) + __image_begin = .; + *(.image) + __image_end = .; + . = ALIGN(4096); + __ramdisk_begin = .; + *(.ramdisk) + __ramdisk_end = .; + . = ALIGN(4096); + __sysmap_begin = .; + *(.sysmap) + __sysmap_end = .; + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); +} diff -Nru a/arch/ppc/boot/lib/zlib.c b/arch/ppc/boot/lib/zlib.c --- a/arch/ppc/boot/lib/zlib.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/boot/lib/zlib.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.zlib.c 1.8 05/18/01 15:17:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * This file is derived from various .h and .c files from the zlib-0.95 @@ -651,11 +651,6 @@ /* load local pointers */ #define LOAD {LOADIN LOADOUT} -/* - * The IBM 150 firmware munges the data right after _etext[]. This - * protects it. -- Cort - */ -local uInt protect_mask[] = {0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0}; /* And'ing with mask[n] masks the lower n bits */ local uInt inflate_mask[] = { 0x0000, diff -Nru a/arch/ppc/boot/mbx/Makefile b/arch/ppc/boot/mbx/Makefile --- a/arch/ppc/boot/mbx/Makefile Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,135 +0,0 @@ -# BK Id: SCCS/s.Makefile 1.7 06/05/01 20:20:05 paulus -# -# -# arch/ppc/mbxboot/Makefile -# -# 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 archive -# for more details. -# -# Copyright (C) 1994 by Linus Torvalds -# Adapted for PowerPC by Gary Thomas -# modified by Cort (cort@cs.nmt.edu) -# -.c.s: - $(CC) $(CFLAGS) -S -o $*.s $< -.s.o: - $(AS) -o $*.o $< -.c.o: - $(CC) $(CFLAGS) -c -o $*.o $< -.S.s: - $(CPP) $(AFLAGS) -traditional -o $*.o $< -.S.o: - $(CC) $(AFLAGS) -traditional -c -o $*.o $< - -TFTPIMAGE := /tftpboot/zImage.embedded - -OFFSET := ../utils/offset -SIZE := ../utils/size - -LIBS := ../lib/zlib.a -OBJCOPY_ARGS := -O elf32-powerpc - -ifdef CONFIG_8xx -ZLINKFLAGS := -T $(TOPDIR)/arch/$(ARCH)/vmlinux.lds -Ttext 0x00180000 -OBJECTS := head.o m8xx_tty.o -CFLAGS += -DCONFIG_8xx -endif - -ifdef CONFIG_8260 -ZLINKFLAGS := -T $(TOPDIR)/arch/$(ARCH)/vmlinux.lds -Ttext 0x00400000 -OBJECTS := head_8260.o m8260_tty.o embed_config.o -CFLAGS += -DCONFIG_8260 -endif - -OBJECTS += ../common/misc-common.o misc.o ../common/string.o -OBJCOPY_ARGS = -O elf32-powerpc - -ifeq ($(CONFIG_MBX),y) -OBJECTS += iic.o embed_config.o pci.o qspan_pci.o -CFLAGS += -DCONFIG_MBX -endif -ifeq ($(CONFIG_RPXLITE),y) -CFLAGS += -DCONFIG_RPXLITE -OBJECTS += iic.o embed_config.o -endif -ifeq ($(CONFIG_RPXCLASSIC),y) -CFLAGS += -DCONFIG_RPXCLASSIC -OBJECTS += iic.o embed_config.o pci.o qspan_pci.o -endif -ifeq ($(CONFIG_BSEIP),y) -CFLAGS += -DCONFIG_BSEIP -OBJECTS += iic.o embed_config.o -endif -ifeq ($(CONFIG_FADS),y) -CFLAGS += -DCONFIG_FADS -OBJECTS += embed_config.o -endif - -all: zImage - -misc.o: misc.c - $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \ - -DZIMAGE_SIZE=0 -c -o $@ $*.c - -zvmlinux.initrd: $(OBJECTS) $(LIBS) ../images/vmlinux.gz - $(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=../images/ramdisk.image.gz \ - --add-section=image=../images/vmlinux.gz \ - $@.tmp $@ - $(CC) $(CFLAGS) -DINITRD_OFFSET=`sh $(OFFSET) $(OBJDUMP) $@ initrd` \ - -DINITRD_SIZE=`sh $(SIZE) $(OBJDUMP) $@ initrd` \ - -DZIMAGE_OFFSET=`sh $(OFFSET) $(OBJDUMP) $@ image` \ - -DZIMAGE_SIZE=`sh $(SIZE) $(OBJDUMP) $@ image` \ - -c -o misc.o misc.c - $(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=../images/ramdisk.image.gz \ - --add-section=image=../images/vmlinux.gz \ - $@.tmp ../images/$@.embedded - -zImage: zvmlinux -ifeq ($(CONFIG_RPXCLASSIC),y) - dd if=../images/zvmlinux.embedded of=../images/zImage.embedded bs=65536 skip=1 -else - ln -sf ../images/zvmlinux.embedded ../images/zImage.embedded -endif - -zImage.initrd: zvmlinux.initrd -ifeq ($(CONFIG_RPXCLASSIC),y) - dd if=../images/zvmlinux.initrd.embedded of=../images/zImage.initrd.embedded bs=65536 skip=1 -else - ln -sf ../images/zvmlinux.initrd.embedded ../images/zImage.initrd.embedded -endif - -zvmlinux: $(OBJECTS) $(LIBS) ../images/vmlinux.gz -# -# build the boot loader image and then compute the offset into it -# for the kernel image -# - $(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=image=../images/vmlinux.gz \ - $@.tmp $@ -# -# then with the offset rebuild the bootloader so we know where the kernel is -# - $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ - -DZIMAGE_OFFSET=`sh $(OFFSET) $(OBJDUMP) $@ image` \ - -DZIMAGE_SIZE=`sh $(SIZE) $(OBJDUMP) $@ image` \ - -c -o misc.o misc.c - $(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=image=../images/vmlinux.gz \ - $@.tmp ../images/$@.embedded -# Remove zvmlinux and zvmlinux.temp, we have ../images/zvmlinux.embedded - rm -f $@.tmp $@ - -znetboot : zImage - cp ../images/zImage.embedded $(TFTPIMAGE) - -znetboot.initrd : zImage.initrd - cp ../images/zImage.initrd.embedded $(TFTPIMAGE) - -include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc/boot/mbx/embed_config.c b/arch/ppc/boot/mbx/embed_config.c --- a/arch/ppc/boot/mbx/embed_config.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,593 +0,0 @@ -/* - * BK Id: SCCS/s.embed_config.c 1.7 05/18/01 07:54:04 patch - */ - -/* Board specific functions for those embedded 8xx boards that do - * not have boot monitor support for board information. - */ -#include -#include -#ifdef CONFIG_8xx -#include -#endif -#ifdef CONFIG_8260 -#include -#include -#endif - - -/* IIC functions. - * These are just the basic master read/write operations so we can - * examine serial EEPROM. - */ -extern void iic_read(uint devaddr, u_char *buf, uint offset, uint count); -static u_char aschex_to_byte(u_char *cp); - -/* Supply a default Ethernet address for those eval boards that don't - * ship with one. This is an address from the MBX board I have, so - * it is unlikely you will find it on your network. - */ -static ushort def_enet_addr[] = { 0x0800, 0x3e26, 0x1559 }; - -#if defined(CONFIG_MBX) - -/* The MBX hands us a pretty much ready to go board descriptor. This - * is where the idea started in the first place. - */ -void -embed_config(bd_t *bd) -{ - u_char *mp; - u_char eebuf[128]; - int i; - - /* Read the first 128 bytes of the EEPROM. There is more, - * but this is all we need. - */ - iic_read(0xa4, eebuf, 0, 128); - - /* All we are looking for is the Ethernet MAC address. The - * first 8 bytes are 'MOTOROLA', so check for part of that. - * If it's there, assume we have a valid MAC address. If not, - * grab our default one. - */ - if ((*(uint *)eebuf) == 0x4d4f544f) - mp = &eebuf[0x4c]; - else - mp = (u_char *)def_enet_addr; - - for (i=0; i<6; i++) { - bd->bi_enetaddr[i] = *mp++; - } - - /* The boot rom passes these to us in MHz. Linux now expects - * them to be in Hz. - */ - bd->bi_intfreq *= 1000000; - bd->bi_busfreq *= 1000000; - - /* Stuff a baud rate here as well. - */ - bd->bi_baudrate = 9600; -} -#endif /* CONFIG_MBX */ - -#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) || defined(CONFIG_RPX6) - -/* Helper functions for Embedded Planet boards. -*/ -static void -rpx_eth(bd_t *bd, u_char *cp) -{ - int i; - - for (i=0; i<6; i++) { - bd->bi_enetaddr[i] = aschex_to_byte(cp); - cp += 2; - } -} - -static uint -rpx_baseten(u_char *cp) -{ - uint retval; - - retval = 0; - - while (*cp != '\n') { - retval *= 10; - retval += (*cp) - '0'; - cp++; - } - return(retval); -} - -static void -rpx_brate(bd_t *bd, u_char *cp) -{ - uint rate; - - rate = 0; - - while (*cp != '\n') { - rate *= 10; - rate += (*cp) - '0'; - cp++; - } - - bd->bi_baudrate = rate * 100; -} - -static void -rpx_memsize(bd_t *bd, u_char *cp) -{ - uint size; - - size = 0; - - while (*cp != '\n') { - size *= 10; - size += (*cp) - '0'; - cp++; - } - - bd->bi_memsize = size * 1024 * 1024; -} - -static void -rpx_cpuspeed(bd_t *bd, u_char *cp) -{ - uint num, den; - - num = den = 0; - - while (*cp != '\n') { - num *= 10; - num += (*cp) - '0'; - cp++; - if (*cp == '/') { - cp++; - den = (*cp) - '0'; - break; - } - } - - /* I don't know why the RPX just can't state the actual - * CPU speed..... - */ - if (den) { - num /= den; - num *= den; - } - bd->bi_intfreq = bd->bi_busfreq = num * 1000000; - - /* The 8xx can only run a maximum 50 MHz bus speed (until - * Motorola changes this :-). Greater than 50 MHz parts - * run internal/2 for bus speed. - */ - if (num > 50) - bd->bi_busfreq /= 2; -} - -/* Because I didn't find anything that would do this....... -*/ -u_char -aschex_to_byte(u_char *cp) -{ - u_char byte, c; - - c = *cp++; - - if ((c >= 'A') && (c <= 'F')) { - c -= 'A'; - c += 10; - } - else if ((c >= 'a') && (c <= 'f')) { - c -= 'a'; - c += 10; - } - else { - c -= '0'; - } - - byte = c * 16; - - c = *cp; - - if ((c >= 'A') && (c <= 'F')) { - c -= 'A'; - c += 10; - } - else if ((c >= 'a') && (c <= 'f')) { - c -= 'a'; - c += 10; - } - else { - c -= '0'; - } - - byte += c; - - return(byte); -} -#endif - -#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) - -/* Read the EEPROM on the RPX-Lite board. -*/ -void -embed_config(bd_t *bd) -{ - u_char eebuf[256], *cp; - - /* Read the first 256 bytes of the EEPROM. I think this - * is really all there is, and I hope if it gets bigger the - * info we want is still up front. - */ -#if 1 - iic_read(0xa8, eebuf, 0, 128); - iic_read(0xa8, &eebuf[128], 128, 128); - - /* We look for two things, the Ethernet address and the - * serial baud rate. The records are separated by - * newlines. - */ - cp = eebuf; - for (;;) { - if (*cp == 'E') { - cp++; - if (*cp == 'A') { - cp += 2; - rpx_eth(bd, cp); - } - } - if (*cp == 'S') { - cp++; - if (*cp == 'B') { - cp += 2; - rpx_brate(bd, cp); - } - } - if (*cp == 'D') { - cp++; - if (*cp == '1') { - cp += 2; - rpx_memsize(bd, cp); - } - } - if (*cp == 'H') { - cp++; - if (*cp == 'Z') { - cp += 2; - rpx_cpuspeed(bd, cp); - } - } - - /* Scan to the end of the record. - */ - while ((*cp != '\n') && (*cp != 0xff)) - cp++; - - /* If the next character is a 0 or ff, we are done. - */ - cp++; - if ((*cp == 0) || (*cp == 0xff)) - break; - } - bd->bi_memstart = 0; -#else - /* For boards without initialized EEPROM. - */ - bd->bi_memstart = 0; - bd->bi_memsize = (8 * 1024 * 1024); - bd->bi_intfreq = 48000000; - bd->bi_busfreq = 48000000; - bd->bi_baudrate = 9600; -#endif -} -#endif /* RPXLITE || RPXCLASSIC */ - -#ifdef CONFIG_BSEIP -/* Build a board information structure for the BSE ip-Engine. - * There is more to come since we will add some environment - * variables and a function to read them. - */ -void -embed_config(bd_t *bd) -{ - u_char *cp; - int i; - - /* Baud rate and processor speed will eventually come - * from the environment variables. - */ - bd->bi_baudrate = 9600; - - /* Get the Ethernet station address from the Flash ROM. - */ - cp = (u_char *)0xfe003ffa; - for (i=0; i<6; i++) { - bd->bi_enetaddr[i] = *cp++; - } - - /* The rest of this should come from the environment as well. - */ - bd->bi_memstart = 0; - bd->bi_memsize = (16 * 1024 * 1024); - bd->bi_intfreq = 48000000; - bd->bi_busfreq = 48000000; -} -#endif /* BSEIP */ - -#ifdef CONFIG_FADS -/* Build a board information structure for the FADS. - */ -void -embed_config(bd_t *bd) -{ - u_char *cp; - int i; - - /* Just fill in some known values. - */ - bd->bi_baudrate = 9600; - - /* Use default enet. - */ - cp = (u_char *)def_enet_addr; - for (i=0; i<6; i++) { - bd->bi_enetaddr[i] = *cp++; - } - - bd->bi_memstart = 0; - bd->bi_memsize = (8 * 1024 * 1024); - bd->bi_intfreq = 40000000; - bd->bi_busfreq = 40000000; -} -#endif /* FADS */ - -#ifdef CONFIG_8260 -/* Compute 8260 clock values if the rom doesn't provide them. - * We can't compute the internal core frequency (I don't know how to - * do that). - */ -static void -clk_8260(bd_t *bd) -{ - uint scmr, vco_out, clkin; - uint plldf, pllmf, busdf, brgdf, cpmdf; - volatile immap_t *ip; - - ip = (immap_t *)IMAP_ADDR; - scmr = ip->im_clkrst.car_scmr; - - /* The clkin is always bus frequency. - */ - clkin = bd->bi_busfreq; - - /* Collect the bits from the scmr. - */ - plldf = (scmr >> 12) & 1; - pllmf = scmr & 0xfff; - cpmdf = (scmr >> 16) & 0x0f; - busdf = (scmr >> 20) & 0x0f; - - /* This is arithmetic from the 8260 manual. - */ - vco_out = clkin / (plldf + 1); - vco_out *= 2 * (pllmf + 1); - bd->bi_vco = vco_out; /* Save for later */ - - bd->bi_cpmfreq = vco_out / 2; /* CPM Freq, in MHz */ - - /* Set Baud rate divisor. The power up default is divide by 16, - * but we set it again here in case it was changed. - */ - ip->im_clkrst.car_sccr = 1; /* DIV 16 BRG */ - bd->bi_brgfreq = vco_out / 16; -} -#endif - -#ifdef CONFIG_EST8260 -void -embed_config(bd_t **bdp) -{ - u_char *cp; - int i; - bd_t *bd; - - bd = *bdp; -#if 0 - /* This is actually provided by my boot rom. I have it - * here for those people that may load the kernel with - * a JTAG/COP tool and not the rom monitor. - */ - bd->bi_baudrate = 115200; - bd->bi_intfreq = 200000000; - bd->bi_busfreq = 66666666; - bd->bi_cpmfreq = 66666666; - bd->bi_brgfreq = 33333333; - bd->bi_memsize = 16 * 1024 * 1024; -#else - /* The boot rom passes these to us in MHz. Linux now expects - * them to be in Hz. - */ - bd->bi_intfreq *= 1000000; - bd->bi_busfreq *= 1000000; - bd->bi_cpmfreq *= 1000000; - bd->bi_brgfreq *= 1000000; -#endif - - cp = (u_char *)def_enet_addr; - for (i=0; i<6; i++) { - bd->bi_enetaddr[i] = *cp++; - } -} -#endif /* EST8260 */ - -#ifdef CONFIG_SBS8260 -/* We have to fill in everything. -*/ -static bd_t bdinfo; - -void -embed_config(bd_t **bdp) -{ - u_char *cp; - int i; - bd_t *bd; - - /* This should provided by the boot rom. - */ - bd = &bdinfo; - *bdp = bd; - bd->bi_baudrate = 9600; - bd->bi_memsize = 64 * 1024 * 1024; - - /* Set all of the clocks. We have to know the speed of the - * external clock. The development board had 66 MHz. - */ - bd->bi_busfreq = 66666666; - clk_8260(bd); - - /* I don't know how to compute this yet. - */ - bd->bi_intfreq = 133000000; - - - cp = (u_char *)def_enet_addr; - for (i=0; i<6; i++) { - bd->bi_enetaddr[i] = *cp++; - } -} -#endif /* SBS8260 */ - -#ifdef CONFIG_RPX6 -/* The pointer we are given is for the string of key values. - */ -static bd_t bdinfo; - -void -embed_config(bd_t **bdp) -{ - u_char *cp, *keyvals; - int i; - bd_t *bd; - - keyvals = (u_char *)*bdp; - - bd = &bdinfo; - *bdp = bd; - - /* This is almost identical to the RPX-Lite/Classic functions - * on the 8xx boards. It would be nice to have a key lookup - * function in a string, but the format of all of the fields - * is slightly different. - */ - cp = keyvals; - for (;;) { - if (*cp == 'E') { - cp++; - if (*cp == 'A') { - cp += 2; - rpx_eth(bd, cp); - } - } - if (*cp == 'S') { - cp++; - if (*cp == 'B') { - cp += 2; - bd->bi_baudrate = rpx_baseten(cp); - } - } - if (*cp == 'D') { - cp++; - if (*cp == '1') { - cp += 2; - bd->bi_memsize = rpx_baseten(cp) * 1024 * 1024; - } - } - if (*cp == 'X') { - cp++; - if (*cp == 'T') { - cp += 2; - bd->bi_busfreq = rpx_baseten(cp); - } - } - if (*cp == 'N') { - cp++; - if (*cp == 'V') { - cp += 2; - bd->bi_nvsize = rpx_baseten(cp) * 1024 * 1024; - } - } - - /* Scan to the end of the record. - */ - while ((*cp != '\n') && (*cp != 0xff)) - cp++; - - /* If the next character is a 0 or ff, we are done. - */ - cp++; - if ((*cp == 0) || (*cp == 0xff)) - break; - } - bd->bi_memstart = 0; - - /* The memory size includes both the 60x and local bus DRAM. - * I don't want to use the local bus DRAM for real memory, - * so subtract it out. It would be nice if they were separate - * keys. - */ - bd->bi_memsize -= 32 * 1024 * 1024; - - /* Set all of the clocks. We have to know the speed of the - * external clock. - */ - clk_8260(bd); - - /* I don't know how to compute this yet. - */ - bd->bi_intfreq = 200000000; -} -#endif /* RPX6 for testing */ - -#ifdef CONFIG_ADS8260 -/* We have to fill in everything. -*/ -static bd_t bdinfo; - -void -embed_config(bd_t **bdp) -{ - u_char *cp; - int i; - bd_t *bd; - - /* This should provided by the boot rom. - */ - bd = &bdinfo; - *bdp = bd; - bd->bi_baudrate = 9600; - bd->bi_memsize = 16 * 1024 * 1024; - - /* Set all of the clocks. We have to know the speed of the - * external clock. The development board had 66 MHz. - */ - bd->bi_busfreq = 66666666; - clk_8260(bd); - - /* I don't know how to compute this yet. - */ - bd->bi_intfreq = 200000000; - - - cp = (u_char *)def_enet_addr; - for (i=0; i<6; i++) { - bd->bi_enetaddr[i] = *cp++; - } -} -#endif /* ADS8260 */ - diff -Nru a/arch/ppc/boot/mbx/gzimage.c b/arch/ppc/boot/mbx/gzimage.c --- a/arch/ppc/boot/mbx/gzimage.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -/* - * BK Id: SCCS/s.gzimage.c 1.6 05/18/01 15:17:06 cort - */ -/* - * gzimage.c - * - * Dummy file to allow a compressed zImage to be added - * into a linker section, accessed by the boot coode - */ - -char dummy_for_gzimage; diff -Nru a/arch/ppc/boot/mbx/head.S b/arch/ppc/boot/mbx/head.S --- a/arch/ppc/boot/mbx/head.S Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,243 +0,0 @@ -/* - * BK Id: SCCS/s.head.S 1.9 05/18/01 07:54:04 patch - */ -#include -#include "../../kernel/ppc_defs.h" -#include "../../kernel/ppc_asm.tmpl" -#include -#include - - .text - -/* - * This code is loaded by the ROM loader at some arbitrary location. - * Move it to high memory so that it can load the kernel at 0x0000. - * - * This is a three step process that will also work when booting from - * a Flash PROM normally located in high memory. - * - * First, the entire image is loaded into some high memory address. - * This is usually at or above 0x02000000. This is done by a network - * boot function supported by the board or a debugger over BDM port. - * - * Second, the start up function here will relocate the decompress - * function to run at the link address of 0x01000000. - * - * Last, the decompression function will reloate the initrd, zImage, and - * the residual data to locations under 8 Meg. This is necessary because - * the embedded kernel start up uses 8 Meg translations to access physical - * space before the MMU is enabled. Finally, the zImage is uncompressed - * to location 0 and we jump to it. - * - * On the MBX, - * R1 - Stack pointer at a high memory address. - * R3 - Pointer to Board Information Block. - * R4 - Pointer to argument string. - * Interrupts masked, cache and MMU disabled. - * - * ...and the first and second functions listed above are - * done for us (it knows ELF images). - * - * For other embedded boards we build the Board Information Block. - */ - - .globl start -start: - bl start_ -start_: -#ifndef CONFIG_MBX - lis r11, local_bd_info@h - ori r11, r11, local_bd_info@l -#else - mr r11, r3 -#endif - - mfmsr r3 /* Turn off interrupts */ - li r4,0 - ori r4,r4,MSR_EE - andc r3,r3,r4 - mtmsr r3 - - li r4,0 /* Zero DER to prevent FRZ */ - mtspr SPRN_DER,r4 - -/* check if we need to relocate ourselves to the link addr or were we - loaded there to begin with -- Cort */ - lis r4,start@h - ori r4,r4,start@l - mflr r3 - subi r3,r3,4 /* we get the nip, not the ip of the branch */ - mr r8,r3 -#if 0 - cmp 0,r3,r4 - beq start_ldr /* Branch if loaded OK */ -#endif - -/* - * no matter where we're loaded, move ourselves to -Ttext address - * This computes the sizes we need to determine other things. - */ - lis r5,end@h - ori r5,r5,end@l - addi r5,r5,3 /* Round up - just in case */ - sub r5,r5,r4 /* Compute # longwords to move */ - srwi r5,r5,2 - mtctr r5 - mr r7,r5 - li r6,0 - subi r3,r3,4 /* Set up for loop */ - subi r4,r4,4 -00: lwzu r5,4(r3) - stwu r5,4(r4) - xor r6,r6,r5 - bdnz 00b - - lis r3,start_ldr@h - ori r3,r3,start_ldr@l - mtlr r3 /* Easiest way to do an absolute jump */ - blr - -start_ldr: -/* Most 8xx boards don't boot up with the I-cache enabled. Do that - * now because the decompress runs much faster that way. - */ - lis r3, IDC_INVALL@h - mtspr IC_CST, r3 - lis r3, IDC_ENABLE@h - mtspr IC_CST, r3 - -/* Clear all of BSS */ - lis r3,edata@h - ori r3,r3,edata@l - lis r4,end@h - ori r4,r4,end@l - subi r3,r3,4 - subi r4,r4,4 - li r0,0 -50: stwu r0,4(r3) - cmp 0,r3,r4 - bne 50b - - lis r1,.stack@h - ori r1,r1,.stack@l - addi r1,r1,4096*2 - subi r1,r1,256 - li r2,0x000F /* Mask pointer to 16-byte boundary */ - andc r1,r1,r2 - - /* Perform configuration of the various boards. This is done - * by reading some configuration data from EEPROM and building - * the board information structure. - */ - mr r3, r11 - mr r21, r11 - mr r22, r8 - mr r23, r7 - mr r24, r6 - - bl embed_config - mr r3, r21 - bl serial_init /* Init MBX serial port */ - - mr r11, r21 - mr r8, r22 - mr r7, r23 - mr r6, r24 - -#ifdef CONFIG_MBX - /* On the MBX (or anything that will TFTP load an ELF image), - * we have to find the intermediate address. The ELF loader - * only moves the Linux boostrap/decompress, not the zImage. - */ -#define ILAP_ADDRESS 0xfa000020 - lis r8, ILAP_ADDRESS@h - lwz r8, ILAP_ADDRESS@l(r8) - addis r8, r8, 1 /* Add 64K */ -#endif - - mr r3,r8 /* Load point */ - mr r4,r7 /* Program length */ - mr r5,r6 /* Checksum */ - mr r6,r11 /* Residual data */ - bl decompress_kernel - - /* changed to use r3 (as firmware does) for kernel - as ptr to residual -- Cort*/ - lis r6,cmd_line@h - ori r6,r6,cmd_line@l - lwz r6, 0(r6) - subi r7,r6,1 -00: lbzu r2,1(r7) - cmpi 0,r2,0 - bne 00b - - /* r4,r5 have initrd_start, size */ - lis r2,initrd_start@h - ori r2,r2,initrd_start@l - lwz r4,0(r2) - lis r2,initrd_end@h - ori r2,r2,initrd_end@l - lwz r5,0(r2) - - /* The world starts from the beginning. - */ - li r9,0x0 - mtlr r9 - - /* Invalidate the instruction cache because we just copied a - * bunch of kernel instructions. - */ - lis r9, IDC_INVALL@h - mtspr IC_CST, r9 - - blr -hang: - b hang - -/* - * Delay for a number of microseconds - * -- Use the BUS timer (assumes 66MHz) - */ - .globl udelay -udelay: - mulli r4,r3,1000 /* nanoseconds */ - addi r4,r4,59 - li r5,60 - divw r4,r4,r5 /* BUS ticks */ -1: mftbu r5 - mftb r6 - mftbu r7 - cmp 0,r5,r7 - bne 1b /* Get [synced] base time */ - addc r9,r6,r4 /* Compute end time */ - addze r8,r5 -2: mftbu r5 - cmp 0,r5,r8 - blt 2b - bgt 3f - mftb r6 - cmp 0,r6,r9 - blt 2b -3: blr - -.globl _get_MSR -_get_MSR: - mfmsr r3 - blr - -.globl _put_MSR -_put_MSR: - mtmsr r3 - blr - - .comm .stack,4096*2,4 -#ifndef CONFIG_MBX -local_bd_info: - .long 0 - .long 0x01000000 - .long 64 - .long 64 - .long 0 - .long 0 - .long 0 -#endif diff -Nru a/arch/ppc/boot/mbx/head_8260.S b/arch/ppc/boot/mbx/head_8260.S --- a/arch/ppc/boot/mbx/head_8260.S Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,259 +0,0 @@ -/* - * BK Id: SCCS/s.head_8260.S 1.8 05/18/01 07:54:04 patch - */ -#include "../../kernel/ppc_defs.h" -#include "../../kernel/ppc_asm.tmpl" -#include -#include - - .text - -/* - * Boot loader philosophy: - * - * ROM loads us to some arbitrary location - * ROM loads these registers: - * - * R3 = Pointer to the board configuration data - * R5 = Pointer to Open Firmware data - * - * ROM jumps to start/start_ - * Move the boot code to the link address (4 MB) - * Call decompress_kernel() - * Relocate the initrd, zimage and residual data to 4 MB - * Decompress the kernel to 0 - * Jump to the kernel entry - * -- Cort - */ - .globl start -start: - bl start_ -start_: - mr r11,r3 /* Save pointer to residual/board data */ - mr r25,r5 /* Save OFW pointer */ - li r3,MSR_IP /* Establish default MSR value */ - mtmsr r3 - -/* check if we need to relocate ourselves to the link addr or were we - loaded there to begin with -- Cort */ - lis r4,start@h - ori r4,r4,start@l - mflr r3 - subi r3,r3,4 /* we get the nip, not the ip of the branch */ - mr r8,r3 - cmp 0,r3,r4 - bne 1010f -/* compute size of whole image in words. this should be moved to - * start_ldr() -- Cort - */ - lis r4,start@h - ori r4,r4,start@l - lis r5,end@h - ori r5,r5,end@l - addi r5,r5,3 /* round up */ - sub r5,r5,r4 - srwi r5,r5,2 - mr r7,r5 - b start_ldr -1010: -/* - * no matter where we're loaded, move ourselves to -Ttext address - */ -relocate: - mflr r3 /* Compute code bias */ - subi r3,r3,4 - mr r8,r3 - lis r4,start@h - ori r4,r4,start@l - lis r5,end@h - ori r5,r5,end@l - addi r5,r5,3 /* Round up - just in case */ - sub r5,r5,r4 /* Compute # longwords to move */ - srwi r5,r5,2 - mtctr r5 - mr r7,r5 - li r6,0 - subi r3,r3,4 /* Set up for loop */ - subi r4,r4,4 -00: lwzu r5,4(r3) - stwu r5,4(r4) - xor r6,r6,r5 - bdnz 00b - lis r3,start_ldr@h - ori r3,r3,start_ldr@l - mtlr r3 /* Easiest way to do an absolute jump */ - blr -start_ldr: -/* Clear all of BSS */ - lis r3,edata@h - ori r3,r3,edata@l - lis r4,end@h - ori r4,r4,end@l - subi r3,r3,4 - subi r4,r4,4 - li r0,0 -50: stwu r0,4(r3) - cmp 0,r3,r4 - bne 50b -90: mr r9,r1 /* Save old stack pointer (in case it matters) */ - lis r1,.stack@h - ori r1,r1,.stack@l - addi r1,r1,4096*2 - subi r1,r1,256 - li r2,0x000F /* Mask pointer to 16-byte boundary */ - andc r1,r1,r2 - - /* Speed us up a little. - */ - bl flush_instruction_cache - -/* Run loader */ - mr r3,r8 /* Load point */ - mr r4,r7 /* Program length */ - mr r5,r6 /* Checksum */ - mr r6,r11 /* Residual data */ - mr r7,r25 /* OFW interfaces */ - bl decompress_kernel - - /* changed to use r3 (as firmware does) for kernel - as ptr to residual -- Cort*/ - lis r6,cmd_line@h - ori r6,r6,cmd_line@l - lwz r6, 0(r6) - subi r7,r6,1 -00: lbzu r2,1(r7) - cmpi 0,r2,0 - bne 00b - - /* r4,r5 have initrd_start, size */ - lis r2,initrd_start@h - ori r2,r2,initrd_start@l - lwz r4,0(r2) - lis r2,initrd_end@h - ori r2,r2,initrd_end@l - lwz r5,0(r2) - - /* tell kernel we're prep */ - /* - * get start address of kernel code which is stored as a coff - * entry. see boot/head.S -- Cort - */ - li r9,0x4 - mtlr r9 - lis r10,0xdeadc0de@h - ori r10,r10,0xdeadc0de@l - li r9,0 - stw r10,0(r9) -/* - * The Radstone firmware maps PCI memory at 0xc0000000 using BAT2 - * so disable BATs before setting this to avoid a clash - */ - li r8,0 - mtspr DBAT0U,r8 - mtspr DBAT1U,r8 - mtspr DBAT2U,r8 - mtspr DBAT3U,r8 - mtspr IBAT0U,r8 - mtspr IBAT1U,r8 - mtspr IBAT2U,r8 - mtspr IBAT3U,r8 - - blr -hang: - b hang - -/* - * Delay for a number of microseconds - * -- Use the BUS timer (assumes 66MHz) - */ - .globl udelay -udelay: - mfspr r4,PVR - srwi r4,r4,16 - cmpi 0,r4,1 /* 601 ? */ - bne .udelay_not_601 -00: li r0,86 /* Instructions / microsecond? */ - mtctr r0 -10: addi r0,r0,0 /* NOP */ - bdnz 10b - subic. r3,r3,1 - bne 00b - blr - -.udelay_not_601: - mulli r4,r3,1000 /* nanoseconds */ - addi r4,r4,59 - li r5,60 - divw r4,r4,r5 /* BUS ticks */ -1: mftbu r5 - mftb r6 - mftbu r7 - cmp 0,r5,r7 - bne 1b /* Get [synced] base time */ - addc r9,r6,r4 /* Compute end time */ - addze r8,r5 -2: mftbu r5 - cmp 0,r5,r8 - blt 2b - bgt 3f - mftb r6 - cmp 0,r6,r9 - blt 2b -3: blr - -.globl _get_HID0 -_get_HID0: - mfspr r3,HID0 - blr - -.globl _put_HID0 -_put_HID0: - mtspr HID0,r3 - blr - -.globl _get_MSR -_get_MSR: - mfmsr r3 - blr - -.globl _put_MSR -_put_MSR: - mtmsr r3 - blr - -/* - * Flush instruction cache - * *** I'm really paranoid here! - */ -_GLOBAL(flush_instruction_cache) - mflr r5 - bl flush_data_cache - mfspr r3,HID0 /* Caches are controlled by this register */ - li r4,0 - ori r4,r4,(HID0_ICE|HID0_ICFI) - or r3,r3,r4 /* Need to enable+invalidate to clear */ - mtspr HID0,r3 - andc r3,r3,r4 - ori r3,r3,HID0_ICE /* Enable cache */ - mtspr HID0,r3 - mtlr r5 - blr - -#define NUM_CACHE_LINES 128*8 -#define CACHE_LINE_SIZE 32 -#define cache_flush_buffer 0x1000 - -/* - * Flush data cache - * *** I'm really paranoid here! - */ -_GLOBAL(flush_data_cache) - lis r3,cache_flush_buffer@h - ori r3,r3,cache_flush_buffer@l - li r4,NUM_CACHE_LINES - mtctr r4 -00: lwz r4,0(r3) - addi r3,r3,CACHE_LINE_SIZE /* Next line, please */ - bdnz 00b -10: blr - .comm .stack,4096*2,4 diff -Nru a/arch/ppc/boot/mbx/iic.c b/arch/ppc/boot/mbx/iic.c --- a/arch/ppc/boot/mbx/iic.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,218 +0,0 @@ -/* - * BK Id: SCCS/s.iic.c 1.10 09/14/01 19:30:13 trini - */ - -/* Minimal support functions to read configuration from IIC EEPROMS - * on MPC8xx boards. Originally written for RPGC RPX-Lite. - * Dan Malek (dmalek@jlc.net). - */ -#include -#include -#include -#include - - -/* IIC functions. - * These are just the basic master read/write operations so we can - * examine serial EEPROM. - */ -void iic_read(uint devaddr, u_char *buf, uint offset, uint count); - -static int iic_init_done; - -static void -iic_init() -{ - volatile iic_t *iip; - volatile i2c8xx_t *i2c; - volatile cpm8xx_t *cp; - volatile immap_t *immap; - uint dpaddr; - - immap = (immap_t *)IMAP_ADDR; - cp = (cpm8xx_t *)&(immap->im_cpm); - - /* Reset the CPM. This is necessary on the 860 processors - * that may have started the SCC1 ethernet without relocating - * the IIC. - * This also stops the Ethernet in case we were loaded by a - * BOOTP rom monitor. - */ - cp->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); - - /* Wait for it. - */ - while (cp->cp_cpcr & (CPM_CR_RST | CPM_CR_FLG)); - - /* Remove any microcode patches. We will install our own - * later. - */ - cp->cp_cpmcr1 = 0; - cp->cp_cpmcr2 = 0; - cp->cp_cpmcr3 = 0; - cp->cp_cpmcr4 = 0; - cp->cp_rccr = 0; - - iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; - i2c = (i2c8xx_t *)&(immap->im_i2c); - - /* Initialize Port B IIC pins. - */ - cp->cp_pbpar |= 0x00000030; - cp->cp_pbdir |= 0x00000030; - cp->cp_pbodr |= 0x00000030; - - /* Initialize the parameter ram. - */ - - /* Allocate space for a two transmit and one receive buffer - * descriptor in the DP ram. - * For now, this address seems OK, but it may have to - * change with newer versions of the firmware. - */ - dpaddr = 0x0840; - - /* Set up the IIC parameters in the parameter ram. - */ - iip->iic_tbase = dpaddr; - iip->iic_rbase = dpaddr + (2 * sizeof(cbd_t)); - - iip->iic_tfcr = SMC_EB; - iip->iic_rfcr = SMC_EB; - - /* This should really be done by the reader/writer. - */ - iip->iic_mrblr = 128; - - /* Initialize Tx/Rx parameters. - */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); - - /* Select an arbitrary address. Just make sure it is unique. - */ - i2c->i2c_i2add = 0x34; - - /* Make clock run maximum slow. - */ - i2c->i2c_i2brg = 7; - - /* Disable interrupts. - */ - i2c->i2c_i2cmr = 0; - i2c->i2c_i2cer = 0xff; - - /* Enable SDMA. - */ - immap->im_siu_conf.sc_sdcr = 1; - - iic_init_done = 1; -} - -/* Read from IIC. - * Caller provides device address, memory buffer, and byte count. - */ -static u_char iitemp[32]; - -void -iic_read(uint devaddr, u_char *buf, uint offset, uint count) -{ - volatile iic_t *iip; - volatile i2c8xx_t *i2c; - volatile cbd_t *tbdf, *rbdf; - volatile cpm8xx_t *cp; - volatile immap_t *immap; - u_char *tb; - uint temp; - - /* If the interface has not been initialized, do that now. - */ - if (!iic_init_done) - iic_init(); - - immap = (immap_t *)IMAP_ADDR; - cp = (cpm8xx_t *)&(immap->im_cpm); - - iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; - i2c = (i2c8xx_t *)&(immap->im_i2c); - - tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase]; - rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase]; - - /* Send a "dummy write" operation. This is a write request with - * only the offset sent, followed by another start condition. - * This will ensure we start reading from the first location - * of the EEPROM. - */ - tb = iitemp; - tb = (u_char *)(((uint)tb + 15) & ~15); - tbdf->cbd_bufaddr = (int)tb; - *tb = devaddr & 0xfe; /* Device address */ - *(tb+1) = offset; /* Offset */ - tbdf->cbd_datlen = 2; /* Length */ - tbdf->cbd_sc = - BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START; - - i2c->i2c_i2mod = 1; /* Enable */ - i2c->i2c_i2cer = 0xff; - i2c->i2c_i2com = 0x81; /* Start master */ - - /* Wait for IIC transfer. - */ -#if 0 - while ((i2c->i2c_i2cer & 3) == 0); - - if (tbdf->cbd_sc & BD_SC_READY) - printf("IIC ra complete but tbuf ready\n"); -#else - temp = 10000000; - while ((tbdf->cbd_sc & BD_SC_READY) && (temp != 0)) - temp--; -#if 0 - /* We can't do this...there is no serial port yet! - */ - if (temp == 0) { - printf("Timeout reading EEPROM\n"); - return; - } -#endif -#endif - - /* Chip errata, clear enable. - */ - i2c->i2c_i2mod = 0; - - /* To read, we need an empty buffer of the proper length. - * All that is used is the first byte for address, the remainder - * is just used for timing (and doesn't really have to exist). - */ - tbdf->cbd_bufaddr = (int)tb; - *tb = devaddr | 1; /* Device address */ - rbdf->cbd_bufaddr = (uint)buf; /* Desination buffer */ - tbdf->cbd_datlen = rbdf->cbd_datlen = count + 1; /* Length */ - tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START; - rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; - - /* Chip bug, set enable here. - */ - i2c->i2c_i2mod = 1; /* Enable */ - i2c->i2c_i2cer = 0xff; - i2c->i2c_i2com = 0x81; /* Start master */ - - /* Wait for IIC transfer. - */ -#if 0 - while ((i2c->i2c_i2cer & 1) == 0); - - if (rbdf->cbd_sc & BD_SC_EMPTY) - printf("IIC read complete but rbuf empty\n"); -#else - temp = 10000000; - while ((tbdf->cbd_sc & BD_SC_READY) && (temp != 0)) - temp--; -#endif - - /* Chip errata, clear enable. - */ - i2c->i2c_i2mod = 0; -} diff -Nru a/arch/ppc/boot/mbx/m8260_tty.c b/arch/ppc/boot/mbx/m8260_tty.c --- a/arch/ppc/boot/mbx/m8260_tty.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,312 +0,0 @@ -/* - * BK Id: SCCS/s.m8260_tty.c 1.7 05/18/01 07:54:04 patch - */ - - -/* Minimal serial functions needed to send messages out the serial - * port on SMC1. - */ -#include -#include -#include - -uint no_print; -extern char *params[]; -extern int nparams; -static u_char cons_hold[128], *sgptr; -static int cons_hold_cnt; - -/* If defined, enables serial console. The value (1 through 4) - * should designate which SCC is used, but this isn't complete. Only - * SCC1 is known to work at this time. - */ -#ifdef CONFIG_SCC_CONSOLE -#define SCC_CONSOLE 1 -#endif - -void -serial_init(bd_t *bd) -{ - volatile smc_t *sp; - volatile smc_uart_t *up; - volatile scc_t *sccp; - volatile scc_uart_t *sup; - volatile cbd_t *tbdf, *rbdf; - volatile immap_t *ip; - volatile iop8260_t *io; - volatile cpm8260_t *cp; - uint dpaddr, memaddr; - - ip = (immap_t *)IMAP_ADDR; - cp = &ip->im_cpm; - io = &ip->im_ioport; - - /* Perform a reset. - */ - cp->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); - - /* Wait for it. - */ - while (cp->cp_cpcr & CPM_CR_FLG); - -#ifdef CONFIG_ADS8260 - /* Enable the RS-232 transceivers. - */ - *(volatile uint *)(BCSR_ADDR + 4) &= - ~(BCSR1_RS232_EN1 | BCSR1_RS232_EN2); -#endif - -#ifdef SCC_CONSOLE - sccp = (scc_t *)&(ip->im_scc[SCC_CONSOLE-1]); - sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; - sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); - sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); - - /* Use Port D for SCC1 instead of other functions. - */ - io->iop_ppard |= 0x00000003; - io->iop_psord &= ~0x00000001; /* Rx */ - io->iop_psord |= 0x00000002; /* Tx */ - io->iop_pdird &= ~0x00000001; /* Rx */ - io->iop_pdird |= 0x00000002; /* Tx */ - -#else - sp = (smc_t*)&(ip->im_smc[0]); - *(ushort *)(&ip->im_dprambase[PROFF_SMC1_BASE]) = PROFF_SMC1; - up = (smc_uart_t *)&ip->im_dprambase[PROFF_SMC1]; - - /* Disable transmitter/receiver. - */ - sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); - - /* Use Port D for SMC1 instead of other functions. - */ - io->iop_ppard |= 0x00c00000; - io->iop_pdird |= 0x00400000; - io->iop_pdird &= ~0x00800000; - io->iop_psord &= ~0x00c00000; -#endif - - /* Allocate space for two buffer descriptors in the DP ram. - * For now, this address seems OK, but it may have to - * change with newer versions of the firmware. - */ - dpaddr = 0x0800; - - /* Grab a few bytes from the top of memory. - */ - memaddr = (bd->bi_memsize - 256) & ~15; - - /* Set the physical address of the host memory buffers in - * the buffer descriptors. - */ - rbdf = (cbd_t *)&ip->im_dprambase[dpaddr]; - rbdf->cbd_bufaddr = memaddr; - rbdf->cbd_sc = 0; - tbdf = rbdf + 1; - tbdf->cbd_bufaddr = memaddr+128; - tbdf->cbd_sc = 0; - - /* Set up the uart parameters in the parameter ram. - */ -#ifdef SCC_CONSOLE - sup->scc_genscc.scc_rbase = dpaddr; - sup->scc_genscc.scc_tbase = dpaddr + sizeof(cbd_t); - - /* Set up the uart parameters in the - * parameter ram. - */ - sup->scc_genscc.scc_rfcr = CPMFCR_GBL | CPMFCR_EB; - sup->scc_genscc.scc_tfcr = CPMFCR_GBL | CPMFCR_EB; - - sup->scc_genscc.scc_mrblr = 128; - sup->scc_maxidl = 8; - sup->scc_brkcr = 1; - sup->scc_parec = 0; - sup->scc_frmec = 0; - sup->scc_nosec = 0; - sup->scc_brkec = 0; - sup->scc_uaddr1 = 0; - sup->scc_uaddr2 = 0; - sup->scc_toseq = 0; - sup->scc_char1 = 0x8000; - sup->scc_char2 = 0x8000; - sup->scc_char3 = 0x8000; - sup->scc_char4 = 0x8000; - sup->scc_char5 = 0x8000; - sup->scc_char6 = 0x8000; - sup->scc_char7 = 0x8000; - sup->scc_char8 = 0x8000; - sup->scc_rccm = 0xc0ff; - - /* Send the CPM an initialize command. - */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_SCC1_PAGE, CPM_CR_SCC1_SBLOCK, 0, - CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - sccp->scc_gsmrh = 0; - sccp->scc_gsmrl = - (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); - - /* Disable all interrupts and clear all pending - * events. - */ - sccp->scc_sccm = 0; - sccp->scc_scce = 0xffff; - sccp->scc_dsr = 0x7e7e; - sccp->scc_pmsr = 0x3000; - - /* Wire BRG1 to SCC1. The console driver will take care of - * others. - */ - ip->im_cpmux.cmx_scr = 0; -#else - up->smc_rbase = dpaddr; - up->smc_tbase = dpaddr+sizeof(cbd_t); - up->smc_rfcr = CPMFCR_EB; - up->smc_tfcr = CPMFCR_EB; - up->smc_brklen = 0; - up->smc_brkec = 0; - up->smc_brkcr = 0; - up->smc_mrblr = 128; - up->smc_maxidl = 8; - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; - - /* Mask all interrupts and remove anything pending. - */ - sp->smc_smcm = 0; - sp->smc_smce = 0xff; - - /* Set up the baud rate generator. - */ - ip->im_cpmux.cmx_smr = 0; -#endif - - /* The baud rate divisor needs to be coordinated with clk_8260(). - */ - ip->im_brgc1 = - (((bd->bi_brgfreq/16) / bd->bi_baudrate) << 1) | - CPM_BRG_EN; - - /* Make the first buffer the only buffer. - */ - tbdf->cbd_sc |= BD_SC_WRAP; - rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; - - /* Initialize Tx/Rx parameters. - */ -#ifdef SCC_CONSOLE - sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); -#else - cp->cp_cpcr = mk_cr_cmd(CPM_CR_SMC1_PAGE, CPM_CR_SMC1_SBLOCK, 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); - - /* Enable transmitter/receiver. - */ - sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; -#endif -} - -void -serial_putc(void *ignored, const char c) -{ - volatile cbd_t *tbdf; - volatile char *buf; - volatile smc_uart_t *up; - volatile scc_uart_t *sup; - volatile immap_t *ip; - extern bd_t *board_info; - - ip = (immap_t *)IMAP_ADDR; -#ifdef SCC_CONSOLE - sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; - tbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_tbase]; -#else - up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); - tbdf = (cbd_t *)&ip->im_dprambase[up->smc_tbase]; -#endif - - /* Wait for last character to go. - */ - buf = (char *)tbdf->cbd_bufaddr; - while (tbdf->cbd_sc & BD_SC_READY); - - *buf = c; - tbdf->cbd_datlen = 1; - tbdf->cbd_sc |= BD_SC_READY; -} - -char -serial_getc(void *ignored) -{ - char c; - - if (cons_hold_cnt <= 0) { - cons_hold_cnt = serial_readbuf(cons_hold); - sgptr = cons_hold; - } - c = *sgptr++; - cons_hold_cnt--; - - return(c); -} - -int -serial_readbuf(u_char *cbuf) -{ - volatile cbd_t *rbdf; - volatile char *buf; - volatile smc_uart_t *up; - volatile scc_uart_t *sup; - volatile immap_t *ip; - int i, nc; - - ip = (immap_t *)IMAP_ADDR; - -#ifdef SCC_CONSOLE - sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; - rbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_rbase]; -#else - up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); - rbdf = (cbd_t *)&ip->im_dprambase[up->smc_rbase]; -#endif - - /* Wait for character to show up. - */ - buf = (char *)rbdf->cbd_bufaddr; - while (rbdf->cbd_sc & BD_SC_EMPTY); - nc = rbdf->cbd_datlen; - for (i=0; icbd_sc |= BD_SC_EMPTY; - - return(nc); -} - -int -serial_tstc(void *ignored) -{ - volatile cbd_t *rbdf; - volatile smc_uart_t *up; - volatile scc_uart_t *sup; - volatile immap_t *ip; - - ip = (immap_t *)IMAP_ADDR; -#ifdef SCC_CONSOLE - sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; - rbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_rbase]; -#else - up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); - rbdf = (cbd_t *)&ip->im_dprambase[up->smc_rbase]; -#endif - - return(!(rbdf->cbd_sc & BD_SC_EMPTY)); -} diff -Nru a/arch/ppc/boot/mbx/m8xx_tty.c b/arch/ppc/boot/mbx/m8xx_tty.c --- a/arch/ppc/boot/mbx/m8xx_tty.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,288 +0,0 @@ -/* - * BK Id: SCCS/s.m8xx_tty.c 1.10 09/14/01 19:30:13 trini - */ - - -/* Minimal serial functions needed to send messages out the serial - * port on the MBX console. - * - * The MBX uxes SMC1 for the serial port. We reset the port and use - * only the first BD that EPPC-Bug set up as a character FIFO. - * - * Later versions (at least 1.4, maybe earlier) of the MBX EPPC-Bug - * use COM1 instead of SMC1 as the console port. This kinda sucks - * for the rest of the kernel, so here we force the use of SMC1 again. - */ -#include -#include -#include -#include -#include - -#ifdef CONFIG_MBX -#define MBX_CSR1 ((volatile u_char *)0xfa100000) -#define CSR1_COMEN (u_char)0x02 -#endif - -#ifdef TQM_SMC2_CONSOLE -#define PROFF_CONS PROFF_SMC2 -#define CPM_CR_CH_CONS CPM_CR_CH_SMC2 -#define SMC_INDEX 1 -static volatile iop8xx_t *iopp = (iop8xx_t *)&(((immap_t *)IMAP_ADDR)->im_ioport); -#else -#define PROFF_CONS PROFF_SMC1 -#define CPM_CR_CH_CONS CPM_CR_CH_SMC1 -#define SMC_INDEX 0 -#endif - -static cpm8xx_t *cpmp = (cpm8xx_t *)&(((immap_t *)IMAP_ADDR)->im_cpm); - -void -serial_init(bd_t *bd) -{ - volatile smc_t *sp; - volatile smc_uart_t *up; - volatile cbd_t *tbdf, *rbdf; - volatile cpm8xx_t *cp; - uint dpaddr, memaddr, ui; - - cp = cpmp; - sp = (smc_t*)&(cp->cp_smc[SMC_INDEX]); - up = (smc_uart_t *)&cp->cp_dparam[PROFF_CONS]; - - /* Disable transmitter/receiver. - */ - sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); - -#ifdef CONFIG_FADS - /* Enable SMC1/2 transceivers. - */ - *((volatile uint *)BCSR1) &= ~(BCSR1_RS232EN_1|BCSR1_RS232EN_2); -#endif - -#ifndef CONFIG_MBX - { - /* Initialize SMCx and use it for the console port. - */ - - /* Enable SDMA. - */ - ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sdcr = 1; - -#ifdef TQM_SMC2_CONSOLE - /* Use Port A for SMC2 instead of other functions. - */ - iopp->iop_papar |= 0x00c0; - iopp->iop_padir &= ~0x00c0; - iopp->iop_paodr &= ~0x00c0; -#else - /* Use Port B for SMCs instead of other functions. - */ - cp->cp_pbpar |= 0x00000cc0; - cp->cp_pbdir &= ~0x00000cc0; - cp->cp_pbodr &= ~0x00000cc0; -#endif - - /* Allocate space for two buffer descriptors in the DP ram. - * For now, this address seems OK, but it may have to - * change with newer versions of the firmware. - */ - dpaddr = 0x0800; - - /* Grab a few bytes from the top of memory for SMC FIFOs. - */ - memaddr = (bd->bi_memsize - 32) & ~15; - - /* Set the physical address of the host memory buffers in - * the buffer descriptors. - */ - rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; - rbdf->cbd_bufaddr = memaddr; - rbdf->cbd_sc = 0; - tbdf = rbdf + 1; - tbdf->cbd_bufaddr = memaddr+4; - tbdf->cbd_sc = 0; - - /* Set up the uart parameters in the parameter ram. - */ - up->smc_rbase = dpaddr; - up->smc_tbase = dpaddr+sizeof(cbd_t); - up->smc_rfcr = SMC_EB; - up->smc_tfcr = SMC_EB; - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; - - /* Mask all interrupts and remove anything pending. - */ - sp->smc_smcm = 0; - sp->smc_smce = 0xff; - - /* Set up the baud rate generator. - * See 8xx_io/commproc.c for details. - * This wires BRG1 to SMC1 and BRG2 to SMC2; - */ - cp->cp_simode = 0x10000000; - ui = bd->bi_intfreq / 16 / bd->bi_baudrate; -#ifdef TQM_SMC2_CONSOLE - cp->cp_brgc2 = -#else - cp->cp_brgc1 = -#endif - ((ui - 1) < 4096) - ? (((ui - 1) << 1) | CPM_BRG_EN) - : ((((ui / 16) - 1) << 1) | CPM_BRG_EN | CPM_BRG_DIV16); - -#else /* CONFIG_MBX */ - if (*MBX_CSR1 & CSR1_COMEN) { - /* COM1 is enabled. Initialize SMC1 and use it for - * the console port. - */ - - /* Enable SDMA. - */ - ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sdcr = 1; - - /* Use Port B for SMCs instead of other functions. - */ - cp->cp_pbpar |= 0x00000cc0; - cp->cp_pbdir &= ~0x00000cc0; - cp->cp_pbodr &= ~0x00000cc0; - - /* Allocate space for two buffer descriptors in the DP ram. - * For now, this address seems OK, but it may have to - * change with newer versions of the firmware. - */ - dpaddr = 0x0800; - - /* Grab a few bytes from the top of memory. EPPC-Bug isn't - * running any more, so we can do this. - */ - memaddr = (bd->bi_memsize - 32) & ~15; - - /* Set the physical address of the host memory buffers in - * the buffer descriptors. - */ - rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; - rbdf->cbd_bufaddr = memaddr; - rbdf->cbd_sc = 0; - tbdf = rbdf + 1; - tbdf->cbd_bufaddr = memaddr+4; - tbdf->cbd_sc = 0; - - /* Set up the uart parameters in the parameter ram. - */ - up->smc_rbase = dpaddr; - up->smc_tbase = dpaddr+sizeof(cbd_t); - up->smc_rfcr = SMC_EB; - up->smc_tfcr = SMC_EB; - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; - - /* Mask all interrupts and remove anything pending. - */ - sp->smc_smcm = 0; - sp->smc_smce = 0xff; - - /* Set up the baud rate generator. - * See 8xx_io/commproc.c for details. - */ - cp->cp_simode = 0x10000000; - cp->cp_brgc1 = - (((bd->bi_intfreq/16) / 9600) << 1) | CPM_BRG_EN; - - /* Enable SMC1 for console output. - */ - *MBX_CSR1 &= ~CSR1_COMEN; - } - else { -#endif /* ndef CONFIG_MBX */ - /* SMCx is used as console port. - */ - tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase]; - rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase]; - - /* Issue a stop transmit, and wait for it. - */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_CONS, - CPM_CR_STOP_TX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); - } - - /* Make the first buffer the only buffer. - */ - tbdf->cbd_sc |= BD_SC_WRAP; - rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; - - /* Single character receive. - */ - up->smc_mrblr = 1; - up->smc_maxidl = 0; - - /* Initialize Tx/Rx parameters. - */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_CONS, CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); - - /* Enable transmitter/receiver. - */ - sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; -} - -void -serial_putc(void *ignored, const char c) -{ - volatile cbd_t *tbdf; - volatile char *buf; - volatile smc_uart_t *up; - - up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_CONS]; - tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; - - /* Wait for last character to go. - */ - buf = (char *)tbdf->cbd_bufaddr; - while (tbdf->cbd_sc & BD_SC_READY); - - *buf = c; - tbdf->cbd_datlen = 1; - tbdf->cbd_sc |= BD_SC_READY; -} - -char -serial_getc(void *ignored) -{ - volatile cbd_t *rbdf; - volatile char *buf; - volatile smc_uart_t *up; - char c; - - up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_CONS]; - rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; - - /* Wait for character to show up. - */ - buf = (char *)rbdf->cbd_bufaddr; - while (rbdf->cbd_sc & BD_SC_EMPTY); - c = *buf; - rbdf->cbd_sc |= BD_SC_EMPTY; - - return(c); -} - -int -serial_tstc(void *ignored) -{ - volatile cbd_t *rbdf; - volatile smc_uart_t *up; - - up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_CONS]; - rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; - - return(!(rbdf->cbd_sc & BD_SC_EMPTY)); -} diff -Nru a/arch/ppc/boot/mbx/misc.c b/arch/ppc/boot/mbx/misc.c --- a/arch/ppc/boot/mbx/misc.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,265 +0,0 @@ -/* - * BK Id: SCCS/s.misc.c 1.13 07/27/01 11:44:37 trini - */ -/* - * Adapted for PowerPC by Gary Thomas - * - * Rewritten by Cort Dougan (cort@cs.nmt.edu) - * One day to be replaced by a single bootloader for chrp/prep/pmac. -- Cort - */ - -#include -#include -#include "zlib.h" -#include -#include -#include -#include -#include -#ifdef CONFIG_8xx -#include -#endif -#ifdef CONFIG_8260 -#include -#endif - -#include "nonstdio.h" - -/* - * The following references are needed to cause the linker to pull in the - * gzimage.o and rdimage.o files. These object files are special, - * since they get placed into the .gzimage and .rdimage ELF sections - * of the zvmlinux and zvmlinux.initrd files. - */ -extern char dummy_for_gzimage; -extern char dummy_for_rdimage; - -/* - * Please send me load/board info and such data for hardware not - * listed here so I can keep track since things are getting tricky - * with the different load addrs with different firmware. This will - * help to avoid breaking the load/boot process. - * -- Cort - */ -char *avail_ram; -char *end_avail; - -/* See comment below..... -*/ -unsigned int initrd_offset, initrd_size; - -/* Because of the limited amount of memory on embedded, it presents - * loading problems. The biggest is that we load this boot program - * into a relatively low memory address, and the Linux kernel Bss often - * extends into this space when it get loaded. When the kernel starts - * and zeros the BSS space, it also writes over the information we - * save here and pass to the kernel (command line and board info). - * On these boards, we grab some known memory holes to hold this information. - */ -char cmd_buf[256]; -char *cmd_line = cmd_buf; - -/* We need to pass along a 'dummy' com_port. */ -unsigned long com_port = 0; - -/* This is the default cmdline that will be given to the user at boot time.. - * If none was specified at compile time, we'll give it one that should work. - * -- Tom */ -#ifdef CONFIG_CMDLINE_BOOL -char compiled_string[] = CONFIG_CMDLINE; -#endif -char ramroot_string[] = "root=/dev/ram"; -char netroot_string[] = "root=/dev/nfs rw"; - -bd_t hold_resid_buf; -bd_t *hold_residual = &hold_resid_buf; -unsigned long initrd_start = 0, initrd_end = 0; -char *zimage_start; -int zimage_size; - -extern void gunzip(void *, int, unsigned char *, int *); - -unsigned long -decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, bd_t *bp) -{ - int timer; - extern unsigned long start; - char *cp, ch; - -#ifdef CONFIG_8260 - /* I don't know why I didn't do it this way on the 8xx....... - */ - embed_config(&bp); - serial_init(bp); -#endif - - /* These values must be variables. If not, the compiler optimizer - * will remove some code, causing the size of the code to vary - * when these values are zero. This is bad because we first - * compile with these zero to determine the size and offsets - * in an image, than compile again with these set to the proper - * discovered value.....Ya know, we used to read these from the - * header a long time ago..... - */ - initrd_offset = INITRD_OFFSET; - initrd_size = INITRD_SIZE; - - /* Grab some space for the command line and board info. Since - * we no longer use the ELF header, but it was loaded, grab - * that space. - */ -#ifdef CONFIG_MBX - cmd_line = (char *)(load_addr - 0x10000); - - /* To be like everyone else, we need one too, although this - * board information is passed from the boot rom. - */ - bp->bi_baudrate = 9600; -#else - cmd_line = (char *)(0x200000); -#endif - hold_residual = (bd_t *)(cmd_line + sizeof(cmd_buf)); - /* copy board data */ - if (bp) - memcpy(hold_residual,bp,sizeof(bd_t)); - - /* Set end of memory available to us. It is always the highest - * memory address provided by the board information. - */ - end_avail = (char *)(bp->bi_memsize); - - puts("loaded at: "); puthex(load_addr); - puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); - if ( (unsigned long)load_addr != (unsigned long)&start ) - { - puts("relocated to: "); puthex((unsigned long)&start); - puts(" "); - puthex((unsigned long)((unsigned long)&start + (4*num_words))); - puts("\n"); - } - - if ( bp ) - { - puts("board data at: "); puthex((unsigned long)bp); - puts(" "); - puthex((unsigned long)((unsigned long)bp + sizeof(bd_t))); - puts("\n"); - puts("relocated to: "); - puthex((unsigned long)hold_residual); - puts(" "); - puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t))); - puts("\n"); - } - - /* we have to subtract 0x10000 here to correct for objdump including the - size of the elf header which we strip -- Cort */ - zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET); - zimage_size = ZIMAGE_SIZE; - - if ( initrd_offset ) - initrd_start = load_addr - 0x10000 + initrd_offset; - else - initrd_start = 0; - initrd_end = initrd_size + initrd_start; - - /* - * setup avail_ram - this is the first part of ram usable - * by the uncompress code. -- Cort - */ - avail_ram = (char *)PAGE_ALIGN((unsigned long)zimage_start+zimage_size); - if ( ((load_addr+(num_words*4)) > (unsigned long) avail_ram) - && (load_addr <= 0x01000000) ) - avail_ram = (char *)(load_addr+(num_words*4)); - if ( (((unsigned long)&start+(num_words*4)) > (unsigned long) avail_ram) - && (load_addr <= 0x01000000) ) - avail_ram = (char *)((unsigned long)&start+(num_words*4)); - - /* relocate zimage */ - puts("zimage at: "); puthex((unsigned long)zimage_start); - puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); - /* - * There is no reason (yet) to relocate zImage for embedded boards. - * To support boot from flash rom on 8xx embedded boards, I - * assume if zimage start is over 16M we are booting from flash. - * In this case, avilable ram will start just above the space we - * have allocated for the command buffer and board information. - */ - if ((unsigned long)zimage_start > 0x01000000) - avail_ram = (char *)PAGE_ALIGN((unsigned long)hold_residual + sizeof(bd_t)); - - /* relocate initrd */ - if ( initrd_start ) - { - puts("initrd at: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); - - /* We only have to relocate initrd if we find it is in Flash - * rom. This is because the kernel thinks it can toss the - * pages into the free memory pool after it is done. Use - * the same 16M test. - */ - if ((unsigned long)initrd_start > 0x01000000) { - memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE), - (void *)initrd_start, - initrd_size ); - initrd_start = PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE); - initrd_end = initrd_start + initrd_size; - end_avail = (char *)initrd_start; - puts("relocated to: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); - } - else { - avail_ram = (char *)PAGE_ALIGN((unsigned long)initrd_end); - } - } - - - puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); - puthex((unsigned long)end_avail); puts("\n"); - puts("\nLinux/PPC load: "); - timer = 0; - cp = cmd_line; - /* This is where we try and pick the right command line for booting. - * If we were given one at compile time, use it. It Is Right. - * If we weren't, see if we have a ramdisk. If so, thats root. - * When in doubt, give them the netroot (root=/dev/nfs rw) -- Tom */ -#ifdef CONFIG_CMDLINE_BOOL - memcpy (cmd_line, compiled_string, sizeof(compiled_string)); -#else - if (initrd_start) - memcpy (cmd_line, ramroot_string, sizeof(ramroot_string)); - else - memcpy (cmd_line, netroot_string, sizeof(netroot_string)); -#endif - while ( *cp ) putc(*cp++); - while (timer++ < 5*1000) { - if (tstc()) { - while ((ch = getc()) != '\n' && ch != '\r') { - if (ch == '\b' || ch == '\177') { - if (cp != cmd_line) { - cp--; - puts("\b \b"); - } - } else if (ch == '\030' /* ^x */ - || ch == '\025') { /* ^u */ - while (cp != cmd_line) { - cp--; - puts("\b \b"); - } - } else { - *cp++ = ch; - putc(ch); - } - } - break; /* Exit 'timer' loop */ - } - udelay(1000); /* 1 msec */ - } - *cp = 0; - puts("\nUncompressing Linux..."); - - gunzip(0, 0x400000, zimage_start, &zimage_size); - puts("done.\n"); - puts("Now booting the kernel\n"); - return (unsigned long)hold_residual; -} diff -Nru a/arch/ppc/boot/mbx/pci.c b/arch/ppc/boot/mbx/pci.c --- a/arch/ppc/boot/mbx/pci.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,255 +0,0 @@ -/* - * BK Id: SCCS/s.pci.c 1.6 05/18/01 15:17:06 cort - */ -/* Stand alone funtions for QSpan Tundra support. - */ -#include -#include -#include -#include - -/* To map PCI devices, you first write 0xffffffff into the device - * base address registers. When the register is read back, the - * number of most significant '1' bits describes the amount of address - * space needed for mapping. If the most significant bit is not set, - * either the device does not use that address register, or it has - * a fixed address that we can't change. After the address is assigned, - * the command register has to be written to enable the card. - */ -typedef struct { - u_char pci_bus; - u_char pci_devfn; - ushort pci_command; - uint pci_addrs[6]; -} pci_map_t; - -/* We should probably dynamically allocate these structures. -*/ -#define MAX_PCI_DEVS 32 -int pci_dev_cnt; -pci_map_t pci_map[MAX_PCI_DEVS]; - -void pci_conf_write(int bus, int device, int func, int reg, uint writeval); -void pci_conf_read(int bus, int device, int func, int reg, void *readval); -void probe_addresses(int bus, int devfn); -void map_pci_addrs(void); - -/* This is a really stripped version of PCI bus scan. All we are - * looking for are devices that exist. - */ -pci_scanner(int addr_probe) -{ - unsigned int devfn, l, max, class, bus_number; - unsigned char cmd, irq, tmp, hdr_type, is_multi; - int reg; - - is_multi = 0; - bus_number = 0; - for (devfn = 0; devfn < 0xff; ++devfn) { - /* The device numbers are comprised of upper 5 bits of - * device number and lower 3 bits of multi-function number. - */ - if ((devfn & 7) && !is_multi) { - /* Don't scan multifunction addresses if this is - * not a multifunction device. - */ - continue; - } - - /* Read the header to determine card type. - */ - qs_pci_read_config_byte(bus_number, devfn, PCI_HEADER_TYPE, - &hdr_type); - - /* If this is a base device number, check the header to - * determine if it is mulifunction. - */ - if ((devfn & 7) == 0) - is_multi = hdr_type & 0x80; - - /* Check to see if the board is really in the slot. - */ - qs_pci_read_config_dword(bus_number, devfn, PCI_VENDOR_ID, &l); - /* some broken boards return 0 if a slot is empty: */ - if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || - l == 0xffff0000) { - /* Nothing there. - */ - is_multi = 0; - continue; - } - - /* If we are not performing an address probe, - * just simply print out some information. - */ - if (!addr_probe) { - qs_pci_read_config_dword(bus_number, devfn, - PCI_CLASS_REVISION, &class); - - class >>= 8; /* upper 3 bytes */ - -#if 0 - printf("Found (%3d:%d): vendor 0x%04x, device 0x%04x, class 0x%06x\n", - (devfn >> 3), (devfn & 7), - (l & 0xffff), (l >> 16) & 0xffff, class); -#else - puts("Found ("); puthex(devfn >> 3); - puts(":"); puthex(devfn & 7); - puts("): vendor "); puthex(l & 0xffff); - puts(", device "); puthex((l >> 16) & 0xffff); - puts(", class "); puthex(class); puts("\n"); -#endif - } - else { - /* If this is a "normal" device, build address list. - */ - if ((hdr_type & 0x7f) == PCI_HEADER_TYPE_NORMAL) - probe_addresses(bus_number, devfn); - } - } - - /* Now map the boards. - */ - if (addr_probe) - map_pci_addrs(); -} - -/* Probe addresses for the specified device. This is a destructive - * operation because it writes the registers. - */ -void -probe_addresses(bus, devfn) -{ - int i; - uint pciaddr; - ushort pcicmd; - pci_map_t *pm; - - if (pci_dev_cnt >= MAX_PCI_DEVS) { - puts("Too many PCI devices\n"); - return; - } - - pm = &pci_map[pci_dev_cnt++]; - - pm->pci_bus = bus; - pm->pci_devfn = devfn; - - for (i=0; i<6; i++) { - qs_pci_write_config_dword(bus, devfn, PCI_BASE_ADDRESS_0 + (i * 4), -1); - qs_pci_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0 + (i * 4), - &pciaddr); - pm->pci_addrs[i] = pciaddr; - qs_pci_read_config_word(bus, devfn, PCI_COMMAND, &pcicmd); - pm->pci_command = pcicmd; - } -} - -/* Map the cards into the PCI space. The PCI has separate memory - * and I/O spaces. In addition, some memory devices require mapping - * below 1M. The least significant 4 bits of the address register - * provide information. If this is an I/O device, only the LS bit - * is used to indicate that, so I/O devices can be mapped to a two byte - * boundard. Memory addresses can be mapped to a 32 byte boundary. - * The QSpan implementations usually have a 1Gbyte space for each - * memory and I/O spaces. - * - * This isn't a terribly fancy algorithm. I just map the spaces from - * the top starting with the largest address space. When finished, - * the registers are written and the card enabled. - * - * While the Tundra can map a large address space on most boards, we - * need to be careful because it may overlap other devices (like IMMR). - */ -#define MEMORY_SPACE_SIZE 0x20000000 -#define IO_SPACE_SIZE 0x20000000 - -void -map_pci_addrs() -{ - uint pci_mem_top, pci_mem_low; - uint pci_io_top; - uint addr_mask, reg_addr, space; - int i, j; - pci_map_t *pm; - - pci_mem_top = MEMORY_SPACE_SIZE; - pci_io_top = IO_SPACE_SIZE; - pci_mem_low = (1 * 1024 * 1024); /* Below one meg addresses */ - - /* We can't map anything more than the maximum space, but test - * for it anyway to catch devices out of range. - */ - addr_mask = 0x80000000; - - do { - space = (~addr_mask) + 1; /* Size of the space */ - for (i=0; ipci_addrs[j]; - if ((reg_addr & 0x80000000) == 0) - continue; - if (reg_addr & PCI_BASE_ADDRESS_SPACE_IO) { - if ((reg_addr & PCI_BASE_ADDRESS_IO_MASK) != addr_mask) - continue; - if (pci_io_top < space) { - puts("Out of PCI I/O space\n"); - } - else { - pci_io_top -= space; - pm->pci_addrs[j] = pci_io_top; - pm->pci_command |= PCI_COMMAND_IO; - } - } - else { - if ((reg_addr & PCI_BASE_ADDRESS_MEM_MASK) != addr_mask) - continue; - - /* Memory space. Test if below 1M. - */ - if (reg_addr & PCI_BASE_ADDRESS_MEM_TYPE_1M) { - if (pci_mem_low < space) { - puts("Out of PCI 1M space\n"); - } - else { - pci_mem_low -= space; - pm->pci_addrs[j] = pci_mem_low; - } - } - else { - if (pci_mem_top < space) { - puts("Out of PCI Mem space\n"); - } - else { - pci_mem_top -= space; - pm->pci_addrs[j] = pci_mem_top; - } - } - pm->pci_command |= PCI_COMMAND_MEMORY; - } - } - } - addr_mask >>= 1; - addr_mask |= 0x80000000; - } while (addr_mask != 0xfffffffe); - - /* Now, run the list one more time and map everything. - */ - for (i=0; ipci_bus, pm->pci_devfn, - PCI_BASE_ADDRESS_0 + (j * 4), pm->pci_addrs[j]); - } - - /* Enable memory or address mapping. - */ - qs_pci_write_config_word(pm->pci_bus, pm->pci_devfn, PCI_COMMAND, - pm->pci_command); - } -} - diff -Nru a/arch/ppc/boot/mbx/qspan_pci.c b/arch/ppc/boot/mbx/qspan_pci.c --- a/arch/ppc/boot/mbx/qspan_pci.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,271 +0,0 @@ -/* - * BK Id: SCCS/s.qspan_pci.c 1.6 05/18/01 15:17:06 cort - */ -/* - * LinuxPPC arch/ppc/kernel/qspan_pci.c Dan Malek (dmalek@jlc.net) - * - * QSpan Motorola bus to PCI bridge. The config address register - * is located 0x500 from the base of the bridge control/status registers. - * The data register is located at 0x504. - * This is a two step operation. First, the address register is written, - * then the data register is read/written as required. - * I don't know what to do about interrupts (yet). - */ - -#include -#include -#include -#include - -/* - * When reading the configuration space, if something does not respond - * the bus times out and we get a machine check interrupt. So, the - * good ol' exception tables come to mind to trap it and return some - * value. - * - * On an error we just return a -1, since that is what the caller wants - * returned if nothing is present. I copied this from __get_user_asm, - * with the only difference of returning -1 instead of EFAULT. - * There is an associated hack in the machine check trap code. - * - * The QSPAN is also a big endian device, that is it makes the PCI - * look big endian to us. This presents a problem for the Linux PCI - * functions, which assume little endian. For example, we see the - * first 32-bit word like this: - * ------------------------ - * | Device ID | Vendor ID | - * ------------------------ - * If we read/write as a double word, that's OK. But in our world, - * when read as a word, device ID is at location 0, not location 2 as - * the little endian PCI would believe. We have to switch bits in - * the PCI addresses given to us to get the data to/from the correct - * byte lanes. - * - * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5. - * It always forces the MS bit to zero. Therefore, dev_fn values - * greater than 128 are returned as "no device found" errors. - * - * The QSPAN can only perform long word (32-bit) configuration cycles. - * The "offset" must have the two LS bits set to zero. Read operations - * require we read the entire word and then sort out what should be - * returned. Write operations other than long word require that we - * read the long word, update the proper word or byte, then write the - * entire long word back. - * - * PCI Bridge hack. We assume (correctly) that bus 0 is the primary - * PCI bus from the QSPAN. If we are called with a bus number other - * than zero, we create a Type 1 configuration access that a downstream - * PCI bridge will interpret. - */ - -#define __get_pci_config(x, addr, op) \ - __asm__ __volatile__( \ - "1: "op" %0,0(%1)\n" \ - " eieio\n" \ - "2:\n" \ - ".section .fixup,\"ax\"\n" \ - "3: li %0,-1\n" \ - " b 2b\n" \ - ".section __ex_table,\"a\"\n" \ - " .align 2\n" \ - " .long 1b,3b\n" \ - ".text" \ - : "=r"(x) : "r"(addr)) - -#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500)) -#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504)) - -#define mk_config_addr(bus, dev, offset) \ - (((bus)<<16) | ((dev)<<8) | (offset & 0xfc)) - -#define mk_config_type1(bus, dev, offset) \ - mk_config_addr(bus, dev, offset) | 1; - -/* Initialize the QSpan device registers after power up. -*/ -qspan_init() -{ - uint *qptr; - - - - qptr = (uint *)PCI_CSR_ADDR; - - /* PCI Configuration/status. Upper bits written to clear - * pending interrupt or status. Lower bits enable QSPAN as - * PCI master, enable memory and I/O cycles, and enable PCI - * parity error checking. - * IMPORTANT: The last two bits of this word enable PCI - * master cycles into the QBus. The QSpan is broken and can't - * meet the timing specs of the PQ bus for this to work. Therefore, - * if you don't have external bus arbitration, you can't use - * this function. - */ -#ifdef EXTERNAL_PQ_ARB - qptr[1] = 0xf9000147; -#else - qptr[1] = 0xf9000144; -#endif - - /* PCI Misc configuration. Set PCI latency timer resolution - * of 8 cycles, set cache size to 4 x 32. - */ - qptr[3] = 0; - - /* Set up PCI Target address mapping. Enable, Posted writes, - * 2Gbyte space (processor memory controller determines actual size). - */ - qptr[64] = 0x8f000080; - - /* Map processor 0x80000000 to PCI 0x00000000. - * Processor address bit 1 determines I/O type access (0x80000000) - * or memory type access (0xc0000000). - */ - qptr[65] = 0x80000000; - - /* Enable error logging and clear any pending error status. - */ - qptr[80] = 0x90000000; - - qptr[512] = 0x000c0003; - - /* Set up Qbus slave image. - */ - qptr[960] = 0x01000000; - qptr[961] = 0x000000d1; - qptr[964] = 0x00000000; - qptr[965] = 0x000000d1; - -} - -/* Functions to support PCI bios-like features to read/write configuration - * space. If the function fails for any reason, a -1 (0xffffffff) value - * must be returned. - */ -#define DEVICE_NOT_FOUND (-1) -#define SUCCESSFUL 0 - -int qs_pci_read_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val) -{ - uint temp; - u_char *cp; - - if ((bus > 7) || (dev_fn > 127)) { - *val = 0xff; - return DEVICE_NOT_FOUND; - } - - if (bus == 0) - *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); - else - *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); - __get_pci_config(temp, QS_CONFIG_DATA, "lwz"); - - offset ^= 0x03; - cp = ((u_char *)&temp) + (offset & 0x03); - *val = *cp; - return SUCCESSFUL; -} - -int qs_pci_read_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short *val) -{ - uint temp; - ushort *sp; - - if ((bus > 7) || (dev_fn > 127)) { - *val = 0xffff; - return DEVICE_NOT_FOUND; - } - - if (bus == 0) - *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); - else - *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); - __get_pci_config(temp, QS_CONFIG_DATA, "lwz"); - offset ^= 0x02; - - sp = ((ushort *)&temp) + ((offset >> 1) & 1); - *val = *sp; - return SUCCESSFUL; -} - -int qs_pci_read_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val) -{ - if ((bus > 7) || (dev_fn > 127)) { - *val = 0xffffffff; - return DEVICE_NOT_FOUND; - } - if (bus == 0) - *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); - else - *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); - __get_pci_config(*val, QS_CONFIG_DATA, "lwz"); - return SUCCESSFUL; -} - -int qs_pci_write_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char val) -{ - uint temp; - u_char *cp; - - if ((bus > 7) || (dev_fn > 127)) - return DEVICE_NOT_FOUND; - - qs_pci_read_config_dword(bus, dev_fn, offset, &temp); - - offset ^= 0x03; - cp = ((u_char *)&temp) + (offset & 0x03); - *cp = val; - - if (bus == 0) - *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); - else - *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); - *QS_CONFIG_DATA = temp; - - return SUCCESSFUL; -} - -int qs_pci_write_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short val) -{ - uint temp; - ushort *sp; - - if ((bus > 7) || (dev_fn > 127)) - return DEVICE_NOT_FOUND; - - qs_pci_read_config_dword(bus, dev_fn, offset, &temp); - - offset ^= 0x02; - sp = ((ushort *)&temp) + ((offset >> 1) & 1); - *sp = val; - - if (bus == 0) - *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); - else - *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); - *QS_CONFIG_DATA = temp; - - return SUCCESSFUL; -} - -int qs_pci_write_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int val) -{ - if ((bus > 7) || (dev_fn > 127)) - return DEVICE_NOT_FOUND; - - if (bus == 0) - *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); - else - *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); - *(unsigned int *)QS_CONFIG_DATA = val; - - return SUCCESSFUL; -} - diff -Nru a/arch/ppc/boot/mbx/rdimage.c b/arch/ppc/boot/mbx/rdimage.c --- a/arch/ppc/boot/mbx/rdimage.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -/* - * BK Id: SCCS/s.rdimage.c 1.6 05/18/01 15:17:06 cort - */ -/* - * rdimage.c - * - * Dummy file to allow a compressed initrd to be added - * into a linker section, accessed by the boot coode - */ - -char dummy_for_rdimage; diff -Nru a/arch/ppc/boot/pmac/Makefile b/arch/ppc/boot/pmac/Makefile --- a/arch/ppc/boot/pmac/Makefile Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/boot/pmac/Makefile Tue Feb 19 18:08:59 2002 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.Makefile 1.14 07/27/01 20:24:17 trini +# BK Id: %F% %I% %G% %U% %#% # # Makefile for making XCOFF bootable images for booting on PowerMacs # using Open Firmware. @@ -9,10 +9,10 @@ # Tom Rini January 2001 OBJCOPY_ARGS = -O aixcoff-rs6000 -R .stab -R .stabstr -R .comment -COFF_LD_ARGS = -e _start -T ld.script -Ttext 500000 -Tdata 510000 -Bstatic -CHRP_LD_ARGS = -Ttext 0x01000000 +COFF_LD_ARGS = -T ../ld.script -e _start -Ttext 0x00500000 -Bstatic +CHRP_LD_ARGS = -T ../ld.script -Ttext 0x01000000 -COMMONOBJS = start.o misc.o ../common/string.o image.o ../common/ofcommon.o +COMMONOBJS = start.o misc.o ../common/string.o ../common/ofcommon.o COFFOBJS = ../common/coffcrt0.o $(COMMONOBJS) coffmain.o CHRPOBJS = ../common/crt0.o $(COMMONOBJS) chrpmain.o LIBS = $(TOPDIR)/lib/lib.a ../lib/zlib.a @@ -20,62 +20,58 @@ MKNOTE := ../utils/mknote SIZE := ../utils/size OFFSET := ../utils/offset -PIGGYBACK := ../utils/piggyback HACKCOFF := ../utils/hack-coff -ifeq ($(CONFIG_PPC64BRIDGE),y) -MSIZE=.64 -else -MSIZE= +ifdef CONFIG_SMP +END := .smp endif - -ifeq ($(CONFIG_SMP),y) -TFTPIMAGE=/tftpboot/zImage.pmac.smp$(MSIZE) -else -TFTPIMAGE=/tftpboot/zImage.pmac$(MSIZE) +ifdef CONFIG_PPC64BRIDGE +END += .64 endif -../common/crt0.o: - $(MAKE) -C ../common crt0.o +TFTPIMAGE=/tftpboot/zImage.pmac$(END) ../common/coffcrt0.o: $(MAKE) -C ../common coffcrt0.o -chrpmain.o: chrpmain.c - $(CC) $(CFLAGS) -DSYSMAP_OFFSET=0 -DSYSMAP_SIZE=0 -c chrpmain.c +image.o: ../images/vmlinux.gz ../common/dummy.o + $(OBJCOPY) ../common/dummy.o $@ -R .comment \ + --add-section=.image=../images/vmlinux.gz \ + --set-section-flags=.image=contents,alloc,load,readonly,data +ifdef CONFIG_XMON + $(OBJCOPY) $@ $@ \ + --add-section=.sysmap=$(TOPDIR)/System.map \ + --set-section-flags=.sysmap=contents,alloc,load,readonly,data +endif znetboot: vmlinux.coff vmlinux.elf-pmac zImage cp ../images/vmlinux.coff $(TFTPIMAGE) cp ../images/vmlinux.elf-pmac $(TFTPIMAGE).elf -znetboot.initrd: vmlinux.coff.initrd vmlinux.initrd.elf-pmac - cp ../images/vmlinux.coff.initrd $(TFTPIMAGE) - cp ../images/vmlinux.elf-pmac.initrd $(TFTPIMAGE).elf - -#floppy: zImage -# mount -t hfs /dev/fd0 /mnt -# cp vmlinux.coff /mnt -# umount /mnt +znetboot.initrd: vmlinux.initrd.coff vmlinux.initrd.elf-pmac + cp ../images/vmlinux.initrd.coff $(TFTPIMAGE) + cp ../images/vmlinux.initrd.elf-pmac $(TFTPIMAGE).elf -miboot.image: dummy.o ../images/vmlinux.gz +miboot.image: ../common/dummy.o ../images/vmlinux.gz $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=image=../images/vmlinux.gz \ - dummy.o ../images/$@ + ../common/dummy.o ../images/$@ -miboot.image.initrd: miboot.image ../images/ramdisk.image.gz +miboot.initrd.image: miboot.image ../images/ramdisk.image.gz $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=../images/ramdisk.image.gz \ ../images/miboot.image ../images/$@ -coffboot: $(COFFOBJS) $(LIBS) ../common/no_initrd.o ld.script ../images/vmlinux.gz - $(LD) -o $@ $(COFF_LD_ARGS) $(COFFOBJS) ../common/no_initrd.o $(LIBS) - -coffboot.initrd: $(COFFOBJS) $(LIBS) initrd.o ld.script ../images/vmlinux.gz - $(LD) -o $@ $(COFF_LD_ARGS) $(COFFOBJS) initrd.o $(LIBS) - -image.o: $(PIGGYBACK) ../images/vmlinux.gz - $(PIGGYBACK) image < ../images/vmlinux.gz | $(AS) -o $@ - -initrd.o: ../images/ramdisk.image.gz $(PIGGYBACK) - $(PIGGYBACK) initrd < ../images/ramdisk.image.gz | $(AS) -o $@ +coffboot: $(COFFOBJS) image.o $(LIBS) ../ld.script + $(LD) -o $@ $(COFF_LD_ARGS) $(COFFOBJS) image.o $(LIBS) + $(OBJCOPY) $@ $@ -R .comment + +coffboot.initrd: $(COFFOBJS) image.o $(LIBS) ../ld.script \ + ../images/ramdisk.image.gz + $(OBJCOPY) image.o image-coff.o \ + --add-section=.ramdisk=../images/ramdisk.image.gz \ + --set-section-flags=.ramdisk=contents,alloc,load,readonly,data + $(LD) -o $@ $(COFF_LD_ARGS) $(COFFOBJS) image-coff.o $(LIBS) + $(OBJCOPY) $@ $@ -R .comment + rm -f image-coff.o vmlinux.coff: coffboot $(HACKCOFF) $(OBJCOPY) $(OBJCOPY_ARGS) coffboot ../images/$@ @@ -83,33 +79,32 @@ rm -f coffboot ln -sf vmlinux.coff ../images/zImage.pmac -vmlinux.coff.initrd: coffboot.initrd $(HACKCOFF) +vmlinux.initrd.coff: coffboot.initrd $(HACKCOFF) $(OBJCOPY) $(OBJCOPY_ARGS) coffboot.initrd ../images/$@ $(HACKCOFF) ../images/$@ rm -f coffboot.initrd - ln -sf vmlinux.coff.initrd ../images/zImage.initrd.pmac + ln -sf vmlinux.initrd.coff ../images/zImage.initrd.pmac -vmlinux.elf-pmac: $(CHRPOBJS) $(LIBS) ../common/no_initrd.o $(MKNOTE) ../images/vmlinux.gz - $(LD) $(CHRP_LD_ARGS) -o ../images/$@ $(CHRPOBJS) ../common/no_initrd.o $(LIBS) +vmlinux.elf-pmac: $(CHRPOBJS) $(LIBS) $(MKNOTE) image.o + $(LD) $(CHRP_LD_ARGS) -o ../images/$@ $(CHRPOBJS) $(LIBS) image.o $(MKNOTE) > note $(OBJCOPY) ../images/$@ ../images/$@ --add-section=.note=note \ - --add-section=sysmap=$(TOPDIR)/System.map -R .comment - $(CC) $(CFLAGS) chrpmain.c -c -o chrpmain.o \ - -DSYSMAP_OFFSET=`sh $(OFFSET) $(OBJDUMP) ../images/$@ sysmap` \ - -DSYSMAP_SIZE=`sh $(SIZE) $(OBJDUMP) ../images/$@ sysmap` - $(LD) $(CHRP_LD_ARGS) -o ../images/$@ $(CHRPOBJS) ../common/no_initrd.o $(LIBS) - $(OBJCOPY) ../images/$@ ../images/$@ --add-section=.note=note \ - --add-section=sysmap=$(TOPDIR)/System.map -R .comment + -R .comment -R .ramdisk rm -f note -vmlinux.initrd.elf-pmac: $(CHRPOBJS) $(LIBS) initrd.o $(MKNOTE) ../images/vmlinux.gz - $(LD) $(CHRP_LD_ARGS) -o ../images/$@ $(CHRPOBJS) initrd.o $(LIBS) +vmlinux.initrd.elf-pmac: $(CHRPOBJS) $(LIBS) $(MKNOTE) image.o \ + ../images/ramdisk.image.gz + $(OBJCOPY) image.o image-elf.o \ + --add-section=.ramdisk=../images/ramdisk.image.gz \ + --set-section-flags=.ramdisk=contents,alloc,load,readonly,data + $(LD) $(CHRP_LD_ARGS) -o ../images/$@ $(CHRPOBJS) $(LIBS) image-elf.o $(MKNOTE) > note - $(OBJCOPY) ../images/$@ ../images/$@ --add-section=.note=note -R .comment - rm -f note + $(OBJCOPY) ../images/$@ ../images/$@ --add-section=.note=note \ + -R .comment + rm -f note image-elf.o zImage: vmlinux.coff vmlinux.elf-pmac miboot.image -zImage.initrd: vmlinux.coff.initrd vmlinux.initrd.elf-pmac miboot.image.initrd +zImage.initrd: vmlinux.initrd.coff vmlinux.initrd.elf-pmac miboot.initrd.image include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc/boot/pmac/chrpmain.c b/arch/ppc/boot/pmac/chrpmain.c --- a/arch/ppc/boot/pmac/chrpmain.c Tue Feb 19 18:08:56 2002 +++ b/arch/ppc/boot/pmac/chrpmain.c Tue Feb 19 18:08:56 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.chrpmain.c 1.16 07/27/01 20:24:18 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Copyright (C) Paul Mackerras 1997. @@ -10,15 +10,17 @@ * 2 of the License, or (at your option) any later version. */ #include "nonstdio.h" -#include "zlib.h" #include +#include + +/* Passed from the linker */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin[], __ramdisk_end; +extern char _start, _end; -extern char _end[]; -extern char image_data[], initrd_data[]; -extern int image_len, initrd_len; extern int getprop(void *, const char *, void *, int); extern unsigned int heap_max; -extern void *claim(unsigned int, unsigned int, unsigned int); +extern void *claim(unsigned int virt, unsigned int size, unsigned int align); extern void *finddevice(const char *); extern void flush_cache(void *start, unsigned int len); extern void gunzip(void *, int, unsigned char *, int *); @@ -45,21 +47,23 @@ void *dst; unsigned char *im; unsigned initrd_start, initrd_size; - extern char _start; printf("chrpboot starting: loaded at 0x%p\n", &_start); - if (initrd_len) { - initrd_size = initrd_len; + + initrd_size = (char *)(&__ramdisk_end) - (char *)(&__ramdisk_begin); + if (initrd_size) { initrd_start = (RAM_END - initrd_size) & ~0xFFF; a1 = initrd_start; a2 = initrd_size; claim(initrd_start, RAM_END - initrd_start, 0); - printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n", initrd_start, - initrd_data,initrd_size); - memcpy((char *)initrd_start, initrd_data, initrd_size); - } - im = image_data; - len = image_len; + printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n\r", + initrd_start, (char *)(&__ramdisk_begin), initrd_size); + memcpy((char *)initrd_start, (char *)(&__ramdisk_begin), initrd_size); + } else + a2 = 0xdeadbeef; + + im = (char *)(&__image_begin); + len = (char *)(&__image_end) - (char *)(&__image_begin); /* claim 3MB starting at PROG_START */ claim(PROG_START, PROG_SIZE, 0); dst = (void *) PROG_START; diff -Nru a/arch/ppc/boot/pmac/coffmain.c b/arch/ppc/boot/pmac/coffmain.c --- a/arch/ppc/boot/pmac/coffmain.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/boot/pmac/coffmain.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.coffmain.c 1.14 07/27/01 20:24:18 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Copyright (C) Paul Mackerras 1997. @@ -9,11 +9,17 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include +#include + #include "nonstdio.h" #include "zlib.h" -#include -extern char _start[], _end[]; +/* Passed from the linker */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin[], __ramdisk_end; +extern char _start, _end; + extern char *claim(unsigned, unsigned, unsigned); extern char image_data[], initrd_data[]; extern int initrd_len, image_len; @@ -50,18 +56,21 @@ printf("coffboot starting: loaded at 0x%p\n", &_start); setup_bats(RAM_START); - if (initrd_len) { - initrd_size = initrd_len; + + initrd_size = (char *)(&__ramdisk_end) - (char *)(&__ramdisk_begin); + if (initrd_size) { initrd_start = (RAM_END - initrd_size) & ~0xFFF; a1 = initrd_start; a2 = initrd_size; - claim(initrd_start - RAM_START, RAM_END - initrd_start, 0); - printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n", - initrd_start, initrd_data, initrd_size); - memcpy((char *)initrd_start, initrd_data, initrd_size); - } - im = image_data; - len = image_len; + claim(initrd_start, RAM_END - initrd_start, 0); + printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n\r", + initrd_start, (char *)(&__ramdisk_begin), initrd_size); + memcpy((char *)initrd_start, (char *)(&__ramdisk_begin), initrd_size); + } else + a2 = 0xdeadbeef; + + im = (char *)(&__image_begin); + len = (char *)(&__image_end) - (char *)(&__image_begin); /* claim 4MB starting at 0 */ claim(0, PROG_SIZE, 0); dst = (void *) RAM_START; diff -Nru a/arch/ppc/boot/pmac/dummy.c b/arch/ppc/boot/pmac/dummy.c --- a/arch/ppc/boot/pmac/dummy.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,7 +0,0 @@ -/* - * BK Id: SCCS/s.dummy.c 1.6 05/18/01 15:17:15 cort - */ -int main(void) -{ - return 0; -} diff -Nru a/arch/ppc/boot/pmac/ld.script b/arch/ppc/boot/pmac/ld.script --- a/arch/ppc/boot/pmac/ld.script Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,69 +0,0 @@ -OUTPUT_ARCH(powerpc) -SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); -/* Do we need any of these for elf? - __DYNAMIC = 0; */ -SECTIONS -{ - /* Read-only sections, merged into text segment: */ - . = + SIZEOF_HEADERS; - .interp : { *(.interp) } - .hash : { *(.hash) } - .dynsym : { *(.dynsym) } - .dynstr : { *(.dynstr) } - .rel.text : { *(.rel.text) } - .rela.text : { *(.rela.text) } - .rel.data : { *(.rel.data) } - .rela.data : { *(.rela.data) } - .rel.rodata : { *(.rel.rodata) } - .rela.rodata : { *(.rela.rodata) } - .rel.got : { *(.rel.got) } - .rela.got : { *(.rela.got) } - .rel.ctors : { *(.rel.ctors) } - .rela.ctors : { *(.rela.ctors) } - .rel.dtors : { *(.rel.dtors) } - .rela.dtors : { *(.rela.dtors) } - .rel.bss : { *(.rel.bss) } - .rela.bss : { *(.rela.bss) } - .rel.plt : { *(.rel.plt) } - .rela.plt : { *(.rela.plt) } - .init : { *(.init) } =0 - .plt : { *(.plt) } - .text : - { - *(.text) - *(.rodata) - *(.rodata.*) - *(.rodata1) - *(.got1) - } - .fini : { *(.fini) } =0 - .ctors : { *(.ctors) } - .dtors : { *(.dtors) } - _etext = .; - PROVIDE (etext = .); - /* Read-write section, merged into data segment: */ - . = (. + 0x0FFF) & 0xFFFFF000; - .data : - { - *(.data) - *(.data1) - *(.sdata) - *(.sdata2) - *(.got.plt) *(.got) - *(.dynamic) - CONSTRUCTORS - } - _edata = .; - PROVIDE (edata = .); - __bss_start = .; - .bss : - { - *(.sbss) *(.scommon) - *(.dynbss) - *(.bss) - *(COMMON) - } - _end = . ; - PROVIDE (end = .); -} - diff -Nru a/arch/ppc/boot/prep/Makefile b/arch/ppc/boot/prep/Makefile --- a/arch/ppc/boot/prep/Makefile Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/boot/prep/Makefile Tue Feb 19 18:08:59 2002 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.Makefile 1.26 09/25/01 07:54:40 trini +# BK Id: %F% %I% %G% %U% %#% # # arch/ppc/boot/Makefile # @@ -17,16 +17,15 @@ USE_STANDARD_AS_RULE := true -ifeq ($(CONFIG_SMP),y) -TFTPIMAGE = /tftpboot/zImage.prep.smp -else TFTPIMAGE = /tftpboot/zImage.prep +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE = $(TFTPBOOT).smp endif -ZLINKFLAGS = -T $(TOPDIR)/arch/$(ARCH)/vmlinux.lds \ - -Ttext 0x00800000 -obj-y := head.o misc.o ../common/misc-common.o \ - ../common/string.o of1275.o +LD_ARGS = -T ../ld.script -Ttext 0x00800000 -Bstatic +obj-y := head.o ../simple/legacy.o misc.o of1275.o \ + ../common/util.o ../common/string.o \ + ../common/misc-common.o OBJCOPY_ARGS = -O elf32-powerpc LIBS = ../lib/zlib.a @@ -38,57 +37,34 @@ SIZE := ../utils/size OFFSET := ../utils/offset -all: zImage +# Extra include search dirs +CFLAGS_kbd.o += -I$(TOPDIR)/drivers/char -misc.o: misc.c - $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \ - -DZIMAGE_SIZE=0 -c -o $@ $*.c +all: zImage -zvmlinux.initrd: $(obj-y) $(LIBS) ../images/vmlinux.gz - $(LD) $(ZLINKFLAGS) -o $@.tmp $(obj-y) $(LIBS) +zImage: $(obj-y) $(LIBS) ../ld.script ../images/vmlinux.gz ../common/dummy.o \ + $(MKPREP) $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=../images/ramdisk.image.gz \ - --add-section=image=../images/vmlinux.gz \ - $@.tmp $@ - $(CC) $(CFLAGS) -DINITRD_OFFSET=`sh $(OFFSET) $(OBJDUMP) $@ initrd` \ - -DINITRD_SIZE=`sh $(SIZE) $(OBJDUMP) $@ initrd` \ - -DZIMAGE_OFFSET=`sh $(OFFSET) $(OBJDUMP) $@ image` \ - -DZIMAGE_SIZE=`sh $(SIZE) $(OBJDUMP) $@ image` \ - -c -o misc.o misc.c - $(LD) $(ZLINKFLAGS) -o $@.tmp $(obj-y) $(LIBS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=../images/ramdisk.image.gz \ - --add-section=image=../images/vmlinux.gz \ - $@.tmp $@ - rm -f $@.tmp zvmlinux - -zImage: zvmlinux $(MKPREP) - $(MKPREP) -pbp zvmlinux ../images/$@.prep - rm -f zvmlinux - -zImage.initrd: zvmlinux.initrd $(MKPREP) - $(MKPREP) -pbp zvmlinux.initrd ../images/$@.prep - rm -f zvmlinux.initrd + --add-section=.image=../images/vmlinux.gz \ + --set-section-flags=.image=contents,alloc,load,readonly,data \ + ../common/dummy.o image.o + $(LD) $(LD_ARGS) -o $@ $(obj-y) image.o $(LIBS) + $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr + $(MKPREP) -pbp $@ ../images/$@.prep + rm -f $@ -zvmlinux: $(obj-y) $(LIBS) ../images/vmlinux.gz -# -# build the boot loader image and then compute the offset into it -# for the kernel image -# - $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(obj-y) $(LIBS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=image=../images/vmlinux.gz zvmlinux.tmp $@ -# -# then with the offset rebuild the bootloader so we know where the kernel is -# - $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ - -DZIMAGE_OFFSET=`sh $(OFFSET) $(OBJDUMP) zvmlinux image` \ - -DZIMAGE_SIZE=`sh $(SIZE) $(OBJDUMP) zvmlinux image` \ - -c -o misc.o misc.c - $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(obj-y) $(LIBS) +zImage.initrd: $(obj-y) $(LIBS) ../ld.script ../images/vmlinux.gz $(MKPREP) \ + ../common/dummy.o $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=image=../images/vmlinux.gz $@.tmp $@ - rm $@.tmp + --add-section=.ramdisk=../images/ramdisk.image.gz \ + --set-section-flags=.ramdisk=contents,alloc,load,readonly,data \ + --add-section=.image=../images/vmlinux.gz \ + --set-section-flags=.image=contents,alloc,load,readonly,data \ + ../common/dummy.o image.o + $(LD) $(LD_ARGS) -o $@ $(obj-y) image.o $(LIBS) + $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr + $(MKPREP) -pbp $@ ../images/$@.prep + rm -f $@ floppy: zImage dd if=../images/zImage.prep of=/dev/fd0H1440 bs=64b diff -Nru a/arch/ppc/boot/prep/head.S b/arch/ppc/boot/prep/head.S --- a/arch/ppc/boot/prep/head.S Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/boot/prep/head.S Tue Feb 19 18:08:58 2002 @@ -1,8 +1,8 @@ /* - * BK Id: SCCS/s.head.S 1.11 07/31/01 16:36:06 trini + * BK Id: %F% %I% %G% %U% %#% */ -#include "../../kernel/ppc_defs.h" -#include "../../kernel/ppc_asm.tmpl" + +#include #include #include @@ -22,34 +22,49 @@ start: bl start_ start_: + + /* Enable, invalidate, Disable L1 icache/dcache */ + li r8, 0 + ori r8, r8, (HID0_ICE|HID0_DCE|HID0_ICFI|HID0_DCI) + mfspr r11,HID0 + or r11,r11,r8 + andc r10,r11,r8 + isync + mtspr HID0,r8 + sync + isync + mtspr HID0,r10 + sync + isync + mr r11,r3 /* Save pointer to residual/board data */ - mr r25,r5 /* Save OFW pointer */ - li r3,MSR_IP /* Establish default MSR value */ + mr r25,r5 /* Save OFW pointer */ + + /* Save the original MSR value */ + mfmsr r26 + + /* Establish default MSR value */ + li r3,MSR_IP|MSR_FP mtmsr r3 -/* check if we need to relocate ourselves to the link addr or were we - loaded there to begin with -- Cort */ - lis r4,start@h - ori r4,r4,start@l - mflr r3 - subi r3,r3,4 /* we get the nip, not the ip of the branch */ - mr r8,r3 - cmp 0,r3,r4 - bne 1010f -/* compute size of whole image in words. this should be moved to - * start_ldr() -- Cort - */ + /* compute the size of the whole image in words. */ lis r4,start@h ori r4,r4,start@l lis r5,end@h ori r5,r5,end@l addi r5,r5,3 /* round up */ - sub r5,r5,r4 + sub r5,r5,r4 /* end - start */ srwi r5,r5,2 - mr r7,r5 - b start_ldr -1010: -/* + mr r7,r5 /* Save for later use. */ + + /* check if we need to relocate ourselves to the link addr or were + * we loaded there to begin with -- Cort */ + mflr r3 + subi r3,r3,4 /* we get the nip, not the ip of the branch */ + mr r8,r3 + cmp 0,r3,r4 + beq start_ldr /* If 0, we don't need to relocate */ +/* * no matter where we're loaded, move ourselves to -Ttext address */ relocate: @@ -58,15 +73,10 @@ mr r8,r3 lis r4,start@h ori r4,r4,start@l - lis r5,end@h - ori r5,r5,end@l - addi r5,r5,3 /* Round up - just in case */ - sub r5,r5,r4 /* Compute # longwords to move */ - srwi r5,r5,2 - mtctr r5 - mr r7,r5 + mr r5,r7 /* Get the # of longwords again */ + mtctr r5 /* Setup for loop */ li r6,0 - subi r3,r3,4 /* Set up for loop */ + subi r3,r3,4 subi r4,r4,4 00: lwzu r5,4(r3) stwu r5,4(r4) @@ -74,9 +84,17 @@ bdnz 00b lis r3,start_ldr@h ori r3,r3,start_ldr@l - mtlr r3 /* Easiest way to do an absolute jump */ + mtlr r3 /* Easiest way to do an absolute jump */ blr + start_ldr: +/* Some boards don't boot up with the I-cache enabled. Do that + * now because the decompress runs much faster that way. + * As a side effect, we have to ensure the data cache is not enabled + * so we can access the serial I/O without trouble. + */ + bl flush_instruction_cache + /* Clear all of BSS */ lis r3,edata@h ori r3,r3,edata@l @@ -95,11 +113,12 @@ subi r1,r1,256 li r2,0x000F /* Mask pointer to 16-byte boundary */ andc r1,r1,r2 -/* Setup ISA_io */ - lis r3,ISA_io@h - ori r3,r3,ISA_io@l - lis r4,0x8000 - stw r4,0(r3) + + /* Store the original MSR into 'orig_MSR' */ + lis r3,orig_MSR@h + ori r3,r3,orig_MSR@l + stw r26,0(r3) + /* Run loader */ mr r3,r8 /* Load point */ mr r4,r7 /* Program length */ @@ -107,147 +126,61 @@ mr r6,r11 /* Residual data */ mr r7,r25 /* OFW interfaces */ bl decompress_kernel - - /* changed to use r3 (as firmware does) for kernel - as ptr to residual -- Cort*/ - lis r6,cmd_line@h - ori r6,r6,cmd_line@l - lwz r6, 0(r6) - subi r7,r6,1 -00: lbzu r2,1(r7) - cmpi 0,r2,0 - bne 00b - - /* r4,r5 have initrd_start, size */ - lis r2,initrd_start@h - ori r2,r2,initrd_start@l - lwz r4,0(r2) - lis r2,initrd_end@h - ori r2,r2,initrd_end@l - lwz r5,0(r2) - - - /* tell kernel we're prep */ - /* - * get start address of kernel code which is stored as a coff - * entry. see boot/head.S -- Cort + + /* + * We have to do this after decompress_kernel, just to make + * sure we don't wipe out things mapped in BATs which we need. + * -- Tom */ - li r9,0x4 + li r6,0 + /* Test for a 601 */ + mfspr r9,PVR + srwi r9,r9,16 + cmpi 0,r9,1 /* 601 ? */ + beq .clearbats_601 + + /* Clear BATS */ + mtspr DBAT0U,r6 + mtspr DBAT0L,r6 + mtspr DBAT1U,r6 + mtspr DBAT1L,r6 + mtspr DBAT2U,r6 + mtspr DBAT2L,r6 + mtspr DBAT3U,r6 + mtspr DBAT3L,r6 +.clearbats_601: + mtspr IBAT0U,r6 + mtspr IBAT0L,r6 + mtspr IBAT1U,r6 + mtspr IBAT1L,r6 + mtspr IBAT2U,r6 + mtspr IBAT2L,r6 + mtspr IBAT3U,r6 + mtspr IBAT3L,r6 + isync + sync + sync + + /* Set segment registers */ + li r6,16 /* load up segment register values */ + mtctr r6 /* for context 0 */ + lis r6,0x2000 /* Ku = 1, VSID = 0 */ + li r10,0 +3: mtsrin r6,r10 + addi r6,r6,0x111 /* increment VSID */ + addis r10,r10,0x1000 /* address of next segment */ + bdnz 3b + + /* tell kernel we're prep, by putting 0xdeadc0de at KERNELLOAD, + * and tell the kernel to start on the 4th instruction since we + * overwrite the first 3 sometimes (which are 'nop'). + */ + li r9,0xc mtlr r9 lis r10,0xdeadc0de@h ori r10,r10,0xdeadc0de@l li r9,0 stw r10,0(r9) -/* - * The Radstone firmware maps PCI memory at 0xc0000000 using BAT2 - * so disable BATs before setting this to avoid a clash - */ - li r8,0 - mtspr DBAT0U,r8 - mtspr DBAT1U,r8 - mtspr DBAT2U,r8 - mtspr DBAT3U,r8 - mtspr IBAT0U,r8 - mtspr IBAT1U,r8 - mtspr IBAT2U,r8 - mtspr IBAT3U,r8 - - blr -hang: - b hang - -/* - * Delay for a number of microseconds - * -- Use the BUS timer (assumes 66MHz) - */ - .globl udelay -udelay: - mfspr r4,PVR - srwi r4,r4,16 - cmpi 0,r4,1 /* 601 ? */ - bne .udelay_not_601 -00: li r0,86 /* Instructions / microsecond? */ - mtctr r0 -10: addi r0,r0,0 /* NOP */ - bdnz 10b - subic. r3,r3,1 - bne 00b - blr - -.udelay_not_601: - mulli r4,r3,1000 /* nanoseconds */ - addi r4,r4,59 - li r5,60 - divw r4,r4,r5 /* BUS ticks */ -1: mftbu r5 - mftb r6 - mftbu r7 - cmp 0,r5,r7 - bne 1b /* Get [synced] base time */ - addc r9,r6,r4 /* Compute end time */ - addze r8,r5 -2: mftbu r5 - cmp 0,r5,r8 - blt 2b - bgt 3f - mftb r6 - cmp 0,r6,r9 - blt 2b -3: blr - -.globl _get_HID0 -_get_HID0: - mfspr r3,HID0 - blr - -.globl _put_HID0 -_put_HID0: - mtspr HID0,r3 - blr - -.globl _get_MSR -_get_MSR: - mfmsr r3 - blr - -.globl _put_MSR -_put_MSR: - mtmsr r3 blr -/* - * Flush instruction cache - * *** I'm really paranoid here! - */ -_GLOBAL(flush_instruction_cache) - mflr r5 - bl flush_data_cache - mfspr r3,HID0 /* Caches are controlled by this register */ - li r4,0 - ori r4,r4,(HID0_ICE|HID0_ICFI) - or r3,r3,r4 /* Need to enable+invalidate to clear */ - mtspr HID0,r3 - andc r3,r3,r4 - ori r3,r3,HID0_ICE /* Enable cache */ - mtspr HID0,r3 - mtlr r5 - blr - -#define NUM_CACHE_LINES 128*8 -#define CACHE_LINE_SIZE 32 -#define cache_flush_buffer 0x1000 - -/* - * Flush data cache - * *** I'm really paranoid here! - */ -_GLOBAL(flush_data_cache) - lis r3,cache_flush_buffer@h - ori r3,r3,cache_flush_buffer@l - li r4,NUM_CACHE_LINES - mtctr r4 -00: lwz r4,0(r3) - addi r3,r3,CACHE_LINE_SIZE /* Next line, please */ - bdnz 00b -10: blr .comm .stack,4096*2,4 diff -Nru a/arch/ppc/boot/prep/kbd.c b/arch/ppc/boot/prep/kbd.c --- a/arch/ppc/boot/prep/kbd.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/boot/prep/kbd.c Tue Feb 19 18:08:57 2002 @@ -1,9 +1,10 @@ /* - * BK Id: SCCS/s.kbd.c 1.7 05/18/01 06:20:29 patch + * BK Id: %F% %I% %G% %U% %#% */ + #include -#include <../drivers/char/defkeymap.c> /* yeah I know it's bad -- Cort */ +#include "defkeymap.c" /* yeah I know it's bad -- Cort */ unsigned char shfts, ctls, alts, caps; @@ -130,20 +131,29 @@ goto loop; } -static void kbdreset(void) +static int __kbdreset(void) { unsigned char c; - int i; + int i, t; /* flush input queue */ + t = 2000; while ((inb(KBSTATP) & KBINRDY)) { (void)inb(KBDATAP); + if (--t == 0) + return 1; } /* Send self-test */ - while (inb(KBSTATP) & KBOUTRDY) ; + t = 20000; + while (inb(KBSTATP) & KBOUTRDY) + if (--t == 0) + return 2; outb(KBSTATP,0xAA); - while ((inb(KBSTATP) & KBINRDY) == 0) ; /* wait input ready */ + t = 200000; + while ((inb(KBSTATP) & KBINRDY) == 0) /* wait input ready */ + if (--t == 0) + return 3; if ((c = inb(KBDATAP)) != 0x55) { puts("Keyboard self test failed - result:"); @@ -151,15 +161,23 @@ puts("\n"); } /* Enable interrupts and keyboard controller */ - while (inb(KBSTATP) & KBOUTRDY) ; - outb(KBSTATP,0x60); - while (inb(KBSTATP) & KBOUTRDY) ; + t = 20000; + while (inb(KBSTATP) & KBOUTRDY) + if (--t == 0) return 4; + outb(KBSTATP,0x60); + t = 20000; + while (inb(KBSTATP) & KBOUTRDY) + if (--t == 0) return 5; outb(KBDATAP,0x45); for (i = 0; i < 10000; i++) udelay(1); - - while (inb(KBSTATP) & KBOUTRDY) ; + + t = 20000; + while (inb(KBSTATP) & KBOUTRDY) + if (--t == 0) return 6; outb(KBSTATP,0x20); - while ((inb(KBSTATP) & KBINRDY) == 0) ; /* wait input ready */ + t = 200000; + while ((inb(KBSTATP) & KBINRDY) == 0) /* wait input ready */ + if (--t == 0) return 7; if (! (inb(KBDATAP) & 0x40)) { /* * Quote from PS/2 System Reference Manual: @@ -169,15 +187,31 @@ * output-buffer-full bit in the Controller Status * register are set 0." (KBINRDY and KBOUTRDY) */ - - while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) ; + t = 200000; + while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) + if (--t == 0) return 8; outb(KBDATAP,0xF0); - while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) ; + t = 200000; + while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) + if (--t == 0) return 9; outb(KBDATAP,0x01); } - - while (inb(KBSTATP) & KBOUTRDY) ; + t = 20000; + while (inb(KBSTATP) & KBOUTRDY) + if (--t == 0) return 10; outb(KBSTATP,0xAE); + return 0; +} + +static void kbdreset(void) +{ + int ret = __kbdreset(); + + if (ret) { + puts("__kbdreset failed: "); + puthex(ret); + puts("\n"); + } } /* We have to actually read the keyboard when CRT_tstc is called, diff -Nru a/arch/ppc/boot/prep/misc.c b/arch/ppc/boot/prep/misc.c --- a/arch/ppc/boot/prep/misc.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/boot/prep/misc.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.misc.c 1.20 09/24/01 18:42:54 trini + * BK Id: %F% %I% %G% %U% %#% * * arch/ppc/boot/prep/misc.c * @@ -32,12 +32,16 @@ */ char *avail_ram; char *end_avail; + +/* The linker tells us where the image is. */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin, __ramdisk_end; extern char _end[]; #ifdef CONFIG_CMDLINE #define CMDLINE CONFIG_CMDLINE #else -#define CMDLINE ""; +#define CMDLINE "" #endif char cmd_preset[] = CMDLINE; char cmd_buf[256]; @@ -46,16 +50,9 @@ int keyb_present = 1; /* keyboard controller is present by default */ RESIDUAL hold_resid_buf; RESIDUAL *hold_residual = &hold_resid_buf; -unsigned long initrd_start = 0, initrd_end = 0; +unsigned long initrd_size = 0; +unsigned long orig_MSR; -/* These values must be variables. If not, the compiler optimizer - * will remove some code, causing the size of the code to vary - * when these values are zero. This is bad because we first - * compile with these zero to determine the size and offsets - * in an image, than compile again with these set to the proper - * discovered value. - */ -unsigned int initrd_offset, initrd_size; char *zimage_start; int zimage_size; @@ -71,16 +68,14 @@ extern int CRT_tstc(void); extern void of_init(void *handler); extern int of_finddevice(const char *device_specifier, int *phandle); -extern int of_getprop(int phandle, const char *name, void *buf, int buflen, +extern int of_getprop(int phandle, const char *name, void *buf, int buflen, int *size); extern int vga_init(unsigned char *ISA_mem); extern void gunzip(void *, int, unsigned char *, int *); -extern void _put_HID0(unsigned int val); extern void _put_MSR(unsigned int val); -extern unsigned int _get_HID0(void); -extern unsigned int _get_MSR(void); -extern unsigned long serial_init(int chan); +extern unsigned long serial_init(int chan, void *ignored); +extern void setup_legacy(void); void writel(unsigned int val, unsigned int address) @@ -90,7 +85,7 @@ *(unsigned int *)address = cpu_to_le32(val); } -unsigned int +unsigned int readl(unsigned int address) { /* Ensure I/O operations complete */ @@ -98,8 +93,8 @@ return le32_to_cpu(*(unsigned int *)address); } -#define PCI_CFG_ADDR(dev,off) ((0x80<<24) | (dev<<8) | (off&0xfc)) -#define PCI_CFG_DATA(off) (0x80000cfc+(off&3)) +#define PCI_CFG_ADDR(dev,off) ((0x80<<24) | (dev<<8) | (off&0xfc)) +#define PCI_CFG_DATA(off) (0x80000cfc+(off&3)) static void pci_read_config_32(unsigned char devfn, @@ -123,38 +118,14 @@ } #endif /* CONFIG_VGA_CONSOLE */ -/* - * This routine is used to control the second processor on the - * Motorola dual processor platforms. - */ -void -park_cpus(void) -{ - volatile void (*go)(RESIDUAL *, int, int, char *, int); - unsigned int i; - volatile unsigned long *smp_iar = &(hold_residual->VitalProductData.SmpIar); - - /* Wait for indication to continue. If the kernel - was not compiled with SMP support then the second - processor will spin forever here makeing the kernel - multiprocessor safe. */ - while (*smp_iar == 0) { - for (i=0; i < 512; i++); - } - - (unsigned long)go = hold_residual->VitalProductData.SmpIar; - go(hold_residual, 0, 0, cmd_line, sizeof(cmd_preset)); -} - unsigned long decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual, void *OFW_interface) { - int timer; + int timer = 0; extern unsigned long start; char *cp, ch; unsigned long TotalMemory; - unsigned long orig_MSR; int dev_handle; int mem_info[2]; int res, size; @@ -162,26 +133,42 @@ unsigned char base_mod; int start_multi = 0; unsigned int pci_viddid, pci_did, tulip_pci_base, tulip_base; - - /* - * IBM's have the MMU on, so we have to disable it or - * things get really unhappy in the kernel when - * trying to setup the BATs with the MMU on - * -- Cort - */ - flush_instruction_cache(); - _put_HID0(_get_HID0() & ~0x0000C000); - _put_MSR((orig_MSR = _get_MSR()) & ~0x0030); + setup_legacy(); #if defined(CONFIG_SERIAL_CONSOLE) - com_port = serial_init(0); + com_port = serial_init(0, NULL); #endif /* CONFIG_SERIAL_CONSOLE */ #if defined(CONFIG_VGA_CONSOLE) vga_init((unsigned char *)0xC0000000); #endif /* CONFIG_VGA_CONSOLE */ - if (residual) - { + /* + * Tell the user where we were loaded at and where we were relocated + * to for debugging this process. + */ + puts("loaded at: "); puthex(load_addr); + puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); + if ( (unsigned long)load_addr != (unsigned long)&start ) { + puts("relocated to: "); puthex((unsigned long)&start); + puts(" "); + puthex((unsigned long)((unsigned long)&start + (4*num_words))); + puts("\n"); + } + + if (residual) { + /* + * Tell the user where the residual data is. + */ + puts("board data at: "); puthex((unsigned long)residual); + puts(" "); + puthex((unsigned long)((unsigned long)residual + + sizeof(RESIDUAL))); + puts("\nrelocated to: ");puthex((unsigned long)hold_residual); + puts(" "); + puthex((unsigned long)((unsigned long)hold_residual + + sizeof(RESIDUAL))); + puts("\n"); + /* Is this Motorola PPCBug? */ if ((1 & residual->VitalProductData.FirmwareSupports) && (1 == residual->VitalProductData.FirmwareSupplier)) { @@ -198,8 +185,7 @@ ((pci_did == PCI_DEVICE_ID_DEC_TULIP_FAST) || (pci_did == PCI_DEVICE_ID_DEC_TULIP) || (pci_did == PCI_DEVICE_ID_DEC_TULIP_PLUS) || - (pci_did == PCI_DEVICE_ID_DEC_21142))) - { + (pci_did == PCI_DEVICE_ID_DEC_21142))) { pci_read_config_32(0x70, 0x10, &tulip_pci_base); @@ -213,7 +199,7 @@ /* If this is genesis 2 board then check for no * keyboard controller and more than one processor. */ - if (board_type == 0xe0) { + if (board_type == 0xe0) { base_mod = inb(0x803); /* if a MVME2300/2400 or a Sitka then no keyboard */ if((base_mod == 0xFA) || (base_mod == 0xF9) || @@ -225,14 +211,17 @@ * park the other processor so that the * kernel knows where to find them. */ - if (residual->MaxNumCpus > 1) { + if (residual->MaxNumCpus > 1) start_multi = 1; - } } memcpy(hold_residual,residual,sizeof(RESIDUAL)); } else { + /* Tell the user we didn't find anything. */ + puts("No residual data found.\n"); + /* Assume 32M in the absence of more info... */ TotalMemory = 0x02000000; + /* * This is a 'best guess' check. We want to make sure * we don't try this on a PReP box without OF @@ -240,118 +229,66 @@ */ while (OFW_interface && ((unsigned long)OFW_interface < 0x10000000) ) { - /* The MMU needs to be on when we call OFW */ + /* We need to restore the slightly inaccurate + * MSR so that OpenFirmware will behave. -- Tom + */ _put_MSR(orig_MSR); of_init(OFW_interface); /* get handle to memory description */ - res = of_finddevice("/memory@0", + res = of_finddevice("/memory@0", &dev_handle); - // puthex(res); puts("\n"); - if (res) break; - + if (res) + break; + /* get the info */ - // puts("get info = "); - res = of_getprop(dev_handle, - "reg", - mem_info, - sizeof(mem_info), + res = of_getprop(dev_handle, + "reg", + mem_info, + sizeof(mem_info), &size); - // puthex(res); puts(", info = "); puthex(mem_info[0]); - // puts(" "); puthex(mem_info[1]); puts("\n"); - if (res) break; - + if (res) + break; + TotalMemory = mem_info[1]; break; } + hold_residual->TotalMemory = TotalMemory; residual = hold_residual; - /* Turn MMU back off */ - _put_MSR(orig_MSR & ~0x0030); - } - if (start_multi) { - hold_residual->VitalProductData.SmpIar = 0; - hold_residual->Cpus[1].CpuState = CPU_GOOD_FW; - residual->VitalProductData.SmpIar = (unsigned long)park_cpus; - residual->Cpus[1].CpuState = CPU_GOOD; - hold_residual->VitalProductData.Reserved5 = 0xdeadbeef; - } + /* Enforce a sane MSR for booting. */ + _put_MSR(MSR_IP); + } /* assume the chunk below 8M is free */ end_avail = (char *)0x00800000; - /* tell the user where we were loaded at and where we - * were relocated to for debugging this process + /* + * We link ourself to 0x00800000. When we run, we relocate + * ourselves there. So we just need __image_begin for the + * start. -- Tom */ - puts("loaded at: "); puthex(load_addr); - puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); - if ( (unsigned long)load_addr != (unsigned long)&start ) - { - puts("relocated to: "); puthex((unsigned long)&start); - puts(" "); - puthex((unsigned long)((unsigned long)&start + (4*num_words))); - puts("\n"); - } - - if ( residual ) - { - puts("board data at: "); puthex((unsigned long)residual); - puts(" "); - puthex((unsigned long)((unsigned long)residual + sizeof(RESIDUAL))); - puts("\n"); - puts("relocated to: "); - puthex((unsigned long)hold_residual); - puts(" "); - puthex((unsigned long)((unsigned long)hold_residual + sizeof(RESIDUAL))); - puts("\n"); - } + zimage_start = (char *)(unsigned long)(&__image_begin); + zimage_size = (unsigned long)(&__image_end) - + (unsigned long)(&__image_begin); - /* we have to subtract 0x10000 here to correct for objdump including the - size of the elf header which we strip -- Cort */ - zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET); - zimage_size = ZIMAGE_SIZE; - initrd_offset = INITRD_OFFSET; - initrd_size = INITRD_SIZE; - - if ( initrd_offset ) - initrd_start = load_addr - 0x10000 + initrd_offset; - else - initrd_start = 0; - initrd_end = initrd_size + initrd_start; + initrd_size = (unsigned long)(&__ramdisk_end) - + (unsigned long)(&__ramdisk_begin); /* - * Find a place to stick the zimage and initrd and - * relocate them if we have to. -- Cort + * The zImage and initrd will be between start and _end, so they've + * already been moved once. We're good to go now. -- Tom */ avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); puts("zimage at: "); puthex((unsigned long)zimage_start); - puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); - if ( (unsigned long)zimage_start <= 0x00800000 ) - { - memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size ); - zimage_start = (char *)avail_ram; - puts("relocated to: "); puthex((unsigned long)zimage_start); - puts(" "); - puthex((unsigned long)zimage_size+(unsigned long)zimage_start); - puts("\n"); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); + puts("\n"); - /* relocate initrd */ - if ( initrd_start ) - { - puts("initrd at: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); - avail_ram = (char *)PAGE_ALIGN( - (unsigned long)zimage_size+(unsigned long)zimage_start); - memcpy ((void *)avail_ram, (void *)initrd_start, initrd_size ); - initrd_start = (unsigned long)avail_ram; - initrd_end = initrd_start + initrd_size; - puts("relocated to: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); - } - } else if ( initrd_start ) { - puts("initrd at: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); + if ( initrd_size ) { + puts("initrd at: "); + puthex((unsigned long)(&__ramdisk_begin)); + puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n"); } avail_ram = (char *)0x00400000; @@ -363,10 +300,10 @@ CRT_tstc(); /* Forces keyboard to be initialized */ puts("\nLinux/PPC load: "); - timer = 0; cp = cmd_line; memcpy (cmd_line, cmd_preset, sizeof(cmd_preset)); - while ( *cp ) putc(*cp++); + while ( *cp ) + putc(*cp++); while (timer++ < 5*1000) { if (tstc()) { while ((ch = getc()) != '\n' && ch != '\r') { @@ -392,26 +329,24 @@ udelay(1000); /* 1 msec */ } *cp = 0; - puts("\n"); + puts("\nUncompressing Linux..."); - /* mappings on early boot can only handle 16M */ - if ( (int)(cmd_line[0]) > (16<<20)) - puts("cmd_line located > 16M\n"); - if ( (int)hold_residual > (16<<20)) - puts("hold_residual located > 16M\n"); - if ( initrd_start > (16<<20)) - puts("initrd_start located > 16M\n"); - - puts("Uncompressing Linux..."); - gunzip(0, 0x400000, zimage_start, &zimage_size); puts("done.\n"); - + + if (start_multi) { + puts("Parking cpu1 at 0xc0\n"); + residual->VitalProductData.SmpIar = (unsigned long)0xc0; + residual->Cpus[1].CpuState = CPU_GOOD; + hold_residual->VitalProductData.Reserved5 = 0xdeadbeef; + } + { struct bi_record *rec; - - rec = (struct bi_record *)_ALIGN((unsigned long)(zimage_size)+(1<<20)-1,(1<<20)); - + + rec = (struct bi_record *)_ALIGN((unsigned long)(zimage_size) + + (1 << 20) - 1, (1 << 20)); + rec->tag = BI_FIRST; rec->size = sizeof(struct bi_record); rec = (struct bi_record *)((unsigned long)rec + rec->size); @@ -420,31 +355,33 @@ memcpy( (void *)rec->data, "prepboot", 9); rec->size = sizeof(struct bi_record) + 8 + 1; rec = (struct bi_record *)((unsigned long)rec + rec->size); - + rec->tag = BI_MACHTYPE; rec->data[0] = _MACH_prep; rec->data[1] = 0; - rec->size = sizeof(struct bi_record) + 2 * sizeof(unsigned long); + rec->size = sizeof(struct bi_record) + 2 * + sizeof(unsigned long); rec = (struct bi_record *)((unsigned long)rec + rec->size); - + rec->tag = BI_CMD_LINE; memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1); rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1; - rec = (struct bi_record *)((ulong)rec + rec->size); - + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + if ( initrd_size ) { + rec->tag = BI_INITRD; + rec->data[0] = (unsigned long)(&__ramdisk_begin); + rec->data[1] = initrd_size; + rec->size = sizeof(struct bi_record) + 2 * + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + + rec->size); + } + rec->tag = BI_LAST; rec->size = sizeof(struct bi_record); rec = (struct bi_record *)((unsigned long)rec + rec->size); } puts("Now booting the kernel\n"); return (unsigned long)hold_residual; -} - -/* - * PCI/ISA I/O support - */ -unsigned long -local_to_PCI(unsigned long addr) -{ - return (addr | 0x80000000); } diff -Nru a/arch/ppc/boot/prep/vreset.c b/arch/ppc/boot/prep/vreset.c --- a/arch/ppc/boot/prep/vreset.c Tue Feb 19 18:08:56 2002 +++ b/arch/ppc/boot/prep/vreset.c Tue Feb 19 18:08:56 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.vreset.c 1.11 07/25/01 18:13:07 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * vreset.c @@ -29,7 +29,7 @@ struct VaRegs; /* - * VGA Register + * VGA Register */ struct VgaRegs { @@ -108,51 +108,6 @@ { ENDMK } }; -struct VgaRegs S3TextRegs[NREGS+1] = { - /* port index value */ - /* SR Regs */ - { 0x3c4, 0x1, 0x0 }, - { 0x3c4, 0x2, 0x3 }, - { 0x3c4, 0x3, 0x0 }, - { 0x3c4, 0x4, 0x2 }, - /* CR Regs */ - { 0x3d4, 0x0, 0x5f }, - { 0x3d4, 0x1, 0x4f }, - { 0x3d4, 0x2, 0x50 }, - { 0x3d4, 0x3, 0x82 }, - { 0x3d4, 0x4, 0x55 }, - { 0x3d4, 0x5, 0x81 }, - { 0x3d4, 0x6, 0xbf }, - { 0x3d4, 0x7, 0x1f }, - { 0x3d4, 0x8, 0x00 }, - { 0x3d4, 0x9, 0x4f }, - { 0x3d4, 0xa, 0x0d }, - { 0x3d4, 0xb, 0x0e }, - { 0x3d4, 0xc, 0x00 }, - { 0x3d4, 0xd, 0x00 }, - { 0x3d4, 0xe, 0x00 }, - { 0x3d4, 0xf, 0x00 }, - { 0x3d4, 0x10, 0x9c }, - { 0x3d4, 0x11, 0x8e }, - { 0x3d4, 0x12, 0x8f }, - { 0x3d4, 0x13, 0x28 }, - { 0x3d4, 0x14, 0x1f }, - { 0x3d4, 0x15, 0x96 }, - { 0x3d4, 0x16, 0xb9 }, - { 0x3d4, 0x17, 0xa3 }, - /* GR Regs */ - { 0x3ce, 0x0, 0x0 }, - { 0x3ce, 0x1, 0x0 }, - { 0x3ce, 0x2, 0x0 }, - { 0x3ce, 0x3, 0x0 }, - { 0x3ce, 0x4, 0x0 }, - { 0x3ce, 0x5, 0x10 }, - { 0x3ce, 0x6, 0xe }, - { 0x3ce, 0x7, 0x0 }, - { 0x3ce, 0x8, 0xff }, - { ENDMK } -}; - struct RGBColors { unsigned char r, g, b; @@ -161,9 +116,9 @@ /* * Default console text mode color table. * These values were obtained by booting Linux with - * text mode firmware & then dumping the registers. + * text mode firmware & then dumping the registers. */ -struct RGBColors TextCLUT[256] = +struct RGBColors TextCLUT[256] = { /* red green blue */ { 0x0, 0x0, 0x0 }, @@ -424,8 +379,8 @@ }; unsigned char AC[21] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, - 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00}; static int scanPCI(int start_slt); @@ -443,20 +398,13 @@ outb(port, val >> 8); outb(port+1, val); } - -#define PPC_601 1 int vga_init(unsigned char *ISA_mem) { int slot; struct VgaRegs *VgaTextRegs; -#if 0 - if ((_get_PVR()>>16) == PPC_601) { - return(old_vga_init(ISA_mem)); - } -#endif - + /* See if VGA already in TEXT mode - exit if so! */ outb(0x3CE, 0x06); if ((inb(0x3CF) & 0x01) == 0){ @@ -470,20 +418,19 @@ while((slot = scanPCI(slot)) > -1) { /* find video card in use */ unlockVideo(slot); /* enable I/O to card */ VgaTextRegs = GenVgaTextRegs; - + switch (PCIVendor(slot)) { default: break; case(S3Vendor): unlockS3(); - VgaTextRegs = S3TextRegs; break; - + case(CirrusVendor): outw(0x3C4, 0x0612); /* unlock ext regs */ outw(0x3C4, 0x0700); /* reset ext sequence mode */ break; - + case(ParadiseVendor): /* IBM Portable 850 */ outw(0x3ce, 0x0f05); /* unlock pardise registers */ outw(0x3c4, 0x0648); @@ -508,7 +455,7 @@ } outw(0x3d4, 0x34a0); break; - + #if 0 /* Untested - probably doesn't work */ case(MatroxVendor): case(DiamondVendor): @@ -518,7 +465,7 @@ mdelay(1000); #endif }; - + outw(0x3C4, 0x0120); /* disable video */ setTextRegs(VgaTextRegs); /* initial register setup */ setTextCLUT(0); /* load color lookup table */ @@ -526,23 +473,23 @@ setTextRegs(VgaTextRegs); /* reload registers */ outw(0x3C4, 0x0100); /* re-enable video */ clearVideoMemory(); - + if (PCIVendor(slot) == S3Vendor) { outb(0x3c2, 0x63); /* MISC */ } /* endif */ - + #ifdef DEBUG printslots(); mdelay(5000); #endif - + mdelay(1000); /* give time for the video monitor to come up */ } return (1); /* 'CRT' I/O supported */ } /* - * Write to VGA Attribute registers. + * Write to VGA Attribute registers. */ void writeAttr(unsigned char index, unsigned char data, unsigned char videoOn) @@ -563,18 +510,18 @@ /* * saved settings - */ + */ while( svp->io_port != ENDMK ) { outb(svp->io_port, svp->io_index); outb(svp->io_port+1, svp->io_value); - svp++; + svp++; } outb(0x3c2, 0x67); /* MISC */ outb(0x3c6, 0xff); /* MASK */ for ( i = 0; i < 0x10; i++) - writeAttr(i, AC[i], 0); /* pallete */ + writeAttr(i, AC[i], 0); /* pallete */ writeAttr(0x10, 0x0c, 0); /* text mode */ writeAttr(0x11, 0x00, 0); /* overscan color (border) */ writeAttr(0x12, 0x0f, 0); /* plane enable */ @@ -587,9 +534,9 @@ { int i; - outb(0x3C6, 0xFF); + outb(0x3C6, 0xFF); i = inb(0x3C7); - outb(0x3C8, 0); + outb(0x3C8, 0); i = inb(0x3C7); for ( i = 0; i < 256; i++) { @@ -604,14 +551,14 @@ { int i, j; unsigned char *font_page = (unsigned char *) &ISA_mem[0xA0000]; - + outb(0x3C2, 0x67); - /* - * Load font + /* + * Load font */ i = inb(0x3DA); /* Reset Attr toggle */ - outb(0x3C0,0x30); + outb(0x3C0,0x30); outb(0x3C0, 0x01); /* graphics mode */ outw(0x3C4, 0x0001); /* reset sequencer */ @@ -706,7 +653,7 @@ } /* - * cursor() sets an offset (0-1999) into the 80x25 text area + * cursor() sets an offset (0-1999) into the 80x25 text area. */ void cursor(int x, int y) @@ -737,14 +684,14 @@ #define NPCIREGS 5 -/* - should use devfunc number/indirect method to be totally safe on - all machines, this works for now on 3 slot Moto boxes +/* + should use devfunc number/indirect method to be totally safe on + all machines, this works for now on 3 slot Moto boxes */ struct PCI_ConfigInfo { unsigned long * config_addr; - unsigned long regs[NPCIREGS]; + unsigned long regs[NPCIREGS]; } PCI_slots [NSLOTS] = { { (unsigned long *)0x80808000, 0xDEADBEEF }, /* onboard */ @@ -761,10 +708,10 @@ /* * The following code modifies the PCI Command register - * to enable memory and I/O accesses. - */ + * to enable memory and I/O accesses. + */ void -unlockVideo(int slot) +unlockVideo(int slot) { volatile unsigned char * ppci; @@ -811,13 +758,13 @@ pslot->regs[r] = SwapBytes ( pslot->config_addr[r] ); } /* card in slot ? */ - if ( pslot->regs[DEVID] != 0xFFFFFFFF ) { + if ( pslot->regs[DEVID] != 0xFFFFFFFF ) { /* VGA ? */ - if ( ((pslot->regs[CLASS] & 0xFFFFFF00) == 0x03000000) || + if ( ((pslot->regs[CLASS] & 0xFFFFFF00) == 0x03000000) || ((pslot->regs[CLASS] & 0xFFFFFF00) == 0x00010000)) { highVgaSlot = slt; /* did firmware enable it ? */ - if ( (pslot->regs[CMD] & 0x03) ) { + if ( (pslot->regs[CMD] & 0x03) ) { theSlot = slt; break; } @@ -840,7 +787,7 @@ #ifdef DEBUG static -void printslots(void) +void printslots(void) { int i; #if 0 @@ -853,7 +800,7 @@ i, pslot->config_addr, pslot->regs[0], pslot->regs[2]); #else puts("PCI Slot number: "); puthex(i); - puts(" Vendor ID: "); + puts(" Vendor ID: "); puthex(PCIVendor(i)); puts("\n"); #endif } diff -Nru a/arch/ppc/boot/simple/Makefile b/arch/ppc/boot/simple/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,261 @@ +# This is far from simple, but I couldn't think of a good name. This is +# for making the 'zImage' or 'zImage.initrd' on a number of targets. +# +# Author: Tom Rini +# +# Copyright 2001 MontaVista Software Inc. +# +# Notes: For machine targets which produce more than one image, define +# ZNETBOOT and ZNETBOOTRD to the image which should be available for +# 'znetboot' and 'znetboot.initrd` +# +# 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. + +USE_STANDARD_AS_RULE := true + +# Normally, we use the 'misc-simple.c' file for decompress_kernel and +# whatnot. Sometimes we need to override this however. +MISC := ../common/misc-simple.o +ifeq ($(CONFIG_TREEBOOT),y) +ZIMAGE := zImage-TREE +ZIMAGEINITRD := zImage.initrd-TREE +TFTPIMAGE := /tftpboot/zImage.embedded +MISC := misc-embedded.o +endif +ifeq ($(CONFIG_EMBEDDEDBOOT),y) +ZIMAGE := zImage-EMBEDDED +ZIMAGEINITRD := zImage.initrd-EMBEDDED +TFTPIMAGE := /tftpboot/zImage.embedded +MISC := misc-embedded.o +endif +ifeq ($(CONFIG_EV64260),y) +ZIMAGE := zImage-EV64260 +ZIMAGEINITRD := zImage.initrd-EV64260 +HEADHELP := direct.o misc-ev64260.o +TFTPIMAGE := /tftpboot/zImage.ev64260 +endif +ifeq ($(CONFIG_GEMINI),y) +ZIMAGE := zImage-SMON +ZIMAGEINITRD := zImage.initrd-SMON +HEADHELP := direct.o +TFTPIMAGE := /tftpboot/zImage.gemini +endif +ifeq ($(CONFIG_MENF1),y) +ZIMAGE := zImage-MENF1 +ZIMAGEINITRD := zImage.initrd-MENF1 +HEADHELP := chrpmap.o +TFTPIMAGE := /tftpboot/zImage.menf1 +endif +ifeq ($(CONFIG_K2),y) +ZIMAGE := zImage-K2 +ZIMAGEINITRD := zImage.initrd-K2 +HEADHELP := legacy.o +TFTPIMAGE := /tftpboot/zImage.k2 +endif +# kbuild-2.4 'feature', only one of these will ever by 'y' at a time. +# The rest will be unset. +ifeq ($(CONFIG_MCPN765)$(CONFIG_MVME5100)$(CONFIG_PRPMC750)$(CONFIG_PRPMC800)$(CONFIG_LOPEC)$(CONFIG_PPLUS),y) +ZIMAGE := zImage-PPLUS +ZIMAGEINITRD := zImage.initrd-PPLUS +HEADHELP := direct.o +TFTPIMAGE := /tftpboot/zImage.pplus +ZNETBOOT := zImage.pplus +ZNETBOOTRD := zImage.initrd.pplus +endif +ifeq ($(CONFIG_PPLUS),y) +HEADHELP := legacy.o +endif +ifeq ($(CONFIG_PCORE),y) +ZIMAGE := zImage-PCORE +ZIMAGEINITRD := zImage.initrd-PCORE +HEADHELP := chrpmap.o +TFTPIMAGE := /tftpboot/zImage.pcore +endif +#Ugh, should come up with a better nameing convention.. +ifeq ($(CONFIG_POWERPMC250),y) +ZIMAGE := zImage-PCORE +ZIMAGEINITRD := zImage.initrd-PCORE +HEADHELP := direct.o +TFTPIMAGE := /tftpboot/zImage.pcore +endif +ifeq ($(CONFIG_SANDPOINT),y) +ZIMAGE := zImage-SP +ZIMAGEINITRD := zImage.initrd-SP +HEADHELP := direct.o +TFTPIMAGE := /tftpboot/zImage.sandpoint +endif +ifeq ($(CONFIG_SPRUCE),y) +ZIMAGE := zImage-SPRUCE +ZIMAGEINITRD := zImage.initrd-SPRUCE +HEADHELP := direct.o +MISC := misc-spruce.o +TFTPIMAGE := /tftpboot/zImage.spruce +endif +ifeq ($(CONFIG_ZX4500),y) +ZIMAGE := zImage-ZX4500 +ZIMAGEINITRD := zImage.initrd-ZX4500 +HEADHELP := direct.o +TFTPIMAGE := /tftpboot/zImage.zx4500 +endif +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE += .smp +endif +ifeq ($(CONFIG_REDWOOD_4),y) +# This is a treeboot that needs init functions until the +# boot rom is sorted out (i.e. this is short lived) +EXTRA_AFLAGS := -Wa,-m405 +HEADHELP := rw4/rw4_init.o rw4/rw4_init_brd.o +endif + +# Default linker args. Link at 0x00800000 or 0x00400000 by default, but +# allow it to be overridden. +ifeq ($(CONFIG_BOOT_LOAD_BOOL),y) +LD_ARGS := -T ../ld.script -Ttext $(CONFIG_BOOT_LOAD) \ + -Bstatic +else +LD_ARGS = -T ../ld.script -Ttext 0x00800000 -Bstatic +ifeq ($(CONFIG_8260)$(CONFIG_4xx)$(CONFIG_8xx),y) +LD_ARGS := -T ../ld.script -Ttext 0x00400000 -Bstatic +endif +endif +OBJCOPY_ARGS := -O elf32-powerpc + +# head.o and ../common/relocate.o must be at the start. +obj-y := head.o ../common/relocate.o $(HEADHELP) \ + $(MISC) ../common/misc-common.o \ + ../common/string.o ../common/util.o +obj-$(CONFIG_4xx) += embed_config.o +obj-$(CONFIG_8xx) += embed_config.o +obj-$(CONFIG_8260) += embed_config.o +obj-$(CONFIG_BSEIP) += iic.o +obj-$(CONFIG_MBX) += iic.o pci.o qspan_pci.o +obj-$(CONFIG_RPXCLASSIC) += iic.o pci.o qspan_pci.o +obj-$(CONFIG_RPXLITE) += iic.o +# Different boards need different serial implementations. +ifeq ($(CONFIG_SERIAL_CONSOLE),y) +obj-$(CONFIG_8xx) += m8xx_tty.o +obj-$(CONFIG_8260) += m8260_tty.o +obj-$(CONFIG_GT64260_CONSOLE) += gt64260_tty.o +obj-$(CONFIG_SERIAL) += ../common/ns16550.o +endif + +LIBS := ../lib/zlib.a + +# Tools +MKBUGBOOT := ../utils/mkbugboot +MKPREP := ../utils/mkprep +MKTREE := ../utils/mktree + +zvmlinux: $(obj-y) $(LIBS) ../ld.script ../images/vmlinux.gz ../common/dummy.o + $(OBJCOPY) $(OBJCOPY_ARGS) \ + --add-section=.image=../images/vmlinux.gz \ + --set-section-flags=.image=contents,alloc,load,readonly,data \ + ../common/dummy.o image.o + $(LD) $(LD_ARGS) -o $@ $(obj-y) image.o $(LIBS) + $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr \ + -R .ramdisk -R .sysmap + +zvmlinux.initrd: $(obj-y) $(LIBS) ../ld.script ../images/vmlinux.gz \ + ../common/dummy.o + $(OBJCOPY) $(OBJCOPY_ARGS) \ + --add-section=.ramdisk=../images/ramdisk.image.gz \ + --set-section-flags=.ramdisk=contents,alloc,load,readonly,data \ + --add-section=.image=../images/vmlinux.gz \ + --set-section-flags=.image=contents,alloc,load,readonly,data \ + ../common/dummy.o image.o + $(LD) $(LD_ARGS) -o $@ $(obj-y) image.o $(LIBS) + $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr \ + -R .sysmap + +# Sort-of dummy rules, that let us format the image we want. +zImage: $(ZIMAGE) + rm -f zvmlinux +zImage.initrd: $(ZIMAGEINITRD) + rm -f zvmlinux.initrd + +znetboot: zImage +ifneq ($(ZNETBOOT),) + cp ../images/$(ZNETBOOT) $(TFTPIMAGE) +else + cp ../images/zImage.* $(TFTPIMAGE) +endif + +znetboot.initrd: zImage.initrd +ifneq ($(ZNETBOOTRD),) + cp ../images/$(ZNETBOOTRD) $(TFTPIMAGE) +else + cp ../images/zImage.* $(TFTPIMAGE) +endif + +zImage-EMBEDDED: zvmlinux + mv zvmlinux ../images/zImage.embedded + +zImage.initrd-EMBEDDED: zvmlinux.initrd + mv zvmlinux.initrd ../images/zImage.initrd.embedded + +zImage-K2: zvmlinux + mv zvmlinux ../images/zImage.k2 + +zImage.initrd-K2: zvmlinux.initrd + mv zvmlinux.initrd ../images/zImage.initrd.k2 + +zImage-EV64260: zvmlinux + mv zvmlinux ../images/zImage.ev64260 + +zImage.initrd-EV64260: zvmlinux.initrd + mv zvmlinux.initrd ../images/zImage.initrd.ev64260 + +zImage-MENF1: zvmlinux + $(MKPREP) -pbp zvmlinux ../images/zImage.menf1 + +zImage.initrd-MENF1: zvmlinux.initrd + $(MKPREP) -pbp zvmlinux.initrd ../images/zImage.initrd.menf1 + +zImage-PCORE: zvmlinux + dd if=zvmlinux of=../images/zImage.pcore skip=64 bs=1k + +zImage.initrd-PCORE: zvmlinux.initrd + dd if=zvmlinux.initrd of=../images/zImage.initrd.pcore skip=64 bs=1k + +zImage-PPLUS: zvmlinux $(MKPREP) $(MKBUGBOOT) + $(MKPREP) -pbp zvmlinux ../images/zImage.pplus + $(MKBUGBOOT) zvmlinux ../images/zImage.bugboot + +zImage.initrd-PPLUS: zvmlinux.initrd $(MKPREP) $(MKBUGBOOT) + $(MKPREP) -pbp zvmlinux.initrd ../images/zImage.initrd.pplus + $(MKBUGBOOT) zvmlinux.initrd ../images/zImage.initrd.bugboot + +zImage-SMON: zvmlinux + dd if=zvmlinux of=../images/zImage.gemini skip=64 bs=1k + +zImage.initrd-SMON: zvmlinux.initrd + dd if=zvmlinux.initrd of=../images/zImage.initrd.gemini skip=64 bs=1k + +zImage-SP: zvmlinux + mv zvmlinux ../images/zImage.sandpoint + +zImage.initrd-SP: zvmlinux.initrd + mv zvmlinux.initrd ../images/zImage.initrd.sandpoint + +zImage-SPRUCE: zvmlinux + $(MKTREE) zvmlinux ../images/zImage.spruce 0x800000 + +zImage.initrd-SPRUCE: zvmlinux.initrd + $(MKTREE) zvmlinux.initrd ../images/zImage.initrd.spruce 0x800000 + +zImage-TREE: zvmlinux + $(MKTREE) zvmlinux ../images/zImage.treeboot + +zImage.initrd-TREE: zvmlinux.initrd + $(MKTREE) zvmlinux.initrd ../images/zImage.initrd.treeboot + +zImage-ZX4500: zvmlinux + dd if=zvmlinux of=../images/zImage.zx4500 skip=64 bs=1k + +zImage.initrd-ZX4500: zvmlinux.initrd + dd if=zvmlinux.initrd of=../images/zImage.initrd.zx4500 skip=64 bs=1k + +include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc/boot/simple/chrpmap.S b/arch/ppc/boot/simple/chrpmap.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/chrpmap.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,19 @@ +/* + * arch/ppc/boot/simple/chrpmap.S + * + * Author: Tom Rini + * + * This will go and setup ISA_io to 0xFE00000. + */ + +#include + + .text + + .globl setup_legacy +setup_legacy: + lis r3,ISA_io@h /* Load ISA_io */ + ori r3,r3,ISA_io@l + lis r4,0xFE00 /* Load the value, 0xFE00000 */ + stw r4,0(r3) /* store */ + blr diff -Nru a/arch/ppc/boot/simple/direct.S b/arch/ppc/boot/simple/direct.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/direct.S Tue Feb 19 18:09:01 2002 @@ -0,0 +1,14 @@ +/* + * arch/ppc/boot/simple/direct.S + * + * Author: Tom Rini + * + * This is an empty function for machines which use SERIAL_IO_MEM + * and don't need ISA_io set to anything but 0; + */ + + .text + + .globl setup_legacy +setup_legacy: + blr diff -Nru a/arch/ppc/boot/simple/embed_config.c b/arch/ppc/boot/simple/embed_config.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/embed_config.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,755 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ + +/* Board specific functions for those embedded 8xx boards that do + * not have boot monitor support for board information. + */ +#include +#include +#ifdef CONFIG_8xx +#include +#endif +#ifdef CONFIG_8260 +#include +#include +#endif +#ifdef CONFIG_4xx +#include +#endif +#if defined(CONFIG_405GP) || defined(CONFIG_NP405H) || defined(CONFIG_NP405L) +#include +#endif + +/* For those boards that don't provide one. +*/ +#if !defined(CONFIG_MBX) +static bd_t bdinfo; +#endif + +/* IIC functions. + * These are just the basic master read/write operations so we can + * examine serial EEPROM. + */ +extern void iic_read(uint devaddr, u_char *buf, uint offset, uint count); + +/* Supply a default Ethernet address for those eval boards that don't + * ship with one. This is an address from the MBX board I have, so + * it is unlikely you will find it on your network. + */ +static ushort def_enet_addr[] = { 0x0800, 0x3e26, 0x1559 }; + +#if defined(CONFIG_MBX) + +/* The MBX hands us a pretty much ready to go board descriptor. This + * is where the idea started in the first place. + */ +void +embed_config(bd_t **bdp) +{ + u_char *mp; + u_char eebuf[128]; + int i; + bd_t *bd; + + bd = *bdp; + + /* Read the first 128 bytes of the EEPROM. There is more, + * but this is all we need. + */ + iic_read(0xa4, eebuf, 0, 128); + + /* All we are looking for is the Ethernet MAC address. The + * first 8 bytes are 'MOTOROLA', so check for part of that. + * If it's there, assume we have a valid MAC address. If not, + * grab our default one. + */ + if ((*(uint *)eebuf) == 0x4d4f544f) + mp = &eebuf[0x4c]; + else + mp = (u_char *)def_enet_addr; + + for (i=0; i<6; i++) + bd->bi_enetaddr[i] = *mp++; + + /* The boot rom passes these to us in MHz. Linux now expects + * them to be in Hz. + */ + bd->bi_intfreq *= 1000000; + bd->bi_busfreq *= 1000000; + + /* Stuff a baud rate here as well. + */ + bd->bi_baudrate = 9600; +} +#endif /* CONFIG_MBX */ + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) || \ + defined(CONFIG_RPX6) || defined(CONFIG_EP405) +/* Helper functions for Embedded Planet boards. +*/ +/* Because I didn't find anything that would do this....... +*/ +u_char +aschex_to_byte(u_char *cp) +{ + u_char byte, c; + + c = *cp++; + + if ((c >= 'A') && (c <= 'F')) { + c -= 'A'; + c += 10; + } else if ((c >= 'a') && (c <= 'f')) { + c -= 'a'; + c += 10; + } else + c -= '0'; + + byte = c * 16; + + c = *cp; + + if ((c >= 'A') && (c <= 'F')) { + c -= 'A'; + c += 10; + } else if ((c >= 'a') && (c <= 'f')) { + c -= 'a'; + c += 10; + } else + c -= '0'; + + byte += c; + + return(byte); +} + +static void +rpx_eth(bd_t *bd, u_char *cp) +{ + int i; + + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = aschex_to_byte(cp); + cp += 2; + } +} + +#ifdef CONFIG_RPX6 +static uint +rpx_baseten(u_char *cp) +{ + uint retval; + + retval = 0; + + while (*cp != '\n') { + retval *= 10; + retval += (*cp) - '0'; + cp++; + } + return(retval); +} +#endif + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) +static void +rpx_brate(bd_t *bd, u_char *cp) +{ + uint rate; + + rate = 0; + + while (*cp != '\n') { + rate *= 10; + rate += (*cp) - '0'; + cp++; + } + + bd->bi_baudrate = rate * 100; +} + +static void +rpx_cpuspeed(bd_t *bd, u_char *cp) +{ + uint num, den; + + num = den = 0; + + while (*cp != '\n') { + num *= 10; + num += (*cp) - '0'; + cp++; + if (*cp == '/') { + cp++; + den = (*cp) - '0'; + break; + } + } + + /* I don't know why the RPX just can't state the actual + * CPU speed..... + */ + if (den) { + num /= den; + num *= den; + } + bd->bi_intfreq = bd->bi_busfreq = num * 1000000; + + /* The 8xx can only run a maximum 50 MHz bus speed (until + * Motorola changes this :-). Greater than 50 MHz parts + * run internal/2 for bus speed. + */ + if (num > 50) + bd->bi_busfreq /= 2; +} +#endif + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) || defined(CONFIG_EP405) +static void +rpx_memsize(bd_t *bd, u_char *cp) +{ + uint size; + + size = 0; + + while (*cp != '\n') { + size *= 10; + size += (*cp) - '0'; + cp++; + } + + bd->bi_memsize = size * 1024 * 1024; +} +#endif /* LITE || CLASSIC || EP405 */ + +#endif /* Embedded Planet boards */ + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) + +/* Read the EEPROM on the RPX-Lite board. +*/ +void +embed_config(bd_t **bdp) +{ + u_char eebuf[256], *cp; + bd_t *bd; + + /* Read the first 256 bytes of the EEPROM. I think this + * is really all there is, and I hope if it gets bigger the + * info we want is still up front. + */ + bd = &bdinfo; + *bdp = bd; + +#if 1 + iic_read(0xa8, eebuf, 0, 128); + iic_read(0xa8, &eebuf[128], 128, 128); + + /* We look for two things, the Ethernet address and the + * serial baud rate. The records are separated by + * newlines. + */ + cp = eebuf; + for (;;) { + if (*cp == 'E') { + cp++; + if (*cp == 'A') { + cp += 2; + rpx_eth(bd, cp); + } + } + if (*cp == 'S') { + cp++; + if (*cp == 'B') { + cp += 2; + rpx_brate(bd, cp); + } + } + if (*cp == 'D') { + cp++; + if (*cp == '1') { + cp += 2; + rpx_memsize(bd, cp); + } + } + if (*cp == 'H') { + cp++; + if (*cp == 'Z') { + cp += 2; + rpx_cpuspeed(bd, cp); + } + } + + /* Scan to the end of the record. + */ + while ((*cp != '\n') && (*cp != 0xff)) + cp++; + + /* If the next character is a 0 or ff, we are done. + */ + cp++; + if ((*cp == 0) || (*cp == 0xff)) + break; + } + bd->bi_memstart = 0; +#else + /* For boards without initialized EEPROM. + */ + bd->bi_memstart = 0; + bd->bi_memsize = (8 * 1024 * 1024); + bd->bi_intfreq = 48000000; + bd->bi_busfreq = 48000000; + bd->bi_baudrate = 9600; +#endif +} +#endif /* RPXLITE || RPXCLASSIC */ + +#ifdef CONFIG_BSEIP +/* Build a board information structure for the BSE ip-Engine. + * There is more to come since we will add some environment + * variables and a function to read them. + */ +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + bd = &bdinfo; + *bdp = bd; + + /* Baud rate and processor speed will eventually come + * from the environment variables. + */ + bd->bi_baudrate = 9600; + + /* Get the Ethernet station address from the Flash ROM. + */ + cp = (u_char *)0xfe003ffa; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } + + /* The rest of this should come from the environment as well. + */ + bd->bi_memstart = 0; + bd->bi_memsize = (16 * 1024 * 1024); + bd->bi_intfreq = 48000000; + bd->bi_busfreq = 48000000; +} +#endif /* BSEIP */ + +#ifdef CONFIG_FADS +/* Build a board information structure for the FADS. + */ +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + bd = &bdinfo; + *bdp = bd; + + /* Just fill in some known values. + */ + bd->bi_baudrate = 9600; + + /* Use default enet. + */ + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } + + bd->bi_memstart = 0; + bd->bi_memsize = (8 * 1024 * 1024); + bd->bi_intfreq = 40000000; + bd->bi_busfreq = 40000000; +} +#endif /* FADS */ + +#ifdef CONFIG_8260 +/* Compute 8260 clock values if the rom doesn't provide them. + * We can't compute the internal core frequency (I don't know how to + * do that). + */ +static void +clk_8260(bd_t *bd) +{ + uint scmr, vco_out, clkin; + uint plldf, pllmf, busdf, brgdf, cpmdf; + volatile immap_t *ip; + + ip = (immap_t *)IMAP_ADDR; + scmr = ip->im_clkrst.car_scmr; + + /* The clkin is always bus frequency. + */ + clkin = bd->bi_busfreq; + + /* Collect the bits from the scmr. + */ + plldf = (scmr >> 12) & 1; + pllmf = scmr & 0xfff; + cpmdf = (scmr >> 16) & 0x0f; + busdf = (scmr >> 20) & 0x0f; + + /* This is arithmetic from the 8260 manual. + */ + vco_out = clkin / (plldf + 1); + vco_out *= 2 * (pllmf + 1); + bd->bi_vco = vco_out; /* Save for later */ + + bd->bi_cpmfreq = vco_out / 2; /* CPM Freq, in MHz */ + + /* Set Baud rate divisor. The power up default is divide by 16, + * but we set it again here in case it was changed. + */ + ip->im_clkrst.car_sccr = 1; /* DIV 16 BRG */ + bd->bi_brgfreq = vco_out / 16; +} +#endif + +#ifdef CONFIG_EST8260 +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + bd = *bdp; +#if 0 + /* This is actually provided by my boot rom. I have it + * here for those people that may load the kernel with + * a JTAG/COP tool and not the rom monitor. + */ + bd->bi_baudrate = 115200; + bd->bi_intfreq = 200000000; + bd->bi_busfreq = 66666666; + bd->bi_cpmfreq = 66666666; + bd->bi_brgfreq = 33333333; + bd->bi_memsize = 16 * 1024 * 1024; +#else + /* The boot rom passes these to us in MHz. Linux now expects + * them to be in Hz. + */ + bd->bi_intfreq *= 1000000; + bd->bi_busfreq *= 1000000; + bd->bi_cpmfreq *= 1000000; + bd->bi_brgfreq *= 1000000; +#endif + + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } +} +#endif /* EST8260 */ + +#ifdef CONFIG_SBS8260 +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + /* This should provided by the boot rom. + */ + bd = &bdinfo; + *bdp = bd; + bd->bi_baudrate = 9600; + bd->bi_memsize = 64 * 1024 * 1024; + + /* Set all of the clocks. We have to know the speed of the + * external clock. The development board had 66 MHz. + */ + bd->bi_busfreq = 66666666; + clk_8260(bd); + + /* I don't know how to compute this yet. + */ + bd->bi_intfreq = 133000000; + + + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } +} +#endif /* SBS8260 */ + +#ifdef CONFIG_RPX6 +void +embed_config(bd_t **bdp) +{ + u_char *cp, *keyvals; + int i; + bd_t *bd; + + keyvals = (u_char *)*bdp; + + bd = &bdinfo; + *bdp = bd; + + /* This is almost identical to the RPX-Lite/Classic functions + * on the 8xx boards. It would be nice to have a key lookup + * function in a string, but the format of all of the fields + * is slightly different. + */ + cp = keyvals; + for (;;) { + if (*cp == 'E') { + cp++; + if (*cp == 'A') { + cp += 2; + rpx_eth(bd, cp); + } + } + if (*cp == 'S') { + cp++; + if (*cp == 'B') { + cp += 2; + bd->bi_baudrate = rpx_baseten(cp); + } + } + if (*cp == 'D') { + cp++; + if (*cp == '1') { + cp += 2; + bd->bi_memsize = rpx_baseten(cp) * 1024 * 1024; + } + } + if (*cp == 'X') { + cp++; + if (*cp == 'T') { + cp += 2; + bd->bi_busfreq = rpx_baseten(cp); + } + } + if (*cp == 'N') { + cp++; + if (*cp == 'V') { + cp += 2; + bd->bi_nvsize = rpx_baseten(cp) * 1024 * 1024; + } + } + + /* Scan to the end of the record. + */ + while ((*cp != '\n') && (*cp != 0xff)) + cp++; + + /* If the next character is a 0 or ff, we are done. + */ + cp++; + if ((*cp == 0) || (*cp == 0xff)) + break; + } + bd->bi_memstart = 0; + + /* The memory size includes both the 60x and local bus DRAM. + * I don't want to use the local bus DRAM for real memory, + * so subtract it out. It would be nice if they were separate + * keys. + */ + bd->bi_memsize -= 32 * 1024 * 1024; + + /* Set all of the clocks. We have to know the speed of the + * external clock. + */ + clk_8260(bd); + + /* I don't know how to compute this yet. + */ + bd->bi_intfreq = 200000000; +} +#endif /* RPX6 for testing */ + +#ifdef CONFIG_ADS8260 +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + /* This should provided by the boot rom. + */ + bd = &bdinfo; + *bdp = bd; + bd->bi_baudrate = 9600; + bd->bi_memsize = 16 * 1024 * 1024; + + /* Set all of the clocks. We have to know the speed of the + * external clock. The development board had 66 MHz. + */ + bd->bi_busfreq = 66666666; + clk_8260(bd); + + /* I don't know how to compute this yet. + */ + bd->bi_intfreq = 200000000; + + + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } +} +#endif /* ADS8260 */ + +#ifdef CONFIG_WILLOW +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + /* Willow has Open Firmware....I should learn how to get this + * information from it. + */ + bd = &bdinfo; + *bdp = bd; + bd->bi_baudrate = 9600; + bd->bi_memsize = 32 * 1024 * 1024; + + /* Set all of the clocks. We have to know the speed of the + * external clock. The development board had 66 MHz. + */ + bd->bi_busfreq = 66666666; + clk_8260(bd); + + /* I don't know how to compute this yet. + */ + bd->bi_intfreq = 200000000; + + + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } +} +#endif /* WILLOW */ + +#ifdef CONFIG_TREEBOOT +/* This could possibly work for all treeboot roms. +*/ +#define BOARD_INFO_VECTOR 0xFFFE0B50 + +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd, *treeboot_bd; + bd_t *(*get_board_info)(void) = + (bd_t *(*)(void))(*(unsigned long *)BOARD_INFO_VECTOR); +#if !defined(CONFIG_STB03xxx) + volatile emac_t *emacp; + emacp = (emac_t *)EMAC0_BASE; /* assume 1st emac - armin */ + + /* shut down the Ethernet controller that the boot rom + * sometimes leaves running. + */ + mtdcr(DCRN_MALCR, MALCR_MMSR); /* 1st reset MAL */ + while (mfdcr(DCRN_MALCR) & MALCR_MMSR) {}; /* wait for the reset */ + emacp->em0mr0 = 0x20000000; /* then reset EMAC */ + eieio(); +#endif + + bd = &bdinfo; + *bdp = bd; + if ((treeboot_bd = get_board_info()) != NULL) { + memcpy(bd, treeboot_bd, sizeof(bd_t)); + } + else { + /* Hmmm...better try to stuff some defaults. + */ + bd->bi_memsize = 16 * 1024 * 1024; + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + /* I should probably put different ones here, + * hopefully only one is used. + */ + bd->BD_EMAC_ADDR(0,i) = *cp; + +#ifdef CONFIG_PCI + bd->bi_pci_enetaddr[i] = *cp++; +#endif + } + bd->bi_intfreq = 200000000; + bd->bi_busfreq = 100000000; +#ifdef CONFIG_PCI + bd->bi_pci_busfreq = 66666666; +#endif + /* Yeah, this look weird, but on Redwood 4 they are + * different object in the structure. When RW5 uses + * OpenBIOS, it requires a special value. + */ +#ifdef CONFIG_REDWOOD_5 + bd->bi_intfreq = 200 * 1000 * 1000; + bd->bi_busfreq = 0; + + bd->bi_tbfreq = 27 * 1000 * 1000; +#elif CONFIG_REDWOOD_4 + bd->bi_tbfreq = bd->bi_intfreq; +#endif + } +} +#endif + +#ifdef CONFIG_EP405 +void +embed_config(bd_t **bdp) +{ + u_char *cp; + bd_t *bd; + + bd = &bdinfo; + *bdp = bd; +#if 1 + cp = (u_char *)0xF0000EE0; + for (;;) { + if (*cp == 'E') { + cp++; + if (*cp == 'A') { + cp += 2; + rpx_eth(bd, cp); + } + } + + if (*cp == 'D') { + cp++; + if (*cp == '1') { + cp += 2; + rpx_memsize(bd, cp); + } + } + + while ((*cp != '\n') && (*cp != 0xff)) + cp++; + + cp++; + if ((*cp == 0) || (*cp == 0xff)) + break; + } + bd->bi_intfreq = 200000000; + bd->bi_busfreq = 100000000; + bd->bi_pci_busfreq= 33000000 ; +#else + + bd->bi_memsize = 64000000; + bd->bi_intfreq = 200000000; + bd->bi_busfreq = 100000000; + bd->bi_pci_busfreq= 33000000 ; +#endif +} +#endif + diff -Nru a/arch/ppc/boot/simple/gt64260_tty.c b/arch/ppc/boot/simple/gt64260_tty.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/gt64260_tty.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,327 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/boot/simple/gt64260_tty.c + * + * Bootloader version of the embedded MPSC/UART driver for the GT64260[A]. + * Note: Due to 64260A errata, DMA will be used for UART input (via SDMA). + * + * Author: Mark A. Greer + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* This code assumes that the data cache has been disabled (L1, L2, L3). */ + +#include +#include +#include +#include +#include + +extern void udelay(long); +static void stop_dma(int chan); + +static u32 gt64260_base = EV64260_BRIDGE_REG_BASE; /* base addr of 64260 */ + +inline unsigned +gt64260_in_le32(volatile unsigned *addr) +{ + unsigned ret; + + __asm__ __volatile__("lwbrx %0,0,%1; eieio" : "=r" (ret) : + "r" (addr), "m" (*addr)); + return ret; +} + +inline void +gt64260_out_le32(volatile unsigned *addr, int val) +{ + __asm__ __volatile__("stwbrx %1,0,%2; eieio" : "=m" (*addr) : + "r" (val), "r" (addr)); +} + +#define GT64260_REG_READ(offs) \ + (gt64260_in_le32((volatile uint *)(gt64260_base + (offs)))) +#define GT64260_REG_WRITE(offs, d) \ + (gt64260_out_le32((volatile uint *)(gt64260_base + (offs)), (int)(d))) + + +static struct { + u32 sdc; + u32 sdcm; + u32 rx_desc; + u32 rx_buf_ptr; + u32 scrdp; + u32 tx_desc; + u32 sctdp; + u32 sftdp; +} sdma_regs; + +#define SDMA_REGS_INIT(chan) { \ + sdma_regs.sdc = GT64260_SDMA_##chan##_SDC; \ + sdma_regs.sdcm = GT64260_SDMA_##chan##_SDCM; \ + sdma_regs.rx_desc = GT64260_SDMA_##chan##_RX_DESC; \ + sdma_regs.rx_buf_ptr = GT64260_SDMA_##chan##_RX_BUF_PTR; \ + sdma_regs.scrdp = GT64260_SDMA_##chan##_SCRDP; \ + sdma_regs.tx_desc = GT64260_SDMA_##chan##_TX_DESC; \ + sdma_regs.sctdp = GT64260_SDMA_##chan##_SCTDP; \ + sdma_regs.sftdp = GT64260_SDMA_##chan##_SFTDP; \ +} + +typedef struct { + volatile u16 bufsize; + volatile u16 bytecnt; + volatile u32 cmd_stat; + volatile u32 next_desc_ptr; + volatile u32 buffer; +} gt64260_rx_desc_t; + +typedef struct { + volatile u16 bytecnt; + volatile u16 shadow; + volatile u32 cmd_stat; + volatile u32 next_desc_ptr; + volatile u32 buffer; +} gt64260_tx_desc_t; + +#define MAX_RESET_WAIT 10000 +#define MAX_TX_WAIT 10000 + +#define RX_NUM_DESC 2 +#define TX_NUM_DESC 2 + +#define RX_BUF_SIZE 16 +#define TX_BUF_SIZE 16 + +static gt64260_rx_desc_t rd[RX_NUM_DESC] __attribute__ ((aligned(32))); +static gt64260_tx_desc_t td[TX_NUM_DESC] __attribute__ ((aligned(32))); + +static char rx_buf[RX_NUM_DESC * RX_BUF_SIZE] __attribute__ ((aligned(32))); +static char tx_buf[TX_NUM_DESC * TX_BUF_SIZE] __attribute__ ((aligned(32))); + +static int cur_rd = 0; +static int cur_td = 0; + + +#define RX_INIT_RDP(rdp) { \ + (rdp)->bufsize = 2; \ + (rdp)->bytecnt = 0; \ + (rdp)->cmd_stat = GT64260_SDMA_DESC_CMDSTAT_L | \ + GT64260_SDMA_DESC_CMDSTAT_F | \ + GT64260_SDMA_DESC_CMDSTAT_O; \ +} + +unsigned long +serial_init(int chan, void *ignored) +{ + u32 mpsc_adjust, sdma_adjust, brg_bcr; + int i; + + stop_dma(0); + stop_dma(1); + + if (chan != 1) { + chan = 0; /* default to chan 0 if anything but 1 */ + mpsc_adjust = 0; + sdma_adjust = 0; + brg_bcr = GT64260_BRG_0_BCR; + SDMA_REGS_INIT(0); + } + else { + mpsc_adjust = 0x1000; + sdma_adjust = 0x2000; + brg_bcr = GT64260_BRG_1_BCR; + SDMA_REGS_INIT(1); + } + + /* Set up ring buffers */ + for (i=0; i= TX_NUM_DESC) cur_td = 0; + + *(unchar *)(tdp->buffer ^ 7) = c; + tdp->bytecnt = 1; + tdp->shadow = 1; + tdp->cmd_stat = GT64260_SDMA_DESC_CMDSTAT_L | + GT64260_SDMA_DESC_CMDSTAT_F | GT64260_SDMA_DESC_CMDSTAT_O; + + GT64260_REG_WRITE(sdma_regs.sctdp, tdp); + GT64260_REG_WRITE(sdma_regs.sftdp, tdp); + GT64260_REG_WRITE(sdma_regs.sdcm, + GT64260_REG_READ(sdma_regs.sdcm) | GT64260_SDMA_SDCM_TXD); + + return; +} + +unsigned char +serial_getc(unsigned long com_port) +{ + gt64260_rx_desc_t *rdp; + unchar c = '\0'; + + rdp = &rd[cur_rd]; + + if ((rdp->cmd_stat & (GT64260_SDMA_DESC_CMDSTAT_O | + GT64260_SDMA_DESC_CMDSTAT_ES)) == 0) { + c = *(unchar *)(rdp->buffer ^ 7); + RX_INIT_RDP(rdp); + if (++cur_rd >= RX_NUM_DESC) cur_rd = 0; + } + + return c; +} + +int +serial_tstc(unsigned long com_port) +{ + gt64260_rx_desc_t *rdp; + int loop_count = 0; + int rc = 0; + + rdp = &rd[cur_rd]; + + /* Go thru rcv desc's until empty looking for one with data (no error)*/ + while (((rdp->cmd_stat & GT64260_SDMA_DESC_CMDSTAT_O) == 0) && + (loop_count++ < RX_NUM_DESC)) { + + /* If there was an error, reinit the desc & continue */ + if ((rdp->cmd_stat & GT64260_SDMA_DESC_CMDSTAT_ES) != 0) { + RX_INIT_RDP(rdp); + if (++cur_rd >= RX_NUM_DESC) cur_rd = 0; + rdp = (gt64260_rx_desc_t *)rdp->next_desc_ptr; + } + else { + rc = 1; + break; + } + } + + return rc; +} + +void +serial_close(unsigned long com_port) +{ + stop_dma(com_port); + return; +} diff -Nru a/arch/ppc/boot/simple/head.S b/arch/ppc/boot/simple/head.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/head.S Tue Feb 19 18:09:01 2002 @@ -0,0 +1,120 @@ +/* + * arch/ppc/boot/simple/head.S + * + * Initial board bringup code for many different boards. + * + * Author: Tom Rini + * trini@mvista.com + * Derived from arch/ppc/boot/prep/head.S (Cort Dougan, many others). + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + + .text + +/* + * Begin at some arbitrary location in RAM or Flash + * Initialize core registers + * Configure memory controller (Not executing from RAM) + * Move the boot code to the link address (8M) + * Setup C stack + * Initialize UART + * Decompress the kernel to 0x0 + * Jump to the kernel entry + * + */ + + .globl start +start: + bl start_ +#ifdef CONFIG_TREEBOOT + /* The IBM "Tree" bootrom knows that the address of the bootrom + * read only structure is 4 bytes after _start. + */ + .long 0x62726f6d # structure ID - "brom" + .long 0x5f726f00 # - "_ro\0" + .long 1 # structure version + .long bootrom_cmdline # address of *bootrom_cmdline +#endif + +start_: +#ifdef CONFIG_FORCE + /* We have some really bad firmware. We must disable the L1 + * icache/dcache now or the board won't boot. + */ + li r4,0x0000 + isync + mtspr HID0,r4 + sync + isync +#endif + +#if defined(CONFIG_MBX) || defined(CONFIG_RPX6) + mr r29,r3 /* On the MBX860, r3 is the board info pointer. + * On the RPXSUPER, r3 points to the + * NVRAM configuration keys. + */ +#endif + + mflr r3 /* Save our actual starting address. */ + + /* The following functions we call must not modify r3 or r4..... + */ +#ifdef CONFIG_6xx + bl disable_6xx_mmu + bl disable_6xx_l1cache +#if defined(CONFIG_FORCE) || defined(CONFIG_K2) || defined(CONFIG_EV64260) + bl _setup_L2CR +#endif +#endif + +#ifdef CONFIG_8xx + mfmsr r8 /* Turn off interrupts */ + li r9,0 + ori r9,r9,MSR_EE + andc r8,r8,r9 + mtmsr r8 + + /* We do this because some boot roms don't initialize the + * processor correctly. Don't do this if you want to debug + * using a BDM device. + */ + li r4,0 /* Zero DER to prevent FRZ */ + mtspr SPRN_DER,r4 +#endif + +#ifdef CONFIG_REDWOOD_4 + /* All of this Redwood 4 stuff will soon disappear when the + * boot rom is straightened out. + */ + mr r29, r3 /* Easier than changing the other code */ + bl HdwInit + mr r3, r29 +#endif + +#if defined(CONFIG_MBX) || defined(CONFIG_RPX6) + mr r4,r29 /* put the board info pointer where the relocate + * routine will find it + */ +#endif + +#ifdef CONFIG_EV64260 + /* Move 64260's base regs & CS window for external UART */ + bl ev64260_init +#endif + + /* Get the load address. + */ + subi r3, r3, 4 /* Get the actual IP, not NIP */ + b relocate + diff -Nru a/arch/ppc/boot/simple/iic.c b/arch/ppc/boot/simple/iic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/iic.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,218 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ + +/* Minimal support functions to read configuration from IIC EEPROMS + * on MPC8xx boards. Originally written for RPGC RPX-Lite. + * Dan Malek (dmalek@jlc.net). + */ +#include +#include +#include +#include + + +/* IIC functions. + * These are just the basic master read/write operations so we can + * examine serial EEPROM. + */ +void iic_read(uint devaddr, u_char *buf, uint offset, uint count); + +static int iic_init_done; + +static void +iic_init(void) +{ + volatile iic_t *iip; + volatile i2c8xx_t *i2c; + volatile cpm8xx_t *cp; + volatile immap_t *immap; + uint dpaddr; + + immap = (immap_t *)IMAP_ADDR; + cp = (cpm8xx_t *)&(immap->im_cpm); + + /* Reset the CPM. This is necessary on the 860 processors + * that may have started the SCC1 ethernet without relocating + * the IIC. + * This also stops the Ethernet in case we were loaded by a + * BOOTP rom monitor. + */ + cp->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); + + /* Wait for it. + */ + while (cp->cp_cpcr & (CPM_CR_RST | CPM_CR_FLG)); + + /* Remove any microcode patches. We will install our own + * later. + */ + cp->cp_cpmcr1 = 0; + cp->cp_cpmcr2 = 0; + cp->cp_cpmcr3 = 0; + cp->cp_cpmcr4 = 0; + cp->cp_rccr = 0; + + iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; + i2c = (i2c8xx_t *)&(immap->im_i2c); + + /* Initialize Port B IIC pins. + */ + cp->cp_pbpar |= 0x00000030; + cp->cp_pbdir |= 0x00000030; + cp->cp_pbodr |= 0x00000030; + + /* Initialize the parameter ram. + */ + + /* Allocate space for a two transmit and one receive buffer + * descriptor in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + */ + dpaddr = 0x0840; + + /* Set up the IIC parameters in the parameter ram. + */ + iip->iic_tbase = dpaddr; + iip->iic_rbase = dpaddr + (2 * sizeof(cbd_t)); + + iip->iic_tfcr = SMC_EB; + iip->iic_rfcr = SMC_EB; + + /* This should really be done by the reader/writer. + */ + iip->iic_mrblr = 128; + + /* Initialize Tx/Rx parameters. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Select an arbitrary address. Just make sure it is unique. + */ + i2c->i2c_i2add = 0x34; + + /* Make clock run maximum slow. + */ + i2c->i2c_i2brg = 7; + + /* Disable interrupts. + */ + i2c->i2c_i2cmr = 0; + i2c->i2c_i2cer = 0xff; + + /* Enable SDMA. + */ + immap->im_siu_conf.sc_sdcr = 1; + + iic_init_done = 1; +} + +/* Read from IIC. + * Caller provides device address, memory buffer, and byte count. + */ +static u_char iitemp[32]; + +void +iic_read(uint devaddr, u_char *buf, uint offset, uint count) +{ + volatile iic_t *iip; + volatile i2c8xx_t *i2c; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8xx_t *cp; + volatile immap_t *immap; + u_char *tb; + uint temp; + + /* If the interface has not been initialized, do that now. + */ + if (!iic_init_done) + iic_init(); + + immap = (immap_t *)IMAP_ADDR; + cp = (cpm8xx_t *)&(immap->im_cpm); + + iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; + i2c = (i2c8xx_t *)&(immap->im_i2c); + + tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase]; + rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase]; + + /* Send a "dummy write" operation. This is a write request with + * only the offset sent, followed by another start condition. + * This will ensure we start reading from the first location + * of the EEPROM. + */ + tb = iitemp; + tb = (u_char *)(((uint)tb + 15) & ~15); + tbdf->cbd_bufaddr = (int)tb; + *tb = devaddr & 0xfe; /* Device address */ + *(tb+1) = offset; /* Offset */ + tbdf->cbd_datlen = 2; /* Length */ + tbdf->cbd_sc = + BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START; + + i2c->i2c_i2mod = 1; /* Enable */ + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2com = 0x81; /* Start master */ + + /* Wait for IIC transfer. + */ +#if 0 + while ((i2c->i2c_i2cer & 3) == 0); + + if (tbdf->cbd_sc & BD_SC_READY) + printf("IIC ra complete but tbuf ready\n"); +#else + temp = 10000000; + while ((tbdf->cbd_sc & BD_SC_READY) && (temp != 0)) + temp--; +#if 0 + /* We can't do this...there is no serial port yet! + */ + if (temp == 0) { + printf("Timeout reading EEPROM\n"); + return; + } +#endif +#endif + + /* Chip errata, clear enable. + */ + i2c->i2c_i2mod = 0; + + /* To read, we need an empty buffer of the proper length. + * All that is used is the first byte for address, the remainder + * is just used for timing (and doesn't really have to exist). + */ + tbdf->cbd_bufaddr = (int)tb; + *tb = devaddr | 1; /* Device address */ + rbdf->cbd_bufaddr = (uint)buf; /* Desination buffer */ + tbdf->cbd_datlen = rbdf->cbd_datlen = count + 1; /* Length */ + tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START; + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + + /* Chip bug, set enable here. + */ + i2c->i2c_i2mod = 1; /* Enable */ + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2com = 0x81; /* Start master */ + + /* Wait for IIC transfer. + */ +#if 0 + while ((i2c->i2c_i2cer & 1) == 0); + + if (rbdf->cbd_sc & BD_SC_EMPTY) + printf("IIC read complete but rbuf empty\n"); +#else + temp = 10000000; + while ((tbdf->cbd_sc & BD_SC_READY) && (temp != 0)) + temp--; +#endif + + /* Chip errata, clear enable. + */ + i2c->i2c_i2mod = 0; +} diff -Nru a/arch/ppc/boot/simple/legacy.S b/arch/ppc/boot/simple/legacy.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/legacy.S Tue Feb 19 18:09:01 2002 @@ -0,0 +1,19 @@ +/* + * arch/ppc/boot/simple/legacy.S + * + * Author: Tom Rini + * + * This will go and setup ISA_io to 0x8000000. + */ + +#include + + .text + + .globl setup_legacy +setup_legacy: + lis r3,ISA_io@h /* Load ISA_io */ + ori r3,r3,ISA_io@l + lis r4,0x8000 /* Load the value, 0x8000000 */ + stw r4,0(r3) /* store */ + blr diff -Nru a/arch/ppc/boot/simple/m8260_tty.c b/arch/ppc/boot/simple/m8260_tty.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/m8260_tty.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,323 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ + + +/* Minimal serial functions needed to send messages out the serial + * port on SMC1. + */ +#include +#include +#include + +uint no_print; +extern char *params[]; +extern int nparams; +static u_char cons_hold[128], *sgptr; +static int cons_hold_cnt; + +/* If defined, enables serial console. The value (1 through 4) + * should designate which SCC is used, but this isn't complete. Only + * SCC1 is known to work at this time. + */ +#ifdef CONFIG_SCC_CONSOLE +#define SCC_CONSOLE 1 +#endif + +unsigned long +serial_init(int ignored, bd_t *bd) +{ + volatile smc_t *sp; + volatile smc_uart_t *up; +#ifdef SCC_CONSOLE + volatile scc_t *sccp; + volatile scc_uart_t *sup; +#endif + volatile cbd_t *tbdf, *rbdf; + volatile immap_t *ip; + volatile iop8260_t *io; + volatile cpm8260_t *cp; + uint dpaddr, memaddr; + + ip = (immap_t *)IMAP_ADDR; + cp = &ip->im_cpm; + io = &ip->im_ioport; + + /* Perform a reset. + */ + cp->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); + + /* Wait for it. + */ + while (cp->cp_cpcr & CPM_CR_FLG); + +#ifdef CONFIG_ADS8260 + /* Enable the RS-232 transceivers. + */ + *(volatile uint *)(BCSR_ADDR + 4) &= + ~(BCSR1_RS232_EN1 | BCSR1_RS232_EN2); +#endif + +#ifdef SCC_CONSOLE + sccp = (scc_t *)&(ip->im_scc[SCC_CONSOLE-1]); + sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; + sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); + sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* Use Port D for SCC1 instead of other functions. + */ + io->iop_ppard |= 0x00000003; + io->iop_psord &= ~0x00000001; /* Rx */ + io->iop_psord |= 0x00000002; /* Tx */ + io->iop_pdird &= ~0x00000001; /* Rx */ + io->iop_pdird |= 0x00000002; /* Tx */ + +#else + sp = (smc_t*)&(ip->im_smc[0]); + *(ushort *)(&ip->im_dprambase[PROFF_SMC1_BASE]) = PROFF_SMC1; + up = (smc_uart_t *)&ip->im_dprambase[PROFF_SMC1]; + + /* Disable transmitter/receiver. + */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* Use Port D for SMC1 instead of other functions. + */ + io->iop_ppard |= 0x00c00000; + io->iop_pdird |= 0x00400000; + io->iop_pdird &= ~0x00800000; + io->iop_psord &= ~0x00c00000; +#endif + + /* Allocate space for two buffer descriptors in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + */ + dpaddr = 0x0800; + + /* Grab a few bytes from the top of memory. + */ + memaddr = (bd->bi_memsize - 256) & ~15; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&ip->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = memaddr; + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = memaddr+128; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. + */ +#ifdef SCC_CONSOLE + sup->scc_genscc.scc_rbase = dpaddr; + sup->scc_genscc.scc_tbase = dpaddr + sizeof(cbd_t); + + /* Set up the uart parameters in the + * parameter ram. + */ + sup->scc_genscc.scc_rfcr = CPMFCR_GBL | CPMFCR_EB; + sup->scc_genscc.scc_tfcr = CPMFCR_GBL | CPMFCR_EB; + + sup->scc_genscc.scc_mrblr = 128; + sup->scc_maxidl = 8; + sup->scc_brkcr = 1; + sup->scc_parec = 0; + sup->scc_frmec = 0; + sup->scc_nosec = 0; + sup->scc_brkec = 0; + sup->scc_uaddr1 = 0; + sup->scc_uaddr2 = 0; + sup->scc_toseq = 0; + sup->scc_char1 = 0x8000; + sup->scc_char2 = 0x8000; + sup->scc_char3 = 0x8000; + sup->scc_char4 = 0x8000; + sup->scc_char5 = 0x8000; + sup->scc_char6 = 0x8000; + sup->scc_char7 = 0x8000; + sup->scc_char8 = 0x8000; + sup->scc_rccm = 0xc0ff; + + /* Send the CPM an initialize command. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SCC1_PAGE, CPM_CR_SCC1_SBLOCK, 0, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sccp->scc_gsmrh = 0; + sccp->scc_gsmrl = + (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + + /* Disable all interrupts and clear all pending + * events. + */ + sccp->scc_sccm = 0; + sccp->scc_scce = 0xffff; + sccp->scc_dsr = 0x7e7e; + sccp->scc_pmsr = 0x3000; + + /* Wire BRG1 to SCC1. The console driver will take care of + * others. + */ + ip->im_cpmux.cmx_scr = 0; +#else + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = CPMFCR_EB; + up->smc_tfcr = CPMFCR_EB; + up->smc_brklen = 0; + up->smc_brkec = 0; + up->smc_brkcr = 0; + up->smc_mrblr = 128; + up->smc_maxidl = 8; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* Set up the baud rate generator. + */ + ip->im_cpmux.cmx_smr = 0; +#endif + + /* The baud rate divisor needs to be coordinated with clk_8260(). + */ + ip->im_brgc1 = + (((bd->bi_brgfreq/16) / bd->bi_baudrate) << 1) | + CPM_BRG_EN; + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Initialize Tx/Rx parameters. + */ +#ifdef SCC_CONSOLE + sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); +#else + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SMC1_PAGE, CPM_CR_SMC1_SBLOCK, 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Enable transmitter/receiver. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; +#endif + + /* This is ignored. + */ + return 0; +} + +int +serial_readbuf(u_char *cbuf) +{ + volatile cbd_t *rbdf; + volatile char *buf; + volatile smc_uart_t *up; + volatile scc_uart_t *sup; + volatile immap_t *ip; + int i, nc; + + ip = (immap_t *)IMAP_ADDR; + +#ifdef SCC_CONSOLE + sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; + rbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_rbase]; +#else + up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); + rbdf = (cbd_t *)&ip->im_dprambase[up->smc_rbase]; +#endif + + /* Wait for character to show up. + */ + buf = (char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY); + nc = rbdf->cbd_datlen; + for (i=0; icbd_sc |= BD_SC_EMPTY; + + return(nc); +} + +void +serial_putc(void *ignored, const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + volatile scc_uart_t *sup; + volatile immap_t *ip; + extern bd_t *board_info; + + ip = (immap_t *)IMAP_ADDR; +#ifdef SCC_CONSOLE + sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; + tbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_tbase]; +#else + up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); + tbdf = (cbd_t *)&ip->im_dprambase[up->smc_tbase]; +#endif + + /* Wait for last character to go. + */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY); + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +char +serial_getc(void *ignored) +{ + char c; + + if (cons_hold_cnt <= 0) { + cons_hold_cnt = serial_readbuf(cons_hold); + sgptr = cons_hold; + } + c = *sgptr++; + cons_hold_cnt--; + + return(c); +} + +int +serial_tstc(void *ignored) +{ + volatile cbd_t *rbdf; + volatile smc_uart_t *up; + volatile scc_uart_t *sup; + volatile immap_t *ip; + + ip = (immap_t *)IMAP_ADDR; +#ifdef SCC_CONSOLE + sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; + rbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_rbase]; +#else + up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); + rbdf = (cbd_t *)&ip->im_dprambase[up->smc_rbase]; +#endif + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} + +void +serial_close(unsigned long com_port) +{ +} diff -Nru a/arch/ppc/boot/simple/m8xx_tty.c b/arch/ppc/boot/simple/m8xx_tty.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/m8xx_tty.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,300 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ + + +/* Minimal serial functions needed to send messages out the serial + * port on the MBX console. + * + * The MBX uxes SMC1 for the serial port. We reset the port and use + * only the first BD that EPPC-Bug set up as a character FIFO. + * + * Later versions (at least 1.4, maybe earlier) of the MBX EPPC-Bug + * use COM1 instead of SMC1 as the console port. This kinda sucks + * for the rest of the kernel, so here we force the use of SMC1 again. + */ +#include +#include +#include +#include +#include + +#ifdef CONFIG_MBX +#define MBX_CSR1 ((volatile u_char *)0xfa100000) +#define CSR1_COMEN (u_char)0x02 +#endif + +#ifdef TQM_SMC2_CONSOLE +#define PROFF_CONS PROFF_SMC2 +#define CPM_CR_CH_CONS CPM_CR_CH_SMC2 +#define SMC_INDEX 1 +static volatile iop8xx_t *iopp = (iop8xx_t *)&(((immap_t *)IMAP_ADDR)->im_ioport); +#else +#define PROFF_CONS PROFF_SMC1 +#define CPM_CR_CH_CONS CPM_CR_CH_SMC1 +#define SMC_INDEX 0 +#endif + +static cpm8xx_t *cpmp = (cpm8xx_t *)&(((immap_t *)IMAP_ADDR)->im_cpm); + +unsigned long +serial_init(int ignored, bd_t *bd) +{ + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8xx_t *cp; + uint dpaddr, memaddr; +#ifndef CONFIG_MBX + uint ui; +#endif + + cp = cpmp; + sp = (smc_t*)&(cp->cp_smc[SMC_INDEX]); + up = (smc_uart_t *)&cp->cp_dparam[PROFF_CONS]; + + /* Disable transmitter/receiver. + */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + +#ifdef CONFIG_FADS + /* Enable SMC1/2 transceivers. + */ + *((volatile uint *)BCSR1) &= ~(BCSR1_RS232EN_1|BCSR1_RS232EN_2); +#endif + +#ifndef CONFIG_MBX + { + /* Initialize SMCx and use it for the console port. + */ + + /* Enable SDMA. + */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sdcr = 1; + +#ifdef TQM_SMC2_CONSOLE + /* Use Port A for SMC2 instead of other functions. + */ + iopp->iop_papar |= 0x00c0; + iopp->iop_padir &= ~0x00c0; + iopp->iop_paodr &= ~0x00c0; +#else + /* Use Port B for SMCs instead of other functions. + */ + cp->cp_pbpar |= 0x00000cc0; + cp->cp_pbdir &= ~0x00000cc0; + cp->cp_pbodr &= ~0x00000cc0; +#endif + + /* Allocate space for two buffer descriptors in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + */ + dpaddr = 0x0800; + + /* Grab a few bytes from the top of memory for SMC FIFOs. + */ + memaddr = (bd->bi_memsize - 32) & ~15; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; + rbdf->cbd_bufaddr = memaddr; + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = memaddr+4; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* Set up the baud rate generator. + * See 8xx_io/commproc.c for details. + * This wires BRG1 to SMC1 and BRG2 to SMC2; + */ + cp->cp_simode = 0x10000000; + ui = bd->bi_intfreq / 16 / bd->bi_baudrate; +#ifdef TQM_SMC2_CONSOLE + cp->cp_brgc2 = +#else + cp->cp_brgc1 = +#endif + ((ui - 1) < 4096) + ? (((ui - 1) << 1) | CPM_BRG_EN) + : ((((ui / 16) - 1) << 1) | CPM_BRG_EN | CPM_BRG_DIV16); + +#else /* CONFIG_MBX */ + if (*MBX_CSR1 & CSR1_COMEN) { + /* COM1 is enabled. Initialize SMC1 and use it for + * the console port. + */ + + /* Enable SDMA. + */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sdcr = 1; + + /* Use Port B for SMCs instead of other functions. + */ + cp->cp_pbpar |= 0x00000cc0; + cp->cp_pbdir &= ~0x00000cc0; + cp->cp_pbodr &= ~0x00000cc0; + + /* Allocate space for two buffer descriptors in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + */ + dpaddr = 0x0800; + + /* Grab a few bytes from the top of memory. EPPC-Bug isn't + * running any more, so we can do this. + */ + memaddr = (bd->bi_memsize - 32) & ~15; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; + rbdf->cbd_bufaddr = memaddr; + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = memaddr+4; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* Set up the baud rate generator. + * See 8xx_io/commproc.c for details. + */ + cp->cp_simode = 0x10000000; + cp->cp_brgc1 = + (((bd->bi_intfreq/16) / 9600) << 1) | CPM_BRG_EN; + + /* Enable SMC1 for console output. + */ + *MBX_CSR1 &= ~CSR1_COMEN; + } + else { +#endif /* ndef CONFIG_MBX */ + /* SMCx is used as console port. + */ + tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase]; + rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase]; + + /* Issue a stop transmit, and wait for it. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_CONS, + CPM_CR_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Single character receive. + */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + + /* Initialize Tx/Rx parameters. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_CONS, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Enable transmitter/receiver. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + /* This is ignored. + */ + return 0; +} + +void +serial_putc(void *ignored, const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_CONS]; + tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + + /* Wait for last character to go. + */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY); + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +char +serial_getc(void *ignored) +{ + volatile cbd_t *rbdf; + volatile char *buf; + volatile smc_uart_t *up; + char c; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_CONS]; + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + /* Wait for character to show up. + */ + buf = (char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY); + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +int +serial_tstc(void *ignored) +{ + volatile cbd_t *rbdf; + volatile smc_uart_t *up; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_CONS]; + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} + +void +serial_close(unsigned long com_port) +{ +} diff -Nru a/arch/ppc/boot/simple/misc-embedded.c b/arch/ppc/boot/simple/misc-embedded.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/misc-embedded.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,242 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + * + * Originally adapted by Gary Thomas. Much additional work by + * Cort Dougan . On top of that still more work by + * Dan Malek . + * + * Currently maintained by: Tom Rini + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nonstdio.h" +#include "zlib.h" + +/* The linker tells us where the image is. */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin, __ramdisk_end; +extern char _end[]; + +/* Because of the limited amount of memory on embedded, it presents + * loading problems. The biggest is that we load this boot program + * into a relatively low memory address, and the Linux kernel Bss often + * extends into this space when it get loaded. When the kernel starts + * and zeros the BSS space, it also writes over the information we + * save here and pass to the kernel (usually board info). + * On these boards, we grab some known memory holes to hold this information. + */ +char cmd_buf[256]; +char *cmd_line = cmd_buf; +char *avail_ram; +char *end_avail; +char *zimage_start; + +/* This is for 4xx treeboot. It provides a place for the bootrom + * give us a pointer to a rom environment command line. + */ +char *bootrom_cmdline = ""; + +/* This is the default cmdline that will be given to the user at boot time.. + * If none was specified at compile time, we'll give it one that should work. + * -- Tom */ +#ifdef CONFIG_CMDLINE_BOOL +char compiled_string[] = CONFIG_CMDLINE; +#endif +char ramroot_string[] = "root=/dev/ram"; +char netroot_string[] = "root=/dev/nfs rw ip=auto"; + +/* Serial port to use. */ +unsigned long com_port; + +bd_t hold_resid_buf; +bd_t *hold_residual = &hold_resid_buf; + +extern unsigned long serial_init(int chan, bd_t *bp); +extern void serial_close(unsigned long com_port); +extern unsigned long start; +extern void flush_instruction_cache(void); +extern void gunzip(void *, int, unsigned char *, int *); +extern void embed_config(bd_t **bp); + +unsigned long +decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, bd_t *bp) +{ + char *cp, ch; + int timer = 0, zimage_size; + unsigned long initrd_size; + + /* First, capture the embedded board information. Then + * initialize the serial console port. + */ + embed_config(&bp); +#ifdef CONFIG_SERIAL_CONSOLE + com_port = serial_init(0, bp); +#endif + + /* Grab some space for the command line and board info. Since + * we no longer use the ELF header, but it was loaded, grab + * that space. + */ +#ifdef CONFIG_MBX + /* Because of the way the MBX loads the ELF image, we can't + * tell where we started. We read a magic variable from the NVRAM + * that gives us the intermediate buffer load address. + */ + load_addr = *(uint *)0xfa000020; + load_addr += 0x10000; /* Skip ELF header */ +#endif + /* copy board data */ + if (bp) + memcpy(hold_residual,bp,sizeof(bd_t)); + + /* Set end of memory available to us. It is always the highest + * memory address provided by the board information. + */ + end_avail = (char *)(bp->bi_memsize); + + puts("\nloaded at: "); puthex(load_addr); + puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); + if ( (unsigned long)load_addr != (unsigned long)&start ) { + puts("relocated to: "); puthex((unsigned long)&start); + puts(" "); + puthex((unsigned long)((unsigned long)&start + (4*num_words))); + puts("\n"); + } + + if ( bp ) { + puts("board data at: "); puthex((unsigned long)bp); + puts(" "); + puthex((unsigned long)((unsigned long)bp + sizeof(bd_t))); + puts("\nrelocated to: "); + puthex((unsigned long)hold_residual); + puts(" "); + puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t))); + puts("\n"); + } + + /* + * We link ourself to an arbitrary low address. When we run, we + * relocate outself to that address. __image_being points to + * the part of the image where the zImage is. -- Tom + */ + zimage_start = (char *)(unsigned long)(&__image_begin); + zimage_size = (unsigned long)(&__image_end) - + (unsigned long)(&__image_begin); + + initrd_size = (unsigned long)(&__ramdisk_end) - + (unsigned long)(&__ramdisk_begin); + + /* + * The zImage and initrd will be between start and _end, so they've + * already been moved once. We're good to go now. -- Tom + */ + puts("zimage at: "); puthex((unsigned long)zimage_start); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); + puts("\n"); + + if ( initrd_size ) { + puts("initrd at: "); + puthex((unsigned long)(&__ramdisk_begin)); + puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n"); + } + + /* + * setup avail_ram - this is the first part of ram usable + * by the uncompress code. Anything after this program in RAM + * is now fair game. -- Tom + */ + avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); + + puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); + puthex((unsigned long)end_avail); puts("\n"); + puts("\nLinux/PPC load: "); + cp = cmd_line; + /* This is where we try and pick the right command line for booting. + * If we were given one at compile time, use it. It Is Right. + * If we weren't, see if we have a ramdisk. If so, thats root. + * When in doubt, give them the netroot (root=/dev/nfs rw) -- Tom + */ +#ifdef CONFIG_CMDLINE_BOOL + memcpy (cmd_line, compiled_string, sizeof(compiled_string)); +#else + if ( initrd_size ) + memcpy (cmd_line, ramroot_string, sizeof(ramroot_string)); + else + memcpy (cmd_line, netroot_string, sizeof(netroot_string)); +#endif + while ( *cp ) + putc(*cp++); + while (timer++ < 5*1000) { + if (tstc()) { + while ((ch = getc()) != '\n' && ch != '\r') { + if (ch == '\b' || ch == '\177') { + if (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else if (ch == '\030' /* ^x */ + || ch == '\025') { /* ^u */ + while (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else { + *cp++ = ch; + putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; + puts("\nUncompressing Linux..."); + + gunzip(0, 0x400000, zimage_start, &zimage_size); + flush_instruction_cache(); + puts("done.\n"); + { + struct bi_record *rec; + + rec = (struct bi_record *)_ALIGN((unsigned long)zimage_size + + (1 << 20) - 1,(1 << 20)); + + rec->tag = BI_FIRST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_CMD_LINE; + memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1); + rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1; + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + if ( initrd_size ) { + rec->tag = BI_INITRD; + rec->data[0] = (unsigned long)(&__ramdisk_begin); + rec->data[1] = initrd_size; + rec->size = sizeof(struct bi_record) + 2 * + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + + rec->size); + } + + rec->tag = BI_LAST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + } + puts("Now booting the kernel\n"); + serial_close(com_port); + + return (unsigned long)hold_residual; +} diff -Nru a/arch/ppc/boot/simple/misc-ev64260.S b/arch/ppc/boot/simple/misc-ev64260.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/misc-ev64260.S Tue Feb 19 18:09:01 2002 @@ -0,0 +1,63 @@ +/* + * arch/ppc/boot/simple/misc-ev64260.S + * + * Host bridge init code for the Marvell/Galileo EV-64260-BP evaluation board + * with a GT64260 onboard. + * + * Author: Mark Greer + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#include + + .globl ev64260_init +ev64260_init: + li r20,0 + li r23,20 + + /* Relocate galileo's regs */ + addis r25,0,GT64260_INTERNAL_SPACE_DEFAULT_ADDR@h + ori r25,r25,GT64260_INTERNAL_SPACE_DECODE + lwbrx r26,0,(r25) + lis r24,0xffff + and r26,r26,r24 + addis r24,0,EV64260_BRIDGE_REG_BASE@h + srw r24,r24,r23 + or r26,r26,r24 + stwbrx r26,0,(r25) + sync + + /* Wait for write to take effect */ + addis r25,0,EV64260_BRIDGE_REG_BASE@h + ori r25,r25,GT64260_INTERNAL_SPACE_DECODE +1: lwbrx r24,0,(r25) + cmpw r24,r26 + bne 1b + + /* Change CS2 (UARTS on device module) window */ + addis r25,0,EV64260_BRIDGE_REG_BASE@h + ori r25,r25,GT64260_CPU_CS_DECODE_2_BOT + addis r26,0,EV64260_UART_BASE@h + srw r26,r26,r23 + stwbrx r26,0,(r25) + sync + + addis r25,0,EV64260_BRIDGE_REG_BASE@h + ori r25,r25,GT64260_CPU_CS_DECODE_2_TOP + addis r26,0,EV64260_UART_END@h + srw r26,r26,r23 + stwbrx r26,0,(r25) + sync + + blr diff -Nru a/arch/ppc/boot/simple/misc-spruce.c b/arch/ppc/boot/simple/misc-spruce.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/misc-spruce.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,440 @@ +/* + * arch/ppc/boot/spruce/misc.c + * + * Misc. bootloader code for IBM Spruce reference platform + * + * Authors: Johnnie Peters + * Matt Porter + * + * Derived from arch/ppc/boot/prep/misc.c + * + * Copyright 2000-2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "zlib.h" + +/* Define some important locations of the Spruce. */ +#define SPRUCE_PCI_CONFIG_ADDR 0xfec00000 +#define SPRUCE_PCI_CONFIG_DATA 0xfec00004 +#define SPRUCE_ISA_IO_BASE 0xf8000000 + +unsigned long com_port; + +char *avail_ram; +char *end_avail; + +/* The linker tells us where the image is. */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin, __ramdisk_end; +extern char _end[]; + +#ifdef CONFIG_CMDLINE +#define CMDLINE CONFIG_CMDLINE +#else +#define CMDLINE "" +#endif +char cmd_preset[] = CMDLINE; +char cmd_buf[256]; +char *cmd_line = cmd_buf; + +unsigned long initrd_size = 0; + +char *zimage_start; +int zimage_size; + +extern void udelay(long); +extern void puts(const char *); +extern void putc(const char c); +extern void puthex(unsigned long val); +extern int getc(void); +extern int tstc(void); +extern void gunzip(void *, int, unsigned char *, int *); + +extern unsigned long serial_init(int chan, void *ignored); + +/* PCI configuration space access routines. */ +unsigned int *pci_config_address = (unsigned int *)SPRUCE_PCI_CONFIG_ADDR; +unsigned char *pci_config_data = (unsigned char *)SPRUCE_PCI_CONFIG_DATA; + +void cpc700_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + *val= (in_le32((unsigned *)pci_config_data) >> (8 * (offset & 3))) & 0xff; +} + +void cpc700_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + out_8(pci_config_data + (offset&3), val); +} + +void cpc700_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + *val= in_le16((unsigned short *)(pci_config_data + (offset&3))); +} + +void cpc700_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + out_le16((unsigned short *)(pci_config_data + (offset&3)), val); +} + +void cpc700_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + *val= in_le32((unsigned *)pci_config_data); +} + +void cpc700_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + out_le32((unsigned *)pci_config_data, val); +} + +unsigned long isa_io_base = SPRUCE_ISA_IO_BASE; + +#define PCNET32_WIO_RDP 0x10 +#define PCNET32_WIO_RAP 0x12 +#define PCNET32_WIO_RESET 0x14 + +#define PCNET32_DWIO_RDP 0x10 +#define PCNET32_DWIO_RAP 0x14 +#define PCNET32_DWIO_RESET 0x18 + +/* Processor interface config register access */ +#define PIFCFGADDR 0xff500000 +#define PIFCFGDATA 0xff500004 + +#define PLBMIFOPT 0x18 /* PLB Master Interface Options */ + +#define MEM_MBEN 0x24 +#define MEM_TYPE 0x28 +#define MEM_B1SA 0x3c +#define MEM_B1EA 0x5c +#define MEM_B2SA 0x40 +#define MEM_B2EA 0x60 + +unsigned long +decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum) +{ + int timer = 0; + char *cp, ch; + + int loop; + int csr0; + int csr_id; + int *mem_addr = (int *)0xff500008; + int *mem_data = (int *)0xff50000c; + int mem_size = 0; + unsigned long mem_mben; + unsigned long mem_type; + unsigned long mem_start; + unsigned long mem_end; + int *pif_addr = (int *)0xff500000; + int *pif_data = (int *)0xff500004; + int pci_devfn; + int found_multi = 0; + unsigned short vendor; + unsigned short device; + unsigned short command; + unsigned char header_type; + unsigned int bar0; + +#ifdef CONFIG_SERIAL_CONSOLE + /* Initialize the serial console port */ + com_port = serial_init(0, NULL); +#endif + + /* + * Gah, these firmware guys need to learn that hardware + * byte swapping is evil! Disable all hardware byte + * swapping so it doesn't hurt anyone. + */ + *pif_addr = PLBMIFOPT; + asm("sync"); + *pif_data = 0x00000000; + asm("sync"); + + /* Get the size of memory from the memory controller. */ + *mem_addr = MEM_MBEN; + asm("sync"); + mem_mben = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + *mem_addr = MEM_TYPE; + asm("sync"); + mem_type = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + *mem_addr = MEM_TYPE; + /* Confirm bank 1 has DRAM memory */ + if ((mem_mben & 0x40000000) && + ((mem_type & 0x30000000) == 0x10000000)) { + *mem_addr = MEM_B1SA; + asm("sync"); + mem_start = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + *mem_addr = MEM_B1EA; + asm("sync"); + mem_end = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + mem_size = mem_end - mem_start + 0x100000; + } + + /* Confirm bank 2 has DRAM memory */ + if ((mem_mben & 0x20000000) && + ((mem_type & 0xc000000) == 0x4000000)) { + *mem_addr = MEM_B2SA; + asm("sync"); + mem_start = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + *mem_addr = MEM_B2EA; + asm("sync"); + mem_end = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + mem_size += mem_end - mem_start + 0x100000; + } + + /* Search out and turn off the PcNet ethernet boot device. */ + for (pci_devfn = 1; pci_devfn < 0xff; pci_devfn++) { + if (PCI_FUNC(pci_devfn) && !found_multi) + continue; + + cpc700_pcibios_read_config_byte(0, pci_devfn, + PCI_HEADER_TYPE, &header_type); + + if (!PCI_FUNC(pci_devfn)) + found_multi = header_type & 0x80; + + cpc700_pcibios_read_config_word(0, pci_devfn, PCI_VENDOR_ID, + &vendor); + + if (vendor != 0xffff) { + cpc700_pcibios_read_config_word(0, pci_devfn, + PCI_DEVICE_ID, &device); + + /* If this PCI device is the Lance PCNet board then turn it off */ + if ((vendor == PCI_VENDOR_ID_AMD) && + (device == PCI_DEVICE_ID_AMD_LANCE)) { + + /* Turn on I/O Space on the board. */ + cpc700_pcibios_read_config_word(0, pci_devfn, + PCI_COMMAND, &command); + command |= 0x1; + cpc700_pcibios_write_config_word(0, pci_devfn, + PCI_COMMAND, command); + + /* Get the I/O space address */ + cpc700_pcibios_read_config_dword(0, pci_devfn, + PCI_BASE_ADDRESS_0, &bar0); + bar0 &= 0xfffffffe; + + /* Reset the PCNet Board */ + inl (bar0+PCNET32_DWIO_RESET); + inw (bar0+PCNET32_WIO_RESET); + + /* First do a work oriented read of csr0. If the value is + * 4 then this is the correct mode to access the board. + * If not try a double word ortiented read. + */ + outw(0, bar0 + PCNET32_WIO_RAP); + csr0 = inw(bar0 + PCNET32_WIO_RDP); + + if (csr0 == 4) { + /* Check the Chip id register */ + outw(88, bar0 + PCNET32_WIO_RAP); + csr_id = inw(bar0 + PCNET32_WIO_RDP); + + if (csr_id) { + /* This is the valid mode - set the stop bit */ + outw(0, bar0 + PCNET32_WIO_RAP); + outw(csr0, bar0 + PCNET32_WIO_RDP); + } + } else { + outl(0, bar0 + PCNET32_DWIO_RAP); + csr0 = inl(bar0 + PCNET32_DWIO_RDP); + if (csr0 == 4) { + /* Check the Chip id register */ + outl(88, bar0 + PCNET32_WIO_RAP); + csr_id = inl(bar0 + PCNET32_WIO_RDP); + + if (csr_id) { + /* This is the valid mode - set the stop bit*/ + outl(0, bar0 + PCNET32_WIO_RAP); + outl(csr0, bar0 + PCNET32_WIO_RDP); + } + } + } + } + } + } + + /* assume the chunk below 8M is free */ + end_avail = (char *)0x00800000; + + /* + * We link ourself to 0x00800000. When we run, we relocate + * ourselves there. So we just need __image_begin for the + * start. -- Tom + */ + zimage_start = (char *)(unsigned long)(&__image_begin); + zimage_size = (unsigned long)(&__image_end) - + (unsigned long)(&__image_begin); + + initrd_size = (unsigned long)(&__ramdisk_end) - + (unsigned long)(&__ramdisk_begin); + + /* + * The zImage and initrd will be between start and _end, so they've + * already been moved once. We're good to go now. -- Tom + */ + avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); + puts("zimage at: "); puthex((unsigned long)zimage_start); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); + puts("\n"); + + if ( initrd_size ) { + puts("initrd at: "); + puthex((unsigned long)(&__ramdisk_begin)); + puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n"); + } + + avail_ram = (char *)0x00400000; + end_avail = (char *)0x00800000; + puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); + puthex((unsigned long)end_avail); puts("\n"); + + /* Display standard Linux/PPC boot prompt for kernel args */ + puts("\nLinux/PPC load: "); + cp = cmd_line; + memcpy (cmd_line, cmd_preset, sizeof(cmd_preset)); + while ( *cp ) putc(*cp++); + while (timer++ < 5*1000) { + if (tstc()) { + while ((ch = getc()) != '\n' && ch != '\r') { + if (ch == '\b') { + if (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else { + *cp++ = ch; + putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; + puts("\n"); + + puts("Uncompressing Linux..."); + + gunzip(0, 0x400000, zimage_start, &zimage_size); + + puts("done.\n"); + + { + struct bi_record *rec; + + rec = (struct bi_record *)_ALIGN((ulong)zimage_size + + (1<<20)-1,(1<<20)); + + rec->tag = BI_FIRST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_BOOTLOADER_ID; + memcpy( (void *)rec->data, "spruceboot", 11); + rec->size = sizeof(struct bi_record) + 10 + 1; + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_MEMSIZE; + rec->data[0] = mem_size; + rec->size = sizeof(struct bi_record) + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_CMD_LINE; + memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1); + rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1; + rec = (struct bi_record *)((ulong)rec + rec->size); + + if ( initrd_size ) { + rec->tag = BI_INITRD; + rec->data[0] = (unsigned long)(&__ramdisk_begin); + rec->data[1] = initrd_size; + rec->size = sizeof(struct bi_record) + 2 * + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + + rec->size); + } + + rec->tag = BI_LAST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + } + + puts("Now booting the kernel\n"); + + return 0; +} diff -Nru a/arch/ppc/boot/simple/pci.c b/arch/ppc/boot/simple/pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,277 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* Stand alone funtions for QSpan Tundra support. + */ +#include +#include +#include + +extern void puthex(unsigned long val); +extern void puts(const char *); + +/* To map PCI devices, you first write 0xffffffff into the device + * base address registers. When the register is read back, the + * number of most significant '1' bits describes the amount of address + * space needed for mapping. If the most significant bit is not set, + * either the device does not use that address register, or it has + * a fixed address that we can't change. After the address is assigned, + * the command register has to be written to enable the card. + */ +typedef struct { + u_char pci_bus; + u_char pci_devfn; + ushort pci_command; + uint pci_addrs[6]; +} pci_map_t; + +/* We should probably dynamically allocate these structures. +*/ +#define MAX_PCI_DEVS 32 +int pci_dev_cnt; +pci_map_t pci_map[MAX_PCI_DEVS]; + +void pci_conf_write(int bus, int device, int func, int reg, uint writeval); +void pci_conf_read(int bus, int device, int func, int reg, void *readval); +void probe_addresses(int bus, int devfn); +void map_pci_addrs(void); + +extern int +qs_pci_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val); +extern int +qs_pci_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int +qs_pci_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val); +extern int +qs_pci_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val); +extern int +qs_pci_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); +extern int +qs_pci_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val); + + +/* This is a really stripped version of PCI bus scan. All we are + * looking for are devices that exist. + */ +void +pci_scanner(int addr_probe) +{ + unsigned int devfn, l, class, bus_number; + unsigned char hdr_type, is_multi; + + is_multi = 0; + bus_number = 0; + for (devfn = 0; devfn < 0xff; ++devfn) { + /* The device numbers are comprised of upper 5 bits of + * device number and lower 3 bits of multi-function number. + */ + if ((devfn & 7) && !is_multi) { + /* Don't scan multifunction addresses if this is + * not a multifunction device. + */ + continue; + } + + /* Read the header to determine card type. + */ + qs_pci_read_config_byte(bus_number, devfn, PCI_HEADER_TYPE, + &hdr_type); + + /* If this is a base device number, check the header to + * determine if it is mulifunction. + */ + if ((devfn & 7) == 0) + is_multi = hdr_type & 0x80; + + /* Check to see if the board is really in the slot. + */ + qs_pci_read_config_dword(bus_number, devfn, PCI_VENDOR_ID, &l); + /* some broken boards return 0 if a slot is empty: */ + if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || + l == 0xffff0000) { + /* Nothing there. + */ + is_multi = 0; + continue; + } + + /* If we are not performing an address probe, + * just simply print out some information. + */ + if (!addr_probe) { + qs_pci_read_config_dword(bus_number, devfn, + PCI_CLASS_REVISION, &class); + + class >>= 8; /* upper 3 bytes */ + +#if 0 + printf("Found (%3d:%d): vendor 0x%04x, device 0x%04x, class 0x%06x\n", + (devfn >> 3), (devfn & 7), + (l & 0xffff), (l >> 16) & 0xffff, class); +#else + puts("Found ("); puthex(devfn >> 3); + puts(":"); puthex(devfn & 7); + puts("): vendor "); puthex(l & 0xffff); + puts(", device "); puthex((l >> 16) & 0xffff); + puts(", class "); puthex(class); puts("\n"); +#endif + } + else { + /* If this is a "normal" device, build address list. + */ + if ((hdr_type & 0x7f) == PCI_HEADER_TYPE_NORMAL) + probe_addresses(bus_number, devfn); + } + } + + /* Now map the boards. + */ + if (addr_probe) + map_pci_addrs(); +} + +/* Probe addresses for the specified device. This is a destructive + * operation because it writes the registers. + */ +void +probe_addresses(bus, devfn) +{ + int i; + uint pciaddr; + ushort pcicmd; + pci_map_t *pm; + + if (pci_dev_cnt >= MAX_PCI_DEVS) { + puts("Too many PCI devices\n"); + return; + } + + pm = &pci_map[pci_dev_cnt++]; + + pm->pci_bus = bus; + pm->pci_devfn = devfn; + + for (i=0; i<6; i++) { + qs_pci_write_config_dword(bus, devfn, PCI_BASE_ADDRESS_0 + (i * 4), -1); + qs_pci_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0 + (i * 4), + &pciaddr); + pm->pci_addrs[i] = pciaddr; + qs_pci_read_config_word(bus, devfn, PCI_COMMAND, &pcicmd); + pm->pci_command = pcicmd; + } +} + +/* Map the cards into the PCI space. The PCI has separate memory + * and I/O spaces. In addition, some memory devices require mapping + * below 1M. The least significant 4 bits of the address register + * provide information. If this is an I/O device, only the LS bit + * is used to indicate that, so I/O devices can be mapped to a two byte + * boundard. Memory addresses can be mapped to a 32 byte boundary. + * The QSpan implementations usually have a 1Gbyte space for each + * memory and I/O spaces. + * + * This isn't a terribly fancy algorithm. I just map the spaces from + * the top starting with the largest address space. When finished, + * the registers are written and the card enabled. + * + * While the Tundra can map a large address space on most boards, we + * need to be careful because it may overlap other devices (like IMMR). + */ +#define MEMORY_SPACE_SIZE 0x20000000 +#define IO_SPACE_SIZE 0x20000000 + +void +map_pci_addrs() +{ + uint pci_mem_top, pci_mem_low; + uint pci_io_top; + uint addr_mask, reg_addr, space; + int i, j; + pci_map_t *pm; + + pci_mem_top = MEMORY_SPACE_SIZE; + pci_io_top = IO_SPACE_SIZE; + pci_mem_low = (1 * 1024 * 1024); /* Below one meg addresses */ + + /* We can't map anything more than the maximum space, but test + * for it anyway to catch devices out of range. + */ + addr_mask = 0x80000000; + + do { + space = (~addr_mask) + 1; /* Size of the space */ + for (i=0; ipci_addrs[j]; + if ((reg_addr & 0x80000000) == 0) + continue; + if (reg_addr & PCI_BASE_ADDRESS_SPACE_IO) { + if ((reg_addr & PCI_BASE_ADDRESS_IO_MASK) != addr_mask) + continue; + if (pci_io_top < space) { + puts("Out of PCI I/O space\n"); + } + else { + pci_io_top -= space; + pm->pci_addrs[j] = pci_io_top; + pm->pci_command |= PCI_COMMAND_IO; + } + } + else { + if ((reg_addr & PCI_BASE_ADDRESS_MEM_MASK) != addr_mask) + continue; + + /* Memory space. Test if below 1M. + */ + if (reg_addr & PCI_BASE_ADDRESS_MEM_TYPE_1M) { + if (pci_mem_low < space) { + puts("Out of PCI 1M space\n"); + } + else { + pci_mem_low -= space; + pm->pci_addrs[j] = pci_mem_low; + } + } + else { + if (pci_mem_top < space) { + puts("Out of PCI Mem space\n"); + } + else { + pci_mem_top -= space; + pm->pci_addrs[j] = pci_mem_top; + } + } + pm->pci_command |= PCI_COMMAND_MEMORY; + } + } + } + addr_mask >>= 1; + addr_mask |= 0x80000000; + } while (addr_mask != 0xfffffffe); + + /* Now, run the list one more time and map everything. + */ + for (i=0; ipci_bus, pm->pci_devfn, + PCI_BASE_ADDRESS_0 + (j * 4), pm->pci_addrs[j]); + } + + /* Enable memory or address mapping. + */ + qs_pci_write_config_word(pm->pci_bus, pm->pci_devfn, PCI_COMMAND, + pm->pci_command); + } +} + diff -Nru a/arch/ppc/boot/simple/qspan_pci.c b/arch/ppc/boot/simple/qspan_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/qspan_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,272 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * LinuxPPC arch/ppc/kernel/qspan_pci.c Dan Malek (dmalek@jlc.net) + * + * QSpan Motorola bus to PCI bridge. The config address register + * is located 0x500 from the base of the bridge control/status registers. + * The data register is located at 0x504. + * This is a two step operation. First, the address register is written, + * then the data register is read/written as required. + * I don't know what to do about interrupts (yet). + */ + +#include +#include +#include +#include + +/* + * When reading the configuration space, if something does not respond + * the bus times out and we get a machine check interrupt. So, the + * good ol' exception tables come to mind to trap it and return some + * value. + * + * On an error we just return a -1, since that is what the caller wants + * returned if nothing is present. I copied this from __get_user_asm, + * with the only difference of returning -1 instead of EFAULT. + * There is an associated hack in the machine check trap code. + * + * The QSPAN is also a big endian device, that is it makes the PCI + * look big endian to us. This presents a problem for the Linux PCI + * functions, which assume little endian. For example, we see the + * first 32-bit word like this: + * ------------------------ + * | Device ID | Vendor ID | + * ------------------------ + * If we read/write as a double word, that's OK. But in our world, + * when read as a word, device ID is at location 0, not location 2 as + * the little endian PCI would believe. We have to switch bits in + * the PCI addresses given to us to get the data to/from the correct + * byte lanes. + * + * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5. + * It always forces the MS bit to zero. Therefore, dev_fn values + * greater than 128 are returned as "no device found" errors. + * + * The QSPAN can only perform long word (32-bit) configuration cycles. + * The "offset" must have the two LS bits set to zero. Read operations + * require we read the entire word and then sort out what should be + * returned. Write operations other than long word require that we + * read the long word, update the proper word or byte, then write the + * entire long word back. + * + * PCI Bridge hack. We assume (correctly) that bus 0 is the primary + * PCI bus from the QSPAN. If we are called with a bus number other + * than zero, we create a Type 1 configuration access that a downstream + * PCI bridge will interpret. + */ + +#define __get_pci_config(x, addr, op) \ + __asm__ __volatile__( \ + "1: "op" %0,0(%1)\n" \ + " eieio\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r"(x) : "r"(addr)) + +#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500)) +#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504)) + +#define mk_config_addr(bus, dev, offset) \ + (((bus)<<16) | ((dev)<<8) | (offset & 0xfc)) + +#define mk_config_type1(bus, dev, offset) \ + mk_config_addr(bus, dev, offset) | 1; + +/* Initialize the QSpan device registers after power up. +*/ +void +qspan_init(void) +{ + uint *qptr; + + + + qptr = (uint *)PCI_CSR_ADDR; + + /* PCI Configuration/status. Upper bits written to clear + * pending interrupt or status. Lower bits enable QSPAN as + * PCI master, enable memory and I/O cycles, and enable PCI + * parity error checking. + * IMPORTANT: The last two bits of this word enable PCI + * master cycles into the QBus. The QSpan is broken and can't + * meet the timing specs of the PQ bus for this to work. Therefore, + * if you don't have external bus arbitration, you can't use + * this function. + */ +#ifdef EXTERNAL_PQ_ARB + qptr[1] = 0xf9000147; +#else + qptr[1] = 0xf9000144; +#endif + + /* PCI Misc configuration. Set PCI latency timer resolution + * of 8 cycles, set cache size to 4 x 32. + */ + qptr[3] = 0; + + /* Set up PCI Target address mapping. Enable, Posted writes, + * 2Gbyte space (processor memory controller determines actual size). + */ + qptr[64] = 0x8f000080; + + /* Map processor 0x80000000 to PCI 0x00000000. + * Processor address bit 1 determines I/O type access (0x80000000) + * or memory type access (0xc0000000). + */ + qptr[65] = 0x80000000; + + /* Enable error logging and clear any pending error status. + */ + qptr[80] = 0x90000000; + + qptr[512] = 0x000c0003; + + /* Set up Qbus slave image. + */ + qptr[960] = 0x01000000; + qptr[961] = 0x000000d1; + qptr[964] = 0x00000000; + qptr[965] = 0x000000d1; + +} + +/* Functions to support PCI bios-like features to read/write configuration + * space. If the function fails for any reason, a -1 (0xffffffff) value + * must be returned. + */ +#define DEVICE_NOT_FOUND (-1) +#define SUCCESSFUL 0 + +int qs_pci_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + uint temp; + u_char *cp; + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xff; + return DEVICE_NOT_FOUND; + } + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_pci_config(temp, QS_CONFIG_DATA, "lwz"); + + offset ^= 0x03; + cp = ((u_char *)&temp) + (offset & 0x03); + *val = *cp; + return SUCCESSFUL; +} + +int qs_pci_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + uint temp; + ushort *sp; + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xffff; + return DEVICE_NOT_FOUND; + } + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_pci_config(temp, QS_CONFIG_DATA, "lwz"); + offset ^= 0x02; + + sp = ((ushort *)&temp) + ((offset >> 1) & 1); + *val = *sp; + return SUCCESSFUL; +} + +int qs_pci_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xffffffff; + return DEVICE_NOT_FOUND; + } + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_pci_config(*val, QS_CONFIG_DATA, "lwz"); + return SUCCESSFUL; +} + +int qs_pci_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + uint temp; + u_char *cp; + + if ((bus > 7) || (dev_fn > 127)) + return DEVICE_NOT_FOUND; + + qs_pci_read_config_dword(bus, dev_fn, offset, &temp); + + offset ^= 0x03; + cp = ((u_char *)&temp) + (offset & 0x03); + *cp = val; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *QS_CONFIG_DATA = temp; + + return SUCCESSFUL; +} + +int qs_pci_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + uint temp; + ushort *sp; + + if ((bus > 7) || (dev_fn > 127)) + return DEVICE_NOT_FOUND; + + qs_pci_read_config_dword(bus, dev_fn, offset, &temp); + + offset ^= 0x02; + sp = ((ushort *)&temp) + ((offset >> 1) & 1); + *sp = val; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *QS_CONFIG_DATA = temp; + + return SUCCESSFUL; +} + +int qs_pci_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + if ((bus > 7) || (dev_fn > 127)) + return DEVICE_NOT_FOUND; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *(unsigned int *)QS_CONFIG_DATA = val; + + return SUCCESSFUL; +} + diff -Nru a/arch/ppc/boot/simple/rw4/ppc_40x.h b/arch/ppc/boot/simple/rw4/ppc_40x.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/rw4/ppc_40x.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,664 @@ +/*----------------------------------------------------------------------------+ +| This source code has been made available to you by IBM on an AS-IS +| basis. Anyone receiving this source is licensed under IBM +| copyrights to use it in any way he or she deems fit, including +| copying it, modifying it, compiling it, and redistributing it either +| with or without modifications. No license under IBM patents or +| patent applications is to be implied by the copyright license. +| +| Any user of this software should understand that IBM cannot provide +| technical support for this software and will not be responsible for +| any consequences resulting from the use of this software. +| +| Any person who transfers this source code or any derivative work +| must include the IBM copyright notice, this paragraph, and the +| preceding two paragraphs in the transferred software. +| +| COPYRIGHT I B M CORPORATION 1997 +| LICENSED MATERIAL - PROGRAM PROPERTY OF I B M ++----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ +| Author: Tony J. Cerreto +| Component: Assembler include file. +| File: ppc_40x.h +| Purpose: Include file containing PPC DCR defines. +| +| Changes: +| Date Author Comment +| --------- ------ -------------------------------------------------------- +| 01-Mar-00 tjc Created ++----------------------------------------------------------------------------*/ +/* added by linguohui*/ +#define MW +/*----------------------------------------------------------------------------+ +| PPC Special purpose registers Numbers ++----------------------------------------------------------------------------*/ +#define ccr0 0x3b3 /* core configuration reg */ +#define ctr 0x009 /* count register */ +#define ctrreg 0x009 /* count register */ +#define dbcr0 0x3f2 /* debug control register 0 */ +#define dbcr1 0x3bd /* debug control register 1 */ +#define dbsr 0x3f0 /* debug status register */ +#define dccr 0x3fa /* data cache control reg. */ +#define dcwr 0x3ba /* data cache write-thru reg */ +#define dear 0x3d5 /* data exeption address reg */ +#define esr 0x3d4 /* execption syndrome registe */ +#define evpr 0x3d6 /* exeption vector prefix reg */ +#define iccr 0x3fb /* instruction cache cntrl re */ +#define icdbdr 0x3d3 /* instr cache dbug data reg */ +#define lrreg 0x008 /* link register */ +#define pid 0x3b1 /* process id reg */ +#define pit 0x3db /* programmable interval time */ +#define pvr 0x11f /* processor version register */ +#define sgr 0x3b9 /* storage guarded reg */ +#define sler 0x3bb /* storage little endian reg */ +#define sprg0 0x110 /* special general purpose 0 */ +#define sprg1 0x111 /* special general purpose 1 */ +#define sprg2 0x112 /* special general purpose 2 */ +#define sprg3 0x113 /* special general purpose 3 */ +#define sprg4 0x114 /* special general purpose 4 */ +#define sprg5 0x115 /* special general purpose 5 */ +#define sprg6 0x116 /* special general purpose 6 */ +#define sprg7 0x117 /* special general purpose 7 */ +#define srr0 0x01a /* save/restore register 0 */ +#define srr1 0x01b /* save/restore register 1 */ +#define srr2 0x3de /* save/restore register 2 */ +#define srr3 0x3df /* save/restore register 3 */ +#define tbhi 0x11D +#define tblo 0x11C +#define tcr 0x3da /* timer control register */ +#define tsr 0x3d8 /* timer status register */ +#define xerreg 0x001 /* fixed point exception */ +#define xer 0x001 /* fixed point exception */ +#define zpr 0x3b0 /* zone protection reg */ + +/*----------------------------------------------------------------------------+ +| Decompression Controller ++----------------------------------------------------------------------------*/ +#define kiar 0x014 /* Decompression cntl addr reg */ +#define kidr 0x015 /* Decompression cntl data reg */ +#define kitor0 0x00 /* index table origin Reg 0 */ +#define kitor1 0x01 /* index table origin Reg 1 */ +#define kitor2 0x02 /* index table origin Reg 2 */ +#define kitor3 0x03 /* index table origin Reg 3 */ +#define kaddr0 0x04 /* addr decode Definition Reg 0 */ +#define kaddr1 0x05 /* addr decode Definition Reg 1 */ +#define kconf 0x40 /* Decompression cntl config reg */ +#define kid 0x41 /* Decompression cntl id reg */ +#define kver 0x42 /* Decompression cntl ver number */ +#define kpear 0x50 /* bus error addr reg (PLB) */ +#define kbear 0x51 /* bus error addr reg (DCP-EBC) */ +#define kesr0 0x52 /* bus error status reg 0 */ + +/*----------------------------------------------------------------------------+ +| Romeo Specific Device Control Register Numbers. ++----------------------------------------------------------------------------*/ +#ifndef VESTA +#define cdbcr 0x3d7 /* cache debug cntrl reg */ + +#define a_latcnt 0x1a9 /* PLB Latency count */ +#define a_tgval 0x1ac /* tone generation value */ +#define a_plb_pr 0x1bf /* PLB priority */ + +#define cic_sel1 0x031 /* select register 1 */ +#define cic_sel2 0x032 /* select register 2 */ + +#define clkgcrst 0x122 /* chip reset register */ + +#define cp_cpmsr 0x100 /*rstatus register */ +#define cp_cpmer 0x101 /* enable register */ + +#define dcp_kiar 0x190 /* indirect address register */ +#define dcp_kidr 0x191 /* indirect data register */ + +#define hsmc_mcgr 0x1c0 /* HSMC global register */ +#define hsmc_mcbesr 0x1c1 /* bus error status register */ +#define hsmc_mcbear 0x1c2 /* bus error address register*/ +#define hsmc_mcbr0 0x1c4 /* SDRAM sub-ctrl bank reg 0 */ +#define hsmc_mccr0 0x1c5 /* SDRAM sub-ctrl ctrl reg 0 */ +#define hsmc_mcbr1 0x1c7 /* SDRAM sub-ctrl bank reg 1 */ +#define hsmc_mccr1 0x1c8 /* SDRAM sub-ctrl ctrl reg 1 */ +#define hsmc_sysr 0x1d1 /* system register */ +#define hsmc_data 0x1d2 /* data register */ +#define hsmc_mccrr 0x1d3 /* refresh register */ + +#define ocm_pbar 0x1E0 /* base address register */ + +#define plb0_pacr0 0x057 /* PLB arbiter control reg */ +#define plb1_pacr1 0x067 /* PLB arbiter control reg */ + +#define v_displb 0x157 /* set left border of display*/ +#define v_disptb 0x158 /* top border of display */ +#define v_osd_la 0x159 /* first link address for OSD*/ +#define v_ptsdlta 0x15E /* PTS delta register */ +#define v_v0base 0x16C /* base mem add for VBI-0 */ +#define v_v1base 0x16D /* base mem add for VBI-1 */ +#define v_osbase 0x16E /* base mem add for OSD data */ +#endif + +/*----------------------------------------------------------------------------+ +| Vesta Device Control Register Numbers. ++----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ +| Cross bar switch. ++----------------------------------------------------------------------------*/ +#define cbs0_cr 0x010 /* CBS configuration register */ + +/*----------------------------------------------------------------------------+ +| DCR external master (DCRX). ++----------------------------------------------------------------------------*/ +#define dcrx0_icr 0x020 /* internal control register */ +#define dcrx0_isr 0x021 /* internal status register */ +#define dcrx0_ecr 0x022 /* external control register */ +#define dcrx0_esr 0x023 /* external status register */ +#define dcrx0_tar 0x024 /* target address register */ +#define dcrx0_tdr 0x025 /* target data register */ +#define dcrx0_igr 0x026 /* interrupt generation register */ +#define dcrx0_bcr 0x027 /* buffer control register */ + +/*----------------------------------------------------------------------------+ +| Chip interconnect configuration. ++----------------------------------------------------------------------------*/ +#define cic0_cr 0x030 /* CIC control register */ +#define cic0_vcr 0x033 /* video macro control reg */ +#define cic0_sel3 0x035 /* select register 3 */ + +/*----------------------------------------------------------------------------+ +| Chip interconnect configuration. ++----------------------------------------------------------------------------*/ +#define sgpo0_sgpO 0x036 /* simplified GPIO output */ +#define sgpo0_gpod 0x037 /* simplified GPIO open drain */ +#define sgpo0_gptc 0x038 /* simplified GPIO tristate cntl */ +#define sgpo0_gpi 0x039 /* simplified GPIO input */ + +/*----------------------------------------------------------------------------+ +| Universal interrupt controller. ++----------------------------------------------------------------------------*/ +#define uic0_sr 0x040 /* status register */ +#define uic0_srs 0x041 /* status register set */ +#define uic0_er 0x042 /* enable register */ +#define uic0_cr 0x043 /* critical register */ +#define uic0_pr 0x044 /* parity register */ +#define uic0_tr 0x045 /* triggering register */ +#define uic0_msr 0x046 /* masked status register */ +#define uic0_vr 0x047 /* vector register */ +#define uic0_vcr 0x048 /* enable config register */ + +/*----------------------------------------------------------------------------+ +| PLB 0 and 1. ++----------------------------------------------------------------------------*/ +#define pb0_pesr 0x054 /* PLB error status reg 0 */ +#define pb0_pesrs 0x055 /* PLB error status reg 0 set */ +#define pb0_pear 0x056 /* PLB error address reg */ + +#define pb1_pesr 0x064 /* PLB error status reg 1 */ +#define pb1_pesrs 0x065 /* PLB error status reg 1 set */ +#define pb1_pear 0x066 /* PLB error address reg */ + +/*----------------------------------------------------------------------------+ +| EBIU DCR registers. ++----------------------------------------------------------------------------*/ +#define ebiu0_brcrh0 0x070 /* bus region register 0 high */ +#define ebiu0_brcrh1 0x071 /* bus region register 1 high */ +#define ebiu0_brcrh2 0x072 /* bus region register 2 high */ +#define ebiu0_brcrh3 0x073 /* bus region register 3 high */ +#define ebiu0_brcrh4 0x074 /* bus region register 4 high */ +#define ebiu0_brcrh5 0x075 /* bus region register 5 high */ +#define ebiu0_brcrh6 0x076 /* bus region register 6 high */ +#define ebiu0_brcrh7 0x077 /* bus region register 7 high */ +#define ebiu0_brcr0 0x080 /* bus region register 0 */ +#define ebiu0_brcr1 0x081 /* bus region register 1 */ +#define ebiu0_brcr2 0x082 /* bus region register 2 */ +#define ebiu0_brcr3 0x083 /* bus region register 3 */ +#define ebiu0_brcr4 0x084 /* bus region register 4 */ +#define ebiu0_brcr5 0x085 /* bus region register 5 */ +#define ebiu0_brcr6 0x086 /* bus region register 6 */ +#define ebiu0_brcr7 0x087 /* bus region register 7 */ +#define ebiu0_bear 0x090 /* bus error address register */ +#define ebiu0_besr 0x091 /* bus error syndrome reg */ +#define ebiu0_besr0s 0x093 /* bus error syndrome reg */ +#define ebiu0_biucr 0x09a /* bus interface control reg */ + +/*----------------------------------------------------------------------------+ +| OPB bridge. ++----------------------------------------------------------------------------*/ +#define opbw0_gesr 0x0b0 /* error status reg */ +#define opbw0_gesrs 0x0b1 /* error status reg */ +#define opbw0_gear 0x0b2 /* error address reg */ + +/*----------------------------------------------------------------------------+ +| DMA. ++----------------------------------------------------------------------------*/ +#define dma0_cr0 0x0c0 /* DMA channel control reg 0 */ +#define dma0_ct0 0x0c1 /* DMA count register 0 */ +#define dma0_da0 0x0c2 /* DMA destination addr reg 0 */ +#define dma0_sa0 0x0c3 /* DMA source addr register 0 */ +#define dma0_cc0 0x0c4 /* DMA chained count 0 */ +#define dma0_cr1 0x0c8 /* DMA channel control reg 1 */ +#define dma0_ct1 0x0c9 /* DMA count register 1 */ +#define dma0_da1 0x0ca /* DMA destination addr reg 1 */ +#define dma0_sa1 0x0cb /* DMA source addr register 1 */ +#define dma0_cc1 0x0cc /* DMA chained count 1 */ +#define dma0_cr2 0x0d0 /* DMA channel control reg 2 */ +#define dma0_ct2 0x0d1 /* DMA count register 2 */ +#define dma0_da2 0x0d2 /* DMA destination addr reg 2 */ +#define dma0_sa2 0x0d3 /* DMA source addr register 2 */ +#define dma0_cc2 0x0d4 /* DMA chained count 2 */ +#define dma0_cr3 0x0d8 /* DMA channel control reg 3 */ +#define dma0_ct3 0x0d9 /* DMA count register 3 */ +#define dma0_da3 0x0da /* DMA destination addr reg 3 */ +#define dma0_sa3 0x0db /* DMA source addr register 3 */ +#define dma0_cc3 0x0dc /* DMA chained count 3 */ +#define dma0_sr 0x0e0 /* DMA status register */ +#define dma0_srs 0x0e1 /* DMA status register */ +#define dma0_s1 0x031 /* DMA select1 register */ +#define dma0_s2 0x032 /* DMA select2 register */ + +/*---------------------------------------------------------------------------+ +| Clock and power management. ++----------------------------------------------------------------------------*/ +#define cpm0_fr 0x102 /* force register */ + +/*----------------------------------------------------------------------------+ +| Serial Clock Control. ++----------------------------------------------------------------------------*/ +#define ser0_ccr 0x120 /* serial clock control register */ + +/*----------------------------------------------------------------------------+ +| Audio Clock Control. ++----------------------------------------------------------------------------*/ +#define aud0_apcr 0x121 /* audio clock ctrl register */ + +/*----------------------------------------------------------------------------+ +| DENC. ++----------------------------------------------------------------------------*/ +#define denc0_idr 0x130 /* DENC ID register */ +#define denc0_cr1 0x131 /* control register 1 */ +#define denc0_rr1 0x132 /* microvision 1 (reserved 1) */ +#define denc0_cr2 0x133 /* control register 2 */ +#define denc0_rr2 0x134 /* microvision 2 (reserved 2) */ +#define denc0_rr3 0x135 /* microvision 3 (reserved 3) */ +#define denc0_rr4 0x136 /* microvision 4 (reserved 4) */ +#define denc0_rr5 0x137 /* microvision 5 (reserved 5) */ +#define denc0_ccdr 0x138 /* closed caption data */ +#define denc0_cccr 0x139 /* closed caption control */ +#define denc0_trr 0x13A /* teletext request register */ +#define denc0_tosr 0x13B /* teletext odd field line se */ +#define denc0_tesr 0x13C /* teletext even field line s */ +#define denc0_rlsr 0x13D /* RGB rhift left register */ +#define denc0_vlsr 0x13E /* video level shift register */ +#define denc0_vsr 0x13F /* video scaling register */ + +/*----------------------------------------------------------------------------+ +| Video decoder. Suspect 0x179, 0x169, 0x16a, 0x152 (rc). ++----------------------------------------------------------------------------*/ +#define vid0_ccntl 0x140 /* control decoder operation */ +#define vid0_cmode 0x141 /* video operational mode */ +#define vid0_sstc0 0x142 /* STC high order bits 31:0 */ +#define vid0_sstc1 0x143 /* STC low order bit 32 */ +#define vid0_spts0 0x144 /* PTS high order bits 31:0 */ +#define vid0_spts1 0x145 /* PTS low order bit 32 */ +#define vid0_fifo 0x146 /* FIFO data port */ +#define vid0_fifos 0x147 /* FIFO status */ +#define vid0_cmd 0x148 /* send command to decoder */ +#define vid0_cmdd 0x149 /* port for command params */ +#define vid0_cmdst 0x14A /* command status */ +#define vid0_cmdad 0x14B /* command address */ +#define vid0_procia 0x14C /* instruction store */ +#define vid0_procid 0x14D /* data port for I_Store */ +#define vid0_osdm 0x151 /* OSD mode control */ +#define vid0_hosti 0x152 /* base interrupt register */ +#define vid0_mask 0x153 /* interrupt mask register */ +#define vid0_dispm 0x154 /* operational mode for Disp */ +#define vid0_dispd 0x155 /* setting for 'Sync' delay */ +#define vid0_vbctl 0x156 /* VBI */ +#define vid0_ttxctl 0x157 /* teletext control */ +#define vid0_disptb 0x158 /* display left/top border */ +#define vid0_osdgla 0x159 /* Graphics plane link addr */ +#define vid0_osdila 0x15A /* Image plane link addr */ +#define vid0_rbthr 0x15B /* rate buffer threshold */ +#define vid0_osdcla 0x15C /* Cursor link addr */ +#define vid0_stcca 0x15D /* STC common address */ +#define vid0_ptsctl 0x15F /* PTS Control */ +#define vid0_wprot 0x165 /* write protect for I_Store */ +#define vid0_vcqa 0x167 /* video clip queued block Ad */ +#define vid0_vcql 0x168 /* video clip queued block Le */ +#define vid0_blksz 0x169 /* block size bytes for copy op */ +#define vid0_srcad 0x16a /* copy source address bits 6-31 */ +#define vid0_udbas 0x16B /* base mem add for user data */ +#define vid0_vbibas 0x16C /* base mem add for VBI 0/1 */ +#define vid0_osdibas 0x16D /* Image plane base address */ +#define vid0_osdgbas 0x16E /* Graphic plane base address */ +#define vid0_rbbase 0x16F /* base mem add for video buf */ +#define vid0_dramad 0x170 /* DRAM address */ +#define vid0_dramdt 0x171 /* data port for DRAM access */ +#define vid0_dramcs 0x172 /* DRAM command and statusa */ +#define vid0_vcwa 0x173 /* v clip work address */ +#define vid0_vcwl 0x174 /* v clip work length */ +#define vid0_mseg0 0x175 /* segment address 0 */ +#define vid0_mseg1 0x176 /* segment address 1 */ +#define vid0_mseg2 0x177 /* segment address 2 */ +#define vid0_mseg3 0x178 /* segment address 3 */ +#define vid0_fbbase 0x179 /* frame buffer base memory */ +#define vid0_osdcbas 0x17A /* Cursor base addr */ +#define vid0_lboxtb 0x17B /* top left border */ +#define vid0_trdly 0x17C /* transparency gate delay */ +#define vid0_sbord 0x17D /* left/top small pict. bord. */ +#define vid0_zoffs 0x17E /* hor/ver zoom window */ +#define vid0_rbsz 0x17F /* rate buffer size read */ + +/*----------------------------------------------------------------------------+ +| Transport demultiplexer. ++----------------------------------------------------------------------------*/ +#define xpt0_lr 0x180 /* demux location register */ +#define xpt0_data 0x181 /* demux data register */ +#define xpt0_ir 0x182 /* demux interrupt register */ + +#define xpt0_config1 0x0000 /* configuration 1 */ +#define xpt0_control1 0x0001 /* control 1 */ +#define xpt0_festat 0x0002 /* Front-end status */ +#define xpt0_feimask 0x0003 /* Front_end interrupt Mask */ +#define xpt0_ocmcnfg 0x0004 /* OCM Address */ +#define xpt0_settapi 0x0005 /* Set TAP Interrupt */ + +#define xpt0_pcrhi 0x0010 /* PCR High */ +#define xpt0_pcrlow 0x0011 /* PCR Low */ +#define xpt0_lstchi 0x0012 /* Latched STC High */ +#define xpt0_lstclow 0x0013 /* Latched STC Low */ +#define xpt0_stchi 0x0014 /* STC High */ +#define xpt0_stclow 0x0015 /* STC Low */ +#define xpt0_pwm 0x0016 /* PWM */ +#define xpt0_pcrstct 0x0017 /* PCR-STC Threshold */ +#define xpt0_pcrstcd 0x0018 /* PCR-STC Delta */ +#define xpt0_stccomp 0x0019 /* STC Compare */ +#define xpt0_stccmpd 0x001a /* STC Compare Disarm */ + +#define xpt0_dsstat 0x0048 /* Descrambler Status */ +#define xpt0_dsimask 0x0049 /* Descrambler Interrupt Mask */ + +#define xpt0_vcchng 0x01f0 /* Video Channel Change */ +#define xpt0_acchng 0x01f1 /* Audio Channel Change */ +#define xpt0_axenable 0x01fe /* Aux PID Enables */ +#define xpt0_pcrpid 0x01ff /* PCR PID */ + +#define xpt0_config2 0x1000 /* Configuration 2 */ +#define xpt0_pbuflvl 0x1002 /* Packet Buffer Level */ +#define xpt0_intmask 0x1003 /* Interrupt Mask */ +#define xpt0_plbcnfg 0x1004 /* PLB Configuration */ + +#define xpt0_qint 0x1010 /* Queues Interrupts */ +#define xpt0_qintmsk 0x1011 /* Queues Interrupts Mask */ +#define xpt0_astatus 0x1012 /* Audio Status */ +#define xpt0_aintmask 0x1013 /* Audio Interrupt Mask */ +#define xpt0_vstatus 0x1014 /* Video Status */ +#define xpt0_vintmask 0x1015 /* Video Interrupt Mask */ + +#define xpt0_qbase 0x1020 /* Queue Base */ +#define xpt0_bucketq 0x1021 /* Bucket Queue */ +#define xpt0_qstops 0x1024 /* Queue Stops */ +#define xpt0_qresets 0x1025 /* Queue Resets */ +#define xpt0_sfchng 0x1026 /* Section Filter Change */ + +/*----------------------------------------------------------------------------+ +| Audio decoder. Suspect 0x1ad, 0x1b4, 0x1a3, 0x1a5 (read/write status) ++----------------------------------------------------------------------------*/ +#define aud0_ctrl0 0x1a0 /* control 0 */ +#define aud0_ctrl1 0x1a1 /* control 1 */ +#define aud0_ctrl2 0x1a2 /* control 2 */ +#define aud0_cmd 0x1a3 /* command register */ +#define aud0_isr 0x1a4 /* interrupt status register */ +#define aud0_imr 0x1a5 /* interrupt mask register */ +#define aud0_dsr 0x1a6 /* decoder status register */ +#define aud0_stc 0x1a7 /* system time clock */ +#define aud0_csr 0x1a8 /* channel status register */ +#define aud0_lcnt 0x1a9 /* queued address register 2 */ +#define aud0_pts 0x1aa /* presentation time stamp */ +#define aud0_tgctrl 0x1ab /* tone generation control */ +#define aud0_qlr2 0x1ac /* queued length register 2 */ +#define aud0_auxd 0x1ad /* aux data */ +#define aud0_strmid 0x1ae /* stream ID */ +#define aud0_qar 0x1af /* queued address register */ +#define aud0_dsps 0x1b0 /* DSP status */ +#define aud0_qlr 0x1b1 /* queued len address */ +#define aud0_dspc 0x1b2 /* DSP control */ +#define aud0_wlr2 0x1b3 /* working length register 2 */ +#define aud0_instd 0x1b4 /* instruction download */ +#define aud0_war 0x1b5 /* working address register */ +#define aud0_seg1 0x1b6 /* segment 1 base register */ +#define aud0_seg2 0x1b7 /* segment 2 base register */ +#define aud0_avf 0x1b9 /* audio att value front */ +#define aud0_avr 0x1ba /* audio att value rear */ +#define aud0_avc 0x1bb /* audio att value center */ +#define aud0_seg3 0x1bc /* segment 3 base register */ +#define aud0_offset 0x1bd /* offset address */ +#define aud0_wrl 0x1be /* working length register */ +#define aud0_war2 0x1bf /* working address register 2 */ + +/*----------------------------------------------------------------------------+ +| High speed memory controller 0 and 1. ++----------------------------------------------------------------------------*/ +#define hsmc0_gr 0x1e0 /* HSMC global register */ +#define hsmc0_besr 0x1e1 /* bus error status register */ +#define hsmc0_bear 0x1e2 /* bus error address register */ +#define hsmc0_br0 0x1e4 /* SDRAM sub-ctrl bank reg 0 */ +#define hsmc0_cr0 0x1e5 /* SDRAM sub-ctrl ctrl reg 0 */ +#define hsmc0_br1 0x1e7 /* SDRAM sub-ctrl bank reg 1 */ +#define hsmc0_cr1 0x1e8 /* SDRAM sub-ctrl ctrl reg 1 */ +#define hsmc0_sysr 0x1f1 /* system register */ +#define hsmc0_data 0x1f2 /* data register */ +#define hsmc0_crr 0x1f3 /* refresh register */ + +#define hsmc1_gr 0x1c0 /* HSMC global register */ +#define hsmc1_besr 0x1c1 /* bus error status register */ +#define hsmc1_bear 0x1c2 /* bus error address register */ +#define hsmc1_br0 0x1c4 /* SDRAM sub-ctrl bank reg 0 */ +#define hsmc1_cr0 0x1c5 /* SDRAM sub-ctrl ctrl reg 0 */ +#define hsmc1_br1 0x1c7 /* SDRAM sub-ctrl bank reg 1 */ +#define hsmc1_cr1 0x1c8 /* SDRAM sub-ctrl ctrl reg 1 */ +#define hsmc1_sysr 0x1d1 /* system register */ +#define hsmc1_data 0x1d2 /* data register */ +#define hsmc1_crr 0x1d3 /* refresh register */ + +/*----------------------------------------------------------------------------+ +| Machine State Register bit definitions. ++----------------------------------------------------------------------------*/ +#define msr_ape 0x00100000 +#define msr_apa 0x00080000 +#define msr_we 0x00040000 +#define msr_ce 0x00020000 +#define msr_ile 0x00010000 +#define msr_ee 0x00008000 +#define msr_pr 0x00004000 +#define msr_me 0x00001000 +#define msr_de 0x00000200 +#define msr_ir 0x00000020 +#define msr_dr 0x00000010 +#define msr_le 0x00000001 + +/*----------------------------------------------------------------------------+ +| Used during interrupt processing. ++----------------------------------------------------------------------------*/ +#define stack_reg_image_size 160 + +/*----------------------------------------------------------------------------+ +| Function prolog definition and other Metaware (EABI) defines. ++----------------------------------------------------------------------------*/ +#ifdef MW + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + +#define function_prolog(func_name) .text; \ + .align 2; \ + .globl func_name; \ + func_name: +#define function_epilog(func_name) .type func_name,@function; \ + .size func_name,.-func_name + +#define function_call(func_name) bl func_name + +#define stack_frame_min 8 +#define stack_frame_bc 0 +#define stack_frame_lr 4 +#define stack_neg_off 0 + +#endif + +/*----------------------------------------------------------------------------+ +| Function prolog definition and other DIAB (Elf) defines. ++----------------------------------------------------------------------------*/ +#ifdef ELF_DIAB + +fprolog: macro f_name + .text + .align 2 + .globl f_name +f_name: + endm + +fepilog: macro f_name + .type f_name,@function + .size f_name,.-f_name + endm + +#define function_prolog(func_name) fprolog func_name +#define function_epilog(func_name) fepilog func_name +#define function_call(func_name) bl func_name + +#define stack_frame_min 8 +#define stack_frame_bc 0 +#define stack_frame_lr 4 +#define stack_neg_off 0 + +#endif + +/*----------------------------------------------------------------------------+ +| Function prolog definition and other Xlc (XCOFF) defines. ++----------------------------------------------------------------------------*/ +#ifdef XCOFF + +.machine "403ga" + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + +#define function_prolog(func_name) .csect .func_name[PR]; \ + .globl .func_name[PR]; \ + func_name: + +#define function_epilog(func_name) .toc; \ + .csect func_name[DS]; \ + .globl func_name[DS]; \ + .long .func_name[PR]; \ + .long TOC[tc0] + +#define function_call(func_name) .extern .func_name[PR]; \ + stw r2,stack_frame_toc(r1); \ + mfspr r2,sprg0; \ + bl .func_name[PR]; \ + lwz r2,stack_frame_toc(r1) + +#define stack_frame_min 56 +#define stack_frame_bc 0 +#define stack_frame_lr 8 +#define stack_frame_toc 20 +#define stack_neg_off 276 + +#endif +#define function_prolog(func_name) .text; \ + .align 2; \ + .globl func_name; \ + func_name: +#define function_epilog(func_name) .type func_name,@function; \ + .size func_name,.-func_name + +#define function_call(func_name) bl func_name + +/*----------------------------------------------------------------------------+ +| Function prolog definition for GNU ++----------------------------------------------------------------------------*/ +#ifdef _GNU_TOOL + +#define function_prolog(func_name) .globl func_name; \ + func_name: +#define function_epilog(func_name) + +#endif diff -Nru a/arch/ppc/boot/simple/rw4/rw4_init.S b/arch/ppc/boot/simple/rw4/rw4_init.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/rw4/rw4_init.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,78 @@ +#define VESTA +#include "ppc_40x.h" +# + .align 2 + .text +# +# added by linguohui + .extern initb_ebiu0, initb_config, hdw_init_finish + .extern initb_hsmc0, initb_hsmc1, initb_cache +# end added + .globl HdwInit +# +HdwInit: +# +#-----------------------------------------------------------------------* +# If we are not executing from the FLASH get out * +#-----------------------------------------------------------------------* +# SAW keep this or comment out a la Hawthorne? +# r3 contains NIP when used with Linux +# rlwinm r28, r3, 8, 24, 31 # if MSB == 0xFF -> FLASH address +# cmpwi r28, 0xff +# bne locn01 +# +# +#------------------------------------------------------------------------ +# Init_cpu. Bank registers are setup for the IBM STB. +#------------------------------------------------------------------------ +# +# Setup processor core clock to be driven off chip. This is GPI4 bit +# twenty. Setup Open Drain, Output Select, Three-State Control, and +# Three-State Select registers. +# + + + pb0pesr = 0x054 + pb0pear = 0x056 + + mflr r30 + +#----------------------------------------------------------------------------- +# Vectors will be at 0x1F000000 +# Dummy Machine check handler just does RFI before true handler gets installed +#----------------------------------------------------------------------------- +#if 1 /* xuwentao added*/ +#ifdef SDRAM16MB + lis r10,0x0000 + addi r10,r10,0x0000 +#else + lis r10,0x1F00 + addi r10,r10,0x0000 +#endif + + mtspr evpr,r10 #EVPR: 0x0 or 0x1f000000 depending + isync # on SDRAM memory model used. + + lis r10,0xFFFF # clear PB0_PESR because some + ori r10,r10,0xFFFF # transitions from flash,changed by linguohui + mtdcr pb0pesr,r10 # to load RAM image via RiscWatch + lis r10,0x0000 # cause PB0_PESR machine checks + mtdcr pb0pear,r10 + addis r10,r10,0x0000 # clear the + mtxer r10 # XER just in case... +#endif /* xuwentao*/ + + bl initb_ebiu0 # init EBIU + + bl initb_config # config PPC and board + + + + +#------------------------------------------------------------------------ +# EVPR setup moved to top of this function. +#------------------------------------------------------------------------ +# + mtlr r30 + blr + .end diff -Nru a/arch/ppc/boot/simple/rw4/rw4_init_brd.S b/arch/ppc/boot/simple/rw4/rw4_init_brd.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/rw4/rw4_init_brd.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1125 @@ +/*----------------------------------------------------------------------------+ +| This source code has been made available to you by IBM on an AS-IS +| basis. Anyone receiving this source is licensed under IBM +| copyrights to use it in any way he or she deems fit, including +| copying it, modifying it, compiling it, and redistributing it either +| with or without modifications. No license under IBM patents or +| patent applications is to be implied by the copyright license. +| +| Any user of this software should understand that IBM cannot provide +| technical support for this software and will not be responsible for +| any consequences resulting from the use of this software. +| +| Any person who transfers this source code or any derivative work +| must include the IBM copyright notice, this paragraph, and the +| preceding two paragraphs in the transferred software. +| +| COPYRIGHT I B M CORPORATION 1997 +| LICENSED MATERIAL - PROGRAM PROPERTY OF I B M ++----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ +| Author: Tony J. Cerreto +| Component: BSPS +| File: init_brd.s +| Purpose: Vesta Evaluation Board initialization subroutines. The following +| routines are available: +| 1. INITB_EBIU0: Initialize EBIU0. +| 2. INITB_CONFIG: Configure board. +| 3. INITB_HSMC0: Initialize HSMC0 (SDRAM). +| 4. INITB_HSMC1: Initialize HSMC1 (SDRAM). +| 5. INITB_CACHE: Initialize Data and Instruction Cache. +| 6. INITB_DCACHE: Initialize Data Cache. +| 7. INITB_ICACHE: Initialize Instruction Cache. +| 8. INITB_GET_CSPD: Get CPU Speed (Bus Speed and Processor Speed) +| +| Changes: +| Date: Author Comment: +| --------- ------ -------- +| 01-Mar-00 tjc Created +| 04-Mar-00 jfh Modified CIC_SEL3_VAL to support 1284 (Mux3 & GPIO 21-28) +| 04-Mar-00 jfh Modified XILINIX Reg 0 to support 1284 (Mux3 & GPIO 21-28) +| 04-Mar-00 jfh Modified XILINIX Reg 1 to support 1284 (Mux3 & GPIO 21-28) +| 04-Mar-00 jfh Modified XILINIX Reg 4 to support 1284 (Mux3 & GPIO 21-28) +| 19-May-00 rlb Relcoated HSMC0 to 0x1F000000 to support 32MB of contiguous +| SDRAM space. Changed cache ctl regs to reflect this. +| 22-May-00 tjc Changed initb_get_cspd interface and eliminated +| initb_get_bspd routines. +| 26-May-00 tjc Added two nop instructions after all mtxxx/mfxxx +| instructions due to PPC405 bug. ++----------------------------------------------------------------------------*/ +#define VESTA +#include "ppc_40x.h" +#include "stb.h" + +/*----------------------------------------------------------------------------+ +| BOARD CONFIGURATION DEFINES ++----------------------------------------------------------------------------*/ +#define CBS0_CR_VAL 0x00000002 /* CBS control reg value */ +#define CIC0_CR_VAL 0xD0800448 /* CIC control reg value */ +#define CIC0_SEL3_VAL 0x11500000 /* CIC select 3 reg value */ +#define CIC0_VCR_VAL 0x00631700 /* CIC video cntl reg value */ + +/*----------------------------------------------------------------------------+ +| EBIU0 BANK REGISTERS DEFINES ++----------------------------------------------------------------------------*/ +#define EBIU0_BRCRH0_VAL 0x00000000 /* BR High 0 (Extension Reg)*/ +#define EBIU0_BRCRH1_VAL 0x00000000 /* BR High 1 (Extension Reg)*/ +#define EBIU0_BRCRH2_VAL 0x40000000 /* BR High 2 (Extension Reg)*/ +#define EBIU0_BRCRH3_VAL 0x40000000 /* BR High 3 (Extension Reg)*/ +#define EBIU0_BRCRH4_VAL 0x00000000 /* BR High 4 (Extension Reg)*/ +#define EBIU0_BRCRH5_VAL 0x00000000 /* BR High 5 (Extension Reg)*/ +#define EBIU0_BRCRH6_VAL 0x00000000 /* BR High 6 (Extension Reg)*/ +#define EBIU0_BRCRH7_VAL 0x40000000 /* BR High 7 (Extension Reg)*/ + +#define EBIU0_BRCR0_VAL 0xFC58BFFE /* BR 0: 16 bit Flash 4 MB */ +#define EBIU0_BRCR1_VAL 0xFF00BFFE /* BR 1: Ext Connector 1 MB */ +#if 1 +#define EBIU0_BRCR2_VAL 0x207CFFBE /* BR 2: Xilinx 8 MB */ + /* twt == 0x3f */ +#else +#define EBIU0_BRCR2_VAL 0x207CCFBE /* BR 2: Xilinx 8 MB */ + /* twt == 0x0f */ +#endif +#define EBIU0_BRCR3_VAL 0x407CBFBE /* BR 3: IDE Drive 8 MB */ +#define EBIU0_BRCR4_VAL 0xFF00BFFF /* BR 4: Disabled. 0 MB */ +#define EBIU0_BRCR5_VAL 0xFF00BFFF /* BR 5: Disabled. 0 MB */ +#define EBIU0_BRCR6_VAL 0xFF00BFFF /* BR 6: Disabled. 0 MB */ +#define EBIU0_BRCR7_VAL 0xCE3F0003 /* BR 7: Line Mode DMA 2 MB */ + +/*----------------------------------------------------------------------------+ +| GPIO DEFINES ++----------------------------------------------------------------------------*/ +#define STB_GPIO0_OUTPUT (STB_GPIO0_BASE_ADDRESS+ 0x00) +#define STB_GPIO0_TC (STB_GPIO0_BASE_ADDRESS+ 0x04) +#define STB_GPIO0_OS_0_31 (STB_GPIO0_BASE_ADDRESS+ 0x08) +#define STB_GPIO0_OS_32_63 (STB_GPIO0_BASE_ADDRESS+ 0x0C) +#define STB_GPIO0_TS_0_31 (STB_GPIO0_BASE_ADDRESS+ 0x10) +#define STB_GPIO0_TS_32_63 (STB_GPIO0_BASE_ADDRESS+ 0x14) +#define STB_GPIO0_OD (STB_GPIO0_BASE_ADDRESS+ 0x18) +#define STB_GPIO0_INPUT (STB_GPIO0_BASE_ADDRESS+ 0x1C) +#define STB_GPIO0_R1 (STB_GPIO0_BASE_ADDRESS+ 0x20) +#define STB_GPIO0_R2 (STB_GPIO0_BASE_ADDRESS+ 0x24) +#define STB_GPIO0_R3 (STB_GPIO0_BASE_ADDRESS+ 0x28) +#define STB_GPIO0_IS_1_0_31 (STB_GPIO0_BASE_ADDRESS+ 0x30) +#define STB_GPIO0_IS_1_32_63 (STB_GPIO0_BASE_ADDRESS+ 0x34) +#define STB_GPIO0_IS_2_0_31 (STB_GPIO0_BASE_ADDRESS+ 0x38) +#define STB_GPIO0_IS_2_32_63 (STB_GPIO0_BASE_ADDRESS+ 0x3C) +#define STB_GPIO0_IS_3_0_31 (STB_GPIO0_BASE_ADDRESS+ 0x40) +#define STB_GPIO0_IS_3_32_63 (STB_GPIO0_BASE_ADDRESS+ 0x44) +#define STB_GPIO0_SS_1 (STB_GPIO0_BASE_ADDRESS+ 0x50) +#define STB_GPIO0_SS_2 (STB_GPIO0_BASE_ADDRESS+ 0x54) +#define STB_GPIO0_SS_3 (STB_GPIO0_BASE_ADDRESS+ 0x58) + +#define GPIO0_TC_VAL 0x0C020004 /* three-state control val */ +#define GPIO0_OS_0_31_VAL 0x51A00004 /* output select 0-31 val */ +#define GPIO0_OS_32_63_VAL 0x0000002F /* output select 32-63 val */ +#define GPIO0_TS_0_31_VAL 0x51A00000 /* three-state sel 0-31 val*/ +#define GPIO0_TS_32_63_VAL 0x0000000F /* three-state sel 32-63 val*/ +#define GPIO0_OD_VAL 0xC0000004 /* open drain val */ +#define GPIO0_IS_1_0_31_VAL 0x50000151 /* input select 1 0-31 val */ +#define GPIO0_IS_1_32_63_VAL 0x00000000 /* input select 1 32-63 val */ +#define GPIO0_IS_2_0_31_VAL 0x00000000 /* input select 2 0-31 val */ +#define GPIO0_IS_2_32_63_VAL 0x00000000 /* input select 2 32-63 val */ +#define GPIO0_IS_3_0_31_VAL 0x00000440 /* input select 3 0-31 val */ +#define GPIO0_IS_3_32_63_VAL 0x00000000 /* input select 3 32-63 val */ +#define GPIO0_SS_1_VAL 0x00000000 /* sync select 1 val */ +#define GPIO0_SS_2_VAL 0x00000000 /* sync select 2 val */ +#define GPIO0_SS_3_VAL 0x00000000 /* sync select 3 val */ + +/*----------------------------------------------------------------------------+ +| XILINX DEFINES ++----------------------------------------------------------------------------*/ +#define STB_XILINX_LED (STB_FPGA_BASE_ADDRESS+ 0x0100) +#define STB_XILINX1_REG0 (STB_FPGA_BASE_ADDRESS+ 0x40000) +#define STB_XILINX1_REG1 (STB_FPGA_BASE_ADDRESS+ 0x40002) +#define STB_XILINX1_REG2 (STB_FPGA_BASE_ADDRESS+ 0x40004) +#define STB_XILINX1_REG3 (STB_FPGA_BASE_ADDRESS+ 0x40006) +#define STB_XILINX1_REG4 (STB_FPGA_BASE_ADDRESS+ 0x40008) +#define STB_XILINX1_REG5 (STB_FPGA_BASE_ADDRESS+ 0x4000A) +#define STB_XILINX1_REG6 (STB_FPGA_BASE_ADDRESS+ 0x4000C) +#define STB_XILINX1_ID (STB_FPGA_BASE_ADDRESS+ 0x4000E) +#define STB_XILINX1_FLUSH (STB_FPGA_BASE_ADDRESS+ 0x4000E) +#define STB_XILINX2_REG0 (STB_FPGA_BASE_ADDRESS+ 0x80000) +#define STB_XILINX2_REG1 (STB_FPGA_BASE_ADDRESS+ 0x80002) +#define STB_XILINX2_REG2 (STB_FPGA_BASE_ADDRESS+ 0x80004) + +#define XILINX1_R0_VAL 0x2440 /* Xilinx 1 Register 0 Val */ +#define XILINX1_R1_VAL 0x0025 /* Xilinx 1 Register 1 Val */ +#define XILINX1_R2_VAL 0x0441 /* Xilinx 1 Register 2 Val */ +#define XILINX1_R3_VAL 0x0008 /* Xilinx 1 Register 3 Val */ +#define XILINX1_R4_VAL 0x0100 /* Xilinx 1 Register 4 Val */ +#define XILINX1_R5_VAL 0x6810 /* Xilinx 1 Register 5 Val */ +#define XILINX1_R6_VAL 0x0000 /* Xilinx 1 Register 6 Val */ +#if 0 +#define XILINX2_R0_VAL 0x0008 /* Xilinx 2 Register 0 Val */ +#define XILINX2_R1_VAL 0x0000 /* Xilinx 2 Register 1 Val */ +#else +#define XILINX2_R0_VAL 0x0018 /* disable IBM IrDA RxD */ +#define XILINX2_R1_VAL 0x0008 /* enable SICC MAX chip */ +#endif +#define XILINX2_R2_VAL 0x0000 /* Xilinx 2 Register 2 Val */ + +/*----------------------------------------------------------------------------+ +| HSMC BANK REGISTERS DEFINES ++----------------------------------------------------------------------------*/ +#ifdef SDRAM16MB +#define HSMC0_BR0_VAL 0x000D2D55 /* 0x1F000000-007FFFFF R/W */ +#define HSMC0_BR1_VAL 0x008D2D55 /* 0x1F800000-1FFFFFFF R/W */ +#else +#define HSMC0_BR0_VAL 0x1F0D2D55 /* 0x1F000000-007FFFFF R/W */ +#define HSMC0_BR1_VAL 0x1F8D2D55 /* 0x1F800000-1FFFFFFF R/W */ +#endif +#define HSMC1_BR0_VAL 0xA00D2D55 /* 0xA0000000-A07FFFFF R/W */ +#define HSMC1_BR1_VAL 0xA08D2D55 /* 0xA0800000-A0FFFFFF R/W */ + +/*----------------------------------------------------------------------------+ +| CACHE DEFINES ++----------------------------------------------------------------------------*/ +#define DCACHE_NLINES 128 /* no. D-cache lines */ +#define DCACHE_NBYTES 32 /* no. bytes/ D-cache line */ +#define ICACHE_NLINES 256 /* no. I-cache lines */ +#define ICACHE_NBYTES 32 /* no. bytes/ I-cache line */ +#ifdef SDRAM16MB +#define DCACHE_ENABLE 0x80000000 /* D-cache regions to enable*/ +#define ICACHE_ENABLE 0x80000001 /* I-cache regions to enable*/ +#else +#define DCACHE_ENABLE 0x18000000 /* D-cache regions to enable*/ +#define ICACHE_ENABLE 0x18000001 /* I-cache regions to enable*/ +#endif + +/*----------------------------------------------------------------------------+ +| CPU CORE SPEED CALCULATION DEFINES ++----------------------------------------------------------------------------*/ +#define GCS_LCNT 500000 /* CPU speed loop count */ +#define GCS_TROW_BYTES 8 /* no. bytes in table row */ +#define GCS_CTICK_TOL 100 /* allowable clock tick tol */ +#define GCS_NMULT 4 /* no. of core speed mults */ + + /*--------------------------------------------------------------------+ + | No. 13.5Mhz + | Clock Ticks + | based on a + | loop count Bus + | of 100,000 Speed + +--------------------------------------------------------------------*/ +gcs_lookup_table: + .int 50000, 54000000 /* 54.0 Mhz */ + .int 66667, 40500000 /* 40.5 Mhz */ + .int 54545, 49500000 /* 49.5 Mhz */ + .int 46154, 58500000 /* 58.5 Mhz */ + .int 0, 0 /* end of table flag */ + + +/*****************************************************************************+ +| XXXXXXX XXX XXX XXXXXX XXXXXXX XXXXXX XX XX XX XXXX +| XX X XX XX X XX X XX X XX XX XXX XX XXXX XX +| XX X XXX XX XX X XX XX XXXX XX XX XX XX +| XXXX X XX XXXX XXXXX XX XXXX XX XX XX +| XX X XXX XX XX X XX XX XX XXX XXXXXX XX +| XX X XX XX XX XX X XX XX XX XX XX XX XX XX +| XXXXXXX XXX XXX XXXX XXXXXXX XXX XX XX XX XX XX XXXXXXX ++*****************************************************************************/ +/****************************************************************************** +| +| Routine: INITB_EBIU0. +| +| Purpose: Initialize all the EBIU0 Bank Registers +| Parameters: None. +| Returns: None. +| +******************************************************************************/ + function_prolog(initb_ebiu0) + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 0 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR0_VAL@h + ori r10,r10,EBIU0_BRCR0_VAL@l + mtdcr ebiu0_brcr0,r10 + lis r10,EBIU0_BRCRH0_VAL@h + ori r10,r10,EBIU0_BRCRH0_VAL@l + mtdcr ebiu0_brcrh0,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 1 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR1_VAL@h + ori r10,r10,EBIU0_BRCR1_VAL@l + mtdcr ebiu0_brcr1,r10 + lis r10,EBIU0_BRCRH1_VAL@h + ori r10,r10,EBIU0_BRCRH1_VAL@l + mtdcr ebiu0_brcrh1,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 2 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR2_VAL@h + ori r10,r10,EBIU0_BRCR2_VAL@l + mtdcr ebiu0_brcr2,r10 + lis r10,EBIU0_BRCRH2_VAL@h + ori r10,r10,EBIU0_BRCRH2_VAL@l + mtdcr ebiu0_brcrh2,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 3 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR3_VAL@h + ori r10,r10,EBIU0_BRCR3_VAL@l + mtdcr ebiu0_brcr3,r10 + lis r10,EBIU0_BRCRH3_VAL@h + ori r10,r10,EBIU0_BRCRH3_VAL@l + mtdcr ebiu0_brcrh3,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 4 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR4_VAL@h + ori r10,r10,EBIU0_BRCR4_VAL@l + mtdcr ebiu0_brcr4,r10 + lis r10,EBIU0_BRCRH4_VAL@h + ori r10,r10,EBIU0_BRCRH4_VAL@l + mtdcr ebiu0_brcrh4,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 5 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR5_VAL@h + ori r10,r10,EBIU0_BRCR5_VAL@l + mtdcr ebiu0_brcr5,r10 + lis r10,EBIU0_BRCRH5_VAL@h + ori r10,r10,EBIU0_BRCRH5_VAL@l + mtdcr ebiu0_brcrh5,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 6 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR6_VAL@h + ori r10,r10,EBIU0_BRCR6_VAL@l + mtdcr ebiu0_brcr6,r10 + lis r10,EBIU0_BRCRH6_VAL@h + ori r10,r10,EBIU0_BRCRH6_VAL@l + mtdcr ebiu0_brcrh6,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 7 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR7_VAL@h + ori r10,r10,EBIU0_BRCR7_VAL@l + mtdcr ebiu0_brcr7,r10 + lis r10,EBIU0_BRCRH7_VAL@h + ori r10,r10,EBIU0_BRCRH7_VAL@l + mtdcr ebiu0_brcrh7,r10 + + blr + function_epilog(initb_ebiu0) + + +/****************************************************************************** +| +| Routine: INITB_CONFIG +| +| Purpose: Configure the Vesta Evaluation Board. The following items +| will be configured: +| 1. Cross-Bar Switch. +| 2. Chip Interconnect. +| 3. Clear/reset key PPC registers. +| 4. Xilinx and GPIO Registers. +| +| Returns: None. +| +******************************************************************************/ + function_prolog(initb_config) + /*--------------------------------------------------------------------+ + | Init CROSS-BAR SWITCH + +--------------------------------------------------------------------*/ + lis r10,CBS0_CR_VAL@h /* r10 <- CBS Cntl Reg val */ + ori r10,r10,CBS0_CR_VAL@l + mtdcr cbs0_cr,r10 + + /*--------------------------------------------------------------------+ + | Init Chip-Interconnect (CIC) Registers + +--------------------------------------------------------------------*/ + lis r10,CIC0_CR_VAL@h /* r10 <- CIC Cntl Reg val */ + ori r10,r10,CIC0_CR_VAL@l + mtdcr cic0_cr,r10 + + lis r10,CIC0_SEL3_VAL@h /* r10 <- CIC SEL3 Reg val */ + ori r10,r10,CIC0_SEL3_VAL@l + mtdcr cic0_sel3,r10 + + lis r10,CIC0_VCR_VAL@h /* r10 <- CIC Vid C-Reg val */ + ori r10,r10,CIC0_VCR_VAL@l + mtdcr cic0_vcr,r10 + + /*--------------------------------------------------------------------+ + | Clear SGR and DCWR + +--------------------------------------------------------------------*/ + li r10,0x0000 + mtspr sgr,r10 + mtspr dcwr,r10 + + /*--------------------------------------------------------------------+ + | Clear/set up some machine state registers. + +--------------------------------------------------------------------*/ + li r10,0x0000 /* r10 <- 0 */ + mtdcr ebiu0_besr,r10 /* clr Bus Err Syndrome Reg */ + mtspr esr,r10 /* clr Exceptn Syndrome Reg */ + mttcr r10 /* timer control register */ + + mtdcr uic0_er,r10 /* disable all interrupts */ + + /* UIC_IIC0 | UIC_IIC1 | UIC_U0 | UIC_IR_RCV | UIC_IR_XMIT */ + lis r10, 0x00600e00@h + ori r10,r10,0x00600e00@l + mtdcr uic0_pr,r10 + + li r10,0x00000020 /* UIC_EIR1 */ + mtdcr uic0_tr,r10 + + lis r10,0xFFFF /* r10 <- 0xFFFFFFFF */ + ori r10,r10,0xFFFF /* */ + mtdbsr r10 /* clear/reset the dbsr */ + mtdcr uic0_sr,r10 /* clear pending interrupts */ + + li r10,0x1000 /* set Machine Exception bit*/ + oris r10,r10,0x2 /* set Criticl Exception bit*/ + mtmsr r10 /* change MSR */ + + /*--------------------------------------------------------------------+ + | Clear XER. + +--------------------------------------------------------------------*/ + li r10,0x0000 + mtxer r10 + + /*--------------------------------------------------------------------+ + | Init GPIO0 Registers + +--------------------------------------------------------------------*/ + lis r10, STB_GPIO0_TC@h /* Three-state control */ + ori r10,r10,STB_GPIO0_TC@l + lis r11, GPIO0_TC_VAL@h + ori r11,r11,GPIO0_TC_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_OS_0_31@h /* output select 0-31 */ + ori r10,r10,STB_GPIO0_OS_0_31@l + lis r11, GPIO0_OS_0_31_VAL@h + ori r11,r11,GPIO0_OS_0_31_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_OS_32_63@h /* output select 32-63 */ + ori r10,r10,STB_GPIO0_OS_32_63@l + lis r11, GPIO0_OS_32_63_VAL@h + ori r11,r11,GPIO0_OS_32_63_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_TS_0_31@h /* three-state select 0-31 */ + ori r10,r10,STB_GPIO0_TS_0_31@l + lis r11, GPIO0_TS_0_31_VAL@h + ori r11,r11,GPIO0_TS_0_31_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_TS_32_63@h /* three-state select 32-63 */ + ori r10,r10,STB_GPIO0_TS_32_63@l + lis r11, GPIO0_TS_32_63_VAL@h + ori r11,r11,GPIO0_TS_32_63_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_OD@h /* open drain */ + ori r10,r10,STB_GPIO0_OD@l + lis r11, GPIO0_OD_VAL@h + ori r11,r11,GPIO0_OD_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_1_0_31@h /* input select 1, 0-31 */ + ori r10,r10,STB_GPIO0_IS_1_0_31@l + lis r11, GPIO0_IS_1_0_31_VAL@h + ori r11,r11,GPIO0_IS_1_0_31_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_1_32_63@h /* input select 1, 32-63 */ + ori r10,r10,STB_GPIO0_IS_1_32_63@l + lis r11, GPIO0_IS_1_32_63_VAL@h + ori r11,r11,GPIO0_IS_1_32_63_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_2_0_31@h /* input select 2, 0-31 */ + ori r10,r10,STB_GPIO0_IS_2_0_31@l + lis r11, GPIO0_IS_2_0_31_VAL@h + ori r11,r11,GPIO0_IS_2_0_31_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_2_32_63@h /* input select 2, 32-63 */ + ori r10,r10,STB_GPIO0_IS_2_32_63@l + lis r11, GPIO0_IS_2_32_63_VAL@h + ori r11,r11,GPIO0_IS_2_32_63_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_3_0_31@h /* input select 3, 0-31 */ + ori r10,r10,STB_GPIO0_IS_3_0_31@l + lis r11, GPIO0_IS_3_0_31_VAL@h + ori r11,r11,GPIO0_IS_3_0_31_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_3_32_63@h /* input select 3, 32-63 */ + ori r10,r10,STB_GPIO0_IS_3_32_63@l + lis r11, GPIO0_IS_3_32_63_VAL@h + ori r11,r11,GPIO0_IS_3_32_63_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_SS_1@h /* sync select 1 */ + ori r10,r10,STB_GPIO0_SS_1@l + lis r11, GPIO0_SS_1_VAL@h + ori r11,r11,GPIO0_SS_1_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_SS_2@h /* sync select 2 */ + ori r10,r10,STB_GPIO0_SS_2@l + lis r11, GPIO0_SS_2_VAL@h + ori r11,r11,GPIO0_SS_2_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_SS_3@h /* sync select 3 */ + ori r10,r10,STB_GPIO0_SS_3@l + lis r11, GPIO0_SS_3_VAL@h + ori r11,r11,GPIO0_SS_3_VAL@l + stw r11,0(r10) + + /*--------------------------------------------------------------------+ + | Init Xilinx #1 Registers + +--------------------------------------------------------------------*/ + lis r10, STB_XILINX1_REG0@h /* init Xilinx1 Reg 0 */ + ori r10,r10,STB_XILINX1_REG0@l + li r11,XILINX1_R0_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG1@h /* init Xilinx1 Reg 1 */ + ori r10,r10,STB_XILINX1_REG1@l + li r11,XILINX1_R1_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG2@h /* init Xilinx1 Reg 2 */ + ori r10,r10,STB_XILINX1_REG2@l + li r11,XILINX1_R2_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG3@h /* init Xilinx1 Reg 3 */ + ori r10,r10,STB_XILINX1_REG3@l + li r11,XILINX1_R3_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG4@h /* init Xilinx1 Reg 4 */ + ori r10,r10,STB_XILINX1_REG4@l + li r11,XILINX1_R4_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG5@h /* init Xilinx1 Reg 5 */ + ori r10,r10,STB_XILINX1_REG5@l + li r11,XILINX1_R5_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG6@h /* init Xilinx1 Reg 6 */ + ori r10,r10,STB_XILINX1_REG6@l + li r11,XILINX1_R6_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_FLUSH@h /* latch registers in Xilinx*/ + ori r10,r10,STB_XILINX1_FLUSH@l + li r11,0x0000 + sth r11,0(r10) + + /*--------------------------------------------------------------------+ + | Init Xilinx #2 Registers + +--------------------------------------------------------------------*/ + lis r10, STB_XILINX2_REG0@h /* init Xilinx2 Reg 0 */ + ori r10,r10,STB_XILINX2_REG0@l + li r11,XILINX2_R0_VAL + sth r11,0(r10) + + lis r10, STB_XILINX2_REG1@h /* init Xilinx2 Reg 1 */ + ori r10,r10,STB_XILINX2_REG1@l + li r11,XILINX2_R1_VAL + sth r11,0(r10) + + lis r10, STB_XILINX2_REG2@h /* init Xilinx2 Reg 2 */ + ori r10,r10,STB_XILINX2_REG2@l + li r11,XILINX2_R2_VAL + sth r11,0(r10) + + blr + function_epilog(initb_config) + + +/****************************************************************************** +| +| Routine: INITB_HSMC0. +| +| Purpose: Initialize the HSMC0 Registers for SDRAM +| Parameters: None. +| Returns: R3 = 0: Successful +| = -1: Unsuccessful, SDRAM did not reset properly. +| +******************************************************************************/ + function_prolog(initb_hsmc0) + mflr r0 /* Save return addr */ + + /*--------------------------------------------------------------------+ + | Set Global SDRAM Controller to recommended default + +--------------------------------------------------------------------*/ + lis r10,0x6C00 + ori r10,r10,0x0000 + mtdcr hsmc0_gr,r10 + + /*--------------------------------------------------------------------+ + | Set HSMC0 Data Register to recommended default + +--------------------------------------------------------------------*/ + lis r10,0x0037 + ori r10,r10,0x0000 + mtdcr hsmc0_data,r10 + + /*--------------------------------------------------------------------+ + | Init HSMC0 Bank Register 0 + +--------------------------------------------------------------------*/ + lis r10,HSMC0_BR0_VAL@h + ori r10,r10,HSMC0_BR0_VAL@l + mtdcr hsmc0_br0,r10 + + /*--------------------------------------------------------------------+ + | Init HSMC0 Bank Register 1 + +--------------------------------------------------------------------*/ + lis r10,HSMC0_BR1_VAL@h + ori r10,r10,HSMC0_BR1_VAL@l + mtdcr hsmc0_br1,r10 + + /*--------------------------------------------------------------------+ + | Set HSMC0 Control Reg 0 + +--------------------------------------------------------------------*/ + lis r10,0x8077 /* PRECHARGE ALL DEVICE BKS */ + ori r10,r10,0x0000 + mtdcr hsmc0_cr0,r10 + li r3,0x0000 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne cr0,hsmc0_err + + lis r10,0x8078 /* AUTO-REFRESH */ + ori r10,r10,0x0000 + mtdcr hsmc0_cr0,r10 + li r3,0x0000 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne cr0,hsmc0_err + + lis r10,0x8070 /* PROG MODE W/DATA REG VAL */ + ori r10,r10,0x8000 + mtdcr hsmc0_cr0,r10 + li r3,0x0000 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne hsmc0_err + + /*--------------------------------------------------------------------+ + | Set HSMC0 Control Reg 1 + +--------------------------------------------------------------------*/ + lis r10,0x8077 /* PRECHARGE ALL DEVICE BKS */ + ori r10,r10,0x0000 + mtdcr hsmc0_cr1,r10 + li r3,0x0001 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne cr0,hsmc0_err + + lis r10,0x8078 /* AUTO-REFRESH */ + ori r10,r10,0x0000 + mtdcr hsmc0_cr1,r10 + li r3,0x0001 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne cr0,hsmc0_err + + lis r10,0x8070 /* PROG MODE W/DATA REG VAL */ + ori r10,r10,0x8000 + mtdcr hsmc0_cr1,r10 + li r3,0x0001 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne cr0,hsmc0_err + + /*--------------------------------------------------------------------+ + | Set HSMC0 Refresh Register + +--------------------------------------------------------------------*/ + lis r10,0x0FE1 + ori r10,r10,0x0000 + mtdcr hsmc0_crr,r10 + li r3,0 + +hsmc0_err: + mtlr r0 + blr + function_epilog(initb_hsmc0) + + +/****************************************************************************** +| +| Routine: INITB_HSMC1. +| +| Purpose: Initialize the HSMC1 Registers for SDRAM +| Parameters: None. +| Returns: R3 = 0: Successful +| = -1: Unsuccessful, SDRAM did not reset properly. +| +******************************************************************************/ + function_prolog(initb_hsmc1) + mflr r0 /* Save return addr */ + + /*--------------------------------------------------------------------+ + | Set Global SDRAM Controller to recommended default + +--------------------------------------------------------------------*/ + lis r10,0x6C00 + ori r10,r10,0x0000 + mtdcr hsmc1_gr,r10 + + /*--------------------------------------------------------------------+ + | Set HSMC1 Data Register to recommended default + +--------------------------------------------------------------------*/ + lis r10,0x0037 + ori r10,r10,0x0000 + mtdcr hsmc1_data,r10 + + /*--------------------------------------------------------------------+ + | Init HSMC1 Bank Register 0 + +--------------------------------------------------------------------*/ + lis r10,HSMC1_BR0_VAL@h + ori r10,r10,HSMC1_BR0_VAL@l + mtdcr hsmc1_br0,r10 + + /*--------------------------------------------------------------------+ + | Init HSMC1 Bank Register 1 + +--------------------------------------------------------------------*/ + lis r10,HSMC1_BR1_VAL@h + ori r10,r10,HSMC1_BR1_VAL@l + mtdcr hsmc1_br1,r10 + + /*--------------------------------------------------------------------+ + | Set HSMC1 Control Reg 0 + +--------------------------------------------------------------------*/ + lis r10,0x8077 /* PRECHARGE ALL DEVICE BANKS */ + ori r10,r10,0x0000 + mtdcr hsmc1_cr0,r10 + li r3,0x0002 + bl hsmc_cr_wait /* wait for operation completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + lis r10,0x8078 /* AUTO-REFRESH */ + ori r10,r10,0x0000 + mtdcr hsmc1_cr0,r10 + li r3,0x0002 + bl hsmc_cr_wait /* wait for operation completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + lis r10,0x8070 /* PROGRAM MODE W/DATA REG VALUE */ + ori r10,r10,0x8000 + mtdcr hsmc1_cr0,r10 + li r3,0x0002 + bl hsmc_cr_wait /* wait for operation completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + /*--------------------------------------------------------------------+ + | Set HSMC1 Control Reg 1 + +--------------------------------------------------------------------*/ + lis r10,0x8077 /* PRECHARGE ALL DEVICE BKS */ + ori r10,r10,0x0000 + mtdcr hsmc1_cr1,r10 + li r3,0x0003 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + lis r10,0x8078 /* AUTO-REFRESH */ + ori r10,r10,0x0000 + mtdcr hsmc1_cr1,r10 + li r3,0x0003 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + lis r10,0x8070 /* PROG MODE W/DATA REG VAL */ + ori r10,r10,0x8000 + mtdcr hsmc1_cr1,r10 + li r3,0x0003 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + /*--------------------------------------------------------------------+ + | Set HSMC1 Refresh Register + +--------------------------------------------------------------------*/ + lis r10,0x0FE1 + ori r10,r10,0x0000 + mtdcr hsmc1_crr,r10 + xor r3,r3,r3 + +hsmc1_err: + mtlr r0 + blr + function_epilog(initb_hsmc1) + + +/****************************************************************************** +| +| Routine: INITB_CACHE +| +| Purpose: This routine will enable Data and Instruction Cache. +| The Data Cache is an 8K two-way set associative and the +| Instruction Cache is an 16K two-way set associative cache. +| +| Parameters: None. +| +| Returns: None. +| +******************************************************************************/ + function_prolog(initb_cache) + mflr r0 /* Save return addr */ + + bl initb_Dcache /* enable D-Cache */ + bl initb_Icache /* enable I-Cache */ + + mtlr r0 + blr + function_epilog(initb_cache) + + +/****************************************************************************** +| +| Routine: INITB_DCACHE +| +| Purpose: This routine will invalidate all data in the Data Cache and +| then enable D-Cache. If cache is enabled already, the D-Cache +| will be flushed before the data is invalidated. +| +| Parameters: None. +| +| Returns: None. +| +******************************************************************************/ + function_prolog(initb_Dcache) + /*--------------------------------------------------------------------+ + | Flush Data Cache if enabled + +--------------------------------------------------------------------*/ + mfdccr r10 /* r10 <- DCCR */ + isync /* ensure prev insts done */ + cmpwi r10,0x00 + beq ic_dcinv /* D-cache off, invalidate */ + + /*--------------------------------------------------------------------+ + | Data Cache enabled, force known memory addresses to be Cached + +--------------------------------------------------------------------*/ + lis r10,HSMC0_BR0_VAL@h /* r10 <- first memory loc */ + andis. r10,r10,0xFFF0 + li r11,DCACHE_NLINES /* r11 <- # A-way addresses */ + addi r11,r11,DCACHE_NLINES /* r11 <- # B-way addresses */ + mtctr r11 /* set loop counter */ + +ic_dcload: + lwz r12,0(r10) /* force cache of address */ + addi r10,r10,DCACHE_NBYTES /* r10 <- next memory loc */ + bdnz ic_dcload + sync /* ensure prev insts done */ + isync + + /*--------------------------------------------------------------------+ + | Flush the known memory addresses from Cache + +--------------------------------------------------------------------*/ + lis r10,HSMC0_BR0_VAL@h /* r10 <- first memory loc */ + andis. r10,r10,0xFFF0 + mtctr r11 /* set loop counter */ + +ic_dcflush: + dcbf 0,r10 /* flush D-cache line */ + addi r10,r10,DCACHE_NBYTES /* r10 <- next memory loc */ + bdnz ic_dcflush + sync /* ensure prev insts done */ + isync + + /*--------------------------------------------------------------------+ + | Disable then invalidate Data Cache + +--------------------------------------------------------------------*/ + li r10,0 /* r10 <- 0 */ + mtdccr r10 /* disable the D-Cache */ + isync /* ensure prev insts done */ + +ic_dcinv: + li r10,0 /* r10 <- line address */ + li r11,DCACHE_NLINES /* r11 <- # lines in cache */ + mtctr r11 /* set loop counter */ + +ic_dcloop: + dccci 0,r10 /* invalidate A/B cache lns */ + addi r10,r10,DCACHE_NBYTES /* bump to next line */ + bdnz ic_dcloop + sync /* ensure prev insts done */ + isync + + /*--------------------------------------------------------------------+ + | Enable Data Cache + +--------------------------------------------------------------------*/ + lis r10,DCACHE_ENABLE@h /* r10 <- D-cache enable msk*/ + ori r10,r10,DCACHE_ENABLE@l + mtdccr r10 + sync /* ensure prev insts done */ + isync + + blr + function_epilog(initb_Dcache) + + +/****************************************************************************** +| +| Routine: INITB_ICACHE +| +| Purpose: This routine will invalidate all data in the Instruction +| Cache then enable I-Cache. +| +| Parameters: None. +| +| Returns: None. +| +******************************************************************************/ + function_prolog(initb_Icache) + /*--------------------------------------------------------------------+ + | Invalidate Instruction Cache + +--------------------------------------------------------------------*/ + li r10,0 /* r10 <- lines address */ + iccci 0,r10 /* invalidate all I-cache */ + sync /* ensure prev insts done */ + isync + + /*--------------------------------------------------------------------+ + | Enable Instruction Cache + +--------------------------------------------------------------------*/ + lis r10,ICACHE_ENABLE@h /* r10 <- I-cache enable msk*/ + ori r10,r10,ICACHE_ENABLE@l + mticcr r10 + sync /* ensure prev insts done */ + isync + + blr + function_epilog(initb_Icache) + +#if 0 +/****************************************************************************** +| +| Routine: INITB_GET_CSPD +| +| Purpose: Determine the CPU Core Speed. The 13.5 Mhz Time Base +| Counter (TBC) is used to measure a conditional branch +| instruction. +| +| Parameters: R3 = Address of Bus Speed +| R4 = Address of Core Speed +| +| Returns: (R3) = >0: Bus Speed. +| 0: Bus Speed not found in Look-Up Table. +| (R4) = >0: Core Speed. +| 0: Core Speed not found in Look-Up Table. +| +| Note: 1. This routine assumes the bdnz branch instruction takes +| two instruction cycles to complete. +| 2. This routine must be called before interrupts are enabled. +| +******************************************************************************/ + function_prolog(initb_get_cspd) + mflr r0 /* Save return address */ + /*--------------------------------------------------------------------+ + | Set-up timed loop + +--------------------------------------------------------------------*/ + lis r9,gcs_time_loop@h /* r9 <- addr loop instr */ + ori r9,r9,gcs_time_loop@l + lis r10,GCS_LCNT@h /* r10 <- loop count */ + ori r10,r10,GCS_LCNT@l + mtctr r10 /* ctr <- loop count */ + lis r11,STB_TIMERS_TBC@h /* r11 <- TBC register addr */ + ori r11,r11,STB_TIMERS_TBC@l + li r12,0 /* r12 <- 0 */ + + /*--------------------------------------------------------------------+ + | Cache timed-loop instruction + +--------------------------------------------------------------------*/ + icbt 0,r9 + sync + isync + + /*--------------------------------------------------------------------+ + | Get number of 13.5 Mhz cycles to execute time-loop + +--------------------------------------------------------------------*/ + stw r12,0(r11) /* reset TBC */ +gcs_time_loop: + bdnz+ gcs_time_loop /* force branch pred taken */ + lwz r5,0(r11) /* r5 <- num 13.5 Mhz ticks */ + li r6,5 /* LUT based on 1/5th the...*/ + divw r5,r5,r6 /*..loop count used */ + sync + isync + + /*--------------------------------------------------------------------+ + | Look-up core speed based on TBC value + +--------------------------------------------------------------------*/ + lis r6,gcs_lookup_table@h /* r6 <- pts at core spd LUT*/ + ori r6,r6,gcs_lookup_table@l + bl gcs_cspd_lookup /* find core speed in LUT */ + + mtlr r0 /* set return address */ + blr + function_epilog(initb_get_cspd) + +#endif +/*****************************************************************************+ +| XXXX XX XX XXXXXX XXXXXXX XXXXXX XX XX XX XXXX +| XX XXX XX X XX X XX X XX XX XXX XX XXXX XX +| XX XXXX XX XX XX X XX XX XXXX XX XX XX XX +| XX XX XXXX XX XXXX XXXXX XX XXXX XX XX XX +| XX XX XXX XX XX X XX XX XX XXX XXXXXX XX +| XX XX XX XX XX X XX XX XX XX XX XX XX XX +| XXXX XX XX XXXX XXXXXXX XXX XX XX XX XX XX XXXXXXX ++*****************************************************************************/ +/****************************************************************************** +| +| Routine: HSMC_CR_WAIT +| +| Purpose: Wait for the HSMC Control Register (bits 12-16) to be reset +| after an auto-refresh, pre-charge or program mode register +| command execution. +| +| Parameters: R3 = HSMC Control Register ID. +| 0: HSMC0 CR0 +| 1: HSMC0 CR1 +| 2: HSMC1 CR0 +| 3: HSMC1 CR1 +| +| Returns: R3 = 0: Successful +| -1: Unsuccessful +| +******************************************************************************/ +hsmc_cr_wait: + + li r11,10 /* r11 <- retry counter */ + mtctr r11 /* set retry counter */ + mr r11,r3 /* r11 <- HSMC CR reg id */ + +hsmc_cr_rep: + bdz hsmc_cr_err /* branch if max retries hit*/ + + /*--------------------------------------------------------------------+ + | GET HSMCx_CRx value based on HSMC Control Register ID + +--------------------------------------------------------------------*/ +try_hsmc0_cr0: /* CHECK IF ID=HSMC0 CR0 REG*/ + cmpwi cr0,r11,0x0000 + bne cr0,try_hsmc0_cr1 + mfdcr r10,hsmc0_cr0 /* r11 <- HSMC0 CR0 value */ + b hsmc_cr_read + +try_hsmc0_cr1: /* CHECK IF ID=HSMC0 CR1 REG*/ + cmpwi cr0,r11,0x0001 + bne cr0,try_hsmc1_cr0 + mfdcr r10,hsmc0_cr1 /* r10 <- HSMC0 CR1 value */ + b hsmc_cr_read + +try_hsmc1_cr0: /* CHECK IF ID=HSMC1 CR0 REG*/ + cmpwi cr0,r11,0x0002 + bne cr0,try_hsmc1_cr1 + mfdcr r10,hsmc1_cr0 /* r10 <- HSMC1 CR0 value */ + b hsmc_cr_read + +try_hsmc1_cr1: /* CHECK IF ID=HSMC1 CR1 REG*/ + cmpwi cr0,r11,0x0003 + bne cr0,hsmc_cr_err + mfdcr r10,hsmc1_cr1 /* r10 <- HSMC1 CR1 value */ + + /*--------------------------------------------------------------------+ + | Check if HSMC CR register was reset after command execution + +--------------------------------------------------------------------*/ +hsmc_cr_read: + lis r12,0x000F /* create "AND" mask */ + ori r12,r12,0x8000 + and. r10,r10,r12 /* r10 <- HSMC CR bits 12-16*/ + bne cr0,hsmc_cr_rep /* wait for bits to reset */ + li r3,0 /* set return code = success*/ + b hsmc_cr_done + +hsmc_cr_err: /* ERROR: SDRAM didn't reset*/ + li r3,-1 /* set RC=unsuccessful */ + +hsmc_cr_done: + blr + +#if 0 +/****************************************************************************** +| +| Routine: GCS_CSPD_LOOKUP +| +| Purpose: Uses the number of 13.5 Mhz clock ticks found after executing +| the branch instruction time loop to look-up the CPU Core Speed +| in the Core Speed Look-up Table. +| +| Parameters: R3 = Address of Bus Speed +| R4 = Address of Core Speed +| R5 = Number of 13.5 Mhz clock ticks found in time loop. +| R6 = Pointer to Core-Speed Look-Up Table +| +| Returns: (R3) = >0: Bus Speed. +| 0: Bus Speed not found in Look-Up Table. +| (R4) = >0: Core Speed. +| 0: Core Speed not found in Look-Up Table. +| +| Note: Core Speed = Bus Speed * Mult Factor (1-4x). +| +******************************************************************************/ +gcs_cspd_lookup: + + li r9,1 /* r9 <- core speed mult */ + /*--------------------------------------------------------------------+ + | Get theoritical number 13.5 Mhz ticks for a given Bus Speed from + | Look-up Table. Check all mult factors to determine if calculated + | value matches theoretical value (within a tolerance). + +--------------------------------------------------------------------*/ +gcs_cspd_loop: + lwz r10,0(r6) /* r10 <- no. ticks from LUT*/ + divw r10,r10,r9 /* r10 <- div mult (1-4x) */ + subi r11,r10,GCS_CTICK_TOL /* r11 <- no. tks low range */ + addi r12,r10,GCS_CTICK_TOL /* r12 <- no. tks high range*/ + + cmpw cr0,r5,r11 /* calc value within range? */ + blt gcs_cspd_retry /* less than low range */ + cmpw cr0,r5,r12 + bgt gcs_cspd_retry /* greater than high range */ + b gcs_cspd_fnd /* calc value within range */ + + /*--------------------------------------------------------------------+ + | SO FAR CORE SPEED NOT FOUND: Check next mult factor + +--------------------------------------------------------------------*/ +gcs_cspd_retry: + addi r9,r9,1 /* bump mult factor (1-4x) */ + cmpwi cr0,r9,GCS_NMULT + ble gcs_cspd_loop + + /*--------------------------------------------------------------------+ + | SO FAR CORE SPEED NOT FOUND: Point at next Bus Speed in LUT + +--------------------------------------------------------------------*/ + li r9,1 /* reset mult factor */ + addi r6,r6,GCS_TROW_BYTES /* point at next table entry*/ + lwz r10,0(r6) + cmpwi cr0,r10,0 /* check for EOT flag */ + bne gcs_cspd_loop + + /*--------------------------------------------------------------------+ + | COMPUTE CORE SPEED AND GET BUS SPEED FROM LOOK-UP TABLE + +--------------------------------------------------------------------*/ +gcs_cspd_fnd: + lwz r5,4(r6) /* r5 <- Bus Speed in LUT */ + mullw r6,r5,r9 /* r6 <- Core speed */ + stw r5,0(r3) /* (r3) <- Bus Speed */ + stw r6,0(r4) /* (r4) <- Core Speed */ + + blr +#endif diff -Nru a/arch/ppc/boot/simple/rw4/stb.h b/arch/ppc/boot/simple/rw4/stb.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/simple/rw4/stb.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,239 @@ +/*----------------------------------------------------------------------------+ +| This source code has been made available to you by IBM on an AS-IS +| basis. Anyone receiving this source is licensed under IBM +| copyrights to use it in any way he or she deems fit, including +| copying it, modifying it, compiling it, and redistributing it either +| with or without modifications. No license under IBM patents or +| patent applications is to be implied by the copyright license. +| +| Any user of this software should understand that IBM cannot provide +| technical support for this software and will not be responsible for +| any consequences resulting from the use of this software. +| +| Any person who transfers this source code or any derivative work +| must include the IBM copyright notice, this paragraph, and the +| preceding two paragraphs in the transferred software. +| +| COPYRIGHT I B M CORPORATION 1999 +| LICENSED MATERIAL - PROGRAM PROPERTY OF I B M ++----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ +| Author: Maciej P. Tyrlik +| Component: Include file. +| File: stb.h +| Purpose: Common Set-tob-box definitions. +| Changes: +| Date: Comment: +| ----- -------- +| 14-Jan-97 Created for ElPaso pass 1 MPT +| 13-May-97 Added function prototype and global variables MPT +| 08-Dec-98 Added RAW IR task information MPT +| 19-Jan-99 Port to Romeo MPT +| 19-May-00 Changed SDRAM to 32MB contiguous 0x1F000000 - 0x20FFFFFF RLB ++----------------------------------------------------------------------------*/ + +#ifndef _stb_h_ +#define _stb_h_ + +/*----------------------------------------------------------------------------+ +| Read/write from I/O macros. ++----------------------------------------------------------------------------*/ +#define inbyte(port) (*((unsigned char volatile *)(port))) +#define outbyte(port,data) *(unsigned char volatile *)(port)=\ + (unsigned char)(data) + +#define inshort(port) (*((unsigned short volatile *)(port))) +#define outshort(port,data) *(unsigned short volatile *)(port)=\ + (unsigned short)(data) + +#define inword(port) (*((unsigned long volatile *)(port))) +#define outword(port,data) *(unsigned long volatile *)(port)=\ + (unsigned long)(data) + +/*----------------------------------------------------------------------------+ +| STB interrupts. ++----------------------------------------------------------------------------*/ +#define STB_XP_TP_INT 0 +#define STB_XP_APP_INT 1 +#define STB_AUD_INT 2 +#define STB_VID_INT 3 +#define STB_DMA0_INT 4 +#define STB_DMA1_INT 5 +#define STB_DMA2_INT 6 +#define STB_DMA3_INT 7 +#define STB_SCI_INT 8 +#define STB_I2C1_INT 9 +#define STB_I2C2_INT 10 +#define STB_GPT_PWM0 11 +#define STB_GPT_PWM1 12 +#define STB_SCP_INT 13 +#define STB_SSP_INT 14 +#define STB_GPT_PWM2 15 +#define STB_EXT5_INT 16 +#define STB_EXT6_INT 17 +#define STB_EXT7_INT 18 +#define STB_EXT8_INT 19 +#define STB_SCC_INT 20 +#define STB_SICC_RECV_INT 21 +#define STB_SICC_TRAN_INT 22 +#define STB_PPU_INT 23 +#define STB_DCRX_INT 24 +#define STB_EXT0_INT 25 +#define STB_EXT1_INT 26 +#define STB_EXT2_INT 27 +#define STB_EXT3_INT 28 +#define STB_EXT4_INT 29 +#define STB_REDWOOD_ENET_INT STB_EXT1_INT + +/*----------------------------------------------------------------------------+ +| STB tasks, task stack sizes, and task priorities. The actual task priority +| is 1 more than the specified number since priority 0 is reserved (system +| internaly adds 1 to supplied priority number). ++----------------------------------------------------------------------------*/ +#define STB_IDLE_TASK_SS (5* 1024) +#define STB_IDLE_TASK_PRIO 0 +#define STB_LEDTEST_SS (2* 1024) +#define STB_LEDTEST_PRIO 0 +#define STB_CURSOR_TASK_SS (10* 1024) +#define STB_CURSOR_TASK_PRIO 7 +#define STB_MPEG_TASK_SS (10* 1024) +#define STB_MPEG_TASK_PRIO 9 +#define STB_DEMUX_TASK_SS (10* 1024) +#define STB_DEMUX_TASK_PRIO 20 +#define RAW_STB_IR_TASK_SS (10* 1024) +#define RAW_STB_IR_TASK_PRIO 20 + +#define STB_SERIAL_ER_TASK_SS (10* 1024) +#define STB_SERIAL_ER_TASK_PRIO 1 +#define STB_CA_TASK_SS (10* 1024) +#define STB_CA_TASK_PRIO 8 + +#define INIT_DEFAULT_VIDEO_SS (10* 1024) +#define INIT_DEFAULT_VIDEO_PRIO 8 +#define INIT_DEFAULT_SERVI_SS (10* 1024) +#define INIT_DEFAULT_SERVI_PRIO 8 +#define INIT_DEFAULT_POST_SS (10* 1024) +#define INIT_DEFAULT_POST_PRIO 8 +#define INIT_DEFAULT_INTER_SS (10* 1024) +#define INIT_DEFAULT_INTER_PRIO 8 +#define INIT_DEFAULT_BR_SS (10* 1024) +#define INIT_DEFAULT_BR_PRIO 8 +#define INITIAL_TASK_STACK_SIZE (32* 1024) + +#ifdef VESTA +/*----------------------------------------------------------------------------+ +| Vesta Overall Address Map (all addresses are double mapped, bit 0 of the +| address is not decoded. Numbers below are dependent on board configuration. +| FLASH, SDRAM, DRAM numbers can be affected by actual board setup. +| +| FFE0,0000 - FFFF,FFFF FLASH +| F200,0000 - F210,FFFF FPGA logic +| Ethernet = F200,0000 +| LED Display = F200,0100 +| Xilinx #1 Regs = F204,0000 +| Xilinx #2 Regs = F208,0000 +| Spare = F20C,0000 +| IDE CS0 = F210,0000 +| F410,0000 - F410,FFFF IDE CS1 +| C000,0000 - C7FF,FFFF OBP +| C000,0000 - C000,0014 SICC (16550 + infra red) +| C001,0000 - C001,0018 PPU (Parallel Port) +| C002,0000 - C002,001B SC0 (Smart Card 0) +| C003,0000 - C003,000F I2C0 +| C004,0000 - C004,0009 SCC (16550 UART) +| C005,0000 - C005,0124 GPT (Timers) +| C006,0000 - C006,0058 GPIO0 +| C007,0000 - C007,001b SC1 (Smart Card 1) +| C008,0000 - C008,FFFF Unused +| C009,0000 - C009,FFFF Unused +| C00A,0000 - C00A,FFFF Unused +| C00B,0000 - C00B,000F I2C1 +| C00C,0000 - C00C,0006 SCP +| C00D,0000 - C00D,0010 SSP +| A000,0000 - A0FF,FFFF SDRAM1 (16M) +| 0000,0000 - 00FF,FFFF SDRAM0 (16M) ++----------------------------------------------------------------------------*/ +#define STB_FLASH_BASE_ADDRESS 0xFFE00000 +#define STB_FPGA_BASE_ADDRESS 0xF2000000 +#define STB_SICC_BASE_ADDRESS 0xC0000000 +#define STB_PPU_BASE_ADDR 0xC0010000 +#define STB_SC0_BASE_ADDRESS 0xC0020000 +#define STB_I2C1_BASE_ADDRESS 0xC0030000 +#define STB_SCC_BASE_ADDRESS 0xC0040000 +#define STB_TIMERS_BASE_ADDRESS 0xC0050000 +#define STB_GPIO0_BASE_ADDRESS 0xC0060000 +#define STB_SC1_BASE_ADDRESS 0xC0070000 +#define STB_I2C2_BASE_ADDRESS 0xC00B0000 +#define STB_SCP_BASE_ADDRESS 0xC00C0000 +#define STB_SSP_BASE_ADDRESS 0xC00D0000 +/*----------------------------------------------------------------------------+ +|The following are used by the IBM RTOS SW. +|15-May-00 Changed these values to reflect movement of base addresses in +|order to support 32MB of contiguous SDRAM space. +|Points to the cacheable region since these values are used in IBM RTOS +|to establish the vector address. ++----------------------------------------------------------------------------*/ +#define STB_SDRAM1_BASE_ADDRESS 0x20000000 +#define STB_SDRAM1_SIZE 0x01000000 +#define STB_SDRAM0_BASE_ADDRESS 0x1F000000 +#define STB_SDRAM0_SIZE 0x01000000 + +#else +/*----------------------------------------------------------------------------+ +| ElPaso Overall Address Map (all addresses are double mapped, bit 0 of the +| address is not decoded. Numbers below are dependent on board configuration. +| FLASH, SDRAM, DRAM numbers can be affected by actual board setup. OPB +| devices are inside the ElPaso chip. +| FFE0,0000 - FFFF,FFFF FLASH +| F144,0000 - F104,FFFF FPGA logic +| F140,0000 - F100,0000 ethernet (through FPGA logic) +| C000,0000 - C7FF,FFFF OBP +| C000,0000 - C000,0014 SICC (16550+ infra red) +| C001,0000 - C001,0016 PPU (parallel port) +| C002,0000 - C002,001B SC (smart card) +| C003,0000 - C003,000F I2C 1 +| C004,0000 - C004,0009 SCC (16550 UART) +| C005,0000 - C005,0124 Timers +| C006,0000 - C006,0058 GPIO0 +| C007,0000 - C007,0058 GPIO1 +| C008,0000 - C008,0058 GPIO2 +| C009,0000 - C009,0058 GPIO3 +| C00A,0000 - C00A,0058 GPIO4 +| C00B,0000 - C00B,000F I2C 2 +| C00C,0000 - C00C,0006 SCP +| C00D,0000 - C00D,0006 SSP +| A000,0000 - A0FF,FFFF SDRAM 16M +| 0000,0000 - 00FF,FFFF DRAM 16M ++----------------------------------------------------------------------------*/ +#define STB_FLASH_BASE_ADDRESS 0xFFE00000 +#define STB_FPGA_BASE_ADDRESS 0xF1440000 +#define STB_ENET_BASE_ADDRESS 0xF1400000 +#define STB_SICC_BASE_ADDRESS 0xC0000000 +#define STB_PPU_BASE_ADDR 0xC0010000 +#define STB_SC_BASE_ADDRESS 0xC0020000 +#define STB_I2C1_BASE_ADDRESS 0xC0030000 +#define STB_SCC_BASE_ADDRESS 0xC0040000 +#define STB_TIMERS_BASE_ADDRESS 0xC0050000 +#define STB_GPIO0_BASE_ADDRESS 0xC0060000 +#define STB_GPIO1_BASE_ADDRESS 0xC0070000 +#define STB_GPIO2_BASE_ADDRESS 0xC0080000 +#define STB_GPIO3_BASE_ADDRESS 0xC0090000 +#define STB_GPIO4_BASE_ADDRESS 0xC00A0000 +#define STB_I2C2_BASE_ADDRESS 0xC00B0000 +#define STB_SCP_BASE_ADDRESS 0xC00C0000 +#define STB_SSP_BASE_ADDRESS 0xC00D0000 +#define STB_SDRAM_BASE_ADDRESS 0xA0000000 +#endif + +/*----------------------------------------------------------------------------+ +| Other common defines. ++----------------------------------------------------------------------------*/ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#endif /* _stb_h_ */ diff -Nru a/arch/ppc/boot/tree/Makefile b/arch/ppc/boot/tree/Makefile --- a/arch/ppc/boot/tree/Makefile Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,66 +0,0 @@ -# BK Id: SCCS/s.Makefile 1.7 06/15/01 13:16:10 paulus -# -# -# Module name: Makefile -# -# Description: -# Makefile for the IBM "tree" evaluation board Linux kernel -# boot loaders. -# -# -# Copyright (c) 1999 Grant Erickson -# -# PPC-405 modification -# Copyright 2000-2001 MontaVista Software Inc. -# Author: MontaVista Software, Inc. -# frank_rowand@mvista.com or source@mvista.com -# debbie_chu@mvista.com -# - -HOSTCFLAGS = -O -I$(TOPDIR)/include - -CC = $(CROSS_COMPILE)gcc -LD = $(CROSS_COMPILE)ld -OBJCOPY = $(CROSS_COMPILE)objcopy -OBJDUMP = $(CROSS_COMPILE)objdump - -GZIP = gzip -vf9 -RM = rm -f -MKEVIMG = ../utils/mkevimg -l -c -MKIRIMG = ../utils/mkirimg -CFLAGS += -I$(TOPDIR)/drivers/net -LD_ARGS = -e _start -T ld.script -Ttext 0x00200000 -Bstatic - -OBJS = ../common/crt0.o main.o misc.o irSect.o ../common/string.o \ - ../common/misc-common.o ../common/ns16550.o -LIBS = ../lib/zlib.a - -treeboot: $(OBJS) $(LIBS) ld.script - $(LD) -o $@ $(LD_ARGS) $(OBJS) $(LIBS) - -zImage: vmlinux.img - -zImage.initrd: vmlinux.initrd.img - -treeboot.image: treeboot - $(OBJCOPY) --add-section=image=../images/vmlinux.gz treeboot $@ - -treeboot.initrd: treeboot.image ramdisk.image.gz - $(OBJCOPY) --add-section=initrd=ramdisk.image.gz treeboot.image $@ - -vmlinux.img: treeboot.image - $(OBJDUMP) --syms treeboot.image | grep irSectStart > irSectStart.txt - $(MKIRIMG) treeboot.image treeboot.image.out irSectStart.txt - $(MKEVIMG) treeboot.image.out ../images/vmlinux.tree.img - $(RM) treeboot.image treeboot.image.out irSectStart.txt - -vmlinux.initrd.img: treeboot.initrd - $(OBJDUMP) --all-headers treeboot.initrd | grep irSectStart > irSectStart.txt - $(MKIRIMG) treeboot.initrd treeboot.initrd.out irSectStart.txt - $(MKEVIMG) treeboot.initrd.out ../images/vmlinux.tree.initrd.img - $(RM) treeboot.initrd treeboot.initrd.out irSectStart.txt - -clean: - rm -f treeboot treeboot.image treeboot.initrd irSectStart.txt vmlinux.* - -include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc/boot/tree/irSect.c b/arch/ppc/boot/tree/irSect.c --- a/arch/ppc/boot/tree/irSect.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,39 +0,0 @@ -/* - * BK Id: SCCS/s.irSect.c 1.6 05/18/01 15:17:22 cort - */ -/* - * - * Copyright (c) 1999 Grant Erickson - * - * Module name: irSect.c - * - * Description: - * Defines variables to hold the absolute starting address and size - * of the Linux kernel "image" and the initial RAM disk "initrd" - * sections within the boot loader. - * - */ - -#include "irSect.h" - - -/* - * The order of globals below must not change. If more globals are added, - * you must change the script 'mkirimg' accordingly. - * - */ - -/* - * irSectStart must be at beginning of file - */ -unsigned int irSectStart = 0xdeadbeaf; - -unsigned int imageSect_start = 0; -unsigned int imageSect_size = 0; -unsigned int initrdSect_start = 0; -unsigned int initrdSect_size = 0; - -/* - * irSectEnd must be at end of file - */ -unsigned int irSectEnd = 0xdeadbeaf; diff -Nru a/arch/ppc/boot/tree/irSect.h b/arch/ppc/boot/tree/irSect.h --- a/arch/ppc/boot/tree/irSect.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,35 +0,0 @@ -/* - * BK Id: SCCS/s.irSect.h 1.6 05/18/01 15:17:22 cort - */ -/* - * - * Copyright (c) 1999 Grant Erickson - * - * Module name: irSect.h - * - * Description: - * Defines variables to hold the absolute starting address and size - * of the Linux kernel "image" and the initial RAM disk "initrd" - * sections within the boot loader. - * - */ - -#ifndef __IRSECT_H__ -#define __IRSECT_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -extern unsigned int imageSect_start; -extern unsigned int imageSect_size; - -extern unsigned int initrdSect_start; -extern unsigned int initrdSect_size; - - -#ifdef __cplusplus -} -#endif - -#endif /* __IRSECT_H__ */ diff -Nru a/arch/ppc/boot/tree/ld.script b/arch/ppc/boot/tree/ld.script --- a/arch/ppc/boot/tree/ld.script Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,69 +0,0 @@ -OUTPUT_ARCH(powerpc) -SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); -/* Do we need any of these for elf? - __DYNAMIC = 0; */ -SECTIONS -{ - /* Read-only sections, merged into text segment: */ - . = + SIZEOF_HEADERS; - .interp : { *(.interp) } - .hash : { *(.hash) } - .dynsym : { *(.dynsym) } - .dynstr : { *(.dynstr) } - .rel.text : { *(.rel.text) } - .rela.text : { *(.rela.text) } - .rel.data : { *(.rel.data) } - .rela.data : { *(.rela.data) } - .rel.rodata : { *(.rel.rodata) } - .rela.rodata : { *(.rela.rodata) } - .rel.got : { *(.rel.got) } - .rela.got : { *(.rela.got) } - .rel.ctors : { *(.rel.ctors) } - .rela.ctors : { *(.rela.ctors) } - .rel.dtors : { *(.rel.dtors) } - .rela.dtors : { *(.rela.dtors) } - .rel.bss : { *(.rel.bss) } - .rela.bss : { *(.rela.bss) } - .rel.plt : { *(.rel.plt) } - .rela.plt : { *(.rela.plt) } - .init : { *(.init) } =0 - .plt : { *(.plt) } - .text : - { - *(.text) - *(.rodata) - *(.rodata.*) - *(.rodata1) - *(.got1) - } - .fini : { *(.fini) } =0 - .ctors : { *(.ctors) } - .dtors : { *(.dtors) } - _etext = .; - PROVIDE (etext = .); - /* Read-write section, merged into data segment: */ - . = (. + 0x0FFF) & 0xFFFFF000; - .data : - { - *(.data) - *(.data1) - *(.sdata) - *(.sdata2) - *(.got.plt) *(.got) - *(.dynamic) - CONSTRUCTORS - } - _edata = .; - PROVIDE (edata = .); - __bss_start = .; - .bss : - { - *(.sbss) *(.scommon) - *(.dynbss) - *(.bss) - *(COMMON) - } - _end = . ; - PROVIDE (end = .); -} - diff -Nru a/arch/ppc/boot/tree/main.c b/arch/ppc/boot/tree/main.c --- a/arch/ppc/boot/tree/main.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,212 +0,0 @@ -/* - * BK Id: SCCS/s.main.c 1.9 06/15/01 13:16:10 paulus - */ -/* - * Copyright (c) 1997 Paul Mackerras - * Initial Power Macintosh COFF version. - * Copyright (c) 1999 Grant Erickson - * Modifications for an ELF-based IBM evaluation board version. - * Copyright 2000-2001 MontaVista Software Inc. - * PPC405GP modifications - * Author: MontaVista Software, Inc. - * frank_rowand@mvista.com or source@mvista.com - * debbie_chu@mvista.com - * - * Module name: main.c - * - * Description: - * This module does most of the real work for the boot loader. It - * checks the variables holding the absolute start address and size - * of the Linux kernel "image" and initial RAM disk "initrd" sections - * and if they are present, moves them to their "proper" locations. - * - * For the Linux kernel, "proper" is physical address 0x00000000. - * For the RAM disk, "proper" is the image's size below the top - * of physical memory. The Linux kernel may be in either raw - * binary form or compressed with GNU zip (aka gzip). - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - */ - -#include -#include - -#include "nonstdio.h" -#include "irSect.h" -#if defined(CONFIG_SERIAL_CONSOLE) -#include "ns16550.h" -#endif /* CONFIG_SERIAL_CONSOLE */ - - -/* Preprocessor Defines */ - -/* - * Location of the IBM boot ROM function pointer address for retrieving - * the board information structure. - */ - -#define BOARD_INFO_VECTOR 0xFFFE0B50 - -/* - * Warning: the board_info doesn't contain valid data until get_board_info() - * gets called in start(). - */ -#define RAM_SIZE board_info.bi_memsize - -#define RAM_PBASE 0x00000000 -#define RAM_PEND (RAM_PBASE + RAM_SIZE) - -#define RAM_VBASE 0xC0000000 -#define RAM_VEND (RAM_VBASE + RAM_SIZE) - -#define RAM_START RAM_PBASE -#define RAM_END RAM_PEND -#define RAM_FREE (imageSect_start + imageSect_size + initrdSect_size) - -#define PROG_START RAM_START - - -/* Function Macros */ - -#define ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) - -/* Global Variables */ - -/* Needed by zalloc and zfree for allocating memory */ - -char *avail_ram; /* Indicates start of RAM available for heap */ -char *end_avail; /* Indicates end of RAM available for heap */ - -/* Needed for serial I/O. -*/ -extern unsigned long *com_port; - -bd_t board_info; - -/* -** The bootrom may change bootrom_cmdline to point to a buffer in the -** bootrom. -*/ -char *bootrom_cmdline = ""; -char treeboot_bootrom_cmdline[512]; - -#ifdef CONFIG_CMDLINE -char *cmdline = CONFIG_CMDLINE; -#else -char *cmdline = ""; -#endif - -/* Function Prototypes */ - -extern void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp); - -void -kick_watchdog(void) -{ -#ifdef CONFIG_405GP - mtspr(SPRN_TSR, (TSR_ENW | TSR_WIS)); -#endif -} - -void start(void) -{ - void *options; - int ns, oh, i; - unsigned long sa, len; - void *dst; - unsigned char *im; - unsigned long initrd_start, initrd_size; - bd_t *(*get_board_info)(void) = - (bd_t *(*)(void))(*(unsigned long *)BOARD_INFO_VECTOR); - bd_t *bip = NULL; - - - com_port = (struct NS16550 *)serial_init(0); - -#ifdef CONFIG_405GP - /* turn off on-chip ethernet */ - /* This is to fix a problem with early walnut bootrom. */ - - { - /* Physical mapping of ethernet register space. */ - static struct ppc405_enet_regs *ppc405_enet_regp = - (struct ppc405_enet_regs *)PPC405_EM0_REG_ADDR; - - mtdcr(DCRN_MALCR, MALCR_MMSR); /* 1st reset MAL */ - - while (mfdcr(DCRN_MALCR) & MALCR_MMSR) {}; /* wait for the reset */ - - ppc405_enet_regp->em0mr0 = 0x20000000; /* then reset EMAC */ - } -#endif - - if ((bip = get_board_info()) != NULL) - memcpy(&board_info, bip, sizeof(bd_t)); - - /* Init RAM disk (initrd) section */ - - kick_watchdog(); - - if (initrdSect_start != 0 && (initrd_size = initrdSect_size) != 0) { - initrd_start = (RAM_END - initrd_size) & ~0xFFF; - - _printk("Initial RAM disk at 0x%08x (%u bytes)\n", - initrd_start, initrd_size); - - memcpy((char *)initrd_start, - (char *)(initrdSect_start), - initrdSect_size); - - end_avail = (char *)initrd_start; - } else { - initrd_start = initrd_size = 0; - end_avail = (char *)RAM_END; - } - - /* Linux kernel image section */ - - kick_watchdog(); - - im = (unsigned char *)(imageSect_start); - len = imageSect_size; - dst = (void *)PROG_START; - - /* Check for the gzip archive magic numbers */ - - if (im[0] == 0x1f && im[1] == 0x8b) { - - /* The gunzip routine needs everything nice and aligned */ - - void *cp = (void *)ALIGN_UP(RAM_FREE, 8); - avail_ram = (void *)(cp + ALIGN_UP(len, 8)); /* used by zalloc() */ - memcpy(cp, im, len); - - /* I'm not sure what the 0x200000 parameter is for, but it works. */ - /* It tells gzip the end of the area you wish to reserve, and it - * can use data past that point....unfortunately, this value - * isn't big enough (luck ran out). -- Dan - */ - - gunzip(dst, 0x400000, cp, (int *)&len); - } else { - memmove(dst, im, len); - } - - kick_watchdog(); - - flush_cache(dst, len); - - sa = (unsigned long)dst; - - (*(void (*)())sa)(&board_info, - initrd_start, - initrd_start + initrd_size, - cmdline, - cmdline + strlen(cmdline)); - - pause(); -} diff -Nru a/arch/ppc/boot/tree/misc.S b/arch/ppc/boot/tree/misc.S --- a/arch/ppc/boot/tree/misc.S Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,43 +0,0 @@ -/* - * BK Id: SCCS/s.misc.S 1.7 05/18/01 06:20:29 patch - */ -/* - * Copyright (C) Paul Mackerras 1997. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include "../../kernel/ppc_asm.tmpl" - - .text - -/* - * Flush the dcache and invalidate the icache for a range of addresses. - * - * flush_cache(addr, len) - */ - .global flush_cache -flush_cache: - mfpvr r5 # Get processor version register - extrwi r5,r5,16,0 # Get the version bits - cmpwi cr0,r5,0x0020 # Is this a 403-based processor? - beq 1f # Yes, it is - li r5,32 # It is not a 403, set to 32 bytes - addi r4,r4,32-1 # len += line_size - 1 - srwi. r4,r4,5 # Convert from bytes to lines - b 2f -1: li r5,16 # It is a 403, set to 16 bytes - addi r4,r4,16-1 # len += line_size - 1 - srwi. r4,r4,4 # Convert from bytes to lines -2: mtctr r4 # Set-up the counter register - beqlr # If it is 0, we are done -3: dcbf r0,r3 # Flush and invalidate the data line - icbi r0,r3 # Invalidate the instruction line - add r3,r3,r5 # Move to the next line - bdnz 3b # Are we done yet? - sync - isync - blr # Return to the caller diff -Nru a/arch/ppc/boot/utils/Makefile b/arch/ppc/boot/utils/Makefile --- a/arch/ppc/boot/utils/Makefile Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/boot/utils/Makefile Tue Feb 19 18:08:59 2002 @@ -10,7 +10,8 @@ all: dummy # Simple programs with 1 file and no extra CFLAGS -UTILS = addnote hack-coff mkprep mksimage mknote piggyback mkpmon mkbugboot +UTILS = addnote hack-coff mkprep mknote mkbugboot mktree \ + addSystemMap addRamdDisk $(UTILS): $(HOSTCC) $(HOSTCFLAGS) -o $@ $@.c diff -Nru a/arch/ppc/boot/utils/addRamDisk.c b/arch/ppc/boot/utils/addRamDisk.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/utils/addRamDisk.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include +#include +#include + +#define ElfHeaderSize (64 * 1024) +#define ElfPages (ElfHeaderSize / 4096) +#define KERNELBASE (0xc0000000) + +void get4k(FILE *file, char *buf ) +{ + unsigned j; + unsigned num = fread(buf, 1, 4096, file); + for ( j=num; j<4096; ++j ) + buf[j] = 0; +} + +void put4k(FILE *file, char *buf ) +{ + fwrite(buf, 1, 4096, file); +} + +void death(const char *msg, FILE *fdesc, const char *fname) +{ + printf(msg); + fclose(fdesc); + unlink(fname); + exit(1); +} + +int main(int argc, char **argv) +{ + char inbuf[4096]; + FILE *ramDisk = NULL; + FILE *inputVmlinux = NULL; + FILE *outputVmlinux = NULL; + unsigned i = 0; + u_int32_t ramFileLen = 0; + u_int32_t ramLen = 0; + u_int32_t roundR = 0; + u_int32_t kernelLen = 0; + u_int32_t actualKernelLen = 0; + u_int32_t round = 0; + u_int32_t roundedKernelLen = 0; + u_int32_t ramStartOffs = 0; + u_int32_t ramPages = 0; + u_int32_t roundedKernelPages = 0; + u_int32_t hvReleaseData = 0; + u_int32_t eyeCatcher = 0xc8a5d9c4; + u_int32_t naca = 0; + u_int32_t xRamDisk = 0; + u_int32_t xRamDiskSize = 0; + if ( argc < 2 ) { + printf("Name of RAM disk file missing.\n"); + exit(1); + } + + if ( argc < 3 ) { + printf("Name of vmlinux file missing.\n"); + exit(1); + } + + if ( argc < 4 ) { + printf("Name of vmlinux output file missing.\n"); + exit(1); + } + + ramDisk = fopen(argv[1], "r"); + if ( ! ramDisk ) { + printf("RAM disk file \"%s\" failed to open.\n", argv[1]); + exit(1); + } + inputVmlinux = fopen(argv[2], "r"); + if ( ! inputVmlinux ) { + printf("vmlinux file \"%s\" failed to open.\n", argv[2]); + exit(1); + } + outputVmlinux = fopen(argv[3], "w+"); + if ( ! outputVmlinux ) { + printf("output vmlinux file \"%s\" failed to open.\n", argv[3]); + exit(1); + } + fseek(ramDisk, 0, SEEK_END); + ramFileLen = ftell(ramDisk); + fseek(ramDisk, 0, SEEK_SET); + printf("%s file size = %d\n", argv[1], ramFileLen); + + ramLen = ramFileLen; + + roundR = 4096 - (ramLen % 4096); + if ( roundR ) { + printf("Rounding RAM disk file up to a multiple of 4096, adding %d\n", roundR); + ramLen += roundR; + } + + printf("Rounded RAM disk size is %d\n", ramLen); + fseek(inputVmlinux, 0, SEEK_END); + kernelLen = ftell(inputVmlinux); + fseek(inputVmlinux, 0, SEEK_SET); + printf("kernel file size = %d\n", kernelLen); + if ( kernelLen == 0 ) { + printf("You must have a linux kernel specified as argv[2]\n"); + exit(1); + } + + actualKernelLen = kernelLen - ElfHeaderSize; + + printf("actual kernel length (minus ELF header) = %d\n", actualKernelLen); + + round = actualKernelLen % 4096; + roundedKernelLen = actualKernelLen; + if ( round ) + roundedKernelLen += (4096 - round); + + printf("actual kernel length rounded up to a 4k multiple = %d\n", roundedKernelLen); + + ramStartOffs = roundedKernelLen; + ramPages = ramLen / 4096; + + printf("RAM disk pages to copy = %d\n", ramPages); + + // Copy 64K ELF header + for (i=0; i<(ElfPages); ++i) { + get4k( inputVmlinux, inbuf ); + put4k( outputVmlinux, inbuf ); + } + + roundedKernelPages = roundedKernelLen / 4096; + + fseek(inputVmlinux, ElfHeaderSize, SEEK_SET); + + for ( i=0; i +#include +#include +#include +#include + +void xlate( char * inb, char * trb, unsigned len ) +{ + unsigned i; + for ( i=0; i> 4; + char c2 = c & 0xf; + if ( c1 > 9 ) + c1 = c1 + 'A' - 10; + else + c1 = c1 + '0'; + if ( c2 > 9 ) + c2 = c2 + 'A' - 10; + else + c2 = c2 + '0'; + *trb++ = c1; + *trb++ = c2; + } + *trb = 0; +} + +#define ElfHeaderSize (64 * 1024) +#define ElfPages (ElfHeaderSize / 4096) +#define KERNELBASE (0xc0000000) + +void get4k( /*istream *inf*/FILE *file, char *buf ) +{ + unsigned j; + unsigned num = fread(buf, 1, 4096, file); + for ( j=num; j<4096; ++j ) + buf[j] = 0; +} + +void put4k( /*ostream *outf*/FILE *file, char *buf ) +{ + fwrite(buf, 1, 4096, file); +} + +int main(int argc, char **argv) +{ + char inbuf[4096]; + FILE *ramDisk = NULL; + FILE *inputVmlinux = NULL; + FILE *outputVmlinux = NULL; + unsigned i = 0; + unsigned long ramFileLen = 0; + unsigned long ramLen = 0; + unsigned long roundR = 0; + unsigned long kernelLen = 0; + unsigned long actualKernelLen = 0; + unsigned long round = 0; + unsigned long roundedKernelLen = 0; + unsigned long ramStartOffs = 0; + unsigned long ramPages = 0; + unsigned long roundedKernelPages = 0; + if ( argc < 2 ) { + printf("Name of System Map file missing.\n"); + exit(1); + } + + if ( argc < 3 ) { + printf("Name of vmlinux file missing.\n"); + exit(1); + } + + if ( argc < 4 ) { + printf("Name of vmlinux output file missing.\n"); + exit(1); + } + + ramDisk = fopen(argv[1], "r"); + if ( ! ramDisk ) { + printf("System Map file \"%s\" failed to open.\n", argv[1]); + exit(1); + } + inputVmlinux = fopen(argv[2], "r"); + if ( ! inputVmlinux ) { + printf("vmlinux file \"%s\" failed to open.\n", argv[2]); + exit(1); + } + outputVmlinux = fopen(argv[3], "w"); + if ( ! outputVmlinux ) { + printf("output vmlinux file \"%s\" failed to open.\n", argv[3]); + exit(1); + } + fseek(ramDisk, 0, SEEK_END); + ramFileLen = ftell(ramDisk); + fseek(ramDisk, 0, SEEK_SET); + printf("%s file size = %ld\n", argv[1], ramFileLen); + + ramLen = ramFileLen; + + roundR = 4096 - (ramLen % 4096); + if ( roundR ) { + printf("Rounding System Map file up to a multiple of 4096, adding %ld\n", roundR); + ramLen += roundR; + } + + printf("Rounded System Map size is %ld\n", ramLen); + fseek(inputVmlinux, 0, SEEK_END); + kernelLen = ftell(inputVmlinux); + fseek(inputVmlinux, 0, SEEK_SET); + printf("kernel file size = %ld\n", kernelLen); + if ( kernelLen == 0 ) { + printf("You must have a linux kernel specified as argv[2]\n"); + exit(1); + } + + actualKernelLen = kernelLen - ElfHeaderSize; + + printf("actual kernel length (minus ELF header) = %ld\n", actualKernelLen); + + round = actualKernelLen % 4096; + roundedKernelLen = actualKernelLen; + if ( round ) + roundedKernelLen += (4096 - round); + + printf("actual kernel length rounded up to a 4k multiple = %ld\n", roundedKernelLen); + + ramStartOffs = roundedKernelLen; + ramPages = ramLen / 4096; + + printf("System map pages to copy = %ld\n", ramPages); + + // Copy 64K ELF header + for (i=0; i<(ElfPages); ++i) { + get4k( inputVmlinux, inbuf ); + put4k( outputVmlinux, inbuf ); + } + + + + roundedKernelPages = roundedKernelLen / 4096; + + fseek(inputVmlinux, ElfHeaderSize, SEEK_SET); + + { + for ( i=0; i + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define ELF_HEADER_SIZE 65536 + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __i386__ +#define cpu_to_be32(x) le32_to_cpu(x) +#define cpu_to_be16(x) le16_to_cpu(x) +#else +#define cpu_to_be32(x) (x) +#define cpu_to_be16(x) (x) +#endif + +#define cpu_to_le32(x) le32_to_cpu((x)) +unsigned long le32_to_cpu(unsigned long x) +{ + return (((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24)); +} + +#define cpu_to_le16(x) le16_to_cpu((x)) +unsigned short le16_to_cpu(unsigned short x) +{ + return (((x & 0x00ff) << 8) | + ((x & 0xff00) >> 8)); +} + +/* size of read buffer */ +#define SIZE 0x1000 + +/* typedef long int32_t; */ +typedef unsigned long uint32_t; +typedef unsigned short uint16_t; +typedef unsigned char uint8_t; + +/* PPCBUG ROM boot header */ +typedef struct bug_boot_header { + uint8_t magic_word[4]; /* "BOOT" */ + uint32_t entry_offset; /* Offset from top of header to code */ + uint32_t routine_length; /* Length of code */ + uint8_t routine_name[8]; /* Name of the boot code */ +} bug_boot_header_t; + +#define HEADER_SIZE sizeof(bug_boot_header_t) + +uint32_t copy_image(int32_t in_fd, int32_t out_fd) +{ + uint8_t buf[SIZE]; + int n; + uint32_t image_size = 0; + uint8_t zero = 0; + + lseek(in_fd, ELF_HEADER_SIZE, SEEK_SET); + + /* Copy an image while recording its size */ + while ( (n = read(in_fd, buf, SIZE)) > 0 ) + { + image_size = image_size + n; + write(out_fd, buf, n); + } + + /* BUG romboot requires that our size is divisible by 2 */ + /* align image to 2 byte boundary */ + if (image_size % 2) + { + image_size++; + write(out_fd, &zero, 1); + } + + return image_size; +} + +void write_bugboot_header(int32_t out_fd, uint32_t boot_size) +{ + uint8_t header_block[HEADER_SIZE]; + bug_boot_header_t *bbh = (bug_boot_header_t *)&header_block[0]; + + bzero(header_block, HEADER_SIZE); + + /* Fill in the PPCBUG ROM boot header */ + strncpy(bbh->magic_word, "BOOT", 4); /* PPCBUG magic word */ + bbh->entry_offset = cpu_to_be32(HEADER_SIZE); /* Entry address */ + bbh->routine_length= cpu_to_be32(HEADER_SIZE+boot_size+2); /* Routine length */ + strncpy(bbh->routine_name, "LINUXROM", 8); /* Routine name */ + + /* Output the header and bootloader to the file */ + write(out_fd, header_block, HEADER_SIZE); +} + +uint16_t calc_checksum(int32_t bug_fd) +{ + uint32_t checksum_var = 0; + uint8_t buf[2]; + int n; + + /* Checksum loop */ + while ( (n = read(bug_fd, buf, 2) ) ) + { + checksum_var = checksum_var + *(uint16_t *)buf; + + /* If we carry out, mask it and add one to the checksum */ + if (checksum_var >> 16) + checksum_var = (checksum_var & 0x0000ffff) + 1; + } + + return checksum_var; +} + +int main(int argc, char *argv[]) +{ + int32_t image_fd, bugboot_fd; + int argptr = 1; + uint32_t kernel_size = 0; + uint16_t checksum = 0; + uint8_t bugbootname[256]; + + if ( (argc != 3) ) + { + fprintf(stderr, "usage: %s \n",argv[0]); + exit(-1); + } + + /* Get file args */ + + /* kernel image file */ + if ((image_fd = open( argv[argptr] , 0)) < 0) + exit(-1); + argptr++; + + /* bugboot file */ + if ( !strcmp( argv[argptr], "-" ) ) + bugboot_fd = 1; /* stdout */ + else + if ((bugboot_fd = creat( argv[argptr] , 0755)) < 0) + exit(-1); + else + strcpy(bugbootname, argv[argptr]); + argptr++; + + /* Set file position after ROM header block where zImage will be written */ + lseek(bugboot_fd, HEADER_SIZE, SEEK_SET); + + /* Copy kernel image into bugboot image */ + kernel_size = copy_image(image_fd, bugboot_fd); + close(image_fd); + + /* Set file position to beginning where header/romboot will be written */ + lseek(bugboot_fd, 0, SEEK_SET); + + /* Write out BUG header/romboot */ + write_bugboot_header(bugboot_fd, kernel_size); + + /* Close bugboot file */ + close(bugboot_fd); + + /* Reopen it as read/write */ + bugboot_fd = open(bugbootname, O_RDWR); + + /* Calculate checksum */ + checksum = calc_checksum(bugboot_fd); + + /* Write out the calculated checksum */ + write(bugboot_fd, &checksum, 2); + + return 0; +} diff -Nru a/arch/ppc/boot/utils/mkevimg b/arch/ppc/boot/utils/mkevimg --- a/arch/ppc/boot/utils/mkevimg Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,488 +0,0 @@ -#!/usr/bin/perl - -# -# Copyright (c) 1998-1999 TiVo, Inc. -# All rights reserved. -# -# Copyright (c) 1999 Grant Erickson -# Major syntactic and usability rework. -# -# Module name: mkevimg -# -# Description: -# Converts an ELF output file from the linker into the format used by -# the IBM evaluation board ROM Monitor to load programs from a host -# onto the evaluation board. The ELF file must be an otherwise execut- -# able file (with the text and data addresses bound at link time) and -# have space reserved after the entry point for the load information -# block: -# -# typedef struct boot_block { -# unsigned long magic; 0x0052504F -# unsigned long dest; Target address of the image -# unsigned long num_512blocks; Size, rounded-up, in 512 byte blocks -# unsigned long debug_flag; Run the debugger or image after load -# unsigned long entry_point; The image address to jump to after load -# unsigned long checksum; 32 bit checksum including header -# unsigned long reserved[2]; -# } boot_block_t; -# -# - -use File::Basename; -use Getopt::Std; - -# -# usage() -# -# Description: -# This routine prints out the proper command line usage for this program -# -# Input(s): -# status - Flag determining what usage information will be printed and what -# the exit status of the program will be after the information is -# printed. -# -# Output(s): -# N/A -# -# Returns: -# This subroutine does not return. -# - -sub usage { - my($status); - $status = $_[0]; - - printf("Usage: %s [-hlvV] \n", - $program); - - if ($status != 0) { - printf("Try `%s -h' for more information.\n", $program); - } - - if ($status != 1) { - print(" -c Put checksum in load information block.\n"); - print(" -h Print out this message and exit.\n"); - print(" -l Linux mode; if present, copy 'image' and 'initrd' sections.\n"); - print(" -v Verbose. Print out lots of ELF information.\n"); - print(" -V Print out version information and exit.\n"); - } - - exit($status); -} - -# -# version() -# -# Description: -# This routine prints out program version information -# -# Input(s): -# N/A -# -# Output(s): -# N/A -# -# Returns: -# This subroutine does not return. -# - -sub version { - print("mkevimg Version 1.1.0\n"); - print("Copyright (c) 1998-1999 TiVo, Inc.\n"); - print("Copyright (c) 1999 Grant Erickson \n"); - - exit (0); -} - -# -# file_check() -# -# Description: -# This routine checks an input file to ensure that it exists, is a -# regular file, and is readable. -# -# Input(s): -# file - Input file to be checked. -# -# Output(s): -# N/A -# -# Returns: -# 0 if the file exists, is a regular file, and is readable, otherwise -1. -# - -sub file_check { - my($file); - $file = $_[0]; - - if (!(-e $file)) { - printf("The file \"%s\" does not exist.\n", $file); - return (-1); - } elsif (!(-f $file)) { - printf("The file \"%s\" is not a regular file.\n", $file); - return (-1); - } elsif (!(-r $file)) { - printf("The file \"%s\" is not readable.\n", $file); - return (-1); - } - - return (0); -} - -# -# decode_options() -# -# Description: -# This routine steps through the command-line arguments, parsing out -# recognzied options. -# -# Input(s): -# N/A -# -# Output(s): -# N/A -# -# Returns: -# N/A -# - -sub decode_options { - - if (!getopts("chlvV")) { - usage(1); - } - - if ($opt_c) { - $do_checksum = 1; - } - - if ($opt_h) { - usage(0); - } - - if ($opt_l) { - $linux = 1; - } - - if ($opt_V) { - version(); - exit (0); - } - - if ($opt_v) { - $verbose = 1; - } - - if (!($ifile = shift(@ARGV))) { - usage(1); - } - - if (!($ofile = shift(@ARGV))) { - usage (1); - } - - if (file_check($ifile)) { - exit(1); - } - -} - -# -# ELF file and section header field numbers -# - -require '../utils/elf.pl'; - -# -# Main program body -# - -{ - $program = basename($0); - - decode_options(); - - open(ELF, "<$ifile") || die "Cannot open input file"; - - $ifilesize = (-s $ifile); - - if ($verbose) { - print("Output file: $ofile\n"); - print("Input file: $ifile, $ifilesize bytes.\n"); - } - - if (read(ELF, $ibuf, $ifilesize) != $ifilesize) { - print("Failed to read input file!\n"); - exit(1); - } - - # - # Parse ELF header - # - - @eh = unpack("a16n2N5n6", $ibuf); - - # - # Make sure this is actually a PowerPC ELF file. - # - - if (substr($eh[$e_ident], 0, 4) ne "\177ELF") { - printf("The file \"%s\" is not an ELF file.\n", $ifile); - exit (1); - } elsif ($eh[$e_machine] != 20) { - printf("The file \"%s\" is not a PowerPC ELF file.\n", $ifile); - exit (1); - } - - if ($verbose) { - print("File header:\n"); - printf(" Identifier: %s\n", $eh[$e_ident]); - printf(" Type: %d\n", $eh[$e_type]); - printf(" Machine: %d\n", $eh[$e_machine]); - printf(" Version: %d\n", $eh[$e_version]); - printf(" Entry point: 0x%08x\n", $eh[$e_entry]); - printf(" Program header offset: 0x%x\n", $eh[$e_phoff]); - printf(" Section header offset: 0x%x\n", $eh[$e_shoff]); - printf(" Flags: 0x%08x\n", $eh[$e_flags]); - printf(" Header size: %d\n", $eh[$e_ehsize]); - printf(" Program entry size: %d\n", $eh[$e_phentsize]); - printf(" Program table entries: %d\n", $eh[$e_phnum]); - printf(" Section header size: %d\n", $eh[$e_shentsize]); - printf(" Section table entries: %d\n", $eh[$e_shnum]); - printf(" String table section: %d\n", $eh[$e_shstrndx]); - } - - # - # Find the section header for the string table. - # - - $strtable_section_offset = $eh[$e_shoff] + - $eh[$e_shstrndx] * $eh[$e_shentsize]; - - if ($verbose) { - printf("String table section header offset: 0x%x\n", - $strtable_section_offset); - } - - # - # Find the start of the string table. - # - - @strh = unpack("N10", substr($ibuf, $strtable_section_offset, - $eh[$e_shentsize])); - - if ($verbose) { - printf("Section name strings start at: 0x%x, %d bytes.\n", - $strh[$sh_offset], $strh[$sh_size]); - } - - $names = substr($ibuf, $strh[$sh_offset], $strh[$sh_size]); - - # Grab each section header and find '.text' and '.bss' sections in - # particular. - - if ($verbose) { - print("Section headers:\n"); - print("Idx Name Size Address File off Algn\n"); - print("--- ------------------------ -------- -------- -------- ----\n"); - } - - $off = $eh[$e_shoff]; - - for($i = 0; $i < $eh[$e_shnum]; $i++, $off += $eh[$e_shentsize]) { - @sh = unpack("N10", substr($ibuf, $off, $eh[$e_shentsize])); - - # Take the first section name from the array returned by split. - - ($name) = split(/\000/, substr($names, $sh[$sh_name])); - - if ($verbose) { - printf("%3d %-24s %8x %08x %08x %4d\n", - $i, $name, $sh[$sh_size], $sh[$sh_addr], - $sh[$sh_offset], $sh[$sh_addralign]); - } - - # Attempt to find the .text and .bss sections - - if ($name =~ /^\.bss$/) { - ($bss_addr, $bss_offset, $bss_size) = - ($sh[$sh_addr], $sh[$sh_offset], $sh[$sh_size]); - - } elsif ($name =~ /^\.text$/) { - ($text_addr, $text_offset, $text_size) = - ($sh[$sh_addr], $sh[$sh_offset], $sh[$sh_size]); - - } elsif ($linux && ($name =~ /^\image$/)) { - $image_found = 1; - - ($image_addr, $image_offset, $image_size) = - ($sh[$sh_addr], $sh[$sh_offset], $sh[$sh_size]); - - } elsif ($linux && ($name =~ /^\initrd$/)) { - $initrd_found = 1; - - ($initrd_addr, $initrd_offset, $initrd_size) = - ($sh[$sh_addr], $sh[$sh_offset], $sh[$sh_size]); - - } - } - - printf("Text section - Address: 0x%08x, Size: 0x%08x\n", - $text_addr, $text_size); - printf("Bss section - Address: 0x%08x, Size: 0x%08x\n", - $bss_addr, $bss_size); - - if ($linux) { - if ($image_found) { - printf("Image section - Address: 0x%08x, Size: 0x%08x\n", - $image_addr, $image_size); - } - - if ($initrd_found) { - printf("Initrd section - Address: 0x%08x, Size: 0x%08x\n", - $initrd_addr, $initrd_size); - } - } - - # - # Open output file - # - - open(BOOT, ">$ofile") || die "Cannot open output file"; - - # - # Compute image size - # - - $output_size = $bss_offset - $text_offset + $bss_size; - - if ($linux && $image_found) { - $output_size += $image_size; - } - - if ($linux && $initrd_found) { - $output_size += $initrd_size; - } - - # - # Compute size with header - # - - $header = pack("H8N7", "0052504f", 0, 0, 0, 0, 0, 0, 0); - $num_blocks = ($output_size + length($header) + 511) / 512; - - # - # Write IBM PowerPC evaluation board boot_block_t header - # - - $header = pack("H8N7", "0052504f", $text_addr, $num_blocks, 0, - $text_addr, 0, 0, 0); - - - $bytes = length($header); - - if (($resid = syswrite(BOOT, $header, $bytes)) != $bytes) { - die("Could not write boot image header to output file."); - } - - printf("Entry point = 0x%08x\n", $text_addr); - printf("Image size = 0x%08x (%d bytes) (%d blocks).\n", - $output_size, $output_size, $num_blocks); - - # - # Write image starting after ELF and program headers and - # continuing to beginning of bss - # - - $bytes = $bss_offset - $text_offset + $bss_size; - - if (($resid = syswrite(BOOT, $ibuf, $bytes, $text_offset)) != $bytes) { - die("Could not write boot image to output file.\n"); - } - - # - # If configured, write out the image and initrd sections as well - # - - if ($linux) { - if ($image_found) { - $bytes = $image_size; - if (($resid = syswrite(BOOT, $ibuf, $bytes, $image_offset)) != $bytes) { - die("Could not write boot image to output file.\n"); - } - } - - if ($initrd_found) { - $bytes = $initrd_size; - if (($resid = syswrite(BOOT, $ibuf, $bytes, $initrd_offset)) != $bytes) { - die("Could not write boot image to output file.\n"); - } - } - } - - # - # Pad to a multiple of 512 bytes - # If the (size of the boot image mod 512) is between 509 and 511 bytes - # then the tftp to the Walnut fails. This may be fixed in more recent - # Walnut bootrom. - # - - $pad_size = 512 - ((length($header) + $output_size) % 512); - if ($pad_size == 512) { - $pad_size = 0; - } - - if ($pad_size != 0) { - - if ($verbose) { - print("Padding boot image by an additional $pad_size bytes.\n"); - } - - $pad_string = pack("H8","deadbeef") x 128; - - syswrite(BOOT, $pad_string, $pad_size) or - die "Could not pad boot image in output file.\n"; - - } - - # - # Compute 32 bit checksum over entire file. - # - - if ($do_checksum) { - - close(BOOT); - open(BOOT, "+<$ofile") || die "Cannot open output file"; - undef $/; - $temp = unpack("%32N*", ); - # Solaris and PPC Linux return 0x80000000 for "-$temp" when $temp - # is negative. "~($temp - 1)" negates $temp properly. - $csum = ~($temp - 1); - printf("Checksum = 0x%08x\r\n", $csum); - - # - # Rewrite IBM PowerPC evaluation board boot_block_t header, - # this time with the checksum included - # - - $header = pack("H8N7", "0052504f", $text_addr, $num_blocks, 0, - $text_addr, $csum, 0, 0); - - seek(BOOT, 0, 0); - syswrite(BOOT, $header, length($header)) or - die("Could not write boot image header to output file."); - - } - - # - # Clean-up and leave - # - - close(BOOT); - - print("\nBoot image file \"$ofile\" built successfully.\n\n"); - - exit(0); -} diff -Nru a/arch/ppc/boot/utils/mkimage.wrapper b/arch/ppc/boot/utils/mkimage.wrapper --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/utils/mkimage.wrapper Tue Feb 19 18:09:00 2002 @@ -0,0 +1,16 @@ +#!/bin/bash + +# +# Build PPCBoot image when `mkimage' tool is available. +# + +MKIMAGE=$(type -path mkimage) + +if [ -z "${MKIMAGE}" ]; then + # Doesn't exist + echo '"mkimage" command not found - PPCBoot images will not be built' >&2 + exit 0; +fi + +# Call "mkimage" to create PPCBoot image +${MKIMAGE} "$@" diff -Nru a/arch/ppc/boot/utils/mkirimg b/arch/ppc/boot/utils/mkirimg --- a/arch/ppc/boot/utils/mkirimg Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,367 +0,0 @@ -#!/usr/bin/perl -# -# Copyright (c) 1998-1999 TiVo, Inc. -# Original ELF parsing code. -# -# Copyright (c) 1999 Grant Erickson -# Original code from 'mkevimg'. -# -# Module name: mkirimg -# -# Description: -# Reads an ELF file and assigns global variables 'imageSect_start', -# 'imageSect_size', 'initrdSect_start', and 'initrdSect_size' from -# the "image" and "initrd" section header information. It then -# rewrites the input ELF file with assigned globals to an output -# file. -# -# An input file, "irSectStart.txt" has the memory address of -# 'irSectStart'. The irSectStart memory address is used to find -# the global variables in the ".data" section of the ELF file. -# The 'irSectStart' and the above global variables are defined -# in "irSect.c". -# -# - -use File::Basename; -use Getopt::Std; - -# -# usage() -# -# Description: -# This routine prints out the proper command line usage for this program -# -# Input(s): -# status - Flag determining what usage information will be printed and what -# the exit status of the program will be after the information is -# printed. -# -# Output(s): -# N/A -# -# Returns: -# This subroutine does not return. -# - -sub usage { - my($status); - $status = $_[0]; - - printf("Usage: %s [-hvV] \n", - $program); - - if ($status != 0) { - printf("Try `%s -h' for more information.\n", $program); - } - - if ($status != 1) { - print(" -h Print out this message and exit.\n"); - print(" -v Verbose. Print out lots of ELF information.\n"); - print(" -V Print out version information and exit.\n"); - } - - exit($status); -} - -# -# version() -# -# Description: -# This routine prints out program version information -# -# Input(s): -# N/A -# -# Output(s): -# N/A -# -# Returns: -# This subroutine does not return. -# - -sub version { - print("mkirimg Version 1.1.0\n"); - print("Copyright (c) 1998-1999 TiVo, Inc.\n"); - print("Copyright (c) 1999 Grant Erickson \n"); - - exit (0); -} - -# -# file_check() -# -# Description: -# This routine checks an input file to ensure that it exists, is a -# regular file, and is readable. -# -# Input(s): -# file - Input file to be checked. -# -# Output(s): -# N/A -# -# Returns: -# 0 if the file exists, is a regular file, and is readable, otherwise -1. -# - -sub file_check { - my($file); - $file = $_[0]; - - if (!(-e $file)) { - printf("The file \"%s\" does not exist.\n", $file); - return (-1); - } elsif (!(-f $file)) { - printf("The file \"%s\" is not a regular file.\n", $file); - return (-1); - } elsif (!(-r $file)) { - printf("The file \"%s\" is not readable.\n", $file); - return (-1); - } - - return (0); -} - -# -# decode_options() -# -# Description: -# This routine steps through the command-line arguments, parsing out -# recognzied options. -# -# Input(s): -# N/A -# -# Output(s): -# N/A -# -# Returns: -# N/A -# - -sub decode_options { - - if (!getopts("hvV")) { - usage(1); - } - - if ($opt_h) { - usage(0); - } - - if ($opt_V) { - version(); - exit (0); - } - - if ($opt_v) { - $verbose = 1; - } - - if (!($ElfFile = shift(@ARGV))) { - usage(1); - } - - if (!($OutputFile = shift(@ARGV))) { - usage (1); - } - - if (!($IrFile = shift(@ARGV))) { - usage (1); - } - - if (file_check($ElfFile)) { - exit(1); - } - - if (file_check($IrFile)) { - exit(1); - } -} - -# -# ELF file and section header field numbers -# - -require '../utils/elf.pl'; - -# -# Main program body -# - -{ - $program = basename($0); - decode_options(); - - open(ELF, "<$ElfFile") || die "Cannot open input file"; - open(OUTPUT, ">$OutputFile") || die "Cannot open output file"; - open(IR, "$IrFile") || die "Cannot open input file"; - - $ElfFilesize = (-s $ElfFile); - - if (read(ELF, $ibuf, $ElfFilesize) != $ElfFilesize) { - print("Failed to read ELF input file!\n"); - exit(1); - } - - if (read(IR, $irbuf, 8) != 8) { - print("Failed to read Ir input file!\n"); - exit(1); - } - - # - # Parse ELF header - # - - @eh = unpack("a16n2N5n6", $ibuf); - - # - # Make sure this is actually a PowerPC ELF file. - # - - if (substr($eh[$e_ident], 0, 4) ne "\177ELF") { - printf("The file \"%s\" is not an ELF file.\n", $ElfFile); - exit (1); - } elsif ($eh[$e_machine] != 20) { - printf("The file \"%s\" is not a PowerPC ELF file.\n", $ElfFile); - exit (1); - } - - # - # Find the section header for the string table. - # - - $strtable_section_offset = $eh[$e_shoff] + - - $eh[$e_shstrndx] * $eh[$e_shentsize]; - - if ($verbose) { - printf("String table section header offset: 0x%x\n", - $strtable_section_offset); - } - - # - # Find the start of the string table. - # - - @strh = unpack("N10", substr($ibuf, $strtable_section_offset, - $eh[$e_shentsize])); - - if ($verbose) { - printf("Section name strings start at: 0x%x, %d bytes.\n", - $strh[$sh_offset], $strh[$sh_size]); - } - - $names = substr($ibuf, $strh[$sh_offset], $strh[$sh_size]); - - # Grab each section header and find '.data', 'image', and - # 'initrd' sections in particular. - - $off = $eh[$e_shoff]; - $imageFound = 0; - $initrdFound = 0; - - for($i = 0; $i < $eh[$e_shnum]; $i++, $off += $eh[$e_shentsize]) { - @sh = unpack("N10", substr($ibuf, $off, $eh[$e_shentsize])); - - # Take the first section name from the array returned by split. - - ($name) = split(/\000/, substr($names, $sh[$sh_name])); - - # Attempt to find the .data, image, and initrd sections - - if ($name =~ /^\image$/) { - ($image_addr, $image_offset, $image_size) = - ($sh[$sh_addr], $sh[$sh_offset], $sh[$sh_size]); - $imageFound = 1; - - } elsif ($name =~ /^\initrd$/) { - ($initrd_addr, $initrd_offset, $initrd_size) = - ($sh[$sh_addr], $sh[$sh_offset], $sh[$sh_size]); - $initrdFound = 1; - - } elsif ($name =~ /^\.data$/) { - ($data_addr, $data_offset, $data_size) = - ($sh[$sh_addr], $sh[$sh_offset], $sh[$sh_size]); - - } elsif ($name =~ /^\.bss$/) { - ($bss_addr, $bss_offset, $bss_size) = - ($sh[$sh_addr], $sh[$sh_offset], $sh[$sh_size]); - - } - } - - if ($verbose) { - printf("Data section - Address: 0x%08x, Size: 0x%08x, File Offset 0x%08x\n", - $data_addr, $data_size, $data_offset); - printf("Bss section - Address: 0x%08x, Size: 0x%08x, File Offset 0x%08x\n", - $bss_addr, $bss_size, $bss_offset); - } - - if ($verbose) { - if ($imageFound) { - printf("Image section - Address: 0x%08x, Size: 0x%08x\n", - $image_addr, $image_size); - } else { - printf("Image section not found in file: $ElfFile\n"); - } - - if ($initrdFound) { - printf("Initrd section - Address: 0x%08x, Size: 0x%08x\n", - $initrd_addr, $initrd_size); - } else { - printf("Initrd section not found in file: $ElfFile\n"); - } - } - - # get file offset of irSectStart - - $irSectStartoffset = hex ($irbuf); - - if ($verbose) { - printf("irSectStartOffset Address: 0x%08x\n", $irSectStartoffset); - } - - # get the offset of global variables - - $initialOffset = ($irSectStartoffset - $data_addr) + $data_offset + 4; - - # write modified values to OUTPUT file - - syswrite(OUTPUT, $ibuf, $initialOffset); - - if ($imageFound) { - $testN = pack ("N2", $bss_addr + $bss_size, $image_size); - syswrite(OUTPUT, $testN, length($testN)); - printf("Updated symbol \"imageSect_start\" to 0x%08x\n", - $bss_addr + $bss_size); - printf("Updated symbol \"imageSect_size\" to 0x%08x\n", $image_size); - } else { - syswrite(OUTPUT, $ibuf, 8, $initialOffset); - } - - if ($initrdFound) { - $testN = pack ("N2", $bss_addr + $bss_size + $image_size, $initrd_size); - syswrite(OUTPUT, $testN, length($testN)); - printf("Updated symbol \"initrdSect_start\" to 0x%08x\n", - $bss_addr + $bss_size + $image_size); - printf("Updated symbol \"initrdSect_size\" to 0x%08x\n", $initrd_size); - } else { - syswrite(OUTPUT, $ibuf,8, $initialOffset + 8); - } - - syswrite(OUTPUT, $ibuf, $ElfFilesize - ($initialOffset + 16), - $initialOffset + 16); - - # - # Clean-up and leave - # - - close (ELF); - close (OUTPUT); - close (IR); - - exit (0); -} - diff -Nru a/arch/ppc/boot/utils/mksimage.c b/arch/ppc/boot/utils/mksimage.c --- a/arch/ppc/boot/utils/mksimage.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,127 +0,0 @@ -/* - * BK Id: SCCS/s.mksimage.c 1.6 05/18/01 15:16:42 cort - */ -/* - * - * - * - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - - -#define SIZE 1024 -#define BLOCK_ALIGN(x) (((x)+SIZE-1)&(~(SIZE-1))) - -static void -die(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - fputc('\n', stderr); - exit(1); -} - -static void -usage(void) -{ - printf("Usage: mkbinimg -o \n"); - exit(1); -} - -static int -copy_blocks(int ifd, int ofd, unsigned long *offset, unsigned long *size) -{ - off_t cur; - int amt; - unsigned long len = 0; - char buffer[SIZE]; - - cur = lseek(ofd, 0, SEEK_CUR); - - if (cur % SIZE) { - cur = BLOCK_ALIGN(cur); - cur = lseek(ofd, cur, SEEK_SET); - } - - *offset = (unsigned long) cur; - while((amt = read(ifd, buffer, SIZE)) > 0) { - write(ofd, buffer, amt); - len += amt; - } - *size = len; - return 0; -} - - -int -main(int argc, char *argv[]) -{ - char *kernel, *loader, *rdimage = NULL; - unsigned long ld_off, kern_off, rd_off; - unsigned long ld_size, kern_size, rd_size; - int fd, ofd, len; - char buffer[500]; - - if (argc < 5 && !strcmp(argv[argc-2], "-o")) - usage(); - - if (argc > 5) - rdimage = argv[3]; - - kernel = argv[2]; - loader = argv[1]; - - ofd = open(argv[argc-1], (O_RDWR|O_CREAT), 0755); - if (ofd < 0) { - die("can't open %s: %s", argv[5], strerror(errno)); - } - - ld_off = kern_off = rd_off = 0; - ld_size = kern_size = rd_size = 0; - memset(buffer, 0, 500); - len = 0; - - fd = open(loader, O_RDONLY); - if (fd < 0) - die("can't open loader: %s", strerror(errno)); - - copy_blocks(fd, ofd, &ld_off, &ld_size); - len = sprintf(buffer, "bootloader: %x %x\n", ld_off, ld_size); - close(fd); - - fd = open(kernel, O_RDONLY); - if (fd < 0) - die("can't open kernel: %s", strerror(errno)); - - copy_blocks(fd, ofd, &kern_off, &kern_size); - len += sprintf(buffer+len, "zimage: %x %x\n", kern_off, kern_size); - close(fd); - - if (rdimage) { - fd = open(rdimage, O_RDONLY); - if (fd < 0) - die("can't get ramdisk: %s", strerror(errno)); - - copy_blocks(fd, ofd, &rd_off, &rd_size); - close(fd); - } - - len += sprintf(buffer+len, "initrd: %x %x", rd_off, rd_size); - - close(ofd); - - printf("%s\n", buffer); - - return 0; -} - diff -Nru a/arch/ppc/boot/utils/mktree.c b/arch/ppc/boot/utils/mktree.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/boot/utils/mktree.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,149 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + * + * Makes a tree bootable image for IBM Evaluation boards. + * Basically, just take a zImage, skip the ELF header, and stuff + * a 32 byte header on the front. + * + * We use htonl, which is a network macro, to make sure we're doing + * The Right Thing on an LE machine. It's non-obvious, but it should + * work on anything BSD'ish. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* This gets tacked on the front of the image. There are also a few + * bytes allocated after the _start label used by the boot rom (see + * head.S for details). + */ +typedef struct boot_block { + unsigned long bb_magic; /* 0x0052504F */ + unsigned long bb_dest; /* Target address of the image */ + unsigned long bb_num_512blocks; /* Size, rounded-up, in 512 byte blks */ + unsigned long bb_debug_flag; /* Run debugger or image after load */ + unsigned long bb_entry_point; /* The image address to start */ + unsigned long bb_checksum; /* 32 bit checksum including header */ + unsigned long reserved[2]; +} boot_block_t; + +#define IMGBLK 512 +char tmpbuf[IMGBLK]; + +int main(int argc, char *argv[]) +{ + int in_fd, out_fd; + int nblks, i; + uint cksum, *cp; + struct stat st; + boot_block_t bt; + + if (argc < 3) { + fprintf(stderr, "usage: %s [entry-point]\n",argv[0]); + exit(1); + } + + if (stat(argv[1], &st) < 0) { + perror("stat"); + exit(2); + } + + nblks = (st.st_size + IMGBLK) / IMGBLK; + + bt.bb_magic = htonl(0x0052504F); + + /* If we have the optional entry point parameter, use it */ + if (argc == 4) + bt.bb_dest = bt.bb_entry_point = htonl(strtoul(argv[3], NULL, 0)); + else + bt.bb_dest = bt.bb_entry_point = htonl(0x500000); + + /* We know these from the linker command. + * ...and then move it up into memory a little more so the + * relocation can happen. + */ + bt.bb_num_512blocks = htonl(nblks); + bt.bb_debug_flag = 0; + + bt.bb_checksum = 0; + + /* To be neat and tidy :-). + */ + bt.reserved[0] = 0; + bt.reserved[1] = 0; + + if ((in_fd = open(argv[1], O_RDONLY)) < 0) { + perror("zImage open"); + exit(3); + } + + if ((out_fd = open(argv[2], (O_RDWR | O_CREAT | O_TRUNC), 0666)) < 0) { + perror("bootfile open"); + exit(3); + } + + cksum = 0; + cp = (uint *)&bt; + for (i=0; i 0) { + if (read(in_fd, tmpbuf, IMGBLK) < 0) { + perror("zImage read"); + exit(5); + } + cp = (uint *)tmpbuf; + for (i=0; i -#include - -extern long ce_exec_config[]; - -int main(int argc, char *argv[]) -{ - int i, cnt, pos, len; - unsigned int cksum, val; - unsigned char *lp; - unsigned char buf[8192]; - if (argc != 2) - { - fprintf(stderr, "usage: %s name out-file\n", - argv[0]); - exit(1); - } - fprintf(stdout, "#\n"); - fprintf(stdout, "# Miscellaneous data structures:\n"); - fprintf(stdout, "# WARNING - this file is automatically generated!\n"); - fprintf(stdout, "#\n"); - fprintf(stdout, "\n"); - fprintf(stdout, "\t.data\n"); - fprintf(stdout, "\t.globl %s_data\n", argv[1]); - fprintf(stdout, "%s_data:\n", argv[1]); - pos = 0; - cksum = 0; - while ((len = read(0, buf, sizeof(buf))) > 0) - { - cnt = 0; - lp = (unsigned char *)buf; - len = (len + 3) & ~3; /* Round up to longwords */ - for (i = 0; i < len; i += 4) - { - if (cnt == 0) - { - fprintf(stdout, "\t.long\t"); - } - fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); - val = *(unsigned long *)lp; - cksum ^= val; - lp += 4; - if (++cnt == 4) - { - cnt = 0; - fprintf(stdout, " # %x \n", pos+i-12); - fflush(stdout); - } else - { - fprintf(stdout, ","); - } - } - if (cnt) - { - fprintf(stdout, "0\n"); - } - pos += len; - } - fprintf(stdout, "\t.globl %s_len\n", argv[1]); - fprintf(stdout, "%s_len:\t.long\t0x%x\n", argv[1], pos); - fflush(stdout); - fclose(stdout); - fprintf(stderr, "cksum = %x\n", cksum); - exit(0); -} - diff -Nru a/arch/ppc/boot/utils/sioffset b/arch/ppc/boot/utils/sioffset --- a/arch/ppc/boot/utils/sioffset Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,4 +0,0 @@ -#!/bin/bash - -OFFSET=`grep $1 sImage.map | awk '{print $2}'` -echo "0x"$OFFSET diff -Nru a/arch/ppc/boot/utils/sisize b/arch/ppc/boot/utils/sisize --- a/arch/ppc/boot/utils/sisize Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,4 +0,0 @@ -#!/bin/bash - -OFFSET=`grep $1 sImage.map | awk '{print $3}'` -echo "0x"$OFFSET diff -Nru a/arch/ppc/boot/utils/size b/arch/ppc/boot/utils/size --- a/arch/ppc/boot/utils/size Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,4 +0,0 @@ -#!/bin/bash - -OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux | awk '{print $3}'` -echo "0x"$OFFSET diff -Nru a/arch/ppc/config.in b/arch/ppc/config.in --- a/arch/ppc/config.in Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/config.in Tue Feb 19 18:08:57 2002 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.config.in 1.45 11/08/01 07:57:40 paulus +# BK Id: %F% %I% %G% %U% %#% # # For a description of the syntax of this configuration file, # see Documentation/kbuild/config-language.txt. @@ -20,20 +20,23 @@ "6xx/7xx/74xx/8260 CONFIG_6xx \ 4xx CONFIG_4xx \ POWER3 CONFIG_POWER3 \ - POWER4 CONFIG_POWER4 \ - 8xx CONFIG_8xx" 6xx + 8xx CONFIG_8xx \ + iSeries CONFIG_PPC_ISERIES" 6xx if [ "$CONFIG_6xx" = "y" ]; then bool 'MPC8260 CPM Support' CONFIG_8260 fi -if [ "$CONFIG_POWER3" = "y" -o "$CONFIG_POWER4" = "y" ]; then +if [ "$CONFIG_POWER3" = "y" ]; then define_bool CONFIG_PPC64BRIDGE y define_bool CONFIG_ALL_PPC y fi -if [ "$CONFIG_6xx" = "y" -o "$CONFIG_POWER3" = "y" -o \ - "$CONFIG_POWER4" = "y" ]; then +if [ "$CONFIG_PPC_ISERIES" = "y" ]; then + define_bool CONFIG_PPC64BRIDGE y +fi + +if [ "$CONFIG_6xx" = "y" -o "$CONFIG_POWER3" = "y" ]; then define_bool CONFIG_PPC_STD_MMU y else define_bool CONFIG_PPC_STD_MMU n @@ -41,17 +44,30 @@ if [ "$CONFIG_8260" = "y" ]; then define_bool CONFIG_SERIAL_CONSOLE y - bool 'Support for EST8260' CONFIG_EST8260 + choice 'Machine Type' \ + "EST8260 CONFIG_EST8260 \ + SBS8260 CONFIG_SBS8260 \ + RPXSUPER CONFIG_RPX6 \ + TQM8260 CONFIG_TQM8260 \ + Willow CONFIG_WILLOW" Willow fi if [ "$CONFIG_4xx" = "y" ]; then choice 'Machine Type' \ - "Oak CONFIG_OAK \ - Walnut CONFIG_WALNUT" Oak + "Ash CONFIG_ASH \ + Ceder CONFIG_CEDER \ + CPCI405 CONFIG_CPCI405 \ + EP405 CONFIG_EP405 \ + Oak CONFIG_OAK \ + Redwood-4 CONFIG_REDWOOD_4 \ + Redwood-5 CONFIG_REDWOOD_5 \ + Tivo CONFIG_TIVO \ + Walnut CONFIG_WALNUT" Walnut fi if [ "$CONFIG_8xx" = "y" ]; then define_bool CONFIG_SERIAL_CONSOLE y + define_bool CONFIG_NOT_COHERENT_CACHE y choice 'Machine Type' \ "RPX-Lite CONFIG_RPXLITE \ @@ -63,11 +79,16 @@ TQM855L CONFIG_TQM855L \ TQM860L CONFIG_TQM860L \ FPS850L CONFIG_FPS850L \ - TQM860 CONFIG_TQM860 \ SPD823TS CONFIG_SPD823TS \ IVMS8 CONFIG_IVMS8 \ IVML24 CONFIG_IVML24 \ SM850 CONFIG_SM850 \ + HERMES CONFIG_HERMES_PRO \ + IP860 CONFIG_IP860 \ + LWMON CONFIG_LWMON \ + PCU_E CONFIG_PCU_E \ + CCM CONFIG_CCM \ + LANTEC CONFIG_LANTEC \ MBX CONFIG_MBX \ WinCept CONFIG_WINCEPT" RPX-Lite @@ -84,8 +105,60 @@ if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "n" ]; then choice 'Machine Type' \ "CHRP/PowerMac/PReP CONFIG_ALL_PPC \ - Amiga-APUS CONFIG_APUS \ - Synergy-Gemini CONFIG_GEMINI" CHRP/PowerMac/PReP + Amiga-APUS CONFIG_APUS \ + Cogent-Willow CONFIG_WILLOW \ + Force-PowerCore CONFIG_PCORE \ + Force-PowerPMC250 CONFIG_POWERPMC250 \ + Galileo-EV-64260-BP CONFIG_EV64260 \ + IBM-Spruce CONFIG_SPRUCE \ + MEN-F1 CONFIG_MENF1 \ + Motorola-LoPEC CONFIG_LOPEC \ + Motorola-MCPN765 CONFIG_MCPN765 \ + Motorola-MVME5100 CONFIG_MVME5100 \ + Motorola-PowerPlus CONFIG_PPLUS \ + Motorola-PrPMC750 CONFIG_PRPMC750 \ + Motorola-PrPMC800 CONFIG_PRPMC800 \ + Motorola-Sandpoint CONFIG_SANDPOINT \ + SBS-Adirondack CONFIG_ADIR \ + SBS-K2 CONFIG_K2 \ + Synergy-Gemini CONFIG_GEMINI \ + Zynx-ZX4500 CONFIG_ZX4500" CHRP/PowerMac/PReP +fi + +if [ "$CONFIG_PCORE" = "y" \ + -o "$CONFIG_POWERPMC250" = "y" ]; then + define_bool CONFIG_FORCE y +fi + +if [ "$CONFIG_FORCE" = "y" \ + -o "$CONFIG_MENF1" = "y" \ + -o "$CONFIG_SANDPOINT" = "y" \ + -o "$CONFIG_ZX4500" = "y" ]; then + bool 'Enable MPC10x store gathering' CONFIG_MPC10X_STORE_GATHERING +fi + +if [ "$CONFIG_EV64260" = "y" ]; then + define_bool CONFIG_GT64260 y + define_int CONFIG_SERIAL_CONSOLE_BAUD 115200 +fi + +if [ "$CONFIG_K2" = "y" ]; then + bool 'Enable CPC710 data gathering' CONFIG_CPC710_DATA_GATHERING +fi + +if [ "$CONFIG_MVME5100" = "y" ]; then + bool 'MVME5100 configured with an IPMC761' CONFIG_MVME5100_IPMC761_PRESENT +fi + +if [ "$CONFIG_SANDPOINT" = "y" ]; then + bool 'Sandpoint X3' CONFIG_SANDPOINT_X3 + if [ "$CONFIG_SANDPOINT_X3" = "y" ]; then + define_bool CONFIG_EPIC_SERIAL_MODE y + fi +fi + +if [ "$CONFIG_SPRUCE" = "y" ]; then + bool 'Spruce baud clock support' CONFIG_SPRUCE_BAUD_33M fi if [ "$CONFIG_PPC_STD_MMU" != "y" ]; then @@ -96,6 +169,7 @@ if [ "$CONFIG_SMP" = "y" ]; then bool ' Distribute interrupts on all CPUs by default' CONFIG_IRQ_ALL_CPUS fi +define_bool CONFIG_PREEMPT n if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "n" ];then bool 'AltiVec Support' CONFIG_ALTIVEC @@ -110,14 +184,121 @@ bool 'Math emulation' CONFIG_MATH_EMULATION fi +if [ "$CONFIG_4xx" = "y" ]; then +# It's often necessary to know the specific 4xx processor type. +# Fortunately, it is impled (so far) from the board type, so we +# don't need to ask more redundant questions. + if [ "$CONFIG_ASH" = "y" ]; then + define_bool CONFIG_NP405H y + define_bool CONFIG_TREEBOOT y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_CEDER" = "y" ]; then + define_bool CONFIG_NP405L y + define_bool CONFIG_BIOS_FIXUP y + define_bool CONFIG_TREEBOOT y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_CPCI405" = "y" ]; then + define_bool CONFIG_405GP y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_EP405" = "y" ]; then + define_bool CONFIG_405GP y + define_bool CONFIG_BIOS_FIXUP y + define_bool CONFIG_EMBEDDEDBOOT y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_OAK" = "y" -o "$CONFIG_TIVO" = "y" ]; then + define_bool CONFIG_403GCX y + define_bool CONFIG_TREEBOOT y + fi + if [ "$CONFIG_REDWOOD_4" = "y" ]; then + define_bool CONFIG_STB03xxx y + define_bool CONFIG_TREEBOOT y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_REDWOOD_5" = "y" ]; then + define_bool CONFIG_STB03xxx y + define_bool CONFIG_TREEBOOT y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_WALNUT" = "y" ]; then + define_bool CONFIG_405GP y + define_bool CONFIG_BIOS_FIXUP y + define_bool CONFIG_TREEBOOT y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + + bool 'Blue Logic DMA' CONFIG_405_DMA + dep_bool 'Power Management support (experimental)' CONFIG_PM $CONFIG_EXPERIMENTAL + + if [ "$CONFIG_4xx" = "y" ]; then + choice 'TTYS0 device and default console' \ + "UART0 CONFIG_UART0_TTYS0 \ + UART1 CONFIG_UART0_TTYS1" UART0 + fi + + define_bool CONFIG_IBM405_ERR51 y + define_bool CONFIG_NOT_COHERENT_CACHE y +fi + +if [ "$CONFIG_8xx" = "y" -o "$CONFIG_8260" = "y" ]; then + define_bool CONFIG_EMBEDDEDBOOT y +fi endmenu mainmenu_option next_comment comment 'General setup' -bool 'High memory support (experimental)' CONFIG_HIGHMEM +bool 'Prompt for advanced kernel configuration options' CONFIG_ADVANCED_OPTIONS +bool 'High memory support' CONFIG_HIGHMEM +if [ "$CONFIG_ADVANCED_OPTIONS" = "y" ]; then + if [ "$CONFIG_HIGHMEM" = "y" ]; then + bool " Set high memory pool address" CONFIG_HIGHMEM_START_BOOL + if [ "$CONFIG_HIGHMEM_START_BOOL" = "y" ]; then + hex " Virtual start address of high memory pool" CONFIG_HIGHMEM_START 0xfe000000 + fi + bool " Set maximum low memory" CONFIG_LOWMEM_SIZE_BOOL + if [ "$CONFIG_LOWMEM_SIZE_BOOL" = "y" ]; then + hex " Maximum low memory size (in bytes)" CONFIG_LOWMEM_SIZE 0x20000000 + fi + fi -define_bool CONFIG_ISA n + bool "Set custom kernel base address" CONFIG_KERNEL_START_BOOL + if [ "$CONFIG_KERNEL_START_BOOL" = "y" ]; then + hex " Virtual address of kernel base" CONFIG_KERNEL_START 0xc0000000 + fi + bool "Set custom user task size" CONFIG_TASK_SIZE_BOOL + if [ "$CONFIG_TASK_SIZE_BOOL" = "y" ]; then + hex " Size of user task space" CONFIG_TASK_SIZE 0x80000000 + fi + if [ "$CONFIG_8xx" = "y" ]; then + bool "Pinned Kernel TLBs (860 ONLY)" CONFIG_PIN_TLB + fi + if [ "$CONFIG_4xx" = "y" ]; then + bool "Pinned Kernel TLBs" CONFIG_PIN_TLB + fi + if [ "$CONFIG_ALL_PPC" = "n" ]; then + bool "Set the boot link/load address" CONFIG_BOOT_LOAD_BOOL + if [ "$CONFIG_BOOT_LOAD_BOOL" = "y" ]; then + hex " Link/load address for booting" CONFIG_BOOT_LOAD 0x00400000 + fi + fi +fi + +if [ "$CONFIG_ALL_PPC" = "y" ]; then + bool 'Support for ISA-bus hardware' CONFIG_ISA +else + define_bool CONFIG_ISA n +fi define_bool CONFIG_EISA n define_bool CONFIG_SBUS n @@ -125,7 +306,8 @@ define_bool CONFIG_MCA n if [ "$CONFIG_4xx" = "y" -o "$CONFIG_8260" = "y" ]; then - define_bool CONFIG_PCI n + bool "Enable PCI" CONFIG_PCI + bool 'PC PS/2 style Keyboard' CONFIG_PC_KEYBOARD else if [ "$CONFIG_8xx" = "y" ]; then bool 'QSpan PCI' CONFIG_PCI_QSPAN @@ -135,7 +317,12 @@ bool 'PCI for Permedia2' CONFIG_PCI_PERMEDIA define_bool CONFIG_PCI $CONFIG_PCI_PERMEDIA else - define_bool CONFIG_PCI y + if [ "$CONFIG_PPC_ISERIES" = "y" ]; then + bool "IBM iSeries Native I/O Support" CONFIG_PCI_ISERIES + define_bool CONFIG_PCI $CONFIG_PCI_ISERIES + else + define_bool CONFIG_PCI y + fi fi fi fi @@ -160,7 +347,7 @@ source drivers/parport/Config.in -if [ "$CONFIG_4xx" != "y" ]; then +if [ "$CONFIG_PPC_ISERIES" != "y" ]; then if [ "$CONFIG_APUS" != "y" ]; then tristate 'Support for /dev/rtc' CONFIG_PPC_RTC else @@ -175,7 +362,6 @@ if [ "$CONFIG_ALL_PPC" = "y" ]; then bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE bool 'Support for RTAS (RunTime Abstraction Services) in /proc' CONFIG_PPC_RTAS - bool 'Support for early boot text console (BootX or OpenFirmware only)' CONFIG_BOOTX_TEXT bool 'Support for PReP Residual Data' CONFIG_PREP_RESIDUAL fi @@ -251,8 +437,12 @@ fi endmenu +source drivers/message/fusion/Config.in + source drivers/ieee1394/Config.in +source drivers/message/i2o/Config.in + if [ "$CONFIG_NET" = "y" ]; then mainmenu_option next_comment comment 'Network device support' @@ -300,6 +490,33 @@ fi endmenu + +if [ "$CONFIG_PPC_ISERIES" = "y" ]; then + mainmenu_option next_comment + comment 'iSeries device drivers' + tristate 'iSeries Virtual Console Support' CONFIG_VIOCONS + tristate 'iSeries Virtual I/O disk support' CONFIG_VIODASD + if [ "$CONFIG_VIODASD" = "y" -o "$CONFIG_VIODASD" = "m" ]; then + bool 'iSeries Virtual disk IDE emulation' CONFIG_VIODASD_IDE + fi + tristate 'iSeries Virtual I/O CD support' CONFIG_VIOCD + if [ "$CONFIG_VIOCD" = "y" -o "$CONFIG_VIOCD" = "m" ]; then + bool 'iSeries Virtual CD Aztech emulation' CONFIG_VIOCD_AZTECH + fi + tristate 'iSeries Virtual Tape Support' CONFIG_VIOTAPE + tristate 'iSeries Virtual Ethernet driver support' CONFIG_VETH + if [ "$CONFIG_VIOCONS" != "n" -o "$CONFIG_VIODASD" != "n" \ + -o "$CONFIG_VIOTAPE" != "n" -o "$CONFIG_VIOCD" != "n" ]; then + define_bool CONFIG_VIOPATH y + fi + endmenu +fi + +if [ "$CONFIG_VIOCD" = "y" ]; then + define_bool CONFIG_CD_NO_IDESCSI y + define_bool CONFIG_BLK_DEV_IDECD y +fi + source drivers/input/Config.in mainmenu_option next_comment @@ -311,6 +528,10 @@ bool 'Support for PMU based PowerMacs' CONFIG_ADB_PMU if [ "$CONFIG_ADB_PMU" = "y" ]; then bool ' Power management support for PowerBooks' CONFIG_PMAC_PBOOK + if [ "$CONFIG_PMAC_PBOOK" = "y" ]; then + define_bool CONFIG_PM y + tristate ' APM emulation' CONFIG_PMAC_APM_EMU + fi # made a separate option since backlight may end up beeing used # on non-powerbook machines (but only on PMU based ones AFAIK) bool ' Backlight control for LCD screens' CONFIG_PMAC_BACKLIGHT @@ -336,6 +557,9 @@ if [ "$CONFIG_INPUT" != "n" ]; then define_bool CONFIG_MAC_HID y fi + if [ "$CONFIG_ADB_CUDA" != "n" ]; then + bool 'Support for ANS LCD display' CONFIG_ANSLCD + fi fi endmenu @@ -349,8 +573,8 @@ comment 'Sound' tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/dmasound/Config.in - source drivers/sound/Config.in + source sound/oss/dmasound/Config.in + source sound/Config.in fi endmenu @@ -363,18 +587,56 @@ source arch/ppc/8260_io/Config.in fi +if [ "$CONFIG_4xx" = "y"]; then + mainmenu_option next_comment + comment 'IBM 4xx options' + if [ "$CONFIG_STB03xxx" = "y" ]; then + bool 'STB IR Keyboard' CONFIG_STB_KB + bool 'SICC Serial port' CONFIG_SERIAL_SICC + if [ "$CONFIG_SERIAL_SICC" = "y" -a "$CONFIG_UART0_TTYS1" = "y" ]; then + define_bool CONFIG_UART1_DFLT_CONSOLE y + define_bool CONFIG_SERIAL_SICC_CONSOLE y + fi + fi + endmenu +fi + source drivers/usb/Config.in if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then source net/bluetooth/Config.in fi +source lib/Config.in + mainmenu_option next_comment comment 'Kernel hacking' bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ bool 'Include kgdb kernel debugger' CONFIG_KGDB +if [ "$CONFIG_KGDB" = "y" ]; then + choice 'Serial Port' \ + "ttyS0 CONFIG_KGDB_TTYS0 \ + ttyS1 CONFIG_KGDB_TTYS1 \ + ttyS2 CONFIG_KGDB_TTYS2 \ + ttyS3 CONFIG_KGDB_TTYS3" ttyS1 +fi bool 'Include xmon kernel debugger' CONFIG_XMON -endmenu +bool 'Include BDI-2000 user context switcher' CONFIG_BDI_SWITCH +if [ "$CONFIG_KGDB" = "y" -o "$CONFIG_XMON" = "y" \ + -o "$CONFIG_BDI_SWITCH" = "y" ]; then + bool 'Add any additional compile options' CONFIG_MORE_COMPILE_OPTIONS + if [ "$CONFIG_MORE_COMPILE_OPTIONS" = "y" ]; then + string 'Additional compile arguments' CONFIG_COMPILE_OPTIONS "-g -ggdb" + fi +fi -source lib/Config.in +if [ "$CONFIG_ALL_PPC" = "y" ]; then + bool 'Support for early boot text console (BootX or OpenFirmware only)' CONFIG_BOOTX_TEXT +fi +if [ "$CONFIG_MCPN765" = "y" -o "$CONFIG_SANDPOINT" = "y" \ + -o "$CONFIG_ZX4500" = "y" -o "$CONFIG_PRPMC800" = "y" \ + -o "$CONFIG_4xx" = "y" -o "$CONFIG_GT64260" = "y" ]; then + bool 'Support for early boot texts over serial port' CONFIG_SERIAL_TEXT_DEBUG +fi +endmenu diff -Nru a/arch/ppc/configs/FADS_defconfig b/arch/ppc/configs/FADS_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/FADS_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,476 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +# CONFIG_6xx is not set +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +CONFIG_8xx=y +# CONFIG_PPC_ISERIES is not set +CONFIG_SERIAL_CONSOLE=y +CONFIG_NOT_COHERENT_CACHE=y +# CONFIG_RPXLITE is not set +# CONFIG_RPXCLASSIC is not set +# CONFIG_BSEIP is not set +CONFIG_FADS=y +# CONFIG_TQM823L is not set +# CONFIG_TQM850L is not set +# CONFIG_TQM855L is not set +# CONFIG_TQM860L is not set +# CONFIG_FPS850L is not set +# CONFIG_SPD823TS is not set +# CONFIG_IVMS8 is not set +# CONFIG_IVML24 is not set +# CONFIG_SM850 is not set +# CONFIG_HERMES_PRO is not set +# CONFIG_IP860 is not set +# CONFIG_LWMON is not set +# CONFIG_PCU_E is not set +# CONFIG_CCM is not set +# CONFIG_LANTEC is not set +# CONFIG_MBX is not set +# CONFIG_WINCEPT is not set +# CONFIG_PPC601_SYNC_FIX is not set +# CONFIG_ALL_PPC is not set +# CONFIG_SMP is not set +CONFIG_MATH_EMULATION=y + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +# CONFIG_PCI_QSPAN is not set +# CONFIG_PCI is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_PPC_RTC is not set +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_VIODASD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_ARM_AM79C961A is not set +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_NCR885E is not set +# CONFIG_OAKNET is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_ACENIC_OMIT_TIGON_I is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_VETH is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=32 +# CONFIG_VIOCONS is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set +# CONFIG_VIOTAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_EXT2_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_MSDOS_PARTITION is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# MPC8xx CPM Options +# +CONFIG_SCC_ENET=y +CONFIG_SCC1_ENET=y +# CONFIG_SCC2_ENET is not set +# CONFIG_SCC3_ENET is not set +# CONFIG_FEC_ENET is not set +CONFIG_ENET_BIG_BUFFERS=y +CONFIG_SMC2_UART=y +# CONFIG_ALTSMC2 is not set +# CONFIG_CONS_SMC2 is not set +# CONFIG_USE_SCC_IO is not set + +# +# Generic MPC8xx Options +# +CONFIG_8xx_COPYBACK=y +# CONFIG_8xx_CPU6 is not set +# CONFIG_UCODE_PATCH is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set diff -Nru a/arch/ppc/configs/IVMS8_defconfig b/arch/ppc/configs/IVMS8_defconfig --- a/arch/ppc/configs/IVMS8_defconfig Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/configs/IVMS8_defconfig Tue Feb 19 18:08:57 2002 @@ -25,7 +25,6 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set CONFIG_8xx=y # CONFIG_PPC_STD_MMU is not set CONFIG_SERIAL_CONSOLE=y diff -Nru a/arch/ppc/configs/SM850_defconfig b/arch/ppc/configs/SM850_defconfig --- a/arch/ppc/configs/SM850_defconfig Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/configs/SM850_defconfig Tue Feb 19 18:08:59 2002 @@ -25,7 +25,6 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set CONFIG_8xx=y # CONFIG_PPC_STD_MMU is not set CONFIG_SERIAL_CONSOLE=y diff -Nru a/arch/ppc/configs/SPD823TS_defconfig b/arch/ppc/configs/SPD823TS_defconfig --- a/arch/ppc/configs/SPD823TS_defconfig Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/configs/SPD823TS_defconfig Tue Feb 19 18:08:59 2002 @@ -25,7 +25,6 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set CONFIG_8xx=y # CONFIG_PPC_STD_MMU is not set CONFIG_SERIAL_CONSOLE=y diff -Nru a/arch/ppc/configs/TQM823L_defconfig b/arch/ppc/configs/TQM823L_defconfig --- a/arch/ppc/configs/TQM823L_defconfig Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/configs/TQM823L_defconfig Tue Feb 19 18:08:58 2002 @@ -25,7 +25,6 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set CONFIG_8xx=y # CONFIG_PPC_STD_MMU is not set CONFIG_SERIAL_CONSOLE=y diff -Nru a/arch/ppc/configs/TQM8260_defconfig b/arch/ppc/configs/TQM8260_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/TQM8260_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,433 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +CONFIG_8260=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_EST8260 is not set +CONFIG_TQM8260=y +# CONFIG_PPC601_SYNC_FIX is not set +# CONFIG_ALL_PPC is not set +# CONFIG_SMP is not set +CONFIG_MACH_SPECIFIC=y +CONFIG_SASH=y +CONFIG_SASH_PATH="/bin/sash" + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +# CONFIG_PCI is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_PPC_RTC is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_NCR885E is not set +# CONFIG_OAKNET is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=32 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +CONFIG_FLASH=y +CONFIG_AMD_FLASH=y +# CONFIG_INTEL_FLASH is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_MSDOS_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# MPC8260 Communication Options +# +# CONFIG_SCC_ENET is not set +CONFIG_FEC_ENET=y +# CONFIG_FCC1_ENET is not set +CONFIG_FCC2_ENET=y +# CONFIG_FCC3_ENET is not set +# CONFIG_USE_MDIO is not set + +# +# Generic MPC82xx Options +# +CONFIG_DCACHE_DISABLE=y + +# +# USB support +# +# CONFIG_USB is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set diff -Nru a/arch/ppc/configs/TQM850L_defconfig b/arch/ppc/configs/TQM850L_defconfig --- a/arch/ppc/configs/TQM850L_defconfig Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/configs/TQM850L_defconfig Tue Feb 19 18:08:57 2002 @@ -25,7 +25,6 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set CONFIG_8xx=y # CONFIG_PPC_STD_MMU is not set CONFIG_SERIAL_CONSOLE=y diff -Nru a/arch/ppc/configs/TQM860L_defconfig b/arch/ppc/configs/TQM860L_defconfig --- a/arch/ppc/configs/TQM860L_defconfig Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/configs/TQM860L_defconfig Tue Feb 19 18:08:59 2002 @@ -25,10 +25,11 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set CONFIG_8xx=y +# CONFIG_PPC_ISERIES is not set # CONFIG_PPC_STD_MMU is not set CONFIG_SERIAL_CONSOLE=y +CONFIG_NOT_COHERENT_CACHE=y # CONFIG_RPXLITE is not set # CONFIG_RPXCLASSIC is not set # CONFIG_BSEIP is not set @@ -38,12 +39,18 @@ # CONFIG_TQM855L is not set CONFIG_TQM860L=y # CONFIG_FPS850L is not set -# CONFIG_TQM860 is not set # CONFIG_SPD823TS is not set # CONFIG_IVMS8 is not set # CONFIG_IVML24 is not set # CONFIG_SM850 is not set +# CONFIG_HERMES_PRO is not set +# CONFIG_IP860 is not set +# CONFIG_LWMON is not set +# CONFIG_PCU_E is not set +# CONFIG_CCM is not set +# CONFIG_LANTEC is not set # CONFIG_MBX is not set +# CONFIG_IDIF860 is not set # CONFIG_WINCEPT is not set CONFIG_TQM8xxL=y # CONFIG_ALL_PPC is not set @@ -93,6 +100,7 @@ # Block devices # # CONFIG_BLK_DEV_FD is not set +# CONFIG_VIODASD is not set # CONFIG_BLK_DEV_XD is not set # CONFIG_PARIDE is not set # CONFIG_BLK_CPQ_DA is not set @@ -263,6 +271,7 @@ # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_SK98LIN is not set +# CONFIG_VETH is not set # CONFIG_FDDI is not set # CONFIG_HIPPI is not set # CONFIG_PLIP is not set @@ -338,6 +347,7 @@ # CONFIG_SERIAL_NONSTANDARD is not set CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=32 +# CONFIG_VIOCONS is not set # # I2C support @@ -363,6 +373,7 @@ # Input core support is needed for joysticks # # CONFIG_QIC02_TAPE is not set +# CONFIG_VIOTAPE is not set # # Watchdog Cards @@ -489,10 +500,20 @@ # CONFIG_SCC3_ENET is not set # CONFIG_FEC_ENET is not set CONFIG_ENET_BIG_BUFFERS=y +CONFIG_SMC1_UART_RX_BDNUM=4 +CONFIG_SMC1_UART_RX_BDSIZE=32 +CONFIG_SMC1_UART_TX_BDNUM=4 +CONFIG_SMC1_UART_TX_BDSIZE=32 CONFIG_SMC2_UART=y # CONFIG_ALTSMC2 is not set # CONFIG_CONS_SMC2 is not set +CONFIG_UART_MAXIDL_SMC2=1 +CONFIG_SMC2_UART_RX_BDNUM=4 +CONFIG_SMC2_UART_RX_BDSIZE=32 +CONFIG_SMC2_UART_TX_BDNUM=4 +CONFIG_SMC2_UART_TX_BDSIZE=32 # CONFIG_USE_SCC_IO is not set +CONFIG_STATUS_LED=y # # Generic MPC8xx Options @@ -614,3 +635,4 @@ # CONFIG_MAGIC_SYSRQ is not set # CONFIG_KGDB is not set # CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set diff -Nru a/arch/ppc/configs/adir_defconfig b/arch/ppc/configs/adir_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/adir_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,647 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +# CONFIG_PRPMC800 is not set +# CONFIG_SANDPOINT is not set +CONFIG_ADIR=y +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_WILLOW is not set +# CONFIG_ZX4500 is not set +# CONFIG_SMP is not set +# CONFIG_ALTIVEC is not set +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +CONFIG_PARPORT=y +CONFIG_PARPORT_PC=y +CONFIG_PARPORT_PC_CML1=y +# CONFIG_PARPORT_SERIAL is not set +CONFIG_PARPORT_PC_FIFO=y +CONFIG_PARPORT_PC_SUPERIO=y +# CONFIG_PARPORT_AMIGA is not set +# CONFIG_PARPORT_MFC3 is not set +# CONFIG_PARPORT_ATARI is not set +# CONFIG_PARPORT_SUNBPP is not set +# CONFIG_PARPORT_OTHER is not set +CONFIG_PARPORT_1284=y +CONFIG_PPC_RTC=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +CONFIG_BLK_DEV_FD=y +# CONFIG_VIODASD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_FTP=m +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_UNCLEAN=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_MIRROR=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_NAT_FTP=m +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_TARGET_LOG is not set +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_COMPAT_IPCHAINS=m +CONFIG_IP_NF_NAT_NEEDED=y +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 +CONFIG_CHR_DEV_ST=y +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_SR_EXTRA_DEVS=2 +CONFIG_CHR_DEV_SG=y +# CONFIG_SCSI_DEBUG_QUEUES is not set +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CPQFCTS is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_IMM is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +CONFIG_SCSI_NCR53C8XX=y +CONFIG_SCSI_SYM53C8XX=y +CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 +CONFIG_SCSI_NCR53C8XX_SYNC=20 +# CONFIG_SCSI_NCR53C8XX_PROFILE is not set +# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set +# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_DEBUG is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +# CONFIG_TULIP is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_LAN_SAA9730 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +# CONFIG_PRINTER is not set +# CONFIG_PPDEV is not set +# CONFIG_VIOCONS is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_VIOTAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +CONFIG_USB_UHCI=y +CONFIG_USB_OHCI=y +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_FREECOM=y +# CONFIG_USB_STORAGE_ISD200 is not set +CONFIG_USB_STORAGE_DPCM=y +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +CONFIG_USB_ACM=m +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_OV511 is not set +# CONFIG_USB_PWC is not set +# CONFIG_USB_SE401 is not set +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +CONFIG_USB_SERIAL_VISOR=m +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +CONFIG_XMON=y +# CONFIG_BDI_SWITCH is not set diff -Nru a/arch/ppc/configs/apus_defconfig b/arch/ppc/configs/apus_defconfig --- a/arch/ppc/configs/apus_defconfig Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/configs/apus_defconfig Tue Feb 19 18:08:58 2002 @@ -25,7 +25,6 @@ CONFIG_6xx=y # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set # CONFIG_8xx is not set # CONFIG_8260 is not set CONFIG_PPC_STD_MMU=y diff -Nru a/arch/ppc/configs/ash_defconfig b/arch/ppc/configs/ash_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/ash_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,569 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +# CONFIG_6xx is not set +CONFIG_4xx=y +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_PPC_STD_MMU is not set +CONFIG_ASH=y +# CONFIG_CEDER is not set +# CONFIG_CPCI405 is not set +# CONFIG_EP405 is not set +# CONFIG_OAK is not set +# CONFIG_REDWOOD_4 is not set +# CONFIG_REDWOOD_5 is not set +# CONFIG_TIVO is not set +# CONFIG_WALNUT is not set +# CONFIG_ALL_PPC is not set +# CONFIG_SMP is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_NP405H=y +CONFIG_BIOS_FIXUP=y +CONFIG_TREEBOOT=y +CONFIG_IBM405_ERR77=y +CONFIG_IBM_OCP=y +# CONFIG_405_DMA is not set +CONFIG_UART0_TTYS0=y +# CONFIG_UART0_TTYS1 is not set +CONFIG_IBM405_ERR51=y +CONFIG_NOT_COHERENT_CACHE=y + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +# CONFIG_PC_KEYBOARD is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_COPS_DAYNA is not set +# CONFIG_COPS_TANGENT is not set +# CONFIG_IPDDP is not set +# CONFIG_IPDDP_ENCAP is not set +# CONFIG_IPDDP_DECAP is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_PCI is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +CONFIG_IBM_OCP_ENET=y +# CONFIG_IBM_OCP_ENET_ERROR_MSG is not set +CONFIG_IBM_OCP_ENET_RX_BUFF=64 +CONFIG_IBM_OCP_ENET_TX_BUFF=8 +CONFIG_IBM_OCP_ENET_GAP=8 +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_PROC is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_WDT is not set +# CONFIG_WDTPCI is not set +# CONFIG_PCWATCHDOG is not set +# CONFIG_ACQUIRE_WDT is not set +# CONFIG_ADVANTECH_WDT is not set +# CONFIG_EUROTECH_WDT is not set +# CONFIG_IB700_WDT is not set +# CONFIG_I810_TCO is not set +# CONFIG_MIXCOMWD is not set +# CONFIG_60XX_WDT is not set +# CONFIG_W83877F_WDT is not set +# CONFIG_MACHZ_WDT is not set +CONFIG_PPC405_WDT=y +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# MPC4xx Driver Options +# + +# +# USB support +# +# CONFIG_USB is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_SERIAL_TEXT_DEBUG is not set diff -Nru a/arch/ppc/configs/bseip_defconfig b/arch/ppc/configs/bseip_defconfig --- a/arch/ppc/configs/bseip_defconfig Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/configs/bseip_defconfig Tue Feb 19 18:08:58 2002 @@ -23,7 +23,6 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set CONFIG_8xx=y # CONFIG_PPC_STD_MMU is not set CONFIG_SERIAL_CONSOLE=y diff -Nru a/arch/ppc/configs/ceder_defconfig b/arch/ppc/configs/ceder_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/ceder_defconfig Tue Feb 19 18:09:01 2002 @@ -0,0 +1,611 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +# CONFIG_6xx is not set +CONFIG_4xx=y +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_PPC_STD_MMU is not set +CONFIG_CEDER=y +# CONFIG_CPCI405 is not set +# CONFIG_EP405 is not set +# CONFIG_OAK is not set +# CONFIG_REDWOOD_4 is not set +# CONFIG_REDWOOD_5 is not set +# CONFIG_TIVO is not set +# CONFIG_WALNUT is not set +# CONFIG_ALL_PPC is not set +# CONFIG_SMP is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_NP405=y +CONFIG_BIOS_FIXUP=y +CONFIG_TREEBOOT=y +CONFIG_IBM405_ERR77=y +CONFIG_IBM_OCP=y +# CONFIG_405_DMA is not set +CONFIG_UART0_TTYS0=y +# CONFIG_UART0_TTYS1 is not set +CONFIG_IBM405_ERR51=y +CONFIG_NOT_COHERENT_CACHE=y + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +# CONFIG_PCI is not set +# CONFIG_PC_KEYBOARD is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_COPS_DAYNA is not set +# CONFIG_COPS_TANGENT is not set +# CONFIG_IPDDP is not set +# CONFIG_IPDDP_ENCAP is not set +# CONFIG_IPDDP_DECAP is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +CONFIG_IBM_OCP_ENET=y +# CONFIG_IBM_OCP_ENET_ERROR_MSG is not set +CONFIG_IBM_OCP_ENET_RX_BUFF=64 +CONFIG_IBM_OCP_ENET_TX_BUFF=8 +CONFIG_IBM_OCP_ENET_GAP=8 +# CONFIG_SUNLANCE is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_PROC is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_WDT is not set +# CONFIG_WDTPCI is not set +# CONFIG_PCWATCHDOG is not set +# CONFIG_ACQUIRE_WDT is not set +# CONFIG_ADVANTECH_WDT is not set +# CONFIG_EUROTECH_WDT is not set +# CONFIG_IB700_WDT is not set +# CONFIG_I810_TCO is not set +# CONFIG_MIXCOMWD is not set +# CONFIG_60XX_WDT is not set +# CONFIG_W83877F_WDT is not set +# CONFIG_MACHZ_WDT is not set +CONFIG_PPC405_WDT=y +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# MPC4xx Driver Options +# + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_SERIAL_TEXT_DEBUG is not set diff -Nru a/arch/ppc/configs/common_defconfig b/arch/ppc/configs/common_defconfig --- a/arch/ppc/configs/common_defconfig Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/configs/common_defconfig Tue Feb 19 18:08:58 2002 @@ -4,6 +4,7 @@ # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y # # Code maturity level options @@ -25,13 +26,27 @@ CONFIG_6xx=y # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set # CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set # CONFIG_8260 is not set CONFIG_PPC_STD_MMU=y CONFIG_ALL_PPC=y # CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +# CONFIG_POWERPMC250 is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +# CONFIG_PRPMC800 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_ADIR is not set +# CONFIG_K2 is not set # CONFIG_GEMINI is not set +# CONFIG_WILLOW is not set +# CONFIG_ZX4500 is not set # CONFIG_SMP is not set CONFIG_ALTIVEC=y CONFIG_TAU=y @@ -71,7 +86,6 @@ CONFIG_PPC601_SYNC_FIX=y CONFIG_PROC_DEVICETREE=y CONFIG_PPC_RTAS=y -CONFIG_BOOTX_TEXT=y CONFIG_PREP_RESIDUAL=y CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="console=ttyS0,9600 console=tty0 root=/dev/sda2" @@ -119,8 +133,6 @@ # CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set -CONFIG_NETLINK=y -# CONFIG_RTNETLINK is not set # CONFIG_NETLINK_DEV is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set @@ -133,6 +145,7 @@ # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set # CONFIG_INET_ECN is not set CONFIG_SYN_COOKIES=y @@ -343,15 +356,11 @@ # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set -CONFIG_SCSI_NCR53C8XX=y -CONFIG_SCSI_SYM53C8XX=y -CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 -CONFIG_SCSI_NCR53C8XX_SYNC=20 -# CONFIG_SCSI_NCR53C8XX_PROFILE is not set -# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set -# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set @@ -388,7 +397,9 @@ # # Appletalk devices # -# CONFIG_APPLETALK is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_IPDDP is not set # CONFIG_DUMMY is not set # CONFIG_BONDING is not set # CONFIG_EQUALIZER is not set @@ -443,6 +454,7 @@ # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_RHINE_MMIO is not set # CONFIG_WINBOND_840 is not set # CONFIG_NET_POCKET is not set @@ -544,6 +556,7 @@ CONFIG_FB_MATROX_MILLENIUM=y CONFIG_FB_MATROX_MYSTIQUE=y # CONFIG_FB_MATROX_G100 is not set +# CONFIG_FB_MATROX_I2C is not set # CONFIG_FB_MATROX_G450 is not set # CONFIG_FB_MATROX_MULTIHEAD is not set CONFIG_FB_ATY=y @@ -587,8 +600,10 @@ # CONFIG_ADB_CUDA=y CONFIG_ADB_PMU=y -# CONFIG_PMAC_PBOOK is not set -# CONFIG_PMAC_BACKLIGHT is not set +CONFIG_PMAC_PBOOK=y +CONFIG_PM=y +CONFIG_PMAC_APM_EMU=y +CONFIG_PMAC_BACKLIGHT=y # CONFIG_MAC_FLOPPY is not set CONFIG_MAC_SERIAL=m CONFIG_ADB=y @@ -597,6 +612,7 @@ CONFIG_MAC_ADBKEYCODES=y CONFIG_MAC_EMUMOUSEBTN=y CONFIG_MAC_HID=y +# CONFIG_ANSLCD is not set # # Character devices @@ -612,7 +628,12 @@ # # I2C support # -# CONFIG_I2C is not set +CONFIG_I2C=m +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +CONFIG_I2C_KEYWEST=m +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_PROC=m # # Mice @@ -693,11 +714,15 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set CONFIG_HFS_FS=m # CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set @@ -710,6 +735,7 @@ # CONFIG_RAMFS is not set CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set @@ -734,13 +760,15 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y -# CONFIG_NFS_V3 is not set +CONFIG_NFS_V3=y # CONFIG_ROOT_NFS is not set CONFIG_NFSD=y -# CONFIG_NFSD_V3 is not set +CONFIG_NFSD_V3=y CONFIG_SUNRPC=y CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y # CONFIG_SMB_FS is not set # CONFIG_NCP_FS is not set # CONFIG_NCPFS_PACKET_SIGNING is not set @@ -751,6 +779,8 @@ # CONFIG_NCPFS_SMALLDOS is not set # CONFIG_NCPFS_NLS is not set # CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set # # Partition Types @@ -818,8 +848,10 @@ # Sound # CONFIG_SOUND=m -CONFIG_DMASOUND_AWACS=m +CONFIG_DMASOUND_PMAC=m CONFIG_DMASOUND=m +CONFIG_I2C=m +CONFIG_I2C_KEYWEST=m # CONFIG_SOUND_BT878 is not set # CONFIG_SOUND_CMPCI is not set # CONFIG_SOUND_EMU10K1 is not set @@ -854,7 +886,6 @@ CONFIG_USB_DEVICEFS=y # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_LONG_TIMEOUT is not set -# CONFIG_USB_LARGE_CONFIG is not set # # USB Controllers @@ -868,12 +899,12 @@ # # CONFIG_USB_AUDIO is not set # CONFIG_USB_BLUETOOTH is not set -# CONFIG_USB_STORAGE is not set +CONFIG_USB_STORAGE=m # CONFIG_USB_STORAGE_DEBUG is not set # CONFIG_USB_STORAGE_DATAFAB is not set -# CONFIG_USB_STORAGE_FREECOM is not set +CONFIG_USB_STORAGE_FREECOM=y # CONFIG_USB_STORAGE_ISD200 is not set -# CONFIG_USB_STORAGE_DPCM is not set +CONFIG_USB_STORAGE_DPCM=y # CONFIG_USB_STORAGE_HP8200e is not set # CONFIG_USB_STORAGE_SDDR09 is not set # CONFIG_USB_STORAGE_JUMPSHOT is not set @@ -932,15 +963,15 @@ # CONFIG_USB_SERIAL_IR is not set # CONFIG_USB_SERIAL_EDGEPORT is not set # CONFIG_USB_SERIAL_KEYSPAN_PDA is not set -# CONFIG_USB_SERIAL_KEYSPAN is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y # CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set # CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y # CONFIG_USB_SERIAL_MCT_U232 is not set # CONFIG_USB_SERIAL_PL2303 is not set # CONFIG_USB_SERIAL_CYBERJACK is not set @@ -963,3 +994,5 @@ CONFIG_MAGIC_SYSRQ=y # CONFIG_KGDB is not set CONFIG_XMON=y +# CONFIG_BDI_SWITCH is not set +CONFIG_BOOTX_TEXT=y diff -Nru a/arch/ppc/configs/cpci405_defconfig b/arch/ppc/configs/cpci405_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/cpci405_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,646 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +# CONFIG_6xx is not set +CONFIG_4xx=y +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_PPC_STD_MMU is not set +# CONFIG_CEDER is not set +CONFIG_CPCI405=y +# CONFIG_EP405 is not set +# CONFIG_OAK is not set +# CONFIG_REDWOOD_4 is not set +# CONFIG_REDWOOD_5 is not set +# CONFIG_TIVO is not set +# CONFIG_WALNUT is not set +# CONFIG_ALL_PPC is not set +# CONFIG_SMP is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_405GP=y +CONFIG_IBM405_ERR77=y +CONFIG_IBM_OCP=y +# CONFIG_405_DMA is not set +CONFIG_UART0_TTYS0=y +# CONFIG_UART0_TTYS1 is not set +CONFIG_IBM405_ERR51=y +CONFIG_NOT_COHERENT_CACHE=y + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +# CONFIG_PC_KEYBOARD is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_PPC_RTC is not set +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_COPS_DAYNA is not set +# CONFIG_COPS_TANGENT is not set +# CONFIG_IPDDP is not set +# CONFIG_IPDDP_ENCAP is not set +# CONFIG_IPDDP_DECAP is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_BLK_DEV_SL82C105 is not set +CONFIG_BLK_DEV_CPCI405_IDE=y +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_DMA_NONPCI is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_PCI is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +CONFIG_IBM_OCP_ENET=y +# CONFIG_IBM_OCP_ENET_ERROR_MSG is not set +CONFIG_IBM_OCP_ENET_RX_BUFF=64 +CONFIG_IBM_OCP_ENET_TX_BUFF=8 +CONFIG_IBM_OCP_ENET_GAP=8 +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_PROC is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +CONFIG_PPC405_GPIO=y + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# MPC4xx Driver Options +# + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_SERIAL_TEXT_DEBUG is not set diff -Nru a/arch/ppc/configs/ep405_defconfig b/arch/ppc/configs/ep405_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/ep405_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,605 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +# CONFIG_6xx is not set +CONFIG_4xx=y +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_PPC_STD_MMU is not set +# CONFIG_CEDER is not set +# CONFIG_CPCI405 is not set +CONFIG_EP405=y +# CONFIG_OAK is not set +# CONFIG_REDWOOD_4 is not set +# CONFIG_REDWOOD_5 is not set +# CONFIG_TIVO is not set +# CONFIG_WALNUT is not set +# CONFIG_ALL_PPC is not set +# CONFIG_SMP is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_405GP=y +CONFIG_BIOS_FIXUP=y +CONFIG_EMBEDDEDBOOT=y +CONFIG_IBM405_ERR77=y +CONFIG_IBM_OCP=y +# CONFIG_405_DMA is not set +CONFIG_UART0_TTYS0=y +# CONFIG_UART0_TTYS1 is not set +CONFIG_IBM405_ERR51=y +CONFIG_NOT_COHERENT_CACHE=y + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +# CONFIG_PC_KEYBOARD is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_PPC_RTC is not set +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_COPS_DAYNA is not set +# CONFIG_COPS_TANGENT is not set +# CONFIG_IPDDP is not set +# CONFIG_IPDDP_ENCAP is not set +# CONFIG_IPDDP_DECAP is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_PCI is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +CONFIG_IBM_OCP_ENET=y +# CONFIG_IBM_OCP_ENET_ERROR_MSG is not set +CONFIG_IBM_OCP_ENET_RX_BUFF=64 +CONFIG_IBM_OCP_ENET_TX_BUFF=8 +CONFIG_IBM_OCP_ENET_GAP=8 +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_PROC is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_PPC405_GPIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# MPC4xx Driver Options +# + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_SERIAL_TEXT_DEBUG is not set diff -Nru a/arch/ppc/configs/est8260_defconfig b/arch/ppc/configs/est8260_defconfig --- a/arch/ppc/configs/est8260_defconfig Tue Feb 19 18:09:00 2002 +++ b/arch/ppc/configs/est8260_defconfig Tue Feb 19 18:09:00 2002 @@ -23,7 +23,6 @@ CONFIG_6xx=y # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set # CONFIG_8xx is not set CONFIG_8260=y CONFIG_PPC_STD_MMU=y diff -Nru a/arch/ppc/configs/ev64260_defconfig b/arch/ppc/configs/ev64260_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/ev64260_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,607 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_ADVANCED_OPTIONS is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_WILLOW is not set +# CONFIG_PCORE is not set +# CONFIG_POWERPMC250 is not set +CONFIG_EV64260=y +# CONFIG_SPRUCE is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PPLUS is not set +# CONFIG_PRPMC750 is not set +# CONFIG_PRPMC800 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_ADIR is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_ZX4500 is not set +CONFIG_GT64260=y +CONFIG_SERIAL_CONSOLE_BAUD=115200 + +# +# Galileo GT64260 Options +# +CONFIG_GT64260_ETH=y +CONFIG_GT64260_ETH_0=y +CONFIG_GT64260_ETH_0_MACADDR="feffff000000" +CONFIG_GT64260_ETH_1=y +CONFIG_GT64260_ETH_1_MACADDR="feffff000001" +CONFIG_GT64260_ETH_2=y +CONFIG_GT64260_ETH_2_MACADDR="feffff000002" +# CONFIG_GT64260_MPSC is not set +# CONFIG_SMP is not set +CONFIG_ALTIVEC=y +CONFIG_TAU=y +# CONFIG_TAU_INT is not set +# CONFIG_TAU_AVERAGE is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_PCI_NAMES=y +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0,115200 ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_CONNTRACK is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_COMPAT_IPCHAINS is not set +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_COPS_DAYNA is not set +# CONFIG_COPS_TANGENT is not set +# CONFIG_IPDDP is not set +# CONFIG_IPDDP_ENCAP is not set +# CONFIG_IPDDP_DECAP is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_PCI is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_8139_NEW_RX_RESET is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_RHINE_MMIO is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +CONFIG_I2C=m +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_PROC=m + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_SERIAL_TEXT_DEBUG is not set diff -Nru a/arch/ppc/configs/gemini_defconfig b/arch/ppc/configs/gemini_defconfig --- a/arch/ppc/configs/gemini_defconfig Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/configs/gemini_defconfig Tue Feb 19 18:08:59 2002 @@ -4,6 +4,7 @@ # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y # # Code maturity level options @@ -25,7 +26,6 @@ CONFIG_6xx=y # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set # CONFIG_8xx is not set # CONFIG_8260 is not set CONFIG_PPC_STD_MMU=y @@ -109,8 +109,6 @@ # CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set -CONFIG_NETLINK=y -# CONFIG_RTNETLINK is not set # CONFIG_NETLINK_DEV is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set @@ -122,6 +120,7 @@ # CONFIG_IP_PNP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set # CONFIG_INET_ECN is not set # CONFIG_SYN_COOKIES is not set @@ -221,15 +220,11 @@ # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set -# CONFIG_SCSI_NCR53C8XX is not set -CONFIG_SCSI_SYM53C8XX=y -CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 -CONFIG_SCSI_NCR53C8XX_SYNC=20 -# CONFIG_SCSI_NCR53C8XX_PROFILE is not set -# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set -# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set @@ -432,11 +427,15 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set @@ -449,6 +448,7 @@ # CONFIG_RAMFS is not set CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set @@ -473,6 +473,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set # CONFIG_ROOT_NFS is not set @@ -490,6 +491,8 @@ # CONFIG_NCPFS_SMALLDOS is not set # CONFIG_NCPFS_NLS is not set # CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set # # Partition Types diff -Nru a/arch/ppc/configs/iSeries_defconfig b/arch/ppc/configs/iSeries_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/iSeries_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,476 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +# CONFIG_6xx is not set +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +CONFIG_PPC_ISERIES=y +CONFIG_PPC64BRIDGE=y +# CONFIG_ALL_PPC is not set +# CONFIG_SMP is not set + +# +# General setup +# +CONFIG_HIGHMEM=y +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +# CONFIG_PCI_ISERIES is not set +# CONFIG_PCI is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +CONFIG_BINFMT_MISC=y +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +# CONFIG_NET_ETHERNET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +CONFIG_CD_NO_IDESCSI=y +# CONFIG_AZTCD is not set +# CONFIG_GSCD is not set +# CONFIG_SBPCD is not set +# CONFIG_MCD is not set +# CONFIG_MCDX is not set +# CONFIG_OPTCD is not set +# CONFIG_CM206 is not set +# CONFIG_SJCD is not set +# CONFIG_ISP16_CDI is not set +# CONFIG_CDU31A is not set +# CONFIG_CDU535 is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# iSeries device drivers +# +CONFIG_VIOCONS=y +CONFIG_VIODASD=y +# CONFIG_VIODASD_IDE is not set +CONFIG_VIOCD=y +# CONFIG_VIOCD_AZTECH is not set +CONFIG_VIOTAPE=y +CONFIG_VETH=y +CONFIG_VIOPATH=y +CONFIG_CD_NO_IDESCSI=y +CONFIG_BLK_DEV_IDECD=y + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_REISERFS_FS=y +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_CRAMFS=y +# CONFIG_TMPFS is not set +CONFIG_RAMFS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set diff -Nru a/arch/ppc/configs/ibmchrp_defconfig b/arch/ppc/configs/ibmchrp_defconfig --- a/arch/ppc/configs/ibmchrp_defconfig Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/configs/ibmchrp_defconfig Tue Feb 19 18:08:59 2002 @@ -4,6 +4,7 @@ # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y # # Code maturity level options @@ -25,7 +26,6 @@ CONFIG_6xx=y # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set # CONFIG_8xx is not set # CONFIG_8260 is not set CONFIG_PPC_STD_MMU=y @@ -112,8 +112,6 @@ # CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set -CONFIG_NETLINK=y -# CONFIG_RTNETLINK is not set # CONFIG_NETLINK_DEV is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set @@ -126,6 +124,7 @@ # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set # CONFIG_INET_ECN is not set CONFIG_SYN_COOKIES=y @@ -252,15 +251,11 @@ # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set -# CONFIG_SCSI_NCR53C8XX is not set -CONFIG_SCSI_SYM53C8XX=y -CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 -CONFIG_SCSI_NCR53C8XX_SYNC=20 -# CONFIG_SCSI_NCR53C8XX_PROFILE is not set -# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set -# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set @@ -343,6 +338,7 @@ # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_RHINE_MMIO is not set # CONFIG_WINBOND_840 is not set # CONFIG_NET_POCKET is not set @@ -546,7 +542,7 @@ # CONFIG_WATCHDOG is not set # CONFIG_INTEL_RNG is not set CONFIG_NVRAM=y -CONFIG_RTC=y +# CONFIG_RTC is not set # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set @@ -571,11 +567,15 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set @@ -588,6 +588,7 @@ # CONFIG_RAMFS is not set CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set @@ -612,6 +613,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set # CONFIG_NFS_FS is not set # CONFIG_NFS_V3 is not set # CONFIG_ROOT_NFS is not set @@ -629,6 +631,8 @@ # CONFIG_NCPFS_SMALLDOS is not set # CONFIG_NCPFS_NLS is not set # CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set # # Partition Types @@ -677,7 +681,7 @@ # CONFIG_NLS_CODEPAGE_874 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_CODEPAGE_1251 is not set -# CONFIG_NLS_ISO8859_1 is not set +CONFIG_NLS_ISO8859_1=m # CONFIG_NLS_ISO8859_2 is not set # CONFIG_NLS_ISO8859_3 is not set # CONFIG_NLS_ISO8859_4 is not set diff -Nru a/arch/ppc/configs/k2_defconfig b/arch/ppc/configs/k2_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/k2_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,638 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_WILLOW is not set +# CONFIG_PCORE is not set +# CONFIG_POWERPMC250 is not set +# CONFIG_EV64260 is not set +# CONFIG_SPRUCE is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PPLUS is not set +# CONFIG_PRPMC750 is not set +# CONFIG_PRPMC800 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_ADIR is not set +CONFIG_K2=y +# CONFIG_GEMINI is not set +# CONFIG_ZX4500 is not set +# CONFIG_CPC710_DATA_GATHERING is not set +# CONFIG_SMP is not set +# CONFIG_ALTIVEC is not set +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_FTP=m +# CONFIG_IP_NF_IRC is not set +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +# CONFIG_IP_NF_MATCH_LENGTH is not set +# CONFIG_IP_NF_MATCH_TTL is not set +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_UNCLEAN=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_MIRROR=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +CONFIG_IP_NF_NAT_FTP=m +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_TARGET_LOG is not set +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_COMPAT_IPCHAINS=m +CONFIG_IP_NF_NAT_NEEDED=y +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +CONFIG_BLK_DEV_IDEDMA_PCI=y +CONFIG_BLK_DEV_ADMA=y +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_IDEDMA_PCI_AUTO is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_PCI_WIP is not set +# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_AEC62XX_TUNING is not set +CONFIG_BLK_DEV_ALI15X3=y +# CONFIG_WDC_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_HPT34X_AUTODMA is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX is not set +# CONFIG_PDC202XX_BURST is not set +# CONFIG_PDC202XX_FORCE is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_IDEDMA_IVB is not set +# CONFIG_DMA_NONPCI is not set +CONFIG_BLK_DEV_IDE_MODES=y +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +# CONFIG_TULIP is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +CONFIG_XMON=y +# CONFIG_BDI_SWITCH is not set diff -Nru a/arch/ppc/configs/lopec_defconfig b/arch/ppc/configs/lopec_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/lopec_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,575 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +# CONFIG_MENF1 is not set +CONFIG_LOPEC=y +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_ZX4500 is not set +# CONFIG_PPC601_SYNC_FIX is not set +# CONFIG_SMP is not set +# CONFIG_ALTIVEC is not set +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +CONFIG_BINFMT_MISC=m +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_PPC_RTC is not set +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_VIODASD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_CONNTRACK is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_COMPAT_IPCHAINS is not set +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_SCSI_DEBUG_QUEUES is not set +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CPQFCTS is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_NCR53C8XX is not set +CONFIG_SCSI_SYM53C8XX=y +CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 +CONFIG_SCSI_NCR53C8XX_SYNC=20 +# CONFIG_SCSI_NCR53C8XX_PROFILE is not set +# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set +# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_MESH is not set +# CONFIG_SCSI_MAC53C94 is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_ARM_AM79C961A is not set +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_NCR885E is not set +# CONFIG_OAKNET is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +# CONFIG_TULIP is not set +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_ACENIC_OMIT_TIGON_I is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_VETH is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +# CONFIG_VIOCONS is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_VIOTAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_EXT2_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +CONFIG_USB_OHCI=y +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_OV511 is not set +# CONFIG_USB_PWC is not set +# CONFIG_USB_SE401 is not set +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_PLUSB is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_NET1080 is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +CONFIG_XMON=y +# CONFIG_BDI_SWITCH is not set diff -Nru a/arch/ppc/configs/mbx_defconfig b/arch/ppc/configs/mbx_defconfig --- a/arch/ppc/configs/mbx_defconfig Tue Feb 19 18:08:56 2002 +++ b/arch/ppc/configs/mbx_defconfig Tue Feb 19 18:08:56 2002 @@ -23,7 +23,6 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set CONFIG_8xx=y # CONFIG_PPC_STD_MMU is not set CONFIG_SERIAL_CONSOLE=y diff -Nru a/arch/ppc/configs/mcpn765_defconfig b/arch/ppc/configs/mcpn765_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/mcpn765_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,475 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +CONFIG_MCPN765=y +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +# CONFIG_PRPMC800 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_ADIR is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_WILLOW is not set +# CONFIG_ZX4500 is not set +# CONFIG_SMP is not set +CONFIG_ALTIVEC=y +# CONFIG_TAU is not set + +# +# General setup +# +CONFIG_HIGHMEM=y +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_VIODASD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +# CONFIG_EEPRO100 is not set +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +CONFIG_SERIAL_EXTENDED=y +# CONFIG_SERIAL_MANY_PORTS is not set +CONFIG_SERIAL_SHARE_IRQ=y +CONFIG_SERIAL_DETECT_IRQ=y +# CONFIG_SERIAL_MULTIPORT is not set +# CONFIG_HUB6 is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +# CONFIG_VIOCONS is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_VIOTAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_EXT2_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_DEBUG_TEXT is not set diff -Nru a/arch/ppc/configs/menf1_defconfig b/arch/ppc/configs/menf1_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/menf1_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,684 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +CONFIG_MENF1=y +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +# CONFIG_PRPMC800 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_ADIR is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_WILLOW is not set +# CONFIG_ZX4500 is not set +CONFIG_MPC10X_STORE_GATHERING=y +# CONFIG_SMP is not set +# CONFIG_ALTIVEC is not set +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_FTP=m +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_UNCLEAN=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_MIRROR=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_NAT_FTP=m +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_TARGET_LOG is not set +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_COMPAT_IPCHAINS=m +CONFIG_IP_NF_NAT_NEEDED=y +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +# CONFIG_BLK_DEV_IDECS is not set +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEPCI=y +# CONFIG_IDEPCI_SHARE_IRQ is not set +# CONFIG_BLK_DEV_IDEDMA_PCI is not set +# CONFIG_BLK_DEV_ADMA is not set +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_IDEDMA_PCI_AUTO is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_PCI_WIP is not set +# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_AEC62XX_TUNING is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_WDC_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_HPT34X_AUTODMA is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX is not set +# CONFIG_PDC202XX_BURST is not set +# CONFIG_PDC202XX_FORCE is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_DMA_NONPCI is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +# CONFIG_EEPRO100 is not set +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_MWAVE is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set diff -Nru a/arch/ppc/configs/mvme5100_defconfig b/arch/ppc/configs/mvme5100_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/mvme5100_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,630 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_8260 is not set +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +# CONFIG_MENF1 is not set +# CONFIG_MCPN765 is not set +CONFIG_MVME5100=y +# CONFIG_PRPMC750 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_ZX4500 is not set +# CONFIG_MVME5100_IPMC761_PRESENT is not set +# CONFIG_PPC601_SYNC_FIX is not set +# CONFIG_SMP is not set +CONFIG_ALTIVEC=y +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_FILTER=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_FTP=m +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_UNCLEAN=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_MIRROR=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_NAT_FTP=m +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_TARGET_LOG is not set +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_COMPAT_IPCHAINS=m +CONFIG_IP_NF_NAT_NEEDED=y +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEPCI=y +# CONFIG_IDEPCI_SHARE_IRQ is not set +# CONFIG_BLK_DEV_IDEDMA_PCI is not set +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_IDEDMA_PCI_AUTO is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_PCI_WIP is not set +# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_AEC62XX_TUNING is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_WDC_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD7409 is not set +# CONFIG_AMD7409_OVERRIDE is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_HPT34X_AUTODMA is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX is not set +# CONFIG_PDC202XX_BURST is not set +# CONFIG_BLK_DEV_OSB4 is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_DMA_NONPCI is not set +# CONFIG_BLK_DEV_IDE_MODES is not set + +# +# SCSI support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_SR_EXTRA_DEVS=2 +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_DEBUG_QUEUES is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CPQFCTS is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_NCR53C8XX is not set +CONFIG_SCSI_SYM53C8XX=y +CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 +CONFIG_SCSI_NCR53C8XX_SYNC=20 +# CONFIG_SCSI_NCR53C8XX_PROFILE is not set +# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set +# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_MESH is not set +# CONFIG_SCSI_MAC53C94 is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_NCR885E is not set +# CONFIG_OAKNET is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_EEPRO100_PM is not set +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set diff -Nru a/arch/ppc/configs/oak_defconfig b/arch/ppc/configs/oak_defconfig --- a/arch/ppc/configs/oak_defconfig Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/configs/oak_defconfig Tue Feb 19 18:08:57 2002 @@ -4,6 +4,7 @@ # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y # # Code maturity level options @@ -25,14 +26,27 @@ # CONFIG_6xx is not set CONFIG_4xx=y # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set # CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set # CONFIG_PPC_STD_MMU is not set +# CONFIG_CEDER is not set +# CONFIG_CPCI405 is not set +# CONFIG_EP405 is not set CONFIG_OAK=y +# CONFIG_REDWOOD_4 is not set +# CONFIG_REDWOOD_5 is not set +# CONFIG_TIVO is not set # CONFIG_WALNUT is not set # CONFIG_ALL_PPC is not set # CONFIG_SMP is not set # CONFIG_MATH_EMULATION is not set +CONFIG_403GCX=y +CONFIG_TREEBOOT=y +# CONFIG_405_DMA is not set +CONFIG_UART0_TTYS0=y +# CONFIG_UART0_TTYS1 is not set +CONFIG_IBM405_ERR51=y +CONFIG_NOT_COHERENT_CACHE=y # # General setup @@ -43,6 +57,7 @@ # CONFIG_SBUS is not set # CONFIG_MCA is not set # CONFIG_PCI is not set +# CONFIG_PC_KEYBOARD is not set CONFIG_NET=y CONFIG_SYSCTL=y CONFIG_SYSVIPC=y @@ -58,6 +73,7 @@ # Parallel port support # # CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y # CONFIG_CMDLINE_BOOL is not set # @@ -102,7 +118,7 @@ # Networking options # # CONFIG_PACKET is not set -# CONFIG_NETLINK is not set +# CONFIG_NETLINK_DEV is not set # CONFIG_NETFILTER is not set # CONFIG_FILTER is not set CONFIG_UNIX=y @@ -116,6 +132,7 @@ # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set # CONFIG_INET_ECN is not set CONFIG_SYN_COOKIES=y # CONFIG_IPV6 is not set @@ -128,6 +145,18 @@ # # CONFIG_IPX is not set # CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_COPS_DAYNA is not set +# CONFIG_COPS_TANGENT is not set +# CONFIG_IPDDP is not set +# CONFIG_IPDDP_ENCAP is not set +# CONFIG_IPDDP_DECAP is not set # CONFIG_DECNET is not set # CONFIG_BRIDGE is not set # CONFIG_X25 is not set @@ -157,6 +186,24 @@ # CONFIG_SCSI is not set # +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# # Network device support # CONFIG_NETDEVICES=y @@ -169,6 +216,7 @@ # CONFIG_BONDING is not set # CONFIG_EQUALIZER is not set # CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set # # Ethernet (10 or 100Mbit) @@ -177,6 +225,7 @@ # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set +CONFIG_OAKNET=y # CONFIG_SUNLANCE is not set # CONFIG_SUNBMAC is not set # CONFIG_SUNQE is not set @@ -332,11 +381,15 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set @@ -349,6 +402,7 @@ # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set @@ -373,6 +427,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -390,6 +445,8 @@ # CONFIG_NCPFS_SMALLDOS is not set # CONFIG_NCPFS_NLS is not set # CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set # # Partition Types @@ -405,6 +462,10 @@ # CONFIG_SOUND is not set # +# MPC4xx Driver Options +# + +# # USB support # # CONFIG_USB is not set @@ -483,6 +544,7 @@ # CONFIG_USB_SERIAL_EMPEG is not set # CONFIG_USB_SERIAL_FTDI_SIO is not set # CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set # CONFIG_USB_SERIAL_IR is not set # CONFIG_USB_SERIAL_EDGEPORT is not set # CONFIG_USB_SERIAL_KEYSPAN_PDA is not set @@ -496,6 +558,7 @@ # CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set # CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set # CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set # CONFIG_USB_SERIAL_PL2303 is not set # CONFIG_USB_SERIAL_CYBERJACK is not set # CONFIG_USB_SERIAL_XIRCOM is not set @@ -517,3 +580,5 @@ # CONFIG_MAGIC_SYSRQ is not set # CONFIG_KGDB is not set # CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_SERIAL_TEXT_DEBUG is not set diff -Nru a/arch/ppc/configs/pcore_defconfig b/arch/ppc/configs/pcore_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/pcore_defconfig Tue Feb 19 18:09:01 2002 @@ -0,0 +1,569 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_8260 is not set +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +CONFIG_PCORE=y +# CONFIG_MENF1 is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_ZX4500 is not set +# CONFIG_MPC10X_STORE_GATHERING is not set +# CONFIG_PPC601_SYNC_FIX is not set +# CONFIG_SMP is not set +CONFIG_ALTIVEC=y +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_FTP=m +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_UNCLEAN=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_MIRROR=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_NAT_FTP=m +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_TARGET_LOG is not set +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_COMPAT_IPCHAINS=m +CONFIG_IP_NF_NAT_NEEDED=y +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_SR_EXTRA_DEVS=2 +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_DEBUG_QUEUES is not set +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CPQFCTS is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_NCR53C8XX is not set +CONFIG_SCSI_SYM53C8XX=y +CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 +CONFIG_SCSI_NCR53C8XX_SYNC=20 +# CONFIG_SCSI_NCR53C8XX_PROFILE is not set +# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set +# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_MESH is not set +# CONFIG_SCSI_MAC53C94 is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_NCR885E is not set +# CONFIG_OAKNET is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_EEPRO100_PM is not set +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +CONFIG_BUSMOUSE=y +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_LOGIBUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_MOUSE=y +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set diff -Nru a/arch/ppc/configs/pmac_defconfig b/arch/ppc/configs/pmac_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/pmac_defconfig Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1076 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_POWER4 is not set +# CONFIG_8xx is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +CONFIG_ALL_PPC=y +# CONFIG_APUS is not set +# CONFIG_GEMINI is not set +# CONFIG_SMP is not set +CONFIG_ALTIVEC=y +CONFIG_TAU=y +# CONFIG_TAU_INT is not set +# CONFIG_TAU_AVERAGE is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +CONFIG_BINFMT_MISC=m +CONFIG_PCI_NAMES=y +CONFIG_HOTPLUG=y + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=m +CONFIG_CARDBUS=y +CONFIG_I82092=y +CONFIG_I82365=y +CONFIG_TCIC=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +CONFIG_PPC601_SYNC_FIX=y +CONFIG_PROC_DEVICETREE=y +CONFIG_PPC_RTAS=y +CONFIG_BOOTX_TEXT=y +# CONFIG_PREP_RESIDUAL is not set +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +CONFIG_BLK_DEV_FD=m +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_FILTER=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_IRC=m +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_LENGTH=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_UNCLEAN=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_MIRROR=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_NAT_SNMP_BASIC=m +CONFIG_IP_NF_NAT_IRC=m +CONFIG_IP_NF_NAT_FTP=m +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_TARGET_LOG is not set +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_COMPAT_IPCHAINS=m +CONFIG_IP_NF_NAT_NEEDED=y +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +CONFIG_ATALK=m +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +CONFIG_BLK_DEV_IDEFLOPPY=y +CONFIG_BLK_DEV_IDESCSI=y + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +CONFIG_BLK_DEV_IDEDMA_PCI=y +CONFIG_BLK_DEV_ADMA=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_IDEDMA_PCI_AUTO=y +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_PCI_WIP is not set +# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_AEC62XX_TUNING is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_WDC_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_AMD74XX_OVERRIDE is not set +CONFIG_BLK_DEV_CMD64X=y +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_HPT34X_AUTODMA is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX is not set +# CONFIG_PDC202XX_BURST is not set +# CONFIG_PDC202XX_FORCE is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +CONFIG_BLK_DEV_SL82C105=y +CONFIG_BLK_DEV_IDE_PMAC=y +CONFIG_BLK_DEV_IDEDMA_PMAC=y +CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO=y +CONFIG_BLK_DEV_IDEDMA=y +CONFIG_BLK_DEV_IDEPCI=y +# CONFIG_IDE_CHIPSETS is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_IDEDMA_IVB is not set +# CONFIG_DMA_NONPCI is not set +CONFIG_BLK_DEV_IDE_MODES=y +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set + +# +# SCSI support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 +CONFIG_CHR_DEV_ST=y +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_SR_EXTRA_DEVS=2 +CONFIG_CHR_DEV_SG=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_DEBUG_QUEUES is not set +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +CONFIG_SCSI_AIC7XXX=m +CONFIG_AIC7XXX_CMDS_PER_DEVICE=253 +CONFIG_AIC7XXX_RESET_DELAY_MS=15000 +# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set +CONFIG_SCSI_AIC7XXX_OLD=m +# CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT is not set +CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE=8 +CONFIG_AIC7XXX_OLD_PROC_STATS=y +# CONFIG_SCSI_DPT_I2O is not set +CONFIG_SCSI_ADVANSYS=m +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CPQFCTS is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_DEBUG is not set +CONFIG_SCSI_MESH=y +CONFIG_SCSI_MESH_SYNC_RATE=5 +CONFIG_SCSI_MAC53C94=y + +# +# PCMCIA SCSI adapter support +# +# CONFIG_SCSI_PCMCIA is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +CONFIG_IEEE1394=m + +# +# Device Drivers +# +CONFIG_IEEE1394_PCILYNX=m +# CONFIG_IEEE1394_PCILYNX_LOCALRAM is not set +# CONFIG_IEEE1394_PCILYNX_PORTS is not set +CONFIG_IEEE1394_OHCI1394=m + +# +# Protocol Drivers +# +CONFIG_IEEE1394_VIDEO1394=m +CONFIG_IEEE1394_SBP2=m +CONFIG_IEEE1394_RAWIO=m +# CONFIG_IEEE1394_VERBOSEDEBUG is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Appletalk devices +# +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_IPDDP is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MACE=y +# CONFIG_MACE_AAUI_PORT is not set +CONFIG_BMAC=y +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +CONFIG_SUNGEM=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +CONFIG_PCNET32=y +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +CONFIG_TULIP_MMIO=y +CONFIG_DE4X5=m +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +# CONFIG_EEPRO100 is not set +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_RHINE_MMIO is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=y +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=m +# CONFIG_PPPOE is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y +# CONFIG_STRIP is not set +# CONFIG_WAVELAN is not set +# CONFIG_ARLAN is not set +# CONFIG_AIRONET4500 is not set +# CONFIG_AIRONET4500_NONCS is not set +# CONFIG_AIRONET4500_PROC is not set +# CONFIG_AIRO is not set +CONFIG_HERMES=m +CONFIG_APPLE_AIRPORT=m +# CONFIG_PLX_HERMES is not set + +# +# Wireless Pcmcia cards support +# +CONFIG_PCMCIA_HERMES=m +# CONFIG_AIRO_CS is not set +CONFIG_NET_WIRELESS=y + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +# CONFIG_PCMCIA_3C589 is not set +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_PCMCIA_PCNET is not set +# CONFIG_PCMCIA_AXNET is not set +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_PCMCIA_SMC91C92 is not set +# CONFIG_PCMCIA_XIRC2PS is not set +# CONFIG_ARCNET_COM20020_CS is not set +# CONFIG_PCMCIA_IBMTR is not set +# CONFIG_PCMCIA_XIRCOM is not set +# CONFIG_PCMCIA_XIRTULIP is not set +CONFIG_NET_PCMCIA_RADIO=y +# CONFIG_PCMCIA_RAYCS is not set +# CONFIG_PCMCIA_NETWAVE is not set +# CONFIG_PCMCIA_WAVELAN is not set +# CONFIG_AIRONET4500_CS is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +CONFIG_IRDA=m + +# +# IrDA protocols +# +CONFIG_IRLAN=m +CONFIG_IRNET=m +CONFIG_IRCOMM=m +# CONFIG_IRDA_ULTRA is not set +# CONFIG_IRDA_OPTIONS is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +CONFIG_IRTTY_SIR=m +# CONFIG_IRPORT_SIR is not set + +# +# Dongle support +# +# CONFIG_DONGLE is not set + +# +# FIR device drivers +# +# CONFIG_USB_IRDA is not set +# CONFIG_NSC_FIR is not set +# CONFIG_WINBOND_FIR is not set +# CONFIG_TOSHIBA_FIR is not set +# CONFIG_SMC_IRCC_FIR is not set +# CONFIG_ALI_FIR is not set +# CONFIG_VLSI_FIR is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +CONFIG_FB=y +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FB_RIVA is not set +# CONFIG_FB_CLGEN is not set +# CONFIG_FB_PM2 is not set +# CONFIG_FB_CYBER2000 is not set +CONFIG_FB_OF=y +CONFIG_FB_CONTROL=y +CONFIG_FB_PLATINUM=y +CONFIG_FB_VALKYRIE=y +CONFIG_FB_CT65550=y +CONFIG_FB_IMSTT=y +# CONFIG_FB_S3TRIO is not set +# CONFIG_FB_VGA16 is not set +CONFIG_FB_MATROX=y +CONFIG_FB_MATROX_MILLENIUM=y +CONFIG_FB_MATROX_MYSTIQUE=y +# CONFIG_FB_MATROX_G100 is not set +# CONFIG_FB_MATROX_I2C is not set +# CONFIG_FB_MATROX_G450 is not set +# CONFIG_FB_MATROX_MULTIHEAD is not set +CONFIG_FB_ATY=y +CONFIG_FB_ATY_GX=y +CONFIG_FB_ATY_CT=y +CONFIG_FB_RADEON=y +CONFIG_FB_ATY128=y +# CONFIG_FB_SIS is not set +CONFIG_FB_3DFX=y +# CONFIG_FB_VOODOO1 is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FBCON_ADVANCED is not set +CONFIG_FBCON_CFB8=y +CONFIG_FBCON_CFB16=y +CONFIG_FBCON_CFB24=y +CONFIG_FBCON_CFB32=y +# CONFIG_FBCON_FONTWIDTH8_ONLY is not set +CONFIG_FBCON_FONTS=y +# CONFIG_FONT_8x8 is not set +CONFIG_FONT_8x16=y +CONFIG_FONT_SUN8x16=y +CONFIG_FONT_SUN12x22=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +CONFIG_FB_COMPAT_XPMAC=y + +# +# Input core support +# +CONFIG_INPUT=y +CONFIG_INPUT_KEYBDEV=y +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y + +# +# Macintosh device drivers +# +CONFIG_ADB_CUDA=y +CONFIG_ADB_PMU=y +CONFIG_PMAC_PBOOK=y +CONFIG_PM=y +CONFIG_PMAC_APM_EMU=y +CONFIG_PMAC_BACKLIGHT=y +CONFIG_MAC_FLOPPY=y +CONFIG_MAC_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set +CONFIG_ADB=y +CONFIG_ADB_MACIO=y +CONFIG_INPUT_ADBHID=y +CONFIG_MAC_ADBKEYCODES=y +CONFIG_MAC_EMUMOUSEBTN=y +CONFIG_MAC_HID=y +# CONFIG_ANSLCD is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=m +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +CONFIG_I2C=m +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +CONFIG_I2C_KEYWEST=m +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_PROC=m + +# +# Mice +# +CONFIG_BUSMOUSE=y +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_LOGIBUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_MOUSE=y +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_INPUT_NS558 is not set +# CONFIG_INPUT_LIGHTNING is not set +# CONFIG_INPUT_PCIGAME is not set +# CONFIG_INPUT_CS461X is not set +# CONFIG_INPUT_EMU10K1 is not set +# CONFIG_INPUT_SERIO is not set +# CONFIG_INPUT_SERPORT is not set + +# +# Joysticks +# +# CONFIG_INPUT_ANALOG is not set +# CONFIG_INPUT_A3D is not set +# CONFIG_INPUT_ADI is not set +# CONFIG_INPUT_COBRA is not set +# CONFIG_INPUT_GF2K is not set +# CONFIG_INPUT_GRIP is not set +# CONFIG_INPUT_INTERACT is not set +# CONFIG_INPUT_TMDC is not set +# CONFIG_INPUT_SIDEWINDER is not set +# CONFIG_INPUT_IFORCE_USB is not set +# CONFIG_INPUT_IFORCE_232 is not set +# CONFIG_INPUT_WARRIOR is not set +# CONFIG_INPUT_MAGELLAN is not set +# CONFIG_INPUT_SPACEORB is not set +# CONFIG_INPUT_SPACEBALL is not set +# CONFIG_INPUT_STINGER is not set +# CONFIG_INPUT_DB9 is not set +# CONFIG_INPUT_GAMECON is not set +# CONFIG_INPUT_TURBOGRAFX is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +CONFIG_NVRAM=y +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_PCMCIA_SERIAL_CS is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +CONFIG_HFS_FS=m +# CONFIG_BFS_FS is not set +CONFIG_EXT3_FS=y +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +# CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=m +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=y +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +CONFIG_SMB_NLS=y +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ISO8859_1=m +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Sound +# +CONFIG_SOUND=m +CONFIG_DMASOUND_PMAC=m +CONFIG_DMASOUND=m +CONFIG_I2C=m +CONFIG_I2C_KEYWEST=m +# CONFIG_SOUND_BT878 is not set +# CONFIG_SOUND_CMPCI is not set +# CONFIG_SOUND_EMU10K1 is not set +# CONFIG_MIDI_EMU10K1 is not set +# CONFIG_SOUND_FUSION is not set +# CONFIG_SOUND_CS4281 is not set +# CONFIG_SOUND_ES1370 is not set +# CONFIG_SOUND_ES1371 is not set +# CONFIG_SOUND_ESSSOLO1 is not set +# CONFIG_SOUND_MAESTRO is not set +# CONFIG_SOUND_MAESTRO3 is not set +# CONFIG_SOUND_ICH is not set +# CONFIG_SOUND_RME96XX is not set +# CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +# CONFIG_SOUND_VIA82CXXX is not set +# CONFIG_MIDI_VIA82CXXX is not set +# CONFIG_SOUND_OSS is not set +# CONFIG_SOUND_TVMIXER is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_LONG_TIMEOUT is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +CONFIG_USB_OHCI=y + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_WACOM is not set + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +CONFIG_USB_SCANNER=m +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +CONFIG_USB_SERIAL_VISOR=m +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +CONFIG_MAGIC_SYSRQ=y +# CONFIG_KGDB is not set +CONFIG_XMON=y diff -Nru a/arch/ppc/configs/power3_defconfig b/arch/ppc/configs/power3_defconfig --- a/arch/ppc/configs/power3_defconfig Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/configs/power3_defconfig Tue Feb 19 18:08:58 2002 @@ -4,6 +4,7 @@ # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y # # Code maturity level options @@ -25,7 +26,6 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set CONFIG_POWER3=y -# CONFIG_POWER4 is not set # CONFIG_8xx is not set CONFIG_PPC64BRIDGE=y CONFIG_ALL_PPC=y @@ -66,6 +66,7 @@ # CONFIG_PARPORT_AMIGA is not set # CONFIG_PARPORT_MFC3 is not set # CONFIG_PARPORT_ATARI is not set +# CONFIG_PARPORT_GSC is not set # CONFIG_PARPORT_SUNBPP is not set # CONFIG_PARPORT_OTHER is not set # CONFIG_PARPORT_1284 is not set @@ -119,8 +120,6 @@ # CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set -CONFIG_NETLINK=y -# CONFIG_RTNETLINK is not set # CONFIG_NETLINK_DEV is not set # CONFIG_NETFILTER is not set # CONFIG_FILTER is not set @@ -132,6 +131,7 @@ # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set # CONFIG_INET_ECN is not set CONFIG_SYN_COOKIES=y # CONFIG_IPV6 is not set @@ -224,15 +224,11 @@ # CONFIG_SCSI_IMM is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set -# CONFIG_SCSI_NCR53C8XX is not set -CONFIG_SCSI_SYM53C8XX=y -CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 -CONFIG_SCSI_NCR53C8XX_SYNC=20 -# CONFIG_SCSI_NCR53C8XX_PROFILE is not set -# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set -# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set @@ -315,6 +311,7 @@ # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_RHINE_MMIO is not set # CONFIG_WINBOND_840 is not set # CONFIG_NET_POCKET is not set @@ -474,6 +471,7 @@ # CONFIG_I2C_VELLEMAN is not set CONFIG_I2C_ALGOPCF=y # CONFIG_I2C_ELEKTOR is not set +# CONFIG_I2C_KEYWEST is not set CONFIG_I2C_CHARDEV=y # CONFIG_I2C_PROC is not set @@ -553,11 +551,15 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set CONFIG_FAT_FS=y CONFIG_MSDOS_FS=y # CONFIG_UMSDOS_FS is not set @@ -570,6 +572,7 @@ # CONFIG_RAMFS is not set CONFIG_ISO9660_FS=y CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set @@ -594,6 +597,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set # CONFIG_ROOT_NFS is not set @@ -611,6 +615,8 @@ # CONFIG_NCPFS_SMALLDOS is not set # CONFIG_NCPFS_NLS is not set # CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set # # Partition Types @@ -665,7 +671,7 @@ # Sound # CONFIG_SOUND=y -# CONFIG_DMASOUND_AWACS is not set +# CONFIG_DMASOUND_PMAC is not set # CONFIG_SOUND_BT878 is not set # CONFIG_SOUND_CMPCI is not set # CONFIG_SOUND_EMU10K1 is not set diff -Nru a/arch/ppc/configs/pplus_defconfig b/arch/ppc/configs/pplus_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/pplus_defconfig Tue Feb 19 18:09:01 2002 @@ -0,0 +1,780 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_WILLOW is not set +# CONFIG_PCORE is not set +# CONFIG_POWERPMC250 is not set +# CONFIG_EV64260 is not set +# CONFIG_SPRUCE is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +CONFIG_PPLUS=y +# CONFIG_PRPMC750 is not set +# CONFIG_PRPMC800 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_ADIR is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_ZX4500 is not set +# CONFIG_SMP is not set +# CONFIG_ALTIVEC is not set +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +CONFIG_BLK_DEV_FD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_FTP=m +# CONFIG_IP_NF_IRC is not set +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +# CONFIG_IP_NF_MATCH_LENGTH is not set +# CONFIG_IP_NF_MATCH_TTL is not set +# CONFIG_IP_NF_MATCH_TCPMSS is not set +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_UNCLEAN=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_MIRROR=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +CONFIG_IP_NF_NAT_FTP=m +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_TARGET_LOG is not set +# CONFIG_IP_NF_TARGET_TCPMSS is not set +CONFIG_IP_NF_COMPAT_IPCHAINS=m +CONFIG_IP_NF_NAT_NEEDED=y +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +# CONFIG_BLK_DEV_IDECS is not set +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +CONFIG_BLK_DEV_IDEFLOPPY=y +CONFIG_BLK_DEV_IDESCSI=y + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +CONFIG_BLK_DEV_IDEDMA_PCI=y +CONFIG_BLK_DEV_ADMA=y +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_IDEDMA_PCI_AUTO is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_PCI_WIP is not set +# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_AEC62XX_TUNING is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_WDC_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_HPT34X_AUTODMA is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX is not set +# CONFIG_PDC202XX_BURST is not set +# CONFIG_PDC202XX_FORCE is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +CONFIG_BLK_DEV_VIA82CXXX=y +CONFIG_BLK_DEV_SL82C105=y +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_IDEDMA_IVB is not set +# CONFIG_DMA_NONPCI is not set +CONFIG_BLK_DEV_IDE_MODES=y +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set + +# +# SCSI support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 +CONFIG_CHR_DEV_ST=y +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_SR_EXTRA_DEVS=2 +CONFIG_CHR_DEV_SG=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_DEBUG_QUEUES is not set +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CPQFCTS is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_NCR53C8XX is not set +CONFIG_SCSI_SYM53C8XX=y +CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 +CONFIG_SCSI_NCR53C8XX_SYNC=20 +# CONFIG_SCSI_NCR53C8XX_PROFILE is not set +# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set +# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_DEBUG is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +CONFIG_BUSMOUSE=y +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_LOGIBUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_MOUSE=y +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set diff -Nru a/arch/ppc/configs/prpmc750_defconfig b/arch/ppc/configs/prpmc750_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/prpmc750_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,567 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +CONFIG_PRPMC750=y +# CONFIG_PRPMC800 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_ADIR is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_WILLOW is not set +# CONFIG_ZX4500 is not set +# CONFIG_SMP is not set +# CONFIG_ALTIVEC is not set +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_PPC_RTC is not set +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_FTP=m +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_UNCLEAN=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_MIRROR=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_NAT_FTP=m +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_TARGET_LOG is not set +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_COMPAT_IPCHAINS=m +CONFIG_IP_NF_NAT_NEEDED=y +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +CONFIG_VT=y +# CONFIG_VT_CONSOLE is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +CONFIG_BUSMOUSE=y +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_LOGIBUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_MOUSE=y +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_MWAVE is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set diff -Nru a/arch/ppc/configs/prpmc800_defconfig b/arch/ppc/configs/prpmc800_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/prpmc800_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,525 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +CONFIG_PRPMC800=y +# CONFIG_SANDPOINT is not set +# CONFIG_ADIR is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_WILLOW is not set +# CONFIG_ZX4500 is not set +# CONFIG_SMP is not set +# CONFIG_ALTIVEC is not set +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_PPC_RTC is not set +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_VIODASD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_FTP=m +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_UNCLEAN=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_MIRROR=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_NAT_FTP=m +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_TARGET_LOG is not set +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_COMPAT_IPCHAINS=m +CONFIG_IP_NF_NAT_NEEDED=y +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_LAN_SAA9730 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +CONFIG_VT=y +# CONFIG_VT_CONSOLE is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +# CONFIG_VIOCONS is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +CONFIG_BUSMOUSE=y +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_LOGIBUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_MOUSE=y +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set +# CONFIG_VIOTAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_DEBUG_TEXT is not set diff -Nru a/arch/ppc/configs/redwood5_defconfig b/arch/ppc/configs/redwood5_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/redwood5_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,582 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +# CONFIG_6xx is not set +CONFIG_4xx=y +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_PPC_STD_MMU is not set +# CONFIG_CEDER is not set +# CONFIG_CPCI405 is not set +# CONFIG_EP405 is not set +# CONFIG_OAK is not set +# CONFIG_REDWOOD_4 is not set +CONFIG_REDWOOD_5=y +# CONFIG_TIVO is not set +# CONFIG_WALNUT is not set +# CONFIG_ALL_PPC is not set +# CONFIG_SMP is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_STB03xxx=y +CONFIG_TREEBOOT=y +CONFIG_IBM405_ERR77=y +CONFIG_IBM_OCP=y +# CONFIG_405_DMA is not set +CONFIG_UART0_TTYS0=y +# CONFIG_UART0_TTYS1 is not set +CONFIG_IBM405_ERR51=y +CONFIG_NOT_COHERENT_CACHE=y + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +# CONFIG_PCI is not set +# CONFIG_PC_KEYBOARD is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_PPC_RTC is not set +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_COPS_DAYNA is not set +# CONFIG_COPS_TANGENT is not set +# CONFIG_IPDDP is not set +# CONFIG_IPDDP_ENCAP is not set +# CONFIG_IPDDP_DECAP is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +CONFIG_IBM_OCP_IDE=y +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_DMA_NONPCI is not set +CONFIG_BLK_DEV_IDE_MODES=y +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_IBM_OCP_ENET is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +CONFIG_NET_VENDOR_SMC=y +# CONFIG_WD80x3 is not set +# CONFIG_ULTRAMCA is not set +# CONFIG_ULTRA is not set +# CONFIG_ULTRA32 is not set +# CONFIG_SMC9194 is not set +CONFIG_SMC91111=y +CONFIG_SMC91111_ADVANCED=y +CONFIG_SMC91111_BYTE_SWAP=y +# CONFIG_SMC91111_USE_8_BIT is not set +# CONFIG_SMC91111_USE_32_BIT is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_PROC is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# MPC4xx Driver Options +# +# CONFIG_STB_KB is not set +# CONFIG_SERIAL_SICC is not set + +# +# USB support +# +# CONFIG_USB is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_SERIAL_TEXT_DEBUG is not set diff -Nru a/arch/ppc/configs/redwood_defconfig b/arch/ppc/configs/redwood_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/redwood_defconfig Tue Feb 19 18:09:01 2002 @@ -0,0 +1,546 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +# CONFIG_6xx is not set +CONFIG_4xx=y +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_PPC_STD_MMU is not set +# CONFIG_OAK is not set +CONFIG_REDWOOD_4=y +# CONFIG_REDWOOD_5 is not set +# CONFIG_TIVO is not set +# CONFIG_EP405 is not set +# CONFIG_CPCI405 is not set +# CONFIG_WALNUT is not set +# CONFIG_ALL_PPC is not set +# CONFIG_SMP is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_STB03xxx=y +CONFIG_TREEBOOT=y +CONFIG_IBM405_ERR77=y +# CONFIG_405_DMA is not set +CONFIG_IBM405_ERR51=y +CONFIG_NOT_COHERENT_CACHE=y + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +# CONFIG_PCI is not set +# CONFIG_PC_KEYBOARD is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_PPC_RTC is not set +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +CONFIG_OAKNET=y +# CONFIG_SUNLANCE is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_PROC is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_MWAVE is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# MPC4xx Driver Options +# +# CONFIG_STB_KB is not set +# CONFIG_SERIAL_SICC is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_DEBUG_TEXT is not set diff -Nru a/arch/ppc/configs/rpxcllf_defconfig b/arch/ppc/configs/rpxcllf_defconfig --- a/arch/ppc/configs/rpxcllf_defconfig Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/configs/rpxcllf_defconfig Tue Feb 19 18:08:58 2002 @@ -23,7 +23,6 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set CONFIG_8xx=y # CONFIG_PPC_STD_MMU is not set CONFIG_SERIAL_CONSOLE=y diff -Nru a/arch/ppc/configs/rpxlite_defconfig b/arch/ppc/configs/rpxlite_defconfig --- a/arch/ppc/configs/rpxlite_defconfig Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/configs/rpxlite_defconfig Tue Feb 19 18:08:58 2002 @@ -23,7 +23,6 @@ # CONFIG_6xx is not set # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set CONFIG_8xx=y # CONFIG_PPC_STD_MMU is not set CONFIG_SERIAL_CONSOLE=y diff -Nru a/arch/ppc/configs/sandpoint_defconfig b/arch/ppc/configs/sandpoint_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/sandpoint_defconfig Tue Feb 19 18:09:01 2002 @@ -0,0 +1,655 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +# CONFIG_POWERPMC250 is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +# CONFIG_PRPMC800 is not set +CONFIG_SANDPOINT=y +# CONFIG_ADIR is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_WILLOW is not set +# CONFIG_ZX4500 is not set +# CONFIG_MPC10X_STORE_GATHERING is not set +CONFIG_SANDPOINT_X3=y +CONFIG_EPIC_SERIAL_MODE=y +# CONFIG_SMP is not set +CONFIG_ALTIVEC=y +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +CONFIG_BINFMT_MISC=y +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +# CONFIG_BLK_DEV_IDECS is not set +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +CONFIG_BLK_DEV_IDEDMA_PCI=y +CONFIG_BLK_DEV_ADMA=y +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_IDEDMA_PCI_AUTO is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_PCI_WIP is not set +# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_AEC62XX_TUNING is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_WDC_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_HPT34X_AUTODMA is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX is not set +# CONFIG_PDC202XX_BURST is not set +# CONFIG_PDC202XX_FORCE is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +CONFIG_BLK_DEV_SL82C105=y +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_IDEDMA_IVB is not set +# CONFIG_DMA_NONPCI is not set +CONFIG_BLK_DEV_IDE_MODES=y +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Controllers +# +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_DEBUG_TEXT is not set diff -Nru a/arch/ppc/configs/spruce_defconfig b/arch/ppc/configs/spruce_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/spruce_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,531 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +CONFIG_SPRUCE=y +# CONFIG_PCORE is not set +# CONFIG_POWERPMC250 is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +# CONFIG_PRPMC800 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_ADIR is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +# CONFIG_WILLOW is not set +# CONFIG_ZX4500 is not set +# CONFIG_SPRUCE_BAUD_33M is not set +# CONFIG_SMP is not set +# CONFIG_ALTIVEC is not set +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +CONFIG_PCNET32=y +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +# CONFIG_TULIP is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +# CONFIG_EEPRO100 is not set +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +CONFIG_MOUSE=y +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_RIO500 is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set diff -Nru a/arch/ppc/configs/walnut_defconfig b/arch/ppc/configs/walnut_defconfig --- a/arch/ppc/configs/walnut_defconfig Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/configs/walnut_defconfig Tue Feb 19 18:08:58 2002 @@ -4,6 +4,7 @@ # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y # # Code maturity level options @@ -25,14 +26,30 @@ # CONFIG_6xx is not set CONFIG_4xx=y # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set # CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set # CONFIG_PPC_STD_MMU is not set +# CONFIG_CEDER is not set +# CONFIG_CPCI405 is not set +# CONFIG_EP405 is not set # CONFIG_OAK is not set +# CONFIG_REDWOOD_4 is not set +# CONFIG_REDWOOD_5 is not set +# CONFIG_TIVO is not set CONFIG_WALNUT=y # CONFIG_ALL_PPC is not set # CONFIG_SMP is not set # CONFIG_MATH_EMULATION is not set +CONFIG_405GP=y +CONFIG_BIOS_FIXUP=y +CONFIG_TREEBOOT=y +CONFIG_IBM405_ERR77=y +CONFIG_IBM_OCP=y +# CONFIG_405_DMA is not set +CONFIG_UART0_TTYS0=y +# CONFIG_UART0_TTYS1 is not set +CONFIG_IBM405_ERR51=y +CONFIG_NOT_COHERENT_CACHE=y # # General setup @@ -42,7 +59,8 @@ # CONFIG_EISA is not set # CONFIG_SBUS is not set # CONFIG_MCA is not set -# CONFIG_PCI is not set +CONFIG_PCI=y +# CONFIG_PC_KEYBOARD is not set CONFIG_NET=y CONFIG_SYSCTL=y CONFIG_SYSVIPC=y @@ -51,6 +69,7 @@ CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set # CONFIG_HOTPLUG is not set # CONFIG_PCMCIA is not set @@ -58,6 +77,7 @@ # Parallel port support # # CONFIG_PARPORT is not set +CONFIG_PPC_RTC=y # CONFIG_CMDLINE_BOOL is not set # @@ -102,7 +122,7 @@ # Networking options # # CONFIG_PACKET is not set -# CONFIG_NETLINK is not set +# CONFIG_NETLINK_DEV is not set # CONFIG_NETFILTER is not set # CONFIG_FILTER is not set CONFIG_UNIX=y @@ -116,6 +136,7 @@ # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set # CONFIG_INET_ECN is not set CONFIG_SYN_COOKIES=y # CONFIG_IPV6 is not set @@ -128,6 +149,18 @@ # # CONFIG_IPX is not set # CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_COPS_DAYNA is not set +# CONFIG_COPS_TANGENT is not set +# CONFIG_IPDDP is not set +# CONFIG_IPDDP_ENCAP is not set +# CONFIG_IPDDP_DECAP is not set # CONFIG_DECNET is not set # CONFIG_BRIDGE is not set # CONFIG_X25 is not set @@ -157,6 +190,30 @@ # CONFIG_SCSI is not set # +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_PCI is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# # Network device support # CONFIG_NETDEVICES=y @@ -169,6 +226,7 @@ # CONFIG_BONDING is not set # CONFIG_EQUALIZER is not set # CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set # # Ethernet (10 or 100Mbit) @@ -177,7 +235,13 @@ # CONFIG_MACE is not set # CONFIG_BMAC is not set # CONFIG_GMAC is not set +CONFIG_IBM_OCP_ENET=y +# CONFIG_IBM_OCP_ENET_ERROR_MSG is not set +CONFIG_IBM_OCP_ENET_RX_BUFF=64 +CONFIG_IBM_OCP_ENET_TX_BUFF=8 +CONFIG_IBM_OCP_ENET_GAP=8 # CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set # CONFIG_SUNBMAC is not set # CONFIG_SUNQE is not set # CONFIG_SUNLANCE is not set @@ -186,6 +250,7 @@ # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set # CONFIG_NET_ISA is not set # CONFIG_NET_PCI is not set # CONFIG_NET_POCKET is not set @@ -315,6 +380,7 @@ # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set +# CONFIG_PPC405_GPIO is not set # # Ftape, the floppy tape device driver @@ -336,11 +402,15 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set @@ -353,6 +423,7 @@ # CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set @@ -377,6 +448,7 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y # CONFIG_NFS_V3 is not set CONFIG_ROOT_NFS=y @@ -394,6 +466,8 @@ # CONFIG_NCPFS_SMALLDOS is not set # CONFIG_NCPFS_NLS is not set # CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set # # Partition Types @@ -409,6 +483,10 @@ # CONFIG_SOUND is not set # +# MPC4xx Driver Options +# + +# # USB support # # CONFIG_USB is not set @@ -487,6 +565,7 @@ # CONFIG_USB_SERIAL_EMPEG is not set # CONFIG_USB_SERIAL_FTDI_SIO is not set # CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set # CONFIG_USB_SERIAL_IR is not set # CONFIG_USB_SERIAL_EDGEPORT is not set # CONFIG_USB_SERIAL_KEYSPAN_PDA is not set @@ -500,6 +579,7 @@ # CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set # CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set # CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set # CONFIG_USB_SERIAL_PL2303 is not set # CONFIG_USB_SERIAL_CYBERJACK is not set # CONFIG_USB_SERIAL_XIRCOM is not set @@ -521,3 +601,5 @@ # CONFIG_MAGIC_SYSRQ is not set # CONFIG_KGDB is not set # CONFIG_XMON is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_SERIAL_TEXT_DEBUG is not set diff -Nru a/arch/ppc/configs/zx4500_defconfig b/arch/ppc/configs/zx4500_defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/configs/zx4500_defconfig Tue Feb 19 18:09:00 2002 @@ -0,0 +1,479 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_6xx=y +# CONFIG_4xx is not set +# CONFIG_POWER3 is not set +# CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set +# CONFIG_8260 is not set +CONFIG_PPC_STD_MMU=y +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +# CONFIG_MENF1 is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_K2 is not set +# CONFIG_GEMINI is not set +CONFIG_ZX4500=y +# CONFIG_MPC10X_STORE_GATHERING is not set +# CONFIG_PPC601_SYNC_FIX is not set +# CONFIG_SMP is not set +# CONFIG_ALTIVEC is not set +# CONFIG_TAU is not set + +# +# General setup +# +# CONFIG_HIGHMEM is not set +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_PCI_NAMES=y +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +# CONFIG_PPC_RTC is not set +# CONFIG_CMDLINE_BOOL is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_VIODASD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_ARM_AM79C961A is not set +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_NCR885E is not set +# CONFIG_OAKNET is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +CONFIG_EEPRO100=y +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139TOO is not set +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_ACENIC_OMIT_TIGON_I is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_VETH is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +CONFIG_SERIAL_EXTENDED=y +# CONFIG_SERIAL_MANY_PORTS is not set +CONFIG_SERIAL_SHARE_IRQ=y +CONFIG_SERIAL_DETECT_IRQ=y +# CONFIG_SERIAL_MULTIPORT is not set +# CONFIG_HUB6 is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +# CONFIG_VIOCONS is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set +# CONFIG_VIOTAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +CONFIG_NFSD=y +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_DEBUG_TEXT is not set diff -Nru a/arch/ppc/defconfig b/arch/ppc/defconfig --- a/arch/ppc/defconfig Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/defconfig Tue Feb 19 18:08:58 2002 @@ -4,6 +4,7 @@ # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y # # Code maturity level options @@ -25,13 +26,27 @@ CONFIG_6xx=y # CONFIG_4xx is not set # CONFIG_POWER3 is not set -# CONFIG_POWER4 is not set # CONFIG_8xx is not set +# CONFIG_PPC_ISERIES is not set # CONFIG_8260 is not set CONFIG_PPC_STD_MMU=y CONFIG_ALL_PPC=y # CONFIG_APUS is not set +# CONFIG_SPRUCE is not set +# CONFIG_PCORE is not set +# CONFIG_POWERPMC250 is not set +# CONFIG_MENF1 is not set +# CONFIG_LOPEC is not set +# CONFIG_MCPN765 is not set +# CONFIG_MVME5100 is not set +# CONFIG_PRPMC750 is not set +# CONFIG_PRPMC800 is not set +# CONFIG_SANDPOINT is not set +# CONFIG_ADIR is not set +# CONFIG_K2 is not set # CONFIG_GEMINI is not set +# CONFIG_WILLOW is not set +# CONFIG_ZX4500 is not set # CONFIG_SMP is not set CONFIG_ALTIVEC=y CONFIG_TAU=y @@ -71,7 +86,6 @@ CONFIG_PPC601_SYNC_FIX=y CONFIG_PROC_DEVICETREE=y CONFIG_PPC_RTAS=y -CONFIG_BOOTX_TEXT=y CONFIG_PREP_RESIDUAL=y CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="console=ttyS0,9600 console=tty0 root=/dev/sda2" @@ -119,8 +133,6 @@ # CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set -CONFIG_NETLINK=y -# CONFIG_RTNETLINK is not set # CONFIG_NETLINK_DEV is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set @@ -133,6 +145,7 @@ # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set # CONFIG_INET_ECN is not set CONFIG_SYN_COOKIES=y @@ -343,15 +356,11 @@ # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set -CONFIG_SCSI_NCR53C8XX=y -CONFIG_SCSI_SYM53C8XX=y -CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 -CONFIG_SCSI_NCR53C8XX_SYNC=20 -# CONFIG_SCSI_NCR53C8XX_PROFILE is not set -# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set -# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set @@ -388,7 +397,9 @@ # # Appletalk devices # -# CONFIG_APPLETALK is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_IPDDP is not set # CONFIG_DUMMY is not set # CONFIG_BONDING is not set # CONFIG_EQUALIZER is not set @@ -443,6 +454,7 @@ # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_RHINE_MMIO is not set # CONFIG_WINBOND_840 is not set # CONFIG_NET_POCKET is not set @@ -544,6 +556,7 @@ CONFIG_FB_MATROX_MILLENIUM=y CONFIG_FB_MATROX_MYSTIQUE=y # CONFIG_FB_MATROX_G100 is not set +# CONFIG_FB_MATROX_I2C is not set # CONFIG_FB_MATROX_G450 is not set # CONFIG_FB_MATROX_MULTIHEAD is not set CONFIG_FB_ATY=y @@ -587,8 +600,10 @@ # CONFIG_ADB_CUDA=y CONFIG_ADB_PMU=y -# CONFIG_PMAC_PBOOK is not set -# CONFIG_PMAC_BACKLIGHT is not set +CONFIG_PMAC_PBOOK=y +CONFIG_PM=y +CONFIG_PMAC_APM_EMU=y +CONFIG_PMAC_BACKLIGHT=y # CONFIG_MAC_FLOPPY is not set CONFIG_MAC_SERIAL=m CONFIG_ADB=y @@ -597,6 +612,7 @@ CONFIG_MAC_ADBKEYCODES=y CONFIG_MAC_EMUMOUSEBTN=y CONFIG_MAC_HID=y +# CONFIG_ANSLCD is not set # # Character devices @@ -612,7 +628,12 @@ # # I2C support # -# CONFIG_I2C is not set +CONFIG_I2C=m +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +CONFIG_I2C_KEYWEST=m +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_PROC=m # # Mice @@ -693,11 +714,15 @@ # CONFIG_AUTOFS4_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set CONFIG_HFS_FS=m # CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set @@ -710,6 +735,7 @@ # CONFIG_RAMFS is not set CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set @@ -734,13 +760,15 @@ # Network File Systems # # CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y -# CONFIG_NFS_V3 is not set +CONFIG_NFS_V3=y # CONFIG_ROOT_NFS is not set CONFIG_NFSD=y -# CONFIG_NFSD_V3 is not set +CONFIG_NFSD_V3=y CONFIG_SUNRPC=y CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y # CONFIG_SMB_FS is not set # CONFIG_NCP_FS is not set # CONFIG_NCPFS_PACKET_SIGNING is not set @@ -751,6 +779,8 @@ # CONFIG_NCPFS_SMALLDOS is not set # CONFIG_NCPFS_NLS is not set # CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set # # Partition Types @@ -818,8 +848,10 @@ # Sound # CONFIG_SOUND=m -CONFIG_DMASOUND_AWACS=m +CONFIG_DMASOUND_PMAC=m CONFIG_DMASOUND=m +CONFIG_I2C=m +CONFIG_I2C_KEYWEST=m # CONFIG_SOUND_BT878 is not set # CONFIG_SOUND_CMPCI is not set # CONFIG_SOUND_EMU10K1 is not set @@ -854,7 +886,6 @@ CONFIG_USB_DEVICEFS=y # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_LONG_TIMEOUT is not set -# CONFIG_USB_LARGE_CONFIG is not set # # USB Controllers @@ -868,12 +899,12 @@ # # CONFIG_USB_AUDIO is not set # CONFIG_USB_BLUETOOTH is not set -# CONFIG_USB_STORAGE is not set +CONFIG_USB_STORAGE=m # CONFIG_USB_STORAGE_DEBUG is not set # CONFIG_USB_STORAGE_DATAFAB is not set -# CONFIG_USB_STORAGE_FREECOM is not set +CONFIG_USB_STORAGE_FREECOM=y # CONFIG_USB_STORAGE_ISD200 is not set -# CONFIG_USB_STORAGE_DPCM is not set +CONFIG_USB_STORAGE_DPCM=y # CONFIG_USB_STORAGE_HP8200e is not set # CONFIG_USB_STORAGE_SDDR09 is not set # CONFIG_USB_STORAGE_JUMPSHOT is not set @@ -932,15 +963,15 @@ # CONFIG_USB_SERIAL_IR is not set # CONFIG_USB_SERIAL_EDGEPORT is not set # CONFIG_USB_SERIAL_KEYSPAN_PDA is not set -# CONFIG_USB_SERIAL_KEYSPAN is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y # CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set # CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set -# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y # CONFIG_USB_SERIAL_MCT_U232 is not set # CONFIG_USB_SERIAL_PL2303 is not set # CONFIG_USB_SERIAL_CYBERJACK is not set @@ -963,3 +994,5 @@ CONFIG_MAGIC_SYSRQ=y # CONFIG_KGDB is not set CONFIG_XMON=y +# CONFIG_BDI_SWITCH is not set +CONFIG_BOOTX_TEXT=y diff -Nru a/arch/ppc/iSeries/HvCall.c b/arch/ppc/iSeries/HvCall.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/HvCall.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,127 @@ +/* + * HvCall.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _HVCALLSC_H +#include +#endif +#include + +#ifndef _HVTYPES_H +#include +#endif + +//===================================================================== +// Note that this call takes at MOST one page worth of data +int HvCall_readLogBuffer(HvLpIndex lpIndex, void *buffer, u64 bufLen) +{ + struct HvLpBufferList *bufList; + u64 bytesLeft = bufLen; + u64 leftThisPage; + u64 curPtr = (unsigned long) buffer; + u64 retVal; + int npages; + int i; + + npages = 0; + while (bytesLeft) + { + npages++; + leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr; + + if (leftThisPage > bytesLeft) + bytesLeft = 0; + else + bytesLeft -= leftThisPage; + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + if (npages == 0) + return 0; + + bufList = (struct HvLpBufferList *)kmalloc(npages * sizeof(struct HvLpBufferList), GFP_ATOMIC); + bytesLeft = bufLen; + curPtr = (unsigned long) buffer; + for(i=0; i bytesLeft) + { + bufList[i].len = bytesLeft; + bytesLeft = 0; + } + else + { + bufList[i].len = leftThisPage; + bytesLeft -= leftThisPage; + } + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + + retVal = HvCall3(HvCallBaseReadLogBuffer,lpIndex, virt_to_absolute((unsigned long)bufList), bufLen); + + kfree(bufList); + + return (int)retVal; +} + +//===================================================================== +void HvCall_writeLogBuffer(const void *buffer, u64 bufLen) +{ + struct HvLpBufferList bufList; + u64 bytesLeft = bufLen; + u64 leftThisPage; + u64 curPtr = (unsigned long) buffer; + + while (bytesLeft) + { + bufList.addr = virt_to_absolute( curPtr ); + + leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr; + + if (leftThisPage > bytesLeft) + { + bufList.len = bytesLeft; + bytesLeft = 0; + } + else + { + bufList.len = leftThisPage; + bytesLeft -= leftThisPage; + } + + HvCall2(HvCallBaseWriteLogBuffer, virt_to_absolute((unsigned long)&bufList), bufList.len); + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } +} + diff -Nru a/arch/ppc/iSeries/HvLpConfig.c b/arch/ppc/iSeries/HvLpConfig.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/HvLpConfig.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,27 @@ +/* + * HvLpConfig.c + * Copyright (C) 2001 Kyle A. Lucke, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _HVLPCONFIG_H +#include +#endif + +HvLpIndex HvLpConfig_getLpIndex_outline(void) +{ + return HvLpConfig_getLpIndex(); +} diff -Nru a/arch/ppc/iSeries/HvLpEvent.c b/arch/ppc/iSeries/HvLpEvent.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/HvLpEvent.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,87 @@ +/* + * HvLpEvent.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +// Array of LpEvent handler functions +LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; +unsigned lpEventHandlerPaths[HvLpEvent_Type_NumTypes]; + +// Register a handler for an LpEvent type + +int HvLpEvent_registerHandler( HvLpEvent_Type eventType, LpEventHandler handler ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes ) { + lpEventHandler[eventType] = handler; + rc = 0; + } + return rc; + +} + +int HvLpEvent_unregisterHandler( HvLpEvent_Type eventType ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes ) { + if ( !lpEventHandlerPaths[eventType] ) { + lpEventHandler[eventType] = NULL; + rc = 0; + } + } + return rc; +} + +// (lpIndex is the partition index of the target partition. +// needed only for VirtualIo, VirtualLan and SessionMgr. Zero +// indicates to use our partition index - for the other types) +int HvLpEvent_openPath( HvLpEvent_Type eventType, HvLpIndex lpIndex ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes && + lpEventHandler[eventType] ) { + if ( lpIndex == 0 ) + lpIndex = itLpNaca.xLpIndex; + HvCallEvent_openLpEventPath( lpIndex, eventType ); + ++lpEventHandlerPaths[eventType]; + rc = 0; + } + return rc; +} + +int HvLpEvent_closePath( HvLpEvent_Type eventType, HvLpIndex lpIndex ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes && + lpEventHandler[eventType] && + lpEventHandlerPaths[eventType] ) { + if ( lpIndex == 0 ) + lpIndex = itLpNaca.xLpIndex; + HvCallEvent_closeLpEventPath( lpIndex, eventType ); + --lpEventHandlerPaths[eventType]; + rc = 0; + } + return rc; +} + diff -Nru a/arch/ppc/iSeries/ItLpQueue.c b/arch/ppc/iSeries/ItLpQueue.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/ItLpQueue.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,178 @@ +/* + * ItLpQueue.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static __inline__ int set_inUse( struct ItLpQueue * lpQueue ) +{ + int t; + u32 * inUseP = &(lpQueue->xInUseWord); + + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 \n\ + cmpi 0,%0,0 \n\ + li %0,0 \n\ + bne- 2f \n\ + addi %0,%0,1 \n\ + stwcx. %0,0,%2 \n\ + bne- 1b \n\ +2: eieio" + : "=&r" (t), "=m" (lpQueue->xInUseWord) + : "r" (inUseP), "m" (lpQueue->xInUseWord) + : "cc"); + + return t; +} + +static __inline__ void clear_inUse( struct ItLpQueue * lpQueue ) +{ + lpQueue->xInUseWord = 0; +} + +// Array of LpEvent handler functions +extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; +unsigned long ItLpQueueInProcess = 0; + +struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * lpQueue ) +{ + struct HvLpEvent * nextLpEvent = + (struct HvLpEvent *)lpQueue->xSlicCurEventPtr; + if ( nextLpEvent->xFlags.xValid ) { + // Set pointer to next potential event + lpQueue->xSlicCurEventPtr += ((nextLpEvent->xSizeMinus1 + + LpEventAlign ) / + LpEventAlign ) * + LpEventAlign; + // Wrap to beginning if no room at end + if (lpQueue->xSlicCurEventPtr > lpQueue->xSlicLastValidEventPtr) + lpQueue->xSlicCurEventPtr = lpQueue->xSlicEventStackPtr; + } + else + nextLpEvent = NULL; + + return nextLpEvent; +} + +int ItLpQueue_isLpIntPending( struct ItLpQueue * lpQueue ) +{ + struct HvLpEvent * nextLpEvent = + (struct HvLpEvent *)lpQueue->xSlicCurEventPtr; + return ( nextLpEvent->xFlags.xValid | + lpQueue->xPlicOverflowIntPending); +} + +void ItLpQueue_clearValid( struct HvLpEvent * event ) +{ + // Clear the valid bit of the event + // Also clear bits within this event that might + // look like valid bits (on 64-byte boundaries) + unsigned extra = (( event->xSizeMinus1 + LpEventAlign ) / + LpEventAlign ) - 1; + switch ( extra ) { + case 3: + ((struct HvLpEvent*)((char*)event+3*LpEventAlign))->xFlags.xValid=0; + case 2: + ((struct HvLpEvent*)((char*)event+2*LpEventAlign))->xFlags.xValid=0; + case 1: + ((struct HvLpEvent*)((char*)event+1*LpEventAlign))->xFlags.xValid=0; + case 0: + } + mb(); + event->xFlags.xValid = 0; +} + +// No lock is necessary when processing the Lp Queue because a single +// processor is assigned to each lpqueue. Interrupts are disabled +// while processing events. +// Some device drivers' interrupt handlers run with interrupts +// enabled. This requires us to prevent being re-entered here. +// We use the xInUse flag for that. + +unsigned lpQueue_proc_count[32] = {}; + +unsigned ItLpQueue_process( struct ItLpQueue * lpQueue, struct pt_regs *regs ) +{ + unsigned numIntsProcessed = 0; + struct HvLpEvent * nextLpEvent; + + // If we have recursed, just return + if ( !set_inUse( lpQueue ) ) + return 0; + + if (ItLpQueueInProcess == 0) + ItLpQueueInProcess = 1; + else + BUG(); + + ++lpQueue_proc_count[current->processor]; + + for (;;) { + nextLpEvent = ItLpQueue_getNextLpEvent( lpQueue ); + + if ( nextLpEvent ) { + // Count events to return to caller + // and count processed events in lpQueue + ++numIntsProcessed; + lpQueue->xLpIntCount++; + // Call appropriate handler here, passing + // a pointer to the LpEvent. The handler + // must make a copy of the LpEvent if it + // needs it in a bottom half. (perhaps for + // an ACK) + + // Handlers are responsible for ACK processing + + // The Hypervisor guarantees that LpEvents will + // only be delivered with types that we have + // registered for, so no type check is necessary + // here! + if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes ) + lpQueue->xLpIntCountByType[nextLpEvent->xType]++; + if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes && + lpEventHandler[nextLpEvent->xType] ) + lpEventHandler[nextLpEvent->xType](nextLpEvent, regs); + else + printk(KERN_INFO "Unexpected Lp Event type=%d\n", nextLpEvent->xType ); + + ItLpQueue_clearValid( nextLpEvent ); + } + else // No more valid events + // If overflow events are pending + // process them + if ( lpQueue->xPlicOverflowIntPending ) { + HvCallEvent_getOverflowLpEvents( + lpQueue->xIndex); + } + else // If nothing left then we are done + break; + } + + ItLpQueueInProcess = 0; + mb(); + clear_inUse( lpQueue ); + + return numIntsProcessed; +} diff -Nru a/arch/ppc/iSeries/LparData.c b/arch/ppc/iSeries/LparData.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/LparData.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,331 @@ +/* + * LparData.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __KERNEL__ 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ReleaseData.h" + +// maxProcessors is the number of physical processors +// The number of logical processors is twice that +// number to support hardware multi-threading. +// If CONFIG_SMP is not defined, then logical +// processors will be defined, but the other threads +// will spin forever in iSeries_head.S +#define maxProcessors 16 + +extern char _start_boltedStacks[]; +unsigned maxPacas = maxProcessors * 2; + +// The LparMap is used by the hypervisor to map the load area. +// This indicates that the load area should be mapped to VSID +// 0x000000000000C and that this should be made addressable at +// ESID 0x00000000C. On 32-bit machines this is equivalent to +// loading segment register 12 with VSID 12. +// 8192 indicates to map 8192 pages (32 MB) of the load area. + +struct LparMap xLparMap = { + xNumberEsids: 4, // Number ESID/VSID pairs + xNumberRanges: 1, // Number of memory ranges + xSegmentTableOffs: 0, // Segment Table Page (unused) + xKernelEsidC: 0xC, // ESID to map + xKernelVsidC: 0xCCC, // VSID to map + xKernelEsidD: 0xD, // ESID to map + xKernelVsidD: 0xDDD, // VSID to map + xKernelEsidE: 0xE, // ESID to map + xKernelVsidE: 0xEEE, // VSID to map + xKernelEsidF: 0xF, // ESID to map + xKernelVsidF: 0xFFF, // VSID to map + + xPages: HvPagesToMap, // # of pages to map (8192) + xOffset: 0, // Offset into load area + xVPN: 0xCCC0000 // VPN of first mapped page +}; + +// The Naca has a pointer to the ItVpdAreas. The hypervisor finds +// the Naca via the HvReleaseData area. The HvReleaseData has the +// offset into the Naca of the pointer to the ItVpdAreas. + +extern struct ItVpdAreas itVpdAreas; + +struct Naca xNaca = { + 0, (void *)&itVpdAreas, + 0, 0, // Ram Disk start + 0, 0 // Ram Disk size +}; + +// The LpQueue is used to pass event data from the hypervisor to +// the partition. This is where I/O interrupt events are communicated. +// The ItLpQueue must be initialized (even though only to all zeros) +// If it were uninitialized (in .bss) it would get zeroed after the +// kernel gets control. The hypervisor will have filled in some fields +// before the kernel gets control. By initializing it we keep it out +// of the .bss + +struct ItLpQueue xItLpQueue = {}; + +// The Paca is an array with one entry per processor. Each contains an +// ItLpPaca, which contains the information shared between the +// hypervisor and Linux. Each also contains an ItLpRegSave area which +// is used by the hypervisor to save registers. +// On systems with hardware multi-threading, there are two threads +// per processor. The Paca array must contain an entry for each thread. +// The VPD Areas will give a max logical processors = 2 * max physical +// processors. The processor VPD array needs one entry per physical +// processor (not thread). + +#define PacaInit( n, start, lpq ) \ + { 0, (struct ItLpPaca *)(((char *)(&xPaca[(n)]))+offsetof(struct Paca, xLpPaca)), \ + 0, (struct ItLpRegSave *)(((char *)(&xPaca[(n)]))+offsetof(struct Paca, xRegSav)), \ + 0, 0, 0, \ + (n), (start), \ + 0, \ + 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0},\ + (lpq), \ + 0, 0, {0}, \ + { /* LpPaca */ \ + xDesc: 0xd397d781, /* "LpPa" */ \ + xSize: sizeof(struct ItLpPaca), \ + xFPRegsInUse: 1, \ + xDynProcStatus: 2, \ + xEndOfQuantum: 0xffffffffffffffff \ + }, \ + { /* LpRegSave */ \ + 0xd397d9e2, /* "LpRS" */ \ + sizeof(struct ItLpRegSave) \ + } \ + } + +struct Paca xPaca[maxProcessors*2] = { + PacaInit( 0, 1, &xItLpQueue ) + ,PacaInit( 1, 0, 0 ) + ,PacaInit( 2, 0, 0 ) + ,PacaInit( 3, 0, 0 ) + ,PacaInit( 4, 0, 0 ) + ,PacaInit( 5, 0, 0 ) + ,PacaInit( 6, 0, 0 ) + ,PacaInit( 7, 0, 0 ) + ,PacaInit( 8, 0, 0 ) + ,PacaInit( 9, 0, 0 ) + ,PacaInit( 10, 0, 0 ) + ,PacaInit( 11, 0, 0 ) + ,PacaInit( 12, 0, 0 ) + ,PacaInit( 13, 0, 0 ) + ,PacaInit( 14, 0, 0 ) + ,PacaInit( 15, 0, 0 ) + ,PacaInit( 16, 0, 0 ) + ,PacaInit( 17, 0, 0 ) + ,PacaInit( 18, 0, 0 ) + ,PacaInit( 19, 0, 0 ) + ,PacaInit( 20, 0, 0 ) + ,PacaInit( 21, 0, 0 ) + ,PacaInit( 22, 0, 0 ) + ,PacaInit( 23, 0, 0 ) + ,PacaInit( 24, 0, 0 ) + ,PacaInit( 25, 0, 0 ) + ,PacaInit( 26, 0, 0 ) + ,PacaInit( 27, 0, 0 ) + ,PacaInit( 28, 0, 0 ) + ,PacaInit( 29, 0, 0 ) + ,PacaInit( 30, 0, 0 ) + ,PacaInit( 31, 0, 0 ) +}; + + + +// The HvReleaseData is the root of the information shared between +// the hypervisor and Linux. + +struct HvReleaseData + hvReleaseData = { 0xc8a5d9c4, // desc = "HvRD" ebcdic + sizeof(struct HvReleaseData), + offsetof(struct Naca, xItVpdAreas64), + 0, &xNaca, // 64-bit Naca address + ((u32)&xLparMap-KERNELBASE), + 0, + 1, // tags inactive mode + 1, // 32-bit mode + 0, // shared processors + 0, // hardware multithreading + 6, // TEMP: set me back to 0 + // this allows non-ga 450 driver + 3, // We are v5r1m0 + 3, // Min supported PLIC = v5r1m0 + 3, // Min usuable PLIC = v5r1m0 + RELEASEDATA, + {0} + }; + + +struct ItLpNaca itLpNaca = { 0xd397d581, // desc = "LpNa" ebcdic + 0x0400, // size of ItLpNaca + 0x0300, 19, // offset to int array, # ents + 0, 0, 0, // Part # of primary, serv, me + 0, 0x100, // # of LP queues, offset + 0, 0, 0, // Piranha stuff + { 0,0,0,0,0 }, // reserved + 0,0,0,0,0,0,0, // stuff + { 0,0,0,0,0 }, // reserved + 0, // reserved + 0, // VRM index of PLIC + 0, 0, // min supported, compat SLIC + 0, // 64-bit addr of load area + 0, // chunks for load area + 0, // reserved + { 0 }, // 72 reserved bytes + { 0 }, // 128 reserved bytes + { 0 }, // Old LP Queue + { 0 }, // 384 reserved bytes + { + 0xc0000100, // int 0x100 + 0xc0000200, // int 0x200 + 0xc0000300, // int 0x300 + 0xc0000400, // int 0x400 + 0xc0000500, // int 0x500 + 0xc0000600, // int 0x600 + 0xc0000700, // int 0x700 + 0xc0000800, // int 0x800 + 0xc0000900, // int 0x900 + 0xc0000a00, // int 0xa00 + 0xc0000b00, // int 0xb00 + 0xc0000c00, // int 0xc00 + 0xc0000d00, // int 0xd00 + 0xc0000e00, // int 0xe00 + 0xc0000f00, // int 0xf00 + 0xc0001000, // int 0x1000 + 0xc0001010, // int 0x1010 + 0xc0001020, // int 0x1020 CPU ctls + 0xc0000500 // SC Ret Hdlr + // int 0x380 + // int 0x480 + } + }; + +// All of the data structures that will be filled in by the hypervisor +// must be initialized (even if only to zeroes) to keep them out of +// the bss. If in bss, they will be zeroed by the kernel startup code +// after the hypervisor has filled them in. + +struct ItIplParmsReal xItIplParmsReal = {}; + +struct IoHriProcessorVpd xIoHriProcessorVpd[maxProcessors] = { + { + xTimeBaseFreq: 50000000 + } +}; + + +u64 xMsVpd[3400] = {}; // Space for Main Store Vpd 27,200 bytes + +u64 xRecoveryLogBuffer[32] = {}; // Space for Recovery Log Buffer + +struct SpCommArea xSpCommArea = { + 0xE2D7C3C2, + 1, + {0}, + 0, 0, 0, 0, {0} +}; + +struct ItVpdAreas itVpdAreas = { + 0xc9a3e5c1, // "ItVA" + sizeof( struct ItVpdAreas ), + 0, 0, + 26, // # VPD array entries + 10, // # DMA array entries + maxProcessors*2, maxProcessors, // Max logical, physical procs + offsetof(struct ItVpdAreas,xPlicDmaToks),// offset to DMA toks + offsetof(struct ItVpdAreas,xSlicVpdAdrs),// offset to VPD addrs + offsetof(struct ItVpdAreas,xPlicDmaLens),// offset to DMA lens + offsetof(struct ItVpdAreas,xSlicVpdLens),// offset to VPD lens + 0, // max slot labels + 1, // max LP queues + {0}, {0}, // reserved + {0}, // DMA lengths + {0}, // DMA tokens + { // VPD lengths + 0,0,0,0, // 0 - 3 + sizeof(struct Paca), // 4 length of Paca + 0, // 5 + sizeof(struct ItIplParmsReal),// 6 length of IPL parms + 26992, // 7 length of MS VPD + 0, // 8 + sizeof(struct ItLpNaca),// 9 length of LP Naca + 0, // 10 + 256, // 11 length of Recovery Log Buf + sizeof(struct SpCommArea), // 12 length of SP Comm area + 0,0,0, // 13 - 15 + sizeof(struct IoHriProcessorVpd),// 16 length of Proc Vpd + 0,0,0,0,0,0, // 17 - 22 + sizeof(struct ItLpQueue),// 23 length of Lp Queue + 0,0 // 24 - 25 + }, + { // VPD addresses + {0},{0},{0},{0}, // 0 - 3 + {0, &xPaca[0]}, // 4 first Paca + {0}, // 5 + {0, &xItIplParmsReal}, // 6 IPL parms + {0, &xMsVpd}, // 7 MS Vpd + {0}, // 8 + {0, &itLpNaca}, // 9 LpNaca + {0}, // 10 + {0, &xRecoveryLogBuffer},// 11 Recovery Log Buffer + {0, &xSpCommArea}, // 12 Sp Comm Area + {0},{0},{0}, // 13 - 15 + {0, &xIoHriProcessorVpd},// 16 Proc Vpd + {0},{0},{0},{0},{0},{0},// 17 - 22 + {0, &xItLpQueue}, // 23 Lp Queue + {0},{0} + } + +}; + +// The size of this array determines how much main store can be +// configured for use in the partition. 16384 allows 16384 * 256KB +// which is 4GB. This is enough for the 32-bit +// implementation, but the msChunks array will need to be dynamically +// allocated for really big partitions. +u32 msChunks[16384] = {0}; +u32 totalLpChunks = 0; + +// Data area used in flush_hash_page +long long flush_hash_page_hpte[2] = {0,0}; + +u64 virt_to_absolute_outline(u32 address) +{ + return virt_to_absolute(address); +} diff -Nru a/arch/ppc/iSeries/Makefile b/arch/ppc/iSeries/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,26 @@ +# +# Makefile for Linux arch/ppc/iSeries source directory +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := iSeries.o +export-objs := iSeries_ksyms.o +obj-y += LparData.o ItLpQueue.o HvLpEvent.o HvCall.o mf.o iSeries_proc.o mf_proc.o iSeries_ksyms.o HvLpConfig.o pmc_proc.o rtc.o + +obj-$(CONFIG_PCI) += XmPciLpEvent.o iSeries_FlightRecorder.o iSeries_IoMmTable.o iSeries_VpdInfo.o iSeries_fixup.o iSeries_irq.o iSeries_pci.o iSeries_pci_proc.o iSeries_reset_device.o + +all: iSeries.o + +LparData.c:: ReleaseData.h + +ReleaseData.h: $(TOPDIR)/Makefile + /bin/bash ./createReleaseData $(KERNELRELEASE) > $@ + +clean: + rm -f ReleaseData.h + +include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc/iSeries/XmPciLpEvent.c b/arch/ppc/iSeries/XmPciLpEvent.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/XmPciLpEvent.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,150 @@ +/* + * File XmPciLpEvent.h created by Wayne Holm on Mon Jan 15 2001. + * + * This module handles PCI interrupt events sent by the AS400 Hypervisor. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +enum XmPciLpEvent_Subtype { + XmPciLpEvent_BusCreated = 0, // PHB has been created + XmPciLpEvent_BusFailed = 1, // PHB has failed + XmPciLpEvent_BusRecovered = 12, // PHB has been recovered + XmPciLpEvent_NodeFailed = 4, // Multi-adapter bridge has failed + XmPciLpEvent_NodeRecovered = 5, // Multi-adapter bridge has recovered + XmPciLpEvent_SlotInterrupt = 22 // Slot interrupt +}; + +struct XmPciLpEvent_BusInterrupt { + HvBusNumber busNumber; + HvSubBusNumber subBusNumber; +}; + +struct XmPciLpEvent_NodeInterrupt { + HvBusNumber busNumber; + HvSubBusNumber subBusNumber; + HvAgentId deviceId; +}; + +struct XmPciLpEvent { + struct HvLpEvent hvLpEvent; + + union { + u64 alignData; // Align on an 8-byte boundary + + struct { + u32 fisr; + HvBusNumber busNumber; + HvSubBusNumber subBusNumber; + HvAgentId deviceId; + } slotInterrupt; + + struct XmPciLpEvent_BusInterrupt busFailed; + struct XmPciLpEvent_BusInterrupt busRecovered; + struct XmPciLpEvent_BusInterrupt busCreated; + + struct XmPciLpEvent_NodeInterrupt nodeFailed; + struct XmPciLpEvent_NodeInterrupt nodeRecovered; + + } eventData; + +}; + +static void intReceived(struct XmPciLpEvent* eventParm, struct pt_regs* regsParm); + +static void XmPciLpEvent_handler( struct HvLpEvent* eventParm, struct pt_regs* regsParm) { + if (eventParm && eventParm->xType == HvLpEvent_Type_PciIo) { + switch( eventParm->xFlags.xFunction ) { + case HvLpEvent_Function_Int: + intReceived( (struct XmPciLpEvent*)eventParm, regsParm ); + break; + case HvLpEvent_Function_Ack: + printk(KERN_ERR "XmPciLpEvent.c: unexpected ack received\n"); + break; + default: + printk(KERN_ERR "XmPciLpEvent.c: unexpected event function %d\n", + (int)eventParm->xFlags.xFunction); + break; + }; + } + else { + if (event) { + printk(KERN_ERR "XmPciLpEvent.c: unrecognized event type 0x%x\n", + (int)eventParm->xType); + } + else { + printk(KERN_ERR "XmPciLpEvent.c: NULL event received\n"); + } + } +} + +static void intReceived(struct XmPciLpEvent* eventParm, struct pt_regs* regsParm) { + int irq; + + switch (eventParm->hvLpEvent.xSubtype) { + case XmPciLpEvent_SlotInterrupt: + irq = eventParm->hvLpEvent.xCorrelationToken; + /* Dispatch the interrupt handlers for this irq */ + ppc_irq_dispatch_handler(regsParm, irq); + HvCallPci_eoi(eventParm->eventData.slotInterrupt.busNumber, + eventParm->eventData.slotInterrupt.subBusNumber, + eventParm->eventData.slotInterrupt.deviceId); + break; + /* Ignore error recovery events for now */ + case XmPciLpEvent_BusCreated: + printk(KERN_INFO "XmPciLpEvent.c: system bus %d created\n", eventParm->eventData.busCreated.busNumber); + break; + case XmPciLpEvent_BusFailed: + printk(KERN_INFO "XmPciLpEvent.c: system bus %d failed\n", eventParm->eventData.busFailed.busNumber); + break; + case XmPciLpEvent_BusRecovered: + printk(KERN_INFO "XmPciLpEvent.c: system bus %d recovered\n", eventParm->eventData.busRecovered.busNumber); + break; + case XmPciLpEvent_NodeFailed: + printk(KERN_INFO "XmPciLpEvent.c: multi-adapter bridge %d/%d/%d failed\n", eventParm->eventData.nodeFailed.busNumber, eventParm->eventData.nodeFailed.subBusNumber, eventParm->eventData.nodeFailed.deviceId); + break; + case XmPciLpEvent_NodeRecovered: + printk(KERN_INFO "XmPciLpEvent.c: multi-adapter bridge %d/%d/%d recovered\n", eventParm->eventData.nodeRecovered.busNumber, eventParm->eventData.nodeRecovered.subBusNumber, eventParm->eventData.nodeRecovered.deviceId); + break; + default: + printk(KERN_ERR "XmPciLpEvent.c: unrecognized event subtype 0x%x\n", + eventParm->hvLpEvent.xSubtype); + break; + }; +} + + +/* This should be called sometime prior to buswalk (init_IRQ would be good) */ +int XmPciLpEvent_init() { + int xRc; + + xRc = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo, &XmPciLpEvent_handler); + if (xRc == 0) { + xRc = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0); + if (xRc != 0) { + printk(KERN_ERR "XmPciLpEvent.c: open event path failed with rc 0x%x\n", xRc); + } + } + else { + printk(KERN_ERR "XmPciLpEvent.c: register handler failed with rc 0x%x\n", xRc); + } + + return xRc; +} + diff -Nru a/arch/ppc/iSeries/createReleaseData b/arch/ppc/iSeries/createReleaseData --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/createReleaseData Tue Feb 19 18:09:01 2002 @@ -0,0 +1,115 @@ +#!/bin/bash +#################################################### +# Build a hex ebcdic representation of the +# KERNELRELEASE for use in the iSeries +# hvReleaseData structure +#################################################### + +if [ $# -ne 1 ] +then + echo "Syntax: createReleaseData kernelversion" + exit 1 +fi + +len=${#1} + +rd='' +#################################################### +# ReleaseData is maximum of 12 chars +#################################################### +if [ $len -gt 12 ] +then + len=12 +fi + +#for (( i=0 ; $i < $len ; i=$i+1 )) ; +i=0 +while (($i<$len)); +do + char=${1:$i:1} + case $char in + 'a') xchar='0x81';; + 'b') xchar='0x82';; + 'c') xchar='0x83';; + 'd') xchar='0x84';; + 'e') xchar='0x85';; + 'f') xchar='0x86';; + 'g') xchar='0x87';; + 'h') xchar='0x88';; + 'i') xchar='0x89';; + 'j') xchar='0x91';; + 'k') xchar='0x92';; + 'l') xchar='0x93';; + 'm') xchar='0x94';; + 'n') xchar='0x95';; + 'o') xchar='0x96';; + 'p') xchar='0x97';; + 'q') xchar='0x98';; + 'r') xchar='0x99';; + 's') xchar='0xA2';; + 't') xchar='0xA3';; + 'u') xchar='0xA4';; + 'v') xchar='0xA5';; + 'w') xchar='0xA6';; + 'x') xchar='0xA7';; + 'y') xchar='0xA8';; + 'z') xchar='0xA9';; + 'A') xchar='0xC1';; + 'B') xchar='0xC2';; + 'C') xchar='0xC3';; + 'D') xchar='0xC4';; + 'E') xchar='0xC5';; + 'F') xchar='0xC6';; + 'G') xchar='0xC7';; + 'H') xchar='0xC8';; + 'I') xchar='0xC9';; + 'J') xchar='0xD1';; + 'K') xchar='0xD2';; + 'L') xchar='0xD3';; + 'M') xchar='0xD4';; + 'N') xchar='0xD5';; + 'O') xchar='0xD6';; + 'P') xchar='0xD7';; + 'Q') xchar='0xD8';; + 'R') xchar='0xD9';; + 'S') xchar='0xE2';; + 'T') xchar='0xE3';; + 'U') xchar='0xE4';; + 'V') xchar='0xE5';; + 'W') xchar='0xE6';; + 'X') xchar='0xE7';; + 'Y') xchar='0xE8';; + 'Z') xchar='0xE9';; + '0') xchar='0xF0';; + '1') xchar='0xF1';; + '2') xchar='0xF2';; + '3') xchar='0xF3';; + '4') xchar='0xF4';; + '5') xchar='0xF5';; + '6') xchar='0xF6';; + '7') xchar='0xF7';; + '8') xchar='0xF8';; + '9') xchar='0xF9';; + '.') xchar='0x4B';; + '-') xchar='0x60';; + '_') xchar='0x6D';; + '+') xchar='0x4E';; + '@') xchar='0x7C';; + '$') xchar='0x5B';; + '%') xchar='0x6C';; + *) xchar='';; + esac + + rd=${rd}${xchar} + if [ $(($i+1)) -lt $len ] + then + rd=${rd}', ' + fi + i=$i+1 +done + +#echo "#define RELEASEDATA { $rd }">ReleaseData.h + +echo "#define RELEASEDATA { $rd }" + + diff -Nru a/arch/ppc/iSeries/iSeries_FlightRecorder.c b/arch/ppc/iSeries/iSeries_FlightRecorder.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_FlightRecorder.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,89 @@ +/************************************************************************/ +/* File iSeries_FlightRecorder.c created by Al Trautman on Jan 22 2001. */ +/************************************************************************/ +/* This code supports the pci interface on the IBM iSeries systems. */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Jan 22, 2001 */ +/* Added Time Stamps, April 12, 2001 */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include +/************************************************************************/ +/* Log entry into buffer, */ +/* ->If entry is going to wrap, log "WRAP" and start at the top. */ +/************************************************************************/ +void iSeries_LogFr_Entry(FlightRecorder* Fr, char* LogText) { + int Residual, TextLen; + if(Fr->StartingPointer > 0) { /* Initialized yet? */ + Residual = FlightRecorderSize - (Fr->CurrentPointer - Fr->StartingPointer); + TextLen = strlen(LogText); /* Length of Text */ + if(TextLen+16 > Residual) { /* Room for text or need to wrap*/ + strcpy(Fr->CurrentPointer,"WRAP"); + ++Fr->WrapCount; /* Increment Wraps */ + Fr->CurrentPointer = Fr->StartingPointer; + } + strcpy(Fr->CurrentPointer,LogText); + Fr->CurrentPointer += TextLen+1; + strcpy(Fr->CurrentPointer,"<="); + } +} +/************************************************************************/ +/* Log entry with time */ +/************************************************************************/ +void iSeries_LogFr_Time(FlightRecorder* Fr, char* LogText) { + struct rtc_time Rtc; + char LogBuffer[256]; + mf_getRtc(&Rtc); + sprintf(LogBuffer,"%02d:%02d:%02d %s", + Rtc.tm_hour,Rtc.tm_min,Rtc.tm_sec, + LogText); + iSeries_LogFr_Entry(Fr,LogBuffer); +} +/************************************************************************/ +/* Log Entry with Date and call Time Log */ +/************************************************************************/ +void iSeries_LogFr_Date(FlightRecorder* Fr, char* LogText) { + struct rtc_time Rtc; + char LogBuffer[256]; + mf_getRtc(&Rtc); + sprintf(LogBuffer,"%02d.%02d.%02d %02d:%02d:%02d %s", + Rtc.tm_year+1900, Rtc.tm_mon, Rtc.tm_mday, + Rtc.tm_hour,Rtc.tm_min,Rtc.tm_sec, + LogText); + iSeries_LogFr_Entry(Fr,LogBuffer); +} +/************************************************************************/ +/* Initialized the Flight Recorder */ +/************************************************************************/ +void iSeries_Fr_Initialize(FlightRecorder* Fr, char* Signature) { + if(strlen(Signature) > 16) memcpy(Fr->Signature,Signature,16); + else strcpy(Fr->Signature,Signature); + Fr->StartingPointer = &Fr->Buffer[0]; + Fr->CurrentPointer = Fr->StartingPointer; + Fr->logEntry = iSeries_LogFr_Entry; + Fr->logDate = iSeries_LogFr_Date; + Fr->logTime = iSeries_LogFr_Time; + Fr->logEntry(Fr,"FR Initialized."); /* Note, can't use time yet! */ +} diff -Nru a/arch/ppc/iSeries/iSeries_IoMmTable.c b/arch/ppc/iSeries/iSeries_IoMmTable.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_IoMmTable.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,206 @@ +/************************************************************************/ +/* This module supports the iSeries I/O Address translation mapping */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, December 14, 2000 */ +/* Added Bar table for IoMm performance. */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include "iSeries_IoMmTable.h" +#include "iSeries_pci.h" + +void iSeries_allocateDeviceBars(struct pci_dev* PciDevPtr); +/*******************************************************************/ +/* Table defines */ +/* Entry size is 4 MB * 1024 Entries = 4GB. */ +/*******************************************************************/ +#define iSeries_IoMmTable_Entry_Size 0x00400000 +#define iSeries_IoMmTable_Size 1024 +#define iSeries_Base_Io_Memory 0xFFFFFFFF + +/*******************************************************************/ +/* Static and Global variables */ +/*******************************************************************/ +struct pci_dev* iSeries_IoMmTable[iSeries_IoMmTable_Size]; +u8 iSeries_IoBarTable[iSeries_IoMmTable_Size]; +static int iSeries_CurrentIndex; +static char* iSeriesPciIoText = "iSeries PCI I/O"; +static spinlock_t iSeriesIoMmTableLock = SPIN_LOCK_UNLOCKED; +/*******************************************************************/ +/* iSeries_IoMmTable_Initialize */ +/*******************************************************************/ +/* - Initalizes the Address Translation Table and get it ready for */ +/* use. Must be called before any client calls any of the other */ +/* methods. */ +/*******************************************************************/ +void iSeries_IoMmTable_Initialize(void) { + int Index; + spin_lock(&iSeriesIoMmTableLock); + for(Index=0;Index 0) { + printk("PCI: iSeries_allocateDeviceBars 0x%08X\n",(int)PciDevPtr); + sprintf(PciFrBuffer,"IoBars %08X",(int)PciDevPtr); + ISERIES_PCI_FR(PciFrBuffer); + } + for(BarNumber = 0; BarNumber <= PCI_ROM_RESOURCE; ++BarNumber) { + BarResource = &PciDevPtr->resource[BarNumber]; + iSeries_IoMmTable_AllocateEntry(PciDevPtr, BarNumber); + } +} + +/*******************************************************************/ +/* iSeries_IoMmTable_AllocateEntry */ +/*******************************************************************/ +/* Adds pci_dev entry in address translation table */ +/*******************************************************************/ +/* - Allocates the number of entries required in table base on BAR */ +/* size. */ +/* - This version, allocates top down, starting at 4GB. */ +/* - The size is round up to be a multiple of entry size. */ +/* - CurrentIndex is decremented to keep track of the last entry. */ +/* - Builds the resource entry for allocated BARs. */ +/*******************************************************************/ +void iSeries_IoMmTable_AllocateEntry(struct pci_dev* PciDevPtr, u32 BarNumber) { + struct resource* BarResource = &PciDevPtr->resource[BarNumber]; + int BarSize = BarResource->end - BarResource->start; + u32 BarStartAddr; + u32 BarEndAddr; + /***************************************************************/ + /* No space to allocate, skip Allocation. */ + /***************************************************************/ + if(BarSize == 0) return; /* Quick stage exit */ + + /***************************************************************/ + /* Allocate the table entries needed. */ + /***************************************************************/ + spin_lock(&iSeriesIoMmTableLock); + while(BarSize > 0) { + iSeries_IoMmTable[iSeries_CurrentIndex] = PciDevPtr; + iSeries_IoBarTable[iSeries_CurrentIndex] = BarNumber; + BarSize -= iSeries_IoMmTable_Entry_Size; + --iSeries_CurrentIndex; /* Next Free entry */ + } + spin_unlock(&iSeriesIoMmTableLock); + BarStartAddr = iSeries_IoMmTable_Entry_Size*(iSeries_CurrentIndex+1); + BarEndAddr = BarStartAddr + (u32)(BarResource->end - BarResource->start); + /***************************************************************/ + /* Build Resource info */ + /***************************************************************/ + BarResource->name = iSeriesPciIoText; + BarResource->start = (long)BarStartAddr; + BarResource->end = (long)BarEndAddr; + + /***************************************************************/ + /* Tracing */ + /***************************************************************/ + if(PciTraceFlag > 0) { + printk("PCI: BarAloc %04X-%08X-%08X\n",iSeries_CurrentIndex+1,(int)BarStartAddr, (int)BarEndAddr); + sprintf(PciFrBuffer,"IoMmAloc %04X-%08X-%08X", + iSeries_CurrentIndex+1,(int)BarStartAddr, (int)BarEndAddr); + ISERIES_PCI_FR(PciFrBuffer); + } +} +/*******************************************************************/ +/* Translates an I/O Memory address to pci_dev* */ +/*******************************************************************/ +struct pci_dev* iSeries_xlateIoMmAddress(u32* IoAddress) { + int PciDevIndex; + struct pci_dev* PciDevPtr; + PciDevIndex = (u32)IoAddress/iSeries_IoMmTable_Entry_Size; + PciDevPtr = iSeries_IoMmTable[PciDevIndex]; + if(PciDevPtr == 0) { + printk("PCI: Invalid I/O Address: 0x%08X\n",(int)IoAddress); + sprintf(PciFrBuffer,"Invalid MMIO Address 0x%08X",(int)IoAddress); + ISERIES_PCI_FR(PciFrBuffer); + } + return PciDevPtr; +} +/************************************************************************/ +/* Returns the Bar number of Address */ +/************************************************************************/ +int iSeries_IoMmTable_Bar(u32 *IoAddress) { + int BarIndex = (u32)IoAddress/iSeries_IoMmTable_Entry_Size; + int BarNumber = iSeries_IoBarTable[BarIndex]; + return BarNumber; +} +/************************************************************************/ +/* Return the Bar Base Address or 0. */ +/************************************************************************/ +u32* iSeries_IoMmTable_BarBase(u32 *IoAddress) { + u32 BaseAddr = -1; + pciDev* PciDev = iSeries_xlateIoMmAddress(IoAddress); + if(PciDev != 0) { + int BarNumber = iSeries_IoMmTable_Bar(IoAddress); + if(BarNumber != -1) { + BaseAddr = (&PciDev->resource[BarNumber])->start; + } + } + return (u32*)BaseAddr; +} +/************************************************************************/ +/* Return the Bar offset within the Bar Space */ +/* Note: Assumes that address is valid. */ +/************************************************************************/ +u32 iSeries_IoMmTable_BarOffset(u32* IoAddress) { + u32 BaseAddr = (u32)iSeries_IoMmTable_BarBase(IoAddress); + return (u32)IoAddress-BaseAddr; +} +/************************************************************************/ +/* Return 0 if Address is valid I/O Address */ +/************************************************************************/ +int iSeries_Is_IoMmAddress(unsigned long IoAddress) { + if( iSeries_IoMmTable_Bar((u32*)IoAddress) == -1) return 1; + else return 0; +} +/************************************************************************/ +/* Helper Methods to get TableSize and TableSizeEntry. */ +/************************************************************************/ +u32 iSeries_IoMmTable_TableEntrySize(void) { return iSeries_IoMmTable_Entry_Size; } +u32 iSeries_IoMmTable_TableSize(void) { return iSeries_IoMmTable_Size; } + + diff -Nru a/arch/ppc/iSeries/iSeries_IoMmTable.h b/arch/ppc/iSeries/iSeries_IoMmTable.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_IoMmTable.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,105 @@ +#ifndef _ISERIES_IOMMTABLE_H +#define _ISERIES_IOMMTABLE_H +/************************************************************************/ +/* File iSeries_IoMmTable.h created by Allan Trautman on Dec 12 2001. */ +/************************************************************************/ +/* Interfaces for the write/read Io address translation table. */ +/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created December 12, 2000 */ +/* End Change Activity */ +/************************************************************************/ + +/************************************************************************/ +/* iSeries_IoMmTable_Initialize */ +/************************************************************************/ +/* - Initalizes the Address Translation Table and get it ready for use. */ +/* Must be called before any client calls any of the other methods. */ +/* */ +/* Parameters: None. */ +/* */ +/* Return: None. */ +/************************************************************************/ +extern void iSeries_IoMmTable_Initialize(void); + +/************************************************************************/ +/* iSeries_allocateDeviceBars */ +/************************************************************************/ +/* - Allocates ALL pci_dev BAR's and updates the resources with the BAR */ +/* value. BARS with zero length will not have the resources. The */ +/* HvCallPci_getBarParms is used to get the size of the BAR space. */ +/* It calls as400_IoMmTable_AllocateEntry to allocate each entry. */ +/* */ +/* Parameters: */ +/* pci_dev = Pointer to pci_dev structure that will be mapped to pseudo */ +/* I/O Address. */ +/* */ +/* Return: */ +/* The pci_dev I/O resources updated with pseudo I/O Addresses. */ +/************************************************************************/ +extern void iSeries_allocateDeviceBars(struct pci_dev* Device); + +/************************************************************************/ +/* iSeries_IoMmTable_AllocateEntry */ +/************************************************************************/ +/* - Allocates(adds) the pci_dev entry in the Address Translation Table */ +/* and updates the Resources for the device. */ +/* */ +/* Parameters: */ +/* pci_dev = Pointer to pci_dev structure that will be mapped to pseudo */ +/* I/O Address. */ +/* */ +/* BarNumber = Which Bar to be allocated. */ +/* */ +/* Return: */ +/* The pseudo I/O Address in the resources that will map to the */ +/* pci_dev on iSeries_xlateIoMmAddress call. */ +/************************************************************************/ +extern void iSeries_IoMmTable_AllocateEntry(struct pci_dev* Device, u32 BarNumber); + +/************************************************************************/ +/* iSeries_xlateIoMmAddress */ +/************************************************************************/ +/* - Translates an I/O Memory address to pci_dev that has been allocated*/ +/* the psuedo I/O Address. */ +/* */ +/* Parameters: */ +/* IoAddress = I/O Memory Address. */ +/* */ +/* Return: */ +/* A pci_dev pointer to the device mapped to the I/O address. */ +/************************************************************************/ +extern struct pci_dev* iSeries_xlateIoMmAddress(u32* IoAddress); + +/************************************************************************/ +/* Helper Methods */ +/************************************************************************/ +extern int iSeries_IoMmTable_Bar(u32 *IoAddress); +extern u32* iSeries_IoMmTable_BarBase(u32* IoAddress); +extern u32 iSeries_IoMmTable_BarOffset(u32* IoAddress); +extern int iSeries_Is_IoMmAddress(unsigned long address); + +/************************************************************************/ +/* Helper Methods to get TableSize and TableSizeEntry. */ +/************************************************************************/ +extern u32 iSeries_IoMmTable_TableEntrySize(void); +extern u32 iSeries_IoMmTable_TableSize(void); + +#endif /* _ISERIES_IOMMTABLE_H */ diff -Nru a/arch/ppc/iSeries/iSeries_VpdInfo.c b/arch/ppc/iSeries/iSeries_VpdInfo.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_VpdInfo.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,339 @@ +/************************************************************************/ +/* File iSeries_vpdInfo.c created by Allan Trautman on Fri Feb 2 2001. */ +/************************************************************************/ +/* This code gets the card location of the hardware */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Feb 2, 2001 */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "iSeries_pci.h" + +/************************************************/ +/* Size of Bus VPD data */ +/************************************************/ +#define BUS_VPDSIZE 1024 +/************************************************/ +/* Bus Vpd Tags */ +/************************************************/ +#define VpdEndOfDataTag 0x78 +#define VpdEndOfAreaTag 0x79 +#define VpdIdStringTag 0x82 +#define VpdVendorAreaTag 0x84 +/************************************************/ +/* Mfg Area Tags */ +/************************************************/ +#define VpdAsmPartNumber 0x504E // "PN" +#define VpdFruFlag 0x4647 // "FG" +#define VpdFruLocation 0x464C // "FL" +#define VpdFruFrameId 0x4649 // "FI" +#define VpdFruPartNumber 0x464E // "FN" +#define VpdFruPowerData 0x5052 // "PR" +#define VpdFruSerial 0x534E // "SN" +#define VpdFruType 0x4343 // "CC" +#define VpdFruCcinExt 0x4345 // "CE" +#define VpdFruRevision 0x5256 // "RV" +#define VpdSlotMapFormat 0x4D46 // "MF" +#define VpdSlotMap 0x534D // "SM" + +/************************************************/ +/* Structures of the areas */ +/************************************************/ +struct BusVpdAreaStruct { + u8 Tag; + u8 LowLength; + u8 HighLength; +}; +typedef struct BusVpdAreaStruct BusVpdArea; +#define BUS_ENTRY_SIZE 3 + +struct MfgVpdAreaStruct { + u16 Tag; + u8 TagLength; +}; +typedef struct MfgVpdAreaStruct MfgVpdArea; +#define MFG_ENTRY_SIZE 3 + +struct SlotMapFormatStruct { + u16 Tag; + u8 TagLength; + u16 Format; +}; + +struct FrameIdMapStruct{ + u16 Tag; + u8 TagLength; + u8 FrameId; +}; +typedef struct FrameIdMapStruct FrameIdMap; + +struct SlotMapStruct { + u8 AgentId; + u8 SecondaryAgentId; + u8 PhbId; + char CardLocation[3]; + char Parms[8]; + char Reserved[2]; +}; +typedef struct SlotMapStruct SlotMap; +#define SLOT_ENTRY_SIZE 16 + +/****************************************************************/ +/* Prototypes */ +/****************************************************************/ +static void iSeries_Parse_Vpd(BusVpdArea*, int, LocationData*); +static void iSeries_Parse_MfgArea(MfgVpdArea*,int, LocationData*); +static void iSeries_Parse_SlotArea(SlotMap*, int, LocationData*); +static void iSeries_Parse_PhbId(BusVpdArea*, int, LocationData*); + +/****************************************************************/ +/* */ +/* */ +/* */ +/****************************************************************/ +LocationData* iSeries_GetLocationData(struct pci_dev* PciDev) { + LocationData* LocationPtr = 0; + BusVpdArea* BusVpdPtr = 0; + int BusVpdLen = 0; + BusVpdPtr = (BusVpdArea*)kmalloc(BUS_VPDSIZE, GFP_KERNEL); + BusVpdLen = HvCallPci_getBusVpd(ISERIES_GET_LPAR_BUS(PciDev->bus->number),REALADDR(BusVpdPtr),BUS_VPDSIZE); + /* printk("PCI: VpdBuffer 0x%08X \n",(int)BusVpdPtr); */ + /***************************************************************/ + /* Need to set Agent Id, Bus in location info before the call */ + /***************************************************************/ + LocationPtr = (LocationData*)kmalloc(LOCATION_DATA_SIZE, GFP_KERNEL); + LocationPtr->Bus = ISERIES_GET_LPAR_BUS(PciDev->bus->number); + LocationPtr->Board = 0; + LocationPtr->FrameId = 0; + iSeries_Parse_PhbId(BusVpdPtr,BusVpdLen,LocationPtr); + LocationPtr->Card = PCI_SLOT(PciDev->devfn); + strcpy(LocationPtr->CardLocation,"Cxx"); + LocationPtr->AgentId = ISERIES_DECODE_DEVICE(PciDev->devfn); + LocationPtr->SecondaryAgentId = 0x10; + /* And for Reference only. */ + LocationPtr->LinuxBus = PciDev->bus->number; + LocationPtr->LinuxDevFn = PciDev->devfn; + /***************************************************************/ + /* Any data to process? */ + /***************************************************************/ + if(BusVpdLen > 0) { + iSeries_Parse_Vpd(BusVpdPtr, BUS_VPDSIZE, LocationPtr); + } + else { + ISERIES_PCI_FR("No VPD Data...."); + } + kfree(BusVpdPtr); + + return LocationPtr; +} +/****************************************************************/ +/* */ +/****************************************************************/ +void iSeries_Parse_Vpd(BusVpdArea* VpdData,int VpdLen, LocationData* LocationPtr) { + MfgVpdArea* MfgVpdPtr = 0; + int BusTagLen = 0; + BusVpdArea* BusVpdPtr = VpdData; + int BusVpdLen = VpdLen; + /*************************************************************/ + /* Make sure this is what I think it is */ + /*************************************************************/ + if(BusVpdPtr->Tag != VpdIdStringTag) { /*0x82 */ + ISERIES_PCI_FR("Not 0x82 start."); + return; + } + BusTagLen = (BusVpdPtr->HighLength*256)+BusVpdPtr->LowLength; + BusTagLen += BUS_ENTRY_SIZE; + BusVpdPtr = (BusVpdArea*)((u32)BusVpdPtr+BusTagLen); + BusVpdLen -= BusTagLen; + /*************************************************************/ + /* Parse the Data */ + /*************************************************************/ + while(BusVpdLen > 0 ) { + BusTagLen = (BusVpdPtr->HighLength*256)+BusVpdPtr->LowLength; + /*********************************************************/ + /* End of data Found */ + /*********************************************************/ + if(BusVpdPtr->Tag == VpdEndOfAreaTag) { + BusVpdLen = 0; /* Done, Make sure */ + } + /*********************************************************/ + /* Was Mfg Data Found */ + /*********************************************************/ + else if(BusVpdPtr->Tag == VpdVendorAreaTag) { + MfgVpdPtr = (MfgVpdArea*)((u32)BusVpdPtr+BUS_ENTRY_SIZE); + iSeries_Parse_MfgArea(MfgVpdPtr,BusTagLen,LocationPtr); + } + /********************************************************/ + /* On to the next tag. */ + /********************************************************/ + if(BusVpdLen > 0) { + BusTagLen += BUS_ENTRY_SIZE; + BusVpdPtr = (BusVpdArea*)((u32)BusVpdPtr+BusTagLen); + BusVpdLen -= BusTagLen; + } + } +} + +/*****************************************************************/ +/* Parse the Mfg Area */ +/*****************************************************************/ +void iSeries_Parse_MfgArea(MfgVpdArea* VpdDataPtr,int VpdLen, LocationData* LocationPtr) { + SlotMap* SlotMapPtr = 0; + u16 SlotMapFmt = 0; + int MfgTagLen = 0; + MfgVpdArea* MfgVpdPtr = VpdDataPtr; + int MfgVpdLen = VpdLen; + + /*************************************************************/ + /* Parse Mfg Data */ + /*************************************************************/ + while(MfgVpdLen > 0) { + MfgTagLen = MfgVpdPtr->TagLength; + if (MfgVpdPtr->Tag == VpdFruFlag) {} /* FG */ + else if(MfgVpdPtr->Tag == VpdFruSerial) {} /* SN */ + else if(MfgVpdPtr->Tag == VpdAsmPartNumber){} /* PN */ + /*********************************************************/ + /* Frame ID */ + /*********************************************************/ + if(MfgVpdPtr->Tag == VpdFruFrameId) { /* FI */ + LocationPtr->FrameId = ((FrameIdMap*)MfgVpdPtr)->FrameId; + } + /*********************************************************/ + /* Slot Map Format */ + /*********************************************************/ + else if(MfgVpdPtr->Tag == VpdSlotMapFormat){ /* MF */ + SlotMapFmt = ((struct SlotMapFormatStruct*)MfgVpdPtr)->Format; + } + /*********************************************************/ + /* Slot Labels */ + /*********************************************************/ + else if(MfgVpdPtr->Tag == VpdSlotMap){ /* SM */ + if(SlotMapFmt == 0x1004) SlotMapPtr = (SlotMap*)((u32)MfgVpdPtr+MFG_ENTRY_SIZE+1); + else SlotMapPtr = (SlotMap*)((u32)MfgVpdPtr+MFG_ENTRY_SIZE); + iSeries_Parse_SlotArea(SlotMapPtr,MfgTagLen, LocationPtr); + } + /*********************************************************/ + /* Point to the next Mfg Area */ + /* Use defined size, sizeof give wrong answer */ + /*********************************************************/ + MfgTagLen += MFG_ENTRY_SIZE; + MfgVpdPtr = (MfgVpdArea*)( (u32)MfgVpdPtr + MfgTagLen); + MfgVpdLen -= MfgTagLen; + } +} +/*****************************************************************/ +/* Look for "BUS" Tag to set the PhbId. */ +/*****************************************************************/ +void iSeries_Parse_PhbId(BusVpdArea* VpdData,int VpdLen,LocationData* LocationPtr) { + int PhbId = 0xff; /* Not found flag */ + char* PhbPtr = (char*)VpdData+3; /* Skip over 82 tag */ + int DataLen = VpdLen; + while(DataLen > 0) { + if(*PhbPtr == 'B' && *(PhbPtr+1) == 'U' && *(PhbPtr+2) == 'S') { + if(*(PhbPtr+3) == ' ') PhbPtr += 4;/* Skip white spac*/ + else PhbPtr += 3; + if (*PhbPtr == '0') PhbId = 0; /* Don't convert, */ + else if(*PhbPtr == '1') PhbId = 1; /* Sanity check */ + else if(*PhbPtr == '2') PhbId = 2; /* values */ + DataLen = 0; /* Exit loop. */ + } + ++PhbPtr; + --DataLen; + } + LocationPtr->PhbId = PhbId; +} +/*****************************************************************/ +/* Parse the Slot Area */ +/*****************************************************************/ +void iSeries_Parse_SlotArea(SlotMap* MapPtr,int MapLen, LocationData* LocationPtr) { + int SlotMapLen = MapLen; + SlotMap* SlotMapPtr = MapPtr; + /*************************************************************/ + /* Parse Slot label until we find the one requrested */ + /*************************************************************/ + while(SlotMapLen > 0) { + if(SlotMapPtr->AgentId == LocationPtr->AgentId && + SlotMapPtr->SecondaryAgentId == LocationPtr->SecondaryAgentId) { + /*****************************************************/ + /* If Phb wasn't found, grab the first one found. */ + /*****************************************************/ + if(LocationPtr->PhbId == 0xff) LocationPtr->PhbId = SlotMapPtr->PhbId; + if( SlotMapPtr->PhbId == LocationPtr->PhbId ) { + /*****************************************************/ + /* Found what we were looking for, extract the data. */ + /*****************************************************/ + memcpy(&LocationPtr->CardLocation,&SlotMapPtr->CardLocation,3); + LocationPtr->CardLocation[3] = 0; /* Null terminate*/ + SlotMapLen = 0; /* We are done */ + } + } + /*********************************************************/ + /* Point to the next Slot */ + /* Use defined size, sizeof may give wrong answer */ + /*********************************************************/ + SlotMapLen -= SLOT_ENTRY_SIZE; + SlotMapPtr = (SlotMap*)((u32)SlotMapPtr+SLOT_ENTRY_SIZE); + } +} +/************************************************************************/ +/* Formats the device information. */ +/* - Pass in pci_dev* pointer to the device. */ +/* - Pass in buffer to place the data. Danger here is the buffer must */ +/* be as big as the client says it is. Should be at least 128 bytes.*/ +/* Return will the length of the string data put in the buffer. */ +/* Format: */ +/* PCI: Bus 0, Device 26, Vendor 0x12AE iSeries: Bus 2, Device 34, Fr*/ +/* ame 1, Card C10 Ethernet controller */ +/************************************************************************/ +int iSeries_Device_Information(struct pci_dev* PciDev,char* Buffer, int BufferSize) { + LocationData* LocationPtr; /* VPD Information */ + char* BufPtr = Buffer; + int LineLen = 0; + if(BufferSize >= 128) { + LineLen = sprintf(BufPtr+LineLen,"PCI: Bus%3d, Device%3d, Vendor %04X ", + PciDev->bus->number, PCI_SLOT(PciDev->devfn),PciDev->vendor); + + LocationPtr = iSeries_GetLocationData(PciDev); + LineLen += sprintf(BufPtr+LineLen,"iSeries: Bus%3d, Device%3d, Frame%3d, Card %4s ", + LocationPtr->Bus,LocationPtr->AgentId, + LocationPtr->FrameId,LocationPtr->CardLocation); + kfree(LocationPtr); + + if(pci_class_name(PciDev->class >> 8) == 0) { + LineLen += sprintf(BufPtr+LineLen,"0x%04X ",(int)(PciDev->class >> 8)); + } + else { + LineLen += sprintf(BufPtr+LineLen,"%s",pci_class_name(PciDev->class >> 8) ); + } + } + return LineLen; +} diff -Nru a/arch/ppc/iSeries/iSeries_fixup.c b/arch/ppc/iSeries/iSeries_fixup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_fixup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,255 @@ +/************************************************************************/ +/* This module supports the iSeries PCI bus device detection */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, December 13, 2000 by Wayne Holm */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iSeries_IoMmTable.h" +#include "iSeries_pci.h" + + + +unsigned int __init iSeries_scan_slot(struct pci_dev* temp_dev, + u16 hvBus, u8 slotSubBus, u8 maxAgents) +{ + u8 hvIdSel, hvFunction, hvAgentId; + u64 hvRc = 0; + u64 noConnectRc = 0xFFFF; + HvAgentId slotAgentId; + int irq; + + slotAgentId = ISERIES_PCI_AGENTID(ISERIES_GET_DEVICE_FROM_SUBBUS(slotSubBus), ISERIES_GET_FUNCTION_FROM_SUBBUS(slotSubBus)); + irq = iSeries_allocate_IRQ(hvBus, 0, slotAgentId); + for (hvIdSel = 1; hvIdSel <= maxAgents; ++hvIdSel) { + /* Connect all functions of any device found. However, only call pci_scan_slot + once for each idsel. pci_scan_slot handles multifunction devices appropriately */ + for (hvFunction = 0; hvFunction < 8 && hvRc == 0; ++hvFunction) { + hvAgentId = ISERIES_PCI_AGENTID(hvIdSel, hvFunction); + hvRc = HvCallXm_connectBusUnit(hvBus, slotSubBus, hvAgentId, irq); + if (hvRc == 0) { + noConnectRc = 0; + HvCallPci_configStore8(hvBus, slotSubBus, hvAgentId, PCI_INTERRUPT_LINE, irq); // Store the irq in the interrupt line register of the function config space + } + } + if (noConnectRc == 0) { + /* This assumes that the node slot is always on the primary bus! */ + temp_dev->devfn = ISERIES_ENCODE_SUBBUS(ISERIES_GET_DEVICE_FROM_SUBBUS(slotSubBus), + ISERIES_GET_FUNCTION_FROM_SUBBUS(slotSubBus), + 0); + iSeries_assign_IRQ(irq, hvBus, 0, slotAgentId); + pci_scan_slot(temp_dev); + } + } + return 0; +} + +void __init iSeries_fixup_bus(struct pci_bus* bus) +{ + struct pci_dev temp_dev; + struct pci_controller* hose; + u16 hvBus; + u8 hvSubBus, hvIdSel, hvFunction, hvAgentId, maxAgents, irq; + u64 hvRc, devInfoRealAdd, bridgeInfoRealAdd; + struct HvCallPci_DeviceInfo* devInfo; + struct HvCallPci_BridgeInfo* bridgeInfo; + u64 noConnectRc = 0xFFFF; + + hose = (struct pci_controller*)(bus->sysdata); + /* Get the hv bus number from the hose arch_data */ + hvBus = ISERIES_GET_HOSE_HV_BUSNUM(hose); + /* Initialize the global bus number map for this bus */ + iSeries_GlobalBusMap[bus->number][_HVBUSNUMBER_] = hvBus; + iSeries_GlobalBusMap[bus->number][_HVSUBBUSNUMBER_] = 0; + maxAgents = 7; + /* If not a primary bus, set the hypervisor subBus number of this bus into the map */ + if (bus->parent != NULL) { + bridgeInfo = kmalloc(sizeof(*bridgeInfo), GFP_KERNEL); + /* Perform linux virtual address to iSeries PLIC real address translation */ + bridgeInfoRealAdd = virt_to_absolute((u32)bridgeInfo); + bridgeInfoRealAdd = bridgeInfoRealAdd | 0x8000000000000000; + /* Find the Hypervisor address of the bridge which created this bus... */ + if (ISERIES_IS_SUBBUS_ENCODED_IN_DEVFN(bus->self->devfn)) { // This assumes that the bus passed to this function is connected to an EADS slot. The subbus encoding algorithm only applies to bridge cards attached directly to EADS. Future TODO is to allow for n levels of bridging. + hvSubBus = ISERIES_DEVFN_DECODE_SUBBUS(bus->self->devfn); + hvIdSel = 1; + } + else { + /* The hose arch_data needs to map Linux bus number -> PHB bus/subBus number + Could also use a global table, might be cheaper for multiple PHBs */ + // hvSubBus = iSeries_GlobalBusMap[bus->parent->number][_HVSUBBUSNUMBER_]; + hvSubBus = 0; // The bridge card devfn is not subbus encoded and is directly attached to the PCI primary bus. Its subbus number is 0 and the card config space is accessed via type 0 config cycles. + hvIdSel = PCI_SLOT(bus->self->devfn); + } + hvAgentId = ISERIES_PCI_AGENTID(hvIdSel, PCI_FUNC(bus->self->devfn)); + /* Now we know the HV bus/subbus/agent of the bridge creating this bus, + go get the subbus number from HV */ + hvRc = HvCallPci_getBusUnitInfo(hvBus, hvSubBus, hvAgentId, + bridgeInfoRealAdd, sizeof(*bridgeInfo)); + if (hvRc != 0 || bridgeInfo->busUnitInfo.deviceType != HvCallPci_BridgeDevice) { + kfree(bridgeInfo); + // return -1; + } + iSeries_GlobalBusMap[bus->number][_HVSUBBUSNUMBER_] = bridgeInfo->subBusNumber; + maxAgents = bridgeInfo->maxAgents; + kfree(bridgeInfo); + } + /* Bus number mapping is complete, from here on cfgIos should result in HvCalls */ + + hvSubBus = iSeries_GlobalBusMap[bus->number][_HVSUBBUSNUMBER_]; + + devInfo = kmalloc(sizeof(*devInfo), GFP_KERNEL); + devInfoRealAdd = virt_to_absolute((u32)devInfo); + devInfoRealAdd = devInfoRealAdd | 0x8000000000000000; + memset(&temp_dev, 0, sizeof(temp_dev)); + temp_dev.bus = bus; + temp_dev.sysdata = bus->sysdata; + + for (hvIdSel=1; hvIdSel <= maxAgents; ++hvIdSel) { + hvRc = HvCallPci_getDeviceInfo(hvBus, hvSubBus, hvIdSel, + devInfoRealAdd, sizeof(*devInfo)); + if (hvRc == 0) { + switch(devInfo->deviceType) { + case HvCallPci_NodeDevice: + /* bridgeInfo = kmalloc(HvCallPci_MaxBusUnitInfoSize, GFP_KERNEL); */ + bridgeInfo = kmalloc(sizeof(*bridgeInfo), GFP_KERNEL); + /* Loop through each node function to find usable bridges. Scan + the node bridge to create devices. The devices will appear + as if they were connected to the primary bus. */ + for (hvFunction=0; hvFunction < 8; ++hvFunction) { + hvAgentId = ISERIES_PCI_AGENTID(hvIdSel, hvFunction); + irq = 0; + /* Note: hvSubBus should always be 0 here! */ + hvRc = HvCallXm_connectBusUnit(hvBus, hvSubBus, hvAgentId, irq); + if (hvRc == 0) { + bridgeInfoRealAdd = virt_to_absolute((u32)bridgeInfo); + bridgeInfoRealAdd = bridgeInfoRealAdd | 0x8000000000000000; + hvRc = HvCallPci_getBusUnitInfo(hvBus, hvSubBus, hvAgentId, + bridgeInfoRealAdd, sizeof(*bridgeInfo)); + if (hvRc == 0 && bridgeInfo->busUnitInfo.deviceType == HvCallPci_BridgeDevice) + { + // scan any card plugged into this slot + iSeries_scan_slot(&temp_dev, hvBus, bridgeInfo->subBusNumber, + bridgeInfo->maxAgents); + } + } + } + kfree(bridgeInfo); + break; + + case HvCallPci_MultiFunctionDevice: + /* Attempt to connect each device function, then use architecture independent + pci_scan_slot to build the device(s) */ + irq = bus->self->irq; // Assume that this multi-function device is attached to a secondary bus. Get the irq from the dev struct for the bus and pass to Hv on the function connects as well as write it into the interrupt line registers of each function + for (hvFunction=0; hvFunction < 8 && hvRc == 0; ++hvFunction) { + hvAgentId = ISERIES_PCI_AGENTID(hvIdSel, hvFunction); + /* Try to connect each function. */ + hvRc = HvCallXm_connectBusUnit(hvBus, hvSubBus, hvAgentId, irq); + if (hvRc == 0) { + noConnectRc = 0; + HvCallPci_configStore8(hvBus, hvSubBus, hvAgentId, PCI_INTERRUPT_LINE, irq); // Store the irq in the interrupt line register of the function config space + } + } + if (noConnectRc == 0) { + noConnectRc = 0xFFFF; // Reset to error value in case other multi-function devices are attached to this bus + // Note: using hvIdSel assumes this device is on a secondary bus! + temp_dev.devfn = PCI_DEVFN(hvIdSel, 0); + pci_scan_slot(&temp_dev); + } + break; + + case HvCallPci_BridgeDevice: + case HvCallPci_IoaDevice: + /* Single function devices, just try to connect and use pci_scan_slot to + build the device */ + irq = bus->self->irq; + hvAgentId = ISERIES_PCI_AGENTID(hvIdSel, 0); + hvRc = HvCallXm_connectBusUnit(hvBus, hvSubBus, hvAgentId, irq); + if (hvRc == 0) { + HvCallPci_configStore8(hvBus, hvSubBus, hvAgentId, PCI_INTERRUPT_LINE, irq); // Store the irq in the interrupt line register of the device config space + // Note: using hvIdSel assumes this device is on a secondary bus! + temp_dev.devfn = PCI_DEVFN(hvIdSel, 0); + pci_scan_slot(&temp_dev); + } + break; + + default : /* Unrecognized device */ + break; + + }; /* end of switch */ + } + } + + kfree(devInfo); + // return 0; +} +/* Initialize bar space base and limit addresses for each device in the tree */ +void __init iSeries_fixup( void ) { + struct pci_dev *dev; + u8 LinuxBus, iSeriesBus, LastBusNumber; + char DeviceInfoBuffer[256]; + + /*********************************************************/ + /* PCI: Allocate Bars space for each device */ + /*********************************************************/ + pci_for_each_dev(dev) { + iSeries_allocateDeviceBars(dev); + } + /*********************************************************/ + /* Create the TCEs for each iSeries bus now that we know */ + /* how many buses there are. Need only create TCE for */ + /* for each iSeries bus. Multiple linux buses could */ + /* be on the same iSeries bus. AHT */ + /*********************************************************/ + LastBusNumber = 0xFF; /* Invalid */ + for( LinuxBus = 0; LinuxBus < 255; ++ LinuxBus) { + iSeriesBus = ISERIES_GET_LPAR_BUS(LinuxBus); + if(iSeriesBus == 0xFF) break; /* Done */ + else if(LastBusNumber != iSeriesBus ){ /* New Bus */ + create_pci_bus_tce_table(iSeriesBus); + LastBusNumber = iSeriesBus; /* Remember */ + } + } + /*********************************************************/ + /* List out all the PCI devices found... This will go */ + /* into the etc/proc/iSeries/pci info as well.... */ + /* This is to help service figure out who is who...... */ + /*********************************************************/ + pci_for_each_dev(dev) { + struct resource* BarResource = &dev->resource[0]; + iSeries_Device_Information(dev,DeviceInfoBuffer,256); + printk("%s\n",DeviceInfoBuffer); + printk("PCI: Bus%3d, Device%3d, %s at 0x%08x, irq %3d\n", + dev->bus->number, PCI_SLOT(dev->devfn),dev->name,(int)BarResource->start,dev->irq); + } + /* */ + iSeries_activate_IRQs(); // Unmask all device interrupts for assigned IRQs +} + diff -Nru a/arch/ppc/iSeries/iSeries_irq.c b/arch/ppc/iSeries/iSeries_irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_irq.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,256 @@ +/************************************************************************/ +/* This module supports the iSeries PCI bus interrupt handling */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, December 13, 2000 by Wayne Holm */ +/* End Change Activity */ +/************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +hw_irq_controller iSeries_IRQ_handler = { + "iSeries irq controller", + iSeries_startup_IRQ, /* startup */ + iSeries_shutdown_IRQ, /* shutdown */ + iSeries_enable_IRQ, /* enable */ + iSeries_disable_IRQ, /* disable */ + NULL, /* ack */ + iSeries_end_IRQ, /* end */ + NULL /* set_affinity */ +}; + + +struct iSeries_irqEntry { + u32 dsa; + struct iSeries_irqEntry* next; +}; + +struct iSeries_irqAnchor { + u8 valid : 1; + u8 reserved : 7; + u16 entryCount; + struct iSeries_irqEntry* head; +}; + +struct iSeries_irqAnchor iSeries_irqMap[NR_IRQS]; + +void iSeries_init_irqMap(int irq); + +/* This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c */ +void __init iSeries_init_IRQ(void) +{ + + int i; + + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].handler = &iSeries_IRQ_handler; + irq_desc[i].status = 0; + irq_desc[i].status |= IRQ_DISABLED; + irq_desc[i].depth = 1; + iSeries_init_irqMap(i); + } + + /* Register PCI event handler and open an event path */ + XmPciLpEvent_init(); + + return; +} + +/* Called by iSeries_init_IRQ */ +void __init iSeries_init_irqMap(int irq) { + /* Prevent IRQs 0 and 255 from being used. IRQ 0 appears in + uninitialized devices. IRQ 255 appears in the PCI interrupt + line register if a PCI error occurs */ + iSeries_irqMap[irq].valid = (irq == 0 || irq == 255)? 0 : 1; + iSeries_irqMap[irq].entryCount = 0; + iSeries_irqMap[irq].head = NULL; +} + +/* This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot */ +int __init iSeries_allocate_IRQ(HvBusNumber busNumber, HvSubBusNumber subBusNumber, HvAgentId deviceId) { + + u8 idsel = (deviceId >> 4); + u8 function = deviceId & 0x0F; + int irq = ((((busNumber-1)*16 + (idsel-1)*8 + function)*9/8) % 254) + 1; + return irq; +} + +/* This is called out of iSeries_scan_slot to assign the EADS slot to its IRQ number */ +int __init iSeries_assign_IRQ(int irq, HvBusNumber busNumber, HvSubBusNumber subBusNumber, HvAgentId deviceId) { + + int rc; + u32 dsa = (busNumber << 16) | (subBusNumber << 8) | deviceId; + struct iSeries_irqEntry* newEntry; + unsigned long flags; + + if (irq < 0 || irq >= NR_IRQS) + return -1; + + newEntry = kmalloc(sizeof(*newEntry), GFP_KERNEL); + if (newEntry == NULL) + return -ENOMEM; + newEntry->dsa = dsa; + newEntry->next = NULL; + + /* Probably not necessary to lock the irq since allocation is only + done during buswalk, but it should not hurt anything except a little + performance */ + spin_lock_irqsave(&irq_desc[irq].lock, flags); + + if (iSeries_irqMap[irq].valid) { + /* Push the new element onto the irq stack */ + newEntry->next = iSeries_irqMap[irq].head; + iSeries_irqMap[irq].head = newEntry; + ++iSeries_irqMap[irq].entryCount; + rc = 0; + } + else + rc = -1; + + spin_unlock_irqrestore(&irq_desc[irq].lock, flags); + + if (rc != 0 && newEntry) + kfree(newEntry); + + return rc; + +} + + +/* This is called by iSeries_activate_IRQs */ +unsigned int iSeries_startup_IRQ(unsigned int irq) { + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, function, mask; + + /* irq should be locked by the caller */ + + for(entry=iSeries_irqMap[irq].head; entry!=NULL; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + function = deviceId & 0x0F; + /* Link the IRQ number to the bridge */ + HvCallXm_connectBusUnit(bus, subBus, deviceId, irq); + /* Unmask bridge interrupts in the FISR */ + mask = 0x01010000 << function; + HvCallPci_unmaskFisr(bus, subBus, deviceId, mask); + } + + return 0; +} + +/* This is called out of iSeries_fixup to + activate interrupt generation for usable slots */ +void __init iSeries_activate_IRQs() { + int irq; + unsigned long flags; + + for (irq=0; irq < NR_IRQS; irq++) { + spin_lock_irqsave(&irq_desc[irq].lock, flags); + irq_desc[irq].handler->startup(irq); + spin_unlock_irqrestore(&irq_desc[irq].lock, flags); + } + +} + +/* this is not called anywhere currently */ +void iSeries_shutdown_IRQ(unsigned int irq) { + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, function, mask; + + /* irq should be locked by the caller */ + + for(entry=iSeries_irqMap[irq].head; entry; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + function = deviceId & 0x0F; + /* Invalidate the IRQ number in the bridge */ + HvCallXm_connectBusUnit(bus, subBus, deviceId, 0); + /* Mask bridge interrupts in the FISR */ + mask = 0x01010000 << function; + HvCallPci_maskFisr(bus, subBus, deviceId, mask); + } + +} + + +/* This will be called by device drivers (via disable_IRQ to disable + INTA in the bridge interrupt status register */ +void iSeries_disable_IRQ(unsigned int irq) { + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, mask; + + /* The IRQ has already been locked by the caller */ + + for(entry=iSeries_irqMap[irq].head; entry; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + /* Mask secondary INTA */ + mask = 0x80000000; + HvCallPci_maskInterrupts(bus, subBus, deviceId, mask); + } +} + +/* This will be called by device drivers (via enable_IRQ to enable + INTA in the bridge interrupt status register */ +void iSeries_enable_IRQ(unsigned int irq) { + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, mask; + + /* The IRQ has already been locked by the caller */ + + for(entry=iSeries_irqMap[irq].head; entry; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + /* Unmask secondary INTA */ + mask = 0x80000000; + HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask); + } +} + +/* Need to define this so ppc_irq_dispatch_handler will NOT call + enable_IRQ at the end of interrupt handling. However, this + does nothing because there is not enough information provided + to do the EOI HvCall. This is done by XmPciLpEvent.c */ +void iSeries_end_IRQ(unsigned int irq) { +} + diff -Nru a/arch/ppc/iSeries/iSeries_ksyms.c b/arch/ppc/iSeries/iSeries_ksyms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_ksyms.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,82 @@ +/* File iSeries_ksyms.c created by root on Tue Feb 13 2001. */ + +/* Change Activity: */ +/* End Change Activity */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EXPORT_SYMBOL(HvLpEvent_registerHandler); +EXPORT_SYMBOL(HvLpEvent_unregisterHandler); +EXPORT_SYMBOL(HvLpEvent_openPath); +EXPORT_SYMBOL(HvLpEvent_closePath); + +EXPORT_SYMBOL(HvCall1); +EXPORT_SYMBOL(HvCall2); +EXPORT_SYMBOL(HvCall3); +EXPORT_SYMBOL(HvCall4); +EXPORT_SYMBOL(HvCall5); +EXPORT_SYMBOL(HvCall6); +EXPORT_SYMBOL(HvCall7); +EXPORT_SYMBOL(HvCall0); +EXPORT_SYMBOL(HvCall0Ret16); +EXPORT_SYMBOL(HvCall1Ret16); +EXPORT_SYMBOL(HvCall2Ret16); +EXPORT_SYMBOL(HvCall3Ret16); +EXPORT_SYMBOL(HvCall4Ret16); +EXPORT_SYMBOL(HvCall5Ret16); +EXPORT_SYMBOL(HvCall6Ret16); +EXPORT_SYMBOL(HvCall7Ret16); + +EXPORT_SYMBOL(HvLpConfig_getLpIndex_outline); +EXPORT_SYMBOL(virt_to_absolute_outline); + +EXPORT_SYMBOL(mf_allocateLpEvents); +EXPORT_SYMBOL(mf_deallocateLpEvents); + +EXPORT_SYMBOL(iSeries_proc_callback); + + +#ifdef CONFIG_PCI +EXPORT_SYMBOL(pci_map_single); +EXPORT_SYMBOL(pci_unmap_single); + +EXPORT_SYMBOL(iSeries_Readb); +EXPORT_SYMBOL(iSeries_Readw); +EXPORT_SYMBOL(iSeries_Readl); +EXPORT_SYMBOL(iSeries_Writeb); +EXPORT_SYMBOL(iSeries_Writew); +EXPORT_SYMBOL(iSeries_Writel); + +EXPORT_SYMBOL(iSeries_memcpy_fromio); +EXPORT_SYMBOL(iSeries_memcpy_toio); + +EXPORT_SYMBOL(iSeries_GetLocationData); + +EXPORT_SYMBOL(iSeries_Set_PciTraceFlag); +EXPORT_SYMBOL(iSeries_Get_PciTraceFlag); + +EXPORT_SYMBOL(iSeries_Device_Reset_NoIrq); +EXPORT_SYMBOL(iSeries_Device_Reset_Generic); +EXPORT_SYMBOL(iSeries_Device_Reset); +EXPORT_SYMBOL(iSeries_Device_RestoreConfigRegs); +EXPORT_SYMBOL(iSeries_Device_SaveConfigRegs); +EXPORT_SYMBOL(iSeries_Device_ToggleReset); +#endif + + + diff -Nru a/arch/ppc/iSeries/iSeries_pci.c b/arch/ppc/iSeries/iSeries_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_pci.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,587 @@ +/************************************************************************/ +/* File iSeries_pci.c created by Allan Trautman on Tue Jan 9 2001. */ +/************************************************************************/ +/* This code supports the pci interface on the IBM iSeries systems. */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Jan 9, 2001 */ +/* August, 10, 2001 Added PCI Retry code. */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include + +/************************************************************************/ +/* Arch specific's */ +/************************************************************************/ +#include /* Has Io Instructions. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "iSeries_IoMmTable.h" +#include "iSeries_pci.h" + +extern int panic_timeout; /* Panic Timeout reference */ + +/************************************************************************/ +/* /proc/iSeries/pci initialization. */ +/************************************************************************/ +extern void iSeries_pci_proc_init(struct proc_dir_entry *iSeries_proc); + void iSeries_pci_IoError(char* OpCode,iSeries_Device* Device); + +/************************************************************************/ +/* PCI Config Space Access functions for IBM iSeries Systems */ +/* */ +/* Modeled after the IBM Python */ +/* int pci_read_config_word(struct pci_dev*, int Offset, u16* ValuePtr) */ +/************************************************************************/ +/* Note: Normal these routines are defined by a macro and branch */ +/* table is created and stuffed in the pci_bus structure. */ +/* The following is for reference, if it was done this way. However for*/ +/* the first time out, the routines are individual coded. */ +/************************************************************************/ +/* This is the low level architecture depend interface routines. */ +/* 1. They must be in specific order, see for base */ +/* structure. */ +/* 2. The code in not inline here, it calls specific routines in */ +/* this module and HvCalls */ +/* 3. The common convention is to put a pointer to the structure in */ +/* pci_bus->sysdata. */ +/* 4. Direct call is the same as in 0) { + sprintf(PciFrBuffer,"RCB: %02X,%02X,%02X,%04X Rtn: %04X,%02X", + Device.BusNumber, Device.SubBus, Device.DevFn, Offset, + Device.RCode, *ValuePtr); + ISERIES_PCI_FR(PciFrBuffer); + if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer); + } + } + return Device.RCode; +} +int iSeries_pci_read_config_word( struct pci_dev* PciDev, int Offset, u16* ValuePtr) { + iSeries_Device Device; + build_iSeries_Device(&Device, PciDev); + if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */ + else { + Device.RCode = HvCallPci_configLoad16(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, ValuePtr); + if(Device.RCode != 0 || PciTraceFlag > 0) { + sprintf(PciFrBuffer,"RCW: %02X,%02X,%02X,%04X Rtn: %04X,%04X", + Device.BusNumber, Device.SubBus, Device.DevFn, Offset, + Device.RCode, *ValuePtr); + ISERIES_PCI_FR(PciFrBuffer); + if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer); + } + } + return Device.RCode; +} +int iSeries_pci_read_config_dword( struct pci_dev* PciDev, int Offset, u32* ValuePtr) { + iSeries_Device Device; + build_iSeries_Device(&Device, PciDev); + if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */ + else { + Device.RCode = HvCallPci_configLoad32(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, ValuePtr); + if(Device.RCode != 0 || PciTraceFlag > 0) { + sprintf(PciFrBuffer,"RCL: %02X,%02X,%02X,%04X Rtn: %04X,%08X", + Device.BusNumber, Device.SubBus, Device.DevFn, Offset, + Device.RCode, *ValuePtr); + ISERIES_PCI_FR(PciFrBuffer); + if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer); + } + } + return Device.RCode; +} +int iSeries_pci_write_config_byte( struct pci_dev* PciDev, int Offset, u8 Value) { + iSeries_Device Device; + build_iSeries_Device(&Device, PciDev); + if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */ + else { + Device.RCode = HvCallPci_configStore8(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, Value); + if(Device.RCode != 0 || PciTraceFlag > 0) { + sprintf(PciFrBuffer,"WCB: %02X,%02X,%02X,%04X Rtn: %04X,%02X", + Device.BusNumber, Device.SubBus, Device.DevFn, Offset, + Device.RCode, Value); + ISERIES_PCI_FR(PciFrBuffer); + if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer); + } + } + return Device.RCode; +} +int iSeries_pci_write_config_word( struct pci_dev* PciDev, int Offset, u16 Value) { + iSeries_Device Device; + build_iSeries_Device(&Device, PciDev); + if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */ + else { + Device.RCode = HvCallPci_configStore16(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, Value); + if(Device.RCode != 0 || PciTraceFlag > 0) { + sprintf(PciFrBuffer,"WCW: %02X,%02X,%02X,%04X Rtn: %04X,%04X", + Device.BusNumber, Device.SubBus, Device.DevFn, Offset, + Device.RCode, Value); + ISERIES_PCI_FR(PciFrBuffer); + if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer); + } + } + return Device.RCode; +} +int iSeries_pci_write_config_dword(struct pci_dev* PciDev, int Offset, u32 Value) { + iSeries_Device Device; + build_iSeries_Device(&Device, PciDev); + if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */ + else { + Device.RCode = HvCallPci_configStore32(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, Value); + if(Device.RCode != 0 || PciTraceFlag > 0) { + sprintf(PciFrBuffer,"WCL: %02X,%02X,%02X,%04X Rtn: %04X,%08X", + Device.BusNumber, Device.SubBus, Device.DevFn, Offset, + Device.RCode, Value); + ISERIES_PCI_FR(PciFrBuffer); + if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer); + } + } + return Device.RCode; +} +/************************************************************************/ +/* Branch Table */ +/************************************************************************/ +struct pci_ops iSeries_pci_ops = { + iSeries_pci_read_config_byte, + iSeries_pci_read_config_word, + iSeries_pci_read_config_dword, + iSeries_pci_write_config_byte, + iSeries_pci_write_config_word, + iSeries_pci_write_config_dword +}; + +/************************************************************************/ +/* Dump the device info into the Flight Recorder */ +/* Call should have 3 char text to prefix FR Entry */ +/************************************************************************/ +void iSeries_DumpDevice(char* Text, iSeries_Device* Device) { + sprintf(PciFrBuffer,"%s:%02X%02X%02X %04X",Text,Device->BusNumber,Device->SubBus,Device->DevFn,Device->RCode); + ISERIES_PCI_FR(PciFrBuffer); + if(Device->BarNumber != 0xFF) { + sprintf(PciFrBuffer,"BAR:%02X %04X ",Device->BarNumber,Device->BarOffset); + ISERIES_PCI_FR(PciFrBuffer); + } + if(Device->RCode != 0) { + /*****************************************************************/ + /* PCI I/O ERROR RDL: Bus: 0x01 Device: 0x06 ReturnCode: 0x000B */ + /*****************************************************************/ + printk("PCI: I/O ERROR %s: Bus: 0x%02X Device: 0x%02X ReturnCode: 0x%04X\n", + Text,Device->PciDevPtr->bus->number,Device->PciDevPtr->devfn,Device->RCode); + } +} + +int IoCounts = 0; +int RetryCounts = 0; +int IoRetry = 0; + +/************************************************************************ + * Check Return Code + * -> On Failure, print and log information. + * Increment Retry Count, if exceeds max, panic partition. + * -> If in retry, print and log success + ************************************************************************ + * PCI: ReadL: I/O Error( 0): 0x1234 + * PCI: Device..: 17:38.10 Bar: 0x00 Offset 0018 + * PCI: Device..: 17:38.10 Retry( 1) Operation ReadL: + * PCI: Retry Successful on Device: 17:38.10 + ************************************************************************/ +int CheckReturnCode(char* TextHdr, iSeries_Device* Device) { + ++IoCounts; + if(Device->RCode != 0) { + + sprintf(PciFrBuffer,"%s: I/O Error(%2d): 0x%04X\n", TextHdr, IoRetry,Device->RCode); + ISERIES_PCI_FR(PciFrBuffer); + printk("PCI: %s",PciFrBuffer); + + sprintf(PciFrBuffer,"Device..: %02X:%02X.%02X Bar: 0x%02X Offset %04X\n", + Device->BusNumber, Device->SubBus, Device->DevFn, + Device->BarNumber, Device->BarOffset); + ISERIES_PCI_FR(PciFrBuffer); + printk("PCI: %s",PciFrBuffer); + + /* Bump the retry and check for max. */ + ++RetryCounts; + ++IoRetry; + + /* Retry Count exceeded or RIO Bus went down. */ + if(IoRetry > 7 || Device->RCode == 0x206) { + mf_displaySrc(0xB6000103); + panic_timeout = 0; + panic("PCI: Hardware I/O Error, SRC B6000103, Automatic Reboot Disabled."); + } + else { + sprintf(PciFrBuffer,"Device..: %02X:%02X.%02X Retry(%2d) Operation: %s\n", + Device->BusNumber, Device->SubBus, Device->DevFn, + IoRetry,TextHdr); + ISERIES_PCI_FR(PciFrBuffer); + printk("PCI: %s\n",PciFrBuffer); + } + } + else { + if(IoRetry > 0 ) { + sprintf(PciFrBuffer,"Retry Successful on Device: %02X:%02X.%02X\n", + Device->BusNumber, Device->SubBus, Device->DevFn); + ISERIES_PCI_FR(PciFrBuffer); + printk("PCI: %s\n",PciFrBuffer); + } + } + return Device->RCode; +} + +/************************************************************************/ +/* Read MM I/O Instructions for the iSeries */ +/* On MM I/O error, all ones are returned and iSeries_pci_IoError is cal*/ +/* else, data is returned in big Endian format. */ +/************************************************************************/ +/* iSeries_Readb = Read Byte ( 8 bit) */ +/* iSeries_Readw = Read Word (16 bit) */ +/* iSeries_Readl = Read Long (32 bit) */ +/************************************************************************/ +u8 iSeries_Readb(u32* IoAddress) { + iSeries_Device Device; + u8 IoData; + u8 Data = -1; + IoRetry = 0; + if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) { + do { + Device.RCode = HvCallPci_barLoad8 (Device.BusNumber, Device.SubBus,Device.DevFn, + Device.BarNumber, Device.BarOffset, &IoData); + Data = IoData; + if(Device.RCode != 0) CheckReturnCode("ReadB",&Device); + } while(Device.RCode != 0); + } + return Data; +} +u16 iSeries_Readw(u32* IoAddress) { + iSeries_Device Device; + u16 IoData; + u16 Data = -1; + IoRetry = 0; + if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) { + do { + Device.RCode = HvCallPci_barLoad16(Device.BusNumber, Device.SubBus,Device.DevFn, + Device.BarNumber, Device.BarOffset, &IoData); + Data = swab16(IoData); + if(Device.RCode != 0) CheckReturnCode("ReadW",&Device); + } while(Device.RCode != 0); + } + return Data; +} +u32 iSeries_Readl(u32* IoAddress) { + iSeries_Device Device; + u32 Data = -1; + u32 IoData; + IoRetry = 0; + if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) { + IoRetry = 0; + do { + Device.RCode = HvCallPci_barLoad32(Device.BusNumber, Device.SubBus,Device.DevFn, + Device.BarNumber, Device.BarOffset, &IoData); + Data = swab32(IoData); + if(Device.RCode != 0) CheckReturnCode("ReadL",&Device); + } while(Device.RCode != 0); + } + return Data; +} +/************************************************************************/ +/* Write MM I/O Instructions for the iSeries */ +/* On MM I/O error, iSeries_pci_IoError is called */ +/* Data is in big Endian format. */ +/************************************************************************/ +/* iSeries_Writeb = Write Byte (8 bit) */ +/* iSeries_Writew = Write Word(16 bit) */ +/* iSeries_Writel = Write Long(32 bit) */ +/************************************************************************/ +void iSeries_Writeb(u8 Data,u32* IoAddress) { + iSeries_Device Device; + u8 IoData = Data; + IoRetry = 0; + if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) { + do { + Device.RCode = HvCallPci_barStore8 (Device.BusNumber, Device.SubBus,Device.DevFn, + Device.BarNumber, Device.BarOffset, IoData); + if(Device.RCode != 0) CheckReturnCode("WrteB",&Device); + } while(Device.RCode != 0); + } +} +void iSeries_Writew(u16 Data,u32* IoAddress) { + iSeries_Device Device; + u16 IoData = swab16(Data); + IoRetry = 0; + if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) { + do { + Device.RCode = HvCallPci_barStore16(Device.BusNumber, Device.SubBus,Device.DevFn, + Device.BarNumber, Device.BarOffset, IoData); + if(Device.RCode != 0) CheckReturnCode("WrteW",&Device); + } while(Device.RCode != 0); + } +} +void iSeries_Writel(u32 Data,u32* IoAddress) { + iSeries_Device Device; + u32 IoData = swab32(Data); + IoRetry = 0; + if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) { + do { + Device.RCode = HvCallPci_barStore32(Device.BusNumber, Device.SubBus,Device.DevFn, + Device.BarNumber, Device.BarOffset, IoData); + if(Device.RCode != 0) CheckReturnCode("WrteL",&Device); + } while(Device.RCode != 0); + } +} +/************************************************************************/ +/* iSeries I/O Remap */ +/* -> Check to see if one of ours */ +/* -> If not, return null to help find the bug. */ +/************************************************************************/ +void* iSeries_ioremap (unsigned long offset, unsigned long size) { + if(iSeries_xlateIoMmAddress((u32*)offset) != 0) { + return (void*)offset; + } + else { + return NULL; + } +} +/************************************************************************/ +/* Routine to build the iSeries_Device for the pci device. */ +/************************************************************************/ +void build_iSeries_Device(iSeries_Device* Device, struct pci_dev* DevPtr) { + Device->PciDevPtr = DevPtr; + Device->BusNumber = ISERIES_GET_LPAR_BUS(DevPtr->bus->number); + if(ISERIES_GET_LPAR_SUBBUS(DevPtr->bus->number) == 0) { + Device->SubBus = ISERIES_DEVFN_DECODE_SUBBUS(DevPtr->devfn); + Device->DevFn = 0x10 | ISERIES_DECODE_FUNCTION(DevPtr->devfn); + } + else { + Device->SubBus = ISERIES_GET_LPAR_SUBBUS(DevPtr->bus->number); + Device->DevFn = ISERIES_44_FORMAT(DevPtr->devfn); + } + Device->BarNumber = 0xFF; + Device->BarOffset = 0; + Device->RCode = 0; +} +/************************************************************************/ +/* Routine to build the iSeries_Device for the IoAddress. */ +/************************************************************************/ +int build_iSeries_Device_From_IoAddress(iSeries_Device* Device, u32* IoAddress) { + Device->PciDevPtr = iSeries_xlateIoMmAddress(IoAddress); + if(Device->PciDevPtr != 0) { /* Valid pci_dev? */ + build_iSeries_Device(Device,Device->PciDevPtr); + Device->BarNumber = iSeries_IoMmTable_Bar(IoAddress); + if( Device->BarNumber != 0xFF) { + Device->BarOffset = iSeries_IoMmTable_BarOffset(IoAddress); + return 0; + } + else { + sprintf(PciFrBuffer,"I/O BAR Address: 0x%08X",(int)IoAddress); + ISERIES_PCI_FR(PciFrBuffer); + printk("PCI: Invalid I/O Address 0x%08X\n",(int)IoAddress); + return -1; + } + } + printk("PCI: Invalid I/O Address 0x%08X\n",(int)IoAddress); + return -1; +} +/************************************************************************/ +/* Returns the iSeries bus value */ +/************************************************************************/ +u8 iSeries_Get_Bus(struct pci_dev* DevPtr) { + return iSeries_GlobalBusMap[DevPtr->bus->number][_HVBUSNUMBER_]; +} +/************************************************************************/ +/* Returns the iSeries subbus value */ +/************************************************************************/ +u8 iSeries_Get_SubBus(struct pci_dev* DevPtr) { + u8 SubBus = iSeries_GlobalBusMap[DevPtr->bus->number][_HVSUBBUSNUMBER_]; + if (SubBus == 0xFF) SubBus = 0xFF; + else if(SubBus == 0) SubBus = ISERIES_DEVFN_DECODE_SUBBUS(DevPtr->devfn); + else SubBus = ISERIES_GET_LPAR_SUBBUS(DevPtr->bus->number); + return SubBus; +} +/************************************************************************/ +/* Returns the iSeries Device and Function Number */ +/************************************************************************/ +u8 iSeries_Get_DevFn(struct pci_dev* DevPtr) { + u8 SubBus = iSeries_GlobalBusMap[DevPtr->bus->number][_HVSUBBUSNUMBER_]; + u8 DevFn; + if (SubBus == 0xFF) DevFn = 0; + else if(SubBus == 0) DevFn = 0x10 | ISERIES_DECODE_FUNCTION(DevPtr->devfn); + else DevFn = ISERIES_44_FORMAT(DevPtr->devfn); + return DevFn; +} + +/************************************************************************/ +/* This is provides the mapping from the Linux bus and devfn to ISeries */ +/* Bus and Subbus. */ +/* Initialize to all FFs, translations will fail if FF entry found. */ +/************************************************************************/ +u8 iSeries_GlobalBusMap[256][2]; /* Global Bus Mapping */ +void __init iSeries_Initialize_GlobalBusMap(void) { + int Index; + for(Index = 0; Index < 256; ++Index) { + iSeries_GlobalBusMap[Index][_HVBUSNUMBER_] = 0xFF; + iSeries_GlobalBusMap[Index][_HVSUBBUSNUMBER_] = 0xFF; + } + ISERIES_PCI_FR("IntGlobalBusMap"); +} +/************************************************************************/ +/* Create the Flight Recorder */ +/************************************************************************/ +FlightRecorder PciFlightRecorder; /* Pci Flight Recorder */ +FlightRecorder* PciFr; /* Pointer to Fr */ +char PciFrBufferData[128]; /* Working buffer */ +char* PciFrBuffer; /* Pointer to buffer */ +int PciTraceFlag = 0; /* Trace Level Flag */ +struct pci_dev* PciDeviceTrace; /* Device Tracing */ + +void __init iSeries_Initialize_FlightRecorder(void) { + PciFr = &PciFlightRecorder; + PciFrBuffer = &PciFrBufferData[0]; + iSeries_Fr_Initialize(PciFr, "PciFlightRecord"); + ISERIES_PCI_FR("August 10,2001."); + PciTraceFlag = 0; +} +/************************************************************************/ +/* Function to turn on and off the Flight Recorder Trace Flag */ +/************************************************************************/ +int iSeries_Set_PciTraceFlag(int TraceFlag) { + int TempFlag = PciTraceFlag; + PciTraceFlag = TraceFlag; + return TempFlag; +} +int iSeries_Get_PciTraceFlag(void) { + return PciTraceFlag; +} +void iSeries_Set_PciFilter(struct pci_dev* PciDevice) { + PciDeviceTrace = PciDevice; +} +/************************************************************************/ +/* Initialize the I/O Tables and maps */ +/************************************************************************/ +void __init iSeries_pci_Initialize(void) { + iSeries_Initialize_FlightRecorder(); /* Flight Recorder 1st. */ + iSeries_Initialize_GlobalBusMap(); /* Global Bus Map */ + iSeries_IoMmTable_Initialize(); /* Io Translate table. */ + iSeries_proc_callback(&iSeries_pci_proc_init); + iSeries_Set_PciErpFlag(4); /* Erp Level set to 4 */ +} + +/************************************************************************/ +/* Erp level when PCI I/O Errors are detected. */ +/* 0 = Be quiet about the errors. */ +/* 1 >= Log error information and continue on. */ +/* 2 = Printk the error information and continue on. */ +/* 3 = Printk the error information and put task to sleep. */ +/* 4 = Printk the error information and panic the kernel with no reboo*/ +/************************************************************************/ +int iSeries_pci_ErpLevel = 0; +/************************************************************************/ +/* Allows clients to set the Erp State */ +/************************************************************************/ +int iSeries_Set_PciErpFlag(int ErpFlag) { + int SavedState = iSeries_pci_ErpLevel; + printk("PCI: PciERPFlag set to %d.\n", ErpFlag); + iSeries_pci_ErpLevel = ErpFlag; + return SavedState; +} +extern int panic_timeout; /* Panic Timeout reference */ +/************************************************************************/ +/* Fatal I/O Error, crash the system. */ +/* PCI: Fatal I/O Error, Device 00/00, Error 0x0000, Status Reg 0x0000 */ +/* PCI: Kernel Panic called with reboot disabled. */ +/************************************************************************/ +void iSeries_pci_IoError(char* OpCode,iSeries_Device* Device) { + char DeviceInfo[128]; + char ErrorInfo[128]; + struct pci_dev* PciDev = Device->PciDevPtr; + + sprintf(ErrorInfo,"I/O Error Detected. Op: %s, Bus%3d, Device%3d Error: 0x%04X\n", + OpCode,PciDev->bus->number, PCI_SLOT(PciDev->devfn),Device->RCode); + iSeries_Device_Information(PciDev,DeviceInfo,128); + + /* Log the error in the flight recorder */ + if(iSeries_pci_ErpLevel > 0) { + if( Device->RCode != 0x0102) { /* Previous error */ + ISERIES_PCI_FR(ErrorInfo); + ISERIES_PCI_FR(DeviceInfo); + } + } + if(iSeries_pci_ErpLevel > 1) { + printk("PCI: %s",ErrorInfo); + printk("PCI: %s\n",DeviceInfo); + } + if(iSeries_pci_ErpLevel == 3) { + printk("PCI: Current process 0x%08X put to sleep for debug.\n",current->pid); + { + DECLARE_WAITQUEUE(WaitQueue, current); + add_wait_queue(¤t->wait_chldexit,&WaitQueue); + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(¤t->wait_chldexit,&WaitQueue); + } + } + else if(iSeries_pci_ErpLevel == 4) { + mf_displaySrc(0xB6000103); + panic_timeout = 0; /* Don't reboot. */ + /* printk("PCI: Hardware I/O Error, SRC B6000103, Kernel Panic called.\n");*/ + /* panic("Automatic Reboot Disabled.") */ + panic("PCI: Hardware I/O Error, SRC B6000103, Automatic Reboot Disabled."); + } +} + +/************************************************************************/ +/* I/0 Memory copy MUST use mmio commands on iSeries */ +/************************************************************************/ +void* iSeries_memcpy_toio(void *dest, void *source, int n) +{ + char *dst = dest; + char *src = source; + + while (n--) { + writeb (*src++, dst++); + } + + return dest; +} + +void* iSeries_memcpy_fromio(void *dest, void *source, int n) +{ + char *dst = dest; + char *src = source; + + while (n--) + *dst++ = readb (src++); + + return dest; +} diff -Nru a/arch/ppc/iSeries/iSeries_pci.h b/arch/ppc/iSeries/iSeries_pci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_pci.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,139 @@ +#ifdef CONFIG_PPC_ISERIES +#ifndef _ISERIES_PCI_H +#define _ISERIES_PCI_H +/************************************************************************/ +/* File iSeries_pci.h created by Allan Trautman on Tue Jan 9 2001. */ +/************************************************************************/ +/* Define some useful macros for the iseries pci routines. */ +/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created December 28, 2000 */ +/* Converted to iseries_pci.h Jan 25, 2001 */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include + +/************************************************************************************/ +/* Define some useful macros. */ +/* These macros started with Wayne, Al renamed and refined. */ +/************************************************************************************/ +/* Encodes SubBus address(seddddfff), Works only for bridges under EADS 1 and 2. */ +/************************************************************************************/ +/* #define ISERIES_ENCODE_SUBBUS(eads, bridge, device) \ + (0x80 | ((eads-1 & 0x01) << 6) | ((bridge & 0x0F) << 3) | (device & 0x07)) */ +#define ISERIES_ENCODE_SUBBUS(e, ef, df) (((0x80 | ((e&0x02)<<5) | ((ef & 0x07)<<3)) & 0xF8) | (df & 0x03)) // Al - Please Review + +/************************************************************************************/ +/* Combines IdSel and Function into Iseries 4.4 format */ +/* For Linux, see PCI_DEVFN(slot,func) in include/linux/pci.h */ +/************************************************************************************/ +// #define ISERIES_PCI_AGENTID(idsel,func) ((idsel & 0x0F) << 4) | (func & 0x07) +#define ISERIES_PCI_AGENTID(idsel,func) (((idsel & 0x0F) << 4) | (func & 0x0F)) // Al - Please Review + +/************************************************************************************/ +/* Converts DeviceFunction from Linux 5.3(dddddfff) to Iseries 4.4(dddd0fff) */ +/* Converts DeviceFunction from Iseries 4.4(dddd0fff) to Linux 5.3(dddddfff) */ +/************************************************************************************/ +#define ISERIES_44_FORMAT(devfn53) (((devfn53 & 0xF8) << 1) | (devfn53 & 0x07)) +#define ISERIES_53_FORMAT(devfn44) (((devfn44 & 0xF0) >> 1) | (devfn44 & 0x07)) + +/************************************************************************************/ +/* Tests for encoded subbus. */ +/************************************************************************************/ +#define ISERIES_IS_SUBBUS_ENCODED_IN_DEVFN(devfn) ((devfn & 0x80) == 0x80) + +/************************************************************************************/ +/* Decodes the Iseries subbus to devfn, ONLY Works for bus 0!! Use Table lookup. */ +/************************************************************************************/ +/* #define ISERIES_DEVFN_DECODE_SUBBUS(devfn) \ + ((((devfn & 0x40) >> 1) + 0x20) | ((devfn >> 1) & 0x1C)) */ +#define ISERIES_DEVFN_DECODE_SUBBUS(devfn) (((((devfn >> 6 ) & 0x1) + 1) << 5) | (((devfn >> 3) & 0x7) << 2)) // Al - Please Review + +/************************************************************************************/ +/* Decodes Linux DevFn to Iseries DevFn, bridge device, or function. */ +/* For Linux, see PCI_SLOT and PCI_FUNC in include/linux/pci.h */ +/************************************************************************************/ +#define ISERIES_DECODE_DEVFN(linuxdevfn) (((linuxdevfn & 0x71) << 1) | (linuxdevfn & 0x07)) +#define ISERIES_DECODE_DEVICE(linuxdevfn) (((linuxdevfn & 0x38) >> 3) |(((linuxdevfn & 0x40) >> 2) + 0x10)) +#define ISERIES_DECODE_FUNCTION(linuxdevfn) (linuxdevfn & 0x07) + +#define ISERIES_GET_DEVICE_FROM_SUBBUS(subbus) ((subbus >> 5) & 0x7) +#define ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus) ((subbus >> 2) & 0x7) +#define ISERIES_GET_HOSE_HV_BUSNUM(hose) (((struct iSeries_hose_arch_data *)(hose->arch_data))->hvBusNumber) + +/************************************************************************************/ +/* Retreives Iseries Bus and SubBus from GlobalBusMap */ +/************************************************************************************/ +#define ISERIES_GET_LPAR_BUS(linux_bus) iSeries_GlobalBusMap[linux_bus][_HVBUSNUMBER_] +#define ISERIES_GET_LPAR_SUBBUS(linux_bus) iSeries_GlobalBusMap[linux_bus][_HVSUBBUSNUMBER_] + +#define ISERIES_ADD_BUS_GLOBALBUSMAP(linuxbus, iseriesbus, iseriessubbus) \ + iSeries_GlobalBusMap[linuxbus][_HVBUSNUMBER_] = iseriesbus; \ + iSeries_GlobalBusMap[linuxbus][_HVSUBBUSNUMBER_] = iseriessubbus; + +/************************************************************************************/ +/* Global Bus map */ +/* Bus and Subbus index values into the global bus number map array. */ +/************************************************************************************/ +#define ISERIES_GLOBALBUSMAP_SIZE 256 +#define _HVBUSNUMBER_ 0 +#define _HVSUBBUSNUMBER_ 1 +extern u8 iSeries_GlobalBusMap[ISERIES_GLOBALBUSMAP_SIZE][2]; +void iSeries_Initialize_GlobalBusMap(void); +#define pci_assign_all_buses() 1 // Al - NEW + +/************************************************************************************/ +/* Converts Virtual Address to Real Address for Hypervisor calls */ +/************************************************************************************/ +#define REALADDR(virtaddr) (0x8000000000000000 | (virt_to_absolute((u32)virtaddr) )) + +/************************************************************************************/ +/* Define TRUE and FALSE Values for Al */ +/************************************************************************************/ +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +typedef struct pci_dev pciDev; +/************************************************************************/ +/* Routines to build the iSeries_Device for the pci device. */ +/************************************************************************/ +extern void build_iSeries_Device(iSeries_Device* Device, struct pci_dev* DevPtr); +extern int build_iSeries_Device_From_IoAddress(iSeries_Device* Device, u32* IoAddress); +extern void iSeries_pci_Initialize(void); + +/************************************************************************/ +/* Flight Recorder Debug Support */ +/************************************************************************/ +extern int PciTraceFlag; /* Conditional Trace */ +void iSeries_Initialize_FlightRecorder(void); +int iSeries_Set_PciTraceFlag(int Flag); /* Sets flag, return old*/ +int iSeries_Get_PciTraceFlag(void); /* Gets Flag. */ +void iSeries_DumpDevice(char* Text, iSeries_Device* ); + +#endif /* _ISERIES_PCI_H */ +#endif /*CONFIG_PPC_ISERIES */ + diff -Nru a/arch/ppc/iSeries/iSeries_pci_proc.c b/arch/ppc/iSeries/iSeries_pci_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_pci_proc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,185 @@ +/************************************************************************/ +/* File iSeries pci_proc.c created by Allan Trautman on Feb 27 2001. */ +/************************************************************************/ +/* Create /proc/iSeries/pci file that contains iSeries card location. */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Feb 27, 2001 */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +static struct proc_dir_entry *pci_proc_root = NULL; +static struct proc_dir_entry *pciFr_proc_root = NULL; + +int iSeries_proc_pci_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); +int iSeries_proc_pci_write_proc(struct file *file, const char *buffer, unsigned long count, void *data); +int iSeries_proc_pciFr_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); +int iSeries_proc_pciFr_write_proc(struct file *file, const char *buffer, unsigned long count, void *data); + + +void iSeries_pci_proc_init(struct proc_dir_entry *iSeries_proc) { + printk("PCI: Creating /proc/iSeries/pci\n"); + + /* Read = User,Group,Other, Write User */ + pci_proc_root = create_proc_entry("pci", S_IFREG | S_IRUGO | S_IWUSR, iSeries_proc); + if (!pci_proc_root) return; + pci_proc_root->nlink = 1; + pci_proc_root->data = (void *)0; + pci_proc_root->read_proc = iSeries_proc_pci_read_proc; + pci_proc_root->write_proc = iSeries_proc_pci_write_proc; + + /* Read = User,Group,Other, Write User */ + printk("PCI: Creating /proc/iSeries/pciFr\n"); + pciFr_proc_root = create_proc_entry("pciFr", S_IFREG | S_IRUGO | S_IWUSR, iSeries_proc); + if (!pciFr_proc_root) return; + pciFr_proc_root->nlink = 1; + pciFr_proc_root->data = (void *)0; + pciFr_proc_root->read_proc = iSeries_proc_pciFr_read_proc; + pciFr_proc_root->write_proc = iSeries_proc_pciFr_write_proc; +} + +/*******************************************************************************/ +/* Get called when client reads the /proc/iSeries/pci file. The data returned */ +/* is the iSeries card locations for service. */ +/*******************************************************************************/ +int iSeries_proc_pci_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { + int LineLen; /* Size of new Data */ + struct pci_dev* PciDev; /* Device pointer */ + struct net_device *dev; /* net_device pointer */ + int DeviceCount; /* Device Number */ + /***************************************************************************/ + /* ### Bus Device Bus Dev Frm Card */ + /* 1. Linux: 0/ 28 iSeries: 24/ 36/ 1/C14 */ + /* 2. Linux: 0/ 30 iSeries: 24/ 38/ 2/C14 */ + /***************************************************************************/ + DeviceCount = 1; /* Count the devices listed. */ + LineLen = 0; /* Reset Length */ + + /***************************************************************************/ + /* List the devices */ + /***************************************************************************/ + pci_for_each_dev(PciDev) { + LineLen += sprintf(page+LineLen,"%3d. ",DeviceCount); + LineLen += iSeries_Device_Information(PciDev,page+LineLen,count-LineLen); + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (dev->base_addr == PciDev->resource[0].start ) { /* Yep, a net_device */ + LineLen += sprintf(page+LineLen, ", Net device: %s", dev->name); + } /* if */ + } /* for */ + LineLen += sprintf(page+LineLen,"\n"); + ++DeviceCount; /* Add for the list. */ + /************************************************************************/ + /* Run out of room in system buffer. */ + /************************************************************************/ + if(LineLen+80 >= count) { /* Room for another line? No. bail out */ + LineLen +=sprintf(page+LineLen,"/proc/pci file full!\n"); + break; + } + } + /***************************************************************************/ + /* If no devices, tell user that instead */ + /***************************************************************************/ + if(DeviceCount == 1) { + LineLen +=sprintf(page+LineLen,"No PCI devices found\n"); + } + /***************************************************************************/ + /* Update counts and return */ + /***************************************************************************/ + *eof = LineLen; + return LineLen; +} + +/*******************************************************************************/ +/* Do nothing, Nothing to support for the write */ +/*******************************************************************************/ +int iSeries_proc_pci_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { + return count; +} + +/*******************************************************************************/ +/* Get called when client reads the /proc/iSeries/pci file. The data returned */ +/* is the iSeries card locations for service. */ +/*******************************************************************************/ +int iSeries_proc_pciFr_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { + struct pci_dev* PciDev; /* Device pointer */ + int DeviceCount = 1; /* Device Number */ + int LineLen = 0; /* Size of new Data */ + + printk("PCI: Dump Flight Recorder!\n"); + /***************************************************************************/ + /* List the devices */ + /***************************************************************************/ + pci_for_each_dev(PciDev) { + LineLen += sprintf(page+LineLen,"%3d. 0x%08X ",DeviceCount,(int)PciDev); + LineLen += sprintf(page+LineLen,"Bus: %02X, Device: %02X ",PciDev->bus->number,PciDev->devfn); + LineLen += sprintf(page+LineLen,"\n"); + ++DeviceCount; /* Add for the list. */ + } + LineLen += sprintf(page+LineLen,"--\n"); + /***************************************************************************/ + /* The Flight Recorder */ + /* Someday handle wrap */ + /***************************************************************************/ + if (PciFr->StartingPointer != NULL) { + char* StartEntry = (char*)PciFr->StartingPointer; + char* EndEntry = (char*)PciFr->CurrentPointer; + while(EndEntry > StartEntry && LineLen+40 < count) { + LineLen += sprintf(page+LineLen,"%s\n",StartEntry); + StartEntry += strlen(StartEntry) + 1; + } + if(LineLen+40 >= count) { + printk("PCI: Max Count Hit %d and %d\n",LineLen,count); + } + } + /***************************************************************************/ + /* Update counts and return */ + /***************************************************************************/ + printk("PCI: End of File at %d\n",LineLen); + *eof = LineLen; + return LineLen; +} +/*******************************************************************************/ +/* Flight Recorder Controls */ +/*******************************************************************************/ +int iSeries_proc_pciFr_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { + if(buffer != 0 && strlen(buffer) > 0) { + if( strstr(buffer,"trace on") != NULL) { + iSeries_Set_PciTraceFlag(1); + ISERIES_PCI_FR_DATE("PCI Trace turned on!"); + } + else if(strstr(buffer,"trace off") != NULL) { + iSeries_Set_PciTraceFlag(0); + ISERIES_PCI_FR_DATE("PCI Trace turned off!"); + } + else { + ISERIES_PCI_FR_TIME("PCI Trace Option Invalid!"); + readl(0x00000000); + } + } + return count; +} diff -Nru a/arch/ppc/iSeries/iSeries_proc.c b/arch/ppc/iSeries/iSeries_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_proc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,154 @@ +/* + * iSeries_proc.c + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#include +#include +#ifndef _ISERIES_PROC_H +#include +#endif + + +static struct proc_dir_entry * iSeries_proc_root = NULL; +static int iSeries_proc_initializationDone = 0; +static spinlock_t iSeries_proc_lock; + +struct iSeries_proc_registration +{ + struct iSeries_proc_registration *next; + iSeriesProcFunction functionMember; +}; + + +struct iSeries_proc_registration preallocated[16]; +#define MYQUEUETYPE(T) struct MYQueue##T +#define MYQUEUE(T) \ +MYQUEUETYPE(T) \ +{ \ +struct T *head; \ +struct T *tail; \ +} +#define MYQUEUECTOR(q) do { (q)->head = NULL; (q)->tail = NULL; } while(0) +#define MYQUEUEENQ(q, p) \ +do { \ +(p)->next = NULL; \ +if ((q)->head != NULL) \ +{ \ +(q)->head->next = (p); \ +(q)->head = (p); \ +} \ +else \ +{ \ +(q)->tail = (q)->head = (p); \ +} \ +} while(0) + +#define MYQUEUEDEQ(q,p) \ +do { \ +(p) = (q)->tail; \ +if ((p) != NULL) \ +{ \ +(q)->tail = (p)->next; \ +(p)->next = NULL; \ +} \ +if ((q)->tail == NULL) \ +(q)->head = NULL; \ +} while(0) +MYQUEUE(iSeries_proc_registration); +typedef MYQUEUETYPE(iSeries_proc_registration) aQueue; + + +aQueue iSeries_free; +aQueue iSeries_queued; + +void iSeries_proc_early_init(void) +{ + int i = 0; + unsigned long flags; + iSeries_proc_initializationDone = 0; + spin_lock_init(&iSeries_proc_lock); + MYQUEUECTOR(&iSeries_free); + MYQUEUECTOR(&iSeries_queued); + + spin_lock_irqsave(&iSeries_proc_lock, flags); + for (i = 0; i < 16; ++i) + { + MYQUEUEENQ(&iSeries_free, preallocated+i); + } + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + +void iSeries_proc_create(void) +{ + unsigned long flags; + struct iSeries_proc_registration *reg = NULL; + spin_lock_irqsave(&iSeries_proc_lock, flags); + printk("iSeries_proc: Creating /proc/iSeries\n"); + + iSeries_proc_root = proc_mkdir("iSeries", 0); + if (!iSeries_proc_root) return; + + MYQUEUEDEQ(&iSeries_queued, reg); + + while (reg != NULL) + { + (*(reg->functionMember))(iSeries_proc_root); + + MYQUEUEDEQ(&iSeries_queued, reg); + } + + iSeries_proc_initializationDone = 1; + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + +void iSeries_proc_callback(iSeriesProcFunction initFunction) +{ + unsigned long flags; + spin_lock_irqsave(&iSeries_proc_lock, flags); + + if (iSeries_proc_initializationDone) + { + (*initFunction)(iSeries_proc_root); + } + else + { + struct iSeries_proc_registration *reg = NULL; + + MYQUEUEDEQ(&iSeries_free, reg); + + if (reg != NULL) + { +// printk("Registering %p in reg %p\n", initFunction, reg); + reg->functionMember = initFunction; + + MYQUEUEENQ(&iSeries_queued, reg); + } + else + { + printk("Couldn't get a queue entry\n"); + } + } + + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + + diff -Nru a/arch/ppc/iSeries/iSeries_reset_device.c b/arch/ppc/iSeries/iSeries_reset_device.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/iSeries_reset_device.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,196 @@ +/************************************************************************/ +/* File iSeries_reset_device.c created by Allan Trautman on Mar 21 2001.*/ +/************************************************************************/ +/* This code supports the pci interface on the IBM iSeries systems. */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, March 20, 2001 */ +/* April 30, 2001, Added return codes on functions. */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include +/************************************************************************/ +/* Arch specific's */ +/************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "iSeries_pci.h" + +/************************************************************************/ +/* Interface to Reset Device, see .h for parms and flavors. */ +/************************************************************************/ +int iSeries_Device_Reset_NoIrq(struct pci_dev* PciDev) { + return iSeries_Device_Reset(PciDev,0,0,1); +} +int iSeries_Device_Reset_Generic(struct pci_dev* PciDev) { + return iSeries_Device_Reset(PciDev,0,0,0); +} +int iSeries_Device_Reset(struct pci_dev* PciDev, int AssertTime, int DelayTime, int IrqState) { + int RCode = 0; + PciReqsSaveArea* RegSaveArea = NULL; + if(PciDev != 0) { + if(IrqState == 0) disable_irq(PciDev->irq); + RegSaveArea = iSeries_Device_SaveConfigRegs(PciDev); + if(RegSaveArea != NULL) { + RCode = iSeries_Device_ToggleReset(PciDev, AssertTime, DelayTime); + if(RCode == 0) { + RCode = iSeries_Device_RestoreConfigRegs(RegSaveArea); + } + } + else { + RCode = -1; + } + if(IrqState == 0) enable_irq(PciDev->irq); + } + return RCode; +} +/************************************************************************/ +/* Interface to toggle the reset line */ +/* Time is in .1 seconds, need for seconds. */ +/************************************************************************/ +int iSeries_Device_ToggleReset(struct pci_dev* PciDev, int AssertTime, int DelayTime) { + unsigned long AssertDelay = AssertTime; + unsigned long WaitDelay = DelayTime; + u16 Bus = ISERIES_GET_LPAR_BUS(PciDev->bus->number); + u8 Slot = ISERIES_DECODE_DEVICE(PciDev->devfn); + int RCode = 0; + + /* Set defaults */ + if(AssertTime < 5) AssertDelay = 5; /* Default is .5 second */ + if(WaitDelay < 30) WaitDelay = 30; /* Default is 3 seconds */ + + /* Assert reset for time specified */ + AssertDelay *= HZ; /* Convert to ticks. */ + AssertDelay /= 10; /* Adjust to whole count */ + RCode = HvCallPci_setSlotReset(Bus, 0x00, Slot, 1); + set_current_state(TASK_UNINTERRUPTIBLE); /* Only Wait. */ + schedule_timeout(AssertDelay); /* Sleep for the time */ + RCode += HvCallPci_setSlotReset(Bus, 0x00, Slot, 0); + + /* Wait for device to reset */ + WaitDelay *= HZ; /* Ticks */ + WaitDelay /= 10; /* Whole count */ + set_current_state(TASK_UNINTERRUPTIBLE); /* Only Wait. */ + schedule_timeout(WaitDelay); /* Sleep for the time */ + + if(RCode == 0) { + sprintf(PciFrBuffer,"Slot Reset on Bus%3d, Device%3d!\n",Bus, Slot); + } + else { + sprintf(PciFrBuffer,"Slot Reset on Bus%3d, Device%3d Failed! RCode: %04X\n",Bus, Slot, RCode); + } + ISERIES_PCI_FR_TIME(PciFrBuffer); + printk("PCI: %s\n",PciFrBuffer); + return RCode; +} + +/************************************************************************/ +/* Allocates space and save the config registers for a device. */ +/************************************************************************/ +/* Note: This does byte reads so the data may appear byte swapped */ +/* when compared to read word or dword. */ +/* The data returned is a structure and will be freed automatically on */ +/* the restore of the data. The is checking so if the save fails, the */ +/* data will not be restore. Yes I know, you are most likey toast. */ +/************************************************************************/ +PciReqsSaveArea* iSeries_Device_SaveConfigRegs(struct pci_dev* DevPtr) { + int Register = 0; + struct pci_dev* PciDev = DevPtr; + PciReqsSaveArea* RegSaveArea = (PciReqsSaveArea*)kmalloc(sizeof(PciReqsSaveArea), GFP_KERNEL); + /*printk("PCI: Save Configuration Registers. 0x%08X\n",(int)RegSaveArea); */ + if(RegSaveArea == 0) { + printk("PCI: Allocation failure in Save Configuration Registers.\n"); + } + /********************************************************************/ + /* Initialize Area. */ + /********************************************************************/ + else { + RegSaveArea->PciDev = DevPtr; + RegSaveArea->Flags = 0x01; + RegSaveArea->ByteCount = PCI_MAX_LAT+1; /* Number of Bytes */ + RegSaveArea->RCode = 0; + RegSaveArea->FailReg = 0; + /****************************************************************/ + /* Save All the Regs, NOTE: restore skips the first 16 bytes. */ + /****************************************************************/ + for(Register = 0;Register < RegSaveArea->ByteCount && RegSaveArea->RCode == 0; ++Register) { + RegSaveArea->RCode = pci_read_config_byte(PciDev, Register, &RegSaveArea->Regs[Register]); + } + /* Check for error during the save. */ + if(RegSaveArea->RCode != 0) { + printk("PCI: I/O Failure in Save Configuration Registers. 0x%02X, 0x%04X\n", + Register,RegSaveArea->RCode); + RegSaveArea->Flags |= 0x80; /* Ouch Flag. */ + RegSaveArea->FailReg = Register; /* Stuff this way */ + } + } + return RegSaveArea; +} +/************************************************************************/ +/* Restores the registers saved via the save function. See the save */ +/* function for details. */ +/************************************************************************/ +int iSeries_Device_RestoreConfigRegs(PciReqsSaveArea* SaveArea) { + int RCode = 0; + if(SaveArea == 0 || SaveArea->PciDev == 0 || + (SaveArea->Flags & 0x80) == 0x80 || SaveArea->RCode != 0) { + printk("PCI: Invalid SaveArea passed to Restore Configuration Registers. 0x%08X\n",(int)SaveArea); + RCode = -1; + } + else { + int Register; + struct pci_dev* PciDev = SaveArea->PciDev; + /***************************************************************/ + /* Don't touch the Cmd or BIST regs, user must restore those. */ + /* Restore PCI_CACHE_LINE_SIZE & PCI_LATENCY_TIMER */ + /* Restore Saved Regs from 0x10 to 0x3F */ + /***************************************************************/ + pci_write_config_byte(PciDev, PCI_CACHE_LINE_SIZE, SaveArea->Regs[PCI_CACHE_LINE_SIZE]); + pci_write_config_byte(PciDev, PCI_LATENCY_TIMER, SaveArea->Regs[PCI_LATENCY_TIMER]); + + for(Register = PCI_BASE_ADDRESS_0; Register < SaveArea->ByteCount && SaveArea->RCode == 0; ++Register) { + SaveArea->RCode = pci_write_config_byte(PciDev, Register, SaveArea->Regs[Register]); + } + if(SaveArea->RCode != 0) { + printk("PCI: I/O Failure in Restore Configuration Registers %d, %02X\n",Register,SaveArea->RCode); + SaveArea->FailReg = Register; + RCode = SaveArea->RCode; + } + else { + RCode = 0; + } + /***************************************************************/ + /* Is the Auto Free Flag on */ + /***************************************************************/ + if(SaveArea->Flags && 0x01 == 0x01 ) { + /* printk("PCI: Auto Free Register Save Area. 0x%08X\n",(int)SaveArea); */ + kfree(SaveArea); + } + } + return RCode; +} diff -Nru a/arch/ppc/iSeries/mf.c b/arch/ppc/iSeries/mf.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/mf.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1211 @@ +/* + * mf.c + * Copyright (C) 2001 Troy D. Armstrong IBM Corporation + * + * This modules exists as an interface between a Linux secondary partition + * running on an iSeries and the primary partition's Virtual Service + * Processor (VSP) object. The VSP has final authority over powering on/off + * all partitions in the iSeries. It also provides miscellaneous low-level + * machine facility type operations. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + + +/* + * This is the structure layout for the Machine Facilites LPAR event + * flows. + */ +struct VspCmdData; +struct CeMsgData; +union SafeCast +{ + u64 ptrAsU64; + void *ptr; +}; + + +typedef void (*CeMsgCompleteHandler)( void *token, struct CeMsgData *vspCmdRsp ); + +struct CeMsgCompleteData +{ + CeMsgCompleteHandler xHdlr; + void *xToken; +}; + +struct VspRspData +{ + struct semaphore *xSemaphore; + struct VspCmdData *xResponse; +}; + +struct IoMFLpEvent +{ + struct HvLpEvent xHvLpEvent; + + u16 xSubtypeRc; + u16 xRsvd1; + u32 xRsvd2; + + union + { + + struct AllocData + { + u16 xSize; + u16 xType; + u32 xCount; + u16 xRsvd3; + u8 xRsvd4; + HvLpIndex xTargetLp; + } xAllocData; + + struct CeMsgData + { + u8 xCEMsg[12]; + char xReserved[4]; + struct CeMsgCompleteData *xToken; + } xCEMsgData; + + struct VspCmdData + { + union SafeCast xTokenUnion; + u16 xCmd; + HvLpIndex xLpIndex; + u8 xRc; + u32 xReserved1; + + union VspCmdSubData + { + struct + { + u64 xState; + } xGetStateOut; + + struct + { + u64 xIplType; + } xGetIplTypeOut, xFunction02SelectIplTypeIn; + + struct + { + u64 xIplMode; + } xGetIplModeOut, xFunction02SelectIplModeIn; + + struct + { + u64 xPage[4]; + } xGetSrcHistoryIn; + + struct + { + u64 xFlag; + } xGetAutoIplWhenPrimaryIplsOut, xSetAutoIplWhenPrimaryIplsIn, xWhiteButtonPowerOffIn, xFunction08FastPowerOffIn, xIsSpcnRackPowerIncompleteOut; + + struct + { + u64 xToken; + u64 xAddressType; + u64 xSide; + u32 xTransferLength; + u32 xOffset; + } xSetKernelImageIn, xGetKernelImageIn, xSetKernelCmdLineIn, xGetKernelCmdLineIn; + + struct + { + u32 xTransferLength; + } xGetKernelImageOut,xGetKernelCmdLineOut; + + + u8 xReserved2[80]; + + } xSubData; + } xVspCmd; + } xUnion; +}; + + +/* + * All outgoing event traffic is kept on a FIFO queue. The first + * pointer points to the one that is outstanding, and all new + * requests get stuck on the end. Also, we keep a certain number of + * preallocated stack elements so that we can operate very early in + * the boot up sequence (before kmalloc is ready). + */ +struct StackElement +{ + struct StackElement * next; + struct IoMFLpEvent event; + MFCompleteHandler hdlr; + char dmaData[72]; + unsigned dmaDataLength; + unsigned remoteAddress; +}; +static spinlock_t spinlock; +static struct StackElement * head = NULL; +static struct StackElement * tail = NULL; +static struct StackElement * avail = NULL; +static struct StackElement prealloc[16]; + +/* + * Put a stack element onto the available queue, so it can get reused. + * Attention! You must have the spinlock before calling! + */ +void free( struct StackElement * element ) +{ + if( element != NULL ) + { + element->next = avail; + avail = element; + } +} + +/* + * Enqueue the outbound event onto the stack. If the queue was + * empty to begin with, we must also issue it via the Hypervisor + * interface. There is a section of code below that will touch + * the first stack pointer without the protection of the spinlock. + * This is OK, because we know that nobody else will be modifying + * the first pointer when we do this. + */ +static int signalEvent( struct StackElement * newElement ) +{ + int rc = 0; + unsigned long flags; + int go = 1; + struct StackElement * element; + HvLpEvent_Rc hvRc; + + /* enqueue the event */ + if( newElement != NULL ) + { + spin_lock_irqsave( &spinlock, flags ); + if( head == NULL ) + head = newElement; + else + { + go = 0; + tail->next = newElement; + } + newElement->next = NULL; + tail = newElement; + spin_unlock_irqrestore( &spinlock, flags ); + } + + /* send the event */ + while( go ) + { + go = 0; + + /* any DMA data to send beforehand? */ + if( head->dmaDataLength > 0 ) + HvCallEvent_dmaToSp( head->dmaData, head->remoteAddress, head->dmaDataLength, HvLpDma_Direction_LocalToRemote ); + + hvRc = HvCallEvent_signalLpEvent(&head->event.xHvLpEvent); + if( hvRc != HvLpEvent_Rc_Good ) + { + printk( KERN_ERR "mf.c: HvCallEvent_signalLpEvent() failed with %d\n", (int)hvRc ); + + spin_lock_irqsave( &spinlock, flags ); + element = head; + head = head->next; + if( head != NULL ) + go = 1; + spin_unlock_irqrestore( &spinlock, flags ); + + if( element == newElement ) + rc = -EIO; + else + { + if( element->hdlr != NULL ) + { + union SafeCast mySafeCast; + mySafeCast.ptrAsU64 = element->event.xHvLpEvent.xCorrelationToken; + (*element->hdlr)( mySafeCast.ptr, -EIO ); + } + } + + spin_lock_irqsave( &spinlock, flags ); + free( element ); + spin_unlock_irqrestore( &spinlock, flags ); + } + } + + return rc; +} + +/* + * Allocate a new StackElement structure, and initialize it. + */ +static struct StackElement * newStackElement( void ) +{ + struct StackElement * newElement = NULL; + HvLpIndex primaryLp = HvLpConfig_getPrimaryLpIndex(); + unsigned long flags; + + if( newElement == NULL ) + { + spin_lock_irqsave( &spinlock, flags ); + if( avail != NULL ) + { + newElement = avail; + avail = avail->next; + } + spin_unlock_irqrestore( &spinlock, flags ); + } + + if( newElement == NULL ) + newElement = kmalloc(sizeof(struct StackElement),GFP_ATOMIC); + + if( newElement == NULL ) + { + printk( KERN_ERR "mf.c: unable to kmalloc %d bytes\n", sizeof(struct StackElement) ); + return NULL; + } + + memset( newElement, 0, sizeof(struct StackElement) ); + newElement->event.xHvLpEvent.xFlags.xValid = 1; + newElement->event.xHvLpEvent.xFlags.xAckType = HvLpEvent_AckType_ImmediateAck; + newElement->event.xHvLpEvent.xFlags.xAckInd = HvLpEvent_AckInd_DoAck; + newElement->event.xHvLpEvent.xFlags.xFunction = HvLpEvent_Function_Int; + newElement->event.xHvLpEvent.xType = HvLpEvent_Type_MachineFac; + newElement->event.xHvLpEvent.xSourceLp = HvLpConfig_getLpIndex(); + newElement->event.xHvLpEvent.xTargetLp = primaryLp; + newElement->event.xHvLpEvent.xSizeMinus1 = sizeof(newElement->event)-1; + newElement->event.xHvLpEvent.xRc = HvLpEvent_Rc_Good; + newElement->event.xHvLpEvent.xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac); + newElement->event.xHvLpEvent.xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac); + + return newElement; +} + +static int signalVspInstruction( struct VspCmdData *vspCmd ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + struct VspRspData response; + DECLARE_MUTEX_LOCKED(Semaphore); + response.xSemaphore = &Semaphore; + response.xResponse = vspCmd; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + newElement->event.xHvLpEvent.xSubtype = 6; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0); + newElement->event.xUnion.xVspCmd.xTokenUnion.ptr = &response; + newElement->event.xUnion.xVspCmd.xCmd = vspCmd->xCmd; + newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex(); + newElement->event.xUnion.xVspCmd.xRc = 0xFF; + newElement->event.xUnion.xVspCmd.xReserved1 = 0; + memcpy(&(newElement->event.xUnion.xVspCmd.xSubData),&(vspCmd->xSubData), sizeof(vspCmd->xSubData)); + mb(); + + rc = signalEvent(newElement); + } + + if (rc == 0) + { + down(&Semaphore); + } + + return rc; +} + + +/* + * Send a 12-byte CE message to the primary partition VSP object + */ +static int signalCEMsg( char * ceMsg, void * token ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + newElement->event.xHvLpEvent.xSubtype = 0; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0); + memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 ); + newElement->event.xUnion.xCEMsgData.xToken = token; + rc = signalEvent(newElement); + } + + return rc; +} + +/* + * Send a 12-byte CE message and DMA data to the primary partition VSP object + */ +static int dmaAndSignalCEMsg( char * ceMsg, void * token, void * dmaData, unsigned dmaDataLength, unsigned remoteAddress ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + newElement->event.xHvLpEvent.xSubtype = 0; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0); + memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 ); + newElement->event.xUnion.xCEMsgData.xToken = token; + memcpy( newElement->dmaData, dmaData, dmaDataLength ); + newElement->dmaDataLength = dmaDataLength; + newElement->remoteAddress = remoteAddress; + rc = signalEvent(newElement); + } + + return rc; +} + +/* + * Initiate a nice (hopefully) shutdown of Linux. We simply are + * going to try and send the init process a SIGINT signal. If + * this fails (why?), we'll simply force it off in a not-so-nice + * manner. + */ +static int shutdown( void ) +{ + int rc = kill_proc(1,SIGINT,1); + + if( rc ) + { + printk( KERN_ALERT "mf.c: SIGINT to init failed (%d), hard shutdown commencing\n", rc ); + mf_powerOff(); + } + else + printk( KERN_ALERT "mf.c: init has been successfully notified to proceed with shutdown\n" ); + + return rc; +} + +/* + * The primary partition VSP object is sending us a new + * event flow. Handle it... + */ +static void intReceived( struct IoMFLpEvent * event ) +{ + int freeIt = 0; + struct StackElement * two = NULL; + /* ack the interrupt */ + event->xHvLpEvent.xRc = HvLpEvent_Rc_Good; + HvCallEvent_ackLpEvent( &event->xHvLpEvent ); + + /* process interrupt */ + switch( event->xHvLpEvent.xSubtype ) + { + case 0: /* CE message */ + switch( event->xUnion.xCEMsgData.xCEMsg[3] ) + { + case 0x5B: /* power control notification */ + if( (event->xUnion.xCEMsgData.xCEMsg[5]&0x20) != 0 ) + { + printk( KERN_ALERT "mf.c: Commencing partition shutdown\n" ); + if( shutdown() == 0 ) + signalCEMsg( "\x00\x00\x00\xDB\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + } + break; + case 0xC0: /* get time */ + { + if ( (head != NULL) && ( head->event.xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) ) + { + freeIt = 1; + if ( head->event.xUnion.xCEMsgData.xToken != 0 ) + { + CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr; + void * token = head->event.xUnion.xCEMsgData.xToken->xToken; + + if (xHdlr != NULL) + (*xHdlr)( token, &(event->xUnion.xCEMsgData) ); + } + } + } + break; + } + + /* remove from queue */ + if ( freeIt == 1 ) + { + unsigned long flags; + spin_lock_irqsave( &spinlock, flags ); + if( head != NULL ) + { + struct StackElement *oldHead = head; + head = head->next; + two = head; + free( oldHead ); + } + spin_unlock_irqrestore( &spinlock, flags ); + } + + /* send next waiting event */ + if( two != NULL ) + signalEvent( NULL ); + break; + case 1: /* IT sys shutdown */ + printk( KERN_ALERT "mf.c: Commencing system shutdown\n" ); + shutdown(); + break; + } +} + +/* + * The primary partition VSP object is acknowledging the receipt + * of a flow we sent to them. If there are other flows queued + * up, we must send another one now... + */ +static void ackReceived( struct IoMFLpEvent * event ) +{ + unsigned long flags; + struct StackElement * two = NULL; + unsigned long freeIt = 0; + + /* handle current event */ + if( head != NULL ) + { + switch( event->xHvLpEvent.xSubtype ) + { + case 0: /* CE msg */ + if( event->xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) + { + if ( event->xUnion.xCEMsgData.xCEMsg[2] != 0 ) + { + freeIt = 1; + if ( head->event.xUnion.xCEMsgData.xToken != 0 ) + { + CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr; + void * token = head->event.xUnion.xCEMsgData.xToken->xToken; + + if (xHdlr != NULL) + (*xHdlr)( token, &(event->xUnion.xCEMsgData) ); + } + } + } + else + { + freeIt = 1; + } + break; + case 4: /* allocate */ + case 5: /* deallocate */ + if( head->hdlr != NULL ) + { + union SafeCast mySafeCast; + mySafeCast.ptrAsU64 = event->xHvLpEvent.xCorrelationToken; + (*head->hdlr)( mySafeCast.ptr, event->xUnion.xAllocData.xCount ); + } + freeIt = 1; + break; + case 6: + { + struct VspRspData *rsp = (struct VspRspData *)event->xUnion.xVspCmd.xTokenUnion.ptr; + + if (rsp != NULL) + { + if (rsp->xResponse != NULL) + memcpy(rsp->xResponse, &(event->xUnion.xVspCmd), sizeof(event->xUnion.xVspCmd)); + if (rsp->xSemaphore != NULL) + up(rsp->xSemaphore); + } + else + { + printk( KERN_ERR "mf.c: no rsp\n"); + } + freeIt = 1; + } + break; + } + } + else + printk( KERN_ERR "mf.c: stack empty for receiving ack\n" ); + + /* remove from queue */ + spin_lock_irqsave( &spinlock, flags ); + if(( head != NULL ) && ( freeIt == 1 )) + { + struct StackElement *oldHead = head; + head = head->next; + two = head; + free( oldHead ); + } + spin_unlock_irqrestore( &spinlock, flags ); + + /* send next waiting event */ + if( two != NULL ) + signalEvent( NULL ); +} + +/* + * This is the generic event handler we are registering with + * the Hypervisor. Ensure the flows are for us, and then + * parse it enough to know if it is an interrupt or an + * acknowledge. + */ +static void hvHandler( struct HvLpEvent * event, struct pt_regs * regs ) +{ + if( (event != NULL) && (event->xType == HvLpEvent_Type_MachineFac) ) + { + switch( event->xFlags.xFunction ) + { + case HvLpEvent_Function_Ack: + ackReceived( (struct IoMFLpEvent *)event ); + break; + case HvLpEvent_Function_Int: + intReceived( (struct IoMFLpEvent *)event ); + break; + default: + printk( KERN_ERR "mf.c: non ack/int event received\n" ); + break; + } + } + else + printk( KERN_ERR "mf.c: alien event received\n" ); +} + +/* + * Global kernel interface to allocate and seed events into the + * Hypervisor. + */ +void mf_allocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned size, + unsigned count, + MFCompleteHandler hdlr, + void * userToken ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + union SafeCast mine; + mine.ptr = userToken; + newElement->event.xHvLpEvent.xSubtype = 4; + newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('A'<<0); + newElement->event.xUnion.xAllocData.xTargetLp = targetLp; + newElement->event.xUnion.xAllocData.xType = type; + newElement->event.xUnion.xAllocData.xSize = size; + newElement->event.xUnion.xAllocData.xCount = count; + newElement->hdlr = hdlr; + rc = signalEvent(newElement); + } + + if( (rc != 0) && (hdlr != NULL) ) + (*hdlr)( userToken, rc ); +} + +/* + * Global kernel interface to unseed and deallocate events already in + * Hypervisor. + */ +void mf_deallocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned count, + MFCompleteHandler hdlr, + void * userToken ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + union SafeCast mine; + mine.ptr = userToken; + newElement->event.xHvLpEvent.xSubtype = 5; + newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('D'<<0); + newElement->event.xUnion.xAllocData.xTargetLp = targetLp; + newElement->event.xUnion.xAllocData.xType = type; + newElement->event.xUnion.xAllocData.xCount = count; + newElement->hdlr = hdlr; + rc = signalEvent(newElement); + } + + if( (rc != 0) && (hdlr != NULL) ) + (*hdlr)( userToken, rc ); +} + +/* + * Global kernel interface to tell the VSP object in the primary + * partition to power this partition off. + */ +void mf_powerOff( void ) +{ + printk( KERN_ALERT "mf.c: Down it goes...\n" ); + signalCEMsg( "\x00\x00\x00\x4D\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + for(;;); +} + +/* + * Global kernel interface to tell the VSP object in the primary + * partition to reboot this partition. + */ +void mf_reboot( void ) +{ + printk( KERN_ALERT "mf.c: Preparing to bounce...\n" ); + signalCEMsg( "\x00\x00\x00\x4E\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + for(;;); +} + +/* + * Display a single word SRC onto the VSP control panel. + */ +void mf_displaySrc( u32 word ) +{ + u8 ce[12]; + + memcpy( ce, "\x00\x00\x00\x4A\x00\x00\x00\x01\x00\x00\x00\x00", 12 ); + ce[8] = word>>24; + ce[9] = word>>16; + ce[10] = word>>8; + ce[11] = word; + signalCEMsg( ce, NULL ); +} + +/* + * Display a single word SRC of the form "PROGXXXX" on the VSP control panel. + */ +void mf_displayProgress( u16 value ) +{ + u8 ce[12]; + u8 src[72]; + + memcpy( ce, "\x00\x00\x04\x4A\x00\x00\x00\x48\x00\x00\x00\x00", 12 ); + memcpy( src, + "\x01\x00\x00\x01" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "PROGxxxx" + " ", + 72 ); + src[6] = value>>8; + src[7] = value&255; + src[44] = "0123456789ABCDEF"[(value>>12)&15]; + src[45] = "0123456789ABCDEF"[(value>>8)&15]; + src[46] = "0123456789ABCDEF"[(value>>4)&15]; + src[47] = "0123456789ABCDEF"[value&15]; + dmaAndSignalCEMsg( ce, NULL, src, sizeof(src), 9*64*1024 ); +} + +/* + * Clear the VSP control panel. Used to "erase" an SRC that was + * previously displayed. + */ +void mf_clearSrc( void ) +{ + signalCEMsg( "\x00\x00\x00\x4B\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); +} + +/* + * Initialization code here. + */ +void mf_init( void ) +{ + int i; + + /* initialize */ + spin_lock_init( &spinlock ); + for( i = 0; i < sizeof(prealloc)/sizeof(*prealloc); ++i ) + free( &prealloc[i] ); + HvLpEvent_registerHandler( HvLpEvent_Type_MachineFac, &hvHandler ); + + /* virtual continue ack */ + signalCEMsg( "\x00\x00\x00\x57\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + + /* initialization complete */ + printk( KERN_NOTICE "mf.c: iSeries Linux LPAR Machine Facilities initialized\n" ); + + iSeries_proc_callback(&mf_proc_init); +} + +void mf_setSide(char side) +{ + int rc = 0; + u64 newSide = 0; + struct VspCmdData myVspCmd; + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + if (side == 'A') + newSide = 0; + else if (side == 'B') + newSide = 1; + else if (side == 'C') + newSide = 2; + else + newSide = 3; + + myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = newSide; + myVspCmd.xCmd = 10; + + rc = signalVspInstruction(&myVspCmd); +} + +char mf_getSide(void) +{ + char returnValue = ' '; + int rc = 0; + struct VspCmdData myVspCmd; + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 2; + myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = 0; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if(rc != 0) + { + return returnValue; + } + else + { + if (myVspCmd.xRc == 0) + { + if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 0) + returnValue = 'A'; + else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 1) + returnValue = 'B'; + else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 2) + returnValue = 'C'; + else + returnValue = 'D'; + } + } + + return returnValue; +} + +void mf_getSrcHistory(char *buffer, int size) +{ + /* struct IplTypeReturnStuff returnStuff; + struct StackElement * newElement = newStackElement(); + int rc = 0; + char *pages[4]; + + pages[0] = kmalloc(4096, GFP_ATOMIC); + pages[1] = kmalloc(4096, GFP_ATOMIC); + pages[2] = kmalloc(4096, GFP_ATOMIC); + pages[3] = kmalloc(4096, GFP_ATOMIC); + if(( newElement == NULL ) || (pages[0] == NULL) || (pages[1] == NULL) || (pages[2] == NULL) || (pages[3] == NULL)) + rc = -ENOMEM; + else + { + returnStuff.xType = 0; + returnStuff.xRc = 0; + returnStuff.xDone = 0; + newElement->event.xHvLpEvent.xSubtype = 6; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0); + newElement->event.xUnion.xVspCmd.xEvent = &returnStuff; + newElement->event.xUnion.xVspCmd.xCmd = 4; + newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex(); + newElement->event.xUnion.xVspCmd.xRc = 0xFF; + newElement->event.xUnion.xVspCmd.xReserved1 = 0; + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[0] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[0])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[1] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[1])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[2] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[2])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[3] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[3])); + mb(); + rc = signalEvent(newElement); + } + + if(rc != 0) + { + return; + } + else + { + while (returnStuff.xDone != 1) + { + udelay(10); + } + + if (returnStuff.xRc == 0) + { + memcpy(buffer, pages[0], size); + } + } + + kfree(pages[0]); + kfree(pages[1]); + kfree(pages[2]); + kfree(pages[3]);*/ +} + +void mf_setCmdLine(const char *cmdline, int size, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + dma_addr_t dma_addr = 0; + char *page = pci_alloc_consistent(NULL, size, &dma_addr); + + if (page == NULL) { + printk(KERN_ERR "mf.c: couldn't allocate memory to set command line\n"); + return; + } + + copy_from_user(page, cmdline, size); + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 31; + myVspCmd.xSubData.xSetKernelCmdLineIn.xToken = dma_addr; + myVspCmd.xSubData.xSetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xSetKernelCmdLineIn.xSide = side; + myVspCmd.xSubData.xSetKernelCmdLineIn.xTransferLength = size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + pci_free_consistent(NULL, size, page, dma_addr); +} + +int mf_getCmdLine(char *cmdline, int *size, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + int len = *size; + dma_addr_t dma_addr = pci_map_single(NULL, cmdline, *size, PCI_DMA_FROMDEVICE); + + memset(cmdline, 0, *size); + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 33; + myVspCmd.xSubData.xGetKernelCmdLineIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelCmdLineIn.xSide = side; + myVspCmd.xSubData.xGetKernelCmdLineIn.xTransferLength = *size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if ( ! rc ) { + + if (myVspCmd.xRc == 0) + { + len = myVspCmd.xSubData.xGetKernelCmdLineOut.xTransferLength; + } + // else + // { + // memcpy(cmdline, "Bad cmdline", 11); + // } + } + + pci_unmap_single(NULL, dma_addr, *size, PCI_DMA_FROMDEVICE); + + return len; +} + + +int mf_setVmlinuxChunk(const char *buffer, int size, int offset, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + + dma_addr_t dma_addr = 0; + + char *page = pci_alloc_consistent(NULL, size, &dma_addr); + + if (page == NULL) { + printk(KERN_ERR "mf.c: couldn't allocate memory to set vmlinux chunk\n"); + return -ENOMEM; + } + + copy_from_user(page, buffer, size); + memset(&myVspCmd, 0, sizeof(myVspCmd)); + + myVspCmd.xCmd = 30; + myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelImageIn.xSide = side; + myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset; + myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if(rc == 0) + { + if (myVspCmd.xRc == 0) + { + rc = 0; + } + else + { + rc = -ENOMEM; + } + } + + pci_free_consistent(NULL, size, page, dma_addr); + + return rc; +} + +int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + int len = *size; + + dma_addr_t dma_addr = pci_map_single(NULL, buffer, *size, PCI_DMA_FROMDEVICE); + + memset(buffer, 0, len); + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 32; + myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelImageIn.xSide = side; + myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset; + myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = len; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if(rc == 0) + { + if (myVspCmd.xRc == 0) + { + *size = myVspCmd.xSubData.xGetKernelImageOut.xTransferLength; + } + else + { + rc = -ENOMEM; + } + } + + pci_unmap_single(NULL, dma_addr, *size, PCI_DMA_FROMDEVICE); + + return rc; +} + +int mf_setRtcTime(unsigned long time) +{ + struct rtc_time tm; + + to_tm(time, &tm); + + return mf_setRtc( &tm ); +} + +struct RtcTimeData +{ + struct semaphore *xSemaphore; + struct CeMsgData xCeMsg; + int xRc; +}; + +void getRtcTimeComplete(void * token, struct CeMsgData *ceMsg) +{ + struct RtcTimeData *rtc = (struct RtcTimeData *)token; + + memcpy(&(rtc->xCeMsg), ceMsg, sizeof(rtc->xCeMsg)); + + rtc->xRc = 0; + up(rtc->xSemaphore); +} + +static unsigned long lastsec = 1; + +int mf_getRtcTime(unsigned long *time) +{ +// unsigned long usec, tsec; + + u32 dataWord1 = *((u32 *)(&xSpCommArea.xBcdTimeAtIplStart)); + u32 dataWord2 = *(((u32 *)&(xSpCommArea.xBcdTimeAtIplStart)) + 1); + int year = 1970; + int year1 = ( dataWord1 >> 24 ) & 0x000000FF; + int year2 = ( dataWord1 >> 16 ) & 0x000000FF; + int sec = ( dataWord1 >> 8 ) & 0x000000FF; + int min = dataWord1 & 0x000000FF; + int hour = ( dataWord2 >> 24 ) & 0x000000FF; + int day = ( dataWord2 >> 8 ) & 0x000000FF; + int mon = dataWord2 & 0x000000FF; + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year1); + BCD_TO_BIN(year2); + year = year1 * 100 + year2; + + *time = mktime(year, mon, day, hour, min, sec); + + *time += ( jiffies / HZ ); + + // Now THIS is a nasty hack! + // It ensures that the first two calls to mf_getRtcTime get different + // answers. That way the loop in init_time (time.c) will not think + // the clock is stuck. + if ( lastsec ) { + *time -= lastsec; + --lastsec; + } + + return 0; + +} + +int mf_getRtc( struct rtc_time * tm ) +{ + + struct CeMsgCompleteData ceComplete; + struct RtcTimeData rtcData; + int rc = 0; + DECLARE_MUTEX_LOCKED(Semaphore); + + memset(&ceComplete, 0, sizeof(ceComplete)); + memset(&rtcData, 0, sizeof(rtcData)); + + rtcData.xSemaphore = &Semaphore; + + ceComplete.xHdlr = &getRtcTimeComplete; + ceComplete.xToken = (void *)&rtcData; + + rc = signalCEMsg( "\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00", &ceComplete ); + + if ( rc == 0 ) + { + down(&Semaphore); + + if ( rtcData.xRc == 0) + { + if ( ( rtcData.xCeMsg.xCEMsg[2] == 0xa9 ) || + ( rtcData.xCeMsg.xCEMsg[2] == 0xaf ) ) { + /* TOD clock is not set */ + tm->tm_sec = 1; + tm->tm_min = 1; + tm->tm_hour = 1; + tm->tm_mday = 10; + tm->tm_mon = 8; + tm->tm_year = 71; + mf_setRtc( tm ); + } + { + u32 dataWord1 = *((u32 *)(rtcData.xCeMsg.xCEMsg+4)); + u32 dataWord2 = *((u32 *)(rtcData.xCeMsg.xCEMsg+8)); + u8 year = (dataWord1 >> 16 ) & 0x000000FF; + u8 sec = ( dataWord1 >> 8 ) & 0x000000FF; + u8 min = dataWord1 & 0x000000FF; + u8 hour = ( dataWord2 >> 24 ) & 0x000000FF; + u8 day = ( dataWord2 >> 8 ) & 0x000000FF; + u8 mon = dataWord2 & 0x000000FF; + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + + if ( year <= 69 ) + year += 100; + + tm->tm_sec = sec; + tm->tm_min = min; + tm->tm_hour = hour; + tm->tm_mday = day; + tm->tm_mon = mon; + tm->tm_year = year; + } + } + else + { + rc = rtcData.xRc; + tm->tm_sec = 0; + tm->tm_min = 0; + tm->tm_hour = 0; + tm->tm_mday = 15; + tm->tm_mon = 5; + tm->tm_year = 52; + + } + tm->tm_wday = 0; + tm->tm_yday = 0; + tm->tm_isdst = 0; + + } + + return rc; + +} + +int mf_setRtc(struct rtc_time * tm) +{ + char ceTime[12] = "\x00\x00\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00"; + int rc = 0; + u8 day, mon, hour, min, sec, y1, y2; + unsigned year; + + year = 1900 + tm->tm_year; + y1 = year / 100; + y2 = year % 100; + + sec = tm->tm_sec; + min = tm->tm_min; + hour = tm->tm_hour; + day = tm->tm_mday; + mon = tm->tm_mon + 1; + + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hour); + BIN_TO_BCD(mon); + BIN_TO_BCD(day); + BIN_TO_BCD(y1); + BIN_TO_BCD(y2); + + ceTime[4] = y1; + ceTime[5] = y2; + ceTime[6] = sec; + ceTime[7] = min; + ceTime[8] = hour; + ceTime[10] = day; + ceTime[11] = mon; + + rc = signalCEMsg( ceTime, NULL ); + + return rc; +} + + + diff -Nru a/arch/ppc/iSeries/mf_proc.c b/arch/ppc/iSeries/mf_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/mf_proc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,309 @@ +/* + * mf_proc.c + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _MF_PROC_H +#include +#endif +#ifndef MF_H_INCLUDED +#include +#endif +#include + +static struct proc_dir_entry *mf_proc_root = NULL; + +int proc_mf_dump_cmdline +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_dump_vmlinux +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_dump_side +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_change_side +(struct file *file, const char *buffer, unsigned long count, void *data); + +int proc_mf_dump_src +(char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_mf_change_src (struct file *file, const char *buffer, unsigned long count, void *data); +int proc_mf_change_cmdline(struct file *file, const char *buffer, unsigned long count, void *data); +int proc_mf_change_vmlinux(struct file *file, const char *buffer, unsigned long count, void *data); + + +void mf_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent = NULL; + struct proc_dir_entry *mf_a = NULL; + struct proc_dir_entry *mf_b = NULL; + struct proc_dir_entry *mf_c = NULL; + struct proc_dir_entry *mf_d = NULL; + + mf_proc_root = proc_mkdir("mf", iSeries_proc); + if (!mf_proc_root) return; + + mf_a = proc_mkdir("A", mf_proc_root); + if (!mf_a) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_a); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_a); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_b = proc_mkdir("B", mf_proc_root); + if (!mf_b) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_b); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)1; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_b); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)1; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_c = proc_mkdir("C", mf_proc_root); + if (!mf_c) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_c); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)2; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_c); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)2; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_d = proc_mkdir("D", mf_proc_root); + if (!mf_d) return; + + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_d); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)3; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR, mf_d); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)3; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = NULL; + + ent = create_proc_entry("side", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_side; + ent->write_proc = proc_mf_change_side; + + ent = create_proc_entry("src", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_src; + ent->write_proc = proc_mf_change_src; + + +} + +int proc_mf_dump_cmdline +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = count; + char *p; + + len = mf_getCmdLine(page, &len, (u64)(int)data); + + p = page + len - 1; + while ( p > page ) { + if ( (*p == 0) || (*p == ' ') ) + --p; + else + break; + } + if ( *p != '\n' ) { + ++p; + *p = '\n'; + } + ++p; + *p = 0; + len = p - page; + + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_mf_dump_vmlinux +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int sizeToGet = count; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (mf_getVmlinuxChunk(page, &sizeToGet, off, (u64)(int)data) == 0) + { + if (sizeToGet != 0) + { + *start = page + off; + printk("mf_proc.c: got count %d off %d\n", sizeToGet, (int)off); + return sizeToGet; + } + else + { + printk("mf_proc.c: eof\n"); + *eof = 1; + return 0; + } + } + else + { + printk("mf_proc.c: eof\n"); + *eof = 1; + return 0; + } +} + + +int proc_mf_dump_side +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + char mf_current_side = mf_getSide(); + len = sprintf(page, "%c\n", mf_current_side); + + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +int proc_mf_change_side(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if ((*buffer != 'A') && + (*buffer != 'B') && + (*buffer != 'C') && + (*buffer != 'D')) + { + printk(KERN_ERR "mf_proc.c: proc_mf_change_side: invalid side\n"); + return -EINVAL; + } + + mf_setSide(*buffer); + + return count; +} + +int proc_mf_dump_src +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + mf_getSrcHistory(page, count); + len = count; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_mf_change_src(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if ((count < 4) && (count != 1)) + { + printk(KERN_ERR "mf_proc: invalid src\n"); + return -EINVAL; + } + + if ((count == 1) && ((*buffer) == '\0')) + { + mf_clearSrc(); + } + else + { + mf_displaySrc(*(u32 *)buffer); + } + + return count; +} + +int proc_mf_change_cmdline(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + mf_setCmdLine(buffer, count, (u64)(int)data); + + return count; +} + +int proc_mf_change_vmlinux(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + mf_setVmlinuxChunk(buffer, count, file->f_pos, (u64)(int)data); + file->f_pos += count; + + return count; +} diff -Nru a/arch/ppc/iSeries/pmc_proc.c b/arch/ppc/iSeries/pmc_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/pmc_proc.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,647 @@ +/* + * pmc_proc.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _PMC_PROC_H +#include +#endif + +#include +#include +#include +#include + +#define MMCR0 795 +#define MMCR1 798 +#define MMCRA 786 +#define PMC1 787 +#define PMC2 788 +#define PMC3 789 +#define PMC4 790 +#define PMC5 791 +#define PMC6 792 +#define PMC7 793 +#define PMC8 794 + +static int proc_pmc_control_mode = 0; +#define PMC_CONTROL_CPI 1 +#define PMC_CONTROL_TLB 2 + +static struct proc_dir_entry *pmc_proc_root = NULL; + +int proc_get_lpevents( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data); + +int proc_pmc_get_control( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_mmcr0( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_mmcr1( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_mmcra( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc1( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc2( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc3( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc4( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc5( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc6( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc7( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc8( char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data); + +void pmc_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent = NULL; + + ent = create_proc_entry("lpevents", S_IFREG|S_IRUGO, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_get_lpevents; + ent->write_proc = proc_reset_lpevents; + + pmc_proc_root = proc_mkdir("pmc", iSeries_proc); + if (!pmc_proc_root) return; + + ent = create_proc_entry("control", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_control; + ent->write_proc = proc_pmc_set_control; + + ent = create_proc_entry("mmcr0", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_mmcr0; + ent->write_proc = proc_pmc_set_mmcr0; + + ent = create_proc_entry("mmcr1", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_mmcr1; + ent->write_proc = proc_pmc_set_mmcr1; + + ent = create_proc_entry("mmcra", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_mmcra; + ent->write_proc = proc_pmc_set_mmcra; + + ent = create_proc_entry("pmc1", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc1; + ent->write_proc = proc_pmc_set_pmc1; + + ent = create_proc_entry("pmc2", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc2; + ent->write_proc = proc_pmc_set_pmc2; + + ent = create_proc_entry("pmc3", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc3; + ent->write_proc = proc_pmc_set_pmc3; + + ent = create_proc_entry("pmc4", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc4; + ent->write_proc = proc_pmc_set_pmc4; + + ent = create_proc_entry("pmc5", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc5; + ent->write_proc = proc_pmc_set_pmc5; + + ent = create_proc_entry("pmc6", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc6; + ent->write_proc = proc_pmc_set_pmc6; + + ent = create_proc_entry("pmc7", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc7; + ent->write_proc = proc_pmc_set_pmc7; + + ent = create_proc_entry("pmc8", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc8; + ent->write_proc = proc_pmc_set_pmc8; + + +} + +static int pmc_calc_metrics( char *page, char **start, off_t off, int count, int *eof, int len) +{ + if ( len <= off+count) + *eof = 1; + *start = page+off; + len -= off; + if ( len > count ) + len = count; + if ( len < 0 ) + len = 0; + return len; +} + +static char * lpEventTypes[9] = { + "Hypervisor\t\t", + "Machine Facilities\t", + "Session Manager\t", + "SPD I/O\t\t", + "Virtual Bus\t\t", + "PCI I/O\t\t", + "RIO I/O\t\t", + "Virtual Lan\t\t", + "Virtual I/O\t\t" + }; + + +int proc_get_lpevents +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + unsigned i; + int len = 0; + + len += sprintf( page+len, "LpEventQueue 0\n" ); + len += sprintf( page+len, " events processed:\t%lu\n", + (unsigned long)xItLpQueue.xLpIntCount ); + for (i=0; i<9; ++i) { + len += sprintf( page+len, " %s %10lu\n", + lpEventTypes[i], + (unsigned long)xItLpQueue.xLpIntCountByType[i] ); + } + return pmc_calc_metrics( page, start, off, count, eof, len ); + +} + +int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + return count; +} + +int proc_pmc_get_control +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + if ( proc_pmc_control_mode == PMC_CONTROL_CPI ) { + unsigned long mach_cycles = mfspr( PMC5 ); + unsigned long inst_complete = mfspr( PMC4 ); + unsigned long inst_dispatch = mfspr( PMC3 ); + unsigned long thread_active_run = mfspr( PMC1 ); + unsigned long thread_active = mfspr( PMC2 ); + unsigned long cpi = 0; + unsigned long cpithou = 0; + unsigned long remain; + + if ( inst_complete ) { + cpi = thread_active_run / inst_complete; + remain = thread_active_run % inst_complete; + if ( inst_complete > 1000000 ) + cpithou = remain / ( inst_complete / 1000 ); + else + cpithou = ( remain * 1000 ) / inst_complete; + } + len += sprintf( page+len, "PMC CPI Mode\nRaw Counts\n" ); + len += sprintf( page+len, "machine cycles : %12lu\n", mach_cycles ); + len += sprintf( page+len, "thread active cycles : %12lu\n\n", thread_active ); + + len += sprintf( page+len, "instructions completed : %12lu\n", inst_complete ); + len += sprintf( page+len, "instructions dispatched : %12lu\n", inst_dispatch ); + len += sprintf( page+len, "thread active run cycles : %12lu\n", thread_active_run ); + + len += sprintf( page+len, "thread active run cycles/instructions completed\n" ); + len += sprintf( page+len, "CPI = %lu.%03lu\n", cpi, cpithou ); + + } + else if ( proc_pmc_control_mode == PMC_CONTROL_TLB ) { + len += sprintf( page+len, "PMC TLB Mode\n" ); + len += sprintf( page+len, "I-miss count : %12u\n", mfspr( PMC1 ) ); + len += sprintf( page+len, "I-miss latency : %12u\n", mfspr( PMC2 ) ); + len += sprintf( page+len, "D-miss count : %12u\n", mfspr( PMC3 ) ); + len += sprintf( page+len, "D-miss latency : %12u\n", mfspr( PMC4 ) ); + len += sprintf( page+len, "IERAT miss count : %12u\n", mfspr( PMC5 ) ); + len += sprintf( page+len, "D-reference count : %12u\n", mfspr( PMC6 ) ); + len += sprintf( page+len, "miss PTEs searched : %12u\n", mfspr( PMC7 ) ); + len += sprintf( page+len, "miss >8 PTEs searched : %12u\n", mfspr( PMC8 ) ); + } + /* IMPLEMENT ME */ + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_mmcr0 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(MMCR0) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_mmcr1 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(MMCR1) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_mmcra +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(MMCRA) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc1 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(PMC1) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc2 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(PMC2) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc3 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(PMC3) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc4 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(PMC4) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc5 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(PMC5) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc6 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(PMC6) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc7 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(PMC7) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc8 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08x", mfspr(PMC8) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +unsigned long proc_pmc_conv_int( const char *buf, unsigned count ) +{ + const char * p; + char b0, b1; + unsigned v, multiplier, mult, i; + unsigned long val; + multiplier = 10; + p = buf; + if ( count >= 3 ) { + b0 = buf[0]; + b1 = buf[1]; + if ( ( b0 == '0' ) && + ( ( b1 == 'x' ) || ( b1 == 'X' ) ) ) { + p = buf + 2; + count -= 2; + multiplier = 16; + } + + } + val = 0; + for ( i=0; i= '0' ) && ( b0 <= '9' ) ) + v = b0 - '0'; + else if ( multiplier == 16 ) { + if ( ( b0 >= 'a' ) && ( b0 <= 'f' ) ) + v = b0 - 'a' + 10; + else if ( ( b0 >= 'A' ) && ( b0 <= 'F' ) ) + v = b0 - 'A' + 10; + else + mult = 1; + } + else + mult = 1; + val *= mult; + val += v; + } + + return val; + +} + +static inline void proc_pmc_stop(void) +{ + // Freeze all counters, leave everything else alone + mtspr( MMCR0, mfspr( MMCR0 ) | 0x80000000 ); +} + +static inline void proc_pmc_start(void) +{ + // Unfreeze all counters, leave everything else alone + mtspr( MMCR0, mfspr( MMCR0 ) & ~0x80000000 ); + +} + +static inline void proc_pmc_reset(void) +{ + // Clear all the PMCs to zero + // Assume a "stop" has already frozen the counters + // Clear all the PMCs + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + +} + +static inline void proc_pmc_cpi(void) +{ + /* Configure the PMC registers to count cycles and instructions */ + /* so we can compute cpi */ + /* + * MMCRA[30] = 1 Don't count in wait state (CTRL[31]=0) + * MMCR0[6] = 1 Freeze counters when any overflow + * MMCR0[19:25] = 0x01 PMC1 counts Thread Active Run Cycles + * MMCR0[26:31] = 0x05 PMC2 counts Thread Active Cycles + * MMCR1[0:4] = 0x07 PMC3 counts Instructions Dispatched + * MMCR1[5:9] = 0x03 PMC4 counts Instructions Completed + * MMCR1[10:14] = 0x06 PMC5 counts Machine Cycles + * + */ + + proc_pmc_control_mode = PMC_CONTROL_CPI; + + // Indicate to hypervisor that we are using the PMCs + ((struct Paca *)mfspr(SPRG1))->xLpPacaPtr->xPMCRegsInUse = 1; + + // Freeze all counters + mtspr( MMCR0, 0x80000000 ); + mtspr( MMCR1, 0x00000000 ); + + // Clear all the PMCs + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + + // Freeze counters in Wait State (CTRL[31]=0) + mtspr( MMCRA, 0x00000002 ); + + // PMC3<-0x07, PMC4<-0x03, PMC5<-0x06 + mtspr( MMCR1, 0x38cc0000 ); + + mb(); + + // PMC1<-0x01, PMC2<-0x05 + // Start all counters + mtspr( MMCR0, 0x02000045 ); + +} + +static inline void proc_pmc_tlb(void) +{ + /* Configure the PMC registers to count tlb misses */ + /* + * MMCR0[6] = 1 Freeze counters when any overflow + * MMCR0[19:25] = 0x55 Group count + * PMC1 counts I misses + * PMC2 counts I miss duration (latency) + * PMC3 counts D misses + * PMC4 counts D miss duration (latency) + * PMC5 counts IERAT misses + * PMC6 counts D references (including PMC7) + * PMC7 counts miss PTEs searched + * PMC8 counts miss >8 PTEs searched + * + */ + + proc_pmc_control_mode = PMC_CONTROL_TLB; + + // Indicate to hypervisor that we are using the PMCs + ((struct Paca *)mfspr(SPRG1))->xLpPacaPtr->xPMCRegsInUse = 1; + + // Freeze all counters + mtspr( MMCR0, 0x80000000 ); + mtspr( MMCR1, 0x00000000 ); + + // Clear all the PMCs + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + + mtspr( MMCRA, 0x00000000 ); + + mb(); + + // PMC1<-0x55 + // Start all counters + mtspr( MMCR0, 0x02001540 ); + +} + +int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + if ( ! strncmp( buffer, "stop", 4 ) ) + proc_pmc_stop(); + else if ( ! strncmp( buffer, "start", 5 ) ) + proc_pmc_start(); + else if ( ! strncmp( buffer, "reset", 5 ) ) + proc_pmc_reset(); + else if ( ! strncmp( buffer, "cpi", 3 ) ) + proc_pmc_cpi(); + else if ( ! strncmp( buffer, "tlb", 3 ) ) + proc_pmc_tlb(); + + /* IMPLEMENT ME */ + return count; +} + +int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + v = v & ~0x04000000; /* Don't allow interrupts for now */ + if ( v & ~0x80000000 ) /* Inform hypervisor we are using PMCs */ + ((struct Paca *)mfspr(SPRG1))->xLpPacaPtr->xPMCRegsInUse = 1; + else + ((struct Paca *)mfspr(SPRG1))->xLpPacaPtr->xPMCRegsInUse = 0; + mtspr( MMCR0, v ); + + return count; +} + +int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( MMCR1, v ); + + return count; +} + +int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + v = v & ~0x00008000; /* Don't allow interrupts for now */ + mtspr( MMCRA, v ); + + return count; +} + + +int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC1, v ); + + return count; +} + +int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC2, v ); + + return count; +} + +int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC3, v ); + + return count; +} + +int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC4, v ); + + return count; +} + +int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC5, v ); + + return count; +} + +int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC6, v ); + + return count; +} + +int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC7, v ); + + return count; +} + +int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC8, v ); + + return count; +} + + diff -Nru a/arch/ppc/iSeries/rtc.c b/arch/ppc/iSeries/rtc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/iSeries/rtc.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,265 @@ +/* + * Real Time Clock interface for IBM iSeries + * + * Based on rtc.c by Paul Gortmaker + * + * This driver allows use of the real time clock + * from user space. It exports the /dev/rtc + * interface supporting various ioctl() and also the + * /proc/driver/rtc pseudo-file for status information. + * + * iSeries does not support RTC interrupts nor an alarm. + * + * 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. + * + * 1.0 Mike Corrigan: IBM iSeries rtc support + */ + +#define RTC_VERSION "1.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* + * We sponge a minor off of the misc major. No need slurping + * up another valuable major dev number for this. If you add + * an ioctl, make sure you don't conflict with SPARC's RTC + * ioctls. + */ + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin); + +static ssize_t rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static void get_rtc_time (struct rtc_time *rtc_tm); + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +/* + * If this driver ever becomes modularised, it will be really nice + * to make the epoch retain its value across module reload... + */ + +static unsigned long epoch = 1900; /* year corresponding to 0x00 */ + +static const unsigned char days_in_mo[] = +{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* + * Now all the various file operations that we export. + */ + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static ssize_t rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + return -EIO; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_time wtime; + + switch (cmd) { + case RTC_RD_TIME: /* Read the time/date from RTC */ + { + get_rtc_time(&wtime); + break; + } + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time rtc_tm; + unsigned char mon, day, hrs, min, sec, leap_yr; + unsigned int yrs; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + yrs = rtc_tm.tm_year; + mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm.tm_mday; + hrs = rtc_tm.tm_hour; + min = rtc_tm.tm_min; + sec = rtc_tm.tm_sec; + + if (yrs < 70) + return -EINVAL; + + leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); + + if ((mon > 12) || (day == 0)) + return -EINVAL; + + if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) + return -EINVAL; + + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) + return -EINVAL; + + if ( yrs > 169 ) + return -EINVAL; + + mf_setRtc( &rtc_tm ); + + return 0; + } + case RTC_EPOCH_READ: /* Read the epoch. */ + { + return put_user (epoch, (unsigned long *)arg); + } + case RTC_EPOCH_SET: /* Set the epoch. */ + { + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + epoch = arg; + return 0; + } + default: + return -EINVAL; + } + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * The various file operations we support. + */ + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: rtc_llseek, + read: rtc_read, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, +}; + +static struct miscdevice rtc_dev= +{ + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int __init rtc_init(void) +{ + misc_register(&rtc_dev); + create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); + + printk(KERN_INFO "iSeries Real Time Clock Driver v" RTC_VERSION "\n"); + + return 0; +} + +static void __exit rtc_exit (void) +{ + remove_proc_entry ("driver/rtc", NULL); + misc_deregister(&rtc_dev); +} + +module_init(rtc_init); +module_exit(rtc_exit); +EXPORT_NO_SYMBOLS; + +/* + * Info exported via "/proc/driver/rtc". + */ + +static int rtc_proc_output (char *buf) +{ + + char *p; + struct rtc_time tm; + + p = buf; + + get_rtc_time(&tm); + + /* + * There is no way to tell if the luser has the RTC set for local + * time or for Universal Standard Time (GMT). Probably local though. + */ + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04lu\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); + + p += sprintf(p, + "DST_enable\t: no\n" + "BCD\t\t: yes\n" + "24hr\t\t: yes\n" ); + + return p - buf; +} + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = rtc_proc_output (page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static void get_rtc_time(struct rtc_time *rtc_tm) +{ + mf_getRtc( rtc_tm ); + + rtc_tm->tm_mon--; +} + + diff -Nru a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile --- a/arch/ppc/kernel/Makefile Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/Makefile Tue Feb 19 18:08:59 2002 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.Makefile 1.34 10/16/01 15:58:42 trini +# BK Id: %F% %I% %G% %U% %#% # # # Makefile for the linux kernel. @@ -14,17 +14,24 @@ ifdef CONFIG_PPC64BRIDGE EXTRA_AFLAGS := -Wa,-mppc64bridge endif +ifdef CONFIG_4xx +EXTRA_AFLAGS := -Wa,-m405 +endif + +CFLAGS_prom_init.o += -mrelocatable-lib +CFLAGS_btext.o += -mrelocatable-lib # Start off with 'head.o', change as needed. HEAD-y := head.o HEAD-$(CONFIG_4xx) := head_4xx.o HEAD-$(CONFIG_8xx) := head_8xx.o +HEAD-$(CONFIG_PPC_ISERIES) := iSeries_head.o all: $(HEAD-y) kernel.o O_TARGET := kernel.o -export-objs := ppc_ksyms.o prep_setup.o time.o +export-objs := ppc_ksyms.o time.o ppc405_dma.o obj-y := entry.o traps.o irq.o idle.o time.o misc.o \ process.o signal.o ptrace.o align.o \ @@ -32,16 +39,20 @@ cputable.o ppc_htab.o obj-$(CONFIG_6xx) += l2cr.o obj-$(CONFIG_MODULES) += ppc_ksyms.o -obj-$(CONFIG_POWER4) += xics.o -obj-$(CONFIG_PCI) += pci.o pci-dma.o +obj-$(CONFIG_PCI) += pci.o +ifneq ($(CONFIG_PPC_ISERIES),y) +obj-$(CONFIG_PCI) += pci-dma.o +endif obj-$(CONFIG_KGDB) += ppc-stub.o obj-$(CONFIG_SMP) += smp.o -obj-$(CONFIG_4xx) += ppc4xx_pic.o -obj-$(CONFIG_OAK) += oak_setup.o -obj-$(CONFIG_WALNUT) += walnut_setup.o obj-$(CONFIG_TAU) += temp.o -ifeq ($(CONFIG_WALNUT),y) -obj-$(CONFIG_PCI) += galaxy_pci.o +ifeq ($(CONFIG_4xx),y) +obj-$(CONFIG_4xx) += ppc4xx_setup.o ppc4xx_pic.o ppc4xx_serial.o +obj-$(CONFIG_PPC_RTC) += todc_time.o +obj-$(CONFIG_KGDB) += ppc4xx_kgdb.o +obj-$(CONFIG_405_DMA) += ppc405_dma.o +obj-$(CONFIG_PCI) += ppc405_pci.o indirect_pci.o pci_auto.o +obj-$(CONFIG_PM) += ppc4xx_pm.o endif obj-$(CONFIG_8xx) += m8xx_setup.o ppc8xx_pic.o ifeq ($(CONFIG_8xx),y) @@ -51,28 +62,41 @@ endif endif obj-$(CONFIG_MBX) += i8259.o -obj-$(CONFIG_APUS) += apus_setup.o -ifeq ($(CONFIG_APUS),y) -obj-$(CONFIG_PCI) += apus_pci.o -endif -obj-$(CONFIG_ALL_PPC) += pmac_pic.o pmac_setup.o pmac_time.o prom.o \ - feature.o pmac_pci.o chrp_setup.o \ - chrp_time.o chrp_pci.o open_pic.o \ - indirect_pci.o i8259.o prep_pci.o \ - prep_time.o prep_nvram.o prep_setup.o -obj-$(CONFIG_NVRAM) += pmac_nvram.o -obj-$(CONFIG_PMAC_BACKLIGHT) += pmac_backlight.o -obj-$(CONFIG_PMAC_PBOOK) += sleep.o -obj-$(CONFIG_PREP_RESIDUAL) += residual.o -obj-$(CONFIG_PPC_RTAS) += error_log.o proc_rtas.o -obj-$(CONFIG_GEMINI) += gemini_prom.o gemini_pci.o gemini_setup.o \ - open_pic.o +obj-$(CONFIG_ALL_PPC) += prom_init.o prom.o open_pic.o \ + indirect_pci.o i8259.o +obj-$(CONFIG_ADIR) += i8259.o indirect_pci.o pci_auto.o \ + todc_time.o +obj-$(CONFIG_EV64260) += gt64260_common.o gt64260_pic.o \ + indirect_pci.o todc_time.o pci_auto.o +obj-$(CONFIG_GEMINI) += open_pic.o +obj-$(CONFIG_K2) += i8259.o indirect_pci.o todc_time.o \ + pci_auto.o +obj-$(CONFIG_LOPEC) += mpc10x_common.o indirect_pci.o pci_auto.o \ + open_pic.o i8259.o todc_time.o +obj-$(CONFIG_MCPN765) += todc_time.o indirect_pci.o pci_auto.o \ + open_pic.o i8259.o pplus_common.o +obj-$(CONFIG_MENF1) += todc_time.o i8259.o mpc10x_common.o \ + pci_auto.o indirect_pci.o +obj-$(CONFIG_MVME5100) += open_pic.o todc_time.o indirect_pci.o \ + i8259.o pci_auto.o pplus_common.o +obj-$(CONFIG_PCORE) += mpc10x_common.o todc_time.o i8259.o \ + indirect_pci.o pci_auto.o +obj-$(CONFIG_POWERPMC250) += open_pic.o mpc10x_common.o \ + indirect_pci.o pci_auto.o +obj-$(CONFIG_PPLUS) += pplus_common.o open_pic.o i8259.o \ + indirect_pci.o todc_time.o pci_auto.o +obj-$(CONFIG_PRPMC750) += open_pic.o indirect_pci.o pci_auto.o \ + pplus_common.o +obj-$(CONFIG_PRPMC800) += open_pic.o indirect_pci.o pci_auto.o \ + pplus_common.o harrier.o +obj-$(CONFIG_SANDPOINT) += i8259.o open_pic.o mpc10x_common.o \ + pci_auto.o indirect_pci.o todc_time.o +obj-$(CONFIG_SPRUCE) += indirect_pci.o pci_auto.o todc_time.o +obj-$(CONFIG_ZX4500) += indirect_pci.o pci_auto.o mpc10x_common.o \ + i8259.o open_pic.o obj-$(CONFIG_8260) += m8260_setup.o ppc8260_pic.o obj-$(CONFIG_BOOTX_TEXT) += btext.o - -ifeq ($(CONFIG_SMP),y) -obj-$(CONFIG_ALL_PPC) += pmac_smp.o chrp_smp.o -endif +obj-$(CONFIG_PPC_ISERIES) += iSeries_misc.o include $(TOPDIR)/Rules.make @@ -82,7 +106,8 @@ head.o: head.S ppc_defs.h head_4xx.o: head_4xx.S ppc_defs.h head_8xx.o: head_8xx.S ppc_defs.h -gemini_prom.o: gemini_prom.S ppc_defs.h +iSeries_head.o: iSeries_head.S ppc_defs.h +iSeries_misc.o: iSeries_misc.S ppc_defs.h ppc_defs.h: mk_defs.c ppc_defs.head \ $(TOPDIR)/include/asm/mmu.h \ @@ -98,8 +123,3 @@ find_name : find_name.c $(HOSTCC) $(HOSTCFLAGS) -o find_name find_name.c - -checks: checks.c - $(HOSTCC) -I$(HPATH) $(HOSTCFLAGS) -D__KERNEL__ -fno-builtin -o checks checks.c - ./checks - diff -Nru a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c --- a/arch/ppc/kernel/align.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/align.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.align.c 1.5 05/17/01 18:14:21 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * align.c - handle alignment exceptions for the Power PC. @@ -24,7 +24,7 @@ unsigned char flags; }; -#if defined(CONFIG_4xx) || defined(CONFIG_POWER4) +#if defined(CONFIG_4xx) #define OPCD(inst) (((inst) & 0xFC000000) >> 26) #define RS(inst) (((inst) & 0x03E00000) >> 21) #define RA(inst) (((inst) & 0x001F0000) >> 16) @@ -187,7 +187,7 @@ fix_alignment(struct pt_regs *regs) { int instr, nb, flags; -#if defined(CONFIG_4xx) || defined(CONFIG_POWER4) +#if defined(CONFIG_4xx) int opcode, f1, f2, f3; #endif int i, t; @@ -200,11 +200,9 @@ unsigned char v[8]; } data; -#if defined(CONFIG_4xx) || defined(CONFIG_POWER4) +#if defined(CONFIG_4xx) /* The 4xx-family processors have no DSISR register, * so we emulate it. - * The POWER4 has a DSISR register but doesn't set it on - * an alignment fault. -- paulus */ instr = *((unsigned int *)regs->nip); diff -Nru a/arch/ppc/kernel/apus_pci.c b/arch/ppc/kernel/apus_pci.c --- a/arch/ppc/kernel/apus_pci.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,187 +0,0 @@ -/* - * BK Id: SCCS/s.apus_pci.c 1.5 09/08/01 15:47:42 paulus - */ -/* - * Copyright (C) Michel Dänzer - * - * APUS PCI routines. - * - * Currently, only B/CVisionPPC cards (Permedia2) are supported. - * - * Thanks to Geert Uytterhoeven for the idea: - * Read values from given config space(s) for the first devices, -1 otherwise - * - */ - -#include -#ifdef CONFIG_AMIGA - -#include -#include -#include -#include - -#include -#include -#include - -#include "apus_pci.h" - - -/* These definitions are mostly adapted from pm2fb.c */ - -#undef APUS_PCI_MASTER_DEBUG -#ifdef APUS_PCI_MASTER_DEBUG -#define DPRINTK(a,b...) printk(KERN_DEBUG "apus_pci: %s: " a, __FUNCTION__ , ## b) -#else -#define DPRINTK(a,b...) -#endif - -/* - * The _DEFINITIVE_ memory mapping/unmapping functions. - * This is due to the fact that they're changing soooo often... - */ -#define DEFW() wmb() -#define DEFR() rmb() -#define DEFRW() mb() - -#define DEVNO(d) ((d)>>3) -#define FNNO(d) ((d)&7) - - -extern unsigned long powerup_PCI_present; - -static struct pci_controller *apus_hose; - - -void *pci_io_base(unsigned int bus) -{ - return 0; -} - - -#define cfg_read(val, addr, type, op) *val = op((type)(addr)) -#define cfg_write(val, addr, type, op) op((val), (type *)(addr)); DEFW() -#define cfg_read_bad *val = ~0; -#define cfg_write_bad ; -#define cfg_read_val(val) *val -#define cfg_write_val(val) val - -#define APUS_PCI_OP(rw, size, type, op, mask) \ -int \ -apus_pcibios_##rw##_config_##size(struct pci_dev *dev, int offset, type val) \ -{ \ - int fnno = FNNO(dev->devfn); \ - int devno = DEVNO(dev->devfn); \ - \ - if (dev->bus->number > 0 || devno != 1) { \ - cfg_##rw##_bad; \ - return PCIBIOS_DEVICE_NOT_FOUND; \ - } \ - /* base address + function offset + offset ^ endianness conversion */ \ - cfg_##rw(val, apus_hose->cfg_data + (fnno<<5) + (offset ^ mask), \ - type, op); \ - \ - DPRINTK(#op " b: 0x%x, d: 0x%x, f: 0x%x, o: 0x%x, v: 0x%x\n", \ - dev->bus->number, dev->devfn>>3, dev->devfn&7, \ - offset, cfg_##rw##_val(val)); \ - return PCIBIOS_SUCCESSFUL; \ -} - -APUS_PCI_OP(read, byte, u8 *, readb, 3) -APUS_PCI_OP(read, word, u16 *, readw, 2) -APUS_PCI_OP(read, dword, u32 *, readl, 0) -APUS_PCI_OP(write, byte, u8, writeb, 3) -APUS_PCI_OP(write, word, u16, writew, 2) -APUS_PCI_OP(write, dword, u32, writel, 0) - - -static struct pci_ops apus_pci_ops = { - apus_pcibios_read_config_byte, - apus_pcibios_read_config_word, - apus_pcibios_read_config_dword, - apus_pcibios_write_config_byte, - apus_pcibios_write_config_word, - apus_pcibios_write_config_dword -}; - -static struct resource pci_mem = { "B/CVisionPPC PCI mem", CVPPC_FB_APERTURE_ONE, CVPPC_PCI_CONFIG, IORESOURCE_MEM }; - -void __init -apus_pcibios_fixup(void) -{ -/* struct pci_dev *dev = pci_find_slot(0, 1<<3); - unsigned int reg, val, offset;*/ - - /* FIXME: interrupt? */ - /*dev->interrupt = xxx;*/ - - request_resource(&iomem_resource, &pci_mem); - printk("%s: PCI mem resource requested\n", __FUNCTION__); -} - -static void __init apus_pcibios_fixup_bus(struct pci_bus *bus) -{ - bus->resource[1] = &pci_mem; -} - - -/* - * This is from pm2fb.c again - * - * Check if PCI (B/CVisionPPC) is available, initialize it and set up - * the pcibios_* pointers - */ - - -void __init -apus_setup_pci_ptrs(void) -{ - if (!powerup_PCI_present) { - DPRINTK("no PCI bridge detected\n"); - return; - } - DPRINTK("Phase5 B/CVisionPPC PCI bridge detected.\n"); - - apus_hose = pcibios_alloc_controller(); - if (!apus_hose) { - printk("apus_pci: Can't allocate PCI controller structure\n"); - return; - } - - if (!(apus_hose->cfg_data = ioremap(CVPPC_PCI_CONFIG, 256))) { - printk("apus_pci: unable to map PCI config region\n"); - return; - } - - if (!(apus_hose->cfg_addr = ioremap(CSPPC_PCI_BRIDGE, 256))) { - printk("apus_pci: unable to map PCI bridge\n"); - return; - } - - writel(CSPPCF_BRIDGE_BIG_ENDIAN, apus_hose->cfg_addr + CSPPC_BRIDGE_ENDIAN); - DEFW(); - - writel(CVPPC_REGS_REGION, apus_hose->cfg_data+ PCI_BASE_ADDRESS_0); - DEFW(); - writel(CVPPC_FB_APERTURE_ONE, apus_hose->cfg_data + PCI_BASE_ADDRESS_1); - DEFW(); - writel(CVPPC_FB_APERTURE_TWO, apus_hose->cfg_data + PCI_BASE_ADDRESS_2); - DEFW(); - writel(CVPPC_ROM_ADDRESS, apus_hose->cfg_data + PCI_ROM_ADDRESS); - DEFW(); - - writel(0xef000000 | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER, apus_hose->cfg_data + PCI_COMMAND); - DEFW(); - - apus_hose->first_busno = 0; - apus_hose->last_busno = 0; - apus_hose->ops = &apus_pci_ops; - ppc_md.pcibios_fixup = apus_pcibios_fixup; - ppc_md.pcibios_fixup_bus = apus_pcibios_fixup_bus; - - return; -} - -#endif /* CONFIG_AMIGA */ diff -Nru a/arch/ppc/kernel/apus_pci.h b/arch/ppc/kernel/apus_pci.h --- a/arch/ppc/kernel/apus_pci.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,40 +0,0 @@ -/* - * BK Id: SCCS/s.apus_pci.h 1.4 05/17/01 18:14:21 cort - */ -/* - * Phase5 CybervisionPPC (TVP4020) definitions for the Permedia2 framebuffer - * driver. - * - * Copyright (c) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) - * -------------------------------------------------------------------------- - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file README.legal in the main directory of this archive - * for more details. - */ - -#ifndef APUS_PCI_H -#define APUS_PCI_H - - -#include "pci.h" - - -#define CSPPC_PCI_BRIDGE 0xfffe0000 -#define CSPPC_BRIDGE_ENDIAN 0x0000 -#define CSPPC_BRIDGE_INT 0x0010 - -#define CVPPC_PCI_CONFIG 0xfffc0000 -#define CVPPC_ROM_ADDRESS 0xe2000001 -#define CVPPC_REGS_REGION 0xef000000 -#define CVPPC_FB_APERTURE_ONE 0xe0000000 -#define CVPPC_FB_APERTURE_TWO 0xe1000000 -#define CVPPC_FB_SIZE 0x00800000 - -/* CVPPC_BRIDGE_ENDIAN */ -#define CSPPCF_BRIDGE_BIG_ENDIAN 0x02 - -/* CVPPC_BRIDGE_INT */ -#define CSPPCF_BRIDGE_ACTIVE_INT2 0x01 - - -#endif /* APUS_PCI_H */ diff -Nru a/arch/ppc/kernel/apus_setup.c b/arch/ppc/kernel/apus_setup.c --- a/arch/ppc/kernel/apus_setup.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1113 +0,0 @@ -/* - * BK Id: SCCS/s.apus_setup.c 1.24 11/13/01 21:26:07 paulus - */ -/* - * linux/arch/ppc/kernel/apus_setup.c - * - * Copyright (C) 1998, 1999 Jesper Skov - * - * Basically what is needed to replace functionality found in - * arch/m68k allowing Amiga drivers to work under APUS. - * Bits of code and/or ideas from arch/m68k and arch/ppc files. - * - * TODO: - * This file needs a *really* good cleanup. Restructure and optimize. - * Make sure it can be compiled for non-APUS configs. Begin to move - * Amiga specific stuff into mach/amiga. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_APUS -#include -#endif - -/* Needs INITSERIAL call in head.S! */ -#undef APUS_DEBUG - - -#include -#define T_CHAR (0x0000) /* char: don't touch */ -#define T_SHORT (0x4000) /* short: 12 -> 21 */ -#define T_INT (0x8000) /* int: 1234 -> 4321 */ -#define T_TEXT (0xc000) /* text: 12 -> 21 */ - -#define T_MASK_TYPE (0xc000) -#define T_MASK_COUNT (0x3fff) - -#define D_CHAR(cnt) (T_CHAR | (cnt)) -#define D_SHORT(cnt) (T_SHORT | (cnt)) -#define D_INT(cnt) (T_INT | (cnt)) -#define D_TEXT(cnt) (T_TEXT | (cnt)) - -static u_short driveid_types[] = { - D_SHORT(10), /* config - vendor2 */ - D_TEXT(20), /* serial_no */ - D_SHORT(3), /* buf_type, buf_size - ecc_bytes */ - D_TEXT(48), /* fw_rev - model */ - D_CHAR(2), /* max_multsect - vendor3 */ - D_SHORT(1), /* dword_io */ - D_CHAR(2), /* vendor4 - capability */ - D_SHORT(1), /* reserved50 */ - D_CHAR(4), /* vendor5 - tDMA */ - D_SHORT(4), /* field_valid - cur_sectors */ - D_INT(1), /* cur_capacity */ - D_CHAR(2), /* multsect - multsect_valid */ - D_INT(1), /* lba_capacity */ - D_SHORT(194) /* dma_1word - reservedyy */ -}; - -#define num_driveid_types (sizeof(driveid_types)/sizeof(*driveid_types)) - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "local_irq.h" - -unsigned long m68k_machtype; -char debug_device[6] = ""; - -extern void amiga_init_IRQ(void); - -void (*mach_sched_init) (void (*handler)(int, void *, struct pt_regs *)) __initdata = NULL; -/* machine dependent keyboard functions */ -int (*mach_keyb_init) (void) __initdata = NULL; -int (*mach_kbdrate) (struct kbd_repeat *) = NULL; -void (*mach_kbd_leds) (unsigned int) = NULL; -/* machine dependent irq functions */ -void (*mach_init_IRQ) (void) __initdata = NULL; -void (*(*mach_default_handler)[]) (int, void *, struct pt_regs *) = NULL; -void (*mach_get_model) (char *model) = NULL; -int (*mach_get_hardware_list) (char *buffer) = NULL; -int (*mach_get_irq_list) (struct seq_file *, void *) = NULL; -void (*mach_process_int) (int, struct pt_regs *) = NULL; -/* machine dependent timer functions */ -unsigned long (*mach_gettimeoffset) (void); -void (*mach_gettod) (int*, int*, int*, int*, int*, int*); -int (*mach_hwclk) (int, struct hwclk_time*) = NULL; -int (*mach_set_clock_mmss) (unsigned long) = NULL; -void (*mach_reset)( void ); -long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */ -#if defined(CONFIG_AMIGA_FLOPPY) -void (*mach_floppy_setup) (char *, int *) __initdata = NULL; -#endif -#ifdef CONFIG_HEARTBEAT -void (*mach_heartbeat) (int) = NULL; -extern void apus_heartbeat (void); -#endif - -extern unsigned long amiga_model; -extern unsigned decrementer_count;/* count value for 1e6/HZ microseconds */ -extern unsigned count_period_num; /* 1 decrementer count equals */ -extern unsigned count_period_den; /* count_period_num / count_period_den us */ - -int num_memory = 0; -struct mem_info memory[NUM_MEMINFO];/* memory description */ -/* FIXME: Duplicate memory data to avoid conflicts with m68k shared code. */ -int m68k_realnum_memory = 0; -struct mem_info m68k_memory[NUM_MEMINFO];/* memory description */ - -struct mem_info ramdisk; - -extern void amiga_floppy_setup(char *, int *); -extern void config_amiga(void); - -static int __60nsram = 0; - -/* for cpuinfo */ -static int __bus_speed = 0; -static int __speed_test_failed = 0; - -/********************************************** COMPILE PROTECTION */ -/* Provide some stubs that links to Amiga specific functions. - * This allows CONFIG_APUS to be removed from generic PPC files while - * preventing link errors for other PPC targets. - */ -unsigned long apus_get_rtc_time(void) -{ -#ifdef CONFIG_APUS - extern unsigned long m68k_get_rtc_time(void); - - return m68k_get_rtc_time (); -#else - return 0; -#endif -} - -int apus_set_rtc_time(unsigned long nowtime) -{ -#ifdef CONFIG_APUS - extern int m68k_set_rtc_time(unsigned long nowtime); - - return m68k_set_rtc_time (nowtime); -#else - return 0; -#endif -} - - - -/* Here some functions we don't support, but which the other ports reference */ -int pckbd_setkeycode(unsigned int scancode, unsigned int keycode) -{ - printk("Bogus call to " __FILE__ ":" __FUNCTION__ "\n"); - return 0; -} -int pckbd_getkeycode(unsigned int scancode) -{ - printk("Bogus call to " __FILE__ ":" __FUNCTION__ "\n"); - return 0; -} -int pckbd_translate(unsigned char scancode, unsigned char *keycode, - char raw_mode) -{ - printk("Bogus call to " __FILE__ ":" __FUNCTION__ "\n"); - return 0; -} -char pckbd_unexpected_up(unsigned char keycode) -{ - printk("Bogus call to " __FILE__ ":" __FUNCTION__ "\n"); - return 0; -} -void pckbd_leds(unsigned char leds) -{ - printk("Bogus call to " __FILE__ ":" __FUNCTION__ "\n"); -} -void pckbd_init_hw(void) -{ - printk("Bogus call to " __FILE__ ":" __FUNCTION__ "\n"); -} -unsigned char pckbd_sysrq_xlate[128]; - -struct pci_bus * __init pci_scan_peer_bridge(int bus) -{ - printk("Bogus call to " __FILE__ ":" __FUNCTION__ "\n"); - return NULL; -} - -/*********************************************************** SETUP */ -/* From arch/m68k/kernel/setup.c. */ -void __init apus_setup_arch(void) -{ -#ifdef CONFIG_APUS - extern char cmd_line[]; - int i; - char *p, *q; - - /* Let m68k-shared code know it should do the Amiga thing. */ - m68k_machtype = MACH_AMIGA; - - /* Parse the command line for arch-specific options. - * For the m68k, this is currently only "debug=xxx" to enable printing - * certain kernel messages to some machine-specific device. */ - for( p = cmd_line; p && *p; ) { - i = 0; - if (!strncmp( p, "debug=", 6 )) { - strncpy( debug_device, p+6, sizeof(debug_device)-1 ); - debug_device[sizeof(debug_device)-1] = 0; - if ((q = strchr( debug_device, ' ' ))) *q = 0; - i = 1; - } else if (!strncmp( p, "60nsram", 7 )) { - APUS_WRITE (APUS_REG_WAITSTATE, - REGWAITSTATE_SETRESET - |REGWAITSTATE_PPCR - |REGWAITSTATE_PPCW); - __60nsram = 1; - i = 1; - } - - if (i) { - /* option processed, delete it */ - if ((q = strchr( p, ' ' ))) - strcpy( p, q+1 ); - else - *p = 0; - } else { - if ((p = strchr( p, ' ' ))) ++p; - } - } - - config_amiga(); - -#if 0 /* Enable for logging - also include logging.o in Makefile rule */ - { -#define LOG_SIZE 4096 - void* base; - - /* Throw away some memory - the P5 firmare stomps on top - * of CHIP memory during bootup. - */ - amiga_chip_alloc(0x1000); - - base = amiga_chip_alloc(LOG_SIZE+sizeof(klog_data_t)); - LOG_INIT(base, base+sizeof(klog_data_t), LOG_SIZE); - } -#endif -#endif -} - -int -apus_show_cpuinfo(struct seq_file *m) -{ - extern int __map_without_bats; - extern unsigned long powerup_PCI_present; - - seq_printf(m, "machine\t\t: Amiga\n"); - seq_printf(m, "bus speed\t: %d%s", __bus_speed, - (__speed_test_failed) ? " [failed]\n" : "\n"); - seq_printf(m, "using BATs\t: %s\n", - (__map_without_bats) ? "No" : "Yes"); - seq_printf(m, "ram speed\t: %dns\n", (__60nsram) ? 60 : 70); - seq_printf(m, "PCI bridge\t: %s\n", - (powerup_PCI_present) ? "Yes" : "No"); - return 0; -} - -static void get_current_tb(unsigned long long *time) -{ - __asm __volatile ("1:mftbu 4 \n\t" - " mftb 5 \n\t" - " mftbu 6 \n\t" - " cmpw 4,6 \n\t" - " bne 1b \n\t" - " stw 4,0(%0)\n\t" - " stw 5,4(%0)\n\t" - : - : "r" (time) - : "r4", "r5", "r6"); -} - - -void apus_calibrate_decr(void) -{ -#ifdef CONFIG_APUS - unsigned long freq; - - /* This algorithm for determining the bus speed was - contributed by Ralph Schmidt. */ - unsigned long long start, stop; - int bus_speed; - int speed_test_failed = 0; - - { - unsigned long loop = amiga_eclock / 10; - - get_current_tb (&start); - while (loop--) { - unsigned char tmp; - - tmp = ciaa.pra; - } - get_current_tb (&stop); - } - - bus_speed = (((unsigned long)(stop-start))*10*4) / 1000000; - if (AMI_1200 == amiga_model) - bus_speed /= 2; - - if ((bus_speed >= 47) && (bus_speed < 53)) { - bus_speed = 50; - freq = 12500000; - } else if ((bus_speed >= 57) && (bus_speed < 63)) { - bus_speed = 60; - freq = 15000000; - } else if ((bus_speed >= 63) && (bus_speed < 69)) { - bus_speed = 67; - freq = 16666667; - } else { - printk ("APUS: Unable to determine bus speed (%d). " - "Defaulting to 50MHz", bus_speed); - bus_speed = 50; - freq = 12500000; - speed_test_failed = 1; - } - - /* Ease diagnostics... */ - { - extern int __map_without_bats; - extern unsigned long powerup_PCI_present; - - printk ("APUS: BATs=%d, BUS=%dMHz", - (__map_without_bats) ? 0 : 1, - bus_speed); - if (speed_test_failed) - printk ("[FAILED - please report]"); - - printk (", RAM=%dns, PCI bridge=%d\n", - (__60nsram) ? 60 : 70, - (powerup_PCI_present) ? 1 : 0); - - /* print a bit more if asked politely... */ - if (!(ciaa.pra & 0x40)){ - extern unsigned int bat_addrs[4][3]; - int b; - for (b = 0; b < 4; ++b) { - printk ("APUS: BAT%d ", b); - printk ("%08x-%08x -> %08x\n", - bat_addrs[b][0], - bat_addrs[b][1], - bat_addrs[b][2]); - } - } - - } - - printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", - freq/1000000, freq%1000000); - tb_ticks_per_jiffy = freq / HZ; - tb_to_us = mulhwu_scale_factor(freq, 1000000); - - __bus_speed = bus_speed; - __speed_test_failed = speed_test_failed; -#endif -} - -void arch_gettod(int *year, int *mon, int *day, int *hour, - int *min, int *sec) -{ -#ifdef CONFIG_APUS - if (mach_gettod) - mach_gettod(year, mon, day, hour, min, sec); - else - *year = *mon = *day = *hour = *min = *sec = 0; -#endif -} - -/* for "kbd-reset" cmdline param */ -__init -void kbd_reset_setup(char *str, int *ints) -{ -} - -/*********************************************************** FLOPPY */ -#if defined(CONFIG_AMIGA_FLOPPY) -__init -void floppy_setup(char *str, int *ints) -{ - if (mach_floppy_setup) - mach_floppy_setup (str, ints); -} -#endif - -/*********************************************************** MEMORY */ -#define KMAP_MAX 32 -unsigned long kmap_chunks[KMAP_MAX*3]; -int kmap_chunk_count = 0; - -/* From pgtable.h */ -static __inline__ pte_t *my_find_pte(struct mm_struct *mm,unsigned long va) -{ - pgd_t *dir = 0; - pmd_t *pmd = 0; - pte_t *pte = 0; - - va &= PAGE_MASK; - - dir = pgd_offset( mm, va ); - if (dir) - { - pmd = pmd_offset(dir, va & PAGE_MASK); - if (pmd && pmd_present(*pmd)) - { - pte = pte_offset(pmd, va); - } - } - return pte; -} - - -/* Again simulating an m68k/mm/kmap.c function. */ -void kernel_set_cachemode( unsigned long address, unsigned long size, - unsigned int cmode ) -{ - unsigned long mask, flags; - - switch (cmode) - { - case IOMAP_FULL_CACHING: - mask = ~(_PAGE_NO_CACHE | _PAGE_GUARDED); - flags = 0; - break; - case IOMAP_NOCACHE_SER: - mask = ~0; - flags = (_PAGE_NO_CACHE | _PAGE_GUARDED); - break; - default: - panic ("kernel_set_cachemode() doesn't support mode %d\n", - cmode); - break; - } - - size /= PAGE_SIZE; - address &= PAGE_MASK; - while (size--) - { - pte_t *pte; - - pte = my_find_pte(&init_mm, address); - if ( !pte ) - { - printk("pte NULL in kernel_set_cachemode()\n"); - return; - } - - pte_val (*pte) &= mask; - pte_val (*pte) |= flags; - flush_tlb_page(find_vma(&init_mm,address),address); - - address += PAGE_SIZE; - } -} - -unsigned long mm_ptov (unsigned long paddr) -{ - unsigned long ret; - if (paddr < 16*1024*1024) - ret = ZTWO_VADDR(paddr); - else { - int i; - - for (i = 0; i < kmap_chunk_count;){ - unsigned long phys = kmap_chunks[i++]; - unsigned long size = kmap_chunks[i++]; - unsigned long virt = kmap_chunks[i++]; - if (paddr >= phys - && paddr < (phys + size)){ - ret = virt + paddr - phys; - goto exit; - } - } - - ret = (unsigned long) __va(paddr); - } -exit: -#ifdef DEBUGPV - printk ("PTOV(%lx)=%lx\n", paddr, ret); -#endif - return ret; -} - -int mm_end_of_chunk (unsigned long addr, int len) -{ - if (memory[0].addr + memory[0].size == addr + len) - return 1; - return 0; -} - -/*********************************************************** CACHE */ - -#define L1_CACHE_BYTES 32 -#define MAX_CACHE_SIZE 8192 -void cache_push(__u32 addr, int length) -{ - addr = mm_ptov(addr); - - if (MAX_CACHE_SIZE < length) - length = MAX_CACHE_SIZE; - - while(length > 0){ - __asm ("dcbf 0,%0\n\t" - : : "r" (addr)); - addr += L1_CACHE_BYTES; - length -= L1_CACHE_BYTES; - } - /* Also flush trailing block */ - __asm ("dcbf 0,%0\n\t" - "sync \n\t" - : : "r" (addr)); -} - -void cache_clear(__u32 addr, int length) -{ - if (MAX_CACHE_SIZE < length) - length = MAX_CACHE_SIZE; - - addr = mm_ptov(addr); - - __asm ("dcbf 0,%0\n\t" - "sync \n\t" - "icbi 0,%0 \n\t" - "isync \n\t" - : : "r" (addr)); - - addr += L1_CACHE_BYTES; - length -= L1_CACHE_BYTES; - - while(length > 0){ - __asm ("dcbf 0,%0\n\t" - "sync \n\t" - "icbi 0,%0 \n\t" - "isync \n\t" - : : "r" (addr)); - addr += L1_CACHE_BYTES; - length -= L1_CACHE_BYTES; - } - - __asm ("dcbf 0,%0\n\t" - "sync \n\t" - "icbi 0,%0 \n\t" - "isync \n\t" - : : "r" (addr)); -} - -/****************************************************** from setup.c */ -void -apus_restart(char *cmd) -{ - cli(); - - APUS_WRITE(APUS_REG_LOCK, - REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK2); - APUS_WRITE(APUS_REG_LOCK, - REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK3); - APUS_WRITE(APUS_REG_LOCK, - REGLOCK_BLACKMAGICK2|REGLOCK_BLACKMAGICK3); - APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); - APUS_WRITE(APUS_REG_RESET, REGRESET_AMIGARESET); - for(;;); -} - -void -apus_power_off(void) -{ - for (;;); -} - -void -apus_halt(void) -{ - apus_restart(NULL); -} - -/****************************************************** from setup.c/IDE */ -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) -/* - * IDE stuff. - */ - -#if 0 /* no longer used -- paulus */ -void -apus_ide_fix_driveid(struct hd_driveid *id) -{ - u_char *p = (u_char *)id; - int i, j, cnt; - u_char t; - - if (!MACH_IS_AMIGA && !MACH_IS_MAC) - return; - for (i = 0; i < num_driveid_types; i++) { - cnt = driveid_types[i] & T_MASK_COUNT; - switch (driveid_types[i] & T_MASK_TYPE) { - case T_CHAR: - p += cnt; - break; - case T_SHORT: - for (j = 0; j < cnt; j++) { - t = p[0]; - p[0] = p[1]; - p[1] = t; - p += 2; - } - break; - case T_INT: - for (j = 0; j < cnt; j++) { - t = p[0]; - p[0] = p[3]; - p[3] = t; - t = p[1]; - p[1] = p[2]; - p[2] = t; - p += 4; - } - break; - case T_TEXT: - for (j = 0; j < cnt; j += 2) { - t = p[0]; - p[0] = p[1]; - p[1] = t; - p += 2; - } - break; - } - } -} -#endif /* 0 */ - -__init -void apus_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, - ide_ioreg_t ctrl_port, int *irq) -{ - if (data_port || ctrl_port) - printk("apus_ide_init_hwif_ports: must not be called\n"); -} -#endif -/****************************************************** IRQ stuff */ - -static unsigned int apus_irq_cannonicalize(unsigned int irq) -{ - return irq; -} - -int show_apus_interrupts(struct seq_file *p, void *v) -{ -#ifdef CONFIG_APUS - extern int show_amiga_interrupts(struct seq_file *p, void *v) - - return show_amiga_interrupts(p, v); -#else - return 0; -#endif -} - -/* IPL must be between 0 and 7 */ -static inline void apus_set_IPL(unsigned long ipl) -{ - APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_DISABLEINT); - APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); - APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | ((~ipl) & IPLEMU_IPLMASK)); - APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); -} - -static inline unsigned long apus_get_IPL(void) -{ - /* This returns the present IPL emulation level. */ - unsigned long __f; - APUS_READ(APUS_IPL_EMU, __f); - return ((~__f) & IPLEMU_IPLMASK); -} - -static inline unsigned long apus_get_prev_IPL(struct pt_regs* regs) -{ - /* The value saved in mq is the IPL_EMU value at the time of - interrupt. The lower bits are the current interrupt level, - the upper bits the requested level. Thus, to restore the - IPL level to the post-interrupt state, we will need to use - the lower bits. */ - unsigned long __f = regs->mq; - return ((~__f) & IPLEMU_IPLMASK); -} - - -#ifdef CONFIG_APUS -void free_irq(unsigned int irq, void *dev_id) -{ - extern void amiga_free_irq(unsigned int irq, void *dev_id); - - amiga_free_irq (irq, dev_id); -} - -int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char * devname, void *dev_id) -{ - extern int amiga_request_irq(unsigned int irq, - void (*handler)(int, void *, - struct pt_regs *), - unsigned long flags, - const char *devname, - void *dev_id); - - return amiga_request_irq (irq, handler, irqflags, devname, dev_id); -} - -/* In Linux/m68k the sys_request_irq deals with vectors 0-7. That's what - callers expect - but on Linux/APUS we actually use the IRQ_AMIGA_AUTO - vectors (24-31), so we put this dummy function in between to adjust - the vector argument (rather have cruft here than in the generic irq.c). */ -int sys_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char * devname, void *dev_id) -{ - extern int request_sysirq(unsigned int irq, - void (*handler)(int, void *, - struct pt_regs *), - unsigned long irqflags, - const char * devname, void *dev_id); - return request_sysirq(irq+IRQ_AMIGA_AUTO, handler, irqflags, - devname, dev_id); -} -#endif - -int apus_get_irq(struct pt_regs* regs) -{ -#ifdef CONFIG_APUS - int level = apus_get_IPL(); - -#ifdef __INTERRUPT_DEBUG - printk("<%d:%d>", level, apus_get_prev_IPL(regs)); -#endif - - if (0 == level) - return -8; - if (7 == level) - return -9; - - return level + IRQ_AMIGA_AUTO; -#else - return 0; -#endif -} - -void apus_post_irq(struct pt_regs* regs, int level) -{ -#ifdef __INTERRUPT_DEBUG - printk("{%d}", apus_get_prev_IPL(regs)); -#endif - /* Restore IPL to the previous value */ - apus_set_IPL(apus_get_prev_IPL(regs)); -} - -/****************************************************** keyboard */ -static int apus_kbd_setkeycode(unsigned int scancode, unsigned int keycode) -{ - return -EOPNOTSUPP; -} - -static int apus_kbd_getkeycode(unsigned int scancode) -{ - return scancode > 127 ? -EINVAL : scancode; -} - -static int apus_kbd_translate(unsigned char keycode, unsigned char *keycodep, - char raw_mode) -{ - *keycodep = keycode; - return 1; -} - -static char apus_kbd_unexpected_up(unsigned char keycode) -{ - return 0200; -} - -static void apus_kbd_leds(unsigned char leds) -{ -} - -static void apus_kbd_init_hw(void) -{ -#ifdef CONFIG_APUS - extern int amiga_keyb_init(void); - - amiga_keyb_init(); -#endif -} - - -/****************************************************** debugging */ - -/* some serial hardware definitions */ -#define SDR_OVRUN (1<<15) -#define SDR_RBF (1<<14) -#define SDR_TBE (1<<13) -#define SDR_TSRE (1<<12) - -#define AC_SETCLR (1<<15) -#define AC_UARTBRK (1<<11) - -#define SER_DTR (1<<7) -#define SER_RTS (1<<6) -#define SER_DCD (1<<5) -#define SER_CTS (1<<4) -#define SER_DSR (1<<3) - -static __inline__ void ser_RTSon(void) -{ - ciab.pra &= ~SER_RTS; /* active low */ -} - -int __debug_ser_out( unsigned char c ) -{ - custom.serdat = c | 0x100; - mb(); - while (!(custom.serdatr & 0x2000)) - barrier(); - return 1; -} - -unsigned char __debug_ser_in( void ) -{ - unsigned char c; - - /* XXX: is that ok?? derived from amiga_ser.c... */ - while( !(custom.intreqr & IF_RBF) ) - barrier(); - c = custom.serdatr; - /* clear the interrupt, so that another character can be read */ - custom.intreq = IF_RBF; - return c; -} - -int __debug_serinit( void ) -{ - unsigned long flags; - - save_flags (flags); - cli(); - - /* turn off Rx and Tx interrupts */ - custom.intena = IF_RBF | IF_TBE; - - /* clear any pending interrupt */ - custom.intreq = IF_RBF | IF_TBE; - - restore_flags (flags); - - /* - * set the appropriate directions for the modem control flags, - * and clear RTS and DTR - */ - ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ - ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ - -#ifdef CONFIG_KGDB - /* turn Rx interrupts on for GDB */ - custom.intena = IF_SETCLR | IF_RBF; - ser_RTSon(); -#endif - - return 0; -} - -void __debug_print_hex(unsigned long x) -{ - int i; - char hexchars[] = "0123456789ABCDEF"; - - for (i = 0; i < 8; i++) { - __debug_ser_out(hexchars[(x >> 28) & 15]); - x <<= 4; - } - __debug_ser_out('\n'); - __debug_ser_out('\r'); -} - -void __debug_print_string(char* s) -{ - unsigned char c; - while((c = *s++)) - __debug_ser_out(c); - __debug_ser_out('\n'); - __debug_ser_out('\r'); -} - -static void apus_progress(char *s, unsigned short value) -{ - __debug_print_string(s); -} - -/****************************************************** init */ - -/* The number of spurious interrupts */ -volatile unsigned int num_spurious; - -#define NUM_IRQ_NODES 100 -static irq_node_t nodes[NUM_IRQ_NODES]; - -extern void (*amiga_default_handler[AUTO_IRQS])(int, void *, struct pt_regs *); - -static const char *default_names[SYS_IRQS] = { - "spurious int", "int1 handler", "int2 handler", "int3 handler", - "int4 handler", "int5 handler", "int6 handler", "int7 handler" -}; - -irq_node_t *new_irq_node(void) -{ - irq_node_t *node; - short i; - - for (node = nodes, i = NUM_IRQ_NODES-1; i >= 0; node++, i--) - if (!node->handler) - return node; - - printk ("new_irq_node: out of nodes\n"); - return NULL; -} - -extern void amiga_enable_irq(unsigned int irq); -extern void amiga_disable_irq(unsigned int irq); - -struct hw_interrupt_type amiga_irqctrl = { - " Amiga ", - NULL, - NULL, - amiga_enable_irq, - amiga_disable_irq, - 0, - 0 -}; - -#define HARDWARE_MAPPED_SIZE (512*1024) -unsigned long __init apus_find_end_of_memory(void) -{ - int shadow = 0; - unsigned long total; - - /* The memory size reported by ADOS excludes the 512KB - reserved for PPC exception registers and possibly 512KB - containing a shadow of the ADOS ROM. */ - { - unsigned long size = memory[0].size; - - /* If 2MB aligned, size was probably user - specified. We can't tell anything about shadowing - in this case so skip shadow assignment. */ - if (0 != (size & 0x1fffff)){ - /* Align to 512KB to ensure correct handling - of both memfile and system specified - sizes. */ - size = ((size+0x0007ffff) & 0xfff80000); - /* If memory is 1MB aligned, assume - shadowing. */ - shadow = !(size & 0x80000); - } - - /* Add the chunk that ADOS does not see. by aligning - the size to the nearest 2MB limit upwards. */ - memory[0].size = ((size+0x001fffff) & 0xffe00000); - } - - total = memory[0].size; - - /* Remove the memory chunks that are controlled by special - Phase5 hardware. */ - - /* Remove the upper 512KB if it contains a shadow of - the ADOS ROM. FIXME: It might be possible to - disable this shadow HW. Check the booter - (ppc_boot.c) */ - if (shadow) - total -= HARDWARE_MAPPED_SIZE; - - /* Remove the upper 512KB where the PPC exception - vectors are mapped. */ - total -= HARDWARE_MAPPED_SIZE; - - /* Linux/APUS only handles one block of memory -- the one on - the PowerUP board. Other system memory is horrible slow in - comparison. The user can use other memory for swapping - using the z2ram device. */ - ram_phys_base = memory[0].addr; - return total; -} - -static void __init -apus_map_io(void) -{ - /* Map PPC exception vectors. */ - io_block_mapping(0xfff00000, 0xfff00000, 0x00020000, _PAGE_KERNEL); - /* Map chip and ZorroII memory */ - io_block_mapping(zTwoBase, 0x00000000, 0x01000000, _PAGE_IO); -} - -__init -void apus_init_IRQ(void) -{ - int i; - - for ( i = 0 ; i < NR_IRQS ; i++ ) - irq_desc[i].handler = &amiga_irqctrl; - - for (i = 0; i < NUM_IRQ_NODES; i++) - nodes[i].handler = NULL; - - for (i = 0; i < AUTO_IRQS; i++) { - if (amiga_default_handler[i] != NULL) - sys_request_irq(i, amiga_default_handler[i], - 0, default_names[i], NULL); - } - - amiga_init_IRQ(); - -} - -__init -void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - extern int parse_bootinfo(const struct bi_record *); - extern char _end[]; - - /* Parse bootinfo. The bootinfo is located right after - the kernel bss */ - parse_bootinfo((const struct bi_record *)&_end); -#ifdef CONFIG_BLK_DEV_INITRD - /* Take care of initrd if we have one. Use data from - bootinfo to avoid the need to initialize PPC - registers when kernel is booted via a PPC reset. */ - if ( ramdisk.addr ) { - initrd_start = (unsigned long) __va(ramdisk.addr); - initrd_end = (unsigned long) - __va(ramdisk.size + ramdisk.addr); - } -#endif /* CONFIG_BLK_DEV_INITRD */ - - ISA_DMA_THRESHOLD = 0x00ffffff; - - ppc_md.setup_arch = apus_setup_arch; - ppc_md.show_cpuinfo = apus_show_cpuinfo; - ppc_md.irq_cannonicalize = apus_irq_cannonicalize; - ppc_md.init_IRQ = apus_init_IRQ; - ppc_md.get_irq = apus_get_irq; - -#error Should use the ->end() member of irq_desc[x]. -- Cort - /*ppc_md.post_irq = apus_post_irq;*/ - -#ifdef CONFIG_HEARTBEAT - ppc_md.heartbeat = apus_heartbeat; - ppc_md.heartbeat_count = 1; -#endif -#ifdef APUS_DEBUG - __debug_serinit(); - ppc_md.progress = apus_progress; -#endif - ppc_md.init = NULL; - - ppc_md.restart = apus_restart; - ppc_md.power_off = apus_power_off; - ppc_md.halt = apus_halt; - - ppc_md.time_init = NULL; - ppc_md.set_rtc_time = apus_set_rtc_time; - ppc_md.get_rtc_time = apus_get_rtc_time; - ppc_md.calibrate_decr = apus_calibrate_decr; - - ppc_md.find_end_of_memory = apus_find_end_of_memory; - ppc_md.setup_io_mappings = apus_map_io; - - ppc_md.nvram_read_val = NULL; - ppc_md.nvram_write_val = NULL; - - /* These should not be used for the APUS yet, since it uses - the M68K keyboard now. */ - ppc_md.kbd_setkeycode = apus_kbd_setkeycode; - ppc_md.kbd_getkeycode = apus_kbd_getkeycode; - ppc_md.kbd_translate = apus_kbd_translate; - ppc_md.kbd_unexpected_up = apus_kbd_unexpected_up; - ppc_md.kbd_leds = apus_kbd_leds; - ppc_md.kbd_init_hw = apus_kbd_init_hw; - -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) - ppc_ide_md.ide_init_hwif = apus_ide_init_hwif_ports; -#endif -} - - -/*************************************************** coexistence */ -void __init adbdev_init(void) -{ -} diff -Nru a/arch/ppc/kernel/bitops.c b/arch/ppc/kernel/bitops.c --- a/arch/ppc/kernel/bitops.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/bitops.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.bitops.c 1.7 05/17/01 18:14:21 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * Copyright (C) 1996 Paul Mackerras. @@ -21,8 +21,9 @@ __asm__ __volatile__(SMP_WMB "\n\ 1: lwarx %0,0,%3 \n\ - or %0,%0,%2 \n\ - stwcx. %0,0,%3 \n\ + or %0,%0,%2 \n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ bne 1b" SMP_MB : "=&r" (old), "=m" (*p) @@ -38,8 +39,9 @@ __asm__ __volatile__(SMP_WMB "\n\ 1: lwarx %0,0,%3 \n\ - andc %0,%0,%2 \n\ - stwcx. %0,0,%3 \n\ + andc %0,%0,%2 \n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ bne 1b" SMP_MB : "=&r" (old), "=m" (*p) @@ -55,8 +57,9 @@ __asm__ __volatile__(SMP_WMB "\n\ 1: lwarx %0,0,%3 \n\ - xor %0,%0,%2 \n\ - stwcx. %0,0,%3 \n\ + xor %0,%0,%2 \n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ bne 1b" SMP_MB : "=&r" (old), "=m" (*p) @@ -72,8 +75,9 @@ __asm__ __volatile__(SMP_WMB "\n\ 1: lwarx %0,0,%4 \n\ - or %1,%0,%3 \n\ - stwcx. %1,0,%4 \n\ + or %1,%0,%3 \n" + PPC405_ERR77(0,%4) +" stwcx. %1,0,%4 \n\ bne 1b" SMP_MB : "=&r" (old), "=&r" (t), "=m" (*p) @@ -91,8 +95,9 @@ __asm__ __volatile__(SMP_WMB "\n\ 1: lwarx %0,0,%4 \n\ - andc %1,%0,%3 \n\ - stwcx. %1,0,%4 \n\ + andc %1,%0,%3 \n" + PPC405_ERR77(0,%4) +" stwcx. %1,0,%4 \n\ bne 1b" SMP_MB : "=&r" (old), "=&r" (t), "=m" (*p) @@ -110,8 +115,9 @@ __asm__ __volatile__(SMP_WMB "\n\ 1: lwarx %0,0,%4 \n\ - xor %1,%0,%3 \n\ - stwcx. %1,0,%4 \n\ + xor %1,%0,%3 \n" + PPC405_ERR77(0,%4) +" stwcx. %1,0,%4 \n\ bne 1b" SMP_MB : "=&r" (old), "=&r" (t), "=m" (*p) diff -Nru a/arch/ppc/kernel/btext.c b/arch/ppc/kernel/btext.c --- a/arch/ppc/kernel/btext.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/btext.c Tue Feb 19 18:08:57 2002 @@ -44,10 +44,10 @@ static unsigned char vga_font[cmapsz]; -int boot_text_mapped = 1; +int boot_text_mapped; +int force_printk_to_btext = 0; -boot_infos_t *disp_bi; -boot_infos_t fake_bi; +boot_infos_t disp_bi; extern char *klimit; @@ -69,48 +69,47 @@ void __init btext_init(boot_infos_t *bi) { - unsigned long offset = reloc_offset(); - - RELOC(g_loc_X) = 0; - RELOC(g_loc_Y) = 0; - RELOC(g_max_loc_X) = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8; - RELOC(g_max_loc_Y) = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16; - RELOC(disp_bi) = PTRUNRELOC(bi); + g_loc_X = 0; + g_loc_Y = 0; + g_max_loc_X = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8; + g_max_loc_Y = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16; + disp_bi = *bi; + boot_text_mapped = 1; } void __init -btext_welcome(boot_infos_t* bi) +btext_welcome(void) { - unsigned long offset = reloc_offset(); unsigned long flags; unsigned long pvr; - - btext_drawstring(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n")); - btext_drawstring(RELOC("\nlinked at : 0x")); + boot_infos_t* bi = &disp_bi; + + btext_drawstring("Welcome to Linux, kernel " UTS_RELEASE "\n"); + btext_drawstring("\nlinked at : 0x"); btext_drawhex(KERNELBASE); - btext_drawstring(RELOC("\nframe buffer at : 0x")); + btext_drawstring("\nframe buffer at : 0x"); btext_drawhex((unsigned long)bi->dispDeviceBase); - btext_drawstring(RELOC(" (phys), 0x")); + btext_drawstring(" (phys), 0x"); btext_drawhex((unsigned long)bi->logicalDisplayBase); - btext_drawstring(RELOC(" (log)")); - btext_drawstring(RELOC("\nklimit : 0x")); - btext_drawhex((unsigned long)RELOC(klimit)); - btext_drawstring(RELOC("\nMSR : 0x")); + btext_drawstring(" (log)"); + btext_drawstring("\nklimit : 0x"); + btext_drawhex((unsigned long)klimit); + btext_drawstring("\nMSR : 0x"); __asm__ __volatile__ ("mfmsr %0" : "=r" (flags)); btext_drawhex(flags); __asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr)); pvr >>= 16; if (pvr > 1) { - btext_drawstring(RELOC("\nHID0 : 0x")); + btext_drawstring("\nHID0 : 0x"); __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags)); btext_drawhex(flags); } if (pvr == 8 || pvr == 12 || pvr == 0x800c) { - btext_drawstring(RELOC("\nICTC : 0x")); + btext_drawstring("\nICTC : 0x"); __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags)); btext_drawhex(flags); } - btext_drawstring(RELOC("\n\n")); + btext_drawstring("\n\n"); } /* Calc BAT values for mapping the display and store them @@ -131,28 +130,28 @@ void __init btext_prepare_BAT(void) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); - unsigned long addr = (unsigned long)bi->dispDeviceBase; + boot_infos_t* bi = &disp_bi; unsigned long vaddr = KERNELBASE + 0x10000000; + unsigned long addr; unsigned long lowbits; - if (!RELOC(disp_bi)) { - RELOC(boot_text_mapped) = 0; + addr = (unsigned long)bi->dispDeviceBase; + if (!addr) { + boot_text_mapped = 0; return; } if (PVR_VER(mfspr(PVR)) != 1) { /* 603, 604, G3, G4, ... */ lowbits = addr & ~0xFF000000UL; addr &= 0xFF000000UL; - RELOC(disp_BAT[0]) = vaddr | (BL_16M<<2) | 2; - RELOC(disp_BAT[1]) = addr | (_PAGE_NO_CACHE | _PAGE_GUARDED | BPP_RW); + disp_BAT[0] = vaddr | (BL_16M<<2) | 2; + disp_BAT[1] = addr | (_PAGE_NO_CACHE | _PAGE_GUARDED | BPP_RW); } else { /* 601 */ lowbits = addr & ~0xFF800000UL; addr &= 0xFF800000UL; - RELOC(disp_BAT[0]) = vaddr | (_PAGE_NO_CACHE | PP_RWXX) | 4; - RELOC(disp_BAT[1]) = addr | BL_8M | 0x40; + disp_BAT[0] = vaddr | (_PAGE_NO_CACHE | PP_RWXX) | 4; + disp_BAT[1] = addr | BL_8M | 0x40; } bi->logicalDisplayBase = (void *) (vaddr + lowbits); } @@ -164,15 +163,12 @@ btext_setup_display(int width, int height, int depth, int pitch, unsigned long address) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi; + boot_infos_t* bi = &disp_bi; - RELOC(disp_bi) = &fake_bi; - bi = PTRRELOC((&fake_bi)); - RELOC(g_loc_X) = 0; - RELOC(g_loc_Y) = 0; - RELOC(g_max_loc_X) = width / 8; - RELOC(g_max_loc_Y) = height / 16; + g_loc_X = 0; + g_loc_Y = 0; + g_max_loc_X = width / 8; + g_max_loc_Y = height / 16; bi->logicalDisplayBase = (unsigned char *)address; bi->dispDeviceBase = (unsigned char *)address; bi->dispDeviceRowBytes = pitch; @@ -180,6 +176,7 @@ bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0; bi->dispDeviceRect[2] = width; bi->dispDeviceRect[3] = height; + boot_text_mapped = 1; } /* Here's a small text engine to use during early boot @@ -197,16 +194,18 @@ map_boot_text(void) { unsigned long base, offset, size; - if (disp_bi == 0) + boot_infos_t *bi = &disp_bi; + + if (bi->dispDeviceBase == 0) return; - base = ((unsigned long) disp_bi->dispDeviceBase) & 0xFFFFF000UL; - offset = ((unsigned long) disp_bi->dispDeviceBase) - base; - size = disp_bi->dispDeviceRowBytes * disp_bi->dispDeviceRect[3] + offset - + disp_bi->dispDeviceRect[0]; - disp_bi->logicalDisplayBase = ioremap(base, size); - if (disp_bi->logicalDisplayBase == 0) + base = ((unsigned long) bi->dispDeviceBase) & 0xFFFFF000UL; + offset = ((unsigned long) bi->dispDeviceBase) - base; + size = bi->dispDeviceRowBytes * bi->dispDeviceRect[3] + offset + + bi->dispDeviceRect[0]; + bi->logicalDisplayBase = ioremap(base, size); + if (bi->logicalDisplayBase == 0) return; - disp_bi->logicalDisplayBase += offset; + bi->logicalDisplayBase += offset; boot_text_mapped = 1; } @@ -229,22 +228,24 @@ btext_update_display(unsigned long phys, int width, int height, int depth, int pitch) { - if (disp_bi == 0) + boot_infos_t *bi = &disp_bi; + + if (bi->dispDeviceBase == 0) return; - /* check it's the same frame buffer (within 64MB) */ - if ((phys ^ (unsigned long)disp_bi->dispDeviceBase) & 0xfc000000) { + + /* check it's the same frame buffer (within 256MB) */ + if ((phys ^ (unsigned long)bi->dispDeviceBase) & 0xf0000000) return; - } - disp_bi->dispDeviceBase = (__u8 *) phys; - disp_bi->dispDeviceRect[0] = 0; - disp_bi->dispDeviceRect[1] = 0; - disp_bi->dispDeviceRect[2] = width; - disp_bi->dispDeviceRect[3] = height; - disp_bi->dispDeviceDepth = depth; - disp_bi->dispDeviceRowBytes = pitch; + bi->dispDeviceBase = (__u8 *) phys; + bi->dispDeviceRect[0] = 0; + bi->dispDeviceRect[1] = 0; + bi->dispDeviceRect[2] = width; + bi->dispDeviceRect[3] = height; + bi->dispDeviceDepth = depth; + bi->dispDeviceRowBytes = pitch; if (boot_text_mapped) { - iounmap(disp_bi->logicalDisplayBase); + iounmap(bi->logicalDisplayBase); boot_text_mapped = 0; } map_boot_text(); @@ -256,8 +257,7 @@ void BTEXT btext_clearscreen(void) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); + boot_infos_t* bi = &disp_bi; unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; @@ -279,8 +279,7 @@ void BTEXT btext_flushscreen(void) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); + boot_infos_t* bi = &disp_bi; unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; @@ -301,8 +300,7 @@ static BTEXT void scrollscreen(void) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); + boot_infos_t* bi = &disp_bi; unsigned long *src = (unsigned long *)calc_base(bi,0,16); unsigned long *dst = (unsigned long *)calc_base(bi,0,0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * @@ -336,49 +334,48 @@ void BTEXT btext_drawchar(char c) { - unsigned long offset = reloc_offset(); int cline = 0, x; - if (!RELOC(boot_text_mapped)) + if (!boot_text_mapped) return; switch (c) { case '\b': - if (RELOC(g_loc_X) > 0) - --RELOC(g_loc_X); + if (g_loc_X > 0) + --g_loc_X; break; case '\t': - RELOC(g_loc_X) = (RELOC(g_loc_X) & -8) + 8; + g_loc_X = (g_loc_X & -8) + 8; break; case '\r': - RELOC(g_loc_X) = 0; + g_loc_X = 0; break; case '\n': - RELOC(g_loc_X) = 0; - RELOC(g_loc_Y)++; + g_loc_X = 0; + g_loc_Y++; cline = 1; break; default: - draw_byte(c, RELOC(g_loc_X)++, RELOC(g_loc_Y)); + draw_byte(c, g_loc_X++, g_loc_Y); } - if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) { - RELOC(g_loc_X) = 0; - RELOC(g_loc_Y)++; + if (g_loc_X >= g_max_loc_X) { + g_loc_X = 0; + g_loc_Y++; cline = 1; } #ifndef NO_SCROLL - while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) { + while (g_loc_Y >= g_max_loc_Y) { scrollscreen(); - RELOC(g_loc_Y)--; + g_loc_Y--; } #else /* wrap around from bottom to top of screen so we don't waste time scrolling each line. -- paulus. */ - if (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) - RELOC(g_loc_Y) = 0; + if (g_loc_Y >= g_max_loc_Y) + g_loc_Y = 0; if (cline) { - for (x = 0; x < RELOC(g_max_loc_X); ++x) - draw_byte(' ', x, RELOC(g_loc_Y)); + for (x = 0; x < g_max_loc_X; ++x) + draw_byte(' ', x, g_loc_Y); } #endif } @@ -386,9 +383,7 @@ void BTEXT btext_drawstring(const char *c) { - unsigned long offset = reloc_offset(); - - if (!RELOC(boot_text_mapped)) + if (!boot_text_mapped) return; while (*c) btext_drawchar(*c++); @@ -398,34 +393,34 @@ btext_drawhex(unsigned long v) { static char hex_table[] = "0123456789abcdef"; - unsigned long offset = reloc_offset(); - if (!RELOC(boot_text_mapped)) + if (!boot_text_mapped) return; - btext_drawchar(RELOC(hex_table)[(v >> 28) & 0x0000000FUL]); - btext_drawchar(RELOC(hex_table)[(v >> 24) & 0x0000000FUL]); - btext_drawchar(RELOC(hex_table)[(v >> 20) & 0x0000000FUL]); - btext_drawchar(RELOC(hex_table)[(v >> 16) & 0x0000000FUL]); - btext_drawchar(RELOC(hex_table)[(v >> 12) & 0x0000000FUL]); - btext_drawchar(RELOC(hex_table)[(v >> 8) & 0x0000000FUL]); - btext_drawchar(RELOC(hex_table)[(v >> 4) & 0x0000000FUL]); - btext_drawchar(RELOC(hex_table)[(v >> 0) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 8) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 4) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 0) & 0x0000000FUL]); btext_drawchar(' '); } static void BTEXT draw_byte(unsigned char c, long locX, long locY) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); + boot_infos_t* bi = &disp_bi; unsigned char *base = calc_base(bi, locX << 3, locY << 4); - unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16]; + unsigned char *font = &vga_font[((unsigned long)c) * 16]; int rb = bi->dispDeviceRowBytes; switch(bi->dispDeviceDepth) { + case 24: case 32: draw_byte_32(font, (unsigned long *)base, rb); break; + case 15: case 16: draw_byte_16(font, (unsigned long *)base, rb); break; @@ -490,8 +485,7 @@ int l, bits; int fg = 0xFFFFFFFFUL; int bg = 0x00000000UL; - unsigned long offset = reloc_offset(); - unsigned long *eb = RELOC(expand_bits_16); + unsigned long *eb = expand_bits_16; for (l = 0; l < 16; ++l) { @@ -510,8 +504,7 @@ int l, bits; int fg = 0x0F0F0F0FUL; int bg = 0x00000000UL; - unsigned long offset = reloc_offset(); - unsigned long *eb = RELOC(expand_bits_8); + unsigned long *eb = expand_bits_8; for (l = 0; l < 16; ++l) { diff -Nru a/arch/ppc/kernel/checks.c b/arch/ppc/kernel/checks.c --- a/arch/ppc/kernel/checks.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,58 +0,0 @@ -/* - * BK Id: SCCS/s.checks.c 1.6 05/17/01 18:14:21 cort - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* - * Do various before compile checks of data structures - * -- Cort - */ -int main(void) -{ - int ret = 0; -#if 0 - if ( sizeof(struct thread_struct) % 16 ) - { - printf("Thread struct is not modulo 16 bytes: " - "%d bytes total, %d bytes off\n", - sizeof(struct thread_struct), - sizeof(struct thread_struct)%16); - ret = -1; - } -#endif - - if ( sizeof(struct pt_regs) % 16 ) - { - printf("pt_regs struct is not modulo 16 bytes: " - "%d bytes total, %d bytes off\n", - sizeof(struct pt_regs), - sizeof(struct pt_regs)%16); - ret = -1; - - } - - printf("Task size : %d bytes\n" - "Tss size : %d bytes\n" - "pt_regs size : %d bytes\n" - "Kernel stack size: %d bytes\n", - sizeof(struct task_struct), sizeof(struct thread_struct), - sizeof(struct pt_regs), - sizeof(union task_union) - sizeof(struct task_struct)); - return ret; -} diff -Nru a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c --- a/arch/ppc/kernel/chrp_pci.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,330 +0,0 @@ -/* - * BK Id: SCCS/s.chrp_pci.c 1.22 09/08/01 15:47:42 paulus - */ -/* - * CHRP pci routines. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "open_pic.h" -#include "pci.h" - -/* LongTrail */ -unsigned long gg2_pci_config_base; - -#define pci_config_addr(dev, offset) \ -(gg2_pci_config_base | ((dev->bus->number)<<16) | ((dev->devfn)<<8) | (offset)) - -volatile struct Hydra *Hydra = NULL; - -/* - * The VLSI Golden Gate II has only 512K of PCI configuration space, so we - * limit the bus number to 3 bits - */ - -#define cfg_read(val, addr, type, op) *val = op((type)(addr)) -#define cfg_write(val, addr, type, op) op((type *)(addr), (val)) - -#define cfg_read_bad(val, size) *val = bad_##size; -#define cfg_write_bad(val, size) - -#define bad_byte 0xff -#define bad_word 0xffff -#define bad_dword 0xffffffffU - -#define GG2_PCI_OP(rw, size, type, op) \ -int __chrp gg2_##rw##_config_##size(struct pci_dev *dev, int off, type val) \ -{ \ - if (dev->bus->number > 7) { \ - cfg_##rw##_bad(val, size) \ - return PCIBIOS_DEVICE_NOT_FOUND; \ - } \ - cfg_##rw(val, pci_config_addr(dev, off), type, op); \ - return PCIBIOS_SUCCESSFUL; \ -} - -GG2_PCI_OP(read, byte, u8 *, in_8) -GG2_PCI_OP(read, word, u16 *, in_le16) -GG2_PCI_OP(read, dword, u32 *, in_le32) -GG2_PCI_OP(write, byte, u8, out_8) -GG2_PCI_OP(write, word, u16, out_le16) -GG2_PCI_OP(write, dword, u32, out_le32) - -static struct pci_ops gg2_pci_ops = -{ - gg2_read_config_byte, - gg2_read_config_word, - gg2_read_config_dword, - gg2_write_config_byte, - gg2_write_config_word, - gg2_write_config_dword -}; - -/* - * Access functions for PCI config space on IBM "python" host bridges. - */ -#define PYTHON_CFA(b, d, o) (0x80 | ((b) << 8) | ((d) << 16) \ - | (((o) & ~3) << 24)) - -#define PYTHON_PCI_OP(rw, size, type, op, mask) \ -int __chrp \ -python_##rw##_config_##size(struct pci_dev *dev, int offset, type val) \ -{ \ - struct pci_controller *hose = dev->sysdata; \ - \ - out_be32(hose->cfg_addr, \ - PYTHON_CFA(dev->bus->number, dev->devfn, offset)); \ - cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ - return PCIBIOS_SUCCESSFUL; \ -} - -PYTHON_PCI_OP(read, byte, u8 *, in_8, 3) -PYTHON_PCI_OP(read, word, u16 *, in_le16, 2) -PYTHON_PCI_OP(read, dword, u32 *, in_le32, 0) -PYTHON_PCI_OP(write, byte, u8, out_8, 3) -PYTHON_PCI_OP(write, word, u16, out_le16, 2) -PYTHON_PCI_OP(write, dword, u32, out_le32, 0) - -static struct pci_ops python_pci_ops = -{ - python_read_config_byte, - python_read_config_word, - python_read_config_dword, - python_write_config_byte, - python_write_config_word, - python_write_config_dword -}; - -/* - * Access functions for PCI config space using RTAS calls. - */ -#define RTAS_PCI_READ_OP(size, type, nbytes) \ -int __chrp \ -rtas_read_config_##size(struct pci_dev *dev, int offset, type val) \ -{ \ - unsigned long addr = (offset & 0xff) | ((dev->devfn & 0xff) << 8) \ - | ((dev->bus->number & 0xff) << 16); \ - unsigned long ret = ~0UL; \ - int rval; \ - \ - rval = call_rtas("read-pci-config", 2, 2, &ret, addr, nbytes); \ - *val = ret; \ - return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; \ -} - -#define RTAS_PCI_WRITE_OP(size, type, nbytes) \ -int __chrp \ -rtas_write_config_##size(struct pci_dev *dev, int offset, type val) \ -{ \ - unsigned long addr = (offset & 0xff) | ((dev->devfn & 0xff) << 8) \ - | ((dev->bus->number & 0xff) << 16); \ - int rval; \ - \ - rval = call_rtas("write-pci-config", 3, 1, NULL, \ - addr, nbytes, (ulong)val); \ - return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; \ -} - -RTAS_PCI_READ_OP(byte, u8 *, 1) -RTAS_PCI_READ_OP(word, u16 *, 2) -RTAS_PCI_READ_OP(dword, u32 *, 4) -RTAS_PCI_WRITE_OP(byte, u8, 1) -RTAS_PCI_WRITE_OP(word, u16, 2) -RTAS_PCI_WRITE_OP(dword, u32, 4) - -static struct pci_ops rtas_pci_ops = -{ - rtas_read_config_byte, - rtas_read_config_word, - rtas_read_config_dword, - rtas_write_config_byte, - rtas_write_config_word, - rtas_write_config_dword -}; - - /* - * Temporary fixes for PCI devices. These should be replaced by OF query - * code -- Geert - */ - -static u_char hydra_openpic_initsenses[] __initdata = { - 1, /* HYDRA_INT_SIO */ - 0, /* HYDRA_INT_SCSI_DMA */ - 0, /* HYDRA_INT_SCCA_TX_DMA */ - 0, /* HYDRA_INT_SCCA_RX_DMA */ - 0, /* HYDRA_INT_SCCB_TX_DMA */ - 0, /* HYDRA_INT_SCCB_RX_DMA */ - 1, /* HYDRA_INT_SCSI */ - 1, /* HYDRA_INT_SCCA */ - 1, /* HYDRA_INT_SCCB */ - 1, /* HYDRA_INT_VIA */ - 1, /* HYDRA_INT_ADB */ - 0, /* HYDRA_INT_ADB_NMI */ - /* all others are 1 (= default) */ -}; - -int __init -hydra_init(void) -{ - struct device_node *np; - - np = find_devices("mac-io"); - if (np == NULL || np->n_addrs == 0) { - printk(KERN_WARNING "Warning: no mac-io found\n"); - return 0; - } - Hydra = ioremap(np->addrs[0].address, np->addrs[0].size); - printk("Hydra Mac I/O at %x\n", np->addrs[0].address); - out_le32(&Hydra->Feature_Control, (HYDRA_FC_SCC_CELL_EN | - HYDRA_FC_SCSI_CELL_EN | - HYDRA_FC_SCCA_ENABLE | - HYDRA_FC_SCCB_ENABLE | - HYDRA_FC_ARB_BYPASS | - HYDRA_FC_MPIC_ENABLE | - HYDRA_FC_SLOW_SCC_PCLK | - HYDRA_FC_MPIC_IS_MASTER)); - OpenPIC_Addr = &Hydra->OpenPIC; - OpenPIC_InitSenses = hydra_openpic_initsenses; - OpenPIC_NumInitSenses = sizeof(hydra_openpic_initsenses); - return 1; -} - -void __init -chrp_pcibios_fixup(void) -{ - struct pci_dev *dev; - struct device_node *np; - - /* PCI interrupts are controlled by the OpenPIC */ - pci_for_each_dev(dev) { - np = pci_device_to_OF_node(dev); - if ((np != 0) && (np->n_intrs > 0) && (np->intrs[0].line != 0)) - dev->irq = np->intrs[0].line; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - } -} - -void __init -chrp_find_bridges(void) -{ - struct device_node *dev; - int *bus_range; - int len, index = -1; - struct pci_controller *hose; - volatile unsigned char *cfg; - unsigned int *dma; - char *model, *machine; - int is_longtrail = 0, is_mot = 0; - struct device_node *root = find_path_device("/"); -#ifdef CONFIG_POWER3 - unsigned int *opprop = (unsigned int *) - get_property(root, "platform-open-pic", NULL); - int i; -#endif - - /* - * The PCI host bridge nodes on some machines don't have - * properties to adequately identify them, so we have to - * look at what sort of machine this is as well. - */ - machine = get_property(root, "model", NULL); - if (machine != NULL) { - is_longtrail = strncmp(machine, "IBM,LongTrail", 13) == 0; - is_mot = strncmp(machine, "MOT", 3) == 0; - } - for (dev = root->child; dev != NULL; dev = dev->sibling) { - if (dev->type == NULL || strcmp(dev->type, "pci") != 0) - continue; - ++index; - /* The GG2 bridge on the LongTrail doesn't have an address */ - if (dev->n_addrs < 1 && !is_longtrail) { - printk(KERN_WARNING "Can't use %s: no address\n", - dev->full_name); - continue; - } - bus_range = (int *) get_property(dev, "bus-range", &len); - if (bus_range == NULL || len < 2 * sizeof(int)) { - printk(KERN_WARNING "Can't get bus-range for %s\n", - dev->full_name); - continue; - } - if (bus_range[1] == bus_range[0]) - printk(KERN_INFO "PCI bus %d", bus_range[0]); - else - printk(KERN_INFO "PCI buses %d..%d", - bus_range[0], bus_range[1]); - printk(" controlled by %s", dev->type); - if (dev->n_addrs > 0) - printk(" at %x", dev->addrs[0].address); - printk("\n"); - - hose = pcibios_alloc_controller(); - if (!hose) { - printk("Can't allocate PCI controller structure for %s\n", - dev->full_name); - continue; - } - hose->arch_data = dev; - hose->first_busno = bus_range[0]; - hose->last_busno = bus_range[1]; - - model = get_property(dev, "model", NULL); - if (model == NULL) - model = ""; - if (device_is_compatible(dev, "IBM,python")) { - hose->ops = &python_pci_ops; - cfg = ioremap(dev->addrs[0].address + 0xf8000, 0x20); - hose->cfg_addr = (volatile unsigned int *) cfg; - hose->cfg_data = cfg + 0x10; - } else if (is_mot - || strncmp(model, "Motorola, Grackle", 17) == 0) { - setup_grackle(hose); - } else if (is_longtrail) { - hose->ops = &gg2_pci_ops; - gg2_pci_config_base = (unsigned long) - ioremap(GG2_PCI_CONFIG_BASE, 0x80000); - } else { - printk("No methods for %s (model %s), using RTAS\n", - dev->full_name, model); - hose->ops = &rtas_pci_ops; - } - - pci_process_bridge_OF_ranges(hose, dev, index == 0); - -#ifdef CONFIG_POWER3 - if (opprop != NULL) { - i = prom_n_addr_cells(root) * (index + 2) - 1; - openpic_setup_ISU(index, opprop[i]); - } -#endif /* CONFIG_POWER3 */ - - /* check the first bridge for a property that we can - use to set pci_dram_offset */ - dma = (unsigned int *) - get_property(dev, "ibm,dma-ranges", &len); - if (index == 0 && dma != NULL && len >= 6 * sizeof(*dma)) { - pci_dram_offset = dma[2] - dma[3]; - printk("pci_dram_offset = %lx\n", pci_dram_offset); - } - } - - ppc_md.pcibios_fixup = chrp_pcibios_fixup; -} diff -Nru a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c --- a/arch/ppc/kernel/chrp_setup.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,634 +0,0 @@ -/* - * BK Id: SCCS/s.chrp_setup.c 1.38 11/13/01 21:26:07 paulus - */ -/* - * linux/arch/ppc/kernel/setup.c - * - * Copyright (C) 1995 Linus Torvalds - * Adapted from 'alpha' version by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) - */ - -/* - * bootup setup stuff.. - */ - -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "local_irq.h" -#include "i8259.h" -#include "open_pic.h" -#include "xics.h" - -unsigned long chrp_get_rtc_time(void); -int chrp_set_rtc_time(unsigned long nowtime); -void chrp_calibrate_decr(void); -long chrp_time_init(void); - -void chrp_find_bridges(void); -void chrp_event_scan(void); -void rtas_display_progress(char *, unsigned short); -void rtas_indicator_progress(char *, unsigned short); -void btext_progress(char *, unsigned short); - -extern unsigned long pmac_find_end_of_memory(void); -extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); -extern int pckbd_getkeycode(unsigned int scancode); -extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, - char raw_mode); -extern char pckbd_unexpected_up(unsigned char keycode); -extern void pckbd_leds(unsigned char leds); -extern void pckbd_init_hw(void); -extern unsigned char pckbd_sysrq_xlate[128]; -extern void select_adb_keyboard(void); -extern int of_show_percpuinfo(struct seq_file *, int); - -extern kdev_t boot_dev; - -extern PTE *Hash, *Hash_end; -extern unsigned long Hash_size, Hash_mask; -extern int probingmem; -extern unsigned long loops_per_jiffy; -static int max_width; - -#ifdef CONFIG_SMP -extern struct smp_ops_t chrp_smp_ops; -extern struct smp_ops_t xics_smp_ops; -#endif - -static const char *gg2_memtypes[4] = { - "FPM", "SDRAM", "EDO", "BEDO" -}; -static const char *gg2_cachesizes[4] = { - "256 KB", "512 KB", "1 MB", "Reserved" -}; -static const char *gg2_cachetypes[4] = { - "Asynchronous", "Reserved", "Flow-Through Synchronous", - "Pipelined Synchronous" -}; -static const char *gg2_cachemodes[4] = { - "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" -}; - -int __chrp -chrp_show_cpuinfo(struct seq_file *m) -{ - int i, sdramen; - unsigned int t; - struct device_node *root; - const char *model = ""; - - root = find_path_device("/"); - if (root) - model = get_property(root, "model", NULL); - seq_printf(m, "machine\t\t: CHRP %s\n", model); - - /* longtrail (goldengate) stuff */ - if (!strncmp(model, "IBM,LongTrail", 13)) { - /* VLSI VAS96011/12 `Golden Gate 2' */ - /* Memory banks */ - sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+ - GG2_PCI_DRAM_CTRL)) - >>31) & 1; - for (i = 0; i < (sdramen ? 4 : 6); i++) { - t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+ - GG2_PCI_DRAM_BANK0+ - i*4)); - if (!(t & 1)) - continue; - switch ((t>>8) & 0x1f) { - case 0x1f: - model = "4 MB"; - break; - case 0x1e: - model = "8 MB"; - break; - case 0x1c: - model = "16 MB"; - break; - case 0x18: - model = "32 MB"; - break; - case 0x10: - model = "64 MB"; - break; - case 0x00: - model = "128 MB"; - break; - default: - model = "Reserved"; - break; - } - seq_printf(m, "memory bank %d\t: %s %s\n", i, model, - gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); - } - /* L2 cache */ - t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL)); - seq_printf(m, "board l2\t: %s %s (%s)\n", - gg2_cachesizes[(t>>7) & 3], - gg2_cachetypes[(t>>2) & 3], - gg2_cachemodes[t & 3]); - } - return 0; -} - -/* - * Fixes for the National Semiconductor PC78308VUL SuperI/O - * - * Some versions of Open Firmware incorrectly initialize the IRQ settings - * for keyboard and mouse - */ -static inline void __init sio_write(u8 val, u8 index) -{ - outb(index, 0x15c); - outb(val, 0x15d); -} - -static inline u8 __init sio_read(u8 index) -{ - outb(index, 0x15c); - return inb(0x15d); -} - -static void __init sio_fixup_irq(const char *name, u8 device, u8 level, - u8 type) -{ - u8 level0, type0, active; - - /* select logical device */ - sio_write(device, 0x07); - active = sio_read(0x30); - level0 = sio_read(0x70); - type0 = sio_read(0x71); - if (level0 != level || type0 != type || !active) { - printk(KERN_WARNING "sio: %s irq level %d, type %d, %sactive: " - "remapping to level %d, type %d, active\n", - name, level0, type0, !active ? "in" : "", level, type); - sio_write(0x01, 0x30); - sio_write(level, 0x70); - sio_write(type, 0x71); - } -} - -static void __init sio_init(void) -{ - struct device_node *root; - - if ((root = find_path_device("/")) && - !strncmp(get_property(root, "model", NULL), "IBM,LongTrail", 13)) { - /* logical device 0 (KBC/Keyboard) */ - sio_fixup_irq("keyboard", 0, 1, 2); - /* select logical device 1 (KBC/Mouse) */ - sio_fixup_irq("mouse", 1, 12, 2); - } -} - - -void __init -chrp_setup_arch(void) -{ - struct device_node *device; - - /* init to some ~sane value until calibrate_delay() runs */ - loops_per_jiffy = 50000000/HZ; - -#ifdef CONFIG_BLK_DEV_INITRD - /* this is fine for chrp */ - initrd_below_start_ok = 1; - - if (initrd_start) - ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); - else -#endif - ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */ - - /* Lookup PCI host bridges */ - chrp_find_bridges(); - -#ifndef CONFIG_PPC64BRIDGE - /* - * Temporary fixes for PCI devices. - * -- Geert - */ - hydra_init(); /* Mac I/O */ - -#endif /* CONFIG_PPC64BRIDGE */ - - /* Some IBM machines don't have the hydra -- Cort */ - if (!OpenPIC_Addr) { - struct device_node *root; - unsigned long *opprop; - int n; - - root = find_path_device("/"); - opprop = (unsigned long *) get_property - (root, "platform-open-pic", NULL); - n = prom_n_addr_cells(root); - if (opprop != 0) { - printk("OpenPIC addrs: %lx %lx %lx\n", - opprop[n-1], opprop[2*n-1], opprop[3*n-1]); - OpenPIC_Addr = ioremap(opprop[n-1], 0x40000); - } - } - - /* - * Fix the Super I/O configuration - */ - sio_init(); - - /* - * Setup the console operations - */ -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - - /* Get the event scan rate for the rtas so we know how - * often it expects a heartbeat. -- Cort - */ - if ( rtas_data ) { - struct property *p; - device = find_devices("rtas"); - for ( p = device->properties; - p && strncmp(p->name, "rtas-event-scan-rate", 20); - p = p->next ) - /* nothing */ ; - if ( p && *(unsigned long *)p->value ) { - ppc_md.heartbeat = chrp_event_scan; - ppc_md.heartbeat_reset = (HZ/(*(unsigned long *)p->value)*30)-1; - ppc_md.heartbeat_count = 1; - printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", - *(unsigned long *)p->value, ppc_md.heartbeat_reset ); - } - } -} - -void __chrp -chrp_event_scan(void) -{ - unsigned char log[1024]; - unsigned long ret = 0; - /* XXX: we should loop until the hardware says no more error logs -- Cort */ - call_rtas( "event-scan", 4, 1, &ret, 0xffffffff, 0, - __pa(log), 1024 ); - ppc_md.heartbeat_count = ppc_md.heartbeat_reset; -} - -void __chrp -chrp_restart(char *cmd) -{ - printk("RTAS system-reboot returned %d\n", - call_rtas("system-reboot", 0, 1, NULL)); - for (;;); -} - -void __chrp -chrp_power_off(void) -{ - /* allow power on only with power button press */ - printk("RTAS power-off returned %d\n", - call_rtas("power-off", 2, 1, NULL,0xffffffff,0xffffffff)); - for (;;); -} - -void __chrp -chrp_halt(void) -{ - chrp_power_off(); -} - -u_int __chrp -chrp_irq_cannonicalize(u_int irq) -{ - if (irq == 2) - return 9; - return irq; -} - -void __init chrp_init_IRQ(void) -{ - struct device_node *np; - int i; - unsigned int *addrp; - unsigned char* chrp_int_ack_special = 0; - unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS]; - int nmi_irq = -1; -#if defined(CONFIG_VT) && defined(CONFIG_ADB_KEYBOARD) && defined(XMON) - struct device_node *kbd; -#endif - - if (!(np = find_devices("pci")) - || !(addrp = (unsigned int *) - get_property(np, "8259-interrupt-acknowledge", NULL))) - printk("Cannot find pci to get ack address\n"); - else - chrp_int_ack_special = (unsigned char *) - ioremap(addrp[prom_n_addr_cells(np)-1], 1); - /* hydra still sets OpenPIC_InitSenses to a static set of values */ - if (OpenPIC_InitSenses == NULL) { - prom_get_irq_senses(init_senses, NUM_8259_INTERRUPTS, NR_IRQS); - OpenPIC_InitSenses = init_senses; - OpenPIC_NumInitSenses = NR_IRQS - NUM_8259_INTERRUPTS; - } - openpic_init(1, NUM_8259_INTERRUPTS, chrp_int_ack_special, nmi_irq); - for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ ) - irq_desc[i].handler = &i8259_pic; - i8259_init(); -#if defined(CONFIG_VT) && defined(CONFIG_ADB_KEYBOARD) && defined(XMON) - /* see if there is a keyboard in the device tree - with a parent of type "adb" */ - for (kbd = find_devices("keyboard"); kbd; kbd = kbd->next) - if (kbd->parent && kbd->parent->type - && strcmp(kbd->parent->type, "adb") == 0) - break; - if (kbd) - request_irq( HYDRA_INT_ADB_NMI, xmon_irq, 0, "XMON break", 0); -#endif -} - -void __init -chrp_init2(void) -{ -#ifdef CONFIG_NVRAM - pmac_nvram_init(); -#endif - - request_region(0x20,0x20,"pic1"); - request_region(0xa0,0x20,"pic2"); - request_region(0x00,0x20,"dma1"); - request_region(0x40,0x20,"timer"); - request_region(0x80,0x10,"dma page reg"); - request_region(0xc0,0x20,"dma2"); - - if (ppc_md.progress) - ppc_md.progress(" Have fun! ", 0x7777); - -#if defined(CONFIG_VT) && (defined(CONFIG_ADB_KEYBOARD) || defined(CONFIG_INPUT)) - /* see if there is a keyboard in the device tree - with a parent of type "adb" */ - { - struct device_node *kbd; - - for (kbd = find_devices("keyboard"); kbd; kbd = kbd->next) { - if (kbd->parent && kbd->parent->type - && strcmp(kbd->parent->type, "adb") == 0) { - select_adb_keyboard(); - break; - } - } - } -#endif /* CONFIG_VT && (CONFIG_ADB_KEYBOARD || CONFIG_INPUT) */ -} - -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) -/* - * IDE stuff. - */ - -static int __chrp -chrp_ide_check_region(ide_ioreg_t from, unsigned int extent) -{ - return check_region(from, extent); -} - -static void __chrp -chrp_ide_request_region(ide_ioreg_t from, - unsigned int extent, - const char *name) -{ - request_region(from, extent, name); -} - -static void __chrp -chrp_ide_release_region(ide_ioreg_t from, - unsigned int extent) -{ - release_region(from, extent); -} - -static void __chrp -chrp_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) -{ - ide_ioreg_t reg = data_port; - int i; - - for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { - hw->io_ports[i] = reg; - reg += 1; - } - hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; -} -#endif - -/* - * One of the main thing these mappings are needed for is so that - * xmon can get to the serial port early on. We probably should - * handle the machines with the mpc106 as well as the python (F50) - * and the GG2 (longtrail). Actually we should look in the device - * tree and do the right thing. - */ -static void __init -chrp_map_io(void) -{ - char *name; - - /* - * The code below tends to get removed, please don't take it out. - * The F50 needs this mapping and it you take it out I'll track you - * down and slap your hands. If it causes problems please email me. - * -- Cort - */ - name = get_property(find_path_device("/"), "name", NULL); - if (name && strncmp(name, "IBM-70", 6) == 0 - && strstr(name, "-F50")) { - io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); - io_block_mapping(0x90000000, 0x90000000, 0x10000000, _PAGE_IO); - return; - } else { - io_block_mapping(0xf8000000, 0xf8000000, 0x04000000, _PAGE_IO); - } -} - -void __init -chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ -#ifdef CONFIG_BLK_DEV_INITRD - /* take care of initrd if we have one */ - if ( r6 ) - { - initrd_start = r6 + KERNELBASE; - initrd_end = r6 + r7 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_INITRD */ - - ISA_DMA_THRESHOLD = ~0L; - DMA_MODE_READ = 0x44; - DMA_MODE_WRITE = 0x48; - isa_io_base = CHRP_ISA_IO_BASE; /* default value */ - - ppc_md.setup_arch = chrp_setup_arch; - ppc_md.show_percpuinfo = of_show_percpuinfo; - ppc_md.show_cpuinfo = chrp_show_cpuinfo; - ppc_md.irq_cannonicalize = chrp_irq_cannonicalize; -#ifndef CONFIG_POWER4 - ppc_md.init_IRQ = chrp_init_IRQ; - ppc_md.get_irq = openpic_get_irq; -#else - ppc_md.init_IRQ = xics_init_IRQ; - ppc_md.get_irq = xics_get_irq; -#endif /* CONFIG_POWER4 */ - - ppc_md.init = chrp_init2; - - ppc_md.restart = chrp_restart; - ppc_md.power_off = chrp_power_off; - ppc_md.halt = chrp_halt; - - ppc_md.time_init = chrp_time_init; - ppc_md.set_rtc_time = chrp_set_rtc_time; - ppc_md.get_rtc_time = chrp_get_rtc_time; - ppc_md.calibrate_decr = chrp_calibrate_decr; - - ppc_md.find_end_of_memory = pmac_find_end_of_memory; - ppc_md.setup_io_mappings = chrp_map_io; - -#ifdef CONFIG_VT - /* these are adjusted in chrp_init2 if we have an ADB keyboard */ - ppc_md.kbd_setkeycode = pckbd_setkeycode; - ppc_md.kbd_getkeycode = pckbd_getkeycode; - ppc_md.kbd_translate = pckbd_translate; - ppc_md.kbd_unexpected_up = pckbd_unexpected_up; - ppc_md.kbd_leds = pckbd_leds; - ppc_md.kbd_init_hw = pckbd_init_hw; -#ifdef CONFIG_MAGIC_SYSRQ - ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; - SYSRQ_KEY = 0x54; -#endif /* CONFIG_MAGIC_SYSRQ */ -#endif /* CONFIG_VT */ - - if (rtas_data) { - struct device_node *rtas; - unsigned int *p; - - rtas = find_devices("rtas"); - if (rtas != NULL) { - if (get_property(rtas, "display-character", NULL)) { - ppc_md.progress = rtas_display_progress; - p = (unsigned int *) get_property - (rtas, "ibm,display-line-length", NULL); - if (p) - max_width = *p; - } else if (get_property(rtas, "set-indicator", NULL)) - ppc_md.progress = rtas_indicator_progress; - } - } -#ifdef CONFIG_BOOTX_TEXT - if (ppc_md.progress == NULL && boot_text_mapped) - ppc_md.progress = btext_progress; -#endif - -#ifdef CONFIG_SMP -#ifndef CONFIG_POWER4 - ppc_md.smp_ops = &chrp_smp_ops; -#else - ppc_md.smp_ops = &xics_smp_ops; -#endif /* CONFIG_POWER4 */ -#endif /* CONFIG_SMP */ - -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) - ppc_ide_md.ide_check_region = chrp_ide_check_region; - ppc_ide_md.ide_request_region = chrp_ide_request_region; - ppc_ide_md.ide_release_region = chrp_ide_release_region; - ppc_ide_md.ide_init_hwif = chrp_ide_init_hwif_ports; -#endif - - /* - * Print the banner, then scroll down so boot progress - * can be printed. -- Cort - */ - if ( ppc_md.progress ) ppc_md.progress("Linux/PPC "UTS_RELEASE"\n", 0x0); -} - -void __chrp -rtas_display_progress(char *s, unsigned short hex) -{ - int width; - char *os = s; - - if ( call_rtas( "display-character", 1, 1, NULL, '\r' ) ) - return; - - width = max_width; - while ( *os ) - { - if ( (*os == '\n') || (*os == '\r') ) - width = max_width; - else - width--; - call_rtas( "display-character", 1, 1, NULL, *os++ ); - /* if we overwrite the screen length */ - if ( width == 0 ) - while ( (*os != 0) && (*os != '\n') && (*os != '\r') ) - os++; - } - - /*while ( width-- > 0 )*/ - call_rtas( "display-character", 1, 1, NULL, ' ' ); -} - -void __chrp -rtas_indicator_progress(char *s, unsigned short hex) -{ - call_rtas("set-indicator", 3, 1, NULL, 6, 0, hex); -} - -#ifdef CONFIG_BOOTX_TEXT -void -btext_progress(char *s, unsigned short hex) -{ - prom_print(s); - prom_print("\n"); -} -#endif /* CONFIG_BOOTX_TEXT */ diff -Nru a/arch/ppc/kernel/chrp_smp.c b/arch/ppc/kernel/chrp_smp.c --- a/arch/ppc/kernel/chrp_smp.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,149 +0,0 @@ -/* - * BK Id: %F% %I% %G% %U% %#% - */ -/* - * Smp support for CHRP machines. - * - * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great - * deal of code from the sparc and intel versions. - * - * Copyright (C) 1999 Cort Dougan - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#define __KERNEL_SYSCALLS__ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "open_pic.h" - -extern unsigned long smp_chrp_cpu_nr; - -static int __init -smp_chrp_probe(void) -{ - if (smp_chrp_cpu_nr > 1) - openpic_request_IPIs(); - - return smp_chrp_cpu_nr; -} - -static void __init -smp_chrp_kick_cpu(int nr) -{ - *(unsigned long *)KERNELBASE = nr; - asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); -} - -static void __init -smp_chrp_setup_cpu(int cpu_nr) -{ - static atomic_t ready = ATOMIC_INIT(1); - static volatile int frozen = 0; - - if (cpu_nr == 0) { - /* wait for all the others */ - while (atomic_read(&ready) < smp_num_cpus) - barrier(); - atomic_set(&ready, 1); - /* freeze the timebase */ - call_rtas("freeze-time-base", 0, 1, NULL); - mb(); - frozen = 1; - /* XXX assumes this is not a 601 */ - set_tb(0, 0); - last_jiffy_stamp(0) = 0; - while (atomic_read(&ready) < smp_num_cpus) - barrier(); - /* thaw the timebase again */ - call_rtas("thaw-time-base", 0, 1, NULL); - mb(); - frozen = 0; - smp_tb_synchronized = 1; - } else { - atomic_inc(&ready); - while (!frozen) - barrier(); - set_tb(0, 0); - last_jiffy_stamp(0) = 0; - mb(); - atomic_inc(&ready); - while (frozen) - barrier(); - } - - if (OpenPIC_Addr) - do_openpic_setup_cpu(); -} - -#ifdef CONFIG_POWER4 -static void __chrp -smp_xics_message_pass(int target, int msg, unsigned long data, int wait) -{ - /* for now, only do reschedule messages - since we only have one IPI */ - if (msg != PPC_MSG_RESCHEDULE) - return; - for (i = 0; i < smp_num_cpus; ++i) { - if (target == MSG_ALL || target == i - || (target == MSG_ALL_BUT_SELF - && i != smp_processor_id())) - xics_cause_IPI(i); - } -} - -static int __chrp -smp_xics_probe(void) -{ - return smp_chrp_cpu_nr; -} - -static void __chrp -smp_xics_setup_cpu(int cpu_nr) -{ - if (cpu_nr > 0) - xics_setup_cpu(); -} -#endif /* CONFIG_POWER4 */ - -/* CHRP with openpic */ -struct smp_ops_t chrp_smp_ops __chrpdata = { - smp_openpic_message_pass, - smp_chrp_probe, - smp_chrp_kick_cpu, - smp_chrp_setup_cpu, -}; - -#ifdef CONFIG_POWER4 -/* CHRP with new XICS interrupt controller */ -struct smp_ops_t xics_smp_ops __chrpdata = { - smp_xics_message_pass, - smp_xics_probe, - smp_chrp_kick_cpu, - smp_xics_setup_cpu, -}; -#endif /* CONFIG_POWER4 */ diff -Nru a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c --- a/arch/ppc/kernel/chrp_time.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,195 +0,0 @@ -/* - * BK Id: SCCS/s.chrp_time.c 1.10 09/08/01 15:47:42 paulus - */ -/* - * linux/arch/i386/kernel/time.c - * - * Copyright (C) 1991, 1992, 1995 Linus Torvalds - * - * Adapted for PowerPC (PreP) by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) - * copied and modified from intel version - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -extern spinlock_t rtc_lock; - -static int nvram_as1 = NVRAM_AS1; -static int nvram_as0 = NVRAM_AS0; -static int nvram_data = NVRAM_DATA; - -long __init chrp_time_init(void) -{ - struct device_node *rtcs; - int base; - - rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); - if (rtcs == NULL || rtcs->addrs == NULL) - return 0; - base = rtcs->addrs[0].address; - nvram_as1 = 0; - nvram_as0 = base; - nvram_data = base + 1; - - return 0; -} - -int __chrp chrp_cmos_clock_read(int addr) -{ - if (nvram_as1 != 0) - outb(addr>>8, nvram_as1); - outb(addr, nvram_as0); - return (inb(nvram_data)); -} - -void __chrp chrp_cmos_clock_write(unsigned long val, int addr) -{ - if (nvram_as1 != 0) - outb(addr>>8, nvram_as1); - outb(addr, nvram_as0); - outb(val, nvram_data); - return; -} - -/* - * Set the hardware clock. -- Cort - */ -int __chrp chrp_set_rtc_time(unsigned long nowtime) -{ - unsigned char save_control, save_freq_select; - struct rtc_time tm; - - spin_lock(&rtc_lock); - to_tm(nowtime, &tm); - - save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ - - chrp_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); - - save_freq_select = chrp_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ - - chrp_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - tm.tm_year -= 1900; - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BIN_TO_BCD(tm.tm_sec); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_mon); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_year); - } - chrp_cmos_clock_write(tm.tm_sec,RTC_SECONDS); - chrp_cmos_clock_write(tm.tm_min,RTC_MINUTES); - chrp_cmos_clock_write(tm.tm_hour,RTC_HOURS); - chrp_cmos_clock_write(tm.tm_mon,RTC_MONTH); - chrp_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); - chrp_cmos_clock_write(tm.tm_year,RTC_YEAR); - - /* The following flags have to be released exactly in this order, - * otherwise the DS12887 (popular MC146818A clone with integrated - * battery and quartz) will not reset the oscillator and will not - * update precisely 500 ms later. You won't find this mentioned in - * the Dallas Semiconductor data sheets, but who believes data - * sheets anyway ... -- Markus Kuhn - */ - chrp_cmos_clock_write(save_control, RTC_CONTROL); - chrp_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); - - if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) - time_state = TIME_OK; - spin_unlock(&rtc_lock); - return 0; -} - -unsigned long __chrp chrp_get_rtc_time(void) -{ - unsigned int year, mon, day, hour, min, sec; - int uip, i; - - /* The Linux interpretation of the CMOS clock register contents: - * When the Update-In-Progress (UIP) flag goes from 1 to 0, the - * RTC registers show the second which has precisely just started. - * Let's hope other operating systems interpret the RTC the same way. - */ - - /* Since the UIP flag is set for about 2.2 ms and the clock - * is typically written with a precision of 1 jiffy, trying - * to obtain a precision better than a few milliseconds is - * an illusion. Only consistency is interesting, this also - * allows to use the routine for /dev/rtc without a potential - * 1 second kernel busy loop triggered by any reader of /dev/rtc. - */ - - for ( i = 0; i<1000000; i++) { - uip = chrp_cmos_clock_read(RTC_FREQ_SELECT); - sec = chrp_cmos_clock_read(RTC_SECONDS); - min = chrp_cmos_clock_read(RTC_MINUTES); - hour = chrp_cmos_clock_read(RTC_HOURS); - day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); - mon = chrp_cmos_clock_read(RTC_MONTH); - year = chrp_cmos_clock_read(RTC_YEAR); - uip |= chrp_cmos_clock_read(RTC_FREQ_SELECT); - if ((uip & RTC_UIP)==0) break; - } - - if (!(chrp_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - { - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } - if ((year += 1900) < 1970) - year += 100; - return mktime(year, mon, day, hour, min, sec); -} - - -void __init chrp_calibrate_decr(void) -{ - struct device_node *cpu; - unsigned int freq, *fp; - - if (via_calibrate_decr()) - return; - - /* - * The cpu node should have a timebase-frequency property - * to tell us the rate at which the decrementer counts. - */ - freq = 16666000; /* hardcoded default */ - cpu = find_type_devices("cpu"); - if (cpu != 0) { - fp = (unsigned int *) - get_property(cpu, "timebase-frequency", NULL); - if (fp != 0) - freq = *fp; - } - printk("time_init: decrementer frequency = %u.%.6u MHz\n", - freq/1000000, freq%1000000); - tb_ticks_per_jiffy = freq / HZ; - tb_to_us = mulhwu_scale_factor(freq, 1000000); -} diff -Nru a/arch/ppc/kernel/cputable.c b/arch/ppc/kernel/cputable.c --- a/arch/ppc/kernel/cputable.c Tue Feb 19 18:09:00 2002 +++ b/arch/ppc/kernel/cputable.c Tue Feb 19 18:09:00 2002 @@ -26,15 +26,14 @@ extern void __setup_cpu_604(int cpu_nr); extern void __setup_cpu_750(int cpu_nr); extern void __setup_cpu_7400(int cpu_nr); +extern void __setup_cpu_7410(int cpu_nr); extern void __setup_cpu_7450(int cpu_nr); extern void __setup_cpu_power3(int cpu_nr); -extern void __setup_cpu_power4(int cpu_nr); extern void __setup_cpu_8xx(int cpu_nr); extern void __setup_cpu_generic(int cpu_nr); -#define CLASSIC_PPC (!defined(CONFIG_8xx) && \ - !defined(CONFIG_4xx) && !defined(CONFIG_POWER3) && \ - !defined(CONFIG_POWER4) && !defined(CONFIG_PPC_ISERIES)) +#define CLASSIC_PPC (!defined(CONFIG_8xx) && !defined(CONFIG_4xx) && \ + !defined(CONFIG_POWER3) && !defined(CONFIG_PPC_ISERIES)) /* This table only contains "desktop" CPUs, it need to be filled with embedded * ones as well... @@ -113,16 +112,40 @@ 32, 32, __setup_cpu_604 }, - { /* 750 (0x4202, don't support TAU ?) */ - 0xffffffff, 0x00084202, "750", + { /* 740/750 (0x4202, don't support TAU ?) */ + 0xffffffff, 0x00084202, "740/750", CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_HPTE_TABLE, COMMON_PPC, 32, 32, __setup_cpu_750 }, - { /* 750CX */ - 0xffffff00, 0x00082200, "750CX", + { /* 745/755 */ + 0xfffff000, 0x00083000, "745/755", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB | + CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE, + COMMON_PPC, + 32, 32, + __setup_cpu_750 + }, + { /* 750CX (80100 and 8010x?) */ + 0xfffffff0, 0x00080100, "750CX", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB | + CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE, + COMMON_PPC, + 32, 32, + __setup_cpu_750 + }, + { /* 750CX (82201 and 82202) */ + 0xfffffff0, 0x00082200, "750CX", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB | + CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE, + COMMON_PPC, + 32, 32, + __setup_cpu_750 + }, + { /* 750CXe (82214) */ + 0xfffffff0, 0x00082210, "750CXe", CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE, COMMON_PPC, @@ -159,9 +182,18 @@ CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE, COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC, 32, 32, - __setup_cpu_7400 + __setup_cpu_7410 }, - { /* 7450 */ + { /* 7450 2.0 - no doze/nap */ + 0xffffffff, 0x80000200, "7450", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450, + COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC, + 32, 32, + __setup_cpu_7450 + }, + { /* 7450 others */ 0xffff0000, 0x80000000, "7450", CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | @@ -215,15 +247,6 @@ __setup_cpu_power3 }, #endif /* CONFIG_PPC64BRIDGE */ -#ifdef CONFIG_POWER4 - { /* Power4 */ - 0xffff0000, 0x00350000, "Power4", - CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE, - COMMON_PPC | PPC_FEATURE_64, - 128, 128, - __setup_cpu_power4 - }, -#endif /* CONFIG_POWER4 */ #ifdef CONFIG_8xx { /* 8xx */ 0xffff0000, 0x00500000, "8xx", @@ -268,6 +291,20 @@ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB, PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, 32, 32, + 0, /*__setup_cpu_405 */ + }, + { /* STB 04xxx */ + 0xffff0000, 0x41810000, "STB04xxx", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + 32, 32, + 0, /*__setup_cpu_405 */ + }, + { /* NP405L */ + 0xffff0000, 0x41610000, "NP405L", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + 16, 8, 0, /*__setup_cpu_405 */ }, #endif /* CONFIG_4xx */ diff -Nru a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S --- a/arch/ppc/kernel/entry.S Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/entry.S Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.entry.S 1.22 08/15/01 22:43:06 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * PowerPC version @@ -11,6 +11,7 @@ * rewritten by Paul Mackerras. * Copyright (C) 1996 Paul Mackerras. * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * Adaptations for iSeries Lpar by Mike Corrigan & Dave Boutcher * * This file contains the system call entry code, context switch * code, and exception/interrupt return code for PowerPC. @@ -22,7 +23,6 @@ * */ -#include "ppc_asm.h" #include #include #include @@ -31,10 +31,82 @@ #include #include #include +#include +#include +#include "ppc_defs.h" +#ifdef CONFIG_PPC_ISERIES +#include "iSeries_asm.h" +#endif /* CONFIG_PPC_ISERIES */ #undef SHOW_SYSCALLS #undef SHOW_SYSCALLS_TASK +#ifndef CONFIG_PPC_ISERIES /* iSeries version is in iSeries_head.S */ +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception, turning + * on address translation. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + stw r23,_MSR(r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + andi. r23,r23,MSR_PR + mfspr r23,SPRG3 + addi r2,r23,-THREAD /* set r2 to current */ + tovirt(r2,r2) + beq 2f /* if from user, fix up THREAD.regs */ + addi r24,r1,STACK_FRAME_OVERHEAD + stw r24,PT_REGS(r23) +#ifdef CONFIG_ALTIVEC +BEGIN_FTR_SECTION + mfspr r22,SPRN_VRSAVE /* if G4, save vrsave register value */ + stw r22,THREAD_VRSAVE(r23) +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) +#endif /* CONFIG_ALTIVEC */ + b 3f +2: /* if from kernel, check for stack overflow */ + lwz r22,THREAD_INFO-THREAD(r23) + cmplw r1,r22 /* if r1 <= current->thread_info */ + ble- stack_ovf /* then the kernel stack overflowed */ +3: + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + FIX_SRR1(r20,r22) + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + RFI /* jump to handler, enable MMU */ + +/* + * On kernel stack overflow, load up an initial stack pointer + * and call StackOverflow(regs), which should not return. + */ +stack_ovf: + addi r3,r1,STACK_FRAME_OVERHEAD + lis r1,init_thread_union@ha + addi r1,r1,init_thread_union@l + addi r1,r1,THREAD_SIZE-STACK_FRAME_OVERHEAD + lis r24,StackOverflow@ha + addi r24,r24,StackOverflow@l + li r20,MSR_KERNEL + FIX_SRR1(r20,r22) + mtspr SRR0,r24 + mtspr SRR1,r20 + SYNC + RFI +#endif /* CONFIG_PPC_ISERIES */ + #ifdef SHOW_SYSCALLS_TASK .data show_syscalls_task: @@ -45,6 +117,10 @@ * Handle a system call. */ .text + .stabs "arch/ppc/kernel/",N_SO,0,0,0f + .stabs "entry.S",N_SO,0,0,0f +0: + _GLOBAL(DoSyscall) stw r0,THREAD+LAST_SYSCALL(r2) lwz r11,_CCR(r1) /* Clear SO bit in CR */ @@ -86,8 +162,9 @@ beq- 10f cmpi 0,r0,0x6666 /* Special case for 'sys_rt_sigreturn' */ beq- 16f - lwz r10,TASK_PTRACE(r2) - andi. r10,r10,PT_TRACESYS + rlwinm r10,r1,0,0,18 /* current_thread_info() */ + lwz r10,TI_FLAGS(r10) + andi. r10,r10,_TIF_SYSCALL_TRACE bne- 50f cmpli 0,r0,NR_syscalls bge- 66f @@ -142,7 +219,7 @@ bge ret_from_except b 20b /* Traced system call support */ -50: bl syscall_trace +50: bl do_syscall_trace lwz r0,GPR0(r1) /* Restore original registers */ lwz r3,GPR3(r1) lwz r4,GPR4(r1) @@ -177,7 +254,7 @@ oris r10,r10,0x1000 stw r10,_CCR(r1) 60: stw r3,GPR3(r1) /* Update return value */ - bl syscall_trace + bl do_syscall_trace b ret_from_except 66: li r3,ENOSYS b 52b @@ -197,6 +274,9 @@ * On entry, r3 points to the THREAD for the current task, r4 * points to the THREAD for the new task. * + * This routine is always called with interrupts disabled + * (soft disabled for iSeries). + * * Note: there are two ways to get to the "going out" portion * of this code; either by coming in via the entry (_switch) * or via "fork" which must set up an environment equivalent @@ -216,6 +296,7 @@ SAVE_8GPRS(14, r1) SAVE_10GPRS(22, r1) mflr r20 /* Return to switch caller */ + stw r20,INT_FRAME_SIZE+4(r1) mfmsr r22 li r0,MSR_FP /* Disable floating-point */ #ifdef CONFIG_ALTIVEC @@ -223,10 +304,12 @@ oris r0,r0,MSR_VEC@h /* Disable altivec */ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) #endif /* CONFIG_ALTIVEC */ + and. r0,r0,r22 /* FP or altivec enabled? */ + beq+ 1f andc r22,r22,r0 mtmsr r22 isync - stw r20,_NIP(r1) +1: stw r20,_NIP(r1) stw r22,_MSR(r1) stw r20,_LINK(r1) mfcr r20 @@ -260,10 +343,13 @@ .globl ret_from_fork ret_from_fork: +#ifdef CONFIG_SMP bl schedule_tail -#error lwz r0,TASK_PTRACE(r2) - andi. r0,r0,PT_TRACESYS -#error bnel- syscall_trace +#endif + rlwinm r3,r1,0,0,18 + lwz r3,TI_FLAGS(r3) + andi. r0,r3,_TIF_SYSCALL_TRACE + bnel- do_syscall_trace b ret_from_except .globl ret_from_intercept @@ -276,94 +362,115 @@ beq restore .globl ret_from_except ret_from_except: - lwz r3,_MSR(r1) /* Returning to user mode? */ - andi. r3,r3,MSR_PR - beq+ do_signal_ret /* if so, check need_resched and signals */ -#error lwz r3,NEED_RESCHED(r2) - cmpi 0,r3,0 /* check need_resched flag */ - beq+ 7f - bl schedule -#error 7: lwz r5,SIGPENDING(r2) /* Check for pending unblocked signals */ - cmpwi 0,r5,0 - beq+ do_signal_ret - li r3,0 - addi r4,r1,STACK_FRAME_OVERHEAD -#error bl do_signal - .globl do_signal_ret -do_signal_ret: - .globl ret_to_user_hook -ret_to_user_hook: - nop -restore: - lwz r3,_XER(r1) - mtspr XER,r3 - REST_10GPRS(9,r1) - REST_10GPRS(19,r1) - REST_2GPRS(29,r1) + REST_10GPRS(13,r1) + REST_8GPRS(23,r1) REST_GPR(31,r1) - /* make sure we hard disable here, even if rtl is active, to protect - * SRR[01] and SPRG2 -- Cort - */ - mfmsr r0 /* Get current interrupt state */ - rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ + /* Hard-disable interrupts so that current_thread_info()->flags + * can't change between when we test it and when we return + * from the interrupt. */ +recheck: + mfmsr r10 + rlwinm r0,r10,0,17,15 /* clear MSR_EE in r0 */ +#ifdef CONFIG_4xx + rlwinm r0,r0,0,23,21 /* clear MSR_DE in r0 */ +#endif SYNC /* Some chip revs have problems here... */ mtmsr r0 /* Update machine state */ - stwcx. r0,0,r1 /* to clear the reservation */ + lwz r3,_MSR(r1) /* Returning to user mode? */ + andi. r3,r3,MSR_PR + beq+ restore /* if not, just restore regs and return */ + + /* Check current_thread_info()->flags */ + rlwinm r3,r1,0,0,18 + lwz r3,TI_FLAGS(r3) + andi. r0,r3,(_TIF_SIGPENDING|_TIF_NEED_RESCHED) + bne do_work + + .globl ret_to_user_hook +ret_to_user_hook: + nop - /* if returning to user mode, set new sprg2 and save kernel SP */ - lwz r0,_MSR(r1) - andi. r0,r0,MSR_PR - beq+ 1f #ifdef CONFIG_ALTIVEC BEGIN_FTR_SECTION lwz r0,THREAD+THREAD_VRSAVE(r2) mtspr SPRN_VRSAVE,r0 /* if G4, restore VRSAVE reg */ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) #endif /* CONFIG_ALTIVEC */ + addi r0,r1,INT_FRAME_SIZE /* size of frame */ stw r0,THREAD+KSP(r2) /* save kernel stack pointer */ + +#ifndef CONFIG_PPC_ISERIES tophys(r8,r1) CLR_TOP32(r8) mtspr SPRG2,r8 /* phys exception stack pointer */ -1: +#else /* CONFIG_PPC_ISERIES */ + mfspr r2,SPRG1 /* Get Paca address */ + stw r1,PACAKSAVE(r2) /* save exception stack pointer */ +#endif /* CONFIG_PPC_ISERIES */ + + /* interrupts are hard-disabled at this point */ +restore: + REST_8GPRS(4, r1) + REST_GPR(12, r1) + lwz r3,_XER(r1) + mtspr XER,r3 + + PPC405_ERR77(0,r1) + stwcx. r0,0,r1 /* to clear the reservation */ + lwz r3,_CTR(r1) lwz r0,_LINK(r1) mtctr r3 mtlr r0 - REST_4GPRS(3, r1) - REST_2GPRS(7, r1) - /* We have to "dummy" load from the context save area in case - * these instructions cause an MMU fault. If this happens - * after we load SRR0/SRR1, our return context is hosed. -- Dan - */ - lwz r0,GPR0(r1) - lwz r0,GPR2(r1) - lwz r0,GPR1(r1) + lwz r0,_MSR(r1) + lwz r3,_CCR(r1) + FIX_SRR1(r0,r2) + lwz r2,_NIP(r1) + mtcrf 0xFF,r3 - /* We re-use r3,r4 here (the load above was to cause the MMU - * fault if necessary). Using r3,r4 removes the need to "dummy" - * load the CCR and NIP. Since we load them we may as well - * use them. + /* + * We can't afford to take an exception between setting SRR0/1 + * and the rfi. Since GPR0(r1) .. GPR3(r1) are in the same cache + * line, loading r3 here should mean that we should have a HPTE + * (for classic PPC) or TLB entry (for 4xx/8xx) for that cache + * line, even if it isn't covered by a BAT register. + * In addition, the cache line itself will be in L1 cache. + * There is still the possibility of the HPTE getting evicted + * on SMP systems. */ - lwz r3,_CCR(r1) - lwz r4,_NIP(r1) + lwz r3,GPR3(r1) - lwz r0,_MSR(r1) - FIX_SRR1(r0,r2) mtspr SRR1,r0 - mtcrf 0xFF,r3 - mtspr SRR0,r4 + mtspr SRR0,r2 lwz r0,GPR0(r1) lwz r2,GPR2(r1) - lwz r3,GPR3(r1) - lwz r4,GPR4(r1) lwz r1,GPR1(r1) SYNC + PPC405_ERR77_SYNC RFI +do_work: + ori r10,r10,MSR_EE + SYNC + mtmsr r10 /* hard-enable interrupts */ + andi. r0,r3,_TIF_NEED_RESCHED + beq 1f + bl schedule + b recheck +1: + andi. r0,r3,_TIF_SIGPENDING + beq 2f + li r3,0 + addi r4,r1,STACK_FRAME_OVERHEAD + bl do_signal + b recheck +2: + /* nobody uses the TIF_NOTIFY_RESUME bit yet */ + b recheck /* * PROM code for specific machines follows. Put it @@ -375,8 +482,7 @@ * On CHRP, the Run-Time Abstraction Services (RTAS) have to be * called with the MMU off. */ - .globl enter_rtas -enter_rtas: +_GLOBAL(enter_rtas) mflr r0 stw r0,20(r1) lis r4,rtas_data@ha @@ -391,9 +497,9 @@ mfmsr r9 stw r9,8(r1) li r0,0 - ori r0,r0,MSR_EE|MSR_SE|MSR_BE + ori r0,r0,MSR_EE|MSR_SE|MSR_BE|MSR_FE0|MSR_FE1 andc r0,r9,r0 - li r10,MSR_IR|MSR_DR|MSR_FE0|MSR_FE1|MSR_FP + li r10,MSR_IR|MSR_DR|MSR_FP andc r9,r0,r10 SYNC /* disable interrupts so SRR0/1 */ mtmsr r0 /* don't get trashed */ diff -Nru a/arch/ppc/kernel/error_log.c b/arch/ppc/kernel/error_log.c --- a/arch/ppc/kernel/error_log.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,186 +0,0 @@ -/* - * BK Id: SCCS/s.error_log.c 1.6 05/17/01 18:14:21 cort - */ -/* - * arch/ppc/kernel/error_log.c - * - * Copyright (c) 2000 Tilmann Bitterberg - * (tilmann@bitterberg.de) - * - * Error processing of errors found by rtas even-scan routine - * which is done with every heartbeat. (chrp_setup.c) - */ - -#include - -#include - -#include "error_log.h" - -/* ****************************************************************** */ -/* - * EVENT-SCAN - * The whole stuff below here doesn't take any action when it found - * an error, it just prints as much information as possible and - * then its up to the user to decide what to do. - * - * Returns 0 if no errors were found - * Returns 1 if there may be more errors - */ -int ppc_rtas_errorlog_scan(void) -{ -const char *_errlog_severity[] = { -#ifdef VERBOSE_ERRORS - "No Error\n\t\ -Should require no further information", - "Event\n\t\ -This is not really an error, it is an event. I use events\n\t\ -to communicate with RTAS back and forth.", - "Warning\n\t\ -Indicates a non-state-losing error, either fully recovered\n\t\ -by RTAS or not needing recovery. Ignore it.", - "Error sync\n\t\ -May only be fatal to a certain program or thread. Recovery\n\t\ -and continuation is possible, if I only had a handler for\n\t\ -this. Less serious", - "Error\n\t\ -Less serious, but still causing a loss of data and state.\n\t\ -I can't tell you exactly what to do, You have to decide\n\t\ -with help from the target and initiator field, what kind\n\t\ -of further actions may take place.", - "Fatal\n\t\ -Represent a permanent hardware failure and I believe this\n\t\ -affects my overall performance and behaviour. I would not\n\t\ -attempt to continue normal operation." -#else - "No Error", - "Event", - "Warning", - "Error sync", - "Error", - "Fatal" -#endif /* VERBOSE_ERRORS */ -}; - -#if 0 /* unused?? */ -const char *_errlog_disposition[] = { -#ifdef VERBOSE_ERRORS - "Fully recovered\n\t\ -There was an error, but it is fully recovered by RTAS.", - "Limited recovery\n\t\ -RTAS was able to recover the state of the machine, but some\n\t\ -feature of the machine has been disabled or lost (for example\n\t\ -error checking) or performance may suffer.", - "Not recovered\n\t\ -Whether RTAS did not try to recover anything or recovery failed:\n\t\ -HOUSTON, WE HAVE A PROBLEM!" -#else - "Fully recovered", - "Limited recovery", - "Not recovered" -#endif /* VERBOSE_ERRORS */ -}; -#endif - -const char *_errlog_extended[] = { -#ifdef VERBOSE_ERRORS - "Not present\n\t\ -Sad, the RTAS call didn't return an extended error log.", - "Present\n\t\ -The extended log is present and hopefully it contains a lot of\n\t\ -useful information, which leads to the solution of the problem." -#else - "Not present", - "Present" -#endif /* VERBOSE_ERRORS */ -}; - -const char *_errlog_initiator[] = { - "Unknown or not applicable", - "CPU", - "PCI", - "ISA", - "Memory", - "Power management" -}; - -const char *_errlog_target[] = { - "Unknown or not applicable", - "CPU", - "PCI", - "ISA", - "Memory", - "Power management" -}; - rtas_error_log error_log; - char logdata[1024]; - int error; -#if 0 /* unused?? */ - int retries = 0; /* if HW error, try 10 times */ -#endif - - error = call_rtas ("event-scan", 4, 1, (unsigned long *)&error_log, - INTERNAL_ERROR | EPOW_WARNING, - 0, __pa(logdata), 1024); - - if (error == 1) /* no errors found */ - return 0; - - if (error == -1) { - printk(KERN_ERR "Unable to get errors. Do you a favor and throw this box away\n"); - return 0; - } - if (error_log.version != 1) - printk(KERN_WARNING "Unknown version (%d), please implement me\n", - error_log.version); - - switch (error_log.disposition) { - case DISP_FULLY_RECOVERED: - /* there was an error, but everything is fine now */ - return 0; - case DISP_NOT_RECOVERED: - printk("We have a really serious Problem!\n"); - case DISP_LIMITED_RECOVERY: - printk("Error classification\n"); - printk("Severity : %s\n", - ppc_rtas_errorlog_check_severity (error_log)); - printk("Initiator : %s\n", - ppc_rtas_errorlog_check_initiator (error_log)); - printk("Target : %s\n", - ppc_rtas_errorlog_check_target (error_log)); - printk("Type : %s\n", - ppc_rtas_errorlog_check_type (error_log)); - printk("Ext. log : %s\n", - ppc_rtas_errorlog_check_extended (error_log)); - if (error_log.extended) - ppc_rtas_errorlog_disect_extended (logdata); - return 1; - default: - /* nothing */ - break; - } - return 0; -} -/* ****************************************************************** */ -const char * ppc_rtas_errorlog_check_type (rtas_error_log error_log) -{ - const char *_errlog_type[] = { - "unknown type", - "too many tries failed", - "TCE error", - "RTAS device failed", - "target timed out", - "parity error on data", /* 5 */ - "parity error on address", - "parity error on external cache", - "access to invalid address", - "uncorrectable ECC error", - "corrected ECC error" /* 10 */ - }; - if (error_log.type == TYPE_EPOW) - return "EPOW"; - if (error_log.type >= TYPE_PMGM_POWER_SW_ON) - return "PowerMGM Event (not handled right now)"; - return _errlog_type[error_log.type]; -} - diff -Nru a/arch/ppc/kernel/error_log.h b/arch/ppc/kernel/error_log.h --- a/arch/ppc/kernel/error_log.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,98 +0,0 @@ -/* - * BK Id: SCCS/s.error_log.h 1.5 05/17/01 18:14:21 cort - */ -#ifndef __ERROR_LOG_H__ -#define __ERROR_LOG_H__ - -#define VERBOSE_ERRORS 1 /* Maybe I enlarge the kernel too much */ -#undef VERBOSE_ERRORS - -/* Event classes */ -/* XXX: Endianess correct? NOW*/ -#define INTERNAL_ERROR 0x80000000 /* set bit 0 */ -#define EPOW_WARNING 0x40000000 /* set bit 1 */ -#define POWERMGM_EVENTS 0x20000000 /* set bit 2 */ - -/* event-scan returns */ -#define SEVERITY_FATAL 0x5 -#define SEVERITY_ERROR 0x4 -#define SEVERITY_ERROR_SYNC 0x3 -#define SEVERITY_WARNING 0x2 -#define SEVERITY_EVENT 0x1 -#define SEVERITY_NO_ERROR 0x0 -#define DISP_FULLY_RECOVERED 0x0 -#define DISP_LIMITED_RECOVERY 0x1 -#define DISP_NOT_RECOVERED 0x2 -#define PART_PRESENT 0x0 -#define PART_NOT_PRESENT 0x1 -#define INITIATOR_UNKNOWN 0x0 -#define INITIATOR_CPU 0x1 -#define INITIATOR_PCI 0x2 -#define INITIATOR_ISA 0x3 -#define INITIATOR_MEMORY 0x4 -#define INITIATOR_POWERMGM 0x5 -#define TARGET_UNKNOWN 0x0 -#define TARGET_CPU 0x1 -#define TARGET_PCI 0x2 -#define TARGET_ISA 0x3 -#define TARGET_MEMORY 0x4 -#define TARGET_POWERMGM 0x5 -#define TYPE_RETRY 0x01 -#define TYPE_TCE_ERR 0x02 -#define TYPE_INTERN_DEV_FAIL 0x03 -#define TYPE_TIMEOUT 0x04 -#define TYPE_DATA_PARITY 0x05 -#define TYPE_ADDR_PARITY 0x06 -#define TYPE_CACHE_PARITY 0x07 -#define TYPE_ADDR_INVALID 0x08 -#define TYPE_ECC_UNCORR 0x09 -#define TYPE_ECC_CORR 0x0a -#define TYPE_EPOW 0x40 -/* I don't add PowerMGM events right now, this is a different topic */ -#define TYPE_PMGM_POWER_SW_ON 0x60 -#define TYPE_PMGM_POWER_SW_OFF 0x61 -#define TYPE_PMGM_LID_OPEN 0x62 -#define TYPE_PMGM_LID_CLOSE 0x63 -#define TYPE_PMGM_SLEEP_BTN 0x64 -#define TYPE_PMGM_WAKE_BTN 0x65 -#define TYPE_PMGM_BATTERY_WARN 0x66 -#define TYPE_PMGM_BATTERY_CRIT 0x67 -#define TYPE_PMGM_SWITCH_TO_BAT 0x68 -#define TYPE_PMGM_SWITCH_TO_AC 0x69 -#define TYPE_PMGM_KBD_OR_MOUSE 0x6a -#define TYPE_PMGM_ENCLOS_OPEN 0x6b -#define TYPE_PMGM_ENCLOS_CLOSED 0x6c -#define TYPE_PMGM_RING_INDICATE 0x6d -#define TYPE_PMGM_LAN_ATTENTION 0x6e -#define TYPE_PMGM_TIME_ALARM 0x6f -#define TYPE_PMGM_CONFIG_CHANGE 0x70 -#define TYPE_PMGM_SERVICE_PROC 0x71 - -typedef struct _rtas_error_log { - unsigned long version:8; /* Architectural version */ - unsigned long severity:3; /* Severity level of error */ - unsigned long disposition:2; /* Degree of recovery */ - unsigned long extended:1; /* extended log present? */ - unsigned long /* reserved */ :2; /* Reserved for future use */ - unsigned long initiator:4; /* Initiator of event */ - unsigned long target:4; /* Target of failed operation */ - unsigned long type:8; /* General event or error*/ - unsigned long extended_log_length:32; /* length in bytes */ -} rtas_error_log; - -/* ****************************************************************** */ -#define ppc_rtas_errorlog_check_severity(x) \ - (_errlog_severity[x.severity]) -#define ppc_rtas_errorlog_check_target(x) \ - (_errlog_target[x.target]) -#define ppc_rtas_errorlog_check_initiator(x) \ - (_errlog_initiator[x.initiator]) -#define ppc_rtas_errorlog_check_extended(x) \ - (_errlog_extended[x.extended]) -#define ppc_rtas_errorlog_disect_extended(x) \ - do { /* implement me */ } while(0) -extern const char * ppc_rtas_errorlog_check_type (rtas_error_log error_log); -extern int ppc_rtas_errorlog_scan(void); - - -#endif /* __ERROR_LOG_H__ */ diff -Nru a/arch/ppc/kernel/feature.c b/arch/ppc/kernel/feature.c --- a/arch/ppc/kernel/feature.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1300 +0,0 @@ -/* - * BK Id: SCCS/s.feature.c 1.21 09/08/01 15:47:42 paulus - */ -/* - * arch/ppc/kernel/feature.c - * - * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) - * Ben. Herrenschmidt (benh@kernel.crashing.org) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#undef DEBUG_FEATURE - -#define MAX_FEATURE_CONTROLLERS 2 -#define FREG(c,r) (&(((c)->reg)[(r)>>2])) - -/* Keylargo reg. access. */ -#define KL_FCR(r) (keylargo_base + ((r) >> 2)) -#define KL_IN(r) (in_le32(KL_FCR(r))) -#define KL_OUT(r,v) (out_le32(KL_FCR(r), (v))) -#define KL_BIS(r,v) (KL_OUT((r), KL_IN(r) | (v))) -#define KL_BIC(r,v) (KL_OUT((r), KL_IN(r) & ~(v))) -#define KL_GPIO_IN(r) (in_8(((volatile u8 *)keylargo_base)+(r))) -#define KL_GPIO_OUT(r,v) (out_8(((volatile u8 *)keylargo_base)+(r), (v))) -#define KL_LOCK() spin_lock_irqsave(&keylargo->lock, flags) -#define KL_UNLOCK() spin_unlock_irqrestore(&keylargo->lock, flags) - -/* Uni-N reg. access. Note that Uni-N regs are big endian */ -#define UN_REG(r) (uninorth_base + ((r) >> 2)) -#define UN_IN(r) (in_be32(UN_REG(r))) -#define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) -#define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) -#define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) - -typedef struct feature_bit { - int reg; /* reg. offset from mac-io base */ - unsigned int polarity; /* 0 = normal, 1 = inverse */ - unsigned int mask; /* bit mask */ -} fbit; - -/* Those features concern for OHare-based PowerBooks (2400, 3400, 3500) - */ -static fbit feature_bits_ohare_pbook[] __pmacdata = { - {0x38,0,0}, /* FEATURE_null */ - {0x38,0,OH_SCC_RESET}, /* FEATURE_Serial_reset */ - {0x38,0,OH_SCC_ENABLE}, /* FEATURE_Serial_enable */ - {0x38,0,OH_SCCA_IO}, /* FEATURE_Serial_IO_A */ - {0x38,0,OH_SCCB_IO}, /* FEATURE_Serial_IO_B */ - {0x38,0,OH_FLOPPY_ENABLE}, /* FEATURE_SWIM3_enable */ - {0x38,0,OH_MESH_ENABLE}, /* FEATURE_MESH_enable */ - {0x38,0,OH_IDE0_ENABLE}, /* FEATURE_IDE0_enable */ - {0x38,1,OH_IDE0_RESET_N}, /* FEATURE_IDE0_reset */ - {0x38,0,OH_IOBUS_ENABLE}, /* FEATURE_IOBUS_enable */ - {0x38,1,OH_BAY_RESET_N}, /* FEATURE_Mediabay_reset */ - {0x38,1,OH_BAY_POWER_N}, /* FEATURE_Mediabay_power */ - {0x38,0,OH_BAY_PCI_ENABLE}, /* FEATURE_Mediabay_PCI_enable */ - {0x38,0,OH_BAY_IDE_ENABLE}, /* FEATURE_IDE1_enable */ - {0x38,1,OH_IDE1_RESET_N}, /* FEATURE_IDE1_reset */ - {0x38,0,OH_BAY_FLOPPY_ENABLE}, /* FEATURE_Mediabay_floppy_enable */ - {0x38,0,0}, /* FEATURE_BMac_reset */ - {0x38,0,0}, /* FEATURE_BMac_IO_enable */ - {0x38,0,0}, /* FEATURE_Modem_power */ - {0x38,0,0}, /* FEATURE_Slow_SCC_PCLK */ - {0x38,0,0}, /* FEATURE_Sound_Power */ - {0x38,0,0}, /* FEATURE_Sound_CLK_Enable */ - {0x38,0,0}, /* FEATURE_IDE2_enable */ - {0x38,0,0}, /* FEATURE_IDE2_reset */ - {0x38,0,0}, /* FEATURE_Mediabay_IDE_switch */ - {0x38,0,0}, /* FEATURE_Mediabay_content */ - {0x38,0,0}, /* FEATURE_Airport_reset */ -}; - -/* Those bits concern heathrow-based desktop machines (Beige G3s). We have removed - * the SCC related bits and init them once. They have proven to occasionally cause - * problems with the desktop units. - */ -static fbit feature_bits_heathrow[] __pmacdata = { - {0x38,0,0}, /* FEATURE_null */ - {0x38,0,0}, /* FEATURE_Serial_reset */ - {0x38,0,0}, /* FEATURE_Serial_enable */ - {0x38,0,0}, /* FEATURE_Serial_IO_A */ - {0x38,0,0}, /* FEATURE_Serial_IO_B */ - {0x38,0,HRW_SWIM_ENABLE}, /* FEATURE_SWIM3_enable */ - {0x38,0,HRW_MESH_ENABLE}, /* FEATURE_MESH_enable */ - {0x38,0,HRW_IDE0_ENABLE}, /* FEATURE_IDE0_enable */ - {0x38,1,HRW_IDE0_RESET_N}, /* FEATURE_IDE0_reset */ - {0x38,0,HRW_IOBUS_ENABLE}, /* FEATURE_IOBUS_enable */ - {0x38,1,0}, /* FEATURE_Mediabay_reset */ - {0x38,1,0}, /* FEATURE_Mediabay_power */ - {0x38,0,0}, /* FEATURE_Mediabay_PCI_enable */ - {0x38,0,HRW_BAY_IDE_ENABLE}, /* FEATURE_IDE1_enable */ - {0x38,1,HRW_IDE1_RESET_N}, /* FEATURE_IDE1_reset */ - {0x38,0,0}, /* FEATURE_Mediabay_floppy_enable */ - {0x38,0,HRW_BMAC_RESET}, /* FEATURE_BMac_reset */ - {0x38,0,HRW_BMAC_IO_ENABLE}, /* FEATURE_BMac_IO_enable */ - {0x38,1,0}, /* FEATURE_Modem_power */ - {0x38,0,HRW_SLOW_SCC_PCLK}, /* FEATURE_Slow_SCC_PCLK */ - {0x38,1,0}, /* FEATURE_Sound_Power */ - {0x38,0,0}, /* FEATURE_Sound_CLK_Enable */ - {0x38,0,0}, /* FEATURE_IDE2_enable */ - {0x38,0,0}, /* FEATURE_IDE2_reset */ - {0x38,0,0}, /* FEATURE_Mediabay_IDE_switch */ - {0x38,0,0}, /* FEATURE_Mediabay_content */ - {0x38,0,0}, /* FEATURE_Airport_reset */ -}; - -/* Those bits concern heathrow-based PowerBooks (wallstreet/mainstreet). - * Heathrow-based desktop macs (Beige G3s) are _not_ handled here - */ -static fbit feature_bits_wallstreet[] __pmacdata = { - {0x38,0,0}, /* FEATURE_null */ - {0x38,0,HRW_RESET_SCC}, /* FEATURE_Serial_reset */ - {0x38,0,HRW_SCC_ENABLE}, /* FEATURE_Serial_enable */ - {0x38,0,HRW_SCCA_IO}, /* FEATURE_Serial_IO_A */ - {0x38,0,HRW_SCCB_IO}, /* FEATURE_Serial_IO_B */ - {0x38,0,HRW_SWIM_ENABLE}, /* FEATURE_SWIM3_enable */ - {0x38,0,HRW_MESH_ENABLE}, /* FEATURE_MESH_enable */ - {0x38,0,HRW_IDE0_ENABLE}, /* FEATURE_IDE0_enable */ - {0x38,1,HRW_IDE0_RESET_N}, /* FEATURE_IDE0_reset */ - {0x38,0,HRW_IOBUS_ENABLE}, /* FEATURE_IOBUS_enable */ - {0x38,1,HRW_BAY_RESET_N}, /* FEATURE_Mediabay_reset */ - {0x38,1,HRW_BAY_POWER_N}, /* FEATURE_Mediabay_power */ - {0x38,0,HRW_BAY_PCI_ENABLE}, /* FEATURE_Mediabay_PCI_enable */ - {0x38,0,HRW_BAY_IDE_ENABLE}, /* FEATURE_IDE1_enable */ - {0x38,1,HRW_IDE1_RESET_N}, /* FEATURE_IDE1_reset */ - {0x38,0,HRW_BAY_FLOPPY_ENABLE}, /* FEATURE_Mediabay_floppy_enable */ - {0x38,0,HRW_BMAC_RESET}, /* FEATURE_BMac_reset */ - {0x38,0,HRW_BMAC_IO_ENABLE}, /* FEATURE_BMac_IO_enable */ - {0x38,1,HRW_MODEM_POWER_N}, /* FEATURE_Modem_power */ - {0x38,0,HRW_SLOW_SCC_PCLK}, /* FEATURE_Slow_SCC_PCLK */ - {0x38,1,HRW_SOUND_POWER_N}, /* FEATURE_Sound_Power */ - {0x38,0,HRW_SOUND_CLK_ENABLE}, /* FEATURE_Sound_CLK_Enable */ - {0x38,0,0}, /* FEATURE_IDE2_enable */ - {0x38,0,0}, /* FEATURE_IDE2_reset */ - {0x38,0,0}, /* FEATURE_Mediabay_IDE_switch */ - {0x38,0,0}, /* FEATURE_Mediabay_content */ - {0x38,0,0}, /* FEATURE_Airport_reset */ -}; - -/* - * Those bits are from a 1999 G3 PowerBook, with a paddington chip. - * Mostly the same as the heathrow. They are used on both PowerBooks - * and desktop machines using the paddington chip - */ -static fbit feature_bits_paddington[] __pmacdata = { - {0x38,0,0}, /* FEATURE_null */ - {0x38,0,PADD_RESET_SCC}, /* FEATURE_Serial_reset */ - {0x38,0,HRW_SCC_ENABLE}, /* FEATURE_Serial_enable */ - {0x38,0,HRW_SCCA_IO}, /* FEATURE_Serial_IO_A */ - {0x38,0,HRW_SCCB_IO}, /* FEATURE_Serial_IO_B */ - {0x38,0,HRW_SWIM_ENABLE}, /* FEATURE_SWIM3_enable */ - {0x38,0,HRW_MESH_ENABLE}, /* FEATURE_MESH_enable */ - {0x38,0,HRW_IDE0_ENABLE}, /* FEATURE_IDE0_enable */ - {0x38,1,HRW_IDE0_RESET_N}, /* FEATURE_IDE0_reset */ - {0x38,0,HRW_IOBUS_ENABLE}, /* FEATURE_IOBUS_enable */ - {0x38,1,HRW_BAY_RESET_N}, /* FEATURE_Mediabay_reset */ - {0x38,1,HRW_BAY_POWER_N}, /* FEATURE_Mediabay_power */ - {0x38,0,HRW_BAY_PCI_ENABLE}, /* FEATURE_Mediabay_PCI_enable */ - {0x38,0,HRW_BAY_IDE_ENABLE}, /* FEATURE_IDE1_enable */ - {0x38,1,HRW_IDE1_RESET_N}, /* FEATURE_IDE1_reset */ - {0x38,0,HRW_BAY_FLOPPY_ENABLE}, /* FEATURE_Mediabay_floppy_enable */ - {0x38,0,HRW_BMAC_RESET}, /* FEATURE_BMac_reset */ - {0x38,0,HRW_BMAC_IO_ENABLE}, /* FEATURE_BMac_IO_enable */ - {0x38,1,PADD_MODEM_POWER_N}, /* FEATURE_Modem_power */ - {0x38,0,HRW_SLOW_SCC_PCLK}, /* FEATURE_Slow_SCC_PCLK */ - {0x38,1,HRW_SOUND_POWER_N}, /* FEATURE_Sound_Power */ - {0x38,0,HRW_SOUND_CLK_ENABLE}, /* FEATURE_Sound_CLK_Enable */ - {0x38,0,0}, /* FEATURE_IDE2_enable */ - {0x38,0,0}, /* FEATURE_IDE2_reset */ - {0x38,0,0}, /* FEATURE_Mediabay_IDE_switch */ - {0x38,0,0}, /* FEATURE_Mediabay_content */ - {0x38,0,0}, /* FEATURE_Airport_reset */ -}; - -/* Those bits are for Core99 machines (iBook,G4,iMacSL/DV,Pismo,...). - * Note: Different sets may be needed for iBook, especially for sound - */ -static fbit feature_bits_keylargo[] __pmacdata = { - {0x38,0,0}, /* FEATURE_null */ - {0x38,0,KL0_SCC_RESET}, /* FEATURE_Serial_reset */ - {0x38,0,KL0_SERIAL_ENABLE}, /* FEATURE_Serial_enable */ - {0x38,0,KL0_SCC_A_INTF_ENABLE}, /* FEATURE_Serial_IO_A */ - {0x38,0,KL0_SCC_B_INTF_ENABLE}, /* FEATURE_Serial_IO_B */ - {0x38,0,0}, /* FEATURE_SWIM3_enable */ - {0x38,0,0}, /* FEATURE_MESH_enable */ - {0x3c,0,KL1_EIDE0_ENABLE}, /* FEATURE_IDE0_enable */ - {0x3c,1,KL1_EIDE0_RESET_N}, /* FEATURE_IDE0_reset */ - {0x38,0,0}, /* FEATURE_IOBUS_enable */ - {0x34,1,KL_MBCR_MB0_DEV_RESET}, /* FEATURE_Mediabay_reset */ - {0x34,1,KL_MBCR_MB0_DEV_POWER}, /* FEATURE_Mediabay_power */ - {0x38,0,0}, /* FEATURE_Mediabay_PCI_enable */ - {0x3c,0,KL1_EIDE1_ENABLE}, /* FEATURE_IDE1_enable */ - {0x3c,1,KL1_EIDE1_RESET_N}, /* FEATURE_IDE1_reset */ - {0x38,0,0}, /* FEATURE_Mediabay_floppy_enable */ - {0x38,0,0}, /* FEATURE_BMac_reset */ - {0x38,0,0}, /* FEATURE_BMac_IO_enable */ - {0x40,1,KL2_MODEM_POWER_N}, /* FEATURE_Modem_power */ - {0x38,0,0}, /* FEATURE_Slow_SCC_PCLK */ - {0x38,0,0}, /* FEATURE_Sound_Power */ - {0x38,0,0}, /* FEATURE_Sound_CLK_Enable */ - {0x3c,0,KL1_UIDE_ENABLE}, /* FEATURE_IDE2_enable */ - {0x3c,1,KL1_UIDE_RESET_N}, /* FEATURE_IDE2_reset */ - {0x34,0,KL_MBCR_MB0_DEV_ENABLE},/* FEATURE_Mediabay_IDE_switch */ - {0x34,0,KL_MBCR_MB0_ENABLE}, /* FEATURE_Mediabay_content */ - {0x40,1,KL2_AIRPORT_RESET_N}, /* FEATURE_Airport_reset */ -}; - -/* definition of a feature controller object */ -struct feature_controller { - fbit* bits; - volatile u32* reg; - struct device_node* device; - spinlock_t lock; -}; - -/* static functions */ -static struct feature_controller* -feature_add_controller(struct device_node *controller_device, fbit* bits); - -static struct feature_controller* -feature_lookup_controller(struct device_node *device); - -static void uninorth_init(void); -static void keylargo_init(void); -#ifdef CONFIG_PMAC_PBOOK -static void heathrow_prepare_for_sleep(struct feature_controller* ctrler); -static void heathrow_wakeup(struct feature_controller* ctrler); -static void core99_prepare_for_sleep(struct feature_controller* ctrler); -static void core99_wake_up(struct feature_controller* ctrler); -#endif /* CONFIG_PMAC_PBOOK */ - -/* static variables */ -static struct feature_controller controllers[MAX_FEATURE_CONTROLLERS]; -static int controller_count = 0; - -/* Core99 stuffs */ -/*static*/ volatile u32* uninorth_base; -static volatile u32* keylargo_base; -static struct feature_controller* keylargo; -static int uninorth_rev; -static int keylargo_rev; -static u32 board_features; -static int airport_pwr_state; -static struct device_node* airport_dev; -static struct device_node* uninorth_fw; - -/* Feature bits for Apple motherboards */ -#define FTR_NEED_OPENPIC_TWEAK 0x00000001 -#define FTR_CAN_NAP 0x00000002 -#define FTR_HAS_FW_POWER 0x00000004 -#define FTR_CAN_SLEEP 0x00000008 - -/* This table currently lacks most oldworld machines, but - * they currently don't need it so... - * - * Todo: The whole feature_xxx mecanism need to be redone - * some way to be able to handle the new kind of features - * exposed by core99. At this point, the main "tables" will - * probably be in this table, which will have to be filled with - * all known machines - */ -/* Warning: Don't change ordering of entries as some machines - * adverstise beeing compatible with several models. In those - * case, the "highest" has to be first - */ -static struct board_features_t { - char* compatible; - u32 features; -} board_features_datas[] __initdata = -{ - { "AAPL,PowerMac G3", 0 }, /* Beige G3 */ - { "iMac,1", 0 }, /* First iMac (gossamer) */ - { "PowerMac1,1", 0 }, /* B&W G3 / Yikes */ - { "PowerMac1,2", 0 }, /* B&W G3 / Yikes */ - { "PowerMac2,1", FTR_CAN_SLEEP }, /* r128 based iMac */ - { "PowerMac2,2", FTR_HAS_FW_POWER|FTR_CAN_SLEEP }, /* Summer 2000 iMac */ - { "PowerMac4,1", FTR_CAN_SLEEP }, /* iMac "Flower Power" */ - { "PowerMac3,1", FTR_NEED_OPENPIC_TWEAK }, /* Sawtooth (G4) */ - { "PowerMac3,2", 0 }, /* G4/Dual G4 */ - { "PowerMac3,3", FTR_NEED_OPENPIC_TWEAK }, /* G4/Dual G4 */ - { "PowerMac3,4", 0 }, /* QuickSilver G4 */ - { "PowerMac3,5", 0 }, /* QuickSilver G4 */ - { "PowerMac5,1", FTR_CAN_NAP }, /* Cube */ - { "AAPL,3400/2400", FTR_CAN_SLEEP }, /* 2400/3400 PowerBook */ - { "AAPL,3500", FTR_CAN_SLEEP }, /* 3500 PowerBook (G3) */ - { "AAPL,PowerBook1998", FTR_CAN_SLEEP }, /* Wallstreet PowerBook */ - { "PowerBook1,1", FTR_CAN_SLEEP }, /* 101 (Lombard) PowerBook */ - { "PowerBook4,1", FTR_CAN_NAP|FTR_CAN_SLEEP| /* New polycarbonate iBook */ - 0/*FTR_HAS_FW_POWER*/ }, - { "PowerBook2,1", FTR_CAN_SLEEP }, /* iBook */ - { "PowerBook2,2", FTR_CAN_SLEEP /*| FTR_CAN_NAP*/ }, /* iBook FireWire */ - { "PowerBook3,1", FTR_CAN_SLEEP|FTR_CAN_NAP| /* PowerBook 2000 (Pismo) */ - FTR_HAS_FW_POWER }, - { "PowerBook3,2", FTR_CAN_NAP|FTR_CAN_SLEEP| /* PowerBook Titanium */ - 0/*FTR_HAS_FW_POWER*/ }, - { NULL, 0 } -}; - -extern unsigned long powersave_nap; - -void __init -feature_init(void) -{ - struct device_node *np; - u32 *rev; - int i; - char* model; - - if (_machine != _MACH_Pmac) - return; - - np = find_path_device("/"); - if (!np) { - printk(KERN_ERR "feature.c: Can't find device-tree root !\n"); - return; - } - model = (char*)get_property(np, "model", NULL); - if (model) { - printk("PowerMac model: %s\n", model); - /* Figure out motherboard type & options */ - for(i=0;board_features_datas[i].compatible;i++) { - if (!strcmp(board_features_datas[i].compatible, model)) { - board_features = board_features_datas[i].features; - break; - } - } - } - - /* Set default value of powersave_nap on machines that support it */ - if (board_features & FTR_CAN_NAP) - powersave_nap = 1; - - /* Track those poor mac-io's */ - np = find_devices("mac-io"); - while (np != NULL) { - /* KeyLargo contains several (5 ?) FCR registers in mac-io, - * plus some gpio's which could eventually be handled here. - */ - if (device_is_compatible(np, "Keylargo")) { - struct feature_controller* ctrler = - feature_add_controller(np, feature_bits_keylargo); - if (ctrler) { - keylargo = ctrler; - keylargo_base = ctrler->reg; - rev = (u32 *)get_property(ctrler->device, "revision-id", NULL); - if (rev) - keylargo_rev = *rev; - - rev = (u32 *)get_property(ctrler->device, "device-id", NULL); - if (rev && (*rev) == 0x0025) - keylargo_rev |= KL_PANGEA_REV; - } - } else if (device_is_compatible(np, "paddington")) { - feature_add_controller(np, feature_bits_paddington); - /* We disable the modem power bit on Yikes as it can - * do bad things (it's the nvram power) - */ - if (machine_is_compatible("PowerMac1,1") || - machine_is_compatible("PowerMac1,2")) { - feature_bits_paddington[FEATURE_Modem_power].mask = 0; - } - } else if (machine_is_compatible("AAPL,PowerBook1998")) { - feature_add_controller(np, feature_bits_wallstreet); - } else { - struct feature_controller* ctrler = - feature_add_controller(np, feature_bits_heathrow); - if (ctrler) - out_le32(FREG(ctrler,HEATHROW_FEATURE_REG), - in_le32(FREG(ctrler,HEATHROW_FEATURE_REG)) | HRW_DEFAULTS); - - } - np = np->next; - } - if (controller_count == 0) - { - np = find_devices("ohare"); - if (np) { - if (find_devices("via-pmu") != NULL) - feature_add_controller(np, feature_bits_ohare_pbook); - else - /* else not sure; maybe this is a Starmax? */ - feature_add_controller(np, NULL); - } - } - - /* Locate core99 Uni-N */ - np = find_devices("uni-n"); - if (np && np->n_addrs > 0) { - uninorth_base = ioremap(np->addrs[0].address, 0x1000); - uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); - printk("Uninorth at 0x%08x\n", np->addrs[0].address); - } - if (uninorth_base && keylargo_base) - printk("Uni-N revision: %d, KeyLargo revision: %d %s\n", - uninorth_rev, keylargo_rev, - (keylargo_rev & KL_PANGEA_REV) ? "(Pangea chipset)" : ""); - - if (uninorth_base) - uninorth_init(); - if (keylargo_base) - keylargo_init(); - - if (controller_count) - printk(KERN_INFO "Registered %d feature controller(s)\n", controller_count); - -#if defined(CONFIG_PMAC_PBOOK) && !defined(CONFIG_DMASOUND_AWACS) - /* On PowerBooks, we disable the sound chip when dmasound is a module - * or not used at all - */ - if (controller_count && find_devices("via-pmu") != NULL) { - feature_clear(controllers[0].device, FEATURE_Sound_power); - feature_clear(controllers[0].device, FEATURE_Sound_CLK_enable); - } -#endif - -#ifdef CONFIG_PMAC_PBOOK - /* On PowerBooks, we disable the serial ports by default, they - * will be re-enabled by the driver - */ -#ifndef CONFIG_XMON - if (controller_count && find_devices("via-pmu") != NULL) { - feature_set_modem_power(NULL, 0); - feature_clear(controllers[0].device, FEATURE_Serial_IO_A); - feature_clear(controllers[0].device, FEATURE_Serial_IO_B); - feature_clear(controllers[0].device, FEATURE_Serial_enable); - if (controller_count > 1) { - feature_clear(controllers[1].device, FEATURE_Serial_IO_A); - feature_clear(controllers[1].device, FEATURE_Serial_IO_B); - feature_clear(controllers[1].device, FEATURE_Serial_enable); - } - } -#endif -#endif -} - -static struct feature_controller __init * -feature_add_controller(struct device_node *controller_device, fbit* bits) -{ - struct feature_controller* controller; - - if (controller_count >= MAX_FEATURE_CONTROLLERS) { - printk(KERN_INFO "Feature controller %s skipped(MAX:%d)\n", - controller_device->full_name, MAX_FEATURE_CONTROLLERS); - return NULL; - } - controller = &controllers[controller_count]; - - controller->bits = bits; - controller->device = controller_device; - if (controller_device->n_addrs == 0) { - printk(KERN_ERR "No addresses for %s\n", - controller_device->full_name); - return NULL; - } - - /* We remap the entire mac-io here. Normally, this will just - * give us back our already existing BAT mapping - */ - controller->reg = (volatile u32 *)ioremap( - controller_device->addrs[0].address, - controller_device->addrs[0].size); - - if (bits == NULL) { - printk(KERN_INFO "Twiddling the magic ohare bits\n"); - out_le32(FREG(controller,OHARE_FEATURE_REG), STARMAX_FEATURES); - return NULL; - } - - spin_lock_init(&controller->lock); - - controller_count++; - - return controller; -} - -static struct feature_controller __pmac * -feature_lookup_controller(struct device_node *device) -{ - int i; - - if (device == NULL) - return NULL; - - while(device) - { - for (i=0; iparent; - } - -#ifdef DEBUG_FEATURE - printk("feature: <%s> not found on any controller\n", - device->name); -#endif - - return NULL; -} - -int __pmac -feature_set(struct device_node* device, enum system_feature f) -{ - struct feature_controller* controller; - unsigned long flags; - unsigned long value; - fbit* bit; - - if (f >= FEATURE_last) - return -EINVAL; - - controller = feature_lookup_controller(device); - if (!controller) - return -ENODEV; - bit = &controller->bits[f]; - if (!bit->mask) - return -EINVAL; - -#ifdef DEBUG_FEATURE - printk("feature: <%s> setting feature %d in controller @0x%x\n", - device->name, (int)f, (unsigned int)controller->reg); -#endif - - spin_lock_irqsave(&controller->lock, flags); - value = in_le32(FREG(controller, bit->reg)); - value = bit->polarity ? (value & ~bit->mask) : (value | bit->mask); - out_le32(FREG(controller, bit->reg), value); - (void)in_le32(FREG(controller, bit->reg)); - spin_unlock_irqrestore(&controller->lock, flags); - - return 0; -} - -int __pmac -feature_clear(struct device_node* device, enum system_feature f) -{ - struct feature_controller* controller; - unsigned long flags; - unsigned long value; - fbit* bit; - - if (f >= FEATURE_last) - return -EINVAL; - - controller = feature_lookup_controller(device); - if (!controller) - return -ENODEV; - bit = &controller->bits[f]; - if (!bit->mask) - return -EINVAL; - -#ifdef DEBUG_FEATURE - printk("feature: <%s> clearing feature %d in controller @0x%x\n", - device->name, (int)f, (unsigned int)controller->reg); -#endif - - spin_lock_irqsave(&controller->lock, flags); - value = in_le32(FREG(controller, bit->reg)); - value = bit->polarity ? (value | bit->mask) : (value & ~bit->mask); - out_le32(FREG(controller, bit->reg), value); - (void)in_le32(FREG(controller, bit->reg)); - spin_unlock_irqrestore(&controller->lock, flags); - - return 0; -} - -int __pmac -feature_test(struct device_node* device, enum system_feature f) -{ - struct feature_controller* controller; - unsigned long value; - fbit* bit; - - if (f >= FEATURE_last) - return -EINVAL; - - controller = feature_lookup_controller(device); - if (!controller) - return -ENODEV; - bit = &controller->bits[f]; - if (!bit->mask) - return -EINVAL; - -#ifdef DEBUG_FEATURE - printk("feature: <%s> clearing feature %d in controller @0x%x\n", - device->name, (int)f, (unsigned int)controller->reg); -#endif - /* If one feature contains several bits, all of them must be set - * for value to be true, or all of them must be 0 if polarity is - * inverse - */ - value = (in_le32(FREG(controller, bit->reg)) & bit->mask); - return bit->polarity ? (value == 0) : (value == bit->mask); -} - -int __pmac -feature_can_sleep(void) -{ - return ((board_features & FTR_CAN_SLEEP) != 0); -} - -/* - * Core99 functions - * - * Note: We currently assume there is _one_ UniN chip and _one_ KeyLargo - * chip, which is the case on all Core99 machines so far - */ - -/* Only one GMAC is assumed */ -void __pmac -feature_set_gmac_power(struct device_node* device, int power) -{ - unsigned long flags; - - if (!uninorth_base || !keylargo) - return; - - /* TODO: Handle save/restore of PCI config space here - */ - - /* XXX We use the keylargo spinlock, but we never - * have uninorth without keylargo, so... - */ - KL_LOCK(); - if (power) - UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC); - else - UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC); - (void)UN_IN(UNI_N_CLOCK_CNTL); - KL_UNLOCK(); - udelay(20); -} - -void __pmac -feature_gmac_phy_reset(struct device_node* device) -{ - unsigned long flags; - - if (!keylargo_base || !keylargo) - return; - - KL_LOCK(); - KL_GPIO_OUT(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE); - (void)KL_GPIO_IN(KL_GPIO_ETH_PHY_RESET); - KL_UNLOCK(); - mdelay(10); - KL_LOCK(); - KL_GPIO_OUT(KL_GPIO_ETH_PHY_RESET, - KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA); - (void)KL_GPIO_IN(KL_GPIO_ETH_PHY_RESET); - KL_UNLOCK(); - mdelay(10); -} - -/* Pass the node of the correct controller, please */ -void __pmac -feature_set_usb_power(struct device_node* device, int power) -{ - char* prop; - int number; - u32 reg; - - unsigned long flags; - - if (!keylargo_base || !keylargo) - return; - - prop = (char *)get_property(device, "AAPL,clock-id", NULL); - if (!prop) - return; - if (strncmp(prop, "usb0u048", strlen("usb0u048")) == 0) - number = 0; - else if (strncmp(prop, "usb1u148", strlen("usb1u148")) == 0) - number = 2; - else - return; - - KL_LOCK(); - if (power) { - /* Turn ON */ - - if (number == 0) { - KL_BIC(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1)); - (void)KL_IN(KEYLARGO_FCR0); - KL_UNLOCK(); - mdelay(1); - KL_LOCK(); - KL_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE); - } else { - KL_BIC(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1)); - KL_UNLOCK(); - (void)KL_IN(KEYLARGO_FCR0); - mdelay(1); - KL_LOCK(); - KL_BIS(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE); - } - reg = KL_IN(KEYLARGO_FCR4); - reg &= ~(KL4_SET_PORT_ENABLE(number) | KL4_SET_PORT_RESUME(number) | - KL4_SET_PORT_CONNECT(number) | KL4_SET_PORT_DISCONNECT(number)); - reg &= ~(KL4_SET_PORT_ENABLE(number+1) | KL4_SET_PORT_RESUME(number+1) | - KL4_SET_PORT_CONNECT(number+1) | KL4_SET_PORT_DISCONNECT(number+1)); - KL_OUT(KEYLARGO_FCR4, reg); - (void)KL_IN(KEYLARGO_FCR4); - udelay(10); - } else { - /* Turn OFF */ - - reg = KL_IN(KEYLARGO_FCR4); - reg |= KL4_SET_PORT_ENABLE(number) | KL4_SET_PORT_RESUME(number) | - KL4_SET_PORT_CONNECT(number) | KL4_SET_PORT_DISCONNECT(number); - reg |= KL4_SET_PORT_ENABLE(number+1) | KL4_SET_PORT_RESUME(number+1) | - KL4_SET_PORT_CONNECT(number+1) | KL4_SET_PORT_DISCONNECT(number+1); - KL_OUT(KEYLARGO_FCR4, reg); - (void)KL_IN(KEYLARGO_FCR4); - udelay(1); - if (number == 0) { - KL_BIC(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE); - (void)KL_IN(KEYLARGO_FCR0); - udelay(1); - KL_BIS(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1)); - (void)KL_IN(KEYLARGO_FCR0); - } else { - KL_BIC(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE); - (void)KL_IN(KEYLARGO_FCR0); - udelay(1); - KL_BIS(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1)); - (void)KL_IN(KEYLARGO_FCR0); - } - udelay(1); - } - KL_UNLOCK(); -} - -void __pmac -feature_set_firewire_power(struct device_node* device, int power) -{ - unsigned long flags; - - /* TODO: should probably handle save/restore of PCI config space here - */ - - if (!uninorth_fw || (device && uninorth_fw != device)) - return; - if (!uninorth_base) - return; - - /* XXX We use the keylargo spinlock, but we never - * have uninorth without keylargo, so... - */ - KL_LOCK(); - if (power) - UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW); - else - UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW); - (void)UN_IN(UNI_N_CLOCK_CNTL); - KL_UNLOCK(); - udelay(20); -} - -/* Warning: will kill the PHY.. */ -void __pmac -feature_set_firewire_cable_power(struct device_node* device, int power) -{ - unsigned long flags; - u8 gpioValue = power ? 0 : 4; - - if (!uninorth_fw || (device && uninorth_fw != device)) - return; - if (!keylargo_base || !(board_features & FTR_HAS_FW_POWER)) - return; - KL_LOCK(); - KL_GPIO_OUT(KL_GPIO_FW_CABLE_POWER, gpioValue); - (void)KL_GPIO_IN(KL_GPIO_FW_CABLE_POWER); - KL_UNLOCK(); -} - -void -feature_set_modem_power(struct device_node* device, int power) -{ - unsigned long flags; - - if (!device) { - device = find_devices("ch-a"); - while(device && !device_is_compatible(device, "cobalt")) - device = device->next; - if (!device) - return; - } - if (keylargo && (keylargo_rev & KL_PANGEA_REV)) { - KL_LOCK(); - if (power) { - /* Assert modem reset */ - KL_GPIO_OUT(KL_GPIO_MODEM_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE); - (void)KL_GPIO_IN(KL_GPIO_MODEM_RESET); - udelay(10); - /* Power up modem */ - KL_GPIO_OUT(KL_GPIO_MODEM_POWER, KEYLARGO_GPIO_OUTPUT_ENABLE); - (void)KL_GPIO_IN(KL_GPIO_MODEM_POWER); - udelay(10); - /* Release modem reset */ - KL_GPIO_OUT(KL_GPIO_MODEM_RESET, - KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA); - (void)KL_GPIO_IN(KL_GPIO_MODEM_RESET); - } else { - /* Power down modem */ - KL_GPIO_OUT(KL_GPIO_MODEM_POWER, - KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA); - (void)KL_GPIO_IN(KL_GPIO_MODEM_POWER); - } - KL_UNLOCK(); - } else { - if (power) { - mdelay(300); - feature_set(device, FEATURE_Modem_power); - mdelay(5); - feature_clear(device, FEATURE_Modem_power); - mdelay(10); - feature_set(device, FEATURE_Modem_power); - } else { - feature_clear(device, FEATURE_Modem_power); - mdelay(10); - } - } -} - -#ifdef CONFIG_SMP -void __pmac -feature_core99_kick_cpu(int cpu_nr) -{ - const int reset_lines[] = { KL_GPIO_RESET_CPU0, - KL_GPIO_RESET_CPU1, - KL_GPIO_RESET_CPU2, - KL_GPIO_RESET_CPU3 }; - int reset_io; - unsigned long flags; - - if (!keylargo_base || cpu_nr > 3) - return; - reset_io = reset_lines[cpu_nr]; - - KL_LOCK(); - KL_GPIO_OUT(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE); - (void)KL_GPIO_IN(reset_io); - udelay(1); - KL_GPIO_OUT(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA); - (void)KL_GPIO_IN(reset_io); - KL_UNLOCK(); -} -#endif /* CONFIG_SMP */ - -void __pmac -feature_set_airport_power(struct device_node* device, int power) -{ - unsigned long flags; - - if (!keylargo_base || !airport_dev || airport_dev != device) - return; - if (airport_pwr_state == power) - return; - if (power) { - /* This code is a reproduction of OF enable-cardslot - * and init-wireless methods, slightly hacked until - * I got it working. - */ - KL_LOCK(); - KL_GPIO_OUT(KEYLARGO_GPIO_0+0xf, 5); - (void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xf); - KL_UNLOCK(); - mdelay(10); - KL_LOCK(); - KL_GPIO_OUT(KEYLARGO_GPIO_0+0xf, 4); - (void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xf); - KL_UNLOCK(); - - mdelay(10); - - KL_LOCK(); - KL_BIC(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N); - (void)KL_IN(KEYLARGO_FCR2); - udelay(10); - KL_GPIO_OUT(KEYLARGO_GPIO_EXTINT_0+0xb, 0); - (void)KL_GPIO_IN(KEYLARGO_GPIO_EXTINT_0+0xb); - udelay(10); - KL_GPIO_OUT(KEYLARGO_GPIO_EXTINT_0+0xa, 0x28); - (void)KL_GPIO_IN(KEYLARGO_GPIO_EXTINT_0+0xa); - udelay(10); - KL_GPIO_OUT(KEYLARGO_GPIO_EXTINT_0+0xd, 0x28); - (void)KL_GPIO_IN(KEYLARGO_GPIO_EXTINT_0+0xd); - udelay(10); - KL_GPIO_OUT(KEYLARGO_GPIO_0+0xd, 0x28); - (void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xd); - udelay(10); - KL_GPIO_OUT(KEYLARGO_GPIO_0+0xe, 0x28); - (void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xe); - KL_UNLOCK(); - udelay(10); - KL_OUT(0x1c000, 0); - - mdelay(1); - out_8((volatile u8*)KL_FCR(0x1a3e0), 0x41); - (void)in_8((volatile u8*)KL_FCR(0x1a3e0)); - udelay(10); - KL_LOCK(); - KL_BIS(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N); - (void)KL_IN(KEYLARGO_FCR2); - KL_UNLOCK(); - mdelay(100); - } else { - KL_LOCK(); - KL_BIC(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N); - (void)KL_IN(KEYLARGO_FCR2); - KL_GPIO_OUT(KL_GPIO_AIRPORT_0, 0); - KL_GPIO_OUT(KL_GPIO_AIRPORT_1, 0); - KL_GPIO_OUT(KL_GPIO_AIRPORT_2, 0); - KL_GPIO_OUT(KL_GPIO_AIRPORT_3, 0); - KL_GPIO_OUT(KL_GPIO_AIRPORT_4, 0); - (void)KL_GPIO_IN(KL_GPIO_AIRPORT_4); - KL_UNLOCK(); - } - airport_pwr_state = power; -} - -/* Initialize the Core99 UniNorth host bridge and memory controller - */ -static void __init -uninorth_init(void) -{ - struct device_node* gmac, *fw; - unsigned long actrl; - - /* Set the arbitrer QAck delay according to what Apple does - */ - if (uninorth_rev < 0x10) { - actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK; - actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 : - UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT; - UN_OUT(UNI_N_ARB_CTRL, actrl); - } - - /* Enable GMAC for now for PCI probing. It will be disabled - * later on after PCI probe - */ - gmac = find_devices("ethernet"); - while(gmac) { - if (device_is_compatible(gmac, "gmac")) - break; - gmac = gmac->next; - } - if (gmac) - feature_set_gmac_power(gmac, 1); - - /* Enable FW before PCI probe. Will be disabled later on - */ - fw = find_devices("firewire"); - if (fw && (device_is_compatible(fw, "pci106b,18") || - device_is_compatible(fw, "pci106b,30"))) { - uninorth_fw = fw; - feature_set_firewire_power(fw, 1); - } -} - -/* Initialize the Core99 KeyLargo ASIC. - */ -static void __init -keylargo_init(void) -{ - struct device_node* np; - - KL_BIS(KEYLARGO_FCR2, KL2_MPIC_ENABLE); - - /* Lookup for an airport card, and disable it if found - * to save power (will be re-enabled by driver if used) - */ - np = find_devices("radio"); - if (np && np->parent == keylargo->device) - airport_dev = np; - - if (airport_dev) { - airport_pwr_state = 1; - feature_set_airport_power(airport_dev, 0); - } - -} - -#ifdef CONFIG_PMAC_PBOOK -void __pmac -feature_prepare_for_sleep(void) -{ - /* We assume gatwick is second */ - struct feature_controller* ctrler = &controllers[0]; - - if (controller_count > 1 && - device_is_compatible(ctrler->device, "gatwick")) - ctrler = &controllers[1]; - - if (ctrler->bits == feature_bits_wallstreet || - ctrler->bits == feature_bits_paddington) { - heathrow_prepare_for_sleep(ctrler); - return; - } - if (ctrler->bits == feature_bits_keylargo) { - core99_prepare_for_sleep(ctrler); - return; - } -} - -void __pmac -feature_wake_up(void) -{ - struct feature_controller* ctrler = &controllers[0]; - - if (controller_count > 1 && - device_is_compatible(ctrler->device, "gatwick")) - ctrler = &controllers[1]; - - if (ctrler->bits == feature_bits_wallstreet || - ctrler->bits == feature_bits_paddington) { - heathrow_wakeup(ctrler); - return; - } - if (ctrler->bits == feature_bits_keylargo) { - core99_wake_up(ctrler); - return; - } -} - -static u32 save_fcr[5]; -static u32 save_mbcr; -static u32 save_gpio_levels[2]; -static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT]; -static u8 save_gpio_normal[KEYLARGO_GPIO_CNT]; -static u32 save_unin_clock_ctl; -static struct dbdma_regs save_dbdma[13]; - -static void __pmac -heathrow_prepare_for_sleep(struct feature_controller* ctrler) -{ - save_mbcr = in_le32(FREG(ctrler, 0x34)); - save_fcr[0] = in_le32(FREG(ctrler, 0x38)); - save_fcr[1] = in_le32(FREG(ctrler, 0x3c)); - - out_le32(FREG(ctrler, 0x38), save_fcr[0] & ~HRW_IOBUS_ENABLE); -} - -static void __pmac -heathrow_wakeup(struct feature_controller* ctrler) -{ - out_le32(FREG(ctrler, 0x38), save_fcr[0]); - out_le32(FREG(ctrler, 0x3c), save_fcr[1]); - out_le32(FREG(ctrler, 0x34), save_mbcr); - mdelay(1); - out_le32(FREG(ctrler, 0x38), save_fcr[0] | HRW_IOBUS_ENABLE); - mdelay(1); -} - -static void __pmac -turn_off_keylargo(void) -{ - u32 temp; - - mdelay(1); - KL_BIS(KEYLARGO_FCR0, KL0_USB_REF_SUSPEND); - (void)KL_IN(KEYLARGO_FCR0); - mdelay(100); - - KL_BIC(KEYLARGO_FCR0, KL0_SCCA_ENABLE | KL0_SCCB_ENABLE | - KL0_SCC_CELL_ENABLE | - KL0_IRDA_ENABLE | KL0_IRDA_CLK32_ENABLE | - KL0_IRDA_CLK19_ENABLE); - - (void)KL_IN(KEYLARGO_FCR0); udelay(10); - KL_BIS(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_ENABLE); - (void)KL_IN(KEYLARGO_MBCR); udelay(10); - - KL_BIC(KEYLARGO_FCR1, - KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT | - KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE | - KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT | - KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE | - KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE | - KL1_EIDE0_ENABLE | KL1_EIDE0_RESET_N | - KL1_EIDE1_ENABLE | KL1_EIDE1_RESET_N | - KL1_UIDE_ENABLE); - (void)KL_IN(KEYLARGO_FCR1); udelay(10); - - KL_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N); - udelay(10); - KL_BIC(KEYLARGO_FCR2, KL2_IOBUS_ENABLE); - udelay(10); - temp = KL_IN(KEYLARGO_FCR3); - if (keylargo_rev >= 2) - temp |= (KL3_SHUTDOWN_PLL2X | KL3_SHUTDOWN_PLL_TOTAL); - - temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 | - KL3_SHUTDOWN_PLLKW35 | KL3_SHUTDOWN_PLLKW12; - temp &= ~(KL3_CLK66_ENABLE | KL3_CLK49_ENABLE | KL3_CLK45_ENABLE - | KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE - | KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE); - KL_OUT(KEYLARGO_FCR3, temp); - (void)KL_IN(KEYLARGO_FCR3); udelay(10); -} - -static void __pmac -turn_off_pangea(void) -{ - u32 temp; - - KL_BIC(KEYLARGO_FCR0, KL0_SCCA_ENABLE | KL0_SCCB_ENABLE | - KL0_SCC_CELL_ENABLE | - KL0_USB0_CELL_ENABLE | KL0_USB1_CELL_ENABLE); - - (void)KL_IN(KEYLARGO_FCR0); udelay(10); - KL_BIS(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_ENABLE); - (void)KL_IN(KEYLARGO_MBCR); udelay(10); - - KL_BIC(KEYLARGO_FCR1, - KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT | - KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE | - KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT | - KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE | - KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE | - KL1_UIDE_ENABLE); - (void)KL_IN(KEYLARGO_FCR1); udelay(10); - - KL_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N); - udelay(10); - temp = KL_IN(KEYLARGO_FCR3); - temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 | - KL3_SHUTDOWN_PLLKW35; - temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE - | KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE - | KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE); - KL_OUT(KEYLARGO_FCR3, temp); - (void)KL_IN(KEYLARGO_FCR3); udelay(10); -} - -static void __pmac -core99_prepare_for_sleep(struct feature_controller* ctrler) -{ - int i; - volatile u8* base8; - - /* - * Save various bits of KeyLargo - */ - - /* We power off the wireless slot in case it was not done - * by the driver. We don't power it on automatically however - */ - feature_set_airport_power(airport_dev, 0); - - /* We power off the FW cable. Should be done by the driver... */ - feature_set_firewire_power(NULL, 0); - feature_set_firewire_cable_power(NULL, 0); - - /* We make sure int. modem is off (in case driver lost it) */ - feature_set_modem_power(NULL, 0); - - /* Save the state of the various GPIOs */ - save_gpio_levels[0] = KL_IN(KEYLARGO_GPIO_LEVELS0); - save_gpio_levels[1] = KL_IN(KEYLARGO_GPIO_LEVELS1); - base8 = ((volatile u8 *)keylargo_base) + KEYLARGO_GPIO_EXTINT_0; - for (i=0; i>2)); - save_dbdma[i].cmdptr_hi = in_le32(&chan->cmdptr_hi); - save_dbdma[i].cmdptr = in_le32(&chan->cmdptr); - save_dbdma[i].intr_sel = in_le32(&chan->intr_sel); - save_dbdma[i].br_sel = in_le32(&chan->br_sel); - save_dbdma[i].wait_sel = in_le32(&chan->wait_sel); - } - - /* - * Turn off as much as we can - */ - if (keylargo_rev & KL_PANGEA_REV) - turn_off_pangea(); - else - turn_off_keylargo(); - - /* - * Put the host bridge to sleep - */ - - save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL); - UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl & - ~(UNI_N_CLOCK_CNTL_GMAC|UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/)); - udelay(100); - UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING); - UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP); - - /* - * FIXME: A bit of black magic with OpenPIC (don't ask me why) - */ - if (board_features & FTR_NEED_OPENPIC_TWEAK) { - KL_BIS(0x506e0, 0x00400000); - KL_BIS(0x506e0, 0x80000000); - } -} - -static void __pmac -core99_wake_up(struct feature_controller* ctrler) -{ - int i; - volatile u8* base8; - - /* - * Wakeup the host bridge - */ - UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL); - udelay(10); - UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING); - udelay(10); - - /* - * Restore KeyLargo - */ - - KL_OUT(KEYLARGO_MBCR, save_mbcr); - (void)KL_IN(KEYLARGO_MBCR); udelay(10); - KL_OUT(KEYLARGO_FCR0, save_fcr[0]); - (void)KL_IN(KEYLARGO_FCR0); udelay(10); - KL_OUT(KEYLARGO_FCR1, save_fcr[1]); - (void)KL_IN(KEYLARGO_FCR1); udelay(10); - KL_OUT(KEYLARGO_FCR2, save_fcr[2]); - (void)KL_IN(KEYLARGO_FCR2); udelay(10); - KL_OUT(KEYLARGO_FCR3, save_fcr[3]); - (void)KL_IN(KEYLARGO_FCR3); udelay(10); - KL_OUT(KEYLARGO_FCR4, save_fcr[4]); - (void)KL_IN(KEYLARGO_FCR4); udelay(10); - - for (i=0; i<13; i++) { - volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*) - (keylargo_base + ((0x8000+i*0x100)>>2)); - out_le32(&chan->control, (ACTIVE|DEAD|WAKE|FLUSH|PAUSE|RUN)<<16); - while (in_le32(&chan->status) & ACTIVE) - mb(); - out_le32(&chan->cmdptr_hi, save_dbdma[i].cmdptr_hi); - out_le32(&chan->cmdptr, save_dbdma[i].cmdptr); - out_le32(&chan->intr_sel, save_dbdma[i].intr_sel); - out_le32(&chan->br_sel, save_dbdma[i].br_sel); - out_le32(&chan->wait_sel, save_dbdma[i].wait_sel); - } - - KL_OUT(KEYLARGO_GPIO_LEVELS0, save_gpio_levels[0]); - KL_OUT(KEYLARGO_GPIO_LEVELS1, save_gpio_levels[1]); - base8 = ((volatile u8 *)keylargo_base) + KEYLARGO_GPIO_EXTINT_0; - for (i=0; i #include #include - -#include "pci.h" +#include /* Preprocessor Defines */ diff -Nru a/arch/ppc/kernel/gemini_pci.c b/arch/ppc/kernel/gemini_pci.c --- a/arch/ppc/kernel/gemini_pci.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,124 +0,0 @@ -/* - * BK Id: SCCS/s.gemini_pci.c 1.6 10/11/01 08:51:46 trini - */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "pci.h" - -#define pci_config_addr(bus,dev,offset) \ - (0x80000000 | (bus<<16) | (dev<<8) | offset) - - -int -gemini_pcibios_read_config_byte(struct pci_dev *dev, int offset, u8 *val) -{ - unsigned long reg; - reg = grackle_read(pci_config_addr(dev->bus->number, dev->devfn, - (offset & ~(0x3)))); - *val = ((reg >> ((offset & 0x3) << 3)) & 0xff); - return PCIBIOS_SUCCESSFUL; -} - -int -gemini_pcibios_read_config_word(struct pci_dev *dev, int offset, u16 *val) -{ - unsigned long reg; - reg = grackle_read(pci_config_addr(dev->bus->number, dev->devfn, - (offset & ~(0x3)))); - *val = ((reg >> ((offset & 0x3) << 3)) & 0xffff); - return PCIBIOS_SUCCESSFUL; -} - -int -gemini_pcibios_read_config_dword(struct pci_dev *dev, int offset, u32 *val) -{ - *val = grackle_read(pci_config_addr(dev->bus->number, dev->devfn, - (offset & ~(0x3)))); - return PCIBIOS_SUCCESSFUL; -} - -int -gemini_pcibios_write_config_byte(struct pci_dev *dev, int offset, u8 val) -{ - unsigned long reg; - int shifts = offset & 0x3; - unsigned int addr = pci_config_addr(dev->bus->number, dev->devfn, - (offset & ~(0x3))); - - reg = grackle_read(addr); - reg = (reg & ~(0xff << (shifts << 3))) | (val << (shifts << 3)); - grackle_write(addr, reg ); - return PCIBIOS_SUCCESSFUL; -} - -int -gemini_pcibios_write_config_word(struct pci_dev *dev, int offset, u16 val) -{ - unsigned long reg; - int shifts = offset & 0x3; - unsigned int addr = pci_config_addr(dev->bus->number, dev->devfn, - (offset & ~(0x3))); - - reg = grackle_read(addr); - reg = (reg & ~(0xffff << (shifts << 3))) | (val << (shifts << 3)); - grackle_write(addr, reg ); - return PCIBIOS_SUCCESSFUL; -} - -int -gemini_pcibios_write_config_dword(struct pci_dev *dev, int offset, u32 val) -{ - grackle_write(pci_config_addr(dev->bus->number, dev->devfn, - (offset & ~(0x3))), val); - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops gemini_pci_ops = -{ - gemini_pcibios_read_config_byte, - gemini_pcibios_read_config_word, - gemini_pcibios_read_config_dword, - gemini_pcibios_write_config_byte, - gemini_pcibios_write_config_word, - gemini_pcibios_write_config_dword -}; - -void __init gemini_pcibios_fixup(void) -{ - int i; - struct pci_dev *dev; - - pci_for_each_dev(dev) { - for(i = 0; i < 6; i++) { - if (dev->resource[i].flags & IORESOURCE_IO) { - dev->resource[i].start |= (0xfe << 24); - dev->resource[i].end |= (0xfe << 24); - } - } - } -} - - -/* The "bootloader" for Synergy boards does none of this for us, so we need to - lay it all out ourselves... --Dan */ -void __init gemini_find_bridges(void) -{ - struct pci_controller* hose; - - ppc_md.pcibios_fixup = gemini_pcibios_fixup; - - hose = pcibios_alloc_controller(); - if (!hose) - return; - hose->ops = &gemini_pci_ops; -} diff -Nru a/arch/ppc/kernel/gemini_prom.S b/arch/ppc/kernel/gemini_prom.S --- a/arch/ppc/kernel/gemini_prom.S Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,99 +0,0 @@ -/* - * BK Id: SCCS/s.gemini_prom.S 1.5 05/17/01 18:14:21 cort - */ -/* - * arch/ppc/kernel/gemini_prom.S - * - * Not really prom support code (yet), but sort of anti-prom code. The current - * bootloader does a number of things it shouldn't and doesn't do things that it - * should. The stuff in here is mainly a hodge-podge collection of setup code - * to get the board up and running. - * ---Dan - */ - -#include "ppc_asm.tmpl" -#include "ppc_defs.h" -#include -#include -#include -#include - -#define HID0_ABE (1<<3) - -/* - * On 750's the MMU is on when Linux is booted, so we need to clear out the - * bootloader's BAT settings, make sure we're in supervisor state (gotcha!), - * and turn off the MMU. - * - */ - -_GLOBAL(gemini_prom_init) -#ifdef CONFIG_SMP - /* Since the MMU's on, get stuff in rom space that we'll need */ - lis r4,GEMINI_CPUSTAT@h - ori r4,r4,GEMINI_CPUSTAT@l - lbz r5,0(r4) - andi. r5,r5,3 - mr r24,r5 /* cpu # used later on */ -#endif - mfmsr r4 - li r3,MSR_PR /* ensure supervisor! */ - ori r3,r3,MSR_IR|MSR_DR - andc r4,r4,r3 - mtmsr r4 - isync -#if 0 - /* zero out the bats now that the MMU is off */ -prom_no_mmu: - li r3,0 - mtspr IBAT0U,r3 - mtspr IBAT0L,r3 - mtspr IBAT1U,r3 - mtspr IBAT1L,r3 - mtspr IBAT2U,r3 - mtspr IBAT2L,r3 - mtspr IBAT3U,r3 - mtspr IBAT3L,r3 - - mtspr DBAT0U,r3 - mtspr DBAT0L,r3 - mtspr DBAT1U,r3 - mtspr DBAT1L,r3 - mtspr DBAT2U,r3 - mtspr DBAT2L,r3 - mtspr DBAT3U,r3 - mtspr DBAT3L,r3 -#endif - - /* the bootloader (as far as I'm currently aware) doesn't mess with page - tables, but since we're already here, might as well zap these, too */ - li r4,0 - mtspr SDR1,r4 - - li r4,16 - mtctr r4 - li r3,0 - li r4,0 -3: mtsrin r3,r4 - addi r3,r3,1 - bdnz 3b - -#ifdef CONFIG_SMP - /* The 750 book (and Mot/IBM support) says that this will "assist" snooping - when in SMP. Not sure yet whether this should stay or leave... */ - mfspr r4,HID0 - ori r4,r4,HID0_ABE - mtspr HID0,r4 - sync -#endif /* CONFIG_SMP */ - blr - -/* apparently, SMon doesn't pay attention to HID0[SRST]. Disable the MMU and - branch to 0xfff00100 */ -_GLOBAL(_gemini_reboot) - lis r5,GEMINI_BOOT_INIT@h - ori r5,r5,GEMINI_BOOT_INIT@l - li r6,MSR_IP - mtspr SRR0,r5 - mtspr SRR1,r6 - rfi diff -Nru a/arch/ppc/kernel/gemini_setup.c b/arch/ppc/kernel/gemini_setup.c --- a/arch/ppc/kernel/gemini_setup.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,584 +0,0 @@ -/* - * BK Id: SCCS/s.gemini_setup.c 1.14 10/18/01 11:16:28 trini - */ -/* - * linux/arch/ppc/kernel/setup.c - * - * Copyright (C) 1995 Linus Torvalds - * Adapted from 'alpha' version by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) - * Synergy Microsystems board support by Dan Cox (dan@synergymicro.com) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "local_irq.h" -#include "open_pic.h" - -void gemini_find_bridges(void); -static int gemini_get_clock_speed(void); -extern void gemini_pcibios_fixup(void); - -static char *gemini_board_families[] = { - "VGM", "VSS", "KGM", "VGR", "VCM", "VCS", "KCM", "VCR" -}; -static int gemini_board_count = sizeof(gemini_board_families) / - sizeof(gemini_board_families[0]); - -static unsigned int cpu_7xx[16] = { - 0, 15, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 -}; -static unsigned int cpu_6xx[16] = { - 0, 0, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 0, 12, 7, 0 -}; - -/* - * prom_init is the Gemini version of prom.c:prom_init. We only need - * the BSS clearing code, so I copied that out of prom.c. This is a - * lot simpler than hacking prom.c so it will build with Gemini. -VAL - */ - -#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) - -unsigned long -prom_init(void) -{ - unsigned long offset = reloc_offset(); - unsigned long phys; - extern char __bss_start, _end; - - /* First zero the BSS -- use memset, some arches don't have - * caches on yet */ - memset_io(PTRRELOC(&__bss_start),0 , &_end - &__bss_start); - - /* Default */ - phys = offset + KERNELBASE; - - gemini_prom_init(); - - return phys; -} - -int -gemini_show_cpuinfo(struct seq_file *m) -{ - unsigned char reg, rev; - char *family; - unsigned int type; - - reg = readb(GEMINI_FEAT); - family = gemini_board_families[((reg>>4) & 0xf)]; - if (((reg>>4) & 0xf) > gemini_board_count) - printk(KERN_ERR "cpuinfo(): unable to determine board family\n"); - - reg = readb(GEMINI_BREV); - type = (reg>>4) & 0xf; - rev = reg & 0xf; - - reg = readb(GEMINI_BECO); - - seq_printf(m, "machine\t\t: Gemini %s%d, rev %c, eco %d\n", - family, type, (rev + 'A'), (reg & 0xf)); - - seq_printf(m, "board\t\t: Gemini %s", family); - if (type > 9) - seq_printf(m, "%c", (type - 10) + 'A'); - else - seq_printf(m, "%d", type); - - seq_printf(m, ", rev %c, eco %d\n", (rev + 'A'), (reg & 0xf)); - - seq_printf(m, "clock\t\t: %dMhz\n", gemini_get_clock_speed()); - - return 0; -} - -static u_char gemini_openpic_initsenses[] = { - 1, - 1, - 1, - 1, - 0, - 0, - 1, /* remainder are level-triggered */ -}; - -#define GEMINI_MPIC_ADDR (0xfcfc0000) -#define GEMINI_MPIC_PCI_CFG (0x80005800) - -void __init gemini_openpic_init(void) -{ - - OpenPIC_Addr = (volatile struct OpenPIC *) - grackle_read(GEMINI_MPIC_PCI_CFG + 0x10); - OpenPIC_InitSenses = gemini_openpic_initsenses; - OpenPIC_NumInitSenses = sizeof( gemini_openpic_initsenses ); - - ioremap( GEMINI_MPIC_ADDR, OPENPIC_SIZE); -} - - -extern unsigned long loops_per_jiffy; -extern int root_mountflags; -extern char cmd_line[]; - -void -gemini_heartbeat(void) -{ - static unsigned long led = GEMINI_LEDBASE+(4*8); - static char direction = 8; - - /* We only want to do this on 1 CPU */ - if (smp_processor_id()) - return; - *(char *)led = 0; - if ( (led + direction) > (GEMINI_LEDBASE+(7*8)) || - (led + direction) < (GEMINI_LEDBASE+(4*8)) ) - direction *= -1; - led += direction; - *(char *)led = 0xff; - ppc_md.heartbeat_count = ppc_md.heartbeat_reset; -} - -void __init gemini_setup_arch(void) -{ - extern char cmd_line[]; - - - loops_per_jiffy = 50000000/HZ; - -#ifdef CONFIG_BLK_DEV_INITRD - /* bootable off CDROM */ - if (initrd_start) - ROOT_DEV = MKDEV(SCSI_CDROM_MAJOR, 0); - else -#endif - ROOT_DEV = to_kdev_t(0x0801); - - /* nothing but serial consoles... */ - sprintf(cmd_line, "%s console=ttyS0", cmd_line); - - printk("Boot arguments: %s\n", cmd_line); - - ppc_md.heartbeat = gemini_heartbeat; - ppc_md.heartbeat_reset = HZ/8; - ppc_md.heartbeat_count = 1; - - /* Lookup PCI hosts */ - gemini_find_bridges(); - /* take special pains to map the MPIC, since it isn't mapped yet */ - gemini_openpic_init(); - /* start the L2 */ - gemini_init_l2(); -} - - -int -gemini_get_clock_speed(void) -{ - unsigned long hid1, pvr; - int clock; - - pvr = mfspr(PVR); - hid1 = (mfspr(HID1) >> 28) & 0xf; - if (PVR_VER(pvr) == 8 || - PVR_VER(pvr) == 12) - hid1 = cpu_7xx[hid1]; - else - hid1 = cpu_6xx[hid1]; - - switch((readb(GEMINI_BSTAT) & 0xc) >> 2) { - - case 0: - default: - clock = (hid1*100)/3; - break; - - case 1: - clock = (hid1*125)/3; - break; - - case 2: - clock = (hid1*50); - break; - } - - return clock; -} - -void __init gemini_init_l2(void) -{ - unsigned char reg, brev, fam, creg; - unsigned long cache; - unsigned long pvr; - - reg = readb(GEMINI_L2CFG); - brev = readb(GEMINI_BREV); - fam = readb(GEMINI_FEAT); - pvr = mfspr(PVR); - - switch(PVR_VER(pvr)) { - - case 8: - if (reg & 0xc0) - cache = (((reg >> 6) & 0x3) << 28); - else - cache = 0x3 << 28; - -#ifdef CONFIG_SMP - /* Pre-3.0 processor revs had snooping errata. Leave - their L2's disabled with SMP. -- Dan */ - if (PVR_CFG(pvr) < 3) { - printk("Pre-3.0 750; L2 left disabled!\n"); - return; - } -#endif /* CONFIG_SMP */ - - /* Special case: VGM5-B's came before L2 ratios were set on - the board. Processor speed shouldn't be too high, so - set L2 ratio to 1:1.5. */ - if ((brev == 0x51) && ((fam & 0xa0) >> 4) == 0) - reg |= 1; - - /* determine best cache ratio based upon what the board - tells us (which sometimes _may_ not be true) and - the processor speed. */ - else { - if (gemini_get_clock_speed() > 250) - reg = 2; - } - break; - case 12: - { - static unsigned long l2_size_val = 0; - - if (!l2_size_val) - l2_size_val = _get_L2CR(); - cache = l2_size_val; - break; - } - case 4: - case 9: - creg = readb(GEMINI_CPUSTAT); - if (((creg & 0xc) >> 2) != 1) - printk("Dual-604 boards don't support the use of L2\n"); - else - writeb(1, GEMINI_L2CFG); - return; - default: - printk("Unknown processor; L2 left disabled\n"); - return; - } - - cache |= ((1<>2)&0x3) { - case 0: - default: - freq = 66667; - break; - case 1: - freq = 83000; - break; - case 2: - freq = 100000; - break; - } - - freq *= 1000; - divisor = 4; - tb_ticks_per_jiffy = freq / HZ / divisor; - tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); -} - -unsigned long __init gemini_find_end_of_memory(void) -{ - unsigned long total; - unsigned char reg; - - reg = readb(GEMINI_MEMCFG); - total = ((1<<((reg & 0x7) - 1)) * - (8<<((reg >> 3) & 0x7))); - total *= (1024*1024); - return total; -} - -static void __init -gemini_map_io(void) -{ - io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); - io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); -} - -#ifdef CONFIG_SMP -static int -smp_gemini_probe(void) -{ - int i, nr; - - nr = (readb(GEMINI_CPUSTAT) & GEMINI_CPU_COUNT_MASK) >> 2; - if (nr == 0) - nr = 4; - - if (nr > 1) { - openpic_request_IPIs(); - for (i = 1; i < nr; ++i) - smp_hw_index[i] = i; - } - - return nr; -} - -static void -smp_gemini_kick_cpu(int nr) -{ - openpic_reset_processor_phys(1 << nr); - openpic_reset_processor_phys(0); -} - -static void -smp_gemini_setup_cpu(int cpu_nr) -{ - if (OpenPIC_Addr) - do_openpic_setup_cpu(); - if (cpu_nr > 0) - gemini_init_l2(); -} - -static struct smp_ops_t gemini_smp_ops = { - smp_openpic_message_pass, - smp_gemini_probe, - smp_gemini_kick_cpu, - smp_gemini_setup_cpu, -}; -#endif /* CONFIG_SMP */ - -void __init platform_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - int i; - - for(i = 0; i < GEMINI_LEDS; i++) - gemini_led_off(i); - - ISA_DMA_THRESHOLD = 0; - DMA_MODE_READ = 0; - DMA_MODE_WRITE = 0; - -#ifdef CONFIG_BLK_DEV_INITRD - if ( r4 ) - { - initrd_start = r4 + KERNELBASE; - initrd_end = r5 + KERNELBASE; - } -#endif - - ppc_md.setup_arch = gemini_setup_arch; - ppc_md.show_cpuinfo = gemini_show_cpuinfo; - ppc_md.irq_cannonicalize = NULL; - ppc_md.init_IRQ = gemini_init_IRQ; - ppc_md.get_irq = openpic_get_irq; - ppc_md.init = NULL; - - ppc_md.restart = gemini_restart; - ppc_md.power_off = gemini_power_off; - ppc_md.halt = gemini_halt; - - ppc_md.time_init = gemini_time_init; - ppc_md.set_rtc_time = gemini_set_rtc_time; - ppc_md.get_rtc_time = gemini_get_rtc_time; - ppc_md.calibrate_decr = gemini_calibrate_decr; - - ppc_md.find_end_of_memory = gemini_find_end_of_memory; - ppc_md.setup_io_mappings = gemini_map_io; - - /* no keyboard/mouse/video stuff yet.. */ - ppc_md.kbd_setkeycode = NULL; - ppc_md.kbd_getkeycode = NULL; - ppc_md.kbd_translate = NULL; - ppc_md.kbd_unexpected_up = NULL; - ppc_md.kbd_leds = NULL; - ppc_md.kbd_init_hw = NULL; - ppc_md.ppc_kbd_sysrq_xlate = NULL; - ppc_md.pcibios_fixup_bus = gemini_pcibios_fixup; - -#ifdef CONFIG_SMP - ppc_md.smp_ops = &gemini_smp_ops; -#endif /* CONFIG_SMP */ -} diff -Nru a/arch/ppc/kernel/gt64260_common.c b/arch/ppc/kernel/gt64260_common.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/gt64260_common.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1666 @@ +/* + * arch/ppc/kernel/gt64260_common.c + * + * Common routines for the Marvell/Galileo GT64260 (Discovery) host bridge, + * interrupt controller, memory controller, serial controller, enet controller, + * etc. + * + * Author: Mark A. Greer + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * The GT64260 port is the result of hard work from many people from + * many companies. In particular, employees of Marvell/Galileo, Mission + * Critical Linux, Xyterra, and MontaVista Software were heavily involved. + */ + +/* + * At last count, the 64260-B-0 has 65 errata and 24 restrictions. The odds of + * you getting it to work well, under stress, for a long period of time are + * low. If nothing else, you will likely run into an interrupt controller + * bug. + * + * The newer 64260A-B-0 is much improved but has its own problems. + * Dave Wilhardt has discovered that you must set + * up your PCI snoop regions to be prefetchable with 4-word bursts AND set the + * "Memory Write and Invalidate bit" (bit 4) in the cmd reg of each PCI device + * before coherency works between PCI and other devices. Many thanks to Dave. + * + * So far this code has been tested on Marvell/Galileo EV-64260-BP and + * an EV-64260A-BP uni-processor boards with 750 and 7400 processors. + * It has not yet been tested with a 7410 or 7450, or on an smp system. + * + * Note: I have not had any luck moving the base register address of the bridge + * with the gt64260_set_base() routine. I move it in the bootloader + * before starting the kernel. I haven't really looked into it so it + * may be an easy fix. -- MAG + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +u32 gt64260_base; /* Virtual base address of 64260's regs */ +u32 gt64260_revision; /* Revision of the chip */ +u8 gt64260_pci_exclude_bridge = TRUE; + + +/* + ***************************************************************************** + * + * Bridge Initialization Routines + * + ***************************************************************************** + */ +static void gt64260_check_errata(struct pci_controller *hose_a, + struct pci_controller *hose_b); + +/* + * Typical '_find_bridges()' routine for boards with a GT64260 bridge. + */ +int __init +gt64260_find_bridges(u32 phys_base_addr, gt64260_bridge_info_t *info, + int ((*map_irq)(struct pci_dev *, unsigned char, unsigned char))) +{ + struct pci_controller *hose_a, *hose_b; + u32 io_base_a, io_base_b; + int rc; + + + gt64260_base = (u32)ioremap(phys_base_addr,GT64260_INTERNAL_SPACE_SIZE); + + hose_a = pcibios_alloc_controller(); + if (!hose_a) + return -1; + + hose_b = pcibios_alloc_controller(); + if (!hose_b) + return -1; + + info->hose_a = hose_a; + info->hose_b = hose_b; + + /* Ends up mapping PCI Config addr/data pairs twice */ + setup_indirect_pci(hose_a, + phys_base_addr + GT64260_PCI_0_CONFIG_ADDR, + phys_base_addr + GT64260_PCI_0_CONFIG_DATA); + + setup_indirect_pci(hose_b, + phys_base_addr + GT64260_PCI_1_CONFIG_ADDR, + phys_base_addr + GT64260_PCI_1_CONFIG_DATA); + + if ((rc = gt64260_bridge_init(info)) != 0) { + iounmap((void *)hose_a->cfg_addr); + iounmap((void *)hose_a->cfg_data); + iounmap((void *)hose_b->cfg_addr); + iounmap((void *)hose_b->cfg_data); + iounmap((void *)gt64260_base); + return rc; + } + + /* ioremap PCI I/O regions */ + io_base_b = (u32)ioremap(info->pci_1_io_start_proc,info->pci_1_io_size); + io_base_a = (u32)ioremap(info->pci_0_io_start_proc,info->pci_0_io_size); + isa_io_base = io_base_a; + + hose_a->first_busno = 0; + hose_a->last_busno = 0xff; + + pci_init_resource(&hose_a->io_resource, + 0, /* really: io_base_a - isa_io_base */ + info->pci_0_io_size - 1, + IORESOURCE_IO, + "host bridge PCI bus 0"); + hose_a->io_space.start = info->pci_0_io_start_pci; + hose_a->io_space.end = info->pci_0_io_start_pci + + info->pci_0_io_size - 1; + hose_a->io_base_virt = (void *)isa_io_base; + + pci_init_resource(&hose_a->mem_resources[0], + info->pci_0_mem_start_proc, + info->pci_0_mem_start_proc + info->pci_0_mem_size - 1, + IORESOURCE_MEM, + "host bridge PCI bus 0"); + hose_a->mem_space.start = info->pci_0_mem_start_pci_lo; + hose_a->mem_space.end = info->pci_0_mem_start_pci_lo + + info->pci_0_mem_size - 1; + hose_a->pci_mem_offset = (info->pci_0_mem_start_proc - + info->pci_0_mem_start_pci_lo); + + hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); + + + hose_b->first_busno = hose_a->last_busno + 1; + hose_b->bus_offset = hose_b->first_busno; + hose_b->last_busno = 0xff; + + pci_init_resource(&hose_b->io_resource, + io_base_b - isa_io_base, + io_base_b - isa_io_base + info->pci_1_io_size - 1, + IORESOURCE_IO, + "host bridge PCI bus 1"); + hose_b->io_space.start = info->pci_1_io_start_pci; + hose_b->io_space.end = info->pci_1_io_start_pci + + info->pci_1_io_size - 1; + hose_b->io_base_virt = (void *)isa_io_base; + + pci_init_resource(&hose_b->mem_resources[0], + info->pci_1_mem_start_proc, + info->pci_1_mem_start_proc + info->pci_1_mem_size - 1, + IORESOURCE_MEM, + "host bridge PCI bus 1"); + hose_b->mem_space.start = info->pci_1_mem_start_pci_lo; + hose_b->mem_space.end = info->pci_1_mem_start_pci_lo + + info->pci_1_mem_size - 1; + hose_b->pci_mem_offset = (info->pci_1_mem_start_proc - + info->pci_1_mem_start_pci_lo); + + hose_b->last_busno = pciauto_bus_scan(hose_b, hose_b->first_busno); + + + ppc_md.pci_exclude_device = gt64260_pci_exclude_device; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = map_irq; + + return 0; +} /* gt64260_find_bridges() */ + +/* + * gt64260_bridge_init() + * + * Perform bridge initialization for a "typical" setup for a PPC system. + */ +int __init +gt64260_bridge_init(gt64260_bridge_info_t *info) +{ + int window; + u16 u16_val; + u32 u32_val; + int rc = 0; + u8 save_exclude; + + /* + * Count on firmware to set/clear other bits in this register. + * + * Set CPU CONFIG Reg bit: + * bit 13 - Pipeline + * bit 16 - RdOOO + * + * Clear CPU Config Reg bit: + * bit 12 - endianess + * bit 27 - RemapWrDis + */ + u32_val = gt_read(GT64260_CPU_CONFIG); + u32_val |= ((1<<13) | (1<<16)); + u32_val &= ~((1<<8) | (1<<12) | (1<<27)); + gt_write(GT64260_CPU_CONFIG, u32_val); + + /* PCI 0/1 Timeout and Retry limits */ + u32_val = gt_read(GT64260_PCI_0_TO_RETRY); + u32_val |= 0x0000ffff; + gt_write(GT64260_PCI_0_TO_RETRY, u32_val); + + u32_val = gt_read(GT64260_PCI_1_TO_RETRY); + u32_val |= 0x0000ffff; + gt_write(GT64260_PCI_1_TO_RETRY, u32_val); + + save_exclude = gt64260_pci_exclude_bridge; + gt64260_pci_exclude_bridge = FALSE; + + /* Set class code to indicate host bridge */ + early_read_config_dword(info->hose_a, + info->hose_a->first_busno, + PCI_DEVFN(0,0), + PCI_CLASS_REVISION, + &u32_val); + u32_val &= 0x000000ff; + gt64260_revision = u32_val; /* a 64260 or 64260A? */ + u32_val |= (PCI_CLASS_BRIDGE_HOST << 16); + early_write_config_dword(info->hose_a, + info->hose_a->first_busno, + PCI_DEVFN(0,0), + PCI_CLASS_REVISION, + u32_val); + + early_read_config_dword(info->hose_b, + info->hose_b->first_busno, + PCI_DEVFN(0,0), + PCI_CLASS_REVISION, + &u32_val); + u32_val &= 0x000000ff; + u32_val |= (PCI_CLASS_BRIDGE_HOST << 16); + early_write_config_dword(info->hose_b, + info->hose_b->first_busno, + PCI_DEVFN(0,0), + PCI_CLASS_REVISION, + u32_val); + + /* Enable 64260 to be PCI master & respond to PCI MEM cycles */ + early_read_config_word(info->hose_a, + info->hose_a->first_busno, + PCI_DEVFN(0,0), + PCI_COMMAND, + &u16_val); + u16_val |= (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + early_write_config_word(info->hose_a, + info->hose_a->first_busno, + PCI_DEVFN(0,0), + PCI_COMMAND, + u16_val); + + early_read_config_word(info->hose_b, + info->hose_b->first_busno, + PCI_DEVFN(0,0), + PCI_COMMAND, + &u16_val); + u16_val |= (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + early_write_config_word(info->hose_b, + info->hose_b->first_busno, + PCI_DEVFN(0,0), + PCI_COMMAND, + u16_val); + + gt64260_pci_exclude_bridge = save_exclude; + + /* + * Disable all CPU windows on the bridge except for SCS 0 which + * is covering all of system memory.: + */ + gt64260_cpu_disable_all_windows(); + + /* + * Set CPU snoop window to indicate all of system memory + * is covered with wirte-back cache. + */ + gt64260_cpu_snoop_set_window(0, + 0x00000000, + info->mem_size, + GT64260_CPU_SNOOP_WB); + + /* + * Set up CPU->PCI mappings (so CPU can get at PCI dev regs/mem). + * Will only use one of the four CPU->PCI MEM windows on each bus. + */ + gt64260_cpu_set_pci_io_window(0, + info->pci_0_io_start_proc, + info->pci_0_io_start_pci, + info->pci_0_io_size, + info->pci_0_io_swap); + + gt64260_cpu_set_pci_mem_window(0, + 0, + info->pci_0_mem_start_proc, + info->pci_0_mem_start_pci_hi, + info->pci_0_mem_start_pci_lo, + info->pci_0_mem_size, + info->pci_0_mem_swap); + + gt64260_cpu_set_pci_io_window(1, + info->pci_1_io_start_proc, + info->pci_1_io_start_pci, + info->pci_1_io_size, + info->pci_1_io_swap); + + gt64260_cpu_set_pci_mem_window(1, + 0, + info->pci_1_mem_start_proc, + info->pci_1_mem_start_pci_hi, + info->pci_1_mem_start_pci_lo, + info->pci_1_mem_size, + info->pci_1_mem_swap); + + /* + * Set up PCI MEM->system memory mapping (bridge slave PCI window). + * + * Set BAR enables to allow only the SCS0 slave window to respond + * to PCI read/write cycles. + */ + gt64260_pci_bar_enable(0, GT64260_PCI_SLAVE_BAR_REG_ENABLES_SCS_0); + gt64260_pci_bar_enable(1, GT64260_PCI_SLAVE_BAR_REG_ENABLES_SCS_0); + + /* + * For virt_to_bus & bus_to_virt to work correctly, this mapping + * must be the same on both PCI buses. + */ + gt64260_pci_slave_scs_set_window(info->hose_a, + 0, + 0x00000000, + 0x00000000, + info->mem_size); + + gt64260_pci_slave_scs_set_window(info->hose_b, + 0, + 0x00000000, + 0x00000000, + info->mem_size); + pci_dram_offset = 0; /* System mem at same addr on PCI & cpu bus */ + + /* Disable all the access control windows */ + for (window=0; windowmem_size, + (GT64260_PCI_ACC_CNTL_PREFETCHEN | + GT64260_PCI_ACC_CNTL_MBURST_4_WORDS | + GT64260_PCI_ACC_CNTL_SWAP_BYTE)); + + gt64260_pci_acc_cntl_set_window(1, + 0, + 0x00000000, + 0x00000000, + info->mem_size, + (GT64260_PCI_ACC_CNTL_PREFETCHEN | + GT64260_PCI_ACC_CNTL_MBURST_4_WORDS | + GT64260_PCI_ACC_CNTL_SWAP_BYTE)); + + gt64260_pci_snoop_set_window(0, + 0, + 0x00000000, + 0x00000000, + info->mem_size, + GT64260_PCI_SNOOP_WB); + + gt64260_pci_snoop_set_window(1, + 0, + 0x00000000, + 0x00000000, + info->mem_size, + GT64260_PCI_SNOOP_WB); + + gt64260_check_errata(info->hose_a, info->hose_b); + + + /* Set latency timer (to 64) & cacheline size; clear BIST */ + gt64260_pci_exclude_bridge = FALSE; + u32_val = ((0x04 << 8) | (L1_CACHE_LINE_SIZE / 4)); + + early_write_config_dword(info->hose_a, + info->hose_a->first_busno, + PCI_DEVFN(0,0), + PCI_CACHE_LINE_SIZE, + u32_val); + early_write_config_dword(info->hose_b, + info->hose_b->first_busno, + PCI_DEVFN(0,0), + PCI_CACHE_LINE_SIZE, + u32_val); + gt64260_pci_exclude_bridge = TRUE; + + return rc; +} /* gt64260_bridge_init() */ + +/* + * gt64260_check_errata() + * + * Apply applicable errata and restrictions from 0.5 of the + * Errata and Restrictions document from Marvell/Galileo. + */ +static void __init +gt64260_check_errata(struct pci_controller *hose_a, + struct pci_controller *hose_b) +{ + u32 val; + u8 save_exclude; + + /* Currently 2 versions, 64260 and 64260A */ + if (gt64260_revision == GT64260) { + save_exclude = gt64260_pci_exclude_bridge; + gt64260_pci_exclude_bridge = FALSE; + + /* FEr#5, FEr#12 */ + early_read_config_dword(hose_a, + hose_a->first_busno, + PCI_DEVFN(0,0), + PCI_COMMAND, + &val); + val &= ~(PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY); + early_write_config_dword(hose_a, + hose_a->first_busno, + PCI_DEVFN(0,0), + PCI_COMMAND, + val); + + early_read_config_dword(hose_b, + hose_b->first_busno, + PCI_DEVFN(0,0), + PCI_COMMAND, + &val); + val &= ~(PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY); + early_write_config_dword(hose_b, + hose_b->first_busno, + PCI_DEVFN(0,0), + PCI_COMMAND, + val); + gt64260_pci_exclude_bridge = save_exclude; + + /* FEr#12, FEr#13 */ + gt_clr_bits(GT64260_PCI_0_CMD, ((1<<4) | (1<<5) | (1<<9))); + gt_clr_bits(GT64260_PCI_1_CMD, ((1<<4) | (1<<5) | (1<<9))); + + /* FEr#54 */ + gt_clr_bits(GT64260_CPU_SNOOP_BASE_0, 0xfffcf000); + gt_clr_bits(GT64260_CPU_SNOOP_BASE_1, 0xfffcf000); + gt_clr_bits(GT64260_CPU_SNOOP_BASE_2, 0xfffcf000); + gt_clr_bits(GT64260_CPU_SNOOP_BASE_3, 0xfffcf000); + + /* R#18 */ + gt_set_bits(GT64260_SDRAM_CONFIG, (1<<26)); + + } else if (gt64260_revision == GT64260A) { + /* R#18 */ + gt_set_bits(GT64260_SDRAM_CONFIG, (1<<26)); + + /* No longer errata so turn on */ + gt_set_bits(GT64260_PCI_0_CMD, ((1<<4) | (1<<5) | (1<<9))); + gt_set_bits(GT64260_PCI_1_CMD, ((1<<4) | (1<<5) | (1<<9))); + } +} /* gt64260_check_errata() */ + + +/* + ***************************************************************************** + * + * General Window Setting Routines + * + ***************************************************************************** + */ + +static int +gt64260_set_32bit_window(u32 base_addr, + u32 size, + u32 other_bits, + u32 bot_reg, + u32 top_reg) +{ + u32 val; + + if (size > 0) { + /* Set up the window on the CPU side */ + gt_write(bot_reg, (base_addr >> 20) | other_bits); + gt_write(top_reg, (base_addr + size - 1) >> 20); + } else { /* Disable window */ + gt_write(top_reg, 0x00000000); + gt_write(bot_reg, 0x00000fff | other_bits); + } + + val = gt_read(bot_reg); /* Flush FIFO */ + return 0; +} /* gt64260_set_32bit_window() */ + +static int +gt64260_set_64bit_window(u32 base_addr_hi, + u32 base_addr_lo, + u32 size, + u32 other_bits, + u32 bot_reg_hi, + u32 bot_reg_lo, + u32 top_reg) +{ + int rc; + + if ((rc = gt64260_set_32bit_window(base_addr_lo, + size, + other_bits, + bot_reg_lo, + top_reg)) == 0) { + + gt_write(bot_reg_hi, base_addr_hi); + base_addr_hi = gt_read(bot_reg_hi); /* Flush FIFO */ + } + + return rc; +} /* gt64260_set_64bit_window() */ + + +/* + ***************************************************************************** + * + * CPU Configuration Routines + * + ***************************************************************************** + */ + +int +gt64260_cpu_scs_set_window(u32 window, + u32 base_addr, + u32 size) +{ + static u32 + cpu_scs_windows[GT64260_CPU_SCS_DECODE_WINDOWS][2] = { + { GT64260_CPU_SCS_DECODE_0_BOT, GT64260_CPU_SCS_DECODE_0_TOP }, + { GT64260_CPU_SCS_DECODE_1_BOT, GT64260_CPU_SCS_DECODE_1_TOP }, + { GT64260_CPU_SCS_DECODE_2_BOT, GT64260_CPU_SCS_DECODE_2_TOP }, + { GT64260_CPU_SCS_DECODE_3_BOT, GT64260_CPU_SCS_DECODE_3_TOP }, + }; /* cpu_scs_windows[][] */ + int rc = -1; + + if (window < GT64260_CPU_SCS_DECODE_WINDOWS) { + rc = gt64260_set_32bit_window(base_addr, + size, + 0, + cpu_scs_windows[window][0], + cpu_scs_windows[window][1]); + } + + return rc; +} /* gt64260_cpu_scs_set_window() */ + +int +gt64260_cpu_cs_set_window(u32 window, + u32 base_addr, + u32 size) +{ + static u32 + cpu_cs_windows[GT64260_CPU_CS_DECODE_WINDOWS][2] = { + { GT64260_CPU_CS_DECODE_0_BOT, GT64260_CPU_CS_DECODE_0_TOP }, + { GT64260_CPU_CS_DECODE_1_BOT, GT64260_CPU_CS_DECODE_1_TOP }, + { GT64260_CPU_CS_DECODE_2_BOT, GT64260_CPU_CS_DECODE_2_TOP }, + { GT64260_CPU_CS_DECODE_3_BOT, GT64260_CPU_CS_DECODE_3_TOP }, + }; /* cpu_cs_windows[][] */ + int rc = -1; + + if (window < GT64260_CPU_CS_DECODE_WINDOWS) { + rc = gt64260_set_32bit_window(base_addr, + size, + 0, + cpu_cs_windows[window][0], + cpu_cs_windows[window][1]); + } + + return rc; +} /* gt64260_cpu_cs_set_window() */ + +int +gt64260_cpu_boot_set_window(u32 base_addr, + u32 size) +{ + int rc; + + rc = gt64260_set_32bit_window(base_addr, + size, + 0, + GT64260_CPU_BOOT_CS_DECODE_0_BOT, + GT64260_CPU_BOOT_CS_DECODE_0_TOP); + + return rc; +} /* gt64260_cpu_boot_set_window() */ + +/* + * gt64260_cpu_set_pci_io_window() + * + * Set up a CPU window into PCI I/O or MEM space. + * Always do Read/Modify/Write to window regs. + */ +static int +gt64260_cpu_pci_set_window(u32 cpu_base_addr, + u32 pci_base_addr, + u32 size, + u32 other_bits, + u32 bot_reg, + u32 top_reg, + u32 remap_reg) +{ + u32 val; + int rc; + + if ((rc = gt64260_set_32bit_window(cpu_base_addr, + size, + other_bits, + bot_reg, + top_reg)) == 0) { + + /* Set up CPU->PCI remapping (on lower 32 bits) */ + gt_write(remap_reg, pci_base_addr >> 20); + val = gt_read(bot_reg); /* Flush FIFO */ + } + + return rc; +} /* gt64260_cpu_pci_set_window() */ + + +/* + * gt64260_cpu_set_pci_io_window() + * + * Set up a CPU window into PCI I/O space. + * Always do Read/Modify/Write to window regs. + */ +int +gt64260_cpu_set_pci_io_window(u32 pci_bus, + u32 cpu_base_addr, + u32 pci_base_addr, + u32 size, + u32 swap) +{ + /* 2 PCI buses with 1 I/O window each (from CPU point of view) */ + static u32 + cpu_pci_io_windows[GT64260_PCI_BUSES][3] = { + { GT64260_CPU_PCI_0_IO_DECODE_BOT, + GT64260_CPU_PCI_0_IO_DECODE_TOP, + GT64260_CPU_PCI_0_IO_REMAP }, + + { GT64260_CPU_PCI_1_IO_DECODE_BOT, + GT64260_CPU_PCI_1_IO_DECODE_TOP, + GT64260_CPU_PCI_1_IO_REMAP }, + }; /* cpu_pci_io_windows[][] */ + int rc = -1; + + if (pci_bus < GT64260_PCI_BUSES) { + rc = gt64260_cpu_pci_set_window(cpu_base_addr, + pci_base_addr, + size, + swap, + cpu_pci_io_windows[pci_bus][0], + cpu_pci_io_windows[pci_bus][1], + cpu_pci_io_windows[pci_bus][2]); + } + + return rc; +} /* gt64260_cpu_set_pci_io_window() */ + +/* + * gt64260_cpu_set_pci_mem_window() + * + * Set up a CPU window into PCI MEM space (4 PCI MEM windows per PCI bus). + * Always do Read/Modify/Write to window regs. + */ +int +gt64260_cpu_set_pci_mem_window(u32 pci_bus, + u32 window, + u32 cpu_base_addr, + u32 pci_base_addr_hi, + u32 pci_base_addr_lo, + u32 size, + u32 swap_64bit) +{ + /* 2 PCI buses with 4 memory windows each (from CPU point of view) */ + static u32 + cpu_pci_mem_windows[GT64260_PCI_BUSES][GT64260_PCI_MEM_WINDOWS_PER_BUS][4] = { + { /* PCI 0 */ + { GT64260_CPU_PCI_0_MEM_0_DECODE_BOT, + GT64260_CPU_PCI_0_MEM_0_DECODE_TOP, + GT64260_CPU_PCI_0_MEM_0_REMAP_HI, + GT64260_CPU_PCI_0_MEM_0_REMAP_LO }, + + { GT64260_CPU_PCI_0_MEM_1_DECODE_BOT, + GT64260_CPU_PCI_0_MEM_1_DECODE_TOP, + GT64260_CPU_PCI_0_MEM_1_REMAP_HI, + GT64260_CPU_PCI_0_MEM_1_REMAP_LO }, + + { GT64260_CPU_PCI_0_MEM_2_DECODE_BOT, + GT64260_CPU_PCI_0_MEM_2_DECODE_TOP, + GT64260_CPU_PCI_0_MEM_2_REMAP_HI, + GT64260_CPU_PCI_0_MEM_2_REMAP_LO }, + + { GT64260_CPU_PCI_0_MEM_3_DECODE_BOT, + GT64260_CPU_PCI_0_MEM_3_DECODE_TOP, + GT64260_CPU_PCI_0_MEM_3_REMAP_HI, + GT64260_CPU_PCI_0_MEM_3_REMAP_LO } + }, + + { /* PCI 1 */ + { GT64260_CPU_PCI_1_MEM_0_DECODE_BOT, + GT64260_CPU_PCI_1_MEM_0_DECODE_TOP, + GT64260_CPU_PCI_1_MEM_0_REMAP_HI, + GT64260_CPU_PCI_1_MEM_0_REMAP_LO }, + + { GT64260_CPU_PCI_1_MEM_1_DECODE_BOT, + GT64260_CPU_PCI_1_MEM_1_DECODE_TOP, + GT64260_CPU_PCI_1_MEM_1_REMAP_HI, + GT64260_CPU_PCI_1_MEM_1_REMAP_LO }, + + { GT64260_CPU_PCI_1_MEM_2_DECODE_BOT, + GT64260_CPU_PCI_1_MEM_2_DECODE_TOP, + GT64260_CPU_PCI_1_MEM_2_REMAP_HI, + GT64260_CPU_PCI_1_MEM_2_REMAP_LO }, + + { GT64260_CPU_PCI_1_MEM_3_DECODE_BOT, + GT64260_CPU_PCI_1_MEM_3_DECODE_TOP, + GT64260_CPU_PCI_1_MEM_3_REMAP_HI, + GT64260_CPU_PCI_1_MEM_3_REMAP_LO }, + } + }; /* cpu_pci_mem_windows[][][] */ + u32 remap_reg, remap; + int rc = -1; + + if ((pci_bus < GT64260_PCI_BUSES) && + (window < GT64260_PCI_MEM_WINDOWS_PER_BUS)) { + + if (gt64260_cpu_pci_set_window( + cpu_base_addr, + pci_base_addr_lo, + size, + swap_64bit, + cpu_pci_mem_windows[pci_bus][window][0], + cpu_pci_mem_windows[pci_bus][window][1], + cpu_pci_mem_windows[pci_bus][window][3]) == 0) { + + remap_reg = cpu_pci_mem_windows[pci_bus][window][2]; + gt_write(remap_reg, pci_base_addr_hi); + + remap = gt_read(remap_reg); /* Flush FIFO */ + + rc = 0; + } + } + + return rc; +} /* gt64260_cpu_set_pci_mem_window() */ + +int +gt64260_cpu_prot_set_window(u32 window, + u32 base_addr, + u32 size, + u32 access_bits) +{ + static u32 + cpu_prot_windows[GT64260_CPU_PROT_WINDOWS][2] = { + { GT64260_CPU_PROT_BASE_0, GT64260_CPU_PROT_TOP_0 }, + { GT64260_CPU_PROT_BASE_1, GT64260_CPU_PROT_TOP_1 }, + { GT64260_CPU_PROT_BASE_2, GT64260_CPU_PROT_TOP_2 }, + { GT64260_CPU_PROT_BASE_3, GT64260_CPU_PROT_TOP_3 }, + { GT64260_CPU_PROT_BASE_4, GT64260_CPU_PROT_TOP_4 }, + { GT64260_CPU_PROT_BASE_5, GT64260_CPU_PROT_TOP_5 }, + { GT64260_CPU_PROT_BASE_6, GT64260_CPU_PROT_TOP_6 }, + { GT64260_CPU_PROT_BASE_7, GT64260_CPU_PROT_TOP_7 }, + }; /* cpu_prot_windows[][] */ + int rc = -1; + + if (window < GT64260_CPU_PROT_WINDOWS) { + rc = gt64260_set_32bit_window(base_addr, + size, + access_bits, + cpu_prot_windows[window][0], + cpu_prot_windows[window][1]); + } + + return rc; +} /* gt64260_cpu_prot_set_window() */ + +int +gt64260_cpu_snoop_set_window(u32 window, + u32 base_addr, + u32 size, + u32 snoop_type) +{ + static u32 + cpu_snoop_windows[GT64260_CPU_SNOOP_WINDOWS][2] = { + { GT64260_CPU_SNOOP_BASE_0, GT64260_CPU_SNOOP_TOP_0 }, + { GT64260_CPU_SNOOP_BASE_1, GT64260_CPU_SNOOP_TOP_1 }, + { GT64260_CPU_SNOOP_BASE_2, GT64260_CPU_SNOOP_TOP_2 }, + { GT64260_CPU_SNOOP_BASE_3, GT64260_CPU_SNOOP_TOP_3 }, + }; /* cpu_snoop_windows[][] */ + int rc = -1; + + if ((window < GT64260_CPU_SNOOP_WINDOWS) && + (snoop_type <= GT64260_CPU_SNOOP_WB)) { + + rc = gt64260_set_32bit_window(base_addr, + size, + snoop_type, + cpu_snoop_windows[window][0], + cpu_snoop_windows[window][1]); + } + + return rc; +} /* gt64260_cpu_snoop_set_window() */ + +void +gt64260_cpu_disable_all_windows(void) +{ + int pci_bus, window; + + /* Don't disable SCS windows b/c we need to access system memory */ + + for (window=0; windowfirst_busno, + devfn, + pci_cfg_hdr_offset, + &val); + val &= 0x0000000f; + early_write_config_dword(hose, + hose->first_busno, + devfn, + pci_cfg_hdr_offset, + pci_base_addr | val); + gt64260_pci_exclude_bridge = save_exclude; + + return 0; +} /* gt64260_pci_slave_set_window() */ + +int +gt64260_pci_slave_scs_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr, + u32 cpu_base_addr, + u32 size) +{ + static u32 + pci_scs_windows[GT64260_PCI_BUSES][GT64260_PCI_SCS_WINDOWS][4] = { + { /* PCI 0 */ + { 0, 0x10, + GT64260_PCI_0_SLAVE_SCS_0_SIZE, + GT64260_PCI_0_SLAVE_SCS_0_REMAP }, + { 0, 0x14, + GT64260_PCI_0_SLAVE_SCS_1_SIZE, + GT64260_PCI_0_SLAVE_SCS_1_REMAP }, + { 0, 0x18, + GT64260_PCI_0_SLAVE_SCS_2_SIZE, + GT64260_PCI_0_SLAVE_SCS_2_REMAP }, + { 0, 0x1c, + GT64260_PCI_0_SLAVE_SCS_3_SIZE, + GT64260_PCI_0_SLAVE_SCS_3_REMAP }, + }, + { /* PCI 1 */ + { 0, 0x10, + GT64260_PCI_1_SLAVE_SCS_0_SIZE, + GT64260_PCI_1_SLAVE_SCS_0_REMAP }, + { 0, 0x14, + GT64260_PCI_1_SLAVE_SCS_1_SIZE, + GT64260_PCI_1_SLAVE_SCS_1_REMAP }, + { 0, 0x18, + GT64260_PCI_1_SLAVE_SCS_2_SIZE, + GT64260_PCI_1_SLAVE_SCS_2_REMAP }, + { 0, 0x1c, + GT64260_PCI_1_SLAVE_SCS_3_SIZE, + GT64260_PCI_1_SLAVE_SCS_3_REMAP }, + } + }; /* pci_scs_windows[][][] */ + int pci_bus; + int rc = -1; + + if (window < GT64260_PCI_SCS_WINDOWS) { + pci_bus = (hose->first_busno == 0) ? 0 : 1; + + rc = gt64260_pci_slave_set_window( + hose, + pci_base_addr, + cpu_base_addr, + size, + pci_scs_windows[pci_bus][window][0], + pci_scs_windows[pci_bus][window][1], + pci_scs_windows[pci_bus][window][2], + pci_scs_windows[pci_bus][window][3]); + } + + return rc; +} /* gt64260_pci_slave_scs_set_window() */ + +int +gt64260_pci_slave_cs_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr, + u32 cpu_base_addr, + u32 size) +{ + static u32 + pci_cs_windows[GT64260_PCI_BUSES][GT64260_PCI_CS_WINDOWS][4] = { + { /* PCI 0 */ + { 1, 0x10, + GT64260_PCI_0_SLAVE_CS_0_SIZE, + GT64260_PCI_0_SLAVE_CS_0_REMAP }, + { 1, 0x14, + GT64260_PCI_0_SLAVE_CS_1_SIZE, + GT64260_PCI_0_SLAVE_CS_1_REMAP }, + { 1, 0x18, + GT64260_PCI_0_SLAVE_CS_2_SIZE, + GT64260_PCI_0_SLAVE_CS_2_REMAP }, + { 1, 0x1c, + GT64260_PCI_0_SLAVE_CS_3_SIZE, + GT64260_PCI_0_SLAVE_CS_3_REMAP }, + }, + { /* PCI 1 */ + { 1, 0x10, + GT64260_PCI_1_SLAVE_CS_0_SIZE, + GT64260_PCI_1_SLAVE_CS_0_REMAP }, + { 1, 0x14, + GT64260_PCI_1_SLAVE_CS_1_SIZE, + GT64260_PCI_1_SLAVE_CS_1_REMAP }, + { 1, 0x18, + GT64260_PCI_1_SLAVE_CS_2_SIZE, + GT64260_PCI_1_SLAVE_CS_2_REMAP }, + { 1, 0x1c, + GT64260_PCI_1_SLAVE_CS_3_SIZE, + GT64260_PCI_1_SLAVE_CS_3_REMAP }, + } + }; /* pci_cs_windows[][][] */ + int pci_bus; + int rc = -1; + + if (window < GT64260_PCI_CS_WINDOWS) { + pci_bus = (hose->first_busno == 0) ? 0 : 1; + + rc = gt64260_pci_slave_set_window( + hose, + pci_base_addr, + cpu_base_addr, + size, + pci_cs_windows[pci_bus][window][0], + pci_cs_windows[pci_bus][window][1], + pci_cs_windows[pci_bus][window][2], + pci_cs_windows[pci_bus][window][3]); + } + + return rc; +} /* gt64260_pci_slave_cs_set_window() */ + +int +gt64260_pci_slave_boot_set_window(struct pci_controller *hose, + u32 pci_base_addr, + u32 cpu_base_addr, + u32 size) +{ + int rc; + + rc = gt64260_pci_slave_set_window(hose, + pci_base_addr, + cpu_base_addr, + size, + 1, + 0x20, + GT64260_PCI_1_SLAVE_BOOT_SIZE, + GT64260_PCI_1_SLAVE_BOOT_REMAP); + + return rc; +} /* gt64260_pci_slave_boot_set_window() */ + +int +gt64260_pci_slave_p2p_mem_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr, + u32 other_bus_base_addr, + u32 size) +{ + static u32 + pci_p2p_mem_windows[GT64260_PCI_BUSES][GT64260_PCI_P2P_MEM_WINDOWS][4]={ + { /* PCI 0 */ + { 2, 0x10, + GT64260_PCI_0_SLAVE_P2P_MEM_0_SIZE, + GT64260_PCI_0_SLAVE_P2P_MEM_0_REMAP_LO }, + { 2, 0x14, + GT64260_PCI_0_SLAVE_P2P_MEM_1_SIZE, + GT64260_PCI_0_SLAVE_P2P_MEM_1_REMAP_LO }, + }, + { /* PCI 1 */ + { 2, 0x10, + GT64260_PCI_1_SLAVE_P2P_MEM_0_SIZE, + GT64260_PCI_1_SLAVE_P2P_MEM_0_REMAP_LO }, + { 2, 0x14, + GT64260_PCI_1_SLAVE_P2P_MEM_1_SIZE, + GT64260_PCI_1_SLAVE_P2P_MEM_1_REMAP_LO }, + } + }; /* pci_p2p_mem_windows[][][] */ + int pci_bus; + int rc = -1; + + if (window < GT64260_PCI_P2P_MEM_WINDOWS) { + pci_bus = (hose->first_busno == 0) ? 0 : 1; + + rc = gt64260_pci_slave_set_window( + hose, + pci_base_addr, + other_bus_base_addr, + size, + pci_p2p_mem_windows[pci_bus][window][0], + pci_p2p_mem_windows[pci_bus][window][1], + pci_p2p_mem_windows[pci_bus][window][2], + pci_p2p_mem_windows[pci_bus][window][3]); + } + + return rc; +} /* gt64260_pci_slave_p2p_mem_set_window() */ + +int +gt64260_pci_slave_p2p_io_set_window(struct pci_controller *hose, + u32 pci_base_addr, + u32 other_bus_base_addr, + u32 size) +{ + int rc; + + rc = gt64260_pci_slave_set_window(hose, + pci_base_addr, + other_bus_base_addr, + size, + 2, + 0x18, + GT64260_PCI_1_SLAVE_P2P_IO_SIZE, + GT64260_PCI_1_SLAVE_P2P_IO_REMAP); + + return rc; +} /* gt64260_pci_slave_p2p_io_set_window() */ + +int +gt64260_pci_slave_dac_scs_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr_hi, + u32 pci_base_addr_lo, + u32 cpu_base_addr, + u32 size) +{ + static u32 + pci_dac_scs_windows[GT64260_PCI_BUSES][GT64260_PCI_DAC_SCS_WINDOWS][5]={ + { /* PCI 0 */ + { 4, 0x10, 0x14, + GT64260_PCI_0_SLAVE_DAC_SCS_0_SIZE, + GT64260_PCI_0_SLAVE_DAC_SCS_0_REMAP }, + { 4, 0x18, 0x1c, + GT64260_PCI_0_SLAVE_DAC_SCS_1_SIZE, + GT64260_PCI_0_SLAVE_DAC_SCS_1_REMAP }, + { 5, 0x10, 0x14, + GT64260_PCI_0_SLAVE_DAC_SCS_2_SIZE, + GT64260_PCI_0_SLAVE_DAC_SCS_2_REMAP }, + { 5, 0x18, 0x1c, + GT64260_PCI_0_SLAVE_DAC_SCS_3_SIZE, + GT64260_PCI_0_SLAVE_DAC_SCS_3_REMAP }, + }, + { /* PCI 1 */ + { 4, 0x10, 0x14, + GT64260_PCI_1_SLAVE_DAC_SCS_0_SIZE, + GT64260_PCI_1_SLAVE_DAC_SCS_0_REMAP }, + { 4, 0x18, 0x1c, + GT64260_PCI_1_SLAVE_DAC_SCS_1_SIZE, + GT64260_PCI_1_SLAVE_DAC_SCS_1_REMAP }, + { 5, 0x10, 0x14, + GT64260_PCI_1_SLAVE_DAC_SCS_2_SIZE, + GT64260_PCI_1_SLAVE_DAC_SCS_2_REMAP }, + { 5, 0x18, 0x1c, + GT64260_PCI_1_SLAVE_DAC_SCS_3_SIZE, + GT64260_PCI_1_SLAVE_DAC_SCS_3_REMAP }, + } + }; /* pci_dac_scs_windows[][][] */ + int pci_bus; + int rc = -1; + + if (window < GT64260_PCI_DAC_SCS_WINDOWS) { + pci_bus = (hose->first_busno == 0) ? 0 : 1; + + rc = gt64260_pci_slave_set_window( + hose, + pci_base_addr_lo, + cpu_base_addr, + size, + pci_dac_scs_windows[pci_bus][window][0], + pci_dac_scs_windows[pci_bus][window][1], + pci_dac_scs_windows[pci_bus][window][3], + pci_dac_scs_windows[pci_bus][window][4]); + + early_write_config_dword( + hose, + hose->first_busno, + PCI_DEVFN(0, pci_dac_scs_windows[pci_bus][window][0]), + pci_dac_scs_windows[pci_bus][window][2], + pci_base_addr_hi); + } + + return rc; +} /* gt64260_pci_slave_dac_scs_set_window() */ + +int +gt64260_pci_slave_dac_cs_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr_hi, + u32 pci_base_addr_lo, + u32 cpu_base_addr, + u32 size) +{ + static u32 + pci_dac_cs_windows[GT64260_PCI_BUSES][GT64260_PCI_DAC_CS_WINDOWS][5] = { + { /* PCI 0 */ + { 6, 0x10, 0x14, + GT64260_PCI_0_SLAVE_DAC_CS_0_SIZE, + GT64260_PCI_0_SLAVE_DAC_CS_0_REMAP }, + { 6, 0x18, 0x1c, + GT64260_PCI_0_SLAVE_DAC_CS_1_SIZE, + GT64260_PCI_0_SLAVE_DAC_CS_1_REMAP }, + { 6, 0x20, 0x24, + GT64260_PCI_0_SLAVE_DAC_CS_2_SIZE, + GT64260_PCI_0_SLAVE_DAC_CS_2_REMAP }, + { 7, 0x10, 0x14, + GT64260_PCI_0_SLAVE_DAC_CS_3_SIZE, + GT64260_PCI_0_SLAVE_DAC_CS_3_REMAP }, + }, + { /* PCI 1 */ + { 6, 0x10, 0x14, + GT64260_PCI_1_SLAVE_DAC_CS_0_SIZE, + GT64260_PCI_1_SLAVE_DAC_CS_0_REMAP }, + { 6, 0x18, 0x1c, + GT64260_PCI_1_SLAVE_DAC_CS_1_SIZE, + GT64260_PCI_1_SLAVE_DAC_CS_1_REMAP }, + { 6, 0x20, 0x24, + GT64260_PCI_1_SLAVE_DAC_CS_2_SIZE, + GT64260_PCI_1_SLAVE_DAC_CS_2_REMAP }, + { 7, 0x10, 0x14, + GT64260_PCI_1_SLAVE_DAC_CS_3_SIZE, + GT64260_PCI_1_SLAVE_DAC_CS_3_REMAP }, + } + }; /* pci_dac_cs_windows[][][] */ + int pci_bus; + int rc = -1; + + if (window < GT64260_PCI_CS_WINDOWS) { + pci_bus = (hose->first_busno == 0) ? 0 : 1; + + rc = gt64260_pci_slave_set_window( + hose, + pci_base_addr_lo, + cpu_base_addr, + size, + pci_dac_cs_windows[pci_bus][window][0], + pci_dac_cs_windows[pci_bus][window][1], + pci_dac_cs_windows[pci_bus][window][3], + pci_dac_cs_windows[pci_bus][window][4]); + + early_write_config_dword( + hose, + hose->first_busno, + PCI_DEVFN(0, pci_dac_cs_windows[pci_bus][window][0]), + pci_dac_cs_windows[pci_bus][window][2], + pci_base_addr_hi); + } + + return rc; +} /* gt64260_pci_slave_dac_cs_set_window() */ + +int +gt64260_pci_slave_dac_boot_set_window(struct pci_controller *hose, + u32 pci_base_addr_hi, + u32 pci_base_addr_lo, + u32 cpu_base_addr, + u32 size) +{ + int rc; + + rc = gt64260_pci_slave_set_window(hose, + pci_base_addr_lo, + cpu_base_addr, + size, + 7, + 0x18, + GT64260_PCI_1_SLAVE_BOOT_SIZE, + GT64260_PCI_1_SLAVE_BOOT_REMAP); + + early_write_config_dword(hose, + hose->first_busno, + PCI_DEVFN(0, 7), + 0x1c, + pci_base_addr_hi); + + return rc; +} /* gt64260_pci_slave_dac_boot_set_window() */ + +int +gt64260_pci_slave_dac_p2p_mem_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr_hi, + u32 pci_base_addr_lo, + u32 other_bus_base_addr, + u32 size) +{ + static u32 + pci_dac_p2p_mem_windows[GT64260_PCI_BUSES][GT64260_PCI_DAC_P2P_MEM_WINDOWS][5] = { + { /* PCI 0 */ + { 4, 0x20, 0x24, + GT64260_PCI_0_SLAVE_DAC_P2P_MEM_0_SIZE, + GT64260_PCI_0_SLAVE_DAC_P2P_MEM_0_REMAP_LO }, + { 5, 0x20, 0x24, + GT64260_PCI_0_SLAVE_DAC_P2P_MEM_1_SIZE, + GT64260_PCI_0_SLAVE_DAC_P2P_MEM_1_REMAP_LO }, + }, + { /* PCI 1 */ + { 4, 0xa0, 0xa4, + GT64260_PCI_0_SLAVE_DAC_P2P_MEM_0_SIZE, + GT64260_PCI_0_SLAVE_DAC_P2P_MEM_0_REMAP_LO }, + { 5, 0xa0, 0xa4, + GT64260_PCI_0_SLAVE_DAC_P2P_MEM_1_SIZE, + GT64260_PCI_0_SLAVE_DAC_P2P_MEM_1_REMAP_LO }, + } + }; /* pci_dac_p2p_windows[][][] */ + int pci_bus; + int rc = -1; + + if (window < GT64260_PCI_P2P_MEM_WINDOWS) { + pci_bus = (hose->first_busno == 0) ? 0 : 1; + + rc = gt64260_pci_slave_set_window( + hose, + pci_base_addr_lo, + other_bus_base_addr, + size, + pci_dac_p2p_mem_windows[pci_bus][window][0], + pci_dac_p2p_mem_windows[pci_bus][window][1], + pci_dac_p2p_mem_windows[pci_bus][window][3], + pci_dac_p2p_mem_windows[pci_bus][window][4]); + + early_write_config_dword( + hose, + hose->first_busno, + PCI_DEVFN(0, pci_dac_p2p_mem_windows[pci_bus][window][0]), + pci_dac_p2p_mem_windows[pci_bus][window][2], + pci_base_addr_hi); + } + + return rc; +} /* gt64260_pci_slave_dac_p2p_mem_set_window() */ + + +/* + ***************************************************************************** + * + * PCI Control Configuration Routines + * + ***************************************************************************** + */ + + +int +gt64260_pci_acc_cntl_set_window(u32 pci_bus, + u32 window, + u32 base_addr_hi, + u32 base_addr_lo, + u32 size, + u32 features) +{ + static u32 + pci_acc_cntl_windows[GT64260_PCI_BUSES][GT64260_PCI_ACC_CNTL_WINDOWS][3] = { + { /* PCI 0 */ + { GT64260_PCI_0_ACC_CNTL_0_BASE_HI, + GT64260_PCI_0_ACC_CNTL_0_BASE_LO, + GT64260_PCI_0_ACC_CNTL_0_TOP }, + + { GT64260_PCI_0_ACC_CNTL_1_BASE_HI, + GT64260_PCI_0_ACC_CNTL_1_BASE_LO, + GT64260_PCI_0_ACC_CNTL_1_TOP }, + + { GT64260_PCI_0_ACC_CNTL_2_BASE_HI, + GT64260_PCI_0_ACC_CNTL_2_BASE_LO, + GT64260_PCI_0_ACC_CNTL_2_TOP }, + + { GT64260_PCI_0_ACC_CNTL_3_BASE_HI, + GT64260_PCI_0_ACC_CNTL_3_BASE_LO, + GT64260_PCI_0_ACC_CNTL_3_TOP }, + + { GT64260_PCI_0_ACC_CNTL_4_BASE_HI, + GT64260_PCI_0_ACC_CNTL_4_BASE_LO, + GT64260_PCI_0_ACC_CNTL_4_TOP }, + + { GT64260_PCI_0_ACC_CNTL_5_BASE_HI, + GT64260_PCI_0_ACC_CNTL_5_BASE_LO, + GT64260_PCI_0_ACC_CNTL_5_TOP }, + + { GT64260_PCI_0_ACC_CNTL_6_BASE_HI, + GT64260_PCI_0_ACC_CNTL_6_BASE_LO, + GT64260_PCI_0_ACC_CNTL_6_TOP }, + + { GT64260_PCI_0_ACC_CNTL_7_BASE_HI, + GT64260_PCI_0_ACC_CNTL_7_BASE_LO, + GT64260_PCI_0_ACC_CNTL_7_TOP }, + }, + { /* PCI 1 */ + { GT64260_PCI_1_ACC_CNTL_0_BASE_HI, + GT64260_PCI_1_ACC_CNTL_0_BASE_LO, + GT64260_PCI_1_ACC_CNTL_0_TOP }, + + { GT64260_PCI_1_ACC_CNTL_1_BASE_HI, + GT64260_PCI_1_ACC_CNTL_1_BASE_LO, + GT64260_PCI_1_ACC_CNTL_1_TOP }, + + { GT64260_PCI_1_ACC_CNTL_2_BASE_HI, + GT64260_PCI_1_ACC_CNTL_2_BASE_LO, + GT64260_PCI_1_ACC_CNTL_2_TOP }, + + { GT64260_PCI_1_ACC_CNTL_3_BASE_HI, + GT64260_PCI_1_ACC_CNTL_3_BASE_LO, + GT64260_PCI_1_ACC_CNTL_3_TOP }, + + { GT64260_PCI_1_ACC_CNTL_4_BASE_HI, + GT64260_PCI_1_ACC_CNTL_4_BASE_LO, + GT64260_PCI_1_ACC_CNTL_4_TOP }, + + { GT64260_PCI_1_ACC_CNTL_5_BASE_HI, + GT64260_PCI_1_ACC_CNTL_5_BASE_LO, + GT64260_PCI_1_ACC_CNTL_5_TOP }, + + { GT64260_PCI_1_ACC_CNTL_6_BASE_HI, + GT64260_PCI_1_ACC_CNTL_6_BASE_LO, + GT64260_PCI_1_ACC_CNTL_6_TOP }, + + { GT64260_PCI_1_ACC_CNTL_7_BASE_HI, + GT64260_PCI_1_ACC_CNTL_7_BASE_LO, + GT64260_PCI_1_ACC_CNTL_7_TOP }, + } + }; /* pci_acc_cntl_windows[][][] */ + int rc = -1; + + if ((pci_bus < GT64260_PCI_BUSES) && + (window < GT64260_PCI_ACC_CNTL_WINDOWS)) { + + rc = gt64260_set_64bit_window( + base_addr_hi, + base_addr_lo, + size, + features, + pci_acc_cntl_windows[pci_bus][window][0], + pci_acc_cntl_windows[pci_bus][window][1], + pci_acc_cntl_windows[pci_bus][window][2]); + } + + return rc; +} /* gt64260_pci_acc_cntl_set_window() */ + +int +gt64260_pci_snoop_set_window(u32 pci_bus, + u32 window, + u32 base_addr_hi, + u32 base_addr_lo, + u32 size, + u32 snoop_type) +{ + static u32 + pci_snoop_windows[GT64260_PCI_BUSES][GT64260_PCI_SNOOP_WINDOWS][3] = { + { /* PCI 0 */ + { GT64260_PCI_0_SNOOP_0_BASE_HI, + GT64260_PCI_0_SNOOP_0_BASE_LO, + GT64260_PCI_0_SNOOP_0_TOP }, + + { GT64260_PCI_0_SNOOP_1_BASE_HI, + GT64260_PCI_0_SNOOP_1_BASE_LO, + GT64260_PCI_0_SNOOP_1_TOP }, + + { GT64260_PCI_0_SNOOP_2_BASE_HI, + GT64260_PCI_0_SNOOP_2_BASE_LO, + GT64260_PCI_0_SNOOP_2_TOP }, + + { GT64260_PCI_0_SNOOP_3_BASE_HI, + GT64260_PCI_0_SNOOP_3_BASE_LO, + GT64260_PCI_0_SNOOP_3_TOP }, + }, + { /* PCI 1 */ + { GT64260_PCI_1_SNOOP_0_BASE_HI, + GT64260_PCI_1_SNOOP_0_BASE_LO, + GT64260_PCI_1_SNOOP_0_TOP }, + + { GT64260_PCI_1_SNOOP_1_BASE_HI, + GT64260_PCI_1_SNOOP_1_BASE_LO, + GT64260_PCI_1_SNOOP_1_TOP }, + + { GT64260_PCI_1_SNOOP_2_BASE_HI, + GT64260_PCI_1_SNOOP_2_BASE_LO, + GT64260_PCI_1_SNOOP_2_TOP }, + + { GT64260_PCI_1_SNOOP_3_BASE_HI, + GT64260_PCI_1_SNOOP_3_BASE_LO, + GT64260_PCI_1_SNOOP_3_TOP }, + }, + }; /* pci_snoop_windows[][][] */ + int rc = -1; + + if ((pci_bus < GT64260_PCI_BUSES) && + (window < GT64260_PCI_SNOOP_WINDOWS)) { + + rc = gt64260_set_64bit_window( + base_addr_hi, + base_addr_lo, + size, + snoop_type, + pci_snoop_windows[pci_bus][window][0], + pci_snoop_windows[pci_bus][window][1], + pci_snoop_windows[pci_bus][window][2]); + } + + return rc; +} /* gt64260_pci_snoop_set_window() */ + +/* + ***************************************************************************** + * + * 64260's Register Base Address Routines + * + ***************************************************************************** + */ + +/* + * gt64260_remap_bridge_regs() + * + * Move the bridge's register to the specified base address. + * Assume that there are no other windows overlapping this area and that + * all but the highest 3 nibbles are 0. + */ +int +gt64260_set_base(u32 new_base) +{ + u32 val; + int limit = 100000; + int rc = 0; + + val = gt_read(GT64260_INTERNAL_SPACE_DECODE); + val = (new_base >> 20) | (val & 0xffff0000); + gt_write(GT64260_INTERNAL_SPACE_DECODE, val); + + iounmap((void *)gt64260_base); + gt64260_base = (u32)ioremap((new_base & 0xfff00000), + GT64260_INTERNAL_SPACE_SIZE); + + do { /* Wait for bridge to move its regs */ + val = gt_read(GT64260_INTERNAL_SPACE_DECODE); + } while ((val != 0xffffffff) && (limit-- > 0)); + + if (limit <= 0) { + rc = -1; + } + + return rc; +} /* gt64260_remap_bridge_regs() */ + +/* + * gt64260_get_base() + * + * Return the current virtual base address of the 64260's registers. + */ +int +gt64260_get_base(u32 *base) +{ + *base = gt64260_base; + return 0; +} /* gt64260_remap_bridge_regs() */ + +/* + ***************************************************************************** + * + * Exclude PCI config space access to bridge itself + * + ***************************************************************************** + */ + +/* + * gt64260_exclude_pci_device() + * + * This routine causes the PCI subsystem to skip the PCI device in slot 0 + * (which is the 64260 itself) unless explicitly allowed. + */ +int +gt64260_pci_exclude_device(u8 bus, u8 devfn) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + + /* Skip slot 0 and 1 on both hoses */ + if ((gt64260_pci_exclude_bridge == TRUE) && + (PCI_SLOT(devfn) == 0) && + (hose->first_busno == bus)) { + + return PCIBIOS_DEVICE_NOT_FOUND; + } + else { + return PCIBIOS_SUCCESSFUL; + } +} /* gt64260_pci_exclude_device() */ + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) + +/* + * gt64260_putc() + * + * Dump a character out the MPSC port for gt64260_mpsc_progress + * this assumes the baud rate has already been set up and the + * MPSC initialized by the bootloader or firmware. + */ + +static inline void +gt_putc(char c){ + mb(); + gt_write(GT64260_MPSC_0_CHR_1, c); + mb(); + gt_write(GT64260_MPSC_0_CHR_2, 0x200); + mb(); + + udelay(10000); +} + +void +puthex(unsigned long val){ + + int i; + + for (i = 7; i >= 0; i--) { + gt_putc("0123456789ABCDEF"[(val>>28) & 0x0f]); + val <<= 4; + } + gt_putc('\r'); + gt_putc('\n'); + +} + + +void +gt64260_mpsc_progress(char *s, unsigned short hex){ + /* spit stuff out the 64260 mpsc */ + + volatile char c; + while ((c = *s++) != 0){ + gt_putc(c); + if ( c == '\n' ) gt_putc('\r'); + } + gt_putc('\n'); + gt_putc('\r'); + + return; +} + +#endif /* CONFIG_DEBUG_TEXT */ diff -Nru a/arch/ppc/kernel/gt64260_pic.c b/arch/ppc/kernel/gt64260_pic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/gt64260_pic.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,245 @@ +/* + * arch/ppc/kernel/gt64260_pic.c + * + * Interrupt controller support for Galileo's GT64260. + * + * Author: Chris Zankel + * Modified by: Mark A. Greer + * + * Based on sources from Rabeeh Khoury / Galileo Technology + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * This file contains the specific functions to support the GT64260 + * interrupt controller. + * + * The GT64260 has two main interrupt registers (high and low) that + * summarizes the interrupts generated by the units of the GT64260. + * Each bit is assigned to an interrupt number, where the low register + * are assigned from IRQ0 to IRQ31 and the high cause register + * from IRQ32 to IRQ63 + * The GPP (General Purpose Port) interrupts are assigned from IRQ64 (GPP0) + * to IRQ95 (GPP31). + * get_irq() returns the lowest interrupt number that is currently asserted. + * + * Note: + * - This driver does not initialize the GPP when used as an interrupt + * input. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* ========================== forward declaration ========================== */ + +static void gt64260_unmask_irq(unsigned int); +static void gt64260_mask_irq(unsigned int); + +/* ========================== local declarations =========================== */ + +struct hw_interrupt_type gt64260_pic = { + " GT64260_PIC ", /* typename */ + NULL, /* startup */ + NULL, /* shutdown */ + gt64260_unmask_irq, /* enable */ + gt64260_mask_irq, /* disable */ + gt64260_mask_irq, /* ack */ + NULL, /* end */ + NULL /* set_affinity */ +}; + +u32 gt64260_irq_base = 0; /* GT64260 handles the next 96 IRQs from here */ + +/* gt64260_init_irq() + * + * This function initializes the interrupt controller. It assigns + * all interrupts from IRQ0 to IRQ95 to the gt64260 interrupt controller. + * + * Input Variable(s): + * None. + * + * Outpu. Variable(s): + * None. + * + * Returns: + * void + * + * Note: + * We register all GPP inputs as interrupt source, but disable them. + */ + +__init void +gt64260_init_irq(void) +{ + int i; + + if ( ppc_md.progress ) ppc_md.progress("gt64260_init_irq: enter", 0x0); + + ppc_cached_irq_mask[0] = 0; + ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */ + ppc_cached_irq_mask[2] = 0; + + /* disable all interrupts and clear current interrupts */ + gt_write(GT64260_GPP_INTR_MASK, ppc_cached_irq_mask[2]); + gt_write(GT64260_GPP_INTR_CAUSE,0); + gt_write(GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0]); + gt_write(GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1]); + + /* use the gt64260 for all (possible) interrupt sources */ + for( i = gt64260_irq_base; i < (gt64260_irq_base + 96); i++ ) { + irq_desc[i].handler = >64260_pic; + } + + if ( ppc_md.progress ) ppc_md.progress("gt64260_init_irq: exit", 0x0); +} + + +/* gt64260_get_irq() + * + * This function returns the lowest interrupt number of all interrupts that + * are currently asserted. + * + * Input Variable(s): + * struct pt_regs* not used + * + * Output Variable(s): + * None. + * + * Returns: + * int or -2 (bogus interrupt) + * + */ +int +gt64260_get_irq(struct pt_regs *regs) +{ + int irq; + int irq_gpp; + + irq = gt_read(GT64260_IC_MAIN_CAUSE_LO); + irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]); + + if (irq == -1) { + irq = gt_read(GT64260_IC_MAIN_CAUSE_HI); + irq = __ilog2((irq & 0x0f000db7) & ppc_cached_irq_mask[1]); + + if (irq == -1) { + irq = -2; /* bogus interrupt, should never happen */ + } else { + if (irq >= 24) { + irq_gpp = gt_read(GT64260_GPP_INTR_CAUSE); + irq_gpp = __ilog2(irq_gpp & + ppc_cached_irq_mask[2]); + + if (irq_gpp == -1) { + irq = -2; + } else { + irq = irq_gpp + 64; + gt_write(GT64260_GPP_INTR_CAUSE, ~(1<<(irq-64))); + } + } else { + irq += 32; + } + } + } + + if( irq < 0 ) { + return( irq ); + } else { + return( gt64260_irq_base + irq ); + } +} + +/* gt64260_unmask_irq() + * + * This function enables an interrupt. + * + * Input Variable(s): + * unsigned int interrupt number (IRQ0...IRQ95). + * + * Output Variable(s): + * None. + * + * Returns: + * void + */ + +static void +gt64260_unmask_irq(unsigned int irq) +{ + irq -= gt64260_irq_base; + if (irq > 31) { + if (irq > 63) { + /* unmask GPP irq */ + gt_write(GT64260_GPP_INTR_MASK, + ppc_cached_irq_mask[2] |= (1<<(irq-64))); + } else { + /* mask high interrupt register */ + gt_write(GT64260_IC_CPU_INTR_MASK_HI, + ppc_cached_irq_mask[1] |= (1<<(irq-32))); + } + } else { + /* mask low interrupt register */ + gt_write(GT64260_IC_CPU_INTR_MASK_LO, + ppc_cached_irq_mask[0] |= (1< 31) { + if (irq > 63) { + /* mask GPP irq */ + gt_write(GT64260_GPP_INTR_MASK, + ppc_cached_irq_mask[2] &= ~(1<<(irq-64))); + } else { + /* mask high interrupt register */ + gt_write(GT64260_IC_CPU_INTR_MASK_HI, + ppc_cached_irq_mask[1] &= ~(1<<(irq-32))); + } + } else { + /* mask low interrupt register */ + gt_write(GT64260_IC_CPU_INTR_MASK_LO, + ppc_cached_irq_mask[0] &= ~(1< +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * Initialize the Motorola MCG Harrier host bridge. + * + * This means setting up the PPC bus to PCI memory and I/O space mappings, + * setting the PCI memory space address of the MPIC (mapped straight + * through), and ioremap'ing the mpic registers. + * 'OpenPIC_Addr' will be set correctly by this routine. + * This routine will not change the PCI_CONFIG_ADDR or PCI_CONFIG_DATA + * addresses and assumes that the mapping of PCI memory space back to system + * memory is set up correctly by PPCBug. + */ +int __init +harrier_init(struct pci_controller *hose, + uint ppc_reg_base, + ulong processor_pci_mem_start, + ulong processor_pci_mem_end, + ulong processor_pci_io_start, + ulong processor_pci_io_end, + ulong processor_mpic_base) +{ + uint addr, offset; + + /* + * Some sanity checks... + */ + if (((processor_pci_mem_start&0xffff0000) != processor_pci_mem_start) || + ((processor_pci_io_start &0xffff0000) != processor_pci_io_start)) { + printk("harrier_init: %s\n", + "PPC to PCI mappings must start on 64 KB boundaries"); + return -1; + } + + if (((processor_pci_mem_end &0x0000ffff) != 0x0000ffff) || + ((processor_pci_io_end &0x0000ffff) != 0x0000ffff)) { + printk("harrier_init: PPC to PCI mappings %s\n", + "must end just before a 64 KB boundaries"); + return -1; + } + + if (((processor_pci_mem_end - processor_pci_mem_start) != + (hose->mem_space.end - hose->mem_space.start)) || + ((processor_pci_io_end - processor_pci_io_start) != + (hose->io_space.end - hose->io_space.start))) { + printk("harrier_init: %s\n", + "PPC and PCI memory or I/O space sizes don't match"); + return -1; + } + + if ((processor_mpic_base & 0xfffc0000) != processor_mpic_base) { + printk("harrier_init: %s\n", + "MPIC address must start on 256 KB boundary"); + return -1; + } + + if ((pci_dram_offset & 0xffff0000) != pci_dram_offset) { + printk("harrier_init: %s\n", + "pci_dram_offset must be multiple of 64 KB"); + return -1; + } + + /* + * Program the OTAD/OTOF registers to set up the PCI Mem & I/O + * space mappings. These are the mappings going from the processor to + * the PCI bus. + * + * Note: Don't need to 'AND' start/end addresses with 0xffff0000 + * because sanity check above ensures that they are properly + * aligned. + */ + + /* Set up PPC->PCI Mem mapping */ + addr = processor_pci_mem_start | (processor_pci_mem_end >> 16); + offset = (hose->mem_space.start - processor_pci_mem_start) | 0x92; + out_be32((uint *)(ppc_reg_base + HARRIER_OTAD0_OFF), addr); + out_be32((uint *)(ppc_reg_base + HARRIER_OTOF0_OFF), offset); + + /* Set up PPC->PCI I/O mapping -- Contiguous I/O space */ + addr = processor_pci_io_start | (processor_pci_io_end >> 16); + offset = (hose->io_space.start - processor_pci_io_start) | 0x80; + out_be32((uint *)(ppc_reg_base + HARRIER_OTAD1_OFF), addr); + out_be32((uint *)(ppc_reg_base + HARRIER_OTOF1_OFF), offset); + + /* Enable MPIC */ + OpenPIC_Addr = (void *)processor_mpic_base; + addr = (processor_mpic_base >> 16) | 1; + out_be16((ushort *)(ppc_reg_base + HARRIER_MBAR_OFF), addr); + out_8((u_char *)(ppc_reg_base + HARRIER_MPIC_CSR_OFF), + HARRIER_MPIC_OPI_ENABLE); + + return 0; +} + +/* + * Find the amount of RAM present. + * This assumes that PPCBug has initialized the memory controller (SMC) + * on the Harrier correctly (i.e., it does no sanity checking). + * It also assumes that the memory base registers are set to configure the + * memory as contigous starting with "RAM A BASE", "RAM B BASE", etc. + * however, RAM base registers can be skipped (e.g. A, B, C are set, + * D is skipped but E is set is okay). + */ +#define MB (1024*1024UL) + +static uint harrier_size_table[] __initdata = { + 0 * MB, /* 0 ==> 0 MB */ + 32 * MB, /* 1 ==> 32 MB */ + 64 * MB, /* 2 ==> 64 MB */ + 64 * MB, /* 3 ==> 64 MB */ + 128 * MB, /* 4 ==> 128 MB */ + 128 * MB, /* 5 ==> 128 MB */ + 128 * MB, /* 6 ==> 128 MB */ + 256 * MB, /* 7 ==> 256 MB */ + 256 * MB, /* 8 ==> 256 MB */ + 256 * MB, /* 9 ==> 256 MB */ + 512 * MB, /* a ==> 512 MB */ + 512 * MB, /* b ==> 512 MB */ + 512 * MB, /* c ==> 512 MB */ + 1024 * MB, /* d ==> 1024 MB */ + 1024 * MB, /* e ==> 1024 MB */ + 2048 * MB, /* f ==> 2048 MB */ +}; + +/* + * *** WARNING: You MUST have a BAT set up to map in the XCSR regs *** + * + * Read the memory controller's registers to determine the amount of system + * memory. Assumes that the memory controller registers are already mapped + * into virtual memory--too early to use ioremap(). + */ +unsigned long __init +harrier_get_mem_size(uint xcsr_base) +{ + ulong last_addr; + int i; + uint vend_dev_id; + uint *size_table; + uint val; + uint *csrp; + uint size; + int size_table_entries; + + vend_dev_id = in_be32((uint *)xcsr_base + PCI_VENDOR_ID); + + if (((vend_dev_id & 0xffff0000) >> 16) != PCI_VENDOR_ID_MOTOROLA) { + printk("harrier_get_mem_size: %s (0x%x)\n", + "Not a Motorola Memory Controller", vend_dev_id); + return 0; + } + + vend_dev_id &= 0x0000ffff; + + if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_HARRIER) { + size_table = harrier_size_table; + size_table_entries = sizeof(harrier_size_table) / + sizeof(harrier_size_table[0]); + } + else { + printk("harrier_get_mem_size: %s (0x%x)\n", + "Not a Harrier", vend_dev_id); + return 0; + } + + last_addr = 0; + + csrp = (uint *)(xcsr_base + HARRIER_SDBA_OFF); + for (i=0; i<8; i++) { + val = in_be32(csrp++); + + if (val & 0x100) { /* If enabled */ + size = val >> HARRIER_SDB_SIZE_SHIFT; + size &= HARRIER_SDB_SIZE_MASK; + if (size >= size_table_entries) { + break; /* Register not set correctly */ + } + size = size_table[size]; + + val &= ~(size-1); + val += size; + + if (val > last_addr) { + last_addr = val; + } + } + } + + return last_addr; +} diff -Nru a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S --- a/arch/ppc/kernel/head.S Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/head.S Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.head.S 1.31 10/18/01 15:02:09 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * PowerPC version @@ -22,17 +22,19 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. - * + * */ #include -#include "ppc_asm.h" #include #include #include #include #include #include +#include +#include +#include "ppc_defs.h" #ifdef CONFIG_APUS #include @@ -48,9 +50,9 @@ ld RB,(n*32)+24(reg); \ mtspr DBAT##n##U,RA; \ mtspr DBAT##n##L,RB; \ - + #else /* CONFIG_PPC64BRIDGE */ - + /* 601 only have IBAT; cr0.eq is set on 601 when using this macro */ #define LOAD_BAT(n, reg, RA, RB) \ /* see the comment for clear_bats() -- Cort */ \ @@ -66,10 +68,13 @@ lwz RB,(n*16)+12(reg); \ mtspr DBAT##n##U,RA; \ mtspr DBAT##n##L,RB; \ -1: +1: #endif /* CONFIG_PPC64BRIDGE */ .text + .stabs "arch/ppc/kernel/",N_SO,0,0,0f + .stabs "head.S",N_SO,0,0,0f +0: .globl _stext _stext: @@ -86,8 +91,8 @@ * but we're always started by some kind of bootloader now. * -- Cort */ - nop - nop + nop /* used by __secondary_hold on prep (mtx) and chrp smp */ + nop /* used by __secondary_hold on prep (mtx) and chrp smp */ nop /* PMAC @@ -113,7 +118,7 @@ * PREP * This is jumped to on prep systems right after the kernel is relocated * to its proper place in memory by the boot loader. The expected layout - * of the regs is: + * of the regs is: * r3: ptr to residual data * r4: initrd_start or if no initrd then 0 * r5: initrd_end - unused if r4 is 0 @@ -124,7 +129,7 @@ * start_here() to do the real work. * -- Cort */ - + .globl __start __start: /* @@ -153,7 +158,6 @@ bl fix_mem_constants #endif /* CONFIG_APUS */ -#ifndef CONFIG_GEMINI /* Switch MMU off, clear BATs and flush TLB. At this point, r3 contains * the physical address we are running at, returned by early_init() */ @@ -161,28 +165,11 @@ __after_mmu_off: bl clear_bats bl flush_tlbs -#endif -#ifndef CONFIG_POWER4 - /* POWER4 doesn't have BATs */ bl initial_bats #if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) bl setup_disp_bat #endif -#else /* CONFIG_POWER4 */ -/* - * Load up the SDR1 and segment register values now - * since we don't have the BATs. - */ - bl reloc_offset - addis r4,r3,_SDR1@ha /* get the value from _SDR1 */ - lwz r4,_SDR1@l(r4) /* assume hash table below 4GB */ - mtspr SDR1,r4 - slbia - lis r5,0x2000 /* set pseudo-segment reg 12 */ - ori r5,r5,0x0ccc - mtsr 12,r5 -#endif /* CONFIG_POWER4 */ #ifndef CONFIG_APUS /* @@ -217,11 +204,17 @@ SYNC RFI /* enables MMU */ -#ifdef CONFIG_SMP +/* + * We need __secondary_hold as a place to hold the other cpus on + * an SMP machine, even when we are running a UP kernel. + */ + . = 0xc0 /* for prep bootloader */ + li r3,1 /* MTX only has 1 cpu */ .globl __secondary_hold __secondary_hold: /* tell the master we're here */ stw r3,4(0) +#ifdef CONFIG_SMP 100: lwz r4,0(0) /* wait until we're told to start */ cmpw 0,r4,r3 @@ -229,7 +222,9 @@ /* our cpu # was at addr 0 - go */ mr r24,r3 /* cpu # */ b __secondary_start -#endif +#else + b . +#endif /* CONFIG_SMP */ /* * Exception entry code. This code runs with address translation @@ -289,13 +284,11 @@ .long ret_from_except /* System reset */ -#ifdef CONFIG_SMP /* MVME/MTX and gemini start the secondary here */ -#ifdef CONFIG_GEMINI +/* core99 pmac starts the seconary here by changing the vector, and + putting it back to what it was (UnknownException) when done. */ +#if defined(CONFIG_GEMINI) && defined(CONFIG_SMP) . = 0x100 b __secondary_start_gemini -#else /* CONFIG_GEMINI */ - STD_EXCEPTION(0x100, Reset, __secondary_start_psurge) -#endif /* CONFIG_GEMINI */ #else STD_EXCEPTION(0x100, Reset, UnknownException) #endif @@ -388,16 +381,12 @@ EXCEPTION_PROLOG; addi r3,r1,STACK_FRAME_OVERHEAD li r20,MSR_KERNEL -#ifndef CONFIG_APUS li r4,0 bl transfer_to_handler .globl do_IRQ_intercept do_IRQ_intercept: .long do_IRQ; .long ret_from_intercept -#else - bl apus_interrupt_entry -#endif /* CONFIG_APUS */ /* Alignment exception */ . = 0x600 @@ -487,7 +476,7 @@ Trap_0f: EXCEPTION_PROLOG b trap_0f_cont - + /* * Handle TLB miss for instruction on 603/603e. * Note: we get an alternate set of r0 - r3 to use automatically. @@ -636,7 +625,7 @@ mtcrf 0x80,r3 /* Restore CR0 */ mtmsr r0 b DataAccess - + /* * Handle TLB miss for DATA Store on 603/603e */ @@ -746,74 +735,11 @@ #endif /* CONFIG_PPC64BRIDGE */ /* - * This code finishes saving the registers to the exception frame - * and jumps to the appropriate handler for the exception, turning - * on address translation. - */ - .globl transfer_to_handler -transfer_to_handler: - stw r22,_NIP(r21) - stw r23,_MSR(r21) - SAVE_4GPRS(8, r21) - SAVE_8GPRS(12, r21) - SAVE_8GPRS(24, r21) - andi. r23,r23,MSR_PR - mfspr r23,SPRG3 /* if from user, fix up THREAD.regs */ - beq 2f - addi r24,r1,STACK_FRAME_OVERHEAD - stw r24,PT_REGS(r23) -#ifdef CONFIG_ALTIVEC -BEGIN_FTR_SECTION - mfspr r22,SPRN_VRSAVE /* if G4, save vrsave register value */ - stw r22,THREAD_VRSAVE(r23) -END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) -#endif /* CONFIG_ALTIVEC */ -2: addi r2,r23,-THREAD /* set r2 to current */ - tovirt(r2,r2) - mflr r23 - andi. r24,r23,0x3f00 /* get vector offset */ - stw r24,TRAP(r21) - li r22,0 - stw r22,RESULT(r21) - mtspr SPRG2,r22 /* r1 is now kernel sp */ - addi r24,r2,TASK_STRUCT_SIZE /* check for kernel stack overflow */ - cmplw 0,r1,r2 - cmplw 1,r1,r24 - crand 1,1,4 - bgt- stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ - lwz r24,0(r23) /* virtual address of handler */ - lwz r23,4(r23) /* where to go when done */ - FIX_SRR1(r20,r22) - mtspr SRR0,r24 - mtspr SRR1,r20 - mtlr r23 - SYNC - RFI /* jump to handler, enable MMU */ - -/* - * On kernel stack overflow, load up an initial stack pointer - * and call StackOverflow(regs), which should not return. - */ -stack_ovf: - addi r3,r1,STACK_FRAME_OVERHEAD - lis r1,init_task_union@ha - addi r1,r1,init_task_union@l - addi r1,r1,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD - lis r24,StackOverflow@ha - addi r24,r24,StackOverflow@l - li r20,MSR_KERNEL - FIX_SRR1(r20,r22) - mtspr SRR0,r24 - mtspr SRR1,r20 - SYNC - RFI - -/* - * Disable FP for the task which had the FPU previously, + * This task wants to use the FPU now. + * On UP, disable FP for the task which had the FPU previously, * and save its floating-point registers in its thread_struct. - * Enables the FPU for use in the kernel on return. - * On SMP we know the fpu is free, since we give it up every - * switch. -- Cort + * Load up this task's FP registers from its thread_struct, + * enable the FPU for the current task and return to the task. */ load_up_fpu: mfmsr r5 @@ -830,14 +756,13 @@ * to another. Instead we call giveup_fpu in switch_to. */ #ifndef CONFIG_SMP - lis r6,0 /* get __pa constant */ - tophys(r6,r6) + tophys(r6,0) /* get __pa constant */ addis r3,r6,last_task_used_math@ha lwz r4,last_task_used_math@l(r3) cmpi 0,r4,0 beq 1f add r4,r4,r6 - addi r4,r4,THREAD /* want THREAD of last_task_used_math */ + addi r4,r4,THREAD /* want last_task_used_math->thread */ SAVE_32FPRS(0, r4) mffs fr0 stfd fr0,THREAD_FPSCR-4(r4) @@ -850,8 +775,10 @@ 1: #endif /* CONFIG_SMP */ /* enable use of FP after return */ - ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 mfspr r5,SPRG3 /* current task's THREAD (phys) */ + lwz r4,THREAD_FPEXC_MODE(r5) + ori r23,r23,MSR_FP /* enable FP for current */ + or r23,r23,r4 lfd fr0,THREAD_FPSCR-4(r5) mtfsf 0xff,fr0 REST_32FPRS(0, r5) @@ -875,7 +802,7 @@ lwz r21,GPR21(r21) SYNC RFI - + /* * FP unavailable trap from kernel - print a message, but let * the task use FP in the kernel until it returns to user mode. @@ -1020,7 +947,7 @@ #endif /* CONFIG_SMP */ blr #endif /* CONFIG_ALTIVEC */ - + /* * giveup_fpu(tsk) * Disable FP for the task given as the argument, @@ -1031,9 +958,10 @@ giveup_fpu: mfmsr r5 ori r5,r5,MSR_FP - SYNC + SYNC_601 + ISYNC_601 mtmsr r5 /* enable use of fpu now */ - SYNC + SYNC_601 isync cmpi 0,r3,0 beqlr- /* if no previous owner, done */ @@ -1172,62 +1100,6 @@ sync /* additional sync needed on g4 */ isync /* No speculative loading until now */ blr - -apus_interrupt_entry: - /* This is horrible, but there's no way around it. Enable the - * data cache so the IRQ hardware register can be accessed - * without cache intervention. Then disable interrupts and get - * the current emulated m68k IPL value. - */ - - mfmsr 20 - xori r20,r20,MSR_DR - SYNC - mtmsr r20 - isync - - lis r4,APUS_IPL_EMU@h - - li r20,(IPLEMU_SETRESET|IPLEMU_DISABLEINT) - stb r20,APUS_IPL_EMU@l(r4) - eieio - - lbz r3,APUS_IPL_EMU@l(r4) - - li r2,IPLEMU_IPLMASK - rlwinm. r20,r3,32-3,29,31 - bne 2f - mr r20,r2 /* lvl7! Need to reset state machine. */ - b 3f -2: cmp 0,r20,r2 - beq 1f -3: eieio - stb r2,APUS_IPL_EMU@l(r4) - ori r20,r20,IPLEMU_SETRESET - eieio - stb r20,APUS_IPL_EMU@l(r4) -1: eieio - li r20,IPLEMU_DISABLEINT - stb r20,APUS_IPL_EMU@l(r4) - - /* At this point we could do some magic to avoid the overhead - * of calling the C interrupt handler in case of a spurious - * interrupt. Could not get a simple hack to work though. - */ - - mfmsr r20 - xori r20,r20,MSR_DR - SYNC - mtmsr r20 - isync - - stw r3,(_CCR+4)(r21); - - addi r3,r1,STACK_FRAME_OVERHEAD; - li r20,MSR_KERNEL; - bl transfer_to_handler; - .long do_IRQ; - .long ret_from_except /*********************************************************************** * Please note that on APUS the exception handlers are located at the @@ -1247,10 +1119,9 @@ andc r4,r4,r3 mtspr HID0,r4 sync - bl prom_init + bl gemini_prom_init b __secondary_start #endif /* CONFIG_GEMINI */ - .globl __secondary_start_psurge __secondary_start_psurge: li r24,1 /* cpu # */ @@ -1287,15 +1158,15 @@ bl identify_cpu bl call_setup_cpu /* Call setup_cpu for this CPU */ - /* get current */ - lis r2,current_set@h - ori r2,r2,current_set@l - tophys(r2,r2) - slwi r24,r24,2 /* get current_set[cpu#] */ - lwzx r2,r2,r24 + /* get current_thread_info and current */ + lis r1,secondary_ti@ha + tophys(r1,r1) + lwz r1,secondary_ti@l(r1) + tophys(r2,r1) + lwz r2,TI_TASK(r2) /* stack */ - addi r1,r2,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD + addi r1,r1,THREAD_SIZE-STACK_FRAME_OVERHEAD li r0,0 tophys(r3,r1) stw r0,0(r3) @@ -1310,7 +1181,6 @@ mtspr SPRG3,r4 li r3,0 mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ - stw r3,PT_REGS(r4) /* set thread.regs to 0 for kernel thread */ /* enable MMU and jump to start_secondary */ li r4,MSR_KERNEL @@ -1347,12 +1217,22 @@ bl setup_750_7400_hid0 mtlr r4 blr +_GLOBAL(__setup_cpu_7410) + mflr r4 + bl setup_common_caches + bl setup_750_7400_hid0 + li r3,0 + mtspr SPRN_L2CR2,r3 + mtlr r4 + blr _GLOBAL(__setup_cpu_7450) + mflr r4 + bl setup_common_caches + bl setup_7450_hid0 + mtlr r4 blr _GLOBAL(__setup_cpu_power3) blr -_GLOBAL(__setup_cpu_power4) - blr _GLOBAL(__setup_cpu_generic) blr @@ -1407,6 +1287,47 @@ isync blr +/* 7450 + * Enable Store Gathering (SGE), Branch Folding (FOLD) + * Branch History Table (BHTE), Branch Target ICache (BTIC) + * Dynamic Power Management (DPM), Speculative (SPD) + * Ensure our data cache instructions really operate. + * Timebase has to be running or we wouldn't have made it here, + * just ensure we don't disable it. + * Clear Instruction cache throttling (ICTC) + */ +setup_7450_hid0: + /* We check for the presence of an L3 cache setup by + * the firmware. If any, we disable DOZE capability + */ + mfspr r11,SPRN_L3CR + andis. r11,r11,L3CR_L3E@h + beq 1f + li r7,CPU_FTR_CAN_DOZE + lwz r6,CPU_SPEC_FEATURES(r5) + andc r6,r6,r7 + stw r6,CPU_SPEC_FEATURES(r5) +1: + mfspr r11,HID0 + + /* All of the bits we have to set..... + */ + ori r11,r11,HID0_SGE | HID0_FOLD | HID0_BHTE | HID0_BTIC + oris r11,r11,HID0_DPM@h /* enable dynamic power mgmt */ + + /* All of the bits we have to clear.... + */ + li r3,HID0_SPD | HID0_NOPDST | HID0_NOPTI + andc r11,r11,r3 /* clear SPD: enable speculative */ + li r3,0 + + mtspr ICTC,r3 /* Instruction Cache Throttling off */ + isync + mtspr HID0,r11 + sync + isync + blr + /* * Load stuff into the MMU. Intended to be called with * IR=0 and DR=0. @@ -1417,7 +1338,7 @@ tophys(r6,r6) lwz r6,_SDR1@l(r6) mtspr SDR1,r6 -#ifdef CONFIG_PPC64BRIDGE +#ifdef CONFIG_PPC64BRIDGE /* clear the ASR so we only use the pseudo-segment registers. */ li r6,0 mtasr r6 @@ -1430,7 +1351,6 @@ addi r3,r3,0x111 /* increment VSID */ addis r4,r4,0x1000 /* address of next segment */ bdnz 3b -#ifndef CONFIG_POWER4 /* Load the BAT registers with the values set up by MMU_init. MMU_init takes care of whether we're on a 601 or not. */ mfpvr r3 @@ -1443,7 +1363,6 @@ LOAD_BAT(1,r3,r4,r5) LOAD_BAT(2,r3,r4,r5) LOAD_BAT(3,r3,r4,r5) -#endif /* CONFIG_POWER4 */ blr /* @@ -1456,8 +1375,8 @@ bl call_setup_cpu /* ptr to current */ - lis r2,init_task_union@h - ori r2,r2,init_task_union@l + lis r2,init_task@h + ori r2,r2,init_task@l /* Set up for using our exception vectors */ /* ptr to phys current thread */ tophys(r4,r2) @@ -1468,9 +1387,10 @@ mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ /* stack */ - addi r1,r2,TASK_UNION_SIZE + lis r1,init_thread_union@ha + addi r1,r1,init_thread_union@l li r0,0 - stwu r0,-STACK_FRAME_OVERHEAD(r1) + stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1) /* * Do early bootinfo parsing, platform-specific initialization, * and set up the MMU. @@ -1519,6 +1439,19 @@ TLBSYNC /* ... on all CPUs */ bl load_up_mmu + + /* Add helper information for the Abatron bdiGDB debugger. + * We do this here because we know the mmu is disabled, and + * will be enabled for real in just a few instructions. + */ + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + stw r5, 0xf0(r0) /* This much match your Abatron config */ + lis r6, swapper_pg_dir@h + ori r6, r6, swapper_pg_dir@l + tophys(r5, r5) + stw r6, 0(r5) + /* Now turn on the MMU for real! */ li r4,MSR_KERNEL FIX_SRR1(r4,r5) @@ -1538,6 +1471,15 @@ addis r3,r3,0x6000 /* Set Ks, Ku bits */ li r0,NUM_USER_SEGMENTS mtctr r0 + +#ifdef CONFIG_BDI_SWITCH + /* Context switch the PTE pointer for the Abatron BDI2000. + * The PGDIR is passed as second argument. + */ + lis r5, KERNELBASE@h + lwz r5, 0xf0(r5) + stw r4, 0x4(r5) +#endif li r4,0 3: #ifdef CONFIG_PPC64BRIDGE @@ -1561,22 +1503,21 @@ * -- Cort */ clear_bats: -#if !defined(CONFIG_GEMINI) li r20,0 mfspr r9,PVR rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ cmpwi r9, 1 beq 1f - + mtspr DBAT0U,r20 - mtspr DBAT0L,r20 + mtspr DBAT0L,r20 mtspr DBAT1U,r20 mtspr DBAT1L,r20 mtspr DBAT2U,r20 - mtspr DBAT2L,r20 + mtspr DBAT2L,r20 mtspr DBAT3U,r20 mtspr DBAT3L,r20 -1: +1: mtspr IBAT0U,r20 mtspr IBAT0L,r20 mtspr IBAT1U,r20 @@ -1585,10 +1526,8 @@ mtspr IBAT2L,r20 mtspr IBAT3U,r20 mtspr IBAT3L,r20 -#endif /* !defined(CONFIG_GEMINI) */ blr -#ifndef CONFIG_GEMINI flush_tlbs: lis r20, 0x40 1: addic. r20, r20, -0x1000 @@ -1607,9 +1546,7 @@ mtspr SRR1,r3 sync RFI -#endif -#ifndef CONFIG_POWER4 /* * Use the first pair of BAT registers to map the 1st 16MB * of RAM to KERNELBASE. From this point on we can't safely @@ -1645,7 +1582,7 @@ #else ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */ #endif /* CONFIG_APUS */ - + #ifdef CONFIG_PPC64BRIDGE /* clear out the high 32 bits in the BAT */ clrldi r11,r11,32 @@ -1682,7 +1619,6 @@ blr #endif /* !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) */ -#endif /* CONFIG_POWER4 */ #ifdef CONFIG_8260 /* Jump into the system reset for the rom. @@ -1734,12 +1670,12 @@ .globl swapper_pg_dir swapper_pg_dir: - .space 4096 + .space 4096 /* * This space gets a copy of optional info passed to us by the bootstrap * Used to pass parameters into the kernel like root=/dev/sda1, etc. - */ + */ .globl cmd_line cmd_line: .space 512 @@ -1752,3 +1688,9 @@ .long 0, 0, 0, 0, 0, 0, 0, 0 .long 0, 0, 0, 0, 0, 0, 0, 0 .long 0, 0, 0, 0, 0, 0, 0, 0 + +/* Room for two PTE pointers, usually the kernel and current user pointers + * to their respective root page table. + */ +abatron_pteptrs: + .space 8 diff -Nru a/arch/ppc/kernel/head_4xx.S b/arch/ppc/kernel/head_4xx.S --- a/arch/ppc/kernel/head_4xx.S Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/head_4xx.S Tue Feb 19 18:08:59 2002 @@ -1,7 +1,4 @@ /* - * BK Id: SCCS/s.head_4xx.S 1.6 05/21/01 11:50:00 paulus - */ -/* * Copyright (c) 1995-1996 Gary Thomas * Initial PowerPC version. * Copyright (c) 1996 Cort Dougan @@ -14,6 +11,13 @@ * PowerPC 403GCX modifications. * Copyright (c) 1999 Grant Erickson * PowerPC 403GCX/405GP modifications. + * Copyright 2000 MontaVista Software Inc. + * PPC405 modifications + * PowerPC 403GCX/405GP modifications. + * Author: MontaVista Software, Inc. + * frank_rowand@mvista.com or source@mvista.com + * debbie_chu@mvista.com + * * * Module name: head_4xx.S * @@ -28,139 +32,92 @@ */ #include - #include #include -#include #include - -#include "ppc_asm.h" - +#include +#include +#include +#include +#include +#include "ppc_defs.h" /* Preprocessor Defines */ #define STND_EXC 0 #define CRIT_EXC 1 -### -### Check to make sure the right processor has been defined. -### - -#if !defined(CONFIG_4xx) -#error "This file is only appropriate for kernels supporting the PPC4xx." -#endif - -### -### Execution entry point. -### - -### -### As with the other PowerPC ports, it is expected that when code -### execution begins here, the following registers contain valid, yet -### optional, information: -### -### r3 - Board info structure pointer (DRAM, frequency, MAC address, etc.) -### r4 - Starting address of the init RAM disk -### r5 - Ending address of the init RAM disk -### r6 - Start of kernel command line string (e.g. "mem=96m") -### r7 - End of kernel command line string -### - +/* As with the other PowerPC ports, it is expected that when code + * execution begins here, the following registers contain valid, yet + * optional, information: + * + * r3 - Board info structure pointer (DRAM, frequency, MAC address, etc.) + * r4 - Starting address of the init RAM disk + * r5 - Ending address of the init RAM disk + * r6 - Start of kernel command line string (e.g. "mem=96m") + * r7 - End of kernel command line string + * + * This is all going to change RSN when we add bi_recs....... -- Dan + */ .text _GLOBAL(_stext) _GLOBAL(_start) - ## Save residual data, init RAM disk, and command line parameters - + + /* Save parameters we are passed. + */ mr r31,r3 mr r30,r4 mr r29,r5 mr r28,r6 mr r27,r7 + li r24,0 /* CPU number */ - ## Set the ID for this CPU - - li r24,0 - - ## Invalidate all TLB entries - - tlbia - - ## We should still be executing code at physical address 0x0000xxxx - ## at this point. However, start_here is at virtual address - ## 0xC000xxxx. So, set up a TLB mapping to cover this once - ## translation is enabled. - - lis r3,KERNELBASE@h # Load the kernel virtual address - ori r3,r3,KERNELBASE@l - tophys(r4,r3) # Load the kernel physical address + /* We have to turn on the MMU right away so we get cache modes + * set correctly. + */ + bl initial_mmu - ## Save the existing PID and load the kernel PID. - - mfspr r7,SPRN_PID # Save the old PID - li r0,0 - mtspr SPRN_PID,r0 # Load the kernel PID - - ## Configure and load entry into TLB slot 0. - - clrrwi r4,r4,10 # Mask off the real page number - ori r4,r4,(TLB_WR | TLB_EX) # Set the write and execute bits - - clrrwi r3,r3,10 # Mask off the effective page number - ori r3,r3,(TLB_VALID | TLB_PAGESZ(PAGESZ_16M)) - - tlbwe r4,r0,TLB_DATA # Load the data portion of the entry - tlbwe r3,r0,TLB_TAG # Load the tag portion of the entry - isync - - mtspr SPRN_PID,r7 # Restore the existing PID - - ## Establish the exception vector base - - lis r4,KERNELBASE@h # EVPR only uses the high 16-bits - tophys(r0,r4) # Use the physical address - mtspr SPRN_EVPR,r0 - - ## Enable the MMU and jump to the main PowerPC kernel start-up code +/* We now have the lower 16 Meg mapped into TLB entries, and the caches + * ready to work. + */ +turn_on_mmu: + li r0,MSR_KERNEL + mtspr SRR1,r0 + lis r0,start_here@h + ori r0,r0,start_here@l + mtspr SRR0,r0 + SYNC + rfi /* enables MMU */ - mfmsr r0 # Get the machine state register - ori r0,r0,(MSR_DR | MSR_IR) # Enable data and instr. translation - mtspr SPRN_SRR1,r0 # Set up the new machine state register - lis r0,start_here@h - ori r0,r0,start_here@l - mtspr SPRN_SRR0,r0 # Set up the new instruction pointer - rfi # Jump to start_here w/ translation on - - -### -### Exception vector entry code. This code runs with address translation -### turned off (i.e. using physical addresses). We assume SPRG3 has the -### physical address of the current task thread_struct. -### - - ## Common exception code for all exception types. - -#define COMMON_PROLOG \ -0: mtspr SPRN_SPRG0,r20; /* We need r20, move it to SPRG0 */\ - mtspr SPRN_SPRG1,r21; /* We need r21, move it to SPRG1 */\ - mfcr r20; /* We need the CR, move it to r20 */\ - mfspr r21,SPRN_SPRG2; /* Exception stack to use */\ - cmpwi cr0,r21,0; /* From user mode or RTAS? */\ - bne 1f; /* Not RTAS, branch */\ - tophys(r21, r1); /* Convert vka in r1 to pka in r21 */\ - subi r21,r21,INT_FRAME_SIZE; /* Allocate an exception frame */\ -1: stw r20,_CCR(r21); /* Save CR on the stack */\ - stw r22,GPR22(r21); /* Save r22 on the stack */\ - stw r23,GPR23(r21); /* r23 Save on the stack */\ - mfspr r20,SPRN_SPRG0; /* Get r20 back out of SPRG0 */\ - stw r20,GPR20(r21); /* Save r20 on the stack */\ - mfspr r22,SPRN_SPRG1; /* Get r21 back out of SPRG0 */\ - stw r22,GPR21(r21); /* Save r21 on the stack */\ - mflr r20; \ - stw r20,_LINK(r21); /* Save LR on the stack */\ - mfctr r22; \ - stw r22,_CTR(r21); /* Save CTR on the stack */\ - mfspr r20,XER; \ - stw r20,_XER(r21); /* Save XER on the stack */ +/* Exception vector entry code. This code runs with address translation + * turned off (i.e. using physical addresses). We assume SPRG3 has the + * physical address of the current task thread_struct. + */ + +#define COMMON_PROLOG(n) \ +0: mtspr SPRN_SPRG0,r20; /* We need r20, move it to SPRG0 */\ + mtspr SPRN_SPRG1,r21; /* We need r21, move it to SPRG1 */\ + mfcr r20; /* We need the CR, move it to r20 */\ + mfspr r21,SPRN_SPRG2; /* Exception stack to use */\ + cmpwi cr0,r21,0; /* From user mode or RTAS? */\ + bne 1f; /* Not RTAS, branch */\ + tophys(r21, r1); /* Convert vka in r1 to pka in r21 */\ + subi r21,r21,INT_FRAME_SIZE; /* Allocate an exception frame */\ +1: stw r20,_CCR(r21); /* Save CR on the stack */\ + stw r22,GPR22(r21); /* Save r22 on the stack */\ + stw r23,GPR23(r21); /* r23 Save on the stack */\ + mfspr r20,SPRN_SPRG0; /* Get r20 back out of SPRG0 */\ + stw r20,GPR20(r21); /* Save r20 on the stack */\ + mfspr r22,SPRN_SPRG1; /* Get r21 back out of SPRG0 */\ + stw r22,GPR21(r21); /* Save r21 on the stack */\ + mflr r20; \ + stw r20,_LINK(r21); /* Save LR on the stack */\ + mfctr r22; \ + stw r22,_CTR(r21); /* Save CTR on the stack */\ + mfspr r20,XER; \ + stw r20,_XER(r21); /* Save XER on the stack */\ + mfspr r20,SPRN_DBCR0; \ + stw r20,_DBCR0(r21); /* Save Debug Control on the stack */ #define COMMON_EPILOG \ stw r0,GPR0(r21); /* Save r0 on the stack */\ @@ -171,25 +128,22 @@ SAVE_4GPRS(3, r21); /* Save r3 through r6 on the stack */\ SAVE_GPR(7, r21); /* Save r7 on the stack */ - ## Common exception code for standard (non-critical) exceptions. - -#define STND_EXCEPTION_PROLOG \ - COMMON_PROLOG; \ +#define STND_EXCEPTION_PROLOG(n) \ + COMMON_PROLOG(n); \ mfspr r22,SPRN_SRR0; /* Faulting instruction address */\ + lis r20,MSR_WE@h; \ mfspr r23,SPRN_SRR1; /* MSR at the time of fault */\ + andc r23,r23,r20; /* disable processor wait state */\ COMMON_EPILOG; - ## Common exception code for critical exceptions. - -#define CRIT_EXCEPTION_PROLOG \ - COMMON_PROLOG; \ +#define CRIT_EXCEPTION_PROLOG(n) \ + COMMON_PROLOG(n); \ mfspr r22,SPRN_SRR2; /* Faulting instruction address */\ + lis r20,MSR_WE@h; \ mfspr r23,SPRN_SRR3; /* MSR at the time of fault */\ + andc r23,r23,r20; /* disable processor wait state */\ COMMON_EPILOG; -### -### Macros for specific exception types -### #define START_EXCEPTION(n, label) \ . = n; \ @@ -200,68 +154,213 @@ bl transfer_to_handler; \ .long func; \ .long ret_from_except - - + + #define STND_EXCEPTION(n, label, func) \ START_EXCEPTION(n, label); \ - STND_EXCEPTION_PROLOG; \ + STND_EXCEPTION_PROLOG(n); \ addi r3,r1,STACK_FRAME_OVERHEAD; \ li r7,STND_EXC; \ li r20,MSR_KERNEL; \ FINISH_EXCEPTION(func) - + #define CRIT_EXCEPTION(n, label, func) \ START_EXCEPTION(n, label); \ - CRIT_EXCEPTION_PROLOG; \ + CRIT_EXCEPTION_PROLOG(n); \ addi r3,r1,STACK_FRAME_OVERHEAD; \ li r7,CRIT_EXC; \ li r20,MSR_KERNEL; \ FINISH_EXCEPTION(func) - -### -### Exception vectors. -### - -### 0x0100 - Critical Interrupt Exception +/* Exception vectors. +*/ + +/* 0x0100 - Critical Interrupt Exception +*/ CRIT_EXCEPTION(0x0100, CriticalInterrupt, UnknownException) -### 0x0200 - Machine Check Exception - +/* 0x0200 - Machine Check Exception +*/ +#if 0 CRIT_EXCEPTION(0x0200, MachineCheck, MachineCheckException) +#else + START_EXCEPTION(0x0200, MachineCheck) + CRIT_EXCEPTION_PROLOG(0x0200) + + /* + lis r4,0x0400 + mtdcr DCRN_POB0_BESR0,r4 + */ +#ifdef DCRN_POB0_BEAR + mfdcr r4,DCRN_POB0_BEAR + mfdcr r4,DCRN_POB0_BESR0 + mfdcr r4,DCRN_POB0_BESR1 +#endif -### 0x0300 - Data Storage Exception +#ifdef DCRN_PLB0_BEAR + mfdcr r4,DCRN_PLB0_ACR + mfdcr r4,DCRN_PLB0_BEAR + mfdcr r4,DCRN_PLB0_BESR +#endif - START_EXCEPTION(0x0300, DataAccess) - STND_EXCEPTION_PROLOG - mfspr r5,SPRN_ESR # Grab the ESR, save it, pass as arg3 - stw r5,_ESR(r21) - mfspr r4,SPRN_DEAR # Grab the DEAR, save it, pass as arg2 - stw r4,_DEAR(r21) addi r3,r1,STACK_FRAME_OVERHEAD - li r7,STND_EXC # This is a standard exception + li r7,CRIT_EXC li r20,MSR_KERNEL - rlwimi r20,r23,0,16,16 # Copy EE bit from the saved MSR - FINISH_EXCEPTION(do_page_fault) # do_page_fault(regs, ESR, DEAR) - -### 0x0400 - Instruction Storage Exception + FINISH_EXCEPTION(MachineCheckException) +#endif +/* 0x0300 - Data Storage Exception + * This happens for just a few reasons. U0 set (but we don't do that), + * or zone protection fault (user violation, write to protected page). + * If this is just an update of modified status, we do that quickly + * and exit. Otherwise, we call heavywight functions to do the work. + */ + START_EXCEPTION(0x0300, DataStore) + mtspr SPRG0, r20 /* Save some working registers */ + mtspr SPRG1, r21 +#ifdef CONFIG_403GCX + stw r22, 0(r0) + stw r23, 4(r0) + mfcr r21 + mfspr r22, SPRN_PID + stw r21, 8(r0) + stw r22, 12(r0) +#else + mtspr SPRG4, r22 + mtspr SPRG5, r23 + mfcr r21 + mfspr r22, SPRN_PID + mtspr SPRG7, r21 + mtspr SPRG6, r22 +#endif + + /* First, check if it was a zone fault (which means a user + * tried to access a kernel or read-protected page - always + * a SEGV). All other faults here must be stores, so no + * need to check ESR_DST as well. */ + mfspr r20, SPRN_ESR + andis. r20, r20, ESR_DIZ@h + bne 2f + + mfspr r20, SPRN_DEAR /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r21, r20, 0x8000 + beq 3f + lis r21, swapper_pg_dir@h + ori r21, r21, swapper_pg_dir@l + li r23, 0 + mtspr SPRN_PID, r23 /* TLB will have 0 TID */ + b 4f + + /* Get the PGD for the current thread. + */ +3: + mfspr r21,SPRG3 + lwz r21,PGDIR(r21) +4: + tophys(r21, r21) + rlwimi r21, r20, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ + lwz r21, 0(r21) /* Get L1 entry */ + rlwinm. r22, r21, 0, 0, 19 /* Extract L2 (pte) base address */ + beq 2f /* Bail if no table */ + + tophys(r22, r22) + rlwimi r22, r20, 22, 20, 29 /* Compute PTE address */ + lwz r21, 0(r22) /* Get Linux PTE */ + + andi. r23, r21, _PAGE_RW /* Is it writeable? */ + beq 2f /* Bail if not */ + + /* Update 'changed'. + */ + ori r21, r21, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE + stw r21, 0(r22) /* Update Linux page table */ + + /* Most of the Linux PTE is ready to load into the TLB LO. + * We set ZSEL, where only the LS-bit determines user access. + * We set execute, because we don't have the granularity to + * properly set this at the page level (Linux problem). + * If shared is set, we cause a zero PID->TID load. + * Many of these bits are software only. Bits we don't set + * here we (properly should) assume have the appropriate value. + */ + li r22, 0x0ce2 + andc r21, r21, r22 /* Make sure 20, 21 are zero */ + + /* find the TLB index that caused the fault. It has to be here. + */ + tlbsx r23, 0, r20 + + tlbwe r21, r23, TLB_DATA /* Load TLB LO */ + + /* Done...restore registers and get out of here. + */ +#ifdef CONFIG_403GCX + lwz r22, 12(r0) + lwz r21, 8(r0) + mtspr SPRN_PID, r22 + mtcr r21 + lwz r23, 4(r0) + lwz r22, 0(r0) +#else + mfspr r22, SPRG6 + mfspr r21, SPRG7 + mtspr SPRN_PID, r22 + mtcr r21 + mfspr r23, SPRG5 + mfspr r22, SPRG4 +#endif + mfspr r21, SPRG1 + mfspr r20, SPRG0 + PPC405_ERR77_SYNC + rfi /* Should sync shadow TLBs */ + +2: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ +#ifdef CONFIG_403GCX + lwz r22, 12(r0) + lwz r21, 8(r0) + mtspr SPRN_PID, r22 + mtcr r21 + lwz r23, 4(r0) + lwz r22, 0(r0) +#else + mfspr r22, SPRG6 + mfspr r21, SPRG7 + mtspr SPRN_PID, r22 + mtcr r21 + mfspr r23, SPRG5 + mfspr r22, SPRG4 +#endif + mfspr r21, SPRG1 + mfspr r20, SPRG0 + b DataAccess + +/* 0x0400 - Instruction Storage Exception + * I don't know why it is called "Storage"....This is caused by a fetch + * from non-execute or guarded pages. + */ START_EXCEPTION(0x0400, InstructionAccess) - STND_EXCEPTION_PROLOG - mr r4,r22 # Pass SRR0 as arg2 - mr r5,r23 # Pass SRR1 as arg3 + STND_EXCEPTION_PROLOG(0x0400) + mr r4,r22 /* Pass SRR0 as arg2 */ + li r5,0 /* Pass zero as arg3 */ addi r3,r1,STACK_FRAME_OVERHEAD - li r7,STND_EXC # This is a standard exception + li r7,STND_EXC li r20,MSR_KERNEL - rlwimi r20,r23,0,16,16 # Copy EE bit from the saved MSR - FINISH_EXCEPTION(do_page_fault) # do_page_fault(regs, SRR0, SRR1) - -### 0x0500 - External Interrupt Exception + rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ + FINISH_EXCEPTION(do_page_fault) /* do_page_fault(regs, SRR0, SRR1) */ +/* 0x0500 - External Interrupt Exception +*/ START_EXCEPTION(0x0500, HardwareInterrupt) - STND_EXCEPTION_PROLOG + STND_EXCEPTION_PROLOG(0x0500) addi r3,r1,STACK_FRAME_OVERHEAD li r7,STND_EXC li r20,MSR_KERNEL @@ -271,54 +370,70 @@ .long do_IRQ .long ret_from_intercept -### 0x0600 - Alignment Exception - +/* 0x0600 - Alignment Exception +*/ START_EXCEPTION(0x0600, Alignment) - STND_EXCEPTION_PROLOG - mfspr r4,SPRN_DEAR # Grab the DEAR and save it + STND_EXCEPTION_PROLOG(0x0600) + mfspr r4,SPRN_DEAR /* Grab the DEAR and save it */ stw r4,_DEAR(r21) addi r3,r1,STACK_FRAME_OVERHEAD - li r7,STND_EXC # This is a standard exception + li r7,STND_EXC li r20,MSR_KERNEL - rlwimi r20,r23,0,16,16 # Copy EE bit from the saved MSR + rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ FINISH_EXCEPTION(AlignmentException) -### 0x0700 - Program Exception - +/* 0x0700 - Program Exception +*/ START_EXCEPTION(0x0700, ProgramCheck) - STND_EXCEPTION_PROLOG + STND_EXCEPTION_PROLOG(0x0700) addi r3,r1,STACK_FRAME_OVERHEAD - li r7,STND_EXC # This is a standard exception + li r7,STND_EXC li r20,MSR_KERNEL - rlwimi r20,r23,0,16,16 # Copy EE bit from the saved MSR + rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ FINISH_EXCEPTION(ProgramCheckException) - - STND_EXCEPTION(0x0800, Trap_08, UnknownException) + + +/* I'm stealing this unused vector location to build a standard exception + * frame for Data TLB Access errors. The other Data TLB exceptions will bail + * out to this point if they can't resolve the lightweight TLB fault. + */ + START_EXCEPTION(0x0800, DataAccess) + STND_EXCEPTION_PROLOG(0x0800) + mfspr r5,SPRN_ESR /* Grab the ESR, save it, pass arg3 */ + stw r5,_ESR(r21) + mfspr r4,SPRN_DEAR /* Grab the DEAR, save it, pass arg2 */ + stw r4,_DEAR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r7,STND_EXC + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ + FINISH_EXCEPTION(do_page_fault) /* do_page_fault(regs, ESR, DEAR) */ + STND_EXCEPTION(0x0900, Trap_09, UnknownException) STND_EXCEPTION(0x0A00, Trap_0A, UnknownException) - STND_EXCEPTION(0x0B00, Trap_0B, UnknownException) -### 0x0C00 - System Call Exception - + STND_EXCEPTION(0x0B00, Trap_0B, UnknownException) +/* 0x0C00 - System Call Exception +*/ START_EXCEPTION(0x0C00, SystemCall) - STND_EXCEPTION_PROLOG + STND_EXCEPTION_PROLOG(0x0C00) stw r3,ORIG_GPR3(r21) - li r7,STND_EXC # This is a standard exception + li r7,STND_EXC li r20,MSR_KERNEL - rlwimi r20,r23,0,16,16 # Copy EE bit from the saved MSR + rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ FINISH_EXCEPTION(DoSyscall) STND_EXCEPTION(0x0D00, Trap_0D, UnknownException) STND_EXCEPTION(0x0E00, Trap_0E, UnknownException) STND_EXCEPTION(0x0F00, Trap_0F, UnknownException) -### 0x1000 - Programmable Interval Timer (PIT) Exception - +/* 0x1000 - Programmable Interval Timer (PIT) Exception +*/ START_EXCEPTION(0x1000, Decrementer) - STND_EXCEPTION_PROLOG - lis r0,TSR_PIS@h # Set-up the PIT exception mask - mtspr SPRN_TSR,r0 # Clear the PIT exception + STND_EXCEPTION_PROLOG(0x1000) + lis r0,TSR_PIS@h /* Set-up the PIT exception mask */ + mtspr SPRN_TSR,r0 /* Clear the PIT exception */ addi r3,r1,STACK_FRAME_OVERHEAD - li r7,STND_EXC # This is a standard exception + li r7,STND_EXC li r20,MSR_KERNEL bl transfer_to_handler _GLOBAL(timer_interrupt_intercept) @@ -326,28 +441,239 @@ .long ret_from_intercept #if 0 -### 0x1010 - Fixed Interval Timer (FIT) Exception - +/* NOTE: + * FIT and WDT handlers are not implemented yet. + */ + +/* 0x1010 - Fixed Interval Timer (FIT) Exception +*/ STND_EXCEPTION(0x1010, FITException, UnknownException) -### 0x1020 - Watchdog Timer (WDT) Exception +/* 0x1020 - Watchdog Timer (WDT) Exception +*/ CRIT_EXCEPTION(0x1020, WDTException, UnknownException) #endif -### 0x1100 - Data TLB Miss Exception +/* 0x1100 - Data TLB Miss Exception + * As the name implies, translation is not in the MMU, so search the + * page tables and fix it. The only purpose of this function is to + * load TLB entries from the page table if they exist. + */ + START_EXCEPTION(0x1100, DTLBMiss) + mtspr SPRG0, r20 /* Save some working registers */ + mtspr SPRG1, r21 +#ifdef CONFIG_403GCX + stw r22, 0(r0) + stw r23, 4(r0) + mfcr r21 + mfspr r22, SPRN_PID + stw r21, 8(r0) + stw r22, 12(r0) +#else + mtspr SPRG4, r22 + mtspr SPRG5, r23 + mfcr r21 + mfspr r22, SPRN_PID + mtspr SPRG7, r21 + mtspr SPRG6, r22 +#endif + mfspr r20, SPRN_DEAR /* Get faulting address */ - STND_EXCEPTION(0x1100, DTLBMiss, PPC4xx_dtlb_miss) + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r21, r20, 0x8000 + beq 3f + lis r21, swapper_pg_dir@h + ori r21, r21, swapper_pg_dir@l + li r23, 0 + mtspr SPRN_PID, r23 /* TLB will have 0 TID */ + b 4f + + /* Get the PGD for the current thread. + */ +3: + mfspr r21,SPRG3 + lwz r21,PGDIR(r21) +4: + tophys(r21, r21) + rlwimi r21, r20, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ + lwz r21, 0(r21) /* Get L1 entry */ + rlwinm. r22, r21, 0, 0, 19 /* Extract L2 (pte) base address */ + beq 2f /* Bail if no table */ + + tophys(r22, r22) + rlwimi r22, r20, 22, 20, 29 /* Compute PTE address */ + lwz r21, 0(r22) /* Get Linux PTE */ + andi. r23, r21, _PAGE_PRESENT + beq 2f -### 0x1200 - Instruction TLB Miss Exception + ori r21, r21, _PAGE_ACCESSED + stw r21, 0(r22) - STND_EXCEPTION(0x1200, ITLBMiss, PPC4xx_itlb_miss) + /* Most of the Linux PTE is ready to load into the TLB LO. + * We set ZSEL, where only the LS-bit determines user access. + * We set execute, because we don't have the granularity to + * properly set this at the page level (Linux problem). + * If shared is set, we cause a zero PID->TID load. + * Many of these bits are software only. Bits we don't set + * here we (properly should) assume have the appropriate value. + */ + li r22, 0x0ce2 + andc r21, r21, r22 /* Make sure 20, 21 are zero */ + + b finish_tlb_load + + +2: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ +#ifdef CONFIG_403GCX + lwz r22, 12(r0) + lwz r21, 8(r0) + mtspr SPRN_PID, r22 + mtcr r21 + lwz r23, 4(r0) + lwz r22, 0(r0) +#else + mfspr r22, SPRG6 + mfspr r21, SPRG7 + mtspr SPRN_PID, r22 + mtcr r21 + mfspr r23, SPRG5 + mfspr r22, SPRG4 +#endif + mfspr r21, SPRG1 + mfspr r20, SPRG0 + b DataAccess + +/* 0x1200 - Instruction TLB Miss Exception + * Nearly the same as above, except we get our information from different + * registers and bailout to a different point. + */ + START_EXCEPTION(0x1200, ITLBMiss) + mtspr SPRG0, r20 /* Save some working registers */ + mtspr SPRG1, r21 +#ifdef CONFIG_403GCX + stw r22, 0(r0) + stw r23, 4(r0) + mfcr r21 + mfspr r22, SPRN_PID + stw r21, 8(r0) + stw r22, 12(r0) +#else + mtspr SPRG4, r22 + mtspr SPRG5, r23 + mfcr r21 + mfspr r22, SPRN_PID + mtspr SPRG7, r21 + mtspr SPRG6, r22 +#endif + mfspr r20, SRR0 /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r21, r20, 0x8000 + beq 3f + lis r21, swapper_pg_dir@h + ori r21, r21, swapper_pg_dir@l + li r23, 0 + mtspr SPRN_PID, r23 /* TLB will have 0 TID */ + b 4f + + /* Get the PGD for the current thread. + */ +3: + mfspr r21,SPRG3 + lwz r21,PGDIR(r21) +4: + tophys(r21, r21) + rlwimi r21, r20, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ + lwz r21, 0(r21) /* Get L1 entry */ + rlwinm. r22, r21, 0, 0, 19 /* Extract L2 (pte) base address */ + beq 2f /* Bail if no table */ + + tophys(r22, r22) + rlwimi r22, r20, 22, 20, 29 /* Compute PTE address */ + lwz r21, 0(r22) /* Get Linux PTE */ + andi. r23, r21, _PAGE_PRESENT + beq 2f + + ori r21, r21, _PAGE_ACCESSED + stw r21, 0(r22) + + /* Most of the Linux PTE is ready to load into the TLB LO. + * We set ZSEL, where only the LS-bit determines user access. + * We set execute, because we don't have the granularity to + * properly set this at the page level (Linux problem). + * If shared is set, we cause a zero PID->TID load. + * Many of these bits are software only. Bits we don't set + * here we (properly should) assume have the appropriate value. + */ + li r22, 0x0ce2 + andc r21, r21, r22 /* Make sure 20, 21 are zero */ + + b finish_tlb_load + + /* Done...restore registers and get out of here. + */ +#ifdef CONFIG_403GCX + lwz r22, 12(r0) + lwz r21, 8(r0) + mtspr SPRN_PID, r22 + mtcr r21 + lwz r23, 4(r0) + lwz r22, 0(r0) +#else + mfspr r22, SPRG6 + mfspr r21, SPRG7 + mtspr SPRN_PID, r22 + mtcr r21 + mfspr r23, SPRG5 + mfspr r22, SPRG4 +#endif + mfspr r21, SPRG1 + mfspr r20, SPRG0 + PPC405_ERR77_SYNC + rfi /* Should sync shadow TLBs */ + +2: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ +#ifdef CONFIG_403GCX + lwz r22, 12(r0) + lwz r21, 8(r0) + mtspr SPRN_PID, r22 + mtcr r21 + lwz r23, 4(r0) + lwz r22, 0(r0) +#else + mfspr r22, SPRG6 + mfspr r21, SPRG7 + mtspr SPRN_PID, r22 + mtcr r21 + mfspr r23, SPRG5 + mfspr r22, SPRG4 +#endif + mfspr r21, SPRG1 + mfspr r20, SPRG0 + b InstructionAccess STND_EXCEPTION(0x1300, Trap_13, UnknownException) STND_EXCEPTION(0x1400, Trap_14, UnknownException) STND_EXCEPTION(0x1500, Trap_15, UnknownException) STND_EXCEPTION(0x1600, Trap_16, UnknownException) +#ifdef CONFIG_IBM405_ERR51 + /* 405GP errata 51 */ + START_EXCEPTION(0x1700, Trap_17) + b DTLBMiss +#else STND_EXCEPTION(0x1700, Trap_17, UnknownException) +#endif STND_EXCEPTION(0x1800, Trap_18, UnknownException) STND_EXCEPTION(0x1900, Trap_19, UnknownException) STND_EXCEPTION(0x1A00, Trap_1A, UnknownException) @@ -356,219 +682,338 @@ STND_EXCEPTION(0x1D00, Trap_1D, UnknownException) STND_EXCEPTION(0x1E00, Trap_1E, UnknownException) STND_EXCEPTION(0x1F00, Trap_1F, UnknownException) - -### 0x2000 - Debug Exception - CRIT_EXCEPTION(0x2000, DebugTrap, UnknownException) - -### -### Other PowerPC processors, namely those derived from the 6xx-series -### have vectors from 0x2100 through 0x2F00 defined, but marked as reserved. -### However, for the 4xx-series processors these are neither defined nor -### reserved. -### - -### -### This code finishes saving the registers to the exception frame -### and jumps to the appropriate handler for the exception, turning -### on address translation. -### - -_GLOBAL(transfer_to_handler) - stw r22,_NIP(r21) # Save the faulting IP on the stack - stw r23,_MSR(r21) # Save the exception MSR on the stack - SAVE_4GPRS(8, r21) # Save r8 through r11 on the stack - SAVE_8GPRS(12, r21) # Save r12 through r19 on the stack - SAVE_8GPRS(24, r21) # Save r24 through r31 on the stack - andi. r23,r23,MSR_PR # Is this from user space? - mfspr r23,SPRN_SPRG3 # If from user, fix up THREAD.regs - beq 2f # No, it is from the kernel; branch. - addi r24,r1,STACK_FRAME_OVERHEAD - stw r24,PT_REGS(r23) # -2: addi r2,r23,-THREAD # Set r2 to current thread - tovirt(r2,r2) - mflr r23 - andi. r24,r23,0x3f00 # Get vector offset - stw r24,TRAP(r21) - li r22,0 - stw r22,RESULT(r21) - mtspr SPRN_SPRG2,r22 # r1 is now the kernel stack pointer - addi r24,r2,TASK_STRUCT_SIZE # Check for kernel stack overflow - cmplw cr0,r1,r2 - cmplw cr1,r1,r24 - crand cr1,cr1,cr4 - bgt- stack_ovf # If r2 < r1 < r2 + TASK_STRUCT_SIZE - lwz r24,0(r23) # Virtual address of the handler - lwz r23,4(r23) # Handler return pointer - cmpwi cr0,r7,STND_EXC # What type of exception is this? - bne 3f # It is a critical exception... - - ## Standard exception jump path - - mtspr SPRN_SRR0,r24 # Set up the instruction pointer - mtspr SPRN_SRR1,r20 # Set up the machine state register - mtlr r23 # Set up the return pointer - SYNC - rfi # Enable the MMU, jump to the handler +/* 0x2000 - Debug Exception +*/ + START_EXCEPTION(0x2000, DebugTrap) + b check_single_step_in_exception +ret_to_debug_exception: + CRIT_EXCEPTION_PROLOG(0x2000) + addi r3,r1,STACK_FRAME_OVERHEAD + li r7,CRIT_EXC; + li r20,MSR_KERNEL + FINISH_EXCEPTION(DebugException) - ## Critical exception jump path +/* Make sure the final interrupt handler has not spilled past the + * end of its allotted space. + */ + .=0x2100 -3: mtspr SPRN_SRR2,r24 # Set up the instruction pointer - mtspr SPRN_SRR3,r20 # Set up the machine state register - mtlr r23 # Set up the return pointer - SYNC - rfci # Enable the MMU, jump to the handler +/* Check for a single step debug exception while in an exception + * handler before state has been saved. This is to catch the case + * where an instruction that we are trying to single step causes + * an exception (eg ITLB miss) and thus the first instruction of + * the exception handler generates a single step debug exception. + * + * If we get a debug trap on the first instruction of an exception handler, + * we reset the MSR_DE in the _exception handlers_ MSR (the debug trap is + * a critical exception, so we are using SPRN_SRR3 to manipulate the MSR). + * The exception handler was handling a non-critical interrupt, so it will + * save (and later restore) the MSR via SPRN_SRR1, which will still have + * the MSR_DE bit set. + */ +check_single_step_in_exception: -### -### On kernel stack overlow, load up an initial stack pointer and call -### StackOverflow(regs), which should NOT return. -### + /* This first instruction was already executed by the exception + * handler and must be the first instruction of every exception + * handler. + */ + mtspr SPRN_SPRG0,r20 /* Save some working registers... */ + mtspr SPRN_SPRG1,r21 + mfcr r20 /* ..and the cr because we change it */ + + mfspr r21,SPRN_SRR3 /* MSR at the time of fault */ + andi. r21,r21,MSR_PR + bne+ 2f /* trapped from problem state */ + + mfspr r21,SPRN_SRR2 /* Faulting instruction address */ + cmplwi r21,0x2100 + bgt+ 2f /* address above exception vectors */ + + lis r21,DBSR_IC@h /* Remove the trap status */ + mtspr SPRN_DBSR,r21 + + mfspr r21,SPRN_SRR3 + rlwinm r21,r21,0,23,21 /* clear MSR_DE */ + mtspr SPRN_SRR3, r21 /* restore MSR at rcfi without DE */ + + mtcrf 0xff,r20 /* restore registers */ + mfspr r21,SPRN_SPRG1 + mfspr r20,SPRN_SPRG0 + + sync + rfci /* return to the exception handler */ + +2: + mtcrf 0xff,r20 /* restore registers */ + mfspr r21,SPRN_SPRG1 + mfspr r20,SPRN_SPRG0 + b ret_to_debug_exception + +/* Other PowerPC processors, namely those derived from the 6xx-series + * have vectors from 0x2100 through 0x2F00 defined, but marked as reserved. + * However, for the 4xx-series processors these are neither defined nor + * reserved. + */ + + /* Damn, I came up one instruction too many to fit into the + * exception space :-). Both the instruction and data TLB + * miss get to this point to load the TLB. + * r20 - EA of fault + * r21 - TLB LO (info from Linux PTE) + * r22, r23 - avilable to use + * PID - loaded with proper value when we get here + * Upon exit, we reload everything and RFI. + * Actually, it will fit now, but oh well.....a common place + * to load the TLB. + */ +finish_tlb_load: + + /* Since it has a unified TLB, and we can take data faults on + * instruction pages by copying data, we have to check if the + * EPN is already in the TLB. + */ + tlbsx. r23, 0, r20 + beq 6f + + /* load the next available TLB index. + */ + lis r22, tlb_4xx_index@h + ori r22, r22, tlb_4xx_index@l + tophys(r22, r22) + lwz r23, 0(r22) + addi r23, r23, 1 +#ifdef CONFIG_PIN_TLB + cmpwi 0, r23, 61 /* reserve entries 62, 63 for kernel */ + ble 7f + li r23, 0 +7: +#else + andi. r23, r23, (PPC4XX_TLB_SIZE-1) +#endif + stw r23, 0(r22) -stack_ovf: - addi r3,r1,STACK_FRAME_OVERHEAD - lis r1,init_task_union@ha - addi r1,r1,init_task_union@l - addi r1,r1,TASK_UNION_SIZE - STACK_FRAME_OVERHEAD - lis r24,StackOverflow@ha - addi r24,r24,StackOverflow@l - li r20,MSR_KERNEL - mtspr SPRN_SRR0,r24 # Set up the instruction pointer - mtspr SPRN_SRR1,r20 # Set up the machine state register - SYNC - rfi # Enable the MMU, jump to StackOverflow +6: + tlbwe r21, r23, TLB_DATA /* Load TLB LO */ -### -### extern void giveup_altivec(struct task_struct *prev) -### -### The PowerPC 4xx family of processors do not have AltiVec capabilities, so -### this just returns. -### + /* Create EPN. This is the faulting address plus a static + * set of bits. These are size, valid, E, U0, and ensure + * bits 20 and 21 are zero. + */ + li r22, 0x00c0 + rlwimi r20, r22, 0, 20, 31 + tlbwe r20, r23, TLB_TAG /* Load TLB HI */ + + /* Done...restore registers and get out of here. + */ +#ifdef CONFIG_403GCX + lwz r22, 12(r0) + lwz r21, 8(r0) + mtspr SPRN_PID, r22 + mtcr r21 + lwz r23, 4(r0) + lwz r22, 0(r0) +#else + mfspr r22, SPRG6 + mfspr r21, SPRG7 + mtspr SPRN_PID, r22 + mtcr r21 + mfspr r23, SPRG5 + mfspr r22, SPRG4 +#endif + mfspr r21, SPRG1 + mfspr r20, SPRG0 + PPC405_ERR77_SYNC + rfi /* Should sync shadow TLBs */ +/* extern void giveup_altivec(struct task_struct *prev) + * + * The PowerPC 4xx family of processors do not have AltiVec capabilities, so + * this just returns. + */ _GLOBAL(giveup_altivec) blr - -### -### extern void giveup_fpu(struct task_struct *prev) -### -### The PowerPC 4xx family of processors do not have an FPU, so this just -### returns. -### +/* extern void giveup_fpu(struct task_struct *prev) + * + * The PowerPC 4xx family of processors do not have an FPU, so this just + * returns. + */ _GLOBAL(giveup_fpu) blr -### -### extern void abort(void) -### -### At present, this routine just applies a system reset. -### - +/* extern void abort(void) + * + * At present, this routine just applies a system reset. + */ _GLOBAL(abort) - mfspr r13,SPRN_DBCR - oris r13,r13,DBCR_RST(DBCR_RST_SYSTEM)@h - mtspr SPRN_DBCR,r13 - + mfspr r13,SPRN_DBCR0 + oris r13,r13,DBCR_RST(DBCR_RST_SYSTEM)@h + mtspr SPRN_DBCR0,r13 -### -### This is where the main kernel code starts. -### +/* This is where the main kernel code starts. + */ start_here: - ## Establish a pointer to the current task - - lis r2,init_task_union@h - ori r2,r2,init_task_union@l - - ## Clear out the BSS as per ANSI C requirements - - lis r7,_end@ha - addi r7,r7,_end@l - lis r8,__bss_start@ha - addi r8,r8,__bss_start@l - subf r7,r8,r7 - addi r7,r7,3 - srwi. r7,r7,2 - beq 2f - addi r8,r8,-4 - mtctr r7 - li r0,0 -3: stwu r0,4(r8) - bdnz 3b - ## Stack - -2: addi r1,r2,TASK_UNION_SIZE + /* ptr to current */ + lis r2,init_task@h + ori r2,r2,init_task@l + + /* ptr to phys current thread */ + tophys(r4,r2) + addi r4,r4,THREAD /* init task's THREAD */ + mtspr SPRG3,r4 + li r3,0 + mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ + + /* stack */ + lis r1,init_thread_union@ha + addi r1,r1,init_thread_union@l li r0,0 - stwu r0,-STACK_FRAME_OVERHEAD(r1) + stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1) - ## Determine what type of platform this is. + bl early_init /* We have to do this with MMU on */ +/* + * Decide what sort of machine this is and initialize the MMU. + */ mr r3,r31 mr r4,r30 mr r5,r29 mr r6,r28 mr r7,r27 - bl identify_machine - - ## Initialize the memory management unit. - + bl machine_init bl MMU_init - ## Go back to running unmapped so that we can change to our - ## exception vectors. - +/* Go back to running unmapped so we can load up new values + * and change to using our exception vectors. + * On the 4xx, all we have to do is invalidate the TLB to clear + * the old 16M byte TLB mappings. + */ lis r4,2f@h ori r4,r4,2f@l tophys(r4,r4) li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) - mtspr SPRN_SRR0,r4 # Set up the instruction pointer - mtspr SPRN_SRR1,r3 # Set up the machine state register + mtspr SRR0,r4 + mtspr SRR1,r3 rfi - ## Load up the kernel context +/* Load up the kernel context */ +2: + SYNC /* Force all PTE updates to finish */ +#ifndef CONFIG_PIN_TLB + tlbia /* Clear all TLB entries */ + sync /* wait for tlbia/tlbie to finish */ +#endif + + /* set up the PTE pointers for the Abatron bdiGDB. + */ + lis r6, swapper_pg_dir@h + ori r6, r6, swapper_pg_dir@l + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + stw r5, 0xf0(r0) /* Must match your Abatron config file */ + tophys(r5,r5) + stw r6, 0(r5) + +/* Now turn on the MMU for real! */ + li r4,MSR_KERNEL + lis r3,start_kernel@h + ori r3,r3,start_kernel@l + mtspr SRR0,r3 + mtspr SRR1,r4 + rfi /* enable MMU and jump to start_kernel */ + +/* Set up the initial MMU state so we can do the first level of + * kernel initialization. This maps the first 16 MBytes of memory 1:1 + * virtual to physical and more importantly sets the cache mode. + */ +initial_mmu: + tlbia /* Invalidate all TLB entries */ + sync + + /* We should still be executing code at physical address 0x0000xxxx + * at this point. However, start_here is at virtual address + * 0xC000xxxx. So, set up a TLB mapping to cover this once + * translation is enabled. + */ + + lis r3,KERNELBASE@h /* Load the kernel virtual address */ + ori r3,r3,KERNELBASE@l + tophys(r4,r3) /* Load the kernel physical address */ + + /* Load the kernel PID. + */ + li r0,0 + mtspr SPRN_PID,r0 + sync + + /* Configure and load two entries into TLB slots 62 and 63. + * In case we are pinning TLBs, these are reserved in by the + * other TLB functions. If not reserving, then it doesn't + * matter where they are loaded. + */ + clrrwi r4,r4,10 /* Mask off the real page number */ + ori r4,r4,(TLB_WR | TLB_EX) /* Set the write and execute bits */ + + clrrwi r3,r3,10 /* Mask off the effective page number */ + ori r3,r3,(TLB_VALID | TLB_PAGESZ(PAGESZ_16M)) + + li r0,62 /* TLB slot 62 */ + + tlbwe r4,r0,TLB_DATA /* Load the data portion of the entry */ + tlbwe r3,r0,TLB_TAG /* Load the tag portion of the entry */ + + addis r4, r4, 0x0100 /* Map next 16 M entries */ + addis r3, r3, 0x0100 + + li r0,63 /* TLB slot 63 */ + + tlbwe r4,r0,TLB_DATA + tlbwe r3,r0,TLB_TAG + isync + + /* Establish the exception vector base + */ + lis r4,KERNELBASE@h /* EVPR only uses the high 16-bits */ + tophys(r0,r4) /* Use the physical address */ + mtspr SPRN_EVPR,r0 + + blr -2: SYNC # Force all PTE updates to finish -# tlbia # Clear all TLB entries -# sync # Wait for tlbia to finish... - - ## Set up for using our exception vectors - - tophys(r4,r2) # Pointer to physical current thread - addi r4,r4,THREAD # The init task thread - mtspr SPRN_SPRG3,r4 # Save it for exceptions later - li r3,0 # - mtspr SPRN_SPRG2,r3 # 0 implies r1 has kernel stack pointer - - ## Really turn on the MMU and jump into the kernel - - lis r4,MSR_KERNEL@h - ori r4,r4,MSR_KERNEL@l - lis r3,start_kernel@h - ori r3,r3,start_kernel@l - mtspr SPRN_SRR0,r3 # Set up the instruction pointer - mtspr SPRN_SRR1,r4 # Set up the machine state register - rfi # Enable the MMU, jump to the kernel _GLOBAL(set_context) + +#ifdef CONFIG_BDI_SWITCH + /* Context switch the PTE pointer for the Abatron BDI2000. + * The PGDIR is the second parameter. + */ + lis r5, KERNELBASE@h + lwz r5, 0xf0(r5) + stw r4, 0x4(r5) +#endif mtspr SPRN_PID,r3 blr -### -### We put a few things here that have to be page-aligned. This stuff -### goes at the beginning of the data segment, which is page-aligned. -### - +/* We put a few things here that have to be page-aligned. This stuff + * goes at the beginning of the data segment, which is page-aligned. + */ .data _GLOBAL(sdata) _GLOBAL(empty_zero_page) .space 4096 _GLOBAL(swapper_pg_dir) - .space 4096 - -### -### This space gets a copy of optional info passed to us by the bootstrap -### which is used to pass parameters into the kernel like root=/dev/sda1, etc. -### + .space 4096 +/* This space gets a copy of optional info passed to us by the bootstrap + * which is used to pass parameters into the kernel like root=/dev/sda1, etc. + */ _GLOBAL(cmd_line) .space 512 + +/* Room for two PTE pointers, usually the kernel and current user pointers + * to their respective root page table. + */ +abatron_pteptrs: + .space 8 diff -Nru a/arch/ppc/kernel/head_8xx.S b/arch/ppc/kernel/head_8xx.S --- a/arch/ppc/kernel/head_8xx.S Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/head_8xx.S Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.head_8xx.S 1.23 09/16/01 19:32:54 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * arch/ppc/kernel/except_8xx.S @@ -24,14 +24,16 @@ * */ -#include "ppc_asm.h" +#include #include #include -#include #include #include #include #include +#include +#include +#include "ppc_defs.h" .text .globl _stext @@ -334,7 +336,7 @@ beq 2f /* If zero, don't try to find a pte */ /* We have a pte table, so load the MI_TWC with the attributes - * for this page, which has only bit 31 set. + * for this "segment." */ tophys(r21,r21) ori r21,r21,1 /* Set valid bit */ @@ -343,7 +345,7 @@ stw r3, 12(r0) lwz r3, 12(r0) #endif - mtspr MI_TWC, r21 /* Set page attributes */ + mtspr MI_TWC, r21 /* Set segment attributes */ #ifdef CONFIG_8xx_CPU6 li r3, 0x3b80 stw r3, 12(r0) @@ -362,8 +364,6 @@ * set. All other Linux PTE bits control the behavior * of the MMU. */ - li r21, 0x0600 - andc r20, r20, r21 /* Clear 21, 22 */ li r21, 0x00f0 rlwimi r20, r21, 0, 24, 28 /* Set 24-27, clear 28 */ @@ -456,8 +456,6 @@ * set. All other Linux PTE bits control the behavior * of the MMU. */ - li r21, 0x0600 - andc r20, r20, r21 /* Clear 21, 22 */ li r21, 0x00f0 rlwimi r20, r21, 0, 24, 28 /* Set 24-27, clear 28 */ @@ -521,6 +519,34 @@ andis. r21, r20, 0x0200 /* If set, indicates store op */ beq 2f + /* The EA of a data TLB miss is automatically stored in the MD_EPN + * register. The EA of a data TLB error is automatically stored in + * the DAR, but not the MD_EPN register. We must copy the 20 most + * significant bits of the EA from the DAR to MD_EPN before we + * start walking the page tables. We also need to copy the CASID + * value from the M_CASID register. + * Addendum: The EA of a data TLB error is _supposed_ to be stored + * in DAR, but it seems that this doesn't happen in some cases, such + * as when the error is due to a dcbi instruction to a page with a + * TLB that doesn't have the changed bit set. In such cases, there + * does not appear to be any way to recover the EA of the error + * since it is neither in DAR nor MD_EPN. As a workaround, the + * _PAGE_HWWRITE bit is set for all kernel data pages when the PTEs + * are initialized in mapin_ram(). This will avoid the problem, + * assuming we only use the dcbi instruction on kernel addresses. + */ + mfspr r20, DAR + rlwinm r21, r20, 0, 0, 19 + ori r21, r21, MD_EVALID + mfspr r20, M_CASID + rlwimi r21, r20, 0, 28, 31 +#ifdef CONFIG_8xx_CPU6 + li r3, 0x3780 + stw r3, 12(r0) + lwz r3, 12(r0) +#endif + mtspr MD_EPN, r21 + mfspr r20, M_TWB /* Get level 1 table entry address */ /* If we are faulting a kernel address, we have to use the @@ -564,8 +590,6 @@ * set. All other Linux PTE bits control the behavior * of the MMU. */ - li r21, 0x0600 - andc r20, r20, r21 /* Clear 21, 22 */ li r21, 0x00f0 rlwimi r20, r21, 0, 24, 28 /* Set 24-27, clear 28 */ @@ -613,63 +637,6 @@ . = 0x2000 -/* - * This code finishes saving the registers to the exception frame - * and jumps to the appropriate handler for the exception, turning - * on address translation. - */ - .globl transfer_to_handler -transfer_to_handler: - stw r22,_NIP(r21) - lis r22,MSR_POW@h - andc r23,r23,r22 - stw r23,_MSR(r21) - SAVE_4GPRS(8, r21) - SAVE_8GPRS(12, r21) - SAVE_8GPRS(24, r21) - andi. r23,r23,MSR_PR - mfspr r23,SPRG3 /* if from user, fix up THREAD.regs */ - beq 2f - addi r24,r1,STACK_FRAME_OVERHEAD - stw r24,PT_REGS(r23) -2: addi r2,r23,-THREAD /* set r2 to current */ - tovirt(r2,r2) - mflr r23 - andi. r24,r23,0x3f00 /* get vector offset */ - stw r24,TRAP(r21) - li r22,0 - stw r22,RESULT(r21) - mtspr SPRG2,r22 /* r1 is now kernel sp */ - addi r24,r2,TASK_STRUCT_SIZE /* check for kernel stack overflow */ - cmplw 0,r1,r2 - cmplw 1,r1,r24 - crand 1,1,4 - bgt- stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ - lwz r24,0(r23) /* virtual address of handler */ - lwz r23,4(r23) /* where to go when done */ - mtspr SRR0,r24 - mtspr SRR1,r20 - mtlr r23 - SYNC - rfi /* jump to handler, enable MMU */ - -/* - * On kernel stack overflow, load up an initial stack pointer - * and call StackOverflow(regs), which should not return. - */ -stack_ovf: - addi r3,r1,STACK_FRAME_OVERHEAD - lis r1,init_task_union@ha - addi r1,r1,init_task_union@l - addi r1,r1,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD - lis r24,StackOverflow@ha - addi r24,r24,StackOverflow@l - li r20,MSR_KERNEL - mtspr SRR0,r24 - mtspr SRR1,r20 - SYNC - rfi - .globl giveup_fpu giveup_fpu: blr @@ -683,10 +650,9 @@ * This is where the main kernel code starts. */ start_here: - /* ptr to current */ - lis r2,init_task_union@h - ori r2,r2,init_task_union@l + lis r2,init_task@h + ori r2,r2,init_task@l /* ptr to phys current thread */ tophys(r4,r2) @@ -696,9 +662,10 @@ mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ /* stack */ - addi r1,r2,TASK_UNION_SIZE + lis r1,init_thread_union@ha + addi r1,r1,init_thread_union@l li r0,0 - stwu r0,-STACK_FRAME_OVERHEAD(r1) + stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1) bl early_init /* We have to do this with MMU on */ @@ -769,16 +736,31 @@ * kernel initialization. This maps the first 8 MBytes of memory 1:1 * virtual to physical. Also, set the cache mode since that is defined * by TLB entries and perform any additional mapping (like of the IMMR). + * If configured to pin some TLBs, we pin the first 8 Mbytes of kernel, + * 24 Mbytes of data, and the 8M IMMR space. Anything not covered by + * these mappings is mapped by page tables. */ initial_mmu: tlbia /* Invalidate all TLB entries */ +#ifdef CONFIG_PIN_TLB + lis r8, MI_RSV4I@h + ori r8, r8, 0x1c00 +#else li r8, 0 - mtspr MI_CTR, r8 /* Set instruction control to zero */ - lis r8, MD_RESETVAL@h +#endif + mtspr MI_CTR, r8 /* Set instruction MMU control */ + +#ifdef CONFIG_PIN_TLB + lis r10, (MD_RSV4I | MD_RESETVAL)@h + ori r10, r10, 0x1c00 + mr r8, r10 +#else + lis r10, MD_RESETVAL@h +#endif #ifndef CONFIG_8xx_COPYBACK - oris r8, r8, MD_WTDEF@h + oris r10, r10, MD_WTDEF@h #endif - mtspr MD_CTR, r8 /* Set data TLB control */ + mtspr MD_CTR, r10 /* Set data TLB control */ /* Now map the lower 8 Meg into the TLBs. For this quick hack, * we can load the instruction and data TLB registers with the @@ -802,6 +784,10 @@ /* Map another 8 MByte at the IMMR to get the processor * internal registers (among other things). */ +#ifdef CONFIG_PIN_TLB + addi r10, r10, 0x0100 + mtspr MD_CTR, r10 +#endif mfspr r9, 638 /* Get current IMMR */ andis. r9, r9, 0xff80 /* Get 8Mbyte boundary */ @@ -814,6 +800,30 @@ mr r8, r9 /* Create paddr for TLB */ ori r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */ mtspr MD_RPN, r8 + +#ifdef CONFIG_PIN_TLB + /* Map two more 8M kernel data pages. + */ + addi r10, r10, 0x0100 + mtspr MD_CTR, r10 + + lis r8, KERNELBASE@h /* Create vaddr for TLB */ + addis r8, r8, 0x0080 /* Add 8M */ + ori r8, r8, MI_EVALID /* Mark it valid */ + mtspr MD_EPN, r8 + li r9, MI_PS8MEG /* Set 8M byte page */ + ori r9, r9, MI_SVALID /* Make it valid */ + mtspr MD_TWC, r9 + li r11, MI_BOOTINIT /* Create RPN for address 0 */ + addis r11, r11, 0x0080 /* Add 8M */ + mtspr MD_RPN, r8 + + addis r8, r8, 0x0080 /* Add 8M */ + mtspr MD_EPN, r8 + mtspr MD_TWC, r9 + addis r11, r11, 0x0080 /* Add 8M */ + mtspr MD_RPN, r8 +#endif /* Since the cache is enabled according to the information we * just loaded into the TLB, invalidate and enable the caches here. diff -Nru a/arch/ppc/kernel/i8259.c b/arch/ppc/kernel/i8259.c --- a/arch/ppc/kernel/i8259.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/i8259.c Tue Feb 19 18:08:59 2002 @@ -1,13 +1,17 @@ /* - * BK Id: SCCS/s.i8259.c 1.7 05/17/01 18:14:21 cort + * BK Id: %F% %I% %G% %U% %#% */ #include #include +#include +#include #include #include #include -#include "i8259.h" +#include + +static volatile char *pci_intack; /* RO, gives us the irq vector */ unsigned char cached_8259[2] = { 0xff, 0xff }; #define cached_A1 (cached_8259[0]) @@ -17,32 +21,56 @@ int i8259_pic_irq_offset; -int i8259_irq(int cpu) +/* Acknowledge the irq using the PCI host bridge's interrupt acknowledge + * feature. (Polling is somehow broken on some IBM and Motorola PReP boxes.) + */ +int i8259_irq(void) +{ + int irq; + + spin_lock/*_irqsave*/(&i8259_lock/*, flags*/); + + irq = *pci_intack & 0xff; + if (irq==7) { + /* + * This may be a spurious interrupt. + * + * Read the interrupt status register (ISR). If the most + * significant bit is not set then there is no valid + * interrupt. + */ + if(~inb(0x20)&0x80) { + irq = -1; + } + } + spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/); + return irq; +} + +/* Poke the 8259's directly using poll commands. */ +int i8259_poll(void) { int irq; - + spin_lock/*_irqsave*/(&i8259_lock/*, flags*/); - /* - * Perform an interrupt acknowledge cycle on controller 1 - */ - outb(0x0C, 0x20); - irq = inb(0x20) & 7; - if (irq == 2) - { - /* - * Interrupt is cascaded so perform interrupt - * acknowledge on controller 2 - */ - outb(0x0C, 0xA0); - irq = (inb(0xA0) & 7) + 8; - } - else if (irq==7) - { - /* - * This may be a spurious interrupt - * - * Read the interrupt status register. If the most - * significant bit is not set then there is no valid + /* + * Perform an interrupt acknowledge cycle on controller 1 + */ + outb(0x0C, 0x20); /* prepare for poll */ + irq = inb(0x20) & 7; + if (irq == 2) { + /* + * Interrupt is cascaded so perform interrupt + * acknowledge on controller 2 + */ + outb(0x0C, 0xA0); /* prepare for poll */ + irq = (inb(0xA0) & 7) + 8; + } else if (irq==7) { + /* + * This may be a spurious interrupt + * + * Read the interrupt status register. If the most + * significant bit is not set then there is no valid * interrupt */ outb(0x0b, 0x20); @@ -58,44 +86,44 @@ static void i8259_mask_and_ack_irq(unsigned int irq_nr) { unsigned long flags; - + spin_lock_irqsave(&i8259_lock, flags); - if ( irq_nr >= i8259_pic_irq_offset ) - irq_nr -= i8259_pic_irq_offset; + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; - if (irq_nr > 7) { - cached_A1 |= 1 << (irq_nr-8); - inb(0xA1); /* DUMMY */ - outb(cached_A1,0xA1); - outb(0x20,0xA0); /* Non-specific EOI */ - outb(0x20,0x20); /* Non-specific EOI to cascade */ - } else { - cached_21 |= 1 << irq_nr; - inb(0x21); /* DUMMY */ - outb(cached_21,0x21); - outb(0x20,0x20); /* Non-specific EOI */ - } + if (irq_nr > 7) { + cached_A1 |= 1 << (irq_nr-8); + inb(0xA1); /* DUMMY */ + outb(cached_A1,0xA1); + outb(0x20,0xA0); /* Non-specific EOI */ + outb(0x20,0x20); /* Non-specific EOI to cascade */ + } else { + cached_21 |= 1 << irq_nr; + inb(0x21); /* DUMMY */ + outb(cached_21,0x21); + outb(0x20,0x20); /* Non-specific EOI */ + } spin_unlock_irqrestore(&i8259_lock, flags); } static void i8259_set_irq_mask(int irq_nr) { - outb(cached_A1,0xA1); - outb(cached_21,0x21); + outb(cached_A1,0xA1); + outb(cached_21,0x21); } - + static void i8259_mask_irq(unsigned int irq_nr) { unsigned long flags; spin_lock_irqsave(&i8259_lock, flags); - if ( irq_nr >= i8259_pic_irq_offset ) - irq_nr -= i8259_pic_irq_offset; - if ( irq_nr < 8 ) - cached_21 |= 1 << irq_nr; - else - cached_A1 |= 1 << (irq_nr-8); - i8259_set_irq_mask(irq_nr); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + if ( irq_nr < 8 ) + cached_21 |= 1 << irq_nr; + else + cached_A1 |= 1 << (irq_nr-8); + i8259_set_irq_mask(irq_nr); spin_unlock_irqrestore(&i8259_lock, flags); } @@ -104,13 +132,13 @@ unsigned long flags; spin_lock_irqsave(&i8259_lock, flags); - if ( irq_nr >= i8259_pic_irq_offset ) - irq_nr -= i8259_pic_irq_offset; - if ( irq_nr < 8 ) - cached_21 &= ~(1 << irq_nr); - else - cached_A1 &= ~(1 << (irq_nr-8)); - i8259_set_irq_mask(irq_nr); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + if ( irq_nr < 8 ) + cached_21 &= ~(1 << irq_nr); + else + cached_A1 &= ~(1 << (irq_nr-8)); + i8259_set_irq_mask(irq_nr); spin_unlock_irqrestore(&i8259_lock, flags); } @@ -121,36 +149,62 @@ } struct hw_interrupt_type i8259_pic = { - " i8259 ", - NULL, - NULL, - i8259_unmask_irq, - i8259_mask_irq, - i8259_mask_and_ack_irq, - i8259_end_irq, - NULL + " i8259 ", + NULL, + NULL, + i8259_unmask_irq, + i8259_mask_irq, + i8259_mask_and_ack_irq, + i8259_end_irq, + NULL +}; + +static struct resource pic1_iores = { + "8259 (master)", 0x20, 0x21, IORESOURCE_BUSY }; -void __init i8259_init(void) +static struct resource pic2_iores = { + "8259 (slave)", 0xa0, 0xa1, IORESOURCE_BUSY +}; + +static struct resource pic_edgectrl_iores = { + "8259 edge control", 0x4d0, 0x4d1, IORESOURCE_BUSY +}; + +void __init i8259_init(long intack_addr) { unsigned long flags; - + spin_lock_irqsave(&i8259_lock, flags); - /* init master interrupt controller */ - outb(0x11, 0x20); /* Start init sequence */ - outb(0x00, 0x21); /* Vector base */ - outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ - outb(0x01, 0x21); /* Select 8086 mode */ - outb(0xFF, 0x21); /* Mask all */ - /* init slave interrupt controller */ - outb(0x11, 0xA0); /* Start init sequence */ - outb(0x08, 0xA1); /* Vector base */ - outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ - outb(0x01, 0xA1); /* Select 8086 mode */ - outb(0xFF, 0xA1); /* Mask all */ - outb(cached_A1, 0xA1); - outb(cached_21, 0x21); + /* init master interrupt controller */ + outb(0x11, 0x20); /* Start init sequence */ + outb(0x00, 0x21); /* Vector base */ + outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0x21); /* Select 8086 mode */ + + /* init slave interrupt controller */ + outb(0x11, 0xA0); /* Start init sequence */ + outb(0x08, 0xA1); /* Vector base */ + outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0xA1); /* Select 8086 mode */ + + /* always read ISR */ + outb(0x0B, 0x20); + outb(0x0B, 0xA0); + + /* Mask all interrupts */ + outb(cached_A1, 0xA1); + outb(cached_21, 0x21); + spin_unlock_irqrestore(&i8259_lock, flags); - request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT, - "82c59 secondary cascade", NULL ); + + /* reserve our resources */ + request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT, + "82c59 secondary cascade", NULL ); + request_resource(&ioport_resource, &pic1_iores); + request_resource(&ioport_resource, &pic2_iores); + request_resource(&ioport_resource, &pic_edgectrl_iores); + + if (intack_addr) + pci_intack = ioremap(intack_addr, 1); } diff -Nru a/arch/ppc/kernel/i8259.h b/arch/ppc/kernel/i8259.h --- a/arch/ppc/kernel/i8259.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,15 +0,0 @@ -/* - * BK Id: SCCS/s.i8259.h 1.5 05/17/01 18:14:21 cort - */ - -#ifndef _PPC_KERNEL_i8259_H -#define _PPC_KERNEL_i8259_H - -#include "local_irq.h" - -extern struct hw_interrupt_type i8259_pic; - -void i8259_init(void); -int i8259_irq(int); - -#endif /* _PPC_KERNEL_i8259_H */ diff -Nru a/arch/ppc/kernel/iSeries_asm.h b/arch/ppc/kernel/iSeries_asm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/iSeries_asm.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,62 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/kernel/iSeries_asm.h + * + * Definitions used by various bits of low-level assembly code on iSeries. + * + * Copyright (C) 2001 IBM Corp. + * + * 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. + */ + +#define CHECKLPQUEUE(ra,rb,rc) \ + mfspr rb,SPRG1; /* Get Paca address */\ + lbz ra,PACALPPACA+LPPACAIPIINT(rb); /* Get IPI int flag */\ + cmpi 0,ra,0; /* IPI occurred in hypervisor ? */\ + bne 99f; /* If so, skip rest */\ + lwz ra,PACALPQUEUE(rb); /* Get LpQueue address */\ + cmpi 0,ra,0; /* Does LpQueue exist? */\ + beq 99f; /* If not skip rest */\ + lbz rb,LPQINUSEWORD(ra); /* Test for LpQueue recursion */\ + cmpi 0,rb,1; /* If we are about to recurse */\ + beq 99f; /* If so, skip rest */\ + lwz rb,LPQCUREVENTPTR(ra); /* Get current LpEvent */\ + lbz rb,LPEVENTFLAGS(rb); /* Get Valid bit */\ + lbz rc,LPQOVERFLOW(ra); /* Get LpQueue overflow */\ + andi. ra,rb,0x0080; /* Isolate Valid bit */\ + or. ra,ra,rc; /* 0 == no pending events */\ +99: + +#define CHECKDECR(ra,rb) \ + mfspr rb,SPRG1; /* Get Paca address */\ + lbz ra,PACALPPACA+LPPACADECRINT(rb); /* Get DECR int flag */\ + cmpi 0,ra,0; /* DECR occurred in hypervisor ? */\ + beq 99f; /* If not, skip rest */\ + xor ra,ra,ra; \ + stb ra,PACALPPACA+LPPACADECRINT(rb); /* Clear DECR int flag */\ +99: + +#define CHECKANYINT(ra,rb,rc) \ + mfspr rb,SPRG1; /* Get Paca address */\ + ld ra,PACALPPACA+LPPACAANYINT(rb); /* Get all interrupt flags */\ + /* Note use of ld, protected by soft/hard disabled */\ + cmpldi 0,ra,0; /* Any interrupt occurred while soft disabled? */\ + bne 99f; /* If so, skip rest */\ + lwz ra,PACALPQUEUE(rb); /* Get LpQueue address */\ + cmpi 0,ra,0; /* Does LpQueue exist? */\ + beq 99f; /* If not skip rest */\ + lwz rb,LPQINUSEWORD(ra); /* Test for LpQueue recursion */\ + cmpi 0,rb,1; /* If we are about to recurse */\ + beq 99f; /* If so, skip rest */\ + lwz rb,LPQCUREVENTPTR(ra); /* Get current LpEvent */\ + lbz rb,LPEVENTFLAGS(rb); /* Get Valid bit */\ + lbz rc,LPQOVERFLOW(ra); /* Get LpQueue overflow */\ + andi. ra,rb,0x0080; /* Isolate Valid bit */\ + or. ra,ra,rc; /* 0 == no pending events */\ +99: + diff -Nru a/arch/ppc/kernel/iSeries_head.S b/arch/ppc/kernel/iSeries_head.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/iSeries_head.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1512 @@ +/* + * arch/ppc/kernel/iSeries_head.S + * + * Adapted from arch/ppc/kernel/head.S + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * Adapted for iSeries by Mike Corrigan + * Updated by Dave Boutcher + * + * This file contains the low-level support and setup for the + * iSeries LPAR platform. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ppc_defs.h" +#include "iSeries_asm.h" + + + .text + .globl _stext +_stext: + +/* iSeries LPAR + * + * In an iSeries partition, the operating system has no direct access + * to the hashed page table. The iSeries hypervisor manages the + * hashed page table, and is directed by the operating system in the + * partition. The partition, Linux in this case, always runs with + * MSR.IR and MSR.DR equal to 1. The hypervisor establishes + * addressibility for the first 64 MB of memory at 0xC0000000 by + * building a hashed page table and setting segment register 12. + * + * The partition memory is not physically contiguous, nor necessarily + * addressable with a 32-bit address. The hypervisor provides functions + * which the kernel can use to discover the layout of memory. The + * iSeries LPAR specific code in the kernel will build a table that maps + * contiguous pseudo-real addresses starting at zero to the actual + * physical addresses owned by this partition. In 32-bit mode we will + * restrict ourselves to no more than 768 MB (or maybe 1 GB) + * + * When Linux interrupt handlers get control, the hypervisor has + * already saved SRR0 and SRR1 into a control block shared between + * the hypervisor and Linux. This is know as the ItLpPaca. The values + * in the actual SRR0 and SRR1 are not valid. This requires a change in + * the way the SPRG registers are used. The definitions are: + * + * Register old definition new definition + * + * SPRG0 temp - used to save gpr reserved for hypervisor + * SPRG1 temp - used to save gpr addr of Paca + * SPRG2 0 or kernel stack frame temp - used to save gpr + * SPRG3 Linux thread Linux thread + * + * The Paca contains the address of the ItLpPaca. The Paca is known only + * to Linux, while the ItLpPaca is shared between Linux and the + * hypervisor. + * + * The value that used to be in SPRG2 will now be saved in the Paca, + * as will at least one GPR. + */ + + .globl __start +__start: + b start_here + + + . = 0x020 + + /* iSeries LPAR hypervisor expects a 64-bit offset of + the hvReleaseData structure (see HvReleaseData.h) + at offset 0x20. This is the base for all common + control blocks between the hypervisor and the kernel + */ + + .long 0 + .long hvReleaseData-KERNELBASE + .long 0 + .long msChunks-KERNELBASE + .long 0 + .long pidhash-KERNELBASE + /* Pointer to start of embedded System.map */ + .long 0 + .globl embedded_sysmap_start +embedded_sysmap_start: + .long 0 + /* Pointer to end of embedded System.map */ + .long 0 + .globl embedded_sysmap_end +embedded_sysmap_end: + .long 0 + + + . = 0x060 + + .globl ste_fault_count +ste_fault_count: + .long 0 + .globl set_context_count +set_context_count: + .long 0 + .globl yield_count +yield_count: + .long 0 + .globl update_times_count +update_times_count: + .long 0 + .globl update_wall_jiffies_count +update_wall_jiffies_count: + .long 0 + .globl update_wall_jiffies_ticks +update_wall_jiffies_ticks: + .long 0 + + +/* + * We assume SPRG1 has the address of the Paca and SPRG3 + * has the address of the task's thread_struct. + * SPRG2 is used as a scratch register (as required by the + * hypervisor). SPRG0 is reserved for the hypervisor. + * + * The ItLpPaca has the values of SRR0 and SRR1 that the + * hypervisor saved at the point of the actual interrupt. + * + * The Paca contains the value that the non-LPAR PPC Linux Kernel + * keeps in SPRG2, which is either zero (if the interrupt + * occurred in the kernel) or the address of the available + * space on the kernel stack (if the interrupt occurred + * in user code). +*/ +#define EXCEPTION_PROLOG_1 \ + mtspr SPRG2,r20; /* use SPRG2 as scratch reg */\ + mfspr r20,SPRG1; /* get Paca */\ + /* must do std not stw because soft disable protects \ + * 64-bit register use (in HvCall, maybe others) \ + */\ + std r21,PACAR21(r20); /* Save GPR21 in Paca */\ + std r22,PACAR22(r20); /* Save GPR22 in Paca */\ + mfcr r22 /* Get CR */ + +#define EXCEPTION_PROLOG_2 \ + lwz r21,PACAKSAVE(r20); /* exception stack to use */\ + cmpwi 0,r21,0; /* user mode or kernel */\ + bne 1f; /* 0 -> r1, else use PACAKSAVE */\ + subi r21,r1,INT_FRAME_SIZE; /* alloc exc. frame */\ +1: stw r1,GPR1(r21); \ + mr r1,r21; \ + stw r22,_CCR(r1); /* save CR in stackframe */ \ + mflr r22; \ + stw r22,_LINK(r1); /* Save LR in stackframe */ \ + bl save_regs; /* now save everything else */ \ + ld r22,PACALPPACA+LPPACASRR0(r20); /* Get SRR0 from ItLpPaca */\ + ld r23,PACALPPACA+LPPACASRR1(r20) /* Get SRR1 from ItLpPaca */ + +#define EXCEPTION_PROLOG_EXIT \ + mtcrf 0xff,r22; \ + ld r22,PACALPPACA+LPPACASRR0(r20); \ + ld r21,PACALPPACA+LPPACASRR1(r20); \ + mtspr SRR0,r22; \ + mtspr SRR1,r21; \ + ld r22,PACAR22(r20); \ + ld r21,PACAR21(r20); \ + mfspr r20,SPRG2; \ + RFI + +#define EXCEPTION_PROLOG \ + EXCEPTION_PROLOG_1; \ + EXCEPTION_PROLOG_2 + + +/* + * Note: code which follows this uses cr0.eq (set if from kernel), + * r21, r22 (SRR0), and r23 (SRR1). + */ + +/* + * Exception vectors. + */ +#define STD_EXCEPTION(n, label, hdlr) \ + . = n; \ +label: \ + EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + li r20,0; /* soft disabled */\ + bl transfer_to_handler; \ + .long hdlr; \ + .long ret_from_except + +/* System reset */ + . = 0x100 +SystemReset: + mfspr r3,SPRG3 /* Get Paca address */ + mtspr SPRG1,r3 /* Set Linux SPRG1 -> Paca */ + lhz r24,PACAPACAINDEX(r3) /* Get processor # */ + cmpi 0,r24,0 /* Are we processor 0? */ + beq start_here /* Start up the first processor */ + mfspr r4,CTRLF + li r5,RUNLATCH + andc r4,r4,r5 /* Turn off the run light */ + mtspr CTRLT,r4 +1: + HMT_LOW +#ifdef CONFIG_SMP + lbz r23,PACAPROCSTART(r3) /* Test if this processor + * should start */ + cmpi 0,r23,0 + bne secondary_start +secondary_smp_loop: + /* Let the Hypervisor know we are alive */ + /* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */ + lis r3,0x8002 + rldicr r0,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */ + rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */ + or r3,r3,r0 /* r3 = r3 | r0 */ +#else /* CONFIG_SMP */ + /* Yield the processor. This is required for non-SMP kernels + which are running on multi-threaded machines. */ + lis r3,0x8000 + rldicr r3,r3,32,15 /* r3 = (r3 << 32) & 0xffff000000000000 */ + addi r3,r3,18 /* r3 = 0x8000000000000012 which is "yield" */ + li r4,0 /* "yield timed" */ + li r5,-1 /* "yield forever" */ +#endif /* CONFIG_SMP */ + li r0,-1 /* r0=-1 indicates a Hypervisor call */ + sc /* Invoke the hypervisor via a system call */ + mfspr r3,SPRG1 /* Put r3 back */ + b 1b /* If SMP not configured, secondaries + * loop forever */ + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data access exception. */ + . = 0x300 +DataAccess: + EXCEPTION_PROLOG + mfspr r4,DAR + stw r4,_DAR(r1) + mfspr r5,DSISR + stw r5,_DSISR(r1) + + andis. r0,r5,0x0020 /* Is this a segment fault? */ + bne ste_fault /* Yes - go reload segment regs */ + + /* This should and with 0xd7ff */ + andis. r0,r5,0xa470 /* Can we handle as little fault? */ + bne 1f /* */ + + rlwinm r3,r5,32-15,21,21 /* DSISR_STORE -> _PAGE_RW */ + + /* + * r3 contains the required access permissions + * r4 contains the faulting address + */ + + stw r22,_NIP(r1) /* Help with debug if dsi loop */ + bl hash_page /* Try to handle as hpte fault */ + lwz r4,_DAR(r1) /* Get original DAR */ + lwz r5,_DSISR(r1) /* and original DSISR */ + +1: addi r3,r1,STACK_FRAME_OVERHEAD + lwz r20,_SOFTE(r1) + bl transfer_to_handler + .long do_page_fault + .long ret_from_except + + +/* Instruction access exception. */ + . = 0x400 +InstructionAccess: + EXCEPTION_PROLOG + mr r4,r22 + mr r5,r23 + + andis. r0,r23,0x0020 /* Is this a segment fault? */ + bne ste_fault /* Yes - go reload segment regs */ + + andis. r0,r23,0x4000 /* no pte found? */ + beq 1f /* if so, try to put a PTE */ + + li r3,0 + bl hash_page /* Try to handle as hpte fault */ + mr r4,r22 + mr r5,r23 + +1: addi r3,r1,STACK_FRAME_OVERHEAD + lwz r20,_SOFTE(r1) + bl transfer_to_handler + .long do_page_fault + .long ret_from_except + +/* External interrupt */ + . = 0x500; +HardwareInterrupt: + EXCEPTION_PROLOG_1 + lbz r21,PACAPROCENABLED(r20) + cmpi 0,r21,0 + bne 1f + EXCEPTION_PROLOG_EXIT +1: EXCEPTION_PROLOG_2 +do_pending_int: + addi r3,r1,STACK_FRAME_OVERHEAD + li r4,0 + li r20,0 /* Soft disabled */ + bl transfer_to_handler + .globl do_IRQ_intercept +do_IRQ_intercept: + .long do_IRQ; + .long ret_from_intercept + +/* Alignment exception */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG + mfspr r4,DAR + stw r4,_DAR(r1) + mfspr r5,DSISR + stw r5,_DSISR(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + lbz r20,PACAPROCENABLED(r20) /* preserve soft en/disabled */ + bl transfer_to_handler + .long AlignmentException + .long ret_from_except + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + lbz r20,PACAPROCENABLED(r20) /* preserve soft en/disabled */ + bl transfer_to_handler + .long ProgramCheckException + .long ret_from_except + +/* Floating-point unavailable */ + . = 0x800 +FPUnavailable: + EXCEPTION_PROLOG + lwz r3,PACAKSAVE(r20) + cmpwi 0,r3,0 + beq 1f + b load_up_fpu +1: + li r20,0 /* soft disabled */ + bl transfer_to_handler /* if from kernel, take a trap */ + .long KernelFP + .long ret_from_except + + . = 0x900 +Decrementer: + EXCEPTION_PROLOG_1 + lbz r21,PACAPROCENABLED(r20) + cmpi 0,r21,0 + bne 1f + + li r21,1 + stb r21,PACALPPACA+LPPACADECRINT(r20) + lwz r21,PACADEFAULTDECR(r20) + mtspr DEC,r21 + EXCEPTION_PROLOG_EXIT +1: EXCEPTION_PROLOG_2 + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,0 /* Soft disabled */ + bl transfer_to_handler + .globl timer_interrupt_intercept +timer_interrupt_intercept: + .long timer_interrupt + .long ret_from_intercept + + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + +/* System call */ + . = 0xc00 +SystemCall: + EXCEPTION_PROLOG + /* Store r3 to the kernel stack */ + stw r3,ORIG_GPR3(r1) + lbz r20,PACAPROCENABLED(r20) /* preserve soft en/disabled */ + bl transfer_to_handler + .long DoSyscall + .long ret_from_except + +/* Single step - not used on 601 */ + STD_EXCEPTION(0xd00, SingleStep, SingleStepException) +/* + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) +*/ + STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint) +/* + STD_EXCEPTION(0x1400, SMI, SMIException) + STD_EXCEPTION(0x1500, Trap_15, UnknownException) + STD_EXCEPTION(0x1600, Trap_16, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, TAUException) + STD_EXCEPTION(0x1800, Trap_18, UnknownException) + STD_EXCEPTION(0x1900, Trap_19, UnknownException) + STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) + STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) + STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) + STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) + STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) + STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) + STD_EXCEPTION(0x2000, RunMode, RunModeException) + STD_EXCEPTION(0x2100, Trap_21, UnknownException) + STD_EXCEPTION(0x2200, Trap_22, UnknownException) + STD_EXCEPTION(0x2300, Trap_23, UnknownException) + STD_EXCEPTION(0x2400, Trap_24, UnknownException) + STD_EXCEPTION(0x2500, Trap_25, UnknownException) + STD_EXCEPTION(0x2600, Trap_26, UnknownException) + STD_EXCEPTION(0x2700, Trap_27, UnknownException) + STD_EXCEPTION(0x2800, Trap_28, UnknownException) + STD_EXCEPTION(0x2900, Trap_29, UnknownException) + STD_EXCEPTION(0x2a00, Trap_2a, UnknownException) + STD_EXCEPTION(0x2b00, Trap_2b, UnknownException) + STD_EXCEPTION(0x2c00, Trap_2c, UnknownException) + STD_EXCEPTION(0x2d00, Trap_2d, UnknownException) + STD_EXCEPTION(0x2e00, Trap_2e, UnknownException) + STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) +*/ + . = 0x3000 + + /* This code saves: CTR, XER, DAR, DSISR, SRR0, SRR1, */ + /* r0, r2-r13, r20-r24 */ + /* It uses R22 as a scratch register */ +save_regs: + ld r22,PACAR21(r20) /* Get GPR21 from Paca */ + stw r22,GPR21(r1) /* Save GPR21 in stackframe */ + ld r22,PACAR22(r20) /* Get GPR22 from Paca */ + stw r22,GPR22(r1) /* Save GPR22 in stackframe */ + stw r23,GPR23(r1) /* Save GPR23 in stackframe */ + stw r24,GPR24(r1) /* Save GPR24 in stackframe */ + mfspr r22,SPRG2 /* Get GPR20 from SPRG2 */ + stw r22,GPR20(r1) /* Save GPR20 in stackframe */ + mfctr r22 + stw r22,_CTR(r1) + mfspr r22,XER + stw r22,_XER(r1) + lbz r22,PACAPROCENABLED(r20)/* Get soft enabled/disabled */ + stw r22,_SOFTE(r1) + stw r0,GPR0(r1) + SAVE_8GPRS(2, r1) + SAVE_4GPRS(10, r1) + blr + +ste_fault: + bl set_kernel_segregs + + mfspr r3,SPRG1 + li r4,0 + stb r4,PACAPROCENABLED(r3) /* Soft disable prevents going to */ + /* do_pending_int on recursive fault */ + + lis r3,ste_fault_count@ha + lwz r4,ste_fault_count@l(r3) + addi r4,r4,1 + stw r4,ste_fault_count@l(r3) + + mfspr r3,SPRG3 /* get thread */ + addi r3,r3,-THREAD /* get 'current' */ + lwz r3,MM(r3) /* get mm */ + cmpi 0,r3,0 /* if no mm */ + beq 1f /* then use context 0 (kernel) */ + lwz r3,CONTEXT(r3) /* get context */ +1: + /* set_context kills r0, r3, r4 and CTR */ + bl set_context + + lwz r3,_SOFTE(r1) + cmpi 0,r3,0 + beq 5f /* skip checks if restoring disabled */ + + CHECKANYINT(r4,r5,r6) /* if pending interrupts, process them */ + bne- do_pending_int +5: + mfspr r4,SPRG1 + stb r3,PACAPROCENABLED(r4) /* Restore enabled/disabled */ + + b fault_exit + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception, turning + * on address translation. + * + * At this point r0-r13, r20-r24, CCR, CTR, LINK, XER, DAR and DSISR + * are saved on a stack. SRR0 is in r22, SRR1 is in r23 + * r1 points to the stackframe, r1 points to the kernel stackframe + * We no longer have any dependency on data saved in the PACA, SRR0, SRR1 + * DAR or DSISR. We now copy the registers to the kernel stack (which + * might cause little faults). Any little fault will be handled without + * saving state. Thus when the little fault is completed, it will rfi + * back to the original faulting instruction. + */ + .globl transfer_to_handler +transfer_to_handler: + + mfspr r6,SPRG1 + li r7,0 + stw r7,PACAKSAVE(r6) /* Force new frame for recursive fault */ + + /* Restore the regs used above -- parameters to syscall */ + lwz r6,GPR6(r1) + lwz r7,GPR7(r1) + + stw r22,_NIP(r1) + stw r23,_MSR(r1) + SAVE_4GPRS(14, r1) + SAVE_2GPRS(18, r1) + SAVE_4GPRS(25, r1) + SAVE_2GPRS(29, r1) + SAVE_GPR(31, r1) + + andi. r23,r23,MSR_PR + mfspr r23,SPRG3 + addi r2,r23,-THREAD /* set r2 to current */ + beq 2f /* if from user, fix up THREAD.regs */ + addi r24,r1,STACK_FRAME_OVERHEAD + stw r24,PT_REGS(r23) + b 3f +2: /* if from kernel, check for stack overflow */ + lwz r22,THREAD_INFO(r2) + cmplw r1,r22 /* if r1 <= current->thread_info */ + ble- stack_ovf /* then the kernel stack overflowed */ +3: + li r22,0 + stw r22,RESULT(r1) + mfspr r23,SPRG1 /* Get Paca address */ + stb r20,PACAPROCENABLED(r23) /* soft enable or disabled */ + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r1) + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + li r20,MSR_KERNEL + ori r20,r20,MSR_EE /* Always hard enabled */ + FIX_SRR1(r20,r22) + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + RFI /* jump to handler, enable MMU */ + +/* + * On kernel stack overflow, load up an initial stack pointer + * and call StackOverflow(regs), which should not return. + */ +stack_ovf: + addi r3,r1,STACK_FRAME_OVERHEAD + lis r1,init_thread_union@ha + addi r1,r1,init_thread_union@l + addi r1,r1,THREAD_SIZE-STACK_FRAME_OVERHEAD + mfspr r24,SPRG1 + li r20,0 + stb r20,PACAPROCENABLED(r24) /* soft disable */ + lis r24,StackOverflow@ha + addi r24,r24,StackOverflow@l + li r20,MSR_KERNEL + ori r20,r20,MSR_EE /* Always hard enabled */ + FIX_SRR1(r20,r22) + mtspr SRR0,r24 + mtspr SRR1,r20 + RFI + +/* + * Disable FP for the task which had the FPU previously, + * and save its floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + * On SMP we know the fpu is free, since we give it up every + * switch. -- Cort + * Assume r20 points to PACA on entry + */ +load_up_fpu: + mfmsr r5 + ori r5,r5,MSR_FP + SYNC + MTMSRD(r5) /* enable use of fpu now */ + isync +/* + * For SMP, we don't do lazy FPU switching because it just gets too + * horrendously complex, especially when a task switches from one CPU + * to another. Instead we call giveup_fpu in switch_to. + */ +#ifndef CONFIG_SMP + lis r3,last_task_used_math@ha + lwz r4,last_task_used_math@l(r3) + cmpi 0,r4,0 + beq 1f + addi r4,r4,THREAD /* want last_task_used_math->thread */ + SAVE_32FPRS(0, r4) + mffs fr0 + stfd fr0,THREAD_FPSCR-4(r4) + lwz r5,PT_REGS(r4) + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r20,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r20 /* disable FP for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#endif /* CONFIG_SMP */ + /* enable use of FP after return */ + ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 + mfspr r5,SPRG3 /* current task's THREAD (phys) */ + lfd fr0,THREAD_FPSCR-4(r5) + mtfsf 0xff,fr0 + REST_32FPRS(0, r5) +#ifndef CONFIG_SMP + subi r4,r5,THREAD + stw r4,last_task_used_math@l(r3) +#endif /* CONFIG_SMP */ + /* restore registers and return */ + lwz r3,_CCR(r21) + lwz r4,_LINK(r21) + mtcrf 0xff,r3 + mtlr r4 + REST_GPR(1, r21) + REST_4GPRS(3, r21) + /* we haven't used ctr or xer */ + mtspr SRR1,r23 + mtspr SRR0,r22 + + REST_GPR(20, r21) + REST_2GPRS(22, r21) + lwz r21,GPR21(r21) + SYNC + RFI + +/* + * FP unavailable trap from kernel - print a message, but let + * the task use FP in the kernel until it returns to user mode. + */ +KernelFP: + lwz r3,_MSR(r1) + ori r3,r3,MSR_FP + stw r3,_MSR(r1) /* enable use of FP after return */ + lis r3,86f@h + ori r3,r3,86f@l + mr r4,r2 /* current */ + lwz r5,_NIP(r1) + bl printk + b ret_from_except +86: .string "floating point used in kernel (task=%p, pc=%x)\n" + .align 4 + +/* + * giveup_fpu(tsk) + * Disable FP for the task given as the argument, + * and save the floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + */ + .globl giveup_fpu +giveup_fpu: + mfmsr r5 + ori r5,r5,MSR_FP + mtmsr r5 /* enable use of fpu now */ + cmpi 0,r3,0 + beqlr- /* if no previous owner, done */ + addi r3,r3,THREAD /* want THREAD of task */ + lwz r5,PT_REGS(r3) + cmpi 0,r5,0 + SAVE_32FPRS(0, r3) + mffs fr0 + stfd fr0,THREAD_FPSCR-4(r3) + beq 1f + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r3,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r3 /* disable FP for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#ifndef CONFIG_SMP + li r5,0 + lis r4,last_task_used_math@ha + stw r5,last_task_used_math@l(r4) +#endif /* CONFIG_SMP */ + blr + +#ifdef CONFIG_SMP +secondary_start: + lis r3,0, + mr r4,r24 + bl identify_cpu + bl call_setup_cpu /* Call setup_cpu for this CPU */ + + /* get current */ + HMT_MEDIUM /* Set thread priority to MEDIUM */ + lis r2,current_set@h + ori r2,r2,current_set@l + slwi r24,r24,2 /* get current_set[cpu#] */ + lwzx r2,r2,r24 + + /* stack */ + addi r1,r2,THREAD_SIZE-STACK_FRAME_OVERHEAD + li r0,0 + stw r0,0(r1) + + /* load up the MMU */ + bl load_up_mmu + + /* ptr to phys current thread */ + addi r4,r2,THREAD /* phys address of our thread_struct */ + rlwinm r4,r4,0,0,31 + mtspr SPRG3,r4 + + /* Set up address of Paca in current thread */ + lis r23,xPaca@ha + addi r23,r23,xPaca@l + /* r24 has CPU # * 4 at this point. The Paca is 2048 bytes + long so multiply r24 by 512 to index into the array of Pacas */ + slwi r24,r24,9 + add r23,r23,r24 + rlwinm r23,r23,0,0,31 + mtspr SPRG1,r23 + + li r3,0 + stw r3,PACAKSAVE(r23) /* 0 => r1 has kernel sp */ + + stb r3,PACAPROCENABLED(r23) /* Soft disabled */ + + /* enable MMU and jump to start_secondary */ + + li r4,MSR_KERNEL + ori r4,r4,MSR_EE /* Hard enabled */ + lis r3,start_secondary@h + ori r3,r3,start_secondary@l + mtspr SRR0,r3 + FIX_SRR1(r4,r3) + mtspr SRR1,r4 + RFI +#endif /* CONFIG_SMP */ + +/* + * Load stuff into the MMU. Intended to be called with + * IR=0 and DR=0. + */ +load_up_mmu: + li r0,16 /* load up segment register values */ + mtctr r0 /* for context 0 */ + lis r3,0x2000 /* Ku = 1, VSID = 0 */ + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,0x111 /* increment VSID */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 3b + blr + +/* + * This is where the main kernel code starts. + */ +start_here: + + /* ptr to current */ + + lis r2,init_task@h + ori r2,r2,init_task@l + + /* Set up for using our exception vectors */ + + addi r4,r2,THREAD /* init task's THREAD */ + rlwinm r4,r4,0,0,31 + mtspr SPRG3,r4 + + /* Get address of Paca for processor 0 */ + lis r11,xPaca@ha + addi r11,r11,xPaca@l + rlwinm r11,r11,0,0,31 + mtspr SPRG1,r11 + + li r3,0 + stw r3,PACAKSAVE(r11) /* 0 => r1 has kernel sp */ + + stb r3,PACAPROCENABLED(r11) /* Soft disabled */ + + lis r1,init_thread_union@ha + addi r1,r1,init_thread_union@l + li r0,0 + stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1) + + /* fix klimit for system map */ + lis r6,embedded_sysmap_end@ha + lwz r7,embedded_sysmap_end@l(r6) + + cmpi 0,r7,0 + beq 5f + + lis r6, KERNELBASE@h + add r7,r7,r6 + addi r7,r7,4095 + li r6,0x0FFF + andc r7,r7,r6 + + lis r6,klimit@ha + stw r7,klimit@l(r6) +5: + +/* + * Decide what sort of machine this is and initialize the MMU. + */ + bl early_init /* We have to do this with MMU on */ + + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + li r6,0 /* No cmdline parameters */ + bl platform_init + bl MMU_init + + bl load_up_mmu + + li r4,MSR_KERNEL + ori r4,r4,MSR_EE /* Hard enabled */ + FIX_SRR1(r4,r5) + lis r3,start_kernel@h + ori r3,r3,start_kernel@l + mtspr SRR0,r3 + mtspr SRR1,r4 + RFI /* ensure correct MSR and jump to + start_kernel */ +hash_page: + mflr r21 /* Save LR in r21 */ + + /* + * We hard enable here (but first soft disable) so that the hash_page + * code can spin on the hash_table_lock without problem on a shared + * processor + */ + li r0,0 + stb r0,PACAPROCENABLED(r20) /* Soft disable */ + + mfmsr r0 + ori r0,r0,MSR_EE + mtmsr r0 /* Hard enable */ + + bl create_hpte /* add the hash table entry */ + /* + * Now go back to hard disabled + */ + mfmsr r0 + li r4,0 + ori r4,r4,MSR_EE + andc r0,r0,r4 + mtmsr r0 /* Hard disable */ + + lwz r0,_SOFTE(r1) + mtlr r21 /* restore LR */ + mr r21,r1 /* restore r21 */ + + cmpi 0,r0,0 /* See if we will soft enable in */ + /* fault_exit */ + beq 5f /* if not, skip checks */ + + CHECKANYINT(r4,r5,r6) /* if pending interrupts, process them */ + bne- do_pending_int + +5: stb r0,PACAPROCENABLED(r20) /* Restore soft enable/disable */ + + cmpi 0,r3,0 /* check return code form create_hpte */ + bnelr + +/* + * htab_reloads counts the number of times we have to fault an + * HPTE into the hash table. This should only happen after a + * fork (because fork does a flush_tlb_mm) or a vmalloc or ioremap. + * Where a page is faulted into a process's address space, + * update_mmu_cache gets called to put the HPTE into the hash table + * and those are counted as preloads rather than reloads. + */ + lis r2,htab_reloads@ha + lwz r3,htab_reloads@l(r2) + addi r3,r3,1 + stw r3,htab_reloads@l(r2) + +fault_exit: + + lwz r3,_CCR(r1) + lwz r4,_LINK(r1) + lwz r5,_CTR(r1) + lwz r6,_XER(r1) + + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + + lwz r0,GPR0(r1) + REST_8GPRS(2, r1) + REST_4GPRS(10, r1) + FIX_SRR1(r23,r20) + mtspr SRR1,r23 + mtspr SRR0,r22 + REST_4GPRS(20, r1) + + lwz r1,GPR1(r1) + RFI + +/* + * Set up the segment registers for a new context. + * context in r3 + */ +_GLOBAL(set_context) + mulli r3,r3,897 /* multiply context by skew factor */ + rlwinm r3,r3,4,8,27 /* VSID = (context & 0xfffff) << 4 */ + addis r3,r3,0x6000 /* Set Ks, Ku bits */ + li r0,NUM_USER_SEGMENTS + mtctr r0 + + li r4,0 +3: + mtsrin r3,r4 + addi r3,r3,0x111 /* next VSID */ + rlwinm r3,r3,0,8,3 /* clear out any overflow from VSID field */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 3b + isync + +set_kernel_segregs: + +/* + * Reload the last four segment registers because they + * might have been clobbered by the hypervisor if we + * are running on a shared processor + */ + lis r3,0x2000 /* Set Ku = 1 */ + addi r3,r3,0xCCC /* Set VSID = CCC */ + lis r4,0xC000 /* Set SR = C */ + li r0,4 /* Load regs C, D, E and F */ + mtctr r0 +4: mtsrin r3,r4 + addi r3,r3,0x111 /* increment VSID */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 4b + isync + + blr + + +/* Hypervisor call + * + * Invoke the iSeries hypervisor (PLIC) via the System Call instruction. + * Parameters are passed to this routine in registers r3 - r10 and are + * converted to 64-bit by combining registers. eg. r3 <- r3 + * r4 <- r5,r6, r5 <- r7,r8, r6 <- r9,r10 + * + * r3 contains the HV function to be called + * r5,r6 contain the first 64-bit operand + * r7,r8 contain the second 64-bit operand + * r9,r10 contain the third 64-bit operand + * caller's stack frame +8 contains the fourth 64-bit operand + * caller's stack frame +16 contains the fifth 64-bit operand + * caller's stack frame +24 contains the sixth 64-bit operand + * caller's stack frame +32 contains the seventh 64-bit operand + * + */ + +_GLOBAL(HvCall) +_GLOBAL(HvCall0) +_GLOBAL(HvCall1) +_GLOBAL(HvCall2) +_GLOBAL(HvCall3) +_GLOBAL(HvCall4) +_GLOBAL(HvCall5) +_GLOBAL(HvCall6) +_GLOBAL(HvCall7) + /* + * Stack a frame and save one reg so we can hang on to + * the old MSR + */ + stwu r1,-64(r1) + stw r31,60(r1) + + stw r22,24(r1) + stw r23,28(r1) + stw r24,32(r1) + stw r25,36(r1) + stw r26,40(r1) + stw r27,44(r1) + stw r28,48(r1) + stw r29,52(r1) + + /* + * The hypervisor assumes CR fields 0-4 are volatile, but + * gcc assumes CR fields 2-7 are non-volatile. + * We must save and restore the CR here + */ + mfcr r31 + stw r31,20(r1) + + /* Before we can convert to using 64-bit registers we must + * soft disable external interrupts as the interrupt handlers + * don't preserve the high half of the registers + */ + + mfspr r11,SPRG1 /* Get the Paca pointer */ + lbz r31,PACAPROCENABLED(r11) /* Get soft enable/disable flag */ + li r0,0 + stb r0,PACAPROCENABLED(r11) /* Soft disable */ + + /* Get parameters four through seven */ + + lwz r22,72(r1) + lwz r23,76(r1) + lwz r24,80(r1) + lwz r25,84(r1) + lwz r26,88(r1) + lwz r27,92(r1) + lwz r28,96(r1) + lwz r29,100(r1) + /* Now it is safe to operate on 64-bit registers + * + * Format the operands into the 64-bit registers + * + */ + rldicr r5,r5,32,31 /* r5 = r5 << 32 */ + rldicl r6,r6,0,32 /* r6 = r6 & 0x00000000ffffffff */ + or r4,r5,r6 /* r4 = r5 | r6 */ + rldicr r7,r7,32,31 /* r7 = r7 << 32 */ + rldicl r8,r8,0,32 /* r8 = r8 & 0x00000000ffffffff */ + or r5,r7,r8 /* r5 = r7 | r8 */ + rldicr r9,r9,32,31 /* r9 = r9 << 32 */ + rldicl r10,r10,0,32 /* r10 = r10 & 0x00000000ffffffff */ + or r6,r9,r10 /* r6 = r9 | r10 */ + rldicr r22,r22,32,31 /* r22 = r22 << 32 */ + rldicl r23,r23,0,32 /* r23 = r23 & 0x00000000ffffffff */ + or r7,r22,r23 /* r7 = r22 | r23 */ + rldicr r24,r24,32,31 /* r24 = r24 << 32 */ + rldicl r25,r25,0,32 /* r25 = r25 & 0x00000000ffffffff */ + or r8,r24,r25 /* r8 = r24 | r25 */ + rldicr r26,r26,32,31 /* r26 = r26 << 32 */ + rldicl r27,r27,0,32 /* r27 = r27 & 0x00000000ffffffff */ + or r9,r26,r27 /* r9 = r26 | r27 */ + rldicr r28,r28,32,31 /* r28 = r28 << 32 */ + rldicl r29,r29,0,32 /* r29 = r29 & 0x00000000ffffffff */ + or r10,r28,r29 /* r10 = r28 | r29 */ + + /* + * Extract the hypervisor function call number from R3 + * and format it into the 64-bit R3. + */ + rldicr r0,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */ + rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */ + or r3,r3,r0 /* r3 = r3 | r0 */ + + /* + * r0 = 0xffffffffffffffff indicates a hypervisor call + */ + li r0,-1 /* r1 = 0xffffffffffffffff */ + + /* Invoke the hypervisor via the System Call instruction */ + + sc + + HMT_MEDIUM + + /* Return value in 64-bit R3 + * format it into R3 and R4 + */ + rldicl r4,r3,0,32 /* r4 = r3 & 0x00000000ffffffff */ + rldicl r3,r3,32,32 /* r3 = (r3 >> 32) & 0x00000000ffffffff */ + + /* We are now done with 64-bit registers it is safe to touch + * the stack again. + */ + lwz r22,24(r1) + lwz r23,28(r1) + lwz r24,32(r1) + lwz r25,36(r1) + lwz r26,40(r1) + lwz r27,44(r1) + lwz r28,48(r1) + lwz r29,52(r1) + + /* While we were running in the hypervisor, a decrementer or + * external interrupt may have occured. If we are about to + * enable here, we must check for these and process them + */ + + cmpi 0,r31,0 /* check if going to enable */ + beq 1f /* skip checks if staying disabled */ + + /* Save r3, r4 and LR */ + stw r3,52(r1) + stw r4,48(r1) + mflr r3 + stw r3,44(r1) + + /* enable and check for decrementers/lpEvents */ + mr r3,r31 + bl __restore_flags + + /* Restore r3, r4 and LR */ + lwz r3,44(r1) + mtlr r3 + lwz r3,52(r1) + lwz r4,48(r1) + +1: + /* + * Unstack the frame and restore r31 and the CR + */ + lwz r31,20(r1) + mtcrf 0xff,r31 + lwz r31,60(r1) + lwz r1,0(r1) + + blr + +/* Hypervisor call with return data + * + * Invoke the iSeries hypervisor (PLIC) via the System Call instruction. + * The Hv function ID is passed in r3 + * The address of the return data structure is passed in r4 + * Parameters are passed to this routine in registers r5 - r10 and are + * converted to 64-bit by combining registers. eg. r3 <- r3 + * r4 <- r5,r6, r5 <- r7,r8, r6 <- r9,r10 + * + * r3 contains the HV function to be called + * r4 contains the address of the return data structure + * r5,r6 contain the first 64-bit operand + * r7,r8 contain the second 64-bit operand + * r9,r10 contain the third 64-bit operand + * caller's stack frame +8 contains the fourth 64-bit operand + * caller's stack frame +16 contains the fifth 64-bit operand + * caller's stack frame +24 contains the sixth 64-bit operand + * caller's stack frame +32 contains the seventh 64-bit operand + * + */ + +_GLOBAL(HvCallRet16) +_GLOBAL(HvCall0Ret16) +_GLOBAL(HvCall1Ret16) +_GLOBAL(HvCall2Ret16) +_GLOBAL(HvCall3Ret16) +_GLOBAL(HvCall4Ret16) +_GLOBAL(HvCall5Ret16) +_GLOBAL(HvCall6Ret16) +_GLOBAL(HvCall7Ret16) + + /* + * Stack a frame and save some regs + */ + stwu r1,-64(r1) + stw r31,60(r1) + stw r30,56(r1) + + stw r22,24(r1) + stw r23,28(r1) + stw r24,32(r1) + stw r25,36(r1) + stw r26,40(r1) + stw r27,44(r1) + stw r28,48(r1) + stw r29,52(r1) + + mr r30,r4 /* Save return data address */ + + /* + * The hypervisor assumes CR fields 0-4 are volatile, but + * gcc assumes CR fields 2-7 are non-volatile. + * We must save and restore the CR here + */ + mfcr r31 + stw r31,20(r1) + + /* Before we can convert to using 64-bit registers we must + * soft disable external interrupts as the interrupt handlers + * don't preserve the high half of the registers + */ + + mfspr r11,SPRG1 /* Get the Paca pointer */ + lbz r31,PACAPROCENABLED(r11) /* Get soft enable/disable flag */ + li r0,0 + stb r0,PACAPROCENABLED(r11) /* Soft disable */ + + /* Get parameters four through seven */ + + lwz r22,76(r1) + lwz r23,80(r1) + lwz r24,84(r1) + lwz r25,88(r1) + lwz r26,92(r1) + lwz r27,96(r1) + lwz r28,100(r1) + lwz r29,104(r1) + + /* Now it is safe to operate on 64-bit registers + * + */ + + /* + * Format the operands into the 64-bit registers + */ + + rldicr r5,r5,32,31 /* r5 = r5 << 32 */ + rldicl r6,r6,0,32 /* r6 = r6 & 0x00000000ffffffff */ + or r4,r5,r6 /* r4 = r5 | r6 */ + rldicr r7,r7,32,31 /* r7 = r7 << 32 */ + rldicl r8,r8,0,32 /* r8 = r8 & 0x00000000ffffffff */ + or r5,r7,r8 /* r5 = r7 | r8 */ + rldicr r9,r9,32,31 /* r9 = r9 << 32 */ + rldicl r10,r10,0,32 /* r10 = r10 & 0x00000000ffffffff */ + or r6,r9,r10 /* r6 = r9 | r10 */ + rldicr r22,r22,32,31 /* r22 = r22 << 32 */ + rldicl r23,r23,0,32 /* r23 = r23 & 0x00000000ffffffff */ + or r7,r22,r23 /* r7 = r22 | r23 */ + rldicr r24,r24,32,31 /* r24 = r24 << 32 */ + rldicl r25,r25,0,32 /* r25 = r25 & 0x00000000ffffffff */ + or r8,r24,r25 /* r8 = r24 | r25 */ + rldicr r26,r26,32,31 /* r26 = r26 << 32 */ + rldicl r27,r27,0,32 /* r27 = r27 & 0x00000000ffffffff */ + or r9,r26,r27 /* r9 = r26 | r27 */ + rldicr r28,r28,32,31 /* r28 = r28 << 32 */ + rldicl r29,r29,0,32 /* r29 = r29 & 0x00000000ffffffff */ + or r10,r28,r29 /* r10 = r28 | r29 */ + /* + * Extract the hypervisor function call number from R3 + * and format it into the 64-bit R3. + */ + rldicr r0,r3,32,15 /* r4 = (r3 << 32) & 0xffff000000000000 */ + rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */ + or r3,r3,r0 /* r3 = r3 | r4 */ + + /* + * r0 = 0xffffffffffffffff indicates a hypervisor call + */ + li r0,-1 /* r1 = 0xffffffffffffffff */ + + /* Invoke the hypervisor via the System Call instruction */ + + sc + + HMT_MEDIUM + + /* Return values in 64-bit R3, R4, R5 and R6 + * place R3 and R4 into data structure, R5 into R3,R4 + */ + rldicl r6,r3,32,32 /* r6 = (r3 >> 32) & 0x00000000ffffffff */ + rldicl r7,r3,0,32 /* r7 = r3 & 0x00000000ffffffff */ + rldicl r8,r4,32,32 /* r8 = (r4 >> 32) & 0x00000000ffffffff */ + rldicl r9,r4,0,32 /* r9 = r4 & 0x00000000ffffffff */ + + rldicl r4,r5,0,32 /* r4 = r5 & 0x00000000ffffffff */ + rldicl r3,r5,32,32 /* r3 = (r5 >> 32) & 0x00000000ffffffff */ + + /* We are now done with 64-bit registers it is safe to touch + * the stack again. + */ + stw r6,0(r30) /* Save returned data */ + stw r7,4(r30) + stw r8,8(r30) + stw r9,12(r30) + + lwz r22,24(r1) + lwz r23,28(r1) + lwz r24,32(r1) + lwz r25,36(r1) + lwz r26,40(r1) + lwz r27,44(r1) + lwz r28,48(r1) + lwz r29,52(r1) + + /* While we were running in the hypervisor, a decrementer or + * external interrupt may have occured. If we are about to + * enable here, we must check for these and process them + */ + + cmpi 0,r31,0 /* check if going to enable */ + beq 1f /* skip checks if staying disabled */ + + /* Save r3, r4 and LR */ + stw r3,48(r1) + stw r4,44(r1) + mflr r3 + stw r3,40(r1) + + /* enable and check for decrementers/lpEvents */ + mr r3,r31 + bl __restore_flags + + lwz r3,40(r1) + mtlr r3 + lwz r3,48(r1) + lwz r4,44(r1) + +1: + /* + * Unstack the frame and restore r30, r31 and CR + */ + lwz r31,20(r1) + mtcrf 0xff,r31 + lwz r30,56(r1) + lwz r31,60(r1) + lwz r1,0(r1) + + blr + + +/* Hypervisor call (use no stack) + * + * These functions must be called with interrupts soft disabled. + * The caller is responsible for saving the non-volatile CR + * The operands should already be formatted into the 64-bit registers + * + * Invoke the iSeries hypervisor (PLIC) via the System Call instruction. + * + * r3 contains the HV function to be called + * r4 contains the first 64-bit operand + * r5 contains the second 64-bit operand + * r6 contains the third 64-bit operand + * r7 contains the fourth 64-bit operand + * r8 contains the fifth 64-bit operand + * r9 contains the sixth 64-bit operand + * r10 contains the seventh 64-bit operand + * + * data is returned in 64-bit registers r3-r6 + * + */ + +_GLOBAL(HvXCall) +_GLOBAL(HvXCall0) +_GLOBAL(HvXCall1) +_GLOBAL(HvXCall2) +_GLOBAL(HvXCall3) + /* + * Extract the hypervisor function call number from R3 + * and format it into the 64-bit R3. + */ + rldicr r0,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */ + rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */ + or r3,r3,r0 /* r3 = r3 | r0 */ + + /* + * r0 = 0xffffffffffffffff indicates a hypervisor call + */ + li r0,-1 /* r1 = 0xffffffffffffffff */ + + /* Invoke the hypervisor via the System Call instruction */ + + sc + + blr + +_GLOBAL(__setup_cpu_power3) + blr +_GLOBAL(__setup_cpu_power4) + blr +_GLOBAL(__setup_cpu_generic) + blr + +_GLOBAL(iSeries_check_intr) + mflr r31 + lwz r5,_SOFTE(r1) + cmpi 0,r5,0 + beqlr + mfspr r5,SPRG1 + lbz r5,PACAPROCENABLED(r5) + cmpi 0,r5,0 + bnelr + /* Check for lost decrementer interrupts. + * (If decrementer popped while we were in the hypervisor) + * (calls timer_interrupt if so) + */ +3: CHECKDECR(r4,r5) + /* Check for pending interrupts. If no interrupts pending, + * then CR0 = "eq" and r4 == 0 + * (kills registers r5 and r6) + */ + beq+ 1f + addi r3,r1,STACK_FRAME_OVERHEAD + bl timer_interrupt +1: + CHECKLPQUEUE(r4,r5,r6) + beq+ 2f + addi r3,r1,STACK_FRAME_OVERHEAD + bl do_IRQ + b 3b + +2: mtlr r31 + blr + +/* + * Fake an interrupt from kernel mode. + * This is used when enable_irq loses an interrupt. + * We only fill in the stack frame minimally. + */ +_GLOBAL(fake_interrupt) + mflr r0 + stw r0,4(r1) + stwu r1,-INT_FRAME_SIZE(r1) + stw r0,_NIP(r1) + stw r0,_LINK(r1) + mfmsr r3 + stw r3,_MSR(r1) + li r0,0x0fac + stw r0,TRAP(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + li r4,1 + bl do_IRQ + addi r1,r1,INT_FRAME_SIZE + lwz r0,4(r1) + + mtlr r0 + blr + +/* + * Fake a decrementer from kernel mode. + * This is used when the decrementer pops in + * the hypervisor. We only fill in the stack + * frame minimally + */ +_GLOBAL(fake_decrementer) + mflr r0 + stw r0,4(r1) + stwu r1,-INT_FRAME_SIZE(r1) + stw r0,_NIP(r1) + stw r0,_LINK(r1) + mfmsr r3 + stw r3,_MSR(r1) + li r0,0x0fac + stw r0,TRAP(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + bl timer_interrupt + addi r1,r1,INT_FRAME_SIZE + lwz r0,4(r1) + + mtlr r0 + blr + +_GLOBAL(create_hpte) + stwu r1,-INT_FRAME_SIZE(r1) + stw r0,GPR0(r1) + lwz r0,0(r1) + stw r0,GPR1(r1) + /* r3-r13 are caller saved */ + stw r2,GPR2(r1) + SAVE_8GPRS(4, r1) + SAVE_2GPRS(12, r1) + SAVE_4GPRS(20,r1) + mfspr r20,XER + mfctr r22 + stw r20,_XER(r1) + stw r22,_CTR(r1) + mflr r20 + mfmsr r22 + stw r20,_NIP(r1) + stw r22,_MSR(r1) + stw r20,_LINK(r1) + bl iSeries_create_hpte + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + REST_8GPRS(4, r1) + REST_2GPRS(12, r1) + lwz r20,_NIP(r1) + lwz r22,_XER(r1) + mtlr r20 + mtspr XER,r22 + lwz r20,_CTR(r1) + mtctr r20 + + REST_4GPRS(20,r1) + addi r1,r1,INT_FRAME_SIZE + blr + +### +### extern void abort(void) +### +### Invoke the hypervisor to kill the partition. +### + +_GLOBAL(abort) + + +/* + * We put a few things here that have to be page-aligned. + * This stuff goes at the beginning of the data segment, + * which is page-aligned. + */ + .data + .globl sdata +sdata: + .globl empty_zero_page +empty_zero_page: + .space 4096 + + .globl swapper_pg_dir +swapper_pg_dir: + .space 4096 + +/* + * This space gets a copy of optional info passed to us by the bootstrap + * Used to pass parameters into the kernel like root=/dev/sda1, etc. + */ + .globl cmd_line +cmd_line: + .space 512 diff -Nru a/arch/ppc/kernel/iSeries_misc.S b/arch/ppc/kernel/iSeries_misc.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/iSeries_misc.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,469 @@ + /* + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com) + * updated by Dave Boutcher (boutcher@us.ibm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ppc_defs.h" +#include "iSeries_asm.h" + + .text + .align 5 + +#ifdef CONFIG_SMP + .comm hash_table_lock,4 +#endif /* CONFIG_SMP */ + +_GLOBAL(is_msr_enabled) + mfmsr r3 + andi. r3,r3,MSR_EE + beqlr /* Return r3=0 indicating disabled */ + li r3,1 + blr /* Return r3=1 indicating enabled */ + +_GLOBAL(is_soft_enabled) + mfspr r3,SPRG1 + lbz r3,PACAPROCENABLED(r3) + blr + +_GLOBAL(get_tb64) + /* hard disable because we are using a 64-bit register + * and we don't want it to get trashed in an interrupt + * handler + */ + + mfmsr r5 + rlwinm r0,r5,0,17,15 /* clear MSR_EE in r0 */ + mtmsr r0 /* hard disable */ + + mftb r3 + rldicl r4,r3,0,32 + rldicl r3,r3,32,32 + + mtmsr r5 /* restore MSR_EE */ + blr + +/* void __save_flags(unsigned long *flags) */ +_GLOBAL(__save_flags_ptr) + mfspr r4,SPRG1 /* Get Paca pointer */ + lbz r4,PACAPROCENABLED(r4) + stw r4,0(r3) + blr +_GLOBAL(__save_flags_ptr_end) + +/* void __restore_flags(unsigned long flags) */ +_GLOBAL(__restore_flags) + cmpi 0,r3,0 /* Are we enabling? */ + beq 0f /* No - then skip interrupt checks */ + +3: + mfspr r4,SPRG1 + lbz r4,PACAPROCENABLED(r4) + cmpi 0,r4,0 /* Are we already enabled? */ + bne 0f /* Yes - then skip interrupt checks */ + + CHECKDECR(r4,r5) + bne- do_fake_decrementer + + CHECKLPQUEUE(r4,r5,r6) + bne- do_lost_interrupts +2: + mfmsr r0 + rlwinm r0,r0,0,17,15 /* hard disable */ + mtmsr r0 + + CHECKANYINT(r4,r5,r6) + ori r0,r0,MSR_EE + beq 1f + mtmsr r0 /* hard enable */ + b 3b /* process more interrupts */ +1: + mfspr r4,SPRG1 + stb r3,PACAPROCENABLED(r4) + mtmsr r0 /* hard enable */ + blr +0: + mfspr r4,SPRG1 + stb r3,PACAPROCENABLED(r4) + blr +_GLOBAL(__restore_flags_end) + +_GLOBAL(__cli) + mfspr r4,SPRG1 + li r3,0 + stb r3,PACAPROCENABLED(r4) + blr /* Done */ +_GLOBAL(__cli_end) + +_GLOBAL(__sti) + li r3,1 + b __restore_flags +_GLOBAL(__sti_end) + +/* + * We were about to enable interrupts but we have to simulate + * some interrupts that were lost by enable_irq first. + */ +_GLOBAL(do_lost_interrupts) + stwu r1,-32(r1) + mflr r0 + stw r0,36(r1) + stw r3,28(r1) +1: bl fake_interrupt + bl check_softirqs + mfmsr r0 + rlwinm r0,r0,0,17,15 /* hard disable */ + mtmsr r0 + + CHECKANYINT(r4,r5,r6) + ori r0,r0,MSR_EE + beq 2f + mtmsr r0 /* hard enable */ + b 1b /* process more interrupts */ + +2: lwz r3,28(r1) + mfspr r4,SPRG1 + stb r3,PACAPROCENABLED(r4) /* restore soft interrupt state */ + mtmsr r0 /* hard enable */ + lwz r0,36(r1) + mtlr r0 + + addi r1,r1,32 + blr + +/* + * Simulate a decrementer interrupt + */ +do_fake_decrementer: + stwu r1,-32(r1) + mflr r0 + stw r0,36(r1) + stw r3,28(r1) + bl fake_decrementer + bl check_softirqs + lwz r0,36(r1) + mtlr r0 + + lwz r3,28(r1) + addi r1,r1,32 + + mfmsr r0 /* hard disable */ + rlwinm r0,r0,0,17,15 + mtmsr r0 + + CHECKANYINT(r4,r5,r6) /* Check for any interrupts pending */ + ori r0,r0,MSR_EE + beq 1f + mtmsr r0 /* hard enable */ + b do_lost_interrupts /* Handle more interrupts */ + +1: mfspr r4,SPRG1 + stb r3,PACAPROCENABLED(r4) /* soft enable */ + mtmsr r0 /* hard enable */ + blr + +/* + * do softirqs if necessary + */ +check_softirqs: + stwu r1,-32(r1) + mflr r0 + stw r0,36(r1) + + lis r4,irq_stat@ha + addi r4,r4,irq_stat@l +#ifdef CONFIG_SMP + lwz r3,CPU(r2) + slwi r3,r3,7 + add r4,r4,r3 +#endif + lwz r5,0(r4) + lwz r4,4(r4) + and. r5,r5,r4 + beq+ 2f + bl do_softirq +2: + lwz r0,36(r1) + mtlr r0 + + addi r1,r1,32 + blr + + +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * + * flush_icache_range(unsigned long start, unsigned long stop) + */ + +_GLOBAL(flush_icache_range) + + +/* + * Flush the data cache to memory + * + * Different models of iSeries's have different cache line sizes + * and in some cases i-cache and d-cache line sizes differ from + * each other. + */ + + lis r7,iSeries_dcache_line_size@ha + lhz r7,iSeries_dcache_line_size@l(r7) + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 /* ensure we get enough */ + lis r9,iSeries_log_dcache_line_size@ha + lhz r9,iSeries_log_dcache_line_size@l(r9) + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +1: dcbst 0,r6 + add r6,r6,r7 + bdnz 1b + sync + +/* Now invalidate the instruction cache */ + + lis r7,iSeries_icache_line_size@ha + lhz r7,iSeries_icache_line_size@l(r7) + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 + lis r9,iSeries_log_icache_line_size@ha + lhz r9,iSeries_log_icache_line_size@l(r9) + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +2: icbi 0,r6 + add r6,r6,r7 + bdnz 2b + sync + isync + blr + + +/* + * Like above, but only do the D-cache. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_dcache_range) + +/* + * Flush the data cache to memory + * + * Different models of iSeries's have different cache line sizes + */ + + lis r7,iSeries_dcache_line_size@ha + lhz r7,iSeries_dcache_line_size@l(r7) + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 /* ensure we get enough */ + lis r9,iSeries_log_dcache_line_size@ha + lhz r9,iSeries_log_dcache_line_size@l(r9) + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +0: dcbst 0,r6 + add r6,r6,r7 + bdnz 0b + sync + blr + + +/* + * Flush a particular page from the data cache to RAM. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * + * void __flush_page_to_ram(void *page) + * void __flush_dcache_icache(void *page) + */ +_GLOBAL(__flush_page_to_ram) +_GLOBAL(__flush_dcache_icache) + +/* + * Flush the data cache to memory + * + * Different models of iSeries's have different cache line sizes + */ + +/* Flush the dcache */ + + rlwinm r3,r3,0,0,19 /* Page align */ + lis r4,iSeries_dcache_lines_per_page@ha + lhz r4,iSeries_dcache_lines_per_page@l(r4) + lis r5,iSeries_dcache_line_size@ha + lhz r5,iSeries_dcache_line_size@l(r5) + mr r6,r3 + mtctr r4 +0: dcbst 0,r6 + add r6,r6,r5 + bdnz 0b + sync + +/* Now invalidate the icache */ + + lis r4,iSeries_icache_lines_per_page@ha + lhz r4,iSeries_icache_lines_per_page@l(r4) + lis r5,iSeries_icache_line_size@ha + lhz r5,iSeries_icache_line_size@l(r5) + mtctr r4 +1: icbi 0,r3 + add r3,r3,r5 + bdnz 1b + sync + isync + blr + + +/* + * Flush a particular page from the instruction cache. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * + * void __flush_icache_page(void *page) + */ +_GLOBAL(__flush_icache_page) + + +/* + * Different models of iSeries's have different cache line sizes + */ + +/* Invalidate the icache */ + + rlwinm r3,r3,0,0,19 /* Page align */ + lis r4,iSeries_icache_lines_per_page@ha + lhz r4,iSeries_icache_lines_per_page@l(r4) + lis r5,iSeries_icache_line_size@ha + lhz r5,iSeries_icache_line_size@l(r5) + mtctr r4 +1: icbi 0,r3 + add r3,r3,r5 + bdnz 1b + sync + isync + blr + +/* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + + rlwinm r3,r3,0,0,19 /* Page align */ + lis r4,iSeries_dcache_lines_per_page@ha + lhz r4,iSeries_dcache_lines_per_page@l(r4) + lis r5,iSeries_dcache_line_size@ha + lhz r5,iSeries_dcache_line_size@l(r5) + mtctr r4 +0: dcbz 0,r3 + add r3,r3,r5 + bdnz 0b + blr + + +/* + * Copy a whole page. We use the dcbz instruction on the destination + * to reduce memory traffic (it eliminates the unnecessary reads of + * the destination into cache). This requires that the destination + * is cacheable. + */ +#define COPY_16_BYTES \ + lwz r6,4(r4); \ + lwz r7,8(r4); \ + lwz r8,12(r4); \ + lwzu r9,16(r4); \ + stw r6,4(r3); \ + stw r7,8(r3); \ + stw r8,12(r3); \ + stwu r9,16(r3) + +_GLOBAL(copy_page) + + rlwinm r3,r3,0,0,19 /* Page align */ + rlwinm r4,r4,0,0,19 + lis r5,iSeries_dcache_lines_per_page@ha + lhz r5,iSeries_dcache_lines_per_page@l(r5) + lis r6,iSeries_dcache_line_size@ha + lhz r0,iSeries_dcache_line_size@l(r6) + mtctr r5 + addi r3,r3,-4 + addi r4,r4,-4 + li r10,4 + + cmpi 0,r0,32 + beq do_32_byte_line + cmpi 0,r0,64 + beq do_64_byte_line + cmpi 0,r0,128 + beq do_128_byte_line + + /* We don't have code specifically for this cache line size */ + /* Assume that the cache line size is at least 16 (and of */ + /* course a multiple of 16) */ + /* This code will work for all power-of-2 cache line sizes */ + /* from 16 to 4096 */ + +1: mr r5,r0 + dcbz r10,r3 +0: COPY_16_BYTES + addi r5,r5,-16 + or. r5,r5,r5 + bne 0b + bdnz 1b + blr + +do_32_byte_line: + dcbz r10,r3 + COPY_16_BYTES + COPY_16_BYTES + bdnz do_32_byte_line + blr + +do_64_byte_line: + dcbz r10,r3 + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + bdnz do_64_byte_line + blr + +do_128_byte_line: + dcbz r10,r3 + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + bdnz do_128_byte_line + blr diff -Nru a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c --- a/arch/ppc/kernel/idle.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/idle.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.idle.c 1.16 10/16/01 15:58:42 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Idle daemon for PowerPC. Idle daemon will handle any action @@ -32,6 +32,20 @@ #include #include #include +#ifdef CONFIG_PPC_ISERIES +#include +#include +#include +#include + +static void yield_shared_processor(void); +static void run_light_on(int on); + +extern unsigned long yield_count; + +#else /* CONFIG_PPC_ISERIES */ +#define run_light_on(x) do { } while (0) +#endif /* CONFIG_PPC_ISERIES */ void zero_paged(void); void power_save(void); @@ -53,21 +67,22 @@ do_power_save = 1; /* endless loop with no priority at all */ - current->nice = 20; - init_idle(); for (;;) { +#ifdef CONFIG_PPC_ISERIES + if (!current->need_resched) { + /* Turn off the run light */ + run_light_on(0); + yield_shared_processor(); + } + HMT_low(); +#endif #ifdef CONFIG_SMP - if (!do_power_save) { - /* - * Deal with another CPU just having chosen a thread to - * run here: - */ - int oldval = xchg(¤t->need_resched, -1); - - if (!oldval) { - while (need_resched()) - barrier(); /* Do Nothing */ + if (!need_resched()) { + set_thread_flag(TIF_POLLING_NRFLAG); + while (!test_thread_flag(TIF_NEED_RESCHED)) + barrier(); + clear_thread_flag(TIF_POLLING_NRFLAG); } } #endif @@ -75,9 +90,17 @@ power_save(); if (need_resched()) { + run_light_on(1); schedule(); check_pgt_cache(); } +#ifdef CONFIG_PPC_ISERIES + else { + run_light_on(0); + yield_shared_processor(); + HMT_low(); + } +#endif /* CONFIG_PPC_ISERIES */ } return 0; } @@ -108,6 +131,7 @@ register unsigned long tmp; asm ( "101:lwarx %1,0,%3\n" /* reserve zero_cache */ " lwz %0,0(%1)\n" /* get next -- new zero_cache */ + PPC405_ERR77(0,%3) " stwcx. %0,0,%3\n" /* update zero_cache */ " bne- 101b\n" /* if lost reservation try again */ : "=&r" (tmp), "=&r" (page), "+m" (zero_cache) @@ -205,6 +229,7 @@ #ifdef CONFIG_SMP " sync\n" /* let store settle */ #endif + PPC405_ERR77(0,%2) " stwcx. %3,0,%2\n" /* update zero_cache in mem */ " bne- 101b\n" /* if lost reservation try again */ : "=&r" (tmp), "+m" (zero_quicklist) @@ -228,6 +253,13 @@ void power_save(void) { unsigned long hid0; + int nap = powersave_nap; + + /* 7450 has no DOZE mode mode, we return if powersave_nap + * isn't enabled + */ + if (!nap && cur_cpu_spec[smp_processor_id()]->cpu_features & CPU_FTR_SPEC7450) + return; /* * Disable interrupts to prevent a lost wakeup * when going to sleep. This is necessary even with @@ -255,3 +287,64 @@ _nmask_and_or_msr(0, MSR_EE); } +#ifdef CONFIG_PPC_ISERIES + +extern void fake_interrupt(void); +extern u64 get_tb64(void); + +void run_light_on(int on) +{ + unsigned long CTRL; + + CTRL = mfspr(CTRLF); + CTRL = on? (CTRL | RUNLATCH): (CTRL & ~RUNLATCH); + mtspr(CTRLT, CTRL); +} + +void yield_shared_processor(void) +{ + struct Paca *paca; + u64 tb; + + /* Poll for I/O events */ + __cli(); + __sti(); + + paca = (struct Paca *)mfspr(SPRG1); + if ( paca->xLpPaca.xSharedProc ) { + HvCall_setEnabledInterrupts( HvCall_MaskIPI | + HvCall_MaskLpEvent | + HvCall_MaskLpProd | + HvCall_MaskTimeout ); + + /* + * Check here for any of the above pending... + * IPI and Decrementers are indicated in ItLpPaca + * LpEvents are indicated on the LpQueue + * + * Disabling/enabling will check for LpEvents, IPIs + * and decrementers + */ + __cli(); + __sti(); + + ++yield_count; + + /* Get current tb value */ + tb = get_tb64(); + /* Compute future tb value when yield will expire */ + tb += tb_ticks_per_jiffy; + HvCall_yieldProcessor( HvCall_YieldTimed, tb ); + + /* Check here for any of the above pending or timeout expired*/ + __cli(); + /* + * The decrementer stops during the yield. Just force + * a fake decrementer now and the timer_interrupt + * code will straighten it all out + */ + paca->xLpPaca.xDecrInt = 1; + __sti(); + } +} +#endif /* CONFIG_PPC_ISERIES */ diff -Nru a/arch/ppc/kernel/indirect_pci.c b/arch/ppc/kernel/indirect_pci.c --- a/arch/ppc/kernel/indirect_pci.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/indirect_pci.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.indirect_pci.c 1.10 09/08/01 15:47:42 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * Support for indirect PCI bridges. @@ -24,8 +24,6 @@ #include #include -#include "pci.h" - #define cfg_read(val, addr, type, op) *val = op((type)(addr)) #define cfg_write(val, addr, type, op) op((type *)(addr), (val)) @@ -35,9 +33,13 @@ { \ struct pci_controller *hose = dev->sysdata; \ \ + if (ppc_md.pci_exclude_device) \ + if (ppc_md.pci_exclude_device(dev->bus->number, dev->devfn)) \ + return PCIBIOS_DEVICE_NOT_FOUND; \ + \ out_be32(hose->cfg_addr, \ ((offset & 0xfc) << 24) | (dev->devfn << 16) \ - | (dev->bus->number << 8) | 0x80); \ + | ((dev->bus->number - hose->bus_offset) << 8) | 0x80); \ cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ return PCIBIOS_SUCCESSFUL; \ } diff -Nru a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c --- a/arch/ppc/kernel/irq.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/kernel/irq.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.irq.c 1.32 08/24/01 20:07:37 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * arch/ppc/kernel/irq.c @@ -51,20 +51,15 @@ #include #include -#include #include #include #include #include -#include #include #include -#include -#include -#include #include -#include "local_irq.h" +#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) extern atomic_t ipi_recv; extern atomic_t ipi_sent; @@ -190,9 +185,6 @@ * now, this is what I need. -- Dan */ #define request_irq request_8xxirq -#elif defined(CONFIG_APUS) -#define request_irq request_sysirq -#define free_irq sys_free_irq #endif void free_irq(unsigned int irq, void* dev_id) @@ -374,15 +366,12 @@ int show_interrupts(struct seq_file *p, void *v) { -#ifdef CONFIG_APUS - return show_apus_interrupts(p, v); -#else int i, j; struct irqaction * action; seq_puts(p, " "); for (j=0; jtypename ); + if (irq_desc[i].handler) + seq_printf(p, " %s ", irq_desc[i].handler->typename); else seq_puts(p, " None "); seq_printf(p, "%s", (irq_desc[i].status & IRQ_LEVEL) ? "Level " : "Edge "); - seq_printf(p, " %s",action->name); - for (action=action->next; action; action = action->next) { + seq_printf(p, " %s", action->name); + for (action = action->next; action; action = action->next) seq_printf(p, ", %s", action->name); - } seq_putc(p, '\n'); } #ifdef CONFIG_TAU_INT if (tau_initialized){ seq_puts(p, "TAU: "); for (j = 0; j < smp_num_cpus; j++) - seq_printf(p, "%10u ", - tau_interrupts(j)); + seq_printf(p, "%10u ", tau_interrupts(j)); seq_puts(p, " PowerPC Thermal Assist (cpu temp)\n"); } #endif #ifdef CONFIG_SMP /* should this be per processor send/receive? */ seq_printf(p, "IPI (recv/sent): %10u/%u\n", - atomic_read(&ipi_recv), atomic_read(&ipi_sent)); + atomic_read(&ipi_recv), atomic_read(&ipi_sent)); #endif seq_printf(p, "BAD: %10u\n", ppc_spurious_interrupts); return 0; -#endif /* CONFIG_APUS */ } static inline void @@ -535,31 +521,35 @@ spin_unlock(&desc->lock); } +#ifndef CONFIG_PPC_ISERIES /* iSeries version is in iSeries_pic.c */ int do_IRQ(struct pt_regs *regs) { int cpu = smp_processor_id(); - int irq; - hardirq_enter(cpu); - - /* every arch is required to have a get_irq -- Cort */ - irq = ppc_md.get_irq(regs); + int irq, first = 1; + hardirq_enter( cpu ); - if (irq >= 0) { - ppc_irq_dispatch_handler( regs, irq ); - } else if (irq != -2) { - /* -2 means ignore, already handled */ - if (ppc_spurious_interrupts < 10) - printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", - irq, regs->nip); + /* + * Every platform is required to implement ppc_md.get_irq. + * This function will either return an irq number or -1 to + * indicate there are no more pending. But the first time + * through the loop this means there wasn't and IRQ pending. + * The value -2 is for buggy hardware and means that this IRQ + * has already been handled. -- Tom + */ + while ((irq = ppc_md.get_irq(regs)) >= 0) { + ppc_irq_dispatch_handler(regs, irq); + first = 0; + } + if (irq != -2 && first) /* That's not SMP safe ... but who cares ? */ ppc_spurious_interrupts++; - } hardirq_exit( cpu ); if (softirq_pending(cpu)) do_softirq(); return 1; /* lets ret_from_int know we can do checks */ } +#endif /* CONFIG_PPC_ISERIES */ unsigned long probe_irq_on (void) { @@ -591,32 +581,21 @@ #ifdef CONFIG_SMP unsigned char global_irq_holder = NO_PROC_ID; unsigned volatile long global_irq_lock; /* pendantic :long for set_bit--RR*/ -atomic_t global_irq_count; atomic_t global_bh_count; static void show(char * str) { - int i; - unsigned long *stack; int cpu = smp_processor_id(); printk("\n%s, CPU %d:\n", str, cpu); - printk("irq: %d [%d %d]\n", - atomic_read(&global_irq_count), + printk("irq: [%d %d]\n", local_irq_count(0), local_irq_count(1)); printk("bh: %d [%d %d]\n", atomic_read(&global_bh_count), local_bh_count(0), local_bh_count(1)); - stack = (unsigned long *) &str; - for (i = 40; i ; i--) { - unsigned long x = *++stack; - if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) { - printk("<[%08lx]> ", x); - } - } } static inline void wait_on_bh(void) @@ -643,11 +622,9 @@ * for bottom half handlers unless we're * already executing in one.. */ - if (!atomic_read(&global_irq_count)) { - if (local_bh_count(cpu) - || !atomic_read(&global_bh_count)) - break; - } + if (!irqs_running()) + if (local_bh_count(cpu) || !spin_is_locked(&global_bh_lock)) + break; /* Duh, we have to loop. Release the lock to avoid deadlocks */ clear_bit(0,&global_irq_lock); @@ -658,16 +635,19 @@ count = ~0; } __sti(); - /* don't worry about the lock race Linus found - * on intel here. -- Cort + /* + * We have to allow irqs to arrive between __sti and __cli + * Some cpus apparently won't cause the interrupt + * for several instructions. We hope that isync will + * catch this --Troy */ + __asm__ __volatile__ ("isync"); __cli(); - if (atomic_read(&global_irq_count)) + if (irqs_running()) continue; if (global_irq_lock) continue; - if (!local_bh_count(cpu) - && atomic_read(&global_bh_count)) + if (!local_bh_count(cpu) && spin_is_locked(&global_bh_lock)) continue; if (!test_and_set_bit(0,&global_irq_lock)) break; @@ -698,7 +678,7 @@ */ void synchronize_irq(void) { - if (atomic_read(&global_irq_count)) { + if (irqs_running()) { /* Stupid approach */ cli(); sti(); diff -Nru a/arch/ppc/kernel/l2cr.S b/arch/ppc/kernel/l2cr.S --- a/arch/ppc/kernel/l2cr.S Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/l2cr.S Tue Feb 19 18:08:57 2002 @@ -30,13 +30,19 @@ *********** Thu, July 13, 2000. - Terry: Added isync to correct for an errata. + + 22 August 2001. + - DanM: Finally added the 7450 patch I've had for the past + several months. The L2CR is similar, but I'm going + to assume the user of this functions knows what they + are doing. Author: Terry Greeniaus (tgree@phys.ualberta.ca) Please e-mail updates to this file to me, thanks! */ #include #include -#include "ppc_asm.h" +#include /* Usage: @@ -71,6 +77,15 @@ features, such as L2DO which caches only data, or L2TS which causes cache pushes from the L1 cache to go to the L2 cache instead of to main memory. + +IMPORTANT: + Starting with the 7450, the bits in this register have moved + or behave differently. The Enable, Parity Enable, Size, + and L2 Invalidate are the only bits that have not moved. + The size is read-only for these processors with internal L2 + cache, and the invalidate is a control as well as status. + -- Dan + */ /* * Summary: this procedure ignores the L2I bit in the value passed in, @@ -115,6 +130,8 @@ /**** Might be a good idea to set L2DO here - to prevent instructions from getting into the cache. But since we invalidate the next time we enable the cache it doesn't really matter. + Don't do this unless you accomodate all processor variations. + The bit moved on the 7450..... ****/ lis r4,0x0002 @@ -159,12 +176,21 @@ sync isync /* For errata */ +BEGIN_FTR_SECTION + /* On the 7450, we wait for the L2I bit to clear...... + */ +10: mfspr r3,L2CR + andis. r4,r3,0x0020 + bne 10b + b 11f +END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450) + /* Wait for the invalidation to complete */ 3: mfspr r3,L2CR rlwinm. r4,r3,0,31,31 bne 3b - rlwinm r3,r3,0,11,9 /* Turn off the L2I bit */ +11: rlwinm r3,r3,0,11,9 /* Turn off the L2I bit */ sync mtspr L2CR,r3 sync diff -Nru a/arch/ppc/kernel/local_irq.h b/arch/ppc/kernel/local_irq.h --- a/arch/ppc/kernel/local_irq.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,22 +0,0 @@ -/* - * BK Id: SCCS/s.local_irq.h 1.7 05/17/01 18:14:21 cort - */ - -#ifndef _PPC_KERNEL_LOCAL_IRQ_H -#define _PPC_KERNEL_LOCAL_IRQ_H - -#include -#include -#include -#include -#include - -void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); - -#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) - -extern int ppc_spurious_interrupts; -extern int ppc_second_irq; -extern struct irqaction *ppc_irq_action[NR_IRQS]; - -#endif /* _PPC_KERNEL_LOCAL_IRQ_H */ diff -Nru a/arch/ppc/kernel/m8260_setup.c b/arch/ppc/kernel/m8260_setup.c --- a/arch/ppc/kernel/m8260_setup.c Tue Feb 19 18:09:00 2002 +++ b/arch/ppc/kernel/m8260_setup.c Tue Feb 19 18:09:00 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.m8260_setup.c 1.30 11/13/01 21:26:07 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/arch/ppc/kernel/setup.c @@ -45,8 +45,9 @@ #include #include #include - +#include #include + #include "ppc8260_pic.h" static int m8260_set_rtc_time(unsigned long time); @@ -98,17 +99,25 @@ static static int m8260_set_rtc_time(unsigned long time) { +#ifdef CONFIG_TQM8260 + ((immap_t *)IMAP_ADDR)->im_sit.sit_tmcnt = time; + ((immap_t *)IMAP_ADDR)->im_sit.sit_tmcntsc = 0x3; +#else rtc_time = time; +#endif return(0); } static unsigned long m8260_get_rtc_time(void) { - +#ifdef CONFIG_TQM8260 + return ((immap_t *)IMAP_ADDR)->im_sit.sit_tmcnt; +#else /* Get time from the RTC. */ return((unsigned long)rtc_time); +#endif } static void @@ -121,8 +130,11 @@ * of the reset vector. If that doesn't work for you, change this * or the reboot program to send a proper address. */ +#ifdef CONFIG_TQM8260 + startaddr = 0x40000104; +#else startaddr = 0xff000104; - +#endif if (cmd != NULL) { if (!strncmp(cmd, "startaddr=", 10)) startaddr = simple_strtoul(&cmd[10], NULL, 0); @@ -150,14 +162,13 @@ bd_t *bp; bp = (bd_t *)__res; - + seq_printf(m, "core clock\t: %d MHz\n" "CPM clock\t: %d MHz\n" "bus clock\t: %d MHz\n", bp->bi_intfreq / 1000000, bp->bi_cpmfreq / 1000000, bp->bi_busfreq / 1000000); - return 0; } @@ -178,7 +189,7 @@ #endif for ( i = 0 ; i < NR_SIU_INTS ; i++ ) irq_desc[i].handler = &ppc8260_pic; - + /* Initialize the default interrupt mapping priorities, * in case the boot rom changed something on us. */ @@ -197,7 +208,7 @@ { bd_t *binfo; extern unsigned char __res[]; - + binfo = (bd_t *)__res; return binfo->bi_memsize; @@ -219,22 +230,20 @@ platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { + parse_bootinfo(find_bootinfo()); if ( r3 ) memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); - + #ifdef CONFIG_BLK_DEV_INITRD /* take care of initrd if we have one */ - if ( r4 ) - { + if ( r4 ) { initrd_start = r4 + KERNELBASE; initrd_end = r5 + KERNELBASE; } #endif /* CONFIG_BLK_DEV_INITRD */ /* take care of cmd line */ - if ( r6 ) - { - + if ( r6 ) { *(char *)(r7+KERNELBASE) = 0; strcpy(cmd_line, (char *)(r6+KERNELBASE)); } diff -Nru a/arch/ppc/kernel/m8xx_setup.c b/arch/ppc/kernel/m8xx_setup.c --- a/arch/ppc/kernel/m8xx_setup.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/m8xx_setup.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.m8xx_setup.c 1.40 11/13/01 21:26:07 paulus + * BK Id: %F% %I% %G% %U% %#% * * linux/arch/ppc/kernel/setup.c * @@ -43,8 +43,9 @@ #include #include #include - +#include #include + #include "ppc8xx_pic.h" static int m8xx_set_rtc_time(unsigned long time); @@ -57,25 +58,30 @@ extern unsigned long find_available_memory(void); extern void m8xx_cpm_reset(uint); +extern void rpxfb_alloc_pages(void); void __init m8xx_setup_arch(void) { int cpm_page; - + cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE); - + /* Reset the Communication Processor Module. */ m8xx_cpm_reset(cpm_page); +#ifdef CONFIG_FB_RPX + rpxfb_alloc_pages(); +#endif + #ifdef notdef ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ #endif - + #ifdef CONFIG_BLK_DEV_INITRD #if 0 - ROOT_DEV = to_kdev_t(0x0200); /* floppy */ + ROOT_DEV = to_kdev_t(0x0200); /* floppy */ rd_prompt = 1; rd_doload = 1; rd_image_start = 0; @@ -105,6 +111,9 @@ xmon(0); #endif machine_restart(NULL); + + /* not reached */ + for (;;); } /* A place holder for time base interrupts, if they are ever enabled. */ @@ -217,7 +226,7 @@ __asm__("mtmsr %0" : : "r" (msr) ); dummy = ((immap_t *)IMAP_ADDR)->im_clkrst.res[0]; - printk("Restart failed\n"); + printk("Restart failed\n"); while(1); } @@ -240,9 +249,9 @@ bd_t *bp; bp = (bd_t *)__res; - - seq_printf(m, "clock\t\t: %ldMHz\n" - "bus clock\t: %ldMHz\n", + + seq_printf(m, "clock\t\t: %dMHz\n" + "bus clock\t: %dMHz\n", bp->bi_intfreq / 1000000, bp->bi_busfreq / 1000000); @@ -263,7 +272,7 @@ for ( i = 0 ; i < NR_SIU_INTS ; i++ ) irq_desc[i].handler = &ppc8xx_pic; - + /* We could probably incorporate the CPM into the multilevel * interrupt structure. */ @@ -296,7 +305,7 @@ { bd_t *binfo; extern unsigned char __res[]; - + binfo = (bd_t *)__res; return binfo->bi_memsize; @@ -343,9 +352,11 @@ platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { + parse_bootinfo(find_bootinfo()); + if ( r3 ) memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); - + #ifdef CONFIG_PCI m8xx_setup_pci_ptrs(); #endif @@ -360,7 +371,7 @@ #endif /* CONFIG_BLK_DEV_INITRD */ /* take care of cmd line */ if ( r6 ) - { + { *(char *)(r7+KERNELBASE) = 0; strcpy(cmd_line, (char *)(r6+KERNELBASE)); } @@ -394,5 +405,5 @@ #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) m8xx_ide_init(); -#endif +#endif } diff -Nru a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S --- a/arch/ppc/kernel/misc.S Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/misc.S Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.misc.S 1.32 10/18/01 17:29:53 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * This file contains miscellaneous low-level functions. @@ -23,7 +23,10 @@ #include #include #include -#include "ppc_asm.h" +#include +#include +#include +#include "ppc_defs.h" .text @@ -39,7 +42,6 @@ * Returns (address we're running at) - (address we were linked at) * for use before the text and data are mapped to KERNELBASE. */ - _GLOBAL(reloc_offset) mflr r0 bl 1f @@ -51,6 +53,62 @@ blr /* + * add_reloc_offset(x) returns x + reloc_offset(). + */ +_GLOBAL(add_reloc_offset) + mflr r0 + bl 1f +1: mflr r5 + lis r4,1b@ha + addi r4,r4,1b@l + subf r5,r4,r5 + add r3,r3,r5 + mtlr r0 + blr + +/* + * sub_reloc_offset(x) returns x - reloc_offset(). + */ +_GLOBAL(sub_reloc_offset) + mflr r0 + bl 1f +1: mflr r5 + lis r4,1b@ha + addi r4,r4,1b@l + subf r5,r4,r5 + subf r3,r5,r3 + mtlr r0 + blr + +/* + * reloc_got2 runs through the .got2 section adding an offset + * to each entry. + */ +_GLOBAL(reloc_got2) + mflr r11 + lis r7,__got2_start@ha + addi r7,r7,__got2_start@l + lis r8,__got2_end@ha + addi r8,r8,__got2_end@l + subf r8,r7,r8 + srwi. r8,r8,2 + beqlr + mtctr r8 + bl 1f +1: mflr r0 + lis r4,1b@ha + addi r4,r4,1b@l + subf r0,r4,r0 + add r7,r0,r7 +2: lwz r0,0(r7) + add r0,r0,r3 + stw r0,0(r7) + addi r7,r7,4 + bdnz 2b + mtlr r11 + blr + +/* * identify_cpu, * called with r3 = data offset and r4 = CPU number * doesn't change r3 @@ -81,6 +139,7 @@ * r3 = data offset (not changed) */ _GLOBAL(do_cpu_ftr_fixups) +#ifndef CONFIG_PPC_ISERIES /* Get CPU 0 features */ addis r6,r3,cur_cpu_spec@ha addi r6,r6,cur_cpu_spec@l @@ -124,10 +183,16 @@ sync /* additional sync needed on g4 */ isync b 1b +#else /* CONFIG_PPC_ISERIES */ + blr +#endif /* CONFIG_PPC_ISERIES */ /* * call_setup_cpu - call the setup_cpu function for this cpu * r3 = data offset, r24 = cpu number + * + * Don't change register layout, the setup function may rely + * on r5 containing a relocated pointer to the current cpu spec. */ _GLOBAL(call_setup_cpu) addis r5,r3,cur_cpu_spec@ha @@ -141,6 +206,7 @@ mr r3,r24 bctr +#ifndef CONFIG_PPC_ISERIES /* iSeries version is in iSeries_misc.S */ /* void __save_flags_ptr(unsigned long *flags) */ _GLOBAL(__save_flags_ptr) mfmsr r4 @@ -268,7 +334,7 @@ nop nop _GLOBAL(__sti_end) - +#endif /* CONFIG_PPC_ISERIES */ /* * complement mask on the msr then "or" some values on. @@ -288,6 +354,20 @@ * Flush MMU TLB */ _GLOBAL(_tlbia) +#if defined(CONFIG_4xx) && defined(CONFIG_PIN_TLB) + /* This needs to be coordinated with other pinning functions since + * we don't keep a memory location of number of entries to reduce + * cache pollution during these operations. + */ + lis r3, 0 + sync +1: + tlbwe r3, r3, TLB_TAG /* just ensure V is clear */ + addi r3, r3, 1 /* so r3 works fine for that */ + cmpwi 0, r3, 61 /* reserve last two entries */ + ble 1b + isync +#else #if defined(CONFIG_SMP) mfmsr r10 SYNC @@ -296,11 +376,13 @@ SYNC lis r9,hash_table_lock@h ori r9,r9,hash_table_lock@l - lwz r8,PROCESSOR(r2) + rlwinm r8,r1,0,0,18 + lwz r8,TI_CPU(r8) oris r8,r8,10 10: lwarx r7,0,r9 cmpi 0,r7,0 bne- 10b + /* No 405 Erratum 77 fix needed here, because 4xx can't do SMP */ stwcx. r8,0,r9 bne- 10b #endif /* CONFIG_SMP */ @@ -314,12 +396,24 @@ mtmsr r10 SYNC #endif +#endif blr /* * Flush MMU TLB for a particular address */ _GLOBAL(_tlbie) +#ifdef CONFIG_4xx + tlbsx. r3, 0, r3 + bne 10f + sync + /* There are only 64 TLB entries, so r3 < 64, which means bit 25, is clear. + * Since 25 is the V bit in the TLB_TAG, loading this value will invalidate + * the TLB entry. */ + tlbwe r3, r3, TLB_TAG + isync +10: +#else #if defined(CONFIG_SMP) mfmsr r10 SYNC @@ -328,11 +422,13 @@ SYNC lis r9,hash_table_lock@h ori r9,r9,hash_table_lock@l - lwz r8,PROCESSOR(r2) + rlwinm r8,r1,0,0,18 + lwz r8,TI_CPU(r8) oris r8,r8,11 10: lwarx r7,0,r9 cmpi 0,r7,0 bne- 10b + PPC405_ERR77(0,r9) stwcx. r8,0,r9 bne- 10b eieio @@ -346,6 +442,7 @@ mtmsr r10 SYNC #endif +#endif /* CONFIG_4xx */ blr /* @@ -358,8 +455,17 @@ lis r5, IDC_INVALL@h mtspr IC_CST, r5 #elif defined(CONFIG_4xx) +#ifdef CONFIG_403GCX + li r3, 512 + mtctr r3 + lis r4, KERNELBASE@h +1: iccci 0, r4 + addi r4, r4, 16 + bdnz 1b +#else lis r3, KERNELBASE@h iccci 0,r3 +#endif #else mfspr r3,PVR rlwinm r3,r3,16,16,31 @@ -369,10 +475,11 @@ mfspr r3,HID0 ori r3,r3,HID0_ICFI mtspr HID0,r3 -#endif /* CONFIG_8xx */ +#endif /* CONFIG_8xx/4xx */ isync blr +#ifndef CONFIG_PPC_ISERIES /* iSeries version is in iSeries_misc.S */ /* * Write any modified data cache blocks out to memory * and invalidate the corresponding instruction cache blocks. @@ -469,15 +576,40 @@ sync /* wait for dcbi's to get to ram */ blr +#ifdef CONFIG_NOT_COHERENT_CACHE +/* This is a bad one....It is used by 'consistent_sync' functions when + * there isn't any handle on the virtual address needed by the usual + * cache flush instructions. On the MPC8xx, we can use the cache line + * flush command, on others all we can do is read enough data to completely + * reload the cache, flushing old data out. + */ + +/* Cache organization. The 4xx has a 8K (128 line) cache, and the 8xx + * has 1, 2, 4, 8K variants. For now, cover worst case. When we can + * deteremine actual size, we will use that later. + */ +#define CACHE_NWAYS 2 +#define CACHE_NLINES 128 + +_GLOBAL(flush_dcache_all) + li r4, (CACHE_NWAYS * CACHE_NLINES) + mtctr r4 + lis r5, KERNELBASE@h +1: lwz r3, 0(r5) /* Load one word from every line */ + addi r5, r5, L1_CACHE_LINE_SIZE + bdnz 1b + blr +#endif /* CONFIG_NOT_COHERENT_CACHE */ + /* * Flush a particular page from the data cache to RAM. * Note: this is necessary because the instruction cache does *not* * snoop from the data cache. * This is a no-op on the 601 which has a unified cache. * - * void __flush_page_to_ram(void *page) + * void __flush_dcache_icache(void *page) */ -_GLOBAL(__flush_page_to_ram) +_GLOBAL(__flush_dcache_icache) mfspr r5,PVR rlwinm r5,r5,16,16,31 cmpi 0,r5,1 @@ -497,28 +629,6 @@ sync isync blr - -/* - * Flush a particular page from the instruction cache. - * Note: this is necessary because the instruction cache does *not* - * snoop from the data cache. - * This is a no-op on the 601 which has a unified cache. - * - * void __flush_icache_page(void *page) - */ -_GLOBAL(__flush_icache_page) - mfspr r5,PVR - rlwinm r5,r5,16,16,31 - cmpi 0,r5,1 - beqlr /* for 601, do nothing */ - li r4,4096/L1_CACHE_LINE_SIZE /* Number of lines in a page */ - mtctr r4 -1: icbi 0,r3 - addi r3,r3,L1_CACHE_LINE_SIZE - bdnz 1b - sync - isync - blr /* * Clear a page using the dcbz instruction, which doesn't cause any @@ -563,8 +673,8 @@ li r5,4 #ifndef CONFIG_8xx -#if MAX_L1_COPY_PREFETCH > 1 - li r0,MAX_L1_COPY_PREFETCH +#if MAX_COPY_PREFETCH > 1 + li r0,MAX_COPY_PREFETCH li r11,4 mtctr r0 11: dcbt r11,r4 @@ -599,6 +709,7 @@ #endif bdnz 1b blr +#endif /* CONFIG_PPC_ISERIES */ /* * Atomic [test&set] exchange @@ -610,6 +721,7 @@ _GLOBAL(xchg_u32) mr r5,r3 /* Save pointer */ 10: lwarx r3,0,r5 /* Fetch old value & reserve */ + PPC405_ERR77(0,r5) stwcx. r4,0,r5 /* Update with new value */ bne- 10b /* Retry if "reservation" (i.e. lock) lost */ blr @@ -621,12 +733,14 @@ _GLOBAL(atomic_clear_mask) 10: lwarx r5,0,r4 andc r5,r5,r3 + PPC405_ERR77(0,r4) stwcx. r5,0,r4 bne- 10b blr _GLOBAL(atomic_set_mask) 10: lwarx r5,0,r4 or r5,r5,r3 + PPC405_ERR77(0,r4) stwcx. r5,0,r4 bne- 10b blr @@ -866,10 +980,8 @@ sc cmpi 0,r3,0 /* parent or child? */ bnelr /* return if parent */ - li r0,0 /* clear out p->thread.regs */ - stw r0,THREAD+PT_REGS(r2) /* since we don't have user ctx */ - addi r1,r2,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD - stw r0,0(r1) + li r0,0 /* make top-level stack frame */ + stwu r0,-16(r1) mtlr r6 /* fn addr in lr */ mr r3,r4 /* load arg and call fn */ blrl @@ -1122,6 +1234,18 @@ .long sys_mincore .long sys_gettid .long sys_tkill + .long sys_setxattr + .long sys_lsetxattr /* 210 */ + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr + .long sys_fgetxattr + .long sys_listxattr /* 215 */ + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr + .long sys_lremovexattr + .long sys_fremovexattr /* 220 */ .rept NR_syscalls-(.-sys_call_table)/4 .long sys_ni_syscall .endr diff -Nru a/arch/ppc/kernel/mk_defs.c b/arch/ppc/kernel/mk_defs.c --- a/arch/ppc/kernel/mk_defs.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/mk_defs.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.mk_defs.c 1.11 08/19/01 22:43:23 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * This program is used to generate definitions needed by @@ -27,6 +27,14 @@ #include #include #include +#include + +#ifdef CONFIG_PPC_ISERIES +#include +#include +#include +#include +#endif /* CONFIG_PPC_ISERIES */ #define DEFINE(sym, val) \ asm volatile("\n#define\t" #sym "\t%0" : : "i" (val)) @@ -34,24 +42,14 @@ int main(void) { - /*DEFINE(KERNELBASE, KERNELBASE);*/ - DEFINE(STATE, offsetof(struct task_struct, state)); - DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task)); - DEFINE(COUNTER, offsetof(struct task_struct, counter)); - DEFINE(PROCESSOR, offsetof(struct task_struct, processor)); -#error DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending)); DEFINE(THREAD, offsetof(struct task_struct, thread)); + DEFINE(THREAD_INFO, offsetof(struct task_struct, thread_info)); DEFINE(MM, offsetof(struct task_struct, mm)); - DEFINE(ACTIVE_MM, offsetof(struct task_struct, active_mm)); - DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct)); DEFINE(KSP, offsetof(struct thread_struct, ksp)); DEFINE(PGDIR, offsetof(struct thread_struct, pgdir)); DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); - DEFINE(PT_TRACESYS, PT_TRACESYS); - DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); -#error DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); -#error DEFINE(NEED_RESCHED, offsetof(struct task_struct, need_resched)); + DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode)); DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); #ifdef CONFIG_ALTIVEC @@ -60,7 +58,6 @@ DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr)); #endif /* CONFIG_ALTIVEC */ /* Interrupt register frame */ - DEFINE(TASK_UNION_SIZE, sizeof(union task_union)); DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs)); /* in fact we only use gpr0 - gpr9 and gpr20 - gpr23 */ @@ -126,6 +123,35 @@ DEFINE(CPU_SPEC_PVR_VALUE, offsetof(struct cpu_spec, pvr_value)); DEFINE(CPU_SPEC_FEATURES, offsetof(struct cpu_spec, cpu_features)); DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup)); + +#ifdef CONFIG_PPC_ISERIES + DEFINE(PACAPROCENABLED, offsetof(struct Paca, xProcEnabled)); + DEFINE(PACAPACAINDEX, offsetof(struct Paca, xPacaIndex)); + DEFINE(PACAPROCSTART, offsetof(struct Paca, xProcStart)); + DEFINE(PACAKSAVE, offsetof(struct Paca, xKsave)); + DEFINE(PACASAVEDMSR, offsetof(struct Paca, xSavedMsr)); + DEFINE(PACASAVEDLR, offsetof(struct Paca, xSavedLr)); + DEFINE(PACACONTEXTOVERFLOW, offsetof(struct Paca, xContextOverflow)); + DEFINE(PACAR21, offsetof(struct Paca, xR21)); + DEFINE(PACAR22, offsetof(struct Paca, xR22)); + DEFINE(PACALPQUEUE, offsetof(struct Paca, lpQueuePtr)); + DEFINE(PACALPPACA, offsetof(struct Paca, xLpPaca)); + DEFINE(PACA_STRUCT_SIZE, sizeof(struct Paca)); + DEFINE(LPREGSAV, offsetof(struct Paca, xRegSav)); + DEFINE(PACADEFAULTDECR, offsetof(struct Paca, default_decr)); + DEFINE(LPPACAANYINT, offsetof(struct ItLpPaca, xRsvd)); + DEFINE(LPPACASRR0, offsetof(struct ItLpPaca, xSavedSrr0)); + DEFINE(LPPACASRR1, offsetof(struct ItLpPaca, xSavedSrr1)); + DEFINE(LPPACADECRINT, offsetof(struct ItLpPaca, xDecrInt)); + DEFINE(LPPACAIPIINT, offsetof(struct ItLpPaca, xIpiCnt)); + DEFINE(LPQCUREVENTPTR, offsetof(struct ItLpQueue, xSlicCurEventPtr)); + DEFINE(LPQOVERFLOW, offsetof(struct ItLpQueue, xPlicOverflowIntPending)); + DEFINE(LPQINUSEWORD, offsetof(struct ItLpQueue, xInUseWord)); + DEFINE(LPEVENTFLAGS, offsetof(struct HvLpEvent, xFlags)); + DEFINE(CONTEXT, offsetof(struct mm_struct, context)); + DEFINE(_SOFTE, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, mq)); + DEFINE(PACA_EXT_INTS, offsetof(struct Paca, ext_ints)); +#endif /* CONFIG_PPC_ISERIES */ DEFINE(NUM_USER_SEGMENTS, TASK_SIZE>>28); return 0; diff -Nru a/arch/ppc/kernel/mpc10x_common.c b/arch/ppc/kernel/mpc10x_common.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/mpc10x_common.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,378 @@ + +/* + * arch/ppc/kernel/mpc10x_common.c + * + * Common routines for the Motorola SPS MPC106, MPC107 and MPC8240 Host bridge, + * Mem ctlr, EPIC, etc. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * *** WARNING - A BAT MUST be set to access the PCI config addr/data regs *** + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Set resources to match bridge memory map */ +void __init +mpc10x_bridge_set_resources(int map, struct pci_controller *hose) +{ + + switch (map) { + case MPC10X_MEM_MAP_A: + pci_init_resource(&hose->io_resource, + 0x00000000, + 0x3f7fffff, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource (&hose->mem_resources[0], + 0xc0000000, + 0xfeffffff, + IORESOURCE_MEM, + "PCI host bridge"); + break; + case MPC10X_MEM_MAP_B: + pci_init_resource(&hose->io_resource, + 0x00000000, + 0x00bfffff, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource (&hose->mem_resources[0], + 0x80000000, + 0xfcffffff, + IORESOURCE_MEM, + "PCI host bridge"); + break; + default: + printk("mpc10x_bridge_set_resources: " + "Invalid map specified\n"); + if (ppc_md.progress) + ppc_md.progress("mpc10x:exit1", 0x100); + } +} +/* + * Do some initialization and put the EUMB registers at the specified address + * (also map the EPIC registers into virtual space--OpenPIC_Addr will be set). + * + * The EPIC is not on the 106, only the 8240 and 107. + */ +int __init +mpc10x_bridge_init(struct pci_controller *hose, + uint current_map, + uint new_map, + uint phys_eumb_base) +{ + int host_bridge, picr1, picr1_bit; + ulong pci_config_addr, pci_config_data; + u_char pir, byte; + + if (ppc_md.progress) ppc_md.progress("mpc10x:enter", 0x100); + + /* Set up for current map so we can get at config regs */ + switch (current_map) { + case MPC10X_MEM_MAP_A: + setup_indirect_pci(hose, + MPC10X_MAPA_CNFG_ADDR, + MPC10X_MAPA_CNFG_DATA); + break; + case MPC10X_MEM_MAP_B: + setup_indirect_pci(hose, + MPC10X_MAPB_CNFG_ADDR, + MPC10X_MAPB_CNFG_DATA); + break; + default: + printk("mpc10x_bridge_init: %s\n", + "Invalid current map specified"); + if (ppc_md.progress) + ppc_md.progress("mpc10x:exit1", 0x100); + return -1; + } + + /* Make sure its a supported bridge */ + early_read_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_VENDOR_ID, + &host_bridge); + + switch (host_bridge) { + case MPC10X_BRIDGE_106: + case MPC10X_BRIDGE_8240: + case MPC10X_BRIDGE_107: + case MPC10X_BRIDGE_8245: + break; + default: + if (ppc_md.progress) + ppc_md.progress("mpc10x:exit2", 0x100); + return -1; + } + + switch (new_map) { + case MPC10X_MEM_MAP_A: + MPC10X_SETUP_HOSE(hose, A); + pci_config_addr = MPC10X_MAPA_CNFG_ADDR; + pci_config_data = MPC10X_MAPA_CNFG_DATA; + picr1_bit = MPC10X_CFG_PICR1_ADDR_MAP_A; + break; + case MPC10X_MEM_MAP_B: + MPC10X_SETUP_HOSE(hose, B); + pci_config_addr = MPC10X_MAPB_CNFG_ADDR; + pci_config_data = MPC10X_MAPB_CNFG_DATA; + picr1_bit = MPC10X_CFG_PICR1_ADDR_MAP_B; + break; + default: + printk("mpc10x_bridge_init: %s\n", + "Invalid new map specified"); + if (ppc_md.progress) + ppc_md.progress("mpc10x:exit3", 0x100); + return -1; + } + + /* Make bridge use the 'new_map', if not already usng it */ + if (current_map != new_map) { + early_read_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, + &picr1); + + picr1 = (picr1 & ~MPC10X_CFG_PICR1_ADDR_MAP_MASK) | + picr1_bit; + + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, + picr1); + + asm volatile("sync"); + + /* Undo old mappings & map in new cfg data/addr regs */ + iounmap((void *)hose->cfg_addr); + iounmap((void *)hose->cfg_data); + + setup_indirect_pci(hose, + pci_config_addr, + pci_config_data); + } + + /* Setup resources to match map */ + mpc10x_bridge_set_resources(new_map, hose); + + /* + * Want processor accesses of 0xFDxxxxxx to be mapped + * to PCI memory space at 0x00000000. Do not want + * host bridge to respond to PCI memory accesses of + * 0xFDxxxxxx. Do not want host bridge to respond + * to PCI memory addresses 0xFD000000-0xFDFFFFFF; + * want processor accesses from 0x000A0000-0x000BFFFF + * to be forwarded to system memory. + * + * Only valid if not in agent mode and using MAP B. + */ + if (new_map == MPC10X_MEM_MAP_B) { + early_read_config_byte(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_MAPB_OPTIONS_REG, + &byte); + + byte &= ~(MPC10X_CFG_MAPB_OPTIONS_PFAE | + MPC10X_CFG_MAPB_OPTIONS_PCICH | + MPC10X_CFG_MAPB_OPTIONS_PROCCH); + + if (host_bridge != MPC10X_BRIDGE_106) { + byte |= MPC10X_CFG_MAPB_OPTIONS_CFAE; + } + + early_write_config_byte(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_MAPB_OPTIONS_REG, + byte); + } + + if (host_bridge != MPC10X_BRIDGE_106) { + early_read_config_byte(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PIR_REG, + &pir); + + if (pir != MPC10X_CFG_PIR_HOST_BRIDGE) { + printk("Host bridge in Agent mode\n"); + /* Read or Set LMBAR & PCSRBAR? */ + } + + /* Set base addr of the 8240/107 EUMB. */ + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_EUMBBAR, + phys_eumb_base); + + /* Map EPIC register part of EUMB into vitual memory */ + OpenPIC_Addr = + ioremap(phys_eumb_base + MPC10X_EUMB_EPIC_OFFSET, + MPC10X_EUMB_EPIC_SIZE); + } + +#ifdef CONFIG_MPC10X_STORE_GATHERING + mpc10x_enable_store_gathering(hose); +#endif + + if (ppc_md.progress) ppc_md.progress("mpc10x:exit", 0x100); + return 0; +} + +/* + * Need to make our own PCI config space access macros because + * mpc10x_get_mem_size() is called before the data structures are set up for + * the 'early_xxx' and 'indirect_xxx' routines to work. + * Assumes bus 0. + */ +#define MPC10X_CFG_read(val, addr, type, op) *val = op((type)(addr)) +#define MPC10X_CFG_write(val, addr, type, op) op((type *)(addr), (val)) + +#define MPC10X_PCI_OP(rw, size, type, op, mask) \ +static void \ +mpc10x_##rw##_config_##size(uint *cfg_addr, uint *cfg_data, int devfn, int offset, type val) \ +{ \ + out_be32(cfg_addr, \ + ((offset & 0xfc) << 24) | (devfn << 16) \ + | (0 << 8) | 0x80); \ + MPC10X_CFG_##rw(val, cfg_data + (offset & mask), type, op); \ + return; \ +} + +MPC10X_PCI_OP(read, byte, u8 *, in_8, 3) +MPC10X_PCI_OP(read, dword, u32 *, in_le32, 0) +#if 0 /* Not used */ +MPC10X_PCI_OP(write, byte, u8, out_8, 3) +MPC10X_PCI_OP(read, word, u16 *, in_le16, 2) +MPC10X_PCI_OP(write, word, u16, out_le16, 2) +MPC10X_PCI_OP(write, dword, u32, out_le32, 0) +#endif + +/* + * Read the memory controller registers to determine the amount of memory in + * the system. This assumes that the firmware has correctly set up the memory + * controller registers. + */ +unsigned long __init +mpc10x_get_mem_size(uint mem_map) +{ + uint *config_addr, *config_data, val; + ulong start, end, total, offset; + int i; + u_char bank_enables; + + switch (mem_map) { + case MPC10X_MEM_MAP_A: + config_addr = (uint *)MPC10X_MAPA_CNFG_ADDR; + config_data = (uint *)MPC10X_MAPA_CNFG_DATA; + break; + case MPC10X_MEM_MAP_B: + config_addr = (uint *)MPC10X_MAPB_CNFG_ADDR; + config_data = (uint *)MPC10X_MAPB_CNFG_DATA; + break; + default: + return 0; + } + + mpc10x_read_config_byte(config_addr, + config_data, + PCI_DEVFN(0,0), + MPC10X_MCTLR_MEM_BANK_ENABLES, + &bank_enables); + + total = 0; + + for (i=0; i<8; i++) { + if (bank_enables & (1 << i)) { + offset = MPC10X_MCTLR_MEM_START_1 + ((i > 3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, + config_data, + PCI_DEVFN(0,0), + offset, + &val); + start = (val >> ((i & 3) << 3)) & 0xff; + + offset = MPC10X_MCTLR_EXT_MEM_START_1 + ((i>3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, + config_data, + PCI_DEVFN(0,0), + offset, + &val); + val = (val >> ((i & 3) << 3)) & 0x03; + start = (val << 28) | (start << 20); + + offset = MPC10X_MCTLR_MEM_END_1 + ((i > 3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, + config_data, + PCI_DEVFN(0,0), + offset, + &val); + end = (val >> ((i & 3) << 3)) & 0xff; + + offset = MPC10X_MCTLR_EXT_MEM_END_1 + ((i > 3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, + config_data, + PCI_DEVFN(0,0), + offset, + &val); + val = (val >> ((i & 3) << 3)) & 0x03; + end = (val << 28) | (end << 20) | 0xfffff; + + total += (end - start + 1); + } + } + + return total; +} + +int __init +mpc10x_enable_store_gathering(struct pci_controller *hose) +{ + uint picr1; + + early_read_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, + &picr1); + + picr1 |= MPC10X_CFG_PICR1_ST_GATH_EN; + + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, + picr1); + + return 0; +} diff -Nru a/arch/ppc/kernel/oak_setup.c b/arch/ppc/kernel/oak_setup.c --- a/arch/ppc/kernel/oak_setup.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,297 +0,0 @@ -/* - * BK Id: SCCS/s.oak_setup.c 1.12 11/13/01 21:26:07 paulus - */ -/* - * - * Copyright (c) 1999-2000 Grant Erickson - * - * Module name: oak_setup.c - * - * Description: - * Architecture- / platform-specific boot-time initialization code for - * the IBM PowerPC 403GCX "Oak" evaluation board. Adapted from original - * code by Gary Thomas, Cort Dougan , and Dan Malek - * . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "local_irq.h" -#include "ppc4xx_pic.h" -#include -#include "oak_setup.h" - - - - - - - -/* Function Prototypes */ - -extern void abort(void); - -/* Global Variables */ - -unsigned char __res[sizeof(bd_t)]; - - -/* - * void __init oak_init() - * - * Description: - * This routine... - * - * Input(s): - * r3 - Optional pointer to a board information structure. - * r4 - Optional pointer to the physical starting address of the init RAM - * disk. - * r5 - Optional pointer to the physical ending address of the init RAM - * disk. - * r6 - Optional pointer to the physical starting address of any kernel - * command-line parameters. - * r7 - Optional pointer to the physical ending address of any kernel - * command-line parameters. - * - * Output(s): - * N/A - * - * Returns: - * N/A - * - */ -void __init -platform_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - /* - * If we were passed in a board information, copy it into the - * residual data area. - */ - if (r3) { - memcpy((void *)__res, (void *)(r3 + KERNELBASE), sizeof(bd_t)); - } - -#if defined(CONFIG_BLK_DEV_INITRD) - /* - * If the init RAM disk has been configured in, and there's a valid - * starting address for it, set it up. - */ - if (r4) { - initrd_start = r4 + KERNELBASE; - initrd_end = r5 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_INITRD */ - - /* Copy the kernel command line arguments to a safe place. */ - - if (r6) { - *(char *)(r7 + KERNELBASE) = 0; - strcpy(cmd_line, (char *)(r6 + KERNELBASE)); - } - - /* Initialize machine-dependency vectors */ - - ppc_md.setup_arch = oak_setup_arch; - ppc_md.show_percpuinfo = oak_show_percpuinfo; - ppc_md.irq_cannonicalize = NULL; - ppc_md.init_IRQ = oak_init_IRQ; - ppc_md.get_irq = oak_get_irq; - ppc_md.init = NULL; - - ppc_md.restart = oak_restart; - ppc_md.power_off = oak_power_off; - ppc_md.halt = oak_halt; - - ppc_md.time_init = oak_time_init; - ppc_md.set_rtc_time = oak_set_rtc_time; - ppc_md.get_rtc_time = oak_get_rtc_time; - ppc_md.calibrate_decr = oak_calibrate_decr; - - ppc_md.kbd_setkeycode = NULL; - ppc_md.kbd_getkeycode = NULL; - ppc_md.kbd_translate = NULL; - ppc_md.kbd_unexpected_up = NULL; - ppc_md.kbd_leds = NULL; - ppc_md.kbd_init_hw = NULL; - ppc_md.ppc_kbd_sysrq_xlate = NULL; -} - -/* - * Document me. - */ -void __init -oak_setup_arch(void) -{ - /* XXX - Implement me */ -} - -/* - * int oak_show_percpuinfo() - * - * Description: - * This routine pretty-prints the platform's internal CPU and bus clock - * frequencies into the buffer for usage in /proc/cpuinfo. - * - * Input(s): - * *buffer - Buffer into which CPU and bus clock frequencies are to be - * printed. - * - * Output(s): - * *buffer - Buffer with the CPU and bus clock frequencies. - * - * Returns: - * The number of bytes copied into 'buffer' if OK, otherwise zero or less - * on error. - */ -int -oak_show_percpuinfo(struct seq_file *m, int i) -{ - bd_t *bp = (bd_t *)__res; - - seq_printf(m, "clock\t\t: %dMHz\n" - "bus clock\t\t: %dMHz\n", - bp->bi_intfreq / 1000000, - bp->bi_busfreq / 1000000); - - return 0; -} - -/* - * Document me. - */ -void __init -oak_init_IRQ(void) -{ - int i; - - ppc4xx_pic_init(); - - for (i = 0; i < NR_IRQS; i++) { - irq_desc[i].handler = ppc4xx_pic; - } - - return; -} - -/* - * Document me. - */ -int -oak_get_irq(struct pt_regs *regs) -{ - return (ppc4xx_pic_get_irq(regs)); -} - -/* - * Document me. - */ -void -oak_restart(char *cmd) -{ - abort(); -} - -/* - * Document me. - */ -void -oak_power_off(void) -{ - oak_restart(NULL); -} - -/* - * Document me. - */ -void -oak_halt(void) -{ - oak_restart(NULL); -} - -/* - * Document me. - */ -long __init -oak_time_init(void) -{ - /* XXX - Implement me */ - return 0; -} - -/* - * Document me. - */ -int __init -oak_set_rtc_time(unsigned long time) -{ - /* XXX - Implement me */ - - return (0); -} - -/* - * Document me. - */ -unsigned long __init -oak_get_rtc_time(void) -{ - /* XXX - Implement me */ - - return (0); -} - -/* - * void __init oak_calibrate_decr() - * - * Description: - * This routine retrieves the internal processor frequency from the board - * information structure, sets up the kernel timer decrementer based on - * that value, enables the 403 programmable interval timer (PIT) and sets - * it up for auto-reload. - * - * Input(s): - * N/A - * - * Output(s): - * N/A - * - * Returns: - * N/A - * - */ -void __init -oak_calibrate_decr(void) -{ - unsigned int freq; - bd_t *bip = (bd_t *)__res; - - freq = bip->bi_intfreq; - - decrementer_count = freq / HZ; - count_period_num = 1; - count_period_den = freq; - - /* Enable the PIT and set auto-reload of its value */ - - mtspr(SPRN_TCR, TCR_PIE | TCR_ARE); - - /* Clear any pending timer interrupts */ - - mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS); -} diff -Nru a/arch/ppc/kernel/oak_setup.h b/arch/ppc/kernel/oak_setup.h --- a/arch/ppc/kernel/oak_setup.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,53 +0,0 @@ -/* - * BK Id: SCCS/s.oak_setup.h 1.5 05/17/01 18:14:21 cort - */ -/* - * - * Copyright (c) 1999-2000 Grant Erickson - * - * Module name: oak_setup.h - * - * Description: - * Architecture- / platform-specific boot-time initialization code for - * the IBM PowerPC 403GCX "Oak" evaluation board. Adapted from original - * code by Gary Thomas, Cort Dougan , and Dan Malek - * . - * - */ - -#ifndef __OAK_SETUP_H__ -#define __OAK_SETUP_H__ - -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -extern unsigned char __res[sizeof(bd_t)]; - -extern void oak_init(unsigned long r3, - unsigned long ird_start, - unsigned long ird_end, - unsigned long cline_start, - unsigned long cline_end); -extern void oak_setup_arch(void); -extern int oak_setup_residual(char *buffer); -extern void oak_init_IRQ(void); -extern int oak_get_irq(struct pt_regs *regs); -extern void oak_restart(char *cmd); -extern void oak_power_off(void); -extern void oak_halt(void); -extern void oak_time_init(void); -extern int oak_set_rtc_time(unsigned long now); -extern unsigned long oak_get_rtc_time(void); -extern void oak_calibrate_decr(void); - - -#ifdef __cplusplus -} -#endif - -#endif /* __OAK_SETUP_H__ */ diff -Nru a/arch/ppc/kernel/open_pic.c b/arch/ppc/kernel/open_pic.c --- a/arch/ppc/kernel/open_pic.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/open_pic.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.open_pic.c 1.31 10/11/01 12:09:11 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling @@ -17,40 +17,40 @@ #include #include #include -#include #include #include #include #include #include #include +#include +#include -#include "local_irq.h" -#include "open_pic.h" #include "open_pic_defs.h" +#ifdef CONFIG_PRPMC800 +#define OPENPIC_BIG_ENDIAN +#endif + void* OpenPIC_Addr; static volatile struct OpenPIC *OpenPIC = NULL; u_int OpenPIC_NumInitSenses __initdata = 0; u_char *OpenPIC_InitSenses __initdata = NULL; extern int use_of_interrupt_tree; -void find_ISUs(void); - static u_int NumProcessors; static u_int NumSources; -#ifdef CONFIG_POWER3 -static int NumISUs; -#endif static int open_pic_irq_offset; static volatile unsigned char* chrp_int_ack_special; - -OpenPIC_SourcePtr ISU[OPENPIC_MAX_ISU]; +static volatile OpenPIC_Source *ISR[NR_IRQS]; /* Global Operations */ static void openpic_disable_8259_pass_through(void); static void openpic_set_priority(u_int pri); static void openpic_set_spurious(u_int vector); +static void openpic_enable_sie(void); +static void openpic_eicr_set_clk(u_int clkval); +static void openpic_eicr_set_clk(u_int clkval); #ifdef CONFIG_SMP /* Interprocessor Interrupts */ @@ -67,7 +67,7 @@ static void openpic_disable_irq(u_int irq); static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity, int is_level); -static void openpic_mapirq(u_int irq, u_int cpumask); +static void openpic_mapirq(u_int irq, u_int cpumask, u_int keepmask); /* * These functions are not used but the code is kept here @@ -150,7 +150,8 @@ */ extern unsigned long* _get_SP(void); #define check_arg_irq(irq) \ - if (irq < open_pic_irq_offset || irq >= (NumSources+open_pic_irq_offset)){ \ + if (irq < open_pic_irq_offset || irq >= NumSources+open_pic_irq_offset \ + || ISR[irq - open_pic_irq_offset] == 0) { \ printk("open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \ print_backtrace(_get_SP()); } #define check_arg_cpu(cpu) \ @@ -166,23 +167,25 @@ #define check_arg_cpu(cpu) do {} while (0) #endif -#ifdef CONFIG_POWER3 - #define GET_ISU(source) ISU[(source) >> 4][(source) & 0xf] -#else - #define GET_ISU(source) ISU[0][(source)] -#endif - u_int openpic_read(volatile u_int *addr) { u_int val; +#ifdef OPENPIC_BIG_ENDIAN + val = in_be32(addr); +#else val = in_le32(addr); +#endif return val; } static inline void openpic_write(volatile u_int *addr, u_int val) { +#ifdef OPENPIC_BIG_ENDIAN + out_be32(addr, val); +#else out_le32(addr, val); +#endif } static inline u_int openpic_readfield(volatile u_int *addr, u_int mask) @@ -221,7 +224,7 @@ u_int openpic_read_IPI(volatile u_int* addr) { u_int val = 0; -#ifdef CONFIG_POWER3 +#if defined(OPENPIC_BIG_ENDIAN) || defined(CONFIG_POWER3) val = in_be32(addr); #else val = in_le32(addr); @@ -260,6 +263,20 @@ } #endif /* CONFIG_SMP */ +void openpic_set_sources(int first_irq, int num_irqs, void *first_ISR) +{ + volatile OpenPIC_Source *src = first_ISR; + int i, last_irq; + + last_irq = first_irq + num_irqs; + if (last_irq > NumSources) + NumSources = last_irq; + if (src == 0) + src = &((struct OpenPIC *)OpenPIC_Addr)->Source[first_irq]; + for (i = first_irq; i < last_irq; ++i, ++src) + ISR[i] = src; +} + void __init openpic_init(int main_pic, int offset, unsigned char* chrp_ack, int programmer_switch_irq) { @@ -273,7 +290,13 @@ } OpenPIC = (volatile struct OpenPIC *)OpenPIC_Addr; - if ( ppc_md.progress ) ppc_md.progress("openpic enter",0x122); +#ifdef CONFIG_EPIC_SERIAL_MODE + /* Have to start from ground zero. + */ + openpic_reset(); +#endif + + if (ppc_md.progress) ppc_md.progress("openpic enter", 0x122); t = openpic_read(&OpenPIC->Global.Feature_Reporting0); switch (t & OPENPIC_FEATURE_VERSION_MASK) { @@ -292,8 +315,11 @@ } NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; - NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> - OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; + if (NumSources == 0) + openpic_set_sources(0, + ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1, + NULL); printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, NumProcessors, NumSources, OpenPIC); timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); @@ -328,8 +354,6 @@ } #endif - find_ISUs(); - /* Initialize external interrupts */ if (ppc_md.progress) ppc_md.progress("openpic ext",0x3bc); @@ -338,13 +362,16 @@ /* SIOint (8259 cascade) is special */ if (offset) { openpic_initirq(0, 8, offset, 1, 1); - openpic_mapirq(0, 1<<0); + openpic_mapirq(0, 1<<0, 0); } /* Init all external sources */ for (i = 1; i < NumSources; i++) { int pri, sense; + if (ISR[i] == 0) + continue; + /* the bootloader may have left it enabled (bad !) */ openpic_disable_irq(i+offset); @@ -356,7 +383,7 @@ /* Enabled, Priority 8 or 9 */ openpic_initirq(i, pri, i+offset, !sense, sense); /* Processor 0 */ - openpic_mapirq(i, 1<<0); + openpic_mapirq(i, 1<<0, 0); } /* Init descriptors */ @@ -373,43 +400,17 @@ "82c59 cascade", NULL)) printk("Unable to get OpenPIC IRQ 0 for cascade\n"); } +#ifdef CONFIG_EPIC_SERIAL_MODE + openpic_disable_8259_pass_through(); + openpic_eicr_set_clk(7); /* Slowest value until we know better */ + openpic_enable_sie(); openpic_set_priority(0); - openpic_disable_8259_pass_through(); - - if (ppc_md.progress) ppc_md.progress("openpic exit",0x222); -} - -#ifdef CONFIG_POWER3 -void openpic_setup_ISU(int isu_num, unsigned long addr) -{ - if (isu_num >= OPENPIC_MAX_ISU) - return; - ISU[isu_num] = (OpenPIC_SourcePtr) ioremap(addr, 0x400); - if (isu_num >= NumISUs) - NumISUs = isu_num + 1; -} -#endif - -void find_ISUs(void) -{ -#ifdef CONFIG_POWER3 - /* Use /interrupt-controller/reg and - * /interrupt-controller/interrupt-ranges from OF device tree - * the ISU array is setup in chrp_pci.c in ibm_add_bridges - * as a result - * -- tgall - */ - - /* basically each ISU is a bus, and this assumes that - * open_pic_isu_count interrupts per bus are possible - * ISU == Interrupt Source - */ - NumSources = NumISUs * 0x10; - #else - /* for non-distributed OpenPIC implementations it's in the IDU -- Cort */ - ISU[0] = (OpenPIC_Source *)OpenPIC->Source; + openpic_disable_8259_pass_through(); + openpic_set_priority(0); #endif + + if (ppc_md.progress) ppc_md.progress("openpic exit",0x222); } static void openpic_reset(void) @@ -421,6 +422,18 @@ mb(); } +static void openpic_enable_sie(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration1, + OPENPIC_EICR_SIE); +} + +static void openpic_eicr_set_clk(u_int clkval) +{ + openpic_writefield(&OpenPIC->Global.Global_Configuration1, + OPENPIC_EICR_S_CLK_MASK, (clkval << 28)); +} + #ifdef notused static void openpic_enable_8259_pass_through(void) { @@ -602,8 +615,8 @@ * we should make sure we also change the default values of irq_affinity * in irq.c. */ - for (i = 0; i < NumSources ; i++) - openpic_mapirq(i, openpic_read(&GET_ISU(i).Destination) | msk); + for (i = 0; i < NumSources; i++) + openpic_mapirq(i, msk, ~0U); #endif /* CONFIG_IRQ_ALL_CPUS */ openpic_set_priority(0); @@ -653,26 +666,29 @@ */ static void openpic_enable_irq(u_int irq) { + volatile u_int *vpp; + check_arg_irq(irq); - openpic_clearfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK); + vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority; + openpic_clearfield(vpp, OPENPIC_MASK); /* make sure mask gets to controller before we return to user */ do { mb(); /* sync is probably useless here */ - } while(openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, - OPENPIC_MASK)); + } while (openpic_readfield(vpp, OPENPIC_MASK)); } static void openpic_disable_irq(u_int irq) { + volatile u_int *vpp; u32 vp; check_arg_irq(irq); - openpic_setfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK); + vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority; + openpic_setfield(vpp, OPENPIC_MASK); /* make sure mask gets to controller before we return to user */ do { mb(); /* sync is probably useless here */ - vp = openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, - OPENPIC_MASK | OPENPIC_ACTIVITY); + vp = openpic_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY); } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); } @@ -709,7 +725,7 @@ */ static void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) { - openpic_safe_writefield(&GET_ISU(irq).Vector_Priority, + openpic_safe_writefield(&ISR[irq]->Vector_Priority, OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, (pri << OPENPIC_PRIORITY_SHIFT) | vec | @@ -721,9 +737,13 @@ /* * Map an interrupt source to one or more CPUs */ -static void openpic_mapirq(u_int irq, u_int physmask) +static void openpic_mapirq(u_int irq, u_int physmask, u_int keepmask) { - openpic_write(&GET_ISU(irq).Destination, physmask); + if (ISR[irq] == 0) + return; + if (keepmask != 0) + physmask |= openpic_read(&ISR[irq]->Destination) & keepmask; + openpic_write(&ISR[irq]->Destination, physmask); } #ifdef notused @@ -734,9 +754,10 @@ */ static void openpic_set_sense(u_int irq, int sense) { - openpic_safe_writefield(&GET_ISU(irq).Vector_Priority, - OPENPIC_SENSE_LEVEL, - (sense ? OPENPIC_SENSE_LEVEL : 0)); + if (ISR[irq] != 0) + openpic_safe_writefield(&ISR[irq]->Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); } #endif /* notused */ @@ -757,7 +778,7 @@ static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask) { - openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask)); + openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask), 0); } #ifdef CONFIG_SMP @@ -784,9 +805,6 @@ /* * Clean up needed. -VAL */ -#ifndef CONFIG_GEMINI - extern int i8259_irq(int cpu); -#endif int irq = openpic_irq(); /* Management of the cascade should be moved out of here */ @@ -801,7 +819,7 @@ irq = *chrp_int_ack_special; #ifndef CONFIG_GEMINI else - irq = i8259_irq( smp_processor_id() ); + irq = i8259_poll(); #endif openpic_eoi(); } @@ -858,9 +876,11 @@ for (i=0; iGlobal.IPI_Vector_Priority(i)); for (i=0; iSource[i].Vector_Priority) + if (ISR[i] == 0) + continue; + save_irq_src_vp[i] = openpic_read(&ISR[i]->Vector_Priority) & ~OPENPIC_ACTIVITY; - save_irq_src_dest[i] = openpic_read(&OpenPIC->Source[i].Destination); + save_irq_src_dest[i] = openpic_read(&ISR[i]->Destination); } spin_unlock_irqrestore(&openpic_setup_lock, flags); } @@ -876,15 +896,19 @@ openpic_reset(); for (i=0; iGlobal.IPI_Vector_Priority(i), save_ipi_vp[i]); + openpic_write(&OpenPIC->Global.IPI_Vector_Priority(i), + save_ipi_vp[i]); for (i=0; iSource[i].Vector_Priority, save_irq_src_vp[i]); - openpic_write(&OpenPIC->Source[i].Destination, save_irq_src_dest[i]); + if (ISR[i] == 0) + continue; + openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); + openpic_write(&ISR[i]->Destination, save_irq_src_dest[i]); } openpic_set_spurious(OPENPIC_VEC_SPURIOUS+open_pic_irq_offset); openpic_disable_8259_pass_through(); for (i=0; iProcessor[i].Current_Task_Priority, save_cpu_task_pri[i]); + openpic_write(&OpenPIC->Processor[i].Current_Task_Priority, + save_cpu_task_pri[i]); spin_unlock_irqrestore(&openpic_setup_lock, flags); } diff -Nru a/arch/ppc/kernel/open_pic.h b/arch/ppc/kernel/open_pic.h --- a/arch/ppc/kernel/open_pic.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,68 +0,0 @@ -/* - * BK Id: SCCS/s.open_pic.h 1.14 10/11/01 12:09:12 trini - */ -/* - * arch/ppc/kernel/open_pic.h -- OpenPIC Interrupt Handling - * - * Copyright (C) 1997 Geert Uytterhoeven - * - * 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 archive - * for more details. - * - */ - -#ifndef _PPC_KERNEL_OPEN_PIC_H -#define _PPC_KERNEL_OPEN_PIC_H - -#include - -#define OPENPIC_SIZE 0x40000 - -/* - * Non-offset'ed vector numbers - */ - -#define OPENPIC_VEC_TIMER 64 /* and up */ -#define OPENPIC_VEC_IPI 72 /* and up */ -#define OPENPIC_VEC_SPURIOUS 127 - -/* OpenPIC IRQ controller structure */ -extern struct hw_interrupt_type open_pic; - -/* OpenPIC IPI controller structure */ -#ifdef CONFIG_SMP -extern struct hw_interrupt_type open_pic_ipi; -#endif /* CONFIG_SMP */ - -extern u_int OpenPIC_NumInitSenses; -extern u_char *OpenPIC_InitSenses; -extern void* OpenPIC_Addr; - -/* Exported functions */ -extern void openpic_init(int, int, unsigned char *, int); -extern u_int openpic_irq(void); -extern void openpic_eoi(void); -extern void openpic_request_IPIs(void); -extern void do_openpic_setup_cpu(void); -extern int openpic_get_irq(struct pt_regs *regs); -extern void openpic_reset_processor_phys(u_int cpumask); -extern void openpic_setup_ISU(int isu_num, unsigned long addr); -extern void openpic_cause_IPI(u_int ipi, u_int cpumask); -extern void smp_openpic_message_pass(int target, int msg, unsigned long data, - int wait); - -extern inline int openpic_to_irq(int irq) -{ - /* IRQ 0 usually means 'disabled'.. don't mess with it - * exceptions to this (sandpoint maybe?) - * shouldn't use openpic_to_irq - */ - if (irq != 0){ - return irq += NUM_8259_INTERRUPTS; - } else { - return 0; - } -} -/*extern int open_pic_irq_offset;*/ -#endif /* _PPC_KERNEL_OPEN_PIC_H */ diff -Nru a/arch/ppc/kernel/open_pic_defs.h b/arch/ppc/kernel/open_pic_defs.h --- a/arch/ppc/kernel/open_pic_defs.h Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/kernel/open_pic_defs.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.open_pic_defs.h 1.8 08/20/01 22:33:28 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/openpic.h -- OpenPIC definitions @@ -207,6 +207,14 @@ #define OPENPIC_CONFIG_RESET 0x80000000 #define OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE 0x20000000 #define OPENPIC_CONFIG_BASE_MASK 0x000fffff + + /* + * Global Configuration Register 1 + * This is the EICR on EPICs. + */ + +#define OPENPIC_EICR_S_CLK_MASK 0x70000000 +#define OPENPIC_EICR_SIE 0x08000000 /* * Vendor Identification Register diff -Nru a/arch/ppc/kernel/pci-dma.c b/arch/ppc/kernel/pci-dma.c --- a/arch/ppc/kernel/pci-dma.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/kernel/pci-dma.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.pci-dma.c 1.5 05/17/01 18:14:21 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * Copyright (C) 2000 Ani Joshi @@ -25,7 +25,11 @@ if (hwdev == NULL || hwdev->dma_mask != 0xffffffff) gfp |= GFP_DMA; +#ifdef CONFIG_NOT_COHERENT_CACHE + ret = consistent_alloc(gfp, size, dma_handle); +#else ret = (void *)__get_free_pages(gfp, get_order(size)); +#endif if (ret != NULL) { memset(ret, 0, size); @@ -37,5 +41,9 @@ void pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) { +#ifdef CONFIG_NOT_COHERENT_CACHE + consistent_free(vaddr); +#else free_pages((unsigned long)vaddr, get_order(size)); +#endif } diff -Nru a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c --- a/arch/ppc/kernel/pci.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/pci.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.pci.c 1.35 11/13/01 08:19:57 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Common pmac/prep/chrp pci routines. -- Cort @@ -19,16 +19,13 @@ #include #include #include +#include #include -#include #include #include -#include #include -#include "pci.h" - -#define DEBUG +#undef DEBUG #ifdef DEBUG #define DBG(x...) printk(x) @@ -42,8 +39,13 @@ void pcibios_make_OF_bus_map(void); +static int pci_relocate_bridge_resource(struct pci_bus *bus, int i); +static int probe_resource(struct pci_bus *parent, struct resource *pr, + struct resource *res, struct resource **conflict); +static void update_bridge_base(struct pci_bus *bus, int i); static void pcibios_fixup_resources(struct pci_dev* dev); static void fixup_broken_pcnet32(struct pci_dev* dev); +static int reparent_resources(struct resource *parent, struct resource *res); static void fixup_rev1_53c810(struct pci_dev* dev); #ifdef CONFIG_ALL_PPC static void pcibios_fixup_cardbus(struct pci_dev* dev); @@ -66,7 +68,7 @@ { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources }, #ifdef CONFIG_ALL_PPC /* We should add per-machine fixup support in xxx_setup.c or xxx_pci.c */ - { PCI_FIXUP_FINAL, PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1211, pcibios_fixup_cardbus }, + { PCI_FIXUP_FINAL, PCI_VENDOR_ID_TI, PCI_ANY_ID, pcibios_fixup_cardbus }, #endif /* CONFIG_ALL_PPC */ { 0 } }; @@ -96,7 +98,7 @@ void pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) + struct resource *res, int resource) { u32 new, check; int reg; @@ -104,6 +106,7 @@ unsigned long io_offset; new = res->start; + res->flags &= ~IORESOURCE_UNSET; if (hose && res->flags & IORESOURCE_IO) { io_offset = (unsigned long)hose->io_base_virt - isa_io_base; new -= io_offset; @@ -128,6 +131,9 @@ "%s/%d (%08x != %08x)\n", dev->slot_name, resource, new, check); } + printk(KERN_INFO "PCI: moved device %s resource %d (%lx) to %x\n", + dev->slot_name, resource, res->flags, + new & ~PCI_REGION_FLAG_MASK); } static void @@ -143,13 +149,14 @@ } for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { struct resource *res = dev->resource + i; - if (!res->start || !res->flags) + if (!res->flags) continue; - if (res->end == 0xffffffff) { + if (!res->start || res->end == 0xffffffff) { DBG("PCI:%s Resource %d [%08lx-%08lx] is unassigned\n", dev->slot_name, i, res->start, res->end); res->end -= res->start; res->start = 0; + res->flags |= IORESOURCE_UNSET; continue; } offset = 0; @@ -169,24 +176,35 @@ #endif } } + + /* Call machine specific resource fixup */ + if (ppc_md.pcibios_fixup_resources) + ppc_md.pcibios_fixup_resources(dev); } #ifdef CONFIG_ALL_PPC static void pcibios_fixup_cardbus(struct pci_dev* dev) { + if (_machine != _MACH_Pmac) + return; /* * Fix the interrupt routing on the TI1211 chip on the 1999 * G3 powerbook, which doesn't get initialized properly by OF. + * Same problem with the 1410 of the new titanium pbook which + * has the same register. */ if (dev->vendor == PCI_VENDOR_ID_TI - && dev->device == PCI_DEVICE_ID_TI_1211) { - u32 val; + && (dev->device == PCI_DEVICE_ID_TI_1211 || + dev->device == PCI_DEVICE_ID_TI_1410)) { + u8 val; /* 0x8c == TI122X_IRQMUX, 2 says to route the INTA signal out the MFUNC0 pin */ - if (pci_read_config_dword(dev, 0x8c, &val) == 0 - && val == 0) - pci_write_config_dword(dev, 0x8c, 2); + if (pci_read_config_byte(dev, 0x8c, &val) == 0) + pci_write_config_byte(dev, 0x8c, (val & ~0x0f) | 2); + /* Disable ISA interrupt mode */ + if (pci_read_config_byte(dev, 0x92, &val) == 0) + pci_write_config_byte(dev, 0x92, val & ~0x06); } } #endif /* CONFIG_ALL_PPC */ @@ -271,25 +289,245 @@ for (ln = bus_list->next; ln != bus_list; ln=ln->next) { bus = pci_bus_b(ln); for (i = 0; i < 4; ++i) { - if ((res = bus->resource[i]) == NULL || !res->flags) + if ((res = bus->resource[i]) == NULL || !res->flags + || res->start > res->end) continue; if (bus->parent == NULL) pr = (res->flags & IORESOURCE_IO)? &ioport_resource: &iomem_resource; - else + else { pr = pci_find_parent_resource(bus->self, res); + if (pr == res) { + /* this happens when the generic PCI + * code (wrongly) decides that this + * bridge is transparent -- paulus + */ + continue; + } + } - if (pr && request_resource(pr, res) == 0) - continue; + DBG("PCI: bridge rsrc %lx..%lx (%lx), parent %p\n", + res->start, res->end, res->flags, pr); + if (pr) { + if (request_resource(pr, res) == 0) + continue; + /* + * Must be a conflict with an existing entry. + * Move that entry (or entries) under the + * bridge resource and try again. + */ + if (reparent_resources(pr, res) == 0) + continue; + } printk(KERN_ERR "PCI: Cannot allocate resource region " "%d of PCI bridge %d\n", i, bus->number); - DBG("PCI: resource is %lx..%lx (%lx), parent %p\n", - res->start, res->end, res->flags, pr); + if (pci_relocate_bridge_resource(bus, i)) + bus->resource[i] = NULL; } pcibios_allocate_bus_resources(&bus->children); } } +/* + * Reparent resource children of pr that conflict with res + * under res, and make res replace those children. + */ +static int __init +reparent_resources(struct resource *parent, struct resource *res) +{ + struct resource *p, **pp; + struct resource **firstpp = NULL; + + for (pp = &parent->child; (p = *pp) != NULL; pp = &p->sibling) { + if (p->end < res->start) + continue; + if (res->end < p->start) + break; + if (p->start < res->start || p->end > res->end) + return -1; /* not completely contained */ + if (firstpp == NULL) + firstpp = pp; + } + if (firstpp == NULL) + return -1; /* didn't find any conflicting entries? */ + res->parent = parent; + res->child = *firstpp; + res->sibling = *pp; + *firstpp = res; + *pp = NULL; + for (p = res->child; p != NULL; p = p->sibling) { + p->parent = res; + DBG(KERN_INFO "PCI: reparented %s [%lx..%lx] under %s\n", + p->name, p->start, p->end, res->name); + } + return 0; +} + +/* + * A bridge has been allocated a range which is outside the range + * of its parent bridge, so it needs to be moved. + */ +static int __init +pci_relocate_bridge_resource(struct pci_bus *bus, int i) +{ + struct resource *res, *pr, *conflict; + unsigned long try, size; + int j; + struct pci_bus *parent = bus->parent; + + if (parent == NULL) { + /* shouldn't ever happen */ + printk(KERN_ERR "PCI: can't move host bridge resource\n"); + return -1; + } + res = bus->resource[i]; + pr = NULL; + for (j = 0; j < 4; j++) { + struct resource *r = parent->resource[j]; + if (!r) + continue; + if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM)) + continue; + if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH)) { + pr = r; + break; + } + if (res->flags & IORESOURCE_PREFETCH) + pr = r; + } + if (pr == NULL) + return -1; + size = res->end - res->start; + if (pr->start > pr->end || size > pr->end - pr->start) + return -1; + try = pr->end; + for (;;) { + res->start = try - size; + res->end = try; + if (probe_resource(bus->parent, pr, res, &conflict) == 0) + break; + if (conflict->start <= pr->start + size) + return -1; + try = conflict->start - 1; + } + if (request_resource(pr, res)) { + DBG(KERN_ERR "PCI: huh? couldn't move to %lx..%lx\n", + res->start, res->end); + return -1; /* "can't happen" */ + } + update_bridge_base(bus, i); + printk(KERN_INFO "PCI: bridge %d resource %d moved to %lx..%lx\n", + bus->number, i, res->start, res->end); + return 0; +} + +static int __init +probe_resource(struct pci_bus *parent, struct resource *pr, + struct resource *res, struct resource **conflict) +{ + struct pci_bus *bus; + struct pci_dev *dev; + struct resource *r; + struct list_head *ln; + int i; + + for (r = pr->child; r != NULL; r = r->sibling) { + if (r->end >= res->start && res->end >= r->start) { + *conflict = r; + return 1; + } + } + for (ln = parent->children.next; ln != &parent->children; + ln = ln->next) { + bus = pci_bus_b(ln); + for (i = 0; i < 4; ++i) { + if ((r = bus->resource[i]) == NULL) + continue; + if (!r->flags || r->start > r->end || r == res) + continue; + if (pci_find_parent_resource(bus->self, r) != pr) + continue; + if (r->end >= res->start && res->end >= r->start) { + *conflict = r; + return 1; + } + } + } + for (ln = parent->devices.next; ln != &parent->devices; ln=ln->next) { + dev = pci_dev_b(ln); + for (i = 0; i < 6; ++i) { + r = &dev->resource[i]; + if (!r->flags || (r->flags & IORESOURCE_UNSET)) + continue; + if (pci_find_parent_resource(bus->self, r) != pr) + continue; + if (r->end >= res->start && res->end >= r->start) { + *conflict = r; + return 1; + } + } + } + return 0; +} + +static void __init +update_bridge_base(struct pci_bus *bus, int i) +{ + struct resource *res = bus->resource[i]; + u8 io_base_lo, io_limit_lo; + u16 mem_base, mem_limit; + u16 cmd; + unsigned long start, end, off; + struct pci_dev *dev = bus->self; + struct pci_controller *hose = dev->sysdata; + + if (!hose) { + printk("update_bridge_base: no hose?\n"); + return; + } + pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_write_config_word(dev, PCI_COMMAND, + cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)); + if (res->flags & IORESOURCE_IO) { + off = (unsigned long) hose->io_base_virt - isa_io_base; + start = res->start - off; + end = res->end - off; + io_base_lo = (start >> 8) & PCI_IO_RANGE_MASK; + io_limit_lo = (end >> 8) & PCI_IO_RANGE_MASK; + if (end > 0xffff) { + pci_write_config_word(dev, PCI_IO_BASE_UPPER16, + start >> 16); + pci_write_config_word(dev, PCI_IO_LIMIT_UPPER16, + end >> 16); + io_base_lo |= PCI_IO_RANGE_TYPE_32; + } else + io_base_lo |= PCI_IO_RANGE_TYPE_16; + pci_write_config_byte(dev, PCI_IO_BASE, io_base_lo); + pci_write_config_byte(dev, PCI_IO_LIMIT, io_limit_lo); + + } else if ((res->flags & (IORESOURCE_MEM | IORESOURCE_PREFETCH)) + == IORESOURCE_MEM) { + off = hose->pci_mem_offset; + mem_base = ((res->start - off) >> 16) & PCI_MEMORY_RANGE_MASK; + mem_limit = ((res->end - off) >> 16) & PCI_MEMORY_RANGE_MASK; + pci_write_config_word(dev, PCI_MEMORY_BASE, mem_base); + pci_write_config_word(dev, PCI_MEMORY_LIMIT, mem_limit); + + } else if ((res->flags & (IORESOURCE_MEM | IORESOURCE_PREFETCH)) + == (IORESOURCE_MEM | IORESOURCE_PREFETCH)) { + off = hose->pci_mem_offset; + mem_base = ((res->start - off) >> 16) & PCI_PREF_RANGE_MASK; + mem_limit = ((res->end - off) >> 16) & PCI_PREF_RANGE_MASK; + pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, mem_base); + pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, mem_limit); + + } else { + DBG(KERN_ERR "PCI: ugh, bridge %s res %d has flags=%lx\n", + dev->slot_name, i, res->flags); + } + pci_write_config_word(dev, PCI_COMMAND, cmd); +} + static inline void alloc_resource(struct pci_dev *dev, int idx) { struct resource *pr, *r = &dev->resource[idx]; @@ -304,6 +542,7 @@ DBG("PCI: parent is %p: %08lx-%08lx (f=%lx)\n", pr, pr->start, pr->end, pr->flags); /* We'll assign a new address later */ + r->flags |= IORESOURCE_UNSET; r->end -= r->start; r->start = 0; } @@ -323,8 +562,8 @@ r = &dev->resource[idx]; if (r->parent) /* Already allocated */ continue; - if (!r->start) /* Not assigned at all */ - continue; + if (!r->flags || (r->flags & IORESOURCE_UNSET)) + continue; /* Not assigned at all */ if (r->flags & IORESOURCE_IO) disabled = !(command & PCI_COMMAND_IO); else @@ -370,7 +609,7 @@ * or because we have decided the old address was * unusable for some reason. */ - if (!r->start && r->end && + if ((r->flags & IORESOURCE_UNSET) && r->end && (!ppc_md.pcibios_enable_device_hook || !ppc_md.pcibios_enable_device_hook(dev, 1))) pci_assign_resource(dev, idx); @@ -396,9 +635,9 @@ pci_read_config_word(dev, PCI_COMMAND, &cmd); old_cmd = cmd; - for(idx=0; idx<6; idx++) { + for (idx=0; idx<6; idx++) { r = &dev->resource[idx]; - if (!r->start && r->end) { + if (r->flags & IORESOURCE_UNSET) { printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); return -EINVAL; } @@ -438,7 +677,7 @@ /* * Functions below are used on OpenFirmware machines. */ -static void +static void __openfirmware make_one_node_map(struct device_node* node, u8 pci_bus) { int *bus_range; @@ -449,7 +688,7 @@ bus_range = (int *) get_property(node, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s\n", - node->full_name); + node->full_name); return; } pci_to_OF_bus_map[pci_bus] = bus_range[0]; @@ -472,7 +711,7 @@ } } -void +void __openfirmware pcibios_make_OF_bus_map(void) { int i; @@ -512,17 +751,17 @@ #endif } -static struct device_node* -scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn) +typedef int (*pci_OF_scan_iterator)(struct device_node* node, void* data); + +static struct device_node* __openfirmware +scan_OF_pci_childs(struct device_node* node, pci_OF_scan_iterator filter, void* data) { struct device_node* sub_node; for (; node != 0;node = node->sibling) { - unsigned int *class_code, *reg; + unsigned int *class_code; - reg = (unsigned int *) get_property(node, "reg", 0); - if (reg && ((reg[0] >> 8) & 0xff) == dev_fn - && ((reg[0] >> 16) & 0xff) == bus) + if (filter(node, data)) return node; /* For PCI<->PCI bridges or CardBus bridges, we go down @@ -535,13 +774,34 @@ (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) && strcmp(node->name, "multifunc-device")) continue; - sub_node = scan_OF_childs_for_device(node->child, bus, dev_fn); + sub_node = scan_OF_pci_childs(node->child, filter, data); if (sub_node) return sub_node; } return NULL; } +static int +scan_OF_pci_childs_iterator(struct device_node* node, void* data) +{ + unsigned int *reg; + u8* fdata = (u8*)data; + + reg = (unsigned int *) get_property(node, "reg", 0); + if (reg && ((reg[0] >> 8) & 0xff) == fdata[1] + && ((reg[0] >> 16) & 0xff) == fdata[0]) + return 1; + return 0; +} + +static struct device_node* __openfirmware +scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn) +{ + u8 filter_data[2] = {bus, dev_fn}; + + return scan_OF_pci_childs(node, scan_OF_pci_childs_iterator, filter_data); +} + /* * Scans the OF tree for a device node matching a PCI device */ @@ -598,6 +858,12 @@ return NULL; } +static int __openfirmware +find_OF_pci_device_filter(struct device_node* node, void* data) +{ + return ((void *)node == data); +} + /* * Returns the PCI device matching a given OF node */ @@ -605,21 +871,40 @@ pci_device_from_OF_node(struct device_node* node, u8* bus, u8* devfn) { unsigned int *reg; - int i; + struct pci_controller* hose; + struct pci_dev* dev; if (!have_of) return -ENODEV; + /* Make sure it's really a PCI device */ + hose = pci_find_hose_for_OF_device(node); + if (!hose || !hose->arch_data) + return -ENODEV; + if (!scan_OF_pci_childs(((struct device_node*)hose->arch_data)->child, + find_OF_pci_device_filter, (void *)node)) + return -ENODEV; reg = (unsigned int *) get_property(node, "reg", 0); if (!reg) return -ENODEV; *bus = (reg[0] >> 16) & 0xff; - for (i=0; pci_to_OF_bus_map && i> 8) & 0xff); - return 0; + + /* Ok, here we need some tweak. If we have already renumbered + * all busses, we can't rely on the OF bus number any more. + * the pci_to_OF_bus_map is not enough as several PCI busses + * may match the same OF bus number. + */ + if (!pci_to_OF_bus_map) + return 0; + pci_for_each_dev(dev) { + if (pci_to_OF_bus_map[dev->bus->number] != *bus) + continue; + if (dev->devfn != *devfn) + continue; + *bus = dev->bus->number; + return 0; + } + return -ENODEV; } void __init @@ -709,6 +994,24 @@ ranges += np; } } + +/* We create the "pci-OF-bus-map" property now so it appears in the + * /proc device tree + */ +void __init +pci_create_OF_bus_map(void) +{ + struct property* of_prop; + + of_prop = (struct property*) alloc_bootmem(sizeof(struct property) + 256); + if (of_prop && find_path_device("/")) { + memset(of_prop, -1, sizeof(struct property) + 256); + of_prop->name = "pci-OF-bus-map"; + of_prop->length = 256; + of_prop->value = (unsigned char *)&of_prop[1]; + prom_add_property(find_path_device("/"), of_prop); + } +} #endif /* CONFIG_ALL_PPC */ void __init @@ -739,6 +1042,10 @@ if (pci_assign_all_busses && have_of) pcibios_make_OF_bus_map(); + /* Do machine dependent PCI interrupt routing */ + if (ppc_md.pci_swizzle && ppc_md.pci_map_irq) + pci_fixup_irqs(ppc_md.pci_swizzle, ppc_md.pci_map_irq); + /* Call machine dependant fixup */ if (ppc_md.pcibios_fixup) ppc_md.pcibios_fixup(); @@ -754,10 +1061,23 @@ ppc_md.pcibios_after_init(); } -int __init -pcibios_assign_all_busses(void) +unsigned char __init +common_swizzle(struct pci_dev *dev, unsigned char *pinp) { - return pci_assign_all_busses; + struct pci_controller *hose = dev->sysdata; + + if (dev->bus->number != hose->first_busno) { + u8 pin = *pinp; + do { + pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + /* Move up the chain of bridges. */ + dev = dev->bus->self; + } while (dev->bus->self); + *pinp = pin; + + /* The slot is the idsel of the last bridge. */ + } + return PCI_SLOT(dev->devfn); } void __init @@ -863,7 +1183,7 @@ old_cmd = cmd; for (idx=0; idx<6; idx++) { r = &dev->resource[idx]; - if (!r->start && r->end) { + if (r->flags & IORESOURCE_UNSET) { printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); return -EINVAL; } @@ -1150,6 +1470,19 @@ } return result; +} + +void __init +pci_init_resource(struct resource *res, unsigned long start, unsigned long end, + int flags, char *name) +{ + res->start = start; + res->end = end; + res->flags = flags; + res->name = name; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; } /* diff -Nru a/arch/ppc/kernel/pci.h b/arch/ppc/kernel/pci.h --- a/arch/ppc/kernel/pci.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,27 +0,0 @@ -/* - * BK Id: SCCS/s.pci.h 1.10 08/08/01 16:35:43 paulus - */ - -#ifndef __PPC_KERNEL_PCI_H__ -#define __PPC_KERNEL_PCI_H__ - -/* Configure those in your xxx_init() or xxx_setup_arch() function */ -extern unsigned long isa_io_base; -extern unsigned long isa_mem_base; -extern unsigned long pci_dram_offset; - -/* Set this to 1 if you want the kernel to re-assign all PCI - * bus numbers - */ -extern int pci_assign_all_busses; - - -extern struct pci_controller* pcibios_alloc_controller(void); -extern struct pci_controller* pci_find_hose_for_OF_device( - struct device_node* node); - -extern void setup_indirect_pci(struct pci_controller* hose, - u32 cfg_addr, u32 cfg_data); -extern void setup_grackle(struct pci_controller *hose); - -#endif /* __PPC_KERNEL_PCI_H__ */ diff -Nru a/arch/ppc/kernel/pci_auto.c b/arch/ppc/kernel/pci_auto.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/pci_auto.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,519 @@ +/* + * arch/ppc/kernel/pci_auto.c + * + * PCI autoconfiguration library + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * The CardBus support is very preliminary. Preallocating space is + * the way to go but will require some change in card services to + * make it useful. Eventually this will ensure that we can put + * multiple CB bridges behind multiple P2P bridges. For now, at + * least it ensures that we place the CB bridge BAR and assigned + * initial bus numbers. I definitely need to do something about + * the lack of 16-bit I/O support. -MDP + */ + +#include +#include +#include + +#include + +#define PCIAUTO_IDE_MODE_MASK 0x05 + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif /* DEBUG */ + +static int pciauto_upper_iospc; +static int pciauto_upper_memspc; + +void __init pciauto_setup_bars(struct pci_controller *hose, + int current_bus, + int pci_devfn, + int bar_limit) +{ + int bar_response, bar_size, bar_value; + int bar, addr_mask; + int * upper_limit; + int found_mem64 = 0; + + DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n", + current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) ); + + for (bar = PCI_BASE_ADDRESS_0; bar <= bar_limit; bar+=4) { + /* Tickle the BAR and get the response */ + early_write_config_dword(hose, + current_bus, + pci_devfn, + bar, + 0xffffffff); + early_read_config_dword(hose, + current_bus, + pci_devfn, + bar, + &bar_response); + + /* If BAR is not implemented go to the next BAR */ + if (!bar_response) + continue; + + /* Check the BAR type and set our address mask */ + if (bar_response & PCI_BASE_ADDRESS_SPACE) { + addr_mask = PCI_BASE_ADDRESS_IO_MASK; + upper_limit = &pciauto_upper_iospc; + DBG("PCI Autoconfig: BAR 0x%x, I/O, ", bar); + } else { + if ( (bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == + PCI_BASE_ADDRESS_MEM_TYPE_64) + found_mem64 = 1; + + addr_mask = PCI_BASE_ADDRESS_MEM_MASK; + upper_limit = &pciauto_upper_memspc; + DBG("PCI Autoconfig: BAR 0x%x, Mem ", bar); + } + + /* Calculate requested size */ + bar_size = ~(bar_response & addr_mask) + 1; + + /* Allocate a base address */ + bar_value = (*upper_limit - bar_size) & ~(bar_size - 1); + + /* Write it out and update our limit */ + early_write_config_dword(hose, + current_bus, + pci_devfn, + bar, + bar_value); + + *upper_limit = bar_value; + + /* + * If we are a 64-bit decoder then increment to the + * upper 32 bits of the bar and force it to locate + * in the lower 4GB of memory. + */ + if (found_mem64) { + bar += 4; + early_write_config_dword(hose, + current_bus, + pci_devfn, + bar, + 0x00000000); + found_mem64 = 0; + } + + DBG("size=0x%x, address=0x%x\n", + bar_size, bar_value); + } + +} + +void __init pciauto_prescan_setup_bridge(struct pci_controller *hose, + int current_bus, + int pci_devfn, + int sub_bus, + int *iosave, + int *memsave) +{ + /* Configure bus number registers */ + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_PRIMARY_BUS, + current_bus); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SECONDARY_BUS, + sub_bus + 1); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SUBORDINATE_BUS, + 0xff); + + /* Round memory allocator to 1MB boundary */ + pciauto_upper_memspc &= ~(0x100000 - 1); + *memsave = pciauto_upper_memspc; + + /* Round I/O allocator to 4KB boundary */ + pciauto_upper_iospc &= ~(0x1000 - 1); + *iosave = pciauto_upper_iospc; + + /* Set up memory and I/O filter limits, assume 32-bit I/O space */ + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_MEMORY_LIMIT, + ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_IO_LIMIT, + ((pciauto_upper_iospc - 1) & 0x0000f000) >> 8); + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_IO_LIMIT_UPPER16, + ((pciauto_upper_iospc - 1) & 0xffff0000) >> 16); + + /* Zero upper 32 bits of prefetchable base/limit */ + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_PREF_BASE_UPPER32, + 0); + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_PREF_LIMIT_UPPER32, + 0); +} + +void __init pciauto_postscan_setup_bridge(struct pci_controller *hose, + int current_bus, + int pci_devfn, + int sub_bus, + int *iosave, + int *memsave) +{ + int cmdstat; + + /* Configure bus number registers */ + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SUBORDINATE_BUS, + sub_bus); + + /* + * Round memory allocator to 1MB boundary. + * If no space used, allocate minimum. + */ + pciauto_upper_memspc &= ~(0x100000 - 1); + if (*memsave == pciauto_upper_memspc) + pciauto_upper_memspc -= 0x00100000; + + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_MEMORY_BASE, + pciauto_upper_memspc >> 16); + + /* Allocate 1MB for pre-fretch */ + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_PREF_MEMORY_LIMIT, + ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16); + + pciauto_upper_memspc -= 0x100000; + + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_PREF_MEMORY_BASE, + pciauto_upper_memspc >> 16); + + /* Round I/O allocator to 4KB boundary */ + pciauto_upper_iospc &= ~(0x1000 - 1); + if (*iosave == pciauto_upper_iospc) + pciauto_upper_iospc -= 0x1000; + + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_IO_BASE, + (pciauto_upper_iospc & 0x0000f000) >> 8); + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_IO_BASE_UPPER16, + pciauto_upper_iospc >> 16); + + /* Enable memory and I/O accesses, enable bus master */ + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + &cmdstat); + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + cmdstat | + PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER); +} + +void __init pciauto_prescan_setup_cardbus_bridge(struct pci_controller *hose, + int current_bus, + int pci_devfn, + int sub_bus, + int *iosave, + int *memsave) +{ + /* Configure bus number registers */ + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_PRIMARY_BUS, + current_bus); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SECONDARY_BUS, + sub_bus + 1); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SUBORDINATE_BUS, + 0xff); + + /* Round memory allocator to 4KB boundary */ + pciauto_upper_memspc &= ~(0x1000 - 1); + *memsave = pciauto_upper_memspc; + + /* Round I/O allocator to 4 byte boundary */ + pciauto_upper_iospc &= ~(0x4 - 1); + *iosave = pciauto_upper_iospc; + + /* Set up memory and I/O filter limits, assume 32-bit I/O space */ + early_write_config_dword(hose, + current_bus, + pci_devfn, + 0x20, + pciauto_upper_memspc - 1); + early_write_config_dword(hose, + current_bus, + pci_devfn, + 0x30, + pciauto_upper_iospc - 1); +} + +void __init pciauto_postscan_setup_cardbus_bridge(struct pci_controller *hose, + int current_bus, + int pci_devfn, + int sub_bus, + int *iosave, + int *memsave) +{ + int cmdstat; + + /* + * Configure subordinate bus number. The PCI subsystem + * bus scan will renumber buses (reserving three additional + * for this PCI<->CardBus bridge for the case where a CardBus + * adapter contains a P2P or CB2CB bridge. + */ + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SUBORDINATE_BUS, + sub_bus); + + /* + * Reserve an additional 4MB for mem space and 16KB for + * I/O space. This should cover any additional space + * requirement of unusual CardBus devices with + * additional bridges that can consume more address space. + * + * Although pcmcia-cs currently will reprogram bridge + * windows, the goal is to add an option to leave them + * alone and use the bridge window ranges as the regions + * that are searched for free resources upon hot-insertion + * of a device. This will allow a PCI<->CardBus bridge + * configured by this routine to happily live behind a + * P2P bridge in a system. + */ + pciauto_upper_memspc -= 0x00400000; + pciauto_upper_iospc -= 0x00004000; + + /* Round memory allocator to 4KB boundary */ + pciauto_upper_memspc &= ~(0x1000 - 1); + + early_write_config_dword(hose, + current_bus, + pci_devfn, + 0x1c, + pciauto_upper_memspc); + + /* Round I/O allocator to 4 byte boundary */ + pciauto_upper_iospc &= ~(0x4 - 1); + early_write_config_dword(hose, + current_bus, + pci_devfn, + 0x2c, + pciauto_upper_iospc); + + /* Enable memory and I/O accesses, enable bus master */ + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + &cmdstat); + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + cmdstat | + PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER); +} + +int __init pciauto_bus_scan(struct pci_controller *hose, int current_bus) +{ + int sub_bus, pci_devfn, pci_class, cmdstat, found_multi = 0; + unsigned short vid; + unsigned char header_type; + + /* + * Fetch our I/O and memory space upper boundaries used + * to allocated base addresses on this hose. + */ + if (current_bus == hose->first_busno) { + pciauto_upper_iospc = hose->io_space.end + 1; + pciauto_upper_memspc = hose->mem_space.end + 1; + } + + sub_bus = current_bus; + + for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) { + /* Skip our host bridge */ + if ( (current_bus == hose->first_busno) && (pci_devfn == 0) ) + continue; + + if (PCI_FUNC(pci_devfn) && !found_multi) + continue; + + /* If config space read fails from this device, move on */ + if (early_read_config_byte(hose, + current_bus, + pci_devfn, + PCI_HEADER_TYPE, + &header_type)) + continue; + + if (!PCI_FUNC(pci_devfn)) + found_multi = header_type & 0x80; + + early_read_config_word(hose, + current_bus, + pci_devfn, + PCI_VENDOR_ID, + &vid); + + if (vid != 0xffff) { + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_CLASS_REVISION, &pci_class); + if ( (pci_class >> 16) == PCI_CLASS_BRIDGE_PCI ) { + int iosave, memsave; + + DBG("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_SLOT(pci_devfn)); + /* Allocate PCI I/O and/or memory space */ + pciauto_setup_bars(hose, + current_bus, + pci_devfn, + PCI_BASE_ADDRESS_1); + + pciauto_prescan_setup_bridge(hose, + current_bus, + pci_devfn, + sub_bus, + &iosave, + &memsave); + sub_bus = pciauto_bus_scan(hose, sub_bus+1); + pciauto_postscan_setup_bridge(hose, + current_bus, + pci_devfn, + sub_bus, + &iosave, + &memsave); + } else if ((pci_class >> 16) == PCI_CLASS_BRIDGE_CARDBUS) { + int iosave, memsave; + + DBG("PCI Autoconfig: Found CardBus bridge, device %d function %d\n", PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn)); + /* Place CardBus Socket/ExCA registers */ + pciauto_setup_bars(hose, + current_bus, + pci_devfn, + PCI_BASE_ADDRESS_0); + + pciauto_prescan_setup_cardbus_bridge(hose, + current_bus, + pci_devfn, + sub_bus, + &iosave, + &memsave); + sub_bus = pciauto_bus_scan(hose, sub_bus+1); + pciauto_postscan_setup_cardbus_bridge(hose, + current_bus, + pci_devfn, + sub_bus, + &iosave, + &memsave); + } else { + if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) { + unsigned char prg_iface; + + early_read_config_byte(hose, + current_bus, + pci_devfn, + PCI_CLASS_PROG, + &prg_iface); + if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) { + DBG("PCI Autoconfig: Skipping legacy mode IDE controller\n"); + continue; + } + } + /* Allocate PCI I/O and/or memory space */ + pciauto_setup_bars(hose, + current_bus, + pci_devfn, + PCI_BASE_ADDRESS_5); + + /* + * Enable some standard settings + */ + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + &cmdstat); + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + cmdstat | + PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_LATENCY_TIMER, + 0x80); + } + } + } + return sub_bus; +} diff -Nru a/arch/ppc/kernel/pmac_backlight.c b/arch/ppc/kernel/pmac_backlight.c --- a/arch/ppc/kernel/pmac_backlight.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,150 +0,0 @@ -/* - * BK Id: SCCS/s.pmac_backlight.c 1.8 09/08/01 15:47:42 paulus - */ -/* - * Miscellaneous procedures for dealing with the PowerMac hardware. - * Contains support for the backlight. - * - * Copyright (C) 2000 Benjamin Herrenschmidt - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static struct backlight_controller *backlighter = NULL; -static void* backlighter_data = NULL; -static int backlight_autosave = 0; -static int backlight_level = BACKLIGHT_MAX; -static int backlight_enabled = 1; - -void __pmac -register_backlight_controller(struct backlight_controller *ctrler, void *data, char *type) -{ - struct device_node* bk_node; - char *prop; - int valid = 0; - - bk_node = find_devices("backlight"); - -#ifdef CONFIG_ADB_PMU - /* Special case for the old PowerBook since I can't test on it */ - backlight_autosave = machine_is_compatible("AAPL,3400/2400") - || machine_is_compatible("AAPL,3500"); - if ((backlight_autosave - || machine_is_compatible("AAPL,PowerBook1998") - || machine_is_compatible("PowerBook1,1")) - && !strcmp(type, "pmu")) - valid = 1; -#endif - if (bk_node) { - prop = get_property(bk_node, "backlight-control", NULL); - if (prop && !strncmp(prop, type, strlen(type))) - valid = 1; - } - if (!valid) - return; - backlighter = ctrler; - backlighter_data = data; - - if (bk_node && !backlight_autosave) - prop = get_property(bk_node, "bklt", NULL); - else - prop = NULL; - if (prop) { - backlight_level = ((*prop)+1) >> 1; - if (backlight_level > BACKLIGHT_MAX) - backlight_level = BACKLIGHT_MAX; - } - -#ifdef CONFIG_ADB_PMU - if (backlight_autosave) { - struct adb_request req; - pmu_request(&req, NULL, 2, 0xd9, 0); - while (!req.complete) - pmu_poll(); - backlight_level = req.reply[1] >> 4; - } -#endif - if (!backlighter->set_enable(1, backlight_level, data)) - backlight_enabled = 1; - - printk(KERN_INFO "Registered \"%s\" backlight controller, level: %d/15\n", - type, backlight_level); -} - -void __pmac -unregister_backlight_controller(struct backlight_controller *ctrler, void *data) -{ - /* We keep the current backlight level (for now) */ - if (ctrler == backlighter && data == backlighter_data) - backlighter = NULL; -} - -int __pmac -set_backlight_enable(int enable) -{ - int rc; - - if (!backlighter) - return -ENODEV; - rc = backlighter->set_enable(enable, backlight_level, backlighter_data); - if (!rc) - backlight_enabled = enable; - return rc; -} - -int __pmac -get_backlight_enable(void) -{ - if (!backlighter) - return -ENODEV; - return backlight_enabled; -} - -int __pmac -set_backlight_level(int level) -{ - int rc = 0; - - if (!backlighter) - return -ENODEV; - if (level < BACKLIGHT_MIN) - level = BACKLIGHT_OFF; - if (level > BACKLIGHT_MAX) - level = BACKLIGHT_MAX; - if (backlight_enabled) - rc = backlighter->set_level(level, backlighter_data); - if (!rc) - backlight_level = level; - if (!rc && !backlight_autosave) { - level <<=1; - if (level & 0x10) - level |= 0x01; - // -- todo: save to property "bklt" - } - return rc; -} - -int __pmac -get_backlight_level(void) -{ - if (!backlighter) - return -ENODEV; - return backlight_level; -} diff -Nru a/arch/ppc/kernel/pmac_nvram.c b/arch/ppc/kernel/pmac_nvram.c --- a/arch/ppc/kernel/pmac_nvram.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,402 +0,0 @@ -/* - * BK Id: SCCS/s.pmac_nvram.c 1.15 09/08/01 15:47:42 paulus - */ -/* - * Miscellaneous procedures for dealing with the PowerMac hardware. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#undef DEBUG - -#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ - -#define CORE99_SIGNATURE 0x5a -#define CORE99_ADLER_START 0x14 - -/* Core99 nvram is a flash */ -#define CORE99_FLASH_STATUS_DONE 0x80 -#define CORE99_FLASH_STATUS_ERR 0x38 -#define CORE99_FLASH_CMD_ERASE_CONFIRM 0xd0 -#define CORE99_FLASH_CMD_ERASE_SETUP 0x20 -#define CORE99_FLASH_CMD_RESET 0xff -#define CORE99_FLASH_CMD_WRITE_SETUP 0x40 - -/* CHRP NVRAM header */ -struct chrp_header { - u8 signature; - u8 cksum; - u16 len; - char name[12]; - u8 data[0]; -}; - -struct core99_header { - struct chrp_header hdr; - u32 adler; - u32 generation; - u32 reserved[2]; -}; - -/* - * Read and write the non-volatile RAM on PowerMacs and CHRP machines. - */ -static int nvram_naddrs; -static volatile unsigned char *nvram_addr; -static volatile unsigned char *nvram_data; -static int nvram_mult, is_core_99; -static int core99_bank = 0; -static int nvram_partitions[3]; - -/* FIXME: kmalloc fails to allocate the image now that I had to move it - * before time_init(). For now, I allocate a static buffer here - * but it's a waste of space on all but core99 machines - */ -#if 0 -static char* nvram_image; -#else -static char nvram_image[NVRAM_SIZE] __pmacdata; -#endif - -extern int pmac_newworld; - -static u8 __openfirmware -chrp_checksum(struct chrp_header* hdr) -{ - u8 *ptr; - u16 sum = hdr->signature; - for (ptr = (u8 *)&hdr->len; ptr < hdr->data; ptr++) - sum += *ptr; - while (sum > 0xFF) - sum = (sum & 0xFF) + (sum>>8); - return sum; -} - -static u32 __pmac -core99_calc_adler(u8 *buffer) -{ - int cnt; - u32 low, high; - - buffer += CORE99_ADLER_START; - low = 1; - high = 0; - for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) { - if ((cnt % 5000) == 0) { - high %= 65521UL; - high %= 65521UL; - } - low += buffer[cnt]; - high += low; - } - low %= 65521UL; - high %= 65521UL; - - return (high << 16) | low; -} - -static u32 __pmac -core99_check(u8* datas) -{ - struct core99_header* hdr99 = (struct core99_header*)datas; - - if (hdr99->hdr.signature != CORE99_SIGNATURE) { -#ifdef DEBUG - printk("Invalid signature\n"); -#endif - return 0; - } - if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) { -#ifdef DEBUG - printk("Invalid checksum\n"); -#endif - return 0; - } - if (hdr99->adler != core99_calc_adler(datas)) { -#ifdef DEBUG - printk("Invalid adler\n"); -#endif - return 0; - } - return hdr99->generation; -} - -static int __pmac -core99_erase_bank(int bank) -{ - int stat, i; - - u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; - - out_8(base, CORE99_FLASH_CMD_ERASE_SETUP); - out_8(base, CORE99_FLASH_CMD_ERASE_CONFIRM); - do { stat = in_8(base); } - while(!(stat & CORE99_FLASH_STATUS_DONE)); - out_8(base, CORE99_FLASH_CMD_RESET); - if (stat & CORE99_FLASH_STATUS_ERR) { - printk("nvram: flash error 0x%02x on erase !\n", stat); - return -ENXIO; - } - for (i=0; iname, "common")) - nvram_partitions[pmac_nvram_OF] = offset + 0x10; - if (!strcmp(hdr->name, "APL,MacOS75")) { - nvram_partitions[pmac_nvram_XPRAM] = offset + 0x10; - nvram_partitions[pmac_nvram_NR] = offset + 0x110; - } - offset += (hdr->len * 0x10); - } while(offset < NVRAM_SIZE); - } else { - nvram_partitions[pmac_nvram_OF] = 0x1800; - nvram_partitions[pmac_nvram_XPRAM] = 0x1300; - nvram_partitions[pmac_nvram_NR] = 0x1400; - } -#ifdef DEBUG - printk("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]); - printk("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]); - printk("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]); -#endif -} - -void __init -pmac_nvram_init(void) -{ - struct device_node *dp; - - nvram_naddrs = 0; - - dp = find_devices("nvram"); - if (dp == NULL) { - printk(KERN_ERR "Can't find NVRAM device\n"); - return; - } - nvram_naddrs = dp->n_addrs; - is_core_99 = device_is_compatible(dp, "nvram,flash"); - if (is_core_99) { - int i; - u32 gen_bank0, gen_bank1; - - if (nvram_naddrs < 1) { - printk(KERN_ERR "nvram: no address\n"); - return; - } -#if 0 - nvram_image = kmalloc(NVRAM_SIZE, GFP_KERNEL); - if (!nvram_image) { - printk(KERN_ERR "nvram: can't allocate image\n"); - return; - } -#endif - nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2); -#ifdef DEBUG - printk("nvram: Checking bank 0...\n"); -#endif - gen_bank0 = core99_check((u8 *)nvram_data); - gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE); - core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0; -#ifdef DEBUG - printk("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); - printk("nvram: Active bank is: %d\n", core99_bank); -#endif - for (i=0; iaddrs[0].address + isa_mem_base, - dp->addrs[0].size); - nvram_mult = 1; - } else if (nvram_naddrs == 1) { - nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); - nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE; - } else if (nvram_naddrs == 2) { - nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); - nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); - } else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) { - nvram_naddrs = -1; - } else { - printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", - nvram_naddrs); - } - lookup_partitions(); -} - -void __pmac -pmac_nvram_update(void) -{ - struct core99_header* hdr99; - - if (!is_core_99 || !nvram_data || !nvram_image) - return; - if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE, - NVRAM_SIZE)) - return; -#ifdef DEBUG - printk("Updating nvram...\n"); -#endif - hdr99 = (struct core99_header*)nvram_image; - hdr99->generation++; - hdr99->hdr.signature = CORE99_SIGNATURE; - hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); - hdr99->adler = core99_calc_adler(nvram_image); - core99_bank = core99_bank ? 0 : 1; - if (core99_erase_bank(core99_bank)) { - printk("nvram: Error erasing bank %d\n", core99_bank); - return; - } - if (core99_write_bank(core99_bank, nvram_image)) - printk("nvram: Error writing bank %d\n", core99_bank); -} - -unsigned char __openfirmware -nvram_read_byte(int addr) -{ - switch (nvram_naddrs) { -#ifdef CONFIG_ADB_PMU - case -1: { - struct adb_request req; - - if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM, - (addr >> 8) & 0xff, addr & 0xff)) - break; - while (!req.complete) - pmu_poll(); - return req.reply[1]; - } -#endif - case 1: - if (is_core_99) - return nvram_image[addr]; - return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; - case 2: - *nvram_addr = addr >> 5; - eieio(); - return nvram_data[(addr & 0x1f) << 4]; - } - return 0; -} - -void __openfirmware -nvram_write_byte(unsigned char val, int addr) -{ - switch (nvram_naddrs) { -#ifdef CONFIG_ADB_PMU - case -1: { - struct adb_request req; - - if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM, - (addr >> 8) & 0xff, addr & 0xff, val)) - break; - while (!req.complete) - pmu_poll(); - break; - } -#endif - case 1: - if (is_core_99) { - nvram_image[addr] = val; - break; - } - nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; - break; - case 2: - *nvram_addr = addr >> 5; - eieio(); - nvram_data[(addr & 0x1f) << 4] = val; - break; - } - eieio(); -} - -int __pmac -pmac_get_partition(int partition) -{ - return nvram_partitions[partition]; -} - -u8 __pmac -pmac_xpram_read(int xpaddr) -{ - int offset = nvram_partitions[pmac_nvram_XPRAM]; - - if (offset < 0) - return 0; - - return nvram_read_byte(xpaddr + offset); -} - -void __pmac -pmac_xpram_write(int xpaddr, u8 data) -{ - int offset = nvram_partitions[pmac_nvram_XPRAM]; - - if (offset < 0) - return; - - nvram_write_byte(xpaddr + offset, data); -} diff -Nru a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c --- a/arch/ppc/kernel/pmac_pci.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,626 +0,0 @@ -/* - * BK Id: SCCS/s.pmac_pci.c 1.27 09/08/01 15:47:42 paulus - */ -/* - * Support for PCI bridges found on Power Macintoshes. - * At present the "bandit" and "chaos" bridges are supported. - * Fortunately you access configuration space in the same - * way with either bridge. - * - * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "pci.h" - -#undef DEBUG - -static void add_bridges(struct device_node *dev); - -/* XXX Could be per-controller, but I don't think we risk anything by - * assuming we won't have both UniNorth and Bandit */ -static int has_uninorth; - -/* - * Magic constants for enabling cache coherency in the bandit/PSX bridge. - */ -#define BANDIT_DEVID_2 8 -#define BANDIT_REVID 3 - -#define BANDIT_DEVNUM 11 -#define BANDIT_MAGIC 0x50 -#define BANDIT_COHERENT 0x40 - -static int __init -fixup_one_level_bus_range(struct device_node *node, int higher) -{ - for (; node != 0;node = node->sibling) { - int * bus_range; - unsigned int *class_code; - int len; - - /* For PCI<->PCI bridges or CardBus bridges, we go down */ - class_code = (unsigned int *) get_property(node, "class-code", 0); - if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && - (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) - continue; - bus_range = (int *) get_property(node, "bus-range", &len); - if (bus_range != NULL && len > 2 * sizeof(int)) { - if (bus_range[1] > higher) - higher = bus_range[1]; - } - higher = fixup_one_level_bus_range(node->child, higher); - } - return higher; -} - -/* This routine fixes the "bus-range" property of all bridges in the - * system since they tend to have their "last" member wrong on macs - * - * Note that the bus numbers manipulated here are OF bus numbers, they - * are not Linux bus numbers. - */ -static void __init -fixup_bus_range(struct device_node *bridge) -{ - int * bus_range; - int len; - - /* Lookup the "bus-range" property for the hose */ - bus_range = (int *) get_property(bridge, "bus-range", &len); - if (bus_range == NULL || len < 2 * sizeof(int)) { - printk(KERN_WARNING "Can't get bus-range for %s\n", - bridge->full_name); - return; - } - bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]); -} - -/* - * Apple MacRISC (UniNorth, Bandit, Chaos) PCI controllers. - * - * The "Bandit" version is present in all early PCI PowerMacs, - * and up to the first ones using Grackle. Some machines may - * have 2 bandit controllers (2 PCI busses). - * - * "Chaos" is used in some "Bandit"-type machines as a bridge - * for the separate display bus. It is accessed the same - * way as bandit, but cannot be probed for devices. It therefore - * has its own config access functions. - * - * The "UniNorth" version is present in all Core99 machines - * (iBook, G4, new IMacs, and all the recent Apple machines). - * It contains 3 controllers in one ASIC. - */ - -#define MACRISC_CFA0(devfn, off) \ - ((1 << (unsigned long)PCI_SLOT(dev_fn)) \ - | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \ - | (((unsigned long)(off)) & 0xFCUL)) - -#define MACRISC_CFA1(bus, devfn, off) \ - ((((unsigned long)(bus)) << 16) \ - |(((unsigned long)(devfn)) << 8) \ - |(((unsigned long)(off)) & 0xFCUL) \ - |1UL) - -static unsigned int __pmac -macrisc_cfg_access(struct pci_controller* hose, u8 bus, u8 dev_fn, u8 offset) -{ - unsigned int caddr; - - if (bus == hose->first_busno) { - if (dev_fn < (11 << 3)) - return 0; - caddr = MACRISC_CFA0(dev_fn, offset); - } else - caddr = MACRISC_CFA1(bus, dev_fn, offset); - - /* Uninorth will return garbage if we don't read back the value ! */ - do { - out_le32(hose->cfg_addr, caddr); - } while(in_le32(hose->cfg_addr) != caddr); - - offset &= has_uninorth ? 0x07 : 0x03; - return (unsigned int)(hose->cfg_data) + (unsigned int)offset; -} - -#define cfg_read(val, addr, type, op, op2) \ - *val = op((type)(addr)) -#define cfg_write(val, addr, type, op, op2) \ - op((type *)(addr), (val)); (void) op2((type *)(addr)) - -#define cfg_read_bad(val, size) *val = bad_##size; -#define cfg_write_bad(val, size) - -#define bad_byte 0xff -#define bad_word 0xffff -#define bad_dword 0xffffffffU - -#define MACRISC_PCI_OP(rw, size, type, op, op2) \ -static int __pmac \ -macrisc_##rw##_config_##size(struct pci_dev *dev, int off, type val) \ -{ \ - struct pci_controller *hose = dev->sysdata; \ - unsigned int addr; \ - \ - addr = macrisc_cfg_access(hose, dev->bus->number, dev->devfn, off); \ - if (!addr) { \ - cfg_##rw##_bad(val, size) \ - return PCIBIOS_DEVICE_NOT_FOUND; \ - } \ - cfg_##rw(val, addr, type, op, op2); \ - return PCIBIOS_SUCCESSFUL; \ -} - -MACRISC_PCI_OP(read, byte, u8 *, in_8, x) -MACRISC_PCI_OP(read, word, u16 *, in_le16, x) -MACRISC_PCI_OP(read, dword, u32 *, in_le32, x) -MACRISC_PCI_OP(write, byte, u8, out_8, in_8) -MACRISC_PCI_OP(write, word, u16, out_le16, in_le16) -MACRISC_PCI_OP(write, dword, u32, out_le32, in_le32) - -static struct pci_ops macrisc_pci_ops = -{ - macrisc_read_config_byte, - macrisc_read_config_word, - macrisc_read_config_dword, - macrisc_write_config_byte, - macrisc_write_config_word, - macrisc_write_config_dword -}; - -/* - * Verifiy that a specific (bus, dev_fn) exists on chaos - */ -static int __pmac -chaos_validate_dev(struct pci_dev *dev, int offset) -{ - if(pci_device_to_OF_node(dev) == 0) - return PCIBIOS_DEVICE_NOT_FOUND; - if((dev->vendor == 0x106b) && (dev->device == 3) && (offset >= 0x10) && - (offset != 0x14) && (offset != 0x18) && (offset <= 0x24)) { - return PCIBIOS_BAD_REGISTER_NUMBER; - } - return PCIBIOS_SUCCESSFUL; -} - -#define CHAOS_PCI_OP(rw, size, type) \ -static int __pmac \ -chaos_##rw##_config_##size(struct pci_dev *dev, int off, type val) \ -{ \ - int result = chaos_validate_dev(dev, off); \ - if(result == PCIBIOS_BAD_REGISTER_NUMBER) { \ - cfg_##rw##_bad(val, size) \ - return PCIBIOS_BAD_REGISTER_NUMBER; \ - } \ - if(result == PCIBIOS_SUCCESSFUL) \ - return macrisc_##rw##_config_##size(dev, off, val); \ - return result; \ -} - -CHAOS_PCI_OP(read, byte, u8 *) -CHAOS_PCI_OP(read, word, u16 *) -CHAOS_PCI_OP(read, dword, u32 *) -CHAOS_PCI_OP(write, byte, u8) -CHAOS_PCI_OP(write, word, u16) -CHAOS_PCI_OP(write, dword, u32) - -static struct pci_ops chaos_pci_ops = -{ - chaos_read_config_byte, - chaos_read_config_word, - chaos_read_config_dword, - chaos_write_config_byte, - chaos_write_config_word, - chaos_write_config_dword -}; - - -/* - * For a bandit bridge, turn on cache coherency if necessary. - * N.B. we could clean this up using the hose ops directly. - */ -static void __init -init_bandit(struct pci_controller *bp) -{ - unsigned int vendev, magic; - int rev; - - /* read the word at offset 0 in config space for device 11 */ - out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID); - udelay(2); - vendev = in_le32((volatile unsigned int *)bp->cfg_data); - if (vendev == (PCI_DEVICE_ID_APPLE_BANDIT << 16) + - PCI_VENDOR_ID_APPLE) { - /* read the revision id */ - out_le32(bp->cfg_addr, - (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID); - udelay(2); - rev = in_8(bp->cfg_data); - if (rev != BANDIT_REVID) - printk(KERN_WARNING - "Unknown revision %d for bandit at %08lx\n", - rev, bp->io_base_phys); - } else if (vendev != (BANDIT_DEVID_2 << 16) + PCI_VENDOR_ID_APPLE) { - printk(KERN_WARNING "bandit isn't? (%x)\n", vendev); - return; - } - - /* read the revision id */ - out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID); - udelay(2); - rev = in_8(bp->cfg_data); - if (rev != BANDIT_REVID) - printk(KERN_WARNING "Unknown revision %d for bandit at %08lx\n", - rev, bp->io_base_phys); - - /* read the word at offset 0x50 */ - out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC); - udelay(2); - magic = in_le32((volatile unsigned int *)bp->cfg_data); - if ((magic & BANDIT_COHERENT) != 0) - return; - magic |= BANDIT_COHERENT; - udelay(2); - out_le32((volatile unsigned int *)bp->cfg_data, magic); - printk(KERN_INFO "Cache coherency enabled for bandit/PSX at %08lx\n", - bp->io_base_phys); -} - - -/* - * Tweak the PCI-PCI bridge chip on the blue & white G3s. - */ -static void __init -init_p2pbridge(void) -{ - struct device_node *p2pbridge; - struct pci_controller* hose; - u8 bus, devfn; - u16 val; - - /* XXX it would be better here to identify the specific - PCI-PCI bridge chip we have. */ - if ((p2pbridge = find_devices("pci-bridge")) == 0 - || p2pbridge->parent == NULL - || strcmp(p2pbridge->parent->name, "pci") != 0) - return; - if (pci_device_from_OF_node(p2pbridge, &bus, &devfn) < 0) { -#ifdef DEBUG - printk("Can't find PCI infos for PCI<->PCI bridge\n"); -#endif - return; - } - /* Warning: At this point, we have not yet renumbered all busses. - * So we must use OF walking to find out hose - */ - hose = pci_find_hose_for_OF_device(p2pbridge); - if (!hose) { -#ifdef DEBUG - printk("Can't find hose for PCI<->PCI bridge\n"); -#endif - return; - } - if (early_read_config_word(hose, bus, devfn, - PCI_BRIDGE_CONTROL, &val) < 0) { - printk(KERN_ERR "init_p2pbridge: couldn't read bridge control\n"); - return; - } - val &= ~PCI_BRIDGE_CTL_MASTER_ABORT; - early_write_config_word(hose, bus, devfn, PCI_BRIDGE_CONTROL, val); -} - -void __init -pmac_find_bridges(void) -{ - add_bridges(find_devices("bandit")); - add_bridges(find_devices("chaos")); - add_bridges(find_devices("pci")); - init_p2pbridge(); -} - -#define GRACKLE_CFA(b, d, o) (0x80 | ((b) << 8) | ((d) << 16) \ - | (((o) & ~3) << 24)) - -#define GRACKLE_PICR1_STG 0x00000040 -#define GRACKLE_PICR1_LOOPSNOOP 0x00000010 - -/* N.B. this is called before bridges is initialized, so we can't - use grackle_pcibios_{read,write}_config_dword. */ -static inline void grackle_set_stg(struct pci_controller* bp, int enable) -{ - unsigned int val; - - out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); - val = in_le32((volatile unsigned int *)bp->cfg_data); - val = enable? (val | GRACKLE_PICR1_STG) : - (val & ~GRACKLE_PICR1_STG); - out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); - out_le32((volatile unsigned int *)bp->cfg_data, val); - (void)in_le32((volatile unsigned int *)bp->cfg_data); -} - -static inline void grackle_set_loop_snoop(struct pci_controller *bp, int enable) -{ - unsigned int val; - - out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); - val = in_le32((volatile unsigned int *)bp->cfg_data); - val = enable? (val | GRACKLE_PICR1_LOOPSNOOP) : - (val & ~GRACKLE_PICR1_LOOPSNOOP); - out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); - out_le32((volatile unsigned int *)bp->cfg_data, val); - (void)in_le32((volatile unsigned int *)bp->cfg_data); -} - -static int __init -setup_uninorth(struct pci_controller* hose, struct reg_property* addr) -{ - pci_assign_all_busses = 1; - has_uninorth = 1; - hose->ops = ¯isc_pci_ops; - hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000); - hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000); - /* We "know" that the bridge at f2000000 has the PCI slots. */ - return addr->address == 0xf2000000; -} - -static void __init -setup_bandit(struct pci_controller* hose, struct reg_property* addr) -{ - hose->ops = ¯isc_pci_ops; - hose->cfg_addr = (volatile unsigned int *) - ioremap(addr->address + 0x800000, 0x1000); - hose->cfg_data = (volatile unsigned char *) - ioremap(addr->address + 0xc00000, 0x1000); - init_bandit(hose); -} - -static void __init -setup_chaos(struct pci_controller* hose, struct reg_property* addr) -{ - /* assume a `chaos' bridge */ - hose->ops = &chaos_pci_ops; - hose->cfg_addr = (volatile unsigned int *) - ioremap(addr->address + 0x800000, 0x1000); - hose->cfg_data = (volatile unsigned char *) - ioremap(addr->address + 0xc00000, 0x1000); -} - -void __init -setup_grackle(struct pci_controller *hose) -{ - setup_indirect_pci(hose, 0xfec00000, 0xfee00000); - if (machine_is_compatible("AAPL,PowerBook1998")) - grackle_set_loop_snoop(hose, 1); -#if 0 /* Disabled for now, HW problems ??? */ - grackle_set_stg(hose, 1); -#endif -} - -/* - * We assume that if we have a G3 powermac, we have one bridge called - * "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise, - * if we have one or more bandit or chaos bridges, we don't have a MPC106. - */ -static void __init -add_bridges(struct device_node *dev) -{ - int len; - struct pci_controller *hose; - struct reg_property *addr; - char* disp_name; - int *bus_range; - int first = 1, primary; - - for (; dev != NULL; dev = dev->next) { - addr = (struct reg_property *) get_property(dev, "reg", &len); - if (addr == NULL || len < sizeof(*addr)) { - printk(KERN_WARNING "Can't use %s: no address\n", - dev->full_name); - continue; - } - bus_range = (int *) get_property(dev, "bus-range", &len); - if (bus_range == NULL || len < 2 * sizeof(int)) { - printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n", - dev->full_name); - } - - hose = pcibios_alloc_controller(); - if (!hose) - continue; - hose->arch_data = dev; - hose->first_busno = bus_range ? bus_range[0] : 0; - hose->last_busno = bus_range ? bus_range[1] : 0xff; - - disp_name = NULL; - primary = first; - if (device_is_compatible(dev, "uni-north")) { - primary = setup_uninorth(hose, addr); - disp_name = "UniNorth"; - } else if (strcmp(dev->name, "pci") == 0) { - /* XXX assume this is a mpc106 (grackle) */ - setup_grackle(hose); - disp_name = "Grackle (MPC106)"; - } else if (strcmp(dev->name, "bandit") == 0) { - setup_bandit(hose, addr); - disp_name = "Bandit"; - } else if (strcmp(dev->name, "chaos") == 0) { - setup_chaos(hose, addr); - disp_name = "Chaos"; - primary = 0; - } - printk(KERN_INFO "Found %s PCI host bridge at 0x%08x. Firmware bus number: %d->%d\n", - disp_name, addr->address, hose->first_busno, hose->last_busno); -#ifdef DEBUG - printk(" ->Hose at 0x%08lx, cfg_addr=0x%08lx,cfg_data=0x%08lx\n", - hose, hose->cfg_addr, hose->cfg_data); -#endif - - /* Interpret the "ranges" property */ - /* This also maps the I/O region and sets isa_io/mem_base */ - pci_process_bridge_OF_ranges(hose, dev, primary); - - /* Fixup "bus-range" OF property */ - fixup_bus_range(dev); - - first &= !primary; - } -} - -static void __init -pcibios_fixup_OF_interrupts(void) -{ - struct pci_dev* dev; - - pci_for_each_dev(dev) - { - /* - * Open Firmware often doesn't initialize the, - * PCI_INTERRUPT_LINE config register properly, so we - * should find the device node and se if it has an - * AAPL,interrupts property. - */ - unsigned char pin; - struct device_node* node; - - if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || !pin) - continue; /* No interrupt generated -> no fixup */ - node = pci_device_to_OF_node(dev); - if (!node) { - printk("No OF node for device %x:%x\n", dev->bus->number, dev->devfn >> 3); - continue; - } - /* this is the node, see if it has interrupts */ - if (node->n_intrs > 0) - dev->irq = node->intrs[0].line; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - } -} - -void __init -pmac_pcibios_fixup(void) -{ - /* Fixup interrupts according to OF tree */ - pcibios_fixup_OF_interrupts(); -} - -int __pmac -pmac_pci_enable_device_hook(struct pci_dev *dev, int initial) -{ - struct device_node* node; - int updatecfg = 0; - - node = pci_device_to_OF_node(dev); - - /* We don't want to enable USB controllers absent from the OF tree - * (iBook second controller) - */ - if (dev->vendor == PCI_VENDOR_ID_APPLE - && dev->device == PCI_DEVICE_ID_APPLE_KL_USB && !node) - return -EINVAL; - - /* Firewire & GMAC were disabled after PCI probe, the driver is - * claiming them, we must re-enable them now. - */ - if (node && !strcmp(node->name, "firewire") && - (device_is_compatible(node, "pci106b,18") || - device_is_compatible(node, "pci106b,30"))) { - feature_set_firewire_cable_power(node, 1); - feature_set_firewire_power(node, 1); - updatecfg = 1; - } - if (node && !strcmp(node->name, "ethernet") && - device_is_compatible(node, "gmac")) { - feature_set_gmac_power(node, 1); - updatecfg = 1; - } - - if (updatecfg) { - u16 cmd; - - /* - * Make sure PCI is correctly configured - * - * We use old pci_bios versions of the function since, by - * default, gmac is not powered up, and so will be absent - * from the kernel initial PCI lookup. - * - * Should be replaced by 2.4 new PCI mecanisms and really - * regiser the device. - */ - pci_read_config_word(dev, PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; - pci_write_config_word(dev, PCI_COMMAND, cmd); - pci_write_config_byte(dev, PCI_LATENCY_TIMER, 16); - pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8); - } - - return 0; -} - -/* We power down some devices after they have been probed. They'll - * be powered back on later on - */ -void __init -pmac_pcibios_after_init(void) -{ - struct device_node* nd; - -#ifdef CONFIG_BLK_DEV_IDE - struct pci_dev *dev; - - /* OF fails to initialize IDE controllers on macs - * (and maybe other machines) - * - * Ideally, this should be moved to the IDE layer, but we need - * to check specifically with Andre Hedrick how to do it cleanly - * since the common IDE code seem to care about the fact that the - * BIOS may have disabled a controller. - * - * -- BenH - */ - pci_for_each_dev(dev) { - if ((dev->class >> 16) == PCI_BASE_CLASS_STORAGE) - pci_enable_device(dev); - } -#endif /* CONFIG_BLK_DEV_IDE */ - - nd = find_devices("firewire"); - while (nd) { - if (nd->parent && (device_is_compatible(nd, "pci106b,18") || - device_is_compatible(nd, "pci106b,30")) - && device_is_compatible(nd->parent, "uni-north")) { - feature_set_firewire_power(nd, 0); - feature_set_firewire_cable_power(nd, 0); - } - nd = nd->next; - } - nd = find_devices("ethernet"); - while (nd) { - if (nd->parent && device_is_compatible(nd, "gmac") - && device_is_compatible(nd->parent, "uni-north")) - feature_set_gmac_power(nd, 0); - nd = nd->next; - } -} - diff -Nru a/arch/ppc/kernel/pmac_pic.c b/arch/ppc/kernel/pmac_pic.c --- a/arch/ppc/kernel/pmac_pic.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,496 +0,0 @@ -/* - * BK Id: SCCS/s.pmac_pic.c 1.20 09/08/01 15:47:42 paulus - */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "pmac_pic.h" -#include "open_pic.h" - -/* pmac */struct pmac_irq_hw { - unsigned int flag; - unsigned int enable; - unsigned int ack; - unsigned int level; -}; - -/* XXX these addresses should be obtained from the device tree */ -static volatile struct pmac_irq_hw *pmac_irq_hw[4] = { - (struct pmac_irq_hw *) 0xf3000020, - (struct pmac_irq_hw *) 0xf3000010, - (struct pmac_irq_hw *) 0xf4000020, - (struct pmac_irq_hw *) 0xf4000010, -}; - -static int max_irqs; -static int max_real_irqs; - -static spinlock_t pmac_pic_lock = SPIN_LOCK_UNLOCKED; - - -#define GATWICK_IRQ_POOL_SIZE 10 -static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE]; - -/* - * Mark an irq as "lost". This is only used on the pmac - * since it can lose interrupts (see pmac_set_irq_mask). - * -- Cort - */ -void __pmac __set_lost(unsigned long irq_nr) -{ - if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) { - atomic_inc(&ppc_n_lost_interrupts); - set_dec(1); - } -} - -static void __pmac pmac_mask_and_ack_irq(unsigned int irq_nr) -{ - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; - unsigned long flags; - - if ((unsigned)irq_nr >= max_irqs) - return; - - clear_bit(irq_nr, ppc_cached_irq_mask); - if (test_and_clear_bit(irq_nr, ppc_lost_interrupts)) - atomic_dec(&ppc_n_lost_interrupts); - spin_lock_irqsave(&pmac_pic_lock, flags); - out_le32(&pmac_irq_hw[i]->ack, bit); - out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); - out_le32(&pmac_irq_hw[i]->ack, bit); - do { - /* make sure ack gets to controller before we enable - interrupts */ - mb(); - } while(in_le32(&pmac_irq_hw[i]->flag) & bit); - spin_unlock_irqrestore(&pmac_pic_lock, flags); -} - -static void __pmac pmac_set_irq_mask(unsigned int irq_nr) -{ - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; - unsigned long flags; - - if ((unsigned)irq_nr >= max_irqs) - return; - - spin_lock_irqsave(&pmac_pic_lock, flags); - /* enable unmasked interrupts */ - out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); - - do { - /* make sure mask gets to controller before we - return to user */ - mb(); - } while((in_le32(&pmac_irq_hw[i]->enable) & bit) - != (ppc_cached_irq_mask[i] & bit)); - - /* - * Unfortunately, setting the bit in the enable register - * when the device interrupt is already on *doesn't* set - * the bit in the flag register or request another interrupt. - */ - if ((bit & ppc_cached_irq_mask[i]) - && (ld_le32(&pmac_irq_hw[i]->level) & bit) - && !(ld_le32(&pmac_irq_hw[i]->flag) & bit)) - __set_lost((ulong)irq_nr); - spin_unlock_irqrestore(&pmac_pic_lock, flags); -} - -static void __pmac pmac_mask_irq(unsigned int irq_nr) -{ - clear_bit(irq_nr, ppc_cached_irq_mask); - pmac_set_irq_mask(irq_nr); - mb(); -} - -static void __pmac pmac_unmask_irq(unsigned int irq_nr) -{ - set_bit(irq_nr, ppc_cached_irq_mask); - pmac_set_irq_mask(irq_nr); -} - -static void __pmac pmac_end_irq(unsigned int irq_nr) -{ - if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { - set_bit(irq_nr, ppc_cached_irq_mask); - pmac_set_irq_mask(irq_nr); - } -} - - -struct hw_interrupt_type pmac_pic = { - " PMAC-PIC ", - NULL, - NULL, - pmac_unmask_irq, - pmac_mask_irq, - pmac_mask_and_ack_irq, - pmac_end_irq, - NULL -}; - -struct hw_interrupt_type gatwick_pic = { - " GATWICK ", - NULL, - NULL, - pmac_unmask_irq, - pmac_mask_irq, - pmac_mask_and_ack_irq, - pmac_end_irq, - NULL -}; - -static void gatwick_action(int cpl, void *dev_id, struct pt_regs *regs) -{ - int irq, bits; - - for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | ppc_lost_interrupts[i]; - if (bits == 0) - continue; - irq += __ilog2(bits); - break; - } - /* The previous version of this code allowed for this case, we - * don't. Put this here to check for it. - * -- Cort - */ - if ( irq_desc[irq].handler != &gatwick_pic ) - printk("gatwick irq not from gatwick pic\n"); - else - ppc_irq_dispatch_handler( regs, irq ); -} - -int -pmac_get_irq(struct pt_regs *regs) -{ - int irq; - unsigned long bits = 0; - -#ifdef CONFIG_SMP - void psurge_smp_message_recv(struct pt_regs *); - - /* IPI's are a hack on the powersurge -- Cort */ - if ( smp_processor_id() != 0 ) { - psurge_smp_message_recv(regs); - return -2; /* ignore, already handled */ - } -#endif /* CONFIG_SMP */ - for (irq = max_real_irqs; (irq -= 32) >= 0; ) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | ppc_lost_interrupts[i]; - if (bits == 0) - continue; - irq += __ilog2(bits); - break; - } - - return irq; -} - -/* This routine will fix some missing interrupt values in the device tree - * on the gatwick mac-io controller used by some PowerBooks - */ -static void __init -pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) -{ - struct device_node *node; - int count; - - memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool)); - node = gw->child; - count = 0; - while(node) - { - /* Fix SCC */ - if (strcasecmp(node->name, "escc") == 0) - if (node->child) { - if (node->child->n_intrs < 3) { - node->child->intrs = &gatwick_int_pool[count]; - count += 3; - } - node->child->n_intrs = 3; - node->child->intrs[0].line = 15+irq_base; - node->child->intrs[1].line = 4+irq_base; - node->child->intrs[2].line = 5+irq_base; - printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n", - node->child->intrs[0].line, - node->child->intrs[1].line, - node->child->intrs[2].line); - } - /* Fix media-bay & left SWIM */ - if (strcasecmp(node->name, "media-bay") == 0) { - struct device_node* ya_node; - - if (node->n_intrs == 0) - node->intrs = &gatwick_int_pool[count++]; - node->n_intrs = 1; - node->intrs[0].line = 29+irq_base; - printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n", - node->intrs[0].line); - - ya_node = node->child; - while(ya_node) - { - if (strcasecmp(ya_node->name, "floppy") == 0) { - if (ya_node->n_intrs < 2) { - ya_node->intrs = &gatwick_int_pool[count]; - count += 2; - } - ya_node->n_intrs = 2; - ya_node->intrs[0].line = 19+irq_base; - ya_node->intrs[1].line = 1+irq_base; - printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n", - ya_node->intrs[0].line, ya_node->intrs[1].line); - } - if (strcasecmp(ya_node->name, "ata4") == 0) { - if (ya_node->n_intrs < 2) { - ya_node->intrs = &gatwick_int_pool[count]; - count += 2; - } - ya_node->n_intrs = 2; - ya_node->intrs[0].line = 14+irq_base; - ya_node->intrs[1].line = 3+irq_base; - printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n", - ya_node->intrs[0].line, ya_node->intrs[1].line); - } - ya_node = ya_node->sibling; - } - } - node = node->sibling; - } - if (count > 10) { - printk("WARNING !! Gatwick interrupt pool overflow\n"); - printk(" GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE); - printk(" requested = %d\n", count); - } -} - -/* - * The PowerBook 3400/2400/3500 can have a combo ethernet/modem - * card which includes an ohare chip that acts as a second interrupt - * controller. If we find this second ohare, set it up and fix the - * interrupt value in the device tree for the ethernet chip. - */ -static int __init enable_second_ohare(void) -{ - unsigned char bus, devfn; - unsigned short cmd; - unsigned long addr; - struct device_node *irqctrler = find_devices("pci106b,7"); - struct device_node *ether; - - if (irqctrler == NULL || irqctrler->n_addrs <= 0) - return -1; - addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40); - pmac_irq_hw[1] = (volatile struct pmac_irq_hw *)(addr + 0x20); - max_irqs = 64; - if (pci_device_from_OF_node(irqctrler, &bus, &devfn) == 0) { - struct pci_controller* hose = pci_find_hose_for_OF_device(irqctrler); - if (!hose) - printk(KERN_ERR "Can't find PCI hose for OHare2 !\n"); - else { - early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; - cmd &= ~PCI_COMMAND_IO; - early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd); - } - } - - /* Fix interrupt for the modem/ethernet combo controller. The number - in the device tree (27) is bogus (correct for the ethernet-only - board but not the combo ethernet/modem board). - The real interrupt is 28 on the second controller -> 28+32 = 60. - */ - ether = find_devices("pci1011,14"); - if (ether && ether->n_intrs > 0) { - ether->intrs[0].line = 60; - printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n", - ether->intrs[0].line); - } - - /* Return the interrupt number of the cascade */ - return irqctrler->intrs[0].line; -} - -void __init -pmac_pic_init(void) -{ - int i; - struct device_node *irqctrler; - unsigned long addr; - int irq_cascade = -1; - - /* We first try to detect Apple's new Core99 chipset, since mac-io - * is quite different on those machines and contains an IBM MPIC2. - */ - irqctrler = find_type_devices("open-pic"); - if (irqctrler != NULL) - { - printk("PowerMac using OpenPIC irq controller\n"); - if (irqctrler->n_addrs > 0) - { - int nmi_irq = -1; - unsigned char senses[NR_IRQS]; -#ifdef CONFIG_XMON - struct device_node* pswitch; - - pswitch = find_devices("programmer-switch"); - if (pswitch && pswitch->n_intrs) - nmi_irq = pswitch->intrs[0].line; -#endif /* CONFIG_XMON */ - prom_get_irq_senses(senses, 0, NR_IRQS); - OpenPIC_InitSenses = senses; - OpenPIC_NumInitSenses = NR_IRQS; - ppc_md.get_irq = openpic_get_irq; - OpenPIC_Addr = ioremap(irqctrler->addrs[0].address, - irqctrler->addrs[0].size); - openpic_init(1, 0, 0, nmi_irq); -#ifdef CONFIG_XMON - if (nmi_irq >= 0) - request_irq(nmi_irq, xmon_irq, 0, - "NMI - XMON", 0); -#endif /* CONFIG_XMON */ - return; - } - irqctrler = NULL; - } - - /* - * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts, - * 1998 G3 Series PowerBooks have 128, - * other powermacs have 32. - * The combo ethernet/modem card for the Powerstar powerbooks - * (2400/3400/3500, ohare based) has a second ohare chip - * effectively making a total of 64. - */ - max_irqs = max_real_irqs = 32; - irqctrler = find_devices("mac-io"); - if (irqctrler) - { - max_real_irqs = 64; - if (irqctrler->next) - max_irqs = 128; - else - max_irqs = 64; - } - for ( i = 0; i < max_real_irqs ; i++ ) - irq_desc[i].handler = &pmac_pic; - - /* get addresses of first controller */ - if (irqctrler) { - if (irqctrler->n_addrs > 0) { - addr = (unsigned long) - ioremap(irqctrler->addrs[0].address, 0x40); - for (i = 0; i < 2; ++i) - pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) - (addr + (2 - i) * 0x10); - } - - /* get addresses of second controller */ - irqctrler = irqctrler->next; - if (irqctrler && irqctrler->n_addrs > 0) { - addr = (unsigned long) - ioremap(irqctrler->addrs[0].address, 0x40); - for (i = 2; i < 4; ++i) - pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) - (addr + (4 - i) * 0x10); - irq_cascade = irqctrler->intrs[0].line; - if (device_is_compatible(irqctrler, "gatwick")) - pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs); - } - } else { - /* older powermacs have a GC (grand central) or ohare at - f3000000, with interrupt control registers at f3000020. */ - addr = (unsigned long) ioremap(0xf3000000, 0x40); - pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20); - } - - /* PowerBooks 3400 and 3500 can have a second controller in a second - ohare chip, on the combo ethernet/modem card */ - if (machine_is_compatible("AAPL,3400/2400") - || machine_is_compatible("AAPL,3500")) - irq_cascade = enable_second_ohare(); - - /* disable all interrupts in all controllers */ - for (i = 0; i * 32 < max_irqs; ++i) - out_le32(&pmac_irq_hw[i]->enable, 0); - - /* get interrupt line of secondary interrupt controller */ - if (irq_cascade >= 0) { - printk(KERN_INFO "irq: secondary controller on irq %d\n", - (int)irq_cascade); - for ( i = max_real_irqs ; i < max_irqs ; i++ ) - irq_desc[i].handler = &gatwick_pic; - request_irq( irq_cascade, gatwick_action, SA_INTERRUPT, - "cascade", 0 ); - } - printk("System has %d possible interrupts\n", max_irqs); - if (max_irqs != max_real_irqs) - printk(KERN_DEBUG "%d interrupts on main controller\n", - max_real_irqs); - -#ifdef CONFIG_XMON - request_irq(20, xmon_irq, 0, "NMI - XMON", 0); -#endif /* CONFIG_XMON */ -} - -#ifdef CONFIG_PMAC_PBOOK -/* - * These procedures are used in implementing sleep on the powerbooks. - * sleep_save_intrs() saves the states of all interrupt enables - * and disables all interrupts except for the nominated one. - * sleep_restore_intrs() restores the states of all interrupt enables. - */ -unsigned int sleep_save_mask[2]; - -void __pmac -pmac_sleep_save_intrs(int viaint) -{ - sleep_save_mask[0] = ppc_cached_irq_mask[0]; - sleep_save_mask[1] = ppc_cached_irq_mask[1]; - ppc_cached_irq_mask[0] = 0; - ppc_cached_irq_mask[1] = 0; - if (viaint > 0) - set_bit(viaint, ppc_cached_irq_mask); - out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]); - if (max_real_irqs > 32) - out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]); - (void)in_le32(&pmac_irq_hw[0]->flag); - /* make sure mask gets to controller before we return to caller */ - mb(); - (void)in_le32(&pmac_irq_hw[0]->enable); -} - -void __pmac -pmac_sleep_restore_intrs(void) -{ - int i; - - out_le32(&pmac_irq_hw[0]->enable, 0); - if (max_real_irqs > 32) - out_le32(&pmac_irq_hw[1]->enable, 0); - mb(); - for (i = 0; i < max_real_irqs; ++i) - if (test_bit(i, sleep_save_mask)) - pmac_unmask_irq(i); -} -#endif /* CONFIG_PMAC_PBOOK */ diff -Nru a/arch/ppc/kernel/pmac_pic.h b/arch/ppc/kernel/pmac_pic.h --- a/arch/ppc/kernel/pmac_pic.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,14 +0,0 @@ -/* - * BK Id: SCCS/s.pmac_pic.h 1.9 08/19/01 22:23:04 paulus - */ -#ifndef _PPC_KERNEL_PMAC_PIC_H -#define _PPC_KERNEL_PMAC_PIC_H - -#include "local_irq.h" - -extern struct hw_interrupt_type pmac_pic; - -void pmac_pic_init(void); -int pmac_get_irq(struct pt_regs *regs); - -#endif /* _PPC_KERNEL_PMAC_PIC_H */ diff -Nru a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c --- a/arch/ppc/kernel/pmac_setup.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,838 +0,0 @@ -/* - * BK Id: SCCS/s.pmac_setup.c 1.43 11/13/01 21:26:07 paulus - */ -/* - * linux/arch/ppc/kernel/setup.c - * - * PowerPC version - * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) - * - * Adapted for Power Macintosh by Paul Mackerras - * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) - * - * Derived from "arch/alpha/kernel/setup.c" - * Copyright (C) 1995 Linus Torvalds - * - * 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. - * - */ - -/* - * bootup setup stuff.. - */ - -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "local_irq.h" -#include "pmac_pic.h" -#include "../mm/mem_pieces.h" - -#undef SHOW_GATWICK_IRQS - -extern long pmac_time_init(void); -extern unsigned long pmac_get_rtc_time(void); -extern int pmac_set_rtc_time(unsigned long nowtime); -extern void pmac_read_rtc_time(void); -extern void pmac_calibrate_decr(void); -extern void pmac_pcibios_fixup(void); -extern void pmac_find_bridges(void); - -extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode); -extern int mackbd_getkeycode(unsigned int scancode); -extern int mackbd_translate(unsigned char keycode, unsigned char *keycodep, - char raw_mode); -extern char mackbd_unexpected_up(unsigned char keycode); -extern void mackbd_leds(unsigned char leds); -extern void __init mackbd_init_hw(void); -extern int mac_hid_kbd_translate(unsigned char scancode, unsigned char *keycode, - char raw_mode); -extern char mac_hid_kbd_unexpected_up(unsigned char keycode); -extern void mac_hid_init_hw(void); -extern unsigned char mac_hid_kbd_sysrq_xlate[]; -extern unsigned char pckbd_sysrq_xlate[]; -extern unsigned char mackbd_sysrq_xlate[]; -extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); -extern int pckbd_getkeycode(unsigned int scancode); -extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, - char raw_mode); -extern char pckbd_unexpected_up(unsigned char keycode); -extern int keyboard_sends_linux_keycodes; -extern void pmac_nvram_update(void); - -extern int pmac_pci_enable_device_hook(struct pci_dev *dev, int initial); -extern void pmac_pcibios_after_init(void); - -struct device_node *memory_node; - -unsigned char drive_info; - -int ppc_override_l2cr = 0; -int ppc_override_l2cr_value; -int has_l2cache = 0; - -static int current_root_goodness = -1; - -extern char saved_command_line[]; - -extern int pmac_newworld; - -#define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ - -extern void zs_kgdb_hook(int tty_num); -static void ohare_init(void); -#ifdef CONFIG_BOOTX_TEXT -void pmac_progress(char *s, unsigned short hex); -#endif - -sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN; - -#ifdef CONFIG_SMP -extern struct smp_ops_t psurge_smp_ops; -extern struct smp_ops_t core99_smp_ops; - -volatile static long int core99_l2_cache; -void __init -core99_init_l2(void) -{ - int cpu = smp_processor_id(); - - if (!(cur_cpu_spec[0]->cpu_features & CPU_FTR_L2CR)) - return; - - if (cpu == 0){ - core99_l2_cache = _get_L2CR(); - printk("CPU0: L2CR is %lx\n", core99_l2_cache); - } else { - printk("CPU%d: L2CR was %lx\n", cpu, _get_L2CR()); - _set_L2CR(0); - _set_L2CR(core99_l2_cache); - printk("CPU%d: L2CR set to %lx\n", cpu, core99_l2_cache); - } -} -#endif /* CONFIG_SMP */ - -/* - * Assume here that all clock rates are the same in a - * smp system. -- Cort - */ -int __openfirmware -of_show_percpuinfo(struct seq_file *m, int i) -{ - struct device_node *cpu_node; - int *fp, s; - - cpu_node = find_type_devices("cpu"); - if (!cpu_node) - return 0; - for (s = 0; s < i && cpu_node->next; s++) - cpu_node = cpu_node->next; - fp = (int *) get_property(cpu_node, "clock-frequency", NULL); - if (fp) - seq_printf(m, "clock\t\t: %dMHz\n", *fp / 1000000); - return 0; -} - -int __pmac -pmac_show_cpuinfo(struct seq_file *m) -{ - struct device_node *np; - char *pp; - int plen; - - /* find motherboard type */ - seq_printf(m, "machine\t\t: "); - np = find_devices("device-tree"); - if (np != NULL) { - pp = (char *) get_property(np, "model", NULL); - if (pp != NULL) - seq_printf(m, "%s\n", pp); - else - seq_printf(m, "PowerMac\n"); - pp = (char *) get_property(np, "compatible", &plen); - if (pp != NULL) { - seq_printf(m, "motherboard\t:"); - while (plen > 0) { - int l = strlen(pp) + 1; - seq_printf(m, " %s", pp); - plen -= l; - pp += l; - } - seq_printf(m, "\n"); - } - } else - seq_printf(m, "PowerMac\n"); - - /* find l2 cache info */ - np = find_devices("l2-cache"); - if (np == 0) - np = find_type_devices("cache"); - if (np != 0) { - unsigned int *ic = (unsigned int *) - get_property(np, "i-cache-size", NULL); - unsigned int *dc = (unsigned int *) - get_property(np, "d-cache-size", NULL); - seq_printf(m, "L2 cache\t:"); - has_l2cache = 1; - if (get_property(np, "cache-unified", NULL) != 0 && dc) { - seq_printf(m, " %dK unified", *dc / 1024); - } else { - if (ic) - seq_printf(m, " %dK instruction", *ic / 1024); - if (dc) - seq_printf(m, "%s %dK data", - (ic? " +": ""), *dc / 1024); - } - pp = get_property(np, "ram-type", NULL); - if (pp) - seq_printf(m, " %s", pp); - seq_printf(m, "\n"); - } - - /* find ram info */ - np = find_devices("memory"); - if (np != 0) { - int n; - struct reg_property *reg = (struct reg_property *) - get_property(np, "reg", &n); - - if (reg != 0) { - unsigned long total = 0; - - for (n /= sizeof(struct reg_property); n > 0; --n) - total += (reg++)->size; - seq_printf(m, "memory\t\t: %luMB\n", total >> 20); - } - } - - /* Checks "l2cr-value" property in the registry */ - np = find_devices("cpus"); - if (np == 0) - np = find_type_devices("cpu"); - if (np != 0) { - unsigned int *l2cr = (unsigned int *) - get_property(np, "l2cr-value", NULL); - if (l2cr != 0) { - seq_printf(m, "l2cr override\t: 0x%x\n", *l2cr); - } - } - - /* Indicate newworld/oldworld */ - seq_printf(m, "pmac-generation\t: %s\n", - pmac_newworld ? "NewWorld" : "OldWorld"); - - - return 0; -} - -#ifdef CONFIG_SCSI -/* Find the device number for the disk (if any) at target tgt - on host adaptor host. We just need to get the prototype from - sd.h */ -#include -#include "../../../drivers/scsi/scsi.h" -#include "../../../drivers/scsi/sd.h" - -#endif - -#ifdef CONFIG_VT -/* - * Dummy mksound function that does nothing. - * The real one is in the dmasound driver. - */ -static void __pmac -pmac_mksound(unsigned int hz, unsigned int ticks) -{ -} -#endif /* CONFIG_VT */ - -static volatile u32 *sysctrl_regs; - -void __init -pmac_setup_arch(void) -{ - struct device_node *cpu; - int *fp; - unsigned long pvr; - - pvr = PVR_VER(mfspr(PVR)); - - /* Set loops_per_jiffy to a half-way reasonable value, - for use until calibrate_delay gets called. */ - cpu = find_type_devices("cpu"); - if (cpu != 0) { - fp = (int *) get_property(cpu, "clock-frequency", NULL); - if (fp != 0) { - if (pvr == 4 || pvr >= 8) - /* 604, G3, G4 etc. */ - loops_per_jiffy = *fp / HZ; - else - /* 601, 603, etc. */ - loops_per_jiffy = *fp / (2*HZ); - } else - loops_per_jiffy = 50000000 / HZ; - } - - /* this area has the CPU identification register - and some registers used by smp boards */ - sysctrl_regs = (volatile u32 *) ioremap(0xf8000000, 0x1000); - ohare_init(); - - /* Lookup PCI hosts */ - pmac_find_bridges(); - - /* Checks "l2cr-value" property in the registry */ - if (cur_cpu_spec[0]->cpu_features & CPU_FTR_L2CR) { - struct device_node *np = find_devices("cpus"); - if (np == 0) - np = find_type_devices("cpu"); - if (np != 0) { - unsigned int *l2cr = (unsigned int *) - get_property(np, "l2cr-value", NULL); - if (l2cr != 0) { - ppc_override_l2cr = 1; - ppc_override_l2cr_value = *l2cr; - _set_L2CR(0); - _set_L2CR(ppc_override_l2cr_value); - } - } - } - - if (ppc_override_l2cr) - printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n", - ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000) - ? "enabled" : "disabled"); - -#ifdef CONFIG_SMP - /* somewhat of a hack */ - core99_init_l2(); -#endif - -#ifdef CONFIG_KGDB - zs_kgdb_hook(0); -#endif - -#ifdef CONFIG_ADB_CUDA - find_via_cuda(); -#else - if (find_devices("via-cuda")) { - printk("WARNING ! Your machine is Cuda based but your kernel\n"); - printk(" wasn't compiled with CONFIG_ADB_CUDA option !\n"); - } -#endif -#ifdef CONFIG_ADB_PMU - find_via_pmu(); -#else - if (find_devices("via-pmu")) { - printk("WARNING ! Your machine is PMU based but your kernel\n"); - printk(" wasn't compiled with CONFIG_ADB_PMU option !\n"); - } -#endif -#ifdef CONFIG_NVRAM - pmac_nvram_init(); -#endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif -#ifdef CONFIG_VT - kd_mksound = pmac_mksound; -#endif -#ifdef CONFIG_BLK_DEV_INITRD - if (initrd_start) - ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); - else -#endif - ROOT_DEV = to_kdev_t(DEFAULT_ROOT_DEVICE); - -#ifdef CONFIG_SMP - /* Check for Core99 */ - if (find_devices("uni-n")) - ppc_md.smp_ops = &core99_smp_ops; - else - ppc_md.smp_ops = &psurge_smp_ops; -#endif /* CONFIG_SMP */ -} - -static void __init ohare_init(void) -{ - /* - * Turn on the L2 cache. - * We assume that we have a PSX memory controller iff - * we have an ohare I/O controller. - */ - if (find_devices("ohare") != NULL) { - if (((sysctrl_regs[2] >> 24) & 0xf) >= 3) { - if (sysctrl_regs[4] & 0x10) - sysctrl_regs[4] |= 0x04000020; - else - sysctrl_regs[4] |= 0x04000000; - if(has_l2cache) - printk(KERN_INFO "Level 2 cache enabled\n"); - } - } -} - -extern char *bootpath; -extern char *bootdevice; -void *boot_host; -int boot_target; -int boot_part; -extern kdev_t boot_dev; - -void __init -pmac_init2(void) -{ -#ifdef CONFIG_ADB_PMU - via_pmu_start(); -#endif -#ifdef CONFIG_ADB_CUDA - via_cuda_start(); -#endif -#ifdef CONFIG_PMAC_PBOOK - media_bay_init(); -#endif -} - -#ifdef CONFIG_SCSI -void __init -note_scsi_host(struct device_node *node, void *host) -{ - int l; - char *p; - - l = strlen(node->full_name); - if (bootpath != NULL && bootdevice != NULL - && strncmp(node->full_name, bootdevice, l) == 0 - && (bootdevice[l] == '/' || bootdevice[l] == 0)) { - boot_host = host; - /* - * There's a bug in OF 1.0.5. (Why am I not surprised.) - * If you pass a path like scsi/sd@1:0 to canon, it returns - * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0 - * That is, the scsi target number doesn't get preserved. - * So we pick the target number out of bootpath and use that. - */ - p = strstr(bootpath, "/sd@"); - if (p != NULL) { - p += 4; - boot_target = simple_strtoul(p, NULL, 10); - p = strchr(p, ':'); - if (p != NULL) - boot_part = simple_strtoul(p + 1, NULL, 10); - } - } -} -#endif - -#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) -kdev_t __init -find_ide_boot(void) -{ - char *p; - int n; - kdev_t __init pmac_find_ide_boot(char *bootdevice, int n); - - if (bootdevice == NULL) - return 0; - p = strrchr(bootdevice, '/'); - if (p == NULL) - return 0; - n = p - bootdevice; - - return pmac_find_ide_boot(bootdevice, n); -} -#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */ - -void __init -find_boot_device(void) -{ -#if defined(CONFIG_SCSI) && defined(CONFIG_BLK_DEV_SD) - if (boot_host != NULL) { - boot_dev = sd_find_target(boot_host, boot_target); - if (boot_dev != 0) - return; - } -#endif -#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) - boot_dev = find_ide_boot(); -#endif -} - -/* can't be __init - can be called whenever a disk is first accessed */ -void __pmac -note_bootable_part(kdev_t dev, int part, int goodness) -{ - static int found_boot = 0; - char *p; - - /* Do nothing if the root has been mounted already. */ - if (init_task.fs->rootmnt != NULL) - return; - if ((goodness <= current_root_goodness) && - (ROOT_DEV != to_kdev_t(DEFAULT_ROOT_DEVICE))) - return; - p = strstr(saved_command_line, "root="); - if (p != NULL && (p == saved_command_line || p[-1] == ' ')) - return; - - if (!found_boot) { - find_boot_device(); - found_boot = 1; - } - if (boot_dev == 0 || dev == boot_dev) { - ROOT_DEV = MKDEV(MAJOR(dev), MINOR(dev) + part); - boot_dev = NODEV; - current_root_goodness = goodness; - } -} - -void __pmac -pmac_restart(char *cmd) -{ -#ifdef CONFIG_ADB_CUDA - struct adb_request req; -#endif /* CONFIG_ADB_CUDA */ - -#ifdef CONFIG_NVRAM - pmac_nvram_update(); -#endif - - switch (sys_ctrler) { -#ifdef CONFIG_ADB_CUDA - case SYS_CTRLER_CUDA: - cuda_request(&req, NULL, 2, CUDA_PACKET, - CUDA_RESET_SYSTEM); - for (;;) - cuda_poll(); - break; -#endif /* CONFIG_ADB_CUDA */ -#ifdef CONFIG_ADB_PMU - case SYS_CTRLER_PMU: - pmu_restart(); - break; -#endif /* CONFIG_ADB_PMU */ - default: ; - } -} - -void __pmac -pmac_power_off(void) -{ -#ifdef CONFIG_ADB_CUDA - struct adb_request req; -#endif /* CONFIG_ADB_CUDA */ - -#ifdef CONFIG_NVRAM - pmac_nvram_update(); -#endif - - switch (sys_ctrler) { -#ifdef CONFIG_ADB_CUDA - case SYS_CTRLER_CUDA: - cuda_request(&req, NULL, 2, CUDA_PACKET, - CUDA_POWERDOWN); - for (;;) - cuda_poll(); - break; -#endif /* CONFIG_ADB_CUDA */ -#ifdef CONFIG_ADB_PMU - case SYS_CTRLER_PMU: - pmu_shutdown(); - break; -#endif /* CONFIG_ADB_PMU */ - default: ; - } -} - -void __pmac -pmac_halt(void) -{ - pmac_power_off(); -} - - -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) -/* - * IDE stuff. - */ -static int __pmac -pmac_ide_check_region(ide_ioreg_t from, unsigned int extent) -{ - /* - * We only do the check_region if `from' looks like a genuine - * I/O port number. If it actually refers to a memory-mapped - * register, it should be OK. - */ - if (from < ~_IO_BASE) - return check_region(from, extent); - return 0; -} - -static void __pmac -pmac_ide_request_region(ide_ioreg_t from, - unsigned int extent, - const char *name) -{ - if (from < ~_IO_BASE) - request_region(from, extent, name); -} - -static void __pmac -pmac_ide_release_region(ide_ioreg_t from, - unsigned int extent) -{ - if (from < ~_IO_BASE) - release_region(from, extent); -} - -/* - * This is only used if we have a PCI IDE controller, not - * for the IDE controller in the ohare/paddington/heathrow/keylargo. - */ -static void __pmac -pmac_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, - ide_ioreg_t ctrl_port, int *irq) -{ - ide_ioreg_t reg = data_port; - int i; - - for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { - hw->io_ports[i] = reg; - reg += 1; - } - hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; -} -#endif - -/* - * Read in a property describing some pieces of memory. - */ - -static void __init -get_mem_prop(char *name, struct mem_pieces *mp) -{ - struct reg_property *rp; - int i, s; - unsigned int *ip; - int nac = prom_n_addr_cells(memory_node); - int nsc = prom_n_size_cells(memory_node); - - ip = (unsigned int *) get_property(memory_node, name, &s); - if (ip == NULL) { - printk(KERN_ERR "error: couldn't get %s property on /memory\n", - name); - abort(); - } - s /= (nsc + nac) * 4; - rp = mp->regions; - for (i = 0; i < s; ++i, ip += nac+nsc) { - if (nac >= 2 && ip[nac-2] != 0) - continue; - rp->address = ip[nac-1]; - if (nsc >= 2 && ip[nac+nsc-2] != 0) - rp->size = ~0U; - else - rp->size = ip[nac+nsc-1]; - ++rp; - } - mp->n_regions = rp - mp->regions; - - /* Make sure the pieces are sorted. */ - mem_pieces_sort(mp); - mem_pieces_coalesce(mp); -} - -/* - * On systems with Open Firmware, collect information about - * physical RAM and which pieces are already in use. - * At this point, we have (at least) the first 8MB mapped with a BAT. - * Our text, data, bss use something over 1MB, starting at 0. - * Open Firmware may be using 1MB at the 4MB point. - */ -unsigned long __init -pmac_find_end_of_memory(void) -{ - unsigned long a, total; - struct mem_pieces phys_mem; - - memory_node = find_devices("memory"); - if (memory_node == NULL) { - printk(KERN_ERR "can't find memory node\n"); - abort(); - } - - /* - * Find out where physical memory is, and check that it - * starts at 0 and is contiguous. It seems that RAM is - * always physically contiguous on Power Macintoshes. - * - * Supporting discontiguous physical memory isn't hard, - * it just makes the virtual <-> physical mapping functions - * more complicated (or else you end up wasting space - * in mem_map). - */ - get_mem_prop("reg", &phys_mem); - if (phys_mem.n_regions == 0) - panic("No RAM??"); - a = phys_mem.regions[0].address; - if (a != 0) - panic("RAM doesn't start at physical address 0"); - total = phys_mem.regions[0].size; - - if (phys_mem.n_regions > 1) { - printk("RAM starting at 0x%x is not contiguous\n", - phys_mem.regions[1].address); - printk("Using RAM from 0 to 0x%lx\n", total-1); - } - - return total; -} - -void __init -select_adb_keyboard(void) -{ -#ifdef CONFIG_VT -#ifdef CONFIG_INPUT - ppc_md.kbd_init_hw = mac_hid_init_hw; - ppc_md.kbd_translate = mac_hid_kbd_translate; - ppc_md.kbd_unexpected_up = mac_hid_kbd_unexpected_up; - ppc_md.kbd_setkeycode = 0; - ppc_md.kbd_getkeycode = 0; - ppc_md.kbd_leds = 0; -#ifdef CONFIG_MAGIC_SYSRQ -#ifdef CONFIG_MAC_ADBKEYCODES - if (!keyboard_sends_linux_keycodes) { - ppc_md.ppc_kbd_sysrq_xlate = mac_hid_kbd_sysrq_xlate; - SYSRQ_KEY = 0x69; - } else -#endif /* CONFIG_MAC_ADBKEYCODES */ - { - ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; - SYSRQ_KEY = 0x54; - } -#endif /* CONFIG_MAGIC_SYSRQ */ -#elif defined(CONFIG_ADB_KEYBOARD) - ppc_md.kbd_setkeycode = mackbd_setkeycode; - ppc_md.kbd_getkeycode = mackbd_getkeycode; - ppc_md.kbd_translate = mackbd_translate; - ppc_md.kbd_unexpected_up = mackbd_unexpected_up; - ppc_md.kbd_leds = mackbd_leds; - ppc_md.kbd_init_hw = mackbd_init_hw; -#ifdef CONFIG_MAGIC_SYSRQ - ppc_md.ppc_kbd_sysrq_xlate = mackbd_sysrq_xlate; - SYSRQ_KEY = 0x69; -#endif /* CONFIG_MAGIC_SYSRQ */ -#endif /* CONFIG_INPUT_ADBHID/CONFIG_ADB_KEYBOARD */ -#endif /* CONFIG_VT */ -} - -void __init -pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - /* isa_io_base gets set in pmac_find_bridges */ - isa_mem_base = PMAC_ISA_MEM_BASE; - pci_dram_offset = PMAC_PCI_DRAM_OFFSET; - ISA_DMA_THRESHOLD = ~0L; - DMA_MODE_READ = 1; - DMA_MODE_WRITE = 2; - - ppc_md.setup_arch = pmac_setup_arch; - ppc_md.show_cpuinfo = pmac_show_cpuinfo; - ppc_md.show_percpuinfo = of_show_percpuinfo; - ppc_md.irq_cannonicalize = NULL; - ppc_md.init_IRQ = pmac_pic_init; - ppc_md.get_irq = pmac_get_irq; /* Changed later on ... */ - ppc_md.init = pmac_init2; - - ppc_md.pcibios_fixup = pmac_pcibios_fixup; - ppc_md.pcibios_enable_device_hook = pmac_pci_enable_device_hook; - ppc_md.pcibios_after_init = pmac_pcibios_after_init; - - ppc_md.restart = pmac_restart; - ppc_md.power_off = pmac_power_off; - ppc_md.halt = pmac_halt; - - ppc_md.time_init = pmac_time_init; - ppc_md.set_rtc_time = pmac_set_rtc_time; - ppc_md.get_rtc_time = pmac_get_rtc_time; - ppc_md.calibrate_decr = pmac_calibrate_decr; - - ppc_md.find_end_of_memory = pmac_find_end_of_memory; - - select_adb_keyboard(); - -#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) - ppc_ide_md.ide_check_region = pmac_ide_check_region; - ppc_ide_md.ide_request_region = pmac_ide_request_region; - ppc_ide_md.ide_release_region = pmac_ide_release_region; - ppc_ide_md.ide_init_hwif = pmac_ide_init_hwif_ports; -#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */ - -#ifdef CONFIG_BOOTX_TEXT - ppc_md.progress = pmac_progress; -#endif /* CONFIG_BOOTX_TEXT */ - - if (ppc_md.progress) ppc_md.progress("pmac_init(): exit", 0); - -} - -#ifdef CONFIG_BOOTX_TEXT -extern void drawchar(char c); -extern void drawstring(const char *c); -extern boot_infos_t *disp_bi; -void __init -pmac_progress(char *s, unsigned short hex) -{ - if (disp_bi == 0) - return; - btext_drawstring(s); - btext_drawchar('\n'); -} -#endif /* CONFIG_BOOTX_TEXT */ diff -Nru a/arch/ppc/kernel/pmac_smp.c b/arch/ppc/kernel/pmac_smp.c --- a/arch/ppc/kernel/pmac_smp.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,489 +0,0 @@ -/* - * BK Id: %F% %I% %G% %U% %#% - */ -/* - * SMP support for power macintosh. - * - * We support both the old "powersurge" SMP architecture - * and the current Core99 (G4 PowerMac) machines. - * - * Support Macintosh G4 SMP by Troy Benjegerdes (hozer@drgw.net) - * and Ben Herrenschmidt . - * - * Support for DayStar quad CPU cards - * Copyright (C) XLR8, Inc. 1994-2000 - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#define __KERNEL_SYSCALLS__ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "open_pic.h" - -/* - * Powersurge (old powermac SMP) support. - */ - -extern void __secondary_start_psurge(void); -extern void __secondary_start_psurge2(void); /* Temporary horrible hack */ -extern void __secondary_start_psurge3(void); /* Temporary horrible hack */ - -/* Addresses for powersurge registers */ -#define HAMMERHEAD_BASE 0xf8000000 -#define HHEAD_CONFIG 0x90 -#define HHEAD_SEC_INTR 0xc0 - -/* register for interrupting the primary processor on the powersurge */ -/* N.B. this is actually the ethernet ROM! */ -#define PSURGE_PRI_INTR 0xf3019000 - -/* register for storing the start address for the secondary processor */ -/* N.B. this is the PCI config space address register for the 1st bridge */ -#define PSURGE_START 0xf2800000 - -/* Daystar/XLR8 4-CPU card */ -#define PSURGE_QUAD_REG_ADDR 0xf8800000 - -#define PSURGE_QUAD_IRQ_SET 0 -#define PSURGE_QUAD_IRQ_CLR 1 -#define PSURGE_QUAD_IRQ_PRIMARY 2 -#define PSURGE_QUAD_CKSTOP_CTL 3 -#define PSURGE_QUAD_PRIMARY_ARB 4 -#define PSURGE_QUAD_BOARD_ID 6 -#define PSURGE_QUAD_WHICH_CPU 7 -#define PSURGE_QUAD_CKSTOP_RDBK 8 -#define PSURGE_QUAD_RESET_CTL 11 - -#define PSURGE_QUAD_OUT(r, v) (out_8(quad_base + ((r) << 4) + 4, (v))) -#define PSURGE_QUAD_IN(r) (in_8(quad_base + ((r) << 4) + 4) & 0x0f) -#define PSURGE_QUAD_BIS(r, v) (PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) | (v))) -#define PSURGE_QUAD_BIC(r, v) (PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) & ~(v))) - -/* virtual addresses for the above */ -static volatile u8 *hhead_base; -static volatile u8 *quad_base; -static volatile u32 *psurge_pri_intr; -static volatile u8 *psurge_sec_intr; -static volatile u32 *psurge_start; - -/* what sort of powersurge board we have */ -static int psurge_type; - -/* values for psurge_type */ -#define PSURGE_DUAL 0 -#define PSURGE_QUAD_OKEE 1 -#define PSURGE_QUAD_COTTON 2 -#define PSURGE_QUAD_ICEGRASS 3 - -/* l2 cache stuff for dual G4 macs */ -extern void core99_init_l2(void); - -/* - * Set and clear IPIs for powersurge. - */ -static inline void psurge_set_ipi(int cpu) -{ - if (cpu == 0) - in_be32(psurge_pri_intr); - else if (psurge_type == PSURGE_DUAL) - out_8(psurge_sec_intr, 0); - else - PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_SET, 1 << cpu); -} - -static inline void psurge_clr_ipi(int cpu) -{ - if (cpu > 0) { - if (psurge_type == PSURGE_DUAL) - out_8(psurge_sec_intr, ~0); - else - PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, 1 << cpu); - } -} - -/* - * On powersurge (old SMP powermac architecture) we don't have - * separate IPIs for separate messages like openpic does. Instead - * we have a bitmap for each processor, where a 1 bit means that - * the corresponding message is pending for that processor. - * Ideally each cpu's entry would be in a different cache line. - * -- paulus. - */ -static unsigned long psurge_smp_message[NR_CPUS]; - -void __pmac -psurge_smp_message_recv(struct pt_regs *regs) -{ - int cpu = smp_processor_id(); - int msg; - - /* clear interrupt */ - psurge_clr_ipi(cpu); - - if (smp_num_cpus < 2) - return; - - /* make sure there is a message there */ - for (msg = 0; msg < 4; msg++) - if (test_and_clear_bit(msg, &psurge_smp_message[cpu])) - smp_message_recv(msg, regs); -} - -void __pmac -psurge_primary_intr(int irq, void *d, struct pt_regs *regs) -{ - psurge_smp_message_recv(regs); -} - -static void __pmac -smp_psurge_message_pass(int target, int msg, unsigned long data, int wait) -{ - int i; - - if (smp_num_cpus < 2) - return; - - for (i = 0; i < smp_num_cpus; i++) { - if (target == MSG_ALL - || (target == MSG_ALL_BUT_SELF && i != smp_processor_id()) - || target == i) { - set_bit(msg, &psurge_smp_message[i]); - psurge_set_ipi(i); - } - } -} - -/* - * Determine a quad card presence. We read the board ID register, we - * force the data bus to change to something else, and we read it again. - * It it's stable, then the register probably exist (ugh !) - */ -static int __init psurge_quad_probe(void) -{ - int type; - unsigned int i; - - type = PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID); - if (type < PSURGE_QUAD_OKEE || type > PSURGE_QUAD_ICEGRASS - || type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID)) - return PSURGE_DUAL; - - /* looks OK, try a slightly more rigorous test */ - /* bogus is not necessarily cacheline-aligned, - though I don't suppose that really matters. -- paulus */ - for (i = 0; i < 100; i++) { - volatile u32 bogus[8]; - bogus[(0+i)%8] = 0x00000000; - bogus[(1+i)%8] = 0x55555555; - bogus[(2+i)%8] = 0xFFFFFFFF; - bogus[(3+i)%8] = 0xAAAAAAAA; - bogus[(4+i)%8] = 0x33333333; - bogus[(5+i)%8] = 0xCCCCCCCC; - bogus[(6+i)%8] = 0xCCCCCCCC; - bogus[(7+i)%8] = 0x33333333; - wmb(); - asm volatile("dcbf 0,%0" : : "r" (bogus) : "memory"); - mb(); - if (type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID)) - return PSURGE_DUAL; - } - return type; -} - -static void __init psurge_quad_init(void) -{ - int procbits; - - if (ppc_md.progress) ppc_md.progress("psurge_quad_init", 0x351); - procbits = ~PSURGE_QUAD_IN(PSURGE_QUAD_WHICH_CPU); - if (psurge_type == PSURGE_QUAD_ICEGRASS) - PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits); - else - PSURGE_QUAD_BIC(PSURGE_QUAD_CKSTOP_CTL, procbits); - mdelay(33); - out_8(psurge_sec_intr, ~0); - PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, procbits); - PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits); - if (psurge_type != PSURGE_QUAD_ICEGRASS) - PSURGE_QUAD_BIS(PSURGE_QUAD_CKSTOP_CTL, procbits); - PSURGE_QUAD_BIC(PSURGE_QUAD_PRIMARY_ARB, procbits); - mdelay(33); - PSURGE_QUAD_BIC(PSURGE_QUAD_RESET_CTL, procbits); - mdelay(33); - PSURGE_QUAD_BIS(PSURGE_QUAD_PRIMARY_ARB, procbits); - mdelay(33); -} - -static int __init smp_psurge_probe(void) -{ - int i, ncpus; - - /* We don't do SMP on the PPC601 -- paulus */ - if (PVR_VER(mfspr(PVR)) == 1) - return 1; - - /* - * The powersurge cpu board can be used in the generation - * of powermacs that have a socket for an upgradeable cpu card, - * including the 7500, 8500, 9500, 9600. - * The device tree doesn't tell you if you have 2 cpus because - * OF doesn't know anything about the 2nd processor. - * Instead we look for magic bits in magic registers, - * in the hammerhead memory controller in the case of the - * dual-cpu powersurge board. -- paulus. - */ - if (find_devices("hammerhead") == NULL) - return 1; - - hhead_base = ioremap(HAMMERHEAD_BASE, 0x800); - quad_base = ioremap(PSURGE_QUAD_REG_ADDR, 1024); - psurge_sec_intr = hhead_base + HHEAD_SEC_INTR; - - psurge_type = psurge_quad_probe(); - if (psurge_type != PSURGE_DUAL) { - psurge_quad_init(); - /* All released cards using this HW design have 4 CPUs */ - ncpus = 4; - } else { - iounmap((void *) quad_base); - if ((in_8(hhead_base + HHEAD_CONFIG) & 0x02) == 0) { - /* not a dual-cpu card */ - iounmap((void *) hhead_base); - return 1; - } - ncpus = 2; - } - - psurge_start = ioremap(PSURGE_START, 4); - psurge_pri_intr = ioremap(PSURGE_PRI_INTR, 4); - - /* this is not actually strictly necessary -- paulus. */ - for (i = 1; i < ncpus; ++i) - smp_hw_index[i] = i; - - if (ppc_md.progress) ppc_md.progress("smp_psurge_probe - done", 0x352); - - return ncpus; -} - -static void __init smp_psurge_kick_cpu(int nr) -{ - void (*start)(void) = __secondary_start_psurge; - unsigned long a; - - /* may need to flush here if secondary bats aren't setup */ - for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32) - asm volatile("dcbf 0,%0" : : "r" (a) : "memory"); - asm volatile("sync"); - - if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu", 0x353); - - /* setup entry point of secondary processor */ - switch (nr) { - case 2: - start = __secondary_start_psurge2; - break; - case 3: - start = __secondary_start_psurge3; - break; - } - - out_be32(psurge_start, __pa(start)); - mb(); - - psurge_set_ipi(nr); - udelay(10); - psurge_clr_ipi(nr); - - if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu - done", 0x354); -} - -/* - * With the dual-cpu powersurge board, the decrementers and timebases - * of both cpus are frozen after the secondary cpu is started up, - * until we give the secondary cpu another interrupt. This routine - * uses this to get the timebases synchronized. - * -- paulus. - */ -static void __init psurge_dual_sync_tb(int cpu_nr) -{ - static volatile int sec_tb_reset = 0; - int t; - - set_dec(tb_ticks_per_jiffy); - set_tb(0, 0); - last_jiffy_stamp(cpu_nr) = 0; - - if (cpu_nr > 0) { - mb(); - sec_tb_reset = 1; - return; - } - - /* wait for the secondary to have reset its TB before proceeding */ - for (t = 10000000; t > 0 && !sec_tb_reset; --t) - ; - - /* now interrupt the secondary, starting both TBs */ - psurge_set_ipi(1); - - smp_tb_synchronized = 1; -} - -static void __init -smp_psurge_setup_cpu(int cpu_nr) -{ - - if (cpu_nr == 0) { - if (smp_num_cpus < 2) - return; - /* reset the entry point so if we get another intr we won't - * try to startup again */ - out_be32(psurge_start, 0x100); - if (request_irq(30, psurge_primary_intr, 0, "primary IPI", 0)) - printk(KERN_ERR "Couldn't get primary IPI interrupt"); - } - - if (psurge_type == PSURGE_DUAL) - psurge_dual_sync_tb(cpu_nr); -} - -static int __init -smp_core99_probe(void) -{ - struct device_node *cpus; - int i, ncpus = 1; - - if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345); - cpus = find_type_devices("cpu"); - if (cpus) - while ((cpus = cpus->next) != NULL) - ++ncpus; - printk("smp_core99_probe: found %d cpus\n", ncpus); - if (ncpus > 1) { - openpic_request_IPIs(); - for (i = 1; i < ncpus; ++i) - smp_hw_index[i] = i; - } - - return ncpus; -} - -static void __init -smp_core99_kick_cpu(int nr) -{ - unsigned long save_vector, new_vector; - unsigned long flags; -#if 1 /* New way... */ - volatile unsigned long *vector - = ((volatile unsigned long *)(KERNELBASE+0x100)); - if (nr < 1 || nr > 3) - return; -#else - volatile unsigned long *vector - = ((volatile unsigned long *)(KERNELBASE+0x500)); - if (nr != 1) - return; -#endif - if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu", 0x346); - - local_irq_save(flags); - local_irq_disable(); - - /* Save reset vector */ - save_vector = *vector; - - /* Setup fake reset vector that does - * b __secondary_start_psurge - KERNELBASE - */ - switch(nr) { - case 1: - new_vector = (unsigned long)__secondary_start_psurge; - break; - case 2: - new_vector = (unsigned long)__secondary_start_psurge2; - break; - case 3: - new_vector = (unsigned long)__secondary_start_psurge3; - break; - } - *vector = 0x48000002 + new_vector - KERNELBASE; - - /* flush data cache and inval instruction cache */ - flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); - - /* Put some life in our friend */ - feature_core99_kick_cpu(nr); - - /* FIXME: We wait a bit for the CPU to take the exception, I should - * instead wait for the entry code to set something for me. Well, - * ideally, all that crap will be done in prom.c and the CPU left - * in a RAM-based wait loop like CHRP. - */ - mdelay(1); - - /* Restore our exception vector */ - *vector = save_vector; - flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); - - local_irq_restore(flags); - if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347); -} - -static void __init -smp_core99_setup_cpu(int cpu_nr) -{ - /* Setup openpic */ - do_openpic_setup_cpu(); - - /* Setup L2 */ - if (cpu_nr != 0) - core99_init_l2(); - else - if (ppc_md.progress) ppc_md.progress("core99_setup_cpu 0 done", 0x349); -} - -/* PowerSurge-style Macs */ -struct smp_ops_t psurge_smp_ops __pmacdata = { - smp_psurge_message_pass, - smp_psurge_probe, - smp_psurge_kick_cpu, - smp_psurge_setup_cpu, -}; - -/* Core99 Macs (dual G4s) */ -struct smp_ops_t core99_smp_ops __pmacdata = { - smp_openpic_message_pass, - smp_core99_probe, - smp_core99_kick_cpu, - smp_core99_setup_cpu, -}; diff -Nru a/arch/ppc/kernel/pmac_time.c b/arch/ppc/kernel/pmac_time.c --- a/arch/ppc/kernel/pmac_time.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,286 +0,0 @@ -/* - * BK Id: SCCS/s.pmac_time.c 1.16 09/08/01 15:47:42 paulus - */ -/* - * Support for periodic interrupts (100 per second) and for getting - * the current time from the RTC on Power Macintoshes. - * - * We use the decrementer register for our periodic interrupts. - * - * Paul Mackerras August 1996. - * Copyright (C) 1996 Paul Mackerras. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern rwlock_t xtime_lock; - -/* Apparently the RTC stores seconds since 1 Jan 1904 */ -#define RTC_OFFSET 2082844800 - -/* - * Calibrate the decrementer frequency with the VIA timer 1. - */ -#define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */ - -/* VIA registers */ -#define RS 0x200 /* skip between registers */ -#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ -#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ -#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ -#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ -#define ACR (11*RS) /* Auxiliary control register */ -#define IFR (13*RS) /* Interrupt flag register */ - -/* Bits in ACR */ -#define T1MODE 0xc0 /* Timer 1 mode */ -#define T1MODE_CONT 0x40 /* continuous interrupts */ - -/* Bits in IFR and IER */ -#define T1_INT 0x40 /* Timer 1 interrupt */ - -extern struct timezone sys_tz; - -long __init -pmac_time_init(void) -{ -#ifdef CONFIG_NVRAM - s32 delta = 0; - int dst; - - delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16; - delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8; - delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb); - if (delta & 0x00800000UL) - delta |= 0xFF000000UL; - dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0); - printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60, - dst ? "on" : "off"); - return delta; -#else - return 0; -#endif -} - -unsigned long __pmac -pmac_get_rtc_time(void) -{ -#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) - struct adb_request req; - unsigned long now; -#endif - - /* Get the time from the RTC */ - switch (sys_ctrler) { -#ifdef CONFIG_ADB_CUDA - case SYS_CTRLER_CUDA: - if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0) - return 0; - while (!req.complete) - cuda_poll(); - if (req.reply_len != 7) - printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", - req.reply_len); - now = (req.reply[3] << 24) + (req.reply[4] << 16) - + (req.reply[5] << 8) + req.reply[6]; - return now - RTC_OFFSET; -#endif /* CONFIG_ADB_CUDA */ -#ifdef CONFIG_ADB_PMU - case SYS_CTRLER_PMU: - if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) - return 0; - while (!req.complete) - pmu_poll(); - if (req.reply_len != 5) - printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", - req.reply_len); - now = (req.reply[1] << 24) + (req.reply[2] << 16) - + (req.reply[3] << 8) + req.reply[4]; - return now - RTC_OFFSET; -#endif /* CONFIG_ADB_PMU */ - default: ; - } - return 0; -} - -int __pmac -pmac_set_rtc_time(unsigned long nowtime) -{ -#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) - struct adb_request req; -#endif - - nowtime += RTC_OFFSET; - - switch (sys_ctrler) { -#ifdef CONFIG_ADB_CUDA - case SYS_CTRLER_CUDA: - if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME, - nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0) - return 0; - while (!req.complete) - cuda_poll(); - if ((req.reply_len != 3) && (req.reply_len != 7)) - printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n", - req.reply_len); - return 1; -#endif /* CONFIG_ADB_CUDA */ -#ifdef CONFIG_ADB_PMU - case SYS_CTRLER_PMU: - if (pmu_request(&req, NULL, 5, PMU_SET_RTC, - nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0) - return 0; - while (!req.complete) - pmu_poll(); - if (req.reply_len != 0) - printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n", - req.reply_len); - return 1; -#endif /* CONFIG_ADB_PMU */ - default: - return 0; - } -} - -/* - * Calibrate the decrementer register using VIA timer 1. - * This is used both on powermacs and CHRP machines. - */ -int __init -via_calibrate_decr(void) -{ - struct device_node *vias; - volatile unsigned char *via; - int count = VIA_TIMER_FREQ_6 / HZ; - unsigned int dstart, dend; - - vias = find_devices("via-cuda"); - if (vias == 0) - vias = find_devices("via-pmu"); - if (vias == 0) - vias = find_devices("via"); - if (vias == 0 || vias->n_addrs == 0) - return 0; - via = (volatile unsigned char *) - ioremap(vias->addrs[0].address, vias->addrs[0].size); - - /* set timer 1 for continuous interrupts */ - out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT); - /* set the counter to a small value */ - out_8(&via[T1CH], 2); - /* set the latch to `count' */ - out_8(&via[T1LL], count); - out_8(&via[T1LH], count >> 8); - /* wait until it hits 0 */ - while ((in_8(&via[IFR]) & T1_INT) == 0) - ; - dstart = get_dec(); - /* clear the interrupt & wait until it hits 0 again */ - in_8(&via[T1CL]); - while ((in_8(&via[IFR]) & T1_INT) == 0) - ; - dend = get_dec(); - - tb_ticks_per_jiffy = (dstart - dend) / 6; - tb_to_us = mulhwu_scale_factor(dstart - dend, 60000); - - printk(KERN_INFO "via_calibrate_decr: ticks per jiffy = %u (%u ticks)\n", - tb_ticks_per_jiffy, dstart - dend); - - return 1; -} - -#ifdef CONFIG_PMAC_PBOOK -/* - * Reset the time after a sleep. - */ -static int __pmac -time_sleep_notify(struct pmu_sleep_notifier *self, int when) -{ - static unsigned long time_diff; - unsigned long flags; - - switch (when) { - case PBOOK_SLEEP_NOW: - read_lock_irqsave(&xtime_lock, flags); - time_diff = xtime.tv_sec - pmac_get_rtc_time(); - read_unlock_irqrestore(&xtime_lock, flags); - break; - case PBOOK_WAKE: - write_lock_irqsave(&xtime_lock, flags); - xtime.tv_sec = pmac_get_rtc_time() + time_diff; - set_dec(tb_ticks_per_jiffy); - /* No currently-supported powerbook has a 601, - so use get_tbl, not native */ - last_jiffy_stamp(0) = tb_last_stamp = get_tbl(); - xtime.tv_usec = 0; - last_rtc_update = xtime.tv_sec; - write_unlock_irqrestore(&xtime_lock, flags); - break; - } - return PBOOK_SLEEP_OK; -} - -static struct pmu_sleep_notifier time_sleep_notifier __pmacdata = { - time_sleep_notify, SLEEP_LEVEL_MISC, -}; -#endif /* CONFIG_PMAC_PBOOK */ - -/* - * Query the OF and get the decr frequency. - * This was taken from the pmac time_init() when merging the prep/pmac - * time functions. - */ -void __init -pmac_calibrate_decr(void) -{ - struct device_node *cpu; - unsigned int freq, *fp; - -#ifdef CONFIG_PMAC_PBOOK - pmu_register_sleep_notifier(&time_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ - - /* We assume MacRISC2 machines have correct device-tree - * calibration. That's better since the VIA itself seems - * to be slightly off. --BenH - */ - if (!machine_is_compatible("MacRISC2")) - if (via_calibrate_decr()) - return; - - /* - * The cpu node should have a timebase-frequency property - * to tell us the rate at which the decrementer counts. - */ - cpu = find_type_devices("cpu"); - if (cpu == 0) - panic("can't find cpu node in time_init"); - fp = (unsigned int *) get_property(cpu, "timebase-frequency", NULL); - if (fp == 0) - panic("can't get cpu timebase frequency"); - freq = *fp; - printk("time_init: decrementer frequency = %u.%.6u MHz\n", - freq/1000000, freq%1000000); - tb_ticks_per_jiffy = freq / HZ; - tb_to_us = mulhwu_scale_factor(freq, 1000000); -} diff -Nru a/arch/ppc/kernel/ppc-stub.c b/arch/ppc/kernel/ppc-stub.c --- a/arch/ppc/kernel/ppc-stub.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/ppc-stub.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ppc-stub.c 1.6 05/17/01 18:14:21 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * ppc-stub.c: KGDB support for the Linux kernel. @@ -61,7 +61,7 @@ * * The following gdb commands are supported: * - * command function Return value + * command function Return value * * g return the value of the CPU registers hex data or ENN * G set the value of the CPU registers OK or ENN @@ -131,12 +131,16 @@ static u_int fault_jmp_buf[100]; static int kdebug; + static const char hexchars[]="0123456789abcdef"; /* Place where we save old trap entries for restoration - sparc*/ /* struct tt_entry kgdb_savettable[256]; */ /* typedef void (*trapfunc_t)(void); */ +static void kgdb_fault_handler(struct pt_regs *regs); +static void handle_exception (struct pt_regs *regs); + #if 0 /* Install an exception handler for kgdb */ static void exceptionHandler(int tnum, unsigned int *tfunc) @@ -188,14 +192,45 @@ mem2hex(char *mem, char *buf, int count) { unsigned char ch; + unsigned short tmp_s; + unsigned long tmp_l; if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { debugger_fault_handler = kgdb_fault_handler; - while (count-- > 0) { - ch = *mem++; - *buf++ = hexchars[ch >> 4]; - *buf++ = hexchars[ch & 0xf]; + + /* Accessing 16 bit and 32 bit objects in a single + ** load instruction is required to avoid bad side + ** effects for some IO registers. + */ + + if ((count == 2) && (((long)mem & 1) == 0)) { + tmp_s = *(unsigned short *)mem; + mem += 2; + *buf++ = hexchars[(tmp_s >> 12) & 0xf]; + *buf++ = hexchars[(tmp_s >> 8) & 0xf]; + *buf++ = hexchars[(tmp_s >> 4) & 0xf]; + *buf++ = hexchars[tmp_s & 0xf]; + + } else if ((count == 4) && (((long)mem & 3) == 0)) { + tmp_l = *(unsigned int *)mem; + mem += 4; + *buf++ = hexchars[(tmp_l >> 28) & 0xf]; + *buf++ = hexchars[(tmp_l >> 24) & 0xf]; + *buf++ = hexchars[(tmp_l >> 20) & 0xf]; + *buf++ = hexchars[(tmp_l >> 16) & 0xf]; + *buf++ = hexchars[(tmp_l >> 12) & 0xf]; + *buf++ = hexchars[(tmp_l >> 8) & 0xf]; + *buf++ = hexchars[(tmp_l >> 4) & 0xf]; + *buf++ = hexchars[tmp_l & 0xf]; + + } else { + while (count-- > 0) { + ch = *mem++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } } + } else { /* error condition */ } @@ -210,17 +245,58 @@ static char * hex2mem(char *buf, char *mem, int count) { - int i; unsigned char ch; + int i; + char *orig_mem; + unsigned short tmp_s; + unsigned long tmp_l; + + orig_mem = mem; if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { debugger_fault_handler = kgdb_fault_handler; - for (i=0; i# */ +/* scan for the sequence $# */ static void getpacket(char *buffer) { @@ -316,14 +392,14 @@ } while (checksum != xmitcsum); } -/* send the packet in buffer. */ +/* send the packet in buffer. */ static void putpacket(unsigned char *buffer) { unsigned char checksum; int count; unsigned char ch, recv; - /* $#. */ + /* $#. */ do { putDebugChar('$'); checksum = 0; @@ -428,7 +504,7 @@ return 1; } -/* Convert the SPARC hardware trap type code to a unix signal number. */ +/* Convert the hardware trap type code to a unix signal number. */ /* * This table contains the mapping between PowerPC hardware trap types, and * signals, which are primarily what GDB understands. @@ -438,20 +514,47 @@ unsigned int tt; /* Trap type code for powerpc */ unsigned char signo; /* Signal that we map this trap into */ } hard_trap_info[] = { - { 0x200, SIGSEGV }, /* machine check */ - { 0x300, SIGSEGV }, /* address error (store) */ - { 0x400, SIGBUS }, /* instruction bus error */ - { 0x500, SIGINT }, /* interrupt */ - { 0x600, SIGBUS }, /* alingment */ - { 0x700, SIGTRAP }, /* breakpoint trap */ - { 0x800, SIGFPE }, /* fpu unavail */ - { 0x900, SIGALRM }, /* decrementer */ - { 0xa00, SIGILL }, /* reserved */ - { 0xb00, SIGILL }, /* reserved */ - { 0xc00, SIGCHLD }, /* syscall */ - { 0xd00, SIGTRAP }, /* single-step/watch */ - { 0xe00, SIGFPE }, /* fp assist */ +#if defined(CONFIG_4xx) + { 0x100, SIGINT }, /* critical input interrupt */ + { 0x200, SIGSEGV }, /* machine check */ + { 0x300, SIGSEGV }, /* data storage */ + { 0x400, SIGBUS }, /* instruction storage */ + { 0x500, SIGINT }, /* interrupt */ + { 0x600, SIGBUS }, /* alignment */ + { 0x700, SIGILL }, /* program */ + { 0x800, SIGILL }, /* reserved */ + { 0x900, SIGILL }, /* reserved */ + { 0xa00, SIGILL }, /* reserved */ + { 0xb00, SIGILL }, /* reserved */ + { 0xc00, SIGCHLD }, /* syscall */ + { 0xd00, SIGILL }, /* reserved */ + { 0xe00, SIGILL }, /* reserved */ + { 0xf00, SIGILL }, /* reserved */ + /* + ** 0x1000 PIT + ** 0x1010 FIT + ** 0x1020 watchdog + ** 0x1100 data TLB miss + ** 0x1200 instruction TLB miss + */ + { 0x2000, SIGTRAP}, /* debug */ +#else + { 0x200, SIGSEGV }, /* machine check */ + { 0x300, SIGSEGV }, /* address error (store) */ + { 0x400, SIGBUS }, /* instruction bus error */ + { 0x500, SIGINT }, /* interrupt */ + { 0x600, SIGBUS }, /* alingment */ + { 0x700, SIGTRAP }, /* breakpoint trap */ + { 0x800, SIGFPE }, /* fpu unavail */ + { 0x900, SIGALRM }, /* decrementer */ + { 0xa00, SIGILL }, /* reserved */ + { 0xb00, SIGILL }, /* reserved */ + { 0xc00, SIGCHLD }, /* syscall */ + { 0xd00, SIGTRAP }, /* single-step/watch */ + { 0xe00, SIGFPE }, /* fp assist */ +#endif { 0, 0} /* Must be last */ + }; static int computeSignal(unsigned int tt) @@ -462,7 +565,7 @@ if (ht->tt == tt) return ht->signo; - return SIGHUP; /* default for things we don't know about */ + return SIGHUP; /* default for things we don't know about */ } #define PC_REGNUM 64 @@ -493,7 +596,7 @@ #ifdef KGDB_DEBUG printk("kgdb: entering handle_exception; trap [0x%x]\n", - (unsigned int)regs->trap); + (unsigned int)regs->trap); #endif kgdb_interruptible(0); @@ -510,7 +613,7 @@ sigval = computeSignal(regs->trap); ptr = remcomOutBuffer; -#if 0 +#if defined(CONFIG_4xx) *ptr++ = 'S'; *ptr++ = hexchars[sigval >> 4]; *ptr++ = hexchars[sigval & 0xf]; @@ -533,6 +636,8 @@ *ptr++ = 0; putpacket(remcomOutBuffer); + if (kdebug) + printk("remcomOutBuffer: %s\n", remcomOutBuffer); /* XXX We may want to add some features dealing with poking the * XXX page tables, ... (look at sparc-stub.c for more info) @@ -544,7 +649,7 @@ getpacket(remcomInBuffer); switch (remcomInBuffer[0]) { - case '?': /* report most recent signal */ + case '?': /* report most recent signal */ remcomOutBuffer[0] = 'S'; remcomOutBuffer[1] = hexchars[sigval >> 4]; remcomOutBuffer[2] = hexchars[sigval & 0xf]; @@ -601,7 +706,7 @@ } break; - case 'G': /* set the value of the CPU registers */ + case 'G': /* set the value of the CPU registers */ { ptr = &remcomInBuffer[1]; @@ -639,15 +744,14 @@ ptr = &remcomInBuffer[1]; - if (hexToInt(&ptr, &addr) - && *ptr++ == ',' - && hexToInt(&ptr, &length)) { - if (mem2hex((char *)addr, remcomOutBuffer,length)) + if (hexToInt(&ptr, &addr) && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, remcomOutBuffer, + length)) break; - strcpy (remcomOutBuffer, "E03"); - } else { - strcpy(remcomOutBuffer,"E01"); - } + strcpy(remcomOutBuffer, "E03"); + } else + strcpy(remcomOutBuffer, "E01"); break; case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ @@ -655,50 +759,63 @@ ptr = &remcomInBuffer[1]; - if (hexToInt(&ptr, &addr) - && *ptr++ == ',' - && hexToInt(&ptr, &length) - && *ptr++ == ':') { - if (hex2mem(ptr, (char *)addr, length)) { + if (hexToInt(&ptr, &addr) && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length)) strcpy(remcomOutBuffer, "OK"); - } else { + else strcpy(remcomOutBuffer, "E03"); - } flush_icache_range(addr, addr+length); - } else { + } else strcpy(remcomOutBuffer, "E02"); - } break; - case 'k': /* kill the program, actually just continue */ - case 'c': /* cAA..AA Continue; address AA..AA optional */ + case 'k': /* kill the program, actually just continue */ + case 'c': /* cAA..AA Continue; address AA..AA optional */ /* try to read optional parameter, pc unchanged if no parm */ ptr = &remcomInBuffer[1]; - if (hexToInt(&ptr, &addr)) { + if (hexToInt(&ptr, &addr)) regs->nip = addr; - } /* Need to flush the instruction cache here, as we may have deposited a * breakpoint, and the icache probably has no way of knowing that a data ref to * some location may have changed something that is in the instruction cache. */ kgdb_flush_cache_all(); +#if defined(CONFIG_4xx) + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); +#endif set_msr(msr); + kgdb_interruptible(1); unlock_kernel(); kgdb_active = 0; + if (kdebug) { + printk("remcomInBuffer: %s\n", remcomInBuffer); + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + } return; case 's': kgdb_flush_cache_all(); +#if defined(CONFIG_4xx) + regs->msr |= MSR_DE; + regs->dbcr0 |= (DBCR0_IDM | DBCR0_IC); + set_msr(msr); +#else regs->msr |= MSR_SE; -#if 0 set_msr(msr | MSR_SE); #endif unlock_kernel(); kgdb_active = 0; + if (kdebug) { + printk("remcomInBuffer: %s\n", remcomInBuffer); + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + } return; case 'r': /* Reset (if user process..exit ???)*/ @@ -729,7 +846,7 @@ asm(" .globl breakinst breakinst: .long 0x7d821008 - "); + "); } /* Output string in GDB O-packet format if GDB has connected. If nothing @@ -739,24 +856,23 @@ { char buffer[512]; - if (!kgdb_started) - return 0; + if (!kgdb_started) + return 0; - count = (count <= (sizeof(buffer) / 2 - 2)) + count = (count <= (sizeof(buffer) / 2 - 2)) ? count : (sizeof(buffer) / 2 - 2); buffer[0] = 'O'; mem2hex (s, &buffer[1], count); putpacket(buffer); - return 1; + return 1; } -#ifndef CONFIG_8xx +#if defined(CONFIG_6xx) || defined(CONFIG_POWER3) || defined(CONFIG_ISERIES) -/* I don't know why other platforms don't need this. The function for - * the 8xx is found in arch/ppc/8xx_io/uart.c. -- Dan - */ +/* This is used on arches which don't have a serial driver that maps + * the ports for us */ void kgdb_map_scc(void) { diff -Nru a/arch/ppc/kernel/ppc405_dma.c b/arch/ppc/kernel/ppc405_dma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/ppc405_dma.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,511 @@ +/* + * linux/arch/ppc/kernel/ppc405_dma.c + * + * BRIEF MODULE DESCRIPTION + * IBM 405 DMA Controller Functions + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* + * Function prototypes + */ + +int hw_init_dma_channel(unsigned int, ppc_dma_ch_t *); +int init_dma_channel(unsigned int); +int get_channel_config(unsigned int, ppc_dma_ch_t *); +int set_channel_priority(unsigned int, unsigned int); +unsigned int get_peripheral_width(unsigned int); +int alloc_dma_handle(sgl_handle_t *, unsigned int, unsigned int); +void free_dma_handle(sgl_handle_t); + + +ppc_dma_ch_t dma_channels[MAX_405GP_DMA_CHANNELS]; + +/* + * Configures a DMA channel, including the peripheral bus width, if a + * peripheral is attached to the channel, the polarity of the DMAReq and + * DMAAck signals, etc. This information should really be setup by the boot + * code, since most likely the configuration won't change dynamically. + * If the kernel has to call this function, it's recommended that it's + * called from platform specific init code. The driver should not need to + * call this function. + */ +int hw_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t *p_init) +{ + unsigned int polarity; + uint32_t control = 0; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + +#ifdef DEBUG_405DMA + if (!p_init) { + printk("hw_init_dma_channel: NULL p_init\n"); + return DMA_STATUS_NULL_POINTER; + } + if (dmanr >= MAX_405GP_DMA_CHANNELS) { + printk("hw_init_dma_channel: bad channel %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } +#endif + +#if DCRN_POL > 0 + polarity = mfdcr(DCRN_POL); +#else + polarity = 0; +#endif + + /* Setup the control register based on the values passed to + * us in p_init. Then, over-write the control register with this + * new value. + */ + + control |= ( + SET_DMA_CIE_ENABLE(p_init->int_enable) | /* interrupt enable */ + SET_DMA_BEN(p_init->buffer_enable) | /* buffer enable */ + SET_DMA_ETD(p_init->etd_output) | /* end of transfer pin */ + SET_DMA_TCE(p_init->tce_enable) | /* terminal count enable */ + SET_DMA_PL(p_init->pl) | /* peripheral location */ + SET_DMA_DAI(p_init->dai) | /* dest addr increment */ + SET_DMA_SAI(p_init->sai) | /* src addr increment */ + SET_DMA_PRIORITY(p_init->cp) | /* channel priority */ + SET_DMA_PW(p_init->pwidth) | /* peripheral/bus width */ + SET_DMA_PSC(p_init->psc) | /* peripheral setup cycles */ + SET_DMA_PWC(p_init->pwc) | /* peripheral wait cycles */ + SET_DMA_PHC(p_init->phc) | /* peripheral hold cycles */ + SET_DMA_PREFETCH(p_init->pf) /* read prefetch */ + ); + + switch (dmanr) { + case 0: + /* clear all polarity signals and then "or" in new signal levels */ + polarity &= ~(DMAReq0_ActiveLow | DMAAck0_ActiveLow | EOT0_ActiveLow); + polarity |= p_dma_ch->polarity; +#if DCRN_POL > 0 + mtdcr(DCRN_POL, polarity); +#endif + mtdcr(DCRN_DMACR0, control); + break; + case 1: + polarity &= ~(DMAReq1_ActiveLow | DMAAck1_ActiveLow | EOT1_ActiveLow); + polarity |= p_dma_ch->polarity; +#if DCRN_POL > 0 + mtdcr(DCRN_POL, polarity); +#endif + mtdcr(DCRN_DMACR1, control); + break; + case 2: + polarity &= ~(DMAReq2_ActiveLow | DMAAck2_ActiveLow | EOT2_ActiveLow); + polarity |= p_dma_ch->polarity; +#if DCRN_POL > 0 + mtdcr(DCRN_POL, polarity); +#endif + mtdcr(DCRN_DMACR2, control); + break; + case 3: + polarity &= ~(DMAReq3_ActiveLow | DMAAck3_ActiveLow | EOT3_ActiveLow); + polarity |= p_dma_ch->polarity; +#if DCRN_POL > 0 + mtdcr(DCRN_POL, polarity); +#endif + mtdcr(DCRN_DMACR3, control); + break; + default: + return DMA_STATUS_BAD_CHANNEL; + } + + /* save these values in our dma channel structure */ + memcpy(p_dma_ch, p_init, sizeof(ppc_dma_ch_t)); + + /* + * The peripheral width values written in the control register are: + * PW_8 0 + * PW_16 1 + * PW_32 2 + * PW_64 3 + * + * Since the DMA count register takes the number of "transfers", + * we need to divide the count sent to us in certain + * functions by the appropriate number. It so happens that our + * right shift value is equal to the peripheral width value. + */ + p_dma_ch->shift = p_init->pwidth; + + /* + * Save the control word for easy access. + */ + p_dma_ch->control = control; + + mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */ + return DMA_STATUS_GOOD; +} + + + + +/* + * This function returns the channel configuration. + */ +int get_channel_config(unsigned int dmanr, ppc_dma_ch_t *p_dma_ch) +{ + unsigned int polarity; + unsigned int control; + +#if DCRN_POL > 0 + polarity = mfdcr(DCRN_POL); +#else + polarity = 0; +#endif + + switch (dmanr) { + case 0: + p_dma_ch->polarity = + polarity & (DMAReq0_ActiveLow | DMAAck0_ActiveLow | EOT0_ActiveLow); + control = mfdcr(DCRN_DMACR0); + break; + case 1: + p_dma_ch->polarity = + polarity & (DMAReq1_ActiveLow | DMAAck1_ActiveLow | EOT1_ActiveLow); + control = mfdcr(DCRN_DMACR1); + break; + case 2: + p_dma_ch->polarity = + polarity & (DMAReq2_ActiveLow | DMAAck2_ActiveLow | EOT2_ActiveLow); + control = mfdcr(DCRN_DMACR2); + break; + case 3: + p_dma_ch->polarity = + polarity & (DMAReq3_ActiveLow | DMAAck3_ActiveLow | EOT3_ActiveLow); + control = mfdcr(DCRN_DMACR3); + break; + default: + return DMA_STATUS_BAD_CHANNEL; + } + + p_dma_ch->cp = GET_DMA_PRIORITY(control); + p_dma_ch->pwidth = GET_DMA_PW(control); + p_dma_ch->psc = GET_DMA_PSC(control); + p_dma_ch->pwc = GET_DMA_PWC(control); + p_dma_ch->phc = GET_DMA_PHC(control); + p_dma_ch->pf = GET_DMA_PREFETCH(control); + p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control); + p_dma_ch->shift = GET_DMA_PW(control); + + return DMA_STATUS_GOOD; +} + +/* + * Sets the priority for the DMA channel dmanr. + * Since this is setup by the hardware init function, this function + * can be used to dynamically change the priority of a channel. + * + * Acceptable priorities: + * + * PRIORITY_LOW + * PRIORITY_MID_LOW + * PRIORITY_MID_HIGH + * PRIORITY_HIGH + * + */ +int set_channel_priority(unsigned int dmanr, unsigned int priority) +{ + unsigned int control; + +#ifdef DEBUG_405DMA + if ( (priority != PRIORITY_LOW) && + (priority != PRIORITY_MID_LOW) && + (priority != PRIORITY_MID_HIGH) && + (priority != PRIORITY_HIGH)) { + printk("set_channel_priority: bad priority: 0x%x\n", priority); + } +#endif + + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + control|= SET_DMA_PRIORITY(priority); + mtdcr(DCRN_DMACR0, control); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + control|= SET_DMA_PRIORITY(priority); + mtdcr(DCRN_DMACR1, control); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + control|= SET_DMA_PRIORITY(priority); + mtdcr(DCRN_DMACR2, control); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + control|= SET_DMA_PRIORITY(priority); + mtdcr(DCRN_DMACR3, control); + break; + default: +#ifdef DEBUG_405DMA + printk("set_channel_priority: bad channel: %d\n", dmanr); +#endif + return DMA_STATUS_BAD_CHANNEL; + } + return DMA_STATUS_GOOD; +} + + + +/* + * Returns the width of the peripheral attached to this channel. This assumes + * that someone who knows the hardware configuration, boot code or some other + * init code, already set the width. + * + * The return value is one of: + * PW_8 + * PW_16 + * PW_32 + * PW_64 + * + * The function returns 0 on error. + */ +unsigned int get_peripheral_width(unsigned int dmanr) +{ + unsigned int control; + + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + break; + default: +#ifdef DEBUG_405DMA + printk("get_peripheral_width: bad channel: %d\n", dmanr); +#endif + return 0; + } + return(GET_DMA_PW(control)); +} + + + + +/* + * Create a scatter/gather list handle. This is simply a structure which + * describes a scatter/gather list. + * + * A handle is returned in "handle" which the driver should save in order to + * be able to access this list later. A chunk of memory will be allocated + * to be used by the API for internal management purposes, including managing + * the sg list and allocating memory for the sgl descriptors. One page should + * be more than enough for that purpose. Perhaps it's a bit wasteful to use + * a whole page for a single sg list, but most likely there will be only one + * sg list per channel. + * + * Interrupt notes: + * Each sgl descriptor has a copy of the DMA control word which the DMA engine + * loads in the control register. The control word has a "global" interrupt + * enable bit for that channel. Interrupts are further qualified by a few bits + * in the sgl descriptor count register. In order to setup an sgl, we have to + * know ahead of time whether or not interrupts will be enabled at the completion + * of the transfers. Thus, enable_dma_interrupt()/disable_dma_interrupt() MUST + * be called before calling alloc_dma_handle(). If the interrupt mode will never + * change after powerup, then enable_dma_interrupt()/disable_dma_interrupt() + * do not have to be called -- interrupts will be enabled or disabled based + * on how the channel was configured after powerup by the hw_init_dma_channel() + * function. Each sgl descriptor will be setup to interrupt if an error occurs; + * however, only the last descriptor will be setup to interrupt. Thus, an + * interrupt will occur (if interrupts are enabled) only after the complete + * sgl transfer is done. + */ +int alloc_dma_handle(sgl_handle_t *phandle, unsigned int mode, unsigned int dmanr) +{ + sgl_list_info_t *psgl; + dma_addr_t dma_addr; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + uint32_t sg_command; + void *ret; + +#ifdef DEBUG_405DMA + if (!phandle) { + printk("alloc_dma_handle: null handle pointer\n"); + return DMA_STATUS_NULL_POINTER; + } + switch (mode) { + case DMA_MODE_READ: + case DMA_MODE_WRITE: + case DMA_MODE_MM: + case DMA_MODE_MM_DEVATSRC: + case DMA_MODE_MM_DEVATDST: + break; + default: + printk("alloc_dma_handle: bad mode 0x%x\n", mode); + return DMA_STATUS_BAD_MODE; + } + if (dmanr >= MAX_405GP_DMA_CHANNELS) { + printk("alloc_dma_handle: invalid channel 0x%x\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } +#endif + + /* Get a page of memory, which is zeroed out by pci_alloc_consistent() */ + +/* wrong not a pci device - armin */ + /* psgl = (sgl_list_info_t *) pci_alloc_consistent(NULL, SGL_LIST_SIZE, &dma_addr); +*/ + + ret = consistent_alloc(GFP_ATOMIC |GFP_DMA, SGL_LIST_SIZE, &dma_addr); + if (ret != NULL) { + memset(ret, 0,SGL_LIST_SIZE ); + psgl = (sgl_list_info_t *) ret; + } + + + if (psgl == NULL) { + *phandle = (sgl_handle_t)NULL; + return DMA_STATUS_OUT_OF_MEMORY; + } + + psgl->dma_addr = dma_addr; + psgl->dmanr = dmanr; + + /* + * Modify and save the control word. These word will get written to each sgl + * descriptor. The DMA engine then loads this control word into the control + * register every time it reads a new descriptor. + */ + psgl->control = p_dma_ch->control; + psgl->control &= ~(DMA_TM_MASK | DMA_TD); /* clear all "mode" bits first */ + psgl->control |= (mode | DMA_CH_ENABLE); /* save the control word along with the mode */ + + if (p_dma_ch->int_enable) { + psgl->control |= DMA_CIE_ENABLE; /* channel interrupt enabled */ + } + else { + psgl->control &= ~DMA_CIE_ENABLE; + } + +#if DCRN_ASGC > 0 + sg_command = mfdcr(DCRN_ASGC); + switch (dmanr) { + case 0: + sg_command |= SSG0_MASK_ENABLE; + break; + case 1: + sg_command |= SSG1_MASK_ENABLE; + break; + case 2: + sg_command |= SSG2_MASK_ENABLE; + break; + case 3: + sg_command |= SSG3_MASK_ENABLE; + break; + default: +#ifdef DEBUG_405DMA + printk("alloc_dma_handle: bad channel: %d\n", dmanr); +#endif + free_dma_handle((sgl_handle_t)psgl); + *phandle = (sgl_handle_t)NULL; + return DMA_STATUS_BAD_CHANNEL; + } + + mtdcr(DCRN_ASGC, sg_command); /* enable writing to this channel's sgl control bits */ +#else + (void)sg_command; +#endif + psgl->sgl_control = SG_ERI_ENABLE | SG_LINK; /* sgl descriptor control bits */ + + if (p_dma_ch->int_enable) { + if (p_dma_ch->tce_enable) + psgl->sgl_control |= SG_TCI_ENABLE; + else + psgl->sgl_control |= SG_ETI_ENABLE; + } + + *phandle = (sgl_handle_t)psgl; + return DMA_STATUS_GOOD; +} + + + +/* + * Destroy a scatter/gather list handle that was created by alloc_dma_handle(). + * The list must be empty (contain no elements). + */ +void free_dma_handle(sgl_handle_t handle) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *)handle; + + if (!handle) { +#ifdef DEBUG_405DMA + printk("free_dma_handle: got NULL\n"); +#endif + return; + } + else if (psgl->phead) { +#ifdef DEBUG_405DMA + printk("free_dma_handle: list not empty\n"); +#endif + return; + } + else if (!psgl->dma_addr) { /* should never happen */ +#ifdef DEBUG_405DMA + printk("free_dma_handle: no dma address\n"); +#endif + return; + } + + /* wrong not a PCI device -armin */ + /* pci_free_consistent(NULL, SGL_LIST_SIZE, (void *)psgl, psgl->dma_addr); */ + // free_pages((unsigned long)psgl, get_order(SGL_LIST_SIZE)); + consistent_free((void *)psgl); + + +} + + +EXPORT_SYMBOL(hw_init_dma_channel); +EXPORT_SYMBOL(get_channel_config); +EXPORT_SYMBOL(set_channel_priority); +EXPORT_SYMBOL(get_peripheral_width); +EXPORT_SYMBOL(alloc_dma_handle); +EXPORT_SYMBOL(free_dma_handle); +EXPORT_SYMBOL(dma_channels); diff -Nru a/arch/ppc/kernel/ppc405_pci.c b/arch/ppc/kernel/ppc405_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/ppc405_pci.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,207 @@ +/* + * FILE NAME: ppc405_pci.c + * + * BRIEF MODULE DESCRIPTION: + * Based on arch/ppc/kernel/indirect.c, Copyright (C) 1998 Gabriel Paubert. + * + * Author: MontaVista Software, Inc. + * Frank Rowand + * Debbie Chu + * + * Copyright 2000 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_DEBUG_BRINGUP +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +extern void bios_fixup(struct pci_controller *, void *); +extern int ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, + unsigned char pin); +extern struct pcil0_regs *PCIL_ADDR[]; + +void +ppc405_pcibios_fixup_resources(struct pci_dev *dev) +{ + int i; + unsigned long max_host_addr; + unsigned long min_host_addr; + struct resource *res; + + /* + * openbios puts some graphics cards in the same range as the host + * controller uses to map to SDRAM. Fix it. + */ + + min_host_addr = 0; + max_host_addr = PPC405_PCI_MEM_BASE - 1; + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + res = dev->resource + i; + if (!res->start) + continue; + if ((res->flags & IORESOURCE_MEM) && + (((res->start >= min_host_addr) + && (res->start <= max_host_addr)) + || ((res->end >= min_host_addr) + && (res->end <= max_host_addr)) + || ((res->start < min_host_addr) + && (res->end > max_host_addr)) + ) + ) { + + DBG(KERN_ERR "PCI: 0x%lx <= resource[%d] <= 0x%lx" + ", bus 0x%x dev 0x%2.2x.%1.1x,\n" + KERN_ERR " %s\n" + KERN_ERR " fixup will be attempted later\n", + min_host_addr, i, max_host_addr, + dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), dev->name); + + /* force pcibios_assign_resources() to assign a new address */ + res->end -= res->start; + res->start = 0; + } + } +} + +static int +ppc4xx_exclude_device(unsigned char bus, unsigned char devfn) +{ + /* We prevent us from seeing ourselves to avoid having + * the kernel try to remap our BAR #1 and fuck up bus + * master from external PCI devices + */ + return (bus == 0 && devfn == 0); +} + +void +ppc4xx_find_bridges(void) +{ + struct pci_controller *hose_a; + struct pcil0_regs *pcip; + unsigned int tmp_addr; + unsigned int tmp_size; + unsigned int reg_index; + unsigned int new_pmm_max; + unsigned int new_pmm_min; + + isa_io_base = 0; + isa_mem_base = 0; + pci_dram_offset = 0; + + /* Check if running in slave mode */ + if ((mfdcr(DCRN_CHPSR) & PSR_PCI_ARBIT_EN) == 0) { + printk("Running as PCI slave, kernel PCI disabled !\n"); + return; + } + /* Setup PCI32 hose */ + hose_a = pcibios_alloc_controller(); + if (!hose_a) + return; + setup_indirect_pci(hose_a, PPC405_PCI_CONFIG_ADDR, + PPC405_PCI_CONFIG_DATA); + pcip = ioremap((unsigned long) PCIL_ADDR[0], PAGE_SIZE); + if (pcip != NULL) { + +#if defined(CONFIG_BIOS_FIXUP) + bios_fixup(hose_a, pcip); +#endif + new_pmm_min = 0xffffffff; + for (reg_index = 0; reg_index < 3; reg_index++) { + tmp_size = in_le32((void *) &(pcip->pmm[reg_index].ma)); // *_PMM0MA + if (tmp_size & 0x1) { + tmp_addr = in_le32((void *) &(pcip->pmm[reg_index].pcila)); // *_PMM0PCILA + if (tmp_addr < PPC405_PCI_PHY_MEM_BASE) { + printk(KERN_DEBUG + "Disabling mapping to PCI mem addr 0x%8.8x\n", + tmp_addr); + out_le32((void *) &(pcip->pmm[reg_index].ma), tmp_size & ~1); // *_PMMOMA + } else { + tmp_addr = in_le32((void *) &(pcip->pmm[reg_index].la)); // *_PMMOLA + if (tmp_addr < new_pmm_min) + new_pmm_min = tmp_addr; + tmp_addr = + tmp_addr + (0xffffffff - + (tmp_size & + 0xffffc000)); + if (tmp_addr > PPC405_PCI_UPPER_MEM) { + new_pmm_max = tmp_addr; // PPC405_PCI_UPPER_MEM + } else { + new_pmm_max = + PPC405_PCI_UPPER_MEM; + } + } + } + + } // for + + iounmap(pcip); + } + hose_a->first_busno = 0; + hose_a->last_busno = 0xff; + hose_a->pci_mem_offset = 0; + + /* Setup bridge memory/IO ranges & resources + * TODO: Handle firmwares setting up a legacy ISA mem base + */ + hose_a->io_space.start = PPC405_PCI_LOWER_IO; + hose_a->io_space.end = PPC405_PCI_UPPER_IO; + hose_a->mem_space.start = new_pmm_min; + hose_a->mem_space.end = new_pmm_max; + hose_a->io_base_phys = PPC405_PCI_PHY_IO_BASE; + hose_a->io_base_virt = ioremap(hose_a->io_base_phys, 0x10000); + hose_a->io_resource.start = 0; + hose_a->io_resource.end = PPC405_PCI_UPPER_IO-PPC405_PCI_LOWER_IO; + hose_a->io_resource.flags = IORESOURCE_IO; + hose_a->io_resource.name = "PCI I/O"; + hose_a->mem_resources[0].start = new_pmm_min; + hose_a->mem_resources[0].end = new_pmm_max; + hose_a->mem_resources[0].flags = IORESOURCE_MEM; + hose_a->mem_resources[0].name = "PCI Memory"; + isa_io_base = (int)hose_a->io_base_virt; + isa_mem_base = 0; /* ISA not implemented */ + ISA_DMA_THRESHOLD = 0x00ffffff; /* ??? ISA not implemented */ + + /* Scan busses & initial setup by pci_auto */ + hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); + hose_a->last_busno = 0; + + /* Setup ppc_md */ + ppc_md.pcibios_fixup = NULL; + ppc_md.pci_exclude_device = ppc4xx_exclude_device; + ppc_md.pcibios_fixup_resources = ppc405_pcibios_fixup_resources; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = ppc405_map_irq; +} diff -Nru a/arch/ppc/kernel/ppc4xx_kgdb.c b/arch/ppc/kernel/ppc4xx_kgdb.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/ppc4xx_kgdb.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,124 @@ +#include +#include +#include +#include + + + +#define LSR_DR 0x01 /* Data ready */ +#define LSR_OE 0x02 /* Overrun */ +#define LSR_PE 0x04 /* Parity error */ +#define LSR_FE 0x08 /* Framing error */ +#define LSR_BI 0x10 /* Break */ +#define LSR_THRE 0x20 /* Xmit holding register empty */ +#define LSR_TEMT 0x40 /* Xmitter empty */ +#define LSR_ERR 0x80 /* Error */ + +#include + +extern struct NS16550* COM_PORTS[]; +#ifndef NULL +#define NULL 0x00 +#endif + +static volatile struct NS16550 *kgdb_debugport = NULL; + +volatile struct NS16550 * +NS16550_init(int chan) +{ + volatile struct NS16550 *com_port; + int quot; +#ifdef BASE_BAUD + quot = BASE_BAUD / 9600; +#else + quot = 0x000c; /* 0xc = 9600 baud (on a pc) */ +#endif + + com_port = (struct NS16550 *) COM_PORTS[chan]; + + com_port->lcr = 0x00; + com_port->ier = 0xFF; + com_port->ier = 0x00; + com_port->lcr = com_port->lcr | 0x80; /* Access baud rate */ + com_port->dll = ( quot & 0x00ff ); /* 0xc = 9600 baud */ + com_port->dlm = ( quot & 0xff00 ) >> 8; + com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */ + com_port->mcr = 0x00; /* RTS/DTR */ + com_port->fcr = 0x07; /* Clear & enable FIFOs */ + + return( com_port ); +} + + +void +NS16550_putc(volatile struct NS16550 *com_port, unsigned char c) +{ + while ((com_port->lsr & LSR_THRE) == 0) + ; + com_port->thr = c; + return; +} + +unsigned char +NS16550_getc(volatile struct NS16550 *com_port) +{ + while ((com_port->lsr & LSR_DR) == 0) + ; + return (com_port->rbr); +} + +unsigned char +NS16550_tstc(volatile struct NS16550 *com_port) +{ + return ((com_port->lsr & LSR_DR) != 0); +} + + +#if defined(CONFIG_KGDB_TTYS0) +#define KGDB_PORT 0 +#elif defined(CONFIG_KGDB_TTYS1) +#define KGDB_PORT 1 +#elif defined(CONFIG_KGDB_TTYS2) +#define KGDB_PORT 2 +#elif defined(CONFIG_KGDB_TTYS3) +#define KGDB_PORT 3 +#else +#error "invalid kgdb_tty port" +#endif + +void putDebugChar( unsigned char c ) +{ + if ( kgdb_debugport == NULL ) + kgdb_debugport = NS16550_init(KGDB_PORT); + NS16550_putc( kgdb_debugport, c ); +} + +int getDebugChar( void ) +{ + if (kgdb_debugport == NULL) + kgdb_debugport = NS16550_init(KGDB_PORT); + + return(NS16550_getc(kgdb_debugport)); +} + +void kgdb_interruptible(int enable) +{ + return; +} + +void putDebugString(char* str) +{ + while (*str != '\0') { + putDebugChar(*str); + str++; + } + putDebugChar('\r'); + return; +} + +void +kgdb_map_scc(void) +{ + printk("kgdb init \n"); + kgdb_debugport = NS16550_init(KGDB_PORT); +} diff -Nru a/arch/ppc/kernel/ppc4xx_pic.c b/arch/ppc/kernel/ppc4xx_pic.c --- a/arch/ppc/kernel/ppc4xx_pic.c Tue Feb 19 18:08:56 2002 +++ b/arch/ppc/kernel/ppc4xx_pic.c Tue Feb 19 18:08:56 2002 @@ -1,7 +1,4 @@ /* - * BK Id: SCCS/s.ppc4xx_pic.c 1.5 05/17/01 18:14:21 cort - */ -/* * * Copyright (c) 1999 Grant Erickson * @@ -33,25 +30,29 @@ #include #include #include - -#include "local_irq.h" -#include "ppc4xx_pic.h" - +#include +#include /* Global Variables */ struct hw_interrupt_type *ppc4xx_pic; +/* Six of one, half dozen of the other....#ifdefs, separate files, + * other tricks..... + * + * There are basically two types of interrupt controllers, the 403 AIC + * and the "others" with UIC. I just kept them both here separated + * with #ifdefs, but it seems to change depending upon how supporting + * files (like ppc4xx.h) change. -- Dan. + */ + +#ifdef CONFIG_403 /* Function Prototypes */ -static void ppc403_aic_enable(unsigned int irq); -static void ppc403_aic_disable(unsigned int irq); -static void ppc403_aic_disable_and_ack(unsigned int irq); - -static void ppc405_uic_enable(unsigned int irq); -static void ppc405_uic_disable(unsigned int irq); -static void ppc405_uic_disable_and_ack(unsigned int irq); +static void ppc403_aic_enable(unsigned int irq); +static void ppc403_aic_disable(unsigned int irq); +static void ppc403_aic_disable_and_ack(unsigned int irq); static struct hw_interrupt_type ppc403_aic = { "403GC AIC", @@ -63,55 +64,11 @@ 0 }; -static struct hw_interrupt_type ppc405_uic = { - "405GP UIC", - NULL, - NULL, - ppc405_uic_enable, - ppc405_uic_disable, - ppc405_uic_disable_and_ack, - 0 -}; - -/* - * Document me. - */ -void __init -ppc4xx_pic_init(void) -{ - unsigned long ver = PVR_VER(mfspr(SPRN_PVR)); - - switch (ver) { - - case PVR_VER(PVR_403GC): - /* - * Disable all external interrupts until they are - * explicity requested. - */ - ppc_cached_irq_mask[0] = 0; - mtdcr(DCRN_EXIER, 0); - - ppc4xx_pic = &ppc403_aic; - break; - - case PVR_VER(PVR_405GP): - ppc4xx_pic = &ppc405_uic; - break; - } - - return; -} - -/* - * XXX - Currently 403-specific! - * - * Document me. - */ int -ppc4xx_pic_get_irq(struct pt_regs *regs) +ppc403_pic_get_irq(struct pt_regs *regs) { int irq; - unsigned long bits, mask = (1 << 31); + unsigned long bits; /* * Only report the status of those interrupts that are actually @@ -123,19 +80,17 @@ /* * Walk through the interrupts from highest priority to lowest, and * report the first pending interrupt found. + * We want PPC, not C bit numbering, so just subtract the ffs() + * result from 32. */ + irq = 32 - ffs(bits); - for (irq = 0; irq < NR_IRQS; irq++, mask >>= 1) { - if (bits & mask) - break; - } + if (irq == NR_AIC_IRQS) + irq = -1; return (irq); } -/* - * Document me. - */ static void ppc403_aic_enable(unsigned int irq) { @@ -148,9 +103,6 @@ mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); } -/* - * Document me. - */ static void ppc403_aic_disable(unsigned int irq) { @@ -163,9 +115,6 @@ mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); } -/* - * Document me. - */ static void ppc403_aic_disable_and_ack(unsigned int irq) { @@ -179,29 +128,131 @@ mtdcr(DCRN_EXISR, (1 << (31 - bit))); } -/* - * Document me. - */ +#else /* !CONFIG_403 */ + static void ppc405_uic_enable(unsigned int irq) { - /* XXX - Implement me. */ + int bit, word; + + bit = irq & 0x1f; + word = irq >> 5; + + ppc_cached_irq_mask[word] |= 1 << (31 - bit); + mtdcr(DCRN_UIC0_ER, ppc_cached_irq_mask[word]); } -/* - * Document me. - */ static void ppc405_uic_disable(unsigned int irq) { - /* XXX - Implement me. */ + int bit, word; + + bit = irq & 0x1f; + word = irq >> 5; + + ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); + mtdcr(DCRN_UIC0_ER, ppc_cached_irq_mask[word]); } -/* - * Document me. - */ static void ppc405_uic_disable_and_ack(unsigned int irq) { - /* XXX - Implement me. */ + int bit, word; + + bit = irq & 0x1f; + word = irq >> 5; + + ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); + mtdcr(DCRN_UIC0_ER, ppc_cached_irq_mask[word]); + mtdcr(DCRN_UIC0_SR, (1 << (31 - bit))); +} + +static void +ppc405_uic_end(unsigned int irq) +{ + int bit, word; + unsigned int tr_bits; + + bit = irq & 0x1f; + word = irq >> 5; + + tr_bits = mfdcr(DCRN_UIC0_TR); + if ((tr_bits & (1 << (31 - bit))) == 0) { + /* level trigger */ + mtdcr(DCRN_UIC0_SR, 1 << (31 - bit)); + } + + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + ppc_cached_irq_mask[word] |= 1 << (31 - bit); + mtdcr(DCRN_UIC0_ER, ppc_cached_irq_mask[word]); + } +} + +static struct hw_interrupt_type ppc405_uic = { +#if defined (CONFIG_405GP) + "405GP UIC", +#else + "NP405 UIC", +#endif + NULL, + NULL, + ppc405_uic_enable, + ppc405_uic_disable, + ppc405_uic_disable_and_ack, + ppc405_uic_end, + 0 +}; + +int +ppc405_pic_get_irq(struct pt_regs *regs) +{ + int irq; + unsigned long bits; + + /* + * Only report the status of those interrupts that are actually + * enabled. + */ + + bits = mfdcr(DCRN_UIC0_MSR); + + /* + * Walk through the interrupts from highest priority to lowest, and + * report the first pending interrupt found. + * We want PPC, not C bit numbering, so just subtract the ffs() + * result from 32. + */ + irq = 32 - ffs(bits); + + if (irq == NR_AIC_IRQS) + irq = -1; + + return (irq); +} +#endif + +void __init +ppc4xx_pic_init(void) +{ + /* + * Disable all external interrupts until they are + * explicity requested. + */ + ppc_cached_irq_mask[0] = 0; + +#ifdef CONFIG_403 + mtdcr(DCRN_EXIER, ppc_cached_irq_mask[0]); + + ppc4xx_pic = &ppc403_aic; + ppc_md.get_irq = ppc403_pic_get_irq; +#else + mtdcr(DCRN_UIC0_ER, ppc_cached_irq_mask[0]); + + /* Set all interrupts to non-critical. + */ + mtdcr(DCRN_UIC0_CR, 0); + + ppc4xx_pic = &ppc405_uic; + ppc_md.get_irq = ppc405_pic_get_irq; +#endif } diff -Nru a/arch/ppc/kernel/ppc4xx_pic.h b/arch/ppc/kernel/ppc4xx_pic.h --- a/arch/ppc/kernel/ppc4xx_pic.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,30 +0,0 @@ -/* - * BK Id: SCCS/s.ppc4xx_pic.h 1.8 06/15/01 13:56:56 paulus - */ -/* - * - * Copyright (c) 1999 Grant Erickson - * - * Module name: ppc4xx_pic.h - * - * Description: - * Interrupt controller driver for PowerPC 4xx-based processors. - */ - -#ifndef __PPC4XX_PIC_H__ -#define __PPC4XX_PIC_H__ - -#include -#include "local_irq.h" - -/* External Global Variables */ - -extern struct hw_interrupt_type *ppc4xx_pic; - - -/* Function Prototypes */ - -extern void ppc4xx_pic_init(void); -extern int ppc4xx_pic_get_irq(struct pt_regs *regs); - -#endif /* __PPC4XX_PIC_H__ */ diff -Nru a/arch/ppc/kernel/ppc4xx_pm.c b/arch/ppc/kernel/ppc4xx_pm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/ppc4xx_pm.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,75 @@ +/* + * file: ppc4xx_pm.c + * + * This an attempt to get Power Management going for the IBM 4xx processor. + * This was derived from the ppc4xx._setup.c file + * + * Armin Kuster akuster@mvista.com + * Jan 2002 + * + * + * Copyright 2002 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * Version 1.0 (02/14/01) - A. Kuster + * Initial version - moved pm code from ppc4xx_setup.c + * + * 1.1 02/21/01 - A. Kuster + * minor fixes, init value to 0 & += to &= + * added stb03 ifdef for 2nd i2c device + */ + +#include +#include + +#include + +void __init +ppc4xx_pm_init(void) +{ + + unsigned int value = 0; + + /* turn off unused hardware to save power */ +#ifdef CONFIG_405GP + value |= CPM_DCP; /* CodePack */ +#endif + +#if !defined(CONFIG_IBM_OCP_GPIO) + value |= CPM_GPIO0; +#endif + +#if !defined(CONFIG_PPC405_I2C_ADAP) + value |= CPM_IIC0; +#ifdef CONFIG_STB03xxx + value |= CPM_IIC1; +#endif +#endif + + +#if !defined(CONFIG_405_DMA) + value |= CPM_DMA; +#endif + + mtdcr(DCRN_CPMFR, value); + +} diff -Nru a/arch/ppc/kernel/ppc4xx_serial.c b/arch/ppc/kernel/ppc4xx_serial.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/ppc4xx_serial.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,190 @@ + +/* + * linux/arch/ppc/kernel/ppc405_serial.c + * + * This is a fairly standard 165xx type device that will eventually + * be merged with other similar processor/boards. -- Dan + * + * BRIEF MODULE DESCRIPTION + * Console I/O support for Early kernel bringup. + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * frank_rowand@mvista.com or source@mvista.com + * debbie_chu@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. + */ + +#include + +#if defined(CONFIG_IBM405GP) || defined(CONFIG_IBM405CR) + +#ifdef CONFIG_KGDB +#include +#include +#endif + +#ifdef CONFIG_DEBUG_BRINGUP + +#include + +extern void ftr_reset_preferred_console(void); + + +static int ppc405_sercons_setup(struct console *co, char *options) +{ +#ifdef CONFIG_UART0_DEBUG_CONSOLE + volatile unsigned char *uart_dll = (char *)0xef600300; + volatile unsigned char *uart_fcr = (char *)0xef600302; + volatile unsigned char *uart_lcr = (char *)0xef600303; +#endif + +#ifdef CONFIG_UART1_DEBUG_CONSOLE + volatile unsigned char *uart_dll = (char *)0xef600400; + volatile unsigned char *uart_fcr = (char *)0xef600402; + volatile unsigned char *uart_lcr = (char *)0xef600403; +#endif + + *uart_lcr = *uart_lcr | 0x80; /* DLAB on */ + +/* ftr revisit - there is no config option for this +** also see include/asm-ppc/ppc405_serial.h +** +** #define CONFIG_IBM405GP_INTERNAL_CLOCK +*/ + + +#ifdef CONFIG_IBM405GP_INTERNAL_CLOCK + /* ftr revisit + ** why is bit 19 of chcr0 (0x1000) being set? + */ + /* 0x2a results in data corruption, kgdb works with 0x28 */ + *uart_dll = 0x28; /* 9600 baud */ + _put_CHCR0((_get_CHCR0() & 0xffffe000) | 0x103e); +#else + *uart_dll = 0x48; /* 9600 baud */ +#endif + *uart_lcr = *uart_lcr & 0x7f; /* DLAB off */ + + return 0; +} + + +/* + * This is a bringup hack, writing directly to uart0 or uart1 + */ + +static void +ppc405_sercons_write(struct console *co, const char *ptr, + unsigned nb) +{ + int i; + +#ifdef CONFIG_UART0_DEBUG_CONSOLE + volatile unsigned char *uart_xmit = (char *)0xef600300; + volatile unsigned char *uart_lsr = (char *)0xef600305; +#endif + +#ifdef CONFIG_UART1_DEBUG_CONSOLE + volatile unsigned char *uart_xmit = (char *)0xef600400; + volatile unsigned char *uart_lsr = (char *)0xef600405; +#endif + + for (i = 0; i < nb; ++i) { + + /* wait for transmit reg (possibly fifo) to empty */ + while ((*uart_lsr & 0x40) == 0) + ; + + *uart_xmit = (ptr[i] & 0xff); + + if (ptr[i] == '\n') { + + /* add a carriage return */ + + /* wait for transmit reg (possibly fifo) to empty */ + while ((*uart_lsr & 0x40) == 0) + ; + + *uart_xmit = '\r'; + } + } + + return; +} + + +static int +ppc405_sercons_read(struct console *co, char *ptr, unsigned nb) +{ +#ifdef CONFIG_UART0_DEBUG_CONSOLE + volatile unsigned char *uart_rcv = (char *)0xef600300; + volatile unsigned char *uart_lsr = (char *)0xef600305; +#endif + +#ifdef CONFIG_UART1_DEBUG_CONSOLE + volatile unsigned char *uart_rcv = (char *)0xef600400; + volatile unsigned char *uart_lsr = (char *)0xef600405; +#endif + + + /* ftr revisit: not tested */ + + if (nb == 0) + return(0); + + if (!ptr) + return(-1); + + /* wait for receive reg (possibly fifo) to contain data */ + while ((*uart_lsr & 0x01) == 0) + ; + + *ptr = *uart_rcv; + + return(1); +} + +static struct console ppc405_sercons = { + name: "dbg_cons", + write: ppc405_console_write, + setup: ppc405_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void +register_debug_console(void) +{ + register_console(&ppc405_sercons); +} + +void +unregister_debug_console(void) +{ + unregister_console(&ppc405_sercons); +} + +#endif /* CONFIG_DEBUG_BRINGUP */ + +#endif /* #if defined(CONFIG_IBM405GP) || defined(CONFIG_IBM405CR) */ diff -Nru a/arch/ppc/kernel/ppc4xx_setup.c b/arch/ppc/kernel/ppc4xx_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/ppc4xx_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,415 @@ +/* + * + * Copyright (c) 1999-2000 Grant Erickson + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Author: MontaVista Software, Inc. + * Frank Rowand + * Debbie Chu + * + * Module name: ppc4xx_setup.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * IBM PowerPC 4xx based boards. Adapted from original + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + * History: 11/09/2001 - armin + * rename board_setup_nvram_access to board_init. board_init is + * used for all other board specific instructions needed during + * platform_init. + * moved RTC to board.c files + * moved VT/FB to board.c files + * moved r/w4 ide to redwood.c + * + */ + +#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 + +/* Function Prototypes */ +extern void abort(void); +extern void ppc4xx_find_bridges(void); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_pretranslate(unsigned char scancode, char raw_mode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); + +extern int nonpci_ide_default_irq(ide_ioreg_t base); +extern void nonpci_ide_init_hwif_ports(hw_regs_t * hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, int *irq); + +extern void ppc4xx_wdt_heartbeat(void); +extern int wdt_enable; +extern unsigned long wdt_period; + +/* Board specific functions */ +extern void board_setup_arch(void); +extern void board_io_mapping(void); +extern void board_setup_irq(void); +extern void board_init(void); + +/* Global Variables */ +unsigned char __res[sizeof (bd_t)]; + +static void __init +ppc4xx_setup_arch(void) +{ + + /* Setup PCI host bridges */ + +#ifdef CONFIG_PCI + ppc4xx_find_bridges(); +#endif + +#if defined(CONFIG_FB) + conswitchp = &dummy_con; +#endif + + board_setup_arch(); +} + +/* + * This routine pretty-prints the platform's internal CPU clock + * frequencies into the buffer for usage in /proc/cpuinfo. + */ + +static int +ppc4xx_show_percpuinfo(struct seq_file *m, int i) +{ + bd_t *bip = (bd_t *) __res; + + seq_printf(m, "clock\t\t: %ldMHz\n", (long) bip->bi_intfreq / 1000000); + + return 0; +} + +/* + * This routine pretty-prints the platform's internal bus clock + * frequencies into the buffer for usage in /proc/cpuinfo. + */ +static int +ppc4xx_show_cpuinfo(struct seq_file *m) +{ + bd_t *bip = (bd_t *) __res; + + seq_printf(m, "machine\t\t: %s\n", PPC4xx_MACHINE_NAME); + seq_printf(m, "plb bus clock\t: %ldMHz\n", + (long) bip->bi_busfreq / 1000000); +#ifdef CONFIG_PCI + seq_printf(m, "pci bus clock\t: %dMHz\n", + bip->bi_pci_busfreq / 1000000); +#endif + + return 0; +} + +/* + * Return the virtual address representing the top of physical RAM. + */ +static unsigned long __init +ppc4xx_find_end_of_memory(void) +{ + bd_t *bip = (bd_t *) __res; + + return ((unsigned long) bip->bi_memsize); +} + +static void __init +m4xx_map_io(void) +{ + io_block_mapping(PPC4xx_ONB_IO_VADDR, + PPC4xx_ONB_IO_PADDR, PPC4xx_ONB_IO_SIZE, _PAGE_IO); +#ifdef CONFIG_PCI + io_block_mapping(PPC4xx_PCI_IO_VADDR, + PPC4xx_PCI_IO_PADDR, PPC4xx_PCI_IO_SIZE, _PAGE_IO); + io_block_mapping(PPC4xx_PCI_CFG_VADDR, + PPC4xx_PCI_CFG_PADDR, PPC4xx_PCI_CFG_SIZE, _PAGE_IO); + io_block_mapping(PPC4xx_PCI_LCFG_VADDR, + PPC4xx_PCI_LCFG_PADDR, PPC4xx_PCI_LCFG_SIZE, _PAGE_IO); +#endif + board_io_mapping(); +} + +static void __init +ppc4xx_init_IRQ(void) +{ + int i; + + ppc4xx_pic_init(); + + for (i = 0; i < NR_IRQS; i++) + irq_desc[i].handler = ppc4xx_pic; + + /* give board specific code a chance to setup things */ + board_setup_irq(); + return; +} + +static void +ppc4xx_restart(char *cmd) +{ + printk("%s\n", cmd); + abort(); +} + +static void +ppc4xx_power_off(void) +{ + printk("System Halted\n"); + __cli(); + while (1) ; +} + +static void +ppc4xx_halt(void) +{ + printk("System Halted\n"); + __cli(); + while (1) ; +} + +/* + * This routine retrieves the internal processor frequency from the board + * information structure, sets up the kernel timer decrementer based on + * that value, enables the 4xx programmable interval timer (PIT) and sets + * it up for auto-reload. + */ +static void __init +ppc4xx_calibrate_decr(void) +{ + unsigned int freq; + bd_t *bip = (bd_t *) __res; + +#if defined(CONFIG_WALNUT) || defined(CONFIG_CEDER) + /* Walnut boot rom sets DCR CHCR1 (aka CPC0_CR1) bit CETE to 1 */ + mtdcr(DCRN_CHCR1, mfdcr(DCRN_CHCR1) & ~CHR1_CETE); +#endif +#ifdef CONFIG_REDWOOD_5 + freq = bip->bi_tbfreq; +#else + freq = bip->bi_intfreq; + +#endif + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + /* Set the time base to zero. + ** At 200 Mhz, time base will rollover in ~2925 years. + */ + + mtspr(SPRN_TBWL, 0); + mtspr(SPRN_TBWU, 0); + + /* Clear any pending timer interrupts */ + + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS); + mtspr(SPRN_TCR, TCR_PIE | TCR_ARE); + + /* Set the PIT reload value and just let it run. */ + mtspr(SPRN_PIT, tb_ticks_per_jiffy); +} + +#ifdef CONFIG_DEBUG_TEXT +static void +ppc4xx_progress(char *s, unsigned short hex) +{ + printk("%s\n\r", s); +} +#endif + +/* + * IDE stuff. + * should be generic for every IDE PCI chipset + */ +#if defined(CONFIG_BLK_DEV_IDE) +static int +ppc4xx_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +static void +ppc4xx_ide_request_region(ide_ioreg_t from, unsigned int extent, + const char *name) +{ + request_region(from, extent, name); + return; +} + +static void +ppc4xx_ide_release_region(ide_ioreg_t from, unsigned int extent) +{ + release_region(from, extent); + return; +} +#endif + +#if defined(CONFIG_BLK_DEV_IDEPCI) +static void +ppc4xx_ide_init_hwif_ports(hw_regs_t * hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, int *irq) +{ + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i) + hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET; + + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; +} +#endif + +TODC_ALLOC(); + +/* + * Input(s): + * r3 - Optional pointer to a board information structure. + * r4 - Optional pointer to the physical starting address of the init RAM + * disk. + * r5 - Optional pointer to the physical ending address of the init RAM + * disk. + * r6 - Optional pointer to the physical starting address of any kernel + * command-line parameters. + * r7 - Optional pointer to the physical ending address of any kernel + * command-line parameters. + */ +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *) __res, (void *) (r3 + KERNELBASE), + sizeof (bd_t)); + + } +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *) (r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *) (r6 + KERNELBASE)); + } +#if defined(CONFIG_PPC405_WDT) +/* Look for wdt= option on command line */ + if (strstr(cmd_line, "wdt=")) { + int valid_wdt = 0; + char *p, *q; + for (q = cmd_line; (p = strstr(q, "wdt=")) != 0;) { + q = p + 4; + if (p > cmd_line && p[-1] != ' ') + continue; + wdt_period = simple_strtoul(q, &q, 0); + valid_wdt = 1; + ++q; + } + wdt_enable = valid_wdt; + } +#endif + + /* Initialize machine-dependency vectors */ + + ppc_md.setup_arch = ppc4xx_setup_arch; + ppc_md.show_percpuinfo = ppc4xx_show_percpuinfo; + ppc_md.show_cpuinfo = ppc4xx_show_cpuinfo; + ppc_md.init_IRQ = ppc4xx_init_IRQ; + + ppc_md.restart = ppc4xx_restart; + ppc_md.power_off = ppc4xx_power_off; + ppc_md.halt = ppc4xx_halt; + + ppc_md.calibrate_decr = ppc4xx_calibrate_decr; + +#ifdef CONFIG_PPC405_WDT + ppc_md.heartbeat = ppc4xx_wdt_heartbeat; +#endif + ppc_md.heartbeat_count = 0; + + ppc_md.find_end_of_memory = ppc4xx_find_end_of_memory; + ppc_md.setup_io_mappings = m4xx_map_io; + +#ifdef CONFIG_DEBUG_TEXT + ppc_md.progress = ppc4xx_progress; +#endif + +#if defined(CONFIG_VT) && defined(CONFIG_PC_KEYBOARD) +#if defined(CONFIG_REDWOOD_4) && defined(CONFIG_STB_KB) + redwood_irkb_init(); +#else + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#endif +#endif + +/* +** m8xx_setup.c, prep_setup.c use +** defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +*/ +#if defined (CONFIG_IDE) + ppc_ide_md.ide_request_region = ppc4xx_ide_request_region; + ppc_ide_md.ide_release_region = ppc4xx_ide_release_region; + ppc_ide_md.ide_check_region = ppc4xx_ide_check_region; +#if defined(CONFIG_BLK_DEV_IDEPCI) + ppc_ide_md.ide_init_hwif = ppc4xx_ide_init_hwif_ports; +#elif defined (CONFIG_DMA_NONPCI) /* ON board IDE */ + ppc_ide_md.default_irq = nonpci_ide_default_irq; + ppc_ide_md.ide_init_hwif = nonpci_ide_init_hwif_ports; +#endif +#endif + board_init(); + + return; +} diff -Nru a/arch/ppc/kernel/ppc8260_pic.c b/arch/ppc/kernel/ppc8260_pic.c --- a/arch/ppc/kernel/ppc8260_pic.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/ppc8260_pic.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ppc8260_pic.c 1.5 05/17/01 18:14:21 cort + * BK Id: %F% %I% %G% %U% %#% */ #include @@ -85,6 +85,22 @@ sipnr[word] = 1 << (31 - bit); } +static void m8260_end_irq(unsigned int irq_nr) +{ + int bit, word; + volatile uint *simr; + + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(immr->im_intctl.ic_simrh); + ppc_cached_irq_mask[word] |= (1 << (31 - bit)); + simr[word] = ppc_cached_irq_mask[word]; + } +} + struct hw_interrupt_type ppc8260_pic = { " 8260 SIU ", NULL, @@ -92,6 +108,7 @@ m8260_unmask_irq, m8260_mask_irq, m8260_mask_and_ack, + m8260_end_irq, 0 }; @@ -106,6 +123,9 @@ * to get the irq number. */ bits = immr->im_intctl.ic_sivec; irq = bits >> 26; + + if (irq == 0) + return(-1); #if 0 irq += ppc8260_pic.irq_offset; #endif diff -Nru a/arch/ppc/kernel/ppc8260_pic.h b/arch/ppc/kernel/ppc8260_pic.h --- a/arch/ppc/kernel/ppc8260_pic.h Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/ppc8260_pic.h Tue Feb 19 18:08:59 2002 @@ -1,11 +1,11 @@ /* - * BK Id: SCCS/s.ppc8260_pic.h 1.7 05/17/01 18:14:21 cort + * BK Id: %F% %I% %G% %U% %#% */ #ifndef _PPC_KERNEL_PPC8260_H #define _PPC_KERNEL_PPC8260_H -#include "local_irq.h" +#include extern struct hw_interrupt_type ppc8260_pic; diff -Nru a/arch/ppc/kernel/ppc8xx_pic.c b/arch/ppc/kernel/ppc8xx_pic.c --- a/arch/ppc/kernel/ppc8xx_pic.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/ppc8xx_pic.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ppc8xx_pic.c 1.10 05/17/01 18:14:21 cort + * BK Id: %F% %I% %G% %U% %#% */ #include #include @@ -44,6 +44,21 @@ ppc_cached_irq_mask[word]; } +static void m8xx_end_irq(unsigned int irq_nr) +{ + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + int bit, word; + + bit = irq_nr & 0x1f; + word = irq_nr >> 5; + + ppc_cached_irq_mask[word] |= (1 << (31-bit)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = + ppc_cached_irq_mask[word]; + } +} + + static void m8xx_mask_and_ack(unsigned int irq_nr) { int bit, word; @@ -64,6 +79,7 @@ m8xx_unmask_irq, m8xx_mask_irq, m8xx_mask_and_ack, + m8xx_end_irq, 0 }; @@ -97,19 +113,26 @@ #endif +/* + * We either return a valid interrupt or -1 if there is nothing pending + */ int m8xx_get_irq(struct pt_regs *regs) { int irq; - unsigned long bits = 0; - /* For MPC8xx, read the SIVEC register and shift the bits down - * to get the irq number. */ - bits = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec; - irq = bits >> 26; -#if 0 - irq += ppc8xx_pic.irq_offset; -#endif + /* For MPC8xx, read the SIVEC register and shift the bits down + * to get the irq number. + */ + irq = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec >> 26; + + /* + * When we read the sivec without an interrupt to process, we will + * get back SIU_LEVEL7. In this case, return -1 + */ + if (irq == SIU_LEVEL7) + return -1; + return irq; } diff -Nru a/arch/ppc/kernel/ppc8xx_pic.h b/arch/ppc/kernel/ppc8xx_pic.h --- a/arch/ppc/kernel/ppc8xx_pic.h Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/ppc8xx_pic.h Tue Feb 19 18:08:59 2002 @@ -1,11 +1,11 @@ /* - * BK Id: SCCS/s.ppc8xx_pic.h 1.7 05/17/01 18:14:21 cort + * BK Id: %F% %I% %G% %U% %#% */ #ifndef _PPC_KERNEL_PPC8xx_H #define _PPC_KERNEL_PPC8xx_H #include -#include "local_irq.h" +#include extern struct hw_interrupt_type ppc8xx_pic; @@ -15,7 +15,7 @@ int m8xx_get_irq(struct pt_regs *regs); #ifdef CONFIG_MBX -#include "i8259.h" +#include #include void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs); #endif diff -Nru a/arch/ppc/kernel/ppc_asm.h b/arch/ppc/kernel/ppc_asm.h --- a/arch/ppc/kernel/ppc_asm.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,158 +0,0 @@ -/* - * BK Id: SCCS/s.ppc_asm.h 1.18 10/18/01 15:02:09 trini - */ -/* - * arch/ppc/kernel/ppc_asm.h - * - * Definitions used by various bits of low-level assembly code on PowerPC. - * - * Copyright (C) 1995-1999 Gary Thomas, Paul Mackerras, Cort Dougan. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include - -#include "ppc_asm.tmpl" -#include "ppc_defs.h" - -/* - * Macros for storing registers into and loading registers from - * exception frames. - */ -#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base) -#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) -#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) -#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) -#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) -#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base) -#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) -#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) -#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) -#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) - -#define SAVE_FPR(n, base) stfd n,THREAD_FPR0+8*(n)(base) -#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) -#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) -#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) -#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) -#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) -#define REST_FPR(n, base) lfd n,THREAD_FPR0+8*(n)(base) -#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) -#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) -#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) -#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) -#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) - -/* - * Once a version of gas that understands the AltiVec instructions - * is freely available, we can do this the normal way... - paulus - */ -#define LVX(r,a,b) .long (31<<26)+((r)<<21)+((a)<<16)+((b)<<11)+(103<<1) -#define STVX(r,a,b) .long (31<<26)+((r)<<21)+((a)<<16)+((b)<<11)+(231<<1) -#define MFVSCR(r) .long (4<<26)+((r)<<21)+(770<<1) -#define MTVSCR(r) .long (4<<26)+((r)<<11)+(802<<1) - -#define SAVE_VR(n,b,base) li b,THREAD_VR0+(16*(n)); STVX(n,b,base) -#define SAVE_2VR(n,b,base) SAVE_VR(n,b,base); SAVE_VR(n+1,b,base) -#define SAVE_4VR(n,b,base) SAVE_2VR(n,b,base); SAVE_2VR(n+2,b,base) -#define SAVE_8VR(n,b,base) SAVE_4VR(n,b,base); SAVE_4VR(n+4,b,base) -#define SAVE_16VR(n,b,base) SAVE_8VR(n,b,base); SAVE_8VR(n+8,b,base) -#define SAVE_32VR(n,b,base) SAVE_16VR(n,b,base); SAVE_16VR(n+16,b,base) -#define REST_VR(n,b,base) li b,THREAD_VR0+(16*(n)); LVX(n,b,base) -#define REST_2VR(n,b,base) REST_VR(n,b,base); REST_VR(n+1,b,base) -#define REST_4VR(n,b,base) REST_2VR(n,b,base); REST_2VR(n+2,b,base) -#define REST_8VR(n,b,base) REST_4VR(n,b,base); REST_4VR(n+4,b,base) -#define REST_16VR(n,b,base) REST_8VR(n,b,base); REST_8VR(n+8,b,base) -#define REST_32VR(n,b,base) REST_16VR(n,b,base); REST_16VR(n+16,b,base) - -#ifdef CONFIG_PPC601_SYNC_FIX -#define SYNC \ -BEGIN_FTR_SECTION \ - sync; \ - isync; \ -END_FTR_SECTION_IFSET(CPU_FTR_601) -#define SYNC_601 \ -BEGIN_FTR_SECTION \ - sync; \ -END_FTR_SECTION_IFSET(CPU_FTR_601) -#define ISYNC_601 \ -BEGIN_FTR_SECTION \ - isync; \ -END_FTR_SECTION_IFSET(CPU_FTR_601) -#else -#define SYNC -#define SYNC_601 -#define ISYNC_601 -#endif - -#ifndef CONFIG_SMP -#define TLBSYNC -#else /* CONFIG_SMP */ -/* tlbsync is not implemented on 601 */ -#define TLBSYNC \ -BEGIN_FTR_SECTION \ - tlbsync; \ - sync; \ -END_FTR_SECTION_IFCLR(CPU_FTR_601) -#endif - -/* - * This instruction is not implemented on the PPC 603 or 601; however, on - * the 403GCX and 405GP tlbia IS defined and tlbie is not. - * All of these instructions exist in the 8xx, they have magical powers, - * and they must be used. - */ - -#if !defined(CONFIG_4xx) && !defined(CONFIG_8xx) -#define tlbia \ - li r4,1024; \ - mtctr r4; \ - lis r4,KERNELBASE@h; \ -0: tlbie r4; \ - addi r4,r4,0x1000; \ - bdnz 0b -#endif - -/* - * On APUS (Amiga PowerPC cpu upgrade board), we don't know the - * physical base address of RAM at compile time. - */ -#define tophys(rd,rs) \ -0: addis rd,rs,-KERNELBASE@h; \ - .section ".vtop_fixup","aw"; \ - .align 1; \ - .long 0b; \ - .previous - -#define tovirt(rd,rs) \ -0: addis rd,rs,KERNELBASE@h; \ - .section ".ptov_fixup","aw"; \ - .align 1; \ - .long 0b; \ - .previous - -/* - * On 64-bit cpus, we use the rfid instruction instead of rfi, but - * we then have to make sure we preserve the top 32 bits except for - * the 64-bit mode bit, which we clear. - */ -#ifdef CONFIG_PPC64BRIDGE -#define FIX_SRR1(ra, rb) \ - mr rb,ra; \ - mfmsr ra; \ - clrldi ra,ra,1; /* turn off 64-bit mode */ \ - rldimi ra,rb,0,32 -#define RFI .long 0x4c000024 /* rfid instruction */ -#define MTMSRD(r) .long (0x7c000164 + ((r) << 21)) /* mtmsrd */ -#define CLR_TOP32(r) rlwinm (r),(r),0,0,31 /* clear top 32 bits */ - -#else -#define FIX_SRR1(ra, rb) -#define RFI rfi -#define MTMSRD(r) mtmsr r -#define CLR_TOP32(r) -#endif /* CONFIG_PPC64BRIDGE */ diff -Nru a/arch/ppc/kernel/ppc_asm.tmpl b/arch/ppc/kernel/ppc_asm.tmpl --- a/arch/ppc/kernel/ppc_asm.tmpl Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,115 +0,0 @@ -/* Condition Register Bit Fields */ - -#define cr0 0 -#define cr1 1 -#define cr2 2 -#define cr3 3 -#define cr4 4 -#define cr5 5 -#define cr6 6 -#define cr7 7 - - -/* General Purpose Registers (GPRs) */ - -#define r0 0 -#define r1 1 -#define r2 2 -#define r3 3 -#define r4 4 -#define r5 5 -#define r6 6 -#define r7 7 -#define r8 8 -#define r9 9 -#define r10 10 -#define r11 11 -#define r12 12 -#define r13 13 -#define r14 14 -#define r15 15 -#define r16 16 -#define r17 17 -#define r18 18 -#define r19 19 -#define r20 20 -#define r21 21 -#define r22 22 -#define r23 23 -#define r24 24 -#define r25 25 -#define r26 26 -#define r27 27 -#define r28 28 -#define r29 29 -#define r30 30 -#define r31 31 - - -/* Floating Point Registers (FPRs) */ - -#define fr0 0 -#define fr1 1 -#define fr2 2 -#define fr3 3 -#define fr4 4 -#define fr5 5 -#define fr6 6 -#define fr7 7 -#define fr8 8 -#define fr9 9 -#define fr10 10 -#define fr11 11 -#define fr12 12 -#define fr13 13 -#define fr14 14 -#define fr15 15 -#define fr16 16 -#define fr17 17 -#define fr18 18 -#define fr19 19 -#define fr20 20 -#define fr21 21 -#define fr22 22 -#define fr23 23 -#define fr24 24 -#define fr25 25 -#define fr26 26 -#define fr27 27 -#define fr28 28 -#define fr29 29 -#define fr30 30 -#define fr31 31 - -#define vr0 0 -#define vr1 1 -#define vr2 2 -#define vr3 3 -#define vr4 4 -#define vr5 5 -#define vr6 6 -#define vr7 7 -#define vr8 8 -#define vr9 9 -#define vr10 10 -#define vr11 11 -#define vr12 12 -#define vr13 13 -#define vr14 14 -#define vr15 15 -#define vr16 16 -#define vr17 17 -#define vr18 18 -#define vr19 19 -#define vr20 20 -#define vr21 21 -#define vr22 22 -#define vr23 23 -#define vr24 24 -#define vr25 25 -#define vr26 26 -#define vr27 27 -#define vr28 28 -#define vr29 29 -#define vr30 30 -#define vr31 31 diff -Nru a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c --- a/arch/ppc/kernel/ppc_ksyms.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/ppc_ksyms.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ppc_ksyms.c 1.59 11/04/01 22:58:20 paulus + * BK Id: %F% %I% %G% %U% %#% */ #include #include @@ -12,18 +12,17 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -36,7 +35,7 @@ #include #include #include -#include +#include #include #include #include @@ -60,7 +59,7 @@ extern void ppc_generic_ide_fix_driveid(struct hd_driveid *id); extern void transfer_to_handler(void); -extern void syscall_trace(void); +extern void do_syscall_trace(void); extern void do_IRQ(struct pt_regs *regs); extern void MachineCheckException(struct pt_regs *regs); extern void AlignmentException(struct pt_regs *regs); @@ -82,7 +81,7 @@ EXPORT_SYMBOL(clear_page); EXPORT_SYMBOL(do_signal); -EXPORT_SYMBOL(syscall_trace); +EXPORT_SYMBOL(do_syscall_trace); EXPORT_SYMBOL(transfer_to_handler); EXPORT_SYMBOL(do_IRQ); EXPORT_SYMBOL(MachineCheckException); @@ -161,14 +160,18 @@ EXPORT_SYMBOL(_outsw_ns); EXPORT_SYMBOL(_insl_ns); EXPORT_SYMBOL(_outsl_ns); +EXPORT_SYMBOL(iopa); +EXPORT_SYMBOL(mm_ptov); +#ifndef CONFIG_PPC_ISERIES EXPORT_SYMBOL(ioremap); EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); -EXPORT_SYMBOL(iopa); -EXPORT_SYMBOL(mm_ptov); +#endif +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) EXPORT_SYMBOL(ppc_ide_md); EXPORT_SYMBOL(ppc_generic_ide_fix_driveid); +#endif #ifdef CONFIG_PCI EXPORT_SYMBOL_NOVERS(isa_io_base); @@ -176,19 +179,32 @@ EXPORT_SYMBOL_NOVERS(pci_dram_offset); EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); +EXPORT_SYMBOL(pci_bus_io_base); +EXPORT_SYMBOL(pci_bus_io_base_phys); +EXPORT_SYMBOL(pci_bus_mem_base_phys); +EXPORT_SYMBOL(pci_bus_to_hose); +EXPORT_SYMBOL(pci_resource_to_bus); +EXPORT_SYMBOL(pci_phys_to_bus); +EXPORT_SYMBOL(pci_bus_to_phys); #endif /* CONFIG_PCI */ +#ifdef CONFIG_NOT_COHERENT_CACHE +EXPORT_SYMBOL(consistent_alloc); +EXPORT_SYMBOL(consistent_free); +EXPORT_SYMBOL(consistent_sync); +#endif + EXPORT_SYMBOL(start_thread); EXPORT_SYMBOL(kernel_thread); -/*EXPORT_SYMBOL(__restore_flags);*/ -/*EXPORT_SYMBOL(_disable_interrupts); - EXPORT_SYMBOL(_enable_interrupts);*/ EXPORT_SYMBOL(flush_instruction_cache); EXPORT_SYMBOL(giveup_fpu); EXPORT_SYMBOL(enable_kernel_fp); EXPORT_SYMBOL(flush_icache_range); EXPORT_SYMBOL(flush_dcache_range); +EXPORT_SYMBOL(flush_icache_user_range); +EXPORT_SYMBOL(flush_icache_page); +EXPORT_SYMBOL(flush_dcache_page); EXPORT_SYMBOL(xchg_u32); #ifdef CONFIG_ALTIVEC EXPORT_SYMBOL(last_task_used_altivec); @@ -196,7 +212,6 @@ #endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_SMP EXPORT_SYMBOL(global_irq_lock); -EXPORT_SYMBOL(global_irq_count); EXPORT_SYMBOL(global_irq_holder); EXPORT_SYMBOL(__global_cli); EXPORT_SYMBOL(__global_sti); @@ -247,26 +262,11 @@ EXPORT_SYMBOL(machine_is_compatible); EXPORT_SYMBOL(find_all_nodes); EXPORT_SYMBOL(get_property); -EXPORT_SYMBOL(pci_bus_io_base); -EXPORT_SYMBOL(pci_bus_io_base_phys); -EXPORT_SYMBOL(pci_bus_mem_base_phys); +EXPORT_SYMBOL(request_OF_resource); +EXPORT_SYMBOL(release_OF_resource); EXPORT_SYMBOL(pci_device_to_OF_node); EXPORT_SYMBOL(pci_device_from_OF_node); -EXPORT_SYMBOL(pci_bus_to_hose); -EXPORT_SYMBOL(pci_resource_to_bus); -EXPORT_SYMBOL(pci_phys_to_bus); -EXPORT_SYMBOL(pci_bus_to_phys); EXPORT_SYMBOL(pmac_newworld); -EXPORT_SYMBOL(feature_set); -EXPORT_SYMBOL(feature_clear); -EXPORT_SYMBOL(feature_test); -EXPORT_SYMBOL(feature_set_gmac_power); -EXPORT_SYMBOL(feature_gmac_phy_reset); -EXPORT_SYMBOL(feature_set_usb_power); -EXPORT_SYMBOL(feature_set_firewire_power); -EXPORT_SYMBOL(feature_set_firewire_cable_power); -EXPORT_SYMBOL(feature_set_modem_power); -EXPORT_SYMBOL(feature_set_airport_power); #endif /* defined(CONFIG_ALL_PPC) */ #if defined(CONFIG_BOOTX_TEXT) EXPORT_SYMBOL(btext_update_display); @@ -293,14 +293,16 @@ EXPORT_SYMBOL_NOVERS(memmove); EXPORT_SYMBOL_NOVERS(memscan); EXPORT_SYMBOL_NOVERS(memcmp); +EXPORT_SYMBOL_NOVERS(memchr); EXPORT_SYMBOL(abs); -#ifdef CONFIG_VGA_CONSOLE +#if defined(CONFIG_FB_VGA16_MODULE) EXPORT_SYMBOL(screen_info); #endif EXPORT_SYMBOL(__delay); +#ifndef INLINE_IRQS EXPORT_SYMBOL(__sti); EXPORT_SYMBOL(__sti_end); EXPORT_SYMBOL(__cli); @@ -309,6 +311,7 @@ EXPORT_SYMBOL(__save_flags_ptr_end); EXPORT_SYMBOL(__restore_flags); EXPORT_SYMBOL(__restore_flags_end); +#endif EXPORT_SYMBOL(timer_interrupt_intercept); EXPORT_SYMBOL(timer_interrupt); EXPORT_SYMBOL(do_IRQ_intercept); @@ -319,7 +322,9 @@ EXPORT_SYMBOL(get_wchan); EXPORT_SYMBOL(console_drivers); #ifdef CONFIG_XMON +extern void xmon_printf(char *fmt, ...); EXPORT_SYMBOL(xmon); +EXPORT_SYMBOL(xmon_printf); #endif EXPORT_SYMBOL(__up); EXPORT_SYMBOL(__down); @@ -343,10 +348,12 @@ #ifdef CONFIG_8xx EXPORT_SYMBOL(__res); -EXPORT_SYMBOL(request_8xxirq); EXPORT_SYMBOL(cpm_install_handler); EXPORT_SYMBOL(cpm_free_handler); #endif /* CONFIG_8xx */ +#if defined(CONFIG_8xx) || defined(CONFIG_8260) +EXPORT_SYMBOL(request_8xxirq); +#endif EXPORT_SYMBOL(ret_to_user_hook); EXPORT_SYMBOL(next_mmu_context); @@ -361,3 +368,7 @@ extern long *ret_from_intercept; EXPORT_SYMBOL(ret_from_intercept); EXPORT_SYMBOL(cur_cpu_spec); +#if defined(CONFIG_ALL_PPC) +extern unsigned long agp_special_page; +EXPORT_SYMBOL_NOVERS(agp_special_page); +#endif /* defined(CONFIG_ALL_PPC) */ diff -Nru a/arch/ppc/kernel/pplus_common.c b/arch/ppc/kernel/pplus_common.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/pplus_common.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,317 @@ +/* + * arch/ppc/kernel/pplus_common.c + * + * Common Motorola PowerPlus Platform--really Falcon/Raven or HAWK. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * The Falcon/Raven and HAWK has 4 sets of registers: + * 1) PPC Registers which define the mappings from PPC bus to PCI bus, + * etc. + * 2) PCI Registers which define the mappings from PCI bus to PPC bus and the + * MPIC base address. + * 3) MPIC registers. + * 4) System Memory Controller (SMC) registers. + */ + +/* + * Initialize the Motorola MCG Raven or HAWK host bridge. + * + * This means setting up the PPC bus to PCI memory and I/O space mappings, + * setting the PCI memory space address of the MPIC (mapped straight + * through), and ioremap'ing the mpic registers. + * This routine will set the PCI_CONFIG_ADDR or PCI_CONFIG_DATA + * addresses based on the PCI I/O address that is passed in. + * 'OpenPIC_Addr' will be set correctly by this routine. + */ +int __init +pplus_init(struct pci_controller *hose, + uint ppc_reg_base, + ulong processor_pci_mem_start, + ulong processor_pci_mem_end, + ulong processor_pci_io_start, + ulong processor_pci_io_end, + ulong processor_mpic_base) +{ + uint addr, offset; + + /* + * Some sanity checks... + */ + if (((processor_pci_mem_start&0xffff0000) != processor_pci_mem_start) || + ((processor_pci_io_start &0xffff0000) != processor_pci_io_start)) { + printk("pplus_init: %s\n", + "PPC to PCI mappings must start on 64 KB boundaries"); + return -1; + } + + if (((processor_pci_mem_end &0x0000ffff) != 0x0000ffff) || + ((processor_pci_io_end &0x0000ffff) != 0x0000ffff)) { + printk("pplus_init: PPC to PCI mappings %s\n", + "must end just before a 64 KB boundaries"); + return -1; + } + + if (((processor_pci_mem_end - processor_pci_mem_start) != + (hose->mem_space.end - hose->mem_space.start)) || + ((processor_pci_io_end - processor_pci_io_start) != + (hose->io_space.end - hose->io_space.start))) { + printk("pplus_init: %s\n", + "PPC and PCI memory or I/O space sizes don't match"); + return -1; + } + + if ((processor_mpic_base & 0xfffc0000) != processor_mpic_base) { + printk("pplus_init: %s\n", + "MPIC address must start on 256 MB boundary"); + return -1; + } + + if ((pci_dram_offset & 0xffff0000) != pci_dram_offset) { + printk("pplus_init: %s\n", + "pci_dram_offset must be multiple of 64 KB"); + return -1; + } + + /* + * Disable previous PPC->PCI mappings. + */ + out_be32((uint *)(ppc_reg_base + PPLUS_PPC_XSOFF0_OFF), 0x00000000); + out_be32((uint *)(ppc_reg_base + PPLUS_PPC_XSOFF1_OFF), 0x00000000); + out_be32((uint *)(ppc_reg_base + PPLUS_PPC_XSOFF2_OFF), 0x00000000); + out_be32((uint *)(ppc_reg_base + PPLUS_PPC_XSOFF3_OFF), 0x00000000); + + /* + * Program the XSADD/XSOFF registers to set up the PCI Mem & I/O + * space mappings. These are the mappings going from the processor to + * the PCI bus. + * + * Note: Don't need to 'AND' start/end addresses with 0xffff0000 + * because sanity check above ensures that they are properly + * aligned. + */ + + /* Set up PPC->PCI Mem mapping */ + addr = processor_pci_mem_start | (processor_pci_mem_end >> 16); + offset = (hose->mem_space.start - processor_pci_mem_start) | 0xd2; + out_be32((uint *)(ppc_reg_base + PPLUS_PPC_XSADD0_OFF), addr); + out_be32((uint *)(ppc_reg_base + PPLUS_PPC_XSOFF0_OFF), offset); + + /* Set up PPC->MPIC mapping on the bridge */ + addr = processor_mpic_base | + (((processor_mpic_base + PPLUS_MPIC_SIZE) >> 16) - 1); + offset = 0xc2; /* No write posting for this PCI Mem space */ + out_be32((uint *)(ppc_reg_base + PPLUS_PPC_XSADD1_OFF), addr); + out_be32((uint *)(ppc_reg_base + PPLUS_PPC_XSOFF1_OFF), offset); + + /* Set up PPC->PCI I/O mapping -- Contiguous I/O space */ + addr = processor_pci_io_start | (processor_pci_io_end >> 16); + offset = (hose->io_space.start - processor_pci_io_start) | 0xc0; + out_be32((uint *)(ppc_reg_base + PPLUS_PPC_XSADD3_OFF), addr); + out_be32((uint *)(ppc_reg_base + PPLUS_PPC_XSOFF3_OFF), offset); + + hose->io_base_virt = (void *)ioremap(processor_pci_io_start, + (processor_pci_io_end - processor_pci_io_start + 1)); + + /* + * Set up the indirect method of accessing PCI config space. + * The PCI config addr/data pair based on start addr of PCI I/O space. + */ + setup_indirect_pci(hose, + processor_pci_io_start + PPLUS_PCI_CONFIG_ADDR_OFF, + processor_pci_io_start + PPLUS_PCI_CONFIG_DATA_OFF); + + /* + * Disable previous PCI->PPC mappings. + */ + + /* XXXX Put in mappings from PCI bus to processor bus XXXX */ + + /* + * Disable MPIC response to PCI I/O space (BAR 0). + * Make MPIC respond to PCI Mem space at specified address. + * (BAR 1). + */ + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_BASE_ADDRESS_0, + 0x00000000 | 0x1); + + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_BASE_ADDRESS_1, + processor_mpic_base | 0x0); + + /* Map MPIC into vitual memory */ + OpenPIC_Addr = ioremap(processor_mpic_base, PPLUS_MPIC_SIZE); + + return 0; +} + +/* + * Find the amount of RAM present. + * This assumes that PPCBug has initialized the memory controller (SMC) + * on the Falcon/HAWK correctly (i.e., it does no sanity checking). + * It also assumes that the memory base registers are set to configure the + * memory as contigous starting with "RAM A BASE", "RAM B BASE", etc. + * however, RAM base registers can be skipped (e.g. A, B, C are set, + * D is skipped but E is set is okay). + */ +#define MB (1024*1024) + +static uint reg_offset_table[] __initdata = { + PPLUS_SMC_RAM_A_SIZE_REG_OFF, + PPLUS_SMC_RAM_B_SIZE_REG_OFF, + PPLUS_SMC_RAM_C_SIZE_REG_OFF, + PPLUS_SMC_RAM_D_SIZE_REG_OFF, + PPLUS_SMC_RAM_E_SIZE_REG_OFF, + PPLUS_SMC_RAM_F_SIZE_REG_OFF, + PPLUS_SMC_RAM_G_SIZE_REG_OFF, + PPLUS_SMC_RAM_H_SIZE_REG_OFF +}; + +static uint falcon_size_table[] __initdata = { + 0 * MB, /* 0 ==> 0 MB */ + 16 * MB, /* 1 ==> 16 MB */ + 32 * MB, /* 2 ==> 32 MB */ + 64 * MB, /* 3 ==> 64 MB */ + 128 * MB, /* 4 ==> 128 MB */ + 256 * MB, /* 5 ==> 256 MB */ + 1024 * MB, /* 6 ==> 1024 MB (1 GB) */ +}; + +static uint hawk_size_table[] __initdata = { + 0 * MB, /* 0 ==> 0 MB */ + 32 * MB, /* 1 ==> 32 MB */ + 64 * MB, /* 2 ==> 64 MB */ + 64 * MB, /* 3 ==> 64 MB */ + 128 * MB, /* 4 ==> 128 MB */ + 128 * MB, /* 5 ==> 128 MB */ + 128 * MB, /* 6 ==> 128 MB */ + 256 * MB, /* 7 ==> 256 MB */ + 256 * MB, /* 8 ==> 256 MB */ + 512 * MB, /* 9 ==> 512 MB */ +}; + +/* + * *** WARNING: You MUST have a BAT set up to map in the SMC regs *** + * + * Read the memory controller's registers to determine the amount of system + * memory. Assumes that the memory controller registers are already mapped + * into virtual memory--too early to use ioremap(). + */ +unsigned long __init +pplus_get_mem_size(uint smc_base) +{ + unsigned long total; + int i, size_table_entries, reg_limit; + uint vend_dev_id; + uint *size_table; + u_char val; + + + vend_dev_id = in_be32((uint *)smc_base + PCI_VENDOR_ID); + + if (((vend_dev_id & 0xffff0000) >> 16) != PCI_VENDOR_ID_MOTOROLA) { + printk("pplus_get_mem_size: %s (0x%x)\n", + "Not a Motorola Memory Controller", vend_dev_id); + return 0; + } + + vend_dev_id &= 0x0000ffff; + + if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_FALCON) { + size_table = falcon_size_table; + size_table_entries = sizeof(falcon_size_table) / + sizeof(falcon_size_table[0]); + + reg_limit = PPLUS_FALCON_SMC_REG_COUNT; + } + else if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_HAWK) { + size_table = hawk_size_table; + size_table_entries = sizeof(hawk_size_table) / + sizeof(hawk_size_table[0]); + reg_limit = PPLUS_HAWK_SMC_REG_COUNT; + } + else { + printk("pplus_get_mem_size: %s (0x%x)\n", + "Not a Falcon or HAWK", vend_dev_id); + return 0; + } + + total = 0; + + /* Check every reg because PPCBug may skip some */ + for (i=0; i -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static char nvramData[MAX_PREP_NVRAM]; -static NVRAM_MAP *nvram=(NVRAM_MAP *)&nvramData[0]; - -unsigned char __prep prep_nvram_read_val(int addr) -{ - outb(addr, PREP_NVRAM_AS0); - outb(addr>>8, PREP_NVRAM_AS1); - return inb(PREP_NVRAM_DATA); -} - -void __prep prep_nvram_write_val(int addr, - unsigned char val) -{ - outb(addr, PREP_NVRAM_AS0); - outb(addr>>8, PREP_NVRAM_AS1); - outb(val, PREP_NVRAM_DATA); -} - -void __init init_prep_nvram(void) -{ - unsigned char *nvp; - int i; - int nvramSize; - - /* - * The following could fail if the NvRAM were corrupt but - * we expect the boot firmware to have checked its checksum - * before boot - */ - nvp = (char *) &nvram->Header; - for (i=0; iHeader.GEAddress+nvram->Header.GELength; - if(nvramSize>MAX_PREP_NVRAM) - { - /* - * NvRAM is too large - */ - nvram->Header.GELength=0; - return; - } - - /* - * Read the remainder of the PReP NvRAM - */ - nvp = (char *) &nvram->GEArea[0]; - for (i=sizeof(HEADER); iGEArea)) < nvram->Header.GELength) - && (*cp == '\0')) - { - cp++; - } - - if ((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) { - return cp; - } else { - return NULL; - } -} - - - diff -Nru a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c --- a/arch/ppc/kernel/prep_pci.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1249 +0,0 @@ -/* - * BK Id: SCCS/s.prep_pci.c 1.31 10/05/01 17:48:18 trini - */ -/* - * PReP pci functions. - * Originally by Gary Thomas - * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) - * - * The motherboard routes/maps will disappear shortly. -- Cort - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pci.h" -#include "open_pic.h" - -#define MAX_DEVNR 22 - -/* Which PCI interrupt line does a given device [slot] use? */ -/* Note: This really should be two dimensional based in slot/pin used */ -static unsigned char *Motherboard_map; -unsigned char *Motherboard_map_name; - -/* How is the 82378 PIRQ mapping setup? */ -static unsigned char *Motherboard_routes; - -static void (*Motherboard_non0)(struct pci_dev *); - -static void Powerplus_Map_Non0(struct pci_dev *); - -/* Used for Motorola to store system config register */ -static unsigned long *ProcInfo; - -/* Tables for known hardware */ - -/* Motorola PowerStackII - Utah */ -static char Utah_pci_IRQ_map[23] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 5, /* Slot 2 - SCSI - NCR825A */ - 0, /* Slot 3 - unused */ - 1, /* Slot 4 - Ethernet - DEC2114x */ - 0, /* Slot 5 - unused */ - 3, /* Slot 6 - PCI Card slot #1 */ - 4, /* Slot 7 - PCI Card slot #2 */ - 5, /* Slot 8 - PCI Card slot #3 */ - 5, /* Slot 9 - PCI Bridge */ - /* added here in case we ever support PCI bridges */ - /* Secondary PCI bus cards are at slot-9,6 & slot-9,7 */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 5, /* Slot 12 - SCSI - NCR825A */ - 0, /* Slot 13 - unused */ - 3, /* Slot 14 - enet */ - 0, /* Slot 15 - unused */ - 2, /* Slot 16 - unused */ - 3, /* Slot 17 - unused */ - 5, /* Slot 18 - unused */ - 0, /* Slot 19 - unused */ - 0, /* Slot 20 - unused */ - 0, /* Slot 21 - unused */ - 0, /* Slot 22 - unused */ -}; - -static char Utah_pci_IRQ_routes[] __prepdata = -{ - 0, /* Line 0 - Unused */ - 9, /* Line 1 */ - 10, /* Line 2 */ - 11, /* Line 3 */ - 14, /* Line 4 */ - 15, /* Line 5 */ -}; - -/* Motorola PowerStackII - Omaha */ -/* no integrated SCSI or ethernet */ -static char Omaha_pci_IRQ_map[23] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 3, /* Slot 2 - Winbond EIDE */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 1, /* Slot 6 - PCI slot 1 */ - 2, /* Slot 7 - PCI slot 2 */ - 3, /* Slot 8 - PCI slot 3 */ - 4, /* Slot 9 - PCI slot 4 */ /* needs indirect access */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 0, /* Slot 12 - unused */ - 0, /* Slot 13 - unused */ - 0, /* Slot 14 - unused */ - 0, /* Slot 15 - unused */ - 1, /* Slot 16 - PCI slot 1 */ - 2, /* Slot 17 - PCI slot 2 */ - 3, /* Slot 18 - PCI slot 3 */ - 4, /* Slot 19 - PCI slot 4 */ /* needs indirect access */ - 0, - 0, - 0, -}; - -static char Omaha_pci_IRQ_routes[] __prepdata = -{ - 0, /* Line 0 - Unused */ - 9, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* Motorola PowerStack */ -static char Blackhawk_pci_IRQ_map[19] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 1, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ - 1, /* Slot P7 */ - 2, /* Slot P6 */ - 3, /* Slot P5 */ -}; - -static char Blackhawk_pci_IRQ_routes[] __prepdata = -{ - 0, /* Line 0 - Unused */ - 9, /* Line 1 */ - 11, /* Line 2 */ - 15, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* Motorola Mesquite */ -static char Mesquite_pci_IRQ_map[23] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 0, /* Slot 12 - unused */ - 0, /* Slot 13 - unused */ - 2, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ - 3, /* Slot 16 - PMC */ - 0, /* Slot 17 - unused */ - 0, /* Slot 18 - unused */ - 0, /* Slot 19 - unused */ - 0, /* Slot 20 - unused */ - 0, /* Slot 21 - unused */ - 0, /* Slot 22 - unused */ -}; - -/* Motorola Sitka */ -static char Sitka_pci_IRQ_map[21] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 0, /* Slot 12 - unused */ - 0, /* Slot 13 - unused */ - 2, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ - 9, /* Slot 16 - PMC 1 */ - 12, /* Slot 17 - PMC 2 */ - 0, /* Slot 18 - unused */ - 0, /* Slot 19 - unused */ - 4, /* Slot 20 - NT P2P bridge */ -}; - -/* Motorola MTX */ -static char MTX_pci_IRQ_map[23] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 2, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ - 9, /* Slot 16 - PCI/PMC slot 1 */ - 10, /* Slot 17 - PCI/PMC slot 2 */ - 11, /* Slot 18 - PCI slot 3 */ - 0, /* Slot 19 - unused */ - 0, /* Slot 20 - unused */ - 0, /* Slot 21 - unused */ - 0, /* Slot 22 - unused */ -}; - -/* Motorola MTX Plus */ -/* Secondary bus interrupt routing is not supported yet */ -static char MTXplus_pci_IRQ_map[23] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 2, /* Slot 14 - Ethernet 1 */ - 0, /* Slot 15 - unused */ - 9, /* Slot 16 - PCI slot 1P */ - 10, /* Slot 17 - PCI slot 2P */ - 11, /* Slot 18 - PCI slot 3P */ - 10, /* Slot 19 - Ethernet 2 */ - 0, /* Slot 20 - P2P Bridge */ - 0, /* Slot 21 - unused */ - 0, /* Slot 22 - unused */ -}; - -static char Raven_pci_IRQ_routes[] __prepdata = -{ - 0, /* This is a dummy structure */ -}; - -/* Motorola MVME16xx */ -static char Genesis_pci_IRQ_map[16] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 1, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ -}; - -static char Genesis_pci_IRQ_routes[] __prepdata = -{ - 0, /* Line 0 - Unused */ - 10, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -static char Genesis2_pci_IRQ_map[23] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - Ethernet */ - 0, /* Slot 11 - Universe PCI - VME Bridge */ - 3, /* Slot 12 - unused */ - 0, /* Slot 13 - unused */ - 2, /* Slot 14 - SCSI */ - 0, /* Slot 15 - unused */ - 9, /* Slot 16 - PMC 1 */ - 12, /* Slot 17 - pci */ - 11, /* Slot 18 - pci */ - 10, /* Slot 19 - pci */ - 0, /* Slot 20 - pci */ - 0, /* Slot 21 - unused */ - 0, /* Slot 22 - unused */ -}; - -/* Motorola Series-E */ -static char Comet_pci_IRQ_map[23] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 1, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ - 1, /* Slot 16 - PCI slot 1 */ - 2, /* Slot 17 - PCI slot 2 */ - 3, /* Slot 18 - PCI slot 3 */ - 4, /* Slot 19 - PCI bridge */ - 0, - 0, - 0, -}; - -static char Comet_pci_IRQ_routes[] __prepdata = -{ - 0, /* Line 0 - Unused */ - 10, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* Motorola Series-EX */ -static char Comet2_pci_IRQ_map[23] __prepdata = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 3, /* Slot 2 - SCSI - NCR825A */ - 0, /* Slot 3 - unused */ - 1, /* Slot 4 - Ethernet - DEC2104X */ - 0, /* Slot 5 - unused */ - 1, /* Slot 6 - PCI slot 1 */ - 2, /* Slot 7 - PCI slot 2 */ - 3, /* Slot 8 - PCI slot 3 */ - 4, /* Slot 9 - PCI bridge */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI - NCR825A */ - 0, /* Slot 13 - unused */ - 1, /* Slot 14 - Ethernet - DEC2104X */ - 0, /* Slot 15 - unused */ - 1, /* Slot 16 - PCI slot 1 */ - 2, /* Slot 17 - PCI slot 2 */ - 3, /* Slot 18 - PCI slot 3 */ - 4, /* Slot 19 - PCI bridge */ - 0, - 0, - 0, -}; - -static char Comet2_pci_IRQ_routes[] __prepdata = -{ - 0, /* Line 0 - Unused */ - 10, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15, /* Line 4 */ -}; - -/* - * ibm 830 (and 850?). - * This is actually based on the Carolina motherboard - * -- Cort - */ -static char ibm8xx_pci_IRQ_map[23] __prepdata = { - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - FireCoral */ - 4, /* Slot 12 - Ethernet PCIINTD# */ - 2, /* Slot 13 - PCI Slot #2 */ - 2, /* Slot 14 - S3 Video PCIINTD# */ - 0, /* Slot 15 - onboard SCSI (INDI) [1] */ - 3, /* Slot 16 - NCR58C810 RS6000 Only PCIINTC# */ - 0, /* Slot 17 - unused */ - 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ - 0, /* Slot 19 - unused */ - 0, /* Slot 20 - unused */ - 0, /* Slot 21 - unused */ - 2, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ -}; - -static char ibm8xx_pci_IRQ_routes[] __prepdata = { - 0, /* Line 0 - unused */ - 15, /* Line 1 */ - 15, /* Line 2 */ - 15, /* Line 3 */ - 15, /* Line 4 */ -}; - -/* - * a 6015 ibm board - * -- Cort - */ -static char ibm6015_pci_IRQ_map[23] __prepdata = { - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - */ - 1, /* Slot 12 - SCSI */ - 2, /* Slot 13 - */ - 2, /* Slot 14 - */ - 1, /* Slot 15 - */ - 1, /* Slot 16 - */ - 0, /* Slot 17 - */ - 2, /* Slot 18 - */ - 0, /* Slot 19 - */ - 0, /* Slot 20 - */ - 0, /* Slot 21 - */ - 2, /* Slot 22 - */ -}; - -static char ibm6015_pci_IRQ_routes[] __prepdata = { - 0, /* Line 0 - unused */ - 13, /* Line 1 */ - 15, /* Line 2 */ - 15, /* Line 3 */ - 15, /* Line 4 */ -}; - - -/* IBM Nobis and Thinkpad 850 */ -static char Nobis_pci_IRQ_map[23] __prepdata ={ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 0, /* Slot 14 - unused */ - 0, /* Slot 15 - unused */ -}; - -static char Nobis_pci_IRQ_routes[] __prepdata = { - 0, /* Line 0 - Unused */ - 13, /* Line 1 */ - 13, /* Line 2 */ - 13, /* Line 3 */ - 13 /* Line 4 */ -}; - -/* - * IBM RS/6000 43p/140 -- paulus - * XXX we should get all this from the residual data - */ -static char ibm43p_pci_IRQ_map[23] __prepdata = { - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - FireCoral ISA bridge */ - 6, /* Slot 12 - Ethernet */ - 0, /* Slot 13 - openpic */ - 0, /* Slot 14 - unused */ - 0, /* Slot 15 - unused */ - 7, /* Slot 16 - NCR58C825a onboard scsi */ - 0, /* Slot 17 - unused */ - 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ - 0, /* Slot 19 - unused */ - 0, /* Slot 20 - unused */ - 0, /* Slot 21 - unused */ - 1, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ -}; - -static char ibm43p_pci_IRQ_routes[] __prepdata = { - 0, /* Line 0 - unused */ - 15, /* Line 1 */ - 15, /* Line 2 */ - 15, /* Line 3 */ - 15, /* Line 4 */ -}; - -/* Motorola PowerPlus architecture PCI IRQ tables */ -/* Interrupt line values for INTA-D on primary/secondary MPIC inputs */ - -struct powerplus_irq_list -{ - unsigned char primary[4]; /* INT A-D */ - unsigned char secondary[4]; /* INT A-D */ -}; - -/* - * For standard PowerPlus boards, bus 0 PCI INTs A-D are routed to - * OpenPIC inputs 9-12. PCI INTs A-D from the on board P2P bridge - * are routed to OpenPIC inputs 5-8. These values are offset by - * 16 in the table to reflect the Linux kernel interrupt value. - */ -struct powerplus_irq_list Powerplus_pci_IRQ_list __prepdata = -{ - {25, 26, 27, 28}, - {21, 22, 23, 24} -}; - -/* - * For the MCP750 (system slot board), cPCI INTs A-D are routed to - * OpenPIC inputs 8-11 and the PMC INTs A-D are routed to OpenPIC - * input 3. On a hot swap MCP750, the companion card PCI INTs A-D - * are routed to OpenPIC inputs 12-15. These values are offset by - * 16 in the table to reflect the Linux kernel interrupt value. - */ -struct powerplus_irq_list Mesquite_pci_IRQ_list __prepdata = -{ - {24, 25, 26, 27}, - {28, 29, 30, 31} -}; - -/* - * This table represents the standard PCI swizzle defined in the - * PCI bus specification. - */ -static unsigned char prep_pci_intpins[4][4] __prepdata = -{ - { 1, 2, 3, 4}, /* Buses 0, 4, 8, ... */ - { 2, 3, 4, 1}, /* Buses 1, 5, 9, ... */ - { 3, 4, 1, 2}, /* Buses 2, 6, 10 ... */ - { 4, 1, 2, 3}, /* Buses 3, 7, 11 ... */ -}; - -/* We have to turn on LEVEL mode for changed IRQ's */ -/* All PCI IRQ's need to be level mode, so this should be something - * other than hard-coded as well... IRQ's are individually mappable - * to either edge or level. - */ - -/* - * 8259 edge/level control definitions - */ -#define ISA8259_M_ELCR 0x4d0 -#define ISA8259_S_ELCR 0x4d1 - -#define ELCRS_INT15_LVL 0x80 -#define ELCRS_INT14_LVL 0x40 -#define ELCRS_INT12_LVL 0x10 -#define ELCRS_INT11_LVL 0x08 -#define ELCRS_INT10_LVL 0x04 -#define ELCRS_INT9_LVL 0x02 -#define ELCRS_INT8_LVL 0x01 -#define ELCRM_INT7_LVL 0x80 -#define ELCRM_INT5_LVL 0x20 - -#define CFGPTR(dev) (0x80800000 | (1<<(dev>>3)) | ((dev&7)<<8) | offset) -#define DEVNO(dev) (dev>>3) - -#define cfg_read(val, addr, type, op) *val = op((type)(addr)) -#define cfg_write(val, addr, type, op) op((type *)(addr), (val)) - -#define cfg_read_bad(val, size) *val = bad_##size; -#define cfg_write_bad(val, size) - -#define bad_byte 0xff -#define bad_word 0xffff -#define bad_dword 0xffffffffU - -#define PREP_PCI_OP(rw, size, type, op) \ -static int __prep \ -prep_##rw##_config_##size(struct pci_dev *dev, int offset, type val) \ -{ \ - if ((dev->bus->number != 0) || (DEVNO(dev->devfn) > MAX_DEVNR)) \ - { \ - cfg_##rw##_bad(val, size) \ - return PCIBIOS_DEVICE_NOT_FOUND; \ - } \ - cfg_##rw(val, CFGPTR(dev->devfn), type, op); \ - return PCIBIOS_SUCCESSFUL; \ -} - -PREP_PCI_OP(read, byte, u8 *, in_8) -PREP_PCI_OP(read, word, u16 *, in_le16) -PREP_PCI_OP(read, dword, u32 *, in_le32) -PREP_PCI_OP(write, byte, u8, out_8) -PREP_PCI_OP(write, word, u16, out_le16) -PREP_PCI_OP(write, dword, u32, out_le32) - -static struct pci_ops prep_pci_ops = -{ - prep_read_config_byte, - prep_read_config_word, - prep_read_config_dword, - prep_write_config_byte, - prep_write_config_word, - prep_write_config_dword -}; - -#define MOTOROLA_CPUTYPE_REG 0x800 -#define MOTOROLA_BASETYPE_REG 0x803 -#define MPIC_RAVEN_ID 0x48010000 -#define MPIC_HAWK_ID 0x48030000 -#define MOT_PROC2_BIT 0x800 - -static u_char mvme2600_openpic_initsenses[] __initdata = { - 1, /* MVME2600_INT_SIO */ - 0, /* MVME2600_INT_FALCN_ECC_ERR */ - 1, /* MVME2600_INT_PCI_ETHERNET */ - 1, /* MVME2600_INT_PCI_SCSI */ - 1, /* MVME2600_INT_PCI_GRAPHICS */ - 1, /* MVME2600_INT_PCI_VME0 */ - 1, /* MVME2600_INT_PCI_VME1 */ - 1, /* MVME2600_INT_PCI_VME2 */ - 1, /* MVME2600_INT_PCI_VME3 */ - 1, /* MVME2600_INT_PCI_INTA */ - 1, /* MVME2600_INT_PCI_INTB */ - 1, /* MVME2600_INT_PCI_INTC */ - 1, /* MVME2600_INT_PCI_INTD */ - 1, /* MVME2600_INT_LM_SIG0 */ - 1, /* MVME2600_INT_LM_SIG1 */ -}; - -#define MOT_RAVEN_PRESENT 0x1 -#define MOT_HAWK_PRESENT 0x2 - -int mot_entry = -1; -int prep_keybd_present = 1; -int MotMPIC; -int mot_multi; - -int __init -raven_init(void) -{ - unsigned int devid; - unsigned int pci_membase; - unsigned char base_mod; - - /* Check to see if the Raven chip exists. */ - if ( _prep_type != _PREP_Motorola) { - OpenPIC_Addr = NULL; - return 0; - } - - /* Check to see if this board is a type that might have a Raven. */ - if ((inb(MOTOROLA_CPUTYPE_REG) & 0xF0) != 0xE0) { - OpenPIC_Addr = NULL; - return 0; - } - - /* Check the first PCI device to see if it is a Raven. */ - early_read_config_dword(0, 0, 0, PCI_VENDOR_ID, &devid); - - switch (devid & 0xffff0000) { - case MPIC_RAVEN_ID: - MotMPIC = MOT_RAVEN_PRESENT; - break; - case MPIC_HAWK_ID: - MotMPIC = MOT_HAWK_PRESENT; - break; - default: - OpenPIC_Addr = NULL; - return 0; - } - - - /* Read the memory base register. */ - early_read_config_dword(0, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase); - - if (pci_membase == 0) { - OpenPIC_Addr = NULL; - return 0; - } - - /* Map the Raven MPIC registers to virtual memory. */ - OpenPIC_Addr = ioremap(pci_membase+0xC0000000, 0x22000); - - OpenPIC_InitSenses = mvme2600_openpic_initsenses; - OpenPIC_NumInitSenses = sizeof(mvme2600_openpic_initsenses); - - ppc_md.get_irq = openpic_get_irq; - - /* If raven is present on Motorola store the system config register - * for later use. - */ - ProcInfo = (unsigned long *)ioremap(0xfef80400, 4); - - /* Indicate to system if this is a multiprocessor board */ - if (!(*ProcInfo & MOT_PROC2_BIT)) { - mot_multi = 1; - } - - /* This is a hack. If this is a 2300 or 2400 mot board then there is - * no keyboard controller and we have to indicate that. - */ - base_mod = inb(MOTOROLA_BASETYPE_REG); - if ((MotMPIC == MOT_HAWK_PRESENT) || (base_mod == 0xF9) || - (base_mod == 0xFA) || (base_mod == 0xE1)) - prep_keybd_present = 0; - - return 1; -} - -struct mot_info { - int cpu_type; /* 0x100 mask assumes for Raven and Hawk boards that the level/edge are set */ - /* 0x200 if this board has a Hawk chip. */ - int base_type; - int max_cpu; /* ored with 0x80 if this board should be checked for multi CPU */ - const char *name; - unsigned char *map; - unsigned char *routes; - void (*map_non0_bus)(struct pci_dev *); /* For boards with more than bus 0 devices. */ - struct powerplus_irq_list *pci_irq_list; /* List of PCI MPIC inputs */ - unsigned char secondary_bridge_devfn; /* devfn of secondary bus transparent bridge */ -} mot_info[] __prepdata = { - {0x300, 0x00, 0x00, "MVME 2400", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, - {0x010, 0x00, 0x00, "Genesis", Genesis_pci_IRQ_map, Genesis_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, - {0x020, 0x00, 0x00, "Powerstack (Series E)", Comet_pci_IRQ_map, Comet_pci_IRQ_routes, NULL, NULL, 0x00}, - {0x040, 0x00, 0x00, "Blackhawk (Powerstack)", Blackhawk_pci_IRQ_map, Blackhawk_pci_IRQ_routes, NULL, NULL, 0x00}, - {0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)", Omaha_pci_IRQ_map, Omaha_pci_IRQ_routes, NULL, NULL, 0x00}, - {0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)", Utah_pci_IRQ_map, Utah_pci_IRQ_routes, NULL, NULL, 0x00}, - {0x0A0, 0x00, 0x00, "Powerstack (Series EX)", Comet2_pci_IRQ_map, Comet2_pci_IRQ_routes, NULL, NULL, 0x00}, - {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Mesquite_pci_IRQ_list, 0xFF}, - {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", Sitka_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, - {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Mesquite_pci_IRQ_list, 0xC0}, - {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xA0}, - {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xA0}, - {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, - {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, - {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, - {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, - {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, - {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, - {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, - {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, - {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, - {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, - {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, - {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, - {0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, - {0x000, 0x00, 0x00, "", NULL, NULL, NULL, NULL, 0x00} -}; - -void __init -ibm_prep_init(void) -{ - u32 addr; -#ifdef CONFIG_PREP_RESIDUAL - PPC_DEVICE *mpic; -#endif - - if (inb(0x0852) == 0xd5) { - /* This is for the 43p-140 */ - early_read_config_dword(0, 0, PCI_DEVFN(13, 0), - PCI_BASE_ADDRESS_0, &addr); - if (addr != 0xffffffff - && !(addr & PCI_BASE_ADDRESS_SPACE_IO) - && (addr &= PCI_BASE_ADDRESS_MEM_MASK) != 0) { - addr += PREP_ISA_MEM_BASE; - OpenPIC_Addr = ioremap(addr, 0x40000); - ppc_md.get_irq = openpic_get_irq; - } - } - -#ifdef CONFIG_PREP_RESIDUAL - mpic = residual_find_device(-1, NULL, SystemPeripheral, - ProgrammableInterruptController, MPIC, 0); - if (mpic != NULL) - printk("mpic = %p\n", mpic); -#endif -} - -static void __init -ibm43p_pci_map_non0(struct pci_dev *dev) -{ - unsigned char intpin; - static unsigned char bridge_intrs[4] = { 3, 4, 5, 8 }; - - if (dev == NULL) - return; - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &intpin); - if (intpin < 1 || intpin > 4) - return; - intpin = (PCI_SLOT(dev->devfn) + intpin - 1) & 3; - dev->irq = openpic_to_irq(bridge_intrs[intpin]); - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); -} - -void __init -prep_route_pci_interrupts(void) -{ - unsigned char *ibc_pirq = (unsigned char *)0x80800860; - unsigned char *ibc_pcicon = (unsigned char *)0x80800840; - int i; - - if ( _prep_type == _PREP_Motorola) - { - unsigned short irq_mode; - unsigned char cpu_type; - unsigned char base_mod; - int entry; - - cpu_type = inb(MOTOROLA_CPUTYPE_REG) & 0xF0; - base_mod = inb(MOTOROLA_BASETYPE_REG); - - for (entry = 0; mot_info[entry].cpu_type != 0; entry++) { - if (mot_info[entry].cpu_type & 0x200) { /* Check for Hawk chip */ - if (!(MotMPIC & MOT_HAWK_PRESENT)) - continue; - } else { /* Check non hawk boards */ - if ((mot_info[entry].cpu_type & 0xff) != cpu_type) - continue; - - if (mot_info[entry].base_type == 0) { - mot_entry = entry; - break; - } - - if (mot_info[entry].base_type != base_mod) - continue; - } - - if (!(mot_info[entry].max_cpu & 0x80)) { - mot_entry = entry; - break; - } - - /* processor 1 not present and max processor zero indicated */ - if ((*ProcInfo & MOT_PROC2_BIT) && !(mot_info[entry].max_cpu & 0x7f)) { - mot_entry = entry; - break; - } - - /* processor 1 present and max processor zero indicated */ - if (!(*ProcInfo & MOT_PROC2_BIT) && (mot_info[entry].max_cpu & 0x7f)) { - mot_entry = entry; - break; - } - } - - if (mot_entry == -1) /* No particular cpu type found - assume Blackhawk */ - mot_entry = 3; - - Motherboard_map_name = (unsigned char *)mot_info[mot_entry].name; - Motherboard_map = mot_info[mot_entry].map; - Motherboard_routes = mot_info[mot_entry].routes; - Motherboard_non0 = mot_info[mot_entry].map_non0_bus; - - if (!(mot_info[entry].cpu_type & 0x100)) { - /* AJF adjust level/edge control according to routes */ - irq_mode = 0; - for (i = 1; i <= 4; i++) - irq_mode |= ( 1 << Motherboard_routes[i] ); - outb( irq_mode & 0xff, 0x4d0 ); - outb( (irq_mode >> 8) & 0xff, 0x4d1 ); - } - } else if ( _prep_type == _PREP_IBM ) { - unsigned char planar_id = inb(0x0852); - unsigned char irq_edge_mask_lo, irq_edge_mask_hi; - - printk("IBM ID: %08x\n", planar_id); - switch(planar_id) { - case 0xff: - Motherboard_map_name = "IBM Thinkpad 850/860"; - Motherboard_map = Nobis_pci_IRQ_map; - Motherboard_routes = Nobis_pci_IRQ_routes; - irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ - irq_edge_mask_hi = 0xA0; /* irq's 13, 15 level-triggered */ - break; - case 0xfc: - Motherboard_map_name = "IBM 6015/7020 (Sandalfoot/Sandalbow)"; - Motherboard_map = ibm6015_pci_IRQ_map; - Motherboard_routes = ibm6015_pci_IRQ_routes; - irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ - irq_edge_mask_hi = 0xA0; /* irq's 13, 15 level-triggered */ - break; - case 0xd5: - Motherboard_map_name = "IBM 43P-140 (Tiger1)"; - Motherboard_map = ibm43p_pci_IRQ_map; - Motherboard_routes = ibm43p_pci_IRQ_routes; - Motherboard_non0 = ibm43p_pci_map_non0; - irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ - irq_edge_mask_hi = 0xA0; /* irq's 13, 15 level-triggered */ - break; - default: - printk(KERN_ERR "Unknown IBM motherboard! Defaulting to Carolina.\n"); - case 0xf0: /* PowerSeries 830/850 */ - case 0xf1: /* PowerSeries 830/850 */ - case 0xf2: /* PowerSeries 830/850 */ - case 0xf4: /* 7248-43P */ - case 0xf5: /* 7248-43P */ - case 0xf6: /* 7248-43P */ - case 0xf7: /* 7248-43P (missing from Carolina Tech Spec) */ - Motherboard_map_name = "IBM PS830/PS850/7248 (Carolina)"; - Motherboard_map = ibm8xx_pci_IRQ_map; - Motherboard_routes = ibm8xx_pci_IRQ_routes; - irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ - irq_edge_mask_hi = 0xA4; /* irq's 10, 13, 15 level-triggered */ - break; - } - - outb(inb(0x04d0)|irq_edge_mask_lo, 0x04d0); /* primary 8259 */ - outb(inb(0x04d1)|irq_edge_mask_hi, 0x04d1); /* cascaded 8259 */ - } else { - printk("No known machine pci routing!\n"); - return; - } - - /* Set up mapping from slots */ - for (i = 1; i <= 4; i++) - ibc_pirq[i-1] = Motherboard_routes[i]; - /* Enable PCI interrupts */ - *ibc_pcicon |= 0x20; -} - -void __init -prep_pib_init(void) -{ - unsigned char reg; - unsigned short short_reg; - - struct pci_dev *dev = NULL; - - if (( _prep_type == _PREP_Motorola) && (OpenPIC_Addr)) { - /* - * Perform specific configuration for the Via Tech or - * or Winbond PCI-ISA-Bridge part. - */ - if ((dev = pci_find_device(PCI_VENDOR_ID_VIA, - PCI_DEVICE_ID_VIA_82C586_1, dev))) { - /* - * PPCBUG does not set the enable bits - * for the IDE device. Force them on here. - */ - pci_read_config_byte(dev, 0x40, ®); - - reg |= 0x03; /* IDE: Chip Enable Bits */ - pci_write_config_byte(dev, 0x40, reg); - } - if ((dev = pci_find_device(PCI_VENDOR_ID_VIA, - PCI_DEVICE_ID_VIA_82C586_2, - dev)) && (dev->devfn = 0x5a)) { - /* Force correct USB interrupt */ - dev->irq = 11; - pci_write_config_byte(dev, - PCI_INTERRUPT_LINE, - dev->irq); - } - if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND, - PCI_DEVICE_ID_WINBOND_83C553, dev))) { - /* Clear PCI Interrupt Routing Control Register. */ - short_reg = 0x0000; - pci_write_config_word(dev, 0x44, short_reg); - if (OpenPIC_Addr){ - /* Route IDE interrupts to IRQ 14 */ - reg = 0xEE; - pci_write_config_byte(dev, 0x43, reg); - } - } - } - - if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND, - PCI_DEVICE_ID_WINBOND_82C105, dev))){ - if (OpenPIC_Addr){ - /* - * Disable LEGIRQ mode so PCI INTS are routed - * directly to the 8259 and enable both channels - */ - pci_write_config_dword(dev, 0x40, 0x10ff0033); - - /* Force correct IDE interrupt */ - dev->irq = 14; - pci_write_config_byte(dev, - PCI_INTERRUPT_LINE, - dev->irq); - } else { - /* Enable LEGIRQ for PCI INT -> 8259 IRQ routing */ - pci_write_config_dword(dev, 0x40, 0x10ff08a1); - } - } -} - -static void __init -Powerplus_Map_Non0(struct pci_dev *dev) -{ - struct pci_bus *pbus; /* Parent bus structure pointer */ - struct pci_dev *tdev = dev; /* Temporary device structure */ - unsigned int devnum; /* Accumulated device number */ - unsigned char intline; /* Linux interrupt value */ - unsigned char intpin; /* PCI interrupt pin */ - - /* Check for valid PCI dev pointer */ - if (dev == NULL) return; - - /* Initialize bridge IDSEL variable */ - devnum = PCI_SLOT(tdev->devfn); - - /* Read the interrupt pin of the device and adjust for indexing */ - pcibios_read_config_byte(dev->bus->number, dev->devfn, - PCI_INTERRUPT_PIN, &intpin); - - /* If device doesn't request an interrupt, return */ - if ( (intpin < 1) || (intpin > 4) ) - return; - - intpin--; - - /* - * Walk up to bus 0, adjusting the interrupt pin for the standard - * PCI bus swizzle. - */ - do { - intpin = (prep_pci_intpins[devnum % 4][intpin]) - 1; - pbus = tdev->bus; /* up one level */ - tdev = pbus->self; - devnum = PCI_SLOT(tdev->devfn); - } while(tdev->bus->number); - - /* Use the primary interrupt inputs by default */ - intline = mot_info[mot_entry].pci_irq_list->primary[intpin]; - - /* - * If the board has secondary interrupt inputs, walk the bus and - * note the devfn of the bridge from bus 0. If it is the same as - * the devfn of the bus bridge with secondary inputs, use those. - * Otherwise, assume it's a PMC site and get the interrupt line - * value from the interrupt routing table. - */ - if (mot_info[mot_entry].secondary_bridge_devfn) { - pbus = dev->bus; - - while (pbus->primary != 0) - pbus = pbus->parent; - - if ((pbus->self)->devfn != 0xA0) { - if ((pbus->self)->devfn == mot_info[mot_entry].secondary_bridge_devfn) - intline = mot_info[mot_entry].pci_irq_list->secondary[intpin]; - else { - if ((char *)(mot_info[mot_entry].map) == (char *)Mesquite_pci_IRQ_map) - intline = mot_info[mot_entry].map[((pbus->self)->devfn)/8] + 16; - else { - int i; - for (i=0;i<3;i++) - intpin = (prep_pci_intpins[devnum % 4][intpin]) - 1; - intline = mot_info[mot_entry].pci_irq_list->primary[intpin]; - } - } - } - } - - /* Write calculated interrupt value to header and device list */ - dev->irq = intline; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, (u8)dev->irq); -} - -void __init -prep_pcibios_fixup(void) -{ - struct pci_dev *dev; - extern unsigned char *Motherboard_map; - extern unsigned char *Motherboard_routes; - unsigned char i; - - prep_route_pci_interrupts(); - - printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name); - if (OpenPIC_Addr) { - /* PCI interrupts are controlled by the OpenPIC */ - pci_for_each_dev(dev) { - if (dev->bus->number == 0) { - dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); - pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_LINE, dev->irq); - } else { - if (Motherboard_non0 != NULL) - Motherboard_non0(dev); - } - } - - /* Setup the Winbond or Via PIB */ - prep_pib_init(); - - return; - } - - pci_for_each_dev(dev) { - /* - * Use our old hard-coded kludge to figure out what - * irq this device uses. This is necessary on things - * without residual data. -- Cort - */ - unsigned char d = PCI_SLOT(dev->devfn); - dev->irq = Motherboard_routes[Motherboard_map[d]]; - - for ( i = 0 ; i <= 5 ; i++ ) { - /* - * Relocate PCI I/O resources if necessary so the - * standard 256MB BAT covers them. - */ - if ( (pci_resource_flags(dev, i) & IORESOURCE_IO) && - (dev->resource[i].start > 0x10000000)) { - printk("Relocating PCI address %lx -> %lx\n", - dev->resource[i].start, - (dev->resource[i].start & - 0x00FFFFFF)| 0x01000000); - dev->resource[i].start = - (dev->resource[i].start & 0x00FFFFFF) - | 0x01000000; - pci_write_config_dword(dev, - PCI_BASE_ADDRESS_0 + (i*0x4), - dev->resource[i].start); - dev->resource[i].end = - (dev->resource[i].end & 0x00FFFFFF) - | 0x01000000; - } - } -#if 0 - /* - * If we have residual data and if it knows about this - * device ask it what the irq is. - * -- Cort - */ - ppcd = residual_find_device_id( ~0L, dev->device, - -1,-1,-1, 0); -#endif - } -} - -static void __init -prep_init_resource(struct resource *res, unsigned long start, - unsigned long end, int flags) -{ - res->flags = flags; - res->start = start; - res->end = end; - res->name = "PCI host bridge"; - res->parent = NULL; - res->sibling = NULL; - res->child = NULL; -} - -void __init -prep_find_bridges(void) -{ - struct pci_controller* hose; - - hose = pcibios_alloc_controller(); - if (!hose) - return; - - hose->first_busno = 0; - hose->last_busno = 0xff; - hose->pci_mem_offset = PREP_ISA_MEM_BASE; - hose->io_base_phys = PREP_ISA_IO_BASE; - hose->io_base_virt = (void *)0x80000000; /* see prep_map_io() */ - prep_init_resource(&hose->io_resource, 0, 0x0fffffff, IORESOURCE_IO); - prep_init_resource(&hose->mem_resources[0], 0xc0000000, 0xfeffffff, - IORESOURCE_MEM); - - printk("PReP architecture\n"); - { -#ifdef CONFIG_PREP_RESIDUAL - PPC_DEVICE *hostbridge; - - hostbridge = residual_find_device(PROCESSORDEVICE, NULL, - BridgeController, PCIBridge, -1, 0); - if (hostbridge && - hostbridge->DeviceId.Interface == PCIBridgeIndirect) { - PnP_TAG_PACKET * pkt; - pkt = PnP_find_large_vendor_packet( - res->DevicePnPHeap+hostbridge->AllocatedOffset, - 3, 0); - if(pkt) { -#define p pkt->L4_Pack.L4_Data.L4_PPCPack - setup_indirect_pci(hose, - ld_le32((unsigned *) (p.PPCData)), - ld_le32((unsigned *) (p.PPCData+8))); - } else - setup_indirect_pci(hose, 0x80000cf8, 0x80000cfc); - } else -#endif /* CONFIG_PREP_RESIDUAL */ - hose->ops = &prep_pci_ops; - } - - ppc_md.pcibios_fixup = prep_pcibios_fixup; -} diff -Nru a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c --- a/arch/ppc/kernel/prep_setup.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,927 +0,0 @@ -/* - * BK Id: SCCS/s.prep_setup.c 1.44 11/13/01 21:26:07 paulus - */ -/* - * linux/arch/ppc/kernel/setup.c - * - * Copyright (C) 1995 Linus Torvalds - * Adapted from 'alpha' version by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) - * - * Support for PReP (Motorola MTX/MVME) - * by Troy Benjegerdes (hozer@drgw.net) - */ - -/* - * bootup setup stuff.. - */ - -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "local_irq.h" -#include "i8259.h" -#include "open_pic.h" - -#if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE) -#include <../drivers/sound/sound_config.h> -#include <../drivers/sound/dev_table.h> -#endif - -unsigned char ucSystemType; -unsigned char ucBoardRev; -unsigned char ucBoardRevMaj, ucBoardRevMin; - -extern unsigned long mc146818_get_rtc_time(void); -extern int mc146818_set_rtc_time(unsigned long nowtime); -extern unsigned long mk48t59_get_rtc_time(void); -extern int mk48t59_set_rtc_time(unsigned long nowtime); - -extern unsigned char prep_nvram_read_val(int addr); -extern void prep_nvram_write_val(int addr, - unsigned char val); -extern unsigned char rs_nvram_read_val(int addr); -extern void rs_nvram_write_val(int addr, - unsigned char val); -extern void ibm_prep_init(void); - -extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); -extern int pckbd_getkeycode(unsigned int scancode); -extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, - char raw_mode); -extern char pckbd_unexpected_up(unsigned char keycode); -extern void pckbd_leds(unsigned char leds); -extern void pckbd_init_hw(void); -extern unsigned char pckbd_sysrq_xlate[]; - -extern void prep_find_bridges(void); -extern char saved_command_line[]; - -int _prep_type; - -#define cached_21 (((char *)(ppc_cached_irq_mask))[3]) -#define cached_A1 (((char *)(ppc_cached_irq_mask))[2]) - -/* for the mac fs */ -kdev_t boot_dev; -/* used in nasty hack for sound - see prep_setup_arch() -- Cort */ -long ppc_cs4232_dma, ppc_cs4232_dma2; - -extern PTE *Hash, *Hash_end; -extern unsigned long Hash_size, Hash_mask; -extern int probingmem; -extern unsigned long loops_per_jiffy; - -#ifdef CONFIG_SOUND_MODULE -EXPORT_SYMBOL(ppc_cs4232_dma); -EXPORT_SYMBOL(ppc_cs4232_dma2); -#endif - -static int __prep -prep_show_cpuinfo(struct seq_file *m) -{ - extern char *Motherboard_map_name; - int cachew; -#ifdef CONFIG_PREP_RESIDUAL - int i; -#endif - - seq_printf(m, "machine\t\t: PReP %s\n", Motherboard_map_name); - - switch ( _prep_type ) { - case _PREP_IBM: - cachew = inw(0x80c); - if (cachew & (1<<6)) - seq_printf(m, "Upgrade CPU\n"); - seq_printf(m, "L2\t\t: "); - if (cachew & (1<<7)) { - seq_printf(m, "not present\n"); - goto no_l2; - } - seq_printf(m, "%sKb,", (cachew & (1 << 10))? "512" : "256"); - seq_printf(m, "%ssync\n", (cachew & (1 << 15))? "" : "a"); - break; - case _PREP_Motorola: - cachew = *((unsigned char *)CACHECRBA); - seq_printf(m, "L2\t\t: "); - switch (cachew & L2CACHE_MASK) { - case L2CACHE_512KB: - seq_printf(m, "512Kb"); - break; - case L2CACHE_256KB: - seq_printf(m, "256Kb"); - break; - case L2CACHE_1MB: - seq_printf(m, "1MB"); - break; - case L2CACHE_NONE: - seq_printf(m, "none\n"); - goto no_l2; - break; - default: - seq_printf(m, "%x\n", cachew); - } - - seq_printf(m, ", parity %s", - (cachew & L2CACHE_PARITY)? "enabled" : "disabled"); - - seq_printf(m, " SRAM:"); - - switch ( ((cachew & 0xf0) >> 4) & ~(0x3) ) { - case 1: seq_printf(m, "synchronous,parity,flow-through\n"); - break; - case 2: seq_printf(m, "asynchronous,no parity\n"); - break; - case 3: seq_printf(m, "asynchronous,parity\n"); - break; - default:seq_printf(m, "synchronous,pipelined,no parity\n"); - break; - } - break; - default: - break; - } - -no_l2: -#ifdef CONFIG_PREP_RESIDUAL - if (res->ResidualLength == 0) { - /* print info about SIMMs */ - seq_printf(m, "simms\t\t: "); - for (i = 0; (res->ActualNumMemories) && (i < MAX_MEMS); i++) { - if (res->Memories[i].SIMMSize != 0) - seq_printf(m, "%d:%ldM ", i, - (res->Memories[i].SIMMSize > 1024) ? - res->Memories[i].SIMMSize>>20 : - res->Memories[i].SIMMSize); - } - seq_printf(m, "\n"); - } -#endif - - return 0; -} - -static int __prep -prep_show_percpuinfo(struct seq_file *m, int i) -{ - int len = 0; - - /* PREP's without residual data will give incorrect values here */ - seq_printf(m, "clock\t\t: "); -#ifdef CONFIG_PREP_RESIDUAL - if (res->ResidualLength) - seq_printf(m, "%ldMHz\n", - (res->VitalProductData.ProcessorHz > 1024) ? - res->VitalProductData.ProcessorHz>>20 : - res->VitalProductData.ProcessorHz); - else -#endif /* CONFIG_PREP_RESIDUAL */ - seq_printf(m, "???\n"); - - return 0; -} - -static void __init -prep_setup_arch(void) -{ - unsigned char reg; -#if 0 /* unused?? */ - unsigned char ucMothMemType; - unsigned char ucEquipPres1; -#endif - - /* init to some ~sane value until calibrate_delay() runs */ - loops_per_jiffy = 50000000; - - /* Lookup PCI host bridges */ - prep_find_bridges(); - - /* Set up floppy in PS/2 mode */ - outb(0x09, SIO_CONFIG_RA); - reg = inb(SIO_CONFIG_RD); - reg = (reg & 0x3F) | 0x40; - outb(reg, SIO_CONFIG_RD); - outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ - - /* - * We need to set up the NvRAM access routines early as prep_init - * has yet to be called - */ - ppc_md.nvram_read_val = prep_nvram_read_val; - ppc_md.nvram_write_val = prep_nvram_write_val; - - /* we should determine this according to what we find! -- Cort */ - switch ( _prep_type ) - { - case _PREP_IBM: - /* Enable L2. Assume we don't need to flush -- Cort*/ - *(unsigned char *)(0x8000081c) |= 3; - ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ - break; - case _PREP_Motorola: - /* Enable L2. Assume we don't need to flush -- Cort*/ - *(unsigned char *)(0x8000081c) |= 3; -#ifdef CONFIG_BLK_DEV_INITRD - if (initrd_start) - ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ - else -#endif -#ifdef CONFIG_ROOT_NFS - ROOT_DEV = to_kdev_t(0x00ff); /* /dev/nfs */ -#else - ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 */ -#endif - break; - } - - /* Read in NVRAM data */ - init_prep_nvram(); - - /* if no bootargs, look in NVRAM */ - if ( cmd_line[0] == '\0' ) { - char *bootargs; - bootargs = prep_nvram_get_var("bootargs"); - if (bootargs != NULL) { - strcpy(cmd_line, bootargs); - /* again.. */ - strcpy(saved_command_line, cmd_line); - } - } - -#ifdef CONFIG_SOUND_CS4232 - /* - * setup proper values for the cs4232 driver so we don't have - * to recompile for the motorola or ibm workstations sound systems. - * This is a really nasty hack, but unless we change the driver - * it's the only way to support both addrs from one binary. - * -- Cort - */ - if ( _machine == _MACH_prep ) - { - extern struct card_info snd_installed_cards[]; - struct card_info *snd_ptr; - - for ( snd_ptr = snd_installed_cards; - snd_ptr < &snd_installed_cards[num_sound_cards]; - snd_ptr++ ) - { - if ( snd_ptr->card_type == SNDCARD_CS4232 ) - { - if ( _prep_type == _PREP_Motorola ) - { - snd_ptr->config.io_base = 0x830; - snd_ptr->config.irq = 10; - snd_ptr->config.dma = ppc_cs4232_dma = 6; - snd_ptr->config.dma2 = ppc_cs4232_dma2 = 7; - } - if ( _prep_type == _PREP_IBM ) - { - snd_ptr->config.io_base = 0x530; - snd_ptr->config.irq = 5; - snd_ptr->config.dma = ppc_cs4232_dma = 1; - /* this is wrong - but leave it for now */ - snd_ptr->config.dma2 = ppc_cs4232_dma2 = 7; - } - } - } - } -#endif /* CONFIG_SOUND_CS4232 */ - - /*print_residual_device_info();*/ - - switch (_prep_type) { - case _PREP_Motorola: - raven_init(); - break; - case _PREP_IBM: - ibm_prep_init(); - break; - } - -#ifdef CONFIG_VGA_CONSOLE - /* remap the VGA memory */ - vgacon_remap_base = 0xf0000000; - /*vgacon_remap_base = ioremap(0xc0000000, 0xba000);*/ - conswitchp = &vga_con; -#elif defined(CONFIG_DUMMY_CONSOLE) - conswitchp = &dummy_con; -#endif -} - -/* - * Determine the decrementer frequency from the residual data - * This allows for a faster boot as we do not need to calibrate the - * decrementer against another clock. This is important for embedded systems. - */ -static int __init -prep_res_calibrate_decr(void) -{ -#ifdef CONFIG_PREP_RESIDUAL - unsigned long freq, divisor = 4; - - if ( res->VitalProductData.ProcessorBusHz ) { - freq = res->VitalProductData.ProcessorBusHz; - printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", - (freq/divisor)/1000000, - (freq/divisor)%1000000); - tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); - tb_ticks_per_jiffy = freq / HZ / divisor; - return 0; - } else -#endif - return 1; -} - -/* - * Uses the on-board timer to calibrate the on-chip decrementer register - * for prep systems. On the pmac the OF tells us what the frequency is - * but on prep we have to figure it out. - * -- Cort - */ -/* Done with 3 interrupts: the first one primes the cache and the - * 2 following ones measure the interval. The precision of the method - * is still doubtful due to the short interval sampled. - */ -static volatile int calibrate_steps __initdata = 3; -static unsigned tbstamp __initdata = 0; - -static void __init -prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs *regs) -{ - unsigned long t, freq; - int step=--calibrate_steps; - - t = get_tbl(); - if (step > 0) { - tbstamp = t; - } else { - freq = (t - tbstamp)*HZ; - printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", - freq/1000000, freq%1000000); - tb_ticks_per_jiffy = freq / HZ; - tb_to_us = mulhwu_scale_factor(freq, 1000000); - } -} - -static void __init -prep_calibrate_decr(void) -{ - int res; - - /* Try and get this from the residual data. */ - res = prep_res_calibrate_decr(); - - /* If we didn't get it from the residual data, try this. */ - if ( res ) { - unsigned long flags; - - save_flags(flags); - -#define TIMER0_COUNT 0x40 -#define TIMER_CONTROL 0x43 - /* set timer to periodic mode */ - outb_p(0x34,TIMER_CONTROL);/* binary, mode 2, LSB/MSB, ch 0 */ - /* set the clock to ~100 Hz */ - outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */ - outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */ - - if (request_irq(0, prep_calibrate_decr_handler, 0, "timer", NULL) != 0) - panic("Could not allocate timer IRQ!"); - __sti(); - /* wait for calibrate */ - while ( calibrate_steps ) - ; - restore_flags(flags); - free_irq( 0, NULL); - } -} - -static long __init -mk48t59_init(void) { - unsigned char tmp; - - tmp = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLB); - if (tmp & MK48T59_RTC_CB_STOP) { - printk("Warning: RTC was stopped, date will be wrong.\n"); - ppc_md.nvram_write_val(MK48T59_RTC_CONTROLB, - tmp & ~MK48T59_RTC_CB_STOP); - /* Low frequency crystal oscillators may take a very long - * time to startup and stabilize. For now just ignore the - * the issue, but attempting to calibrate the decrementer - * from the RTC just after this wakeup is likely to be very - * inaccurate. Firmware should not allow to load - * the OS with the clock stopped anyway... - */ - } - /* Ensure that the clock registers are updated */ - tmp = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); - tmp &= ~(MK48T59_RTC_CA_READ | MK48T59_RTC_CA_WRITE); - ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, tmp); - return 0; -} - -/* We use the NVRAM RTC to time a second to calibrate the decrementer, - * the RTC registers have just been set up in the right state by the - * preceding routine. - */ -static void __init -mk48t59_calibrate_decr(void) -{ - unsigned long freq; - unsigned long t1; - unsigned char save_control; - long i; - unsigned char sec; - - - /* Make sure the time is not stopped. */ - save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLB); - - ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, - (save_control & (~MK48T59_RTC_CB_STOP))); - - /* Now make sure the read bit is off so the value will change. */ - save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); - save_control &= ~MK48T59_RTC_CA_READ; - ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); - - - /* Read the seconds value to see when it changes. */ - sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); - /* Actually this is bad for precision, we should have a loop in - * which we only read the seconds counter. nvram_read_val writes - * the address bytes on every call and this takes a lot of time. - * Perhaps an nvram_wait_change method returning a time - * stamp with a loop count as parameter would be the solution. - */ - for (i = 0 ; i < 1000000 ; i++) { /* may take up to 1 second... */ - t1 = get_tbl(); - if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { - break; - } - } - - sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); - for (i = 0 ; i < 1000000 ; i++) { /* Should take up 1 second... */ - freq = get_tbl()-t1; - if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) - break; - } - - printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", - freq/1000000, freq%1000000); - tb_ticks_per_jiffy = freq / HZ; - tb_to_us = mulhwu_scale_factor(freq, 1000000); -} - -static void __prep -prep_restart(char *cmd) -{ - unsigned long i = 10000; - - __cli(); - - /* set exception prefix high - to the prom */ - _nmask_and_or_msr(0, MSR_IP); - - /* make sure bit 0 (reset) is a 0 */ - outb( inb(0x92) & ~1L , 0x92 ); - /* signal a reset to system control port A - soft reset */ - outb( inb(0x92) | 1 , 0x92 ); - - while ( i != 0 ) i++; - panic("restart failed\n"); -} - -static void __prep -prep_halt(void) -{ - unsigned long flags; - __cli(); - /* set exception prefix high - to the prom */ - save_flags( flags ); - restore_flags( flags|MSR_IP ); - - /* make sure bit 0 (reset) is a 0 */ - outb( inb(0x92) & ~1L , 0x92 ); - /* signal a reset to system control port A - soft reset */ - outb( inb(0x92) | 1 , 0x92 ); - - while ( 1 ) ; - /* - * Not reached - */ -} - -/* - * On IBM PReP's, power management is handled by a Signetics 87c750 behind the - * Utah component on the ISA bus. To access the 750 you must write a series of - * nibbles to port 0x82a (decoded by the Utah). This is described somewhat in - * the IBM Carolina Technical Specification. - * -Hollis - */ -static void __prep -utah_sig87c750_setbit(unsigned int bytenum, unsigned int bitnum, int value) -{ - /* - * byte1: 0 0 0 1 0 d a5 a4 - * byte2: 0 0 0 1 a3 a2 a1 a0 - * - * d = the bit's value, enabled or disabled - * (a5 a4 a3) = the byte number, minus 20 - * (a2 a1 a0) = the bit number - * - * example: set the 5th bit of byte 21 (21.5) - * a5 a4 a3 = 001 (byte 1) - * a2 a1 a0 = 101 (bit 5) - * - * byte1 = 0001 0100 (0x14) - * byte2 = 0001 1101 (0x1d) - */ - unsigned char byte1=0x10, byte2=0x10; - const unsigned int pm_reg_1=0x82a; /* ISA address */ - - /* the 750's '20.0' is accessed as '0.0' through Utah (which adds 20) */ - bytenum -= 20; - - byte1 |= (!!value) << 2; /* set d */ - byte1 |= (bytenum >> 1) & 0x3; /* set a5, a4 */ - - byte2 |= (bytenum & 0x1) << 3; /* set a3 */ - byte2 |= bitnum & 0x7; /* set a2, a1, a0 */ - - outb(byte1, pm_reg_1); /* first nibble */ - mb(); - udelay(100); /* important: let controller recover */ - - outb(byte2, pm_reg_1); /* second nibble */ - mb(); - udelay(100); /* important: let controller recover */ -} - -static void __prep -prep_power_off(void) -{ - if ( _prep_type == _PREP_IBM) { - /* tested on: - * Carolina's: 7248-43P, 6070 (PowerSeries 850) - * should work on: - * Carolina: 6050 (PowerSeries 830) - * 7043-140 (Tiger 1) - */ - unsigned long flags; - __cli(); - /* set exception prefix high - to the prom */ - save_flags( flags ); - restore_flags( flags|MSR_IP ); - - utah_sig87c750_setbit(21, 5, 1); /* set bit 21.5, "PMEXEC_OFF" */ - - while ( 1 ) ; - /* not reached */ - } else { - prep_halt(); - } -} - -static unsigned int __prep -prep_irq_cannonicalize(u_int irq) -{ - if (irq == 2) - { - return 9; - } - else - { - return irq; - } -} - -static int __prep -prep_get_irq(struct pt_regs *regs) -{ - return i8259_irq(smp_processor_id()); -} - -static void __init -prep_init_IRQ(void) -{ - int i; - - if (OpenPIC_Addr != NULL) - openpic_init(1, NUM_8259_INTERRUPTS, 0, -1); - for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ ) - irq_desc[i].handler = &i8259_pic; - i8259_init(); -} - -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) -/* - * IDE stuff. - */ -static int __prep -prep_ide_default_irq(ide_ioreg_t base) -{ - switch (base) { - case 0x1f0: return 13; - case 0x170: return 13; - case 0x1e8: return 11; - case 0x168: return 10; - case 0xfff0: return 14; /* MCP(N)750 ide0 */ - case 0xffe0: return 15; /* MCP(N)750 ide1 */ - default: return 0; - } -} - -static ide_ioreg_t __prep -prep_ide_default_io_base(int index) -{ - switch (index) { - case 0: return 0x1f0; - case 1: return 0x170; - case 2: return 0x1e8; - case 3: return 0x168; - default: - return 0; - } -} - -static int __prep -prep_ide_check_region(ide_ioreg_t from, unsigned int extent) -{ - return check_region(from, extent); -} - -static void __prep -prep_ide_request_region(ide_ioreg_t from, - unsigned int extent, - const char *name) -{ - request_region(from, extent, name); -} - -static void __prep -prep_ide_release_region(ide_ioreg_t from, - unsigned int extent) -{ - release_region(from, extent); -} - -static void __init -prep_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) -{ - ide_ioreg_t reg = data_port; - int i; - - for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { - hw->io_ports[i] = reg; - reg += 1; - } - if (ctrl_port) { - hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; - } else { - hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206; - } - if (irq != NULL) - *irq = 0; -} -#endif - -#ifdef CONFIG_SMP -/* PReP (MTX) support */ -static int __init -smp_prep_probe(void) -{ - extern int mot_multi; - - if (mot_multi) { - openpic_request_IPIs(); - smp_hw_index[1] = 1; - return 2; - } - - return 1; -} - -static void __init -smp_prep_kick_cpu(int nr) -{ - *(unsigned long *)KERNELBASE = nr; - asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); - printk("CPU1 reset, waiting\n"); -} - -static void __init -smp_prep_setup_cpu(int cpu_nr) -{ - if (OpenPIC_Addr) - do_openpic_setup_cpu(); -} - -static struct smp_ops_t prep_smp_ops __prepdata = { - smp_openpic_message_pass, - smp_prep_probe, - smp_prep_kick_cpu, - smp_prep_setup_cpu, -}; -#endif /* CONFIG_SMP */ - -/* - * This finds the amount of physical ram and does necessary - * setup for prep. This is pretty architecture specific so - * this will likely stay separate from the pmac. - * -- Cort - */ -static unsigned long __init -prep_find_end_of_memory(void) -{ - unsigned long total = 0; - extern unsigned int boot_mem_size; - -#ifdef CONFIG_PREP_RESIDUAL - total = res->TotalMemory; -#endif - - if (total == 0 && boot_mem_size != 0) - total = boot_mem_size; - else if (total == 0) { - /* - * I need a way to probe the amount of memory if the residual - * data doesn't contain it. -- Cort - */ - total = 0x02000000; - printk(KERN_INFO "Ramsize from residual data was 0" - " -- defaulting to %ldM\n", total>>20); - } - - return (total); -} - -/* - * Setup the bat mappings we're going to load that cover - * the io areas. RAM was mapped by mapin_ram(). - * -- Cort - */ -static void __init -prep_map_io(void) -{ - io_block_mapping(0x80000000, PREP_ISA_IO_BASE, 0x10000000, _PAGE_IO); - io_block_mapping(0xf0000000, PREP_ISA_MEM_BASE, 0x08000000, _PAGE_IO); -} - -static void __init -prep_init2(void) -{ -#ifdef CONFIG_NVRAM - request_region(PREP_NVRAM_AS0, 0x8, "nvram"); -#endif - request_region(0x20,0x20,"pic1"); - request_region(0xa0,0x20,"pic2"); - request_region(0x00,0x20,"dma1"); - request_region(0x40,0x20,"timer"); - request_region(0x80,0x10,"dma page reg"); - request_region(0xc0,0x20,"dma2"); -} - -void __init -prep_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ -#ifdef CONFIG_PREP_RESIDUAL - /* make a copy of residual data */ - if ( r3 ) { - memcpy((void *)res,(void *)(r3+KERNELBASE), - sizeof(RESIDUAL)); - } -#endif - -#ifdef CONFIG_BLK_DEV_INITRD - if ( r4 ) - { - initrd_start = r4 + KERNELBASE; - initrd_end = r5 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_INITRD */ - - /* Copy cmd_line parameters */ - if ( r6 ) - { - *(char *)(r7 + KERNELBASE) = 0; - strcpy(cmd_line, (char *)(r6 + KERNELBASE)); - } - - isa_io_base = PREP_ISA_IO_BASE; - isa_mem_base = PREP_ISA_MEM_BASE; - pci_dram_offset = PREP_PCI_DRAM_OFFSET; - ISA_DMA_THRESHOLD = 0x00ffffff; - DMA_MODE_READ = 0x44; - DMA_MODE_WRITE = 0x48; - - /* figure out what kind of prep workstation we are */ -#ifdef CONFIG_PREP_RESIDUAL - if ( res->ResidualLength != 0 ) - { - if ( !strncmp(res->VitalProductData.PrintableModel,"IBM",3) ) - _prep_type = _PREP_IBM; - else - _prep_type = _PREP_Motorola; - } - else /* assume motorola if no residual (netboot?) */ -#endif - { - _prep_type = _PREP_Motorola; - } - - ppc_md.setup_arch = prep_setup_arch; - ppc_md.show_percpuinfo = prep_show_percpuinfo; - ppc_md.show_cpuinfo = prep_show_cpuinfo; - ppc_md.irq_cannonicalize = prep_irq_cannonicalize; - ppc_md.init_IRQ = prep_init_IRQ; - /* this gets changed later on if we have an OpenPIC -- Cort */ - ppc_md.get_irq = prep_get_irq; - ppc_md.init = prep_init2; - - ppc_md.restart = prep_restart; - ppc_md.power_off = prep_power_off; - ppc_md.halt = prep_halt; - - ppc_md.time_init = NULL; - if (_prep_type == _PREP_IBM) { - ppc_md.set_rtc_time = mc146818_set_rtc_time; - ppc_md.get_rtc_time = mc146818_get_rtc_time; - ppc_md.calibrate_decr = prep_calibrate_decr; - } else { - ppc_md.set_rtc_time = mk48t59_set_rtc_time; - ppc_md.get_rtc_time = mk48t59_get_rtc_time; - ppc_md.calibrate_decr = mk48t59_calibrate_decr; - ppc_md.time_init = mk48t59_init; - } - - ppc_md.find_end_of_memory = prep_find_end_of_memory; - ppc_md.setup_io_mappings = prep_map_io; - -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) - ppc_ide_md.default_irq = prep_ide_default_irq; - ppc_ide_md.default_io_base = prep_ide_default_io_base; - ppc_ide_md.ide_check_region = prep_ide_check_region; - ppc_ide_md.ide_request_region = prep_ide_request_region; - ppc_ide_md.ide_release_region = prep_ide_release_region; - ppc_ide_md.ide_init_hwif = prep_ide_init_hwif_ports; -#endif - -#ifdef CONFIG_VT - ppc_md.kbd_setkeycode = pckbd_setkeycode; - ppc_md.kbd_getkeycode = pckbd_getkeycode; - ppc_md.kbd_translate = pckbd_translate; - ppc_md.kbd_unexpected_up = pckbd_unexpected_up; - ppc_md.kbd_leds = pckbd_leds; - ppc_md.kbd_init_hw = pckbd_init_hw; -#ifdef CONFIG_MAGIC_SYSRQ - ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; - SYSRQ_KEY = 0x54; -#endif -#endif - -#ifdef CONFIG_SMP - ppc_md.smp_ops = &prep_smp_ops; -#endif /* CONFIG_SMP */ -} diff -Nru a/arch/ppc/kernel/prep_time.c b/arch/ppc/kernel/prep_time.c --- a/arch/ppc/kernel/prep_time.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,228 +0,0 @@ -/* - * BK Id: SCCS/s.prep_time.c 1.10 09/08/01 15:47:42 paulus - */ -/* - * linux/arch/i386/kernel/time.c - * - * Copyright (C) 1991, 1992, 1995 Linus Torvalds - * - * Adapted for PowerPC (PreP) by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) - * copied and modified from intel version - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -extern spinlock_t rtc_lock; - -/* - * The motorola uses the m48t18 rtc (includes DS1643) whose registers - * are at a higher end of nvram (1ff8-1fff) than the ibm mc146818 - * rtc (ds1386) which has regs at addr 0-d). The intel gets - * past this because the bios emulates the mc146818. - * - * Why in the world did they have to use different clocks? - * - * Right now things are hacked to check which machine we're on then - * use the appropriate macro. This is very very ugly and I should - * probably have a function that checks which machine we're on then - * does things correctly transparently or a function pointer which - * is setup at boot time to use the correct addresses. - * -- Cort - */ - -/* - * Set the hardware clock. -- Cort - */ -__prep -int mc146818_set_rtc_time(unsigned long nowtime) -{ - unsigned char save_control, save_freq_select; - struct rtc_time tm; - - spin_lock(&rtc_lock); - to_tm(nowtime, &tm); - - /* tell the clock it's being set */ - save_control = CMOS_READ(RTC_CONTROL); - - CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); - - /* stop and reset prescaler */ - save_freq_select = CMOS_READ(RTC_FREQ_SELECT); - - CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - tm.tm_year = (tm.tm_year - 1900) % 100; - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BIN_TO_BCD(tm.tm_sec); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_mon); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_year); - } - CMOS_WRITE(tm.tm_sec, RTC_SECONDS); - CMOS_WRITE(tm.tm_min, RTC_MINUTES); - CMOS_WRITE(tm.tm_hour, RTC_HOURS); - CMOS_WRITE(tm.tm_mon, RTC_MONTH); - CMOS_WRITE(tm.tm_mday, RTC_DAY_OF_MONTH); - CMOS_WRITE(tm.tm_year, RTC_YEAR); - - /* The following flags have to be released exactly in this order, - * otherwise the DS12887 (popular MC146818A clone with integrated - * battery and quartz) will not reset the oscillator and will not - * update precisely 500 ms later. You won't find this mentioned in - * the Dallas Semiconductor data sheets, but who believes data - * sheets anyway ... -- Markus Kuhn - */ - CMOS_WRITE(save_control, RTC_CONTROL); - CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); - spin_unlock(&rtc_lock); - - return 0; -} - -__prep -unsigned long mc146818_get_rtc_time(void) -{ - unsigned int year, mon, day, hour, min, sec; - int uip, i; - - /* The Linux interpretation of the CMOS clock register contents: - * When the Update-In-Progress (UIP) flag goes from 1 to 0, the - * RTC registers show the second which has precisely just started. - * Let's hope other operating systems interpret the RTC the same way. - */ - - /* Since the UIP flag is set for about 2.2 ms and the clock - * is typically written with a precision of 1 jiffy, trying - * to obtain a precision better than a few milliseconds is - * an illusion. Only consistency is interesting, this also - * allows to use the routine for /dev/rtc without a potential - * 1 second kernel busy loop triggered by any reader of /dev/rtc. - */ - - for ( i = 0; i<1000000; i++) { - uip = CMOS_READ(RTC_FREQ_SELECT); - sec = CMOS_READ(RTC_SECONDS); - min = CMOS_READ(RTC_MINUTES); - hour = CMOS_READ(RTC_HOURS); - day = CMOS_READ(RTC_DAY_OF_MONTH); - mon = CMOS_READ(RTC_MONTH); - year = CMOS_READ(RTC_YEAR); - uip |= CMOS_READ(RTC_FREQ_SELECT); - if ((uip & RTC_UIP)==0) break; - } - - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) - || RTC_ALWAYS_BCD) - { - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } - if ((year += 1900) < 1970) - year += 100; - return mktime(year, mon, day, hour, min, sec); -} - -__prep -int mk48t59_set_rtc_time(unsigned long nowtime) -{ - unsigned char save_control; - struct rtc_time tm; - - spin_lock(&rtc_lock); - to_tm(nowtime, &tm); - - /* tell the clock it's being written */ - save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); - - ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, - (save_control | MK48T59_RTC_CA_WRITE)); - - tm.tm_year = (tm.tm_year - 1900) % 100; - BIN_TO_BCD(tm.tm_sec); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_mon); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_year); - - ppc_md.nvram_write_val(MK48T59_RTC_SECONDS, tm.tm_sec); - ppc_md.nvram_write_val(MK48T59_RTC_MINUTES, tm.tm_min); - ppc_md.nvram_write_val(MK48T59_RTC_HOURS, tm.tm_hour); - ppc_md.nvram_write_val(MK48T59_RTC_MONTH, tm.tm_mon); - ppc_md.nvram_write_val(MK48T59_RTC_DAY_OF_MONTH, tm.tm_mday); - ppc_md.nvram_write_val(MK48T59_RTC_YEAR, tm.tm_year); - - /* Turn off the write bit. */ - ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); - spin_unlock(&rtc_lock); - - return 0; -} - -__prep -unsigned long mk48t59_get_rtc_time(void) -{ - unsigned char save_control; - unsigned int year, mon, day, hour, min, sec; - - /* Simple: freeze the clock, read it and allow updates again */ - save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); - save_control &= ~MK48T59_RTC_CA_READ; - ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); - - /* Set the register to read the value. */ - ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, - (save_control | MK48T59_RTC_CA_READ)); - - sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); - min = ppc_md.nvram_read_val(MK48T59_RTC_MINUTES); - hour = ppc_md.nvram_read_val(MK48T59_RTC_HOURS); - day = ppc_md.nvram_read_val(MK48T59_RTC_DAY_OF_MONTH); - mon = ppc_md.nvram_read_val(MK48T59_RTC_MONTH); - year = ppc_md.nvram_read_val(MK48T59_RTC_YEAR); - - /* Let the time values change again. */ - ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); - - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - - year = year + 1900; - if (year < 1970) { - year += 100; - } - - return mktime(year, mon, day, hour, min, sec); -} diff -Nru a/arch/ppc/kernel/proc_rtas.c b/arch/ppc/kernel/proc_rtas.c --- a/arch/ppc/kernel/proc_rtas.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,787 +0,0 @@ -/* - * BK Id: SCCS/s.proc_rtas.c 1.5 05/17/01 18:14:22 cort - */ -/* - * arch/ppc/kernel/proc_rtas.c - * Copyright (C) 2000 Tilmann Bitterberg - * (tilmann@bitterberg.de) - * - * RTAS (Runtime Abstraction Services) stuff - * Intention is to provide a clean user interface - * to use the RTAS. - * - * TODO: - * Split off a header file and maybe move it to a different - * location. Write Documentation on what the /proc/rtas/ entries - * actually do. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include /* for ppc_md */ -#include - -/* Token for Sensors */ -#define KEY_SWITCH 0x0001 -#define ENCLOSURE_SWITCH 0x0002 -#define THERMAL_SENSOR 0x0003 -#define LID_STATUS 0x0004 -#define POWER_SOURCE 0x0005 -#define BATTERY_VOLTAGE 0x0006 -#define BATTERY_REMAINING 0x0007 -#define BATTERY_PERCENTAGE 0x0008 -#define EPOW_SENSOR 0x0009 -#define BATTERY_CYCLESTATE 0x000a -#define BATTERY_CHARGING 0x000b - -/* IBM specific sensors */ -#define IBM_SURVEILLANCE 0x2328 /* 9000 */ -#define IBM_FANRPM 0x2329 /* 9001 */ -#define IBM_VOLTAGE 0x232a /* 9002 */ -#define IBM_DRCONNECTOR 0x232b /* 9003 */ -#define IBM_POWERSUPPLY 0x232c /* 9004 */ -#define IBM_INTQUEUE 0x232d /* 9005 */ - -/* Status return values */ -#define SENSOR_CRITICAL_HIGH 13 -#define SENSOR_WARNING_HIGH 12 -#define SENSOR_NORMAL 11 -#define SENSOR_WARNING_LOW 10 -#define SENSOR_CRITICAL_LOW 9 -#define SENSOR_SUCCESS 0 -#define SENSOR_HW_ERROR -1 -#define SENSOR_BUSY -2 -#define SENSOR_NOT_EXIST -3 -#define SENSOR_DR_ENTITY -9000 - -/* Location Codes */ -#define LOC_SCSI_DEV_ADDR 'A' -#define LOC_SCSI_DEV_LOC 'B' -#define LOC_CPU 'C' -#define LOC_DISKETTE 'D' -#define LOC_ETHERNET 'E' -#define LOC_FAN 'F' -#define LOC_GRAPHICS 'G' -/* reserved / not used 'H' */ -#define LOC_IO_ADAPTER 'I' -/* reserved / not used 'J' */ -#define LOC_KEYBOARD 'K' -#define LOC_LCD 'L' -#define LOC_MEMORY 'M' -#define LOC_NV_MEMORY 'N' -#define LOC_MOUSE 'O' -#define LOC_PLANAR 'P' -#define LOC_OTHER_IO 'Q' -#define LOC_PARALLEL 'R' -#define LOC_SERIAL 'S' -#define LOC_DEAD_RING 'T' -#define LOC_RACKMOUNTED 'U' /* for _u_nit is rack mounted */ -#define LOC_VOLTAGE 'V' -#define LOC_SWITCH_ADAPTER 'W' -#define LOC_OTHER 'X' -#define LOC_FIRMWARE 'Y' -#define LOC_SCSI 'Z' - -/* Tokens for indicators */ -#define TONE_FREQUENCY 0x0001 /* 0 - 1000 (HZ)*/ -#define TONE_VOLUME 0x0002 /* 0 - 100 (%) */ -#define SYSTEM_POWER_STATE 0x0003 -#define WARNING_LIGHT 0x0004 -#define DISK_ACTIVITY_LIGHT 0x0005 -#define HEX_DISPLAY_UNIT 0x0006 -#define BATTERY_WARNING_TIME 0x0007 -#define CONDITION_CYCLE_REQUEST 0x0008 -#define SURVEILLANCE_INDICATOR 0x2328 /* 9000 */ -#define DR_ACTION 0x2329 /* 9001 */ -#define DR_INDICATOR 0x232a /* 9002 */ -/* 9003 - 9004: Vendor specific */ -#define GLOBAL_INTERRUPT_QUEUE 0x232d /* 9005 */ -/* 9006 - 9999: Vendor specific */ - -/* other */ -#define MAX_SENSORS 17 /* I only know of 17 sensors */ -#define MAX_LINELENGTH 256 -#define SENSOR_PREFIX "ibm,sensor-" -#define cel_to_fahr(x) ((x*9/5)+32) - - -/* Globals */ -static struct proc_dir_entry *proc_rtas; -static struct rtas_sensors sensors; -static struct device_node *rtas; -static unsigned long power_on_time = 0; /* Save the time the user set */ -static char progress_led[MAX_LINELENGTH]; - -static unsigned long rtas_tone_frequency = 1000; -static unsigned long rtas_tone_volume = 0; - -/* ****************STRUCTS******************************************* */ -struct individual_sensor { - unsigned int token; - unsigned int quant; -}; - -struct rtas_sensors { - struct individual_sensor sensor[MAX_SENSORS]; - unsigned int quant; -}; - -/* ****************************************************************** */ -/* Declarations */ -static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, - int count, int *eof, void *data); -static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); - -static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, - size_t count, loff_t *ppos); - -struct file_operations ppc_rtas_poweron_operations = { - read: ppc_rtas_poweron_read, - write: ppc_rtas_poweron_write -}; -struct file_operations ppc_rtas_progress_operations = { - read: ppc_rtas_progress_read, - write: ppc_rtas_progress_write -}; - -struct file_operations ppc_rtas_clock_operations = { - read: ppc_rtas_clock_read, - write: ppc_rtas_clock_write -}; - -struct file_operations ppc_rtas_tone_freq_operations = { - read: ppc_rtas_tone_freq_read, - write: ppc_rtas_tone_freq_write -}; -struct file_operations ppc_rtas_tone_volume_operations = { - read: ppc_rtas_tone_volume_read, - write: ppc_rtas_tone_volume_write -}; - -int ppc_rtas_find_all_sensors (void); -int ppc_rtas_process_sensor(struct individual_sensor s, int state, - int error, char * buf); -char * ppc_rtas_process_error(int error); -int get_location_code(struct individual_sensor s, char * buf); -int check_location_string (char *c, char * buf); -int check_location (char *c, int idx, char * buf); - -/* ****************************************************************** */ -/* MAIN */ -/* ****************************************************************** */ -void proc_rtas_init(void) -{ - struct proc_dir_entry *entry; - - rtas = find_devices("rtas"); - if ((rtas == 0) || (_machine != _MACH_chrp)) { - return; - } - - proc_rtas = proc_mkdir("rtas", 0); - if (proc_rtas == 0) - return; - - /* /proc/rtas entries */ - - entry = create_proc_entry("progress", S_IRUGO|S_IWUSR, proc_rtas); - if (entry) entry->proc_fops = &ppc_rtas_progress_operations; - - entry = create_proc_entry("clock", S_IRUGO|S_IWUSR, proc_rtas); - if (entry) entry->proc_fops = &ppc_rtas_clock_operations; - - entry = create_proc_entry("poweron", S_IWUSR|S_IRUGO, proc_rtas); - if (entry) entry->proc_fops = &ppc_rtas_poweron_operations; - - create_proc_read_entry("sensors", S_IRUGO, proc_rtas, - ppc_rtas_sensor_read, NULL); - - entry = create_proc_entry("frequency", S_IWUSR|S_IRUGO, proc_rtas); - if (entry) entry->proc_fops = &ppc_rtas_tone_freq_operations; - - entry = create_proc_entry("volume", S_IWUSR|S_IRUGO, proc_rtas); - if (entry) entry->proc_fops = &ppc_rtas_tone_volume_operations; -} - -/* ****************************************************************** */ -/* POWER-ON-TIME */ -/* ****************************************************************** */ -static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - struct rtc_time tm; - unsigned long nowtime; - char *dest; - int error; - - nowtime = simple_strtoul(buf, &dest, 10); - if (*dest != '\0' && *dest != '\n') { - printk("ppc_rtas_poweron_write: Invalid time\n"); - return count; - } - power_on_time = nowtime; /* save the time */ - - to_tm(nowtime, &tm); - - error = call_rtas("set-time-for-power-on", 7, 1, NULL, - tm.tm_year, tm.tm_mon, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, 0 /* nano */); - if (error != 0) - printk(KERN_WARNING "error: setting poweron time returned: %s\n", - ppc_rtas_process_error(error)); - return count; -} -/* ****************************************************************** */ -static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - int n; - if (power_on_time == 0) - n = sprintf(buf, "Power on time not set\n"); - else - n = sprintf(buf, "%lu\n", power_on_time); - - if (*ppos >= strlen(buf)) - return 0; - if (n > strlen(buf) - *ppos) - n = strlen(buf) - *ppos; - if (n > count) - n = count; - *ppos += n; - return n; -} - -/* ****************************************************************** */ -/* PROGRESS */ -/* ****************************************************************** */ -static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - unsigned long hex; - - strcpy(progress_led, buf); /* save the string */ - /* Lets see if the user passed hexdigits */ - hex = simple_strtoul(buf, NULL, 10); - - ppc_md.progress ((char *)buf, hex); - return count; - - /* clear the line */ /* ppc_md.progress(" ", 0xffff);*/ -} -/* ****************************************************************** */ -static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - int n = 0; - if (progress_led != NULL) - n = sprintf (buf, "%s\n", progress_led); - if (*ppos >= strlen(buf)) - return 0; - if (n > strlen(buf) - *ppos) - n = strlen(buf) - *ppos; - if (n > count) - n = count; - *ppos += n; - return n; -} - -/* ****************************************************************** */ -/* CLOCK */ -/* ****************************************************************** */ -static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - struct rtc_time tm; - unsigned long nowtime; - char *dest; - int error; - - nowtime = simple_strtoul(buf, &dest, 10); - if (*dest != '\0' && *dest != '\n') { - printk("ppc_rtas_clock_write: Invalid time\n"); - return count; - } - - to_tm(nowtime, &tm); - error = call_rtas("set-time-of-day", 7, 1, NULL, - tm.tm_year, tm.tm_mon, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, 0); - if (error != 0) - printk(KERN_WARNING "error: setting the clock returned: %s\n", - ppc_rtas_process_error(error)); - return count; -} -/* ****************************************************************** */ -static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - unsigned int year, mon, day, hour, min, sec; - unsigned long *ret = kmalloc(4*8, GFP_KERNEL); - int n, error; - - error = call_rtas("get-time-of-day", 0, 8, ret); - - year = ret[0]; mon = ret[1]; day = ret[2]; - hour = ret[3]; min = ret[4]; sec = ret[5]; - - if (error != 0){ - printk(KERN_WARNING "error: reading the clock returned: %s\n", - ppc_rtas_process_error(error)); - n = sprintf (buf, "0"); - } else { - n = sprintf (buf, "%lu\n", mktime(year, mon, day, hour, min, sec)); - } - kfree(ret); - - if (*ppos >= strlen(buf)) - return 0; - if (n > strlen(buf) - *ppos) - n = strlen(buf) - *ppos; - if (n > count) - n = count; - *ppos += n; - return n; -} - -/* ****************************************************************** */ -/* SENSOR STUFF */ -/* ****************************************************************** */ -static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, - int count, int *eof, void *data) -{ - int i,j,n; - unsigned long ret; - int state, error; - char buffer[MAX_LINELENGTH*MAX_SENSORS]; /* May not be enough */ - - if (count < 0) - return -EINVAL; - - n = sprintf ( buffer , "RTAS (RunTime Abstraction Services) Sensor Information\n"); - n += sprintf ( buffer+n, "Sensor\t\tValue\t\tCondition\tLocation\n"); - n += sprintf ( buffer+n, "********************************************************\n"); - - if (ppc_rtas_find_all_sensors() != 0) { - n += sprintf ( buffer+n, "\nNo sensors are available\n"); - goto return_string; - } - - for (i=0; i= 0) { - error = call_rtas("get-sensor-state", 2, 2, &ret, - sensors.sensor[i].token, sensors.sensor[i].quant-j); - state = (int) ret; - n += ppc_rtas_process_sensor(sensors.sensor[i], state, error, buffer+n ); - n += sprintf (buffer+n, "\n"); - j--; - } /* while */ - } /* for */ - -return_string: - if (off >= strlen(buffer)) { - *eof = 1; - return 0; - } - if (n > strlen(buffer) - off) - n = strlen(buffer) - off; - if (n > count) - n = count; - else - *eof = 1; - memcpy(buf, buffer + off, n); - *start = buf; - return n; -} - -/* ****************************************************************** */ - -int ppc_rtas_find_all_sensors (void) -{ - unsigned long *utmp; - int len, i, j; - - utmp = (unsigned long *) get_property(rtas, "rtas-sensors", &len); - if (utmp == NULL) { - printk (KERN_ERR "error: could not get rtas-sensors\n"); - return 1; - } - - sensors.quant = len / 8; /* int + int */ - - for (i=0, j=0; j= llen) pos=0; - } - return n; -} -/* ****************************************************************** */ -/* INDICATORS - Tone Frequency */ -/* ****************************************************************** */ -static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - unsigned long freq; - char *dest; - int error; - freq = simple_strtoul(buf, &dest, 10); - if (*dest != '\0' && *dest != '\n') { - printk("ppc_rtas_tone_freq_write: Invalid tone freqency\n"); - return count; - } - if (freq < 0) freq = 0; - rtas_tone_frequency = freq; /* save it for later */ - error = call_rtas("set-indicator", 3, 1, NULL, - TONE_FREQUENCY, 0, freq); - if (error != 0) - printk(KERN_WARNING "error: setting tone frequency returned: %s\n", - ppc_rtas_process_error(error)); - return count; -} -/* ****************************************************************** */ -static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - int n; - n = sprintf(buf, "%lu\n", rtas_tone_frequency); - - if (*ppos >= strlen(buf)) - return 0; - if (n > strlen(buf) - *ppos) - n = strlen(buf) - *ppos; - if (n > count) - n = count; - *ppos += n; - return n; -} -/* ****************************************************************** */ -/* INDICATORS - Tone Volume */ -/* ****************************************************************** */ -static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - unsigned long volume; - char *dest; - int error; - volume = simple_strtoul(buf, &dest, 10); - if (*dest != '\0' && *dest != '\n') { - printk("ppc_rtas_tone_volume_write: Invalid tone volume\n"); - return count; - } - if (volume < 0) volume = 0; - if (volume > 100) volume = 100; - - rtas_tone_volume = volume; /* save it for later */ - error = call_rtas("set-indicator", 3, 1, NULL, - TONE_VOLUME, 0, volume); - if (error != 0) - printk(KERN_WARNING "error: setting tone volume returned: %s\n", - ppc_rtas_process_error(error)); - return count; -} -/* ****************************************************************** */ -static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - int n; - n = sprintf(buf, "%lu\n", rtas_tone_volume); - - if (*ppos >= strlen(buf)) - return 0; - if (n > strlen(buf) - *ppos) - n = strlen(buf) - *ppos; - if (n > count) - n = count; - *ppos += n; - return n; -} diff -Nru a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c --- a/arch/ppc/kernel/process.c Tue Feb 19 18:09:00 2002 +++ b/arch/ppc/kernel/process.c Tue Feb 19 18:09:00 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.process.c 1.31 10/02/01 09:51:41 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/arch/ppc/kernel/process.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include @@ -42,20 +44,30 @@ #include #include #include +#ifdef CONFIG_PPC_ISERIES +#include +#endif int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); extern unsigned long _get_SP(void); struct task_struct *last_task_used_math = NULL; struct task_struct *last_task_used_altivec = NULL; + static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM(init_mm); -/* this is 16-byte aligned because it has a stack in it */ -union task_union __attribute((aligned(16))) init_task_union = { - INIT_TASK(init_task_union.task) -}; + +/* this is 8kB-aligned so we can get to the thread_info struct + at the base of it from the stack pointer with 1 integer instruction. */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = +{ INIT_THREAD_INFO(init_task) }; + +/* initial task structure */ +struct task_struct init_task = INIT_TASK(init_task); + /* only used to get secondary processor up */ struct task_struct *current_set[NR_CPUS] = {&init_task, }; @@ -185,9 +197,7 @@ return 1; } -void -_switch_to(struct task_struct *prev, struct task_struct *new, - struct task_struct **last) +void switch_to(struct task_struct *prev, struct task_struct *new) { struct thread_struct *new_thread, *old_thread; unsigned long s; @@ -209,7 +219,7 @@ * every switch, just a save. * -- Cort */ - if ( prev->thread.regs && (prev->thread.regs->msr & MSR_FP) ) + if (prev->thread.regs && (prev->thread.regs->msr & MSR_FP)) giveup_fpu(prev); #ifdef CONFIG_ALTIVEC /* @@ -228,8 +238,6 @@ #endif /* CONFIG_ALTIVEC */ #endif /* CONFIG_SMP */ - current_set[smp_processor_id()] = new; - /* Avoid the trap. On smp this this never happens since * we don't set last_task_used_altivec -- Cort */ @@ -237,7 +245,7 @@ new->thread.regs->msr |= MSR_VEC; new_thread = &new->thread; old_thread = ¤t->thread; - *last = _switch(old_thread, new_thread); + _switch(old_thread, new_thread); __restore_flags(s); } @@ -260,7 +268,7 @@ printk("\nlast math %p last altivec %p", last_task_used_math, last_task_used_altivec); -#ifdef CONFIG_4xx +#if defined(CONFIG_4xx) && defined(DCRN_PLB0_BEAR) printk("\nPLB0: bear= 0x%8.8x acr= 0x%8.8x besr= 0x%8.8x\n", mfdcr(DCRN_POB0_BEAR), mfdcr(DCRN_PLB0_ACR), mfdcr(DCRN_PLB0_BESR)); @@ -270,7 +278,7 @@ #endif #ifdef CONFIG_SMP - printk(" CPU: %d", current->processor); + printk(" CPU: %d", smp_processor_id()); #endif /* CONFIG_SMP */ printk("\n"); @@ -325,7 +333,7 @@ { struct pt_regs *childregs, *kregs; extern void ret_from_fork(void); - unsigned long sp = (unsigned long)p + sizeof(union task_union); + unsigned long sp = (unsigned long)p->thread_info + THREAD_SIZE; unsigned long childframe; /* Copy registers */ @@ -336,9 +344,10 @@ /* for kernel thread, set `current' and stackptr in new task */ childregs->gpr[1] = sp + sizeof(struct pt_regs); childregs->gpr[2] = (unsigned long) p; - } + p->thread.regs = NULL; /* no user register state */ + } else + p->thread.regs = childregs; childregs->gpr[3] = 0; /* Result from fork() */ - p->thread.regs = childregs; sp -= STACK_FRAME_OVERHEAD; childframe = sp; @@ -355,6 +364,9 @@ sp -= STACK_FRAME_OVERHEAD; p->thread.ksp = sp; kregs->nip = (unsigned long)ret_from_fork; +#ifdef CONFIG_PPC_ISERIES + kregs->softEnable = ((struct Paca *)mfspr(SPRG1))->xProcEnabled; +#endif /* * copy fpu info - assume lazy fpu switch now always @@ -391,7 +403,10 @@ { set_fs(USER_DS); memset(regs->gpr, 0, sizeof(regs->gpr)); - memset(®s->ctr, 0, 5 * sizeof(regs->ctr)); + regs->ctr = 0; + regs->link = 0; + regs->xer = 0; + regs->ccr = 0; regs->nip = nip; regs->gpr[1] = sp; regs->msr = MSR_USER; @@ -399,9 +414,30 @@ last_task_used_math = 0; if (last_task_used_altivec == current) last_task_used_altivec = 0; + memset(current->thread.fpr, 0, sizeof(current->thread.fpr)); current->thread.fpscr = 0; +#ifdef CONFIG_ALTIVEC + memset(current->thread.vr, 0, sizeof(current->thread.vr)); + memset(¤t->thread.vscr, 0, sizeof(current->thread.vscr)); + current->thread.vrsave = 0; +#endif /* CONFIG_ALTIVEC */ } +#if 0 +int set_fpexc_mode(struct task_struct *tsk, unsigned int val) +{ + struct pt_regs *regs = tsk->thread.regs; + + if (val > PR_FP_EXC_PRECISE) + return -EINVAL; + tsk->thread.fpexc_mode = __pack_fe01(val); + if (regs != NULL && (regs->msr & MSR_FP) != 0) + regs->msr = (regs->msr & ~(MSR_FE0|MSR_FE1)) + | tsk->thread.fpexc_mode; + return 0; +} +#endif + int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) { @@ -465,6 +501,27 @@ printk("\n"); } +void show_trace_task(struct task_struct *tsk) +{ + unsigned long stack_top = (unsigned long) tsk->thread_info + THREAD_SIZE; + unsigned long sp, prev_sp; + int count = 0; + + if (tsk == NULL) + return; + sp = (unsigned long) &tsk->thread.ksp; + do { + prev_sp = sp; + sp = *(unsigned long *)sp; + if (sp <= prev_sp || sp >= stack_top || (sp & 3) != 0) + break; + if (count > 0) + printk("[%08lx] ", *(unsigned long *)(sp + 4)); + } while (++count < 16); + if (count > 1) + printk("\n"); +} + #if 0 /* * Low level print for debugging - Cort @@ -566,7 +623,7 @@ unsigned long get_wchan(struct task_struct *p) { unsigned long ip, sp; - unsigned long stack_page = (unsigned long) p; + unsigned long stack_page = (unsigned long) p->thread_info; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; diff -Nru a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c --- a/arch/ppc/kernel/prom.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/kernel/prom.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.prom.c 1.42 09/08/01 15:47:42 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * Procedures for interfacing to the Open Firmware PROM on @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include @@ -34,28 +37,13 @@ #include #include #include -#include "open_pic.h" +#include +#include #ifdef CONFIG_FB #include #endif -/* - * Properties whose value is longer than this get excluded from our - * copy of the device tree. This way we don't waste space storing - * things like "driver,AAPL,MacOS,PowerPC" properties. But this value - * does need to be big enough to ensure that we don't lose things - * like the interrupt-map property on a PCI-PCI bridge. - */ -#define MAX_PROPERTY_LENGTH 4096 - -struct prom_args { - const char *service; - int nargs; - int nret; - void *args[10]; -}; - struct pci_address { unsigned a_hi; unsigned a_mid; @@ -68,26 +56,12 @@ unsigned size_lo; }; -struct pci_range { - struct pci_address addr; - unsigned phys; - unsigned size_hi; - unsigned size_lo; -}; - struct isa_reg_property { unsigned space; unsigned address; unsigned size; }; -struct pci_intr_map { - struct pci_address addr; - unsigned dunno; - phandle int_ctrler; - unsigned intr; -}; - typedef unsigned long interpret_func(struct device_node *, unsigned long, int, int); static interpret_func interpret_pci_props; @@ -96,27 +70,7 @@ static interpret_func interpret_macio_props; static interpret_func interpret_root_props; -#ifndef FB_MAX /* avoid pulling in all of the fb stuff */ -#define FB_MAX 8 -#endif -char *prom_display_paths[FB_MAX] __initdata = { 0, }; -phandle prom_display_nodes[FB_MAX] __initdata; -unsigned int prom_num_displays __initdata = 0; -char *of_stdout_device __initdata = 0; -ihandle prom_disp_node __initdata = 0; - -prom_entry prom __initdata = 0; -ihandle prom_chosen __initdata = 0; -ihandle prom_stdout __initdata = 0; - extern char *klimit; -char *bootpath; -char *bootdevice; - -unsigned int rtas_data; /* physical pointer */ -unsigned int rtas_entry; /* physical pointer */ -unsigned int rtas_size; -unsigned int old_rtas; /* Set for a newworld or CHRP machine */ int use_of_interrupt_tree; @@ -125,553 +79,30 @@ int pmac_newworld; -static struct device_node *allnodes; +extern unsigned int rtas_entry; /* physical pointer */ + +extern struct device_node *allnodes; -static void *call_prom(const char *service, int nargs, int nret, ...); -static void prom_exit(void); -static unsigned long copy_device_tree(unsigned long, unsigned long); -static unsigned long inspect_node(phandle, struct device_node *, unsigned long, - unsigned long, struct device_node ***); static unsigned long finish_node(struct device_node *, unsigned long, interpret_func *, int, int); static unsigned long finish_node_interrupts(struct device_node *, unsigned long); -static unsigned long check_display(unsigned long); -static int prom_next_node(phandle *); -static void *early_get_property(unsigned long, unsigned long, char *); static struct device_node *find_phandle(phandle); -#ifdef CONFIG_BOOTX_TEXT -static void setup_disp_fake_bi(ihandle dp); -#endif - extern void enter_rtas(void *); void phys_call_rtas(int, int, int, ...); extern char cmd_line[512]; /* XXX */ -boot_infos_t *boot_infos; +extern boot_infos_t *boot_infos; unsigned long dev_tree_size; -#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) - -/* Is boot-info compatible ? */ -#define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION) -#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) -#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) - -/* - * Note that prom_init() and anything called from prom_init() must - * use the RELOC/PTRRELOC macros to access any static data in - * memory, since the kernel may be running at an address that is - * different from the address that it was linked at. - * (Note that strings count as static variables.) - */ - -static void __init -prom_exit() -{ - struct prom_args args; - unsigned long offset = reloc_offset(); - - args.service = "exit"; - args.nargs = 0; - args.nret = 0; - RELOC(prom)(&args); - for (;;) /* should never get here */ - ; -} - -void __init -prom_enter(void) -{ - struct prom_args args; - unsigned long offset = reloc_offset(); - - args.service = RELOC("enter"); - args.nargs = 0; - args.nret = 0; - RELOC(prom)(&args); -} - -static void * __init -call_prom(const char *service, int nargs, int nret, ...) -{ - va_list list; - int i; - unsigned long offset = reloc_offset(); - struct prom_args prom_args; - - prom_args.service = service; - prom_args.nargs = nargs; - prom_args.nret = nret; - va_start(list, nret); - for (i = 0; i < nargs; ++i) - prom_args.args[i] = va_arg(list, void *); - va_end(list); - for (i = 0; i < nret; ++i) - prom_args.args[i + nargs] = 0; - RELOC(prom)(&prom_args); - return prom_args.args[nargs]; -} - -void __init -prom_print(const char *msg) -{ - const char *p, *q; - unsigned long offset = reloc_offset(); - - if (RELOC(prom_stdout) == 0) - return; - - for (p = msg; *p != 0; p = q) { - for (q = p; *q != 0 && *q != '\n'; ++q) - ; - if (q > p) - call_prom(RELOC("write"), 3, 1, RELOC(prom_stdout), - p, q - p); - if (*q != 0) { - ++q; - call_prom(RELOC("write"), 3, 1, RELOC(prom_stdout), - RELOC("\r\n"), 2); - } - } -} - -static void __init -prom_print_hex(unsigned int v) -{ - char buf[16]; - int i, c; - - for (i = 0; i < 8; ++i) { - c = (v >> ((7-i)*4)) & 0xf; - c += (c >= 10)? ('a' - 10): '0'; - buf[i] = c; - } - buf[i] = ' '; - buf[i+1] = 0; - prom_print(buf); -} - -unsigned long smp_chrp_cpu_nr __initdata = 0; - -#ifdef CONFIG_SMP -/* - * With CHRP SMP we need to use the OF to start the other - * processors so we can't wait until smp_boot_cpus (the OF is - * trashed by then) so we have to put the processors into - * a holding pattern controlled by the kernel (not OF) before - * we destroy the OF. - * - * This uses a chunk of high memory, puts some holding pattern - * code there and sends the other processors off to there until - * smp_boot_cpus tells them to do something. We do that by using - * physical address 0x0. The holding pattern checks that address - * until its cpu # is there, when it is that cpu jumps to - * __secondary_start(). smp_boot_cpus() takes care of setting those - * values. - * - * We also use physical address 0x4 here to tell when a cpu - * is in its holding pattern code. - * - * -- Cort - */ -static void __init -prom_hold_cpus(unsigned long mem) -{ - extern void __secondary_hold(void); - unsigned long i; - int cpu; - phandle node; - unsigned long offset = reloc_offset(); - char type[16], *path; - unsigned int reg; - - /* - * XXX: hack to make sure we're chrp, assume that if we're - * chrp we have a device_type property -- Cort - */ - node = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); - if ( (int)call_prom(RELOC("getprop"), 4, 1, node, - RELOC("device_type"),type, sizeof(type)) <= 0) - return; - - /* copy the holding pattern code to someplace safe (0) */ - /* the holding pattern is now within the first 0x100 - bytes of the kernel image -- paulus */ - memcpy((void *)0, (void *)(KERNELBASE + offset), 0x100); - flush_icache_range(0, 0x100); - - /* look for cpus */ - *(unsigned long *)(0x0) = 0; - asm volatile("dcbf 0,%0": : "r" (0) : "memory"); - for (node = 0; prom_next_node(&node); ) { - type[0] = 0; - call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), - type, sizeof(type)); - if (strcmp(type, RELOC("cpu")) != 0) - continue; - path = (char *) mem; - memset(path, 0, 256); - if ((int) call_prom(RELOC("package-to-path"), 3, 1, - node, path, 255) < 0) - continue; - reg = -1; - call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), - ®, sizeof(reg)); - cpu = RELOC(smp_chrp_cpu_nr)++; - RELOC(smp_hw_index)[cpu] = reg; - /* XXX: hack - don't start cpu 0, this cpu -- Cort */ - if (cpu == 0) - continue; - prom_print(RELOC("starting cpu ")); - prom_print(path); - *(ulong *)(0x4) = 0; - call_prom(RELOC("start-cpu"), 3, 0, node, - __pa(__secondary_hold), cpu); - prom_print(RELOC("...")); - for ( i = 0 ; (i < 10000) && (*(ulong *)(0x4) == 0); i++ ) - ; - if (*(ulong *)(0x4) == cpu) - prom_print(RELOC("ok\n")); - else { - prom_print(RELOC("failed: ")); - prom_print_hex(*(ulong *)0x4); - prom_print(RELOC("\n")); - } - } -} -#endif /* CONFIG_SMP */ - -void __init -bootx_init(unsigned long r4, unsigned long phys) -{ - boot_infos_t *bi = (boot_infos_t *) r4; - unsigned long space; - unsigned long ptr, x; - char *model; - unsigned long offset = reloc_offset(); - - RELOC(boot_infos) = PTRUNRELOC(bi); - if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) - bi->logicalDisplayBase = 0; - -#ifdef CONFIG_BOOTX_TEXT - btext_init(bi); - - /* - * Test if boot-info is compatible. Done only in config - * CONFIG_BOOTX_TEXT since there is nothing much we can do - * with an incompatible version, except display a message - * and eventually hang the processor... - * - * I'll try to keep enough of boot-info compatible in the - * future to always allow display of this message; - */ - if (!BOOT_INFO_IS_COMPATIBLE(bi)) { - btext_drawstring(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n")); - btext_flushscreen(); - } -#endif /* CONFIG_BOOTX_TEXT */ - - /* New BootX enters kernel with MMU off, i/os are not allowed - here. This hack will have been done by the boostrap anyway. - */ - if (bi->version < 4) { - /* - * XXX If this is an iMac, turn off the USB controller. - */ - model = (char *) early_get_property - (r4 + bi->deviceTreeOffset, 4, RELOC("model")); - if (model - && (strcmp(model, RELOC("iMac,1")) == 0 - || strcmp(model, RELOC("PowerMac1,1")) == 0)) { - out_le32((unsigned *)0x80880008, 1); /* XXX */ - } - } - - /* Move klimit to enclose device tree, args, ramdisk, etc... */ - if (bi->version < 5) { - space = bi->deviceTreeOffset + bi->deviceTreeSize; - if (bi->ramDisk) - space = bi->ramDisk + bi->ramDiskSize; - } else - space = bi->totalParamsSize; - RELOC(klimit) = PTRUNRELOC((char *) bi + space); - - /* New BootX will have flushed all TLBs and enters kernel with - MMU switched OFF, so this should not be useful anymore. - */ - if (bi->version < 4) { - /* - * Touch each page to make sure the PTEs for them - * are in the hash table - the aim is to try to avoid - * getting DSI exceptions while copying the kernel image. - */ - for (ptr = (KERNELBASE + offset) & PAGE_MASK; - ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) - x = *(volatile unsigned long *)ptr; - } - -#ifdef CONFIG_BOOTX_TEXT - /* - * Note that after we call prepare_disp_BAT, we can't do - * prom_draw*, flushscreen or clearscreen until we turn the MMU - * on, since prepare_disp_BAT sets disp_bi->logicalDisplayBase - * to a virtual address. - */ - btext_prepare_BAT(); -#endif -} - -#ifdef CONFIG_PPC64BRIDGE -/* - * Set up a hash table with a set of entries in it to map the - * first 64MB of RAM. This is used on 64-bit machines since - * some of them don't have BATs. - * We assume the PTE will fit in the primary PTEG. - */ - -static inline void make_pte(unsigned long htab, unsigned int hsize, - unsigned int va, unsigned int pa, int mode) -{ - unsigned int *pteg; - unsigned int hash, i, vsid; - - vsid = ((va >> 28) * 0x111) << 12; - hash = ((va ^ vsid) >> 5) & 0x7fff80; - pteg = (unsigned int *)(htab + (hash & (hsize - 1))); - for (i = 0; i < 8; ++i, pteg += 4) { - if ((pteg[1] & 1) == 0) { - pteg[1] = vsid | ((va >> 16) & 0xf80) | 1; - pteg[3] = pa | mode; - break; - } - } -} - -extern unsigned long _SDR1; -extern PTE *Hash; -extern unsigned long Hash_size; - -static void __init -prom_alloc_htab(void) -{ - unsigned int hsize; - unsigned long htab; - unsigned int addr; - unsigned long offset = reloc_offset(); - - /* - * Because of OF bugs we can't use the "claim" client - * interface to allocate memory for the hash table. - * This code is only used on 64-bit PPCs, and the only - * 64-bit PPCs at the moment are RS/6000s, and their - * OF is based at 0xc00000 (the 12M point), so we just - * arbitrarily use the 0x800000 - 0xc00000 region for the - * hash table. - * -- paulus. - */ -#ifdef CONFIG_POWER4 - hsize = 4 << 20; /* POWER4 has no BATs */ -#else - hsize = 2 << 20; -#endif /* CONFIG_POWER4 */ - htab = (8 << 20); - RELOC(Hash) = (void *)(htab + KERNELBASE); - RELOC(Hash_size) = hsize; - RELOC(_SDR1) = htab + __ilog2(hsize) - 18; - - /* - * Put in PTEs for the first 64MB of RAM - */ - cacheable_memzero((void *)htab, hsize); - for (addr = 0; addr < 0x4000000; addr += 0x1000) - make_pte(htab, hsize, addr + KERNELBASE, addr, - _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX); -} -#endif /* CONFIG_PPC64BRIDGE */ - -static void __init -prom_instantiate_rtas(void) -{ - ihandle prom_rtas; - unsigned int i; - struct prom_args prom_args; - unsigned long offset = reloc_offset(); - - prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas")); - if (prom_rtas == (void *) -1) - return; - - RELOC(rtas_size) = 0; - call_prom(RELOC("getprop"), 4, 1, prom_rtas, - RELOC("rtas-size"), &RELOC(rtas_size), sizeof(rtas_size)); - prom_print(RELOC("instantiating rtas")); - if (RELOC(rtas_size) == 0) { - RELOC(rtas_data) = 0; - } else { - /* - * Ask OF for some space for RTAS. - * Actually OF has bugs so we just arbitrarily - * use memory at the 6MB point. - */ - RELOC(rtas_data) = 6 << 20; - prom_print(RELOC(" at ")); - prom_print_hex(RELOC(rtas_data)); - } - - prom_rtas = call_prom(RELOC("open"), 1, 1, RELOC("/rtas")); - prom_print(RELOC("...")); - prom_args.service = RELOC("call-method"); - prom_args.nargs = 3; - prom_args.nret = 2; - prom_args.args[0] = RELOC("instantiate-rtas"); - prom_args.args[1] = prom_rtas; - prom_args.args[2] = (void *) RELOC(rtas_data); - RELOC(prom)(&prom_args); - i = 0; - if (prom_args.args[3] == 0) - i = (unsigned int)prom_args.args[4]; - RELOC(rtas_entry) = i; - if ((RELOC(rtas_entry) == -1) || (RELOC(rtas_entry) == 0)) - prom_print(RELOC(" failed\n")); - else - prom_print(RELOC(" done\n")); -} - -/* - * We enter here early on, when the Open Firmware prom is still - * handling exceptions and the MMU hash table for us. - */ -unsigned long __init -prom_init(int r3, int r4, prom_entry pp) -{ - unsigned long mem; - ihandle prom_mmu; - unsigned long offset = reloc_offset(); - int l; - char *p, *d; - unsigned long phys; - - /* Default */ - phys = offset + KERNELBASE; - - /* First get a handle for the stdout device */ - RELOC(prom) = pp; - RELOC(prom_chosen) = call_prom(RELOC("finddevice"), 1, 1, - RELOC("/chosen")); - if (RELOC(prom_chosen) == (void *)-1) - prom_exit(); - if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), - RELOC("stdout"), &RELOC(prom_stdout), - sizeof(prom_stdout)) <= 0) - prom_exit(); - - /* Get the full OF pathname of the stdout device */ - mem = (unsigned long) RELOC(klimit) + offset; - p = (char *) mem; - memset(p, 0, 256); - call_prom(RELOC("instance-to-path"), 3, 1, RELOC(prom_stdout), p, 255); - RELOC(of_stdout_device) = PTRUNRELOC(p); - mem += strlen(p) + 1; - - /* Get the boot device and translate it to a full OF pathname. */ - p = (char *) mem; - l = (int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), - RELOC("bootpath"), p, 1<<20); - if (l > 0) { - p[l] = 0; /* should already be null-terminated */ - RELOC(bootpath) = PTRUNRELOC(p); - mem += l + 1; - d = (char *) mem; - *d = 0; - call_prom(RELOC("canon"), 3, 1, p, d, 1<<20); - RELOC(bootdevice) = PTRUNRELOC(d); - mem = ALIGN(mem + strlen(d) + 1); - } - - prom_instantiate_rtas(); - -#ifdef CONFIG_PPC64BRIDGE - /* - * Find out how much memory we have and allocate a - * suitably-sized hash table. - */ - prom_alloc_htab(); -#endif - - mem = check_display(mem); - - prom_print(RELOC("copying OF device tree...")); - mem = copy_device_tree(mem, mem + (1<<20)); - prom_print(RELOC("done\n")); - -#ifdef CONFIG_SMP - prom_hold_cpus(mem); -#endif - - RELOC(klimit) = (char *) (mem - offset); - - /* If we are already running at 0xc0000000, we assume we were loaded by - * an OF bootloader which did set a BAT for us. This breaks OF translate - * so we force phys to be 0 - */ - if (offset == 0) - phys = 0; - else { - if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), - RELOC("mmu"), &prom_mmu, sizeof(prom_mmu)) <= 0) { - prom_print(RELOC(" no MMU found\n")); - } else { - int nargs; - struct prom_args prom_args; - nargs = 4; - prom_args.service = RELOC("call-method"); - prom_args.nargs = nargs; - prom_args.nret = 4; - prom_args.args[0] = RELOC("translate"); - prom_args.args[1] = prom_mmu; - prom_args.args[2] = (void *)(offset + KERNELBASE); - prom_args.args[3] = (void *)1; - RELOC(prom)(&prom_args); - - /* We assume the phys. address size is 3 cells */ - if (prom_args.args[nargs] != 0) - prom_print(RELOC(" (translate failed)\n")); - else - phys = (unsigned long)prom_args.args[nargs+3]; - } - } - -#ifdef CONFIG_BOOTX_TEXT - if (RELOC(prom_disp_node) != 0) - setup_disp_fake_bi(RELOC(prom_disp_node)); -#endif - - /* Use quiesce call to get OF to shut down any devices it's using */ - prom_print(RELOC("Calling quiesce ...\n")); - call_prom(RELOC("quiesce"), 0, 0); - -#ifdef CONFIG_BOOTX_TEXT - btext_prepare_BAT(); -#endif - - prom_print(RELOC("returning ")); - prom_print_hex(phys); - prom_print(RELOC(" from prom_init\n")); - RELOC(prom_stdout) = 0; - - return phys; -} - -void phys_call_rtas(int service, int nargs, int nret, ...) +void __openfirmware +phys_call_rtas(int service, int nargs, int nret, ...) { va_list list; union { unsigned long words[16]; double align; } u; - unsigned long offset = reloc_offset(); void (*rtas)(void *, unsigned long); int i; @@ -683,340 +114,8 @@ u.words[i+3] = va_arg(list, unsigned long); va_end(list); - rtas = (void (*)(void *, unsigned long)) RELOC(rtas_entry); - rtas(&u, RELOC(rtas_data)); -} - -static int __init -prom_set_color(ihandle ih, int i, int r, int g, int b) -{ - struct prom_args prom_args; - unsigned long offset = reloc_offset(); - - prom_args.service = RELOC("call-method"); - prom_args.nargs = 6; - prom_args.nret = 1; - prom_args.args[0] = RELOC("color!"); - prom_args.args[1] = ih; - prom_args.args[2] = (void *) i; - prom_args.args[3] = (void *) b; - prom_args.args[4] = (void *) g; - prom_args.args[5] = (void *) r; - RELOC(prom)(&prom_args); - return (int) prom_args.args[6]; -} - -/* - * If we have a display that we don't know how to drive, - * we will want to try to execute OF's open method for it - * later. However, OF will probably fall over if we do that - * we've taken over the MMU. - * So we check whether we will need to open the display, - * and if so, open it now. - */ -static unsigned long __init -check_display(unsigned long mem) -{ - phandle node; - ihandle ih; - int i; - unsigned long offset = reloc_offset(); - char type[16], *path; - static unsigned char default_colors[] = { - 0x00, 0x00, 0x00, - 0x00, 0x00, 0xaa, - 0x00, 0xaa, 0x00, - 0x00, 0xaa, 0xaa, - 0xaa, 0x00, 0x00, - 0xaa, 0x00, 0xaa, - 0xaa, 0xaa, 0x00, - 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, - 0x55, 0x55, 0xff, - 0x55, 0xff, 0x55, - 0x55, 0xff, 0xff, - 0xff, 0x55, 0x55, - 0xff, 0x55, 0xff, - 0xff, 0xff, 0x55, - 0xff, 0xff, 0xff - }; - - RELOC(prom_disp_node) = 0; - - for (node = 0; prom_next_node(&node); ) { - type[0] = 0; - call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), - type, sizeof(type)); - if (strcmp(type, RELOC("display")) != 0) - continue; - /* It seems OF doesn't null-terminate the path :-( */ - path = (char *) mem; - memset(path, 0, 256); - if ((int) call_prom(RELOC("package-to-path"), 3, 1, - node, path, 255) < 0) - continue; - - /* - * If this display is the device that OF is using for stdout, - * move it to the front of the list. - */ - mem += strlen(path) + 1; - i = RELOC(prom_num_displays)++; - if (RELOC(of_stdout_device) != 0 && i > 0 - && strcmp(PTRRELOC(RELOC(of_stdout_device)), path) == 0) { - for (; i > 0; --i) { - RELOC(prom_display_paths[i]) - = RELOC(prom_display_paths[i-1]); - RELOC(prom_display_nodes[i]) - = RELOC(prom_display_nodes[i-1]); - } - } - RELOC(prom_display_paths[i]) = PTRUNRELOC(path); - RELOC(prom_display_nodes[i]) = node; - if (i == 0) - RELOC(prom_disp_node) = node; - if (RELOC(prom_num_displays) >= FB_MAX) - break; - } - -try_again: - /* - * Open the first display and set its colormap. - */ - if (RELOC(prom_num_displays) > 0) { - path = PTRRELOC(RELOC(prom_display_paths[0])); - prom_print(RELOC("opening display ")); - prom_print(path); - ih = call_prom(RELOC("open"), 1, 1, path); - if (ih == 0 || ih == (ihandle) -1) { - prom_print(RELOC("... failed\n")); - for (i=1; i 0) - RELOC(prom_disp_node) = RELOC(prom_display_nodes[0]); - else - RELOC(prom_disp_node) = NULL; - goto try_again; - } else { - prom_print(RELOC("... ok\n")); - /* - * Setup a usable color table when the appropriate - * method is available. - * Should update this to use set-colors. - */ - for (i = 0; i < 32; i++) - if (prom_set_color(ih, i, RELOC(default_colors)[i*3], - RELOC(default_colors)[i*3+1], - RELOC(default_colors)[i*3+2]) != 0) - break; - -#ifdef CONFIG_FB - for (i = 0; i < LINUX_LOGO_COLORS; i++) - if (prom_set_color(ih, i + 32, - RELOC(linux_logo_red)[i], - RELOC(linux_logo_green)[i], - RELOC(linux_logo_blue)[i]) != 0) - break; -#endif /* CONFIG_FB */ - } - } - - return ALIGN(mem); -} - -/* This function will enable the early boot text when doing OF booting. This - * way, xmon output should work too - */ -#ifdef CONFIG_BOOTX_TEXT -static void __init -setup_disp_fake_bi(ihandle dp) -{ - int width = 640, height = 480, depth = 8, pitch; - unsigned address; - unsigned long offset = reloc_offset(); - struct pci_reg_property addrs[8]; - int i, naddrs; - char name[32]; - char *getprop = RELOC("getprop"); - - prom_print(RELOC("Initializing fake screen: ")); - - memset(name, 0, sizeof(name)); - call_prom(getprop, 4, 1, dp, RELOC("name"), name, sizeof(name)); - name[sizeof(name)-1] = 0; - prom_print(name); - prom_print(RELOC("\n")); - call_prom(getprop, 4, 1, dp, RELOC("width"), &width, sizeof(width)); - call_prom(getprop, 4, 1, dp, RELOC("height"), &height, sizeof(height)); - call_prom(getprop, 4, 1, dp, RELOC("depth"), &depth, sizeof(depth)); - pitch = width * ((depth + 7) / 8); - call_prom(getprop, 4, 1, dp, RELOC("linebytes"), - &pitch, sizeof(pitch)); - if (pitch == 1) - pitch = 0x1000; /* for strange IBM display */ - address = 0; - call_prom(getprop, 4, 1, dp, RELOC("address"), - &address, sizeof(address)); - if (address == 0) { - /* look for an assigned address with a size of >= 1MB */ - naddrs = (int) call_prom(getprop, 4, 1, dp, - RELOC("assigned-addresses"), - addrs, sizeof(addrs)); - naddrs /= sizeof(struct pci_reg_property); - for (i = 0; i < naddrs; ++i) { - if (addrs[i].size_lo >= (1 << 20)) { - address = addrs[i].addr.a_lo; - /* use the BE aperture if possible */ - if (addrs[i].size_lo >= (16 << 20)) - address += (8 << 20); - break; - } - } - if (address == 0) { - prom_print(RELOC("Failed to get address\n")); - return; - } - } - /* kludge for valkyrie */ - if (strcmp(name, RELOC("valkyrie")) == 0) - address += 0x1000; - - btext_setup_display(width, height, depth, pitch, address); -} -#endif - -static int __init -prom_next_node(phandle *nodep) -{ - phandle node; - unsigned long offset = reloc_offset(); - - if ((node = *nodep) != 0 - && (*nodep = call_prom(RELOC("child"), 1, 1, node)) != 0) - return 1; - if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) - return 1; - for (;;) { - if ((node = call_prom(RELOC("parent"), 1, 1, node)) == 0) - return 0; - if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) - return 1; - } -} - -/* - * Make a copy of the device tree from the PROM. - */ -static unsigned long __init -copy_device_tree(unsigned long mem_start, unsigned long mem_end) -{ - phandle root; - unsigned long new_start; - struct device_node **allnextp; - unsigned long offset = reloc_offset(); - - root = call_prom(RELOC("peer"), 1, 1, (phandle)0); - if (root == (phandle)0) { - prom_print(RELOC("couldn't get device tree root\n")); - prom_exit(); - } - allnextp = &RELOC(allnodes); - mem_start = ALIGN(mem_start); - new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp); - *allnextp = 0; - return new_start; -} - -static unsigned long __init -inspect_node(phandle node, struct device_node *dad, - unsigned long mem_start, unsigned long mem_end, - struct device_node ***allnextpp) -{ - int l; - phandle child; - struct device_node *np; - struct property *pp, **prev_propp; - char *prev_name, *namep; - unsigned char *valp; - unsigned long offset = reloc_offset(); - - np = (struct device_node *) mem_start; - mem_start += sizeof(struct device_node); - memset(np, 0, sizeof(*np)); - np->node = node; - **allnextpp = PTRUNRELOC(np); - *allnextpp = &np->allnext; - if (dad != 0) { - np->parent = PTRUNRELOC(dad); - /* we temporarily use the `next' field as `last_child'. */ - if (dad->next == 0) - dad->child = PTRUNRELOC(np); - else - dad->next->sibling = PTRUNRELOC(np); - dad->next = np; - } - - /* get and store all properties */ - prev_propp = &np->properties; - prev_name = RELOC(""); - for (;;) { - pp = (struct property *) mem_start; - namep = (char *) (pp + 1); - pp->name = PTRUNRELOC(namep); - if ((int) call_prom(RELOC("nextprop"), 3, 1, node, prev_name, - namep) <= 0) - break; - mem_start = ALIGN((unsigned long)namep + strlen(namep) + 1); - prev_name = namep; - valp = (unsigned char *) mem_start; - pp->value = PTRUNRELOC(valp); - pp->length = (int) - call_prom(RELOC("getprop"), 4, 1, node, namep, - valp, mem_end - mem_start); - if (pp->length < 0) - continue; -#ifdef MAX_PROPERTY_LENGTH - if (pp->length > MAX_PROPERTY_LENGTH) - continue; /* ignore this property */ -#endif - mem_start = ALIGN(mem_start + pp->length); - *prev_propp = PTRUNRELOC(pp); - prev_propp = &pp->next; - } - if (np->node != NULL) { - /* Add a "linux,phandle" property" */ - pp = (struct property *) mem_start; - *prev_propp = PTRUNRELOC(pp); - prev_propp = &pp->next; - namep = (char *) (pp + 1); - pp->name = PTRUNRELOC(namep); - strcpy(namep, RELOC("linux,phandle")); - mem_start = ALIGN((unsigned long)namep + strlen(namep) + 1); - pp->value = (unsigned char *) PTRUNRELOC(&np->node); - pp->length = sizeof(np->node); - } - *prev_propp = NULL; - - /* get the node's full name */ - l = (int) call_prom(RELOC("package-to-path"), 3, 1, node, - (char *) mem_start, mem_end - mem_start); - if (l >= 0) { - np->full_name = PTRUNRELOC((char *) mem_start); - *(char *)(mem_start + l) = 0; - mem_start = ALIGN(mem_start + l + 1); - } - - /* do all our children */ - child = call_prom(RELOC("child"), 1, 1, node); - while (child != (void *)0) { - mem_start = inspect_node(child, np, mem_start, mem_end, - allnextpp); - child = call_prom(RELOC("peer"), 1, 1, child); - } - - return mem_start; + rtas = (void (*)(void *, unsigned long)) rtas_entry; + rtas(&u, rtas_data); } /* @@ -1081,26 +180,6 @@ klimit = (char *) mem; } -/* - * early_get_property is used to access the device tree image prepared - * by BootX very early on, before the pointers in it have been relocated. - */ -static void * __init -early_get_property(unsigned long base, unsigned long node, char *prop) -{ - struct device_node *np = (struct device_node *)(base + node); - struct property *pp; - - for (pp = np->properties; pp != 0; pp = pp->next) { - pp = (struct property *) (base + (unsigned long)pp); - if (strcmp((char *)((unsigned long)pp->name + base), - prop) == 0) { - return (void *)((unsigned long)pp->value + base); - } - } - return 0; -} - static unsigned long __init finish_node(struct device_node *np, unsigned long mem_start, interpret_func *ifunc, int naddrc, int nsizec) @@ -1111,10 +190,15 @@ np->name = get_property(np, "name", 0); np->type = get_property(np, "device_type", 0); + if (!np->name) + np->name = ""; + if (!np->type) + np->type = ""; + /* get the device addresses and interrupts */ - if (ifunc != NULL) { + if (ifunc != NULL) mem_start = ifunc(np, mem_start, naddrc, nsizec); - } + if (use_of_interrupt_tree) mem_start = finish_node_interrupts(np, mem_start); @@ -1126,12 +210,6 @@ if (ip != NULL) nsizec = *ip; - /* the f50 sets the name to 'display' and 'compatible' to what we - * expect for the name -- Cort - */ - if (!strcmp(np->name, "display")) - np->name = get_property(np, "compatible", 0); - if (np->parent == NULL) ifunc = interpret_root_props; else if (np->type == 0) @@ -1145,6 +223,8 @@ ifunc = interpret_macio_props; else if (!strcmp(np->type, "isa")) ifunc = interpret_isa_props; + else if (!strcmp(np->name, "uni-n")) + ifunc = interpret_root_props; else if (!((ifunc == interpret_dbdma_props || ifunc == interpret_macio_props) && (!strcmp(np->type, "escc") @@ -1508,7 +588,7 @@ i = 0; adr = (struct address_range *) mem_start; while ((l -= sizeof(struct reg_property)) >= 0) { - adr[i].space = 0; + adr[i].space = 2; adr[i].address = rp[i].address + base_address; adr[i].size = rp[i].size; ++i; @@ -1544,14 +624,13 @@ struct reg_property *rp; struct address_range *adr; unsigned long base_address; - int i, l, keylargo, *ip; + int i, l, *ip; struct device_node *db; base_address = 0; for (db = np->parent; db != NULL; db = db->parent) { if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { base_address = db->addrs[0].address; - keylargo = device_is_compatible(db, "Keylargo"); break; } } @@ -1561,7 +640,7 @@ i = 0; adr = (struct address_range *) mem_start; while ((l -= sizeof(struct reg_property)) >= 0) { - adr[i].space = 0; + adr[i].space = 2; adr[i].address = rp[i].address + base_address; adr[i].size = rp[i].size; ++i; @@ -1616,7 +695,7 @@ if (use_of_interrupt_tree) return mem_start; - + ip = (int *) get_property(np, "interrupts", &l); if (ip != 0) { np->intrs = (struct interrupt_info *) mem_start; @@ -1645,7 +724,7 @@ i = 0; adr = (struct address_range *) mem_start; while ((l -= rpsize) >= 0) { - adr[i].space = (naddrc >= 2? rp[naddrc-2]: 0); + adr[i].space = (naddrc >= 2? rp[naddrc-2]: 2); adr[i].address = rp[naddrc - 1]; adr[i].size = rp[naddrc + nsizec - 1]; ++i; @@ -1786,7 +865,7 @@ machine_is_compatible(const char *compat) { struct device_node *root; - + root = find_path_device("/"); if (root == 0) return 0; @@ -1870,12 +949,178 @@ { struct property **next = &np->properties; - prop->next = NULL; + prop->next = NULL; while (*next) next = &(*next)->next; *next = prop; } +/* I quickly hacked that one, check against spec ! */ +static inline unsigned long __openfirmware +bus_space_to_resource_flags(unsigned int bus_space) +{ + u8 space = (bus_space >> 24) & 0xf; + if (space == 0) + space = 0x02; + if (space == 0x02) + return IORESOURCE_MEM; + else if (space == 0x01) + return IORESOURCE_IO; + else { + printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n", + bus_space); + return 0; + } +} + +static struct resource* __openfirmware +find_parent_pci_resource(struct pci_dev* pdev, struct address_range *range) +{ + unsigned long mask; + int i; + + /* Check this one */ + mask = bus_space_to_resource_flags(range->space); + for (i=0; iresource[i].flags & mask) == mask && + pdev->resource[i].start <= range->address && + pdev->resource[i].end > range->address) { + if ((range->address + range->size - 1) > pdev->resource[i].end) { + /* Add better message */ + printk(KERN_WARNING "PCI/OF resource overlap !\n"); + return NULL; + } + break; + } + } + if (i == DEVICE_COUNT_RESOURCE) + return NULL; + return &pdev->resource[i]; +} + +/* + * Request an OF device resource. Currently handles child of PCI devices, + * or other nodes attached to the root node. Ultimately, put some + * link to resources in the OF node. + * WARNING: out_resource->name should be initialized before calling this + * function. + */ +struct resource* __openfirmware +request_OF_resource(struct device_node* node, int index, const char* name_postfix) +{ + struct pci_dev* pcidev; + u8 pci_bus, pci_devfn; + unsigned long iomask; + struct device_node* nd; + struct resource* parent; + struct resource *res = NULL; + int nlen, plen; + + if (index >= node->n_addrs) + goto fail; + + /* Sanity check on bus space */ + iomask = bus_space_to_resource_flags(node->addrs[index].space); + if (iomask & IORESOURCE_MEM) + parent = &iomem_resource; + else if (iomask & IORESOURCE_IO) + parent = &ioport_resource; + else + goto fail; + + /* Find a PCI parent if any */ + nd = node; + pcidev = NULL; + while(nd) { + if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) + pcidev = pci_find_slot(pci_bus, pci_devfn); + if (pcidev) break; + nd = nd->parent; + } + if (pcidev) + parent = find_parent_pci_resource(pcidev, &node->addrs[index]); + if (!parent) { + printk(KERN_WARNING "request_OF_resource(%s), parent not found\n", + node->name); + goto fail; + } + + res = __request_region(parent, node->addrs[index].address, node->addrs[index].size, NULL); + if (!res) + goto fail; + nlen = strlen(node->name); + plen = name_postfix ? strlen(name_postfix) : 0; + res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL); + if (res->name) { + strcpy((char *)res->name, node->name); + if (plen) + strcpy((char *)res->name+nlen, name_postfix); + } + return res; +fail: + return NULL; +} + +int __openfirmware +release_OF_resource(struct device_node* node, int index) +{ + struct pci_dev* pcidev; + u8 pci_bus, pci_devfn; + unsigned long iomask; + struct device_node* nd; + struct resource* parent; + struct resource *res = NULL; + + if (index >= node->n_addrs) + return -EINVAL; + + /* Sanity check on bus space */ + iomask = bus_space_to_resource_flags(node->addrs[index].space); + if (iomask & IORESOURCE_MEM) + parent = &iomem_resource; + else if (iomask & IORESOURCE_IO) + parent = &ioport_resource; + else + return -EINVAL; + + /* Find a PCI parent if any */ + nd = node; + pcidev = NULL; + while(nd) { + if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) + pcidev = pci_find_slot(pci_bus, pci_devfn); + if (pcidev) break; + nd = nd->parent; + } + if (pcidev) + parent = find_parent_pci_resource(pcidev, &node->addrs[index]); + if (!parent) { + printk(KERN_WARNING "request_OF_resource(%s), parent not found\n", + node->name); + return -ENODEV; + } + + /* Find us in the parent */ + res = parent->child; + while (res) { + if (res->start == node->addrs[index].address && + res->end == (res->start + node->addrs[index].size - 1)) + break; + res = res->sibling; + } + if (!res) + return -ENODEV; + + if (res->name) { + kfree(res->name); + res->name = NULL; + } + release_resource(res); + kfree(res); + + return 0; +} + #if 0 void __openfirmware print_properties(struct device_node *np) @@ -1961,11 +1206,11 @@ u.words[i+3] = va_arg(list, unsigned long); va_end(list); - /* Shouldn't we enable kernel FP here ? enter_rtas will play - * with MSR_FE0|MSR_FE1|MSR_FP so I assume rtas might use - * floating points. If that's the case, then we need to make - * sure any lazy FP context is backed up - * --BenH + /* + * RTAS doesn't use floating point. + * Or at least, according to the CHRP spec we enter RTAS + * with FP disabled, and it doesn't change the FP registers. + * -- paulus. */ spin_lock_irqsave(&rtas_lock, s); enter_rtas((void *)__pa(&u)); @@ -1975,14 +1220,4 @@ for (i = 0; i < nret-1; ++i) outputs[i] = u.words[i+nargs+4]; return u.words[nargs+3]; -} - -void __init -abort() -{ -#ifdef CONFIG_XMON - xmon(NULL); -#endif - for (;;) - prom_exit(); } diff -Nru a/arch/ppc/kernel/prom_init.c b/arch/ppc/kernel/prom_init.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/prom_init.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,899 @@ +/* + * Note that prom_init() and anything called from prom_init() + * may be running at an address that is different from the address + * that it was linked at. References to static data items are + * handled by compiling this file with -mrelocatable-lib. + */ + +#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 + +#ifdef CONFIG_FB +#include +#endif + +/* + * Properties whose value is longer than this get excluded from our + * copy of the device tree. This way we don't waste space storing + * things like "driver,AAPL,MacOS,PowerPC" properties. But this value + * does need to be big enough to ensure that we don't lose things + * like the interrupt-map property on a PCI-PCI bridge. + */ +#define MAX_PROPERTY_LENGTH 4096 + +#ifndef FB_MAX /* avoid pulling in all of the fb stuff */ +#define FB_MAX 8 +#endif + +#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) + +struct prom_args { + const char *service; + int nargs; + int nret; + void *args[10]; +}; + +struct pci_address { + unsigned a_hi; + unsigned a_mid; + unsigned a_lo; +}; + +struct pci_reg_property { + struct pci_address addr; + unsigned size_hi; + unsigned size_lo; +}; + +struct pci_range { + struct pci_address addr; + unsigned phys; + unsigned size_hi; + unsigned size_lo; +}; + +struct isa_reg_property { + unsigned space; + unsigned address; + unsigned size; +}; + +struct pci_intr_map { + struct pci_address addr; + unsigned dunno; + phandle int_ctrler; + unsigned intr; +}; + +static void prom_exit(void); +static void *call_prom(const char *service, int nargs, int nret, ...); +static void *call_prom_ret(const char *service, int nargs, int nret, + void **rets, ...); +static void prom_print_hex(unsigned int v); +static int prom_set_color(ihandle ih, int i, int r, int g, int b); +static int prom_next_node(phandle *nodep); +static unsigned long check_display(unsigned long mem); +static void setup_disp_fake_bi(ihandle dp); +static unsigned long copy_device_tree(unsigned long mem_start, + unsigned long mem_end); +static unsigned long inspect_node(phandle node, struct device_node *dad, + unsigned long mem_start, unsigned long mem_end, + struct device_node ***allnextpp); +static void prom_hold_cpus(unsigned long mem); +static void prom_instantiate_rtas(void); +static void * early_get_property(unsigned long base, unsigned long node, + char *prop); + +prom_entry prom __initdata = 0; +ihandle prom_chosen __initdata = 0; +ihandle prom_stdout __initdata = 0; + +char *prom_display_paths[FB_MAX] __initdata = { 0, }; +phandle prom_display_nodes[FB_MAX] __initdata; +unsigned int prom_num_displays __initdata = 0; +static char *of_stdout_device __initdata = 0; +static ihandle prom_disp_node __initdata = 0; + +unsigned int rtas_data; /* physical pointer */ +unsigned int rtas_entry; /* physical pointer */ +unsigned int rtas_size; +unsigned int old_rtas; + +boot_infos_t *boot_infos; +char *bootpath; +char *bootdevice; +struct device_node *allnodes; + +extern char *klimit; +extern char _stext; + +static void __init +prom_exit(void) +{ + struct prom_args args; + + args.service = "exit"; + args.nargs = 0; + args.nret = 0; + prom(&args); + for (;;) /* should never get here */ + ; +} + +static void * __init +call_prom(const char *service, int nargs, int nret, ...) +{ + va_list list; + int i; + struct prom_args prom_args; + + prom_args.service = service; + prom_args.nargs = nargs; + prom_args.nret = nret; + va_start(list, nret); + for (i = 0; i < nargs; ++i) + prom_args.args[i] = va_arg(list, void *); + va_end(list); + for (i = 0; i < nret; ++i) + prom_args.args[i + nargs] = 0; + prom(&prom_args); + return prom_args.args[nargs]; +} + +static void * __init +call_prom_ret(const char *service, int nargs, int nret, void **rets, ...) +{ + va_list list; + int i; + struct prom_args prom_args; + + prom_args.service = service; + prom_args.nargs = nargs; + prom_args.nret = nret; + va_start(list, rets); + for (i = 0; i < nargs; ++i) + prom_args.args[i] = va_arg(list, void *); + va_end(list); + for (i = 0; i < nret; ++i) + prom_args.args[i + nargs] = 0; + prom(&prom_args); + for (i = 1; i < nret; ++i) + rets[i-1] = prom_args.args[nargs + i]; + return prom_args.args[nargs]; +} + +void __init +prom_print(const char *msg) +{ + const char *p, *q; + + if (prom_stdout == 0) + return; + + for (p = msg; *p != 0; p = q) { + for (q = p; *q != 0 && *q != '\n'; ++q) + ; + if (q > p) + call_prom("write", 3, 1, prom_stdout, p, q - p); + if (*q != 0) { + ++q; + call_prom("write", 3, 1, prom_stdout, "\r\n", 2); + } + } +} + +static void __init +prom_print_hex(unsigned int v) +{ + char buf[16]; + int i, c; + + for (i = 0; i < 8; ++i) { + c = (v >> ((7-i)*4)) & 0xf; + c += (c >= 10)? ('a' - 10): '0'; + buf[i] = c; + } + buf[i] = ' '; + buf[i+1] = 0; + prom_print(buf); +} + +static int __init +prom_set_color(ihandle ih, int i, int r, int g, int b) +{ + struct prom_args prom_args; + + prom_args.service = "call-method"; + prom_args.nargs = 6; + prom_args.nret = 1; + prom_args.args[0] = "color!"; + prom_args.args[1] = ih; + prom_args.args[2] = (void *) i; + prom_args.args[3] = (void *) b; + prom_args.args[4] = (void *) g; + prom_args.args[5] = (void *) r; + prom(&prom_args); + return (int) prom_args.args[6]; +} + +static int __init +prom_next_node(phandle *nodep) +{ + phandle node; + + if ((node = *nodep) != 0 + && (*nodep = call_prom("child", 1, 1, node)) != 0) + return 1; + if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + return 1; + for (;;) { + if ((node = call_prom("parent", 1, 1, node)) == 0) + return 0; + if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + return 1; + } +} + +/* + * If we have a display that we don't know how to drive, + * we will want to try to execute OF's open method for it + * later. However, OF will probably fall over if we do that + * we've taken over the MMU. + * So we check whether we will need to open the display, + * and if so, open it now. + */ +static unsigned long __init +check_display(unsigned long mem) +{ + phandle node; + ihandle ih; + int i; + char type[16], *path; + static unsigned char default_colors[] = { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, + 0x00, 0xaa, 0x00, + 0x00, 0xaa, 0xaa, + 0xaa, 0x00, 0x00, + 0xaa, 0x00, 0xaa, + 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, + 0x55, 0x55, 0xff, + 0x55, 0xff, 0x55, + 0x55, 0xff, 0xff, + 0xff, 0x55, 0x55, + 0xff, 0x55, 0xff, + 0xff, 0xff, 0x55, + 0xff, 0xff, 0xff + }; + + prom_disp_node = 0; + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom("getprop", 4, 1, node, "device_type", + type, sizeof(type)); + if (strcmp(type, "display") != 0) + continue; + /* It seems OF doesn't null-terminate the path :-( */ + path = (char *) mem; + memset(path, 0, 256); + if ((int) call_prom("package-to-path", 3, 1, + node, path, 255) < 0) + continue; + + /* + * If this display is the device that OF is using for stdout, + * move it to the front of the list. + */ + mem += strlen(path) + 1; + i = prom_num_displays++; + if (of_stdout_device != 0 && i > 0 + && strcmp(of_stdout_device, path) == 0) { + for (; i > 0; --i) { + prom_display_paths[i] + = prom_display_paths[i-1]; + prom_display_nodes[i] + = prom_display_nodes[i-1]; + } + } + prom_display_paths[i] = path; + prom_display_nodes[i] = node; + if (i == 0) + prom_disp_node = node; + if (prom_num_displays >= FB_MAX) + break; + } + +try_again: + /* + * Open the first display and set its colormap. + */ + if (prom_num_displays > 0) { + path = prom_display_paths[0]; + prom_print("opening display "); + prom_print(path); + ih = call_prom("open", 1, 1, path); + if (ih == 0 || ih == (ihandle) -1) { + prom_print("... failed\n"); + for (i=1; i 0) + prom_disp_node = prom_display_nodes[0]; + else + prom_disp_node = NULL; + goto try_again; + } else { + prom_print("... ok\n"); + /* + * Setup a usable color table when the appropriate + * method is available. + * Should update this to use set-colors. + */ + for (i = 0; i < 32; i++) + if (prom_set_color(ih, i, default_colors[i*3], + default_colors[i*3+1], + default_colors[i*3+2]) != 0) + break; + +#ifdef CONFIG_FB + for (i = 0; i < LINUX_LOGO_COLORS; i++) + if (prom_set_color(ih, i + 32, + linux_logo_red[i], + linux_logo_green[i], + linux_logo_blue[i]) != 0) + break; +#endif /* CONFIG_FB */ + } + } + + return ALIGN(mem); +} + +/* This function will enable the early boot text when doing OF booting. This + * way, xmon output should work too + */ +static void __init +setup_disp_fake_bi(ihandle dp) +{ +#ifdef CONFIG_BOOTX_TEXT + int width = 640, height = 480, depth = 8, pitch; + unsigned address; + struct pci_reg_property addrs[8]; + int i, naddrs; + char name[32]; + char *getprop = "getprop"; + + prom_print("Initializing fake screen: "); + + memset(name, 0, sizeof(name)); + call_prom(getprop, 4, 1, dp, "name", name, sizeof(name)); + name[sizeof(name)-1] = 0; + prom_print(name); + prom_print("\n"); + call_prom(getprop, 4, 1, dp, "width", &width, sizeof(width)); + call_prom(getprop, 4, 1, dp, "height", &height, sizeof(height)); + call_prom(getprop, 4, 1, dp, "depth", &depth, sizeof(depth)); + pitch = width * ((depth + 7) / 8); + call_prom(getprop, 4, 1, dp, "linebytes", + &pitch, sizeof(pitch)); + if (pitch == 1) + pitch = 0x1000; /* for strange IBM display */ + address = 0; + call_prom(getprop, 4, 1, dp, "address", + &address, sizeof(address)); + if (address == 0) { + /* look for an assigned address with a size of >= 1MB */ + naddrs = (int) call_prom(getprop, 4, 1, dp, + "assigned-addresses", + addrs, sizeof(addrs)); + naddrs /= sizeof(struct pci_reg_property); + for (i = 0; i < naddrs; ++i) { + if (addrs[i].size_lo >= (1 << 20)) { + address = addrs[i].addr.a_lo; + /* use the BE aperture if possible */ + if (addrs[i].size_lo >= (16 << 20)) + address += (8 << 20); + break; + } + } + if (address == 0) { + prom_print("Failed to get address\n"); + return; + } + } + /* kludge for valkyrie */ + if (strcmp(name, "valkyrie") == 0) + address += 0x1000; + + btext_setup_display(width, height, depth, pitch, address); + + btext_prepare_BAT(); +#endif /* CONFIG_BOOTX_TEXT */ +} + +/* + * Make a copy of the device tree from the PROM. + */ +static unsigned long __init +copy_device_tree(unsigned long mem_start, unsigned long mem_end) +{ + phandle root; + unsigned long new_start; + struct device_node **allnextp; + + root = call_prom("peer", 1, 1, (phandle)0); + if (root == (phandle)0) { + prom_print("couldn't get device tree root\n"); + prom_exit(); + } + allnextp = &allnodes; + mem_start = ALIGN(mem_start); + new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp); + *allnextp = 0; + return new_start; +} + +static unsigned long __init +inspect_node(phandle node, struct device_node *dad, + unsigned long mem_start, unsigned long mem_end, + struct device_node ***allnextpp) +{ + int l; + phandle child; + struct device_node *np; + struct property *pp, **prev_propp; + char *prev_name, *namep; + unsigned char *valp; + + np = (struct device_node *) mem_start; + mem_start += sizeof(struct device_node); + memset(np, 0, sizeof(*np)); + np->node = node; + **allnextpp = PTRUNRELOC(np); + *allnextpp = &np->allnext; + if (dad != 0) { + np->parent = PTRUNRELOC(dad); + /* we temporarily use the `next' field as `last_child'. */ + if (dad->next == 0) + dad->child = PTRUNRELOC(np); + else + dad->next->sibling = PTRUNRELOC(np); + dad->next = np; + } + + /* get and store all properties */ + prev_propp = &np->properties; + prev_name = ""; + for (;;) { + pp = (struct property *) mem_start; + namep = (char *) (pp + 1); + pp->name = PTRUNRELOC(namep); + if ((int) call_prom("nextprop", 3, 1, node, prev_name, + namep) <= 0) + break; + mem_start = ALIGN((unsigned long)namep + strlen(namep) + 1); + prev_name = namep; + valp = (unsigned char *) mem_start; + pp->value = PTRUNRELOC(valp); + pp->length = (int) + call_prom("getprop", 4, 1, node, namep, + valp, mem_end - mem_start); + if (pp->length < 0) + continue; +#ifdef MAX_PROPERTY_LENGTH + if (pp->length > MAX_PROPERTY_LENGTH) + continue; /* ignore this property */ +#endif + mem_start = ALIGN(mem_start + pp->length); + *prev_propp = PTRUNRELOC(pp); + prev_propp = &pp->next; + } + if (np->node != NULL) { + /* Add a "linux,phandle" property" */ + pp = (struct property *) mem_start; + *prev_propp = PTRUNRELOC(pp); + prev_propp = &pp->next; + namep = (char *) (pp + 1); + pp->name = PTRUNRELOC(namep); + strcpy(namep, "linux,phandle"); + mem_start = ALIGN((unsigned long)namep + strlen(namep) + 1); + pp->value = (unsigned char *) PTRUNRELOC(&np->node); + pp->length = sizeof(np->node); + } + *prev_propp = NULL; + + /* get the node's full name */ + l = (int) call_prom("package-to-path", 3, 1, node, + (char *) mem_start, mem_end - mem_start); + if (l >= 0) { + np->full_name = PTRUNRELOC((char *) mem_start); + *(char *)(mem_start + l) = 0; + mem_start = ALIGN(mem_start + l + 1); + } + + /* do all our children */ + child = call_prom("child", 1, 1, node); + while (child != (void *)0) { + mem_start = inspect_node(child, np, mem_start, mem_end, + allnextpp); + child = call_prom("peer", 1, 1, child); + } + + return mem_start; +} + +unsigned long smp_chrp_cpu_nr __initdata = 0; + +/* + * With CHRP SMP we need to use the OF to start the other + * processors so we can't wait until smp_boot_cpus (the OF is + * trashed by then) so we have to put the processors into + * a holding pattern controlled by the kernel (not OF) before + * we destroy the OF. + * + * This uses a chunk of high memory, puts some holding pattern + * code there and sends the other processors off to there until + * smp_boot_cpus tells them to do something. We do that by using + * physical address 0x0. The holding pattern checks that address + * until its cpu # is there, when it is that cpu jumps to + * __secondary_start(). smp_boot_cpus() takes care of setting those + * values. + * + * We also use physical address 0x4 here to tell when a cpu + * is in its holding pattern code. + * + * -- Cort + * + * Note that we have to do this if we have more than one CPU, + * even if this is a UP kernel. Otherwise when we trash OF + * the other CPUs will start executing some random instructions + * and crash the system. -- paulus + */ +static void __init +prom_hold_cpus(unsigned long mem) +{ + extern void __secondary_hold(void); + unsigned long i; + int cpu; + phandle node; + char type[16], *path; + unsigned int reg; + + /* + * XXX: hack to make sure we're chrp, assume that if we're + * chrp we have a device_type property -- Cort + */ + node = call_prom("finddevice", 1, 1, "/"); + if ((int)call_prom("getprop", 4, 1, node, + "device_type",type, sizeof(type)) <= 0) + return; + + /* copy the holding pattern code to someplace safe (0) */ + /* the holding pattern is now within the first 0x100 + bytes of the kernel image -- paulus */ + memcpy((void *)0, &_stext, 0x100); + flush_icache_range(0, 0x100); + + /* look for cpus */ + *(unsigned long *)(0x0) = 0; + asm volatile("dcbf 0,%0": : "r" (0) : "memory"); + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom("getprop", 4, 1, node, "device_type", + type, sizeof(type)); + if (strcmp(type, "cpu") != 0) + continue; + path = (char *) mem; + memset(path, 0, 256); + if ((int) call_prom("package-to-path", 3, 1, + node, path, 255) < 0) + continue; + reg = -1; + call_prom("getprop", 4, 1, node, "reg", ®, sizeof(reg)); + cpu = smp_chrp_cpu_nr++; +#ifdef CONFIG_SMP + smp_hw_index[cpu] = reg; +#endif /* CONFIG_SMP */ + /* XXX: hack - don't start cpu 0, this cpu -- Cort */ + if (cpu == 0) + continue; + prom_print("starting cpu "); + prom_print(path); + *(ulong *)(0x4) = 0; + call_prom("start-cpu", 3, 0, node, + (char *)__secondary_hold - &_stext, cpu); + prom_print("..."); + for ( i = 0 ; (i < 10000) && (*(ulong *)(0x4) == 0); i++ ) + ; + if (*(ulong *)(0x4) == cpu) + prom_print("ok\n"); + else { + prom_print("failed: "); + prom_print_hex(*(ulong *)0x4); + prom_print("\n"); + } + } +} + +static void __init +prom_instantiate_rtas(void) +{ + ihandle prom_rtas; + unsigned int i; + struct prom_args prom_args; + + prom_rtas = call_prom("finddevice", 1, 1, "/rtas"); + if (prom_rtas == (void *) -1) + return; + + rtas_size = 0; + call_prom("getprop", 4, 1, prom_rtas, + "rtas-size", &rtas_size, sizeof(rtas_size)); + prom_print("instantiating rtas"); + if (rtas_size == 0) { + rtas_data = 0; + } else { + /* + * Ask OF for some space for RTAS. + * Actually OF has bugs so we just arbitrarily + * use memory at the 6MB point. + */ + rtas_data = 6 << 20; + prom_print(" at "); + prom_print_hex(rtas_data); + } + + prom_rtas = call_prom("open", 1, 1, "/rtas"); + prom_print("..."); + prom_args.service = "call-method"; + prom_args.nargs = 3; + prom_args.nret = 2; + prom_args.args[0] = "instantiate-rtas"; + prom_args.args[1] = prom_rtas; + prom_args.args[2] = (void *) rtas_data; + prom(&prom_args); + i = 0; + if (prom_args.args[3] == 0) + i = (unsigned int)prom_args.args[4]; + rtas_entry = i; + if ((rtas_entry == -1) || (rtas_entry == 0)) + prom_print(" failed\n"); + else + prom_print(" done\n"); +} + +/* + * We enter here early on, when the Open Firmware prom is still + * handling exceptions and the MMU hash table for us. + */ +unsigned long __init +prom_init(int r3, int r4, prom_entry pp) +{ + unsigned long mem; + ihandle prom_mmu; + unsigned long offset = reloc_offset(); + int i, l; + char *p, *d; + unsigned long phys; + void *result[3]; + + /* Default */ + phys = (unsigned long) &_stext; + + /* First get a handle for the stdout device */ + prom = pp; + prom_chosen = call_prom("finddevice", 1, 1, + "/chosen"); + if (prom_chosen == (void *)-1) + prom_exit(); + if ((int) call_prom("getprop", 4, 1, prom_chosen, + "stdout", &prom_stdout, + sizeof(prom_stdout)) <= 0) + prom_exit(); + + /* Get the full OF pathname of the stdout device */ + mem = (unsigned long) klimit + offset; + p = (char *) mem; + memset(p, 0, 256); + call_prom("instance-to-path", 3, 1, prom_stdout, p, 255); + of_stdout_device = p; + mem += strlen(p) + 1; + + /* Get the boot device and translate it to a full OF pathname. */ + p = (char *) mem; + l = (int) call_prom("getprop", 4, 1, prom_chosen, + "bootpath", p, 1<<20); + if (l > 0) { + p[l] = 0; /* should already be null-terminated */ + bootpath = PTRUNRELOC(p); + mem += l + 1; + d = (char *) mem; + *d = 0; + call_prom("canon", 3, 1, p, d, 1<<20); + bootdevice = PTRUNRELOC(d); + mem = ALIGN(mem + strlen(d) + 1); + } + + prom_instantiate_rtas(); + + mem = check_display(mem); + + prom_print("copying OF device tree..."); + mem = copy_device_tree(mem, mem + (1<<20)); + prom_print("done\n"); + + prom_hold_cpus(mem); + + klimit = (char *) (mem - offset); + + /* If we are already running at 0xc0000000, we assume we were + * loaded by an OF bootloader which did set a BAT for us. + * This breaks OF translate so we force phys to be 0. + */ + if (offset == 0) + phys = 0; + else if ((int) call_prom("getprop", 4, 1, prom_chosen, "mmu", + &prom_mmu, sizeof(prom_mmu)) <= 0) { + prom_print(" no MMU found\n"); + } else if ((int)call_prom_ret("call-method", 4, 4, result, "translate", + prom_mmu, &_stext, 1) != 0) { + prom_print(" (translate failed)\n"); + } else { + /* We assume the phys. address size is 3 cells */ + phys = (unsigned long)result[2]; + } + + if (prom_disp_node != 0) + setup_disp_fake_bi(prom_disp_node); + + /* Use quiesce call to get OF to shut down any devices it's using */ + prom_print("Calling quiesce ...\n"); + call_prom("quiesce", 0, 0); + + /* Relocate various pointers which will be used once the + kernel is running at the address it was linked at. */ + for (i = 0; i < prom_num_displays; ++i) + prom_display_paths[i] = PTRUNRELOC(prom_display_paths[i]); + + prom_print("returning 0x"); + prom_print_hex(phys); + prom_print("from prom_init\n"); + prom_stdout = 0; + + return phys; +} + +/* + * early_get_property is used to access the device tree image prepared + * by BootX very early on, before the pointers in it have been relocated. + */ +static void * __init +early_get_property(unsigned long base, unsigned long node, char *prop) +{ + struct device_node *np = (struct device_node *)(base + node); + struct property *pp; + + for (pp = np->properties; pp != 0; pp = pp->next) { + pp = (struct property *) (base + (unsigned long)pp); + if (strcmp((char *)((unsigned long)pp->name + base), + prop) == 0) { + return (void *)((unsigned long)pp->value + base); + } + } + return 0; +} + +/* Is boot-info compatible ? */ +#define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION) +#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) +#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) + +void __init +bootx_init(unsigned long r4, unsigned long phys) +{ + boot_infos_t *bi = (boot_infos_t *) r4; + unsigned long space; + unsigned long ptr, x; + char *model; + + boot_infos = PTRUNRELOC(bi); + if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) + bi->logicalDisplayBase = 0; + +#ifdef CONFIG_BOOTX_TEXT + btext_init(bi); + + /* + * Test if boot-info is compatible. Done only in config + * CONFIG_BOOTX_TEXT since there is nothing much we can do + * with an incompatible version, except display a message + * and eventually hang the processor... + * + * I'll try to keep enough of boot-info compatible in the + * future to always allow display of this message; + */ + if (!BOOT_INFO_IS_COMPATIBLE(bi)) { + btext_drawstring(" !!! WARNING - Incompatible version of BootX !!!\n\n\n"); + btext_flushscreen(); + } +#endif /* CONFIG_BOOTX_TEXT */ + + /* New BootX enters kernel with MMU off, i/os are not allowed + here. This hack will have been done by the boostrap anyway. + */ + if (bi->version < 4) { + /* + * XXX If this is an iMac, turn off the USB controller. + */ + model = (char *) early_get_property + (r4 + bi->deviceTreeOffset, 4, "model"); + if (model + && (strcmp(model, "iMac,1") == 0 + || strcmp(model, "PowerMac1,1") == 0)) { + out_le32((unsigned *)0x80880008, 1); /* XXX */ + } + } + + /* Move klimit to enclose device tree, args, ramdisk, etc... */ + if (bi->version < 5) { + space = bi->deviceTreeOffset + bi->deviceTreeSize; + if (bi->ramDisk) + space = bi->ramDisk + bi->ramDiskSize; + } else + space = bi->totalParamsSize; + klimit = PTRUNRELOC((char *) bi + space); + + /* New BootX will have flushed all TLBs and enters kernel with + MMU switched OFF, so this should not be useful anymore. + */ + if (bi->version < 4) { + /* + * Touch each page to make sure the PTEs for them + * are in the hash table - the aim is to try to avoid + * getting DSI exceptions while copying the kernel image. + */ + for (ptr = ((unsigned long) &_stext) & PAGE_MASK; + ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) + x = *(volatile unsigned long *)ptr; + } + +#ifdef CONFIG_BOOTX_TEXT + /* + * Note that after we call btext_prepare_BAT, we can't do + * prom_draw*, flushscreen or clearscreen until we turn the MMU + * on, since btext_prepare_BAT sets disp_bi.logicalDisplayBase + * to a virtual address. + */ + btext_prepare_BAT(); +#endif +} diff -Nru a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c --- a/arch/ppc/kernel/ptrace.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/kernel/ptrace.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ptrace.c 1.8 07/07/01 17:00:08 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/arch/ppc/kernel/ptrace.c @@ -71,6 +71,64 @@ return -EIO; } +#ifdef CONFIG_ALTIVEC +/* + * Get contents of AltiVec register state in task TASK + */ +static inline int get_vrregs(unsigned long *data, struct task_struct *task) +{ + int i, j; + + if (!access_ok(VERIFY_WRITE, data, 133 * sizeof(unsigned long))) + return -EFAULT; + + /* copy AltiVec registers VR[0] .. VR[31] */ + for (i = 0; i < 32; i++) + for (j = 0; j < 4; j++, data++) + if (__put_user(task->thread.vr[i].u[j], data)) + return -EFAULT; + + /* copy VSCR */ + for (i = 0; i < 4; i++, data++) + if (__put_user(task->thread.vscr.u[i], data)) + return -EFAULT; + + /* copy VRSAVE */ + if (__put_user(task->thread.vrsave, data)) + return -EFAULT; + + return 0; +} + +/* + * Write contents of AltiVec register state into task TASK. + */ +static inline int set_vrregs(struct task_struct *task, unsigned long *data) +{ + int i, j; + + if (!access_ok(VERIFY_READ, data, 133 * sizeof(unsigned long))) + return -EFAULT; + + /* copy AltiVec registers VR[0] .. VR[31] */ + for (i = 0; i < 32; i++) + for (j = 0; j < 4; j++, data++) + if (__get_user(task->thread.vr[i].u[j], data)) + return -EFAULT; + + /* copy VSCR */ + for (i = 0; i < 4; i++, data++) + if (__get_user(task->thread.vscr.u[i], data)) + return -EFAULT; + + /* copy VRSAVE */ + if (__get_user(task->thread.vrsave, data)) + return -EFAULT; + + return 0; +} +#endif + static inline void set_single_step(struct task_struct *task) { @@ -132,14 +190,9 @@ ret = ptrace_attach(child); goto out_tsk; } - ret = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } - if (child->p_pptr != current) + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) goto out_tsk; switch (request) { @@ -219,10 +272,11 @@ ret = -EIO; if ((unsigned long) data > _NSIG) break; - if (request == PTRACE_SYSCALL) - child->ptrace |= PT_TRACESYS; - else - child->ptrace &= ~PT_TRACESYS; + if (request == PTRACE_SYSCALL) { + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } else { + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } child->exit_code = data; /* make sure the single step bit is not set. */ clear_single_step(child); @@ -251,7 +305,7 @@ ret = -EIO; if ((unsigned long) data > _NSIG) break; - child->ptrace &= ~PT_TRACESYS; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); set_single_step(child); child->exit_code = data; /* give it a chance to run. */ @@ -264,21 +318,39 @@ ret = ptrace_detach(child, data); break; +#ifdef CONFIG_ALTIVEC + case PTRACE_GETVRREGS: + /* Get the child altivec register state. */ + if (child->thread.regs->msr & MSR_VEC) + giveup_altivec(child); + ret = get_vrregs((unsigned long *)data, child); + break; + + case PTRACE_SETVRREGS: + /* Set the child altivec register state. */ + /* this is to clear the MSR_VEC bit to force a reload + * of register state from memory */ + if (child->thread.regs->msr & MSR_VEC) + giveup_altivec(child); + ret = set_vrregs(child, (unsigned long *)data); + break; +#endif + default: ret = -EIO; break; } out_tsk: - free_task_struct(child); + put_task_struct(child); out: unlock_kernel(); return ret; } -void syscall_trace(void) +void do_syscall_trace(void) { - if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) - != (PT_PTRACED|PT_TRACESYS)) + if (!test_thread_flag(TIF_SYSCALL_TRACE) + || !(current->ptrace & PT_PTRACED)) return; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; diff -Nru a/arch/ppc/kernel/qspan_pci.c b/arch/ppc/kernel/qspan_pci.c --- a/arch/ppc/kernel/qspan_pci.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/kernel/qspan_pci.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.qspan_pci.c 1.5 05/17/01 18:14:22 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * QSpan pci routines. @@ -29,8 +29,7 @@ #include #include #include - -#include "pci.h" +#include /* diff -Nru a/arch/ppc/kernel/residual.c b/arch/ppc/kernel/residual.c --- a/arch/ppc/kernel/residual.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,878 +0,0 @@ -/* - * BK Id: SCCS/s.residual.c 1.13 09/11/01 16:54:34 trini - */ -/* - * Code to deal with the PReP residual data. - * - * Written by: Cort Dougan (cort@cs.nmt.edu) - * Improved _greatly_ and rewritten by Gabriel Paubert (paubert@iram.es) - * - * This file is based on the following documentation: - * - * IBM Power Personal Systems Architecture - * Residual Data - * Document Number: PPS-AR-FW0001 - * - * 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 archive - * for more details. - * - */ - -#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 - - -unsigned char __res[sizeof(RESIDUAL)] __prepdata = {0,}; -RESIDUAL *res = (RESIDUAL *)&__res; - -char * PnP_BASE_TYPES[] __initdata = { - "Reserved", - "MassStorageDevice", - "NetworkInterfaceController", - "DisplayController", - "MultimediaController", - "MemoryController", - "BridgeController", - "CommunicationsDevice", - "SystemPeripheral", - "InputDevice", - "ServiceProcessor" - }; - -/* Device Sub Type Codes */ - -unsigned char * PnP_SUB_TYPES[] __initdata = { - "\001\000SCSIController", - "\001\001IDEController", - "\001\002FloppyController", - "\001\003IPIController", - "\001\200OtherMassStorageController", - "\002\000EthernetController", - "\002\001TokenRingController", - "\002\002FDDIController", - "\002\0x80OtherNetworkController", - "\003\000VGAController", - "\003\001SVGAController", - "\003\002XGAController", - "\003\200OtherDisplayController", - "\004\000VideoController", - "\004\001AudioController", - "\004\200OtherMultimediaController", - "\005\000RAM", - "\005\001FLASH", - "\005\200OtherMemoryDevice", - "\006\000HostProcessorBridge", - "\006\001ISABridge", - "\006\002EISABridge", - "\006\003MicroChannelBridge", - "\006\004PCIBridge", - "\006\005PCMCIABridge", - "\006\006VMEBridge", - "\006\200OtherBridgeDevice", - "\007\000RS232Device", - "\007\001ATCompatibleParallelPort", - "\007\200OtherCommunicationsDevice", - "\010\000ProgrammableInterruptController", - "\010\001DMAController", - "\010\002SystemTimer", - "\010\003RealTimeClock", - "\010\004L2Cache", - "\010\005NVRAM", - "\010\006PowerManagement", - "\010\007CMOS", - "\010\010OperatorPanel", - "\010\011ServiceProcessorClass1", - "\010\012ServiceProcessorClass2", - "\010\013ServiceProcessorClass3", - "\010\014GraphicAssist", - "\010\017SystemPlanar", - "\010\200OtherSystemPeripheral", - "\011\000KeyboardController", - "\011\001Digitizer", - "\011\002MouseController", - "\011\003TabletController", - "\011\0x80OtherInputController", - "\012\000GeneralMemoryController", - NULL -}; - -/* Device Interface Type Codes */ - -unsigned char * PnP_INTERFACES[] __initdata = { - "\000\000\000General", - "\001\000\000GeneralSCSI", - "\001\001\000GeneralIDE", - "\001\001\001ATACompatible", - - "\001\002\000GeneralFloppy", - "\001\002\001Compatible765", - "\001\002\002NS398_Floppy", /* NS Super I/O wired to use index - register at port 398 and data - register at port 399 */ - "\001\002\003NS26E_Floppy", /* Ports 26E and 26F */ - "\001\002\004NS15C_Floppy", /* Ports 15C and 15D */ - "\001\002\005NS2E_Floppy", /* Ports 2E and 2F */ - "\001\002\006CHRP_Floppy", /* CHRP Floppy in PR*P system */ - - "\001\003\000GeneralIPI", - - "\002\000\000GeneralEther", - "\002\001\000GeneralToken", - "\002\002\000GeneralFDDI", - - "\003\000\000GeneralVGA", - "\003\001\000GeneralSVGA", - "\003\002\000GeneralXGA", - - "\004\000\000GeneralVideo", - "\004\001\000GeneralAudio", - "\004\001\001CS4232Audio", /* CS 4232 Plug 'n Play Configured */ - - "\005\000\000GeneralRAM", - /* This one is obviously wrong ! */ - "\005\000\000PCIMemoryController", /* PCI Config Method */ - "\005\000\001RS6KMemoryController", /* RS6K Config Method */ - "\005\001\000GeneralFLASH", - - "\006\000\000GeneralHostBridge", - "\006\001\000GeneralISABridge", - "\006\002\000GeneralEISABridge", - "\006\003\000GeneralMCABridge", - /* GeneralPCIBridge = 0, */ - "\006\004\000PCIBridgeDirect", - "\006\004\001PCIBridgeIndirect", - "\006\004\002PCIBridgeRS6K", - "\006\005\000GeneralPCMCIABridge", - "\006\006\000GeneralVMEBridge", - - "\007\000\000GeneralRS232", - "\007\000\001COMx", - "\007\000\002Compatible16450", - "\007\000\003Compatible16550", - "\007\000\004NS398SerPort", /* NS Super I/O wired to use index - register at port 398 and data - register at port 399 */ - "\007\000\005NS26ESerPort", /* Ports 26E and 26F */ - "\007\000\006NS15CSerPort", /* Ports 15C and 15D */ - "\007\000\007NS2ESerPort", /* Ports 2E and 2F */ - - "\007\001\000GeneralParPort", - "\007\001\001LPTx", - "\007\001\002NS398ParPort", /* NS Super I/O wired to use index - register at port 398 and data - register at port 399 */ - "\007\001\003NS26EParPort", /* Ports 26E and 26F */ - "\007\001\004NS15CParPort", /* Ports 15C and 15D */ - "\007\001\005NS2EParPort", /* Ports 2E and 2F */ - - "\010\000\000GeneralPIC", - "\010\000\001ISA_PIC", - "\010\000\002EISA_PIC", - "\010\000\003MPIC", - "\010\000\004RS6K_PIC", - - "\010\001\000GeneralDMA", - "\010\001\001ISA_DMA", - "\010\001\002EISA_DMA", - - "\010\002\000GeneralTimer", - "\010\002\001ISA_Timer", - "\010\002\002EISA_Timer", - "\010\003\000GeneralRTC", - "\010\003\001ISA_RTC", - - "\010\004\001StoreThruOnly", - "\010\004\002StoreInEnabled", - "\010\004\003RS6KL2Cache", - - "\010\005\000IndirectNVRAM", /* Indirectly addressed */ - "\010\005\001DirectNVRAM", /* Memory Mapped */ - "\010\005\002IndirectNVRAM24", /* Indirectly addressed - 24 bit */ - - "\010\006\000GeneralPowerManagement", - "\010\006\001EPOWPowerManagement", - "\010\006\002PowerControl", // d1378 - - "\010\007\000GeneralCMOS", - - "\010\010\000GeneralOPPanel", - "\010\010\001HarddiskLight", - "\010\010\002CDROMLight", - "\010\010\003PowerLight", - "\010\010\004KeyLock", - "\010\010\005ANDisplay", /* AlphaNumeric Display */ - "\010\010\006SystemStatusLED", /* 3 digit 7 segment LED */ - "\010\010\007CHRP_SystemStatusLED", /* CHRP LEDs in PR*P system */ - - "\010\011\000GeneralServiceProcessor", - "\010\012\000GeneralServiceProcessor", - "\010\013\000GeneralServiceProcessor", - - "\010\014\001TransferData", - "\010\014\002IGMC32", - "\010\014\003IGMC64", - - "\010\017\000GeneralSystemPlanar", /* 10/5/95 */ - NULL - }; - -static const unsigned char __init *PnP_SUB_TYPE_STR(unsigned char BaseType, - unsigned char SubType) { - unsigned char ** s=PnP_SUB_TYPES; - while (*s && !((*s)[0]==BaseType - && (*s)[1]==SubType)) s++; - if (*s) return *s+2; - else return("Unknown !"); -}; - -static const unsigned char __init *PnP_INTERFACE_STR(unsigned char BaseType, - unsigned char SubType, - unsigned char Interface) { - unsigned char ** s=PnP_INTERFACES; - while (*s && !((*s)[0]==BaseType - && (*s)[1]==SubType - && (*s)[2]==Interface)) s++; - if (*s) return *s+3; - else return NULL; -}; - -static void __init printsmallvendor(PnP_TAG_PACKET *pkt, int size) { - int i, c; - char decomp[4]; -#define p pkt->S14_Pack.S14_Data.S14_PPCPack - switch(p.Type) { - case 1: - /* Decompress first 3 chars */ - c = *(unsigned short *)p.PPCData; - decomp[0]='A'-1+((c>>10)&0x1F); - decomp[1]='A'-1+((c>>5)&0x1F); - decomp[2]='A'-1+(c&0x1F); - decomp[3]=0; - printk(" Chip identification: %s%4.4X\n", - decomp, ld_le16((unsigned short *)(p.PPCData+2))); - break; - default: - printk(" Small vendor item type 0x%2.2x, data (hex): ", - p.Type); - for(i=0; iS1_Pack.Tag)) { - case PnPVersion: - printk(" PnPversion 0x%x.%x\n", - pkt->S1_Pack.Version[0], /* How to interpret version ? */ - pkt->S1_Pack.Version[1]); - break; -// case Logicaldevice: - break; -// case CompatibleDevice: - break; - case IRQFormat: -#define p pkt->S4_Pack - printk(" IRQ Mask 0x%4.4x, %s %s sensitive\n", - ld_le16((unsigned short *)p.IRQMask), - intlevel[(size>3) ? !(p.IRQInfo&0x05) : 0], - intsense[(size>3) ? !(p.IRQInfo&0x03) : 0]); -#undef p - break; - case DMAFormat: -#define p pkt->S5_Pack - printk(" DMA channel mask 0x%2.2x, info 0x%2.2x\n", - p.DMAMask, p.DMAInfo); -#undef p - break; - case StartDepFunc: - printk("Start dependent function:\n"); - break; - case EndDepFunc: - printk("End dependent function\n"); - break; - case IOPort: -#define p pkt->S8_Pack - printk(" Variable (%d decoded bits) I/O port\n" - " from 0x%4.4x to 0x%4.4x, alignment %d, %d ports\n", - p.IOInfo&ISAAddr16bit?16:10, - ld_le16((unsigned short *)p.RangeMin), - ld_le16((unsigned short *)p.RangeMax), - p.IOAlign, p.IONum); -#undef p - break; - case FixedIOPort: -#define p pkt->S9_Pack - printk(" Fixed (10 decoded bits) I/O port from %3.3x to %3.3x\n", - (p.Range[1]<<8)|p.Range[0], - ((p.Range[1]<<8)|p.Range[0])+p.IONum-1); -#undef p - break; - case Res1: - case Res2: - case Res3: - printk(" Undefined packet type %d!\n", - tag_small_item_name(pkt->S1_Pack.Tag)); - break; - case SmallVendorItem: - printsmallvendor(pkt,size); - break; - default: - printk(" Type 0x2.2x%d, size=%d\n", - pkt->S1_Pack.Tag, size); - break; - } -} - -static void __init printlargevendor(PnP_TAG_PACKET * pkt, int size) { - static const unsigned char * addrtype[] = {"I/O", "Memory", "System"}; - static const unsigned char * inttype[] = {"8259", "MPIC", "RS6k BUID %d"}; - static const unsigned char * convtype[] = {"Bus Memory", "Bus I/O", "DMA"}; - static const unsigned char * transtype[] = {"direct", "mapped", "direct-store segment"}; - static const unsigned char * L2type[] = {"WriteThru", "CopyBack"}; - static const unsigned char * L2assoc[] = {"DirectMapped", "2-way set"}; - - int i; - char tmpstr[30], *t; -#define p pkt->L4_Pack.L4_Data.L4_PPCPack - switch(p.Type) { - case 2: - printk(" %d K %s %s L2 cache, %d/%d bytes line/sector size\n", - ld_le32((unsigned int *)p.PPCData), - L2type[p.PPCData[10]-1], - L2assoc[p.PPCData[4]-1], - ld_le16((unsigned short *)p.PPCData+3), - ld_le16((unsigned short *)p.PPCData+4)); - break; - case 3: - printk(" PCI Bridge parameters\n" - " ConfigBaseAddress %0x\n" - " ConfigBaseData %0x\n" - " Bus number %d\n", - ld_le32((unsigned int *)p.PPCData), - ld_le32((unsigned int *)(p.PPCData+8)), - p.PPCData[16]); - for(i=20; iS1_Pack.Tag)) { - case LargeVendorItem: - printlargevendor(pkt, size); - break; - default: - printk(" Type 0x2.2x%d, size=%d\n", - pkt->S1_Pack.Tag, size); - break; - } -} -static void __init printpackets(PnP_TAG_PACKET * pkt, const char * cat) { - if (pkt->S1_Pack.Tag== END_TAG) { - printk(" No packets describing %s resources.\n", cat); - return; - } - printk( " Packets describing %s resources:\n",cat); - do { - int size; - if (tag_type(pkt->S1_Pack.Tag)) { - size= 3 + - pkt->L1_Pack.Count0 + - pkt->L1_Pack.Count1*256; - printlargepacket(pkt, size); - } else { - size=tag_small_count(pkt->S1_Pack.Tag)+1; - printsmallpacket(pkt, size); - } - (unsigned char *) pkt+=size; - } while (pkt->S1_Pack.Tag != END_TAG); -} - -void __init print_residual_device_info(void) -{ - int i; - PPC_DEVICE *dev; -#define did dev->DeviceId - - /* make sure we have residual data first */ - if ( res->ResidualLength == 0 ) - return; - - printk("Residual: %ld devices\n", res->ActualNumDevices); - for ( i = 0; - i < res->ActualNumDevices ; - i++) - { - char decomp[4], sn[20]; - const char * s; - dev = &res->Devices[i]; - s = PnP_INTERFACE_STR(did.BaseType, did.SubType, - did.Interface); - if(!s) { - sprintf(sn, "interface %d", did.Interface); - s=sn; - } - if ( did.BusId & PCIDEVICE ) - printk("PCI Device, Bus %d, DevFunc 0x%x:", - dev->BusAccess.PCIAccess.BusNumber, - dev->BusAccess.PCIAccess.DevFuncNumber); - if ( did.BusId & PNPISADEVICE ) printk("PNPISA Device:"); - if ( did.BusId & ISADEVICE ) - printk("ISA Device, Slot %d, LogicalDev %d:", - dev->BusAccess.ISAAccess.SlotNumber, - dev->BusAccess.ISAAccess.LogicalDevNumber); - if ( did.BusId & EISADEVICE ) printk("EISA Device:"); - if ( did.BusId & PROCESSORDEVICE ) - printk("ProcBus Device, Bus %d, BUID %d: ", - dev->BusAccess.ProcBusAccess.BusNumber, - dev->BusAccess.ProcBusAccess.BUID); - if ( did.BusId & PCMCIADEVICE ) printk("PCMCIA "); - if ( did.BusId & VMEDEVICE ) printk("VME "); - if ( did.BusId & MCADEVICE ) printk("MCA "); - if ( did.BusId & MXDEVICE ) printk("MX "); - /* Decompress first 3 chars */ - decomp[0]='A'-1+((did.DevId>>26)&0x1F); - decomp[1]='A'-1+((did.DevId>>21)&0x1F); - decomp[2]='A'-1+((did.DevId>>16)&0x1F); - decomp[3]=0; - printk(" %s%4.4lX, %s, %s, %s\n", - decomp, did.DevId&0xffff, - PnP_BASE_TYPES[did.BaseType], - PnP_SUB_TYPE_STR(did.BaseType,did.SubType), - s); - if ( dev->AllocatedOffset ) - printpackets( (union _PnP_TAG_PACKET *) - &res->DevicePnPHeap[dev->AllocatedOffset], - "allocated"); - if ( dev->PossibleOffset ) - printpackets( (union _PnP_TAG_PACKET *) - &res->DevicePnPHeap[dev->PossibleOffset], - "possible"); - if ( dev->CompatibleOffset ) - printpackets( (union _PnP_TAG_PACKET *) - &res->DevicePnPHeap[dev->CompatibleOffset], - "compatible"); - } -} - - -#if 0 -static void __init printVPD(void) { -#define vpd res->VitalProductData - int ps=vpd.PageSize, i, j; - static const char* Usage[]={ - "FirmwareStack", "FirmwareHeap", "FirmwareCode", "BootImage", - "Free", "Unpopulated", "ISAAddr", "PCIConfig", - "IOMemory", "SystemIO", "SystemRegs", "PCIAddr", - "UnPopSystemRom", "SystemROM", "ResumeBlock", "Other" - }; - static const unsigned char *FWMan[]={ - "IBM", "Motorola", "FirmWorks", "Bull" - }; - static const unsigned char *FWFlags[]={ - "Conventional", "OpenFirmware", "Diagnostics", "LowDebug", - "MultiBoot", "LowClient", "Hex41", "FAT", - "ISO9660", "SCSI_ID_Override", "Tape_Boot", "FW_Boot_Path" - }; - static const unsigned char *ESM[]={ - "Port92", "PCIConfigA8", "FF001030", "????????" - }; - static const unsigned char *SIOM[]={ - "Port850", "????????", "PCIConfigA8", "????????" - }; - - printk("Model: %s\n",vpd.PrintableModel); - printk("Serial: %s\n", vpd.Serial); - printk("FirmwareSupplier: %s\n", FWMan[vpd.FirmwareSupplier]); - printk("FirmwareFlags:"); - for(j=0; j<12; j++) { - if (vpd.FirmwareSupports & (1<2 ? 2 : vpd.EndianSwitchMethod]); - printk("SpreadIOMethod: %s\n", - SIOM[vpd.SpreadIOMethod>3 ? 3 : vpd.SpreadIOMethod]); - printk("Processor/Bus frequencies (Hz): %ld/%ld\n", - vpd.ProcessorHz, vpd.ProcessorBusHz); - printk("Time Base Divisor: %ld\n", vpd.TimeBaseDivisor); - printk("WordWidth, PageSize: %ld, %d\n", vpd.WordWidth, ps); - printk("Cache sector size, Lock granularity: %ld, %ld\n", - vpd.CoherenceBlockSize, vpd.GranuleSize); - for (i=0; iActualNumMemSegs; i++) { - int mask=res->Segs[i].Usage, first, j; - printk("%8.8lx-%8.8lx ", - res->Segs[i].BasePage*ps, - (res->Segs[i].PageCount+res->Segs[i].BasePage)*ps-1); - for(j=15, first=1; j>=0; j--) { - if (mask&(1<DeviceId - - /* make sure we have residual data first */ - if ( res->ResidualLength == 0 ) - return; - printk("Residual: %ld devices\n", res->ActualNumDevices); - for ( i = 0; - i < res->ActualNumDevices ; - i++) - { - dev = &res->Devices[i]; - /* - * pci devices - */ - if ( did.BusId & PCIDEVICE ) - { - printk("PCI Device:"); - /* unknown vendor */ - if ( !strncmp( "Unknown", pci_strvendor(did.DevId>>16), 7) ) - printk(" id %08lx types %d/%d", did.DevId, - did.BaseType, did.SubType); - /* known vendor */ - else - printk(" %s %s", - pci_strvendor(did.DevId>>16), - pci_strdev(did.DevId>>16, - did.DevId&0xffff) - ); - - if ( did.BusId & PNPISADEVICE ) - { - printk(" pnp:"); - /* get pnp info on the device */ - pkt = (union _PnP_TAG_PACKET *) - &res->DevicePnPHeap[dev->AllocatedOffset]; - for (; pkt->S1_Pack.Tag != DF_END_TAG; - pkt++ ) - { - if ( (pkt->S1_Pack.Tag == S4_Packet) || - (pkt->S1_Pack.Tag == S4_Packet_flags) ) - printk(" irq %02x%02x", - pkt->S4_Pack.IRQMask[0], - pkt->S4_Pack.IRQMask[1]); - } - } - printk("\n"); - continue; - } - /* - * isa devices - */ - if ( did.BusId & ISADEVICE ) - { - printk("ISA Device: basetype: %d subtype: %d", - did.BaseType, did.SubType); - printk("\n"); - continue; - } - /* - * eisa devices - */ - if ( did.BusId & EISADEVICE ) - { - printk("EISA Device: basetype: %d subtype: %d", - did.BaseType, did.SubType); - printk("\n"); - continue; - } - /* - * proc bus devices - */ - if ( did.BusId & PROCESSORDEVICE ) - { - printk("ProcBus Device: basetype: %d subtype: %d", - did.BaseType, did.SubType); - printk("\n"); - continue; - } - /* - * pcmcia devices - */ - if ( did.BusId & PCMCIADEVICE ) - { - printk("PCMCIA Device: basetype: %d subtype: %d", - did.BaseType, did.SubType); - printk("\n"); - continue; - } - printk("Unknown bus access device: busid %lx\n", - did.BusId); - } -} -#endif - -/* Returns the device index in the residual data, - any of the search items may be set as -1 for wildcard, - DevID number field (second halfword) is big endian ! - - Examples: - - search for the Interrupt controller (8259 type), 2 methods: - 1) i8259 = residual_find_device(~0, - NULL, - SystemPeripheral, - ProgrammableInterruptController, - ISA_PIC, - 0); - 2) i8259 = residual_find_device(~0, "PNP0000", -1, -1, -1, 0) - - - search for the first two serial devices, whatever their type) - iserial1 = residual_find_device(~0,NULL, - CommunicationsDevice, - RS232Device, - -1, 0) - iserial2 = residual_find_device(~0,NULL, - CommunicationsDevice, - RS232Device, - -1, 1) - - but search for typical COM1 and COM2 is not easy due to the - fact that the interface may be anything and the name "PNP0500" or - "PNP0501". Quite bad. - -*/ - -/* devid are easier to uncompress than to compress, so to minimize bloat -in this rarely used area we unencode and compare */ - -/* in residual data number is big endian in the device table and -little endian in the heap, so we use two parameters to avoid writing -two very similar functions */ - -static int __init same_DevID(unsigned short vendor, - unsigned short Number, - char * str) -{ - static unsigned const char hexdigit[]="0123456789ABCDEF"; - if (strlen(str)!=7) return 0; - if ( ( ((vendor>>10)&0x1f)+'A'-1 == str[0]) && - ( ((vendor>>5)&0x1f)+'A'-1 == str[1]) && - ( (vendor&0x1f)+'A'-1 == str[2]) && - (hexdigit[(Number>>12)&0x0f] == str[3]) && - (hexdigit[(Number>>8)&0x0f] == str[4]) && - (hexdigit[(Number>>4)&0x0f] == str[5]) && - (hexdigit[Number&0x0f] == str[6]) ) return 1; - return 0; -} - -PPC_DEVICE __init *residual_find_device(unsigned long BusMask, - unsigned char * DevID, - int BaseType, - int SubType, - int Interface, - int n) -{ - int i; - if ( !res->ResidualLength ) return NULL; - for (i=0; iActualNumDevices; i++) { -#define Dev res->Devices[i].DeviceId - if ( (Dev.BusId&BusMask) && - (BaseType==-1 || Dev.BaseType==BaseType) && - (SubType==-1 || Dev.SubType==SubType) && - (Interface==-1 || Dev.Interface==Interface) && - (DevID==NULL || same_DevID((Dev.DevId>>16)&0xffff, - Dev.DevId&0xffff, DevID)) && - !(n--) ) return res->Devices+i; -#undef Dev - } - return 0; -} - -PPC_DEVICE __init *residual_find_device_id(unsigned long BusMask, - unsigned short DevID, - int BaseType, - int SubType, - int Interface, - int n) -{ - int i; - if ( !res->ResidualLength ) return NULL; - for (i=0; iActualNumDevices; i++) { -#define Dev res->Devices[i].DeviceId - if ( (Dev.BusId&BusMask) && - (BaseType==-1 || Dev.BaseType==BaseType) && - (SubType==-1 || Dev.SubType==SubType) && - (Interface==-1 || Dev.Interface==Interface) && - (DevID==0xffff || (Dev.DevId&0xffff) == DevID) && - !(n--) ) return res->Devices+i; -#undef Dev - } - return 0; -} - -PnP_TAG_PACKET *PnP_find_packet(unsigned char *p, - unsigned packet_tag, - int n) -{ - unsigned mask, masked_tag, size; - if(!p) return 0; - if (tag_type(packet_tag)) mask=0xff; else mask=0xF8; - masked_tag = packet_tag&mask; - for(; *p != END_TAG; p+=size) { - if ((*p & mask) == masked_tag && !(n--)) - return (PnP_TAG_PACKET *) p; - if (tag_type(*p)) - size=ld_le16((unsigned short *)(p+1))+3; - else - size=tag_small_count(*p)+1; - } - return 0; /* not found */ -} - -PnP_TAG_PACKET __init *PnP_find_small_vendor_packet(unsigned char *p, - unsigned packet_type, - int n) -{ - int next=0; - while (p) { - p = (unsigned char *) PnP_find_packet(p, 0x70, next); - if (p && p[1]==packet_type && !(n--)) - return (PnP_TAG_PACKET *) p; - next = 1; - }; - return 0; /* not found */ -} - -PnP_TAG_PACKET __init *PnP_find_large_vendor_packet(unsigned char *p, - unsigned packet_type, - int n) -{ - int next=0; - while (p) { - p = (unsigned char *) PnP_find_packet(p, 0x84, next); - if (p && p[3]==packet_type && !(n--)) - return (PnP_TAG_PACKET *) p; - next = 1; - }; - return 0; /* not found */ -} diff -Nru a/arch/ppc/kernel/semaphore.c b/arch/ppc/kernel/semaphore.c --- a/arch/ppc/kernel/semaphore.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/kernel/semaphore.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.semaphore.c 1.12 05/17/01 18:14:22 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * PowerPC-specific semaphore code. @@ -39,6 +39,7 @@ " srawi %1,%0,31\n" " andc %1,%0,%1\n" " add %1,%1,%4\n" + PPC405_ERR77(0,%3) " stwcx. %1,0,%3\n" " bne 1b" : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) diff -Nru a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c --- a/arch/ppc/kernel/setup.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/setup.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.setup.c 1.65 11/18/01 20:57:25 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Common prep/pmac/chrp boot and setup code. @@ -29,35 +29,32 @@ #include #include #include -#ifdef CONFIG_8xx -#include -#include -#endif -#ifdef CONFIG_8260 -#include -#include -#endif -#ifdef CONFIG_4xx -#include -#endif #include #include #include -#include #include #include +#include + +#if defined CONFIG_KGDB +#include +#endif extern void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7); extern void bootx_init(unsigned long r4, unsigned long phys); -extern unsigned long reloc_offset(void); extern void identify_cpu(unsigned long offset, unsigned long cpu); extern void do_cpu_ftr_fixups(unsigned long offset); +extern void reloc_got2(unsigned long offset); #ifdef CONFIG_XMON extern void xmon_map_scc(void); #endif +#ifdef CONFIG_KGDB +extern void kgdb_map_scc(void); +#endif + extern boot_infos_t *boot_infos; char saved_command_line[256]; unsigned char aux_device_present; @@ -69,8 +66,6 @@ size value reported by the boot loader. */ unsigned int boot_mem_size; -int parse_bootinfo(void); - unsigned long ISA_DMA_THRESHOLD; unsigned long DMA_MODE_READ, DMA_MODE_WRITE; @@ -103,7 +98,8 @@ int icache_bsize; int ucache_bsize; -#ifdef CONFIG_VGA_CONSOLE +#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_FB_VGA16) || \ + defined(CONFIG_FB_VGA16_MODULE) || defined(CONFIG_FB_VESA) struct screen_info screen_info = { 0, 25, /* orig-x, orig-y */ 0, /* unused */ @@ -115,7 +111,7 @@ 1, /* orig-video-isVGA */ 16 /* orig-video-points */ }; -#endif /* CONFIG_VGA_CONSOLE */ +#endif /* CONFIG_VGA_CONSOLE || CONFIG_FB_VGA16 || CONFIG_FB_VESA */ void machine_restart(char *cmd) { @@ -166,7 +162,7 @@ return 0; pvr = cpu_data[i].pvr; lpj = cpu_data[i].loops_per_jiffy; - seq_printf(m, "processor\t: %lu\n", i); + seq_printf(m, "processor\t: %d\n", i); #else pvr = mfspr(PVR); lpj = loops_per_jiffy; @@ -291,22 +287,22 @@ do_cpu_ftr_fixups(offset); #if defined(CONFIG_ALL_PPC) + reloc_got2(offset); + /* If we came here from BootX, clear the screen, * set up some pointers and return. */ - if ((r3 == 0x426f6f58) && (r5 == 0)) { + if ((r3 == 0x426f6f58) && (r5 == 0)) bootx_init(r4, phys); - return phys; - } - /* check if we're prep, return if we are */ - if ( *(unsigned long *)(0) == 0xdeadc0de ) - return phys; - - /* + /* + * don't do anything on prep * for now, don't use bootinfo because it breaks yaboot 0.5 * and assume that if we didn't find a magic number, we have OF */ - phys = prom_init(r3, r4, (prom_entry)r5); + else if (*(unsigned long *)(0) != 0xdeadc0de) + phys = prom_init(r3, r4, (prom_entry)r5); + + reloc_got2(-offset); #endif return phys; @@ -343,14 +339,14 @@ unsigned long r6, unsigned long r7) { #ifdef CONFIG_BOOTX_TEXT - extern boot_infos_t *disp_bi; - - if (disp_bi) { + if (boot_text_mapped) { btext_clearscreen(); - btext_welcome(disp_bi); + btext_welcome(); } #endif + parse_bootinfo(find_bootinfo()); + /* if we didn't get any bootinfo telling us what we are... */ if (_machine == 0) { /* prep boot loader tells us if we're prep or not */ @@ -409,15 +405,14 @@ char *p; #ifdef CONFIG_BLK_DEV_INITRD - if (r3 && r4 && r4 != 0xdeadbeef) - { - if (r3 < KERNELBASE) - r3 += KERNELBASE; - initrd_start = r3; - initrd_end = r3 + r4; - ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); - initrd_below_start_ok = 1; - } + if (r3 && r4 && r4 != 0xdeadbeef) { + if (r3 < KERNELBASE) + r3 += KERNELBASE; + initrd_start = r3; + initrd_end = r3 + r4; + ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); + initrd_below_start_ok = 1; + } #endif chosen = find_devices("chosen"); if (chosen != NULL) { @@ -429,6 +424,12 @@ } } cmd_line[sizeof(cmd_line) - 1] = 0; +#ifdef CONFIG_ADB + if (strstr(cmd_line, "adb_sync")) { + extern int __adb_probe_sync; + __adb_probe_sync = 1; + } +#endif /* CONFIG_ADB */ switch (_machine) { case _MACH_Pmac: @@ -441,7 +442,7 @@ } #endif /* CONFIG_ALL_PPC */ -int parse_bootinfo(void) +struct bi_record *find_bootinfo(void) { struct bi_record *rec; extern char __bss_start[]; @@ -455,11 +456,16 @@ */ rec = (struct bi_record *)_ALIGN((ulong)__bss_start+0x10000+(1<<20)-1,(1<<20)); if ( rec->tag != BI_FIRST ) - return -1; + return NULL; } - for ( ; rec->tag != BI_LAST ; - rec = (struct bi_record *)((ulong)rec + rec->size) ) - { + return rec; +} + +void parse_bootinfo(struct bi_record *rec) +{ + if (rec == NULL || rec->tag != BI_FIRST) + return; + while (rec->tag != BI_LAST) { ulong *data = rec->data; switch (rec->tag) { case BI_CMD_LINE: @@ -485,9 +491,8 @@ boot_mem_size = data[0]; break; } + rec = (struct bi_record *)((ulong)rec + rec->size); } - - return 0; } /* @@ -504,8 +509,6 @@ strcpy(cmd_line, CONFIG_CMDLINE); #endif /* CONFIG_CMDLINE */ - parse_bootinfo(); - platform_init(r3, r4, r5, r6, r7); if (ppc_md.progress) @@ -525,7 +528,7 @@ } __setup("l2cr=", ppc_setup_l2cr); -void __init ppc_init(void) +int __init ppc_init(void) { /* clear the progress line */ if ( ppc_md.progress ) ppc_md.progress(" ", 0xffff); @@ -533,9 +536,10 @@ if (ppc_md.init != NULL) { ppc_md.init(); } + return 0; } -subsys_initcall(ppc_init); +arch_initcall(ppc_init); /* Warning, IO base is not yet inited */ void __init setup_arch(char **cmdline_p) @@ -549,8 +553,13 @@ loops_per_jiffy = 500000000 / HZ; #ifdef CONFIG_ALL_PPC - feature_init(); -#endif + /* This could be called "early setup arch", it must be done + * now because xmon need it + */ + if (_machine == _MACH_Pmac) + pmac_feature_init(); /* New cool way */ +#endif /* CONFIG_ALL_PPC */ + #ifdef CONFIG_XMON xmon_map_scc(); if (strstr(cmd_line, "xmon")) @@ -561,7 +570,12 @@ #if defined(CONFIG_KGDB) kgdb_map_scc(); set_debug_traps(); - breakpoint(); + if (strstr(cmd_line, "nokgdb")) + printk("kgdb default breakpoint deactivated on command line\n"); + else { + printk("kgdb default breakpoint activated\n"); + breakpoint(); + } #endif /* @@ -596,24 +610,6 @@ ppc_md.setup_arch(); if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab); -#if defined(CONFIG_PCI) && defined(CONFIG_ALL_PPC) - /* We create the "pci-OF-bus-map" property now so it appear in the - * /proc device tree - */ - if (have_of) { - struct property* of_prop; - - of_prop = (struct property*)alloc_bootmem(sizeof(struct property) + 256); - if (of_prop && find_path_device("/")) { - memset(of_prop, -1, sizeof(struct property) + 256); - of_prop->name = "pci-OF-bus-map"; - of_prop->length = 256; - of_prop->value = (unsigned char *)&of_prop[1]; - prom_add_property(find_path_device("/"), of_prop); - } - } -#endif /* CONFIG_PCI && CONFIG_ALL_PPC */ - paging_init(); sort_exception_table(); @@ -621,6 +617,7 @@ ppc_md.ppc_machine = _machine; } +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) /* Convert the shorts/longs in hd_driveid from little to big endian; * chars are endian independant, of course, but strings need to be flipped. * (Despite what it says in drivers/block/ide.h, they come up as little @@ -715,3 +712,4 @@ id->words206_254[i] = __le16_to_cpu(id->words206_254[i]); id->integrity_word = __le16_to_cpu(id->integrity_word); } +#endif diff -Nru a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c --- a/arch/ppc/kernel/signal.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/signal.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.signal.c 1.7 05/17/01 18:14:22 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/arch/ppc/kernel/signal.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -105,7 +107,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->gpr[3] = -EINTR; @@ -142,7 +144,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->gpr[3] = -EINTR; @@ -180,7 +182,7 @@ siginitset(&new_ka.sa.sa_mask, mask); } - ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + ret = do_sigaction(sig, (act? &new_ka: NULL), (oact? &old_ka: NULL)); if (!ret && oact) { if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || @@ -252,8 +254,10 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); + if (regs->msr & MSR_FP) + giveup_fpu(current); rt_sf++; /* Look at next rt_sigframe */ if (rt_sf == (struct rt_sigframe *)(sigctx.regs)) { @@ -263,8 +267,6 @@ * see handle_signal() */ sr = (struct sigregs *) sigctx.regs; - if (regs->msr & MSR_FP ) - giveup_fpu(current); if (copy_from_user(saved_regs, &sr->gp_regs, sizeof(sr->gp_regs))) goto badframe; @@ -298,6 +300,7 @@ if (get_user(prevsp, &sr->gp_regs[PT_R1]) || put_user(prevsp, (unsigned long *) regs->gpr[1])) goto badframe; + current->thread.fpscr = 0; } return ret; @@ -328,6 +331,7 @@ goto badframe; flush_icache_range((unsigned long) &frame->tramp[0], (unsigned long) &frame->tramp[2]); + current->thread.fpscr = 0; /* turn off all fp exceptions */ /* Retrieve rt_sigframe from stack and set up registers for signal handler @@ -377,15 +381,15 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); + if (regs->msr & MSR_FP ) + giveup_fpu(current); sc++; /* Look at next sigcontext */ if (sc == (struct sigcontext_struct *)(sigctx.regs)) { /* Last stacked signal - restore registers */ sr = (struct sigregs *) sigctx.regs; - if (regs->msr & MSR_FP ) - giveup_fpu(current); if (copy_from_user(saved_regs, &sr->gp_regs, sizeof(sr->gp_regs))) goto badframe; @@ -413,6 +417,7 @@ if (get_user(prevsp, &sr->gp_regs[PT_R1]) || put_user(prevsp, (unsigned long *) regs->gpr[1])) goto badframe; + current->thread.fpscr = 0; } return ret; @@ -431,8 +436,8 @@ if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) goto badframe; - if (regs->msr & MSR_FP) - giveup_fpu(current); + if (regs->msr & MSR_FP) + giveup_fpu(current); if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) || __copy_to_user(&frame->fp_regs, current->thread.fpr, ELF_NFPREG * sizeof(double)) @@ -441,6 +446,7 @@ goto badframe; flush_icache_range((unsigned long) &frame->tramp[0], (unsigned long) &frame->tramp[2]); + current->thread.fpscr = 0; /* turn off all fp exceptions */ newsp -= __SIGNAL_FRAMESIZE; if (put_user(regs->gpr[1], (unsigned long *)newsp) @@ -527,7 +533,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } return; diff -Nru a/arch/ppc/kernel/sleep.S b/arch/ppc/kernel/sleep.S --- a/arch/ppc/kernel/sleep.S Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,366 +0,0 @@ -/* - * BK Id: SCCS/s.sleep.S 1.13 08/19/01 22:23:04 paulus - */ -/* - * This file contains sleep low-level functions for PowerBook G3. - * Copyright (C) 1999 Benjamin Herrenschmidt (benh@kernel.crashing.org) - * and Paul Mackerras (paulus@samba.org). - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - */ - -#include "ppc_asm.tmpl" -#include -#include - -#define MAGIC 0x4c617273 /* 'Lars' */ - -/* - * Structure for storing CPU registers on the stack. - */ -#define SL_SP 0 -#define SL_PC 4 -#define SL_MSR 8 -#define SL_SDR1 0xc -#define SL_SPRG0 0x10 /* 4 sprg's */ -#define SL_DBAT0 0x20 -#define SL_IBAT0 0x28 -#define SL_DBAT1 0x30 -#define SL_IBAT1 0x38 -#define SL_DBAT2 0x40 -#define SL_IBAT2 0x48 -#define SL_DBAT3 0x50 -#define SL_IBAT3 0x58 -#define SL_TB 0x60 -#define SL_HID0 0x68 -#define SL_R2 0x6c -#define SL_R12 0x70 /* r12 to r31 */ -#define SL_SIZE (SL_R12 + 80) - -#define tophys(rd,rs) addis rd,rs,-KERNELBASE@h -#define tovirt(rd,rs) addis rd,rs,KERNELBASE@h - - .text - .align 5 - -/* This gets called by via-pmu.c late during the sleep process. - * The PMU was already send the sleep command and will shut us down - * soon. We need to save all that is needed and setup the wakeup - * vector that will be called by the ROM on wakeup - */ -_GLOBAL(low_sleep_handler) - mflr r0 - stw r0,4(r1) - stwu r1,-SL_SIZE(r1) - stw r2,SL_R2(r1) - stmw r12,SL_R12(r1) - - /* Save MSR & SDR1 */ - mfmsr r4 - stw r4,SL_MSR(r1) - mfsdr1 r4 - stw r4,SL_SDR1(r1) - - /* Get a stable timebase and save it */ -1: mftbu r4 - stw r4,SL_TB(r1) - mftb r5 - stw r5,SL_TB+4(r1) - mftbu r3 - cmpw r3,r4 - bne 1b - - /* Save SPRGs */ - mfsprg r4,0 - stw r4,SL_SPRG0(r1) - mfsprg r4,1 - stw r4,SL_SPRG0+4(r1) - mfsprg r4,2 - stw r4,SL_SPRG0+8(r1) - mfsprg r4,3 - stw r4,SL_SPRG0+12(r1) - - /* Save BATs */ - mfdbatu r4,0 - stw r4,SL_DBAT0(r1) - mfdbatl r4,0 - stw r4,SL_DBAT0+4(r1) - mfdbatu r4,1 - stw r4,SL_DBAT1(r1) - mfdbatl r4,1 - stw r4,SL_DBAT1+4(r1) - mfdbatu r4,2 - stw r4,SL_DBAT2(r1) - mfdbatl r4,2 - stw r4,SL_DBAT2+4(r1) - mfdbatu r4,3 - stw r4,SL_DBAT3(r1) - mfdbatl r4,3 - stw r4,SL_DBAT3+4(r1) - mfibatu r4,0 - stw r4,SL_IBAT0(r1) - mfibatl r4,0 - stw r4,SL_IBAT0+4(r1) - mfibatu r4,1 - stw r4,SL_IBAT1(r1) - mfibatl r4,1 - stw r4,SL_IBAT1+4(r1) - mfibatu r4,2 - stw r4,SL_IBAT2(r1) - mfibatl r4,2 - stw r4,SL_IBAT2+4(r1) - mfibatu r4,3 - stw r4,SL_IBAT3(r1) - mfibatl r4,3 - stw r4,SL_IBAT3+4(r1) - - /* Save HID0 */ - mfspr r4,HID0 - stw r4,SL_HID0(r1) - - /* The ROM can wake us up via 2 different vectors: - * - On wallstreet & lombard, we must write a magic - * value 'Lars' at address 4 and a pointer to a - * memory location containing the PC to resume from - * at address 0. - * - On Core99, we must store the wakeup vector at - * address 0x80 and eventually it's parameters - * at address 0x84. I've have some trouble with those - * parameters however and I no longer use them. - */ - lis r5,grackle_wake_up@ha - addi r5,r5,grackle_wake_up@l - tophys(r5,r5) - stw r5,SL_PC(r1) - lis r4,KERNELBASE@h - tophys(r5,r1) - addi r5,r5,SL_PC - lis r6,MAGIC@ha - addi r6,r6,MAGIC@l - stw r5,0(r4) - stw r6,4(r4) - /* Setup stuffs at 0x80-0x84 for Core99 */ - lis r3,core99_wake_up@ha - addi r3,r3,core99_wake_up@l - tophys(r3,r3) - stw r3,0x80(r4) - stw r5,0x84(r4) - /* Store a pointer to our backup storage into - * a kernel global - */ - lis r3,sleep_storage@ha - addi r3,r3,sleep_storage@l - stw r5,0(r3) - - -/* - * Flush the L1 data cache by reading the first 128kB of RAM - * and then flushing the same area with the dcbf instruction. - * The L2 cache has already been disabled. - */ - li r4,0x1000 /* 128kB / 32B */ - mtctr r4 - lis r4,KERNELBASE@h -1: - lwz r0,0(r4) - addi r4,r4,0x0020 /* Go to start of next cache line */ - bdnz 1b - sync - - li r4,0x1000 /* 128kB / 32B */ - mtctr r4 - lis r4,KERNELBASE@h -1: - dcbf r0,r4 - addi r4,r4,0x0020 /* Go to start of next cache line */ - bdnz 1b - sync - -/* - * Set the HID0 and MSR for sleep. - */ - mfspr r2,HID0 - rlwinm r2,r2,0,10,7 /* clear doze, nap */ - oris r2,r2,HID0_SLEEP@h - sync - mtspr HID0,r2 - sync - -/* This loop puts us back to sleep in case we have a spurrious - * wakeup so that the host bridge properly stays asleep. The - * CPU will be turned off, either after a known time (about 1 - * second) on wallstreet & lombard, or as soon as the CPU enters - * SLEEP mode on core99 - */ - mfmsr r2 - oris r2,r2,MSR_POW@h -1: sync - mtmsr r2 - isync - b 1b - -/* - * Here is the resume code. - */ - - -/* - * Core99 machines resume here - * r4 has the physical address of SL_PC(sp) (unused) - */ -_GLOBAL(core99_wake_up) - /* Make sure HID0 no longer contains any sleep bit */ - mfspr r3,HID0 - rlwinm r3,r3,0,11,7 /* clear SLEEP, NAP, DOZE bits */ - mtspr HID0,r3 - sync - isync - - /* Won't that cause problems on CPU that doesn't support it ? */ - lis r3, 0 - mtspr SPRN_MMCR0, r3 - - /* sanitize MSR */ - mfmsr r3 - ori r3,r3,MSR_EE|MSR_IP - xori r3,r3,MSR_EE|MSR_IP - sync - isync - mtmsr r3 - sync - isync - - /* Recover sleep storage */ - lis r3,sleep_storage@ha - addi r3,r3,sleep_storage@l - tophys(r3,r3) - lwz r1,0(r3) - - /* Pass thru to older resume code ... */ -/* - * Here is the resume code for older machines. - * r1 has the physical address of SL_PC(sp). - */ - -grackle_wake_up: - /* Enable and then Flash inval the instruction & data cache */ - mfspr r3,HID0 - ori r3,r3, HID0_ICE|HID0_ICFI|HID0_DCE|HID0_DCI - sync - isync - mtspr HID0,r3 - xori r3,r3, HID0_ICFI|HID0_DCI - mtspr HID0,r3 - sync - - /* Restore the remaining bits of the HID0 register. */ - subi r1,r1,SL_PC - lwz r3,SL_HID0(r1) - sync - isync - mtspr HID0,r3 - sync - isync - - /* Restore the kernel's segment registers, the - BATs, and SDR1. Then we can turn on the MMU. */ - li r0,16 /* load up segment register values */ - mtctr r0 /* for context 0 */ - lis r3,0x2000 /* Ku = 1, VSID = 0 */ - li r4,0 -3: mtsrin r3,r4 - addi r3,r3,0x111 /* increment VSID */ - addis r4,r4,0x1000 /* address of next segment */ - bdnz 3b - - lwz r4,SL_SDR1(r1) - mtsdr1 r4 - lwz r4,SL_SPRG0(r1) - mtsprg 0,r4 - lwz r4,SL_SPRG0+4(r1) - mtsprg 1,r4 - lwz r4,SL_SPRG0+8(r1) - mtsprg 2,r4 - lwz r4,SL_SPRG0+12(r1) - mtsprg 3,r4 - - lwz r4,SL_DBAT0(r1) - mtdbatu 0,r4 - lwz r4,SL_DBAT0+4(r1) - mtdbatl 0,r4 - lwz r4,SL_DBAT1(r1) - mtdbatu 1,r4 - lwz r4,SL_DBAT1+4(r1) - mtdbatl 1,r4 - lwz r4,SL_DBAT2(r1) - mtdbatu 2,r4 - lwz r4,SL_DBAT2+4(r1) - mtdbatl 2,r4 - lwz r4,SL_DBAT3(r1) - mtdbatu 3,r4 - lwz r4,SL_DBAT3+4(r1) - mtdbatl 3,r4 - lwz r4,SL_IBAT0(r1) - mtibatu 0,r4 - lwz r4,SL_IBAT0+4(r1) - mtibatl 0,r4 - lwz r4,SL_IBAT1(r1) - mtibatu 1,r4 - lwz r4,SL_IBAT1+4(r1) - mtibatl 1,r4 - lwz r4,SL_IBAT2(r1) - mtibatu 2,r4 - lwz r4,SL_IBAT2+4(r1) - mtibatl 2,r4 - lwz r4,SL_IBAT3(r1) - mtibatu 3,r4 - lwz r4,SL_IBAT3+4(r1) - mtibatl 3,r4 - - /* Flush all TLBs */ - lis r4,0x1000 -1: addic. r4,r4,-0x1000 - tlbie r4 - blt 1b - sync - - /* restore the MSR and turn on the MMU */ - lwz r3,SL_MSR(r1) - bl turn_on_mmu - - /* get back the stack pointer */ - tovirt(r1,r1) - - /* Restore TB */ - li r3,0 - mttbl r3 - lwz r3,SL_TB(r1) - lwz r4,SL_TB+4(r1) - mttbu r3 - mttbl r4 - - /* Restore the callee-saved registers and return */ - lwz r2,SL_R2(r1) - lmw r12,SL_R12(r1) - addi r1,r1,SL_SIZE - lwz r0,4(r1) - mtlr r0 - blr - -turn_on_mmu: - mflr r4 - tovirt(r4,r4) - mtsrr0 r4 - mtsrr1 r3 - sync - isync - rfi - - .data - .globl sleep_storage -sleep_storage: - .long 0 diff -Nru a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c --- a/arch/ppc/kernel/smp.c Tue Feb 19 18:08:59 2002 +++ b/arch/ppc/kernel/smp.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.smp.c 1.34 10/11/01 12:06:01 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Smp support for ppc. @@ -37,8 +37,7 @@ #include #include #include - -#include "open_pic.h" +#include int smp_threads_ready; volatile int smp_commenced; @@ -51,11 +50,12 @@ spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; unsigned int prof_multiplier[NR_CPUS]; unsigned int prof_counter[NR_CPUS]; -cycles_t cacheflush_time; +unsigned long cache_decay_ticks; static int max_cpus __initdata = NR_CPUS; unsigned long cpu_online_map; int smp_hw_index[NR_CPUS]; static struct smp_ops_t *smp_ops; +struct thread_info *secondary_ti; /* all cpu mappings are 1-1 -- Cort */ volatile unsigned long cpu_callin_map[NR_CPUS]; @@ -68,6 +68,12 @@ extern int cpu_idle(void *unused); void smp_call_function_interrupt(void); void smp_message_pass(int target, int msg, unsigned long data, int wait); +static int __smp_call_function(void (*func) (void *info), void *info, + int wait, int target); + +#ifdef CONFIG_PPC_ISERIES +extern void smp_iSeries_space_timers( unsigned nr ); +#endif /* Since OpenPIC has only 4 IPIs, we use slightly different message numbers. * @@ -106,7 +112,7 @@ smp_call_function_interrupt(); break; case PPC_MSG_RESCHEDULE: - current->work.need_resched = 1; + set_need_resched(); break; case PPC_MSG_INVALIDATE_TLB: _tlbia(); @@ -190,8 +196,8 @@ * in the system. */ -int smp_call_function (void (*func) (void *info), void *info, int nonatomic, - int wait) +int smp_call_function(void (*func) (void *info), void *info, int nonatomic, + int wait) /* * [SUMMARY] Run a function on all other CPUs. * The function to run. This must be fast and non-blocking. @@ -205,12 +211,23 @@ * hardware interrupt handler, you may call it from a bottom half handler. */ { + if (smp_num_cpus <= 1) + return 0; + return __smp_call_function(func, info, wait, MSG_ALL_BUT_SELF); +} + +static int __smp_call_function(void (*func) (void *info), void *info, + int wait, int target) +{ struct call_data_struct data; - int ret = -1, cpus = smp_num_cpus-1; + int ret = -1; int timeout; + int ncpus = 1; - if (!cpus) - return 0; + if (target == MSG_ALL_BUT_SELF) + ncpus = smp_num_cpus - 1; + else if (target == MSG_ALL) + ncpus = smp_num_cpus; data.func = func; data.info = info; @@ -222,11 +239,11 @@ spin_lock_bh(&call_lock); call_data = &data; /* Send a message to all other CPUs and wait for them to respond */ - smp_message_pass(MSG_ALL_BUT_SELF, PPC_MSG_CALL_FUNCTION, 0, 0); + smp_message_pass(target, PPC_MSG_CALL_FUNCTION, 0, 0); /* Wait for response */ timeout = 1000000; - while (atomic_read(&data.started) != cpus) { + while (atomic_read(&data.started) != ncpus) { if (--timeout == 0) { printk("smp_call_function on cpu %d: other cpus not responding (%d)\n", smp_processor_id(), atomic_read(&data.started)); @@ -238,7 +255,7 @@ if (wait) { timeout = 1000000; - while (atomic_read(&data.finished) != cpus) { + while (atomic_read(&data.finished) != ncpus) { if (--timeout == 0) { printk("smp_call_function on cpu %d: other cpus not finishing (%d/%d)\n", smp_processor_id(), atomic_read(&data.finished), atomic_read(&data.started)); @@ -274,9 +291,28 @@ atomic_inc(&call_data->finished); } +/* + * Task migration callback. + */ +void smp_task_migration_interrupt(void *new_task) +{ + task_t *p; + + p = new_task; + sched_task_migrated(p); +} + +/* + * This function sends a 'task migration' IPI to another CPU. + * Must be called from syscall contexts, with interrupts *enabled*. + */ +void smp_migrate_task(int cpu, task_t *p) +{ + __smp_call_function(smp_task_migration_interrupt, p, 0, cpu); +} + void __init smp_boot_cpus(void) { - extern struct task_struct *current_set[NR_CPUS]; int i, cpu_nr; struct task_struct *p; @@ -290,9 +326,6 @@ * cpu 0, the master -- Cort */ cpu_callin_map[0] = 1; - current->processor = 0; - - init_idle(); for (i = 0; i < NR_CPUS; i++) { prof_counter[i] = 1; @@ -300,10 +333,9 @@ } /* - * XXX very rough, assumes 20 bus cycles to read a cache line, - * timebase increments every 4 bus cycles, 32kB L1 data cache. + * XXX very rough. */ - cacheflush_time = 5 * 1024; + cache_decay_ticks = HZ/100; smp_ops = ppc_md.smp_ops; if (smp_ops == NULL) { @@ -311,7 +343,7 @@ return; } - /* Probe arch for CPUs */ + /* Probe platform for CPUs */ cpu_nr = smp_ops->probe(); /* @@ -320,38 +352,26 @@ */ if (cpu_nr > max_cpus) cpu_nr = max_cpus; +#ifdef CONFIG_PPC_ISERIES + smp_iSeries_space_timers( cpu_nr ); +#endif for (i = 1; i < cpu_nr; i++) { int c; struct pt_regs regs; /* create a process for the processor */ - /* we don't care about the values in regs since we'll - never reschedule the forked task. */ - /* We DO care about one bit in the pt_regs we - pass to do_fork. That is the MSR_FP bit in - regs.msr. If that bit is on, then do_fork - (via copy_thread) will call giveup_fpu. - giveup_fpu will get a pointer to our (current's) - last register savearea via current->thread.regs - and using that pointer will turn off the MSR_FP, - MSR_FE0 and MSR_FE1 bits. At this point, this - pointer is pointing to some arbitrary point within - our stack. */ - + /* only regs.msr is actually used, and 0 is OK for it */ memset(®s, 0, sizeof(struct pt_regs)); - if (do_fork(CLONE_VM|CLONE_PID, 0, ®s, 0) < 0) panic("failed fork for CPU %d", i); p = init_task.prev_task; if (!p) panic("No idle task for CPU %d", i); - del_from_runqueue(p); + init_idle(p, i); unhash_process(p); - init_tasks[i] = p; - p->processor = i; - p->cpus_runnable = 1 << i; /* we schedule the first task manually */ - current_set[i] = p; + secondary_ti = p->thread_info; + p->thread_info->cpu = i; /* * There was a cache flush loop here to flush the cache @@ -368,11 +388,10 @@ * use this value that I found through experimentation. * -- Cort */ - for ( c = 1000; c && !cpu_callin_map[i] ; c-- ) + for (c = 1000; c && !cpu_callin_map[i]; c--) udelay(100); - if ( cpu_callin_map[i] ) - { + if (cpu_callin_map[i]) { char buf[32]; sprintf(buf, "found cpu %d", i); if (ppc_md.progress) ppc_md.progress(buf, 0x350+i); @@ -499,7 +518,7 @@ void __init smp_callin(void) { - int cpu = current->processor; + int cpu = smp_processor_id(); smp_store_cpu_info(cpu); set_dec(tb_ticks_per_jiffy); @@ -507,8 +526,6 @@ smp_ops->setup_cpu(cpu); - init_idle(); - /* * This cpu is now "online". Only set them online * before they enter the loop below since write access @@ -518,7 +535,7 @@ */ cpu_online_map |= 1UL << smp_processor_id(); - while(!smp_commenced) + while (!smp_commenced) barrier(); /* see smp_commence for more info */ diff -Nru a/arch/ppc/kernel/softemu8xx.c b/arch/ppc/kernel/softemu8xx.c --- a/arch/ppc/kernel/softemu8xx.c Tue Feb 19 18:09:00 2002 +++ b/arch/ppc/kernel/softemu8xx.c Tue Feb 19 18:09:00 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.softemu8xx.c 1.8 05/17/01 18:14:22 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * Software emulation of some PPC instructions for the 8xx core. @@ -35,6 +35,11 @@ #include #include +extern void +print_8xx_pte(struct mm_struct *mm, unsigned long addr); +extern int +get_8xx_pte(struct mm_struct *mm, unsigned long addr); + /* Eventually we may need a look-up table, but this works for now. */ #define LFS 48 @@ -117,7 +122,7 @@ default: retval = 1; printk("Bad emulation %s/%d\n" - " NIP: %08x instruction: %08x opcode: %x " + " NIP: %08lx instruction: %08x opcode: %x " "A: %x B: %x C: %x code: %x rc: %x\n", current->comm,current->pid, regs->nip, diff -Nru a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c --- a/arch/ppc/kernel/time.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/kernel/time.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.time.c 1.26 10/05/01 08:29:42 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Common time routines among all ppc machines. @@ -87,6 +87,11 @@ extern unsigned long wall_jiffies; +#ifdef CONFIG_PPC_ISERIES +extern u64 get_tb64(void); +extern u64 next_jiffy_update_tb[]; +#endif + static long time_offset; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; @@ -106,6 +111,8 @@ return delta; } +#ifndef CONFIG_PPC_ISERIES /* iSeries version is in iSeries_time.c */ + extern unsigned long prof_cpu_mask; extern unsigned int * prof_buffer; extern unsigned long prof_len; @@ -181,7 +188,7 @@ * We should have an rtc call that only sets the minutes and * seconds like on Intel to avoid problems with non UTC clocks. */ - if ( (time_status & STA_UNSYNC) == 0 && + if ( ppc_md.set_rtc_time && (time_status & STA_UNSYNC) == 0 && xtime.tv_sec - last_rtc_update >= 659 && abs(xtime.tv_usec - (1000000-1000000/HZ)) < 500000/HZ && jiffies - wall_jiffies == 1) { @@ -211,6 +218,7 @@ return 1; /* lets ret_from_int know we can do checks */ } +#endif /* CONFIG_PPC_ISERIES */ /* * This version of gettimeofday has microsecond resolution. @@ -223,7 +231,11 @@ read_lock_irqsave(&xtime_lock, flags); sec = xtime.tv_sec; usec = xtime.tv_usec; +#ifdef CONFIG_PPC_ISERIES + delta = tb_ticks_per_jiffy - ( next_jiffy_update_tb[0] - get_tb64() ); +#else delta = tb_ticks_since(tb_last_stamp); +#endif #ifdef CONFIG_SMP /* As long as timebases are not in sync, gettimeofday can only * have jiffy resolution on SMP. @@ -354,11 +366,10 @@ } } -#define TICK_SIZE tick -#define FEBRUARY 2 -#define STARTOFTIME 1970 -#define SECDAY 86400L -#define SECYR (SECDAY * 365) +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) #define leapyear(year) ((year) % 4 == 0) #define days_in_year(a) (leapyear(a) ? 366 : 365) #define days_in_month(a) (month_days[(a) - 1]) @@ -367,55 +378,12 @@ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; -/* - * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) - */ -void GregorianDay(struct rtc_time * tm) -{ - int leapsToDate; - int lastYear; - int day; - int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - - lastYear=tm->tm_year-1; - - /* - * Number of leap corrections to apply up to end of last year - */ - leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; - - /* - * This year is a leap year if it is divisible by 4 except when it is - * divisible by 100 unless it is divisible by 400 - * - * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be - */ - if((tm->tm_year%4==0) && - ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && - (tm->tm_mon>2)) - { - /* - * We are past Feb. 29 in a leap year - */ - day=1; - } - else - { - day=0; - } - - day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + - tm->tm_mday; - - tm->tm_wday=day%7; -} - void to_tm(int tim, struct rtc_time * tm) { - register int i; - register long hms, day; + register int i; + register long hms, day, gday; - day = tim / SECDAY; + gday = day = tim / SECDAY; hms = tim % SECDAY; /* Hours, minutes, seconds are easy */ @@ -440,9 +408,9 @@ tm->tm_mday = day + 1; /* - * Determine the day of week + * Determine the day of week. Jan. 1, 1970 was a Thursday. */ - GregorianDay(tm); + tm->tm_wday = (gday + 4) % 7; } /* Auxiliary function to compute scaling factors */ diff -Nru a/arch/ppc/kernel/todc_time.c b/arch/ppc/kernel/todc_time.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/kernel/todc_time.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,456 @@ +/* + * arch/ppc/kernel/todc_time.c + * + * Time of Day Clock support for the M48T35, M48T37, M48T59, and MC146818 + * Real Time Clocks/Timekeepers. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Depending on the hardware on your board and your board design, the + * RTC/NVRAM may be accessed either directly (like normal memory) or via + * address/data registers. If your board uses the direct method, set + * 'nvram_data' to the base address of your nvram and leave 'nvram_as0' and + * 'nvram_as1' NULL. If your board uses address/data regs to access nvram, + * set 'nvram_as0' to the address of the lower byte, set 'nvram_as1' to the + * address of the upper byte (leave NULL if using mv146818), and set + * 'nvram_data' to the address of the 8-bit data register. + * + * You also need to set 'ppc_md.nvram_read_val' and 'ppc_md.nvram_write_val' to + * the proper routines. There are standard ones defined further down in + * this file that you can use. + * + * There is a built in assumption that the RTC and NVRAM are accessed by the + * same mechanism (i.e., ppc_md.nvram_read_val, etc works for both). + * + * Note: Even though the documentation for the various RTC chips say that it + * take up to a second before it starts updating once the 'R' bit is + * cleared, they always seem to update even though we bang on it many + * times a second. This is true, except for the Dallas Semi 1746/1747 + * (possibly others). Those chips seem to have a real problem whenever + * we set the 'R' bit before reading them, they basically stop counting. + * --MAG + */ + +/* + * 'todc_info' should be initialized in your *_setup.c file to + * point to a fully initialized 'todc_info_t' structure. + * This structure holds all the register offsets for your particular + * TODC/RTC chip. + * TODC_ALLOC()/TODC_INIT() will allocate and initialize this table for you. + */ + +#ifdef RTC_FREQ_SELECT +#undef RTC_FREQ_SELECT +#define RTC_FREQ_SELECT control_b /* Register A */ +#endif + +#ifdef RTC_CONTROL +#undef RTC_CONTROL +#define RTC_CONTROL control_a /* Register B */ +#endif + +#ifdef RTC_INTR_FLAGS +#undef RTC_INTR_FLAGS +#define RTC_INTR_FLAGS watchdog /* Register C */ +#endif + +#ifdef RTC_VALID +#undef RTC_VALID +#define RTC_VALID interrupts /* Register D */ +#endif + +/* Access routines when RTC accessed directly (like normal memory) */ +u_char +todc_direct_read_val(int addr) +{ + return readb(todc_info->nvram_data + addr); +} + +void +todc_direct_write_val(int addr, unsigned char val) +{ + writeb(val, todc_info->nvram_data + addr); + return; +} + +/* Access routines for accessing m48txx type chips via addr/data regs */ +u_char +todc_m48txx_read_val(int addr) +{ + outb(addr, todc_info->nvram_as0); + outb(addr>>todc_info->as0_bits, todc_info->nvram_as1); + return inb(todc_info->nvram_data); +} + +void +todc_m48txx_write_val(int addr, unsigned char val) +{ + outb(addr, todc_info->nvram_as0); + outb(addr>>todc_info->as0_bits, todc_info->nvram_as1); + outb(val, todc_info->nvram_data); + return; +} + +/* Access routines for accessing mc146818 type chips via addr/data regs */ +u_char +todc_mc146818_read_val(int addr) +{ + outb(addr, todc_info->nvram_as0); + return inb(todc_info->nvram_data); +} + +void +todc_mc146818_write_val(int addr, unsigned char val) +{ + outb(addr, todc_info->nvram_as0); + outb(val, todc_info->nvram_data); + return; +} + +/* + * TODC routines + * + * There is some ugly stuff in that there are assumptions for the mc146818. + * + * Assumptions: + * - todc_info->control_a has the offset as mc146818 Register B reg + * - todc_info->control_b has the offset as mc146818 Register A reg + * - m48txx control reg's write enable or 'W' bit is same as + * mc146818 Register B 'SET' bit (i.e., 0x80) + * + * These assumptions were made to make the code simpler. + */ +long __init +todc_time_init(void) +{ + static u_char not_initialized = 1; + + /* Make sure clocks are running */ + if (not_initialized) { + u_char cntl_b; + + cntl_b = ppc_md.nvram_read_val(todc_info->control_b); + + if (todc_info->rtc_type == TODC_TYPE_MC146818) { + if ((cntl_b & 0x70) != 0x20) { + printk(KERN_INFO "TODC %s %s\n", + "real-time-clock was stopped.", + "Now starting..."); + cntl_b &= ~0x70; + cntl_b |= 0x20; + } + + ppc_md.nvram_write_val(todc_info->control_b, cntl_b); + } + else if (todc_info->rtc_type == TODC_TYPE_DS1501) { + u_char month; + + todc_info->enable_read = TODC_DS1501_CNTL_B_TE; + todc_info->enable_write = TODC_DS1501_CNTL_B_TE; + + month = ppc_md.nvram_read_val(todc_info->month); + + if ((month & 0x80) == 0x80) { + printk(KERN_INFO "TODC %s %s\n", + "real-time-clock was stopped.", + "Now starting..."); + month &= ~0x80; + ppc_md.nvram_write_val(todc_info->month, month); + } + + cntl_b &= ~TODC_DS1501_CNTL_B_TE; + ppc_md.nvram_write_val(todc_info->control_b, cntl_b); + } + else { /* must be a m48txx type */ + u_char cntl_a; + + todc_info->enable_read = TODC_MK48TXX_CNTL_A_R; + todc_info->enable_write = TODC_MK48TXX_CNTL_A_W; + + cntl_a = ppc_md.nvram_read_val(todc_info->control_a); + + /* Check & clear STOP bit in control B register */ + if (cntl_b & TODC_MK48TXX_DAY_CB) { + printk(KERN_INFO "TODC %s %s\n", + "real-time-clock was stopped.", + "Now starting..."); + + cntl_a |= todc_info->enable_write; + cntl_b &= ~TODC_MK48TXX_DAY_CB;/* Start Oscil */ + + ppc_md.nvram_write_val(todc_info->control_a, + cntl_a); + ppc_md.nvram_write_val(todc_info->control_b, + cntl_b); + } + + /* Make sure READ & WRITE bits are cleared. */ + cntl_a &= ~(todc_info->enable_write | + todc_info->enable_read); + ppc_md.nvram_write_val(todc_info->control_a, cntl_a); + } + + not_initialized = 0; + } + + + return 0; +} + +/* + * There is some ugly stuff in that there are assumptions that for a mc146818, + * the todc_info->control_a has the offset of the mc146818 Register B reg and + * that the register'ss 'SET' bit is the same as the m48txx's write enable + * bit in the control register of the m48txx (i.e., 0x80). + * + * It was done to make the code look simpler. + */ +ulong +todc_get_rtc_time(void) +{ + uint year, mon, day, hour, min, sec; + uint limit, i; + u_char save_control, uip; + + save_control = ppc_md.nvram_read_val(todc_info->control_a); + + if (todc_info->rtc_type != TODC_TYPE_MC146818) { + limit = 1; + + switch (todc_info->rtc_type) { + case TODC_TYPE_DS1557: + case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ + case TODC_TYPE_DS1747: + break; + default: + ppc_md.nvram_write_val(todc_info->control_a, + (save_control | todc_info->enable_read)); + } + } + else { + limit = 100000000; + } + + for (i=0; irtc_type == TODC_TYPE_MC146818) { + uip = ppc_md.nvram_read_val(todc_info->RTC_FREQ_SELECT); + } + + sec = ppc_md.nvram_read_val(todc_info->seconds) & 0x7f; + min = ppc_md.nvram_read_val(todc_info->minutes) & 0x7f; + hour = ppc_md.nvram_read_val(todc_info->hours) & 0x3f; + day = ppc_md.nvram_read_val(todc_info->day_of_month) & 0x3f; + mon = ppc_md.nvram_read_val(todc_info->month) & 0x1f; + year = ppc_md.nvram_read_val(todc_info->year) & 0xff; + + if (todc_info->rtc_type == TODC_TYPE_MC146818) { + uip |= ppc_md.nvram_read_val( + todc_info->RTC_FREQ_SELECT); + if ((uip & RTC_UIP) == 0) break; + } + } + + if (todc_info->rtc_type != TODC_TYPE_MC146818) { + switch (todc_info->rtc_type) { + case TODC_TYPE_DS1557: + case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ + case TODC_TYPE_DS1747: + break; + default: + save_control &= ~(todc_info->enable_read); + ppc_md.nvram_write_val(todc_info->control_a, + save_control); + } + } + + if ((todc_info->rtc_type != TODC_TYPE_MC146818) || + ((save_control & RTC_DM_BINARY) == 0) || + RTC_ALWAYS_BCD) { + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + + year = year + 1900; + if (year < 1970) { + year += 100; + } + + return mktime(year, mon, day, hour, min, sec); +} + +int +todc_set_rtc_time(unsigned long nowtime) +{ + struct rtc_time tm; + u_char save_control, save_freq_select; + + to_tm(nowtime, &tm); + + save_control = ppc_md.nvram_read_val(todc_info->control_a); + + /* Assuming MK48T59_RTC_CA_WRITE & RTC_SET are equal */ + ppc_md.nvram_write_val(todc_info->control_a, + (save_control | todc_info->enable_write)); + save_control &= ~(todc_info->enable_write); /* in case it was set */ + + if (todc_info->rtc_type == TODC_TYPE_MC146818) { + save_freq_select = + ppc_md.nvram_read_val(todc_info->RTC_FREQ_SELECT); + ppc_md.nvram_write_val(todc_info->RTC_FREQ_SELECT, + save_freq_select | RTC_DIV_RESET2); + } + + + tm.tm_year = (tm.tm_year - 1900) % 100; + + if ((todc_info->rtc_type != TODC_TYPE_MC146818) || + ((save_control & RTC_DM_BINARY) == 0) || + RTC_ALWAYS_BCD) { + + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + } + + ppc_md.nvram_write_val(todc_info->seconds, tm.tm_sec); + ppc_md.nvram_write_val(todc_info->minutes, tm.tm_min); + ppc_md.nvram_write_val(todc_info->hours, tm.tm_hour); + ppc_md.nvram_write_val(todc_info->month, tm.tm_mon); + ppc_md.nvram_write_val(todc_info->day_of_month, tm.tm_mday); + ppc_md.nvram_write_val(todc_info->year, tm.tm_year); + + ppc_md.nvram_write_val(todc_info->control_a, save_control); + + if (todc_info->rtc_type == TODC_TYPE_MC146818) { + ppc_md.nvram_write_val(todc_info->RTC_FREQ_SELECT, + save_freq_select); + } + + return 0; +} + +/* + * Manipulates read bit to reliably read seconds at a high rate. + */ +static unsigned char __init todc_read_timereg(int addr) +{ + unsigned char save_control, val; + + switch (todc_info->rtc_type) { + case TODC_TYPE_DS1557: + case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ + case TODC_TYPE_DS1747: + case TODC_TYPE_MC146818: + break; + default: + save_control = + ppc_md.nvram_read_val(todc_info->control_a); + ppc_md.nvram_write_val(todc_info->control_a, + (save_control | todc_info->enable_read)); + } + val = ppc_md.nvram_read_val(addr); + + switch (todc_info->rtc_type) { + case TODC_TYPE_DS1557: + case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ + case TODC_TYPE_DS1747: + case TODC_TYPE_MC146818: + break; + default: + save_control &= ~(todc_info->enable_read); + ppc_md.nvram_write_val(todc_info->control_a, + save_control); + } + + return val; +} + +/* + * This was taken from prep_setup.c + * Use the NVRAM RTC to time a second to calibrate the decrementer. + */ +void __init +todc_calibrate_decr(void) +{ + ulong freq; + ulong tbl, tbu; + long i, loop_count; + u_char sec; + + todc_time_init(); + + /* + * Actually this is bad for precision, we should have a loop in + * which we only read the seconds counter. nvram_read_val writes + * the address bytes on every call and this takes a lot of time. + * Perhaps an nvram_wait_change method returning a time + * stamp with a loop count as parameter would be the solution. + */ + /* + * Need to make sure the tbl doesn't roll over so if tbu increments + * during this test, we need to do it again. + */ + loop_count = 0; + + sec = todc_read_timereg(todc_info->seconds) & 0x7f; + + do { + tbu = get_tbu(); + + for (i = 0 ; i < 10000000 ; i++) {/* may take up to 1 second */ + tbl = get_tbl(); + + if ((todc_read_timereg(todc_info->seconds) & 0x7f) != sec) { + break; + } + } + + sec = todc_read_timereg(todc_info->seconds) & 0x7f; + + for (i = 0 ; i < 10000000 ; i++) { /* Should take 1 second */ + freq = get_tbl(); + + if ((todc_read_timereg(todc_info->seconds) & 0x7f) != sec) { + break; + } + } + + freq -= tbl; + } while ((get_tbu() != tbu) && (++loop_count < 2)); + + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + return; +} diff -Nru a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c --- a/arch/ppc/kernel/traps.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/kernel/traps.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.traps.c 1.22 10/11/01 10:33:09 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/arch/ppc/kernel/traps.c @@ -38,6 +38,9 @@ #include #include #include +#ifdef CONFIG_PMAC_BACKLIGHT +#include +#endif extern int fix_alignment(struct pt_regs *); extern void bad_page_fault(struct pt_regs *, unsigned long, int sig); @@ -66,6 +69,13 @@ int (*debugger_iabr_match)(struct pt_regs *regs); int (*debugger_dabr_match)(struct pt_regs *regs); void (*debugger_fault_handler)(struct pt_regs *regs); +#else +#define debugger(regs) do { } while (0) +#define debugger_bpt(regs) 0 +#define debugger_sstep(regs) 0 +#define debugger_iabr_match(regs) 0 +#define debugger_dabr_match(regs) 0 +#define debugger_fault_handler ((void (*)(struct pt_regs *))0) #endif #endif @@ -74,15 +84,19 @@ */ -spinlock_t oops_lock = SPIN_LOCK_UNLOCKED; +spinlock_t die_lock = SPIN_LOCK_UNLOCKED; void die(const char * str, struct pt_regs * fp, long err) { console_verbose(); - spin_lock_irq(&oops_lock); + spin_lock_irq(&die_lock); +#ifdef CONFIG_PMAC_BACKLIGHT + set_backlight_enable(1); + set_backlight_level(BACKLIGHT_MAX); +#endif printk("Oops: %s, sig: %ld\n", str, err); show_regs(fp); - spin_unlock_irq(&oops_lock); + spin_unlock_irq(&die_lock); /* do_exit() should take care of panic'ing from an interrupt * context so we don't handle it here */ @@ -94,9 +108,7 @@ { if (!user_mode(regs)) { -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) debugger(regs); -#endif die("Exception in kernel mode", regs, signr); } force_sig(signr, current); @@ -111,7 +123,7 @@ unsigned long msr = regs->msr; if (user_mode(regs)) { - _exception(SIGSEGV, regs); + _exception(SIGSEGV, regs); return; } @@ -120,12 +132,10 @@ bad_page_fault(regs, regs->dar, SIGBUS); return; #endif -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) if (debugger_fault_handler) { debugger_fault_handler(regs); return; } -#endif #ifdef CONFIG_ALL_PPC /* @@ -135,7 +145,7 @@ * table. * Note that the 601 only takes a machine check on TEA * (transfer error ack) signal assertion, and does not - * set of the top 16 bits of SRR1. + * set any of the top 16 bits of SRR1. * -- paulus. */ if (((msr & 0xffff0000) == 0 || (msr & (0x80000 | 0x40000))) @@ -169,12 +179,13 @@ #endif /* CONFIG_ALL_PPC */ printk("Machine check in kernel mode.\n"); printk("Caused by (from SRR1=%lx): ", msr); - switch (msr & 0xF0000) { + switch (msr & 0x601F0000) { case 0x80000: printk("Machine check signal\n"); break; case 0: /* for 601 */ case 0x40000: + case 0x140000: /* 7450 MSS error and TEA */ printk("Transfer error ack signal\n"); break; case 0x20000: @@ -183,26 +194,30 @@ case 0x10000: printk("Address parity error signal\n"); break; + case 0x20000000: + printk("L1 Data Cache error\n"); + break; + case 0x40000000: + printk("L1 Instruction Cache error\n"); + break; + case 0x00100000: + printk("L2 data cache parity error\n"); + break; default: printk("Unknown values in msr\n"); } -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) debugger(regs); -#endif die("machine check", regs, SIGBUS); } void SMIException(struct pt_regs *regs) { -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) - { - debugger(regs); - return; - } -#endif + debugger(regs); +#if !(defined(CONFIG_XMON) || defined(CONFIG_KGDB)) show_regs(regs); panic("System Management Interrupt"); +#endif } void @@ -210,23 +225,21 @@ { printk("Bad trap at PC: %lx, SR: %lx, vector=%lx %s\n", regs->nip, regs->msr, regs->trap, print_tainted()); - _exception(SIGTRAP, regs); + _exception(SIGTRAP, regs); } void InstructionBreakpoint(struct pt_regs *regs) { -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) if (debugger_iabr_match(regs)) return; -#endif _exception(SIGTRAP, regs); } void RunModeException(struct pt_regs *regs) { - _exception(SIGTRAP, regs); + _exception(SIGTRAP, regs); } /* Illegal instruction emulation support. Originally written to @@ -272,51 +285,54 @@ void ProgramCheckException(struct pt_regs *regs) { + int errcode; + #if defined(CONFIG_4xx) unsigned int esr = mfspr(SPRN_ESR); + int isbpt = esr & ESR_PTR; + extern int do_mathemu(struct pt_regs *regs); + + if (isbpt) + mtspr(SPRN_DBSR, DBSR_TIE); +#ifdef CONFIG_MATH_EMULATION + if (!isbpt && do_mathemu(regs) == 0) + return; +#endif /* CONFIG_MATH_EMULATION */ + +#else /* ! CONFIG_4xx */ + int isbpt = regs->msr & 0x20000; - if (esr & ESR_PTR) { -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) - if (debugger_bpt(regs)) - return; -#endif - _exception(SIGTRAP, regs); - } else { - _exception(SIGILL, regs); - } -#else if (regs->msr & 0x100000) { /* IEEE FP exception */ _exception(SIGFPE, regs); - } else if (regs->msr & 0x20000) { + return; + } +#endif /* ! CONFIG_4xx */ + + if (isbpt) { /* trap exception */ -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) if (debugger_bpt(regs)) return; -#endif _exception(SIGTRAP, regs); - } else { - /* Try to emulate it if we should. */ - int errcode; - if ((errcode = emulate_instruction(regs))) { - if (errcode == -EFAULT) - _exception(SIGBUS, regs); - else - _exception(SIGILL, regs); - } + return; + } + + /* Try to emulate it if we should. */ + if ((errcode = emulate_instruction(regs))) { + if (errcode == -EFAULT) + _exception(SIGBUS, regs); + else + _exception(SIGILL, regs); } -#endif } void SingleStepException(struct pt_regs *regs) { regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) if (debugger_sstep(regs)) return; -#endif - _exception(SIGTRAP, regs); + _exception(SIGTRAP, regs); } void @@ -337,7 +353,7 @@ bad_page_fault(regs, regs->dar, SIGSEGV); return; } - _exception(SIGBUS, regs); + _exception(SIGBUS, regs); } void @@ -345,9 +361,7 @@ { printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n", current, regs->gpr[1]); -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) debugger(regs); -#endif show_regs(regs); panic("kernel stack overflow"); } @@ -365,20 +379,20 @@ SoftwareEmulation(struct pt_regs *regs) { extern int do_mathemu(struct pt_regs *); + extern int Soft_emulate_8xx(struct pt_regs *); int errcode; if (!user_mode(regs)) { -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) debugger(regs); -#endif die("Kernel Mode Software FPU Emulation", regs, SIGFPE); } #ifdef CONFIG_MATH_EMULATION - if ((errcode = do_mathemu(regs))) { + errcode = do_mathemu(regs); #else - if ((errcode = Soft_emulate_8xx(regs))) { + errcode = Soft_emulate_8xx(regs); #endif + if (errcode) { if (errcode > 0) _exception(SIGFPE, regs); else if (errcode == -EFAULT) @@ -387,7 +401,36 @@ _exception(SIGILL, regs); } } -#endif +#endif /* CONFIG_8xx */ + +#if defined(CONFIG_4xx) + +void DebugException(struct pt_regs *regs) +{ + unsigned long debug_status; + + debug_status = mfspr(SPRN_DBSR); + + regs->msr &= ~MSR_DE; /* Turn off 'debug' bit */ + if (debug_status & DBSR_TIE) { /* trap instruction*/ + + mtspr(SPRN_DBSR, DBSR_TIE); + + if (!user_mode(regs) && debugger_bpt(regs)) + return; + _exception(SIGTRAP, regs); + + } else if (debug_status & DBSR_IC) { /* instruction completion */ + + mtspr(SPRN_DBSR, DBSR_IC); + regs->dbcr0 &= ~DBCR0_IC; + + if (!user_mode(regs) && debugger_sstep(regs)) + return; + _exception(SIGTRAP, regs); + } +} +#endif /* CONFIG_4xx */ #if !defined(CONFIG_TAU_INT) void diff -Nru a/arch/ppc/kernel/walnut_setup.c b/arch/ppc/kernel/walnut_setup.c --- a/arch/ppc/kernel/walnut_setup.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,292 +0,0 @@ -/* - * BK Id: SCCS/s.walnut_setup.c 1.10 11/13/01 21:26:07 paulus - */ -/* - * - * Copyright (c) 1999-2000 Grant Erickson - * - * Module name: walnut_setup.c - * - * Description: - * Architecture- / platform-specific boot-time initialization code for - * the IBM PowerPC 403GP "Walnut" evaluation board. Adapted from original - * code by Gary Thomas, Cort Dougan , and Dan Malek - * . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "local_irq.h" -#include "ppc4xx_pic.h" -#include -#include "walnut_setup.h" - - -/* Function Prototypes */ - -extern void abort(void); - -/* Global Variables */ - -unsigned char __res[sizeof(bd_t)]; - - -/* - * void __init walnut_init() - * - * Description: - * This routine... - * - * Input(s): - * r3 - Optional pointer to a board information structure. - * r4 - Optional pointer to the physical starting address of the init RAM - * disk. - * r5 - Optional pointer to the physical ending address of the init RAM - * disk. - * r6 - Optional pointer to the physical starting address of any kernel - * command-line parameters. - * r7 - Optional pointer to the physical ending address of any kernel - * command-line parameters. - * - * Output(s): - * N/A - * - * Returns: - * N/A - * - */ -void __init -walnut_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - /* - * If we were passed in a board information, copy it into the - * residual data area. - */ - if (r3) { - memcpy((void *)__res, (void *)(r3 + KERNELBASE), sizeof(bd_t)); - } - -#if defined(CONFIG_BLK_DEV_INITRD) - /* - * If the init RAM disk has been configured in, and there's a valid - * starting address for it, set it up. - */ - if (r4) { - initrd_start = r4 + KERNELBASE; - initrd_end = r5 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_INITRD */ - - /* Copy the kernel command line arguments to a safe place. */ - - if (r6) { - *(char *)(r7 + KERNELBASE) = 0; - strcpy(cmd_line, (char *)(r6 + KERNELBASE)); - } - - /* Initialize machine-dependency vectors */ - - ppc_md.setup_arch = walnut_setup_arch; - ppc_md.show_percpuinfo = walnut_show_percpuinfo; - ppc_md.irq_cannonicalize = NULL; - ppc_md.init_IRQ = walnut_init_IRQ; - ppc_md.get_irq = walnut_get_irq; - ppc_md.init = NULL; - - ppc_md.restart = walnut_restart; - ppc_md.power_off = walnut_power_off; - ppc_md.halt = walnut_halt; - - ppc_md.time_init = walnut_time_init; - ppc_md.set_rtc_time = walnut_set_rtc_time; - ppc_md.get_rtc_time = walnut_get_rtc_time; - ppc_md.calibrate_decr = walnut_calibrate_decr; - - ppc_md.kbd_setkeycode = NULL; - ppc_md.kbd_getkeycode = NULL; - ppc_md.kbd_translate = NULL; - ppc_md.kbd_unexpected_up = NULL; - ppc_md.kbd_leds = NULL; - ppc_md.kbd_init_hw = NULL; - ppc_md.ppc_kbd_sysrq_xlate = NULL; -} - -/* - * Document me. - */ -void __init -walnut_setup_arch(void) -{ - /* XXX - Implement me */ -} - -/* - * int walnut_show_percpuinfo() - * - * Description: - * This routine pretty-prints the platform's internal CPU and bus clock - * frequencies into the buffer for usage in /proc/cpuinfo. - * - * Input(s): - * *buffer - Buffer into which CPU and bus clock frequencies are to be - * printed. - * - * Output(s): - * *buffer - Buffer with the CPU and bus clock frequencies. - * - * Returns: - * The number of bytes copied into 'buffer' if OK, otherwise zero or less - * on error. - */ -int -walnut_show_percpuinfo(struct seq_file *m) -{ - bd_t *bp = (bd_t *)__res; - - seq_printf(m, "clock\t\t: %dMHz\n" - "bus clock\t\t: %dMHz\n", - bp->bi_intfreq / 1000000, - bp->bi_busfreq / 1000000); - - return 0; -} - -/* - * Document me. - */ -void __init -walnut_init_IRQ(void) -{ - int i; - - ppc4xx_pic_init(); - - for (i = 0; i < NR_IRQS; i++) { - irq_desc[i].handler = ppc4xx_pic; - } - - return; -} - -/* - * Document me. - */ -int -walnut_get_irq(struct pt_regs *regs) -{ - return (ppc4xx_pic_get_irq(regs)); -} - -/* - * Document me. - */ -void -walnut_restart(char *cmd) -{ - abort(); -} - -/* - * Document me. - */ -void -walnut_power_off(void) -{ - walnut_restart(NULL); -} - -/* - * Document me. - */ -void -walnut_halt(void) -{ - walnut_restart(NULL); -} - -/* - * Document me. - */ -long __init -walnut_time_init(void) -{ - /* XXX - Implement me */ - return 0; -} - -/* - * Document me. - */ -int __init -walnut_set_rtc_time(unsigned long time) -{ - /* XXX - Implement me */ - - return (0); -} - -/* - * Document me. - */ -unsigned long __init -walnut_get_rtc_time(void) -{ - /* XXX - Implement me */ - - return (0); -} - -/* - * void __init walnut_calibrate_decr() - * - * Description: - * This routine retrieves the internal processor frequency from the board - * information structure, sets up the kernel timer decrementer based on - * that value, enables the 403 programmable interval timer (PIT) and sets - * it up for auto-reload. - * - * Input(s): - * N/A - * - * Output(s): - * N/A - * - * Returns: - * N/A - * - */ -void __init -walnut_calibrate_decr(void) -{ - unsigned int freq; - bd_t *bip = (bd_t *)__res; - - freq = bip->bi_intfreq; - - decrementer_count = freq / HZ; - count_period_num = 1; - count_period_den = freq; - - /* Enable the PIT and set auto-reload of its value */ - - mtspr(SPRN_TCR, TCR_PIE | TCR_ARE); - - /* Clear any pending timer interrupts */ - - mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS); -} diff -Nru a/arch/ppc/kernel/walnut_setup.h b/arch/ppc/kernel/walnut_setup.h --- a/arch/ppc/kernel/walnut_setup.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,53 +0,0 @@ -/* - * BK Id: SCCS/s.walnut_setup.h 1.5 05/17/01 18:14:22 cort - */ -/* - * - * Copyright (c) 1999-2000 Grant Erickson - * - * Module name: walnut_setup.c - * - * Description: - * Architecture- / platform-specific boot-time initialization code for - * the IBM PowerPC 405GP "Walnut" evaluation board. Adapted from original - * code by Gary Thomas, Cort Dougan , and Dan Malek - * . - * - */ - -#ifndef __WALNUT_SETUP_H__ -#define __WALNUT_SETUP_H__ - -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -extern unsigned char __res[sizeof(bd_t)]; - -extern void walnut_init(unsigned long r3, - unsigned long ird_start, - unsigned long ird_end, - unsigned long cline_start, - unsigned long cline_end); -extern void walnut_setup_arch(void); -extern int walnut_setup_residual(char *buffer); -extern void walnut_init_IRQ(void); -extern int walnut_get_irq(struct pt_regs *regs); -extern void walnut_restart(char *cmd); -extern void walnut_power_off(void); -extern void walnut_halt(void); -extern void walnut_time_init(void); -extern int walnut_set_rtc_time(unsigned long now); -extern unsigned long walnut_get_rtc_time(void); -extern void walnut_calibrate_decr(void); - - -#ifdef __cplusplus -} -#endif - -#endif /* __WALNUT_SETUP_H__ */ diff -Nru a/arch/ppc/kernel/xics.c b/arch/ppc/kernel/xics.c --- a/arch/ppc/kernel/xics.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,217 +0,0 @@ -/* - * BK Id: SCCS/s.xics.c 1.5 05/17/01 18:14:22 cort - */ -/* - * arch/ppc/kernel/xics.c - * - * Copyright 2000 IBM Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include "i8259.h" -#include "xics.h" - -void xics_enable_irq(u_int irq); -void xics_disable_irq(u_int irq); -void xics_mask_and_ack_irq(u_int irq); -void xics_end_irq(u_int irq); - -struct hw_interrupt_type xics_pic = { - " XICS ", - NULL, - NULL, - xics_enable_irq, - xics_disable_irq, - xics_mask_and_ack_irq, - xics_end_irq -}; - -struct hw_interrupt_type xics_8259_pic = { - " XICS/8259", - NULL, - NULL, - NULL, - NULL, - xics_mask_and_ack_irq, - NULL -}; - -#define XICS_IPI 2 -#define XICS_IRQ_8259_CASCADE 0x2c -#define XICS_IRQ_OFFSET 16 -#define XICS_IRQ_SPURIOUS 0 - -#define DEFAULT_SERVER 0 -#define DEFAULT_PRIORITY 0 - -struct xics_ipl { - union { - u32 word; - u8 bytes[4]; - } xirr_poll; - union { - u32 word; - u8 bytes[4]; - } xirr; - u32 dummy; - union { - u32 word; - u8 bytes[4]; - } qirr; -}; - -struct xics_info { - volatile struct xics_ipl * per_cpu[NR_CPUS]; -}; - -struct xics_info xics_info; - -#define xirr_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr.word) -#define cppr_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr.bytes[0]) -#define poll_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr_poll.word) -#define qirr_info(n_cpu) (xics_info.per_cpu[n_cpu]->qirr.bytes[0]) - -void -xics_enable_irq( - u_int irq - ) -{ - int status; - int call_status; - - irq -= XICS_IRQ_OFFSET; - if (irq == XICS_IPI) - return; - call_status = call_rtas("ibm,set-xive", 3, 1, (ulong*)&status, - irq, DEFAULT_SERVER, DEFAULT_PRIORITY); - if( call_status != 0 ) { - printk("xics_enable_irq: irq=%x: call_rtas failed; retn=%x, status=%x\n", - irq, call_status, status); - return; - } -} - -void -xics_disable_irq( - u_int irq - ) -{ - int status; - int call_status; - - irq -= XICS_IRQ_OFFSET; - call_status = call_rtas("ibm,int-off", 1, 1, (ulong*)&status, irq); - if( call_status != 0 ) { - printk("xics_disable_irq: irq=%x: call_rtas failed, retn=%x\n", - irq, call_status); - return; - } -} - -void -xics_end_irq( - u_int irq - ) -{ - int cpu = smp_processor_id(); - - cppr_info(cpu) = 0; /* actually the value overwritten by ack */ - xirr_info(cpu) = (0xff<<24) | (irq-XICS_IRQ_OFFSET); -} - -void -xics_mask_and_ack_irq( - u_int irq - ) -{ - int cpu = smp_processor_id(); - - if( irq < XICS_IRQ_OFFSET ) { - i8259_pic.ack(irq); - xirr_info(cpu) = (0xff<<24) | XICS_IRQ_8259_CASCADE; - } - else { - cppr_info(cpu) = 0xff; - } -} - -int -xics_get_irq(struct pt_regs *regs) -{ - u_int cpu = smp_processor_id(); - u_int vec; - int irq; - - vec = xirr_info(cpu); - /* (vec >> 24) == old priority */ - vec &= 0x00ffffff; - /* for sanity, this had better be < NR_IRQS - 16 */ - if( vec == XICS_IRQ_8259_CASCADE ) - irq = i8259_irq(cpu); - else if( vec == XICS_IRQ_SPURIOUS ) - irq = -1; - else - irq = vec + XICS_IRQ_OFFSET; - return irq; -} - -#ifdef CONFIG_SMP -void xics_ipi_action(int irq, void *dev_id, struct pt_regs *regs) -{ - qirr_info(smp_processor_id()) = 0xff; - smp_message_recv(MSG_RESCHEDULE, regs); -} - -void xics_cause_IPI(int cpu) -{ - qirr_info(cpu) = 0; -} - -void xics_setup_cpu(void) -{ - int cpu = smp_processor_id(); - - cppr_info(cpu) = 0xff; -} -#endif /* CONFIG_SMP */ - -void -xics_init_IRQ( void ) -{ - int i; - extern unsigned long smp_chrp_cpu_nr; - -#ifdef CONFIG_SMP - for (i = 0; i < smp_chrp_cpu_nr; ++i) - xics_info.per_cpu[i] = - ioremap(0xfe000000 + smp_hw_index[i] * 0x1000, 0x20); -#else - xics_info.per_cpu[0] = ioremap(0xfe000000, 0x20); -#endif /* CONFIG_SMP */ - xics_8259_pic.enable = i8259_pic.enable; - xics_8259_pic.disable = i8259_pic.disable; - for (i = 0; i < 16; ++i) - irq_desc[i].handler = &xics_8259_pic; - for (; i < NR_IRQS; ++i) - irq_desc[i].handler = &xics_pic; - - cppr_info(0) = 0xff; - if (request_irq(XICS_IRQ_8259_CASCADE + XICS_IRQ_OFFSET, no_action, - 0, "8259 cascade", 0)) - printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n"); - i8259_init(); - -#ifdef CONFIG_SMP - request_irq(XICS_IPI + XICS_IRQ_OFFSET, xics_ipi_action, 0, "IPI", 0); -#endif -} diff -Nru a/arch/ppc/kernel/xics.h b/arch/ppc/kernel/xics.h --- a/arch/ppc/kernel/xics.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,26 +0,0 @@ -/* - * BK Id: SCCS/s.xics.h 1.5 05/17/01 18:14:22 cort - */ -/* - * arch/ppc/kernel/xics.h - * - * Copyright 2000 IBM Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#ifndef _PPC_KERNEL_XICS_H -#define _PPC_KERNEL_XICS_H - -#include "local_irq.h" - -extern struct hw_interrupt_type xics_pic; -extern struct hw_interrupt_type xics_8259_pic; - -void xics_init_IRQ(void); -int xics_get_irq(struct pt_regs *); - -#endif /* _PPC_KERNEL_XICS_H */ diff -Nru a/arch/ppc/lib/checksum.S b/arch/ppc/lib/checksum.S --- a/arch/ppc/lib/checksum.S Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/lib/checksum.S Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.checksum.S 1.8 08/20/01 22:09:34 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * This file contains assembly-language implementations @@ -18,7 +18,7 @@ #include #include #include -#include "../kernel/ppc_asm.tmpl" +#include .text diff -Nru a/arch/ppc/lib/locks.c b/arch/ppc/lib/locks.c --- a/arch/ppc/lib/locks.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/lib/locks.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.locks.c 1.11 08/19/01 22:27:32 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * Locks for smp ppc @@ -35,8 +35,9 @@ __asm__ __volatile__ ("\n\ 1: lwarx %0,0,%1\n\ cmpwi 0,%0,0\n\ - bne 2f\n\ - stwcx. %2,0,%1\n\ + bne 2f\n" + PPC405_ERR77(0,%1) +" stwcx. %2,0,%1\n\ bne- 1b\n\ isync\n\ 2:" @@ -47,7 +48,7 @@ return ret; } -void _spin_lock(spinlock_t *lock) +void _raw_spin_lock(spinlock_t *lock) { int cpu = smp_processor_id(); unsigned int stuck = INIT_STUCK; @@ -68,7 +69,7 @@ lock->owner_cpu = cpu; } -int spin_trylock(spinlock_t *lock) +int _raw_spin_trylock(spinlock_t *lock) { if (__spin_trylock(&lock->lock)) return 0; @@ -77,7 +78,7 @@ return 1; } -void _spin_unlock(spinlock_t *lp) +void _raw_spin_unlock(spinlock_t *lp) { if ( !lp->lock ) printk("_spin_unlock(%p): no lock cpu %d curr PC %p %s/%d\n", @@ -98,7 +99,7 @@ * with the high bit (sign) being the "write" bit. * -- Cort */ -void _read_lock(rwlock_t *rw) +void _raw_read_lock(rwlock_t *rw) { unsigned long stuck = INIT_STUCK; int cpu = smp_processor_id(); @@ -125,7 +126,7 @@ wmb(); } -void _read_unlock(rwlock_t *rw) +void _raw_read_unlock(rwlock_t *rw) { if ( rw->lock == 0 ) printk("_read_unlock(): %s/%d (nip %08lX) lock %lx\n", @@ -135,7 +136,7 @@ atomic_dec((atomic_t *) &(rw)->lock); } -void _write_lock(rwlock_t *rw) +void _raw_write_lock(rwlock_t *rw) { unsigned long stuck = INIT_STUCK; int cpu = smp_processor_id(); @@ -175,7 +176,7 @@ wmb(); } -void _write_unlock(rwlock_t *rw) +void _raw_write_unlock(rwlock_t *rw) { if ( !(rw->lock & (1<<31)) ) printk("_write_lock(): %s/%d (nip %08lX) lock %lx\n", diff -Nru a/arch/ppc/lib/string.S b/arch/ppc/lib/string.S --- a/arch/ppc/lib/string.S Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/lib/string.S Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.string.S 1.9 10/25/01 10:08:51 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * String handling functions for PowerPC. @@ -11,11 +11,11 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ -#include "../kernel/ppc_asm.tmpl" #include #include #include #include +#include #define COPY_16_BYTES \ lwz r7,4(r4); \ @@ -65,13 +65,14 @@ .text .text + .stabs "arch/ppc/lib/",N_SO,0,0,0f + .stabs "string.S",N_SO,0,0,0f CACHELINE_BYTES = L1_CACHE_LINE_SIZE LG_CACHELINE_BYTES = LG_L1_CACHE_LINE_SIZE CACHELINE_MASK = (L1_CACHE_LINE_SIZE-1) - .globl strcpy -strcpy: +_GLOBAL(strcpy) addi r5,r3,-1 addi r4,r4,-1 1: lbzu r0,1(r4) @@ -80,8 +81,7 @@ bne 1b blr - .globl strncpy -strncpy: +_GLOBAL(strncpy) cmpwi 0,r5,0 beqlr mtctr r5 @@ -93,8 +93,7 @@ bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ blr - .globl strcat -strcat: +_GLOBAL(strcat) addi r5,r3,-1 addi r4,r4,-1 1: lbzu r0,1(r5) @@ -107,8 +106,7 @@ bne 1b blr - .globl strcmp -strcmp: +_GLOBAL(strcmp) addi r5,r3,-1 addi r4,r4,-1 1: lbzu r3,1(r5) @@ -119,8 +117,7 @@ beq 1b blr - .globl strlen -strlen: +_GLOBAL(strlen) addi r4,r3,-1 1: lbzu r0,1(r4) cmpwi 0,r0,0 @@ -133,8 +130,7 @@ * to set them to zero. This requires that the destination * area is cacheable. -- paulus */ - .globl cacheable_memzero -cacheable_memzero: +_GLOBAL(cacheable_memzero) mr r5,r4 li r4,0 addi r6,r3,-4 @@ -165,6 +161,12 @@ stw r4, 8(r6) stw r4, 12(r6) stw r4, 16(r6) +#if CACHE_LINE_SIZE >= 32 + stw r4, 20(r6) + stw r4, 24(r6) + stw r4, 28(r6) + stw r4, 32(r6) +#endif /* CACHE_LINE_SIZE */ #endif addi r6,r6,CACHELINE_BYTES bdnz 10b @@ -184,8 +186,7 @@ bdnz 8b blr - .globl memset -memset: +_GLOBAL(memset) rlwimi r4,r4,8,16,23 rlwimi r4,r4,16,0,15 addi r6,r3,-4 @@ -210,8 +211,7 @@ bdnz 8b blr - .globl bcopy -bcopy: +_GLOBAL(bcopy) mr r6,r3 mr r3,r4 mr r4,r6 @@ -224,8 +224,7 @@ * We only use this version if the source and dest don't overlap. * -- paulus. */ - .global cacheable_memcpy -cacheable_memcpy: +_GLOBAL(cacheable_memcpy) add r7,r3,r5 /* test if the src & dst overlap */ add r8,r4,r5 cmplw 0,r4,r7 @@ -299,14 +298,12 @@ bdnz 40b 65: blr - .globl memmove -memmove: +_GLOBAL(memmove) cmplw 0,r3,r4 bgt backwards_memcpy /* fall through */ - .globl memcpy -memcpy: +_GLOBAL(memcpy) srwi. r7,r5,3 addi r6,r3,-4 addi r4,r4,-4 @@ -347,8 +344,7 @@ mtctr r7 b 1b - .globl backwards_memcpy -backwards_memcpy: +_GLOBAL(backwards_memcpy) rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ add r6,r3,r5 add r4,r4,r5 @@ -383,9 +379,8 @@ beq 2b mtctr r7 b 1b - - .globl memcmp -memcmp: + +_GLOBAL(memcmp) cmpwi 0,r5,0 ble- 2f mtctr r5 @@ -399,8 +394,7 @@ 2: li r3,0 blr - .global memchr -memchr: +_GLOBAL(memchr) cmpwi 0,r5,0 ble- 2f mtctr r5 @@ -412,8 +406,7 @@ 2: li r3,0 blr - .globl __copy_tofrom_user -__copy_tofrom_user: +_GLOBAL(__copy_tofrom_user) addi r4,r4,-4 addi r6,r3,-4 neg r0,r3 @@ -445,23 +438,23 @@ #if !defined(CONFIG_8xx) /* Here we decide how far ahead to prefetch the source */ -#if MAX_L1_COPY_PREFETCH > 1 +#if MAX_COPY_PREFETCH > 1 /* Heuristically, for large transfers we prefetch - MAX_L1_COPY_PREFETCH cachelines ahead. For small transfers + MAX_COPY_PREFETCH cachelines ahead. For small transfers we prefetch 1 cacheline ahead. */ - cmpwi r0,MAX_L1_COPY_PREFETCH + cmpwi r0,MAX_COPY_PREFETCH li r7,1 li r3,4 ble 111f - li r7,MAX_L1_COPY_PREFETCH + li r7,MAX_COPY_PREFETCH 111: mtctr r7 112: dcbt r3,r4 addi r3,r3,CACHELINE_BYTES bdnz 112b -#else /* MAX_L1_COPY_PREFETCH == 1 */ +#else /* MAX_COPY_PREFETCH == 1 */ li r3,CACHELINE_BYTES + 4 dcbt r11,r4 -#endif /* MAX_L1_COPY_PREFETCH */ +#endif /* MAX_COPY_PREFETCH */ #endif /* CONFIG_8xx */ mtctr r0 @@ -606,8 +599,7 @@ .long 114b,120b .text - .globl __clear_user -__clear_user: +_GLOBAL(__clear_user) addi r6,r3,-4 li r3,0 li r5,0 @@ -644,8 +636,7 @@ .long 8b,99b .text - .globl __strncpy_from_user -__strncpy_from_user: +_GLOBAL(__strncpy_from_user) addi r6,r3,-1 addi r4,r4,-1 cmpwi 0,r5,0 @@ -668,8 +659,7 @@ .text /* r3 = str, r4 = len (> 0), r5 = top (highest addr) */ - .globl __strnlen_user -__strnlen_user: +_GLOBAL(__strnlen_user) addi r7,r3,-1 subf r6,r7,r5 /* top+1 - str */ cmplw 0,r4,r6 diff -Nru a/arch/ppc/mm/4xx_mmu.c b/arch/ppc/mm/4xx_mmu.c --- a/arch/ppc/mm/4xx_mmu.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/mm/4xx_mmu.c Tue Feb 19 18:08:57 2002 @@ -93,6 +93,6 @@ * vectors and the kernel live in real-mode. */ - mtspr(SPRN_DCCR, 0x80000000); /* 128 MB of data space at 0x0. */ - mtspr(SPRN_ICCR, 0x80000000); /* 128 MB of instr. space at 0x0. */ + mtspr(SPRN_DCCR, 0xF0000000); /* 512 MB of data space at 0x0. */ + mtspr(SPRN_ICCR, 0xF0000000); /* 512 MB of instr. space at 0x0. */ } diff -Nru a/arch/ppc/mm/4xx_tlb.c b/arch/ppc/mm/4xx_tlb.c --- a/arch/ppc/mm/4xx_tlb.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,361 +0,0 @@ -/* - * BK Id: SCCS/s.4xx_tlb.c 1.5 05/17/01 18:14:23 cort - */ -/* - * - * Copyright (c) 1998-1999 TiVo, Inc. - * Original implementation. - * Copyright (c) 1999-2000 Grant Erickson - * Minor rework. - * - * Module name: 4xx_tlb.c - * - * Description: - * Routines for manipulating the TLB on PowerPC 400-class processors. - * - */ - -#include - -#include -#include -#include -#include -#include - - -/* Preprocessor Defines */ - -#if !defined(TRUE) || TRUE != 1 -#define TRUE 1 -#endif - -#if !defined(FALSE) || FALSE != 0 -#define FALSE 0 -#endif - - -/* Global Variables */ - -static int pinned = 0; - - -/* Function Prototypes */ - -static int PPC4xx_tlb_miss(struct pt_regs *, unsigned long, int); - -extern void do_page_fault(struct pt_regs *, unsigned long, unsigned long); - - -/* - * () - * - * Description: - * This routine... - * - * Input(s): - * - * - * Output(s): - * - * - * Returns: - * - * - */ -static inline void -PPC4xx_tlb_write(unsigned long tag, unsigned long data, unsigned int index) -{ - asm("tlbwe %0,%1,1" : : "r" (data), "r" (index)); - asm("tlbwe %0,%1,0" : : "r" (tag), "r" (index)); -} - -/* - * () - * - * Description: - * This routine... - * - * Input(s): - * - * - * Output(s): - * - * - * Returns: - * - * - */ -void -PPC4xx_flush_tlb_all(void) -{ - int i; - unsigned long flags, pid; - - save_flags(flags); - cli(); - - pid = mfspr(SPRN_PID); - mtspr(SPRN_PID, 0); - - for (i = pinned; i < PPC4XX_TLB_SIZE; i++) { - PPC4xx_tlb_write(0, 0, i); - } - asm("sync;isync"); - - mtspr(SPRN_PID, pid); - restore_flags(flags); -} - -/* - * () - * - * Description: - * This routine... - * - * Input(s): - * - * - * Output(s): - * - * - * Returns: - * - * - */ -void -PPC4xx_dtlb_miss(struct pt_regs *regs) -{ - unsigned long addr = mfspr(SPRN_DEAR); - int write = mfspr(SPRN_ESR) & ESR_DST; - - if (PPC4xx_tlb_miss(regs, addr, write) < 0) { - sti(); - do_page_fault(regs, addr, write); - cli(); - } - -} - -/* - * () - * - * Description: - * This routine... - * - * Input(s): - * - * - * Output(s): - * - * - * Returns: - * - * - */ -void -PPC4xx_itlb_miss(struct pt_regs *regs) -{ - unsigned long addr = regs->nip; - - if (PPC4xx_tlb_miss(regs, addr, 0) < 0) { - sti(); - do_page_fault(regs, addr, 0); - cli(); - } -} - -/* - * () - * - * Description: - * This routine... - * - * Input(s): - * - * - * Output(s): - * - * - * Returns: - * - * - */ -void -PPC4xx_tlb_pin(unsigned long va, unsigned long pa, int pagesz, int cache) -{ - unsigned long tag, data; - unsigned long opid; - - if (pinned >= PPC4XX_TLB_SIZE) - return; - - opid = mfspr(SPRN_PID); - mtspr(SPRN_PID, 0); - - data = (pa & TLB_RPN_MASK) | TLB_WR; - - if (cache) - data |= (TLB_EX); - else - data |= (TLB_G | TLB_I); - - tag = (va & TLB_EPN_MASK) | TLB_VALID | pagesz; - - PPC4xx_tlb_write(tag, data, pinned++); - - mtspr(SPRN_PID, opid); - return; -} - -/* - * () - * - * Description: - * This routine... - * - * Input(s): - * - * - * Output(s): - * - * - * Returns: - * - * - */ -void -PPC4xx_tlb_unpin(unsigned long va, unsigned long pa, int size) -{ - /* XXX - To be implemented. */ -} - -/* - * () - * - * Description: - * This routine... - * - * Input(s): - * - * - * Output(s): - * - * - * Returns: - * - * - */ -static inline void -PPC4xx_tlb_update(unsigned long addr, pte_t *pte) -{ - unsigned long data, tag, rand; - int i, found = 1; - - /* Construct the hardware TLB entry from the Linux-style PTE */ - - tag = tag = (addr & PAGE_MASK) | TLB_VALID | TLB_PAGESZ(PAGESZ_4K); - data = data = (pte_val(*pte) & PAGE_MASK) | TLB_EX | TLB_WR; - -#if 0 - if (pte_val(*pte) & _PAGE_HWWRITE) - data |= TLB_WR; -#endif - - if (pte_val(*pte) & _PAGE_NO_CACHE) - data |= TLB_I; - - if (pte_val(*pte) & _PAGE_GUARDED) - data |= TLB_G; - - if (addr < KERNELBASE) - data |= TLB_ZSEL(1); - - /* Attempt to match the new tag to an existing entry in the TLB. */ - - asm("tlbsx. %0,0,%2;" - "beq 1f;" - "li %1,0;1:" : "=r" (i), "=r" (found) : "r" (tag)); - - /* - * If we found a match for the tag, reuse the entry index and update - * the tag and data portions. Otherwise, we did not find a match. Use - * the lower 5 bits of the lower time base register as a pseudo-random - * index into the TLB and replace the entry at that index. - */ - - if (found) { - PPC4xx_tlb_write(tag, data, i); - } else { - rand = mfspr(SPRN_TBLO) & (PPC4XX_TLB_SIZE - 1); - rand += pinned; - if (rand >= PPC4XX_TLB_SIZE) - rand -= pinned; - - PPC4xx_tlb_write(tag, data, rand); - asm("isync;sync"); - } -} - -/* - * () - * - * Description: - * This routine... - * - * Input(s): - * - * - * Output(s): - * - * - * Returns: - * - * - */ -static int -PPC4xx_tlb_miss(struct pt_regs *regs, unsigned long addr, int write) -{ - unsigned long spid, ospid; - struct mm_struct *mm; - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - if (!user_mode(regs) && (addr >= KERNELBASE)) { - mm = &init_mm; - spid = 0; - } else { - mm = current->mm; - spid = mfspr(SPRN_PID); - } - - pgd = pgd_offset(mm, addr); - if (pgd_none(*pgd)) - goto bad; - - pmd = pmd_offset(pgd, addr); - if (pmd_none(*pmd)) - goto bad; - - pte = pte_offset(pmd, addr); - if (pte_none(*pte) || !pte_present(*pte)) - goto bad; - - if (write) { - if (!pte_write(*pte)) - goto bad; - - set_pte(pte, pte_mkdirty(*pte)); - } - set_pte(pte, pte_mkyoung(*pte)); - - ospid = mfspr(SPRN_PID); - mtspr(SPRN_PID, spid); - PPC4xx_tlb_update(addr, pte); - mtspr(SPRN_PID, ospid); - - return (0); -bad: - return (-1); -} diff -Nru a/arch/ppc/mm/4xx_tlb.h b/arch/ppc/mm/4xx_tlb.h --- a/arch/ppc/mm/4xx_tlb.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,38 +0,0 @@ -/* - * BK Id: SCCS/s.4xx_tlb.h 1.5 05/17/01 18:14:23 cort - */ -/* - * - * Copyright (c) 1999 Grant Erickson - * - * Module name: 4xx_tlb.h - * - * Description: - * Routines for manipulating the TLB on PowerPC 400-class processors. - * - */ - -#ifndef __4XX_TLB_H__ -#define __4XX_TLB_H__ - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* Function Prototypes */ - -extern void PPC4xx_tlb_pin(unsigned long va, unsigned long pa, - int pagesz, int cache); -extern void PPC4xx_tlb_unpin(unsigned long va, unsigned long pa, - int size); -extern void PPC4xx_tlb_flush_all(void); -extern void PPC4xx_tlb_flush(unsigned long va, int pid); - - -#ifdef __cplusplus -} -#endif - -#endif /* __4XX_TLB_H__ */ diff -Nru a/arch/ppc/mm/Makefile b/arch/ppc/mm/Makefile --- a/arch/ppc/mm/Makefile Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/mm/Makefile Tue Feb 19 18:08:58 2002 @@ -1,4 +1,4 @@ -# BK Id: SCCS/s.Makefile 1.8 08/16/01 17:25:47 paulus +# BK Id: %F% %I% %G% %U% %#% # # # Makefile for the linux ppc-specific parts of the memory manager. @@ -21,7 +21,7 @@ obj-$(CONFIG_PPC_STD_MMU) += hashtable.o ppc_mmu.o tlb.o obj-$(CONFIG_PPC_ISERIES) += iSeries_hashtable.o iSeries_mmu.o tlb.o -obj-$(CONFIG_4xx) += cachemap.o 4xx_mmu.o -obj-$(CONFIG_8xx) += cachemap.o +obj-$(CONFIG_4xx) += 4xx_mmu.o +obj-$(CONFIG_NOT_COHERENT_CACHE) += cachemap.o include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc/mm/cachemap.c b/arch/ppc/mm/cachemap.c --- a/arch/ppc/mm/cachemap.c Tue Feb 19 18:09:00 2002 +++ b/arch/ppc/mm/cachemap.c Tue Feb 19 18:09:00 2002 @@ -52,12 +52,19 @@ extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep); +/* This function will allocate the requested contiguous pages and + * map them into the kernel's vmalloc() space. This is done so we + * get unique mapping for these pages, outside of the kernel's 1:1 + * virtual:physical mapping. This is necessary so we can cover large + * portions of the kernel with single large page TLB entries, and + * still get unique uncached pages for consistent DMA. + */ void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle) { - int order, rsize; - unsigned long page; - void *ret; - pte_t *pte; + int order, err, i; + unsigned long page, va, pa, flags; + struct vm_struct *area; + void *ret; if (in_interrupt()) BUG(); @@ -79,23 +86,29 @@ */ invalidate_dcache_range(page, page + size); - ret = (void *)page; - *dma_handle = virt_to_bus(ret); + /* Allocate some common virtual space to map the new pages. + */ + area = get_vm_area(size, VM_ALLOC); + if (area == 0) { + free_pages(page, order); + return NULL; + } + va = VMALLOC_VMADDR(area->addr); + ret = (void *)va; - /* Chase down all of the PTEs and mark them uncached. + /* This gives us the real physical address of the first page. */ - rsize = (int)size; - while (rsize > 0) { - if (get_pteptr(&init_mm, page, &pte)) { - pte_val(*pte) |= _PAGE_NO_CACHE | _PAGE_GUARDED; - flush_tlb_page(find_vma(&init_mm,page),page); - } - else { - BUG(); - return NULL; - } - page += PAGE_SIZE; - rsize -= PAGE_SIZE; + *dma_handle = pa = virt_to_bus(page); + + flags = _PAGE_KERNEL | _PAGE_NO_CACHE; + + err = 0; + for (i = 0; i < size && err == 0; i += PAGE_SIZE) + err = map_page(va+i, pa+i, flags); + + if (err) { + vfree((void *)va); + return NULL; } return ret; @@ -103,42 +116,12 @@ /* * free page(s) as defined by the above mapping. - * The caller has to tell us the size so we can free the proper number - * of pages. We can't vmalloc() a new space for these pages and simply - * call vfree() like some other architectures because we could end up - * with aliased cache lines (or at least a cache line with the wrong - * attributes). This can happen when the PowerPC speculative loads - * across page boundaries. */ -void consistent_free(void *vaddr, size_t size) +void consistent_free(void *vaddr) { - int order, rsize; - unsigned long addr; - pte_t *pte; - if (in_interrupt()) BUG(); - - size = PAGE_ALIGN(size); - order = get_order(size); - - /* Chase down all of the PTEs and mark them cached again. - */ - addr = (unsigned long)vaddr; - rsize = (int)size; - while (rsize > 0) { - if (get_pteptr(&init_mm, addr, &pte)) { - pte_val(*pte) &= ~(_PAGE_NO_CACHE | _PAGE_GUARDED); - flush_tlb_page(find_vma(&init_mm,addr),addr); - } - else { - BUG(); - return; - } - addr += PAGE_SIZE; - rsize -= PAGE_SIZE; - } - free_pages((unsigned long)vaddr, order); + vfree(vaddr); } /* @@ -162,4 +145,18 @@ flush_dcache_range(start, end); break; } +} + +/* + * consistent_sync_page make a page are consistent. identical + * to consistent_sync, but takes a struct page instead of a virtual address + */ + +void consistent_sync_page(struct page *page, unsigned long offset, +size_t size, int direction) +{ + unsigned long start; + + start = (unsigned long)(page->virtual) + offset; + consistent_sync(start, size, direction); } diff -Nru a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c --- a/arch/ppc/mm/fault.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/mm/fault.c Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.fault.c 1.15 09/24/01 16:35:10 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * arch/ppc/mm/fault.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,7 @@ extern void die_if_kernel(char *, struct pt_regs *, long); void bad_page_fault(struct pt_regs *, unsigned long, int sig); void do_page_fault(struct pt_regs *, unsigned long, unsigned long); +extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep); /* * For 600- and 800-family processors, the error_code parameter is DSISR @@ -136,6 +138,36 @@ if (is_write) { if (!(vma->vm_flags & VM_WRITE)) goto bad_area; +#if defined(CONFIG_4xx) + /* an exec - 4xx allows for per-page execute permission */ + } else if (regs->trap == 0x400) { + pte_t *ptep; + +#if 0 + /* It would be nice to actually enforce the VM execute + permission on CPUs which can do so, but far too + much stuff in userspace doesn't get the permissions + right, so we let any page be executed for now. */ + if (! (vma->vm_flags & VM_EXEC)) + goto bad_area; +#endif + + /* Since 4xx supports per-page execute permission, + * we lazily flush dcache to icache. */ + if (get_pteptr(mm, address, &ptep) && pte_present(*ptep)) { + struct page *page = pte_page(*ptep); + + if (! test_bit(PG_arch_1, &page->flags)) { + __flush_dcache_icache((unsigned long)kmap(page)); + kunmap(page); + set_bit(PG_arch_1, &page->flags); + } + pte_update(ptep, 0, _PAGE_HWEXEC); + _tlbie(address); + up_read(&mm->mmap_sem); + return; + } +#endif /* a read */ } else { /* protection fault */ @@ -197,8 +229,7 @@ out_of_memory: up_read(&mm->mmap_sem); if (current->pid == 1) { - current->policy |= SCHED_YIELD; - schedule(); + yield(); down_read(&mm->mmap_sem); goto survive; } diff -Nru a/arch/ppc/mm/hashtable.S b/arch/ppc/mm/hashtable.S --- a/arch/ppc/mm/hashtable.S Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/mm/hashtable.S Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.hashtable.S 1.18 08/15/01 22:43:07 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * arch/ppc/kernel/hashtable.S @@ -27,11 +27,13 @@ */ #include -#include "../kernel/ppc_asm.h" #include #include #include #include +#include +#include +#include #ifdef CONFIG_SMP .comm hash_table_lock,4 @@ -62,9 +64,7 @@ #ifdef CONFIG_SMP addis r2,r7,hash_table_lock@h ori r2,r2,hash_table_lock@l - mfspr r5,SPRG3 - lwz r0,PROCESSOR-THREAD(r5) - oris r0,r0,0x0fff + lis r0,0x0fff b 10f 11: lwz r6,0(r2) cmpwi 0,r6,0 @@ -214,8 +214,9 @@ #ifdef CONFIG_SMP lis r9,hash_table_lock@h ori r9,r9,hash_table_lock@l - lwz r8,PROCESSOR(r2) - oris r8,r8,10 + rlwinm r8,r1,0,0,18 + lwz r8,TI_CPU(r8) + oris r8,r8,12 10: lwarx r7,0,r9 cmpi 0,r7,0 bne- 11f @@ -333,25 +334,6 @@ ori r8,r8,_PAGE_COHERENT /* set M (coherence required) */ #endif -#ifdef CONFIG_POWER4 - /* - * XXX hack hack hack - translate 32-bit "physical" addresses - * in the linux page tables to 42-bit real addresses in such - * a fashion that we can get at the I/O we need to access. - * -- paulus - */ - cmpwi r8,0 - rlwinm r0,r8,16,16,30 - bge 57f - cmplwi r0,0xfe00 - li r0,0x3fd - bne 56f - li r0,0x3ff -56: sldi r0,r0,32 - or r8,r8,r0 -57: -#endif - /* Construct the high word of the PPC-style PTE (r5) */ #ifndef CONFIG_PPC64BRIDGE rlwinm r5,r3,7,1,24 /* put VSID in 0x7fffff80 bits */ @@ -448,21 +430,6 @@ lwz r6,next_slot@l(r4) addi r6,r6,PTE_SIZE andi. r6,r6,7*PTE_SIZE -#ifdef CONFIG_POWER4 - /* - * Since we don't have BATs on POWER4, we rely on always having - * PTEs in the hash table to map the hash table and the code - * that manipulates it in virtual mode, namely flush_hash_page and - * flush_hash_segments. Otherwise we can get a DSI inside those - * routines which leads to a deadlock on the hash_table_lock on - * SMP machines. We avoid this by never overwriting the first - * PTE of each PTEG if it is already valid. - * -- paulus. - */ - bne 102f - li r6,PTE_SIZE -102: -#endif /* CONFIG_POWER4 */ stw r6,next_slot@l(r4) add r4,r3,r6 @@ -544,7 +511,8 @@ #ifdef CONFIG_SMP lis r9,hash_table_lock@h ori r9,r9,hash_table_lock@l - lwz r8,PROCESSOR(r2) + rlwinm r8,r1,0,0,18 + lwz r8,TI_CPU(r8) oris r8,r8,9 10: lwarx r7,0,r9 cmpi 0,r7,0 diff -Nru a/arch/ppc/mm/iSeries_hashtable.c b/arch/ppc/mm/iSeries_hashtable.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/mm/iSeries_hashtable.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,223 @@ +/* + * + * + * Copyright (c) 2000 Mike Corrigan IBM Corporation + * updated by Dave Boutcher (boutcher@us.ibm.com) + * + * Module name: iSeries_hashtable.c + * + * Description: + * Handles Hash Table faults for iSeries LPAR. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int iSeries_hpt_loaded; + +static spinlock_t hash_table_lock = SPIN_LOCK_UNLOCKED; + +extern unsigned long htab_reloads; // Defined in ppc/kernel/ppc_htab.c +extern unsigned long htab_evicts; +unsigned long htab_pp_update = 0; + +unsigned long flush_hash_page_count = 0; +unsigned long Hash_mask; + +unsigned long flush_hash_range_count = 0; +unsigned long flush_hash_range_hv_finds = 0; +unsigned long flush_hash_range_hv_evicts = 0; +extern int iSeries_max_kernel_hpt_slot; +static unsigned next_slot = 4; // good enough starting value + +extern void flush_hash_range( u64, u64, unsigned ); + +static inline u32 computeHptePP( unsigned long pte ) +{ + return ( ( pte & _PAGE_USER ) >> 1 ) | + ( ( ( pte & _PAGE_USER ) >> 2 ) & + ( (~pte & _PAGE_RW ) >> 10 ) ); +} + +static inline u32 computeHpteHash( unsigned long vsid, + unsigned long pid ) +{ + return ( vsid ^ pid ) & Hash_mask; +} + +/* + * Should be called with hash page lock + */ +static void __create_hpte(unsigned long vsid, unsigned long va, unsigned long newpte) +{ + PTE hpte; + u64 vpn; + u64 rtnIndex; + u64 *hpte0Ptr, *hpte1Ptr; + u32 newpp, gIndex; + vsid = vsid & 0x7ffffff; + vpn = ((u64)vsid << 16) | ((va >> 12) & 0xffff); + + hpte0Ptr = (u64 *)&hpte; + hpte1Ptr = hpte0Ptr + 1; + *hpte0Ptr = *hpte1Ptr = 0; + + rtnIndex = HvCallHpt_findValid( &hpte, vpn ); + + newpp = computeHptePP( newpte ); + + if ( hpte.v ) { + /* A matching valid entry was found + * Just update the pp bits + */ + ++htab_pp_update; + HvCallHpt_setPp( rtnIndex, newpp ); + } else { /* No matching entry was found Build new hpte */ + hpte.vsid = vsid; + hpte.api = (va >> 23) & 0x1f; + hpte.v = 1; + hpte.rpn = physRpn_to_absRpn(newpte>>12); + hpte.r = 1; + hpte.c = 1; + hpte.m = 1; + hpte.pp = newpp; + + if ( ( rtnIndex != ~0 ) && + ( rtnIndex != 0x00000000ffffffff ) ) { + /* Free entry was found */ + if ( ( rtnIndex >> 63 ) || + ( rtnIndex & 0x80000000 ) ) + hpte.h = 1; + HvCallHpt_addValidate( + rtnIndex, + hpte.h, + &hpte ); + } else { + /* No free entry was found */ + gIndex = computeHpteHash( vsid, vpn & 0xffff ); + rtnIndex = gIndex*8 + next_slot; + if ( ++next_slot > 7 ) + next_slot = iSeries_max_kernel_hpt_slot+1; + HvCallHpt_invalidateSetSwBitsGet( + rtnIndex, 0, 1 ); + HvCallHpt_addValidate( + rtnIndex, 0, &hpte ); + ++htab_evicts; + } + } +} + +int iSeries_create_hpte( unsigned long access, unsigned long va ) +{ + struct thread_struct *ts; + pgd_t * pg; + pmd_t * pm; + pte_t * pt; + u32 vsid; + unsigned flags; + + vsid = mfsrin( va ) & 0x07ffffff; + + if ( va >= KERNELBASE ) + pg = swapper_pg_dir; + else { + // Get the thread structure + ts = (struct thread_struct *)mfspr(SPRG3); + // Get the page directory + pg = ts->pgdir; + } + + pg = pg + pgd_index( va ); // offset into first level + pm = pmd_offset( pg, va ); // offset into second level + if ( pmd_none( *pm ) ) // if no third level + return 1; // indicate failure + pt = pte_offset( pm, va ); // offset into third level + + access |= _PAGE_PRESENT; // _PAGE_PRESENT also needed + + spin_lock( &hash_table_lock ); + // check if pte is in the required state + if ( ( access & ~(pte_val(*pt)) ) ) { + spin_unlock( &hash_table_lock ); + return 1; + } + + /* pte allows the access we are making */ + flags = _PAGE_ACCESSED | _PAGE_HASHPTE | _PAGE_COHERENT; + if ( access & _PAGE_RW ) /* If write access */ + flags |= _PAGE_RW; + + /* atomically update pte */ + pte_update( pt, 0, flags ); + __create_hpte(vsid, + va, + pte_val(*pt)); + + spin_unlock( &hash_table_lock ); + return 0; +} + +void add_hash_page(unsigned context, unsigned long va, pte_t *ptep) +{ + spin_lock( &hash_table_lock ); + pte_update(ptep,0,_PAGE_HASHPTE); + __create_hpte(CTX_TO_VSID(context, va), + va, + pte_val(*ptep)); + spin_unlock( &hash_table_lock ); +} + +int flush_hash_page(unsigned context, unsigned long va, pte_t *ptep) +{ + int rc; + PTE hpte; + u64 vpn; + unsigned long vsid; + u64 rtnIndex; + u64 *hpte0Ptr, *hpte1Ptr; + + vsid = CTX_TO_VSID(context, va); + + vpn = ((u64)vsid << 16) | ((va >> 12) & 0xffff); + + hpte0Ptr = (u64 *)&hpte; + hpte1Ptr = hpte0Ptr + 1; + *hpte0Ptr = *hpte1Ptr = 0; + + spin_lock( &hash_table_lock ); + rtnIndex = HvCallHpt_findValid( &hpte, vpn ); + + if ( hpte.v ) { + pte_update(ptep, _PAGE_HASHPTE, 0); + HvCallHpt_invalidateSetSwBitsGet(rtnIndex, 0, 1 ); + rc = 0; + } else + rc = 1; + spin_unlock( &hash_table_lock ); + return rc; +} + diff -Nru a/arch/ppc/mm/iSeries_mmu.c b/arch/ppc/mm/iSeries_mmu.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/mm/iSeries_mmu.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,186 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * Procedures for MMU handling on iSeries systems, where we + * have to call the hypervisor to change things in the hash + * table. + * + * Copyright (C) 2001 IBM Corp. + * updated by Dave Boutcher (boutcher@us.ibm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmu_decl.h" + +int iSeries_max_kernel_hpt_slot = 0; +extern unsigned maxPacas; +extern int iSeries_hpt_loaded; +PTE *Hash = 0; + +#ifdef CONFIG_PCI +extern int iSeries_Is_IoMmAddress(unsigned long address); + +/*********************************************************************/ +/* iSeries maps I/O space to device, just leave the address where is.*/ +/*********************************************************************/ +void* ioremap(unsigned long addr, unsigned long size) +{ + return (void*)addr; +} + +void* __ioremap(unsigned long addr, unsigned long size, unsigned long flags) +{ + return (void*)addr; +} + +/********************************************************************/ +/* iSeries did not remapped the space. */ +/********************************************************************/ +void iounmap(void *addr) +{ + return; +} +#endif /* CONFIG_PCI */ + +/* + * Map as much of memory as will fit into the first entry of each + * PPC HPTE Group. (These are the "bolted" entries which will + * never be cast out). The iSeries Hypervisor has already mapped + * the first 32 MB (specified in LparMap.h). Here we map as + * much more as we can. + */ + +void __init MMU_init_hw(void) +{ + PTE hpte; + u64 *hpte0Ptr, *hpte1Ptr; + u32 HptSizeGroups, msPages, rpn, vsid, ea; + u64 rtnIndex; + u32 hpteIndex; + u32 group; + unsigned long numAdded; + + if ( ppc_md.progress ) ppc_md.progress("hash:enter", 0x105); + + hpte0Ptr = (u64 *)&hpte; + hpte1Ptr = hpte0Ptr + 1; + + /* Get the number of Hpt groups */ + HptSizeGroups = (u32)HvCallHpt_getHptPages() * 32; + Hash_mask = HptSizeGroups - 1; + + /* Number of pages in memory */ + msPages = totalLpChunks << 6; + + /* For each virtual page in kernel space, add a hpte if there + isn't one already in slot 0 of the primary pteg. */ + + numAdded = 0; + + for ( ea = (u32)KERNELBASE; ea < (u32)high_memory; ea+= PAGE_SIZE) { + rpn = ea >> 12; + + vsid = ((ea >> 28) * 0x111); + + rtnIndex = HvCallHpt_findValid( &hpte, + (rpn & 0xffff) | (vsid << 16)); + hpteIndex = (u32)rtnIndex; + if ( hpte.v ) /* If valid entry found */ + continue; /* Already mapped, nothing to do */ + if ( rtnIndex == ~0 ) /* If no free entry found */ + BUG(); /* Can't map this page bolted */ + if ( rtnIndex >> 63 ) /* If first free slot is secondary */ + BUG(); /* Can't map this page bolted */ + if ( (hpteIndex & 7) > 2) /* Not in first 3 slots */ + BUG(); + /* + * If returned index is the first in the primary group + * then build an hpt entry for this page. + */ + *hpte0Ptr = *hpte1Ptr = 0; + hpte.vsid = vsid; + hpte.api = (rpn >> 11) & 0x1f; + hpte.h = 0; + hpte.v = 1; + hpte.rpn = physRpn_to_absRpn( rpn ); + hpte.r = 1; + hpte.c = 1; + hpte.m = 1; + hpte.w = 0; + hpte.i = 0; + hpte.g = 0; + hpte.pp = 0; + HvCallHpt_addValidate( hpteIndex, 0, &hpte ); + ++numAdded; + group = rtnIndex & 0x07; + if (group > iSeries_max_kernel_hpt_slot) + iSeries_max_kernel_hpt_slot = group; + } + + printk( "iSeries_hashinit: added %ld hptes to existing mapping. Max group %x\n", + numAdded, iSeries_max_kernel_hpt_slot ); + + if ( ppc_md.progress ) ppc_md.progress("hash:done", 0x205); + + iSeries_hpt_loaded = 1; + Hash = (void *)0xFFFFFFFF; +} + +/* + * This is called at the end of handling a user page fault, when the + * fault has been handled by updating a PTE in the linux page tables. + * We use it to preload an HPTE into the hash table corresponding to + * the updated linux PTE. + */ +void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, + pte_t pte) +{ + struct mm_struct *mm; + pmd_t *pmd; + pte_t *ptep; + static int nopreload; + + if (nopreload) + return; + mm = (address < TASK_SIZE)? vma->vm_mm: &init_mm; + pmd = pmd_offset(pgd_offset(mm, address), address); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, address); + add_hash_page(mm->context, address, ptep); + } +} diff -Nru a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c --- a/arch/ppc/mm/init.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/mm/init.c Tue Feb 19 18:08:58 2002 @@ -1,8 +1,8 @@ /* - * BK Id: SCCS/s.init.c 1.36 09/22/01 14:03:09 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* - * PowerPC version + * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) * * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) @@ -49,19 +49,32 @@ #include "mem_pieces.h" #include "mmu_decl.h" +#ifdef CONFIG_LOWMEM_SIZE_BOOL +#define MAX_LOW_MEM CONFIG_LOWMEM_SIZE +#else #define MAX_LOW_MEM (0xF0000000UL - KERNELBASE) +#endif /* CONFIG_LOWMEM_SIZE_BOOL */ + +#ifdef CONFIG_PPC_ISERIES +extern void create_virtual_bus_tce_table(void); +#endif mmu_gather_t mmu_gathers[NR_CPUS]; -void *end_of_DRAM; unsigned long total_memory; unsigned long total_lowmem; +unsigned long ppc_memstart; +unsigned long ppc_memoffset = PAGE_OFFSET; + int mem_init_done; int init_bootmem_done; int boot_mapsize; unsigned long totalram_pages; unsigned long totalhigh_pages; +#ifdef CONFIG_ALL_PPC +unsigned long agp_special_page; +#endif extern char _end[]; extern char etext[], _stext[]; @@ -85,7 +98,7 @@ char *klimit = _end; struct mem_pieces phys_avail; -extern char *sysmap; +extern char *sysmap; extern unsigned long sysmap_size; /* @@ -122,7 +135,6 @@ { int i,free = 0,total = 0,reserved = 0; int shared = 0, cached = 0; - struct task_struct *p; int highmem = 0; printk("Mem-info:\n"); @@ -140,7 +152,7 @@ else if (!page_count(mem_map+i)) free++; else - shared += atomic_read(&mem_map[i].count) - 1; + shared += page_count(mem_map+i) - 1; } printk("%d pages of RAM\n",total); printk("%d pages of HIGHMEM\n", highmem); @@ -150,49 +162,6 @@ printk("%d pages swap cached\n",cached); printk("%d pages in page table cache\n",(int)pgtable_cache_size); show_buffers(); - printk("%-8s %3s %8s %8s %8s %9s %8s", "Process", "Pid", - "Ctx", "Ctx<<4", "Last Sys", "pc", "task"); -#ifdef CONFIG_SMP - printk(" %3s", "CPU"); -#endif /* CONFIG_SMP */ - printk("\n"); - for_each_task(p) - { - printk("%-8.8s %3d %8ld %8ld %8ld %c%08lx %08lx ", - p->comm,p->pid, - (p->mm)?p->mm->context:0, - (p->mm)?(p->mm->context<<4):0, - p->thread.last_syscall, - (p->thread.regs)?user_mode(p->thread.regs) ? 'u' : 'k' : '?', - (p->thread.regs)?p->thread.regs->nip:0, - (ulong)p); - { - int iscur = 0; -#ifdef CONFIG_SMP - printk("%3d ", p->processor); - if ( (p->processor != NO_PROC_ID) && - (p == current_set[p->processor]) ) - { - iscur = 1; - printk("current"); - } -#else - if ( p == current ) - { - iscur = 1; - printk("current"); - } - - if ( p == last_task_used_math ) - { - if ( iscur ) - printk(","); - printk("last math"); - } -#endif /* CONFIG_SMP */ - printk("\n"); - } - } } void si_meminfo(struct sysinfo *val) @@ -319,7 +288,6 @@ total_memory = total_lowmem; #endif /* CONFIG_HIGHMEM */ } - end_of_DRAM = __va(total_lowmem); set_phys_avail(total_lowmem); /* Initialize the MMU hardware */ @@ -352,9 +320,10 @@ ppc_md.progress("MMU:exit", 0x211); #ifdef CONFIG_BOOTX_TEXT - /* Must be done last, or ppc_md.progress will die */ - if (have_of) - map_boot_text(); + /* By default, we are no longer mapped */ + boot_text_mapped = 0; + /* Must be done last, or ppc_md.progress will die. */ + map_boot_text(); #endif } @@ -401,8 +370,11 @@ } start = PAGE_ALIGN(start); - boot_mapsize = init_bootmem(start >> PAGE_SHIFT, - total_lowmem >> PAGE_SHIFT); + min_low_pfn = start >> PAGE_SHIFT; + max_low_pfn = (PPC_MEMSTART + total_lowmem) >> PAGE_SHIFT; + boot_mapsize = init_bootmem_node(&contig_page_data, min_low_pfn, + PPC_MEMSTART >> PAGE_SHIFT, + max_low_pfn); /* remove the bootmem bitmap from the available memory */ mem_pieces_remove(&phys_avail, start, boot_mapsize, 1); @@ -455,12 +427,10 @@ highmem_mapnr = total_lowmem >> PAGE_SHIFT; highmem_start_page = mem_map + highmem_mapnr; - max_mapnr = total_memory >> PAGE_SHIFT; -#else - max_mapnr = max_low_pfn; #endif /* CONFIG_HIGHMEM */ + max_mapnr = total_memory >> PAGE_SHIFT; - high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); + high_memory = (void *) __va(PPC_MEMSTART + total_lowmem); num_physpages = max_mapnr; /* RAM is assumed contiguous */ totalram_pages += free_all_bootmem(); @@ -474,21 +444,23 @@ } #endif /* CONFIG_BLK_DEV_INITRD */ -#if defined(CONFIG_ALL_PPC) +#if defined(CONFIG_ALL_PPC) /* mark the RTAS pages as reserved */ if ( rtas_data ) for (addr = (ulong)__va(rtas_data); addr < PAGE_ALIGN((ulong)__va(rtas_data)+rtas_size) ; addr += PAGE_SIZE) SetPageReserved(virt_to_page(addr)); + if (agp_special_page) + SetPageReserved(virt_to_page(agp_special_page)); #endif /* defined(CONFIG_ALL_PPC) */ if ( sysmap ) for (addr = (unsigned long)sysmap; addr < PAGE_ALIGN((unsigned long)sysmap+sysmap_size) ; addr += PAGE_SIZE) SetPageReserved(virt_to_page(addr)); - - for (addr = PAGE_OFFSET; addr < (unsigned long)end_of_DRAM; + + for (addr = PAGE_OFFSET; addr < (unsigned long)high_memory; addr += PAGE_SIZE) { if (!PageReserved(virt_to_page(addr))) continue; @@ -526,6 +498,15 @@ if (sysmap) printk("System.map loaded at 0x%08x for debugger, size: %ld bytes\n", (unsigned int)sysmap, sysmap_size); +#if defined(CONFIG_ALL_PPC) + if (agp_special_page) + printk(KERN_INFO "AGP special page: 0x%08lx\n", agp_special_page); +#endif /* defined(CONFIG_ALL_PPC) */ +#ifdef CONFIG_PPC_ISERIES + + create_virtual_bus_tce_table(); + +#endif /* CONFIG_PPC_ISERIES */ mem_init_done = 1; } @@ -543,7 +524,7 @@ * physical memory. */ - phys_avail.regions[0].address = 0; + phys_avail.regions[0].address = PPC_MEMSTART; phys_avail.regions[0].size = total_memory; phys_avail.n_regions = 1; @@ -572,6 +553,20 @@ /* remove the sysmap pages from the available memory */ if (sysmap) mem_pieces_remove(&phys_avail, __pa(sysmap), sysmap_size, 1); + /* Because of some uninorth weirdness, we need a page of + * memory as high as possible (it must be outside of the + * bus address seen as the AGP aperture). It will be used + * by the r128 DRM driver + * + * FIXME: We need to make sure that page doesn't overlap any of the\ + * above. This could be done by improving mem_pieces_find to be able + * to do a backward search from the end of the list. + */ + if (_machine == _MACH_Pmac && find_devices("uni-north-agp")) { + agp_special_page = (total_memory - PAGE_SIZE); + mem_pieces_remove(&phys_avail, agp_special_page, PAGE_SIZE, 0); + agp_special_page = (unsigned long)__va(agp_special_page); + } #endif /* CONFIG_ALL_PPC */ } @@ -581,23 +576,43 @@ mem_pieces_remove(&phys_avail, start, size, 1); } -void flush_page_to_ram(struct page *page) +/* + * This is called when a page has been modified by the kernel. + * It just marks the page as not i-cache clean. We do the i-cache + * flush later when the page is given to a user process, if necessary. + */ +void flush_dcache_page(struct page *page) { - unsigned long vaddr = (unsigned long) kmap(page); - __flush_page_to_ram(vaddr); - kunmap(page); + clear_bit(PG_arch_1, &page->flags); } -/* - * set_pte stores a linux PTE into the linux page table. - * On machines which use an MMU hash table we avoid changing the - * _PAGE_HASHPTE bit. - */ -void set_pte(pte_t *ptep, pte_t pte) +void flush_icache_page(struct vm_area_struct *vma, struct page *page) { -#if _PAGE_HASHPTE != 0 - pte_update(ptep, ~_PAGE_HASHPTE, pte_val(pte) & ~_PAGE_HASHPTE); -#else - *ptep = pte; -#endif + if (page->mapping && !PageReserved(page) + && !test_bit(PG_arch_1, &page->flags)) { + __flush_dcache_icache(kmap(page)); + kunmap(page); + set_bit(PG_arch_1, &page->flags); + } +} + +void clear_user_page(void *page, unsigned long vaddr) +{ + clear_page(page); +} + +void copy_user_page(void *vto, void *vfrom, unsigned long vaddr) +{ + copy_page(vto, vfrom); + __flush_dcache_icache(vto); +} + +void flush_icache_user_range(struct vm_area_struct *vma, struct page *page, + unsigned long addr, int len) +{ + unsigned long maddr; + + maddr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK); + flush_icache_range(maddr, maddr + len); + kunmap(page); } diff -Nru a/arch/ppc/mm/mem_pieces.c b/arch/ppc/mm/mem_pieces.c --- a/arch/ppc/mm/mem_pieces.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/mm/mem_pieces.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.mem_pieces.c 1.5 05/17/01 18:14:23 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * Copyright (c) 1996 Paul Mackerras @@ -45,7 +45,7 @@ a = (a + align - 1) & -align; if (a + size <= e) { mem_pieces_remove(mp, a, size, 1); - return __va(a); + return (void *) __va(a); } } panic("Couldn't find %u bytes at %u alignment\n", size, align); diff -Nru a/arch/ppc/mm/mmu_decl.h b/arch/ppc/mm/mmu_decl.h --- a/arch/ppc/mm/mmu_decl.h Tue Feb 19 18:09:00 2002 +++ b/arch/ppc/mm/mmu_decl.h Tue Feb 19 18:09:00 2002 @@ -31,14 +31,12 @@ extern void reserve_phys_mem(unsigned long start, unsigned long size); extern int __map_without_bats; -extern void *end_of_DRAM; extern unsigned long ioremap_base; extern unsigned long ioremap_bot; extern unsigned int rtas_data, rtas_size; extern unsigned long total_memory; extern unsigned long total_lowmem; -extern unsigned long ram_phys_base; extern int mem_init_done; extern PTE *Hash, *Hash_end; diff -Nru a/arch/ppc/mm/pgtable.c b/arch/ppc/mm/pgtable.c --- a/arch/ppc/mm/pgtable.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/mm/pgtable.c Tue Feb 19 18:08:58 2002 @@ -35,8 +35,6 @@ #include "mmu_decl.h" -unsigned long ram_phys_base; - unsigned long ioremap_base; unsigned long ioremap_bot; int io_bat_index; @@ -199,25 +197,25 @@ #endif /* HAVE_BATS */ v = KERNELBASE; - p = ram_phys_base; + p = PPC_MEMSTART; for (s = 0; s < total_lowmem; s += PAGE_SIZE) { /* On the MPC8xx, we want the page shared so we * don't get ASID compares on kernel space. */ - f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED; + f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED | _PAGE_HWEXEC; #if defined(CONFIG_KGDB) || defined(CONFIG_XMON) /* Allows stub to set breakpoints everywhere */ - f |= _PAGE_RW | _PAGE_DIRTY; -#else + f |= _PAGE_WRENABLE; +#else /* !CONFIG_KGDB && !CONFIG_XMON */ if ((char *) v < _stext || (char *) v >= etext) - f |= _PAGE_RW | _PAGE_DIRTY; + f |= _PAGE_WRENABLE; #ifdef CONFIG_PPC_STD_MMU else /* On the powerpc (not all), no user access forces R/W kernel access */ f |= _PAGE_USER; #endif /* CONFIG_PPC_STD_MMU */ -#endif /* CONFIG_KGDB */ +#endif /* CONFIG_KGDB || CONFIG_XMON */ map_page(v, p, f); v += PAGE_SIZE; p += PAGE_SIZE; diff -Nru a/arch/ppc/mm/ppc_mmu.c b/arch/ppc/mm/ppc_mmu.c --- a/arch/ppc/mm/ppc_mmu.c Tue Feb 19 18:08:58 2002 +++ b/arch/ppc/mm/ppc_mmu.c Tue Feb 19 18:08:58 2002 @@ -51,7 +51,7 @@ u64 word[2]; #else u32 word[2]; -#endif +#endif } BATS[4][2]; /* 4 pairs of IBAT, DBAT */ struct batrange { /* stores address ranges mapped by BATs */ @@ -96,8 +96,8 @@ /* Make sure we don't map a block larger than the smallest alignment of the physical address. */ - /* alignment of ram_phys_base */ - align = ~(ram_phys_base-1) & ram_phys_base; + /* alignment of PPC_MEMSTART */ + align = ~(PPC_MEMSTART-1) & PPC_MEMSTART; /* set BAT block size to MIN(max_size, align) */ if (align && align < max_size) max_size = align; @@ -108,7 +108,7 @@ break; } - setbat(2, KERNELBASE, ram_phys_base, bl, _PAGE_KERNEL); + setbat(2, KERNELBASE, PPC_MEMSTART, bl, _PAGE_KERNEL); done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1; if ((done < tot) && !bat_addrs[3].limit) { /* use BAT3 to cover a bit more */ @@ -116,7 +116,7 @@ for (bl = 128<<10; bl < max_size; bl <<= 1) if (bl * 2 > tot) break; - setbat(3, KERNELBASE+done, ram_phys_base+done, bl, + setbat(3, KERNELBASE+done, PPC_MEMSTART+done, bl, _PAGE_KERNEL); } } @@ -179,111 +179,101 @@ */ void __init MMU_init_hw(void) { - int Hash_bits, mb, mb2; - unsigned int hmask; + unsigned int hmask, mb, mb2; + unsigned int n_hpteg, lg_n_hpteg; extern unsigned int hash_page_patch_A[]; extern unsigned int hash_page_patch_B[], hash_page_patch_C[]; extern unsigned int hash_page[]; extern unsigned int flush_hash_patch_A[], flush_hash_patch_B[]; -#ifdef CONFIG_PPC64BRIDGE - /* The hash table has already been allocated and initialized - in prom.c */ - Hash_mask = (Hash_size >> 7) - 1; - hmask = Hash_mask >> 9; - Hash_bits = __ilog2(Hash_size) - 7; - mb = 25 - Hash_bits; - if (Hash_bits > 16) - Hash_bits = 16; - mb2 = 25 - Hash_bits; - - /* Remove the hash table from the available memory */ - if (Hash) - reserve_phys_mem(__pa(Hash), Hash_size); - -#else /* CONFIG_PPC64BRIDGE */ - unsigned int h; - - if ((cur_cpu_spec[0]->cpu_features & CPU_FTR_HPTE_TABLE) == 0) + if ((cur_cpu_spec[0]->cpu_features & CPU_FTR_HPTE_TABLE) == 0) { + /* + * Put a blr (procedure return) instruction at the + * start of hash_page, since we can still get DSI + * exceptions on a 603. + */ + hash_page[0] = 0x4e800020; + flush_icache_range((unsigned long) &hash_page[0], + (unsigned long) &hash_page[1]); return; + } + if ( ppc_md.progress ) ppc_md.progress("hash:enter", 0x105); + +#ifdef CONFIG_PPC64BRIDGE +#define LG_HPTEG_SIZE 7 /* 128 bytes per HPTEG */ +#define SDR1_LOW_BITS (lg_n_hpteg - 11) +#define MIN_N_HPTEG 2048 /* min 256kB hash table */ +#else +#define LG_HPTEG_SIZE 6 /* 64 bytes per HPTEG */ +#define SDR1_LOW_BITS ((n_hpteg - 1) >> 10) +#define MIN_N_HPTEG 1024 /* min 64kB hash table */ +#endif + /* - * Allow 64k of hash table for every 16MB of memory, - * up to a maximum of 2MB. + * Allow 1 HPTE (1/8 HPTEG) for each page of memory. + * This is less than the recommended amount, but then + * Linux ain't AIX. */ - for (h = 64<<10; h < total_memory / 256 && h < (2<<20); h *= 2) - ; - Hash_size = h; - Hash_mask = (h >> 6) - 1; - hmask = Hash_mask >> 10; - Hash_bits = __ilog2(h) - 6; - mb = 26 - Hash_bits; - if (Hash_bits > 16) - Hash_bits = 16; - mb2 = 26 - Hash_bits; + n_hpteg = total_memory / (PAGE_SIZE * 8); + if (n_hpteg < MIN_N_HPTEG) + n_hpteg = MIN_N_HPTEG; + lg_n_hpteg = __ilog2(n_hpteg); + if (n_hpteg & (n_hpteg - 1)) { + ++lg_n_hpteg; /* round up if not power of 2 */ + n_hpteg = 1 << lg_n_hpteg; + } + + Hash_size = n_hpteg << LG_HPTEG_SIZE; + Hash_mask = n_hpteg - 1; + hmask = Hash_mask >> (16 - LG_HPTEG_SIZE); + mb2 = mb = 32 - LG_HPTEG_SIZE - lg_n_hpteg; + if (lg_n_hpteg > 16) + mb2 = 16 - LG_HPTEG_SIZE; + /* + * Find some memory for the hash table. + */ if ( ppc_md.progress ) ppc_md.progress("hash:find piece", 0x322); - /* Find some memory for the hash table. */ - if ( Hash_size ) { - Hash = mem_pieces_find(Hash_size, Hash_size); - cacheable_memzero(Hash, Hash_size); - _SDR1 = __pa(Hash) | (Hash_mask >> 10); - } else - Hash = 0; -#endif /* CONFIG_PPC64BRIDGE */ + Hash = mem_pieces_find(Hash_size, Hash_size); + cacheable_memzero(Hash, Hash_size); + _SDR1 = __pa(Hash) | SDR1_LOW_BITS; + Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", total_memory >> 20, Hash_size >> 10, Hash); - if (Hash_size) { - if ( ppc_md.progress ) ppc_md.progress("hash:patch", 0x345); - Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); - /* - * Patch up the instructions in hashtable.S:create_hpte - */ - hash_page_patch_A[0] = (hash_page_patch_A[0] & ~0xffff) - | ((unsigned int)(Hash) >> 16); - hash_page_patch_A[1] = (hash_page_patch_A[1] & ~0x7c0) - | (mb << 6); - hash_page_patch_A[2] = (hash_page_patch_A[2] & ~0x7c0) - | (mb2 << 6); - hash_page_patch_B[0] = (hash_page_patch_B[0] & ~0xffff) - | hmask; - hash_page_patch_C[0] = (hash_page_patch_C[0] & ~0xffff) - | hmask; - /* - * Ensure that the locations we've patched have been written - * out from the data cache and invalidated in the instruction - * cache, on those machines with split caches. - */ - flush_icache_range((unsigned long) &hash_page_patch_A[0], - (unsigned long) &hash_page_patch_C[1]); - /* - * Patch up the instructions in hashtable.S:flush_hash_page - */ - flush_hash_patch_A[0] = (flush_hash_patch_A[0] & ~0xffff) - | ((unsigned int)(Hash) >> 16); - flush_hash_patch_A[1] = (flush_hash_patch_A[1] & ~0x7c0) - | (mb << 6); - flush_hash_patch_A[2] = (flush_hash_patch_A[2] & ~0x7c0) - | (mb2 << 6); - flush_hash_patch_B[0] = (flush_hash_patch_B[0] & ~0xffff) - | hmask; - flush_icache_range((unsigned long) &flush_hash_patch_A[0], - (unsigned long) &flush_hash_patch_B[1]); - } - else { - Hash_end = 0; - /* - * Put a blr (procedure return) instruction at the - * start of hash_page, since we can still get DSI - * exceptions on a 603. - */ - hash_page[0] = 0x4e800020; - flush_icache_range((unsigned long) &hash_page[0], - (unsigned long) &hash_page[1]); - } + + /* + * Patch up the instructions in hashtable.S:create_hpte + */ + if ( ppc_md.progress ) ppc_md.progress("hash:patch", 0x345); + hash_page_patch_A[0] = (hash_page_patch_A[0] & ~0xffff) + | ((unsigned int)(Hash) >> 16); + hash_page_patch_A[1] = (hash_page_patch_A[1] & ~0x7c0) | (mb << 6); + hash_page_patch_A[2] = (hash_page_patch_A[2] & ~0x7c0) | (mb2 << 6); + hash_page_patch_B[0] = (hash_page_patch_B[0] & ~0xffff) | hmask; + hash_page_patch_C[0] = (hash_page_patch_C[0] & ~0xffff) | hmask; + + /* + * Ensure that the locations we've patched have been written + * out from the data cache and invalidated in the instruction + * cache, on those machines with split caches. + */ + flush_icache_range((unsigned long) &hash_page_patch_A[0], + (unsigned long) &hash_page_patch_C[1]); + + /* + * Patch up the instructions in hashtable.S:flush_hash_page + */ + flush_hash_patch_A[0] = (flush_hash_patch_A[0] & ~0xffff) + | ((unsigned int)(Hash) >> 16); + flush_hash_patch_A[1] = (flush_hash_patch_A[1] & ~0x7c0) | (mb << 6); + flush_hash_patch_A[2] = (flush_hash_patch_A[2] & ~0x7c0) | (mb2 << 6); + flush_hash_patch_B[0] = (flush_hash_patch_B[0] & ~0xffff) | hmask; + flush_icache_range((unsigned long) &flush_hash_patch_A[0], + (unsigned long) &flush_hash_patch_B[1]); if ( ppc_md.progress ) ppc_md.progress("hash:done", 0x205); } @@ -303,6 +293,9 @@ static int nopreload; if (Hash == 0 || nopreload) + return; + /* We only want HPTEs for linux PTEs that have _PAGE_ACCESSED set */ + if (!pte_young(pte)) return; mm = (address < TASK_SIZE)? vma->vm_mm: &init_mm; pmd = pmd_offset(pgd_offset(mm, address), address); diff -Nru a/arch/ppc/platforms/Makefile b/arch/ppc/platforms/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,76 @@ +# BK Id: %F% %I% %G% %U% %#% +# +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +USE_STANDARD_AS_RULE := true + +ifdef CONFIG_PPC64BRIDGE +EXTRA_AFLAGS := -Wa,-mppc64bridge +endif +ifdef CONFIG_4xx +EXTRA_AFLAGS := -Wa,-m405 +endif + +# Extra CFLAGS so we don't have to do relative includes +CFLAGS_pmac_setup.o += -I$(TOPDIR)/arch/$(ARCH)/mm + +all: platform.o + +O_TARGET := platform.o + +export-objs := prep_setup.o + +obj-$(CONFIG_CEDER) += ceder.o ibmnp405l.o +obj-$(CONFIG_CPCI405) += cpci405.o ibm405gp.o +obj-$(CONFIG_EP405) += ep405.o ibm405gp.o +obj-$(CONFIG_REDWOOD_4) += redwood.o ibmstb3.o +obj-$(CONFIG_REDWOOD_5) += redwood5.o ibmstb4.o +obj-$(CONFIG_WALNUT) += walnut.o ibm405gp.o +obj-$(CONFIG_ASH) += ash.o ibmnp405h.o +obj-$(CONFIG_APUS) += apus_setup.o +ifeq ($(CONFIG_APUS),y) +obj-$(CONFIG_PCI) += apus_pci.o +endif +obj-$(CONFIG_ALL_PPC) += pmac_pic.o pmac_setup.o pmac_time.o \ + pmac_feature.o pmac_pci.o chrp_setup.o\ + chrp_time.o chrp_pci.o prep_pci.o \ + prep_time.o prep_nvram.o prep_setup.o +ifeq ($(CONFIG_ALL_PPC),y) +obj-$(CONFIG_NVRAM) += pmac_nvram.o +endif +obj-$(CONFIG_PMAC_BACKLIGHT) += pmac_backlight.o +obj-$(CONFIG_PMAC_PBOOK) += sleep.o +obj-$(CONFIG_PPC_RTAS) += error_log.o proc_rtas.o +obj-$(CONFIG_PREP_RESIDUAL) += residual.o +obj-$(CONFIG_ADIR) += adir_setup.o adir_pic.o adir_pci.o +obj-$(CONFIG_EV64260) += ev64260_setup.o +obj-$(CONFIG_GEMINI) += gemini_pci.o gemini_setup.o gemini_prom.o +obj-$(CONFIG_K2) += k2_setup.o k2_pci.o +obj-$(CONFIG_LOPEC) += lopec_setup.o lopec_pci.o +obj-$(CONFIG_MCPN765) += mcpn765_setup.o mcpn765_pci.o +obj-$(CONFIG_MENF1) += menf1_setup.o menf1_pci.o +obj-$(CONFIG_MVME5100) += mvme5100_setup.o mvme5100_pci.o +obj-$(CONFIG_PCORE) += pcore_setup.o pcore_pci.o +obj-$(CONFIG_POWERPMC250) += powerpmc250.o +obj-$(CONFIG_PPLUS) += pplus_pci.o pplus_setup.o prep_nvram.o +obj-$(CONFIG_PRPMC750) += prpmc750_setup.o prpmc750_pci.o +obj-$(CONFIG_PRPMC800) += prpmc800_setup.o prpmc800_pci.o +obj-$(CONFIG_SANDPOINT) += sandpoint_setup.o sandpoint_pci.o +obj-$(CONFIG_SPRUCE) += spruce_setup.o spruce_pci.o cpc700_pic.o +obj-$(CONFIG_ZX4500) += zx4500_setup.o zx4500_pci.o +obj-$(CONFIG_PPC_ISERIES) += iSeries_setup.o iSeries_time.o \ + iSeries_dma.o iSeries_pic.o + +ifeq ($(CONFIG_SMP),y) +obj-$(CONFIG_ALL_PPC) += pmac_smp.o chrp_smp.o +obj-$(CONFIG_PPC_ISERIES) += iSeries_smp.o +endif + +include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc/platforms/adir.h b/arch/ppc/platforms/adir.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/adir.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,95 @@ +/* + * arch/ppc/platforms/adir.h + * + * Definitions for SBS Adirondack board support + * + * By Michael Sokolov + */ + +#ifndef __PPC_PLATFORMS_ADIR_H +#define __PPC_PLATFORMS_ADIR_H + +/* + * SBS Adirondack definitions + */ + +/* PPC physical address space layout. We use the one set up by the firmware. */ +#define ADIR_PCI32_MEM_BASE 0x80000000 +#define ADIR_PCI32_MEM_SIZE 0x20000000 +#define ADIR_PCI64_MEM_BASE 0xA0000000 +#define ADIR_PCI64_MEM_SIZE 0x20000000 +#define ADIR_PCI32_IO_BASE 0xC0000000 +#define ADIR_PCI32_IO_SIZE 0x10000000 +#define ADIR_PCI64_IO_BASE 0xD0000000 +#define ADIR_PCI64_IO_SIZE 0x10000000 +#define ADIR_PCI64_PHB 0xFF400000 +#define ADIR_PCI32_PHB 0xFF500000 + +#define ADIR_PCI64_CONFIG_ADDR (ADIR_PCI64_PHB + 0x000f8000) +#define ADIR_PCI64_CONFIG_DATA (ADIR_PCI64_PHB + 0x000f8010) + +#define ADIR_PCI32_CONFIG_ADDR (ADIR_PCI32_PHB + 0x000f8000) +#define ADIR_PCI32_CONFIG_DATA (ADIR_PCI32_PHB + 0x000f8010) + +/* System memory as seen from PCI */ +#define ADIR_PCI_SYS_MEM_BASE 0x80000000 + +/* Static virtual mapping of PCI I/O */ +#define ADIR_PCI32_VIRT_IO_BASE 0xFE000000 +#define ADIR_PCI32_VIRT_IO_SIZE 0x01000000 +#define ADIR_PCI64_VIRT_IO_BASE 0xFF000000 +#define ADIR_PCI64_VIRT_IO_SIZE 0x01000000 + +/* Registers */ +#define ADIR_NVRAM_RTC_ADDR 0x74 +#define ADIR_NVRAM_RTC_DATA 0x75 + +#define ADIR_BOARD_ID_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF0) +#define ADIR_CPLD1REV_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF1) +#define ADIR_CPLD2REV_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF2) +#define ADIR_FLASHCTL_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF3) +#define ADIR_CPC710_STAT_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF4) +#define ADIR_CLOCK_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF5) +#define ADIR_GPIO_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF8) +#define ADIR_MISC_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF9) +#define ADIR_LED_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFFA) + +#define ADIR_CLOCK_REG_PD 0x10 +#define ADIR_CLOCK_REG_SPREAD 0x08 +#define ADIR_CLOCK_REG_SEL133 0x04 +#define ADIR_CLOCK_REG_SEL1 0x02 +#define ADIR_CLOCK_REG_SEL0 0x01 + +#define ADIR_PROCA_INT_MASK (ADIR_PCI32_VIRT_IO_BASE + 0x0EFFF0) +#define ADIR_PROCB_INT_MASK (ADIR_PCI32_VIRT_IO_BASE + 0x0EFFF2) +#define ADIR_PROCA_INT_STAT (ADIR_PCI32_VIRT_IO_BASE + 0x0EFFF4) +#define ADIR_PROCB_INT_STAT (ADIR_PCI32_VIRT_IO_BASE + 0x0EFFF6) + +/* Linux IRQ numbers */ +#define ADIR_IRQ_NONE -1 +#define ADIR_IRQ_SERIAL2 3 +#define ADIR_IRQ_SERIAL1 4 +#define ADIR_IRQ_FDC 6 +#define ADIR_IRQ_PARALLEL 7 +#define ADIR_IRQ_VIA_AUDIO 10 +#define ADIR_IRQ_VIA_USB 11 +#define ADIR_IRQ_IDE0 14 +#define ADIR_IRQ_IDE1 15 +#define ADIR_IRQ_PCI0_INTA 16 +#define ADIR_IRQ_PCI0_INTB 17 +#define ADIR_IRQ_PCI0_INTC 18 +#define ADIR_IRQ_PCI0_INTD 19 +#define ADIR_IRQ_PCI1_INTA 20 +#define ADIR_IRQ_PCI1_INTB 21 +#define ADIR_IRQ_PCI1_INTC 22 +#define ADIR_IRQ_PCI1_INTD 23 +#define ADIR_IRQ_MBSCSI 24 /* motherboard SCSI */ +#define ADIR_IRQ_MBETH1 25 /* motherboard Ethernet 1 */ +#define ADIR_IRQ_MBETH0 26 /* motherboard Ethernet 0 */ +#define ADIR_IRQ_CPC710_INT1 27 +#define ADIR_IRQ_CPC710_INT2 28 +#define ADIR_IRQ_VT82C686_NMI 29 +#define ADIR_IRQ_VT82C686_INTR 30 +#define ADIR_IRQ_INTERPROC 31 + +#endif /* __PPC_PLATFORMS_ADIR_H */ diff -Nru a/arch/ppc/platforms/adir_pci.c b/arch/ppc/platforms/adir_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/adir_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,247 @@ +/* + * arch/ppc/platforms/adir_pci.c + * + * PCI support for SBS Adirondack + * + * By Michael Sokolov + * based on the K2 version by Matt Porter + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "cpc710.h" +#include "adir.h" + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif /* DEBUG */ + +static inline int __init +adir_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ +#define PCIIRQ(a,b,c,d) {ADIR_IRQ_##a,ADIR_IRQ_##b,ADIR_IRQ_##c,ADIR_IRQ_##d}, + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + /* + * The three PCI devices on the motherboard have dedicated lines to the + * CPLD interrupt controller, bypassing the standard PCI INTA-D and the + * PC interrupt controller. All other PCI devices (slots) have usual + * staggered INTA-D lines, resulting in 8 lines total (PCI0 INTA-D and + * PCI1 INTA-D). All 8 go to the CPLD interrupt controller. PCI0 INTA-D + * also go to the south bridge, so we have the option of taking them + * via the CPLD interrupt controller or via the south bridge 8259 + * 8258 thingy. PCI1 INTA-D can only be taken via the CPLD interrupt + * controller. We take all PCI interrupts via the CPLD interrupt + * controller as recommended by SBS. + * + * We also have some monkey business with the PCI devices within the + * VT82C686B south bridge itself. This chip actually has 7 functions on + * its IDSEL. Function 0 is the actual south bridge, function 1 is IDE, + * and function 4 is some special stuff. The other 4 functions are just + * regular PCI devices bundled in the chip. 2 and 3 are USB UHCIs and 5 + * and 6 are audio (not supported on the Adirondack). + * + * This is where the monkey business begins. PCI devices are supposed + * to signal normal PCI interrupts. But the 4 functions in question are + * located in the south bridge chip, which is designed with the + * assumption that it will be fielding PCI INTA-D interrupts rather + * than generating them. Here's what it does. Each of the functions in + * question routes its interrupt to one of the IRQs on the 8259 thingy. + * Which one? It looks at the Interrupt Line register in the PCI config + * space, even though the PCI spec says it's for BIOS/OS interaction + * only. + * + * How do we deal with this? We take these interrupts via 8259 IRQs as + * we have to. We return the desired IRQ numbers from this routine when + * called for the functions in question. The PCI scan code will then + * stick our return value into the Interrupt Line register in the PCI + * config space, and the interrupt will actually go there. We identify + * these functions within the south bridge IDSEL by their interrupt pin + * numbers, as the VT82C686B has 04 in the Interrupt Pin register for + * USB and 03 for audio. + */ + if (!hose->index) { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + /* south bridge */ PCIIRQ(IDE0, NONE, VIA_AUDIO, VIA_USB) + /* Ethernet 0 */ PCIIRQ(MBETH0, MBETH0, MBETH0, MBETH0) + /* PCI0 slot 1 */ PCIIRQ(PCI0_INTB, PCI0_INTC, PCI0_INTD, PCI0_INTA) + /* PCI0 slot 2 */ PCIIRQ(PCI0_INTC, PCI0_INTD, PCI0_INTA, PCI0_INTB) + /* PCI0 slot 3 */ PCIIRQ(PCI0_INTD, PCI0_INTA, PCI0_INTB, PCI0_INTC) + }; + const long min_idsel = 3, max_idsel = 7, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } else { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + /* Ethernet 1 */ PCIIRQ(MBETH1, MBETH1, MBETH1, MBETH1) + /* SCSI */ PCIIRQ(MBSCSI, MBSCSI, MBSCSI, MBSCSI) + /* PCI1 slot 1 */ PCIIRQ(PCI1_INTB, PCI1_INTC, PCI1_INTD, PCI1_INTA) + /* PCI1 slot 2 */ PCIIRQ(PCI1_INTC, PCI1_INTD, PCI1_INTA, PCI1_INTB) + /* PCI1 slot 3 */ PCIIRQ(PCI1_INTD, PCI1_INTA, PCI1_INTB, PCI1_INTC) + }; + const long min_idsel = 3, max_idsel = 7, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } +#undef PCIIRQ +} + +static void +adir_pcibios_fixup_resources(struct pci_dev *dev) +{ + int i; + + if ((dev->vendor == PCI_VENDOR_ID_IBM) && + (dev->device == PCI_DEVICE_ID_IBM_CPC710_PCI64)) + { + DBG("Fixup CPC710 resources\n"); + for (i=0; iresource[i].start = 0; + dev->resource[i].end = 0; + } + } +} + +/* + * CPC710 DD3 has an errata causing it to hang the system if a type 0 config + * cycle is attempted on its PCI32 interface with a device number > 21. + * CPC710's PCI bridges map device numbers 1 through 21 to AD11 through AD31. + * Per the PCI spec it MUST accept all other device numbers and do nothing, and + * software MUST scan all device numbers without assuming how IDSELs are + * mapped. However, as the CPC710 DD3's errata causes such correct scanning + * procedure to hang the system, we have no choice but to introduce this hack + * of knowingly avoiding device numbers > 21 on PCI0, + */ +static int +adir_exclude_device(u_char bus, u_char devfn) +{ + if ((bus == 0) && (PCI_SLOT(devfn) > 21)) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} + +void adir_find_bridges(void) +{ + struct pci_controller *hose_a, *hose_b; + + /* Setup PCI32 hose */ + hose_a = pcibios_alloc_controller(); + if (!hose_a) + return; + + hose_a->first_busno = 0; + hose_a->last_busno = 0xff; + hose_a->pci_mem_offset = ADIR_PCI32_MEM_BASE; + hose_a->io_space.start = 0; + hose_a->io_space.end = ADIR_PCI32_VIRT_IO_SIZE - 1; + hose_a->mem_space.start = 0; + hose_a->mem_space.end = ADIR_PCI32_MEM_SIZE - 1; + hose_a->io_resource.start = 0; + hose_a->io_resource.end = ADIR_PCI32_VIRT_IO_SIZE - 1; + hose_a->io_resource.flags = IORESOURCE_IO; + hose_a->mem_resources[0].start = ADIR_PCI32_MEM_BASE; + hose_a->mem_resources[0].end = ADIR_PCI32_MEM_BASE + + ADIR_PCI32_MEM_SIZE - 1; + hose_a->mem_resources[0].flags = IORESOURCE_MEM; + hose_a->io_base_phys = ADIR_PCI32_IO_BASE; + hose_a->io_base_virt = (void *) ADIR_PCI32_VIRT_IO_BASE; + + ppc_md.pci_exclude_device = adir_exclude_device; + setup_indirect_pci(hose_a, ADIR_PCI32_CONFIG_ADDR, + ADIR_PCI32_CONFIG_DATA); + + /* Initialize PCI32 bus registers */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_BUS_NUMBER, + hose_a->first_busno); + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + hose_a->last_busno); + + hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); + + /* Write out correct max subordinate bus number for hose A */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + hose_a->last_busno); + + /* Setup PCI64 hose */ + hose_b = pcibios_alloc_controller(); + if (!hose_b) + return; + + hose_b->first_busno = hose_a->last_busno + 1; + hose_b->last_busno = 0xff; + hose_b->pci_mem_offset = ADIR_PCI64_MEM_BASE; + hose_b->io_space.start = 0; + hose_b->io_space.end = ADIR_PCI64_VIRT_IO_SIZE - 1; + hose_b->mem_space.start = 0; + hose_b->mem_space.end = ADIR_PCI64_MEM_SIZE - 1; + hose_b->io_resource.start = 0; + hose_b->io_resource.end = ADIR_PCI64_VIRT_IO_SIZE - 1; + hose_b->io_resource.flags = IORESOURCE_IO; + hose_b->mem_resources[0].start = ADIR_PCI64_MEM_BASE; + hose_b->mem_resources[0].end = ADIR_PCI64_MEM_BASE + + ADIR_PCI64_MEM_SIZE - 1; + hose_b->mem_resources[0].flags = IORESOURCE_MEM; + hose_b->io_base_phys = ADIR_PCI64_IO_BASE; + hose_b->io_base_virt = (void *) ADIR_PCI64_VIRT_IO_BASE; + + setup_indirect_pci(hose_b, ADIR_PCI64_CONFIG_ADDR, + ADIR_PCI64_CONFIG_DATA); + + /* Initialize PCI64 bus registers */ + early_write_config_byte(hose_b, + 0, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + 0xff); + + early_write_config_byte(hose_b, + 0, + PCI_DEVFN(0, 0), + CPC710_BUS_NUMBER, + hose_b->first_busno); + + hose_b->last_busno = pciauto_bus_scan(hose_b, + hose_b->first_busno); + + /* Write out correct max subordinate bus number for hose B */ + early_write_config_byte(hose_b, + hose_b->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + hose_b->last_busno); + + ppc_md.pcibios_fixup = NULL; + ppc_md.pcibios_fixup_resources = adir_pcibios_fixup_resources; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = adir_map_irq; +} diff -Nru a/arch/ppc/platforms/adir_pic.c b/arch/ppc/platforms/adir_pic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/adir_pic.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,132 @@ +/* + * arch/ppc/platforms/adir_pic.c + * + * Interrupt controller support for SBS Adirondack + * + * By Michael Sokolov + * based on the K2 and SCM versions by Matt Porter + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "adir.h" + +static void adir_onboard_pic_enable(unsigned int irq); +static void adir_onboard_pic_disable(unsigned int irq); + +static void +no_action(int cpl, void *dev_id, struct pt_regs *regs) +{ +} + +__init static void +adir_onboard_pic_init(void) +{ + volatile u_short *maskreg = (volatile u_short *) ADIR_PROCA_INT_MASK; + + /* Disable all Adirondack onboard interrupts */ + out_be16(maskreg, 0xFFFF); +} + +static int +adir_onboard_pic_get_irq(void) +{ + volatile u_short *statreg = (volatile u_short *) ADIR_PROCA_INT_STAT; + int irq; + u_short int_status, int_test; + + int_status = in_be16(statreg); + for (irq = 0, int_test = 1; irq < 16; irq++, int_test <<= 1) { + if (int_status & int_test) + break; + } + + if (irq == 16) + return -1; + + return (irq+16); +} + +static void +adir_onboard_pic_enable(unsigned int irq) +{ + volatile u_short *maskreg = (volatile u_short *) ADIR_PROCA_INT_MASK; + + /* Change irq to Adirondack onboard native value */ + irq -= 16; + + /* Enable requested irq number */ + out_be16(maskreg, in_be16(maskreg) & ~(1 << irq)); +} + +static void +adir_onboard_pic_disable(unsigned int irq) +{ + volatile u_short *maskreg = (volatile u_short *) ADIR_PROCA_INT_MASK; + + /* Change irq to Adirondack onboard native value */ + irq -= 16; + + /* Disable requested irq number */ + out_be16(maskreg, in_be16(maskreg) | (1 << irq)); +} + +static struct hw_interrupt_type adir_onboard_pic = { + " ADIR PIC ", + NULL, + NULL, + adir_onboard_pic_enable, /* unmask */ + adir_onboard_pic_disable, /* mask */ + adir_onboard_pic_disable, /* mask and ack */ + NULL, + NULL +}; + +/* + * Linux interrupt values are assigned as follows: + * + * 0-15 VT82C686 8259 interrupts + * 16-31 Adirondack CPLD interrupts + */ +__init void +adir_init_IRQ(void) +{ + int i; + + /* Initialize the cascaded 8259's on the VT82C686 */ + for (i=0; i<16; i++) + irq_desc[i].handler = &i8259_pic; + i8259_init(NULL); + + /* Initialize Adirondack CPLD PIC and enable 8259 interrupt cascade */ + for (i=16; i<32; i++) + irq_desc[i].handler = &adir_onboard_pic; + adir_onboard_pic_init(); + + /* Enable 8259 interrupt cascade */ + request_irq(ADIR_IRQ_VT82C686_INTR, + no_action, + SA_INTERRUPT, + "82c59 primary cascade", + NULL); +} + +int +adir_get_irq(void) +{ + int irq; + + if ((irq = adir_onboard_pic_get_irq()) < 0) + return irq; + + if (irq == ADIR_IRQ_VT82C686_INTR) + irq = i8259_poll(); + + return irq; +} diff -Nru a/arch/ppc/platforms/adir_setup.c b/arch/ppc/platforms/adir_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/adir_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,211 @@ +/* + * arch/ppc/platforms/adir_setup.c + * + * Board setup routines for SBS Adirondack + * + * By Michael Sokolov + * based on the K2 version by Matt Porter + */ + +#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 "adir.h" + +extern void adir_init_IRQ(void); +extern int adir_get_irq(struct pt_regs *); +extern void adir_find_bridges(void); +extern unsigned long loops_per_jiffy; + +static unsigned int cpu_750cx[16] = { + 5, 15, 14, 0, 4, 13, 0, 9, 6, 11, 8, 10, 16, 12, 7, 0 +}; + +static int +adir_get_bus_speed(void) +{ + if (!(*((u_char *) ADIR_CLOCK_REG) & ADIR_CLOCK_REG_SEL133)) + return 100000000; + else + return 133333333; +} + +static int +adir_get_cpu_speed(void) +{ + unsigned long hid1; + int cpu_speed; + + hid1 = mfspr(HID1) >> 28; + + hid1 = cpu_750cx[hid1]; + + cpu_speed = adir_get_bus_speed()*hid1/2; + return cpu_speed; +} + +static void __init +adir_calibrate_decr(void) +{ + int freq, divisor = 4; + + /* determine processor bus speed */ + freq = adir_get_bus_speed(); + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); +} + +static int +adir_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: SBS\n"); + seq_printf(m, "machine\t\t: Adirondack\n"); + seq_printf(m, "cpu speed\t: %dMhz\n", adir_get_cpu_speed()/1000000); + seq_printf(m, "bus speed\t: %dMhz\n", adir_get_bus_speed()/1000000); + seq_printf(m, "memory type\t: SDRAM\n"); + + return 0; +} + +extern char cmd_line[]; + +TODC_ALLOC(); + +static void __init +adir_setup_arch(void) +{ + unsigned int cpu; + + /* Setup TODC access */ + TODC_INIT(TODC_TYPE_MC146818, ADIR_NVRAM_RTC_ADDR, 0, + ADIR_NVRAM_RTC_DATA, 8); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Setup PCI host bridges */ + adir_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00FF); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0801); /* /dev/sda1 */ +#endif + + /* Identify the system */ + printk("System Identification: SBS Adirondack - PowerPC 750CXe @ %d Mhz\n", adir_get_cpu_speed()/1000000); + printk("SBS Adirondack port (C) 2001 SBS Technologies, Inc.\n"); + + /* Identify the CPU manufacturer */ + cpu = mfspr(PVR); + printk("CPU manufacturer: IBM [rev=%04x]\n", (cpu & 0xffff)); +} + +static void +adir_restart(char *cmd) +{ + __cli(); + /* SRR0 has system reset vector, SRR1 has default MSR value */ + /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */ + __asm__ __volatile__ + ("lis 3,0xfff0\n\t" + "ori 3,3,0x0100\n\t" + "mtspr 26,3\n\t" + "li 3,0\n\t" + "mtspr 27,3\n\t" + "rfi\n\t"); + for(;;); +} + +static void +adir_power_off(void) +{ + for(;;); +} + +static void +adir_halt(void) +{ + adir_restart(NULL); +} + +extern unsigned int boot_mem_size; + +static unsigned long __init +adir_find_end_of_memory(void) +{ + return boot_mem_size; +} + +static void __init +adir_map_io(void) +{ + io_block_mapping(ADIR_PCI32_VIRT_IO_BASE, ADIR_PCI32_IO_BASE, + ADIR_PCI32_VIRT_IO_SIZE, _PAGE_IO); + io_block_mapping(ADIR_PCI64_VIRT_IO_BASE, ADIR_PCI64_IO_BASE, + ADIR_PCI64_VIRT_IO_SIZE, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* + * On the Adirondack we use bi_recs and pass the pointer to them in R3. + */ + parse_bootinfo((struct bi_record *) (r3 + KERNELBASE)); + + /* Remember, isa_io_base is virtual but isa_mem_base is physical! */ + isa_io_base = ADIR_PCI32_VIRT_IO_BASE; + isa_mem_base = ADIR_PCI32_MEM_BASE; + pci_dram_offset = ADIR_PCI_SYS_MEM_BASE; + + ppc_md.setup_arch = adir_setup_arch; + ppc_md.show_cpuinfo = adir_show_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = adir_init_IRQ; + ppc_md.get_irq = adir_get_irq; + ppc_md.init = NULL; + + ppc_md.find_end_of_memory = adir_find_end_of_memory; + ppc_md.setup_io_mappings = adir_map_io; + + ppc_md.restart = adir_restart; + ppc_md.power_off = adir_power_off; + ppc_md.halt = adir_halt; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_mc146818_read_val; + ppc_md.nvram_write_val = todc_mc146818_write_val; + ppc_md.calibrate_decr = adir_calibrate_decr; +} diff -Nru a/arch/ppc/platforms/apus_pci.c b/arch/ppc/platforms/apus_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/apus_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,188 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * Copyright (C) Michel Dänzer + * + * APUS PCI routines. + * + * Currently, only B/CVisionPPC cards (Permedia2) are supported. + * + * Thanks to Geert Uytterhoeven for the idea: + * Read values from given config space(s) for the first devices, -1 otherwise + * + */ + +#include +#ifdef CONFIG_AMIGA + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "apus_pci.h" + + +/* These definitions are mostly adapted from pm2fb.c */ + +#undef APUS_PCI_MASTER_DEBUG +#ifdef APUS_PCI_MASTER_DEBUG +#define DPRINTK(a,b...) printk(KERN_DEBUG "apus_pci: %s: " a, __FUNCTION__ , ## b) +#else +#define DPRINTK(a,b...) +#endif + +/* + * The _DEFINITIVE_ memory mapping/unmapping functions. + * This is due to the fact that they're changing soooo often... + */ +#define DEFW() wmb() +#define DEFR() rmb() +#define DEFRW() mb() + +#define DEVNO(d) ((d)>>3) +#define FNNO(d) ((d)&7) + + +extern unsigned long powerup_PCI_present; + +static struct pci_controller *apus_hose; + + +void *pci_io_base(unsigned int bus) +{ + return 0; +} + + +#define cfg_read(val, addr, type, op) *val = op((type)(addr)) +#define cfg_write(val, addr, type, op) op((val), (type *)(addr)); DEFW() +#define cfg_read_bad *val = ~0; +#define cfg_write_bad ; +#define cfg_read_val(val) *val +#define cfg_write_val(val) val + +#define APUS_PCI_OP(rw, size, type, op, mask) \ +int \ +apus_pcibios_##rw##_config_##size(struct pci_dev *dev, int offset, type val) \ +{ \ + int fnno = FNNO(dev->devfn); \ + int devno = DEVNO(dev->devfn); \ + \ + if (dev->bus->number > 0 || devno != 1) { \ + cfg_##rw##_bad; \ + return PCIBIOS_DEVICE_NOT_FOUND; \ + } \ + /* base address + function offset + offset ^ endianness conversion */ \ + cfg_##rw(val, apus_hose->cfg_data + (fnno<<5) + (offset ^ mask), \ + type, op); \ + \ + DPRINTK(#op " b: 0x%x, d: 0x%x, f: 0x%x, o: 0x%x, v: 0x%x\n", \ + dev->bus->number, dev->devfn>>3, dev->devfn&7, \ + offset, cfg_##rw##_val(val)); \ + return PCIBIOS_SUCCESSFUL; \ +} + +APUS_PCI_OP(read, byte, u8 *, readb, 3) +APUS_PCI_OP(read, word, u16 *, readw, 2) +APUS_PCI_OP(read, dword, u32 *, readl, 0) +APUS_PCI_OP(write, byte, u8, writeb, 3) +APUS_PCI_OP(write, word, u16, writew, 2) +APUS_PCI_OP(write, dword, u32, writel, 0) + + +static struct pci_ops apus_pci_ops = { + apus_pcibios_read_config_byte, + apus_pcibios_read_config_word, + apus_pcibios_read_config_dword, + apus_pcibios_write_config_byte, + apus_pcibios_write_config_word, + apus_pcibios_write_config_dword +}; + +static struct resource pci_mem = { "B/CVisionPPC PCI mem", CVPPC_FB_APERTURE_ONE, CVPPC_PCI_CONFIG, IORESOURCE_MEM }; + +void __init +apus_pcibios_fixup(void) +{ +/* struct pci_dev *dev = pci_find_slot(0, 1<<3); + unsigned int reg, val, offset;*/ + + /* FIXME: interrupt? */ + /*dev->interrupt = xxx;*/ + + request_resource(&iomem_resource, &pci_mem); + printk("%s: PCI mem resource requested\n", __FUNCTION__); +} + +static void __init apus_pcibios_fixup_bus(struct pci_bus *bus) +{ + bus->resource[1] = &pci_mem; +} + + +/* + * This is from pm2fb.c again + * + * Check if PCI (B/CVisionPPC) is available, initialize it and set up + * the pcibios_* pointers + */ + + +void __init +apus_setup_pci_ptrs(void) +{ + if (!powerup_PCI_present) { + DPRINTK("no PCI bridge detected\n"); + return; + } + DPRINTK("Phase5 B/CVisionPPC PCI bridge detected.\n"); + + apus_hose = pcibios_alloc_controller(); + if (!apus_hose) { + printk("apus_pci: Can't allocate PCI controller structure\n"); + return; + } + + if (!(apus_hose->cfg_data = ioremap(CVPPC_PCI_CONFIG, 256))) { + printk("apus_pci: unable to map PCI config region\n"); + return; + } + + if (!(apus_hose->cfg_addr = ioremap(CSPPC_PCI_BRIDGE, 256))) { + printk("apus_pci: unable to map PCI bridge\n"); + return; + } + + writel(CSPPCF_BRIDGE_BIG_ENDIAN, apus_hose->cfg_addr + CSPPC_BRIDGE_ENDIAN); + DEFW(); + + writel(CVPPC_REGS_REGION, apus_hose->cfg_data+ PCI_BASE_ADDRESS_0); + DEFW(); + writel(CVPPC_FB_APERTURE_ONE, apus_hose->cfg_data + PCI_BASE_ADDRESS_1); + DEFW(); + writel(CVPPC_FB_APERTURE_TWO, apus_hose->cfg_data + PCI_BASE_ADDRESS_2); + DEFW(); + writel(CVPPC_ROM_ADDRESS, apus_hose->cfg_data + PCI_ROM_ADDRESS); + DEFW(); + + writel(0xef000000 | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER, apus_hose->cfg_data + PCI_COMMAND); + DEFW(); + + apus_hose->first_busno = 0; + apus_hose->last_busno = 0; + apus_hose->ops = &apus_pci_ops; + ppc_md.pcibios_fixup = apus_pcibios_fixup; + ppc_md.pcibios_fixup_bus = apus_pcibios_fixup_bus; + + return; +} + +#endif /* CONFIG_AMIGA */ diff -Nru a/arch/ppc/platforms/apus_pci.h b/arch/ppc/platforms/apus_pci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/apus_pci.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,37 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * Phase5 CybervisionPPC (TVP4020) definitions for the Permedia2 framebuffer + * driver. + * + * Copyright (c) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) + * -------------------------------------------------------------------------- + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + */ + +#ifndef APUS_PCI_H +#define APUS_PCI_H + + +#define CSPPC_PCI_BRIDGE 0xfffe0000 +#define CSPPC_BRIDGE_ENDIAN 0x0000 +#define CSPPC_BRIDGE_INT 0x0010 + +#define CVPPC_PCI_CONFIG 0xfffc0000 +#define CVPPC_ROM_ADDRESS 0xe2000001 +#define CVPPC_REGS_REGION 0xef000000 +#define CVPPC_FB_APERTURE_ONE 0xe0000000 +#define CVPPC_FB_APERTURE_TWO 0xe1000000 +#define CVPPC_FB_SIZE 0x00800000 + +/* CVPPC_BRIDGE_ENDIAN */ +#define CSPPCF_BRIDGE_BIG_ENDIAN 0x02 + +/* CVPPC_BRIDGE_INT */ +#define CSPPCF_BRIDGE_ACTIVE_INT2 0x01 + + +#endif /* APUS_PCI_H */ diff -Nru a/arch/ppc/platforms/apus_setup.c b/arch/ppc/platforms/apus_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/apus_setup.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,866 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/platforms/apus_setup.c + * + * Copyright (C) 1998, 1999 Jesper Skov + * + * Basically what is needed to replace functionality found in + * arch/m68k allowing Amiga drivers to work under APUS. + * Bits of code and/or ideas from arch/m68k and arch/ppc files. + * + * TODO: + * This file needs a *really* good cleanup. Restructure and optimize. + * Make sure it can be compiled for non-APUS configs. Begin to move + * Amiga specific stuff into mach/amiga. + */ + +#include +#include +#include +#include +#include +#include + +/* Needs INITSERIAL call in head.S! */ +#undef APUS_DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned long m68k_machtype; +char debug_device[6] = ""; + +extern void amiga_init_IRQ(void); + +extern int amiga_kbd_translate(unsigned char keycode, unsigned char *keycodep, char raw_mode); +extern char amiga_sysrq_xlate[128]; + +extern void apus_setup_pci_ptrs(void); + +void (*mach_sched_init) (void (*handler)(int, void *, struct pt_regs *)) __initdata = NULL; +/* machine dependent keyboard functions */ +int (*mach_keyb_init) (void) __initdata = NULL; +int (*mach_kbdrate) (struct kbd_repeat *) = NULL; +void (*mach_kbd_leds) (unsigned int) = NULL; +/* machine dependent irq functions */ +void (*mach_init_IRQ) (void) __initdata = NULL; +void (*(*mach_default_handler)[]) (int, void *, struct pt_regs *) = NULL; +void (*mach_get_model) (char *model) = NULL; +int (*mach_get_hardware_list) (char *buffer) = NULL; +int (*mach_get_irq_list) (struct seq_file *, void *) = NULL; +void (*mach_process_int) (int, struct pt_regs *) = NULL; +/* machine dependent timer functions */ +unsigned long (*mach_gettimeoffset) (void); +void (*mach_gettod) (int*, int*, int*, int*, int*, int*); +int (*mach_hwclk) (int, struct hwclk_time*) = NULL; +int (*mach_set_clock_mmss) (unsigned long) = NULL; +void (*mach_reset)( void ); +long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */ +#if defined(CONFIG_AMIGA_FLOPPY) +void (*mach_floppy_setup) (char *, int *) __initdata = NULL; +#endif +#ifdef CONFIG_HEARTBEAT +void (*mach_heartbeat) (int) = NULL; +extern void apus_heartbeat (void); +#endif + +extern unsigned long amiga_model; +extern unsigned decrementer_count;/* count value for 1e6/HZ microseconds */ +extern unsigned count_period_num; /* 1 decrementer count equals */ +extern unsigned count_period_den; /* count_period_num / count_period_den us */ + +int num_memory = 0; +struct mem_info memory[NUM_MEMINFO];/* memory description */ +/* FIXME: Duplicate memory data to avoid conflicts with m68k shared code. */ +int m68k_realnum_memory = 0; +struct mem_info m68k_memory[NUM_MEMINFO];/* memory description */ + +struct mem_info ramdisk; + +extern void amiga_floppy_setup(char *, int *); +extern void config_amiga(void); + +static int __60nsram = 0; + +/* for cpuinfo */ +static int __bus_speed = 0; +static int __speed_test_failed = 0; + +/********************************************** COMPILE PROTECTION */ +/* Provide some stubs that links to Amiga specific functions. + * This allows CONFIG_APUS to be removed from generic PPC files while + * preventing link errors for other PPC targets. + */ +unsigned long apus_get_rtc_time(void) +{ +#ifdef CONFIG_APUS + extern unsigned long m68k_get_rtc_time(void); + + return m68k_get_rtc_time (); +#else + return 0; +#endif +} + +int apus_set_rtc_time(unsigned long nowtime) +{ +#ifdef CONFIG_APUS + extern int m68k_set_rtc_time(unsigned long nowtime); + + return m68k_set_rtc_time (nowtime); +#else + return 0; +#endif +} + +/*********************************************************** SETUP */ +/* From arch/m68k/kernel/setup.c. */ +void __init apus_setup_arch(void) +{ +#ifdef CONFIG_APUS + extern char cmd_line[]; + int i; + char *p, *q; + + /* Let m68k-shared code know it should do the Amiga thing. */ + m68k_machtype = MACH_AMIGA; + + /* Parse the command line for arch-specific options. + * For the m68k, this is currently only "debug=xxx" to enable printing + * certain kernel messages to some machine-specific device. */ + for( p = cmd_line; p && *p; ) { + i = 0; + if (!strncmp( p, "debug=", 6 )) { + strncpy( debug_device, p+6, sizeof(debug_device)-1 ); + debug_device[sizeof(debug_device)-1] = 0; + if ((q = strchr( debug_device, ' ' ))) *q = 0; + i = 1; + } else if (!strncmp( p, "60nsram", 7 )) { + APUS_WRITE (APUS_REG_WAITSTATE, + REGWAITSTATE_SETRESET + |REGWAITSTATE_PPCR + |REGWAITSTATE_PPCW); + __60nsram = 1; + i = 1; + } + + if (i) { + /* option processed, delete it */ + if ((q = strchr( p, ' ' ))) + strcpy( p, q+1 ); + else + *p = 0; + } else { + if ((p = strchr( p, ' ' ))) ++p; + } + } + + config_amiga(); + +#if 0 /* Enable for logging - also include logging.o in Makefile rule */ + { +#define LOG_SIZE 4096 + void* base; + + /* Throw away some memory - the P5 firmare stomps on top + * of CHIP memory during bootup. + */ + amiga_chip_alloc(0x1000); + + base = amiga_chip_alloc(LOG_SIZE+sizeof(klog_data_t)); + LOG_INIT(base, base+sizeof(klog_data_t), LOG_SIZE); + } +#endif +#endif +} + +int +apus_show_cpuinfo(struct seq_file *m) +{ + extern int __map_without_bats; + extern unsigned long powerup_PCI_present; + + seq_printf(m, "machine\t\t: Amiga\n"); + seq_printf(m, "bus speed\t: %d%s", __bus_speed, + (__speed_test_failed) ? " [failed]\n" : "\n"); + seq_printf(m, "using BATs\t: %s\n", + (__map_without_bats) ? "No" : "Yes"); + seq_printf(m, "ram speed\t: %dns\n", (__60nsram) ? 60 : 70); + seq_printf(m, "PCI bridge\t: %s\n", + (powerup_PCI_present) ? "Yes" : "No"); + return 0; +} + +static void get_current_tb(unsigned long long *time) +{ + __asm __volatile ("1:mftbu 4 \n\t" + " mftb 5 \n\t" + " mftbu 6 \n\t" + " cmpw 4,6 \n\t" + " bne 1b \n\t" + " stw 4,0(%0)\n\t" + " stw 5,4(%0)\n\t" + : + : "r" (time) + : "r4", "r5", "r6"); +} + + +void apus_calibrate_decr(void) +{ +#ifdef CONFIG_APUS + unsigned long freq; + + /* This algorithm for determining the bus speed was + contributed by Ralph Schmidt. */ + unsigned long long start, stop; + int bus_speed; + int speed_test_failed = 0; + + { + unsigned long loop = amiga_eclock / 10; + + get_current_tb (&start); + while (loop--) { + unsigned char tmp; + + tmp = ciaa.pra; + } + get_current_tb (&stop); + } + + bus_speed = (((unsigned long)(stop-start))*10*4) / 1000000; + if (AMI_1200 == amiga_model) + bus_speed /= 2; + + if ((bus_speed >= 47) && (bus_speed < 53)) { + bus_speed = 50; + freq = 12500000; + } else if ((bus_speed >= 57) && (bus_speed < 63)) { + bus_speed = 60; + freq = 15000000; + } else if ((bus_speed >= 63) && (bus_speed < 69)) { + bus_speed = 67; + freq = 16666667; + } else { + printk ("APUS: Unable to determine bus speed (%d). " + "Defaulting to 50MHz", bus_speed); + bus_speed = 50; + freq = 12500000; + speed_test_failed = 1; + } + + /* Ease diagnostics... */ + { + extern int __map_without_bats; + extern unsigned long powerup_PCI_present; + + printk ("APUS: BATs=%d, BUS=%dMHz", + (__map_without_bats) ? 0 : 1, + bus_speed); + if (speed_test_failed) + printk ("[FAILED - please report]"); + + printk (", RAM=%dns, PCI bridge=%d\n", + (__60nsram) ? 60 : 70, + (powerup_PCI_present) ? 1 : 0); + + /* print a bit more if asked politely... */ + if (!(ciaa.pra & 0x40)){ + extern unsigned int bat_addrs[4][3]; + int b; + for (b = 0; b < 4; ++b) { + printk ("APUS: BAT%d ", b); + printk ("%08x-%08x -> %08x\n", + bat_addrs[b][0], + bat_addrs[b][1], + bat_addrs[b][2]); + } + } + + } + + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + __bus_speed = bus_speed; + __speed_test_failed = speed_test_failed; +#endif +} + +void arch_gettod(int *year, int *mon, int *day, int *hour, + int *min, int *sec) +{ +#ifdef CONFIG_APUS + if (mach_gettod) + mach_gettod(year, mon, day, hour, min, sec); + else + *year = *mon = *day = *hour = *min = *sec = 0; +#endif +} + +/* for "kbd-reset" cmdline param */ +__init +void kbd_reset_setup(char *str, int *ints) +{ +} + +/*********************************************************** FLOPPY */ +#if defined(CONFIG_AMIGA_FLOPPY) +__init +void floppy_setup(char *str, int *ints) +{ + if (mach_floppy_setup) + mach_floppy_setup (str, ints); +} +#endif + +/*********************************************************** MEMORY */ +#define KMAP_MAX 32 +unsigned long kmap_chunks[KMAP_MAX*3]; +int kmap_chunk_count = 0; + +/* From pgtable.h */ +static __inline__ pte_t *my_find_pte(struct mm_struct *mm,unsigned long va) +{ + pgd_t *dir = 0; + pmd_t *pmd = 0; + pte_t *pte = 0; + + va &= PAGE_MASK; + + dir = pgd_offset( mm, va ); + if (dir) + { + pmd = pmd_offset(dir, va & PAGE_MASK); + if (pmd && pmd_present(*pmd)) + { + pte = pte_offset(pmd, va); + } + } + return pte; +} + + +/* Again simulating an m68k/mm/kmap.c function. */ +void kernel_set_cachemode( unsigned long address, unsigned long size, + unsigned int cmode ) +{ + unsigned long mask, flags; + + switch (cmode) + { + case IOMAP_FULL_CACHING: + mask = ~(_PAGE_NO_CACHE | _PAGE_GUARDED); + flags = 0; + break; + case IOMAP_NOCACHE_SER: + mask = ~0; + flags = (_PAGE_NO_CACHE | _PAGE_GUARDED); + break; + default: + panic ("kernel_set_cachemode() doesn't support mode %d\n", + cmode); + break; + } + + size /= PAGE_SIZE; + address &= PAGE_MASK; + while (size--) + { + pte_t *pte; + + pte = my_find_pte(&init_mm, address); + if ( !pte ) + { + printk("pte NULL in kernel_set_cachemode()\n"); + return; + } + + pte_val (*pte) &= mask; + pte_val (*pte) |= flags; + flush_tlb_page(find_vma(&init_mm,address),address); + + address += PAGE_SIZE; + } +} + +unsigned long mm_ptov (unsigned long paddr) +{ + unsigned long ret; + if (paddr < 16*1024*1024) + ret = ZTWO_VADDR(paddr); + else { + int i; + + for (i = 0; i < kmap_chunk_count;){ + unsigned long phys = kmap_chunks[i++]; + unsigned long size = kmap_chunks[i++]; + unsigned long virt = kmap_chunks[i++]; + if (paddr >= phys + && paddr < (phys + size)){ + ret = virt + paddr - phys; + goto exit; + } + } + + ret = (unsigned long) __va(paddr); + } +exit: +#ifdef DEBUGPV + printk ("PTOV(%lx)=%lx\n", paddr, ret); +#endif + return ret; +} + +int mm_end_of_chunk (unsigned long addr, int len) +{ + if (memory[0].addr + memory[0].size == addr + len) + return 1; + return 0; +} + +/*********************************************************** CACHE */ + +#define L1_CACHE_BYTES 32 +#define MAX_CACHE_SIZE 8192 +void cache_push(__u32 addr, int length) +{ + addr = mm_ptov(addr); + + if (MAX_CACHE_SIZE < length) + length = MAX_CACHE_SIZE; + + while(length > 0){ + __asm ("dcbf 0,%0\n\t" + : : "r" (addr)); + addr += L1_CACHE_BYTES; + length -= L1_CACHE_BYTES; + } + /* Also flush trailing block */ + __asm ("dcbf 0,%0\n\t" + "sync \n\t" + : : "r" (addr)); +} + +void cache_clear(__u32 addr, int length) +{ + if (MAX_CACHE_SIZE < length) + length = MAX_CACHE_SIZE; + + addr = mm_ptov(addr); + + __asm ("dcbf 0,%0\n\t" + "sync \n\t" + "icbi 0,%0 \n\t" + "isync \n\t" + : : "r" (addr)); + + addr += L1_CACHE_BYTES; + length -= L1_CACHE_BYTES; + + while(length > 0){ + __asm ("dcbf 0,%0\n\t" + "sync \n\t" + "icbi 0,%0 \n\t" + "isync \n\t" + : : "r" (addr)); + addr += L1_CACHE_BYTES; + length -= L1_CACHE_BYTES; + } + + __asm ("dcbf 0,%0\n\t" + "sync \n\t" + "icbi 0,%0 \n\t" + "isync \n\t" + : : "r" (addr)); +} + +/****************************************************** from setup.c */ +void +apus_restart(char *cmd) +{ + cli(); + + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK2); + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK3); + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK2|REGLOCK_BLACKMAGICK3); + APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); + APUS_WRITE(APUS_REG_RESET, REGRESET_AMIGARESET); + for(;;); +} + +void +apus_power_off(void) +{ + for (;;); +} + +void +apus_halt(void) +{ + apus_restart(NULL); +} + +/****************************************************** IRQ stuff */ + +static unsigned char last_ipl[8]; + +int apus_get_irq(struct pt_regs* regs) +{ + unsigned char ipl_emu, mask; + unsigned int level; + + APUS_READ(APUS_IPL_EMU, ipl_emu); + level = (ipl_emu >> 3) & IPLEMU_IPLMASK; + mask = IPLEMU_SETRESET|IPLEMU_DISABLEINT|level; + level ^= 7; + + /* Save previous IPL value */ + if (last_ipl[level]) + return -2; + last_ipl[level] = ipl_emu; + + /* Set to current IPL value */ + APUS_WRITE(APUS_IPL_EMU, mask); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT|level); + + +#ifdef __INTERRUPT_DEBUG + printk("<%d:%d>", level, ~ipl_emu & IPLEMU_IPLMASK); +#endif + return level + IRQ_AMIGA_AUTO; +} + +void apus_end_irq(unsigned int irq) +{ + unsigned char ipl_emu; + unsigned int level = irq - IRQ_AMIGA_AUTO; +#ifdef __INTERRUPT_DEBUG + printk("{%d}", ~last_ipl[level] & IPLEMU_IPLMASK); +#endif + /* Restore IPL to the previous value */ + ipl_emu = last_ipl[level] & IPLEMU_IPLMASK; + APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET|IPLEMU_DISABLEINT|ipl_emu); + last_ipl[level] = 0; + ipl_emu ^= 7; + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT|ipl_emu); +} + +/****************************************************** keyboard */ +static int apus_kbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + return -EOPNOTSUPP; +} + +static int apus_kbd_getkeycode(unsigned int scancode) +{ + return scancode > 127 ? -EINVAL : scancode; +} + +static char apus_kbd_unexpected_up(unsigned char keycode) +{ + return 0200; +} + +static void apus_kbd_init_hw(void) +{ +#ifdef CONFIG_APUS + extern int amiga_keyb_init(void); + + amiga_keyb_init(); +#endif +} + + +/****************************************************** debugging */ + +/* some serial hardware definitions */ +#define SDR_OVRUN (1<<15) +#define SDR_RBF (1<<14) +#define SDR_TBE (1<<13) +#define SDR_TSRE (1<<12) + +#define AC_SETCLR (1<<15) +#define AC_UARTBRK (1<<11) + +#define SER_DTR (1<<7) +#define SER_RTS (1<<6) +#define SER_DCD (1<<5) +#define SER_CTS (1<<4) +#define SER_DSR (1<<3) + +static __inline__ void ser_RTSon(void) +{ + ciab.pra &= ~SER_RTS; /* active low */ +} + +int __debug_ser_out( unsigned char c ) +{ + custom.serdat = c | 0x100; + mb(); + while (!(custom.serdatr & 0x2000)) + barrier(); + return 1; +} + +unsigned char __debug_ser_in( void ) +{ + unsigned char c; + + /* XXX: is that ok?? derived from amiga_ser.c... */ + while( !(custom.intreqr & IF_RBF) ) + barrier(); + c = custom.serdatr; + /* clear the interrupt, so that another character can be read */ + custom.intreq = IF_RBF; + return c; +} + +int __debug_serinit( void ) +{ + unsigned long flags; + + save_flags (flags); + cli(); + + /* turn off Rx and Tx interrupts */ + custom.intena = IF_RBF | IF_TBE; + + /* clear any pending interrupt */ + custom.intreq = IF_RBF | IF_TBE; + + restore_flags (flags); + + /* + * set the appropriate directions for the modem control flags, + * and clear RTS and DTR + */ + ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ + ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ + +#ifdef CONFIG_KGDB + /* turn Rx interrupts on for GDB */ + custom.intena = IF_SETCLR | IF_RBF; + ser_RTSon(); +#endif + + return 0; +} + +void __debug_print_hex(unsigned long x) +{ + int i; + char hexchars[] = "0123456789ABCDEF"; + + for (i = 0; i < 8; i++) { + __debug_ser_out(hexchars[(x >> 28) & 15]); + x <<= 4; + } + __debug_ser_out('\n'); + __debug_ser_out('\r'); +} + +void __debug_print_string(char* s) +{ + unsigned char c; + while((c = *s++)) + __debug_ser_out(c); + __debug_ser_out('\n'); + __debug_ser_out('\r'); +} + +static void apus_progress(char *s, unsigned short value) +{ + __debug_print_string(s); +} + +/****************************************************** init */ + +/* The number of spurious interrupts */ +volatile unsigned int num_spurious; + +extern struct irqaction amiga_sys_irqaction[AUTO_IRQS]; + + +extern void amiga_enable_irq(unsigned int irq); +extern void amiga_disable_irq(unsigned int irq); + +struct hw_interrupt_type amiga_sys_irqctrl = { + typename: "Amiga IPL", + end: apus_end_irq, +}; + +struct hw_interrupt_type amiga_irqctrl = { + typename: "Amiga ", + enable: amiga_enable_irq, + disable: amiga_disable_irq, +}; + +#define HARDWARE_MAPPED_SIZE (512*1024) +unsigned long __init apus_find_end_of_memory(void) +{ + int shadow = 0; + unsigned long total; + + /* The memory size reported by ADOS excludes the 512KB + reserved for PPC exception registers and possibly 512KB + containing a shadow of the ADOS ROM. */ + { + unsigned long size = memory[0].size; + + /* If 2MB aligned, size was probably user + specified. We can't tell anything about shadowing + in this case so skip shadow assignment. */ + if (0 != (size & 0x1fffff)){ + /* Align to 512KB to ensure correct handling + of both memfile and system specified + sizes. */ + size = ((size+0x0007ffff) & 0xfff80000); + /* If memory is 1MB aligned, assume + shadowing. */ + shadow = !(size & 0x80000); + } + + /* Add the chunk that ADOS does not see. by aligning + the size to the nearest 2MB limit upwards. */ + memory[0].size = ((size+0x001fffff) & 0xffe00000); + } + + ppc_memstart = memory[0].addr; + ppc_memoffset = PAGE_OFFSET - PPC_MEMSTART; + total = memory[0].size; + + /* Remove the memory chunks that are controlled by special + Phase5 hardware. */ + + /* Remove the upper 512KB if it contains a shadow of + the ADOS ROM. FIXME: It might be possible to + disable this shadow HW. Check the booter + (ppc_boot.c) */ + if (shadow) + total -= HARDWARE_MAPPED_SIZE; + + /* Remove the upper 512KB where the PPC exception + vectors are mapped. */ + total -= HARDWARE_MAPPED_SIZE; + + /* Linux/APUS only handles one block of memory -- the one on + the PowerUP board. Other system memory is horrible slow in + comparison. The user can use other memory for swapping + using the z2ram device. */ + return total; +} + +static void __init +apus_map_io(void) +{ + /* Map PPC exception vectors. */ + io_block_mapping(0xfff00000, 0xfff00000, 0x00020000, _PAGE_KERNEL); + /* Map chip and ZorroII memory */ + io_block_mapping(zTwoBase, 0x00000000, 0x01000000, _PAGE_IO); +} + +__init +void apus_init_IRQ(void) +{ + struct irqaction *action; + int i; + +#ifdef CONFIG_PCI + apus_setup_pci_ptrs(); +#endif + + for ( i = 0 ; i < AMI_IRQS; i++ ) { + irq_desc[i].status = IRQ_LEVEL; + if (i < IRQ_AMIGA_AUTO) { + irq_desc[i].handler = &amiga_irqctrl; + } else { + irq_desc[i].handler = &amiga_sys_irqctrl; + action = &amiga_sys_irqaction[i-IRQ_AMIGA_AUTO]; + if (action->name) + setup_irq(i, action); + } + } + + amiga_init_IRQ(); + +} + +__init +void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + extern int parse_bootinfo(const struct bi_record *); + extern char _end[]; + + /* Parse bootinfo. The bootinfo is located right after + the kernel bss */ + parse_bootinfo((const struct bi_record *)&_end); +#ifdef CONFIG_BLK_DEV_INITRD + /* Take care of initrd if we have one. Use data from + bootinfo to avoid the need to initialize PPC + registers when kernel is booted via a PPC reset. */ + if ( ramdisk.addr ) { + initrd_start = (unsigned long) __va(ramdisk.addr); + initrd_end = (unsigned long) + __va(ramdisk.size + ramdisk.addr); + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + ISA_DMA_THRESHOLD = 0x00ffffff; + + ppc_md.setup_arch = apus_setup_arch; + ppc_md.show_cpuinfo = apus_show_cpuinfo; + ppc_md.init_IRQ = apus_init_IRQ; + ppc_md.get_irq = apus_get_irq; + +#ifdef CONFIG_HEARTBEAT + ppc_md.heartbeat = apus_heartbeat; + ppc_md.heartbeat_count = 1; +#endif +#ifdef APUS_DEBUG + __debug_serinit(); + ppc_md.progress = apus_progress; +#endif + ppc_md.init = NULL; + + ppc_md.restart = apus_restart; + ppc_md.power_off = apus_power_off; + ppc_md.halt = apus_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = apus_set_rtc_time; + ppc_md.get_rtc_time = apus_get_rtc_time; + ppc_md.calibrate_decr = apus_calibrate_decr; + + ppc_md.find_end_of_memory = apus_find_end_of_memory; + ppc_md.setup_io_mappings = apus_map_io; + + /* These should not be used for the APUS yet, since it uses + the M68K keyboard now. */ + ppc_md.kbd_setkeycode = apus_kbd_setkeycode; + ppc_md.kbd_getkeycode = apus_kbd_getkeycode; + ppc_md.kbd_translate = amiga_kbd_translate; + ppc_md.kbd_unexpected_up = apus_kbd_unexpected_up; + ppc_md.kbd_init_hw = apus_kbd_init_hw; +#ifdef CONFIG_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = amiga_sysrq_xlate; + SYSRQ_KEY = 0xff; +#endif +} diff -Nru a/arch/ppc/platforms/ash.c b/arch/ppc/platforms/ash.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ash.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,108 @@ +/* + * + * Copyright 2001 MontaVista Software Inc. + * + * IBM NP405H ash eval board + * + */ +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_PPC_RTC +#include +#endif + +void *ash_rtc_base; + +/* Some IRQs unique to Walnut. + * Used by the generic 405 PCI setup functions in ppc4xx_pci.c + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 1 - PCI slot 1 */ + {29, 29, 29, 29}, /* IDSEL 2 - PCI slot 2 */ + {30, 30, 30, 30}, /* IDSEL 3 - PCI slot 3 */ + {31, 31, 31, 31}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +board_setup_arch(void) +{ + + bd_t *bip = (bd_t *)__res; + +#ifdef CONFIG_PPC_RTC + /* RTC step for the walnut */ + ash_rtc_base = (void *) ASH_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, ash_rtc_base, ash_rtc_base,ash_rtc_base, 8); +#endif /* CONFIG_PPC_RTC */ +#define CONFIG_DEBUG_BRINGUP +#ifdef CONFIG_DEBUG_BRINGUP + printk("\n"); + printk("machine\t: %s\n", PPC4xx_MACHINE_NAME); + printk("\n"); + printk("bi_s_version\t %s\n", bip->bi_s_version); + printk("bi_r_version\t %s\n", bip->bi_r_version); + printk("bi_memsize\t 0x%8.8x\t %dMBytes\n", bip->bi_memsize,bip->bi_memsize/(1024*1000)); + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 0, + bip->bi_enetaddr[0][0], bip->bi_enetaddr[0][1], + bip->bi_enetaddr[0][2], bip->bi_enetaddr[0][3], + bip->bi_enetaddr[0][4], bip->bi_enetaddr[0][5]); + + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 1, + bip->bi_enetaddr[1][0], bip->bi_enetaddr[1][1], + bip->bi_enetaddr[1][2], bip->bi_enetaddr[1][3], + bip->bi_enetaddr[1][4], bip->bi_enetaddr[1][5]); + + printk("bi_intfreq\t 0x%8.8x\t clock:\t %dMhz\n", + bip->bi_intfreq, bip->bi_intfreq/ 1000000); + + printk("bi_busfreq\t 0x%8.8x\t plb bus clock:\t %dMHz\n", + bip->bi_busfreq, bip->bi_busfreq / 1000000 ); + printk("bi_pci_busfreq\t 0x%8.8x\t pci bus clock:\t %dMHz\n", + bip->bi_pci_busfreq, bip->bi_pci_busfreq/1000000); + + printk("\n"); +#endif +} + +void __init +board_io_mapping(void) +{ + io_block_mapping(ASH_RTC_VADDR, + ASH_RTC_PADDR, ASH_RTC_SIZE, _PAGE_IO); +} +void __init +board_setup_irq(void) +{ + +} + +void __init +board_init(void) +{ +#ifdef CONFIG_PPC_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +} diff -Nru a/arch/ppc/platforms/ash.h b/arch/ppc/platforms/ash.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ash.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,86 @@ +/* + * + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * akuster@mvista.com or source@mvista.com + * + * Module name: ash.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * Ash eval board. + * + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_ASH_H__ +#define __ASM_ASH_H__ +#include +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Ash" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[EMAC_NUMS][6]; /* Local Ethernet MAC address */ + unsigned char bi_pci_enetaddr[6]; + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI speed in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +/* Memory map for the IBM "Ash" NP405H evaluation board. + */ + +extern void *ash_rtc_base; +#define ASH_RTC_PADDR ((uint)0xf0000000) +#define ASH_RTC_VADDR ASH_RTC_PADDR +#define ASH_RTC_SIZE ((uint)8*1024) + + +/* Early initialization address mapping for block_io. + * Standard 405GP map. + */ +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +#define NR_BOARD_IRQS 32 + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define PPC4xx_MACHINE_NAME "IBM NP405H Ceder" + +extern char pci_irq_table[][4]; + + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_ASH_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/bseip.h b/arch/ppc/platforms/bseip.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/bseip.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,42 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ + +/* + * A collection of structures, addresses, and values associated with + * the Bright Star Engineering ip-Engine board. Copied from the MBX stuff. + * + * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) + */ +#ifndef __MACH_BSEIP_DEFS +#define __MACH_BSEIP_DEFS + +#ifndef __ASSEMBLY__ +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in Hz */ + unsigned char bi_enetaddr[6]; + unsigned int bi_baudrate; +} bd_t; + +extern bd_t m8xx_board_info; + +/* Memory map is configured by the PROM startup. + * All we need to get started is the IMMR. + */ +#define IMAP_ADDR ((uint)0xff000000) +#define IMAP_SIZE ((uint)(64 * 1024)) +#define PCMCIA_MEM_ADDR ((uint)0x04000000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) +#endif /* !__ASSEMBLY__ */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif diff -Nru a/arch/ppc/platforms/ccm.h b/arch/ppc/platforms/ccm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ccm.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,28 @@ +/* + * Siemens Card Controller Module specific definitions + * + * Copyright (c) 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_CCM_H +#define __MACH_CCM_H + +#include + +#include + +#define CCM_IMMR_BASE 0xF0000000 /* phys. addr of IMMR */ +#define CCM_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR CCM_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE CCM_IMAP_SIZE /* mapped size of IMMR area */ + +#define FEC_INTERRUPT 15 /* = SIU_LEVEL7 */ +#define DEC_INTERRUPT 13 /* = SIU_LEVEL6 */ +#define CPM_INTERRUPT 11 /* = SIU_LEVEL5 (was: SIU_LEVEL2) */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_CCM_H */ diff -Nru a/arch/ppc/platforms/ceder.c b/arch/ppc/platforms/ceder.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ceder.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,83 @@ +/* + * + * Copyright 2001 MontaVista Software Inc. + * + * IBM NP405L ceder eval board + * + */ +#include +#include +#include +#include + +#include + +#ifdef CONFIG_PPC_RTC +#include +#endif + +void *ceder_rtc_base; + +void __init +board_setup_arch(void) +{ + + bd_t *bip = (bd_t *)__res; + +#ifdef CONFIG_PPC_RTC + /* RTC step for the walnut */ + ceder_rtc_base = (void *) CEDER_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, ceder_rtc_base, ceder_rtc_base,ceder_rtc_base, 8); +#endif /* CONFIG_PPC_RTC */ +#define CONFIG_DEBUG_BRINGUP +#ifdef CONFIG_DEBUG_BRINGUP + printk("\n"); + printk("machine\t: %s\n", PPC4xx_MACHINE_NAME); + printk("\n"); + printk("bi_s_version\t %s\n", bip->bi_s_version); + printk("bi_r_version\t %s\n", bip->bi_r_version); + printk("bi_memsize\t 0x%8.8x\t %dMBytes\n", bip->bi_memsize,bip->bi_memsize/(1024*1000)); + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 0, + bip->bi_enetaddr[0][0], bip->bi_enetaddr[0][1], + bip->bi_enetaddr[0][2], bip->bi_enetaddr[0][3], + bip->bi_enetaddr[0][4], bip->bi_enetaddr[0][5]); + + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 1, + bip->bi_enetaddr[1][0], bip->bi_enetaddr[1][1], + bip->bi_enetaddr[1][2], bip->bi_enetaddr[1][3], + bip->bi_enetaddr[1][4], bip->bi_enetaddr[1][5]); + + printk("bi_intfreq\t 0x%8.8x\t clock:\t %dMhz\n", + bip->bi_intfreq, bip->bi_intfreq/ 1000000); + + printk("bi_busfreq\t 0x%8.8x\t plb bus clock:\t %dMHz\n", + bip->bi_busfreq, bip->bi_busfreq / 1000000 ); + printk("bi_pci_busfreq\t 0x%8.8x\t pci bus clock:\t %dMHz\n", + bip->bi_pci_busfreq, bip->bi_pci_busfreq/1000000); + + printk("\n"); +#endif +} + +void __init +board_io_mapping(void) +{ + io_block_mapping(CEDER_RTC_VADDR, + CEDER_RTC_PADDR, CEDER_RTC_SIZE, _PAGE_IO); +} +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ +#ifdef CONFIG_PPC_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +} diff -Nru a/arch/ppc/platforms/ceder.h b/arch/ppc/platforms/ceder.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ceder.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,85 @@ +/* + * + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * akuster@mvista.com or source@mvista.com + * + * Module name: ceder.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * Ceder eval board. + * + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_CEDER_H__ +#define __ASM_CEDER_H__ +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Ceder" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[EMAC_NUMS][6]; /* Local Ethernet MAC address */ + unsigned char bi_pci_mac[6]; + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI speed in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +/* Memory map for the IBM "Ceder" NP405 evaluation board. + */ + +extern void *ceder_rtc_base; +#define CEDER_RTC_PADDR ((uint)0xf0000000) +#define CEDER_RTC_VADDR CEDER_RTC_PADDR +#define CEDER_RTC_SIZE ((uint)8*1024) + + +/* Early initialization address mapping for block_io. + * Standard 405GP map. + */ +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +#define NR_BOARD_IRQS 32 + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define PPC4xx_MACHINE_NAME "IBM NP405L Ceder" + +extern char pci_irq_table[][4]; + + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_CEDER_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/chrp_pci.c b/arch/ppc/platforms/chrp_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/chrp_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,312 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * CHRP pci routines. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* LongTrail */ +unsigned long gg2_pci_config_base; + +#define pci_config_addr(dev, offset) \ +(gg2_pci_config_base | ((dev->bus->number)<<16) | ((dev->devfn)<<8) | (offset)) + +volatile struct Hydra *Hydra = NULL; + +/* + * The VLSI Golden Gate II has only 512K of PCI configuration space, so we + * limit the bus number to 3 bits + */ + +#define cfg_read(val, addr, type, op) *val = op((type)(addr)) +#define cfg_write(val, addr, type, op) op((type *)(addr), (val)) + +#define cfg_read_bad(val, size) *val = bad_##size; +#define cfg_write_bad(val, size) + +#define bad_byte 0xff +#define bad_word 0xffff +#define bad_dword 0xffffffffU + +#define GG2_PCI_OP(rw, size, type, op) \ +int __chrp gg2_##rw##_config_##size(struct pci_dev *dev, int off, type val) \ +{ \ + if (dev->bus->number > 7) { \ + cfg_##rw##_bad(val, size) \ + return PCIBIOS_DEVICE_NOT_FOUND; \ + } \ + cfg_##rw(val, pci_config_addr(dev, off), type, op); \ + return PCIBIOS_SUCCESSFUL; \ +} + +GG2_PCI_OP(read, byte, u8 *, in_8) +GG2_PCI_OP(read, word, u16 *, in_le16) +GG2_PCI_OP(read, dword, u32 *, in_le32) +GG2_PCI_OP(write, byte, u8, out_8) +GG2_PCI_OP(write, word, u16, out_le16) +GG2_PCI_OP(write, dword, u32, out_le32) + +static struct pci_ops gg2_pci_ops = +{ + gg2_read_config_byte, + gg2_read_config_word, + gg2_read_config_dword, + gg2_write_config_byte, + gg2_write_config_word, + gg2_write_config_dword +}; + +/* + * Access functions for PCI config space on IBM "python" host bridges. + */ +#define PYTHON_CFA(b, d, o) (0x80 | ((b) << 8) | ((d) << 16) \ + | (((o) & ~3) << 24)) + +#define PYTHON_PCI_OP(rw, size, type, op, mask) \ +int __chrp \ +python_##rw##_config_##size(struct pci_dev *dev, int offset, type val) \ +{ \ + struct pci_controller *hose = dev->sysdata; \ + \ + out_be32(hose->cfg_addr, \ + PYTHON_CFA(dev->bus->number, dev->devfn, offset)); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ + return PCIBIOS_SUCCESSFUL; \ +} + +PYTHON_PCI_OP(read, byte, u8 *, in_8, 3) +PYTHON_PCI_OP(read, word, u16 *, in_le16, 2) +PYTHON_PCI_OP(read, dword, u32 *, in_le32, 0) +PYTHON_PCI_OP(write, byte, u8, out_8, 3) +PYTHON_PCI_OP(write, word, u16, out_le16, 2) +PYTHON_PCI_OP(write, dword, u32, out_le32, 0) + +static struct pci_ops python_pci_ops = +{ + python_read_config_byte, + python_read_config_word, + python_read_config_dword, + python_write_config_byte, + python_write_config_word, + python_write_config_dword +}; + +/* + * Access functions for PCI config space using RTAS calls. + */ +#define RTAS_PCI_READ_OP(size, type, nbytes) \ +int __chrp \ +rtas_read_config_##size(struct pci_dev *dev, int offset, type val) \ +{ \ + unsigned long addr = (offset & 0xff) | ((dev->devfn & 0xff) << 8) \ + | ((dev->bus->number & 0xff) << 16); \ + unsigned long ret = ~0UL; \ + int rval; \ + \ + rval = call_rtas("read-pci-config", 2, 2, &ret, addr, nbytes); \ + *val = ret; \ + return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; \ +} + +#define RTAS_PCI_WRITE_OP(size, type, nbytes) \ +int __chrp \ +rtas_write_config_##size(struct pci_dev *dev, int offset, type val) \ +{ \ + unsigned long addr = (offset & 0xff) | ((dev->devfn & 0xff) << 8) \ + | ((dev->bus->number & 0xff) << 16); \ + int rval; \ + \ + rval = call_rtas("write-pci-config", 3, 1, NULL, \ + addr, nbytes, (ulong)val); \ + return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; \ +} + +RTAS_PCI_READ_OP(byte, u8 *, 1) +RTAS_PCI_READ_OP(word, u16 *, 2) +RTAS_PCI_READ_OP(dword, u32 *, 4) +RTAS_PCI_WRITE_OP(byte, u8, 1) +RTAS_PCI_WRITE_OP(word, u16, 2) +RTAS_PCI_WRITE_OP(dword, u32, 4) + +static struct pci_ops rtas_pci_ops = +{ + rtas_read_config_byte, + rtas_read_config_word, + rtas_read_config_dword, + rtas_write_config_byte, + rtas_write_config_word, + rtas_write_config_dword +}; + +int __init +hydra_init(void) +{ + struct device_node *np; + + np = find_devices("mac-io"); + if (np == NULL || np->n_addrs == 0) + return 0; + Hydra = ioremap(np->addrs[0].address, np->addrs[0].size); + printk("Hydra Mac I/O at %x\n", np->addrs[0].address); + printk("Hydra Feature_Control was %x", + in_le32(&Hydra->Feature_Control)); + out_le32(&Hydra->Feature_Control, (HYDRA_FC_SCC_CELL_EN | + HYDRA_FC_SCSI_CELL_EN | + HYDRA_FC_SCCA_ENABLE | + HYDRA_FC_SCCB_ENABLE | + HYDRA_FC_ARB_BYPASS | + HYDRA_FC_MPIC_ENABLE | + HYDRA_FC_SLOW_SCC_PCLK | + HYDRA_FC_MPIC_IS_MASTER)); + printk(", now %x\n", in_le32(&Hydra->Feature_Control)); + return 1; +} + +void __init +chrp_pcibios_fixup(void) +{ + struct pci_dev *dev; + struct device_node *np; + + /* PCI interrupts are controlled by the OpenPIC */ + pci_for_each_dev(dev) { + np = pci_device_to_OF_node(dev); + if ((np != 0) && (np->n_intrs > 0) && (np->intrs[0].line != 0)) + dev->irq = np->intrs[0].line; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } +} + +#define PRG_CL_RESET_VALID 0x00010000 + +static void __init +setup_python(struct pci_controller *hose, struct device_node *dev) +{ + u32 *reg, val; + volatile unsigned char *cfg; + + hose->ops = &python_pci_ops; + cfg = ioremap(dev->addrs[0].address + 0xf8000, 0x20); + hose->cfg_addr = (volatile unsigned int *) cfg; + hose->cfg_data = cfg + 0x10; + + /* Clear the magic go-slow bit */ + reg = (u32 *) ioremap(dev->addrs[0].address + 0xf6000, 0x40); + val = in_be32(®[12]); + if (val & PRG_CL_RESET_VALID) { + out_be32(®[12], val & ~PRG_CL_RESET_VALID); + in_be32(®[12]); + } + iounmap(reg); +} + +void __init +chrp_find_bridges(void) +{ + struct device_node *dev; + int *bus_range; + int len, index = -1; + struct pci_controller *hose; + unsigned int *dma; + char *model, *machine; + int is_longtrail = 0, is_mot = 0; + struct device_node *root = find_path_device("/"); + + /* + * The PCI host bridge nodes on some machines don't have + * properties to adequately identify them, so we have to + * look at what sort of machine this is as well. + */ + machine = get_property(root, "model", NULL); + if (machine != NULL) { + is_longtrail = strncmp(machine, "IBM,LongTrail", 13) == 0; + is_mot = strncmp(machine, "MOT", 3) == 0; + } + for (dev = root->child; dev != NULL; dev = dev->sibling) { + if (dev->type == NULL || strcmp(dev->type, "pci") != 0) + continue; + ++index; + /* The GG2 bridge on the LongTrail doesn't have an address */ + if (dev->n_addrs < 1 && !is_longtrail) { + printk(KERN_WARNING "Can't use %s: no address\n", + dev->full_name); + continue; + } + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s\n", + dev->full_name); + continue; + } + if (bus_range[1] == bus_range[0]) + printk(KERN_INFO "PCI bus %d", bus_range[0]); + else + printk(KERN_INFO "PCI buses %d..%d", + bus_range[0], bus_range[1]); + printk(" controlled by %s", dev->type); + if (dev->n_addrs > 0) + printk(" at %x", dev->addrs[0].address); + printk("\n"); + + hose = pcibios_alloc_controller(); + if (!hose) { + printk("Can't allocate PCI controller structure for %s\n", + dev->full_name); + continue; + } + hose->arch_data = dev; + hose->first_busno = bus_range[0]; + hose->last_busno = bus_range[1]; + + model = get_property(dev, "model", NULL); + if (model == NULL) + model = ""; + if (device_is_compatible(dev, "IBM,python")) { + setup_python(hose, dev); + } else if (is_mot + || strncmp(model, "Motorola, Grackle", 17) == 0) { + setup_grackle(hose); + } else if (is_longtrail) { + hose->ops = &gg2_pci_ops; + gg2_pci_config_base = (unsigned long) + ioremap(GG2_PCI_CONFIG_BASE, 0x80000); + } else { + printk("No methods for %s (model %s), using RTAS\n", + dev->full_name, model); + hose->ops = &rtas_pci_ops; + } + + pci_process_bridge_OF_ranges(hose, dev, index == 0); + + /* check the first bridge for a property that we can + use to set pci_dram_offset */ + dma = (unsigned int *) + get_property(dev, "ibm,dma-ranges", &len); + if (index == 0 && dma != NULL && len >= 6 * sizeof(*dma)) { + pci_dram_offset = dma[2] - dma[3]; + printk("pci_dram_offset = %lx\n", pci_dram_offset); + } + } + + ppc_md.pcibios_fixup = chrp_pcibios_fixup; +} diff -Nru a/arch/ppc/platforms/chrp_setup.c b/arch/ppc/platforms/chrp_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/chrp_setup.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,653 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/platforms/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + */ + +/* + * bootup setup stuff.. + */ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned long chrp_get_rtc_time(void); +int chrp_set_rtc_time(unsigned long nowtime); +void chrp_calibrate_decr(void); +long chrp_time_init(void); + +void chrp_find_bridges(void); +void chrp_event_scan(void); +void rtas_display_progress(char *, unsigned short); +void rtas_indicator_progress(char *, unsigned short); +void btext_progress(char *, unsigned short); + +extern unsigned long pmac_find_end_of_memory(void); +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; +extern void select_adb_keyboard(void); +extern int of_show_percpuinfo(struct seq_file *, int); + +extern kdev_t boot_dev; + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern int probingmem; +extern unsigned long loops_per_jiffy; +static int max_width; + +#ifdef CONFIG_SMP +extern struct smp_ops_t chrp_smp_ops; +#endif + +static const char *gg2_memtypes[4] = { + "FPM", "SDRAM", "EDO", "BEDO" +}; +static const char *gg2_cachesizes[4] = { + "256 KB", "512 KB", "1 MB", "Reserved" +}; +static const char *gg2_cachetypes[4] = { + "Asynchronous", "Reserved", "Flow-Through Synchronous", + "Pipelined Synchronous" +}; +static const char *gg2_cachemodes[4] = { + "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" +}; + +int __chrp +chrp_show_cpuinfo(struct seq_file *m) +{ + int i, sdramen; + unsigned int t; + struct device_node *root; + const char *model = ""; + + root = find_path_device("/"); + if (root) + model = get_property(root, "model", NULL); + seq_printf(m, "machine\t\t: CHRP %s\n", model); + + /* longtrail (goldengate) stuff */ + if (!strncmp(model, "IBM,LongTrail", 13)) { + /* VLSI VAS96011/12 `Golden Gate 2' */ + /* Memory banks */ + sdramen = (in_le32((unsigned *)(gg2_pci_config_base+ + GG2_PCI_DRAM_CTRL)) + >>31) & 1; + for (i = 0; i < (sdramen ? 4 : 6); i++) { + t = in_le32((unsigned *)(gg2_pci_config_base+ + GG2_PCI_DRAM_BANK0+ + i*4)); + if (!(t & 1)) + continue; + switch ((t>>8) & 0x1f) { + case 0x1f: + model = "4 MB"; + break; + case 0x1e: + model = "8 MB"; + break; + case 0x1c: + model = "16 MB"; + break; + case 0x18: + model = "32 MB"; + break; + case 0x10: + model = "64 MB"; + break; + case 0x00: + model = "128 MB"; + break; + default: + model = "Reserved"; + break; + } + seq_printf(m, "memory bank %d\t: %s %s\n", i, model, + gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); + } + /* L2 cache */ + t = in_le32((unsigned *)(gg2_pci_config_base+GG2_PCI_CC_CTRL)); + seq_printf(m, "board l2\t: %s %s (%s)\n", + gg2_cachesizes[(t>>7) & 3], + gg2_cachetypes[(t>>2) & 3], + gg2_cachemodes[t & 3]); + } + return 0; +} + +/* + * Fixes for the National Semiconductor PC78308VUL SuperI/O + * + * Some versions of Open Firmware incorrectly initialize the IRQ settings + * for keyboard and mouse + */ +static inline void __init sio_write(u8 val, u8 index) +{ + outb(index, 0x15c); + outb(val, 0x15d); +} + +static inline u8 __init sio_read(u8 index) +{ + outb(index, 0x15c); + return inb(0x15d); +} + +static void __init sio_fixup_irq(const char *name, u8 device, u8 level, + u8 type) +{ + u8 level0, type0, active; + + /* select logical device */ + sio_write(device, 0x07); + active = sio_read(0x30); + level0 = sio_read(0x70); + type0 = sio_read(0x71); + if (level0 != level || type0 != type || !active) { + printk(KERN_WARNING "sio: %s irq level %d, type %d, %sactive: " + "remapping to level %d, type %d, active\n", + name, level0, type0, !active ? "in" : "", level, type); + sio_write(0x01, 0x30); + sio_write(level, 0x70); + sio_write(type, 0x71); + } +} + +static void __init sio_init(void) +{ + struct device_node *root; + + if ((root = find_path_device("/")) && + !strncmp(get_property(root, "model", NULL), "IBM,LongTrail", 13)) { + /* logical device 0 (KBC/Keyboard) */ + sio_fixup_irq("keyboard", 0, 1, 2); + /* select logical device 1 (KBC/Mouse) */ + sio_fixup_irq("mouse", 1, 12, 2); + } +} + + +void __init +chrp_setup_arch(void) +{ + struct device_node *device; + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + /* this is fine for chrp */ + initrd_below_start_ok = 1; + + if (initrd_start) + ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); + else +#endif + ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */ + + /* Lookup PCI host bridges */ + chrp_find_bridges(); + +#ifndef CONFIG_PPC64BRIDGE + /* + * Temporary fixes for PCI devices. + * -- Geert + */ + hydra_init(); /* Mac I/O */ + +#endif /* CONFIG_PPC64BRIDGE */ + + /* + * Fix the Super I/O configuration + */ + sio_init(); + + /* + * Setup the console operations + */ +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + /* Get the event scan rate for the rtas so we know how + * often it expects a heartbeat. -- Cort + */ + if ( rtas_data ) { + struct property *p; + device = find_devices("rtas"); + for ( p = device->properties; + p && strncmp(p->name, "rtas-event-scan-rate", 20); + p = p->next ) + /* nothing */ ; + if ( p && *(unsigned long *)p->value ) { + ppc_md.heartbeat = chrp_event_scan; + ppc_md.heartbeat_reset = (HZ/(*(unsigned long *)p->value)*30)-1; + ppc_md.heartbeat_count = 1; + printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", + *(unsigned long *)p->value, ppc_md.heartbeat_reset ); + } + } + + pci_create_OF_bus_map(); +} + +void __chrp +chrp_event_scan(void) +{ + unsigned char log[1024]; + unsigned long ret = 0; + /* XXX: we should loop until the hardware says no more error logs -- Cort */ + call_rtas( "event-scan", 4, 1, &ret, 0xffffffff, 0, + __pa(log), 1024 ); + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; +} + +void __chrp +chrp_restart(char *cmd) +{ + printk("RTAS system-reboot returned %d\n", + call_rtas("system-reboot", 0, 1, NULL)); + for (;;); +} + +void __chrp +chrp_power_off(void) +{ + /* allow power on only with power button press */ + printk("RTAS power-off returned %d\n", + call_rtas("power-off", 2, 1, NULL,0xffffffff,0xffffffff)); + for (;;); +} + +void __chrp +chrp_halt(void) +{ + chrp_power_off(); +} + +u_int __chrp +chrp_irq_cannonicalize(u_int irq) +{ + if (irq == 2) + return 9; + return irq; +} + +/* + * Finds the open-pic node and sets OpenPIC_Addr based on its reg property. + * Then checks if it has an interrupt-ranges property. If it does then + * we have a distributed open-pic, so call openpic_set_sources to tell + * the openpic code where to find the interrupt source registers. + */ +static void __init chrp_find_openpic(void) +{ + struct device_node *np; + int len, i; + unsigned int *iranges; + void *isu; + + np = find_type_devices("open-pic"); + if (np == NULL || np->n_addrs == 0) + return; + printk(KERN_INFO "OpenPIC at %x (size %x)\n", + np->addrs[0].address, np->addrs[0].size); + OpenPIC_Addr = ioremap(np->addrs[0].address, 0x40000); + if (OpenPIC_Addr == NULL) { + printk(KERN_ERR "Failed to map OpenPIC!\n"); + return; + } + + iranges = (unsigned int *) get_property(np, "interrupt-ranges", &len); + if (iranges == NULL || len < 2 * sizeof(unsigned int)) + return; /* not distributed */ + + /* + * The first pair of cells in interrupt-ranges refers to the + * IDU; subsequent pairs refer to the ISUs. + */ + len /= 2 * sizeof(unsigned int); + if (np->n_addrs < len) { + printk(KERN_ERR "Insufficient addresses for distributed" + " OpenPIC (%d < %d)\n", np->n_addrs, len); + return; + } + if (iranges[1] != 0) { + printk(KERN_INFO "OpenPIC irqs %d..%d in IDU\n", + iranges[0], iranges[0] + iranges[1] - 1); + openpic_set_sources(iranges[0], iranges[1], NULL); + } + for (i = 1; i < len; ++i) { + iranges += 2; + printk(KERN_INFO "OpenPIC irqs %d..%d in ISU at %x (%x)\n", + iranges[0], iranges[0] + iranges[1] - 1, + np->addrs[i].address, np->addrs[i].size); + isu = ioremap(np->addrs[i].address, np->addrs[i].size); + if (isu != NULL) + openpic_set_sources(iranges[0], iranges[1], isu); + else + printk(KERN_ERR "Failed to map OpenPIC ISU at %x!\n", + np->addrs[i].address); + } +} + +void __init chrp_init_IRQ(void) +{ + struct device_node *np; + int i; + unsigned char* chrp_int_ack_special = 0; + unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS]; + int nmi_irq = -1; +#if defined(CONFIG_VT) && defined(CONFIG_ADB_KEYBOARD) && defined(XMON) + struct device_node *kbd; +#endif + + for (np = find_devices("pci"); np != NULL; np = np->next) { + unsigned int *addrp = (unsigned int *) + get_property(np, "8259-interrupt-acknowledge", NULL); + if (addrp == NULL) + continue; + chrp_int_ack_special = (unsigned char *) + ioremap(addrp[prom_n_addr_cells(np)-1], 1); + break; + } + if (np == NULL) + printk("Cannot find pci to get ack address\n"); + + chrp_find_openpic(); + + prom_get_irq_senses(init_senses, NUM_8259_INTERRUPTS, NR_IRQS); + OpenPIC_InitSenses = init_senses; + OpenPIC_NumInitSenses = NR_IRQS - NUM_8259_INTERRUPTS; + + openpic_init(1, NUM_8259_INTERRUPTS, chrp_int_ack_special, nmi_irq); + + for (i = 0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + i8259_init(0); + +#if defined(CONFIG_VT) && defined(CONFIG_ADB_KEYBOARD) && defined(XMON) + /* see if there is a keyboard in the device tree + with a parent of type "adb" */ + for (kbd = find_devices("keyboard"); kbd; kbd = kbd->next) + if (kbd->parent && kbd->parent->type + && strcmp(kbd->parent->type, "adb") == 0) + break; + if (kbd) + request_irq(HYDRA_INT_ADB_NMI, xmon_irq, 0, "XMON break", 0); +#endif +} + +void __init +chrp_init2(void) +{ +#ifdef CONFIG_NVRAM + pmac_nvram_init(); +#endif + + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); + + if (ppc_md.progress) + ppc_md.progress(" Have fun! ", 0x7777); + +#if defined(CONFIG_VT) && (defined(CONFIG_ADB_KEYBOARD) || defined(CONFIG_INPUT)) + /* see if there is a keyboard in the device tree + with a parent of type "adb" */ + { + struct device_node *kbd; + + for (kbd = find_devices("keyboard"); kbd; kbd = kbd->next) { + if (kbd->parent && kbd->parent->type + && strcmp(kbd->parent->type, "adb") == 0) { + select_adb_keyboard(); + break; + } + } + } +#endif /* CONFIG_VT && (CONFIG_ADB_KEYBOARD || CONFIG_INPUT) */ +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ + +static int __chrp +chrp_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +static void __chrp +chrp_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); +} + +static void __chrp +chrp_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); +} +#endif + +/* + * One of the main thing these mappings are needed for is so that + * xmon can get to the serial port early on. We probably should + * handle the machines with the mpc106 as well as the python (F50) + * and the GG2 (longtrail). Actually we should look in the device + * tree and do the right thing. + */ +static void __init +chrp_map_io(void) +{ + char *name; + + /* + * The code below tends to get removed, please don't take it out. + * The F50 needs this mapping and it you take it out I'll track you + * down and slap your hands. If it causes problems please email me. + * -- Cort + */ + name = get_property(find_path_device("/"), "name", NULL); + if (name && strncmp(name, "IBM-70", 6) == 0 + && strstr(name, "-F50")) { + io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); + io_block_mapping(0x90000000, 0x90000000, 0x10000000, _PAGE_IO); + return; + } else { + io_block_mapping(0xf8000000, 0xf8000000, 0x04000000, _PAGE_IO); + } +} + +void __init +chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r6 ) + { + initrd_start = r6 + KERNELBASE; + initrd_end = r6 + r7 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + ISA_DMA_THRESHOLD = ~0L; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + isa_io_base = CHRP_ISA_IO_BASE; /* default value */ + + ppc_md.setup_arch = chrp_setup_arch; + ppc_md.show_percpuinfo = of_show_percpuinfo; + ppc_md.show_cpuinfo = chrp_show_cpuinfo; + ppc_md.irq_cannonicalize = chrp_irq_cannonicalize; + ppc_md.init_IRQ = chrp_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.init = chrp_init2; + + ppc_md.restart = chrp_restart; + ppc_md.power_off = chrp_power_off; + ppc_md.halt = chrp_halt; + + ppc_md.time_init = chrp_time_init; + ppc_md.set_rtc_time = chrp_set_rtc_time; + ppc_md.get_rtc_time = chrp_get_rtc_time; + ppc_md.calibrate_decr = chrp_calibrate_decr; + + ppc_md.find_end_of_memory = pmac_find_end_of_memory; + ppc_md.setup_io_mappings = chrp_map_io; + +#ifdef CONFIG_VT + /* these are adjusted in chrp_init2 if we have an ADB keyboard */ + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x54; +#endif /* CONFIG_MAGIC_SYSRQ */ +#endif /* CONFIG_VT */ + + if (rtas_data) { + struct device_node *rtas; + unsigned int *p; + + rtas = find_devices("rtas"); + if (rtas != NULL) { + if (get_property(rtas, "display-character", NULL)) { + ppc_md.progress = rtas_display_progress; + p = (unsigned int *) get_property + (rtas, "ibm,display-line-length", NULL); + if (p) + max_width = *p; + } else if (get_property(rtas, "set-indicator", NULL)) + ppc_md.progress = rtas_indicator_progress; + } + } +#ifdef CONFIG_BOOTX_TEXT + if (ppc_md.progress == NULL && boot_text_mapped) + ppc_md.progress = btext_progress; +#endif + +#ifdef CONFIG_SMP + ppc_md.smp_ops = &chrp_smp_ops; +#endif /* CONFIG_SMP */ + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.ide_check_region = chrp_ide_check_region; + ppc_ide_md.ide_request_region = chrp_ide_request_region; + ppc_ide_md.ide_release_region = chrp_ide_release_region; +#endif + + /* + * Print the banner, then scroll down so boot progress + * can be printed. -- Cort + */ + if ( ppc_md.progress ) ppc_md.progress("Linux/PPC "UTS_RELEASE"\n", 0x0); +} + +void __chrp +rtas_display_progress(char *s, unsigned short hex) +{ + int width; + char *os = s; + + if ( call_rtas( "display-character", 1, 1, NULL, '\r' ) ) + return; + + width = max_width; + while ( *os ) + { + if ( (*os == '\n') || (*os == '\r') ) + width = max_width; + else + width--; + call_rtas( "display-character", 1, 1, NULL, *os++ ); + /* if we overwrite the screen length */ + if ( width == 0 ) + while ( (*os != 0) && (*os != '\n') && (*os != '\r') ) + os++; + } + + /*while ( width-- > 0 )*/ + call_rtas( "display-character", 1, 1, NULL, ' ' ); +} + +void __chrp +rtas_indicator_progress(char *s, unsigned short hex) +{ + call_rtas("set-indicator", 3, 1, NULL, 6, 0, hex); +} + +#ifdef CONFIG_BOOTX_TEXT +void +btext_progress(char *s, unsigned short hex) +{ + prom_print(s); + prom_print("\n"); +} +#endif /* CONFIG_BOOTX_TEXT */ diff -Nru a/arch/ppc/platforms/chrp_smp.c b/arch/ppc/platforms/chrp_smp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/chrp_smp.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,107 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * Smp support for CHRP machines. + * + * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great + * deal of code from the sparc and intel versions. + * + * Copyright (C) 1999 Cort Dougan + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern unsigned long smp_chrp_cpu_nr; + +static int __init +smp_chrp_probe(void) +{ + if (smp_chrp_cpu_nr > 1) + openpic_request_IPIs(); + + return smp_chrp_cpu_nr; +} + +static void __init +smp_chrp_kick_cpu(int nr) +{ + *(unsigned long *)KERNELBASE = nr; + asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); +} + +static void __init +smp_chrp_setup_cpu(int cpu_nr) +{ + static atomic_t ready = ATOMIC_INIT(1); + static volatile int frozen = 0; + + if (cpu_nr == 0) { + /* wait for all the others */ + while (atomic_read(&ready) < smp_num_cpus) + barrier(); + atomic_set(&ready, 1); + /* freeze the timebase */ + call_rtas("freeze-time-base", 0, 1, NULL); + mb(); + frozen = 1; + /* XXX assumes this is not a 601 */ + set_tb(0, 0); + last_jiffy_stamp(0) = 0; + while (atomic_read(&ready) < smp_num_cpus) + barrier(); + /* thaw the timebase again */ + call_rtas("thaw-time-base", 0, 1, NULL); + mb(); + frozen = 0; + smp_tb_synchronized = 1; + } else { + atomic_inc(&ready); + while (!frozen) + barrier(); + set_tb(0, 0); + last_jiffy_stamp(0) = 0; + mb(); + atomic_inc(&ready); + while (frozen) + barrier(); + } + + if (OpenPIC_Addr) + do_openpic_setup_cpu(); +} + +/* CHRP with openpic */ +struct smp_ops_t chrp_smp_ops __chrpdata = { + smp_openpic_message_pass, + smp_chrp_probe, + smp_chrp_kick_cpu, + smp_chrp_setup_cpu, +}; diff -Nru a/arch/ppc/platforms/chrp_time.c b/arch/ppc/platforms/chrp_time.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/chrp_time.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,195 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/platforms/chrp_time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * Adapted for PowerPC (PReP) by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu). + * Copied and modified from arch/i386/kernel/time.c + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern spinlock_t rtc_lock; + +static int nvram_as1 = NVRAM_AS1; +static int nvram_as0 = NVRAM_AS0; +static int nvram_data = NVRAM_DATA; + +long __init chrp_time_init(void) +{ + struct device_node *rtcs; + int base; + + rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); + if (rtcs == NULL || rtcs->addrs == NULL) + return 0; + base = rtcs->addrs[0].address; + nvram_as1 = 0; + nvram_as0 = base; + nvram_data = base + 1; + + return 0; +} + +int __chrp chrp_cmos_clock_read(int addr) +{ + if (nvram_as1 != 0) + outb(addr>>8, nvram_as1); + outb(addr, nvram_as0); + return (inb(nvram_data)); +} + +void __chrp chrp_cmos_clock_write(unsigned long val, int addr) +{ + if (nvram_as1 != 0) + outb(addr>>8, nvram_as1); + outb(addr, nvram_as0); + outb(val, nvram_data); + return; +} + +/* + * Set the hardware clock. -- Cort + */ +int __chrp chrp_set_rtc_time(unsigned long nowtime) +{ + unsigned char save_control, save_freq_select; + struct rtc_time tm; + + spin_lock(&rtc_lock); + to_tm(nowtime, &tm); + + save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ + + chrp_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = chrp_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ + + chrp_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year -= 1900; + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + } + chrp_cmos_clock_write(tm.tm_sec,RTC_SECONDS); + chrp_cmos_clock_write(tm.tm_min,RTC_MINUTES); + chrp_cmos_clock_write(tm.tm_hour,RTC_HOURS); + chrp_cmos_clock_write(tm.tm_mon,RTC_MONTH); + chrp_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); + chrp_cmos_clock_write(tm.tm_year,RTC_YEAR); + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + chrp_cmos_clock_write(save_control, RTC_CONTROL); + chrp_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); + + if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) + time_state = TIME_OK; + spin_unlock(&rtc_lock); + return 0; +} + +unsigned long __chrp chrp_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int uip, i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + + /* Since the UIP flag is set for about 2.2 ms and the clock + * is typically written with a precision of 1 jiffy, trying + * to obtain a precision better than a few milliseconds is + * an illusion. Only consistency is interesting, this also + * allows to use the routine for /dev/rtc without a potential + * 1 second kernel busy loop triggered by any reader of /dev/rtc. + */ + + for ( i = 0; i<1000000; i++) { + uip = chrp_cmos_clock_read(RTC_FREQ_SELECT); + sec = chrp_cmos_clock_read(RTC_SECONDS); + min = chrp_cmos_clock_read(RTC_MINUTES); + hour = chrp_cmos_clock_read(RTC_HOURS); + day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); + mon = chrp_cmos_clock_read(RTC_MONTH); + year = chrp_cmos_clock_read(RTC_YEAR); + uip |= chrp_cmos_clock_read(RTC_FREQ_SELECT); + if ((uip & RTC_UIP)==0) break; + } + + if (!(chrp_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + + +void __init chrp_calibrate_decr(void) +{ + struct device_node *cpu; + unsigned int freq, *fp; + + if (via_calibrate_decr()) + return; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + freq = 16666000; /* hardcoded default */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (unsigned int *) + get_property(cpu, "timebase-frequency", NULL); + if (fp != 0) + freq = *fp; + } + printk("time_init: decrementer frequency = %u.%.6u MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); +} diff -Nru a/arch/ppc/platforms/cpc700.h b/arch/ppc/platforms/cpc700.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/cpc700.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,109 @@ +/* + * include/asm-ppc/cpc700.h + * + * Header file for IBM CPC700 Host Bridge, et. al. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2000 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + */ + +/* + * This file contains the defines and macros for the IBM CPC700 host bridge, + * memory controller, PIC, UARTs, IIC, and Timers. + */ + +#ifndef _ASMPPC_CPC700_H +#define _ASMPPC_CPC700_H + +#include +#include +#include + +#define CPC700_OUT_32(a,d) (*(u_int *)a = d) +#define CPC700_IN_32(a) (*(u_int *)a) + +/* + * PCI Section + */ +#define CPC700_PCI_CONFIG_ADDR 0xfec00000 +#define CPC700_PCI_CONFIG_DATA 0xfec00004 + +#define CPC700_PMM0_LOCAL 0xff400000 +#define CPC700_PMM0_MASK_ATTR 0xff400004 +#define CPC700_PMM0_PCI_LOW 0xff400008 +#define CPC700_PMM0_PCI_HIGH 0xff40000c +#define CPC700_PMM1_LOCAL 0xff400010 +#define CPC700_PMM1_MASK_ATTR 0xff400014 +#define CPC700_PMM1_PCI_LOW 0xff400018 +#define CPC700_PMM1_PCI_HIGH 0xff40001c +#define CPC700_PMM2_LOCAL 0xff400020 +#define CPC700_PMM2_MASK_ATTR 0xff400024 +#define CPC700_PMM2_PCI_LOW 0xff400028 +#define CPC700_PMM2_PCI_HIGH 0xff40002c +#define CPC700_PTM1_MEMSIZE 0xff400030 +#define CPC700_PTM1_LOCAL 0xff400034 +#define CPC700_PTM2_MEMSIZE 0xff400038 +#define CPC700_PTM2_LOCAL 0xff40003c + +/* + * PIC Section + * + * IBM calls the CPC700's programmable interrupt controller the Universal + * Interrupt Controller or UIC. + */ + +/* + * UIC Register Addresses. + */ +#define CPC700_UIC_UICSR 0xff500880 /* Status Reg (Rd/Clr)*/ +#define CPC700_UIC_UICSRS 0xff500884 /* Status Reg (Set) */ +#define CPC700_UIC_UICER 0xff500888 /* Enable Reg */ +#define CPC700_UIC_UICCR 0xff50088c /* Critical Reg */ +#define CPC700_UIC_UICPR 0xff500890 /* Polarity Reg */ +#define CPC700_UIC_UICTR 0xff500894 /* Trigger Reg */ +#define CPC700_UIC_UICMSR 0xff500898 /* Masked Status Reg */ +#define CPC700_UIC_UICVR 0xff50089c /* Vector Reg */ +#define CPC700_UIC_UICVCR 0xff5008a0 /* Vector Config Reg */ + +#define CPC700_UIC_UICER_ENABLE 0x00000001 /* Enable an IRQ */ + +#define CPC700_UIC_UICVCR_31_HI 0x00000000 /* IRQ 31 hi priority */ +#define CPC700_UIC_UICVCR_0_HI 0x00000001 /* IRQ 0 hi priority */ +#define CPC700_UIC_UICVCR_BASE_MASK 0xfffffffc +#define CPC700_UIC_UICVCR_ORDER_MASK 0x00000001 + +/* Specify value of a bit for an IRQ. */ +#define CPC700_UIC_IRQ_BIT(i) ((0x00000001) << (31 - (i))) + +/* + * UIC Exports... + */ +extern struct hw_interrupt_type cpc700_pic; +extern unsigned int cpc700_irq_assigns[27][2]; + +extern void __init cpc700_init_IRQ(void); +extern int cpc700_get_irq(struct pt_regs *); + +#endif /* _ASMPPC_CPC700_H */ diff -Nru a/arch/ppc/platforms/cpc700_pic.c b/arch/ppc/platforms/cpc700_pic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/cpc700_pic.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,205 @@ +/* + * arch/ppc/platforms/cpc700_pic.c + * + * Interrupt controller support for IBM Spruce + * + * Authors: Mark Greer, Matt Porter, and Johnnie Peters + * mgreer@mvista.com + * mporter@mvista.com + * jpeters@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cpc700.h" + +static void +cpc700_unmask_irq(unsigned int irq) +{ + unsigned int tr_bits; + + /* + * IRQ 31 is largest IRQ supported. + * IRQs 17-19 are reserved. + */ + if ((irq <= 31) && ((irq < 17) || (irq > 19))) { + tr_bits = CPC700_IN_32(CPC700_UIC_UICTR); + + if ((tr_bits & (1 << (31 - irq))) == 0) { + /* level trigger interrupt, clear bit in status + * register */ + CPC700_OUT_32(CPC700_UIC_UICSR, 1 << (31 - irq)); + } + + /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ + ppc_cached_irq_mask[0] |= CPC700_UIC_IRQ_BIT(irq); + + CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); + } + return; +} + +static void +cpc700_mask_irq(unsigned int irq) +{ + /* + * IRQ 31 is largest IRQ supported. + * IRQs 17-19 are reserved. + */ + if ((irq <= 31) && ((irq < 17) || (irq > 19))) { + /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ + ppc_cached_irq_mask[0] &= + ~CPC700_UIC_IRQ_BIT(irq); + + CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); + } + return; +} + +static void +cpc700_mask_and_ack_irq(unsigned int irq) +{ + u_int bit; + + /* + * IRQ 31 is largest IRQ supported. + * IRQs 17-19 are reserved. + */ + if ((irq <= 31) && ((irq < 17) || (irq > 19))) { + /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ + bit = CPC700_UIC_IRQ_BIT(irq); + + ppc_cached_irq_mask[0] &= ~bit; + CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); + CPC700_OUT_32(CPC700_UIC_UICSR, bit); /* Write 1 clears IRQ */ + } + return; +} + +static struct hw_interrupt_type cpc700_pic = { + "CPC700 PIC", + NULL, + NULL, + cpc700_unmask_irq, + cpc700_mask_irq, + cpc700_mask_and_ack_irq, + NULL, + NULL +}; + +__init static void +cpc700_pic_init_irq(unsigned int irq) +{ + unsigned int tmp; + + /* Set interrupt sense */ + tmp = CPC700_IN_32(CPC700_UIC_UICTR); + if (cpc700_irq_assigns[irq][0] == 0) { + tmp &= ~CPC700_UIC_IRQ_BIT(irq); + } else { + tmp |= CPC700_UIC_IRQ_BIT(irq); + } + CPC700_OUT_32(CPC700_UIC_UICTR, tmp); + + /* Set interrupt polarity */ + tmp = CPC700_IN_32(CPC700_UIC_UICPR); + if (cpc700_irq_assigns[irq][1]) { + tmp |= CPC700_UIC_IRQ_BIT(irq); + } else { + tmp &= ~CPC700_UIC_IRQ_BIT(irq); + } + CPC700_OUT_32(CPC700_UIC_UICPR, tmp); + + /* Set interrupt critical */ + tmp = CPC700_IN_32(CPC700_UIC_UICCR); + tmp |= CPC700_UIC_IRQ_BIT(irq); + CPC700_OUT_32(CPC700_UIC_UICCR, tmp); + + return; +} + +__init void +cpc700_init_IRQ(void) +{ + int i; + + ppc_cached_irq_mask[0] = 0; + CPC700_OUT_32(CPC700_UIC_UICER, 0x00000000); /* Disable all irq's */ + CPC700_OUT_32(CPC700_UIC_UICSR, 0xffffffff); /* Clear cur intrs */ + CPC700_OUT_32(CPC700_UIC_UICCR, 0xffffffff); /* Gen INT not MCP */ + CPC700_OUT_32(CPC700_UIC_UICPR, 0x00000000); /* Active low */ + CPC700_OUT_32(CPC700_UIC_UICTR, 0x00000000); /* Level Sensitive */ + CPC700_OUT_32(CPC700_UIC_UICVR, CPC700_UIC_UICVCR_0_HI); + /* IRQ 0 is highest */ + + for (i = 0; i < 17; i++) { + irq_desc[i].handler = &cpc700_pic; + cpc700_pic_init_irq(i); + } + + for (i = 20; i < 27; i++) { + irq_desc[i].handler = &cpc700_pic; + cpc700_pic_init_irq(i); + } + + return; +} + + + +/* + * Find the highest IRQ that generating an interrupt, if any. + */ +int +cpc700_get_irq(struct pt_regs *regs) +{ + int irq = 0; + u_int irq_status, irq_test = 1; + + irq_status = CPC700_IN_32(CPC700_UIC_UICMSR); + + do + { + if (irq_status & irq_test) + break; + irq++; + irq_test <<= 1; + } while (irq < NR_IRQS); + + + if (irq == NR_IRQS) + irq = 33; + + return (31 - irq); +} diff -Nru a/arch/ppc/platforms/cpc710.h b/arch/ppc/platforms/cpc710.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/cpc710.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,77 @@ +/* + * arch/ppc/platforms/cpc710.h + * + * Definitions for the IBM CPC710 PCI Host Bridge + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __PPC_PLATFORMS_CPC710_H +#define __PPC_PLATFORMS_CPC710_H + +/* General bridge and memory controller registers */ +#define PIDR 0xff000008 +#define CNFR 0xff00000c +#define RSTR 0xff000010 +#define UCTL 0xff001000 +#define MPSR 0xff001010 +#define SIOC 0xff001020 +#define ABCNTL 0xff001030 +#define SRST 0xff001040 +#define ERRC 0xff001050 +#define SESR 0xff001060 +#define SEAR 0xff001070 +#define PGCHP 0xff001100 +#define GPDIR 0xff001130 +#define ATAS 0xff001160 +#define AVDG 0xff001170 +#define MCCR 0xff001200 +#define MESR 0xff001220 +#define MEAR 0xff001230 +#define MCER0 0xff001300 +#define MCER1 0xff001310 +#define MCER2 0xff001320 +#define MCER3 0xff001330 +#define MCER4 0xff001340 +#define MCER5 0xff001350 +#define MCER6 0xff001360 +#define MCER7 0xff001370 + +/* + * PCI32/64 configuration registers + * Given as offsets from their + * respective physical segment BAR + */ +#define PIBAR 0x000f7800 +#define PMBAR 0x000f7810 +#define MSIZE 0x000f7f40 +#define IOSIZE 0x000f7f60 +#define SMBAR 0x000f7f80 +#define SIBAR 0x000f7fc0 +#define PSSIZE 0x000f8100 +#define PPSIZE 0x000f8110 +#define BARPS 0x000f8120 +#define BARPP 0x000f8130 +#define PSBAR 0x000f8140 +#define PPBAR 0x000f8150 + +/* System standard configuration registers space */ +#define DCR 0xff200000 +#define DID 0xff200004 +#define BAR 0xff200018 + +/* Device specific configuration space */ +#define PCIENB 0xff201000 + +/* Configuration space registers */ +#define CPC710_BUS_NUMBER 0x40 +#define CPC710_SUB_BUS_NUMBER 0x41 + +#endif /* __PPC_PLATFORMS_CPC710_H */ diff -Nru a/arch/ppc/platforms/cpci405.c b/arch/ppc/platforms/cpci405.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/cpci405.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,78 @@ +/* + * arch/ppc/platforms/cpci405.c + * + * Board setup routines for the esd CPCI-405 cPCI Board. + * + * Author: Stefan Roese + * stefan.roese@esd-electronics.com + * + * Copyright 2001 esd electronic system design - hannover germany + * + * 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. + * + * History: 11/09/2001 - armin + * added board_init to add in additional instuctions needed during platfrom_init + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Some IRQs unique to CPCI-405. + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 15 - cPCI slot 8 */ + {29, 29, 29, 29}, /* IDSEL 16 - cPCI slot 7 */ + {30, 30, 30, 30}, /* IDSEL 17 - cPCI slot 6 */ + {27, 27, 27, 27}, /* IDSEL 18 - cPCI slot 5 */ + {28, 28, 28, 28}, /* IDSEL 19 - cPCI slot 4 */ + {29, 29, 29, 29}, /* IDSEL 20 - cPCI slot 3 */ + {30, 30, 30, 30}, /* IDSEL 21 - cPCI slot 2 */ + }; + const long min_idsel = 15, max_idsel = 21, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +board_setup_arch(void) +{ +} + +void __init +board_io_mapping(void) +{ +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ +#ifdef CONFIG_PPC_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +} diff -Nru a/arch/ppc/platforms/cpci405.h b/arch/ppc/platforms/cpci405.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/cpci405.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,45 @@ +/* + * CPCI-405 board specific definitions + * + * Copyright (c) 2001 Stefan Roese (stefan.roese@esd-electronics.com) + */ + +#ifndef __ASM_CPCI405_H__ +#define __ASM_CPCI405_H__ + +#include + +/* We have a 405GP core */ +#include + +#include + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +/* Early initialization address mapping for block_io. + * Standard 405GP map. + */ +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define PPC4xx_MACHINE_NAME "esd CPCI-405" + +#endif /* __ASM_CPCI405_H__ */ diff -Nru a/arch/ppc/platforms/ep405.c b/arch/ppc/platforms/ep405.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ep405.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,187 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + * + * Copyright 2001 MontaVista Software Inc. + * + * + * Not much needed for the Embedded Planet 405gp board + * + * History: 11/09/2001 - armin + * added board_init to add in additional instuctions needed during platfrom_init + * cleaned up map_irq. + * + * 1/22/2002 - Armin + * converted pci to ocp + * + + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +void *ep405_bcsr; +void *ep405_nvram; + +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 1 - PCI slot 1 */ + {29, 29, 29, 29}, /* IDSEL 2 - PCI slot 2 */ + {30, 30, 30, 30}, /* IDSEL 3 - PCI slot 3 */ + {31, 31, 31, 31}, /* IDSEL 4 - PCI slot 4 */ + }; + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +board_setup_arch(void) +{ +#ifdef CONFIG_PPC_RTC + /* FIXME: what if NVRAM size is not 512k */ + TODC_INIT(TODC_TYPE_DS1557, ep405_nvram, ep405_nvram, ep405_nvram, 8); +#endif /* CONFIG_PPC_RTC */ +} + +void __init +bios_fixup(struct pci_controller *hose, void *pcil0_base) +{ + + unsigned int bar_response, bar; + struct pcil0_regs *pcip; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + */ + +#ifdef DEBUG + int i; + pcip = (struct pcil0_regs *) pcil0_base; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#else + pcip = (struct pcil0_regs *) pcil0_base; +#endif + /* added for IBM boot rom version 1.15 bios bar changes -AK */ + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000000); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* Zero config bars */ + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + 0x00000000); + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + /* end work arround */ + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); +#endif +} + +void __init +board_io_mapping(void) +{ + ep405_bcsr = ioremap(EP405_BCSR_PADDR, EP405_BCSR_SIZE); + ep405_nvram = ioremap(EP405_NVRAM_PADDR, EP405_NVRAM_SIZE); +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ +#ifdef CONFIG_PPC_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +} diff -Nru a/arch/ppc/platforms/ep405.h b/arch/ppc/platforms/ep405.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ep405.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,64 @@ +/* + * Copyright 2000 MontaVista Software Inc. + * http://www.mvista.com + * + * + * Embedded Planet 405GP board + * http://www.embeddedplanet.com + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_EP405_H__ +#define __ASM_EP405_H__ + +/* We have a 405GP core */ +#include + +#ifndef __ASSEMBLY__ +typedef struct board_info { + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +extern void *ep405_bcsr; +extern void *ep405_nvram; + +/* Map for the BCSR and NVRAM space */ +#define EP405_BCSR_PADDR ((uint)0xf4000000) +#define EP405_BCSR_SIZE ((uint)16) +#define EP405_NVRAM_PADDR ((uint)0xf4200000) +/* FIXME: what if the board has something other than 512k NVRAM */ +#define EP405_NVRAM_SIZE ((uint)512*1024) + +/* Early initialization address mapping for block_io. + * Standard 405GP map. + */ +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial defines */ +#define BASE_BAUD 399193 + +#define PPC4xx_MACHINE_NAME "Embedded Planet 405GP" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_EP405_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/error_log.c b/arch/ppc/platforms/error_log.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/error_log.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,186 @@ +/* + * BK Id: SCCS/s.error_log.c 1.6 05/17/01 18:14:21 cort + */ +/* + * arch/ppc/kernel/error_log.c + * + * Copyright (c) 2000 Tilmann Bitterberg + * (tilmann@bitterberg.de) + * + * Error processing of errors found by rtas even-scan routine + * which is done with every heartbeat. (chrp_setup.c) + */ + +#include + +#include + +#include "error_log.h" + +/* ****************************************************************** */ +/* + * EVENT-SCAN + * The whole stuff below here doesn't take any action when it found + * an error, it just prints as much information as possible and + * then its up to the user to decide what to do. + * + * Returns 0 if no errors were found + * Returns 1 if there may be more errors + */ +int ppc_rtas_errorlog_scan(void) +{ +const char *_errlog_severity[] = { +#ifdef VERBOSE_ERRORS + "No Error\n\t\ +Should require no further information", + "Event\n\t\ +This is not really an error, it is an event. I use events\n\t\ +to communicate with RTAS back and forth.", + "Warning\n\t\ +Indicates a non-state-losing error, either fully recovered\n\t\ +by RTAS or not needing recovery. Ignore it.", + "Error sync\n\t\ +May only be fatal to a certain program or thread. Recovery\n\t\ +and continuation is possible, if I only had a handler for\n\t\ +this. Less serious", + "Error\n\t\ +Less serious, but still causing a loss of data and state.\n\t\ +I can't tell you exactly what to do, You have to decide\n\t\ +with help from the target and initiator field, what kind\n\t\ +of further actions may take place.", + "Fatal\n\t\ +Represent a permanent hardware failure and I believe this\n\t\ +affects my overall performance and behaviour. I would not\n\t\ +attempt to continue normal operation." +#else + "No Error", + "Event", + "Warning", + "Error sync", + "Error", + "Fatal" +#endif /* VERBOSE_ERRORS */ +}; + +#if 0 /* unused?? */ +const char *_errlog_disposition[] = { +#ifdef VERBOSE_ERRORS + "Fully recovered\n\t\ +There was an error, but it is fully recovered by RTAS.", + "Limited recovery\n\t\ +RTAS was able to recover the state of the machine, but some\n\t\ +feature of the machine has been disabled or lost (for example\n\t\ +error checking) or performance may suffer.", + "Not recovered\n\t\ +Whether RTAS did not try to recover anything or recovery failed:\n\t\ +HOUSTON, WE HAVE A PROBLEM!" +#else + "Fully recovered", + "Limited recovery", + "Not recovered" +#endif /* VERBOSE_ERRORS */ +}; +#endif + +const char *_errlog_extended[] = { +#ifdef VERBOSE_ERRORS + "Not present\n\t\ +Sad, the RTAS call didn't return an extended error log.", + "Present\n\t\ +The extended log is present and hopefully it contains a lot of\n\t\ +useful information, which leads to the solution of the problem." +#else + "Not present", + "Present" +#endif /* VERBOSE_ERRORS */ +}; + +const char *_errlog_initiator[] = { + "Unknown or not applicable", + "CPU", + "PCI", + "ISA", + "Memory", + "Power management" +}; + +const char *_errlog_target[] = { + "Unknown or not applicable", + "CPU", + "PCI", + "ISA", + "Memory", + "Power management" +}; + rtas_error_log error_log; + char logdata[1024]; + int error; +#if 0 /* unused?? */ + int retries = 0; /* if HW error, try 10 times */ +#endif + + error = call_rtas ("event-scan", 4, 1, (unsigned long *)&error_log, + INTERNAL_ERROR | EPOW_WARNING, + 0, __pa(logdata), 1024); + + if (error == 1) /* no errors found */ + return 0; + + if (error == -1) { + printk(KERN_ERR "Unable to get errors. Do you a favor and throw this box away\n"); + return 0; + } + if (error_log.version != 1) + printk(KERN_WARNING "Unknown version (%d), please implement me\n", + error_log.version); + + switch (error_log.disposition) { + case DISP_FULLY_RECOVERED: + /* there was an error, but everything is fine now */ + return 0; + case DISP_NOT_RECOVERED: + printk("We have a really serious Problem!\n"); + case DISP_LIMITED_RECOVERY: + printk("Error classification\n"); + printk("Severity : %s\n", + ppc_rtas_errorlog_check_severity (error_log)); + printk("Initiator : %s\n", + ppc_rtas_errorlog_check_initiator (error_log)); + printk("Target : %s\n", + ppc_rtas_errorlog_check_target (error_log)); + printk("Type : %s\n", + ppc_rtas_errorlog_check_type (error_log)); + printk("Ext. log : %s\n", + ppc_rtas_errorlog_check_extended (error_log)); + if (error_log.extended) + ppc_rtas_errorlog_disect_extended (logdata); + return 1; + default: + /* nothing */ + break; + } + return 0; +} +/* ****************************************************************** */ +const char * ppc_rtas_errorlog_check_type (rtas_error_log error_log) +{ + const char *_errlog_type[] = { + "unknown type", + "too many tries failed", + "TCE error", + "RTAS device failed", + "target timed out", + "parity error on data", /* 5 */ + "parity error on address", + "parity error on external cache", + "access to invalid address", + "uncorrectable ECC error", + "corrected ECC error" /* 10 */ + }; + if (error_log.type == TYPE_EPOW) + return "EPOW"; + if (error_log.type >= TYPE_PMGM_POWER_SW_ON) + return "PowerMGM Event (not handled right now)"; + return _errlog_type[error_log.type]; +} + diff -Nru a/arch/ppc/platforms/error_log.h b/arch/ppc/platforms/error_log.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/error_log.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,98 @@ +/* + * BK Id: SCCS/s.error_log.h 1.5 05/17/01 18:14:21 cort + */ +#ifndef __ERROR_LOG_H__ +#define __ERROR_LOG_H__ + +#define VERBOSE_ERRORS 1 /* Maybe I enlarge the kernel too much */ +#undef VERBOSE_ERRORS + +/* Event classes */ +/* XXX: Endianess correct? NOW*/ +#define INTERNAL_ERROR 0x80000000 /* set bit 0 */ +#define EPOW_WARNING 0x40000000 /* set bit 1 */ +#define POWERMGM_EVENTS 0x20000000 /* set bit 2 */ + +/* event-scan returns */ +#define SEVERITY_FATAL 0x5 +#define SEVERITY_ERROR 0x4 +#define SEVERITY_ERROR_SYNC 0x3 +#define SEVERITY_WARNING 0x2 +#define SEVERITY_EVENT 0x1 +#define SEVERITY_NO_ERROR 0x0 +#define DISP_FULLY_RECOVERED 0x0 +#define DISP_LIMITED_RECOVERY 0x1 +#define DISP_NOT_RECOVERED 0x2 +#define PART_PRESENT 0x0 +#define PART_NOT_PRESENT 0x1 +#define INITIATOR_UNKNOWN 0x0 +#define INITIATOR_CPU 0x1 +#define INITIATOR_PCI 0x2 +#define INITIATOR_ISA 0x3 +#define INITIATOR_MEMORY 0x4 +#define INITIATOR_POWERMGM 0x5 +#define TARGET_UNKNOWN 0x0 +#define TARGET_CPU 0x1 +#define TARGET_PCI 0x2 +#define TARGET_ISA 0x3 +#define TARGET_MEMORY 0x4 +#define TARGET_POWERMGM 0x5 +#define TYPE_RETRY 0x01 +#define TYPE_TCE_ERR 0x02 +#define TYPE_INTERN_DEV_FAIL 0x03 +#define TYPE_TIMEOUT 0x04 +#define TYPE_DATA_PARITY 0x05 +#define TYPE_ADDR_PARITY 0x06 +#define TYPE_CACHE_PARITY 0x07 +#define TYPE_ADDR_INVALID 0x08 +#define TYPE_ECC_UNCORR 0x09 +#define TYPE_ECC_CORR 0x0a +#define TYPE_EPOW 0x40 +/* I don't add PowerMGM events right now, this is a different topic */ +#define TYPE_PMGM_POWER_SW_ON 0x60 +#define TYPE_PMGM_POWER_SW_OFF 0x61 +#define TYPE_PMGM_LID_OPEN 0x62 +#define TYPE_PMGM_LID_CLOSE 0x63 +#define TYPE_PMGM_SLEEP_BTN 0x64 +#define TYPE_PMGM_WAKE_BTN 0x65 +#define TYPE_PMGM_BATTERY_WARN 0x66 +#define TYPE_PMGM_BATTERY_CRIT 0x67 +#define TYPE_PMGM_SWITCH_TO_BAT 0x68 +#define TYPE_PMGM_SWITCH_TO_AC 0x69 +#define TYPE_PMGM_KBD_OR_MOUSE 0x6a +#define TYPE_PMGM_ENCLOS_OPEN 0x6b +#define TYPE_PMGM_ENCLOS_CLOSED 0x6c +#define TYPE_PMGM_RING_INDICATE 0x6d +#define TYPE_PMGM_LAN_ATTENTION 0x6e +#define TYPE_PMGM_TIME_ALARM 0x6f +#define TYPE_PMGM_CONFIG_CHANGE 0x70 +#define TYPE_PMGM_SERVICE_PROC 0x71 + +typedef struct _rtas_error_log { + unsigned long version:8; /* Architectural version */ + unsigned long severity:3; /* Severity level of error */ + unsigned long disposition:2; /* Degree of recovery */ + unsigned long extended:1; /* extended log present? */ + unsigned long /* reserved */ :2; /* Reserved for future use */ + unsigned long initiator:4; /* Initiator of event */ + unsigned long target:4; /* Target of failed operation */ + unsigned long type:8; /* General event or error*/ + unsigned long extended_log_length:32; /* length in bytes */ +} rtas_error_log; + +/* ****************************************************************** */ +#define ppc_rtas_errorlog_check_severity(x) \ + (_errlog_severity[x.severity]) +#define ppc_rtas_errorlog_check_target(x) \ + (_errlog_target[x.target]) +#define ppc_rtas_errorlog_check_initiator(x) \ + (_errlog_initiator[x.initiator]) +#define ppc_rtas_errorlog_check_extended(x) \ + (_errlog_extended[x.extended]) +#define ppc_rtas_errorlog_disect_extended(x) \ + do { /* implement me */ } while(0) +extern const char * ppc_rtas_errorlog_check_type (rtas_error_log error_log); +extern int ppc_rtas_errorlog_scan(void); + + +#endif /* __ERROR_LOG_H__ */ diff -Nru a/arch/ppc/platforms/est8260.h b/arch/ppc/platforms/est8260.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/est8260.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,31 @@ +/* + * BK Id: SCCS/s.est8260.h 1.5 05/17/01 18:14:24 cort + */ + +/* Board information for the EST8260, which should be generic for + * all 8260 boards. The IMMR is now given to us so the hard define + * will soon be removed. All of the clock values are computed from + * the configuration SCMR and the Power-On-Reset word. + */ + +#define IMAP_ADDR ((uint)0xf0000000) + + +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in MHz */ + unsigned int bi_cpmfreq; /* CPM Freq, in MHz */ + unsigned int bi_brgfreq; /* BRG Freq, in MHz */ + unsigned int bi_vco; /* VCO Out from PLL */ + unsigned int bi_baudrate; /* Default console baud rate */ + unsigned int bi_immr; /* IMMR when called from boot rom */ + unsigned char bi_enetaddr[6]; +} bd_t; + +extern bd_t m8xx_board_info; + diff -Nru a/arch/ppc/platforms/ev64260.h b/arch/ppc/platforms/ev64260.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ev64260.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,69 @@ +/* + * arch/ppc/platforms/ev64260.h + * + * Definitions for Marvell/Galileo EV-64260-BP Evaluation Board. + * + * Author: Mark A. Greer + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * The GT64260 has 2 PCI buses each with 1 window from the CPU bus to + * PCI I/O space and 4 windows from the CPU bus to PCI MEM space. + * We'll only use one PCI MEM window on each PCI bus. + */ + +#ifndef __PPC_PLATFORMS_EV64260_H +#define __PPC_PLATFORMS_EV64260_H + +#define EV64260_BRIDGE_REG_BASE 0xf8000000 +#define EV64260_BRIDGE_REG_BASE_TO_TOP 0x08000000U + +#define EV64260_TODC_BASE 0xfc800000 +#define EV64260_TODC_LEN 0x00800000 +#define EV64260_TODC_END (EV64260_TODC_BASE + \ + EV64260_TODC_LEN - 1) + +#define EV64260_UART_BASE 0xfd000000 +#define EV64260_UART_LEN 0x00800000 +#define EV64260_UART_END (EV64260_UART_BASE + \ + EV64260_UART_LEN - 1) +/* Serial driver setup. */ +#define EV64260_SERIAL_0 (EV64260_UART_BASE + 0x20) +#define EV64260_SERIAL_1 EV64260_UART_BASE + +#define BASE_BAUD ( 3686400 / 16 ) + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 2 +#endif + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +#if !defined(CONFIG_GT64260_CONSOLE) +/* Required for bootloader's ns16550.c code */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, EV64260_SERIAL_0, 85, STD_COM_FLAGS, /* ttyS0 */\ + iomem_base: (u8 *)EV64260_SERIAL_0, \ + iomem_reg_shift: 2, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS +#else +#define SERIAL_PORT_DFNS +#endif + +#endif /* __PPC_PLATFORMS_EV64260_H */ diff -Nru a/arch/ppc/platforms/ev64260_setup.c b/arch/ppc/platforms/ev64260_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ev64260_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,477 @@ +/* + * arch/ppc/platforms/ev64260_setup.c + * + * Board setup routines for the Marvell/Galileo EV-64260-BP Evaluation Board. + * + * Author: Mark A. Greer + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * The EV-64260-BP port is the result of hard work from many people from + * many companies. In particular, employees of Marvell/Galileo, Mission + * Critical Linux, Xyterra, and MontaVista Software were heavily involved. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(CONFIG_GT64260_CONSOLE) +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern char cmd_line[]; +unsigned long ev64260_find_end_of_memory(void); + +TODC_ALLOC(); + +/* + * Marvell/Galileo EV-64260-BP Evaluation Board PCI interrupt routing. + */ +static int __init +ev64260_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + + if (hose->index == 0) { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 91, 0, 0, 0 }, /* IDSEL 7 - PCI bus 0 */ + { 91, 0, 0, 0 }, /* IDSEL 8 - PCI bus 0 */ + }; + + const long min_idsel = 7, max_idsel = 8, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } + else { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 93, 0, 0, 0 }, /* IDSEL 7 - PCI bus 1 */ + { 93, 0, 0, 0 }, /* IDSEL 8 - PCI bus 1 */ + }; + + const long min_idsel = 7, max_idsel = 8, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } +} + +static void __init +ev64260_setup_bridge(void) +{ + gt64260_bridge_info_t info; + int window; + + GT64260_BRIDGE_INFO_DEFAULT(&info, ev64260_find_end_of_memory()); + + /* Lookup PCI host bridges */ + if (gt64260_find_bridges(EV64260_BRIDGE_REG_BASE, + &info, + ev64260_map_irq)) { + printk("Bridge initialization failed.\n"); + } + + /* + * Enabling of PCI internal-vs-external arbitration + * is a platform- and errata-dependent decision. + */ + if(gt64260_revision == GT64260) { + /* FEr#35 */ + gt_clr_bits(GT64260_PCI_0_ARBITER_CNTL, (1<<31)); + gt_clr_bits(GT64260_PCI_1_ARBITER_CNTL, (1<<31)); + } else if( gt64260_revision == GT64260A ) { + gt_set_bits(GT64260_PCI_0_ARBITER_CNTL, (1<<31)); + gt_set_bits(GT64260_PCI_1_ARBITER_CNTL, (1<<31)); + /* Make external GPP interrupts level sensitive */ + gt_set_bits(GT64260_COMM_ARBITER_CNTL, (1<<10)); + /* Doc Change 9: > 100 MHz so must be set */ + gt_set_bits(GT64260_CPU_CONFIG, (1<<23)); + } + + gt_set_bits(GT64260_CPU_MASTER_CNTL, (1<<9)); /* Only 1 cpu */ + + /* SCS windows not disabled above, disable all but SCS 0 */ + for (window=1; window GPP 21 (DUART channel A intr) + * MPP 22 -> GPP 22 (DUART channel B intr) + * + * In MPP Control 3 Register + * MPP 27 -> GPP 27 (PCI 0 INTA) + * MPP 29 -> GPP 29 (PCI 1 INTA) + */ + gt_clr_bits(GT64260_MPP_CNTL_2, + ((1<<20) | (1<<21) | (1<<22) | (1<<23) | + (1<<24) | (1<<25) | (1<<26) | (1<<27))); + + gt_clr_bits(GT64260_MPP_CNTL_3, + ((1<<12) | (1<<13) | (1<<14) | (1<<15) | + (1<<20) | (1<<21) | (1<<22) | (1<<23))); + + gt_write(GT64260_GPP_LEVEL_CNTL, 0x000002c6); + + /* DUART & PCI interrupts are active low */ + gt_set_bits(GT64260_GPP_LEVEL_CNTL, + ((1<<21) | (1<<22) | (1<<27) | (1<<29))); + + /* Clear any pending interrupts for these inputs and enable them. */ + gt_write(GT64260_GPP_INTR_CAUSE, + ~((1<<21) | (1<<22) | (1<<27) | (1<<29))); + gt_set_bits(GT64260_GPP_INTR_MASK, + ((1<<21) | (1<<22)| (1<<27) | (1<<29))); + gt_set_bits(GT64260_IC_CPU_INTR_MASK_HI, ((1<<26) | (1<<27))); + + /* Set MPSC Multiplex RMII */ + /* NOTE: ethernet driver modifies bit 0 and 1 */ + gt_write(GT64260_MPP_SERIAL_PORTS_MULTIPLEX, 0x00001102); + + return; +} + + +static void __init +ev64260_setup_arch(void) +{ +#if !defined(CONFIG_GT64260_CONSOLE) + struct serial_struct serial_req; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("ev64260_setup_arch: enter", 0); + + loops_per_jiffy = 50000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00FF); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 SCSI disk */ +#endif + + if ( ppc_md.progress ) + ppc_md.progress("ev64260_setup_arch: find_bridges", 0); + + /* + * Set up the L2CR register. + * L2 cache was invalidated by bootloader. + */ + switch (PVR_VER(mfspr(PVR))) { + case PVR_VER(PVR_750): + _set_L2CR(0xfd100000); + break; + case PVR_VER(PVR_7400): + case PVR_VER(PVR_7410): + _set_L2CR(0xcd100000); + break; + /* case PVR_VER(PVR_7450): */ + /* XXXX WHAT VALUE?? FIXME */ + break; + } + + ev64260_setup_bridge(); + + TODC_INIT(TODC_TYPE_DS1501, 0, 0, ioremap(EV64260_TODC_BASE,0x20), 8); + +#if !defined(CONFIG_GT64260_CONSOLE) + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.line = 0; + serial_req.baud_base = BASE_BAUD; + serial_req.port = 0; + serial_req.irq = 85; + serial_req.flags = STD_COM_FLAGS; + serial_req.io_type = SERIAL_IO_MEM; + serial_req.iomem_base = ioremap(EV64260_SERIAL_0, 0x20); + serial_req.iomem_reg_shift = 2; + + if (early_serial_setup(&serial_req) != 0) { + printk("Early serial init of port 0 failed\n"); + } + + /* Assume early_serial_setup() doesn't modify serial_req */ + serial_req.line = 1; + serial_req.port = 1; + serial_req.irq = 86; + serial_req.iomem_base = ioremap(EV64260_SERIAL_1, 0x20); + + if (early_serial_setup(&serial_req) != 0) { + printk("Early serial init of port 1 failed\n"); + } +#endif + + printk("Marvell/Galileo EV-64260-BP Evaluation Board\n"); + printk("EV-64260-BP port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + if ( ppc_md.progress ) + ppc_md.progress("ev64260_setup_arch: exit", 0); + + return; +} + +static void __init +ev64260_init_irq(void) +{ + gt64260_init_irq(); + + if(gt64260_revision != GT64260) { + /* XXXX Kludge--need to fix gt64260_init_irq() interface */ + /* Mark PCI intrs level sensitive */ + irq_desc[91].status |= IRQ_LEVEL; + irq_desc[93].status |= IRQ_LEVEL; + } +} + +unsigned long __init +ev64260_find_end_of_memory(void) +{ + return 32*1024*1024; /* XXXX FIXME */ +} + +static void +ev64260_reset_board(void) +{ + __cli(); + + /* Set exception prefix high - to the firmware */ + _nmask_and_or_msr(0, MSR_IP); + + /* XXX FIXME */ + printk("XXXX **** trying to reset board ****\n"); + return; +} + +static void +ev64260_restart(char *cmd) +{ + volatile ulong i = 10000000; + + ev64260_reset_board(); + + while (i-- > 0); + panic("restart failed\n"); +} + +static void +ev64260_halt(void) +{ + __cli(); + while (1); + /* NOTREACHED */ +} + +static void +ev64260_power_off(void) +{ + ev64260_halt(); + /* NOTREACHED */ +} + +static int +ev64260_show_cpuinfo(struct seq_file *m) +{ + uint pvid; + + pvid = mfspr(PVR); + seq_printf(m, "vendor\t\t: Marvell/Galileo\n"); + seq_printf(m, "machine\t\t: EV-64260-BP\n"); + seq_printf(m, "PVID\t\t: 0x%x, vendor: %s\n", + pvid, (pvid & (1<<15) ? "IBM" : "Motorola")); + + return 0; +} + +/* DS1501 RTC has too much variation to use RTC for calibration */ +static void __init +ev64260_calibrate_decr(void) +{ + ulong freq; + + freq = 100000000 / 4; + + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + return; +} + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +ev64260_set_bat(void) +{ + unsigned long bat3u, bat3l; + static int mapping_set = 0; + + if (!mapping_set) { + + __asm__ __volatile__( + " lis %0,0xf000\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x1ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (bat3u), "=r" (bat3l)); + + mapping_set = 1; + } + + return; +} + +#if !defined(CONFIG_GT64260_CONSOLE) +#include +#include +#include + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in */ +}; + +static void +ev64260_16550_progress(char *s, unsigned short hex) +{ + volatile char c; + volatile unsigned long com_port; + u16 shift; + + com_port = rs_table[0].port; + shift = rs_table[0].iomem_reg_shift; + + while ((c = *s++) != 0) { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = c; + + if (c == '\n') { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = '\r'; + } + } + + /* Move to next line on */ + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = '\n'; + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = '\r'; + + return; +} +#endif /* !CONFIG_GT64260_CONSOLE */ +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + isa_mem_base = 0; + + ppc_md.setup_arch = ev64260_setup_arch; + ppc_md.show_cpuinfo = ev64260_show_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = ev64260_init_irq; + ppc_md.get_irq = gt64260_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = ev64260_restart; + ppc_md.power_off = ev64260_power_off; + ppc_md.halt = ev64260_halt; + + ppc_md.find_end_of_memory = ev64260_find_end_of_memory; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = ev64260_calibrate_decr; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + + ppc_md.heartbeat = NULL; + ppc_md.heartbeat_reset = 0; + ppc_md.heartbeat_count = 0; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ev64260_set_bat(); +#ifdef CONFIG_GT64260_CONSOLE + gt64260_base = EV64260_BRIDGE_REG_BASE; + ppc_md.progress = gt64260_mpsc_progress; /* embedded UART */ +#else + ppc_md.progress = ev64260_16550_progress; /* Dev module DUART */ +#endif +#else /* !CONFIG_SERIAL_TEXT_DEBUG */ + ppc_md.progress = NULL; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + + return; +} diff -Nru a/arch/ppc/platforms/fads.h b/arch/ppc/platforms/fads.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/fads.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,61 @@ +/* + * BK Id: SCCS/s.fads.h 1.14 10/26/01 10:14:09 trini + */ + +/* + * A collection of structures, addresses, and values associated with + * the Motorola 860T FADS board. Copied from the MBX stuff. + * + * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) + */ +#ifdef __KERNEL__ +#ifndef __ASM_FADS_H__ +#define __ASM_FADS_H__ + +#include + +#include + +/* Memory map is configured by the PROM startup. + * I tried to follow the FADS manual, although the startup PROM + * dictates this and we simply have to move some of the physical + * addresses for Linux. + */ +#define BCSR_ADDR ((uint)0xff010000) +#define BCSR_SIZE ((uint)(64 * 1024)) +#define BCSR0 ((uint)0xff010000) +#define BCSR1 ((uint)0xff010004) +#define BCSR2 ((uint)0xff010008) +#define BCSR3 ((uint)0xff01000c) +#define BCSR4 ((uint)0xff010010) + +#define IMAP_ADDR ((uint)0xff000000) +#define IMAP_SIZE ((uint)(64 * 1024)) + +#define PCMCIA_MEM_ADDR ((uint)0xff020000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) + +/* Bits of interest in the BCSRs. + */ +#define BCSR1_ETHEN ((uint)0x20000000) +#define BCSR1_RS232EN_1 ((uint)0x01000000) +#define BCSR1_RS232EN_2 ((uint)0x00040000) +#define BCSR4_ETHLOOP ((uint)0x80000000) /* EEST Loopback */ +#define BCSR4_EEFDX ((uint)0x40000000) /* EEST FDX enable */ +#define BCSR4_FETH_EN ((uint)0x08000000) /* PHY enable */ +#define BCSR4_FETHCFG0 ((uint)0x04000000) /* PHY autoneg mode */ +#define BCSR4_FETHCFG1 ((uint)0x00400000) /* PHY autoneg mode */ +#define BCSR4_FETHFDE ((uint)0x02000000) /* PHY FDX advertise */ +#define BCSR4_FETHRST ((uint)0x00200000) /* PHY Reset */ + +/* Interrupt level assignments. + */ +#define FEC_INTERRUPT SIU_LEVEL1 /* FEC interrupt */ +#define PHY_INTERRUPT SIU_IRQ2 /* PHY link change interrupt */ + +/* We don't use the 8259. + */ +#define NR_8259_INTS 0 + +#endif /* __ASM_FADS_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/gemini.h b/arch/ppc/platforms/gemini.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/gemini.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,171 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * include/asm-ppc/platforms/gemini.h + * + * + * Onboard registers and descriptions for Synergy Microsystems' + * "Gemini" boards. + * + */ +#ifdef __KERNEL__ +#ifndef __PPC_GEMINI_H +#define __PPC_GEMINI_H + +/* Registers */ + +#define GEMINI_SERIAL_B (0xffeffb00) +#define GEMINI_SERIAL_A (0xffeffb08) +#define GEMINI_USWITCH (0xffeffd00) +#define GEMINI_BREV (0xffeffe00) +#define GEMINI_BECO (0xffeffe08) +#define GEMINI_FEAT (0xffeffe10) +#define GEMINI_BSTAT (0xffeffe18) +#define GEMINI_CPUSTAT (0xffeffe20) +#define GEMINI_L2CFG (0xffeffe30) +#define GEMINI_MEMCFG (0xffeffe38) +#define GEMINI_FLROM (0xffeffe40) +#define GEMINI_P0PCI (0xffeffe48) +#define GEMINI_FLWIN (0xffeffe50) +#define GEMINI_P0INTMASK (0xffeffe60) +#define GEMINI_P0INTAP (0xffeffe68) +#define GEMINI_PCIERR (0xffeffe70) +#define GEMINI_LEDBASE (0xffeffe80) +#define GEMINI_RTC (0xffe9fff8) +#define GEMINI_LEDS 8 +#define GEMINI_SWITCHES 8 + + +/* Flash ROM bit definitions */ +#define GEMINI_FLS_WEN (1<<0) +#define GEMINI_FLS_JMP (1<<6) +#define GEMINI_FLS_BOOT (1<<7) + +/* Memory bit definitions */ +#define GEMINI_MEM_TYPE_MASK 0xc0 +#define GEMINI_MEM_SIZE_MASK 0x38 +#define GEMINI_MEM_BANK_MASK 0x07 + +/* L2 cache bit definitions */ +#define GEMINI_L2_SIZE_MASK 0xc0 +#define GEMINI_L2_RATIO_MASK 0x03 + +/* Timebase register bit definitons */ +#define GEMINI_TIMEB0_EN (1<<0) +#define GEMINI_TIMEB1_EN (1<<1) +#define GEMINI_TIMEB2_EN (1<<2) +#define GEMINI_TIMEB3_EN (1<<3) + +/* CPU status bit definitions */ +#define GEMINI_CPU_ID_MASK 0x03 +#define GEMINI_CPU_COUNT_MASK 0x0c +#define GEMINI_CPU0_HALTED (1<<4) +#define GEMINI_CPU1_HALTED (1<<5) +#define GEMINI_CPU2_HALTED (1<<6) +#define GEMINI_CPU3_HALTED (1<<7) + +/* Board status bit definitions */ +#define GEMINI_BRD_FAIL (1<<0) /* FAIL led is lit */ +#define GEMINI_BRD_BUS_MASK 0x0c /* PowerPC bus speed */ + +/* Board family/feature bit descriptions */ +#define GEMINI_FEAT_HAS_FLASH (1<<0) +#define GEMINI_FEAT_HAS_ETH (1<<1) +#define GEMINI_FEAT_HAS_SCSI (1<<2) +#define GEMINI_FEAT_HAS_P0 (1<<3) +#define GEMINI_FEAT_FAM_MASK 0xf0 + +/* Mod/ECO bit definitions */ +#define GEMINI_ECO_LEVEL_MASK 0x0f +#define GEMINI_MOD_MASK 0xf0 + +/* Type/revision bit definitions */ +#define GEMINI_REV_MASK 0x0f +#define GEMINI_TYPE_MASK 0xf0 + +/* User switch definitions */ +#define GEMINI_SWITCH_VERBOSE 1 /* adds "debug" to boot cmd line */ +#define GEMINI_SWITCH_SINGLE_USER 7 /* boots into "single-user" mode */ + +#define SGS_RTC_CONTROL 0 +#define SGS_RTC_SECONDS 1 +#define SGS_RTC_MINUTES 2 +#define SGS_RTC_HOURS 3 +#define SGS_RTC_DAY 4 +#define SGS_RTC_DAY_OF_MONTH 5 +#define SGS_RTC_MONTH 6 +#define SGS_RTC_YEAR 7 + +#define SGS_RTC_SET 0x80 +#define SGS_RTC_IS_STOPPED 0x80 + +#define GRACKLE_CONFIG_ADDR_ADDR (0xfec00000) +#define GRACKLE_CONFIG_DATA_ADDR (0xfee00000) + +#define GEMINI_BOOT_INIT (0xfff00100) + +#ifndef __ASSEMBLY__ + +static inline void grackle_write( unsigned long addr, unsigned long data ) +{ + __asm__ __volatile__( + " stwbrx %1, 0, %0\n \ + sync\n \ + stwbrx %3, 0, %2\n \ + sync " + : /* no output */ + : "r" (GRACKLE_CONFIG_ADDR_ADDR), "r" (addr), + "r" (GRACKLE_CONFIG_DATA_ADDR), "r" (data)); +} + +static inline unsigned long grackle_read( unsigned long addr ) +{ + unsigned long val; + + __asm__ __volatile__( + " stwbrx %1, 0, %2\n \ + sync\n \ + lwbrx %0, 0, %3\n \ + sync " + : "=r" (val) + : "r" (addr), "r" (GRACKLE_CONFIG_ADDR_ADDR), + "r" (GRACKLE_CONFIG_DATA_ADDR)); + + return val; +} + +static inline void gemini_led_on( int led ) +{ + if (led >= 0 && led < GEMINI_LEDS) + *(unsigned char *)(GEMINI_LEDBASE + (led<<3)) = 1; +} + +static inline void gemini_led_off(int led) +{ + if (led >= 0 && led < GEMINI_LEDS) + *(unsigned char *)(GEMINI_LEDBASE + (led<<3)) = 0; +} + +static inline int gemini_led_val(int led) +{ + int val = 0; + if (led >= 0 && led < GEMINI_LEDS) + val = *(unsigned char *)(GEMINI_LEDBASE + (led<<3)); + return (val & 0x1); +} + +/* returns processor id from the board */ +static inline int gemini_processor(void) +{ + unsigned char cpu = *(unsigned char *)(GEMINI_CPUSTAT); + return (int) ((cpu == 0) ? 4 : (cpu & GEMINI_CPU_ID_MASK)); +} + + +extern void _gemini_reboot(void); +extern void gemini_prom_init(void); +extern void gemini_init_l2(void); +#endif /* __ASSEMBLY__ */ +#endif +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/gemini_pci.c b/arch/ppc/platforms/gemini_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/gemini_pci.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,122 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define pci_config_addr(bus,dev,offset) \ + (0x80000000 | (bus<<16) | (dev<<8) | offset) + + +int +gemini_pcibios_read_config_byte(struct pci_dev *dev, int offset, u8 *val) +{ + unsigned long reg; + reg = grackle_read(pci_config_addr(dev->bus->number, dev->devfn, + (offset & ~(0x3)))); + *val = ((reg >> ((offset & 0x3) << 3)) & 0xff); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_read_config_word(struct pci_dev *dev, int offset, u16 *val) +{ + unsigned long reg; + reg = grackle_read(pci_config_addr(dev->bus->number, dev->devfn, + (offset & ~(0x3)))); + *val = ((reg >> ((offset & 0x3) << 3)) & 0xffff); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_read_config_dword(struct pci_dev *dev, int offset, u32 *val) +{ + *val = grackle_read(pci_config_addr(dev->bus->number, dev->devfn, + (offset & ~(0x3)))); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_write_config_byte(struct pci_dev *dev, int offset, u8 val) +{ + unsigned long reg; + int shifts = offset & 0x3; + unsigned int addr = pci_config_addr(dev->bus->number, dev->devfn, + (offset & ~(0x3))); + + reg = grackle_read(addr); + reg = (reg & ~(0xff << (shifts << 3))) | (val << (shifts << 3)); + grackle_write(addr, reg ); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_write_config_word(struct pci_dev *dev, int offset, u16 val) +{ + unsigned long reg; + int shifts = offset & 0x3; + unsigned int addr = pci_config_addr(dev->bus->number, dev->devfn, + (offset & ~(0x3))); + + reg = grackle_read(addr); + reg = (reg & ~(0xffff << (shifts << 3))) | (val << (shifts << 3)); + grackle_write(addr, reg ); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_write_config_dword(struct pci_dev *dev, int offset, u32 val) +{ + grackle_write(pci_config_addr(dev->bus->number, dev->devfn, + (offset & ~(0x3))), val); + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops gemini_pci_ops = +{ + gemini_pcibios_read_config_byte, + gemini_pcibios_read_config_word, + gemini_pcibios_read_config_dword, + gemini_pcibios_write_config_byte, + gemini_pcibios_write_config_word, + gemini_pcibios_write_config_dword +}; + +void __init gemini_pcibios_fixup(void) +{ + int i; + struct pci_dev *dev; + + pci_for_each_dev(dev) { + for(i = 0; i < 6; i++) { + if (dev->resource[i].flags & IORESOURCE_IO) { + dev->resource[i].start |= (0xfe << 24); + dev->resource[i].end |= (0xfe << 24); + } + } + } +} + + +/* The "bootloader" for Synergy boards does none of this for us, so we need to + lay it all out ourselves... --Dan */ +void __init gemini_find_bridges(void) +{ + struct pci_controller* hose; + + ppc_md.pcibios_fixup = gemini_pcibios_fixup; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + hose->ops = &gemini_pci_ops; +} diff -Nru a/arch/ppc/platforms/gemini_prom.S b/arch/ppc/platforms/gemini_prom.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/gemini_prom.S Tue Feb 19 18:08:57 2002 @@ -0,0 +1,98 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/kernel/gemini_prom.S + * + * Not really prom support code (yet), but sort of anti-prom code. The current + * bootloader does a number of things it shouldn't and doesn't do things that it + * should. The stuff in here is mainly a hodge-podge collection of setup code + * to get the board up and running. + * ---Dan + */ + +#include +#include +#include +#include +#include + +#define HID0_ABE (1<<3) + +/* + * On 750's the MMU is on when Linux is booted, so we need to clear out the + * bootloader's BAT settings, make sure we're in supervisor state (gotcha!), + * and turn off the MMU. + * + */ + +_GLOBAL(gemini_prom_init) +#ifdef CONFIG_SMP + /* Since the MMU's on, get stuff in rom space that we'll need */ + lis r4,GEMINI_CPUSTAT@h + ori r4,r4,GEMINI_CPUSTAT@l + lbz r5,0(r4) + andi. r5,r5,3 + mr r24,r5 /* cpu # used later on */ +#endif + mfmsr r4 + li r3,MSR_PR /* ensure supervisor! */ + ori r3,r3,MSR_IR|MSR_DR + andc r4,r4,r3 + mtmsr r4 + isync +#if 0 + /* zero out the bats now that the MMU is off */ +prom_no_mmu: + li r3,0 + mtspr IBAT0U,r3 + mtspr IBAT0L,r3 + mtspr IBAT1U,r3 + mtspr IBAT1L,r3 + mtspr IBAT2U,r3 + mtspr IBAT2L,r3 + mtspr IBAT3U,r3 + mtspr IBAT3L,r3 + + mtspr DBAT0U,r3 + mtspr DBAT0L,r3 + mtspr DBAT1U,r3 + mtspr DBAT1L,r3 + mtspr DBAT2U,r3 + mtspr DBAT2L,r3 + mtspr DBAT3U,r3 + mtspr DBAT3L,r3 +#endif + + /* the bootloader (as far as I'm currently aware) doesn't mess with page + tables, but since we're already here, might as well zap these, too */ + li r4,0 + mtspr SDR1,r4 + + li r4,16 + mtctr r4 + li r3,0 + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,1 + bdnz 3b + +#ifdef CONFIG_SMP + /* The 750 book (and Mot/IBM support) says that this will "assist" snooping + when in SMP. Not sure yet whether this should stay or leave... */ + mfspr r4,HID0 + ori r4,r4,HID0_ABE + mtspr HID0,r4 + sync +#endif /* CONFIG_SMP */ + blr + +/* apparently, SMon doesn't pay attention to HID0[SRST]. Disable the MMU and + branch to 0xfff00100 */ +_GLOBAL(_gemini_reboot) + lis r5,GEMINI_BOOT_INIT@h + ori r5,r5,GEMINI_BOOT_INIT@l + li r6,MSR_IP + mtspr SRR0,r5 + mtspr SRR1,r6 + rfi diff -Nru a/arch/ppc/platforms/gemini_serial.h b/arch/ppc/platforms/gemini_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/gemini_serial.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,44 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +#ifdef __KERNEL__ +#ifndef __ASMPPC_GEMINI_SERIAL_H +#define __ASMPPC_GEMINI_SERIAL_H + +#include +#include + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +/* Rate for the 24.576 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (24576000 / 16) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF) +#endif + +#define STD_SERIAL_PORT_DEFNS \ + { 0, BASE_BAUD, GEMINI_SERIAL_A, 15, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, GEMINI_SERIAL_B, 14, STD_COM_FLAGS }, /* ttyS1 */ \ + +#ifdef CONFIG_GEMINI_PU32 +#define PU32_SERIAL_PORT_DEFNS \ + { 0, BASE_BAUD, NULL, 0, STD_COM_FLAGS }, +#else +#define PU32_SERIAL_PORT_DEFNS +#endif + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DEFNS \ + PU32_SERIAL_PORT_DEFNS + +#endif +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/gemini_setup.c b/arch/ppc/platforms/gemini_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/gemini_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,591 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/platforms/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * Synergy Microsystems board support by Dan Cox (dan@synergymicro.com) + * + */ + +#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 + +void gemini_find_bridges(void); +static int gemini_get_clock_speed(void); +extern void gemini_pcibios_fixup(void); + +static char *gemini_board_families[] = { + "VGM", "VSS", "KGM", "VGR", "VCM", "VCS", "KCM", "VCR" +}; +static int gemini_board_count = sizeof(gemini_board_families) / + sizeof(gemini_board_families[0]); + +static unsigned int cpu_7xx[16] = { + 0, 15, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 +}; +static unsigned int cpu_6xx[16] = { + 0, 0, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 0, 12, 7, 0 +}; + +/* + * prom_init is the Gemini version of prom.c:prom_init. We only need + * the BSS clearing code, so I copied that out of prom.c. This is a + * lot simpler than hacking prom.c so it will build with Gemini. -VAL + */ + +#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) + +unsigned long +prom_init(void) +{ + unsigned long offset = reloc_offset(); + unsigned long phys; + extern char __bss_start, _end; + + /* First zero the BSS -- use memset, some arches don't have + * caches on yet */ + memset_io(PTRRELOC(&__bss_start),0 , &_end - &__bss_start); + + /* Default */ + phys = offset + KERNELBASE; + + gemini_prom_init(); + + return phys; +} + +int +gemini_show_cpuinfo(struct seq_file *m) +{ + unsigned char reg, rev; + char *family; + unsigned int type; + + reg = readb(GEMINI_FEAT); + family = gemini_board_families[((reg>>4) & 0xf)]; + if (((reg>>4) & 0xf) > gemini_board_count) + printk(KERN_ERR "cpuinfo(): unable to determine board family\n"); + + reg = readb(GEMINI_BREV); + type = (reg>>4) & 0xf; + rev = reg & 0xf; + + reg = readb(GEMINI_BECO); + + seq_printf(m, "machine\t\t: Gemini %s%d, rev %c, eco %d\n", + family, type, (rev + 'A'), (reg & 0xf)); + + seq_printf(m, "board\t\t: Gemini %s", family); + if (type > 9) + seq_printf(m, "%c", (type - 10) + 'A'); + else + seq_printf(m, "%d", type); + + seq_printf(m, ", rev %c, eco %d\n", (rev + 'A'), (reg & 0xf)); + + seq_printf(m, "clock\t\t: %dMhz\n", gemini_get_clock_speed()); + + return 0; +} + +static u_char gemini_openpic_initsenses[] = { + 1, + 1, + 1, + 1, + 0, + 0, + 1, /* remainder are level-triggered */ +}; + +#define GEMINI_MPIC_ADDR (0xfcfc0000) +#define GEMINI_MPIC_PCI_CFG (0x80005800) + +void __init gemini_openpic_init(void) +{ + + OpenPIC_Addr = (volatile struct OpenPIC *) + grackle_read(GEMINI_MPIC_PCI_CFG + 0x10); + OpenPIC_InitSenses = gemini_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof( gemini_openpic_initsenses ); + + ioremap( GEMINI_MPIC_ADDR, OPENPIC_SIZE); +} + + +extern unsigned long loops_per_jiffy; +extern int root_mountflags; +extern char cmd_line[]; + +void +gemini_heartbeat(void) +{ + static unsigned long led = GEMINI_LEDBASE+(4*8); + static char direction = 8; + + + /* We only want to do this on 1 CPU */ + if (smp_processor_id()) + return; + *(char *)led = 0; + if ( (led + direction) > (GEMINI_LEDBASE+(7*8)) || + (led + direction) < (GEMINI_LEDBASE+(4*8)) ) + direction *= -1; + led += direction; + *(char *)led = 0xff; + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; +} + +void __init gemini_setup_arch(void) +{ + extern char cmd_line[]; + + + loops_per_jiffy = 50000000/HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + /* bootable off CDROM */ + if (initrd_start) + ROOT_DEV = MKDEV(SCSI_CDROM_MAJOR, 0); + else +#endif + ROOT_DEV = to_kdev_t(0x0801); + + /* nothing but serial consoles... */ + sprintf(cmd_line, "%s console=ttyS0", cmd_line); + + printk("Boot arguments: %s\n", cmd_line); + + ppc_md.heartbeat = gemini_heartbeat; + ppc_md.heartbeat_reset = HZ/8; + ppc_md.heartbeat_count = 1; + + /* Lookup PCI hosts */ + gemini_find_bridges(); + /* take special pains to map the MPIC, since it isn't mapped yet */ + gemini_openpic_init(); + /* start the L2 */ + gemini_init_l2(); +} + + +int +gemini_get_clock_speed(void) +{ + unsigned long hid1, pvr; + int clock; + + pvr = mfspr(PVR); + hid1 = (mfspr(HID1) >> 28) & 0xf; + if (PVR_VER(pvr) == 8 || + PVR_VER(pvr) == 12) + hid1 = cpu_7xx[hid1]; + else + hid1 = cpu_6xx[hid1]; + + switch((readb(GEMINI_BSTAT) & 0xc) >> 2) { + + case 0: + default: + clock = (hid1*100)/3; + break; + + case 1: + clock = (hid1*125)/3; + break; + + case 2: + clock = (hid1*50); + break; + } + + return clock; +} + +void __init gemini_init_l2(void) +{ + unsigned char reg, brev, fam, creg; + unsigned long cache; + unsigned long pvr; + + reg = readb(GEMINI_L2CFG); + brev = readb(GEMINI_BREV); + fam = readb(GEMINI_FEAT); + pvr = mfspr(PVR); + + switch(PVR_VER(pvr)) { + + case 8: + if (reg & 0xc0) + cache = (((reg >> 6) & 0x3) << 28); + else + cache = 0x3 << 28; + +#ifdef CONFIG_SMP + /* Pre-3.0 processor revs had snooping errata. Leave + their L2's disabled with SMP. -- Dan */ + if (PVR_CFG(pvr) < 3) { + printk("Pre-3.0 750; L2 left disabled!\n"); + return; + } +#endif /* CONFIG_SMP */ + + /* Special case: VGM5-B's came before L2 ratios were set on + the board. Processor speed shouldn't be too high, so + set L2 ratio to 1:1.5. */ + if ((brev == 0x51) && ((fam & 0xa0) >> 4) == 0) + reg |= 1; + + /* determine best cache ratio based upon what the board + tells us (which sometimes _may_ not be true) and + the processor speed. */ + else { + if (gemini_get_clock_speed() > 250) + reg = 2; + } + break; + case 12: + { + static unsigned long l2_size_val = 0; + + if (!l2_size_val) + l2_size_val = _get_L2CR(); + cache = l2_size_val; + break; + } + case 4: + case 9: + creg = readb(GEMINI_CPUSTAT); + if (((creg & 0xc) >> 2) != 1) + printk("Dual-604 boards don't support the use of L2\n"); + else + writeb(1, GEMINI_L2CFG); + return; + default: + printk("Unknown processor; L2 left disabled\n"); + return; + } + + cache |= ((1<>2)&0x3) { + case 0: + default: + freq = 66667; + break; + case 1: + freq = 83000; + break; + case 2: + freq = 100000; + break; + } + + freq *= 1000; + divisor = 4; + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); +} + +unsigned long __init gemini_find_end_of_memory(void) +{ + unsigned long total; + unsigned char reg; + + reg = readb(GEMINI_MEMCFG); + total = ((1<<((reg & 0x7) - 1)) * + (8<<((reg >> 3) & 0x7))); + total *= (1024*1024); + return total; +} + +static void __init +gemini_map_io(void) +{ + io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); + io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); +} + +#ifdef CONFIG_SMP +static int +smp_gemini_probe(void) +{ + int i, nr; + + nr = (readb(GEMINI_CPUSTAT) & GEMINI_CPU_COUNT_MASK) >> 2; + if (nr == 0) + nr = 4; + + if (nr > 1) { + openpic_request_IPIs(); + for (i = 1; i < nr; ++i) + smp_hw_index[i] = i; + } + + return nr; +} + +static void +smp_gemini_kick_cpu(int nr) +{ + openpic_reset_processor_phys(1 << nr); + openpic_reset_processor_phys(0); +} + +static void +smp_gemini_setup_cpu(int cpu_nr) +{ + if (OpenPIC_Addr) + do_openpic_setup_cpu(); + if (cpu_nr > 0) + gemini_init_l2(); +} + +static struct smp_ops_t gemini_smp_ops = { + smp_openpic_message_pass, + smp_gemini_probe, + smp_gemini_kick_cpu, + smp_gemini_setup_cpu, +}; +#endif /* CONFIG_SMP */ + +void __init platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + int i; + + /* Restore BATs for now */ + mtspr(DBAT3U, 0xf0001fff); + mtspr(DBAT3L, 0xf000002a); + + parse_bootinfo(find_bootinfo()); + + for(i = 0; i < GEMINI_LEDS; i++) + gemini_led_off(i); + + ISA_DMA_THRESHOLD = 0; + DMA_MODE_READ = 0; + DMA_MODE_WRITE = 0; + +#ifdef CONFIG_BLK_DEV_INITRD + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif + + ppc_md.setup_arch = gemini_setup_arch; + ppc_md.show_cpuinfo = gemini_show_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = gemini_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = gemini_restart; + ppc_md.power_off = gemini_power_off; + ppc_md.halt = gemini_halt; + + ppc_md.time_init = gemini_time_init; + ppc_md.set_rtc_time = gemini_set_rtc_time; + ppc_md.get_rtc_time = gemini_get_rtc_time; + ppc_md.calibrate_decr = gemini_calibrate_decr; + + ppc_md.find_end_of_memory = gemini_find_end_of_memory; + ppc_md.setup_io_mappings = gemini_map_io; + + /* no keyboard/mouse/video stuff yet.. */ + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; + ppc_md.ppc_kbd_sysrq_xlate = NULL; + ppc_md.pcibios_fixup_bus = gemini_pcibios_fixup; + +#ifdef CONFIG_SMP + ppc_md.smp_ops = &gemini_smp_ops; +#endif /* CONFIG_SMP */ +} diff -Nru a/arch/ppc/platforms/hermes.h b/arch/ppc/platforms/hermes.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/hermes.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,27 @@ +/* + * Multidata HERMES-PRO ( / SL ) board specific definitions + * + * Copyright (c) 2000, 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_HERMES_H +#define __MACH_HERMES_H + +#include + +#include + +#define HERMES_IMMR_BASE 0xFF000000 /* phys. addr of IMMR */ +#define HERMES_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR HERMES_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE HERMES_IMAP_SIZE /* mapped size of IMMR area */ + +#define FEC_INTERRUPT 9 /* = SIU_LEVEL4 */ +#define CPM_INTERRUPT 11 /* = SIU_LEVEL5 (was: SIU_LEVEL2) */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_HERMES_H */ diff -Nru a/arch/ppc/platforms/iSeries_dma.c b/arch/ppc/platforms/iSeries_dma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/iSeries_dma.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1049 @@ +/* + * iSeries_dma.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * Dynamic DMA mapping support. + * + * Manages the TCE space assigned to this partition + * + * modeled from pci-dma.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pci_dev * iSeries_veth_dev = NULL; +struct pci_dev * iSeries_vio_dev = NULL; + +struct TceTable virtBusTceTable; /* Tce table for virtual bus */ + +struct TceTable * tceTables[256]; // Tce tables for 256 busses + // Bus 255 is the virtual bus + // zero indicates no bus defined + // allocates a contiguous range of tces (power-of-2 size) +static long alloc_tce_range( struct TceTable *, + unsigned order ); + // allocates a contiguous range of tces (power-of-2 size) + // assumes lock already held +static long alloc_tce_range_nolock( struct TceTable *, + unsigned order ); + // frees a contiguous range of tces (power-of-2 size) +static void free_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + // frees a contiguous rnage of tces (power-of-2 size) + // assumes lock already held +static void free_tce_range_nolock( struct TceTable *, + long tcenum, + unsigned order ); + // allocates a range of tces and sets them to the + // pages +static dma_addr_t get_tces( struct TceTable *, + unsigned order, + void *page, + unsigned numPages, + int tceType, + int direction ); +static void free_tces( struct TceTable *, + dma_addr_t tce, + unsigned order, + unsigned numPages ); +static long test_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr, unsigned long numTces ); + +static unsigned long num_tces_sg( struct scatterlist *sg, + int nents ); + +static dma_addr_t create_tces_sg( struct TceTable *tbl, + struct scatterlist *sg, + int nents, + unsigned numTces, + int tceType, + int direction ); + +static unsigned __inline__ count_leading_zeros32( unsigned long x ) +{ + unsigned lz; + asm("cntlzw %0,%1" : "=r"(lz) : "r"(x)); + return lz; +} + +static void __inline__ build_tce( struct TceTable * tbl, long tcenum, + unsigned long uaddr, int tceType, int direction ) +{ + union Tce tce; + + tce.wholeTce = 0; + tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; + // If for virtual bus + if ( tceType == TCE_VB ) { + tce.tceBits.valid = 1; + tce.tceBits.allIo = 1; + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.readWrite = 1; + } + // If for PCI bus + else { + tce.tceBits.readWrite = 1; // Read allowed + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.pciWrite = 1; + } + HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + +} + + + +// Build a TceTable structure. This contains a multi-level bit map which +// is used to manage allocation of the tce space. + +struct TceTable * build_tce_table( struct HvTceTableManagerCB * tceTableParms, + struct TceTable * tbl ) +{ + unsigned long bits, bytes, totalBytes; + unsigned long numBits[NUM_TCE_LEVELS], numBytes[NUM_TCE_LEVELS]; + unsigned i, k, m; + unsigned char * pos, * p, b; + + tbl->size = tceTableParms->size; + tbl->busNumber = tceTableParms->busNumber; + tbl->startOffset = tceTableParms->startOffset; + tbl->index = tceTableParms->index; + spin_lock_init( &(tbl->lock) ); + + tbl->mlbm.maxLevel = 0; + + // Compute number of bits and bytes for each level of the + // multi-level bit map + // + totalBytes = 0; + bits = tbl->size * (PAGE_SIZE / sizeof( union Tce )); + + for ( i=0; imlbm.level[i].map = pos; + tbl->mlbm.maxLevel = i; + + if ( numBits[i] & 1 ) { + p = pos + numBytes[i] - 1; + m = (( numBits[i] % 8) - 1) & 7; + *p = 0x80 >> m; +#ifdef DEBUG_TCE + printk("build_tce_table: level %d last bit %x\n", i, 0x80>>m ); +#endif + } + } + else + tbl->mlbm.level[i].map = 0; + /* see the comment up above on the totalBytes calculation for why we do this. */ + pos += (numBytes[i] + 7) & ~7; + tbl->mlbm.level[i].numBits = numBits[i]; + tbl->mlbm.level[i].numBytes = numBytes[i]; + + } + + // For the highest level, turn on all the bits + + i = tbl->mlbm.maxLevel; + p = tbl->mlbm.level[i].map; + m = numBits[i]; +#ifdef DEBUG_TCE + printk("build_tce_table: highest level (%d) has all bits set\n", i); +#endif + for (k=0; k= 8 ) { + // handle full bytes + *p++ = 0xff; + m -= 8; + } + else { + // handle the last partial byte + b = 0x80; + *p = 0; + while (m) { + *p |= b; + b >>= 1; + --m; + } + } + } + + return tbl; + +} + +static long alloc_tce_range( struct TceTable *tbl, unsigned order ) +{ + long retval; + unsigned long flags; + + // Lock the tce allocation bitmap + spin_lock_irqsave( &(tbl->lock), flags ); + + // Do the actual work + retval = alloc_tce_range_nolock( tbl, order ); + + // Unlock the tce allocation bitmap + spin_unlock_irqrestore( &(tbl->lock), flags ); + + return retval; +} + +static long alloc_tce_range_nolock( struct TceTable *tbl, unsigned order ) +{ + unsigned long numBits, numBytes; + unsigned long i, bit, block, mask; + long tcenum; + u32 * map; + + // If the order (power of 2 size) requested is larger than our + // biggest, indicate failure + if ( order > tbl->mlbm.maxLevel ) { +#ifdef DEBUG_TCE + printk("alloc_tce_range_nolock: invalid order requested, order = %d\n", order ); +#endif + return -1; + } + + numBits = tbl->mlbm.level[order].numBits; + numBytes = tbl->mlbm.level[order].numBytes; + map = (u32 *)(tbl->mlbm.level[order].map); + + // Initialize return value to -1 (failure) + tcenum = -1; + + // Loop through the bytes of the bitmap + for (i=0; i> bit); + *map &= mask; + // compute the index into our tce table for + // the first tce in the block +#ifdef DEBUG_TCE + printk("alloc_tce_range_nolock: allocating block %ld, (byte=%ld, bit=%ld) order %d\n", block, i, bit, order ); +#endif + tcenum = block << order; + break; + } + ++map; + } + +#ifdef DEBUG_TCE + if ( tcenum == -1 ) { + printk("alloc_tce_range_nolock: no available blocks of order = %d\n", order ); + if ( order < tbl->mlbm.maxLevel ) + printk("alloc_tce_range_nolock: trying next bigger size\n" ); + else + printk("alloc_tce_range_nolock: maximum size reached...failing\n"); + } +#endif + + // If no block of the requested size was found, try the next + // size bigger. If one of those is found, return the second + // half of the block to freespace and keep the first half + if ( ( tcenum == -1 ) && ( order < tbl->mlbm.maxLevel ) ) { + tcenum = alloc_tce_range_nolock( tbl, order+1 ); + if ( tcenum != -1 ) { + free_tce_range_nolock( tbl, tcenum+(1<lock), flags ); + + // Do the actual work + free_tce_range_nolock( tbl, tcenum, order ); + + // Unlock the tce allocation bitmap + spin_unlock_irqrestore( &(tbl->lock), flags ); + +} + +static void free_tce_range_nolock( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + unsigned char * map, * bytep; + + if ( order > tbl->mlbm.maxLevel ) { + printk("free_tce_range: order too large, order = %d\n", order ); + return; + } + + block = tcenum >> order; + if ( tcenum != (block << order ) ) { + printk("free_tce_range: tcenum %lx is not on appropriate boundary for order %x\n", tcenum, order ); + return; + } + if ( block >= tbl->mlbm.level[order].numBits ) { + printk("free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", tcenum, order, tbl->mlbm.level[order].numBits ); + return; + } +#ifdef DEBUG_TCE + if ( test_tce_range( tbl, tcenum, order ) ) { + printk("free_tce_range: freeing range not completely allocated.\n"); + printk("free_tce_range: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); + } +#endif + map = tbl->mlbm.level[order].map; + byte = block / 8; + bit = block % 8; + mask = 0x80 >> bit; + bytep = map + byte; +#ifdef DEBUG_TCE + printk("free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n",block, byte, bit, order); + if ( *bytep & mask ) + printk("free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); +#endif + *bytep |= mask; + + // If there is a higher level in the bit map than this we may be + // able to buddy up this block with its partner. + // If this is the highest level we can't buddy up + // If this level has an odd number of bits and + // we are freeing the last block we can't buddy up + // don't buddy up if it's in the first 1/4 of the bits + if ( ( order < tbl->mlbm.maxLevel ) && + ( ( 0 == ( tbl->mlbm.level[order].numBits & 1 ) ) || + ( block < tbl->mlbm.level[order].numBits-1 ) ) && + ( block > (tbl->mlbm.level[order].numBits/4) ) ) { + + // See if we can buddy up the block we just freed + bit &= 6; // get to the first of the buddy bits + mask = 0xc0 >> bit; // build two bit mask + b = *bytep & mask; // Get the two bits + if ( 0 == (b ^ mask) ) { // If both bits are on + // both of the buddy blocks are free we can combine them + *bytep ^= mask; // turn off the two bits + block = ( byte * 8 ) + bit; // block of first of buddies + tcenum = block << order; + // free the buddied block +#ifdef DEBUG_TCE + printk("free_tce_range: buddying up block %ld and block %ld\n", block, block+1); +#endif + free_tce_range_nolock( tbl, tcenum, order+1 ); + } + } +} + +static long test_tce_range( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + long retval, retLeft, retRight; + unsigned char * map; + + map = tbl->mlbm.level[order].map; + block = tcenum >> order; + byte = block / 8; // Byte within bitmap + bit = block % 8; // Bit within byte + mask = 0x80 >> bit; + b = (*(map+byte) & mask ); // 0 if block is allocated, else free + if ( b ) + retval = 1; // 1 == block is free + else + retval = 0; // 0 == block is allocated + // Test bits at all levels below this to ensure that all agree + + if (order) { + retLeft = test_tce_range( tbl, tcenum, order-1 ); + retRight = test_tce_range( tbl, tcenum+(1<<(order-1)), order-1 ); + if ( retLeft || retRight ) { + retval = 2; + } + } + + // Test bits at all levels above this to ensure that all agree + + return retval; +} + +static dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int tceType, int direction ) +{ + long tcenum; + unsigned long uaddr; + unsigned i; + dma_addr_t retTce = NO_TCE; + + uaddr = (unsigned long)page & PAGE_MASK; + + // Allocate a range of tces + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + // We got the tces we wanted + tcenum += tbl->startOffset; // Offset into real TCE table + retTce = tcenum << PAGE_SHIFT; // Set the return dma address + // Setup a tce for each page + for (i=0; isize * (PAGE_SIZE / sizeof(union Tce))) - 1; + + tcenum = dma_addr >> PAGE_SHIFT; + tcenum -= tbl->startOffset; + + if ( tcenum > maxTcenum ) { + printk("free_tces: tcenum > maxTcenum, tcenum = %ld, maxTcenum = %ld\n", + tcenum, maxTcenum ); + printk("free_tces: TCE Table at %16lx\n", (unsigned long)tbl ); + printk("free_tces: bus# %lu\n", (unsigned long)tbl->busNumber ); + printk("free_tces: size %lu\n", (unsigned long)tbl->size ); + printk("free_tces: startOff %lu\n", (unsigned long)tbl->startOffset ); + printk("free_tces: index %lu\n", (unsigned long)tbl->index ); + return; + } + + freeTce = tcenum; + + for (i=0; iindex, (u64)tcenum, tce.wholeTce ); + ++tcenum; + } + + free_tce_range( tbl, freeTce, order ); + +} + +void __init create_virtual_bus_tce_table(void) +{ + struct TceTable * t; + struct HvTceTableManagerCB virtBusTceTableParms; + u64 absParmsPtr; + + virtBusTceTableParms.busNumber = 255; /* Bus 255 is the virtual bus */ + virtBusTceTableParms.virtualBusFlag = 0xff; /* Ask for virtual bus */ + + absParmsPtr = virt_to_absolute( (u32)&virtBusTceTableParms ); + HvCallXm_getTceTableParms( absParmsPtr ); + + t = build_tce_table( &virtBusTceTableParms, &virtBusTceTable ); + if ( t ) { + tceTables[255] = t; + printk("Virtual Bus TCE table built successfully.\n"); + printk(" TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + printk(" TCE table token = %d\n", + (unsigned)t->index ); + printk(" TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else + printk("Virtual Bus TCE table failed.\n"); +} + +void __init create_pci_bus_tce_table( unsigned busNumber ) +{ + struct TceTable * t; + struct TceTable * newTceTable; + struct HvTceTableManagerCB pciBusTceTableParms; + u64 absParmsPtr; + unsigned i; + + if ( busNumber > 254 ) { + printk("PCI Bus TCE table failed.\n"); + printk(" Invalid bus number %u\n", busNumber ); + return; + } + + newTceTable = kmalloc( sizeof(struct TceTable), GFP_KERNEL ); + + pciBusTceTableParms.busNumber = busNumber; + pciBusTceTableParms.virtualBusFlag = 0; + + absParmsPtr = virt_to_absolute( (u32)&pciBusTceTableParms ); + HvCallXm_getTceTableParms( absParmsPtr ); + + // Determine if the table identified by the index and startOffset + // returned by the hypervisor for this bus has already been created. + + for ( i=0; i<255; ++i) { + t = tceTables[i]; + if ( t ) { + if ( ( t->index == pciBusTceTableParms.index ) && + ( t->startOffset == pciBusTceTableParms.startOffset ) ) { + if ( t->size != pciBusTceTableParms.size ) + printk("PCI Bus %d Shares a TCE table with Bus %d, but sizes differ\n", busNumber, i ); + else + printk("PCI Bus %d Shares a TCE table with bus %d\n", busNumber, i ); + tceTables[busNumber] = t; + break; + } + } + } + + if ( ! tceTables[busNumber] ) { + + t = build_tce_table( &pciBusTceTableParms, newTceTable ); + if ( t ) { + tceTables[busNumber] = t; + printk("PCI Bus %d TCE table built successfully.\n", busNumber); + printk(" TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + printk(" TCE table token = %d\n", + (unsigned)t->index ); + printk(" TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else { + kfree( newTceTable ); + printk("PCI Bus %d TCE table failed.\n", busNumber ); + } + } +} + + +// Allocates a contiguous real buffer and creates TCEs over it. +// Returns the virtual address of the buffer and sets dma_handle +// to the dma address (tce) of the first page. +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + struct TceTable * tbl; + void *ret = NULL; + unsigned order, nPages, bus; + dma_addr_t tce; + int tceType; + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + // If no pci_dev then use virtual bus + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + // Get the iSeries bus # to use as an index + // into the TCE table array + bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return NULL; +#endif /* CONFIG_PCI */ + } + + tbl = tceTables[bus]; + if ( tbl ) { + // Alloc enough pages (and possibly more) + ret = (void *)__get_free_pages( GFP_ATOMIC, order ); + if ( ret ) { + // Page allocation succeeded + memset(ret, 0, nPages << PAGE_SHIFT); + // Set up tces to cover the allocated range + tce = get_tces( tbl, order, ret, nPages, tceType, + PCI_DMA_BIDIRECTIONAL ); + if ( tce == NO_TCE ) { +#ifdef DEBUG_TCE + printk("pci_alloc_consistent: get_tces failed\n" ); +#endif + free_pages( (unsigned long)ret, order ); + ret = NULL; + } + else + { + *dma_handle = tce; + } + } +#ifdef DEBUG_TCE + else + printk("pci_alloc_consistent: __get_free_pages failed for order = %d\n", order); +#endif + } + + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + struct TceTable * tbl; + unsigned order, nPages, bus; + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + // If no pci_dev then use virtual bus + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + // Get the iSeries bus # to use as an index + // into the TCE table array + bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + printk("pci_free_consistent: invalid bus # %d\n", bus ); + printk("pci_free_consistent: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + tbl = tceTables[bus]; + + if ( tbl ) { + free_tces( tbl, dma_handle, order, nPages ); + free_pages( (unsigned long)vaddr, order ); + } +} + +// Creates TCEs for a user provided buffer. The user buffer must be +// contiguous real kernel storage (not vmalloc). The address of the buffer +// passed here is the kernel (virtual) address of the buffer. The buffer +// need not be page aligned, the dma_addr_t returned will point to the same +// byte within the page as vaddr. +dma_addr_t pci_map_single( struct pci_dev *hwdev, void *vaddr, size_t size, int direction ) +{ + struct TceTable * tbl; + dma_addr_t dma_handle; + unsigned long uaddr; + unsigned order, nPages, bus; + int tceType; + + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_handle = NO_TCE; + + uaddr = (unsigned long)vaddr; + nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + // If no pci_dev then use virtual bus + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + // Get the iSeries bus # to use as an index + // into the TCE table array + bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return NO_TCE; +#endif /* CONFIG_PCI */ + + } + + tbl = tceTables[bus]; + + if ( tbl ) { + dma_handle = get_tces( tbl, order, vaddr, nPages, tceType, + direction ); + dma_handle |= ( uaddr & ~PAGE_MASK ); + } + + return dma_handle; +} + +void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ) +{ + struct TceTable * tbl; + unsigned order, nPages, bus; + + if ( direction == PCI_DMA_NONE ) + BUG(); + + nPages = PAGE_ALIGN( dma_handle + size ) - ( dma_handle & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + // If no pci_dev then use virtual bus + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + // Get the iSeries bus # to use as an index + // into the TCE table array + bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + printk("pci_unmap_single: invalid bus # %d\n", bus ); + printk("pci_unmap_single: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + tbl = tceTables[bus]; + + if ( tbl ) + free_tces( tbl, dma_handle, order, nPages ); + +} + +// Figure out how many TCEs are actually going to be required +// to map this scatterlist. This code is not optimal. It +// takes into account the case where entry n ends in the same +// page in which entry n+1 starts. It does not handle the +// general case of entry n ending in the same page in which +// entry m starts. + +static unsigned long num_tces_sg( struct scatterlist *sg, int nents ) +{ + unsigned long nTces, numPages, startPage, endPage, prevEndPage; + unsigned i; + + prevEndPage = 0; + nTces = 0; + + for (i=0; iaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + // Simple optimization: if the previous entry ended + // in the same page in which this entry starts + // then we can reduce the required pages by one. + // This matches assumptions in fill_scatterlist_sg and + // create_tces_sg + if ( startPage == prevEndPage ) + --numPages; + nTces += numPages; + prevEndPage = endPage; + sg++; + } + return nTces; +} + +// Fill in the dma data in the scatterlist +// return the number of dma sg entries created +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr , unsigned long numTces) +{ + struct scatterlist *dma_sg; + u32 cur_start_dma; + unsigned long cur_len_dma, cur_end_virt, uaddr; + unsigned num_dma_ents; + + dma_sg = sg; + num_dma_ents = 1; + + // Process the first sg entry + cur_start_dma = dma_addr + ((unsigned long)sg->address & (~PAGE_MASK)); + cur_len_dma = sg->length; + // cur_end_virt holds the address of the byte immediately after the + // end of the current buffer. + cur_end_virt = (unsigned long)sg->address + cur_len_dma; + // Later code assumes that unused sg->dma_address and sg->dma_length + // fields will be zero. Other archs seem to assume that the user + // (device driver) guarantees that...I don't want to depend on that + sg->dma_address = sg->dma_length = 0; + + // Process the rest of the sg entries + while (--nents) { + ++sg; + // Clear possibly unused fields. Note: sg >= dma_sg so + // this can't be clearing a field we've already set + sg->dma_address = sg->dma_length = 0; + + // Check if it is possible to make this next entry + // contiguous (in dma space) with the previous entry. + + // The entries can be contiguous in dma space if + // the previous entry ends immediately before the + // start of the current entry (in virtual space) + // or if the previous entry ends at a page boundary + // and the current entry starts at a page boundary. + uaddr = (unsigned long)sg->address; + if ( ( uaddr != cur_end_virt ) && + ( ( ( uaddr | cur_end_virt ) & (~PAGE_MASK) ) || + ( ( uaddr & PAGE_MASK ) == ( ( cur_end_virt-1 ) & PAGE_MASK ) ) ) ) { + // This entry can not be contiguous in dma space. + // save the previous dma entry and start a new one + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + ++dma_sg; + ++num_dma_ents; + + cur_start_dma += cur_len_dma-1; + // If the previous entry ends and this entry starts + // in the same page then they share a tce. In that + // case don't bump cur_start_dma to the next page + // in dma space. This matches assumptions made in + // num_tces_sg and create_tces_sg. + if ((uaddr & PAGE_MASK) == ((cur_end_virt-1) & PAGE_MASK)) + cur_start_dma &= PAGE_MASK; + else + cur_start_dma = PAGE_ALIGN(cur_start_dma+1); + cur_start_dma += ( uaddr & (~PAGE_MASK) ); + cur_len_dma = 0; + } + // Accumulate the length of this entry for the next + // dma entry + cur_len_dma += sg->length; + cur_end_virt = uaddr + sg->length; + } + // Fill in the last dma entry + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + if ((((cur_start_dma +cur_len_dma - 1)>> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1) != numTces) + { + printk("fill_scatterlist_sg: numTces %ld, used tces %d\n", + numTces, + (unsigned)(((cur_start_dma + cur_len_dma - 1) >> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1)); + } + + + return num_dma_ents; +} + +// Call the hypervisor to create the TCE entries. +// return the number of TCEs created +static dma_addr_t create_tces_sg( struct TceTable *tbl, struct scatterlist *sg, + int nents, unsigned numTces, int tceType, int direction ) +{ + unsigned order, i, j; + unsigned long startPage, endPage, prevEndPage, numPages, uaddr; + long tcenum, starttcenum; + dma_addr_t dmaAddr; + + dmaAddr = NO_TCE; + + order = get_order( numTces << PAGE_SHIFT ); + // allocate a block of tces + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + tcenum += tbl->startOffset; + starttcenum = tcenum; + dmaAddr = tcenum << PAGE_SHIFT; + prevEndPage = 0; + for (j=0; jaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + + uaddr = (unsigned long)sg->address; + + // If the previous entry ended in the same page that + // the current page starts then they share that + // tce and we reduce the number of tces we need + // by one. This matches assumptions made in + // num_tces_sg and fill_scatterlist_sg + if ( startPage == prevEndPage ) { + --numPages; + uaddr += PAGE_SIZE; + } + + for (i=0; idma_address = pci_map_single( hwdev, sg->address, + sg->length, direction ); + sg->dma_length = sg->length; + return 1; + } + + if ( direction == PCI_DMA_NONE ) + BUG(); + + // If no pci_dev then use virtual bus + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + // Get the iSeries bus # to use as an index + // into the TCE table array + bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return 0; +#endif /* CONFIG_PCI */ + } + + tbl = tceTables[bus]; + + if ( tbl ) { + // Compute the number of tces required + numTces = num_tces_sg( sg, nents ); + // Create the tces and get the dma address + dma_handle = create_tces_sg( tbl, sg, nents, numTces, + tceType, direction ); + + // Fill in the dma scatterlist + num_dma = fill_scatterlist_sg( sg, nents, dma_handle, numTces ); + } + + return num_dma; +} + +void pci_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int direction ) +{ + struct TceTable * tbl; + unsigned order, numTces, bus, i; + dma_addr_t dma_end_page, dma_start_page; + + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_start_page = sg->dma_address & PAGE_MASK; + for ( i=nelms; i>0; --i ) { + unsigned k = i - 1; + if ( sg[k].dma_length ) { + dma_end_page = ( sg[k].dma_address + + sg[k].dma_length - 1 ) & PAGE_MASK; + break; + } + } + + numTces = ((dma_end_page - dma_start_page ) >> PAGE_SHIFT) + 1; + order = get_order( numTces << PAGE_SHIFT ); + + // If no pci_dev then use virtual bus + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + // Get the iSeries bus # to use as an index + // into the TCE table array + bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + printk("pci_unmap_sg: invalid bus # %d\n", bus ); + printk("pci_unmap_sg: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + + tbl = tceTables[bus]; + + if ( tbl ) + free_tces( tbl, dma_start_page, order, numTces ); + +} + diff -Nru a/arch/ppc/platforms/iSeries_pic.c b/arch/ppc/platforms/iSeries_pic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/iSeries_pic.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,85 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/platforms/iSeries_pic.c + * + */ + + +#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 +extern void iSeries_smp_message_recv( struct pt_regs * ); +extern int is_soft_enabled(void); + +extern void __no_lpq_restore_flags(unsigned long flags); +extern void iSeries_smp_message_recv( struct pt_regs * ); +unsigned lpEvent_count = 0; + +int do_IRQ(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + unsigned long flags; + struct Paca * paca; + struct ItLpQueue *lpq; + + if ( is_soft_enabled() ) + BUG(); + + hardirq_enter( cpu ); + + paca = (struct Paca *)mfspr(SPRG1); + +#ifdef CONFIG_SMP + if ( paca->xLpPacaPtr->xIpiCnt ) { + paca->xLpPacaPtr->xIpiCnt = 0; + iSeries_smp_message_recv( regs ); + } +#endif /* CONFIG_SMP */ + + lpq = paca->lpQueuePtr; + if ( lpq ) { + __save_flags( flags ); + __cli(); + lpEvent_count += ItLpQueue_process( lpq, regs ); + __restore_flags( flags ); + } + + hardirq_exit( cpu ); + + if ( paca->xLpPacaPtr->xDecrInt ) { + paca->xLpPacaPtr->xDecrInt = 0; + /* Signal a fake decrementer interrupt */ + timer_interrupt( regs ); + } + + if (softirq_pending(cpu)) + do_softirq(); + return 1; /* lets ret_from_int know we can do checks */ +} diff -Nru a/arch/ppc/platforms/iSeries_setup.c b/arch/ppc/platforms/iSeries_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/iSeries_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,822 @@ +/* + * + * + * Copyright (c) 2000 Mike Corrigan + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: iSeries_setup.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM iSeries LPAR. Adapted from original code by Grant Erickson and + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "iSeries_setup.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Function Prototypes */ + +extern void abort(void); +static void build_iSeries_Memory_Map( void ); +static void setup_iSeries_cache_sizes( void ); +extern void iSeries_pci_Initialize(void); +static int iSeries_show_cpuinfo(struct seq_file *m); +static int iSeries_show_percpuinfo(struct seq_file *m, int i); +extern struct pci_ops iSeries_pci_ops; + +/* Global Variables */ + +unsigned short iSeries_icache_line_size = 0; +unsigned short iSeries_dcache_line_size = 0; +unsigned short iSeries_icache_lines_per_page = 0; +unsigned short iSeries_dcache_lines_per_page = 0; +unsigned short iSeries_log_icache_line_size = 0; +unsigned short iSeries_log_dcache_line_size = 0; + +unsigned long procFreqHz = 0; +unsigned long procFreqMhz = 0; +unsigned long procFreqMhzHundreths = 0; + +unsigned long tbFreqHz = 0; +unsigned long tbFreqMhz = 0; +unsigned long tbFreqMhzHundreths = 0; + +unsigned long decr_overclock = 8; +unsigned long decr_overclock_proc0 = 8; +unsigned long decr_overclock_set = 0; +unsigned long decr_overclock_proc0_set = 0; + +extern unsigned long embedded_sysmap_start; +extern unsigned long embedded_sysmap_end; + +extern unsigned long sysmap; +extern unsigned long sysmap_size; +extern unsigned long end_of_DRAM; // Defined in ppc/mm/init.c + +#ifdef CONFIG_SMP +extern struct smp_ops_t iSeries_smp_ops; +#endif /* CONFIG_SMP */ + +/* XXX for now... */ +#ifndef CONFIG_PCI +unsigned long isa_io_base; +#endif + +/* + * void __init platform_init() + * + * Description: + * This routine... + * + * Input(s): + * r3 - Optional pointer to a board information structure. + * r4 - Optional pointer to the physical starting address of the init RAM + * disk. + * r5 - Optional pointer to the physical ending address of the init RAM + * disk. + * r6 - Optional pointer to the physical starting address of any kernel + * command-line parameters. + * r7 - Optional pointer to the physical ending address of any kernel + * command-line parameters. + * + * Output(s): + * N/A + * + * Returns: + * N/A + * + */ + +extern int rd_size; // Defined in drivers/block/rd.c +extern u64 next_jiffy_update_tb[]; +extern u64 get_tb64(void); + +unsigned long __init iSeries_find_end_of_memory(void) +{ + /* totalLpChunks contains the size of memory (in units of 256K) */ + unsigned long memory_end = (totalLpChunks << 18); + + #ifndef CONFIG_HIGHMEM + /* Max memory if highmem is not configured is 768 MB */ + if (memory_end > (768 << 20)) + memory_end = 768 << 20; + #endif /* CONFIG_HIGHMEM */ + + return memory_end; +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured and there is + * a non-zero starting address for it, set it up + */ + if ( xNaca.xRamDisk ) { + initrd_start = xNaca.xRamDisk + KERNELBASE; + initrd_end = initrd_start + xNaca.xRamDiskSize * PAGE_SIZE; + initrd_below_start_ok = 1; // ramdisk in kernel space + ROOT_DEV = MKDEV( RAMDISK_MAJOR, 0 ); + + if ( ((rd_size*1024)/PAGE_SIZE) < xNaca.xRamDiskSize ) + rd_size = (xNaca.xRamDiskSize*PAGE_SIZE)/1024; + } else + +#endif /* CONFIG_BLK_DEV_INITRD */ +#if CONFIG_VIODASD_IDE + { + ROOT_DEV = MKDEV( IDE0_MAJOR, 1 ); + } +#elif defined(CONFIG_VIODASD) + { + ROOT_DEV = MKDEV( VIODASD_MAJOR, 1 ); + } +#endif /* CONFIG_VIODASD_IDE */ + + /* If an embedded System.map has been added to the kernel, + * set it up. + */ + if ( embedded_sysmap_start ) { + sysmap = embedded_sysmap_start + KERNELBASE; + sysmap_size = embedded_sysmap_end - embedded_sysmap_start; + } + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *)(r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6 + KERNELBASE)); + } + + /* Initialize the table which translate Linux physical addresses to + * iSeries absolute addresses + */ + + build_iSeries_Memory_Map(); + + setup_iSeries_cache_sizes(); + + /* Initialize machine-dependency vectors */ + + ppc_md.setup_arch = iSeries_setup_arch; + ppc_md.show_cpuinfo = iSeries_show_cpuinfo; + ppc_md.show_percpuinfo = iSeries_show_percpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = iSeries_init_IRQ; + ppc_md.get_irq = iSeries_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = iSeries_restart; + ppc_md.power_off = iSeries_power_off; + ppc_md.halt = iSeries_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = iSeries_set_rtc_time; + ppc_md.get_rtc_time = iSeries_get_rtc_time; + ppc_md.calibrate_decr = iSeries_calibrate_decr; + ppc_md.progress = iSeries_progress; + ppc_md.find_end_of_memory = iSeries_find_end_of_memory; + + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; +#ifdef CONFIG_PCI + ppc_md.pcibios_fixup_bus = iSeries_fixup_bus; + ppc_md.pcibios_fixup = iSeries_fixup; +#else + ppc_md.pcibios_fixup_bus = NULL; + ppc_md.pcibios_fixup = NULL; +#endif /* CONFIG_PCI */ + +#if defined(CONFIG_MAGIC_SYSRQ) + ppc_md.ppc_kbd_sysrq_xlate = NULL; +#endif + +#ifdef CONFIG_SMP + ppc_md.smp_ops = &iSeries_smp_ops; +#endif /* CONFIG_SMP */ + + // Associate Lp Event Queue 0 with processor 0 + HvCallEvent_setLpEventQueueInterruptProc( 0, 0 ); + + { + // copy the command line parameter from the primary VSP + char *p, *q; + HvCallEvent_dmaToSp( cmd_line, + 2*64*1024, + 256, + HvLpDma_Direction_RemoteToLocal ); + p = q = cmd_line + 255; + while( p > cmd_line ) { + if ((*p == 0) || (*p == ' ') || (*p == '\n')) + --p; + else + break; + } + if ( p < q ) + *(p+1) = 0; + } + + next_jiffy_update_tb[0] = get_tb64(); + + iSeries_proc_early_init(); + + mf_init(); + + iSeries_proc_callback( &pmc_proc_init ); + + return; +} + +/* + * The iSeries may have very large memories ( > 128 GB ) and a partition + * may get memory in "chunks" that may be anywhere in the 2**52 real + * address space. The chunks are 256K in size. To map this to the + * memory model Linux expects, the iSeries specific code builds a + * translation table to translate what Linux thinks are "physical" + * addresses to the actual real addresses. This allows us to make + * it appear to Linux that we have contiguous memory starting at + * physical address zero while in fact this could be far from the truth. + * To avoid confusion, I'll let the words physical and/or real address + * apply to the Linux addresses while I'll use "absolute address" to + * refer to the actual hardware real address. + * + * build_iSeries_Memory_Map gets information from the Hypervisor and + * looks at the Main Store VPD to determine the absolute addresses + * of the memory that has been assigned to our partition and builds + * a table used to translate Linux's physical addresses to these + * absolute addresses. Absolute addresses are needed when + * communicating with the hypervisor (e.g. to build HPT entries) + */ + +static void __init build_iSeries_Memory_Map(void) +{ + u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize; + u32 hptFirstChunk, hptLastChunk, hptSizeChunks; + u32 absAddrHi, absAddrLo; + u32 nextPhysChunk; + u32 holeFirstChunk, holeSizeChunks; + u32 totalChunks,moreChunks; + u32 currChunk, thisChunk, absChunk; + u32 currDword; + u32 chunkBit; + u64 holeStart, holeEnd, holeSize; + u64 map; + struct IoHriMainStoreSegment4 * msVpd; + + // Get absolute address of our load area + // and map it to physical address 0 + // This guarantees that the loadarea ends up at physical 0 + // otherwise, it might not be returned by PLIC as the first + // chunks + + loadAreaFirstChunk = (u32)(itLpNaca.xLoadAreaAddr >> 18); + loadAreaSize = itLpNaca.xLoadAreaChunks; + + loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; + + // Get absolute address of our HPT and remember it so + // we won't map it to any physical address + + hptFirstChunk = (u32)(HvCallHpt_getHptAddress() >> 18 ); + hptSizeChunks = (u32)(HvCallHpt_getHptPages() >> 6 ); + hptLastChunk = hptFirstChunk + hptSizeChunks - 1; + + loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; + + absAddrLo = loadAreaFirstChunk << 18; + absAddrHi = loadAreaFirstChunk >> 14; + + printk( "Mapping load area - physical addr = 0, absolute addr = %08x%08x\n", + absAddrHi, absAddrLo ); + printk( "Load area size %dK\n", loadAreaSize*256 ); + + nextPhysChunk = 0; + + for ( absChunk = loadAreaFirstChunk; absChunk <= loadAreaLastChunk; ++absChunk ) { + if ( ( absChunk < hptFirstChunk ) || + ( absChunk > hptLastChunk ) ) { + msChunks[nextPhysChunk] = absChunk; + ++nextPhysChunk; + } + } + // Get absolute address of our HPT and remember it so + // we won't map it to any physical address + hptFirstChunk = (u32)(HvCallHpt_getHptAddress() >> 18 ); + hptSizeChunks = (u32)(HvCallHpt_getHptPages() >> 6 ); + hptLastChunk = hptFirstChunk + hptSizeChunks - 1; + absAddrLo = hptFirstChunk << 18; + absAddrHi = hptFirstChunk >> 14; + + printk( "HPT absolute addr = %08x%08x, size = %dK\n", + absAddrHi, absAddrLo, hptSizeChunks*256 ); + + // Determine if absolute memory has any + // holes so that we can interpret the + // access map we get back from the hypervisor + // correctly. + + msVpd = (struct IoHriMainStoreSegment4 *)xMsVpd; + holeStart = msVpd->nonInterleavedBlocksStartAdr; + holeEnd = msVpd->nonInterleavedBlocksEndAdr; + holeSize = holeEnd - holeStart; + + if ( holeSize ) { + holeStart = holeStart & 0x000fffffffffffff; + holeStart = holeStart >> 18; + holeFirstChunk = (u32)holeStart; + holeSize = holeSize >> 18; + holeSizeChunks = (u32)holeSize; + printk( "Main store hole: start chunk = %0x, size = %0x chunks\n", + holeFirstChunk, holeSizeChunks ); + } + else { + holeFirstChunk = 0xffffffff; + holeSizeChunks = 0; + } + + // Process the main store access map from the hypervisor + // to build up our physical -> absolute translation table + + totalChunks = (u32)HvLpConfig_getMsChunks(); + + if ((totalChunks-hptSizeChunks) > 16384) { + panic("More than 4GB of memory assigned to this partition"); + } + + currChunk = 0; + currDword = 0; + moreChunks = totalChunks; + + while ( moreChunks ) { + map = HvCallSm_get64BitsOfAccessMap( itLpNaca.xLpIndex, + currDword ); + thisChunk = currChunk; + while ( map ) { + chunkBit = map >> 63; + map <<= 1; + if ( chunkBit ) { + --moreChunks; + absChunk = thisChunk; + if ( absChunk >= holeFirstChunk ) + absChunk += holeSizeChunks; + if ( ( ( absChunk < hptFirstChunk ) || + ( absChunk > hptLastChunk ) ) && + ( ( absChunk < loadAreaFirstChunk ) || + ( absChunk > loadAreaLastChunk ) ) ) { +// printk( "Mapping physical = %0x to absolute %0x for 256K\n", nextPhysChunk << 18, absChunk << 18 ); + msChunks[nextPhysChunk] = absChunk; + ++nextPhysChunk; + } + } + ++thisChunk; + } + ++currDword; + currChunk += 64; + } + + // main store size (in chunks) is + // totalChunks - hptSizeChunks + // which should be equal to + // nextPhysChunk + + totalLpChunks = nextPhysChunk; + +} + +/* + * Set up the variables that describe the cache line sizes + * for this machine. + */ + +static void __init setup_iSeries_cache_sizes(void) +{ + unsigned i,n; + iSeries_icache_line_size = xIoHriProcessorVpd[0].xInstCacheOperandSize; + iSeries_dcache_line_size = xIoHriProcessorVpd[0].xDataCacheOperandSize; + iSeries_icache_lines_per_page = PAGE_SIZE / iSeries_icache_line_size; + iSeries_dcache_lines_per_page = PAGE_SIZE / iSeries_dcache_line_size; + i = iSeries_icache_line_size; + n = 0; + while ((i=(i/2))) ++n; + iSeries_log_icache_line_size = n; + i = iSeries_dcache_line_size; + n = 0; + while ((i=(i/2))) ++n; + iSeries_log_dcache_line_size = n; + printk( "D-cache line size = %d (log = %d)\n", + (unsigned)iSeries_dcache_line_size, + (unsigned)iSeries_log_dcache_line_size ); + printk( "I-cache line size = %d (log = %d)\n", + (unsigned)iSeries_icache_line_size, + (unsigned)iSeries_log_icache_line_size ); + +} + + +int piranha_simulator = 0; + +/* + * Document me. + */ +void __init +iSeries_setup_arch(void) +{ + void * eventStack; + u32 procFreq; + u32 tbFreq; +// u32 evStackContigReal; +// u64 evStackReal; + + /* Setup the Lp Event Queue */ + + /* Associate Lp Event Queue 0 with processor 0 */ + +// HvCallEvent_setLpEventQueueInterruptProc( 0, 0 ); + + /* Allocate a page for the Event Stack + * The hypervisor wants the absolute real address, so + * we subtract out the KERNELBASE and add in the + * absolute real address of the kernel load area + */ + + eventStack = alloc_bootmem_pages( LpEventStackSize ); + + memset( eventStack, 0, LpEventStackSize ); + + /* Invoke the hypervisor to initialize the event stack */ + + HvCallEvent_setLpEventStack( 0, eventStack, LpEventStackSize ); + + /* Initialize fields in our Lp Event Queue */ + + xItLpQueue.xHSlicEventStackPtr = 0; + xItLpQueue.xSlicEventStackPtr = (char *)eventStack; + xItLpQueue.xHSlicCurEventPtr = 0; + xItLpQueue.xSlicCurEventPtr = (char *)eventStack; + xItLpQueue.xHSlicLastValidEventPtr = 0; + xItLpQueue.xSlicLastValidEventPtr = (char *)eventStack + + (LpEventStackSize - LpEventMaxSize); + xItLpQueue.xIndex = 0; + + if ( itLpNaca.xPirEnvironMode == 0 ) { + printk("Running on Piranha simulator\n"); + piranha_simulator = 1; + } + // Compute processor frequency + procFreq = ( 0x40000000 / (xIoHriProcessorVpd[0].xProcFreq / 1600) ); + procFreqHz = procFreq * 10000; + procFreqMhz = procFreq / 100; + procFreqMhzHundreths = procFreq - (procFreqMhz * 100 ); + + // Compute time base frequency + tbFreq = ( 0x40000000 / (xIoHriProcessorVpd[0].xTimeBaseFreq / 400) ); + tbFreqHz = tbFreq * 10000; + tbFreqMhz = tbFreq / 100; + tbFreqMhzHundreths = tbFreq - (tbFreqMhz * 100 ); + + printk("Max logical processors = %d\n", + itVpdAreas.xSlicMaxLogicalProcs ); + printk("Max physical processors = %d\n", + itVpdAreas.xSlicMaxPhysicalProcs ); + printk("Processor frequency = %lu.%02lu\n", + procFreqMhz, + procFreqMhzHundreths ); + printk("Time base frequency = %lu.%02lu\n", + tbFreqMhz, + tbFreqMhzHundreths ); + printk("Processor version = %x\n", + xIoHriProcessorVpd[0].xPVR ); + +#ifdef CONFIG_PCI + /* Initialize the flight recorder, global bus map and pci memory table */ + iSeries_pci_Initialize(); + + /* Setup the PCI controller list */ + iSeries_build_hose_list(); +#endif /* CONFIG_PCI */ +/* + // copy the command line parameter from the primary VSP + HvCallEvent_dmaToSp( cmd_line, + 2*64*1024, + 256, + HvLpDma_Direction_RemoteToLocal ); + + mf_init(); + viopath_init(); +*/ +} + +/* + * int iSeries_show_percpuinfo() + * + * Description: + * This routine pretty-prints CPU information gathered from the VPD + * for use in /proc/cpuinfo + * + * Input(s): + * *buffer - Buffer into which CPU data is to be printed. + * + * Output(s): + * *buffer - Buffer with CPU data. + * + * Returns: + * The number of bytes copied into 'buffer' if OK, otherwise zero or less + * on error. + */ +static int +iSeries_show_percpuinfo(struct seq_file *m, int i) +{ + seq_printf(m, "clock\t\t: %lu.%02luMhz\n", + procFreqMhz, procFreqMhzHundreths ); +// seq_printf(m, " processor clock\t\t: %ldMHz\n", +// ((unsigned long)xIoHriProcessorVpd[0].xProcFreq)/1000000); + seq_printf(m, "time base\t: %lu.%02luMHz\n", + tbFreqMhz, tbFreqMhzHundreths ); +// seq_printf(m, " time base freq\t\t: %ldMHz\n", +// ((unsigned long)xIoHriProcessorVpd[0].xTimeBaseFreq)/1000000); + seq_printf(m, "i-cache\t\t: %d\n", + iSeries_icache_line_size); + seq_printf(m, "d-cache\t\t: %d\n", + iSeries_dcache_line_size); + + return 0; +} + +static int iSeries_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: iSeries Logical Partition\n"); + + return 0; +} + +#ifndef CONFIG_PCI +/* + * Document me. + * and Implement me. + * If no Native I/O, do nothing routine. + */ + void __init +iSeries_init_IRQ(void) +{ + return; +} +#endif + +/* + * Document me. + * and Implement me. + */ +int +iSeries_get_irq(struct pt_regs *regs) +{ +/* + return (ppc4xx_pic_get_irq(regs)); +*/ + /* -2 means ignore this interrupt */ + return -2; +} + +/* + * Document me. + */ +void +iSeries_restart(char *cmd) +{ + mf_reboot(); +} + +/* + * Document me. + */ +void +iSeries_power_off(void) +{ + mf_powerOff(); +} + +/* + * Document me. + */ +void +iSeries_halt(void) +{ + mf_powerOff(); +} + +/* + * Nothing to do here. + */ +void __init +iSeries_time_init(void) +{ + /* Nothing to do */ +} + +/* + * Set the RTC in the virtual service processor + * This requires flowing LpEvents to the primary partition + */ +int iSeries_set_rtc_time(unsigned long time) +{ + mf_setRtcTime(time); + return 0; +} + +/* + * Get the RTC from the virtual service processor + * This requires flowing LpEvents to the primary partition + */ +unsigned long iSeries_get_rtc_time(void) +{ + /* XXX - Implement me */ + unsigned long time; + mf_getRtcTime(&time); + return (time); +} + +/* + * void __init iSeries_calibrate_decr() + * + * Description: + * This routine retrieves the internal processor frequency from the VPD, + * and sets up the kernel timer decrementer based on that value. + * + */ + +void __init +iSeries_calibrate_decr(void) +{ + u32 freq; + u32 tbf; + struct Paca * paca; + + /* Compute decrementer (and TB) frequency + * in cycles/sec + */ + + tbf = xIoHriProcessorVpd[0].xTimeBaseFreq / 16; + + freq = 0x010000000; + freq = freq / tbf; /* cycles / usec */ + freq = freq * 1000000; /* now in cycles/sec */ + + /* Set the amount to refresh the decrementer by. This + * is the number of decrementer ticks it takes for + * 1/HZ seconds. + */ + + /* decrementer_count = freq / HZ; + * count_period_num = 1; + * count_period_den = freq; */ + + if ( decr_overclock_set && !decr_overclock_proc0_set ) + decr_overclock_proc0 = decr_overclock; + + tb_ticks_per_jiffy = freq / HZ; + paca = (struct Paca *)mfspr(SPRG1); + paca->default_decr = tb_ticks_per_jiffy / decr_overclock_proc0; + tb_to_us = mulhwu_scale_factor(freq, 1000000); +} +void __init +iSeries_progress( char * st, unsigned short code ) +{ + printk( "Progress: [%04x] - %s\n", (unsigned)code, st ); + if (code != 0xffff) + mf_displayProgress( code ); + else + mf_clearSrc(); +} + +#ifdef CONFIG_PCI +/* + * unsigned int __init iSeries_build_hose_list() + * + * Description: + * This routine builds a list of the PCI host bridges that + * connect PCI buses either partially or fully owned by + * this guest partition + * + */ +unsigned int __init iSeries_build_hose_list ( ) { + struct pci_controller* hose; + struct iSeries_hose_arch_data* hose_data; + u64 hvRc; + u16 hvbusnum; + int LxBusNumber = 0; /* Linux Bus number for grins */ + + /* Check to make sure the device probing will work on this iSeries Release. */ + if(hvReleaseData.xVrmIndex !=3) { + printk("PCI: iSeries Lpar and Linux native PCI I/O code is incompatible.\n"); + printk("PCI: A newer version of the Linux kernel is need for this iSeries release.\n"); + return 0; + } + + + for (hvbusnum = 0; hvbusnum < 256; hvbusnum++) { /* All PCI buses which could be owned by this guest partition will be numbered by the hypervisor between 1 & 255 */ + hvRc = HvCallXm_testBus (hvbusnum); /* Call the system hypervisor to query guest partition ownership status of this bus */ + if (hvRc == 0) { /* This bus is partially/fully owned by this guest partition */ + hose = (struct pci_controller*)pcibios_alloc_controller(); // Create the hose for this PCI bus + hose->first_busno = LxBusNumber; /* This just for debug. pcibios will */ + hose->last_busno = 0xff; /* assign the bus numbers. */ + hose->ops = &iSeries_pci_ops; + /* Create the iSeries_arch_data for the hose and cache the HV bus number in it so that pci bios can build the global bus map */ + hose_data = (struct iSeries_hose_arch_data *) alloc_bootmem(sizeof(struct iSeries_hose_arch_data)); + memset(hose_data, 0, sizeof(*hose_data)); + hose->arch_data = (void *) hose_data; + ((struct iSeries_hose_arch_data *)(hose->arch_data))->hvBusNumber = hvbusnum; + LxBusNumber += 1; /* Keep track for debug */ + } + } + pci_assign_all_busses = 1; /* Let Linux assign the bus numbers in pcibios_init */ + return 0; +} +#endif /* CONFIG_PCI */ + +int iSeries_spread_lpevents( char * str ) +{ + /* The parameter is the number of processors to share in processing lp events */ + unsigned long i; + unsigned long val = simple_strtoul(str, NULL, 0 ); + if ( ( val > 0 ) && ( val <= maxPacas ) ) { + for( i=1; i= 1 ) && ( val <= 48 ) ) { + decr_overclock_proc0_set = 1; + decr_overclock_proc0 = val; + printk("proc 0 decrementer overclock factor of %ld\n", val); + } + else { + printk("invalid proc 0 decrementer overclock factor of %ld\n", val); + } + return 1; +} + +int iSeries_decr_overclock( char * str ) +{ + unsigned long val = simple_strtoul( str, NULL, 0 ); + if ( ( val >= 1 ) && ( val <= 48 ) ) { + decr_overclock_set = 1; + decr_overclock = val; + printk("decrementer overclock factor of %ld\n", val); + } + else { + printk("invalid decrementer overclock factor of %ld\n", val); + } + return 1; +} + +__setup("spread_lpevents=", iSeries_spread_lpevents ); +__setup("decr_overclock_proc0=", iSeries_decr_overclock_proc0 ); +__setup("decr_overclock=", iSeries_decr_overclock ); + diff -Nru a/arch/ppc/platforms/iSeries_setup.h b/arch/ppc/platforms/iSeries_setup.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/iSeries_setup.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2000 Mike Corrigan + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: iSeries_setup.h + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM iSeries LPAR. Adapted from original code by Grant Erickson and + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ISERIES_SETUP_H__ +#define __ISERIES_SETUP_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern void iSeries_init(unsigned long r3, + unsigned long ird_start, + unsigned long ird_end, + unsigned long cline_start, + unsigned long cline_end); +extern void iSeries_setup_arch(void); +extern int iSeries_setup_residual(char *buffer); +extern int iSeries_get_cpuinfo(char *buffer); +extern void iSeries_init_IRQ(void); +extern int iSeries_get_irq(struct pt_regs *regs); +extern void iSeries_restart(char *cmd); +extern void iSeries_power_off(void); +extern void iSeries_halt(void); +extern void iSeries_time_init(void); +extern int iSeries_set_rtc_time(unsigned long now); +extern unsigned long iSeries_get_rtc_time(void); +extern void iSeries_calibrate_decr(void); +extern void iSeries_progress( char *, unsigned short ); +extern unsigned int iSeries_build_hose_list(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __ISERIES_SETUP_H__ */ diff -Nru a/arch/ppc/platforms/iSeries_smp.c b/arch/ppc/platforms/iSeries_smp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/iSeries_smp.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,133 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * SMP support for iSeries/LPAR. + * + * Copyright (C) 2001 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +extern u64 get_tb64(void); +extern u64 next_jiffy_update_tb[]; + +static unsigned long iSeries_smp_message[NR_CPUS]; + +void iSeries_smp_message_recv( struct pt_regs * regs ) +{ + int cpu = smp_processor_id(); + int msg; + + if ( smp_num_cpus < 2 ) + return; + + for ( msg = 0; msg < 4; ++msg ) + if ( test_and_clear_bit( msg, &iSeries_smp_message[cpu] ) ) + smp_message_recv( msg, regs ); + +} + +static void smp_iSeries_message_pass(int target, int msg, unsigned long data, int wait) +{ + int i; + for (i = 0; i < smp_num_cpus; ++i) { + if ( (target == MSG_ALL) || + (target == i) || + ((target == MSG_ALL_BUT_SELF) && (i != smp_processor_id())) ) { + set_bit( msg, &iSeries_smp_message[i] ); + HvCall_sendIPI(&(xPaca[i])); + } + } +} + +static int smp_iSeries_probe(void) +{ + unsigned i; + unsigned np; + struct ItLpPaca * lpPaca; + + np = 0; + for (i=0; ixDynProcStatus < 2 ) + ++np; + } + + smp_tb_synchronized = 1; + return np; +} + +extern unsigned long decr_overclock; +static void smp_iSeries_kick_cpu(int nr) +{ + struct ItLpPaca * lpPaca; + // Verify we have a Paca for processor nr + if ( ( nr <= 0 ) || + ( nr >= maxPacas ) ) + return; + // Verify that our partition has a processor nr + lpPaca = xPaca[nr].xLpPacaPtr; + if ( lpPaca->xDynProcStatus >= 2 ) + return; + xPaca[nr].default_decr = tb_ticks_per_jiffy / decr_overclock; + // The processor is currently spinning, waiting + // for the xProcStart field to become non-zero + // After we set xProcStart, the processor will + // continue on to secondary_start in iSeries_head.S + xPaca[nr].xProcStart = 1; +} + +static void smp_iSeries_setup_cpu(int nr) +{ + set_dec( xPaca[nr].default_decr ); +} + +void smp_iSeries_space_timers( unsigned nr ) +{ + unsigned offset,i; + + offset = tb_ticks_per_jiffy / nr; + for ( i=1; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +u64 next_jiffy_update_tb[NR_CPUS]; +extern u64 get_tb64(void); + +extern rwlock_t xtime_lock; +extern unsigned long wall_jiffies; + +extern unsigned long prof_cpu_mask; +extern unsigned int * prof_buffer; +extern unsigned long prof_len; +extern unsigned long prof_shift; +extern char _stext; + +extern int is_soft_enabled(void); + +static inline void ppc_do_profile (unsigned long nip) +{ + if (!prof_buffer) + return; + + /* + * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. + * (default is all CPUs.) + */ + if (!((1<>= prof_shift; + /* + * Don't ignore out-of-bounds EIP values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (nip > prof_len-1) + nip = prof_len-1; + atomic_inc((atomic_t *)&prof_buffer[nip]); +} + + /* + * For iSeries shared processors, we have to let the hypervisor + * set the hardware decrementer. We set a virtual decrementer + * in the ItLpPaca and call the hypervisor if the virtual + * decrementer is less than the current value in the hardware + * decrementer. (almost always the new decrementer value will + * be greater than the current hardware decementer so the hypervisor + * call will not be needed) + * + * When we yield the processor in idle.c (needed for shared processors) + * we cannot yield for too long or the 32-bit tbl may wrap and then + * we would lose jiffies. In addition, if we yield for too long, + * we might be pretty far off on timing for device drivers and such. + * When the hypervisor returns to us after a yield we need to + * determine whether decrementers might have been missed. If so + * we need to drive the timer_interrupt code to catch up (using + * the tb) + * + * For processors other than processor 0, there is no correction + * (in the code below) to next_dec so they're last_jiffy_stamp + * values are going to be way off. + * + */ +int timerRetEnabled = 0; +int timerRetDisabled = 0; + +extern unsigned long iSeries_dec_value; +int timer_interrupt(struct pt_regs * regs) +{ + long next_dec; + struct Paca * paca; + unsigned long cpu = smp_processor_id(); + paca = (struct Paca *)mfspr(SPRG1); + + if ( is_soft_enabled() ) + BUG(); + + if (regs->softEnable) + timerRetEnabled++; + else + timerRetDisabled++; + + hardirq_enter(cpu); + + if (!user_mode(regs)) + ppc_do_profile(instruction_pointer(regs)); + while ( next_jiffy_update_tb[cpu] < get_tb64() ) { +#ifdef CONFIG_SMP + smp_local_timer_interrupt(regs); +#endif + if ( cpu == 0 ) { + write_lock(&xtime_lock); + do_timer(regs); + if ( (time_status & STA_UNSYNC) == 0 && + xtime.tv_sec - last_rtc_update >= 659 && + abs(xtime.tv_usec - (1000000-1000000/HZ)) < 500000/HZ && + jiffies - wall_jiffies == 1) { + if (ppc_md.set_rtc_time(xtime.tv_sec+1 + time_offset) == 0) + last_rtc_update = xtime.tv_sec+1; + else + /* Try again one minute later */ + last_rtc_update += 60; + } + write_unlock(&xtime_lock); + + } + next_jiffy_update_tb[cpu] += tb_ticks_per_jiffy; + } + next_dec = next_jiffy_update_tb[cpu] - get_tb64(); + if ( next_dec > paca->default_decr ) + next_dec = paca->default_decr; + paca->xLpPacaPtr->xDecrInt = 0; + set_dec( (unsigned)next_dec ); + + hardirq_exit(cpu); + + if (softirq_pending(cpu)) + do_softirq(); + return 1; +} diff -Nru a/arch/ppc/platforms/ibm405.h b/arch/ppc/platforms/ibm405.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibm405.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,333 @@ +/* + * ibm405.h + * + * This was derived from the ppc4xx.h and contains + * common 405 offsets + * + * Armin Kuster akuster@pacbell.net + * Jan, 2002 + * + * + * Copyright 2002 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * Version 1.0 (02/01/17) - A. Kuster + * Initial version - moved 405 specific out of the other core.h's + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM405_H__ +#define __ASM_IBM405_H__ + +#ifdef DCRN_BE_BASE +#define DCRN_BEAR (DCRN_BE_BASE + 0x0) /* Bus Error Address Register */ +#define DCRN_BESR (DCRN_BE_BASE + 0x1) /* Bus Error Syndrome Register */ +#endif +/* DCRN_BESR */ +#define BESR_DSES 0x80000000 /* Data-Side Error Status */ +#define BESR_DMES 0x40000000 /* DMA Error Status */ +#define BESR_RWS 0x20000000 /* Read/Write Status */ +#define BESR_ETMASK 0x1C000000 /* Error Type */ +#define ET_PROT 0 +#define ET_PARITY 1 +#define ET_NCFG 2 +#define ET_BUSERR 4 +#define ET_BUSTO 6 + +#ifdef DCRN_CHCR_BASE +#define DCRN_CHCR0 (DCRN_CHCR_BASE + 0x0) /* Chip Control Register 1 */ +#define DCRN_CHCR1 (DCRN_CHCR_BASE + 0x1) /* Chip Control Register 2 */ +#endif +#define CHR1_CETE 0x00800000 /* CPU external timer enable */ +#define CHR1_PCIPW 0x00008000 /* PCI Int enable/Peripheral Write enable */ + +#ifdef DCRN_CHPSR_BASE +#define DCRN_CHPSR (DCRN_CHPSR_BASE + 0x0) /* Chip Pin Strapping */ +#endif + +#ifdef DCRN_CPMFR_BASE +#define DCRN_CPMFR (DCRN_CPMFR_BASE + 0x0) /* CPM Force */ +#endif + +#ifdef DCRN_CPMSR_BASE +#define DCRN_CPMSR (DCRN_CPMSR_BASE + 0x0) /* CPM Status */ +#define DCRN_CPMER (DCRN_CPMSR_BASE + 0x1) /* CPM Enable */ +#endif + +#ifdef DCRN_DCP0_BASE +/* Decompression Controller Address */ +#define DCRN_DCP0_CFGADDR (DCRN_DCP0_BASE + 0x0) +/* Decompression Controller Data */ +#define DCRN_DCP0_CFGDATA (DCRN_DCP0_BASE + 0x1) +#else +#define DCRN_DCP0_CFGADDR 0x0 +#define DCRN_DCP0_CFGDATA 0x0 +#endif + +#ifdef DCRN_DMA0_BASE +/* DMA Channel Control Register 0 */ +#define DCRN_DMACR0 (DCRN_DMA0_BASE + 0x0) +#define DCRN_DMACT0 (DCRN_DMA0_BASE + 0x1) /* DMA Count Register 0 */ +/* DMA Destination Address Register 0 */ +#define DCRN_DMADA0 (DCRN_DMA0_BASE + 0x2) +/* DMA Source Address Register 0 */ +#define DCRN_DMASA0 (DCRN_DMA0_BASE + 0x3) +#ifdef DCRNCAP_DMA_CC +/* DMA Chained Count Register 0 */ +#define DCRN_DMACC0 (DCRN_DMA0_BASE + 0x4) +#endif +#ifdef DCRNCAP_DMA_SG +/* DMA Scatter/Gather Descriptor Addr 0 */ +#define DCRN_ASG0 (DCRN_DMA0_BASE + 0x4) +#endif +#endif + +#ifdef DCRN_DMA1_BASE +/* DMA Channel Control Register 1 */ +#define DCRN_DMACR1 (DCRN_DMA1_BASE + 0x0) +#define DCRN_DMACT1 (DCRN_DMA1_BASE + 0x1) /* DMA Count Register 1 */ +/* DMA Destination Address Register 1 */ +#define DCRN_DMADA1 (DCRN_DMA1_BASE + 0x2) +/* DMA Source Address Register 1 */ +#define DCRN_DMASA1 (DCRN_DMA1_BASE + 0x3) /* DMA Source Address Register 1 */ +#ifdef DCRNCAP_DMA_CC +/* DMA Chained Count Register 1 */ +#define DCRN_DMACC1 (DCRN_DMA1_BASE + 0x4) +#endif +#ifdef DCRNCAP_DMA_SG +/* DMA Scatter/Gather Descriptor Addr 1 */ +#define DCRN_ASG1 (DCRN_DMA1_BASE + 0x4) +#endif +#endif + +#ifdef DCRN_DMA2_BASE +#define DCRN_DMACR2 (DCRN_DMA2_BASE + 0x0) /* DMA Channel Control Register 2 */ +#define DCRN_DMACT2 (DCRN_DMA2_BASE + 0x1) /* DMA Count Register 2 */ +#define DCRN_DMADA2 (DCRN_DMA2_BASE + 0x2) /* DMA Destination Address Register 2 */ +#define DCRN_DMASA2 (DCRN_DMA2_BASE + 0x3) /* DMA Source Address Register 2 */ +#ifdef DCRNCAP_DMA_CC +#define DCRN_DMACC2 (DCRN_DMA2_BASE + 0x4) /* DMA Chained Count Register 2 */ +#endif +#ifdef DCRNCAP_DMA_SG +#define DCRN_ASG2 (DCRN_DMA2_BASE + 0x4) /* DMA Scatter/Gather Descriptor Addr 2 */ +#endif +#endif + +#ifdef DCRN_DMA3_BASE +#define DCRN_DMACR3 (DCRN_DMA3_BASE + 0x0) /* DMA Channel Control Register 3 */ +#define DCRN_DMACT3 (DCRN_DMA3_BASE + 0x1) /* DMA Count Register 3 */ +#define DCRN_DMADA3 (DCRN_DMA3_BASE + 0x2) /* DMA Destination Address Register 3 */ +#define DCRN_DMASA3 (DCRN_DMA3_BASE + 0x3) /* DMA Source Address Register 3 */ +#ifdef DCRNCAP_DMA_CC +#define DCRN_DMACC3 (DCRN_DMA3_BASE + 0x4) /* DMA Chained Count Register 3 */ +#endif +#ifdef DCRNCAP_DMA_SG +#define DCRN_ASG3 (DCRN_DMA3_BASE + 0x4) /* DMA Scatter/Gather Descriptor Addr 3 */ +#endif +#endif + +#ifdef DCRN_DMASR_BASE +#define DCRN_DMASR (DCRN_DMASR_BASE + 0x0) /* DMA Status Register */ +#ifdef DCRNCAP_DMA_SG +#define DCRN_ASGC (DCRN_DMASR_BASE + 0x3) /* DMA Scatter/Gather Command */ +/* don't know if these two registers always exist if scatter/gather exists */ +#define DCRN_POL (DCRN_DMASR_BASE + 0x6) /* DMA Polarity Register */ +#define DCRN_SLP (DCRN_DMASR_BASE + 0x5) /* DMA Sleep Register */ +#endif +#endif + +#ifdef DCRN_EBC_BASE +#define DCRN_EBCCFGADR (DCRN_EBC_BASE + 0x0) /* Peripheral Controller Address */ +#define DCRN_EBCCFGDATA (DCRN_EBC_BASE + 0x1) /* Peripheral Controller Data */ +#endif + +#ifdef DCRN_EXIER_BASE +#define DCRN_EXIER (DCRN_EXIER_BASE + 0x0) /* External Interrupt Enable Register */ +#endif + +#ifdef DCRN_EXISR_BASE +#define DCRN_EXISR (DCRN_EXISR_BASE + 0x0) /* External Interrupt Status Register */ +#endif + +#define EXIER_CIE 0x80000000 /* Critical Interrupt Enable */ +#define EXIER_SRIE 0x08000000 /* Serial Port Rx Int. Enable */ +#define EXIER_STIE 0x04000000 /* Serial Port Tx Int. Enable */ +#define EXIER_JRIE 0x02000000 /* JTAG Serial Port Rx Int. Enable */ +#define EXIER_JTIE 0x01000000 /* JTAG Serial Port Tx Int. Enable */ +#define EXIER_D0IE 0x00800000 /* DMA Channel 0 Interrupt Enable */ +#define EXIER_D1IE 0x00400000 /* DMA Channel 1 Interrupt Enable */ +#define EXIER_D2IE 0x00200000 /* DMA Channel 2 Interrupt Enable */ +#define EXIER_D3IE 0x00100000 /* DMA Channel 3 Interrupt Enable */ +#define EXIER_E0IE 0x00000010 /* External Interrupt 0 Enable */ +#define EXIER_E1IE 0x00000008 /* External Interrupt 1 Enable */ +#define EXIER_E2IE 0x00000004 /* External Interrupt 2 Enable */ +#define EXIER_E3IE 0x00000002 /* External Interrupt 3 Enable */ +#define EXIER_E4IE 0x00000001 /* External Interrupt 4 Enable */ + +#ifdef DCRN_IOCR_BASE +#define DCRN_IOCR (DCRN_IOCR_BASE + 0x0) /* Input/Output Configuration Register */ +#endif +#define IOCR_E0TE 0x80000000 +#define IOCR_E0LP 0x40000000 +#define IOCR_E1TE 0x20000000 +#define IOCR_E1LP 0x10000000 +#define IOCR_E2TE 0x08000000 +#define IOCR_E2LP 0x04000000 +#define IOCR_E3TE 0x02000000 +#define IOCR_E3LP 0x01000000 +#define IOCR_E4TE 0x00800000 +#define IOCR_E4LP 0x00400000 +#define IOCR_EDT 0x00080000 +#define IOCR_SOR 0x00040000 +#define IOCR_EDO 0x00008000 +#define IOCR_2XC 0x00004000 +#define IOCR_ATC 0x00002000 +#define IOCR_SPD 0x00001000 +#define IOCR_BEM 0x00000800 +#define IOCR_PTD 0x00000400 +#define IOCR_ARE 0x00000080 +#define IOCR_DRC 0x00000020 +#define IOCR_RDM(x) (((x) & 0x3) << 3) +#define IOCR_TCS 0x00000004 +#define IOCR_SCS 0x00000002 +#define IOCR_SPC 0x00000001 + +#ifdef DCRN_MAL_BASE +#define DCRN_MALCR (DCRN_MAL_BASE + 0x0) /* MAL Configuration */ +#define DCRN_MALDBR (DCRN_MAL_BASE + 0x3) /* Debug Register */ +#define DCRN_MALESR (DCRN_MAL_BASE + 0x1) /* Error Status */ +#define DCRN_MALIER (DCRN_MAL_BASE + 0x2) /* Interrupt Enable */ +#define DCRN_MALTXCARR (DCRN_MAL_BASE + 0x5) /* TX Channed Active Reset Register */ +#define DCRN_MALTXCASR (DCRN_MAL_BASE + 0x4) /* TX Channel Active Set Register */ +#define DCRN_MALTXDEIR (DCRN_MAL_BASE + 0x7) /* Tx Descriptor Error Interrupt */ +#define DCRN_MALTXEOBISR (DCRN_MAL_BASE + 0x6) /* Tx End of Buffer Interrupt Status */ +#define DCRN_MALRXCARR (DCRN_MAL_BASE + 0x11) /* RX Channed Active Reset Register */ +#define DCRN_MALRXCASR (DCRN_MAL_BASE + 0x10) /* RX Channel Active Set Register */ +#define DCRN_MALRXDEIR (DCRN_MAL_BASE + 0x13) /* Rx Descriptor Error Interrupt */ +#define DCRN_MALRXEOBISR (DCRN_MAL_BASE + 0x12) /* Rx End of Buffer Interrupt Status */ +#define DCRN_MALRXCTP0R (DCRN_MAL_BASE + 0x40) /* Channel Rx 0 Channel Table Pointer */ +#define DCRN_MALTXCTP0R (DCRN_MAL_BASE + 0x20) /* Channel Tx 0 Channel Table Pointer */ +#define DCRN_MALTXCTP1R (DCRN_MAL_BASE + 0x21) /* Channel Tx 1 Channel Table Pointer */ +#define DCRN_MALRCBS0 (DCRN_MAL_BASE + 0x60) /* Channel Rx 0 Channel Buffer Size */ +#endif + /* DCRN_MALCR */ +#define MALCR_MMSR 0x80000000 /* MAL Software reset */ +#define MALCR_PLBP_1 0x00400000 /* MAL reqest priority: */ +#define MALCR_PLBP_2 0x00800000 /* lowsest is 00 */ +#define MALCR_PLBP_3 0x00C00000 /* highest */ +#define MALCR_GA 0x00200000 /* Guarded Active Bit */ +#define MALCR_OA 0x00100000 /* Ordered Active Bit */ +#define MALCR_PLBLE 0x00080000 /* PLB Lock Error Bit */ +#define MALCR_PLBLT_1 0x00040000 /* PLB Latency Timer */ +#define MALCR_PLBLT_2 0x00020000 +#define MALCR_PLBLT_3 0x00010000 +#define MALCR_PLBLT_4 0x00008000 +#define MALCR_PLBLT_DEFAULT 0x00078000 /* JSP: Is this a valid default?? */ +#define MALCR_PLBB 0x00004000 /* PLB Burst Deactivation Bit */ +#define MALCR_OPBBL 0x00000080 /* OPB Lock Bit */ +#define MALCR_EOPIE 0x00000004 /* End Of Packet Interrupt Enable */ +#define MALCR_LEA 0x00000002 /* Locked Error Active */ +#define MALCR_MSD 0x00000001 /* MAL Scroll Descriptor Bit */ +/* DCRN_MALESR */ +#define MALESR_EVB 0x80000000 /* Error Valid Bit */ +#define MALESR_CIDRX 0x40000000 /* Channel ID Receive */ +#define MALESR_DE 0x00100000 /* Descriptor Error */ +#define MALESR_OEN 0x00080000 /* OPB Non-Fullword Error */ +#define MALESR_OTE 0x00040000 /* OPB Timeout Error */ +#define MALESR_OSE 0x00020000 /* OPB Slave Error */ +#define MALESR_PEIN 0x00010000 /* PLB Bus Error Indication */ +#define MALESR_DEI 0x00000010 /* Descriptor Error Interrupt */ +#define MALESR_ONEI 0x00000008 /* OPB Non-Fullword Error Interrupt */ +#define MALESR_OTEI 0x00000004 /* OPB Timeout Error Interrupt */ +#define MALESR_OSEI 0x00000002 /* OPB Slace Error Interrupt */ +#define MALESR_PBEI 0x00000001 /* PLB Bus Error Interrupt */ +/* DCRN_MALIER */ +#define MALIER_DE 0x00000010 /* Descriptor Error Interrupt Enable */ +#define MALIER_NE 0x00000008 /* OPB Non-word Transfer Int Enable */ +#define MALIER_TE 0x00000004 /* OPB Time Out Error Interrupt Enable */ +#define MALIER_OPBE 0x00000002 /* OPB Slave Error Interrupt Enable */ +#define MALIER_PLBE 0x00000001 /* PLB Error Interrupt Enable */ +/* DCRN_MALTXEOBISR */ +#define MALOBISR_CH0 0x80000000 /* EOB channel 1 bit */ +#define MALOBISR_CH2 0x40000000 /* EOB channel 2 bit */ + +#ifdef DCRN_PLB0_BASE +#define DCRN_PLB0_BESR (DCRN_PLB0_BASE + 0x0) +#define DCRN_PLB0_BEAR (DCRN_PLB0_BASE + 0x2) +/* doesn't exist on stb03xxx? */ +#define DCRN_PLB0_ACR (DCRN_PLB0_BASE + 0x3) +#endif + +#ifdef DCRN_PLB1_BASE +#define DCRN_PLB1_BESR (DCRN_PLB1_BASE + 0x0) +#define DCRN_PLB1_BEAR (DCRN_PLB1_BASE + 0x1) +/* doesn't exist on stb03xxx? */ +#define DCRN_PLB1_ACR (DCRN_PLB1_BASE + 0x2) +#endif + +#ifdef DCRN_PLLMR_BASE +#define DCRN_PLLMR (DCRN_PLLMR_BASE + 0x0) /* PL1 Mode */ +#endif + +#ifdef DCRN_POB0_BASE +#define DCRN_POB0_BESR0 (DCRN_POB0_BASE + 0x0) +#define DCRN_POB0_BEAR (DCRN_POB0_BASE + 0x2) +#define DCRN_POB0_BESR1 (DCRN_POB0_BASE + 0x4) +#endif + +#ifdef DCRN_UIC0_BASE +#define DCRN_UIC0_SR (DCRN_UIC0_BASE + 0x0) +#define DCRN_UIC0_ER (DCRN_UIC0_BASE + 0x2) +#define DCRN_UIC0_CR (DCRN_UIC0_BASE + 0x3) +#define DCRN_UIC0_PR (DCRN_UIC0_BASE + 0x4) +#define DCRN_UIC0_TR (DCRN_UIC0_BASE + 0x5) +#define DCRN_UIC0_MSR (DCRN_UIC0_BASE + 0x6) +#define DCRN_UIC0_VR (DCRN_UIC0_BASE + 0x7) +#define DCRN_UIC0_VCR (DCRN_UIC0_BASE + 0x8) +#endif + +#ifdef DCRN_UIC1_BASE +#define DCRN_UIC1_SR (DCRN_UIC1_BASE + 0x0) +#define DCRN_UIC1_SRS (DCRN_UIC1_BASE + 0x1) +#define DCRN_UIC1_ER (DCRN_UIC1_BASE + 0x2) +#define DCRN_UIC1_CR (DCRN_UIC1_BASE + 0x3) +#define DCRN_UIC1_PR (DCRN_UIC1_BASE + 0x4) +#define DCRN_UIC1_TR (DCRN_UIC1_BASE + 0x5) +#define DCRN_UIC1_MSR (DCRN_UIC1_BASE + 0x6) +#define DCRN_UIC1_VR (DCRN_UIC1_BASE + 0x7) +#define DCRN_UIC1_VCR (DCRN_UIC1_BASE + 0x8) +#endif + +#ifdef DCRN_SDRAM0_BASE +#define DCRN_SDRAM0_CFGADDR (DCRN_SDRAM0_BASE + 0x0) /* Memory Controller Address */ +#define DCRN_SDRAM0_CFGDATA (DCRN_SDRAM0_BASE + 0x1) /* Memory Controller Data */ +#endif + +#ifdef DCRN_OCM0_BASE +#define DCRN_OCMISARC (DCRN_OCM0_BASE + 0x0) /* OCM Instr Side Addr Range Compare */ +#define DCRN_OCMISCR (DCRN_OCM0_BASE + 0x1) /* OCM Instr Side Control */ +#define DCRN_OCMDSARC (DCRN_OCM0_BASE + 0x2) /* OCM Data Side Addr Range Compare */ +#define DCRN_OCMDSCR (DCRN_OCM0_BASE + 0x3) /* OCM Data Side Control */ +#endif + +#endif /* __ASM_IBM405_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/ibm405gp.c b/arch/ppc/platforms/ibm405gp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibm405gp.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,63 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibm405gp.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by 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. + * + * History: 12/26/2001 - armin + * initial release + * + */ + +#include +#include +#include +#include +#include +#include +#include + +const struct pcil0_regs *PCIL_ADDR[] = { + (struct pcil0_regs *) PCIL0_BASE, +}; + +const struct NS16550 *COM_PORTS[] = { + (struct NS16550 *) UART0_IO_BASE, + (struct NS16550 *) UART1_IO_BASE, +}; + +struct iic_regs *IIC_ADDR[] = { + (struct iic_regs *) IIC0_BASE, +}; + +const struct gpio_regs *GPIO_ADDR[] = { + (struct gpio_regs *) GPIO0_BASE, +}; + +const struct emac_regs *EMAC_ADDR[] = { + (struct emac_regs *) EMAC0_BASE, +}; diff -Nru a/arch/ppc/platforms/ibm405gp.h b/arch/ppc/platforms/ibm405gp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibm405gp.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,176 @@ +/* + * ibm405gp.h + * + * This was derived from the ppc4xx.h and all 405GP specific + * definition and board inclusions were moved here. + * + * Armin Kuster akuster@mvista.com + * Oct, 2001 + * + * + * Copyright 2001 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * Version 1.0 (01/10/10) - A. Kuster + * Initial version - moved 40GP specific out of ppc4xx.h + * - moved emac reg from ppc405_enet.h + * + * Version 1.1 02/01/17 - A. Kuster + * Moved offsets to ibm405.h + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM405GP_H__ +#define __ASM_IBM405GP_H__ + +#include +#include + +/* ibm405.h at bottom of this file */ + +/* PCI + * PCI Bridge config reg definitions + * see 17-19 of manual + */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 + +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x80000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 2 + +#define UART0_INT 0 +#define UART1_INT 1 + +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE (u8 *) 0xEF600300 +#define UART1_IO_BASE (u8 *) 0xEF600400 +#define IIC0_BASE 0xEF600500 +#define OPB0_BASE 0xEF600600 +#define GPIO0_BASE 0xEF600700 +#define EMAC0_BASE 0xEF600800 + +#define EMAC_NUMS 1 +#define UART_NUMS 2 +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) +#endif + +/* DCR defines */ +#define DCRN_CHCR_BASE 0x0B1 +#define DCRN_CHPSR_BASE 0x0B4 +#define DCRN_CPMSR_BASE 0x0B8 +#define DCRN_CPMFR_BASE 0x0BA + +#define DCRN_CHPSR_BASE 0x0B4 +#define PSR_PLL_FWD_MASK 0xC0000000 +#define PSR_PLL_FDBACK_MASK 0x30000000 +#define PSR_PLL_TUNING_MASK 0x0E000000 +#define PSR_PLB_CPU_MASK 0x01800000 +#define PSR_OPB_PLB_MASK 0x00600000 +#define PSR_PCI_PLB_MASK 0x00180000 +#define PSR_EB_PLB_MASK 0x00060000 +#define PSR_ROM_WIDTH_MASK 0x00018000 +#define PSR_ROM_LOC 0x00004000 +#define PSR_PCI_ASYNC_EN 0x00001000 +#define PSR_PCI_ARBIT_EN 0x00000400 + +#define CPM_IIC0 0x80000000 /* IIC interface */ +#define CPM_PCI 0x40000000 /* PCI bridge */ +#define CPM_CPU 0x20000000 /* processor core */ +#define CPM_DMA 0x10000000 /* DMA controller */ +#define CPM_BRG 0x08000000 /* PLB to OPB bridge */ +#define CPM_DCP 0x04000000 /* CodePack */ +#define CPM_EBC 0x02000000 /* ROM/SRAM peripheral controller */ +#define CPM_SDRAM0 0x01000000 /* SDRAM memory controller */ +#define CPM_PLB 0x00800000 /* PLB bus arbiter */ +#define CPM_GPIO0 0x00400000 /* General Purpose IO (??) */ +#define CPM_UART0 0x00200000 /* serial port 0 */ +#define CPM_UART1 0x00100000 /* serial port 1 */ +#define CPM_UIC 0x00080000 /* Universal Interrupt Controller */ +#define CPM_TMRCLK 0x00040000 /* CPU timers */ +#define CPM_EMAC_MM 0x00020000 /* on-chip ethernet MM unit */ +#define CPM_EMAC_RM 0x00010000 /* on-chip ethernet RM unit */ +#define CPM_EMAC_TM 0x00008000 /* on-chip ethernet TM unit */ + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 + +#include + +#endif /* __ASM_IBM405GP_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/ibm_ocp.h b/arch/ppc/platforms/ibm_ocp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibm_ocp.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,192 @@ +/* + * ibm_ips.h + * + * This was dirived from the ppc4xx.h and all 405GP specific definition and board + * inclusions where moved here. + * + * Current Maintainer + * Armin Kuster akuster@mvista.com + * Nov, 2001 + * + * + * Copyright 2001 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * Version 1.0 (01/11/26) - A. Kuster + * Initial version - + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM_IPS_H__ +#define __ASM_IBM_IPS_H__ + +#ifndef __ASSEMBLY__ +#include + + /* PCI 32 */ + +struct pmm_regs { + u32 la; + u32 ma; + u32 pcila; + u32 pciha; +}; + +typedef struct pcil0_regs { + struct pmm_regs pmm[3]; + u32 ptm1ms; + u32 ptm1la; + u32 ptm2ms; + u32 ptm2la; +} pci0_t; + +/* Serial Ports */ + +#define thr rbr +#define iir fcr +#define dll rbr +#define dlm ier + +typedef struct NS16550 { + u8 rbr; /* 0 */ + u8 ier; /* 1 */ + u8 fcr; /* 2 */ + u8 lcr; /* 3 */ + u8 mcr; /* 4 */ + u8 lsr; /* 5 */ + u8 msr; /* 6 */ + u8 scr; /* 7 */ +} uart_t; + +/* I2c */ +typedef struct iic_regs { + u16 mdbuf; + u16 sbbuf; + u8 lmadr; + u8 hmadr; + u8 cntl; + u8 mdcntl; + u8 sts; + u8 extsts; + u8 lsadr; + u8 hsadr; + u8 clkdiv; + u8 intmsk; + u8 xfrcnt; + u8 xtcntlss; + u8 directcntl; +} iic_t; + +/* OPB arbiter */ +typedef struct opb { + u8 pr; + u8 cr; +} opb_t; + +/* General purpose i/o */ + +typedef struct gpio_regs { + u32 or; + u32 tcr; + u32 pad[4]; + u32 odr; + u32 ir; +} gpio_t; + +/* Emac */ +typedef struct emac_regs { + volatile u32 em0mr0; + volatile u32 em0mr1; + volatile u32 em0tmr0; + volatile u32 em0tmr1; + volatile u32 em0rmr; + volatile u32 em0isr; + volatile u32 em0iser; + volatile u32 em0iahr; + volatile u32 em0ialr; + volatile u32 em0vtpid; + volatile u32 em0vtci; + volatile u32 em0ptr; + volatile u32 em0iaht1; + volatile u32 em0iaht2; + volatile u32 em0iaht3; + volatile u32 em0iaht4; + volatile u32 em0gaht1; + volatile u32 em0gaht2; + volatile u32 em0gaht3; + volatile u32 em0gaht4; + volatile u32 em0lsal; + volatile u32 em0lsah; + volatile u32 em0ipgvr; + volatile u32 em0stacr; + volatile u32 em0trtr; + volatile u32 em0rwmr; +} emac_t; + +/* ZMII bridge */ +typedef struct zmii_regs { + u32 fer; /* Function enable reg */ + u32 ssr; /* Spedd select reg */ + u32 smiirs; /* SMII status reg */ +} zmii_t; + +/* Structure of the memory mapped IDE control. +*/ +typedef struct ide_regs { + unsigned int si_stat; /* IDE status */ + unsigned int si_intenable; /* IDE interrupt enable */ + unsigned int si_control; /* IDE control */ + unsigned int pad0[0x3d]; + unsigned int si_c0rt; /* Chan 0 Register transfer timing */ + unsigned int si_c0fpt; /* Chan 0 Fast PIO transfer timing */ + unsigned int si_c0timo; /* Chan 0 timeout */ + unsigned int pad1[2]; + unsigned int si_c0d0u; /* Chan 0 UDMA transfer timing */ +#define si_c0d0m si_c0d0u /* Chan 0 Multiword DMA timing */ + unsigned int pad2; + unsigned int si_c0d1u; /* Chan 0 dev 1 UDMA timing */ +#define si_c0d1m si_c0d1u /* Chan 0 dev 1 Multiword DMA timing */ + unsigned int si_c0c; /* Chan 0 Control */ + unsigned int si_c0s0; /* Chan 0 Status 0 */ + unsigned int si_c0ie; /* Chan 0 Interrupt Enable */ + unsigned int si_c0s1; /* Chan 0 Status 0 */ + unsigned int pad4[4]; + unsigned int si_c0dcm; /* Chan 0 DMA Command */ + unsigned int si_c0tb; /* Chan 0 PRD Table base address */ + unsigned int si_c0dct; /* Chan 0 DMA Count */ + unsigned int si_c0da; /* Chan 0 DMA Address */ + unsigned int si_c0sr; /* Chan 0 Slew Rate Output Control */ + unsigned char pad5[0xa2]; + unsigned short si_c0adc; /* Chan 0 Alt status/control */ + unsigned char si_c0d; /* Chan 0 data */ + unsigned char si_c0ef; /* Chan 0 error/features */ + unsigned char si_c0sct; /* Chan 0 sector count */ + unsigned char si_c0sn; /* Chan 0 sector number */ + unsigned char si_c0cl; /* Chan 0 cylinder low */ + unsigned char si_c0ch; /* Chan 0 cylinder high */ + unsigned char si_c0dh; /* Chan 0 device/head */ + unsigned char si_c0scm; /* Chan 0 status/command */ +} ide_t; + +#endif /* __ASSEMBLY__ */ +#endif /* __ASM_IBM_IPS_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/ibmnp405h.c b/arch/ppc/platforms/ibmnp405h.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibmnp405h.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,78 @@ +/* + * + * Copyright 2000-2002 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibmnp405h.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by 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. + * + * History: 01/02/2002 - armin + * initial release + * + */ + +#include +#include +#include +#include +#include +#include +#include + +const struct NS16550* COM_PORTS[] = +{ + (struct NS16550*) UART0_IO_BASE, + (struct NS16550*) UART1_IO_BASE, +}; + +const struct pcil0_regs* PCIL0[]= +{ + (struct pcil0_regs*) PCIL0_BASE, +}; + + +const struct iic_regs* IIC_ADDR[]= +{ + (struct iic_regs*) IIC0_BASE, +}; + + +const struct gpio_regs* GPIO_ADDR[] = +{ + (struct gpio_regs*) GPIO0_BASE, +}; + +const struct emac_regs* EMAC_ADDR[]= +{ + (struct emac_regs*) EMAC0_BASE, + (struct emac_regs*) EMAC1_BASE, + (struct emac_regs*) EMAC2_BASE, + (struct emac_regs*) EMAC3_BASE +}; + +const struct zmii_regs* ZMII_ADDR[]= +{ + (zmii_t*) ZMII0_BASE, +}; diff -Nru a/arch/ppc/platforms/ibmnp405h.h b/arch/ppc/platforms/ibmnp405h.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibmnp405h.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,172 @@ +/* + * ibmnp405h.h + * + * This was dirived from the ibm405gp.h and other previus works in ppc4xx.h + * + * Current maintainer + * Armin Kuster akuster@mvista.com + * Jan, 2002 + * + * + * Copyright 2002 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * Version 1.0 (02/01/03) - A. Kuster + * Initial version + * + * Version 1.1 02/01/17 - A. Kuster + * moved common ofsets to ibm405.h + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMNP405H_H__ +#define __ASM_IBMNP405H_H__ + +#include +#include + +/* ibm405.h at bottom of this file */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x00000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_ADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_ADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_ADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_ADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 4 + +#define UART0_INT 0 +#define UART1_INT 1 +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 +#define IIC0_BASE 0xEF600500 +#define OPB0_BASE 0xEF600600 +#define GPIO0_BASE 0xEF600700 +#define EMAC0_BASE 0xEF600800 +#define EMAC1_BASE 0xEF600900 +#define EMAC2_BASE 0xEF600900 +#define EMAC3_BASE 0xEF600900 +#define ZMII0_BASE 0xEF600C10 + +#define EMAC_NUMS 4 +#define UART_NUMS 2 +#define ZMII_NUMS 1 +#define BD_EMAC_ADDR(e,i) bi_enetaddr[e][i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) +#endif + +/* DCR defines */ +/* ------------------------------------------------------------------------- */ + +#define DCRN_CHCR_BASE 0x0B1 +#define DCRN_CHPSR_BASE 0x0B4 +#define DCRN_CPMSR_BASE 0x0B8 +#define DCRN_CPMFR_BASE 0x0BA + +#define CPM_IIC0 0x80000000 /* IIC interface */ +#define CPM_PCI 0x40000000 /* PCI bridge */ +#define CPM_CPU 0x20000000 /* processor core */ +#define CPM_DMA 0x10000000 /* DMA controller */ +#define CPM_BRG 0x08000000 /* PLB to OPB bridge */ +#define CPM_DCP 0x04000000 /* CodePack */ +#define CPM_EBC 0x02000000 /* ROM/SRAM peripheral controller */ +#define CPM_SDRAM0 0x01000000 /* SDRAM memory controller */ +#define CPM_PLB 0x00800000 /* PLB bus arbiter */ +#define CPM_GPIO0 0x00400000 /* General Purpose IO (??) */ +#define CPM_UART0 0x00200000 /* serial port 0 */ +#define CPM_UART1 0x00100000 /* serial port 1 */ +#define CPM_UIC = 0x00080000 /* Universal Interrupt Controller */ +#define CPM_TMRCLK 0x00040000 /* CPU timers */ +#define CPM_EMAC_MM 0x00020000 /* on-chip ethernet MM unit */ +#define CPM_EMAC_RM 0x00010000 /* on-chip ethernet RM unit */ +#define CPM_EMAC_TM 0x00008000 /* on-chip ethernet TM unit */ + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 + +/* unique H offsets */ + +#ifdef DCRN_UIC1_BASE +#define DCRN_UIC1_SR (DCRN_UIC1_BASE + 0x0) +#define DCRN_UIC1_SRS (DCRN_UIC1_BASE + 0x1) +#define DCRN_UIC1_ER (DCRN_UIC1_BASE + 0x2) +#define DCRN_UIC1_CR (DCRN_UIC1_BASE + 0x3) +#define DCRN_UIC1_PR (DCRN_UIC1_BASE + 0x4) +#define DCRN_UIC1_TR (DCRN_UIC1_BASE + 0x5) +#define DCRN_UIC1_MSR (DCRN_UIC1_BASE + 0x6) +#define DCRN_UIC1_VR (DCRN_UIC1_BASE + 0x7) +#define DCRN_UIC1_VCR (DCRN_UIC1_BASE + 0x8) +#endif + +#include + +#endif /* __ASM_IBMNP405H_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/ibmnp405l.c b/arch/ppc/platforms/ibmnp405l.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibmnp405l.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,70 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibmnp405l.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by 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. + * + * History: 12/26/2001 - armin + * initial release + * + */ + +#include +#include +#include +#include +#include +#include +#include + +const struct NS16550* COM_PORTS[] = +{ + (struct NS16550*) UART0_IO_BASE, + (struct NS16550*) UART1_IO_BASE, +}; + +const struct iic_regs* IIC_ADDR[]= +{ + (struct iic_regs*) IIC0_BASE, +}; + + +const struct gpio_regs* GPIO_ADDR[] = +{ + (struct gpio_regs*) GPIO0_BASE, +}; + +const struct emac_regs* EMAC_ADDR[]= +{ + (struct emac_regs*) EMAC0_BASE, + (struct emac_regs*) EMAC1_BASE +}; + +const struct zmii_regs* ZMII_ADDR[]= +{ + (zmii_t*) ZMII0_BASE, +}; diff -Nru a/arch/ppc/platforms/ibmnp405l.h b/arch/ppc/platforms/ibmnp405l.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibmnp405l.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,156 @@ +/* + * ibmnp405l.h + * + * This was dirived from the ibm405gp.h and other previus works in ppc4xx.h + * + * Current maintainer + * Armin Kuster akuster@mvista.com + * Oct, 2001 + * + * + * Copyright 2001 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * Version 1.0 (01/10/10) - A. Kuster + * Initial version + * + * Version 1.1 (02/01/16) - A. Kuster + * Cleaned out common 405B3 core stuff + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMNP405L_H__ +#define __ASM_IBMNP405L_H__ + +#include +#include + +/* ibm405.h at bottom of this file */ + +#define PPC4xx_PCI_IO_ADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_ADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_ADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_ADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 2 + +#define UART0_INT 0 +#define UART1_INT 1 + +#define UART0_IO_BASE (u8 *)0xEF600300 +#define UART1_IO_BASE (u8 *)0xEF600400 +#define IIC0_BASE 0xEF600500 +#define OPB0_BASE 0xEF600600 +#define GPIO0_BASE 0xEF600700 +#define EMAC0_BASE 0xEF600800 +#define EMAC1_BASE 0xEF600900 +#define ZMII0_BASE 0xEF600C10 + +#define EMAC_NUMS 2 +#define UART_NUMS 2 +#define ZMII_NUMS 1 +#define BD_EMAC_ADDR(e,i) bi_enetaddr[e][i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) +#endif + +/* DCR defines */ +/* ------------------------------------------------------------------------- */ + +#define DCRN_CHCR_BASE 0x0B1 +#define DCRN_CHPSR_BASE 0x0B4 +#define DCRN_CPMSR_BASE 0x0B8 +#define DCRN_CPMFR_BASE 0x0BA + +#define CPM_IIC0 0x80000000 /* IIC interface */ +#define CPM_PCI 0x40000000 /* PCI bridge */ +#define CPM_CPU 0x20000000 /* processor core */ +#define CPM_DMA 0x10000000 /* DMA controller */ +#define CPM_BRG 0x08000000 /* PLB to OPB bridge */ +#define CPM_DCP 0x04000000 /* CodePack */ +#define CPM_EBC 0x02000000 /* ROM/SRAM peripheral controller */ +#define CPM_SDRAM0 0x01000000 /* SDRAM memory controller */ +#define CPM_PLB 0x00800000 /* PLB bus arbiter */ +#define CPM_GPIO0 0x00400000 /* General Purpose IO (??) */ +#define CPM_UART0 0x00200000 /* serial port 0 */ +#define CPM_UART1 0x00100000 /* serial port 1 */ +#define CPM_UIC 0x00080000 /* Universal Interrupt Controller */ +#define CPM_TMRCLK 0x00040000 /* CPU timers */ +#define CPM_EMAC_MM 0x00020000 /* on-chip ethernet MM unit */ +#define CPM_EMAC_RM 0x00010000 /* on-chip ethernet RM unit */ +#define CPM_EMAC_TM 0x00008000 /* on-chip ethernet TM unit */ + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define DCRN_UIC1_BASE 0x0C0 + +/* need to clean this up - armin */ + +#ifdef DCRN_UIC1_BASE +#define DCRN_UIC1_SR (DCRN_UIC1_BASE + 0x0) +#define DCRN_UIC1_SRS (DCRN_UIC1_BASE + 0x1) +#define DCRN_UIC1_ER (DCRN_UIC1_BASE + 0x2) +#define DCRN_UIC1_CR (DCRN_UIC1_BASE + 0x3) +#define DCRN_UIC1_PR (DCRN_UIC1_BASE + 0x4) +#define DCRN_UIC1_TR (DCRN_UIC1_BASE + 0x5) +#define DCRN_UIC1_MSR (DCRN_UIC1_BASE + 0x6) +#define DCRN_UIC1_VR (DCRN_UIC1_BASE + 0x7) +#define DCRN_UIC1_VCR (DCRN_UIC1_BASE + 0x8) +#endif + +#include + +#endif /* __ASM_IBMNP405L_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/ibmstb3.c b/arch/ppc/platforms/ibmstb3.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibmstb3.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,53 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibmstb3.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by 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. + * + * History: 01/08/2002 - armin + * initial release + * + */ + +#include +#include + +const struct NS16550* COM_PORTS[] = +{ + (struct NS16550*) UART0_IO_BASE, +}; + +const struct iic_regs* IIC_ADDR[]= +{ + (struct iic_regs*) IIC0_BASE, +}; + +const struct gpio_regs* GPIO_ADDR[] = +{ + (struct gpio_regs*) GPIO0_BASE, +}; + diff -Nru a/arch/ppc/platforms/ibmstb3.h b/arch/ppc/platforms/ibmstb3.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibmstb3.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,288 @@ +/* + * ibmstbx.h + * + * This was dirived from the ppc4xx.h and all stbx specific definitions + * are located here. + * + * Armin Kuster + * Tom Rini + * Oct, 2001 + * + * + * + * Copyright 2001 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * Version 1.0 Oct 10, 2001 - A. Kuster + * Initial version - moved stbx specific out of ibm4xx.h + * + * Version 1.1 Oct 25, 2001 - T. Rini + * Lots of cleanups, and we get included by the board-specific file. + * + * Version 1.2 Jan 16, 2002 - A. Kuster + * Removed common dcr offests that are now in ibm405.h + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMSTBX_H__ +#define __ASM_IBMSTBX_H__ + +#include +#include + +/* ibm405.h at bottom of this file */ + +/* + * Memory map for the IBM "Redwood-4" STB03xxx evaluation board. + * + * The STB03xxx internal i/o addresses don't work for us 1:1, + * so we need to map them at a well know virtual address. + * + * 4000 000x uart1 -> 0xe000 000x + * 4001 00xx ppu + * 4002 00xx smart card + * 4003 000x iic + * 4004 000x uart0 + * 4005 0xxx timer + * 4006 00xx gpio + * 4007 00xx smart card + * 400b 000x iic + * 400c 000x scp + * 400d 000x modem + */ + +#define STB03xxx_IO_BASE ((uint)0xe0000000) +#define PPC4xx_ONB_IO_PADDR ((uint)0x40000000) +#define PPC4xx_ONB_IO_VADDR STB03xxx_IO_BASE +#define PPC4xx_ONB_IO_SIZE ((uint)14*64*1024) + +/* Since we're into address mapping hacks, at least try to hide + * it under a macro..... + */ +#define STB03xxx_MAP_IO_ADDR(a) (((uint)(a) & 0x000fffff) + PPC4xx_ONB_IO_VADDR) + +#define RS_TABLE_SIZE 1 +#define UART0_INT 20 +#ifdef __BOOTER__ +#define UART0_IO_BASE (u8 *)0x40040000 +#else +#define UART0_IO_BASE (u8 *)STB03xxx_MAP_IO_ADDR(0x40040000) +#endif + + /* UART 0 is duped here so when the SICC is the default console + * then ttys1 is configured properly - armin + */ + +#define UART1_INT 20 +#ifdef __BOOTER__ +#define UART1_IO_BASE (u8 *)0x40040000 +#else +#define UART1_IO_BASE (u8 *)STB03xxx_MAP_IO_ADDR(0x40040000) +#endif + +/* need to make this work in scheme - armin */ + +#define SICC0_INTRX 21 +#define SICC0_INTTX 22 +#define SICC0_IO_BASE ((uint* )0x40000000) + +#define IIC0_BASE 0x40030000 +#define IIC1_BASE 0x400b0000 +#define OPB0_BASE 0x40010000 +#define GPIO0_BASE 0x40060000 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) +#endif + +/* ------------------------------------------------------------------------- */ + +#define DCRN_DCRX_BASE 0x020 +#define DCRN_CIC_BASE 0x030 +#define DCRN_UIC0_BASE 0x040 +#define DCRN_PLB0_BASE 0x054 +#define DCRN_PLB1_BASE 0x064 +#define DCRN_EBIMC_BASE 0x070 +#define DCRN_POB0_BASE 0x0B0 + +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRNCAP_DMA_CC 1 /* have DMA chained count capability */ +#define DCRN_DMASR_BASE 0x0E0 + +#define DCRN_CPMFR_BASE 0x102 +#define DCRN_SCCR_BASE 0x120 + +#define CPM_IIC0 0x80000000 /* IIC 0 interface */ +#define CPM_I1284 0x40000000 /* IEEE-1284 */ +#define CPM_IIC1 0x20000000 /* IIC 1 interface */ +#define CPM_CPU 0x10000000 /* PPC405B3 clock control */ +#define CPM_AUD 0x08000000 /* Audio Decoder */ +#define CPM_EBIU 0x04000000 /* External Bus Interface Unit */ +#define CPM_SDRAM1 0x02000000 /* SDRAM 1 memory controller */ +#define CPM_DMA 0x01000000 /* DMA controller */ +#define CPM_RES_1 0x00800000 /* reserved */ +#define CPM_RES_2 0x00400000 /* reserved */ +#define CPM_RES_3 0x00200000 /* reserved */ +#define CPM_UART1 0x00100000 /* Serial 1 / Infrared */ +#define CPM_UART0 0x00080000 /* Serial 0 / 16550 */ +#define CPM_DCRX 0x00040000 /* DCR Extension */ +#define CPM_SC0 0x00020000 /* Smart Card 0 */ +#define CPM_RES_4 0x00010000 /* reserved */ +#define CPM_SC1 0x00008000 /* Smart Card 1 */ +#define CPM_SDRAM0 0x00004000 /* SDRAM 0 memory controller */ +#define CPM_XPT54 0x00002000 /* Transport - 54 Mhz */ +#define CPM_CBS 0x00001000 /* Cross Bar Switch */ +#define CPM_GPT 0x00000800 /* GPTPWM */ +#define CPM_GPIO0 0x00000400 /* General Purpose IO 0 */ +#define CPM_DENC 0x00000200 /* Digital video Encoder */ +#define CPM_TMRCLK 0x00000100 /* CPU timers */ +#define CPM_XPT27 0x00000080 /* Transport - 27 Mhz */ +#define CPM_UIC 0x00000040 /* Universal Interrupt Controller */ +#define CPM_RES_5 0x00000020 /* reserved */ +#define CPM_MSI 0x00000010 /* Modem Serial Interface (SSP) */ +#define CPM_UART2 0x00000008 /* Serial Control Port */ +#define CPM_DSCR 0x00000004 /* Descrambler */ +#define CPM_VID2 0x00000002 /* Video Decoder clock domain 2 */ +#define CPM_RES_6 0x00000001 /* reserved */ + +/* 0x80000000 */ +#define UIC_XPORT 0x40000000 /* 1 Transport */ +#define UIC_AUDIO 0x20000000 /* 2 Audio Decoder */ +#define UIC_VIDEO 0x10000000 /* 3 Video Decoder */ +#define UIC_D0 0x08000000 /* 4 DMA Channel 0 */ +#define UIC_D1 0x04000000 /* 5 DMA Channel 1 */ +#define UIC_D2 0x02000000 /* 6 DMA Channel 2 */ +#define UIC_D3 0x01000000 /* 7 DMA Channel 3 */ +#define UIC_SC0 0x00800000 /* 8 SmartCard 0 Controller */ +#define UIC_IIC0 0x00400000 /* 9 IIC 0 */ +#define UIC_IIC1 0x00200000 /* 10 IIC 1 */ +#define UIC_PWM0 0x00100000 /* 11 GPT_PWM 0: Capture Timers */ +#define UIC_PWM1 0x00080000 /* 12 GPT_PWM 1: Compare Timers */ +#define UIC_SCP 0x00040000 /* 13 Serial Control Port */ +#define UIC_SSP 0x00020000 /* 14 Soft Modem/Synchronous Serial Port */ +#define UIC_PWM2 0x00010000 /* 15 GPT_PWM 2: Down Counters */ +#define UIC_SC1 0x00008000 /* 16 SmartCard 1 Controller */ +#define UIC_EIR7 0x00004000 /* 17 External IRQ 7 */ +#define UIC_EIR8 0x00002000 /* 18 External IRQ 8 */ +#define UIC_EIR9 0x00001000 /* 19 External IRQ 9 */ +#define UIC_U0 0x00000800 /* 20 UART0 */ +#define UIC_IR_RCV 0x00000400 /* 21 Serial 1 / Infrared UART Receive */ +#define UIC_IR_XMIT 0x00000200 /* 22 Serial 1 / Infrared UART Transmit */ +#define UIC_IEEE1284 0x00000100 /* 23 IEEE-1284 / PPU */ +#define UIC_DCRX 0x00000080 /* 24 DCRX */ +#define UIC_EIR0 0x00000040 /* 25 External IRQ 0 */ +#define UIC_EIR1 0x00000020 /* 26 External IRQ 1 */ +#define UIC_EIR2 0x00000010 /* 27 External IRQ 2 */ +#define UIC_EIR3 0x00000008 /* 28 External IRQ 3 */ +#define UIC_EIR4 0x00000004 /* 29 External IRQ 4 */ +#define UIC_EIR5 0x00000002 /* 30 External IRQ 5 */ +#define UIC_EIR6 0x00000001 /* 31 External IRQ 6 */ + +#ifdef DCRN_CIC_BASE +#define DCRN_CICCR (DCRN_CIC_BASE + 0x0) /* CIC Control Register */ +#define DCRN_DMAS1 (DCRN_CIC_BASE + 0x1) /* DMA Select1 Register */ +#define DCRN_DMAS2 (DCRN_CIC_BASE + 0x2) /* DMA Select2 Register */ +#define DCRN_CICVCR (DCRN_CIC_BASE + 0x3) /* CIC Video COntro Register */ +#define DCRN_CICSEL3 (DCRN_CIC_BASE + 0x5) /* CIC Select 3 Register */ +#define DCRN_SGPO (DCRN_CIC_BASE + 0x6) /* CIC GPIO Output Register */ +#define DCRN_SGPOD (DCRN_CIC_BASE + 0x7) /* CIC GPIO OD Register */ +#define DCRN_SGPTC (DCRN_CIC_BASE + 0x8) /* CIC GPIO Tristate Ctrl Reg */ +#define DCRN_SGPI (DCRN_CIC_BASE + 0x9) /* CIC GPIO Input Reg */ +#endif + +#ifdef DCRN_DCRX_BASE +#define DCRN_DCRXICR (DCRN_DCRX_BASE + 0x0) /* Internal Control Register */ +#define DCRN_DCRXISR (DCRN_DCRX_BASE + 0x1) /* Internal Status Register */ +#define DCRN_DCRXECR (DCRN_DCRX_BASE + 0x2) /* External Control Register */ +#define DCRN_DCRXESR (DCRN_DCRX_BASE + 0x3) /* External Status Register */ +#define DCRN_DCRXTAR (DCRN_DCRX_BASE + 0x4) /* Target Address Register */ +#define DCRN_DCRXTDR (DCRN_DCRX_BASE + 0x5) /* Target Data Register */ +#define DCRN_DCRXIGR (DCRN_DCRX_BASE + 0x6) /* Interrupt Generation Register */ +#define DCRN_DCRXBCR (DCRN_DCRX_BASE + 0x7) /* Line Buffer Control Register */ +#endif + +#ifdef DCRN_EBC_BASE +#define DCRN_EBCCFGADR (DCRN_EBC_BASE + 0x0) /* Peripheral Controller Address */ +#define DCRN_EBCCFGDATA (DCRN_EBC_BASE + 0x1) /* Peripheral Controller Data */ +#endif + +#ifdef DCRN_EBIMC_BASE +#define DCRN_BRCRH0 (DCRN_EBIMC_BASE + 0x0) /* Bus Region Config High 0 */ +#define DCRN_BRCRH1 (DCRN_EBIMC_BASE + 0x1) /* Bus Region Config High 1 */ +#define DCRN_BRCRH2 (DCRN_EBIMC_BASE + 0x2) /* Bus Region Config High 2 */ +#define DCRN_BRCRH3 (DCRN_EBIMC_BASE + 0x3) /* Bus Region Config High 3 */ +#define DCRN_BRCRH4 (DCRN_EBIMC_BASE + 0x4) /* Bus Region Config High 4 */ +#define DCRN_BRCRH5 (DCRN_EBIMC_BASE + 0x5) /* Bus Region Config High 5 */ +#define DCRN_BRCRH6 (DCRN_EBIMC_BASE + 0x6) /* Bus Region Config High 6 */ +#define DCRN_BRCRH7 (DCRN_EBIMC_BASE + 0x7) /* Bus Region Config High 7 */ +#define DCRN_BRCR0 (DCRN_EBIMC_BASE + 0x10) /* BRC 0 */ +#define DCRN_BRCR1 (DCRN_EBIMC_BASE + 0x11) /* BRC 1 */ +#define DCRN_BRCR2 (DCRN_EBIMC_BASE + 0x12) /* BRC 2 */ +#define DCRN_BRCR3 (DCRN_EBIMC_BASE + 0x13) /* BRC 3 */ +#define DCRN_BRCR4 (DCRN_EBIMC_BASE + 0x14) /* BRC 4 */ +#define DCRN_BRCR5 (DCRN_EBIMC_BASE + 0x15) /* BRC 5 */ +#define DCRN_BRCR6 (DCRN_EBIMC_BASE + 0x16) /* BRC 6 */ +#define DCRN_BRCR7 (DCRN_EBIMC_BASE + 0x17) /* BRC 7 */ +#define DCRN_BEAR0 (DCRN_EBIMC_BASE + 0x20) /* Bus Error Address Register */ +#define DCRN_BESR0 (DCRN_EBIMC_BASE + 0x21) /* Bus Error Status Register */ +#define DCRN_BIUCR (DCRN_EBIMC_BASE + 0x2A) /* Bus Interfac Unit Ctrl Reg */ +#endif + +#ifdef DCRN_SCCR_BASE +#define DCRN_SCCR (DCRN_SCCR_BASE + 0x0) +#endif + +#ifdef DCRN_SDRAM0_BASE +#define DCRN_SDRAM0_CFGADDR (DCRN_SDRAM0_BASE + 0x0) /* Memory Controller Address */ +#define DCRN_SDRAM0_CFGDATA (DCRN_SDRAM0_BASE + 0x1) /* Memory Controller Data */ +#endif + +#ifdef DCRN_OCM0_BASE +#define DCRN_OCMISARC (DCRN_OCM0_BASE + 0x0) /* OCM Instr Side Addr Range Compare */ +#define DCRN_OCMISCR (DCRN_OCM0_BASE + 0x1) /* OCM Instr Side Control */ +#define DCRN_OCMDSARC (DCRN_OCM0_BASE + 0x2) /* OCM Data Side Addr Range Compare */ +#define DCRN_OCMDSCR (DCRN_OCM0_BASE + 0x3) /* OCM Data Side Control */ +#endif + +#include + +#endif /* __ASM_IBMSTBX_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/ibmstb4.c b/arch/ppc/platforms/ibmstb4.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibmstb4.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,61 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibmstb4.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by 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. + * + * History: 12/26/2001 - armin + * initial release + * + */ + +#include +#include + +const struct NS16550* COM_PORTS[] = +{ + (struct NS16550*) UART0_IO_BASE, + (struct NS16550*) UART1_IO_BASE, + (struct NS16550*) UART2_IO_BASE, +}; + +const struct iic_regs* IIC_ADDR[]= +{ + (struct iic_regs*) IIC0_BASE, +}; + + +const struct gpio_regs* GPIO_ADDR[] = +{ + (struct gpio_regs*) GPIO0_BASE, +}; + +const struct ide_regs* IDE_ADDR[] = +{ + (struct ide_regs*) IDE0_IO_ADDR, + +}; diff -Nru a/arch/ppc/platforms/ibmstb4.h b/arch/ppc/platforms/ibmstb4.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ibmstb4.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,268 @@ +/* + * ibmstb4.h + * + * This was dirived from the ibm405gp.h and other previus works in ppc4xx.h + * + * Current maintainer + * Armin Kuster akuster@mvista.com + * DEC, 2001 + * + * + * Copyright 2001 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * Version 1.0 (01/10/10) - A. Kuster + * Initial version + * + * Version 1.1 02/01/17 - A. Kuster + * moved common offsets to ibm405.h + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMSTB4_H__ +#define __ASM_IBMSTB4_H__ + +#include +#include + +/* serial port defines */ +#define STB04xxx_IO_BASE ((uint)0xe0000000) +#define PPC4xx_PCI_IO_ADDR STB04xxx_IO_BASE +#define PPC4xx_ONB_IO_PADDR STB04xxx_IO_BASE +#define PPC4xx_ONB_IO_VADDR ((uint)0xe0000000) +#define PPC4xx_ONB_IO_SIZE ((uint)14*64*1024) + +/* + * map STB04xxx internal i/o address (0x400x00xx) to an address + * which is below the 2GB limit... + * + * 4000 000x uart1 -> 0xe000 000x + * 4001 00xx ppu + * 4002 00xx smart card + * 4003 000x iic + * 4004 000x uart0 + * 4005 0xxx timer + * 4006 00xx gpio + * 4007 00xx smart card + * 400b 000x iic + * 400c 000x scp + * 400d 000x modem + * 400e 000x uart2 +*/ +#define STB04xxx_MAP_IO_ADDR(a) (((uint)(a)) + (STB04xxx_IO_BASE - 0x40000000)) + +#define RS_TABLE_SIZE 3 +#define UART0_INT 20 +#define UART0_IO_BASE (u8 *)0x40040000 +#define UART1_INT 21 +#define UART1_IO_BASE (u8 *)0x40000000 +#define UART2_INT 31 +#define UART2_IO_BASE (u8 *)0x400e0000 + +#define IDE0_IO_ADDR 0x400F0000 +#define IDE0_IO_SIZE 0x200 + +#define IIC0_BASE 0x40030000 +#define IIC1_BASE 0x400b0000 +#define OPB0_BASE 0x40000000 +#define GPIO0_BASE 0x40060000 + +#define UART_NUMS 3 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) \ + STD_UART_OP(2) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) \ + STD_UART_OP(2) +#endif + +#if defined(CONFIG_UART0_TTYS2) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(2) \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRNCAP_DMA_CC 1 /* have DMA chained count capability */ +#define DCRN_DMASR_BASE 0x0E0 +#define DCRN_PLB0_BASE 0x054 +#define DCRN_PLB1_BASE 0x064 +#define DCRN_POB0_BASE 0x0B0 +#define DCRN_SCCR_BASE 0x120 +#define DCRN_UIC0_BASE 0x040 +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRN_CIC_BASE 0x030 +#define DCRN_DMASR_BASE 0x0E0 +#define DCRN_EBIMC_BASE 0x070 +#define DCRN_DCRX_BASE 0x020 +#define DCRN_CPMFR_BASE 0x102 +#define DCRN_SCCR_BASE 0x120 + +#define CPM_IIC0 0x80000000 /* IIC 0 interface */ +#define CPM_I1284 0x40000000 /* IEEE-1284 */ +#define CPM_IIC1 0x20000000 /* IIC 1 interface */ +#define CPM_CPU 0x10000000 /* PPC405B3 clock control */ +#define CPM_AUD 0x08000000 /* Audio Decoder */ +#define CPM_EBIU 0x04000000 /* External Bus Interface Unit */ +#define CPM_SDRAM1 0x02000000 /* SDRAM 1 memory controller */ +#define CPM_DMA 0x01000000 /* DMA controller */ +#define CPM_RES_1 0x00800000 /* reserved */ +#define CPM_RES_2 0x00400000 /* reserved */ +#define CPM_RES_3 0x00200000 /* reserved */ +#define CPM_UART1 0x00100000 /* Serial 1 / Infrared */ +#define CPM_UART0 0x00080000 /* Serial 0 / 16550 */ +#define CPM_DCRX 0x00040000 /* DCR Extension */ +#define CPM_SC0 0x00020000 /* Smart Card 0 */ +#define CPM_RES_4 0x00010000 /* reserved */ +#define CPM_SC1 0x00008000 /* Smart Card 1 */ +#define CPM_SDRAM0 0x00004000 /* SDRAM 0 memory controller */ +#define CPM_XPT54 0x00002000 /* Transport - 54 Mhz */ +#define CPM_CBS 0x00001000 /* Cross Bar Switch */ +#define CPM_GPT 0x00000800 /* GPTPWM */ +#define CPM_GPIO0 0x00000400 /* General Purpose IO 0 */ +#define CPM_DENC 0x00000200 /* Digital video Encoder */ +#define CPM_TMRCLK 0x00000100 /* CPU timers */ +#define CPM_XPT27 0x00000080 /* Transport - 27 Mhz */ +#define CPM_UIC 0x00000040 /* Universal Interrupt Controller */ +#define CPM_RES_5 0x00000020 /* reserved */ +#define CPM_MSI 0x00000010 /* Modem Serial Interface (SSP) */ +#define CPM_UART2 0x00000008 /* Serial Control Port */ +#define CPM_DSCR 0x00000004 /* Descrambler */ +#define CPM_VID2 0x00000002 /* Video Decoder clock domain 2 */ +#define CPM_RES_6 0x00000001 /* reserved */ + +/* 0x80000000 */ +#define UIC_XPORT 0x40000000 /* 1 Transport */ +#define UIC_AUDIO 0x20000000 /* 2 Audio Decoder */ +#define UIC_VIDEO 0x10000000 /* 3 Video Decoder */ +#define UIC_D0 0x08000000 /* 4 DMA Channel 0 */ +#define UIC_D1 0x04000000 /* 5 DMA Channel 1 */ +#define UIC_D2 0x02000000 /* 6 DMA Channel 2 */ +#define UIC_D3 0x01000000 /* 7 DMA Channel 3 */ +#define UIC_SC0 0x00800000 /* 8 SmartCard 0 Controller */ +#define UIC_IIC0 0x00400000 /* 9 IIC 0 */ +#define UIC_IIC1 0x00200000 /* 10 IIC 1 */ +#define UIC_PWM0 0x00100000 /* 11 GPT_PWM 0: Capture Timers */ +#define UIC_PWM1 0x00080000 /* 12 GPT_PWM 1: Compare Timers */ +#define UIC_SCP 0x00040000 /* 13 Serial Control Port */ +#define UIC_SSP 0x00020000 /* 14 Soft Modem/Synchronous Serial Port */ +#define UIC_PWM2 0x00010000 /* 15 GPT_PWM 2: Down Counters */ +#define UIC_SC1 0x00008000 /* 16 SmartCard 1 Controller */ +#define UIC_EIR7 0x00004000 /* 17 External IRQ 7 */ +#define UIC_EIR8 0x00002000 /* 18 External IRQ 8 */ +#define UIC_EIR9 0x00001000 /* 19 External IRQ 9 */ +#define UIC_U0 0x00000800 /* 20 UART0 */ +#define UIC_IR_RCV 0x00000400 /* 21 Serial 1 / Infrared UART Receive */ +#define UIC_IR_XMIT 0x00000200 /* 22 Serial 1 / Infrared UART Transmit */ +#define UIC_IEEE1284 0x00000100 /* 23 IEEE-1284 / PPU */ +#define UIC_DCRX 0x00000080 /* 24 DCRX */ +#define UIC_EIR0 0x00000040 /* 25 External IRQ 0 */ +#define UIC_EIR1 0x00000020 /* 26 External IRQ 1 */ +#define UIC_EIR2 0x00000010 /* 27 External IRQ 2 */ +#define UIC_EIR3 0x00000008 /* 28 External IRQ 3 */ +#define UIC_EIR4 0x00000004 /* 29 External IRQ 4 */ +#define UIC_EIR5 0x00000002 /* 30 External IRQ 5 */ +#define UIC_EIR6 0x00000001 /* 31 External IRQ 6 */ + +#define DCRN_BEAR (DCRN_BE_BASE + 0x0) /* Bus Error Address Register */ +#define DCRN_BESR (DCRN_BE_BASE + 0x1) /* Bus Error Syndrome Register */ +/* DCRN_BESR */ +#define BESR_DSES 0x80000000 /* Data-Side Error Status */ +#define BESR_DMES 0x40000000 /* DMA Error Status */ +#define BESR_RWS 0x20000000 /* Read/Write Status */ +#define BESR_ETMASK 0x1C000000 /* Error Type */ +#define ET_PROT 0 +#define ET_PARITY 1 +#define ET_NCFG 2 +#define ET_BUSERR 4 +#define ET_BUSTO 6 + +#define CHR1_CETE 0x00800000 /* CPU external timer enable */ +#define CHR1_PCIPW 0x00008000 /* PCI Int enable/Peripheral Write enable */ + +#define DCRN_CICCR (DCRN_CIC_BASE + 0x0) /* CIC Control Register */ +#define DCRN_DMAS1 (DCRN_CIC_BASE + 0x1) /* DMA Select1 Register */ +#define DCRN_DMAS2 (DCRN_CIC_BASE + 0x2) /* DMA Select2 Register */ +#define DCRN_CICVCR (DCRN_CIC_BASE + 0x3) /* CIC Video COntro Register */ +#define DCRN_CICSEL3 (DCRN_CIC_BASE + 0x5) /* CIC Select 3 Register */ +#define DCRN_SGPO (DCRN_CIC_BASE + 0x6) /* CIC GPIO Output Register */ +#define DCRN_SGPOD (DCRN_CIC_BASE + 0x7) /* CIC GPIO OD Register */ +#define DCRN_SGPTC (DCRN_CIC_BASE + 0x8) /* CIC GPIO Tristate Ctrl Reg */ +#define DCRN_SGPI (DCRN_CIC_BASE + 0x9) /* CIC GPIO Input Reg */ + +#define DCRN_DCRXICR (DCRN_DCRX_BASE + 0x0) /* Internal Control Register */ +#define DCRN_DCRXISR (DCRN_DCRX_BASE + 0x1) /* Internal Status Register */ +#define DCRN_DCRXECR (DCRN_DCRX_BASE + 0x2) /* External Control Register */ +#define DCRN_DCRXESR (DCRN_DCRX_BASE + 0x3) /* External Status Register */ +#define DCRN_DCRXTAR (DCRN_DCRX_BASE + 0x4) /* Target Address Register */ +#define DCRN_DCRXTDR (DCRN_DCRX_BASE + 0x5) /* Target Data Register */ +#define DCRN_DCRXIGR (DCRN_DCRX_BASE + 0x6) /* Interrupt Generation Register */ +#define DCRN_DCRXBCR (DCRN_DCRX_BASE + 0x7) /* Line Buffer Control Register */ + +#define DCRN_BRCRH0 (DCRN_EBIMC_BASE + 0x0) /* Bus Region Config High 0 */ +#define DCRN_BRCRH1 (DCRN_EBIMC_BASE + 0x1) /* Bus Region Config High 1 */ +#define DCRN_BRCRH2 (DCRN_EBIMC_BASE + 0x2) /* Bus Region Config High 2 */ +#define DCRN_BRCRH3 (DCRN_EBIMC_BASE + 0x3) /* Bus Region Config High 3 */ +#define DCRN_BRCRH4 (DCRN_EBIMC_BASE + 0x4) /* Bus Region Config High 4 */ +#define DCRN_BRCRH5 (DCRN_EBIMC_BASE + 0x5) /* Bus Region Config High 5 */ +#define DCRN_BRCRH6 (DCRN_EBIMC_BASE + 0x6) /* Bus Region Config High 6 */ +#define DCRN_BRCRH7 (DCRN_EBIMC_BASE + 0x7) /* Bus Region Config High 7 */ +#define DCRN_BRCR0 (DCRN_EBIMC_BASE + 0x10) /* BRC 0 */ +#define DCRN_BRCR1 (DCRN_EBIMC_BASE + 0x11) /* BRC 1 */ +#define DCRN_BRCR2 (DCRN_EBIMC_BASE + 0x12) /* BRC 2 */ +#define DCRN_BRCR3 (DCRN_EBIMC_BASE + 0x13) /* BRC 3 */ +#define DCRN_BRCR4 (DCRN_EBIMC_BASE + 0x14) /* BRC 4 */ +#define DCRN_BRCR5 (DCRN_EBIMC_BASE + 0x15) /* BRC 5 */ +#define DCRN_BRCR6 (DCRN_EBIMC_BASE + 0x16) /* BRC 6 */ +#define DCRN_BRCR7 (DCRN_EBIMC_BASE + 0x17) /* BRC 7 */ +#define DCRN_BEAR0 (DCRN_EBIMC_BASE + 0x20) /* Bus Error Address Register */ +#define DCRN_BESR0 (DCRN_EBIMC_BASE + 0x21) /* Bus Error Status Register */ +#define DCRN_BIUCR (DCRN_EBIMC_BASE + 0x2A) /* Bus Interfac Unit Ctrl Reg */ + +#include + +#endif /* __ASM_IBMSTB4_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/ip860.h b/arch/ppc/platforms/ip860.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ip860.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,36 @@ +/* + * MicroSys IP860 VMEBus board specific definitions + * + * Copyright (c) 2000, 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_IP860_H +#define __MACH_IP860_H + +#include + +#include + +#define IP860_IMMR_BASE 0xF1000000 /* phys. addr of IMMR */ +#define IP860_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR IP860_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE IP860_IMAP_SIZE /* mapped size of IMMR area */ + +/* + * MPC8xx Chip Select Usage + */ +#define IP860_BOOT_CS 0 /* Boot (VMEBus or Flash) Chip Select 0 */ +#define IP860_FLASH_CS 1 /* Flash is on Chip Select 1 */ +#define IP860_SDRAM_CS 2 /* SDRAM is on Chip Select 2 */ +#define IP860_SRAM_CS 3 /* SRAM is on Chip Select 3 */ +#define IP860_BCSR_CS 4 /* BCSR is on Chip Select 4 */ +#define IP860_IP_CS 5 /* IP Slots are on Chip Select 5 */ +#define IP860_VME_STD_CS 6 /* VME Standard I/O is on Chip Select 6 */ +#define IP860_VME_SHORT_CS 7 /* VME Short I/O is on Chip Select 7 */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_IP860_H */ diff -Nru a/arch/ppc/platforms/ivms8.h b/arch/ppc/platforms/ivms8.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/ivms8.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,59 @@ +/* + * BK Id: SCCS/s.ivms8.h 1.8 10/26/01 10:14:09 trini + */ +/* + * Speech Design Integrated Voicemail board specific definitions + * - IVMS8 (small, 8 channels) + * - IVML24 (large, 24 channels) + * + * In 2.5 when we force a new bootloader, we can merge these two, and add + * in _MACH_'s for them. -- Tom + * + * Copyright (c) 2000, 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IVMS8_H__ +#define __ASM_IVMS8_H__ + +#include + +#include + +#define IVMS_IMMR_BASE 0xFFF00000 /* phys. addr of IMMR */ +#define IVMS_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR IVMS_IMMR_BASE /* phys. base address of IMMR area */ +#define IMAP_SIZE IVMS_IMAP_SIZE /* mapped size of IMMR area */ + +#define PCMCIA_MEM_ADDR ((uint)0xFE100000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) + +#define FEC_INTERRUPT 9 /* = SIU_LEVEL4 */ +#define IDE0_INTERRUPT 10 /* = IRQ5 */ +#define CPM_INTERRUPT 11 /* = SIU_LEVEL5 (was: SIU_LEVEL2) */ +#define PHY_INTERRUPT 12 /* = IRQ6 */ + +/* override the default number of IDE hardware interfaces */ +#define MAX_HWIFS 1 + +/* + * Definitions for IDE0 Interface + */ +#define IDE0_BASE_OFFSET 0x0000 /* Offset in PCMCIA memory */ +#define IDE0_DATA_REG_OFFSET 0x0000 +#define IDE0_ERROR_REG_OFFSET 0x0081 +#define IDE0_NSECTOR_REG_OFFSET 0x0082 +#define IDE0_SECTOR_REG_OFFSET 0x0083 +#define IDE0_LCYL_REG_OFFSET 0x0084 +#define IDE0_HCYL_REG_OFFSET 0x0085 +#define IDE0_SELECT_REG_OFFSET 0x0086 +#define IDE0_STATUS_REG_OFFSET 0x0087 +#define IDE0_CONTROL_REG_OFFSET 0x0106 +#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ + +/* We don't use the 8259. */ +#define NR_8259_INTS 0 + +#endif /* __ASM_IVMS8_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/k2.h b/arch/ppc/platforms/k2.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/k2.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,84 @@ +/* + * arch/ppc/platforms/k2.h + * + * Definitions for SBS K2 board support + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __PPC_PLATFORMS_K2_H +#define __PPC_PLATFORMS_K2_H + +/* + * SBS K2 definitions + */ + +#define K2_PCI64_BAR 0xff400000 +#define K2_PCI32_BAR 0xff500000 + +#define K2_PCI64_CONFIG_ADDR (K2_PCI64_BAR + 0x000f8000) +#define K2_PCI64_CONFIG_DATA (K2_PCI64_BAR + 0x000f8010) + +#define K2_PCI32_CONFIG_ADDR (K2_PCI32_BAR + 0x000f8000) +#define K2_PCI32_CONFIG_DATA (K2_PCI32_BAR + 0x000f8010) + +#define K2_PCI64_MEM_BASE 0xd0000000 +#define K2_PCI64_IO_BASE 0x80100000 + +#define K2_PCI32_MEM_BASE 0xc0000000 +#define K2_PCI32_IO_BASE 0x80000000 + +#define K2_PCI32_SYS_MEM_BASE 0x80000000 +#define K2_PCI64_SYS_MEM_BASE K2_PCI32_SYS_MEM_BASE + +#define K2_PCI32_LOWER_MEM 0x00000000 +#define K2_PCI32_UPPER_MEM 0x0fffffff +#define K2_PCI32_LOWER_IO 0x00000000 +#define K2_PCI32_UPPER_IO 0x000fffff + +#define K2_PCI64_LOWER_MEM 0x10000000 +#define K2_PCI64_UPPER_MEM 0x1fffffff +#define K2_PCI64_LOWER_IO 0x00100000 +#define K2_PCI64_UPPER_IO 0x001fffff + +#define K2_ISA_IO_BASE K2_PCI32_IO_BASE +#define K2_ISA_MEM_BASE K2_PCI32_MEM_BASE + +#define K2_BOARD_ID_REG (K2_ISA_IO_BASE + 0x800) +#define K2_MISC_REG (K2_ISA_IO_BASE + 0x804) +#define K2_MSIZ_GEO_REG (K2_ISA_IO_BASE + 0x808) +#define K2_HOT_SWAP_REG (K2_ISA_IO_BASE + 0x80c) +#define K2_PLD2_REG (K2_ISA_IO_BASE + 0x80e) +#define K2_PLD3_REG (K2_ISA_IO_BASE + 0x80f) + +#define K2_BUS_SPD(board_id) (board_id >> 2) & 3 + +#define K2_RTC_BASE_OFFSET 0x90000 +#define K2_RTC_BASE_ADDRESS (K2_PCI32_MEM_BASE + K2_RTC_BASE_OFFSET) +#define K2_RTC_SIZE 0x8000 + +#define K2_MEM_SIZE_MASK 0xe0 +#define K2_MEM_SIZE(size_reg) (size_reg & K2_MEM_SIZE_MASK) >> 5 +#define K2_MEM_SIZE_1GB 0x40000000 +#define K2_MEM_SIZE_512MB 0x20000000 +#define K2_MEM_SIZE_256MB 0x10000000 +#define K2_MEM_SIZE_128MB 0x08000000 + +#define K2_L2CACHE_MASK 0x03 /* Mask for 2 L2 Cache bits */ +#define K2_L2CACHE_512KB 0x00 /* 512KB */ +#define K2_L2CACHE_256KB 0x01 /* 256KB */ +#define K2_L2CACHE_1MB 0x02 /* 1MB */ +#define K2_L2CACHE_NONE 0x03 /* None */ + +#define K2_GEO_ADR_MASK 0x1f + +#define K2_SYS_SLOT_MASK 0x08 + +#endif /* __PPC_PLATFORMS_K2_H */ diff -Nru a/arch/ppc/platforms/k2_pci.c b/arch/ppc/platforms/k2_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/k2_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,365 @@ +/* + * arch/ppc/platforms/k2_pci.c + * + * PCI support for SBS K2 + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "cpc710.h" +#include "k2.h" + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif /* DEBUG */ + +static inline int __init +k2_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + /* + * Check our hose index. If we are zero then we are on the + * local PCI hose, otherwise we are on the cPCI hose. + */ + if (!hose->index) + { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {1, 0, 0, 0}, /* Ethernet */ + {5, 5, 5, 5}, /* PMC Site 1 */ + {6, 6, 6, 6}, /* PMC Site 2 */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* PCI-ISA Bridge */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {15, 0, 0, 0}, /* M5229 IDE */ + }; + const long min_idsel = 3, max_idsel = 17, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } + else + { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {10, 11, 12, 9}, /* cPCI slot 8 */ + {11, 12, 9, 10}, /* cPCI slot 7 */ + {12, 9, 10, 11}, /* cPCI slot 6 */ + {9, 10, 11, 12}, /* cPCI slot 5 */ + {10, 11, 12, 9}, /* cPCI slot 4 */ + {11, 12, 9, 10}, /* cPCI slot 3 */ + {12, 9, 10, 11}, /* cPCI slot 2 */ + }; + const long min_idsel = 15, max_idsel = 21, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } +} + +void k2_pcibios_fixup(void) +{ +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + struct pci_dev *ide_dev; + + /* + * Enable DMA support on hdc + */ + ide_dev = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M5229, + NULL); + + if (ide_dev) { + + unsigned long ide_dma_base; + + ide_dma_base = pci_resource_start(ide_dev, 4); + outb(0x00, ide_dma_base+0x2); + outb(0x20, ide_dma_base+0xa); + } +#endif +} + +void k2_pcibios_fixup_resources(struct pci_dev *dev) +{ + int i; + + if ((dev->vendor == PCI_VENDOR_ID_IBM) && + (dev->device == PCI_DEVICE_ID_IBM_CPC710_PCI64)) + { + DBG("Fixup CPC710 resources\n"); + for (i=0; iresource[i].start = 0; + dev->resource[i].end = 0; + } + } +} + +void k2_setup_hoses(void) +{ + struct pci_controller *hose_a, *hose_b; + + /* + * Reconfigure CPC710 memory map so + * we have some more PCI memory space. + */ + + /* Set FPHB mode */ + __raw_writel(0x808000e0, PGCHP); /* Set FPHB mode */ + + /* PCI32 mappings */ + __raw_writel(0x00000000, K2_PCI32_BAR+PIBAR); /* PCI I/O base */ + __raw_writel(0x00000000, K2_PCI32_BAR+PMBAR); /* PCI Mem base */ + __raw_writel(0xf0000000, K2_PCI32_BAR+MSIZE); /* 256MB */ + __raw_writel(0xfff00000, K2_PCI32_BAR+IOSIZE); /* 1MB */ + __raw_writel(0xc0000000, K2_PCI32_BAR+SMBAR); /* Base@0xc0000000 */ + __raw_writel(0x80000000, K2_PCI32_BAR+SIBAR); /* Base@0x80000000 */ + __raw_writel(0x000000c0, K2_PCI32_BAR+PSSIZE); /* 1GB space */ + __raw_writel(0x000000c0, K2_PCI32_BAR+PPSIZE); /* 1GB space */ + __raw_writel(0x00000000, K2_PCI32_BAR+BARPS); /* Base@0x00000000 */ + __raw_writel(0x00000000, K2_PCI32_BAR+BARPP); /* Base@0x00000000 */ + __raw_writel(0x00000080, K2_PCI32_BAR+PSBAR); /* Base@0x80 */ + __raw_writel(0x00000000, K2_PCI32_BAR+PPBAR); + + /* PCI64 mappings */ + __raw_writel(0x00100000, K2_PCI64_BAR+PIBAR); /* PCI I/O base */ + __raw_writel(0x10000000, K2_PCI64_BAR+PMBAR); /* PCI Mem base */ + __raw_writel(0xf0000000, K2_PCI64_BAR+MSIZE); /* 256MB */ + __raw_writel(0xfff00000, K2_PCI64_BAR+IOSIZE); /* 1MB */ + __raw_writel(0xd0000000, K2_PCI64_BAR+SMBAR); /* Base@0xd0000000 */ + __raw_writel(0x80100000, K2_PCI64_BAR+SIBAR); /* Base@0x80100000 */ + __raw_writel(0x000000c0, K2_PCI64_BAR+PSSIZE); /* 1GB space */ + __raw_writel(0x000000c0, K2_PCI64_BAR+PPSIZE); /* 1GB space */ + __raw_writel(0x00000000, K2_PCI64_BAR+BARPS); /* Base@0x00000000 */ + __raw_writel(0x00000000, K2_PCI64_BAR+BARPP); /* Base@0x00000000 */ + + /* Setup PCI32 hose */ + hose_a = pcibios_alloc_controller(); + if (!hose_a) + return; + + hose_a->first_busno = 0; + hose_a->last_busno = 0xff; + hose_a->pci_mem_offset = K2_PCI32_MEM_BASE; + + pci_init_resource(&hose_a->io_resource, + K2_PCI32_LOWER_IO, + K2_PCI32_UPPER_IO, + IORESOURCE_IO, + "PCI32 host bridge"); + + pci_init_resource(&hose_a->mem_resources[0], + K2_PCI32_LOWER_MEM + K2_PCI32_MEM_BASE, + K2_PCI32_UPPER_MEM + K2_PCI32_MEM_BASE, + IORESOURCE_MEM, + "PCI32 host bridge"); + + hose_a->io_space.start = K2_PCI32_LOWER_IO; + hose_a->io_space.end = K2_PCI32_UPPER_IO; + hose_a->mem_space.start = K2_PCI32_LOWER_MEM; + hose_a->mem_space.end = K2_PCI32_UPPER_MEM; + hose_a->io_base_virt = (void *)K2_ISA_IO_BASE; + + setup_indirect_pci(hose_a, K2_PCI32_CONFIG_ADDR, K2_PCI32_CONFIG_DATA); + + /* Initialize PCI32 bus registers */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_BUS_NUMBER, + hose_a->first_busno); + + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + hose_a->last_busno); + + /* Enable PCI interrupt polling */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), + 0x45, + 0x80); + + /* Route polled PCI interrupts */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), + 0x48, + 0x58); + + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), + 0x49, + 0x07); + + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), + 0x4a, + 0x31); + + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), + 0x4b, + 0xb9); + + /* route secondary IDE channel interrupt to IRQ 15 */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), + 0x75, + 0x0f); + + /* enable IDE controller IDSEL */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), + 0x58, + 0x48); + + /* Enable IDE function */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(17, 0), + 0x50, + 0x03); + + /* Set M5229 IDE controller to native mode */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(17, 0), + PCI_CLASS_PROG, + 0xdf); + + hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); + + /* Write out correct max subordinate bus number for hose A */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + hose_a->last_busno); + + /* Only setup PCI64 hose if we are in the system slot */ + if (!(readb(K2_MISC_REG) & K2_SYS_SLOT_MASK)) + { + /* Setup PCI64 hose */ + hose_b = pcibios_alloc_controller(); + if (!hose_b) + return; + + hose_b->first_busno = hose_a->last_busno + 1; + hose_b->last_busno = 0xff; + + /* Reminder: quit changing the following, it is correct. */ + hose_b->pci_mem_offset = K2_PCI32_MEM_BASE; + + pci_init_resource(&hose_b->io_resource, + K2_PCI64_LOWER_IO, + K2_PCI64_UPPER_IO, + IORESOURCE_IO, + "PCI64 host bridge"); + + pci_init_resource(&hose_b->mem_resources[0], + K2_PCI64_LOWER_MEM + K2_PCI32_MEM_BASE, + K2_PCI64_UPPER_MEM + K2_PCI32_MEM_BASE, + IORESOURCE_MEM, + "PCI64 host bridge"); + + hose_b->io_space.start = K2_PCI64_LOWER_IO; + hose_b->io_space.end = K2_PCI64_UPPER_IO; + hose_b->mem_space.start = K2_PCI64_LOWER_MEM; + hose_b->mem_space.end = K2_PCI64_UPPER_MEM; + hose_b->io_base_virt = (void *)K2_ISA_IO_BASE; + + setup_indirect_pci(hose_b, + K2_PCI64_CONFIG_ADDR, + K2_PCI64_CONFIG_DATA); + + /* Initialize PCI64 bus registers */ + early_write_config_byte(hose_b, + 0, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + 0xff); + + early_write_config_byte(hose_b, + 0, + PCI_DEVFN(0, 0), + CPC710_BUS_NUMBER, + hose_b->first_busno); + + hose_b->last_busno = pciauto_bus_scan(hose_b, + hose_b->first_busno); + + /* Write out correct max subordinate bus number for hose B */ + early_write_config_byte(hose_b, + hose_b->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + hose_b->last_busno); + + /* Configure PCI64 PSBAR */ + early_write_config_dword(hose_b, + hose_b->first_busno, + PCI_DEVFN(0, 0), + PCI_BASE_ADDRESS_0, + K2_PCI64_SYS_MEM_BASE); + } + + /* Configure i8259 level/edge settings */ + outb(0x62, 0x4d0); + outb(0xde, 0x4d1); + +#ifdef CONFIG_CPC710_DATA_GATHERING + { + unsigned int tmp; + tmp = __raw_readl(ABCNTL); + /* Enable data gathering on both PCI interfaces */ + __raw_writel(tmp | 0x05000000, ABCNTL); + } +#endif + + ppc_md.pcibios_fixup = k2_pcibios_fixup; + ppc_md.pcibios_fixup_resources = k2_pcibios_fixup_resources; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = k2_map_irq; +} diff -Nru a/arch/ppc/platforms/k2_setup.c b/arch/ppc/platforms/k2_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/k2_setup.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,382 @@ +/* + * arch/ppc/platforms/k2_setup.c + * + * Board setup routines for SBS K2 + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#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 "k2.h" + +extern void k2_setup_hoses(void); +extern unsigned long loops_per_jiffy; + +static unsigned int cpu_7xx[16] = { + 0, 15, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 +}; +static unsigned int cpu_6xx[16] = { + 0, 0, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 0, 12, 7, 0 +}; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* IDE functions */ +static int +k2_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +static void +k2_ide_request_region(ide_ioreg_t from, unsigned int extent, + const char *name) +{ + request_region(from, extent, name); +} + +static void +k2_ide_release_region(ide_ioreg_t from, unsigned int extent) +{ + release_region(from, extent); +} + +static void __init +k2_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, int *irq) +{ + ide_ioreg_t reg = data_port; + int i = 8; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = reg; + reg += 1; + } + if (ctrl_port) + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + else + hw->io_ports[IDE_CONTROL_OFFSET] = + hw->io_ports[IDE_DATA_OFFSET] + 0x206; + + if (irq != NULL) + *irq = 0; +} +#endif + +static int +k2_get_bus_speed(void) +{ + int bus_speed; + unsigned char board_id; + + board_id = *(unsigned char *)K2_BOARD_ID_REG; + + switch( K2_BUS_SPD(board_id) ) { + + case 0: + default: + bus_speed = 100000000; + break; + + case 1: + bus_speed = 83333333; + break; + + case 2: + bus_speed = 75000000; + break; + + case 3: + bus_speed = 66666666; + break; + } + return bus_speed; +} + +static int +k2_get_cpu_speed(void) +{ + unsigned long hid1; + int cpu_speed; + + hid1 = mfspr(HID1) >> 28; + + if ((mfspr(PVR) >> 16) == 8) + hid1 = cpu_7xx[hid1]; + else + hid1 = cpu_6xx[hid1]; + + cpu_speed = k2_get_bus_speed()*hid1/2; + return cpu_speed; +} + +static void __init +k2_calibrate_decr(void) +{ + int freq, divisor = 4; + + /* determine processor bus speed */ + freq = k2_get_bus_speed(); + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); +} + +static int +k2_show_cpuinfo(struct seq_file *m) +{ + unsigned char k2_geo_bits, k2_system_slot; + + seq_printf(m, "vendor\t\t: SBS\n"); + seq_printf(m, "machine\t\t: K2\n"); + seq_printf(m, "cpu speed\t: %dMhz\n", k2_get_cpu_speed()/1000000); + seq_printf(m, "bus speed\t: %dMhz\n", k2_get_bus_speed()/1000000); + seq_printf(m, "memory type\t: SDRAM\n"); + + k2_geo_bits = readb(K2_MSIZ_GEO_REG) & K2_GEO_ADR_MASK; + k2_system_slot = !(readb(K2_MISC_REG) & K2_SYS_SLOT_MASK); + seq_printf(m, "backplane\t: %s slot board", + k2_system_slot ? "System" : "Non system"); + seq_printf(m, "with geographical address %x\n", k2_geo_bits); + + return 0; +} + +extern char cmd_line[]; + +TODC_ALLOC(); + +static void __init +k2_setup_arch(void) +{ + unsigned int cpu; + + /* Setup TODC access */ + TODC_INIT(TODC_TYPE_MK48T37, 0, 0, + ioremap(K2_RTC_BASE_ADDRESS, K2_RTC_SIZE), + 8); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Setup PCI host bridges */ + k2_setup_hoses(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00FF); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x1601); /* /dev/hdc1 */ +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + /* Identify the system */ + printk("System Identification: SBS K2 - PowerPC 750 @ %d Mhz\n", k2_get_cpu_speed()/1000000); + printk("SBS K2 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + /* Identify the CPU manufacturer */ + cpu = PVR_REV(mfspr(PVR)); + printk("CPU manufacturer: %s [rev=%04x]\n", (cpu & (1<<15)) ? "IBM" : + "Motorola", cpu); +} + +static void +k2_restart(char *cmd) +{ + __cli(); + /* SRR0 has system reset vector, SRR1 has default MSR value */ + /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */ + __asm__ __volatile__ + ("lis 3,0xfff0\n\t" + "ori 3,3,0x0100\n\t" + "mtspr 26,3\n\t" + "li 3,0\n\t" + "mtspr 27,3\n\t" + "rfi\n\t"); + for(;;); +} + +static void +k2_power_off(void) +{ + for(;;); +} + +static void +k2_halt(void) +{ + k2_restart(NULL); +} + +/* + * Set BAT 3 to map PCI32 I/O space. + */ +static __inline__ void +k2_set_bat(void) +{ + unsigned long bat3u, bat3l; + static int mapping_set = 0; + + if (!mapping_set) + { + __asm__ __volatile__ + ("lis %0,0x8000\n\t" + "ori %1,%0,0x002a\n\t" + "ori %0,%0,0x1ffe\n\t" + "mtspr 0x21e,%0\n\t" + "mtspr 0x21f,%1\n\t" + "isync\n\t" + "sync\n\t" + : "=r" (bat3u), "=r" (bat3l)); + + mapping_set = 1; + } + return; +} + +static unsigned long __init +k2_find_end_of_memory(void) +{ + unsigned long total; + unsigned char msize = 7; /* Default to 128MB */ + + k2_set_bat(); + + msize = K2_MEM_SIZE(readb(K2_MSIZ_GEO_REG)); + + switch (msize) + { + case 2: + /* + * This will break without a lowered + * KERNELBASE or CONFIG_HIGHMEM on. + * It seems non 1GB builds exist yet, + * though. + */ + total = K2_MEM_SIZE_1GB; + break; + case 3: + case 4: + total = K2_MEM_SIZE_512MB; + break; + case 5: + case 6: + total = K2_MEM_SIZE_256MB; + break; + case 7: + total = K2_MEM_SIZE_128MB; + break; + default: + printk("K2: Invalid memory size detected, defaulting to 128MB\n"); + total = K2_MEM_SIZE_128MB; + break; + } + return total; +} + +static void __init +k2_map_io(void) +{ + io_block_mapping(K2_PCI32_IO_BASE, + K2_PCI32_IO_BASE, + 0x00200000, + _PAGE_IO); + io_block_mapping(0xff000000, + 0xff000000, + 0x01000000, + _PAGE_IO); +} + +static void __init +k2_init_irq(void) +{ + int i; + + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].handler = &i8259_pic; + + i8259_init(NULL); +} + +static int +k2_get_irq(struct pt_regs *regs) +{ + return i8259_poll(); +} + +void __init platform_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7) +{ + parse_bootinfo((struct bi_record *) (r3 + KERNELBASE)); + + isa_io_base = K2_ISA_IO_BASE; + isa_mem_base = K2_ISA_MEM_BASE; + pci_dram_offset = K2_PCI32_SYS_MEM_BASE; + + ppc_md.setup_arch = k2_setup_arch; + ppc_md.show_cpuinfo = k2_show_cpuinfo; + ppc_md.init_IRQ = k2_init_irq; + ppc_md.get_irq = k2_get_irq; + + ppc_md.find_end_of_memory = k2_find_end_of_memory; + ppc_md.setup_io_mappings = k2_map_io; + + ppc_md.restart = k2_restart; + ppc_md.power_off = k2_power_off; + ppc_md.halt = k2_halt; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = k2_calibrate_decr; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.ide_check_region = k2_ide_check_region; + ppc_ide_md.ide_request_region = k2_ide_request_region; + ppc_ide_md.ide_release_region = k2_ide_release_region; + ppc_ide_md.ide_init_hwif = k2_ide_init_hwif_ports; +#endif +} + diff -Nru a/arch/ppc/platforms/lantec.h b/arch/ppc/platforms/lantec.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/lantec.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,21 @@ +/* + * LANTEC board specific definitions + * + * Copyright (c) 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_LANTEC_H +#define __MACH_LANTEC_H + +#include + +#include + +#define IMAP_ADDR 0xFFF00000 /* physical base address of IMMR area */ +#define IMAP_SIZE (64 * 1024) /* mapped size of IMMR area */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_LANTEC_H */ diff -Nru a/arch/ppc/platforms/lopec_pci.c b/arch/ppc/platforms/lopec_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/lopec_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,108 @@ +/* + * arch/ppc/platforms/lopec_pci.c + * + * PCI setup routines for the Motorola LoPEC. + * + * Author: Dan Cox + * danc@mvista.com (or, alternately, source@mvista.com) + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static inline int __init +lopec_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + int irq; + static char pci_irq_table[][4] = { + {16, 0, 0, 0}, /* ID 11 - Winbond */ + {22, 0, 0, 0}, /* ID 12 - SCSI */ + {0, 0, 0, 0}, /* ID 13 - nothing */ + {17, 0, 0, 0}, /* ID 14 - 82559 Ethernet */ + {27, 0, 0, 0}, /* ID 15 - USB */ + {23, 0, 0, 0}, /* ID 16 - PMC slot 1 */ + {24, 0, 0, 0}, /* ID 17 - PMC slot 2 */ + {25, 0, 0, 0}, /* ID 18 - PCI slot */ + {0, 0, 0, 0}, /* ID 19 - nothing */ + {0, 0, 0, 0}, /* ID 20 - nothing */ + {0, 0, 0, 0}, /* ID 21 - nothing */ + {0, 0, 0, 0}, /* ID 22 - nothing */ + {0, 0, 0, 0}, /* ID 23 - nothing */ + {0, 0, 0, 0}, /* ID 24 - PMC slot 1b */ + {0, 0, 0, 0}, /* ID 25 - nothing */ + {0, 0, 0, 0} /* ID 26 - PMC Slot 2b */ + }; + const long min_idsel = 11, max_idsel = 26, irqs_per_slot = 4; + + irq = PCI_IRQ_TABLE_LOOKUP; + if (!irq) + return 0; + + return irq; +} + +void __init +lopec_setup_winbond_83553(struct pci_controller *hose) +{ + int devfn; + + devfn = PCI_DEVFN(11,0); + + /* IDE interrupt routing (primary 14, secondary 15) */ + early_write_config_byte(hose, 0, devfn, 0x43, 0xef); + /* PCI interrupt routing */ + early_write_config_word(hose, 0, devfn, 0x44, 0x0000); + + /* ISA-PCI address decoder */ + early_write_config_byte(hose, 0, devfn, 0x48, 0xf0); + + /* RTC, kb, not used in PPC */ + early_write_config_byte(hose, 0, devfn, 0x4d, 0x00); + early_write_config_byte(hose, 0, devfn, 0x4e, 0x04); + devfn = PCI_DEVFN(11, 1); + early_write_config_byte(hose, 0, devfn, 0x09, 0x8f); + early_write_config_dword(hose, 0, devfn, 0x40, 0x00ff0011); +} + +void __init +lopec_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + if (mpc10x_bridge_init(hose, + MPC10X_MEM_MAP_B, + MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE) == 0) { + + hose->mem_resources[0].end = 0xffffffff; + lopec_setup_winbond_83553(hose); + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = lopec_map_irq; + } +} diff -Nru a/arch/ppc/platforms/lopec_serial.h b/arch/ppc/platforms/lopec_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/lopec_serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,41 @@ +/* + * include/asm-ppc/lopec_serial.h + * + * Definitions for Motorola LoPEC board. + * + * Author: Dan Cox + * danc@mvista.com (or, alternately, source@mvista.com) + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __H_LOPEC_SERIAL +#define __H_LOPEC_SERIAL + +#define RS_TABLE_SIZE 3 + +#define BASE_BAUD (1843200 / 16) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, 0xffe10000, 29, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe10000, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, 0xffe11000, 20, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe11000, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, 0xffe12000, 21, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe12000, \ + io_type: SERIAL_IO_MEM } + +#endif diff -Nru a/arch/ppc/platforms/lopec_setup.c b/arch/ppc/platforms/lopec_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/lopec_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,358 @@ +/* + * arch/ppc/platforms/lopec_setup.c + * + * Setup routines for the Motorola LoPEC. + * + * Author: Dan Cox + * danc@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#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 + +#define LOPEC_SIO_IRQ 16 +#define LOPEC_SYSSTAT1 0xffe00000 + +extern void lopec_find_bridges(void); + +static u_char lopec_openpic_initsenses[32] __initdata = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1 +}; + + +static int +lopec_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: Motorola LoPec\n"); + return 0; +} + +static u32 +lopec_irq_cannonicalize(u32 irq) +{ + if (irq == 2) + return 9; + else + return irq; +} + +static void +lopec_restart(char *cmd) +{ + /* force a hard reset, if possible */ + unsigned char reg = *((unsigned char *) LOPEC_SYSSTAT1); + reg |= 0x80; + *((unsigned char *) LOPEC_SYSSTAT1) = reg; + + __cli(); + while(1); +} + +static void +lopec_halt(void) +{ + __cli(); + while(1); +} + +static void +lopec_power_off(void) +{ + lopec_halt(); +} + +static int +lopec_get_irq(struct pt_regs *regs) +{ + int irq, cascade_irq; + + irq = openpic_irq(); + + if (irq == LOPEC_SIO_IRQ) { + cascade_irq = i8259_poll(); + + if (cascade_irq != -1) { + irq = cascade_irq; + openpic_eoi(); + } + } + else if (irq == OPENPIC_VEC_SPURIOUS) + irq = -1; + + return irq; +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +int lopec_ide_ports_known = 0; +static ide_ioreg_t lopec_ide_regbase[MAX_HWIFS]; +static ide_ioreg_t lopec_ide_ctl_regbase[MAX_HWIFS]; +static ide_ioreg_t lopec_idedma_regbase; + +static void +lopec_ide_probe(void) +{ + struct pci_dev *dev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_82C105, + NULL); + lopec_ide_ports_known = 1; + + if (dev) { + lopec_ide_regbase[0] = dev->resource[0].start; + lopec_ide_regbase[1] = dev->resource[2].start; + lopec_ide_ctl_regbase[0] = dev->resource[1].start; + lopec_ide_ctl_regbase[1] = dev->resource[3].start; + lopec_idedma_regbase = dev->resource[4].start; + } +} + +static int +lopec_ide_default_irq(ide_ioreg_t base) +{ + if (lopec_ide_ports_known == 0) + lopec_ide_probe(); + + if (base == lopec_ide_regbase[0]) + return 14; + else if (base == lopec_ide_regbase[1]) + return 15; + else + return 0; +} + +static void +lopec_ide_request_region(ide_ioreg_t from, unsigned int to, + const char *name) +{ + request_region(from, to, name); +} + +static ide_ioreg_t +lopec_ide_default_io_base(int index) +{ + if (lopec_ide_ports_known == 0) + lopec_ide_probe(); + return lopec_ide_regbase[index]; +} + +static int +lopec_ide_check_region(ide_ioreg_t from, unsigned int to) +{ + return check_region(from, to); +} + +static void +lopec_ide_release_region(ide_ioreg_t from, unsigned int to) +{ + release_region(from, to); +} + + +static void __init +lopec_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data, + ide_ioreg_t ctl, int *irq) +{ + ide_ioreg_t reg = data; + uint alt_status_base; + int i; + + for(i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) + hw->io_ports[i] = reg++; + + if (data == lopec_ide_regbase[0]) { + alt_status_base = lopec_ide_ctl_regbase[0] + 2; + hw->irq = 14; + } + else if (data == lopec_ide_regbase[1]) { + alt_status_base = lopec_ide_ctl_regbase[1] + 2; + hw->irq = 15; + } + else { + alt_status_base = 0; + hw->irq = 0; + } + + if (ctl) + hw->io_ports[IDE_CONTROL_OFFSET] = ctl; + else + hw->io_ports[IDE_CONTROL_OFFSET] = alt_status_base; + + if (irq != NULL) + *irq = hw->irq; + +} +#endif /* BLK_DEV_IDE */ + +static void __init +lopec_init_IRQ(void) +{ + int i; + + OpenPIC_InitSenses = lopec_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(lopec_openpic_initsenses); + openpic_init(1, 0, NULL, -1); + + for(i = 0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + if (request_irq(LOPEC_SIO_IRQ, no_action, SA_INTERRUPT, + "8259 cascade to EPIC", NULL)) { + printk("Unable to get EPIC %d for cascade.\n", + LOPEC_SIO_IRQ); + } + + i8259_init(NULL); +} + +static void __init +lopec_init2(void) +{ + outb(0x00, 0x4d0); + outb(0xc0, 0x4d1); + + request_region(0x00, 0x20, "dma1"); + request_region(0x20, 0x20, "pic1"); + request_region(0x40, 0x20, "timer"); + request_region(0x80, 0x10, "dma page reg"); + request_region(0xa0, 0x20, "pic2"); + request_region(0xc0, 0x20, "dma2"); +} + +static void __init +lopec_map_io(void) +{ + io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); + io_block_mapping(0xb0000000, 0xb0000000, 0x10000000, _PAGE_IO); +} + +static void __init +lopec_set_bat(void) +{ + unsigned long batu, batl; + + __asm__ __volatile__( + "lis %0,0xf800\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x0ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (batu), "=r" (batl)); +} + +static unsigned long __init +lopec_find_end_of_memory(void) +{ + lopec_set_bat(); + return mpc10x_get_mem_size(MPC10X_MEM_MAP_B); +} + +TODC_ALLOC(); + +static void __init +lopec_setup_arch(void) +{ + + TODC_INIT(TODC_TYPE_MK48T37, 0, 0, + ioremap(0xffe80000, 0x8000), 8); + + loops_per_jiffy = 100000000/HZ; + + lopec_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + else +#elif defined(CONFIG_ROOT_NFS) + ROOT_DEV = to_kdev_t(0x00ff); +#elif defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_ID_MODULE) + ROOT_DEV = to_kdev_t(0x0301); +#else + ROOT_DEV = to_kdev_t(0x0801); +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = lopec_setup_arch; + ppc_md.show_cpuinfo = lopec_show_cpuinfo; + ppc_md.irq_cannonicalize = lopec_irq_cannonicalize; + ppc_md.init_IRQ = lopec_init_IRQ; + ppc_md.get_irq = lopec_get_irq; + ppc_md.init = lopec_init2; + + ppc_md.restart = lopec_restart; + ppc_md.power_off = lopec_power_off; + ppc_md.halt = lopec_halt; + + ppc_md.find_end_of_memory = lopec_find_end_of_memory; + ppc_md.setup_io_mappings = lopec_map_io; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_ID_MODULE) + ppc_ide_md.default_irq = lopec_ide_default_irq; + ppc_ide_md.default_io_base = lopec_ide_default_io_base; + ppc_ide_md.ide_request_region = lopec_ide_request_region; + ppc_ide_md.ide_check_region = lopec_ide_check_region; + ppc_ide_md.ide_release_region = lopec_ide_release_region; + ppc_ide_md.ide_init_hwif = lopec_ide_init_hwif_ports; +#endif +} diff -Nru a/arch/ppc/platforms/lwmon.h b/arch/ppc/platforms/lwmon.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/lwmon.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,60 @@ +/* + * Liebherr LWMON board specific definitions + * + * Copyright (c) 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_LWMON_H +#define __MACH_LWMON_H + +#include + +#include + +#define IMAP_ADDR 0xFFF00000 /* physical base address of IMMR area */ +#define IMAP_SIZE (64 * 1024) /* mapped size of IMMR area */ + +/*----------------------------------------------------------------------- + * PCMCIA stuff + *----------------------------------------------------------------------- + * + */ +#define PCMCIA_MEM_SIZE ( 64 << 20 ) + +#define MAX_HWIFS 1 /* overwrite default in include/asm-ppc/ide.h */ + +/* + * Definitions for IDE0 Interface + */ +#define IDE0_BASE_OFFSET 0 +#define IDE0_DATA_REG_OFFSET (PCMCIA_MEM_SIZE + 0x320) +#define IDE0_ERROR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 1) +#define IDE0_NSECTOR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 2) +#define IDE0_SECTOR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 3) +#define IDE0_LCYL_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 4) +#define IDE0_HCYL_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 5) +#define IDE0_SELECT_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 6) +#define IDE0_STATUS_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 7) +#define IDE0_CONTROL_REG_OFFSET 0x0106 +#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ + +#define IDE0_INTERRUPT 13 + +/* + * Definitions for I2C devices + */ +#define I2C_ADDR_AUDIO 0x28 /* Audio volume control */ +#define I2C_ADDR_SYSMON 0x2E /* LM87 System Monitor */ +#define I2C_ADDR_RTC 0x51 /* PCF8563 RTC */ +#define I2C_ADDR_POWER_A 0x52 /* PCMCIA/USB power switch, channel A */ +#define I2C_ADDR_POWER_B 0x53 /* PCMCIA/USB power switch, channel B */ +#define I2C_ADDR_KEYBD 0x56 /* PIC LWE keyboard */ +#define I2C_ADDR_PICIO 0x57 /* PIC IO Expander */ +#define I2C_ADDR_EEPROM 0x58 /* EEPROM AT24C164 */ + + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_LWMON_H */ diff -Nru a/arch/ppc/platforms/mbx.h b/arch/ppc/platforms/mbx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/mbx.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,98 @@ +/* + * BK Id: SCCS/s.mbx.h 1.11 08/17/01 15:23:17 paulus + */ +/* + * A collection of structures, addresses, and values associated with + * the Motorola MBX boards. This was originally created for the + * MBX860, and probably needs revisions for other boards (like the 821). + * When this file gets out of control, we can split it up into more + * meaningful pieces. + * + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + */ +#ifdef __KERNEL__ +#ifndef __MACH_MBX_DEFS +#define __MACH_MBX_DEFS + +#ifndef __ASSEMBLY__ +/* A Board Information structure that is given to a program when + * EPPC-Bug starts it up. + */ +typedef struct bd_info { + unsigned int bi_tag; /* Should be 0x42444944 "BDID" */ + unsigned int bi_size; /* Size of this structure */ + unsigned int bi_revision; /* revision of this structure */ + unsigned int bi_bdate; /* EPPCbug date, i.e. 0x11061997 */ + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in Hz */ + unsigned int bi_clun; /* Boot device controller */ + unsigned int bi_dlun; /* Boot device logical dev */ + + /* These fields are not part of the board information structure + * provided by the boot rom. They are filled in by embed_config.c + * so we have the information consistent with other platforms. + */ + unsigned char bi_enetaddr[6]; + unsigned int bi_baudrate; +} bd_t; + +/* Memory map for the MBX as configured by EPPC-Bug. We could reprogram + * The SIU and PCI bridge, and try to use larger MMU pages, but the + * performance gain is not measureable and it certainly complicates the + * generic MMU model. + * + * In a effort to minimize memory usage for embedded applications, any + * PCI driver or ISA driver must request or map the region required by + * the device. For convenience (and since we can map up to 4 Mbytes with + * a single page table page), the MMU initialization will map the + * NVRAM, Status/Control registers, CPM Dual Port RAM, and the PCI + * Bridge CSRs 1:1 into the kernel address space. + */ +#define PCI_ISA_IO_ADDR ((unsigned)0x80000000) +#define PCI_ISA_IO_SIZE ((uint)(512 * 1024 * 1024)) +#define PCI_IDE_ADDR ((unsigned)0x81000000) +#define PCI_ISA_MEM_ADDR ((unsigned)0xc0000000) +#define PCI_ISA_MEM_SIZE ((uint)(512 * 1024 * 1024)) +#define PCMCIA_MEM_ADDR ((uint)0xe0000000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024 * 1024)) +#define PCMCIA_DMA_ADDR ((uint)0xe4000000) +#define PCMCIA_DMA_SIZE ((uint)(64 * 1024 * 1024)) +#define PCMCIA_ATTRB_ADDR ((uint)0xe8000000) +#define PCMCIA_ATTRB_SIZE ((uint)(64 * 1024 * 1024)) +#define PCMCIA_IO_ADDR ((uint)0xec000000) +#define PCMCIA_IO_SIZE ((uint)(64 * 1024 * 1024)) +#define NVRAM_ADDR ((uint)0xfa000000) +#define NVRAM_SIZE ((uint)(1 * 1024 * 1024)) +#define MBX_CSR_ADDR ((uint)0xfa100000) +#define MBX_CSR_SIZE ((uint)(1 * 1024 * 1024)) +#define IMAP_ADDR ((uint)0xfa200000) +#define IMAP_SIZE ((uint)(64 * 1024)) +#define PCI_CSR_ADDR ((uint)0xfa210000) +#define PCI_CSR_SIZE ((uint)(64 * 1024)) + +/* Map additional physical space into well known virtual addresses. Due + * to virtual address mapping, these physical addresses are not accessible + * in a 1:1 virtual to physical mapping. + */ +#define ISA_IO_VIRT_ADDR ((uint)0xfa220000) +#define ISA_IO_VIRT_SIZE ((uint)64 * 1024) + +/* Interrupt assignments. + * These are defined (and fixed) by the MBX hardware implementation. + */ +#define POWER_FAIL_INT SIU_IRQ0 /* Power fail */ +#define TEMP_HILO_INT SIU_IRQ1 /* Temperature sensor */ +#define QSPAN_INT SIU_IRQ2 /* PCI Bridge (DMA CTLR?) */ +#define ISA_BRIDGE_INT SIU_IRQ3 /* All those PC things */ +#define COMM_L_INT SIU_IRQ6 /* MBX Comm expansion connector pin */ +#define STOP_ABRT_INT SIU_IRQ7 /* Stop/Abort header pin */ +#endif /* !__ASSEMBLY__ */ + +/* The MBX uses the 8259. +*/ +#define NR_8259_INTS 16 + +#endif +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/mcpn765.h b/arch/ppc/platforms/mcpn765.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/mcpn765.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,80 @@ +/* + * arch/ppc/platforms/mcpn765.h + * + * Definitions for Motorola MCG MCPN765 cPCI Board. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * From Processor to PCI: + * PCI Mem Space: 0x80000000 - 0xc0000000 -> 0x80000000 - 0xc0000000 (1 GB) + * PCI I/O Space: 0xfd800000 - 0xfe000000 -> 0x00000000 - 0x00800000 (8 MB) + * Note: Must skip 0xfe000000-0xfe400000 for CONFIG_HIGHMEM/PKMAP area + * MPIC in PCI Mem Space: 0xfe800000 - 0xfe830000 (not all used by MPIC) + * + * From PCI to Processor: + * System Memory: 0x00000000 -> 0x00000000 + */ + +#ifndef __PPC_PLATFORMS_MCPN765_H +#define __PPC_PLATFORMS_MCPN765_H + +/* PCI Memory space mapping info */ +#define MCPN765_PCI_MEM_SIZE 0x40000000U +#define MCPN765_PROC_PCI_MEM_START 0x80000000U +#define MCPN765_PROC_PCI_MEM_END (MCPN765_PROC_PCI_MEM_START + \ + MCPN765_PCI_MEM_SIZE - 1) +#define MCPN765_PCI_MEM_START 0x80000000U +#define MCPN765_PCI_MEM_END (MCPN765_PCI_MEM_START + \ + MCPN765_PCI_MEM_SIZE - 1) + +/* PCI I/O space mapping info */ +#define MCPN765_PCI_IO_SIZE 0x00800000U +#define MCPN765_PROC_PCI_IO_START 0xfd800000U +#define MCPN765_PROC_PCI_IO_END (MCPN765_PROC_PCI_IO_START + \ + MCPN765_PCI_IO_SIZE - 1) +#define MCPN765_PCI_IO_START 0x00000000U +#define MCPN765_PCI_IO_END (MCPN765_PCI_IO_START + \ + MCPN765_PCI_IO_SIZE - 1) + +/* System memory mapping info */ +#define MCPN765_PCI_DRAM_OFFSET 0x00000000U +#define MCPN765_PCI_PHY_MEM_OFFSET 0x00000000U + +#define MCPN765_ISA_MEM_BASE 0x00000000U +#define MCPN765_ISA_IO_BASE MCPN765_PROC_PCI_IO_START + +/* Define base addresses for important sets of registers */ +#define MCPN765_HAWK_MPIC_BASE 0xfe800000U +#define MCPN765_HAWK_SMC_BASE 0xfef80000U +#define MCPN765_HAWK_PPC_REG_BASE 0xfeff0000U + +/* Define MCPN765 board register addresses. */ +#define MCPN765_BOARD_STATUS_REG 0xfef88080U +#define MCPN765_BOARD_MODFAIL_REG 0xfef88090U +#define MCPN765_BOARD_MODRST_REG 0xfef880a0U +#define MCPN765_BOARD_TBEN_REG 0xfef880c0U +#define MCPN765_BOARD_GEOGRAPHICAL_REG 0xfef880e8U +#define MCPN765_BOARD_EXT_FEATURE_REG 0xfef880f0U +#define MCPN765_BOARD_LAST_RESET_REG 0xfef880f8U + +/* UART base addresses are defined in */ + +/* Define the NVRAM/RTC address strobe & data registers */ +#define MCPN765_PHYS_NVRAM_AS0 0xfef880c8U +#define MCPN765_PHYS_NVRAM_AS1 0xfef880d0U +#define MCPN765_PHYS_NVRAM_DATA 0xfef880d8U + + +extern void mcpn765_find_bridges(void); + +#endif /* __PPC_PLATFORMS_MCPN765_H */ diff -Nru a/arch/ppc/platforms/mcpn765_pci.c b/arch/ppc/platforms/mcpn765_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/mcpn765_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,109 @@ +/* + * arch/ppc/platforms/mcpn765_pci.c + * + * PCI setup routines for the Motorola MCG MCPN765 cPCI board. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mcpn765.h" + + +/* + * Motorola MCG MCPN765 interrupt routing. + */ +static inline int +mcpn765_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 14, 0, 0, 0 }, /* IDSEL 11 - have to manually set */ + { 0, 0, 0, 0 }, /* IDSEL 12 - unused */ + { 0, 0, 0, 0 }, /* IDSEL 13 - unused */ + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 0 */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 25, 26, 27, 28 }, /* IDSEL 16 - PMC Slot 1 */ + { 28, 25, 26, 27 }, /* IDSEL 17 - PMC Slot 2 */ + { 0, 0, 0, 0 }, /* IDSEL 18 - PMC 2B Connector XXXX */ + { 29, 0, 0, 0 }, /* IDSEL 19 - Enet 1 */ + { 20, 0, 0, 0 }, /* IDSEL 20 - 21554 cPCI bridge */ + }; + + const long min_idsel = 11, max_idsel = 20, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +void __init +mcpn765_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = MCPN765_PCI_PHY_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, + MCPN765_PCI_IO_START, + MCPN765_PCI_IO_END, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + MCPN765_PCI_MEM_START, + MCPN765_PCI_MEM_END, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = MCPN765_PCI_IO_START; + hose->io_space.end = MCPN765_PCI_IO_END; + hose->mem_space.start = MCPN765_PCI_MEM_START; + hose->mem_space.end = MCPN765_PCI_MEM_END - PPLUS_MPIC_SIZE; + + if (pplus_init(hose, + MCPN765_HAWK_PPC_REG_BASE, + MCPN765_PROC_PCI_MEM_START, + MCPN765_PROC_PCI_MEM_END - PPLUS_MPIC_SIZE, + MCPN765_PROC_PCI_IO_START, + MCPN765_PROC_PCI_IO_END, + MCPN765_PCI_MEM_END - PPLUS_MPIC_SIZE + 1) != 0) { + printk("Could not initialize HAWK bridge\n"); + } + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = NULL; + ppc_md.pcibios_fixup_bus = NULL; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = mcpn765_map_irq; + + return; +} diff -Nru a/arch/ppc/platforms/mcpn765_serial.h b/arch/ppc/platforms/mcpn765_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/mcpn765_serial.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,65 @@ +/* + * include/asm-ppc/mcpn765_serial.h + * + * Definitions for Motorola MCG MCPN765 cPCI board support + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __ASMPPC_MCPN765_SERIAL_H +#define __ASMPPC_MCPN765_SERIAL_H + +#include + +/* Define the UART base addresses */ +#define MCPN765_SERIAL_1 0xfef88000 +#define MCPN765_SERIAL_2 0xfef88200 +#define MCPN765_SERIAL_3 0xfef88400 +#define MCPN765_SERIAL_4 0xfef88600 + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD ( 1843200 / 16 ) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +/* All UART IRQ's are wire-OR'd to IRQ 17 */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, MCPN765_SERIAL_1, 17, STD_COM_FLAGS, /* ttyS0 */\ + iomem_base: (u8 *)MCPN765_SERIAL_1, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_2, 17, STD_COM_FLAGS, /* ttyS1 */\ + iomem_base: (u8 *)MCPN765_SERIAL_2, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_3, 17, STD_COM_FLAGS, /* ttyS2 */\ + iomem_base: (u8 *)MCPN765_SERIAL_3, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_4, 17, STD_COM_FLAGS, /* ttyS3 */\ + iomem_base: (u8 *)MCPN765_SERIAL_4, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +#endif /* __ASMPPC_MCPN765_SERIAL_H */ diff -Nru a/arch/ppc/platforms/mcpn765_setup.c b/arch/ppc/platforms/mcpn765_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/mcpn765_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,502 @@ +/* + * arch/ppc/platforms/mcpn765_setup.c + * + * Board setup routines for the Motorola MCG MCPN765 cPCI Board. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * This file adds support for the Motorola MCG MCPN765. + */ +#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 +#include +#include + +#include "mcpn765.h" + +static u_char mcpn765_openpic_initsenses[] __initdata = { + 0, /* 16: i8259 cascade (active high) */ + 1, /* 17: COM1,2,3,4 */ + 1, /* 18: Enet 1 (front panel) */ + 1, /* 19: HAWK WDT XXXX */ + 1, /* 20: 21554 PCI-PCI bridge */ + 1, /* 21: cPCI INTA# */ + 1, /* 22: cPCI INTB# */ + 1, /* 23: cPCI INTC# */ + 1, /* 24: cPCI INTD# */ + 1, /* 25: PMC1 INTA#, PMC2 INTB# */ + 1, /* 26: PMC1 INTB#, PMC2 INTC# */ + 1, /* 27: PMC1 INTC#, PMC2 INTD# */ + 1, /* 28: PMC1 INTD#, PMC2 INTA# */ + 1, /* 29: Enet 2 (connected to J3) */ + 1, /* 30: Abort Switch */ + 1, /* 31: RTC Alarm */ +}; + + +extern u_int openpic_irq(void); +extern char cmd_line[]; + +int use_of_interrupt_tree = 0; + +static void mcpn765_halt(void); + +TODC_ALLOC(); + +static void __init +mcpn765_setup_arch(void) +{ + struct pci_controller *hose; + + if ( ppc_md.progress ) + ppc_md.progress("mcpn765_setup_arch: enter", 0); + + loops_per_jiffy = 50000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00FF); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 SCSI disk */ +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("mcpn765_setup_arch: find_bridges", 0); + + /* Lookup PCI host bridges */ + mcpn765_find_bridges(); + + hose = pci_bus_to_hose(0); + isa_io_base = (ulong)hose->io_base_virt; + + TODC_INIT(TODC_TYPE_MK48T37, + (MCPN765_PHYS_NVRAM_AS0 - isa_io_base), + (MCPN765_PHYS_NVRAM_AS1 - isa_io_base), + (MCPN765_PHYS_NVRAM_DATA - isa_io_base), + 8); + + OpenPIC_InitSenses = mcpn765_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(mcpn765_openpic_initsenses); + + printk("Motorola MCG MCPN765 cPCI Non-System Board\n"); + printk("MCPN765 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + if ( ppc_md.progress ) + ppc_md.progress("mcpn765_setup_arch: exit", 0); + + return; +} + +/* + * Initialize the VIA 82c586b. + */ +static void __init +mcpn765_setup_via_82c586b(void) +{ + struct pci_dev *dev; + u_char c; + + if ((dev = pci_find_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_1, + NULL)) == NULL) { + printk("No VIA ISA bridge found\n"); + mcpn765_halt(); + /* NOTREACHED */ + } + + /* + * PPCBug doesn't set the enable bits for the IDE device. + * Turn them on now. + */ + pcibios_read_config_byte(dev->bus->number, dev->devfn, 0x40, &c); + c |= 0x03; + pcibios_write_config_byte(dev->bus->number, dev->devfn, 0x40, c); + + return; +} + +static void __init +mcpn765_init2(void) +{ + /* Do MCPN765 board specific initialization. */ + mcpn765_setup_via_82c586b(); + + request_region(0x00,0x20,"dma1"); + request_region(0x20,0x20,"pic1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xa0,0x20,"pic2"); + request_region(0xc0,0x20,"dma2"); + + return; +} + +/* + * Interrupt setup and service. + * Have MPIC on HAWK and cascaded 8259s on VIA 82586 cascaded to MPIC. + */ +static void __init +mcpn765_init_IRQ(void) +{ + int i; + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: enter", 0); + + openpic_init(1, NUM_8259_INTERRUPTS, NULL, -1); + + for(i=0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + i8259_init(NULL); + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: exit", 0); + + return; +} + +static u32 +mcpn765_irq_cannonicalize(u32 irq) +{ + if (irq == 2) + return 9; + else + return irq; +} + +static unsigned long __init +mcpn765_find_end_of_memory(void) +{ + return pplus_get_mem_size(MCPN765_HAWK_SMC_BASE); +} + +static void __init +mcpn765_map_io(void) +{ + io_block_mapping(0xfe800000, 0xfe800000, 0x00800000, _PAGE_IO); +} + +static void +mcpn765_reset_board(void) +{ + __cli(); + + /* Set exception prefix high - to the firmware */ + _nmask_and_or_msr(0, MSR_IP); + + out_8((u_char *)MCPN765_BOARD_MODRST_REG, 0x01); + + return; +} + +static void +mcpn765_restart(char *cmd) +{ + volatile ulong i = 10000000; + + mcpn765_reset_board(); + + while (i-- > 0); + panic("restart failed\n"); +} + +static void +mcpn765_power_off(void) +{ + mcpn765_halt(); + /* NOTREACHED */ +} + +static void +mcpn765_halt(void) +{ + __cli(); + while (1); + /* NOTREACHED */ +} + +static int +mcpn765_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Motorola MCG\n"); + seq_printf(m, "machine\t\t: MCPN765\n"); + + return 0; +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE support. + */ +static int mcpn765_ide_ports_known = 0; +static ide_ioreg_t mcpn765_ide_regbase[MAX_HWIFS]; +static ide_ioreg_t mcpn765_ide_ctl_regbase[MAX_HWIFS]; +static ide_ioreg_t mcpn765_idedma_regbase; + +static void +mcpn765_ide_probe(void) +{ + struct pci_dev *pdev = pci_find_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_1, + NULL); + + if(pdev) { + mcpn765_ide_regbase[0]=pdev->resource[0].start; + mcpn765_ide_regbase[1]=pdev->resource[2].start; + mcpn765_ide_ctl_regbase[0]=pdev->resource[1].start; + mcpn765_ide_ctl_regbase[1]=pdev->resource[3].start; + mcpn765_idedma_regbase=pdev->resource[4].start; + } + + mcpn765_ide_ports_known = 1; + return; +} + +static int +mcpn765_ide_default_irq(ide_ioreg_t base) +{ + if (mcpn765_ide_ports_known == 0) + mcpn765_ide_probe(); + + if (base == mcpn765_ide_regbase[0]) + return 14; + else if (base == mcpn765_ide_regbase[1]) + return 14; + else + return 0; +} + +static ide_ioreg_t +mcpn765_ide_default_io_base(int index) +{ + if (mcpn765_ide_ports_known == 0) + mcpn765_ide_probe(); + + return mcpn765_ide_regbase[index]; +} + +static int +mcpn765_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +static void +mcpn765_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); + return; +} + +static void +mcpn765_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); + return; +} + +static void __init +mcpn765_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, int *irq) +{ + ide_ioreg_t reg = data_port; + uint alt_status_base; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = reg++; + } + + if (data_port == mcpn765_ide_regbase[0]) { + alt_status_base = mcpn765_ide_ctl_regbase[0] + 2; + hw->irq = 14; + } else if (data_port == mcpn765_ide_regbase[1]) { + alt_status_base = mcpn765_ide_ctl_regbase[1] + 2; + hw->irq = 14; + } else { + alt_status_base = 0; + hw->irq = 0; + } + + if (ctrl_port) + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + else + hw->io_ports[IDE_CONTROL_OFFSET] = alt_status_base; + + if (irq != NULL) + *irq = hw->irq; + + return; +} +#endif + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +mcpn765_set_bat(void) +{ + unsigned long bat3u, bat3l; + static int mapping_set = 0; + + if (!mapping_set) { + + __asm__ __volatile__( + " lis %0,0xf000\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x1ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (bat3u), "=r" (bat3l)); + + mapping_set = 1; + } + + return; +} + +#ifdef CONFIG_SERIAL_TEXT_DEBUG +#include +#include +#include + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in */ +}; + +static void +mcpn765_progress(char *s, unsigned short hex) +{ + volatile char c; + volatile unsigned long com_port; + u16 shift; + + com_port = rs_table[0].port; + shift = rs_table[0].iomem_reg_shift; + + while ((c = *s++) != 0) { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = c; + + if (c == '\n') { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = '\r'; + } + } +} +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* Map in board regs, etc. */ + mcpn765_set_bat(); + + isa_mem_base = MCPN765_ISA_MEM_BASE; + pci_dram_offset = MCPN765_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = mcpn765_setup_arch; + ppc_md.show_cpuinfo = mcpn765_show_cpuinfo; + ppc_md.irq_cannonicalize = mcpn765_irq_cannonicalize; + ppc_md.init_IRQ = mcpn765_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.init = mcpn765_init2; + + ppc_md.restart = mcpn765_restart; + ppc_md.power_off = mcpn765_power_off; + ppc_md.halt = mcpn765_halt; + + ppc_md.find_end_of_memory = mcpn765_find_end_of_memory; + ppc_md.setup_io_mappings = mcpn765_map_io; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; + + ppc_md.heartbeat = NULL; + ppc_md.heartbeat_reset = 0; + ppc_md.heartbeat_count = 0; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = mcpn765_progress; +#else /* !CONFIG_SERIAL_TEXT_DEBUG */ + ppc_md.progress = NULL; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_irq = mcpn765_ide_default_irq; + ppc_ide_md.default_io_base = mcpn765_ide_default_io_base; + ppc_ide_md.ide_check_region = mcpn765_ide_check_region; + ppc_ide_md.ide_request_region = mcpn765_ide_request_region; + ppc_ide_md.ide_release_region = mcpn765_ide_release_region; + ppc_ide_md.ide_init_hwif = mcpn765_ide_init_hwif_ports; +#endif + + return; +} diff -Nru a/arch/ppc/platforms/menf1.h b/arch/ppc/platforms/menf1.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/menf1.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,26 @@ +/* + * arch/ppc/platforms/menf1.h + * + * Definitions for MEN F1 board support + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __PPC_PLATFORMS_MENF1_H +#define __PPC_PLATFORMS_MENF1_H + +#define MENF1_NVRAM_AS0 0x70 +#define MENF1_NVRAM_AS1 0x72 +#define MENF1_NVRAM_DATA 0x71 + +#define MENF1_IDE0_BASE_ADDR 0x1f0 +#define MENF1_IDE1_BASE_ADDR 0x170 + +#endif /* __PPC_PLATFORMS_MENF1_H */ diff -Nru a/arch/ppc/platforms/menf1_pci.c b/arch/ppc/platforms/menf1_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/menf1_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,100 @@ +/* + * arch/ppc/platforms/menf1_pci.c + * + * PCI support for MEN F1 + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "menf1.h" + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif /* DEBUG */ + +static inline int __init +menf1_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {10, 11, 7, 9}, /* IDSEL 26 - PCMIP 0 */ + {0, 0, 0, 0}, /* IDSEL 27 - M5229 IDE */ + {0, 0, 0, 0}, /* IDSEL 28 - M7101 PMU */ + {9, 10, 11, 7}, /* IDSEL 29 - PCMIP 1 */ + {10, 11, 7, 9}, /* IDSEL 30 - P2P Bridge */ + }; + const long min_idsel = 26, max_idsel = 30, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +static int +menf1_exclude_device(u_char bus, u_char devfn) +{ + if ((bus == 0) && (devfn == 0xe0)) { + return PCIBIOS_DEVICE_NOT_FOUND; + } + else { + return PCIBIOS_SUCCESSFUL; + } +} + +void __init +menf1_find_bridges(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + ppc_md.pci_exclude_device = menf1_exclude_device; + + mpc10x_bridge_init(hose, + MPC10X_MEM_MAP_B, + MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + { + /* Add ISA bus wait states */ + unsigned char isa_control; + + early_read_config_byte(hose, 0, 0x90, 0x43, &isa_control); + isa_control |= 0x33; + early_write_config_byte(hose, 0, 0x90, 0x43, isa_control); + } + + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = menf1_map_irq; +} diff -Nru a/arch/ppc/platforms/menf1_setup.c b/arch/ppc/platforms/menf1_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/menf1_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,313 @@ +/* + * arch/ppc/platforms/menf1_setup.c + + * Board setup routines for MEN F1 + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#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 "menf1.h" + +extern void menf1_find_bridges(void); +extern unsigned long loops_per_jiffy; + +/* Dummy variable to satisfy mpc10x_common.o */ +void *OpenPIC_Addr; + +static int +menf1_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: MEN F1\n"); + + return 0; +} + +static void __init +menf1_setup_arch(void) +{ + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Lookup PCI host bridges */ + menf1_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00ff); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0302); /* /dev/hda2 */ +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + printk("MEN F1 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); +} + +static void +menf1_restart(char *cmd) +{ + + int picr1; + struct pci_dev *pdev; + + __cli(); + + /* + * Firmware doesn't like re-entry using Map B (CHRP), so make sure the + * PCI bridge is using MAP A (PReP). + */ + + pdev = pci_find_slot(0, PCI_DEVFN(0,0)); + + while(pdev == NULL); /* paranoia */ + + pci_read_config_dword(pdev, MPC10X_CFG_PICR1_REG, &picr1); + + picr1 = (picr1 & ~MPC10X_CFG_PICR1_ADDR_MAP_MASK) | + MPC10X_CFG_PICR1_ADDR_MAP_A; + + pci_write_config_dword(pdev, MPC10X_CFG_PICR1_REG, picr1); + + asm volatile("sync"); + + /* SRR0 has system reset vector, SRR1 has default MSR value */ + /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */ + __asm__ __volatile__ + ("\n\ + lis 3,0xfff0 + ori 3,3,0x0100 + mtspr 26,3 + li 3,0 + mtspr 27,3 + rfi + "); + while(1); +} + +static void +menf1_halt(void) +{ + __cli(); + while (1); +} + +static void +menf1_power_off(void) +{ + menf1_halt(); +} + +static void __init +menf1_init_IRQ(void) +{ + int i; + + for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ ) + irq_desc[i].handler = &i8259_pic; + i8259_init(NULL); +} + +static int menf1_get_irq(struct pt_regs *regs) +{ + return i8259_poll(); +} + +/* + * Set BAT 3 to map 0xF0000000. + */ +static __inline__ void +menf1_set_bat(void) +{ + static int mapping_set = 0; + + if (!mapping_set) + { + + /* wait for all outstanding memory accesses to complete */ + mb(); + + /* setup DBATs */ + mtspr(DBAT3U, 0xf0001ffe); + mtspr(DBAT3L, 0xf000002a); + + /* wait for updates */ + mb(); + + mapping_set = 1; + } + return; +} + +static unsigned long __init +menf1_find_end_of_memory(void) +{ + /* Cover the I/O with a BAT */ + menf1_set_bat(); + + /* Read the memory size from the MPC107 SMC */ + return mpc10x_get_mem_size(MPC10X_MEM_MAP_B); +} + +static void __init +menf1_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* IDE functions */ + +static int +menf1_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +static void +menf1_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); +} + +static void +menf1_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); +} + +static void __init +menf1_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, int *irq) +{ + ide_ioreg_t reg = data_port; + int i = 8; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = reg; + reg += 1; + } + if (ctrl_port) + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + else + hw->io_ports[IDE_CONTROL_OFFSET] = + hw->io_ports[IDE_DATA_OFFSET] + 0x206; + + if (irq != NULL) + *irq = 0; +} + +static int +menf1_ide_default_irq(ide_ioreg_t base) +{ + if (base == MENF1_IDE0_BASE_ADDR) + return 14; + else if (base == MENF1_IDE1_BASE_ADDR) + return 15; + else + return 0; +} + +static ide_ioreg_t +menf1_ide_default_io_base(int index) +{ + if (index == 0) + return MENF1_IDE0_BASE_ADDR; + else if (index == 1) + return MENF1_IDE1_BASE_ADDR; + else + return 0; +} +#endif + +TODC_ALLOC(); + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + + ppc_md.setup_arch = menf1_setup_arch; + ppc_md.show_cpuinfo = menf1_show_cpuinfo; + ppc_md.init_IRQ = menf1_init_IRQ; + ppc_md.get_irq = menf1_get_irq; + + ppc_md.find_end_of_memory = menf1_find_end_of_memory; + ppc_md.setup_io_mappings = menf1_map_io; + + ppc_md.restart = menf1_restart; + ppc_md.power_off = menf1_power_off; + ppc_md.halt = menf1_halt; + + TODC_INIT(TODC_TYPE_MK48T59, + MENF1_NVRAM_AS0, + MENF1_NVRAM_AS1, + MENF1_NVRAM_DATA, + 7); + + ppc_md.time_init = todc_time_init; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_io_base = menf1_ide_default_io_base; + ppc_ide_md.default_irq = menf1_ide_default_irq; + ppc_ide_md.ide_check_region = menf1_ide_check_region; + ppc_ide_md.ide_request_region = menf1_ide_request_region; + ppc_ide_md.ide_release_region = menf1_ide_release_region; + ppc_ide_md.ide_init_hwif = menf1_ide_init_hwif_ports; +#endif +} diff -Nru a/arch/ppc/platforms/mvme5100.h b/arch/ppc/platforms/mvme5100.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/mvme5100.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,74 @@ +/* + * include/asm-ppc/platforms/mvme5100.h + * + * Definitions for Motorola MVME5100. + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_MVME5100_H__ +#define __ASM_MVME5100_H__ + +#define MVME5100_HAWK_SMC_BASE 0xfef80000 + +#define MVME5100_PCI_CONFIG_ADDR 0xfe000cf8 +#define MVME5100_PCI_CONFIG_DATA 0xfe000cfc + +#define MVME5100_PCI_IO_BASE 0xfe000000 +#define MVME5100_PCI_MEM_BASE 0x80000000 + +#define MVME5100_PCI_MEM_OFFSET 0x00000000 + +#define MVME5100_PCI_DRAM_OFFSET 0x00000000 +#define MVME5100_ISA_MEM_BASE 0x00000000 +#define MVME5100_ISA_IO_BASE MVME5100_PCI_IO_BASE + +#define MVME5100_PCI_LOWER_MEM 0x80000000 +#define MVME5100_PCI_UPPER_MEM 0xf3f7ffff +#define MVME5100_PCI_LOWER_IO 0x00000000 +#define MVME5100_PCI_UPPER_IO 0x0077ffff + +/* MVME5100 board register addresses. */ +#define MVME5100_BOARD_STATUS_REG 0xfef88080 +#define MVME5100_BOARD_MODFAIL_REG 0xfef88090 +#define MVME5100_BOARD_MODRST_REG 0xfef880a0 +#define MVME5100_BOARD_TBEN_REG 0xfef880c0 +#define MVME5100_BOARD_SW_READ_REG 0xfef880e0 +#define MVME5100_BOARD_GEO_ADDR_REG 0xfef880e8 +#define MVME5100_BOARD_EXT_FEATURE1_REG 0xfef880f0 +#define MVME5100_BOARD_EXT_FEATURE2_REG 0xfef88100 + +/* Define the NVRAM/RTC address strobe & data registers */ +#define MVME5100_PHYS_NVRAM_AS0 0xfef880c8 +#define MVME5100_PHYS_NVRAM_AS1 0xfef880d0 +#define MVME5100_PHYS_NVRAM_DATA 0xfef880d8 + +#define MVME5100_NVRAM_AS0 (MVME5100_PHYS_NVRAM_AS0 - MVME5100_ISA_IO_BASE) +#define MVME5100_NVRAM_AS1 (MVME5100_PHYS_NVRAM_AS1 - MVME5100_ISA_IO_BASE) +#define MVME5100_NVRAM_DATA (MVME5100_PHYS_NVRAM_DATA - MVME5100_ISA_IO_BASE) + +/* UART clock, addresses, and irq */ +#define MVME5100_BASE_BAUD 1843200 +#define MVME5100_SERIAL_1 0xfef88000 +#define MVME5100_SERIAL_2 0xfef88200 +#ifdef CONFIG_MVME5100_IPMC761_PRESENT +#define MVME5100_SERIAL_IRQ 17 +#else +#define MVME5100_SERIAL_IRQ 1 +#endif + +#define MVME5100_WINBOND_DEVFN 0x58 +#define MVME5100_WINBOND_VIDDID 0x056510ad + +extern void mvme5100_setup_bridge(void); + +#endif /* __ASM_MVME5100_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/mvme5100_pci.c b/arch/ppc/platforms/mvme5100_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/mvme5100_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,123 @@ +/* + * arch/ppc/platforms/mvme5100_pci.c + * + * PCI setup routines for the Motorola MVME5100. + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static inline int +mvme5100_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + int irq; + + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 0, 0, 0, 0 }, /* IDSEL 11 - Winbond */ + { 0, 0, 0, 0 }, /* IDSEL 12 - unused */ + { 21, 22, 23, 24 }, /* IDSEL 13 - Universe II */ + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 1 */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 25, 26, 27, 28 }, /* IDSEL 16 - PMC Slot 1 */ + { 28, 25, 26, 27 }, /* IDSEL 17 - PMC Slot 2 */ + { 0, 0, 0, 0 }, /* IDSEL 18 - unused */ + { 29, 0, 0, 0 }, /* IDSEL 19 - Enet 2 */ + { 0, 0, 0, 0 }, /* IDSEL 20 - PMCSPAN */ + }; + + const long min_idsel = 11, max_idsel = 20, irqs_per_slot = 4; + irq = PCI_IRQ_TABLE_LOOKUP; + /* If lookup is zero, always return 0 */ + if (!irq) + return 0; + else +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + /* If IPMC761 present, return table value */ + return irq; +#else + /* If IPMC761 not present, we don't have an i8259 so adjust */ + return (irq - NUM_8259_INTERRUPTS); +#endif +} + +static void +mvme5100_pcibios_fixup_resources(struct pci_dev *dev) +{ + int i; + + if ((dev->vendor == PCI_VENDOR_ID_MOTOROLA) && + (dev->device == PCI_DEVICE_ID_MOTOROLA_HAWK)) + for (i=0; iresource[i].start = 0; + dev->resource[i].end = 0; + } +} + +void __init +mvme5100_setup_bridge(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = MVME5100_PCI_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, + MVME5100_PCI_LOWER_IO, + MVME5100_PCI_UPPER_IO, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + MVME5100_PCI_LOWER_MEM, + MVME5100_PCI_UPPER_MEM, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = MVME5100_PCI_LOWER_IO; + hose->io_space.end = MVME5100_PCI_UPPER_IO; + hose->mem_space.start = MVME5100_PCI_LOWER_MEM; + hose->mem_space.end = MVME5100_PCI_UPPER_MEM; + hose->io_base_virt = (void *)MVME5100_ISA_IO_BASE; + + /* Use indirect method of Hawk */ + setup_indirect_pci(hose, + MVME5100_PCI_CONFIG_ADDR, + MVME5100_PCI_CONFIG_DATA); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup_resources = mvme5100_pcibios_fixup_resources; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = mvme5100_map_irq; +} diff -Nru a/arch/ppc/platforms/mvme5100_serial.h b/arch/ppc/platforms/mvme5100_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/mvme5100_serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,56 @@ +/* + * include/asm-ppc/mvme5100_serial.h + * + * Definitions for Motorola MVME5100 support + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_MVME5100_SERIAL_H__ +#define __ASM_MVME5100_SERIAL_H__ + +#include +#include + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +#define BASE_BAUD ( MVME5100_BASE_BAUD / 16 ) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +/* All UART IRQ's are wire-OR'd to one MPIC IRQ */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, MVME5100_SERIAL_1, \ + MVME5100_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (unsigned char *)MVME5100_SERIAL_1, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MVME5100_SERIAL_2, \ + MVME5100_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS1 */ \ + iomem_base: (unsigned char *)MVME5100_SERIAL_2, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +#endif /* __ASM_MVME5100_SERIAL_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/mvme5100_setup.c b/arch/ppc/platforms/mvme5100_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/mvme5100_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,284 @@ +/* + * arch/ppc/platforms/mvme5100_setup.c + * + * Board setup routines for the Motorola MVME5100. + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#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 +#include +#include + +extern char cmd_line[]; + +static u_char mvme5100_openpic_initsenses[] __initdata = { + 0, /* 16: i8259 cascade (active high) */ + 1, /* 17: TL16C550 UART 1,2 */ + 1, /* 18: Enet 1 (front panel or P2) */ + 1, /* 19: Hawk Watchdog 1,2 */ + 1, /* 20: DS1621 thermal alarm */ + 1, /* 21: Universe II LINT0# */ + 1, /* 22: Universe II LINT1# */ + 1, /* 23: Universe II LINT2# */ + 1, /* 24: Universe II LINT3# */ + 1, /* 25: PMC1 INTA#, PMC2 INTB# */ + 1, /* 26: PMC1 INTB#, PMC2 INTC# */ + 1, /* 27: PMC1 INTC#, PMC2 INTD# */ + 1, /* 28: PMC1 INTD#, PMC2 INTA# */ + 1, /* 29: Enet 2 (front panel) */ + 1, /* 30: Abort Switch */ + 1, /* 31: RTC Alarm */ +}; + +static void __init +mvme5100_setup_arch(void) +{ + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: enter", 0); + + loops_per_jiffy = 50000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00FF); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 */ +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: find_bridges", 0); + + /* Setup PCI host bridge */ + mvme5100_setup_bridge(); + + /* Find and map our OpenPIC */ + pplus_mpic_init(MVME5100_PCI_MEM_OFFSET); + OpenPIC_InitSenses = mvme5100_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(mvme5100_openpic_initsenses); + + printk("MVME5100 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: exit", 0); + + return; +} + +static void __init +mvme5100_init2(void) +{ +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + request_region(0x00,0x20,"dma1"); + request_region(0x20,0x20,"pic1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xa0,0x20,"pic2"); + request_region(0xc0,0x20,"dma2"); +#endif + return; +} + +/* + * Interrupt setup and service. + * Have MPIC on HAWK and cascaded 8259s on Winbond cascaded to MPIC. + */ +static void __init +mvme5100_init_IRQ(void) +{ +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + int i; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: enter", 0); + +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + openpic_init(1, NUM_8259_INTERRUPTS, NULL, -1); + + for(i=0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + i8259_init(NULL); +#else + openpic_init(1, 0, NULL, -1); +#endif + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: exit", 0); + + return; +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +mvme5100_set_bat(void) +{ + unsigned long bat3u, bat3l; + static int mapping_set = 0; + + if (!mapping_set) { + + __asm__ __volatile__( + " lis %0,0xf000\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x1ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (bat3u), "=r" (bat3l)); + + mapping_set = 1; + } + + return; +} + +static unsigned long __init +mvme5100_find_end_of_memory(void) +{ + mvme5100_set_bat(); + return pplus_get_mem_size(MVME5100_HAWK_SMC_BASE); +} + +static void __init +mvme5100_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); + ioremap_base = 0xfe000000; +} + +static void +mvme5100_reset_board(void) +{ + __cli(); + + /* Set exception prefix high - to the firmware */ + _nmask_and_or_msr(0, MSR_IP); + + out_8((u_char *)MVME5100_BOARD_MODRST_REG, 0x01); + + return; +} + +static void +mvme5100_restart(char *cmd) +{ + volatile ulong i = 10000000; + + mvme5100_reset_board(); + + while (i-- > 0); + panic("restart failed\n"); +} + +static void +mvme5100_halt(void) +{ + __cli(); + while (1); +} + +static void +mvme5100_power_off(void) +{ + mvme5100_halt(); +} + +static int +mvme5100_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Motorola\n"); + seq_printf(m, "machine\t\t: MVME5100\n"); + + return 0; +} + +TODC_ALLOC(); + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + isa_io_base = MVME5100_ISA_IO_BASE; + isa_mem_base = MVME5100_ISA_MEM_BASE; + pci_dram_offset = MVME5100_PCI_DRAM_OFFSET; + + ppc_md.setup_arch = mvme5100_setup_arch; + ppc_md.show_cpuinfo = mvme5100_show_cpuinfo; + ppc_md.init_IRQ = mvme5100_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.init = mvme5100_init2; + + ppc_md.restart = mvme5100_restart; + ppc_md.power_off = mvme5100_power_off; + ppc_md.halt = mvme5100_halt; + + ppc_md.find_end_of_memory = mvme5100_find_end_of_memory; + ppc_md.setup_io_mappings = mvme5100_map_io; + + TODC_INIT(TODC_TYPE_MK48T37, + MVME5100_NVRAM_AS0, + MVME5100_NVRAM_AS1, + MVME5100_NVRAM_DATA, + 8); + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; + + ppc_md.progress = NULL; +} diff -Nru a/arch/ppc/platforms/oak.h b/arch/ppc/platforms/oak.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/oak.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,74 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * + * Copyright (c) 1999 Grant Erickson + * + * Module name: oak.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * 403G{A,B,C,CX} "Oak" evaluation board. Anything specific to the pro- + * cessor itself is defined elsewhere. + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_OAK_H__ +#define __ASM_OAK_H__ + +/* We have an IBM 403G{A,B,C,CX} core */ +#include + +#define _IO_BASE 0 +#define _ISA_MEM_BASE 0 +#define PCI_DRAM_OFFSET 0 + +/* Memory map for the "Oak" evaluation board */ + +#define PPC403SPU_IO_BASE 0x40000000 /* 403 On-chip serial port */ +#define PPC403SPU_IO_SIZE 0x00000008 +#define OAKSERIAL_IO_BASE 0x7E000000 /* NS16550DV serial port */ +#define OAKSERIAL_IO_SIZE 0x00000008 +#define OAKNET_IO_BASE 0xF4000000 /* NS83902AV Ethernet */ +#define OAKNET_IO_SIZE 0x00000040 +#define OAKPROM_IO_BASE 0xFFFE0000 /* AMD 29F010 Flash ROM */ +#define OAKPROM_IO_SIZE 0x00020000 + + +/* Interrupt assignments fixed by the hardware implementation */ + +/* This is annoying kbuild-2.4 problem. -- Tom */ + +#define PPC403SPU_RX_INT 4 /* AIC_INT4 */ +#define PPC403SPU_TX_INT 5 /* AIC_INT5 */ +#define OAKNET_INT 27 /* AIC_INT27 */ +#define OAKSERIAL_INT 28 /* AIC_INT28 */ + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Oak" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* Bus speed, in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +#define PPC4xx_MACHINE_NAME "IBM Oak" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_OAK_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/oak_setup.c b/arch/ppc/platforms/oak_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/oak_setup.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,293 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: oak_setup.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM PowerPC 403GCX "Oak" evaluation board. Adapted from original + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "oak_setup.h" + +/* Function Prototypes */ + +extern void abort(void); + +/* Global Variables */ + +unsigned char __res[sizeof(bd_t)]; + + +/* + * void __init oak_init() + * + * Description: + * This routine... + * + * Input(s): + * r3 - Optional pointer to a board information structure. + * r4 - Optional pointer to the physical starting address of the init RAM + * disk. + * r5 - Optional pointer to the physical ending address of the init RAM + * disk. + * r6 - Optional pointer to the physical starting address of any kernel + * command-line parameters. + * r7 - Optional pointer to the physical ending address of any kernel + * command-line parameters. + * + * Output(s): + * N/A + * + * Returns: + * N/A + * + */ +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *)__res, (void *)(r3 + KERNELBASE), sizeof(bd_t)); + } + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *)(r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6 + KERNELBASE)); + } + + /* Initialize machine-dependency vectors */ + + ppc_md.setup_arch = oak_setup_arch; + ppc_md.show_percpuinfo = oak_show_percpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = oak_init_IRQ; + ppc_md.get_irq = oak_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = oak_restart; + ppc_md.power_off = oak_power_off; + ppc_md.halt = oak_halt; + + ppc_md.time_init = oak_time_init; + ppc_md.set_rtc_time = oak_set_rtc_time; + ppc_md.get_rtc_time = oak_get_rtc_time; + ppc_md.calibrate_decr = oak_calibrate_decr; + + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; + ppc_md.ppc_kbd_sysrq_xlate = NULL; +} + +/* + * Document me. + */ +void __init +oak_setup_arch(void) +{ + /* XXX - Implement me */ +} + +/* + * int oak_show_percpuinfo() + * + * Description: + * This routine pretty-prints the platform's internal CPU and bus clock + * frequencies into the buffer for usage in /proc/cpuinfo. + * + * Input(s): + * *buffer - Buffer into which CPU and bus clock frequencies are to be + * printed. + * + * Output(s): + * *buffer - Buffer with the CPU and bus clock frequencies. + * + * Returns: + * The number of bytes copied into 'buffer' if OK, otherwise zero or less + * on error. + */ +int +oak_show_percpuinfo(struct seq_file *m, int i) +{ + bd_t *bp = (bd_t *)__res; + + seq_printf(m, "clock\t\t: %dMHz\n" + "bus clock\t\t: %dMHz\n", + bp->bi_intfreq / 1000000, + bp->bi_busfreq / 1000000); + + return 0; +} + +/* + * Document me. + */ +void __init +oak_init_IRQ(void) +{ + int i; + + ppc4xx_pic_init(); + + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].handler = ppc4xx_pic; + } + + return; +} + +/* + * Document me. + */ +int +oak_get_irq(struct pt_regs *regs) +{ + return (ppc4xx_pic_get_irq(regs)); +} + +/* + * Document me. + */ +void +oak_restart(char *cmd) +{ + abort(); +} + +/* + * Document me. + */ +void +oak_power_off(void) +{ + oak_restart(NULL); +} + +/* + * Document me. + */ +void +oak_halt(void) +{ + oak_restart(NULL); +} + +/* + * Document me. + */ +long __init +oak_time_init(void) +{ + /* XXX - Implement me */ + return 0; +} + +/* + * Document me. + */ +int __init +oak_set_rtc_time(unsigned long time) +{ + /* XXX - Implement me */ + + return (0); +} + +/* + * Document me. + */ +unsigned long __init +oak_get_rtc_time(void) +{ + /* XXX - Implement me */ + + return (0); +} + +/* + * void __init oak_calibrate_decr() + * + * Description: + * This routine retrieves the internal processor frequency from the board + * information structure, sets up the kernel timer decrementer based on + * that value, enables the 403 programmable interval timer (PIT) and sets + * it up for auto-reload. + * + * Input(s): + * N/A + * + * Output(s): + * N/A + * + * Returns: + * N/A + * + */ +void __init +oak_calibrate_decr(void) +{ + unsigned int freq; + bd_t *bip = (bd_t *)__res; + + freq = bip->bi_intfreq; + + decrementer_count = freq / HZ; + count_period_num = 1; + count_period_den = freq; + + /* Enable the PIT and set auto-reload of its value */ + + mtspr(SPRN_TCR, TCR_PIE | TCR_ARE); + + /* Clear any pending timer interrupts */ + + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS); +} diff -Nru a/arch/ppc/platforms/oak_setup.h b/arch/ppc/platforms/oak_setup.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/oak_setup.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,53 @@ +/* + * BK Id: SCCS/s.oak_setup.h 1.5 05/17/01 18:14:21 cort + */ +/* + * + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: oak_setup.h + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM PowerPC 403GCX "Oak" evaluation board. Adapted from original + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + */ + +#ifndef __OAK_SETUP_H__ +#define __OAK_SETUP_H__ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +extern unsigned char __res[sizeof(bd_t)]; + +extern void oak_init(unsigned long r3, + unsigned long ird_start, + unsigned long ird_end, + unsigned long cline_start, + unsigned long cline_end); +extern void oak_setup_arch(void); +extern int oak_setup_residual(char *buffer); +extern void oak_init_IRQ(void); +extern int oak_get_irq(struct pt_regs *regs); +extern void oak_restart(char *cmd); +extern void oak_power_off(void); +extern void oak_halt(void); +extern void oak_time_init(void); +extern int oak_set_rtc_time(unsigned long now); +extern unsigned long oak_get_rtc_time(void); +extern void oak_calibrate_decr(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __OAK_SETUP_H__ */ diff -Nru a/arch/ppc/platforms/pcore.h b/arch/ppc/platforms/pcore.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pcore.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,41 @@ +/* + * arch/ppc/platforms/pcore.h + * + * Definitions for Force PowerCore board support + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __PPC_PLATFORMS_PCORE_H +#define __PPC_PLATFORMS_PCORE_H + +#include + +#define PCORE_TYPE_6750 1 +#define PCORE_TYPE_680 2 + +#define PCORE_NVRAM_AS0 0x73 +#define PCORE_NVRAM_AS1 0x75 +#define PCORE_NVRAM_DATA 0x77 + +#define PCORE_DCCR_REG (MPC10X_MAPB_ISA_IO_BASE + 0x308) +#define PCORE_DCCR_L2_MASK 0xc0 +#define PCORE_DCCR_L2_0KB 0x00 +#define PCORE_DCCR_L2_256KB 0x40 +#define PCORE_DCCR_L2_512KB 0xc0 +#define PCORE_DCCR_L2_1MB 0x80 +#define PCORE_DCCR_L2_2MB 0x00 + +#define PCORE_WINBOND_IDE_INT 0x43 +#define PCORE_WINBOND_PCI_INT 0x44 +#define PCORE_WINBOND_PRI_EDG_LVL 0x4d0 +#define PCORE_WINBOND_SEC_EDG_LVL 0x4d1 + +#endif /* __PPC_PLATFORMS_PCORE_H */ diff -Nru a/arch/ppc/platforms/pcore_pci.c b/arch/ppc/platforms/pcore_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pcore_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,144 @@ +/* + * arch/ppc/platforms/pcore_pci.c + * + * PCI support for Force PCORE boards + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pcore.h" + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif /* DEBUG */ + +static inline int __init +pcore_6750_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {9, 10, 11, 12}, /* IDSEL 24 - DEC 21554 */ + {10, 0, 0, 0}, /* IDSEL 25 - DEC 21143 */ + {11, 12, 9, 10}, /* IDSEL 26 - PMC I */ + {12, 9, 10, 11}, /* IDSEL 27 - PMC II */ + {0, 0, 0, 0}, /* IDSEL 28 - unused */ + {0, 0, 9, 0}, /* IDSEL 29 - unused */ + {0, 0, 0, 0}, /* IDSEL 30 - Winbond */ + }; + const long min_idsel = 24, max_idsel = 30, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +static inline int __init +pcore_680_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {9, 10, 11, 12}, /* IDSEL 24 - Sentinel */ + {10, 0, 0, 0}, /* IDSEL 25 - i82559 #1 */ + {11, 12, 9, 10}, /* IDSEL 26 - PMC I */ + {12, 9, 10, 11}, /* IDSEL 27 - PMC II */ + {9, 0, 0, 0}, /* IDSEL 28 - i82559 #2 */ + {0, 0, 0, 0}, /* IDSEL 29 - unused */ + {0, 0, 0, 0}, /* IDSEL 30 - Winbond */ + }; + const long min_idsel = 24, max_idsel = 30, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +pcore_pcibios_fixup(void) +{ + struct pci_dev *dev; + + if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_83C553, + 0))) + { + /* Reroute interrupts both IDE channels to 15 */ + pci_write_config_byte(dev, + PCORE_WINBOND_IDE_INT, + 0xff); + + /* Route INTA-D to IRQ9-12, respectively */ + pci_write_config_word(dev, + PCORE_WINBOND_PCI_INT, + 0x9abc); + + /* + * Set up 8259 edge/level triggering + */ + outb(0x00, PCORE_WINBOND_PRI_EDG_LVL); + outb(0x1e, PCORE_WINBOND_SEC_EDG_LVL); + } +} + +int __init +pcore_find_bridges(void) +{ + struct pci_controller* hose; + int host_bridge, board_type; + + hose = pcibios_alloc_controller(); + if (!hose) + return 0; + + mpc10x_bridge_init(hose, + MPC10X_MEM_MAP_B, + MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE); + + /* Determine board type */ + early_read_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_VENDOR_ID, + &host_bridge); + if (host_bridge == MPC10X_BRIDGE_106) + board_type = PCORE_TYPE_6750; + else /* MPC10X_BRIDGE_107 */ + board_type = PCORE_TYPE_680; + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = pcore_pcibios_fixup; + ppc_md.pci_swizzle = common_swizzle; + + if (board_type == PCORE_TYPE_6750) + ppc_md.pci_map_irq = pcore_6750_map_irq; + else /* PCORE_TYPE_680 */ + ppc_md.pci_map_irq = pcore_680_map_irq; + + return board_type; +} diff -Nru a/arch/ppc/platforms/pcore_setup.c b/arch/ppc/platforms/pcore_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pcore_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,257 @@ +/* + * arch/ppc/platforms/pcore_setup.c + * + * Setup routines for Force PCORE boards + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#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 "pcore.h" + +extern int pcore_find_bridges(void); +extern unsigned long loops_per_jiffy; + +static int board_type; + +/* Dummy variable to satisfy mpc10x_common.o */ +void *OpenPIC_Addr; + +static int +pcore_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Force Computers\n"); + + if (board_type == PCORE_TYPE_6750) + seq_printf(m, "machine\t\t: PowerCore 6750\n"); + else /* PCORE_TYPE_680 */ + seq_printf(m, "machine\t\t: PowerCore 680\n"); + + seq_printf(m, "L2\t\t: " ); + if (board_type == PCORE_TYPE_6750) + switch (readb(PCORE_DCCR_REG) & PCORE_DCCR_L2_MASK) + { + case PCORE_DCCR_L2_0KB: + seq_printf(m, "nocache"); + break; + case PCORE_DCCR_L2_256KB: + seq_printf(m, "256KB"); + break; + case PCORE_DCCR_L2_1MB: + seq_printf(m, "1MB"); + break; + case PCORE_DCCR_L2_512KB: + seq_printf(m, "512KB"); + break; + default: + seq_printf(m, "error"); + break; + } + else /* PCORE_TYPE_680 */ + switch (readb(PCORE_DCCR_REG) & PCORE_DCCR_L2_MASK) + { + case PCORE_DCCR_L2_2MB: + seq_printf(m, "2MB"); + break; + case PCORE_DCCR_L2_256KB: + seq_printf(m, "reserved"); + break; + case PCORE_DCCR_L2_1MB: + seq_printf(m, "1MB"); + break; + case PCORE_DCCR_L2_512KB: + seq_printf(m, "512KB"); + break; + default: + seq_printf(m, "error"); + break; + } + + seq_printf(m, "\n"); + + return 0; +} + +static void __init +pcore_setup_arch(void) +{ + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Lookup PCI host bridges */ + board_type = pcore_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00ff); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 */ +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + printk("Force PCore port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); +} + +static void +pcore_restart(char *cmd) +{ + __cli(); + /* Hard reset */ + writeb(0x11, 0xfe000332); + while(1); +} + +static void +pcore_halt(void) +{ + __cli(); + /* Turn off user LEDs */ + writeb(0x00, 0xfe000300); + while (1); +} + +static void +pcore_power_off(void) +{ + pcore_halt(); +} + + +static void __init +pcore_init_IRQ(void) +{ + int i; + + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].handler = &i8259_pic; + + i8259_init(NULL); +} + +static int +pcore_get_irq(struct pt_regs *regs) +{ + return i8259_poll(); +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +pcore_set_bat(void) +{ + unsigned long bat3u, bat3l; + static int mapping_set = 0; + + if (!mapping_set) { + __asm__ __volatile__( + " lis %0,0xf000\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x1ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (bat3u), "=r" (bat3l)); + + mapping_set = 1; + } + return; +} + +static unsigned long __init +pcore_find_end_of_memory(void) +{ + /* Cover I/O space with a BAT */ + /* yuck, better hope your ram size is a power of 2 -- paulus */ + pcore_set_bat(); + + return mpc10x_get_mem_size(MPC10X_MEM_MAP_B); +} + +static void __init +pcore_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); +} + +TODC_ALLOC(); + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + + ppc_md.setup_arch = pcore_setup_arch; + ppc_md.show_cpuinfo = pcore_show_cpuinfo; + ppc_md.init_IRQ = pcore_init_IRQ; + ppc_md.get_irq = pcore_get_irq; + + ppc_md.find_end_of_memory = pcore_find_end_of_memory; + ppc_md.setup_io_mappings = pcore_map_io; + + ppc_md.restart = pcore_restart; + ppc_md.power_off = pcore_power_off; + ppc_md.halt = pcore_halt; + + TODC_INIT(TODC_TYPE_MK48T59, + PCORE_NVRAM_AS0, + PCORE_NVRAM_AS1, + PCORE_NVRAM_DATA, + 8); + + ppc_md.time_init = todc_time_init; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; +} diff -Nru a/arch/ppc/platforms/pcu_e.h b/arch/ppc/platforms/pcu_e.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pcu_e.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,28 @@ +/* + * Siemens PCU E board specific definitions + * + * Copyright (c) 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_PCU_E_H +#define __MACH_PCU_E_H + +#include + +#include + +#define PCU_E_IMMR_BASE 0xFE000000 /* phys. addr of IMMR */ +#define PCU_E_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR PCU_E_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE PCU_E_IMAP_SIZE /* mapped size of IMMR area */ + +#define FEC_INTERRUPT 15 /* = SIU_LEVEL7 */ +#define DEC_INTERRUPT 13 /* = SIU_LEVEL6 */ +#define CPM_INTERRUPT 11 /* = SIU_LEVEL5 (was: SIU_LEVEL2) */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_PCU_E_H */ diff -Nru a/arch/ppc/platforms/pmac_backlight.c b/arch/ppc/platforms/pmac_backlight.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pmac_backlight.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,150 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * Miscellaneous procedures for dealing with the PowerMac hardware. + * Contains support for the backlight. + * + * Copyright (C) 2000 Benjamin Herrenschmidt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static struct backlight_controller *backlighter = NULL; +static void* backlighter_data = NULL; +static int backlight_autosave = 0; +static int backlight_level = BACKLIGHT_MAX; +static int backlight_enabled = 1; + +void __pmac +register_backlight_controller(struct backlight_controller *ctrler, void *data, char *type) +{ + struct device_node* bk_node; + char *prop; + int valid = 0; + + bk_node = find_devices("backlight"); + +#ifdef CONFIG_ADB_PMU + /* Special case for the old PowerBook since I can't test on it */ + backlight_autosave = machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500"); + if ((backlight_autosave + || machine_is_compatible("AAPL,PowerBook1998") + || machine_is_compatible("PowerBook1,1")) + && !strcmp(type, "pmu")) + valid = 1; +#endif + if (bk_node) { + prop = get_property(bk_node, "backlight-control", NULL); + if (prop && !strncmp(prop, type, strlen(type))) + valid = 1; + } + if (!valid) + return; + backlighter = ctrler; + backlighter_data = data; + + if (bk_node && !backlight_autosave) + prop = get_property(bk_node, "bklt", NULL); + else + prop = NULL; + if (prop) { + backlight_level = ((*prop)+1) >> 1; + if (backlight_level > BACKLIGHT_MAX) + backlight_level = BACKLIGHT_MAX; + } + +#ifdef CONFIG_ADB_PMU + if (backlight_autosave) { + struct adb_request req; + pmu_request(&req, NULL, 2, 0xd9, 0); + while (!req.complete) + pmu_poll(); + backlight_level = req.reply[0] >> 4; + } +#endif + if (!backlighter->set_enable(1, backlight_level, data)) + backlight_enabled = 1; + + printk(KERN_INFO "Registered \"%s\" backlight controller, level: %d/15\n", + type, backlight_level); +} + +void __pmac +unregister_backlight_controller(struct backlight_controller *ctrler, void *data) +{ + /* We keep the current backlight level (for now) */ + if (ctrler == backlighter && data == backlighter_data) + backlighter = NULL; +} + +int __pmac +set_backlight_enable(int enable) +{ + int rc; + + if (!backlighter) + return -ENODEV; + rc = backlighter->set_enable(enable, backlight_level, backlighter_data); + if (!rc) + backlight_enabled = enable; + return rc; +} + +int __pmac +get_backlight_enable(void) +{ + if (!backlighter) + return -ENODEV; + return backlight_enabled; +} + +int __pmac +set_backlight_level(int level) +{ + int rc = 0; + + if (!backlighter) + return -ENODEV; + if (level < BACKLIGHT_MIN) + level = BACKLIGHT_OFF; + if (level > BACKLIGHT_MAX) + level = BACKLIGHT_MAX; + if (backlight_enabled) + rc = backlighter->set_level(level, backlighter_data); + if (!rc) + backlight_level = level; + if (!rc && !backlight_autosave) { + level <<=1; + if (level & 0x10) + level |= 0x01; + // -- todo: save to property "bklt" + } + return rc; +} + +int __pmac +get_backlight_level(void) +{ + if (!backlighter) + return -ENODEV; + return backlight_level; +} diff -Nru a/arch/ppc/platforms/pmac_feature.c b/arch/ppc/platforms/pmac_feature.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pmac_feature.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,2126 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/platforms/pmac_feature.c + * + * Copyright (C) 1996-2001 Paul Mackerras (paulus@cs.anu.edu.au) + * Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * TODO: + * + * - Replace mdelay with some schedule loop if possible + * - Shorten some obfuscated delays on some routines (like modem + * power) + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG_FEATURE + +#ifdef DEBUG_FEATURE +#define DBG(fmt,...) printk(KERN_DEBUG fmt) +#else +#define DBG(fmt,...) +#endif + +/* Exported from arch/ppc/kernel/idle.c */ +extern unsigned long powersave_nap; + +/* + * We use a single global lock to protect accesses. Each driver has + * to take care of it's own locking + */ +static spinlock_t feature_lock __pmacdata = SPIN_LOCK_UNLOCKED; + +#define LOCK(flags) spin_lock_irqsave(&feature_lock, flags); +#define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags); + +/* + * Helper functions regarding the various flavors of mac-io + */ + +#define MAX_MACIO_CHIPS 2 + +enum { + macio_unknown = 0, + macio_grand_central, + macio_ohare, + macio_ohareII, + macio_heathrow, + macio_gatwick, + macio_paddington, + macio_keylargo, + macio_pangea +}; + +static const char* macio_names[] __pmacdata = +{ + "Unknown", + "Grand Central", + "OHare", + "OHareII", + "Heathrow", + "Gatwick", + "Paddington", + "Keylargo", + "Pangea" +}; + +static struct macio_chip +{ + struct device_node* of_node; + int type; + int rev; + volatile u32* base; + unsigned long flags; +} macio_chips[MAX_MACIO_CHIPS] __pmacdata; + +#define MACIO_FLAG_SCCA_ON 0x00000001 +#define MACIO_FLAG_SCCB_ON 0x00000002 +#define MACIO_FLAG_SCC_LOCKED 0x00000004 +#define MACIO_FLAG_AIRPORT_ON 0x00000010 +#define MACIO_FLAG_FW_SUPPORTED 0x00000020 + +static struct macio_chip* __pmac +macio_find(struct device_node* child, int type) +{ + while(child) { + int i; + + for (i=0; i < MAX_MACIO_CHIPS && macio_chips[i].of_node; i++) + if (child == macio_chips[i].of_node && + (!type || macio_chips[i].type == type)) + return &macio_chips[i]; + child = child->parent; + } + return NULL; +} + +#define MACIO_FCR32(macio, r) ((macio)->base + ((r) >> 2)) +#define MACIO_FCR8(macio, r) (((volatile u8*)((macio)->base)) + (r)) + +#define MACIO_IN32(r) (in_le32(MACIO_FCR32(macio,r))) +#define MACIO_OUT32(r,v) (out_le32(MACIO_FCR32(macio,r), (v))) +#define MACIO_BIS(r,v) (MACIO_OUT32((r), MACIO_IN32(r) | (v))) +#define MACIO_BIC(r,v) (MACIO_OUT32((r), MACIO_IN32(r) & ~(v))) +#define MACIO_IN8(r) (in_8(MACIO_FCR8(macio,r))) +#define MACIO_OUT8(r,v) (out_8(MACIO_FCR8(macio,r), (v))) + +/* + * Uninorth reg. access. Note that Uni-N regs are big endian + */ + +#define UN_REG(r) (uninorth_base + ((r) >> 2)) +#define UN_IN(r) (in_be32(UN_REG(r))) +#define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) +#define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) +#define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) + +static struct device_node* uninorth_node __pmacdata; +static u32* uninorth_base __pmacdata; +static u32 uninorth_rev __pmacdata; + + +/* + * For each motherboard family, we have a table of functions pointers + * that handle the various features. + */ + +typedef int (*feature_call)(struct device_node* node, int param, int value); + +struct feature_table_entry { + unsigned int selector; + feature_call function; +}; + +struct pmac_mb_def +{ + const char* model_string; + const char* model_name; + int model_id; + struct feature_table_entry* features; + unsigned long board_flags; +}; +static struct pmac_mb_def pmac_mb __pmacdata; + +/* + * Here are the chip specific feature functions + */ + +static inline int __pmac +simple_feature_tweak(struct device_node* node, int type, int reg, u32 mask, int value) +{ + struct macio_chip* macio; + unsigned long flags; + + macio = macio_find(node, type); + if (!macio) + return -ENODEV; + LOCK(flags); + if (value) + MACIO_BIS(reg, mask); + else + MACIO_BIC(reg, mask); + (void)MACIO_IN32(reg); + UNLOCK(flags); + + return 0; +} + +static int __pmac +generic_scc_enable(struct device_node* node, u32 enable_mask, u32 reset_mask, + int param, int value) +{ + struct macio_chip* macio; + unsigned long chan_mask; + unsigned long fcr; + unsigned long flags; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + if (!strcmp(node->name, "ch-a")) + chan_mask = MACIO_FLAG_SCCA_ON; + else if (!strcmp(node->name, "ch-b")) + chan_mask = MACIO_FLAG_SCCB_ON; + else + return -ENODEV; + + if (value) { + LOCK(flags); + fcr = MACIO_IN32(OHARE_FCR); + /* Check if scc cell need enabling */ + if (!(fcr & OH_SCC_ENABLE)) { + fcr |= enable_mask; + MACIO_OUT32(OHARE_FCR, fcr); + fcr |= reset_mask; + MACIO_OUT32(OHARE_FCR, fcr); + UNLOCK(flags); + (void)MACIO_IN32(OHARE_FCR); + mdelay(15); + LOCK(flags); + fcr &= ~reset_mask; + MACIO_OUT32(OHARE_FCR, fcr); + } + if (chan_mask & MACIO_FLAG_SCCA_ON) + fcr |= OH_SCCA_IO; + if (chan_mask & MACIO_FLAG_SCCB_ON) + fcr |= OH_SCCB_IO; + MACIO_OUT32(OHARE_FCR, fcr); + macio->flags |= chan_mask; + UNLOCK(flags); + if (param & PMAC_SCC_FLAG_XMON) + macio->flags |= MACIO_FLAG_SCC_LOCKED; + } else { + if (macio->flags & MACIO_FLAG_SCC_LOCKED) + return -EPERM; + LOCK(flags); + fcr = MACIO_IN32(OHARE_FCR); + if (chan_mask & MACIO_FLAG_SCCA_ON) + fcr &= ~OH_SCCA_IO; + if (chan_mask & MACIO_FLAG_SCCB_ON) + fcr &= ~OH_SCCB_IO; + MACIO_OUT32(OHARE_FCR, fcr); + if ((fcr & (OH_SCCA_IO | OH_SCCB_IO)) == 0) { + fcr &= ~enable_mask; + MACIO_OUT32(OHARE_FCR, fcr); + } + macio->flags &= ~(chan_mask); + UNLOCK(flags); + mdelay(10); + } + return 0; +} + +static int __pmac +ohare_scc_enable(struct device_node* node, int param, int value) +{ + int rc; + +#ifdef CONFIG_ADB_PMU + if (value && (param & 0xfff) == PMAC_SCC_IRDA) + pmu_enable_irled(1); +#endif /* CONFIG_ADB_PMU */ + rc = generic_scc_enable(node, OH_SCC_ENABLE, OH_SCC_RESET, param, value); +#ifdef CONFIG_ADB_PMU + if ((param & 0xfff) == PMAC_SCC_IRDA && (rc || !value)) + pmu_enable_irled(0); +#endif /* CONFIG_ADB_PMU */ + return rc; +} + +static int __pmac +ohare_floppy_enable(struct device_node* node, int param, int value) +{ + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_FLOPPY_ENABLE, value); +} + +static int __pmac +ohare_mesh_enable(struct device_node* node, int param, int value) +{ + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_MESH_ENABLE, value); +} + +static int __pmac +ohare_ide_enable(struct device_node* node, int param, int value) +{ + switch(param) { + case 0: + /* For some reason, setting the bit in set_initial_features() + * doesn't stick. I'm still investigating... --BenH. + */ + if (value) + simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_IOBUS_ENABLE, 1); + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_IDE0_ENABLE, value); + case 1: + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_BAY_IDE_ENABLE, value); + default: + return -ENODEV; + } +} + +static int __pmac +ohare_ide_reset(struct device_node* node, int param, int value) +{ + switch(param) { + case 0: + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_IDE0_RESET_N, !value); + case 1: + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_IDE1_RESET_N, !value); + default: + return -ENODEV; + } +} + +static int __pmac +ohare_sleep_state(struct device_node* node, int param, int value) +{ + struct macio_chip* macio = &macio_chips[0]; + + if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) + return -EPERM; + if (value) { + MACIO_BIC(OHARE_FCR, OH_IOBUS_ENABLE); + } else { + MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); + } + + return 0; +} + +static int __pmac +heathrow_scc_enable(struct device_node* node, int param, int value) +{ + int rc; + +#ifdef CONFIG_ADB_PMU + if (value && param == PMAC_SCC_IRDA) + pmu_enable_irled(1); +#endif /* CONFIG_ADB_PMU */ + /* Fixme: It's possible that wallstreet (heathrow) is different + * than other paddington machines. I still have to figure that + * out exactly, for now, the paddington values are used + */ + rc = generic_scc_enable(node, HRW_SCC_ENABLE, PADD_RESET_SCC, param, value); +#ifdef CONFIG_ADB_PMU + if (param == PMAC_SCC_IRDA && (rc || !value)) + pmu_enable_irled(0); +#endif /* CONFIG_ADB_PMU */ + return rc; +} + +static int __pmac +heathrow_modem_enable(struct device_node* node, int param, int value) +{ + struct macio_chip* macio; + u8 gpio; + unsigned long flags; + + macio = macio_find(node, macio_unknown); + if (!macio) + return -ENODEV; + gpio = MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1; + if (!value) { + LOCK(flags); + MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio); + UNLOCK(flags); + (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); + mdelay(250); + } + if (pmac_mb.model_id != PMAC_TYPE_YOSEMITE && + pmac_mb.model_id != PMAC_TYPE_YIKES) { + LOCK(flags); + /* We use the paddington values as they seem to work properly + * on the wallstreet (heathrow) as well. I can't tell why we + * had to flip them on older feature.c, the fact is that new + * code uses the paddington values which are also the ones used + * in Darwin, and that works on wallstreet ! + */ + if (value) + MACIO_BIC(HEATHROW_FCR, PADD_MODEM_POWER_N); + else + MACIO_BIS(HEATHROW_FCR, PADD_MODEM_POWER_N); + UNLOCK(flags); + (void)MACIO_IN32(HEATHROW_FCR); + mdelay(250); + } + if (value) { + LOCK(flags); + MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1); + (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio); + (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1); + (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + } + return 0; +} + +static int __pmac +heathrow_floppy_enable(struct device_node* node, int param, int value) +{ + return simple_feature_tweak(node, macio_unknown, + HEATHROW_FCR, + HRW_SWIM_ENABLE|HRW_BAY_FLOPPY_ENABLE, + value); +} + +static int __pmac +heathrow_mesh_enable(struct device_node* node, int param, int value) +{ + struct macio_chip* macio; + unsigned long flags; + + macio = macio_find(node, macio_unknown); + if (!macio) + return -ENODEV; + LOCK(flags); + /* Set clear mesh cell enable */ + if (value) + MACIO_BIS(HEATHROW_FCR, HRW_MESH_ENABLE); + else + MACIO_BIC(HEATHROW_FCR, HRW_MESH_ENABLE); + (void)MACIO_IN32(HEATHROW_FCR); + udelay(10); + /* Set/Clear termination power (todo: test ! the bit value + * used by Darwin doesn't seem to match what we used so + * far. If you experience problems, turn #if 1 into #if 0 + * and tell me about it --BenH. + */ +#if 1 + if (value) + MACIO_BIC(HEATHROW_MBCR, 0x00000004); + else + MACIO_BIS(HEATHROW_MBCR, 0x00000004); +#else + if (value) + MACIO_BIC(HEATHROW_MBCR, 0x00040000); + else + MACIO_BIS(HEATHROW_MBCR, 0x00040000); +#endif + (void)MACIO_IN32(HEATHROW_MBCR); + udelay(10); + UNLOCK(flags); + + return 0; +} + +static int __pmac +heathrow_ide_enable(struct device_node* node, int param, int value) +{ + switch(param) { + case 0: + return simple_feature_tweak(node, macio_unknown, + HEATHROW_FCR, HRW_IDE0_ENABLE, value); + case 1: + return simple_feature_tweak(node, macio_unknown, + HEATHROW_FCR, HRW_BAY_IDE_ENABLE, value); + default: + return -ENODEV; + } +} + +static int __pmac +heathrow_ide_reset(struct device_node* node, int param, int value) +{ + switch(param) { + case 0: + return simple_feature_tweak(node, macio_unknown, + HEATHROW_FCR, HRW_IDE0_RESET_N, !value); + case 1: + return simple_feature_tweak(node, macio_unknown, + HEATHROW_FCR, HRW_IDE1_RESET_N, !value); + default: + return -ENODEV; + } +} + +static int __pmac +heathrow_bmac_enable(struct device_node* node, int param, int value) +{ + struct macio_chip* macio; + unsigned long flags; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + if (value) { + LOCK(flags); + MACIO_BIS(HEATHROW_FCR, HRW_BMAC_IO_ENABLE); + MACIO_BIS(HEATHROW_FCR, HRW_BMAC_RESET); + UNLOCK(flags); + (void)MACIO_IN32(HEATHROW_FCR); + mdelay(10); + LOCK(flags); + MACIO_BIC(HEATHROW_FCR, HRW_BMAC_RESET); + UNLOCK(flags); + (void)MACIO_IN32(HEATHROW_FCR); + mdelay(10); + } else { + LOCK(flags); + MACIO_BIC(HEATHROW_FCR, HRW_BMAC_IO_ENABLE); + UNLOCK(flags); + } + return 0; +} + +static int __pmac +heathrow_sound_enable(struct device_node* node, int param, int value) +{ + struct macio_chip* macio; + unsigned long flags; + + /* B&W G3 and Yikes don't support that properly (the + * sound appear to never come back after beeing shut down). + */ + if (pmac_mb.model_id == PMAC_TYPE_YOSEMITE || + pmac_mb.model_id == PMAC_TYPE_YIKES) + return 0; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + if (value) { + LOCK(flags); + MACIO_BIS(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE); + MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N); + UNLOCK(flags); + (void)MACIO_IN32(HEATHROW_FCR); + } else { + LOCK(flags); + MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N); + MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE); + UNLOCK(flags); + } + return 0; +} + +static u32 save_fcr[5] __pmacdata; +static u32 save_mbcr __pmacdata; +static u32 save_gpio_levels[2] __pmacdata; +static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT] __pmacdata; +static u8 save_gpio_normal[KEYLARGO_GPIO_CNT] __pmacdata; +static u32 save_unin_clock_ctl __pmacdata; +static struct dbdma_regs save_dbdma[13] __pmacdata; +static struct dbdma_regs save_alt_dbdma[13] __pmacdata; + +static void __pmac +dbdma_save(struct macio_chip* macio, struct dbdma_regs* save) +{ + int i; + + /* Save state & config of DBDMA channels */ + for (i=0; i<13; i++) { + volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*) + (macio->base + ((0x8000+i*0x100)>>2)); + save[i].cmdptr_hi = in_le32(&chan->cmdptr_hi); + save[i].cmdptr = in_le32(&chan->cmdptr); + save[i].intr_sel = in_le32(&chan->intr_sel); + save[i].br_sel = in_le32(&chan->br_sel); + save[i].wait_sel = in_le32(&chan->wait_sel); + } +} + +static void __pmac +dbdma_restore(struct macio_chip* macio, struct dbdma_regs* save) +{ + int i; + + /* Save state & config of DBDMA channels */ + for (i=0; i<13; i++) { + volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*) + (macio->base + ((0x8000+i*0x100)>>2)); + out_le32(&chan->control, (ACTIVE|DEAD|WAKE|FLUSH|PAUSE|RUN)<<16); + while (in_le32(&chan->status) & ACTIVE) + mb(); + out_le32(&chan->cmdptr_hi, save[i].cmdptr_hi); + out_le32(&chan->cmdptr, save[i].cmdptr); + out_le32(&chan->intr_sel, save[i].intr_sel); + out_le32(&chan->br_sel, save[i].br_sel); + out_le32(&chan->wait_sel, save[i].wait_sel); + } +} + +static void __pmac +heathrow_sleep(struct macio_chip* macio, int secondary) +{ + if (secondary) { + dbdma_save(macio, save_alt_dbdma); + save_fcr[2] = MACIO_IN32(0x38); + save_fcr[3] = MACIO_IN32(0x3c); + } else { + dbdma_save(macio, save_dbdma); + save_fcr[0] = MACIO_IN32(0x38); + save_fcr[1] = MACIO_IN32(0x3c); + save_mbcr = MACIO_IN32(0x34); + /* Make sure sound is shut down */ + MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N); + MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE); + /* This seems to be necessary as well or the fan + * keeps coming up and battery drains fast */ + MACIO_BIC(HEATHROW_FCR, HRW_IOBUS_ENABLE); + } + /* Make sure modem is shut down */ + MACIO_OUT8(HRW_GPIO_MODEM_RESET, + MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1); + MACIO_BIS(HEATHROW_FCR, PADD_MODEM_POWER_N); + MACIO_BIC(HEATHROW_FCR, OH_SCCA_IO|OH_SCCB_IO|HRW_SCC_ENABLE); + + /* Let things settle */ + (void)MACIO_IN32(HEATHROW_FCR); + mdelay(1); +} + +static void __pmac +heathrow_wakeup(struct macio_chip* macio, int secondary) +{ + if (secondary) { + MACIO_OUT32(0x38, save_fcr[2]); + (void)MACIO_IN32(0x38); + mdelay(1); + MACIO_OUT32(0x3c, save_fcr[3]); + (void)MACIO_IN32(0x38); + mdelay(10); + dbdma_restore(macio, save_alt_dbdma); + } else { + MACIO_OUT32(0x38, save_fcr[0] | HRW_IOBUS_ENABLE); + (void)MACIO_IN32(0x38); + mdelay(1); + MACIO_OUT32(0x3c, save_fcr[1]); + (void)MACIO_IN32(0x38); + mdelay(1); + MACIO_OUT32(0x34, save_mbcr); + (void)MACIO_IN32(0x38); + mdelay(10); + dbdma_restore(macio, save_dbdma); + } +} + +static int __pmac +heathrow_sleep_state(struct device_node* node, int param, int value) +{ + if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) + return -EPERM; + if (value == 1) { + if (macio_chips[1].type == macio_gatwick) + heathrow_sleep(&macio_chips[0], 1); + heathrow_sleep(&macio_chips[0], 0); + } else if (value == 0) { + heathrow_wakeup(&macio_chips[0], 0); + if (macio_chips[1].type == macio_gatwick) + heathrow_wakeup(&macio_chips[0], 1); + } + return 0; +} + +static int __pmac +core99_scc_enable(struct device_node* node, int param, int value) +{ + struct macio_chip* macio; + unsigned long flags; + unsigned long chan_mask; + u32 fcr; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + if (!strcmp(node->name, "ch-a")) + chan_mask = MACIO_FLAG_SCCA_ON; + else if (!strcmp(node->name, "ch-b")) + chan_mask = MACIO_FLAG_SCCB_ON; + else + return -ENODEV; + + if (value) { + int need_reset_scc = 0; + int need_reset_irda = 0; + + LOCK(flags); + fcr = MACIO_IN32(KEYLARGO_FCR0); + /* Check if scc cell need enabling */ + if (!(fcr & KL0_SCC_CELL_ENABLE)) { + fcr |= KL0_SCC_CELL_ENABLE; + need_reset_scc = 1; + } + if (chan_mask & MACIO_FLAG_SCCA_ON) { + fcr |= KL0_SCCA_ENABLE; + /* Don't enable line drivers for I2S modem */ + if ((param & 0xfff) == PMAC_SCC_I2S1) + fcr &= ~KL0_SCC_A_INTF_ENABLE; + else + fcr |= KL0_SCC_A_INTF_ENABLE; + } + if (chan_mask & MACIO_FLAG_SCCB_ON) { + fcr |= KL0_SCCB_ENABLE; + /* Perform irda specific inits */ + if ((param & 0xfff) == PMAC_SCC_IRDA) { + fcr &= ~KL0_SCC_B_INTF_ENABLE; + fcr |= KL0_IRDA_ENABLE; + fcr |= KL0_IRDA_CLK32_ENABLE | KL0_IRDA_CLK19_ENABLE; + fcr |= KL0_IRDA_SOURCE1_SEL; + fcr &= ~(KL0_IRDA_FAST_CONNECT|KL0_IRDA_DEFAULT1|KL0_IRDA_DEFAULT0); + fcr &= ~(KL0_IRDA_SOURCE2_SEL|KL0_IRDA_HIGH_BAND); + need_reset_irda = 1; + } else + fcr |= KL0_SCC_B_INTF_ENABLE; + } + MACIO_OUT32(KEYLARGO_FCR0, fcr); + macio->flags |= chan_mask; + if (need_reset_scc) { + MACIO_BIS(KEYLARGO_FCR0, KL0_SCC_RESET); + (void)MACIO_IN32(KEYLARGO_FCR0); + UNLOCK(flags); + mdelay(15); + LOCK(flags); + MACIO_BIC(KEYLARGO_FCR0, KL0_SCC_RESET); + } + if (need_reset_irda) { + MACIO_BIS(KEYLARGO_FCR0, KL0_IRDA_RESET); + (void)MACIO_IN32(KEYLARGO_FCR0); + UNLOCK(flags); + mdelay(15); + LOCK(flags); + MACIO_BIC(KEYLARGO_FCR0, KL0_IRDA_RESET); + } + UNLOCK(flags); + if (param & PMAC_SCC_FLAG_XMON) + macio->flags |= MACIO_FLAG_SCC_LOCKED; + } else { + if (macio->flags & MACIO_FLAG_SCC_LOCKED) + return -EPERM; + LOCK(flags); + fcr = MACIO_IN32(KEYLARGO_FCR0); + if (chan_mask & MACIO_FLAG_SCCA_ON) + fcr &= ~KL0_SCCA_ENABLE; + if (chan_mask & MACIO_FLAG_SCCB_ON) { + fcr &= ~KL0_SCCB_ENABLE; + /* Perform irda specific clears */ + if ((param & 0xfff) == PMAC_SCC_IRDA) { + fcr &= ~KL0_IRDA_ENABLE; + fcr &= ~(KL0_IRDA_CLK32_ENABLE | KL0_IRDA_CLK19_ENABLE); + fcr &= ~(KL0_IRDA_FAST_CONNECT|KL0_IRDA_DEFAULT1|KL0_IRDA_DEFAULT0); + fcr &= ~(KL0_IRDA_SOURCE1_SEL|KL0_IRDA_SOURCE2_SEL|KL0_IRDA_HIGH_BAND); + } + } + MACIO_OUT32(KEYLARGO_FCR0, fcr); + if ((fcr & (KL0_SCCA_ENABLE | KL0_SCCB_ENABLE)) == 0) { + fcr &= ~KL0_SCC_CELL_ENABLE; + MACIO_OUT32(KEYLARGO_FCR0, fcr); + } + macio->flags &= ~(chan_mask); + UNLOCK(flags); + mdelay(10); + } + return 0; +} + +static int __pmac +core99_modem_enable(struct device_node* node, int param, int value) +{ + struct macio_chip* macio; + u8 gpio; + unsigned long flags; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + gpio = MACIO_IN8(KL_GPIO_MODEM_RESET); + gpio |= KEYLARGO_GPIO_OUTPUT_ENABLE; + gpio &= ~KEYLARGO_GPIO_OUTOUT_DATA; + + if (!value) { + LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio); + UNLOCK(flags); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + mdelay(250); + } + LOCK(flags); + if (value) { + MACIO_BIC(KEYLARGO_FCR2, KL2_ALT_DATA_OUT); + UNLOCK(flags); + (void)MACIO_IN32(KEYLARGO_FCR2); + mdelay(250); + } else { + MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT); + UNLOCK(flags); + } + if (value) { + LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + } + return 0; +} + +static int __pmac +core99_ide_enable(struct device_node* node, int param, int value) +{ + switch(param) { + case 0: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_EIDE0_ENABLE, value); + case 1: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_EIDE1_ENABLE, value); + case 2: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_UIDE_ENABLE, value); + default: + return -ENODEV; + } +} + +static int __pmac +core99_ide_reset(struct device_node* node, int param, int value) +{ + switch(param) { + case 0: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_EIDE0_RESET_N, !value); + case 1: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_EIDE1_RESET_N, !value); + case 2: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_UIDE_RESET_N, !value); + default: + return -ENODEV; + } +} + +static int __pmac +core99_gmac_enable(struct device_node* node, int param, int value) +{ + unsigned long flags; + + LOCK(flags); + if (value) + UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC); + else + UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC); + (void)UN_IN(UNI_N_CLOCK_CNTL); + UNLOCK(flags); + udelay(20); + + return 0; +} + +static int __pmac +core99_gmac_phy_reset(struct device_node* node, int param, int value) +{ + unsigned long flags; + struct macio_chip* macio; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea) + return -ENODEV; + + LOCK(flags); + MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE); + (void)MACIO_IN8(KL_GPIO_ETH_PHY_RESET); + UNLOCK(flags); + mdelay(10); + LOCK(flags); + MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE + | KEYLARGO_GPIO_OUTOUT_DATA); + UNLOCK(flags); + mdelay(10); + + return 0; +} + +static int __pmac +core99_sound_chip_enable(struct device_node* node, int param, int value) +{ + struct macio_chip* macio; + unsigned long flags; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + + /* Do a better probe code, screamer G4 desktops & + * iMacs can do that too, add a recalibrate in + * the driver as well + */ + if (pmac_mb.model_id == PMAC_TYPE_PISMO || + pmac_mb.model_id == PMAC_TYPE_TITANIUM) { + LOCK(flags); + if (value) + MACIO_OUT8(KL_GPIO_SOUND_POWER, + KEYLARGO_GPIO_OUTPUT_ENABLE | + KEYLARGO_GPIO_OUTOUT_DATA); + else + MACIO_OUT8(KL_GPIO_SOUND_POWER, + KEYLARGO_GPIO_OUTPUT_ENABLE); + (void)MACIO_IN8(KL_GPIO_SOUND_POWER); + UNLOCK(flags); + } + return 0; +} + +static int __pmac +core99_airport_enable(struct device_node* node, int param, int value) +{ + struct macio_chip* macio; + unsigned long flags; + int state; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + + /* Hint: we allow passing of macio itself for the sake of the + * sleep code + */ + if (node != macio->of_node && + (!node->parent || node->parent != macio->of_node)) + return -ENODEV; + state = (macio->flags & MACIO_FLAG_AIRPORT_ON) != 0; + if (value == state) + return 0; + if (value) { + /* This code is a reproduction of OF enable-cardslot + * and init-wireless methods, slightly hacked until + * I got it working. + */ + LOCK(flags); + MACIO_OUT8(KEYLARGO_GPIO_0+0xf, 5); + (void)MACIO_IN8(KEYLARGO_GPIO_0+0xf); + UNLOCK(flags); + mdelay(10); + LOCK(flags); + MACIO_OUT8(KEYLARGO_GPIO_0+0xf, 4); + (void)MACIO_IN8(KEYLARGO_GPIO_0+0xf); + UNLOCK(flags); + + mdelay(10); + + LOCK(flags); + MACIO_BIC(KEYLARGO_FCR2, KL2_CARDSEL_16); + (void)MACIO_IN32(KEYLARGO_FCR2); + udelay(10); + MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xb, 0); + (void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xb); + udelay(10); + MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xa, 0x28); + (void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xa); + udelay(10); + MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xd, 0x28); + (void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xd); + udelay(10); + MACIO_OUT8(KEYLARGO_GPIO_0+0xd, 0x28); + (void)MACIO_IN8(KEYLARGO_GPIO_0+0xd); + udelay(10); + MACIO_OUT8(KEYLARGO_GPIO_0+0xe, 0x28); + (void)MACIO_IN8(KEYLARGO_GPIO_0+0xe); + UNLOCK(flags); + udelay(10); + MACIO_OUT32(0x1c000, 0); + mdelay(1); + MACIO_OUT8(0x1a3e0, 0x41); + (void)MACIO_IN8(0x1a3e0); + udelay(10); + LOCK(flags); + MACIO_BIS(KEYLARGO_FCR2, KL2_CARDSEL_16); + (void)MACIO_IN32(KEYLARGO_FCR2); + UNLOCK(flags); + mdelay(100); + + macio->flags |= MACIO_FLAG_AIRPORT_ON; + } else { + LOCK(flags); + MACIO_BIC(KEYLARGO_FCR2, KL2_CARDSEL_16); + (void)MACIO_IN32(KEYLARGO_FCR2); + MACIO_OUT8(KL_GPIO_AIRPORT_0, 0); + MACIO_OUT8(KL_GPIO_AIRPORT_1, 0); + MACIO_OUT8(KL_GPIO_AIRPORT_2, 0); + MACIO_OUT8(KL_GPIO_AIRPORT_3, 0); + MACIO_OUT8(KL_GPIO_AIRPORT_4, 0); + (void)MACIO_IN8(KL_GPIO_AIRPORT_4); + UNLOCK(flags); + + macio->flags &= ~MACIO_FLAG_AIRPORT_ON; + } + return 0; +} + +#ifdef CONFIG_SMP +static int __pmac +core99_reset_cpu(struct device_node* node, int param, int value) +{ + const int reset_lines[] = { KL_GPIO_RESET_CPU0, + KL_GPIO_RESET_CPU1, + KL_GPIO_RESET_CPU2, + KL_GPIO_RESET_CPU3 }; + int reset_io; + unsigned long flags; + struct macio_chip* macio; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea) + return -ENODEV; + if (param > 3 || param < 0) + return -ENODEV; + + reset_io = reset_lines[param]; + + LOCK(flags); + MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE); + (void)MACIO_IN8(reset_io); + udelay(1); + MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA); + (void)MACIO_IN8(reset_io); + UNLOCK(flags); + + return 0; +} +#endif /* CONFIG_SMP */ + +static int __pmac +core99_usb_enable(struct device_node* node, int param, int value) +{ + struct macio_chip* macio; + unsigned long flags; + char* prop; + int number; + u32 reg; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea) + return -ENODEV; + + prop = (char *)get_property(node, "AAPL,clock-id", NULL); + if (!prop) + return -ENODEV; + if (strncmp(prop, "usb0u048", strlen("usb0u048")) == 0) + number = 0; + else if (strncmp(prop, "usb1u148", strlen("usb1u148")) == 0) + number = 2; + else + return -ENODEV; + + /* Sorry for the brute-force locking, but this is only used during + * sleep and the timing seem to be critical + */ + LOCK(flags); + if (value) { + /* Turn ON */ + if (number == 0) { + MACIO_BIC(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1)); + (void)MACIO_IN32(KEYLARGO_FCR0); + UNLOCK(flags); + mdelay(1); + LOCK(flags); + MACIO_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE); + } else { + MACIO_BIC(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1)); + UNLOCK(flags); + (void)MACIO_IN32(KEYLARGO_FCR0); + mdelay(1); + LOCK(flags); + MACIO_BIS(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE); + } + reg = MACIO_IN32(KEYLARGO_FCR4); + reg &= ~(KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | + KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number)); + reg &= ~(KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | + KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1)); + MACIO_OUT32(KEYLARGO_FCR4, reg); + (void)MACIO_IN32(KEYLARGO_FCR4); + udelay(10); + } else { + /* Turn OFF */ + reg = MACIO_IN32(KEYLARGO_FCR4); + reg |= KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | + KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number); + reg |= KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | + KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1); + MACIO_OUT32(KEYLARGO_FCR4, reg); + (void)MACIO_IN32(KEYLARGO_FCR4); + udelay(1); + if (number == 0) { + MACIO_BIC(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE); + (void)MACIO_IN32(KEYLARGO_FCR0); + udelay(1); + MACIO_BIS(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1)); + (void)MACIO_IN32(KEYLARGO_FCR0); + } else { + MACIO_BIC(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE); + (void)MACIO_IN32(KEYLARGO_FCR0); + udelay(1); + MACIO_BIS(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1)); + (void)MACIO_IN32(KEYLARGO_FCR0); + } + udelay(1); + } + UNLOCK(flags); + + return 0; +} + +static int __pmac +core99_firewire_enable(struct device_node* node, int param, int value) +{ + unsigned long flags; + struct macio_chip* macio; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea) + return -ENODEV; + if (!(macio->flags & MACIO_FLAG_FW_SUPPORTED)) + return -ENODEV; + + LOCK(flags); + if (value) { + UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW); + (void)UN_IN(UNI_N_CLOCK_CNTL); + } else { + UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW); + (void)UN_IN(UNI_N_CLOCK_CNTL); + } + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static int __pmac +core99_firewire_cable_power(struct device_node* node, int param, int value) +{ + unsigned long flags; + struct macio_chip* macio; + + /* Trick: we allow NULL node */ + if ((pmac_mb.board_flags & PMAC_MB_HAS_FW_POWER) == 0) + return -ENODEV; + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea) + return -ENODEV; + if (!(macio->flags & MACIO_FLAG_FW_SUPPORTED)) + return -ENODEV; + + LOCK(flags); + if (value) { + MACIO_OUT8(KL_GPIO_FW_CABLE_POWER , 0); + MACIO_IN8(KL_GPIO_FW_CABLE_POWER); + udelay(10); + } else { + MACIO_OUT8(KL_GPIO_FW_CABLE_POWER , 4); + MACIO_IN8(KL_GPIO_FW_CABLE_POWER); udelay(10); + } + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static int __pmac +core99_read_gpio(struct device_node* node, int param, int value) +{ + struct macio_chip* macio = &macio_chips[0]; + + return MACIO_IN8(param); +} + + +static int __pmac +core99_write_gpio(struct device_node* node, int param, int value) +{ + struct macio_chip* macio = &macio_chips[0]; + + MACIO_OUT8(param, (u8)(value & 0xff)); + return 0; +} + +static void __pmac +keylargo_shutdown(struct macio_chip* macio, int restart) +{ + u32 temp; + + mdelay(1); + MACIO_BIS(KEYLARGO_FCR0, KL0_USB_REF_SUSPEND); + (void)MACIO_IN32(KEYLARGO_FCR0); + mdelay(100); + + MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE | + KL0_SCC_CELL_ENABLE | + KL0_IRDA_ENABLE | KL0_IRDA_CLK32_ENABLE | + KL0_IRDA_CLK19_ENABLE); + + (void)MACIO_IN32(KEYLARGO_FCR0); udelay(10); + MACIO_BIC(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK); + (void)MACIO_IN32(KEYLARGO_MBCR); udelay(10); + + MACIO_BIC(KEYLARGO_FCR1, + KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT | + KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE | + KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT | + KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE | + KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE | + KL1_EIDE0_ENABLE | KL1_EIDE0_RESET_N | + KL1_EIDE1_ENABLE | KL1_EIDE1_RESET_N | + KL1_UIDE_ENABLE); + (void)MACIO_IN32(KEYLARGO_FCR1); udelay(10); + + MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT); + udelay(10); + MACIO_BIC(KEYLARGO_FCR2, KL2_IOBUS_ENABLE); + udelay(10); + temp = MACIO_IN32(KEYLARGO_FCR3); + if (macio->rev >= 2) + temp |= (KL3_SHUTDOWN_PLL2X | KL3_SHUTDOWN_PLL_TOTAL); + + temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 | + KL3_SHUTDOWN_PLLKW35 | KL3_SHUTDOWN_PLLKW12; + temp &= ~(KL3_CLK66_ENABLE | KL3_CLK49_ENABLE | KL3_CLK45_ENABLE + | KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE + | KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE); + MACIO_OUT32(KEYLARGO_FCR3, temp); + (void)MACIO_IN32(KEYLARGO_FCR3); udelay(10); +} + +static void __pmac +pangea_shutdown(struct macio_chip* macio, int restart) +{ + u32 temp; + + MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE | + KL0_SCC_CELL_ENABLE | + KL0_USB0_CELL_ENABLE | KL0_USB1_CELL_ENABLE); + + (void)MACIO_IN32(KEYLARGO_FCR0); udelay(10); + MACIO_BIC(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK); + (void)MACIO_IN32(KEYLARGO_MBCR); udelay(10); + + MACIO_BIC(KEYLARGO_FCR1, + KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT | + KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE | + KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT | + KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE | + KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE | + KL1_UIDE_ENABLE); + (void)MACIO_IN32(KEYLARGO_FCR1); udelay(10); + + MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT); + udelay(10); + temp = MACIO_IN32(KEYLARGO_FCR3); + temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 | + KL3_SHUTDOWN_PLLKW35; + temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE + | KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE + | KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE); + MACIO_OUT32(KEYLARGO_FCR3, temp); + (void)MACIO_IN32(KEYLARGO_FCR3); udelay(10); +} + +static int __pmac +core99_sleep(void) +{ + struct macio_chip* macio; + int i; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea) + return -ENODEV; + + /* We power off the wireless slot in case it was not done + * by the driver. We don't power it on automatically however + */ + if (macio->flags & MACIO_FLAG_AIRPORT_ON) + core99_airport_enable(macio->of_node, 0, 0); + + /* We power off the FW cable. Should be done by the driver... */ + if (macio->flags & MACIO_FLAG_FW_SUPPORTED) { + core99_firewire_enable(NULL, 0, 0); + core99_firewire_cable_power(NULL, 0, 0); + } + + /* We make sure int. modem is off (in case driver lost it) */ + core99_modem_enable(macio->of_node, 0, 0); + /* We make sure the sound is off as well */ + core99_sound_chip_enable(macio->of_node, 0, 0); + + /* + * Save various bits of KeyLargo + */ + + /* Save the state of the various GPIOs */ + save_gpio_levels[0] = MACIO_IN32(KEYLARGO_GPIO_LEVELS0); + save_gpio_levels[1] = MACIO_IN32(KEYLARGO_GPIO_LEVELS1); + for (i=0; itype == macio_pangea) + pangea_shutdown(macio, 0); + else if (macio->type == macio_keylargo) + keylargo_shutdown(macio, 0); + + /* + * Put the host bridge to sleep + */ + + save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL); + UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl & + ~(UNI_N_CLOCK_CNTL_GMAC|UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/)); + udelay(100); + UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING); + UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP); + + /* + * FIXME: A bit of black magic with OpenPIC (don't ask me why) + */ + if (pmac_mb.model_id == PMAC_TYPE_SAWTOOTH) { + MACIO_BIS(0x506e0, 0x00400000); + MACIO_BIS(0x506e0, 0x80000000); + } + return 0; +} + +static int __pmac +core99_wake_up(void) +{ + struct macio_chip* macio; + int i; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea) + return -ENODEV; + + /* + * Wakeup the host bridge + */ + UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL); + udelay(10); + UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING); + udelay(10); + + /* + * Restore KeyLargo + */ + + MACIO_OUT32(KEYLARGO_MBCR, save_mbcr); + (void)MACIO_IN32(KEYLARGO_MBCR); udelay(10); + MACIO_OUT32(KEYLARGO_FCR0, save_fcr[0]); + (void)MACIO_IN32(KEYLARGO_FCR0); udelay(10); + MACIO_OUT32(KEYLARGO_FCR1, save_fcr[1]); + (void)MACIO_IN32(KEYLARGO_FCR1); udelay(10); + MACIO_OUT32(KEYLARGO_FCR2, save_fcr[2]); + (void)MACIO_IN32(KEYLARGO_FCR2); udelay(10); + MACIO_OUT32(KEYLARGO_FCR3, save_fcr[3]); + (void)MACIO_IN32(KEYLARGO_FCR3); udelay(10); + MACIO_OUT32(KEYLARGO_FCR4, save_fcr[4]); + (void)MACIO_IN32(KEYLARGO_FCR4); udelay(10); + + dbdma_restore(macio, save_dbdma); + + MACIO_OUT32(KEYLARGO_GPIO_LEVELS0, save_gpio_levels[0]); + MACIO_OUT32(KEYLARGO_GPIO_LEVELS1, save_gpio_levels[1]); + for (i=0; itype) { + case macio_grand_central: + pmac_mb.model_id = PMAC_TYPE_PSURGE; + pmac_mb.model_name = "Unknown PowerSurge"; + break; + case macio_ohare: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_OHARE; + pmac_mb.model_name = "Unknown OHare-based"; + break; + case macio_heathrow: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_HEATHROW; + pmac_mb.model_name = "Unknown Heathrow-based"; + pmac_mb.features = heathrow_desktop_features; + break; + case macio_paddington: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_PADDINGTON; + pmac_mb.model_name = "Unknown Paddington-based"; + pmac_mb.features = paddington_features; + break; + case macio_keylargo: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_CORE99; + pmac_mb.model_name = "Unknown Keylargo-based"; + pmac_mb.features = core99_features; + break; + case macio_pangea: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_PANGEA; + pmac_mb.model_name = "Unknown Pangea-based"; + pmac_mb.features = pangea_features; + break; + default: + return -ENODEV; + } +found: + /* Fixup Hooper vs. Comet */ + if (pmac_mb.model_id == PMAC_TYPE_HOOPER) { + u32* mach_id_ptr = (u32*)ioremap(0xf3000034, 4); + if (!mach_id_ptr) + return -ENODEV; + /* Here, I used to disable the media-bay on comet. It + * appears this is wrong, the floppy connector is actually + * a kind of media-bay and works with the current driver. + */ + if ((*mach_id_ptr) & 0x20000000UL) + pmac_mb.model_id = PMAC_TYPE_COMET; + iounmap(mach_id_ptr); + } + + /* Set default value of powersave_nap on machines that support it. + * It appears that uninorth rev 3 has a problem with it, we don't + * enable it on those. In theory, the flush-on-lock property is + * supposed to be set when not supported, but I'm not very confident + * that all Apple OF revs did it properly, I do it the paranoid way. + */ + while (uninorth_base && uninorth_rev > 3) { + struct device_node* np = find_path_device("/cpus"); + u32 pvr = mfspr(PVR); + if (!np || !np->child) { + printk(KERN_WARNING "Can't find CPU(s) in device tree !\n"); + break; + } + np = np->child; + /* Nap mode not supported on SMP */ + if (np->sibling) + break; + /* Nap mode not supported if flush-on-lock property is present */ + if (get_property(np, "flush-on-lock", NULL)) + break; + /* Some 7450 may have problem with NAP mode too ... */ + if (((pvr >> 16) == 0x8000) && ((pvr & 0xffff) < 0x0201)) + break; + powersave_nap = 1; + printk(KERN_INFO "Processor NAP mode on idle enabled.\n"); + break; + } + + printk(KERN_INFO "PowerMac motherboard: %s\n", pmac_mb.model_name); + return 0; +} + +/* Initialize the Core99 UniNorth host bridge and memory controller + */ +static void __init +probe_uninorth(void) +{ + unsigned long actrl; + + /* Locate core99 Uni-N */ + uninorth_node = find_devices("uni-n"); + if (uninorth_node && uninorth_node->n_addrs > 0) { + uninorth_base = ioremap(uninorth_node->addrs[0].address, 0x1000); + uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); + } else + uninorth_node = NULL; + + if (!uninorth_node) + return; + + printk(KERN_INFO "Found Uninorth memory controller & host bridge, revision: %d\n", + uninorth_rev); + + /* Set the arbitrer QAck delay according to what Apple does + */ + if (uninorth_rev < 0x10) { + actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK; + actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 : + UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT; + UN_OUT(UNI_N_ARB_CTRL, actrl); + } +} + +static void __init +probe_one_macio(const char* name, const char* compat, int type) +{ + struct device_node* node; + int i; + volatile u32* base; + u32* revp; + + node = find_devices(name); + if (!node || !node->n_addrs) + return; + if (compat) + do { + if (device_is_compatible(node, compat)) + break; + node = node->next; + } while (node); + if (!node) + return; + for(i=0; i= MAX_MACIO_CHIPS) { + printk(KERN_ERR "pmac_feature: Please increase MAX_MACIO_CHIPS !\n"); + printk(KERN_ERR "pmac_feature: %s skipped\n", node->full_name); + return; + } + base = (volatile u32*)ioremap(node->addrs[0].address, node->addrs[0].size); + if (!base) { + printk(KERN_ERR "pmac_feature: Can't map mac-io chip !\n"); + return; + } + if (type == macio_keylargo) { + u32* did = (u32 *)get_property(node, "device-id", NULL); + if (*did == 0x00000025) + type = macio_pangea; + } + macio_chips[i].of_node = node; + macio_chips[i].type = type; + macio_chips[i].base = base; + macio_chips[i].flags = MACIO_FLAG_SCCB_ON | MACIO_FLAG_SCCB_ON; + revp = (u32 *)get_property(node, "revision-id", NULL); + if (revp) + macio_chips[i].rev = *revp; + printk(KERN_INFO "Found a %s mac-io controller, rev: %d, mapped at 0x%p\n", + macio_names[type], macio_chips[i].rev, macio_chips[i].base); +} + +static int __init +probe_macios(void) +{ + /* Warning, ordering is important */ + probe_one_macio("gc", NULL, macio_grand_central); + probe_one_macio("ohare", NULL, macio_ohare); + probe_one_macio("pci106b,7", NULL, macio_ohareII); + probe_one_macio("mac-io", "keylargo", macio_keylargo); + probe_one_macio("mac-io", "paddington", macio_paddington); + probe_one_macio("mac-io", "gatwick", macio_gatwick); + probe_one_macio("mac-io", "heathrow", macio_heathrow); + + /* Make sure the "main" macio chip appear first */ + if (macio_chips[0].type == macio_gatwick + && macio_chips[1].type == macio_heathrow) { + struct macio_chip temp = macio_chips[0]; + macio_chips[0] = macio_chips[1]; + macio_chips[1] = temp; + } + if (macio_chips[0].type == macio_ohareII + && macio_chips[1].type == macio_ohare) { + struct macio_chip temp = macio_chips[0]; + macio_chips[0] = macio_chips[1]; + macio_chips[1] = temp; + } + + return (macio_chips[0].of_node == NULL) ? -ENODEV : 0; +} + +static void __init +initial_serial_shutdown(struct device_node* np) +{ + int len; + struct slot_names_prop { + int count; + char name[1]; + } *slots; + char *conn; + int port_type = PMAC_SCC_ASYNC; + int modem = 0; + + slots = (struct slot_names_prop *)get_property(np, "slot-names", &len); + conn = get_property(np, "AAPL,connector", &len); + if (conn && (strcmp(conn, "infrared") == 0)) + port_type = PMAC_SCC_IRDA; + else if (device_is_compatible(np, "cobalt")) + modem = 1; + else if (slots && slots->count > 0) { + if (strcmp(slots->name, "IrDA") == 0) + port_type = PMAC_SCC_IRDA; + else if (strcmp(slots->name, "Modem") == 0) + modem = 1; + } + if (modem) + pmac_call_feature(PMAC_FTR_MODEM_ENABLE, np, 0, 0); + pmac_call_feature(PMAC_FTR_SCC_ENABLE, np, port_type, 0); +} + +static void __init +set_initial_features(void) +{ + struct device_node* np; + + /* That hack appears to be necessary for some StarMax motherboards + * but I'm not too sure it was audited for side-effects on other + * ohare based machines... + * Since I still have difficulties figuring the right way to + * differenciate them all and since that hack was there for a long + * time, I'll keep it around + */ + if (macio_chips[0].type == macio_ohare && !find_devices("via-pmu")) { + struct macio_chip* macio = &macio_chips[0]; + MACIO_OUT32(OHARE_FCR, STARMAX_FEATURES); + } else if (macio_chips[0].type == macio_ohare) { + struct macio_chip* macio = &macio_chips[0]; + MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); + } else if (macio_chips[1].type == macio_ohare) { + struct macio_chip* macio = &macio_chips[1]; + MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); + } + + if (macio_chips[0].type == macio_keylargo || + macio_chips[0].type == macio_pangea) { + /* Enable GMAC for now for PCI probing. It will be disabled + * later on after PCI probe + */ + np = find_devices("ethernet"); + while(np) { + if (np->parent + && device_is_compatible(np->parent, "uni-north") + && device_is_compatible(np, "gmac")) + core99_gmac_enable(np, 0, 1); + np = np->next; + } + + /* Enable FW before PCI probe. Will be disabled later on + * Note: We should have a batter way to check that we are + * dealing with uninorth internal cell and not a PCI cell + * on the external PCI. The code below works though. + */ + np = find_devices("firewire"); + while(np) { + if (np->parent + && device_is_compatible(np->parent, "uni-north") + && (device_is_compatible(np, "pci106b,18") || + device_is_compatible(np, "pci106b,30") || + device_is_compatible(np, "pci11c1,5811"))) { + macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; + core99_firewire_enable(np, 0, 1); + } + np = np->next; + } + + /* Switch airport off */ + np = find_devices("radio"); + while(np) { + if (np && np->parent == macio_chips[0].of_node) { + macio_chips[0].flags |= MACIO_FLAG_AIRPORT_ON; + core99_airport_enable(np, 0, 0); + } + np = np->next; + } + } + + /* On all machines, switch sound off */ + if (macio_chips[0].of_node) + pmac_do_feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, + macio_chips[0].of_node, 0, 0); + + /* On all machines, switch modem & serial ports off */ + np = find_devices("ch-a"); + while(np) { + initial_serial_shutdown(np); + np = np->next; + } + np = find_devices("ch-b"); + while(np) { + initial_serial_shutdown(np); + np = np->next; + } + + /* Let hardware settle down */ + mdelay(10); +} + +void __init +pmac_feature_init(void) +{ + /* Detect the UniNorth memory controller */ + probe_uninorth(); + + /* Probe mac-io controllers */ + if (probe_macios()) { + printk(KERN_WARNING "No mac-io chip found\n"); + return; + } + + /* Probe machine type */ + if (probe_motherboard()) + printk(KERN_WARNING "Unknown PowerMac !\n"); + + /* Set some initial features (turn off some chips that will + * be later turned on) + */ + set_initial_features(); +} + +void __init +pmac_feature_late_init(void) +{ + struct device_node* np; + + /* Request some resources late */ + if (uninorth_node) + request_OF_resource(uninorth_node, 0, NULL); + np = find_devices("hammerhead"); + if (np) + request_OF_resource(np, 0, NULL); + np = find_devices("interrupt-controller"); + if (np) + request_OF_resource(np, 0, NULL); +} diff -Nru a/arch/ppc/platforms/pmac_nvram.c b/arch/ppc/platforms/pmac_nvram.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pmac_nvram.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,403 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * Miscellaneous procedures for dealing with the PowerMac hardware. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ + +#define CORE99_SIGNATURE 0x5a +#define CORE99_ADLER_START 0x14 + +/* Core99 nvram is a flash */ +#define CORE99_FLASH_STATUS_DONE 0x80 +#define CORE99_FLASH_STATUS_ERR 0x38 +#define CORE99_FLASH_CMD_ERASE_CONFIRM 0xd0 +#define CORE99_FLASH_CMD_ERASE_SETUP 0x20 +#define CORE99_FLASH_CMD_RESET 0xff +#define CORE99_FLASH_CMD_WRITE_SETUP 0x40 + +/* CHRP NVRAM header */ +struct chrp_header { + u8 signature; + u8 cksum; + u16 len; + char name[12]; + u8 data[0]; +}; + +struct core99_header { + struct chrp_header hdr; + u32 adler; + u32 generation; + u32 reserved[2]; +}; + +/* + * Read and write the non-volatile RAM on PowerMacs and CHRP machines. + */ +static int nvram_naddrs; +static volatile unsigned char *nvram_addr; +static volatile unsigned char *nvram_data; +static int nvram_mult, is_core_99; +static int core99_bank = 0; +static int nvram_partitions[3]; + +/* FIXME: kmalloc fails to allocate the image now that I had to move it + * before time_init(). For now, I allocate a static buffer here + * but it's a waste of space on all but core99 machines + */ +#if 0 +static char* nvram_image; +#else +static char nvram_image[NVRAM_SIZE] __pmacdata; +#endif + +extern int pmac_newworld; + +static u8 __openfirmware +chrp_checksum(struct chrp_header* hdr) +{ + u8 *ptr; + u16 sum = hdr->signature; + for (ptr = (u8 *)&hdr->len; ptr < hdr->data; ptr++) + sum += *ptr; + while (sum > 0xFF) + sum = (sum & 0xFF) + (sum>>8); + return sum; +} + +static u32 __pmac +core99_calc_adler(u8 *buffer) +{ + int cnt; + u32 low, high; + + buffer += CORE99_ADLER_START; + low = 1; + high = 0; + for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) { + if ((cnt % 5000) == 0) { + high %= 65521UL; + high %= 65521UL; + } + low += buffer[cnt]; + high += low; + } + low %= 65521UL; + high %= 65521UL; + + return (high << 16) | low; +} + +static u32 __pmac +core99_check(u8* datas) +{ + struct core99_header* hdr99 = (struct core99_header*)datas; + + if (hdr99->hdr.signature != CORE99_SIGNATURE) { +#ifdef DEBUG + printk("Invalid signature\n"); +#endif + return 0; + } + if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) { +#ifdef DEBUG + printk("Invalid checksum\n"); +#endif + return 0; + } + if (hdr99->adler != core99_calc_adler(datas)) { +#ifdef DEBUG + printk("Invalid adler\n"); +#endif + return 0; + } + return hdr99->generation; +} + +static int __pmac +core99_erase_bank(int bank) +{ + int stat, i; + + u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; + + out_8(base, CORE99_FLASH_CMD_ERASE_SETUP); + out_8(base, CORE99_FLASH_CMD_ERASE_CONFIRM); + do { stat = in_8(base); } + while(!(stat & CORE99_FLASH_STATUS_DONE)); + out_8(base, CORE99_FLASH_CMD_RESET); + if (stat & CORE99_FLASH_STATUS_ERR) { + printk("nvram: flash error 0x%02x on erase !\n", stat); + return -ENXIO; + } + for (i=0; iname, "common")) + nvram_partitions[pmac_nvram_OF] = offset + 0x10; + if (!strcmp(hdr->name, "APL,MacOS75")) { + nvram_partitions[pmac_nvram_XPRAM] = offset + 0x10; + nvram_partitions[pmac_nvram_NR] = offset + 0x110; + } + offset += (hdr->len * 0x10); + } while(offset < NVRAM_SIZE); + } else { + nvram_partitions[pmac_nvram_OF] = 0x1800; + nvram_partitions[pmac_nvram_XPRAM] = 0x1300; + nvram_partitions[pmac_nvram_NR] = 0x1400; + } +#ifdef DEBUG + printk("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]); + printk("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]); + printk("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]); +#endif +} + +void __init +pmac_nvram_init(void) +{ + struct device_node *dp; + + nvram_naddrs = 0; + + dp = find_devices("nvram"); + if (dp == NULL) { + printk(KERN_ERR "Can't find NVRAM device\n"); + return; + } + nvram_naddrs = dp->n_addrs; + is_core_99 = device_is_compatible(dp, "nvram,flash"); + if (is_core_99) { + int i; + u32 gen_bank0, gen_bank1; + + if (nvram_naddrs < 1) { + printk(KERN_ERR "nvram: no address\n"); + return; + } +#if 0 + nvram_image = kmalloc(NVRAM_SIZE, GFP_KERNEL); + if (!nvram_image) { + printk(KERN_ERR "nvram: can't allocate image\n"); + return; + } +#endif + nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2); +#ifdef DEBUG + printk("nvram: Checking bank 0...\n"); +#endif + gen_bank0 = core99_check((u8 *)nvram_data); + gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE); + core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0; +#ifdef DEBUG + printk("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); + printk("nvram: Active bank is: %d\n", core99_bank); +#endif + for (i=0; iaddrs[0].address + isa_mem_base, + dp->addrs[0].size); + nvram_mult = 1; + } else if (nvram_naddrs == 1) { + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE; + } else if (nvram_naddrs == 2) { + nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + } else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) { + nvram_naddrs = -1; + } else { + printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", + nvram_naddrs); + } + lookup_partitions(); +} + +void __pmac +pmac_nvram_update(void) +{ + struct core99_header* hdr99; + + if (!is_core_99 || !nvram_data || !nvram_image) + return; + if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE, + NVRAM_SIZE)) + return; +#ifdef DEBUG + printk("Updating nvram...\n"); +#endif + hdr99 = (struct core99_header*)nvram_image; + hdr99->generation++; + hdr99->hdr.signature = CORE99_SIGNATURE; + hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); + hdr99->adler = core99_calc_adler(nvram_image); + core99_bank = core99_bank ? 0 : 1; + if (core99_erase_bank(core99_bank)) { + printk("nvram: Error erasing bank %d\n", core99_bank); + return; + } + if (core99_write_bank(core99_bank, nvram_image)) + printk("nvram: Error writing bank %d\n", core99_bank); +} + +unsigned char __openfirmware +nvram_read_byte(int addr) +{ + switch (nvram_naddrs) { +#ifdef CONFIG_ADB_PMU + case -1: { + struct adb_request req; + + if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM, + (addr >> 8) & 0xff, addr & 0xff)) + break; + while (!req.complete) + pmu_poll(); + return req.reply[0]; + } +#endif + case 1: + if (is_core_99) + return nvram_image[addr]; + return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; + case 2: + *nvram_addr = addr >> 5; + eieio(); + return nvram_data[(addr & 0x1f) << 4]; + } + return 0; +} + +void __openfirmware +nvram_write_byte(unsigned char val, int addr) +{ + switch (nvram_naddrs) { +#ifdef CONFIG_ADB_PMU + case -1: { + struct adb_request req; + + if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM, + (addr >> 8) & 0xff, addr & 0xff, val)) + break; + while (!req.complete) + pmu_poll(); + break; + } +#endif + case 1: + if (is_core_99) { + nvram_image[addr] = val; + break; + } + nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; + break; + case 2: + *nvram_addr = addr >> 5; + eieio(); + nvram_data[(addr & 0x1f) << 4] = val; + break; + } + eieio(); +} + +int __pmac +pmac_get_partition(int partition) +{ + return nvram_partitions[partition]; +} + +u8 __pmac +pmac_xpram_read(int xpaddr) +{ + int offset = nvram_partitions[pmac_nvram_XPRAM]; + + if (offset < 0) + return 0; + + return nvram_read_byte(xpaddr + offset); +} + +void __pmac +pmac_xpram_write(int xpaddr, u8 data) +{ + int offset = nvram_partitions[pmac_nvram_XPRAM]; + + if (offset < 0) + return; + + nvram_write_byte(xpaddr + offset, data); +} diff -Nru a/arch/ppc/platforms/pmac_pci.c b/arch/ppc/platforms/pmac_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pmac_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,633 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * Support for PCI bridges found on Power Macintoshes. + * At present the "bandit" and "chaos" bridges are supported. + * Fortunately you access configuration space in the same + * way with either bridge. + * + * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +static void add_bridges(struct device_node *dev); + +/* XXX Could be per-controller, but I don't think we risk anything by + * assuming we won't have both UniNorth and Bandit */ +static int has_uninorth; + +/* + * Magic constants for enabling cache coherency in the bandit/PSX bridge. + */ +#define BANDIT_DEVID_2 8 +#define BANDIT_REVID 3 + +#define BANDIT_DEVNUM 11 +#define BANDIT_MAGIC 0x50 +#define BANDIT_COHERENT 0x40 + +static int __init +fixup_one_level_bus_range(struct device_node *node, int higher) +{ + for (; node != 0;node = node->sibling) { + int * bus_range; + unsigned int *class_code; + int len; + + /* For PCI<->PCI bridges or CardBus bridges, we go down */ + class_code = (unsigned int *) get_property(node, "class-code", 0); + if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && + (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) + continue; + bus_range = (int *) get_property(node, "bus-range", &len); + if (bus_range != NULL && len > 2 * sizeof(int)) { + if (bus_range[1] > higher) + higher = bus_range[1]; + } + higher = fixup_one_level_bus_range(node->child, higher); + } + return higher; +} + +/* This routine fixes the "bus-range" property of all bridges in the + * system since they tend to have their "last" member wrong on macs + * + * Note that the bus numbers manipulated here are OF bus numbers, they + * are not Linux bus numbers. + */ +static void __init +fixup_bus_range(struct device_node *bridge) +{ + int * bus_range; + int len; + + /* Lookup the "bus-range" property for the hose */ + bus_range = (int *) get_property(bridge, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s\n", + bridge->full_name); + return; + } + bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]); +} + +/* + * Apple MacRISC (UniNorth, Bandit, Chaos) PCI controllers. + * + * The "Bandit" version is present in all early PCI PowerMacs, + * and up to the first ones using Grackle. Some machines may + * have 2 bandit controllers (2 PCI busses). + * + * "Chaos" is used in some "Bandit"-type machines as a bridge + * for the separate display bus. It is accessed the same + * way as bandit, but cannot be probed for devices. It therefore + * has its own config access functions. + * + * The "UniNorth" version is present in all Core99 machines + * (iBook, G4, new IMacs, and all the recent Apple machines). + * It contains 3 controllers in one ASIC. + */ + +#define MACRISC_CFA0(devfn, off) \ + ((1 << (unsigned long)PCI_SLOT(dev_fn)) \ + | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \ + | (((unsigned long)(off)) & 0xFCUL)) + +#define MACRISC_CFA1(bus, devfn, off) \ + ((((unsigned long)(bus)) << 16) \ + |(((unsigned long)(devfn)) << 8) \ + |(((unsigned long)(off)) & 0xFCUL) \ + |1UL) + +static unsigned int __pmac +macrisc_cfg_access(struct pci_controller* hose, u8 bus, u8 dev_fn, u8 offset) +{ + unsigned int caddr; + + if (bus == hose->first_busno) { + if (dev_fn < (11 << 3)) + return 0; + caddr = MACRISC_CFA0(dev_fn, offset); + } else + caddr = MACRISC_CFA1(bus, dev_fn, offset); + + /* Uninorth will return garbage if we don't read back the value ! */ + do { + out_le32(hose->cfg_addr, caddr); + } while(in_le32(hose->cfg_addr) != caddr); + + offset &= has_uninorth ? 0x07 : 0x03; + return (unsigned int)(hose->cfg_data) + (unsigned int)offset; +} + +#define cfg_read(val, addr, type, op, op2) \ + *val = op((type)(addr)) +#define cfg_write(val, addr, type, op, op2) \ + op((type *)(addr), (val)); (void) op2((type *)(addr)) + +#define cfg_read_bad(val, size) *val = bad_##size; +#define cfg_write_bad(val, size) + +#define bad_byte 0xff +#define bad_word 0xffff +#define bad_dword 0xffffffffU + +#define MACRISC_PCI_OP(rw, size, type, op, op2) \ +static int __pmac \ +macrisc_##rw##_config_##size(struct pci_dev *dev, int off, type val) \ +{ \ + struct pci_controller *hose = dev->sysdata; \ + unsigned int addr; \ + \ + addr = macrisc_cfg_access(hose, dev->bus->number, dev->devfn, off); \ + if (!addr) { \ + cfg_##rw##_bad(val, size) \ + return PCIBIOS_DEVICE_NOT_FOUND; \ + } \ + cfg_##rw(val, addr, type, op, op2); \ + return PCIBIOS_SUCCESSFUL; \ +} + +MACRISC_PCI_OP(read, byte, u8 *, in_8, x) +MACRISC_PCI_OP(read, word, u16 *, in_le16, x) +MACRISC_PCI_OP(read, dword, u32 *, in_le32, x) +MACRISC_PCI_OP(write, byte, u8, out_8, in_8) +MACRISC_PCI_OP(write, word, u16, out_le16, in_le16) +MACRISC_PCI_OP(write, dword, u32, out_le32, in_le32) + +static struct pci_ops macrisc_pci_ops = +{ + macrisc_read_config_byte, + macrisc_read_config_word, + macrisc_read_config_dword, + macrisc_write_config_byte, + macrisc_write_config_word, + macrisc_write_config_dword +}; + +/* + * Verifiy that a specific (bus, dev_fn) exists on chaos + */ +static int __pmac +chaos_validate_dev(struct pci_dev *dev, int offset) +{ + if(pci_device_to_OF_node(dev) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if((dev->vendor == 0x106b) && (dev->device == 3) && (offset >= 0x10) && + (offset != 0x14) && (offset != 0x18) && (offset <= 0x24)) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + return PCIBIOS_SUCCESSFUL; +} + +#define CHAOS_PCI_OP(rw, size, type) \ +static int __pmac \ +chaos_##rw##_config_##size(struct pci_dev *dev, int off, type val) \ +{ \ + int result = chaos_validate_dev(dev, off); \ + if(result == PCIBIOS_BAD_REGISTER_NUMBER) { \ + cfg_##rw##_bad(val, size) \ + return PCIBIOS_BAD_REGISTER_NUMBER; \ + } \ + if(result == PCIBIOS_SUCCESSFUL) \ + return macrisc_##rw##_config_##size(dev, off, val); \ + return result; \ +} + +CHAOS_PCI_OP(read, byte, u8 *) +CHAOS_PCI_OP(read, word, u16 *) +CHAOS_PCI_OP(read, dword, u32 *) +CHAOS_PCI_OP(write, byte, u8) +CHAOS_PCI_OP(write, word, u16) +CHAOS_PCI_OP(write, dword, u32) + +static struct pci_ops chaos_pci_ops = +{ + chaos_read_config_byte, + chaos_read_config_word, + chaos_read_config_dword, + chaos_write_config_byte, + chaos_write_config_word, + chaos_write_config_dword +}; + + +/* + * For a bandit bridge, turn on cache coherency if necessary. + * N.B. we could clean this up using the hose ops directly. + */ +static void __init +init_bandit(struct pci_controller *bp) +{ + unsigned int vendev, magic; + int rev; + + /* read the word at offset 0 in config space for device 11 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID); + udelay(2); + vendev = in_le32((volatile unsigned int *)bp->cfg_data); + if (vendev == (PCI_DEVICE_ID_APPLE_BANDIT << 16) + + PCI_VENDOR_ID_APPLE) { + /* read the revision id */ + out_le32(bp->cfg_addr, + (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID); + udelay(2); + rev = in_8(bp->cfg_data); + if (rev != BANDIT_REVID) + printk(KERN_WARNING + "Unknown revision %d for bandit at %08lx\n", + rev, bp->io_base_phys); + } else if (vendev != (BANDIT_DEVID_2 << 16) + PCI_VENDOR_ID_APPLE) { + printk(KERN_WARNING "bandit isn't? (%x)\n", vendev); + return; + } + + /* read the revision id */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID); + udelay(2); + rev = in_8(bp->cfg_data); + if (rev != BANDIT_REVID) + printk(KERN_WARNING "Unknown revision %d for bandit at %08lx\n", + rev, bp->io_base_phys); + + /* read the word at offset 0x50 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC); + udelay(2); + magic = in_le32((volatile unsigned int *)bp->cfg_data); + if ((magic & BANDIT_COHERENT) != 0) + return; + magic |= BANDIT_COHERENT; + udelay(2); + out_le32((volatile unsigned int *)bp->cfg_data, magic); + printk(KERN_INFO "Cache coherency enabled for bandit/PSX at %08lx\n", + bp->io_base_phys); +} + + +/* + * Tweak the PCI-PCI bridge chip on the blue & white G3s. + */ +static void __init +init_p2pbridge(void) +{ + struct device_node *p2pbridge; + struct pci_controller* hose; + u8 bus, devfn; + u16 val; + + /* XXX it would be better here to identify the specific + PCI-PCI bridge chip we have. */ + if ((p2pbridge = find_devices("pci-bridge")) == 0 + || p2pbridge->parent == NULL + || strcmp(p2pbridge->parent->name, "pci") != 0) + return; + if (pci_device_from_OF_node(p2pbridge, &bus, &devfn) < 0) { +#ifdef DEBUG + printk("Can't find PCI infos for PCI<->PCI bridge\n"); +#endif + return; + } + /* Warning: At this point, we have not yet renumbered all busses. + * So we must use OF walking to find out hose + */ + hose = pci_find_hose_for_OF_device(p2pbridge); + if (!hose) { +#ifdef DEBUG + printk("Can't find hose for PCI<->PCI bridge\n"); +#endif + return; + } + if (early_read_config_word(hose, bus, devfn, + PCI_BRIDGE_CONTROL, &val) < 0) { + printk(KERN_ERR "init_p2pbridge: couldn't read bridge control\n"); + return; + } + val &= ~PCI_BRIDGE_CTL_MASTER_ABORT; + early_write_config_word(hose, bus, devfn, PCI_BRIDGE_CONTROL, val); +} + +void __init +pmac_find_bridges(void) +{ + add_bridges(find_devices("bandit")); + add_bridges(find_devices("chaos")); + add_bridges(find_devices("pci")); + init_p2pbridge(); +} + +#define GRACKLE_CFA(b, d, o) (0x80 | ((b) << 8) | ((d) << 16) \ + | (((o) & ~3) << 24)) + +#define GRACKLE_PICR1_STG 0x00000040 +#define GRACKLE_PICR1_LOOPSNOOP 0x00000010 + +/* N.B. this is called before bridges is initialized, so we can't + use grackle_pcibios_{read,write}_config_dword. */ +static inline void grackle_set_stg(struct pci_controller* bp, int enable) +{ + unsigned int val; + + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + val = in_le32((volatile unsigned int *)bp->cfg_data); + val = enable? (val | GRACKLE_PICR1_STG) : + (val & ~GRACKLE_PICR1_STG); + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + out_le32((volatile unsigned int *)bp->cfg_data, val); + (void)in_le32((volatile unsigned int *)bp->cfg_data); +} + +static inline void grackle_set_loop_snoop(struct pci_controller *bp, int enable) +{ + unsigned int val; + + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + val = in_le32((volatile unsigned int *)bp->cfg_data); + val = enable? (val | GRACKLE_PICR1_LOOPSNOOP) : + (val & ~GRACKLE_PICR1_LOOPSNOOP); + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + out_le32((volatile unsigned int *)bp->cfg_data, val); + (void)in_le32((volatile unsigned int *)bp->cfg_data); +} + +static int __init +setup_uninorth(struct pci_controller* hose, struct reg_property* addr) +{ + pci_assign_all_busses = 1; + has_uninorth = 1; + hose->ops = ¯isc_pci_ops; + hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000); + hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000); + /* We "know" that the bridge at f2000000 has the PCI slots. */ + return addr->address == 0xf2000000; +} + +static void __init +setup_bandit(struct pci_controller* hose, struct reg_property* addr) +{ + hose->ops = ¯isc_pci_ops; + hose->cfg_addr = (volatile unsigned int *) + ioremap(addr->address + 0x800000, 0x1000); + hose->cfg_data = (volatile unsigned char *) + ioremap(addr->address + 0xc00000, 0x1000); + init_bandit(hose); +} + +static void __init +setup_chaos(struct pci_controller* hose, struct reg_property* addr) +{ + /* assume a `chaos' bridge */ + hose->ops = &chaos_pci_ops; + hose->cfg_addr = (volatile unsigned int *) + ioremap(addr->address + 0x800000, 0x1000); + hose->cfg_data = (volatile unsigned char *) + ioremap(addr->address + 0xc00000, 0x1000); +} + +void __init +setup_grackle(struct pci_controller *hose) +{ + setup_indirect_pci(hose, 0xfec00000, 0xfee00000); + if (machine_is_compatible("AAPL,PowerBook1998")) + grackle_set_loop_snoop(hose, 1); +#if 0 /* Disabled for now, HW problems ??? */ + grackle_set_stg(hose, 1); +#endif +} + +/* + * We assume that if we have a G3 powermac, we have one bridge called + * "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise, + * if we have one or more bandit or chaos bridges, we don't have a MPC106. + */ +static void __init +add_bridges(struct device_node *dev) +{ + int len; + struct pci_controller *hose; + struct reg_property *addr; + char* disp_name; + int *bus_range; + int first = 1, primary; + + for (; dev != NULL; dev = dev->next) { + addr = (struct reg_property *) get_property(dev, "reg", &len); + if (addr == NULL || len < sizeof(*addr)) { + printk(KERN_WARNING "Can't use %s: no address\n", + dev->full_name); + continue; + } + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n", + dev->full_name); + } + + hose = pcibios_alloc_controller(); + if (!hose) + continue; + hose->arch_data = dev; + hose->first_busno = bus_range ? bus_range[0] : 0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + disp_name = NULL; + primary = first; + if (device_is_compatible(dev, "uni-north")) { + primary = setup_uninorth(hose, addr); + disp_name = "UniNorth"; + } else if (strcmp(dev->name, "pci") == 0) { + /* XXX assume this is a mpc106 (grackle) */ + setup_grackle(hose); + disp_name = "Grackle (MPC106)"; + } else if (strcmp(dev->name, "bandit") == 0) { + setup_bandit(hose, addr); + disp_name = "Bandit"; + } else if (strcmp(dev->name, "chaos") == 0) { + setup_chaos(hose, addr); + disp_name = "Chaos"; + primary = 0; + } + printk(KERN_INFO "Found %s PCI host bridge at 0x%08x. Firmware bus number: %d->%d\n", + disp_name, addr->address, hose->first_busno, hose->last_busno); +#ifdef DEBUG + printk(" ->Hose at 0x%08lx, cfg_addr=0x%08lx,cfg_data=0x%08lx\n", + hose, hose->cfg_addr, hose->cfg_data); +#endif + + /* Interpret the "ranges" property */ + /* This also maps the I/O region and sets isa_io/mem_base */ + pci_process_bridge_OF_ranges(hose, dev, primary); + + /* Fixup "bus-range" OF property */ + fixup_bus_range(dev); + + first &= !primary; + } +} + +static void __init +pcibios_fixup_OF_interrupts(void) +{ + struct pci_dev* dev; + + pci_for_each_dev(dev) + { + /* + * Open Firmware often doesn't initialize the, + * PCI_INTERRUPT_LINE config register properly, so we + * should find the device node and se if it has an + * AAPL,interrupts property. + */ + unsigned char pin; + struct device_node* node; + + if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || !pin) + continue; /* No interrupt generated -> no fixup */ + node = pci_device_to_OF_node(dev); + if (!node) { + printk("No OF node for device %x:%x\n", dev->bus->number, dev->devfn >> 3); + continue; + } + /* this is the node, see if it has interrupts */ + if (node->n_intrs > 0) + dev->irq = node->intrs[0].line; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } +} + +void __init +pmac_pcibios_fixup(void) +{ + /* Fixup interrupts according to OF tree */ + pcibios_fixup_OF_interrupts(); +} + +int __pmac +pmac_pci_enable_device_hook(struct pci_dev *dev, int initial) +{ + struct device_node* node; + int updatecfg = 0; + int uninorth_child; + + node = pci_device_to_OF_node(dev); + + /* We don't want to enable USB controllers absent from the OF tree + * (iBook second controller) + */ + if (dev->vendor == PCI_VENDOR_ID_APPLE + && dev->device == PCI_DEVICE_ID_APPLE_KL_USB && !node) + return -EINVAL; + + if (!node) + return 0; + + uninorth_child = node->parent && + device_is_compatible(node->parent, "uni-north"); + + /* Firewire & GMAC were disabled after PCI probe, the driver is + * claiming them, we must re-enable them now. + */ + if (uninorth_child && !strcmp(node->name, "firewire") && + (device_is_compatible(node, "pci106b,18") || + device_is_compatible(node, "pci106b,30") || + device_is_compatible(node, "pci11c1,5811"))) { + pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, node, 0, 1); + pmac_call_feature(PMAC_FTR_1394_ENABLE, node, 0, 1); + updatecfg = 1; + } + if (uninorth_child && !strcmp(node->name, "ethernet") && + device_is_compatible(node, "gmac")) { + pmac_call_feature(PMAC_FTR_GMAC_ENABLE, node, 0, 1); + updatecfg = 1; + } + + if (updatecfg) { + u16 cmd; + + /* + * Make sure PCI is correctly configured + * + * We use old pci_bios versions of the function since, by + * default, gmac is not powered up, and so will be absent + * from the kernel initial PCI lookup. + * + * Should be replaced by 2.4 new PCI mecanisms and really + * regiser the device. + */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; + pci_write_config_word(dev, PCI_COMMAND, cmd); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 16); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8); + } + + return 0; +} + +/* We power down some devices after they have been probed. They'll + * be powered back on later on + */ +void __init +pmac_pcibios_after_init(void) +{ + struct device_node* nd; + +#ifdef CONFIG_BLK_DEV_IDE + struct pci_dev *dev; + + /* OF fails to initialize IDE controllers on macs + * (and maybe other machines) + * + * Ideally, this should be moved to the IDE layer, but we need + * to check specifically with Andre Hedrick how to do it cleanly + * since the common IDE code seem to care about the fact that the + * BIOS may have disabled a controller. + * + * -- BenH + */ + pci_for_each_dev(dev) { + if ((dev->class >> 16) == PCI_BASE_CLASS_STORAGE) + pci_enable_device(dev); + } +#endif /* CONFIG_BLK_DEV_IDE */ + + nd = find_devices("firewire"); + while (nd) { + if (nd->parent && (device_is_compatible(nd, "pci106b,18") || + device_is_compatible(nd, "pci106b,30") || + device_is_compatible(nd, "pci11c1,5811")) + && device_is_compatible(nd->parent, "uni-north")) { + pmac_call_feature(PMAC_FTR_1394_ENABLE, nd, 0, 0); + pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, nd, 0, 0); + } + nd = nd->next; + } + nd = find_devices("ethernet"); + while (nd) { + if (nd->parent && device_is_compatible(nd, "gmac") + && device_is_compatible(nd->parent, "uni-north")) + pmac_call_feature(PMAC_FTR_GMAC_ENABLE, nd, 0, 0); + nd = nd->next; + } +} + diff -Nru a/arch/ppc/platforms/pmac_pic.c b/arch/ppc/platforms/pmac_pic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pmac_pic.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,528 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pmac_pic.h" + +struct pmac_irq_hw { + unsigned int event; + unsigned int enable; + unsigned int ack; + unsigned int level; +}; + +/* Default addresses */ +static volatile struct pmac_irq_hw *pmac_irq_hw[4] __pmacdata = { + (struct pmac_irq_hw *) 0xf3000020, + (struct pmac_irq_hw *) 0xf3000010, + (struct pmac_irq_hw *) 0xf4000020, + (struct pmac_irq_hw *) 0xf4000010, +}; + +#define GC_LEVEL_MASK 0x3ff00000 +#define OHARE_LEVEL_MASK 0x1ff00000 +#define HEATHROW_LEVEL_MASK 0x1ff00000 + +static int max_irqs __pmacdata; +static int max_real_irqs __pmacdata; +static u32 level_mask[4] __pmacdata; + +static spinlock_t pmac_pic_lock __pmacdata = SPIN_LOCK_UNLOCKED; + + +#define GATWICK_IRQ_POOL_SIZE 10 +static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE] __pmacdata; + +/* + * Mark an irq as "lost". This is only used on the pmac + * since it can lose interrupts (see pmac_set_irq_mask). + * -- Cort + */ +void __pmac +__set_lost(unsigned long irq_nr, int nokick) +{ + if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) { + atomic_inc(&ppc_n_lost_interrupts); + if (!nokick) + set_dec(1); + } +} + +static void __pmac +pmac_mask_and_ack_irq(unsigned int irq_nr) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + unsigned long flags; + + if ((unsigned)irq_nr >= max_irqs) + return; + + clear_bit(irq_nr, ppc_cached_irq_mask); + if (test_and_clear_bit(irq_nr, ppc_lost_interrupts)) + atomic_dec(&ppc_n_lost_interrupts); + spin_lock_irqsave(&pmac_pic_lock, flags); + out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); + out_le32(&pmac_irq_hw[i]->ack, bit); + do { + /* make sure ack gets to controller before we enable + interrupts */ + mb(); + } while((in_le32(&pmac_irq_hw[i]->enable) & bit) + != (ppc_cached_irq_mask[i] & bit)); + spin_unlock_irqrestore(&pmac_pic_lock, flags); +} + +static void __pmac pmac_set_irq_mask(unsigned int irq_nr, int nokicklost) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + unsigned long flags; + + if ((unsigned)irq_nr >= max_irqs) + return; + + spin_lock_irqsave(&pmac_pic_lock, flags); + /* enable unmasked interrupts */ + out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); + + do { + /* make sure mask gets to controller before we + return to user */ + mb(); + } while((in_le32(&pmac_irq_hw[i]->enable) & bit) + != (ppc_cached_irq_mask[i] & bit)); + + /* + * Unfortunately, setting the bit in the enable register + * when the device interrupt is already on *doesn't* set + * the bit in the flag register or request another interrupt. + */ + if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level)) + __set_lost((ulong)irq_nr, nokicklost); + spin_unlock_irqrestore(&pmac_pic_lock, flags); +} + +static void __pmac pmac_mask_irq(unsigned int irq_nr) +{ + clear_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr, 0); + mb(); +} + +static void __pmac pmac_unmask_irq(unsigned int irq_nr) +{ + set_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr, 0); +} + +static void __pmac pmac_end_irq(unsigned int irq_nr) +{ + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + set_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr, 1); + } +} + + +struct hw_interrupt_type pmac_pic = { + " PMAC-PIC ", + NULL, + NULL, + pmac_unmask_irq, + pmac_mask_irq, + pmac_mask_and_ack_irq, + pmac_end_irq, + NULL +}; + +struct hw_interrupt_type gatwick_pic = { + " GATWICK ", + NULL, + NULL, + pmac_unmask_irq, + pmac_mask_irq, + pmac_mask_and_ack_irq, + pmac_end_irq, + NULL +}; + +static void gatwick_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + int irq, bits; + + for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) { + int i = irq >> 5; + bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i]; + /* We must read level interrupts from the level register */ + bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]); + bits &= ppc_cached_irq_mask[i]; + if (bits == 0) + continue; + irq += __ilog2(bits); + break; + } + /* The previous version of this code allowed for this case, we + * don't. Put this here to check for it. + * -- Cort + */ + if ( irq_desc[irq].handler != &gatwick_pic ) + printk("gatwick irq not from gatwick pic\n"); + else + ppc_irq_dispatch_handler( regs, irq ); +} + +int +pmac_get_irq(struct pt_regs *regs) +{ + int irq; + unsigned long bits = 0; + +#ifdef CONFIG_SMP + void psurge_smp_message_recv(struct pt_regs *); + + /* IPI's are a hack on the powersurge -- Cort */ + if ( smp_processor_id() != 0 ) { + psurge_smp_message_recv(regs); + return -2; /* ignore, already handled */ + } +#endif /* CONFIG_SMP */ + for (irq = max_real_irqs; (irq -= 32) >= 0; ) { + int i = irq >> 5; + bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i]; + /* We must read level interrupts from the level register */ + bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]); + bits &= ppc_cached_irq_mask[i]; + if (bits == 0) + continue; + irq += __ilog2(bits); + break; + } + + return irq; +} + +/* This routine will fix some missing interrupt values in the device tree + * on the gatwick mac-io controller used by some PowerBooks + */ +static void __init +pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) +{ + struct device_node *node; + int count; + + memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool)); + node = gw->child; + count = 0; + while(node) + { + /* Fix SCC */ + if (strcasecmp(node->name, "escc") == 0) + if (node->child) { + if (node->child->n_intrs < 3) { + node->child->intrs = &gatwick_int_pool[count]; + count += 3; + } + node->child->n_intrs = 3; + node->child->intrs[0].line = 15+irq_base; + node->child->intrs[1].line = 4+irq_base; + node->child->intrs[2].line = 5+irq_base; + printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n", + node->child->intrs[0].line, + node->child->intrs[1].line, + node->child->intrs[2].line); + } + /* Fix media-bay & left SWIM */ + if (strcasecmp(node->name, "media-bay") == 0) { + struct device_node* ya_node; + + if (node->n_intrs == 0) + node->intrs = &gatwick_int_pool[count++]; + node->n_intrs = 1; + node->intrs[0].line = 29+irq_base; + printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n", + node->intrs[0].line); + + ya_node = node->child; + while(ya_node) + { + if (strcasecmp(ya_node->name, "floppy") == 0) { + if (ya_node->n_intrs < 2) { + ya_node->intrs = &gatwick_int_pool[count]; + count += 2; + } + ya_node->n_intrs = 2; + ya_node->intrs[0].line = 19+irq_base; + ya_node->intrs[1].line = 1+irq_base; + printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n", + ya_node->intrs[0].line, ya_node->intrs[1].line); + } + if (strcasecmp(ya_node->name, "ata4") == 0) { + if (ya_node->n_intrs < 2) { + ya_node->intrs = &gatwick_int_pool[count]; + count += 2; + } + ya_node->n_intrs = 2; + ya_node->intrs[0].line = 14+irq_base; + ya_node->intrs[1].line = 3+irq_base; + printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n", + ya_node->intrs[0].line, ya_node->intrs[1].line); + } + ya_node = ya_node->sibling; + } + } + node = node->sibling; + } + if (count > 10) { + printk("WARNING !! Gatwick interrupt pool overflow\n"); + printk(" GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE); + printk(" requested = %d\n", count); + } +} + +/* + * The PowerBook 3400/2400/3500 can have a combo ethernet/modem + * card which includes an ohare chip that acts as a second interrupt + * controller. If we find this second ohare, set it up and fix the + * interrupt value in the device tree for the ethernet chip. + */ +static int __init enable_second_ohare(void) +{ + unsigned char bus, devfn; + unsigned short cmd; + unsigned long addr; + struct device_node *irqctrler = find_devices("pci106b,7"); + struct device_node *ether; + + if (irqctrler == NULL || irqctrler->n_addrs <= 0) + return -1; + addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40); + pmac_irq_hw[1] = (volatile struct pmac_irq_hw *)(addr + 0x20); + max_irqs = 64; + if (pci_device_from_OF_node(irqctrler, &bus, &devfn) == 0) { + struct pci_controller* hose = pci_find_hose_for_OF_device(irqctrler); + if (!hose) + printk(KERN_ERR "Can't find PCI hose for OHare2 !\n"); + else { + early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + cmd &= ~PCI_COMMAND_IO; + early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd); + } + } + + /* Fix interrupt for the modem/ethernet combo controller. The number + in the device tree (27) is bogus (correct for the ethernet-only + board but not the combo ethernet/modem board). + The real interrupt is 28 on the second controller -> 28+32 = 60. + */ + ether = find_devices("pci1011,14"); + if (ether && ether->n_intrs > 0) { + ether->intrs[0].line = 60; + printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n", + ether->intrs[0].line); + } + + /* Return the interrupt number of the cascade */ + return irqctrler->intrs[0].line; +} + +void __init +pmac_pic_init(void) +{ + int i; + struct device_node *irqctrler; + unsigned long addr; + int irq_cascade = -1; + + /* We first try to detect Apple's new Core99 chipset, since mac-io + * is quite different on those machines and contains an IBM MPIC2. + */ + irqctrler = find_type_devices("open-pic"); + if (irqctrler != NULL) + { + printk("PowerMac using OpenPIC irq controller\n"); + if (irqctrler->n_addrs > 0) + { + int nmi_irq = -1; + unsigned char senses[NR_IRQS]; +#ifdef CONFIG_XMON + struct device_node* pswitch; + + pswitch = find_devices("programmer-switch"); + if (pswitch && pswitch->n_intrs) + nmi_irq = pswitch->intrs[0].line; +#endif /* CONFIG_XMON */ + prom_get_irq_senses(senses, 0, NR_IRQS); + OpenPIC_InitSenses = senses; + OpenPIC_NumInitSenses = NR_IRQS; + ppc_md.get_irq = openpic_get_irq; + OpenPIC_Addr = ioremap(irqctrler->addrs[0].address, + irqctrler->addrs[0].size); + openpic_init(1, 0, 0, nmi_irq); +#ifdef CONFIG_XMON + if (nmi_irq >= 0) + request_irq(nmi_irq, xmon_irq, 0, + "NMI - XMON", 0); +#endif /* CONFIG_XMON */ + return; + } + irqctrler = NULL; + } + + /* Get the level/edge settings, assume if it's not + * a Grand Central nor an OHare, then it's an Heathrow + * (or Paddington). + */ + if (find_devices("gc")) + level_mask[0] = GC_LEVEL_MASK; + else if (find_devices("ohare")) { + level_mask[0] = OHARE_LEVEL_MASK; + /* We might have a second cascaded ohare */ + level_mask[1] = OHARE_LEVEL_MASK; + } else { + level_mask[0] = HEATHROW_LEVEL_MASK; + level_mask[1] = 0; + /* We might have a second cascaded heathrow */ + level_mask[2] = HEATHROW_LEVEL_MASK; + level_mask[3] = 0; + } + + /* + * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts, + * 1998 G3 Series PowerBooks have 128, + * other powermacs have 32. + * The combo ethernet/modem card for the Powerstar powerbooks + * (2400/3400/3500, ohare based) has a second ohare chip + * effectively making a total of 64. + */ + max_irqs = max_real_irqs = 32; + irqctrler = find_devices("mac-io"); + if (irqctrler) + { + max_real_irqs = 64; + if (irqctrler->next) + max_irqs = 128; + else + max_irqs = 64; + } + for ( i = 0; i < max_real_irqs ; i++ ) + irq_desc[i].handler = &pmac_pic; + + /* get addresses of first controller */ + if (irqctrler) { + if (irqctrler->n_addrs > 0) { + addr = (unsigned long) + ioremap(irqctrler->addrs[0].address, 0x40); + for (i = 0; i < 2; ++i) + pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) + (addr + (2 - i) * 0x10); + } + + /* get addresses of second controller */ + irqctrler = irqctrler->next; + if (irqctrler && irqctrler->n_addrs > 0) { + addr = (unsigned long) + ioremap(irqctrler->addrs[0].address, 0x40); + for (i = 2; i < 4; ++i) + pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) + (addr + (4 - i) * 0x10); + irq_cascade = irqctrler->intrs[0].line; + if (device_is_compatible(irqctrler, "gatwick")) + pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs); + } + } else { + /* older powermacs have a GC (grand central) or ohare at + f3000000, with interrupt control registers at f3000020. */ + addr = (unsigned long) ioremap(0xf3000000, 0x40); + pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20); + } + + /* PowerBooks 3400 and 3500 can have a second controller in a second + ohare chip, on the combo ethernet/modem card */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) + irq_cascade = enable_second_ohare(); + + /* disable all interrupts in all controllers */ + for (i = 0; i * 32 < max_irqs; ++i) + out_le32(&pmac_irq_hw[i]->enable, 0); + /* mark level interrupts */ + for (i = 0; i < max_irqs; i++) + if (level_mask[i >> 5] & (1UL << (i & 0x1f))) + irq_desc[i].status = IRQ_LEVEL; + + /* get interrupt line of secondary interrupt controller */ + if (irq_cascade >= 0) { + printk(KERN_INFO "irq: secondary controller on irq %d\n", + (int)irq_cascade); + for ( i = max_real_irqs ; i < max_irqs ; i++ ) + irq_desc[i].handler = &gatwick_pic; + request_irq( irq_cascade, gatwick_action, SA_INTERRUPT, + "cascade", 0 ); + } + printk("System has %d possible interrupts\n", max_irqs); + if (max_irqs != max_real_irqs) + printk(KERN_DEBUG "%d interrupts on main controller\n", + max_real_irqs); + +#ifdef CONFIG_XMON + request_irq(20, xmon_irq, 0, "NMI - XMON", 0); +#endif /* CONFIG_XMON */ +} + +#ifdef CONFIG_PMAC_PBOOK +/* + * These procedures are used in implementing sleep on the powerbooks. + * sleep_save_intrs() saves the states of all interrupt enables + * and disables all interrupts except for the nominated one. + * sleep_restore_intrs() restores the states of all interrupt enables. + */ +unsigned int sleep_save_mask[2]; + +void __pmac +pmac_sleep_save_intrs(int viaint) +{ + sleep_save_mask[0] = ppc_cached_irq_mask[0]; + sleep_save_mask[1] = ppc_cached_irq_mask[1]; + ppc_cached_irq_mask[0] = 0; + ppc_cached_irq_mask[1] = 0; + if (viaint > 0) + set_bit(viaint, ppc_cached_irq_mask); + out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]); + if (max_real_irqs > 32) + out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]); + (void)in_le32(&pmac_irq_hw[0]->event); + /* make sure mask gets to controller before we return to caller */ + mb(); + (void)in_le32(&pmac_irq_hw[0]->enable); +} + +void __pmac +pmac_sleep_restore_intrs(void) +{ + int i; + + out_le32(&pmac_irq_hw[0]->enable, 0); + if (max_real_irqs > 32) + out_le32(&pmac_irq_hw[1]->enable, 0); + mb(); + for (i = 0; i < max_real_irqs; ++i) + if (test_bit(i, sleep_save_mask)) + pmac_unmask_irq(i); +} +#endif /* CONFIG_PMAC_PBOOK */ diff -Nru a/arch/ppc/platforms/pmac_pic.h b/arch/ppc/platforms/pmac_pic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pmac_pic.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,14 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +#ifndef __PPC_PLATFORMS_PMAC_PIC_H +#define __PPC_PLATFORMS_PMAC_PIC_H + +#include + +extern struct hw_interrupt_type pmac_pic; + +void pmac_pic_init(void); +int pmac_get_irq(struct pt_regs *regs); + +#endif /* __PPC_PLATFORMS_PMAC_PIC_H */ diff -Nru a/arch/ppc/platforms/pmac_setup.c b/arch/ppc/platforms/pmac_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pmac_setup.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,822 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/platforms/setup.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * + * Derived from "arch/alpha/kernel/setup.c" + * Copyright (C) 1995 Linus Torvalds + * + * 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. + * + */ + +/* + * bootup setup stuff.. + */ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmac_pic.h" +#include "mem_pieces.h" + +#undef SHOW_GATWICK_IRQS + +extern long pmac_time_init(void); +extern unsigned long pmac_get_rtc_time(void); +extern int pmac_set_rtc_time(unsigned long nowtime); +extern void pmac_read_rtc_time(void); +extern void pmac_calibrate_decr(void); +extern void pmac_pcibios_fixup(void); +extern void pmac_find_bridges(void); +extern int pmac_ide_check_base(ide_ioreg_t base); +extern ide_ioreg_t pmac_ide_get_base(int index); + +extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int mackbd_getkeycode(unsigned int scancode); +extern int mackbd_translate(unsigned char keycode, unsigned char *keycodep, + char raw_mode); +extern char mackbd_unexpected_up(unsigned char keycode); +extern void mackbd_leds(unsigned char leds); +extern void __init mackbd_init_hw(void); +extern int mac_hid_kbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char mac_hid_kbd_unexpected_up(unsigned char keycode); +extern void mac_hid_init_hw(void); +extern unsigned char mac_hid_kbd_sysrq_xlate[]; +extern unsigned char pckbd_sysrq_xlate[]; +extern unsigned char mackbd_sysrq_xlate[]; +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern int keyboard_sends_linux_keycodes; +extern void pmac_nvram_update(void); + +extern int pmac_pci_enable_device_hook(struct pci_dev *dev, int initial); +extern void pmac_pcibios_after_init(void); + +extern kdev_t sd_find_target(void *host, int tgt); + +struct device_node *memory_node; + +unsigned char drive_info; + +int ppc_override_l2cr = 0; +int ppc_override_l2cr_value; +int has_l2cache = 0; + +static int current_root_goodness = -1; + +extern char saved_command_line[]; + +extern int pmac_newworld; + +#define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ + +extern void zs_kgdb_hook(int tty_num); +static void ohare_init(void); +#ifdef CONFIG_BOOTX_TEXT +void pmac_progress(char *s, unsigned short hex); +#endif + +sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN; + +#ifdef CONFIG_SMP +extern struct smp_ops_t psurge_smp_ops; +extern struct smp_ops_t core99_smp_ops; + +volatile static long int core99_l2_cache; +void __init +core99_init_l2(void) +{ + int cpu = smp_processor_id(); + + if (!(cur_cpu_spec[0]->cpu_features & CPU_FTR_L2CR)) + return; + + if (cpu == 0){ + core99_l2_cache = _get_L2CR(); + printk("CPU0: L2CR is %lx\n", core99_l2_cache); + } else { + printk("CPU%d: L2CR was %lx\n", cpu, _get_L2CR()); + _set_L2CR(0); + _set_L2CR(core99_l2_cache); + printk("CPU%d: L2CR set to %lx\n", cpu, core99_l2_cache); + } +} +#endif /* CONFIG_SMP */ + +/* + * Assume here that all clock rates are the same in a + * smp system. -- Cort + */ +int __openfirmware +of_show_percpuinfo(struct seq_file *m, int i) +{ + struct device_node *cpu_node; + int *fp, s; + + cpu_node = find_type_devices("cpu"); + if (!cpu_node) + return 0; + for (s = 0; s < i && cpu_node->next; s++) + cpu_node = cpu_node->next; + fp = (int *) get_property(cpu_node, "clock-frequency", NULL); + if (fp) + seq_printf(m, "clock\t\t: %dMHz\n", *fp / 1000000); + return 0; +} + +int __pmac +pmac_show_cpuinfo(struct seq_file *m) +{ + struct device_node *np; + char *pp; + int plen; + + /* find motherboard type */ + seq_printf(m, "machine\t\t: "); + np = find_devices("device-tree"); + if (np != NULL) { + pp = (char *) get_property(np, "model", NULL); + if (pp != NULL) + seq_printf(m, "%s\n", pp); + else + seq_printf(m, "PowerMac\n"); + pp = (char *) get_property(np, "compatible", &plen); + if (pp != NULL) { + seq_printf(m, "motherboard\t:"); + while (plen > 0) { + int l = strlen(pp) + 1; + seq_printf(m, " %s", pp); + plen -= l; + pp += l; + } + seq_printf(m, "\n"); + } + } else + seq_printf(m, "PowerMac\n"); + + /* find l2 cache info */ + np = find_devices("l2-cache"); + if (np == 0) + np = find_type_devices("cache"); + if (np != 0) { + unsigned int *ic = (unsigned int *) + get_property(np, "i-cache-size", NULL); + unsigned int *dc = (unsigned int *) + get_property(np, "d-cache-size", NULL); + seq_printf(m, "L2 cache\t:"); + has_l2cache = 1; + if (get_property(np, "cache-unified", NULL) != 0 && dc) { + seq_printf(m, " %dK unified", *dc / 1024); + } else { + if (ic) + seq_printf(m, " %dK instruction", *ic / 1024); + if (dc) + seq_printf(m, "%s %dK data", + (ic? " +": ""), *dc / 1024); + } + pp = get_property(np, "ram-type", NULL); + if (pp) + seq_printf(m, " %s", pp); + seq_printf(m, "\n"); + } + + /* find ram info */ + np = find_devices("memory"); + if (np != 0) { + int n; + struct reg_property *reg = (struct reg_property *) + get_property(np, "reg", &n); + + if (reg != 0) { + unsigned long total = 0; + + for (n /= sizeof(struct reg_property); n > 0; --n) + total += (reg++)->size; + seq_printf(m, "memory\t\t: %luMB\n", total >> 20); + } + } + + /* Checks "l2cr-value" property in the registry */ + np = find_devices("cpus"); + if (np == 0) + np = find_type_devices("cpu"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + seq_printf(m, "l2cr override\t: 0x%x\n", *l2cr); + } + } + + /* Indicate newworld/oldworld */ + seq_printf(m, "pmac-generation\t: %s\n", + pmac_newworld ? "NewWorld" : "OldWorld"); + + + return 0; +} + +#ifdef CONFIG_VT +/* + * Dummy mksound function that does nothing. + * The real one is in the dmasound driver. + */ +static void __pmac +pmac_mksound(unsigned int hz, unsigned int ticks) +{ +} +#endif /* CONFIG_VT */ + +static volatile u32 *sysctrl_regs; + +void __init +pmac_setup_arch(void) +{ + struct device_node *cpu; + int *fp; + unsigned long pvr; + + pvr = PVR_VER(mfspr(PVR)); + + /* Set loops_per_jiffy to a half-way reasonable value, + for use until calibrate_delay gets called. */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "clock-frequency", NULL); + if (fp != 0) { + if (pvr == 4 || pvr >= 8) + /* 604, G3, G4 etc. */ + loops_per_jiffy = *fp / HZ; + else + /* 601, 603, etc. */ + loops_per_jiffy = *fp / (2*HZ); + } else + loops_per_jiffy = 50000000 / HZ; + } + + /* this area has the CPU identification register + and some registers used by smp boards */ + sysctrl_regs = (volatile u32 *) ioremap(0xf8000000, 0x1000); + ohare_init(); + + /* Lookup PCI hosts */ + pmac_find_bridges(); + + /* Checks "l2cr-value" property in the registry */ + if (cur_cpu_spec[0]->cpu_features & CPU_FTR_L2CR) { + struct device_node *np = find_devices("cpus"); + if (np == 0) + np = find_type_devices("cpu"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + ppc_override_l2cr = 1; + ppc_override_l2cr_value = *l2cr; + _set_L2CR(0); + _set_L2CR(ppc_override_l2cr_value); + } + } + } + + if (ppc_override_l2cr) + printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n", + ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000) + ? "enabled" : "disabled"); + +#ifdef CONFIG_SMP + /* somewhat of a hack */ + core99_init_l2(); +#endif + +#ifdef CONFIG_KGDB + zs_kgdb_hook(0); +#endif + +#ifdef CONFIG_ADB_CUDA + find_via_cuda(); +#else + if (find_devices("via-cuda")) { + printk("WARNING ! Your machine is Cuda based but your kernel\n"); + printk(" wasn't compiled with CONFIG_ADB_CUDA option !\n"); + } +#endif +#ifdef CONFIG_ADB_PMU + find_via_pmu(); +#else + if (find_devices("via-pmu")) { + printk("WARNING ! Your machine is PMU based but your kernel\n"); + printk(" wasn't compiled with CONFIG_ADB_PMU option !\n"); + } +#endif +#ifdef CONFIG_NVRAM + pmac_nvram_init(); +#endif +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif +#ifdef CONFIG_VT + kd_mksound = pmac_mksound; +#endif +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); + else +#endif + ROOT_DEV = to_kdev_t(DEFAULT_ROOT_DEVICE); + +#ifdef CONFIG_SMP + /* Check for Core99 */ + if (find_devices("uni-n")) + ppc_md.smp_ops = &core99_smp_ops; + else + ppc_md.smp_ops = &psurge_smp_ops; +#endif /* CONFIG_SMP */ + + pci_create_OF_bus_map(); +} + +static void __init ohare_init(void) +{ + /* + * Turn on the L2 cache. + * We assume that we have a PSX memory controller iff + * we have an ohare I/O controller. + */ + if (find_devices("ohare") != NULL) { + if (((sysctrl_regs[2] >> 24) & 0xf) >= 3) { + if (sysctrl_regs[4] & 0x10) + sysctrl_regs[4] |= 0x04000020; + else + sysctrl_regs[4] |= 0x04000000; + if(has_l2cache) + printk(KERN_INFO "Level 2 cache enabled\n"); + } + } +} + +extern char *bootpath; +extern char *bootdevice; +void *boot_host; +int boot_target; +int boot_part; +extern kdev_t boot_dev; + +void __init +pmac_init2(void) +{ +#ifdef CONFIG_ADB_PMU + via_pmu_start(); +#endif +#ifdef CONFIG_ADB_CUDA + via_cuda_start(); +#endif +#ifdef CONFIG_PMAC_PBOOK + media_bay_init(); +#endif + pmac_feature_late_init(); +} + +#ifdef CONFIG_SCSI +void __init +note_scsi_host(struct device_node *node, void *host) +{ + int l; + char *p; + + l = strlen(node->full_name); + if (bootpath != NULL && bootdevice != NULL + && strncmp(node->full_name, bootdevice, l) == 0 + && (bootdevice[l] == '/' || bootdevice[l] == 0)) { + boot_host = host; + /* + * There's a bug in OF 1.0.5. (Why am I not surprised.) + * If you pass a path like scsi/sd@1:0 to canon, it returns + * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0 + * That is, the scsi target number doesn't get preserved. + * So we pick the target number out of bootpath and use that. + */ + p = strstr(bootpath, "/sd@"); + if (p != NULL) { + p += 4; + boot_target = simple_strtoul(p, NULL, 10); + p = strchr(p, ':'); + if (p != NULL) + boot_part = simple_strtoul(p + 1, NULL, 10); + } + } +} +#endif + +#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) +kdev_t __init +find_ide_boot(void) +{ + char *p; + int n; + kdev_t __init pmac_find_ide_boot(char *bootdevice, int n); + + if (bootdevice == NULL) + return NODEV; + p = strrchr(bootdevice, '/'); + if (p == NULL) + return NODEV; + n = p - bootdevice; + + return pmac_find_ide_boot(bootdevice, n); +} +#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */ + +void __init +find_boot_device(void) +{ +#if defined(CONFIG_SCSI) && defined(CONFIG_BLK_DEV_SD) + if (boot_host != NULL) { + boot_dev = sd_find_target(boot_host, boot_target); + if (!kdev_same(boot_dev, NODEV)) + return; + } +#endif +#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) + boot_dev = find_ide_boot(); +#endif +} + +static int initializing = 1; + +static int pmac_late_init(void) +{ + initializing = 0; + return 0; +} + +late_initcall(pmac_late_init); + +/* can't be __init - can be called whenever a disk is first accessed */ +void __pmac +note_bootable_part(kdev_t dev, int part, int goodness) +{ + static int found_boot = 0; + char *p; + + if (!initializing) + return; + if ((goodness <= current_root_goodness) && + !kdev_same(ROOT_DEV, to_kdev_t(DEFAULT_ROOT_DEVICE))) + return; + p = strstr(saved_command_line, "root="); + if (p != NULL && (p == saved_command_line || p[-1] == ' ')) + return; + + if (!found_boot) { + find_boot_device(); + found_boot = 1; + } + if (kdev_same(boot_dev, NODEV) || kdev_same(dev, boot_dev)) { + ROOT_DEV = mk_kdev(major(dev), minor(dev) + part); + boot_dev = NODEV; + current_root_goodness = goodness; + } +} + +void __pmac +pmac_restart(char *cmd) +{ +#ifdef CONFIG_ADB_CUDA + struct adb_request req; +#endif /* CONFIG_ADB_CUDA */ + +#ifdef CONFIG_NVRAM + pmac_nvram_update(); +#endif + + switch (sys_ctrler) { +#ifdef CONFIG_ADB_CUDA + case SYS_CTRLER_CUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_RESET_SYSTEM); + for (;;) + cuda_poll(); + break; +#endif /* CONFIG_ADB_CUDA */ +#ifdef CONFIG_ADB_PMU + case SYS_CTRLER_PMU: + pmu_restart(); + break; +#endif /* CONFIG_ADB_PMU */ + default: ; + } +} + +void __pmac +pmac_power_off(void) +{ +#ifdef CONFIG_ADB_CUDA + struct adb_request req; +#endif /* CONFIG_ADB_CUDA */ + +#ifdef CONFIG_NVRAM + pmac_nvram_update(); +#endif + + switch (sys_ctrler) { +#ifdef CONFIG_ADB_CUDA + case SYS_CTRLER_CUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_POWERDOWN); + for (;;) + cuda_poll(); + break; +#endif /* CONFIG_ADB_CUDA */ +#ifdef CONFIG_ADB_PMU + case SYS_CTRLER_PMU: + pmu_shutdown(); + break; +#endif /* CONFIG_ADB_PMU */ + default: ; + } +} + +void __pmac +pmac_halt(void) +{ + pmac_power_off(); +} + + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +static int __pmac +pmac_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ +#ifdef CONFIG_BLK_DEV_IDE_PMAC + if (pmac_ide_check_base(from) >= 0) + return 0; +#endif + return check_region(from, extent); +} + +static void __pmac +pmac_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ +#ifdef CONFIG_BLK_DEV_IDE_PMAC + if (pmac_ide_check_base(from) >= 0) + return; +#endif + request_region(from, extent, name); +} + +static void __pmac +pmac_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ +#ifdef CONFIG_BLK_DEV_IDE_PMAC + if (pmac_ide_check_base(from) >= 0) + return; +#endif + release_region(from, extent); +} +#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */ + +/* + * Read in a property describing some pieces of memory. + */ + +static int __init +get_mem_prop(char *name, struct mem_pieces *mp) +{ + struct reg_property *rp; + int i, s; + unsigned int *ip; + int nac = prom_n_addr_cells(memory_node); + int nsc = prom_n_size_cells(memory_node); + + ip = (unsigned int *) get_property(memory_node, name, &s); + if (ip == NULL) { + printk(KERN_ERR "error: couldn't get %s property on /memory\n", + name); + return 0; + } + s /= (nsc + nac) * 4; + rp = mp->regions; + for (i = 0; i < s; ++i, ip += nac+nsc) { + if (nac >= 2 && ip[nac-2] != 0) + continue; + rp->address = ip[nac-1]; + if (nsc >= 2 && ip[nac+nsc-2] != 0) + rp->size = ~0U; + else + rp->size = ip[nac+nsc-1]; + ++rp; + } + mp->n_regions = rp - mp->regions; + + /* Make sure the pieces are sorted. */ + mem_pieces_sort(mp); + mem_pieces_coalesce(mp); + return 1; +} + +/* + * On systems with Open Firmware, collect information about + * physical RAM and which pieces are already in use. + * At this point, we have (at least) the first 8MB mapped with a BAT. + * Our text, data, bss use something over 1MB, starting at 0. + * Open Firmware may be using 1MB at the 4MB point. + */ +unsigned long __init +pmac_find_end_of_memory(void) +{ + unsigned long a, total; + struct mem_pieces phys_mem; + + /* + * Find out where physical memory is, and check that it + * starts at 0 and is contiguous. It seems that RAM is + * always physically contiguous on Power Macintoshes. + * + * Supporting discontiguous physical memory isn't hard, + * it just makes the virtual <-> physical mapping functions + * more complicated (or else you end up wasting space + * in mem_map). + */ + memory_node = find_devices("memory"); + if (memory_node == NULL || !get_mem_prop("reg", &phys_mem) + || phys_mem.n_regions == 0) + panic("No RAM??"); + a = phys_mem.regions[0].address; + if (a != 0) + panic("RAM doesn't start at physical address 0"); + total = phys_mem.regions[0].size; + + if (phys_mem.n_regions > 1) { + printk("RAM starting at 0x%x is not contiguous\n", + phys_mem.regions[1].address); + printk("Using RAM from 0 to 0x%lx\n", total-1); + } + + return total; +} + +void __init +select_adb_keyboard(void) +{ +#ifdef CONFIG_VT +#ifdef CONFIG_INPUT + ppc_md.kbd_init_hw = mac_hid_init_hw; + ppc_md.kbd_translate = mac_hid_kbd_translate; + ppc_md.kbd_unexpected_up = mac_hid_kbd_unexpected_up; + ppc_md.kbd_setkeycode = 0; + ppc_md.kbd_getkeycode = 0; + ppc_md.kbd_leds = 0; +#ifdef CONFIG_MAGIC_SYSRQ +#ifdef CONFIG_MAC_ADBKEYCODES + if (!keyboard_sends_linux_keycodes) { + ppc_md.ppc_kbd_sysrq_xlate = mac_hid_kbd_sysrq_xlate; + SYSRQ_KEY = 0x69; + } else +#endif /* CONFIG_MAC_ADBKEYCODES */ + { + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x54; + } +#endif /* CONFIG_MAGIC_SYSRQ */ +#elif defined(CONFIG_ADB_KEYBOARD) + ppc_md.kbd_setkeycode = mackbd_setkeycode; + ppc_md.kbd_getkeycode = mackbd_getkeycode; + ppc_md.kbd_translate = mackbd_translate; + ppc_md.kbd_unexpected_up = mackbd_unexpected_up; + ppc_md.kbd_leds = mackbd_leds; + ppc_md.kbd_init_hw = mackbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = mackbd_sysrq_xlate; + SYSRQ_KEY = 0x69; +#endif /* CONFIG_MAGIC_SYSRQ */ +#endif /* CONFIG_INPUT_ADBHID/CONFIG_ADB_KEYBOARD */ +#endif /* CONFIG_VT */ +} + +void __init +pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* isa_io_base gets set in pmac_find_bridges */ + isa_mem_base = PMAC_ISA_MEM_BASE; + pci_dram_offset = PMAC_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = ~0L; + DMA_MODE_READ = 1; + DMA_MODE_WRITE = 2; + + ppc_md.setup_arch = pmac_setup_arch; + ppc_md.show_cpuinfo = pmac_show_cpuinfo; + ppc_md.show_percpuinfo = of_show_percpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = pmac_pic_init; + ppc_md.get_irq = pmac_get_irq; /* Changed later on ... */ + ppc_md.init = pmac_init2; + + ppc_md.pcibios_fixup = pmac_pcibios_fixup; + ppc_md.pcibios_enable_device_hook = pmac_pci_enable_device_hook; + ppc_md.pcibios_after_init = pmac_pcibios_after_init; + + ppc_md.restart = pmac_restart; + ppc_md.power_off = pmac_power_off; + ppc_md.halt = pmac_halt; + + ppc_md.time_init = pmac_time_init; + ppc_md.set_rtc_time = pmac_set_rtc_time; + ppc_md.get_rtc_time = pmac_get_rtc_time; + ppc_md.calibrate_decr = pmac_calibrate_decr; + + ppc_md.find_end_of_memory = pmac_find_end_of_memory; + + ppc_md.feature_call = pmac_do_feature_call; + + select_adb_keyboard(); + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.ide_check_region = pmac_ide_check_region; + ppc_ide_md.ide_request_region = pmac_ide_request_region; + ppc_ide_md.ide_release_region = pmac_ide_release_region; +#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */ + +#ifdef CONFIG_BOOTX_TEXT + ppc_md.progress = pmac_progress; +#endif /* CONFIG_BOOTX_TEXT */ + + if (ppc_md.progress) ppc_md.progress("pmac_init(): exit", 0); + +} + +#ifdef CONFIG_BOOTX_TEXT +void __init +pmac_progress(char *s, unsigned short hex) +{ + if (boot_text_mapped) { + btext_drawstring(s); + btext_drawchar('\n'); + } +} +#endif /* CONFIG_BOOTX_TEXT */ diff -Nru a/arch/ppc/platforms/pmac_smp.c b/arch/ppc/platforms/pmac_smp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pmac_smp.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,488 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * SMP support for power macintosh. + * + * We support both the old "powersurge" SMP architecture + * and the current Core99 (G4 PowerMac) machines. + * + * Support Macintosh G4 SMP by Troy Benjegerdes (hozer@drgw.net) + * and Ben Herrenschmidt . + * + * Support for DayStar quad CPU cards + * Copyright (C) XLR8, Inc. 1994-2000 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Powersurge (old powermac SMP) support. + */ + +extern void __secondary_start_psurge(void); +extern void __secondary_start_psurge2(void); /* Temporary horrible hack */ +extern void __secondary_start_psurge3(void); /* Temporary horrible hack */ + +/* Addresses for powersurge registers */ +#define HAMMERHEAD_BASE 0xf8000000 +#define HHEAD_CONFIG 0x90 +#define HHEAD_SEC_INTR 0xc0 + +/* register for interrupting the primary processor on the powersurge */ +/* N.B. this is actually the ethernet ROM! */ +#define PSURGE_PRI_INTR 0xf3019000 + +/* register for storing the start address for the secondary processor */ +/* N.B. this is the PCI config space address register for the 1st bridge */ +#define PSURGE_START 0xf2800000 + +/* Daystar/XLR8 4-CPU card */ +#define PSURGE_QUAD_REG_ADDR 0xf8800000 + +#define PSURGE_QUAD_IRQ_SET 0 +#define PSURGE_QUAD_IRQ_CLR 1 +#define PSURGE_QUAD_IRQ_PRIMARY 2 +#define PSURGE_QUAD_CKSTOP_CTL 3 +#define PSURGE_QUAD_PRIMARY_ARB 4 +#define PSURGE_QUAD_BOARD_ID 6 +#define PSURGE_QUAD_WHICH_CPU 7 +#define PSURGE_QUAD_CKSTOP_RDBK 8 +#define PSURGE_QUAD_RESET_CTL 11 + +#define PSURGE_QUAD_OUT(r, v) (out_8(quad_base + ((r) << 4) + 4, (v))) +#define PSURGE_QUAD_IN(r) (in_8(quad_base + ((r) << 4) + 4) & 0x0f) +#define PSURGE_QUAD_BIS(r, v) (PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) | (v))) +#define PSURGE_QUAD_BIC(r, v) (PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) & ~(v))) + +/* virtual addresses for the above */ +static volatile u8 *hhead_base; +static volatile u8 *quad_base; +static volatile u32 *psurge_pri_intr; +static volatile u8 *psurge_sec_intr; +static volatile u32 *psurge_start; + +/* what sort of powersurge board we have */ +static int psurge_type; + +/* values for psurge_type */ +#define PSURGE_DUAL 0 +#define PSURGE_QUAD_OKEE 1 +#define PSURGE_QUAD_COTTON 2 +#define PSURGE_QUAD_ICEGRASS 3 + +/* l2 cache stuff for dual G4 macs */ +extern void core99_init_l2(void); + +/* + * Set and clear IPIs for powersurge. + */ +static inline void psurge_set_ipi(int cpu) +{ + if (cpu == 0) + in_be32(psurge_pri_intr); + else if (psurge_type == PSURGE_DUAL) + out_8(psurge_sec_intr, 0); + else + PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_SET, 1 << cpu); +} + +static inline void psurge_clr_ipi(int cpu) +{ + if (cpu > 0) { + if (psurge_type == PSURGE_DUAL) + out_8(psurge_sec_intr, ~0); + else + PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, 1 << cpu); + } +} + +/* + * On powersurge (old SMP powermac architecture) we don't have + * separate IPIs for separate messages like openpic does. Instead + * we have a bitmap for each processor, where a 1 bit means that + * the corresponding message is pending for that processor. + * Ideally each cpu's entry would be in a different cache line. + * -- paulus. + */ +static unsigned long psurge_smp_message[NR_CPUS]; + +void __pmac +psurge_smp_message_recv(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + int msg; + + /* clear interrupt */ + psurge_clr_ipi(cpu); + + if (smp_num_cpus < 2) + return; + + /* make sure there is a message there */ + for (msg = 0; msg < 4; msg++) + if (test_and_clear_bit(msg, &psurge_smp_message[cpu])) + smp_message_recv(msg, regs); +} + +void __pmac +psurge_primary_intr(int irq, void *d, struct pt_regs *regs) +{ + psurge_smp_message_recv(regs); +} + +static void __pmac +smp_psurge_message_pass(int target, int msg, unsigned long data, int wait) +{ + int i; + + if (smp_num_cpus < 2) + return; + + for (i = 0; i < smp_num_cpus; i++) { + if (target == MSG_ALL + || (target == MSG_ALL_BUT_SELF && i != smp_processor_id()) + || target == i) { + set_bit(msg, &psurge_smp_message[i]); + psurge_set_ipi(i); + } + } +} + +/* + * Determine a quad card presence. We read the board ID register, we + * force the data bus to change to something else, and we read it again. + * It it's stable, then the register probably exist (ugh !) + */ +static int __init psurge_quad_probe(void) +{ + int type; + unsigned int i; + + type = PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID); + if (type < PSURGE_QUAD_OKEE || type > PSURGE_QUAD_ICEGRASS + || type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID)) + return PSURGE_DUAL; + + /* looks OK, try a slightly more rigorous test */ + /* bogus is not necessarily cacheline-aligned, + though I don't suppose that really matters. -- paulus */ + for (i = 0; i < 100; i++) { + volatile u32 bogus[8]; + bogus[(0+i)%8] = 0x00000000; + bogus[(1+i)%8] = 0x55555555; + bogus[(2+i)%8] = 0xFFFFFFFF; + bogus[(3+i)%8] = 0xAAAAAAAA; + bogus[(4+i)%8] = 0x33333333; + bogus[(5+i)%8] = 0xCCCCCCCC; + bogus[(6+i)%8] = 0xCCCCCCCC; + bogus[(7+i)%8] = 0x33333333; + wmb(); + asm volatile("dcbf 0,%0" : : "r" (bogus) : "memory"); + mb(); + if (type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID)) + return PSURGE_DUAL; + } + return type; +} + +static void __init psurge_quad_init(void) +{ + int procbits; + + if (ppc_md.progress) ppc_md.progress("psurge_quad_init", 0x351); + procbits = ~PSURGE_QUAD_IN(PSURGE_QUAD_WHICH_CPU); + if (psurge_type == PSURGE_QUAD_ICEGRASS) + PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits); + else + PSURGE_QUAD_BIC(PSURGE_QUAD_CKSTOP_CTL, procbits); + mdelay(33); + out_8(psurge_sec_intr, ~0); + PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, procbits); + PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits); + if (psurge_type != PSURGE_QUAD_ICEGRASS) + PSURGE_QUAD_BIS(PSURGE_QUAD_CKSTOP_CTL, procbits); + PSURGE_QUAD_BIC(PSURGE_QUAD_PRIMARY_ARB, procbits); + mdelay(33); + PSURGE_QUAD_BIC(PSURGE_QUAD_RESET_CTL, procbits); + mdelay(33); + PSURGE_QUAD_BIS(PSURGE_QUAD_PRIMARY_ARB, procbits); + mdelay(33); +} + +static int __init smp_psurge_probe(void) +{ + int i, ncpus; + + /* We don't do SMP on the PPC601 -- paulus */ + if (PVR_VER(mfspr(PVR)) == 1) + return 1; + + /* + * The powersurge cpu board can be used in the generation + * of powermacs that have a socket for an upgradeable cpu card, + * including the 7500, 8500, 9500, 9600. + * The device tree doesn't tell you if you have 2 cpus because + * OF doesn't know anything about the 2nd processor. + * Instead we look for magic bits in magic registers, + * in the hammerhead memory controller in the case of the + * dual-cpu powersurge board. -- paulus. + */ + if (find_devices("hammerhead") == NULL) + return 1; + + hhead_base = ioremap(HAMMERHEAD_BASE, 0x800); + quad_base = ioremap(PSURGE_QUAD_REG_ADDR, 1024); + psurge_sec_intr = hhead_base + HHEAD_SEC_INTR; + + psurge_type = psurge_quad_probe(); + if (psurge_type != PSURGE_DUAL) { + psurge_quad_init(); + /* All released cards using this HW design have 4 CPUs */ + ncpus = 4; + } else { + iounmap((void *) quad_base); + if ((in_8(hhead_base + HHEAD_CONFIG) & 0x02) == 0) { + /* not a dual-cpu card */ + iounmap((void *) hhead_base); + return 1; + } + ncpus = 2; + } + + psurge_start = ioremap(PSURGE_START, 4); + psurge_pri_intr = ioremap(PSURGE_PRI_INTR, 4); + + /* this is not actually strictly necessary -- paulus. */ + for (i = 1; i < ncpus; ++i) + smp_hw_index[i] = i; + + if (ppc_md.progress) ppc_md.progress("smp_psurge_probe - done", 0x352); + + return ncpus; +} + +static void __init smp_psurge_kick_cpu(int nr) +{ + void (*start)(void) = __secondary_start_psurge; + unsigned long a; + + /* may need to flush here if secondary bats aren't setup */ + for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32) + asm volatile("dcbf 0,%0" : : "r" (a) : "memory"); + asm volatile("sync"); + + if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu", 0x353); + + /* setup entry point of secondary processor */ + switch (nr) { + case 2: + start = __secondary_start_psurge2; + break; + case 3: + start = __secondary_start_psurge3; + break; + } + + out_be32(psurge_start, __pa(start)); + mb(); + + psurge_set_ipi(nr); + udelay(10); + psurge_clr_ipi(nr); + + if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu - done", 0x354); +} + +/* + * With the dual-cpu powersurge board, the decrementers and timebases + * of both cpus are frozen after the secondary cpu is started up, + * until we give the secondary cpu another interrupt. This routine + * uses this to get the timebases synchronized. + * -- paulus. + */ +static void __init psurge_dual_sync_tb(int cpu_nr) +{ + static volatile int sec_tb_reset = 0; + int t; + + set_dec(tb_ticks_per_jiffy); + set_tb(0, 0); + last_jiffy_stamp(cpu_nr) = 0; + + if (cpu_nr > 0) { + mb(); + sec_tb_reset = 1; + return; + } + + /* wait for the secondary to have reset its TB before proceeding */ + for (t = 10000000; t > 0 && !sec_tb_reset; --t) + ; + + /* now interrupt the secondary, starting both TBs */ + psurge_set_ipi(1); + + smp_tb_synchronized = 1; +} + +static void __init +smp_psurge_setup_cpu(int cpu_nr) +{ + + if (cpu_nr == 0) { + if (smp_num_cpus < 2) + return; + /* reset the entry point so if we get another intr we won't + * try to startup again */ + out_be32(psurge_start, 0x100); + if (request_irq(30, psurge_primary_intr, 0, "primary IPI", 0)) + printk(KERN_ERR "Couldn't get primary IPI interrupt"); + } + + if (psurge_type == PSURGE_DUAL) + psurge_dual_sync_tb(cpu_nr); +} + +static int __init +smp_core99_probe(void) +{ + struct device_node *cpus; + int i, ncpus = 1; + + if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345); + cpus = find_type_devices("cpu"); + if (cpus) + while ((cpus = cpus->next) != NULL) + ++ncpus; + printk("smp_core99_probe: found %d cpus\n", ncpus); + if (ncpus > 1) { + openpic_request_IPIs(); + for (i = 1; i < ncpus; ++i) + smp_hw_index[i] = i; + } + + return ncpus; +} + +static void __init +smp_core99_kick_cpu(int nr) +{ + unsigned long save_vector, new_vector; + unsigned long flags; +#if 1 /* New way... */ + volatile unsigned long *vector + = ((volatile unsigned long *)(KERNELBASE+0x100)); + if (nr < 1 || nr > 3) + return; +#else + volatile unsigned long *vector + = ((volatile unsigned long *)(KERNELBASE+0x500)); + if (nr != 1) + return; +#endif + if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu", 0x346); + + local_irq_save(flags); + local_irq_disable(); + + /* Save reset vector */ + save_vector = *vector; + + /* Setup fake reset vector that does + * b __secondary_start_psurge - KERNELBASE + */ + switch(nr) { + case 1: + new_vector = (unsigned long)__secondary_start_psurge; + break; + case 2: + new_vector = (unsigned long)__secondary_start_psurge2; + break; + case 3: + new_vector = (unsigned long)__secondary_start_psurge3; + break; + } + *vector = 0x48000002 + new_vector - KERNELBASE; + + /* flush data cache and inval instruction cache */ + flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); + + /* Put some life in our friend */ + pmac_call_feature(PMAC_FTR_RESET_CPU, NULL, nr, 0); + + /* FIXME: We wait a bit for the CPU to take the exception, I should + * instead wait for the entry code to set something for me. Well, + * ideally, all that crap will be done in prom.c and the CPU left + * in a RAM-based wait loop like CHRP. + */ + mdelay(1); + + /* Restore our exception vector */ + *vector = save_vector; + flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); + + local_irq_restore(flags); + if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347); +} + +static void __init +smp_core99_setup_cpu(int cpu_nr) +{ + /* Setup openpic */ + do_openpic_setup_cpu(); + + /* Setup L2 */ + if (cpu_nr != 0) + core99_init_l2(); + else + if (ppc_md.progress) ppc_md.progress("core99_setup_cpu 0 done", 0x349); +} + +/* PowerSurge-style Macs */ +struct smp_ops_t psurge_smp_ops __pmacdata = { + smp_psurge_message_pass, + smp_psurge_probe, + smp_psurge_kick_cpu, + smp_psurge_setup_cpu, +}; + +/* Core99 Macs (dual G4s) */ +struct smp_ops_t core99_smp_ops __pmacdata = { + smp_openpic_message_pass, + smp_core99_probe, + smp_core99_kick_cpu, + smp_core99_setup_cpu, +}; diff -Nru a/arch/ppc/platforms/pmac_time.c b/arch/ppc/platforms/pmac_time.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pmac_time.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,282 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * Support for periodic interrupts (100 per second) and for getting + * the current time from the RTC on Power Macintoshes. + * + * We use the decrementer register for our periodic interrupts. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern rwlock_t xtime_lock; + +/* Apparently the RTC stores seconds since 1 Jan 1904 */ +#define RTC_OFFSET 2082844800 + +/* + * Calibrate the decrementer frequency with the VIA timer 1. + */ +#define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */ + +/* VIA registers */ +#define RS 0x200 /* skip between registers */ +#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ +#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ +#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ +#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ +#define ACR (11*RS) /* Auxiliary control register */ +#define IFR (13*RS) /* Interrupt flag register */ + +/* Bits in ACR */ +#define T1MODE 0xc0 /* Timer 1 mode */ +#define T1MODE_CONT 0x40 /* continuous interrupts */ + +/* Bits in IFR and IER */ +#define T1_INT 0x40 /* Timer 1 interrupt */ + +extern struct timezone sys_tz; + +long __init +pmac_time_init(void) +{ +#ifdef CONFIG_NVRAM + s32 delta = 0; + int dst; + + delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16; + delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8; + delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb); + if (delta & 0x00800000UL) + delta |= 0xFF000000UL; + dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0); + printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60, + dst ? "on" : "off"); + return delta; +#else + return 0; +#endif +} + +unsigned long __pmac +pmac_get_rtc_time(void) +{ +#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) + struct adb_request req; + unsigned long now; +#endif + + /* Get the time from the RTC */ + switch (sys_ctrler) { +#ifdef CONFIG_ADB_CUDA + case SYS_CTRLER_CUDA: + if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0) + return 0; + while (!req.complete) + cuda_poll(); + if (req.reply_len != 7) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + now = (req.reply[3] << 24) + (req.reply[4] << 16) + + (req.reply[5] << 8) + req.reply[6]; + return now - RTC_OFFSET; +#endif /* CONFIG_ADB_CUDA */ +#ifdef CONFIG_ADB_PMU + case SYS_CTRLER_PMU: + if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) + return 0; + while (!req.complete) + pmu_poll(); + if (req.reply_len != 4) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + now = (req.reply[0] << 24) + (req.reply[1] << 16) + + (req.reply[2] << 8) + req.reply[3]; + return now - RTC_OFFSET; +#endif /* CONFIG_ADB_PMU */ + default: ; + } + return 0; +} + +int __pmac +pmac_set_rtc_time(unsigned long nowtime) +{ +#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) + struct adb_request req; +#endif + + nowtime += RTC_OFFSET; + + switch (sys_ctrler) { +#ifdef CONFIG_ADB_CUDA + case SYS_CTRLER_CUDA: + if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME, + nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0) + return 0; + while (!req.complete) + cuda_poll(); + if ((req.reply_len != 3) && (req.reply_len != 7)) + printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n", + req.reply_len); + return 1; +#endif /* CONFIG_ADB_CUDA */ +#ifdef CONFIG_ADB_PMU + case SYS_CTRLER_PMU: + if (pmu_request(&req, NULL, 5, PMU_SET_RTC, + nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0) + return 0; + while (!req.complete) + pmu_poll(); + if (req.reply_len != 0) + printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n", + req.reply_len); + return 1; +#endif /* CONFIG_ADB_PMU */ + default: + return 0; + } +} + +/* + * Calibrate the decrementer register using VIA timer 1. + * This is used both on powermacs and CHRP machines. + */ +int __init +via_calibrate_decr(void) +{ + struct device_node *vias; + volatile unsigned char *via; + int count = VIA_TIMER_FREQ_6 / HZ; + unsigned int dstart, dend; + + vias = find_devices("via-cuda"); + if (vias == 0) + vias = find_devices("via-pmu"); + if (vias == 0) + vias = find_devices("via"); + if (vias == 0 || vias->n_addrs == 0) + return 0; + via = (volatile unsigned char *) + ioremap(vias->addrs[0].address, vias->addrs[0].size); + + /* set timer 1 for continuous interrupts */ + out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT); + /* set the counter to a small value */ + out_8(&via[T1CH], 2); + /* set the latch to `count' */ + out_8(&via[T1LL], count); + out_8(&via[T1LH], count >> 8); + /* wait until it hits 0 */ + while ((in_8(&via[IFR]) & T1_INT) == 0) + ; + dstart = get_dec(); + /* clear the interrupt & wait until it hits 0 again */ + in_8(&via[T1CL]); + while ((in_8(&via[IFR]) & T1_INT) == 0) + ; + dend = get_dec(); + + tb_ticks_per_jiffy = (dstart - dend) / 6; + tb_to_us = mulhwu_scale_factor(dstart - dend, 60000); + + printk(KERN_INFO "via_calibrate_decr: ticks per jiffy = %u (%u ticks)\n", + tb_ticks_per_jiffy, dstart - dend); + + return 1; +} + +#ifdef CONFIG_PMAC_PBOOK +/* + * Reset the time after a sleep. + */ +static int __pmac +time_sleep_notify(struct pmu_sleep_notifier *self, int when) +{ + static unsigned long time_diff; + unsigned long flags; + + switch (when) { + case PBOOK_SLEEP_NOW: + read_lock_irqsave(&xtime_lock, flags); + time_diff = xtime.tv_sec - pmac_get_rtc_time(); + read_unlock_irqrestore(&xtime_lock, flags); + break; + case PBOOK_WAKE: + write_lock_irqsave(&xtime_lock, flags); + xtime.tv_sec = pmac_get_rtc_time() + time_diff; + xtime.tv_usec = 0; + last_rtc_update = xtime.tv_sec; + write_unlock_irqrestore(&xtime_lock, flags); + break; + } + return PBOOK_SLEEP_OK; +} + +static struct pmu_sleep_notifier time_sleep_notifier __pmacdata = { + time_sleep_notify, SLEEP_LEVEL_MISC, +}; +#endif /* CONFIG_PMAC_PBOOK */ + +/* + * Query the OF and get the decr frequency. + * This was taken from the pmac time_init() when merging the prep/pmac + * time functions. + */ +void __init +pmac_calibrate_decr(void) +{ + struct device_node *cpu; + unsigned int freq, *fp; + +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier(&time_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ + + /* We assume MacRISC2 machines have correct device-tree + * calibration. That's better since the VIA itself seems + * to be slightly off. --BenH + */ + if (!machine_is_compatible("MacRISC2")) + if (via_calibrate_decr()) + return; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + cpu = find_type_devices("cpu"); + if (cpu == 0) + panic("can't find cpu node in time_init"); + fp = (unsigned int *) get_property(cpu, "timebase-frequency", NULL); + if (fp == 0) + panic("can't get cpu timebase frequency"); + freq = *fp; + printk("time_init: decrementer frequency = %u.%.6u MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); +} diff -Nru a/arch/ppc/platforms/powerpmc250.c b/arch/ppc/platforms/powerpmc250.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/powerpmc250.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,390 @@ +/* + * arch/ppc/platforms/powerpmc250.c + * + * Board setup routines for Force PowerPMC-250 Processor PMC + * + * Author: Troy Benjegerdes + * Borrowed heavily from prpmc750_*.c by + * Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#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 +#include +#include +#include + +extern void powerpmc250_find_bridges(void); +extern unsigned long loops_per_jiffy; + +static u_char powerpmc250_openpic_initsenses[] __initdata = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, /* PMC INTA (also MPC107 output interrupt INTA) */ + 1, /* PMC INTB (also I82559 Ethernet controller) */ + 1, /* PMC INTC */ + 1, /* PMC INTD */ + 0, /* DUART interrupt (active high) */ +}; + +static int +powerpmc250_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m,"machine\t\t: Force PowerPMC250\n"); + + return 0; +} + +static void __init +powerpmc250_setup_arch(void) +{ + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Lookup PCI host bridges */ + powerpmc250_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00ff); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 */ +#endif + + printk("Force PowerPMC250 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); +} + +#if 0 +/* + * Compute the PrPMC750's bus speed using the baud clock as a + * reference. + */ +unsigned long __init powerpmc250_get_bus_speed(void) +{ + unsigned long tbl_start, tbl_end; + unsigned long current_state, old_state, bus_speed; + unsigned char lcr, dll, dlm; + int baud_divisor, count; + + /* Read the UART's baud clock divisor */ + lcr = readb(PRPMC750_SERIAL_0_LCR); + writeb(lcr | UART_LCR_DLAB, PRPMC750_SERIAL_0_LCR); + dll = readb(PRPMC750_SERIAL_0_DLL); + dlm = readb(PRPMC750_SERIAL_0_DLM); + writeb(lcr & ~UART_LCR_DLAB, PRPMC750_SERIAL_0_LCR); + baud_divisor = (dlm << 8) | dll; + + /* + * Use the baud clock divisor and base baud clock + * to determine the baud rate and use that as + * the number of baud clock edges we use for + * the time base sample. Make it half the baud + * rate. + */ + count = PRPMC750_BASE_BAUD / (baud_divisor * 16); + + /* Find the first edge of the baud clock */ + old_state = readb(PRPMC750_STATUS_REG) & PRPMC750_BAUDOUT_MASK; + do { + current_state = readb(PRPMC750_STATUS_REG) & + PRPMC750_BAUDOUT_MASK; + } while(old_state == current_state); + + old_state = current_state; + + /* Get the starting time base value */ + tbl_start = get_tbl(); + + /* + * Loop until we have found a number of edges equal + * to half the count (half the baud rate) + */ + do { + do { + current_state = readb(PRPMC750_STATUS_REG) & + PRPMC750_BAUDOUT_MASK; + } while(old_state == current_state); + old_state = current_state; + } while (--count); + + /* Get the ending time base value */ + tbl_end = get_tbl(); + + /* Compute bus speed */ + bus_speed = (tbl_end-tbl_start)*128; + + return bus_speed; +} +#endif + +static void __init +powerpmc250_calibrate_decr(void) +{ + unsigned long freq; + int divisor = 4; + + //freq = powerpmc250_get_bus_speed(); +#warning hardcoded bus freq + freq = 100000000; + + tb_ticks_per_jiffy = freq / (HZ * divisor); + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); +} + +static void +powerpmc250_restart(char *cmd) +{ + __cli(); + /* Hard reset */ + writeb(0x11, 0xfe000332); + while(1); +} + +static void +powerpmc250_halt(void) +{ + __cli(); + while (1); +} + +static void +powerpmc250_power_off(void) +{ + powerpmc250_halt(); +} + +/* Resolves the open_pic.c build without including i8259.c */ +int i8259_poll(void) +{ + return 0; +} + +static void __init +powerpmc250_init_IRQ(void) +{ + + OpenPIC_InitSenses = powerpmc250_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(powerpmc250_openpic_initsenses); + openpic_init(1, 0, 0, -1); +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +powerpmc250_set_bat(void) +{ + unsigned long bat3u, bat3l; + static int mapping_set = 0; + + if (!mapping_set) + { + __asm__ __volatile__( + " lis %0,0xf000\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x1ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (bat3u), "=r" (bat3l)); + + mapping_set = 1; + } + return; +} + +static unsigned long __init +powerpmc250_find_end_of_memory(void) +{ + /* Cover I/O space with a BAT */ + /* yuck, better hope your ram size is a power of 2 -- paulus */ + powerpmc250_set_bat(); + + return mpc10x_get_mem_size(MPC10X_MEM_MAP_B); +} + +static void __init +powerpmc250_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + +#ifdef CONFIG_BLK_DEV_INITRD + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif + + /* Copy cmd_line parameters */ + if ( r6) + { + *(char *)(r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6 + KERNELBASE)); + } + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + + ppc_md.setup_arch = powerpmc250_setup_arch; + ppc_md.show_cpuinfo = powerpmc250_show_cpuinfo; + ppc_md.init_IRQ = powerpmc250_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.find_end_of_memory = powerpmc250_find_end_of_memory; + ppc_md.setup_io_mappings = powerpmc250_map_io; + + ppc_md.restart = powerpmc250_restart; + ppc_md.power_off = powerpmc250_power_off; + ppc_md.halt = powerpmc250_halt; + + /* PowerPMC250 has no timekeeper part */ + ppc_md.time_init = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.calibrate_decr = powerpmc250_calibrate_decr; +} + + +/* + * (This used to be arch/ppc/platforms/powerpmc250_pci.c) + * + * PCI support for Force PowerPMC250 + * + */ + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif /* DEBUG */ + +static inline int __init +powerpmc250_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {17, 0, 0, 0}, /* Device 11 - 82559 */ + {0, 0, 0, 0}, /* 12 */ + {0, 0, 0, 0}, /* 13 */ + {0, 0, 0, 0}, /* 14 */ + {0, 0, 0, 0}, /* 15 */ + {16, 17, 18, 19}, /* Device 16 - PMC A1?? */ + }; + const long min_idsel = 11, max_idsel = 16, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +static int +powerpmc250_exclude_device(u_char bus, u_char devfn) +{ + /* + * While doing PCI Scan the MPC107 will 'detect' itself as + * device on the PCI Bus, will create an incorrect response and + * later will respond incorrectly to Configuration read coming + * from another device. + * + * The work around is that when doing a PCI Scan one + * should skip its own device number in the scan. + * + * The top IDsel is AD13 and the middle is AD14. + * + * -- Note from force + */ + + if ((bus == 0) && (PCI_SLOT(devfn) == 13 || PCI_SLOT(devfn) == 14)) { + return PCIBIOS_DEVICE_NOT_FOUND; + } + else { + return PCIBIOS_SUCCESSFUL; + } +} + +void __init +powerpmc250_find_bridges(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + if (!hose){ + printk("Can't allocate PCI 'hose' structure!!!\n"); + return; + } + + hose->first_busno = 0; + hose->last_busno = 0xff; + + if (mpc10x_bridge_init(hose, + MPC10X_MEM_MAP_B, + MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE) == 0) { + + hose->mem_resources[0].end = 0xffffffff; + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + /* ppc_md.pcibios_fixup = pcore_pcibios_fixup; */ + ppc_md.pci_swizzle = common_swizzle; + + ppc_md.pci_exclude_device = powerpmc250_exclude_device; + ppc_md.pci_map_irq = powerpmc250_map_irq; + } else { + if (ppc_md.progress) + ppc_md.progress("Bridge init failed", 0x100); + printk("Host bridge init failed\n"); + } + +} diff -Nru a/arch/ppc/platforms/powerpmc250.h b/arch/ppc/platforms/powerpmc250.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/powerpmc250.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,42 @@ +/* + * include/asm-ppc/platforms/powerpmc250.h + * + * Definitions for Force PowerPMC-250 board support + * + * Author: Troy Benjegerdes + * + * Borrowed heavily from prpmc750.h by Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __ASMPPC_POWERPMC250_H +#define __ASMPPC_POWERPMC250_H + +#include + +#define POWERPMC250_PCI_CONFIG_ADDR 0x80000cf8 +#define POWERPMC250_PCI_CONFIG_DATA 0x80000cfc + +#define POWERPMC250_PCI_PHY_MEM_BASE 0xc0000000 +#define POWERPMC250_PCI_MEM_BASE 0xf0000000 +#define POWERPMC250_PCI_IO_BASE 0x80000000 + +#define POWERPMC250_ISA_IO_BASE POWERPMC250_PCI_IO_BASE +#define POWERPMC250_ISA_MEM_BASE POWERPMC250_PCI_MEM_BASE +#define POWERPMC250_PCI_MEM_OFFSET POWERPMC250_PCI_PHY_MEM_BASE + +#define POWERPMC250_SYS_MEM_BASE 0x80000000 + +#define POWERPMC250_HAWK_SMC_BASE 0xfef80000 + +#define POWERPMC250_BASE_BAUD 12288000 +#define POWERPMC250_SERIAL 0xff000000 +#define POWERPMC250_SERIAL_IRQ 20 + +#endif /* __ASMPPC_POWERPMC250_H */ diff -Nru a/arch/ppc/platforms/powerpmc250_serial.h b/arch/ppc/platforms/powerpmc250_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/powerpmc250_serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,42 @@ +/* + * include/asm-ppc/platforms/powerpmc250_serial.h + * + * Motorola PrPMC750 serial support + * + * Author: Troy Benjegerdes + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifdef __KERNEL__ +#ifndef __ASMPPC_POWERPMC250_SERIAL_H +#define __ASMPPC_POWERPMC250_SERIAL_H + +#include +#include + +#define RS_TABLE_SIZE 1 + +#define BASE_BAUD (POWERPMC250_BASE_BAUD / 16) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF) +#endif + +#define SERIAL_PORT_DFNS \ +{ 0, BASE_BAUD, POWERPMC250_SERIAL, POWERPMC250_SERIAL_IRQ, STD_COM_FLAGS, /* ttyS0 */\ + iomem_base: (u8 *)POWERPMC250_SERIAL, \ + iomem_reg_shift: 0, \ + io_type: SERIAL_IO_MEM } + +#endif +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/pplus_pci.c b/arch/ppc/platforms/pplus_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pplus_pci.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,538 @@ +/* + * arch/ppc/platforms/pplus_pci.c + * + * PCI setup for MCG PowerPlus + * + * Author: Randy Vinson + * + * Derived from original PowerPlus PReP work by + * Cort Dougan, Johnnie Peters, Matt Porter, and + * Troy Benjegerdes. + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +unsigned char *Motherboard_map_name; + +/* Tables for known hardware */ + +/* Motorola Mesquite */ +static inline int +mesquite_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * MPIC interrupts for various IDSEL values (MPIC IRQ0 = + * Linux IRQ16 (to leave room for ISA IRQs at 0-15). + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 0 */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 19, 19, 19, 19 }, /* IDSEL 16 - PMC Slot 1 */ + { 0, 0, 0, 0 }, /* IDSEL 17 - unused */ + { 0, 0, 0, 0 }, /* IDSEL 18 - unused */ + { 0, 0, 0, 0 }, /* IDSEL 19 - unused */ + { 24, 25, 26, 27 }, /* IDSEL 20 - P2P bridge (to cPCI 1) */ + { 0, 0, 0, 0 }, /* IDSEL 21 - unused */ + { 28, 29, 30, 31 } /* IDSEL 22 - P2P bridge (to cPCI 2) */ + }; + + const long min_idsel = 14, max_idsel = 22, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +/* Motorola Sitka */ +static inline int +sitka_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * MPIC interrupts for various IDSEL values (MPIC IRQ0 = + * Linux IRQ16 (to leave room for ISA IRQs at 0-15). + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 0 */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 25, 26, 27, 28 }, /* IDSEL 16 - PMC Slot 1 */ + { 28, 25, 26, 27 }, /* IDSEL 17 - PMC Slot 2 */ + { 0, 0, 0, 0 }, /* IDSEL 18 - unused */ + { 0, 0, 0, 0 }, /* IDSEL 19 - unused */ + { 20, 0, 0, 0 } /* IDSEL 20 - P2P bridge (to cPCI) */ + }; + + const long min_idsel = 14, max_idsel = 20, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +/* Motorola MTX */ +static inline int +MTX_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * MPIC interrupts for various IDSEL values (MPIC IRQ0 = + * Linux IRQ16 (to leave room for ISA IRQs at 0-15). + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 19, 0, 0, 0 }, /* IDSEL 12 - SCSI */ + { 0, 0, 0, 0 }, /* IDSEL 13 - unused */ + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 25, 26, 27, 28 }, /* IDSEL 16 - PMC Slot 1 */ + { 26, 27, 28, 25 }, /* IDSEL 17 - PMC Slot 2 */ + { 27, 28, 25, 26 } /* IDSEL 18 - PCI Slot 3 */ + }; + + const long min_idsel = 12, max_idsel = 18, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +/* Motorola MTX Plus */ +/* Secondary bus interrupt routing is not supported yet */ +static inline int +MTXplus_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * MPIC interrupts for various IDSEL values (MPIC IRQ0 = + * Linux IRQ16 (to leave room for ISA IRQs at 0-15). + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 19, 0, 0, 0 }, /* IDSEL 12 - SCSI */ + { 0, 0, 0, 0 }, /* IDSEL 13 - unused */ + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 1 */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 25, 26, 27, 28 }, /* IDSEL 16 - PCI Slot 1P */ + { 26, 27, 28, 25 }, /* IDSEL 17 - PCI Slot 2P */ + { 27, 28, 25, 26 }, /* IDSEL 18 - PCI Slot 3P */ + { 26, 0, 0, 0 }, /* IDSEL 19 - Enet 2 */ + { 0, 0, 0, 0 } /* IDSEL 20 - P2P Bridge */ + }; + + const long min_idsel = 12, max_idsel = 20, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +static inline int +Genesis2_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + /* 2600 + * Raven 31 + * ISA 11 + * SCSI 12 - IRQ3 + * Univ 13 + * eth 14 - IRQ2 + * VGA 15 - IRQ4 + * PMC1 16 - IRQ9,10,11,12 = PMC1 A-D + * PMC2 17 - IRQ12,9,10,11 = A-D + * SCSI2 18 - IRQ11 + * eth2 19 - IRQ10 + * PCIX 20 - IRQ9,10,11,12 = PCI A-D + */ + + /* 2400 + * Hawk 31 + * ISA 11 + * Univ 13 + * eth 14 - IRQ2 + * PMC1 16 - IRQ9,10,11,12 = PMC A-D + * PMC2 17 - IRQ12,9,10,11 = PMC A-D + * PCIX 20 - IRQ9,10,11,12 = PMC A-D + */ + + /* 2300 + * Raven 31 + * ISA 11 + * Univ 13 + * eth 14 - IRQ2 + * PMC1 16 - 9,10,11,12 = A-D + * PMC2 17 - 9,10,11,12 = B,C,D,A + */ + + static char pci_irq_table[][4] = + /* + * MPIC interrupts for various IDSEL values (MPIC IRQ0 = + * Linux IRQ16 (to leave room for ISA IRQs at 0-15). + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 19, 0, 0, 0 }, /* IDSEL 12 - SCSI */ + { 0, 0, 0, 0 }, /* IDSEL 13 - Universe PCI - VME */ + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 1 */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 25, 26, 27, 28 }, /* IDSEL 16 - PCI/PMC Slot 1P */ + { 28, 25, 26, 27 }, /* IDSEL 17 - PCI/PMC Slot 2P */ + { 27, 28, 25, 26 }, /* IDSEL 18 - PCI Slot 3P */ + { 26, 0, 0, 0 }, /* IDSEL 19 - Enet 2 */ + { 25, 26, 27, 28 } /* IDSEL 20 - P2P Bridge */ + }; + + const long min_idsel = 12, max_idsel = 20, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +#define MOTOROLA_CPUTYPE_REG 0x800 +#define MOTOROLA_BASETYPE_REG 0x803 +#define MPIC_RAVEN_ID 0x48010000 +#define MPIC_HAWK_ID 0x48030000 +#define MOT_PROC2_BIT 0x800 + +static u_char pplus_openpic_initsenses[] __initdata = { + 1, /* MVME2600_INT_SIO */ + 0, /* MVME2600_INT_FALCN_ECC_ERR */ + 1, /* MVME2600_INT_PCI_ETHERNET */ + 1, /* MVME2600_INT_PCI_SCSI */ + 1, /* MVME2600_INT_PCI_GRAPHICS */ + 1, /* MVME2600_INT_PCI_VME0 */ + 1, /* MVME2600_INT_PCI_VME1 */ + 1, /* MVME2600_INT_PCI_VME2 */ + 1, /* MVME2600_INT_PCI_VME3 */ + 1, /* MVME2600_INT_PCI_INTA */ + 1, /* MVME2600_INT_PCI_INTB */ + 1, /* MVME2600_INT_PCI_INTC */ + 1, /* MVME2600_INT_PCI_INTD */ + 1, /* MVME2600_INT_LM_SIG0 */ + 1, /* MVME2600_INT_LM_SIG1 */ +}; + +int mot_entry = -1; +int prep_keybd_present = 1; +int mot_multi = 0; + +int __init raven_init(void) +{ + unsigned short devid; + unsigned char base_mod; + + /* set the MPIC base address */ + early_write_config_dword(0, 0, 0, PCI_BASE_ADDRESS_1, 0x3cfc0000); + + pplus_mpic_init(PREP_ISA_MEM_BASE); + + OpenPIC_InitSenses = pplus_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(pplus_openpic_initsenses); + + ppc_md.get_irq = openpic_get_irq; + + /* This is a hack. If this is a 2300 or 2400 mot board then there is + * no keyboard controller and we have to indicate that. + */ + + early_read_config_word(0, 0, 0, PCI_VENDOR_ID, &devid); + base_mod = inb(MOTOROLA_BASETYPE_REG); + if ((devid == PCI_DEVICE_ID_MOTOROLA_HAWK) || + (base_mod == 0xF9) || + (base_mod == 0xFA) || (base_mod == 0xE1)) + prep_keybd_present = 0; + + return 1; +} + +struct brd_info { + int cpu_type; /* 0x100 mask assumes for Raven and Hawk boards that the level/edge are set */ + /* 0x200 if this board has a Hawk chip. */ + int base_type; + int max_cpu; /* ored with 0x80 if this board should be checked for multi CPU */ + const char *name; + int (*map_irq)(struct pci_dev *, + unsigned char, + unsigned char); +}; +struct brd_info mot_info[] = { + {0x300, 0x00, 0x00, "MVME 2400", Genesis2_map_irq}, + {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", mesquite_map_irq}, + {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", sitka_map_irq}, + {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", mesquite_map_irq}, + {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_map_irq}, + {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_map_irq}, + {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_map_irq}, + {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_map_irq}, + {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_map_irq}, + {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_map_irq}, + {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_map_irq}, + {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_map_irq}, + {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_map_irq}, + {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_map_irq}, + {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_map_irq}, + {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_map_irq}, + {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_map_irq}, + {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_map_irq}, + {0x000, 0x00, 0x00, "", NULL} +}; + +void __init pplus_set_board_type(void) +{ + unsigned char cpu_type; + unsigned char base_mod; + int entry; + unsigned short devid; + unsigned long *ProcInfo = NULL; + + cpu_type = inb(MOTOROLA_CPUTYPE_REG) & 0xF0; + base_mod = inb(MOTOROLA_BASETYPE_REG); + early_read_config_word(0, 0, 0, PCI_VENDOR_ID, &devid); + + for (entry = 0; mot_info[entry].cpu_type != 0; entry++) { + + /* Check for Hawk chip */ + if (mot_info[entry].cpu_type & 0x200) { + if (devid != PCI_DEVICE_ID_MOTOROLA_HAWK) + continue; + } else { + /* store the system config register for later use. */ + ProcInfo = (unsigned long *)ioremap(0xfef80400, 4); + + /* Check non hawk boards */ + if ((mot_info[entry].cpu_type & 0xff) != cpu_type) + continue; + + if (mot_info[entry].base_type == 0) { + mot_entry = entry; + break; + } + + if (mot_info[entry].base_type != base_mod) + continue; + } + + if (!(mot_info[entry].max_cpu & 0x80)) { + mot_entry = entry; + break; + } + + /* processor 1 not present and max processor zero indicated */ + if ((*ProcInfo & MOT_PROC2_BIT) && !(mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } + + /* processor 1 present and max processor zero indicated */ + if (!(*ProcInfo & MOT_PROC2_BIT) && (mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } + + /* Indicate to system if this is a multiprocessor board */ + if (!(*ProcInfo & MOT_PROC2_BIT)) { + mot_multi = 1; + } + } + + if (mot_entry == -1) + + /* No particular cpu type found - assume Mesquite (MCP750) */ + mot_entry = 1; + + Motherboard_map_name = (unsigned char *)mot_info[mot_entry].name; + ppc_md.pci_map_irq = mot_info[mot_entry].map_irq; +} +void __init +pplus_pib_init(void) +{ + unsigned char reg; + unsigned short short_reg; + + struct pci_dev *dev = NULL; + + /* + * Perform specific configuration for the Via Tech or + * or Winbond PCI-ISA-Bridge part. + */ + if ((dev = pci_find_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_1, dev))) { + /* + * PPCBUG does not set the enable bits + * for the IDE device. Force them on here. + */ + pci_read_config_byte(dev, 0x40, ®); + + reg |= 0x03; /* IDE: Chip Enable Bits */ + pci_write_config_byte(dev, 0x40, reg); + } + if ((dev = pci_find_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_2, + dev)) && (dev->devfn = 0x5a)) { + /* Force correct USB interrupt */ + dev->irq = 11; + pci_write_config_byte(dev, + PCI_INTERRUPT_LINE, + dev->irq); + } + if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_83C553, dev))) { + /* Clear PCI Interrupt Routing Control Register. */ + short_reg = 0x0000; + pci_write_config_word(dev, 0x44, short_reg); + /* Route IDE interrupts to IRQ 14 */ + reg = 0xEE; + pci_write_config_byte(dev, 0x43, reg); + } + + if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_82C105, dev))){ + /* + * Disable LEGIRQ mode so PCI INTS are routed + * directly to the 8259 and enable both channels + */ + pci_write_config_dword(dev, 0x40, 0x10ff0033); + + /* Force correct IDE interrupt */ + dev->irq = 14; + pci_write_config_byte(dev, + PCI_INTERRUPT_LINE, + dev->irq); + } +} + +void __init +pplus_set_VIA_IDE_legacy(void) +{ + unsigned short vend, dev; + + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_VENDOR_ID, &vend); + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_DEVICE_ID, &dev); + + if ((vend == PCI_VENDOR_ID_VIA) && + (dev == PCI_DEVICE_ID_VIA_82C586_1)) { + + unsigned char temp; + + /* put back original "standard" port base addresses */ + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_0, 0x1f1); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_1, 0x3f5); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_2, 0x171); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_3, 0x375); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_4, 0xcc01); + + /* put into legacy mode */ + early_read_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + &temp); + temp &= ~0x05; + early_write_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + temp); + } +} + +void +pplus_set_VIA_IDE_native(void) +{ + unsigned short vend, dev; + + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_VENDOR_ID, &vend); + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_DEVICE_ID, &dev); + + if ((vend == PCI_VENDOR_ID_VIA) && + (dev == PCI_DEVICE_ID_VIA_82C586_1)) { + + unsigned char temp; + + /* put into native mode */ + early_read_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + &temp); + temp |= 0x05; + early_write_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + temp); + } +} + +void __init +pplus_pcibios_fixup(void) +{ + + printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name); + + /* Setup the Winbond or Via PIB */ + pplus_pib_init(); +} + +static void __init +pplus_init_resource(struct resource *res, unsigned long start, + unsigned long end, int flags) +{ + res->flags = flags; + res->start = start; + res->end = end; + res->name = "PCI host bridge"; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; +} + +void __init +pplus_setup_hose(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + hose->pci_mem_offset = PREP_ISA_MEM_BASE; + hose->io_base_virt = (void *)PREP_ISA_IO_BASE; + + pplus_init_resource(&hose->io_resource, 0x00000000, 0x0fffffff, + IORESOURCE_IO); + pplus_init_resource(&hose->mem_resources[0], 0xc0000000, 0xfdffffff, + IORESOURCE_MEM); + + hose->io_space.start = 0x00000000; + hose->io_space.end = 0x0fffffff; + hose->mem_space.start = 0x00000000; + hose->mem_space.end = 0x3cfbffff; /* MPIC at 0x3cfc0000-0x3dffffff */ + + setup_indirect_pci(hose, 0x80000cf8, 0x80000cfc); + + pplus_set_VIA_IDE_legacy(); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = pplus_pcibios_fixup; + ppc_md.pci_swizzle = common_swizzle; + pplus_set_board_type(); +} + diff -Nru a/arch/ppc/platforms/pplus_setup.c b/arch/ppc/platforms/pplus_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/pplus_setup.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,553 @@ +/* + * arch/ppc/platforms/pplus_setup.c + * + * Board setup routines for MCG PowerPlus + * + * Author: Randy Vinson + * + * Derived from original PowerPlus PReP work by + * Cort Dougan, Johnnie Peters, Matt Porter, and + * Troy Benjegerdes. + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#undef CONFIG_SERIAL_TEXT_DEBUG +#undef DUMP_DBATS + +TODC_ALLOC(); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; + +extern void pplus_setup_hose(void); + +extern void pplus_set_VIA_IDE_native(void); + +extern unsigned long loops_per_jiffy; + +static int +pplus_show_cpuinfo(struct seq_file *m) +{ + extern char *Motherboard_map_name; + + seq_printf(m, "vendor\t\t: Motorola MCG\n"); + seq_printf(m, "machine\t\t: %s\n", Motherboard_map_name); + + return 0; +} + +static void __init +pplus_setup_arch(void) +{ + unsigned char reg; + + if ( ppc_md.progress ) + ppc_md.progress("pplus_setup_arch: enter\n", 0); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000; + + if ( ppc_md.progress ) + ppc_md.progress("pplus_setup_arch: find_bridges\n", 0); + + /* Setup PCI host bridge */ + pplus_setup_hose(); + + /* Set up floppy in PS/2 mode */ + outb(0x09, SIO_CONFIG_RA); + reg = inb(SIO_CONFIG_RD); + reg = (reg & 0x3F) | 0x40; + outb(reg, SIO_CONFIG_RD); + outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ + + /* Enable L2. Assume we don't need to flush -- Cort*/ + *(unsigned char *)(0x8000081c) |= 3; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00ff); /* /dev/nfs */ +#else + ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 */ +#endif + + printk("PowerPlus port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + if ( ppc_md.progress ) + ppc_md.progress("pplus_setup_arch: raven_init\n", 0); + + raven_init(); + +#ifdef CONFIG_VGA_CONSOLE + /* remap the VGA memory */ + vgacon_remap_base = 0xf0000000; + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif + if ( ppc_md.progress ) + ppc_md.progress("pplus_setup_arch: exit\n", 0); +} + +static void +pplus_restart(char *cmd) +{ + unsigned long i = 10000; + + __cli(); + + /* set VIA IDE controller into native mode */ + pplus_set_VIA_IDE_native(); + + /* set exception prefix high - to the prom */ + _nmask_and_or_msr(0, MSR_IP); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( i != 0 ) i++; + panic("restart failed\n"); +} + +static void +pplus_halt(void) +{ + unsigned long flags; + __cli(); + /* set exception prefix high - to the prom */ + save_flags( flags ); + restore_flags( flags|MSR_IP ); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( 1 ) ; + /* + * Not reached + */ +} + +static void +pplus_power_off(void) +{ + pplus_halt(); +} + +static unsigned int +pplus_irq_cannonicalize(u_int irq) +{ + if (irq == 2) + { + return 9; + } + else + { + return irq; + } +} + +static int +pplus_get_irq(struct pt_regs *regs) +{ + return i8259_poll(); +} + +static void __init +pplus_init_IRQ(void) +{ + int i; + + if (OpenPIC_Addr != NULL) + openpic_init(1, NUM_8259_INTERRUPTS, 0, -1); + for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ ) + irq_desc[i].handler = &i8259_pic; + i8259_init(NULL); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +static int +pplus_ide_default_irq(ide_ioreg_t base) +{ + switch (base) { + case 0x1f0: return 14; + case 0x170: return 15; + default: return 0; + } +} + +static ide_ioreg_t +pplus_ide_default_io_base(int index) +{ + switch (index) { + case 0: return 0x1f0; + case 1: return 0x170; + default: + return 0; + } +} + +static int +pplus_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +static void +pplus_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); +} + +static void +pplus_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); +} + +static void __init +pplus_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) +{ + ide_ioreg_t reg = data_port; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = reg; + reg += 1; + } + if (ctrl_port) { + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + } else { + hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206; + } + if (irq != NULL) + *irq = pplus_ide_default_irq(data_port); +} +#endif + +#ifdef CONFIG_SMP +/* PowerPlus (MTX) support */ +static int __init +smp_pplus_probe(void) +{ + extern int mot_multi; + + if (mot_multi) { + openpic_request_IPIs(); + smp_hw_index[1] = 1; + return 2; + } + + return 1; +} + +static void __init +smp_pplus_kick_cpu(int nr) +{ + *(unsigned long *)KERNELBASE = nr; + asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); + printk("CPU1 reset, waiting\n"); +} + +static void __init +smp_pplus_setup_cpu(int cpu_nr) +{ + if (OpenPIC_Addr) + do_openpic_setup_cpu(); +} + +static struct smp_ops_t pplus_smp_ops = { + smp_openpic_message_pass, + smp_pplus_probe, + smp_pplus_kick_cpu, + smp_pplus_setup_cpu, +}; +#endif /* CONFIG_SMP */ + +#ifdef DUMP_DBATS +static void print_dbat(int idx, u32 bat) { + + char str[64]; + + sprintf(str, "DBAT%c%c = 0x%08x\n", + (char)((idx - DBAT0U) / 2) + '0', + (idx & 1) ? 'L' : 'U', bat); + ppc_md.progress(str, 0); +} + +#define DUMP_DBAT(x) \ + do { \ + u32 __temp = mfspr(x);\ + print_dbat(x, __temp); \ + } while (0) + +static void dump_dbats(void) { + + if (ppc_md.progress) { + DUMP_DBAT(DBAT0U); + DUMP_DBAT(DBAT0L); + DUMP_DBAT(DBAT1U); + DUMP_DBAT(DBAT1L); + DUMP_DBAT(DBAT2U); + DUMP_DBAT(DBAT2L); + DUMP_DBAT(DBAT3U); + DUMP_DBAT(DBAT3L); + } +} +#endif + +static unsigned long __init +pplus_find_end_of_memory(void) +{ + unsigned long total; + + if (ppc_md.progress) + ppc_md.progress("pplus_find_end_of_memory\n",0); + +#ifdef DUMP_DBATS + dump_dbats(); +#endif + + total = pplus_get_mem_size(0xfef80000); + return (total); +} + +static void __init +pplus_map_io(void) +{ + io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); + io_block_mapping(0xf0000000, 0xc0000000, 0x08000000, _PAGE_IO); +} + +static void __init +pplus_init2(void) +{ +#ifdef CONFIG_NVRAM + request_region(PREP_NVRAM_AS0, 0x8, "nvram"); +#endif + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); +} + +/* + * Set BAT 2 to access 0x8000000 so progress messages will work and set BAT 3 + * to 0xf0000000 to access Falcon/Raven or Hawk registers + */ +static __inline__ void +pplus_set_bat(void) +{ + static int mapping_set = 0; + + if (!mapping_set) { + + /* wait for all outstanding memory accesses to complete */ + mb(); + + /* setup DBATs */ + mtspr(DBAT2U, 0x80001ffe); + mtspr(DBAT2L, 0x8000002a); + mtspr(DBAT3U, 0xf0001ffe); + mtspr(DBAT3L, 0xf000002a); + + /* wait for updates */ + mb(); + + mapping_set = 1; + } + + return; +} + +#ifdef CONFIG_SERIAL_TEXT_DEBUG +#include +#include +#include +#include + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in */ +}; + + void +pplus_progress(char *s, unsigned short hex) +{ + volatile char c; + volatile unsigned long com_port; + u16 shift; + + com_port = rs_table[0].port + isa_io_base; + shift = rs_table[0].iomem_reg_shift; + + while ((c = *s++) != 0) { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = c; + + if (c == '\n') { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = '\r'; + } + } +} +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* Map in board regs, etc. */ + pplus_set_bat(); + + isa_io_base = PREP_ISA_IO_BASE; + isa_mem_base = PREP_ISA_MEM_BASE; + pci_dram_offset = PREP_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = pplus_setup_arch; + ppc_md.show_percpuinfo = NULL; + ppc_md.show_cpuinfo = pplus_show_cpuinfo; + ppc_md.irq_cannonicalize = pplus_irq_cannonicalize; + ppc_md.init_IRQ = pplus_init_IRQ; + /* this gets changed later on if we have an OpenPIC -- Cort */ + ppc_md.get_irq = pplus_get_irq; + ppc_md.init = pplus_init2; + + ppc_md.restart = pplus_restart; + ppc_md.power_off = pplus_power_off; + ppc_md.halt = pplus_halt; + + TODC_INIT(TODC_TYPE_MK48T59, PREP_NVRAM_AS0, PREP_NVRAM_AS1, + PREP_NVRAM_DATA, 8); + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; + + ppc_md.find_end_of_memory = pplus_find_end_of_memory; + ppc_md.setup_io_mappings = pplus_map_io; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = pplus_progress; +#else /* !CONFIG_SERIAL_TEXT_DEBUG */ + ppc_md.progress = NULL; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_irq = pplus_ide_default_irq; + ppc_ide_md.default_io_base = pplus_ide_default_io_base; + ppc_ide_md.ide_check_region = pplus_ide_check_region; + ppc_ide_md.ide_request_region = pplus_ide_request_region; + ppc_ide_md.ide_release_region = pplus_ide_release_region; + ppc_ide_md.ide_init_hwif = pplus_ide_init_hwif_ports; +#endif + +#ifdef CONFIG_VT + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x54; +#endif +#endif + +#ifdef CONFIG_SMP + ppc_md.smp_ops = &pplus_smp_ops; +#endif /* CONFIG_SMP */ +} diff -Nru a/arch/ppc/platforms/prep_nvram.c b/arch/ppc/platforms/prep_nvram.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prep_nvram.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,143 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/platforms/prep_nvram.c + * + * Copyright (C) 1998 Corey Minyard + * + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static char nvramData[MAX_PREP_NVRAM]; +static NVRAM_MAP *nvram=(NVRAM_MAP *)&nvramData[0]; + +unsigned char __prep prep_nvram_read_val(int addr) +{ + outb(addr, PREP_NVRAM_AS0); + outb(addr>>8, PREP_NVRAM_AS1); + return inb(PREP_NVRAM_DATA); +} + +void __prep prep_nvram_write_val(int addr, + unsigned char val) +{ + outb(addr, PREP_NVRAM_AS0); + outb(addr>>8, PREP_NVRAM_AS1); + outb(val, PREP_NVRAM_DATA); +} + +void __init init_prep_nvram(void) +{ + unsigned char *nvp; + int i; + int nvramSize; + + /* + * The following could fail if the NvRAM were corrupt but + * we expect the boot firmware to have checked its checksum + * before boot + */ + nvp = (char *) &nvram->Header; + for (i=0; iHeader.GEAddress+nvram->Header.GELength; + if(nvramSize>MAX_PREP_NVRAM) + { + /* + * NvRAM is too large + */ + nvram->Header.GELength=0; + return; + } + + /* + * Read the remainder of the PReP NvRAM + */ + nvp = (char *) &nvram->GEArea[0]; + for (i=sizeof(HEADER); iGEArea)) < nvram->Header.GELength) + && (*cp == '\0')) + { + cp++; + } + + if ((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) { + return cp; + } else { + return NULL; + } +} + + + diff -Nru a/arch/ppc/platforms/prep_pci.c b/arch/ppc/platforms/prep_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prep_pci.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,1272 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * PReP pci functions. + * Originally by Gary Thomas + * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) + * + * The motherboard routes/maps will disappear shortly. -- Cort + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_DEVNR 22 + +/* Which PCI interrupt line does a given device [slot] use? */ +/* Note: This really should be two dimensional based in slot/pin used */ +static unsigned char *Motherboard_map; +unsigned char *Motherboard_map_name; + +/* How is the 82378 PIRQ mapping setup? */ +static unsigned char *Motherboard_routes; + +static void (*Motherboard_non0)(struct pci_dev *); + +static void Powerplus_Map_Non0(struct pci_dev *); + +/* Used for Motorola to store system config register */ +static unsigned long *ProcInfo; + +/* Tables for known hardware */ + +/* Motorola PowerStackII - Utah */ +static char Utah_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 5, /* Slot 2 - SCSI - NCR825A */ + 0, /* Slot 3 - unused */ + 1, /* Slot 4 - Ethernet - DEC2114x */ + 0, /* Slot 5 - unused */ + 3, /* Slot 6 - PCI Card slot #1 */ + 4, /* Slot 7 - PCI Card slot #2 */ + 5, /* Slot 8 - PCI Card slot #3 */ + 5, /* Slot 9 - PCI Bridge */ + /* added here in case we ever support PCI bridges */ + /* Secondary PCI bus cards are at slot-9,6 & slot-9,7 */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 5, /* Slot 12 - SCSI - NCR825A */ + 0, /* Slot 13 - unused */ + 3, /* Slot 14 - enet */ + 0, /* Slot 15 - unused */ + 2, /* Slot 16 - unused */ + 3, /* Slot 17 - unused */ + 5, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +static char Utah_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 9, /* Line 1 */ + 10, /* Line 2 */ + 11, /* Line 3 */ + 14, /* Line 4 */ + 15, /* Line 5 */ +}; + +/* Motorola PowerStackII - Omaha */ +/* no integrated SCSI or ethernet */ +static char Omaha_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 3, /* Slot 2 - Winbond EIDE */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 1, /* Slot 6 - PCI slot 1 */ + 2, /* Slot 7 - PCI slot 2 */ + 3, /* Slot 8 - PCI slot 3 */ + 4, /* Slot 9 - PCI slot 4 */ /* needs indirect access */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 0, /* Slot 14 - unused */ + 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI slot 4 */ /* needs indirect access */ + 0, + 0, + 0, +}; + +static char Omaha_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 9, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* Motorola PowerStack */ +static char Blackhawk_pci_IRQ_map[19] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 1, /* Slot P7 */ + 2, /* Slot P6 */ + 3, /* Slot P5 */ +}; + +static char Blackhawk_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 9, /* Line 1 */ + 11, /* Line 2 */ + 15, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* Motorola Mesquite */ +static char Mesquite_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 3, /* Slot 16 - PMC */ + 0, /* Slot 17 - unused */ + 0, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +/* Motorola Sitka */ +static char Sitka_pci_IRQ_map[21] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PMC 1 */ + 12, /* Slot 17 - PMC 2 */ + 0, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 4, /* Slot 20 - NT P2P bridge */ +}; + +/* Motorola MTX */ +static char MTX_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PCI/PMC slot 1 */ + 10, /* Slot 17 - PCI/PMC slot 2 */ + 11, /* Slot 18 - PCI slot 3 */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +/* Motorola MTX Plus */ +/* Secondary bus interrupt routing is not supported yet */ +static char MTXplus_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet 1 */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PCI slot 1P */ + 10, /* Slot 17 - PCI slot 2P */ + 11, /* Slot 18 - PCI slot 3P */ + 10, /* Slot 19 - Ethernet 2 */ + 0, /* Slot 20 - P2P Bridge */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +static char Raven_pci_IRQ_routes[] __prepdata = +{ + 0, /* This is a dummy structure */ +}; + +/* Motorola MVME16xx */ +static char Genesis_pci_IRQ_map[16] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ +}; + +static char Genesis_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +static char Genesis2_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - Ethernet */ + 0, /* Slot 11 - Universe PCI - VME Bridge */ + 3, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - SCSI */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PMC 1 */ + 12, /* Slot 17 - pci */ + 11, /* Slot 18 - pci */ + 10, /* Slot 19 - pci */ + 0, /* Slot 20 - pci */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +/* Motorola Series-E */ +static char Comet_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI bridge */ + 0, + 0, + 0, +}; + +static char Comet_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* Motorola Series-EX */ +static char Comet2_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 3, /* Slot 2 - SCSI - NCR825A */ + 0, /* Slot 3 - unused */ + 1, /* Slot 4 - Ethernet - DEC2104X */ + 0, /* Slot 5 - unused */ + 1, /* Slot 6 - PCI slot 1 */ + 2, /* Slot 7 - PCI slot 2 */ + 3, /* Slot 8 - PCI slot 3 */ + 4, /* Slot 9 - PCI bridge */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI - NCR825A */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet - DEC2104X */ + 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI bridge */ + 0, + 0, + 0, +}; + +static char Comet2_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15, /* Line 4 */ +}; + +/* + * ibm 830 (and 850?). + * This is actually based on the Carolina motherboard + * -- Cort + */ +static char ibm8xx_pci_IRQ_map[23] __prepdata = { + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - FireCoral */ + 4, /* Slot 12 - Ethernet PCIINTD# */ + 2, /* Slot 13 - PCI Slot #2 */ + 2, /* Slot 14 - S3 Video PCIINTD# */ + 0, /* Slot 15 - onboard SCSI (INDI) [1] */ + 3, /* Slot 16 - NCR58C810 RS6000 Only PCIINTC# */ + 0, /* Slot 17 - unused */ + 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 2, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ +}; + +static char ibm8xx_pci_IRQ_routes[] __prepdata = { + 0, /* Line 0 - unused */ + 15, /* Line 1 */ + 15, /* Line 2 */ + 15, /* Line 3 */ + 15, /* Line 4 */ +}; + +/* + * a 6015 ibm board + * -- Cort + */ +static char ibm6015_pci_IRQ_map[23] __prepdata = { + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - */ + 1, /* Slot 12 - SCSI */ + 2, /* Slot 13 - */ + 2, /* Slot 14 - */ + 1, /* Slot 15 - */ + 1, /* Slot 16 - */ + 0, /* Slot 17 - */ + 2, /* Slot 18 - */ + 0, /* Slot 19 - */ + 0, /* Slot 20 - */ + 0, /* Slot 21 - */ + 2, /* Slot 22 - */ +}; + +static char ibm6015_pci_IRQ_routes[] __prepdata = { + 0, /* Line 0 - unused */ + 13, /* Line 1 */ + 15, /* Line 2 */ + 15, /* Line 3 */ + 15, /* Line 4 */ +}; + + +/* IBM Nobis and Thinkpad 850 */ +static char Nobis_pci_IRQ_map[23] __prepdata ={ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 0, /* Slot 14 - unused */ + 0, /* Slot 15 - unused */ +}; + +static char Nobis_pci_IRQ_routes[] __prepdata = { + 0, /* Line 0 - Unused */ + 13, /* Line 1 */ + 13, /* Line 2 */ + 13, /* Line 3 */ + 13 /* Line 4 */ +}; + +/* + * IBM RS/6000 43p/140 -- paulus + * XXX we should get all this from the residual data + */ +static char ibm43p_pci_IRQ_map[23] __prepdata = { + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - FireCoral ISA bridge */ + 6, /* Slot 12 - Ethernet */ + 0, /* Slot 13 - openpic */ + 0, /* Slot 14 - unused */ + 0, /* Slot 15 - unused */ + 7, /* Slot 16 - NCR58C825a onboard scsi */ + 0, /* Slot 17 - unused */ + 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 1, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ +}; + +static char ibm43p_pci_IRQ_routes[] __prepdata = { + 0, /* Line 0 - unused */ + 15, /* Line 1 */ + 15, /* Line 2 */ + 15, /* Line 3 */ + 15, /* Line 4 */ +}; + +/* Motorola PowerPlus architecture PCI IRQ tables */ +/* Interrupt line values for INTA-D on primary/secondary MPIC inputs */ + +struct powerplus_irq_list +{ + unsigned char primary[4]; /* INT A-D */ + unsigned char secondary[4]; /* INT A-D */ +}; + +/* + * For standard PowerPlus boards, bus 0 PCI INTs A-D are routed to + * OpenPIC inputs 9-12. PCI INTs A-D from the on board P2P bridge + * are routed to OpenPIC inputs 5-8. These values are offset by + * 16 in the table to reflect the Linux kernel interrupt value. + */ +struct powerplus_irq_list Powerplus_pci_IRQ_list __prepdata = +{ + {25, 26, 27, 28}, + {21, 22, 23, 24} +}; + +/* + * For the MCP750 (system slot board), cPCI INTs A-D are routed to + * OpenPIC inputs 8-11 and the PMC INTs A-D are routed to OpenPIC + * input 3. On a hot swap MCP750, the companion card PCI INTs A-D + * are routed to OpenPIC inputs 12-15. These values are offset by + * 16 in the table to reflect the Linux kernel interrupt value. + */ +struct powerplus_irq_list Mesquite_pci_IRQ_list __prepdata = +{ + {24, 25, 26, 27}, + {28, 29, 30, 31} +}; + +/* + * This table represents the standard PCI swizzle defined in the + * PCI bus specification. + */ +static unsigned char prep_pci_intpins[4][4] __prepdata = +{ + { 1, 2, 3, 4}, /* Buses 0, 4, 8, ... */ + { 2, 3, 4, 1}, /* Buses 1, 5, 9, ... */ + { 3, 4, 1, 2}, /* Buses 2, 6, 10 ... */ + { 4, 1, 2, 3}, /* Buses 3, 7, 11 ... */ +}; + +/* We have to turn on LEVEL mode for changed IRQ's */ +/* All PCI IRQ's need to be level mode, so this should be something + * other than hard-coded as well... IRQ's are individually mappable + * to either edge or level. + */ + +/* + * 8259 edge/level control definitions + */ +#define ISA8259_M_ELCR 0x4d0 +#define ISA8259_S_ELCR 0x4d1 + +#define ELCRS_INT15_LVL 0x80 +#define ELCRS_INT14_LVL 0x40 +#define ELCRS_INT12_LVL 0x10 +#define ELCRS_INT11_LVL 0x08 +#define ELCRS_INT10_LVL 0x04 +#define ELCRS_INT9_LVL 0x02 +#define ELCRS_INT8_LVL 0x01 +#define ELCRM_INT7_LVL 0x80 +#define ELCRM_INT5_LVL 0x20 + +#define CFGPTR(dev) (0x80800000 | (1<<(dev>>3)) | ((dev&7)<<8) | offset) +#define DEVNO(dev) (dev>>3) + +#define cfg_read(val, addr, type, op) *val = op((type)(addr)) +#define cfg_write(val, addr, type, op) op((type *)(addr), (val)) + +#define cfg_read_bad(val, size) *val = bad_##size; +#define cfg_write_bad(val, size) + +#define bad_byte 0xff +#define bad_word 0xffff +#define bad_dword 0xffffffffU + +#define PREP_PCI_OP(rw, size, type, op) \ +static int __prep \ +prep_##rw##_config_##size(struct pci_dev *dev, int offset, type val) \ +{ \ + if ((dev->bus->number != 0) || (DEVNO(dev->devfn) > MAX_DEVNR)) \ + { \ + cfg_##rw##_bad(val, size) \ + return PCIBIOS_DEVICE_NOT_FOUND; \ + } \ + cfg_##rw(val, CFGPTR(dev->devfn), type, op); \ + return PCIBIOS_SUCCESSFUL; \ +} + +PREP_PCI_OP(read, byte, u8 *, in_8) +PREP_PCI_OP(read, word, u16 *, in_le16) +PREP_PCI_OP(read, dword, u32 *, in_le32) +PREP_PCI_OP(write, byte, u8, out_8) +PREP_PCI_OP(write, word, u16, out_le16) +PREP_PCI_OP(write, dword, u32, out_le32) + +static struct pci_ops prep_pci_ops = +{ + prep_read_config_byte, + prep_read_config_word, + prep_read_config_dword, + prep_write_config_byte, + prep_write_config_word, + prep_write_config_dword +}; + +#define MOTOROLA_CPUTYPE_REG 0x800 +#define MOTOROLA_BASETYPE_REG 0x803 +#define MPIC_RAVEN_ID 0x48010000 +#define MPIC_HAWK_ID 0x48030000 +#define MOT_PROC2_BIT 0x800 + +static u_char mvme2600_openpic_initsenses[] __initdata = { + 1, /* MVME2600_INT_SIO */ + 0, /* MVME2600_INT_FALCN_ECC_ERR */ + 1, /* MVME2600_INT_PCI_ETHERNET */ + 1, /* MVME2600_INT_PCI_SCSI */ + 1, /* MVME2600_INT_PCI_GRAPHICS */ + 1, /* MVME2600_INT_PCI_VME0 */ + 1, /* MVME2600_INT_PCI_VME1 */ + 1, /* MVME2600_INT_PCI_VME2 */ + 1, /* MVME2600_INT_PCI_VME3 */ + 1, /* MVME2600_INT_PCI_INTA */ + 1, /* MVME2600_INT_PCI_INTB */ + 1, /* MVME2600_INT_PCI_INTC */ + 1, /* MVME2600_INT_PCI_INTD */ + 1, /* MVME2600_INT_LM_SIG0 */ + 1, /* MVME2600_INT_LM_SIG1 */ +}; + +#define MOT_RAVEN_PRESENT 0x1 +#define MOT_HAWK_PRESENT 0x2 + +int mot_entry = -1; +int prep_keybd_present = 1; +int MotMPIC; +int mot_multi; + +int __init +raven_init(void) +{ + unsigned int devid; + unsigned int pci_membase; + unsigned char base_mod; + + /* Check to see if the Raven chip exists. */ + if ( _prep_type != _PREP_Motorola) { + OpenPIC_Addr = NULL; + return 0; + } + + /* Check to see if this board is a type that might have a Raven. */ + if ((inb(MOTOROLA_CPUTYPE_REG) & 0xF0) != 0xE0) { + OpenPIC_Addr = NULL; + return 0; + } + + /* Check the first PCI device to see if it is a Raven. */ + early_read_config_dword(0, 0, 0, PCI_VENDOR_ID, &devid); + + switch (devid & 0xffff0000) { + case MPIC_RAVEN_ID: + MotMPIC = MOT_RAVEN_PRESENT; + break; + case MPIC_HAWK_ID: + MotMPIC = MOT_HAWK_PRESENT; + break; + default: + OpenPIC_Addr = NULL; + return 0; + } + + + /* Read the memory base register. */ + early_read_config_dword(0, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase); + + if (pci_membase == 0) { + OpenPIC_Addr = NULL; + return 0; + } + + /* Map the Raven MPIC registers to virtual memory. */ + OpenPIC_Addr = ioremap(pci_membase+0xC0000000, 0x22000); + + OpenPIC_InitSenses = mvme2600_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(mvme2600_openpic_initsenses); + + ppc_md.get_irq = openpic_get_irq; + + /* If raven is present on Motorola store the system config register + * for later use. + */ + ProcInfo = (unsigned long *)ioremap(0xfef80400, 4); + + /* Indicate to system if this is a multiprocessor board */ + if (!(*ProcInfo & MOT_PROC2_BIT)) { + mot_multi = 1; + } + + /* This is a hack. If this is a 2300 or 2400 mot board then there is + * no keyboard controller and we have to indicate that. + */ + base_mod = inb(MOTOROLA_BASETYPE_REG); + if ((MotMPIC == MOT_HAWK_PRESENT) || (base_mod == 0xF9) || + (base_mod == 0xFA) || (base_mod == 0xE1)) + prep_keybd_present = 0; + + return 1; +} + +struct mot_info { + int cpu_type; /* 0x100 mask assumes for Raven and Hawk boards that the level/edge are set */ + /* 0x200 if this board has a Hawk chip. */ + int base_type; + int max_cpu; /* ored with 0x80 if this board should be checked for multi CPU */ + const char *name; + unsigned char *map; + unsigned char *routes; + void (*map_non0_bus)(struct pci_dev *); /* For boards with more than bus 0 devices. */ + struct powerplus_irq_list *pci_irq_list; /* List of PCI MPIC inputs */ + unsigned char secondary_bridge_devfn; /* devfn of secondary bus transparent bridge */ +} mot_info[] __prepdata = { + {0x300, 0x00, 0x00, "MVME 2400", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x010, 0x00, 0x00, "Genesis", Genesis_pci_IRQ_map, Genesis_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x020, 0x00, 0x00, "Powerstack (Series E)", Comet_pci_IRQ_map, Comet_pci_IRQ_routes, NULL, NULL, 0x00}, + {0x040, 0x00, 0x00, "Blackhawk (Powerstack)", Blackhawk_pci_IRQ_map, Blackhawk_pci_IRQ_routes, NULL, NULL, 0x00}, + {0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)", Omaha_pci_IRQ_map, Omaha_pci_IRQ_routes, NULL, NULL, 0x00}, + {0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)", Utah_pci_IRQ_map, Utah_pci_IRQ_routes, NULL, NULL, 0x00}, + {0x0A0, 0x00, 0x00, "Powerstack (Series EX)", Comet2_pci_IRQ_map, Comet2_pci_IRQ_routes, NULL, NULL, 0x00}, + {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Mesquite_pci_IRQ_list, 0xFF}, + {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", Sitka_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Mesquite_pci_IRQ_list, 0xC0}, + {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xA0}, + {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xA0}, + {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x000, 0x00, 0x00, "", NULL, NULL, NULL, NULL, 0x00} +}; + +void __init +ibm_prep_init(void) +{ + u32 addr; +#ifdef CONFIG_PREP_RESIDUAL + PPC_DEVICE *mpic; +#endif + + if (inb(0x0852) == 0xd5) { + /* This is for the 43p-140 */ + early_read_config_dword(0, 0, PCI_DEVFN(13, 0), + PCI_BASE_ADDRESS_0, &addr); + if (addr != 0xffffffff + && !(addr & PCI_BASE_ADDRESS_SPACE_IO) + && (addr &= PCI_BASE_ADDRESS_MEM_MASK) != 0) { + addr += PREP_ISA_MEM_BASE; + OpenPIC_Addr = ioremap(addr, 0x40000); + ppc_md.get_irq = openpic_get_irq; + } + } + +#ifdef CONFIG_PREP_RESIDUAL + mpic = residual_find_device(-1, NULL, SystemPeripheral, + ProgrammableInterruptController, MPIC, 0); + if (mpic != NULL) + printk("mpic = %p\n", mpic); +#endif +} + +static void __init +ibm43p_pci_map_non0(struct pci_dev *dev) +{ + unsigned char intpin; + static unsigned char bridge_intrs[4] = { 3, 4, 5, 8 }; + + if (dev == NULL) + return; + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &intpin); + if (intpin < 1 || intpin > 4) + return; + intpin = (PCI_SLOT(dev->devfn) + intpin - 1) & 3; + dev->irq = openpic_to_irq(bridge_intrs[intpin]); + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); +} + +void __init +prep_route_pci_interrupts(void) +{ + unsigned char *ibc_pirq = (unsigned char *)0x80800860; + unsigned char *ibc_pcicon = (unsigned char *)0x80800840; + int i; + + if ( _prep_type == _PREP_Motorola) + { + unsigned short irq_mode; + unsigned char cpu_type; + unsigned char base_mod; + int entry; + + cpu_type = inb(MOTOROLA_CPUTYPE_REG) & 0xF0; + base_mod = inb(MOTOROLA_BASETYPE_REG); + + for (entry = 0; mot_info[entry].cpu_type != 0; entry++) { + if (mot_info[entry].cpu_type & 0x200) { /* Check for Hawk chip */ + if (!(MotMPIC & MOT_HAWK_PRESENT)) + continue; + } else { /* Check non hawk boards */ + if ((mot_info[entry].cpu_type & 0xff) != cpu_type) + continue; + + if (mot_info[entry].base_type == 0) { + mot_entry = entry; + break; + } + + if (mot_info[entry].base_type != base_mod) + continue; + } + + if (!(mot_info[entry].max_cpu & 0x80)) { + mot_entry = entry; + break; + } + + /* processor 1 not present and max processor zero indicated */ + if ((*ProcInfo & MOT_PROC2_BIT) && !(mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } + + /* processor 1 present and max processor zero indicated */ + if (!(*ProcInfo & MOT_PROC2_BIT) && (mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } + } + + if (mot_entry == -1) /* No particular cpu type found - assume Blackhawk */ + mot_entry = 3; + + Motherboard_map_name = (unsigned char *)mot_info[mot_entry].name; + Motherboard_map = mot_info[mot_entry].map; + Motherboard_routes = mot_info[mot_entry].routes; + Motherboard_non0 = mot_info[mot_entry].map_non0_bus; + + if (!(mot_info[entry].cpu_type & 0x100)) { + /* AJF adjust level/edge control according to routes */ + irq_mode = 0; + for (i = 1; i <= 4; i++) + irq_mode |= ( 1 << Motherboard_routes[i] ); + outb( irq_mode & 0xff, 0x4d0 ); + outb( (irq_mode >> 8) & 0xff, 0x4d1 ); + } + } else if ( _prep_type == _PREP_IBM ) { + unsigned char planar_id = inb(0x0852); + unsigned char irq_edge_mask_lo, irq_edge_mask_hi; + + printk("IBM ID: %08x\n", planar_id); + switch(planar_id) { + case 0xff: + Motherboard_map_name = "IBM Thinkpad 850/860"; + Motherboard_map = Nobis_pci_IRQ_map; + Motherboard_routes = Nobis_pci_IRQ_routes; + irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ + irq_edge_mask_hi = 0xA0; /* irq's 13, 15 level-triggered */ + break; + case 0xfc: + Motherboard_map_name = "IBM 6015/7020 (Sandalfoot/Sandalbow)"; + Motherboard_map = ibm6015_pci_IRQ_map; + Motherboard_routes = ibm6015_pci_IRQ_routes; + irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ + irq_edge_mask_hi = 0xA0; /* irq's 13, 15 level-triggered */ + break; + case 0xd5: + Motherboard_map_name = "IBM 43P-140 (Tiger1)"; + Motherboard_map = ibm43p_pci_IRQ_map; + Motherboard_routes = ibm43p_pci_IRQ_routes; + Motherboard_non0 = ibm43p_pci_map_non0; + irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ + irq_edge_mask_hi = 0xA0; /* irq's 13, 15 level-triggered */ + break; + default: + printk(KERN_ERR "Unknown IBM motherboard! Defaulting to Carolina.\n"); + case 0xf0: /* PowerSeries 830/850 */ + case 0xf1: /* PowerSeries 830/850 */ + case 0xf2: /* PowerSeries 830/850 */ + case 0xf4: /* 7248-43P */ + case 0xf5: /* 7248-43P */ + case 0xf6: /* 7248-43P */ + case 0xf7: /* 7248-43P (missing from Carolina Tech Spec) */ + Motherboard_map_name = "IBM PS830/PS850/7248 (Carolina)"; + Motherboard_map = ibm8xx_pci_IRQ_map; + Motherboard_routes = ibm8xx_pci_IRQ_routes; + irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ + irq_edge_mask_hi = 0xA4; /* irq's 10, 13, 15 level-triggered */ + break; + } + + outb(inb(0x04d0)|irq_edge_mask_lo, 0x04d0); /* primary 8259 */ + outb(inb(0x04d1)|irq_edge_mask_hi, 0x04d1); /* cascaded 8259 */ + } else { + printk("No known machine pci routing!\n"); + return; + } + + /* Set up mapping from slots */ + for (i = 1; i <= 4; i++) + ibc_pirq[i-1] = Motherboard_routes[i]; + /* Enable PCI interrupts */ + *ibc_pcicon |= 0x20; +} + +void __init +prep_pib_init(void) +{ + unsigned char reg; + unsigned short short_reg; + + struct pci_dev *dev = NULL; + + if (( _prep_type == _PREP_Motorola) && (OpenPIC_Addr)) { + /* + * Perform specific configuration for the Via Tech or + * or Winbond PCI-ISA-Bridge part. + */ + if ((dev = pci_find_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_1, dev))) { + /* + * PPCBUG does not set the enable bits + * for the IDE device. Force them on here. + */ + pci_read_config_byte(dev, 0x40, ®); + + reg |= 0x03; /* IDE: Chip Enable Bits */ + pci_write_config_byte(dev, 0x40, reg); + } + if ((dev = pci_find_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_2, + dev)) && (dev->devfn = 0x5a)) { + /* Force correct USB interrupt */ + dev->irq = 11; + pci_write_config_byte(dev, + PCI_INTERRUPT_LINE, + dev->irq); + } + if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_83C553, dev))) { + /* Clear PCI Interrupt Routing Control Register. */ + short_reg = 0x0000; + pci_write_config_word(dev, 0x44, short_reg); + if (OpenPIC_Addr){ + /* Route IDE interrupts to IRQ 14 */ + reg = 0xEE; + pci_write_config_byte(dev, 0x43, reg); + } + } + } + + if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_82C105, dev))){ + if (OpenPIC_Addr){ + /* + * Disable LEGIRQ mode so PCI INTS are routed + * directly to the 8259 and enable both channels + */ + pci_write_config_dword(dev, 0x40, 0x10ff0033); + + /* Force correct IDE interrupt */ + dev->irq = 14; + pci_write_config_byte(dev, + PCI_INTERRUPT_LINE, + dev->irq); + } else { + /* Enable LEGIRQ for PCI INT -> 8259 IRQ routing */ + pci_write_config_dword(dev, 0x40, 0x10ff08a1); + } + } +} + +static void __init +Powerplus_Map_Non0(struct pci_dev *dev) +{ + struct pci_bus *pbus; /* Parent bus structure pointer */ + struct pci_dev *tdev = dev; /* Temporary device structure */ + unsigned int devnum; /* Accumulated device number */ + unsigned char intline; /* Linux interrupt value */ + unsigned char intpin; /* PCI interrupt pin */ + + /* Check for valid PCI dev pointer */ + if (dev == NULL) return; + + /* Initialize bridge IDSEL variable */ + devnum = PCI_SLOT(tdev->devfn); + + /* Read the interrupt pin of the device and adjust for indexing */ + pcibios_read_config_byte(dev->bus->number, dev->devfn, + PCI_INTERRUPT_PIN, &intpin); + + /* If device doesn't request an interrupt, return */ + if ( (intpin < 1) || (intpin > 4) ) + return; + + intpin--; + + /* + * Walk up to bus 0, adjusting the interrupt pin for the standard + * PCI bus swizzle. + */ + do { + intpin = (prep_pci_intpins[devnum % 4][intpin]) - 1; + pbus = tdev->bus; /* up one level */ + tdev = pbus->self; + devnum = PCI_SLOT(tdev->devfn); + } while(tdev->bus->number); + + /* Use the primary interrupt inputs by default */ + intline = mot_info[mot_entry].pci_irq_list->primary[intpin]; + + /* + * If the board has secondary interrupt inputs, walk the bus and + * note the devfn of the bridge from bus 0. If it is the same as + * the devfn of the bus bridge with secondary inputs, use those. + * Otherwise, assume it's a PMC site and get the interrupt line + * value from the interrupt routing table. + */ + if (mot_info[mot_entry].secondary_bridge_devfn) { + pbus = dev->bus; + + while (pbus->primary != 0) + pbus = pbus->parent; + + if ((pbus->self)->devfn != 0xA0) { + if ((pbus->self)->devfn == mot_info[mot_entry].secondary_bridge_devfn) + intline = mot_info[mot_entry].pci_irq_list->secondary[intpin]; + else { + if ((char *)(mot_info[mot_entry].map) == (char *)Mesquite_pci_IRQ_map) + intline = mot_info[mot_entry].map[((pbus->self)->devfn)/8] + 16; + else { + int i; + for (i=0;i<3;i++) + intpin = (prep_pci_intpins[devnum % 4][intpin]) - 1; + intline = mot_info[mot_entry].pci_irq_list->primary[intpin]; + } + } + } + } + + /* Write calculated interrupt value to header and device list */ + dev->irq = intline; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, (u8)dev->irq); +} + +void __init +prep_pcibios_fixup(void) +{ + struct pci_dev *dev; + extern unsigned char *Motherboard_map; + extern unsigned char *Motherboard_routes; + unsigned char i; + + prep_route_pci_interrupts(); + + printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name); + if (OpenPIC_Addr) { + /* PCI interrupts are controlled by the OpenPIC */ + pci_for_each_dev(dev) { + if (dev->bus->number == 0) { + dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); + pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_LINE, dev->irq); + } else { + if (Motherboard_non0 != NULL) + Motherboard_non0(dev); + } + } + + /* Setup the Winbond or Via PIB */ + prep_pib_init(); + + return; + } + + pci_for_each_dev(dev) { + /* + * Use our old hard-coded kludge to figure out what + * irq this device uses. This is necessary on things + * without residual data. -- Cort + */ + unsigned char d = PCI_SLOT(dev->devfn); + dev->irq = Motherboard_routes[Motherboard_map[d]]; + + for ( i = 0 ; i <= 5 ; i++ ) { + /* + * Relocate PCI I/O resources if necessary so the + * standard 256MB BAT covers them. + */ + if ( (pci_resource_flags(dev, i) & IORESOURCE_IO) && + (dev->resource[i].start > 0x10000000)) { + printk("Relocating PCI address %lx -> %lx\n", + dev->resource[i].start, + (dev->resource[i].start & + 0x00FFFFFF)| 0x01000000); + dev->resource[i].start = + (dev->resource[i].start & 0x00FFFFFF) + | 0x01000000; + pci_write_config_dword(dev, + PCI_BASE_ADDRESS_0 + (i*0x4), + dev->resource[i].start); + dev->resource[i].end = + (dev->resource[i].end & 0x00FFFFFF) + | 0x01000000; + } + } +#if 0 + /* + * If we have residual data and if it knows about this + * device ask it what the irq is. + * -- Cort + */ + ppcd = residual_find_device_id( ~0L, dev->device, + -1,-1,-1, 0); +#endif + } +} + +static void __init +prep_pcibios_after_init(void) +{ +#if 0 + struct pci_dev *dev; + + /* If there is a WD 90C, reset the IO BAR to 0x0 (it started that + * way, but the PCI layer relocated it because it thought 0x0 was + * invalid for a BAR). + * If you don't do this, the card's VGA base will be +0xc0000 + * instead of 0xc0000. vgacon.c (for example) is completely unaware of + * this little quirk. + */ + dev = pci_find_device(PCI_VENDOR_ID_WD, PCI_DEVICE_ID_WD_90C, NULL); + if (dev) { + dev->resource[1].end -= dev->resource[1].start; + dev->resource[1].start = 0; + /* tell the hardware */ + pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0x0); + } +#endif +} + +static void __init +prep_init_resource(struct resource *res, unsigned long start, + unsigned long end, int flags) +{ + res->flags = flags; + res->start = start; + res->end = end; + res->name = "PCI host bridge"; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; +} + +void __init +prep_find_bridges(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = PREP_ISA_MEM_BASE; + hose->io_base_phys = PREP_ISA_IO_BASE; + hose->io_base_virt = (void *)0x80000000; /* see prep_map_io() */ + prep_init_resource(&hose->io_resource, 0, 0x00ffffff, IORESOURCE_IO); + prep_init_resource(&hose->mem_resources[0], 0xc0000000, 0xfeffffff, + IORESOURCE_MEM); + hose->ops = &prep_pci_ops; + + printk("PReP architecture\n"); +#ifdef CONFIG_PREP_RESIDUAL + { + PPC_DEVICE *hostbridge; + + hostbridge = residual_find_device(PROCESSORDEVICE, NULL, + BridgeController, PCIBridge, -1, 0); + if (hostbridge && + hostbridge->DeviceId.Interface == PCIBridgeIndirect) { + PnP_TAG_PACKET * pkt; + pkt = PnP_find_large_vendor_packet( + res->DevicePnPHeap+hostbridge->AllocatedOffset, + 3, 0); + if(pkt) { +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + setup_indirect_pci(hose, + ld_le32((unsigned *) (p.PPCData)), + ld_le32((unsigned *) (p.PPCData+8))); +#undef p + } else + setup_indirect_pci(hose, 0x80000cf8, 0x80000cfc); + } + } +#endif /* CONFIG_PREP_RESIDUAL */ + + ppc_md.pcibios_fixup = prep_pcibios_fixup; + ppc_md.pcibios_after_init = prep_pcibios_after_init; +} diff -Nru a/arch/ppc/platforms/prep_setup.c b/arch/ppc/platforms/prep_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prep_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,940 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/platforms/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * + * Support for PReP (Motorola MTX/MVME) + * by Troy Benjegerdes (hozer@drgw.net) + */ + +/* + * bootup setup stuff.. + */ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned char ucSystemType; +unsigned char ucBoardRev; +unsigned char ucBoardRevMaj, ucBoardRevMin; + +extern unsigned long mc146818_get_rtc_time(void); +extern int mc146818_set_rtc_time(unsigned long nowtime); +extern unsigned long mk48t59_get_rtc_time(void); +extern int mk48t59_set_rtc_time(unsigned long nowtime); + +extern unsigned char prep_nvram_read_val(int addr); +extern void prep_nvram_write_val(int addr, + unsigned char val); +extern unsigned char rs_nvram_read_val(int addr); +extern void rs_nvram_write_val(int addr, + unsigned char val); +extern void ibm_prep_init(void); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[]; + +extern void prep_find_bridges(void); +extern char saved_command_line[]; + +int _prep_type; + +#define cached_21 (((char *)(ppc_cached_irq_mask))[3]) +#define cached_A1 (((char *)(ppc_cached_irq_mask))[2]) + +/* for the mac fs */ +kdev_t boot_dev; + +#ifdef CONFIG_SOUND_CS4232 +long ppc_cs4232_dma, ppc_cs4232_dma2; +#endif + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern int probingmem; +extern unsigned long loops_per_jiffy; + +#ifdef CONFIG_SOUND_CS4232 +EXPORT_SYMBOL(ppc_cs4232_dma); +EXPORT_SYMBOL(ppc_cs4232_dma2); +#endif + +static int __prep +prep_show_cpuinfo(struct seq_file *m) +{ + extern char *Motherboard_map_name; + int cachew; +#ifdef CONFIG_PREP_RESIDUAL + int i; +#endif + + seq_printf(m, "machine\t\t: PReP %s\n", Motherboard_map_name); + + switch ( _prep_type ) { + case _PREP_IBM: + cachew = inw(0x80c); + if (cachew & (1<<6)) + seq_printf(m, "Upgrade CPU\n"); + seq_printf(m, "L2\t\t: "); + if (cachew & (1<<7)) { + seq_printf(m, "not present\n"); + goto no_l2; + } + seq_printf(m, "%sKb,", (cachew & (1 << 10))? "512" : "256"); + seq_printf(m, "%ssync\n", (cachew & (1 << 15))? "" : "a"); + break; + case _PREP_Motorola: + cachew = *((unsigned char *)CACHECRBA); + seq_printf(m, "L2\t\t: "); + switch (cachew & L2CACHE_MASK) { + case L2CACHE_512KB: + seq_printf(m, "512Kb"); + break; + case L2CACHE_256KB: + seq_printf(m, "256Kb"); + break; + case L2CACHE_1MB: + seq_printf(m, "1MB"); + break; + case L2CACHE_NONE: + seq_printf(m, "none\n"); + goto no_l2; + break; + default: + seq_printf(m, "%x\n", cachew); + } + + seq_printf(m, ", parity %s", + (cachew & L2CACHE_PARITY)? "enabled" : "disabled"); + + seq_printf(m, " SRAM:"); + + switch ( ((cachew & 0xf0) >> 4) & ~(0x3) ) { + case 1: seq_printf(m, "synchronous,parity,flow-through\n"); + break; + case 2: seq_printf(m, "asynchronous,no parity\n"); + break; + case 3: seq_printf(m, "asynchronous,parity\n"); + break; + default:seq_printf(m, "synchronous,pipelined,no parity\n"); + break; + } + break; + default: + break; + } + +no_l2: +#ifdef CONFIG_PREP_RESIDUAL + if (res->ResidualLength != 0) { + /* print info about SIMMs */ + seq_printf(m, "simms\t\t: "); + for (i = 0; (res->ActualNumMemories) && (i < MAX_MEMS); i++) { + if (res->Memories[i].SIMMSize != 0) + seq_printf(m, "%d:%ldM ", i, + (res->Memories[i].SIMMSize > 1024) ? + res->Memories[i].SIMMSize>>20 : + res->Memories[i].SIMMSize); + } + seq_printf(m, "\n"); + } +#endif + + return 0; +} + +static int __prep +prep_show_percpuinfo(struct seq_file *m, int i) +{ + /* PREP's without residual data will give incorrect values here */ + seq_printf(m, "clock\t\t: "); +#ifdef CONFIG_PREP_RESIDUAL + if (res->ResidualLength) + seq_printf(m, "%ldMHz\n", + (res->VitalProductData.ProcessorHz > 1024) ? + res->VitalProductData.ProcessorHz / 1000000 : + res->VitalProductData.ProcessorHz); + else +#endif /* CONFIG_PREP_RESIDUAL */ + seq_printf(m, "???\n"); + + return 0; +} + +#ifdef CONFIG_SOUND_CS4232 +static long __init masktoint(unsigned int i) +{ + int t = -1; + while (i >> ++t) + ; + return (t-1); +} + +/* + * ppc_cs4232_dma and ppc_cs4232_dma2 are used in include/asm/dma.h + * to distinguish sound dma-channels from others. This is because + * blocksize on 16 bit dma-channels 5,6,7 is 128k, but + * the cs4232.c uses 64k like on 8 bit dma-channels 0,1,2,3 + */ + +static void __init prep_init_sound(void) +{ + PPC_DEVICE *audiodevice = NULL; + + /* + * Get the needed resource informations from residual data. + * + */ +#ifdef CONFIG_PREP_RESIDUAL + audiodevice = residual_find_device(~0, NULL, MultimediaController, + AudioController, -1, 0); + if (audiodevice != NULL) { + PnP_TAG_PACKET *pkt; + + pkt = PnP_find_packet((unsigned char *)&res->DevicePnPHeap[audiodevice->AllocatedOffset], + S5_Packet, 0); + if (pkt != NULL) + ppc_cs4232_dma = masktoint(pkt->S5_Pack.DMAMask); + pkt = PnP_find_packet((unsigned char*)&res->DevicePnPHeap[audiodevice->AllocatedOffset], + S5_Packet, 1); + if (pkt != NULL) + ppc_cs4232_dma2 = masktoint(pkt->S5_Pack.DMAMask); + } +#endif + + /* + * These are the PReP specs' defaults for the cs4231. We use these + * as fallback incase we don't have residual data. + * At least the IBM Thinkpad 850 with IDE DMA Channels at 6 and 7 + * will use the other values. + */ + if (audiodevice == NULL) { + switch (_prep_type) { + case _PREP_IBM: + ppc_cs4232_dma = 1; + ppc_cs4232_dma2 = -1; + break; + default: + ppc_cs4232_dma = 6; + ppc_cs4232_dma2 = 7; + } + } + + /* + * Find a way to push these informations to the cs4232 driver + * Give it out with printk, when not in cmd_line? + * Append it to cmd_line and saved_command_line? + * Format is cs4232=io,irq,dma,dma2 + */ +} +#endif /* CONFIG_SOUND_CS4232 */ + +/* + * Fill out screen_info according to the residual data. This allows us to use + * at least vesafb. + */ +static void __init +prep_init_vesa(void) +{ +#if defined(CONFIG_PREP_RESIDUAL) && \ + (defined(CONFIG_FB_VGA16) || defined(CONFIG_FB_VGA_16_MODULE) || \ + defined(CONFIG_FB_VESA)) + PPC_DEVICE *vgadev; + + vgadev = residual_find_device(~0, NULL, DisplayController, SVGAController, + -1, 0); + if (vgadev != NULL) { + PnP_TAG_PACKET *pkt; + + pkt = PnP_find_large_vendor_packet( + (unsigned char *)&res->DevicePnPHeap[vgadev->AllocatedOffset], + 0x04, 0); /* 0x04 = Display Tag */ + if (pkt != NULL) { + unsigned char *ptr = (unsigned char *)pkt; + + if (ptr[4]) { + /* graphics mode */ + screen_info.orig_video_isVGA = VIDEO_TYPE_VLFB; + + screen_info.lfb_depth = ptr[4] * 8; + + screen_info.lfb_width = swab16(*(short *)(ptr+6)); + screen_info.lfb_height = swab16(*(short *)(ptr+8)); + screen_info.lfb_linelength = swab16(*(short *)(ptr+10)); + + screen_info.lfb_base = swab32(*(long *)(ptr+12)); + screen_info.lfb_size = swab32(*(long *)(ptr+20)) / 65536; + } + } + } +#endif /* CONFIG_PREP_RESIDUAL */ +} + +static void __init +prep_setup_arch(void) +{ + unsigned char reg; + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000; + + /* Lookup PCI host bridges */ + prep_find_bridges(); + + /* Set up floppy in PS/2 mode */ + outb(0x09, SIO_CONFIG_RA); + reg = inb(SIO_CONFIG_RD); + reg = (reg & 0x3F) | 0x40; + outb(reg, SIO_CONFIG_RD); + outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ + + /* we should determine this according to what we find! -- Cort */ + switch ( _prep_type ) + { + case _PREP_IBM: + /* Enable L2. Assume we don't need to flush -- Cort*/ + *(unsigned char *)(0x8000081c) |= 3; + ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ + break; + case _PREP_Motorola: + /* Enable L2. Assume we don't need to flush -- Cort*/ + *(unsigned char *)(0x8000081c) |= 3; +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00ff); /* /dev/nfs */ +#else + ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 */ +#endif + break; + } + + /* Read in NVRAM data */ + init_prep_nvram(); + + /* if no bootargs, look in NVRAM */ + if ( cmd_line[0] == '\0' ) { + char *bootargs; + bootargs = prep_nvram_get_var("bootargs"); + if (bootargs != NULL) { + strcpy(cmd_line, bootargs); + /* again.. */ + strcpy(saved_command_line, cmd_line); + } + } + +#ifdef CONFIG_SOUND_CS4232 + prep_init_sound(); +#endif /* CONFIG_SOUND_CS4232 */ + + prep_init_vesa(); + + switch (_prep_type) { + case _PREP_Motorola: + raven_init(); + break; + case _PREP_IBM: + ibm_prep_init(); + break; + } + +#ifdef CONFIG_VGA_CONSOLE + /* vgacon.c needs to know where we mapped IO memory in io_block_mapping() */ + vgacon_remap_base = 0xf0000000; + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +} + +/* + * Determine the decrementer frequency from the residual data + * This allows for a faster boot as we do not need to calibrate the + * decrementer against another clock. This is important for embedded systems. + */ +static int __init +prep_res_calibrate_decr(void) +{ +#ifdef CONFIG_PREP_RESIDUAL + unsigned long freq, divisor = 4; + + if ( res->VitalProductData.ProcessorBusHz ) { + freq = res->VitalProductData.ProcessorBusHz; + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + (freq/divisor)/1000000, + (freq/divisor)%1000000); + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); + tb_ticks_per_jiffy = freq / HZ / divisor; + return 0; + } else +#endif + return 1; +} + +/* + * Uses the on-board timer to calibrate the on-chip decrementer register + * for prep systems. On the pmac the OF tells us what the frequency is + * but on prep we have to figure it out. + * -- Cort + */ +/* Done with 3 interrupts: the first one primes the cache and the + * 2 following ones measure the interval. The precision of the method + * is still doubtful due to the short interval sampled. + */ +static volatile int calibrate_steps __initdata = 3; +static unsigned tbstamp __initdata = 0; + +static void __init +prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs *regs) +{ + unsigned long t, freq; + int step=--calibrate_steps; + + t = get_tbl(); + if (step > 0) { + tbstamp = t; + } else { + freq = (t - tbstamp)*HZ; + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + } +} + +static void __init +prep_calibrate_decr(void) +{ + int res; + + /* Try and get this from the residual data. */ + res = prep_res_calibrate_decr(); + + /* If we didn't get it from the residual data, try this. */ + if ( res ) { + unsigned long flags; + + save_flags(flags); + +#define TIMER0_COUNT 0x40 +#define TIMER_CONTROL 0x43 + /* set timer to periodic mode */ + outb_p(0x34,TIMER_CONTROL);/* binary, mode 2, LSB/MSB, ch 0 */ + /* set the clock to ~100 Hz */ + outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */ + outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */ + + if (request_irq(0, prep_calibrate_decr_handler, 0, "timer", NULL) != 0) + panic("Could not allocate timer IRQ!"); + __sti(); + /* wait for calibrate */ + while ( calibrate_steps ) + ; + restore_flags(flags); + free_irq( 0, NULL); + } +} + +static long __init +mk48t59_init(void) { + unsigned char tmp; + + tmp = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLB); + if (tmp & MK48T59_RTC_CB_STOP) { + printk("Warning: RTC was stopped, date will be wrong.\n"); + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLB, + tmp & ~MK48T59_RTC_CB_STOP); + /* Low frequency crystal oscillators may take a very long + * time to startup and stabilize. For now just ignore the + * the issue, but attempting to calibrate the decrementer + * from the RTC just after this wakeup is likely to be very + * inaccurate. Firmware should not allow to load + * the OS with the clock stopped anyway... + */ + } + /* Ensure that the clock registers are updated */ + tmp = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + tmp &= ~(MK48T59_RTC_CA_READ | MK48T59_RTC_CA_WRITE); + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, tmp); + return 0; +} + +/* We use the NVRAM RTC to time a second to calibrate the decrementer, + * the RTC registers have just been set up in the right state by the + * preceding routine. + */ +static void __init +mk48t59_calibrate_decr(void) +{ + unsigned long freq; + unsigned long t1; + unsigned char save_control; + long i; + unsigned char sec; + + + /* Make sure the time is not stopped. */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLB); + + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control & (~MK48T59_RTC_CB_STOP))); + + /* Now make sure the read bit is off so the value will change. */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + save_control &= ~MK48T59_RTC_CA_READ; + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + + /* Read the seconds value to see when it changes. */ + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + /* Actually this is bad for precision, we should have a loop in + * which we only read the seconds counter. nvram_read_val writes + * the address bytes on every call and this takes a lot of time. + * Perhaps an nvram_wait_change method returning a time + * stamp with a loop count as parameter would be the solution. + */ + for (i = 0 ; i < 1000000 ; i++) { /* may take up to 1 second... */ + t1 = get_tbl(); + if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { + break; + } + } + + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + for (i = 0 ; i < 1000000 ; i++) { /* Should take up 1 second... */ + freq = get_tbl()-t1; + if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) + break; + } + + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); +} + +static void __prep +prep_restart(char *cmd) +{ + unsigned long i = 10000; + + __cli(); + + /* set exception prefix high - to the prom */ + _nmask_and_or_msr(0, MSR_IP); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( i != 0 ) i++; + panic("restart failed\n"); +} + +static void __prep +prep_halt(void) +{ + unsigned long flags; + __cli(); + /* set exception prefix high - to the prom */ + save_flags( flags ); + restore_flags( flags|MSR_IP ); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( 1 ) ; + /* + * Not reached + */ +} + +/* + * On IBM PReP's, power management is handled by a Signetics 87c750 behind the + * Utah component on the ISA bus. To access the 750 you must write a series of + * nibbles to port 0x82a (decoded by the Utah). This is described somewhat in + * the IBM Carolina Technical Specification. + * -Hollis + */ +static void __prep +utah_sig87c750_setbit(unsigned int bytenum, unsigned int bitnum, int value) +{ + /* + * byte1: 0 0 0 1 0 d a5 a4 + * byte2: 0 0 0 1 a3 a2 a1 a0 + * + * d = the bit's value, enabled or disabled + * (a5 a4 a3) = the byte number, minus 20 + * (a2 a1 a0) = the bit number + * + * example: set the 5th bit of byte 21 (21.5) + * a5 a4 a3 = 001 (byte 1) + * a2 a1 a0 = 101 (bit 5) + * + * byte1 = 0001 0100 (0x14) + * byte2 = 0001 1101 (0x1d) + */ + unsigned char byte1=0x10, byte2=0x10; + const unsigned int pm_reg_1=0x82a; /* ISA address */ + + /* the 750's '20.0' is accessed as '0.0' through Utah (which adds 20) */ + bytenum -= 20; + + byte1 |= (!!value) << 2; /* set d */ + byte1 |= (bytenum >> 1) & 0x3; /* set a5, a4 */ + + byte2 |= (bytenum & 0x1) << 3; /* set a3 */ + byte2 |= bitnum & 0x7; /* set a2, a1, a0 */ + + outb(byte1, pm_reg_1); /* first nibble */ + mb(); + udelay(100); /* important: let controller recover */ + + outb(byte2, pm_reg_1); /* second nibble */ + mb(); + udelay(100); /* important: let controller recover */ +} + +static void __prep +prep_power_off(void) +{ + if ( _prep_type == _PREP_IBM) { + unsigned long flags; + __cli(); + /* set exception prefix high - to the prom */ + save_flags( flags ); + restore_flags( flags|MSR_IP ); + + utah_sig87c750_setbit(21, 5, 1); /* set bit 21.5, "PMEXEC_OFF" */ + + while ( 1 ) ; + /* not reached */ + } else { + prep_halt(); + } +} + +static unsigned int __prep +prep_irq_cannonicalize(u_int irq) +{ + if (irq == 2) + { + return 9; + } + else + { + return irq; + } +} + +static int __prep +prep_get_irq(struct pt_regs *regs) +{ + return i8259_irq(); +} + +static void __init +prep_init_IRQ(void) +{ + int i; + + if (OpenPIC_Addr != NULL) + openpic_init(1, NUM_8259_INTERRUPTS, 0, -1); + for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ ) + irq_desc[i].handler = &i8259_pic; + i8259_init(0xbffffff0); /* PCI interrupt ack address for MPC105 and 106 */ +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +static int __prep +prep_ide_default_irq(ide_ioreg_t base) +{ + switch (base) { + case 0x1f0: return 13; + case 0x170: return 13; + case 0x1e8: return 11; + case 0x168: return 10; + case 0xfff0: return 14; /* MCP(N)750 ide0 */ + case 0xffe0: return 15; /* MCP(N)750 ide1 */ + default: return 0; + } +} + +static ide_ioreg_t __prep +prep_ide_default_io_base(int index) +{ + switch (index) { + case 0: return 0x1f0; + case 1: return 0x170; + case 2: return 0x1e8; + case 3: return 0x168; + default: + return 0; + } +} + +static int __prep +prep_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +static void __prep +prep_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); +} + +static void __prep +prep_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); +} +#endif + +#ifdef CONFIG_SMP +/* PReP (MTX) support */ +static int __init +smp_prep_probe(void) +{ + extern int mot_multi; + + if (mot_multi) { + openpic_request_IPIs(); + smp_hw_index[1] = 1; + return 2; + } + + return 1; +} + +static void __init +smp_prep_kick_cpu(int nr) +{ + *(unsigned long *)KERNELBASE = nr; + asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); + printk("CPU1 reset, waiting\n"); +} + +static void __init +smp_prep_setup_cpu(int cpu_nr) +{ + if (OpenPIC_Addr) + do_openpic_setup_cpu(); +} + +static struct smp_ops_t prep_smp_ops __prepdata = { + smp_openpic_message_pass, + smp_prep_probe, + smp_prep_kick_cpu, + smp_prep_setup_cpu, +}; +#endif /* CONFIG_SMP */ + +/* + * This finds the amount of physical ram and does necessary + * setup for prep. This is pretty architecture specific so + * this will likely stay separate from the pmac. + * -- Cort + */ +static unsigned long __init +prep_find_end_of_memory(void) +{ + unsigned long total = 0; + extern unsigned int boot_mem_size; + +#ifdef CONFIG_PREP_RESIDUAL + total = res->TotalMemory; +#endif + + if (total == 0 && boot_mem_size != 0) + total = boot_mem_size; + else if (total == 0) { + /* + * I need a way to probe the amount of memory if the residual + * data doesn't contain it. -- Cort + */ + total = 0x02000000; + printk(KERN_INFO "Ramsize from residual data was 0" + " -- defaulting to %ldM\n", total>>20); + } + + return (total); +} + +/* + * Setup the bat mappings we're going to load that cover + * the io areas. RAM was mapped by mapin_ram(). + * -- Cort + */ +static void __init +prep_map_io(void) +{ + io_block_mapping(0x80000000, PREP_ISA_IO_BASE, 0x10000000, _PAGE_IO); + io_block_mapping(0xf0000000, PREP_ISA_MEM_BASE, 0x08000000, _PAGE_IO); +} + +static void __init +prep_init2(void) +{ +#ifdef CONFIG_NVRAM + request_region(PREP_NVRAM_AS0, 0x8, "nvram"); +#endif + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); +} + +void __init +prep_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#ifdef CONFIG_PREP_RESIDUAL + /* make a copy of residual data */ + if ( r3 ) { + memcpy((void *)res,(void *)(r3+KERNELBASE), + sizeof(RESIDUAL)); + } +#endif + + isa_io_base = PREP_ISA_IO_BASE; + isa_mem_base = PREP_ISA_MEM_BASE; + pci_dram_offset = PREP_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + /* figure out what kind of prep workstation we are */ +#ifdef CONFIG_PREP_RESIDUAL + if ( res->ResidualLength != 0 ) { + if ( !strncmp(res->VitalProductData.PrintableModel,"IBM",3) ) + _prep_type = _PREP_IBM; + else + _prep_type = _PREP_Motorola; + } else /* assume motorola if no residual (netboot?) */ +#endif + { + _prep_type = _PREP_Motorola; + } + + ppc_md.setup_arch = prep_setup_arch; + ppc_md.show_percpuinfo = prep_show_percpuinfo; + ppc_md.show_cpuinfo = prep_show_cpuinfo; + ppc_md.irq_cannonicalize = prep_irq_cannonicalize; + ppc_md.init_IRQ = prep_init_IRQ; + /* this gets changed later on if we have an OpenPIC -- Cort */ + ppc_md.get_irq = prep_get_irq; + ppc_md.init = prep_init2; + + ppc_md.restart = prep_restart; + ppc_md.power_off = prep_power_off; + ppc_md.halt = prep_halt; + + ppc_md.nvram_read_val = prep_nvram_read_val; + ppc_md.nvram_write_val = prep_nvram_write_val; + + ppc_md.time_init = NULL; + if (_prep_type == _PREP_IBM) { + ppc_md.set_rtc_time = mc146818_set_rtc_time; + ppc_md.get_rtc_time = mc146818_get_rtc_time; + ppc_md.calibrate_decr = prep_calibrate_decr; + } else { + ppc_md.set_rtc_time = mk48t59_set_rtc_time; + ppc_md.get_rtc_time = mk48t59_get_rtc_time; + ppc_md.calibrate_decr = mk48t59_calibrate_decr; + ppc_md.time_init = mk48t59_init; + } + + ppc_md.find_end_of_memory = prep_find_end_of_memory; + ppc_md.setup_io_mappings = prep_map_io; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_irq = prep_ide_default_irq; + ppc_ide_md.default_io_base = prep_ide_default_io_base; + ppc_ide_md.ide_check_region = prep_ide_check_region; + ppc_ide_md.ide_request_region = prep_ide_request_region; + ppc_ide_md.ide_release_region = prep_ide_release_region; +#endif + +#ifdef CONFIG_VT + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x54; +#endif +#endif + +#ifdef CONFIG_SMP + ppc_md.smp_ops = &prep_smp_ops; +#endif /* CONFIG_SMP */ +} diff -Nru a/arch/ppc/platforms/prep_time.c b/arch/ppc/platforms/prep_time.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prep_time.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,228 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/platforms/prep_time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * Adapted for PowerPC (PReP) by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu). + * Copied and modified from arch/i386/kernel/time.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +extern spinlock_t rtc_lock; + +/* + * The motorola uses the m48t18 rtc (includes DS1643) whose registers + * are at a higher end of nvram (1ff8-1fff) than the ibm mc146818 + * rtc (ds1386) which has regs at addr 0-d). The intel gets + * past this because the bios emulates the mc146818. + * + * Why in the world did they have to use different clocks? + * + * Right now things are hacked to check which machine we're on then + * use the appropriate macro. This is very very ugly and I should + * probably have a function that checks which machine we're on then + * does things correctly transparently or a function pointer which + * is setup at boot time to use the correct addresses. + * -- Cort + */ + +/* + * Set the hardware clock. -- Cort + */ +__prep +int mc146818_set_rtc_time(unsigned long nowtime) +{ + unsigned char save_control, save_freq_select; + struct rtc_time tm; + + spin_lock(&rtc_lock); + to_tm(nowtime, &tm); + + /* tell the clock it's being set */ + save_control = CMOS_READ(RTC_CONTROL); + + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + + /* stop and reset prescaler */ + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); + + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year = (tm.tm_year - 1900) % 100; + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + } + CMOS_WRITE(tm.tm_sec, RTC_SECONDS); + CMOS_WRITE(tm.tm_min, RTC_MINUTES); + CMOS_WRITE(tm.tm_hour, RTC_HOURS); + CMOS_WRITE(tm.tm_mon, RTC_MONTH); + CMOS_WRITE(tm.tm_mday, RTC_DAY_OF_MONTH); + CMOS_WRITE(tm.tm_year, RTC_YEAR); + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + spin_unlock(&rtc_lock); + + return 0; +} + +__prep +unsigned long mc146818_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int uip, i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + + /* Since the UIP flag is set for about 2.2 ms and the clock + * is typically written with a precision of 1 jiffy, trying + * to obtain a precision better than a few milliseconds is + * an illusion. Only consistency is interesting, this also + * allows to use the routine for /dev/rtc without a potential + * 1 second kernel busy loop triggered by any reader of /dev/rtc. + */ + + for ( i = 0; i<1000000; i++) { + uip = CMOS_READ(RTC_FREQ_SELECT); + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + uip |= CMOS_READ(RTC_FREQ_SELECT); + if ((uip & RTC_UIP)==0) break; + } + + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) + || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + +__prep +int mk48t59_set_rtc_time(unsigned long nowtime) +{ + unsigned char save_control; + struct rtc_time tm; + + spin_lock(&rtc_lock); + to_tm(nowtime, &tm); + + /* tell the clock it's being written */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control | MK48T59_RTC_CA_WRITE)); + + tm.tm_year = (tm.tm_year - 1900) % 100; + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + + ppc_md.nvram_write_val(MK48T59_RTC_SECONDS, tm.tm_sec); + ppc_md.nvram_write_val(MK48T59_RTC_MINUTES, tm.tm_min); + ppc_md.nvram_write_val(MK48T59_RTC_HOURS, tm.tm_hour); + ppc_md.nvram_write_val(MK48T59_RTC_MONTH, tm.tm_mon); + ppc_md.nvram_write_val(MK48T59_RTC_DAY_OF_MONTH, tm.tm_mday); + ppc_md.nvram_write_val(MK48T59_RTC_YEAR, tm.tm_year); + + /* Turn off the write bit. */ + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + spin_unlock(&rtc_lock); + + return 0; +} + +__prep +unsigned long mk48t59_get_rtc_time(void) +{ + unsigned char save_control; + unsigned int year, mon, day, hour, min, sec; + + /* Simple: freeze the clock, read it and allow updates again */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + save_control &= ~MK48T59_RTC_CA_READ; + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + /* Set the register to read the value. */ + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control | MK48T59_RTC_CA_READ)); + + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + min = ppc_md.nvram_read_val(MK48T59_RTC_MINUTES); + hour = ppc_md.nvram_read_val(MK48T59_RTC_HOURS); + day = ppc_md.nvram_read_val(MK48T59_RTC_DAY_OF_MONTH); + mon = ppc_md.nvram_read_val(MK48T59_RTC_MONTH); + year = ppc_md.nvram_read_val(MK48T59_RTC_YEAR); + + /* Let the time values change again. */ + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + + year = year + 1900; + if (year < 1970) { + year += 100; + } + + return mktime(year, mon, day, hour, min, sec); +} diff -Nru a/arch/ppc/platforms/proc_rtas.c b/arch/ppc/platforms/proc_rtas.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/proc_rtas.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,787 @@ +/* + * BK Id: SCCS/s.proc_rtas.c 1.5 05/17/01 18:14:22 cort + */ +/* + * arch/ppc/kernel/proc_rtas.c + * Copyright (C) 2000 Tilmann Bitterberg + * (tilmann@bitterberg.de) + * + * RTAS (Runtime Abstraction Services) stuff + * Intention is to provide a clean user interface + * to use the RTAS. + * + * TODO: + * Split off a header file and maybe move it to a different + * location. Write Documentation on what the /proc/rtas/ entries + * actually do. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include /* for ppc_md */ +#include + +/* Token for Sensors */ +#define KEY_SWITCH 0x0001 +#define ENCLOSURE_SWITCH 0x0002 +#define THERMAL_SENSOR 0x0003 +#define LID_STATUS 0x0004 +#define POWER_SOURCE 0x0005 +#define BATTERY_VOLTAGE 0x0006 +#define BATTERY_REMAINING 0x0007 +#define BATTERY_PERCENTAGE 0x0008 +#define EPOW_SENSOR 0x0009 +#define BATTERY_CYCLESTATE 0x000a +#define BATTERY_CHARGING 0x000b + +/* IBM specific sensors */ +#define IBM_SURVEILLANCE 0x2328 /* 9000 */ +#define IBM_FANRPM 0x2329 /* 9001 */ +#define IBM_VOLTAGE 0x232a /* 9002 */ +#define IBM_DRCONNECTOR 0x232b /* 9003 */ +#define IBM_POWERSUPPLY 0x232c /* 9004 */ +#define IBM_INTQUEUE 0x232d /* 9005 */ + +/* Status return values */ +#define SENSOR_CRITICAL_HIGH 13 +#define SENSOR_WARNING_HIGH 12 +#define SENSOR_NORMAL 11 +#define SENSOR_WARNING_LOW 10 +#define SENSOR_CRITICAL_LOW 9 +#define SENSOR_SUCCESS 0 +#define SENSOR_HW_ERROR -1 +#define SENSOR_BUSY -2 +#define SENSOR_NOT_EXIST -3 +#define SENSOR_DR_ENTITY -9000 + +/* Location Codes */ +#define LOC_SCSI_DEV_ADDR 'A' +#define LOC_SCSI_DEV_LOC 'B' +#define LOC_CPU 'C' +#define LOC_DISKETTE 'D' +#define LOC_ETHERNET 'E' +#define LOC_FAN 'F' +#define LOC_GRAPHICS 'G' +/* reserved / not used 'H' */ +#define LOC_IO_ADAPTER 'I' +/* reserved / not used 'J' */ +#define LOC_KEYBOARD 'K' +#define LOC_LCD 'L' +#define LOC_MEMORY 'M' +#define LOC_NV_MEMORY 'N' +#define LOC_MOUSE 'O' +#define LOC_PLANAR 'P' +#define LOC_OTHER_IO 'Q' +#define LOC_PARALLEL 'R' +#define LOC_SERIAL 'S' +#define LOC_DEAD_RING 'T' +#define LOC_RACKMOUNTED 'U' /* for _u_nit is rack mounted */ +#define LOC_VOLTAGE 'V' +#define LOC_SWITCH_ADAPTER 'W' +#define LOC_OTHER 'X' +#define LOC_FIRMWARE 'Y' +#define LOC_SCSI 'Z' + +/* Tokens for indicators */ +#define TONE_FREQUENCY 0x0001 /* 0 - 1000 (HZ)*/ +#define TONE_VOLUME 0x0002 /* 0 - 100 (%) */ +#define SYSTEM_POWER_STATE 0x0003 +#define WARNING_LIGHT 0x0004 +#define DISK_ACTIVITY_LIGHT 0x0005 +#define HEX_DISPLAY_UNIT 0x0006 +#define BATTERY_WARNING_TIME 0x0007 +#define CONDITION_CYCLE_REQUEST 0x0008 +#define SURVEILLANCE_INDICATOR 0x2328 /* 9000 */ +#define DR_ACTION 0x2329 /* 9001 */ +#define DR_INDICATOR 0x232a /* 9002 */ +/* 9003 - 9004: Vendor specific */ +#define GLOBAL_INTERRUPT_QUEUE 0x232d /* 9005 */ +/* 9006 - 9999: Vendor specific */ + +/* other */ +#define MAX_SENSORS 17 /* I only know of 17 sensors */ +#define MAX_LINELENGTH 256 +#define SENSOR_PREFIX "ibm,sensor-" +#define cel_to_fahr(x) ((x*9/5)+32) + + +/* Globals */ +static struct proc_dir_entry *proc_rtas; +static struct rtas_sensors sensors; +static struct device_node *rtas; +static unsigned long power_on_time = 0; /* Save the time the user set */ +static char progress_led[MAX_LINELENGTH]; + +static unsigned long rtas_tone_frequency = 1000; +static unsigned long rtas_tone_volume = 0; + +/* ****************STRUCTS******************************************* */ +struct individual_sensor { + unsigned int token; + unsigned int quant; +}; + +struct rtas_sensors { + struct individual_sensor sensor[MAX_SENSORS]; + unsigned int quant; +}; + +/* ****************************************************************** */ +/* Declarations */ +static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, + int count, int *eof, void *data); +static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); + +static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, + size_t count, loff_t *ppos); + +struct file_operations ppc_rtas_poweron_operations = { + read: ppc_rtas_poweron_read, + write: ppc_rtas_poweron_write +}; +struct file_operations ppc_rtas_progress_operations = { + read: ppc_rtas_progress_read, + write: ppc_rtas_progress_write +}; + +struct file_operations ppc_rtas_clock_operations = { + read: ppc_rtas_clock_read, + write: ppc_rtas_clock_write +}; + +struct file_operations ppc_rtas_tone_freq_operations = { + read: ppc_rtas_tone_freq_read, + write: ppc_rtas_tone_freq_write +}; +struct file_operations ppc_rtas_tone_volume_operations = { + read: ppc_rtas_tone_volume_read, + write: ppc_rtas_tone_volume_write +}; + +int ppc_rtas_find_all_sensors (void); +int ppc_rtas_process_sensor(struct individual_sensor s, int state, + int error, char * buf); +char * ppc_rtas_process_error(int error); +int get_location_code(struct individual_sensor s, char * buf); +int check_location_string (char *c, char * buf); +int check_location (char *c, int idx, char * buf); + +/* ****************************************************************** */ +/* MAIN */ +/* ****************************************************************** */ +void proc_rtas_init(void) +{ + struct proc_dir_entry *entry; + + rtas = find_devices("rtas"); + if ((rtas == 0) || (_machine != _MACH_chrp)) { + return; + } + + proc_rtas = proc_mkdir("rtas", 0); + if (proc_rtas == 0) + return; + + /* /proc/rtas entries */ + + entry = create_proc_entry("progress", S_IRUGO|S_IWUSR, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_progress_operations; + + entry = create_proc_entry("clock", S_IRUGO|S_IWUSR, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_clock_operations; + + entry = create_proc_entry("poweron", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_poweron_operations; + + create_proc_read_entry("sensors", S_IRUGO, proc_rtas, + ppc_rtas_sensor_read, NULL); + + entry = create_proc_entry("frequency", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_tone_freq_operations; + + entry = create_proc_entry("volume", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_tone_volume_operations; +} + +/* ****************************************************************** */ +/* POWER-ON-TIME */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + struct rtc_time tm; + unsigned long nowtime; + char *dest; + int error; + + nowtime = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_poweron_write: Invalid time\n"); + return count; + } + power_on_time = nowtime; /* save the time */ + + to_tm(nowtime, &tm); + + error = call_rtas("set-time-for-power-on", 7, 1, NULL, + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, 0 /* nano */); + if (error != 0) + printk(KERN_WARNING "error: setting poweron time returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + if (power_on_time == 0) + n = sprintf(buf, "Power on time not set\n"); + else + n = sprintf(buf, "%lu\n", power_on_time); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* PROGRESS */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long hex; + + strcpy(progress_led, buf); /* save the string */ + /* Lets see if the user passed hexdigits */ + hex = simple_strtoul(buf, NULL, 10); + + ppc_md.progress ((char *)buf, hex); + return count; + + /* clear the line */ /* ppc_md.progress(" ", 0xffff);*/ +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n = 0; + if (progress_led != NULL) + n = sprintf (buf, "%s\n", progress_led); + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* CLOCK */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + struct rtc_time tm; + unsigned long nowtime; + char *dest; + int error; + + nowtime = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_clock_write: Invalid time\n"); + return count; + } + + to_tm(nowtime, &tm); + error = call_rtas("set-time-of-day", 7, 1, NULL, + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, 0); + if (error != 0) + printk(KERN_WARNING "error: setting the clock returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + unsigned int year, mon, day, hour, min, sec; + unsigned long *ret = kmalloc(4*8, GFP_KERNEL); + int n, error; + + error = call_rtas("get-time-of-day", 0, 8, ret); + + year = ret[0]; mon = ret[1]; day = ret[2]; + hour = ret[3]; min = ret[4]; sec = ret[5]; + + if (error != 0){ + printk(KERN_WARNING "error: reading the clock returned: %s\n", + ppc_rtas_process_error(error)); + n = sprintf (buf, "0"); + } else { + n = sprintf (buf, "%lu\n", mktime(year, mon, day, hour, min, sec)); + } + kfree(ret); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* SENSOR STUFF */ +/* ****************************************************************** */ +static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, + int count, int *eof, void *data) +{ + int i,j,n; + unsigned long ret; + int state, error; + char buffer[MAX_LINELENGTH*MAX_SENSORS]; /* May not be enough */ + + if (count < 0) + return -EINVAL; + + n = sprintf ( buffer , "RTAS (RunTime Abstraction Services) Sensor Information\n"); + n += sprintf ( buffer+n, "Sensor\t\tValue\t\tCondition\tLocation\n"); + n += sprintf ( buffer+n, "********************************************************\n"); + + if (ppc_rtas_find_all_sensors() != 0) { + n += sprintf ( buffer+n, "\nNo sensors are available\n"); + goto return_string; + } + + for (i=0; i= 0) { + error = call_rtas("get-sensor-state", 2, 2, &ret, + sensors.sensor[i].token, sensors.sensor[i].quant-j); + state = (int) ret; + n += ppc_rtas_process_sensor(sensors.sensor[i], state, error, buffer+n ); + n += sprintf (buffer+n, "\n"); + j--; + } /* while */ + } /* for */ + +return_string: + if (off >= strlen(buffer)) { + *eof = 1; + return 0; + } + if (n > strlen(buffer) - off) + n = strlen(buffer) - off; + if (n > count) + n = count; + else + *eof = 1; + memcpy(buf, buffer + off, n); + *start = buf; + return n; +} + +/* ****************************************************************** */ + +int ppc_rtas_find_all_sensors (void) +{ + unsigned long *utmp; + int len, i, j; + + utmp = (unsigned long *) get_property(rtas, "rtas-sensors", &len); + if (utmp == NULL) { + printk (KERN_ERR "error: could not get rtas-sensors\n"); + return 1; + } + + sensors.quant = len / 8; /* int + int */ + + for (i=0, j=0; j= llen) pos=0; + } + return n; +} +/* ****************************************************************** */ +/* INDICATORS - Tone Frequency */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long freq; + char *dest; + int error; + freq = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_tone_freq_write: Invalid tone freqency\n"); + return count; + } + if (freq < 0) freq = 0; + rtas_tone_frequency = freq; /* save it for later */ + error = call_rtas("set-indicator", 3, 1, NULL, + TONE_FREQUENCY, 0, freq); + if (error != 0) + printk(KERN_WARNING "error: setting tone frequency returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + n = sprintf(buf, "%lu\n", rtas_tone_frequency); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} +/* ****************************************************************** */ +/* INDICATORS - Tone Volume */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long volume; + char *dest; + int error; + volume = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_tone_volume_write: Invalid tone volume\n"); + return count; + } + if (volume < 0) volume = 0; + if (volume > 100) volume = 100; + + rtas_tone_volume = volume; /* save it for later */ + error = call_rtas("set-indicator", 3, 1, NULL, + TONE_VOLUME, 0, volume); + if (error != 0) + printk(KERN_WARNING "error: setting tone volume returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + n = sprintf(buf, "%lu\n", rtas_tone_volume); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} diff -Nru a/arch/ppc/platforms/prpmc750.h b/arch/ppc/platforms/prpmc750.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prpmc750.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,65 @@ +/* + * include/asm-ppc/platforms/prpmc750.h + * + * Definitions for Motorola PrPMC750 board support + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_PRPMC750_H__ +#define __ASM_PRPMC750_H__ + +#include + +#define PRPMC750_PCI_CONFIG_ADDR 0x80000cf8 +#define PRPMC750_PCI_CONFIG_DATA 0x80000cfc + +#define PRPMC750_PCI_PHY_MEM_BASE 0xc0000000 +#define PRPMC750_PCI_MEM_BASE 0xf0000000 +#define PRPMC750_PCI_IO_BASE 0x80000000 + +#define PRPMC750_ISA_IO_BASE PRPMC750_PCI_IO_BASE +#define PRPMC750_ISA_MEM_BASE PRPMC750_PCI_MEM_BASE +#define PRPMC750_PCI_MEM_OFFSET PRPMC750_PCI_PHY_MEM_BASE + +#define PRPMC750_SYS_MEM_BASE 0x80000000 + +#define PRPMC750_PCI_LOWER_MEM 0x00000000 +#define PRPMC750_PCI_UPPER_MEM_AUTO 0x3bf7ffff +#define PRPMC750_PCI_UPPER_MEM 0x3bffffff +#define PRPMC750_PCI_LOWER_IO 0x00000000 +#define PRPMC750_PCI_UPPER_IO 0x0ff7ffff + +#define PRPMC750_HAWK_MPIC_BASE 0xfbf80000 +#define PRPMC750_HAWK_SMC_BASE 0xfef80000 + +#define PRPMC750_BASE_BAUD 1843200 +#define PRPMC750_SERIAL_0 0xfef88000 +#define PRPMC750_SERIAL_0_DLL (PRPMC750_SERIAL_0 + (UART_DLL << 4)) +#define PRPMC750_SERIAL_0_DLM (PRPMC750_SERIAL_0 + (UART_DLM << 4)) +#define PRPMC750_SERIAL_0_LCR (PRPMC750_SERIAL_0 + (UART_LCR << 4)) + +#define PRPMC750_STATUS_REG 0xfef88080 +#define PRPMC750_BAUDOUT_MASK 0x02 +#define PRPMC750_MONARCH_MASK 0x01 + +#define PRPMC750_MODRST_REG 0xfef880a0 +#define PRPMC750_MODRST_MASK 0x01 + +#define PRPMC750_PIRQ_REG 0xfef880b0 +#define PRPMC750_SEL1_MASK 0x02 +#define PRPMC750_SEL0_MASK 0x01 + +#define PRPMC750_TBEN_REG 0xfef880c0 +#define PRPMC750_TBEN_MASK 0x01 + +#endif /* __ASM_PRPMC750_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/prpmc750_pci.c b/arch/ppc/platforms/prpmc750_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prpmc750_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,147 @@ +/* + * arch/ppc/platforms/prpmc750_pci.c + * + * PCI support for Motorola PrPMC750 + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * Motorola PrPMC750/PrPMC800 in PrPMCBASE or PrPMC-Carrier + * Combined irq tables. Only Base has IDSEL 14, only Carrier has 21 and 22. + */ +static inline int +prpmc_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {12, 0, 0, 0}, /* IDSEL 14 - Ethernet, base */ + {0, 0, 0, 0}, /* IDSEL 15 - unused */ + {10, 11, 12, 9}, /* IDSEL 16 - PMC A1, PMC1 */ + {10, 11, 12, 9}, /* IDSEL 17 - PrPMC-A-B, PMC2-B */ + {11, 12, 9, 10}, /* IDSEL 18 - PMC A1-B, PMC1-B */ + {0, 0, 0, 0}, /* IDSEL 19 - unused */ + {9, 10, 11, 12}, /* IDSEL 20 - P2P Bridge */ + {11, 12, 9, 10}, /* IDSEL 21 - PMC A2, carrier */ + {12, 9, 10, 11}, /* IDSEL 22 - PMC A2-B, carrier */ + }; + const long min_idsel = 14, max_idsel = 22, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +static void __init +prpmc750_pcibios_fixup(void) +{ + struct pci_dev *dev; + unsigned short wtmp; + + /* + * Kludge to clean up after PPC6BUG which doesn't + * configure the CL5446 VGA card. Also the + * resource subsystem doesn't fixup the + * PCI mem resources on the CL5446. + */ + if ((dev = pci_find_device(PCI_VENDOR_ID_CIRRUS, + PCI_DEVICE_ID_CIRRUS_5446, 0))) + { + dev->resource[0].start += PRPMC750_PCI_PHY_MEM_BASE; + dev->resource[0].end += PRPMC750_PCI_PHY_MEM_BASE; + pci_read_config_word(dev, + PCI_COMMAND, + &wtmp); + pci_write_config_word(dev, + PCI_COMMAND, + wtmp|3); + /* Enable Color mode in MISC reg */ + outb(0x03, 0x3c2); + /* Select DRAM config reg */ + outb(0x0f, 0x3c4); + /* Set proper DRAM config */ + outb(0xdf, 0x3c5); + } +} + +void __init +prpmc750_find_bridges(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = PRPMC750_PCI_PHY_MEM_BASE; + + pci_init_resource(&hose->io_resource, + PRPMC750_PCI_LOWER_IO, + PRPMC750_PCI_UPPER_IO, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + PRPMC750_PCI_LOWER_MEM + PRPMC750_PCI_PHY_MEM_BASE, + PRPMC750_PCI_UPPER_MEM + PRPMC750_PCI_PHY_MEM_BASE, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = PRPMC750_PCI_LOWER_IO; + hose->io_space.end = PRPMC750_PCI_UPPER_IO; + hose->mem_space.start = PRPMC750_PCI_LOWER_MEM; + hose->mem_space.end = PRPMC750_PCI_UPPER_MEM_AUTO; + + hose->io_base_virt = (void *)PRPMC750_ISA_IO_BASE; + + setup_indirect_pci(hose, + PRPMC750_PCI_CONFIG_ADDR, + PRPMC750_PCI_CONFIG_DATA); + + /* + * Disable MPIC response to PCI I/O space (BAR 0). + * Make MPIC respond to PCI Mem space at specified address. + * (BAR 1). + */ + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_BASE_ADDRESS_0, + 0x00000000 | 0x1); + + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_BASE_ADDRESS_1, + (PRPMC750_HAWK_MPIC_BASE - + PRPMC750_PCI_MEM_OFFSET) | 0x0); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = prpmc750_pcibios_fixup; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = prpmc_map_irq; +} diff -Nru a/arch/ppc/platforms/prpmc750_serial.h b/arch/ppc/platforms/prpmc750_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prpmc750_serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,45 @@ +/* + * include/asm-ppc/platforms/prpmc750_serial.h + * + * Motorola PrPMC750 serial support + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_PRPMC750_SERIAL_H__ +#define __ASM_PRPMC750_SERIAL_H__ + +#include +#include + +#define RS_TABLE_SIZE 4 + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (PRPMC750_BASE_BAUD / 16) + +#ifndef SERIAL_MAGIC_KEY +#define kernel_debugger ppc_kernel_debug +#endif + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, PRPMC750_SERIAL_0, 1, STD_COM_FLAGS, \ + iomem_base: (unsigned char *)PRPMC750_SERIAL_0, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM } /* ttyS0 */ + +#endif /* __ASM_PRPMC750_SERIAL_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/prpmc750_setup.c b/arch/ppc/platforms/prpmc750_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prpmc750_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,291 @@ +/* + * arch/ppc/platforms/prpmc750_setup.c + * + * Board setup routines for Motorola PrPMC750 + * + * Author: Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#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 + +extern void prpmc750_find_bridges(void); +extern int mpic_init(void); +extern unsigned long loops_per_jiffy; + +static u_char prpmc750_openpic_initsenses[] __initdata = +{ + 1, /* PRPMC750_INT_HOSTINT0 */ + 1, /* PRPMC750_INT_UART */ + 1, /* PRPMC750_INT_DEBUGINT */ + 1, /* PRPMC750_INT_HAWK_WDT */ + 1, /* PRPMC750_INT_UNUSED */ + 1, /* PRPMC750_INT_ABORT */ + 1, /* PRPMC750_INT_HOSTINT1 */ + 1, /* PRPMC750_INT_HOSTINT2 */ + 1, /* PRPMC750_INT_HOSTINT3 */ + 1, /* PRPMC750_INT_PMC_INTA */ + 1, /* PRPMC750_INT_PMC_INTB */ + 1, /* PRPMC750_INT_PMC_INTC */ + 1, /* PRPMC750_INT_PMC_INTD */ + 1, /* PRPMC750_INT_UNUSED */ + 1, /* PRPMC750_INT_UNUSED */ + 1, /* PRPMC750_INT_UNUSED */ +}; + +static int +prpmc750_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: PrPMC750\n"); + + return 0; +} + +static void __init +prpmc750_setup_arch(void) +{ + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Lookup PCI host bridges */ + prpmc750_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00ff); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 */ +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + /* Find and map our OpenPIC */ + pplus_mpic_init(PRPMC750_PCI_MEM_OFFSET); + OpenPIC_InitSenses = prpmc750_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(prpmc750_openpic_initsenses); + + printk("PrPMC750 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); +} + +/* + * Compute the PrPMC750's bus speed using the baud clock as a + * reference. + */ +static unsigned long __init +prpmc750_get_bus_speed(void) +{ + unsigned long tbl_start, tbl_end; + unsigned long current_state, old_state, bus_speed; + unsigned char lcr, dll, dlm; + int baud_divisor, count; + + /* Read the UART's baud clock divisor */ + lcr = readb(PRPMC750_SERIAL_0_LCR); + writeb(lcr | UART_LCR_DLAB, PRPMC750_SERIAL_0_LCR); + dll = readb(PRPMC750_SERIAL_0_DLL); + dlm = readb(PRPMC750_SERIAL_0_DLM); + writeb(lcr & ~UART_LCR_DLAB, PRPMC750_SERIAL_0_LCR); + baud_divisor = (dlm << 8) | dll; + + /* + * Use the baud clock divisor and base baud clock + * to determine the baud rate and use that as + * the number of baud clock edges we use for + * the time base sample. Make it half the baud + * rate. + */ + count = PRPMC750_BASE_BAUD / (baud_divisor * 16); + + /* Find the first edge of the baud clock */ + old_state = readb(PRPMC750_STATUS_REG) & PRPMC750_BAUDOUT_MASK; + do { + current_state = readb(PRPMC750_STATUS_REG) & + PRPMC750_BAUDOUT_MASK; + } while(old_state == current_state); + + old_state = current_state; + + /* Get the starting time base value */ + tbl_start = get_tbl(); + + /* + * Loop until we have found a number of edges equal + * to half the count (half the baud rate) + */ + do { + do { + current_state = readb(PRPMC750_STATUS_REG) & + PRPMC750_BAUDOUT_MASK; + } while(old_state == current_state); + old_state = current_state; + } while (--count); + + /* Get the ending time base value */ + tbl_end = get_tbl(); + + /* Compute bus speed */ + bus_speed = (tbl_end-tbl_start)*128; + + return bus_speed; +} + +static void __init +prpmc750_calibrate_decr(void) +{ + unsigned long freq; + int divisor = 4; + + freq = prpmc750_get_bus_speed(); + + tb_ticks_per_jiffy = freq / (HZ * divisor); + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); +} + +static void +prpmc750_restart(char *cmd) +{ + __cli(); + writeb(PRPMC750_MODRST_MASK, PRPMC750_MODRST_REG); + while(1); +} + +static void +prpmc750_halt(void) +{ + __cli(); + while (1); +} + +static void +prpmc750_power_off(void) +{ + prpmc750_halt(); +} + +/* Resolves the open_pic.c build without including i8259.c */ +int i8259_poll(void) +{ + return 0; +} + +static void __init +prpmc750_init_IRQ(void) +{ + openpic_init(1, 0, 0, -1); +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +prpmc750_set_bat(void) +{ + unsigned long bat3u, bat3l; + static int mapping_set = 0; + + if (!mapping_set) + { + __asm__ __volatile__( + " lis %0,0xf000\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x1ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (bat3u), "=r" (bat3l)); + + mapping_set = 1; + } + return; +} + +/* + * We need to read the Falcon/Hawk memory controller + * to properly determine this value + */ +static unsigned long __init +prpmc750_find_end_of_memory(void) +{ + /* Cover the Hawk registers with a BAT */ + prpmc750_set_bat(); + + /* Read the memory size from the Hawk SMC */ + return pplus_get_mem_size(PRPMC750_HAWK_SMC_BASE); +} + +static void __init +prpmc750_map_io(void) +{ + io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); + io_block_mapping(0xf0000000, 0xc0000000, 0x08000000, _PAGE_IO); + io_block_mapping(0xf8000000, 0xf8000000, 0x08000000, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + isa_io_base = PRPMC750_ISA_IO_BASE; + isa_mem_base = PRPMC750_ISA_MEM_BASE; + pci_dram_offset = PRPMC750_SYS_MEM_BASE; + + ppc_md.setup_arch = prpmc750_setup_arch; + ppc_md.show_cpuinfo = prpmc750_show_cpuinfo; + ppc_md.init_IRQ = prpmc750_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.find_end_of_memory = prpmc750_find_end_of_memory; + ppc_md.setup_io_mappings = prpmc750_map_io; + + ppc_md.restart = prpmc750_restart; + ppc_md.power_off = prpmc750_power_off; + ppc_md.halt = prpmc750_halt; + + /* PrPMC750 has no timekeeper part */ + ppc_md.time_init = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.calibrate_decr = prpmc750_calibrate_decr; +} diff -Nru a/arch/ppc/platforms/prpmc800.h b/arch/ppc/platforms/prpmc800.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prpmc800.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,56 @@ +/* + * include/asm-ppc/platforms/prpmc800.h + * + * Definitions for Motorola PrPMC800 board support + * + * Author: Dale Farnsworth + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + /* + * From Processor to PCI: + * PCI Mem Space: 0x80000000 - 0xa0000000 -> 0x80000000 - 0xa0000000 (512 MB) + * PCI I/O Space: 0xfe400000 - 0xfeef0000 -> 0x00000000 - 0x00b00000 (11 MB) + * Note: Must skip 0xfe000000-0xfe400000 for CONFIG_HIGHMEM/PKMAP area + * + * From PCI to Processor: + * System Memory: 0x00000000 -> 0x00000000 + */ + + +#ifndef __ASMPPC_PRPMC800_H +#define __ASMPPC_PRPMC800_H + +#define PRPMC800_PCI_CONFIG_ADDR 0xfe000cf8 +#define PRPMC800_PCI_CONFIG_DATA 0xfe000cfc + +#define PRPMC800_PROC_PCI_IO_START 0xfe400000U +#define PRPMC800_PROC_PCI_IO_END 0xfeefffffU +#define PRPMC800_PCI_IO_START 0x00000000U +#define PRPMC800_PCI_IO_END 0x00afffffU + +#define PRPMC800_PROC_PCI_MEM_START 0x80000000U +#define PRPMC800_PROC_PCI_MEM_END 0x9fffffffU +#define PRPMC800_PCI_MEM_START 0x80000000U +#define PRPMC800_PCI_MEM_END 0x9fffffffU + +#define PRPMC800_PCI_DRAM_OFFSET 0x00000000U +#define PRPMC800_PCI_PHY_MEM_OFFSET 0x00000000U + +#define PRPMC800_ISA_IO_BASE PRPMC800_PROC_PCI_IO_START +#define PRPMC800_ISA_MEM_BASE 0x00000000U + +#define PRPMC800_HARRIER_XCSR_BASE 0xfeff0000 +#define PRPMC800_HARRIER_MPIC_BASE 0xff000000 + +#define PRPMC800_SERIAL_1 0xfeff00c0 + +#define PRPMC800_BASE_BAUD 1843200 + + +#endif /* __ASMPPC_PRPMC800_H */ diff -Nru a/arch/ppc/platforms/prpmc800_pci.c b/arch/ppc/platforms/prpmc800_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prpmc800_pci.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,122 @@ +/* + * arch/ppc/platforms/prpmc800_pci.c + * + * PCI support for Motorola PrPMC800 + * + * Author: Dale Farnsworth + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Motorola PrPMC750/PrPMC800 in PrPMCBASE or PrPMC-Carrier + * Combined irq tables. Only Base has IDSEL 14, only Carrier has 21 and 22. + */ +static inline int +prpmc_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {12, 0, 0, 0}, /* IDSEL 14 - Ethernet, base */ + {0, 0, 0, 0}, /* IDSEL 15 - unused */ + {10, 11, 12, 9}, /* IDSEL 16 - PMC A1, PMC1 */ + {10, 11, 12, 9}, /* IDSEL 17 - PrPMC-A-B, PMC2-B */ + {11, 12, 9, 10}, /* IDSEL 18 - PMC A1-B, PMC1-B */ + {0, 0, 0, 0}, /* IDSEL 19 - unused */ + {9, 10, 11, 12}, /* IDSEL 20 - P2P Bridge */ + {11, 12, 9, 10}, /* IDSEL 21 - PMC A2, carrier */ + {12, 9, 10, 11}, /* IDSEL 22 - PMC A2-B, carrier */ + }; + const long min_idsel = 14, max_idsel = 22, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +prpmc800_find_bridges(void) +{ + struct pci_controller* hose; + int host_bridge; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = PRPMC800_PCI_PHY_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, + PRPMC800_PCI_IO_START, + PRPMC800_PCI_IO_END, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + PRPMC800_PCI_MEM_START, + PRPMC800_PCI_MEM_END, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = PRPMC800_PCI_IO_START; + hose->io_space.end = PRPMC800_PCI_IO_END; + hose->mem_space.start = PRPMC800_PCI_MEM_START; + hose->mem_space.end = PRPMC800_PCI_MEM_END; + hose->io_base_virt = (void *)PRPMC800_ISA_IO_BASE; + + setup_indirect_pci(hose, + PRPMC800_PCI_CONFIG_ADDR, + PRPMC800_PCI_CONFIG_DATA); + + /* Get host bridge vendor/dev id */ + early_read_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_VENDOR_ID, + &host_bridge); + + switch (host_bridge) { + case HARRIER_VEND_DEV_ID: + if (harrier_init(hose, + PRPMC800_HARRIER_XCSR_BASE, + PRPMC800_PROC_PCI_MEM_START, + PRPMC800_PROC_PCI_MEM_END, + PRPMC800_PROC_PCI_IO_START, + PRPMC800_PROC_PCI_IO_END, + PRPMC800_HARRIER_MPIC_BASE) != 0) { + printk("Could not initialize HARRIER bridge\n"); + } + break; + default: + printk("Host bridge 0x%x not supported\n", host_bridge); + } + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = NULL; + ppc_md.pcibios_fixup_bus = NULL; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = prpmc_map_irq; +} diff -Nru a/arch/ppc/platforms/prpmc800_serial.h b/arch/ppc/platforms/prpmc800_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prpmc800_serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,51 @@ +/* + * include/asm-ppc/platforms/prpmc800_serial.h + * + * Definitions for Motorola MCG PRPMC800 cPCI board support + * + * Author: Dale Farnsworth dale.farnsworth@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __ASMPPC_PRPMC800_SERIAL_H +#define __ASMPPC_PRPMC800_SERIAL_H + +#include +#include + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (PRPMC800_BASE_BAUD / 16) + +#ifndef SERIAL_MAGIC_KEY +#define kernel_debugger ppc_kernel_debug +#endif + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +/* UARTS are at IRQ 16 */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, PRPMC800_SERIAL_1, 16, STD_COM_FLAGS, /* ttyS0 */\ + iomem_base: (unsigned char *)PRPMC800_SERIAL_1, \ + iomem_reg_shift: 0, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +#endif /* __ASMPPC_PRPMC800_SERIAL_H */ diff -Nru a/arch/ppc/platforms/prpmc800_setup.c b/arch/ppc/platforms/prpmc800_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/prpmc800_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,336 @@ +/* + * + * Author: Dale Farnsworth + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#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 + +#define HARRIER_REVI_REG (PRPMC800_HARRIER_XCSR_BASE+HARRIER_REVI_OFF) +#define HARRIER_UCTL_REG (PRPMC800_HARRIER_XCSR_BASE+HARRIER_UCTL_OFF) +#define HARRIER_MISC_CSR_REG (PRPMC800_HARRIER_XCSR_BASE+HARRIER_MISC_CSR_OFF) +#define HARRIER_IFEVP_REG (PRPMC800_HARRIER_MPIC_BASE+HARRIER_MPIC_IFEVP_OFF) +#define HARRIER_IFEDE_REG (PRPMC800_HARRIER_MPIC_BASE+HARRIER_MPIC_IFEDE_OFF) +#define HARRIER_FEEN_REG (PRPMC800_HARRIER_XCSR_BASE+HARRIER_FEEN_OFF) +#define HARRIER_FEMA_REG (PRPMC800_HARRIER_XCSR_BASE+HARRIER_FEMA_OFF) + +extern void prpmc800_find_bridges(void); +extern int mpic_init(void); +extern unsigned long loops_per_jiffy; + +static u_char prpmc800_openpic_initsenses[] __initdata = +{ + 1, /* PRPMC800_INT_HOSTINT0 */ + 1, /* PRPMC800_INT_UNUSED */ + 1, /* PRPMC800_INT_DEBUGINT */ + 1, /* PRPMC800_INT_HARRIER_WDT */ + 1, /* PRPMC800_INT_UNUSED */ + 1, /* PRPMC800_INT_UNUSED */ + 1, /* PRPMC800_INT_HOSTINT1 */ + 1, /* PRPMC800_INT_HOSTINT2 */ + 1, /* PRPMC800_INT_HOSTINT3 */ + 1, /* PRPMC800_INT_PMC_INTA */ + 1, /* PRPMC800_INT_PMC_INTB */ + 1, /* PRPMC800_INT_PMC_INTC */ + 1, /* PRPMC800_INT_PMC_INTD */ + 1, /* PRPMC800_INT_UNUSED */ + 1, /* PRPMC800_INT_UNUSED */ + 1, /* PRPMC800_INT_UNUSED */ + 1, /* PRPMC800_INT_HARRIER_INT (UARTS, ABORT, DMA) */ +}; + +static int +prpmc800_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: PrPMC800\n"); + + return 0; +} + +static void __init +prpmc800_setup_arch(void) +{ + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Lookup PCI host bridges */ + prpmc800_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00ff); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 */ +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + OpenPIC_InitSenses = prpmc800_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(prpmc800_openpic_initsenses); + + printk("PrPMC800 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); +} + +/* + * Compute the PrPMC800's tbl frequency using the baud clock as a reference. + */ + +static void __init +prpmc800_calibrate_decr(void) +{ + unsigned long tbl_start, tbl_end; + unsigned long current_state, old_state, tb_ticks_per_second; + unsigned int count; + unsigned int harrier_revision; + + harrier_revision = readb(HARRIER_REVI_REG); + if (harrier_revision < 2) { + /* XTAL64 was broken in harrier revision 1 */ + printk("time_init: Harrier revision %d, assuming 100 Mhz bus\n", + harrier_revision); + tb_ticks_per_second = 100000000/4; + tb_ticks_per_jiffy = tb_ticks_per_second / HZ; + tb_to_us = mulhwu_scale_factor(tb_ticks_per_second, 1000000); + return; + } + + /* + * The XTAL64 bit oscillates at the 1/64 the base baud clock + * Set count to XTAL64 cycles per second. Since we'll count + * half-cycles, we'll reach the count in half a second. + */ + count = PRPMC800_BASE_BAUD / 64; + + /* Find the first edge of the baud clock */ + old_state = readb(HARRIER_UCTL_REG) & HARRIER_XTAL64_MASK; + do { + current_state = readb(HARRIER_UCTL_REG) & + HARRIER_XTAL64_MASK; + } while(old_state == current_state); + + old_state = current_state; + + /* Get the starting time base value */ + tbl_start = get_tbl(); + + /* + * Loop until we have found a number of edges (half-cycles) + * equal to the count (half a second) + */ + do { + do { + current_state = readb(HARRIER_UCTL_REG) & + HARRIER_XTAL64_MASK; + } while(old_state == current_state); + old_state = current_state; + } while (--count); + + /* Get the ending time base value */ + tbl_end = get_tbl(); + + /* We only counted for half a second, so double to get ticks/second */ + tb_ticks_per_second = (tbl_end - tbl_start) * 2; + tb_ticks_per_jiffy = tb_ticks_per_second / HZ; + tb_to_us = mulhwu_scale_factor(tb_ticks_per_second, 1000000); +} + +static void +prpmc800_restart(char *cmd) +{ + __cli(); + writeb(HARRIER_RSTOUT_MASK, HARRIER_MISC_CSR_REG); + while(1); +} + +static void +prpmc800_halt(void) +{ + __cli(); + while (1); +} + +static void +prpmc800_power_off(void) +{ + prpmc800_halt(); +} + +/* Resolves the open_pic.c build without including i8259.c */ +int i8259_poll() +{ + return 0; +} + +static void __init +prpmc800_init_IRQ(void) +{ + openpic_init(1, 0, 0, -1); + +#define PRIORITY 15 +#define VECTOR 16 +#define PROCESSOR 0 + /* initialize the harrier's internal interrupt priority 15, irq 1 */ + out_be32((u32 *)HARRIER_IFEVP_REG, (PRIORITY<<16) | VECTOR); + out_be32((u32 *)HARRIER_IFEDE_REG, (1< +#include +#include +#include + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in */ +}; + +void +prpmc800_progress(char *s, unsigned short hex) +{ + volatile char c; + volatile unsigned char *com_port; + volatile unsigned char *com_port_lsr; + + com_port = (volatile unsigned char *) rs_table[0].port; + com_port_lsr = com_port + UART_LSR; + + while ((c = *s++) != 0) { + while ((*com_port_lsr & UART_LSR_THRE) == 0) + ; + *com_port = c; + + if (c == '\n') { + while ((*com_port_lsr & UART_LSR_THRE) == 0) + ; + *com_port = '\r'; + } + } +} +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +/* + * We need to read the Harrier memory controller + * to properly determine this value + */ +static unsigned long __init +prpmc800_find_end_of_memory(void) +{ + /* Cover the harrier registers with a BAT */ + prpmc800_set_bat(); + + /* Read the memory size from the Harrier XCSR */ + return harrier_get_mem_size(PRPMC800_HARRIER_XCSR_BASE); +} + +static void __init +prpmc800_map_io(void) +{ + io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); + io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + prpmc800_set_bat(); + + isa_io_base = PRPMC800_ISA_IO_BASE; + isa_mem_base = PRPMC800_ISA_MEM_BASE; + pci_dram_offset = PRPMC800_PCI_DRAM_OFFSET; + + ppc_md.setup_arch = prpmc800_setup_arch; + ppc_md.show_cpuinfo = prpmc800_show_cpuinfo; + ppc_md.init_IRQ = prpmc800_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.find_end_of_memory = prpmc800_find_end_of_memory; + ppc_md.setup_io_mappings = prpmc800_map_io; + + ppc_md.restart = prpmc800_restart; + ppc_md.power_off = prpmc800_power_off; + ppc_md.halt = prpmc800_halt; + + /* PrPMC800 has no timekeeper part */ + ppc_md.time_init = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.calibrate_decr = prpmc800_calibrate_decr; +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = prpmc800_progress; +#else /* !CONFIG_SERIAL_TEXT_DEBUG */ + ppc_md.progress = NULL; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +} diff -Nru a/arch/ppc/platforms/redwood.c b/arch/ppc/platforms/redwood.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/redwood.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,66 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Author: MontaVista Software, Inc. + * Frank Rowand + * + * Module name: redwood.c + * + * Description: + * + * History: 11/09/2001 - Armin + * added board_init to add in additional instuctions needed during platfrom_init + * + */ + +#include +#include +#include +#include +#include +#include + +void __init +board_setup_arch(void) +{ +} + +void __init +board_io_mapping(void) +{ + int i; + + io_block_mapping(OAKNET_IO_VADDR, + OAKNET_IO_PADDR, OAKNET_IO_SIZE, _PAGE_IO); + +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ +} + +/* hack; blame me dan. -brad */ +#ifdef CONFIG_INPUT_KEYBDEV + +void +handle_scancode(unsigned char scancode, int down) +{ + printk("handle_scancode(scancode=0x%x, down=%d)\n", scancode, down); +} + +static void +kbd_bh(unsigned long dummy) +{ +} + +DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); +void (*kbd_ledfunc) (unsigned int led); + +#endif diff -Nru a/arch/ppc/platforms/redwood.h b/arch/ppc/platforms/redwood.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/redwood.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,51 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * PPC405 modifications + * Author: MontaVista Software, Inc. + * frank_rowand@mvista.com or source@mvista.com + * + * Module name: redwood.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * STB03xxx "Redwood" evaluation board. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_REDWOOD_H__ +#define __ASM_REDWOOD_H__ + +/* Redwoods have an STB03xxx or STB04xxx core */ +#include + +#ifndef __ASSEMBLY__ +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned int bi_dummy; /* field shouldn't exist */ + unsigned char bi_enetaddr[6]; /* Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* Bus speed, in Hz */ + unsigned int bi_tbfreq; /* Software timebase freq */ +} bd_t; +#endif /* !__ASSEMBLY__ */ + +#define OAKNET_IO_PADDR ((uint)0xf2000000) +#define OAKNET_IO_VADDR OAKNET_IO_PADDR +#define OAKNET_IO_BASE OAKNET_IO_VADDR + +/* ftr revisit- io size was 0xffff in old-line, is 0x40 in oak.h */ +#define OAKNET_IO_SIZE 0xffff +#define OAKNET_INT 26 /* EXTINT1 */ + +#define _IO_BASE 0 +#define _ISA_MEM_BASE 0 +#define PCI_DRAM_OFFSET 0 + +#define BASE_BAUD 1312500 + +#define PPC4xx_MACHINE_NAME "IBM Redwood" + +#endif /* __ASM_REDWOOD_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/redwood5.c b/arch/ppc/platforms/redwood5.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/redwood5.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,82 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Author: Armin Kuster + * + * Module name: redwood5.c + * + * Description: + * IBM redwood5 eval board file + * + * History: 12/29/2001 - Armin + * initail release + * + */ + +#include +#include +#include +#include +#include + +void __init +board_setup_arch(void) +{ + + bd_t *bip = (bd_t *)__res; + +#define CONFIG_DEBUG_BRINGUP +#ifdef CONFIG_DEBUG_BRINGUP + printk("\n"); + printk("machine\t: %s\n", PPC4xx_MACHINE_NAME); + printk("\n"); + printk("bi_s_version\t %s\n", bip->bi_s_version); + printk("bi_r_version\t %s\n", bip->bi_r_version); + printk("bi_memsize\t 0x%8.8x\t %dMBytes\n", bip->bi_memsize,bip->bi_memsize/(1024*1000)); + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 0, + bip->bi_enetaddr[0], bip->bi_enetaddr[1], + bip->bi_enetaddr[2], bip->bi_enetaddr[3], + bip->bi_enetaddr[4], bip->bi_enetaddr[5]); + + printk("bi_intfreq\t 0x%8.8x\t clock:\t %dMhz\n", + bip->bi_intfreq, bip->bi_intfreq/ 1000000); + + printk("bi_busfreq\t 0x%8.8x\t plb bus clock:\t %dMHz\n", + bip->bi_busfreq, bip->bi_busfreq / 1000000 ); + printk("bi_tbfreq\t 0x%8.8x\t TB freq:\t %dMHz\n", + bip->bi_tbfreq, bip->bi_tbfreq/1000000); + + printk("\n"); +#endif + +} + +void __init +board_io_mapping(void) +{ + int i; + + for (i = 0; i < 16; i++) { + unsigned long v, p; + + /* 0x400x0000 -> 0xe00x0000 */ + p = 0x40000000 | (i << 16); + v = STB04xxx_IO_BASE | (i << 16); + + io_block_mapping(v, p, PAGE_SIZE, + _PAGE_NO_CACHE | pgprot_val(PAGE_KERNEL) | _PAGE_GUARDED); + } + + +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ +} diff -Nru a/arch/ppc/platforms/redwood5.h b/arch/ppc/platforms/redwood5.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/redwood5.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,54 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * PPC405 modifications + * Author: MontaVista Software, Inc. + * Armin Kuster + * + * Module name: redwood5.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * STB03xxx "Redwood" evaluation board. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_REDWOOD5_H__ +#define __ASM_REDWOOD5_H__ + +/* Redwood5 has an STB04xxx core */ +#include + +#ifndef __ASSEMBLY__ +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned int bi_dummy; /* field shouldn't exist */ + unsigned char bi_enetaddr[6]; /* Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* Bus speed, in Hz */ + unsigned int bi_tbfreq; /* Software timebase freq */ +} bd_t; +#endif /* !__ASSEMBLY__ */ + + +#define SMC91111_BASE_ADDR 0xf2000300 +#define SMC91111_IRQ 28 + +#ifdef MAX_HWIFS +#undef MAX_HWIFS +#endif +#define MAX_HWIFS 1 + +#define _IO_BASE 0 +#define _ISA_MEM_BASE 0 +#define PCI_DRAM_OFFSET 0 + +/* serail defines moved from ppc4xx_serial.h * + */ +#define BASE_BAUD 1267200 + +#define PPC4xx_MACHINE_NAME "IBM Redwood5" + +#endif /* __ASM_REDWOOD5_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/residual.c b/arch/ppc/platforms/residual.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/residual.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,878 @@ +/* + * BK Id: SCCS/s.residual.c 1.13 09/11/01 16:54:34 trini + */ +/* + * Code to deal with the PReP residual data. + * + * Written by: Cort Dougan (cort@cs.nmt.edu) + * Improved _greatly_ and rewritten by Gabriel Paubert (paubert@iram.es) + * + * This file is based on the following documentation: + * + * IBM Power Personal Systems Architecture + * Residual Data + * Document Number: PPS-AR-FW0001 + * + * 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 archive + * for more details. + * + */ + +#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 + + +unsigned char __res[sizeof(RESIDUAL)] __prepdata = {0,}; +RESIDUAL *res = (RESIDUAL *)&__res; + +char * PnP_BASE_TYPES[] __initdata = { + "Reserved", + "MassStorageDevice", + "NetworkInterfaceController", + "DisplayController", + "MultimediaController", + "MemoryController", + "BridgeController", + "CommunicationsDevice", + "SystemPeripheral", + "InputDevice", + "ServiceProcessor" + }; + +/* Device Sub Type Codes */ + +unsigned char * PnP_SUB_TYPES[] __initdata = { + "\001\000SCSIController", + "\001\001IDEController", + "\001\002FloppyController", + "\001\003IPIController", + "\001\200OtherMassStorageController", + "\002\000EthernetController", + "\002\001TokenRingController", + "\002\002FDDIController", + "\002\0x80OtherNetworkController", + "\003\000VGAController", + "\003\001SVGAController", + "\003\002XGAController", + "\003\200OtherDisplayController", + "\004\000VideoController", + "\004\001AudioController", + "\004\200OtherMultimediaController", + "\005\000RAM", + "\005\001FLASH", + "\005\200OtherMemoryDevice", + "\006\000HostProcessorBridge", + "\006\001ISABridge", + "\006\002EISABridge", + "\006\003MicroChannelBridge", + "\006\004PCIBridge", + "\006\005PCMCIABridge", + "\006\006VMEBridge", + "\006\200OtherBridgeDevice", + "\007\000RS232Device", + "\007\001ATCompatibleParallelPort", + "\007\200OtherCommunicationsDevice", + "\010\000ProgrammableInterruptController", + "\010\001DMAController", + "\010\002SystemTimer", + "\010\003RealTimeClock", + "\010\004L2Cache", + "\010\005NVRAM", + "\010\006PowerManagement", + "\010\007CMOS", + "\010\010OperatorPanel", + "\010\011ServiceProcessorClass1", + "\010\012ServiceProcessorClass2", + "\010\013ServiceProcessorClass3", + "\010\014GraphicAssist", + "\010\017SystemPlanar", + "\010\200OtherSystemPeripheral", + "\011\000KeyboardController", + "\011\001Digitizer", + "\011\002MouseController", + "\011\003TabletController", + "\011\0x80OtherInputController", + "\012\000GeneralMemoryController", + NULL +}; + +/* Device Interface Type Codes */ + +unsigned char * PnP_INTERFACES[] __initdata = { + "\000\000\000General", + "\001\000\000GeneralSCSI", + "\001\001\000GeneralIDE", + "\001\001\001ATACompatible", + + "\001\002\000GeneralFloppy", + "\001\002\001Compatible765", + "\001\002\002NS398_Floppy", /* NS Super I/O wired to use index + register at port 398 and data + register at port 399 */ + "\001\002\003NS26E_Floppy", /* Ports 26E and 26F */ + "\001\002\004NS15C_Floppy", /* Ports 15C and 15D */ + "\001\002\005NS2E_Floppy", /* Ports 2E and 2F */ + "\001\002\006CHRP_Floppy", /* CHRP Floppy in PR*P system */ + + "\001\003\000GeneralIPI", + + "\002\000\000GeneralEther", + "\002\001\000GeneralToken", + "\002\002\000GeneralFDDI", + + "\003\000\000GeneralVGA", + "\003\001\000GeneralSVGA", + "\003\002\000GeneralXGA", + + "\004\000\000GeneralVideo", + "\004\001\000GeneralAudio", + "\004\001\001CS4232Audio", /* CS 4232 Plug 'n Play Configured */ + + "\005\000\000GeneralRAM", + /* This one is obviously wrong ! */ + "\005\000\000PCIMemoryController", /* PCI Config Method */ + "\005\000\001RS6KMemoryController", /* RS6K Config Method */ + "\005\001\000GeneralFLASH", + + "\006\000\000GeneralHostBridge", + "\006\001\000GeneralISABridge", + "\006\002\000GeneralEISABridge", + "\006\003\000GeneralMCABridge", + /* GeneralPCIBridge = 0, */ + "\006\004\000PCIBridgeDirect", + "\006\004\001PCIBridgeIndirect", + "\006\004\002PCIBridgeRS6K", + "\006\005\000GeneralPCMCIABridge", + "\006\006\000GeneralVMEBridge", + + "\007\000\000GeneralRS232", + "\007\000\001COMx", + "\007\000\002Compatible16450", + "\007\000\003Compatible16550", + "\007\000\004NS398SerPort", /* NS Super I/O wired to use index + register at port 398 and data + register at port 399 */ + "\007\000\005NS26ESerPort", /* Ports 26E and 26F */ + "\007\000\006NS15CSerPort", /* Ports 15C and 15D */ + "\007\000\007NS2ESerPort", /* Ports 2E and 2F */ + + "\007\001\000GeneralParPort", + "\007\001\001LPTx", + "\007\001\002NS398ParPort", /* NS Super I/O wired to use index + register at port 398 and data + register at port 399 */ + "\007\001\003NS26EParPort", /* Ports 26E and 26F */ + "\007\001\004NS15CParPort", /* Ports 15C and 15D */ + "\007\001\005NS2EParPort", /* Ports 2E and 2F */ + + "\010\000\000GeneralPIC", + "\010\000\001ISA_PIC", + "\010\000\002EISA_PIC", + "\010\000\003MPIC", + "\010\000\004RS6K_PIC", + + "\010\001\000GeneralDMA", + "\010\001\001ISA_DMA", + "\010\001\002EISA_DMA", + + "\010\002\000GeneralTimer", + "\010\002\001ISA_Timer", + "\010\002\002EISA_Timer", + "\010\003\000GeneralRTC", + "\010\003\001ISA_RTC", + + "\010\004\001StoreThruOnly", + "\010\004\002StoreInEnabled", + "\010\004\003RS6KL2Cache", + + "\010\005\000IndirectNVRAM", /* Indirectly addressed */ + "\010\005\001DirectNVRAM", /* Memory Mapped */ + "\010\005\002IndirectNVRAM24", /* Indirectly addressed - 24 bit */ + + "\010\006\000GeneralPowerManagement", + "\010\006\001EPOWPowerManagement", + "\010\006\002PowerControl", // d1378 + + "\010\007\000GeneralCMOS", + + "\010\010\000GeneralOPPanel", + "\010\010\001HarddiskLight", + "\010\010\002CDROMLight", + "\010\010\003PowerLight", + "\010\010\004KeyLock", + "\010\010\005ANDisplay", /* AlphaNumeric Display */ + "\010\010\006SystemStatusLED", /* 3 digit 7 segment LED */ + "\010\010\007CHRP_SystemStatusLED", /* CHRP LEDs in PR*P system */ + + "\010\011\000GeneralServiceProcessor", + "\010\012\000GeneralServiceProcessor", + "\010\013\000GeneralServiceProcessor", + + "\010\014\001TransferData", + "\010\014\002IGMC32", + "\010\014\003IGMC64", + + "\010\017\000GeneralSystemPlanar", /* 10/5/95 */ + NULL + }; + +static const unsigned char __init *PnP_SUB_TYPE_STR(unsigned char BaseType, + unsigned char SubType) { + unsigned char ** s=PnP_SUB_TYPES; + while (*s && !((*s)[0]==BaseType + && (*s)[1]==SubType)) s++; + if (*s) return *s+2; + else return("Unknown !"); +}; + +static const unsigned char __init *PnP_INTERFACE_STR(unsigned char BaseType, + unsigned char SubType, + unsigned char Interface) { + unsigned char ** s=PnP_INTERFACES; + while (*s && !((*s)[0]==BaseType + && (*s)[1]==SubType + && (*s)[2]==Interface)) s++; + if (*s) return *s+3; + else return NULL; +}; + +static void __init printsmallvendor(PnP_TAG_PACKET *pkt, int size) { + int i, c; + char decomp[4]; +#define p pkt->S14_Pack.S14_Data.S14_PPCPack + switch(p.Type) { + case 1: + /* Decompress first 3 chars */ + c = *(unsigned short *)p.PPCData; + decomp[0]='A'-1+((c>>10)&0x1F); + decomp[1]='A'-1+((c>>5)&0x1F); + decomp[2]='A'-1+(c&0x1F); + decomp[3]=0; + printk(" Chip identification: %s%4.4X\n", + decomp, ld_le16((unsigned short *)(p.PPCData+2))); + break; + default: + printk(" Small vendor item type 0x%2.2x, data (hex): ", + p.Type); + for(i=0; iS1_Pack.Tag)) { + case PnPVersion: + printk(" PnPversion 0x%x.%x\n", + pkt->S1_Pack.Version[0], /* How to interpret version ? */ + pkt->S1_Pack.Version[1]); + break; +// case Logicaldevice: + break; +// case CompatibleDevice: + break; + case IRQFormat: +#define p pkt->S4_Pack + printk(" IRQ Mask 0x%4.4x, %s %s sensitive\n", + ld_le16((unsigned short *)p.IRQMask), + intlevel[(size>3) ? !(p.IRQInfo&0x05) : 0], + intsense[(size>3) ? !(p.IRQInfo&0x03) : 0]); +#undef p + break; + case DMAFormat: +#define p pkt->S5_Pack + printk(" DMA channel mask 0x%2.2x, info 0x%2.2x\n", + p.DMAMask, p.DMAInfo); +#undef p + break; + case StartDepFunc: + printk("Start dependent function:\n"); + break; + case EndDepFunc: + printk("End dependent function\n"); + break; + case IOPort: +#define p pkt->S8_Pack + printk(" Variable (%d decoded bits) I/O port\n" + " from 0x%4.4x to 0x%4.4x, alignment %d, %d ports\n", + p.IOInfo&ISAAddr16bit?16:10, + ld_le16((unsigned short *)p.RangeMin), + ld_le16((unsigned short *)p.RangeMax), + p.IOAlign, p.IONum); +#undef p + break; + case FixedIOPort: +#define p pkt->S9_Pack + printk(" Fixed (10 decoded bits) I/O port from %3.3x to %3.3x\n", + (p.Range[1]<<8)|p.Range[0], + ((p.Range[1]<<8)|p.Range[0])+p.IONum-1); +#undef p + break; + case Res1: + case Res2: + case Res3: + printk(" Undefined packet type %d!\n", + tag_small_item_name(pkt->S1_Pack.Tag)); + break; + case SmallVendorItem: + printsmallvendor(pkt,size); + break; + default: + printk(" Type 0x2.2x%d, size=%d\n", + pkt->S1_Pack.Tag, size); + break; + } +} + +static void __init printlargevendor(PnP_TAG_PACKET * pkt, int size) { + static const unsigned char * addrtype[] = {"I/O", "Memory", "System"}; + static const unsigned char * inttype[] = {"8259", "MPIC", "RS6k BUID %d"}; + static const unsigned char * convtype[] = {"Bus Memory", "Bus I/O", "DMA"}; + static const unsigned char * transtype[] = {"direct", "mapped", "direct-store segment"}; + static const unsigned char * L2type[] = {"WriteThru", "CopyBack"}; + static const unsigned char * L2assoc[] = {"DirectMapped", "2-way set"}; + + int i; + char tmpstr[30], *t; +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + switch(p.Type) { + case 2: + printk(" %d K %s %s L2 cache, %d/%d bytes line/sector size\n", + ld_le32((unsigned int *)p.PPCData), + L2type[p.PPCData[10]-1], + L2assoc[p.PPCData[4]-1], + ld_le16((unsigned short *)p.PPCData+3), + ld_le16((unsigned short *)p.PPCData+4)); + break; + case 3: + printk(" PCI Bridge parameters\n" + " ConfigBaseAddress %0x\n" + " ConfigBaseData %0x\n" + " Bus number %d\n", + ld_le32((unsigned int *)p.PPCData), + ld_le32((unsigned int *)(p.PPCData+8)), + p.PPCData[16]); + for(i=20; iS1_Pack.Tag)) { + case LargeVendorItem: + printlargevendor(pkt, size); + break; + default: + printk(" Type 0x2.2x%d, size=%d\n", + pkt->S1_Pack.Tag, size); + break; + } +} +static void __init printpackets(PnP_TAG_PACKET * pkt, const char * cat) { + if (pkt->S1_Pack.Tag== END_TAG) { + printk(" No packets describing %s resources.\n", cat); + return; + } + printk( " Packets describing %s resources:\n",cat); + do { + int size; + if (tag_type(pkt->S1_Pack.Tag)) { + size= 3 + + pkt->L1_Pack.Count0 + + pkt->L1_Pack.Count1*256; + printlargepacket(pkt, size); + } else { + size=tag_small_count(pkt->S1_Pack.Tag)+1; + printsmallpacket(pkt, size); + } + (unsigned char *) pkt+=size; + } while (pkt->S1_Pack.Tag != END_TAG); +} + +void __init print_residual_device_info(void) +{ + int i; + PPC_DEVICE *dev; +#define did dev->DeviceId + + /* make sure we have residual data first */ + if ( res->ResidualLength == 0 ) + return; + + printk("Residual: %ld devices\n", res->ActualNumDevices); + for ( i = 0; + i < res->ActualNumDevices ; + i++) + { + char decomp[4], sn[20]; + const char * s; + dev = &res->Devices[i]; + s = PnP_INTERFACE_STR(did.BaseType, did.SubType, + did.Interface); + if(!s) { + sprintf(sn, "interface %d", did.Interface); + s=sn; + } + if ( did.BusId & PCIDEVICE ) + printk("PCI Device, Bus %d, DevFunc 0x%x:", + dev->BusAccess.PCIAccess.BusNumber, + dev->BusAccess.PCIAccess.DevFuncNumber); + if ( did.BusId & PNPISADEVICE ) printk("PNPISA Device:"); + if ( did.BusId & ISADEVICE ) + printk("ISA Device, Slot %d, LogicalDev %d:", + dev->BusAccess.ISAAccess.SlotNumber, + dev->BusAccess.ISAAccess.LogicalDevNumber); + if ( did.BusId & EISADEVICE ) printk("EISA Device:"); + if ( did.BusId & PROCESSORDEVICE ) + printk("ProcBus Device, Bus %d, BUID %d: ", + dev->BusAccess.ProcBusAccess.BusNumber, + dev->BusAccess.ProcBusAccess.BUID); + if ( did.BusId & PCMCIADEVICE ) printk("PCMCIA "); + if ( did.BusId & VMEDEVICE ) printk("VME "); + if ( did.BusId & MCADEVICE ) printk("MCA "); + if ( did.BusId & MXDEVICE ) printk("MX "); + /* Decompress first 3 chars */ + decomp[0]='A'-1+((did.DevId>>26)&0x1F); + decomp[1]='A'-1+((did.DevId>>21)&0x1F); + decomp[2]='A'-1+((did.DevId>>16)&0x1F); + decomp[3]=0; + printk(" %s%4.4lX, %s, %s, %s\n", + decomp, did.DevId&0xffff, + PnP_BASE_TYPES[did.BaseType], + PnP_SUB_TYPE_STR(did.BaseType,did.SubType), + s); + if ( dev->AllocatedOffset ) + printpackets( (union _PnP_TAG_PACKET *) + &res->DevicePnPHeap[dev->AllocatedOffset], + "allocated"); + if ( dev->PossibleOffset ) + printpackets( (union _PnP_TAG_PACKET *) + &res->DevicePnPHeap[dev->PossibleOffset], + "possible"); + if ( dev->CompatibleOffset ) + printpackets( (union _PnP_TAG_PACKET *) + &res->DevicePnPHeap[dev->CompatibleOffset], + "compatible"); + } +} + + +#if 0 +static void __init printVPD(void) { +#define vpd res->VitalProductData + int ps=vpd.PageSize, i, j; + static const char* Usage[]={ + "FirmwareStack", "FirmwareHeap", "FirmwareCode", "BootImage", + "Free", "Unpopulated", "ISAAddr", "PCIConfig", + "IOMemory", "SystemIO", "SystemRegs", "PCIAddr", + "UnPopSystemRom", "SystemROM", "ResumeBlock", "Other" + }; + static const unsigned char *FWMan[]={ + "IBM", "Motorola", "FirmWorks", "Bull" + }; + static const unsigned char *FWFlags[]={ + "Conventional", "OpenFirmware", "Diagnostics", "LowDebug", + "MultiBoot", "LowClient", "Hex41", "FAT", + "ISO9660", "SCSI_ID_Override", "Tape_Boot", "FW_Boot_Path" + }; + static const unsigned char *ESM[]={ + "Port92", "PCIConfigA8", "FF001030", "????????" + }; + static const unsigned char *SIOM[]={ + "Port850", "????????", "PCIConfigA8", "????????" + }; + + printk("Model: %s\n",vpd.PrintableModel); + printk("Serial: %s\n", vpd.Serial); + printk("FirmwareSupplier: %s\n", FWMan[vpd.FirmwareSupplier]); + printk("FirmwareFlags:"); + for(j=0; j<12; j++) { + if (vpd.FirmwareSupports & (1<2 ? 2 : vpd.EndianSwitchMethod]); + printk("SpreadIOMethod: %s\n", + SIOM[vpd.SpreadIOMethod>3 ? 3 : vpd.SpreadIOMethod]); + printk("Processor/Bus frequencies (Hz): %ld/%ld\n", + vpd.ProcessorHz, vpd.ProcessorBusHz); + printk("Time Base Divisor: %ld\n", vpd.TimeBaseDivisor); + printk("WordWidth, PageSize: %ld, %d\n", vpd.WordWidth, ps); + printk("Cache sector size, Lock granularity: %ld, %ld\n", + vpd.CoherenceBlockSize, vpd.GranuleSize); + for (i=0; iActualNumMemSegs; i++) { + int mask=res->Segs[i].Usage, first, j; + printk("%8.8lx-%8.8lx ", + res->Segs[i].BasePage*ps, + (res->Segs[i].PageCount+res->Segs[i].BasePage)*ps-1); + for(j=15, first=1; j>=0; j--) { + if (mask&(1<DeviceId + + /* make sure we have residual data first */ + if ( res->ResidualLength == 0 ) + return; + printk("Residual: %ld devices\n", res->ActualNumDevices); + for ( i = 0; + i < res->ActualNumDevices ; + i++) + { + dev = &res->Devices[i]; + /* + * pci devices + */ + if ( did.BusId & PCIDEVICE ) + { + printk("PCI Device:"); + /* unknown vendor */ + if ( !strncmp( "Unknown", pci_strvendor(did.DevId>>16), 7) ) + printk(" id %08lx types %d/%d", did.DevId, + did.BaseType, did.SubType); + /* known vendor */ + else + printk(" %s %s", + pci_strvendor(did.DevId>>16), + pci_strdev(did.DevId>>16, + did.DevId&0xffff) + ); + + if ( did.BusId & PNPISADEVICE ) + { + printk(" pnp:"); + /* get pnp info on the device */ + pkt = (union _PnP_TAG_PACKET *) + &res->DevicePnPHeap[dev->AllocatedOffset]; + for (; pkt->S1_Pack.Tag != DF_END_TAG; + pkt++ ) + { + if ( (pkt->S1_Pack.Tag == S4_Packet) || + (pkt->S1_Pack.Tag == S4_Packet_flags) ) + printk(" irq %02x%02x", + pkt->S4_Pack.IRQMask[0], + pkt->S4_Pack.IRQMask[1]); + } + } + printk("\n"); + continue; + } + /* + * isa devices + */ + if ( did.BusId & ISADEVICE ) + { + printk("ISA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * eisa devices + */ + if ( did.BusId & EISADEVICE ) + { + printk("EISA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * proc bus devices + */ + if ( did.BusId & PROCESSORDEVICE ) + { + printk("ProcBus Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * pcmcia devices + */ + if ( did.BusId & PCMCIADEVICE ) + { + printk("PCMCIA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + printk("Unknown bus access device: busid %lx\n", + did.BusId); + } +} +#endif + +/* Returns the device index in the residual data, + any of the search items may be set as -1 for wildcard, + DevID number field (second halfword) is big endian ! + + Examples: + - search for the Interrupt controller (8259 type), 2 methods: + 1) i8259 = residual_find_device(~0, + NULL, + SystemPeripheral, + ProgrammableInterruptController, + ISA_PIC, + 0); + 2) i8259 = residual_find_device(~0, "PNP0000", -1, -1, -1, 0) + + - search for the first two serial devices, whatever their type) + iserial1 = residual_find_device(~0,NULL, + CommunicationsDevice, + RS232Device, + -1, 0) + iserial2 = residual_find_device(~0,NULL, + CommunicationsDevice, + RS232Device, + -1, 1) + - but search for typical COM1 and COM2 is not easy due to the + fact that the interface may be anything and the name "PNP0500" or + "PNP0501". Quite bad. + +*/ + +/* devid are easier to uncompress than to compress, so to minimize bloat +in this rarely used area we unencode and compare */ + +/* in residual data number is big endian in the device table and +little endian in the heap, so we use two parameters to avoid writing +two very similar functions */ + +static int __init same_DevID(unsigned short vendor, + unsigned short Number, + char * str) +{ + static unsigned const char hexdigit[]="0123456789ABCDEF"; + if (strlen(str)!=7) return 0; + if ( ( ((vendor>>10)&0x1f)+'A'-1 == str[0]) && + ( ((vendor>>5)&0x1f)+'A'-1 == str[1]) && + ( (vendor&0x1f)+'A'-1 == str[2]) && + (hexdigit[(Number>>12)&0x0f] == str[3]) && + (hexdigit[(Number>>8)&0x0f] == str[4]) && + (hexdigit[(Number>>4)&0x0f] == str[5]) && + (hexdigit[Number&0x0f] == str[6]) ) return 1; + return 0; +} + +PPC_DEVICE __init *residual_find_device(unsigned long BusMask, + unsigned char * DevID, + int BaseType, + int SubType, + int Interface, + int n) +{ + int i; + if ( !res->ResidualLength ) return NULL; + for (i=0; iActualNumDevices; i++) { +#define Dev res->Devices[i].DeviceId + if ( (Dev.BusId&BusMask) && + (BaseType==-1 || Dev.BaseType==BaseType) && + (SubType==-1 || Dev.SubType==SubType) && + (Interface==-1 || Dev.Interface==Interface) && + (DevID==NULL || same_DevID((Dev.DevId>>16)&0xffff, + Dev.DevId&0xffff, DevID)) && + !(n--) ) return res->Devices+i; +#undef Dev + } + return 0; +} + +PPC_DEVICE __init *residual_find_device_id(unsigned long BusMask, + unsigned short DevID, + int BaseType, + int SubType, + int Interface, + int n) +{ + int i; + if ( !res->ResidualLength ) return NULL; + for (i=0; iActualNumDevices; i++) { +#define Dev res->Devices[i].DeviceId + if ( (Dev.BusId&BusMask) && + (BaseType==-1 || Dev.BaseType==BaseType) && + (SubType==-1 || Dev.SubType==SubType) && + (Interface==-1 || Dev.Interface==Interface) && + (DevID==0xffff || (Dev.DevId&0xffff) == DevID) && + !(n--) ) return res->Devices+i; +#undef Dev + } + return 0; +} + +PnP_TAG_PACKET *PnP_find_packet(unsigned char *p, + unsigned packet_tag, + int n) +{ + unsigned mask, masked_tag, size; + if(!p) return 0; + if (tag_type(packet_tag)) mask=0xff; else mask=0xF8; + masked_tag = packet_tag&mask; + for(; *p != END_TAG; p+=size) { + if ((*p & mask) == masked_tag && !(n--)) + return (PnP_TAG_PACKET *) p; + if (tag_type(*p)) + size=ld_le16((unsigned short *)(p+1))+3; + else + size=tag_small_count(*p)+1; + } + return 0; /* not found */ +} + +PnP_TAG_PACKET __init *PnP_find_small_vendor_packet(unsigned char *p, + unsigned packet_type, + int n) +{ + int next=0; + while (p) { + p = (unsigned char *) PnP_find_packet(p, 0x70, next); + if (p && p[1]==packet_type && !(n--)) + return (PnP_TAG_PACKET *) p; + next = 1; + }; + return 0; /* not found */ +} + +PnP_TAG_PACKET __init *PnP_find_large_vendor_packet(unsigned char *p, + unsigned packet_type, + int n) +{ + int next=0; + while (p) { + p = (unsigned char *) PnP_find_packet(p, 0x84, next); + if (p && p[3]==packet_type && !(n--)) + return (PnP_TAG_PACKET *) p; + next = 1; + }; + return 0; /* not found */ +} diff -Nru a/arch/ppc/platforms/rpxclassic.h b/arch/ppc/platforms/rpxclassic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/rpxclassic.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,104 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ + +/* + * A collection of structures, addresses, and values associated with + * the RPCG RPX-Classic board. Copied from the RPX-Lite stuff. + * + * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) + */ +#ifdef __KERNEL__ +#ifndef __MACH_RPX_DEFS +#define __MACH_RPX_DEFS + +#include + +#ifndef __ASSEMBLY__ +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in Hz */ + unsigned char bi_enetaddr[6]; + unsigned int bi_baudrate; +} bd_t; + +extern bd_t m8xx_board_info; + +/* Memory map is configured by the PROM startup. + * We just map a few things we need. The CSR is actually 4 byte-wide + * registers that can be accessed as 8-, 16-, or 32-bit values. + */ +#define PCI_ISA_IO_ADDR ((unsigned)0x80000000) +#define PCI_ISA_IO_SIZE ((uint)(512 * 1024 * 1024)) +#define PCI_ISA_MEM_ADDR ((unsigned)0xc0000000) +#define PCI_ISA_MEM_SIZE ((uint)(512 * 1024 * 1024)) +#define RPX_CSR_ADDR ((uint)0xfa400000) +#define RPX_CSR_SIZE ((uint)(4 * 1024)) +#define IMAP_ADDR ((uint)0xfa200000) +#define IMAP_SIZE ((uint)(64 * 1024)) +#define PCI_CSR_ADDR ((uint)0x80000000) +#define PCI_CSR_SIZE ((uint)(64 * 1024)) +#define PCMCIA_MEM_ADDR ((uint)0xe0000000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) +#define PCMCIA_IO_ADDR ((uint)0xe4000000) +#define PCMCIA_IO_SIZE ((uint)(4 * 1024)) +#define PCMCIA_ATTRB_ADDR ((uint)0xe8000000) +#define PCMCIA_ATTRB_SIZE ((uint)(4 * 1024)) + +/* Things of interest in the CSR. +*/ +#define BCSR0_ETHEN ((uint)0x80000000) +#define BCSR0_ETHLPBK ((uint)0x40000000) +#define BCSR0_COLTESTDIS ((uint)0x20000000) +#define BCSR0_FULLDPLXDIS ((uint)0x10000000) +#define BCSR0_ENFLSHSEL ((uint)0x04000000) +#define BCSR0_FLASH_SEL ((uint)0x02000000) +#define BCSR0_ENMONXCVR ((uint)0x01000000) + +#define BCSR0_PCMCIAVOLT ((uint)0x000f0000) /* CLLF */ +#define BCSR0_PCMCIA3VOLT ((uint)0x000a0000) /* CLLF */ +#define BCSR0_PCMCIA5VOLT ((uint)0x00060000) /* CLLF */ + +#define BCSR1_IPB5SEL ((uint)0x00100000) +#define BCSR1_PCVCTL4 ((uint)0x00080000) +#define BCSR1_PCVCTL5 ((uint)0x00040000) +#define BCSR1_PCVCTL6 ((uint)0x00020000) +#define BCSR1_PCVCTL7 ((uint)0x00010000) + +#define BCSR2_EN232XCVR ((uint)0x00008000) +#define BCSR2_QSPACESEL ((uint)0x00004000) +#define BCSR2_FETHLEDMODE ((uint)0x00000800) /* CLLF */ + +#if defined(CONFIG_HTDMSOUND) +#include +#endif + +/* define IO_BASE for pcmcia, CLLF only */ +#if !defined(CONFIG_PCI) +#define _IO_BASE 0x80000000 +#define _IO_BASE_SIZE 0x1000 + +/* for pcmcia sandisk */ +#ifdef CONFIG_IDE +#define MAX_HWIFS 1 +#define ide_request_irq(irq,hand,flg,dev,id) request_8xxirq((irq),(hand),(flg),(dev),(id)) +#endif +#endif + +/* Interrupt level assignments. +*/ +#define FEC_INTERRUPT SIU_LEVEL1 /* FEC interrupt */ + +#endif /* !__ASSEMBLY__ */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/rpxhiox.h b/arch/ppc/platforms/rpxhiox.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/rpxhiox.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,45 @@ +/* + * BK Id: SCCS/s.rpxhiox.h 1.3 05/17/01 18:14:25 cort + */ + +/* + * The Embedded Planet HIOX expansion card definitions. + * There were a few different versions of these cards, but only + * the one that escaped real production is defined here. + * + * Copyright (c) 2000 Dan Malek (dmalek@jlc.net) + */ +#ifndef __MACH_RPX_HIOX_DEFS +#define __MACH_RPX_HIOX_DEFS + +#define HIOX_CSR_ADDR ((uint)0xfac00000) +#define HIOX_CSR_SIZE ((uint)(4 * 1024)) +#define HIOX_CSR0_ADDR HIOX_CSR_ADDR +#define HIOX_CSR4_ADDR ((uint)0xfac00004) + +#define HIOX_CSR0_DEFAULT ((uint)0x380f3c00) +#define HIOX_CSR0_ENSCC2 ((uint)0x80000000) +#define HIOX_CSR0_ENSMC2 ((uint)0x04000000) +#define HIOX_CSR0_ENVDOCLK ((uint)0x02000000) +#define HIOX_CSR0_VDORST_HL ((uint)0x01000000) +#define HIOX_CSR0_RS232SEL ((uint)0x0000c000) +#define HIOX_CSR0_SCC3SEL ((uint)0x0000c000) +#define HIOX_CSR0_SMC1SEL ((uint)0x00008000) +#define HIOX_CSR0_SCC1SEL ((uint)0x00004000) +#define HIOX_CSR0_ENTOUCH ((uint)0x00000080) +#define HIOX_CSR0_PDOWN100 ((uint)0x00000060) +#define HIOX_CSR0_PDOWN10 ((uint)0x00000040) +#define HIOX_CSR0_PDOWN1 ((uint)0x00000020) +#define HIOX_CSR0_TSELSPI ((uint)0x00000010) +#define HIOX_CSR0_TIRQSTAT ((uint)0x00000008) +#define HIOX_CSR4_DEFAULT ((uint)0x00000000) +#define HIOX_CSR4_ENTIRQ2 ((uint)0x20000000) +#define HIOX_CSR4_ENTIRQ3 ((uint)0x10000000) +#define HIOX_CSR4_ENAUDIO ((uint)0x00000080) +#define HIOX_CSR4_RSTAUDIO ((uint)0x00000040) /* 0 == reset */ +#define HIOX_CSR4_AUDCLKHI ((uint)0x00000020) +#define HIOX_CSR4_AUDSPISEL ((uint)0x00000010) +#define HIOX_CSR4_AUDIRQSTAT ((uint)0x00000008) +#define HIOX_CSR4_AUDCLKSEL ((uint)0x00000007) + +#endif diff -Nru a/arch/ppc/platforms/rpxlite.h b/arch/ppc/platforms/rpxlite.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/rpxlite.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,84 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ + +/* + * A collection of structures, addresses, and values associated with + * the RPCG RPX-Lite board. Copied from the MBX stuff. + * + * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) + */ +#ifdef __KERNEL__ +#ifndef __MACH_RPX_DEFS +#define __MACH_RPX_DEFS + +#include + +#ifndef __ASSEMBLY__ +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in Hz */ + unsigned char bi_enetaddr[6]; + unsigned int bi_baudrate; +} bd_t; + +extern bd_t m8xx_board_info; + +/* Memory map is configured by the PROM startup. + * We just map a few things we need. The CSR is actually 4 byte-wide + * registers that can be accessed as 8-, 16-, or 32-bit values. + */ +#define RPX_CSR_ADDR ((uint)0xfa400000) +#define RPX_CSR_SIZE ((uint)(4 * 1024)) +#define IMAP_ADDR ((uint)0xfa200000) +#define IMAP_SIZE ((uint)(64 * 1024)) +#define PCMCIA_MEM_ADDR ((uint)0x04000000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) +#define PCMCIA_IO_ADDR ((uint)0x04400000) +#define PCMCIA_IO_SIZE ((uint)(4 * 1024)) + +/* Things of interest in the CSR. +*/ +#define BCSR0_ETHEN ((uint)0x80000000) +#define BCSR0_ETHLPBK ((uint)0x40000000) +#define BCSR0_COLTESTDIS ((uint)0x20000000) +#define BCSR0_FULLDPLXDIS ((uint)0x10000000) +#define BCSR0_LEDOFF ((uint)0x08000000) +#define BCSR0_USBDISABLE ((uint)0x04000000) +#define BCSR0_USBHISPEED ((uint)0x02000000) +#define BCSR0_USBPWREN ((uint)0x01000000) +#define BCSR0_PCMCIAVOLT ((uint)0x000f0000) +#define BCSR0_PCMCIA3VOLT ((uint)0x000a0000) +#define BCSR0_PCMCIA5VOLT ((uint)0x00060000) + +#define BCSR1_IPB5SEL ((uint)0x00100000) +#define BCSR1_PCVCTL4 ((uint)0x00080000) +#define BCSR1_PCVCTL5 ((uint)0x00040000) +#define BCSR1_PCVCTL6 ((uint)0x00020000) +#define BCSR1_PCVCTL7 ((uint)0x00010000) + +#if defined(CONFIG_HTDMSOUND) +#include +#endif +#endif /* !__ASSEMBLY__ */ + +/* define IO_BASE for pcmcia */ +#define _IO_BASE 0x80000000 +#define _IO_BASE_SIZE 0x1000 + +#ifdef CONFIG_IDE +#define MAX_HWIFS 1 +#define ide_request_irq(irq,hand,flg,dev,id) request_8xxirq((irq),(hand),(flg),(dev),(id)) +#endif + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/rpxsuper.h b/arch/ppc/platforms/rpxsuper.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/rpxsuper.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,72 @@ +/* + * A collection of structures, addresses, and values associated with + * the Embedded Planet RPX6 (or RPX Super) MPC8260 board. + * Copied from the RPX-Classic and SBS8260 stuff. + * + * Copyright (c) 2001 Dan Malek + */ +#ifdef __KERNEL__ +#ifndef __ASM_PLATFORMS_RPXSUPER_H__ +#define __ASM_PLATFORMS_RPXSUPER_H__ + +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_nvsize; /* NVRAM size in bytes (can be 0) */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in MHz */ + unsigned int bi_cpmfreq; /* CPM Freq, in MHz */ + unsigned int bi_brgfreq; /* BRG Freq, in MHz */ + unsigned int bi_vco; /* VCO Out from PLL */ + unsigned int bi_baudrate; /* Default console baud rate */ + unsigned int bi_immr; /* IMMR when called from boot rom */ + unsigned char bi_enetaddr[6]; +} bd_t; + +extern bd_t m8xx_board_info; + +/* Memory map is configured by the PROM startup. + * We just map a few things we need. The CSR is actually 4 byte-wide + * registers that can be accessed as 8-, 16-, or 32-bit values. + */ +#define IMAP_ADDR ((uint)0xf0000000) +#define RPX_CSR_ADDR ((uint)0xfa000000) +#define RPX_CSR_SIZE ((uint)(512 * 1024)) +#define RPX_NVRTC_ADDR ((uint)0xfa080000) +#define RPX_NVRTC_SIZE ((uint)(512 * 1024)) + +/* The RPX6 has 16, byte wide control/status registers. + * Not all are used (yet). + */ +extern volatile u_char *rpx6_csr_addr; + +/* Things of interest in the CSR. +*/ +#define BCSR0_ID_MASK ((u_char)0xf0) /* Read only */ +#define BCSR0_SWITCH_MASK ((u_char)0x0f) /* Read only */ +#define BCSR1_XCVR_SMC1 ((u_char)0x80) +#define BCSR1_XCVR_SMC2 ((u_char)0x40) +#define BCSR2_FLASH_WENABLE ((u_char)0x20) +#define BCSR2_NVRAM_ENABLE ((u_char)0x10) +#define BCSR2_ALT_IRQ2 ((u_char)0x08) +#define BCSR2_ALT_IRQ3 ((u_char)0x04) +#define BCSR2_PRST ((u_char)0x02) /* Force reset */ +#define BCSR2_ENPRST ((u_char)0x01) /* Enable POR */ +#define BCSR3_MODCLK_MASK ((u_char)0xe0) +#define BCSR3_ENCLKHDR ((u_char)0x10) +#define BCSR3_LED5 ((u_char)0x04) /* 0 == on */ +#define BCSR3_LED6 ((u_char)0x02) /* 0 == on */ +#define BCSR3_LED7 ((u_char)0x01) /* 0 == on */ +#define BCSR4_EN_PHY ((u_char)0x80) /* Enable PHY */ +#define BCSR4_EN_MII ((u_char)0x40) /* Enable PHY */ +#define BCSR4_MII_READ ((u_char)0x04) +#define BCSR4_MII_MDC ((u_char)0x02) +#define BCSR4_MII_MDIO ((u_char)0x02) +#define BCSR13_FETH_IRQMASK ((u_char)0xf0) +#define BCSR15_FETH_IRQ ((u_char)0x20) + +#endif /* __ASM_PLATFORMS_RPXSUPER_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/sandpoint.h b/arch/ppc/platforms/sandpoint.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/sandpoint.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,70 @@ +/* + * arch/ppc/platforms/sandpoint.h + * + * Definitions for Motorola SPS Sandpoint Test Platform + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2000, 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * Sandpoint uses the CHRP map (Map B). + */ + +#ifndef __PPC_PLATFORMS_SANDPOINT_H +#define __PPC_PLATFORMS_SANDPOINT_H + +#ifdef CONFIG_SANDPOINT_X3 +#define SANDPOINT_SIO_SLOT 0 /* Cascaded from EPIC IRQ 0 */ +#if 0 +/* The Sandpoint X3 allows the IDE interrupt to be directly connected + * from the Windbond (PCI INTC or INTD) to the serial EPIC. Someday + * we should try this, but it was easier to use the existing 83c553 + * initialization than change it to route the different interrupts :-). + * -- Dan + */ +#define SANDPOINT_IDE_INT0 23 /* EPIC 7 */ +#define SANDPOINT_IDE_INT1 24 /* EPIC 8 */ +#else +#define SANDPOINT_IDE_INT0 14 /* 8259 Test */ +#define SANDPOINT_IDE_INT1 15 /* 8259 Test */ +#endif +#else + /* + * Define the PCI slot that the 8259 is sharing interrupts with. + * Valid values are 1 (PCI slot 2) and 2 (PCI slot 3). + */ +#define SANDPOINT_SIO_SLOT 1 + +/* ...and for the IDE from the 8259.... +*/ +#define SANDPOINT_IDE_INT0 14 +#define SANDPOINT_IDE_INT1 15 +#endif + +#define SANDPOINT_SIO_IRQ (SANDPOINT_SIO_SLOT + NUM_8259_INTERRUPTS) + +/* + * The sandpoint boards have processor modules that either have an 8240 or + * an MPC107 host bridge on them. These bridges have an IDSEL line that allows + * them to respond to PCI transactions as if they were a normal PCI devices. + * However, the processor on the processor side of the bridge can not reach + * out onto the PCI bus and then select the bridge or bad things will happen + * (documented in the 8240 and 107 manuals). + * Because of this, we always skip the bridge PCI device when accessing the + * PCI bus. The PCI slot that the bridge occupies is defined by the macro + * below. + */ +#define SANDPOINT_HOST_BRIDGE_IDSEL 12 + + +void sandpoint_find_bridges(void); + +#endif /* __PPC_PLATFORMS_SANDPOINT_H */ diff -Nru a/arch/ppc/platforms/sandpoint_pci.c b/arch/ppc/platforms/sandpoint_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/sandpoint_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,183 @@ +/* + * arch/ppc/platforms/sandpoint_pci.c + * + * PCI setup routines for the Motorola SPS Sandpoint Test Platform + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2000, 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sandpoint.h" + +/* + * Motorola SPS Sandpoint interrupt routing. + */ +static inline int +sandpoint_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { SANDPOINT_SIO_IRQ, + 0, 0, 0 }, /* IDSEL 11 - i8259 on Winbond */ + { 0, 0, 0, 0 }, /* IDSEL 12 - unused */ +#ifdef CONFIG_SANDPOINT_X3 +#if 0 /* This is what it _should_ look like -- Dan */ + { 17, 20, 19, 18 }, /* IDSEL 13 - PCI slot 1 */ + { 18, 17, 20, 19 }, /* IDSEL 14 - PCI slot 2 */ + { 19, 18, 17, 20 }, /* IDSEL 15 - PCI slot 3 */ + { 20, 19, 18, 17 }, /* IDSEL 16 - PCI slot 4 */ +#else + { 18, 21, 20, 19 }, /* IDSEL 13 - PCI slot 1 */ + { 19, 18, 21, 20 }, /* IDSEL 14 - PCI slot 2 */ + { 20, 19, 18, 21 }, /* IDSEL 15 - PCI slot 3 */ + { 21, 20, 19, 18 }, /* IDSEL 16 - PCI slot 4 */ +#endif +#else + { 16, 19, 18, 17 }, /* IDSEL 13 - PCI slot 1 */ + { 17, 16, 19, 18 }, /* IDSEL 14 - PCI slot 2 */ + { 18, 17, 16, 19 }, /* IDSEL 15 - PCI slot 3 */ + { 19, 18, 17, 16 }, /* IDSEL 16 - PCI slot 4 */ +#endif + }; + + const long min_idsel = 11, max_idsel = 16, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +static void __init +sandpoint_setup_winbond_83553(struct pci_controller *hose) +{ + int devfn; + + /* + * Route IDE interrupts directly to the 8259's IRQ 14 & 15. + * We can't route the IDE interrupt to PCI INTC# or INTD# because those + * woule interfere with the PMC's INTC# and INTD# lines. + */ + /* + * Winbond Fcn 0 + */ + devfn = PCI_DEVFN(11,0); + + early_write_config_byte(hose, + 0, + devfn, + 0x43, /* IDE Interrupt Routing Control */ + 0xef); + early_write_config_word(hose, + 0, + devfn, + 0x44, /* PCI Interrupt Routing Control */ + 0x0000); + + /* Want ISA memory cycles to be forwarded to PCI bus */ + early_write_config_byte(hose, + 0, + devfn, + 0x48, /* ISA-to-PCI Addr Decoder Control */ + 0xf0); + + /* Enable RTC and Keyboard address locations. */ + early_write_config_byte(hose, + 0, + devfn, + 0x4d, /* Chip Select Control Register */ + 0x00); + + /* Enable Port 92. */ + early_write_config_byte(hose, + 0, + devfn, + 0x4e, /* AT System Control Register */ + 0x06); + /* + * Winbond Fcn 1 + */ + devfn = PCI_DEVFN(11,1); + + /* Put IDE controller into native mode. */ + early_write_config_byte(hose, + 0, + devfn, + 0x09, /* Programming interface Register */ + 0x8f); + + /* Init IRQ routing, enable both ports, disable fast 16 */ + early_write_config_dword(hose, + 0, + devfn, + 0x40, /* IDE Control/Status Register */ + 0x00ff0011); + return; +} + +static int +sandpoint_exclude_device(u_char bus, u_char devfn) +{ + if ((bus == 0) && (PCI_SLOT(devfn) == SANDPOINT_HOST_BRIDGE_IDSEL)) { + return PCIBIOS_DEVICE_NOT_FOUND; + } + else { + return PCIBIOS_SUCCESSFUL; + } +} + +void __init +sandpoint_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + if (mpc10x_bridge_init(hose, + MPC10X_MEM_MAP_B, + MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE) == 0) { + + /* Do early winbond init, then scan PCI bus */ + sandpoint_setup_winbond_83553(hose); + ppc_md.pci_exclude_device = sandpoint_exclude_device; + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = NULL; + ppc_md.pcibios_fixup_bus = NULL; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = sandpoint_map_irq; + } + else { + if (ppc_md.progress) + ppc_md.progress("Bridge init failed", 0x100); + printk("Host bridge init failed\n"); + } + + return; +} diff -Nru a/arch/ppc/platforms/sandpoint_serial.h b/arch/ppc/platforms/sandpoint_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/sandpoint_serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,51 @@ +/* + * include/asm-ppc/sandpoint_serial.h + * + * Definitions for Motorola SPS Sandpoint Test Platform + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __ASMPPC_SANDPOINT_SERIAL_H +#define __ASMPPC_SANDPOINT_SERIAL_H + +#include + +#define SANDPOINT_SERIAL_0 0xfe0003f8 +#define SANDPOINT_SERIAL_1 0xfe0002f8 + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 2 +#endif + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD ( 1843200 / 16 ) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, SANDPOINT_SERIAL_0, 4, STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (u8 *)SANDPOINT_SERIAL_0, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, SANDPOINT_SERIAL_1, 3, STD_COM_FLAGS, /* ttyS1 */ \ + iomem_base: (u8 *)SANDPOINT_SERIAL_1, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +#endif /* __ASMPPC_SANDPOINT_SERIAL_H */ diff -Nru a/arch/ppc/platforms/sandpoint_setup.c b/arch/ppc/platforms/sandpoint_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/sandpoint_setup.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,694 @@ +/* + * arch/ppc/platforms/sandpoint_setup.c + * + * Board setup routines for the Motorola SPS Sandpoint Test Platform. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2000, 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * This file adds support for the Motorola SPS Sandpoint Test Platform. + * These boards have a PPMC slot for the processor so any combination + * of cpu and host bridge can be attached. This port is for an 8240 PPMC + * module from Motorola SPS and other closely related cpu/host bridge + * combinations (e.g., 750/755/7400 with MPC107 host bridge). + * The sandpoint itself has a Windbond 83c553 (PCI-ISA bridge, 2 DMA ctlrs, 2 + * cascaded 8259 interrupt ctlrs, 8254 Timer/Counter, and an IDE ctlr), a + * National 87308 (RTC, 2 UARTs, Keyboard & mouse ctlrs, and a floppy ctlr), + * and 4 PCI slots (only 2 of which are usable; the other 2 are keyed for 3.3V + * but are really 5V). + * + * The firmware on the sandpoint is called DINK (not my acronym :). This port + * depends on DINK to do some basic initialization (e.g., initialize the memory + * ctlr) and to ensure that the processor is using MAP B (CHRP map). + * + * The switch settings for the Sandpoint board MUST be as follows: + * S3: down + * S4: up + * S5: up + * S6: down + * + * 'down' is in the direction from the PCI slots towards the PPMC slot; + * 'up' is in the direction from the PPMC slot towards the PCI slots. + * Be careful, the way the sandpoint board is installed in XT chasses will + * make the directions reversed. + * + * Since Motorola listened to our suggestions for improvement, we now have + * the Sandpoint X3 board. All of the PCI slots are available, it uses + * the serial interrupt interface (just a hardware thing we need to + * configure properly). + * + * Use the default X3 switch settings. The interrupts are then: + * EPIC Source + * 0 SIOINT (8259, active low) + * 1 PCI #1 + * 2 PCI #2 + * 3 PCI #3 + * 4 PCI #4 + * 7 Winbond INTC (IDE interrupt) + * 8 Winbond INTD (IDE interrupt) + * + */ +#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 +#include +#include +#include +#include + +#include "sandpoint.h" + +extern u_int openpic_irq(void); +extern void openpic_eoi(void); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; + +static void sandpoint_halt(void); + + +/* + * *** IMPORTANT *** + * + * The first 16 entries of 'sandpoint_openpic_initsenses[]' are there and + * initialized to 0 on purpose. DO NOT REMOVE THEM as the 'offset' parameter + * of 'openpic_init()' does not work for the sandpoint because the 8259 + * interrupt is NOT routed to the EPIC's IRQ 0 AND the EPIC's IRQ 0's offset is + * the same as a normal openpic's IRQ 16 offset. + */ +static u_char sandpoint_openpic_initsenses[] __initdata = { + 0, /* 0-15 not used by EPCI but by 8259 (std PC-type IRQs) */ + 0, /* 1 */ + 0, /* 2 */ + 0, /* 3 */ + 0, /* 4 */ + 0, /* 5 */ + 0, /* 6 */ + 0, /* 7 */ + 0, /* 8 */ + 0, /* 9 */ + 0, /* 10 */ + 0, /* 11 */ + 0, /* 12 */ + 0, /* 13 */ + 0, /* 14 */ + 0, /* 15 */ +#ifdef CONFIG_SANDPOINT_X3 + 1, /* 16: EPIC IRQ 0: Active Low -- SIOINT (8259) */ + 0, /* AACK! Shouldn't need this.....see sandpoint_pci.c for more info */ + 1, /* 17: EPIC IRQ 1: Active Low -- PCI Slot 1 */ + 1, /* 18: EPIC IRQ 2: Active Low -- PCI Slot 2 */ + 1, /* 19: EPIC IRQ 3: Active Low -- PCI Slot 3 */ + 1, /* 20: EPIC IRQ 4: Active Low -- PCI Slot 4 */ + 0, /* 21 -- Unused */ + 0, /* 22 -- Unused */ + 1, /* 23 -- IDE (Winbond INT C) */ + 1, /* 24 -- IDE (Winbond INT D) */ + /* 35 - 31 (EPIC 9 - 15) Unused */ +#else + 1, /* 16: EPIC IRQ 0: Active Low -- PCI intrs */ + 1, /* 17: EPIC IRQ 1: Active Low -- PCI (possibly 8259) intrs */ + 1, /* 18: EPIC IRQ 2: Active Low -- PCI (possibly 8259) intrs */ + 1 /* 19: EPIC IRQ 3: Active Low -- PCI intrs */ + /* 20: EPIC IRQ 4: Not used */ +#endif +}; + +static void __init +sandpoint_setup_arch(void) +{ + loops_per_jiffy = 100000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00FF); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0301); /* /dev/hda1 IDE disk */ +#endif + + /* Lookup PCI host bridges */ + sandpoint_find_bridges(); + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + printk("Motorola SPS Sandpoint Test Platform\n"); + printk("Sandpoint port (C) 2000, 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + /* The Sandpoint rom doesn't enable any caches. Do that now. + * The 7450 portion will also set up the L3s once I get enough + * information do do so. If the processor running doesn't have + * and L2, the _set_L2CR is a no-op. + */ + if (cur_cpu_spec[0]->cpu_features & CPU_FTR_SPEC7450) { + /* Just enable L2, the bits are different from others. + */ + _set_L2CR(L2CR_L2E); + } + else { + /* The magic number for Sandpoint/74xx PrPMCs. + */ + _set_L2CR(0xbd014000); + } +} + +#define SANDPOINT_87308_CFG_ADDR 0x15c +#define SANDPOINT_87308_CFG_DATA 0x15d + +#define SANDPOINT_87308_CFG_INB(addr, byte) { \ + outb((addr), SANDPOINT_87308_CFG_ADDR); \ + (byte) = inb(SANDPOINT_87308_CFG_DATA); \ +} + +#define SANDPOINT_87308_CFG_OUTB(addr, byte) { \ + outb((addr), SANDPOINT_87308_CFG_ADDR); \ + outb((byte), SANDPOINT_87308_CFG_DATA); \ +} + +#define SANDPOINT_87308_SELECT_DEV(dev_num) { \ + SANDPOINT_87308_CFG_OUTB(0x07, (dev_num)); \ +} + +#define SANDPOINT_87308_DEV_ENABLE(dev_num) { \ + SANDPOINT_87308_SELECT_DEV(dev_num); \ + SANDPOINT_87308_CFG_OUTB(0x30, 0x01); \ +} + +/* + * Initialize the ISA devices on the Nat'l PC87308VUL SuperIO chip. + */ +static void __init +sandpoint_setup_natl_87308(void) +{ + u_char reg; + + /* + * Enable all the devices on the Super I/O chip. + */ + SANDPOINT_87308_SELECT_DEV(0x00); /* Select kbd logical device */ + SANDPOINT_87308_CFG_OUTB(0xf0, 0x00); /* Set KBC clock to 8 Mhz */ + SANDPOINT_87308_DEV_ENABLE(0x00); /* Enable keyboard */ + SANDPOINT_87308_DEV_ENABLE(0x01); /* Enable mouse */ + SANDPOINT_87308_DEV_ENABLE(0x02); /* Enable rtc */ + SANDPOINT_87308_DEV_ENABLE(0x03); /* Enable fdc (floppy) */ + SANDPOINT_87308_DEV_ENABLE(0x04); /* Enable parallel */ + SANDPOINT_87308_DEV_ENABLE(0x05); /* Enable UART 2 */ + SANDPOINT_87308_CFG_OUTB(0xf0, 0x82); /* Enable bank select regs */ + SANDPOINT_87308_DEV_ENABLE(0x06); /* Enable UART 1 */ + SANDPOINT_87308_CFG_OUTB(0xf0, 0x82); /* Enable bank select regs */ + + /* Set up floppy in PS/2 mode */ + outb(0x09, SIO_CONFIG_RA); + reg = inb(SIO_CONFIG_RD); + reg = (reg & 0x3F) | 0x40; + outb(reg, SIO_CONFIG_RD); + outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ + + return; +} + +/* + * Fix IDE interrupts. + */ +static void __init +sandpoint_fix_winbond_83553(void) +{ + /* Make all 8259 interrupt level sensitive */ + outb(0xf8, 0x4d0); + outb(0xde, 0x4d1); + + return; +} + +static void __init +sandpoint_init2(void) +{ + /* Do Sandpoint board specific initialization. */ + sandpoint_fix_winbond_83553(); + sandpoint_setup_natl_87308(); + + request_region(0x00,0x20,"dma1"); + request_region(0x20,0x20,"pic1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xa0,0x20,"pic2"); + request_region(0xc0,0x20,"dma2"); + + return; +} + +/* + * Interrupt setup and service. Interrrupts on the Sandpoint come + * from the four PCI slots plus the 8259 in the Winbond Super I/O (SIO). + * These interrupts are sent to one of four IRQs on the EPIC. + * The SIO shares its interrupt with either slot 2 or slot 3 (INTA#). + * Slot numbering is confusing. Sometimes in the documentation they + * use 0,1,2,3 and others 1,2,3,4. We will use slots 1,2,3,4 and + * map this to IRQ 16, 17, 18, 19. + * For Sandpoint X3, this has been better designed. The 8259 is + * cascaded from EPIC IRQ0, IRQ1-4 map to PCI slots 1-4, IDE is on + * EPIC 7 and 8. + */ +static void __init +sandpoint_init_IRQ(void) +{ + int i; + + /* + * 3 things cause us to jump through some hoops: + * 1) the EPIC on the 8240 & 107 are not full-blown openpic pic's + * 2) the 8259 is NOT cascaded on the openpic IRQ 0 + * 3) the 8259 shares its interrupt line with some PCI interrupts. + * + * What we'll do is set up the 8259 to be level sensitive, active low + * just like a PCI device. Then, when an interrupt on the IRQ that is + * shared with the 8259 comes in, we'll take a peek at the 8259 to see + * it its generating an interrupt. If it is, we'll handle the 8259 + * interrupt. Otherwise, we'll handle it just like a normal PCI + * interrupt. This does give the 8259 interrupts a higher priority + * than the EPIC ones--hopefully, not a problem. + */ + OpenPIC_InitSenses = sandpoint_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(sandpoint_openpic_initsenses); + + openpic_init(1, 0, NULL, -1); + + /* + * openpic_init() has set up irq_desc[0-23] to be openpic + * interrupts. We need to set irq_desc[0-15] to be 8259 interrupts. + * We then need to request and enable the 8259 irq. + */ + for(i=0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + if (request_irq(SANDPOINT_SIO_IRQ, no_action, SA_INTERRUPT, + "8259 cascade to EPIC", NULL)) { + + printk("Unable to get OpenPIC IRQ %d for cascade\n", + SANDPOINT_SIO_IRQ); + } + + i8259_init(NULL); +} + +static int +sandpoint_get_irq(struct pt_regs *regs) +{ + int irq, cascade_irq; + + irq = openpic_irq(); + + if (irq == SANDPOINT_SIO_IRQ) { + cascade_irq = i8259_poll(); + + if (cascade_irq != -1) { + irq = cascade_irq; + openpic_eoi(); + } + } + else if (irq == OPENPIC_VEC_SPURIOUS) { + irq = -1; + } + + return irq; +} + +static u32 +sandpoint_irq_cannonicalize(u32 irq) +{ + if (irq == 2) + { + return 9; + } + else + { + return irq; + } +} + +static ulong __init +sandpoint_find_end_of_memory(void) +{ + ulong size = 0; + +#if 0 /* Leave out until DINK sets mem ctlr correctly */ + size = mpc10x_get_mem_size(MPC10X_MEM_MAP_B); +#else + size = 32*1024*1024; +#endif + + return size; +} + +static void __init +sandpoint_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); +} + +/* + * Due to Sandpoint X2 errata, the Port 92 will not work. + */ +static void +sandpoint_restart(char *cmd) +{ + __cli(); + + /* Set exception prefix high - to the firmware */ + _nmask_and_or_msr(0, MSR_IP); + + /* Reset system via Port 92 */ + outb(0x00, 0x92); + outb(0x01, 0x92); + for(;;); /* Spin until reset happens */ +} + +static void +sandpoint_power_off(void) +{ + __cli(); + for(;;); /* No way to shut power off with software */ + /* NOTREACHED */ +} + +static void +sandpoint_halt(void) +{ + sandpoint_power_off(); + /* NOTREACHED */ +} + +static int +sandpoint_show_cpuinfo(struct seq_file *m) +{ + uint pvid; + + pvid = mfspr(PVR); + + seq_printf(m, "vendor\t\t: Motorola SPS\n"); + seq_printf(m, "machine\t\t: Sandpoint\n"); + seq_printf(m, "processor\t: PVID: 0x%x, vendor: %s\n", + pvid, (pvid & (1<<15) ? "IBM" : "Motorola")); + + return 0; +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE support. + */ +static int sandpoint_ide_ports_known = 0; +static ide_ioreg_t sandpoint_ide_regbase[MAX_HWIFS]; +static ide_ioreg_t sandpoint_ide_ctl_regbase[MAX_HWIFS]; +static ide_ioreg_t sandpoint_idedma_regbase; + +static void +sandpoint_ide_probe(void) +{ + struct pci_dev *pdev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_82C105, + NULL); + + if(pdev) { + sandpoint_ide_regbase[0]=pdev->resource[0].start; + sandpoint_ide_regbase[1]=pdev->resource[2].start; + sandpoint_ide_ctl_regbase[0]=pdev->resource[1].start; + sandpoint_ide_ctl_regbase[1]=pdev->resource[3].start; + sandpoint_idedma_regbase=pdev->resource[4].start; + } + + sandpoint_ide_ports_known = 1; + return; +} + +static int +sandpoint_ide_default_irq(ide_ioreg_t base) +{ + if (sandpoint_ide_ports_known == 0) + sandpoint_ide_probe(); + + if (base == sandpoint_ide_regbase[0]) + return SANDPOINT_IDE_INT0; + else if (base == sandpoint_ide_regbase[1]) + return SANDPOINT_IDE_INT1; + else + return 0; +} + +static ide_ioreg_t +sandpoint_ide_default_io_base(int index) +{ + if (sandpoint_ide_ports_known == 0) + sandpoint_ide_probe(); + + return sandpoint_ide_regbase[index]; +} + +static int +sandpoint_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +static void +sandpoint_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); + return; +} + +static void +sandpoint_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); + return; +} + +static void __init +sandpoint_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, int *irq) +{ + ide_ioreg_t reg = data_port; + uint alt_status_base; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = reg++; + } + + if (data_port == sandpoint_ide_regbase[0]) { + alt_status_base = sandpoint_ide_ctl_regbase[0] + 2; + hw->irq = 14; + } + else if (data_port == sandpoint_ide_regbase[1]) { + alt_status_base = sandpoint_ide_ctl_regbase[1] + 2; + hw->irq = 15; + } + else { + alt_status_base = 0; + hw->irq = 0; + } + + if (ctrl_port) { + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + } else { + hw->io_ports[IDE_CONTROL_OFFSET] = alt_status_base; + } + + if (irq != NULL) { + *irq = hw->irq; + } + + return; +} +#endif + +/* + * Set BAT 3 to map 0xf8000000 to end of physical memory space 1-to-1. + */ +static __inline__ void +sandpoint_set_bat(void) +{ + unsigned long bat3u, bat3l; + static int mapping_set = 0; + + if (!mapping_set) { + + __asm__ __volatile__( + " lis %0,0xf800\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x0ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (bat3u), "=r" (bat3l)); + + mapping_set = 1; + } + + return; +} + +#ifdef CONFIG_SERIAL_TEXT_DEBUG +#include +#include +#include + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in */ +}; + +static void +sandpoint_progress(char *s, unsigned short hex) +{ + volatile char c; + volatile unsigned long com_port; + u16 shift; + + com_port = rs_table[0].port; + shift = rs_table[0].iomem_reg_shift; + + while ((c = *s++) != 0) { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = c; + + if (c == '\n') { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = '\r'; + } + } +} +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +__init void sandpoint_setup_pci_ptrs(void); + +TODC_ALLOC(); + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* Map in board regs, etc. */ + sandpoint_set_bat(); + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = sandpoint_setup_arch; + ppc_md.show_cpuinfo = sandpoint_show_cpuinfo; + ppc_md.irq_cannonicalize = sandpoint_irq_cannonicalize; + ppc_md.init_IRQ = sandpoint_init_IRQ; + ppc_md.get_irq = sandpoint_get_irq; + ppc_md.init = sandpoint_init2; + + ppc_md.restart = sandpoint_restart; + ppc_md.power_off = sandpoint_power_off; + ppc_md.halt = sandpoint_halt; + + ppc_md.find_end_of_memory = sandpoint_find_end_of_memory; + ppc_md.setup_io_mappings = sandpoint_map_io; + + TODC_INIT(TODC_TYPE_PC97307, 0x70, 0x00, 0x71, 8); + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_mc146818_read_val; + ppc_md.nvram_write_val = todc_mc146818_write_val; + + ppc_md.heartbeat = NULL; + ppc_md.heartbeat_reset = 0; + ppc_md.heartbeat_count = 0; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = sandpoint_progress; +#else /* !CONFIG_SERIAL_TEXT_DEBUG */ + ppc_md.progress = NULL; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +#ifdef CONFIG_VT + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x54; +#endif +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_irq = sandpoint_ide_default_irq; + ppc_ide_md.default_io_base = sandpoint_ide_default_io_base; + ppc_ide_md.ide_check_region = sandpoint_ide_check_region; + ppc_ide_md.ide_request_region = sandpoint_ide_request_region; + ppc_ide_md.ide_release_region = sandpoint_ide_release_region; + ppc_ide_md.ide_init_hwif = sandpoint_ide_init_hwif_ports; +#endif + + return; +} diff -Nru a/arch/ppc/platforms/sbs8260.h b/arch/ppc/platforms/sbs8260.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/sbs8260.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,28 @@ +#ifndef __ASSEMBLY__ +/* Board information for various SBS 8260 cards, which should be generic for + * all 8260 boards. The IMMR is now given to us so the hard define + * will soon be removed. All of the clock values are computed from + * the configuration SCMR and the Power-On-Reset word. + */ + +#define IMAP_ADDR ((uint)0xfe000000) + + +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in MHz */ + unsigned int bi_cpmfreq; /* CPM Freq, in MHz */ + unsigned int bi_brgfreq; /* BRG Freq, in MHz */ + unsigned int bi_vco; /* VCO Out from PLL */ + unsigned int bi_baudrate; /* Default console baud rate */ + unsigned int bi_immr; /* IMMR when called from boot rom */ + unsigned char bi_enetaddr[6]; +} bd_t; + +extern bd_t m8xx_board_info; +#endif /* !__ASSEMBLY__ */ diff -Nru a/arch/ppc/platforms/sleep.S b/arch/ppc/platforms/sleep.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/sleep.S Tue Feb 19 18:08:57 2002 @@ -0,0 +1,447 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * This file contains sleep low-level functions for PowerBook G3. + * Copyright (C) 1999 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * and Paul Mackerras (paulus@samba.org). + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include + +#define MAGIC 0x4c617273 /* 'Lars' */ + +/* + * Structure for storing CPU registers on the stack. + */ +#define SL_SP 0 +#define SL_PC 4 +#define SL_MSR 8 +#define SL_SDR1 0xc +#define SL_SPRG0 0x10 /* 4 sprg's */ +#define SL_DBAT0 0x20 +#define SL_IBAT0 0x28 +#define SL_DBAT1 0x30 +#define SL_IBAT1 0x38 +#define SL_DBAT2 0x40 +#define SL_IBAT2 0x48 +#define SL_DBAT3 0x50 +#define SL_IBAT3 0x58 +#define SL_TB 0x60 +#define SL_HID0 0x68 +#define SL_HID1 0x6c +#define SL_MSSCR0 0x70 +#define SL_MSSSR0 0x74 +#define SL_ICTRL 0x78 +#define SL_LDSTCR 0x7c +#define SL_LDSTDB 0x80 +#define SL_R2 0x84 +#define SL_CR 0x88 +#define SL_R12 0x8c /* r12 to r31 */ +#define SL_SIZE (SL_R12 + 80) + + .text + .align 5 + +/* This gets called by via-pmu.c late during the sleep process. + * The PMU was already send the sleep command and will shut us down + * soon. We need to save all that is needed and setup the wakeup + * vector that will be called by the ROM on wakeup + */ +_GLOBAL(low_sleep_handler) + mflr r0 + stw r0,4(r1) + stwu r1,-SL_SIZE(r1) + mfcr r0 + stw r0,SL_CR(r1) + stw r2,SL_R2(r1) + stmw r12,SL_R12(r1) + + /* Save MSR & SDR1 */ + mfmsr r4 + stw r4,SL_MSR(r1) + mfsdr1 r4 + stw r4,SL_SDR1(r1) + + /* Get a stable timebase and save it */ +1: mftbu r4 + stw r4,SL_TB(r1) + mftb r5 + stw r5,SL_TB+4(r1) + mftbu r3 + cmpw r3,r4 + bne 1b + + /* Save SPRGs */ + mfsprg r4,0 + stw r4,SL_SPRG0(r1) + mfsprg r4,1 + stw r4,SL_SPRG0+4(r1) + mfsprg r4,2 + stw r4,SL_SPRG0+8(r1) + mfsprg r4,3 + stw r4,SL_SPRG0+12(r1) + + /* Save BATs */ + mfdbatu r4,0 + stw r4,SL_DBAT0(r1) + mfdbatl r4,0 + stw r4,SL_DBAT0+4(r1) + mfdbatu r4,1 + stw r4,SL_DBAT1(r1) + mfdbatl r4,1 + stw r4,SL_DBAT1+4(r1) + mfdbatu r4,2 + stw r4,SL_DBAT2(r1) + mfdbatl r4,2 + stw r4,SL_DBAT2+4(r1) + mfdbatu r4,3 + stw r4,SL_DBAT3(r1) + mfdbatl r4,3 + stw r4,SL_DBAT3+4(r1) + mfibatu r4,0 + stw r4,SL_IBAT0(r1) + mfibatl r4,0 + stw r4,SL_IBAT0+4(r1) + mfibatu r4,1 + stw r4,SL_IBAT1(r1) + mfibatl r4,1 + stw r4,SL_IBAT1+4(r1) + mfibatu r4,2 + stw r4,SL_IBAT2(r1) + mfibatl r4,2 + stw r4,SL_IBAT2+4(r1) + mfibatu r4,3 + stw r4,SL_IBAT3(r1) + mfibatl r4,3 + stw r4,SL_IBAT3+4(r1) + + /* Save HID0 */ + mfspr r4,HID0 + stw r4,SL_HID0(r1) + + /* Save 7400/7410/7450 specific registers */ + mfspr r3,PVR + srwi r3,r3,16 + cmpli cr0,r3,0x8000 + cmpli cr1,r3,0x000c + cmpli cr2,r3,0x800c + cror 4*cr1+eq,4*cr1+eq,4*cr2+eq + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq + bne 1f + mfspr r4,SPRN_MSSCR0 + stw r4,SL_MSSCR0(r1) + mfspr r4,SPRN_MSSSR0 + stw r4,SL_MSSSR0(r1) + /* Save 7450 specific registers */ + beq cr1,1f + mfspr r4,HID1 + stw r4,SL_HID1(r1) + mfspr r4,SPRN_ICTRL + stw r4,SL_ICTRL(r1) + mfspr r4,SPRN_LDSTCR + stw r4,SL_LDSTCR(r1) + mfspr r4,SPRN_LDSTDB + stw r4,SL_LDSTDB(r1) +1: + /* The ROM can wake us up via 2 different vectors: + * - On wallstreet & lombard, we must write a magic + * value 'Lars' at address 4 and a pointer to a + * memory location containing the PC to resume from + * at address 0. + * - On Core99, we must store the wakeup vector at + * address 0x80 and eventually it's parameters + * at address 0x84. I've have some trouble with those + * parameters however and I no longer use them. + */ + lis r5,grackle_wake_up@ha + addi r5,r5,grackle_wake_up@l + tophys(r5,r5) + stw r5,SL_PC(r1) + lis r4,KERNELBASE@h + tophys(r5,r1) + addi r5,r5,SL_PC + lis r6,MAGIC@ha + addi r6,r6,MAGIC@l + stw r5,0(r4) + stw r6,4(r4) + /* Setup stuffs at 0x80-0x84 for Core99 */ + lis r3,core99_wake_up@ha + addi r3,r3,core99_wake_up@l + tophys(r3,r3) + stw r3,0x80(r4) + stw r5,0x84(r4) + /* Store a pointer to our backup storage into + * a kernel global + */ + lis r3,sleep_storage@ha + addi r3,r3,sleep_storage@l + stw r5,0(r3) + + +/* + * Flush the L1 data cache by reading the first 128kB of RAM + * and then flushing the same area with the dcbf instruction. + * The L2 cache has already been disabled. + */ + li r4,0x1000 /* 128kB / 32B */ + mtctr r4 + lis r4,KERNELBASE@h +1: + lwz r0,0(r4) + addi r4,r4,0x0020 /* Go to start of next cache line */ + bdnz 1b + sync + + li r4,0x1000 /* 128kB / 32B */ + mtctr r4 + lis r4,KERNELBASE@h +1: + dcbf r0,r4 + addi r4,r4,0x0020 /* Go to start of next cache line */ + bdnz 1b + sync + +/* + * Set the HID0 and MSR for sleep. + */ + mfspr r2,HID0 + rlwinm r2,r2,0,10,7 /* clear doze, nap */ + oris r2,r2,HID0_SLEEP@h + sync + mtspr HID0,r2 + sync + +/* This loop puts us back to sleep in case we have a spurrious + * wakeup so that the host bridge properly stays asleep. The + * CPU will be turned off, either after a known time (about 1 + * second) on wallstreet & lombard, or as soon as the CPU enters + * SLEEP mode on core99 + */ + mfmsr r2 + oris r2,r2,MSR_POW@h +1: sync + mtmsr r2 + isync + b 1b + +/* + * Here is the resume code. + */ + + +/* + * Core99 machines resume here + * r4 has the physical address of SL_PC(sp) (unused) + */ +_GLOBAL(core99_wake_up) + /* Make sure HID0 no longer contains any sleep bit */ + mfspr r3,HID0 + rlwinm r3,r3,0,11,7 /* clear SLEEP, NAP, DOZE bits */ + mtspr HID0,r3 + sync + isync + + /* Won't that cause problems on CPU that doesn't support it ? */ + lis r3, 0 + mtspr SPRN_MMCR0, r3 + + /* sanitize MSR */ + mfmsr r3 + ori r3,r3,MSR_EE|MSR_IP + xori r3,r3,MSR_EE|MSR_IP + sync + isync + mtmsr r3 + sync + isync + + /* Recover sleep storage */ + lis r3,sleep_storage@ha + addi r3,r3,sleep_storage@l + tophys(r3,r3) + lwz r1,0(r3) + + /* Pass thru to older resume code ... */ +/* + * Here is the resume code for older machines. + * r1 has the physical address of SL_PC(sp). + */ + +grackle_wake_up: + /* Enable and then Flash inval the instruction & data cache */ + mfspr r3,HID0 + ori r3,r3, HID0_ICE|HID0_ICFI|HID0_DCE|HID0_DCI + sync + isync + mtspr HID0,r3 + xori r3,r3, HID0_ICFI|HID0_DCI + mtspr HID0,r3 + sync + + /* Restore the kernel's segment registers before + * we do any r1 memory access as we are not sure they + * are in a sane state above the first 256Mb region + */ + li r0,16 /* load up segment register values */ + mtctr r0 /* for context 0 */ + lis r3,0x2000 /* Ku = 1, VSID = 0 */ + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,0x111 /* increment VSID */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 3b + + /* Restore the remaining bits of the HID0 register. */ + subi r1,r1,SL_PC + lwz r3,SL_HID0(r1) + sync + isync + mtspr HID0,r3 + sync + isync + + /* Restore 7400/7410/7450 specific registers */ + mfspr r3,PVR + srwi r3,r3,16 + cmpli cr0,r3,0x8000 + cmpli cr1,r3,0x000c + cmpli cr2,r3,0x800c + cror 4*cr1+eq,4*cr1+eq,4*cr2+eq + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq + bne 1f + lwz r4,SL_MSSCR0(r1) + sync + mtspr SPRN_MSSCR0,r4 + sync + isync + lwz r4,SL_MSSSR0(r1) + sync + mtspr SPRN_MSSSR0,r4 + sync + isync + bne cr2,1f + li r4,0 + mtspr SPRN_L2CR2,r4 + /* Restore 7450 specific registers */ + beq cr1,1f + lwz r4,SL_HID1(r1) + sync + mtspr HID1,r4 + isync + sync + lwz r4,SPRN_ICTRL(r1) + sync + mtspr SPRN_ICTRL,r4 + isync + sync + lwz r4,SPRN_LDSTCR(r1) + sync + mtspr SPRN_LDSTCR,r4 + isync + sync + lwz r4,SL_LDSTDB(r1) + sync + mtspr SPRN_LDSTDB,r4 + isync + sync +1: + /* Restore the BATs, and SDR1. Then we can turn on the MMU. */ + lwz r4,SL_SDR1(r1) + mtsdr1 r4 + lwz r4,SL_SPRG0(r1) + mtsprg 0,r4 + lwz r4,SL_SPRG0+4(r1) + mtsprg 1,r4 + lwz r4,SL_SPRG0+8(r1) + mtsprg 2,r4 + lwz r4,SL_SPRG0+12(r1) + mtsprg 3,r4 + + lwz r4,SL_DBAT0(r1) + mtdbatu 0,r4 + lwz r4,SL_DBAT0+4(r1) + mtdbatl 0,r4 + lwz r4,SL_DBAT1(r1) + mtdbatu 1,r4 + lwz r4,SL_DBAT1+4(r1) + mtdbatl 1,r4 + lwz r4,SL_DBAT2(r1) + mtdbatu 2,r4 + lwz r4,SL_DBAT2+4(r1) + mtdbatl 2,r4 + lwz r4,SL_DBAT3(r1) + mtdbatu 3,r4 + lwz r4,SL_DBAT3+4(r1) + mtdbatl 3,r4 + lwz r4,SL_IBAT0(r1) + mtibatu 0,r4 + lwz r4,SL_IBAT0+4(r1) + mtibatl 0,r4 + lwz r4,SL_IBAT1(r1) + mtibatu 1,r4 + lwz r4,SL_IBAT1+4(r1) + mtibatl 1,r4 + lwz r4,SL_IBAT2(r1) + mtibatu 2,r4 + lwz r4,SL_IBAT2+4(r1) + mtibatl 2,r4 + lwz r4,SL_IBAT3(r1) + mtibatu 3,r4 + lwz r4,SL_IBAT3+4(r1) + mtibatl 3,r4 + + /* Flush all TLBs */ + lis r4,0x1000 +1: addic. r4,r4,-0x1000 + tlbie r4 + blt 1b + sync + + /* restore the MSR and turn on the MMU */ + lwz r3,SL_MSR(r1) + bl turn_on_mmu + + /* get back the stack pointer */ + tovirt(r1,r1) + + /* Restore TB */ + li r3,0 + mttbl r3 + lwz r3,SL_TB(r1) + lwz r4,SL_TB+4(r1) + mttbu r3 + mttbl r4 + + /* Restore the callee-saved registers and return */ + lwz r0,SL_CR(r1) + mtcr r0 + lwz r2,SL_R2(r1) + lmw r12,SL_R12(r1) + addi r1,r1,SL_SIZE + lwz r0,4(r1) + mtlr r0 + blr + +turn_on_mmu: + mflr r4 + tovirt(r4,r4) + mtsrr0 r4 + mtsrr1 r3 + sync + isync + rfi + + .data + .globl sleep_storage +sleep_storage: + .long 0 diff -Nru a/arch/ppc/platforms/spd8xx.h b/arch/ppc/platforms/spd8xx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/spd8xx.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,69 @@ +/* + * BK Id: SCCS/s.spd8xx.h 1.8 10/27/01 13:39:41 trini + */ +/* + * Speech Design SPD8xxTS board specific definitions + * + * Copyright (c) 2000,2001 Wolfgang Denk (wd@denx.de) + */ + +#ifdef __KERNEL__ +#ifndef __ASM_SPD8XX_H__ +#define __ASM_SPD8XX_H__ + +#include + +#include + +#define SPD_IMMR_BASE 0xFFF00000 /* phys. addr of IMMR */ +#define SPD_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR SPD_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE SPD_IMAP_SIZE /* mapped size of IMMR area */ + +#define PCMCIA_MEM_ADDR ((uint)0xFE100000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) + +#define IDE0_INTERRUPT 10 /* = IRQ5 */ +#define IDE1_INTERRUPT 12 /* = IRQ6 */ +#define CPM_INTERRUPT 13 /* = SIU_LEVEL6 (was: SIU_LEVEL2) */ + +/* override the default number of IDE hardware interfaces */ +#define MAX_HWIFS 2 + +/* + * Definitions for IDE0 Interface + */ +#define IDE0_BASE_OFFSET 0x0000 /* Offset in PCMCIA memory */ +#define IDE0_DATA_REG_OFFSET 0x0000 +#define IDE0_ERROR_REG_OFFSET 0x0081 +#define IDE0_NSECTOR_REG_OFFSET 0x0082 +#define IDE0_SECTOR_REG_OFFSET 0x0083 +#define IDE0_LCYL_REG_OFFSET 0x0084 +#define IDE0_HCYL_REG_OFFSET 0x0085 +#define IDE0_SELECT_REG_OFFSET 0x0086 +#define IDE0_STATUS_REG_OFFSET 0x0087 +#define IDE0_CONTROL_REG_OFFSET 0x0106 +#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ + +/* + * Definitions for IDE1 Interface + */ +#define IDE1_BASE_OFFSET 0x0C00 /* Offset in PCMCIA memory */ +#define IDE1_DATA_REG_OFFSET 0x0000 +#define IDE1_ERROR_REG_OFFSET 0x0081 +#define IDE1_NSECTOR_REG_OFFSET 0x0082 +#define IDE1_SECTOR_REG_OFFSET 0x0083 +#define IDE1_LCYL_REG_OFFSET 0x0084 +#define IDE1_HCYL_REG_OFFSET 0x0085 +#define IDE1_SELECT_REG_OFFSET 0x0086 +#define IDE1_STATUS_REG_OFFSET 0x0087 +#define IDE1_CONTROL_REG_OFFSET 0x0106 +#define IDE1_IRQ_REG_OFFSET 0x000A /* not used */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __ASM_SPD8XX_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/spruce.h b/arch/ppc/platforms/spruce.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/spruce.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,73 @@ +/* + * include/asm-ppc/platforms/spruce.h + * + * Definitions for IBM Spruce reference board support + * + * Authors: Matt Porter and Johnnie Peters + * mporter@mvista.com + * jpeters@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_SPRUCE_H__ +#define __ASM_SPRUCE_H__ + +#define SPRUCE_PCI_CONFIG_ADDR 0xfec00000 +#define SPRUCE_PCI_CONFIG_DATA 0xfec00004 + +#define SPRUCE_PCI_PHY_IO_BASE 0xf8000000 +#define SPRUCE_PCI_IO_BASE SPRUCE_PCI_PHY_IO_BASE + +#define SPRUCE_PCI_SYS_MEM_BASE 0x00000000 + +#define SPRUCE_PCI_LOWER_MEM 0x80000000 +#define SPRUCE_PCI_UPPER_MEM 0x9fffffff +#define SPRUCE_PCI_LOWER_IO 0x00000000 +#define SPRUCE_PCI_UPPER_IO 0x03ffffff + +#define SPRUCE_ISA_IO_BASE SPRUCE_PCI_IO_BASE + +#define SPRUCE_MEM_SIZE 0x04000000 +#define SPRUCE_BUS_SPEED 66666667 + +#define SPRUCE_SERIAL_1_ADDR 0xff600300 +#define SPRUCE_SERIAL_2_ADDR 0xff600400 + +#define SPRUCE_NVRAM_BASE_ADDR 0xff800000 +#define SPRUCE_RTC_BASE_ADDR SPRUCE_NVRAM_BASE_ADDR + +#define KEYBOARD_IRQ 22 +#define AUX_IRQ 21 + +unsigned char spruce_read_keyb_data(void); +unsigned char spruce_read_keyb_status(void); + +#define kbd_read_input spruce_read_keyb_data +#define kbd_read_status spruce_read_keyb_status +#define kbd_write_output(val) *((unsigned char *)0xff810000) = (char)val +#define kbd_write_command(val) *((unsigned char *)0xff810001) = (char)val + +#endif /* __ASM_SPRUCE_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/spruce_pci.c b/arch/ppc/platforms/spruce_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/spruce_pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,104 @@ +/* + * arch/ppc/platforms/spruce_pci.c + * + * PCI support for IBM Spruce + * + * Author: Johnnie Peters + * jpeters@mvista.com + * + * Copyright 2000 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "cpc700.h" + +static inline int +spruce_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {23, 24, 25, 26}, /* IDSEL 1 - PCI slot 3 */ + {24, 25, 26, 23}, /* IDSEL 2 - PCI slot 2 */ + {25, 26, 23, 24}, /* IDSEL 3 - PCI slot 1 */ + {26, 23, 24, 25}, /* IDSEL 4 - PCI slot 0 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +void __init +spruce_setup_hose(void) +{ + struct pci_controller *hose; + + /* Setup hose */ + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + pci_init_resource(&hose->io_resource, + SPRUCE_PCI_LOWER_IO, + SPRUCE_PCI_UPPER_IO, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + SPRUCE_PCI_LOWER_MEM, + SPRUCE_PCI_UPPER_MEM, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = SPRUCE_PCI_LOWER_IO; + hose->io_space.end = SPRUCE_PCI_UPPER_IO; + hose->mem_space.start = SPRUCE_PCI_LOWER_MEM; + hose->mem_space.end = SPRUCE_PCI_UPPER_MEM; + hose->io_base_virt = (void *)SPRUCE_ISA_IO_BASE; + + setup_indirect_pci(hose, + SPRUCE_PCI_CONFIG_ADDR, + SPRUCE_PCI_CONFIG_DATA); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = spruce_map_irq; +} diff -Nru a/arch/ppc/platforms/spruce_serial.h b/arch/ppc/platforms/spruce_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/spruce_serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,72 @@ +/* + * include/asm-ppc/spruce_serial.h + * + * Definitions for IBM Spruce reference board support + * + * Authors: Matt Porter and Johnnie Peters + * mporter@mvista.com + * jpeters@mvista.com + * + * Copyright 2000 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + */ + +#ifndef __ASMPPC_SPRUCE_SERIAL_H +#define __ASMPPC_SPRUCE_SERIAL_H + +#include + +/* This is where the serial ports exist */ +#define SPRUCE_SERIAL_1_ADDR 0xff600300 +#define SPRUCE_SERIAL_2_ADDR 0xff600400 + +#define RS_TABLE_SIZE 4 + +/* Rate for the baud clock for the onboard serial chip */ +#ifndef CONFIG_SPRUCE_BAUD_33M +#define BASE_BAUD (30000000 / 4 / 16) +#else +#define BASE_BAUD (33000000 / 4 / 16) +#endif + +#ifndef SERIAL_MAGIC_KEY +#define kernel_debugger ppc_kernel_debug +#endif + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, SPRUCE_SERIAL_1_ADDR, 3, STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (u8 *)SPRUCE_SERIAL_1_ADDR, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, SPRUCE_SERIAL_2_ADDR, 4, STD_COM_FLAGS, /* ttyS1 */ \ + iomem_base: (u8 *)SPRUCE_SERIAL_2_ADDR, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +#endif /* __ASMPPC_SPRUCE_SERIAL_H */ diff -Nru a/arch/ppc/platforms/spruce_setup.c b/arch/ppc/platforms/spruce_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/spruce_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,295 @@ +/* + * arch/ppc/platforms/spruce_setup.c + * + * Board setup routines for IBM Spruce + * + * Authors: Johnnie Peters + * Matt Porter + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + */ + +#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 "cpc700.h" + +extern void spruce_init_IRQ(void); +extern int spruce_get_irq(struct pt_regs *); +extern void spruce_setup_hose(void); + +extern int pckbd_setkeycode(unsigned int, unsigned int); +extern int pckbd_getkeycode(unsigned int); +extern int pckbd_translate(unsigned char, unsigned char *, char); +extern char pckbd_unexpected_up(unsigned char); +extern void pckbd_leds(unsigned char); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; +extern char cmd_line[]; + +/* + * CPC700 PIC interrupt programming table + * + * First entry is the sensitivity (level/edge), second is the polarity. + */ +unsigned int cpc700_irq_assigns[27][2] = { + { 1, 1 }, /* IRQ 0: ECC Correctable Error - rising edge */ + { 1, 1 }, /* IRQ 1: PCI Write Mem Range - rising edge */ + { 0, 1 }, /* IRQ 2: PCI Write Command Reg - active high */ + { 0, 1 }, /* IRQ 3: UART 0 - active high */ + { 0, 1 }, /* IRQ 4: UART 1 - active high */ + { 0, 1 }, /* IRQ 5: ICC 0 - active high */ + { 0, 1 }, /* IRQ 6: ICC 1 - active high */ + { 0, 1 }, /* IRQ 7: GPT Compare 0 - active high */ + { 0, 1 }, /* IRQ 8: GPT Compare 1 - active high */ + { 0, 1 }, /* IRQ 9: GPT Compare 2 - active high */ + { 0, 1 }, /* IRQ 10: GPT Compare 3 - active high */ + { 0, 1 }, /* IRQ 11: GPT Compare 4 - active high */ + { 0, 1 }, /* IRQ 12: GPT Capture 0 - active high */ + { 0, 1 }, /* IRQ 13: GPT Capture 1 - active high */ + { 0, 1 }, /* IRQ 14: GPT Capture 2 - active high */ + { 0, 1 }, /* IRQ 15: GPT Capture 3 - active high */ + { 0, 1 }, /* IRQ 16: GPT Capture 4 - active high */ + { 0, 0 }, /* IRQ 17: Reserved */ + { 0, 0 }, /* IRQ 18: Reserved */ + { 0, 0 }, /* IRQ 19: Reserved */ + { 0, 1 }, /* IRQ 20: FPGA EXT_IRQ0 - active high */ + { 1, 1 }, /* IRQ 21: Mouse - rising edge */ + { 1, 1 }, /* IRQ 22: Keyboard - rising edge */ + { 0, 0 }, /* IRQ 23: PCI Slot 3 - active low */ + { 0, 0 }, /* IRQ 24: PCI Slot 2 - active low */ + { 0, 0 }, /* IRQ 25: PCI Slot 1 - active low */ + { 0, 0 }, /* IRQ 26: PCI Slot 0 - active low */ +}; + +static void __init +spruce_calibrate_decr(void) +{ + int freq, divisor = 4; + + /* determine processor bus speed */ + freq = SPRUCE_BUS_SPEED; + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); +} + +static int +spruce_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: IBM\n"); + seq_printf(m, "machine\t\t: Spruce\n"); + + return 0; +} + +TODC_ALLOC(); + +static void __init +spruce_setup_arch(void) +{ + /* Setup TODC access */ + TODC_INIT(TODC_TYPE_DS1643, 0, 0, SPRUCE_RTC_BASE_ADDR, 8); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000 / HZ; + + /* Setup PCI host bridge */ + spruce_setup_hose(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00FF); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0801); /* /dev/sda1 */ +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + /* Identify the system */ + printk("System Identification: IBM Spruce\n"); + printk("IBM Spruce port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); +} + +static void +spruce_restart(char *cmd) +{ + __cli(); + + /* SRR0 has system reset vector, SRR1 has default MSR value */ + /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */ + __asm__ __volatile__ + ("\n\ + lis 3,0xfff0 + ori 3,3,0x0100 + mtspr 26,3 + li 3,0 + mtspr 27,3 + rfi + "); + for(;;); +} + +static void +spruce_power_off(void) +{ + for(;;); +} + +static void +spruce_halt(void) +{ + spruce_restart(NULL); +} + +extern int boot_mem_size; + +static unsigned long __init +spruce_find_end_of_memory(void) +{ + return boot_mem_size; +} + +static void __init +spruce_map_io(void) +{ + io_block_mapping(SPRUCE_PCI_IO_BASE, SPRUCE_PCI_PHY_IO_BASE, + 0x08000000, _PAGE_IO); +} + +unsigned char spruce_read_keyb_status(void) +{ + unsigned long kbd_status; + + __raw_writel(0x00000088, 0xff500008); + eieio(); + + __raw_writel(0x03000000, 0xff50000c); + eieio(); + + asm volatile(" lis 7,0xff88 \n + ori 7,7,0x8 \n + lswi 6,7,0x8 \n + mr %0,6 \n" + : "=r" (kbd_status) :: "6", "7"); + + __raw_writel(0x00000000, 0xff50000c); + eieio(); + + return (unsigned char)(kbd_status >> 24); +} + +unsigned char spruce_read_keyb_data(void) +{ + unsigned long kbd_data; + + __raw_writel(0x00000088, 0xff500008); + eieio(); + + __raw_writel(0x03000000, 0xff50000c); + eieio(); + + asm volatile(" lis 7,0xff88 \n + lswi 6,7,0x8 \n + mr %0,6 \n" + : "=r" (kbd_data) :: "6", "7"); + + __raw_writel(0x00000000, 0xff50000c); + eieio(); + + return (unsigned char)(kbd_data >> 24); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + isa_io_base = SPRUCE_ISA_IO_BASE; + pci_dram_offset = SPRUCE_PCI_SYS_MEM_BASE; + + ppc_md.setup_arch = spruce_setup_arch; + ppc_md.show_cpuinfo = spruce_show_cpuinfo; + ppc_md.init_IRQ = cpc700_init_IRQ; + ppc_md.get_irq = cpc700_get_irq; + + ppc_md.find_end_of_memory = spruce_find_end_of_memory; + ppc_md.setup_io_mappings = spruce_map_io; + + ppc_md.restart = spruce_restart; + ppc_md.power_off = spruce_power_off; + ppc_md.halt = spruce_halt; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = spruce_calibrate_decr; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + +#ifdef CONFIG_VT + /* Spruce has a PS2 style keyboard */ + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x54; +#endif +#endif +} diff -Nru a/arch/ppc/platforms/tqm8260.h b/arch/ppc/platforms/tqm8260.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/tqm8260.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,17 @@ +/* + * TQM8260 board specific definitions + * + * Copyright (c) 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_TQM8260_H +#define __MACH_TQM8260_H + +#include + +#include + +#define IMAP_ADDR ((uint)0xFFF00000) +#define PHY_INTERRUPT 25 + +#endif /* __MACH_TQM8260_H */ diff -Nru a/arch/ppc/platforms/tqm8xx.h b/arch/ppc/platforms/tqm8xx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/tqm8xx.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,54 @@ +/* + * BK Id: SCCS/s.tqm8xx.h 1.8 08/30/01 09:01:04 trini + */ +/* + * TQM8xx(L) board specific definitions + * + * Copyright (c) 1999,2000,2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_TQM8xx_H +#define __MACH_TQM8xx_H + +#include + +#include + +#define TQM_IMMR_BASE 0xFFF00000 /* phys. addr of IMMR */ +#define TQM_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR TQM_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE TQM_IMAP_SIZE /* mapped size of IMMR area */ + +/*----------------------------------------------------------------------- + * PCMCIA stuff + *----------------------------------------------------------------------- + * + */ +#define PCMCIA_MEM_SIZE ( 64 << 20 ) + +#define MAX_HWIFS 1 /* overwrite default in include/asm-ppc/ide.h */ + +/* + * Definitions for IDE0 Interface + */ +#define IDE0_BASE_OFFSET 0 +#define IDE0_DATA_REG_OFFSET (PCMCIA_MEM_SIZE + 0x320) +#define IDE0_ERROR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 1) +#define IDE0_NSECTOR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 2) +#define IDE0_SECTOR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 3) +#define IDE0_LCYL_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 4) +#define IDE0_HCYL_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 5) +#define IDE0_SELECT_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 6) +#define IDE0_STATUS_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 7) +#define IDE0_CONTROL_REG_OFFSET 0x0106 +#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ + +#define IDE0_INTERRUPT 13 + + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_TQM8xx_H */ diff -Nru a/arch/ppc/platforms/walnut.c b/arch/ppc/platforms/walnut.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/walnut.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,261 @@ +/* + * + * Copyrigh t(c) 1999-2000 Grant Erickson + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Author: MontaVista Software, Inc. + * + * Module name: walnut.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * IBM PowerPC 4xx based boards. Adapted from original + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + * History: 11/09/2001 - armin + * added board_init to add in additional instuctions needed during platfrom_init + * + * 01/22/2002 - Armin + * converted pci to ocp + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PPC_RTC +#include +#endif + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +void *kb_cs; +void *kb_data; +void *walnut_rtc_base; + +/* Some IRQs unique to Walnut. + * Used by the generic 405 PCI setup functions in ppc4xx_pci.c + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 1 - PCI slot 1 */ + {29, 29, 29, 29}, /* IDSEL 2 - PCI slot 2 */ + {30, 30, 30, 30}, /* IDSEL 3 - PCI slot 3 */ + {31, 31, 31, 31}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +board_setup_arch(void) +{ +#define WALNUT_PS2_BASE 0xF0100000 +#define WALNUT_FPGA_BASE 0xF0300000 + + void *fpga_brdc; + unsigned char fpga_brdc_data; + void *fpga_enable; + void *fpga_polarity; + void *fpga_status; + void *fpga_trigger; + + kb_data = ioremap(WALNUT_PS2_BASE, 8); + if (!kb_data) { + printk(KERN_CRIT + "walnut_setup_arch() kb_data ioremap failed\n"); + return; + } + + kb_cs = kb_data + 1; + + fpga_status = ioremap(WALNUT_FPGA_BASE, 8); + if (!fpga_status) { + printk(KERN_CRIT + "walnut_setup_arch() fpga_status ioremap failed\n"); + return; + } + + fpga_enable = fpga_status + 1; + fpga_polarity = fpga_status + 2; + fpga_trigger = fpga_status + 3; + fpga_brdc = fpga_status + 4; + + /* split the keyboard and mouse interrupts */ + fpga_brdc_data = readb(fpga_brdc); + fpga_brdc_data |= 0x80; + writeb(fpga_brdc_data, fpga_brdc); + + writeb(0x3, fpga_enable); + + writeb(0x3, fpga_polarity); + + writeb(0x3, fpga_trigger); + +#ifdef CONFIG_PPC_RTC + /* RTC step for the walnut */ + walnut_rtc_base = (void *) WALNUT_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, walnut_rtc_base, walnut_rtc_base, + walnut_rtc_base, 8); +#endif /* CONFIG_PPC_RTC */ +} + +void __init +bios_fixup(struct pci_controller *hose, void *pcil0_base) +{ + + unsigned int bar_response, bar; + struct pcil0_regs *pcip; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + * The following code is simplified by assuming that the bootrom + * has been well behaved in following this mapping. + */ + +#ifdef DEBUG + int i; + pcip = (struct pcil0_regs *) pcil0_base; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#else + pcip = (struct pcil0_regs *) pcil0_base; +#endif + + /* added for IBM boot rom version 1.15 bios bar changes -AK */ + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000000); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* Zero config bars */ + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + 0x00000000); + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + /* end work arround */ + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif +} + +void __init +board_io_mapping(void) +{ + io_block_mapping(WALNUT_RTC_VADDR, + WALNUT_RTC_PADDR, WALNUT_RTC_SIZE, _PAGE_IO); +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ +#ifdef CONFIG_PPC_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +} diff -Nru a/arch/ppc/platforms/walnut.h b/arch/ppc/platforms/walnut.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/walnut.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,88 @@ +/* + * + * Copyright (c) 1999 Grant Erickson + * + * Copyright 2000 MontaVista Software Inc. + * PPC405 modifications + * Author: MontaVista Software, Inc. + * frank_rowand@mvista.com or source@mvista.com + * debbie_chu@mvista.com + * + * Module name: ppc405.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * based boards. + * + * This includes: + * + * 405GP "Walnut" evaluation board + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_WALNUT_H__ +#define __ASM_WALNUT_H__ + +/* We have a 405GP core */ +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Walnut" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ + unsigned char bi_pci_enetaddr[6]; /* PCI Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + + +/* Memory map for the IBM "Walnut" 405GP evaluation board. + * Generic 4xx plus RTC. + */ + +extern void *walnut_rtc_base; +#define WALNUT_RTC_PADDR ((uint)0xf0000000) +#define WALNUT_RTC_VADDR WALNUT_RTC_PADDR +#define WALNUT_RTC_SIZE ((uint)8*1024) + +/* ps2 keyboard and mouse */ +#define KEYBOARD_IRQ 25 +#define AUX_IRQ 26 + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define WALNUT_PS2_BASE 0xF0100000 +#define WALNUT_FPGA_BASE 0xF0300000 + + +extern void *kb_cs; +extern void *kb_data; +#define kbd_read_input() readb(kb_data) +#define kbd_read_status() readb(kb_cs) +#define kbd_write_output(val) writeb(val, kb_data) +#define kbd_write_command(val) writeb(val, kb_cs) + +#define PPC4xx_MACHINE_NAME "IBM Walnut" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_WALNUT_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/arch/ppc/platforms/zx4500.h b/arch/ppc/platforms/zx4500.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/zx4500.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,70 @@ +/* * arch/ppc/platforms/zx4500.h + * + * Board setup routines for Znyx ZX4500 cPCI board. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2000, 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef __PPC_PLATFORMS_ZX4500_H_ +#define __PPC_PLATFORMS_ZX4500_H_ + +/* + * Define the addresses of CPLD registers in CLPD area. + */ +#define ZX4500_CPLD_BOARD_ID 0xff800001 +#define ZX4500_CPLD_REV 0xff800002 +#define ZX4500_CPLD_RESET 0xff800011 +#define ZX4500_CPLD_PHY1 0xff800014 +#define ZX4500_CPLD_PHY2 0xff800015 +#define ZX4500_CPLD_PHY3 0xff800016 +#define ZX4500_CPLD_SYSCTL 0xff800017 +#define ZX4500_CPLD_EXT_FLASH 0xff800018 +#define ZX4500_CPLD_DUAL1 0xff800019 +#define ZX4500_CPLD_DUAL2 0xff80001A +#define ZX4500_CPLD_STATUS 0xff800030 +#define ZX4500_CPLD_STREAM 0xff800032 +#define ZX4500_CPLD_PHY1_LED 0xff800034 +#define ZX4500_CPLD_PHY2_LED 0xff800035 +#define ZX4500_CPLD_PHY3_LED 0xff800036 +#define ZX4500_CPLD_PHY1_LNK 0xff80003C +#define ZX4500_CPLD_PHY2_LNK 0xff80003D +#define ZX4500_CPLD_PHY3_LNK 0xff80003E + +#define ZX4500_CPLD_RESET_SOFT 0x01 /* Soft Reset */ +#define ZX4500_CPLD_RESET_XBUS 0x40 /* Reset entire board */ + +#define ZX4500_CPLD_SYSCTL_PMC 0x01 /* Enable INTA/B/C/D from PMC */ +#define ZX4500_CPLD_SYSCTL_BCM 0x04 /* Enable INTA from BCM */ +#define ZX4500_CPLD_SYSCTL_SINTA 0x08 /* Enable SINTA from 21554 */ +#define ZX4500_CPLD_SYSCTL_WD 0x20 /* Enable Watchdog Timer */ +#define ZX4500_CPLD_SYSCTL_PMC_TRI 0x80 /* Tri-state PMC EREADY */ + +#define ZX4500_CPLD_DUAL2_LED_PULL 0x01 /* Pull LED */ +#define ZX4500_CPLD_DUAL2_LED_EXT_FAULT 0x02 /* External Fault LED */ +#define ZX4500_CPLD_DUAL2_LED_INT_FAULT 0x04 /* Internal Fault LED */ +#define ZX4500_CPLD_DUAL2_LED_OK 0x08 /* OK LED */ +#define ZX4500_CPLD_DUAL2_LED_CLK 0x10 /* CLK LED */ + +/* + * Defines related to boot string stored in flash. + */ +#define ZX4500_BOOT_STRING_ADDR 0xfff7f000 +#define ZX4500_BOOT_STRING_LEN 80 + +/* + * Define the IDSEL that the PCI bus side of the 8240 is connected to. + * This IDSEL must not be selected from the 8240 processor side. + */ +#define ZX4500_HOST_BRIDGE_IDSEL 20 + + +void zx4500_find_bridges(void); + +#endif /* __PPC_PLATFORMS_ZX4500_H_ */ diff -Nru a/arch/ppc/platforms/zx4500_pci.c b/arch/ppc/platforms/zx4500_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/zx4500_pci.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,140 @@ +/* + * arch/ppc/platforms/zx4500_pci.c + * + * PCI setup routines for Znyx ZX4500 cPCI boards. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2000, 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "zx4500.h" + +/* + * Znyx ZX4500 interrupt routes. + */ +static inline int +zx4500_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 19, 0, 0, 0 }, /* IDSEL 21 - 21554 PCI-cPCI bridge */ + { 18, 0, 0, 0 }, /* IDSEL 22 - BCM5600 INTA */ + { 16, 20, 16, 20 }, /* IDSEL 23 - PPMC Slot */ + }; + + const long min_idsel = 21, max_idsel = 23, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +void __init +zx4500_board_init(struct pci_controller *hose) +{ + uint val; + u_char sysctl; + + /* + * CPLD Registers are mapped in by BAT 3 in zx4500_setup_arch(). + * + * Turn off all interrupts routed through the CPLD. + * Also, turn off watchdog timer and drive PMC EREADY low. + */ + sysctl = in_8((volatile u_char *)ZX4500_CPLD_SYSCTL); + sysctl &= ~(ZX4500_CPLD_SYSCTL_PMC | + ZX4500_CPLD_SYSCTL_BCM | + ZX4500_CPLD_SYSCTL_SINTA | + ZX4500_CPLD_SYSCTL_WD | + ZX4500_CPLD_SYSCTL_PMC_TRI); + out_8((volatile u_char *)ZX4500_CPLD_SYSCTL, sysctl); + + /* + * Kludge the size that BAR2 of the 21554 asks for + * (i.e., set Upstream I/O or Memory 0 Setup Register). + * Old versions of SROM wants 1 GB which is too large, make it ask + * for 256 MB. + */ + early_read_config_dword(hose, 0, PCI_DEVFN(21,0), 0xc4, &val); + + if (val != 0) { + early_write_config_dword(hose, + 0, + PCI_DEVFN(21,0), + 0xc4, + val | 0xf0000000); + } + + return; +} + +static int +zx4500_exclude_device(u_char bus, u_char devfn) +{ + if ((bus == 0) && (PCI_SLOT(devfn) == ZX4500_HOST_BRIDGE_IDSEL)) { + return PCIBIOS_DEVICE_NOT_FOUND; + } + else { + return PCIBIOS_SUCCESSFUL; + } +} + +void __init +zx4500_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + if (mpc10x_bridge_init(hose, + MPC10X_MEM_MAP_B, + MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE) == 0) { + + hose->mem_resources[0].end = 0xffffffff; + + /* Initialize the board */ + zx4500_board_init(hose); + + /* scan PCI bus */ + ppc_md.pci_exclude_device = zx4500_exclude_device; + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = NULL; + ppc_md.pcibios_fixup_bus = NULL; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = zx4500_map_irq; + } + else { + if (ppc_md.progress) + ppc_md.progress("Bridge init failed", 0x100); + printk("Host bridge init failed\n"); + } + + return; +} diff -Nru a/arch/ppc/platforms/zx4500_serial.h b/arch/ppc/platforms/zx4500_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/zx4500_serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,48 @@ +/* + * include/asm-ppc/zx4500_serial.h + * + * Definitions for Znyx ZX4500 board support + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2000, 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __ASMPPC_ZX4500_SERIAL_H +#define __ASMPPC_ZX4500_SERIAL_H + +#include + +/* Define the UART base address (only 1 UART) */ +#define ZX4500_SERIAL_1 0xff880000 + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 1 +#endif + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD ( 1843200 / 16 ) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, ZX4500_SERIAL_1, 17, STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (u8 *)ZX4500_SERIAL_1, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +#endif /* __ASMPPC_ZX4500_SERIAL_H */ diff -Nru a/arch/ppc/platforms/zx4500_setup.c b/arch/ppc/platforms/zx4500_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc/platforms/zx4500_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,360 @@ +/* + * arch/ppc/platforms/zx4500_setup.c + * + * Board setup routines for Znyx ZX4500 family of cPCI boards. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2000, 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * This file adds support for the Znyx ZX4500 series of cPCI boards. + * These boards have an 8240, UART on the processor bus, a PPMC slot (for now + * the card in this slot can _not_ be a monarch), Broadcom BCM5600, and an + * Intel 21554 bridge. + * + * Currently, this port assumes that the 8240 is the master and performs PCI + * arbitration, etc. It is also assumed that the 8240 is wired to come up + * using memory MAP B (CHRP map). + * + * Note: This board port will not work properly as it is. You must apply the + * patch that is at ftp://ftp.mvista.com/pub/Area51/zx4500/zx_patch_2_5 + */ +#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 "zx4500.h" + +static u_char zx4500_openpic_initsenses[] __initdata = { + 0, /* 0-15 are not used on an 8240 EPIC */ + 0, /* 1 */ + 0, /* 2 */ + 0, /* 3 */ + 0, /* 4 */ + 0, /* 5 */ + 0, /* 6 */ + 0, /* 7 */ + 0, /* 8 */ + 0, /* 9 */ + 0, /* 10 */ + 0, /* 11 */ + 0, /* 12 */ + 0, /* 13 */ + 0, /* 14 */ + 0, /* 15 */ + 1, /* 16: EPIC IRQ 0: Active Low -- PMC #INTA & #INTC */ + 1, /* 17: EPIC IRQ 1: Active Low -- UART */ + 1, /* 18: EPIC IRQ 2: Active Low -- BCM5600 #INTA */ + 1, /* 19: EPIC IRQ 3: Active Low -- 21554 #SINTA */ + 1, /* 20: EPIC IRQ 4: Active Low -- PMC #INTB & #INTD */ +}; + + +static void __init +zx4500_setup_arch(void) +{ + char boot_string[ZX4500_BOOT_STRING_LEN + 1]; + char *boot_arg; + extern char cmd_line[]; + + + loops_per_jiffy = 50000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + else +#endif +#if defined(CONFIG_ROOT_NFS) + ROOT_DEV = to_kdev_t(0x00FF); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x0801); /* /dev/sda1 SCSI disk */ +#endif + + /* Get boot string from flash */ + strncpy(boot_string, + (char *)ZX4500_BOOT_STRING_ADDR, + ZX4500_BOOT_STRING_LEN); + boot_string[ZX4500_BOOT_STRING_LEN] = '\0'; + + /* Can be delimited by 0xff */ + boot_arg = strchr(boot_string, 0xff); + + if (boot_arg != NULL) { + *boot_arg = '\0'; + } + + /* First 3 chars must be 'dev'. If not, ignore. */ + if (!strncmp(boot_string, "dev", 3)) { + /* skip 'dev?' and any blanks after it */ + boot_arg = strchr(boot_string, ' '); + + if (boot_arg != NULL) { + while (*boot_arg == ' ') boot_arg++; + strcat(cmd_line, " "); + strcat(cmd_line, boot_arg); + } + } + + /* nothing but serial consoles... */ + printk("Znyx ZX4500 Series High Performance Switch\n"); + printk("ZX4500 port (C) 2000, 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + /* Lookup PCI host bridge */ + zx4500_find_bridges(); + + printk("ZX4500 Board ID: 0x%x, Revision #: 0x%x\n", + in_8((volatile u_char *)ZX4500_CPLD_BOARD_ID), + in_8((volatile u_char *)ZX4500_CPLD_REV)); + + return; +} + +static ulong __init +zx4500_find_end_of_memory(void) +{ + return mpc10x_get_mem_size(MPC10X_MEM_MAP_B); +} + +static void __init +zx4500_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); +} + +/* + * Enable interrupts routed thru CPLD to reach the 8240's EPIC. + * Need to enable all 4 PMC intrs, BCM INTA, and 21554 SINTA to 8240. + * UART intrs routed directly to 8240 (not thru CPLD). + */ +static void __init +zx4500_enable_cpld_intrs(void) +{ + u_char sysctl; + + sysctl = in_8((volatile u_char *)ZX4500_CPLD_SYSCTL); + sysctl |= (ZX4500_CPLD_SYSCTL_PMC | + ZX4500_CPLD_SYSCTL_BCM | + ZX4500_CPLD_SYSCTL_SINTA); + out_8((volatile u_char *)ZX4500_CPLD_SYSCTL, sysctl); + + return; +} + +static void __init +zx4500_init_IRQ(void) +{ + OpenPIC_InitSenses = zx4500_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(zx4500_openpic_initsenses); + + openpic_init(1, 0, NULL, -1); + + zx4500_enable_cpld_intrs(); /* Allow CPLD to route intrs to 8240 */ + + return; +} + +static void +zx4500_restart(char *cmd) +{ + __cli(); + + out_8((volatile u_char *)ZX4500_CPLD_RESET, ZX4500_CPLD_RESET_XBUS); + for (;;); + + panic("Restart failed.\n"); + /* NOTREACHED */ +} + +static void +zx4500_power_off(void) +{ + __cli(); + for(;;); /* No way to shut power off with software */ + /* NOTREACHED */ +} + +static void +zx4500_halt(void) +{ + zx4500_power_off(); + /* NOTREACHED */ +} + +static int +zx4500_get_bus_speed(void) +{ + int bus_speed; + + bus_speed = 100000000; + + return bus_speed; +} + +static int +zx4500_show_cpuinfo(struct seq_file *m) +{ + uint pvid; + + seq_printf(m, "vendor\t\t: Znyx\n"); + seq_printf(m, "machine\t\t: ZX4500\n"); + seq_printf(m, "processor\t: PVID: 0x%x, vendor: %s\n", + pvid, (pvid & (1<<15) ? "IBM" : "Motorola")); + seq_printf(m, "bus speed\t: %dMhz\n", + zx4500_get_bus_speed()/1000000); + + return 0; +} + +static void __init +zx4500_calibrate_decr(void) +{ + ulong freq; + + freq = zx4500_get_bus_speed() / 4; + + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + return; +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space 1-1. + */ +static __inline__ void +zx4500_set_bat(void) +{ + unsigned long bat3u, bat3l; + static int mapping_set = 0; + + if (!mapping_set) { + + __asm__ __volatile__( + " lis %0,0xf800\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x0ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (bat3u), "=r" (bat3l)); + + mapping_set = 1; + } + + return; +} + +#ifdef CONFIG_SERIAL_TEXT_DEBUG +#include +#include +#include + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in */ +}; + +void +zx4500_progress(char *s, unsigned short hex) +{ + volatile char c; + volatile unsigned long com_port; + u16 shift; + + com_port = rs_table[0].port; + shift = rs_table[0].iomem_reg_shift; + + while ((c = *s++) != 0) { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = c; + + if (c == '\n') { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = '\r'; + } + } +} +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* Map in board registers, etc. */ + zx4500_set_bat(); + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + + ppc_md.setup_arch = zx4500_setup_arch; + ppc_md.show_cpuinfo = zx4500_show_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = zx4500_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = zx4500_restart; + ppc_md.power_off = zx4500_power_off; + ppc_md.halt = zx4500_halt; + + ppc_md.find_end_of_memory = zx4500_find_end_of_memory; + ppc_md.setup_io_mappings = zx4500_map_io; + + ppc_md.calibrate_decr = zx4500_calibrate_decr; + + ppc_md.heartbeat = NULL; + ppc_md.heartbeat_reset = 0; + ppc_md.heartbeat_count = 0; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = zx4500_progress; +#else /* !CONFIG_SERIAL_TEXT_DEBUG */ + ppc_md.progress = NULL; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + + return; +} diff -Nru a/arch/ppc/vmlinux.lds b/arch/ppc/vmlinux.lds --- a/arch/ppc/vmlinux.lds Tue Feb 19 18:09:00 2002 +++ b/arch/ppc/vmlinux.lds Tue Feb 19 18:09:00 2002 @@ -33,6 +33,9 @@ *(.text) *(.fixup) *(.got1) + __got2_start = .; + *(.got2) + __got2_end = .; } _etext = .; PROVIDE (etext = .); @@ -46,8 +49,9 @@ .fini : { *(.fini) } =0 .ctors : { *(.ctors) } .dtors : { *(.dtors) } + /* Read-write section, merged into data segment: */ - . = (. + 0x0FFF) & 0xFFFFF000; + . = ALIGN(4096); .data : { *(.data) @@ -76,6 +80,9 @@ . = ALIGN(32); .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + . = ALIGN(8192); + .data.init_task : { *(.data.init_task) } . = ALIGN(4096); __init_begin = .; diff -Nru a/arch/ppc/xmon/nonstdio.h b/arch/ppc/xmon/nonstdio.h --- a/arch/ppc/xmon/nonstdio.h Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/xmon/nonstdio.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.nonstdio.h 1.5 05/17/01 18:14:23 cort + * BK Id: %F% %I% %G% %U% %#% */ typedef int FILE; extern FILE *xmon_stdin, *xmon_stdout; @@ -21,5 +21,6 @@ extern void xmon_printf(const char *, ...); extern void xmon_fprintf(void *, const char *, ...); extern void xmon_sprintf(char *, const char *, ...); +extern void xmon_puts(char*); #define perror(s) printf("%s: no files!\n", (s)) diff -Nru a/arch/ppc/xmon/start.c b/arch/ppc/xmon/start.c --- a/arch/ppc/xmon/start.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/xmon/start.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.start.c 1.16 08/20/01 22:17:58 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * Copyright (C) 1996 Paul Mackerras. @@ -13,9 +13,13 @@ #include #include #include +#include +#include #include #include -#include +#include +#include +#include #include #include #include @@ -24,15 +28,14 @@ #endif static volatile unsigned char *sccc, *sccd; -unsigned long TXRDY, RXRDY; +unsigned int TXRDY, RXRDY, DLAB; extern void xmon_printf(const char *fmt, ...); static int xmon_expect(const char *str, unsigned int timeout); -static int console; static int use_screen; static int via_modem; static int xmon_use_sccb; -static struct device_node *macio_node; +static struct device_node *channel_node; #define TB_SPEED 25000000 @@ -46,12 +49,65 @@ void buf_access(void) { - if ( _machine == _MACH_chrp ) - sccd[3] &= ~0x80; /* reset DLAB */ + if (DLAB) + sccd[3] &= ~DLAB; /* reset DLAB */ } extern int adb_init(void); +#ifdef CONFIG_ALL_PPC +/* + * This looks in the "ranges" property for the primary PCI host bridge + * to find the physical address of the start of PCI/ISA I/O space. + * It is basically a cut-down version of pci_process_bridge_OF_ranges. + */ +static unsigned long chrp_find_phys_io_base(void) +{ + struct device_node *node; + unsigned int *ranges; + unsigned long base = CHRP_ISA_IO_BASE; + int rlen = 0; + int np; + + node = find_devices("isa"); + if (node != NULL) { + node = node->parent; + if (node == NULL || node->type == NULL + || strcmp(node->type, "pci") != 0) + node = NULL; + } + if (node == NULL) + node = find_devices("pci"); + if (node == NULL) + return base; + + ranges = (unsigned int *) get_property(node, "ranges", &rlen); + np = prom_n_addr_cells(node) + 5; + while ((rlen -= np * sizeof(unsigned int)) >= 0) { + if ((ranges[0] >> 24) == 1 && ranges[2] == 0) { + /* I/O space starting at 0, grab the phys base */ + base = ranges[np - 3]; + break; + } + ranges += np; + } + return base; +} +#endif /* CONFIG_ALL_PPC */ + +static void sysrq_handle_xmon(int key, struct pt_regs *regs, + struct kbd_struct *kbd, struct tty_struct *tty) +{ + xmon(regs); +} + +static struct sysrq_key_op sysrq_xmon_op = +{ + handler: sysrq_handle_xmon, + help_msg: "Xmon", + action_msg: "Entering xmon\n", +}; + void xmon_map_scc(void) { @@ -65,8 +121,6 @@ unsigned long addr; #ifdef CONFIG_BOOTX_TEXT if (!machine_is_compatible("iMac")) { - extern boot_infos_t *disp_bi; - /* see if there is a keyboard in the device tree with a parent of type "adb" */ for (np = find_devices("keyboard"); np; np = np->next) @@ -77,11 +131,11 @@ /* needs to be hacked if xmon_printk is to be used from within find_via_pmu() */ #ifdef CONFIG_ADB_PMU - if (np != NULL && disp_bi && find_via_pmu()) + if (np != NULL && boot_text_mapped && find_via_pmu()) use_screen = 1; #endif #ifdef CONFIG_ADB_CUDA - if (np != NULL && disp_bi && find_via_cuda()) + if (np != NULL && boot_text_mapped && find_via_cuda()) use_screen = 1; #endif } @@ -99,6 +153,7 @@ np = np->sibling; if (np != NULL) { /* XXX should parse this properly */ + channel_node = np; slots = get_property(np, "slot-names", &l); if (slots != NULL && l >= 10 && strcmp(slots+4, "Modem") == 0) @@ -126,25 +181,27 @@ RXRDY = 1; np = find_devices("mac-io"); - if (np && np->n_addrs) { - macio_node = np; + if (np && np->n_addrs) addr = np->addrs[0].address + 0x13020; - } base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE); sccc = base + (addr & ~PAGE_MASK); sccd = sccc + 0x10; - } - else - { - /* should already be mapped by the kernel boot */ - sccc = (volatile unsigned char *) (isa_io_base + 0x3fd); - sccd = (volatile unsigned char *) (isa_io_base + 0x3f8); + + } else { + base = (volatile unsigned char *) isa_io_base; + if (_machine == _MACH_chrp) + base = (volatile unsigned char *) + ioremap(chrp_find_phys_io_base(), 0x1000); + + sccc = base + 0x3fd; + sccd = base + 0x3f8; if (xmon_use_sccb) { sccc -= 0x100; sccd -= 0x100; } TXRDY = 0x20; RXRDY = 1; + DLAB = 0x80; } #elif defined(CONFIG_GEMINI) /* should already be mapped by the kernel boot */ @@ -152,8 +209,16 @@ sccd = (volatile unsigned char *) 0xffeffb08; TXRDY = 0x20; RXRDY = 1; - console = 1; + DLAB = 0x80; +#elif defined(CONFIG_405GP) + sccc = (volatile unsigned char *)0xef600305; + sccd = (volatile unsigned char *)0xef600300; + TXRDY = 0x20; + RXRDY = 1; + DLAB = 0x80; #endif /* platform */ + + __sysrq_put_key_op('x', &sysrq_xmon_op); } static int scc_initialized = 0; @@ -210,8 +275,6 @@ ct = 1; --i; } else { - if (console) - printk("%c", c); ct = 0; } buf_access(); @@ -349,12 +412,19 @@ { int i, x; - if (macio_node != 0) - feature_set(macio_node, FEATURE_Serial_enable); - if (via_modem && macio_node != 0) { + if (channel_node != 0) + pmac_call_feature( + PMAC_FTR_SCC_ENABLE, + channel_node, + PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1); + printk(KERN_INFO "Serial port locked ON by debugger !\n"); + if (via_modem && channel_node != 0) { unsigned int t0; - feature_set(macio_node, FEATURE_Modem_power); + pmac_call_feature( + PMAC_FTR_MODEM_ENABLE, + channel_node, 0, 1); + printk(KERN_INFO "Modem powered up by debugger !\n"); t0 = readtb(); while (readtb() - t0 < 3*TB_SPEED) eieio(); diff -Nru a/arch/ppc/xmon/subr_prf.c b/arch/ppc/xmon/subr_prf.c --- a/arch/ppc/xmon/subr_prf.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/xmon/subr_prf.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.subr_prf.c 1.5 05/17/01 18:14:23 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * Written by Cort Dougan to replace the version originally used @@ -51,3 +51,8 @@ va_end(ap); } +void +xmon_puts(char *s) +{ + xmon_write(stdout, s, strlen(s)); +} diff -Nru a/arch/ppc/xmon/xmon.c b/arch/ppc/xmon/xmon.c --- a/arch/ppc/xmon/xmon.c Tue Feb 19 18:08:57 2002 +++ b/arch/ppc/xmon/xmon.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.xmon.c 1.16 09/22/01 15:25:10 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * Routines providing a simple monitor for use on the PowerMac. @@ -14,6 +14,10 @@ #include #include #include +#include +#ifdef CONFIG_PMAC_BACKLIGHT +#include +#endif #include "nonstdio.h" #include "privinst.h" @@ -90,6 +94,7 @@ static void write_spr(int, unsigned); static void super_regs(void); static void print_sysmap(void); +static void sysmap_lookup(void); static void remove_bpts(void); static void insert_bpts(void); static struct bpt *at_breakpoint(unsigned pc); @@ -98,10 +103,7 @@ #ifdef CONFIG_SMP static void cpu_cmd(void); #endif /* CONFIG_SMP */ -#if 0 /* Makes compile with -Wall */ -static char *pretty_print_addr(unsigned long addr); -static char *lookup_name(unsigned long addr); -#endif +static int pretty_print_addr(unsigned long addr); static void csum(void); extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned); @@ -112,6 +114,8 @@ extern void xmon_enter(void); extern void xmon_leave(void); +extern char* xmon_find_symbol(unsigned long addr, unsigned long* saddr); +extern unsigned long xmon_symbol_to_addr(char* symbol); #define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3]) @@ -121,6 +125,7 @@ #define isalnum(c) (('0' <= (c) && (c) <= '9') \ || ('a' <= (c) && (c) <= 'z') \ || ('A' <= (c) && (c) <= 'Z')) +#define isspace(c) (c == ' ' || c == '\t' || c == 10 || c == 13 || c == 0) static char *help_string = "\ Commands:\n\ @@ -138,6 +143,8 @@ r print registers\n\ S print special registers\n\ t print backtrace\n\ + la lookup address in system.map\n\ + ls lookup symbol in system.map\n\ x exit monitor\n\ "; @@ -147,6 +154,19 @@ static struct pt_regs *xmon_regs[NR_CPUS]; +extern inline void sync(void) +{ + asm volatile("sync; isync"); +} + +extern inline void __delay(unsigned int loops) +{ + if (loops != 0) + __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : : + "r" (loops) : "ctr"); +} + + void xmon(struct pt_regs *excp) { @@ -187,6 +207,16 @@ */ #endif /* CONFIG_SMP */ remove_bpts(); +#ifdef CONFIG_PMAC_BACKLIGHT + if( setjmp(bus_error_jmp) == 0 ) { + debugger_fault_handler = handle_fault; + sync(); + set_backlight_enable(1); + set_backlight_level(BACKLIGHT_MAX); + sync(); + } + debugger_fault_handler = 0; +#endif /* CONFIG_PMAC_BACKLIGHT */ cmd = cmds(excp); if (cmd == 's') { xmon_trace[smp_processor_id()] = SSTEP; @@ -318,7 +348,7 @@ } store_inst((void *) bp->address); } -#if !defined(CONFIG_8xx) && !defined(CONFIG_POWER4) +#if !defined(CONFIG_8xx) if (dabr.enabled) set_dabr(dabr.address); if (iabr.enabled) @@ -333,7 +363,7 @@ struct bpt *bp; unsigned instr; -#if !defined(CONFIG_8xx) && !defined(CONFIG_POWER4) +#if !defined(CONFIG_8xx) set_dabr(0); set_iabr(0); #endif @@ -398,6 +428,9 @@ case 'd': dump(); break; + case 'l': + sysmap_lookup(); + break; case 'r': if (excp != NULL) prregs(excp); /* print regs */ @@ -464,8 +497,8 @@ if (cmd == 'i') { /* interrupt other cpu(s) */ cpu = MSG_ALL_BUT_SELF; - scanhex(&cpu); - smp_send_xmon_break(cpu); + if (scanhex(&cpu)) + smp_send_xmon_break(cpu); return; } termch = cmd; @@ -547,8 +580,10 @@ unsigned short fcs; unsigned char v; - scanhex(&adrs); - scanhex(&ncsum); + if (!scanhex(&adrs)) + return; + if (!scanhex(&ncsum)) + return; fcs = 0xffff; for (i = 0; i < ncsum; ++i) { if (mread(adrs+i, &v, 1) == 0) { @@ -570,7 +605,7 @@ cmd = inchar(); switch (cmd) { -#if !defined(CONFIG_8xx) && !defined(CONFIG_POWER4) +#if !defined(CONFIG_8xx) case 'd': mode = 7; cmd = inchar(); @@ -580,6 +615,11 @@ mode = 6; else termch = cmd; + cmd = inchar(); + if (cmd == 'p') + mode &= ~4; + else + termch = cmd; dabr.address = 0; dabr.count = 0; dabr.enabled = scanhex(&dabr.address); @@ -588,11 +628,16 @@ dabr.address = (dabr.address & ~7) | mode; break; case 'i': + cmd = inchar(); + if (cmd == 'p') + mode = 2; + else + mode = 3; iabr.address = 0; iabr.count = 0; iabr.enabled = scanhex(&iabr.address); if (iabr.enabled) - iabr.address |= 3; + iabr.address |= mode; scanhex(&iabr.count); break; #endif @@ -625,6 +670,8 @@ printf("r"); if (dabr.address & 2) printf("w"); + if (dabr.address & 4) + printf("p"); printf("]\n"); } if (iabr.enabled) @@ -661,7 +708,7 @@ unsigned stack[2]; struct pt_regs regs; extern char ret_from_intercept, ret_from_syscall_1, ret_from_syscall_2; - extern char do_signal_ret, ret_from_except; + extern char ret_from_except; printf("backtrace:\n"); @@ -674,12 +721,12 @@ for (; sp != 0; sp = stack[0]) { if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) break; - printf("%x ", stack[1]); + pretty_print_addr(stack[1]); + printf(" "); if (stack[1] == (unsigned) &ret_from_intercept || stack[1] == (unsigned) &ret_from_except || stack[1] == (unsigned) &ret_from_syscall_1 - || stack[1] == (unsigned) &ret_from_syscall_2 - || stack[1] == (unsigned) &do_signal_ret) { + || stack[1] == (unsigned) &ret_from_syscall_2) { if (mread(sp+16, ®s, sizeof(regs)) != sizeof(regs)) break; printf("\nexception:%x [%x] %x ", regs.trap, sp+16, @@ -688,8 +735,8 @@ if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) break; } + printf("\n"); } - printf("\n"); } int @@ -707,10 +754,11 @@ #ifdef CONFIG_SMP printf("cpu %d: ", smp_processor_id()); #endif /* CONFIG_SMP */ - printf("vector: %x at pc = %x", - fp->trap, fp->nip); - printf(", lr = %x, msr = %x, sp = %x [%x]\n", - fp->link, fp->msr, fp->gpr[1], fp); + printf("vector: %x at pc = ", fp->trap); + pretty_print_addr(fp->nip); + printf(", lr = "); + pretty_print_addr(fp->link); + printf("\nmsr = %x, sp = %x [%x]\n", fp->msr, fp->gpr[1], fp); if (fp->trap == 0x300 || fp->trap == 0x600) printf("dar = %x, dsisr = %x\n", fp->dar, fp->dsisr); if (current) @@ -795,8 +843,16 @@ print_sysmap(void) { extern char *sysmap; - if ( sysmap ) - printf("System.map: \n%s", sysmap); + if ( sysmap ) { + printf("System.map: \n"); + if( setjmp(bus_error_jmp) == 0 ) { + debugger_fault_handler = handle_fault; + sync(); + xmon_puts(sysmap); + sync(); + } + debugger_fault_handler = 0; + } else printf("No System.map\n"); } @@ -878,6 +934,14 @@ } #endif +#ifndef CONFIG_PPC_STD_MMU +static void +dump_hash_table() +{ + printf("This CPU doesn't have a hash table.\n"); +} +#else + #ifndef CONFIG_PPC64BRIDGE static void dump_hash_table_seg(unsigned seg, unsigned start, unsigned end) @@ -1024,21 +1088,11 @@ seg_start = seg_end + 0x1000; } } +#endif /* CONFIG_PPC_STD_MMU */ /* * Stuff for reading and writing memory safely */ -extern inline void sync(void) -{ - asm volatile("sync; isync"); -} - -extern inline void __delay(unsigned int loops) -{ - if (loops != 0) - __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : : - "r" (loops) : "ctr"); -} int mread(unsigned adrs, void *buf, int size) @@ -1565,6 +1619,24 @@ } printf("invalid register name '%%%s'\n", regname); return 0; + } else if (c == '$') { + static char symname[64]; + int i; + for (i=0; i<63; i++) { + c = inchar(); + if (isspace(c)) { + termch = c; + break; + } + symname[i] = c; + } + symname[i++] = 0; + *vp = xmon_symbol_to_addr(symname); + if (!(*vp)) { + printf("unknown symbol\n"); + return 0; + } + return 1; } d = hexdigit(c); @@ -1652,38 +1724,169 @@ lineptr = str; } -#if 0 /* Makes compile with -Wall */ -static char *pretty_print_addr(unsigned long addr) +void +sysmap_lookup(void) { - printf("%08x", addr); - if ( lookup_name(addr) ) - printf(" %s", lookup_name(addr) ); - return NULL; + int type = inchar(); + unsigned addr; + static char tmp[64]; + char* cur; + + extern char *sysmap; + extern unsigned long sysmap_size; + if ( !sysmap || !sysmap_size ) + return; + + switch(type) { + case 'a': + if (scanhex(&addr)) { + pretty_print_addr(addr); + printf("\n"); + } + termch = 0; + break; + case 's': + getstring(tmp, 64); + if( setjmp(bus_error_jmp) == 0 ) { + debugger_fault_handler = handle_fault; + sync(); + cur = sysmap; + do { + cur = strstr(cur, tmp); + if (cur) { + static char res[64]; + char *p, *d; + p = cur; + while(p > sysmap && *p != 10) + p--; + if (*p == 10) p++; + d = res; + while(*p && p < (sysmap + sysmap_size) && *p != 10) + *(d++) = *(p++); + *(d++) = 0; + printf("%s\n", res); + cur++; + } + } while (cur); + sync(); + } + debugger_fault_handler = 0; + termch = 0; + break; + } } -#endif -#if 0 /* Makes compile with -Wall */ -static char *lookup_name(unsigned long addr) +static int +pretty_print_addr(unsigned long addr) { + char *sym; + unsigned long saddr; + + printf("%08x", addr); + sym = xmon_find_symbol(addr, &saddr); + if (sym) + printf(" (%s+0x%x)", sym, addr-saddr); + return (sym != 0); +} + +char* +xmon_find_symbol(unsigned long addr, unsigned long* saddr) +{ + static char rbuffer[64]; + char *p, *ep, *limit; + unsigned long prev, next; + char* psym; + extern char *sysmap; extern unsigned long sysmap_size; - char *c = sysmap; - unsigned long cmp; if ( !sysmap || !sysmap_size ) return NULL; -return NULL; -#if 0 - cmp = simple_strtoul(c, &c, 8); - /* XXX crap, we don't want the whole of the rest of the map - paulus */ - strcpy( last, strsep( &c, "\n")); - while ( c < (sysmap+sysmap_size) ) - { - cmp = simple_strtoul(c, &c, 8); - if ( cmp < addr ) - break; - strcpy( last, strsep( &c, "\n")); + + prev = 0; + psym = NULL; + p = sysmap; + limit = p + sysmap_size; + if( setjmp(bus_error_jmp) == 0 ) { + debugger_fault_handler = handle_fault; + sync(); + do { + next = simple_strtoul(p, &p, 16); + if (next > addr && prev <= addr) { + if (!psym) + goto bail; + ep = rbuffer; + p = psym; + while(*p && p < limit && *p == 32) + p++; + while(*p && p < limit && *p != 10 && (ep - rbuffer) < 63) + *(ep++) = *(p++); + *(ep++) = 0; + if (saddr) + *saddr = prev; + debugger_fault_handler = 0; + return rbuffer; + } + prev = next; + psym = p; + while(*p && p < limit && *p != 10) + p++; + if (*p) p++; + } while(*p && p < limit && next); +bail: + sync(); } - return last; -#endif + debugger_fault_handler = 0; + return NULL; } -#endif + +unsigned long +xmon_symbol_to_addr(char* symbol) +{ + char *p, *cur; + char *match; + int goodness = 0; + int result = 0; + + extern char *sysmap; + extern unsigned long sysmap_size; + if ( !sysmap || !sysmap_size ) + return 0; + + if( setjmp(bus_error_jmp) == 0 ) { + debugger_fault_handler = handle_fault; + sync(); + cur = sysmap; + while(cur) { + cur = strstr(cur, symbol); + if (cur) { + int gd = 1; + + /* best match if equal, better match if + * begins with + */ + if (cur == sysmap || *(cur-1) == ' ') { + gd++; + if (cur[strlen(symbol)] == 10) + gd++; + } + if (gd > goodness) { + match = cur; + goodness = gd; + if (gd == 3) + break; + } + cur++; + } + } + if (goodness) { + p = match; + while(p > sysmap && *p != 10) + p--; + if (*p == 10) p++; + result = simple_strtoul(p, &p, 16); + } + sync(); + } + debugger_fault_handler = 0; + return result; +} diff -Nru a/arch/ppc64/Makefile b/arch/ppc64/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,68 @@ +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture +# +# 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 archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# Changes for PPC by Gary Thomas +# Rewritten by Cort Dougan and Paul Mackerras +# Adjusted for PPC64 by Tom Gall +# + +KERNELLOAD =0xc000000000000000 + +LINKFLAGS = -T arch/ppc64/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic +CFLAGS := $(CFLAGS) -fsigned-char -msoft-float -pipe \ + -Wno-uninitialized -mminimal-toc -fno-builtin +CPP = $(CC) -E $(CFLAGS) + + +HEAD := arch/ppc64/kernel/head.o + +ARCH_SUBDIRS = arch/ppc64/kernel arch/ppc64/mm arch/ppc64/lib +SUBDIRS := $(SUBDIRS) $(ARCH_SUBDIRS) +ARCHIVES := arch/ppc64/kernel/kernel.o arch/ppc64/mm/mm.o arch/ppc64/lib/lib.o $(ARCHIVES) +CORE_FILES := arch/ppc64/kernel/kernel.o arch/ppc64/mm/mm.o arch/ppc64/lib/lib.o $(CORE_FILES) + +ifdef CONFIG_XMON +SUBDIRS += arch/ppc64/xmon +CORE_FILES += arch/ppc64/xmon/x.o +endif + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +ifdef CONFIG_PPC_PSERIES +BOOT_TARGETS = zImage znetboot.initrd zImage.initrd +endif + +ifdef CONFIG_PPC_ISERIES +BOOT_TARGETS = vmlinux.sminitrd vmlinux.initrd vmlinux.sm +endif + +$(BOOT_TARGETS): vmlinux + @$(MAKEBOOT) $@ + +znetboot: vmlinux +ifdef CONFIG_SMP + cp -f vmlinux /tftpboot/vmlinux.smp +else + cp -f vmlinux /tftpboot/vmlinux +endif + @$(MAKEBOOT) $@ + +%_config: arch/ppc64/configs/%_defconfig + rm -f .config arch/ppc64/defconfig + cp -f arch/ppc64/configs/$(@:config=defconfig) arch/ppc64/defconfig + +archclean: + rm -f arch/ppc64/kernel/{ppc_defs.h,mk_defs.s,mk_defs_out.c,mk_defs_tpl} + @$(MAKEBOOT) clean + +archmrproper: + +archdep: + $(MAKEBOOT) fastdep diff -Nru a/arch/ppc64/boot/Makefile b/arch/ppc64/boot/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,129 @@ +# Makefile for making ELF bootable images for booting on CHRP +# using Open Firmware. +# +# Geert Uytterhoeven September 1997 +# +# Based on coffboot by Paul Mackerras +# Simplified for ppc64 by Todd Inglett +# +# NOTE: this code is built for 32 bit in ELF32 format even though +# it packages a 64 bit kernel. We do this to simplify the +# bootloader and increase compatibility with OpenFirmware. +# +# To this end we need to define BOOTCC, etc, as the tools +# needed to build the 32 bit image. These are normally HOSTCC, +# but may be a third compiler if, for example, you are cross +# compiling from an intel box. Once the 64bit ppc gcc is +# stable it will probably simply be a compiler switch to +# compile for 32bit mode. +# To make it easier to setup a cross compiler, +# CROSS32_COMPILE is setup as a prefix just like CROSS_COMPILE +# in the toplevel makefile. + +CROSS32_COMPILE = +#CROSS32_COMPILE = /usr/local/ppc/bin/powerpc-linux- + +BOOTCC = $(CROSS32_COMPILE)gcc +BOOTCFLAGS = $(HOSTCFLAGS) -I$(HPATH) +BOOTLD = $(CROSS32_COMPILE)ld +BOOTAS = $(CROSS32_COMPILE)as +BOOTAFLAGS = -D__ASSEMBLY__ $(HOSTCFLAGS) + +.c.o: + $(BOOTCC) $(BOOTCFLAGS) -c -o $*.o $< +.S.o: + $(BOOTCC) $(BOOTAFLAGS) -traditional -c -o $*.o $< + +CFLAGS = $(CPPFLAGS) -O -fno-builtin -DSTDC_HEADERS +LD_ARGS = -Ttext 0x00400000 -e _start + +OBJS = crt0.o start.o main.o zlib.o image.o imagesize.o +#LIBS = $(TOPDIR)/lib/lib.a +LIBS = + +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE=/tftpboot/zImage.chrp.smp +else +TFTPIMAGE=/tftpboot/zImage.chrp +endif + + +ifeq ($(CONFIG_PPC_ISERIES),y) +all: vmlinux.sm +else +all: $(TOPDIR)/zImage +endif + + +znetboot: zImage + cp zImage $(TFTPIMAGE) + + +ifeq ($(CONFIG_PPC_ISERIES),y) + +addSystemMap: addSystemMap.c + $(HOSTCC) $(HOSTCFLAGS) -o addSystemMap addSystemMap.c + +vmlinux.sm: $(TOPDIR)/vmlinux addSystemMap + ./addSystemMap $(TOPDIR)/System.map $(TOPDIR)/vmlinux vmlinux.sm + + +addRamDisk: addRamDisk.c + $(HOSTCC) $(HOSTCFLAGS) -o addRamDisk addRamDisk.c + +vmlinux.initrd: $(TOPDIR)/vmlinux addRamDisk ramdisk.image.gz $(TOPDIR)/System.map + ./addRamDisk ramdisk.image.gz $(TOPDIR)/System.map $(TOPDIR)/vmlinux vmlinux.initrd + +vmlinux.sminitrd: vmlinux.sm addRamDisk ramdisk.image.gz $(TOPDIR)/System.map + ./addRamDisk ramdisk.image.gz $(TOPDIR)/System.map vmlinux.sm vmlinux.sminitrd + +endif + + +znetboot.initrd: zImage.initrd + cp zImage.initrd $(TFTPIMAGE) + +floppy: zImage + mcopy zImage a:zImage + +piggyback: piggyback.c + $(HOSTCC) $(HOSTCFLAGS) -DKERNELBASE=$(KERNELBASE) -o piggyback piggyback.c + +addnote: addnote.c + $(HOSTCC) $(HOSTCFLAGS) -o addnote addnote.c + +image.o: piggyback vmlinux.gz + ./piggyback image < vmlinux.gz | $(BOOTAS) -o image.o + +sysmap.o: piggyback ../../../System.map + ./piggyback sysmap < ../../../System.map | $(BOOTAS) -o sysmap.o + +initrd.o: ramdisk.image.gz piggyback + ./piggyback initrd < ramdisk.image.gz | $(BOOTAS) -o initrd.o + +zImage: $(OBJS) no_initrd.o addnote + $(BOOTLD) $(LD_ARGS) -T zImage.lds -o $@ $(OBJS) no_initrd.o $(LIBS) + ./addnote $@ + +zImage.initrd: $(OBJS) initrd.o addnote + $(BOOTLD) $(LD_ARGS) -T zImage.lds -o $@ $(OBJS) initrd.o $(LIBS) + ./addnote $@ + + +vmlinux.gz: $(TOPDIR)/vmlinux + $(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux + ls -l vmlinux | awk '{printf "/* generated -- do not edit! */\nint uncompressed_size = %d;\n", $$5}' > imagesize.c + $(CROSS_COMPILE)nm -n $(TOPDIR)/vmlinux | tail -1 | awk '{printf "long vmlinux_end = 0x%s;\n", substr($$1,8)}' >> imagesize.c + gzip -vf9 vmlinux + +imagesize.c: vmlinux.gz + +clean: + rm -f piggyback note addnote $(OBJS) zImage zImage.initrd vmlinux.gz no_initrd.o imagesize.c addSystemMap vmlinux.sm addRamDisk vmlinux.initrd vmlinux.sminitrd + +fastdep: + $(TOPDIR)/scripts/mkdep *.[Sch] > .depend + +dep: + $(CPP) $(CPPFLAGS) -M *.S *.c > .depend + diff -Nru a/arch/ppc64/boot/addRamDisk.c b/arch/ppc64/boot/addRamDisk.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/addRamDisk.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,301 @@ +#include +#include +#include +#include +#include +#include +#include + +#define ElfHeaderSize (64 * 1024) +#define ElfPages (ElfHeaderSize / 4096) +#define KERNELBASE (0xc000000000000000) + +void get4k(FILE *file, char *buf ) +{ + unsigned j; + unsigned num = fread(buf, 1, 4096, file); + for ( j=num; j<4096; ++j ) + buf[j] = 0; +} + +void put4k(FILE *file, char *buf ) +{ + fwrite(buf, 1, 4096, file); +} + +void death(const char *msg, FILE *fdesc, const char *fname) +{ + printf(msg); + fclose(fdesc); + unlink(fname); + exit(1); +} + +int main(int argc, char **argv) +{ + char inbuf[4096]; + FILE *ramDisk = NULL; + FILE *sysmap = NULL; + FILE *inputVmlinux = NULL; + FILE *outputVmlinux = NULL; + + unsigned i = 0; + unsigned long ramFileLen = 0; + unsigned long ramLen = 0; + unsigned long roundR = 0; + + unsigned long sysmapFileLen = 0; + unsigned long sysmapLen = 0; + unsigned long sysmapPages = 0; + char* ptr_end = NULL; + unsigned long offset_end = 0; + + unsigned long kernelLen = 0; + unsigned long actualKernelLen = 0; + unsigned long round = 0; + unsigned long roundedKernelLen = 0; + unsigned long ramStartOffs = 0; + unsigned long ramPages = 0; + unsigned long roundedKernelPages = 0; + unsigned long hvReleaseData = 0; + u_int32_t eyeCatcher = 0xc8a5d9c4; + unsigned long naca = 0; + unsigned long xRamDisk = 0; + unsigned long xRamDiskSize = 0; + long padPages = 0; + + + if (argc < 2) { + printf("Name of RAM disk file missing.\n"); + exit(1); + } + + if (argc < 3) { + printf("Name of System Map input file is missing.\n"); + exit(1); + } + + if (argc < 4) { + printf("Name of vmlinux file missing.\n"); + exit(1); + } + + if (argc < 5) { + printf("Name of vmlinux output file missing.\n"); + exit(1); + } + + + ramDisk = fopen(argv[1], "r"); + if ( ! ramDisk ) { + printf("RAM disk file \"%s\" failed to open.\n", argv[1]); + exit(1); + } + + sysmap = fopen(argv[2], "r"); + if ( ! sysmap ) { + printf("System Map file \"%s\" failed to open.\n", argv[2]); + exit(1); + } + + inputVmlinux = fopen(argv[3], "r"); + if ( ! inputVmlinux ) { + printf("vmlinux file \"%s\" failed to open.\n", argv[3]); + exit(1); + } + + outputVmlinux = fopen(argv[4], "w+"); + if ( ! outputVmlinux ) { + printf("output vmlinux file \"%s\" failed to open.\n", argv[4]); + exit(1); + } + + + + /* Input Vmlinux file */ + fseek(inputVmlinux, 0, SEEK_END); + kernelLen = ftell(inputVmlinux); + fseek(inputVmlinux, 0, SEEK_SET); + printf("kernel file size = %d\n", kernelLen); + if ( kernelLen == 0 ) { + printf("You must have a linux kernel specified as argv[3]\n"); + exit(1); + } + + actualKernelLen = kernelLen - ElfHeaderSize; + + printf("actual kernel length (minus ELF header) = %d\n", actualKernelLen); + + round = actualKernelLen % 4096; + roundedKernelLen = actualKernelLen; + if ( round ) + roundedKernelLen += (4096 - round); + printf("Vmlinux length rounded up to a 4k multiple = %ld/0x%lx \n", roundedKernelLen, roundedKernelLen); + roundedKernelPages = roundedKernelLen / 4096; + printf("Vmlinux pages to copy = %ld/0x%lx \n", roundedKernelPages, roundedKernelPages); + + + + /* Input System Map file */ + /* (needs to be processed simply to determine if we need to add pad pages due to the static variables not being included in the vmlinux) */ + fseek(sysmap, 0, SEEK_END); + sysmapFileLen = ftell(sysmap); + fseek(sysmap, 0, SEEK_SET); + printf("%s file size = %ld/0x%lx \n", argv[2], sysmapFileLen, sysmapFileLen); + + sysmapLen = sysmapFileLen; + + roundR = 4096 - (sysmapLen % 4096); + if (roundR) { + printf("Rounding System Map file up to a multiple of 4096, adding %ld/0x%lx \n", roundR, roundR); + sysmapLen += roundR; + } + printf("Rounded System Map size is %ld/0x%lx \n", sysmapLen, sysmapLen); + + /* Process the Sysmap file to determine where _end is */ + sysmapPages = sysmapLen / 4096; + for (i=0; i +#include +#include +#include +#include +#include + +void xlate( char * inb, char * trb, unsigned len ) +{ + unsigned i; + for ( i=0; i> 4; + char c2 = c & 0xf; + if ( c1 > 9 ) + c1 = c1 + 'A' - 10; + else + c1 = c1 + '0'; + if ( c2 > 9 ) + c2 = c2 + 'A' - 10; + else + c2 = c2 + '0'; + *trb++ = c1; + *trb++ = c2; + } + *trb = 0; +} + +#define ElfHeaderSize (64 * 1024) +#define ElfPages (ElfHeaderSize / 4096) + +void get4k( /*istream *inf*/FILE *file, char *buf ) +{ + unsigned j; + unsigned num = fread(buf, 1, 4096, file); + for ( j=num; j<4096; ++j ) + buf[j] = 0; +} + +void put4k( /*ostream *outf*/FILE *file, char *buf ) +{ + fwrite(buf, 1, 4096, file); +} + +int main(int argc, char **argv) +{ + char inbuf[4096]; + FILE *sysmap = NULL; + char* ptr_end = NULL; + FILE *inputVmlinux = NULL; + FILE *outputVmlinux = NULL; + long i = 0; + unsigned long sysmapFileLen = 0; + unsigned long sysmapLen = 0; + unsigned long roundR = 0; + unsigned long kernelLen = 0; + unsigned long actualKernelLen = 0; + unsigned long round = 0; + unsigned long roundedKernelLen = 0; + unsigned long sysmapStartOffs = 0; + unsigned long sysmapPages = 0; + unsigned long roundedKernelPages = 0; + long padPages = 0; + if ( argc < 2 ) + { + printf("Name of System Map file missing.\n"); + exit(1); + } + + if ( argc < 3 ) + { + printf("Name of vmlinux file missing.\n"); + exit(1); + } + + if ( argc < 4 ) + { + printf("Name of vmlinux output file missing.\n"); + exit(1); + } + + sysmap = fopen(argv[1], "r"); + if ( ! sysmap ) + { + printf("System Map file \"%s\" failed to open.\n", argv[1]); + exit(1); + } + inputVmlinux = fopen(argv[2], "r"); + if ( ! inputVmlinux ) + { + printf("vmlinux file \"%s\" failed to open.\n", argv[2]); + exit(1); + } + outputVmlinux = fopen(argv[3], "w"); + if ( ! outputVmlinux ) + { + printf("output vmlinux file \"%s\" failed to open.\n", argv[3]); + exit(1); + } + + + + fseek(inputVmlinux, 0, SEEK_END); + kernelLen = ftell(inputVmlinux); + fseek(inputVmlinux, 0, SEEK_SET); + printf("kernel file size = %ld\n", kernelLen); + if ( kernelLen == 0 ) + { + printf("You must have a linux kernel specified as argv[2]\n"); + exit(1); + } + + + actualKernelLen = kernelLen - ElfHeaderSize; + + printf("actual kernel length (minus ELF header) = %ld/%lxx \n", actualKernelLen, actualKernelLen); + + round = actualKernelLen % 4096; + roundedKernelLen = actualKernelLen; + if ( round ) + roundedKernelLen += (4096 - round); + + printf("Kernel length rounded up to a 4k multiple = %ld/%lxx \n", roundedKernelLen, roundedKernelLen); + roundedKernelPages = roundedKernelLen / 4096; + printf("Kernel pages to copy = %ld/%lxx\n", roundedKernelPages, roundedKernelPages); + + + + /* Sysmap file */ + fseek(sysmap, 0, SEEK_END); + sysmapFileLen = ftell(sysmap); + fseek(sysmap, 0, SEEK_SET); + printf("%s file size = %ld\n", argv[1], sysmapFileLen); + + sysmapLen = sysmapFileLen; + + roundR = 4096 - (sysmapLen % 4096); + if (roundR) + { + printf("Rounding System Map file up to a multiple of 4096, adding %ld\n", roundR); + sysmapLen += roundR; + } + printf("Rounded System Map size is %ld\n", sysmapLen); + + /* Process the Sysmap file to determine the true end of the kernel */ + sysmapPages = sysmapLen / 4096; + printf("System map pages to copy = %ld\n", sysmapPages); + for (i=0; i +#include +#include +#include + +char arch[] = "PowerPC"; + +#define N_DESCR 6 +unsigned int descr[N_DESCR] = { + 0xffffffff, /* real-mode = true */ + 0x00c00000, /* real-base, i.e. where we expect OF to be */ + 0xffffffff, /* real-size */ + 0xffffffff, /* virt-base */ + 0xffffffff, /* virt-size */ + 0x4000, /* load-base */ +}; + +unsigned char buf[512]; + +#define GET_16BE(off) ((buf[off] << 8) + (buf[(off)+1])) +#define GET_32BE(off) ((GET_16BE(off) << 16) + GET_16BE((off)+2)) + +#define PUT_16BE(off, v) (buf[off] = ((v) >> 8) & 0xff, \ + buf[(off) + 1] = (v) & 0xff) +#define PUT_32BE(off, v) (PUT_16BE((off), (v) >> 16), \ + PUT_16BE((off) + 2, (v))) + +/* Structure of an ELF file */ +#define E_IDENT 0 /* ELF header */ +#define E_PHOFF 28 +#define E_PHENTSIZE 42 +#define E_PHNUM 44 +#define E_HSIZE 52 /* size of ELF header */ + +#define EI_MAGIC 0 /* offsets in E_IDENT area */ +#define EI_CLASS 4 +#define EI_DATA 5 + +#define PH_TYPE 0 /* ELF program header */ +#define PH_OFFSET 4 +#define PH_FILESZ 16 +#define PH_HSIZE 32 /* size of program header */ + +#define PT_NOTE 4 /* Program header type = note */ + +#define ELFCLASS32 1 +#define ELFDATA2MSB 2 + +unsigned char elf_magic[4] = { 0x7f, 'E', 'L', 'F' }; + +int +main(int ac, char **av) +{ + int fd, n, i; + int ph, ps, np; + int nnote, ns; + + if (ac != 2) { + fprintf(stderr, "Usage: %s elf-file\n", av[0]); + exit(1); + } + fd = open(av[1], O_RDWR); + if (fd < 0) { + perror(av[1]); + exit(1); + } + + nnote = strlen(arch) + 1 + (N_DESCR + 3) * 4; + + n = read(fd, buf, sizeof(buf)); + if (n < 0) { + perror("read"); + exit(1); + } + + if (n < E_HSIZE || memcmp(&buf[E_IDENT+EI_MAGIC], elf_magic, 4) != 0) + goto notelf; + + if (buf[E_IDENT+EI_CLASS] != ELFCLASS32 + || buf[E_IDENT+EI_DATA] != ELFDATA2MSB) { + fprintf(stderr, "%s is not a big-endian 32-bit ELF image\n", + av[1]); + exit(1); + } + + ph = GET_32BE(E_PHOFF); + ps = GET_16BE(E_PHENTSIZE); + np = GET_16BE(E_PHNUM); + if (ph < E_HSIZE || ps < PH_HSIZE || np < 1) + goto notelf; + if (ph + (np + 1) * ps + nnote > n) + goto nospace; + + for (i = 0; i < np; ++i) { + if (GET_32BE(ph + PH_TYPE) == PT_NOTE) { + fprintf(stderr, "%s already has a note entry\n", + av[1]); + exit(0); + } + ph += ps; + } + + /* XXX check that the area we want to use is all zeroes */ + for (i = 0; i < ps + nnote; ++i) + if (buf[ph + i] != 0) + goto nospace; + + /* fill in the program header entry */ + ns = ph + ps; + PUT_32BE(ph + PH_TYPE, PT_NOTE); + PUT_32BE(ph + PH_OFFSET, ns); + PUT_32BE(ph + PH_FILESZ, nnote); + + /* fill in the note area we point to */ + /* XXX we should probably make this a proper section */ + PUT_32BE(ns, strlen(arch) + 1); + PUT_32BE(ns + 4, N_DESCR * 4); + PUT_32BE(ns + 8, 0x1275); + strcpy(&buf[ns + 12], arch); + ns += 12 + strlen(arch) + 1; + for (i = 0; i < N_DESCR; ++i) + PUT_32BE(ns + i * 4, descr[i]); + + /* Update the number of program headers */ + PUT_16BE(E_PHNUM, np + 1); + + /* write back */ + lseek(fd, (long) 0, SEEK_SET); + i = write(fd, buf, n); + if (i < 0) { + perror("write"); + exit(1); + } + if (i < n) { + fprintf(stderr, "%s: write truncated\n", av[1]); + exit(1); + } + + exit(0); + + notelf: + fprintf(stderr, "%s does not appear to be an ELF file\n", av[0]); + exit(1); + + nospace: + fprintf(stderr, "sorry, I can't find space in %s to put the note\n", + av[0]); + exit(1); +} diff -Nru a/arch/ppc64/boot/crt0.S b/arch/ppc64/boot/crt0.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/crt0.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,265 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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. + * + * NOTE: this code runs in 32 bit mode and is packaged as ELF32. + */ + .text + .globl _start +_start: + lis 9,_start@h + lis 8,_etext@ha + addi 8,8,_etext@l +1: dcbf 0,9 + icbi 0,9 + addi 9,9,0x20 + cmplwi 0,9,8 + blt 1b + sync + isync + + ## Clear out the BSS as per ANSI C requirements + + lis 7,_end@ha + addi 7,7,_end@l # r7 = &_end + lis 8,__bss_start@ha # + addi 8,8,__bss_start@l # r8 = &_bss_start + + ## Determine how large an area, in number of words, to clear + + subf 7,8,7 # r7 = &_end - &_bss_start + 1 + addi 7,7,3 # r7 += 3 + srwi. 7,7,2 # r7 = size in words. + beq 3f # If the size is zero, do not bother + addi 8,8,-4 # r8 -= 4 + mtctr 7 # SPRN_CTR = number of words to clear + li 0,0 # r0 = 0 +2: stwu 0,4(8) # Clear out a word + bdnz 2b # If we are not done yet, keep clearing +3: + + + b start + + + +/* + * Flush the dcache and invalidate the icache for a range of addresses. + * + * flush_cache(addr, len) + */ + .global flush_cache +flush_cache: + addi 4,4,0x1f /* len = (len + 0x1f) / 0x20 */ + rlwinm. 4,4,27,5,31 + mtctr 4 + beqlr +1: dcbf 0,3 + icbi 0,3 + addi 3,3,0x20 + bdnz 1b + sync + isync + blr + + +#define r0 0 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 + + .globl strcpy +strcpy: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strncpy +strncpy: + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + + .globl strcat +strcat: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strcmp +strcmp: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + + .globl strlen +strlen: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + + .globl memset +memset: + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + rlwinm r0,r5,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + + .globl bcopy +bcopy: + mr r6,r3 + mr r3,r4 + mr r4,r6 + b memcpy + + .globl memmove +memmove: + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + + .globl memcpy +memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl backwards_memcpy +backwards_memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl memcmp +memcmp: + cmpwi 0,r5,0 + blelr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr diff -Nru a/arch/ppc64/boot/main.c b/arch/ppc64/boot/main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/main.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,292 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * Updates for PPC64 by Todd Inglett & Dave Engebretsen. + * + * 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. + */ +#define __KERNEL__ +#include "zlib.h" +#include +#include +#include + +void memmove(void *dst, void *im, int len); + +extern void *finddevice(const char *); +extern int getprop(void *, const char *, void *, int); +extern void printf(const char *fmt, ...); +extern int sprintf(char *buf, const char *fmt, ...); +void gunzip(void *, int, unsigned char *, int *); +void *claim(unsigned int, unsigned int, unsigned int); +void flush_cache(void *, int); +void pause(void); +static struct bi_record *make_bi_recs(unsigned long); + +#define RAM_START 0x00000000 +#define RAM_END (64<<20) + +#define BOOT_START ((unsigned long)_start) +#define BOOT_END ((unsigned long)_end) + +/* Value picked to match that used by yaboot */ +#define PROG_START 0x01400000 + +char *avail_ram; +char *begin_avail, *end_avail; +char *avail_high; +unsigned int heap_use; +unsigned int heap_max; +unsigned long initrd_start = 0; +unsigned long initrd_size = 0; + +extern char _end[]; +extern char image_data[]; +extern int image_len; +extern char initrd_data[]; +extern int initrd_len; +extern char sysmap_data[]; +extern int sysmap_len; +extern int uncompressed_size; +extern long vmlinux_end; + +static char scratch[128<<10]; /* 128kB of scratch space for gunzip */ + +typedef void (*kernel_entry_t)( unsigned long, + unsigned long, + void *, + struct bi_record *); + +void +chrpboot(unsigned long a1, unsigned long a2, void *prom) +{ + unsigned len; + void *dst = (void *)-1; + unsigned long claim_addr; + unsigned char *im; + extern char _start; + struct bi_record *bi_recs; + kernel_entry_t kernel_entry; + + printf("chrpboot starting: loaded at 0x%x\n\r", (unsigned)&_start); + + if (initrd_len) { + initrd_size = initrd_len; + initrd_start = (RAM_END - initrd_size) & ~0xFFF; + a1 = a2 = 0; + claim(initrd_start, RAM_END - initrd_start, 0); + printf("initial ramdisk moving 0x%lx <- 0x%lx (%lx bytes)\n\r", + initrd_start, (unsigned long)initrd_data, initrd_size); + memcpy((void *)initrd_start, (void *)initrd_data, initrd_size); + } + + im = image_data; + len = image_len; + uncompressed_size = PAGE_ALIGN(uncompressed_size); + + for(claim_addr = PROG_START; + claim_addr <= PROG_START * 8; + claim_addr += 0x100000) { + printf(" trying: 0x%08lx\n\r", claim_addr); + dst = claim(claim_addr, uncompressed_size, 0); + if (dst != (void *)-1) break; + } + if (dst == (void *)-1) { + printf("claim error, can't allocate kernel memory\n\r"); + return; + } + + if (im[0] == 0x1f && im[1] == 0x8b) { + avail_ram = scratch; + begin_avail = avail_high = avail_ram; + end_avail = scratch + sizeof(scratch); + printf("gunzipping (0x%x <- 0x%x:0x%0x)...", + (unsigned)dst, (unsigned)im, (unsigned)im+len); + gunzip(dst, uncompressed_size, im, &len); + printf("done %u bytes\n\r", len); + printf("%u bytes of heap consumed, max in use %u\n\r", + (unsigned)(avail_high - begin_avail), heap_max); + } else { + memmove(dst, im, len); + } + + flush_cache(dst, len); + + bi_recs = make_bi_recs((unsigned long)dst + vmlinux_end); + + kernel_entry = (kernel_entry_t)dst; + printf( "kernel:\n\r" + " entry addr = 0x%lx\n\r" + " a1 = 0x%lx,\n\r" + " a2 = 0x%lx,\n\r" + " prom = 0x%lx,\n\r" + " bi_recs = 0x%lx,\n\r", + (unsigned long)kernel_entry, a1, a2, + (unsigned long)prom, (unsigned long)bi_recs); + + kernel_entry( a1, a2, prom, bi_recs ); + + printf("returned?\n\r"); + + pause(); +} + +static struct bi_record * +make_bi_recs(unsigned long addr) +{ + struct bi_record *bi_recs; + struct bi_record *rec; + + bi_recs = rec = bi_rec_init(addr); + + rec = bi_rec_alloc(rec, 2); + rec->tag = BI_FIRST; + /* rec->data[0] = ...; # Written below before return */ + /* rec->data[1] = ...; # Written below before return */ + + rec = bi_rec_alloc_bytes(rec, strlen("chrpboot")+1); + rec->tag = BI_BOOTLOADER_ID; + sprintf( (char *)rec->data, "chrpboot"); + + rec = bi_rec_alloc(rec, 2); + rec->tag = BI_MACHTYPE; + rec->data[0] = _MACH_pSeries; + rec->data[1] = 1; + + if ( initrd_size > 0 ) { + rec = bi_rec_alloc(rec, 2); + rec->tag = BI_INITRD; + rec->data[0] = initrd_start; + rec->data[1] = initrd_size; + } + +#if 0 + if ( sysmap_len > 0 ) { + rec = bi_rec_alloc(rec, 2); + rec->tag = BI_SYSMAP; + rec->data[0] = (unsigned long)sysmap_data; + rec->data[1] = sysmap_len; + } +#endif + + rec = bi_rec_alloc(rec, 1); + rec->tag = BI_LAST; + rec->data[0] = (bi_rec_field)bi_recs; + + /* Save the _end_ address of the bi_rec's in the first bi_rec + * data field for easy access by the kernel. + */ + bi_recs->data[0] = (bi_rec_field)rec; + bi_recs->data[1] = (bi_rec_field)rec + rec->size - (bi_rec_field)bi_recs; + + return bi_recs; +} + +struct memchunk { + unsigned int size; + unsigned int pad; + struct memchunk *next; +}; + +static struct memchunk *freechunks; + +void *zalloc(void *x, unsigned items, unsigned size) +{ + void *p; + struct memchunk **mpp, *mp; + + size *= items; + size = _ALIGN(size, sizeof(struct memchunk)); + heap_use += size; + if (heap_use > heap_max) + heap_max = heap_use; + for (mpp = &freechunks; (mp = *mpp) != 0; mpp = &mp->next) { + if (mp->size == size) { + *mpp = mp->next; + return mp; + } + } + p = avail_ram; + avail_ram += size; + if (avail_ram > avail_high) + avail_high = avail_ram; + if (avail_ram > end_avail) { + printf("oops... out of memory\n\r"); + pause(); + } + return p; +} + +void zfree(void *x, void *addr, unsigned nb) +{ + struct memchunk *mp = addr; + + nb = _ALIGN(nb, sizeof(struct memchunk)); + heap_use -= nb; + if (avail_ram == addr + nb) { + avail_ram = addr; + return; + } + mp->size = nb; + mp->next = freechunks; + freechunks = mp; +} + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +#define DEFLATED 8 + +void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) +{ + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + printf("bad gzipped data\n\r"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + printf("gunzip: ran out of data in header\n\r"); + exit(); + } + + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("inflateInit2 returned %d\n\r", r); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + printf("inflate returned %d msg: %s\n\r", r, s.msg); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); +} + diff -Nru a/arch/ppc64/boot/mknote.c b/arch/ppc64/boot/mknote.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/mknote.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,43 @@ +/* + * Copyright (C) Cort Dougan 1999. + * + * 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. + * + * Generate a note section as per the CHRP specification. + * + */ + +#include + +#define PL(x) printf("%c%c%c%c", ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, (x)&0xff ); + +int main(void) +{ +/* header */ + /* namesz */ + PL(strlen("PowerPC")+1); + /* descrsz */ + PL(6*4); + /* type */ + PL(0x1275); + /* name */ + printf("PowerPC"); printf("%c", 0); + +/* descriptor */ + /* real-mode */ + PL(0xffffffff); + /* real-base */ + PL(0x00c00000); + /* real-size */ + PL(0xffffffff); + /* virt-base */ + PL(0xffffffff); + /* virt-size */ + PL(0xffffffff); + /* load-base */ + PL(0x4000); + return 0; +} diff -Nru a/arch/ppc64/boot/no_initrd.c b/arch/ppc64/boot/no_initrd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/no_initrd.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,2 @@ +char initrd_data[1]; +int initrd_len = 0; diff -Nru a/arch/ppc64/boot/piggyback.c b/arch/ppc64/boot/piggyback.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/piggyback.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,74 @@ +/* + * Copyright 2001 IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include + +extern long ce_exec_config[]; + +int main(int argc, char *argv[]) +{ + int i, cnt, pos, len; + unsigned int cksum, val; + unsigned char *lp; + unsigned char buf[8192]; + if (argc != 2) + { + fprintf(stderr, "usage: %s name out-file\n", + argv[0]); + exit(1); + } + fprintf(stdout, "#\n"); + fprintf(stdout, "# Miscellaneous data structures:\n"); + fprintf(stdout, "# WARNING - this file is automatically generated!\n"); + fprintf(stdout, "#\n"); + fprintf(stdout, "\n"); + fprintf(stdout, "\t.data\n"); + fprintf(stdout, "\t.globl %s_data\n", argv[1]); + fprintf(stdout, "%s_data:\n", argv[1]); + pos = 0; + cksum = 0; + while ((len = read(0, buf, sizeof(buf))) > 0) + { + cnt = 0; + lp = (unsigned char *)buf; + len = (len + 3) & ~3; /* Round up to longwords */ + for (i = 0; i < len; i += 4) + { + if (cnt == 0) + { + fprintf(stdout, "\t.long\t"); + } + fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); + val = *(unsigned long *)lp; + cksum ^= val; + lp += 4; + if (++cnt == 4) + { + cnt = 0; + fprintf(stdout, " # %x \n", pos+i-12); + fflush(stdout); + } else + { + fprintf(stdout, ","); + } + } + if (cnt) + { + fprintf(stdout, "0\n"); + } + pos += len; + } + fprintf(stdout, "\t.globl %s_len\n", argv[1]); + fprintf(stdout, "%s_len:\t.long\t0x%x\n", argv[1], pos); + fflush(stdout); + fclose(stdout); + fprintf(stderr, "cksum = %x\n", cksum); + exit(0); +} + diff -Nru a/arch/ppc64/boot/start.c b/arch/ppc64/boot/start.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/start.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,654 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include + +#include + +int (*prom)(void *); + +void *chosen_handle; +void *stdin; +void *stdout; +void *stderr; + +void exit(void); +void *finddevice(const char *name); +int getprop(void *phandle, const char *name, void *buf, int buflen); +void chrpboot(int a1, int a2, void *prom); /* in main.c */ + +void printk(char *fmt, ...); + +void +start(int a1, int a2, void *promptr) +{ + prom = (int (*)(void *)) promptr; + chosen_handle = finddevice("/chosen"); + if (chosen_handle == (void *) -1) + exit(); + if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4) + exit(); + stderr = stdout; + if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4) + exit(); + + chrpboot(a1, a2, promptr); + for (;;) + exit(); +} + +int +write(void *handle, void *ptr, int nb) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "write"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; +} + +int +read(void *handle, void *ptr, int nb) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "read"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; +} + +void +exit() +{ + struct prom_args { + char *service; + } args; + + for (;;) { + args.service = "exit"; + (*prom)(&args); + } +} + +void +pause(void) +{ + struct prom_args { + char *service; + } args; + + args.service = "enter"; + (*prom)(&args); +} + +void * +finddevice(const char *name) +{ + struct prom_args { + char *service; + int nargs; + int nret; + const char *devspec; + void *phandle; + } args; + + args.service = "finddevice"; + args.nargs = 1; + args.nret = 1; + args.devspec = name; + args.phandle = (void *) -1; + (*prom)(&args); + return args.phandle; +} + +void * +claim(unsigned long virt, unsigned long size, unsigned long align) +{ + struct prom_args { + char *service; + int nargs; + int nret; + unsigned int virt; + unsigned int size; + unsigned int align; + void *ret; + } args; + + args.service = "claim"; + args.nargs = 3; + args.nret = 1; + args.virt = virt; + args.size = size; + args.align = align; + (*prom)(&args); + return args.ret; +} + +int +getprop(void *phandle, const char *name, void *buf, int buflen) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *phandle; + const char *name; + void *buf; + int buflen; + int size; + } args; + + args.service = "getprop"; + args.nargs = 4; + args.nret = 1; + args.phandle = phandle; + args.name = name; + args.buf = buf; + args.buflen = buflen; + args.size = -1; + (*prom)(&args); + return args.size; +} + +int +putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + putc('\r', f); + return write(f, &ch, 1) == 1? c: -1; +} + +int +putchar(int c) +{ + return putc(c, stdout); +} + +int +fputs(char *str, void *f) +{ + int n = strlen(str); + + return write(f, str, n) == n? 0: -1; +} + +int +readchar(void) +{ + char ch; + + for (;;) { + switch (read(stdin, &ch, 1)) { + case 1: + return ch; + case -1: + printk("read(stdin) returned -1\r\n"); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int +getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + putchar('\a'); + else { + putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + + + +/* String functions lifted from lib/vsprintf.c and lib/ctype.c */ +unsigned char _ctype[] = { +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ +_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ +_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ +_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ +_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ +_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ + +size_t strnlen(const char * s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base) +{ + unsigned long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((*cp == 'x') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } + while (isxdigit(*cp) && + (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + +long simple_strtol(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoul(cp+1,endp,base); + return simple_strtoul(cp,endp,base); +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +static char * number(char * str, long long num, int base, int size, int precision, int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +/* Forward decl. needed for IP address printing stuff... */ +int sprintf(char * buf, const char *fmt, ...); + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long long num; + int i, base; + char * str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else if (qualifier == 'Z') { + size_t * ip = va_arg(args, size_t *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + if (qualifier == 'L') + num = va_arg(args, long long); + else if (qualifier == 'l') { + num = va_arg(args, unsigned long); + if (flags & SIGN) + num = (signed long) num; + } else if (qualifier == 'Z') { + num = va_arg(args, size_t); + } else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (signed short) num; + } else { + num = va_arg(args, unsigned int); + if (flags & SIGN) + num = (signed int) num; + } + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-buf; +} + +int sprintf(char * buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + va_end(args); + return i; +} + +static char sprint_buf[1024]; + +void +printk(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + write(stdout, sprint_buf, n); +} + +int +printf(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + write(stdout, sprint_buf, n); + return n; +} diff -Nru a/arch/ppc64/boot/zImage.lds b/arch/ppc64/boot/zImage.lds --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/zImage.lds Tue Feb 19 18:09:00 2002 @@ -0,0 +1,78 @@ +OUTPUT_ARCH(powerpc) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .plt : { *(.plt) } + .text : + { + *(.text) + *(.fixup) + *(.got1) + } + . = ALIGN(4096); + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.rodata) + *(.rodata1) + } + .kstrtab : { *(.kstrtab) } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + /* Read-write section, merged into data segment: */ + . = ALIGN(4096); + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + CONSTRUCTORS + } + . = ALIGN(4096); + _edata = .; + PROVIDE (edata = .); + + .fixup : { *(.fixup) } + + . = ALIGN(4096); + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + . = ALIGN(4096); + _end = . ; + PROVIDE (end = .); +} diff -Nru a/arch/ppc64/boot/zlib.c b/arch/ppc64/boot/zlib.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/zlib.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2170 @@ +/* + * This file is derived from various .h and .c files from the zlib-0.95 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. See zlib.h for conditions of + * distribution and use. + * + * Changes that have been made include: + * - changed functions not used outside this file to "local" + * - added minCompression parameter to deflateInit2 + * - added Z_PACKET_FLUSH (see zlib.h for details) + * - added inflateIncomp + * + Copyright (C) 1995 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + gzip@prep.ai.mit.edu madler@alumni.caltech.edu + + * + * + */ + +/*+++++*/ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */ + +#define _Z_UTIL_H + +#include "zlib.h" + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#define FAR + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern char *z_errmsg[]; /* indexed by 1-zlib_error */ + +#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err) +/* To be used only when the state is known to be valid */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + + /* common constants */ + +#define DEFLATED 8 + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + + /* functions */ + +#include +#define zmemcpy memcpy +#define zmemzero(dest, len) memset(dest, 0, len) + +/* Diagnostic functions */ +#ifdef DEBUG_ZLIB +# include +# ifndef verbose +# define verbose 0 +# endif +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len)); + +/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */ +/* void zcfree OF((voidpf opaque, voidpf ptr)); */ + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr, size) \ + (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size)) +#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);} + +/* deflate.h -- internal compression state + * Copyright (C) 1995 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/*+++++*/ +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_blocks_state; +typedef struct inflate_blocks_state FAR inflate_blocks_statef; + +local inflate_blocks_statef * inflate_blocks_new OF(( + z_stream *z, + check_func c, /* check function */ + uInt w)); /* window size */ + +local int inflate_blocks OF(( + inflate_blocks_statef *, + z_stream *, + int)); /* initial return code */ + +local void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_stream *, + uLongf *)); /* check value on output */ + +local int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_stream *, + uLongf *)); /* check value on output */ + +local int inflate_addhistory OF(( + inflate_blocks_statef *, + z_stream *)); + +local int inflate_packet_flush OF(( + inflate_blocks_statef *)); + +/*+++++*/ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +typedef struct inflate_huft_s FAR inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + uInt Nalloc; /* number of these allocated here */ + Bytef *pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit machines) */ + union { + uInt Base; /* literal, length base, or distance base */ + inflate_huft *Next; /* pointer to next level of table */ + } more; +}; + +#ifdef DEBUG_ZLIB + local uInt inflate_hufts; +#endif + +local int inflate_trees_bits OF(( + uIntf *, /* 19 code lengths */ + uIntf *, /* bits tree desired/actual depth */ + inflate_huft * FAR *, /* bits tree result */ + z_stream *)); /* for zalloc, zfree functions */ + +local int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uIntf *, /* that many (total) code lengths */ + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + z_stream *)); /* for zalloc, zfree functions */ + +local int inflate_trees_fixed OF(( + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *)); /* distance tree result */ + +local int inflate_trees_free OF(( + inflate_huft *, /* tables to free */ + z_stream *)); /* for zfree function */ + + +/*+++++*/ +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_codes_state; +typedef struct inflate_codes_state FAR inflate_codes_statef; + +local inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_stream *)); + +local int inflate_codes OF(( + inflate_blocks_statef *, + z_stream *, + int)); + +local void inflate_codes_free OF(( + inflate_codes_statef *, + z_stream *)); + + +/*+++++*/ +/* inflate.c -- zlib interface to inflate modules + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* inflate private state */ +struct internal_state { + + /* mode */ + enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + DONE, /* finished check, done */ + BAD} /* got an error--stay here */ + mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +int inflateReset(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? BLOCKS : METHOD; + inflate_blocks_reset(z->state->blocks, z, &c); + Trace((stderr, "inflate: reset\n")); + return Z_OK; +} + + +int inflateEnd(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z, &c); + ZFREE(z, z->state, sizeof(struct internal_state)); + z->state = Z_NULL; + Trace((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z, w) +z_stream *z; +int w; +{ + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; +/* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */ +/* if (z->zfree == Z_NULL) z->zfree = zcfree; */ + if ((z->state = (struct internal_state FAR *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Trace((stderr, "inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + +int inflateInit(z) +z_stream *z; +{ + return inflateInit2(z, DEF_WBITS); +} + + +#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;} +#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z, f) +z_stream *z; +int f; +{ + int r; + uInt b; + + if (z == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case METHOD: + NEEDBYTE + if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED) + { + z->state->mode = BAD; + z->msg = "unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = BAD; + z->msg = "invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = FLAG; + case FLAG: + NEEDBYTE + if ((b = NEXTBYTE) & 0x20) + { + z->state->mode = BAD; + z->msg = "invalid reserved bit"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = BAD; + z->msg = "incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib header ok\n")); + z->state->mode = BLOCKS; + case BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) + r = inflate_packet_flush(z->state->blocks); + if (r == Z_DATA_ERROR) + { + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r != Z_STREAM_END) + return r; + r = Z_OK; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = DONE; + break; + } + z->state->mode = CHECK4; + case CHECK4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = CHECK3; + case CHECK3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = CHECK2; + case CHECK2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = CHECK1; + case CHECK1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = BAD; + z->msg = "incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib check ok\n")); + z->state->mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + + empty: + if (f != Z_PACKET_FLUSH) + return r; + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_DATA_ERROR; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ + +int inflateIncomp(z) +z_stream *z; +{ + if (z->state->mode != BLOCKS) + return Z_DATA_ERROR; + return inflate_addhistory(z->state->blocks, z); +} + + +int inflateSync(z) +z_stream *z; +{ + uInt n; /* number of bytes to look at */ + Bytef *p; /* pointer to bytes */ + uInt m; /* number of marker bytes found in a row */ + uLong r, w; /* temporaries to save total_in and total_out */ + + /* set up */ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode != BAD) + { + z->state->mode = BAD; + z->state->sub.marker = 0; + } + if ((n = z->avail_in) == 0) + return Z_BUF_ERROR; + p = z->next_in; + m = z->state->sub.marker; + + /* search */ + while (n && m < 4) + { + if (*p == (Byte)(m < 2 ? 0 : 0xff)) + m++; + else if (*p) + m = 0; + else + m = 4 - m; + p++, n--; + } + + /* restore */ + z->total_in += p - z->next_in; + z->next_in = p; + z->avail_in = n; + z->state->sub.marker = m; + + /* return no joy or set up to restart on a new block */ + if (m != 4) + return Z_DATA_ERROR; + r = z->total_in; w = z->total_out; + inflateReset(z); + z->total_in = r; z->total_out = w; + z->state->mode = BLOCKS; + return Z_OK; +} + +#undef NEEDBYTE +#undef NEXTBYTE + +/*+++++*/ +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONEB, /* finished last block, done */ + BADB} /* got a data error--stuck here */ + mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uIntf *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + int nblens; /* # elements allocated at blens */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_huft *tl, *td; /* trees to free */ + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + Bytef *window; /* sliding window */ + Bytef *end; /* one byte after sliding window */ + Bytef *read; /* window read pointer */ + Bytef *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=WAVAIL;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load local pointers */ +#define LOAD {LOADIN LOADOUT} + +/* + * The IBM 150 firmware munges the data right after _etext[]. This + * protects it. -- Cort + */ +local uInt protect_mask[] = {0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0}; +/* And'ing with mask[n] masks the lower n bits */ +local uInt inflate_mask[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush OF(( + inflate_blocks_statef *, + z_stream *, + int)); + +/*+++++*/ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +local int inflate_fast OF(( + uInt, + uInt, + inflate_huft *, + inflate_huft *, + inflate_blocks_statef *, + z_stream *)); + + +/*+++++*/ +/* infblock.c -- interpret and process block types to last block + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* Table for deflate from PKZIP's appnote.txt. */ +local uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +local void inflate_blocks_reset(s, z, c) +inflate_blocks_statef *s; +z_stream *z; +uLongf *c; +{ + if (s->checkfn != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); + if (s->mode == CODES) + { + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + } + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(0L, Z_NULL, 0); + Trace((stderr, "inflate: blocks reset\n")); +} + + +local inflate_blocks_statef *inflate_blocks_new(z, c, w) +z_stream *z; +check_func c; +uInt w; +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s, sizeof(struct inflate_blocks_state)); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Trace((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, &s->check); + return s; +} + + +local int inflate_blocks(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Trace((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Trace((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.tl = Z_NULL; /* don't try to free these */ + s->sub.decode.td = Z_NULL; + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Trace((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BADB; + z->msg = "invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if (((~b) >> 16) != (b & 0xffff)) + { + s->mode = BADB; + z->msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : TYPE; + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BADB; + z->msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (t < 19) + t = 19; + if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.trees.nblens = t; + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, z); + if (t != Z_OK) + { + r = t; + if (r == Z_DATA_ERROR) + s->mode = BADB; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->word.what.Bits; + c = h->more.Base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + s->mode = BADB; + z->msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + inflate_trees_free(s->sub.trees.tb, z); + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, z); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = BADB; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + inflate_trees_free(td, z); + inflate_trees_free(tl, z); + r = Z_MEM_ERROR; + LEAVE + } + ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); + s->sub.decode.codes = c; + s->sub.decode.tl = tl; + s->sub.decode.td = td; + } + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONEB; + case DONEB: + r = Z_STREAM_END; + LEAVE + case BADB: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +local int inflate_blocks_free(s, z, c) +inflate_blocks_statef *s; +z_stream *z; +uLongf *c; +{ + inflate_blocks_reset(s, z, c); + ZFREE(z, s->window, s->end - s->window); + ZFREE(z, s, sizeof(struct inflate_blocks_state)); + Trace((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ +local int inflate_addhistory(s, z) +inflate_blocks_statef *s; +z_stream *z; +{ + uLong b; /* bit buffer */ /* NOT USED HERE */ + uInt k; /* bits in bit buffer */ /* NOT USED HERE */ + uInt t; /* temporary storage */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + if (s->read != s->write) + return Z_STREAM_ERROR; + if (s->mode != TYPE) + return Z_DATA_ERROR; + + /* we're ready to rock */ + LOAD + /* while there is input ready, copy to output buffer, moving + * pointers as needed. + */ + while (n) { + t = n; /* how many to do */ + /* is there room until end of buffer? */ + if (t > m) t = m; + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, t); + zmemcpy(q, p, t); + q += t; + p += t; + n -= t; + z->total_out += t; + s->read = q; /* drag read pointer forward */ +/* WRAP */ /* expand WRAP macro by hand to handle s->read */ + if (q == s->end) { + s->read = q = s->window; + m = WAVAIL; + } + } + UPDATE + return Z_OK; +} + + +/* + * At the end of a Deflate-compressed PPP packet, we expect to have seen + * a `stored' block type value but not the (zero) length bytes. + */ +local int inflate_packet_flush(s) + inflate_blocks_statef *s; +{ + if (s->mode != LENS) + return Z_DATA_ERROR; + s->mode = TYPE; + return Z_OK; +} + + +/*+++++*/ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + + +local int huft_build OF(( + uIntf *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + uIntf *, /* list of base values for non-simple codes */ + uIntf *, /* list of extra bits for non-simple codes */ + inflate_huft * FAR*,/* result: starting table */ + uIntf *, /* maximum lookup bits (returns actual) */ + z_stream *)); /* for zalloc function */ + +local voidpf falloc OF(( + voidpf, /* opaque pointer (not used) */ + uInt, /* number of items */ + uInt)); /* size of item */ + +local void ffree OF(( + voidpf q, /* opaque pointer (not used) */ + voidpf p, /* what to free (not used) */ + uInt n)); /* number of bytes (not used) */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* actually lengths - 2; also see note #13 above about 258 */ +local uInt cplext[] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */ +local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +local uInt cpdext[] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ +#define N_MAX 288 /* maximum number of codes in any set */ + +#ifdef DEBUG_ZLIB + uInt inflate_hufts; +#endif + +local int huft_build(b, n, s, d, e, t, m, zs) +uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ +uInt n; /* number of codes (assumed <= N_MAX) */ +uInt s; /* number of simple-valued codes (0..s-1) */ +uIntf *d; /* list of base values for non-simple codes */ +uIntf *e; /* list of extra bits for non-simple codes */ +inflate_huft * FAR *t; /* result: starting table */ +uIntf *m; /* maximum lookup bits, returns actual */ +z_stream *zs; /* for zalloc function */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), Z_DATA_ERROR if the input is invalid (all zero length codes or an + over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */ +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register uInt i; /* counter, current code */ + register uInt j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register uIntf *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + uInt v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uIntf *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + if ((q = (inflate_huft *)ZALLOC + (zs,z + 1,sizeof(inflate_huft))) == Z_NULL) + { + if (h) + inflate_trees_free(u[0], zs); + return Z_MEM_ERROR; /* not enough memory */ + } + q->word.Nalloc = z + 1; +#ifdef DEBUG_ZLIB + inflate_hufts += z + 1; +#endif + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->next)) = Z_NULL; + u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + r.next = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +local int inflate_trees_bits(c, bb, tb, z) +uIntf *c; /* 19 code lengths */ +uIntf *bb; /* bits tree desired/actual depth */ +inflate_huft * FAR *tb; /* bits tree result */ +z_stream *z; /* for zfree function */ +{ + int r; + + r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z); + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR) + { + inflate_trees_free(*tb, z); + z->msg = "incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + return r; +} + + +local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z) +uInt nl; /* number of literal/length codes */ +uInt nd; /* number of distance codes */ +uIntf *c; /* that many (total) code lengths */ +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +z_stream *z; /* for zfree function */ +{ + int r; + + /* build literal/length tree */ + if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK) + { + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed literal/length tree"; + else if (r == Z_BUF_ERROR) + { + inflate_trees_free(*tl, z); + z->msg = "incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + return r; + } + + /* build distance tree */ + if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK) + { + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed literal/length tree"; + else if (r == Z_BUF_ERROR) { +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + inflate_trees_free(*td, z); + z->msg = "incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + inflate_trees_free(*tl, z); + return r; +#endif + } + + /* done */ + return Z_OK; +} + + +/* build fixed tables only once--keep them here */ +local int fixed_lock = 0; +local int fixed_built = 0; +#define FIXEDH 530 /* number of hufts used by fixed tables */ +local uInt fixed_left = FIXEDH; +local inflate_huft fixed_mem[FIXEDH]; +local uInt fixed_bl; +local uInt fixed_bd; +local inflate_huft *fixed_tl; +local inflate_huft *fixed_td; + + +local voidpf falloc(q, n, s) +voidpf q; /* opaque pointer (not used) */ +uInt n; /* number of items */ +uInt s; /* size of item */ +{ + Assert(s == sizeof(inflate_huft) && n <= fixed_left, + "inflate_trees falloc overflow"); + if (q) s++; /* to make some compilers happy */ + fixed_left -= n; + return (voidpf)(fixed_mem + fixed_left); +} + + +local void ffree(q, p, n) +voidpf q; +voidpf p; +uInt n; +{ + Assert(0, "inflate_trees ffree called!"); + if (q) q = p; /* to make some compilers happy */ +} + + +local int inflate_trees_fixed(bl, bd, tl, td) +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +{ + /* build fixed tables if not built already--lock out other instances */ + while (++fixed_lock > 1) + fixed_lock--; + if (!fixed_built) + { + int k; /* temporary variable */ + unsigned c[288]; /* length list for huft_build */ + z_stream z; /* for falloc function */ + + /* set up fake z_stream for memory routines */ + z.zalloc = falloc; + z.zfree = ffree; + z.opaque = Z_NULL; + + /* literal table */ + for (k = 0; k < 144; k++) + c[k] = 8; + for (; k < 256; k++) + c[k] = 9; + for (; k < 280; k++) + c[k] = 7; + for (; k < 288; k++) + c[k] = 8; + fixed_bl = 7; + huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z); + + /* distance table */ + for (k = 0; k < 30; k++) + c[k] = 5; + fixed_bd = 5; + huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z); + + /* done */ + fixed_built = 1; + } + fixed_lock--; + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +local int inflate_trees_free(t, z) +inflate_huft *t; /* table to free */ +z_stream *z; /* for zfree function */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register inflate_huft *p, *q; + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != Z_NULL) + { + q = (--p)->next; + ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft)); + p = q; + } + return Z_OK; +} + +/*+++++*/ +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ + mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) +uInt bl, bd; +inflate_huft *tl, *td; +z_stream *z; +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +local int inflate_codes(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Bytef *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + if (e & 32) /* end of block */ + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = "invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ +#ifndef __TURBOC__ /* Turbo C bug for following expression */ + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; +#else + f = q - c->sub.copy.dist; + if ((uInt)(q - s->window) < c->sub.copy.dist) + f = s->end - (c->sub.copy.dist - (q - s->window)); +#endif + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +local void inflate_codes_free(c, z) +inflate_codes_statef *c; +z_stream *z; +{ + ZFREE(z, c, sizeof(struct inflate_codes_state)); + Tracev((stderr, "inflate: codes free\n")); +} + +/*+++++*/ +/* inflate_util.c -- data and routines common to blocks and codes + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt n; + Bytef *p, *q; + + /* local copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as far as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, n); + + /* copy as far as end of window */ + zmemcpy(p, q, n); + p += n; + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + zmemcpy(p, q, n); + p += n; + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} + + +/*+++++*/ +/* inffast.c -- process literals and length/distance pairs fast + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* macros for bit input with no checking and for returning unused bytes */ +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<>3);p-=c;k&=7;} + +/* Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. */ + +local int inflate_fast(bl, bd, tl, td, s, z) +uInt bl, bd; +inflate_huft *tl, *td; +inflate_blocks_statef *s; +z_stream *z; +{ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + uInt ml; /* mask for literal/length tree */ + uInt md; /* mask for distance tree */ + uInt c; /* bytes to copy */ + uInt d; /* distance back to copy from */ + Bytef *r; /* copy source pointer */ + + /* load input, output, bit values */ + LOAD + + /* initialize masks */ + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + /* do until not enough input or output space for fast loop */ + do { /* assume called with m >= 258 && n >= 10 */ + /* get literal/length code */ + GRABBITS(20) /* max bits for literal/length code */ + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits for length */ + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + /* decode distance base of block to copy */ + GRABBITS(15); /* max bits for distance code */ + e = (t = td + ((uInt)b & md))->exop; + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits to add to distance base */ + e &= 15; + GRABBITS(e) /* get extra bits (up to 13) */ + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + /* do the copy */ + m -= c; + if ((uInt)(q - s->window) >= d) /* offset before dest */ + { /* just copy */ + r = q - d; + *q++ = *r++; c--; /* minimum count is three, */ + *q++ = *r++; c--; /* so unroll loop a little */ + } + else /* else offset after destination */ + { + e = d - (q - s->window); /* bytes from offset to end */ + r = s->end - e; /* pointer to offset */ + if (c > e) /* if source crosses, */ + { + c -= e; /* copy to end of window */ + do { + *q++ = *r++; + } while (--e); + r = s->window; /* copy rest from start of window */ + } + } + do { /* copy all or what's left */ + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop; + else + { + z->msg = "invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + break; + } + if ((e & 64) == 0) + { + if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = "invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + } while (m >= 258 && n >= 10); + + /* not enough input or output--restore pointers and return */ + UNGRAB + UPDATE + return Z_OK; +} + + +/*+++++*/ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */ + +char *zlib_version = ZLIB_VERSION; + +char *z_errmsg[] = { +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +""}; + + +/*+++++*/ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */ + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf) {s1 += *buf++; s2 += s1;} +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); +#define DO16(buf) DO8(buf); DO8(buf); + +/* ========================================================================= */ +uLong adler32(adler, buf, len) + uLong adler; + Bytef *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + k -= 16; + } + if (k != 0) do { + DO1(buf); + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} diff -Nru a/arch/ppc64/boot/zlib.h b/arch/ppc64/boot/zlib.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/boot/zlib.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,432 @@ +/* */ + +/* + * This file is derived from zlib.h and zconf.h from the zlib-0.95 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. + */ + +/* + * ==FILEVERSION 960122== + * + * This marker is used by the Linux installation script to determine + * whether an up-to-date version of this file is already installed. + */ + +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 0.95, Aug 16th, 1995. + + Copyright (C) 1995 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + gzip@prep.ai.mit.edu madler@alumni.caltech.edu + */ + +#ifndef _ZLIB_H +#define _ZLIB_H + +/* #include "zconf.h" */ /* included directly here */ + +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */ + +/* + The library does not install any signal handler. It is recommended to + add at least a handler for SIGSEGV when decompressing; the library checks + the consistency of the input data whenever possible but may go nuts + for some forms of corrupted input. + */ + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints + * at addresses which are not a multiple of their size. + * Under DOS, -DFAR=far or -DFAR=__far may be needed. + */ + +#ifndef STDC +# if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus) +# define STDC +# endif +#endif + +#ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */ +# include +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +#ifndef FAR +# define FAR +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2 */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + 1 << (windowBits+2) + 1 << (memLevel+9) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +typedef Byte FAR Bytef; +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +/* end of original zconf.h */ + +#define ZLIB_VERSION "0.95P" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms may be added later and will have the same + stream interface. + + For compression the application must provide the output buffer and + may optionally provide the input buffer for optimization. For decompression, + the application must provide the input buffer and may optionally provide + the output buffer for optimization. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidp opaque; /* private data object passed to zalloc and zfree */ + + Byte data_type; /* best guess about the data type: ascii or binary */ + +} z_stream; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_FULL_FLUSH 2 +#define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */ +#define Z_FINISH 4 +#define Z_PACKET_FLUSH 5 +/* See deflate() below for the usage of these constants */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +/* error codes for the compression/decompression functions */ + +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Used to set the data_type field */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +extern char *zlib_version; +/* The application can compare zlib_version and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + */ + + /* basic functions */ + +extern int inflateInit OF((z_stream *strm)); +/* + Initializes the internal stream state for decompression. The fields + zalloc and zfree must be initialized before by the caller. If zalloc and + zfree are set to Z_NULL, inflateInit updates them to use default allocation + functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory. msg is set to null if there is no error message. + inflateInit does not perform any decompression: this will be done by + inflate(). +*/ + + +extern int inflate OF((z_stream *strm, int flush)); +/* + Performs one or both of the following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() always provides as much output as possible + (until there is no more input data or no more space in the output buffer). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). + + If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH, + inflate flushes as much output as possible to the output buffer. The + flushing behavior of inflate is not specified for values of the flush + parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the + current implementation actually flushes as much output as possible + anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data + has been consumed, it is expecting to see the length field of a stored + block; if not, it returns Z_DATA_ERROR. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + inflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if the end of the + compressed data has been reached and all uncompressed output has been + produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if + the stream structure was inconsistent (for example if next_in or next_out + was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no + progress is possible or if there was not enough room in the output buffer + when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then + call inflateSync to look for a good compression block. */ + + +extern int inflateEnd OF((z_stream *strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* advanced functions */ + +extern int inflateInit2 OF((z_stream *strm, + int windowBits)); +/* + This is another version of inflateInit with more compression options. The + fields next_out, zalloc and zfree must be initialized before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library (the value 16 will be allowed soon). The + default value is 15 if inflateInit is used instead. If a compressed stream + with a larger window size is given as input, inflate() will return with + the error code Z_DATA_ERROR instead of trying to allocate a larger window. + + If next_out is not null, the library will use this buffer for the history + buffer; the buffer must either be large enough to hold the entire output + data, or have at least 1< +#include +#include +#include +#include +#include +#include +#ifndef _HVCALLSC_H +#include +#endif +#include + +#ifndef _HVTYPES_H +#include +#endif + + +/*===================================================================== + * Note that this call takes at MOST one page worth of data + */ +int HvCall_readLogBuffer(HvLpIndex lpIndex, void *buffer, u64 bufLen) +{ + struct HvLpBufferList *bufList; + u64 bytesLeft = bufLen; + u64 leftThisPage; + u64 curPtr = virt_to_absolute( (unsigned long) buffer ); + u64 retVal; + int npages; + int i; + + npages = 0; + while (bytesLeft) { + npages++; + leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr; + + if (leftThisPage > bytesLeft) + bytesLeft = 0; + else + bytesLeft -= leftThisPage; + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + if (npages == 0) + return 0; + + bufList = (struct HvLpBufferList *) + kmalloc(npages * sizeof(struct HvLpBufferList), GFP_ATOMIC); + bytesLeft = bufLen; + curPtr = virt_to_absolute( (unsigned long) buffer ); + for(i=0; i bytesLeft) { + bufList[i].len = bytesLeft; + bytesLeft = 0; + } else { + bufList[i].len = leftThisPage; + bytesLeft -= leftThisPage; + } + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + + retVal = HvCall3(HvCallBaseReadLogBuffer, lpIndex, + virt_to_absolute((unsigned long)bufList), bufLen); + + kfree(bufList); + + return (int)retVal; +} + +/*===================================================================== + */ +void HvCall_writeLogBuffer(const void *buffer, u64 bufLen) +{ + struct HvLpBufferList bufList; + u64 bytesLeft = bufLen; + u64 leftThisPage; + u64 curPtr = virt_to_absolute( (unsigned long) buffer ); + + while (bytesLeft) { + bufList.addr = curPtr; + + leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr; + + if (leftThisPage > bytesLeft) { + bufList.len = bytesLeft; + bytesLeft = 0; + } else { + bufList.len = leftThisPage; + bytesLeft -= leftThisPage; + } + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + + HvCall2(HvCallBaseWriteLogBuffer, + virt_to_absolute((unsigned long)&bufList), bufLen); + +} diff -Nru a/arch/ppc64/kernel/HvLpConfig.c b/arch/ppc64/kernel/HvLpConfig.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/HvLpConfig.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,28 @@ +/* + * HvLpConfig.c + * Copyright (C) 2001 Kyle A. Lucke, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _HVLPCONFIG_H +#include +#endif + +HvLpIndex HvLpConfig_getLpIndex_outline(void) +{ + return HvLpConfig_getLpIndex(); +} + diff -Nru a/arch/ppc64/kernel/HvLpEvent.c b/arch/ppc64/kernel/HvLpEvent.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/HvLpEvent.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,77 @@ +/* + * Copyright 2001 Mike Corrigan IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include + +/* Array of LpEvent handler functions */ +LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; +unsigned lpEventHandlerPaths[HvLpEvent_Type_NumTypes]; + +/* Register a handler for an LpEvent type */ + +int HvLpEvent_registerHandler( HvLpEvent_Type eventType, LpEventHandler handler ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes ) { + lpEventHandler[eventType] = handler; + rc = 0; + } + return rc; + +} + +int HvLpEvent_unregisterHandler( HvLpEvent_Type eventType ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes ) { + if ( !lpEventHandlerPaths[eventType] ) { + lpEventHandler[eventType] = NULL; + rc = 0; + } + } + return rc; +} + +/* (lpIndex is the partition index of the target partition. + * needed only for VirtualIo, VirtualLan and SessionMgr. Zero + * indicates to use our partition index - for the other types) + */ +int HvLpEvent_openPath( HvLpEvent_Type eventType, HvLpIndex lpIndex ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes && + lpEventHandler[eventType] ) { + if ( lpIndex == 0 ) + lpIndex = itLpNaca.xLpIndex; + HvCallEvent_openLpEventPath( lpIndex, eventType ); + ++lpEventHandlerPaths[eventType]; + rc = 0; + } + return rc; +} + +int HvLpEvent_closePath( HvLpEvent_Type eventType, HvLpIndex lpIndex ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes && + lpEventHandler[eventType] && + lpEventHandlerPaths[eventType] ) { + if ( lpIndex == 0 ) + lpIndex = itLpNaca.xLpIndex; + HvCallEvent_closeLpEventPath( lpIndex, eventType ); + --lpEventHandlerPaths[eventType]; + rc = 0; + } + return rc; +} + diff -Nru a/arch/ppc64/kernel/ItLpQueue.c b/arch/ppc64/kernel/ItLpQueue.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/ItLpQueue.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,168 @@ +/* + * ItLpQueue.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static __inline__ int set_inUse( struct ItLpQueue * lpQueue ) +{ + int t; + u32 * inUseP = &(lpQueue->xInUseWord); + + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 \n\ + cmpi 0,%0,0 \n\ + li %0,0 \n\ + bne- 2f \n\ + addi %0,%0,1 \n\ + stwcx. %0,0,%2 \n\ + bne- 1b \n\ +2: eieio" + : "=&r" (t), "=m" (lpQueue->xInUseWord) + : "r" (inUseP), "m" (lpQueue->xInUseWord) + : "cc"); + + return t; +} + +static __inline__ void clear_inUse( struct ItLpQueue * lpQueue ) +{ + lpQueue->xInUseWord = 0; +} + +/* Array of LpEvent handler functions */ +extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; +unsigned long ItLpQueueInProcess = 0; + +struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * lpQueue ) +{ + struct HvLpEvent * nextLpEvent = + (struct HvLpEvent *)lpQueue->xSlicCurEventPtr; + if ( nextLpEvent->xFlags.xValid ) { + /* Set pointer to next potential event */ + lpQueue->xSlicCurEventPtr += ((nextLpEvent->xSizeMinus1 + + LpEventAlign ) / + LpEventAlign ) * + LpEventAlign; + /* Wrap to beginning if no room at end */ + if (lpQueue->xSlicCurEventPtr > lpQueue->xSlicLastValidEventPtr) + lpQueue->xSlicCurEventPtr = lpQueue->xSlicEventStackPtr; + } + else + nextLpEvent = NULL; + + return nextLpEvent; +} + +int ItLpQueue_isLpIntPending( struct ItLpQueue * lpQueue ) +{ + int retval = 0; + struct HvLpEvent * nextLpEvent; + if ( lpQueue ) { + nextLpEvent = (struct HvLpEvent *)lpQueue->xSlicCurEventPtr; + retval = nextLpEvent->xFlags.xValid | lpQueue->xPlicOverflowIntPending; + } + return retval; +} + +void ItLpQueue_clearValid( struct HvLpEvent * event ) +{ + /* Clear the valid bit of the event + * Also clear bits within this event that might + * look like valid bits (on 64-byte boundaries) + */ + unsigned extra = (( event->xSizeMinus1 + LpEventAlign ) / + LpEventAlign ) - 1; + switch ( extra ) { + case 3: + ((struct HvLpEvent*)((char*)event+3*LpEventAlign))->xFlags.xValid=0; + case 2: + ((struct HvLpEvent*)((char*)event+2*LpEventAlign))->xFlags.xValid=0; + case 1: + ((struct HvLpEvent*)((char*)event+1*LpEventAlign))->xFlags.xValid=0; + case 0: + ; + } + mb(); + event->xFlags.xValid = 0; +} + +unsigned ItLpQueue_process( struct ItLpQueue * lpQueue, struct pt_regs *regs ) +{ + unsigned numIntsProcessed = 0; + struct HvLpEvent * nextLpEvent; + + /* If we have recursed, just return */ + if ( !set_inUse( lpQueue ) ) + return 0; + + if (ItLpQueueInProcess == 0) + ItLpQueueInProcess = 1; + else + BUG(); + + for (;;) { + nextLpEvent = ItLpQueue_getNextLpEvent( lpQueue ); + if ( nextLpEvent ) { + /* Count events to return to caller + * and count processed events in lpQueue + */ + ++numIntsProcessed; + lpQueue->xLpIntCount++; + /* Call appropriate handler here, passing + * a pointer to the LpEvent. The handler + * must make a copy of the LpEvent if it + * needs it in a bottom half. (perhaps for + * an ACK) + * + * Handlers are responsible for ACK processing + * + * The Hypervisor guarantees that LpEvents will + * only be delivered with types that we have + * registered for, so no type check is necessary + * here! + */ + if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes ) + lpQueue->xLpIntCountByType[nextLpEvent->xType]++; + if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes && + lpEventHandler[nextLpEvent->xType] ) + lpEventHandler[nextLpEvent->xType](nextLpEvent, regs); + else + printk(KERN_INFO "Unexpected Lp Event type=%d\n", nextLpEvent->xType ); + + ItLpQueue_clearValid( nextLpEvent ); + } + else /* No more valid events + * If overflow events are pending + * process them + */ + if ( lpQueue->xPlicOverflowIntPending ) { + HvCallEvent_getOverflowLpEvents( + lpQueue->xIndex); + } + else /* If nothing left then we are done */ + break; + } + + ItLpQueueInProcess = 0; + mb(); + clear_inUse( lpQueue ); + + get_paca()->lpEvent_count += numIntsProcessed; + + return numIntsProcessed; +} diff -Nru a/arch/ppc64/kernel/LparData.c b/arch/ppc64/kernel/LparData.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/LparData.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,252 @@ +/* + * Copyright 2001 Mike Corrigan, IBM Corp + * + * 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. + */ +#define __KERNEL__ 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char _start_boltedStacks[]; + +/* The LparMap data is now located at offset 0x6000 in head.S + * It was put there so that the HvReleaseData could address it + * with a 32-bit offset as required by the iSeries hypervisor + * + * The Naca has a pointer to the ItVpdAreas. The hypervisor finds + * the Naca via the HvReleaseData area. The HvReleaseData has the + * offset into the Naca of the pointer to the ItVpdAreas. + */ + +extern struct ItVpdAreas itVpdAreas; + +/* The LpQueue is used to pass event data from the hypervisor to + * the partition. This is where I/O interrupt events are communicated. + * The ItLpQueue must be initialized (even though only to all zeros) + * If it were uninitialized (in .bss) it would get zeroed after the + * kernel gets control. The hypervisor will have filled in some fields + * before the kernel gets control. By initializing it we keep it out + * of the .bss + */ + +struct ItLpQueue xItLpQueue = {}; + + +/* The HvReleaseData is the root of the information shared between + * the hypervisor and Linux. + */ + +struct HvReleaseData hvReleaseData = { + 0xc8a5d9c4, /* desc = "HvRD" ebcdic */ + sizeof(struct HvReleaseData), + offsetof(struct Naca, xItVpdAreas), + (struct Naca *)(KERNELBASE+0x4000), /* 64-bit Naca address */ + 0x6000, /* offset of LparMap within loadarea (see head.S) */ + 0, + 1, /* tags inactive */ + 0, /* 64 bit */ + 0, /* shared processors */ + 0, /* HMT allowed */ + 6, /* TEMP: This allows non-GA driver */ + 4, /* We are v5r2m0 */ + 3, /* Min supported PLIC = v5r1m0 */ + 3, /* Min usuable PLIC = v5r1m0 */ + { 0xd3, 0x89, 0x95, 0xa4, /* "Linux 2.4 "*/ + 0xa7, 0x40, 0xf2, 0x4b, + 0xf4, 0x4b, 0xf6, 0xf4 }, + {0} +}; + +extern void SystemReset_Iseries(void); +extern void MachineCheck_Iseries(void); +extern void DataAccess_Iseries(void); +extern void InstructionAccess_Iseries(void); +extern void HardwareInterrupt_Iseries(void); +extern void Alignment_Iseries(void); +extern void ProgramCheck_Iseries(void); +extern void FPUnavailable_Iseries(void); +extern void Decrementer_Iseries(void); +extern void Trap_0a_Iseries(void); +extern void Trap_0b_Iseries(void); +extern void SystemCall_Iseries(void); +extern void SingleStep_Iseries(void); +extern void Trap_0e_Iseries(void); +extern void PerformanceMonitor_Iseries(void); +extern void DataAccessSLB_Iseries(void); +extern void InstructionAccessSLB_Iseries(void); + +struct ItLpNaca itLpNaca = { + 0xd397d581, /* desc = "LpNa" ebcdic */ + 0x0400, /* size of ItLpNaca */ + 0x0300, 19, /* offset to int array, # ents */ + 0, 0, 0, /* Part # of primary, serv, me */ + 0, 0x100, /* # of LP queues, offset */ + 0, 0, 0, /* Piranha stuff */ + { 0,0,0,0,0 }, /* reserved */ + 0,0,0,0,0,0,0, /* stuff */ + { 0,0,0,0,0 }, /* reserved */ + 0, /* reserved */ + 0, /* VRM index of PLIC */ + 0, 0, /* min supported, compat SLIC */ + 0, /* 64-bit addr of load area */ + 0, /* chunks for load area */ + 0, 0, /* PASE mask, seg table */ + { 0 }, /* 64 reserved bytes */ + { 0 }, /* 128 reserved bytes */ + { 0 }, /* Old LP Queue */ + { 0 }, /* 384 reserved bytes */ + { + (u64)SystemReset_Iseries, /* 0x100 System Reset */ + (u64)MachineCheck_Iseries, /* 0x200 Machine Check */ + (u64)DataAccess_Iseries, /* 0x300 Data Access */ + (u64)InstructionAccess_Iseries, /* 0x400 Instruction Access */ + (u64)HardwareInterrupt_Iseries, /* 0x500 External */ + (u64)Alignment_Iseries, /* 0x600 Alignment */ + (u64)ProgramCheck_Iseries, /* 0x700 Program Check */ + (u64)FPUnavailable_Iseries, /* 0x800 FP Unavailable */ + (u64)Decrementer_Iseries, /* 0x900 Decrementer */ + (u64)Trap_0a_Iseries, /* 0xa00 Trap 0A */ + (u64)Trap_0b_Iseries, /* 0xb00 Trap 0B */ + (u64)SystemCall_Iseries, /* 0xc00 System Call */ + (u64)SingleStep_Iseries, /* 0xd00 Single Step */ + (u64)Trap_0e_Iseries, /* 0xe00 Trap 0E */ + (u64)PerformanceMonitor_Iseries,/* 0xf00 Performance Monitor */ + 0, /* int 0x1000 */ + 0, /* int 0x1010 */ + 0, /* int 0x1020 CPU ctls */ + (u64)HardwareInterrupt_Iseries, /* SC Ret Hdlr */ + (u64)DataAccessSLB_Iseries, /* 0x380 D-SLB */ + (u64)InstructionAccessSLB_Iseries /* 0x480 I-SLB */ + } +}; + +struct ItIplParmsReal xItIplParmsReal = {}; + +struct IoHriProcessorVpd xIoHriProcessorVpd[maxProcessors] = { + { + xInstCacheOperandSize: 32, + xDataCacheOperandSize: 32, + xProcFreq: 50000000, + xTimeBaseFreq: 50000000, + xPVR: 0x3600 + } +}; + + +u64 xMsVpd[3400] = {}; /* Space for Main Store Vpd 27,200 bytes */ + +u64 xRecoveryLogBuffer[32] = {}; /* Space for Recovery Log Buffer */ + +struct SpCommArea xSpCommArea = { + 0xE2D7C3C2, + 1, + {0}, + 0, 0, 0, 0, {0} +}; + +struct ItVpdAreas itVpdAreas = { + 0xc9a3e5c1, /* "ItVA" */ + sizeof( struct ItVpdAreas ), + 0, 0, + 26, /* # VPD array entries */ + 10, /* # DMA array entries */ + maxProcessors*2, maxProcessors, /* Max logical, physical procs */ + offsetof(struct ItVpdAreas,xPlicDmaToks),/* offset to DMA toks */ + offsetof(struct ItVpdAreas,xSlicVpdAdrs),/* offset to VPD addrs */ + offsetof(struct ItVpdAreas,xPlicDmaLens),/* offset to DMA lens */ + offsetof(struct ItVpdAreas,xSlicVpdLens),/* offset to VPD lens */ + 0, /* max slot labels */ + 1, /* max LP queues */ + {0}, {0}, /* reserved */ + {0}, /* DMA lengths */ + {0}, /* DMA tokens */ + { /* VPD lengths */ + 0,0,0,0, /* 0 - 3 */ + sizeof(struct Paca), /* 4 length of Paca */ + 0, /* 5 */ + sizeof(struct ItIplParmsReal),/* 6 length of IPL parms */ + 26992, /* 7 length of MS VPD */ + 0, /* 8 */ + sizeof(struct ItLpNaca),/* 9 length of LP Naca */ + 0, /* 10 */ + 256, /* 11 length of Recovery Log Buf */ + sizeof(struct SpCommArea), /* 12 length of SP Comm Area */ + 0,0,0, /* 13 - 15 */ + sizeof(struct IoHriProcessorVpd),/* 16 length of Proc Vpd */ + 0,0,0,0,0,0, /* 17 - 22 */ + sizeof(struct ItLpQueue),/* 23 length of Lp Queue */ + 0,0 /* 24 - 25 */ + }, + { /* VPD addresses */ + 0,0,0,0, /* 0 - 3 */ + &xPaca[0], /* 4 first Paca */ + 0, /* 5 */ + &xItIplParmsReal, /* 6 IPL parms */ + &xMsVpd, /* 7 MS Vpd */ + 0, /* 8 */ + &itLpNaca, /* 9 LpNaca */ + 0, /* 10 */ + &xRecoveryLogBuffer, /* 11 Recovery Log Buffer */ + &xSpCommArea, /* 12 SP Comm Area */ + 0,0,0, /* 13 - 15 */ + &xIoHriProcessorVpd, /* 16 Proc Vpd */ + 0,0,0,0,0,0, /* 17 - 22 */ + &xItLpQueue, /* 23 Lp Queue */ + 0,0 + } +}; + + +/* Data area used in flush_hash_page */ +long long flush_hash_page_hpte[2]; + +struct msChunks msChunks = {0, 0, 0, 0, NULL}; + +/* Depending on whether this is called from iSeries or pSeries setup + * code, the location of the msChunks struct may or may not have + * to be reloc'd, so we force the caller to do that for us by passing + * in a pointer to the structure. + */ +unsigned long +msChunks_alloc(unsigned long mem, unsigned long num_chunks, unsigned long chunk_size) +{ + unsigned long offset = reloc_offset(); + struct msChunks *_msChunks = PTRRELOC(&msChunks); + + _msChunks->num_chunks = num_chunks; + _msChunks->chunk_size = chunk_size; + _msChunks->chunk_shift = __ilog2(chunk_size); + _msChunks->chunk_mask = (1UL<<_msChunks->chunk_shift)-1; + + mem = _ALIGN(mem, sizeof(msChunks_entry)); + _msChunks->abs = (msChunks_entry *)(mem + offset); + mem += num_chunks * sizeof(msChunks_entry); + + return mem; +} + + + + diff -Nru a/arch/ppc64/kernel/Makefile b/arch/ppc64/kernel/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,73 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +USE_STANDARD_AS_RULE := true + +EXTRA_CFLAGS = -mno-minimal-toc + +KHEAD := head.o + +all: $(KHEAD) kernel.o + +O_TARGET := kernel.o + +export-objs := ppc_ksyms.o setup.o + +obj-y := ppc_ksyms.o setup.o entry.o traps.o irq.o idle.o \ + time.o process.o signal.o syscalls.o misc.o ptrace.o \ + align.o semaphore.o bitops.o stab.o htab.o pacaData.o \ + LparData.o udbg.o binfmt_elf32.o sys_ppc32.o sys32.o \ + ioctl32.o ptrace32.o signal32.o open_pic.o xics.o \ + pmc.o mf_proc.o proc_pmc.o iSeries_setup.o \ + ItLpQueue.o hvCall.o mf.o HvLpEvent.o ras.o \ + iSeries_proc.o HvCall.o HvLpConfig.o \ + rtc.o init_task.o + +obj-$(CONFIG_PCI) += pci.o pci_dn.o pci_dma.o +obj-$(CONFIG_PPC_EEH) += eeh.o + +ifeq ($(CONFIG_PPC_ISERIES),y) +obj-$(CONFIG_PCI) += iSeries_pci.o iSeries_pci_reset.o iSeries_IoMmTable.o iSeries_irq.o iSeries_VpdInfo.o XmPciLpEvent.o +endif +ifeq ($(CONFIG_PPC_PSERIES),y) +obj-$(CONFIG_PCI) += pSeries_pci.o pSeries_lpar.o pSeries_hvCall.o + +obj-y += rtasd.o +endif + +obj-$(CONFIG_KGDB) += ppc-stub.o + +obj-$(CONFIG_SMP) += smp.o + +# tibit: for matrox_init2() +ifeq ($(CONFIG_NVRAM),y) + obj-$(CONFIG_NVRAM) += pmac_nvram.o +endif + +obj-y += prom.o lmb.o rtas.o rtas-proc.o chrp_setup.o i8259.o + +include $(TOPDIR)/Rules.make + +# +# This is just to get the dependencies... +# + +head.o: head.S ppc_defs.h + +ppc_defs.h: mk_defs.c ppc_defs.head \ + $(TOPDIR)/include/asm/mmu.h \ + $(TOPDIR)/include/asm/processor.h \ + $(TOPDIR)/include/asm/pgtable.h \ + $(TOPDIR)/include/asm/ptrace.h + $(CC) $(CFLAGS) -S mk_defs.c + cp ppc_defs.head ppc_defs.h +# for bk, this way we can write to the file even if it's not checked out + chmod u+w ppc_defs.h + grep '^#define' mk_defs.s >> ppc_defs.h + rm mk_defs.s diff -Nru a/arch/ppc64/kernel/XmPciLpEvent.c b/arch/ppc64/kernel/XmPciLpEvent.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/XmPciLpEvent.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,157 @@ +/* + * File XmPciLpEvent.h created by Wayne Holm on Mon Jan 15 2001. + * + * This module handles PCI interrupt events sent by the iSeries Hypervisor. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +long Pci_Interrupt_Count = 0; +long Pci_Event_Count = 0; + +enum XmPciLpEvent_Subtype { + XmPciLpEvent_BusCreated = 0, // PHB has been created + XmPciLpEvent_BusFailed = 1, // PHB has failed + XmPciLpEvent_BusRecovered = 12, // PHB has been recovered + XmPciLpEvent_NodeFailed = 4, // Multi-adapter bridge has failed + XmPciLpEvent_NodeRecovered = 5, // Multi-adapter bridge has recovered + XmPciLpEvent_SlotInterrupt = 22 // Slot interrupt +}; + +struct XmPciLpEvent_BusInterrupt { + HvBusNumber busNumber; + HvSubBusNumber subBusNumber; +}; + +struct XmPciLpEvent_NodeInterrupt { + HvBusNumber busNumber; + HvSubBusNumber subBusNumber; + HvAgentId deviceId; +}; + +struct XmPciLpEvent { + struct HvLpEvent hvLpEvent; + + union { + u64 alignData; // Align on an 8-byte boundary + + struct { + u32 fisr; + HvBusNumber busNumber; + HvSubBusNumber subBusNumber; + HvAgentId deviceId; + } slotInterrupt; + + struct XmPciLpEvent_BusInterrupt busFailed; + struct XmPciLpEvent_BusInterrupt busRecovered; + struct XmPciLpEvent_BusInterrupt busCreated; + + struct XmPciLpEvent_NodeInterrupt nodeFailed; + struct XmPciLpEvent_NodeInterrupt nodeRecovered; + + } eventData; + +}; + +static void intReceived(struct XmPciLpEvent* eventParm, struct pt_regs* regsParm); + +static void XmPciLpEvent_handler( struct HvLpEvent* eventParm, struct pt_regs* regsParm) +{ + //PPCDBG(PPCDBG_BUSWALK,"XmPciLpEvent_handler, type 0x%x\n",eventParm->xType ); + ++Pci_Event_Count; + + if (eventParm && eventParm->xType == HvLpEvent_Type_PciIo) { + switch( eventParm->xFlags.xFunction ) { + case HvLpEvent_Function_Int: + intReceived( (struct XmPciLpEvent*)eventParm, regsParm ); + break; + case HvLpEvent_Function_Ack: + printk(KERN_ERR "XmPciLpEvent.c: unexpected ack received\n"); + break; + default: + printk(KERN_ERR "XmPciLpEvent.c: unexpected event function %d\n",(int)eventParm->xFlags.xFunction); + break; + } + } + else if (event) { + printk(KERN_ERR "XmPciLpEvent.c: Unrecognized PCI event type 0x%x\n",(int)eventParm->xType); + } + else { + printk(KERN_ERR "XmPciLpEvent.c: NULL event received\n"); + } +} + +static void intReceived(struct XmPciLpEvent* eventParm, struct pt_regs* regsParm) +{ + int irq; + + ++Pci_Interrupt_Count; + //PPCDBG(PPCDBG_BUSWALK,"PCI: XmPciLpEvent.c: intReceived\n"); + + switch (eventParm->hvLpEvent.xSubtype) { + case XmPciLpEvent_SlotInterrupt: + irq = eventParm->hvLpEvent.xCorrelationToken; + /* Dispatch the interrupt handlers for this irq */ + ppc_irq_dispatch_handler(regsParm, irq); + HvCallPci_eoi(eventParm->eventData.slotInterrupt.busNumber, + eventParm->eventData.slotInterrupt.subBusNumber, + eventParm->eventData.slotInterrupt.deviceId); + break; + /* Ignore error recovery events for now */ + case XmPciLpEvent_BusCreated: + printk(KERN_INFO "XmPciLpEvent.c: system bus %d created\n", eventParm->eventData.busCreated.busNumber); + break; + case XmPciLpEvent_BusFailed: + printk(KERN_INFO "XmPciLpEvent.c: system bus %d failed\n", eventParm->eventData.busFailed.busNumber); + break; + case XmPciLpEvent_BusRecovered: + printk(KERN_INFO "XmPciLpEvent.c: system bus %d recovered\n", eventParm->eventData.busRecovered.busNumber); + break; + case XmPciLpEvent_NodeFailed: + printk(KERN_INFO "XmPciLpEvent.c: multi-adapter bridge %d/%d/%d failed\n", eventParm->eventData.nodeFailed.busNumber, eventParm->eventData.nodeFailed.subBusNumber, eventParm->eventData.nodeFailed.deviceId); + break; + case XmPciLpEvent_NodeRecovered: + printk(KERN_INFO "XmPciLpEvent.c: multi-adapter bridge %d/%d/%d recovered\n", eventParm->eventData.nodeRecovered.busNumber, eventParm->eventData.nodeRecovered.subBusNumber, eventParm->eventData.nodeRecovered.deviceId); + break; + default: + printk(KERN_ERR "XmPciLpEvent.c: unrecognized event subtype 0x%x\n", + eventParm->hvLpEvent.xSubtype); + break; + }; +} + + +/* This should be called sometime prior to buswalk (init_IRQ would be good) */ +int XmPciLpEvent_init() +{ + int xRc; + PPCDBG(PPCDBG_BUSWALK,"XmPciLpEvent_init, Register Event type 0x%04X\n",HvLpEvent_Type_PciIo); + + xRc = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo, &XmPciLpEvent_handler); + if (xRc == 0) { + xRc = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0); + if (xRc != 0) { + printk(KERN_ERR "XmPciLpEvent.c: open event path failed with rc 0x%x\n", xRc); + } + } + else { + printk(KERN_ERR "XmPciLpEvent.c: register handler failed with rc 0x%x\n", xRc); + } + return xRc; +} + diff -Nru a/arch/ppc64/kernel/align.c b/arch/ppc64/kernel/align.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/align.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,362 @@ +/* + * align.c - handle alignment exceptions for the Power PC. + * + * Copyright (c) 1996 Paul Mackerras + * Copyright (c) 1998-1999 TiVo, Inc. + * PowerPC 403GCX modifications. + * Copyright (c) 1999 Grant Erickson + * PowerPC 403GCX/405GP modifications. + * Copyright (c) 2001 PPC64 team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct aligninfo { + unsigned char len; + unsigned char flags; +}; + +#define OPCD(inst) (((inst) & 0xFC000000) >> 26) +#define RS(inst) (((inst) & 0x03E00000) >> 21) +#define RA(inst) (((inst) & 0x001F0000) >> 16) +#define IS_DFORM(code) ((code) >= 32 && (code) <= 47) + +#define INVALID { 0, 0 } + +#define LD 1 /* load */ +#define ST 2 /* store */ +#define SE 4 /* sign-extend value */ +#define F 8 /* to/from fp regs */ +#define U 0x10 /* update index register */ +#define M 0x20 /* multiple load/store */ +#define S 0x40 /* single-precision fp, or byte-swap value */ +#define HARD 0x80 /* string, stwcx. */ +#define D 0x100 /* double-word load/store */ + +#define DCBZ 0x5f /* 8xx/82xx dcbz faults when cache not enabled */ + +/* + * The PowerPC stores certain bits of the instruction that caused the + * alignment exception in the DSISR register. This array maps those + * bits to information about the operand length and what the + * instruction would do. + */ +static struct aligninfo aligninfo[128] = { + { 4, LD }, /* 00 0 0000: lwz / lwarx */ + INVALID, /* 00 0 0001 */ + { 4, ST }, /* 00 0 0010: stw */ + INVALID, /* 00 0 0011 */ + { 2, LD }, /* 00 0 0100: lhz */ + { 2, LD+SE }, /* 00 0 0101: lha */ + { 2, ST }, /* 00 0 0110: sth */ + { 4, LD+M }, /* 00 0 0111: lmw */ + { 4, LD+F+S }, /* 00 0 1000: lfs */ + { 8, LD+F }, /* 00 0 1001: lfd */ + { 4, ST+F+S }, /* 00 0 1010: stfs */ + { 8, ST+F }, /* 00 0 1011: stfd */ + INVALID, /* 00 0 1100 */ + { 8, LD }, /* 00 0 1101: ld */ + INVALID, /* 00 0 1110 */ + { 8, ST }, /* 00 0 1111: std */ + { 4, LD+U }, /* 00 1 0000: lwzu */ + INVALID, /* 00 1 0001 */ + { 4, ST+U }, /* 00 1 0010: stwu */ + INVALID, /* 00 1 0011 */ + { 2, LD+U }, /* 00 1 0100: lhzu */ + { 2, LD+SE+U }, /* 00 1 0101: lhau */ + { 2, ST+U }, /* 00 1 0110: sthu */ + { 4, ST+M }, /* 00 1 0111: stmw */ + { 4, LD+F+S+U }, /* 00 1 1000: lfsu */ + { 8, LD+F+U }, /* 00 1 1001: lfdu */ + { 4, ST+F+S+U }, /* 00 1 1010: stfsu */ + { 8, ST+F+U }, /* 00 1 1011: stfdu */ + INVALID, /* 00 1 1100 */ + { 8, ST }, /* 00 1 1101: std */ + INVALID, /* 00 1 1110 */ + INVALID, /* 00 1 1111 */ + { 8, LD }, /* 01 0 0000: ldx */ + INVALID, /* 01 0 0001 */ + { 8, ST }, /* 01 0 0010: stdx */ + INVALID, /* 01 0 0011 */ + INVALID, /* 01 0 0100 */ + INVALID, /* 01 0 0101: lwax?? */ + INVALID, /* 01 0 0110 */ + INVALID, /* 01 0 0111 */ + { 0, LD+HARD }, /* 01 0 1000: lswx */ + { 0, LD+HARD }, /* 01 0 1001: lswi */ + { 0, ST+HARD }, /* 01 0 1010: stswx */ + { 0, ST+HARD }, /* 01 0 1011: stswi */ + INVALID, /* 01 0 1100 */ + { 8, LD+U }, /* 01 0 1101: ldu */ + INVALID, /* 01 0 1110 */ + { 8, ST+U }, /* 01 0 1111: stdu */ + { 8, LD+U }, /* 01 1 0000: ldux */ + INVALID, /* 01 1 0001 */ + { 8, ST+U }, /* 01 1 0010: stdux */ + INVALID, /* 01 1 0011 */ + INVALID, /* 01 1 0100 */ + INVALID, /* 01 1 0101: lwaux?? */ + INVALID, /* 01 1 0110 */ + INVALID, /* 01 1 0111 */ + INVALID, /* 01 1 1000 */ + INVALID, /* 01 1 1001 */ + INVALID, /* 01 1 1010 */ + INVALID, /* 01 1 1011 */ + INVALID, /* 01 1 1100 */ + INVALID, /* 01 1 1101 */ + INVALID, /* 01 1 1110 */ + INVALID, /* 01 1 1111 */ + INVALID, /* 10 0 0000 */ + INVALID, /* 10 0 0001 */ + { 0, ST+HARD }, /* 10 0 0010: stwcx. */ + INVALID, /* 10 0 0011 */ + INVALID, /* 10 0 0100 */ + INVALID, /* 10 0 0101 */ + INVALID, /* 10 0 0110 */ + INVALID, /* 10 0 0111 */ + { 4, LD+S }, /* 10 0 1000: lwbrx */ + INVALID, /* 10 0 1001 */ + { 4, ST+S }, /* 10 0 1010: stwbrx */ + INVALID, /* 10 0 1011 */ + { 2, LD+S }, /* 10 0 1100: lhbrx */ + INVALID, /* 10 0 1101 */ + { 2, ST+S }, /* 10 0 1110: sthbrx */ + INVALID, /* 10 0 1111 */ + INVALID, /* 10 1 0000 */ + INVALID, /* 10 1 0001 */ + INVALID, /* 10 1 0010 */ + INVALID, /* 10 1 0011 */ + INVALID, /* 10 1 0100 */ + INVALID, /* 10 1 0101 */ + INVALID, /* 10 1 0110 */ + INVALID, /* 10 1 0111 */ + INVALID, /* 10 1 1000 */ + INVALID, /* 10 1 1001 */ + INVALID, /* 10 1 1010 */ + INVALID, /* 10 1 1011 */ + INVALID, /* 10 1 1100 */ + INVALID, /* 10 1 1101 */ + INVALID, /* 10 1 1110 */ + { 0, ST+HARD }, /* 10 1 1111: dcbz */ + { 4, LD }, /* 11 0 0000: lwzx */ + INVALID, /* 11 0 0001 */ + { 4, ST }, /* 11 0 0010: stwx */ + INVALID, /* 11 0 0011 */ + { 2, LD }, /* 11 0 0100: lhzx */ + { 2, LD+SE }, /* 11 0 0101: lhax */ + { 2, ST }, /* 11 0 0110: sthx */ + INVALID, /* 11 0 0111 */ + { 4, LD+F+S }, /* 11 0 1000: lfsx */ + { 8, LD+F }, /* 11 0 1001: lfdx */ + { 4, ST+F+S }, /* 11 0 1010: stfsx */ + { 8, ST+F }, /* 11 0 1011: stfdx */ + INVALID, /* 11 0 1100 */ + INVALID, /* 11 0 1101 */ + INVALID, /* 11 0 1110 */ + INVALID, /* 11 0 1111 */ + { 4, LD+U }, /* 11 1 0000: lwzux */ + INVALID, /* 11 1 0001 */ + { 4, ST+U }, /* 11 1 0010: stwux */ + INVALID, /* 11 1 0011 */ + { 2, LD+U }, /* 11 1 0100: lhzux */ + { 2, LD+SE+U }, /* 11 1 0101: lhaux */ + { 2, ST+U }, /* 11 1 0110: sthux */ + INVALID, /* 11 1 0111 */ + { 4, LD+F+S+U }, /* 11 1 1000: lfsux */ + { 8, LD+F+U }, /* 11 1 1001: lfdux */ + { 4, ST+F+S+U }, /* 11 1 1010: stfsux */ + { 8, ST+F+U }, /* 11 1 1011: stfdux */ + INVALID, /* 11 1 1100 */ + INVALID, /* 11 1 1101 */ + INVALID, /* 11 1 1110 */ + INVALID, /* 11 1 1111 */ +}; + +#define SWAP(a, b) (t = (a), (a) = (b), (b) = t) + +int +fix_alignment(struct pt_regs *regs) +{ + int instr, nb, flags; + int opcode, f1, f2, f3; + int i, t; + int reg, areg; + unsigned char *addr; + union { + int l; + long ll; + float f; + double d; + unsigned char v[8]; + } data; + + if (__is_processor(PV_POWER4)) { + /* + * The POWER4 has a DSISR register but doesn't set it on + * an alignment fault. -- paulus + */ + + instr = *((unsigned int *)regs->nip); + opcode = OPCD(instr); + reg = RS(instr); + areg = RA(instr); + + if (IS_DFORM(opcode)) { + f1 = 0; + f2 = (instr & 0x04000000) >> 26; + f3 = (instr & 0x78000000) >> 27; + } else { + f1 = (instr & 0x00000006) >> 1; + f2 = (instr & 0x00000040) >> 6; + f3 = (instr & 0x00000780) >> 7; + } + + instr = ((f1 << 5) | (f2 << 4) | f3); + } else { + reg = (regs->dsisr >> 5) & 0x1f; /* source/dest register */ + areg = regs->dsisr & 0x1f; /* register to update */ + instr = (regs->dsisr >> 10) & 0x7f; + instr |= (regs->dsisr >> 13) & 0x60; + } + + nb = aligninfo[instr].len; + if (nb == 0) { + long *p; + int i; + + if (instr != DCBZ) + return 0; /* too hard or invalid instruction */ + /* + * The dcbz (data cache block zero) instruction + * gives an alignment fault if used on non-cacheable + * memory. We handle the fault mainly for the + * case when we are running with the cache disabled + * for debugging. + */ + p = (long *) (regs->dar & -L1_CACHE_BYTES); + for (i = 0; i < L1_CACHE_BYTES / sizeof(long); ++i) + p[i] = 0; + return 1; + } + + flags = aligninfo[instr].flags; + addr = (unsigned char *)regs->dar; + + /* Verify the address of the operand */ + if (user_mode(regs)) { + if (verify_area((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb)) + return -EFAULT; /* bad address */ + } + + if ((flags & F) && (regs->msr & MSR_FP)) + giveup_fpu(current); + if (flags & M) + return 0; /* too hard for now */ + + /* If we read the operand, copy it in */ + if (flags & LD) { + if (nb == 2) { + data.v[0] = data.v[1] = 0; + if (__get_user(data.v[2], addr) + || __get_user(data.v[3], addr+1)) + return -EFAULT; + } else { + for (i = 0; i < nb; ++i) + if (__get_user(data.v[i], addr+i)) + return -EFAULT; + } + } + /* Unfortunately D (== 0x100) doesn't fit in the aligninfo[n].flags + field. So synthesize it here. */ + if ((flags & F) == 0 && nb == 8) + flags |= D; + + switch (flags & ~U) { + case LD+SE: + if (data.v[2] >= 0x80) + data.v[0] = data.v[1] = -1; + /* fall through */ + case LD: + regs->gpr[reg] = data.l; + break; + case LD+D: + regs->gpr[reg] = data.ll; + break; + case LD+S: + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + regs->gpr[reg] = data.l; + break; + case ST: + data.l = regs->gpr[reg]; + break; + case ST+D: + data.ll = regs->gpr[reg]; + break; + case ST+S: + data.l = regs->gpr[reg]; + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + break; + case LD+F: + current->thread.fpr[reg] = data.d; + break; + case ST+F: + data.d = current->thread.fpr[reg]; + break; + /* these require some floating point conversions... */ + /* we'd like to use the assignment, but we have to compile + * the kernel with -msoft-float so it doesn't use the + * fp regs for copying 8-byte objects. */ + case LD+F+S: + enable_kernel_fp(); + cvt_fd(&data.f, ¤t->thread.fpr[reg], ¤t->thread.fpscr); + /* current->thread.fpr[reg] = data.f; */ + break; + case ST+F+S: + enable_kernel_fp(); + cvt_df(¤t->thread.fpr[reg], &data.f, ¤t->thread.fpscr); + /* data.f = current->thread.fpr[reg]; */ + break; + default: + printk("align: can't handle flags=%x\n", flags); + return 0; + } + + if (flags & ST) { + if (nb == 2) { + if (__put_user(data.v[2], addr) + || __put_user(data.v[3], addr+1)) + return -EFAULT; + } else { + for (i = 0; i < nb; ++i) + if (__put_user(data.v[i], addr+i)) + return -EFAULT; + } + } + + if (flags & U) { + regs->gpr[areg] = regs->dar; + } + + return 1; +} diff -Nru a/arch/ppc64/kernel/binfmt_elf32.c b/arch/ppc64/kernel/binfmt_elf32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/binfmt_elf32.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,79 @@ +/* + * binfmt_elf32.c: Support 32-bit PPC ELF binaries on Power3 and followons. + * based on the SPARC64 version. + * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) + * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) + * + * Copyright (C) 2000,2001 Ken Aaker (kdaaker@rchland.vnet.ibm.com), IBM Corp + * Copyright (C) 2001 Anton Blanchard (anton@au.ibm.com), IBM + * + * 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. + */ + +#define ELF_ARCH EM_PPC +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB; + +#include +#include +#include +#include + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +#define elf_prstatus elf_prstatus32 +struct elf_prstatus32 +{ + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned int pr_sigpend; /* Set of pending signals */ + unsigned int pr_sighold; /* Set of held signals */ + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval32 pr_utime; /* User time */ + struct timeval32 pr_stime; /* System time */ + struct timeval32 pr_cutime; /* Cumulative user time */ + struct timeval32 pr_cstime; /* Cumulative system time */ + elf_gregset_t pr_reg; /* General purpose registers. */ + int pr_fpvalid; /* True if math co-processor being used. */ +}; + +#define elf_prpsinfo elf_prpsinfo32 +struct elf_prpsinfo32 +{ + char pr_state; /* numeric process state */ + char pr_sname; /* char for pr_state */ + char pr_zomb; /* zombie */ + char pr_nice; /* nice val */ + unsigned int pr_flag; /* flags */ + u32 pr_uid; + u32 pr_gid; + pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; + /* Lots missing */ + char pr_fname[16]; /* filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ +}; + +extern void start_thread32(struct pt_regs *, unsigned long, unsigned long); +#undef start_thread +#define start_thread start_thread32 +#define init_elf_binfmt init_elf32_binfmt + +#undef CONFIG_BINFMT_ELF +#ifdef CONFIG_BINFMT_ELF32 +#define CONFIG_BINFMT_ELF CONFIG_BINFMT_ELF32 +#endif +#undef CONFIG_BINFMT_ELF_MODULE +#ifdef CONFIG_BINFMT_ELF32_MODULE +#define CONFIG_BINFMT_ELF_MODULE CONFIG_BINFMT_ELF32_MODULE +#endif + +#include "../../../fs/binfmt_elf.c" diff -Nru a/arch/ppc64/kernel/bitops.c b/arch/ppc64/kernel/bitops.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/bitops.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,148 @@ +/* + * These are too big to be inlined. + */ + +#include +#include +#include + +unsigned long find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) +{ + unsigned long *p = ((unsigned long *) addr) + (offset >> 6); + unsigned long result = offset & ~63UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 63UL; + if (offset) { + tmp = *(p++); + tmp |= ~0UL >> (64-offset); + if (size < 64) + goto found_first; + if (~tmp) + goto found_middle; + size -= 64; + result += 64; + } + while (size & ~63UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 64; + size -= 64; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) /* Are any bits zero? */ + return result + size; /* Nope. */ +found_middle: + return result + ffz(tmp); +} + +static __inline__ unsigned long ___ffs(unsigned long word) +{ + unsigned long result = 0; + + while (!(word & 1UL)) { + result++; + word >>= 1; + } + return result; +} + +unsigned long find_next_bit(void *addr, unsigned long size, unsigned long offset) +{ + unsigned long *p = ((unsigned long *) addr) + (offset >> 6); + unsigned long result = offset & ~63UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 63UL; + if (offset) { + tmp = *(p++); + tmp &= (~0UL << offset); + if (size < 64) + goto found_first; + if (tmp) + goto found_middle; + size -= 64; + result += 64; + } + while (size & ~63UL) { + if ((tmp = *(p++))) + goto found_middle; + result += 64; + size -= 64; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp &= (~0UL >> (64 - size)); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + ___ffs(tmp); +} + +static __inline__ unsigned int ext2_ilog2(unsigned int x) +{ + int lz; + + asm("cntlzw %0,%1" : "=r" (lz) : "r" (x)); + return 31 - lz; +} + +static __inline__ unsigned int ext2_ffz(unsigned int x) +{ + u32 tempRC; + if ((x = ~x) == 0) + return 32; + tempRC = ext2_ilog2(x & -x); + return tempRC; +} + +unsigned long find_next_zero_le_bit(void *addr, unsigned long size, unsigned long offset) +{ + unsigned int *p = ((unsigned int *) addr) + (offset >> 5); + unsigned int result = offset & ~31; + unsigned int tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31; + if (offset) { + tmp = cpu_to_le32p(p++); + tmp |= ~0U >> (32-offset); /* bug or feature ? */ + if (size < 32) + goto found_first; + if (tmp != ~0) + goto found_middle; + size -= 32; + result += 32; + } + while (size >= 32) { + if ((tmp = cpu_to_le32p(p++)) != ~0) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = cpu_to_le32p(p); +found_first: + tmp |= ~0 << size; + if (tmp == ~0) /* Are any bits zero? */ + return result + size; /* Nope. */ +found_middle: + return result + ext2_ffz(tmp); +} diff -Nru a/arch/ppc64/kernel/chrp_setup.c b/arch/ppc64/kernel/chrp_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/chrp_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,375 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * Modified by PPC64 Team, IBM Corp + * + * 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. + */ + +/* + * bootup setup stuff.. + */ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "local_irq.h" +#include "i8259.h" +#include "open_pic.h" +#include "xics.h" +#include + +extern volatile unsigned char *chrp_int_ack_special; +extern struct Naca *naca; + +void chrp_setup_pci_ptrs(void); +void chrp_progress(char *, unsigned short); +void chrp_request_regions(void); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; +extern void openpic_init_IRQ(void); +extern void init_ras_IRQ(void); + +extern void find_and_init_phbs(void); +extern void pSeries_pcibios_fixup(void); +extern void iSeries_pcibios_fixup(void); + +extern void pSeries_get_rtc_time(struct rtc_time *rtc_time); +extern int pSeries_set_rtc_time(struct rtc_time *rtc_time); +void pSeries_calibrate_decr(void); + +kdev_t boot_dev; +unsigned long virtPython0Facilities = 0; // python0 facility area (memory mapped io) (64-bit format) VIRTUAL address. + +extern HPTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern int probingmem; +extern unsigned long loops_per_jiffy; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + +void +chrp_get_cpuinfo(struct seq_file *m) +{ + struct device_node *root; + const char *model = ""; + + root = find_path_device("/"); + if (root) + model = get_property(root, "model", NULL); + seq_printf(m, "machine\t\t: CHRP %s\n", model); +} + +void __init chrp_request_regions(void) { + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); +} + +void __init +chrp_setup_arch(void) +{ + extern char cmd_line[]; + struct device_node *root; + unsigned int *opprop; + + /* openpic global configuration register (64-bit format). */ + /* openpic Interrupt Source Unit pointer (64-bit format). */ + /* python0 facility area (mmio) (64-bit format) REAL address. */ + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000; + +#ifdef CONFIG_BLK_DEV_INITRD + /* this is fine for chrp */ + initrd_below_start_ok = 1; + + if (initrd_start) + ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); + else +#endif + ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */ + + printk("Boot arguments: %s\n", cmd_line); + + /* Find and initialize PCI host bridges */ + /* iSeries needs to be done much later. */ + #ifndef CONFIG_PPC_ISERIES + find_and_init_phbs(); + #endif + + /* Find the Open PIC if present */ + root = find_path_device("/"); + opprop = (unsigned int *) get_property(root, + "platform-open-pic", NULL); + if (opprop != 0) { + int n = prom_n_addr_cells(root); + unsigned long openpic; + + for (openpic = 0; n > 0; --n) + openpic = (openpic << 32) + *opprop++; + printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic); + udbg_printf("OpenPIC addr: %lx\n", openpic); + OpenPIC_Addr = __ioremap(openpic, 0x40000, _PAGE_NO_CACHE); + } + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif +} + +void __init +chrp_init2(void) +{ + /* + * It is sensitive, when this is called (not too earlu) + * -- tibit + */ + chrp_request_regions(); + ppc_md.progress(UTS_RELEASE, 0x7777); +} + + +/* Early initialization. Relocation is on but do not reference unbolted pages */ +void __init pSeries_init_early(void) +{ +#ifdef CONFIG_PPC_PSERIES /* This ifdef should go away */ + void *comport; + + hpte_init_pSeries(); + tce_init_pSeries(); + pSeries_pcibios_init_early(); + +#ifdef CONFIG_SMP + smp_init_pSeries(); +#endif + + /* Map the uart for udbg. */ + comport = (void *)__ioremap(naca->serialPortAddr, 16, _PAGE_NO_CACHE); + udbg_init_uart(comport); + + ppc_md.udbg_putc = udbg_putc; + ppc_md.udbg_getc = udbg_getc; + ppc_md.udbg_getc_poll = udbg_getc_poll; +#endif +} + +void __init +chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#if 0 /* PPPBBB remove this later... -Peter */ +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r6 ) + { + initrd_start = __va(r6); + initrd_end = __va(r6 + r7); + } +#endif /* CONFIG_BLK_DEV_INITRD */ +#endif + + ppc_md.ppc_machine = _machine; + + ppc_md.setup_arch = chrp_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = chrp_get_cpuinfo; + if(naca->interrupt_controller == IC_OPEN_PIC) { + ppc_md.init_IRQ = openpic_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.post_irq = NULL; + } else { + ppc_md.init_IRQ = xics_init_IRQ; + ppc_md.get_irq = xics_get_irq; + ppc_md.post_irq = NULL; + } + ppc_md.init_ras_IRQ = init_ras_IRQ; + + #ifndef CONFIG_PPC_ISERIES + ppc_md.pcibios_fixup = pSeries_pcibios_fixup; + #else + ppc_md.pcibios_fixup = NULL; + // ppc_md.pcibios_fixup = iSeries_pcibios_fixup; + #endif + + + ppc_md.init = chrp_init2; + + ppc_md.restart = rtas_restart; + ppc_md.power_off = rtas_power_off; + ppc_md.halt = rtas_halt; + + ppc_md.time_init = NULL; + ppc_md.get_boot_time = pSeries_get_rtc_time; + ppc_md.get_rtc_time = pSeries_get_rtc_time; + ppc_md.set_rtc_time = pSeries_set_rtc_time; + ppc_md.calibrate_decr = pSeries_calibrate_decr; + + ppc_md.progress = chrp_progress; + +#ifdef CONFIG_VT + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x63; /* Print Screen */ +#endif +#endif + + ppc_md.progress("Linux ppc64\n", 0x0); +} + +void __chrp +chrp_progress(char *s, unsigned short hex) +{ + struct device_node *root; + int width, *p; + char *os; + static int display_character, set_indicator; + static int max_width; + + if (hex) + udbg_printf(" %s\n", s); + + if (!rtas.base || (_machine != _MACH_pSeries)) + return; + + if (max_width == 0) { + if ( (root = find_path_device("/rtas")) && + (p = (unsigned int *)get_property(root, + "ibm,display-line-length", + NULL)) ) + max_width = *p; + else + max_width = 0x10; + display_character = rtas_token("display-character"); + set_indicator = rtas_token("set-indicator"); + } + if (display_character == RTAS_UNKNOWN_SERVICE) { + /* use hex display */ + if (set_indicator == RTAS_UNKNOWN_SERVICE) + return; + rtas_call(set_indicator, 3, 1, NULL, 6, 0, hex); + return; + } + + rtas_call(display_character, 1, 1, NULL, '\r'); + + width = max_width; + os = s; + while ( *os ) + { + if ( (*os == '\n') || (*os == '\r') ) + width = max_width; + else + width--; + rtas_call(display_character, 1, 1, NULL, *os++ ); + /* if we overwrite the screen length */ + if ( width == 0 ) + while ( (*os != 0) && (*os != '\n') && (*os != '\r') ) + os++; + } + + /* Blank to end of line. */ + while ( width-- > 0 ) + rtas_call(display_character, 1, 1, NULL, ' ' ); +} + +extern void setup_default_decr(void); + +void __init pSeries_calibrate_decr(void) +{ + struct device_node *cpu; + struct div_result divres; + int *fp; + unsigned long freq; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + freq = 16666000; /* hardcoded default */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "timebase-frequency", NULL); + if (fp != 0) + freq = *fp; + } + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000 ); + + tb_ticks_per_jiffy = freq / HZ; + tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; + tb_ticks_per_usec = freq / 1000000; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + div128_by_32( 1024*1024, 0, tb_ticks_per_sec, &divres ); + tb_to_xs = divres.result_low; + + setup_default_decr(); +} + diff -Nru a/arch/ppc64/kernel/eeh.c b/arch/ppc64/kernel/eeh.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/eeh.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,313 @@ +/* + * eeh.c + * Copyright (C) 2001 Dave Engebretsen & Todd Inglett IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Change Activity: + * 2001/10/27 : engebret : Created. + * End Change Activity + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pci.h" + +#define BUID_HI(buid) ((buid) >> 32) +#define BUID_LO(buid) ((buid) & 0xffffffff) +#define CONFIG_ADDR(busno, devfn) (((((busno) & 0xff) << 8) | ((devfn) & 0xf8)) << 8) + +unsigned long eeh_total_mmio_reads; +unsigned long eeh_total_mmio_ffs; +unsigned long eeh_false_positives; +/* RTAS tokens */ +static int ibm_set_eeh_option; +static int ibm_set_slot_reset; +static int ibm_read_slot_reset_state; + +static int eeh_implemented; +#define EEH_MAX_OPTS 4096 +static char *eeh_opts; +static int eeh_opts_last; +static int eeh_check_opts_config(struct pci_dev *dev); + + +unsigned long eeh_token(unsigned long phb, unsigned long bus, unsigned long devfn, unsigned long offset) +{ + if (phb > 0xff) + panic("eeh_token: phb 0x%lx is too large\n", phb); + if (offset & 0x0fffffff00000000) + panic("eeh_token: offset 0x%lx is out of range\n", offset); + return ((IO_UNMAPPED_REGION_ID << 60) | (phb << 48UL) | ((bus & 0xff) << 40UL) | (devfn << 32UL) | (offset & 0xffffffff)); +} + + + +int eeh_get_state(unsigned long ea) { + return 0; +} + + +/* Check for an eeh failure at the given token address. + * The given value has been read and it should be 1's (0xff, 0xffff or 0xffffffff). + * + * Probe to determine if an error actually occurred. If not return val. + * Otherwise panic. + */ +unsigned long eeh_check_failure(void *token, unsigned long val) +{ + unsigned long config_addr = (unsigned long)token >> 24; /* PPBBDDRR */ + unsigned long phbidx = (config_addr >> 24) & 0xff; + struct pci_controller *phb; + unsigned long ret, rets[2]; + + config_addr &= 0xffff00; /* 00BBDD00 */ + + if (phbidx >= global_phb_number) { + panic("EEH: checking token %p phb index of %ld is greater than max of %d\n", token, phbidx, global_phb_number-1); + } + phb = phbtab[phbidx]; + eeh_false_positives++; + + ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets, + config_addr, BUID_HI(phb->buid), BUID_LO(phb->buid)); + if (ret == 0 && rets[1] == 1 && rets[2] != 0) { + struct pci_dev *dev; + int bus = ((unsigned long)token >> 40) & 0xffff; /* include PHB# in bus */ + int devfn = (config_addr >> 8) & 0xff; + + dev = pci_find_slot(bus, devfn); + if (dev) + panic("EEH: MMIO failure (%ld) on device:\n %s %s\n", + rets[2], dev->slot_name, dev->name); + else + panic("EEH: MMIO failure (%ld) on device buid %lx, config_addr %lx\n", rets[2], phb->buid, config_addr); + } + return val; /* good case */ +} + +void eeh_init(void) { + ibm_set_eeh_option = rtas_token("ibm,set-eeh-option"); + ibm_set_slot_reset = rtas_token("ibm,set-slot-reset"); + ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state"); + if (ibm_set_eeh_option != RTAS_UNKNOWN_SERVICE) { + printk("PCI Enhanced I/O Error Handling Enabled\n"); + eeh_implemented = 1; + } +} + + +/* Given a PCI device check if eeh should be configured or not. + * This may look at firmware properties and/or kernel cmdline options. + */ +int is_eeh_configured(struct pci_dev *dev) +{ + struct device_node *dn = pci_device_to_OF_node(dev); + struct pci_controller *phb = PCI_GET_PHB_PTR(dev); + unsigned long ret, rets[2]; + + if (dn == NULL || phb == NULL || phb->buid == 0 || !eeh_implemented) + return 0; + + /* Hack: turn off eeh for display class devices. + * This fixes matrox accel framebuffer. + */ + if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) + return 0; + + if (!eeh_check_opts_config(dev)) + return 0; + + ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets, + CONFIG_ADDR(dn->busno, dn->devfn), + BUID_HI(phb->buid), BUID_LO(phb->buid)); + if (ret == 0 && rets[1] == 1) { + printk("EEH: %s %s is EEH capable.\n", dev->slot_name, dev->name); + return 1; + } + return 0; +} + +int eeh_set_option(struct pci_dev *dev, int option) +{ + struct device_node *dn = pci_device_to_OF_node(dev); + struct pci_controller *phb = PCI_GET_PHB_PTR(dev); + + if (dn == NULL || phb == NULL || phb->buid == 0 || !eeh_implemented) + return -2; + + return rtas_call(ibm_set_eeh_option, 4, 1, NULL, + CONFIG_ADDR(dn->busno, dn->devfn), + BUID_HI(phb->buid), BUID_LO(phb->buid), option); +} + + +static int eeh_proc_falsepositive_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + len = sprintf(page, "eeh_false_positives=%ld\n" + "eeh_total_mmio_ffs=%ld\n" + "eeh_total_mmio_reads=%ld\n", + eeh_false_positives, eeh_total_mmio_ffs, eeh_total_mmio_reads); + return len; +} + +/* Implementation of /proc/ppc64/eeh + * For now it is one file showing false positives. + */ +void eeh_init_proc(struct proc_dir_entry *top) +{ + struct proc_dir_entry *ent = create_proc_entry("eeh", S_IRUGO, top); + if (ent) { + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = (void *)eeh_proc_falsepositive_read; + } +} + +/* + * Test if "dev" should be configured on or off. + * This processes the options literally from right to left. + * This lets the user specify stupid combinations of options, + * but at least the result should be very predictable. + */ +static int eeh_check_opts_config(struct pci_dev *dev) +{ + struct device_node *dn = pci_device_to_OF_node(dev); + struct pci_controller *phb = PCI_GET_PHB_PTR(dev); + char devname[32], classname[32], phbname[32]; + char *strs[8], *s; + int nstrs, i; + int ret = 0; + + if (dn == NULL || phb == NULL || phb->buid == 0 || !eeh_implemented) + return 0; + /* Build list of strings to match */ + nstrs = 0; + s = (char *)get_property(dn, "ibm,loc-code", 0); + if (s) + strs[nstrs++] = s; + sprintf(devname, "dev%04x:%04x", dev->vendor, dev->device); + strs[nstrs++] = devname; + sprintf(classname, "class%04x", dev->class); + strs[nstrs++] = classname; + sprintf(phbname, "pci@%lx", phb->buid); + strs[nstrs++] = phbname; + strs[nstrs++] = ""; /* yes, this matches the empty string */ + + /* Now see if any string matches the eeh_opts list. + * The eeh_opts list entries start with + or -. + */ + for (s = eeh_opts; s && (s < (eeh_opts + eeh_opts_last)); s += strlen(s)+1) { + for (i = 0; i < nstrs; i++) { + if (strcasecmp(strs[i], s+1) == 0) { + ret = (strs[0] == '+') ? 1 : 0; + } + } + } + return ret; +} + +/* Handle kernel eeh-on & eeh-off cmd line options for eeh. + * + * We support: + * eeh-off=loc1,loc2,loc3... + * + * and this option can be repeated so + * eeh-off=loc1,loc2 eeh=loc3 + * is the same as eeh-off=loc1,loc2,loc3 + * + * loc is an IBM location code that can be found in a manual or + * via openfirmware (or the Hardware Management Console). + * + * We also support these additional "loc" values: + * + * dev#:# vendor:device id in hex (e.g. dev1022:2000) + * class# class id in hex (e.g. class0200) + * pci@buid all devices under phb (e.g. pci@fef00000) + * + * If no location code is specified all devices are assumed + * so eeh-off means eeh by default is off. + */ + +/* This is implemented as a null separated list of strings. + * Each string looks like this: "+X" or "-X" + * where X is a loc code, dev, class or pci string (as shown above) + * or empty which is used to indicate all. + * + * We interpret this option string list during the buswalk + * so that it will literally behave left-to-right even if + * some combinations don't make sense. Give the user exactly + * what they want! :) + */ + +static int __init eeh_parm(char *str, int state) +{ + char *s, *cur, *curend; + if (!eeh_opts) { + eeh_opts = alloc_bootmem(EEH_MAX_OPTS); + eeh_opts[eeh_opts_last++] = '+'; /* default */ + eeh_opts[eeh_opts_last++] = '\0'; + } + if (*str == '\0') { + eeh_opts[eeh_opts_last++] = state ? '+' : '-'; + eeh_opts[eeh_opts_last++] = '\0'; + return 1; + } + if (*str == '=') + str++; + for (s = str; s && *s != '\0'; s = curend) { + cur = s; + while (*cur == ',') + cur++; /* ignore empties. Don't treat as "all-on" or "all-off" */ + curend = strchr(cur, ','); + if (!curend) + curend = cur + strlen(cur); + if (*cur) { + int curlen = curend-cur; + char *sym = eeh_opts+eeh_opts_last; + if (eeh_opts_last + curlen > EEH_MAX_OPTS-2) { + printk("EEH: sorry...too many eeh cmd line options\n"); + return 1; + } + eeh_opts[eeh_opts_last++] = state ? '+' : '-'; + strncpy(eeh_opts+eeh_opts_last, cur, curlen); + eeh_opts_last += curlen; + eeh_opts[eeh_opts_last++] = '\0'; + } + } + return 1; +} + +static int __init eehoff_parm(char *str) +{ + return eeh_parm(str, 0); +} +static int __init eehon_parm(char *str) +{ + return eeh_parm(str, 1); +} + + +__setup("eeh-off", eehoff_parm); +__setup("eeh-on", eehon_parm); diff -Nru a/arch/ppc64/kernel/entry.S b/arch/ppc64/kernel/entry.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/entry.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,631 @@ +/* + * arch/ppc/kernel/entry.S + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * + * This file contains the system call entry code, context switch + * code, and exception/interrupt return code for PowerPC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "ppc_asm.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PPC_ISERIES +#define DO_SOFT_DISABLE +#endif + +#undef SHOW_SYSCALLS +#undef SHOW_SYSCALLS_TASK + +#ifdef SHOW_SYSCALLS_TASK + .data +show_syscalls_task: + .long -1 +#endif + +/* + * Handle a system call. + */ + .text +_GLOBAL(DoSyscall) + std r0,THREAD+LAST_SYSCALL(r13) + ld r11,_CCR(r1) /* Clear SO bit in CR */ + lis r10,0x1000 + andc r11,r11,r10 + std r11,_CCR(r1) +#ifdef SHOW_SYSCALLS +#ifdef SHOW_SYSCALLS_TASK + LOADBASE(r31,show_syscalls_task) + ld r31,show_syscalls_task@l(r31) + cmp 0,r13,r31 + bne 1f +#endif + LOADADDR(r3,7f) + ld r4,GPR0(r1) + ld r5,GPR3(r1) + ld r6,GPR4(r1) + ld r7,GPR5(r1) + ld r8,GPR6(r1) + ld r9,GPR7(r1) + bl .printk + LOADADDR(r3,77f) + ld r4,GPR8(r1) + ld r5,GPR9(r1) + mr r6,r13 + bl .printk + ld r0,GPR0(r1) + ld r3,GPR3(r1) + ld r4,GPR4(r1) + ld r5,GPR5(r1) + ld r6,GPR6(r1) + ld r7,GPR7(r1) + ld r8,GPR8(r1) +1: +#endif /* SHOW_SYSCALLS */ + clrrdi r10,r1,THREAD_SHIFT + ld r10,TI_FLAGS(r10) + andi. r11,r10,_TIF_SYSCALL_TRACE + bne- 50f + cmpli 0,r0,NR_syscalls + bge- 66f +/* + * Need to vector to 32 Bit or default sys_call_table here, + * based on caller's run-mode / personality. + */ +#ifdef CONFIG_BINFMT_ELF32 + andi. r11,r10,_TIF_32BIT + beq- 15f + LOADADDR(r10,.sys_call_table32) +/* + * Now mung the first 4 parameters into shape, by making certain that + * the high bits (most significant 32 bits in 64 bit reg) are 0 + * for the first 4 parameter regs(3-6). + */ + clrldi r3,r3,32 + clrldi r4,r4,32 + clrldi r5,r5,32 + clrldi r6,r6,32 +#if 0 /* XXX Why not ??? - Anton */ + clrldi r7,r7,32 + clrldi r8,r8,32 +#endif + b 17f +15: +#endif + LOADADDR(r10,.sys_call_table) +17: slwi r0,r0,3 + ldx r10,r10,r0 /* Fetch system call handler [ptr] */ + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD + blrl /* Call handler */ +_GLOBAL(ret_from_syscall_1) +20: std r3,RESULT(r1) /* Save result */ +#ifdef SHOW_SYSCALLS +#ifdef SHOW_SYSCALLS_TASK + cmp 0,r13,r31 + bne 91f +#endif + mr r4,r3 + LOADADDR(r3,79f) + bl .printk + ld r3,RESULT(r1) +91: +#endif + li r10,-_LAST_ERRNO + cmpl 0,r3,r10 + blt 30f + neg r3,r3 + cmpi 0,r3,ERESTARTNOHAND + bne 22f + li r3,EINTR +22: ld r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + std r10,_CCR(r1) +30: std r3,GPR3(r1) /* Update return value */ + b .ret_from_except +66: li r3,ENOSYS + b 22b + +/* Traced system call support */ +50: bl .do_syscall_trace + ld r0,GPR0(r1) /* Restore original registers */ + ld r3,GPR3(r1) + ld r4,GPR4(r1) + ld r5,GPR5(r1) + ld r6,GPR6(r1) + ld r7,GPR7(r1) + ld r8,GPR8(r1) + /* XXX check this - Anton */ + ld r9,GPR9(r1) + cmpli 0,r0,NR_syscalls + bge- 66f +/* + * Need to vector to 32 Bit or default sys_call_table here, + * based on caller's run-mode / personality. + */ +#ifdef CONFIG_BINFMT_ELF32 + clrrdi r10,r1,THREAD_SHIFT + ld r10,TI_FLAGS(r10) + andi. r11,r10,_TIF_32BIT + beq- 55f + LOADADDR(r10,.sys_call_table32) +/* + * Now mung the first 4 parameters into shape, by making certain that + * the high bits (most significant 32 bits in 64 bit reg) are 0 + * for the first 4 parameter regs(3-6). + */ + clrldi r3,r3,32 + clrldi r4,r4,32 + clrldi r5,r5,32 + clrldi r6,r6,32 +#if 0 /* XXX Why not ??? - Anton */ + clrldi r7,r7,32 + clrldi r8,r8,32 +#endif + b 57f +55: +#endif + LOADADDR(r10,.sys_call_table) +57: + slwi r0,r0,3 + ldx r10,r10,r0 /* Fetch system call handler [ptr] */ + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD + blrl /* Call handler */ +_GLOBAL(ret_from_syscall_2) +58: std r3,RESULT(r1) /* Save result */ + std r3,GPR0(r1) /* temporary gross hack to make strace work */ + li r10,-_LAST_ERRNO + cmpl 0,r3,r10 + blt 60f + neg r3,r3 + cmpi 0,r3,ERESTARTNOHAND + bne 57f + li r3,EINTR +57: ld r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + std r10,_CCR(r1) +60: std r3,GPR3(r1) /* Update return value */ + bl .do_syscall_trace + b .ret_from_except +66: li r3,ENOSYS + b 57b +#ifdef SHOW_SYSCALLS +7: .string "syscall %d(%x, %x, %x, %x, %x, " +77: .string "%x, %x), current=%p\n" +79: .string " -> %x\n" + .align 2,0 +#endif + +_GLOBAL(ppc32_sigreturn) + bl .sys32_sigreturn + b 80f + +_GLOBAL(ppc32_rt_sigreturn) + bl .sys32_rt_sigreturn + b 80f + +_GLOBAL(ppc64_sigreturn) + bl .sys_sigreturn + b 80f + +_GLOBAL(ppc64_rt_sigreturn) + bl .sys_rt_sigreturn + +80: clrrdi r4,r1,THREAD_SHIFT + ld r4,TI_FLAGS(r4) + andi. r4,r4,_TIF_SYSCALL_TRACE + bne- 81f + cmpi 0,r3,0 + bge .ret_from_except + b 20b +81: cmpi 0,r3,0 + blt 58b + bl .do_syscall_trace + b .ret_from_except + +/* + * This routine switches between two different tasks. The process + * state of one is saved on its kernel stack. Then the state + * of the other is restored from its kernel stack. The memory + * management hardware is updated to the second process's state. + * Finally, we can return to the second process, via ret_from_except. + * On entry, r3 points to the THREAD for the current task, r4 + * points to the THREAD for the new task. + * + * Note: there are two ways to get to the "going out" portion + * of this code; either by coming in via the entry (_switch) + * or via "fork" which must set up an environment equivalent + * to the "_switch" path. If you change this (or in particular, the + * SAVE_REGS macro), you'll have to change the fork code also. + * + * The code which creates the new task context is in 'copy_thread' + * in arch/ppc/kernel/process.c + */ +_GLOBAL(_switch) + stdu r1,-INT_FRAME_SIZE(r1) + ld r6,0(r1) + std r6,GPR1(r1) + /* r3-r13 are caller saved -- Cort */ + SAVE_GPR(2, r1) + SAVE_8GPRS(14, r1) + SAVE_10GPRS(22, r1) + mflr r20 /* Return to switch caller */ + mfmsr r22 + li r6,MSR_FP /* Disable floating-point */ + andc r22,r22,r6 + mtmsrd r22 + isync + std r20,_NIP(r1) + std r22,_MSR(r1) + std r20,_LINK(r1) + mfcr r20 + std r20,_CCR(r1) + li r6,0x0ff0 + std r6,TRAP(r1) + std r1,KSP(r3) /* Set old stack pointer */ + + mfspr r5,SPRG3 /* Get Paca */ + /* XXX remove - Anton */ + addi r3,r3,-THREAD /* old 'current' for return value */ + addi r13,r4,-THREAD /* Convert THREAD to 'current' */ + std r13,PACACURRENT(r5) /* Set new 'current' */ + +#ifdef CONFIG_PPC_ISERIES +#error fixme + ld r7,TI_FLAGS(r4) /* Get run light flag */ + mfspr r9,CTRLF + srdi r7,r7,1 /* Align to run light bit in CTRL reg */ + insrdi r9,r7,1,63 /* Insert run light into CTRL */ + mtspr CTRLT,r9 +#endif + ld r1,KSP(r4) /* Load new stack pointer */ + ld r6,_CCR(r1) + mtcrf 0xFF,r6 + /* r3-r13 are destroyed -- Cort */ + REST_8GPRS(14, r1) + REST_10GPRS(22, r1) + + ld r7,_NIP(r1) /* Return to _switch caller in new task */ + ld r1,GPR1(r1) + mtlr r7 + blr + +_GLOBAL(ret_from_fork) +#ifdef CONFIG_SMP + bl .schedule_tail +#endif + clrrdi r4,r1,THREAD_SHIFT + ld r4,TI_FLAGS(r4) + andi. r4,r4,_TIF_SYSCALL_TRACE + beq+ .ret_from_except + bl .do_syscall_trace + b .ret_from_except + +_GLOBAL(ret_from_except) +#ifdef CONFIG_PPC_ISERIES + ld r5,SOFTE(r1) + cmpdi 0,r5,0 + beq 4f +irq_recheck: + /* Check for pending interrupts (iSeries) */ + CHECKANYINT(r3,r4) + beq+ 4f /* skip do_IRQ if no interrupts */ + + mfspr r5,SPRG3 + li r3,0 + stb r3,PACAPROCENABLED(r5) /* ensure we are disabled */ + addi r3,r1,STACK_FRAME_OVERHEAD + bl .do_IRQ + b irq_recheck /* loop back and handle more */ +4: +#endif + /* + * Disable interrupts so that current_thread_info()->flags + * can't change between when we test it and when we return + * from the interrupt. + */ +recheck: + mfmsr r10 /* Get current interrupt state */ + li r4,0 + ori r4,r4,MSR_EE|MSR_RI + andc r10,r10,r4 /* clear MSR_EE and MSR_RI */ + mtmsrd r10 /* Update machine state */ + +#ifdef CONFIG_PPC_ISERIES +#error fix iSeries soft disable +#endif + + ld r3,_MSR(r1) /* Returning to user mode? */ + andi. r3,r3,MSR_PR + beq restore /* if not, just restore regs and return */ + + /* Check current_thread_info()->flags */ + clrrdi r3,r1,THREAD_SHIFT + ld r3,TI_FLAGS(r3) + andi. r0,r3,_TIF_USER_WORK_MASK + bne do_work + + addi r0,r1,INT_FRAME_SIZE /* size of frame */ + std r0,THREAD+KSP(r13) /* save kernel stack pointer */ + mfspr r4,SPRG3 /* current task's PACA */ + std r1,PACAKSAVE(r4) /* save exception stack pointer */ + +restore: + ld r3,_CTR(r1) + ld r0,_LINK(r1) + mtctr r3 + mtlr r0 + ld r3,_XER(r1) + mtspr XER,r3 + REST_8GPRS(5, r1) + REST_10GPRS(14, r1) + REST_8GPRS(24, r1) + + stdcx. r0,0,r1 /* to clear the reservation */ + +#ifdef DO_SOFT_DISABLE + ld r0,SOFTE(r1) + stb r0,PACAPROCENABLED(r13) +#endif + + ld r0,_MSR(r1) + mtspr SRR1,r0 + ld r2,_CCR(r1) + mtcrf 0xFF,r2 + ld r2,_NIP(r1) + mtspr SRR0,r2 + REST_GPR(13,r1) + ld r0,GPR0(r1) + ld r2,GPR2(r1) + ld r3,GPR3(r1) + ld r4,GPR4(r1) + ld r1,GPR1(r1) + + rfid + +do_work: + /* Enable interrupts */ + ori r10,r10,MSR_EE|MSR_RI + mtmsrd r10 + + andi. r0,r3,_TIF_NEED_RESCHED + beq 1f + bl .schedule + b recheck + +1: andi. r0,r3,_TIF_SIGPENDING + beq 2f + li r3,0 + addi r4,r1,STACK_FRAME_OVERHEAD + bl .do_signal + b recheck + +2: /* nobody uses the TIF_NOTIFY_RESUME bit yet */ + b recheck + +/* + * On CHRP, the Run-Time Abstraction Services (RTAS) have to be + * called with the MMU off. + * + * In addition, we need to be in 32b mode, at least for now. + * + * Note: r3 is an input parameter to rtas, so don't trash it... + */ +_GLOBAL(enter_rtas) + mflr r0 + std r0,16(r1) + stdu r1,-RTAS_FRAME_SIZE(r1) /* Save SP and create stack space. */ + + /* Because RTAS is running in 32b mode, it clobbers the high order half + * of all registers that it saves. We therefore save those registers + * RTAS might touch to the stack. (r0, r3-r13 are caller saved) + */ + SAVE_GPR(2, r1) /* Save the TOC */ + SAVE_GPR(13, r1) /* Save current */ + SAVE_8GPRS(14, r1) /* Save the non-volatiles */ + SAVE_10GPRS(22, r1) /* ditto */ + + mfcr r4 + std r4,_CCR(r1) + mfctr r5 + std r5,_CTR(r1) + mfspr r6,XER + std r6,_XER(r1) + mfdar r7 + std r7,_DAR(r1) + mfdsisr r8 + std r8,_DSISR(r1) + mfsrr0 r9 + std r9,_SRR0(r1) + mfsrr1 r10 + std r10,_SRR1(r1) + + /* Unfortunatly, the stack pointer and the MSR are also clobbered, + * so they are saved in the PACA (SPRG3) which allows us to restore + * our original state after RTAS returns. + */ + mfspr r4,SPRG3 /* Get PACA */ + std r1,PACAR1(r4) + mfmsr r6 + std r6,PACASAVEDMSR(r4) + + /* Setup our real return addr */ + SET_REG_TO_LABEL(r4,.rtas_return_loc) + SET_REG_TO_CONST(r9,KERNELBASE) + sub r4,r4,r9 + mtlr r4 + + li r0,0 + ori r0,r0,MSR_EE|MSR_SE|MSR_BE|MSR_RI + andc r0,r6,r0 + + li r9,1 + rldicr r9,r9,MSR_SF_LG,(63-MSR_SF_LG) + ori r9,r9,MSR_IR|MSR_DR|MSR_FE0|MSR_FE1|MSR_FP|MSR_RI + andc r6,r0,r9 + sync /* disable interrupts so SRR0/1 */ + mtmsrd r0 /* don't get trashed */ + + SET_REG_TO_LABEL(r4,rtas) + ld r5,RTASENTRY(r4) /* get the rtas->entry value */ + ld r4,RTASBASE(r4) /* get the rtas->base value */ + + mtspr SRR0,r5 + mtspr SRR1,r6 + rfid + +_STATIC(rtas_return_loc) + /* relocation is off at this point */ + mfspr r4,SPRG3 /* Get PACA */ + SET_REG_TO_CONST(r5, KERNELBASE) + sub r4,r4,r5 /* RELOC the PACA base pointer */ + + ld r1,PACAR1(r4) /* Restore our SP */ + LOADADDR(r3,.rtas_restore_regs) + ld r4,PACASAVEDMSR(r4) /* Restore our MSR */ + + mtspr SRR0,r3 + mtspr SRR1,r4 + rfid + +_STATIC(rtas_restore_regs) + /* relocation is on at this point */ + REST_GPR(2, r1) /* Restore the TOC */ + REST_GPR(13, r1) /* Restore current */ + REST_8GPRS(14, r1) /* Restore the non-volatiles */ + REST_10GPRS(22, r1) /* ditto */ + + /* put back current in r13 */ + mfspr r4,SPRG3 + ld r13,PACACURRENT(r4) + + ld r4,_CCR(r1) + mtcr r4 + ld r5,_CTR(r1) + mtctr r5 + ld r6,_XER(r1) + mtspr XER,r6 + ld r7,_DAR(r1) + mtdar r7 + ld r8,_DSISR(r1) + mtdsisr r8 + ld r9,_SRR0(r1) + mtsrr0 r9 + ld r10,_SRR1(r1) + mtsrr1 r10 + + addi r1,r1,RTAS_FRAME_SIZE /* Unstack our frame */ + ld r0,16(r1) /* get return address */ + + mtlr r0 + blr /* return to caller */ + +_GLOBAL(enter_prom) + mflr r0 + std r0,16(r1) + stdu r1,-PROM_FRAME_SIZE(r1) /* Save SP and create stack space */ + + /* Because PROM is running in 32b mode, it clobbers the high order half + * of all registers that it saves. We therefore save those registers + * PROM might touch to the stack. (r0, r3-r13 are caller saved) + */ + SAVE_8GPRS(2, r1) /* Save the TOC & incoming param(s) */ + SAVE_GPR(13, r1) /* Save current */ + SAVE_8GPRS(14, r1) /* Save the non-volatiles */ + SAVE_10GPRS(22, r1) /* ditto */ + + mfcr r4 + std r4,_CCR(r1) + mfctr r5 + std r5,_CTR(r1) + mfspr r6,XER + std r6,_XER(r1) + mfdar r7 + std r7,_DAR(r1) + mfdsisr r8 + std r8,_DSISR(r1) + mfsrr0 r9 + std r9,_SRR0(r1) + mfsrr1 r10 + std r10,_SRR1(r1) + mfmsr r11 + std r11,_MSR(r1) + + /* Unfortunatly, the stack pointer is also clobbered, so it is saved + * in the SPRG2 which allows us to restore our original state after + * PROM returns. + */ + mtspr SPRG2,r1 + + /* put a relocation offset into r3 */ + bl .reloc_offset + LOADADDR(r12,prom) + sub r12,r12,r3 + ld r12,PROMENTRY(r12) /* get the prom->entry value */ + mtlr r12 + + mfmsr r11 /* grab the current MSR */ + li r12,1 + rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) + andc r11,r11,r12 + li r12,1 + rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) + andc r11,r11,r12 + mtmsrd r11 + isync + + REST_8GPRS(2, r1) /* Restore the TOC & param(s) */ + REST_GPR(13, r1) /* Restore current */ + REST_8GPRS(14, r1) /* Restore the non-volatiles */ + REST_10GPRS(22, r1) /* ditto */ + blrl /* Entering PROM here... */ + + mfspr r1,SPRG2 /* Restore the stack pointer */ + ld r6,_MSR(r1) /* Restore the MSR */ + mtmsrd r6 + isync + + REST_GPR(2, r1) /* Restore the TOC */ + REST_GPR(13, r1) /* Restore current */ + REST_8GPRS(14, r1) /* Restore the non-volatiles */ + REST_10GPRS(22, r1) /* ditto */ + + ld r4,_CCR(r1) + mtcr r4 + ld r5,_CTR(r1) + mtctr r5 + ld r6,_XER(r1) + mtspr XER,r6 + ld r7,_DAR(r1) + mtdar r7 + ld r8,_DSISR(r1) + mtdsisr r8 + ld r9,_SRR0(r1) + mtsrr0 r9 + ld r10,_SRR1(r1) + mtsrr1 r10 + addi r1,r1,PROM_FRAME_SIZE + ld r0,16(r1) /* get return address */ + + mtlr r0 + blr /* return to caller */ diff -Nru a/arch/ppc64/kernel/head.S b/arch/ppc64/kernel/head.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/head.S Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1838 @@ +/* + * arch/ppc64/kernel/head.S + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen, Peter Bergner, and + * Mike Corrigan {engebret|bergner|mikejc}@us.ibm.com + * + * This file contains the low-level support and setup for the + * PowerPC-64 platform, including trap and interrupt dispatch. + * + * 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. + */ + +#define SECONDARY_PROCESSORS + +#include "ppc_asm.h" +#include "ppc_defs.h" +#include +#include +#include +#include + +#ifdef CONFIG_PPC_ISERIES +#define DO_SOFT_DISABLE +#endif + +/* + * We layout physical memory as follows: + * 0x0000 - 0x00ff : Secondary processor spin code + * 0x0100 - 0x2fff : pSeries Interrupt prologs + * 0x3000 - 0x3fff : Interrupt support + * 0x4000 - 0x4fff : NACA + * 0x5000 - 0x5fff : Initial segment table + * 0x6000 : iSeries and common interrupt prologs + */ + +/* + * SPRG Usage + * + * Register Definition + * + * SPRG0 reserved for hypervisor + * SPRG1 temp - used to save gpr + * SPRG2 temp - used to save gpr + * SPRG3 virt addr of Paca + */ + +/* + * Entering into this code we make the following assumptions: + * For pSeries: + * 1. The MMU is off & open firmware is running in real mode. + * 2. The kernel is entered at __start + * + * For iSeries: + * 1. The MMU is on (as it always is for iSeries) + * 2. The kernel is entered at SystemReset_Iseries + */ + + .text + .globl _stext +_stext: +_STATIC(__start) + b .__start_initialization_pSeries + + /* At offset 0x20, there is a pointer to iSeries LPAR data. + * This is required by the hypervisor */ + . = 0x20 + .llong hvReleaseData-KERNELBASE + + /* At offset 0x28 and 0x30 are offsets to the msChunks + * array (used by the iSeries LPAR debugger to do translation + * between physical addresses and absolute addresses) and + * to the pidhash table (also used by the debugger) */ + .llong msChunks-KERNELBASE + .llong pidhash-KERNELBASE + + /* Offset 0x38 - Pointer to start of embedded System.map */ + .globl embedded_sysmap_start +embedded_sysmap_start: + .llong 0 + /* Offset 0x40 - Pointer to end of embedded System.map */ + .globl embedded_sysmap_end +embedded_sysmap_end: + .llong 0 + + /* Secondary processors spin on this value until it goes to 1. */ + .globl __secondary_hold_spinloop +__secondary_hold_spinloop: + .llong 0x0 + + /* Secondary processors write this value with their cpu # */ + /* after they enter the spin loop immediatly below. */ + .globl __secondary_hold_acknowledge +__secondary_hold_acknowledge: + .llong 0x0 + + . = 0x60 +/* + * The following code is used on pSeries to hold secondary processors + * in a spin loop after they have been freed from OpenFirmware, but + * before the bulk of the kernel has been relocated. This code + * is relocated to physical address 0x60 before prom_init is run. + * All of it must fit below the first exception vector at 0x100. + */ +_GLOBAL(__secondary_hold) + /* Grab our linux cpu number */ + mr r24,r3 + + /* Tell the master cpu we're here */ + /* Relocation is off & we are located at an address less */ + /* than 0x100, so only need to grab low order offset. */ + std r24,__secondary_hold_acknowledge@l(0) + + /* All secondary cpu's wait here until told to start. */ +100: ld r4,__secondary_hold_spinloop@l(0) + cmpdi 0,r4,1 + bne 100b + +#ifdef CONFIG_HMT + b .hmt_init +#else +#ifdef CONFIG_SMP + mr r3,r24 + b .pseries_secondary_smp_init +#else + BUG_OPCODE +#endif +#endif + +/* + * The following macros define the code that appears as + * the prologue to each of the exception handlers. They + * are split into two parts to allow a single kernel binary + * to be used for pSeries, and iSeries. + */ + +/* + * We make as much of the exception code common between native Pseries + * and Iseries LPAR implementations as possible. + */ + +/* + * This is the start of the interrupt handlers for Pseries + * This code runs with relocation off. + */ +#define EX_SRR0 0 +#define EX_SRR1 8 +#define EX_R20 16 +#define EX_R21 24 +#define EX_R22 32 +#define EX_R23 40 +#define EX_DAR 48 +#define EX_DSISR 56 + +#define EXCEPTION_PROLOG_PSERIES(label) \ + mtspr SPRG2,r20; /* use SPRG2 as scratch reg */ \ + mtspr SPRG1,r21; /* save r21 */ \ + mfspr r20,SPRG3; /* get Paca virt addr */ \ + ld r21,PACAEXCSP(r20); /* get exception stack ptr */ \ + addi r21,r21,EXC_FRAME_SIZE; /* make exception frame */ \ + std r22,EX_R22(r21); /* Save r22 in exc. frame */ \ + std r23,EX_R23(r21); /* Save r23 in exc. frame */ \ + mfspr r22,SRR0; /* EA of interrupted instr */ \ + std r22,EX_SRR0(r21); /* Save SRR0 in exc. frame */ \ + mfspr r23,SRR1; /* machine state at interrupt */ \ + std r23,EX_SRR1(r21); /* Save SRR1 in exc. frame */ \ + clrrdi r22,r20,60; /* Get 0xc part of the vaddr */ \ + ori r22,r22,(label)@l; /* add in the vaddr offset */ \ + /* assumes *_common < 16b */ \ + mfmsr r23; \ + rotldi r23,r23,4; \ + ori r23,r23,0x30B; /* Set IR, DR, SF, ISF, HV */ \ + rotldi r23,r23,60; /* for generic handlers */ \ + mtspr SRR0,r22; \ + mtspr SRR1,r23; \ + mfcr r23; /* save CR in r23 */ \ + rfid + +/* + * This is the start of the interrupt handlers for i_series + * This code runs with relocation on. + */ +#define EXCEPTION_PROLOG_ISERIES \ + mtspr SPRG2,r20; /* use SPRG2 as scratch reg */\ + mtspr SPRG1,r21; /* save r21 */\ + mfspr r20,SPRG3; /* get Paca */\ + ld r21,PACAEXCSP(r20); /* get exception stack ptr */\ + addi r21,r21,EXC_FRAME_SIZE; /* make exception frame */\ + std r22,EX_R22(r21); /* save r22 on exception frame */\ + std r23,EX_R23(r21); /* Save r23 in exc. frame */\ + ld r22,LPPACA+LPPACASRR0(r20); /* Get SRR0 from ItLpPaca */\ + std r22,EX_SRR0(r21); /* save SRR0 in exc. frame */\ + ld r23,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */\ + std r23,EX_SRR1(r21); /* save SRR1 in exc. frame */\ + mfcr r23; /* save CR in r23 */ + +/* + * The common exception prolog is used for all except a few exceptions + * such as a segment miss on a kernel address. We have to be prepared + * to take another exception from the point where we first touch the + * kernel stack onwards. + * + * On entry r20 points to the paca and r21 points to the exception + * frame on entry, r23 contains the saved CR, and relocation is on. + */ +#define EXCEPTION_PROLOG_COMMON \ + mfspr r22,SPRG2; /* Save r20 in exc. frame */ \ + std r22,EX_R20(r21); \ + mfspr r22,SPRG1; /* Save r21 in exc. frame */ \ + std r22,EX_R21(r21); \ + mfspr r22,DAR; /* Save DAR in exc. frame */ \ + std r22,EX_DAR(r21); \ + std r21,PACAEXCSP(r20); /* update exception stack ptr */ \ + /* iff no protection flt */ \ + mfspr r22,DSISR; /* Save DSISR in exc. frame */ \ + std r22,EX_DSISR(r21); \ + ld r22,EX_SRR1(r21); /* Get SRR1 from exc. frame */ \ + andi. r22,r22,MSR_PR; /* Set CR for later branch */ \ + mr r22,r1; /* Save r1 */ \ + subi r1,r1,INT_FRAME_SIZE; /* alloc frame on kernel stack */ \ + beq- 1f; \ + ld r1,PACAKSAVE(r20); /* kernel stack to use */ \ +1: std r22,GPR1(r1); /* save r1 in stackframe */ \ + std r22,0(r1); /* make stack chain pointer */ \ + std r23,_CCR(r1); /* save CR in stackframe */ \ + ld r22,EX_R20(r21); /* move r20 to stackframe */ \ + std r22,GPR20(r1); \ + ld r23,EX_R21(r21); /* move r21 to stackframe */ \ + std r23,GPR21(r1); \ + ld r22,EX_R22(r21); /* move r22 to stackframe */ \ + std r22,GPR22(r1); \ + ld r23,EX_R23(r21); /* move r23 to stackframe */ \ + std r23,GPR23(r1); \ + mflr r22; /* save LR in stackframe */ \ + std r22,_LINK(r1); \ + mfctr r23; /* save CTR in stackframe */ \ + std r23,_CTR(r1); \ + mfspr r22,XER; /* save XER in stackframe */ \ + std r22,_XER(r1); \ + ld r23,EX_DAR(r21); /* move DAR to stackframe */ \ + std r23,_DAR(r1); \ + ld r22,EX_DSISR(r21); /* move DSISR to stackframe */ \ + std r22,_DSISR(r1); \ + lbz r22,PACAPROCENABLED(r20); \ + std r22,SOFTE(r1); \ + ld r22,EX_SRR0(r21); /* get SRR0 from exc. frame */ \ + ld r23,EX_SRR1(r21); /* get SRR1 from exc. frame */ \ + addi r21,r21,-EXC_FRAME_SIZE;/* pop off exception frame */ \ + std r21,PACAEXCSP(r20); \ + SAVE_GPR(0, r1); /* save r0 in stackframe */ \ + SAVE_8GPRS(2, r1); /* save r2 - r13 in stackframe */ \ + SAVE_4GPRS(10, r1); \ + ld r2,PACATOC(r20); \ + ld r13,PACACURRENT(r20) + +/* + * Note: code which follows this uses cr0.eq (set if from kernel), + * r1, r22 (SRR0), and r23 (SRR1). + */ + +/* + * Exception vectors. + */ +#define STD_EXCEPTION_PSERIES(n, label ) \ + . = n; \ + .globl label##_Pseries; \ +label##_Pseries: \ + EXCEPTION_PROLOG_PSERIES( label##_common ) + +#define STD_EXCEPTION_ISERIES( label ) \ + .globl label##_Iseries; \ +label##_Iseries: \ + EXCEPTION_PROLOG_ISERIES; \ + b label##_common + +#define MASKABLE_EXCEPTION_ISERIES( label ) \ + .globl label##_Iseries; \ +label##_Iseries: \ + EXCEPTION_PROLOG_ISERIES; \ + lbz r22,PACAPROFENABLED(r20); \ + cmpi 0,r22,0; \ + bne- label##_Iseries_profile; \ +label##_Iseries_prof_ret: \ + lbz r22,PACAPROCENABLED(r20); \ + cmpi 0,r22,0; \ + beq- label##_Iseries_masked; \ + b label##_common; \ +label##_Iseries_profile: \ + std r24,48(r21); \ + std r25,56(r21); \ + mflr r24; \ + bl do_profile; \ + mtlr r24; \ + ld r24,48(r21); \ + ld r25,56(r21); \ + b label##_Iseries_prof_ret + +#define STD_EXCEPTION_COMMON( trap, label, hdlr ) \ + .globl label##_common; \ +label##_common: \ + EXCEPTION_PROLOG_COMMON; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + li r20,0; \ + li r6,trap; \ + bl .save_remaining_regs; \ + bl hdlr; \ + b .ret_from_except + +/* + * Start of pSeries system interrupt routines + */ + . = 0x100 + .globl __start_interupts +__start_interupts: + + STD_EXCEPTION_PSERIES( 0x100, SystemReset ) + STD_EXCEPTION_PSERIES( 0x200, MachineCheck ) + STD_EXCEPTION_PSERIES( 0x300, DataAccess ) + STD_EXCEPTION_PSERIES( 0x380, DataAccessSLB ) + STD_EXCEPTION_PSERIES( 0x400, InstructionAccess ) + STD_EXCEPTION_PSERIES( 0x480, InstructionAccessSLB ) + STD_EXCEPTION_PSERIES( 0x500, HardwareInterrupt ) + STD_EXCEPTION_PSERIES( 0x600, Alignment ) + STD_EXCEPTION_PSERIES( 0x700, ProgramCheck ) + STD_EXCEPTION_PSERIES( 0x800, FPUnavailable ) + STD_EXCEPTION_PSERIES( 0x900, Decrementer ) + STD_EXCEPTION_PSERIES( 0xa00, Trap_0a ) + STD_EXCEPTION_PSERIES( 0xb00, Trap_0b ) + STD_EXCEPTION_PSERIES( 0xc00, SystemCall ) + STD_EXCEPTION_PSERIES( 0xd00, SingleStep ) + STD_EXCEPTION_PSERIES( 0xe00, Trap_0e ) + STD_EXCEPTION_PSERIES( 0xf00, PerformanceMonitor ) + STD_EXCEPTION_PSERIES( 0x1300, InstructionBreakpoint ) + + . = 0x4000 + .globl __end_interupts + .globl __start_naca +__end_interupts: +__start_naca: + /* Save space for naca. + * The first dword of the Naca is required by iSeries LPAR to + * point to itVpdAreas. On pSeries native, this value is not used. + */ + .llong itVpdAreas + .llong 0x0 + .llong 0x0 + .llong xPaca + + /* + * Space for the initial segment table + * For LPAR, the hypervisor must fill in at least one entry + * before we get control (with relocate on) + */ + + . = 0x5000 + .globl __end_naca + .globl __start_stab +__end_naca: +__start_stab: + + + . = 0x6000 + .globl __end_stab +__end_stab: + + /* + * The iSeries LPAR map is at this fixed address + * so that the HvReleaseData structure can address + * it with a 32-bit offset. + * + * The VSID values below are dependent on the + * VSID generation algorithm. See include/asm/mmu_context.h. + */ + + .llong 1 /* # ESIDs to be mapped by hypervisor */ + .llong 1 /* # memory ranges to be mapped by hypervisor */ + .llong 5 /* Page # of segment table within load area */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0x0c00000000 /* ESID to map (Kernel at EA = 0xC000000000000000) */ + .llong 0x06a99b4b14 /* VSID to map (Kernel at VA = 0x6a99b4b140000000) */ + .llong 8192 /* # pages to map (32 MB) */ + .llong 0 /* Offset from start of loadarea to start of map */ + .llong 0x0006a99b4b140000 /* VPN of first page to map */ + + . = 0x6100 + +/*** ISeries-LPAR interrupt handlers ***/ + + STD_EXCEPTION_ISERIES( MachineCheck ) + STD_EXCEPTION_ISERIES( DataAccess ) + STD_EXCEPTION_ISERIES( DataAccessSLB ) + STD_EXCEPTION_ISERIES( InstructionAccess ) + STD_EXCEPTION_ISERIES( InstructionAccessSLB ) + MASKABLE_EXCEPTION_ISERIES( HardwareInterrupt ) + STD_EXCEPTION_ISERIES( Alignment ) + STD_EXCEPTION_ISERIES( ProgramCheck ) + STD_EXCEPTION_ISERIES( FPUnavailable ) + MASKABLE_EXCEPTION_ISERIES( Decrementer ) + STD_EXCEPTION_ISERIES( Trap_0a ) + STD_EXCEPTION_ISERIES( Trap_0b ) + STD_EXCEPTION_ISERIES( SystemCall ) + STD_EXCEPTION_ISERIES( SingleStep ) + STD_EXCEPTION_ISERIES( Trap_0e ) + STD_EXCEPTION_ISERIES( PerformanceMonitor ) + + .globl SystemReset_Iseries +SystemReset_Iseries: + mfspr 25,SPRG3 /* Get Paca address */ + lhz r24,PACAPACAINDEX(r25) /* Get processor # */ + cmpi 0,r24,0 /* Are we processor 0? */ + beq .__start_initialization_iSeries /* Start up the first processor */ + mfspr r4,CTRLF + li r5,RUNLATCH /* Turn off the run light */ + andc r4,r4,r5 + mtspr CTRLT,r4 + +1: + HMT_LOW +#ifdef CONFIG_SMP + lbz r23,PACAPROCSTART(r25) /* Test if this processor + * should start */ + sync + LOADADDR(r3,current_set) + sldi r28,r24,3 /* get current_set[cpu#] */ + ldx r3,r3,r28 + addi r1,r3,THREAD_SIZE + subi r1,r1,STACK_FRAME_OVERHEAD + + cmpi 0,r23,0 + beq iseries_secondary_smp_loop /* Loop until told to go */ +#ifdef SECONDARY_PROCESSORS + bne .__secondary_start /* Loop until told to go */ +#endif +iseries_secondary_smp_loop: + /* Let the Hypervisor know we are alive */ + /* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */ + lis r3,0x8002 + rldicr r3,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */ +#else /* CONFIG_SMP */ + /* Yield the processor. This is required for non-SMP kernels + which are running on multi-threaded machines. */ + lis r3,0x8000 + rldicr r3,r3,32,15 /* r3 = (r3 << 32) & 0xffff000000000000 */ + addi r3,r3,18 /* r3 = 0x8000000000000012 which is "yield" */ + li r4,0 /* "yield timed" */ + li r5,-1 /* "yield forever" */ +#endif /* CONFIG_SMP */ + li r0,-1 /* r0=-1 indicates a Hypervisor call */ + sc /* Invoke the hypervisor via a system call */ + mfspr r25,SPRG3 /* Put r25 back ???? */ + b 1b /* If SMP not configured, secondaries + * loop forever */ + + .globl HardwareInterrupt_Iseries_masked +HardwareInterrupt_Iseries_masked: + b maskable_exception_exit + + .globl Decrementer_Iseries_masked +Decrementer_Iseries_masked: + li r22,1 + stb r22,PACALPPACA+LPPACADECRINT(r20) + lwz r22,PACADEFAULTDECR(r20) + mtspr DEC,r22 +maskable_exception_exit: + mtcrf 0xff,r23 /* Restore regs and free exception frame */ + ld r22,EX_SRR0(r21) + ld r23,EX_SRR1(r21) + mtspr SRR0,r22 + mtspr SRR1,r23 + ld r22,EX_R22(r21) + ld r23,EX_R23(r21) + mfspr r21,SPRG1 + mfspr r20,SPRG2 + rfid + +/*** Common interrupt handlers ***/ + + STD_EXCEPTION_COMMON( 0x100, SystemReset, .SystemResetException ) + STD_EXCEPTION_COMMON( 0x200, MachineCheck, .MachineCheckException ) + STD_EXCEPTION_COMMON( 0x900, Decrementer, .timer_interrupt ) + STD_EXCEPTION_COMMON( 0xa00, Trap_0a, .UnknownException ) + STD_EXCEPTION_COMMON( 0xb00, Trap_0b, .UnknownException ) + STD_EXCEPTION_COMMON( 0xd00, SingleStep, .SingleStepException ) + STD_EXCEPTION_COMMON( 0xe00, Trap_0e, .UnknownException ) + STD_EXCEPTION_COMMON( 0xf00, PerformanceMonitor, .PerformanceMonitorException ) + STD_EXCEPTION_COMMON(0x1300, InstructionBreakpoint, .InstructionBreakpointException ) + +/* + * Return from an exception which is handled without calling + * save_remaining_regs. The caller is assumed to have done + * EXCEPTION_PROLOG_COMMON. + */ +fast_exception_return: + ld r3,_CCR(r1) + ld r4,_LINK(r1) + ld r5,_CTR(r1) + ld r6,_XER(r1) + mtcr r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + REST_GPR(0, r1) + REST_8GPRS(2, r1) + REST_4GPRS(10, r1) + mtspr SRR1,r23 + mtspr SRR0,r22 + REST_4GPRS(20, r1) + ld r1,GPR1(r1) + rfid + +/* + * Here r20 points to the PACA, r21 to the exception frame, + * r23 contains the saved CR. + * r20 - r23, SRR0 and SRR1 are saved in the exception frame. + */ + .globl DataAccess_common +DataAccess_common: + mfspr r22,DAR + srdi r22,r22,60 + cmpi 0,r22,0xc + + /* Segment fault on a bolted segment. Go off and map that segment. */ + beq .do_stab_bolted +stab_bolted_user_return: + EXCEPTION_PROLOG_COMMON + ld r3,_DSISR(r1) + andis. r0,r3,0xa450 /* weird error? */ + bne 1f /* if not, try to put a PTE */ + andis. r0,r3,0x0020 /* Is it a page table fault? */ + rlwinm r4,r3,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ + ld r3,_DAR(r1) /* into the hash table */ + + beq 2f /* If so handle it */ + li r4,0x300 /* Trap number */ + bl .do_stab_SI + b 1f + +2: bl .do_hash_page_DSI /* Try to handle as hpte fault */ +1: + ld r4,_DAR(r1) + ld r5,_DSISR(r1) + addi r3,r1,STACK_FRAME_OVERHEAD +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) /* Copy saved SOFTE bit */ +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif + li r6,0x300 + bl .save_remaining_regs + bl .do_page_fault + b .ret_from_except + + .globl DataAccessSLB_common +DataAccessSLB_common: + mfspr r22,DAR + srdi r22,r22,60 + cmpi 0,r22,0xc + + /* Segment fault on a bolted segment. Go off and map that segment. */ + beq .do_slb_bolted + + EXCEPTION_PROLOG_COMMON + ld r3,_DAR(r1) + li r4,0x380 /* Exception vector */ + bl .ste_allocate + or. r3,r3,r3 /* Check return code */ + beq fast_exception_return /* Return if we succeeded */ + addi r3,r1,STACK_FRAME_OVERHEAD +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif + li r6,0x380 + bl .save_remaining_regs + bl .do_page_fault + b .ret_from_except + + .globl InstructionAccess_common +InstructionAccess_common: + EXCEPTION_PROLOG_COMMON + + andis. r0,r23,0x0020 /* no ste found? */ + beq 2f + mr r3,r22 /* SRR0 at interrupt */ + li r4,0x400 /* Trap number */ + bl .do_stab_SI + b 1f + +2: andis. r0,r23,0x4000 /* no pte found? */ + beq 1f /* if so, try to put a PTE */ + mr r3,r22 /* into the hash table */ + bl .do_hash_page_ISI /* Try to handle as hpte fault */ +1: + mr r4,r22 + mr r5,r23 + addi r3,r1,STACK_FRAME_OVERHEAD +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif + li r6,0x400 + bl .save_remaining_regs + bl .do_page_fault + b .ret_from_except + + .globl InstructionAccessSLB_common +InstructionAccessSLB_common: + EXCEPTION_PROLOG_COMMON + mr r3,r22 /* SRR0 = NIA */ + li r4,0x480 /* Exception vector */ + bl .ste_allocate + or. r3,r3,r3 /* Check return code */ + beq fast_exception_return /* Return if we succeeded */ + + addi r3,r1,STACK_FRAME_OVERHEAD +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif + li r6,0x380 + bl .save_remaining_regs + bl .do_page_fault + b .ret_from_except + + .globl HardwareInterrupt_common +HardwareInterrupt_common: + EXCEPTION_PROLOG_COMMON +HardwareInterrupt_entry: + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,0 + li r6,0x500 + bl .save_remaining_regs + /* Determine if need to run do_irq on a hardware interrupt stack */ + /* The first invocation of do_irq will occur on the kernel */ + /* stack in the current stack */ + /* All other invocations of do_irq will run on the hardware */ + /* interrupt stack associated with the PACA of the current */ + /* processor. */ + /* */ + /* The call to do_irq will preserve the value of r14 - r31 */ + /* */ + mfspr r20,SPRG3 /* get Paca */ + lbz r21,PACAHRDWINTCOUNT(r20) /* get hardware interrupt cnt */ + cmpi 0,r21,1 /* */ + addi r21,r21,1 /* incr hardware interrupt cnt*/ + stb r21,PACAHRDWINTCOUNT(r20) /* */ + bne 2f /* */ + + mr r14,r1 /* preserve current r1 */ + ld r1,PACAHRDWINTSTACK(r20) /* */ + std r14,0(r1) /* set the back chain */ + bl .do_IRQ + lbz r22,PACAHRDWINTCOUNT(r20) /* get hardware interrupt cnt */ + cmp 0,r22,r21 /* debug test */ + bne 3f + subi r21,r21,1 + stb r21,PACAHRDWINTCOUNT(r20) /* */ + mr r1,r14 /* */ + b .ret_from_except + +2: + bl .do_IRQ + + lbz r22,PACAHRDWINTCOUNT(r20) /* get hardware interrupt cnt */ + cmp 0,r22,r21 /* debug test */ + bne 3f /* */ + subi r21,r21,1 /* decr hardware interrupt cnt*/ + stb r21,PACAHRDWINTCOUNT(r20) /* */ + + b .ret_from_except + +3: + /* error - counts out of sync */ +#ifdef CONFIG_XMON + bl .xmon +#endif +4: b 4b + + + .globl Alignment_common +Alignment_common: + EXCEPTION_PROLOG_COMMON + addi r3,r1,STACK_FRAME_OVERHEAD +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif + li r6,0x600 + bl .save_remaining_regs + bl .AlignmentException + b .ret_from_except + + .globl ProgramCheck_common +ProgramCheck_common: + EXCEPTION_PROLOG_COMMON + addi r3,r1,STACK_FRAME_OVERHEAD +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif + li r6,0x700 + bl .save_remaining_regs + bl .ProgramCheckException + b .ret_from_except + + .globl FPUnavailable_common +FPUnavailable_common: + EXCEPTION_PROLOG_COMMON + bne .load_up_fpu /* if from user, just load it up */ + li r20,0 + li r6,0x800 + bl .save_remaining_regs /* if from kernel, take a trap */ + bl .KernelFP + b .ret_from_except + + .globl SystemCall_common +SystemCall_common: + EXCEPTION_PROLOG_COMMON +#ifdef CONFIG_PPC_ISERIES + cmpi 0,r0,0x5555 /* Special syscall to handle pending */ + bne+ 1f /* interrupts */ + andi. r6,r23,MSR_PR /* Only allowed from kernel */ + beq+ HardwareInterrupt_entry +1: +#endif + std r3,ORIG_GPR3(r1) +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif + li r6,0xC00 + bl .save_remaining_regs + bl .DoSyscall + b .ret_from_except + +_GLOBAL(do_hash_page_ISI) + li r4,0 +_GLOBAL(do_hash_page_DSI) + rlwimi r4,r23,32-13,30,30 /* Insert MSR_PR as _PAGE_USER */ + ori r4,r4,1 /* add _PAGE_PRESENT */ + + mflr r21 /* Save LR in r21 */ + +#ifdef DO_SOFT_DISABLE + /* + * We hard enable here (but first soft disable) so that the hash_page + * code can spin on the hash_table_lock with problem on a shared + * processor. + */ + li r0,0 + stb r0,PACAPROCENABLED(r20) /* Soft Disabled */ + + mfmsr r0 + ori r0,r0,MSR_EE+MSR_RI + mtmsrd r0 /* Hard Enable, RI on */ +#endif + + /* + * r3 contains the faulting address + * r4 contains the required access permissions + * + * at return r3 = 0 for success + */ + + bl .hash_page /* build HPTE if possible */ + +#ifdef DO_SOFT_DISABLE + /* + * Now go back to hard disabled. + */ + mfmsr r0 + li r4,0 + ori r4,r4,MSR_EE+MSR_RI + andc r0,r0,r4 + mtmsrd r0 /* Hard Disable, RI off */ + + ld r0,SOFTE(r1) + cmpdi 0,r0,0 /* See if we will soft enable in */ + /* save_remaining_regs */ + beq 5f + CHECKANYINT(r4,r5) + bne- HardwareInterrupt_entry /* Convert this DSI into an External */ + /* to process interrupts which occurred */ + /* during hash_page */ +5: + stb r0,PACAPROCENABLED(r20) /* Restore soft enable/disable status */ +#endif + or. r3,r3,r3 /* Check return code */ + beq fast_exception_return /* Return from exception on success */ + + mtlr r21 /* restore LR */ + blr /* Return to DSI or ISI on failure */ + +/* + * r20 points to the PACA, r21 to the exception frame, + * r23 contains the saved CR. + * r20 - r23, SRR0 and SRR1 are saved in the exception frame. + * We assume we aren't going to take any exceptions during this procedure. + */ +_GLOBAL(do_stab_bolted) + std r23,EX_DAR(r21) /* save CR in exc. frame */ + + mfspr r22,DSISR + andis. r22,r22,0x0020 + bne+ 2f + ld r22,8(r21) /* get SRR1 */ + andi. r22,r22,MSR_PR /* check if from user */ + bne+ stab_bolted_user_return /* from user, send the error on up */ + li r3,0 +#ifdef CONFIG_XMON + bl .xmon +#endif +1: b 1b +2: + /* (((ea >> 28) & 0x1fff) << 15) | (ea >> 60) */ + mfspr r21,DAR + rldicl r20,r21,36,32 /* Permits a full 32b of ESID */ + rldicr r20,r20,15,48 + rldicl r21,r21,4,60 + or r20,r20,r21 + + li r21,9 /* VSID_RANDOMIZER */ + sldi r21,r21,32 + oris r21,r21,58231 + ori r21,r21,39831 + + mulld r20,r20,r21 + clrldi r20,r20,28 /* r20 = vsid */ + + mfsprg r21,3 + ld r21,PACASTABVIRT(r21) + + /* Hash to the primary group */ + mfspr r22,DAR + rldicl r22,r22,36,59 + rldicr r22,r22,7,56 + or r21,r21,r22 /* r21 = first ste of the group */ + + /* Search the primary group for a free entry */ + li r22,0 +1: + ld r23,0(r21) /* Test valid bit of the current ste */ + rldicl r23,r23,57,63 + cmpwi r23,0 + bne 2f + ld r23,8(r21) /* Get the current vsid part of the ste */ + rldimi r23,r20,12,0 /* Insert the new vsid value */ + std r23,8(r21) /* Put new entry back into the stab */ + eieio /* Order vsid update */ + ld r23,0(r21) /* Get the esid part of the ste */ + mfspr r20,DAR /* Get the new esid */ + rldicl r20,r20,36,28 /* Permits a full 36b of ESID */ + rldimi r23,r20,28,0 /* Insert the new esid value */ + ori r23,r23,144 /* Turn on valid and kp */ + std r23,0(r21) /* Put new entry back into the stab */ + sync /* Order the update */ + b 3f +2: + addi r22,r22,1 + addi r21,r21,16 + cmpldi r22,7 + ble 1b + + /* Stick for only searching the primary group for now. */ + /* At least for now, we use a very simple random castout scheme */ + /* Use the TB as a random number ; OR in 1 to avoid entry 0 */ + mftb r22 + andi. r22,r22,7 + ori r22,r22,1 + sldi r22,r22,4 + + /* r21 currently points to and ste one past the group of interest */ + /* make it point to the randomly selected entry */ + subi r21,r21,128 + or r21,r21,r22 /* r21 is the entry to invalidate */ + + isync /* mark the entry invalid */ + ld r23,0(r21) + li r22,-129 + and r23,r23,r22 + std r23,0(r21) + sync + + ld r23,8(r21) + rldimi r23,r20,12,0 + std r23,8(r21) + eieio + + ld r23,0(r21) /* Get the esid part of the ste */ + mr r22,r23 + mfspr r20,DAR /* Get the new esid */ + rldicl r20,r20,36,28 /* Permits a full 32b of ESID */ + rldimi r23,r20,28,0 /* Insert the new esid value */ + ori r23,r23,144 /* Turn on valid and kp */ + std r23,0(r21) /* Put new entry back into the stab */ + + rldicl r22,r22,36,28 + rldicr r22,r22,28,35 + slbie r22 + sync + +3: + /* All done -- return from exception. */ + mfsprg r20,3 /* Load the PACA pointer */ + ld r21,PACAEXCSP(r20) /* Get the exception frame pointer */ + addi r21,r21,EXC_FRAME_SIZE + ld r23,EX_DAR(r21) /* get saved CR */ + /* note that this is almost identical to maskable_exception_exit */ + mtcr r23 /* restore CR */ + ld r22,EX_SRR0(r21) /* Get SRR0 from exc. frame */ + ld r23,EX_SRR1(r21) /* Get SRR1 from exc. frame */ + mtspr SRR0,r22 + mtspr SRR1,r23 + ld r22,EX_R22(r21) /* restore r22 and r23 */ + ld r23,EX_R23(r21) + mfspr r20,SPRG2 + mfspr r21,SPRG1 + rfid +_TRACEBACK(do_stab_bolted) + +/* + * r20 points to the PACA, r21 to the exception frame, + * r23 contains the saved CR. + * r20 - r23, SRR0 and SRR1 are saved in the exception frame. + * We assume we aren't going to take any exceptions during this procedure. + */ +_GLOBAL(do_slb_bolted) + std r23,48(r21) /* save CR in exc. frame */ + + /* (((ea >> 28) & 0x1fff) << 15) | (ea >> 60) */ + mfspr r21,DAR + rldicl r20,r21,36,32 /* Permits a full 32b of ESID */ + rldicr r20,r20,15,48 + rldicl r21,r21,4,60 + or r20,r20,r21 + + li r21,9 /* VSID_RANDOMIZER */ + sldi r21,r21,32 + oris r21,r21,58231 + ori r21,r21,39831 + + mulld r20,r20,r21 + clrldi r20,r20,28 /* r20 = vsid */ + + /* Search the SLB for a free entry */ + li r22,1 +1: + slbmfee r23,r22 + rldicl r23,r23,37,63 + cmpwi r23,0 + beq 3f /* Found an invalid entry */ + + addi r22,r22,1 + cmpldi r22,64 + blt 1b + + /* No free entry - just take the next entry, round-robin */ + /* XXX we should get the number of SLB entries from the naca */ +SLB_NUM_ENTRIES = 64 + mfspr r21,SPRG3 + ld r22,PACASTABRR(r21) + addi r23,r22,1 + cmpdi r23,SLB_NUM_ENTRIES + blt 2f + li r23,1 +2: std r23,PACASTABRR(r21) + + /* r20 = vsid, r22 = entry */ +3: + /* Put together the vsid portion of the entry. */ + li r21,0 + rldimi r21,r20,12,0 + ori r20,r21,1024 +#ifndef CONFIG_PPC_ISERIES + ori r20,r20,256 /* map kernel region with large ptes */ +#endif + + /* Put together the esid portion of the entry. */ + mfspr r21,DAR /* Get the new esid */ + rldicl r21,r21,36,28 /* Permits a full 36b of ESID */ + li r23,0 + rldimi r23,r21,28,0 /* Insert esid */ + oris r21,r23,2048 /* valid bit */ + rldimi r21,r22,0,52 /* Insert entry */ + + isync + slbmte r20,r21 + isync + + /* All done -- return from exception. */ + mfsprg r20,3 /* Load the PACA pointer */ + ld r21,PACAEXCSP(r20) /* Get the exception frame pointer */ + addi r21,r21,EXC_FRAME_SIZE + ld r23,EX_DAR(r21) /* get saved CR */ + /* note that this is almost identical to maskable_exception_exit */ + mtcr r23 /* restore CR */ + ld r22,EX_SRR0(r21) /* Get SRR0 from exc. frame */ + ld r23,EX_SRR1(r21) /* Get SRR1 from exc. frame */ + mtspr SRR0,r22 + mtspr SRR1,r23 + ld r22,EX_R22(r21) /* restore r22 and r23 */ + ld r23,EX_R23(r21) + mfspr r20,SPRG2 + mfspr r21,SPRG1 + rfid +_TRACEBACK(do_slb_bolted) + +_GLOBAL(do_stab_SI) + mflr r21 /* Save LR in r21 */ + + /* + * r3 contains the faulting address + * r4 contains the required access permissions + * + * at return r3 = 0 for success + */ + + bl .ste_allocate /* build STE if possible */ + or. r3,r3,r3 /* Check return code */ + beq fast_exception_return /* Return from exception on success */ + mtlr r21 /* restore LR */ + blr /* Return to DSI or ISI on failure */ + +/* + * This code finishes saving the registers to the exception frame. + * Address translation is already on. + */ +_GLOBAL(save_remaining_regs) + /* + * Save the rest of the registers into the pt_regs structure + */ + std r22,_NIP(r1) + std r23,_MSR(r1) + std r6,TRAP(r1) + ld r6,GPR6(r1) + SAVE_2GPRS(14, r1) + SAVE_4GPRS(16, r1) + SAVE_8GPRS(24, r1) + + /* + * Clear the RESULT field + */ + li r22,0 + std r22,RESULT(r1) + + /* + * Test if from user state; result will be tested later + */ + andi. r23,r23,MSR_PR /* Set CR for later branch */ + + /* + * Indicate that r1 contains the kernel stack and + * get the Kernel TOC and CURRENT pointers from the Paca + */ + mfspr r23,SPRG3 /* Get PACA */ + std r22,PACAKSAVE(r23) /* r1 is now kernel sp */ + ld r2,PACATOC(r23) /* Get Kernel TOC pointer */ + + /* + * If from user state, update THREAD.regs + */ + beq 2f /* Modify THREAD.regs if from user */ + addi r24,r1,STACK_FRAME_OVERHEAD + std r24,THREAD+PT_REGS(r13) +2: + SET_REG_TO_CONST(r22, MSR_KERNEL) + +#ifdef DO_SOFT_DISABLE + stb r20,PACAPROCENABLED(r23) /* possibly soft enable */ + ori r22,r22,MSR_EE /* always hard enable */ +#else + rldimi r22,r20,15,48 /* Insert desired EE value */ +#endif + + mtmsrd r22 + blr + + +do_profile: + ld r22,8(r21) /* Get SRR1 */ + andi. r22,r22,MSR_PR /* Test if in kernel */ + bnelr /* return if not in kernel */ + ld r22,0(r21) /* Get SRR0 */ + ld r25,PACAPROFSTEXT(r20) /* _stext */ + subf r22,r25,r22 /* offset into kernel */ + lwz r25,PACAPROFSHIFT(r20) + srd r22,r22,r25 + lwz r25,PACAPROFLEN(r20) /* length of profile table (-1) */ + cmp 0,r22,r25 /* off end? */ + ble 1f + mr r22,r25 /* force into last entry */ +1: sldi r22,r22,2 /* convert to offset into buffer */ + ld r25,PACAPROFBUFFER(r20) /* profile buffer */ + add r25,r25,r22 +2: lwarx r22,0,r25 /* atomically increment */ + addi r22,r22,1 + stwcx. r22,0,r25 + bne- 2b + blr + + +/* + * On pSeries, secondary processors spin in the following code. + * At entry, r3 = this processor's number (in Linux terms, not hardware). + */ +_GLOBAL(pseries_secondary_smp_init) + + /* turn on 64-bit mode */ + bl .enable_64b_mode + isync + + /* Set up a Paca value for this processor. */ + LOADADDR(r24, xPaca) /* Get base vaddr of Paca array */ + mulli r25,r3,PACA_SIZE /* Calculate vaddr of right Paca */ + add r25,r25,r24 /* for this processor. */ + + mtspr SPRG3,r25 /* Save vaddr of Paca in SPRG3 */ + mr r24,r3 /* __secondary_start needs cpu# */ + +1: + HMT_LOW + lbz r23,PACAPROCSTART(r25) /* Test if this processor should */ + /* start. */ + sync + + /* Create a temp kernel stack for use before relocation is on. */ + mr r1,r25 + addi r1,r1,PACAGUARD + addi r1,r1,0x1000 + subi r1,r1,STACK_FRAME_OVERHEAD + + cmpi 0,r23,0 +#ifdef CONFIG_SMP +#ifdef SECONDARY_PROCESSORS + bne .__secondary_start +#endif +#endif + b 1b /* Loop until told to go */ + +_GLOBAL(__start_initialization_iSeries) + + LOADADDR(r1,init_thread_union) + addi r1,r1,THREAD_SIZE + li r0,0 + stdu r0,-STACK_FRAME_OVERHEAD(r1) + + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + LOADADDR(r9,naca) + SET_REG_TO_CONST(r4, KERNELBASE) + addi r4,r4,0x4000 + std r4,0(r9) /* set the naca pointer */ + + /* Get the pointer to the segment table */ + ld r6,PACA(r4) /* Get the base Paca pointer */ + ld r4,PACASTABVIRT(r6) + + bl .iSeries_fixup_klimit + + b .start_here_common + +_GLOBAL(__start_initialization_pSeries) + mr r31,r3 /* save parameters */ + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + mr r26,r8 /* YABOOT: debug_print() routine */ + mr r25,r9 /* YABOOT: debug_delay() routine */ + mr r24,r10 /* YABOOT: debug_prom() routine */ + + bl .enable_64b_mode + + /* put a relocation offset into r3 */ + bl .reloc_offset + + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + /* Relocate the TOC from a virt addr to a real addr */ + sub r2,r2,r3 + + /* setup the naca pointer which is needed by prom_init */ + LOADADDR(r9,naca) + sub r9,r9,r3 /* addr of the variable naca */ + + SET_REG_TO_CONST(r4, KERNELBASE) + sub r4,r4,r3 + addi r4,r4,0x4000 + std r4,0(r9) /* set the value of naca */ + + /* DRENG / PPPBBB Fix the following comment!!! -Peter */ + /* The following copies the first 0x100 bytes of code from the */ + /* load addr to physical addr 0x0. This code causes secondary */ + /* processors to spin until a flag in the PACA is set. This */ + /* is done at this time rather than with the entire kernel */ + /* relocation which is done below because we need to cause the */ + /* processors to spin on code that is not going to move while OF */ + /* is still alive. Although the spin code is not actually run on */ + /* a uniprocessor, we always do this copy. */ + SET_REG_TO_CONST(r4, KERNELBASE)/* Src addr */ + sub r4,r4,r3 /* current address of __start */ + /* the source addr */ + li r3,0 /* Dest addr */ + li r5,0x100 /* # bytes of memory to copy */ + li r6,0 /* Destination offset */ + bl .copy_and_flush /* copy the first 0x100 bytes */ + + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + mr r8,r26 + mr r9,r25 + mr r10,r24 + + bl .prom_init + + li r24,0 /* cpu # */ + +/* + * At this point, r3 contains the physical address we are running at, + * returned by prom_init() + */ +_STATIC(__after_prom_start) + +/* + * We need to run with __start at physical address 0. + * This will leave some code in the first 256B of + * real memory, which are reserved for software use. + * The remainder of the first page is loaded with the fixed + * interrupt vectors. The next two pages are filled with + * unknown exception placeholders. + * + * Note: This process overwrites the OF exception vectors. + * r26 == relocation offset + * r27 == KERNELBASE + */ + bl .reloc_offset + mr r26,r3 + SET_REG_TO_CONST(r27,KERNELBASE) + + li r3,0 /* target addr */ + + sub r4,r27,r26 /* source addr */ + /* current address of _start */ + /* i.e. where we are running */ + /* the source addr */ + + LOADADDR(r5,copy_to_here) /* # bytes of memory to copy */ + sub r5,r5,r27 + + li r6,0x100 /* Start offset, the first 0x100 */ + /* bytes were copied earlier. */ + + bl .copy_and_flush /* copy the first n bytes */ + /* this includes the code being */ + /* executed here. */ + + li r0,4f@l /* Jump to the copy of this code */ + mtctr r0 /* that we just made */ + bctr + +4: LOADADDR(r9,rtas) + sub r9,r9,r26 + ld r5,RTASBASE(r9) /* get the value of rtas->base */ + ld r9,RTASSIZE(r9) /* get the value of rtas->size */ + bl .copy_and_flush /* copy upto rtas->base */ + add r6,r6,r9 /* then skip over rtas->size bytes */ + + LOADADDR(r5,klimit) + sub r5,r5,r26 + ld r5,0(r5) /* get the value of klimit */ + sub r5,r5,r27 + bl .copy_and_flush /* copy the rest */ + b .start_here_pSeries + +/* + * Copy routine used to copy the kernel to start at physical address 0 + * and flush and invalidate the caches as needed. + * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset + * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5. + * + * Note: this routine *only* clobbers r0, r6 and lr + */ +_STATIC(copy_and_flush) + addi r5,r5,-8 + addi r6,r6,-8 +4: li r0,16 /* Use the least common */ + /* denominator cache line */ + /* size. This results in */ + /* extra cache line flushes */ + /* but operation is correct. */ + /* Can't get cache line size */ + /* from NACA as it is being */ + /* moved too. */ + + mtctr r0 /* put # words/line in ctr */ +3: addi r6,r6,8 /* copy a cache line */ + ldx r0,r6,r4 + stdx r0,r6,r3 + bdnz 3b + dcbst r6,r3 /* write it to memory */ + sync + icbi r6,r3 /* flush the icache line */ + cmpld 0,r6,r5 + blt 4b + sync + addi r5,r5,8 + addi r6,r6,8 + blr + +.align 8 +copy_to_here: + +/* + * Disable FP for the task which had the FPU previously, + * and save its floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + * On SMP we know the fpu is free, since we give it up every + * switch. -- Cort + */ +_STATIC(load_up_fpu) + mfmsr r5 /* grab the current MSR */ + ori r5,r5,MSR_FP + mtmsrd r5 /* enable use of fpu now */ + isync +/* + * For SMP, we don't do lazy FPU switching because it just gets too + * horrendously complex, especially when a task switches from one CPU + * to another. Instead we call giveup_fpu in switch_to. + * + */ +#ifndef CONFIG_SMP + LOADBASE(r3,last_task_used_math) + ld r4,last_task_used_math@l(r3) + cmpi 0,r4,0 + beq 1f + addi r4,r4,THREAD /* want THREAD of last_task_used_math */ + SAVE_32FPRS(0, r4) + mffs fr0 + stfd fr0,THREAD_FPSCR-4(r4) + ld r5,PT_REGS(r4) + ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r20,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r20 /* disable FP for previous task */ + std r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#endif /* CONFIG_SMP */ + /* enable use of FP after return */ + ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 + addi r5,r13,THREAD /* Get THREAD */ + lfd fr0,THREAD_FPSCR-4(r5) + mtfsf 0xff,fr0 + REST_32FPRS(0, r5) +#ifndef CONFIG_SMP + subi r4,r5,THREAD /* Back to 'current' */ + std r4,last_task_used_math@l(r3) +#endif /* CONFIG_SMP */ + /* restore registers and return */ + b fast_exception_return + +/* + * FP unavailable trap from kernel - print a message, but let + * the task use FP in the kernel until it returns to user mode. + */ +_GLOBAL(KernelFP) + ld r3,_MSR(r1) + ori r3,r3,MSR_FP + std r3,_MSR(r1) /* enable use of FP after return */ + LOADADDR(r3,86f) + mfspr r4,SPRG3 /* Get PACA */ + ld r4,PACACURRENT(r4) /* current */ + ld r5,_NIP(r1) + b .ret_from_except +86: .string "floating point used in kernel (task=%p, pc=%x)\n" + .align 4 + +/* + * giveup_fpu(tsk) + * Disable FP for the task given as the argument, + * and save the floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + */ +_GLOBAL(giveup_fpu) + mfmsr r5 + ori r5,r5,MSR_FP + mtmsrd r5 /* enable use of fpu now */ + isync + cmpi 0,r3,0 + beqlr- /* if no previous owner, done */ + addi r3,r3,THREAD /* want THREAD of task */ + ld r5,PT_REGS(r3) + cmpi 0,r5,0 + SAVE_32FPRS(0, r3) + mffs fr0 + stfd fr0,THREAD_FPSCR-4(r3) + beq 1f + ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r3,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r3 /* disable FP for previous task */ + std r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#ifndef CONFIG_SMP + li r5,0 + LOADBASE(r4,last_task_used_math) + std r5,last_task_used_math@l(r4) +#endif /* CONFIG_SMP */ + blr + +#ifdef CONFIG_SMP +/* + * This function is called after the master CPU has released the + * secondary processors. The execution environment is relocation off. + * The Paca for this processor has the following fields initialized at + * this point: + * 1. Processor number + * 2. Segment table pointer (virtual address) + * On entry the following are set: + * r1 = stack pointer. vaddr for iSeries, raddr (temp stack) for pSeries + * r24 = cpu# (in Linux terms) + * r25 = Paca virtual address + * SPRG3 = Paca virtual address + */ +_GLOBAL(__secondary_start) + + HMT_MEDIUM /* Set thread priority to MEDIUM */ + + /* set up the TOC (virtual address) */ + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + std r2,PACATOC(r25) + li r6,0 + std r6,PACAKSAVE(r25) + stb r6,PACAPROCENABLED(r25) + +#ifndef CONFIG_PPC_ISERIES + /* Initialize the page table pointer register. */ + LOADADDR(r6,_SDR1) + ld r6,0(r6) /* get the value of _SDR1 */ + mtspr SDR1,r6 /* set the htab location */ +#endif + /* Initialize the first segment table (or SLB) entry */ + ld r3,PACASTABVIRT(r25) /* get addr of segment table */ + bl .stab_initialize + + /* load current into r13 */ + ld r13,PACACURRENT(r25) + + /* Initialize the kernel stack. Just a repeat for iSeries. */ + LOADADDR(r3,current_set) + sldi r28,r24,3 /* get current_set[cpu#] */ + ldx r1,r3,r28 + addi r1,r1,THREAD_SIZE + subi r1,r1,STACK_FRAME_OVERHEAD + + ld r3,PACASTABREAL(r25) /* get raddr of segment table */ + ori r4,r3,1 /* turn on valid bit */ + +#ifdef CONFIG_PPC_ISERIES + li r0,-1 /* hypervisor call */ + li r3,1 + sldi r3,r3,63 /* 0x8000000000000000 */ + ori r3,r3,4 /* 0x8000000000000004 */ + sc /* HvCall_setASR */ +#else + mtasr r4 /* set the stab location */ +#endif + li r7,0 + mtlr r7 + + /* enable MMU and jump to start_secondary */ + LOADADDR(r3,.start_secondary_prolog) + SET_REG_TO_CONST(r4, MSR_KERNEL) +#ifdef DO_SOFT_DISABLE + ori r4,r4,MSR_EE +#endif + mtspr SRR0,r3 + mtspr SRR1,r4 + rfid +#endif /* CONFIG_SMP */ + +/* + * Running with relocation on at this point. All we want to do is + * zero the stack back-chain pointer before going into C code. + */ +_GLOBAL(start_secondary_prolog) + li r3,0 + std r3,0(r1) /* Zero the stack frame pointer */ + bl .start_secondary + +/* + * This subroutine clobbers r11, r12 and the LR + */ +_GLOBAL(enable_64b_mode) + mfmsr r11 /* grab the current MSR */ + li r12,1 + rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) + or r11,r11,r12 + li r12,1 + rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) + or r11,r11,r12 + mtmsrd r11 + isync + blr + +/* + * This subroutine clobbers r11, r12 and the LR + */ +_GLOBAL(enable_32b_mode) + mfmsr r11 /* grab the current MSR */ + li r12,1 + rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) + andc r11,r11,r12 + li r12,1 + rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) + andc r11,r11,r12 + mtmsrd r11 + isync + blr + +/* + * This is where the main kernel code starts. + */ +_STATIC(start_here_pSeries) + /* get a new offset, now that the kernel has moved. */ + bl .reloc_offset + mr r26,r3 + + /* setup the naca pointer which is needed by *tab_initialize */ + LOADADDR(r6,naca) + sub r6,r6,r26 /* addr of the variable naca */ + li r27,0x4000 + std r27,0(r6) /* set the value of naca */ + +#ifdef CONFIG_HMT + /* Start up the second thread on cpu 0 */ + mfspr r3,PVR + srwi r3,r3,16 + cmpwi r3,0x34 /* Pulsar */ + beq 90f + cmpwi r3,0x36 /* Icestar */ + beq 90f + cmpwi r3,0x37 /* SStar */ + beq 90f + b 91f /* HMT not supported */ +90: li r3,0 + bl .hmt_start_secondary +91: +#endif + +#ifdef CONFIG_SMP + /* All secondary cpus are now spinning on a common + * spinloop, release them all now so they can start + * to spin on their individual Paca spinloops. + * For non SMP kernels, the secondary cpus never + * get out of the common spinloop. + */ + li r3,1 + LOADADDR(r5,__secondary_hold_spinloop) + tophys(r4,r5) + std r3,0(r4) +#endif + + /* The following gets the stack and TOC set up with the regs */ + /* pointing to the real addr of the kernel stack. This is */ + /* all done to support the C function call below which sets */ + /* up the htab. This is done because we have relocated the */ + /* kernel but are still running in real mode. */ + + LOADADDR(r3,init_thread_union) + sub r3,r3,r26 + + /* set up a stack pointer (physical address) */ + addi r1,r3,THREAD_SIZE + li r0,0 + stdu r0,-STACK_FRAME_OVERHEAD(r1) + + /* set up the TOC (physical address) */ + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + sub r2,r2,r26 + + /* Init naca->debug_switch so it can be used in stab & htab init. */ + bl .ppcdbg_initialize + + /* Get the pointer to the segment table which is used by */ + /* stab_initialize */ + li r27,0x4000 + ld r6,PACA(r27) /* Get the base Paca pointer */ + sub r6,r6,r26 /* convert to physical addr */ + mtspr SPRG3,r6 /* PPPBBB: Temp... -Peter */ + ld r3,PACASTABREAL(r6) + ori r4,r3,1 /* turn on valid bit */ + mtasr r4 /* set the stab location */ + + /* Initialize an initial memory mapping and turn on relocation. */ + bl .stab_initialize + bl .htab_initialize + + LOADADDR(r6,_SDR1) + sub r6,r6,r26 + ld r6,0(r6) /* get the value of _SDR1 */ + mtspr SDR1,r6 /* set the htab location */ + + LOADADDR(r3,.start_here_common) + SET_REG_TO_CONST(r4, MSR_KERNEL) + mtspr SRR0,r3 + mtspr SRR1,r4 + rfid + + /* This is where all platforms converge execution */ +_STATIC(start_here_common) + /* relocation is on at this point */ + + /* Clear out the BSS */ + LOADADDR(r11,_end) + + LOADADDR(r8,__bss_start) + + sub r11,r11,r8 /* bss size */ + addi r11,r11,7 /* round up to an even double word */ + rldicl. r11,r11,61,3 /* shift right by 3 */ + beq 4f + addi r8,r8,-8 + li r0,0 + mtctr r11 /* zero this many doublewords */ +3: stdu r0,8(r8) + bdnz 3b +4: + + /* The following code sets up the SP and TOC now that we are */ + /* running with translation enabled. */ + + LOADADDR(r3,init_thread_union) + + /* set up the stack */ + addi r1,r3,THREAD_SIZE + li r0,0 + stdu r0,-STACK_FRAME_OVERHEAD(r1) + + /* set up the TOC */ + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + /* setup the naca pointer */ + LOADADDR(r9,naca) + + SET_REG_TO_CONST(r8, KERNELBASE) + addi r8,r8,0x4000 + std r8,0(r9) /* set the value of the naca ptr */ + + LOADADDR(r4,naca) /* Get Naca ptr address */ + ld r4,0(r4) /* Get the location of the naca */ + ld r4,PACA(r4) /* Get the base Paca pointer */ + mtspr SPRG3,r4 + + /* ptr to current */ + LOADADDR(r13,init_task) + std r13,PACACURRENT(r4) + + std r2,PACATOC(r4) + li r5,0 + std r0,PACAKSAVE(r4) + + /* ptr to hardware interrupt stack for processor 0 */ + LOADADDR(r3, hardware_int_paca0) + li r5,0x1000 + sldi r5,r5,3 + subi r5,r5,STACK_FRAME_OVERHEAD + + add r3,r3,r5 + std r3,PACAHRDWINTSTACK(r4) + + li r3,0 + stb r3,PACAHRDWINTCOUNT(r4) + + /* Restore the parms passed in from the bootloader. */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + + bl .setup_system + + /* Load up the kernel context */ +5: +#ifdef DO_SOFT_DISABLE + mfspr r4,SPRG3 + li r5,0 + stb r5,PACAPROCENABLED(r4) /* Soft Disabled */ + mfmsr r5 + ori r5,r5,MSR_EE /* Hard Enabled */ + mtmsrd r5 +#endif + + bl .start_kernel + +_GLOBAL(hmt_init) +#ifdef CONFIG_HMT + LOADADDR(r5, hmt_thread_data) + mfspr r7,PVR + srwi r7,r7,16 + cmpwi r7,0x34 /* Pulsar */ + beq 90f + cmpwi r7,0x36 /* Icestar */ + beq 91f + cmpwi r7,0x37 /* SStar */ + beq 91f + b 101f +90: mfspr r6,PIR + andi. r6,r6,0x1f + b 92f +91: mfspr r6,PIR + andi. r6,r6,0x3ff +92: sldi r4,r24,3 + stwx r6,r5,r4 + bl .hmt_start_secondary + b 101f + +__hmt_secondary_hold: + LOADADDR(r5, hmt_thread_data) + clrldi r5,r5,4 + li r7,0 + mfspr r6,PIR + mfspr r8,PVR + srwi r8,r8,16 + cmpwi r8,0x34 + bne 93f + andi. r6,r6,0x1f + b 103f +93: andi. r6,r6,0x3f + +103: lwzx r8,r5,r7 + cmpw r8,r6 + beq 104f + addi r7,r7,8 + b 103b + +104: addi r7,r7,4 + lwzx r9,r5,r7 + mr r24,r9 +101: +#endif + mr r3,r24 + b .pseries_secondary_smp_init + +#ifdef CONFIG_HMT +_GLOBAL(hmt_start_secondary) + LOADADDR(r4,__hmt_secondary_hold) + clrldi r4,r4,4 + mtspr NIADORM, r4 + mfspr r4, MSRDORM + li r5, -65 + and r4, r4, r5 + mtspr MSRDORM, r4 + lis r4,0xffef + ori r4,r4,0x7403 + mtspr TSC, r4 + li r4,0x1f4 + mtspr TST, r4 + mfspr r4, HID0 + ori r4, r4, 0x1 + mtspr HID0, r4 + mfspr r4, CTRLF + oris r4, r4, 0x40 + mtspr CTRLT, r4 + blr +#endif + +/* + * We put a few things here that have to be page-aligned. + * This stuff goes at the beginning of the data segment, + * which is page-aligned. + */ + .data + .align 12 + .globl sdata +sdata: + .globl empty_zero_page +empty_zero_page: + .space 4096 + + .globl swapper_pg_dir +swapper_pg_dir: + .space 4096 + + .globl ioremap_dir +ioremap_dir: + .space 4096 + + .globl hardware_int_paca0 +hardware_int_paca0: + .space 8*4096 + +/* 1 page segment table per cpu (max 48) */ + .globl stab_array +stab_array: + .space 4096 * 48 + +/* + * This space gets a copy of optional info passed to us by the bootstrap + * Used to pass parameters into the kernel like root=/dev/sda1, etc. + */ + .globl cmd_line +cmd_line: + .space 512 diff -Nru a/arch/ppc64/kernel/htab.c b/arch/ppc64/kernel/htab.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/htab.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1230 @@ +/* + * + * + * PowerPC64 port by Mike Corrigan and Dave Engebretsen + * {mikejc|engebret}@us.ibm.com + * + * Copyright (c) 2000 Mike Corrigan + * + * Module name: htab.c + * + * Description: + * PowerPC Hashed Page Table functions + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PPC_EEH +#include +#endif + +/* For iSeries */ +#include + +/* Note: pte --> Linux PTE + * HPTE --> PowerPC Hashed Page Table Entry + */ + +HTAB htab_data = {NULL, 0, 0, 0, 0}; + +int proc_dol2crvec(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp); + +void htab_initialize(void); +void make_pte_LPAR(HPTE *htab, + unsigned long va, unsigned long pa, int mode, + unsigned long hash_mask, int large); + +extern unsigned long reloc_offset(void); +extern unsigned long get_kernel_vsid( unsigned long ea ); +extern void cacheable_memzero( void *, unsigned int ); + +extern unsigned long _SDR1; +extern unsigned long klimit; +extern struct Naca *naca; + +extern unsigned long _ASR; +extern inline void make_ste(unsigned long stab, + unsigned long esid, unsigned long vsid); + +extern char _stext[], _etext[], __start_naca[], __end_stab[]; + +static spinlock_t hash_table_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; + +#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) - offset)) +#define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) +#define RELOC(x) (*PTRRELOC(&(x))) + +extern unsigned long htab_size( unsigned long ); +unsigned long hpte_getword0_iSeries( unsigned long slot ); + +#define KB (1024) +#define MB (1024*KB) +static inline void +create_pte_mapping(unsigned long start, unsigned long end, + unsigned long mode, unsigned long mask, int large) +{ + unsigned long addr, offset = reloc_offset(); + HTAB *_htab_data = PTRRELOC(&htab_data); + HPTE *htab = (HPTE *)__v2a(_htab_data->htab); + unsigned int step; + + if (large) + step = 16*MB; + else + step = 4*KB; + + for (addr = start; addr < end; addr += step) { + unsigned long vsid = get_kernel_vsid(addr); + unsigned long va = (vsid << 28) | (addr & 0xfffffff); + make_pte(htab, va, (unsigned long)__v2a(addr), mode, mask, + large); + } +} + +void +htab_initialize(void) +{ + unsigned long table, htab_size_bytes; + unsigned long pteg_count; + unsigned long mode_ro, mode_rw, mask; + unsigned long offset = reloc_offset(); + struct Naca *_naca = RELOC(naca); + HTAB *_htab_data = PTRRELOC(&htab_data); + + /* + * Calculate the required size of the htab. We want the number of + * PTEGs to equal one half the number of real pages. + */ + htab_size_bytes = 1UL << _naca->pftSize; + pteg_count = htab_size_bytes >> 7; + + /* For debug, make the HTAB 1/8 as big as it normally would be. */ + ifppcdebug(PPCDBG_HTABSIZE) { + pteg_count >>= 3; + htab_size_bytes = pteg_count << 7; + } + + _htab_data->htab_num_ptegs = pteg_count; + _htab_data->htab_hash_mask = pteg_count - 1; + + if(_machine == _MACH_pSeries) { + /* Find storage for the HPT. Must be contiguous in + * the absolute address space. + */ + table = lmb_alloc(htab_size_bytes, htab_size_bytes); + if ( !table ) + panic("ERROR, cannot find space for HPTE\n"); + _htab_data->htab = (HPTE *)__a2v(table); + + /* htab absolute addr + encoded htabsize */ + RELOC(_SDR1) = table + __ilog2(pteg_count) - 11; + + /* Initialize the HPT with no entries */ + cacheable_memzero((void *)table, htab_size_bytes); + } else { + _htab_data->htab = NULL; + RELOC(_SDR1) = 0; + } + + mode_ro = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RXRX; + mode_rw = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX; + mask = pteg_count-1; + + /* Create PTE's for the kernel text and data sections plus + * the HPT and HPTX arrays. Make the assumption that + * (addr & KERNELBASE) == 0 (ie they are disjoint). + * We also assume that the va is <= 64 bits. + */ +#if 0 + create_pte_mapping((unsigned long)_stext, (unsigned long)__start_naca, mode_ro, mask); + create_pte_mapping((unsigned long)__start_naca, (unsigned long)__end_stab, mode_rw, mask); + create_pte_mapping((unsigned long)__end_stab, (unsigned long)_etext, mode_ro, mask); + create_pte_mapping((unsigned long)_etext, RELOC(klimit), mode_rw, mask); + create_pte_mapping((unsigned long)__a2v(table), (unsigned long)__a2v(table+htab_size_bytes), mode_rw, mask); +#else +#ifndef CONFIG_PPC_ISERIES + if (__is_processor(PV_POWER4) && _naca->physicalMemorySize > 256*MB) { + create_pte_mapping((unsigned long)KERNELBASE, + KERNELBASE + 256*MB, mode_rw, mask, 0); + create_pte_mapping((unsigned long)KERNELBASE + 256*MB, + KERNELBASE + (_naca->physicalMemorySize), + mode_rw, mask, 1); + return; + } +#endif + create_pte_mapping((unsigned long)KERNELBASE, + KERNELBASE+(_naca->physicalMemorySize), + mode_rw, mask, 0); +#endif +} +#undef KB +#undef MB + +/* + * Create a pte. Used during initialization only. + * We assume the PTE will fit in the primary PTEG. + */ +void make_pte(HPTE *htab, + unsigned long va, unsigned long pa, int mode, + unsigned long hash_mask, int large) +{ + HPTE *hptep; + unsigned long hash, i; + volatile unsigned long x = 1; + unsigned long vpn; + +#ifdef CONFIG_PPC_PSERIES + if(_machine == _MACH_pSeriesLP) { + make_pte_LPAR(htab, va, pa, mode, hash_mask, large); + return; + } +#endif + + if (large) + vpn = va >> 24; + else + vpn = va >> 12; + + hash = hpt_hash(vpn, large); + + hptep = htab + ((hash & hash_mask)*HPTES_PER_GROUP); + + for (i = 0; i < 8; ++i, ++hptep) { + if ( hptep->dw0.dw0.v == 0 ) { /* !valid */ + hptep->dw1.dword1 = pa | mode; + hptep->dw0.dword0 = 0; + hptep->dw0.dw0.avpn = va >> 23; + hptep->dw0.dw0.bolted = 1; /* bolted */ + hptep->dw0.dw0.v = 1; /* make valid */ + return; + } + } + + /* We should _never_ get here and too early to call xmon. */ + for(;x;x|=1); +} + +/* Functions to invalidate a HPTE */ +static void hpte_invalidate_iSeries( unsigned long slot ) +{ + HvCallHpt_invalidateSetSwBitsGet( slot, 0, 0 ); +} + +static void hpte_invalidate_pSeries( unsigned long slot ) +{ + /* Local copy of the first doubleword of the HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + + /* Locate the HPTE */ + HPTE * hptep = htab_data.htab + slot; + + /* Get the first doubleword of the HPTE */ + hpte_dw0.d = hptep->dw0.dword0; + + /* Invalidate the hpte */ + hptep->dw0.dword0 = 0; + + /* Invalidate the tlb */ + { + unsigned long vsid, group, pi, pi_high; + + vsid = hpte_dw0.h.avpn >> 5; + group = slot >> 3; + if(hpte_dw0.h.h) { + group = ~group; + } + pi = (vsid ^ group) & 0x7ff; + pi_high = (hpte_dw0.h.avpn & 0x1f) << 11; + pi |= pi_high; + _tlbie(pi << 12); + } +} + + +/* Select an available HPT slot for a new HPTE + * return slot index (if in primary group) + * return -slot index (if in secondary group) + */ +static long hpte_selectslot_iSeries( unsigned long vpn ) +{ + HPTE hpte; + long ret_slot, orig_slot; + unsigned long primary_hash; + unsigned long hpteg_slot; + unsigned long slot; + unsigned i, k; + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + + ret_slot = orig_slot = HvCallHpt_findValid( &hpte, vpn ); + if ( hpte.dw0.dw0.v ) { /* If valid ...what do we do now? */ + udbg_printf( "hpte_selectslot_iSeries: vpn 0x%016lx already valid at slot 0x%016lx\n", vpn, ret_slot ); + udbg_printf( "hpte_selectslot_iSeries: returned hpte 0x%016lx 0x%016lx\n", hpte.dw0.dword0, hpte.dw1.dword1 ); + + return (0x8000000000000000); + /* panic("select_hpte_slot found entry already valid\n"); */ + } + if ( ret_slot == -1 ) { /* -1 indicates no available slots */ + + /* No available entry found in secondary group */ + + PMC_SW_SYSTEM(htab_capacity_castouts); + + primary_hash = hpt_hash(vpn, 0); + hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + k = htab_data.next_round_robin++ & 0x7; + + for ( i=0; idw0.dw0.v == 0 ) { + /* If an available slot found, return it */ + return hpteg_slot + i; + } + hptep++; + } + + /* No available entry found in primary group */ + + PMC_SW_SYSTEM(htab_primary_overflows); + + /* Search the secondary group */ + + hpteg_slot = ( ~primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hptep = htab_data.htab + hpteg_slot; + + for (i=0; idw0.dw0.v == 0 ) { + /* If an available slot found, return it */ + return -(hpteg_slot + i); + } + hptep++; + } + + /* No available entry found in secondary group */ + + PMC_SW_SYSTEM(htab_capacity_castouts); + + /* Select an entry in the primary group to replace */ + + hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hptep = htab_data.htab + hpteg_slot; + k = htab_data.next_round_robin++ & 0x7; + + for (i=0; idw0.dword0; + return dword0; +} + +static long hpte_find_iSeries(unsigned long vpn) +{ + HPTE hpte; + long slot; + + slot = HvCallHpt_findValid( &hpte, vpn ); + if ( hpte.dw0.dw0.v ) { + if ( slot < 0 ) { + slot &= 0x7fffffffffffffff; + slot = -slot; + } + } else + slot = -1; + return slot; +} + +static long hpte_find_pSeries(unsigned long vpn) +{ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + long slot; + unsigned long hash; + unsigned long i,j; + + hash = hpt_hash(vpn, 0); + for ( j=0; j<2; ++j ) { + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + for ( i=0; i> 11 ) ) && + ( hpte_dw0.h.v ) && + ( hpte_dw0.h.h == j ) ) { + /* HPTE matches */ + if ( j ) + slot = -slot; + return slot; + } + ++slot; + } + hash = ~hash; + } + return -1; +} + +/* This function is called by iSeries setup when initializing the hpt */ +void build_valid_hpte( unsigned long vsid, unsigned long ea, unsigned long pa, + pte_t * ptep, unsigned hpteflags, unsigned bolted ) +{ + unsigned long vpn, flags; + long hpte_slot; + unsigned hash; + pte_t pte; + + vpn = ((vsid << 28) | ( ea & 0xffff000 )) >> 12; + + spin_lock_irqsave( &hash_table_lock, flags ); + + hpte_slot = ppc_md.hpte_selectslot( vpn ); + hash = 0; + if ( hpte_slot < 0 ) { + if ( hpte_slot == 0x8000000000000000 ) { + udbg_printf("hash_page: ptep = 0x%016lx\n", + (unsigned long)ptep ); + udbg_printf("hash_page: ea = 0x%016lx\n", ea ); + udbg_printf("hash_page: vpn = 0x%016lx\n", vpn ); + + panic("hash_page: hpte already exists\n"); + } + hash = 1; + hpte_slot = -hpte_slot; + } + ppc_md.hpte_create_valid( hpte_slot, vpn, pa >> 12, hash, ptep, + hpteflags, bolted ); + + if ( ptep ) { + /* Get existing pte flags */ + pte = *ptep; + pte_val(pte) &= ~_PAGE_HPTEFLAGS; + + /* Add in the has hpte flag */ + pte_val(pte) |= _PAGE_HASHPTE; + + /* Add in the _PAGE_SECONDARY flag */ + pte_val(pte) |= hash << 15; + + /* Add in the hpte slot */ + pte_val(pte) |= (hpte_slot << 12) & _PAGE_GROUP_IX; + + /* Save the new pte. */ + *ptep = pte; + + } + spin_unlock_irqrestore( &hash_table_lock, flags ); +} + + +/* Create an HPTE and validate it + * It is assumed that the HPT slot currently is invalid. + * The HPTE is set with the vpn, rpn (converted to absolute) + * and flags + */ +static void hpte_create_valid_iSeries(unsigned long slot, unsigned long vpn, + unsigned long prpn, unsigned hash, + void * ptep, unsigned hpteflags, + unsigned bolted ) +{ + /* Local copy of HPTE */ + struct { + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } dw0; + /* Local copy of second doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword1 h; + Hpte_dword1_flags f; + } dw1; + } lhpte; + + unsigned long avpn = vpn >> 11; + unsigned long arpn = physRpn_to_absRpn( prpn ); + + /* Fill in the local HPTE with absolute rpn, avpn and flags */ + lhpte.dw1.d = 0; + lhpte.dw1.h.rpn = arpn; + lhpte.dw1.f.flags = hpteflags; + + lhpte.dw0.d = 0; + lhpte.dw0.h.avpn = avpn; + lhpte.dw0.h.h = hash; + lhpte.dw0.h.bolted = bolted; + lhpte.dw0.h.v = 1; + + /* Now fill in the actual HPTE */ + HvCallHpt_addValidate( slot, hash, (HPTE *)&lhpte ); +} + +static void hpte_create_valid_pSeries(unsigned long slot, unsigned long vpn, + unsigned long prpn, unsigned hash, + void * ptep, unsigned hpteflags, + unsigned bolted) +{ + /* Local copy of HPTE */ + struct { + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } dw0; + /* Local copy of second doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword1 h; + Hpte_dword1_flags f; + } dw1; + } lhpte; + + unsigned long avpn = vpn >> 11; + unsigned long arpn = physRpn_to_absRpn( prpn ); + + HPTE *hptep; + + /* Fill in the local HPTE with absolute rpn, avpn and flags */ + lhpte.dw1.d = 0; + lhpte.dw1.h.rpn = arpn; + lhpte.dw1.f.flags = hpteflags; + + lhpte.dw0.d = 0; + lhpte.dw0.h.avpn = avpn; + lhpte.dw0.h.h = hash; + lhpte.dw0.h.bolted = bolted; + lhpte.dw0.h.v = 1; + + /* Now fill in the actual HPTE */ + hptep = htab_data.htab + slot; + + /* Set the second dword first so that the valid bit + * is the last thing set + */ + + hptep->dw1.dword1 = lhpte.dw1.d; + + /* Guarantee the second dword is visible before + * the valid bit + */ + + __asm__ __volatile__ ("eieio" : : : "memory"); + + /* Now set the first dword including the valid bit */ + hptep->dw0.dword0 = lhpte.dw0.d; + + __asm__ __volatile__ ("ptesync" : : : "memory"); +} + +/* find_linux_pte returns the address of a linux pte for a given + * effective address and directory. If not found, it returns zero. + */ + +pte_t * find_linux_pte( pgd_t * pgdir, unsigned long ea ) +{ + pgd_t *pg; + pmd_t *pm; + pte_t *pt = NULL; + pte_t pte; + pg = pgdir + pgd_index( ea ); + if ( ! pgd_none( *pg ) ) { + + pm = pmd_offset( pg, ea ); + if ( ! pmd_none( *pm ) ) { + pt = pte_offset( pm, ea ); + pte = *pt; + if ( ! pte_present( pte ) ) + pt = NULL; + } + } + + return pt; + +} + +static inline unsigned long computeHptePP( unsigned long pte ) +{ + return ( pte & _PAGE_USER ) | + ( ( ( pte & _PAGE_USER ) >> 1 ) & + ( ( ~( ( pte >> 2 ) & /* _PAGE_RW */ + ( pte >> 7 ) ) ) & /* _PAGE_DIRTY */ + 1 ) ); +} + +static void hpte_updatepp_iSeries(long slot, unsigned long newpp, unsigned long va) +{ + HvCallHpt_setPp( slot, newpp ); +} + +static void hpte_updatepp_pSeries(long slot, unsigned long newpp, unsigned long va) +{ + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + + /* Local copy of second doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword1 h; + Hpte_dword1_flags f; + } hpte_dw1; + + HPTE * hptep = htab_data.htab + slot; + + /* Turn off valid bit in HPTE */ + hpte_dw0.d = hptep->dw0.dword0; + hpte_dw0.h.v = 0; + hptep->dw0.dword0 = hpte_dw0.d; + + /* Ensure it is out of the tlb too */ + _tlbie( va ); + + /* Insert the new pp bits into the HPTE */ + hpte_dw1.d = hptep->dw1.dword1; + hpte_dw1.h.pp = newpp; + hptep->dw1.dword1 = hpte_dw1.d; + + /* Ensure it is visible before validating */ + __asm__ __volatile__ ("eieio" : : : "memory"); + + /* Turn the valid bit back on in HPTE */ + hpte_dw0.h.v = 1; + hptep->dw0.dword0 = hpte_dw0.d; + + __asm__ __volatile__ ("ptesync" : : : "memory"); +} + +/* + * Update the page protection bits. Intended to be used to create + * guard pages for kernel data structures on pages which are bolted + * in the HPT. Assumes pages being operated on will not be stolen. + */ +void hpte_updateboltedpp_iSeries(unsigned long newpp, unsigned long ea ) +{ + unsigned long vsid,va,vpn; + long slot; + + vsid = get_kernel_vsid( ea ); + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + vpn = va >> PAGE_SHIFT; + + slot = ppc_md.hpte_find( vpn ); + HvCallHpt_setPp( slot, newpp ); +} + + +static __inline__ void set_pp_bit(unsigned long pp, HPTE *addr) +{ + unsigned long old; + unsigned long *p = (unsigned long *)(&(addr->dw1)); + + __asm__ __volatile__( + "1: ldarx %0,0,%3\n\ + rldimi %0,%2,0,62\n\ + stdcx. %0,0,%3\n\ + bne 1b" + : "=&r" (old), "=m" (*p) + : "r" (pp), "r" (p), "m" (*p) + : "cc"); +} + +/* + * Update the page protection bits. Intended to be used to create + * guard pages for kernel data structures on pages which are bolted + * in the HPT. Assumes pages being operated on will not be stolen. + */ +void hpte_updateboltedpp_pSeries(unsigned long newpp, unsigned long ea) +{ + unsigned long vsid,va,vpn,flags; + long slot; + HPTE *hptep; + + vsid = get_kernel_vsid( ea ); + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + vpn = va >> PAGE_SHIFT; + + slot = ppc_md.hpte_find( vpn ); + hptep = htab_data.htab + slot; + + set_pp_bit(newpp , hptep); + + /* Ensure it is out of the tlb too */ + spin_lock_irqsave( &hash_table_lock, flags ); + _tlbie( va ); + spin_unlock_irqrestore( &hash_table_lock, flags ); +} + + + +/* This is called very early. */ +void hpte_init_iSeries(void) +{ + ppc_md.hpte_invalidate = hpte_invalidate_iSeries; + ppc_md.hpte_updatepp = hpte_updatepp_iSeries; + ppc_md.hpte_updateboltedpp = hpte_updateboltedpp_iSeries; + ppc_md.hpte_getword0 = hpte_getword0_iSeries; + ppc_md.hpte_selectslot = hpte_selectslot_iSeries; + ppc_md.hpte_create_valid = hpte_create_valid_iSeries; + ppc_md.hpte_find = hpte_find_iSeries; +} +void hpte_init_pSeries(void) +{ + ppc_md.hpte_invalidate = hpte_invalidate_pSeries; + ppc_md.hpte_updatepp = hpte_updatepp_pSeries; + ppc_md.hpte_updateboltedpp = hpte_updateboltedpp_pSeries; + ppc_md.hpte_getword0 = hpte_getword0_pSeries; + ppc_md.hpte_selectslot = hpte_selectslot_pSeries; + ppc_md.hpte_create_valid = hpte_create_valid_pSeries; + ppc_md.hpte_find = hpte_find_pSeries; +} + +/* Handle a fault by adding an HPTE + * If the address can't be determined to be valid + * via Linux page tables, return 1. If handled + * return 0 + */ +int hash_page( unsigned long ea, unsigned long access ) +{ + int rc = 1; + void * pgdir = NULL; + unsigned long va, vsid, vpn; + unsigned long newpp, hash_ind, prpn; + unsigned long hpteflags, regionid; + long slot; + struct mm_struct * mm; + pte_t old_pte, new_pte, *ptep; + + /* Check for invalid addresses. */ + if (!IS_VALID_EA(ea)) { + return 1; + } + + regionid = REGION_ID(ea); + switch ( regionid ) { + case USER_REGION_ID: + mm = current->mm; + if ( mm == NULL ) { + PPCDBG(PPCDBG_MM, "hash_page returning; mm = 0\n"); + return 1; + } + vsid = get_vsid(mm->context, ea ); + break; + case IO_REGION_ID: + mm = &ioremap_mm; + vsid = get_kernel_vsid( ea ); + break; + case VMALLOC_REGION_ID: + mm = &init_mm; + vsid = get_kernel_vsid( ea ); + break; +#ifdef CONFIG_PPC_EEH + case IO_UNMAPPED_REGION_ID: + udbg_printf("EEH Error ea = 0x%lx\n", ea); + PPCDBG_ENTER_DEBUGGER(); + panic("EEH Error ea = 0x%lx\n", ea); + break; +#endif + case KERNEL_REGION_ID: + /* As htab_initialize is now, we shouldn't ever get here since + * we're bolting the entire 0xC0... region. + */ + udbg_printf("Little faulted on kernel address 0x%lx\n", ea); + PPCDBG_ENTER_DEBUGGER(); + panic("Little faulted on kernel address 0x%lx\n", ea); + break; + default: + /* Not a valid range, send the problem up to do_page_fault */ + return 1; + break; + } + + /* Search the Linux page table for a match with va */ + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + vpn = va >> PAGE_SHIFT; + pgdir = mm->pgd; + PPCDBG(PPCDBG_MM, "hash_page ea = 0x%16.16lx, va = 0x%16.16lx\n current = 0x%16.16lx, access = %lx\n", ea, va, current, access); + if ( pgdir == NULL ) { + return 1; + } + + /* Lock the Linux page table to prevent mmap and kswapd + * from modifying entries while we search and update + */ + + spin_lock( &mm->page_table_lock ); + + ptep = find_linux_pte( pgdir, ea ); + /* If no pte found, send the problem up to do_page_fault */ + if ( ! ptep ) { + spin_unlock( &mm->page_table_lock ); + return 1; + } + + /* Acquire the hash table lock to guarantee that the linux + * pte we fetch will not change + */ + spin_lock( &hash_table_lock ); + + old_pte = *ptep; + + /* If the pte is not "present" (valid), send the problem + * up to do_page_fault. + */ + if ( ! pte_present( old_pte ) ) { + spin_unlock( &hash_table_lock ); + spin_unlock( &mm->page_table_lock ); + return 1; + } + + /* At this point we have found a pte (which was present). + * The spinlocks prevent this status from changing + * The hash_table_lock prevents the _PAGE_HASHPTE status + * from changing (RPN, DIRTY and ACCESSED too) + * The page_table_lock prevents the pte from being + * invalidated or modified + */ + +/* At this point, we have a pte (old_pte) which can be used to build or update + * an HPTE. There are 5 cases: + * + * 1. There is a valid (present) pte with no associated HPTE (this is + * the most common case) + * 2. There is a valid (present) pte with an associated HPTE. The + * current values of the pp bits in the HPTE prevent access because the + * user doesn't have appropriate access rights. + * 3. There is a valid (present) pte with an associated HPTE. The + * current values of the pp bits in the HPTE prevent access because we are + * doing software DIRTY bit management and the page is currently not DIRTY. + * 4. This is a Kernel address (0xC---) for which there is no page directory. + * There is an HPTE for this page, but the pp bits prevent access. + * Since we always set up kernel pages with R/W access for the kernel + * this case only comes about for users trying to access the kernel. + * This case is always an error and is not dealt with further here. + * 5. This is a Kernel address (0xC---) for which there is no page directory. + * There is no HPTE for this page. + + * Check the user's access rights to the page. If access should be prevented + * then send the problem up to do_page_fault. + */ + + access |= _PAGE_PRESENT; + if ( 0 == ( access & ~(pte_val(old_pte)) ) ) { + /* + * Check if pte might have an hpte, but we have + * no slot information + */ + if ( pte_val(old_pte) & _PAGE_HPTENOIX ) { + unsigned long slot; + pte_val(old_pte) &= ~_PAGE_HPTEFLAGS; + slot = ppc_md.hpte_find( vpn ); + if ( slot != -1 ) { + if ( slot < 0 ) { + pte_val(old_pte) |= _PAGE_SECONDARY; + slot = -slot; + } + pte_val(old_pte) |= ((slot << 12) & _PAGE_GROUP_IX) | _PAGE_HASHPTE; + + } + } + + /* User has appropriate access rights. */ + new_pte = old_pte; + /* If the attempted access was a store */ + if ( access & _PAGE_RW ) + pte_val(new_pte) |= _PAGE_ACCESSED | + _PAGE_DIRTY; + else + pte_val(new_pte) |= _PAGE_ACCESSED; + + /* Only cases 1, 3 and 5 still in play */ + + newpp = computeHptePP( pte_val(new_pte) ); + + /* Check if pte already has an hpte (case 3) */ + if ( pte_val(old_pte) & _PAGE_HASHPTE ) { + /* There MIGHT be an HPTE for this pte */ + unsigned long hash, slot, secondary; + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + hash = hpt_hash(vpn, 0); + secondary = (pte_val(old_pte) & _PAGE_SECONDARY) >> 15; + if ( secondary ) + hash = ~hash; + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + slot += (pte_val(old_pte) & _PAGE_GROUP_IX) >> 12; + /* If there is an HPTE for this page it is indexed by slot */ + hpte_dw0.d = ppc_md.hpte_getword0( slot ); + if ( (hpte_dw0.h.avpn == (vpn >> 11) ) && + (hpte_dw0.h.v) && + (hpte_dw0.h.h == secondary ) ){ + /* HPTE matches */ + ppc_md.hpte_updatepp( slot, newpp, va ); + if ( !pte_same( old_pte, new_pte ) ) + *ptep = new_pte; + } + else { + /* HPTE is not for this pte */ + pte_val(old_pte) &= ~_PAGE_HPTEFLAGS; + } + } + if ( !( pte_val(old_pte) & _PAGE_HASHPTE ) ) { + /* Cases 1 and 5 */ + /* For these cases we need to create a new + * HPTE and update the linux pte (for + * case 1). For case 5 there is no linux pte. + * + * Find an available HPTE slot + */ + slot = ppc_md.hpte_selectslot( vpn ); + + /* Debug code */ + if ( slot == 0x8000000000000000 ) { + unsigned long xold_pte = pte_val(old_pte); + unsigned long xnew_pte = pte_val(new_pte); + + udbg_printf("hash_page: ptep = 0x%016lx\n", (unsigned long)ptep ); + udbg_printf("hash_page: old_pte = 0x%016lx\n", xold_pte ); + udbg_printf("hash_page: new_pte = 0x%016lx\n", xnew_pte ); + udbg_printf("hash_page: ea = 0x%016lx\n", ea ); + udbg_printf("hash_page: va = 0x%016lx\n", va ); + udbg_printf("hash_page: access = 0x%016lx\n", access ); + + panic("hash_page: hpte already exists\n"); + } + + hash_ind = 0; + if ( slot < 0 ) { + slot = -slot; + hash_ind = 1; + } + + /* Set the physical address */ + prpn = pte_val(old_pte) >> PTE_SHIFT; + + if ( ptep ) { + /* Update the linux pte with the HPTE slot */ + pte_val(new_pte) &= ~_PAGE_HPTEFLAGS; + pte_val(new_pte) |= hash_ind << 15; + pte_val(new_pte) |= (slot<<12) & _PAGE_GROUP_IX; + pte_val(new_pte) |= _PAGE_HASHPTE; + /* No need to use ldarx/stdcx here because all + * who might be updating the pte will hold the page_table_lock + * or the hash_table_lock (we hold both) + */ + *ptep = new_pte; + } + + /* copy appropriate flags from linux pte */ + hpteflags = (pte_val(new_pte) & 0x1f8) | newpp; + + /* Create the HPTE */ + ppc_md.hpte_create_valid( slot, vpn, prpn, hash_ind, ptep, hpteflags, 0 ); + + } + + /* Indicate success */ + rc = 0; + } + + spin_unlock( &hash_table_lock ); + if (ptep) + spin_unlock( &mm->page_table_lock ); + + return rc; +} + +void flush_hash_page( unsigned long context, unsigned long ea, pte_t pte ) +{ + unsigned long vsid, vpn, va, hash, secondary, slot, flags; + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + + if ( (ea >= USER_START ) && ( ea <= USER_END ) ) + vsid = get_vsid( context, ea ); + else + vsid = get_kernel_vsid( ea ); + va = (vsid << 28) | (ea & 0x0fffffff); + vpn = va >> PAGE_SHIFT; + hash = hpt_hash(vpn, 0); + secondary = (pte_val(pte) & _PAGE_SECONDARY) >> 15; + if ( secondary ) + hash = ~hash; + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + slot += (pte_val(pte) & _PAGE_GROUP_IX) >> 12; + /* If there is an HPTE for this page it is indexed by slot */ + + spin_lock_irqsave( &hash_table_lock, flags); + hpte_dw0.d = ppc_md.hpte_getword0( slot ); + if ( (hpte_dw0.h.avpn == (vpn >> 11) ) && + (hpte_dw0.h.v) && + (hpte_dw0.h.h == secondary ) ){ + /* HPTE matches */ + ppc_md.hpte_invalidate( slot ); + } + else { + unsigned k; + /* Temporarily lets check for the hpte in all possible slots */ + for ( secondary = 0; secondary < 2; ++secondary ) { + hash = hpt_hash(vpn, 0); + if ( secondary ) + hash = ~hash; + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + for ( k=0; k<8; ++k ) { + hpte_dw0.d = ppc_md.hpte_getword0( slot+k ); + if ( ( hpte_dw0.h.avpn == (vpn >> 11) ) && + ( hpte_dw0.h.v ) && + ( hpte_dw0.h.h == secondary ) ) { + while (1) ; + } + } + } + + } + spin_unlock_irqrestore( &hash_table_lock, flags ); +} + +int proc_dol2crvec(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int vleft, first=1, len, left, val; +#define TMPBUFLEN 256 + char buf[TMPBUFLEN], *p; + static const char *sizestrings[4] = { + "2MB", "256KB", "512KB", "1MB" + }; + static const char *clockstrings[8] = { + "clock disabled", "+1 clock", "+1.5 clock", "reserved(3)", + "+2 clock", "+2.5 clock", "+3 clock", "reserved(7)" + }; + static const char *typestrings[4] = { + "flow-through burst SRAM", "reserved SRAM", + "pipelined burst SRAM", "pipelined late-write SRAM" + }; + static const char *holdstrings[4] = { + "0.5", "1.0", "(reserved2)", "(reserved3)" + }; + + if ( ((_get_PVR() >> 16) != 8) && ((_get_PVR() >> 16) != 12)) + return -EFAULT; + + if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + + vleft = table->maxlen / sizeof(int); + left = *lenp; + + for (; left /*&& vleft--*/; first=0) { + if (write) { + while (left) { + char c; + if(get_user(c,(char *) buffer)) + return -EFAULT; + if (!isspace(c)) + break; + left--; + ((char *) buffer)++; + } + if (!left) + break; + len = left; + if (len > TMPBUFLEN-1) + len = TMPBUFLEN-1; + if(copy_from_user(buf, buffer, len)) + return -EFAULT; + buf[len] = 0; + p = buf; + if (*p < '0' || *p > '9') + break; + val = simple_strtoul(p, &p, 0); + len = p-buf; + if ((len < left) && *p && !isspace(*p)) + break; + buffer += len; + left -= len; +#if 0 + /* DRENG need a def */ + _set_L2CR(0); + _set_L2CR(val); + while ( _get_L2CR() & 0x1 ) + /* wait for invalidate to finish */; +#endif + + } else { + p = buf; + if (!first) + *p++ = '\t'; +#if 0 + /* DRENG need a def */ + val = _get_L2CR(); +#endif + p += sprintf(p, "0x%08x: ", val); + p += sprintf(p, " %s", (val >> 31) & 1 ? "enabled" : + "disabled"); + p += sprintf(p, ", %sparity", (val>>30)&1 ? "" : "no "); + p += sprintf(p, ", %s", sizestrings[(val >> 28) & 3]); + p += sprintf(p, ", %s", clockstrings[(val >> 25) & 7]); + p += sprintf(p, ", %s", typestrings[(val >> 23) & 2]); + p += sprintf(p, "%s", (val>>22)&1 ? ", data only" : ""); + p += sprintf(p, "%s", (val>>20)&1 ? ", ZZ enabled": ""); + p += sprintf(p, ", %s", (val>>19)&1 ? "write-through" : + "copy-back"); + p += sprintf(p, "%s", (val>>18)&1 ? ", testing" : ""); + p += sprintf(p, ", %sns hold",holdstrings[(val>>16)&3]); + p += sprintf(p, "%s", (val>>15)&1 ? ", DLL slow" : ""); + p += sprintf(p, "%s", (val>>14)&1 ? ", diff clock" :""); + p += sprintf(p, "%s", (val>>13)&1 ? ", DLL bypass" :""); + + p += sprintf(p,"\n"); + + len = strlen(buf); + if (len > left) + len = left; + if(copy_to_user(buffer, buf, len)) + return -EFAULT; + left -= len; + buffer += len; + break; + } + } + + if (!write && !first && left) { + if(put_user('\n', (char *) buffer)) + return -EFAULT; + left--, buffer++; + } + if (write) { + p = (char *) buffer; + while (left) { + char c; + if(get_user(c, p++)) + return -EFAULT; + if (!isspace(c)) + break; + left--; + } + } + if (write && first) + return -EINVAL; + *lenp -= left; + filp->f_pos += *lenp; + return 0; +} + diff -Nru a/arch/ppc64/kernel/hvCall.S b/arch/ppc64/kernel/hvCall.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/hvCall.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,99 @@ +/* + * arch/ppc64/kernel/hvCall.S + * + * + * This file contains the code to perform calls to the + * iSeries LPAR hypervisor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "ppc_asm.h" +#include +#include + + .text + +/* + * Hypervisor call + * + * Invoke the iSeries hypervisor via the System Call instruction + * Parameters are passed to this routine in registers r3 - r10 + * + * r3 contains the HV function to be called + * r4-r10 contain the operands to the hypervisor function + * + */ + +_GLOBAL(HvCall) +_GLOBAL(HvCall0) +_GLOBAL(HvCall1) +_GLOBAL(HvCall2) +_GLOBAL(HvCall3) +_GLOBAL(HvCall4) +_GLOBAL(HvCall5) +_GLOBAL(HvCall6) +_GLOBAL(HvCall7) + + + mfcr r0 + std r0,-8(r1) + stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1) + + /* r0 = 0xffffffffffffffff indicates a hypervisor call */ + + li r0,-1 + + /* Invoke the hypervisor */ + + sc + + ld r1,0(r1) + ld r0,-8(r1) + mtcrf 0xff,r0 + + /* return to caller, return value in r3 */ + + blr + +_GLOBAL(HvCall0Ret16) +_GLOBAL(HvCall1Ret16) +_GLOBAL(HvCall2Ret16) +_GLOBAL(HvCall3Ret16) +_GLOBAL(HvCall4Ret16) +_GLOBAL(HvCall5Ret16) +_GLOBAL(HvCall6Ret16) +_GLOBAL(HvCall7Ret16) + + mfcr r0 + std r0,-8(r1) + std r31,-16(r1) + stdu r1,-(STACK_FRAME_OVERHEAD+32)(r1) + + mr r31,r4 + li r0,-1 + mr r4,r5 + mr r5,r6 + mr r6,r7 + mr r7,r8 + mr r8,r9 + mr r9,r10 + + sc + + std r3,0(r31) + std r4,8(r31) + + mr r3,r5 + + ld r1,0(r1) + ld r0,-8(r1) + mtcrf 0xff,r0 + ld r31,-16(r1) + + blr + + diff -Nru a/arch/ppc64/kernel/i8259.c b/arch/ppc64/kernel/i8259.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/i8259.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,164 @@ +/* + * c 2001 PPC64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include "i8259.h" +#include +#include + +unsigned char cached_8259[2] = { 0xff, 0xff }; +#define cached_A1 (cached_8259[0]) +#define cached_21 (cached_8259[1]) + +static spinlock_t i8259_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; + +int i8259_pic_irq_offset; + +int i8259_irq(int cpu) +{ + int irq; + + spin_lock/*_irqsave*/(&i8259_lock/*, flags*/); + /* + * Perform an interrupt acknowledge cycle on controller 1 + */ + outb(0x0C, 0x20); + irq = inb(0x20) & 7; + if (irq == 2) + { + /* + * Interrupt is cascaded so perform interrupt + * acknowledge on controller 2 + */ + outb(0x0C, 0xA0); + irq = (inb(0xA0) & 7) + 8; + } + else if (irq==7) + { + /* + * This may be a spurious interrupt + * + * Read the interrupt status register. If the most + * significant bit is not set then there is no valid + * interrupt + */ + outb(0x0b, 0x20); + if(~inb(0x20)&0x80) { + spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/); + return -1; + } + } + spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/); + return irq; +} + +static void i8259_mask_and_ack_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + + if (irq_nr > 7) { + cached_A1 |= 1 << (irq_nr-8); + inb(0xA1); /* DUMMY */ + outb(cached_A1,0xA1); + outb(0x20,0xA0); /* Non-specific EOI */ + outb(0x20,0x20); /* Non-specific EOI to cascade */ + } else { + cached_21 |= 1 << irq_nr; + inb(0x21); /* DUMMY */ + outb(cached_21,0x21); + outb(0x20,0x20); /* Non-specific EOI */ + } + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_set_irq_mask(int irq_nr) +{ + outb(cached_A1,0xA1); + outb(cached_21,0x21); +} + +static void i8259_mask_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + if ( irq_nr < 8 ) + cached_21 |= 1 << irq_nr; + else + cached_A1 |= 1 << (irq_nr-8); + i8259_set_irq_mask(irq_nr); + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_unmask_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + if ( irq_nr < 8 ) + cached_21 &= ~(1 << irq_nr); + else + cached_A1 &= ~(1 << (irq_nr-8)); + i8259_set_irq_mask(irq_nr); + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + i8259_unmask_irq(irq); +} + +struct hw_interrupt_type i8259_pic = { + " i8259 ", + NULL, + NULL, + i8259_unmask_irq, + i8259_mask_irq, + i8259_mask_and_ack_irq, + i8259_end_irq, + NULL +}; + +void __init i8259_init(void) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + /* init master interrupt controller */ + outb(0x11, 0x20); /* Start init sequence */ + outb(0x00, 0x21); /* Vector base */ + outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0x21); /* Select 8086 mode */ + outb(0xFF, 0x21); /* Mask all */ + /* init slave interrupt controller */ + outb(0x11, 0xA0); /* Start init sequence */ + outb(0x08, 0xA1); /* Vector base */ + outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0xA1); /* Select 8086 mode */ + outb(0xFF, 0xA1); /* Mask all */ + outb(cached_A1, 0xA1); + outb(cached_21, 0x21); + spin_unlock_irqrestore(&i8259_lock, flags); + request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT, + "82c59 secondary cascade", NULL ); + +} diff -Nru a/arch/ppc64/kernel/i8259.h b/arch/ppc64/kernel/i8259.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/i8259.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,19 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _PPC_KERNEL_i8259_H +#define _PPC_KERNEL_i8259_H + +#include "local_irq.h" + +extern struct hw_interrupt_type i8259_pic; + +void i8259_init(void); +int i8259_irq(int); + +#endif /* _PPC_KERNEL_i8259_H */ diff -Nru a/arch/ppc64/kernel/iSeries_IoMmTable.c b/arch/ppc64/kernel/iSeries_IoMmTable.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/iSeries_IoMmTable.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,163 @@ +/************************************************************************/ +/* This module supports the iSeries I/O Address translation mapping */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, December 14, 2000 */ +/* Added Bar table for IoMm performance. */ +/* Ported to ppc64 */ +/* Added dynamic table allocation */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iSeries_IoMmTable.h" +#include "pci.h" + +/*******************************************************************/ +/* Table defines */ +/* Each Entry size is 4 MB * 1024 Entries = 4GB I/O address space. */ +/*******************************************************************/ +#define Max_Entries 1024 +unsigned long iSeries_IoMmTable_Entry_Size = 0x0000000000400000; +unsigned long iSeries_Base_Io_Memory = 0xE000000000000000; +unsigned long iSeries_Max_Io_Memory = 0xE000000000000000; +static long iSeries_CurrentIndex = 0; + +/*******************************************************************/ +/* Lookup Tables. */ +/*******************************************************************/ +struct iSeries_Device_Node** iSeries_IoMmTable; +u8* iSeries_IoBarTable; + +/*******************************************************************/ +/* Static and Global variables */ +/*******************************************************************/ +static char* iSeriesPciIoText = "iSeries PCI I/O"; +static spinlock_t iSeriesIoMmTableLock = SPIN_LOCK_UNLOCKED; + +/*******************************************************************/ +/* iSeries_IoMmTable_Initialize */ +/*******************************************************************/ +/* Allocates and initalizes the Address Translation Table and Bar */ +/* Tables to get them ready for use. Must be called before any */ +/* I/O space is handed out to the device BARs. */ +/* A follow up method,iSeries_IoMmTable_Status can be called to */ +/* adjust the table after the device BARs have been assiged to */ +/* resize the table. */ +/*******************************************************************/ +void iSeries_IoMmTable_Initialize(void) +{ + spin_lock(&iSeriesIoMmTableLock); + iSeries_IoMmTable = kmalloc(sizeof(void*)*Max_Entries,GFP_KERNEL); + iSeries_IoBarTable = kmalloc(sizeof(u8)*Max_Entries, GFP_KERNEL); + spin_unlock(&iSeriesIoMmTableLock); + PCIFR("IoMmTable Initialized 0x%p", iSeries_IoMmTable); + if(iSeries_IoMmTable == NULL || iSeries_IoBarTable == NULL) { + panic("PCI: I/O tables allocation failed.\n"); + } +} + +/*******************************************************************/ +/* iSeries_IoMmTable_AllocateEntry */ +/*******************************************************************/ +/* Adds pci_dev entry in address translation table */ +/*******************************************************************/ +/* - Allocates the number of entries required in table base on BAR */ +/* size. */ +/* - Allocates starting at iSeries_Base_Io_Memory and increases. */ +/* - The size is round up to be a multiple of entry size. */ +/* - CurrentIndex is incremented to keep track of the last entry. */ +/* - Builds the resource entry for allocated BARs. */ +/*******************************************************************/ +static void iSeries_IoMmTable_AllocateEntry(struct pci_dev* PciDev, int BarNumber) +{ + struct resource* BarResource = &PciDev->resource[BarNumber]; + long BarSize = pci_resource_len(PciDev,BarNumber); + /***********************************************************/ + /* No space to allocate, quick exit, skip Allocation. */ + /***********************************************************/ + if(BarSize == 0) return; + /***********************************************************/ + /* Set Resource values. */ + /***********************************************************/ + spin_lock(&iSeriesIoMmTableLock); + BarResource->name = iSeriesPciIoText; + BarResource->start = iSeries_IoMmTable_Entry_Size*iSeries_CurrentIndex; + BarResource->start+= iSeries_Base_Io_Memory; + BarResource->end = BarResource->start+BarSize-1; + /***********************************************************/ + /* Allocate the number of table entries needed for BAR. */ + /***********************************************************/ + while (BarSize > 0 ) { + *(iSeries_IoMmTable +iSeries_CurrentIndex) = (struct iSeries_Device_Node*)PciDev->sysdata; + *(iSeries_IoBarTable+iSeries_CurrentIndex) = BarNumber; + BarSize -= iSeries_IoMmTable_Entry_Size; + ++iSeries_CurrentIndex; + } + iSeries_Max_Io_Memory = (iSeries_IoMmTable_Entry_Size*iSeries_CurrentIndex)+iSeries_Base_Io_Memory; + spin_unlock(&iSeriesIoMmTableLock); +} + +/*******************************************************************/ +/* iSeries_allocateDeviceBars */ +/*******************************************************************/ +/* - Allocates ALL pci_dev BAR's and updates the resources with the*/ +/* BAR value. BARS with zero length will have the resources */ +/* The HvCallPci_getBarParms is used to get the size of the BAR */ +/* space. It calls iSeries_IoMmTable_AllocateEntry to allocate */ +/* each entry. */ +/* - Loops through The Bar resourses(0 - 5) including the the ROM */ +/* is resource(6). */ +/*******************************************************************/ +void iSeries_allocateDeviceBars(struct pci_dev* PciDev) +{ + struct resource* BarResource; + int BarNumber; + for(BarNumber = 0; BarNumber <= PCI_ROM_RESOURCE; ++BarNumber) { + BarResource = &PciDev->resource[BarNumber]; + iSeries_IoMmTable_AllocateEntry(PciDev, BarNumber); + } +} + +/************************************************************************/ +/* Translates the IoAddress to the device that is mapped to IoSpace. */ +/* This code is inlined, see the iSeries_pci.c file for the replacement.*/ +/************************************************************************/ +struct iSeries_Device_Node* iSeries_xlateIoMmAddress(void* IoAddress) +{ + return NULL; +} + +/************************************************************************ + * Status hook for IoMmTable + ************************************************************************/ +void iSeries_IoMmTable_Status(void) +{ + PCIFR("IoMmTable......: 0x%p",iSeries_IoMmTable); + PCIFR("IoMmTable Range: 0x%p to 0x%p",iSeries_Base_Io_Memory,iSeries_Max_Io_Memory); + return; +} diff -Nru a/arch/ppc64/kernel/iSeries_IoMmTable.h b/arch/ppc64/kernel/iSeries_IoMmTable.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/iSeries_IoMmTable.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,85 @@ +#ifndef _ISERIES_IOMMTABLE_H +#define _ISERIES_IOMMTABLE_H +/************************************************************************/ +/* File iSeries_IoMmTable.h created by Allan Trautman on Dec 12 2001. */ +/************************************************************************/ +/* Interfaces for the write/read Io address translation table. */ +/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created December 12, 2000 */ +/* Ported to ppc64, August 30, 2001 */ +/* End Change Activity */ +/************************************************************************/ + +struct pci_dev; +struct iSeries_Device_Node; + +extern struct iSeries_Device_Node** iSeries_IoMmTable; +extern u8* iSeries_IoBarTable; +extern unsigned long iSeries_Base_Io_Memory; +extern unsigned long iSeries_Max_Io_Memory; +extern unsigned long iSeries_Base_Io_Memory; +extern unsigned long iSeries_IoMmTable_Entry_Size; +/************************************************************************/ +/* iSeries_IoMmTable_Initialize */ +/************************************************************************/ +/* - Initalizes the Address Translation Table and get it ready for use. */ +/* Must be called before any client calls any of the other methods. */ +/* */ +/* Parameters: None. */ +/* */ +/* Return: None. */ +/************************************************************************/ +extern void iSeries_IoMmTable_Initialize(void); +extern void iSeries_IoMmTable_Status(void); + +/************************************************************************/ +/* iSeries_allocateDeviceBars */ +/************************************************************************/ +/* - Allocates ALL pci_dev BAR's and updates the resources with the BAR */ +/* value. BARS with zero length will not have the resources. The */ +/* HvCallPci_getBarParms is used to get the size of the BAR space. */ +/* It calls iSeries_IoMmTable_AllocateEntry to allocate each entry. */ +/* */ +/* Parameters: */ +/* pci_dev = Pointer to pci_dev structure that will be mapped to pseudo */ +/* I/O Address. */ +/* */ +/* Return: */ +/* The pci_dev I/O resources updated with pseudo I/O Addresses. */ +/************************************************************************/ +extern void iSeries_allocateDeviceBars(struct pci_dev* ); + +/************************************************************************/ +/* iSeries_xlateIoMmAddress */ +/************************************************************************/ +/* - Translates an I/O Memory address to Device Node that has been the */ +/* allocated the psuedo I/O Address. */ +/* */ +/* Parameters: */ +/* IoAddress = I/O Memory Address. */ +/* */ +/* Return: */ +/* An iSeries_Device_Node to the device mapped to the I/O address. The*/ +/* BarNumber and BarOffset are valid if the Device Node is returned. */ +/************************************************************************/ +extern struct iSeries_Device_Node* iSeries_xlateIoMmAddress(void* IoAddress); + +#endif /* _ISERIES_IOMMTABLE_H */ diff -Nru a/arch/ppc64/kernel/iSeries_VpdInfo.c b/arch/ppc64/kernel/iSeries_VpdInfo.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/iSeries_VpdInfo.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,317 @@ +/************************************************************************/ +/* File iSeries_vpdInfo.c created by Allan Trautman on Fri Feb 2 2001. */ +/************************************************************************/ +/* This code gets the card location of the hardware */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Feb 2, 2001 */ +/* Ported to ppc64, August 20, 2001 */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +//#include +#include +#include "pci.h" + +/************************************************/ +/* Size of Bus VPD data */ +/************************************************/ +#define BUS_VPDSIZE 1024 +/************************************************/ +/* Bus Vpd Tags */ +/************************************************/ +#define VpdEndOfDataTag 0x78 +#define VpdEndOfAreaTag 0x79 +#define VpdIdStringTag 0x82 +#define VpdVendorAreaTag 0x84 +/************************************************/ +/* Mfg Area Tags */ +/************************************************/ +#define VpdFruFlag 0x4647 // "FG" +#define VpdFruFrameId 0x4649 // "FI" +#define VpdSlotMapFormat 0x4D46 // "MF" +#define VpdAsmPartNumber 0x504E // "PN" +#define VpdFruSerial 0x534E // "SN" +#define VpdSlotMap 0x534D // "SM" + +/************************************************/ +/* Structures of the areas */ +/************************************************/ +struct MfgVpdAreaStruct { + u16 Tag; + u8 TagLength; + u8 AreaData1; + u8 AreaData2; +}; +typedef struct MfgVpdAreaStruct MfgArea; +#define MFG_ENTRY_SIZE 3 + +struct SlotMapStruct { + u8 AgentId; + u8 SecondaryAgentId; + u8 PhbId; + char CardLocation[3]; + char Parms[8]; + char Reserved[2]; +}; +typedef struct SlotMapStruct SlotMap; +#define SLOT_ENTRY_SIZE 16 + +/**************************************************************** + * * + * Bus, Card, Board, FrameId, CardLocation. * + ****************************************************************/ +LocationData* iSeries_GetLocationData(struct pci_dev* PciDev) +{ + struct iSeries_Device_Node* DevNode = (struct iSeries_Device_Node*)PciDev->sysdata; + LocationData* LocationPtr = (LocationData*)kmalloc(LOCATION_DATA_SIZE, GFP_KERNEL); + if (LocationPtr == NULL) { + printk("PCI: LocationData area allocation failed!\n"); + return NULL; + } + memset(LocationPtr,0,LOCATION_DATA_SIZE); + LocationPtr->Bus = ISERIES_BUS(DevNode); + LocationPtr->Board = DevNode->Board; + LocationPtr->FrameId = DevNode->FrameId; + LocationPtr->Card = PCI_SLOT(DevNode->DevFn); + strcpy(&LocationPtr->CardLocation[0],&DevNode->CardLocation[0]); + return LocationPtr; +} + +/************************************************************************/ +/* Formats the device information. */ +/* - Pass in pci_dev* pointer to the device. */ +/* - Pass in buffer to place the data. Danger here is the buffer must */ +/* be as big as the client says it is. Should be at least 128 bytes.*/ +/* Return will the length of the string data put in the buffer. */ +/* Format: */ +/* PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet */ +/* controller */ +/************************************************************************/ +int iSeries_Device_Information(struct pci_dev* PciDev,char* Buffer, int BufferSize) +{ + struct iSeries_Device_Node* DevNode = (struct iSeries_Device_Node*)PciDev->sysdata; + char* BufPtr = Buffer; + int LineLen = 0; + + if (DevNode == NULL) { + LineLen = sprintf(BufPtr+LineLen, "PCI: iSeries_Device_Information DevNode is NULL"); + return LineLen; + } + + if (BufferSize >= 128) { + LineLen = sprintf(BufPtr+LineLen,"PCI: Bus%3d, Device%3d, Vendor %04X ", + ISERIES_BUS(DevNode), PCI_SLOT(PciDev->devfn),PciDev->vendor); + + LineLen += sprintf(BufPtr+LineLen,"Frame%3d, Card %4s ", DevNode->FrameId,DevNode->CardLocation); + + if (pci_class_name(PciDev->class >> 8) == 0) { + LineLen += sprintf(BufPtr+LineLen,"0x%04X ",(int)(PciDev->class >> 8)); + } + else { + LineLen += sprintf(BufPtr+LineLen,"%s",pci_class_name(PciDev->class >> 8) ); + } + } + return LineLen; +} +/************************************************************************/ +/* Build a character string of the device location, Frame 1, Card C10 */ +/************************************************************************/ +int device_Location(struct pci_dev* PciDev,char* BufPtr) +{ + struct iSeries_Device_Node* DevNode = (struct iSeries_Device_Node*)PciDev->sysdata; + return sprintf(BufPtr,"PCI: Bus%3d, Device%3d, Vendor %04X, Location %s", + DevNode->DsaAddr.busNumber, + DevNode->AgentId, + DevNode->Vendor, + DevNode->Location); +} + +/*****************************************************************/ +/* Parse the Slot Area */ +/*****************************************************************/ +void iSeries_Parse_SlotArea(SlotMap* MapPtr,int MapLen, struct iSeries_Device_Node* DevNode) +{ + int SlotMapLen = MapLen; + SlotMap* SlotMapPtr = MapPtr; + /*************************************************************/ + /* Parse Slot label until we find the one requrested */ + /*************************************************************/ + while (SlotMapLen > 0) { + if (SlotMapPtr->AgentId == DevNode->AgentId ) { + /*******************************************************/ + /* If Phb wasn't found, grab the entry first one found.*/ + /*******************************************************/ + if (DevNode->PhbId == 0xff) { + DevNode->PhbId = SlotMapPtr->PhbId; + } + /**************************************************/ + /* Found it, extract the data. */ + /**************************************************/ + if (SlotMapPtr->PhbId == DevNode->PhbId ) { + memcpy(&DevNode->CardLocation,&SlotMapPtr->CardLocation,3); + DevNode->CardLocation[3] = 0; + break; + } + } + /*********************************************************/ + /* Point to the next Slot */ + /*********************************************************/ + SlotMapPtr = (SlotMap*)((char*)SlotMapPtr+SLOT_ENTRY_SIZE); + SlotMapLen -= SLOT_ENTRY_SIZE; + } +} + +/*****************************************************************/ +/* Parse the Mfg Area */ +/*****************************************************************/ +static void iSeries_Parse_MfgArea(u8* AreaData,int AreaLen, struct iSeries_Device_Node* DevNode) +{ + MfgArea* MfgAreaPtr = (MfgArea*)AreaData; + int MfgAreaLen = AreaLen; + u16 SlotMapFmt = 0; + + /*************************************************************/ + /* Parse Mfg Data */ + /*************************************************************/ + while (MfgAreaLen > 0) { + int MfgTagLen = MfgAreaPtr->TagLength; + /*******************************************************/ + /* Frame ID (FI 4649020310 ) */ + /*******************************************************/ + if (MfgAreaPtr->Tag == VpdFruFrameId) { /* FI */ + DevNode->FrameId = MfgAreaPtr->AreaData1; + } + /*******************************************************/ + /* Slot Map Format (MF 4D46020004 ) */ + /*******************************************************/ + else if (MfgAreaPtr->Tag == VpdSlotMapFormat){ /* MF */ + SlotMapFmt = (MfgAreaPtr->AreaData1*256)+(MfgAreaPtr->AreaData2); + } + /*******************************************************/ + /* Slot Map (SM 534D90 */ + /*******************************************************/ + else if (MfgAreaPtr->Tag == VpdSlotMap){ /* SM */ + SlotMap* SlotMapPtr; + if (SlotMapFmt == 0x1004) SlotMapPtr = (SlotMap*)((char*)MfgAreaPtr+MFG_ENTRY_SIZE+1); + else SlotMapPtr = (SlotMap*)((char*)MfgAreaPtr+MFG_ENTRY_SIZE); + iSeries_Parse_SlotArea(SlotMapPtr,MfgTagLen, DevNode); + } + /*********************************************************/ + /* Point to the next Mfg Area */ + /* Use defined size, sizeof give wrong answer */ + /*********************************************************/ + MfgAreaPtr = (MfgArea*)((char*)MfgAreaPtr + MfgTagLen + MFG_ENTRY_SIZE); + MfgAreaLen -= (MfgTagLen + MFG_ENTRY_SIZE); + } +} + +/*****************************************************************/ +/* Look for "BUS".. Data is not Null terminated. */ +/* PHBID of 0xFF indicates PHB was not found in VPD Data. */ +/*****************************************************************/ +static int iSeries_Parse_PhbId(u8* AreaPtr,int AreaLength) +{ + u8* PhbPtr = AreaPtr; + int DataLen = AreaLength; + char PhbId = 0xFF; + while (DataLen > 0) { + if (*PhbPtr == 'B' && *(PhbPtr+1) == 'U' && *(PhbPtr+2) == 'S') { + PhbPtr += 3; + while(*PhbPtr == ' ') ++PhbPtr; + PhbId = (*PhbPtr & 0x0F); + break; + } + ++PhbPtr; + --DataLen; + } + return PhbId; +} + +/****************************************************************/ +/* Parse out the VPD Areas */ +/****************************************************************/ +static void iSeries_Parse_Vpd(u8* VpdData, int VpdDataLen, struct iSeries_Device_Node* DevNode) +{ + u8* TagPtr = VpdData; + int DataLen = VpdDataLen-3; + /*************************************************************/ + /* Parse the Areas */ + /*************************************************************/ + while (*TagPtr != VpdEndOfAreaTag && DataLen > 0) { + int AreaLen = *(TagPtr+1) + (*(TagPtr+2)*256); + u8* AreaData = TagPtr+3; + + if (*TagPtr == VpdIdStringTag) { + DevNode->PhbId = iSeries_Parse_PhbId(AreaData,AreaLen); + } + else if (*TagPtr == VpdVendorAreaTag) { + iSeries_Parse_MfgArea(AreaData,AreaLen,DevNode); + } + /********************************************************* + * Point to next Area. + *********************************************************/ + TagPtr = AreaData + AreaLen; + DataLen -= AreaLen; + } +} + +/**************************************************************** + * iSeries_Get_Location_Code(struct iSeries_Device_Node*) * + * + ****************************************************************/ +void iSeries_Get_Location_Code(struct iSeries_Device_Node* DevNode) +{ + int BusVpdLen = 0; + u8* BusVpdPtr = (u8*)kmalloc(BUS_VPDSIZE, GFP_KERNEL); + if (BusVpdPtr == NULL) { + printk("PCI: Bus VPD Buffer allocation failure.\n"); + return; + } + BusVpdLen = HvCallPci_getBusVpd(ISERIES_BUS(DevNode),REALADDR(BusVpdPtr),BUS_VPDSIZE); + if (BusVpdLen == 0) { + kfree(BusVpdPtr); + printk("PCI: Bus VPD Buffer zero length.\n"); + return; + } + //printk("PCI: BusVpdPtr: %p, %d\n",BusVpdPtr, BusVpdLen); + /*************************************************************/ + /* Make sure this is what I think it is */ + /*************************************************************/ + if (*BusVpdPtr != VpdIdStringTag) { /*0x82 */ + printk("PCI: Bus VPD Buffer missing starting tag.\n"); + kfree(BusVpdPtr); + return; + } + /***************************************************************/ + /***************************************************************/ + iSeries_Parse_Vpd(BusVpdPtr,BusVpdLen, DevNode); + sprintf(DevNode->Location,"Frame%3d, Card %-4s",DevNode->FrameId,DevNode->CardLocation); + kfree(BusVpdPtr); +} diff -Nru a/arch/ppc64/kernel/iSeries_irq.c b/arch/ppc64/kernel/iSeries_irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/iSeries_irq.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,259 @@ +/************************************************************************/ +/* This module supports the iSeries PCI bus interrupt handling */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, December 13, 2000 by Wayne Holm */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +hw_irq_controller iSeries_IRQ_handler = { + "iSeries irq controller", + iSeries_startup_IRQ, /* startup */ + iSeries_shutdown_IRQ, /* shutdown */ + iSeries_enable_IRQ, /* enable */ + iSeries_disable_IRQ, /* disable */ + NULL, /* ack */ + iSeries_end_IRQ, /* end */ + NULL /* set_affinity */ +}; + + +struct iSeries_irqEntry { + u32 dsa; + struct iSeries_irqEntry* next; +}; + +struct iSeries_irqAnchor { + u8 valid : 1; + u8 reserved : 7; + u16 entryCount; + struct iSeries_irqEntry* head; +}; + +struct iSeries_irqAnchor iSeries_irqMap[NR_IRQS]; + +void iSeries_init_irqMap(int irq); + +/* This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c */ +void __init iSeries_init_IRQ(void) +{ + int i; + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].handler = &iSeries_IRQ_handler; + irq_desc[i].status = 0; + irq_desc[i].status |= IRQ_DISABLED; + irq_desc[i].depth = 1; + iSeries_init_irqMap(i); + } + /* Register PCI event handler and open an event path */ + PPCDBG(PPCDBG_BUSWALK,"Register PCI event handler and open an event path\n"); + XmPciLpEvent_init(); + return; +} + +/********************************************************************** + * Called by iSeries_init_IRQ + * Prevent IRQs 0 and 255 from being used. IRQ 0 appears in + * uninitialized devices. IRQ 255 appears in the PCI interrupt + * line register if a PCI error occurs, + *********************************************************************/ +void __init iSeries_init_irqMap(int irq) +{ + iSeries_irqMap[irq].valid = (irq == 0 || irq == 255)? 0 : 1; + iSeries_irqMap[irq].entryCount = 0; + iSeries_irqMap[irq].head = NULL; +} + +/* This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot */ +/* It calculates the irq value for the slot. */ +int __init iSeries_allocate_IRQ(HvBusNumber busNumber, HvSubBusNumber subBusNumber, HvAgentId deviceId) +{ + u8 idsel = (deviceId >> 4); + u8 function = deviceId & 0x0F; + int irq = ((((busNumber-1)*16 + (idsel-1)*8 + function)*9/8) % 254) + 1; + return irq; +} + +/* This is called out of iSeries_scan_slot to assign the EADS slot to its IRQ number */ +int __init iSeries_assign_IRQ(int irq, HvBusNumber busNumber, HvSubBusNumber subBusNumber, HvAgentId deviceId) +{ + int rc; + u32 dsa = (busNumber << 16) | (subBusNumber << 8) | deviceId; + struct iSeries_irqEntry* newEntry; + unsigned long flags; + + if (irq < 0 || irq >= NR_IRQS) { + return -1; + } + newEntry = kmalloc(sizeof(*newEntry), GFP_KERNEL); + if (newEntry == NULL) { + return -ENOMEM; + } + newEntry->dsa = dsa; + newEntry->next = NULL; + /******************************************************************** + * Probably not necessary to lock the irq since allocation is only + * done during buswalk, but it should not hurt anything except a + * little performance to be smp safe. + *******************************************************************/ + spin_lock_irqsave(&irq_desc[irq].lock, flags); + + if (iSeries_irqMap[irq].valid) { + /* Push the new element onto the irq stack */ + newEntry->next = iSeries_irqMap[irq].head; + iSeries_irqMap[irq].head = newEntry; + ++iSeries_irqMap[irq].entryCount; + rc = 0; + PPCDBG(PPCDBG_BUSWALK,"iSeries_assign_IRQ 0x%04X.%02X.%02X = 0x%04X\n",busNumber, subBusNumber, deviceId, irq); + } + else { + printk("PCI: Something is wrong with the iSeries_irqMap. \n"); + kfree(newEntry); + rc = -1; + } + spin_unlock_irqrestore(&irq_desc[irq].lock, flags); + return rc; +} + + +/* This is called by iSeries_activate_IRQs */ +unsigned int iSeries_startup_IRQ(unsigned int irq) +{ + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, function, mask; + for(entry=iSeries_irqMap[irq].head; entry!=NULL; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + function = deviceId & 0x0F; + /* Link the IRQ number to the bridge */ + HvCallXm_connectBusUnit(bus, subBus, deviceId, irq); + /* Unmask bridge interrupts in the FISR */ + mask = 0x01010000 << function; + HvCallPci_unmaskFisr(bus, subBus, deviceId, mask); + PPCDBG(PPCDBG_BUSWALK,"iSeries_activate_IRQ 0x%02X.%02X.%02X Irq:0x%02X\n",bus,subBus,deviceId,irq); + } + return 0; +} + +/* This is called out of iSeries_fixup to activate interrupt + * generation for usable slots */ +void __init iSeries_activate_IRQs() +{ + int irq; + unsigned long flags; + for (irq=0; irq < NR_IRQS; irq++) { + spin_lock_irqsave(&irq_desc[irq].lock, flags); + irq_desc[irq].handler->startup(irq); + spin_unlock_irqrestore(&irq_desc[irq].lock, flags); + } +} + +/* this is not called anywhere currently */ +void iSeries_shutdown_IRQ(unsigned int irq) { + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, function, mask; + + /* irq should be locked by the caller */ + + for (entry=iSeries_irqMap[irq].head; entry; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + function = deviceId & 0x0F; + /* Invalidate the IRQ number in the bridge */ + HvCallXm_connectBusUnit(bus, subBus, deviceId, 0); + /* Mask bridge interrupts in the FISR */ + mask = 0x01010000 << function; + HvCallPci_maskFisr(bus, subBus, deviceId, mask); + } + +} + +/*********************************************************** + * This will be called by device drivers (via disable_IRQ) + * to disable INTA in the bridge interrupt status register. + ***********************************************************/ +void iSeries_disable_IRQ(unsigned int irq) +{ + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, mask; + + /* The IRQ has already been locked by the caller */ + + for (entry=iSeries_irqMap[irq].head; entry; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + /* Mask secondary INTA */ + mask = 0x80000000; + HvCallPci_maskInterrupts(bus, subBus, deviceId, mask); + PPCDBG(PPCDBG_BUSWALK,"iSeries_disable_IRQ 0x%02X.%02X.%02X 0x%04X\n",bus,subBus,deviceId,irq); + } +} + +/*********************************************************** + * This will be called by device drivers (via enable_IRQ) + * to enable INTA in the bridge interrupt status register. + ***********************************************************/ +void iSeries_enable_IRQ(unsigned int irq) +{ + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, mask; + + /* The IRQ has already been locked by the caller */ + for (entry=iSeries_irqMap[irq].head; entry; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + /* Unmask secondary INTA */ + mask = 0x80000000; + HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask); + PPCDBG(PPCDBG_BUSWALK,"iSeries_enable_IRQ 0x%02X.%02X.%02X 0x%04X\n",bus,subBus,deviceId,irq); + } +} + +/* Need to define this so ppc_irq_dispatch_handler will NOT call + enable_IRQ at the end of interrupt handling. However, this + does nothing because there is not enough information provided + to do the EOI HvCall. This is done by XmPciLpEvent.c */ +void iSeries_end_IRQ(unsigned int irq) +{ +} + diff -Nru a/arch/ppc64/kernel/iSeries_pci.c b/arch/ppc64/kernel/iSeries_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/iSeries_pci.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,937 @@ +/* + * iSeries_pci.c + * + * Copyright (C) 2001 Allan Trautman, IBM Corporation + * + * iSeries specific routines for PCI. + * + * Based on code from pci.c and iSeries_pci.c 32bit + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iSeries_IoMmTable.h" +#include "pci.h" + +extern struct pci_controller* hose_head; +extern struct pci_controller** hose_tail; +extern int global_phb_number; +extern int panic_timeout; + +extern struct Naca *naca; +extern struct device_node *allnodes; +extern unsigned long phb_tce_table_init(struct pci_controller *phb); +extern unsigned long iSeries_Base_Io_Memory; + +extern struct pci_ops iSeries_pci_ops; +extern struct flightRecorder* PciFr; +extern struct TceTable* tceTables[256]; + +/******************************************************************* + * Counters and control flags. + *******************************************************************/ +extern long Pci_Io_Read_Count; +extern long Pci_Io_Write_Count; +extern long Pci_Cfg_Read_Count; +extern long Pci_Cfg_Write_Count; +extern long Pci_Error_Count; + +extern int Pci_Retry_Max; +extern int Pci_Error_Flag; +extern int Pci_Trace_Flag; + +extern void iSeries_MmIoTest(void); + + +/******************************************************************* + * Forward declares of prototypes. + *******************************************************************/ +struct iSeries_Device_Node* find_Device_Node(struct pci_dev* PciDev); +struct iSeries_Device_Node* get_Device_Node(struct pci_dev* PciDev); + +unsigned long find_and_init_phbs(void); +void fixup_resources(struct pci_dev *dev); +void iSeries_pcibios_fixup(void); +struct pci_controller* alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) ; + +void iSeries_Scan_PHBs_Slots(struct pci_controller* Phb); +void iSeries_Scan_EADs_Bridge(HvBusNumber Bus, HvSubBusNumber SubBus, int IdSel); +int iSeries_Scan_Bridge_Slot(HvBusNumber Bus, HvSubBusNumber SubBus, int MaxAgents); +void list_device_nodes(void); + +struct pci_dev; + +LIST_HEAD(Global_Device_List); + +int DeviceCount = 0; + +/********************************************************************************** + * Log Error infor in Flight Recorder to system Console. + * Filter out the device not there errors. + * PCI: EADs Connect Failed 0x18.58.10 Rc: 0x00xx + * PCI: Read Vendor Failed 0x18.58.10 Rc: 0x00xx + * PCI: Connect Bus Unit Failed 0x18.58.10 Rc: 0x00xx + **********************************************************************************/ +void pci_Log_Error(char* Error_Text, int Bus, int SubBus, int AgentId, int HvRc) +{ + if( HvRc != 0x0302) { + char ErrorString[128]; + sprintf(ErrorString,"%s Failed: 0x%02X.%02X.%02X Rc: 0x%04X",Error_Text,Bus,SubBus,AgentId,HvRc); + PCIFR(ErrorString); + printk("PCI: %s\n",ErrorString); + } +} + +/********************************************************************************** + * Dump the iSeries Temp Device Node + *<4>buswalk [swapper : - DeviceNode: 0xC000000000634300 + *<4>00. Device Node = 0xC000000000634300 + *<4> - PciDev = 0x0000000000000000 + *<4> - tDevice = 0x 17:01.00 0x1022 00 + *<4> 4. Device Node = 0xC000000000634480 + *<4> - PciDev = 0x0000000000000000 + *<4> - Device = 0x 18:38.16 Irq:0xA7 Vendor:0x1014 Flags:0x00 + *<4> - Devfn = 0xB0: 22.18 + **********************************************************************************/ +void dumpDevice_Node(struct iSeries_Device_Node* DevNode) +{ + udbg_printf("Device Node = 0x%p\n",DevNode); + udbg_printf(" - PciDev = 0x%p\n",DevNode->PciDev); + udbg_printf(" - Device = 0x%4X:%02X.%02X (0x%02X)\n", + ISERIES_BUS(DevNode), + ISERIES_SUBBUS(DevNode), + DevNode->AgentId, + DevNode->DevFn); + udbg_printf(" - DSA = 0x%04X\n",ISERIES_DSA(DevNode)>>32 ); + + udbg_printf(" = Irq:0x%02X Vendor:0x%04X Flags:0x%02X\n", + DevNode->Irq, + DevNode->Vendor, + DevNode->Flags ); + udbg_printf(" - Location = %s\n",DevNode->CardLocation); +} +/********************************************************************************** + * Walk down the device node chain + **********************************************************************************/ +void list_device_nodes(void) +{ + struct list_head* Device_Node_Ptr = Global_Device_List.next; + while(Device_Node_Ptr != &Global_Device_List) { + dumpDevice_Node( (struct iSeries_Device_Node*)Device_Node_Ptr ); + Device_Node_Ptr = Device_Node_Ptr->next; + } +} + + +/*********************************************************************** + * build_device_node(u16 Bus, int SubBus, u8 DevFn) + * + ***********************************************************************/ +struct iSeries_Device_Node* build_device_node(HvBusNumber Bus, HvSubBusNumber SubBus, int AgentId, int Function) +{ + struct iSeries_Device_Node* DeviceNode; + + PPCDBG(PPCDBG_BUSWALK,"- "__FUNCTION__" 0x%02X.%02X.%02X Function: %02X\n",Bus,SubBus,AgentId, Function); + + DeviceNode = kmalloc(sizeof(struct iSeries_Device_Node), GFP_KERNEL); + if(DeviceNode == NULL) return NULL; + + memset(DeviceNode,0,sizeof(struct iSeries_Device_Node) ); + list_add_tail(&DeviceNode->Device_List,&Global_Device_List); + /*DeviceNode->DsaAddr = ((u64)Bus<<48)+((u64)SubBus<<40)+((u64)0x10<<32); */ + ISERIES_BUS(DeviceNode) = Bus; + ISERIES_SUBBUS(DeviceNode) = SubBus; + DeviceNode->DsaAddr.deviceId = 0x10; + DeviceNode->DsaAddr.barNumber = 0; + DeviceNode->AgentId = AgentId; + DeviceNode->DevFn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(AgentId),Function ); + DeviceNode->IoRetry = 0; + iSeries_Get_Location_Code(DeviceNode); + PCIFR("Device 0x%02X.%2X, Node:0x%p ",ISERIES_BUS(DeviceNode),ISERIES_DEVFUN(DeviceNode),DeviceNode); + return DeviceNode; +} +/**************************************************************************** +* +* Allocate pci_controller(phb) initialized common variables. +* +*****************************************************************************/ +struct pci_controller* pci_alloc_pci_controllerX(char *model, enum phb_types controller_type) +{ + struct pci_controller *hose; + hose = (struct pci_controller*)kmalloc(sizeof(struct pci_controller), GFP_KERNEL); + if(hose == NULL) return NULL; + + memset(hose, 0, sizeof(struct pci_controller)); + if(strlen(model) < 8) strcpy(hose->what,model); + else memcpy(hose->what,model,7); + hose->type = controller_type; + hose->global_number = global_phb_number; + global_phb_number++; + + *hose_tail = hose; + hose_tail = &hose->next; + return hose; +} + +/**************************************************************************** + * + * unsigned int __init find_and_init_phbs(void) + * + * Description: + * This function checks for all possible system PCI host bridges that connect + * PCI buses. The system hypervisor is queried as to the guest partition + * ownership status. A pci_controller is build for any bus which is partially + * owned or fully owned by this guest partition. + ****************************************************************************/ +unsigned long __init find_and_init_phbs(void) +{ + struct pci_controller* phb; + HvBusNumber BusNumber; + + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); + + /* Check all possible buses. */ + for (BusNumber = 0; BusNumber < 256; BusNumber++) { + int RtnCode = HvCallXm_testBus(BusNumber); + if (RtnCode == 0) { + phb = pci_alloc_pci_controllerX("PHB HV", phb_type_hypervisor); + if(phb == NULL) { + printk("PCI: Allocate pci_controller failed.\n"); + PCIFR( "Allocate pci_controller failed."); + return -1; + } + phb->pci_mem_offset = phb->local_number = BusNumber; + phb->first_busno = BusNumber; + phb->last_busno = BusNumber; + phb->ops = &iSeries_pci_ops; + + PPCDBG(PPCDBG_BUSWALK, "PCI:Create iSeries pci_controller(%p), Bus: %04X\n",phb,BusNumber); + PCIFR("Create iSeries PHB controller: %04X",BusNumber); + + /***************************************************/ + /* Find and connect the devices. */ + /***************************************************/ + iSeries_Scan_PHBs_Slots(phb); + } + /* Check for Unexpected Return code, a clue that something */ + /* has gone wrong. */ + else if(RtnCode != 0x0301) { + PCIFR("Unexpected Return on Probe(0x%04X): 0x%04X",BusNumber,RtnCode); + } + + } + return 0; +} +/*********************************************************************** + * ppc64_pcibios_init + * + * Chance to initialize and structures or variable before PCI Bus walk. + * + *<4>buswalk [swapper : iSeries_pcibios_init Entry. + *<4>buswalk [swapper : IoMmTable Initialized 0xC00000000034BD30 + *<4>buswalk [swapper : find_and_init_phbs Entry + *<4>buswalk [swapper : Create iSeries pci_controller:(0xC00000001F5C7000), Bus 0x0017 + *<4>buswalk [swapper : Connect EADs: 0x17.00.12 = 0x00 + *<4>buswalk [swapper : iSeries_assign_IRQ 0x0017.00.12 = 0x0091 + *<4>buswalk [swapper : - allocate and assign IRQ 0x17.00.12 = 0x91 + *<4>buswalk [swapper : - FoundDevice: 0x17.28.10 = 0x12AE + *<4>buswalk [swapper : - build_device_node 0x17.28.12 + *<4>buswalk [swapper : iSeries_pcibios_init Exit. + ***********************************************************************/ +void iSeries_pcibios_init(void) +{ + struct pci_controller *phb; + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry.\n"); + + iSeries_IoMmTable_Initialize(); + + find_and_init_phbs(); + + /* Create the TCE Tables */ + phb = hose_head; + while(phb != NULL) { + create_pci_bus_tce_table(phb->local_number); + PCIFR("Bus 0x%04X TCE Table %p",phb->local_number,tceTables[phb->local_number] ); + phb = phb->next; + } + + + pci_assign_all_busses = 0; + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Exit.\n"); +} + +/*********************************************************************** + * iSeries_pcibios_fixup(void) + ***********************************************************************/ +void __init iSeries_pcibios_fixup(void) +{ + struct pci_dev* PciDev; + struct iSeries_Device_Node* DeviceNode; + char Buffer[256]; + int DeviceCount = 0; + + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry.\n"); + + /******************************************************/ + /* Fix up at the device node and pci_dev relationship */ + /******************************************************/ + pci_for_each_dev(PciDev) { + DeviceNode = find_Device_Node(PciDev); + if(DeviceNode != NULL) { + ++DeviceCount; + PciDev->sysdata = (void*)DeviceNode; + DeviceNode->PciDev = PciDev; + + PPCDBG(PPCDBG_BUSWALK,"PciDev 0x%p <==> DevNode 0x%p\n",PciDev,DeviceNode ); + + iSeries_allocateDeviceBars(PciDev); + + PPCDBGCALL(PPCDBG_BUSWALK,dumpPci_Dev(PciDev) ); + + iSeries_Device_Information(PciDev,Buffer, sizeof(Buffer) ); + printk("%d. %s\n",DeviceCount,Buffer); + + } else { + printk("PCI: Device Tree not found for 0x%016lX\n",(unsigned long)PciDev); + } + } + iSeries_IoMmTable_Status(); + + iSeries_activate_IRQs(); + + // This is test code. + //mf_displaySrc(0xC9000100); + //Pci_IoTest(); + // Pci_CfgIoTest(); + // mf_displaySrc(0xC9000500); + // Pci_MMIoTest(); + //mf_displaySrc(0xC9000999); +} +/*********************************************************************** + * iSeries_pcibios_fixup_bus(int Bus) + * + ***********************************************************************/ +void iSeries_pcibios_fixup_bus(struct pci_bus* PciBus) +{ + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__"(0x%04X) Entry.\n",PciBus->number); + +} +/*********************************************************************** + * find_floppy(void) + * + * Finds the default floppy device, if the system has one, and returns + * the pci_dev for the isa bridge for the floppy device. + * + * Note: On iSeries there will only be a virtual diskette. + ***********************************************************************/ +struct pci_dev* +find_floppy(void) +{ + PPCDBG(PPCDBG_BUSWALK,"- Find Floppy pci_dev.. None on iSeries.\n"); + return NULL; +} + + +/*********************************************************************** + * fixup_resources(struct pci_dev *dev) + * + ***********************************************************************/ +void fixup_resources(struct pci_dev *PciDev) +{ + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" PciDev %p\n",PciDev); +} + + +/******************************************************************************** +* Loop through each node function to find usable EADs bridges. +*********************************************************************************/ +void iSeries_Scan_PHBs_Slots(struct pci_controller* Phb) +{ + struct HvCallPci_DeviceInfo* DevInfo; + HvBusNumber Bus = Phb->local_number; /* System Bus */ + HvSubBusNumber SubBus = 0; /* EADs is always 0. */ + int HvRc = 0; + int IdSel = 1; + int MaxAgents = 8; + + DevInfo = (struct HvCallPci_DeviceInfo*)kmalloc(sizeof(struct HvCallPci_DeviceInfo), GFP_KERNEL); + if(DevInfo == NULL) return; + + /******************************************************************************** + * Probe for EADs Bridges + ********************************************************************************/ + for (IdSel=1; IdSel < MaxAgents; ++IdSel) { + HvRc = HvCallPci_getDeviceInfo(Bus, SubBus, IdSel,REALADDR(DevInfo), sizeof(struct HvCallPci_DeviceInfo)); + if (HvRc == 0) { + if(DevInfo->deviceType == HvCallPci_NodeDevice) { + iSeries_Scan_EADs_Bridge(Bus, SubBus, IdSel); + } + else printk("PCI: Invalid System Configuration(0x%02X.\n",DevInfo->deviceType); + } + else pci_Log_Error("getDeviceInfo",Bus, SubBus, IdSel,HvRc); + } + kfree(DevInfo); +} + + +/******************************************************************************** +* +*********************************************************************************/ +void iSeries_Scan_EADs_Bridge(HvBusNumber Bus, HvSubBusNumber SubBus, int IdSel) +{ + struct HvCallPci_BridgeInfo* BridgeInfo; + HvAgentId AgentId; + int Function; + int HvRc; + + BridgeInfo = (struct HvCallPci_BridgeInfo*)kmalloc(sizeof(struct HvCallPci_BridgeInfo), GFP_KERNEL); + if(BridgeInfo == NULL) return; + + /********************************************************************* + * Note: hvSubBus and irq is always be 0 at this level! + *********************************************************************/ + for (Function=0; Function < 8; ++Function) { + AgentId = ISERIES_PCI_AGENTID(IdSel, Function); + HvRc = HvCallXm_connectBusUnit(Bus, SubBus, AgentId, 0); + if (HvRc == 0) { + /* Connect EADs: 0x18.00.12 = 0x00 */ + PPCDBG(PPCDBG_BUSWALK,"PCI:Connect EADs: 0x%02X.%02X.%02X\n",Bus, SubBus, AgentId); + PCIFR( "Connect EADs: 0x%02X.%02X.%02X", Bus, SubBus, AgentId); + HvRc = HvCallPci_getBusUnitInfo(Bus, SubBus, AgentId, + REALADDR(BridgeInfo), sizeof(struct HvCallPci_BridgeInfo)); + if (HvRc == 0) { + PPCDBG(PPCDBG_BUSWALK,"PCI: BridgeInfo, Type: 0x%02X, SubBus 0x%02X, MaxAgents 0x%02X\n", + BridgeInfo->busUnitInfo.deviceType, + BridgeInfo->subBusNumber, + BridgeInfo->maxAgents); + + if (BridgeInfo->busUnitInfo.deviceType == HvCallPci_BridgeDevice) { + /* Scan_Bridge_Slot...: 0x18.00.12 */ + iSeries_Scan_Bridge_Slot(Bus,BridgeInfo->subBusNumber,BridgeInfo->maxAgents); + } + else printk("PCI: Invalid Bridge Configuration(0x%02X)",BridgeInfo->busUnitInfo.deviceType); + } + } + else if(HvRc != 0x000B) pci_Log_Error("EADs Connect",Bus,SubBus,AgentId,HvRc); + } + kfree(BridgeInfo); +} + +/******************************************************************************** +* +* This assumes that the node slot is always on the primary bus! +* +*********************************************************************************/ +int iSeries_Scan_Bridge_Slot(HvBusNumber Bus, HvSubBusNumber SubBus, int MaxAgents) +{ + struct iSeries_Device_Node* DeviceNode; + u16 VendorId = 0; + int HvRc = 0; + int Irq = 0; + int IdSel = ISERIES_GET_DEVICE_FROM_SUBBUS(SubBus); + int Function = ISERIES_GET_FUNCTION_FROM_SUBBUS(SubBus); + HvAgentId AgentId = ISERIES_PCI_AGENTID(IdSel, Function); + HvAgentId EADsIdSel = ISERIES_PCI_AGENTID(IdSel, Function); + int FirstSlotId= 0; + + /**********************************************************/ + /* iSeries_allocate_IRQ.: 0x18.00.12(0xA3) */ + /**********************************************************/ + Irq = iSeries_allocate_IRQ(Bus, 0, AgentId); + iSeries_assign_IRQ(Irq, Bus, 0, AgentId); + PPCDBG(PPCDBG_BUSWALK,"PCI:- allocate and assign IRQ 0x%02X.%02X.%02X = 0x%02X\n",Bus, 0, AgentId, Irq ); + + /**************************************************************************** + * Connect all functions of any device found. + ****************************************************************************/ + for (IdSel = 1; IdSel <= MaxAgents; ++IdSel) { + for (Function = 0; Function < 8; ++Function) { + AgentId = ISERIES_PCI_AGENTID(IdSel, Function); + HvRc = HvCallXm_connectBusUnit(Bus, SubBus, AgentId, Irq); + if( HvRc == 0) { + HvRc = HvCallPci_configLoad16(Bus, SubBus, AgentId, PCI_VENDOR_ID, &VendorId); + if( HvRc == 0) { + /**********************************************************/ + /* FoundDevice: 0x18.28.10 = 0x12AE */ + /**********************************************************/ + HvCallPci_configStore8(Bus, SubBus, AgentId, PCI_INTERRUPT_LINE, Irq); + PPCDBG(PPCDBG_BUSWALK,"PCI:- FoundDevice: 0x%02X.%02X.%02X = 0x%04X\n", + Bus, SubBus, AgentId, VendorId); + ++DeviceCount; + PCIFR("Device(%4d): 0x%02X.%02X.%02X",DeviceCount,Bus, SubBus, AgentId); + DeviceNode = build_device_node(Bus, SubBus, EADsIdSel, Function); + DeviceNode->Vendor = VendorId; + DeviceNode->Irq = Irq; + + /*********************************************************** + * On the first device/function, assign irq to slot + ***********************************************************/ + if(Function == 0) { + FirstSlotId = AgentId; + // AHT iSeries_assign_IRQ(Irq, Bus, SubBus, AgentId); + } + } + else pci_Log_Error("Read Vendor",Bus,SubBus,AgentId,HvRc); + } + else pci_Log_Error("Connect Bus Unit",Bus,SubBus, AgentId,HvRc); + } /* for (Function = 0; Function < 8; ++Function) */ + } /* for (IdSel = 1; IdSel <= MaxAgents; ++IdSel) */ + return HvRc; +} +/************************************************************************/ +/* I/0 Memory copy MUST use mmio commands on iSeries */ +/* To do; For performance, include the hv call directly */ +/************************************************************************/ +void* iSeries_memset(void* dest, char c, size_t Count) +{ + u8 ByteValue = c; + long NumberOfBytes = Count; + char* IoBuffer = dest; + while(NumberOfBytes > 0) { + iSeries_Write_Byte( ByteValue, (void*)IoBuffer ); + ++IoBuffer; + -- NumberOfBytes; + } + return dest; +} +void* iSeries_memcpy_toio(void *dest, void *source, size_t count) +{ + char *dst = dest; + char *src = source; + long NumberOfBytes = count; + while(NumberOfBytes > 0) { + iSeries_Write_Byte(*src++, (void*)dst++); + -- NumberOfBytes; + } + return dest; +} +void* iSeries_memcpy_fromio(void *dest, void *source, size_t count) +{ + char *dst = dest; + char *src = source; + long NumberOfBytes = count; + while(NumberOfBytes > 0) { + *dst++ = iSeries_Read_Byte( (void*)src++); + -- NumberOfBytes; + } + return dest; +} +/********************************************************************************** + * Look down the chain to find the matching Device Device + **********************************************************************************/ +struct iSeries_Device_Node* find_Device_Node(struct pci_dev* PciDev) +{ + struct list_head* Device_Node_Ptr = Global_Device_List.next; + int Bus = PciDev->bus->number; + int DevFn = PciDev->devfn; + + while(Device_Node_Ptr != &Global_Device_List) { + struct iSeries_Device_Node* DevNode = (struct iSeries_Device_Node*)Device_Node_Ptr; + if(Bus == ISERIES_BUS(DevNode) && DevFn == DevNode->DevFn) { + return DevNode; + } + Device_Node_Ptr = Device_Node_Ptr->next; + } + return NULL; +} +/******************************************************************/ +/* Returns the device node for the passed pci_dev */ +/* Sanity Check Node PciDev to passed pci_dev */ +/* If none is found, returns a NULL which the client must handle. */ +/******************************************************************/ +struct iSeries_Device_Node* get_Device_Node(struct pci_dev* PciDev) +{ + struct iSeries_Device_Node* Node; + Node = (struct iSeries_Device_Node*)PciDev->sysdata; + if(Node == NULL ) { + Node = find_Device_Node(PciDev); + } + else if(Node->PciDev != PciDev) { + Node = find_Device_Node(PciDev); + } + return Node; +} +/********************************************************************************** + * + * Read PCI Config Space Code + * + **********************************************************************************/ +/** BYTE *************************************************************************/ +int iSeries_Node_read_config_byte(struct iSeries_Device_Node* DevNode, int Offset, u8* ReadValue) +{ + u8 ReadData; + if(DevNode == NULL) { return 0x301; } + ++Pci_Cfg_Read_Count; + DevNode->ReturnCode = HvCallPci_configLoad8(ISERIES_BUS(DevNode),ISERIES_SUBBUS(DevNode),0x10, + Offset,&ReadData); + if(Pci_Trace_Flag == 1) { + PCIFR("RCB: 0x%04X.%02X 0x%04X = 0x%02X",ISERIES_BUS(DevNode),DevNode->DevFn,Offset,ReadData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: RCB: 0x%04X.%02X Error: 0x%04X\n",ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + PCIFR( "RCB: 0x%04X.%02X Error: 0x%04X", ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + } + *ReadValue = ReadData; + return DevNode->ReturnCode; +} +/** WORD *************************************************************************/ +int iSeries_Node_read_config_word(struct iSeries_Device_Node* DevNode, int Offset, u16* ReadValue) +{ + u16 ReadData; + if(DevNode == NULL) { return 0x301; } + ++Pci_Cfg_Read_Count; + DevNode->ReturnCode = HvCallPci_configLoad16(ISERIES_BUS(DevNode),ISERIES_SUBBUS(DevNode),0x10, + Offset,&ReadData); + if(Pci_Trace_Flag == 1) { + PCIFR("RCW: 0x%04X.%02X 0x%04X = 0x%04X",ISERIES_BUS(DevNode),DevNode->DevFn,Offset,ReadData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: RCW: 0x%04X.%02X Error: 0x%04X\n",ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + PCIFR( "RCW: 0x%04X.%02X Error: 0x%04X", ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + + } + *ReadValue = ReadData; + return DevNode->ReturnCode; +} +/** DWORD *************************************************************************/ +int iSeries_Node_read_config_dword(struct iSeries_Device_Node* DevNode, int Offset, u32* ReadValue) +{ + u32 ReadData; + if(DevNode == NULL) { return 0x301; } + ++Pci_Cfg_Read_Count; + DevNode->ReturnCode = HvCallPci_configLoad32(ISERIES_BUS(DevNode),ISERIES_SUBBUS(DevNode),0x10, + Offset,&ReadData); + if(Pci_Trace_Flag == 1) { + PCIFR("RCL: 0x%04X.%02X 0x%04X = 0x%08X",ISERIES_BUS(DevNode),DevNode->DevFn,Offset,ReadData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: RCL: 0x%04X.%02X Error: 0x%04X\n",ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + PCIFR( "RCL: 0x%04X.%02X Error: 0x%04X", ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + } + *ReadValue = ReadData; + return DevNode->ReturnCode; +} +int iSeries_pci_read_config_byte(struct pci_dev* PciDev, int Offset, u8* ReadValue) { + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_read_config_byte( DevNode ,Offset,ReadValue); +} +int iSeries_pci_read_config_word(struct pci_dev* PciDev, int Offset, u16* ReadValue) { + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_read_config_word( DevNode ,Offset,ReadValue ); +} +int iSeries_pci_read_config_dword(struct pci_dev* PciDev, int Offset, u32* ReadValue) { + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_read_config_dword(DevNode ,Offset,ReadValue ); +} +/**********************************************************************************/ +/* */ +/* Write PCI Config Space */ +/* */ +/** BYTE *************************************************************************/ +int iSeries_Node_write_config_byte(struct iSeries_Device_Node* DevNode, int Offset, u8 WriteData) +{ + ++Pci_Cfg_Write_Count; + DevNode->ReturnCode = HvCallPci_configStore8(ISERIES_BUS(DevNode),ISERIES_SUBBUS(DevNode),0x10, + Offset,WriteData); + if(Pci_Trace_Flag == 1) { + PCIFR("WCB: 0x%04X.%02X 0x%04X = 0x%02X",ISERIES_BUS(DevNode),DevNode->DevFn,Offset,WriteData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: WCB: 0x%04X.%02X Error: 0x%04X\n",ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + PCIFR( "WCB: 0x%04X.%02X Error: 0x%04X", ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + } + return DevNode->ReturnCode; +} +/** WORD *************************************************************************/ +int iSeries_Node_write_config_word(struct iSeries_Device_Node* DevNode, int Offset, u16 WriteData) +{ + ++Pci_Cfg_Write_Count; + DevNode->ReturnCode = HvCallPci_configStore16(ISERIES_BUS(DevNode),ISERIES_SUBBUS(DevNode),0x10, + Offset,WriteData); + if(Pci_Trace_Flag == 1) { + PCIFR("WCW: 0x%04X.%02X 0x%04X = 0x%04X",ISERIES_BUS(DevNode),DevNode->DevFn,Offset,WriteData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: WCW: 0x%04X.%02X Error: 0x%04X\n",ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + PCIFR( "WCW: 0x%04X.%02X Error: 0x%04X", ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + } + return DevNode->ReturnCode; +} +/** DWORD *************************************************************************/ +int iSeries_Node_write_config_dword(struct iSeries_Device_Node* DevNode, int Offset, u32 WriteData) +{ + ++Pci_Cfg_Write_Count; + DevNode->ReturnCode = HvCallPci_configStore32(ISERIES_BUS(DevNode),ISERIES_SUBBUS(DevNode),0x10, + Offset,WriteData); + if(Pci_Trace_Flag == 1) { + PCIFR("WCL: 0x%04X.%02X 0x%04X = 0x%08X",ISERIES_BUS(DevNode),DevNode->DevFn,Offset,WriteData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: WCL: 0x%04X.%02X Error: 0x%04X\n",ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + PCIFR( "WCL: 0x%04X.%02X Error: 0x%04X", ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->ReturnCode); + } + return DevNode->ReturnCode; +} +int iSeries_pci_write_config_byte( struct pci_dev* PciDev,int Offset, u8 WriteValue) +{ + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_write_config_byte( DevNode,Offset,WriteValue); +} +int iSeries_pci_write_config_word( struct pci_dev* PciDev,int Offset,u16 WriteValue) +{ + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_write_config_word( DevNode,Offset,WriteValue); +} +int iSeries_pci_write_config_dword(struct pci_dev* PciDev,int Offset,u32 WriteValue) +{ + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_write_config_dword(DevNode,Offset,WriteValue); +} + +/************************************************************************/ +/* Branch Table */ +/************************************************************************/ +struct pci_ops iSeries_pci_ops = { + iSeries_pci_read_config_byte, + iSeries_pci_read_config_word, + iSeries_pci_read_config_dword, + iSeries_pci_write_config_byte, + iSeries_pci_write_config_word, + iSeries_pci_write_config_dword +}; + +/************************************************************************ + * Check Return Code + * -> On Failure, print and log information. + * Increment Retry Count, if exceeds max, panic partition. + * -> If in retry, print and log success + ************************************************************************ + * PCI: Device 23.90 ReadL I/O Error( 0): 0x1234 + * PCI: Device 23.90 ReadL Retry( 1) + * PCI: Device 23.90 ReadL Retry Successful(1) + ************************************************************************/ +int CheckReturnCode(char* TextHdr, struct iSeries_Device_Node* DevNode, u64 RtnCode) +{ + if(RtnCode != 0) { + ++Pci_Error_Count; + ++DevNode->IoRetry; + PCIFR( "%s: Device 0x%04X:%02X I/O Error(%2d): 0x%04X", + TextHdr,ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->IoRetry,(int)RtnCode); + printk("PCI: %s: Device 0x%04X:%02X I/O Error(%2d): 0x%04X\n", + TextHdr,ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->IoRetry,(int)RtnCode); + /*******************************************************/ + /* Bump the retry and check for retry count exceeded. */ + /* If, Exceeded, panic the system. */ + /*******************************************************/ + if(DevNode->IoRetry > Pci_Retry_Max && Pci_Error_Flag > 0 ) { + mf_displaySrc(0xB6000103); + panic_timeout = 0; + panic("PCI: Hardware I/O Error, SRC B6000103, Automatic Reboot Disabled.\n"); + } + return -1; /* Retry Try */ + } + /******************************************************************** + * If retry was in progress, log success and rest retry count * + *********************************************************************/ + else if(DevNode->IoRetry > 0) { + PCIFR("%s: Device 0x%04X:%02X Retry Successful(%2d).", + TextHdr,ISERIES_BUS(DevNode),DevNode->DevFn,DevNode->IoRetry); + DevNode->IoRetry = 0; + return 0; + } + return 0; +} +/************************************************************************/ +/* Translate the I/O Address into a device node, bar, and bar offset. */ +/* Note: Make sure the passed variable end up on the stack to avoid */ +/* the exposure of being device global. */ +/************************************************************************/ +static inline struct iSeries_Device_Node* xlateIoMmAddress(void* IoAddress, + union HvDsaMap* DsaPtr, + u64* BarOffsetPtr) { + + unsigned long BaseIoAddr = (unsigned long)IoAddress-iSeries_Base_Io_Memory; + long TableIndex = BaseIoAddr/iSeries_IoMmTable_Entry_Size; + struct iSeries_Device_Node* DevNode = *(iSeries_IoMmTable +TableIndex); + if(DevNode != NULL) { + DsaPtr->DsaAddr = ISERIES_DSA(DevNode); + DsaPtr->Dsa.barNumber = *(iSeries_IoBarTable+TableIndex); + *BarOffsetPtr = BaseIoAddr % iSeries_IoMmTable_Entry_Size; + } + else { + panic("PCI: Invalid PCI IoAddress detected!\n"); + } + return DevNode; +} + +/************************************************************************/ +/* Read MM I/O Instructions for the iSeries */ +/* On MM I/O error, all ones are returned and iSeries_pci_IoError is cal*/ +/* else, data is returned in big Endian format. */ +/************************************************************************/ +/* iSeries_Read_Byte = Read Byte ( 8 bit) */ +/* iSeries_Read_Word = Read Word (16 bit) */ +/* iSeries_Read_Long = Read Long (32 bit) */ +/************************************************************************/ +u8 iSeries_Read_Byte(void* IoAddress) +{ + u64 BarOffset; + union HvDsaMap DsaData; + struct HvCallPci_LoadReturn Return; + struct iSeries_Device_Node* DevNode = xlateIoMmAddress(IoAddress,&DsaData,&BarOffset); + + do { + ++Pci_Io_Read_Count; + HvCall3Ret16(HvCallPciBarLoad8, &Return, DsaData.DsaAddr,BarOffset, 0); + } while (CheckReturnCode("RDB",DevNode, Return.rc) != 0); + + if(Pci_Trace_Flag == 1) PCIFR("RDB: IoAddress 0x%p = 0x%02X",IoAddress, (u8)Return.value); + return (u8)Return.value; +} +u16 iSeries_Read_Word(void* IoAddress) +{ + u64 BarOffset; + union HvDsaMap DsaData; + struct HvCallPci_LoadReturn Return; + struct iSeries_Device_Node* DevNode = xlateIoMmAddress(IoAddress,&DsaData,&BarOffset); + + do { + ++Pci_Io_Read_Count; + HvCall3Ret16(HvCallPciBarLoad16,&Return, DsaData.DsaAddr,BarOffset, 0); + } while (CheckReturnCode("RDW",DevNode, Return.rc) != 0); + + if(Pci_Trace_Flag == 1) PCIFR("RDW: IoAddress 0x%p = 0x%04X",IoAddress, swab16((u16)Return.value)); + return swab16((u16)Return.value); +} +u32 iSeries_Read_Long(void* IoAddress) +{ + u64 BarOffset; + union HvDsaMap DsaData; + struct HvCallPci_LoadReturn Return; + struct iSeries_Device_Node* DevNode = xlateIoMmAddress(IoAddress,&DsaData,&BarOffset); + + do { + ++Pci_Io_Read_Count; + HvCall3Ret16(HvCallPciBarLoad32,&Return, DsaData.DsaAddr,BarOffset, 0); + } while (CheckReturnCode("RDL",DevNode, Return.rc) != 0); + + if(Pci_Trace_Flag == 1) PCIFR("RDL: IoAddress 0x%p = 0x%04X",IoAddress, swab32((u32)Return.value)); + return swab32((u32)Return.value); +} +/************************************************************************/ +/* Write MM I/O Instructions for the iSeries */ +/************************************************************************/ +/* iSeries_Write_Byte = Write Byte (8 bit) */ +/* iSeries_Write_Word = Write Word(16 bit) */ +/* iSeries_Write_Long = Write Long(32 bit) */ +/************************************************************************/ +void iSeries_Write_Byte(u8 Data, void* IoAddress) +{ + u64 BarOffset; + union HvDsaMap DsaData; + struct HvCallPci_LoadReturn Return; + struct iSeries_Device_Node* DevNode = xlateIoMmAddress(IoAddress,&DsaData,&BarOffset); + + do { + ++Pci_Io_Write_Count; + Return.rc = HvCall4(HvCallPciBarStore8, DsaData.DsaAddr,BarOffset, Data, 0); + } while (CheckReturnCode("WWB",DevNode, Return.rc) != 0); + if(Pci_Trace_Flag == 1) PCIFR("WWB: IoAddress 0x%p = 0x%02X",IoAddress,Data); +} +void iSeries_Write_Word(u16 Data, void* IoAddress) +{ + u64 BarOffset; + union HvDsaMap DsaData; + struct HvCallPci_LoadReturn Return; + struct iSeries_Device_Node* DevNode = xlateIoMmAddress(IoAddress,&DsaData,&BarOffset); + + do { + ++Pci_Io_Write_Count; + Return.rc = HvCall4(HvCallPciBarStore16,DsaData.DsaAddr,BarOffset, swab16(Data), 0); + } while (CheckReturnCode("WWW",DevNode, Return.rc) != 0); + if(Pci_Trace_Flag == 1) PCIFR("WWW: IoAddress 0x%p = 0x%04X",IoAddress,Data); +} +void iSeries_Write_Long(u32 Data, void* IoAddress) +{ + u64 BarOffset; + union HvDsaMap DsaData; + struct HvCallPci_LoadReturn Return; + struct iSeries_Device_Node* DevNode = xlateIoMmAddress(IoAddress,&DsaData,&BarOffset); + + do { + ++Pci_Io_Write_Count; + Return.rc = HvCall4(HvCallPciBarStore32,DsaData.DsaAddr,BarOffset, swab32(Data), 0); + } while (CheckReturnCode("WWL",DevNode, Return.rc) != 0); + if(Pci_Trace_Flag == 1) PCIFR("WWL: IoAddress 0x%p = 0x%08X",IoAddress, Data); +} +/* + * This is called very early before the page table is setup. + * There are warnings here because of type mismatches.. Okay for now. AHT + */ +void +iSeries_pcibios_init_early(void) +{ + //ppc_md.pcibios_read_config_byte = iSeries_Node_read_config_byte; + //ppc_md.pcibios_read_config_word = iSeries_Node_read_config_word; + //ppc_md.pcibios_read_config_dword = iSeries_Node_read_config_dword; + //ppc_md.pcibios_write_config_byte = iSeries_Node_write_config_byte; + //ppc_md.pcibios_write_config_word = iSeries_Node_write_config_word; + //ppc_md.pcibios_write_config_dword = iSeries_Node_write_config_dword; +} + +/************************************************************************/ +/* Set the slot reset line to the state passed in. */ +/* This is the platform specific for code for the pci_reset_device */ +/* function. */ +/************************************************************************/ +int pci_set_reset(struct pci_dev* PciDev, int State) { + struct iSeries_Device_Node* DeviceNode = (struct iSeries_Device_Node*)PciDev->sysdata; + if (DeviceNode == NULL) { + printk("PCI: Pci Reset Failed, Device Node not found for pci_dev %p\n",PciDev); + return -1; + } + DeviceNode->ReturnCode = HvCallPci_setSlotReset(ISERIES_BUS(DeviceNode),0x00,DeviceNode->AgentId,State); + return DeviceNode->ReturnCode; +} diff -Nru a/arch/ppc64/kernel/iSeries_pci_reset.c b/arch/ppc64/kernel/iSeries_pci_reset.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/iSeries_pci_reset.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,88 @@ +/************************************************************************/ +/* File iSeries_pci_reset.c created by Allan Trautman on Mar 21 2001. */ +/************************************************************************/ +/* This code supports the pci interface on the IBM iSeries systems. */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, March 20, 2001 */ +/* April 30, 2001, Added return codes on functions. */ +/* September 10, 2001, Ported to ppc64. */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "pci.h" + +/************************************************************************/ +/* Interface to toggle the reset line */ +/* Time is in .1 seconds, need for seconds. */ +/************************************************************************/ +int iSeries_Device_ToggleReset(struct pci_dev* PciDev, int AssertTime, int DelayTime) +{ + unsigned long AssertDelay, WaitDelay; + struct iSeries_Device_Node* DeviceNode = (struct iSeries_Device_Node*)PciDev->sysdata; + if (DeviceNode == NULL) { + printk("PCI: Pci Reset Failed, Device Node not found for pci_dev %p\n",PciDev); + return -1; + } + /******************************************************************** + * Set defaults, Assert is .5 second, Wait is 3 seconds. + ********************************************************************/ + if (AssertTime == 0) AssertDelay = ( 5 * HZ)/10; + else AssertDelay = (AssertTime*HZ)/10; + if (WaitDelay == 0) WaitDelay = (30 * HZ)/10; + else WaitDelay = (DelayTime* HZ)/10; + + /******************************************************************** + * Assert reset + ********************************************************************/ + DeviceNode->ReturnCode = HvCallPci_setSlotReset(ISERIES_BUS(DeviceNode),0x00,DeviceNode->AgentId,1); + if (DeviceNode->ReturnCode == 0) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(AssertDelay); /* Sleep for the time */ + DeviceNode->ReturnCode = HvCallPci_setSlotReset(ISERIES_BUS(DeviceNode),0x00,DeviceNode->AgentId, 0); + + /*************************************************************** + * Wait for device to reset + ***************************************************************/ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(WaitDelay); + } + if (DeviceNode->ReturnCode == 0) { + PCIFR("Slot 0x%04X.%02 Reset\n",ISERIES_BUS(DeviceNode),DeviceNode->AgentId ); + } + else { + printk("PCI: Slot 0x%04X.%02X Reset Failed, RCode: %04X\n",ISERIES_BUS(DeviceNode),DeviceNode->AgentId,DeviceNode->ReturnCode); + PCIFR( "Slot 0x%04X.%02X Reset Failed, RCode: %04X\n",ISERIES_BUS(DeviceNode),DeviceNode->AgentId,DeviceNode->ReturnCode); + } + return DeviceNode->ReturnCode; +} diff -Nru a/arch/ppc64/kernel/iSeries_proc.c b/arch/ppc64/kernel/iSeries_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/iSeries_proc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,142 @@ +/* + * iSeries_proc.c + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#include +#include +#ifndef _ISERIES_PROC_H +#include +#endif + + +static struct proc_dir_entry * iSeries_proc_root = NULL; +static int iSeries_proc_initializationDone = 0; +static spinlock_t iSeries_proc_lock; + +struct iSeries_proc_registration +{ + struct iSeries_proc_registration *next; + iSeriesProcFunction functionMember; +}; + + +struct iSeries_proc_registration preallocated[16]; +#define MYQUEUETYPE(T) struct MYQueue##T +#define MYQUEUE(T) \ +MYQUEUETYPE(T) \ +{ \ + struct T *head; \ + struct T *tail; \ +} +#define MYQUEUECTOR(q) do { (q)->head = NULL; (q)->tail = NULL; } while(0) +#define MYQUEUEENQ(q, p) \ +do { \ + (p)->next = NULL; \ + if ((q)->head != NULL) { \ + (q)->head->next = (p); \ + (q)->head = (p); \ + } else { \ + (q)->tail = (q)->head = (p); \ + } \ +} while(0) + +#define MYQUEUEDEQ(q,p) \ +do { \ + (p) = (q)->tail; \ + if ((p) != NULL) { \ + (q)->tail = (p)->next; \ + (p)->next = NULL; \ + } \ + if ((q)->tail == NULL) \ + (q)->head = NULL; \ +} while(0) +MYQUEUE(iSeries_proc_registration); +typedef MYQUEUETYPE(iSeries_proc_registration) aQueue; + + +aQueue iSeries_free; +aQueue iSeries_queued; + +void iSeries_proc_early_init(void) +{ + int i = 0; + unsigned long flags; + iSeries_proc_initializationDone = 0; + spin_lock_init(&iSeries_proc_lock); + MYQUEUECTOR(&iSeries_free); + MYQUEUECTOR(&iSeries_queued); + + spin_lock_irqsave(&iSeries_proc_lock, flags); + for (i = 0; i < 16; ++i) { + MYQUEUEENQ(&iSeries_free, preallocated+i); + } + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + +void iSeries_proc_create(void) +{ + unsigned long flags; + struct iSeries_proc_registration *reg = NULL; + spin_lock_irqsave(&iSeries_proc_lock, flags); + printk("iSeries_proc: Creating /proc/iSeries\n"); + + iSeries_proc_root = proc_mkdir("iSeries", 0); + if (!iSeries_proc_root) return; + + MYQUEUEDEQ(&iSeries_queued, reg); + + while (reg != NULL) { + (*(reg->functionMember))(iSeries_proc_root); + + MYQUEUEDEQ(&iSeries_queued, reg); + } + + iSeries_proc_initializationDone = 1; + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + +void iSeries_proc_callback(iSeriesProcFunction initFunction) +{ + unsigned long flags; + spin_lock_irqsave(&iSeries_proc_lock, flags); + + if (iSeries_proc_initializationDone) { + (*initFunction)(iSeries_proc_root); + } else { + struct iSeries_proc_registration *reg = NULL; + + MYQUEUEDEQ(&iSeries_free, reg); + + if (reg != NULL) { + /* printk("Registering %p in reg %p\n", initFunction, reg); */ + reg->functionMember = initFunction; + + MYQUEUEENQ(&iSeries_queued, reg); + } else { + printk("Couldn't get a queue entry\n"); + } + } + + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + + diff -Nru a/arch/ppc64/kernel/iSeries_setup.c b/arch/ppc64/kernel/iSeries_setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/iSeries_setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,884 @@ +/* + * + * + * Copyright (c) 2000 Mike Corrigan + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: iSeries_setup.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM iSeries LPAR. Adapted from original code by Grant Erickson and + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "iSeries_setup.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Function Prototypes */ + +extern void abort(void); +#ifdef CONFIG_PPC_ISERIES +static void build_iSeries_Memory_Map( void ); +static void setup_iSeries_cache_sizes( void ); +static void iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr); +#endif +void build_valid_hpte( unsigned long vsid, unsigned long ea, unsigned long pa, + pte_t * ptep, unsigned hpteflags, unsigned bolted ); +extern void ppcdbg_initialize(void); +extern void iSeries_pcibios_init(void); +extern void iSeries_pcibios_fixup(void); +extern void iSeries_pcibios_fixup_bus(int); +static void iSeries_setup_dprofile(void); + +/* Global Variables */ + +static unsigned long procFreqHz = 0; +static unsigned long procFreqMhz = 0; +static unsigned long procFreqMhzHundreths = 0; + +static unsigned long tbFreqHz = 0; +static unsigned long tbFreqMhz = 0; +static unsigned long tbFreqMhzHundreths = 0; + +unsigned long dprof_shift = 0; +unsigned long dprof_len = 0; +unsigned int * dprof_buffer = NULL; + +int piranha_simulator = 0; + +extern char _end[]; + +extern struct Naca *naca; +extern int rd_size; /* Defined in drivers/block/rd.c */ +extern unsigned long klimit; +extern unsigned long embedded_sysmap_start; +extern unsigned long embedded_sysmap_end; + +extern unsigned long iSeries_recal_tb; +extern unsigned long iSeries_recal_titan; + +extern char _stext; +extern char _etext; + +static int mf_initialized = 0; + +struct MemoryBlock { + unsigned long absStart; + unsigned long absEnd; + unsigned long logicalStart; + unsigned long logicalEnd; +}; + +/* + * Process the main store vpd to determine where the holes in memory are + * and return the number of physical blocks and fill in the array of + * block data. + */ + +unsigned long iSeries_process_Condor_mainstore_vpd( struct MemoryBlock *mb_array, unsigned long max_entries ) +{ + /* Determine if absolute memory has any + * holes so that we can interpret the + * access map we get back from the hypervisor + * correctly. + */ + + unsigned long holeFirstChunk, holeSizeChunks; + unsigned long numMemoryBlocks = 1; + struct IoHriMainStoreSegment4 * msVpd = (struct IoHriMainStoreSegment4 *)xMsVpd; + unsigned long holeStart = msVpd->nonInterleavedBlocksStartAdr; + unsigned long holeEnd = msVpd->nonInterleavedBlocksEndAdr; + unsigned long holeSize = holeEnd - holeStart; + + printk("Mainstore_VPD: Condor\n"); + + mb_array[0].logicalStart = 0; + mb_array[0].logicalEnd = 0x100000000; + mb_array[0].absStart = 0; + mb_array[0].absEnd = 0x100000000; + + if ( holeSize ) { + numMemoryBlocks = 2; + holeStart = holeStart & 0x000fffffffffffff; + holeStart = addr_to_chunk(holeStart); + holeFirstChunk = holeStart; + holeSize = addr_to_chunk(holeSize); + holeSizeChunks = holeSize; + printk( "Main store hole: start chunk = %0lx, size = %0lx chunks\n", + holeFirstChunk, holeSizeChunks ); + mb_array[0].logicalEnd = holeFirstChunk; + mb_array[0].absEnd = holeFirstChunk; + mb_array[1].logicalStart = holeFirstChunk; + mb_array[1].logicalEnd = 0x100000000 - holeSizeChunks; + mb_array[1].absStart = holeFirstChunk + holeSizeChunks; + mb_array[1].absEnd = 0x100000000; + } + + + return numMemoryBlocks; +} + +#define MaxSegmentAreas 32 +#define MaxSegmentAdrRangeBlocks 128 +#define MaxAreaRangeBlocks 4 +unsigned long iSeries_process_Regatta_mainstore_vpd( struct MemoryBlock *mb_array, unsigned long max_entries ) +{ + struct IoHriMainStoreSegment5 * msVpdP = (struct IoHriMainStoreSegment5 *)xMsVpd; + unsigned long numSegmentBlocks = 0; + u32 existsBits = msVpdP->msAreaExists; + unsigned long area_num; + + printk("Mainstore_VPD: Regatta\n"); + + for ( area_num = 0; area_num < MaxSegmentAreas; ++area_num ) { + unsigned long numAreaBlocks; + struct IoHriMainStoreArea4 * currentArea; + + if ( existsBits & 0x80000000 ) { + unsigned long block_num; + + currentArea = &msVpdP->msAreaArray[area_num]; + numAreaBlocks = currentArea->numAdrRangeBlocks; + + printk("ms_vpd: processing area %2ld blocks=%ld", area_num, numAreaBlocks); + + for ( block_num = 0; block_num < numAreaBlocks; ++block_num ) { + /* Process an address range block */ + struct MemoryBlock tempBlock; + unsigned long i; + + tempBlock.absStart = (unsigned long)currentArea->xAdrRangeBlock[block_num].blockStart; + tempBlock.absEnd = (unsigned long)currentArea->xAdrRangeBlock[block_num].blockEnd; + tempBlock.logicalStart = 0; + tempBlock.logicalEnd = 0; + + printk("\n block %ld absStart=%016lx absEnd=%016lx", block_num, + tempBlock.absStart, tempBlock.absEnd); + + for ( i=0; i 1 ) { + unsigned long m, n; + for ( m=0; mxRamDisk ) { + initrd_start = (unsigned long)__va(naca->xRamDisk); + initrd_end = initrd_start + naca->xRamDiskSize * PAGE_SIZE; + initrd_below_start_ok = 1; // ramdisk in kernel space + ROOT_DEV = MKDEV( RAMDISK_MAJOR, 0 ); + + if ( ((rd_size*1024)/PAGE_SIZE) < naca->xRamDiskSize ) + rd_size = (naca->xRamDiskSize*PAGE_SIZE)/1024; + } else + +#endif /* CONFIG_BLK_DEV_INITRD */ + { + + /* ROOT_DEV = MKDEV( VIODASD_MAJOR, 1 ); */ + } + + iSeries_recal_tb = get_tb(); + iSeries_recal_titan = HvCallXm_loadTod(); + + ppc_md.setup_arch = iSeries_setup_arch; + ppc_md.setup_residual = iSeries_setup_residual; + ppc_md.get_cpuinfo = iSeries_get_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = iSeries_init_IRQ; + ppc_md.init_ras_IRQ = NULL; + ppc_md.get_irq = iSeries_get_irq; + ppc_md.init = NULL; + + ppc_md.pcibios_fixup = iSeries_pcibios_fixup; + ppc_md.pcibios_fixup_bus = iSeries_pcibios_fixup_bus; + + ppc_md.restart = iSeries_restart; + ppc_md.power_off = iSeries_power_off; + ppc_md.halt = iSeries_halt; + + ppc_md.time_init = NULL; + ppc_md.get_boot_time = iSeries_get_boot_time; + ppc_md.set_rtc_time = iSeries_set_rtc_time; + ppc_md.get_rtc_time = iSeries_get_rtc_time; + ppc_md.calibrate_decr = iSeries_calibrate_decr; + ppc_md.progress = iSeries_progress; + + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; + +#if defined(CONFIG_MAGIC_SYSRQ) + ppc_md.ppc_kbd_sysrq_xlate = NULL; +#endif + + hpte_init_iSeries(); + tce_init_iSeries(); + + /* Initialize the table which translate Linux physical addresses to + * AS/400 absolute addresses + */ + + build_iSeries_Memory_Map(); + + setup_iSeries_cache_sizes(); + + /* Initialize machine-dependency vectors */ + + +#ifdef CONFIG_SMP + smp_init_iSeries(); +#endif + + if ( itLpNaca.xPirEnvironMode == 0 ) + piranha_simulator = 1; +#endif +} + +/* + * void __init iSeries_init() + */ + +void __init +iSeries_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* Associate Lp Event Queue 0 with processor 0 */ + HvCallEvent_setLpEventQueueInterruptProc( 0, 0 ); + + { + /* copy the command line parameter from the primary VSP */ + char *p, *q; + HvCallEvent_dmaToSp( cmd_line, + 2*64*1024, + 256, + HvLpDma_Direction_RemoteToLocal ); + + p = q = cmd_line + 255; + while( p > cmd_line ) { + if ((*p == 0) || (*p == ' ') || (*p == '\n')) + --p; + else + break; + } + if ( p < q ) + *(p+1) = 0; + } + + if (strstr(cmd_line, "dprofile=")) { + char *p, *q; + + for (q = cmd_line; (p = strstr(q, "dprofile=")) != 0; ) { + unsigned long size, new_klimit; + q = p + 9; + if (p > cmd_line && p[-1] != ' ') + continue; + dprof_shift = simple_strtoul(q, &q, 0); + dprof_len = (unsigned long)&_etext - (unsigned long)&_stext; + dprof_len >>= dprof_shift; + size = ((dprof_len * sizeof(unsigned int)) + (PAGE_SIZE-1)) & PAGE_MASK; + dprof_buffer = (unsigned int *)((klimit + (PAGE_SIZE-1)) & PAGE_MASK); + new_klimit = ((unsigned long)dprof_buffer) + size; + lmb_reserve( __pa(klimit), (new_klimit-klimit)); + klimit = new_klimit; + memset( dprof_buffer, 0, size ); + } + } + + iSeries_setup_dprofile(); + + iSeries_proc_early_init(); + mf_init(); + mf_initialized = 1; + mb(); + + iSeries_proc_callback( &pmc_proc_init ); +} + +#ifdef CONFIG_PPC_ISERIES +/* + * The iSeries may have very large memories ( > 128 GB ) and a partition + * may get memory in "chunks" that may be anywhere in the 2**52 real + * address space. The chunks are 256K in size. To map this to the + * memory model Linux expects, the AS/400 specific code builds a + * translation table to translate what Linux thinks are "physical" + * addresses to the actual real addresses. This allows us to make + * it appear to Linux that we have contiguous memory starting at + * physical address zero while in fact this could be far from the truth. + * To avoid confusion, I'll let the words physical and/or real address + * apply to the Linux addresses while I'll use "absolute address" to + * refer to the actual hardware real address. + * + * build_iSeries_Memory_Map gets information from the Hypervisor and + * looks at the Main Store VPD to determine the absolute addresses + * of the memory that has been assigned to our partition and builds + * a table used to translate Linux's physical addresses to these + * absolute addresses. Absolute addresses are needed when + * communicating with the hypervisor (e.g. to build HPT entries) + */ + +static void __init build_iSeries_Memory_Map(void) +{ + u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize; + u32 nextPhysChunk; + u32 hptFirstChunk, hptLastChunk, hptSizeChunks, hptSizePages; + u32 num_ptegs; + u32 totalChunks,moreChunks; + u32 currChunk, thisChunk, absChunk; + u32 currDword; + u32 chunkBit; + u64 map; + struct MemoryBlock mb[32]; + unsigned long numMemoryBlocks, curBlock; + + /* Chunk size on iSeries is 256K bytes */ + totalChunks = (u32)HvLpConfig_getMsChunks(); + klimit = msChunks_alloc(klimit, totalChunks, 1UL<<18); + + /* Get absolute address of our load area + * and map it to physical address 0 + * This guarantees that the loadarea ends up at physical 0 + * otherwise, it might not be returned by PLIC as the first + * chunks + */ + + loadAreaFirstChunk = (u32)addr_to_chunk(itLpNaca.xLoadAreaAddr); + loadAreaSize = itLpNaca.xLoadAreaChunks; + + /* Only add the pages already mapped here. + * Otherwise we might add the hpt pages + * The rest of the pages of the load area + * aren't in the HPT yet and can still + * be assigned an arbitrary physical address + */ + if ( (loadAreaSize * 64) > HvPagesToMap ) + loadAreaSize = HvPagesToMap / 64; + + loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; + + /* TODO Do we need to do something if the HPT is in the 64MB load area? + * This would be required if the itLpNaca.xLoadAreaChunks includes + * the HPT size + */ + + printk( "Mapping load area - physical addr = 0000000000000000\n" + " absolute addr = %016lx\n", + chunk_to_addr(loadAreaFirstChunk) ); + printk( "Load area size %dK\n", loadAreaSize*256 ); + + for ( nextPhysChunk = 0; + nextPhysChunk < loadAreaSize; + ++nextPhysChunk ) { + msChunks.abs[nextPhysChunk] = loadAreaFirstChunk+nextPhysChunk; + } + + /* Get absolute address of our HPT and remember it so + * we won't map it to any physical address + */ + + hptFirstChunk = (u32)addr_to_chunk(HvCallHpt_getHptAddress()); + hptSizePages = (u32)(HvCallHpt_getHptPages()); + hptSizeChunks = hptSizePages >> (msChunks.chunk_shift-PAGE_SHIFT); + hptLastChunk = hptFirstChunk + hptSizeChunks - 1; + + printk( "HPT absolute addr = %016lx, size = %dK\n", + chunk_to_addr(hptFirstChunk), hptSizeChunks*256 ); + + /* Fill in the htab_data structure */ + + /* Fill in size of hashed page table */ + num_ptegs = hptSizePages * (PAGE_SIZE/(sizeof(HPTE)*HPTES_PER_GROUP)); + htab_data.htab_num_ptegs = num_ptegs; + htab_data.htab_hash_mask = num_ptegs - 1; + + /* The actual hashed page table is in the hypervisor, we have no direct access */ + htab_data.htab = NULL; + + /* Determine if absolute memory has any + * holes so that we can interpret the + * access map we get back from the hypervisor + * correctly. + */ + numMemoryBlocks = iSeries_process_mainstore_vpd( mb, 32 ); + + /* Process the main store access map from the hypervisor + * to build up our physical -> absolute translation table + */ + curBlock = 0; + currChunk = 0; + currDword = 0; + moreChunks = totalChunks; + + while ( moreChunks ) { + map = HvCallSm_get64BitsOfAccessMap( itLpNaca.xLpIndex, + currDword ); + thisChunk = currChunk; + while ( map ) { + chunkBit = map >> 63; + map <<= 1; + if ( chunkBit ) { + --moreChunks; + + while ( thisChunk >= mb[curBlock].logicalEnd ) { + ++curBlock; + if ( curBlock >= numMemoryBlocks ) + panic("out of memory blocks"); + } + if ( thisChunk < mb[curBlock].logicalStart ) + panic("memory block error"); + + absChunk = mb[curBlock].absStart + ( thisChunk - mb[curBlock].logicalStart ); + + if ( ( ( absChunk < hptFirstChunk ) || + ( absChunk > hptLastChunk ) ) && + ( ( absChunk < loadAreaFirstChunk ) || + ( absChunk > loadAreaLastChunk ) ) ) { + msChunks.abs[nextPhysChunk] = absChunk; + ++nextPhysChunk; + } + } + ++thisChunk; + } + ++currDword; + currChunk += 64; + } + + /* main store size (in chunks) is + * totalChunks - hptSizeChunks + * which should be equal to + * nextPhysChunk + */ + naca->physicalMemorySize = chunk_to_addr(nextPhysChunk); + + /* Bolt kernel mappings for all of memory */ + iSeries_bolt_kernel( 0, naca->physicalMemorySize ); + + lmb_init(); + lmb_add( 0, naca->physicalMemorySize ); + lmb_reserve( 0, __pa(klimit)); + + /* + * Hardcode to GP size. I am not sure where to get this info. DRENG + */ + naca->slb_size = 64; +} + +/* + * Set up the variables that describe the cache line sizes + * for this machine. + */ + +static void __init setup_iSeries_cache_sizes(void) +{ + unsigned i,n; + naca->iCacheL1LineSize = xIoHriProcessorVpd[0].xInstCacheOperandSize; + naca->dCacheL1LineSize = xIoHriProcessorVpd[0].xDataCacheOperandSize; + naca->iCacheL1LinesPerPage = PAGE_SIZE / naca->iCacheL1LineSize; + naca->dCacheL1LinesPerPage = PAGE_SIZE / naca->dCacheL1LineSize; + i = naca->iCacheL1LineSize; + n = 0; + while ((i=(i/2))) ++n; + naca->iCacheL1LogLineSize = n; + i = naca->dCacheL1LineSize; + n = 0; + while ((i=(i/2))) ++n; + naca->dCacheL1LogLineSize = n; + + printk( "D-cache line size = %d (log = %d)\n", + (unsigned)naca->dCacheL1LineSize, + (unsigned)naca->dCacheL1LogLineSize ); + printk( "I-cache line size = %d (log = %d)\n", + (unsigned)naca->iCacheL1LineSize, + (unsigned)naca->iCacheL1LogLineSize ); + +} + +/* + * Bolt the kernel addr space into the HPT + */ + +static void __init iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr) +{ + unsigned long pa; + unsigned long mode_rw = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX; + HPTE hpte; + + for (pa=saddr; pa < eaddr ;pa+=PAGE_SIZE) { + unsigned long ea = (unsigned long)__va(pa); + unsigned long vsid = get_kernel_vsid( ea ); + unsigned long va = ( vsid << 28 ) | ( pa & 0xfffffff ); + unsigned long vpn = va >> PAGE_SHIFT; + unsigned long slot = HvCallHpt_findValid( &hpte, vpn ); + if ( hpte.dw0.dw0.v ) { + /* HPTE exists, so just bolt it */ + HvCallHpt_setSwBits( slot, 0x10, 0 ); + } else { + /* No HPTE exists, so create a new bolted one */ + build_valid_hpte(vsid, ea, pa, NULL, mode_rw, 1); + } + } +} +#endif /* CONFIG_PPC_ISERIES */ + +/* + * Document me. + */ +void __init +iSeries_setup_arch(void) +{ + void * eventStack; + + /* Setup the Lp Event Queue */ + + /* Allocate a page for the Event Stack + * The hypervisor wants the absolute real address, so + * we subtract out the KERNELBASE and add in the + * absolute real address of the kernel load area + */ + + eventStack = alloc_bootmem_pages( LpEventStackSize ); + + memset( eventStack, 0, LpEventStackSize ); + + /* Invoke the hypervisor to initialize the event stack */ + + HvCallEvent_setLpEventStack( 0, eventStack, LpEventStackSize ); + + /* Initialize fields in our Lp Event Queue */ + + xItLpQueue.xSlicEventStackPtr = (char *)eventStack; + xItLpQueue.xSlicCurEventPtr = (char *)eventStack; + xItLpQueue.xSlicLastValidEventPtr = (char *)eventStack + + (LpEventStackSize - LpEventMaxSize); + xItLpQueue.xIndex = 0; + + /* Compute processor frequency */ + procFreqHz = (((1UL<<34) * 1000000) / xIoHriProcessorVpd[0].xProcFreq ); + procFreqMhz = procFreqHz / 1000000; + procFreqMhzHundreths = (procFreqHz/10000) - (procFreqMhz*100); + + /* Compute time base frequency */ + tbFreqHz = (((1UL<<32) * 1000000) / xIoHriProcessorVpd[0].xTimeBaseFreq ); + tbFreqMhz = tbFreqHz / 1000000; + tbFreqMhzHundreths = (tbFreqHz/10000) - (tbFreqMhz*100); + + printk("Max logical processors = %d\n", + itVpdAreas.xSlicMaxLogicalProcs ); + printk("Max physical processors = %d\n", + itVpdAreas.xSlicMaxPhysicalProcs ); + printk("Processor frequency = %lu.%02lu\n", + procFreqMhz, + procFreqMhzHundreths ); + printk("Time base frequency = %lu.%02lu\n", + tbFreqMhz, + tbFreqMhzHundreths ); + printk("Processor version = %x\n", + xIoHriProcessorVpd[0].xPVR ); + +} + +/* + * int iSeries_setup_residual() + * + * Description: + * This routine pretty-prints CPU information gathered from the VPD + * for use in /proc/cpuinfo + * + * Input(s): + * *buffer - Buffer into which CPU data is to be printed. + * + * Output(s): + * *buffer - Buffer with CPU data. + * + * Returns: + * The number of bytes copied into 'buffer' if OK, otherwise zero or less + * on error. + */ +void +iSeries_setup_residual(struct seq_file *m, unsigned long cpu_id) +{ + seq_printf(m, "clock\t\t: %lu.%02luMhz\n", procFreqMhz, + procFreqMhzHundreths); + seq_printf(m, "time base\t: %lu.%02luMHz\n", tbFreqMhz, + tbFreqMhzHundreths); + seq_printf(m, "i-cache\t\t: %d\n", naca->iCacheL1LineSize); + seq_printf(m, "d-cache\t\t: %d\n", naca->dCacheL1LineSize); +} + +void iSeries_get_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: 64-bit iSeries Logical Partition\n"); +} + +/* + * Document me. + * and Implement me. + */ +int +iSeries_get_irq(struct pt_regs *regs) +{ + /* -2 means ignore this interrupt */ + return -2; +} + +/* + * Document me. + */ +void +iSeries_restart(char *cmd) +{ + mf_reboot(); +} + +/* + * Document me. + */ +void +iSeries_power_off(void) +{ + mf_powerOff(); +} + +/* + * Document me. + */ +void +iSeries_halt(void) +{ + mf_powerOff(); +} + +/* + * Nothing to do here. + */ +void __init +iSeries_time_init(void) +{ + /* Nothing to do */ +} + +/* JDH Hack */ +unsigned long jdh_time = 0; + +extern void setup_default_decr(void); + +/* + * void __init iSeries_calibrate_decr() + * + * Description: + * This routine retrieves the internal processor frequency from the VPD, + * and sets up the kernel timer decrementer based on that value. + * + */ +void __init +iSeries_calibrate_decr(void) +{ + unsigned long freq; + unsigned long cyclesPerUsec; + unsigned long tbf; + + struct div_result divres; + + /* Compute decrementer (and TB) frequency + * in cycles/sec + */ + + tbf = xIoHriProcessorVpd[0].xTimeBaseFreq; + + freq = 0x0100000000; + freq *= 1000000; /* 2^32 * 10^6 */ + freq = freq / tbf; /* cycles / sec */ + cyclesPerUsec = freq / 1000000; /* cycles / usec */ + + /* Set the amount to refresh the decrementer by. This + * is the number of decrementer ticks it takes for + * 1/HZ seconds. + */ + + tb_ticks_per_jiffy = freq / HZ; + /* + * tb_ticks_per_sec = freq; would give better accuracy + * but tb_ticks_per_sec = tb_ticks_per_jiffy*HZ; assures + * that jiffies (and xtime) will match the time returned + * by do_gettimeofday. + */ + tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; + tb_ticks_per_usec = cyclesPerUsec; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + div128_by_32( 1024*1024, 0, tb_ticks_per_sec, &divres ); + tb_to_xs = divres.result_low; + setup_default_decr(); +} + +void __init +iSeries_progress( char * st, unsigned short code ) +{ + printk( "Progress: [%04x] - %s\n", (unsigned)code, st ); + if ( !piranha_simulator && mf_initialized ) { + if (code != 0xffff) + mf_displayProgress( code ); + else + mf_clearSrc(); + } +} + + +void iSeries_fixup_klimit(void) +{ + /* Change klimit to take into account any ram disk that may be included */ + if (naca->xRamDisk) + klimit = KERNELBASE + (u64)naca->xRamDisk + (naca->xRamDiskSize * PAGE_SIZE); + else { + /* No ram disk was included - check and see if there was an embedded system map */ + /* Change klimit to take into account any embedded system map */ + if (embedded_sysmap_end) + klimit = KERNELBASE + ((embedded_sysmap_end+4095) & 0xfffffffffffff000); + } +} + +static void iSeries_setup_dprofile(void) +{ + if ( dprof_buffer ) { + unsigned i; + for (i=0; i + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: as400_setup.h + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM AS/400 LPAR. Adapted from original code by Grant Erickson and + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __ISERIES_SETUP_H__ +#define __ISERIES_SETUP_H__ + +#include + +extern void iSeries_init_early(void); +extern void iSeries_init(unsigned long r3, + unsigned long ird_start, + unsigned long ird_end, + unsigned long cline_start, + unsigned long cline_end); +extern void iSeries_setup_arch(void); +extern void iSeries_setup_residual(struct seq_file *m, + unsigned long cpu_id); +extern void iSeries_get_cpuinfo(struct seq_file *m); +extern void iSeries_init_IRQ(void); +extern int iSeries_get_irq(struct pt_regs *regs); +extern void iSeries_restart(char *cmd); +extern void iSeries_power_off(void); +extern void iSeries_halt(void); +extern void iSeries_time_init(void); +extern void iSeries_get_boot_time(struct rtc_time *tm); +extern int iSeries_set_rtc_time(unsigned long now); +extern unsigned long iSeries_get_rtc_time(void); +extern void iSeries_calibrate_decr(void); +extern void iSeries_progress( char *, unsigned short ); + +#endif /* __ISERIES_SETUP_H__ */ diff -Nru a/arch/ppc64/kernel/idle.c b/arch/ppc64/kernel/idle.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/idle.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,141 @@ +/* + * Idle daemon for PowerPC. Idle daemon will handle any action + * that needs to be taken when the system becomes idle. + * + * Written by Cort Dougan (cort@cs.nmt.edu) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +unsigned long maxYieldTime = 0; +unsigned long minYieldTime = 0xffffffffffffffffUL; + +#ifdef CONFIG_PPC_ISERIES +static void yield_shared_processor(void) +{ + struct Paca *paca; + unsigned long tb; + unsigned long yieldTime; + + paca = (struct Paca *)mfspr(SPRG3); + HvCall_setEnabledInterrupts( HvCall_MaskIPI | + HvCall_MaskLpEvent | + HvCall_MaskLpProd | + HvCall_MaskTimeout ); + + tb = get_tb(); + /* Compute future tb value when yield should expire */ + HvCall_yieldProcessor( HvCall_YieldTimed, tb+tb_ticks_per_jiffy ); + + yieldTime = get_tb() - tb; + if ( yieldTime > maxYieldTime ) + maxYieldTime = yieldTime; + + if ( yieldTime < minYieldTime ) + minYieldTime = yieldTime; + + /* The decrementer stops during the yield. Force a fake decrementer + * here and let the timer_interrupt code sort out the actual time. + */ + paca->xLpPaca.xIntDword.xFields.xDecrInt = 1; + process_iSeries_events(); +} +#endif /* CONFIG_PPC_ISERIES */ + +int idled(void) +{ + struct Paca *paca; + long oldval; +#ifdef CONFIG_PPC_ISERIES + unsigned long CTRL; +#endif + + /* endless loop with no priority at all */ +#ifdef CONFIG_PPC_ISERIES + /* ensure iSeries run light will be out when idle */ + current->thread.flags &= ~PPC_FLAG_RUN_LIGHT; + CTRL = mfspr(CTRLF); + CTRL &= ~RUNLATCH; + mtspr(CTRLT, CTRL); +#endif + + printk("cpu %d hits idle loop\n", smp_processor_id()); + + paca = (struct Paca *)mfspr(SPRG3); + + while(1) { + if (need_resched()) { + schedule(); + check_pgt_cache(); + } + } + + for (;;) { +#ifdef CONFIG_PPC_ISERIES + if ( paca->xLpPaca.xSharedProc ) { + if ( ItLpQueue_isLpIntPending( paca->lpQueuePtr ) ) + process_iSeries_events(); + if (!need_resched()) + yield_shared_processor(); + } + else +#endif + { + if (!need_resched()) { + set_thread_flag(TIF_POLLING_NRFLAG); + while (!test_thread_flag(TIF_NEED_RESCHED)) { +#ifdef CONFIG_PPC_ISERIES + HMT_medium(); + if (ItLpQueue_isLpIntPending(paca->lpQueuePtr)) + process_iSeries_events(); +#endif + HMT_low(); + } + clear_thread_flag(TIF_POLLING_NRFLAG); + } + } + HMT_medium(); + if (need_resched()) { + schedule(); + check_pgt_cache(); + } + } + return 0; +} + +/* + * SMP entry into the idle task - calls the same thing as the + * non-smp versions. -- Cort + */ +int cpu_idle(void) +{ + idled(); + return 0; +} diff -Nru a/arch/ppc64/kernel/init_task.c b/arch/ppc64/kernel/init_task.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/init_task.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include +#include + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial thread structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); diff -Nru a/arch/ppc64/kernel/ioctl32.c b/arch/ppc64/kernel/ioctl32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/ioctl32.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,4561 @@ +/* + * ioctl32.c: Conversion between 32bit and 64bit native ioctls. + * + * Based on sparc64 ioctl32.c by: + * + * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * + * ppc64 changes: + * + * Copyright (C) 2000 Ken Aaker (kdaaker@rchland.vnet.ibm.com) + * Copyright (C) 2001 Anton Blanchard (antonb@au.ibm.com) + * + * These routines maintain argument size conversion between 32bit and 64bit + * ioctls. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +#include +#endif /* LVM */ + +#include +/* Ugly hack. */ +#undef __KERNEL__ +#include +#define __KERNEL__ +#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 + +/* Use this to get at 32-bit user passed pointers. + See sys_sparc32.c for description about these. */ +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) \ +({ unsigned long __ret; \ + __asm__ ("clrldi %0, %0, 32" \ + : "=r" (__ret) \ + : "0" (__x)); \ + __ret; \ +}) + +/* Aiee. Someone does not find a difference between int and long */ +#define EXT2_IOC32_GETFLAGS _IOR('f', 1, int) +#define EXT2_IOC32_SETFLAGS _IOW('f', 2, int) +#define EXT2_IOC32_GETVERSION _IOR('v', 1, int) +#define EXT2_IOC32_SETVERSION _IOW('v', 2, int) + +extern asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +static int w_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)arg)) + return -EFAULT; + return err; +} + +static int rw_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + if (get_user(val, (u32 *)arg)) + return -EFAULT; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)arg)) + return -EFAULT; + return err; +} + +static int do_ext2_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* These are just misnamed, they actually get/put from/to user an int */ + switch (cmd) { + case EXT2_IOC32_GETFLAGS: cmd = EXT2_IOC_GETFLAGS; break; + case EXT2_IOC32_SETFLAGS: cmd = EXT2_IOC_SETFLAGS; break; + case EXT2_IOC32_GETVERSION: cmd = EXT2_IOC_GETVERSION; break; + case EXT2_IOC32_SETVERSION: cmd = EXT2_IOC_SETVERSION; break; + } + return sys_ioctl(fd, cmd, arg); +} + +struct video_tuner32 { + s32 tuner; + u8 name[32]; + u32 rangelow, rangehigh; + u32 flags; + u16 mode, signal; +}; + +static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if (get_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __get_user(kp->name[i], &up->name[i]); + __get_user(kp->rangelow, &up->rangelow); + __get_user(kp->rangehigh, &up->rangehigh); + __get_user(kp->flags, &up->flags); + __get_user(kp->mode, &up->mode); + __get_user(kp->signal, &up->signal); + return 0; +} + +static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if (put_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __put_user(kp->name[i], &up->name[i]); + __put_user(kp->rangelow, &up->rangelow); + __put_user(kp->rangehigh, &up->rangehigh); + __put_user(kp->flags, &up->flags); + __put_user(kp->mode, &up->mode); + __put_user(kp->signal, &up->signal); + return 0; +} + +struct video_buffer32 { + /* void * */ u32 base; + s32 height, width, depth, bytesperline; +}; + +static int get_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp; + + if (get_user(tmp, &up->base)) + return -EFAULT; + kp->base = (void *) ((unsigned long)tmp); + __get_user(kp->height, &up->height); + __get_user(kp->width, &up->width); + __get_user(kp->depth, &up->depth); + __get_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp = (u32)((unsigned long)kp->base); + + if (put_user(tmp, &up->base)) + return -EFAULT; + __put_user(kp->height, &up->height); + __put_user(kp->width, &up->width); + __put_user(kp->depth, &up->depth); + __put_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +struct video_clip32 { + s32 x, y, width, height; + /* struct video_clip32 * */ u32 next; +}; + +struct video_window32 { + u32 x, y, width, height, chromakey, flags; + /* struct video_clip32 * */ u32 clips; + s32 clipcount; +}; + +static void free_kvideo_clips(struct video_window *kp) +{ + struct video_clip *cp; + + cp = kp->clips; + if (cp != NULL) + kfree(cp); +} + +static int get_video_window32(struct video_window *kp, struct video_window32 *up) +{ + struct video_clip32 *ucp; + struct video_clip *kcp; + int nclips, err, i; + u32 tmp; + + if (get_user(kp->x, &up->x)) + return -EFAULT; + __get_user(kp->y, &up->y); + __get_user(kp->width, &up->width); + __get_user(kp->height, &up->height); + __get_user(kp->chromakey, &up->chromakey); + __get_user(kp->flags, &up->flags); + __get_user(kp->clipcount, &up->clipcount); + __get_user(tmp, &up->clips); + ucp = (struct video_clip32 *)A(tmp); + kp->clips = NULL; + + nclips = kp->clipcount; + if (nclips == 0) + return 0; + + if (ucp == 0) + return -EINVAL; + + /* Peculiar interface... */ + if (nclips < 0) + nclips = VIDEO_CLIPMAP_SIZE; + + kcp = kmalloc(nclips * sizeof(struct video_clip), GFP_KERNEL); + err = -ENOMEM; + if (kcp == NULL) + goto cleanup_and_err; + + kp->clips = kcp; + for(i = 0; i < nclips; i++) { + __get_user(kcp[i].x, &ucp[i].x); + __get_user(kcp[i].y, &ucp[i].y); + __get_user(kcp[i].width, &ucp[i].width); + __get_user(kcp[i].height, &ucp[i].height); + kcp[nclips].next = NULL; + } + + return 0; + +cleanup_and_err: + free_kvideo_clips(kp); + return err; +} + +/* You get back everything except the clips... */ +static int put_video_window32(struct video_window *kp, struct video_window32 *up) +{ + if (put_user(kp->x, &up->x)) + return -EFAULT; + __put_user(kp->y, &up->y); + __put_user(kp->width, &up->width); + __put_user(kp->height, &up->height); + __put_user(kp->chromakey, &up->chromakey); + __put_user(kp->flags, &up->flags); + __put_user(kp->clipcount, &up->clipcount); + return 0; +} + +#define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32) +#define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32) +#define VIDIOCGWIN32 _IOR('v',9, struct video_window32) +#define VIDIOCSWIN32 _IOW('v',10, struct video_window32) +#define VIDIOCGFBUF32 _IOR('v',11, struct video_buffer32) +#define VIDIOCSFBUF32 _IOW('v',12, struct video_buffer32) +#define VIDIOCGFREQ32 _IOR('v',14, u32) +#define VIDIOCSFREQ32 _IOW('v',15, u32) + +static int do_video_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + union { + struct video_tuner vt; + struct video_buffer vb; + struct video_window vw; + unsigned long vx; + } karg; + mm_segment_t old_fs = get_fs(); + void *up = (void *)arg; + int err = 0; + + /* First, convert the command. */ + switch(cmd) { + case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; + case VIDIOCSWIN32: cmd = VIDIOCSWIN; break; + case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + }; + + switch(cmd) { + case VIDIOCSTUNER: + case VIDIOCGTUNER: + err = get_video_tuner32(&karg.vt, up); + break; + + case VIDIOCSWIN: + err = get_video_window32(&karg.vw, up); + break; + + case VIDIOCSFBUF: + err = get_video_buffer32(&karg.vb, up); + break; + + case VIDIOCSFREQ: + err = get_user(karg.vx, (u32 *)up); + break; + }; + if (err) + goto out; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&karg); + set_fs(old_fs); + + if (cmd == VIDIOCSWIN) + free_kvideo_clips(&karg.vw); + + if (err == 0) { + switch(cmd) { + case VIDIOCGTUNER: + err = put_video_tuner32(&karg.vt, up); + break; + + case VIDIOCGWIN: + err = put_video_window32(&karg.vw, up); + break; + + case VIDIOCGFBUF: + err = put_video_buffer32(&karg.vb, up); + break; + + case VIDIOCGFREQ: + err = put_user(((u32)karg.vx), (u32 *)up); + break; + }; + } +out: + return err; +} + +struct timeval32 { + int tv_sec; + int tv_usec; +}; + +static int do_siocgstamp(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct timeval32 *up = (struct timeval32 *)arg; + struct timeval ktv; + mm_segment_t old_fs = get_fs(); + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&ktv); + set_fs(old_fs); + if (!err) { + err = put_user(ktv.tv_sec, &up->tv_sec); + err |= __put_user(ktv.tv_usec, &up->tv_usec); + } + return err; +} + +struct ifmap32 { + u32 mem_start; + u32 mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +struct ifreq32 { +#define IFHWADDRLEN 6 +#define IFNAMSIZ 16 + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap32 ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + char ifru_newname[IFNAMSIZ]; + __kernel_caddr_t32 ifru_data; + } ifr_ifru; +}; + +struct ifconf32 { + int ifc_len; /* size of buffer */ + __kernel_caddr_t32 ifcbuf; +}; + +#ifdef CONFIG_NET +static int dev_ifname32(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct net_device *dev; + struct ifreq32 ifr32; + int err; + + if (copy_from_user(&ifr32, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + + dev = dev_get_by_index(ifr32.ifr_ifindex); + if (!dev) + return -ENODEV; + + strcpy(ifr32.ifr_name, dev->name); + + err = copy_to_user((struct ifreq32 *)arg, &ifr32, sizeof(struct ifreq32)); + return (err ? -EFAULT : 0); +} +#endif + +static int dev_ifconf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifconf32 ifc32; + struct ifconf ifc; + struct ifreq32 *ifr32; + struct ifreq *ifr; + mm_segment_t old_fs; + unsigned int i, j; + int err; + + if (copy_from_user(&ifc32, (struct ifconf32 *)arg, sizeof(struct ifconf32))) + return -EFAULT; + + if (ifc32.ifcbuf == 0) { + ifc32.ifc_len = 0; + ifc.ifc_len = 0; + ifc.ifc_buf = NULL; + } else { + ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) * + sizeof (struct ifreq); + ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL); + if (!ifc.ifc_buf) + return -ENOMEM; + } + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0; i < ifc32.ifc_len; i += sizeof (struct ifreq32)) { + if (copy_from_user(ifr++, ifr32++, sizeof (struct ifreq32))) { + kfree (ifc.ifc_buf); + return -EFAULT; + } + } + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, SIOCGIFCONF, (unsigned long)&ifc); + set_fs (old_fs); + if (!err) { + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0, j = 0; i < ifc32.ifc_len && j < ifc.ifc_len; + i += sizeof (struct ifreq32), j += sizeof (struct ifreq)) { + if (copy_to_user(ifr32++, ifr++, sizeof (struct ifreq32))) { + err = -EFAULT; + break; + } + } + if (!err) { + if (ifc32.ifcbuf == 0) { + /* Translate from 64-bit structure multiple to + * a 32-bit one. + */ + i = ifc.ifc_len; + i = ((i / sizeof(struct ifreq)) * sizeof(struct ifreq32)); + ifc32.ifc_len = i; + } else { + if (i <= ifc32.ifc_len) + ifc32.ifc_len = i; + else + ifc32.ifc_len = i - sizeof (struct ifreq32); + } + if (copy_to_user((struct ifconf32 *)arg, &ifc32, sizeof(struct ifconf32))) + err = -EFAULT; + } + } + if (ifc.ifc_buf != NULL) + kfree (ifc.ifc_buf); + return err; +} + +static int ethtool_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err, len; + u32 data, ethcmd; + + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); + if (!ifr.ifr_data) + return -EAGAIN; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + + if (get_user(ethcmd, (u32 *)A(data))) { + err = -EFAULT; + goto out; + } + switch (ethcmd) { + case ETHTOOL_GDRVINFO: len = sizeof(struct ethtool_drvinfo); break; + case ETHTOOL_GMSGLVL: + case ETHTOOL_SMSGLVL: + case ETHTOOL_GLINK: + case ETHTOOL_NWAY_RST: len = sizeof(struct ethtool_value); break; + case ETHTOOL_GREGS: { + struct ethtool_regs *regaddr = (struct ethtool_regs *)A(data); + /* darned variable size arguments */ + if (get_user(len, (u32 *)®addr->len)) { + err = -EFAULT; + goto out; + } + len += sizeof(struct ethtool_regs); + break; + } + case ETHTOOL_GSET: + case ETHTOOL_SSET: len = sizeof(struct ethtool_cmd); break; + default: + err = -EOPNOTSUPP; + goto out; + } + + if (copy_from_user(ifr.ifr_data, (char *)A(data), len)) { + err = -EFAULT; + goto out; + } + + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + u32 data; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + len = copy_to_user((char *)A(data), ifr.ifr_data, len); + if (len) + err = -EFAULT; + } + +out: + free_page((unsigned long)ifr.ifr_data); + return err; +} + +static int bond_ioctl(unsigned long fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err, len; + u32 data; + + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); + if (!ifr.ifr_data) + return -EAGAIN; + + switch (cmd) { + case SIOCBONDENSLAVE: + case SIOCBONDRELEASE: + case SIOCBONDSETHWADDR: + case SIOCBONDCHANGEACTIVE: + len = IFNAMSIZ * sizeof(char); + break; + case SIOCBONDSLAVEINFOQUERY: + len = sizeof(struct ifslave); + break; + case SIOCBONDINFOQUERY: + len = sizeof(struct ifbond); + break; + default: + err = -EINVAL; + goto out; + }; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + if (copy_from_user(ifr.ifr_data, (char *)A(data), len)) { + err = -EFAULT; + goto out; + } + + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + len = copy_to_user((char *)A(data), ifr.ifr_data, len); + if (len) + err = -EFAULT; + } + +out: + free_page((unsigned long)ifr.ifr_data); + return err; +} + +static int dev_ifsioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err; + + switch (cmd) { + case SIOCSIFMAP: + err = copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(ifr.ifr_name)); + err |= __get_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_start)); + err |= __get_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_end)); + err |= __get_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.base_addr)); + err |= __get_user(ifr.ifr_map.irq, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.irq)); + err |= __get_user(ifr.ifr_map.dma, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.dma)); + err |= __get_user(ifr.ifr_map.port, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.port)); + if (err) + return -EFAULT; + break; + default: + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + break; + } + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + switch (cmd) { + case SIOCGIFFLAGS: + case SIOCGIFMETRIC: + case SIOCGIFMTU: + case SIOCGIFMEM: + case SIOCGIFHWADDR: + case SIOCGIFINDEX: + case SIOCGIFADDR: + case SIOCGIFBRDADDR: + case SIOCGIFDSTADDR: + case SIOCGIFNETMASK: + case SIOCGIFTXQLEN: + if (copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(struct ifreq32))) + return -EFAULT; + break; + case SIOCGIFMAP: + err = copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(ifr.ifr_name)); + err |= __put_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_start)); + err |= __put_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_end)); + err |= __put_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.base_addr)); + err |= __put_user(ifr.ifr_map.irq, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.irq)); + err |= __put_user(ifr.ifr_map.dma, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.dma)); + err |= __put_user(ifr.ifr_map.port, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.port)); + if (err) + err = -EFAULT; + break; + } + } + return err; +} + +struct rtentry32 { + u32 rt_pad1; + struct sockaddr rt_dst; /* target address */ + struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */ + struct sockaddr rt_genmask; /* target network mask (IP) */ + unsigned short rt_flags; + short rt_pad2; + u32 rt_pad3; + unsigned char rt_tos; + unsigned char rt_class; + short rt_pad4; + short rt_metric; /* +1 for binary compatibility! */ + /* char * */ u32 rt_dev; /* forcing the device at add */ + u32 rt_mtu; /* per route MTU/Window */ + u32 rt_window; /* Window clamping */ + unsigned short rt_irtt; /* Initial RTT */ + +}; + +struct in6_rtmsg32 { + struct in6_addr rtmsg_dst; + struct in6_addr rtmsg_src; + struct in6_addr rtmsg_gateway; + u32 rtmsg_type; + u16 rtmsg_dst_len; + u16 rtmsg_src_len; + u32 rtmsg_metric; + u32 rtmsg_info; + u32 rtmsg_flags; + s32 rtmsg_ifindex; +}; + +extern struct socket *sockfd_lookup(int fd, int *err); + +static int routing_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + int ret; + void *r = NULL; + struct in6_rtmsg r6; + struct rtentry r4; + char devname[16]; + u32 rtdev; + mm_segment_t old_fs = get_fs(); + + struct socket *mysock = sockfd_lookup(fd, &ret); + + if (mysock && mysock->sk && mysock->sk->family == AF_INET6) { /* ipv6 */ + ret = copy_from_user (&r6.rtmsg_dst, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst), + 3 * sizeof(struct in6_addr)); + ret |= __get_user (r6.rtmsg_type, &(((struct in6_rtmsg32 *)arg)->rtmsg_type)); + ret |= __get_user (r6.rtmsg_dst_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst_len)); + ret |= __get_user (r6.rtmsg_src_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_src_len)); + ret |= __get_user (r6.rtmsg_metric, &(((struct in6_rtmsg32 *)arg)->rtmsg_metric)); + ret |= __get_user (r6.rtmsg_info, &(((struct in6_rtmsg32 *)arg)->rtmsg_info)); + ret |= __get_user (r6.rtmsg_flags, &(((struct in6_rtmsg32 *)arg)->rtmsg_flags)); + ret |= __get_user (r6.rtmsg_ifindex, &(((struct in6_rtmsg32 *)arg)->rtmsg_ifindex)); + + r = (void *) &r6; + } else { /* ipv4 */ + ret = copy_from_user (&r4.rt_dst, &(((struct rtentry32 *)arg)->rt_dst), 3 * sizeof(struct sockaddr)); + ret |= __get_user (r4.rt_flags, &(((struct rtentry32 *)arg)->rt_flags)); + ret |= __get_user (r4.rt_metric, &(((struct rtentry32 *)arg)->rt_metric)); + ret |= __get_user (r4.rt_mtu, &(((struct rtentry32 *)arg)->rt_mtu)); + ret |= __get_user (r4.rt_window, &(((struct rtentry32 *)arg)->rt_window)); + ret |= __get_user (r4.rt_irtt, &(((struct rtentry32 *)arg)->rt_irtt)); + ret |= __get_user (rtdev, &(((struct rtentry32 *)arg)->rt_dev)); + if (rtdev) { + ret |= copy_from_user (devname, (char *)A(rtdev), 15); + r4.rt_dev = devname; devname[15] = 0; + } else + r4.rt_dev = 0; + + r = (void *) &r4; + } + + if (ret) + return -EFAULT; + + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, cmd, (long) r); + set_fs (old_fs); + + return ret; +} + +struct hd_geometry32 { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + u32 start; +}; + +static int hdio_getgeo(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct hd_geometry geo; + int err; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, HDIO_GETGEO, (unsigned long)&geo); + set_fs (old_fs); + if (!err) { + err = copy_to_user ((struct hd_geometry32 *)arg, &geo, 4); + err |= __put_user (geo.start, &(((struct hd_geometry32 *)arg)->start)); + } + return err ? -EFAULT : 0; +} + + +static int hdio_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + unsigned long kval; + unsigned int *uvp; + int error; + + set_fs(KERNEL_DS); + error = sys_ioctl(fd, cmd, (long)&kval); + set_fs(old_fs); + + if (error == 0) { + uvp = (unsigned int *)arg; + if (put_user(kval, uvp)) + error = -EFAULT; + } + return error; +} + +struct floppy_struct32 { + unsigned int size; + unsigned int sect; + unsigned int head; + unsigned int track; + unsigned int stretch; + unsigned char gap; + unsigned char rate; + unsigned char spec1; + unsigned char fmt_gap; + const __kernel_caddr_t32 name; +}; + +struct floppy_drive_params32 { + char cmos; + u32 max_dtr; + u32 hlt; + u32 hut; + u32 srt; + u32 spinup; + u32 spindown; + unsigned char spindown_offset; + unsigned char select_delay; + unsigned char rps; + unsigned char tracks; + u32 timeout; + unsigned char interleave_sect; + struct floppy_max_errors max_errors; + char flags; + char read_track; + short autodetect[8]; + int checkfreq; + int native_format; +}; + +struct floppy_drive_struct32 { + signed char flags; + u32 spinup_date; + u32 select_date; + u32 first_read_date; + short probed_format; + short track; + short maxblock; + short maxtrack; + int generation; + int keep_data; + int fd_ref; + int fd_device; + int last_checked; + __kernel_caddr_t32 dmabuf; + int bufblocks; +}; + +struct floppy_fdc_state32 { + int spec1; + int spec2; + int dtr; + unsigned char version; + unsigned char dor; + u32 address; + unsigned int rawcmd:2; + unsigned int reset:1; + unsigned int need_configure:1; + unsigned int perp_mode:2; + unsigned int has_fifo:1; + unsigned int driver_version; + unsigned char track[4]; +}; + +struct floppy_write_errors32 { + unsigned int write_errors; + u32 first_error_sector; + int first_error_generation; + u32 last_error_sector; + int last_error_generation; + unsigned int badness; +}; + +#define FDSETPRM32 _IOW(2, 0x42, struct floppy_struct32) +#define FDDEFPRM32 _IOW(2, 0x43, struct floppy_struct32) +#define FDGETPRM32 _IOR(2, 0x04, struct floppy_struct32) +#define FDSETDRVPRM32 _IOW(2, 0x90, struct floppy_drive_params32) +#define FDGETDRVPRM32 _IOR(2, 0x11, struct floppy_drive_params32) +#define FDGETDRVSTAT32 _IOR(2, 0x12, struct floppy_drive_struct32) +#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct floppy_drive_struct32) +#define FDGETFDCSTAT32 _IOR(2, 0x15, struct floppy_fdc_state32) +#define FDWERRORGET32 _IOR(2, 0x17, struct floppy_write_errors32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} fd_ioctl_trans_table[] = { + { FDSETPRM32, FDSETPRM }, + { FDDEFPRM32, FDDEFPRM }, + { FDGETPRM32, FDGETPRM }, + { FDSETDRVPRM32, FDSETDRVPRM }, + { FDGETDRVPRM32, FDGETDRVPRM }, + { FDGETDRVSTAT32, FDGETDRVSTAT }, + { FDPOLLDRVSTAT32, FDPOLLDRVSTAT }, + { FDGETFDCSTAT32, FDGETFDCSTAT }, + { FDWERRORGET32, FDWERRORGET } +}; + +#define NR_FD_IOCTL_TRANS (sizeof(fd_ioctl_trans_table)/sizeof(fd_ioctl_trans_table[0])) + +static int fd_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + void *karg = NULL; + unsigned int kcmd = 0; + int i, err; + + for (i = 0; i < NR_FD_IOCTL_TRANS; i++) + if (cmd == fd_ioctl_trans_table[i].cmd32) { + kcmd = fd_ioctl_trans_table[i].cmd; + break; + } + if (!kcmd) + return -EINVAL; + + switch (cmd) { + case FDSETPRM32: + case FDDEFPRM32: + case FDGETPRM32: + { + struct floppy_struct *f; + + f = karg = kmalloc(sizeof(struct floppy_struct), GFP_KERNEL); + if (!karg) + return -ENOMEM; + if (cmd == FDGETPRM32) + break; + err = __get_user(f->size, &((struct floppy_struct32 *)arg)->size); + err |= __get_user(f->sect, &((struct floppy_struct32 *)arg)->sect); + err |= __get_user(f->head, &((struct floppy_struct32 *)arg)->head); + err |= __get_user(f->track, &((struct floppy_struct32 *)arg)->track); + err |= __get_user(f->stretch, &((struct floppy_struct32 *)arg)->stretch); + err |= __get_user(f->gap, &((struct floppy_struct32 *)arg)->gap); + err |= __get_user(f->rate, &((struct floppy_struct32 *)arg)->rate); + err |= __get_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); + err |= __get_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); + err |= __get_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + if (err) { + err = -EFAULT; + goto out; + } + break; + } + case FDSETDRVPRM32: + case FDGETDRVPRM32: + { + struct floppy_drive_params *f; + + f = karg = kmalloc(sizeof(struct floppy_drive_params), GFP_KERNEL); + if (!karg) + return -ENOMEM; + if (cmd == FDGETDRVPRM32) + break; + err = __get_user(f->cmos, &((struct floppy_drive_params32 *)arg)->cmos); + err |= __get_user(f->max_dtr, &((struct floppy_drive_params32 *)arg)->max_dtr); + err |= __get_user(f->hlt, &((struct floppy_drive_params32 *)arg)->hlt); + err |= __get_user(f->hut, &((struct floppy_drive_params32 *)arg)->hut); + err |= __get_user(f->srt, &((struct floppy_drive_params32 *)arg)->srt); + err |= __get_user(f->spinup, &((struct floppy_drive_params32 *)arg)->spinup); + err |= __get_user(f->spindown, &((struct floppy_drive_params32 *)arg)->spindown); + err |= __get_user(f->spindown_offset, &((struct floppy_drive_params32 *)arg)->spindown_offset); + err |= __get_user(f->select_delay, &((struct floppy_drive_params32 *)arg)->select_delay); + err |= __get_user(f->rps, &((struct floppy_drive_params32 *)arg)->rps); + err |= __get_user(f->tracks, &((struct floppy_drive_params32 *)arg)->tracks); + err |= __get_user(f->timeout, &((struct floppy_drive_params32 *)arg)->timeout); + err |= __get_user(f->interleave_sect, &((struct floppy_drive_params32 *)arg)->interleave_sect); + err |= __copy_from_user(&f->max_errors, &((struct floppy_drive_params32 *)arg)->max_errors, sizeof(f->max_errors)); + err |= __get_user(f->flags, &((struct floppy_drive_params32 *)arg)->flags); + err |= __get_user(f->read_track, &((struct floppy_drive_params32 *)arg)->read_track); + err |= __copy_from_user(f->autodetect, ((struct floppy_drive_params32 *)arg)->autodetect, sizeof(f->autodetect)); + err |= __get_user(f->checkfreq, &((struct floppy_drive_params32 *)arg)->checkfreq); + err |= __get_user(f->native_format, &((struct floppy_drive_params32 *)arg)->native_format); + if (err) { + err = -EFAULT; + goto out; + } + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + karg = kmalloc(sizeof(struct floppy_drive_struct), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + case FDGETFDCSTAT32: + karg = kmalloc(sizeof(struct floppy_fdc_state), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + case FDWERRORGET32: + karg = kmalloc(sizeof(struct floppy_write_errors), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + default: + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case FDGETPRM32: + { + struct floppy_struct *f = karg; + + err = __put_user(f->size, &((struct floppy_struct32 *)arg)->size); + err |= __put_user(f->sect, &((struct floppy_struct32 *)arg)->sect); + err |= __put_user(f->head, &((struct floppy_struct32 *)arg)->head); + err |= __put_user(f->track, &((struct floppy_struct32 *)arg)->track); + err |= __put_user(f->stretch, &((struct floppy_struct32 *)arg)->stretch); + err |= __put_user(f->gap, &((struct floppy_struct32 *)arg)->gap); + err |= __put_user(f->rate, &((struct floppy_struct32 *)arg)->rate); + err |= __put_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); + err |= __put_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); + err |= __put_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + break; + } + case FDGETDRVPRM32: + { + struct floppy_drive_params *f = karg; + + err = __put_user(f->cmos, &((struct floppy_drive_params32 *)arg)->cmos); + err |= __put_user(f->max_dtr, &((struct floppy_drive_params32 *)arg)->max_dtr); + err |= __put_user(f->hlt, &((struct floppy_drive_params32 *)arg)->hlt); + err |= __put_user(f->hut, &((struct floppy_drive_params32 *)arg)->hut); + err |= __put_user(f->srt, &((struct floppy_drive_params32 *)arg)->srt); + err |= __put_user(f->spinup, &((struct floppy_drive_params32 *)arg)->spinup); + err |= __put_user(f->spindown, &((struct floppy_drive_params32 *)arg)->spindown); + err |= __put_user(f->spindown_offset, &((struct floppy_drive_params32 *)arg)->spindown_offset); + err |= __put_user(f->select_delay, &((struct floppy_drive_params32 *)arg)->select_delay); + err |= __put_user(f->rps, &((struct floppy_drive_params32 *)arg)->rps); + err |= __put_user(f->tracks, &((struct floppy_drive_params32 *)arg)->tracks); + err |= __put_user(f->timeout, &((struct floppy_drive_params32 *)arg)->timeout); + err |= __put_user(f->interleave_sect, &((struct floppy_drive_params32 *)arg)->interleave_sect); + err |= __copy_to_user(&((struct floppy_drive_params32 *)arg)->max_errors, &f->max_errors, sizeof(f->max_errors)); + err |= __put_user(f->flags, &((struct floppy_drive_params32 *)arg)->flags); + err |= __put_user(f->read_track, &((struct floppy_drive_params32 *)arg)->read_track); + err |= __copy_to_user(((struct floppy_drive_params32 *)arg)->autodetect, f->autodetect, sizeof(f->autodetect)); + err |= __put_user(f->checkfreq, &((struct floppy_drive_params32 *)arg)->checkfreq); + err |= __put_user(f->native_format, &((struct floppy_drive_params32 *)arg)->native_format); + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + { + struct floppy_drive_struct *f = karg; + + err = __put_user(f->flags, &((struct floppy_drive_struct32 *)arg)->flags); + err |= __put_user(f->spinup_date, &((struct floppy_drive_struct32 *)arg)->spinup_date); + err |= __put_user(f->select_date, &((struct floppy_drive_struct32 *)arg)->select_date); + err |= __put_user(f->first_read_date, &((struct floppy_drive_struct32 *)arg)->first_read_date); + err |= __put_user(f->probed_format, &((struct floppy_drive_struct32 *)arg)->probed_format); + err |= __put_user(f->track, &((struct floppy_drive_struct32 *)arg)->track); + err |= __put_user(f->maxblock, &((struct floppy_drive_struct32 *)arg)->maxblock); + err |= __put_user(f->maxtrack, &((struct floppy_drive_struct32 *)arg)->maxtrack); + err |= __put_user(f->generation, &((struct floppy_drive_struct32 *)arg)->generation); + err |= __put_user(f->keep_data, &((struct floppy_drive_struct32 *)arg)->keep_data); + err |= __put_user(f->fd_ref, &((struct floppy_drive_struct32 *)arg)->fd_ref); + err |= __put_user(f->fd_device, &((struct floppy_drive_struct32 *)arg)->fd_device); + err |= __put_user(f->last_checked, &((struct floppy_drive_struct32 *)arg)->last_checked); + err |= __put_user((u64)f->dmabuf, &((struct floppy_drive_struct32 *)arg)->dmabuf); + err |= __put_user((u64)f->bufblocks, &((struct floppy_drive_struct32 *)arg)->bufblocks); + break; + } + case FDGETFDCSTAT32: + { + struct floppy_fdc_state *f = karg; + + err = __put_user(f->spec1, &((struct floppy_fdc_state32 *)arg)->spec1); + err |= __put_user(f->spec2, &((struct floppy_fdc_state32 *)arg)->spec2); + err |= __put_user(f->dtr, &((struct floppy_fdc_state32 *)arg)->dtr); + err |= __put_user(f->version, &((struct floppy_fdc_state32 *)arg)->version); + err |= __put_user(f->dor, &((struct floppy_fdc_state32 *)arg)->dor); + err |= __put_user(f->address, &((struct floppy_fdc_state32 *)arg)->address); + err |= __copy_to_user((char *)&((struct floppy_fdc_state32 *)arg)->address + + sizeof(((struct floppy_fdc_state32 *)arg)->address), + (char *)&f->address + sizeof(f->address), sizeof(int)); + err |= __put_user(f->driver_version, &((struct floppy_fdc_state32 *)arg)->driver_version); + err |= __copy_to_user(((struct floppy_fdc_state32 *)arg)->track, f->track, sizeof(f->track)); + break; + } + case FDWERRORGET32: + { + struct floppy_write_errors *f = karg; + + err = __put_user(f->write_errors, &((struct floppy_write_errors32 *)arg)->write_errors); + err |= __put_user(f->first_error_sector, &((struct floppy_write_errors32 *)arg)->first_error_sector); + err |= __put_user(f->first_error_generation, &((struct floppy_write_errors32 *)arg)->first_error_generation); + err |= __put_user(f->last_error_sector, &((struct floppy_write_errors32 *)arg)->last_error_sector); + err |= __put_user(f->last_error_generation, &((struct floppy_write_errors32 *)arg)->last_error_generation); + err |= __put_user(f->badness, &((struct floppy_write_errors32 *)arg)->badness); + break; + } + default: + break; + } + if (err) + err = -EFAULT; + +out: if (karg) kfree(karg); + return err; +} + +struct ppp_option_data32 { + __kernel_caddr_t32 ptr; + __u32 length; + int transmit; +}; +#define PPPIOCSCOMPRESS32 _IOW('t', 77, struct ppp_option_data32) + +struct ppp_idle32 { + __kernel_time_t32 xmit_idle; + __kernel_time_t32 recv_idle; +}; +#define PPPIOCGIDLE32 _IOR('t', 63, struct ppp_idle32) + +static int ppp_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct ppp_option_data32 data32; + struct ppp_option_data data; + struct ppp_idle32 idle32; + struct ppp_idle idle; + unsigned int kcmd; + void *karg; + int err = 0; + + switch (cmd) { + case PPPIOCGIDLE32: + kcmd = PPPIOCGIDLE; + karg = &idle; + break; + case PPPIOCSCOMPRESS32: + if (copy_from_user(&data32, (struct ppp_option_data32 *)arg, sizeof(struct ppp_option_data32))) + return -EFAULT; + data.ptr = kmalloc (data32.length, GFP_KERNEL); + if (!data.ptr) + return -ENOMEM; + if (copy_from_user(data.ptr, (__u8 *)A(data32.ptr), data32.length)) { + kfree(data.ptr); + return -EFAULT; + } + data.length = data32.length; + data.transmit = data32.transmit; + kcmd = PPPIOCSCOMPRESS; + karg = &data; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("ppp_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while (0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + switch (cmd) { + case PPPIOCGIDLE32: + if (err) + return err; + idle32.xmit_idle = idle.xmit_idle; + idle32.recv_idle = idle.recv_idle; + if (copy_to_user((struct ppp_idle32 *)arg, &idle32, sizeof(struct ppp_idle32))) + return -EFAULT; + break; + case PPPIOCSCOMPRESS32: + kfree(data.ptr); + break; + default: + break; + } + return err; +} + + +struct mtget32 { + __u32 mt_type; + __u32 mt_resid; + __u32 mt_dsreg; + __u32 mt_gstat; + __u32 mt_erreg; + __kernel_daddr_t32 mt_fileno; + __kernel_daddr_t32 mt_blkno; +}; +#define MTIOCGET32 _IOR('m', 2, struct mtget32) + +struct mtpos32 { + __u32 mt_blkno; +}; +#define MTIOCPOS32 _IOR('m', 3, struct mtpos32) + +struct mtconfiginfo32 { + __u32 mt_type; + __u32 ifc_type; + __u16 irqnr; + __u16 dmanr; + __u16 port; + __u32 debug; + __u32 have_dens:1; + __u32 have_bsf:1; + __u32 have_fsr:1; + __u32 have_bsr:1; + __u32 have_eod:1; + __u32 have_seek:1; + __u32 have_tell:1; + __u32 have_ras1:1; + __u32 have_ras2:1; + __u32 have_ras3:1; + __u32 have_qfa:1; + __u32 pad1:5; + char reserved[10]; +}; +#define MTIOCGETCONFIG32 _IOR('m', 4, struct mtconfiginfo32) +#define MTIOCSETCONFIG32 _IOW('m', 5, struct mtconfiginfo32) + +static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct mtconfiginfo info; + struct mtget get; + struct mtpos pos; + unsigned long kcmd; + void *karg; + int err = 0; + + switch(cmd) { + case MTIOCPOS32: + kcmd = MTIOCPOS; + karg = &pos; + break; + case MTIOCGET32: + kcmd = MTIOCGET; + karg = &get; + break; + case MTIOCGETCONFIG32: + kcmd = MTIOCGETCONFIG; + karg = &info; + break; + case MTIOCSETCONFIG32: + kcmd = MTIOCSETCONFIG; + karg = &info; + err = __get_user(info.mt_type, &((struct mtconfiginfo32 *)arg)->mt_type); + err |= __get_user(info.ifc_type, &((struct mtconfiginfo32 *)arg)->ifc_type); + err |= __get_user(info.irqnr, &((struct mtconfiginfo32 *)arg)->irqnr); + err |= __get_user(info.dmanr, &((struct mtconfiginfo32 *)arg)->dmanr); + err |= __get_user(info.port, &((struct mtconfiginfo32 *)arg)->port); + err |= __get_user(info.debug, &((struct mtconfiginfo32 *)arg)->debug); + err |= __copy_from_user((char *)&info.debug + sizeof(info.debug), + (char *)&((struct mtconfiginfo32 *)arg)->debug + + sizeof(((struct mtconfiginfo32 *)arg)->debug), sizeof(__u32)); + if (err) + return -EFAULT; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("mt_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while (0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + return err; + switch (cmd) { + case MTIOCPOS32: + err = __put_user(pos.mt_blkno, &((struct mtpos32 *)arg)->mt_blkno); + break; + case MTIOCGET32: + err = __put_user(get.mt_type, &((struct mtget32 *)arg)->mt_type); + err |= __put_user(get.mt_resid, &((struct mtget32 *)arg)->mt_resid); + err |= __put_user(get.mt_dsreg, &((struct mtget32 *)arg)->mt_dsreg); + err |= __put_user(get.mt_gstat, &((struct mtget32 *)arg)->mt_gstat); + err |= __put_user(get.mt_erreg, &((struct mtget32 *)arg)->mt_erreg); + err |= __put_user(get.mt_fileno, &((struct mtget32 *)arg)->mt_fileno); + err |= __put_user(get.mt_blkno, &((struct mtget32 *)arg)->mt_blkno); + break; + case MTIOCGETCONFIG32: + err = __put_user(info.mt_type, &((struct mtconfiginfo32 *)arg)->mt_type); + err |= __put_user(info.ifc_type, &((struct mtconfiginfo32 *)arg)->ifc_type); + err |= __put_user(info.irqnr, &((struct mtconfiginfo32 *)arg)->irqnr); + err |= __put_user(info.dmanr, &((struct mtconfiginfo32 *)arg)->dmanr); + err |= __put_user(info.port, &((struct mtconfiginfo32 *)arg)->port); + err |= __put_user(info.debug, &((struct mtconfiginfo32 *)arg)->debug); + err |= __copy_to_user((char *)&((struct mtconfiginfo32 *)arg)->debug + + sizeof(((struct mtconfiginfo32 *)arg)->debug), + (char *)&info.debug + sizeof(info.debug), sizeof(__u32)); + break; + case MTIOCSETCONFIG32: + break; + } + return err ? -EFAULT: 0; +} + +struct cdrom_read32 { + int cdread_lba; + __kernel_caddr_t32 cdread_bufaddr; + int cdread_buflen; +}; + +struct cdrom_read_audio32 { + union cdrom_addr addr; + u_char addr_format; + int nframes; + __kernel_caddr_t32 buf; +}; + +struct cdrom_generic_command32 { + unsigned char cmd[CDROM_PACKET_SIZE]; + __kernel_caddr_t32 buffer; + unsigned int buflen; + int stat; + __kernel_caddr_t32 sense; + __kernel_caddr_t32 reserved[3]; +}; + +static int cdrom_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct cdrom_read cdread; + struct cdrom_read_audio cdreadaudio; + struct cdrom_generic_command cgc; + __kernel_caddr_t32 addr; + char *data = 0; + void *karg; + int err = 0; + + switch(cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + karg = &cdread; + err = __get_user(cdread.cdread_lba, &((struct cdrom_read32 *)arg)->cdread_lba); + err |= __get_user(addr, &((struct cdrom_read32 *)arg)->cdread_bufaddr); + err |= __get_user(cdread.cdread_buflen, &((struct cdrom_read32 *)arg)->cdread_buflen); + if (err) + return -EFAULT; + data = kmalloc(cdread.cdread_buflen, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdread.cdread_bufaddr = data; + break; + case CDROMREADAUDIO: + karg = &cdreadaudio; + err = copy_from_user(&cdreadaudio.addr, &((struct cdrom_read_audio32 *)arg)->addr, sizeof(cdreadaudio.addr)); + err |= __get_user(cdreadaudio.addr_format, &((struct cdrom_read_audio32 *)arg)->addr_format); + err |= __get_user(cdreadaudio.nframes, &((struct cdrom_read_audio32 *)arg)->nframes); + err |= __get_user(addr, &((struct cdrom_read_audio32 *)arg)->buf); + if (err) + return -EFAULT; + data = kmalloc(cdreadaudio.nframes * 2352, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdreadaudio.buf = data; + break; + case CDROM_SEND_PACKET: + karg = &cgc; + err = copy_from_user(cgc.cmd, &((struct cdrom_generic_command32 *)arg)->cmd, sizeof(cgc.cmd)); + err |= __get_user(addr, &((struct cdrom_generic_command32 *)arg)->buffer); + err |= __get_user(cgc.buflen, &((struct cdrom_generic_command32 *)arg)->buflen); + if (err) + return -EFAULT; + if ((data = kmalloc(cgc.buflen, GFP_KERNEL)) == NULL) + return -ENOMEM; + cgc.buffer = data; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("cdrom_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while (0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + err = copy_to_user((char *)A(addr), data, cdread.cdread_buflen); + break; + case CDROMREADAUDIO: + err = copy_to_user((char *)A(addr), data, cdreadaudio.nframes * 2352); + break; + case CDROM_SEND_PACKET: + err = copy_to_user((char *)A(addr), data, cgc.buflen); + break; + default: + break; + } +out: if (data) + kfree(data); + return err ? -EFAULT : 0; +} + +struct loop_info32 { + int lo_number; /* ioctl r/o */ + __kernel_dev_t32 lo_device; /* ioctl r/o */ + unsigned int lo_inode; /* ioctl r/o */ + __kernel_dev_t32 lo_rdevice; /* ioctl r/o */ + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; /* ioctl w/o */ + int lo_flags; /* ioctl r/o */ + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ + unsigned int lo_init[2]; + char reserved[4]; +}; + +static int loop_status(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct loop_info l; + int err = -EINVAL; + + switch(cmd) { + case LOOP_SET_STATUS: + err = get_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __get_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __get_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __get_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_from_user((char *)&l.lo_offset, (char *)&((struct loop_info32 *)arg)->lo_offset, + 8 + (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) { + err = -EFAULT; + } else { + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + } + break; + case LOOP_GET_STATUS: + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + if (!err) { + err = put_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __put_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __put_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __put_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_to_user((char *)&((struct loop_info32 *)arg)->lo_offset, + (char *)&l.lo_offset, (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) + err = -EFAULT; + } + break; + default: { + static int count = 0; + if (++count <= 20) + printk("%s: Unknown loop ioctl cmd, fd(%d) " + "cmd(%08x) arg(%08lx)\n", + __FUNCTION__, fd, cmd, arg); + } + } + return err; +} + +extern int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); + +#ifdef CONFIG_VT +static int vt_check(struct file *file) +{ + struct tty_struct *tty; + struct inode *inode = file->f_dentry->d_inode; + + if (file->f_op->ioctl != tty_ioctl) + return -EINVAL; + + tty = (struct tty_struct *)file->private_data; + if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) + return -EINVAL; + + if (tty->driver.ioctl != vt_ioctl) + return -EINVAL; + + /* + * To have permissions to do most of the vt ioctls, we either have + * to be the owner of the tty, or super-user. + */ + if (current->tty == tty || suser()) + return 1; + return 0; +} + +struct consolefontdesc32 { + unsigned short charcount; /* characters in font (256 or 512) */ + unsigned short charheight; /* scan lines per character (1-32) */ + u32 chardata; /* font data in expanded form */ +}; + +static int do_fontx_ioctl(unsigned int fd, int cmd, struct consolefontdesc32 *user_cfd, struct file *file) +{ + struct consolefontdesc cfdarg; + struct console_font_op op; + int i, perm; + + perm = vt_check(file); + if (perm < 0) return perm; + + if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc32))) + return -EFAULT; + + cfdarg.chardata = (unsigned char *)A(((struct consolefontdesc32 *)&cfdarg)->chardata); + + switch (cmd) { + case PIO_FONTX: + if (!perm) + return -EPERM; + op.op = KD_FONT_OP_SET; + op.flags = 0; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + return con_font_op(fg_console, &op); + case GIO_FONTX: + if (!cfdarg.chardata) + return 0; + op.op = KD_FONT_OP_GET; + op.flags = 0; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + i = con_font_op(fg_console, &op); + if (i) + return i; + cfdarg.charheight = op.height; + cfdarg.charcount = op.charcount; + ((struct consolefontdesc32 *)&cfdarg)->chardata = (unsigned long)cfdarg.chardata; + if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc32))) + return -EFAULT; + return 0; + } + return -EINVAL; +} + +struct console_font_op32 { + unsigned int op; /* operation code KD_FONT_OP_* */ + unsigned int flags; /* KD_FONT_FLAG_* */ + unsigned int width, height; /* font size */ + unsigned int charcount; + u32 data; /* font data with height fixed to 32 */ +}; + +static int do_kdfontop_ioctl(unsigned int fd, unsigned int cmd, struct console_font_op32 *fontop, struct file *file) +{ + struct console_font_op op; + int perm = vt_check(file), i; + struct vt_struct *vt; + + if (perm < 0) return perm; + + if (copy_from_user(&op, (void *) fontop, sizeof(struct console_font_op32))) + return -EFAULT; + if (!perm && op.op != KD_FONT_OP_GET) + return -EPERM; + op.data = (unsigned char *)A(((struct console_font_op32 *)&op)->data); + op.flags |= KD_FONT_FLAG_OLD; + vt = (struct vt_struct *)((struct tty_struct *)file->private_data)->driver_data; + i = con_font_op(vt->vc_num, &op); + if (i) return i; + ((struct console_font_op32 *)&op)->data = (unsigned long)op.data; + if (copy_to_user((void *) fontop, &op, sizeof(struct console_font_op32))) + return -EFAULT; + return 0; +} + +struct fb_fix_screeninfo32 { + char id[16]; /* identification string eg "TT Builtin" */ + unsigned int smem_start; /* Start of frame buffer mem */ + /* (physical address) */ + __u32 smem_len; /* Length of frame buffer mem */ + __u32 type; /* see FB_TYPE_* */ + __u32 type_aux; /* Interleave for interleaved Planes */ + __u32 visual; /* see FB_VISUAL_* */ + __u16 xpanstep; /* zero if no hardware panning */ + __u16 ypanstep; /* zero if no hardware panning */ + __u16 ywrapstep; /* zero if no hardware ywrap */ + __u32 line_length; /* length of a line in bytes */ + unsigned int mmio_start; /* Start of Memory Mapped I/O */ + /* (physical address) */ + __u32 mmio_len; /* Length of Memory Mapped I/O */ + __u32 accel; /* Type of acceleration available */ + __u16 reserved[3]; /* Reserved for future compatibility */ +}; + +static int do_fbioget_fscreeninfo_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct fb_fix_screeninfo fix; + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (long)&fix); + set_fs(old_fs); + + if (err == 0) { + unsigned int smem_start = fix.smem_start; /* lose top 32 bits */ + unsigned int mmio_start = fix.mmio_start; /* lose top 32 bits */ + int i; + + err = put_user(fix.id[0], &((struct fb_fix_screeninfo32 *)arg)->id[0]); + for (i=1; i<16; i++) { + err |= __put_user(fix.id[i], &((struct fb_fix_screeninfo32 *)arg)->id[i]); + } + err |= __put_user(smem_start, &((struct fb_fix_screeninfo32 *)arg)->smem_start); + err |= __put_user(fix.smem_len, &((struct fb_fix_screeninfo32 *)arg)->smem_len); + err |= __put_user(fix.type, &((struct fb_fix_screeninfo32 *)arg)->type); + err |= __put_user(fix.type_aux, &((struct fb_fix_screeninfo32 *)arg)->type_aux); + err |= __put_user(fix.visual, &((struct fb_fix_screeninfo32 *)arg)->visual); + err |= __put_user(fix.xpanstep, &((struct fb_fix_screeninfo32 *)arg)->xpanstep); + err |= __put_user(fix.ypanstep, &((struct fb_fix_screeninfo32 *)arg)->ypanstep); + err |= __put_user(fix.ywrapstep, &((struct fb_fix_screeninfo32 *)arg)->ywrapstep); + err |= __put_user(fix.line_length, &((struct fb_fix_screeninfo32 *)arg)->line_length); + err |= __put_user(mmio_start, &((struct fb_fix_screeninfo32 *)arg)->mmio_start); + err |= __put_user(fix.mmio_len, &((struct fb_fix_screeninfo32 *)arg)->mmio_len); + err |= __put_user(fix.accel, &((struct fb_fix_screeninfo32 *)arg)->accel); + err |= __put_user(fix.reserved[0], &((struct fb_fix_screeninfo32 *)arg)->reserved[0]); + err |= __put_user(fix.reserved[1], &((struct fb_fix_screeninfo32 *)arg)->reserved[1]); + err |= __put_user(fix.reserved[2], &((struct fb_fix_screeninfo32 *)arg)->reserved[2]); + if (err) + err = -EFAULT; + } + return err; +} + +struct fb_cmap32 { + __u32 start; /* First entry */ + __u32 len; /* Number of entries */ + __u32 redptr; /* Red values */ + __u32 greenptr; + __u32 blueptr; + __u32 transpptr; /* transparency, can be NULL */ +}; + +static int do_fbiogetcmap_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct fb_cmap cmap; + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (long)&cmap); + set_fs(old_fs); + + if (err == 0) { + __u32 redptr = (__u32)(__u64)cmap.red; + __u32 greenptr = (__u32)(__u64)cmap.green; + __u32 blueptr = (__u32)(__u64)cmap.blue; + __u32 transpptr = (__u32)(__u64)cmap.transp; + + err = put_user(cmap.start, &((struct fb_cmap32 *)arg)->start); + err |= __put_user(cmap.len, &((struct fb_cmap32 *)arg)->len); + err |= __put_user(redptr, &((struct fb_cmap32 *)arg)->redptr); + err |= __put_user(greenptr, &((struct fb_cmap32 *)arg)->greenptr); + err |= __put_user(blueptr, &((struct fb_cmap32 *)arg)->blueptr); + err |= __put_user(transpptr, &((struct fb_cmap32 *)arg)->transpptr); + if (err) + err = -EFAULT; + } + return err; +} + +static int do_fbioputcmap_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct fb_cmap cmap; + __u32 redptr, greenptr, blueptr, transpptr; + int err; + + err = get_user(cmap.start, &((struct fb_cmap32 *)arg)->start); + err |= __get_user(cmap.len, &((struct fb_cmap32 *)arg)->len); + err |= __get_user(redptr, &((struct fb_cmap32 *)arg)->redptr); + err |= __get_user(greenptr, &((struct fb_cmap32 *)arg)->greenptr); + err |= __get_user(blueptr, &((struct fb_cmap32 *)arg)->blueptr); + err |= __get_user(transpptr, &((struct fb_cmap32 *)arg)->transpptr); + + if (err) { + err = -EFAULT; + } else { + cmap.red = (__u16 *)(__u64)redptr; + cmap.green = (__u16 *)(__u64)greenptr; + cmap.blue = (__u16 *)(__u64)blueptr; + cmap.transp = (__u16 *)(__u64)transpptr; + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&cmap); + set_fs (old_fs); + } + return err; +} + +struct unimapdesc32 { + unsigned short entry_ct; + u32 entries; +}; + +static int do_unimap_ioctl(unsigned int fd, unsigned int cmd, struct unimapdesc32 *user_ud, struct file *file) +{ + struct unimapdesc32 tmp; + int perm = vt_check(file); + + if (perm < 0) return perm; + if (copy_from_user(&tmp, user_ud, sizeof tmp)) + return -EFAULT; + switch (cmd) { + case PIO_UNIMAP: + if (!perm) return -EPERM; + return con_set_unimap(fg_console, tmp.entry_ct, (struct unipair *)A(tmp.entries)); + case GIO_UNIMAP: + return con_get_unimap(fg_console, tmp.entry_ct, &(user_ud->entry_ct), (struct unipair *)A(tmp.entries)); + } + return 0; +} +#endif /* CONFIG_VT */ +static int do_smb_getmountuid(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + __kernel_uid_t kuid; + int err; + + cmd = SMB_IOC_GETMOUNTUID; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&kuid); + set_fs(old_fs); + + if (err >= 0) + err = put_user(kuid, (__kernel_uid_t32 *)arg); + + return err; +} + +struct atmif_sioc32 { + int number; + int length; + __kernel_caddr_t32 arg; +}; + +struct atm_iobuf32 { + int length; + __kernel_caddr_t32 buffer; +}; + +#define ATM_GETLINKRATE32 _IOW('a', ATMIOC_ITF+1, struct atmif_sioc32) +#define ATM_GETNAMES32 _IOW('a', ATMIOC_ITF+3, struct atm_iobuf32) +#define ATM_GETTYPE32 _IOW('a', ATMIOC_ITF+4, struct atmif_sioc32) +#define ATM_GETESI32 _IOW('a', ATMIOC_ITF+5, struct atmif_sioc32) +#define ATM_GETADDR32 _IOW('a', ATMIOC_ITF+6, struct atmif_sioc32) +#define ATM_RSTADDR32 _IOW('a', ATMIOC_ITF+7, struct atmif_sioc32) +#define ATM_ADDADDR32 _IOW('a', ATMIOC_ITF+8, struct atmif_sioc32) +#define ATM_DELADDR32 _IOW('a', ATMIOC_ITF+9, struct atmif_sioc32) +#define ATM_GETCIRANGE32 _IOW('a', ATMIOC_ITF+10, struct atmif_sioc32) +#define ATM_SETCIRANGE32 _IOW('a', ATMIOC_ITF+11, struct atmif_sioc32) +#define ATM_SETESI32 _IOW('a', ATMIOC_ITF+12, struct atmif_sioc32) +#define ATM_SETESIF32 _IOW('a', ATMIOC_ITF+13, struct atmif_sioc32) +#define ATM_GETSTAT32 _IOW('a', ATMIOC_SARCOM+0, struct atmif_sioc32) +#define ATM_GETSTATZ32 _IOW('a', ATMIOC_SARCOM+1, struct atmif_sioc32) +#define ATM_GETLOOP32 _IOW('a', ATMIOC_SARCOM+2, struct atmif_sioc32) +#define ATM_SETLOOP32 _IOW('a', ATMIOC_SARCOM+3, struct atmif_sioc32) +#define ATM_QUERYLOOP32 _IOW('a', ATMIOC_SARCOM+4, struct atmif_sioc32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} atm_ioctl_map[] = { + { ATM_GETLINKRATE32, ATM_GETLINKRATE }, + { ATM_GETNAMES32, ATM_GETNAMES }, + { ATM_GETTYPE32, ATM_GETTYPE }, + { ATM_GETESI32, ATM_GETESI }, + { ATM_GETADDR32, ATM_GETADDR }, + { ATM_RSTADDR32, ATM_RSTADDR }, + { ATM_ADDADDR32, ATM_ADDADDR }, + { ATM_DELADDR32, ATM_DELADDR }, + { ATM_GETCIRANGE32, ATM_GETCIRANGE }, + { ATM_SETCIRANGE32, ATM_SETCIRANGE }, + { ATM_SETESI32, ATM_SETESI }, + { ATM_SETESIF32, ATM_SETESIF }, + { ATM_GETSTAT32, ATM_GETSTAT }, + { ATM_GETSTATZ32, ATM_GETSTATZ }, + { ATM_GETLOOP32, ATM_GETLOOP }, + { ATM_SETLOOP32, ATM_SETLOOP }, + { ATM_QUERYLOOP32, ATM_QUERYLOOP } +}; + +#define NR_ATM_IOCTL (sizeof(atm_ioctl_map)/sizeof(atm_ioctl_map[0])) + + +static int do_atm_iobuf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atm_iobuf32 iobuf32; + struct atm_iobuf iobuf = { 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&iobuf32, (struct atm_iobuf32*)arg, + sizeof(struct atm_iobuf32)); + if (err) + return -EFAULT; + + iobuf.length = iobuf32.length; + + if (iobuf32.buffer == (__kernel_caddr_t32) NULL || iobuf32.length == 0) { + iobuf.buffer = (void*)(unsigned long)iobuf32.buffer; + } else { + iobuf.buffer = kmalloc(iobuf.length, GFP_KERNEL); + if (iobuf.buffer == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(iobuf.buffer, (void *)A(iobuf32.buffer), iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&iobuf); + set_fs (old_fs); + if (err) + goto out; + + if (iobuf.buffer && iobuf.length > 0) { + err = copy_to_user((void *)A(iobuf32.buffer), iobuf.buffer, iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(iobuf.length, &(((struct atm_iobuf32*)arg)->length)); + + out: + if (iobuf32.buffer && iobuf32.length > 0) + kfree(iobuf.buffer); + + return err; +} + + +static int do_atmif_sioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atmif_sioc32 sioc32; + struct atmif_sioc sioc = { 0, 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&sioc32, (struct atmif_sioc32*)arg, + sizeof(struct atmif_sioc32)); + if (err) + return -EFAULT; + + sioc.number = sioc32.number; + sioc.length = sioc32.length; + + if (sioc32.arg == (__kernel_caddr_t32) NULL || sioc32.length == 0) { + sioc.arg = (void*)(unsigned long)sioc32.arg; + } else { + sioc.arg = kmalloc(sioc.length, GFP_KERNEL); + if (sioc.arg == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(sioc.arg, (void *)A(sioc32.arg), sioc32.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&sioc); + set_fs (old_fs); + if (err) { + goto out; + } + + if (sioc.arg && sioc.length > 0) { + err = copy_to_user((void *)A(sioc32.arg), sioc.arg, sioc.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(sioc.length, &(((struct atmif_sioc32*)arg)->length)); + + out: + if (sioc32.arg && sioc32.length > 0) + kfree(sioc.arg); + + return err; +} + + +static int do_atm_ioctl(unsigned int fd, unsigned int cmd32, unsigned long arg) +{ + int i; + unsigned int cmd = 0; + + switch (cmd32) { + case SONET_GETSTAT: + case SONET_GETSTATZ: + case SONET_GETDIAG: + case SONET_SETDIAG: + case SONET_CLRDIAG: + case SONET_SETFRAMING: + case SONET_GETFRAMING: + case SONET_GETFRSENSE: + return do_atmif_sioc(fd, cmd32, arg); + } + + for (i = 0; i < NR_ATM_IOCTL; i++) { + if (cmd32 == atm_ioctl_map[i].cmd32) { + cmd = atm_ioctl_map[i].cmd; + break; + } + } + if (i == NR_ATM_IOCTL) { + return -EINVAL; + } + + switch (cmd) { + case ATM_GETNAMES: + return do_atm_iobuf(fd, cmd, arg); + + case ATM_GETLINKRATE: + case ATM_GETTYPE: + case ATM_GETESI: + case ATM_GETADDR: + case ATM_RSTADDR: + case ATM_ADDADDR: + case ATM_DELADDR: + case ATM_GETCIRANGE: + case ATM_SETCIRANGE: + case ATM_SETESI: + case ATM_SETESIF: + case ATM_GETSTAT: + case ATM_GETSTATZ: + case ATM_GETLOOP: + case ATM_SETLOOP: + case ATM_QUERYLOOP: + return do_atmif_sioc(fd, cmd, arg); + } + + return -EINVAL; +} + +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* Ugh, LVM. Pitty it was not cleaned up before accepted :((. */ +typedef struct { + uint8_t vg_name[NAME_LEN]; + uint32_t vg_number; + uint32_t vg_access; + uint32_t vg_status; + uint32_t lv_max; + uint32_t lv_cur; + uint32_t lv_open; + uint32_t pv_max; + uint32_t pv_cur; + uint32_t pv_act; + uint32_t dummy; + uint32_t vgda; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pvg_total; + u32 proc; + u32 pv[ABS_MAX_PV + 1]; + u32 lv[ABS_MAX_LV + 1]; + uint8_t vg_uuid[UUID_LEN+1]; /* volume group UUID */ + uint8_t dummy1[200]; +} vg32_t; + +typedef struct { + uint8_t id[2]; + uint16_t version; + lvm_disk_data_t pv_on_disk; + lvm_disk_data_t vg_on_disk; + lvm_disk_data_t pv_namelist_on_disk; + lvm_disk_data_t lv_on_disk; + lvm_disk_data_t pe_on_disk; + uint8_t pv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint8_t system_id[NAME_LEN]; + kdev_t pv_dev; + uint32_t pv_number; + uint32_t pv_status; + uint32_t pv_allocatable; + uint32_t pv_size; + uint32_t lv_cur; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pe_stale; + u32 pe; + u32 inode; + uint8_t pv_uuid[UUID_LEN+1]; +} pv32_t; + +typedef struct { + char lv_name[NAME_LEN]; + u32 lv; +} lv_req32_t; + +typedef struct { + u32 lv_index; + u32 lv; + /* Transfer size because user space and kernel space differ */ + uint16_t size; +} lv_status_byindex_req32_t; + +typedef struct { + __kernel_dev_t32 dev; + u32 lv; +} lv_status_bydev_req32_t; + +typedef struct { + uint8_t lv_name[NAME_LEN]; + kdev_t old_dev; + kdev_t new_dev; + u32 old_pe; + u32 new_pe; +} le_remap_req32_t; + +typedef struct { + char pv_name[NAME_LEN]; + u32 pv; +} pv_status_req32_t; + +typedef struct { + uint8_t lv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint32_t lv_access; + uint32_t lv_status; + uint32_t lv_open; + kdev_t lv_dev; + uint32_t lv_number; + uint32_t lv_mirror_copies; + uint32_t lv_recovery; + uint32_t lv_schedule; + uint32_t lv_size; + u32 lv_current_pe; + uint32_t lv_current_le; + uint32_t lv_allocated_le; + uint32_t lv_stripes; + uint32_t lv_stripesize; + uint32_t lv_badblock; + uint32_t lv_allocation; + uint32_t lv_io_timeout; + uint32_t lv_read_ahead; + /* delta to version 1 starts here */ + u32 lv_snapshot_org; + u32 lv_snapshot_prev; + u32 lv_snapshot_next; + u32 lv_block_exception; + uint32_t lv_remap_ptr; + uint32_t lv_remap_end; + uint32_t lv_chunk_size; + uint32_t lv_snapshot_minor; + char dummy[200]; +} lv32_t; + +typedef struct { + u32 hash[2]; + u32 rsector_org; + kdev_t rdev_org; + u32 rsector_new; + kdev_t rdev_new; +} lv_block_exception32_t; + +static void put_lv_t(lv_t *l) +{ + if (l->lv_current_pe) vfree(l->lv_current_pe); + if (l->lv_block_exception) vfree(l->lv_block_exception); + kfree(l); +} + +static lv_t *get_lv_t(u32 p, int *errp) +{ + int err, i; + u32 ptr1, ptr2; + size_t size; + lv_block_exception32_t *lbe32; + lv_block_exception_t *lbe; + lv32_t *ul = (lv32_t *)A(p); + lv_t *l = (lv_t *) kmalloc(sizeof(lv_t), GFP_KERNEL); + + if (!l) { + *errp = -ENOMEM; + return NULL; + } + memset(l, 0, sizeof(lv_t)); + err = copy_from_user(l, ul, (long)&((lv32_t *)0)->lv_current_pe); + err |= __copy_from_user(&l->lv_current_le, &ul->lv_current_le, + ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le)); + err |= __copy_from_user(&l->lv_remap_ptr, &ul->lv_remap_ptr, + ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); + err |= __get_user(ptr1, &ul->lv_current_pe); + err |= __get_user(ptr2, &ul->lv_block_exception); + if (err) { + kfree(l); + *errp = -EFAULT; + return NULL; + } + if (ptr1) { + size = l->lv_allocated_le * sizeof(pe_t); + l->lv_current_pe = vmalloc(size); + if (l->lv_current_pe) + err = copy_from_user(l->lv_current_pe, (void *)A(ptr1), size); + } + if (!err && ptr2) { + size = l->lv_remap_end * sizeof(lv_block_exception_t); + l->lv_block_exception = lbe = vmalloc(size); + if (l->lv_block_exception) { + lbe32 = (lv_block_exception32_t *)A(ptr2); + memset(lbe, 0, size); + for (i = 0; i < l->lv_remap_end; i++, lbe++, lbe32++) { + err |= get_user(lbe->rsector_org, &lbe32->rsector_org); + err |= __get_user(lbe->rdev_org, &lbe32->rdev_org); + err |= __get_user(lbe->rsector_new, &lbe32->rsector_new); + err |= __get_user(lbe->rdev_new, &lbe32->rdev_new); + } + } + } + if (err || (ptr1 && !l->lv_current_pe) || (ptr2 && !l->lv_block_exception)) { + if (!err) + *errp = -ENOMEM; + else + *errp = -EFAULT; + put_lv_t(l); + return NULL; + } + return l; +} + +static int copy_lv_t(u32 ptr, lv_t *l) +{ + int err; + lv32_t *ul = (lv32_t *)A(ptr); + u32 ptr1; + size_t size; + + err = get_user(ptr1, &ul->lv_current_pe); + if (err) + return -EFAULT; + err = copy_to_user(ul, l, (long)&((lv32_t *)0)->lv_current_pe); + err |= __copy_to_user(&ul->lv_current_le, &l->lv_current_le, + ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le)); + err |= __copy_to_user(&ul->lv_remap_ptr, &l->lv_remap_ptr, + ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); + size = l->lv_allocated_le * sizeof(pe_t); + if (ptr1) + err |= __copy_to_user((void *)A(ptr1), l->lv_current_pe, size); + return err ? -EFAULT : 0; +} + +static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + vg_t *v = NULL; + union { + lv_req_t lv_req; + le_remap_req_t le_remap; + lv_status_byindex_req_t lv_byindex; + lv_status_bydev_req_t lv_bydev; + pv_status_req_t pv_status; + } u; + pv_t p; + int err; + u32 ptr = 0; + int i; + mm_segment_t old_fs; + void *karg = &u; + + switch (cmd) { + case VG_STATUS: + v = kmalloc(sizeof(vg_t), GFP_KERNEL); + if (!v) + return -ENOMEM; + karg = v; + break; + + case VG_CREATE_OLD: + case VG_CREATE: + v = kmalloc(sizeof(vg_t), GFP_KERNEL); + if (!v) + return -ENOMEM; + if (copy_from_user(v, (void *)arg, (long)&((vg32_t *)0)->proc)) { + kfree(v); + return -EFAULT; + } + /* 'proc' field is unused, just NULL it out. */ + v->proc = NULL; + if (copy_from_user(v->vg_uuid, ((vg32_t *)arg)->vg_uuid, UUID_LEN+1)) { + kfree(v); + return -EFAULT; + } + + karg = v; + memset(v->pv, 0, sizeof(v->pv) + sizeof(v->lv)); + if (v->pv_max > ABS_MAX_PV || v->lv_max > ABS_MAX_LV) + return -EPERM; + for (i = 0; i < v->pv_max; i++) { + err = __get_user(ptr, &((vg32_t *)arg)->pv[i]); + if (err) + break; + if (ptr) { + v->pv[i] = kmalloc(sizeof(pv_t), GFP_KERNEL); + if (!v->pv[i]) { + err = -ENOMEM; + break; + } + err = copy_from_user(v->pv[i], (void *)A(ptr), + sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + err = copy_from_user(v->pv[i]->pv_uuid, + ((pv32_t *)A(ptr))->pv_uuid, + UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + + v->pv[i]->pe = NULL; + v->pv[i]->bd = NULL; + } + } + if (!err) { + for (i = 0; i < v->lv_max; i++) { + err = __get_user(ptr, &((vg32_t *)arg)->lv[i]); + if (err) + break; + if (ptr) { + v->lv[i] = get_lv_t(ptr, &err); + if (err) + break; + } + } + } + break; + + case LV_CREATE: + case LV_EXTEND: + case LV_REDUCE: + case LV_REMOVE: + case LV_RENAME: + case LV_STATUS_BYNAME: + err = copy_from_user(&u.pv_status, arg, sizeof(u.pv_status.pv_name)); + if (err) + return -EFAULT; + if (cmd != LV_REMOVE) { + err = __get_user(ptr, &((lv_req32_t *)arg)->lv); + if (err) + return err; + u.lv_req.lv = get_lv_t(ptr, &err); + } else + u.lv_req.lv = NULL; + break; + + case LV_STATUS_BYINDEX: + err = get_user(u.lv_byindex.lv_index, + &((lv_status_byindex_req32_t *)arg)->lv_index); + err |= __get_user(ptr, &((lv_status_byindex_req32_t *)arg)->lv); + if (err) + return err; + u.lv_byindex.lv = get_lv_t(ptr, &err); + break; + + case LV_STATUS_BYDEV: + err = get_user(u.lv_bydev.dev, &((lv_status_bydev_req32_t *)arg)->dev); + err |= __get_user(ptr, &((lv_status_bydev_req32_t *)arg)->lv); + if (err) + return err; + u.lv_bydev.lv = get_lv_t(ptr, &err); + break; + + case VG_EXTEND: + err = copy_from_user(&p, (void *)arg, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) + return -EFAULT; + err = copy_from_user(p.pv_uuid, ((pv32_t *)arg)->pv_uuid, UUID_LEN+1); + if (err) + return -EFAULT; + p.pe = NULL; + p.bd = NULL; + karg = &p; + break; + + case PV_CHANGE: + case PV_STATUS: + err = copy_from_user(&u.pv_status, arg, sizeof(u.lv_req.lv_name)); + if (err) + return -EFAULT; + err = __get_user(ptr, &((pv_status_req32_t *)arg)->pv); + if (err) + return err; + u.pv_status.pv = &p; + if (cmd == PV_CHANGE) { + err = copy_from_user(&p, (void *)A(ptr), + sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) + return -EFAULT; + p.pe = NULL; + p.bd = NULL; + } + break; + }; + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + + switch (cmd) { + case VG_STATUS: + if (!err) { + if (copy_to_user((void *)arg, v, (long)&((vg32_t *)0)->proc) || + clear_user(&((vg32_t *)arg)->proc, sizeof(vg32_t) - (long)&((vg32_t *)0)->proc)) + err = -EFAULT; + } + if (copy_to_user(((vg32_t *)arg)->vg_uuid, v->vg_uuid, UUID_LEN+1)) { + err = -EFAULT; + } + kfree(v); + break; + + case VG_CREATE_OLD: + case VG_CREATE: + for (i = 0; i < v->pv_max; i++) { + if (v->pv[i]) + kfree(v->pv[i]); + } + for (i = 0; i < v->lv_max; i++) { + if (v->lv[i]) + put_lv_t(v->lv[i]); + } + kfree(v); + break; + + case LV_STATUS_BYNAME: + if (!err && u.lv_req.lv) + err = copy_lv_t(ptr, u.lv_req.lv); + /* Fall through */ + + case LV_CREATE: + case LV_EXTEND: + case LV_REDUCE: + if (u.lv_req.lv) + put_lv_t(u.lv_req.lv); + break; + + case LV_STATUS_BYINDEX: + if (u.lv_byindex.lv) { + if (!err) + err = copy_lv_t(ptr, u.lv_byindex.lv); + put_lv_t(u.lv_byindex.lv); + } + break; + + case LV_STATUS_BYDEV: + if (u.lv_bydev.lv) { + if (!err) + err = copy_lv_t(ptr, u.lv_bydev.lv); + put_lv_t(u.lv_byindex.lv); + } + break; + + case PV_STATUS: + if (!err) { + err = copy_to_user((void *)A(ptr), &p, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) + return -EFAULT; + err = copy_to_user(((pv_t *)A(ptr))->pv_uuid, p.pv_uuid, UUID_LEN + 1); + if (err) + return -EFAULT; + } + break; + }; + + return err; +} +#endif + +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +/* This really belongs in include/linux/drm.h -DaveM */ +#include "../../../drivers/char/drm/drm.h" + +typedef struct drm32_version { + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel;/* Patch level */ + int name_len; /* Length of name buffer */ + u32 name; /* Name of driver */ + int date_len; /* Length of date buffer */ + u32 date; /* User-space buffer to hold date */ + int desc_len; /* Length of desc buffer */ + u32 desc; /* User-space buffer to hold desc */ +} drm32_version_t; +#define DRM32_IOCTL_VERSION DRM_IOWR(0x00, drm32_version_t) + +static int drm32_version(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_version_t *uversion = (drm32_version_t *)arg; + char *name_ptr, *date_ptr, *desc_ptr; + u32 tmp1, tmp2, tmp3; + drm_version_t kversion; + mm_segment_t old_fs; + int ret; + + memset(&kversion, 0, sizeof(kversion)); + if (get_user(kversion.name_len, &uversion->name_len) || + get_user(kversion.date_len, &uversion->date_len) || + get_user(kversion.desc_len, &uversion->desc_len) || + get_user(tmp1, &uversion->name) || + get_user(tmp2, &uversion->date) || + get_user(tmp3, &uversion->desc)) + return -EFAULT; + + name_ptr = (char *) A(tmp1); + date_ptr = (char *) A(tmp2); + desc_ptr = (char *) A(tmp3); + + ret = -ENOMEM; + if (kversion.name_len && name_ptr) { + kversion.name = kmalloc(kversion.name_len, GFP_KERNEL); + if (!kversion.name) + goto out; + } + if (kversion.date_len && date_ptr) { + kversion.date = kmalloc(kversion.date_len, GFP_KERNEL); + if (!kversion.date) + goto out; + } + if (kversion.desc_len && desc_ptr) { + kversion.desc = kmalloc(kversion.desc_len, GFP_KERNEL); + if (!kversion.desc) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl (fd, DRM_IOCTL_VERSION, (unsigned long)&kversion); + set_fs(old_fs); + + if (!ret) { + if ((kversion.name && + copy_to_user(name_ptr, kversion.name, kversion.name_len)) || + (kversion.date && + copy_to_user(date_ptr, kversion.date, kversion.date_len)) || + (kversion.desc && + copy_to_user(desc_ptr, kversion.desc, kversion.desc_len))) + ret = -EFAULT; + if (put_user(kversion.version_major, &uversion->version_major) || + put_user(kversion.version_minor, &uversion->version_minor) || + put_user(kversion.version_patchlevel, &uversion->version_patchlevel) || + put_user(kversion.name_len, &uversion->name_len) || + put_user(kversion.date_len, &uversion->date_len) || + put_user(kversion.desc_len, &uversion->desc_len)) + ret = -EFAULT; + } + +out: + if (kversion.name) + kfree(kversion.name); + if (kversion.date) + kfree(kversion.date); + if (kversion.desc) + kfree(kversion.desc); + return ret; +} + +typedef struct drm32_unique { + int unique_len; /* Length of unique */ + u32 unique; /* Unique name for driver instantiation */ +} drm32_unique_t; +#define DRM32_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm32_unique_t) +#define DRM32_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm32_unique_t) + +static int drm32_getsetunique(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_unique_t *uarg = (drm32_unique_t *)arg; + drm_unique_t karg; + mm_segment_t old_fs; + char *uptr; + u32 tmp; + int ret; + + if (get_user(karg.unique_len, &uarg->unique_len)) + return -EFAULT; + karg.unique = NULL; + + if (get_user(tmp, &uarg->unique)) + return -EFAULT; + + uptr = (char *) A(tmp); + + if (uptr) { + karg.unique = kmalloc(karg.unique_len, GFP_KERNEL); + if (!karg.unique) + return -ENOMEM; + if (cmd == DRM32_IOCTL_SET_UNIQUE && + copy_from_user(karg.unique, uptr, karg.unique_len)) { + kfree(karg.unique); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + if (cmd == DRM32_IOCTL_GET_UNIQUE) + ret = sys_ioctl (fd, DRM_IOCTL_GET_UNIQUE, (unsigned long)&karg); + else + ret = sys_ioctl (fd, DRM_IOCTL_SET_UNIQUE, (unsigned long)&karg); + set_fs(old_fs); + + if (!ret) { + if (cmd == DRM32_IOCTL_GET_UNIQUE && + uptr != NULL && + copy_to_user(uptr, karg.unique, karg.unique_len)) + ret = -EFAULT; + if (put_user(karg.unique_len, &uarg->unique_len)) + ret = -EFAULT; + } + + if (karg.unique != NULL) + kfree(karg.unique); + + return ret; +} + +typedef struct drm32_map { + u32 offset; /* Requested physical address (0 for SAREA)*/ + u32 size; /* Requested physical size (bytes) */ + drm_map_type_t type; /* Type of memory to map */ + drm_map_flags_t flags; /* Flags */ + u32 handle; /* User-space: "Handle" to pass to mmap */ + /* Kernel-space: kernel-virtual address */ + int mtrr; /* MTRR slot used */ + /* Private data */ +} drm32_map_t; +#define DRM32_IOCTL_ADD_MAP DRM_IOWR(0x15, drm32_map_t) + +static int drm32_addmap(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_map_t *uarg = (drm32_map_t *) arg; + drm_map_t karg; + mm_segment_t old_fs; + u32 tmp; + int ret; + + ret = get_user(karg.offset, &uarg->offset); + ret |= get_user(karg.size, &uarg->size); + ret |= get_user(karg.type, &uarg->type); + ret |= get_user(karg.flags, &uarg->flags); + ret |= get_user(tmp, &uarg->handle); + ret |= get_user(karg.mtrr, &uarg->mtrr); + if (ret) + return -EFAULT; + + karg.handle = (void *) A(tmp); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_ADD_MAP, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + ret = put_user(karg.offset, &uarg->offset); + ret |= put_user(karg.size, &uarg->size); + ret |= put_user(karg.type, &uarg->type); + ret |= put_user(karg.flags, &uarg->flags); + tmp = (u32) (long)karg.handle; + ret |= put_user(tmp, &uarg->handle); + ret |= put_user(karg.mtrr, &uarg->mtrr); + if (ret) + ret = -EFAULT; + } + + return ret; +} + +typedef struct drm32_buf_info { + int count; /* Entries in list */ + u32 list; /* (drm_buf_desc_t *) */ +} drm32_buf_info_t; +#define DRM32_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm32_buf_info_t) + +static int drm32_info_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_info_t *uarg = (drm32_buf_info_t *)arg; + drm_buf_desc_t *ulist; + drm_buf_info_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (drm_buf_desc_t *) A(tmp); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_desc_t), GFP_KERNEL); + if (!karg.list) + return -EFAULT; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_INFO_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (karg.count <= orig_count && + (copy_to_user(ulist, karg.list, + karg.count * sizeof(drm_buf_desc_t)))) + ret = -EFAULT; + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_free { + int count; + u32 list; /* (int *) */ +} drm32_buf_free_t; +#define DRM32_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm32_buf_free_t) + +static int drm32_free_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_free_t *uarg = (drm32_buf_free_t *)arg; + drm_buf_free_t karg; + mm_segment_t old_fs; + int *ulist; + int ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (int *) A(tmp); + + karg.list = kmalloc(karg.count * sizeof(int), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + if (copy_from_user(karg.list, ulist, (karg.count * sizeof(int)))) + goto out; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_FREE_BUFS, (unsigned long) &karg); + set_fs(old_fs); + +out: + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_pub { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int used; /* Amount of buffer in use (for DMA) */ + u32 address; /* Address of buffer (void *) */ +} drm32_buf_pub_t; + +typedef struct drm32_buf_map { + int count; /* Length of buflist */ + u32 virtual; /* Mmaped area in user-virtual (void *) */ + u32 list; /* Buffer information (drm_buf_pub_t *) */ +} drm32_buf_map_t; +#define DRM32_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm32_buf_map_t) + +static int drm32_map_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_map_t *uarg = (drm32_buf_map_t *)arg; + drm32_buf_pub_t *ulist; + drm_buf_map_t karg; + mm_segment_t old_fs; + int orig_count, ret, i; + u32 tmp1, tmp2; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp1, &uarg->virtual) || + get_user(tmp2, &uarg->list)) + return -EFAULT; + + karg.virtual = (void *) A(tmp1); + ulist = (drm32_buf_pub_t *) A(tmp2); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_pub_t), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + for (i = 0; i < karg.count; i++) { + if (get_user(karg.list[i].idx, &ulist[i].idx) || + get_user(karg.list[i].total, &ulist[i].total) || + get_user(karg.list[i].used, &ulist[i].used) || + get_user(tmp1, &ulist[i].address)) + goto out; + + karg.list[i].address = (void *) A(tmp1); + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_MAP_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + for (i = 0; i < orig_count; i++) { + tmp1 = (u32) (long) karg.list[i].address; + if (put_user(karg.list[i].idx, &ulist[i].idx) || + put_user(karg.list[i].total, &ulist[i].total) || + put_user(karg.list[i].used, &ulist[i].used) || + put_user(tmp1, &ulist[i].address)) { + ret = -EFAULT; + goto out; + } + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + +out: + kfree(karg.list); + return ret; +} + +typedef struct drm32_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 */ + u32 send_indices; /* List of handles to buffers (int *) */ + u32 send_sizes; /* Lengths of data to send (int *) */ + drm_dma_flags_t flags; /* Flags */ + int request_count; /* Number of buffers requested */ + int request_size; /* Desired size for buffers */ + u32 request_indices; /* Buffer information (int *) */ + u32 request_sizes; /* (int *) */ + int granted_count; /* Number of buffers granted */ +} drm32_dma_t; +#define DRM32_IOCTL_DMA DRM_IOWR(0x29, drm32_dma_t) + +/* RED PEN The DRM layer blindly dereferences the send/request + * indice/size arrays even though they are userland + * pointers. -DaveM + */ +static int drm32_dma(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_dma_t *uarg = (drm32_dma_t *) arg; + int *u_si, *u_ss, *u_ri, *u_rs; + drm_dma_t karg; + mm_segment_t old_fs; + int ret; + u32 tmp1, tmp2, tmp3, tmp4; + + karg.send_indices = karg.send_sizes = NULL; + karg.request_indices = karg.request_sizes = NULL; + + if (get_user(karg.context, &uarg->context) || + get_user(karg.send_count, &uarg->send_count) || + get_user(tmp1, &uarg->send_indices) || + get_user(tmp2, &uarg->send_sizes) || + get_user(karg.flags, &uarg->flags) || + get_user(karg.request_count, &uarg->request_count) || + get_user(karg.request_size, &uarg->request_size) || + get_user(tmp3, &uarg->request_indices) || + get_user(tmp4, &uarg->request_sizes) || + get_user(karg.granted_count, &uarg->granted_count)) + return -EFAULT; + + u_si = (int *) A(tmp1); + u_ss = (int *) A(tmp2); + u_ri = (int *) A(tmp3); + u_rs = (int *) A(tmp4); + + if (karg.send_count) { + karg.send_indices = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + karg.send_sizes = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.send_indices || !karg.send_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.send_indices, u_si, + (karg.send_count * sizeof(int))) || + copy_from_user(karg.send_sizes, u_ss, + (karg.send_count * sizeof(int)))) + goto out; + } + + if (karg.request_count) { + karg.request_indices = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + karg.request_sizes = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.request_indices || !karg.request_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.request_indices, u_ri, + (karg.request_count * sizeof(int))) || + copy_from_user(karg.request_sizes, u_rs, + (karg.request_count * sizeof(int)))) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_DMA, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (put_user(karg.context, &uarg->context) || + put_user(karg.send_count, &uarg->send_count) || + put_user(karg.flags, &uarg->flags) || + put_user(karg.request_count, &uarg->request_count) || + put_user(karg.request_size, &uarg->request_size) || + put_user(karg.granted_count, &uarg->granted_count)) + ret = -EFAULT; + + if (karg.send_count) { + if (copy_to_user(u_si, karg.send_indices, + (karg.send_count * sizeof(int))) || + copy_to_user(u_ss, karg.send_sizes, + (karg.send_count * sizeof(int)))) + ret = -EFAULT; + } + if (karg.request_count) { + if (copy_to_user(u_ri, karg.request_indices, + (karg.request_count * sizeof(int))) || + copy_to_user(u_rs, karg.request_sizes, + (karg.request_count * sizeof(int)))) + ret = -EFAULT; + } + } + +out: + if (karg.send_indices) + kfree(karg.send_indices); + if (karg.send_sizes) + kfree(karg.send_sizes); + if (karg.request_indices) + kfree(karg.request_indices); + if (karg.request_sizes) + kfree(karg.request_sizes); + + return ret; +} + +typedef struct drm32_ctx_res { + int count; + u32 contexts; /* (drm_ctx_t *) */ +} drm32_ctx_res_t; +#define DRM32_IOCTL_RES_CTX DRM_IOWR(0x26, drm32_ctx_res_t) + +static int drm32_res_ctx(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_ctx_res_t *uarg = (drm32_ctx_res_t *) arg; + drm_ctx_t *ulist; + drm_ctx_res_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + karg.contexts = NULL; + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->contexts)) + return -EFAULT; + + ulist = (drm_ctx_t *) A(tmp); + + orig_count = karg.count; + if (karg.count && ulist) { + karg.contexts = kmalloc((karg.count * sizeof(drm_ctx_t)), GFP_KERNEL); + if (!karg.contexts) + return -ENOMEM; + if (copy_from_user(karg.contexts, ulist, + (karg.count * sizeof(drm_ctx_t)))) { + kfree(karg.contexts); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_RES_CTX, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (orig_count) { + if (copy_to_user(ulist, karg.contexts, + (orig_count * sizeof(drm_ctx_t)))) + ret = -EFAULT; + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + if (karg.contexts) + kfree(karg.contexts); + + return ret; +} + +#endif + +static int ret_einval(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return -EINVAL; +} + +static int broken_blkgetsize(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* The mkswap binary hard codes it to Intel value :-((( */ + return w_long(fd, BLKGETSIZE, arg); +} + +struct blkpg_ioctl_arg32 { + int op; + int flags; + int datalen; + u32 data; +}; + +static int blkpg_ioctl_trans(unsigned int fd, unsigned int cmd, struct blkpg_ioctl_arg32 *arg) +{ + struct blkpg_ioctl_arg a; + struct blkpg_partition p; + int err; + mm_segment_t old_fs = get_fs(); + + err = get_user(a.op, &arg->op); + err |= __get_user(a.flags, &arg->flags); + err |= __get_user(a.datalen, &arg->datalen); + err |= __get_user((long)a.data, &arg->data); + if (err) return err; + switch (a.op) { + case BLKPG_ADD_PARTITION: + case BLKPG_DEL_PARTITION: + if (a.datalen < sizeof(struct blkpg_partition)) + return -EINVAL; + if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) + return -EFAULT; + a.data = &p; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&a); + set_fs (old_fs); + default: + return -EINVAL; + } + return err; +} + +static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return rw_long(fd, AUTOFS_IOC_SETTIMEOUT, arg); +} + +#if 0 +struct usbdevfs_ctrltransfer32 { + __u8 requesttype; + __u8 request; + __u16 value; + __u16 index; + __u16 length; + __u32 timeout; /* in milliseconds */ + __u32 data; +}; + +#define USBDEVFS_CONTROL32 _IOWR('U', 0, struct usbdevfs_ctrltransfer32) + +static int do_usbdevfs_control(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct usbdevfs_ctrltransfer kctrl; + struct usbdevfs_ctrltransfer32 *uctrl; + mm_segment_t old_fs; + __u32 udata; + void *uptr, *kptr; + int err; + + uctrl = (struct usbdevfs_ctrltransfer32 *) arg; + + if (copy_from_user(&kctrl, uctrl, + (sizeof(struct usbdevfs_ctrltransfer) - + sizeof(void *)))) + return -EFAULT; + + if (get_user(udata, &uctrl->data)) + return -EFAULT; + uptr = (void *) A(udata); + + /* In usbdevice_fs, it limits the control buffer to a page, + * for simplicity so do we. + */ + if (!uptr || kctrl.length > PAGE_SIZE) + return -EINVAL; + + kptr = (void *)__get_free_page(GFP_KERNEL); + + if ((kctrl.requesttype & 0x80) == 0) { + err = -EFAULT; + if (copy_from_user(kptr, uptr, kctrl.length)) + goto out; + } + + kctrl.data = kptr; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_ioctl(fd, USBDEVFS_CONTROL, (unsigned long)&kctrl); + set_fs(old_fs); + + if (err >= 0 && + ((kctrl.requesttype & 0x80) != 0)) { + if (copy_to_user(uptr, kptr, kctrl.length)) + err = -EFAULT; + } + +out: + free_page((unsigned long) kptr); + return err; +} + +struct usbdevfs_bulktransfer32 { + unsigned int ep; + unsigned int len; + unsigned int timeout; /* in milliseconds */ + __u32 data; +}; + +#define USBDEVFS_BULK32 _IOWR('U', 2, struct usbdevfs_bulktransfer32) + +static int do_usbdevfs_bulk(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct usbdevfs_bulktransfer kbulk; + struct usbdevfs_bulktransfer32 *ubulk; + mm_segment_t old_fs; + __u32 udata; + void *uptr, *kptr; + int err; + + ubulk = (struct usbdevfs_bulktransfer32 *) arg; + + if (get_user(kbulk.ep, &ubulk->ep) || + get_user(kbulk.len, &ubulk->len) || + get_user(kbulk.timeout, &ubulk->timeout) || + get_user(udata, &ubulk->data)) + return -EFAULT; + + uptr = (void *) A(udata); + + /* In usbdevice_fs, it limits the control buffer to a page, + * for simplicity so do we. + */ + if (!uptr || kbulk.len > PAGE_SIZE) + return -EINVAL; + + kptr = (void *) __get_free_page(GFP_KERNEL); + + if ((kbulk.ep & 0x80) == 0) { + err = -EFAULT; + if (copy_from_user(kptr, uptr, kbulk.len)) + goto out; + } + + kbulk.data = kptr; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_ioctl(fd, USBDEVFS_BULK, (unsigned long) &kbulk); + set_fs(old_fs); + + if (err >= 0 && + ((kbulk.ep & 0x80) != 0)) { + if (copy_to_user(uptr, kptr, kbulk.len)) + err = -EFAULT; + } + +out: + free_page((unsigned long) kptr); + return err; +} + +/* This needs more work before we can enable it. Unfortunately + * because of the fancy asynchronous way URB status/error is written + * back to userspace, we'll need to fiddle with USB devio internals + * and/or reimplement entirely the frontend of it ourselves. -DaveM + * + * The issue is: + * + * When an URB is submitted via usbdevicefs it is put onto an + * asynchronous queue. When the URB completes, it may be reaped + * via another ioctl. During this reaping the status is written + * back to userspace along with the length of the transfer. + * + * We must translate into 64-bit kernel types so we pass in a kernel + * space copy of the usbdevfs_urb structure. This would mean that we + * must do something to deal with the async entry reaping. First we + * have to deal somehow with this transitory memory we've allocated. + * This is problematic since there are many call sites from which the + * async entries can be destroyed (and thus when we'd need to free up + * this kernel memory). One of which is the close() op of usbdevicefs. + * To handle that we'd need to make our own file_operations struct which + * overrides usbdevicefs's release op with our own which runs usbdevicefs's + * real release op then frees up the kernel memory. + * + * But how to keep track of these kernel buffers? We'd need to either + * keep track of them in some table _or_ know about usbdevicefs internals + * (ie. the exact layout of it's file private, which is actually defined + * in linux/usbdevice_fs.h, the layout of the async queues are private to + * devio.c) + * + * There is one possible other solution I considered, also involving knowledge + * of usbdevicefs internals: + * + * After an URB is submitted, we "fix up" the address back to the user + * space one. This would work if the status/length fields written back + * by the async URB completion lines up perfectly in the 32-bit type with + * the 64-bit kernel type. Unfortunately, it does not because the iso + * frame descriptors, at the end of the struct, can be written back. + * + * I think we'll just need to simply duplicate the devio URB engine here. + */ +#if 0 +struct usbdevfs_urb32 { + __u8 type; + __u8 endpoint; + __s32 status; + __u32 flags; + __u32 buffer; + __s32 buffer_length; + __s32 actual_length; + __s32 start_frame; + __s32 number_of_packets; + __s32 error_count; + __u32 signr; + __u32 usercontext; /* unused */ + struct usbdevfs_iso_packet_desc iso_frame_desc[0]; +}; + +#define USBDEVFS_SUBMITURB32 _IOR('U', 10, struct usbdevfs_urb32) + +static int get_urb32(struct usbdevfs_urb *kurb, + struct usbdevfs_urb32 *uurb) +{ + if (get_user(kurb->type, &uurb->type) || + __get_user(kurb->endpoint, &uurb->endpoint) || + __get_user(kurb->status, &uurb->status) || + __get_user(kurb->flags, &uurb->flags) || + __get_user(kurb->buffer_length, &uurb->buffer_length) || + __get_user(kurb->actual_length, &uurb->actual_length) || + __get_user(kurb->start_frame, &uurb->start_frame) || + __get_user(kurb->number_of_packets, &uurb->number_of_packets) || + __get_user(kurb->error_count, &uurb->error_count) || + __get_user(kurb->signr, &uurb->signr)) + return -EFAULT; + + kurb->usercontext = 0; /* unused currently */ + + return 0; +} + +/* Just put back the values which usbdevfs actually changes. */ +static int put_urb32(struct usbdevfs_urb *kurb, + struct usbdevfs_urb32 *uurb) +{ + if (put_user(kurb->status, &uurb->status) || + __put_user(kurb->actual_length, &uurb->actual_length) || + __put_user(kurb->error_count, &uurb->error_count)) + return -EFAULT; + + if (kurb->number_of_packets != 0) { + int i; + + for (i = 0; i < kurb->number_of_packets; i++) { + if (__put_user(kurb->iso_frame_desc[i].actual_length, + &uurb->iso_frame_desc[i].actual_length) || + __put_user(kurb->iso_frame_desc[i].status, + &uurb->iso_frame_desc[i].status)) + return -EFAULT; + } + } + + return 0; +} + +static int get_urb32_isoframes(struct usbdevfs_urb *kurb, + struct usbdevfs_urb32 *uurb) +{ + unsigned int totlen; + int i; + + if (kurb->type != USBDEVFS_URB_TYPE_ISO) { + kurb->number_of_packets = 0; + return 0; + } + + if (kurb->number_of_packets < 1 || + kurb->number_of_packets > 128) + return -EINVAL; + + if (copy_from_user(&kurb->iso_frame_desc[0], + &uurb->iso_frame_desc[0], + sizeof(struct usbdevfs_iso_packet_desc) * + kurb->number_of_packets)) + return -EFAULT; + + totlen = 0; + for (i = 0; i < kurb->number_of_packets; i++) { + unsigned int this_len; + + this_len = kurb->iso_frame_desc[i].length; + if (this_len > 1023) + return -EINVAL; + + totlen += this_len; + } + + if (totlen > 32768) + return -EINVAL; + + kurb->buffer_length = totlen; + + return 0; +} + +static int do_usbdevfs_urb(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct usbdevfs_urb *kurb; + struct usbdevfs_urb32 *uurb; + mm_segment_t old_fs; + __u32 udata; + void *uptr, *kptr; + unsigned int buflen; + int err; + + uurb = (struct usbdevfs_urb32 *) arg; + + err = -ENOMEM; + kurb = kmalloc(sizeof(struct usbdevfs_urb) + + (sizeof(struct usbdevfs_iso_packet_desc) * 128), + GFP_KERNEL); + if (!kurb) + goto out; + + err = -EFAULT; + if (get_urb32(kurb, uurb)) + goto out; + + err = get_urb32_isoframes(kurb, uurb); + if (err) + goto out; + + err = -EFAULT; + if (__get_user(udata, &uurb->buffer)) + goto out; + uptr = (void *) A(udata); + + err = -ENOMEM; + buflen = kurb->buffer_length; + kptr = kmalloc(buflen, GFP_KERNEL); + if (!kptr) + goto out; + + kurb->buffer = kptr; + + err = -EFAULT; + if (copy_from_user(kptr, uptr, buflen)) + goto out_kptr; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_ioctl(fd, USBDEVFS_SUBMITURB, (unsigned long) kurb); + set_fs(old_fs); + + if (err >= 0) { + /* XXX Shit, this doesn't work for async URBs :-( XXX */ + if (put_urb32(kurb, uurb)) { + err = -EFAULT; + } else if ((kurb->endpoint & USB_DIR_IN) != 0) { + if (copy_to_user(uptr, kptr, buflen)) + err = -EFAULT; + } + } + +out_kptr: + kfree(kptr); + +out: + kfree(kurb); + return err; +} +#endif + +#define USBDEVFS_REAPURB32 _IOW('U', 12, u32) +#define USBDEVFS_REAPURBNDELAY32 _IOW('U', 13, u32) + +static int do_usbdevfs_reapurb(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs; + void *kptr; + int err; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_ioctl(fd, + (cmd == USBDEVFS_REAPURB32 ? + USBDEVFS_REAPURB : + USBDEVFS_REAPURBNDELAY), + (unsigned long) &kptr); + set_fs(old_fs); + + if (err >= 0 && + put_user(((u32)(long)kptr), (u32 *) A(arg))) + err = -EFAULT; + + return err; +} + +struct usbdevfs_disconnectsignal32 { + unsigned int signr; + u32 context; +}; + +#define USBDEVFS_DISCSIGNAL32 _IOR('U', 14, struct usbdevfs_disconnectsignal32) + +static int do_usbdevfs_discsignal(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct usbdevfs_disconnectsignal kdis; + struct usbdevfs_disconnectsignal32 *udis; + mm_segment_t old_fs; + u32 uctx; + int err; + + udis = (struct usbdevfs_disconnectsignal32 *) arg; + + if (get_user(kdis.signr, &udis->signr) || + __get_user(uctx, &udis->context)) + return -EFAULT; + + kdis.context = (void *) (long)uctx; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_ioctl(fd, USBDEVFS_DISCSIGNAL, (unsigned long) &kdis); + set_fs(old_fs); + + return err; +} +#endif + +struct mtd_oob_buf32 { + u32 start; + u32 length; + u32 ptr; /* unsigned char* */ +}; + +#define MEMWRITEOOB32 _IOWR('M',3,struct mtd_oob_buf32) +#define MEMREADOOB32 _IOWR('M',4,struct mtd_oob_buf32) + +static inline int +mtd_rw_oob(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct mtd_oob_buf32 *uarg = (struct mtd_oob_buf32 *)arg; + struct mtd_oob_buf karg; + u32 tmp; + char *ptr; + int ret; + + if (get_user(karg.start, &uarg->start) || + get_user(karg.length, &uarg->length) || + get_user(tmp, &uarg->ptr)) + return -EFAULT; + + ptr = (char *)A(tmp); + if (0 >= karg.length) + return -EINVAL; + + karg.ptr = kmalloc(karg.length, GFP_KERNEL); + if (NULL == karg.ptr) + return -ENOMEM; + + if (copy_from_user(karg.ptr, ptr, karg.length)) { + kfree(karg.ptr); + return -EFAULT; + } + + set_fs(KERNEL_DS); + if (MEMREADOOB32 == cmd) + ret = sys_ioctl(fd, MEMREADOOB, (unsigned long)&karg); + else if (MEMWRITEOOB32 == cmd) + ret = sys_ioctl(fd, MEMWRITEOOB, (unsigned long)&karg); + else + ret = -EINVAL; + set_fs(old_fs); + + if (0 == ret && cmd == MEMREADOOB32) { + ret = copy_to_user(ptr, karg.ptr, karg.length); + ret |= put_user(karg.start, &uarg->start); + ret |= put_user(karg.length, &uarg->length); + } + + kfree(karg.ptr); + return ((0 == ret) ? 0 : -EFAULT); +} + +struct sg_io_hdr_32 +{ + int interface_id; + int dxfer_direction; + unsigned char cmd_len; + unsigned char mx_sb_len; + unsigned short iovec_count; + unsigned int dxfer_len; + u32 dxferp; + u32 cmdp; + u32 sbp; + unsigned int timeout; + unsigned int flags; + int pack_id; + u32 usr_ptr; + unsigned char status; + unsigned char masked_status; + unsigned char msg_status; + unsigned char sb_len_wr; + unsigned short host_status; + unsigned short driver_status; + int resid; + unsigned int duration; + unsigned int info; +}; + +static int do_sg_io(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct sg_io_hdr *sg = kmalloc(sizeof(struct sg_io_hdr), GFP_KERNEL); + struct sg_io_hdr_32 *sg_32 = (struct sg_io_hdr_32 *)arg; + u32 dxferp_32; + u32 cmdp_32; + u32 sbp_32; + u32 usr_ptr_32; + int ret = -EFAULT; + int err; + mm_segment_t old_fs = get_fs(); + + if (!sg) + return -ENOMEM; + + memset(sg, 0, sizeof(*sg)); + + err = copy_from_user(sg, sg_32, offsetof(struct sg_io_hdr, dxferp)); + err |= __get_user(dxferp_32, &sg_32->dxferp); + err |= __get_user(cmdp_32, &sg_32->cmdp); + err |= __get_user(sbp_32, &sg_32->sbp); + + if (err) + goto error; + + sg->dxferp = (void *)A(dxferp_32); + sg->cmdp = (void *)A(cmdp_32); + sg->sbp = (void *)A(sbp_32); + + err = __copy_from_user(&sg->timeout, &sg_32->timeout, + (long)&sg->usr_ptr - (long)&sg->timeout); + + err |= __get_user(usr_ptr_32, &sg_32->usr_ptr); + + if (err) + goto error; + + sg->usr_ptr = (void *)A(usr_ptr_32); + + err = __copy_from_user(&sg->status, &sg_32->status, + sizeof(struct sg_io_hdr) - + offsetof(struct sg_io_hdr, status)); + + if (err) + goto error; + + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, cmd, (unsigned long)sg); + set_fs(old_fs); + + err = copy_to_user(sg_32, sg, offsetof(struct sg_io_hdr, dxferp)); + + dxferp_32 = (unsigned long)sg->dxferp; + cmdp_32 = (unsigned long)sg->cmdp; + sbp_32 = (unsigned long)sg->sbp; + err |= __put_user(dxferp_32, &sg_32->dxferp); + err |= __put_user(cmdp_32, &sg_32->cmdp); + err |= __put_user(sbp_32, &sg_32->sbp); + + if (err) { + ret = -EFAULT; + goto error; + } + + err = __copy_to_user(&sg_32->timeout, &sg->timeout, + (long)&sg->usr_ptr - (long)&sg->timeout); + + usr_ptr_32 = (unsigned long)sg->usr_ptr; + err |= __put_user(usr_ptr_32, &sg_32->usr_ptr); + + if (err) { + ret = -EFAULT; + goto error; + } + + err = __copy_to_user(&sg_32->status, &sg->status, + sizeof(struct sg_io_hdr) - + offsetof(struct sg_io_hdr, status)); + + if (err) + ret = -EFAULT; + +error: + kfree(sg); + return ret; +} + +struct ioctl_trans { + unsigned long cmd; + unsigned long handler; + unsigned long next; +}; + +#define COMPATIBLE_IOCTL(cmd) { cmd, (unsigned long)sys_ioctl, 0 } + +#define HANDLE_IOCTL(cmd,handler) { cmd, (unsigned long)handler, 0 } + +#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,unsigned int) +#define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, __kernel_uid_t32) + +static struct ioctl_trans ioctl_translations[] = { + /* List here explicitly which ioctl's need translation, + * all others default to calling sys_ioctl(). + */ +/* Big T */ +COMPATIBLE_IOCTL(TCGETA), +COMPATIBLE_IOCTL(TCSETA), +COMPATIBLE_IOCTL(TCSETAW), +COMPATIBLE_IOCTL(TCSETAF), +COMPATIBLE_IOCTL(TCSBRK), +COMPATIBLE_IOCTL(TCXONC), +COMPATIBLE_IOCTL(TCFLSH), +COMPATIBLE_IOCTL(TCGETS), +COMPATIBLE_IOCTL(TCSETS), +COMPATIBLE_IOCTL(TCSETSW), +COMPATIBLE_IOCTL(TCSETSF), +COMPATIBLE_IOCTL(TIOCLINUX), +COMPATIBLE_IOCTL(TIOCSTART), +/* Little t */ +COMPATIBLE_IOCTL(TIOCGETD), +COMPATIBLE_IOCTL(TIOCSETD), +COMPATIBLE_IOCTL(TIOCEXCL), +COMPATIBLE_IOCTL(TIOCNXCL), +COMPATIBLE_IOCTL(TIOCCONS), +COMPATIBLE_IOCTL(TIOCGSOFTCAR), +COMPATIBLE_IOCTL(TIOCSSOFTCAR), +COMPATIBLE_IOCTL(TIOCSWINSZ), +COMPATIBLE_IOCTL(TIOCGWINSZ), +COMPATIBLE_IOCTL(TIOCMGET), +COMPATIBLE_IOCTL(TIOCMBIC), +COMPATIBLE_IOCTL(TIOCMBIS), +COMPATIBLE_IOCTL(TIOCMSET), +COMPATIBLE_IOCTL(TIOCPKT), +COMPATIBLE_IOCTL(TIOCNOTTY), +COMPATIBLE_IOCTL(TIOCSTI), +COMPATIBLE_IOCTL(TIOCOUTQ), +COMPATIBLE_IOCTL(TIOCSPGRP), +COMPATIBLE_IOCTL(TIOCGPGRP), +COMPATIBLE_IOCTL(TIOCSCTTY), +COMPATIBLE_IOCTL(TIOCGPTN), +COMPATIBLE_IOCTL(TIOCSPTLCK), +COMPATIBLE_IOCTL(TIOCGSERIAL), +COMPATIBLE_IOCTL(TIOCSSERIAL), +COMPATIBLE_IOCTL(TIOCSERGETLSR), +COMPATIBLE_IOCTL(TIOCSLTC), +/* Big F */ +COMPATIBLE_IOCTL(FBIOGET_VSCREENINFO), +COMPATIBLE_IOCTL(FBIOPUT_VSCREENINFO), +COMPATIBLE_IOCTL(FBIOPAN_DISPLAY), +COMPATIBLE_IOCTL(FBIOGET_FCURSORINFO), +COMPATIBLE_IOCTL(FBIOGET_VCURSORINFO), +COMPATIBLE_IOCTL(FBIOPUT_VCURSORINFO), +COMPATIBLE_IOCTL(FBIOGET_CURSORSTATE), +COMPATIBLE_IOCTL(FBIOPUT_CURSORSTATE), +COMPATIBLE_IOCTL(FBIOGET_CON2FBMAP), +COMPATIBLE_IOCTL(FBIOPUT_CON2FBMAP), +#if 0 +COMPATIBLE_IOCTL(FBIOBLANK), +#endif +/* Little f */ +COMPATIBLE_IOCTL(FIOCLEX), +COMPATIBLE_IOCTL(FIONCLEX), +COMPATIBLE_IOCTL(FIOASYNC), +COMPATIBLE_IOCTL(FIONBIO), +COMPATIBLE_IOCTL(FIONREAD), /* This is also TIOCINQ */ +/* 0x00 */ +COMPATIBLE_IOCTL(FIBMAP), +COMPATIBLE_IOCTL(FIGETBSZ), +/* 0x03 -- HD/IDE ioctl's used by hdparm and friends. + * Some need translations, these do not. + */ +COMPATIBLE_IOCTL(HDIO_GET_IDENTITY), +COMPATIBLE_IOCTL(HDIO_SET_DMA), +COMPATIBLE_IOCTL(HDIO_SET_KEEPSETTINGS), +COMPATIBLE_IOCTL(HDIO_SET_UNMASKINTR), +COMPATIBLE_IOCTL(HDIO_SET_NOWERR), +COMPATIBLE_IOCTL(HDIO_SET_32BIT), +COMPATIBLE_IOCTL(HDIO_SET_MULTCOUNT), +COMPATIBLE_IOCTL(HDIO_DRIVE_CMD), +COMPATIBLE_IOCTL(HDIO_SET_PIO_MODE), +COMPATIBLE_IOCTL(HDIO_SCAN_HWIF), +COMPATIBLE_IOCTL(HDIO_SET_NICE), +/* 0x02 -- Floppy ioctls */ +COMPATIBLE_IOCTL(FDMSGON), +COMPATIBLE_IOCTL(FDMSGOFF), +COMPATIBLE_IOCTL(FDSETEMSGTRESH), +COMPATIBLE_IOCTL(FDFLUSH), +COMPATIBLE_IOCTL(FDWERRORCLR), +COMPATIBLE_IOCTL(FDSETMAXERRS), +COMPATIBLE_IOCTL(FDGETMAXERRS), +COMPATIBLE_IOCTL(FDGETDRVTYP), +COMPATIBLE_IOCTL(FDEJECT), +COMPATIBLE_IOCTL(FDCLRPRM), +COMPATIBLE_IOCTL(FDFMTBEG), +COMPATIBLE_IOCTL(FDFMTEND), +COMPATIBLE_IOCTL(FDRESET), +COMPATIBLE_IOCTL(FDTWADDLE), +COMPATIBLE_IOCTL(FDFMTTRK), +COMPATIBLE_IOCTL(FDRAWCMD), +/* 0x12 */ +COMPATIBLE_IOCTL(BLKROSET), +COMPATIBLE_IOCTL(BLKROGET), +COMPATIBLE_IOCTL(BLKRRPART), +COMPATIBLE_IOCTL(BLKFLSBUF), +COMPATIBLE_IOCTL(BLKRASET), +COMPATIBLE_IOCTL(BLKFRASET), +COMPATIBLE_IOCTL(BLKSECTSET), +COMPATIBLE_IOCTL(BLKSSZGET), +COMPATIBLE_IOCTL(BLKBSZGET), +COMPATIBLE_IOCTL(BLKBSZSET), +COMPATIBLE_IOCTL(BLKGETSIZE64), + +/* RAID */ +COMPATIBLE_IOCTL(RAID_VERSION), +COMPATIBLE_IOCTL(GET_ARRAY_INFO), +COMPATIBLE_IOCTL(GET_DISK_INFO), +COMPATIBLE_IOCTL(PRINT_RAID_DEBUG), +COMPATIBLE_IOCTL(CLEAR_ARRAY), +COMPATIBLE_IOCTL(ADD_NEW_DISK), +COMPATIBLE_IOCTL(HOT_REMOVE_DISK), +COMPATIBLE_IOCTL(SET_ARRAY_INFO), +COMPATIBLE_IOCTL(SET_DISK_INFO), +COMPATIBLE_IOCTL(WRITE_RAID_INFO), +COMPATIBLE_IOCTL(UNPROTECT_ARRAY), +COMPATIBLE_IOCTL(PROTECT_ARRAY), +COMPATIBLE_IOCTL(HOT_ADD_DISK), +COMPATIBLE_IOCTL(SET_DISK_FAULTY), +COMPATIBLE_IOCTL(RUN_ARRAY), +COMPATIBLE_IOCTL(START_ARRAY), +COMPATIBLE_IOCTL(STOP_ARRAY), +COMPATIBLE_IOCTL(STOP_ARRAY_RO), +COMPATIBLE_IOCTL(RESTART_ARRAY_RW), +/* Big K */ +COMPATIBLE_IOCTL(PIO_FONT), +COMPATIBLE_IOCTL(GIO_FONT), +COMPATIBLE_IOCTL(KDSIGACCEPT), +COMPATIBLE_IOCTL(KDGETKEYCODE), +COMPATIBLE_IOCTL(KDSETKEYCODE), +COMPATIBLE_IOCTL(KIOCSOUND), +COMPATIBLE_IOCTL(KDMKTONE), +COMPATIBLE_IOCTL(KDGKBTYPE), +COMPATIBLE_IOCTL(KDSETMODE), +COMPATIBLE_IOCTL(KDGETMODE), +COMPATIBLE_IOCTL(KDSKBMODE), +COMPATIBLE_IOCTL(KDGKBMODE), +COMPATIBLE_IOCTL(KDSKBMETA), +COMPATIBLE_IOCTL(KDGKBMETA), +COMPATIBLE_IOCTL(KDGKBENT), +COMPATIBLE_IOCTL(KDSKBENT), +COMPATIBLE_IOCTL(KDGKBSENT), +COMPATIBLE_IOCTL(KDSKBSENT), +COMPATIBLE_IOCTL(KDGKBDIACR), +COMPATIBLE_IOCTL(KDKBDREP), +COMPATIBLE_IOCTL(KDSKBDIACR), +COMPATIBLE_IOCTL(KDGKBLED), +COMPATIBLE_IOCTL(KDSKBLED), +COMPATIBLE_IOCTL(KDGETLED), +COMPATIBLE_IOCTL(KDSETLED), +COMPATIBLE_IOCTL(GIO_SCRNMAP), +COMPATIBLE_IOCTL(PIO_SCRNMAP), +COMPATIBLE_IOCTL(GIO_UNISCRNMAP), +COMPATIBLE_IOCTL(PIO_UNISCRNMAP), +COMPATIBLE_IOCTL(PIO_FONTRESET), +COMPATIBLE_IOCTL(PIO_UNIMAPCLR), +/* Big S */ +COMPATIBLE_IOCTL(SCSI_IOCTL_GET_IDLUN), +COMPATIBLE_IOCTL(SCSI_IOCTL_PROBE_HOST), +COMPATIBLE_IOCTL(SCSI_IOCTL_DOORLOCK), +COMPATIBLE_IOCTL(SCSI_IOCTL_DOORUNLOCK), +COMPATIBLE_IOCTL(SCSI_IOCTL_TEST_UNIT_READY), +COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_ENABLE), +COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_DISABLE), +COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER), +COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND), +/* Big V */ +COMPATIBLE_IOCTL(VT_SETMODE), +COMPATIBLE_IOCTL(VT_GETMODE), +COMPATIBLE_IOCTL(VT_GETSTATE), +COMPATIBLE_IOCTL(VT_OPENQRY), +COMPATIBLE_IOCTL(VT_ACTIVATE), +COMPATIBLE_IOCTL(VT_WAITACTIVE), +COMPATIBLE_IOCTL(VT_RELDISP), +COMPATIBLE_IOCTL(VT_DISALLOCATE), +COMPATIBLE_IOCTL(VT_RESIZE), +COMPATIBLE_IOCTL(VT_RESIZEX), +COMPATIBLE_IOCTL(VT_LOCKSWITCH), +COMPATIBLE_IOCTL(VT_UNLOCKSWITCH), +/* Little v, the video4linux ioctls */ +COMPATIBLE_IOCTL(VIDIOCGCAP), +COMPATIBLE_IOCTL(VIDIOCGCHAN), +COMPATIBLE_IOCTL(VIDIOCSCHAN), +COMPATIBLE_IOCTL(VIDIOCGPICT), +COMPATIBLE_IOCTL(VIDIOCSPICT), +COMPATIBLE_IOCTL(VIDIOCCAPTURE), +COMPATIBLE_IOCTL(VIDIOCKEY), +COMPATIBLE_IOCTL(VIDIOCGAUDIO), +COMPATIBLE_IOCTL(VIDIOCSAUDIO), +COMPATIBLE_IOCTL(VIDIOCSYNC), +COMPATIBLE_IOCTL(VIDIOCMCAPTURE), +COMPATIBLE_IOCTL(VIDIOCGMBUF), +COMPATIBLE_IOCTL(VIDIOCGUNIT), +COMPATIBLE_IOCTL(VIDIOCGCAPTURE), +COMPATIBLE_IOCTL(VIDIOCSCAPTURE), +/* BTTV specific... */ +COMPATIBLE_IOCTL(_IOW('v', BASE_VIDIOCPRIVATE+0, char [256])), +COMPATIBLE_IOCTL(_IOR('v', BASE_VIDIOCPRIVATE+1, char [256])), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)), +COMPATIBLE_IOCTL(_IOW('v' , BASE_VIDIOCPRIVATE+3, char [16])), /* struct bttv_pll_info */ +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+4, int)), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+5, int)), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+6, int)), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+7, int)), +/* Little p (/dev/rtc, /dev/envctrl, etc.) */ +COMPATIBLE_IOCTL(_IOR('p', 20, int[7])), /* RTCGET */ +COMPATIBLE_IOCTL(_IOW('p', 21, int[7])), /* RTCSET */ +COMPATIBLE_IOCTL(RTC_AIE_ON), +COMPATIBLE_IOCTL(RTC_AIE_OFF), +COMPATIBLE_IOCTL(RTC_UIE_ON), +COMPATIBLE_IOCTL(RTC_UIE_OFF), +COMPATIBLE_IOCTL(RTC_PIE_ON), +COMPATIBLE_IOCTL(RTC_PIE_OFF), +COMPATIBLE_IOCTL(RTC_WIE_ON), +COMPATIBLE_IOCTL(RTC_WIE_OFF), +COMPATIBLE_IOCTL(RTC_ALM_SET), +COMPATIBLE_IOCTL(RTC_ALM_READ), +COMPATIBLE_IOCTL(RTC_RD_TIME), +COMPATIBLE_IOCTL(RTC_SET_TIME), +COMPATIBLE_IOCTL(RTC_WKALM_SET), +COMPATIBLE_IOCTL(RTC_WKALM_RD), +/* Little m */ +COMPATIBLE_IOCTL(MTIOCTOP), +/* Socket level stuff */ +COMPATIBLE_IOCTL(FIOSETOWN), +COMPATIBLE_IOCTL(SIOCSPGRP), +COMPATIBLE_IOCTL(FIOGETOWN), +COMPATIBLE_IOCTL(SIOCGPGRP), +COMPATIBLE_IOCTL(SIOCATMARK), +COMPATIBLE_IOCTL(SIOCSIFLINK), +COMPATIBLE_IOCTL(SIOCSIFENCAP), +COMPATIBLE_IOCTL(SIOCGIFENCAP), +COMPATIBLE_IOCTL(SIOCSIFBR), +COMPATIBLE_IOCTL(SIOCGIFBR), +COMPATIBLE_IOCTL(SIOCSARP), +COMPATIBLE_IOCTL(SIOCGARP), +COMPATIBLE_IOCTL(SIOCDARP), +COMPATIBLE_IOCTL(SIOCSRARP), +COMPATIBLE_IOCTL(SIOCGRARP), +COMPATIBLE_IOCTL(SIOCDRARP), +COMPATIBLE_IOCTL(SIOCADDDLCI), +COMPATIBLE_IOCTL(SIOCDELDLCI), +/* SG stuff */ +COMPATIBLE_IOCTL(SG_SET_TIMEOUT), +COMPATIBLE_IOCTL(SG_GET_TIMEOUT), +COMPATIBLE_IOCTL(SG_EMULATED_HOST), +COMPATIBLE_IOCTL(SG_SET_TRANSFORM), +COMPATIBLE_IOCTL(SG_GET_TRANSFORM), +COMPATIBLE_IOCTL(SG_SET_RESERVED_SIZE), +COMPATIBLE_IOCTL(SG_GET_RESERVED_SIZE), +COMPATIBLE_IOCTL(SG_GET_SCSI_ID), +COMPATIBLE_IOCTL(SG_SET_FORCE_LOW_DMA), +COMPATIBLE_IOCTL(SG_GET_LOW_DMA), +COMPATIBLE_IOCTL(SG_SET_FORCE_PACK_ID), +COMPATIBLE_IOCTL(SG_GET_PACK_ID), +COMPATIBLE_IOCTL(SG_GET_NUM_WAITING), +COMPATIBLE_IOCTL(SG_SET_DEBUG), +COMPATIBLE_IOCTL(SG_GET_SG_TABLESIZE), +COMPATIBLE_IOCTL(SG_GET_COMMAND_Q), +COMPATIBLE_IOCTL(SG_SET_COMMAND_Q), +COMPATIBLE_IOCTL(SG_GET_VERSION_NUM), +COMPATIBLE_IOCTL(SG_NEXT_CMD_LEN), +COMPATIBLE_IOCTL(SG_SCSI_RESET), +COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE), +COMPATIBLE_IOCTL(SG_SET_KEEP_ORPHAN), +COMPATIBLE_IOCTL(SG_GET_KEEP_ORPHAN), +/* PPP stuff */ +COMPATIBLE_IOCTL(PPPIOCGFLAGS), +COMPATIBLE_IOCTL(PPPIOCSFLAGS), +COMPATIBLE_IOCTL(PPPIOCGASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCSASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCGUNIT), +COMPATIBLE_IOCTL(PPPIOCGRASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCSRASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCGMRU), +COMPATIBLE_IOCTL(PPPIOCSMRU), +COMPATIBLE_IOCTL(PPPIOCSMAXCID), +COMPATIBLE_IOCTL(PPPIOCGXASYNCMAP), +COMPATIBLE_IOCTL(LPGETSTATUS), +COMPATIBLE_IOCTL(PPPIOCSXASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCXFERUNIT), +COMPATIBLE_IOCTL(PPPIOCGNPMODE), +COMPATIBLE_IOCTL(PPPIOCSNPMODE), +COMPATIBLE_IOCTL(PPPIOCGDEBUG), +COMPATIBLE_IOCTL(PPPIOCSDEBUG), +COMPATIBLE_IOCTL(PPPIOCNEWUNIT), +COMPATIBLE_IOCTL(PPPIOCATTACH), +COMPATIBLE_IOCTL(PPPIOCDETACH), +COMPATIBLE_IOCTL(PPPIOCSMRRU), +COMPATIBLE_IOCTL(PPPIOCCONNECT), +COMPATIBLE_IOCTL(PPPIOCDISCONN), +COMPATIBLE_IOCTL(PPPIOCATTCHAN), +COMPATIBLE_IOCTL(PPPIOCGCHAN), +/* PPPOX */ +COMPATIBLE_IOCTL(PPPOEIOCSFWD), +COMPATIBLE_IOCTL(PPPOEIOCDFWD), +/* CDROM stuff */ +COMPATIBLE_IOCTL(CDROMPAUSE), +COMPATIBLE_IOCTL(CDROMRESUME), +COMPATIBLE_IOCTL(CDROMPLAYMSF), +COMPATIBLE_IOCTL(CDROMPLAYTRKIND), +COMPATIBLE_IOCTL(CDROMREADTOCHDR), +COMPATIBLE_IOCTL(CDROMREADTOCENTRY), +COMPATIBLE_IOCTL(CDROMSTOP), +COMPATIBLE_IOCTL(CDROMSTART), +COMPATIBLE_IOCTL(CDROMEJECT), +COMPATIBLE_IOCTL(CDROMVOLCTRL), +COMPATIBLE_IOCTL(CDROMSUBCHNL), +COMPATIBLE_IOCTL(CDROMEJECT_SW), +COMPATIBLE_IOCTL(CDROMMULTISESSION), +COMPATIBLE_IOCTL(CDROM_GET_MCN), +COMPATIBLE_IOCTL(CDROMRESET), +COMPATIBLE_IOCTL(CDROMVOLREAD), +COMPATIBLE_IOCTL(CDROMSEEK), +COMPATIBLE_IOCTL(CDROMPLAYBLK), +COMPATIBLE_IOCTL(CDROMCLOSETRAY), +COMPATIBLE_IOCTL(CDROM_SET_OPTIONS), +COMPATIBLE_IOCTL(CDROM_CLEAR_OPTIONS), +COMPATIBLE_IOCTL(CDROM_SELECT_SPEED), +COMPATIBLE_IOCTL(CDROM_SELECT_DISC), +COMPATIBLE_IOCTL(CDROM_MEDIA_CHANGED), +COMPATIBLE_IOCTL(CDROM_DRIVE_STATUS), +COMPATIBLE_IOCTL(CDROM_DISC_STATUS), +COMPATIBLE_IOCTL(CDROM_CHANGER_NSLOTS), +COMPATIBLE_IOCTL(CDROM_LOCKDOOR), +COMPATIBLE_IOCTL(CDROM_DEBUG), +COMPATIBLE_IOCTL(CDROM_GET_CAPABILITY), +/* DVD ioctls */ +COMPATIBLE_IOCTL(DVD_READ_STRUCT), +COMPATIBLE_IOCTL(DVD_WRITE_STRUCT), +COMPATIBLE_IOCTL(DVD_AUTH), +/* Big L */ +COMPATIBLE_IOCTL(LOOP_SET_FD), +COMPATIBLE_IOCTL(LOOP_CLR_FD), +/* Big Q for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_SEQ_RESET), +COMPATIBLE_IOCTL(SNDCTL_SEQ_SYNC), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_INFO), +COMPATIBLE_IOCTL(SNDCTL_SEQ_CTRLRATE), +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETOUTCOUNT), +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETINCOUNT), +COMPATIBLE_IOCTL(SNDCTL_SEQ_PERCMODE), +COMPATIBLE_IOCTL(SNDCTL_FM_LOAD_INSTR), +COMPATIBLE_IOCTL(SNDCTL_SEQ_TESTMIDI), +COMPATIBLE_IOCTL(SNDCTL_SEQ_RESETSAMPLES), +COMPATIBLE_IOCTL(SNDCTL_SEQ_NRSYNTHS), +COMPATIBLE_IOCTL(SNDCTL_SEQ_NRMIDIS), +COMPATIBLE_IOCTL(SNDCTL_MIDI_INFO), +COMPATIBLE_IOCTL(SNDCTL_SEQ_THRESHOLD), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_MEMAVL), +COMPATIBLE_IOCTL(SNDCTL_FM_4OP_ENABLE), +COMPATIBLE_IOCTL(SNDCTL_SEQ_PANIC), +COMPATIBLE_IOCTL(SNDCTL_SEQ_OUTOFBAND), +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETTIME), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_ID), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_CONTROL), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_REMOVESAMPLE), +/* Big T for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_TMR_TIMEBASE), +COMPATIBLE_IOCTL(SNDCTL_TMR_START), +COMPATIBLE_IOCTL(SNDCTL_TMR_STOP), +COMPATIBLE_IOCTL(SNDCTL_TMR_CONTINUE), +COMPATIBLE_IOCTL(SNDCTL_TMR_TEMPO), +COMPATIBLE_IOCTL(SNDCTL_TMR_SOURCE), +COMPATIBLE_IOCTL(SNDCTL_TMR_METRONOME), +COMPATIBLE_IOCTL(SNDCTL_TMR_SELECT), +/* Little m for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_MIDI_PRETIME), +COMPATIBLE_IOCTL(SNDCTL_MIDI_MPUMODE), +COMPATIBLE_IOCTL(SNDCTL_MIDI_MPUCMD), +/* Big P for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_DSP_RESET), +COMPATIBLE_IOCTL(SNDCTL_DSP_SYNC), +COMPATIBLE_IOCTL(SNDCTL_DSP_SPEED), +COMPATIBLE_IOCTL(SNDCTL_DSP_STEREO), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETBLKSIZE), +COMPATIBLE_IOCTL(SNDCTL_DSP_CHANNELS), +COMPATIBLE_IOCTL(SOUND_PCM_WRITE_FILTER), +COMPATIBLE_IOCTL(SNDCTL_DSP_POST), +COMPATIBLE_IOCTL(SNDCTL_DSP_SUBDIVIDE), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETFRAGMENT), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETFMTS), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETFMT), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETOSPACE), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETISPACE), +COMPATIBLE_IOCTL(SNDCTL_DSP_NONBLOCK), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETCAPS), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETTRIGGER), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETTRIGGER), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETIPTR), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETOPTR), +/* SNDCTL_DSP_MAPINBUF, XXX needs translation */ +/* SNDCTL_DSP_MAPOUTBUF, XXX needs translation */ +COMPATIBLE_IOCTL(SNDCTL_DSP_SETSYNCRO), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETDUPLEX), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETODELAY), +COMPATIBLE_IOCTL(SNDCTL_DSP_PROFILE), +COMPATIBLE_IOCTL(SOUND_PCM_READ_RATE), +COMPATIBLE_IOCTL(SOUND_PCM_READ_CHANNELS), +COMPATIBLE_IOCTL(SOUND_PCM_READ_BITS), +COMPATIBLE_IOCTL(SOUND_PCM_READ_FILTER), +/* Big C for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_COPR_RESET), +COMPATIBLE_IOCTL(SNDCTL_COPR_LOAD), +COMPATIBLE_IOCTL(SNDCTL_COPR_RDATA), +COMPATIBLE_IOCTL(SNDCTL_COPR_RCODE), +COMPATIBLE_IOCTL(SNDCTL_COPR_WDATA), +COMPATIBLE_IOCTL(SNDCTL_COPR_WCODE), +COMPATIBLE_IOCTL(SNDCTL_COPR_RUN), +COMPATIBLE_IOCTL(SNDCTL_COPR_HALT), +COMPATIBLE_IOCTL(SNDCTL_COPR_SENDMSG), +COMPATIBLE_IOCTL(SNDCTL_COPR_RCVMSG), +/* Big M for sound/OSS */ +COMPATIBLE_IOCTL(SOUND_MIXER_READ_VOLUME), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_BASS), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_TREBLE), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_SYNTH), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_PCM), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_SPEAKER), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_MIC), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_CD), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_IMIX), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_ALTPCM), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECLEV), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_IGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_OGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE1), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE2), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE3), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL1)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL2)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL3)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_PHONEIN)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_PHONEOUT)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_VIDEO)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_RADIO)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_MONITOR)), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_MUTE), +/* SOUND_MIXER_READ_ENHANCE, same value as READ_MUTE */ +/* SOUND_MIXER_READ_LOUD, same value as READ_MUTE */ +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECSRC), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_DEVMASK), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECMASK), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_STEREODEVS), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_CAPS), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_VOLUME), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_BASS), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_TREBLE), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_SYNTH), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_PCM), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_SPEAKER), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_MIC), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_CD), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_IMIX), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_ALTPCM), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_RECLEV), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_IGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_OGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE1), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE2), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE3), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL1)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL2)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL3)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_PHONEIN)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_PHONEOUT)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_VIDEO)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_RADIO)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_MONITOR)), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_MUTE), +/* SOUND_MIXER_WRITE_ENHANCE, same value as WRITE_MUTE */ +/* SOUND_MIXER_WRITE_LOUD, same value as WRITE_MUTE */ +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_RECSRC), +COMPATIBLE_IOCTL(SOUND_MIXER_INFO), +COMPATIBLE_IOCTL(SOUND_OLD_MIXER_INFO), +COMPATIBLE_IOCTL(SOUND_MIXER_ACCESS), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE1), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE2), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE3), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE4), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE5), +COMPATIBLE_IOCTL(SOUND_MIXER_GETLEVELS), +COMPATIBLE_IOCTL(SOUND_MIXER_SETLEVELS), +COMPATIBLE_IOCTL(OSS_GETVERSION), +/* AUTOFS */ +COMPATIBLE_IOCTL(AUTOFS_IOC_READY), +COMPATIBLE_IOCTL(AUTOFS_IOC_FAIL), +COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC), +COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER), +COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE), +/* DEVFS */ +COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV), +COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK), +COMPATIBLE_IOCTL(DEVFSDIOC_RELEASE_EVENT_QUEUE), +COMPATIBLE_IOCTL(DEVFSDIOC_SET_DEBUG_MASK), +/* Raw devices */ +COMPATIBLE_IOCTL(RAW_SETBIND), +COMPATIBLE_IOCTL(RAW_GETBIND), +/* SMB ioctls which do not need any translations */ +COMPATIBLE_IOCTL(SMB_IOC_NEWCONN), +/* Little a */ +COMPATIBLE_IOCTL(ATMSIGD_CTRL), +COMPATIBLE_IOCTL(ATMARPD_CTRL), +COMPATIBLE_IOCTL(ATMLEC_CTRL), +COMPATIBLE_IOCTL(ATMLEC_MCAST), +COMPATIBLE_IOCTL(ATMLEC_DATA), +COMPATIBLE_IOCTL(ATM_SETSC), +COMPATIBLE_IOCTL(SIOCSIFATMTCP), +COMPATIBLE_IOCTL(SIOCMKCLIP), +COMPATIBLE_IOCTL(ATMARP_MKIP), +COMPATIBLE_IOCTL(ATMARP_SETENTRY), +COMPATIBLE_IOCTL(ATMARP_ENCAP), +COMPATIBLE_IOCTL(ATMTCP_CREATE), +COMPATIBLE_IOCTL(ATMTCP_REMOVE), +COMPATIBLE_IOCTL(ATMMPC_CTRL), +COMPATIBLE_IOCTL(ATMMPC_DATA), +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* 0xfe - lvm */ +COMPATIBLE_IOCTL(VG_SET_EXTENDABLE), +COMPATIBLE_IOCTL(VG_STATUS_GET_COUNT), +COMPATIBLE_IOCTL(VG_STATUS_GET_NAMELIST), +COMPATIBLE_IOCTL(VG_REMOVE), +COMPATIBLE_IOCTL(VG_RENAME), +COMPATIBLE_IOCTL(VG_REDUCE), +COMPATIBLE_IOCTL(PE_LOCK_UNLOCK), +COMPATIBLE_IOCTL(PV_FLUSH), +COMPATIBLE_IOCTL(LVM_LOCK_LVM), +COMPATIBLE_IOCTL(LVM_GET_IOP_VERSION), +#ifdef LVM_TOTAL_RESET +COMPATIBLE_IOCTL(LVM_RESET), +#endif +COMPATIBLE_IOCTL(LV_SET_ACCESS), +COMPATIBLE_IOCTL(LV_SET_STATUS), +COMPATIBLE_IOCTL(LV_SET_ALLOCATION), +COMPATIBLE_IOCTL(LE_REMAP), +COMPATIBLE_IOCTL(LV_BMAP), +COMPATIBLE_IOCTL(LV_SNAPSHOT_USE_RATE), +#endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +COMPATIBLE_IOCTL(DRM_IOCTL_GET_MAGIC), +COMPATIBLE_IOCTL(DRM_IOCTL_IRQ_BUSID), +COMPATIBLE_IOCTL(DRM_IOCTL_AUTH_MAGIC), +COMPATIBLE_IOCTL(DRM_IOCTL_BLOCK), +COMPATIBLE_IOCTL(DRM_IOCTL_UNBLOCK), +COMPATIBLE_IOCTL(DRM_IOCTL_CONTROL), +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_BUFS), +COMPATIBLE_IOCTL(DRM_IOCTL_MARK_BUFS), +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_RM_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_MOD_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_GET_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_SWITCH_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_NEW_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_DRAW), +COMPATIBLE_IOCTL(DRM_IOCTL_RM_DRAW), +COMPATIBLE_IOCTL(DRM_IOCTL_LOCK), +COMPATIBLE_IOCTL(DRM_IOCTL_UNLOCK), +COMPATIBLE_IOCTL(DRM_IOCTL_FINISH), +#endif /* DRM */ +/* elevator */ +COMPATIBLE_IOCTL(BLKELVGET), +COMPATIBLE_IOCTL(BLKELVSET), +/* Big W */ +/* WIOC_GETSUPPORT not yet implemented -E */ +COMPATIBLE_IOCTL(WDIOC_GETSTATUS), +COMPATIBLE_IOCTL(WDIOC_GETBOOTSTATUS), +COMPATIBLE_IOCTL(WDIOC_GETTEMP), +COMPATIBLE_IOCTL(WDIOC_SETOPTIONS), +COMPATIBLE_IOCTL(WDIOC_KEEPALIVE), +/* Bluetooth ioctls */ +COMPATIBLE_IOCTL(HCIDEVUP), +COMPATIBLE_IOCTL(HCIDEVDOWN), +COMPATIBLE_IOCTL(HCIDEVRESET), +COMPATIBLE_IOCTL(HCIRESETSTAT), +COMPATIBLE_IOCTL(HCIGETINFO), +COMPATIBLE_IOCTL(HCIGETDEVLIST), +COMPATIBLE_IOCTL(HCISETRAW), +COMPATIBLE_IOCTL(HCISETSCAN), +COMPATIBLE_IOCTL(HCISETAUTH), +COMPATIBLE_IOCTL(HCIINQUIRY), +COMPATIBLE_IOCTL(PCIIOC_CONTROLLER), +COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_IO), +COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_MEM), +COMPATIBLE_IOCTL(PCIIOC_WRITE_COMBINE), +/* USB */ +COMPATIBLE_IOCTL(USBDEVFS_RESETEP), +COMPATIBLE_IOCTL(USBDEVFS_SETINTERFACE), +COMPATIBLE_IOCTL(USBDEVFS_SETCONFIGURATION), +COMPATIBLE_IOCTL(USBDEVFS_GETDRIVER), +COMPATIBLE_IOCTL(USBDEVFS_DISCARDURB), +COMPATIBLE_IOCTL(USBDEVFS_CLAIMINTERFACE), +COMPATIBLE_IOCTL(USBDEVFS_RELEASEINTERFACE), +COMPATIBLE_IOCTL(USBDEVFS_CONNECTINFO), +COMPATIBLE_IOCTL(USBDEVFS_HUB_PORTINFO), +COMPATIBLE_IOCTL(USBDEVFS_RESET), +COMPATIBLE_IOCTL(USBDEVFS_CLEAR_HALT), +/* MTD */ +COMPATIBLE_IOCTL(MEMGETINFO), +COMPATIBLE_IOCTL(MEMERASE), +COMPATIBLE_IOCTL(MEMLOCK), +COMPATIBLE_IOCTL(MEMUNLOCK), +COMPATIBLE_IOCTL(MEMGETREGIONCOUNT), +COMPATIBLE_IOCTL(MEMGETREGIONINFO), +/* NBD */ +COMPATIBLE_IOCTL(NBD_SET_SOCK), +COMPATIBLE_IOCTL(NBD_SET_BLKSIZE), +COMPATIBLE_IOCTL(NBD_SET_SIZE), +COMPATIBLE_IOCTL(NBD_DO_IT), +COMPATIBLE_IOCTL(NBD_CLEAR_SOCK), +COMPATIBLE_IOCTL(NBD_CLEAR_QUE), +COMPATIBLE_IOCTL(NBD_PRINT_DEBUG), +COMPATIBLE_IOCTL(NBD_SET_SIZE_BLOCKS), +COMPATIBLE_IOCTL(NBD_DISCONNECT), +/* Remove *PRIVATE in 2.5 */ +COMPATIBLE_IOCTL(SIOCDEVPRIVATE), +COMPATIBLE_IOCTL(SIOCDEVPRIVATE+1), +COMPATIBLE_IOCTL(SIOCDEVPRIVATE+2), +COMPATIBLE_IOCTL(SIOCGMIIPHY), +COMPATIBLE_IOCTL(SIOCGMIIREG), +COMPATIBLE_IOCTL(SIOCSMIIREG), +/* And these ioctls need translation */ +HANDLE_IOCTL(MEMREADOOB32, mtd_rw_oob), +HANDLE_IOCTL(MEMWRITEOOB32, mtd_rw_oob), +#ifdef CONFIG_NET +HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32), +#endif +HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf), +HANDLE_IOCTL(SIOCGIFFLAGS, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFFLAGS, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFMETRIC, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFMETRIC, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFMTU, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFMTU, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFMEM, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFMEM, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFHWADDR, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFHWADDR, dev_ifsioc), +HANDLE_IOCTL(SIOCADDMULTI, dev_ifsioc), +HANDLE_IOCTL(SIOCDELMULTI, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFINDEX, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFMAP, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFMAP, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFADDR, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFADDR, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFBRDADDR, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFBRDADDR, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFDSTADDR, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFDSTADDR, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFNETMASK, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFNETMASK, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFPFLAGS, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFPFLAGS, dev_ifsioc), +HANDLE_IOCTL(SIOCGIFTXQLEN, dev_ifsioc), +HANDLE_IOCTL(SIOCSIFTXQLEN, dev_ifsioc), +HANDLE_IOCTL(SIOCETHTOOL, ethtool_ioctl), +HANDLE_IOCTL(SIOCBONDENSLAVE, bond_ioctl), +HANDLE_IOCTL(SIOCBONDRELEASE, bond_ioctl), +HANDLE_IOCTL(SIOCBONDSETHWADDR, bond_ioctl), +HANDLE_IOCTL(SIOCBONDSLAVEINFOQUERY, bond_ioctl), +HANDLE_IOCTL(SIOCBONDINFOQUERY, bond_ioctl), +HANDLE_IOCTL(SIOCBONDCHANGEACTIVE, bond_ioctl), +HANDLE_IOCTL(SIOCADDRT, routing_ioctl), +HANDLE_IOCTL(SIOCDELRT, routing_ioctl), +/* Note SIOCRTMSG is no longer, so this is safe and + * the user would have seen just an -EINVAL anyways. */ +HANDLE_IOCTL(SIOCRTMSG, ret_einval), +HANDLE_IOCTL(SIOCGSTAMP, do_siocgstamp), +HANDLE_IOCTL(HDIO_GETGEO, hdio_getgeo), +HANDLE_IOCTL(BLKRAGET, w_long), +HANDLE_IOCTL(BLKGETSIZE, w_long), +HANDLE_IOCTL(0x1260, broken_blkgetsize), +HANDLE_IOCTL(BLKFRAGET, w_long), +HANDLE_IOCTL(BLKSECTGET, w_long), +HANDLE_IOCTL(BLKPG, blkpg_ioctl_trans), +HANDLE_IOCTL(HDIO_GET_KEEPSETTINGS, hdio_ioctl_trans), +HANDLE_IOCTL(HDIO_GET_UNMASKINTR, hdio_ioctl_trans), +HANDLE_IOCTL(HDIO_GET_DMA, hdio_ioctl_trans), +HANDLE_IOCTL(HDIO_GET_32BIT, hdio_ioctl_trans), +HANDLE_IOCTL(HDIO_GET_MULTCOUNT, hdio_ioctl_trans), +HANDLE_IOCTL(HDIO_GET_NOWERR, hdio_ioctl_trans), +HANDLE_IOCTL(HDIO_GET_NICE, hdio_ioctl_trans), +HANDLE_IOCTL(FDSETPRM32, fd_ioctl_trans), +HANDLE_IOCTL(FDDEFPRM32, fd_ioctl_trans), +HANDLE_IOCTL(FDGETPRM32, fd_ioctl_trans), +HANDLE_IOCTL(FDSETDRVPRM32, fd_ioctl_trans), +HANDLE_IOCTL(FDGETDRVPRM32, fd_ioctl_trans), +HANDLE_IOCTL(FDGETDRVSTAT32, fd_ioctl_trans), +HANDLE_IOCTL(FDPOLLDRVSTAT32, fd_ioctl_trans), +HANDLE_IOCTL(FDGETFDCSTAT32, fd_ioctl_trans), +HANDLE_IOCTL(FDWERRORGET32, fd_ioctl_trans), +HANDLE_IOCTL(PPPIOCGIDLE32, ppp_ioctl_trans), +HANDLE_IOCTL(PPPIOCSCOMPRESS32, ppp_ioctl_trans), +HANDLE_IOCTL(MTIOCGET32, mt_ioctl_trans), +HANDLE_IOCTL(MTIOCPOS32, mt_ioctl_trans), +HANDLE_IOCTL(MTIOCGETCONFIG32, mt_ioctl_trans), +HANDLE_IOCTL(MTIOCSETCONFIG32, mt_ioctl_trans), +HANDLE_IOCTL(CDROMREADMODE2, cdrom_ioctl_trans), +HANDLE_IOCTL(CDROMREADMODE1, cdrom_ioctl_trans), +HANDLE_IOCTL(CDROMREADRAW, cdrom_ioctl_trans), +HANDLE_IOCTL(CDROMREADCOOKED, cdrom_ioctl_trans), +HANDLE_IOCTL(CDROMREADAUDIO, cdrom_ioctl_trans), +HANDLE_IOCTL(CDROMREADALL, cdrom_ioctl_trans), +HANDLE_IOCTL(CDROM_SEND_PACKET, cdrom_ioctl_trans), +HANDLE_IOCTL(LOOP_SET_STATUS, loop_status), +HANDLE_IOCTL(LOOP_GET_STATUS, loop_status), +HANDLE_IOCTL(AUTOFS_IOC_SETTIMEOUT32, ioc_settimeout), +#ifdef CONFIG_VT +HANDLE_IOCTL(PIO_FONTX, do_fontx_ioctl), +HANDLE_IOCTL(GIO_FONTX, do_fontx_ioctl), +HANDLE_IOCTL(PIO_UNIMAP, do_unimap_ioctl), +HANDLE_IOCTL(GIO_UNIMAP, do_unimap_ioctl), +HANDLE_IOCTL(KDFONTOP, do_kdfontop_ioctl), +HANDLE_IOCTL(FBIOGET_FSCREENINFO, do_fbioget_fscreeninfo_ioctl), +HANDLE_IOCTL(FBIOGETCMAP, do_fbiogetcmap_ioctl), +HANDLE_IOCTL(FBIOPUTCMAP, do_fbioputcmap_ioctl), +#endif +HANDLE_IOCTL(EXT2_IOC32_GETFLAGS, do_ext2_ioctl), +HANDLE_IOCTL(EXT2_IOC32_SETFLAGS, do_ext2_ioctl), +HANDLE_IOCTL(EXT2_IOC32_GETVERSION, do_ext2_ioctl), +HANDLE_IOCTL(EXT2_IOC32_SETVERSION, do_ext2_ioctl), +HANDLE_IOCTL(VIDIOCGTUNER32, do_video_ioctl), +HANDLE_IOCTL(VIDIOCSTUNER32, do_video_ioctl), +HANDLE_IOCTL(VIDIOCGWIN32, do_video_ioctl), +HANDLE_IOCTL(VIDIOCSWIN32, do_video_ioctl), +HANDLE_IOCTL(VIDIOCGFBUF32, do_video_ioctl), +HANDLE_IOCTL(VIDIOCSFBUF32, do_video_ioctl), +HANDLE_IOCTL(VIDIOCGFREQ32, do_video_ioctl), +HANDLE_IOCTL(VIDIOCSFREQ32, do_video_ioctl), +/* One SMB ioctl needs translations. */ +HANDLE_IOCTL(SMB_IOC_GETMOUNTUID_32, do_smb_getmountuid), +HANDLE_IOCTL(ATM_GETLINKRATE32, do_atm_ioctl), +HANDLE_IOCTL(ATM_GETNAMES32, do_atm_ioctl), +HANDLE_IOCTL(ATM_GETTYPE32, do_atm_ioctl), +HANDLE_IOCTL(ATM_GETESI32, do_atm_ioctl), +HANDLE_IOCTL(ATM_GETADDR32, do_atm_ioctl), +HANDLE_IOCTL(ATM_RSTADDR32, do_atm_ioctl), +HANDLE_IOCTL(ATM_ADDADDR32, do_atm_ioctl), +HANDLE_IOCTL(ATM_DELADDR32, do_atm_ioctl), +HANDLE_IOCTL(ATM_GETCIRANGE32, do_atm_ioctl), +HANDLE_IOCTL(ATM_SETCIRANGE32, do_atm_ioctl), +HANDLE_IOCTL(ATM_SETESI32, do_atm_ioctl), +HANDLE_IOCTL(ATM_SETESIF32, do_atm_ioctl), +HANDLE_IOCTL(ATM_GETSTAT32, do_atm_ioctl), +HANDLE_IOCTL(ATM_GETSTATZ32, do_atm_ioctl), +HANDLE_IOCTL(ATM_GETLOOP32, do_atm_ioctl), +HANDLE_IOCTL(ATM_SETLOOP32, do_atm_ioctl), +HANDLE_IOCTL(ATM_QUERYLOOP32, do_atm_ioctl), +HANDLE_IOCTL(SONET_GETSTAT, do_atm_ioctl), +HANDLE_IOCTL(SONET_GETSTATZ, do_atm_ioctl), +HANDLE_IOCTL(SONET_GETDIAG, do_atm_ioctl), +HANDLE_IOCTL(SONET_SETDIAG, do_atm_ioctl), +HANDLE_IOCTL(SONET_CLRDIAG, do_atm_ioctl), +HANDLE_IOCTL(SONET_SETFRAMING, do_atm_ioctl), +HANDLE_IOCTL(SONET_GETFRAMING, do_atm_ioctl), +HANDLE_IOCTL(SONET_GETFRSENSE, do_atm_ioctl), +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +HANDLE_IOCTL(VG_STATUS, do_lvm_ioctl), +HANDLE_IOCTL(VG_CREATE_OLD, do_lvm_ioctl), +HANDLE_IOCTL(VG_CREATE, do_lvm_ioctl), +HANDLE_IOCTL(VG_EXTEND, do_lvm_ioctl), +HANDLE_IOCTL(LV_CREATE, do_lvm_ioctl), +HANDLE_IOCTL(LV_REMOVE, do_lvm_ioctl), +HANDLE_IOCTL(LV_EXTEND, do_lvm_ioctl), +HANDLE_IOCTL(LV_REDUCE, do_lvm_ioctl), +HANDLE_IOCTL(LV_RENAME, do_lvm_ioctl), +HANDLE_IOCTL(LV_STATUS_BYNAME, do_lvm_ioctl), +HANDLE_IOCTL(LV_STATUS_BYINDEX, do_lvm_ioctl), +HANDLE_IOCTL(LV_STATUS_BYDEV, do_lvm_ioctl), +HANDLE_IOCTL(PV_CHANGE, do_lvm_ioctl), +HANDLE_IOCTL(PV_STATUS, do_lvm_ioctl), +#endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +HANDLE_IOCTL(DRM32_IOCTL_VERSION, drm32_version), +HANDLE_IOCTL(DRM32_IOCTL_GET_UNIQUE, drm32_getsetunique), +HANDLE_IOCTL(DRM32_IOCTL_SET_UNIQUE, drm32_getsetunique), +HANDLE_IOCTL(DRM32_IOCTL_ADD_MAP, drm32_addmap), +HANDLE_IOCTL(DRM32_IOCTL_INFO_BUFS, drm32_info_bufs), +HANDLE_IOCTL(DRM32_IOCTL_FREE_BUFS, drm32_free_bufs), +HANDLE_IOCTL(DRM32_IOCTL_MAP_BUFS, drm32_map_bufs), +HANDLE_IOCTL(DRM32_IOCTL_DMA, drm32_dma), +HANDLE_IOCTL(DRM32_IOCTL_RES_CTX, drm32_res_ctx), +#endif /* DRM */ +#if 0 +HANDLE_IOCTL(USBDEVFS_CONTROL32, do_usbdevfs_control), +HANDLE_IOCTL(USBDEVFS_BULK32, do_usbdevfs_bulk), +/*HANDLE_IOCTL(USBDEVFS_SUBMITURB32, do_usbdevfs_urb)*/ +HANDLE_IOCTL(USBDEVFS_REAPURB32, do_usbdevfs_reapurb), +HANDLE_IOCTL(USBDEVFS_REAPURBNDELAY32, do_usbdevfs_reapurb), +HANDLE_IOCTL(USBDEVFS_DISCSIGNAL32, do_usbdevfs_discsignal), +#endif +HANDLE_IOCTL(SG_IO, do_sg_io), +}; + +unsigned long ioctl32_hash_table[1024]; + +static inline unsigned long ioctl32_hash(unsigned long cmd) +{ + return ((cmd >> 6) ^ (cmd >> 4) ^ cmd) & 0x3ff; +} + +static void ioctl32_insert_translation(struct ioctl_trans *trans) +{ + unsigned long hash; + struct ioctl_trans *t; + + hash = ioctl32_hash (trans->cmd); + if (!ioctl32_hash_table[hash]) + ioctl32_hash_table[hash] = (long)trans; + else { + t = (struct ioctl_trans *)ioctl32_hash_table[hash]; + while (t->next) + t = (struct ioctl_trans *)(long)t->next; + trans->next = 0; + t->next = (long)trans; + } +} + +static int __init init_sys32_ioctl(void) +{ + int i, size = sizeof(ioctl_translations) / sizeof(struct ioctl_trans); + for (i=0; i < size ;i++) + ioctl32_insert_translation(&ioctl_translations[i]); + return 0; +} + +__initcall(init_sys32_ioctl); + +static struct ioctl_trans *additional_ioctls; + +/* Always call these with kernel lock held! */ + +int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)) +{ + int i; + if (!additional_ioctls) { + additional_ioctls = module_map(PAGE_SIZE); + if (!additional_ioctls) + return -ENOMEM; + memset(additional_ioctls, 0, PAGE_SIZE); + } + for (i = 0; i < PAGE_SIZE/sizeof(struct ioctl_trans); i++) + if (!additional_ioctls[i].cmd) + break; + if (i == PAGE_SIZE/sizeof(struct ioctl_trans)) + return -ENOMEM; + additional_ioctls[i].cmd = cmd; + if (!handler) + additional_ioctls[i].handler = (long)sys_ioctl; + else + additional_ioctls[i].handler = (long)handler; + ioctl32_insert_translation(&additional_ioctls[i]); + return 0; +} + +int unregister_ioctl32_conversion(unsigned int cmd) +{ + unsigned long hash = ioctl32_hash(cmd); + struct ioctl_trans *t, *t1; + + t = (struct ioctl_trans *)ioctl32_hash_table[hash]; + if (!t) return -EINVAL; + if (t->cmd == cmd && t >= additional_ioctls && + (unsigned long)t < ((unsigned long)additional_ioctls) + PAGE_SIZE) { + ioctl32_hash_table[hash] = t->next; + t->cmd = 0; + return 0; + } else while (t->next) { + t1 = (struct ioctl_trans *)t->next; + if (t1->cmd == cmd && t1 >= additional_ioctls && + (unsigned long)t1 < ((unsigned long)additional_ioctls) + PAGE_SIZE) { + t1->cmd = 0; + t->next = t1->next; + return 0; + } + t = t1; + } + return -EINVAL; +} + +asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + int error = -EBADF; + int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); + struct ioctl_trans *t; + + filp = fget(fd); + if (!filp) + goto out2; + + if (!filp->f_op || !filp->f_op->ioctl) { + error = sys_ioctl (fd, cmd, arg); + goto out; + } + + t = (struct ioctl_trans *)ioctl32_hash_table [ioctl32_hash (cmd)]; + + while (t && t->cmd != cmd) + t = (struct ioctl_trans *)t->next; + if (t) { + handler = (void *)t->handler; + error = handler(fd, cmd, arg, filp); + } else { + static int count = 0; + if (++count <= 20) + printk("sys32_ioctl(%s:%d): Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + current->comm, current->pid, + (int)fd, (unsigned int)cmd, (unsigned int)arg); + error = -EINVAL; + } +out: + fput(filp); +out2: + return error; +} diff -Nru a/arch/ppc64/kernel/irq.c b/arch/ppc64/kernel/irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/irq.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,920 @@ +/* + * + * + * arch/ppc/kernel/irq.c + * + * Derived from arch/i386/kernel/irq.c + * Copyright (C) 1992 Linus Torvalds + * Adapted from arch/i386 by Gary Thomas + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * 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 file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + + +#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 "local_irq.h" + +void enable_irq(unsigned int irq_nr); +void disable_irq(unsigned int irq_nr); + +#ifdef CONFIG_SMP +extern void iSeries_smp_message_recv( struct pt_regs * ); +#endif + +volatile unsigned char *chrp_int_ack_special; +static void register_irq_proc (unsigned int irq); + +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = + { [0 ... NR_IRQS-1] = { 0, NULL, NULL, 0, SPIN_LOCK_UNLOCKED}}; + +int ppc_spurious_interrupts = 0; +struct irqaction *ppc_irq_action[NR_IRQS]; +unsigned long lpEvent_count = 0; +#ifdef CONFIG_XMON +extern void xmon(struct pt_regs *regs); +extern int xmon_bpt(struct pt_regs *regs); +extern int xmon_sstep(struct pt_regs *regs); +extern int xmon_iabr_match(struct pt_regs *regs); +extern int xmon_dabr_match(struct pt_regs *regs); +extern void (*xmon_fault_handler)(struct pt_regs *regs); +#endif +#ifdef CONFIG_XMON +extern void (*debugger)(struct pt_regs *regs); +extern int (*debugger_bpt)(struct pt_regs *regs); +extern int (*debugger_sstep)(struct pt_regs *regs); +extern int (*debugger_iabr_match)(struct pt_regs *regs); +extern int (*debugger_dabr_match)(struct pt_regs *regs); +extern void (*debugger_fault_handler)(struct pt_regs *regs); +#endif + +/* nasty hack for shared irq's since we need to do kmalloc calls but + * can't very early in the boot when we need to do a request irq. + * this needs to be removed. + * -- Cort + */ +#define IRQ_KMALLOC_ENTRIES 8 +static int cache_bitmask = 0; +static struct irqaction malloc_cache[IRQ_KMALLOC_ENTRIES]; +extern int mem_init_done; + +void *irq_kmalloc(size_t size, int pri) +{ + unsigned int i; + if ( mem_init_done ) + return kmalloc(size,pri); + for ( i = 0; i < IRQ_KMALLOC_ENTRIES ; i++ ) + if ( ! ( cache_bitmask & (1<flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); + unmask_irq(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); + return 0; +} + +/* This could be promoted to a real free_irq() ... */ +static int +do_free_irq(int irq, void* dev_id) +{ + irq_desc_t *desc; + struct irqaction **p; + unsigned long flags; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + mask_irq(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + +#ifdef CONFIG_SMP + /* Wait to make sure it's not being used on another CPU */ + while (desc->status & IRQ_INPROGRESS) + barrier(); +#endif + irq_kfree(action); + return 0; + } + printk("Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + break; + } + return -ENOENT; +} + +int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) +{ + struct irqaction *action; + int retval; + + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + /* We could implement really free_irq() instead of that... */ + return do_free_irq(irq, dev_id); + + action = (struct irqaction *) + irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) { + printk(KERN_ERR "irq_kmalloc() failed for irq %d !\n", irq); + return -ENOMEM; + } + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->dev_id = dev_id; + action->next = NULL; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + + return 0; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + request_irq(irq, NULL, 0, NULL, dev_id); +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + +/** + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ + + void disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + if (!(desc->status & IRQ_PER_CPU)) + desc->status |= IRQ_DISABLED; + mask_irq(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * disable_irq - disable an irq and wait for completion + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. That is for two disables you need two enables. This + * function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ + +void disable_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + + if (!local_irq_count(smp_processor_id())) { + do { + barrier(); + } while (irq_desc[irq].status & IRQ_INPROGRESS); + } +} + +/** + * enable_irq - enable interrupt handling on an irq + * @irq: Interrupt to enable + * + * Re-enables the processing of interrupts on this IRQ line + * providing no disable_irq calls are now in effect. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + unmask_irq(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq(%u) unbalanced\n", irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +int show_interrupts(struct seq_file *p, void *v) +{ + int i, j; + struct irqaction * action; + + seq_printf(p, " "); + for (j=0; jhandler) + continue; + seq_printf(p, "%3d: ", i); +#ifdef CONFIG_SMP + for (j = 0; j < smp_num_cpus; j++) + seq_printf(p, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); +#else + seq_printf(p, "%10u ", kstat_irqs(i)); +#endif /* CONFIG_SMP */ + if (irq_desc[i].handler) + seq_printf(p, " %s ", irq_desc[i].handler->typename ); + else + seq_printf(p, " None "); + seq_printf(p, "%s", (irq_desc[i].status & IRQ_LEVEL) ? "Level " : "Edge "); + seq_printf(p, " %s",action->name); + for (action=action->next; action; action = action->next) + seq_printf(p, ", %s", action->name); + seq_putc(p, '\n'); + } + seq_printf(p, "BAD: %10u\n", ppc_spurious_interrupts); + return 0; +} + +static inline void +handle_irq_event(int irq, struct pt_regs *regs, struct irqaction *action) +{ + int status = 0; + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); +} + +/* + * Eventually, this should take an array of interrupts and an array size + * so it can dispatch multiple interrupts. + */ +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq) +{ + int status; + struct irqaction *action; + int cpu = smp_processor_id(); + irq_desc_t *desc = irq_desc + irq; + + kstat.irqs[cpu][irq]++; + spin_lock(&desc->lock); + ack_irq(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + if (!(status & IRQ_PER_CPU)) + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + if (!action || !action->handler) { + ppc_spurious_interrupts++; + printk(KERN_DEBUG "Unhandled interrupt %x, disabled\n", irq); + /* We can't call disable_irq here, it would deadlock */ + if (!desc->depth) + desc->depth = 1; + desc->status |= IRQ_DISABLED; + /* This is not a real spurrious interrupt, we + * have to eoi it, so we jump to out + */ + mask_irq(irq); + goto out; + } + status &= ~IRQ_PENDING; /* we commit to handling */ + if (!(status & IRQ_PER_CPU)) + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (!action) + goto out; + + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); + handle_irq_event(irq, regs, action); + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + if (irq_desc[irq].handler) { + if (irq_desc[irq].handler->end) + irq_desc[irq].handler->end(irq); + else if (irq_desc[irq].handler->enable) + irq_desc[irq].handler->enable(irq); + } + spin_unlock(&desc->lock); +} + +int do_IRQ(struct pt_regs *regs, int isfake) +{ + int cpu = smp_processor_id(); + int irq; + struct Paca * paca; + struct ItLpQueue * lpq; + + /* if(cpu) udbg_printf("Entering do_IRQ\n"); */ + + irq_enter(cpu); + + if ( _machine != _MACH_iSeries ) { + + /* every arch is required to have a get_irq -- Cort */ + irq = ppc_md.get_irq( regs ); + + if ( irq >= 0 ) { + ppc_irq_dispatch_handler( regs, irq ); + if (ppc_md.post_irq) + ppc_md.post_irq( regs, irq ); + } else { + /* -2 means ignore, already handled */ + if (irq != -2) { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + ppc_spurious_interrupts++; + } + } + } + /* if on iSeries partition */ + else { + paca = (struct Paca *)mfspr(SPRG3); +#ifdef CONFIG_SMP + if ( paca->xLpPaca.xIntDword.xFields.xIpiCnt ) { + paca->xLpPaca.xIntDword.xFields.xIpiCnt = 0; + iSeries_smp_message_recv( regs ); + } +#endif /* CONFIG_SMP */ + lpq = paca->lpQueuePtr; + if ( lpq && ItLpQueue_isLpIntPending( lpq ) ) + lpEvent_count += ItLpQueue_process( lpq, regs ); + } + + irq_exit(cpu); + + if ( _machine == _MACH_iSeries ) { + if ( paca->xLpPaca.xIntDword.xFields.xDecrInt ) { + paca->xLpPaca.xIntDword.xFields.xDecrInt = 0; + /* Signal a fake decrementer interrupt */ + timer_interrupt( regs ); + } + } + + if (softirq_pending(cpu)) + do_softirq(); + + return 1; /* lets ret_from_int know we can do checks */ +} + +unsigned long probe_irq_on (void) +{ + return 0; +} + +int probe_irq_off (unsigned long irqs) +{ + return 0; +} + +unsigned int probe_irq_mask(unsigned long irqs) +{ + return 0; +} + +void __init init_IRQ(void) +{ + static int once = 0; + + if ( once ) + return; + else + once++; + + ppc_md.init_IRQ(); + if(ppc_md.init_ras_IRQ) ppc_md.init_ras_IRQ(); +} + +#ifdef CONFIG_SMP +unsigned char global_irq_holder = NO_PROC_ID; + +static void show(char * str) +{ + int cpu = smp_processor_id(); + int i; + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [ ", irqs_running()); + for (i = 0; i < smp_num_cpus; i++) + printk("%u ", __brlock_array[i][BR_GLOBALIRQ_LOCK]); + printk("]\nbh: %d [ ", + (spin_is_locked(&global_bh_lock) ? 1 : 0)); + for (i = 0; i < smp_num_cpus; i++) + printk("%u ", local_bh_count(i)); + printk("]\n"); +} + +#define MAXCOUNT 10000000 + +void synchronize_irq(void) +{ + if (irqs_running()) { + cli(); + sti(); + } +} + +static inline void get_irqlock(int cpu) +{ + int count; + + if ((unsigned char)cpu == global_irq_holder) + return; + + count = MAXCOUNT; +again: + br_write_lock(BR_GLOBALIRQ_LOCK); + for (;;) { + spinlock_t *lock; + + if (!irqs_running() && + (local_bh_count(smp_processor_id()) || !spin_is_locked(&global_bh_lock))) + break; + + br_write_unlock(BR_GLOBALIRQ_LOCK); + lock = &__br_write_locks[BR_GLOBALIRQ_LOCK].lock; + while (irqs_running() || + spin_is_locked(lock) || + (!local_bh_count(smp_processor_id()) && spin_is_locked(&global_bh_lock))) { + if (!--count) { + show("get_irqlock"); + count = (~0 >> 1); + } + __sti(); + barrier(); + __cli(); + } + goto again; + } + + global_irq_holder = cpu; +} + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void __global_cli(void) +{ + unsigned long flags; + + __save_flags(flags); + if (flags & (1UL << 15)) { + int cpu = smp_processor_id(); + __cli(); + if (!local_irq_count(cpu)) + get_irqlock(cpu); + } +} + +void __global_sti(void) +{ + int cpu = smp_processor_id(); + + if (!local_irq_count(cpu)) + release_irqlock(cpu); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long __global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + + __save_flags(flags); + local_enabled = (flags >> 15) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!local_irq_count(smp_processor_id())) { + if (local_enabled) + retval = 1; + if (global_irq_holder == (unsigned char) smp_processor_id()) + retval = 0; + } + return retval; +} + +void __global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %016lx caller %p\n", + flags, __builtin_return_address(0)); + } +} + +#endif /* CONFIG_SMP */ + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +#ifdef CONFIG_IRQ_ALL_CPUS +unsigned int irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = 0xffffffff}; +#else /* CONFIG_IRQ_ALL_CPUS */ +unsigned int irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = 0x00000000}; +#endif /* CONFIG_IRQ_ALL_CPUS */ + +#define HEX_DIGITS 8 + +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08x\n", irq_affinity[(int)(long)data]); +} + +static unsigned int parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; + int i; + + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; + } +out: + *ret = value; + return 0; +} + +static int irq_affinity_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (int)(long) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + +/* Why is this disabled ? --BenH */ +#if 0/*CONFIG_SMP*/ + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_online_map)) + return -EINVAL; +#endif + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + +#ifdef CONFIG_PPC_ISERIES + { + unsigned i; + for (i=0; i>= 1; + } + } +#endif + + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + struct proc_dir_entry *entry; + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == NULL)) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + + smp_affinity_entry[irq] = entry; +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) { + if (irq_desc[i].handler == NULL) + continue; + register_irq_proc(i); + } +} + +void no_action(int irq, void *dev, struct pt_regs *regs) +{ +} diff -Nru a/arch/ppc64/kernel/lmb.c b/arch/ppc64/kernel/lmb.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/lmb.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,359 @@ +/* + * + * Procedures for interfacing to Open Firmware. + * + * Peter Bergner, IBM Corp. June 2001. + * Copyright (C) 2001 Peter Bergner. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern unsigned long klimit; +extern unsigned long reloc_offset(void); + + +static long lmb_add_region(struct lmb_region *, unsigned long, unsigned long, unsigned long); + +struct lmb lmb = { + 0, + {0,0,0,{{0,0,0}}}, + {0,0,0,{{0,0,0}}} +}; + + +/* Assumption: base addr of region 1 < base addr of region 2 */ +static void +lmb_coalesce_regions(struct lmb_region *rgn, unsigned long r1, unsigned long r2) +{ + unsigned long i; + + rgn->region[r1].size += rgn->region[r2].size; + for (i=r2; i < rgn->cnt-1 ;i++) { + rgn->region[i].base = rgn->region[i+1].base; + rgn->region[i].physbase = rgn->region[i+1].physbase; + rgn->region[i].size = rgn->region[i+1].size; + rgn->region[i].type = rgn->region[i+1].type; + } + rgn->cnt--; +} + + +/* This routine called with relocation disabled. */ +void +lmb_init(void) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + + /* Create a dummy zero size LMB which will get coalesced away later. + * This simplifies the lmb_add() code below... + */ + _lmb->memory.region[0].base = 0; + _lmb->memory.region[0].size = 0; + _lmb->memory.region[0].type = LMB_MEMORY_AREA; + _lmb->memory.cnt = 1; + + /* Ditto. */ + _lmb->reserved.region[0].base = 0; + _lmb->reserved.region[0].size = 0; + _lmb->reserved.region[0].type = LMB_MEMORY_AREA; + _lmb->reserved.cnt = 1; +} + +/* This is only used here, it doesnt deserve to be in bitops.h */ +static __inline__ long cnt_trailing_zeros(unsigned long mask) +{ + long cnt; + + asm( +" addi %0,%1,-1 \n\ + andc %0,%0,%1 \n\ + cntlzd %0,%0 \n\ + subfic %0,%0,64" + : "=r" (cnt) + : "r" (mask)); + return cnt; +} + +/* This routine called with relocation disabled. */ +void +lmb_analyze(void) +{ + unsigned long i, physbase = 0; + unsigned long total_size = 0; + unsigned long size_mask = 0; + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + + for (i=0; i < _lmb->memory.cnt ;i++) { + unsigned long lmb_size = _lmb->memory.region[i].size; +#ifdef CONFIG_MSCHUNKS + _lmb->memory.region[i].physbase = physbase; + physbase += lmb_size; +#else + _lmb->memory.region[i].physbase = _lmb->memory.region[i].base; +#endif + total_size += lmb_size; + size_mask |= lmb_size; + } + _lmb->memory.size = total_size; + _lmb->memory.lcd_size = (1UL << cnt_trailing_zeros(size_mask)); +} + +/* This routine called with relocation disabled. */ +long +lmb_add(unsigned long base, unsigned long size) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_rgn = &(_lmb->memory); + + return lmb_add_region(_rgn, base, size, LMB_MEMORY_AREA); + +} + +/* This routine called with relocation disabled. */ +long +lmb_add_io(unsigned long base, unsigned long size) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_rgn = &(_lmb->memory); + + return lmb_add_region(_rgn, base, size, LMB_IO_AREA); + +} + +long +lmb_reserve(unsigned long base, unsigned long size) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_rgn = &(_lmb->reserved); + + return lmb_add_region(_rgn, base, size, LMB_MEMORY_AREA); +} + +/* This routine called with relocation disabled. */ +static long +lmb_add_region(struct lmb_region *rgn, unsigned long base, unsigned long size, + unsigned long type) +{ + unsigned long i, coalesced = 0; + long adjacent; + + /* First try and coalesce this LMB with another. */ + for (i=0; i < rgn->cnt ;i++) { + unsigned long rgnbase = rgn->region[i].base; + unsigned long rgnsize = rgn->region[i].size; + unsigned long rgntype = rgn->region[i].type; + + if ( rgntype != type ) + continue; + + adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize); + if ( adjacent > 0 ) { + rgn->region[i].base -= size; + rgn->region[i].physbase -= size; + rgn->region[i].size += size; + coalesced++; + break; + } + else if ( adjacent < 0 ) { + rgn->region[i].size += size; + coalesced++; + break; + } + } + + if ((i < rgn->cnt-1) && lmb_regions_adjacent(rgn, i, i+1) ) { + lmb_coalesce_regions(rgn, i, i+1); + coalesced++; + } + + if ( coalesced ) { + return coalesced; + } else if ( rgn->cnt >= MAX_LMB_REGIONS ) { + return -1; + } + + /* Couldn't coalesce the LMB, so add it to the sorted table. */ + for (i=rgn->cnt-1; i >= 0 ;i--) { + if (base < rgn->region[i].base) { + rgn->region[i+1].base = rgn->region[i].base; + rgn->region[i+1].physbase = rgn->region[i].physbase; + rgn->region[i+1].size = rgn->region[i].size; + rgn->region[i+1].type = rgn->region[i].type; + } else { + rgn->region[i+1].base = base; + rgn->region[i+1].physbase = lmb_abs_to_phys(base); + rgn->region[i+1].size = size; + rgn->region[i+1].type = type; + break; + } + } + rgn->cnt++; + + return 0; +} + +long +lmb_overlaps_region(struct lmb_region *rgn, unsigned long base, unsigned long size) +{ + unsigned long i; + + for (i=0; i < rgn->cnt ;i++) { + unsigned long rgnbase = rgn->region[i].base; + unsigned long rgnsize = rgn->region[i].size; + if ( lmb_addrs_overlap(base,size,rgnbase,rgnsize) ) { + break; + } + } + + return (i < rgn->cnt) ? i : -1; +} + + +unsigned long +lmb_alloc(unsigned long size, unsigned long align) +{ + long i, j; + unsigned long base; + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_mem = &(_lmb->memory); + struct lmb_region *_rsv = &(_lmb->reserved); + + for (i=_mem->cnt-1; i >= 0 ;i--) { + unsigned long lmbbase = _mem->region[i].base; + unsigned long lmbsize = _mem->region[i].size; + unsigned long lmbtype = _mem->region[i].type; + + if ( lmbtype != LMB_MEMORY_AREA ) + continue; + + base = _ALIGN_DOWN(lmbbase+lmbsize-size, align); + + while ( (lmbbase <= base) && + ((j = lmb_overlaps_region(_rsv,base,size)) >= 0) ) { + base = _ALIGN_DOWN(_rsv->region[j].base-size, align); + } + + if ( (base != 0) && (lmbbase <= base) ) + break; + } + + if ( i < 0 ) + return 0; + + lmb_add_region(_rsv, base, size, LMB_MEMORY_AREA); + + return base; +} + +unsigned long +lmb_phys_mem_size(void) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_mem = &(_lmb->memory); + unsigned long idx = _mem->cnt-1; + unsigned long lastbase = _mem->region[idx].physbase; + unsigned long lastsize = _mem->region[idx].size; + + return (lastbase + lastsize); +} + +unsigned long +lmb_end_of_DRAM(void) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_mem = &(_lmb->memory); + unsigned long idx = _mem->cnt-1; +#ifdef CONFIG_MSCHUNKS + unsigned long lastbase = _mem->region[idx].physbase; +#else + unsigned long lastbase = _mem->region[idx].base; +#endif /* CONFIG_MSCHUNKS */ + unsigned long lastsize = _mem->region[idx].size; + + return (lastbase + lastsize); +} + + +unsigned long +lmb_abs_to_phys(unsigned long aa) +{ + unsigned long i, pa = 0; + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_mem = &(_lmb->memory); + + for (i=0; i < _mem->cnt ;i++) { + unsigned long lmbbase = _mem->region[i].base; + unsigned long lmbsize = _mem->region[i].size; + if ( lmb_addrs_overlap(aa,1,lmbbase,lmbsize) ) { + pa = _mem->region[i].physbase + (aa - lmbbase); + break; + } + } + + return pa; +} + +void +lmb_dump(char *str) +{ + unsigned long i; + + udbg_printf("\nlmb_dump: %s\n", str); + udbg_printf(" debug = %s\n", + (lmb.debug) ? "TRUE" : "FALSE"); + udbg_printf(" memory.cnt = %d\n", + lmb.memory.cnt); + udbg_printf(" memory.size = 0x%lx\n", + lmb.memory.size); + udbg_printf(" memory.lcd_size = 0x%lx\n", + lmb.memory.lcd_size); + for (i=0; i < lmb.memory.cnt ;i++) { + udbg_printf(" memory.region[%d].base = 0x%lx\n", + i, lmb.memory.region[i].base); + udbg_printf(" .physbase = 0x%lx\n", + lmb.memory.region[i].physbase); + udbg_printf(" .size = 0x%lx\n", + lmb.memory.region[i].size); + udbg_printf(" .type = 0x%lx\n", + lmb.memory.region[i].type); + } + + udbg_printf("\n"); + udbg_printf(" reserved.cnt = %d\n", + lmb.reserved.cnt); + udbg_printf(" reserved.size = 0x%lx\n", + lmb.reserved.size); + udbg_printf(" reserved.lcd_size = 0x%lx\n", + lmb.reserved.lcd_size); + for (i=0; i < lmb.reserved.cnt ;i++) { + udbg_printf(" reserved.region[%d].base = 0x%lx\n", + i, lmb.reserved.region[i].base); + udbg_printf(" .physbase = 0x%lx\n", + lmb.reserved.region[i].physbase); + udbg_printf(" .size = 0x%lx\n", + lmb.reserved.region[i].size); + udbg_printf(" .type = 0x%lx\n", + lmb.reserved.region[i].type); + } +} diff -Nru a/arch/ppc64/kernel/local_irq.h b/arch/ppc64/kernel/local_irq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/local_irq.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,26 @@ +/* + * c 2001 PowerPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _PPC_KERNEL_LOCAL_IRQ_H +#define _PPC_KERNEL_LOCAL_IRQ_H + +#include +#include +#include +#include +#include + +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); + +#define NR_MASK_WORDS ((NR_IRQS + 63) / 64) + +extern int ppc_spurious_interrupts; +extern int ppc_second_irq; +extern struct irqaction *ppc_irq_action[NR_IRQS]; + +#endif /* _PPC_KERNEL_LOCAL_IRQ_H */ diff -Nru a/arch/ppc64/kernel/mf.c b/arch/ppc64/kernel/mf.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/mf.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1202 @@ +/* + * mf.c + * Copyright (C) 2001 Troy D. Armstrong IBM Corporation + * + * This modules exists as an interface between a Linux secondary partition + * running on an iSeries and the primary partition's Virtual Service + * Processor (VSP) object. The VSP has final authority over powering on/off + * all partitions in the iSeries. It also provides miscellaneous low-level + * machine facility type operations. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + +extern struct pci_dev * iSeries_vio_dev; + +/* + * This is the structure layout for the Machine Facilites LPAR event + * flows. + */ +struct VspCmdData; +struct CeMsgData; +union SafeCast +{ + u64 ptrAsU64; + void *ptr; +}; + + +typedef void (*CeMsgCompleteHandler)( void *token, struct CeMsgData *vspCmdRsp ); + +struct CeMsgCompleteData +{ + CeMsgCompleteHandler xHdlr; + void *xToken; +}; + +struct VspRspData +{ + struct semaphore *xSemaphore; + struct VspCmdData *xResponse; +}; + +struct IoMFLpEvent +{ + struct HvLpEvent xHvLpEvent; + + u16 xSubtypeRc; + u16 xRsvd1; + u32 xRsvd2; + + union + { + + struct AllocData + { + u16 xSize; + u16 xType; + u32 xCount; + u16 xRsvd3; + u8 xRsvd4; + HvLpIndex xTargetLp; + } xAllocData; + + struct CeMsgData + { + u8 xCEMsg[12]; + char xReserved[4]; + struct CeMsgCompleteData *xToken; + } xCEMsgData; + + struct VspCmdData + { + union SafeCast xTokenUnion; + u16 xCmd; + HvLpIndex xLpIndex; + u8 xRc; + u32 xReserved1; + + union VspCmdSubData + { + struct + { + u64 xState; + } xGetStateOut; + + struct + { + u64 xIplType; + } xGetIplTypeOut, xFunction02SelectIplTypeIn; + + struct + { + u64 xIplMode; + } xGetIplModeOut, xFunction02SelectIplModeIn; + + struct + { + u64 xPage[4]; + } xGetSrcHistoryIn; + + struct + { + u64 xFlag; + } xGetAutoIplWhenPrimaryIplsOut, + xSetAutoIplWhenPrimaryIplsIn, + xWhiteButtonPowerOffIn, + xFunction08FastPowerOffIn, + xIsSpcnRackPowerIncompleteOut; + + struct + { + u64 xToken; + u64 xAddressType; + u64 xSide; + u32 xTransferLength; + u32 xOffset; + } xSetKernelImageIn, + xGetKernelImageIn, + xSetKernelCmdLineIn, + xGetKernelCmdLineIn; + + struct + { + u32 xTransferLength; + } xGetKernelImageOut,xGetKernelCmdLineOut; + + + u8 xReserved2[80]; + + } xSubData; + } xVspCmd; + } xUnion; +}; + + +/* + * All outgoing event traffic is kept on a FIFO queue. The first + * pointer points to the one that is outstanding, and all new + * requests get stuck on the end. Also, we keep a certain number of + * preallocated stack elements so that we can operate very early in + * the boot up sequence (before kmalloc is ready). + */ +struct StackElement +{ + struct StackElement * next; + struct IoMFLpEvent event; + MFCompleteHandler hdlr; + char dmaData[72]; + unsigned dmaDataLength; + unsigned remoteAddress; +}; +static spinlock_t spinlock; +static struct StackElement * head = NULL; +static struct StackElement * tail = NULL; +static struct StackElement * avail = NULL; +static struct StackElement prealloc[16]; + +/* + * Put a stack element onto the available queue, so it can get reused. + * Attention! You must have the spinlock before calling! + */ +void free( struct StackElement * element ) +{ + if ( element != NULL ) + { + element->next = avail; + avail = element; + } +} + +/* + * Enqueue the outbound event onto the stack. If the queue was + * empty to begin with, we must also issue it via the Hypervisor + * interface. There is a section of code below that will touch + * the first stack pointer without the protection of the spinlock. + * This is OK, because we know that nobody else will be modifying + * the first pointer when we do this. + */ +static int signalEvent( struct StackElement * newElement ) +{ + int rc = 0; + unsigned long flags; + int go = 1; + struct StackElement * element; + HvLpEvent_Rc hvRc; + + /* enqueue the event */ + if ( newElement != NULL ) + { + spin_lock_irqsave( &spinlock, flags ); + if ( head == NULL ) + head = newElement; + else { + go = 0; + tail->next = newElement; + } + newElement->next = NULL; + tail = newElement; + spin_unlock_irqrestore( &spinlock, flags ); + } + + /* send the event */ + while ( go ) + { + go = 0; + + /* any DMA data to send beforehand? */ + if ( head->dmaDataLength > 0 ) + HvCallEvent_dmaToSp( head->dmaData, head->remoteAddress, head->dmaDataLength, HvLpDma_Direction_LocalToRemote ); + + hvRc = HvCallEvent_signalLpEvent(&head->event.xHvLpEvent); + if ( hvRc != HvLpEvent_Rc_Good ) + { + printk( KERN_ERR "mf.c: HvCallEvent_signalLpEvent() failed with %d\n", (int)hvRc ); + + spin_lock_irqsave( &spinlock, flags ); + element = head; + head = head->next; + if ( head != NULL ) + go = 1; + spin_unlock_irqrestore( &spinlock, flags ); + + if ( element == newElement ) + rc = -EIO; + else { + if ( element->hdlr != NULL ) + { + union SafeCast mySafeCast; + mySafeCast.ptrAsU64 = element->event.xHvLpEvent.xCorrelationToken; + (*element->hdlr)( mySafeCast.ptr, -EIO ); + } + } + + spin_lock_irqsave( &spinlock, flags ); + free( element ); + spin_unlock_irqrestore( &spinlock, flags ); + } + } + + return rc; +} + +/* + * Allocate a new StackElement structure, and initialize it. + */ +static struct StackElement * newStackElement( void ) +{ + struct StackElement * newElement = NULL; + HvLpIndex primaryLp = HvLpConfig_getPrimaryLpIndex(); + unsigned long flags; + + if ( newElement == NULL ) + { + spin_lock_irqsave( &spinlock, flags ); + if ( avail != NULL ) + { + newElement = avail; + avail = avail->next; + } + spin_unlock_irqrestore( &spinlock, flags ); + } + + if ( newElement == NULL ) + newElement = kmalloc(sizeof(struct StackElement),GFP_ATOMIC); + + if ( newElement == NULL ) + { + printk( KERN_ERR "mf.c: unable to kmalloc %ld bytes\n", sizeof(struct StackElement) ); + return NULL; + } + + memset( newElement, 0, sizeof(struct StackElement) ); + newElement->event.xHvLpEvent.xFlags.xValid = 1; + newElement->event.xHvLpEvent.xFlags.xAckType = HvLpEvent_AckType_ImmediateAck; + newElement->event.xHvLpEvent.xFlags.xAckInd = HvLpEvent_AckInd_DoAck; + newElement->event.xHvLpEvent.xFlags.xFunction = HvLpEvent_Function_Int; + newElement->event.xHvLpEvent.xType = HvLpEvent_Type_MachineFac; + newElement->event.xHvLpEvent.xSourceLp = HvLpConfig_getLpIndex(); + newElement->event.xHvLpEvent.xTargetLp = primaryLp; + newElement->event.xHvLpEvent.xSizeMinus1 = sizeof(newElement->event)-1; + newElement->event.xHvLpEvent.xRc = HvLpEvent_Rc_Good; + newElement->event.xHvLpEvent.xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac); + newElement->event.xHvLpEvent.xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac); + + return newElement; +} + +static int signalVspInstruction( struct VspCmdData *vspCmd ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + struct VspRspData response; + DECLARE_MUTEX_LOCKED(Semaphore); + response.xSemaphore = &Semaphore; + response.xResponse = vspCmd; + + if ( newElement == NULL ) + rc = -ENOMEM; + else { + newElement->event.xHvLpEvent.xSubtype = 6; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0); + newElement->event.xUnion.xVspCmd.xTokenUnion.ptr = &response; + newElement->event.xUnion.xVspCmd.xCmd = vspCmd->xCmd; + newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex(); + newElement->event.xUnion.xVspCmd.xRc = 0xFF; + newElement->event.xUnion.xVspCmd.xReserved1 = 0; + memcpy(&(newElement->event.xUnion.xVspCmd.xSubData),&(vspCmd->xSubData), sizeof(vspCmd->xSubData)); + mb(); + + rc = signalEvent(newElement); + } + + if (rc == 0) + { + down(&Semaphore); + } + + return rc; +} + + +/* + * Send a 12-byte CE message to the primary partition VSP object + */ +static int signalCEMsg( char * ceMsg, void * token ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if ( newElement == NULL ) + rc = -ENOMEM; + else { + newElement->event.xHvLpEvent.xSubtype = 0; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0); + memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 ); + newElement->event.xUnion.xCEMsgData.xToken = token; + rc = signalEvent(newElement); + } + + return rc; +} + +/* + * Send a 12-byte CE message and DMA data to the primary partition VSP object + */ +static int dmaAndSignalCEMsg( char * ceMsg, void * token, void * dmaData, unsigned dmaDataLength, unsigned remoteAddress ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if ( newElement == NULL ) + rc = -ENOMEM; + else { + newElement->event.xHvLpEvent.xSubtype = 0; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0); + memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 ); + newElement->event.xUnion.xCEMsgData.xToken = token; + memcpy( newElement->dmaData, dmaData, dmaDataLength ); + newElement->dmaDataLength = dmaDataLength; + newElement->remoteAddress = remoteAddress; + rc = signalEvent(newElement); + } + + return rc; +} + +/* + * Initiate a nice (hopefully) shutdown of Linux. We simply are + * going to try and send the init process a SIGINT signal. If + * this fails (why?), we'll simply force it off in a not-so-nice + * manner. + */ +static int shutdown( void ) +{ + int rc = kill_proc(1,SIGINT,1); + + if ( rc ) + { + printk( KERN_ALERT "mf.c: SIGINT to init failed (%d), hard shutdown commencing\n", rc ); + mf_powerOff(); + } + else + printk( KERN_ALERT "mf.c: init has been successfully notified to proceed with shutdown\n" ); + + return rc; +} + +/* + * The primary partition VSP object is sending us a new + * event flow. Handle it... + */ +static void intReceived( struct IoMFLpEvent * event ) +{ + int freeIt = 0; + struct StackElement * two = NULL; + /* ack the interrupt */ + event->xHvLpEvent.xRc = HvLpEvent_Rc_Good; + HvCallEvent_ackLpEvent( &event->xHvLpEvent ); + + /* process interrupt */ + switch( event->xHvLpEvent.xSubtype ) + { + case 0: /* CE message */ + switch( event->xUnion.xCEMsgData.xCEMsg[3] ) + { + case 0x5B: /* power control notification */ + if ( (event->xUnion.xCEMsgData.xCEMsg[5]&0x20) != 0 ) + { + printk( KERN_ALERT "mf.c: Commencing partition shutdown\n" ); + if ( shutdown() == 0 ) + signalCEMsg( "\x00\x00\x00\xDB\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + } + break; + case 0xC0: /* get time */ + { + if ( (head != NULL) && ( head->event.xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) ) + { + freeIt = 1; + if ( head->event.xUnion.xCEMsgData.xToken != 0 ) + { + CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr; + void * token = head->event.xUnion.xCEMsgData.xToken->xToken; + + if (xHdlr != NULL) + (*xHdlr)( token, &(event->xUnion.xCEMsgData) ); + } + } + } + break; + } + + /* remove from queue */ + if ( freeIt == 1 ) + { + unsigned long flags; + spin_lock_irqsave( &spinlock, flags ); + if ( head != NULL ) + { + struct StackElement *oldHead = head; + head = head->next; + two = head; + free( oldHead ); + } + spin_unlock_irqrestore( &spinlock, flags ); + } + + /* send next waiting event */ + if ( two != NULL ) + signalEvent( NULL ); + break; + case 1: /* IT sys shutdown */ + printk( KERN_ALERT "mf.c: Commencing system shutdown\n" ); + shutdown(); + break; + } +} + +/* + * The primary partition VSP object is acknowledging the receipt + * of a flow we sent to them. If there are other flows queued + * up, we must send another one now... + */ +static void ackReceived( struct IoMFLpEvent * event ) +{ + unsigned long flags; + struct StackElement * two = NULL; + unsigned long freeIt = 0; + + /* handle current event */ + if ( head != NULL ) + { + switch( event->xHvLpEvent.xSubtype ) + { + case 0: /* CE msg */ + if ( event->xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) + { + if ( event->xUnion.xCEMsgData.xCEMsg[2] != 0 ) + { + freeIt = 1; + if ( head->event.xUnion.xCEMsgData.xToken != 0 ) + { + CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr; + void * token = head->event.xUnion.xCEMsgData.xToken->xToken; + + if (xHdlr != NULL) + (*xHdlr)( token, &(event->xUnion.xCEMsgData) ); + } + } + } else { + freeIt = 1; + } + break; + case 4: /* allocate */ + case 5: /* deallocate */ + if ( head->hdlr != NULL ) + { + union SafeCast mySafeCast; + mySafeCast.ptrAsU64 = event->xHvLpEvent.xCorrelationToken; + (*head->hdlr)( mySafeCast.ptr, event->xUnion.xAllocData.xCount ); + } + freeIt = 1; + break; + case 6: + { + struct VspRspData *rsp = (struct VspRspData *)event->xUnion.xVspCmd.xTokenUnion.ptr; + + if (rsp != NULL) + { + if (rsp->xResponse != NULL) + memcpy(rsp->xResponse, &(event->xUnion.xVspCmd), sizeof(event->xUnion.xVspCmd)); + if (rsp->xSemaphore != NULL) + up(rsp->xSemaphore); + } else { + printk( KERN_ERR "mf.c: no rsp\n"); + } + freeIt = 1; + } + break; + } + } + else + printk( KERN_ERR "mf.c: stack empty for receiving ack\n" ); + + /* remove from queue */ + spin_lock_irqsave( &spinlock, flags ); + if (( head != NULL ) && ( freeIt == 1 )) + { + struct StackElement *oldHead = head; + head = head->next; + two = head; + free( oldHead ); + } + spin_unlock_irqrestore( &spinlock, flags ); + + /* send next waiting event */ + if ( two != NULL ) + signalEvent( NULL ); +} + +/* + * This is the generic event handler we are registering with + * the Hypervisor. Ensure the flows are for us, and then + * parse it enough to know if it is an interrupt or an + * acknowledge. + */ +static void hvHandler( struct HvLpEvent * event, struct pt_regs * regs ) +{ + if ( (event != NULL) && (event->xType == HvLpEvent_Type_MachineFac) ) + { + switch( event->xFlags.xFunction ) + { + case HvLpEvent_Function_Ack: + ackReceived( (struct IoMFLpEvent *)event ); + break; + case HvLpEvent_Function_Int: + intReceived( (struct IoMFLpEvent *)event ); + break; + default: + printk( KERN_ERR "mf.c: non ack/int event received\n" ); + break; + } + } + else + printk( KERN_ERR "mf.c: alien event received\n" ); +} + +/* + * Global kernel interface to allocate and seed events into the + * Hypervisor. + */ +void mf_allocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned size, + unsigned count, + MFCompleteHandler hdlr, + void * userToken ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if ( newElement == NULL ) + rc = -ENOMEM; + else { + union SafeCast mine; + mine.ptr = userToken; + newElement->event.xHvLpEvent.xSubtype = 4; + newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('A'<<0); + newElement->event.xUnion.xAllocData.xTargetLp = targetLp; + newElement->event.xUnion.xAllocData.xType = type; + newElement->event.xUnion.xAllocData.xSize = size; + newElement->event.xUnion.xAllocData.xCount = count; + newElement->hdlr = hdlr; + rc = signalEvent(newElement); + } + + if ( (rc != 0) && (hdlr != NULL) ) + (*hdlr)( userToken, rc ); +} + +/* + * Global kernel interface to unseed and deallocate events already in + * Hypervisor. + */ +void mf_deallocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned count, + MFCompleteHandler hdlr, + void * userToken ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if ( newElement == NULL ) + rc = -ENOMEM; + else { + union SafeCast mine; + mine.ptr = userToken; + newElement->event.xHvLpEvent.xSubtype = 5; + newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('D'<<0); + newElement->event.xUnion.xAllocData.xTargetLp = targetLp; + newElement->event.xUnion.xAllocData.xType = type; + newElement->event.xUnion.xAllocData.xCount = count; + newElement->hdlr = hdlr; + rc = signalEvent(newElement); + } + + if ( (rc != 0) && (hdlr != NULL) ) + (*hdlr)( userToken, rc ); +} + +/* + * Global kernel interface to tell the VSP object in the primary + * partition to power this partition off. + */ +void mf_powerOff( void ) +{ + printk( KERN_ALERT "mf.c: Down it goes...\n" ); + signalCEMsg( "\x00\x00\x00\x4D\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + for (;;); +} + +/* + * Global kernel interface to tell the VSP object in the primary + * partition to reboot this partition. + */ +void mf_reboot( void ) +{ + printk( KERN_ALERT "mf.c: Preparing to bounce...\n" ); + signalCEMsg( "\x00\x00\x00\x4E\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + for (;;); +} + +/* + * Display a single word SRC onto the VSP control panel. + */ +void mf_displaySrc( u32 word ) +{ + u8 ce[12]; + + memcpy( ce, "\x00\x00\x00\x4A\x00\x00\x00\x01\x00\x00\x00\x00", 12 ); + ce[8] = word>>24; + ce[9] = word>>16; + ce[10] = word>>8; + ce[11] = word; + signalCEMsg( ce, NULL ); +} + +/* + * Display a single word SRC of the form "PROGXXXX" on the VSP control panel. + */ +void mf_displayProgress( u16 value ) +{ + u8 ce[12]; + u8 src[72]; + + memcpy( ce, "\x00\x00\x04\x4A\x00\x00\x00\x48\x00\x00\x00\x00", 12 ); + memcpy( src, + "\x01\x00\x00\x01" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "PROGxxxx" + " ", + 72 ); + src[6] = value>>8; + src[7] = value&255; + src[44] = "0123456789ABCDEF"[(value>>12)&15]; + src[45] = "0123456789ABCDEF"[(value>>8)&15]; + src[46] = "0123456789ABCDEF"[(value>>4)&15]; + src[47] = "0123456789ABCDEF"[value&15]; + dmaAndSignalCEMsg( ce, NULL, src, sizeof(src), 9*64*1024 ); +} + +/* + * Clear the VSP control panel. Used to "erase" an SRC that was + * previously displayed. + */ +void mf_clearSrc( void ) +{ + signalCEMsg( "\x00\x00\x00\x4B\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); +} + +/* + * Initialization code here. + */ +void mf_init( void ) +{ + int i; + + /* initialize */ + spin_lock_init( &spinlock ); + for ( i = 0; i < sizeof(prealloc)/sizeof(*prealloc); ++i ) + free( &prealloc[i] ); + HvLpEvent_registerHandler( HvLpEvent_Type_MachineFac, &hvHandler ); + + /* virtual continue ack */ + signalCEMsg( "\x00\x00\x00\x57\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + + /* initialization complete */ + printk( KERN_NOTICE "mf.c: iSeries Linux LPAR Machine Facilities initialized\n" ); + + iSeries_proc_callback(&mf_proc_init); +} + +void mf_setSide(char side) +{ + int rc = 0; + u64 newSide = 0; + struct VspCmdData myVspCmd; + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + if (side == 'A') + newSide = 0; + else if (side == 'B') + newSide = 1; + else if (side == 'C') + newSide = 2; + else + newSide = 3; + + myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = newSide; + myVspCmd.xCmd = 10; + + rc = signalVspInstruction(&myVspCmd); +} + +char mf_getSide(void) +{ + char returnValue = ' '; + int rc = 0; + struct VspCmdData myVspCmd; + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 2; + myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = 0; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if (rc != 0) + { + return returnValue; + } else { + if (myVspCmd.xRc == 0) + { + if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 0) + returnValue = 'A'; + else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 1) + returnValue = 'B'; + else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 2) + returnValue = 'C'; + else + returnValue = 'D'; + } + } + + return returnValue; +} + +void mf_getSrcHistory(char *buffer, int size) +{ + /* struct IplTypeReturnStuff returnStuff; + struct StackElement * newElement = newStackElement(); + int rc = 0; + char *pages[4]; + + pages[0] = kmalloc(4096, GFP_ATOMIC); + pages[1] = kmalloc(4096, GFP_ATOMIC); + pages[2] = kmalloc(4096, GFP_ATOMIC); + pages[3] = kmalloc(4096, GFP_ATOMIC); + if (( newElement == NULL ) || (pages[0] == NULL) || (pages[1] == NULL) || (pages[2] == NULL) || (pages[3] == NULL)) + rc = -ENOMEM; + else + { + returnStuff.xType = 0; + returnStuff.xRc = 0; + returnStuff.xDone = 0; + newElement->event.xHvLpEvent.xSubtype = 6; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0); + newElement->event.xUnion.xVspCmd.xEvent = &returnStuff; + newElement->event.xUnion.xVspCmd.xCmd = 4; + newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex(); + newElement->event.xUnion.xVspCmd.xRc = 0xFF; + newElement->event.xUnion.xVspCmd.xReserved1 = 0; + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[0] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[0])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[1] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[1])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[2] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[2])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[3] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[3])); + mb(); + rc = signalEvent(newElement); + } + + if (rc != 0) + { + return; + } + else + { + while (returnStuff.xDone != 1) + { + udelay(10); + } + + if (returnStuff.xRc == 0) + { + memcpy(buffer, pages[0], size); + } + } + + kfree(pages[0]); + kfree(pages[1]); + kfree(pages[2]); + kfree(pages[3]);*/ +} + +void mf_setCmdLine(const char *cmdline, int size, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + dma_addr_t dma_addr = 0; + char *page = pci_alloc_consistent(iSeries_vio_dev, size, &dma_addr); + + if (page == NULL) { + printk(KERN_ERR "mf.c: couldn't allocate memory to set command line\n"); + return; + } + + copy_from_user(page, cmdline, size); + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 31; + myVspCmd.xSubData.xSetKernelCmdLineIn.xToken = dma_addr; + myVspCmd.xSubData.xSetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xSetKernelCmdLineIn.xSide = side; + myVspCmd.xSubData.xSetKernelCmdLineIn.xTransferLength = size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + pci_free_consistent(iSeries_vio_dev, size, page, dma_addr); +} + +int mf_getCmdLine(char *cmdline, int *size, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + int len = *size; + dma_addr_t dma_addr = pci_map_single(iSeries_vio_dev, cmdline, *size, PCI_DMA_FROMDEVICE); + + memset(cmdline, 0, *size); + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 33; + myVspCmd.xSubData.xGetKernelCmdLineIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelCmdLineIn.xSide = side; + myVspCmd.xSubData.xGetKernelCmdLineIn.xTransferLength = *size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if ( ! rc ) { + + if (myVspCmd.xRc == 0) + { + len = myVspCmd.xSubData.xGetKernelCmdLineOut.xTransferLength; + } + /* else + { + memcpy(cmdline, "Bad cmdline", 11); + } + */ + } + + pci_unmap_single(iSeries_vio_dev, dma_addr, *size, PCI_DMA_FROMDEVICE); + + return len; +} + + +int mf_setVmlinuxChunk(const char *buffer, int size, int offset, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + + dma_addr_t dma_addr = 0; + + char *page = pci_alloc_consistent(iSeries_vio_dev, size, &dma_addr); + + if (page == NULL) { + printk(KERN_ERR "mf.c: couldn't allocate memory to set vmlinux chunk\n"); + return -ENOMEM; + } + + copy_from_user(page, buffer, size); + memset(&myVspCmd, 0, sizeof(myVspCmd)); + + myVspCmd.xCmd = 30; + myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelImageIn.xSide = side; + myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset; + myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if (rc == 0) + { + if (myVspCmd.xRc == 0) + { + rc = 0; + } else { + rc = -ENOMEM; + } + } + + pci_free_consistent(iSeries_vio_dev, size, page, dma_addr); + + return rc; +} + +int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + int len = *size; + + dma_addr_t dma_addr = pci_map_single(iSeries_vio_dev, buffer, *size, PCI_DMA_FROMDEVICE); + + memset(buffer, 0, len); + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 32; + myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelImageIn.xSide = side; + myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset; + myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = len; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if (rc == 0) + { + if (myVspCmd.xRc == 0) + { + *size = myVspCmd.xSubData.xGetKernelImageOut.xTransferLength; + } else { + rc = -ENOMEM; + } + } + + pci_unmap_single(iSeries_vio_dev, dma_addr, *size, PCI_DMA_FROMDEVICE); + + return rc; +} + +int mf_setRtcTime(unsigned long time) +{ + struct rtc_time tm; + + to_tm(time, &tm); + + return mf_setRtc( &tm ); +} + +struct RtcTimeData +{ + struct semaphore *xSemaphore; + struct CeMsgData xCeMsg; + int xRc; +}; + +void getRtcTimeComplete(void * token, struct CeMsgData *ceMsg) +{ + struct RtcTimeData *rtc = (struct RtcTimeData *)token; + + memcpy(&(rtc->xCeMsg), ceMsg, sizeof(rtc->xCeMsg)); + + rtc->xRc = 0; + up(rtc->xSemaphore); +} + +static unsigned long lastsec = 1; + +int mf_getRtcTime(unsigned long *time) +{ +/* unsigned long usec, tsec; */ + + u32 dataWord1 = *((u32 *)(&xSpCommArea.xBcdTimeAtIplStart)); + u32 dataWord2 = *(((u32 *)&(xSpCommArea.xBcdTimeAtIplStart)) + 1); + int year = 1970; + int year1 = ( dataWord1 >> 24 ) & 0x000000FF; + int year2 = ( dataWord1 >> 16 ) & 0x000000FF; + int sec = ( dataWord1 >> 8 ) & 0x000000FF; + int min = dataWord1 & 0x000000FF; + int hour = ( dataWord2 >> 24 ) & 0x000000FF; + int day = ( dataWord2 >> 8 ) & 0x000000FF; + int mon = dataWord2 & 0x000000FF; + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year1); + BCD_TO_BIN(year2); + year = year1 * 100 + year2; + + *time = mktime(year, mon, day, hour, min, sec); + + *time += ( jiffies / HZ ); + + /* Now THIS is a nasty hack! + * It ensures that the first two calls to mf_getRtcTime get different + * answers. That way the loop in init_time (time.c) will not think + * the clock is stuck. + */ + if ( lastsec ) { + *time -= lastsec; + --lastsec; + } + + return 0; + +} + +int mf_getRtc( struct rtc_time * tm ) +{ + + struct CeMsgCompleteData ceComplete; + struct RtcTimeData rtcData; + int rc = 0; + DECLARE_MUTEX_LOCKED(Semaphore); + + memset(&ceComplete, 0, sizeof(ceComplete)); + memset(&rtcData, 0, sizeof(rtcData)); + + rtcData.xSemaphore = &Semaphore; + + ceComplete.xHdlr = &getRtcTimeComplete; + ceComplete.xToken = (void *)&rtcData; + + rc = signalCEMsg( "\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00", &ceComplete ); + + if ( rc == 0 ) + { + down(&Semaphore); + + if ( rtcData.xRc == 0) + { + if ( ( rtcData.xCeMsg.xCEMsg[2] == 0xa9 ) || + ( rtcData.xCeMsg.xCEMsg[2] == 0xaf ) ) { + /* TOD clock is not set */ + tm->tm_sec = 1; + tm->tm_min = 1; + tm->tm_hour = 1; + tm->tm_mday = 10; + tm->tm_mon = 8; + tm->tm_year = 71; + mf_setRtc( tm ); + } + { + u32 dataWord1 = *((u32 *)(rtcData.xCeMsg.xCEMsg+4)); + u32 dataWord2 = *((u32 *)(rtcData.xCeMsg.xCEMsg+8)); + u8 year = (dataWord1 >> 16 ) & 0x000000FF; + u8 sec = ( dataWord1 >> 8 ) & 0x000000FF; + u8 min = dataWord1 & 0x000000FF; + u8 hour = ( dataWord2 >> 24 ) & 0x000000FF; + u8 day = ( dataWord2 >> 8 ) & 0x000000FF; + u8 mon = dataWord2 & 0x000000FF; + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + + if ( year <= 69 ) + year += 100; + + tm->tm_sec = sec; + tm->tm_min = min; + tm->tm_hour = hour; + tm->tm_mday = day; + tm->tm_mon = mon; + tm->tm_year = year; + } + } else { + rc = rtcData.xRc; + tm->tm_sec = 0; + tm->tm_min = 0; + tm->tm_hour = 0; + tm->tm_mday = 15; + tm->tm_mon = 5; + tm->tm_year = 52; + + } + tm->tm_wday = 0; + tm->tm_yday = 0; + tm->tm_isdst = 0; + + } + + return rc; + +} + +int mf_setRtc(struct rtc_time * tm) +{ + char ceTime[12] = "\x00\x00\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00"; + int rc = 0; + u8 day, mon, hour, min, sec, y1, y2; + unsigned year; + + year = 1900 + tm->tm_year; + y1 = year / 100; + y2 = year % 100; + + sec = tm->tm_sec; + min = tm->tm_min; + hour = tm->tm_hour; + day = tm->tm_mday; + mon = tm->tm_mon + 1; + + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hour); + BIN_TO_BCD(mon); + BIN_TO_BCD(day); + BIN_TO_BCD(y1); + BIN_TO_BCD(y2); + + ceTime[4] = y1; + ceTime[5] = y2; + ceTime[6] = sec; + ceTime[7] = min; + ceTime[8] = hour; + ceTime[10] = day; + ceTime[11] = mon; + + rc = signalCEMsg( ceTime, NULL ); + + return rc; +} + + + diff -Nru a/arch/ppc64/kernel/mf_proc.c b/arch/ppc64/kernel/mf_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/mf_proc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,301 @@ +/* + * mf_proc.c + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _MF_PROC_H +#include +#endif +#ifndef MF_H_INCLUDED +#include +#endif +#include + +static struct proc_dir_entry *mf_proc_root = NULL; + +int proc_mf_dump_cmdline +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_dump_vmlinux +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_dump_side +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_change_side +(struct file *file, const char *buffer, unsigned long count, void *data); + +int proc_mf_dump_src +(char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_mf_change_src (struct file *file, const char *buffer, unsigned long count, void *data); +int proc_mf_change_cmdline(struct file *file, const char *buffer, unsigned long count, void *data); +int proc_mf_change_vmlinux(struct file *file, const char *buffer, unsigned long count, void *data); + + +void mf_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent = NULL; + struct proc_dir_entry *mf_a = NULL; + struct proc_dir_entry *mf_b = NULL; + struct proc_dir_entry *mf_c = NULL; + struct proc_dir_entry *mf_d = NULL; + + mf_proc_root = proc_mkdir("mf", iSeries_proc); + if (!mf_proc_root) return; + + mf_a = proc_mkdir("A", mf_proc_root); + if (!mf_a) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_a); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_a); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_b = proc_mkdir("B", mf_proc_root); + if (!mf_b) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_b); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)1; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_b); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)1; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_c = proc_mkdir("C", mf_proc_root); + if (!mf_c) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_c); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)2; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_c); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)2; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_d = proc_mkdir("D", mf_proc_root); + if (!mf_d) return; + + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_d); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)3; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR, mf_d); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)3; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = NULL; + + ent = create_proc_entry("side", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_side; + ent->write_proc = proc_mf_change_side; + + ent = create_proc_entry("src", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_src; + ent->write_proc = proc_mf_change_src; +} + +int proc_mf_dump_cmdline +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = count; + char *p; + + len = mf_getCmdLine(page, &len, (u64)data); + + p = page + len - 1; + while ( p > page ) { + if ( (*p == 0) || (*p == ' ') ) + --p; + else + break; + } + if ( *p != '\n' ) { + ++p; + *p = '\n'; + } + ++p; + *p = 0; + len = p - page; + + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_mf_dump_vmlinux +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int sizeToGet = count; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (mf_getVmlinuxChunk(page, &sizeToGet, off, (u64)data) == 0) + { + if (sizeToGet != 0) + { + *start = page + off; + printk("mf_proc.c: got count %d off %d\n", sizeToGet, (int)off); + return sizeToGet; + } else { + printk("mf_proc.c: eof\n"); + *eof = 1; + return 0; + } + } else { + printk("mf_proc.c: eof\n"); + *eof = 1; + return 0; + } +} + + +int proc_mf_dump_side +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + char mf_current_side = mf_getSide(); + len = sprintf(page, "%c\n", mf_current_side); + + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +int proc_mf_change_side(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if ((*buffer != 'A') && + (*buffer != 'B') && + (*buffer != 'C') && + (*buffer != 'D')) + { + printk(KERN_ERR "mf_proc.c: proc_mf_change_side: invalid side\n"); + return -EINVAL; + } + + mf_setSide(*buffer); + + return count; +} + +int proc_mf_dump_src +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + mf_getSrcHistory(page, count); + len = count; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_mf_change_src(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if ((count < 4) && (count != 1)) + { + printk(KERN_ERR "mf_proc: invalid src\n"); + return -EINVAL; + } + + if ((count == 1) && ((*buffer) == '\0')) + { + mf_clearSrc(); + } else { + mf_displaySrc(*(u32 *)buffer); + } + + return count; +} + +int proc_mf_change_cmdline(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + mf_setCmdLine(buffer, count, (u64)data); + + return count; +} + +int proc_mf_change_vmlinux(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + mf_setVmlinuxChunk(buffer, count, file->f_pos, (u64)data); + file->f_pos += count; + + return count; +} diff -Nru a/arch/ppc64/kernel/misc.S b/arch/ppc64/kernel/misc.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/misc.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,959 @@ +/* + * arch/ppc/kernel/misc.S + * + * + * + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com) + * PPC64 updates by Dave Engebretsen (engebret@us.ibm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ppc_asm.h" + + .text + +/* + * Returns (address we're running at) - (address we were linked at) + * for use before the text and data are mapped to KERNELBASE. + */ + +_GLOBAL(reloc_offset) + mflr r0 + bl 1f +1: mflr r3 + LOADADDR(r4,1b) + sub r3,r4,r3 + mtlr r0 + blr + +_GLOBAL(get_msr) + mfmsr r3 + blr + +_GLOBAL(get_dar) + mfdar r3 + blr + +_GLOBAL(get_srr0) + mfsrr0 r3 + blr + +_GLOBAL(get_srr1) + mfsrr1 r3 + blr + +_GLOBAL(get_sp) + mr r3,r1 + blr + +#ifdef CONFIG_PPC_ISERIES +/* unsigned long __no_use_save_flags(void) */ +_GLOBAL(__no_use_save_flags) + mfspr r4,SPRG3 + lbz r3,PACAPROCENABLED(r4) + blr + +/* void __no_use_restore_flags(unsigned long flags) */ +_GLOBAL(__no_use_restore_flags) +/* + * Just set/clear the MSR_EE bit through restore/flags but do not + * change anything else. This is needed by the RT system and makes + * sense anyway. + * -- Cort + */ + mfspr r6,SPRG3 + lbz r5,PACAPROCENABLED(r6) + /* Check if things are setup the way we want _already_. */ + cmpw 0,r3,r5 + beqlr + /* are we enabling interrupts? */ + cmpi 0,r3,0 + stb r3,PACAPROCENABLED(r6) + beqlr + /* Check pending interrupts */ + CHECKANYINT(r4,r5) + beqlr + + /* + * Handle pending interrupts in interrupt context + */ + li r0,0x5555 + sc + blr + +_GLOBAL(__no_use_cli) + mfspr r5,SPRG3 + lbz r3,PACAPROCENABLED(r5) + li r4,0 + stb r4,PACAPROCENABLED(r5) + blr /* Done */ + +_GLOBAL(__no_use_sti) + mfspr r6,SPRG3 + li r3,1 + stb r3,PACAPROCENABLED(r6) + + /* Check for pending interrupts + * A decrementer, IPI or PMC interrupt may have occurred + * while we were in the hypervisor (which enables) + */ + CHECKANYINT(r4,r5) + beqlr + + /* + * Handle pending interrupts in interrupt context + */ + li r0,0x5555 + sc + blr +#endif +/* + * Flush instruction cache. + */ +_GLOBAL(flush_instruction_cache) + +/* + * This is called by kgdb code + * and should probably go away + * to be replaced by invalidating + * the cache lines that are actually + * modified + */ + /* use invalidate-all bit in HID0 + * - is this consistent across all 64-bit cpus? -- paulus */ + mfspr r3,HID0 + ori r3,r3,HID0_ICFI + mtspr HID0,r3 + sync + isync + blr + +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * + * flush_icache_range(unsigned long start, unsigned long stop) + * + * flush all bytes from start through stop-1 inclusive + */ + +_GLOBAL(flush_icache_range) + +/* + * Flush the data cache to memory + * + * Different systems have different cache line sizes + * and in some cases i-cache and d-cache line sizes differ from + * each other. + */ + LOADADDR(r10,naca) /* Get Naca address */ + ld r10,0(r10) + lhz r7,DCACHEL1LINESIZE(r10) /* Get cache line size */ + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 /* ensure we get enough */ + lhz r9,DCACHEL1LOGLINESIZE(r10) /* Get log-2 of cache line size */ + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +1: dcbst 0,r6 + add r6,r6,r7 + bdnz 1b + sync + +/* Now invalidate the instruction cache */ + + lhz r7,ICACHEL1LINESIZE(r10) /* Get Icache line size */ + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 + lhz r9,ICACHEL1LOGLINESIZE(r10) /* Get log-2 of Icache line size */ + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +2: icbi 0,r6 + add r6,r6,r7 + bdnz 2b + isync + blr + +/* + * Like above, but only do the D-cache. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + * + * flush all bytes from start to stop-1 inclusive + */ +_GLOBAL(flush_dcache_range) + +/* + * Flush the data cache to memory + * + * Different systems have different cache line sizes + */ + LOADADDR(r10,naca) /* Get Naca address */ + ld r10,0(r10) + lhz r7,DCACHEL1LINESIZE(r10) /* Get dcache line size */ + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 /* ensure we get enough */ + lhz r9,DCACHEL1LOGLINESIZE(r10) /* Get log-2 of dcache line size */ + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +0: dcbst 0,r6 + add r6,r6,r7 + bdnz 0b + sync + blr + +/* + * Flush a particular page from the data cache to RAM. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * + * void __flush_dcache_icache(void *page) + */ +_GLOBAL(__flush_dcache_icache) +/* + * Flush the data cache to memory + * + * Different systems have different cache line sizes + */ + +/* Flush the dcache */ + LOADADDR(r7,naca) + ld r7,0(r7) + clrrdi r3,r3,12 /* Page align */ + lhz r4,DCACHEL1LINESPERPAGE(r7) /* Get # dcache lines per page */ + lhz r5,DCACHEL1LINESIZE(r7) /* Get dcache line size */ + mr r6,r3 + mtctr r4 +0: dcbst 0,r6 + add r6,r6,r5 + bdnz 0b + sync + +/* Now invalidate the icache */ + + lhz r4,ICACHEL1LINESPERPAGE(r7) /* Get # icache lines per page */ + lhz r5,ICACHEL1LINESIZE(r7) /* Get icache line size */ + mtctr r4 +1: icbi 0,r3 + add r3,r3,r5 + bdnz 1b + isync + blr + +/* + * Copy a whole page. Assumes a 4096B page size. + */ +_GLOBAL(copy_page) + clrrdi r3,r3,12 /* Page align */ + clrrdi r4,r4,12 /* Page align */ + li r5,256 + mtctr r5 + addi r3,r3,-8 + addi r4,r4,-8 + +1: ld r6,8(r4) + ldu r7,16(r4) + std r6,8(r3) + stdu r7,16(r3) + bdnz+ 1b + blr + +/* + * I/O string operations + * + * insb(port, buf, len) + * outsb(port, buf, len) + * insw(port, buf, len) + * outsw(port, buf, len) + * insl(port, buf, len) + * outsl(port, buf, len) + * insw_ns(port, buf, len) + * outsw_ns(port, buf, len) + * insl_ns(port, buf, len) + * outsl_ns(port, buf, len) + * + * The *_ns versions don't do byte-swapping. + */ +_GLOBAL(_insb) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,1 + blelr- +00: lbz r5,0(r3) + eieio + stbu r5,1(r4) + bdnz 00b + blr + +_GLOBAL(_outsb) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,1 + blelr- +00: lbzu r5,1(r4) + stb r5,0(r3) + eieio + bdnz 00b + blr + +_GLOBAL(_insw) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhbrx r5,0,r3 + eieio + sthu r5,2(r4) + bdnz 00b + blr + +_GLOBAL(_outsw) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhzu r5,2(r4) + eieio + sthbrx r5,0,r3 + bdnz 00b + blr + +_GLOBAL(_insl) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwbrx r5,0,r3 + eieio + stwu r5,4(r4) + bdnz 00b + blr + +_GLOBAL(_outsl) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwzu r5,4(r4) + stwbrx r5,0,r3 + eieio + bdnz 00b + blr + +_GLOBAL(ide_insw) +_GLOBAL(_insw_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhz r5,0(r3) + eieio + sthu r5,2(r4) + bdnz 00b + blr + +_GLOBAL(ide_outsw) +_GLOBAL(_outsw_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhzu r5,2(r4) + sth r5,0(r3) + eieio + bdnz 00b + blr + +_GLOBAL(_insl_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwz r5,0(r3) + eieio + stwu r5,4(r4) + bdnz 00b + blr + +_GLOBAL(_outsl_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwzu r5,4(r4) + stw r5,0(r3) + eieio + bdnz 00b + blr + +/* + * Extended precision shifts + * + * R3/R4 has 64 bit value + * R5 has shift count + * result in R3/R4 + * + * ashrdi3: XXXYYY/ZZZAAA -> SSSXXX/YYYZZZ + * ashldi3: XXXYYY/ZZZAAA -> YYYZZZ/AAA000 + * lshrdi3: XXXYYY/ZZZAAA -> 000XXX/YYYZZZ + */ +/* MIKEC: These may no longer be needed...what does gcc expect ? */ + +_GLOBAL(__ashrdi3) + li r6,32 + sub r6,r6,r5 + slw r7,r3,r6 /* isolate YYY */ + srw r4,r4,r5 /* isolate ZZZ */ + or r4,r4,r7 /* YYYZZZ */ + sraw r3,r3,r5 /* SSSXXX */ + blr + +_GLOBAL(__ashldi3) + li r6,32 + sub r6,r6,r5 + srw r7,r4,r6 /* isolate ZZZ */ + slw r4,r4,r5 /* AAA000 */ + slw r3,r3,r5 /* YYY--- */ + or r3,r3,r7 /* YYYZZZ */ + blr + +_GLOBAL(__lshrdi3) + li r6,32 + sub r6,r6,r5 + slw r7,r3,r6 /* isolate YYY */ + srw r4,r4,r5 /* isolate ZZZ */ + or r4,r4,r7 /* YYYZZZ */ + srw r3,r3,r5 /* 000XXX */ + blr + +_GLOBAL(abs) + cmpi 0,r3,0 + bge 10f + neg r3,r3 +10: blr + +_GLOBAL(_get_SP) + mr r3,r1 /* Close enough */ + blr + +_GLOBAL(_get_PVR) + mfspr r3,PVR + blr + +_GLOBAL(_get_PIR) + mfspr r3,PIR + blr + +_GLOBAL(_get_HID0) + mfspr r3,HID0 + blr + +_GLOBAL(cvt_fd) + lfd 0,-4(r5) /* load up fpscr value */ + mtfsf 0xff,0 + lfs 0,0(r3) + stfd 0,0(r4) + mffs 0 /* save new fpscr value */ + stfd 0,-4(r5) + blr + +_GLOBAL(cvt_df) + lfd 0,-4(r5) /* load up fpscr value */ + mtfsf 0xff,0 + lfd 0,0(r3) + stfs 0,0(r4) + mffs 0 /* save new fpscr value */ + stfd 0,-4(r5) + blr + +/* + * Create a kernel thread + * kernel_thread(fn, arg, flags) + */ +_GLOBAL(kernel_thread) + mr r6,r3 /* function */ + ori r3,r5,CLONE_VM /* flags */ + li r0,__NR_clone + sc + cmpi 0,r3,0 /* parent or child? */ + bnelr /* return if parent */ + + li r0,0 /* clear out p->thread.regs */ + std r0,THREAD+PT_REGS(r13) /* since we don't have user ctx */ + + clrrdi r5,r1,THREAD_SHIFT + ld r0,TI_FLAGS(r5) + li r7,_TIF_32BIT + andc r0,r0,r7 +#ifdef CONFIG_PPC_ISERIES + ori r0,r0,_TIF_RUN_LIGHT /* Run light on */ +#endif + std r0,TI_FLAGS(r5) + + ld r2,8(r6) + ld r6,0(r6) + mtlr r6 /* fn addr in lr */ + mr r3,r4 /* load arg and call fn */ + blrl + li r0,__NR_exit /* exit after child exits */ + li r3,0 + sc + +#ifdef CONFIG_BINFMT_ELF32 +/* Why isn't this a) automatic, b) written in 'C'? */ + .data + .align 8 +_GLOBAL(sys_call_table32) + .llong .sys_ni_syscall /* 0 - old "setup()" system call */ + .llong .sys32_exit + .llong .sys32_fork + .llong .sys_read + .llong .sys_write + .llong .sys32_open /* 5 */ + .llong .sys_close + .llong .sys32_waitpid + .llong .sys32_creat + .llong .sys_link + .llong .sys_unlink /* 10 */ + .llong .sys32_execve + .llong .sys_chdir + .llong .sys32_time + .llong .sys32_mknod + .llong .sys32_chmod /* 15 */ + .llong .sys_lchown + .llong .sys_ni_syscall /* old break syscall holder */ + .llong .sys32_stat + .llong .sys32_lseek + .llong .sys_getpid /* 20 */ + .llong .sys32_mount + .llong .sys_oldumount + .llong .sys_setuid + .llong .sys_getuid + .llong .ppc64_sys_stime /* 25 */ + .llong .sys32_ptrace + .llong .sys_alarm + .llong .sys32_fstat + .llong .sys32_pause + .llong .sys32_utime /* 30 */ + .llong .sys_ni_syscall /* old stty syscall holder */ + .llong .sys_ni_syscall /* old gtty syscall holder */ + .llong .sys32_access + .llong .sys32_nice + .llong .sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .llong .sys_sync + .llong .sys32_kill + .llong .sys_rename + .llong .sys32_mkdir + .llong .sys_rmdir /* 40 */ + .llong .sys_dup + .llong .sys_pipe + .llong .sys32_times + .llong .sys_ni_syscall /* old prof syscall holder */ + .llong .sys_brk /* 45 */ + .llong .sys_setgid + .llong .sys_getgid + .llong .sys_signal + .llong .sys_geteuid + .llong .sys_getegid /* 50 */ + .llong .sys_acct + .llong .sys32_umount /* recycled never used phys() */ + .llong .sys_ni_syscall /* old lock syscall holder */ + .llong .sys32_ioctl + .llong .sys32_fcntl /* 55 */ + .llong .sys_ni_syscall /* old mpx syscall holder */ + .llong .sys32_setpgid + .llong .sys_ni_syscall /* old ulimit syscall holder */ + .llong .sys_olduname + .llong .sys32_umask /* 60 */ + .llong .sys_chroot + .llong .sys_ustat + .llong .sys_dup2 + .llong .sys_getppid + .llong .sys_getpgrp /* 65 */ + .llong .sys_setsid + .llong .sys32_sigaction + .llong .sys_sgetmask + .llong .sys32_ssetmask + .llong .sys_setreuid /* 70 */ + .llong .sys_setregid + .llong .sys_sigsuspend + .llong .sys32_sigpending + .llong .sys32_sethostname + .llong .sys32_setrlimit /* 75 */ + .llong .sys32_old_getrlimit + .llong .sys32_getrusage + .llong .sys32_gettimeofday + .llong .sys32_settimeofday + .llong .sys32_getgroups /* 80 */ + .llong .sys32_setgroups + .llong .ppc32_select + .llong .sys_symlink + .llong .sys32_lstat + .llong .sys32_readlink /* 85 */ + .llong .sys_uselib + .llong .sys32_swapon + .llong .sys32_reboot + .llong .old32_readdir + .llong .sys32_mmap /* 90 */ + .llong .sys_munmap + .llong .sys_truncate + .llong .sys_ftruncate + .llong .sys_fchmod + .llong .sys_fchown /* 95 */ + .llong .sys32_getpriority + .llong .sys32_setpriority + .llong .sys_ni_syscall /* old profil syscall holder */ + .llong .sys32_statfs + .llong .sys32_fstatfs /* 100 */ + .llong .sys32_ioperm + .llong .sys32_socketcall + .llong .sys32_syslog + .llong .sys32_setitimer + .llong .sys32_getitimer /* 105 */ + .llong .sys32_newstat + .llong .sys32_newlstat + .llong .sys32_newfstat + .llong .sys_uname + .llong .sys32_iopl /* 110 */ + .llong .sys_vhangup + .llong .sys_ni_syscall /* old 'idle' syscall */ + .llong .sys32_vm86 + .llong .sys32_wait4 + .llong .sys_swapoff /* 115 */ + .llong .sys32_sysinfo + .llong .sys32_ipc + .llong .sys_fsync + .llong .ppc32_sigreturn + .llong .sys32_clone /* 120 */ + .llong .sys32_setdomainname + .llong .ppc64_newuname + .llong .sys32_modify_ldt + .llong .sys32_adjtimex + .llong .sys_mprotect /* 125 */ + .llong .sys32_sigprocmask + .llong .sys32_create_module + .llong .sys32_init_module + .llong .sys32_delete_module + .llong .sys32_get_kernel_syms /* 130 */ + .llong .sys32_quotactl + .llong .sys32_getpgid + .llong .sys_fchdir + .llong .sys32_bdflush + .llong .sys32_sysfs /* 135 */ + .llong .sys32_personality + .llong .sys_ni_syscall /* for afs_syscall */ + .llong .sys_setfsuid + .llong .sys_setfsgid + .llong .sys_llseek /* 140 */ + .llong .sys32_getdents + .llong .ppc32_select + .llong .sys_flock + .llong .sys32_msync + .llong .sys32_readv /* 145 */ + .llong .sys32_writev + .llong .sys32_getsid + .llong .sys_fdatasync + .llong .sys32_sysctl + .llong .sys_mlock /* 150 */ + .llong .sys_munlock + .llong .sys32_mlockall + .llong .sys_munlockall + .llong .sys32_sched_setparam + .llong .sys32_sched_getparam /* 155 */ + .llong .sys32_sched_setscheduler + .llong .sys32_sched_getscheduler + .llong .sys_sched_yield + .llong .sys32_sched_get_priority_max + .llong .sys32_sched_get_priority_min /* 160 */ + .llong .sys32_sched_rr_get_interval + .llong .sys32_nanosleep + .llong .sys32_mremap + .llong .sys_setresuid + .llong .sys_getresuid /* 165 */ + .llong .sys32_query_module + .llong .sys_poll + .llong .sys32_nfsservctl + .llong .sys_setresgid + .llong .sys_getresgid /* 170 */ + .llong .sys32_prctl + .llong .ppc32_rt_sigreturn + .llong .sys32_rt_sigaction + .llong .sys32_rt_sigprocmask + .llong .sys32_rt_sigpending /* 175 */ + .llong .sys32_rt_sigtimedwait + .llong .sys32_rt_sigqueueinfo + .llong .sys32_rt_sigsuspend + .llong .sys32_pread + .llong .sys32_pwrite /* 180 */ + .llong .sys_chown + .llong .sys_getcwd + .llong .sys_capget + .llong .sys_capset + .llong .sys32_sigaltstack /* 185 */ + .llong .sys32_sendfile + .llong .sys_ni_syscall /* streams1 */ + .llong .sys_ni_syscall /* streams2 */ + .llong .sys32_vfork + .llong .sys32_getrlimit /* 190 */ + .llong .sys_ni_syscall /* 191 */ /* Unused */ + .llong .sys_ni_syscall /* 192 - reserved - mmap2 */ + .llong .sys32_truncate64 /* 193 - truncate64 */ + .llong .sys32_ftruncate64 /* 194 - ftruncate64 */ + .llong .sys_stat64 /* 195 - stat64 */ + .llong .sys_lstat64 /* 196 - lstat64 */ + .llong .sys_fstat64 /* 197 - fstat64 */ + .llong .sys32_pciconfig_read /* 198 */ + .llong .sys32_pciconfig_write /* 199 */ + .llong .sys_ni_syscall /* 200 - reserved - sys_pciconfig_iobase */ + .llong .sys_ni_syscall /* 201 - reserved - MacOnLinux - new */ + .llong .sys_getdents64 /* 202 */ + .llong .sys_pivot_root /* 203 */ + .llong .sys32_fcntl64 /* 204 */ + .llong .sys_madvise /* 205 */ + .llong .sys_mincore /* 206 */ + .llong .sys_gettid /* 207 */ + .llong .sys_tkill /* 208 */ + .rept NR_syscalls-208 + .llong .sys_ni_syscall + .endr +#endif + .data + .align 8 +_GLOBAL(sys_call_table) + .llong .sys_ni_syscall /* 0 - old "setup()" system call */ + .llong .sys_exit + .llong .sys_fork + .llong .sys_read + .llong .sys_write + .llong .sys_open /* 5 */ + .llong .sys_close + .llong .sys_waitpid + .llong .sys_creat + .llong .sys_link + .llong .sys_unlink /* 10 */ + .llong .sys_execve + .llong .sys_chdir + .llong .sys64_time + .llong .sys_mknod + .llong .sys_chmod /* 15 */ + .llong .sys_lchown + .llong .sys_ni_syscall /* old break syscall holder */ + .llong .sys_stat + .llong .sys_lseek + .llong .sys_getpid /* 20 */ + .llong .sys_mount + .llong .sys_oldumount + .llong .sys_setuid + .llong .sys_getuid + .llong .ppc64_sys_stime /* 25 */ + .llong .sys_ptrace + .llong .sys_alarm + .llong .sys_fstat + .llong .sys_pause + .llong .sys_utime /* 30 */ + .llong .sys_ni_syscall /* old stty syscall holder */ + .llong .sys_ni_syscall /* old gtty syscall holder */ + .llong .sys_access + .llong .sys_nice + .llong .sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .llong .sys_sync + .llong .sys_kill + .llong .sys_rename + .llong .sys_mkdir + .llong .sys_rmdir /* 40 */ + .llong .sys_dup + .llong .sys_pipe + .llong .sys_times + .llong .sys_ni_syscall /* old prof syscall holder */ + .llong .sys_brk /* 45 */ + .llong .sys_setgid + .llong .sys_getgid + .llong .sys_signal + .llong .sys_geteuid + .llong .sys_getegid /* 50 */ + .llong .sys_acct + .llong .sys_umount /* recycled never used phys() */ + .llong .sys_ni_syscall /* old lock syscall holder */ + .llong .sys_ioctl + .llong .sys_fcntl /* 55 */ + .llong .sys_ni_syscall /* old mpx syscall holder */ + .llong .sys_setpgid + .llong .sys_ni_syscall /* old ulimit syscall holder */ + .llong .sys_olduname + .llong .sys_umask /* 60 */ + .llong .sys_chroot + .llong .sys_ustat + .llong .sys_dup2 + .llong .sys_getppid + .llong .sys_getpgrp /* 65 */ + .llong .sys_setsid + .llong .sys_sigaction + .llong .sys_sgetmask + .llong .sys_ssetmask + .llong .sys_setreuid /* 70 */ + .llong .sys_setregid + .llong .sys_sigsuspend + .llong .sys_sigpending + .llong .sys_sethostname + .llong .sys_setrlimit /* 75 */ + .llong .sys_old_getrlimit + .llong .sys_getrusage + .llong .sys_gettimeofday + .llong .sys_settimeofday + .llong .sys_getgroups /* 80 */ + .llong .sys_setgroups + .llong .sys_select + .llong .sys_symlink + .llong .sys_lstat + .llong .sys_readlink /* 85 */ + .llong .sys_uselib + .llong .sys_swapon + .llong .sys_reboot + .llong .old_readdir + .llong .sys_mmap /* 90 */ + .llong .sys_munmap + .llong .sys_truncate + .llong .sys_ftruncate + .llong .sys_fchmod + .llong .sys_fchown /* 95 */ + .llong .sys_getpriority + .llong .sys_setpriority + .llong .sys_ni_syscall /* old profil syscall holder */ + .llong .sys_statfs + .llong .sys_fstatfs /* 100 */ + .llong .sys_ioperm + .llong .sys_socketcall + .llong .sys_syslog + .llong .sys_setitimer + .llong .sys_getitimer /* 105 */ + .llong .sys_newstat + .llong .sys_newlstat + .llong .sys_newfstat + .llong .sys_uname + .llong .sys_iopl /* 110 */ + .llong .sys_vhangup + .llong .sys_ni_syscall /* old 'idle' syscall */ + .llong .sys_vm86 + .llong .sys_wait4 + .llong .sys_swapoff /* 115 */ + .llong .sys_sysinfo + .llong .sys_ipc + .llong .sys_fsync + .llong .ppc64_sigreturn + .llong .sys_clone /* 120 */ + .llong .sys_setdomainname + .llong .ppc64_newuname + .llong .sys_modify_ldt + .llong .sys_adjtimex + .llong .sys_mprotect /* 125 */ + .llong .sys_sigprocmask + .llong .sys_create_module + .llong .sys_init_module + .llong .sys_delete_module + .llong .sys_get_kernel_syms /* 130 */ + .llong .sys_quotactl + .llong .sys_getpgid + .llong .sys_fchdir + .llong .sys_bdflush + .llong .sys_sysfs /* 135 */ + .llong .sys_personality + .llong .sys_ni_syscall /* for afs_syscall */ + .llong .sys_setfsuid + .llong .sys_setfsgid + .llong .sys_llseek /* 140 */ + .llong .sys_getdents + .llong .sys_select + .llong .sys_flock + .llong .sys_msync + .llong .sys_readv /* 145 */ + .llong .sys_writev + .llong .sys_getsid + .llong .sys_fdatasync + .llong .sys_sysctl + .llong .sys_mlock /* 150 */ + .llong .sys_munlock + .llong .sys_mlockall + .llong .sys_munlockall + .llong .sys_sched_setparam + .llong .sys_sched_getparam /* 155 */ + .llong .sys_sched_setscheduler + .llong .sys_sched_getscheduler + .llong .sys_sched_yield + .llong .sys_sched_get_priority_max + .llong .sys_sched_get_priority_min /* 160 */ + .llong .sys_sched_rr_get_interval + .llong .sys_nanosleep + .llong .sys_mremap + .llong .sys_setresuid + .llong .sys_getresuid /* 165 */ + .llong .sys_query_module + .llong .sys_poll + .llong .sys_nfsservctl + .llong .sys_setresgid + .llong .sys_getresgid /* 170 */ + .llong .sys_prctl + .llong .ppc64_rt_sigreturn + .llong .sys_rt_sigaction + .llong .sys_rt_sigprocmask + .llong .sys_rt_sigpending /* 175 */ + .llong .sys_rt_sigtimedwait + .llong .sys_rt_sigqueueinfo + .llong .sys_rt_sigsuspend + .llong .sys_pread + .llong .sys_pwrite /* 180 */ + .llong .sys_chown + .llong .sys_getcwd + .llong .sys_capget + .llong .sys_capset + .llong .sys_sigaltstack /* 185 */ + .llong .sys_sendfile + .llong .sys_ni_syscall /* streams1 */ + .llong .sys_ni_syscall /* streams2 */ + .llong .sys_vfork + .llong .sys_getrlimit /* 190 */ + .llong .sys_ni_syscall /* 191 */ /* Unused */ + .llong .sys_ni_syscall /* 192 - reserved - mmap2 */ + .llong .sys_ni_syscall /* 193 - reserved - truncate64 */ + .llong .sys_ni_syscall /* 194 - reserved - ftruncate64 */ + .llong .sys_ni_syscall /* 195 - reserved - stat64 */ + .llong .sys_ni_syscall /* 196 - reserved - lstat64 */ + .llong .sys_ni_syscall /* 197 - reserved - fstat64 */ + .llong .sys_pciconfig_read /* 198 */ + .llong .sys_pciconfig_write /* 199 */ + .llong .sys_ni_syscall /* 200 - reserved - sys_pciconfig_iobase */ + .llong .sys_ni_syscall /* 201 - reserved - MacOnLinux - new */ + .llong .sys_getdents64 /* 202 */ + .llong .sys_pivot_root /* 203 */ + .llong .sys_ni_syscall /* 204 */ + .llong .sys_madvise /* 205 */ + .llong .sys_mincore /* 206 */ + .llong .sys_gettid /* 207 */ + .llong .sys_tkill /* 208 */ + .rept NR_syscalls-208 + .llong .sys_ni_syscall + .endr diff -Nru a/arch/ppc64/kernel/mk_defs.c b/arch/ppc64/kernel/mk_defs.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/mk_defs.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,162 @@ +/* + * This program is used to generate definitions needed by + * assembly language modules. + * + * We use the technique used in the OSF Mach kernel code: + * generate asm statements containing #defines, + * compile this file to assembler, and then extract the + * #defines from the assembly-language output. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DEFINE(sym, val) \ + asm volatile("\n#define\t" #sym "\t%0" : : "i" (val)) + +int +main(void) +{ + /* thread struct on stack */ + DEFINE(THREAD_SHIFT, THREAD_SHIFT); + DEFINE(THREAD_SIZE, THREAD_SIZE); + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(_TIF_32BIT, _TIF_32BIT); + + /* task_struct->thread */ + DEFINE(THREAD, offsetof(struct task_struct, thread)); + DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); + DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); + DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); + DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); + DEFINE(KSP, offsetof(struct thread_struct, ksp)); + + DEFINE(MM, offsetof(struct task_struct, mm)); + + /* Naca */ + DEFINE(DCACHEL1LINESIZE, offsetof(struct Naca, dCacheL1LineSize)); + DEFINE(DCACHEL1LOGLINESIZE, offsetof(struct Naca, dCacheL1LogLineSize)); + DEFINE(DCACHEL1LINESPERPAGE, offsetof(struct Naca, dCacheL1LinesPerPage)); + DEFINE(ICACHEL1LINESIZE, offsetof(struct Naca, iCacheL1LineSize)); + DEFINE(ICACHEL1LOGLINESIZE, offsetof(struct Naca, iCacheL1LogLineSize)); + DEFINE(ICACHEL1LINESPERPAGE, offsetof(struct Naca, iCacheL1LinesPerPage)); + DEFINE(SLBSIZE, offsetof(struct Naca, slb_size)); + + /* Paca */ + DEFINE(PACA, offsetof(struct Naca, paca)); + DEFINE(PACA_SIZE, sizeof(struct Paca)); + DEFINE(PACAPACAINDEX, offsetof(struct Paca, xPacaIndex)); + DEFINE(PACAPROCSTART, offsetof(struct Paca, xProcStart)); + DEFINE(PACAKSAVE, offsetof(struct Paca, xKsave)); + DEFINE(PACACURRENT, offsetof(struct Paca, xCurrent)); + DEFINE(PACASAVEDMSR, offsetof(struct Paca, xSavedMsr)); + DEFINE(PACASTABREAL, offsetof(struct Paca, xStab_data.real)); + DEFINE(PACASTABVIRT, offsetof(struct Paca, xStab_data.virt)); + DEFINE(PACASTABRR, offsetof(struct Paca, xStab_data.next_round_robin)); + DEFINE(PACAR1, offsetof(struct Paca, xR1)); + DEFINE(PACALPQUEUE, offsetof(struct Paca, lpQueuePtr)); + DEFINE(PACATOC, offsetof(struct Paca, xTOC)); + DEFINE(PACAEXCSP, offsetof(struct Paca, exception_sp)); + DEFINE(PACAHRDWINTSTACK, offsetof(struct Paca, xHrdIntStack)); + DEFINE(PACAPROCENABLED, offsetof(struct Paca, xProcEnabled)); + DEFINE(PACAHRDWINTCOUNT, offsetof(struct Paca, xHrdIntCount)); + DEFINE(PACADEFAULTDECR, offsetof(struct Paca, default_decr)); + DEFINE(PACAPROFENABLED, offsetof(struct Paca, prof_enabled)); + DEFINE(PACAPROFLEN, offsetof(struct Paca, prof_len)); + DEFINE(PACAPROFSHIFT, offsetof(struct Paca, prof_shift)); + DEFINE(PACAPROFBUFFER, offsetof(struct Paca, prof_buffer)); + DEFINE(PACAPROFSTEXT, offsetof(struct Paca, prof_stext)); + DEFINE(PACALPPACA, offsetof(struct Paca, xLpPaca)); + DEFINE(LPPACA, offsetof(struct Paca, xLpPaca)); + DEFINE(PACAREGSAV, offsetof(struct Paca, xRegSav)); + DEFINE(PACAEXC, offsetof(struct Paca, exception_stack)); + DEFINE(PACAGUARD, offsetof(struct Paca, guard)); + DEFINE(LPPACASRR0, offsetof(struct ItLpPaca, xSavedSrr0)); + DEFINE(LPPACASRR1, offsetof(struct ItLpPaca, xSavedSrr1)); + DEFINE(LPPACAANYINT, offsetof(struct ItLpPaca, xIntDword.xAnyInt)); + DEFINE(LPPACADECRINT, offsetof(struct ItLpPaca, xIntDword.xFields.xDecrInt)); + DEFINE(LPQCUREVENTPTR, offsetof(struct ItLpQueue, xSlicCurEventPtr)); + DEFINE(LPQOVERFLOW, offsetof(struct ItLpQueue, xPlicOverflowIntPending)); + DEFINE(LPEVENTFLAGS, offsetof(struct HvLpEvent, xFlags)); + DEFINE(PROMENTRY, offsetof(struct prom_t, entry)); + + /* RTAS */ + DEFINE(RTASBASE, offsetof(struct rtas_t, base)); + DEFINE(RTASENTRY, offsetof(struct rtas_t, entry)); + DEFINE(RTASSIZE, offsetof(struct rtas_t, size)); + + /* Interrupt register frame */ + DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); + + /* 288 = # of volatile regs, int & fp, for leaf routines */ + /* which do not stack a frame. See the PPC64 ABI. */ + DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 288); + /* Create extra stack space for SRR0 and SRR1 when calling prom/rtas. */ + DEFINE(PROM_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16 + 288); + DEFINE(RTAS_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16 + 288); + DEFINE(GPR0, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[0])); + DEFINE(GPR1, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[1])); + DEFINE(GPR2, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[2])); + DEFINE(GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[3])); + DEFINE(GPR4, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[4])); + DEFINE(GPR5, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[5])); + DEFINE(GPR6, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[6])); + DEFINE(GPR7, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[7])); + DEFINE(GPR8, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[8])); + DEFINE(GPR9, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[9])); + DEFINE(GPR20, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[20])); + DEFINE(GPR21, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[21])); + DEFINE(GPR22, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[22])); + DEFINE(GPR23, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[23])); + /* + * Note: these symbols include _ because they overlap with special + * register names + */ + DEFINE(_NIP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, nip)); + DEFINE(_MSR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, msr)); + DEFINE(_CTR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ctr)); + DEFINE(_LINK, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, link)); + DEFINE(_CCR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ccr)); + DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer)); + DEFINE(_DAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar)); + DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr)); + DEFINE(ORIG_GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3)); + DEFINE(RESULT, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result)); + DEFINE(TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap)); + DEFINE(SOFTE, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, softe)); + + /* These _only_ to be used with {PROM,RTAS}_FRAME_SIZE!!! */ + DEFINE(_SRR0, STACK_FRAME_OVERHEAD+sizeof(struct pt_regs)); + DEFINE(_SRR1, STACK_FRAME_OVERHEAD+sizeof(struct pt_regs)+8); + + DEFINE(CLONE_VM, CLONE_VM); + + return 0; +} diff -Nru a/arch/ppc64/kernel/open_pic.c b/arch/ppc64/kernel/open_pic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/open_pic.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,827 @@ +/* + * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * 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 archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "local_irq.h" +#include "open_pic.h" +#include "open_pic_defs.h" +#include "i8259.h" +#include + +void* OpenPIC_Addr; +static volatile struct OpenPIC *OpenPIC = NULL; +u_int OpenPIC_NumInitSenses __initdata = 0; +u_char *OpenPIC_InitSenses __initdata = NULL; +extern int use_of_interrupt_tree; + +void find_ISUs(void); + +static u_int NumProcessors; +static u_int NumSources; +static int NumISUs; +static int open_pic_irq_offset; +static volatile unsigned char* chrp_int_ack_special; +static int broken_ipi_registers; + +OpenPIC_SourcePtr ISU[OPENPIC_MAX_ISU]; + +static void openpic_end_irq(unsigned int irq_nr); +static void openpic_ack_irq(unsigned int irq_nr); +static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask); + +struct hw_interrupt_type open_pic = { + " OpenPIC ", + NULL, + NULL, + openpic_enable_irq, + openpic_disable_irq, + openpic_ack_irq, + openpic_end_irq, + openpic_set_affinity +}; + +#ifdef CONFIG_SMP +static void openpic_end_ipi(unsigned int irq_nr); +static void openpic_ack_ipi(unsigned int irq_nr); +static void openpic_enable_ipi(unsigned int irq_nr); +static void openpic_disable_ipi(unsigned int irq_nr); + +struct hw_interrupt_type open_pic_ipi = { + " OpenPIC ", + NULL, + NULL, + openpic_enable_ipi, + openpic_disable_ipi, + openpic_ack_ipi, + openpic_end_ipi, + 0 +}; +#endif /* CONFIG_SMP */ + +unsigned int openpic_vec_ipi; +unsigned int openpic_vec_timer; +unsigned int openpic_vec_spurious; + +/* + * Accesses to the current processor's openpic registers + */ +#ifdef CONFIG_SMP +#define THIS_CPU Processor[cpu] +#define DECL_THIS_CPU int cpu = hard_smp_processor_id() +#define CHECK_THIS_CPU check_arg_cpu(cpu) +#else +#define THIS_CPU Processor[hard_smp_processor_id()] +#define DECL_THIS_CPU +#define CHECK_THIS_CPU +#endif /* CONFIG_SMP */ + +#if 0 +#define check_arg_ipi(ipi) \ + if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ + printk(KERN_ERR "open_pic.c:%d: illegal ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ + if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ + printk(KERN_ERR "open_pic.c:%d: illegal timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ + if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ + printk(KERN_ERR "open_pic.c:%d: illegal vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ + if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ + printk(KERN_ERR "open_pic.c:%d: illegal priority %d\n", __LINE__, pri); +/* + * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's + * data has probably been corrupted and we're going to panic or deadlock later + * anyway --Troy + */ +extern unsigned long* _get_SP(void); +#define check_arg_irq(irq) \ + if (irq < open_pic_irq_offset || irq >= (NumSources+open_pic_irq_offset)){ \ + printk(KERN_ERR "open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \ + print_backtrace(_get_SP()); } +#define check_arg_cpu(cpu) \ + if (cpu < 0 || cpu >= OPENPIC_MAX_PROCESSORS){ \ + printk(KERN_ERR "open_pic.c:%d: illegal cpu %d\n", __LINE__, cpu); \ + print_backtrace(_get_SP()); } +#else +#define check_arg_ipi(ipi) do {} while (0) +#define check_arg_timer(timer) do {} while (0) +#define check_arg_vec(vec) do {} while (0) +#define check_arg_pri(pri) do {} while (0) +#define check_arg_irq(irq) do {} while (0) +#define check_arg_cpu(cpu) do {} while (0) +#endif + +#define GET_ISU(source) ISU[(source) >> 4][(source) & 0xf] + +void __init openpic_init_IRQ(void) +{ + struct device_node *np; + int i; + unsigned int *addrp; + unsigned char* chrp_int_ack_special = 0; + unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS]; + int nmi_irq = -1; +#if defined(CONFIG_VT) && defined(CONFIG_ADB_KEYBOARD) && defined(XMON) + struct device_node *kbd; +#endif + + if (!(np = find_devices("pci")) + || !(addrp = (unsigned int *) + get_property(np, "8259-interrupt-acknowledge", NULL))) + printk(KERN_ERR "Cannot find pci to get ack address\n"); + else + chrp_int_ack_special = (unsigned char *) + __ioremap(addrp[prom_n_addr_cells(np)-1], 1, _PAGE_NO_CACHE); + /* hydra still sets OpenPIC_InitSenses to a static set of values */ + if (OpenPIC_InitSenses == NULL) { + prom_get_irq_senses(init_senses, NUM_8259_INTERRUPTS, NR_IRQS); + OpenPIC_InitSenses = init_senses; + OpenPIC_NumInitSenses = NR_IRQS - NUM_8259_INTERRUPTS; + } + openpic_init(1, NUM_8259_INTERRUPTS, chrp_int_ack_special, nmi_irq); + for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ ) + irq_desc[i].handler = &i8259_pic; + i8259_init(); +} + +static inline u_int openpic_read(volatile u_int *addr) +{ + u_int val; + + val = in_le32(addr); + return val; +} + +static inline void openpic_write(volatile u_int *addr, u_int val) +{ + out_le32(addr, val); +} + +static inline u_int openpic_readfield(volatile u_int *addr, u_int mask) +{ + u_int val = openpic_read(addr); + return val & mask; +} + +static inline void openpic_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + u_int val = openpic_read(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, 0); +} + +static inline void openpic_setfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, mask); +} + +static void openpic_safe_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + unsigned int loops = 100000; + + openpic_setfield(addr, OPENPIC_MASK); + while (openpic_read(addr) & OPENPIC_ACTIVITY) { + if (!loops--) { + printk(KERN_ERR "openpic_safe_writefield timeout\n"); + break; + } + } + openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + +#ifdef CONFIG_SMP +static u_int openpic_read_IPI(volatile u_int* addr) +{ + u_int val = 0; + + if (broken_ipi_registers) + /* yes this is right ... bug, feature, you decide! -- tgall */ + val = in_be32(addr); + else + val = in_le32(addr); + + return val; +} + +static void openpic_test_broken_IPI(void) +{ + u_int t; + + openpic_write(&OpenPIC->Global.IPI_Vector_Priority(0), OPENPIC_MASK); + t = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(0)); + if (t == le32_to_cpu(OPENPIC_MASK)) { + printk(KERN_INFO "OpenPIC reversed IPI registers detected\n"); + broken_ipi_registers = 1; + } +} + +/* because of the power3 be / le above, this is needed */ +static inline void openpic_writefield_IPI(volatile u_int* addr, u_int mask, u_int field) +{ + u_int val = openpic_read_IPI(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield_IPI(volatile u_int *addr, u_int mask) +{ + openpic_writefield_IPI(addr, mask, 0); +} + +static inline void openpic_setfield_IPI(volatile u_int *addr, u_int mask) +{ + openpic_writefield_IPI(addr, mask, mask); +} + +static void openpic_safe_writefield_IPI(volatile u_int *addr, u_int mask, u_int field) +{ + unsigned int loops = 100000; + + openpic_setfield_IPI(addr, OPENPIC_MASK); + + /* wait until it's not in use */ + /* BenH: Is this code really enough ? I would rather check the result + * and eventually retry ... + */ + while(openpic_read_IPI(addr) & OPENPIC_ACTIVITY) { + if (!loops--) { + printk(KERN_ERR "openpic_safe_writefield timeout\n"); + break; + } + } + + openpic_writefield_IPI(addr, mask, field | OPENPIC_MASK); +} +#endif /* CONFIG_SMP */ + +void __init openpic_init(int main_pic, int offset, unsigned char* chrp_ack, + int programmer_switch_irq) +{ + u_int t, i; + u_int timerfreq; + const char *version; + + if (!OpenPIC_Addr) { + printk(KERN_INFO "No OpenPIC found !\n"); + return; + } + OpenPIC = (volatile struct OpenPIC *)OpenPIC_Addr; + + ppc_md.progress("openpic enter",0x122); + + t = openpic_read(&OpenPIC->Global.Feature_Reporting0); + switch (t & OPENPIC_FEATURE_VERSION_MASK) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + case 3: + version = "1.3"; + break; + default: + version = "?"; + break; + } + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; + printk(KERN_INFO "OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", + version, NumProcessors, NumSources, OpenPIC); + timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); + if (timerfreq) + printk(KERN_INFO "OpenPIC timer frequency is %d.%06d MHz\n", + timerfreq / 1000000, timerfreq % 1000000); + + if (!main_pic) + return; + + open_pic_irq_offset = offset; + chrp_int_ack_special = (volatile unsigned char*)chrp_ack; + + find_ISUs(); + + /* Initialize timer interrupts */ + ppc_md.progress("openpic timer",0x3ba); + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic_inittimer(i, 0, openpic_vec_timer+i); + /* No processor */ + openpic_maptimer(i, 0); + } + +#ifdef CONFIG_SMP + /* Initialize IPI interrupts */ + ppc_md.progress("openpic ipi",0x3bb); + openpic_test_broken_IPI(); + for (i = 0; i < OPENPIC_NUM_IPI; i++) { + /* Disabled, Priority 10..13 */ + openpic_initipi(i, 10+i, openpic_vec_ipi+i); + /* IPIs are per-CPU */ + irq_desc[openpic_vec_ipi+i].status |= IRQ_PER_CPU; + irq_desc[openpic_vec_ipi+i].handler = &open_pic_ipi; + } +#endif + + /* Initialize external interrupts */ + ppc_md.progress("openpic ext",0x3bc); + + openpic_set_priority(0xf); + + /* SIOint (8259 cascade) is special */ + if (offset) { + openpic_initirq(0, 8, offset, 1, 1); + openpic_mapirq(0, 1<= OPENPIC_MAX_ISU) + return; + ISU[isu_num] = (OpenPIC_SourcePtr) __ioremap(addr, 0x400, _PAGE_NO_CACHE); + if (isu_num >= NumISUs) + NumISUs = isu_num + 1; +} + +void find_ISUs(void) +{ + /* Use /interrupt-controller/reg and + * /interrupt-controller/interrupt-ranges from OF device tree + * the ISU array is setup in chrp_pci.c in ibm_add_bridges + * as a result + * -- tgall + */ + + /* basically each ISU is a bus, and this assumes that + * open_pic_isu_count interrupts per bus are possible + * ISU == Interrupt Source + */ + NumSources = NumISUs * 0x10; + openpic_vec_ipi = NumSources + open_pic_irq_offset; + openpic_vec_timer = openpic_vec_ipi + OPENPIC_NUM_IPI; + openpic_vec_spurious = openpic_vec_timer + OPENPIC_NUM_TIMERS; +} + +static inline void openpic_reset(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); +} + +static inline void openpic_enable_8259_pass_through(void) +{ + openpic_clearfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +static void openpic_disable_8259_pass_through(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +/* + * Find out the current interrupt + */ +static u_int openpic_irq(void) +{ + u_int vec; + DECL_THIS_CPU; + + CHECK_THIS_CPU; + vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); + return vec; +} + +static void openpic_eoi(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + openpic_write(&OpenPIC->THIS_CPU.EOI, 0); + /* Handle PCI write posting */ + (void)openpic_read(&OpenPIC->THIS_CPU.EOI); +} + + +static inline u_int openpic_get_priority(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} + +static void openpic_set_priority(u_int pri) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + check_arg_pri(pri); + openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + +/* + * Get/set the spurious vector + */ +static inline u_int openpic_get_spurious(void) +{ + return openpic_readfield(&OpenPIC->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} + +static void openpic_set_spurious(u_int vec) +{ + check_arg_vec(vec); + openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + +/* + * Convert a cpu mask from logical to physical cpu numbers. + */ +static inline u32 physmask(u32 cpumask) +{ + int i; + u32 mask = 0; + + for (i = 0; i < smp_num_cpus; ++i, cpumask >>= 1) + mask |= (cpumask & 1) << get_hard_smp_processor_id(i); + return mask; +} + +void openpic_init_processor(u_int cpumask) +{ + openpic_write(&OpenPIC->Global.Processor_Initialization, + physmask(cpumask)); +} + +#ifdef CONFIG_SMP +/* + * Initialize an interprocessor interrupt (and disable it) + * + * ipi: OpenPIC interprocessor interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init openpic_initipi(u_int ipi, u_int pri, u_int vec) +{ + check_arg_ipi(ipi); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield_IPI(&OpenPIC->Global.IPI_Vector_Priority(ipi), + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Send an IPI to one or more CPUs + * + * Externally called, however, it takes an IPI number (0...OPENPIC_NUM_IPI) + * and not a system-wide interrupt number + */ +void openpic_cause_IPI(u_int ipi, u_int cpumask) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + check_arg_ipi(ipi); + openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), + physmask(cpumask)); +} + +void openpic_request_IPIs(void) +{ + int i; + + /* + * Make sure this matches what is defined in smp.c for + * smp_message_{pass|recv}() or what shows up in + * /proc/interrupts will be wrong!!! --Troy */ + + if (OpenPIC == NULL) + return; + + request_irq(openpic_vec_ipi, + openpic_ipi_action, 0, "IPI0 (call function)", 0); + request_irq(openpic_vec_ipi+1, + openpic_ipi_action, 0, "IPI1 (reschedule)", 0); + request_irq(openpic_vec_ipi+2, + openpic_ipi_action, 0, "IPI2 (invalidate tlb)", 0); + request_irq(openpic_vec_ipi+3, + openpic_ipi_action, 0, "IPI3 (xmon break)", 0); + + for ( i = 0; i < OPENPIC_NUM_IPI ; i++ ) + openpic_enable_ipi(openpic_vec_ipi+i); +} + +/* + * Do per-cpu setup for SMP systems. + * + * Get IPI's working and start taking interrupts. + * -- Cort + */ +static spinlock_t openpic_setup_lock __initdata = SPIN_LOCK_UNLOCKED; + +void __init do_openpic_setup_cpu(void) +{ +#ifdef CONFIG_IRQ_ALL_CPUS + int i; + u32 msk = 1 << hard_smp_processor_id(); +#endif + + spin_lock(&openpic_setup_lock); + +#ifdef CONFIG_IRQ_ALL_CPUS + /* let the openpic know we want intrs. default affinity + * is 0xffffffff until changed via /proc + * That's how it's done on x86. If we want it differently, then + * we should make sure we also change the default values of irq_affinity + * in irq.c. + */ + for (i = 0; i < NumSources ; i++) + openpic_mapirq(i, openpic_read(&GET_ISU(i).Destination) | msk); +#endif /* CONFIG_IRQ_ALL_CPUS */ + openpic_set_priority(0); + + spin_unlock(&openpic_setup_lock); +} +#endif /* CONFIG_SMP */ + +/* + * Initialize a timer interrupt (and disable it) + * + * timer: OpenPIC timer number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init openpic_inittimer(u_int timer, u_int pri, u_int vec) +{ + check_arg_timer(timer); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Map a timer interrupt to one or more CPUs + */ +static void __init openpic_maptimer(u_int timer, u_int cpumask) +{ + check_arg_timer(timer); + openpic_write(&OpenPIC->Global.Timer[timer].Destination, + physmask(cpumask)); +} + + +/* + * + * All functions below take an offset'ed irq argument + * + */ + + +/* + * Enable/disable an external interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +static void openpic_enable_irq(u_int irq) +{ + unsigned int loops = 100000; + check_arg_irq(irq); + + openpic_clearfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + if (!loops--) { + printk(KERN_ERR "openpic_enable_irq timeout\n"); + break; + } + + mb(); /* sync is probably useless here */ + } while(openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, + OPENPIC_MASK)); +} + +static void openpic_disable_irq(u_int irq) +{ + u32 vp; + unsigned int loops = 100000; + + check_arg_irq(irq); + + openpic_setfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + if (!loops--) { + printk(KERN_ERR "openpic_disable_irq timeout\n"); + break; + } + + mb(); /* sync is probably useless here */ + vp = openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, + OPENPIC_MASK | OPENPIC_ACTIVITY); + } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); +} + +#ifdef CONFIG_SMP +/* + * Enable/disable an IPI interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +void openpic_enable_ipi(u_int irq) +{ + irq -= openpic_vec_ipi; + check_arg_ipi(irq); + openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK); + +} +void openpic_disable_ipi(u_int irq) +{ + /* NEVER disable an IPI... that's just plain wrong! */ +} + +#endif + +/* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ +static void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + openpic_safe_writefield(&GET_ISU(irq).Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_POLARITY_POSITIVE : + OPENPIC_POLARITY_NEGATIVE) | + (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); +} + +/* + * Map an interrupt source to one or more CPUs + */ +static void openpic_mapirq(u_int irq, u_int physmask) +{ + openpic_write(&GET_ISU(irq).Destination, physmask); +} + +/* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ +static inline void openpic_set_sense(u_int irq, int sense) +{ + openpic_safe_writefield(&GET_ISU(irq).Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} + +/* No spinlocks, should not be necessary with the OpenPIC + * (1 register = 1 interrupt and we have the desc lock). + */ +static void openpic_ack_irq(unsigned int irq_nr) +{ +} + +static void openpic_end_irq(unsigned int irq_nr) +{ + if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0) + openpic_eoi(); +} + +static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask) +{ + openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask)); +} + +#ifdef CONFIG_SMP +static void openpic_ack_ipi(unsigned int irq_nr) +{ +} + +static void openpic_end_ipi(unsigned int irq_nr) +{ + /* IPIs are marked IRQ_PER_CPU. This has the side effect of + * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from + * applying to them. We EOI them late to avoid re-entering. + * however, I'm wondering if we could simply let them have the + * SA_INTERRUPT flag and let them execute with all interrupts OFF. + * This would have the side effect of either running cross-CPU + * functions with interrupts off, or we can re-enable them explicitely + * with a __sti() in smp_call_function_interrupt(), since + * smp_call_function() is protected by a spinlock. + * Or maybe we shouldn't set the IRQ_PER_CPU flag on cross-CPU + * function calls IPI at all but that would make a special case. + */ + openpic_eoi(); +} + +static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + smp_message_recv(cpl-openpic_vec_ipi, regs); +} + +#endif /* CONFIG_SMP */ + +int openpic_get_irq(struct pt_regs *regs) +{ + extern int i8259_irq(int cpu); + + int irq = openpic_irq(); + + /* Management of the cascade should be moved out of here */ + if (open_pic_irq_offset && irq == open_pic_irq_offset) + { + /* + * This magic address generates a PCI IACK cycle. + */ + if ( chrp_int_ack_special ) + irq = *chrp_int_ack_special; + else + irq = i8259_irq( smp_processor_id() ); + openpic_eoi(); + } + if (irq == openpic_vec_spurious) + irq = -1; + return irq; +} diff -Nru a/arch/ppc64/kernel/open_pic.h b/arch/ppc64/kernel/open_pic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/open_pic.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,45 @@ +/* + * arch/ppc/kernel/open_pic.h -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * 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 archive + * for more details. + * + */ + +#ifndef _PPC64_KERNEL_OPEN_PIC_H +#define _PPC64_KERNEL_OPEN_PIC_H + +#include + +#define OPENPIC_SIZE 0x40000 + +/* OpenPIC IRQ controller structure */ +extern struct hw_interrupt_type open_pic; + +/* OpenPIC IPI controller structure */ +#ifdef CONFIG_SMP +extern struct hw_interrupt_type open_pic_ipi; +#endif /* CONFIG_SMP */ + +extern u_int OpenPIC_NumInitSenses; +extern u_char *OpenPIC_InitSenses; +extern void* OpenPIC_Addr; + +/* Exported functions */ +extern void openpic_init(int, int, unsigned char *, int); +extern void openpic_request_IPIs(void); +extern void do_openpic_setup_cpu(void); +extern int openpic_get_irq(struct pt_regs *regs); +extern void openpic_init_processor(u_int cpumask); +extern void openpic_setup_ISU(int isu_num, unsigned long addr); +extern void openpic_cause_IPI(u_int ipi, u_int cpumask); + +extern inline int openpic_to_irq(int irq) +{ + return irq += NUM_8259_INTERRUPTS; +} +/*extern int open_pic_irq_offset;*/ +#endif /* _PPC64_KERNEL_OPEN_PIC_H */ diff -Nru a/arch/ppc64/kernel/open_pic_defs.h b/arch/ppc64/kernel/open_pic_defs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/open_pic_defs.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,318 @@ +/* + * linux/openpic.h -- OpenPIC definitions + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is based on the following documentation: + * + * The Open Programmable Interrupt Controller (PIC) + * Register Interface Specification Revision 1.2 + * + * Issue Date: October 1995 + * + * Issued jointly by Advanced Micro Devices and Cyrix Corporation + * + * AMD is a registered trademark of Advanced Micro Devices, Inc. + * Copyright (C) 1995, Advanced Micro Devices, Inc. and Cyrix, Inc. + * All Rights Reserved. + * + * To receive a copy of this documentation, send an email to openpic@amd.com. + * + * 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 archive + * for more details. + */ + +#ifndef _LINUX_OPENPIC_H +#define _LINUX_OPENPIC_H + +#ifdef __KERNEL__ + +#include + +/* + * OpenPIC supports up to 2048 interrupt sources and up to 32 processors + */ + +#define OPENPIC_MAX_SOURCES 2048 +#define OPENPIC_MAX_PROCESSORS 32 +#define OPENPIC_MAX_ISU 32 + +#define OPENPIC_NUM_TIMERS 4 +#define OPENPIC_NUM_IPI 4 +#define OPENPIC_NUM_PRI 16 +#define OPENPIC_NUM_VECTORS OPENPIC_MAX_SOURCES + +/* + * OpenPIC Registers are 32 bits and aligned on 128 bit boundaries + */ + +typedef struct _OpenPIC_Reg { + u_int Reg; /* Little endian! */ + char Pad[0xc]; +} OpenPIC_Reg; + + +/* + * Per Processor Registers + */ + +typedef struct _OpenPIC_Processor { + /* + * Private Shadow Registers (for SLiC backwards compatibility) + */ + u_int IPI0_Dispatch_Shadow; /* Write Only */ + char Pad1[0x4]; + u_int IPI0_Vector_Priority_Shadow; /* Read/Write */ + char Pad2[0x34]; + /* + * Interprocessor Interrupt Command Ports + */ + OpenPIC_Reg _IPI_Dispatch[OPENPIC_NUM_IPI]; /* Write Only */ + /* + * Current Task Priority Register + */ + OpenPIC_Reg _Current_Task_Priority; /* Read/Write */ + char Pad3[0x10]; + /* + * Interrupt Acknowledge Register + */ + OpenPIC_Reg _Interrupt_Acknowledge; /* Read Only */ + /* + * End of Interrupt (EOI) Register + */ + OpenPIC_Reg _EOI; /* Read/Write */ + char Pad5[0xf40]; +} OpenPIC_Processor; + + + /* + * Timer Registers + */ + +typedef struct _OpenPIC_Timer { + OpenPIC_Reg _Current_Count; /* Read Only */ + OpenPIC_Reg _Base_Count; /* Read/Write */ + OpenPIC_Reg _Vector_Priority; /* Read/Write */ + OpenPIC_Reg _Destination; /* Read/Write */ +} OpenPIC_Timer; + + + /* + * Global Registers + */ + +typedef struct _OpenPIC_Global { + /* + * Feature Reporting Registers + */ + OpenPIC_Reg _Feature_Reporting0; /* Read Only */ + OpenPIC_Reg _Feature_Reporting1; /* Future Expansion */ + /* + * Global Configuration Registers + */ + OpenPIC_Reg _Global_Configuration0; /* Read/Write */ + OpenPIC_Reg _Global_Configuration1; /* Future Expansion */ + /* + * Vendor Specific Registers + */ + OpenPIC_Reg _Vendor_Specific[4]; + /* + * Vendor Identification Register + */ + OpenPIC_Reg _Vendor_Identification; /* Read Only */ + /* + * Processor Initialization Register + */ + OpenPIC_Reg _Processor_Initialization; /* Read/Write */ + /* + * IPI Vector/Priority Registers + */ + OpenPIC_Reg _IPI_Vector_Priority[OPENPIC_NUM_IPI]; /* Read/Write */ + /* + * Spurious Vector Register + */ + OpenPIC_Reg _Spurious_Vector; /* Read/Write */ + /* + * Global Timer Registers + */ + OpenPIC_Reg _Timer_Frequency; /* Read/Write */ + OpenPIC_Timer Timer[OPENPIC_NUM_TIMERS]; + char Pad1[0xee00]; +} OpenPIC_Global; + + + /* + * Interrupt Source Registers + */ + +typedef struct _OpenPIC_Source { + OpenPIC_Reg _Vector_Priority; /* Read/Write */ + OpenPIC_Reg _Destination; /* Read/Write */ +} OpenPIC_Source, *OpenPIC_SourcePtr; + + + /* + * OpenPIC Register Map + */ + +struct OpenPIC { + char Pad1[0x1000]; + /* + * Global Registers + */ + OpenPIC_Global Global; + /* + * Interrupt Source Configuration Registers + */ + OpenPIC_Source Source[OPENPIC_MAX_SOURCES]; + /* + * Per Processor Registers + */ + OpenPIC_Processor Processor[OPENPIC_MAX_PROCESSORS]; +}; + +extern volatile struct OpenPIC *OpenPIC; + + +/* + * Current Task Priority Register + */ + +#define OPENPIC_CURRENT_TASK_PRIORITY_MASK 0x0000000f + +/* + * Who Am I Register + */ + +#define OPENPIC_WHO_AM_I_ID_MASK 0x0000001f + +/* + * Feature Reporting Register 0 + */ + +#define OPENPIC_FEATURE_LAST_SOURCE_MASK 0x07ff0000 +#define OPENPIC_FEATURE_LAST_SOURCE_SHIFT 16 +#define OPENPIC_FEATURE_LAST_PROCESSOR_MASK 0x00001f00 +#define OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT 8 +#define OPENPIC_FEATURE_VERSION_MASK 0x000000ff + +/* + * Global Configuration Register 0 + */ + +#define OPENPIC_CONFIG_RESET 0x80000000 +#define OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE 0x20000000 +#define OPENPIC_CONFIG_BASE_MASK 0x000fffff + +/* + * Vendor Identification Register + */ + +#define OPENPIC_VENDOR_ID_STEPPING_MASK 0x00ff0000 +#define OPENPIC_VENDOR_ID_STEPPING_SHIFT 16 +#define OPENPIC_VENDOR_ID_DEVICE_ID_MASK 0x0000ff00 +#define OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT 8 +#define OPENPIC_VENDOR_ID_VENDOR_ID_MASK 0x000000ff + +/* + * Vector/Priority Registers + */ + +#define OPENPIC_MASK 0x80000000 +#define OPENPIC_ACTIVITY 0x40000000 /* Read Only */ +#define OPENPIC_PRIORITY_MASK 0x000f0000 +#define OPENPIC_PRIORITY_SHIFT 16 +#define OPENPIC_VECTOR_MASK 0x000007ff + + +/* + * Interrupt Source Registers + */ + +#define OPENPIC_POLARITY_POSITIVE 0x00800000 +#define OPENPIC_POLARITY_NEGATIVE 0x00000000 +#define OPENPIC_POLARITY_MASK 0x00800000 +#define OPENPIC_SENSE_LEVEL 0x00400000 +#define OPENPIC_SENSE_EDGE 0x00000000 +#define OPENPIC_SENSE_MASK 0x00400000 + + +/* + * Timer Registers + */ + +#define OPENPIC_COUNT_MASK 0x7fffffff +#define OPENPIC_TIMER_TOGGLE 0x80000000 +#define OPENPIC_TIMER_COUNT_INHIBIT 0x80000000 + + +/* + * Aliases to make life simpler + */ + +/* Per Processor Registers */ +#define IPI_Dispatch(i) _IPI_Dispatch[i].Reg +#define Current_Task_Priority _Current_Task_Priority.Reg +#define Interrupt_Acknowledge _Interrupt_Acknowledge.Reg +#define EOI _EOI.Reg + +/* Global Registers */ +#define Feature_Reporting0 _Feature_Reporting0.Reg +#define Feature_Reporting1 _Feature_Reporting1.Reg +#define Global_Configuration0 _Global_Configuration0.Reg +#define Global_Configuration1 _Global_Configuration1.Reg +#define Vendor_Specific(i) _Vendor_Specific[i].Reg +#define Vendor_Identification _Vendor_Identification.Reg +#define Processor_Initialization _Processor_Initialization.Reg +#define IPI_Vector_Priority(i) _IPI_Vector_Priority[i].Reg +#define Spurious_Vector _Spurious_Vector.Reg +#define Timer_Frequency _Timer_Frequency.Reg + +/* Timer Registers */ +#define Current_Count _Current_Count.Reg +#define Base_Count _Base_Count.Reg +#define Vector_Priority _Vector_Priority.Reg +#define Destination _Destination.Reg + +/* Interrupt Source Registers */ +#define Vector_Priority _Vector_Priority.Reg +#define Destination _Destination.Reg + +/* + * Local (static) OpenPIC Operations + */ + + +/* Global Operations */ +static void openpic_reset(void); +static void openpic_enable_8259_pass_through(void); +static void openpic_disable_8259_pass_through(void); +static u_int openpic_irq(void); +static void openpic_eoi(void); +static u_int openpic_get_priority(void); +static void openpic_set_priority(u_int pri); +static u_int openpic_get_spurious(void); +static void openpic_set_spurious(u_int vector); + +#ifdef CONFIG_SMP +/* Interprocessor Interrupts */ +static void openpic_initipi(u_int ipi, u_int pri, u_int vector); +static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs); +#endif + +/* Timer Interrupts */ +static void openpic_inittimer(u_int timer, u_int pri, u_int vector); +static void openpic_maptimer(u_int timer, u_int cpumask); + +/* Interrupt Sources */ +static void openpic_enable_irq(u_int irq); +static void openpic_disable_irq(u_int irq); +static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity, + int is_level); +static void openpic_mapirq(u_int irq, u_int cpumask); +static void openpic_set_sense(u_int irq, int sense); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_OPENPIC_H */ diff -Nru a/arch/ppc64/kernel/pSeries_hvCall.S b/arch/ppc64/kernel/pSeries_hvCall.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/pSeries_hvCall.S Tue Feb 19 18:09:01 2002 @@ -0,0 +1,70 @@ +/* + * arch/ppc64/kernel/pSeries_hvCall.S + * + * + * This file contains the generic code to perform a call to the + * pSeries LPAR hypervisor. + * NOTE: this file will go away when we move to inline this work. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include "ppc_asm.h" + +/* + * hcall interface to pSeries LPAR + */ +#define HSC .long 0x44000022 + +/* long plpar_hcall(unsigned long opcode, R3 + unsigned long arg1, R4 + unsigned long arg2, R5 + unsigned long arg3, R6 + unsigned long arg4, R7 + unsigned long *out1, R8 + unsigned long *out2, R9 + unsigned long *out3); R10 + */ + + .text +_GLOBAL(plpar_hcall) + mfcr r0 + std r0,-8(r1) + stdu r1,-32(r1) + + std r8,-8(r1) /* Save out ptrs. */ + std r9,-16(r1) + std r10,-24(r1) + + HSC /* invoke the hypervisor */ + + ld r10,-8(r1) /* Fetch r4-r7 ret args. */ + std r4,0(r10) + ld r10,-16(r1) + std r5,0(r10) + ld r10,-24(r1) + std r6,0(r10) + + ld r1,0(r1) + ld r0,-8(r1) + mtcrf 0xff,r0 + blr /* return r3 = status */ + + +/* Simple interface with no output values (other than status) */ +_GLOBAL(plpar_hcall_norets) + mfcr r0 + std r0,-8(r1) + HSC /* invoke the hypervisor */ + ld r0,-8(r1) + mtcrf 0xff,r0 + blr /* return r3 = status */ diff -Nru a/arch/ppc64/kernel/pSeries_lpar.c b/arch/ppc64/kernel/pSeries_lpar.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/pSeries_lpar.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,893 @@ +/* + * pSeries_lpar.c + * Copyright (C) 2001 Todd Inglett, IBM Corporation + * + * pSeries LPAR support. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Status return values */ +#define H_Success 0 +#define H_Busy 1 /* Hardware busy -- retry later */ +#define H_Hardware -1 /* Hardware error */ +#define H_Function -2 /* Function not supported */ +#define H_Privilege -3 /* Caller not privileged */ +#define H_Parameter -4 /* Parameter invalid, out-of-range or conflicting */ +#define H_Bad_Mode -5 /* Illegal msr value */ +#define H_PTEG_Full -6 /* PTEG is full */ +#define H_Not_Found -7 /* PTE was not found" */ +#define H_Reserved_DABR -8 /* DABR address is reserved by the hypervisor on this processor" */ + +/* Flags */ +#define H_LARGE_PAGE (1UL<<(63-16)) +#define H_EXACT (1UL<<(63-24)) /* Use exact PTE or return H_PTEG_FULL */ +#define H_R_XLATE (1UL<<(63-25)) /* include a valid logical page num in the pte if the valid bit is set */ +#define H_READ_4 (1UL<<(63-26)) /* Return 4 PTEs */ +#define H_AVPN (1UL<<(63-32)) /* An avpn is provided as a sanity test */ +#define H_ICACHE_INVALIDATE (1UL<<(63-40)) /* icbi, etc. (ignored for IO pages) */ +#define H_ICACHE_SYNCHRONIZE (1UL<<(63-41)) /* dcbst, icbi, etc (ignored for IO pages */ +#define H_ZERO_PAGE (1UL<<(63-48)) /* zero the page before mapping (ignored for IO pages) */ +#define H_COPY_PAGE (1UL<<(63-49)) +#define H_N (1UL<<(63-61)) +#define H_PP1 (1UL<<(63-62)) +#define H_PP2 (1UL<<(63-63)) + + + +/* pSeries hypervisor opcodes */ +#define H_REMOVE 0x04 +#define H_ENTER 0x08 +#define H_READ 0x0c +#define H_CLEAR_MOD 0x10 +#define H_CLEAR_REF 0x14 +#define H_PROTECT 0x18 +#define H_GET_TCE 0x1c +#define H_PUT_TCE 0x20 +#define H_SET_SPRG0 0x24 +#define H_SET_DABR 0x28 +#define H_PAGE_INIT 0x2c +#define H_SET_ASR 0x30 +#define H_ASR_ON 0x34 +#define H_ASR_OFF 0x38 +#define H_LOGICAL_CI_LOAD 0x3c +#define H_LOGICAL_CI_STORE 0x40 +#define H_LOGICAL_CACHE_LOAD 0x44 +#define H_LOGICAL_CACHE_STORE 0x48 +#define H_LOGICAL_ICBI 0x4c +#define H_LOGICAL_DCBF 0x50 +#define H_GET_TERM_CHAR 0x54 +#define H_PUT_TERM_CHAR 0x58 +#define H_REAL_TO_LOGICAL 0x5c +#define H_HYPERVISOR_DATA 0x60 +#define H_EOI 0x64 +#define H_CPPR 0x68 +#define H_IPI 0x6c +#define H_IPOLL 0x70 +#define H_XIRR 0x74 + +#define HSC ".long 0x44000022\n" +#define H_ENTER_r3 "li 3, 0x08\n" + +/* plpar_hcall() -- Generic call interface using above opcodes + * + * The actual call interface is a hypervisor call instruction with + * the opcode in R3 and input args in R4-R7. + * Status is returned in R3 with variable output values in R4-R11. + * Only H_PTE_READ with H_READ_4 uses R6-R11 so we ignore it for now + * and return only two out args which MUST ALWAYS BE PROVIDED. + */ +long plpar_hcall(unsigned long opcode, + unsigned long arg1, + unsigned long arg2, + unsigned long arg3, + unsigned long arg4, + unsigned long *out1, + unsigned long *out2, + unsigned long *out3); + +/* Same as plpar_hcall but for those opcodes that return no values + * other than status. Slightly more efficient. + */ +long plpar_hcall_norets(unsigned long opcode, ...); + + +long plpar_pte_enter(unsigned long flags, + unsigned long ptex, + unsigned long new_pteh, unsigned long new_ptel, + unsigned long *old_pteh_ret, unsigned long *old_ptel_ret) +{ + unsigned long dummy, ret; + ret = plpar_hcall(H_ENTER, flags, ptex, new_pteh, new_ptel, + old_pteh_ret, old_ptel_ret, &dummy); + return(ret); +} + +long plpar_pte_remove(unsigned long flags, + unsigned long ptex, + unsigned long avpn, + unsigned long *old_pteh_ret, unsigned long *old_ptel_ret) +{ + unsigned long dummy; + return plpar_hcall(H_REMOVE, flags, ptex, avpn, 0, + old_pteh_ret, old_ptel_ret, &dummy); +} + +long plpar_pte_read(unsigned long flags, + unsigned long ptex, + unsigned long *old_pteh_ret, unsigned long *old_ptel_ret) +{ + unsigned long dummy; + return plpar_hcall(H_READ, flags, ptex, 0, 0, + old_pteh_ret, old_ptel_ret, &dummy); +} + +long plpar_pte_protect(unsigned long flags, + unsigned long ptex, + unsigned long avpn) +{ + return plpar_hcall_norets(H_PROTECT, flags, ptex); +} + +long plpar_tce_get(unsigned long liobn, + unsigned long ioba, + unsigned long *tce_ret) +{ + unsigned long dummy; + return plpar_hcall(H_GET_TCE, liobn, ioba, 0, 0, + tce_ret, &dummy, &dummy); +} + + +long plpar_tce_put(unsigned long liobn, + unsigned long ioba, + unsigned long tceval) +{ + return plpar_hcall_norets(H_PUT_TCE, liobn, ioba, tceval); +} + +long plpar_get_term_char(unsigned long termno, + unsigned long *len_ret, + char *buf_ret) +{ + unsigned long *lbuf = (unsigned long *)buf_ret; /* ToDo: alignment? */ + return plpar_hcall(H_GET_TERM_CHAR, termno, 0, 0, 0, + len_ret, lbuf+0, lbuf+1); +} + +long plpar_put_term_char(unsigned long termno, + unsigned long len, + const char *buffer) +{ + unsigned long dummy; + unsigned long *lbuf = (unsigned long *)buffer; /* ToDo: alignment? */ + return plpar_hcall(H_PUT_TERM_CHAR, termno, len, + lbuf[0], lbuf[1], &dummy, &dummy, &dummy); +} + +long plpar_eoi(unsigned long xirr) +{ + return plpar_hcall_norets(H_EOI, xirr); +} + +long plpar_cppr(unsigned long cppr) +{ + return plpar_hcall_norets(H_CPPR, cppr); +} + +long plpar_ipi(unsigned long servernum, + unsigned long mfrr) +{ + return plpar_hcall_norets(H_IPI, servernum, mfrr); +} + +long plpar_xirr(unsigned long *xirr_ret) +{ + unsigned long dummy; + return plpar_hcall(H_XIRR, 0, 0, 0, 0, + xirr_ret, &dummy, &dummy); +} + +/* + * The following section contains code that ultimately should + * be put in the relavent file (htab.c, xics.c, etc). It has + * been put here for the time being in order to ease maintainence + * of the pSeries LPAR code until it can all be put into CVS. + */ +static void hpte_invalidate_pSeriesLP(unsigned long slot) +{ + HPTE old_pte; + unsigned long lpar_rc; + unsigned long flags = 0; + + lpar_rc = plpar_pte_remove(flags, + slot, + 0, + &old_pte.dw0.dword0, + &old_pte.dw1.dword1); + if (lpar_rc != H_Success) BUG(); +} + +/* NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and + * the low 3 bits of flags happen to line up. So no transform is needed. + * We can probably optimize here and assume the high bits of newpp are + * already zero. For now I am paranoid. + */ +static void hpte_updatepp_pSeriesLP(long slot, unsigned long newpp, unsigned long va) +{ + unsigned long lpar_rc; + unsigned long flags; + flags = newpp & 3; + lpar_rc = plpar_pte_protect( flags, + slot, + 0); + if (lpar_rc != H_Success) { + udbg_printf( " bad return code from pte protect rc = %lx \n", lpar_rc); + for (;;); + } +} + +static void hpte_updateboltedpp_pSeriesLP(unsigned long newpp, unsigned long ea) +{ + unsigned long lpar_rc; + unsigned long vsid,va,vpn,flags; + long slot; + + vsid = get_kernel_vsid( ea ); + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + vpn = va >> PAGE_SHIFT; + + slot = ppc_md.hpte_find( vpn ); + flags = newpp & 3; + lpar_rc = plpar_pte_protect( flags, + slot, + 0); + if (lpar_rc != H_Success) { + udbg_printf( " bad return code from pte bolted protect rc = %lx \n", lpar_rc); + for (;;); + } +} + + +static unsigned long hpte_getword0_pSeriesLP(unsigned long slot) +{ + unsigned long dword0; + unsigned long lpar_rc; + unsigned long dummy_word1; + unsigned long flags; + /* Read 1 pte at a time */ + /* Do not need RPN to logical page translation */ + /* No cross CEC PFT access */ + flags = 0; + + lpar_rc = plpar_pte_read(flags, + slot, + &dword0, &dummy_word1); + if (lpar_rc != H_Success) { + udbg_printf(" error on pte read in get_hpte0 rc = %lx \n", lpar_rc); + for (;;); + } + + return(dword0); +} + +static long hpte_selectslot_pSeriesLP(unsigned long vpn) +{ + unsigned long primary_hash; + unsigned long hpteg_slot; + unsigned i, k; + unsigned long flags; + HPTE pte_read; + unsigned long lpar_rc; + + /* Search the primary group for an available slot */ + primary_hash = hpt_hash(vpn, 0); + + hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + + /* Read 1 pte at a time */ + /* Do not need RPN to logical page translation */ + /* No cross CEC PFT access */ + flags = 0; + for (i=0; i> 11; + unsigned long arpn = physRpn_to_absRpn( prpn ); + + unsigned long lpar_rc; + unsigned long flags; + HPTE ret_hpte; + + /* Fill in the local HPTE with absolute rpn, avpn and flags */ + lhpte.dw1.d = 0; + lhpte.dw1.h.rpn = arpn; + lhpte.dw1.f.flags = hpteflags; + + lhpte.dw0.d = 0; + lhpte.dw0.h.avpn = avpn; + lhpte.dw0.h.h = hash; + lhpte.dw0.h.bolted = bolted; + lhpte.dw0.h.v = 1; + + /* Now fill in the actual HPTE */ + /* Set CEC cookie to 0 */ + /* Large page = 0 */ + /* Zero page = 0 */ + /* I-cache Invalidate = 0 */ + /* I-cache synchronize = 0 */ + /* Exact = 1 - only modify exact entry */ + flags = H_EXACT; + + if (hpteflags & (_PAGE_GUARDED|_PAGE_NO_CACHE)) + lhpte.dw1.f.flags &= ~_PAGE_COHERENT; +#if 1 + __asm__ __volatile__ ( + H_ENTER_r3 + "mr 4, %1\n" + "mr 5, %2\n" + "mr 6, %3\n" + "mr 7, %4\n" + HSC + "mr %0, 3\n" + : "=r" (lpar_rc) + : "r" (flags), "r" (slot), "r" (lhpte.dw0.d), "r" (lhpte.dw1.d) + : "r3", "r4", "r5", "r6", "r7", "cc"); +#else + lpar_rc = plpar_pte_enter(flags, + slot, + lhpte.dw0.d, + lhpte.dw1.d, + &ret_hpte.dw0.dword0, + &ret_hpte.dw1.dword1); +#endif + if (lpar_rc != H_Success) { + udbg_printf("error on pte enter lapar rc = %ld\n",lpar_rc); + udbg_printf("ent: s=%lx, dw0=%lx, dw1=%lx\n", slot, lhpte.dw0.d, lhpte.dw1.d); + /* xmon_backtrace("backtrace"); */ + for (;;); + } +} + +static long hpte_find_pSeriesLP(unsigned long vpn) +{ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + long slot; + unsigned long hash; + unsigned long i,j; + + hash = hpt_hash(vpn, 0); + for ( j=0; j<2; ++j ) { + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + for ( i=0; i> 11 ) ) && + ( hpte_dw0.h.v ) && + ( hpte_dw0.h.h == j ) ) { + /* HPTE matches */ + if ( j ) + slot = -slot; + return slot; + } + ++slot; + } + hash = ~hash; + } + return -1; +} + +/* + * Create a pte - LPAR . Used during initialization only. + * We assume the PTE will fit in the primary PTEG. + */ +void make_pte_LPAR(HPTE *htab, + unsigned long va, unsigned long pa, int mode, + unsigned long hash_mask, int large) +{ + HPTE local_hpte, ret_hpte; + unsigned long hash, slot, flags,lpar_rc, vpn; + + if (large) + vpn = va >> 24; + else + vpn = va >> 12; + + hash = hpt_hash(vpn, large); + + slot = ((hash & hash_mask)*HPTES_PER_GROUP); + + local_hpte.dw1.dword1 = pa | mode; + local_hpte.dw0.dword0 = 0; + local_hpte.dw0.dw0.avpn = va >> 23; + local_hpte.dw0.dw0.bolted = 1; /* bolted */ + local_hpte.dw0.dw0.v = 1; + + /* Set CEC cookie to 0 */ + /* Large page = 0 */ + /* Zero page = 0 */ + /* I-cache Invalidate = 0 */ + /* I-cache synchronize = 0 */ + /* Exact = 0 - modify any entry in group */ + flags = 0; +#if 1 + __asm__ __volatile__ ( + H_ENTER_r3 + "mr 4, %1\n" + "mr 5, %2\n" + "mr 6, %3\n" + "mr 7, %4\n" + HSC + "mr %0, 3\n" + : "=r" (lpar_rc) + : "r" (flags), "r" (slot), "r" (local_hpte.dw0.dword0), "r" (local_hpte.dw1.dword1) + : "r3", "r4", "r5", "r6", "r7", "cc"); +#else + lpar_rc = plpar_pte_enter(flags, + slot, + local_hpte.dw0.dword0, + local_hpte.dw1.dword1, + &ret_hpte.dw0.dword0, + &ret_hpte.dw1.dword1); +#endif +#if 0 /* NOTE: we explicitly do not check return status here because it is + * "normal" for early boot code to map io regions for which a partition + * has no access. However, we will die if we actually fault on these + * "permission denied" pages. + */ + if (lpar_rc != H_Success) { + /* pSeriesLP_init_early(); */ + udbg_printf("flags=%lx, slot=%lx, dword0=%lx, dword1=%lx, rc=%d\n", flags, slot, local_hpte.dw0.dword0,local_hpte.dw1.dword1, lpar_rc); + BUG(); + } +#endif +} + +static void tce_build_pSeriesLP(struct TceTable *tbl, long tcenum, + unsigned long uaddr, int direction ) +{ + u64 setTceRc; + union Tce tce; + + PPCDBG(PPCDBG_TCE, "build_tce: uaddr = 0x%lx\n", uaddr); + PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx, tbl = 0x%lx, index=%lx\n", + tcenum, tbl, tbl->index); + + tce.wholeTce = 0; + tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; + + tce.tceBits.readWrite = 1; + if ( direction != PCI_DMA_TODEVICE ) tce.tceBits.pciWrite = 1; + + setTceRc = plpar_tce_put((u64)tbl->index, + (u64)tcenum << 12, + tce.wholeTce ); + /* Make sure the update is visible to hardware. + * ToDo: sync after setting *all* the tce's. + */ + __asm__ __volatile__ ("sync" : : : "memory"); + + if(setTceRc) { + PPCDBG(PPCDBG_TCE, "setTce failed. rc=%ld\n", setTceRc); + PPCDBG(PPCDBG_TCE, "\tindex = 0x%lx\n", (u64)tbl->index); + PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx\n", (u64)tcenum); + PPCDBG(PPCDBG_TCE, "\ttce val = 0x%lx\n", tce.wholeTce ); + } +} + +static inline void free_tce_range(struct TceTable *tbl, + long tcenum, unsigned order ) +{ + unsigned long flags; + + /* Lock the tce allocation bitmap */ + spin_lock_irqsave( &(tbl->lock), flags ); + + /* Do the actual work */ + free_tce_range_nolock( tbl, tcenum, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + +} + +static void tce_free_pSeriesLP(struct TceTable *tbl, dma_addr_t dma_addr, + unsigned order, unsigned numPages) +{ + u64 setTceRc; + long tcenum, freeTce, maxTcenum; + unsigned i; + union Tce tce; + + maxTcenum = (tbl->size * (PAGE_SIZE / sizeof(union Tce))) - 1; + + tcenum = dma_addr >> PAGE_SHIFT; + + freeTce = tcenum - tbl->startOffset; + + if ( freeTce > maxTcenum ) { + printk("free_tces: tcenum > maxTcenum\n"); + printk("\ttcenum = 0x%lx\n", tcenum); + printk("\tfreeTce = 0x%lx\n", freeTce); + printk("\tmaxTcenum = 0x%lx\n", maxTcenum); + printk("\tTCE Table = 0x%lx\n", (u64)tbl); + printk("\tbus# = 0x%lx\n", + (u64)tbl->busNumber ); + printk("\tsize = 0x%lx\n", (u64)tbl->size); + printk("\tstartOff = 0x%lx\n", + (u64)tbl->startOffset ); + printk("\tindex = 0x%lx\n", (u64)tbl->index); + return; + } + + for (i=0; iindex, + (u64)tcenum << 12, /* note: not freeTce */ + tce.wholeTce ); + if ( setTceRc ) { + printk("tce_free: setTce failed\n"); + printk("\trc = %ld\n", setTceRc); + printk("\tindex = 0x%lx\n", + (u64)tbl->index); + printk("\ttcenum = 0x%lx\n", (u64)tcenum); + printk("\tfreeTce = 0x%lx\n", (u64)freeTce); + printk("\ttce val = 0x%lx\n", + tce.wholeTce ); + } + + ++tcenum; + } + + /* Make sure the update is visible to hardware. */ + __asm__ __volatile__ ("sync" : : : "memory"); + + free_tce_range( tbl, freeTce, order ); +} + +/* PowerPC Interrupts for lpar. */ +/* NOTE: this typedef is duplicated (for now) from xics.c! */ +typedef struct { + int (*xirr_info_get)(int cpu); + void (*xirr_info_set)(int cpu, int val); + void (*cppr_info)(int cpu, u8 val); + void (*qirr_info)(int cpu, u8 val); +} xics_ops; +static int pSeriesLP_xirr_info_get(int n_cpu) +{ + unsigned long lpar_rc; + unsigned long return_value; + + lpar_rc = plpar_xirr(&return_value); + if (lpar_rc != H_Success) { + panic(" bad return code xirr - rc = %lx \n", lpar_rc); + } + return ((int)(return_value)); +} + +static void pSeriesLP_xirr_info_set(int n_cpu, int value) +{ + unsigned long lpar_rc; + unsigned long val64 = value & 0xffffffff; + + lpar_rc = plpar_eoi(val64); + if (lpar_rc != H_Success) { + panic(" bad return code EOI - rc = %ld, value=%lx \n", lpar_rc, val64); + } +} + +static void pSeriesLP_cppr_info(int n_cpu, u8 value) +{ + unsigned long lpar_rc; + + lpar_rc = plpar_cppr(value); + if (lpar_rc != H_Success) { + panic(" bad return code cppr - rc = %lx \n", lpar_rc); + } +} + +static void pSeriesLP_qirr_info(int n_cpu , u8 value) +{ + unsigned long lpar_rc; + + lpar_rc = plpar_ipi(get_hard_smp_processor_id(n_cpu),value); + if (lpar_rc != H_Success) { + panic(" bad return code qirr -ipi - rc = %lx \n", lpar_rc); + } +} + +xics_ops pSeriesLP_ops = { + pSeriesLP_xirr_info_get, + pSeriesLP_xirr_info_set, + pSeriesLP_cppr_info, + pSeriesLP_qirr_info +}; +/* end TAI-LPAR */ + + +int vtermno; /* virtual terminal# for udbg */ + +static void udbg_putcLP(unsigned char c) +{ + char buf[16]; + unsigned long rc; + + if (c == '\n') + udbg_putcLP('\r'); + + buf[0] = c; + do { + rc = plpar_put_term_char(vtermno, 1, buf); + } while(rc == H_Busy); +} + +/* Buffered chars getc */ +static long inbuflen; +static long inbuf[2]; /* must be 2 longs */ + +static int udbg_getc_pollLP(void) +{ + /* The interface is tricky because it may return up to 16 chars. + * We save them statically for future calls to udbg_getc(). + */ + char ch, *buf = (char *)inbuf; + int i; + long rc; + if (inbuflen == 0) { + /* get some more chars. */ + inbuflen = 0; + rc = plpar_get_term_char(vtermno, &inbuflen, buf); + if (inbuflen == 0 && rc == H_Success) + return -1; + } + ch = buf[0]; + for (i = 1; i < inbuflen; i++) /* shuffle them down. */ + buf[i-1] = buf[i]; + inbuflen--; + return ch; +} + +static unsigned char udbg_getcLP(void) +{ + int ch; + for (;;) { + ch = udbg_getc_pollLP(); + if (ch == -1) { + /* This shouldn't be needed...but... */ + volatile unsigned long delay; + for (delay=0; delay < 2000000; delay++) + ; + } else { + return ch; + } + } +} + + +/* This is called early in setup.c. + * Use it to setup page table ppc_md stuff as well as udbg. + */ +void pSeriesLP_init_early(void) +{ + ppc_md.hpte_invalidate = hpte_invalidate_pSeriesLP; + ppc_md.hpte_updatepp = hpte_updatepp_pSeriesLP; + ppc_md.hpte_updateboltedpp = hpte_updateboltedpp_pSeriesLP; + ppc_md.hpte_getword0 = hpte_getword0_pSeriesLP; + ppc_md.hpte_selectslot = hpte_selectslot_pSeriesLP; + ppc_md.hpte_create_valid = hpte_create_valid_pSeriesLP; + ppc_md.hpte_find = hpte_find_pSeriesLP; + + ppc_md.tce_build = tce_build_pSeriesLP; + ppc_md.tce_free = tce_free_pSeriesLP; + +#ifdef CONFIG_SMP + smp_init_pSeries(); +#endif + pSeries_pcibios_init_early(); + + /* The keyboard is not useful in the LPAR environment. + * Leave all the interfaces NULL. + */ + + if (naca->serialPortAddr) { + void *comport = (void *)__ioremap(naca->serialPortAddr, 16, _PAGE_NO_CACHE); + udbg_init_uart(comport); + ppc_md.udbg_putc = udbg_putc; + ppc_md.udbg_getc = udbg_getc; + ppc_md.udbg_getc_poll = udbg_getc_poll; + } else { + /* lookup the first virtual terminal number in case we don't have a com port. + * Zero is probably correct in case someone calls udbg before the init. + * The property is a pair of numbers. The first is the starting termno (the + * one we use) and the second is the number of terminals. + */ + u32 *termno; + struct device_node *np = find_path_device("/rtas"); + if (np) { + termno = (u32 *)get_property(np, "ibm,termno", 0); + if (termno) + vtermno = termno[0]; + } + ppc_md.udbg_putc = udbg_putcLP; + ppc_md.udbg_getc = udbg_getcLP; + ppc_md.udbg_getc_poll = udbg_getc_pollLP; + } +} + +int hvc_get_chars(int index, char *buf, int count) +{ + unsigned long got; + + if (plpar_hcall(H_GET_TERM_CHAR, index, 0, 0, 0, &got, + (unsigned long *)buf, (unsigned long *)buf+1) == H_Success) { + /* + * Work around a HV bug where it gives us a null + * after every \r. -- paulus + */ + if (got > 0) { + int i; + for (i = 1; i < got; ++i) { + if (buf[i] == 0 && buf[i-1] == '\r') { + --got; + if (i < got) + memmove(&buf[i], &buf[i+1], + got - i); + } + } + } + return got; + } + return 0; +} + +int hvc_put_chars(int index, const char *buf, int count) +{ + unsigned long dummy; + unsigned long *lbuf = (unsigned long *) buf; + long ret; + + ret = plpar_hcall(H_PUT_TERM_CHAR, index, count, lbuf[0], lbuf[1], + &dummy, &dummy, &dummy); + if (ret == H_Success) + return count; + if (ret == H_Busy) + return 0; + return -1; +} + +int hvc_count(int *start_termno) +{ + u32 *termno; + struct device_node *dn; + + if ((dn = find_path_device("/rtas")) != NULL) { + if ((termno = (u32 *)get_property(dn, "ibm,termno", 0)) != NULL) { + if (start_termno) + *start_termno = termno[0]; + return termno[1]; + } + } + return 0; +} diff -Nru a/arch/ppc64/kernel/pSeries_pci.c b/arch/ppc64/kernel/pSeries_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/pSeries_pci.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,839 @@ +/* + * pSeries_pci.c + * + * pSeries_pcibios_init(void)opyright (C) 2001 Dave Engebretsen, IBM Corporation + * + * pSeries specific routines for PCI. + * + * Based on code from pci.c and chrp_pci.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PPC_EEH +#include +#endif + +#include "xics.h" +#include "open_pic.h" +#include "pci.h" + +extern struct device_node *allnodes; + +/******************************************************************* + * Forward declares of prototypes. + *******************************************************************/ +unsigned long find_and_init_phbs(void); +struct pci_controller* alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) ; +void pSeries_pcibios_fixup(void); +static int rtas_fake_read(struct device_node *dn, int offset, int nbytes, unsigned long *returnval); + +/* RTAS tokens */ +static int read_pci_config; +static int write_pci_config; +static int ibm_read_pci_config; +static int ibm_write_pci_config; + +static int s7a_workaround; + +/****************************************************************************** + * + * pSeries I/O Operations to access the PCI configuration space. + * + *****************************************************************************/ +#define RTAS_PCI_READ_OP(size, type, nbytes) \ +int __chrp \ +rtas_read_config_##size(struct device_node *dn, int offset, type val) { \ + unsigned long returnval = ~0L; \ + unsigned long buid; \ + unsigned int addr; \ + int ret; \ + \ + if (dn == NULL) { \ + ret = -2; \ + } else if (dn->status) { \ + ret = -1; \ + } else { \ + addr = (dn->busno << 16) | (dn->devfn << 8) | offset; \ + buid = dn->phb->buid; \ + if (buid) { \ + ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval, addr, buid >> 32, buid & 0xffffffff, nbytes); \ + if (ret < 0) \ + ret = rtas_fake_read(dn, offset, nbytes, &returnval); \ + } else { \ + ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, nbytes); \ + } \ + } \ + *val = returnval; \ + return ret; \ +} \ +int __chrp \ +rtas_pci_read_config_##size(struct pci_dev *dev, int offset, type val) { \ + struct device_node *dn = pci_device_to_OF_node(dev); \ + int ret = rtas_read_config_##size(dn, offset, val); \ + /* udbg_printf("read bus=%x, devfn=%x, ret=%d phb=%lx, dn=%lx\n", dev->bus->number, dev->devfn, ret, dn ? dn->phb : 0, dn); */ \ + return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; \ +} + +#define RTAS_PCI_WRITE_OP(size, type, nbytes) \ +int __chrp \ +rtas_write_config_##size(struct device_node *dn, int offset, type val) { \ + unsigned long buid; \ + unsigned int addr; \ + int ret; \ + \ + if (dn == NULL) { \ + ret = -2; \ + } else if (dn->status) { \ + ret = -1; \ + } else { \ + buid = dn->phb->buid; \ + addr = (dn->busno << 16) | (dn->devfn << 8) | offset; \ + if (buid) { \ + ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, nbytes, (ulong) val); \ + } else { \ + ret = rtas_call(write_pci_config, 3, 1, NULL, addr, nbytes, (ulong)val); \ + } \ + } \ + return ret; \ +} \ +int __chrp \ +rtas_pci_write_config_##size(struct pci_dev *dev, int offset, type val) { \ + return rtas_write_config_##size(pci_device_to_OF_node(dev), offset, val); \ +} + +RTAS_PCI_READ_OP(byte, u8 *, 1) +RTAS_PCI_READ_OP(word, u16 *, 2) +RTAS_PCI_READ_OP(dword, u32 *, 4) +RTAS_PCI_WRITE_OP(byte, u8, 1) +RTAS_PCI_WRITE_OP(word, u16, 2) +RTAS_PCI_WRITE_OP(dword, u32, 4) + +struct pci_ops rtas_pci_ops = { + rtas_pci_read_config_byte, + rtas_pci_read_config_word, + rtas_pci_read_config_dword, + rtas_pci_write_config_byte, + rtas_pci_write_config_word, + rtas_pci_write_config_dword, +}; + +/* + * Handle the case where rtas refuses to do a pci config read. + * This currently only happens with some PHBs in which case we totally fake + * out the values (and call it a speedwagaon -- something we could look up + * in the device tree). + */ +static int +rtas_fake_read(struct device_node *dn, int offset, int nbytes, unsigned long *returnval) +{ + char *device_type = (char *)get_property(dn, "device_type", 0); + u32 *class_code = (u32 *)get_property(dn, "class-code", 0); + + *returnval = ~0; /* float by default */ + + /* udbg_printf("rtas_fake_read dn=%p, offset=0x%02x, nbytes=%d, device_type=%s\n", dn, offset, nbytes, device_type ? device_type : ""); */ + if (device_type && strcmp(device_type, "pci") != 0) + return -3; /* Not a phb or bridge */ + + /* NOTE: class_code != NULL => EADS pci bridge. Else a PHB */ + if (nbytes == 1) { + if (offset == PCI_HEADER_TYPE) + *returnval = 0x80; /* multifunction */ + else if (offset == PCI_INTERRUPT_PIN || offset == PCI_INTERRUPT_LINE) + *returnval = 0; + } else if (nbytes == 2) { + if (offset == PCI_SUBSYSTEM_VENDOR_ID || offset == PCI_SUBSYSTEM_ID) + *returnval = 0; + else if (offset == PCI_COMMAND) + *returnval = PCI_COMMAND_PARITY|PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY; + } else if (nbytes == 4) { + if (offset == PCI_VENDOR_ID) + *returnval = 0x1014 | ((class_code ? 0x8b : 0x102) << 16); /* a phb */ + else if (offset == PCI_REVISION_ID) + *returnval = (class_code ? PCI_CLASS_BRIDGE_PCI : PCI_CLASS_BRIDGE_HOST) << 16; /* revs are zero */ + else if ((offset >= PCI_BASE_ADDRESS_0 && offset <= PCI_BASE_ADDRESS_5) || offset == PCI_ROM_ADDRESS) + *returnval = 0; + } + + /* printk("fake: %s nbytes=%d, offset=%lx ret=%lx\n", class_code ? "EADS" : "PHB", nbytes, offset, *returnval); */ + return 0; +} + +/****************************************************************** + * pci_read_irq_line + * + * Reads the Interrupt Pin to determine if interrupt is use by card. + * If the interrupt is used, then gets the interrupt line from the + * openfirmware and sets it in the pci_dev and pci_config line. + * + ******************************************************************/ +int +pci_read_irq_line(struct pci_dev *Pci_Dev) +{ + u8 InterruptPin; + struct device_node *Node; + + pci_read_config_byte(Pci_Dev, PCI_INTERRUPT_PIN, &InterruptPin); + if (InterruptPin == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Interrupt used by device.\n",Pci_Dev->slot_name); + return 0; + } + Node = pci_device_to_OF_node(Pci_Dev); + if ( Node == NULL) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s Device Node not found.\n",Pci_Dev->slot_name); + return -1; + } + if (Node->n_intrs == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Device OF interrupts defined.\n",Pci_Dev->slot_name); + return -1; + } + Pci_Dev->irq = Node->intrs[0].line; + + if (s7a_workaround) { + if (Pci_Dev->irq > 16) + Pci_Dev->irq -= 3; + } + + pci_write_config_byte(Pci_Dev, PCI_INTERRUPT_LINE, Pci_Dev->irq); + + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s pci_dev->irq = 0x%02X\n",Pci_Dev->slot_name,Pci_Dev->irq); + return 0; +} + +/****************************************************************** + * Find all PHBs in the system and initialize a set of data + * structures to represent them. + ******************************************************************/ +unsigned long __init +find_and_init_phbs(void) +{ + struct device_node *Pci_Node; + struct pci_controller *phb; + unsigned int root_addr_size_words = 0, this_addr_size_words = 0; + unsigned int this_addr_count = 0, range_stride; + unsigned int *ui_ptr = NULL, *ranges; + char *model; + struct pci_range64 range; + struct resource *res; + unsigned int memno, rlen, i, index; + unsigned int *opprop; + int has_isa = 0; + PPCDBG(PPCDBG_PHBINIT, "find_and_init_phbs\n"); + + read_pci_config = rtas_token("read-pci-config"); + write_pci_config = rtas_token("write-pci-config"); + ibm_read_pci_config = rtas_token("ibm,read-pci-config"); + ibm_write_pci_config = rtas_token("ibm,write-pci-config"); +#ifdef CONFIG_PPC_EEH + eeh_init(); +#endif + + if (naca->interrupt_controller == IC_OPEN_PIC) { + opprop = (unsigned int *)get_property(find_path_device("/"), + "platform-open-pic", NULL); + } + + /* Get the root address word size. */ + ui_ptr = (unsigned int *) get_property(find_path_device("/"), + "#size-cells", NULL); + if (ui_ptr) { + root_addr_size_words = *ui_ptr; + } else { + PPCDBG(PPCDBG_PHBINIT, "\tget #size-cells failed.\n"); + return(-1); + } + + if (find_type_devices("isa")) { + has_isa = 1; + PPCDBG(PPCDBG_PHBINIT, "\tFound an ISA bus.\n"); + } + + index = 0; + + /****************************************************************** + * Find all PHB devices and create an object for them. + ******************************************************************/ + for (Pci_Node = find_devices("pci"); Pci_Node != NULL; Pci_Node = Pci_Node->next) { + model = (char *) get_property(Pci_Node, "model", NULL); + if (model != NULL) { + phb = alloc_phb(Pci_Node, model, root_addr_size_words); + if (phb == NULL) return(-1); + } + else { + continue; + } + + /* Get this node's address word size. */ + ui_ptr = (unsigned int *) get_property(Pci_Node, "#size-cells", NULL); + if (ui_ptr) + this_addr_size_words = *ui_ptr; + else + this_addr_size_words = 1; + /* Get this node's address word count. */ + ui_ptr = (unsigned int *) get_property(Pci_Node, "#address-cells", NULL); + if (ui_ptr) + this_addr_count = *ui_ptr; + else + this_addr_count = 3; + + range_stride = this_addr_count + root_addr_size_words + this_addr_size_words; + + memno = 0; + phb->io_base_phys = 0; + + ranges = (unsigned int *) get_property(Pci_Node, "ranges", &rlen); + PPCDBG(PPCDBG_PHBINIT, "\trange_stride = 0x%lx, rlen = 0x%x\n", range_stride, rlen); + + for (i = 0; i < (rlen/sizeof(*ranges)); i+=range_stride) { + /* Put the PCI addr part of the current element into a + * '64' struct. + */ + range = *((struct pci_range64 *)(ranges + i)); + + /* If this is a '32' element, map into a 64 struct. */ + if ((range_stride * sizeof(int)) == + sizeof(struct pci_range32)) { + range.parent_addr = + (unsigned long)(*(ranges + i + 3)); + range.size = + (((unsigned long)(*(ranges + i + 4)))<<32) | + (*(ranges + i + 5)); + } else { + range.parent_addr = + (((unsigned long)(*(ranges + i + 3)))<<32) | + (*(ranges + i + 4)); + range.size = + (((unsigned long)(*(ranges + i + 5)))<<32) | + (*(ranges + i + 6)); + } + + PPCDBG(PPCDBG_PHBINIT, "\trange.parent_addr = 0x%lx\n", + range.parent_addr); + PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.hi = 0x%lx\n", + range.child_addr.a_hi); + PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.mid = 0x%lx\n", + range.child_addr.a_mid); + PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.lo = 0x%lx\n", + range.child_addr.a_lo); + PPCDBG(PPCDBG_PHBINIT, "\trange.size = 0x%lx\n", + range.size); + + res = NULL; + switch ((range.child_addr.a_hi >> 24) & 0x3) { + case 1: /* I/O space */ + PPCDBG(PPCDBG_PHBINIT, "\tIO Space\n"); + phb->io_base_phys = range.parent_addr; + res = &phb->io_resource; + res->name = Pci_Node->full_name; + res->flags = IORESOURCE_IO; +#ifdef CONFIG_PPC_EEH + if (!isa_io_base && has_isa) { + /* map a page for ISA ports. Not EEH protected. */ + isa_io_base = (unsigned long)__ioremap(phb->io_base_phys, PAGE_SIZE, _PAGE_NO_CACHE); + } + res->start = phb->io_base_virt = eeh_token(index, 0, 0, 0); + res->end = eeh_token(index, 0xff, 0xff, 0xffffffff); +#else + phb->io_base_virt = ioremap(phb->io_base_phys, range.size); + if (!pci_io_base) { + pci_io_base = (unsigned long)phb->io_base_virt; + if (has_isa) + isa_io_base = pci_io_base; + } + res->start = ((((unsigned long) range.child_addr.a_mid) << 32) | (range.child_addr.a_lo)); + res->start += (unsigned long)phb->io_base_virt; + res->end = res->start + range.size - 1; +#endif + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + phb->pci_io_offset = range.parent_addr - + ((((unsigned long) + range.child_addr.a_mid) << 32) | + (range.child_addr.a_lo)); + PPCDBG(PPCDBG_PHBINIT, "\tpci_io_offset = 0x%lx\n", + phb->pci_io_offset); + break; + case 2: /* mem space */ + PPCDBG(PPCDBG_PHBINIT, "\tMem Space\n"); + phb->pci_mem_offset = range.parent_addr - + ((((unsigned long) + range.child_addr.a_mid) << 32) | + (range.child_addr.a_lo)); + PPCDBG(PPCDBG_PHBINIT, "\tpci_mem_offset = 0x%lx\n", + phb->pci_mem_offset); + if (memno < sizeof(phb->mem_resources)/sizeof(phb->mem_resources[0])) { + res = &(phb->mem_resources[memno]); + ++memno; + res->name = Pci_Node->full_name; + res->flags = IORESOURCE_MEM; +#ifdef CONFIG_PPC_EEH + res->start = eeh_token(index, 0, 0, 0); + res->end = eeh_token(index, 0xff, 0xff, 0xffffffff); +#else + res->start = range.parent_addr; + res->end = range.parent_addr + range.size - 1; +#endif + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + } + break; + } + } + PPCDBG(PPCDBG_PHBINIT, "\tphb->io_base_phys = 0x%lx\n", + phb->io_base_phys); + PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_mem_offset = 0x%lx\n", + phb->pci_mem_offset); + + if (naca->interrupt_controller == IC_OPEN_PIC) { + int addr = root_addr_size_words * (index + 2) - 1; + openpic_setup_ISU(index, opprop[addr]); + } + index++; + } + pci_devs_phb_init(); + return 0; /*Success */ +} + +/****************************************************************** + * + * Allocate and partially initialize a structure to represent a PHB. + * + ******************************************************************/ +struct pci_controller * +alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) +{ + struct pci_controller *phb; + unsigned int *ui_ptr = NULL, len; + struct reg_property64 reg_struct; + int *bus_range; + int *buid_vals; + + PPCDBG(PPCDBG_PHBINIT, "alloc_phb: %s\n", dev->full_name); + PPCDBG(PPCDBG_PHBINIT, "\tdev = 0x%lx\n", dev); + PPCDBG(PPCDBG_PHBINIT, "\tmodel = 0x%lx\n", model); + PPCDBG(PPCDBG_PHBINIT, "\taddr_size_words = 0x%lx\n", addr_size_words); + + /* Found a PHB, now figure out where his registers are mapped. */ + ui_ptr = (unsigned int *) get_property(dev, "reg", &len); + if (ui_ptr == NULL) { + PPCDBG(PPCDBG_PHBINIT, "\tget reg failed.\n"); + return(NULL); + } + + if (addr_size_words == 1) { + reg_struct.address = ((struct reg_property32 *)ui_ptr)->address; + reg_struct.size = ((struct reg_property32 *)ui_ptr)->size; + } else { + reg_struct = *((struct reg_property64 *)ui_ptr); + } + + PPCDBG(PPCDBG_PHBINIT, "\treg_struct.address = 0x%lx\n", reg_struct.address); + PPCDBG(PPCDBG_PHBINIT, "\treg_struct.size = 0x%lx\n", reg_struct.size); + + /*************************************************************** + * Set chip specific data in the phb, including types & + * register pointers. + ***************************************************************/ + + /**************************************************************** + * Python + ***************************************************************/ + if (strstr(model, "Python")) { + PPCDBG(PPCDBG_PHBINIT, "\tCreate python\n"); + phb = pci_alloc_pci_controller("PHB PY",phb_type_python); + if (phb == NULL) return NULL; + + phb->cfg_addr = (volatile unsigned long *) + ioremap(reg_struct.address + 0xf8000, PAGE_SIZE); + PPCDBG(PPCDBG_PHBINIT, "\tcfg_addr_r = 0x%lx\n", + reg_struct.address + 0xf8000); + PPCDBG(PPCDBG_PHBINIT, "\tcfg_addr_v = 0x%lx\n", + phb->cfg_addr); + phb->cfg_data = (char*)(phb->cfg_addr + 0x02); + phb->phb_regs = (volatile unsigned long *) + ioremap(reg_struct.address + 0xf7000, PAGE_SIZE); + /* Python's register file is 1 MB in size. */ + phb->chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), + 0x100000); + + /* + * Firmware doesnt always clear this bit which is critical + * for good performance - Anton + */ + { + volatile u32 *tmp, i; + +#define PRG_CL_RESET_VALID 0x00010000 + + tmp = (u32 *)((unsigned long)phb->chip_regs + 0xf6030); + + if (*tmp & PRG_CL_RESET_VALID) { + printk("Python workaround: "); + *tmp &= ~PRG_CL_RESET_VALID; + /* + * We must read it back for changes to + * take effect + */ + i = *tmp; + printk("reg0: %x\n", i); + } + } + + /*************************************************************** + * Speedwagon + ***************************************************************/ + } else if (strstr(model, "Speedwagon")) { + PPCDBG(PPCDBG_PHBINIT, "\tCreate speedwagon\n"); + phb = pci_alloc_pci_controller("PHB SW",phb_type_speedwagon); + if (phb == NULL) return NULL; + + if (_machine == _MACH_pSeries) { + phb->cfg_addr = (volatile unsigned long *) + ioremap(reg_struct.address + 0x140, PAGE_SIZE); + phb->cfg_data = (char*)(phb->cfg_addr - 0x02); /* minus is correct */ + phb->phb_regs = (volatile unsigned long *) + ioremap(reg_struct.address, PAGE_SIZE); + /* Speedwagon's register file is 1 MB in size. */ + phb->chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), + 0x100000); + PPCDBG(PPCDBG_PHBINIT, "\tmapping chip_regs from 0x%lx -> 0x%lx\n", + reg_struct.address & 0xfffff, phb->chip_regs); + } else { + phb->cfg_addr = NULL; + phb->cfg_data = NULL; + phb->phb_regs = NULL; + phb->chip_regs = NULL; + } + + phb->local_number = ((reg_struct.address >> 12) & 0xf) - 0x8; + /*************************************************************** + * Trying to build a known just gets the code in trouble. + ***************************************************************/ + } else { + PPCDBG(PPCDBG_PHBINIT, "\tUnknown PHB Type!\n"); + printk("PCI: Unknown Phb Type!\n"); + return NULL; + } + + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + PPCDBG(PPCDBG_PHBINIT, "Can't get bus-range for %s\n", dev->full_name); + kfree(phb); + return(NULL); + } + + /*************************************************************** + * Finished with the initialization + ***************************************************************/ + phb->first_busno = bus_range[0]; + phb->last_busno = bus_range[1]; + + phb->arch_data = dev; + phb->ops = &rtas_pci_ops; + + buid_vals = (int *) get_property(dev, "ibm,fw-phb-id", &len); + if (buid_vals == NULL || len < 2 * sizeof(int)) { + phb->buid = 0; + } else { + /* Big bus system. These systems start new bus numbers under + * each phb. Until pci domains are standard, we depend on a + * patch which makes bus numbers ints and we shift the phb + * number into the upper bits. + */ + struct pci_bus check; + if (sizeof(check.number) == 1 || sizeof(check.primary) == 1 || + sizeof(check.secondary) == 1 || sizeof(check.subordinate) == 1) { + udbg_printf("pSeries_pci: this system has large bus numbers and the kernel was not\n" + "built with the patch that fixes include/linux/pci.h struct pci_bus so\n" + "number, primary, secondary and subordinate are ints.\n"); + panic("pSeries_pci: this system has large bus numbers and the kernel was not\n" + "built with the patch that fixes include/linux/pci.h struct pci_bus so\n" + "number, primary, secondary and subordinate are ints.\n"); + } + phb->buid = (((unsigned long)buid_vals[0]) << 32UL) | + (((unsigned long)buid_vals[1]) & 0xffffffff); + phb->first_busno += (phb->global_number << 8); + phb->last_busno += (phb->global_number << 8); + } + + /* Dump PHB information for Debug */ + PPCDBGCALL(PPCDBG_PHBINIT,dumpPci_Controller(phb) ); + + return phb; +} + +void +fixup_resources(struct pci_dev *dev) +{ + int i; + struct pci_controller *phb = PCI_GET_PHB_PTR(dev); +#ifdef CONFIG_PPC_EEH + struct device_node *dn; + unsigned long eeh_disable_bit; + + /* Add IBM loc code (slot) as a prefix to the device names for service */ + dn = pci_device_to_OF_node(dev); + if (dn) { + char *loc_code = get_property(dn, "ibm,loc-code", 0); + if (loc_code) { + int loc_len = strlen(loc_code); + if (loc_len < sizeof(dev->name)) { + memmove(dev->name+loc_len+1, dev->name, sizeof(dev->name)-loc_len-1); + memcpy(dev->name, loc_code, loc_len); + dev->name[loc_len] = ' '; + dev->name[sizeof(dev->name)-1] = '\0'; + } + } + } + + if (is_eeh_configured(dev)) { + eeh_disable_bit = 0; + printk("PCI: eeh configured for %s %s\n", dev->slot_name, dev->name); + if (eeh_set_option(dev, EEH_ENABLE) != 0) { + printk("PCI: failed to enable eeh for %s %s\n", dev->slot_name, dev->name); + eeh_disable_bit = EEH_TOKEN_DISABLED; + } + } else { + /* Assume device is by default EEH_DISABLE'd */ + printk("PCI: eeh NOT configured for %s %s\n", dev->slot_name, dev->name); + eeh_disable_bit = EEH_TOKEN_DISABLED; + } +#endif + + PPCDBG(PPCDBG_PHBINIT, "fixup_resources:\n"); + PPCDBG(PPCDBG_PHBINIT, "\tphb = 0x%016LX\n", phb); + PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_io_offset = 0x%016LX\n", phb->pci_io_offset); + PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_mem_offset = 0x%016LX\n", phb->pci_mem_offset); + + PPCDBG(PPCDBG_PHBINIT, "\tdev->name = %s\n", dev->name); + PPCDBG(PPCDBG_PHBINIT, "\tdev->vendor:device = 0x%04X : 0x%04X\n", dev->vendor, dev->device); + + if (phb == NULL) + return; + + for (i = 0; i < DEVICE_COUNT_RESOURCE; ++i) { + PPCDBG(PPCDBG_PHBINIT, "\tdevice %x.%x[%d] (flags %x) [%lx..%lx]\n", + dev->bus->number, dev->devfn, i, + dev->resource[i].flags, + dev->resource[i].start, + dev->resource[i].end); + + if ((dev->resource[i].start == 0) && (dev->resource[i].end == 0)) { + continue; + } + + if (dev->resource[i].flags & IORESOURCE_IO) { +#ifdef CONFIG_PPC_EEH + unsigned int busno = dev->bus ? dev->bus->number : 0; + unsigned long size = dev->resource[i].end - dev->resource[i].start; + unsigned long addr = (unsigned long)__ioremap(dev->resource[i].start + phb->io_base_phys, size, _PAGE_NO_CACHE); + if (!addr) + panic("fixup_resources: ioremap failed!\n"); + dev->resource[i].start = eeh_token(phb->global_number, busno, dev->devfn, addr) | eeh_disable_bit; + dev->resource[i].end = dev->resource[i].start + size; +#else + unsigned long offset = (unsigned long)phb->io_base_virt; + dev->resource[i].start += offset; + dev->resource[i].end += offset; +#endif + PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx .. %lx]\n", + dev->resource[i].start, dev->resource[i].end); + } else if (dev->resource[i].flags & IORESOURCE_MEM) { + if (dev->resource[i].start == 0) { + /* Bogus. Probably an unused bridge. */ + dev->resource[i].end = 0; + } else { +#ifdef CONFIG_PPC_EEH + unsigned int busno = dev->bus ? dev->bus->number : 0; + unsigned long size = dev->resource[i].end - dev->resource[i].start; + unsigned long addr = (unsigned long)__ioremap(dev->resource[i].start + phb->pci_mem_offset, size, _PAGE_NO_CACHE); + if (!addr) + panic("fixup_resources: ioremap failed!\n"); + dev->resource[i].start = eeh_token(phb->global_number, busno, dev->devfn, addr) | eeh_disable_bit; + dev->resource[i].end = dev->resource[i].start + size; +#else + dev->resource[i].start += phb->pci_mem_offset; + dev->resource[i].end += phb->pci_mem_offset; +#endif + } + PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx..%lx]\n", + dev->resource[i].start, dev->resource[i].end); + + } else { + continue; + } + + /* zap the 2nd function of the winbond chip */ + if (dev->resource[i].flags & IORESOURCE_IO + && dev->bus->number == 0 && dev->devfn == 0x81) + dev->resource[i].flags &= ~IORESOURCE_IO; + } +} + +static void check_s7a(void) +{ + struct device_node *root; + char *model; + + root = find_path_device("/"); + if (root) { + model = get_property(root, "model", NULL); + if (model && !strcmp(model, "IBM,7013-S7A")) + s7a_workaround = 1; + } +} + +void __init +pSeries_pcibios_fixup(void) +{ + struct pci_dev *dev; + + PPCDBG(PPCDBG_PHBINIT, "pSeries_pcibios_fixup: start\n"); + pci_assign_all_busses = 0; + + check_s7a(); + + pci_for_each_dev(dev) { + pci_read_irq_line(dev); + PPCDBGCALL(PPCDBG_PHBINIT, dumpPci_Dev(dev) ); + } + + if (naca->interrupt_controller == IC_PPC_XIC) { + xics_isa_init(); + } +} + +/*********************************************************************** + * pci_find_hose_for_OF_device + * + * This function finds the PHB that matching device_node in the + * OpenFirmware by scanning all the pci_controllers. + * + ***********************************************************************/ +struct pci_controller* +pci_find_hose_for_OF_device(struct device_node *node) +{ + while (node) { + struct pci_controller *hose; + for (hose=hose_head;hose;hose=hose->next) + if (hose->arch_data == node) + return hose; + node=node->parent; + } + return NULL; +} + +/*********************************************************************** + * find_floppy(void) + * + * Finds the default floppy device, if the system has one, and returns + * the pci_dev for the isa bridge for the floppy device. + * + * Note: This functions finds the first "fdc" device and then looks to + * the parent device which should be the isa bridge device. If there + * is more than one floppy on the system, it will find the first one + * and maybe that is okay. + ***********************************************************************/ +struct pci_dev* +find_floppy(void) +{ + struct device_node *floppy_dn; + struct pci_dev *floppy_dev = NULL; + int *reg; + + floppy_dn = find_type_devices("fdc"); + if (floppy_dn && floppy_dn->parent) { + if ((reg = (unsigned int *)get_property(floppy_dn->parent,"reg", 0)) != NULL) + floppy_dev = pci_find_slot((reg[0] & 0x00ff0000) >> 16, (reg[0] & 0x0000ff00) >> 8); + } + PPCDBG(PPCDBG_BUSWALK,"\tFloppy pci_dev\n"); + PPCDBGCALL(PPCDBG_BUSWALK, dumpPci_Dev(floppy_dev) ); + return floppy_dev; +} + +/*********************************************************************** + * ppc64_pcibios_init + * + * Chance to initialize and structures or variable before PCI Bus walk. + * + ***********************************************************************/ +void +pSeries_pcibios_init(void) +{ + PPCDBG(PPCDBG_PHBINIT, "\tppc64_pcibios_init Entry.\n"); + + if (get_property(find_path_device("/rtas"),"ibm,fw-phb-id",NULL) != NULL) { + PPCDBG(PPCDBG_PHBINIT, "\tFound: ibm,fw-phb-id\n"); + Pci_Large_Bus_System = 1; + } +} + +/* + * This is called very early before the page table is setup. + */ +void +pSeries_pcibios_init_early(void) +{ + ppc_md.pcibios_read_config_byte = rtas_read_config_byte; + ppc_md.pcibios_read_config_word = rtas_read_config_word; + ppc_md.pcibios_read_config_dword = rtas_read_config_dword; + ppc_md.pcibios_write_config_byte = rtas_write_config_byte; + ppc_md.pcibios_write_config_word = rtas_write_config_word; + ppc_md.pcibios_write_config_dword = rtas_write_config_dword; +} +/************************************************************************/ +/* Get a char* of the device physical location(U0.3-P1-I8) */ +/* See the Product Topology in the RS/6000 Architecture. */ +/************************************************************************/ +int device_Location(struct pci_dev *PciDev, char *BufPtr) +{ + struct device_node *DevNode = (struct device_node *)PciDev->sysdata; + return sprintf(BufPtr,"PCI: Bus%3d, Device%3d, Vendor %04X, Location %-12s", + PciDev->bus->number, + PCI_SLOT(PciDev->devfn), + PciDev->vendor, + (char*)get_property(DevNode,"ibm,loc-code",0)); +} +/************************************************************************/ +/* Set the slot reset line to the state passed in. */ +/* This is the platform specific for code for the pci_reset_device */ +/* function. */ +/************************************************************************/ +int pci_set_reset(struct pci_dev *PciDev, int state) +{ + return -1; +} diff -Nru a/arch/ppc64/kernel/pacaData.c b/arch/ppc64/kernel/pacaData.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/pacaData.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,118 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * 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. + */ + +#define __KERNEL__ 1 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* The Paca is an array with one entry per processor. Each contains an + * ItLpPaca, which contains the information shared between the + * hypervisor and Linux. Each also contains an ItLpRegSave area which + * is used by the hypervisor to save registers. + * On systems with hardware multi-threading, there are two threads + * per processor. The Paca array must contain an entry for each thread. + * The VPD Areas will give a max logical processors = 2 * max physical + * processors. The processor VPD array needs one entry per physical + * processor (not thread). + */ +#define PACAINITDATA(number,start,lpq,asrr,asrv) \ +{ \ + xLpPacaPtr: &xPaca[number].xLpPaca, \ + xLpRegSavePtr: &xPaca[number].xRegSav, \ + xPacaIndex: (number), /* Paca Index */ \ + default_decr: 0x00ff0000, /* Initial Decr */ \ + xStab_data: { \ + real: (asrr), /* Real pointer to segment table */ \ + virt: (asrv), /* Virt pointer to segment table */ \ + next_round_robin: 1 /* Round robin index */ \ + }, \ + lpQueuePtr: (lpq), /* &xItLpQueue, */ \ + xRtas: { \ + lock: SPIN_LOCK_UNLOCKED \ + }, \ + xProcStart: (start), /* Processor start */ \ + xLpPaca: { \ + xDesc: 0xd397d781, /* "LpPa" */ \ + xSize: sizeof(struct ItLpPaca), \ + xFPRegsInUse: 1, \ + xDynProcStatus: 2, \ + xDecrVal: 0x00ff0000, \ + xEndOfQuantum: 0xffffffffffffffff \ + }, \ + xRegSav: { \ + xDesc: 0xd397d9e2, /* "LpRS" */ \ + xSize: sizeof(struct ItLpRegSave) \ + }, \ + exception_sp: \ + (&xPaca[number].exception_stack[0]) - EXC_FRAME_SIZE, \ +} + +struct Paca xPaca[maxPacas] __page_aligned = { +#ifdef CONFIG_PPC_ISERIES + PACAINITDATA( 0, 1, &xItLpQueue, 0, 0xc000000000005000), +#else + PACAINITDATA( 0, 1, 0, 0x5000, 0xc000000000005000), +#endif + PACAINITDATA( 1, 0, 0, 0, 0), + PACAINITDATA( 2, 0, 0, 0, 0), + PACAINITDATA( 3, 0, 0, 0, 0), + PACAINITDATA( 4, 0, 0, 0, 0), + PACAINITDATA( 5, 0, 0, 0, 0), + PACAINITDATA( 6, 0, 0, 0, 0), + PACAINITDATA( 7, 0, 0, 0, 0), + PACAINITDATA( 8, 0, 0, 0, 0), + PACAINITDATA( 9, 0, 0, 0, 0), + PACAINITDATA(10, 0, 0, 0, 0), + PACAINITDATA(11, 0, 0, 0, 0), + PACAINITDATA(12, 0, 0, 0, 0), + PACAINITDATA(13, 0, 0, 0, 0), + PACAINITDATA(14, 0, 0, 0, 0), + PACAINITDATA(15, 0, 0, 0, 0), + PACAINITDATA(16, 0, 0, 0, 0), + PACAINITDATA(17, 0, 0, 0, 0), + PACAINITDATA(18, 0, 0, 0, 0), + PACAINITDATA(19, 0, 0, 0, 0), + PACAINITDATA(20, 0, 0, 0, 0), + PACAINITDATA(21, 0, 0, 0, 0), + PACAINITDATA(22, 0, 0, 0, 0), + PACAINITDATA(23, 0, 0, 0, 0), + PACAINITDATA(24, 0, 0, 0, 0), + PACAINITDATA(25, 0, 0, 0, 0), + PACAINITDATA(26, 0, 0, 0, 0), + PACAINITDATA(27, 0, 0, 0, 0), + PACAINITDATA(28, 0, 0, 0, 0), + PACAINITDATA(29, 0, 0, 0, 0), + PACAINITDATA(30, 0, 0, 0, 0), + PACAINITDATA(31, 0, 0, 0, 0), + PACAINITDATA(32, 0, 0, 0, 0), + PACAINITDATA(33, 0, 0, 0, 0), + PACAINITDATA(34, 0, 0, 0, 0), + PACAINITDATA(35, 0, 0, 0, 0), + PACAINITDATA(36, 0, 0, 0, 0), + PACAINITDATA(37, 0, 0, 0, 0), + PACAINITDATA(38, 0, 0, 0, 0), + PACAINITDATA(39, 0, 0, 0, 0), + PACAINITDATA(40, 0, 0, 0, 0), + PACAINITDATA(41, 0, 0, 0, 0), + PACAINITDATA(42, 0, 0, 0, 0), + PACAINITDATA(43, 0, 0, 0, 0), + PACAINITDATA(44, 0, 0, 0, 0), + PACAINITDATA(45, 0, 0, 0, 0), + PACAINITDATA(46, 0, 0, 0, 0), + PACAINITDATA(47, 0, 0, 0, 0) +}; diff -Nru a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/pci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1053 @@ +/* + * + * + * Port for PPC64 David Engebretsen, IBM Corp. + * Contains common pci routines for ppc64 platform, pSeries and iSeries brands. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PPC_EEH +#include +#endif + +#include "pci.h" + +/* pci_io_base -- the base address from which io bars are offsets. + * This is the lowest I/O base address (so bar values are always positive), + * and it *must* be the start of ISA space if an ISA bus exists because + * ISA drivers use hard coded offsets. If no ISA bus exists a dummy + * page is mapped and isa_io_limit prevents access to it. + */ +unsigned long isa_io_base = 0; /* NULL if no ISA bus */ +unsigned long pci_io_base = 0; +unsigned long isa_mem_base = 0; +unsigned long pci_dram_offset = 0; + +/****************************************************************** + * Forward declare of prototypes + ******************************************************************/ +static void pcibios_fixup_resources(struct pci_dev* dev); +static void fixup_broken_pcnet32(struct pci_dev* dev); +static void fixup_windbond_82c105(struct pci_dev* dev); +void fixup_resources(struct pci_dev* dev); + +struct pci_dev *find_floppy(void); +void iSeries_pcibios_init(void); +void pSeries_pcibios_init(void); + + +extern struct Naca *naca; + +int pci_assign_all_busses = 0; + +struct pci_controller* hose_head; +struct pci_controller** hose_tail = &hose_head; + +/******************************************************************* + * Counters and control flags. + *******************************************************************/ +long Pci_Io_Read_Count = 0; +long Pci_Io_Write_Count = 0; +long Pci_Cfg_Read_Count = 0; +long Pci_Cfg_Write_Count= 0; +long Pci_Error_Count = 0; + +int Pci_Retry_Max = 3; /* Only retry 3 times */ +int Pci_Error_Flag = 1; /* Set Retry Error on. */ +int Pci_Trace_Flag = 0; + +/****************************************************************** + * + ******************************************************************/ +int global_phb_number = 0; /* Global phb counter */ +int Pci_Large_Bus_System = 0; +int Pci_Set_IOA_Address = 0; +int Pci_Manage_Phb_Space = 0; +struct pci_controller *phbtab[PCI_MAX_PHB]; + +static int pci_bus_count; + +/* Floppy dev for ppc64_fd_dma_setup(). May be null if no floppy in the system. */ +struct pci_dev *ppc64_floppy_dev = NULL; + +struct pci_fixup pcibios_fixups[] = { + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, fixup_broken_pcnet32 }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, fixup_windbond_82c105 }, + { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources }, + { 0 } +}; + +static void fixup_broken_pcnet32(struct pci_dev* dev) +{ + if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) { + dev->vendor = PCI_VENDOR_ID_AMD; + pci_write_config_word(dev, PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); + pci_name_device(dev); + } +} + +static void fixup_windbond_82c105(struct pci_dev* dev) +{ + /* Assume the windbond 82c105 is the IDE controller on a + * p610. We should probably be more careful in case + * someone tries to plug in a similar adapter. + */ + unsigned int reg; + + printk("Using INTC for W82c105 IDE controller.\n"); + pci_read_config_dword(dev, 0x40, ®); + /* Enable LEGIRQ to use INTC instead of ISA interrupts */ + pci_write_config_dword(dev, 0x40, reg | (1<<11)); +} + + +void pcibios_fixup_pbus_ranges(struct pci_bus *pbus, + struct pbus_set_ranges_data *pranges) +{ +} + + +void +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + u32 new, check; + int reg; + struct pci_controller* hose = PCI_GET_PHB_PTR(dev); + + new = res->start; + if (hose && res->flags & IORESOURCE_MEM) + new -= hose->pci_mem_offset; + new |= (res->flags & PCI_REGION_FLAG_MASK); + if (resource < 6) { + reg = PCI_BASE_ADDRESS_0 + 4*resource; + } else if (resource == PCI_ROM_RESOURCE) { + res->flags |= PCI_ROM_ADDRESS_ENABLE; + reg = dev->rom_base_reg; + } else { + /* Somebody might have asked allocation of a non-standard resource */ + return; + } + + pci_write_config_dword(dev, reg, new); + pci_read_config_dword(dev, reg, &check); + if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { + printk(KERN_ERR "PCI: Error while updating region " + "%s/%d (%08x != %08x)\n", dev->slot_name, resource, + new, check); + } +} + +static void +pcibios_fixup_resources(struct pci_dev* dev) +{ + fixup_resources(dev); +} + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void +pcibios_align_resource(void *data, struct resource *res, unsigned long size) +{ + struct pci_dev *dev = data; + + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (size > 0x100) { + printk(KERN_ERR "PCI: Can not align I/O Region %s %s because size %ld is too large.\n", + dev->slot_name, res->name, size); + } + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ + +static void __init +pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct list_head *ln; + struct pci_bus *bus; + int i; + struct resource *res, *pr; + + /* Depth-First Search on bus tree */ + for (ln=bus_list->next; ln != bus_list; ln=ln->next) { + bus = pci_bus_b(ln); + for (i = 0; i < 4; ++i) { + if ((res = bus->resource[i]) == NULL || !res->flags) + continue; + if (bus->parent == NULL) + pr = (res->flags & IORESOURCE_IO)? + &ioport_resource: &iomem_resource; + else + pr = pci_find_parent_resource(bus->self, res); + + if (pr == res) + continue; /* transparent bus or undefined */ + if (pr && request_resource(pr, res) == 0) + continue; + printk(KERN_ERR "PCI: Cannot allocate resource region " + "%d of PCI bridge %x\n", i, bus->number); + printk(KERN_ERR "PCI: resource is %lx..%lx (%lx), parent %p\n", + res->start, res->end, res->flags, pr); + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +static void __init +pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev; + int idx, disabled; + u16 command; + struct resource *r, *pr; + + pci_for_each_dev(dev) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for(idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->start) /* Address not assigned at all */ + continue; + + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + PPCDBG(PPCDBG_PHBINIT, + "PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n", + r->start, r->end, r->flags, disabled, pass); + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + PPCDBG(PPCDBG_PHBINIT, + "PCI: Cannot allocate resource region %d of device %s, pr = 0x%lx\n", idx, dev->slot_name, pr); + if(pr) { + PPCDBG(PPCDBG_PHBINIT, + "PCI: Cannot allocate resource 0x%lx\n", request_resource(pr,r)); + } + /* We'll assign a new address later */ + r->end -= r->start; + r->start = 0; + } + } + } + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & PCI_ROM_ADDRESS_ENABLE) { + /* Turn the ROM off, leave the resource region, but keep it unregistered. */ + u32 reg; + r->flags &= ~PCI_ROM_ADDRESS_ENABLE; + pci_read_config_dword(dev, dev->rom_base_reg, ®); + pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } + } +} + +static void __init +pcibios_assign_resources(void) +{ + struct pci_dev *dev; + int idx; + struct resource *r; + + pci_for_each_dev(dev) { + int class = dev->class >> 8; + + /* Don't touch classless devices and host bridges */ + if (!class || class == PCI_CLASS_BRIDGE_HOST) + continue; + + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + + /* + * Don't touch IDE controllers and I/O ports of video cards! + */ + if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) || + (class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO))) + continue; + + /* + * We shall assign a new address to this resource, either because + * the BIOS forgot to do so or because we have decided the old + * address was unusable for some reason. + */ + if (!r->start && r->end && ppc_md.pcibios_enable_device_hook && + !ppc_md.pcibios_enable_device_hook(dev, 1)) + pci_assign_resource(dev, idx); + } + + if (0) { /* don't assign ROMs */ + r = &dev->resource[PCI_ROM_RESOURCE]; + r->end -= r->start; + r->start = 0; + if (r->end) + pci_assign_resource(dev, PCI_ROM_RESOURCE); + } + } +} + + +int +pcibios_enable_resources(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +/* + * Allocate pci_controller(phb) initialized common variables. + */ +struct pci_controller * __init +pci_alloc_pci_controller(char *model, enum phb_types controller_type) +{ + struct pci_controller *hose; + PPCDBG(PPCDBG_PHBINIT, "PCI: Allocate pci_controller for %s\n",model); + hose = (struct pci_controller *)alloc_bootmem(sizeof(struct pci_controller)); + if(hose == NULL) { + printk(KERN_ERR "PCI: Allocate pci_controller failed.\n"); + return NULL; + } + memset(hose, 0, sizeof(struct pci_controller)); + if(strlen(model) < 8) strcpy(hose->what,model); + else memcpy(hose->what,model,7); + hose->type = controller_type; + hose->global_number = global_phb_number; + phbtab[global_phb_number++] = hose; + + *hose_tail = hose; + hose_tail = &hose->next; + return hose; +} + +/* + * This fixup is arch independent and probably should go somewhere else. + */ +void __init +pcibios_generic_fixup(void) +{ + struct pci_dev *dev; + + /* Fix miss-identified vendor AMD pcnet32 adapters. */ + dev = NULL; + while ((dev = pci_find_device(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_AMD_LANCE, dev)) != NULL && + dev->class == (PCI_CLASS_NETWORK_ETHERNET << 8)) + dev->vendor = PCI_VENDOR_ID_AMD; +} + + + +/*********************************************************************** + * + * + * + ***********************************************************************/ +void __init +pcibios_init(void) +{ + struct pci_controller *hose; + struct pci_bus *bus; + int next_busno; + +#ifndef CONFIG_PPC_ISERIES + pSeries_pcibios_init(); +#else + iSeries_pcibios_init(); +#endif + + printk("PCI: Probing PCI hardware\n"); + PPCDBG(PPCDBG_BUSWALK,"PCI: Probing PCI hardware\n"); + + + /* Scan all of the recorded PCI controllers. */ + for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { + hose->last_busno = 0xff; + bus = pci_scan_bus(hose->first_busno, hose->ops, hose->arch_data); + hose->bus = bus; + hose->last_busno = bus->subordinate; + if (pci_assign_all_busses || next_busno <= hose->last_busno) + next_busno = hose->last_busno+1; + } + pci_bus_count = next_busno; + + /* Call machine dependant fixup */ + if (ppc_md.pcibios_fixup) { + ppc_md.pcibios_fixup(); + } + + /* Generic fixups */ + pcibios_generic_fixup(); + + /* Allocate and assign resources */ + pcibios_allocate_bus_resources(&pci_root_buses); + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); + pcibios_assign_resources(); + +#ifndef CONFIG_PPC_ISERIES + pci_fix_bus_sysdata(); + + create_tce_tables(); + PPCDBG(PPCDBG_BUSWALK,"pSeries create_tce_tables()\n"); +#endif + ppc64_floppy_dev = find_floppy(); + + printk("PCI: Probing PCI hardware done\n"); + PPCDBG(PPCDBG_BUSWALK,"PCI: Probing PCI hardware done.\n"); + +} + +int __init +pcibios_assign_all_busses(void) +{ + return pci_assign_all_busses; +} + +unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, + unsigned long start, unsigned long size) +{ + return start; +} + +void __init pcibios_fixup_bus(struct pci_bus *bus) +{ + struct pci_controller *phb = PCI_GET_PHB_PTR(bus); + struct resource *res; + unsigned long io_offset; + int i; + +#ifndef CONFIG_PPC_ISERIES + if (bus->parent == NULL) { + /* This is a host bridge - fill in its resources */ + phb->bus = bus; + bus->resource[0] = res = &phb->io_resource; + if (!res->flags) + BUG(); /* No I/O resource for this PHB? */ + + for (i = 0; i < 3; ++i) { + res = &phb->mem_resources[i]; + if (!res->flags) { + if (i == 0) + BUG(); /* No memory resource for this PHB? */ + } + bus->resource[i+1] = res; + } + } else { + /* This is a subordinate bridge */ + pci_read_bridge_bases(bus); + + for (i = 0; i < 4; ++i) { + if ((res = bus->resource[i]) == NULL) + continue; + if (!res->flags) + continue; + if (res == pci_find_parent_resource(bus->self, res)) { + /* Transparent resource -- don't try to "fix" it. */ + continue; + } +#ifdef CONFIG_PPC_EEH + if (res->flags & (IORESOURCE_IO|IORESOURCE_MEM)) { + res->start = eeh_token(phb->global_number, bus->number, 0, 0); + res->end = eeh_token(phb->global_number, bus->number, 0xff, 0xffffffff); + } +#else + if (res->flags & IORESOURCE_IO) { + res->start += (unsigned long)phb->io_base_virt; + res->end += (unsigned long)phb->io_base_virt; + } else if (phb->pci_mem_offset + && (res->flags & IORESOURCE_MEM)) { + if (res->start < phb->pci_mem_offset) { + res->start += phb->pci_mem_offset; + res->end += phb->pci_mem_offset; + } + } +#endif + } + } +#endif + if ( ppc_md.pcibios_fixup_bus ) + ppc_md.pcibios_fixup_bus(bus); +} + +char __init *pcibios_setup(char *str) +{ + return str; +} + +int pcibios_enable_device(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + PPCDBG(PPCDBG_BUSWALK,"PCI: "__FUNCTION__" for device %s \n",dev->slot_name); + if (ppc_md.pcibios_enable_device_hook) + if (ppc_md.pcibios_enable_device_hook(dev, 0)) + return -EINVAL; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", + dev->slot_name, old_cmd, cmd); + PPCDBG(PPCDBG_BUSWALK,"PCI: Enabling device %s \n",dev->slot_name); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +struct pci_controller* +pci_bus_to_hose(int bus) +{ + struct pci_controller* hose = hose_head; + + for (; hose; hose = hose->next) + if (bus >= hose->first_busno && bus <= hose->last_busno) + return hose; + return NULL; +} + +void* +pci_bus_io_base(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return NULL; + return hose->io_base_virt; +} + +unsigned long +pci_bus_io_base_phys(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return 0; + return hose->io_base_phys; +} + +unsigned long +pci_bus_mem_base_phys(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return 0; + return hose->pci_mem_offset; +} + +/* + * Return the index of the PCI controller for device pdev. + */ +int pci_controller_num(struct pci_dev *dev) +{ + struct pci_controller *hose = PCI_GET_PHB_PTR(dev); + + return hose->global_number; +} + +/* + * Platform support for /proc/bus/pci/X/Y mmap()s, + * modelled on the sparc64 implementation by Dave Miller. + * -- paulus. + */ + +/* + * Adjust vm_pgoff of VMA such that it is the physical page offset + * corresponding to the 32-bit pci bus offset for DEV requested by the user. + * + * Basically, the user finds the base address for his device which he wishes + * to mmap. They read the 32-bit value from the config space base register, + * add whatever PAGE_SIZE multiple offset they wish, and feed this into the + * offset parameter of mmap on /proc/bus/pci/XXX for that device. + * + * Returns negative error code on failure, zero on success. + */ +static __inline__ int +__pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state) +{ + struct pci_controller *hose = PCI_GET_PHB_PTR(dev); + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long io_offset = 0; + int i, res_bit; + + if (hose == 0) + return -EINVAL; /* should never happen */ + + /* If memory, add on the PCI bridge address offset */ + if (mmap_state == pci_mmap_mem) { + offset += hose->pci_mem_offset; + res_bit = IORESOURCE_MEM; + } else { + io_offset = (unsigned long)hose->io_base_virt; + offset += io_offset; + res_bit = IORESOURCE_IO; + } + + /* + * Check that the offset requested corresponds to one of the + * resources of the device. + */ + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + struct resource *rp = &dev->resource[i]; + int flags = rp->flags; + + /* treat ROM as memory (should be already) */ + if (i == PCI_ROM_RESOURCE) + flags |= IORESOURCE_MEM; + + /* Active and same type? */ + if ((flags & res_bit) == 0) + continue; + + /* In the range of this resource? */ + if (offset < (rp->start & PAGE_MASK) || offset > rp->end) + continue; + + /* found it! construct the final physical address */ + if (mmap_state == pci_mmap_io) + offset += hose->io_base_phys - io_offset; + + vma->vm_pgoff = offset >> PAGE_SHIFT; + return 0; + } + + return -EINVAL; +} + +/* + * Set vm_flags of VMA, as appropriate for this architecture, for a pci device + * mapping. + */ +static __inline__ void +__pci_mmap_set_flags(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state) +{ + vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO; +} + +/* + * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci + * device mapping. + */ +static __inline__ void +__pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + long prot = pgprot_val(vma->vm_page_prot); + + /* XXX would be nice to have a way to ask for write-through */ + prot |= _PAGE_NO_CACHE; + if (!write_combine) + prot |= _PAGE_GUARDED; + vma->vm_page_prot = __pgprot(prot); +} + +/* + * Perform the actual remap of the pages for a PCI device mapping, as + * appropriate for this architecture. The region in the process to map + * is described by vm_start and vm_end members of VMA, the base physical + * address is found in vm_pgoff. + * The pci device structure is provided so that architectures may make mapping + * decisions on a per-device or per-bus basis. + * + * Returns a negative error code on failure, zero on success. + */ +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, + int write_combine) +{ + int ret; + + ret = __pci_mmap_make_offset(dev, vma, mmap_state); + if (ret < 0) + return ret; + + __pci_mmap_set_flags(dev, vma, mmap_state); + __pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine); + + ret = remap_page_range(vma, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + + return ret; +} + +/* Provide information on locations of various I/O regions in physical + * memory. Do this on a per-card basis so that we choose the right + * root bridge. + * Note that the returned IO or memory base is a physical address + */ + +long +sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn) +{ + struct pci_controller* hose = pci_bus_to_hose(bus); + long result = -EOPNOTSUPP; + + if (!hose) + return -ENODEV; + + switch (which) { + case IOBASE_BRIDGE_NUMBER: + return (long)hose->first_busno; + case IOBASE_MEMORY: + return (long)hose->pci_mem_offset; + case IOBASE_IO: + return (long)hose->io_base_phys; + case IOBASE_ISA_IO: + return (long)isa_io_base; + case IOBASE_ISA_MEM: + return (long)isa_mem_base; + } + + return result; +} +/************************************************************************/ +/* Formats the device information and location for service. */ +/* - Pass in pci_dev* pointer to the device. */ +/* - Pass in buffer to place the data. Danger here is the buffer must */ +/* be as big as the client says it is. Should be at least 128 bytes.*/ +/* Return will the length of the string data put in the buffer. */ +/* The brand specific method device_Location is called. */ +/* Format: */ +/* PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet */ +/* PCI: Bus 0, Device 26, Vendor 0x12AE Location U0.3-P1-I8 Ethernet */ +/* For pSeries, see the Product Topology in the RS/6000 Architecture. */ +/* For iSeries, see the Service Manuals. */ +/************************************************************************/ +int format_device_location(struct pci_dev* PciDev,char* BufPtr, int BufferSize) +{ + struct device_node* DevNode = (struct device_node*)PciDev->sysdata; + int LineLen = 0; + if (DevNode != NULL && BufferSize >= 128) { + LineLen += device_Location(PciDev,BufPtr+LineLen); + LineLen += sprintf(BufPtr+LineLen," %12s",pci_class_name(PciDev->class >> 8) ); + } + return LineLen; +} +/************************************************************************ + * Saves the config registers for a device. * + ************************************************************************ + * Note: This does byte reads so the data may appear byte swapped, * + * The data returned in the pci_config_reg_save_area structure can be * + * used to the restore of the data. If the save failed, the data * + * will not be restore. Yes I know, you are most likey toast. * + ************************************************************************/ +int pci_save_config_regs(struct pci_dev* PciDev,struct pci_config_reg_save_area* SaveArea) +{ + memset(SaveArea,0x00,sizeof(struct pci_config_reg_save_area) ); + SaveArea->PciDev = PciDev; + SaveArea->RCode = 0; + SaveArea->Register = 0; + /****************************************************************** + * Save All the Regs, NOTE: restore skips the first 16 bytes. * + ******************************************************************/ + while (SaveArea->Register < REG_SAVE_SIZE && SaveArea->RCode == 0) { + SaveArea->RCode = pci_read_config_byte(PciDev, SaveArea->Register, &SaveArea->Regs[SaveArea->Register]); + ++SaveArea->Register; + } + if (SaveArea->RCode != 0) { /* Ouch */ + SaveArea->Flags = 0x80; + printk("PCI: pci_restore_save_regs failed! %p\n 0x%04X",PciDev,SaveArea->RCode); + } + else { + SaveArea->Flags = 0x01; + } + return SaveArea->RCode; +} + +/************************************************************************ + * Restores the registers saved via the save function. See the save * + * function for details. * + ************************************************************************/ +int pci_restore_config_regs(struct pci_dev* PciDev,struct pci_config_reg_save_area* SaveArea) +{ + if (SaveArea->PciDev != PciDev || SaveArea->Flags == 0x80 || SaveArea->RCode != 0) { + printk("PCI: pci_restore_config_regs failed! %p\n",PciDev); + return -1; + } + /****************************************************************** + * Don't touch the Cmd or BIST regs, user must restore those. * + * Restore PCI_VENDOR_ID & PCI_DEVICE_ID * + * Restore PCI_CACHE_LINE_SIZE & PCI_LATENCY_TIMER * + * Restore Saved Regs from 0x10 to 0x3F * + ******************************************************************/ + SaveArea->Register = 0; + while(SaveArea->Register < REG_SAVE_SIZE && SaveArea->RCode == 0) { + SaveArea->RCode = pci_write_config_byte(PciDev,SaveArea->Register,SaveArea->Regs[SaveArea->Register]); + ++SaveArea->Register; + if ( SaveArea->Register == PCI_COMMAND) SaveArea->Register = PCI_CACHE_LINE_SIZE; + else if (SaveArea->Register == PCI_HEADER_TYPE) SaveArea->Register = PCI_BASE_ADDRESS_0; + } + if (SaveArea->RCode != 0) { + printk("PCI: pci_restore_config_regs failed! %p\n 0x%04X",PciDev,SaveArea->RCode); + } + return SaveArea->RCode; +} + +/************************************************************************/ +/* Interface to toggle the reset line */ +/* Time is in .1 seconds, need for seconds. */ +/************************************************************************/ +int pci_reset_device(struct pci_dev* PciDev, int AssertTime, int DelayTime) +{ + unsigned long AssertDelay, WaitDelay; + int RtnCode; + /******************************************************************** + * Set defaults, Assert is .5 second, Wait is 3 seconds. + ********************************************************************/ + if (AssertTime == 0) AssertDelay = ( 5 * HZ)/10; + else AssertDelay = (AssertTime*HZ)/10; + if (WaitDelay == 0) WaitDelay = (30 * HZ)/10; + else WaitDelay = (DelayTime* HZ)/10; + + /******************************************************************** + * Assert reset, wait, de-assert reset, wait for IOA to reset. + * - Don't waste the CPU time on jiffies. + ********************************************************************/ + RtnCode = pci_set_reset(PciDev,1); + if (RtnCode == 0) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(AssertDelay); /* Sleep for the time */ + RtnCode = pci_set_reset(PciDev,0); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(WaitDelay); + } + if (RtnCode != 0) { + printk("PCI: Bus%3d, Device%3d, Reset Failed:0x%04X\n",PciDev->bus->number,PCI_SLOT(PciDev->devfn),RtnCode ); + } + return RtnCode; +} + +/***************************************************** + * Dump Resource information + *****************************************************/ +void dumpResources(struct resource* Resource) +{ + if(Resource != NULL) { + int Flags = 0x00000F00 & Resource->flags; + if(Resource->start == 0 && Resource->end == 0) return; + else if(Resource->start == Resource->end ) return; + else { + if (Flags == IORESOURCE_IO) udbg_printf("IO.:"); + else if(Flags == IORESOURCE_MEM) udbg_printf("MEM:"); + else if(Flags == IORESOURCE_IRQ) udbg_printf("IRQ:"); + else udbg_printf("0x%02X:",Resource->flags); + + } + udbg_printf("0x%016LX / 0x%016LX (0x%08X)\n", + Resource->start, Resource->end, Resource->end - Resource->start); + } +} + +int resourceSize(struct resource* Resource) +{ + if(Resource->start == 0 && Resource->end == 0) return 0; + else if(Resource->start == Resource->end ) return 0; + else return (Resource->end-1)-Resource->start; +} + + +/***************************************************** + * Dump PHB information for Debug + *****************************************************/ +void dumpPci_Controller(struct pci_controller* phb) +{ + udbg_printf("\tpci_controller= 0x%016LX\n", phb); + if (phb != NULL) { + udbg_printf("\twhat & type = %s 0x%02X\n ",phb->what,phb->type); + udbg_printf("\tbus = "); + if (phb->bus != NULL) udbg_printf("0x%02X\n", phb->bus->number); + else udbg_printf("\n"); + udbg_printf("\tarch_data = 0x%016LX\n", phb->arch_data); + udbg_printf("\tfirst_busno = 0x%02X\n", phb->first_busno); + udbg_printf("\tlast_busno = 0x%02X\n", phb->last_busno); + udbg_printf("\tio_base_virt* = 0x%016LX\n", phb->io_base_virt); + udbg_printf("\tio_base_phys = 0x%016LX\n", phb->io_base_phys); + udbg_printf("\tpci_mem_offset= 0x%016LX\n", phb->pci_mem_offset); + udbg_printf("\tpci_io_offset = 0x%016LX\n", phb->pci_io_offset); + + udbg_printf("\tcfg_addr = 0x%016LX\n", phb->cfg_addr); + udbg_printf("\tcfg_data = 0x%016LX\n", phb->cfg_data); + udbg_printf("\tphb_regs = 0x%016LX\n", phb->phb_regs); + udbg_printf("\tchip_regs = 0x%016LX\n", phb->chip_regs); + + + udbg_printf("\tResources\n"); + dumpResources(&phb->io_resource); + if (phb->mem_resource_count > 0) dumpResources(&phb->mem_resources[0]); + if (phb->mem_resource_count > 1) dumpResources(&phb->mem_resources[1]); + if (phb->mem_resource_count > 2) dumpResources(&phb->mem_resources[2]); + + udbg_printf("\tglobal_num = 0x%02X\n", phb->global_number); + udbg_printf("\tlocal_num = 0x%02X\n", phb->local_number); + } +} + +/***************************************************** + * Dump PHB information for Debug + *****************************************************/ +void dumpPci_Bus(struct pci_bus* Pci_Bus) +{ + int i; + udbg_printf("\tpci_bus = 0x%016LX \n",Pci_Bus); + if (Pci_Bus != NULL) { + + udbg_printf("\tnumber = 0x%02X \n",Pci_Bus->number); + udbg_printf("\tprimary = 0x%02X \n",Pci_Bus->primary); + udbg_printf("\tsecondary = 0x%02X \n",Pci_Bus->secondary); + udbg_printf("\tsubordinate = 0x%02X \n",Pci_Bus->subordinate); + + for (i=0;i<4;++i) { + if(Pci_Bus->resource[i] == NULL) continue; + if(Pci_Bus->resource[i]->start == 0 && Pci_Bus->resource[i]->end == 0) break; + udbg_printf("\tResources[%d]",i); + dumpResources(Pci_Bus->resource[i]); + } + } +} + +/***************************************************** + * Dump Device information for Debug + *****************************************************/ +void dumpPci_Dev(struct pci_dev* Pci_Dev) +{ + int i; + udbg_printf("\tpci_dev* = 0x%p\n",Pci_Dev); + if ( Pci_Dev == NULL ) return; + udbg_printf("\tname = %s \n",Pci_Dev->name); + udbg_printf("\tbus* = 0x%p\n",Pci_Dev->bus); + udbg_printf("\tsysdata* = 0x%p\n",Pci_Dev->sysdata); + udbg_printf("\tDevice = 0x%4X%02X:%02X.%02X 0x%04X:%04X\n", + PCI_GET_PHB_NUMBER(Pci_Dev), + PCI_GET_BUS_NUMBER(Pci_Dev), + PCI_SLOT(Pci_Dev->devfn), + PCI_FUNC(Pci_Dev->devfn), + Pci_Dev->vendor, + Pci_Dev->device); + udbg_printf("\tHdr/Irq = 0x%02X/0x%02X \n",Pci_Dev->hdr_type,Pci_Dev->irq); + for (i=0;iresource[i].start == 0 && Pci_Dev->resource[i].end == 0) continue; + udbg_printf("\tResources[%d] ",i); + dumpResources(&Pci_Dev->resource[i]); + } + dumpResources(&Pci_Dev->resource[i]); +} diff -Nru a/arch/ppc64/kernel/pci.h b/arch/ppc64/kernel/pci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/pci.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,112 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef __PPC_KERNEL_PCI_H__ +#define __PPC_KERNEL_PCI_H__ + +#include +#include + +extern unsigned long isa_io_base; +extern unsigned long isa_mem_base; +extern unsigned long pci_dram_offset; + +/******************************************************************* + * Platform independant variables referenced. + ******************************************************************* + * Set pci_assign_all_busses to 1 if you want the kernel to re-assign + * all PCI bus numbers. + *******************************************************************/ +extern int pci_assign_all_busses; + +extern struct pci_controller* pci_alloc_pci_controller(char *model, enum phb_types controller_type); +extern struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node); + +extern struct pci_controller* hose_head; +extern struct pci_controller** hose_tail; +/* PHB's are also in a table. */ +#define PCI_MAX_PHB 64 +extern int global_phb_number; +extern struct pci_controller *phbtab[]; + +/******************************************************************* + * Platform functions that are brand specific implementation. + *******************************************************************/ +extern unsigned long find_and_init_phbs(void); + +extern void fixup_resources(struct pci_dev *dev); +extern void ppc64_pcibios_init(void); + +extern int pci_set_reset(struct pci_dev*,int); +extern int device_Location(struct pci_dev*,char*); +extern int format_device_location(struct pci_dev*,char*, int ); + +extern struct pci_dev *ppc64_floppy_dev; + +/******************************************************************* + * PCI device_node operations + *******************************************************************/ +struct device_node; +typedef void *(*traverse_func)(struct device_node *me, void *data); +void *traverse_pci_devices(struct device_node *start, traverse_func pre, traverse_func post, void *data); +void *traverse_all_pci_devices(traverse_func pre); + +void pci_devs_phb_init(void); +void pci_fix_bus_sysdata(void); +struct device_node *fetch_dev_dn(struct pci_dev *dev); + +void iSeries_pcibios_init_early(void); +void pSeries_pcibios_init_early(void); +void pSeries_pcibios_init(void); + +/* Get a device_node from a pci_dev. This code must be fast except in the case + * where the sysdata is incorrect and needs to be fixed up (hopefully just once) + */ +static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev) +{ + struct device_node *dn = (struct device_node *)(dev->sysdata); + if (dn->devfn == dev->devfn && dn->busno == dev->bus->number) + return dn; /* fast path. sysdata is good */ + else + return fetch_dev_dn(dev); +} +/* Use this macro after the PCI bus walk for max performance when it + * is known that sysdata is correct. + */ +#define PCI_GET_DN(dev) ((struct device_node *)((dev)->sysdata)) + + +/******************************************************************* + * Platform configuration flags.. (Live in pci.c) + *******************************************************************/ +extern int Pci_Large_Bus_System; /* System has > 256 buses */ +extern int Pci_Manage_Phb_Space; /* Manage Phb Space for IOAs*/ + +/******************************************************************* + * Helper macros for extracting data from pci structures. + * PCI_GET_PHB_PTR(struct pci_dev*) returns the Phb pointer. + * PCI_GET_PHB_NUMBER(struct pci_dev*) returns the Phb number. + * PCI_GET_BUS_NUMBER(struct pci_dev*) returns the bus number. + *******************************************************************/ +#define PCI_GET_PHB_PTR(dev) (((struct device_node *)(dev)->sysdata)->phb) +#define PCI_GET_PHB_NUMBER(dev) (((dev)->bus->number&0x00FFFF00)>>8) +#define PCI_GET_BUS_NUMBER(dev) ((dev)->bus->number&0x0000FF) + +/******************************************************************* + * Debugging Routines. + *******************************************************************/ +extern void dumpResources(struct resource* Resource); +extern void dumpPci_Controller(struct pci_controller* phb); +extern void dumpPci_Bus(struct pci_bus* Pci_Bus); +extern void dumpPci_Dev(struct pci_dev* Pci_Dev); + +extern void dump_Phb_tree(void); +extern void dump_Bus_tree(void); +extern void dump_Dev_tree(void); + +#endif /* __PPC_KERNEL_PCI_H__ */ diff -Nru a/arch/ppc64/kernel/pci_dma.c b/arch/ppc64/kernel/pci_dma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/pci_dma.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1477 @@ +/* + * pci_dma.c + * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation + * + * Dynamic DMA mapping support. + * + * Manages the TCE space assigned to this partition. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "pci.h" + +// #define DEBUG_TCE 1 + +/* Initialize so this guy does not end up in the BSS section. + * Only used to pass OF initialization data set in prom.c into the main + * kernel code -- data ultimately copied into tceTables[]. + */ +extern struct _of_tce_table of_tce_table[]; + +extern struct pci_controller* hose_head; +extern struct pci_controller** hose_tail; + +struct TceTable virtBusVethTceTable; /* Tce table for virtual ethernet */ +struct TceTable virtBusVioTceTable; /* Tce table for virtual I/O */ + +struct device_node iSeries_veth_dev_node = { tce_table: &virtBusVethTceTable }; +struct device_node iSeries_vio_dev_node = { tce_table: &virtBusVioTceTable }; + +struct pci_dev iSeries_veth_dev_st = { sysdata: &iSeries_veth_dev_node }; +struct pci_dev iSeries_vio_dev_st = { sysdata: &iSeries_vio_dev_node }; + +struct pci_dev * iSeries_veth_dev = &iSeries_veth_dev_st; +struct pci_dev * iSeries_vio_dev = &iSeries_vio_dev_st; + +struct TceTable * tceTables[256]; /* Tce tables for 256 busses + * Bus 255 is the virtual bus + * zero indicates no bus defined + */ +/* allocates a contiguous range of tces (power-of-2 size) */ +static inline long alloc_tce_range(struct TceTable *, + unsigned order ); + +/* allocates a contiguous range of tces (power-of-2 size) + * assumes lock already held + */ +static long alloc_tce_range_nolock(struct TceTable *, + unsigned order ); + +/* frees a contiguous range of tces (power-of-2 size) */ +static inline void free_tce_range(struct TceTable *, + long tcenum, + unsigned order ); + +/* frees a contiguous rnage of tces (power-of-2 size) + * assumes lock already held + */ +void free_tce_range_nolock(struct TceTable *, + long tcenum, + unsigned order ); + +/* allocates a range of tces and sets them to the pages */ +static inline dma_addr_t get_tces( struct TceTable *, + unsigned order, + void *page, + unsigned numPages, + int direction ); + +static long test_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + +static unsigned fill_scatterlist_sg(struct scatterlist *sg, int nents, + dma_addr_t dma_addr, + unsigned long numTces ); + +static unsigned long num_tces_sg( struct scatterlist *sg, + int nents ); + +static dma_addr_t create_tces_sg( struct TceTable *tbl, + struct scatterlist *sg, + int nents, + unsigned numTces, + int direction ); + +static void getTceTableParmsPSeries( struct pci_controller *phb, + struct device_node *dn, + struct TceTable *tce_table_parms ); + +static void getTceTableParmsPSeriesLP(struct pci_controller *phb, + struct device_node *dn, + struct TceTable *newTceTable ); + +void create_pci_bus_tce_table( unsigned long token ); + +u8 iSeries_Get_Bus( struct pci_dev * dv ) +{ + return 0; +} + +static inline struct TceTable *get_tce_table(struct pci_dev *dev) { + + if ( ( _machine == _MACH_iSeries ) && ( dev->bus ) ) + return tceTables[dev->bus->number]; + /* On the iSeries, the virtual bus will take this path. There is a */ + /* fake pci_dev and dev_node built and used. */ + return PCI_GET_DN(dev)->tce_table; +} + +static unsigned long __inline__ count_leading_zeros64( unsigned long x ) +{ + unsigned long lz; + asm("cntlzd %0,%1" : "=r"(lz) : "r"(x)); + return lz; +} + +static void tce_build_iSeries(struct TceTable *tbl, long tcenum, + unsigned long uaddr, int direction ) +{ + u64 setTceRc; + union Tce tce; + + PPCDBG(PPCDBG_TCE, "build_tce: uaddr = 0x%lx\n", uaddr); + PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx, tbl = 0x%lx, index=%lx\n", + tcenum, tbl, tbl->index); + + tce.wholeTce = 0; + tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; + + /* If for virtual bus */ + if ( tbl->tceType == TCE_VB ) { + tce.tceBits.valid = 1; + tce.tceBits.allIo = 1; + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.readWrite = 1; + } else { + /* If for PCI bus */ + tce.tceBits.readWrite = 1; // Read allowed + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.pciWrite = 1; + } + + setTceRc = HvCallXm_setTce((u64)tbl->index, + (u64)tcenum, + tce.wholeTce ); + + if(setTceRc) { + printk("PCI: tce_build failed 0x%lx tcenum: 0x%lx\n", setTceRc, (u64)tcenum); + //PPCDBG(PPCDBG_TCE, "setTce failed. rc=%ld\n", setTceRc); + //PPCDBG(PPCDBG_TCE, "\tindex = 0x%lx\n", (u64)tbl->index); + //PPCDBG(PPCDBG_TCE, "\ttce num = 0x%lx\n", (u64)tcenum); + //PPCDBG(PPCDBG_TCE, "\ttce val = 0x%lx\n", tce.wholeTce ); + } +} + +static void tce_build_pSeries(struct TceTable *tbl, long tcenum, + unsigned long uaddr, int direction ) +{ + union Tce tce; + union Tce *tce_addr; + + PPCDBG(PPCDBG_TCE, "build_tce: uaddr = 0x%lx\n", uaddr); + PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx, tbl = 0x%lx, index=%lx\n", + tcenum, tbl, tbl->index); + + tce.wholeTce = 0; + tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; + + tce.tceBits.readWrite = 1; // Read allowed + if ( direction != PCI_DMA_TODEVICE ) tce.tceBits.pciWrite = 1; + + tce_addr = ((union Tce *)tbl->base) + tcenum; + *tce_addr = (union Tce)tce.wholeTce; + + /* Make sure the update is visible to hardware. */ + __asm__ __volatile__ ("sync" : : : "memory"); +} + +/* + * Build a TceTable structure. This contains a multi-level bit map which + * is used to manage allocation of the tce space. + */ +static struct TceTable *build_tce_table( struct TceTable * tbl ) +{ + unsigned long bits, bytes, totalBytes; + unsigned long numBits[NUM_TCE_LEVELS], numBytes[NUM_TCE_LEVELS]; + unsigned i, k, m; + unsigned char * pos, * p, b; + + PPCDBG(PPCDBG_TCEINIT, "build_tce_table: tbl = 0x%lx\n", tbl); + spin_lock_init( &(tbl->lock) ); + + tbl->mlbm.maxLevel = 0; + + /* Compute number of bits and bytes for each level of the + * multi-level bit map + */ + totalBytes = 0; + bits = tbl->size * (PAGE_SIZE / sizeof( union Tce )); + + for ( i=0; imlbm.level[i].map = pos; + tbl->mlbm.maxLevel = i; + + if ( numBits[i] & 1 ) { + p = pos + numBytes[i] - 1; + m = (( numBits[i] % 8) - 1) & 7; + *p = 0x80 >> m; + PPCDBG(PPCDBG_TCEINIT, "build_tce_table: level %d last bit %x\n", i, 0x80>>m ); + } + } + else + tbl->mlbm.level[i].map = 0; + pos += numBytes[i]; + tbl->mlbm.level[i].numBits = numBits[i]; + tbl->mlbm.level[i].numBytes = numBytes[i]; + } + + /* For the highest level, turn on all the bits */ + + i = tbl->mlbm.maxLevel; + p = tbl->mlbm.level[i].map; + m = numBits[i]; + PPCDBG(PPCDBG_TCEINIT, "build_tce_table: highest level (%d) has all bits set\n", i); + for (k=0; k= 8 ) { + /* handle full bytes */ + *p++ = 0xff; + m -= 8; + } + else if(m>0) { + /* handle the last partial byte */ + b = 0x80; + *p = 0; + while (m) { + *p |= b; + b >>= 1; + --m; + } + } else { + break; + } + } + + return tbl; +} + +static inline long alloc_tce_range( struct TceTable *tbl, unsigned order ) +{ + long retval; + unsigned long flags; + + /* Lock the tce allocation bitmap */ + spin_lock_irqsave( &(tbl->lock), flags ); + + /* Do the actual work */ + retval = alloc_tce_range_nolock( tbl, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + + return retval; +} + +static long alloc_tce_range_nolock( struct TceTable *tbl, unsigned order ) +{ + unsigned long numBits, numBytes; + unsigned long i, bit, block, mask; + long tcenum; + u64 * map; + + /* If the order (power of 2 size) requested is larger than our + * biggest, indicate failure + */ + if(order >= NUM_TCE_LEVELS) { + PPCDBG(PPCDBG_TCE, + "alloc_tce_range_nolock: invalid order: %d\n", order ); + return -1; + } + + numBits = tbl->mlbm.level[order].numBits; + numBytes = tbl->mlbm.level[order].numBytes; + map = (u64 *)tbl->mlbm.level[order].map; + + /* Initialize return value to -1 (failure) */ + tcenum = -1; + + /* Loop through the bytes of the bitmap */ + for (i=0; imlbm.maxLevel ) + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: trying next bigger size\n" ); + else + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: maximum size reached...failing\n"); + } +#endif + + /* If no block of the requested size was found, try the next + * size bigger. If one of those is found, return the second + * half of the block to freespace and keep the first half + */ + if((tcenum == -1) && (order < (NUM_TCE_LEVELS - 1))) { + tcenum = alloc_tce_range_nolock( tbl, order+1 ); + if ( tcenum != -1 ) { + free_tce_range_nolock( tbl, tcenum+(1<lock), flags ); + + /* Do the actual work */ + free_tce_range_nolock( tbl, tcenum, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + +} + +void free_tce_range_nolock(struct TceTable *tbl, + long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + unsigned char * map, * bytep; + + if (order >= NUM_TCE_LEVELS) { + PPCDBG(PPCDBG_TCE, + "free_tce_range: invalid order: %d, tcenum = %d\n", + order, tcenum ); + return; + } + + block = tcenum >> order; + +#ifdef DEBUG_TCE + if ( tcenum != (block << order ) ) { + PPCDBG(PPCDBG_TCE, + "free_tce_range: tcenum %lx misaligned for order %x\n", + tcenum, order ); + return; + } + + + if ( block >= tbl->mlbm.level[order].numBits ) { + PPCDBG(PPCDBG_TCE, + "free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", + tcenum, order, tbl->mlbm.level[order].numBits ); + return; + } + + + if ( test_tce_range( tbl, tcenum, order ) ) { + PPCDBG(PPCDBG_TCE, + "free_tce_range: freeing range not allocated.\n"); + PPCDBG(PPCDBG_TCE, + "\tTceTable %p, tcenum %lx, order %x\n", + tbl, tcenum, order ); + } +#endif + + map = tbl->mlbm.level[order].map; + byte = block / 8; + bit = block % 8; + mask = 0x80 >> bit; + bytep = map + byte; + +#ifdef DEBUG_TCE + PPCDBG(PPCDBG_TCE, + "free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n", + block, byte, bit, order); + if ( *bytep & mask ) + PPCDBG(PPCDBG_TCE, + "free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", + tbl, tcenum, order ); +#endif + + *bytep |= mask; + + /* If there is a higher level in the bit map than this we may be + * able to buddy up this block with its partner. + * If this is the highest level we can't buddy up + * If this level has an odd number of bits and + * we are freeing the last block we can't buddy up + * Don't buddy up if it's in the first 1/4 of the level + */ + if (( block > (tbl->mlbm.level[order].numBits/4) ) && + (( block < tbl->mlbm.level[order].numBits-1 ) || + ( 0 == ( tbl->mlbm.level[order].numBits & 1)))) { + /* See if we can buddy up the block we just freed */ + bit &= 6; /* get to the first of the buddy bits */ + mask = 0xc0 >> bit; /* build two bit mask */ + b = *bytep & mask; /* Get the two bits */ + if ( 0 == (b ^ mask) ) { /* If both bits are on */ + /* both of the buddy blocks are free we can combine them */ + *bytep ^= mask; /* turn off the two bits */ + block = ( byte * 8 ) + bit; /* block of first of buddies */ + tcenum = block << order; + /* free the buddied block */ + PPCDBG(PPCDBG_TCE, + "free_tce_range: buddying blocks %ld & %ld\n", + block, block+1); + free_tce_range_nolock( tbl, tcenum, order+1 ); + } + } +} + +static long test_tce_range( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + long retval, retLeft, retRight; + unsigned char * map; + + map = tbl->mlbm.level[order].map; + block = tcenum >> order; + byte = block / 8; /* Byte within bitmap */ + bit = block % 8; /* Bit within byte */ + mask = 0x80 >> bit; + b = (*(map+byte) & mask ); /* 0 if block is allocated, else free */ + if ( b ) + retval = 1; /* 1 == block is free */ + else + retval = 0; /* 0 == block is allocated */ + /* Test bits at all levels below this to ensure that all agree */ + + if (order) { + retLeft = test_tce_range( tbl, tcenum, order-1 ); + retRight = test_tce_range( tbl, tcenum+(1<<(order-1)), order-1 ); + if ( retLeft || retRight ) { + retval = 2; + } + } + + /* Test bits at all levels above this to ensure that all agree */ + + return retval; +} + +static inline dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int direction ) +{ + long tcenum; + unsigned long uaddr; + unsigned i; + dma_addr_t retTce = NO_TCE; + + uaddr = (unsigned long)page & PAGE_MASK; + + /* Allocate a range of tces */ + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + /* We got the tces we wanted */ + tcenum += tbl->startOffset; /* Offset into real TCE table */ + retTce = tcenum << PAGE_SHIFT; /* Set the return dma address */ + /* Setup a tce for each page */ + for (i=0; isize * (PAGE_SIZE / sizeof(union Tce))) - 1; + + tcenum = dma_addr >> PAGE_SHIFT; + + freeTce = tcenum - tbl->startOffset; + + if ( freeTce > maxTcenum ) { + PPCDBG(PPCDBG_TCE, "free_tces: tcenum > maxTcenum\n"); + PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx\n", tcenum); + PPCDBG(PPCDBG_TCE, "\tmaxTcenum = 0x%lx\n", maxTcenum); + PPCDBG(PPCDBG_TCE, "\tTCE Table = 0x%lx\n", (u64)tbl); + PPCDBG(PPCDBG_TCE, "\tbus# = 0x%lx\n", (u64)tbl->busNumber ); + PPCDBG(PPCDBG_TCE, "\tsize = 0x%lx\n", (u64)tbl->size); + PPCDBG(PPCDBG_TCE, "\tstartOff = 0x%lx\n", (u64)tbl->startOffset ); + PPCDBG(PPCDBG_TCE, "\tindex = 0x%lx\n", (u64)tbl->index); + return; + } + + for (i=0; iindex, + (u64)tcenum, + tce.wholeTce ); + + if ( setTceRc ) { + printk("PCI: tce_free failed 0x%lx tcenum: 0x%lx\n", setTceRc, (u64)tcenum); + //PPCDBG(PPCDBG_TCE, "tce_free: setTce failed\n"); + //PPCDBG(PPCDBG_TCE, "\trc = 0x%lx\n", setTceRc); + //PPCDBG(PPCDBG_TCE, "\tindex = 0x%lx\n", (u64)tbl->index); + //PPCDBG(PPCDBG_TCE, "\ttce num = 0x%lx\n", (u64)tcenum); + //PPCDBG(PPCDBG_TCE, "\ttce val = 0x%lx\n", tce.wholeTce ); + } + + ++tcenum; + } + + free_tce_range( tbl, freeTce, order ); +} + +static void tce_free_pSeries(struct TceTable *tbl, dma_addr_t dma_addr, + unsigned order, unsigned numPages) +{ + long tcenum, freeTce, maxTcenum; + unsigned i; + union Tce tce; + union Tce *tce_addr; + + maxTcenum = (tbl->size * (PAGE_SIZE / sizeof(union Tce))) - 1; + + tcenum = dma_addr >> PAGE_SHIFT; + // tcenum -= tbl->startOffset; + + freeTce = tcenum - tbl->startOffset; + + if ( freeTce > maxTcenum ) { + PPCDBG(PPCDBG_TCE, "free_tces: tcenum > maxTcenum\n"); + PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx\n", tcenum); + PPCDBG(PPCDBG_TCE, "\tmaxTcenum = 0x%lx\n", maxTcenum); + PPCDBG(PPCDBG_TCE, "\tTCE Table = 0x%lx\n", (u64)tbl); + PPCDBG(PPCDBG_TCE, "\tbus# = 0x%lx\n", + (u64)tbl->busNumber ); + PPCDBG(PPCDBG_TCE, "\tsize = 0x%lx\n", (u64)tbl->size); + PPCDBG(PPCDBG_TCE, "\tstartOff = 0x%lx\n", + (u64)tbl->startOffset ); + PPCDBG(PPCDBG_TCE, "\tindex = 0x%lx\n", (u64)tbl->index); + return; + } + + for (i=0; ibase) + tcenum; + *tce_addr = (union Tce)tce.wholeTce; + + ++tcenum; + } + + /* Make sure the update is visible to hardware. */ + __asm__ __volatile__ ("sync" : : : "memory"); + + free_tce_range( tbl, freeTce, order ); +} + +void __init create_virtual_bus_tce_table(void) +{ + struct TceTable *t; + struct TceTableManagerCB virtBusTceTableParms; + u64 absParmsPtr; + + virtBusTceTableParms.busNumber = 255; /* Bus 255 is the virtual bus */ + virtBusTceTableParms.virtualBusFlag = 0xff; /* Ask for virtual bus */ + + absParmsPtr = virt_to_absolute( (u64)&virtBusTceTableParms ); + HvCallXm_getTceTableParms( absParmsPtr ); + + virtBusVethTceTable.size = virtBusTceTableParms.size / 2; + virtBusVethTceTable.busNumber = virtBusTceTableParms.busNumber; + virtBusVethTceTable.startOffset = virtBusTceTableParms.startOffset; + virtBusVethTceTable.index = virtBusTceTableParms.index; + virtBusVethTceTable.tceType = TCE_VB; + + virtBusVioTceTable.size = virtBusTceTableParms.size - virtBusVethTceTable.size; + virtBusVioTceTable.busNumber = virtBusTceTableParms.busNumber; + virtBusVioTceTable.startOffset = virtBusTceTableParms.startOffset + + virtBusVethTceTable.size * (PAGE_SIZE/sizeof(union Tce)); + virtBusVioTceTable.index = virtBusTceTableParms.index; + virtBusVioTceTable.tceType = TCE_VB; + + t = build_tce_table( &virtBusVethTceTable ); + if ( t ) { + tceTables[255] = t; + printk( "Virtual Bus VETH TCE table built successfully.\n"); + printk( " TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + printk( " TCE table token = %d\n", + (unsigned)t->index ); + printk( " TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else + printk( "Virtual Bus VETH TCE table failed.\n"); + + t = build_tce_table( &virtBusVioTceTable ); + if ( t ) { + printk( "Virtual Bus VIO TCE table built successfully.\n"); + printk( " TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + printk( " TCE table token = %d\n", + (unsigned)t->index ); + printk( " TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else + printk( "Virtual Bus VIO TCE table failed.\n"); +} + +void create_tce_tables_for_buses(struct list_head *bus_list) +{ + struct pci_controller* phb; + struct device_node *dn, *first_dn; + int num_slots, num_slots_ilog2; + int first_phb = 1; + + for (phb=hose_head;phb;phb=phb->next) { + first_dn = ((struct device_node *)phb->arch_data)->child; + /* Carve 2GB into the largest dma_window_size possible */ + for (dn = first_dn, num_slots = 0; dn != NULL; dn = dn->sibling) + num_slots++; + num_slots_ilog2 = __ilog2(num_slots); + if ((1<dma_window_size = 1 << (22 - num_slots_ilog2); + /* Reserve 16MB of DMA space on the first PHB. + * We should probably be more careful and use firmware props. + * In reality this space is remapped, not lost. But we don't + * want to get that smart to handle it -- too much work. + */ + phb->dma_window_base_cur = first_phb ? (1 << 12) : 0; + first_phb = 0; + for (dn = first_dn, num_slots = 0; dn != NULL; dn = dn->sibling) { + create_pci_bus_tce_table((unsigned long)dn); + } + } +} + +void create_tce_tables_for_busesLP(struct list_head *bus_list) +{ + struct list_head *ln; + struct pci_bus *bus; + struct device_node *busdn; + u32 *dma_window; + for (ln=bus_list->next; ln != bus_list; ln=ln->next) { + bus = pci_bus_b(ln); + busdn = PCI_GET_DN(bus); + /* NOTE: there should never be a window declared on a bus when + * child devices also have a window. If this should ever be + * architected, we probably want children to have priority. + * In reality, the PHB containing ISA has the property, but otherwise + * it is the pci-bridges that have the property. + */ + dma_window = (u32 *)get_property(busdn, "ibm,dma-window", 0); + if (dma_window) { + /* Busno hasn't been copied yet. + * Do it now because getTceTableParmsPSeriesLP needs it. + */ + busdn->busno = bus->number; + create_pci_bus_tce_table((unsigned long)busdn); + } else + create_tce_tables_for_busesLP(&bus->children); + } +} + +void create_tce_tables(void) { + struct pci_dev *dev; + struct device_node *dn, *mydn; + + if (_machine == _MACH_pSeriesLP) + create_tce_tables_for_busesLP(&pci_root_buses); + else + create_tce_tables_for_buses(&pci_root_buses); + + /* Now copy the tce_table ptr from the bus devices down to every + * pci device_node. This means get_tce_table() won't need to search + * up the device tree to find it. + */ + pci_for_each_dev(dev) { + mydn = dn = PCI_GET_DN(dev); + while (dn && dn->tce_table == NULL) + dn = dn->parent; + if (dn) { + mydn->tce_table = dn->tce_table; + } + } +} + +/* + * iSeries token = busNumber + * pSeries token = pci_controller* + */ +void create_pci_bus_tce_table( unsigned long token ) { + struct TceTable * builtTceTable; + struct TceTable * newTceTable; + struct TceTableManagerCB pciBusTceTableParms; + u64 parmsPtr; + + PPCDBG(PPCDBG_TCE, "Entering create_pci_bus_tce_table.\n"); + PPCDBG(PPCDBG_TCE, "\ttoken = 0x%lx\n", token); + + newTceTable = kmalloc( sizeof(struct TceTable), GFP_KERNEL ); + + if(_machine == _MACH_iSeries) { + if ( token > 254 ) { + printk("PCI: Bus TCE table failed, invalid bus number %lu\n", token ); + return; + } + + pciBusTceTableParms.busNumber = token; + pciBusTceTableParms.virtualBusFlag = 0; + parmsPtr = virt_to_absolute( (u64)&pciBusTceTableParms ); + + /* + * Call HV with the architected data structure to get TCE table + * info. Put the returned data into the Linux representation + * of the TCE table data. + */ + HvCallXm_getTceTableParms( parmsPtr ); + printk("PCI: getTceTableParms: Bus: 0x%lx Size: 0x%lx, Start: 0x%lx, Index: 0x%lx\n", + pciBusTceTableParms.busNumber, + pciBusTceTableParms.size, + pciBusTceTableParms.startOffset, + pciBusTceTableParms.index); + + /* Determine if the table identified by the index and startOffset */ + /* returned by the hypervisor for this bus has already been created. */ + /* If so, set the tceTable entry to point to the linux shared tceTable.*/ + int BusIndex; + for ( BusIndex=0; BusIndex<255; ++BusIndex) { + if (tceTables[BusIndex] != NULL) { + struct TceTable* CmprTceTable = tceTables[BusIndex]; + if ( ( CmprTceTable->index == pciBusTceTableParms.index ) && + ( CmprTceTable->startOffset == pciBusTceTableParms.startOffset ) ) { + tceTables[token] = CmprTceTable; + printk("PCI: Bus %lu Shares a TCE table with bus %d\n",token,BusIndex); + break; + } + } + } + /* No shared table, build a new table for this bus. */ + if (tceTables[token] == NULL) { + newTceTable->size = pciBusTceTableParms.size; + newTceTable->busNumber = pciBusTceTableParms.busNumber; + newTceTable->startOffset = pciBusTceTableParms.startOffset; + newTceTable->index = pciBusTceTableParms.index; + + builtTceTable = build_tce_table( newTceTable ); + builtTceTable->tceType = TCE_PCI; + tceTables[token] = builtTceTable; + } + else { + /* We're using the shared table, not this new one. */ + kfree(newTceTable); + } + + printk("PCI: Pci bus %lu TceTable: %p\n",token,tceTables[token]); + return; + } else { + struct device_node *dn; + struct pci_controller *phb; + + dn = (struct device_node *)token; + phb = dn->phb; + if (_machine == _MACH_pSeries) + getTceTableParmsPSeries(phb, dn, newTceTable); + else + getTceTableParmsPSeriesLP(phb, dn, newTceTable); + builtTceTable = build_tce_table( newTceTable ); + dn->tce_table = builtTceTable; + } + + if(builtTceTable == NULL ) { + kfree( newTceTable ); + PPCDBG(PPCDBG_TCE, "PCI Bus TCE table failed.\n"); + return; + } +} + +static void getTceTableParmsPSeries(struct pci_controller *phb, + struct device_node *dn, + struct TceTable *newTceTable ) { + phandle node; + unsigned long i; + + node = ((struct device_node *)(phb->arch_data))->node; + + PPCDBG(PPCDBG_TCEINIT, "getTceTableParms: start\n"); + PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table = 0x%lx\n", of_tce_table); + PPCDBG(PPCDBG_TCEINIT, "\tphb = 0x%lx\n", phb); + PPCDBG(PPCDBG_TCEINIT, "\tdn = 0x%lx\n", dn); + PPCDBG(PPCDBG_TCEINIT, "\tdn->name = %s\n", dn->name); + PPCDBG(PPCDBG_TCEINIT, "\tdn->full_name= %s\n", dn->full_name); + PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable = 0x%lx\n", newTceTable); + PPCDBG(PPCDBG_TCEINIT, "\tdma_window_size = 0x%lx\n", phb->dma_window_size); + + i = 0; + while(of_tce_table[i].node) { + PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table[%d].node = 0x%lx\n", + i, of_tce_table[i].node); + PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table[%d].base = 0x%lx\n", + i, of_tce_table[i].base); + PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table[%d].size = 0x%lx\n", + i, of_tce_table[i].size >> PAGE_SHIFT); + PPCDBG(PPCDBG_TCEINIT, "\tphb->arch_data->node = 0x%lx\n", + node); + + if(of_tce_table[i].node == node) { + memset((void *)of_tce_table[i].base, + 0, of_tce_table[i].size); + newTceTable->busNumber = phb->bus->number; + + /* Units of tce entries. */ + newTceTable->startOffset = phb->dma_window_base_cur; + + /* Adjust the current table offset to the next */ + /* region. Measured in TCE entries. Force an */ + /* alignment to the size alloted per IOA. This */ + /* makes it easier to remove the 1st 16MB. */ + phb->dma_window_base_cur += (phb->dma_window_size>>3); + phb->dma_window_base_cur &= + ~((phb->dma_window_size>>3)-1); + + /* Set the tce table size - measured in units */ + /* of pages of tce table. */ + newTceTable->size = ((phb->dma_window_base_cur - + newTceTable->startOffset) << 3) + >> PAGE_SHIFT; + + /* Test if we are going over 2GB of DMA space. */ + if(phb->dma_window_base_cur > (1 << 19)) { + udbg_printf("Unexpected number of IOAs under this PHB"); + panic("Unexpected number of IOAs under this PHB"); + } + + newTceTable->base = of_tce_table[i].base; + newTceTable->index = 0; + + PPCDBG(PPCDBG_TCEINIT, + "\tnewTceTable->base = 0x%lx\n", + newTceTable->base); + PPCDBG(PPCDBG_TCEINIT, + "\tnewTceTable->startOffset = 0x%lx" + "(# tce entries)\n", + newTceTable->startOffset); + PPCDBG(PPCDBG_TCEINIT, + "\tnewTceTable->size = 0x%lx" + "(# pages of tce table)\n", + newTceTable->size); + } + i++; + } +} + +/* + * getTceTableParmsPSeriesLP + * + * Function: On pSeries LPAR systems, return TCE table info, given a pci bus. + * + * ToDo: properly interpret the ibm,dma-window property. The definition is: + * logical-bus-number (1 word) + * phys-address (#address-cells words) + * size (#cell-size words) + * + * Currently we hard code these sizes (more or less). + */ +static void getTceTableParmsPSeriesLP(struct pci_controller *phb, + struct device_node *dn, + struct TceTable *newTceTable ) { + u32 *dma_window = (u32 *)get_property(dn, "ibm,dma-window", 0); + if (!dma_window) { + panic("getTceTableParmsPSeriesLP: device %s has no ibm,dma-window property!\n", dn->full_name); + } + + newTceTable->busNumber = dn->busno; + newTceTable->size = (((((unsigned long)dma_window[4] << 32) | (unsigned long)dma_window[5]) >> PAGE_SHIFT) << 3) >> PAGE_SHIFT; + newTceTable->startOffset = ((((unsigned long)dma_window[2] << 32) | (unsigned long)dma_window[3]) >> 12); + newTceTable->base = 0; + newTceTable->index = dma_window[0]; + PPCDBG(PPCDBG_TCEINIT, "getTceTableParmsPSeriesLP for bus 0x%lx:\n", dn->busno); + PPCDBG(PPCDBG_TCEINIT, "\tDevice = %s\n", dn->full_name); + PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable->index = 0x%lx\n", newTceTable->index); + PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable->startOffset = 0x%lx\n", newTceTable->startOffset); + PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable->size = 0x%lx\n", newTceTable->size); +} + +/* Allocates a contiguous real buffer and creates TCEs over it. + * Returns the virtual address of the buffer and sets dma_handle + * to the dma address (tce) of the first page. + */ +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + struct TceTable * tbl; + void *ret = NULL; + unsigned order, nPages; + dma_addr_t tce; + + PPCDBG(PPCDBG_TCE, "pci_alloc_consistent:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx\n", hwdev); + PPCDBG(PPCDBG_TCE, "\tsize = 0x%16.16lx\n", size); + PPCDBG(PPCDBG_TCE, "\tdma_handle = 0x%16.16lx\n", dma_handle); + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + tbl = get_tce_table(hwdev); + + if ( tbl ) { + /* Alloc enough pages (and possibly more) */ + ret = (void *)__get_free_pages( GFP_ATOMIC, order ); + if ( ret ) { + /* Page allocation succeeded */ + memset(ret, 0, nPages << PAGE_SHIFT); + /* Set up tces to cover the allocated range */ + tce = get_tces( tbl, order, ret, nPages, PCI_DMA_BIDIRECTIONAL ); + if ( tce == NO_TCE ) { + PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: get_tces failed\n" ); + free_pages( (unsigned long)ret, order ); + ret = NULL; + } + else + { + *dma_handle = tce; + } + } + else PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: __get_free_pages failed for order = %d\n", order); + } + else PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: get_tce_table failed for 0x%016lx\n", hwdev); + + PPCDBG(PPCDBG_TCE, "\tpci_alloc_consistent: dma_handle = 0x%16.16lx\n", *dma_handle); + PPCDBG(PPCDBG_TCE, "\tpci_alloc_consistent: return = 0x%16.16lx\n", ret); + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + struct TceTable * tbl; + unsigned order, nPages; + + PPCDBG(PPCDBG_TCE, "pci_free_consistent:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, dma_handle = 0x%16.16lx, vaddr = 0x%16.16lx\n", hwdev, size, dma_handle, vaddr); + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + if ( order > 10 ) + PPCDBG(PPCDBG_TCE, "pci_free_consistent: order=%d, size=%d, nPages=%d, dma_handle=%016lx, vaddr=%016lx\n", + order, size, nPages, (unsigned long)dma_handle, (unsigned long)vaddr ); + + tbl = get_tce_table(hwdev); + + if ( tbl ) { + ppc_md.tce_free(tbl, dma_handle, order, nPages); + free_pages( (unsigned long)vaddr, order ); + } +} + +/* Creates TCEs for a user provided buffer. The user buffer must be + * contiguous real kernel storage (not vmalloc). The address of the buffer + * passed here is the kernel (virtual) address of the buffer. The buffer + * need not be page aligned, the dma_addr_t returned will point to the same + * byte within the page as vaddr. + */ +dma_addr_t pci_map_single(struct pci_dev *hwdev, void *vaddr, + size_t size, int direction ) +{ + struct TceTable * tbl; + dma_addr_t dma_handle = NO_TCE; + unsigned long uaddr; + unsigned order, nPages; + + PPCDBG(PPCDBG_TCE, "pci_map_single:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, direction = 0x%16.16lx, vaddr = 0x%16.16lx\n", hwdev, size, direction, vaddr); + if ( direction == PCI_DMA_NONE ) + BUG(); + + uaddr = (unsigned long)vaddr; + nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + tbl = get_tce_table(hwdev); + + if ( tbl ) { + dma_handle = get_tces( tbl, order, vaddr, nPages, direction ); + dma_handle |= ( uaddr & ~PAGE_MASK ); + } + + return dma_handle; +} + +void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ) +{ + struct TceTable * tbl; + unsigned order, nPages; + + PPCDBG(PPCDBG_TCE, "pci_unmap_single:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, direction = 0x%16.16lx, dma_handle = 0x%16.16lx\n", hwdev, size, direction, dma_handle); + if ( direction == PCI_DMA_NONE ) + BUG(); + + nPages = PAGE_ALIGN( dma_handle + size ) - ( dma_handle & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + if ( order > 10 ) + PPCDBG(PPCDBG_TCE, "pci_unmap_single: order=%d, size=%d, nPages=%d, dma_handle=%016lx\n", + order, size, nPages, (unsigned long)dma_handle ); + + tbl = get_tce_table(hwdev); + + if ( tbl ) + ppc_md.tce_free(tbl, dma_handle, order, nPages); + +} + +#if 0 +/* Figure out how many TCEs are actually going to be required + * to map this scatterlist. This code is not optimal. It + * takes into account the case where entry n ends in the same + * page in which entry n+1 starts. It does not handle the + * general case of entry n ending in the same page in which + * entry m starts. + */ +static unsigned long num_tces_sg( struct scatterlist *sg, int nents ) +{ + unsigned long nTces, numPages, startPage, endPage, prevEndPage; + unsigned i; + + prevEndPage = 0; + nTces = 0; + + for (i=0; iaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + /* Simple optimization: if the previous entry ended + * in the same page in which this entry starts + * then we can reduce the required pages by one. + * This matches assumptions in fill_scatterlist_sg and + * create_tces_sg + */ + if ( startPage == prevEndPage ) + --numPages; + nTces += numPages; + prevEndPage = endPage; + sg++; + } + return nTces; +} + +/* Fill in the dma data in the scatterlist + * return the number of dma sg entries created + */ +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr , unsigned long numTces) +{ + struct scatterlist *dma_sg; + u32 cur_start_dma; + unsigned long cur_len_dma, cur_end_virt, uaddr; + unsigned num_dma_ents; + + dma_sg = sg; + num_dma_ents = 1; + + /* Process the first sg entry */ + cur_start_dma = dma_addr + ((unsigned long)sg->address & (~PAGE_MASK)); + cur_len_dma = sg->length; + /* cur_end_virt holds the address of the byte immediately after the + * end of the current buffer. + */ + cur_end_virt = (unsigned long)sg->address + cur_len_dma; + /* Later code assumes that unused sg->dma_address and sg->dma_length + * fields will be zero. Other archs seem to assume that the user + * (device driver) guarantees that...I don't want to depend on that + */ + sg->dma_address = sg->dma_length = 0; + + /* Process the rest of the sg entries */ + while (--nents) { + ++sg; + /* Clear possibly unused fields. Note: sg >= dma_sg so + * this can't be clearing a field we've already set + */ + sg->dma_address = sg->dma_length = 0; + + /* Check if it is possible to make this next entry + * contiguous (in dma space) with the previous entry. + */ + + /* The entries can be contiguous in dma space if + * the previous entry ends immediately before the + * start of the current entry (in virtual space) + * or if the previous entry ends at a page boundary + * and the current entry starts at a page boundary. + */ + uaddr = (unsigned long)sg->address; + if ( ( uaddr != cur_end_virt ) && + ( ( ( uaddr | cur_end_virt ) & (~PAGE_MASK) ) || + ( ( uaddr & PAGE_MASK ) == ( ( cur_end_virt-1 ) & PAGE_MASK ) ) ) ) { + /* This entry can not be contiguous in dma space. + * save the previous dma entry and start a new one + */ + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + ++dma_sg; + ++num_dma_ents; + + cur_start_dma += cur_len_dma-1; + /* If the previous entry ends and this entry starts + * in the same page then they share a tce. In that + * case don't bump cur_start_dma to the next page + * in dma space. This matches assumptions made in + * num_tces_sg and create_tces_sg. + */ + if ((uaddr & PAGE_MASK) == ((cur_end_virt-1) & PAGE_MASK)) + cur_start_dma &= PAGE_MASK; + else + cur_start_dma = PAGE_ALIGN(cur_start_dma+1); + cur_start_dma += ( uaddr & (~PAGE_MASK) ); + cur_len_dma = 0; + } + /* Accumulate the length of this entry for the next + * dma entry + */ + cur_len_dma += sg->length; + cur_end_virt = uaddr + sg->length; + } + /* Fill in the last dma entry */ + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + if ((((cur_start_dma +cur_len_dma - 1)>> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1) != numTces) + { + PPCDBG(PPCDBG_TCE, "fill_scatterlist_sg: numTces %ld, used tces %d\n", + numTces, + (unsigned)(((cur_start_dma + cur_len_dma - 1) >> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1)); + } + + + return num_dma_ents; +} + +/* Call the hypervisor to create the TCE entries. + * return the number of TCEs created + */ +static dma_addr_t create_tces_sg( struct TceTable *tbl, struct scatterlist *sg, + int nents, unsigned numTces, int direction ) +{ + unsigned order, i, j; + unsigned long startPage, endPage, prevEndPage, numPages, uaddr; + long tcenum, starttcenum; + dma_addr_t dmaAddr; + + dmaAddr = NO_TCE; + + order = get_order( numTces << PAGE_SHIFT ); + /* allocate a block of tces */ + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + tcenum += tbl->startOffset; + starttcenum = tcenum; + dmaAddr = tcenum << PAGE_SHIFT; + prevEndPage = 0; + for (j=0; jaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + + uaddr = (unsigned long)sg->address; + + /* If the previous entry ended in the same page that + * the current page starts then they share that + * tce and we reduce the number of tces we need + * by one. This matches assumptions made in + * num_tces_sg and fill_scatterlist_sg + */ + if ( startPage == prevEndPage ) { + --numPages; + uaddr += PAGE_SIZE; + } + + for (i=0; idma_address = pci_map_single( hwdev, sg->address, + sg->length, direction ); + sg->dma_length = sg->length; + return 1; + } + + if ( direction == PCI_DMA_NONE ) + BUG(); + + tbl = get_tce_table(hwdev); + + if ( tbl ) { + /* Compute the number of tces required */ + numTces = num_tces_sg( sg, nents ); + /* Create the tces and get the dma address */ + dma_handle = create_tces_sg( tbl, sg, nents, numTces, direction ); + + /* Fill in the dma scatterlist */ + num_dma = fill_scatterlist_sg( sg, nents, dma_handle, numTces ); + } + + return num_dma; +} + +void pci_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int direction ) +{ + struct TceTable * tbl; + unsigned order, numTces, i; + dma_addr_t dma_end_page, dma_start_page; + + PPCDBG(PPCDBG_TCE, "pci_unmap_sg:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, sg = 0x%16.16lx, direction = 0x%16.16lx, nelms = 0x%16.16lx\n", hwdev, sg, direction, nelms); + + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_start_page = sg->dma_address & PAGE_MASK; + for ( i=nelms; i>0; --i ) { + unsigned k = i - 1; + if ( sg[k].dma_length ) { + dma_end_page = ( sg[k].dma_address + + sg[k].dma_length - 1 ) & PAGE_MASK; + break; + } + } + + numTces = ((dma_end_page - dma_start_page ) >> PAGE_SHIFT) + 1; + order = get_order( numTces << PAGE_SHIFT ); + + if ( order > 10 ) + PPCDBG(PPCDBG_TCE, "pci_unmap_sg: order=%d, numTces=%d, nelms=%d, dma_start_page=%016lx, dma_end_page=%016lx\n", + order, numTces, nelms, (unsigned long)dma_start_page, (unsigned long)dma_end_page ); + + tbl = get_tce_table(hwdev); + + if ( tbl ) + ppc_md.tce_free( tbl, dma_start_page, order, numTces ); + +} +#else +int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, + int direction) +{ + int i; + + for (i = 0; i < nelems; i++) { + unsigned long vaddr = (page_address(sglist->page) + + sglist->offset); + + sglist->dma_address = pci_map_single(pdev, vaddr, + sglist->length, direction); + sglist->dma_length = sglist->length; + sglist++; + } + + return nelems; +} + +void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, + int direction) +{ + while (nelems--) { + pci_unmap_single(pdev, sglist->dma_address, + sglist->dma_length, direction); + sglist++; + } +} +#endif + +/* + * phb_tce_table_init + * + * Function: Display TCE config registers. Could be easily changed + * to initialize the hardware to use TCEs. + */ +unsigned long phb_tce_table_init(struct pci_controller *phb) { + unsigned int r, cfg_rw, i; + unsigned long r64; + phandle node; + + PPCDBG(PPCDBG_TCE, "phb_tce_table_init: start.\n"); + + node = ((struct device_node *)(phb->arch_data))->node; + + PPCDBG(PPCDBG_TCEINIT, "\tphb = 0x%lx\n", phb); + PPCDBG(PPCDBG_TCEINIT, "\tphb->type = 0x%lx\n", phb->type); + PPCDBG(PPCDBG_TCEINIT, "\tphb->phb_regs = 0x%lx\n", phb->phb_regs); + PPCDBG(PPCDBG_TCEINIT, "\tphb->chip_regs = 0x%lx\n", phb->chip_regs); + PPCDBG(PPCDBG_TCEINIT, "\tphb: node = 0x%lx\n", node); + PPCDBG(PPCDBG_TCEINIT, "\tphb->arch_data = 0x%lx\n", phb->arch_data); + + i = 0; + while(of_tce_table[i].node) { + if(of_tce_table[i].node == node) { + if(phb->type == phb_type_python) { + r = *(((unsigned int *)phb->phb_regs) + (0xf10>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR(low) = 0x%x\n", r); + r = *(((unsigned int *)phb->phb_regs) + (0xf00>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR(high) = 0x%x\n", r); + r = *(((unsigned int *)phb->phb_regs) + (0xfd0>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tPHB cfg(rw) = 0x%x\n", r); + break; + } else if(phb->type == phb_type_speedwagon) { + r64 = *(((unsigned long *)phb->chip_regs) + + (0x800>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tNCFG = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x580>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR0 = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x588>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR1 = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x590>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR2 = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x598>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR3 = 0x%lx\n", r64); + cfg_rw = *(((unsigned int *)phb->chip_regs) + + ((0x160 + + (((phb->local_number)+8)<<12))>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tcfg_rw = 0x%x\n", cfg_rw); + } + } + i++; + } + + PPCDBG(PPCDBG_TCEINIT, "phb_tce_table_init: done\n"); + + return(0); +} + +/* These are called very early. */ +void tce_init_pSeries(void) +{ + ppc_md.tce_build = tce_build_pSeries; + ppc_md.tce_free = tce_free_pSeries; +} + +void tce_init_iSeries(void) +{ + ppc_md.tce_build = tce_build_iSeries; + ppc_md.tce_free = tce_free_iSeries; +} diff -Nru a/arch/ppc64/kernel/pci_dn.c b/arch/ppc64/kernel/pci_dn.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/pci_dn.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,385 @@ +/* + * pci_dn.c + * + * Copyright (C) 2001 Todd Inglett, IBM Corporation + * + * PCI manipulation via device_nodes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +/* Traverse_func that inits the PCI fields of the device node. + * NOTE: this *must* be done before read/write config to the device. + */ +static void * __init +update_dn_pci_info(struct device_node *dn, void *data) +{ + struct pci_controller *phb = (struct pci_controller *)data; + u32 *regs; + char *device_type = get_property(dn, "device_type", 0); + + dn->phb = phb; + if (device_type && strcmp(device_type, "pci") == 0 && get_property(dn, "class-code", 0) == 0) { + /* special case for PHB's. Sigh. */ + regs = (u32 *)get_property(dn, "bus-range", 0); + dn->busno = regs[0]; + dn->devfn = 0; /* assumption */ + } else { + regs = (u32 *)get_property(dn, "reg", 0); + if (regs) { + /* First register entry is addr (00BBSS00) */ + dn->busno = (regs[0] >> 16) & 0xff; + dn->devfn = (regs[0] >> 8) & 0xff; + } + } + return NULL; +} + +/* + * Hit all the BARs of all the devices with values from OF. + * This is unnecessary on most systems, but also harmless. + */ +static void * __init +write_OF_bars(struct device_node *dn, void *data) +{ + int i; + u32 oldbar, newbar, newbartest; + u8 config_offset; + char *name = get_property(dn, "name", 0); + char *device_type = get_property(dn, "device_type", 0); + char devname[128]; + sprintf(devname, "%04x:%02x.%x %s (%s)", dn->busno, PCI_SLOT(dn->devfn), PCI_FUNC(dn->devfn), name ? name : "", device_type ? device_type : ""); + + if (device_type && strcmp(device_type, "pci") == 0 && + get_property(dn, "class-code", 0) == 0) + return NULL; /* This is probably a phb. Skip it. */ + + if (dn->n_addrs == 0) + return NULL; /* This is normal for some adapters or bridges */ + + if (dn->addrs == NULL) { + /* This shouldn't happen. */ + printk(KERN_WARNING "write_OF_bars %s: device has %d BARs, but no addrs recorded\n", devname, dn->n_addrs); + return NULL; + } + +#ifndef CONFIG_PPC_ISERIES + for (i = 0; i < dn->n_addrs; i++) { + newbar = dn->addrs[i].address; + config_offset = dn->addrs[i].space & 0xff; + if (ppc_md.pcibios_read_config_dword(dn, config_offset, &oldbar) != PCIBIOS_SUCCESSFUL) { + printk(KERN_WARNING "write_OF_bars %s: read BAR%d failed\n", devname, i); + continue; + } + /* Need to update this BAR. */ + if (ppc_md.pcibios_write_config_dword(dn, config_offset, newbar) != PCIBIOS_SUCCESSFUL) { + printk(KERN_WARNING "write_OF_bars %s: write BAR%d with 0x%08x failed (old was 0x%08x)\n", devname, i, newbar, oldbar); + continue; + } + /* sanity check */ + if (ppc_md.pcibios_read_config_dword(dn, config_offset, &newbartest) != PCIBIOS_SUCCESSFUL) { + printk(KERN_WARNING "write_OF_bars %s: sanity test read BAR%d failed?\n", devname, i); + continue; + } + if ((newbar & PCI_BASE_ADDRESS_MEM_MASK) != (newbartest & PCI_BASE_ADDRESS_MEM_MASK)) { + printk(KERN_WARNING "write_OF_bars %s: oops...BAR%d read back as 0x%08x%s!\n", devname, i, newbartest, (oldbar & PCI_BASE_ADDRESS_MEM_MASK) == (newbartest & PCI_BASE_ADDRESS_MEM_MASK) ? " (original value)" : ""); + continue; + } + } +#endif + return NULL; +} + +#if 0 +/* Traverse_func that starts the BIST (self test) */ +static void * __init +startBIST(struct device_node *dn, void *data) +{ + struct pci_controller *phb = (struct pci_controller *)data; + u8 bist; + + char *name = get_property(dn, "name", 0); + udbg_printf("startBIST: %s phb=%p, device=%p\n", name ? name : "", phb, dn); + + if (ppc_md.pcibios_read_config_byte(dn, PCI_BIST, &bist) == PCIBIOS_SUCCESSFUL) { + if (bist & PCI_BIST_CAPABLE) { + udbg_printf(" -> is BIST capable!\n", phb, dn); + /* Start bist here */ + } + } + return NULL; +} +#endif + + +/****************************************************************** + * Traverse a device tree stopping each PCI device in the tree. + * This is done depth first. As each node is processed, a "pre" + * function is called, the children are processed recursively, and + * then a "post" function is called. + * + * The "pre" and "post" funcs return a value. If non-zero + * is returned from the "pre" func, the traversal stops and this + * value is returned. The return value from "post" is not used. + * This return value is useful when using traverse as + * a method of finding a device. + * + * NOTE: we do not run the funcs for devices that do not appear to + * be PCI except for the start node which we assume (this is good + * because the start node is often a phb which may be missing PCI + * properties). + * We use the class-code as an indicator. If we run into + * one of these nodes we also assume its siblings are non-pci for + * performance. + * + ******************************************************************/ +void *traverse_pci_devices(struct device_node *start, traverse_func pre, traverse_func post, void *data) +{ + struct device_node *dn, *nextdn; + void *ret; + + if (pre && (ret = pre(start, data)) != NULL) + return ret; + for (dn = start->child; dn; dn = nextdn) { + nextdn = NULL; + if (get_property(dn, "class-code", 0)) { + if (pre && (ret = pre(dn, data)) != NULL) + return ret; + if (dn->child) { + /* Depth first...do children */ + nextdn = dn->child; + } else if (dn->sibling) { + /* ok, try next sibling instead. */ + nextdn = dn->sibling; + } else { + /* no more children or siblings...call "post" */ + if (post) + post(dn, data); + } + } + if (!nextdn) { + /* Walk up to next valid sibling. */ + do { + dn = dn->parent; + if (dn == start) + return NULL; + } while (dn->sibling == NULL); + nextdn = dn->sibling; + } + } + return NULL; +} + +/* Same as traverse_pci_devices except this does it for all phbs. + */ +void *traverse_all_pci_devices(traverse_func pre) +{ + struct pci_controller* phb; + void *ret; + for (phb=hose_head;phb;phb=phb->next) + if ((ret = traverse_pci_devices((struct device_node *)phb->arch_data, pre, NULL, phb)) != NULL) + return ret; + return NULL; +} + + +/* Traversal func that looks for a value. + * If found, the device_node is returned (thus terminating the traversal). + */ +static void * +is_devfn_node(struct device_node *dn, void *data) +{ + int busno = ((unsigned long)data >> 8) & 0xff; + int devfn = ((unsigned long)data) & 0xff; + return (devfn == dn->devfn && busno == dn->busno) ? dn : NULL; +} + +/* Same as is_devfn_node except ignore the "fn" part of the "devfn". + */ +static void * +is_devfn_sub_node(struct device_node *dn, void *data) +{ + int busno = ((unsigned long)data >> 8) & 0xff; + int devfn = ((unsigned long)data) & 0xf8; + return (devfn == (dn->devfn & 0xf8) && busno == dn->busno) ? dn : NULL; +} + +/* Given an existing EADs (pci bridge) device node create a fake one + * that will simulate function zero. Make it a sibling of other_eads. + */ +static struct device_node * +create_eads_node(struct device_node *other_eads) +{ + struct device_node *eads = (struct device_node *)kmalloc(sizeof(struct device_node), GFP_KERNEL); + + if (!eads) return NULL; /* huh? */ + *eads = *other_eads; + eads->devfn &= ~7; /* make it function zero */ + eads->tce_table = NULL; + /* NOTE: share properties. We could copy but for now this should suffice. + * The full_name is also incorrect...but seems harmless. + */ + eads->child = NULL; + eads->next = NULL; + other_eads->allnext = eads; + other_eads->sibling = eads; + return eads; +} + +/* This is the "slow" path for looking up a device_node from a + * pci_dev. It will hunt for the device under it's parent's + * phb and then update sysdata for a future fastpath. + * + * It may also do fixups on the actual device since this happens + * on the first read/write. + * + * Note that it also must deal with devices that don't exist. + * In this case it may probe for real hardware ("just in case") + * and add a device_node to the device tree if necessary. + * + */ +struct device_node *fetch_dev_dn(struct pci_dev *dev) +{ + struct device_node *orig_dn = (struct device_node *)dev->sysdata; + struct pci_controller *phb = orig_dn->phb; /* assume same phb as orig_dn */ + struct device_node *phb_dn; + struct device_node *dn; + unsigned long searchval = (dev->bus->number << 8) | dev->devfn; + + phb_dn = (struct device_node *)(phb->arch_data); + dn = (struct device_node *)traverse_pci_devices(phb_dn, is_devfn_node, NULL, (void *)searchval); + if (dn) { + dev->sysdata = dn; + /* ToDo: call some device init hook here */ + } else { + /* Now it is very possible that we can't find the device because it is + * not the zero'th device of a mutifunction device and we don't have + * permission to read the zero'th device. If this is the case, Linux + * would ordinarily skip all the other functions. + */ + if ((searchval & 0x7) == 0) { + struct device_node *thisdevdn; + /* Ok, we are looking for fn == 0. Let's check for other functions. */ + thisdevdn = (struct device_node *)traverse_pci_devices(phb_dn, is_devfn_sub_node, NULL, (void *)searchval); + if (thisdevdn) { + /* Ah ha! There does exist a sub function. Now this isn't an exact + * match for searchval, but in order to get Linux to believe the sub + * functions exist we will need to manufacture a fake device_node + * for this zero'th function. To keept this simple for now we only + * handle pci bridges and we just hand back the found node which + * isn't correct, but Linux won't care. + */ + char *device_type = (char *)get_property(thisdevdn, "device_type", 0); + if (device_type && strcmp(device_type, "pci") == 0) { + return create_eads_node(thisdevdn); + } + } + } + /* ToDo: device not found...probe for it anyway with a fake dn? + struct device_node fake_dn; + memset(&fake_dn, 0, sizeof(fake_dn)); + fake_dn.phb = phb; + fake_dn.busno = dev->bus->number; + fake_dn.devfn = dev->devfn; + ... now do ppc_md.pcibios_read_config_dword(&fake_dn.....) + ... if ok, alloc a real device_node and dn = real_dn; + */ + } + return dn; +} + + +/****************************************************************** + * Actually initialize the phbs. + * The buswalk on this phb has not happened yet. + ******************************************************************/ +void __init +pci_devs_phb_init(void) +{ + /* This must be done first so the device nodes have valid pci info! */ + traverse_all_pci_devices(update_dn_pci_info); + + /* Hack for regatta which does not init the bars correctly */ + traverse_all_pci_devices(write_OF_bars); +#if 0 + traverse_all_pci_devices(startBIST); + mdelay(5000); + traverse_all_pci_devices(checkBIST); +#endif +} + + +static void __init +pci_fixup_bus_sysdata_list(struct list_head *bus_list) +{ + struct list_head *ln; + struct pci_bus *bus; + struct pci_controller *phb; + int newnum; + + for (ln=bus_list->next; ln != bus_list; ln=ln->next) { + bus = pci_bus_b(ln); + if (bus->self) { + bus->sysdata = bus->self->sysdata; + /* Also fixup the bus number on large bus systems to + * include the PHB# in the next byte + */ + phb = PCI_GET_DN(bus)->phb; + if (phb && phb->buid) { + newnum = (phb->global_number << 8) | bus->number; + bus->number = newnum; + sprintf(bus->name, "PCI Bus #%x", bus->number); + } + } + pci_fixup_bus_sysdata_list(&bus->children); + } +} + + +/****************************************************************** + * Fixup the bus->sysdata ptrs to point to the bus' device_node. + * This is done late in pcibios_init(). We do this mostly for + * sanity, but pci_dma.c uses these at DMA time so they must be + * correct. + * To do this we recurse down the bus hierarchy. Note that PHB's + * have bus->self == NULL, but fortunately bus->sysdata is already + * correct in this case. + ******************************************************************/ +void __init +pci_fix_bus_sysdata(void) +{ + pci_fixup_bus_sysdata_list(&pci_root_buses); +} diff -Nru a/arch/ppc64/kernel/pmac_nvram.c b/arch/ppc64/kernel/pmac_nvram.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/pmac_nvram.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,358 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * 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. + * + * Miscellaneous procedures for dealing with the PowerMac hardware. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +/* + * Read and write the non-volatile RAM on PowerMacs and CHRP machines. + */ +static int nvram_naddrs; +static volatile unsigned char *nvram_addr; +static volatile unsigned char *nvram_data; +static int nvram_mult, is_core_99; +static char* nvram_image; +static int core99_bank = 0; +sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN; + +#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ + +#define CORE99_SIGNATURE 0x5a +#define CORE99_ADLER_START 0x14 + +/* Core99 nvram is a flash */ +#define CORE99_FLASH_STATUS_DONE 0x80 +#define CORE99_FLASH_STATUS_ERR 0x38 +#define CORE99_FLASH_CMD_ERASE_CONFIRM 0xd0 +#define CORE99_FLASH_CMD_ERASE_SETUP 0x20 +#define CORE99_FLASH_CMD_RESET 0xff +#define CORE99_FLASH_CMD_WRITE_SETUP 0x40 + +/* CHRP NVRAM header */ +struct chrp_header { + u8 signature; + u8 cksum; + u16 len; + char name[12]; + u8 data[0]; +}; + +struct core99_header { + struct chrp_header hdr; + u32 adler; + u32 generation; + u32 reserved[2]; +}; + +static int nvram_partitions[3]; + +static u8 +chrp_checksum(struct chrp_header* hdr) +{ + u8 *ptr; + u16 sum = hdr->signature; + for (ptr = (u8 *)&hdr->len; ptr < hdr->data; ptr++) + sum += *ptr; + while (sum > 0xFF) + sum = (sum & 0xFF) + (sum>>8); + return sum; +} + +static u32 +core99_calc_adler(u8 *buffer) +{ + int cnt; + u32 low, high; + + buffer += CORE99_ADLER_START; + low = 1; + high = 0; + for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) { + if ((cnt % 5000) == 0) { + high %= 65521UL; + high %= 65521UL; + } + low += buffer[cnt]; + high += low; + } + low %= 65521UL; + high %= 65521UL; + + return (high << 16) | low; +} + +static u32 +core99_check(u8* datas) +{ + struct core99_header* hdr99 = (struct core99_header*)datas; + + if (hdr99->hdr.signature != CORE99_SIGNATURE) { +#ifdef DEBUG + printk("Invalid signature\n"); +#endif + return 0; + } + if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) { +#ifdef DEBUG + printk("Invalid checksum\n"); +#endif + return 0; + } + if (hdr99->adler != core99_calc_adler(datas)) { +#ifdef DEBUG + printk("Invalid adler\n"); +#endif + return 0; + } + return hdr99->generation; +} + +static int +core99_erase_bank(int bank) +{ + int stat, i; + + u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; + + out_8(base, CORE99_FLASH_CMD_ERASE_SETUP); + out_8(base, CORE99_FLASH_CMD_ERASE_CONFIRM); + do { stat = in_8(base); } + while(!(stat & CORE99_FLASH_STATUS_DONE)); + out_8(base, CORE99_FLASH_CMD_RESET); + if (stat & CORE99_FLASH_STATUS_ERR) { + printk("nvram: flash error 0x%02x on erase !\n", stat); + return -ENXIO; + } + for (i=0; in_addrs; + is_core_99 = device_is_compatible(dp, "nvram,flash"); + if (is_core_99) { + int i; + u32 gen_bank0, gen_bank1; + + if (nvram_naddrs < 1) { + printk(KERN_ERR "nvram: no address\n"); + return; + } + nvram_image = kmalloc(NVRAM_SIZE, GFP_KERNEL); + if (!nvram_image) { + printk(KERN_ERR "nvram: can't allocate image\n"); + return; + } + nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2); +#ifdef DEBUG + printk("nvram: Checking bank 0...\n"); +#endif + gen_bank0 = core99_check((u8 *)nvram_data); + gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE); + core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0; +#ifdef DEBUG + printk("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); + printk("nvram: Active bank is: %d\n", core99_bank); +#endif + for (i=0; iaddrs[0].address, dp->addrs[0].size); + nvram_mult = 1; + } else if (nvram_naddrs == 1) { + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE; + } else if (nvram_naddrs == 2) { + nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + } else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) { + nvram_naddrs = -1; + } else { + printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", + nvram_naddrs); + } +} + +void +pmac_nvram_update(void) +{ + struct core99_header* hdr99; + + if (!is_core_99 || !nvram_data || !nvram_image) + return; + if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE, + NVRAM_SIZE)) + return; +#ifdef DEBUG + printk("Updating nvram...\n"); +#endif + hdr99 = (struct core99_header*)nvram_image; + hdr99->generation++; + hdr99->hdr.signature = CORE99_SIGNATURE; + hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); + hdr99->adler = core99_calc_adler(nvram_image); + core99_bank = core99_bank ? 0 : 1; + if (core99_erase_bank(core99_bank)) { + printk("nvram: Error erasing bank %d\n", core99_bank); + return; + } + if (core99_write_bank(core99_bank, nvram_image)) + printk("nvram: Error writing bank %d\n", core99_bank); +} + +__openfirmware +unsigned char nvram_read_byte(int addr) +{ + #ifdef CONFIG_ADB_PMU // -aglitke + struct adb_request req; + #endif + + switch (nvram_naddrs) { +#ifdef CONFIG_ADB_PMU + case -1: + if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM, + (addr >> 8) & 0xff, addr & 0xff)) + break; + while (!req.complete) + pmu_poll(); + return req.reply[1]; +#endif + case 1: + if (is_core_99) + return nvram_image[addr]; + return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; + case 2: + *nvram_addr = addr >> 5; + eieio(); + return nvram_data[(addr & 0x1f) << 4]; + } + return 0; +} + +__openfirmware +void nvram_write_byte(unsigned char val, int addr) +{ + #ifdef CONFIG_ADB_PMU // -aglitke + struct adb_request req; + #endif + switch (nvram_naddrs) { +#ifdef CONFIG_ADB_PMU + case -1: + if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM, + (addr >> 8) & 0xff, addr & 0xff, val)) + break; + while (!req.complete) + pmu_poll(); + break; +#endif + case 1: + if (is_core_99) { + nvram_image[addr] = val; + break; + } + nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; + break; + case 2: + *nvram_addr = addr >> 5; + eieio(); + nvram_data[(addr & 0x1f) << 4] = val; + break; + } + eieio(); +} + +int +pmac_get_partition(int partition) +{ + return nvram_partitions[partition]; +} + +u8 +pmac_xpram_read(int xpaddr) +{ + int offset = nvram_partitions[pmac_nvram_XPRAM]; + + if (offset < 0) + return 0; + + return nvram_read_byte(xpaddr + offset); +} + +void +pmac_xpram_write(int xpaddr, u8 data) +{ + int offset = nvram_partitions[pmac_nvram_XPRAM]; + + if (offset < 0) + return; + + nvram_write_byte(xpaddr + offset, data); +} + + diff -Nru a/arch/ppc64/kernel/pmc.c b/arch/ppc64/kernel/pmc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/pmc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,167 @@ +/* + * pmc.c + * Copyright (C) 2001 Dave Engebretsen & Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Change Activity: + * 2001/06/05 : engebret : Created. + * End Change Activity + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern struct Naca *naca; + +struct _pmc_sw pmc_sw_system = { + 0 +}; + +struct _pmc_sw pmc_sw_cpu[NR_CPUS] = { + {0 }, +}; + +/* + * Provide enough storage for either system level counters or + * one cpu's counters. + */ +struct _pmc_sw_text pmc_sw_text; +struct _pmc_hw_text pmc_hw_text; + +char * +ppc64_pmc_stab(int file) +{ + int n; + unsigned long stab_faults, stab_capacity_castouts, stab_invalidations; + unsigned long i; + + stab_faults = stab_capacity_castouts = stab_invalidations = n = 0; + + if (file == -1) { + for (i = 0; i < smp_num_cpus; i++) { + stab_faults += pmc_sw_cpu[i].stab_faults; + stab_capacity_castouts += pmc_sw_cpu[i].stab_capacity_castouts; + stab_invalidations += pmc_sw_cpu[i].stab_invalidations; + } + n += sprintf(pmc_sw_text.buffer + n, + "Faults 0x%lx\n", stab_faults); + n += sprintf(pmc_sw_text.buffer + n, + "Castouts 0x%lx\n", stab_capacity_castouts); + n += sprintf(pmc_sw_text.buffer + n, + "Invalidations 0x%lx\n", stab_invalidations); + } else { + n += sprintf(pmc_sw_text.buffer + n, + "Faults 0x%lx\n", + pmc_sw_cpu[file].stab_faults); + + n += sprintf(pmc_sw_text.buffer + n, + "Castouts 0x%lx\n", + pmc_sw_cpu[file].stab_capacity_castouts); + + n += sprintf(pmc_sw_text.buffer + n, + "Invalidations 0x%lx\n", + pmc_sw_cpu[file].stab_invalidations); + + for (i = 0; i < STAB_ENTRY_MAX; i++) { + if (pmc_sw_cpu[file].stab_entry_use[i]) { + n += sprintf(pmc_sw_text.buffer + n, + "Entry %02ld 0x%lx\n", i, + pmc_sw_cpu[file].stab_entry_use[i]); + } + } + + } + + return(pmc_sw_text.buffer); +} + +char * +ppc64_pmc_htab(int file) +{ + int n; + unsigned long htab_primary_overflows, htab_capacity_castouts; + unsigned long htab_read_to_write_faults; + + htab_primary_overflows = htab_capacity_castouts = 0; + htab_read_to_write_faults = n = 0; + + if (file == -1) { + n += sprintf(pmc_sw_text.buffer + n, + "Primary Overflows 0x%lx\n", + pmc_sw_system.htab_primary_overflows); + n += sprintf(pmc_sw_text.buffer + n, + "Castouts 0x%lx\n", + pmc_sw_system.htab_capacity_castouts); + } else { + n += sprintf(pmc_sw_text.buffer + n, + "Primary Overflows N/A\n"); + + n += sprintf(pmc_sw_text.buffer + n, + "Castouts N/A\n\n"); + + } + + return(pmc_sw_text.buffer); +} + +char * +ppc64_pmc_hw(int file) +{ + int n; + + n = 0; + if (file == -1) { + n += sprintf(pmc_hw_text.buffer + n, "Not Implemented\n"); + } else { + n += sprintf(pmc_hw_text.buffer + n, + "MMCR0 0x%lx\n", mfspr(MMCR0)); + n += sprintf(pmc_hw_text.buffer + n, + "MMCR1 0x%lx\n", mfspr(MMCR1)); +#if 0 + n += sprintf(pmc_hw_text.buffer + n, + "MMCRA 0x%lx\n", mfspr(MMCRA)); +#endif + + n += sprintf(pmc_hw_text.buffer + n, + "PMC1 0x%lx\n", mfspr(PMC1)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC2 0x%lx\n", mfspr(PMC2)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC3 0x%lx\n", mfspr(PMC3)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC4 0x%lx\n", mfspr(PMC4)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC5 0x%lx\n", mfspr(PMC5)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC6 0x%lx\n", mfspr(PMC6)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC7 0x%lx\n", mfspr(PMC7)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC8 0x%lx\n", mfspr(PMC8)); + } + + return(pmc_hw_text.buffer); +} diff -Nru a/arch/ppc64/kernel/ppc-stub.c b/arch/ppc64/kernel/ppc-stub.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/ppc-stub.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,739 @@ +/* + * ppc-stub.c: KGDB support for the Linux kernel. + * + * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC + * some stuff borrowed from Paul Mackerras' xmon + * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu) + * + * Modifications to run under Linux + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * This file originally came from the gdb sources, and the + * copyright notices have been retained below. + */ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or its performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void breakinst(void); + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +static int initialized = 0; +static int kgdb_active = 0; +static int kgdb_started = 0; +static u_int fault_jmp_buf[100]; +static int kdebug; + +static const char hexchars[]="0123456789abcdef"; + +/* Place where we save old trap entries for restoration - sparc*/ +/* struct tt_entry kgdb_savettable[256]; */ +/* typedef void (*trapfunc_t)(void); */ + +#if 0 +/* Install an exception handler for kgdb */ +static void exceptionHandler(int tnum, unsigned int *tfunc) +{ + /* We are dorking with a live trap table, all irqs off */ +} +#endif + +int +kgdb_setjmp(long *buf) +{ + asm ("mflr 0; stw 0,0(%0);" + "stw 1,4(%0); stw 2,8(%0);" + "mfcr 0; stw 0,12(%0);" + "stmw 13,16(%0)" + : : "r" (buf)); + /* XXX should save fp regs as well */ + return 0; +} +void +kgdb_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm ("lmw 13,16(%0);" + "lwz 0,12(%0); mtcrf 0x38,0;" + "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" + "mtlr 0; mr 3,%1" + : : "r" (buf), "r" (val)); +} +/* Convert ch from a hex digit to an int */ +static int +hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + */ +static unsigned char * +mem2hex(char *mem, char *buf, int count) +{ + unsigned char ch; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + while (count-- > 0) { + ch = *mem++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + } else { + /* error condition */ + } + debugger_fault_handler = 0; + *buf = 0; + return buf; +} + +/* convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written. +*/ +static char * +hex2mem(char *buf, char *mem, int count) +{ + int i; + unsigned char ch; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + for (i=0; i# */ +static void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* wait around for the start character, ignore all other + * characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* send the packet in buffer. */ +static void putpacket(unsigned char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch, recv; + + /* $#. */ + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + recv = getDebugChar(); + } while ((recv & 0x7f) != '+'); +} + +static void kgdb_flush_cache_all(void) +{ + flush_instruction_cache(); +} + + +/* Set up exception handlers for tracing and breakpoints + * [could be called kgdb_init()] + */ +void set_debug_traps(void) +{ +#if 0 + unsigned char c; + + save_and_cli(flags); + + /* In case GDB is started before us, ack any packets (presumably + * "$?#xx") sitting there. + * + * I've found this code causes more problems than it solves, + * so that's why it's commented out. GDB seems to work fine + * now starting either before or after the kernel -bwb + */ + + while((c = getDebugChar()) != '$'); + while((c = getDebugChar()) != '#'); + c = getDebugChar(); /* eat first csum byte */ + c = getDebugChar(); /* eat second csum byte */ + putDebugChar('+'); /* ack it */ +#endif + debugger = kgdb; + debugger_bpt = kgdb_bpt; + debugger_sstep = kgdb_sstep; + debugger_iabr_match = kgdb_iabr_match; + debugger_dabr_match = kgdb_dabr_match; + + initialized = 1; +} + +static void kgdb_fault_handler(struct pt_regs *regs) +{ + kgdb_longjmp((long*)fault_jmp_buf, 1); +} + +int kgdb_bpt(struct pt_regs *regs) +{ + handle_exception(regs); + return 1; +} + +int kgdb_sstep(struct pt_regs *regs) +{ + handle_exception(regs); + return 1; +} + +void kgdb(struct pt_regs *regs) +{ + handle_exception(regs); +} + +int kgdb_iabr_match(struct pt_regs *regs) +{ + printk("kgdb doesn't support iabr, what?!?\n"); + handle_exception(regs); + return 1; +} + +int kgdb_dabr_match(struct pt_regs *regs) +{ + printk("kgdb doesn't support dabr, what?!?\n"); + handle_exception(regs); + return 1; +} + +/* Convert the SPARC hardware trap type code to a unix signal number. */ +/* + * This table contains the mapping between PowerPC hardware trap types, and + * signals, which are primarily what GDB understands. + */ +static struct hard_trap_info +{ + unsigned int tt; /* Trap type code for powerpc */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 0x200, SIGSEGV }, /* machine check */ + { 0x300, SIGSEGV }, /* address error (store) */ + { 0x400, SIGBUS }, /* instruction bus error */ + { 0x500, SIGINT }, /* interrupt */ + { 0x600, SIGBUS }, /* alingment */ + { 0x700, SIGTRAP }, /* breakpoint trap */ + { 0x800, SIGFPE }, /* fpu unavail */ + { 0x900, SIGALRM }, /* decrementer */ + { 0xa00, SIGILL }, /* reserved */ + { 0xb00, SIGILL }, /* reserved */ + { 0xc00, SIGCHLD }, /* syscall */ + { 0xd00, SIGTRAP }, /* single-step/watch */ + { 0xe00, SIGFPE }, /* fp assist */ + { 0, 0} /* Must be last */ +}; + +static int computeSignal(unsigned int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +#define PC_REGNUM 64 +#define SP_REGNUM 1 + +/* + * This function does all command processing for interfacing to gdb. + */ +static void +handle_exception (struct pt_regs *regs) +{ + int sigval; + int addr; + int length; + char *ptr; + unsigned long msr; + + if (debugger_fault_handler) { + debugger_fault_handler(regs); + panic("kgdb longjump failed!\n"); + } + if (kgdb_active) { + printk("interrupt while in kgdb, returning\n"); + return; + } + kgdb_active = 1; + kgdb_started = 1; + +#ifdef KGDB_DEBUG + printk("kgdb: entering handle_exception; trap [0x%x]\n", + (unsigned int)regs->trap); +#endif + + kgdb_interruptible(0); + lock_kernel(); + msr = get_msr(); + set_msr(msr & ~MSR_EE); /* disable interrupts */ + + if (regs->nip == (unsigned long)breakinst) { + /* Skip over breakpoint trap insn */ + regs->nip += 4; + } + + /* reply to host that an exception has occurred */ + sigval = computeSignal(regs->trap); + ptr = remcomOutBuffer; + +#if 0 + *ptr++ = 'S'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; +#else + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + *ptr++ = hexchars[PC_REGNUM >> 4]; + *ptr++ = hexchars[PC_REGNUM & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->nip, ptr, 4); + *ptr++ = ';'; + *ptr++ = hexchars[SP_REGNUM >> 4]; + *ptr++ = hexchars[SP_REGNUM & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(((char *)®s) + SP_REGNUM*4, ptr, 4); + *ptr++ = ';'; +#endif + + *ptr++ = 0; + + putpacket(remcomOutBuffer); + + /* XXX We may want to add some features dealing with poking the + * XXX page tables, ... (look at sparc-stub.c for more info) + * XXX also required hacking to the gdb sources directly... + */ + + while (1) { + remcomOutBuffer[0] = 0; + + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': /* report most recent signal */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[sigval >> 4]; + remcomOutBuffer[2] = hexchars[sigval & 0xf]; + remcomOutBuffer[3] = 0; + break; +#if 0 + case 'q': /* this screws up gdb for some reason...*/ + { + extern long _start, sdata, __bss_start; + + ptr = &remcomInBuffer[1]; + if (strncmp(ptr, "Offsets", 7) != 0) + break; + + ptr = remcomOutBuffer; + sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x", + &_start, &sdata, &__bss_start); + break; + } +#endif + case 'd': + /* toggle debug flag */ + kdebug ^= 1; + break; + + case 'g': /* return the value of the CPU registers. + * some of them are non-PowerPC names :( + * they are stored in gdb like: + * struct { + * u32 gpr[32]; + * f64 fpr[32]; + * u32 pc, ps, cnd, lr; (ps=msr) + * u32 cnt, xer, mq; + * } + */ + { + int i; + ptr = remcomOutBuffer; + /* General Purpose Regs */ + ptr = mem2hex((char *)regs, ptr, 32 * 4); + /* Floating Point Regs - FIXME */ + /*ptr = mem2hex((char *), ptr, 32 * 8);*/ + for(i=0; i<(32*8*2); i++) { /* 2chars/byte */ + ptr[i] = '0'; + } + ptr += 32*8*2; + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = mem2hex((char *)®s->nip, ptr, 4); + ptr = mem2hex((char *)®s->msr, ptr, 4); + ptr = mem2hex((char *)®s->ccr, ptr, 4); + ptr = mem2hex((char *)®s->link, ptr, 4); + ptr = mem2hex((char *)®s->ctr, ptr, 4); + ptr = mem2hex((char *)®s->xer, ptr, 4); + } + break; + + case 'G': /* set the value of the CPU registers */ + { + ptr = &remcomInBuffer[1]; + + /* + * If the stack pointer has moved, you should pray. + * (cause only god can help you). + */ + + /* General Purpose Regs */ + hex2mem(ptr, (char *)regs, 32 * 4); + + /* Floating Point Regs - FIXME?? */ + /*ptr = hex2mem(ptr, ??, 32 * 8);*/ + ptr += 32*8*2; + + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = hex2mem(ptr, (char *)®s->nip, 4); + ptr = hex2mem(ptr, (char *)®s->msr, 4); + ptr = hex2mem(ptr, (char *)®s->ccr, 4); + ptr = hex2mem(ptr, (char *)®s->link, 4); + ptr = hex2mem(ptr, (char *)®s->ctr, 4); + ptr = hex2mem(ptr, (char *)®s->xer, 4); + + strcpy(remcomOutBuffer,"OK"); + } + break; + case 'H': + /* don't do anything, yet, just acknowledge */ + hexToInt(&ptr, &addr); + strcpy(remcomOutBuffer,"OK"); + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, remcomOutBuffer,length)) + break; + strcpy (remcomOutBuffer, "E03"); + } else { + strcpy(remcomOutBuffer,"E01"); + } + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length)) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E03"); + } + flush_icache_range(addr, addr+length); + } else { + strcpy(remcomOutBuffer, "E02"); + } + break; + + + case 'k': /* kill the program, actually just continue */ + case 'c': /* cAA..AA Continue; address AA..AA optional */ + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) { + regs->nip = addr; + } + +/* Need to flush the instruction cache here, as we may have deposited a + * breakpoint, and the icache probably has no way of knowing that a data ref to + * some location may have changed something that is in the instruction cache. + */ + kgdb_flush_cache_all(); + set_msr(msr); + kgdb_interruptible(1); + unlock_kernel(); + kgdb_active = 0; + return; + + case 's': + kgdb_flush_cache_all(); + regs->msr |= MSR_SE; +#if 0 + set_msr(msr | MSR_SE); +#endif + unlock_kernel(); + kgdb_active = 0; + return; + + case 'r': /* Reset (if user process..exit ???)*/ + panic("kgdb reset."); + break; + } /* switch */ + if (remcomOutBuffer[0] && kdebug) { + printk("remcomInBuffer: %s\n", remcomInBuffer); + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + } + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1) */ +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ + +void +breakpoint(void) +{ + if (!initialized) { + printk("breakpoint() called b4 kgdb init\n"); + return; + } + + asm(" .globl breakinst + breakinst: .long 0x7d821008 + "); +} + +/* Output string in GDB O-packet format if GDB has connected. If nothing + output, returns 0 (caller must then handle output). */ +int +kgdb_output_string (const char* s, unsigned int count) +{ + char buffer[512]; + + if (!kgdb_started) + return 0; + + count = (count <= (sizeof(buffer) / 2 - 2)) + ? count : (sizeof(buffer) / 2 - 2); + + buffer[0] = 'O'; + mem2hex (s, &buffer[1], count); + putpacket(buffer); + + return 1; + } diff -Nru a/arch/ppc64/kernel/ppc_asm.h b/arch/ppc64/kernel/ppc_asm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/ppc_asm.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,117 @@ +/* + * arch/ppc/kernel/ppc_asm.h + * + * Definitions used by various bits of low-level assembly code on PowerPC. + * + * Copyright (C) 1995-1999 Gary Thomas, Paul Mackerras, Cort Dougan. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +#include "ppc_asm.tmpl" +#include "ppc_defs.h" + +/* + * Macros for storing registers into and loading registers from + * exception frames. + */ +#define SAVE_GPR(n, base) std n,GPR0+8*(n)(base) +#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) +#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) +#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) +#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) +#define REST_GPR(n, base) ld n,GPR0+8*(n)(base) +#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) +#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) +#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) +#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) + +#define SAVE_FPR(n, base) stfd n,THREAD_FPR0+8*(n)(base) +#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) +#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) +#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) +#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) +#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) +#define REST_FPR(n, base) lfd n,THREAD_FPR0+8*(n)(base) +#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) +#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) +#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) +#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) +#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) + +#define CHECKANYINT(ra,rb) \ + mfspr rb,SPRG3; /* Get Paca address */\ + ld ra,PACALPPACA+LPPACAANYINT(rb); /* Get pending interrupt flags */\ + cmpldi 0,ra,0; + +/* Macros to adjust thread priority for Iseries hardware multithreading */ +#define HMT_LOW or 1,1,1 +#define HMT_MEDIUM or 2,2,2 +#define HMT_HIGH or 3,3,3 + +/* Insert the high 32 bits of the MSR into what will be the new + MSR (via SRR1 and rfid) This preserves the MSR.SF and MSR.ISF + bits. */ + +#define FIX_SRR1(ra, rb) \ + mr rb,ra; \ + mfmsr ra; \ + rldimi ra,rb,0,32 + +#define CLR_TOP32(r) rlwinm (r),(r),0,0,31 /* clear top 32 bits */ + +/* + * LOADADDR( rn, name ) + * loads the address of 'name' into 'rn' + * + * LOADBASE( rn, name ) + * loads the address (less the low 16 bits) of 'name' into 'rn' + * suitable for base+disp addressing + */ +#define LOADADDR(rn,name) \ + lis rn,name##@highest; \ + ori rn,rn,name##@higher; \ + rldicr rn,rn,32,31; \ + oris rn,rn,name##@h; \ + ori rn,rn,name##@l + +#define LOADBASE(rn,name) \ + lis rn,name@highest; \ + ori rn,rn,name@higher; \ + rldicr rn,rn,32,31; \ + oris rn,rn,name@ha + + +#define SET_REG_TO_CONST(reg, value) \ + lis reg,(((value)>>48)&0xFFFF); \ + ori reg,reg,(((value)>>32)&0xFFFF); \ + rldicr reg,reg,32,31; \ + oris reg,reg,(((value)>>16)&0xFFFF); \ + ori reg,reg,((value)&0xFFFF); + +#define SET_REG_TO_LABEL(reg, label) \ + lis reg,(label)@highest; \ + ori reg,reg,(label)@higher; \ + rldicr reg,reg,32,31; \ + oris reg,reg,(label)@h; \ + ori reg,reg,(label)@l; + + +/* PPPBBB - DRENG If KERNELBASE is always 0xC0..., + * Then we can easily do this with one asm insn. -Peter + */ +#define tophys(rd,rs) \ + lis rd,((KERNELBASE>>48)&0xFFFF); \ + rldicr rd,rd,32,31; \ + sub rd,rs,rd + +#define tovirt(rd,rs) \ + lis rd,((KERNELBASE>>48)&0xFFFF); \ + rldicr rd,rd,32,31; \ + add rd,rs,rd + diff -Nru a/arch/ppc64/kernel/ppc_asm.tmpl b/arch/ppc64/kernel/ppc_asm.tmpl --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/ppc_asm.tmpl Tue Feb 19 18:09:00 2002 @@ -0,0 +1,115 @@ +/* Condition Register Bit Fields */ + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + + +/* General Purpose Registers (GPRs) */ + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + + +/* Floating Point Registers (FPRs) */ + +#define fr0 0 +#define fr1 1 +#define fr2 2 +#define fr3 3 +#define fr4 4 +#define fr5 5 +#define fr6 6 +#define fr7 7 +#define fr8 8 +#define fr9 9 +#define fr10 10 +#define fr11 11 +#define fr12 12 +#define fr13 13 +#define fr14 14 +#define fr15 15 +#define fr16 16 +#define fr17 17 +#define fr18 18 +#define fr19 19 +#define fr20 20 +#define fr21 21 +#define fr22 22 +#define fr23 23 +#define fr24 24 +#define fr25 25 +#define fr26 26 +#define fr27 27 +#define fr28 28 +#define fr29 29 +#define fr30 30 +#define fr31 31 + +#define vr0 0 +#define vr1 1 +#define vr2 2 +#define vr3 3 +#define vr4 4 +#define vr5 5 +#define vr6 6 +#define vr7 7 +#define vr8 8 +#define vr9 9 +#define vr10 10 +#define vr11 11 +#define vr12 12 +#define vr13 13 +#define vr14 14 +#define vr15 15 +#define vr16 16 +#define vr17 17 +#define vr18 18 +#define vr19 19 +#define vr20 20 +#define vr21 21 +#define vr22 22 +#define vr23 23 +#define vr24 24 +#define vr25 25 +#define vr26 26 +#define vr27 27 +#define vr28 28 +#define vr29 29 +#define vr30 30 +#define vr31 31 diff -Nru a/arch/ppc64/kernel/ppc_defs.head b/arch/ppc64/kernel/ppc_defs.head --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/ppc_defs.head Tue Feb 19 18:09:00 2002 @@ -0,0 +1,3 @@ +/* + * WARNING! This file is automatically generated - DO NOT EDIT! + */ diff -Nru a/arch/ppc64/kernel/ppc_ksyms.c b/arch/ppc64/kernel/ppc_ksyms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/ppc_ksyms.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,290 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#if 0 +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SMP +#include +#endif /* CONFIG_SMP */ +#ifdef CONFIG_PPC_ISERIES +#include +#include +#endif + +/* Tell string.h we don't want memcpy etc. as cpp defines */ +#define EXPORT_SYMTAB_STROPS + +extern void do_IRQ(struct pt_regs *regs, int isfake); +extern void SystemResetException(struct pt_regs *regs); +extern void MachineCheckException(struct pt_regs *regs); +extern void AlignmentException(struct pt_regs *regs); +extern void ProgramCheckException(struct pt_regs *regs); +extern void SingleStepException(struct pt_regs *regs); +extern int sys_sigreturn(struct pt_regs *regs); +extern int do_signal(sigset_t *, struct pt_regs *); +extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)); +extern int unregister_ioctl32_conversion(unsigned int cmd); + +long long __ashrdi3(long long, int); +long long __ashldi3(long long, int); +long long __lshrdi3(long long, int); +int abs(int); + +extern struct pci_dev * iSeries_veth_dev; +extern struct pci_dev * iSeries_vio_dev; + +EXPORT_SYMBOL(do_signal); +EXPORT_SYMBOL(do_IRQ); +EXPORT_SYMBOL(SystemResetException); +EXPORT_SYMBOL(MachineCheckException); +EXPORT_SYMBOL(AlignmentException); +EXPORT_SYMBOL(ProgramCheckException); +EXPORT_SYMBOL(SingleStepException); +EXPORT_SYMBOL(sys_sigreturn); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(disable_irq_nosync); +#ifdef CONFIG_SMP +EXPORT_SYMBOL(kernel_flag); +EXPORT_SYMBOL(synchronize_irq); +EXPORT_SYMBOL(smp_num_cpus); +#endif /* CONFIG_SMP */ + +EXPORT_SYMBOL(register_ioctl32_conversion); +EXPORT_SYMBOL(unregister_ioctl32_conversion); + +EXPORT_SYMBOL(isa_io_base); +EXPORT_SYMBOL(isa_mem_base); +EXPORT_SYMBOL(pci_io_base); +EXPORT_SYMBOL(pci_dram_offset); + +EXPORT_SYMBOL(find_next_zero_bit); + +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strncpy); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(strstr); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strncmp); + +EXPORT_SYMBOL(__down_interruptible); +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(naca); +EXPORT_SYMBOL(__down); + +/* EXPORT_SYMBOL(csum_partial); already in net/netsyms.c */ +EXPORT_SYMBOL(csum_partial_copy_generic); +EXPORT_SYMBOL(ip_fast_csum); +EXPORT_SYMBOL(csum_tcpudp_magic); + +EXPORT_SYMBOL(__copy_tofrom_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(__strnlen_user); + +/* +EXPORT_SYMBOL(inb); +EXPORT_SYMBOL(inw); +EXPORT_SYMBOL(inl); +EXPORT_SYMBOL(outb); +EXPORT_SYMBOL(outw); +EXPORT_SYMBOL(outl); +EXPORT_SYMBOL(outsl);*/ + +#ifdef CONFIG_MSCHUNKS +EXPORT_SYMBOL(msChunks); +#endif +EXPORT_SYMBOL(reloc_offset); + +#ifdef CONFIG_PPC_ISERIES +EXPORT_SYMBOL(iSeries_proc_callback); +EXPORT_SYMBOL(HvCall0); +EXPORT_SYMBOL(HvCall1); +EXPORT_SYMBOL(HvCall2); +EXPORT_SYMBOL(HvCall3); +EXPORT_SYMBOL(HvCall4); +EXPORT_SYMBOL(HvCall5); +EXPORT_SYMBOL(HvCall6); +EXPORT_SYMBOL(HvCall7); +#endif + +EXPORT_SYMBOL(_insb); +EXPORT_SYMBOL(_outsb); +EXPORT_SYMBOL(_insw); +EXPORT_SYMBOL(_outsw); +EXPORT_SYMBOL(_insl); +EXPORT_SYMBOL(_outsl); +EXPORT_SYMBOL(_insw_ns); +EXPORT_SYMBOL(_outsw_ns); +EXPORT_SYMBOL(_insl_ns); +EXPORT_SYMBOL(_outsl_ns); +EXPORT_SYMBOL(ioremap); +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); + +#ifdef CONFIG_PCI +EXPORT_SYMBOL(pci_alloc_consistent); +EXPORT_SYMBOL(pci_free_consistent); +EXPORT_SYMBOL(pci_map_single); +EXPORT_SYMBOL(pci_unmap_single); +EXPORT_SYMBOL(pci_map_sg); +EXPORT_SYMBOL(pci_unmap_sg); +#ifdef CONFIG_PPC_ISERIES +EXPORT_SYMBOL(iSeries_Write_Long); +EXPORT_SYMBOL(iSeries_GetLocationData); +EXPORT_SYMBOL(iSeries_Read_Long); +EXPORT_SYMBOL(iSeries_Device_ToggleReset); +EXPORT_SYMBOL(iSeries_Write_Word); +EXPORT_SYMBOL(iSeries_memcpy_fromio); +EXPORT_SYMBOL(iSeries_Read_Word); +EXPORT_SYMBOL(iSeries_Read_Byte); +EXPORT_SYMBOL(iSeries_Write_Byte); + +#endif /* CONFIG_PPC_ISERIES */ +#ifdef CONFIG_PPC_EEH +EXPORT_SYMBOL(eeh_check_failure); +EXPORT_SYMBOL(eeh_total_mmio_ffs); +EXPORT_SYMBOL(eeh_total_mmio_reads); +#endif /* CONFIG_PPC_EEH */ +#endif /* CONFIG_PCI */ + +EXPORT_SYMBOL(iSeries_veth_dev); +EXPORT_SYMBOL(iSeries_vio_dev); + +EXPORT_SYMBOL(start_thread); +EXPORT_SYMBOL(kernel_thread); + +EXPORT_SYMBOL(flush_instruction_cache); +EXPORT_SYMBOL(_get_PVR); +EXPORT_SYMBOL(giveup_fpu); +EXPORT_SYMBOL(enable_kernel_fp); +EXPORT_SYMBOL(flush_icache_range); +EXPORT_SYMBOL(flush_icache_user_range); +EXPORT_SYMBOL(flush_icache_page); +EXPORT_SYMBOL(flush_dcache_page); +#ifdef CONFIG_SMP +EXPORT_SYMBOL(__global_cli); +EXPORT_SYMBOL(__global_sti); +EXPORT_SYMBOL(__global_save_flags); +EXPORT_SYMBOL(__global_restore_flags); +#ifdef CONFIG_PPC_ISERIES +EXPORT_SYMBOL(__no_use_restore_flags); +EXPORT_SYMBOL(__no_use_save_flags); +EXPORT_SYMBOL(__no_use_sti); +EXPORT_SYMBOL(__no_use_cli); +#endif +#endif + +#ifndef CONFIG_MACH_SPECIFIC +EXPORT_SYMBOL(_machine); +#endif +EXPORT_SYMBOL(ppc_md); + +EXPORT_SYMBOL(find_devices); +EXPORT_SYMBOL(find_type_devices); +EXPORT_SYMBOL(find_compatible_devices); +EXPORT_SYMBOL(find_path_device); +EXPORT_SYMBOL(device_is_compatible); +EXPORT_SYMBOL(machine_is_compatible); +EXPORT_SYMBOL(find_all_nodes); +EXPORT_SYMBOL(get_property); + +#ifndef CONFIG_PPC_ISERIES +EXPORT_SYMBOL_NOVERS(sys_ctrler); /* tibit */ +#endif +#ifdef CONFIG_NVRAM +EXPORT_SYMBOL(nvram_read_byte); +EXPORT_SYMBOL(nvram_write_byte); +#endif /* CONFIG_NVRAM */ + +EXPORT_SYMBOL_NOVERS(__ashrdi3); +EXPORT_SYMBOL_NOVERS(__ashldi3); +EXPORT_SYMBOL_NOVERS(__lshrdi3); +EXPORT_SYMBOL_NOVERS(memcpy); +EXPORT_SYMBOL_NOVERS(memset); +EXPORT_SYMBOL_NOVERS(memmove); +EXPORT_SYMBOL_NOVERS(memscan); +EXPORT_SYMBOL_NOVERS(memcmp); + +EXPORT_SYMBOL(abs); + +EXPORT_SYMBOL(timer_interrupt); +EXPORT_SYMBOL(irq_desc); +void ppc_irq_dispatch_handler(struct pt_regs *, int); +EXPORT_SYMBOL(ppc_irq_dispatch_handler); +EXPORT_SYMBOL(get_wchan); +EXPORT_SYMBOL(console_drivers); +#ifdef CONFIG_XMON +EXPORT_SYMBOL(xmon); +#endif + +#if defined(CONFIG_KGDB) || defined(CONFIG_XMON) +extern void (*debugger)(struct pt_regs *regs); +extern int (*debugger_bpt)(struct pt_regs *regs); +extern int (*debugger_sstep)(struct pt_regs *regs); +extern int (*debugger_iabr_match)(struct pt_regs *regs); +extern int (*debugger_dabr_match)(struct pt_regs *regs); +extern void (*debugger_fault_handler)(struct pt_regs *regs); + +EXPORT_SYMBOL(debugger); +EXPORT_SYMBOL(debugger_bpt); +EXPORT_SYMBOL(debugger_sstep); +EXPORT_SYMBOL(debugger_iabr_match); +EXPORT_SYMBOL(debugger_dabr_match); +EXPORT_SYMBOL(debugger_fault_handler); +#endif + +#ifdef CONFIG_SMP +EXPORT_SYMBOL(atomic_dec_and_lock); +#endif + +EXPORT_SYMBOL(tb_ticks_per_usec); diff -Nru a/arch/ppc64/kernel/proc_pmc.c b/arch/ppc64/kernel/proc_pmc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/proc_pmc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,790 @@ +/* + * proc_pmc.c + * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: + * 2001 : mikec : Created + * 2001/06/05 : engebret : Software event count support. + * End Change Activity + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +static int proc_pmc_control_mode = 0; + +static struct proc_dir_entry *proc_ppc64_root = NULL; +static struct proc_dir_entry *proc_ppc64_pmc_root = NULL; +static struct proc_dir_entry *proc_ppc64_pmc_system_root = NULL; +static struct proc_dir_entry *proc_ppc64_pmc_cpu_root[NR_CPUS] = {NULL, }; + +static spinlock_t proc_ppc64_lock; + +extern struct Naca *naca; + +int proc_ppc64_pmc_find_file(void *data); +int proc_ppc64_pmc_read(char *page, char **start, off_t off, + int count, int *eof, char *buffer); +int proc_ppc64_pmc_stab_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +int proc_ppc64_pmc_htab_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +int proc_ppc64_pmc_hw_read(char *page, char **start, off_t off, + int count, int *eof, void *data); + +static struct proc_dir_entry *pmc_proc_root = NULL; + +int proc_get_lpevents( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data); + +int proc_get_titanTod( char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_pmc_get_control( char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data); + + +void proc_ppc64_init(void) +{ + unsigned long i; + struct proc_dir_entry *ent = NULL; + char buf[256]; + + printk("proc_ppc64: Creating /proc/ppc64/pmc\n"); + + /* + * Create the root, system, and cpu directories as follows: + * /proc/ppc64/pmc/system + * /proc/ppc64/pmc/cpu0 + */ + spin_lock(&proc_ppc64_lock); + proc_ppc64_root = proc_mkdir("ppc64", 0); + if (!proc_ppc64_root) return; + spin_unlock(&proc_ppc64_lock); + +#ifdef CONFIG_PPC_EEH + eeh_init_proc(proc_ppc64_root); +#endif + + proc_ppc64_pmc_root = proc_mkdir("pmc", proc_ppc64_root); + + proc_ppc64_pmc_system_root = proc_mkdir("system", proc_ppc64_pmc_root); + for (i = 0; i < naca->processorCount; i++) { + sprintf(buf, "cpu%ld", i); + proc_ppc64_pmc_cpu_root[i] = proc_mkdir(buf, proc_ppc64_pmc_root); + } + + + /* Create directories for the software counters. */ + for (i = 0; i < naca->processorCount; i++) { + ent = create_proc_entry("stab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_cpu_root[i]); + if (ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; + ent->read_proc = (void *)proc_ppc64_pmc_stab_read; + ent->write_proc = (void *)proc_ppc64_pmc_stab_read; + } + + ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_cpu_root[i]); + if (ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; + ent->read_proc = (void *)proc_ppc64_pmc_htab_read; + ent->write_proc = (void *)proc_ppc64_pmc_htab_read; + } + } + + ent = create_proc_entry("stab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_system_root); + if (ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_system_root; + ent->read_proc = (void *)proc_ppc64_pmc_stab_read; + ent->write_proc = (void *)proc_ppc64_pmc_stab_read; + } + + ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_system_root); + if (ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_system_root; + ent->read_proc = (void *)proc_ppc64_pmc_htab_read; + ent->write_proc = (void *)proc_ppc64_pmc_htab_read; + } + + /* Create directories for the hardware counters. */ + for (i = 0; i < naca->processorCount; i++) { + ent = create_proc_entry("hardware", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_cpu_root[i]); + if (ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; + ent->read_proc = (void *)proc_ppc64_pmc_hw_read; + ent->write_proc = (void *)proc_ppc64_pmc_hw_read; + } + } + + ent = create_proc_entry("hardware", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_system_root); + if (ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_system_root; + ent->read_proc = (void *)proc_ppc64_pmc_hw_read; + ent->write_proc = (void *)proc_ppc64_pmc_hw_read; + } +} + +/* + * Find the requested 'file' given a proc token. + * + * Inputs: void * data: proc token + * Output: int : (0, ..., +N) = CPU number. + * -1 = System. + */ +int proc_ppc64_pmc_find_file(void *data) +{ + int i; + + if ((unsigned long)data == + (unsigned long) proc_ppc64_pmc_system_root) { + return(-1); + } else { + for (i = 0; i < naca->processorCount; i++) { + if ((unsigned long)data == + (unsigned long)proc_ppc64_pmc_cpu_root[i]) { + return(i); + } + } + } + + /* On error, just default to a type of system. */ + printk("proc_ppc64_pmc_find_file: failed to find file token.\n"); + return(-1); +} + +int +proc_ppc64_pmc_read(char *page, char **start, off_t off, + int count, int *eof, char *buffer) +{ + int buffer_size, n; + + if (count < 0) return 0; + + if (buffer == NULL) { + *eof = 1; + return 0; + } + + /* Check for read beyond EOF */ + buffer_size = n = strlen(buffer); + if (off >= buffer_size) { + *eof = 1; + return 0; + } + if (n > (buffer_size - off)) n = buffer_size - off; + + /* Never return more than was requested */ + if (n > count) { + n = count; + } else { + *eof = 1; + } + + memcpy(page, buffer + off, n); + + *start = page; + + return n; +} + +int +proc_ppc64_pmc_stab_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int n, file; + char *buffer = NULL; + + if (count < 0) return 0; + spin_lock(&proc_ppc64_lock); + + /* Figure out which file is being request. */ + file = proc_ppc64_pmc_find_file(data); + + /* Update the counters and the text buffer representation. */ + buffer = ppc64_pmc_stab(file); + + /* Put the data into the requestor's buffer. */ + n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); + + spin_unlock(&proc_ppc64_lock); + return n; +} + +int +proc_ppc64_pmc_htab_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int n, file; + char *buffer = NULL; + + if (count < 0) return 0; + spin_lock(&proc_ppc64_lock); + + /* Figure out which file is being request. */ + file = proc_ppc64_pmc_find_file(data); + + /* Update the counters and the text buffer representation. */ + buffer = ppc64_pmc_htab(file); + + /* Put the data into the requestor's buffer. */ + n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); + + spin_unlock(&proc_ppc64_lock); + return n; +} + +int +proc_ppc64_pmc_hw_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int n, file; + char *buffer = NULL; + + if (count < 0) return 0; + spin_lock(&proc_ppc64_lock); + + /* Figure out which file is being request. */ + file = proc_ppc64_pmc_find_file(data); + + /* Update the counters and the text buffer representation. */ + buffer = ppc64_pmc_hw(file); + + /* Put the data into the requestor's buffer. */ + n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); + + spin_unlock(&proc_ppc64_lock); + return n; +} + +/* + * DRENG the remainder of these functions still need work ... + */ +void pmc_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent = NULL; + + ent = create_proc_entry("lpevents", S_IFREG|S_IRUGO, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_get_lpevents; + ent->write_proc = proc_reset_lpevents; + + ent = create_proc_entry("titanTod", S_IFREG|S_IRUGO, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_get_titanTod; + ent->write_proc = NULL; + + pmc_proc_root = proc_mkdir("pmc", iSeries_proc); + if (!pmc_proc_root) return; + + ent = create_proc_entry("control", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_control; + ent->write_proc = proc_pmc_set_control; + +} + +static int pmc_calc_metrics( char *page, char **start, off_t off, int count, int *eof, int len) +{ + if ( len <= off+count) + *eof = 1; + *start = page+off; + len -= off; + if ( len > count ) + len = count; + if ( len < 0 ) + len = 0; + return len; +} + +static char * lpEventTypes[9] = { + "Hypervisor\t\t", + "Machine Facilities\t", + "Session Manager\t", + "SPD I/O\t\t", + "Virtual Bus\t\t", + "PCI I/O\t\t", + "RIO I/O\t\t", + "Virtual Lan\t\t", + "Virtual I/O\t\t" + }; + + +int proc_get_lpevents +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + unsigned i; + int len = 0; + + len += sprintf( page+len, "LpEventQueue 0\n" ); + len += sprintf( page+len, " events processed:\t%lu\n", + (unsigned long)xItLpQueue.xLpIntCount ); + for (i=0; i<9; ++i) { + len += sprintf( page+len, " %s %10lu\n", + lpEventTypes[i], + (unsigned long)xItLpQueue.xLpIntCountByType[i] ); + } + len += sprintf( page+len, "\n events processed by processor:\n" ); + for (i=0; iprocessorCount; ++i) { + len += sprintf( page+len, " CPU%02d %10u\n", + i, xPaca[i].lpEvent_count ); + } + + return pmc_calc_metrics( page, start, off, count, eof, len ); + +} + +int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + return count; +} + +static unsigned long startTitan = 0; +static unsigned long startTb = 0; + + +int proc_get_titanTod +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long tb0, titan_tod; + + tb0 = get_tb(); + titan_tod = HvCallXm_loadTod(); + + len += sprintf( page+len, "Titan\n" ); + len += sprintf( page+len, " time base = %016lx\n", tb0 ); + len += sprintf( page+len, " titan tod = %016lx\n", titan_tod ); + len += sprintf( page+len, " xProcFreq = %016x\n", xIoHriProcessorVpd[0].xProcFreq ); + len += sprintf( page+len, " xTimeBaseFreq = %016x\n", xIoHriProcessorVpd[0].xTimeBaseFreq ); + len += sprintf( page+len, " tb_ticks_per_jiffy = %lu\n", tb_ticks_per_jiffy ); + len += sprintf( page+len, " tb_ticks_per_usec = %lu\n", tb_ticks_per_usec ); + + if ( !startTitan ) { + startTitan = titan_tod; + startTb = tb0; + } + else { + unsigned long titan_usec = (titan_tod - startTitan) >> 12; + unsigned long tb_ticks = (tb0 - startTb); + unsigned long titan_jiffies = titan_usec / (1000000/HZ); + unsigned long titan_jiff_usec = titan_jiffies * (1000000/HZ); + unsigned long titan_jiff_rem_usec = titan_usec - titan_jiff_usec; + unsigned long tb_jiffies = tb_ticks / tb_ticks_per_jiffy; + unsigned long tb_jiff_ticks = tb_jiffies * tb_ticks_per_jiffy; + unsigned long tb_jiff_rem_ticks = tb_ticks - tb_jiff_ticks; + unsigned long tb_jiff_rem_usec = tb_jiff_rem_ticks / tb_ticks_per_usec; + unsigned long new_tb_ticks_per_jiffy = (tb_ticks * (1000000/HZ))/titan_usec; + + len += sprintf( page+len, " titan elapsed = %lu uSec\n", titan_usec); + len += sprintf( page+len, " tb elapsed = %lu ticks\n", tb_ticks); + len += sprintf( page+len, " titan jiffies = %lu.%04lu \n", titan_jiffies, titan_jiff_rem_usec ); + len += sprintf( page+len, " tb jiffies = %lu.%04lu\n", tb_jiffies, tb_jiff_rem_usec ); + len += sprintf( page+len, " new tb_ticks_per_jiffy = %lu\n", new_tb_ticks_per_jiffy ); + + } + + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_control +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + if ( proc_pmc_control_mode == PMC_CONTROL_CPI ) { + unsigned long mach_cycles = mfspr( PMC5 ); + unsigned long inst_complete = mfspr( PMC4 ); + unsigned long inst_dispatch = mfspr( PMC3 ); + unsigned long thread_active_run = mfspr( PMC1 ); + unsigned long thread_active = mfspr( PMC2 ); + unsigned long cpi = 0; + unsigned long cpithou = 0; + unsigned long remain; + + if ( inst_complete ) { + cpi = thread_active_run / inst_complete; + remain = thread_active_run % inst_complete; + if ( inst_complete > 1000000 ) + cpithou = remain / ( inst_complete / 1000 ); + else + cpithou = ( remain * 1000 ) / inst_complete; + } + len += sprintf( page+len, "PMC CPI Mode\nRaw Counts\n" ); + len += sprintf( page+len, "machine cycles : %12lu\n", mach_cycles ); + len += sprintf( page+len, "thread active cycles : %12lu\n\n", thread_active ); + + len += sprintf( page+len, "instructions completed : %12lu\n", inst_complete ); + len += sprintf( page+len, "instructions dispatched : %12lu\n", inst_dispatch ); + len += sprintf( page+len, "thread active run cycles : %12lu\n", thread_active_run ); + + len += sprintf( page+len, "thread active run cycles/instructions completed\n" ); + len += sprintf( page+len, "CPI = %lu.%03lu\n", cpi, cpithou ); + + } + else if ( proc_pmc_control_mode == PMC_CONTROL_TLB ) { + len += sprintf( page+len, "PMC TLB Mode\n" ); + len += sprintf( page+len, "I-miss count : %12lu\n", mfspr( PMC1 ) ); + len += sprintf( page+len, "I-miss latency : %12lu\n", mfspr( PMC2 ) ); + len += sprintf( page+len, "D-miss count : %12lu\n", mfspr( PMC3 ) ); + len += sprintf( page+len, "D-miss latency : %12lu\n", mfspr( PMC4 ) ); + len += sprintf( page+len, "IERAT miss count : %12lu\n", mfspr( PMC5 ) ); + len += sprintf( page+len, "D-reference count : %12lu\n", mfspr( PMC6 ) ); + len += sprintf( page+len, "miss PTEs searched : %12lu\n", mfspr( PMC7 ) ); + len += sprintf( page+len, "miss >8 PTEs searched : %12lu\n", mfspr( PMC8 ) ); + } + /* IMPLEMENT ME */ + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +unsigned long proc_pmc_conv_int( const char *buf, unsigned count ) +{ + const char * p; + char b0, b1; + unsigned v, multiplier, mult, i; + unsigned long val; + multiplier = 10; + p = buf; + if ( count >= 3 ) { + b0 = buf[0]; + b1 = buf[1]; + if ( ( b0 == '0' ) && + ( ( b1 == 'x' ) || ( b1 == 'X' ) ) ) { + p = buf + 2; + count -= 2; + multiplier = 16; + } + + } + val = 0; + for ( i=0; i= '0' ) && ( b0 <= '9' ) ) + v = b0 - '0'; + else if ( multiplier == 16 ) { + if ( ( b0 >= 'a' ) && ( b0 <= 'f' ) ) + v = b0 - 'a' + 10; + else if ( ( b0 >= 'A' ) && ( b0 <= 'F' ) ) + v = b0 - 'A' + 10; + else + mult = 1; + } + else + mult = 1; + val *= mult; + val += v; + } + + return val; + +} + +static inline void proc_pmc_stop(void) +{ + /* Freeze all counters, leave everything else alone */ + mtspr( MMCR0, mfspr( MMCR0 ) | 0x80000000 ); +} + +static inline void proc_pmc_start(void) +{ + /* Unfreeze all counters, leave everything else alone */ + mtspr( MMCR0, mfspr( MMCR0 ) & ~0x80000000 ); + +} + +static inline void proc_pmc_reset(void) +{ + /* Clear all the PMCs to zeros + * Assume a "stop" has already frozen the counters + * Clear all the PMCs + */ + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + +} + +static inline void proc_pmc_cpi(void) +{ + /* Configure the PMC registers to count cycles and instructions */ + /* so we can compute cpi */ + /* + * MMCRA[30] = 1 Don't count in wait state (CTRL[31]=0) + * MMCR0[6] = 1 Freeze counters when any overflow + * MMCR0[19:25] = 0x01 PMC1 counts Thread Active Run Cycles + * MMCR0[26:31] = 0x05 PMC2 counts Thread Active Cycles + * MMCR1[0:4] = 0x07 PMC3 counts Instructions Dispatched + * MMCR1[5:9] = 0x03 PMC4 counts Instructions Completed + * MMCR1[10:14] = 0x06 PMC5 counts Machine Cycles + * + */ + + proc_pmc_control_mode = PMC_CONTROL_CPI; + + /* Indicate to hypervisor that we are using the PMCs */ + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; + + /* Freeze all counters */ + mtspr( MMCR0, 0x80000000 ); + mtspr( MMCR1, 0x00000000 ); + + /* Clear all the PMCs */ + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + + /* Freeze counters in Wait State (CTRL[31]=0) */ + mtspr( MMCRA, 0x00000002 ); + + /* PMC3<-0x07, PMC4<-0x03, PMC5<-0x06 */ + mtspr( MMCR1, 0x38cc0000 ); + + mb(); + + /* PMC1<-0x01, PMC2<-0x05 + * Start all counters + */ + mtspr( MMCR0, 0x02000045 ); + +} + +static inline void proc_pmc_tlb(void) +{ + /* Configure the PMC registers to count tlb misses */ + /* + * MMCR0[6] = 1 Freeze counters when any overflow + * MMCR0[19:25] = 0x55 Group count + * PMC1 counts I misses + * PMC2 counts I miss duration (latency) + * PMC3 counts D misses + * PMC4 counts D miss duration (latency) + * PMC5 counts IERAT misses + * PMC6 counts D references (including PMC7) + * PMC7 counts miss PTEs searched + * PMC8 counts miss >8 PTEs searched + * + */ + + proc_pmc_control_mode = PMC_CONTROL_TLB; + + /* Indicate to hypervisor that we are using the PMCs */ + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; + + /* Freeze all counters */ + mtspr( MMCR0, 0x80000000 ); + mtspr( MMCR1, 0x00000000 ); + + /* Clear all the PMCs */ + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + + mtspr( MMCRA, 0x00000000 ); + + mb(); + + /* PMC1<-0x55 + * Start all counters + */ + mtspr( MMCR0, 0x02001540 ); + +} + +int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + if ( ! strncmp( buffer, "stop", 4 ) ) + proc_pmc_stop(); + else if ( ! strncmp( buffer, "start", 5 ) ) + proc_pmc_start(); + else if ( ! strncmp( buffer, "reset", 5 ) ) + proc_pmc_reset(); + else if ( ! strncmp( buffer, "cpi", 3 ) ) + proc_pmc_cpi(); + else if ( ! strncmp( buffer, "tlb", 3 ) ) + proc_pmc_tlb(); + + /* IMPLEMENT ME */ + return count; +} + +int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + v = v & ~0x04000000; /* Don't allow interrupts for now */ + if ( v & ~0x80000000 ) /* Inform hypervisor we are using PMCs */ + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; + else + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 0; + mtspr( MMCR0, v ); + + return count; +} + +int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( MMCR1, v ); + + return count; +} + +int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + v = v & ~0x00008000; /* Don't allow interrupts for now */ + mtspr( MMCRA, v ); + + return count; +} + + +int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC1, v ); + + return count; +} + +int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC2, v ); + + return count; +} + +int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC3, v ); + + return count; +} + +int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC4, v ); + + return count; +} + +int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC5, v ); + + return count; +} + +int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC6, v ); + + return count; +} + +int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC7, v ); + + return count; +} + +int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC8, v ); + + return count; +} + diff -Nru a/arch/ppc64/kernel/process.c b/arch/ppc64/kernel/process.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/process.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,478 @@ +/* + * linux/arch/ppc/kernel/process.c + * + * Derived from "arch/i386/kernel/process.c" + * Copyright (C) 1995 Linus Torvalds + * + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) and + * Paul Mackerras (paulus@cs.anu.edu.au) + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#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 + +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); +extern unsigned long _get_SP(void); + +struct task_struct *last_task_used_math = NULL; + +struct mm_struct ioremap_mm = { pgd : ioremap_dir + ,page_table_lock : SPIN_LOCK_UNLOCKED }; + +char *sysmap = NULL; +unsigned long sysmap_size = 0; + +extern char __toc_start; + +void +enable_kernel_fp(void) +{ +#ifdef CONFIG_SMP + if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) + giveup_fpu(current); + else + giveup_fpu(NULL); /* just enables FP for kernel */ +#else + giveup_fpu(last_task_used_math); +#endif /* CONFIG_SMP */ +} + +int +dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) +{ + if (regs->msr & MSR_FP) + giveup_fpu(current); + memcpy(fpregs, ¤t->thread.fpr[0], sizeof(*fpregs)); + return 1; +} + +void +_switch_to(struct task_struct *prev, struct task_struct *new) +{ + struct thread_struct *new_thread, *old_thread; + unsigned long s; + + __save_flags(s); + __cli(); + +#ifdef CONFIG_SMP + /* avoid complexity of lazy save/restore of fpu + * by just saving it every time we switch out if + * this task used the fpu during the last quantum. + * + * If it tries to use the fpu again, it'll trap and + * reload its fp regs. So we don't have to do a restore + * every switch, just a save. + * -- Cort + */ + if ( prev->thread.regs && (prev->thread.regs->msr & MSR_FP) ) + giveup_fpu(prev); +#endif /* CONFIG_SMP */ + + new_thread = &new->thread; + old_thread = ¤t->thread; + _switch(old_thread, new_thread); + __restore_flags(s); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printk("NIP: %016lX XER: %016lX LR: %016lX REGS: %p TRAP: %04lx %s\n", + regs->nip, regs->xer, regs->link, regs,regs->trap, print_tainted()); + printk("MSR: %016lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + printk("TASK = %p[%d] '%s' ", + current, current->pid, current->comm); + printk("Last syscall: %ld ", current->thread.last_syscall); + printk("\nlast math %p ", last_task_used_math); + +#ifdef CONFIG_SMP + /* printk(" CPU: %d last CPU: %d", current->processor,current->last_processor); */ +#endif /* CONFIG_SMP */ + + printk("\n"); + for (i = 0; i < 32; i++) + { + long r; + if ((i % 4) == 0) + { + printk("GPR%02d: ", i); + } + + if ( __get_user(r, &(regs->gpr[i])) ) + return; + + printk("%016lX ", r); + if ((i % 4) == 3) + { + printk("\n"); + } + } +} + +void exit_thread(void) +{ + if (last_task_used_math == current) + last_task_used_math = NULL; +} + +void flush_thread(void) +{ + if (last_task_used_math == current) + last_task_used_math = NULL; +} + +void +release_thread(struct task_struct *t) +{ +} + +/* + * Copy a thread.. + */ +int +copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, + struct task_struct * p, struct pt_regs * regs) +{ + unsigned long msr; + struct pt_regs *childregs, *kregs; + extern void ret_from_fork(void); + + /* XXX get rid of the -2 Anton */ + + /* Copy registers */ + childregs = ((struct pt_regs *) + ((unsigned long)p->thread_info + THREAD_SIZE + - STACK_FRAME_OVERHEAD)) - 2; + *childregs = *regs; + childregs->gpr[3] = 0; /* Result from fork() */ + p->thread.regs = childregs; + p->thread.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; + p->thread.ksp -= sizeof(struct pt_regs ) + STACK_FRAME_OVERHEAD; + kregs = (struct pt_regs *)(p->thread.ksp + STACK_FRAME_OVERHEAD); + /* The PPC64 compiler makes use of a TOC to contain function + * pointers. The function (ret_from_except) is actually a pointer + * to the TOC entry. The first entry is a pointer to the actual + * function. + */ + kregs->nip = *((unsigned long *)ret_from_fork); + asm volatile("mfmsr %0" : "=r" (msr):); + kregs->msr = msr; + kregs->gpr[1] = (unsigned long)childregs - STACK_FRAME_OVERHEAD; + kregs->gpr[2] = (((unsigned long)&__toc_start) + 0x8000); + + if (usp >= (unsigned long) regs) { + /* Stack is in kernel space - must adjust */ + childregs->gpr[1] = (unsigned long)(childregs + 1); + *((unsigned long *) childregs->gpr[1]) = 0; + childregs->gpr[13] = (unsigned long) p; + } else { + /* Provided stack is in user space */ + childregs->gpr[1] = usp; + } + p->thread.last_syscall = -1; + + /* + * copy fpu info - assume lazy fpu switch now always + * -- Cort + */ + if (regs->msr & MSR_FP) { + giveup_fpu(current); + childregs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1); + } + memcpy(&p->thread.fpr, ¤t->thread.fpr, sizeof(p->thread.fpr)); + p->thread.fpscr = current->thread.fpscr; + + return 0; +} + +/* + * Set up a thread for executing a new program + */ +void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) +{ + /* NIP is *really* a pointer to the function descriptor for + * the elf _start routine. The first entry in the function + * descriptor is the entry address of _start and the second + * entry is the TOC value we need to use. + */ + unsigned long *entry = (unsigned long *)nip; + unsigned long *toc = entry + 1; + + + set_fs(USER_DS); + memset(regs->gpr, 0, sizeof(regs->gpr)); + memset(®s->ctr, 0, 4 * sizeof(regs->ctr)); + __get_user(regs->nip, entry); + regs->gpr[1] = sp; + __get_user(regs->gpr[2], toc); + regs->msr = MSR_USER64; + if (last_task_used_math == current) + last_task_used_math = 0; + current->thread.fpscr = 0; +} + +asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + unsigned long clone_flags = p1; + int res; + + res = do_fork(clone_flags, regs->gpr[1], regs, 0); +#ifdef CONFIG_SMP + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; +#endif /* CONFIG_SMP */ + + return res; +} + +asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + int res; + + res = do_fork(SIGCHLD, regs->gpr[1], regs, 0); + +#ifdef CONFIG_SMP + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; +#endif /* CONFIG_SMP */ + + return res; +} + +asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0); +} + +asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs *regs) +{ + int error; + char * filename; + + filename = getname((char *) a0); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + if (regs->msr & MSR_FP) + giveup_fpu(current); + + error = do_execve(filename, (char **) a1, (char **) a2, regs); + + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); + +out: + return error; +} + +void initialize_paca_hardware_interrupt_stack(void) +{ + extern struct Naca *naca; + + int i; + unsigned long stack; + unsigned long end_of_stack =0; + + for (i=1; i < naca->processorCount; i++) { + /* Carve out storage for the hardware interrupt stack */ + stack = __get_free_pages(GFP_KERNEL, get_order(8*PAGE_SIZE)); + + if ( !stack ) { + printk("ERROR, cannot find space for hardware stack.\n"); + panic(" no hardware stack "); + } + + + /* Store the stack value in the PACA for the processor */ + xPaca[i].xHrdIntStack = stack + (8*PAGE_SIZE) - STACK_FRAME_OVERHEAD; + xPaca[i].xHrdIntCount = 0; + + } + + /* + * __get_free_pages() might give us a page > KERNBASE+256M which + * is mapped with large ptes so we can't set up the guard page. + */ + if (__is_processor(PV_POWER4)) + return; + + for (i=0; i < naca->processorCount; i++) { + /* set page at the top of stack to be protected - prevent overflow */ + end_of_stack = xPaca[i].xHrdIntStack - (8*PAGE_SIZE - STACK_FRAME_OVERHEAD); + ppc_md.hpte_updateboltedpp(PP_RXRX,end_of_stack); + } +} + +extern char _stext[], _etext[]; + +char * ppc_find_proc_name( unsigned * p, char * buf, unsigned buflen ) +{ + unsigned long tb_flags; + unsigned short name_len; + unsigned long tb_start, code_start, code_ptr, code_offset; + unsigned code_len; + strcpy( buf, "Unknown" ); + code_ptr = (unsigned long)p; + code_offset = 0; + if ( ( (unsigned long)p >= (unsigned long)_stext ) && ( (unsigned long)p <= (unsigned long)_etext ) ) { + while ( (unsigned long)p <= (unsigned long)_etext ) { + if ( *p == 0 ) { + tb_start = (unsigned long)p; + ++p; /* Point to traceback flags */ + tb_flags = *((unsigned long *)p); + p += 2; /* Skip over traceback flags */ + if ( tb_flags & TB_NAME_PRESENT ) { + if ( tb_flags & TB_PARMINFO ) + ++p; /* skip over parminfo data */ + if ( tb_flags & TB_HAS_TBOFF ) { + code_len = *p; /* get code length */ + code_start = tb_start - code_len; + code_offset = code_ptr - code_start + 1; + if ( code_offset > 0x100000 ) + break; + ++p; /* skip over code size */ + } + name_len = *((unsigned short *)p); + if ( name_len > (buflen-20) ) + name_len = buflen-20; + memcpy( buf, ((char *)p)+2, name_len ); + buf[name_len] = 0; + if ( code_offset ) + sprintf( buf+name_len, "+0x%lx", code_offset-1 ); + } + break; + } + ++p; + } + } + return buf; +} + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + char name_buf[256]; + + printk("Call backtrace: \n"); + while (sp) { + if (__get_user( i, &sp[2] )) + break; + printk("%016lX ", i); + printk("%s\n", ppc_find_proc_name( (unsigned *)i, name_buf, 256 )); + if (cnt > 32) break; + if (__get_user(sp, (unsigned long **)sp)) + break; + } + printk("\n"); +} + +/* + * These bracket the sleeping functions.. + */ +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched (*(unsigned long *)scheduling_functions_start_here) +#define last_sched (*(unsigned long *)scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long ip, sp; + unsigned long stack_page = (unsigned long)p->thread_info; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + sp = p->thread.ksp; + do { + sp = *(unsigned long *)sp; + if (sp < (stack_page + sizeof(struct thread_struct)) || + sp >= (stack_page + THREAD_SIZE)) + return 0; + if (count > 0) { + ip = *(unsigned long *)(sp + 16); + if (ip < first_sched || ip >= last_sched) + return (ip & 0xFFFFFFFF); + } + } while (count++ < 16); + return 0; +} + +void show_trace_task(struct task_struct *p) +{ + unsigned long ip, sp; + unsigned long stack_page = (unsigned long)p->thread_info; + int count = 0; + + if (!p) + return; + + printk("Call Trace: "); + sp = p->thread.ksp; + do { + sp = *(unsigned long *)sp; + if (sp < (stack_page + sizeof(struct thread_struct)) || + sp >= (stack_page + THREAD_SIZE)) + break; + if (count > 0) { + ip = *(unsigned long *)(sp + 16); + printk("[%016lx] ", ip); + } + } while (count++ < 16); + printk("\n"); +} diff -Nru a/arch/ppc64/kernel/prom.c b/arch/ppc64/kernel/prom.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/prom.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,2239 @@ +/* + * + * + * Procedures for interfacing to Open Firmware. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. + * {engebret|bergner}@us.ibm.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. + */ + +#if 0 +#define DEBUG_YABOOT +#endif + +#if 0 +#define DEBUG_PROM +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_YABOOT +#define call_yaboot(FUNC,...) \ + do { \ + if (FUNC) { \ + struct prom_t *_prom = PTRRELOC(&prom); \ + unsigned long prom_entry = _prom->entry;\ + _prom->entry = (unsigned long)(FUNC); \ + enter_prom(__VA_ARGS__); \ + _prom->entry = prom_entry; \ + } \ + } while (0) +#else +#define call_yaboot(FUNC,...) do { ; } while (0) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "open_pic.h" +#include +#include + +#ifdef CONFIG_FB +#include +#endif + +extern char _end[]; + +/* + * prom_init() is called very early on, before the kernel text + * and data have been mapped to KERNELBASE. At this point the code + * is running at whatever address it has been loaded at, so + * references to extern and static variables must be relocated + * explicitly. The procedure reloc_offset() returns the address + * we're currently running at minus the address we were linked at. + * (Note that strings count as static variables.) + * + * Because OF may have mapped I/O devices into the area starting at + * KERNELBASE, particularly on CHRP machines, we can't safely call + * OF once the kernel has been mapped to KERNELBASE. Therefore all + * OF calls should be done within prom_init(), and prom_init() + * and all routines called within it must be careful to relocate + * references as necessary. + * + * Note that the bss is cleared *after* prom_init runs, so we have + * to make sure that any static or extern variables it accesses + * are put in the data segment. + */ + + +#define PROM_BUG() do { \ + prom_print(RELOC("kernel BUG at ")); \ + prom_print(RELOC(__FILE__)); \ + prom_print(RELOC(":")); \ + prom_print_hex(__LINE__); \ + prom_print(RELOC("!\n")); \ + __asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); \ +} while (0) + + + +struct pci_reg_property { + struct pci_address addr; + u32 size_hi; + u32 size_lo; +}; + + +struct isa_reg_property { + u32 space; + u32 address; + u32 size; +}; + +struct pci_intr_map { + struct pci_address addr; + u32 dunno; + phandle int_ctrler; + u32 intr; +}; + + +typedef unsigned long interpret_func(struct device_node *, unsigned long, + int, int); +#if 0 +static interpret_func interpret_pci_props; +#endif +static unsigned long interpret_pci_props(struct device_node *, unsigned long, + int, int); + +static interpret_func interpret_isa_props; +static interpret_func interpret_root_props; + +#ifndef FB_MAX /* avoid pulling in all of the fb stuff */ +#define FB_MAX 8 +#endif + + +struct prom_t prom = { + 0, /* entry */ + 0, /* chosen */ + 0, /* cpu */ + 0, /* stdout */ + 0, /* disp_node */ + {0,0,0,{0},NULL}, /* args */ + 0, /* version */ + 32, /* encode_phys_size */ + 0 /* bi_rec pointer */ +#ifdef DEBUG_YABOOT + ,NULL /* yaboot */ +#endif +}; + + +char *prom_display_paths[FB_MAX] __initdata = { 0, }; +unsigned int prom_num_displays = 0; +char *of_stdout_device = 0; + +extern struct rtas_t rtas; +extern unsigned long klimit; +extern unsigned long embedded_sysmap_end; +extern struct Naca *naca; +extern struct lmb lmb; +#ifdef CONFIG_MSCHUNKS +extern struct msChunks msChunks; +#endif /* CONFIG_MSCHUNKS */ + +#define MAX_PHB 16 * 3 // 16 Towers * 3 PHBs/tower +struct _of_tce_table of_tce_table[MAX_PHB + 1] = {{0, 0, 0}}; + +char *bootpath = 0; +char *bootdevice = 0; + +struct device_node *allnodes = 0; + +#define UNDEFINED_IRQ 0xffff +unsigned short real_irq_to_virt_map[NR_HW_IRQS]; +unsigned short virt_irq_to_real_map[NR_IRQS]; +int last_virt_irq = 2; /* index of last virt_irq. Skip through IPI */ + +static unsigned long call_prom(const char *service, int nargs, int nret, ...); +static void prom_exit(void); +static unsigned long copy_device_tree(unsigned long); +static unsigned long inspect_node(phandle, struct device_node *, unsigned long, + unsigned long, struct device_node ***); +static unsigned long finish_node(struct device_node *, unsigned long, + interpret_func *, int, int); +static unsigned long finish_node_interrupts(struct device_node *, unsigned long); +static unsigned long check_display(unsigned long); +static int prom_next_node(phandle *); +static struct bi_record * prom_bi_rec_verify(struct bi_record *); +static unsigned long prom_bi_rec_reserve(unsigned long); +static struct device_node *find_phandle(phandle); + +#ifdef CONFIG_MSCHUNKS +static unsigned long prom_initialize_mschunks(unsigned long); +#endif /* CONFIG_MSCHUNKS */ + +extern unsigned long reloc_offset(void); + +extern void enter_prom(void *dummy,...); + +void cacheable_memzero(void *, unsigned int); + +extern char cmd_line[512]; /* XXX */ +unsigned long dev_tree_size; + +#ifdef CONFIG_HMT +struct { + unsigned int pir; + unsigned int threadid; +} hmt_thread_data[NR_CPUS] = {0}; +#endif /* CONFIG_HMT */ + +char testString[] = "LINUX\n"; + + +/* This is the one and *ONLY* place where we actually call open + * firmware from, since we need to make sure we're running in 32b + * mode when we do. We switch back to 64b mode upon return. + */ + +static unsigned long __init +call_prom(const char *service, int nargs, int nret, ...) +{ + int i; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + va_list list; + + _prom->args.service = (u32)LONG_LSW(service); + _prom->args.nargs = nargs; + _prom->args.nret = nret; + _prom->args.rets = (prom_arg_t *)&(_prom->args.args[nargs]); + + va_start(list, nret); + for (i=0; i < nargs ;i++) + _prom->args.args[i] = (prom_arg_t)LONG_LSW(va_arg(list, unsigned long)); + va_end(list); + + for (i=0; i < nret ;i++) + _prom->args.rets[i] = 0; + + enter_prom(&_prom->args); + + return (unsigned long)((nret > 0) ? _prom->args.rets[0] : 0); +} + + +static void __init +prom_exit() +{ + unsigned long offset = reloc_offset(); + + call_prom(RELOC("exit"), 0, 0); + + for (;;) /* should never get here */ + ; +} + +void __init +prom_enter(void) +{ + unsigned long offset = reloc_offset(); + + call_prom(RELOC("enter"), 0, 0); +} + + +void __init +prom_print(const char *msg) +{ + const char *p, *q; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + + if (_prom->stdout == 0) + return; + + for (p = msg; *p != 0; p = q) { + for (q = p; *q != 0 && *q != '\n'; ++q) + ; + if (q > p) + call_prom(RELOC("write"), 3, 1, _prom->stdout, + p, q - p); + if (*q != 0) { + ++q; + call_prom(RELOC("write"), 3, 1, _prom->stdout, + RELOC("\r\n"), 2); + } + } +} + +void +prom_print_hex(unsigned long val) +{ + int i, nibbles = sizeof(val)*2; + char buf[sizeof(val)*2+1]; + + for (i = nibbles-1; i >= 0; i--) { + buf[i] = (val & 0xf) + '0'; + if (buf[i] > '9') + buf[i] += ('a'-'0'-10); + val >>= 4; + } + buf[nibbles] = '\0'; + prom_print(buf); +} + +void +prom_print_nl(void) +{ + unsigned long offset = reloc_offset(); + prom_print(RELOC("\n")); +} + + +static unsigned long +prom_initialize_naca(unsigned long mem) +{ + phandle node; + char type[64]; + unsigned long num_cpus = 0; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + struct Naca *_naca = RELOC(naca); + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_initialize_naca: start...\n")); +#endif + + _naca->pftSize = 0; /* ilog2 of htab size. computed below. */ + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + + if (!strcmp(type, RELOC("cpu"))) { + num_cpus += 1; + + /* We're assuming *all* of the CPUs have the same + * d-cache and i-cache sizes... -Peter + */ + if ( num_cpus == 1 ) { + u32 size; + + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("d-cache-line-size"), + &size, sizeof(size)); + + _naca->dCacheL1LineSize = size; + _naca->dCacheL1LogLineSize = __ilog2(size); + _naca->dCacheL1LinesPerPage = PAGE_SIZE / size; + + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("i-cache-line-size"), + &size, sizeof(size)); + + _naca->iCacheL1LineSize = size; + _naca->iCacheL1LogLineSize = __ilog2(size); + _naca->iCacheL1LinesPerPage = PAGE_SIZE / size; + + if (RELOC(_machine) == _MACH_pSeriesLP) { + u32 pft_size[2]; + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("ibm,pft-size"), + &pft_size, sizeof(pft_size)); + /* pft_size[0] is the NUMA CEC cookie */ + _naca->pftSize = pft_size[1]; + } + } + } else if (!strcmp(type, RELOC("serial"))) { + phandle isa, pci; + struct isa_reg_property reg; + union pci_range ranges; + + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("ibm,aix-loc"), type, sizeof(type)); + + if (strcmp(type, RELOC("S1"))) + continue; + + call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), + ®, sizeof(reg)); + + isa = call_prom(RELOC("parent"), 1, 1, node); + if (!isa) + PROM_BUG(); + pci = call_prom(RELOC("parent"), 1, 1, isa); + if (!pci) + PROM_BUG(); + + call_prom(RELOC("getprop"), 4, 1, pci, RELOC("ranges"), + &ranges, sizeof(ranges)); + + if ( _prom->encode_phys_size == 32 ) + _naca->serialPortAddr = ranges.pci32.phys+reg.address; + else { + _naca->serialPortAddr = + ((((unsigned long)ranges.pci64.phys_hi) << 32) | + (ranges.pci64.phys_lo)) + reg.address; + } + } + } + + _naca->interrupt_controller = IC_INVALID; + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("name"), + type, sizeof(type)); + if (strcmp(type, RELOC("interrupt-controller"))) { + continue; + } + call_prom(RELOC("getprop"), 4, 1, node, RELOC("compatible"), + type, sizeof(type)); + if (strstr(type, RELOC("open-pic"))) { + _naca->interrupt_controller = IC_OPEN_PIC; + } else if (strstr(type, RELOC("ppc-xicp"))) { + _naca->interrupt_controller = IC_PPC_XIC; + } else { + prom_print(RELOC("prom: failed to recognize interrupt-controller\n")); + } + break; + } + + if (_naca->interrupt_controller == IC_INVALID) { + prom_print(RELOC("prom: failed to find interrupt-controller\n")); + PROM_BUG(); + } + + /* We gotta have at least 1 cpu... */ + if ( (_naca->processorCount = num_cpus) < 1 ) + PROM_BUG(); + + _naca->physicalMemorySize = lmb_phys_mem_size(); + + if (RELOC(_machine) == _MACH_pSeries) { + unsigned long rnd_mem_size, pteg_count; + + /* round mem_size up to next power of 2 */ + rnd_mem_size = 1UL << __ilog2(_naca->physicalMemorySize); + if (rnd_mem_size < _naca->physicalMemorySize) + rnd_mem_size <<= 1; + + /* # pages / 2 */ + pteg_count = (rnd_mem_size >> (12 + 1)); + + _naca->pftSize = __ilog2(pteg_count << 7); + } + + if (_naca->pftSize == 0) { + prom_print(RELOC("prom: failed to compute pftSize!\n")); + PROM_BUG(); + } + + /* + * Hardcode to GP size. I am not sure where to get this info + * in general, as there does not appear to be a slb-size OF + * entry. At least in Condor and earlier. DRENG + */ + _naca->slb_size = 64; + +#ifdef DEBUG_PROM + prom_print(RELOC("naca->processorCount = 0x")); + prom_print_hex(_naca->processorCount); + prom_print_nl(); + + prom_print(RELOC("naca->physicalMemorySize = 0x")); + prom_print_hex(_naca->physicalMemorySize); + prom_print_nl(); + + prom_print(RELOC("naca->pftSize = 0x")); + prom_print_hex(_naca->pftSize); + prom_print_nl(); + + prom_print(RELOC("naca->dCacheL1LineSize = 0x")); + prom_print_hex(_naca->dCacheL1LineSize); + prom_print_nl(); + + prom_print(RELOC("naca->dCacheL1LogLineSize = 0x")); + prom_print_hex(_naca->dCacheL1LogLineSize); + prom_print_nl(); + + prom_print(RELOC("naca->dCacheL1LinesPerPage = 0x")); + prom_print_hex(_naca->dCacheL1LinesPerPage); + prom_print_nl(); + + prom_print(RELOC("naca->iCacheL1LineSize = 0x")); + prom_print_hex(_naca->iCacheL1LineSize); + prom_print_nl(); + + prom_print(RELOC("naca->iCacheL1LogLineSize = 0x")); + prom_print_hex(_naca->iCacheL1LogLineSize); + prom_print_nl(); + + prom_print(RELOC("naca->iCacheL1LinesPerPage = 0x")); + prom_print_hex(_naca->iCacheL1LinesPerPage); + prom_print_nl(); + + prom_print(RELOC("naca->serialPortAddr = 0x")); + prom_print_hex(_naca->serialPortAddr); + prom_print_nl(); + + prom_print(RELOC("naca->interrupt_controller = 0x")); + prom_print_hex(_naca->interrupt_controller); + prom_print_nl(); + + prom_print(RELOC("_machine = 0x")); + prom_print_hex(RELOC(_machine)); + prom_print_nl(); + + prom_print(RELOC("prom_initialize_naca: end...\n")); +#endif + + return mem; +} + + +static unsigned long __init +prom_initialize_lmb(unsigned long mem) +{ + phandle node; + char type[64]; + unsigned long i, offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + union lmb_reg_property reg; + unsigned long mem_size, lmb_base, lmb_size; + unsigned long num_regs, bytes_per_reg = (_prom->encode_phys_size*2)/8; + +#ifdef CONFIG_MSCHUNKS +#if 1 + /* Fix me: 630 3G-4G IO hack here... -Peter (PPPBBB) */ + unsigned long io_base = 3UL<<30; + unsigned long io_size = 1UL<<30; + unsigned long have_630 = 1; /* assume we have a 630 */ + +#else + unsigned long io_base = ; + unsigned long io_size = ; +#endif +#endif + + lmb_init(); + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + + if (strcmp(type, RELOC("memory"))) + continue; + + num_regs = call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), + ®, sizeof(reg)) / bytes_per_reg; + + for (i=0; i < num_regs ;i++) { + if (_prom->encode_phys_size == 32) { + lmb_base = reg.addr32[i].address; + lmb_size = reg.addr32[i].size; + } else { + lmb_base = reg.addr64[i].address; + lmb_size = reg.addr64[i].size; + } + +#ifdef CONFIG_MSCHUNKS + if ( lmb_addrs_overlap(lmb_base,lmb_size, + io_base,io_size) ) { + /* If we really have dram here, then we don't + * have a 630! -Peter + */ + have_630 = 0; + } +#endif + if ( lmb_add(lmb_base, lmb_size) < 0 ) + prom_print(RELOC("Too many LMB's, discarding this one...\n")); + else + mem_size =+ lmb_size; + } + + } + +#ifdef CONFIG_MSCHUNKS + if ( have_630 && lmb_addrs_overlap(0,mem_size,io_base,io_size) ) + lmb_add_io(io_base, io_size); +#endif + + lmb_analyze(); + +#ifdef CONFIG_MSCHUNKS + mem = prom_initialize_mschunks(mem); +#endif /* CONFIG_MSCHUNKS */ + + return mem; +} + + +static unsigned long __init +prom_instantiate_rtas(unsigned long mem) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + struct rtas_t *_rtas = PTRRELOC(&rtas); + ihandle prom_rtas; + u32 getprop_rval; + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_instantiate_rtas: start...\n")); +#endif + prom_rtas = (ihandle)call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas")); + if (prom_rtas != (ihandle) -1) { + char hypertas_funcs[1024]; + int rc; + + if ((rc = call_prom(RELOC("getprop"), + 4, 1, prom_rtas, + RELOC("ibm,hypertas-functions"), + hypertas_funcs, + sizeof(hypertas_funcs))) > 0) { + RELOC(_machine) = _MACH_pSeriesLP; + } + + call_prom(RELOC("getprop"), + 4, 1, prom_rtas, + RELOC("rtas-size"), + &getprop_rval, + sizeof(getprop_rval)); + _rtas->size = getprop_rval; + prom_print(RELOC("instantiating rtas")); + if (_rtas->size != 0) { + /* + * Ask OF for some space for RTAS. + * Actually OF has bugs so we just arbitrarily + * use memory at the 6MB point. + */ + // The new code... + mem = PAGE_ALIGN(mem); + _rtas->base = mem + offset - KERNELBASE; + + mem += _rtas->size; + prom_print(RELOC(" at 0x")); + prom_print_hex(_rtas->base); + + prom_rtas = (ihandle)call_prom(RELOC("open"), + 1, 1, RELOC("/rtas")); + prom_print(RELOC("...")); + + if ((long)call_prom(RELOC("call-method"), 3, 2, + RELOC("instantiate-rtas"), + prom_rtas, + _rtas->base) >= 0) { + _rtas->entry = (long)_prom->args.rets[1]; + } + } + + if (_rtas->entry <= 0) { + prom_print(RELOC(" failed\n")); + } else { + prom_print(RELOC(" done\n")); + } + +#ifdef DEBUG_PROM + prom_print(RELOC("rtas->base = 0x")); + prom_print_hex(_rtas->base); + prom_print_nl(); + prom_print(RELOC("rtas->entry = 0x")); + prom_print_hex(_rtas->entry); + prom_print_nl(); + prom_print(RELOC("rtas->size = 0x")); + prom_print_hex(_rtas->size); + prom_print_nl(); +#endif + } +#ifdef DEBUG_PROM + prom_print(RELOC("prom_instantiate_rtas: end...\n")); +#endif + + return mem; +} + +unsigned long prom_strtoul(const char *cp) +{ + unsigned long result = 0,value; + + while (*cp) { + value = *cp-'0'; + result = result*10 + value; + cp++; + } + + return result; +} + + +#ifdef CONFIG_MSCHUNKS +static unsigned long +prom_initialize_mschunks(unsigned long mem) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct msChunks *_msChunks = PTRRELOC(&msChunks); + unsigned long i, pchunk = 0; + unsigned long mem_size = _lmb->memory.size; + unsigned long chunk_size = _lmb->memory.lcd_size; + +#if 1 + /* Fix me: 630 3G-4G IO hack here... -Peter (PPPBBB) */ + unsigned long io_base = 3UL<<30; + unsigned long io_size = 1UL<<30; + + for (i=0; i < _lmb->memory.cnt ;i++) { + unsigned long base = _lmb->memory.region[i].base; + unsigned long size = _lmb->memory.region[i].size; + if ( lmb_addrs_overlap(base,size,io_base,io_size) ) { + /* If we really have dram here, then we don't + * have a 630! -Peter + */ + io_base = mem_size; + io_size = 1; + break; + } + } +#else + unsigned long io_base = ; + unsigned long io_size = ; +#endif + + if ( lmb_addrs_overlap(0,mem_size,io_base,io_size) ) { + lmb_add(io_base, io_size); + lmb_reserve(io_base, io_size); + } + + mem = msChunks_alloc(mem, mem_size / chunk_size, chunk_size); + + for (i=0; i < _lmb->memory.cnt ;i++) { + unsigned long base = _lmb->memory.region[i].base; + unsigned long size = _lmb->memory.region[i].size; + unsigned long achunk = addr_to_chunk(base); + unsigned long end_achunk = addr_to_chunk(base+size); + _lmb->memory.region[i].physbase = chunk_to_addr(pchunk); + for (; achunk < end_achunk ;) { + PTRRELOC(_msChunks->abs)[pchunk++] = achunk++; + } + } + + return mem; +} +#endif /* CONFIG_MSCHUNKS */ + +void +prom_initialize_tce_table(void) +{ + phandle node; + ihandle phb_node; + unsigned long offset = reloc_offset(); + char compatible[64], path[64], type[64]; + unsigned long i, table = 0; + unsigned long base, vbase, align; + unsigned int minalign, minsize; + struct _of_tce_table *prom_tce_table = RELOC(of_tce_table); + unsigned long tce_entry, *tce_entryp; + +#ifdef DEBUG_PROM + prom_print(RELOC("starting prom_initialize_tce_table\n")); +#endif + + /* Search all nodes looking for PHBs. */ + for (node = 0; prom_next_node(&node); ) { + compatible[0] = 0; + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("compatible"), + compatible, sizeof(compatible)); + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + + if ((compatible[0] == 0) || + ((strstr(compatible, RELOC("python")) == NULL) && + (strstr(compatible, RELOC("Speedwagon")) == NULL))) { + continue; + } + if ((type[0] == 0) || (strstr(type, RELOC("pci")) == NULL)) { + continue; + } + + if (call_prom(RELOC("getprop"), 4, 1, node, + RELOC("tce-table-minalign"), &minalign, + sizeof(minalign)) < 0) { + minalign = 0; + } + + if (call_prom(RELOC("getprop"), 4, 1, node, + RELOC("tce-table-minsize"), &minsize, + sizeof(minsize)) < 0) { + minsize = 4UL << 20; + } + + /* Even though we read what OF wants, we just set the table + * size to 4 MB. This is enough to map 2GB of PCI DMA space. + * By doing this, we avoid the pitfalls of trying to DMA to + * MMIO space and the DMA alias hole. + */ + minsize = 4UL << 20; + + /* Align to the greater of the align or size */ + align = (minalign < minsize) ? minsize : minalign; + + /* Carve out storage for the TCE table. */ + base = lmb_alloc(minsize, align); + + if ( !base ) { + prom_print(RELOC("ERROR, cannot find space for TCE table.\n")); + prom_exit(); + } + + vbase = absolute_to_virt(base); + + /* Save away the TCE table attributes for later use. */ + prom_tce_table[table].node = node; + prom_tce_table[table].base = vbase; + prom_tce_table[table].size = minsize; + +#ifdef DEBUG_PROM + prom_print(RELOC("TCE table: 0x")); + prom_print_hex(table); + prom_print_nl(); + + prom_print(RELOC("\tnode = 0x")); + prom_print_hex(node); + prom_print_nl(); + + prom_print(RELOC("\tbase = 0x")); + prom_print_hex(vbase); + prom_print_nl(); + + prom_print(RELOC("\tsize = 0x")); + prom_print_hex(minsize); + prom_print_nl(); +#endif + + /* Initialize the table to have a one-to-one mapping + * over the allocated size. + */ + tce_entryp = (unsigned long *)base; + for (i = 0; i < (minsize >> 3) ;tce_entryp++, i++) { + tce_entry = (i << PAGE_SHIFT); + tce_entry |= 0x3; + *tce_entryp = tce_entry; + } + + /* Call OF to setup the TCE hardware */ + if (call_prom(RELOC("package-to-path"), 3, 1, node, + path, 255) <= 0) { + prom_print(RELOC("package-to-path failed\n")); + } else { + prom_print(RELOC("opened ")); + prom_print(path); + prom_print_nl(); + } + + phb_node = (ihandle)call_prom(RELOC("open"), 1, 1, path); + if ( (long)phb_node <= 0) { + prom_print(RELOC("open failed\n")); + } else { + prom_print(RELOC("open success\n")); + } + call_prom(RELOC("call-method"), 6, 0, + RELOC("set-64-bit-addressing"), + phb_node, + -1, + minsize, + base & 0xffffffff, + (base >> 32) & 0xffffffff); + call_prom(RELOC("close"), 1, 0, phb_node); + + table++; + } + + /* Flag the first invalid entry */ + prom_tce_table[table].node = 0; +#ifdef DEBUG_PROM + prom_print(RELOC("ending prom_initialize_tce_table\n")); +#endif +} + +/* + * With CHRP SMP we need to use the OF to start the other + * processors so we can't wait until smp_boot_cpus (the OF is + * trashed by then) so we have to put the processors into + * a holding pattern controlled by the kernel (not OF) before + * we destroy the OF. + * + * This uses a chunk of low memory, puts some holding pattern + * code there and sends the other processors off to there until + * smp_boot_cpus tells them to do something. The holding pattern + * checks that address until its cpu # is there, when it is that + * cpu jumps to __secondary_start(). smp_boot_cpus() takes care + * of setting those values. + * + * We also use physical address 0x4 here to tell when a cpu + * is in its holding pattern code. + * + * Fixup comment... DRENG / PPPBBB - Peter + * + * -- Cort + */ +static void +prom_hold_cpus(unsigned long mem) +{ + unsigned long i; + unsigned int reg; + phandle node; + unsigned long offset = reloc_offset(); + char type[64], *path; + int cpuid = 0; + extern void __secondary_hold(void); + extern unsigned long __secondary_hold_spinloop; + extern unsigned long __secondary_hold_acknowledge; + unsigned long *spinloop = __v2a(&__secondary_hold_spinloop); + unsigned long *acknowledge = __v2a(&__secondary_hold_acknowledge); + unsigned long secondary_hold = (unsigned long)__v2a(*PTRRELOC((unsigned long *)__secondary_hold)); + struct Naca *_naca = RELOC(naca); + struct Paca *_xPaca = PTRRELOC(&xPaca[0]); + struct prom_t *_prom = PTRRELOC(&prom); + + /* Initially, we must have one active CPU. */ + _naca->processorCount = 1; + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_hold_cpus: start...\n")); + prom_print(RELOC(" 1) spinloop = 0x")); + prom_print_hex(spinloop); + prom_print_nl(); + prom_print(RELOC(" 1) *spinloop = 0x")); + prom_print_hex(*spinloop); + prom_print_nl(); + prom_print(RELOC(" 1) acknowledge = 0x")); + prom_print_hex(acknowledge); + prom_print_nl(); + prom_print(RELOC(" 1) *acknowledge = 0x")); + prom_print_hex(*acknowledge); + prom_print_nl(); + prom_print(RELOC(" 1) secondary_hold = 0x")); + prom_print_hex(secondary_hold); + prom_print_nl(); +#endif + + /* Set the common spinloop variable, so all of the secondary cpus + * will block when they are awakened from their OF spinloop. + * This must occur for both SMP and non SMP kernels, since OF will + * be trashed when we move the kernel. + */ + *spinloop = 0; + +#ifdef CONFIG_HMT + for (i=0; i < NR_CPUS; i++) { + RELOC(hmt_thread_data)[i].pir = 0xdeadbeef; + } +#endif + /* look for cpus */ + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("cpu")) != 0) + continue; + + /* Skip non-configured cpus. */ + call_prom(RELOC("getprop"), 4, 1, node, RELOC("status"), + type, sizeof(type)); + if (strcmp(type, RELOC("okay")) != 0) + continue; + + reg = -1; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), + ®, sizeof(reg)); + + /* Only need to start secondary procs, not ourself. */ + if ( reg == _prom->cpu ) + continue; + + path = (char *) mem; + memset(path, 0, 256); + if ((long) call_prom(RELOC("package-to-path"), 3, 1, + node, path, 255) < 0) + continue; + + cpuid++; + +#ifdef DEBUG_PROM + prom_print_nl(); + prom_print(RELOC("cpuid = 0x")); + prom_print_hex(cpuid); + prom_print_nl(); + prom_print(RELOC("cpu hw idx = 0x")); + prom_print_hex(reg); + prom_print_nl(); +#endif + _xPaca[cpuid].xHwProcNum = reg; + + prom_print(RELOC("starting cpu ")); + prom_print(path); + + /* Init the acknowledge var which will be reset by + * the secondary cpu when it awakens from its OF + * spinloop. + */ + *acknowledge = (unsigned long)-1; + +#ifdef DEBUG_PROM + prom_print(RELOC(" 3) spinloop = 0x")); + prom_print_hex(spinloop); + prom_print_nl(); + prom_print(RELOC(" 3) *spinloop = 0x")); + prom_print_hex(*spinloop); + prom_print_nl(); + prom_print(RELOC(" 3) acknowledge = 0x")); + prom_print_hex(acknowledge); + prom_print_nl(); + prom_print(RELOC(" 3) *acknowledge = 0x")); + prom_print_hex(*acknowledge); + prom_print_nl(); + prom_print(RELOC(" 3) secondary_hold = 0x")); + prom_print_hex(secondary_hold); + prom_print_nl(); + prom_print(RELOC(" 3) cpuid = 0x")); + prom_print_hex(cpuid); + prom_print_nl(); +#endif + call_prom(RELOC("start-cpu"), 3, 0, node, secondary_hold, cpuid); + prom_print(RELOC("...")); + for ( i = 0 ; (i < 100000000) && + (*acknowledge == ((unsigned long)-1)); i++ ) ; +#ifdef DEBUG_PROM + { + unsigned long *p = 0x0; + prom_print(RELOC(" 4) 0x0 = 0x")); + prom_print_hex(*p); + prom_print_nl(); + } +#endif + if (*acknowledge == cpuid) { + prom_print(RELOC("ok\n")); + /* Set the number of active processors. */ + _naca->processorCount++; + } else { + prom_print(RELOC("failed: ")); + prom_print_hex(*acknowledge); + prom_print_nl(); + } + } +#ifdef CONFIG_HMT + /* Only enable HMT on processors that provide support. */ + if (__is_processor(PV_PULSAR) || + __is_processor(PV_ICESTAR) || + __is_processor(PV_SSTAR)) { + prom_print(RELOC(" starting secondary threads\n")); + + for (i=0; i < _naca->processorCount ;i++) { + unsigned long threadid = _naca->processorCount*2-1-i; + + if (i == 0) { + unsigned long pir = _get_PIR(); + if (__is_processor(PV_PULSAR)) { + RELOC(hmt_thread_data)[i].pir = + pir & 0x1f; + } else { + RELOC(hmt_thread_data)[i].pir = + pir & 0x3ff; + } + } + + RELOC(hmt_thread_data)[i].threadid = threadid; +#ifdef DEBUG_PROM + prom_print(RELOC(" cpuid 0x")); + prom_print_hex(i); + prom_print(RELOC(" maps to threadid 0x")); + prom_print_hex(threadid); + prom_print_nl(); + prom_print(RELOC(" pir 0x")); + prom_print_hex(RELOC(hmt_thread_data)[i].pir); + prom_print_nl(); +#endif + _xPaca[threadid].xHwProcNum = _xPaca[i].xHwProcNum+1; + } + _naca->processorCount *= 2; + } else { + prom_print(RELOC("Processor is not HMT capable\n")); + } +#endif + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_hold_cpus: end...\n")); +#endif +} + + +/* + * We enter here early on, when the Open Firmware prom is still + * handling exceptions and the MMU hash table for us. + */ + +unsigned long __init +prom_init(unsigned long r3, unsigned long r4, unsigned long pp, + unsigned long r6, unsigned long r7, yaboot_debug_t *yaboot) +{ + int chrp = 0; + unsigned long mem; + ihandle prom_mmu, prom_op, prom_root, prom_cpu; + phandle cpu_pkg; + unsigned long offset = reloc_offset(); + long l; + char *p, *d; + unsigned long phys; + u32 getprop_rval; + struct Naca *_naca = RELOC(naca); + struct Paca *_xPaca = PTRRELOC(&xPaca[0]); + struct prom_t *_prom = PTRRELOC(&prom); + + /* Default machine type. */ + RELOC(_machine) = _MACH_pSeries; + /* Reset klimit to take into account the embedded system map */ + if (RELOC(embedded_sysmap_end)) + RELOC(klimit) = __va(PAGE_ALIGN(RELOC(embedded_sysmap_end))); + + /* Get a handle to the prom entry point before anything else */ + _prom->entry = pp; + _prom->bi_recs = prom_bi_rec_verify((struct bi_record *)r6); + if ( _prom->bi_recs != NULL ) { + RELOC(klimit) = PTRUNRELOC((unsigned long)_prom->bi_recs + _prom->bi_recs->data[1]); + } + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->dummy,offset>>32,offset&0xffffffff); + call_yaboot(yaboot->printf, RELOC("offset = 0x%08x%08x\n"), LONG_MSW(offset), LONG_LSW(offset)); +#endif + + /* Default */ + phys = KERNELBASE - offset; + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("phys = 0x%08x%08x\n"), LONG_MSW(phys), LONG_LSW(phys)); +#endif + + +#ifdef DEBUG_YABOOT + _prom->yaboot = yaboot; + call_yaboot(yaboot->printf, RELOC("pp = 0x%08x%08x\n"), LONG_MSW(pp), LONG_LSW(pp)); + call_yaboot(yaboot->printf, RELOC("prom = 0x%08x%08x\n"), LONG_MSW(_prom->entry), LONG_LSW(_prom->entry)); +#endif + + /* First get a handle for the stdout device */ + _prom->chosen = (ihandle)call_prom(RELOC("finddevice"), 1, 1, + RELOC("/chosen")); + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("prom->chosen = 0x%08x%08x\n"), LONG_MSW(_prom->chosen), LONG_LSW(_prom->chosen)); +#endif + + if ((long)_prom->chosen <= 0) + prom_exit(); + + if ((long)call_prom(RELOC("getprop"), 4, 1, _prom->chosen, + RELOC("stdout"), &getprop_rval, + sizeof(getprop_rval)) <= 0) + prom_exit(); + + _prom->stdout = (ihandle)(unsigned long)getprop_rval; + +#ifdef DEBUG_YABOOT + if (_prom->stdout == 0) { + call_yaboot(yaboot->printf, RELOC("prom->stdout = 0x%08x%08x\n"), LONG_MSW(_prom->stdout), LONG_LSW(_prom->stdout)); + } + + call_yaboot(yaboot->printf, RELOC("prom->stdout = 0x%08x%08x\n"), LONG_MSW(_prom->stdout), LONG_LSW(_prom->stdout)); +#endif + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("Location: 0x11\n")); +#endif + + mem = RELOC(klimit) - offset; +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("Location: 0x11b\n")); +#endif + + /* Get the full OF pathname of the stdout device */ + p = (char *) mem; + memset(p, 0, 256); + call_prom(RELOC("instance-to-path"), 3, 1, _prom->stdout, p, 255); + RELOC(of_stdout_device) = PTRUNRELOC(p); + mem += strlen(p) + 1; + + getprop_rval = 1; + prom_root = (ihandle)call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); + if (prom_root != (ihandle)-1) { + call_prom(RELOC("getprop"), 4, 1, + prom_root, RELOC("#size-cells"), + &getprop_rval, sizeof(getprop_rval)); + } + _prom->encode_phys_size = (getprop_rval==1) ? 32 : 64; + +#ifdef DEBUG_PROM + prom_print(RELOC("DRENG: Detect OF version...\n")); +#endif + /* Find the OF version */ + prom_op = (ihandle)call_prom(RELOC("finddevice"), 1, 1, RELOC("/openprom")); + if (prom_op != (ihandle)-1) { + char model[64]; + long sz; + sz = (long)call_prom(RELOC("getprop"), 4, 1, prom_op, + RELOC("model"), model, 64); + if (sz > 0) { + char *c; + /* hack to skip the ibm chrp firmware # */ + if ( strncmp(model,RELOC("IBM"),3) ) { + for (c = model; *c; c++) + if (*c >= '0' && *c <= '9') { + _prom->version = *c - '0'; + break; + } + } + else + chrp = 1; + } + } + if (_prom->version >= 3) + prom_print(RELOC("OF Version 3 detected.\n")); + + + /* Determine which cpu is actually running right _now_ */ + if ((long)call_prom(RELOC("getprop"), 4, 1, _prom->chosen, + RELOC("cpu"), &getprop_rval, + sizeof(getprop_rval)) <= 0) + prom_exit(); + + prom_cpu = (ihandle)(unsigned long)getprop_rval; + cpu_pkg = call_prom(RELOC("instance-to-package"), 1, 1, prom_cpu); + call_prom(RELOC("getprop"), 4, 1, + cpu_pkg, RELOC("reg"), + &getprop_rval, sizeof(getprop_rval)); + _prom->cpu = (int)(unsigned long)getprop_rval; + _xPaca[0].xHwProcNum = _prom->cpu; + +#ifdef DEBUG_PROM + prom_print(RELOC("Booting CPU hw index = 0x")); + prom_print_hex(_prom->cpu); + prom_print_nl(); +#endif + + /* Get the boot device and translate it to a full OF pathname. */ + p = (char *) mem; + l = (long) call_prom(RELOC("getprop"), 4, 1, _prom->chosen, + RELOC("bootpath"), p, 1<<20); + if (l > 0) { + p[l] = 0; /* should already be null-terminated */ + RELOC(bootpath) = PTRUNRELOC(p); + mem += l + 1; + d = (char *) mem; + *d = 0; + call_prom(RELOC("canon"), 3, 1, p, d, 1<<20); + RELOC(bootdevice) = PTRUNRELOC(d); + mem = DOUBLEWORD_ALIGN(mem + strlen(d) + 1); + } + + mem = prom_initialize_lmb(mem); + + mem = prom_bi_rec_reserve(mem); + + mem = prom_instantiate_rtas(mem); + + /* Initialize some system info into the Naca early... */ + mem = prom_initialize_naca(mem); + + /* If we are on an SMP machine, then we *MUST* do the + * following, regardless of whether we have an SMP + * kernel or not. + */ + if ( _naca->processorCount > 1 ) + prom_hold_cpus(mem); + + mem = check_display(mem); + +#ifdef DEBUG_PROM + prom_print(RELOC("copying OF device tree...\n")); +#endif + mem = copy_device_tree(mem); + + RELOC(klimit) = mem + offset; + + lmb_reserve(0, __pa(RELOC(klimit))); + + if (RELOC(_machine) == _MACH_pSeries) + prom_initialize_tce_table(); + + if ((long) call_prom(RELOC("getprop"), 4, 1, + _prom->chosen, + RELOC("mmu"), + &getprop_rval, + sizeof(getprop_rval)) <= 0) { + prom_print(RELOC(" no MMU found\n")); + prom_exit(); + } + + /* We assume the phys. address size is 3 cells */ + RELOC(prom_mmu) = (ihandle)(unsigned long)getprop_rval; + + if ((long)call_prom(RELOC("call-method"), 4, 4, + RELOC("translate"), + prom_mmu, + (void *)(KERNELBASE - offset), + (void *)1) != 0) { + prom_print(RELOC(" (translate failed) ")); + } else { + prom_print(RELOC(" (translate ok) ")); + phys = (unsigned long)_prom->args.rets[3]; + } + + /* If OpenFirmware version >= 3, then use quiesce call */ + if (_prom->version >= 3) { + prom_print(RELOC("Calling quiesce ...\n")); + call_prom(RELOC("quiesce"), 0, 0); + phys = KERNELBASE - offset; + } + + prom_print(RELOC("returning from prom_init\n")); + return phys; +} + + +static int +prom_set_color(ihandle ih, int i, int r, int g, int b) +{ + unsigned long offset = reloc_offset(); + + return (int)(long)call_prom(RELOC("call-method"), 6, 1, + RELOC("color!"), + ih, + (void *)(long) i, + (void *)(long) b, + (void *)(long) g, + (void *)(long) r ); +} + +/* + * If we have a display that we don't know how to drive, + * we will want to try to execute OF's open method for it + * later. However, OF will probably fall over if we do that + * we've taken over the MMU. + * So we check whether we will need to open the display, + * and if so, open it now. + */ +static unsigned long __init +check_display(unsigned long mem) +{ + phandle node; + ihandle ih; + int i; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + char type[64], *path; + static unsigned char default_colors[] = { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, + 0x00, 0xaa, 0x00, + 0x00, 0xaa, 0xaa, + 0xaa, 0x00, 0x00, + 0xaa, 0x00, 0xaa, + 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, + 0x55, 0x55, 0xff, + 0x55, 0xff, 0x55, + 0x55, 0xff, 0xff, + 0xff, 0x55, 0x55, + 0xff, 0x55, 0xff, + 0xff, 0xff, 0x55, + 0xff, 0xff, 0xff + }; + + _prom->disp_node = 0; + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("display")) != 0) + continue; + /* It seems OF doesn't null-terminate the path :-( */ + path = (char *) mem; + memset(path, 0, 256); + if ((long) call_prom(RELOC("package-to-path"), 3, 1, + node, path, 255) < 0) + continue; + prom_print(RELOC("opening display ")); + prom_print(path); + ih = (ihandle)call_prom(RELOC("open"), 1, 1, path); + if (ih == (ihandle)0 || ih == (ihandle)-1) { + prom_print(RELOC("... failed\n")); + continue; + } + prom_print(RELOC("... ok\n")); + + if (_prom->disp_node == 0) + _prom->disp_node = (ihandle)(unsigned long)node; + + /* Setup a useable color table when the appropriate + * method is available. Should update this to set-colors */ + for (i = 0; i < 32; i++) + if (prom_set_color(ih, i, RELOC(default_colors)[i*3], + RELOC(default_colors)[i*3+1], + RELOC(default_colors)[i*3+2]) != 0) + break; + +#ifdef CONFIG_FB + for (i = 0; i < LINUX_LOGO_COLORS; i++) + if (prom_set_color(ih, i + 32, + RELOC(linux_logo_red)[i], + RELOC(linux_logo_green)[i], + RELOC(linux_logo_blue)[i]) != 0) + break; +#endif /* CONFIG_FB */ + + /* + * If this display is the device that OF is using for stdout, + * move it to the front of the list. + */ + mem += strlen(path) + 1; + i = RELOC(prom_num_displays)++; + if (RELOC(of_stdout_device) != 0 && i > 0 + && strcmp(PTRRELOC(RELOC(of_stdout_device)), path) == 0) { + for (; i > 0; --i) + RELOC(prom_display_paths[i]) = RELOC(prom_display_paths[i-1]); + } + RELOC(prom_display_paths[i]) = PTRUNRELOC(path); + if (RELOC(prom_num_displays) >= FB_MAX) + break; + } + return DOUBLEWORD_ALIGN(mem); +} + +void +virt_irq_init(void) +{ + int i; + for (i = 0; i < NR_IRQS; i++) + virt_irq_to_real_map[i] = UNDEFINED_IRQ; + for (i = 0; i < NR_HW_IRQS; i++) + real_irq_to_virt_map[i] = UNDEFINED_IRQ; +} + +/* Create a mapping for a real_irq if it doesn't already exist. + * Return the virtual irq as a convenience. + */ +unsigned long +virt_irq_create_mapping(unsigned long real_irq) +{ + unsigned long virq; + if (naca->interrupt_controller == IC_OPEN_PIC) + return real_irq; /* no mapping for openpic (for now) */ + virq = real_irq_to_virt(real_irq); + if (virq == UNDEFINED_IRQ) { + /* Assign a virtual IRQ number */ + if (real_irq < NR_IRQS && virt_irq_to_real(real_irq) == UNDEFINED_IRQ) { + /* A 1-1 mapping will work. */ + virq = real_irq; + } else { + while (last_virt_irq < NR_IRQS && + virt_irq_to_real(++last_virt_irq) != UNDEFINED_IRQ) + /* skip irq's in use */; + if (last_virt_irq >= NR_IRQS) + panic("Too many IRQs are required on this system. NR_IRQS=%d\n", NR_IRQS); + virq = last_virt_irq; + } + virt_irq_to_real_map[virq] = real_irq; + real_irq_to_virt_map[real_irq] = virq; + } + return virq; +} + + +static int __init +prom_next_node(phandle *nodep) +{ + phandle node; + unsigned long offset = reloc_offset(); + + if ((node = *nodep) != 0 + && (*nodep = call_prom(RELOC("child"), 1, 1, node)) != 0) + return 1; + if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) + return 1; + for (;;) { + if ((node = call_prom(RELOC("parent"), 1, 1, node)) == 0) + return 0; + if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) + return 1; + } +} + +/* + * Make a copy of the device tree from the PROM. + */ +static unsigned long __init +copy_device_tree(unsigned long mem_start) +{ + phandle root; + unsigned long new_start; + struct device_node **allnextp; + unsigned long offset = reloc_offset(); + unsigned long mem_end = mem_start + (8<<20); + + root = call_prom(RELOC("peer"), 1, 1, (phandle)0); + if (root == (phandle)0) { + prom_print(RELOC("couldn't get device tree root\n")); + prom_exit(); + } + allnextp = &RELOC(allnodes); + mem_start = DOUBLEWORD_ALIGN(mem_start); + new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp); + *allnextp = 0; + return new_start; +} + +__init +static unsigned long +inspect_node(phandle node, struct device_node *dad, + unsigned long mem_start, unsigned long mem_end, + struct device_node ***allnextpp) +{ + int l; + phandle child; + struct device_node *np; + struct property *pp, **prev_propp; + char *prev_name, *namep; + unsigned char *valp; + unsigned long offset = reloc_offset(); + + np = (struct device_node *) mem_start; + mem_start += sizeof(struct device_node); + memset(np, 0, sizeof(*np)); + np->node = node; + **allnextpp = PTRUNRELOC(np); + *allnextpp = &np->allnext; + if (dad != 0) { + np->parent = PTRUNRELOC(dad); + /* we temporarily use the `next' field as `last_child'. */ + if (dad->next == 0) + dad->child = PTRUNRELOC(np); + else + dad->next->sibling = PTRUNRELOC(np); + dad->next = np; + } + + /* get and store all properties */ + prev_propp = &np->properties; + prev_name = RELOC(""); + for (;;) { + pp = (struct property *) mem_start; + namep = (char *) (pp + 1); + pp->name = PTRUNRELOC(namep); + if ((long) call_prom(RELOC("nextprop"), 3, 1, node, prev_name, + namep) <= 0) + break; + mem_start = DOUBLEWORD_ALIGN((unsigned long)namep + strlen(namep) + 1); + prev_name = namep; + valp = (unsigned char *) mem_start; + pp->value = PTRUNRELOC(valp); + pp->length = (int)(long) + call_prom(RELOC("getprop"), 4, 1, node, namep, + valp, mem_end - mem_start); + if (pp->length < 0) + continue; + mem_start = DOUBLEWORD_ALIGN(mem_start + pp->length); + *prev_propp = PTRUNRELOC(pp); + prev_propp = &pp->next; + } + *prev_propp = 0; + + /* get the node's full name */ + l = (long) call_prom(RELOC("package-to-path"), 3, 1, node, + (char *) mem_start, mem_end - mem_start); + if (l >= 0) { + np->full_name = PTRUNRELOC((char *) mem_start); + *(char *)(mem_start + l) = 0; + mem_start = DOUBLEWORD_ALIGN(mem_start + l + 1); + } + + /* do all our children */ + child = call_prom(RELOC("child"), 1, 1, node); + while (child != (phandle)0) { + mem_start = inspect_node(child, np, mem_start, mem_end, + allnextpp); + child = call_prom(RELOC("peer"), 1, 1, child); + } + + return mem_start; +} + +/* + * finish_device_tree is called once things are running normally + * (i.e. with text and data mapped to the address they were linked at). + * It traverses the device tree and fills in the name, type, + * {n_}addrs and {n_}intrs fields of each node. + */ +void __init +finish_device_tree(void) +{ + unsigned long mem = klimit; + + virt_irq_init(); + + mem = finish_node(allnodes, mem, NULL, 0, 0); + dev_tree_size = mem - (unsigned long) allnodes; + + mem = _ALIGN(mem, PAGE_SIZE); + lmb_reserve(__pa(klimit), mem-klimit); + + klimit = mem; + + rtas.dev = find_devices("rtas"); +} + +static unsigned long __init +finish_node(struct device_node *np, unsigned long mem_start, + interpret_func *ifunc, int naddrc, int nsizec) +{ + struct device_node *child; + int *ip; + + np->name = get_property(np, "name", 0); + np->type = get_property(np, "device_type", 0); + + /* get the device addresses and interrupts */ + if (ifunc != NULL) { + mem_start = ifunc(np, mem_start, naddrc, nsizec); + } + mem_start = finish_node_interrupts(np, mem_start); + + /* Look for #address-cells and #size-cells properties. */ + ip = (int *) get_property(np, "#address-cells", 0); + if (ip != NULL) + naddrc = *ip; + ip = (int *) get_property(np, "#size-cells", 0); + if (ip != NULL) + nsizec = *ip; + + /* the f50 sets the name to 'display' and 'compatible' to what we + * expect for the name -- Cort + */ + ifunc = NULL; + if (!strcmp(np->name, "display")) + np->name = get_property(np, "compatible", 0); + + if (!strcmp(np->name, "device-tree") || np->parent == NULL) + ifunc = interpret_root_props; + else if (np->type == 0) + ifunc = NULL; + else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) + ifunc = interpret_pci_props; + else if (!strcmp(np->type, "isa")) + ifunc = interpret_isa_props; + + for (child = np->child; child != NULL; child = child->sibling) + mem_start = finish_node(child, mem_start, ifunc, + naddrc, nsizec); + + return mem_start; +} + +/* This routine walks the interrupt tree for a given device node and gather + * all necessary informations according to the draft interrupt mapping + * for CHRP. The current version was only tested on Apple "Core99" machines + * and may not handle cascaded controllers correctly. + */ +__init +static unsigned long +finish_node_interrupts(struct device_node *np, unsigned long mem_start) +{ + /* Finish this node */ + unsigned int *isizep, *asizep, *interrupts, *map, *map_mask, *reg; + phandle *parent, map_parent; + struct device_node *node, *parent_node; + int l, isize, ipsize, asize, map_size, regpsize; + + /* Currently, we don't look at all nodes with no "interrupts" property */ + + interrupts = (unsigned int *)get_property(np, "interrupts", &l); + if (interrupts == NULL) + return mem_start; + ipsize = l>>2; + + reg = (unsigned int *)get_property(np, "reg", &l); + regpsize = l>>2; + + /* We assume default interrupt cell size is 1 (bugus ?) */ + isize = 1; + node = np; + + do { + /* We adjust the cell size if the current parent contains an #interrupt-cells + * property */ + isizep = (unsigned int *)get_property(node, "#interrupt-cells", &l); + if (isizep) + isize = *isizep; + + /* We don't do interrupt cascade (ISA) for now, we stop on the first + * controller found + */ + if (get_property(node, "interrupt-controller", &l)) { + int i,j; + + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = ipsize / isize; + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = openpic_to_irq(virt_irq_create_mapping(*interrupts++)); + np->intrs[i].sense = 1; + if (isize > 1) + np->intrs[i].sense = *interrupts++; + for (j=2; j>2; + map_mask = (unsigned int *)get_property(node, "interrupt-map-mask", &l); + asizep = (unsigned int *)get_property(node, "#address-cells", &l); + if (asizep && l == sizeof(unsigned int)) + asize = *asizep; + else + asize = 0; + found = 0; + while (map_size>0 && !found) { + found = 1; + for (i=0; i=regpsize) || ((mask & *map) != (mask & reg[i]))) + found = 0; + map++; + map_size--; + } + for (i=0; iparent; + } while (node); + + return mem_start; +} + +int +prom_n_addr_cells(struct device_node* np) +{ + int* ip; + do { + if (np->parent) + np = np->parent; + ip = (int *) get_property(np, "#address-cells", 0); + if (ip != NULL) + return *ip; + } while (np->parent); + /* No #address-cells property for the root node, default to 1 */ + return 1; +} + +int +prom_n_size_cells(struct device_node* np) +{ + int* ip; + do { + if (np->parent) + np = np->parent; + ip = (int *) get_property(np, "#size-cells", 0); + if (ip != NULL) + return *ip; + } while (np->parent); + /* No #size-cells property for the root node, default to 1 */ + return 1; +} + +static unsigned long __init +interpret_pci_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct address_range *adr; + struct pci_reg_property *pci_addrs; + int i, l; + + pci_addrs = (struct pci_reg_property *) + get_property(np, "assigned-addresses", &l); + if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct pci_reg_property)) >= 0) { + adr[i].space = pci_addrs[i].addr.a_hi; + adr[i].address = pci_addrs[i].addr.a_lo; + adr[i].size = pci_addrs[i].size_lo; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + return mem_start; +} + +static unsigned long __init +interpret_isa_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct isa_reg_property *rp; + struct address_range *adr; + int i, l; + + rp = (struct isa_reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct isa_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = rp[i].space; + adr[i].address = rp[i].address + + (adr[i].space? 0: _ISA_MEM_BASE); + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + return mem_start; +} + +static unsigned long __init +interpret_root_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct address_range *adr; + int i, l; + unsigned int *rp; + int rpsize = (naddrc + nsizec) * sizeof(unsigned int); + + rp = (unsigned int *) get_property(np, "reg", &l); + if (rp != 0 && l >= rpsize) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= rpsize) >= 0) { + adr[i].space = 0; + adr[i].address = rp[naddrc - 1]; + adr[i].size = rp[naddrc + nsizec - 1]; + ++i; + rp += naddrc + nsizec; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + return mem_start; +} + +/* + * Work out the sense (active-low level / active-high edge) + * of each interrupt from the device tree. + */ +void __init +prom_get_irq_senses(unsigned char *senses, int off, int max) +{ + struct device_node *np; + int i, j; + + /* default to level-triggered */ + memset(senses, 1, max - off); + + for (np = allnodes; np != 0; np = np->allnext) { + for (j = 0; j < np->n_intrs; j++) { + i = np->intrs[j].line; + if (i >= off && i < max) + senses[i-off] = np->intrs[j].sense; + } + } +} + +/* + * Construct and return a list of the device_nodes with a given name. + */ +struct device_node * +find_devices(const char *name) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (np->name != 0 && strcasecmp(np->name, name) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* + * Construct and return a list of the device_nodes with a given type. + */ +struct device_node * +find_type_devices(const char *type) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (np->type != 0 && strcasecmp(np->type, type) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* + * Returns all nodes linked together + */ +struct device_node * __openfirmware +find_all_nodes(void) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + *prevp = np; + prevp = &np->next; + } + *prevp = 0; + return head; +} + +/* Checks if the given "compat" string matches one of the strings in + * the device's "compatible" property + */ +int +device_is_compatible(struct device_node *device, const char *compat) +{ + const char* cp; + int cplen, l; + + cp = (char *) get_property(device, "compatible", &cplen); + if (cp == NULL) + return 0; + while (cplen > 0) { + if (strncasecmp(cp, compat, strlen(compat)) == 0) + return 1; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + } + + return 0; +} + + +/* + * Indicates whether the root node has a given value in its + * compatible property. + */ +int +machine_is_compatible(const char *compat) +{ + struct device_node *root; + + root = find_path_device("/"); + if (root == 0) + return 0; + return device_is_compatible(root, compat); +} + +/* + * Construct and return a list of the device_nodes with a given type + * and compatible property. + */ +struct device_node * +find_compatible_devices(const char *type, const char *compat) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (type != NULL + && !(np->type != 0 && strcasecmp(np->type, type) == 0)) + continue; + if (device_is_compatible(np, compat)) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* + * Find the device_node with a given full_name. + */ +struct device_node * +find_path_device(const char *path) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) + return np; + return NULL; +} + +/* + * Find the device_node with a given phandle. + */ +static struct device_node * __init +find_phandle(phandle ph) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->node == ph) + return np; + return NULL; +} + +/* + * Find a property with a given name for a given node + * and return the value. + */ +unsigned char * +get_property(struct device_node *np, const char *name, int *lenp) +{ + struct property *pp; + + for (pp = np->properties; pp != 0; pp = pp->next) + if (strcmp(pp->name, name) == 0) { + if (lenp != 0) + *lenp = pp->length; + return pp->value; + } + return 0; +} + +/* + * Add a property to a node + */ +void __openfirmware +prom_add_property(struct device_node* np, struct property* prop) +{ + struct property **next = &np->properties; + + prop->next = NULL; + while (*next) + next = &(*next)->next; + *next = prop; +} + +#if 0 +void __openfirmware +print_properties(struct device_node *np) +{ + struct property *pp; + char *cp; + int i, n; + + for (pp = np->properties; pp != 0; pp = pp->next) { + printk(KERN_INFO "%s", pp->name); + for (i = strlen(pp->name); i < 16; ++i) + printk(" "); + cp = (char *) pp->value; + for (i = pp->length; i > 0; --i, ++cp) + if ((i > 1 && (*cp < 0x20 || *cp > 0x7e)) + || (i == 1 && *cp != 0)) + break; + if (i == 0 && pp->length > 1) { + /* looks like a string */ + printk(" %s\n", (char *) pp->value); + } else { + /* dump it in hex */ + n = pp->length; + if (n > 64) + n = 64; + if (pp->length % 4 == 0) { + unsigned int *p = (unsigned int *) pp->value; + + n /= 4; + for (i = 0; i < n; ++i) { + if (i != 0 && (i % 4) == 0) + printk("\n "); + printk(" %08x", *p++); + } + } else { + unsigned char *bp = pp->value; + + for (i = 0; i < n; ++i) { + if (i != 0 && (i % 16) == 0) + printk("\n "); + printk(" %02x", *bp++); + } + } + printk("\n"); + if (pp->length > 64) + printk(" ... (length = %d)\n", + pp->length); + } + } +} +#endif + + +void __init +abort() +{ +#ifdef CONFIG_XMON + xmon(NULL); +#endif + for (;;) + prom_exit(); +} + + +/* Verify bi_recs are good */ +static struct bi_record * +prom_bi_rec_verify(struct bi_record *bi_recs) +{ + struct bi_record *first, *last; + + if ( bi_recs == NULL || bi_recs->tag != BI_FIRST ) + return NULL; + + last = (struct bi_record *)bi_recs->data[0]; + if ( last == NULL || last->tag != BI_LAST ) + return NULL; + + first = (struct bi_record *)last->data[0]; + if ( first == NULL || first != bi_recs ) + return NULL; + + return bi_recs; +} + +static unsigned long +prom_bi_rec_reserve(unsigned long mem) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + struct bi_record *rec; + + if ( _prom->bi_recs != NULL) { + + for ( rec=_prom->bi_recs; + rec->tag != BI_LAST; + rec=bi_rec_next(rec) ) { + switch (rec->tag) { +#ifdef CONFIG_BLK_DEV_INITRD + case BI_INITRD: + lmb_reserve(rec->data[0], rec->data[1]); + break; +#endif /* CONFIG_BLK_DEV_INITRD */ + } + } + /* The next use of this field will be after relocation + * is enabled, so convert this physical address into a + * virtual address. + */ + _prom->bi_recs = PTRUNRELOC(_prom->bi_recs); + } + + return mem; +} + diff -Nru a/arch/ppc64/kernel/ptrace.c b/arch/ppc64/kernel/ptrace.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/ptrace.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,337 @@ +/* + * linux/arch/ppc/kernel/ptrace.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * Modified by Cort Dougan (cort@hq.fsmlabs.com) + * and Paul Mackerras (paulus@linuxcare.com.au). + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Set of msr bits that gdb can change on behalf of a process. + */ +#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* + * Get contents of register REGNO in task TASK. + */ +static inline unsigned long get_reg(struct task_struct *task, int regno) +{ + if (regno < sizeof(struct pt_regs) / sizeof(unsigned long)) + return ((unsigned long *)task->thread.regs)[regno]; + return (0); +} + +/* + * Write contents of register REGNO in task TASK. + */ +static inline int put_reg(struct task_struct *task, int regno, + unsigned long data) +{ + if (regno < PT_SOFTE) { + if (regno == PT_MSR) + data = (data & MSR_DEBUGCHANGE) + | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + ((unsigned long *)task->thread.regs)[regno] = data; + return 0; + } + return -EIO; +} + +static inline void +set_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + if (regs != NULL) + regs->msr |= MSR_SE; +} + +static inline void +clear_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + if (regs != NULL) + regs->msr &= ~MSR_SE; +} + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. + */ +void ptrace_disable(struct task_struct *child) +{ + /* make sure the single step bit is not set. */ + clear_single_step(child); +} + +int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret = -EPERM; + + lock_kernel(); + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long index, tmp; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 3; + if ((addr & 7) || index > PT_FPSCR) + break; + + if (index < PT_FPR0) { + tmp = get_reg(child, (int) index); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0]; + } + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* If I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + ret = -EIO; + break; + + /* write the word at location addr in the USER area */ + case PTRACE_POKEUSR: { + unsigned long index; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 3; + if ((addr & 7) || index > PT_FPSCR) + break; + + if (index == PT_ORIG_R3) + break; + if (index < PT_FPR0) { + ret = put_reg(child, index, data); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data; + ret = 0; + } + break; + } + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + set_single_step(child); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: + ret = ptrace_detach(child, data); + break; + + case PPC_PTRACE_GETREGS: + { /* Get GPRs 0 - 31. */ + u64 tmp; + u64 cntr; + ret = 0; + for (cntr=0; cntr<32 && ret==0; ++cntr) + { + tmp = ((u64*)child->thread.regs)[cntr]; + ret = put_user(tmp, (u64*)(data+cntr)); + } + break; + } + + case PPC_PTRACE_SETREGS: + { /* Set GPRs 0 - 31. */ + u64 cntr; + ret = 0; + for (cntr=0; cntr<32 && ret==0; ++cntr) + { + ret = put_reg(child, cntr, *(u64*)(data+cntr)); + } + break; + } + + case PPC_PTRACE_GETFPREGS: + { /* Get FPRs 0 - 31. */ + u64 tmp; + u64 cntr; + ret = -EIO; + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + ret = 0; + for (cntr=0; cntr<32 && ret==0; ++cntr) + { + tmp = ((u64*)child->thread.fpr)[cntr]; + ret = put_user(tmp, (u64*)(data+cntr)); + } + break; + } + + case PPC_PTRACE_SETFPREGS: + { /* Get FPRs 0 - 31. */ + u64 cntr; + ret = -EIO; + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + for (cntr=0; cntr<32; ++cntr) + { + ((u64*)child->thread.fpr)[cntr] = *(u64*)(data+cntr); + } + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +void do_syscall_trace(void) +{ + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + diff -Nru a/arch/ppc64/kernel/ptrace32.c b/arch/ppc64/kernel/ptrace32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/ptrace32.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,395 @@ +/* + * linux/arch/ppc/kernel/ptrace32.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * Modified by Cort Dougan (cort@hq.fsmlabs.com) + * and Paul Mackerras (paulus@linuxcare.com.au). + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Set of msr bits that gdb can change on behalf of a process. + */ +#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* + * Get contents of register REGNO in task TASK. + */ +static inline unsigned long get_reg(struct task_struct *task, int regno) +{ + if (regno < sizeof(struct pt_regs) / sizeof(unsigned long)) + return ((unsigned long *)task->thread.regs)[regno]; + return (0); +} + +/* + * Write contents of register REGNO in task TASK. + * (Put DATA into task TASK's register REGNO.) + */ +static inline int put_reg(struct task_struct *task, int regno, unsigned long data) +{ + if (regno < PT_SOFTE) + { + if (regno == PT_MSR) + data = (data & MSR_DEBUGCHANGE) | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + ((unsigned long *)task->thread.regs)[regno] = data; + return 0; + } + return -EIO; +} + +static inline void +set_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + if (regs != NULL) + regs->msr |= MSR_SE; +} + +static inline void +clear_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + if (regs != NULL) + regs->msr &= ~MSR_SE; +} + +int sys32_ptrace(long request, long pid, unsigned long addr, unsigned long data) +{ + struct task_struct *child; + int ret = -EPERM; + + lock_kernel(); + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + goto out_tsk; + + switch (request) + { + /* Read word at location ADDR */ + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + { + unsigned int tmp_mem_value; + int copied; + + copied = access_process_vm(child, addr, &tmp_mem_value, sizeof(tmp_mem_value), 0); + ret = -EIO; + if (copied != sizeof(tmp_mem_value)) + break; + ret = put_user(tmp_mem_value, (u32*)data); // copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". + break; + } + + /* Read 4 bytes of the other process' storage */ + /* data is a pointer specifying where the user wants the 4 bytes copied into */ + /* addr is a pointer in the user's storage that contains an 8 byte address in the other process of the 4 bytes that is to be read */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + /* when I and D space are separate, these will need to be fixed. */ + case PPC_PTRACE_PEEKTEXT_3264: + case PPC_PTRACE_PEEKDATA_3264: + { + u32 tmp_mem_value; + int copied; + u32* addrOthers; + + ret = -EIO; + + /* Get the addr in the other process that we want to read */ + if (get_user(addrOthers,(u32**)addr) != 0) + break; + + copied = access_process_vm(child, (u64)addrOthers, &tmp_mem_value, sizeof(tmp_mem_value), 0); + if (copied != sizeof(tmp_mem_value)) + break; + ret = put_user(tmp_mem_value, (u32*)data); // copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". + break; + } + + /* Read a register (specified by ADDR) out of the "user area" */ + case PTRACE_PEEKUSR: { + int index; + unsigned int reg32bits; + unsigned long tmp_reg_value; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 2; + if ((addr & 3) || index > PT_FPSCR32) + break; + + if (index < PT_FPR0) { + tmp_reg_value = get_reg(child, index); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + /* the user space code considers the floating point to be + * an array of unsigned int (32 bits) - the index passed + * in is based on this assumption. + */ + tmp_reg_value = ((unsigned int *)child->thread.fpr)[index - PT_FPR0]; + } + reg32bits = tmp_reg_value; + ret = put_user(reg32bits, (u32*)data); // copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". + break; + } + + /* Read 4 bytes out of the other process' pt_regs area */ + /* data is a pointer specifying where the user wants the 4 bytes copied into */ + /* addr is the offset into the other process' pt_regs structure that is to be read */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + case PPC_PTRACE_PEEKUSR_3264: + { + u32 index; + u32 reg32bits; + u64 tmp_reg_value; + u32 numReg; + u32 part; + + ret = -EIO; + /* Determine which register the user wants */ + index = (u64)addr >> 2; /* Divide addr by 4 */ + numReg = index / 2; + /* Determine which part of the register the user wants */ + if (index % 2) + part = 1; /* want the 2nd half of the register (right-most). */ + else + part = 0; /* want the 1st half of the register (left-most). */ + + /* Validate the input - check to see if address is on the wrong boundary or beyond the end of the user area */ + if ((addr & 3) || numReg > PT_FPSCR) + break; + + if (numReg >= PT_FPR0) + { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + } + tmp_reg_value = get_reg(child, numReg); + reg32bits = ((u32*)&tmp_reg_value)[part]; + ret = put_user(reg32bits, (u32*)data); /* copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". */ + break; + } + + /* Write the word at location ADDR */ + /* If I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: { + unsigned int tmp_value_to_write; + tmp_value_to_write = data; + ret = 0; + if (access_process_vm(child, addr, &tmp_value_to_write, sizeof(tmp_value_to_write), 1) == sizeof(tmp_value_to_write)) + break; + ret = -EIO; + break; + } + + /* Write 4 bytes into the other process' storage */ + /* data is the 4 bytes that the user wants written */ + /* addr is a pointer in the user's storage that contains an 8 byte address in the other process where the 4 bytes that is to be written */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + /* when I and D space are separate, these will need to be fixed. */ + case PPC_PTRACE_POKETEXT_3264: + case PPC_PTRACE_POKEDATA_3264: + { + u32 tmp_value_to_write = data; + u32* addrOthers; + int bytesWritten; + + /* Get the addr in the other process that we want to write into */ + ret = -EIO; + if (get_user(addrOthers,(u32**)addr) != 0) + break; + + ret = 0; + bytesWritten = access_process_vm(child, (u64)addrOthers, &tmp_value_to_write, sizeof(tmp_value_to_write), 1); + if (bytesWritten == sizeof(tmp_value_to_write)) + break; + ret = -EIO; + break; + } + + /* Write DATA into location ADDR within the USER area */ + case PTRACE_POKEUSR: { + unsigned long index; + + ret = -EIO; + + /* convert to index and check */ + index = (unsigned long) addr >> 2; + if ((addr & 3) || index > PT_FPSCR32) + break; + + if (index == PT_ORIG_R3) + break; + + + if (index < PT_FPR0) { + ret = put_reg(child, index, data); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + /* the user space code considers the floating point to be + * an array of unsigned int (32 bits) - the index passed + * in is based on this assumption. + */ + + ((unsigned int *)child->thread.fpr)[index - PT_FPR0] = data; + ret = 0; + } + break; + } + + /* Write 4 bytes into the other process' pt_regs area */ + /* data is the 4 bytes that the user wants written */ + /* addr is the offset into the other process' pt_regs structure that is to be written into */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + case PPC_PTRACE_POKEUSR_3264: + { + u32 index; + u32 numReg; + + ret = -EIO; + + /* Determine which register the user wants */ + index = (u64)addr >> 2; /* Divide addr by 4 */ + numReg = index / 2; + + /* Validate the input - check to see if address is on the wrong boundary or beyond the end of the user area */ + if ((addr & 3) || numReg > PT_FPSCR) + break; + /* Insure it is a register we let them change */ + if ((numReg == PT_ORIG_R3) || ((numReg > PT_CCR) && (numReg < PT_FPR0))) + break; + + if (numReg >= PT_FPR0) + { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + } + + if (numReg == PT_MSR) + data = (data & MSR_DEBUGCHANGE) | (child->thread.regs->msr & ~MSR_DEBUGCHANGE); + + ((u32*)child->thread.regs)[index] = data; + ret = 0; + break; + } + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + + /* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + set_single_step(child); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: + ret = ptrace_detach(child, data); + break; + + default: + ret = -EIO; + break; + } +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} diff -Nru a/arch/ppc64/kernel/ras.c b/arch/ppc64/kernel/ras.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/ras.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,165 @@ + +/* + * ras.c + * Copyright (C) 2001 Dave Engebretsen IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Change Activity: + * 2001/09/21 : engebret : Created with minimal EPOW and HW exception support. + * End Change Activity + */ + +#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 +#include + +static void ras_epow_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static void ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs); +void init_ras_IRQ(void); + +/* #define DEBUG */ + +/* + * Initialize handlers for the set of interrupts caused by hardware errors + * and power system events. + */ +void init_ras_IRQ(void) { + struct device_node *np; + unsigned int *ireg, len, i; + + if((np = find_path_device("/event-sources/internal-errors")) && + (ireg = (unsigned int *)get_property(np, "open-pic-interrupt", + &len))) { + for(i=0; i<(len / sizeof(*ireg)); i++) { + request_irq(virt_irq_create_mapping(*(ireg)) + NUM_8259_INTERRUPTS, + &ras_error_interrupt, 0, + "RAS_ERROR", NULL); + ireg++; + } + } + + if((np = find_path_device("/event-sources/epow-events")) && + (ireg = (unsigned int *)get_property(np, "open-pic-interrupt", + &len))) { + for(i=0; i<(len / sizeof(*ireg)); i++) { + request_irq(virt_irq_create_mapping(*(ireg)) + NUM_8259_INTERRUPTS, + &ras_epow_interrupt, 0, + "RAS_EPOW", NULL); + ireg++; + } + } +} + +/* + * Handle power subsystem events (EPOW). + * + * Presently we just log the event has occured. This should be fixed + * to examine the type of power failure and take appropriate action where + * the time horizon permits something useful to be done. + */ +static void +ras_epow_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct rtas_error_log log_entry; + unsigned int size = sizeof(log_entry); + long status = 0xdeadbeef; + + status = rtas_call(rtas_token("check-exception"), 6, 1, NULL, + 0x500, irq, + EPOW_WARNING | POWERMGM_EVENTS, + 1, /* Time Critical */ + __pa(&log_entry), size); + + udbg_printf("EPOW <0x%lx 0x%lx>\n", + *((unsigned long *)&log_entry), status); + printk(KERN_WARNING + "EPOW <0x%lx 0x%lx>\n",*((unsigned long *)&log_entry), status); +} + +/* + * Handle hardware error interrupts. + * + * RTAS check-exception is called to collect data on the exception. If + * the error is deemed recoverable, we log a warning and return. + * For nonrecoverable errors, an error is logged and we stop all processing + * as quickly as possible in order to prevent propagation of the failure. + */ +static void +ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct rtas_error_log log_entry; + unsigned int size = sizeof(log_entry); + long status = 0xdeadbeef; + + status = rtas_call(rtas_token("check-exception"), 6, 1, NULL, + 0x500, irq, + INTERNAL_ERROR, + 1, /* Time Critical */ + __pa(&log_entry), size); + + if((status != 1) && + (log_entry.severity >= SEVERITY_ERROR_SYNC)) { + udbg_printf("HW Error <0x%lx 0x%lx>\n", + *((unsigned long *)&log_entry), status); + printk(KERN_EMERG + "Error: Fatal hardware error <0x%lx 0x%lx>\n", + *((unsigned long *)&log_entry), status); + +#ifndef DEBUG + /* Don't actually power off when debugging so we can test + * without actually failing while injecting errors. + */ + ppc_md.power_off(); +#endif + } else { + udbg_printf("Recoverable HW Error <0x%lx 0x%lx>\n", + *((unsigned long *)&log_entry), status); + printk(KERN_WARNING + "Warning: Recoverable hardware error <0x%lx 0x%lx>\n", + *((unsigned long *)&log_entry), status); + + return; + } +} diff -Nru a/arch/ppc64/kernel/rtas-proc.c b/arch/ppc64/kernel/rtas-proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/rtas-proc.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,796 @@ +/* + * arch/ppc64/kernel/rtas-proc.c + * Copyright (C) 2000 Tilmann Bitterberg + * (tilmann@bitterberg.de) + * + * RTAS (Runtime Abstraction Services) stuff + * Intention is to provide a clean user interface + * to use the RTAS. + * + * TODO: + * Split off a header file and maybe move it to a different + * location. Write Documentation on what the /proc/rtas/ entries + * actually do. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* for ppc_md */ +#include + +/* Token for Sensors */ +#define KEY_SWITCH 0x0001 +#define ENCLOSURE_SWITCH 0x0002 +#define THERMAL_SENSOR 0x0003 +#define LID_STATUS 0x0004 +#define POWER_SOURCE 0x0005 +#define BATTERY_VOLTAGE 0x0006 +#define BATTERY_REMAINING 0x0007 +#define BATTERY_PERCENTAGE 0x0008 +#define EPOW_SENSOR 0x0009 +#define BATTERY_CYCLESTATE 0x000a +#define BATTERY_CHARGING 0x000b + +/* IBM specific sensors */ +#define IBM_SURVEILLANCE 0x2328 /* 9000 */ +#define IBM_FANRPM 0x2329 /* 9001 */ +#define IBM_VOLTAGE 0x232a /* 9002 */ +#define IBM_DRCONNECTOR 0x232b /* 9003 */ +#define IBM_POWERSUPPLY 0x232c /* 9004 */ +#define IBM_INTQUEUE 0x232d /* 9005 */ + +/* Status return values */ +#define SENSOR_CRITICAL_HIGH 13 +#define SENSOR_WARNING_HIGH 12 +#define SENSOR_NORMAL 11 +#define SENSOR_WARNING_LOW 10 +#define SENSOR_CRITICAL_LOW 9 +#define SENSOR_SUCCESS 0 +#define SENSOR_HW_ERROR -1 +#define SENSOR_BUSY -2 +#define SENSOR_NOT_EXIST -3 +#define SENSOR_DR_ENTITY -9000 + +/* Location Codes */ +#define LOC_SCSI_DEV_ADDR 'A' +#define LOC_SCSI_DEV_LOC 'B' +#define LOC_CPU 'C' +#define LOC_DISKETTE 'D' +#define LOC_ETHERNET 'E' +#define LOC_FAN 'F' +#define LOC_GRAPHICS 'G' +/* reserved / not used 'H' */ +#define LOC_IO_ADAPTER 'I' +/* reserved / not used 'J' */ +#define LOC_KEYBOARD 'K' +#define LOC_LCD 'L' +#define LOC_MEMORY 'M' +#define LOC_NV_MEMORY 'N' +#define LOC_MOUSE 'O' +#define LOC_PLANAR 'P' +#define LOC_OTHER_IO 'Q' +#define LOC_PARALLEL 'R' +#define LOC_SERIAL 'S' +#define LOC_DEAD_RING 'T' +#define LOC_RACKMOUNTED 'U' /* for _u_nit is rack mounted */ +#define LOC_VOLTAGE 'V' +#define LOC_SWITCH_ADAPTER 'W' +#define LOC_OTHER 'X' +#define LOC_FIRMWARE 'Y' +#define LOC_SCSI 'Z' + +/* Tokens for indicators */ +#define TONE_FREQUENCY 0x0001 /* 0 - 1000 (HZ)*/ +#define TONE_VOLUME 0x0002 /* 0 - 100 (%) */ +#define SYSTEM_POWER_STATE 0x0003 +#define WARNING_LIGHT 0x0004 +#define DISK_ACTIVITY_LIGHT 0x0005 +#define HEX_DISPLAY_UNIT 0x0006 +#define BATTERY_WARNING_TIME 0x0007 +#define CONDITION_CYCLE_REQUEST 0x0008 +#define SURVEILLANCE_INDICATOR 0x2328 /* 9000 */ +#define DR_ACTION 0x2329 /* 9001 */ +#define DR_INDICATOR 0x232a /* 9002 */ +/* 9003 - 9004: Vendor specific */ +#define GLOBAL_INTERRUPT_QUEUE 0x232d /* 9005 */ +/* 9006 - 9999: Vendor specific */ + +/* other */ +#define MAX_SENSORS 17 /* I only know of 17 sensors */ +#define MAX_LINELENGTH 256 +#define SENSOR_PREFIX "ibm,sensor-" +#define cel_to_fahr(x) ((x*9/5)+32) + + +/* Globals */ +static struct proc_dir_entry *proc_rtas; +static struct rtas_sensors sensors; +static struct device_node *rtas_node; +static unsigned long power_on_time = 0; /* Save the time the user set */ +static char progress_led[MAX_LINELENGTH]; + +static unsigned long rtas_tone_frequency = 1000; +static unsigned long rtas_tone_volume = 0; + +/* ****************STRUCTS******************************************* */ +struct individual_sensor { + unsigned int token; + unsigned int quant; +}; + +struct rtas_sensors { + struct individual_sensor sensor[MAX_SENSORS]; + unsigned int quant; +}; + +/* ****************************************************************** */ +/* Declarations */ +static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, + int count, int *eof, void *data); +static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); + +static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, + size_t count, loff_t *ppos); + +struct file_operations ppc_rtas_poweron_operations = { + read: ppc_rtas_poweron_read, + write: ppc_rtas_poweron_write +}; +struct file_operations ppc_rtas_progress_operations = { + read: ppc_rtas_progress_read, + write: ppc_rtas_progress_write +}; + +struct file_operations ppc_rtas_clock_operations = { + read: ppc_rtas_clock_read, + write: ppc_rtas_clock_write +}; + +struct file_operations ppc_rtas_tone_freq_operations = { + read: ppc_rtas_tone_freq_read, + write: ppc_rtas_tone_freq_write +}; +struct file_operations ppc_rtas_tone_volume_operations = { + read: ppc_rtas_tone_volume_read, + write: ppc_rtas_tone_volume_write +}; + +int ppc_rtas_find_all_sensors (void); +int ppc_rtas_process_sensor(struct individual_sensor s, int state, + int error, char * buf); +char * ppc_rtas_process_error(int error); +int get_location_code(struct individual_sensor s, char * buf); +int check_location_string (char *c, char * buf); +int check_location (char *c, int idx, char * buf); + +/* ****************************************************************** */ +/* MAIN */ +/* ****************************************************************** */ +void proc_rtas_init(void) +{ + struct proc_dir_entry *entry; + + rtas_node = find_devices("rtas"); + if ((rtas_node == 0) || (_machine == _MACH_iSeries)) { + return; + } + + proc_rtas = proc_mkdir("rtas", 0); + if (proc_rtas == 0) + return; + + /* /proc/rtas entries */ + + entry = create_proc_entry("progress", S_IRUGO|S_IWUSR, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_progress_operations; + + entry = create_proc_entry("clock", S_IRUGO|S_IWUSR, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_clock_operations; + + entry = create_proc_entry("poweron", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_poweron_operations; + + create_proc_read_entry("sensors", S_IRUGO, proc_rtas, + ppc_rtas_sensor_read, NULL); + + entry = create_proc_entry("frequency", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_tone_freq_operations; + + entry = create_proc_entry("volume", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_tone_volume_operations; +} + +/* ****************************************************************** */ +/* POWER-ON-TIME */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + struct rtc_time tm; + unsigned long nowtime; + char *dest; + int error; + + nowtime = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_poweron_write: Invalid time\n"); + return count; + } + power_on_time = nowtime; /* save the time */ + + to_tm(nowtime, &tm); + + error = rtas_call(rtas_token("set-time-for-power-on"), 7, 1, NULL, + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, 0 /* nano */); + if (error != 0) + printk(KERN_WARNING "error: setting poweron time returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + if (power_on_time == 0) + n = sprintf(buf, "Power on time not set\n"); + else + n = sprintf(buf, "%lu\n", power_on_time); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* PROGRESS */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long hex; + + strcpy(progress_led, buf); /* save the string */ + /* Lets see if the user passed hexdigits */ + hex = simple_strtoul(buf, NULL, 10); + + ppc_md.progress ((char *)buf, hex); + return count; + + /* clear the line */ /* ppc_md.progress(" ", 0xffff);*/ +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n = 0; + if (progress_led != NULL) + n = sprintf (buf, "%s\n", progress_led); + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* CLOCK */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + struct rtc_time tm; + unsigned long nowtime; + char *dest; + int error; + + nowtime = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_clock_write: Invalid time\n"); + return count; + } + + to_tm(nowtime, &tm); + error = rtas_call(rtas_token("set-time-of-day"), 7, 1, NULL, + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, 0); + if (error != 0) + printk(KERN_WARNING "error: setting the clock returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + unsigned int year, mon, day, hour, min, sec; + unsigned long *ret = kmalloc(4*8, GFP_KERNEL); + int n, error; + + error = rtas_call(rtas_token("get-time-of-day"), 0, 8, ret); + + year = ret[0]; mon = ret[1]; day = ret[2]; + hour = ret[3]; min = ret[4]; sec = ret[5]; + + if (error != 0){ + printk(KERN_WARNING "error: reading the clock returned: %s\n", + ppc_rtas_process_error(error)); + n = sprintf (buf, "0"); + } else { + n = sprintf (buf, "%lu\n", mktime(year, mon, day, hour, min, sec)); + } + kfree(ret); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* SENSOR STUFF */ +/* ****************************************************************** */ +static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, + int count, int *eof, void *data) +{ + int i,j,n; + unsigned long ret; + int state, error; + char *buffer; + int get_sensor_state = rtas_token("get-sensor-state"); + + if (count < 0) + return -EINVAL; + + /* May not be enough */ + buffer = kmalloc(MAX_LINELENGTH*MAX_SENSORS, GFP_KERNEL); + + if (!buffer) + return -ENOMEM; + + memset(buffer, 0, MAX_LINELENGTH*MAX_SENSORS); + + n = sprintf ( buffer , "RTAS (RunTime Abstraction Services) Sensor Information\n"); + n += sprintf ( buffer+n, "Sensor\t\tValue\t\tCondition\tLocation\n"); + n += sprintf ( buffer+n, "********************************************************\n"); + + if (ppc_rtas_find_all_sensors() != 0) { + n += sprintf ( buffer+n, "\nNo sensors are available\n"); + goto return_string; + } + + for (i=0; i= 0) { + error = rtas_call(get_sensor_state, 2, 2, &ret, + sensors.sensor[i].token, sensors.sensor[i].quant-j); + state = (int) ret; + n += ppc_rtas_process_sensor(sensors.sensor[i], state, error, buffer+n ); + n += sprintf (buffer+n, "\n"); + j--; + } /* while */ + } /* for */ + +return_string: + if (off >= strlen(buffer)) { + *eof = 1; + kfree(buffer); + return 0; + } + if (n > strlen(buffer) - off) + n = strlen(buffer) - off; + if (n > count) + n = count; + else + *eof = 1; + memcpy(buf, buffer + off, n); + *start = buf; + kfree(buffer); + return n; +} + +/* ****************************************************************** */ + +int ppc_rtas_find_all_sensors (void) +{ + unsigned long *utmp; + int len, i, j; + + utmp = (unsigned long *) get_property(rtas_node, "rtas-sensors", &len); + if (utmp == NULL) { + printk (KERN_ERR "error: could not get rtas-sensors\n"); + return 1; + } + + sensors.quant = len / 8; /* int + int */ + + for (i=0, j=0; j= llen) pos=0; + } + return n; +} +/* ****************************************************************** */ +/* INDICATORS - Tone Frequency */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long freq; + char *dest; + int error; + freq = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_tone_freq_write: Invalid tone freqency\n"); + return count; + } + if (freq < 0) freq = 0; + rtas_tone_frequency = freq; /* save it for later */ + error = rtas_call(rtas_token("set-indicator"), 3, 1, NULL, + TONE_FREQUENCY, 0, freq); + if (error != 0) + printk(KERN_WARNING "error: setting tone frequency returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + n = sprintf(buf, "%lu\n", rtas_tone_frequency); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} +/* ****************************************************************** */ +/* INDICATORS - Tone Volume */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long volume; + char *dest; + int error; + volume = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_tone_volume_write: Invalid tone volume\n"); + return count; + } + if (volume < 0) volume = 0; + if (volume > 100) volume = 100; + + rtas_tone_volume = volume; /* save it for later */ + error = rtas_call(rtas_token("set-indicator"), 3, 1, NULL, + TONE_VOLUME, 0, volume); + if (error != 0) + printk(KERN_WARNING "error: setting tone volume returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + n = sprintf(buf, "%lu\n", rtas_tone_volume); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} diff -Nru a/arch/ppc64/kernel/rtas.c b/arch/ppc64/kernel/rtas.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/rtas.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,208 @@ +/* + * + * Procedures for interfacing to the RTAS on CHRP machines. + * + * Peter Bergner, IBM March 2001. + * Copyright (C) 2001 IBM. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * prom_init() is called very early on, before the kernel text + * and data have been mapped to KERNELBASE. At this point the code + * is running at whatever address it has been loaded at, so + * references to extern and static variables must be relocated + * explicitly. The procedure reloc_offset() returns the address + * we're currently running at minus the address we were linked at. + * (Note that strings count as static variables.) + * + * Because OF may have mapped I/O devices into the area starting at + * KERNELBASE, particularly on CHRP machines, we can't safely call + * OF once the kernel has been mapped to KERNELBASE. Therefore all + * OF calls should be done within prom_init(), and prom_init() + * and all routines called within it must be careful to relocate + * references as necessary. + * + * Note that the bss is cleared *after* prom_init runs, so we have + * to make sure that any static or extern variables it accesses + * are put in the data segment. + */ + +struct rtas_t rtas = { + lock: SPIN_LOCK_UNLOCKED +}; + +extern unsigned long reloc_offset(void); + +void +phys_call_rtas(int token, int nargs, int nret, ...) +{ + va_list list; + unsigned long offset = reloc_offset(); + struct rtas_args *rtas = PTRRELOC(&(get_paca()->xRtas)); + int i; + + rtas->token = token; + rtas->nargs = nargs; + rtas->nret = nret; + rtas->rets = (rtas_arg_t *)PTRRELOC(&(rtas->args[nargs])); + + va_start(list, nret); + for (i = 0; i < nargs; i++) + rtas->args[i] = (rtas_arg_t)LONG_LSW(va_arg(list, ulong)); + va_end(list); + + enter_rtas(rtas); +} + +void +phys_call_rtas_display_status(char c) +{ + unsigned long offset = reloc_offset(); + struct rtas_args *rtas = PTRRELOC(&(get_paca()->xRtas)); + + rtas->token = 10; + rtas->nargs = 1; + rtas->nret = 1; + rtas->rets = (rtas_arg_t *)PTRRELOC(&(rtas->args[1])); + rtas->args[0] = (int)c; + + enter_rtas(rtas); +} + +void +call_rtas_display_status(char c) +{ + struct rtas_args *rtas = &(get_paca()->xRtas); + + rtas->token = 10; + rtas->nargs = 1; + rtas->nret = 1; + rtas->rets = (rtas_arg_t *)&(rtas->args[1]); + rtas->args[0] = (int)c; + + enter_rtas((void *)__pa((unsigned long)rtas)); +} + +#if 0 +#define DEBUG_RTAS +#endif +__openfirmware +int +rtas_token(const char *service) +{ + int *tokp; + if (rtas.dev == NULL) { +#ifdef DEBUG_RTAS + udbg_printf("\tNo rtas device in device-tree...\n"); +#endif /* DEBUG_RTAS */ + return RTAS_UNKNOWN_SERVICE; + } + tokp = (int *) get_property(rtas.dev, service, NULL); + return tokp ? *tokp : RTAS_UNKNOWN_SERVICE; +} + +__openfirmware +long +rtas_call(int token, int nargs, int nret, + unsigned long *outputs, ...) +{ + va_list list; + int i; + unsigned long s; + struct rtas_args *rtas_args = &(get_paca()->xRtas); + +#ifdef DEBUG_RTAS + udbg_printf("Entering rtas_call\n"); + udbg_printf("\ttoken = 0x%x\n", token); + udbg_printf("\tnargs = %d\n", nargs); + udbg_printf("\tnret = %d\n", nret); + udbg_printf("\t&outputs = 0x%lx\n", outputs); +#endif /* DEBUG_RTAS */ + if (token == RTAS_UNKNOWN_SERVICE) + return -1; + + rtas_args->token = token; + rtas_args->nargs = nargs; + rtas_args->nret = nret; + rtas_args->rets = (rtas_arg_t *)&(rtas_args->args[nargs]); + va_start(list, outputs); + for (i = 0; i < nargs; ++i) { + rtas_args->args[i] = (rtas_arg_t)LONG_LSW(va_arg(list, ulong)); +#ifdef DEBUG_RTAS + udbg_printf("\tnarg[%d] = 0x%lx\n", i, rtas_args->args[i]); +#endif /* DEBUG_RTAS */ + } + va_end(list); + + for (i = 0; i < nret; ++i) + rtas_args->rets[i] = 0; + +#if 0 /* Gotta do something different here, use global lock for now... */ + spin_lock_irqsave(&rtas_args->lock, s); +#else + spin_lock_irqsave(&rtas.lock, s); +#endif +#ifdef DEBUG_RTAS + udbg_printf("\tentering rtas with 0x%lx\n", (void *)__pa((unsigned long)rtas_args)); +#endif /* DEBUG_RTAS */ + enter_rtas((void *)__pa((unsigned long)rtas_args)); +#ifdef DEBUG_RTAS + udbg_printf("\treturned from rtas ...\n"); +#endif /* DEBUG_RTAS */ +#if 0 /* Gotta do something different here, use global lock for now... */ + spin_unlock_irqrestore(&rtas_args->lock, s); +#else + spin_unlock_irqrestore(&rtas.lock, s); +#endif +#ifdef DEBUG_RTAS + for(i=0; i < nret ;i++) + udbg_printf("\tnret[%d] = 0x%lx\n", i, (ulong)rtas_args->rets[i]); +#endif /* DEBUG_RTAS */ + + if (nret > 1 && outputs != NULL) + for (i = 0; i < nret-1; ++i) + outputs[i] = rtas_args->rets[i+1]; + return (ulong)((nret > 0) ? rtas_args->rets[0] : 0); +} + +void __chrp +rtas_restart(char *cmd) +{ + printk("RTAS system-reboot returned %ld\n", + rtas_call(rtas_token("system-reboot"), 0, 1, NULL)); + for (;;); +} + +void __chrp +rtas_power_off(void) +{ + /* allow power on only with power button press */ + printk("RTAS power-off returned %ld\n", + rtas_call(rtas_token("power-off"), 2, 1, NULL,0xffffffff,0xffffffff)); + for (;;); +} + +void __chrp +rtas_halt(void) +{ + rtas_power_off(); +} diff -Nru a/arch/ppc64/kernel/rtasd.c b/arch/ppc64/kernel/rtasd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/rtasd.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2001 Anton Blanchard , IBM + * + * 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. + * + * Communication to userspace based on kernel/printk.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if 0 +#define DEBUG(A...) printk(KERN_ERR A) +#else +#define DEBUG(A...) +#endif + +static spinlock_t rtas_log_lock = SPIN_LOCK_UNLOCKED; + +DECLARE_WAIT_QUEUE_HEAD(rtas_log_wait); + +#define LOG_NUMBER 64 /* must be a power of two */ +#define LOG_NUMBER_MASK (LOG_NUMBER-1) + +static char *rtas_log_buf; +static unsigned long rtas_log_start; +static unsigned long rtas_log_size; + +static int surveillance_requested; +static unsigned int rtas_event_scan_rate; +static unsigned int rtas_error_log_max; + +#define EVENT_SCAN_ALL_EVENTS 0xf0000000 +#define SURVEILLANCE_TOKEN 9000 +#define SURVEILLANCE_TIMEOUT 1 +#define SURVEILLANCE_SCANRATE 1 + +/* + * Since we use 32 bit RTAS, the physical address of this must be below + * 4G or else bad things happen. Allocate this in the kernel data and + * make it big enough. + */ +#define RTAS_ERROR_LOG_MAX 1024 +static unsigned char logdata[RTAS_ERROR_LOG_MAX]; + +static int rtas_log_open(struct inode * inode, struct file * file) +{ + return 0; +} + +static int rtas_log_release(struct inode * inode, struct file * file) +{ + return 0; +} + +static ssize_t rtas_log_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int error; + char *tmp; + unsigned long offset; + + if (!buf || count < rtas_error_log_max) + return -EINVAL; + + count = rtas_error_log_max; + + error = verify_area(VERIFY_WRITE, buf, count); + if (error) + return -EINVAL; + + tmp = kmalloc(rtas_error_log_max, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + error = wait_event_interruptible(rtas_log_wait, rtas_log_size); + if (error) + goto out; + + spin_lock(&rtas_log_lock); + offset = rtas_error_log_max * (rtas_log_start & LOG_NUMBER_MASK); + memcpy(tmp, &rtas_log_buf[offset], count); + rtas_log_start += 1; + rtas_log_size -= 1; + spin_unlock(&rtas_log_lock); + + copy_to_user(buf, tmp, count); + error = count; + +out: + kfree(tmp); + return error; +} + +static unsigned int rtas_log_poll(struct file *file, poll_table * wait) +{ + poll_wait(file, &rtas_log_wait, wait); + if (rtas_log_size) + return POLLIN | POLLRDNORM; + return 0; +} + +struct file_operations proc_rtas_log_operations = { + read: rtas_log_read, + poll: rtas_log_poll, + open: rtas_log_open, + release: rtas_log_release, +}; + +static void log_rtas(char *buf) +{ + unsigned long offset; + + DEBUG("logging rtas event\n"); + + spin_lock(&rtas_log_lock); + + offset = rtas_error_log_max * + ((rtas_log_start+rtas_log_size) & LOG_NUMBER_MASK); + + memcpy(&rtas_log_buf[offset], buf, rtas_error_log_max); + + if (rtas_log_size < LOG_NUMBER) + rtas_log_size += 1; + else + rtas_log_start += 1; + + spin_unlock(&rtas_log_lock); + wake_up_interruptible(&rtas_log_wait); +} + +static int enable_surveillance(void) +{ + int error; + + error = rtas_call(rtas_token("set-indicator"), 3, 1, NULL, SURVEILLANCE_TOKEN, + 0, SURVEILLANCE_TIMEOUT); + + if (error) { + printk(KERN_ERR "rtasd: could not enable surveillance\n"); + return -1; + } + + rtas_event_scan_rate = SURVEILLANCE_SCANRATE; + + return 0; +} + +static int get_eventscan_parms(void) +{ + struct device_node *node; + int *ip; + + node = find_path_device("/rtas"); + + ip = (int *)get_property(node, "rtas-event-scan-rate", NULL); + if (ip == NULL) { + printk(KERN_ERR "rtasd: no rtas-event-scan-rate\n"); + return -1; + } + rtas_event_scan_rate = *ip; + DEBUG("rtas-event-scan-rate %d\n", rtas_event_scan_rate); + + ip = (int *)get_property(node, "rtas-error-log-max", NULL); + if (ip == NULL) { + printk(KERN_ERR "rtasd: no rtas-error-log-max\n"); + return -1; + } + rtas_error_log_max = *ip; + DEBUG("rtas-error-log-max %d\n", rtas_error_log_max); + + if (rtas_error_log_max > RTAS_ERROR_LOG_MAX) { + printk(KERN_ERR "rtasd: truncated error log from %d to %d bytes\n", rtas_error_log_max, RTAS_ERROR_LOG_MAX); + rtas_error_log_max = RTAS_ERROR_LOG_MAX; + } + + return 0; +} + +extern long sys_sched_get_priority_max(int policy); + +static int rtasd(void *unused) +{ + int cpu = 0; + int error; + int first_pass = 1; + int event_scan = rtas_token("event-scan"); + + if (event_scan == RTAS_UNKNOWN_SERVICE || get_eventscan_parms() == -1) + goto error; + + rtas_log_buf = vmalloc(rtas_error_log_max*LOG_NUMBER); + if (!rtas_log_buf) { + printk(KERN_ERR "rtasd: no memory\n"); + goto error; + } + + DEBUG("will sleep for %d jiffies\n", (HZ*60/rtas_event_scan_rate) / 2); + + daemonize(); + sigfillset(¤t->blocked); + sprintf(current->comm, "rtasd"); + +#if 0 + /* Rusty unreal time task */ + current->policy = SCHED_FIFO; + current->nice = sys_sched_get_priority_max(SCHED_FIFO) + 1; +#endif + + cpu = 0; + set_cpus_allowed(current, 1UL << cpu_logical_map(cpu)); + schedule(); + + while(1) { + do { + memset(logdata, 0, rtas_error_log_max); + error = rtas_call(event_scan, 4, 1, NULL, + EVENT_SCAN_ALL_EVENTS, 0, + __pa(logdata), rtas_error_log_max); + if (error == -1) { + printk(KERN_ERR "event-scan failed\n"); + break; + } + + if (error == 0) + log_rtas(logdata); + + } while(error == 0); + + DEBUG("watchdog scheduled on cpu %d\n", smp_processor_id()); + + cpu++; + if (cpu >= smp_num_cpus) { + + if (first_pass && surveillance_requested) { + DEBUG("enabling surveillance\n"); + if (enable_surveillance()) + goto error_vfree; + DEBUG("surveillance enabled\n"); + } + + first_pass = 0; + cpu = 0; + } + + set_cpus_allowed(current, 1UL << cpu_logical_map(cpu)); + + + /* Check all cpus for pending events before sleeping*/ + if (first_pass) { + schedule(); + } else { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((HZ*60/rtas_event_scan_rate) / 2); + } + } + +error_vfree: + vfree(rtas_log_buf); +error: + /* Should delete proc entries */ + return -EINVAL; +} + +static void __init rtas_init(void) +{ + struct proc_dir_entry *rtas_dir, *entry; + + rtas_dir = proc_mkdir("rtas", 0); + if (!rtas_dir) { + printk(KERN_ERR "Failed to create rtas proc directory\n"); + } else { + entry = create_proc_entry("error_log", S_IRUSR, rtas_dir); + if (entry) + entry->proc_fops = &proc_rtas_log_operations; + else + printk(KERN_ERR "Failed to create rtas/error_log proc entry\n"); + } + + if (kernel_thread(rtasd, 0, CLONE_FS) < 0) + printk(KERN_ERR "Failed to start RTAS daemon\n"); + + printk(KERN_ERR "RTAS daemon started\n"); +} + +static int __init surveillance_setup(char *str) +{ + int i; + + if (get_option(&str,&i)) { + if (i == 1) + surveillance_requested = 1; + } + + return 1; +} + +__initcall(rtas_init); +__setup("surveillance=", surveillance_setup); diff -Nru a/arch/ppc64/kernel/rtc.c b/arch/ppc64/kernel/rtc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/rtc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,382 @@ +/* + * Real Time Clock interface for PPC64. + * + * Based on rtc.c by Paul Gortmaker + * + * This driver allows use of the real time clock + * from user space. It exports the /dev/rtc + * interface supporting various ioctl() and also the + * /proc/driver/rtc pseudo-file for status information. + * + * Interface does not support RTC interrupts nor an alarm. + * + * 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. + * + * 1.0 Mike Corrigan: IBM iSeries rtc support + * 1.1 Dave Engebretsen: IBM pSeries rtc support + */ + +#define RTC_VERSION "1.1" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +extern int piranha_simulator; + +/* + * We sponge a minor off of the misc major. No need slurping + * up another valuable major dev number for this. If you add + * an ioctl, make sure you don't conflict with SPARC's RTC + * ioctls. + */ + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin); + +static ssize_t rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +/* + * If this driver ever becomes modularised, it will be really nice + * to make the epoch retain its value across module reload... + */ + +static unsigned long epoch = 1900; /* year corresponding to 0x00 */ + +static const unsigned char days_in_mo[] = +{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* + * Now all the various file operations that we export. + */ + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static ssize_t rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + return -EIO; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_time wtime; + + switch (cmd) { + case RTC_RD_TIME: /* Read the time/date from RTC */ + { + ppc_md.get_rtc_time(&wtime); + break; + } + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time rtc_tm; + unsigned char mon, day, hrs, min, sec, leap_yr; + unsigned int yrs; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + yrs = rtc_tm.tm_year; + mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm.tm_mday; + hrs = rtc_tm.tm_hour; + min = rtc_tm.tm_min; + sec = rtc_tm.tm_sec; + + if (yrs < 70) + return -EINVAL; + + leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); + + if ((mon > 12) || (day == 0)) + return -EINVAL; + + if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) + return -EINVAL; + + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) + return -EINVAL; + + if ( yrs > 169 ) + return -EINVAL; + + ppc_md.set_rtc_time(&rtc_tm); + + return 0; + } + case RTC_EPOCH_READ: /* Read the epoch. */ + { + return put_user (epoch, (unsigned long *)arg); + } + case RTC_EPOCH_SET: /* Set the epoch. */ + { + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + epoch = arg; + return 0; + } + default: + return -EINVAL; + } + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * The various file operations we support. + */ +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: rtc_llseek, + read: rtc_read, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, +}; + +static struct miscdevice rtc_dev= +{ + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int __init rtc_init(void) +{ + misc_register(&rtc_dev); + create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); + + printk(KERN_INFO "i/pSeries Real Time Clock Driver v" RTC_VERSION "\n"); + + return 0; +} + +static void __exit rtc_exit (void) +{ + remove_proc_entry ("driver/rtc", NULL); + misc_deregister(&rtc_dev); +} + +module_init(rtc_init); +module_exit(rtc_exit); +EXPORT_NO_SYMBOLS; + +/* + * Info exported via "/proc/driver/rtc". + */ + +static int rtc_proc_output (char *buf) +{ + + char *p; + struct rtc_time tm; + + p = buf; + + ppc_md.get_rtc_time(&tm); + + /* + * There is no way to tell if the luser has the RTC set for local + * time or for Universal Standard Time (GMT). Probably local though. + */ + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04lu\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); + + p += sprintf(p, + "DST_enable\t: no\n" + "BCD\t\t: yes\n" + "24hr\t\t: yes\n" ); + + return p - buf; +} + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = rtc_proc_output (page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +/* + * Get the RTC from the virtual service processor + * This requires flowing LpEvents to the primary partition + */ +void iSeries_get_rtc_time(struct rtc_time *rtc_tm) +{ + if (piranha_simulator) + return; + + mf_getRtc(rtc_tm); + rtc_tm->tm_mon--; +} + + +void pSeries_get_rtc_time(struct rtc_time *rtc_tm) +{ + unsigned long ret[8]; + int error; + int count; + + /* + * error -2 is clock busy, we keep retrying a few times to see + * if it will come good -- paulus + */ + count = 0; + do { + error = rtas_call(rtas_token("get-time-of-day"), 0, 8, (void *)&ret); + } while (error == -2 && ++count < 1000); + + if (error != 0) { + printk(KERN_WARNING "error: reading the clock failed (%d)\n", + error); + return; + } + + rtc_tm->tm_sec = ret[5]; + rtc_tm->tm_min = ret[4]; + rtc_tm->tm_hour = ret[3]; + rtc_tm->tm_mday = ret[2]; + rtc_tm->tm_mon = ret[1] - 1; + rtc_tm->tm_year = ret[0] - 1900; +} + +int pSeries_set_rtc_time(struct rtc_time *tm) +{ + int error; + int count; + + /* + * error -2 is clock busy, we keep retrying a few times to see + * if it will come good -- paulus + */ + count = 0; + do { + error = rtas_call(rtas_token("set-time-of-day"), 7, 1, NULL, + tm->tm_year + 1900, tm->tm_mon + 1, + tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec, 0); + } while (error == -2 && ++count < 1000); + + if (error != 0) + printk(KERN_WARNING "error: setting the clock failed (%d)\n", + error); + + return 0; +} + +/* + * Set the RTC in the virtual service processor + * This requires flowing LpEvents to the primary partition + */ +int iSeries_set_rtc_time(struct rtc_time *tm) +{ + mf_setRtc(tm); + return 0; +} + +void iSeries_get_boot_time(struct rtc_time *tm) +{ + unsigned long time; + static unsigned long lastsec = 1; + + u32 dataWord1 = *((u32 *)(&xSpCommArea.xBcdTimeAtIplStart)); + u32 dataWord2 = *(((u32 *)&(xSpCommArea.xBcdTimeAtIplStart)) + 1); + int year = 1970; + int year1 = ( dataWord1 >> 24 ) & 0x000000FF; + int year2 = ( dataWord1 >> 16 ) & 0x000000FF; + int sec = ( dataWord1 >> 8 ) & 0x000000FF; + int min = dataWord1 & 0x000000FF; + int hour = ( dataWord2 >> 24 ) & 0x000000FF; + int day = ( dataWord2 >> 8 ) & 0x000000FF; + int mon = dataWord2 & 0x000000FF; + + if ( piranha_simulator ) + return; + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year1); + BCD_TO_BIN(year2); + year = year1 * 100 + year2; + + time = mktime(year, mon, day, hour, min, sec); + time += ( jiffies / HZ ); + + /* Now THIS is a nasty hack! + * It ensures that the first two calls get different answers. + * That way the loop in init_time (time.c) will not think + * the clock is stuck. + */ + if ( lastsec ) { + time -= lastsec; + --lastsec; + } + + to_tm(time, tm); + tm->tm_year -= 1900; + tm->tm_mon -= 1; +} diff -Nru a/arch/ppc64/kernel/semaphore.c b/arch/ppc64/kernel/semaphore.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/semaphore.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,130 @@ +/* + * + * + * PowerPC-specific semaphore code. + * + * Copyright (C) 1999 Cort Dougan + * + * 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. + * + * April 2001 - Reworked by Paul Mackerras + * to eliminate the SMP races in the old version between the updates + * of `count' and `waking'. Now we use negative `count' values to + * indicate that some process(es) are waiting for the semaphore. + */ + +#include +#include +#include + +/* + * Atomically update sem->count. + * This does the equivalent of the following: + * + * old_count = sem->count; + * tmp = MAX(old_count, 0) + incr; + * sem->count = tmp; + * return old_count; + */ +static inline int __sem_update_count(struct semaphore *sem, int incr) +{ + int old_count, tmp; + + __asm__ __volatile__("\n" +"1: lwarx %0,0,%3\n" +" srawi %1,%0,31\n" +" andc %1,%0,%1\n" +" add %1,%1,%4\n" +" stwcx. %1,0,%3\n" +" bne 1b" + : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) + : "r" (&sem->count), "r" (incr), "m" (sem->count) + : "cc"); + + return old_count; +} + +void __up(struct semaphore *sem) +{ + /* + * Note that we incremented count in up() before we came here, + * but that was ineffective since the result was <= 0, and + * any negative value of count is equivalent to 0. + * This ends up setting count to 1, unless count is now > 0 + * (i.e. because some other cpu has called up() in the meantime), + * in which case we just increment count. + */ + __sem_update_count(sem, 1); + wake_up(&sem->wait); +} + +/* + * Note that when we come in to __down or __down_interruptible, + * we have already decremented count, but that decrement was + * ineffective since the result was < 0, and any negative value + * of count is equivalent to 0. + * Thus it is only when we decrement count from some value > 0 + * that we have actually got the semaphore. + */ +void __down(struct semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + tsk->state = TASK_UNINTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + smp_wmb(); + + /* + * Try to get the semaphore. If the count is > 0, then we've + * got the semaphore; we decrement count and exit the loop. + * If the count is 0 or negative, we set it to -1, indicating + * that we are asleep, and then sleep. + */ + while (__sem_update_count(sem, -1) <= 0) { + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE; + } + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + /* + * If there are any more sleepers, wake one of them up so + * that it can either get the semaphore, or set count to -1 + * indicating that there are still processes sleeping. + */ + wake_up(&sem->wait); +} + +int __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + tsk->state = TASK_INTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + smp_wmb(); + + while (__sem_update_count(sem, -1) <= 0) { + if (signal_pending(current)) { + /* + * A signal is pending - give up trying. + * Set sem->count to 0 if it is negative, + * since we are no longer sleeping. + */ + __sem_update_count(sem, 0); + retval = -EINTR; + break; + } + schedule(); + tsk->state = TASK_INTERRUPTIBLE; + } + tsk->state = TASK_RUNNING; + remove_wait_queue(&sem->wait, &wait); + wake_up(&sem->wait); + return retval; +} diff -Nru a/arch/ppc64/kernel/setup.c b/arch/ppc64/kernel/setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/setup.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,740 @@ +/* + * + * Common boot and setup code. + * + * Copyright (C) 2001 PPC64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern unsigned long klimit; +/* extern void *stab; */ +extern HTAB htab_data; +extern unsigned long loops_per_jiffy; + +extern unsigned long embedded_sysmap_start; +extern unsigned long embedded_sysmap_end; + +int have_of = 1; + +extern void chrp_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void chrp_init_map_io_space( void ); +extern void iSeries_init( void ); +extern void iSeries_init_early( void ); +extern void pSeries_init_early( void ); +extern void pSeriesLP_init_early(void); +extern void mm_init_ppc64( void ); + +unsigned long decr_overclock = 1; +unsigned long decr_overclock_proc0 = 1; +unsigned long decr_overclock_set = 0; +unsigned long decr_overclock_proc0_set = 0; + +#ifdef CONFIG_XMON +extern void xmon_map_scc(void); +#endif + +char saved_command_line[256]; +unsigned char aux_device_present; + +void parse_cmd_line(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7); +int parse_bootinfo(void); + +int _machine = _MACH_unknown; + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned long SYSRQ_KEY; +#endif /* CONFIG_MAGIC_SYSRQ */ + +struct machdep_calls ppc_md; +struct Naca *naca; + +/* + * Perhaps we can put the pmac screen_info[] here + * on pmac as well so we don't need the ifdef's. + * Until we get multiple-console support in here + * that is. -- Cort + * Maybe tie it to serial consoles, since this is really what + * these processors use on existing boards. -- Dan + */ +struct screen_info screen_info = { + 0, 25, /* orig-x, orig-y */ + 0, /* unused */ + 0, /* orig-video-page */ + 0, /* orig-video-mode */ + 80, /* orig-video-cols */ + 0,0,0, /* ega_ax, ega_bx, ega_cx */ + 25, /* orig-video-lines */ + 1, /* orig-video-isVGA */ + 16 /* orig-video-points */ +}; + +/* + * These are used in binfmt_elf.c to put aux entries on the stack + * for each elf executable being started. + */ +int dcache_bsize; +int icache_bsize; +int ucache_bsize; + +/* + * Initialize the PPCDBG state. Called before relocation has been enabled. + */ +void ppcdbg_initialize(void) { + unsigned long offset = reloc_offset(); + struct Naca *_naca = RELOC(naca); + + _naca->debug_switch = PPC_DEBUG_DEFAULT; /* | PPCDBG_BUSWALK | PPCDBG_PHBINIT | PPCDBG_MM | PPCDBG_MMINIT | PPCDBG_TCEINIT | PPCDBG_TCE */; +} + +/* + * Initialize a set of PACA's, one for each processor. + * + * At this point, relocation is on, but we have not done any other + * setup of the mm subsystem. + */ +void paca_init(void) { +#if 0 + int processorCount = naca->processorCount, i; + struct Paca *paca[]; + + /* Put the array of paca's on a page boundary & allocate 1/2 page of */ + /* storage for each. */ + klimit += (PAGE_SIZE-1) & PAGE_MASK; + naca->xPaca = paca[0] = klimit; + klimit += ((PAGE_SIZE>>1) * processorCount); + + for(i=0; ixPacaIndex = i; + } +#endif +} + +/* + * Do some initial setup of the system. The paramters are those which + * were passed in from the bootloader. + */ +void setup_system(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* This should be fixed properly in kernel/resource.c */ + iomem_resource.end = MEM_SPACE_LIMIT; + + /* pSeries systems are identified in prom.c via OF. */ + if ( itLpNaca.xLparInstalled == 1 ) + _machine = _MACH_iSeries; + switch (_machine) { + case _MACH_iSeries: + iSeries_init_early(); + break; + +#ifdef CONFIG_PPC_PSERIES + case _MACH_pSeries: + pSeries_init_early(); +#ifdef CONFIG_BLK_DEV_INITRD + initrd_start = initrd_end = 0; +#endif + parse_bootinfo(); + break; + + case _MACH_pSeriesLP: + pSeriesLP_init_early(); +#ifdef CONFIG_BLK_DEV_INITRD + initrd_start = initrd_end = 0; +#endif + parse_bootinfo(); + break; +#endif + } + + udbg_puts("\n-----------------------------------------------------\n"); + udbg_puts("Naca Info...\n\n"); + udbg_puts("naca = 0x"); + udbg_puthex((unsigned long)naca); + udbg_putc('\n'); + + udbg_puts("naca->processorCount = 0x"); + udbg_puthex(naca->processorCount); + udbg_putc('\n'); + + udbg_puts("naca->physicalMemorySize = 0x"); + udbg_puthex(naca->physicalMemorySize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LineSize = 0x"); + udbg_puthex(naca->dCacheL1LineSize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LogLineSize = 0x"); + udbg_puthex(naca->dCacheL1LogLineSize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LinesPerPage = 0x"); + udbg_puthex(naca->dCacheL1LinesPerPage); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LineSize = 0x"); + udbg_puthex(naca->iCacheL1LineSize); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LogLineSize = 0x"); + udbg_puthex(naca->iCacheL1LogLineSize); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LinesPerPage = 0x"); + udbg_puthex(naca->iCacheL1LinesPerPage); + udbg_putc('\n'); + + udbg_puts("naca->pftSize = 0x"); + udbg_puthex(naca->pftSize); + udbg_putc('\n'); + + udbg_puts("naca->serialPortAddr = 0x"); + udbg_puthex(naca->serialPortAddr); + udbg_putc('\n'); + + udbg_puts("naca->interrupt_controller = 0x"); + udbg_puthex(naca->interrupt_controller); + udbg_putc('\n'); + + udbg_printf("\nHTAB Info ...\n\n"); + udbg_puts("htab_data.htab = 0x"); + udbg_puthex((unsigned long)htab_data.htab); + udbg_putc('\n'); + udbg_puts("htab_data.num_ptegs = 0x"); + udbg_puthex(htab_data.htab_num_ptegs); + udbg_putc('\n'); + + udbg_puts("\n-----------------------------------------------------\n"); + + + if ( _machine & _MACH_pSeries ) { + finish_device_tree(); + chrp_init(r3, r4, r5, r6, r7); + } + + mm_init_ppc64(); + + switch (_machine) { + case _MACH_iSeries: + iSeries_init(); + break; + default: + /* The following relies on the device tree being */ + /* fully configured. */ + parse_cmd_line(r3, r4, r5, r6, r7); + } +} + +void machine_restart(char *cmd) +{ + ppc_md.restart(cmd); +} + +void machine_power_off(void) +{ + ppc_md.power_off(); +} + +void machine_halt(void) +{ + ppc_md.halt(); +} + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + unsigned long cpu_id = (unsigned long)v - 1; + unsigned int pvr; + unsigned short maj; + unsigned short min; + +#ifdef CONFIG_SMP + if (cpu_id == NR_CPUS) { + unsigned long bogosum = smp_num_cpus * loops_per_jiffy; + seq_printf(m, "total bogomips\t: %lu.%02lu\n", + bogosum/(500000/HZ), + bogosum/(5000/HZ) % 100); + + if (ppc_md.get_cpuinfo != NULL) + ppc_md.get_cpuinfo(m); + + return 0; + } + + if (!(cpu_online_map & (1<> 8) & 0xFF; + min = pvr & 0xFF; + + seq_printf(m, "processor\t: %lu\n", cpu_id); + seq_printf(m, "cpu\t\t: "); + + pvr = xPaca[cpu_id].pvr; + + switch (PVR_VER(pvr)) { + case PV_PULSAR: + seq_printf(m, "RS64-III (pulsar)\n"); + break; + case PV_POWER4: + seq_printf(m, "POWER4 (gp)\n"); + break; + case PV_ICESTAR: + seq_printf(m, "RS64-III (icestar)\n"); + break; + case PV_SSTAR: + seq_printf(m, "RS64-IV (sstar)\n"); + break; + case PV_630: + seq_printf(m, "POWER3 (630)\n"); + break; + case PV_630p: + seq_printf(m, "POWER3 (630+)\n"); + break; + default: + seq_printf(m, "Unknown (%08x)\n", pvr); + break; + } + + /* + * Assume here that all clock rates are the same in a + * smp system. -- Cort + */ + if (_machine != _MACH_iSeries) { + struct device_node *cpu_node; + int *fp; + + cpu_node = find_type_devices("cpu"); + if (cpu_node) { + fp = (int *) get_property(cpu_node, "clock-frequency", + NULL); + if (fp) + seq_printf(m, "clock\t\t: %dMHz\n", + *fp / 1000000); + } + } + + if (ppc_md.setup_residual != NULL) + ppc_md.setup_residual(m, cpu_id); + + seq_printf(m, "revision\t: %hd.%hd\n", maj, min); + + seq_printf(m, "bogomips\t: %lu.%02lu\n\n", + loops_per_jiffy/(500000/HZ), + loops_per_jiffy/(5000/HZ) % 100); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos <= NR_CPUS ? (void *)((*pos)+1) : NULL; +} +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} +static void c_stop(struct seq_file *m, void *v) +{ +} +struct seq_operations cpuinfo_op = { + start: c_start, + next: c_next, + stop: c_stop, + show: show_cpuinfo, +}; + +/* + * Fetch the cmd_line from open firmware. */ +void parse_cmd_line(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + struct device_node *chosen; + char *p; + +#ifdef CONFIG_BLK_DEV_INITRD + if ((initrd_start == 0) && r3 && r4 && r4 != 0xdeadbeef) { + initrd_start = (r3 >= KERNELBASE) ? r3 : (unsigned long)__va(r3); + initrd_end = initrd_start + r4; + ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); + initrd_below_start_ok = 1; + } +#endif + + cmd_line[0] = 0; + chosen = find_devices("chosen"); + if (chosen != NULL) { + p = get_property(chosen, "bootargs", NULL); + if (p != NULL) + strncpy(cmd_line, p, sizeof(cmd_line)); + } + cmd_line[sizeof(cmd_line) - 1] = 0; + + /* Look for mem= option on command line */ + if (strstr(cmd_line, "mem=")) { + char *p, *q; + unsigned long maxmem = 0; + extern unsigned long __max_memory; + + for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) { + q = p + 4; + if (p > cmd_line && p[-1] != ' ') + continue; + maxmem = simple_strtoul(q, &q, 0); + if (*q == 'k' || *q == 'K') { + maxmem <<= 10; + ++q; + } else if (*q == 'm' || *q == 'M') { + maxmem <<= 20; + ++q; + } + } + __max_memory = maxmem; + } + ppc_md.progress("id mach: done", 0x200); +} + + +char *bi_tag2str(unsigned long tag) +{ + switch (tag) { + case BI_FIRST: + return "BI_FIRST"; + case BI_LAST: + return "BI_LAST"; + case BI_CMD_LINE: + return "BI_CMD_LINE"; + case BI_BOOTLOADER_ID: + return "BI_BOOTLOADER_ID"; + case BI_INITRD: + return "BI_INITRD"; + case BI_SYSMAP: + return "BI_SYSMAP"; + case BI_MACHTYPE: + return "BI_MACHTYPE"; + default: + return "BI_UNKNOWN"; + } +} + +int parse_bootinfo(void) +{ + struct bi_record *rec; + extern char *sysmap; + extern unsigned long sysmap_size; + + rec = prom.bi_recs; + + if ( rec == NULL || rec->tag != BI_FIRST ) + return -1; + + for ( ; rec->tag != BI_LAST ; rec = bi_rec_next(rec) ) { + switch (rec->tag) { + case BI_CMD_LINE: + memcpy(cmd_line, (void *)rec->data, rec->size); + break; + case BI_SYSMAP: + sysmap = (char *)((rec->data[0] >= (KERNELBASE)) + ? rec->data[0] : (unsigned long)__va(rec->data[0])); + sysmap_size = rec->data[1]; + break; +#ifdef CONFIG_BLK_DEV_INITRD + case BI_INITRD: + initrd_start = (unsigned long)__va(rec->data[0]); + initrd_end = initrd_start + rec->data[1]; + ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); + initrd_below_start_ok = 1; + break; +#endif /* CONFIG_BLK_DEV_INITRD */ + } + } + + return 0; +} + +int __init ppc_init(void) +{ + /* clear the progress line */ + ppc_md.progress(" ", 0xffff); + + if (ppc_md.init != NULL) { + ppc_md.init(); + } + return 0; +} + +arch_initcall(ppc_init); + +void __init ppc64_calibrate_delay(void) +{ + loops_per_jiffy = tb_ticks_per_jiffy; + + printk("Calibrating delay loop... %lu.%02lu BogoMips\n", + loops_per_jiffy/(500000/HZ), + loops_per_jiffy/(5000/HZ) % 100); + +} + +extern void (*calibrate_delay)(void); + +/* + * Called into from start_kernel, after lock_kernel has been called. + * Initializes bootmem, which is unsed to manage page allocation until + * mem_init is called. + */ +void __init setup_arch(char **cmdline_p) +{ + extern int panic_timeout; + extern char _etext[], _edata[]; + extern void do_init_bootmem(void); + + calibrate_delay = ppc64_calibrate_delay; + +#ifdef CONFIG_XMON + xmon_map_scc(); + if (strstr(cmd_line, "xmon")) + xmon(0); +#endif /* CONFIG_XMON */ + + ppc_md.progress("setup_arch:enter", 0x3eab); + +#if defined(CONFIG_KGDB) + kgdb_map_scc(); + set_debug_traps(); + breakpoint(); +#endif + /* + * Set cache line size based on type of cpu as a default. + * Systems with OF can look in the properties on the cpu node(s) + * for a possibly more accurate value. + */ + dcache_bsize = naca->dCacheL1LineSize; + icache_bsize = naca->iCacheL1LineSize; + + /* reboot on panic */ + panic_timeout = 180; + + init_mm.start_code = PAGE_OFFSET; + init_mm.end_code = (unsigned long) _etext; + init_mm.end_data = (unsigned long) _edata; + init_mm.brk = (unsigned long) klimit; + + /* Save unparsed command line copy for /proc/cmdline */ + strcpy(saved_command_line, cmd_line); + *cmdline_p = cmd_line; + + /* set up the bootmem stuff with available memory */ + do_init_bootmem(); + ppc_md.progress("setup_arch:bootmem", 0x3eab); + + ppc_md.setup_arch(); + + paging_init(); + ppc_md.progress("setup_arch: exit", 0x3eab); +} + +#ifdef CONFIG_IDE + +/* Convert the shorts/longs in hd_driveid from little to big endian; + * chars are endian independant, of course, but strings need to be flipped. + * (Despite what it says in drivers/block/ide.h, they come up as little + * endian...) + * + * Changes to linux/hdreg.h may require changes here. */ +void ppc64_ide_fix_driveid(struct hd_driveid *id) +{ + int i; + unsigned short *stringcast; + + id->config = __le16_to_cpu(id->config); + id->cyls = __le16_to_cpu(id->cyls); + id->reserved2 = __le16_to_cpu(id->reserved2); + id->heads = __le16_to_cpu(id->heads); + id->track_bytes = __le16_to_cpu(id->track_bytes); + id->sector_bytes = __le16_to_cpu(id->sector_bytes); + id->sectors = __le16_to_cpu(id->sectors); + id->vendor0 = __le16_to_cpu(id->vendor0); + id->vendor1 = __le16_to_cpu(id->vendor1); + id->vendor2 = __le16_to_cpu(id->vendor2); + stringcast = (unsigned short *)&id->serial_no[0]; + for (i = 0; i < (20/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->buf_type = __le16_to_cpu(id->buf_type); + id->buf_size = __le16_to_cpu(id->buf_size); + id->ecc_bytes = __le16_to_cpu(id->ecc_bytes); + stringcast = (unsigned short *)&id->fw_rev[0]; + for (i = 0; i < (8/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + stringcast = (unsigned short *)&id->model[0]; + for (i = 0; i < (40/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->dword_io = __le16_to_cpu(id->dword_io); + id->reserved50 = __le16_to_cpu(id->reserved50); + id->field_valid = __le16_to_cpu(id->field_valid); + id->cur_cyls = __le16_to_cpu(id->cur_cyls); + id->cur_heads = __le16_to_cpu(id->cur_heads); + id->cur_sectors = __le16_to_cpu(id->cur_sectors); + id->cur_capacity0 = __le16_to_cpu(id->cur_capacity0); + id->cur_capacity1 = __le16_to_cpu(id->cur_capacity1); + id->lba_capacity = __le32_to_cpu(id->lba_capacity); + id->dma_1word = __le16_to_cpu(id->dma_1word); + id->dma_mword = __le16_to_cpu(id->dma_mword); + id->eide_pio_modes = __le16_to_cpu(id->eide_pio_modes); + id->eide_dma_min = __le16_to_cpu(id->eide_dma_min); + id->eide_dma_time = __le16_to_cpu(id->eide_dma_time); + id->eide_pio = __le16_to_cpu(id->eide_pio); + id->eide_pio_iordy = __le16_to_cpu(id->eide_pio_iordy); + for (i = 0; i < 2; i++) + id->words69_70[i] = __le16_to_cpu(id->words69_70[i]); + for (i = 0; i < 4; i++) + id->words71_74[i] = __le16_to_cpu(id->words71_74[i]); + id->queue_depth = __le16_to_cpu(id->queue_depth); + for (i = 0; i < 4; i++) + id->words76_79[i] = __le16_to_cpu(id->words76_79[i]); + id->major_rev_num = __le16_to_cpu(id->major_rev_num); + id->minor_rev_num = __le16_to_cpu(id->minor_rev_num); + id->command_set_1 = __le16_to_cpu(id->command_set_1); + id->command_set_2 = __le16_to_cpu(id->command_set_2); + id->cfsse = __le16_to_cpu(id->cfsse); + id->cfs_enable_1 = __le16_to_cpu(id->cfs_enable_1); + id->cfs_enable_2 = __le16_to_cpu(id->cfs_enable_2); + id->csf_default = __le16_to_cpu(id->csf_default); + id->dma_ultra = __le16_to_cpu(id->dma_ultra); + id->word89 = __le16_to_cpu(id->word89); + id->word90 = __le16_to_cpu(id->word90); + id->CurAPMvalues = __le16_to_cpu(id->CurAPMvalues); + id->word92 = __le16_to_cpu(id->word92); + id->hw_config = __le16_to_cpu(id->hw_config); + for (i = 0; i < 32; i++) + id->words94_125[i] = __le16_to_cpu(id->words94_125[i]); + id->last_lun = __le16_to_cpu(id->last_lun); + id->word127 = __le16_to_cpu(id->word127); + id->dlf = __le16_to_cpu(id->dlf); + id->csfo = __le16_to_cpu(id->csfo); + for (i = 0; i < 26; i++) + id->words130_155[i] = __le16_to_cpu(id->words130_155[i]); + id->word156 = __le16_to_cpu(id->word156); + for (i = 0; i < 3; i++) + id->words157_159[i] = __le16_to_cpu(id->words157_159[i]); + for (i = 0; i < 96; i++) + id->words160_255[i] = __le16_to_cpu(id->words160_255[i]); +} +#endif + + +void exception_trace(unsigned long trap) +{ + unsigned long x, srr0, srr1, reg20, reg1, reg21; + + asm("mflr %0" : "=r" (x) :); + asm("mfspr %0,0x1a" : "=r" (srr0) :); + asm("mfspr %0,0x1b" : "=r" (srr1) :); + asm("mr %0,1" : "=r" (reg1) :); + asm("mr %0,20" : "=r" (reg20) :); + asm("mr %0,21" : "=r" (reg21) :); + + udbg_puts("\n"); + udbg_puts("Took an exception : "); udbg_puthex(x); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(reg1); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(reg20); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(reg21); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(srr0); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(srr1); udbg_puts("\n"); +} + +int set_spread_lpevents( char * str ) +{ + /* The parameter is the number of processors to share in processing lp events */ + unsigned long i; + unsigned long val = simple_strtoul( str, NULL, 0 ); + if ( ( val > 0 ) && ( val <= maxPacas ) ) { + for ( i=1; idefault_decr = tb_ticks_per_jiffy / decr_overclock_proc0; + paca->next_jiffy_update_tb = get_tb() + tb_ticks_per_jiffy; +} + +int set_decr_overclock_proc0( char * str ) +{ + unsigned long val = simple_strtoul( str, NULL, 0 ); + if ( ( val >= 1 ) && ( val <= 48 ) ) { + decr_overclock_proc0_set = 1; + decr_overclock_proc0 = val; + printk("proc 0 decrementer overclock factor of %ld\n", val); + } + else + printk("invalid proc 0 decrementer overclock factor of %ld\n", val); + return 1; +} + +int set_decr_overclock( char * str ) +{ + unsigned long val = simple_strtoul( str, NULL, 0 ); + if ( ( val >= 1 ) && ( val <= 48 ) ) { + decr_overclock_set = 1; + decr_overclock = val; + printk("decrementer overclock factor of %ld\n", val); + } + else + printk("invalid decrementer overclock factor of %ld\n", val); + return 1; + +} + +__setup("spread_lpevents=", set_spread_lpevents ); +__setup("decr_overclock_proc0=", set_decr_overclock_proc0 ); +__setup("decr_overclock=", set_decr_overclock ); diff -Nru a/arch/ppc64/kernel/signal.c b/arch/ppc64/kernel/signal.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/signal.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,782 @@ +/* + * linux/arch/ppc64/kernel/signal.c + * + * + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/signal.c" + * Copyright (C) 1991, 1992 Linus Torvalds + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define GP_REGS_SIZE MIN(sizeof(elf_gregset_t), sizeof(struct pt_regs)) + +/* + * These are the flags in the MSR that the user is allowed to change + * by modifying the saved value of the MSR on the stack. SE and BE + * should not be in this list since gdb may want to change these. I.e, + * you should be able to step out of a signal handler to see what + * instruction executes next after the signal handler completes. + * Alternately, if you stepped into a signal handler, you should be + * able to continue 'til the next breakpoint from within the signal + * handler, even if the handler returns. + */ +#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) + +int do_signal(sigset_t *oldset, struct pt_regs *regs); +extern long sys_wait4(pid_t pid, unsigned int *stat_addr, + int options, /*unsigned long*/ struct rusage *ru); + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +long sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7, + struct pt_regs *regs) +{ + sigset_t saveset; + + PPCDBG(PPCDBG_SYS64X, "sys_sigsuspend - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gpr[3] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + /* + * If a signal handler needs to be called, + * do_signal() has set R3 to the signal number (the + * first argument of the signal handler), so don't + * overwrite that with EINTR ! + * In the other cases, do_signal() doesn't touch + * R3, so it's still set to -EINTR (see above). + */ + return regs->gpr[3]; + } +} + +long sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, int p3, int p4, int p6, + int p7, struct pt_regs *regs) +{ + sigset_t saveset, newset; + + + PPCDBG(PPCDBG_SYS64X, "sys_rt_sigsuspend - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gpr[3] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return regs->gpr[3]; + } +} + + + +asmlinkage long sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + struct pt_regs *regs = (struct pt_regs *) &uss; + + PPCDBG(PPCDBG_SYS64X, "sys_sigaltstack - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + return do_sigaltstack(uss, uoss, regs->gpr[1]); +} + +long sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + PPCDBG(PPCDBG_SYS64X, "sys_sigaction - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + + + + return ret; +} + +/* + * When we have signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one or more sigcontext structs + * a gap of __SIGNAL_FRAMESIZE bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * + * XXX ultimately we will have to stack up a siginfo and ucontext + * for each rt signal. + */ +struct sigregs { + elf_gregset_t gp_regs; + double fp_regs[ELF_NFPREG]; + unsigned int tramp[2]; + /* 64 bit API allows for 288 bytes below sp before + decrementing it. */ + int abigap[72]; +}; + + + +struct rt_sigframe +{ + unsigned long _unused[2]; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; +}; + + +/* + * When we have rt signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one rt_sigframe struct (siginfo + ucontext) + * a gap of __SIGNAL_FRAMESIZE bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * + */ + +int sys_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, unsigned long r8, + struct pt_regs *regs) +{ + struct rt_sigframe *rt_sf; + struct sigcontext_struct sigctx; + struct sigregs *sr; + int ret; + elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ + sigset_t set; + stack_t st; + unsigned long prevsp; + + rt_sf = (struct rt_sigframe *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, &rt_sf->uc.uc_mcontext, sizeof(sigctx)) + || copy_from_user(&set, &rt_sf->uc.uc_sigmask, sizeof(set)) + || copy_from_user(&st, &rt_sf->uc.uc_stack, sizeof(st))) + goto badframe; + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + + rt_sf++; /* Look at next rt_sigframe */ + if (rt_sf == (struct rt_sigframe *)(sigctx.regs)) { + /* Last stacked signal - restore registers - + * sigctx is initialized to point to the + * preamble frame (where registers are stored) + * see handle_signal() + */ + sr = (struct sigregs *) sigctx.regs; + if (regs->msr & MSR_FP ) + giveup_fpu(current); + if (copy_from_user(saved_regs, &sr->gp_regs, + sizeof(sr->gp_regs))) + goto badframe; + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + saved_regs[PT_SOFTE] = regs->softe; + memcpy(regs, saved_regs, GP_REGS_SIZE); + if (copy_from_user(current->thread.fpr, &sr->fp_regs, + sizeof(sr->fp_regs))) + goto badframe; + /* This function sets back the stack flags into + the current task structure. */ + sys_sigaltstack(&st, NULL); + + ret = regs->result; + } else { + /* More signals to go */ + /* Set up registers for next signal handler */ + regs->gpr[1] = (unsigned long)rt_sf - __SIGNAL_FRAMESIZE; + if (copy_from_user(&sigctx, &rt_sf->uc.uc_mcontext, sizeof(sigctx))) + goto badframe; + sr = (struct sigregs *) sigctx.regs; + regs->gpr[3] = ret = sigctx.signal; + /* Get the siginfo */ + get_user(regs->gpr[4], (unsigned long *)&rt_sf->pinfo); + /* Get the ucontext */ + get_user(regs->gpr[5], (unsigned long *)&rt_sf->puc); + regs->gpr[6] = (unsigned long) rt_sf; + + regs->link = (unsigned long) &sr->tramp; + regs->nip = sigctx.handler; + if (get_user(prevsp, &sr->gp_regs[PT_R1]) + || put_user(prevsp, (unsigned long *) regs->gpr[1])) + goto badframe; + } + return ret; + +badframe: + do_exit(SIGSEGV); +} + +static void +setup_rt_frame(struct pt_regs *regs, struct sigregs *frame, + signed long newsp) +{ + struct rt_sigframe *rt_sf = (struct rt_sigframe *) newsp; + /* Handler is *really* a pointer to the function descriptor for + * the signal routine. The first entry in the function + * descriptor is the entry address of signal and the second + * entry is the TOC value we need to use. + */ + struct funct_descr_entry { + unsigned long entry; + unsigned long toc; + }; + + struct funct_descr_entry * funct_desc_ptr; + unsigned long temp_ptr; + + /* Set up preamble frame */ + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) + || __copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38000000UL + __NR_rt_sigreturn, &frame->tramp[0]) /* li r0, __NR_rt_sigreturn */ + || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ + goto badframe; + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + /* Retrieve rt_sigframe from stack and + set up registers for signal handler + */ + newsp -= __SIGNAL_FRAMESIZE; + + if ( get_user(temp_ptr, &rt_sf->uc.uc_mcontext.handler)) { + goto badframe; + } + + funct_desc_ptr = ( struct funct_descr_entry *) temp_ptr; + + if (put_user(regs->gpr[1], (unsigned long *)newsp) + || get_user(regs->nip, &funct_desc_ptr->entry) + || get_user(regs->gpr[2], &funct_desc_ptr->toc) + || get_user(regs->gpr[3], &rt_sf->uc.uc_mcontext.signal) + || get_user(regs->gpr[4], (unsigned long *)&rt_sf->pinfo) + || get_user(regs->gpr[5], (unsigned long *)&rt_sf->puc)) + goto badframe; + + regs->gpr[1] = newsp; + regs->gpr[6] = (unsigned long) rt_sf; + regs->link = (unsigned long) frame->tramp; + + + return; + +badframe: +#if DEBUG_SIG + printk("badframe in setup_rt_frame, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + +/* + * Do a signal return; undo the signal stack. + */ +long sys_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, unsigned long r8, + struct pt_regs *regs) +{ + struct sigcontext_struct *sc, sigctx; + struct sigregs *sr; + long ret; + elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ + sigset_t set; + unsigned long prevsp; + + sc = (struct sigcontext_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + + set.sig[0] = sigctx.oldmask; +#if _NSIG_WORDS > 1 + set.sig[1] = sigctx._unused[3]; +#endif + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + + sc++; /* Look at next sigcontext */ + if (sc == (struct sigcontext_struct *)(sigctx.regs)) { + /* Last stacked signal - restore registers */ + sr = (struct sigregs *) sigctx.regs; + if (regs->msr & MSR_FP ) + giveup_fpu(current); + if (copy_from_user(saved_regs, &sr->gp_regs, + sizeof(sr->gp_regs))) + goto badframe; + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + saved_regs[PT_SOFTE] = regs->softe; + memcpy(regs, saved_regs, GP_REGS_SIZE); + + if (copy_from_user(current->thread.fpr, &sr->fp_regs, + sizeof(sr->fp_regs))) + goto badframe; + + ret = regs->result; + + } else { + /* More signals to go */ + regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE; + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + sr = (struct sigregs *) sigctx.regs; + regs->gpr[3] = ret = sigctx.signal; + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) &sr->tramp; + regs->nip = sigctx.handler; + + if (get_user(prevsp, &sr->gp_regs[PT_R1]) + || put_user(prevsp, (unsigned long *) regs->gpr[1])) + goto badframe; + } + return ret; + +badframe: + do_exit(SIGSEGV); +} + +/* + * Set up a signal frame. + */ +static void +setup_frame(struct pt_regs *regs, struct sigregs *frame, + unsigned long newsp) +{ + + /* Handler is *really* a pointer to the function descriptor for + * the signal routine. The first entry in the function + * descriptor is the entry address of signal and the second + * entry is the TOC value we need to use. + */ + struct funct_descr_entry { + unsigned long entry; + unsigned long toc; + }; + + struct funct_descr_entry * funct_desc_ptr; + unsigned long temp_ptr; + + struct sigcontext_struct *sc = (struct sigcontext_struct *) newsp; + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) + || __copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38000000UL + __NR_sigreturn, &frame->tramp[0]) /* li r0, __NR_sigreturn */ + || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ + goto badframe; + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + newsp -= __SIGNAL_FRAMESIZE; + if ( get_user(temp_ptr, &sc->handler)) + goto badframe; + + funct_desc_ptr = ( struct funct_descr_entry *) temp_ptr; + + if (put_user(regs->gpr[1], (unsigned long *)newsp) + || get_user(regs->nip, & funct_desc_ptr ->entry) + || get_user(regs->gpr[2],& funct_desc_ptr->toc) + || get_user(regs->gpr[3], &sc->signal)) + goto badframe; + regs->gpr[1] = newsp; + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) frame->tramp; + + + PPCDBG(PPCDBG_SIGNAL, "setup_frame - returning - regs->gpr[1]=%lx, regs->gpr[4]=%lx, regs->link=%lx \n", + regs->gpr[1], regs->gpr[4], regs->link); + + return; + + badframe: + PPCDBG(PPCDBG_SIGNAL, "setup_frame - badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); PPCDBG_ENTER_DEBUGGER(); +#if DEBUG_SIG + printk("badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + +/* + * OK, we're invoking a handler + */ +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned long *newspp, unsigned long frame) +{ + struct sigcontext_struct *sc; + struct rt_sigframe *rt_sf; + + if (regs->trap == 0x0C00 /* System Call! */ + && ((int)regs->result == -ERESTARTNOHAND || + ((int)regs->result == -ERESTARTSYS && + !(ka->sa.sa_flags & SA_RESTART)))) + regs->result = -EINTR; + /* Set up Signal Frame */ + + if (ka->sa.sa_flags & SA_SIGINFO) { + /* Put a Real Time Context onto stack */ + *newspp -= sizeof(*rt_sf); + rt_sf = (struct rt_sigframe *) *newspp; + if (verify_area(VERIFY_WRITE, rt_sf, sizeof(*rt_sf))) + goto badframe; + + + if (__put_user((unsigned long) ka->sa.sa_handler, &rt_sf->uc.uc_mcontext.handler) + || __put_user(&rt_sf->info, &rt_sf->pinfo) + || __put_user(&rt_sf->uc, &rt_sf->puc) + /* Put the siginfo */ + || __copy_to_user(&rt_sf->info, info, sizeof(*info)) + /* Create the ucontext */ + || __put_user(0, &rt_sf->uc.uc_flags) + || __put_user(0, &rt_sf->uc.uc_link) + || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp) + || __put_user(sas_ss_flags(regs->gpr[1]), + &rt_sf->uc.uc_stack.ss_flags) + || __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size) + || __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset)) + /* mcontext.regs points to preamble register frame */ + || __put_user((struct pt_regs *)frame, &rt_sf->uc.uc_mcontext.regs) + || __put_user(sig, &rt_sf->uc.uc_mcontext.signal)) + goto badframe; + + } else { + /* Put another sigcontext on the stack */ + *newspp -= sizeof(*sc); + sc = (struct sigcontext_struct *) *newspp; + if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) + goto badframe; + + if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler) + || __put_user(oldset->sig[0], &sc->oldmask) +#if _NSIG_WORDS > 1 + || __put_user(oldset->sig[1], &sc->_unused[3]) +#endif + || __put_user((struct pt_regs *)frame, &sc->regs) + || __put_user(sig, &sc->signal)) + goto badframe; + } + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + } + return; + +badframe: +#if DEBUG_SIG + printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n", + regs, frame, *newspp); + printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset); +#endif + do_exit(SIGSEGV); +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +extern int do_signal32(sigset_t *oldset, struct pt_regs *regs); +int do_signal(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + struct k_sigaction *ka; + unsigned long frame, newsp; + + /* + * If the current thread is 32 bit - invoke the + * 32 bit signal handling code + */ + if (test_thread_flag(TIF_32BIT)) + return do_signal32(oldset, regs); + + if (!oldset) + oldset = ¤t->blocked; + + newsp = frame = 0; + + for (;;) { + unsigned long signr; + + PPCDBG(PPCDBG_SIGNAL, "do_signal - (pre) dequeueing signal - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + PPCDBG(PPCDBG_SIGNAL, "do_signal - (aft) dequeueing signal - signal=%lx - pid=%ld current=%lx comm=%s \n", signr, current->pid, current, current->comm); + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + + + PPCDBG(PPCDBG_SIGNAL, "do_signal - ka=%p, action handler=%lx \n", ka, ka->sa.sa_handler); + + if (ka->sa.sa_handler == SIG_IGN) { + PPCDBG(PPCDBG_SIGNAL, "do_signal - into SIG_IGN logic \n"); + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + PPCDBG(PPCDBG_SIGNAL, "do_signal - into SIG_DFL logic \n"); + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sig_exit(signr, exit_code, &info); + /* NOTREACHED */ + } + } + + if ( (ka->sa.sa_flags & SA_ONSTACK) + && (! on_sig_stack(regs->gpr[1]))) + newsp = (current->sas_ss_sp + current->sas_ss_size); + else + newsp = regs->gpr[1]; + newsp = frame = newsp - sizeof(struct sigregs); + + /* Whee! Actually deliver the signal. */ + + PPCDBG(PPCDBG_SIGNAL, "do_signal - GOING TO RUN SIGNAL HANDLER - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + handle_signal(signr, ka, &info, oldset, regs, &newsp, frame); + PPCDBG(PPCDBG_SIGNAL, "do_signal - after running signal handler - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + break; + } + + if (regs->trap == 0x0C00 /* System Call! */ && + ((int)regs->result == -ERESTARTNOHAND || + (int)regs->result == -ERESTARTSYS || + (int)regs->result == -ERESTARTNOINTR)) { + PPCDBG(PPCDBG_SIGNAL, "do_signal - going to back up & retry system call \n"); + regs->gpr[3] = regs->orig_gpr3; + regs->nip -= 4; /* Back up & retry system call */ + regs->result = 0; + } + + if (newsp == frame) + { + PPCDBG(PPCDBG_SIGNAL, "do_signal - returning w/ no signal delivered \n"); + return 0; /* no signals delivered */ + } + + + + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(regs, (struct sigregs *) frame, newsp); + else + setup_frame(regs, (struct sigregs *) frame, newsp); + PPCDBG(PPCDBG_SIGNAL, "do_signal - returning a signal was delivered \n"); + return 1; +} diff -Nru a/arch/ppc64/kernel/signal32.c b/arch/ppc64/kernel/signal32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/signal32.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1479 @@ +/* + * signal32.c: Support 32bit signal syscalls. + * + * Copyright (C) 2001 IBM + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * + * These routines maintain argument size conversion between 32bit and 64bit + * environment. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) +/* + * These are the flags in the MSR that the user is allowed to change + * by modifying the saved value of the MSR on the stack. SE and BE + * should not be in this list since gdb may want to change these. I.e, + * you should be able to step out of a signal handler to see what + * instruction executes next after the signal handler completes. + * Alternately, if you stepped into a signal handler, you should be + * able to continue 'til the next breakpoint from within the signal + * handler, even if the handler returns. + */ +#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) + +struct timespec32 { + s32 tv_sec; + s32 tv_nsec; +}; + +struct sigregs32 { + /***********************************************************************/ + /* the gp_regs array is 32 bit representation of the pt_regs structure */ + /* that was stored on the kernle stack during the system call that */ + /* was interrupted for the signal. */ + /* */ + /* Note that the entire pt_regs regs structure will fit in the gp_regs */ + /* structure because the ELF_NREG value is 48 for PPC and the pt_regs*/ + /* structure contains 44 registers */ + /* */ + /***********************************************************************/ + elf_gregset_t32 gp_regs; + double fp_regs[ELF_NFPREG]; + unsigned int tramp[2]; + /* Programs using the rs6000/xcoff abi can save up to 19 gp regs + and 18 fp regs below sp before decrementing it. */ + int abigap[56]; +}; + + +struct rt_sigframe_32 { + /* Unused space at start of frame to allow for storing of stack pointers */ + unsigned long _unused; + /* This is a 32 bit pointer in user address space + * it is a pointer to the siginfo stucture in the rt stack frame + */ + u32 pinfo; + /* This is a 32 bit pointer in user address space */ + /* it is a pointer to the user context in the rt stack frame */ + u32 puc; + struct siginfo32 info; + struct ucontext32 uc; +}; + + + + + +extern asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru); + + +/****************************************************************************/ +/* Start of nonRT signal support */ +/* */ +/* sigset_t is 32 bits for non-rt signals */ +/* */ +/* System Calls */ +/* sigaction sys32_sigaction */ +/* sigpending sys32_sigpending */ +/* sigprocmask sys32_sigprocmask */ +/* sigreturn sys32_sigreturn */ +/* */ +/* Note sigsuspend has no special 32 bit routine - uses the 64 bit routine */ +/* */ +/* Other routines */ +/* setup_frame32 */ +/* */ +/****************************************************************************/ + + +asmlinkage long sys32_sigaction(int sig, struct old_sigaction32 *act, struct old_sigaction32 *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + PPCDBG(PPCDBG_SYS32, "sys32_sigaction - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (sig < 0) + { + sig = -sig; + } + + if (act) + { + old_sigset_t32 mask; + + ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); + ret |= __get_user((long)new_ka.sa.sa_restorer, &act->sa_restorer); + ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + ret |= __get_user(mask, &act->sa_mask); + if (ret) + return ret; + PPCDBG(PPCDBG_SIGNAL, "sys32_sigaction flags =%lx \n", new_ka.sa.sa_flags); + + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) + { + ret = put_user((long)old_ka.sa.sa_handler, &oact->sa_handler); + ret |= __put_user((long)old_ka.sa.sa_restorer, &oact->sa_restorer); + ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + ret |= __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + + PPCDBG(PPCDBG_SYS32, "sys32_sigaction - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return ret; +} + + + + +extern asmlinkage long sys_sigpending(old_sigset_t *set); + +asmlinkage long sys32_sigpending(old_sigset_t32 *set) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32, "sys32_sigpending - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_sigpending(&s); + set_fs (old_fs); + if (put_user (s, set)) return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sigpending - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return ret; +} + + + + +extern asmlinkage long sys_sigprocmask(int how, old_sigset_t *set, old_sigset_t *oset); + +/* Note: it is necessary to treat how as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sigprocmask(u32 how, old_sigset_t32 *set, old_sigset_t32 *oset) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32, "sys32_sigprocmask - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (set && get_user (s, set)) return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_sigprocmask((int)how, set ? &s : NULL, oset ? &s : NULL); + set_fs (old_fs); + if (ret) return ret; + if (oset && put_user (s, oset)) return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sigprocmask - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return 0; +} + + + +/* + * When we have signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one or more sigcontext structs + * a gap of __SIGNAL_FRAMESIZE32 bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * +*/ + + +/* + * Do a signal return; undo the signal stack. + */ +long sys32_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, unsigned long r8, + struct pt_regs *regs) +{ + struct sigcontext32_struct *sc, sigctx; + struct sigregs32 *sr; + int ret; + elf_gregset_t32 saved_regs; /* an array of ELF_NGREG unsigned ints (32 bits) */ + sigset_t set; + unsigned int prevsp; + + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + sc = (struct sigcontext32_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE32); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + + /* Note that PPC32 puts the upper 32 bits of the sigmask in the */ + /* unused part of the signal stackframe */ + set.sig[0] = sigctx.oldmask + ((long)(sigctx._unused[3])<< 32); + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + + sc++; /* Look at next sigcontext */ + /* If the next sigcontext is actually the sigregs (frame) */ + /* - then no more sigcontexts on the user stack */ + if (sc == (struct sigcontext32_struct*)(u64)sigctx.regs) + { + /* Last stacked signal - restore registers */ + sr = (struct sigregs32*)(u64)sigctx.regs; + if (regs->msr & MSR_FP ) + giveup_fpu(current); + /* copy the 32 bit register values off the user stack */ + /* into the 32 bit register area */ + if (copy_from_user(saved_regs, &sr->gp_regs,sizeof(sr->gp_regs))) + goto badframe; + /**********************************************************************/ + /* The saved reg structure in the frame is an elf_grepset_t32, it is */ + /* a 32 bit register save of the registers in the pt_regs structure */ + /* that was stored on the kernel stack during the system call */ + /* when the system call was interrupted for the signal. Only 32 bits*/ + /* are saved because the sigcontext contains a pointer to the regs */ + /* and the sig context address is passed as a pointer to the signal */ + /* handler. */ + /* */ + /* The entries in the elf_grepset have the same index as the elements */ + /* in the pt_regs structure. */ + /* */ + /**********************************************************************/ + + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + regs->gpr[0] = (u64)(saved_regs[0]) & 0xFFFFFFFF; + regs->gpr[1] = (u64)(saved_regs[1]) & 0xFFFFFFFF; + /**********************************************************************/ + /* Register 2 is the kernel toc - should be reset on any calls into */ + /* the kernel */ + /**********************************************************************/ + regs->gpr[2] = (u64)(saved_regs[2]) & 0xFFFFFFFF; + + regs->gpr[3] = (u64)(saved_regs[3]) & 0xFFFFFFFF; + regs->gpr[4] = (u64)(saved_regs[4]) & 0xFFFFFFFF; + regs->gpr[5] = (u64)(saved_regs[5]) & 0xFFFFFFFF; + regs->gpr[6] = (u64)(saved_regs[6]) & 0xFFFFFFFF; + regs->gpr[7] = (u64)(saved_regs[7]) & 0xFFFFFFFF; + regs->gpr[8] = (u64)(saved_regs[8]) & 0xFFFFFFFF; + regs->gpr[9] = (u64)(saved_regs[9]) & 0xFFFFFFFF; + regs->gpr[10] = (u64)(saved_regs[10]) & 0xFFFFFFFF; + regs->gpr[11] = (u64)(saved_regs[11]) & 0xFFFFFFFF; + regs->gpr[12] = (u64)(saved_regs[12]) & 0xFFFFFFFF; + regs->gpr[13] = (u64)(saved_regs[13]) & 0xFFFFFFFF; + regs->gpr[14] = (u64)(saved_regs[14]) & 0xFFFFFFFF; + regs->gpr[15] = (u64)(saved_regs[15]) & 0xFFFFFFFF; + regs->gpr[16] = (u64)(saved_regs[16]) & 0xFFFFFFFF; + regs->gpr[17] = (u64)(saved_regs[17]) & 0xFFFFFFFF; + regs->gpr[18] = (u64)(saved_regs[18]) & 0xFFFFFFFF; + regs->gpr[19] = (u64)(saved_regs[19]) & 0xFFFFFFFF; + regs->gpr[20] = (u64)(saved_regs[20]) & 0xFFFFFFFF; + regs->gpr[21] = (u64)(saved_regs[21]) & 0xFFFFFFFF; + regs->gpr[22] = (u64)(saved_regs[22]) & 0xFFFFFFFF; + regs->gpr[23] = (u64)(saved_regs[23]) & 0xFFFFFFFF; + regs->gpr[24] = (u64)(saved_regs[24]) & 0xFFFFFFFF; + regs->gpr[25] = (u64)(saved_regs[25]) & 0xFFFFFFFF; + regs->gpr[26] = (u64)(saved_regs[26]) & 0xFFFFFFFF; + regs->gpr[27] = (u64)(saved_regs[27]) & 0xFFFFFFFF; + regs->gpr[28] = (u64)(saved_regs[28]) & 0xFFFFFFFF; + regs->gpr[29] = (u64)(saved_regs[29]) & 0xFFFFFFFF; + regs->gpr[30] = (u64)(saved_regs[30]) & 0xFFFFFFFF; + regs->gpr[31] = (u64)(saved_regs[31]) & 0xFFFFFFFF; + /****************************************************/ + /* restore the non gpr registers */ + /****************************************************/ + regs->msr = (u64)(saved_regs[PT_MSR]) & 0xFFFFFFFF; + /* Insure that the interrupt mode is 64 bit, during 32 bit execution. + * (This is necessary because we only saved lower 32 bits of msr.) + */ + regs->msr = regs->msr | MSR_ISF; /* When this thread is interrupted it should run in 64 bit mode. */ + + regs->nip = (u64)(saved_regs[PT_NIP]) & 0xFFFFFFFF; + regs->orig_gpr3 = (u64)(saved_regs[PT_ORIG_R3]) & 0xFFFFFFFF; + regs->ctr = (u64)(saved_regs[PT_CTR]) & 0xFFFFFFFF; + regs->link = (u64)(saved_regs[PT_LNK]) & 0xFFFFFFFF; + regs->xer = (u64)(saved_regs[PT_XER]) & 0xFFFFFFFF; + regs->ccr = (u64)(saved_regs[PT_CCR]) & 0xFFFFFFFF; + /* regs->softe is left unchanged (like the MSR.EE bit) */ + /******************************************************/ + /* the DAR and the DSISR are only relevant during a */ + /* data or instruction storage interrupt. The value */ + /* will be set to zero. */ + /******************************************************/ + regs->dar = 0; + regs->dsisr = 0; + regs->result = (u64)(saved_regs[PT_RESULT]) & 0xFFFFFFFF; + + if (copy_from_user(current->thread.fpr, &sr->fp_regs, sizeof(sr->fp_regs))) + goto badframe; + + ret = regs->result; + } else { + /* More signals to go */ + regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE32; + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + sr = (struct sigregs32*)(u64)sigctx.regs; + regs->gpr[3] = ret = sigctx.signal; + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) &sr->tramp; + regs->nip = sigctx.handler; + + if (get_user(prevsp, &sr->gp_regs[PT_R1]) + || put_user(prevsp, (unsigned int*) regs->gpr[1])) + goto badframe; + } + + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - normal exit returning %ld - pid=%ld current=%lx comm=%s \n", ret, current->pid, current, current->comm); + return ret; + +badframe: + PPCDBG(PPCDBG_SYS32NI, "sys32_sigreturn - badframe - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + do_exit(SIGSEGV); +} + +/* + * Set up a signal frame. + */ +static void +setup_frame32(struct pt_regs *regs, struct sigregs32 *frame, + unsigned int newsp) +{ + struct sigcontext32_struct *sc = (struct sigcontext32_struct *)(u64)newsp; + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + + /***************************************************************/ + /* */ + /* Copy the register contents for the pt_regs structure on the */ + /* kernel stack to the elf_gregset_t32 structure on the user */ + /* stack. This is a copy of 64 bit register values to 32 bit */ + /* register values. The high order 32 bits of the 64 bit */ + /* registers are not needed since a 32 bit application is */ + /* running and the saved registers are the contents of the */ + /* user registers at the time of a system call. */ + /* */ + /* The values saved on the user stack will be restored into */ + /* the registers during the signal return processing */ + /* */ + /* Note the +1 is needed in order to get the lower 32 bits */ + /* of 64 bit register */ + /***************************************************************/ + if (__copy_to_user(&frame->gp_regs[0], (u32*)(®s->gpr[0])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[1], (u32*)(®s->gpr[1])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[2], (u32*)(®s->gpr[2])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[3], (u32*)(®s->gpr[3])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[4], (u32*)(®s->gpr[4])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[5], (u32*)(®s->gpr[5])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[6], (u32*)(®s->gpr[6])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[7], (u32*)(®s->gpr[7])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[8], (u32*)(®s->gpr[8])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[9], (u32*)(®s->gpr[9])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[10], (u32*)(®s->gpr[10])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[11], (u32*)(®s->gpr[11])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[12], (u32*)(®s->gpr[12])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[13], (u32*)(®s->gpr[13])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[14], (u32*)(®s->gpr[14])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[15], (u32*)(®s->gpr[15])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[16], (u32*)(®s->gpr[16])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[17], (u32*)(®s->gpr[17])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[18], (u32*)(®s->gpr[18])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[19], (u32*)(®s->gpr[19])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[20], (u32*)(®s->gpr[20])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[21], (u32*)(®s->gpr[21])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[22], (u32*)(®s->gpr[22])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[23], (u32*)(®s->gpr[23])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[24], (u32*)(®s->gpr[24])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[25], (u32*)(®s->gpr[25])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[26], (u32*)(®s->gpr[26])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[27], (u32*)(®s->gpr[27])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[28], (u32*)(®s->gpr[28])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[29], (u32*)(®s->gpr[29])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[30], (u32*)(®s->gpr[30])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[31], (u32*)(®s->gpr[31])+1, sizeof(u32))) + goto badframe; + + /*****************************************************************************/ + /* Copy the non gpr registers to the user stack */ + /*****************************************************************************/ + + if (__copy_to_user(&frame->gp_regs[PT_NIP], (u32*)(®s->gpr[PT_NIP])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_MSR], (u32*)(®s->gpr[PT_MSR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_ORIG_R3], (u32*)(®s->gpr[PT_ORIG_R3])+1, + sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CTR], (u32*)(®s->gpr[PT_CTR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_LNK], (u32*)(®s->gpr[PT_LNK])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_XER], (u32*)(®s->gpr[PT_XER])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CCR], (u32*)(®s->gpr[PT_CCR])+1, sizeof(u32)) +# if 0 + || __copy_to_user(&frame->gp_regs[PT_MQ], (u32*)(®s->gpr[PT_MQ])+1, sizeof(u32)) +#endif + || __copy_to_user(&frame->gp_regs[PT_RESULT], (u32*)(®s->gpr[PT_RESULT])+1, + sizeof(u32))) + goto badframe; + + + /*****************************************************************************/ + /* Now copy the floating point registers onto the user stack */ + /* */ + /* Also set up so on the completion of the signal handler, the sys_sigreturn */ + /* will get control to reset the stack */ + /*****************************************************************************/ + if (__copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38000000U + __NR_sigreturn, &frame->tramp[0]) /* li r0, __NR_sigreturn */ + || __put_user(0x44000002U, &frame->tramp[1])) /* sc */ + goto badframe; + + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + newsp -= __SIGNAL_FRAMESIZE32; + if (put_user(regs->gpr[1], (u32*)(u64)newsp) + || get_user(regs->nip, &sc->handler) + || get_user(regs->gpr[3], &sc->signal)) + goto badframe; + + regs->gpr[1] = newsp & 0xFFFFFFFF; + /**************************************************************/ + /* first parameter to the signal handler is the signal number */ + /* - the value is in gpr3 */ + /* second parameter to the signal handler is the sigcontext */ + /* - set the value into gpr4 */ + /**************************************************************/ + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) frame->tramp; + return; + + badframe: + udbg_printf("setup_frame32 - badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); PPCDBG_ENTER_DEBUGGER(); +#if DEBUG_SIG + printk("badframe in setup_frame32, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + + +/****************************************************************************/ +/* Start of RT signal support */ +/* */ +/* sigset_t is 64 bits for rt signals */ +/* */ +/* System Calls */ +/* sigaction sys32_rt_sigaction */ +/* sigpending sys32_rt_sigpending */ +/* sigprocmask sys32_rt_sigprocmask */ +/* sigreturn sys32_rt_sigreturn */ +/* sigtimedwait sys32_rt_sigtimedwait */ +/* sigqueueinfo sys32_rt_sigqueueinfo */ +/* sigsuspend sys32_rt_sigsuspend */ +/* */ +/* Other routines */ +/* setup_rt_frame32 */ +/* siginfo64to32 */ +/* siginfo32to64 */ +/* */ +/* */ +/****************************************************************************/ + + +// This code executes after the rt signal handler in 32 bit mode has completed and +// returned +long sys32_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, unsigned long r8, + struct pt_regs * regs) +{ + struct rt_sigframe_32 *rt_stack_frame; + struct sigcontext32_struct sigctx; + struct sigregs32 *signalregs; + + int ret; + elf_gregset_t32 saved_regs; /* an array of 32 bit register values */ + sigset_t signal_set; + stack_t stack; + unsigned int previous_stack; + + ret = 0; + /* Adjust the inputted reg1 to point to the first rt signal frame */ + rt_stack_frame = (struct rt_sigframe_32 *)(regs->gpr[1] + __SIGNAL_FRAMESIZE32); + /* Copy the information from the user stack */ + if (copy_from_user(&sigctx, &rt_stack_frame->uc.uc_mcontext,sizeof(sigctx)) + || copy_from_user(&signal_set, &rt_stack_frame->uc.uc_sigmask,sizeof(signal_set)) + || copy_from_user(&stack,&rt_stack_frame->uc.uc_stack,sizeof(stack))) + { + /* unable to copy from user storage */ + goto badframe; + } + + /* Unblock the signal that was processed + * After a signal handler runs - + * if the signal is blockable - the signal will be unblocked + * ( sigkill and sigstop are not blockable) + */ + sigdelsetmask(&signal_set, ~_BLOCKABLE); + /* update the current based on the sigmask found in the rt_stackframe */ + spin_lock_irq(¤t->sigmask_lock); + current->blocked = signal_set; + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + + /* Set to point to the next rt_sigframe - this is used to determine whether this + * is the last signal to process + */ + rt_stack_frame ++; + + if (rt_stack_frame == (struct rt_sigframe_32 *)(u64)(sigctx.regs)) + { + signalregs = (struct sigregs32 *) (u64)sigctx.regs; + /* If currently owning the floating point - give them up */ + if (regs->msr & MSR_FP) + { + giveup_fpu(current); + } + if (copy_from_user(saved_regs,&signalregs->gp_regs,sizeof(signalregs->gp_regs))) + { + goto badframe; + } + /**********************************************************************/ + /* The saved reg structure in the frame is an elf_grepset_t32, it is */ + /* a 32 bit register save of the registers in the pt_regs structure */ + /* that was stored on the kernel stack during the system call */ + /* when the system call was interrupted for the signal. Only 32 bits*/ + /* are saved because the sigcontext contains a pointer to the regs */ + /* and the sig context address is passed as a pointer to the signal */ + /* handler. */ + /* */ + /* The entries in the elf_grepset have the same index as the elements */ + /* in the pt_regs structure. */ + /* */ + /**********************************************************************/ + + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + regs->gpr[0] = (u64)(saved_regs[0]) & 0xFFFFFFFF; + regs->gpr[1] = (u64)(saved_regs[1]) & 0xFFFFFFFF; + /**********************************************************************/ + /* Register 2 is the kernel toc - should be reset on any calls into */ + /* the kernel */ + /**********************************************************************/ + regs->gpr[2] = (u64)(saved_regs[2]) & 0xFFFFFFFF; + + regs->gpr[3] = (u64)(saved_regs[3]) & 0xFFFFFFFF; + regs->gpr[4] = (u64)(saved_regs[4]) & 0xFFFFFFFF; + regs->gpr[5] = (u64)(saved_regs[5]) & 0xFFFFFFFF; + regs->gpr[6] = (u64)(saved_regs[6]) & 0xFFFFFFFF; + regs->gpr[7] = (u64)(saved_regs[7]) & 0xFFFFFFFF; + regs->gpr[8] = (u64)(saved_regs[8]) & 0xFFFFFFFF; + regs->gpr[9] = (u64)(saved_regs[9]) & 0xFFFFFFFF; + regs->gpr[10] = (u64)(saved_regs[10]) & 0xFFFFFFFF; + regs->gpr[11] = (u64)(saved_regs[11]) & 0xFFFFFFFF; + regs->gpr[12] = (u64)(saved_regs[12]) & 0xFFFFFFFF; + regs->gpr[13] = (u64)(saved_regs[13]) & 0xFFFFFFFF; + regs->gpr[14] = (u64)(saved_regs[14]) & 0xFFFFFFFF; + regs->gpr[15] = (u64)(saved_regs[15]) & 0xFFFFFFFF; + regs->gpr[16] = (u64)(saved_regs[16]) & 0xFFFFFFFF; + regs->gpr[17] = (u64)(saved_regs[17]) & 0xFFFFFFFF; + regs->gpr[18] = (u64)(saved_regs[18]) & 0xFFFFFFFF; + regs->gpr[19] = (u64)(saved_regs[19]) & 0xFFFFFFFF; + regs->gpr[20] = (u64)(saved_regs[20]) & 0xFFFFFFFF; + regs->gpr[21] = (u64)(saved_regs[21]) & 0xFFFFFFFF; + regs->gpr[22] = (u64)(saved_regs[22]) & 0xFFFFFFFF; + regs->gpr[23] = (u64)(saved_regs[23]) & 0xFFFFFFFF; + regs->gpr[24] = (u64)(saved_regs[24]) & 0xFFFFFFFF; + regs->gpr[25] = (u64)(saved_regs[25]) & 0xFFFFFFFF; + regs->gpr[26] = (u64)(saved_regs[26]) & 0xFFFFFFFF; + regs->gpr[27] = (u64)(saved_regs[27]) & 0xFFFFFFFF; + regs->gpr[28] = (u64)(saved_regs[28]) & 0xFFFFFFFF; + regs->gpr[29] = (u64)(saved_regs[29]) & 0xFFFFFFFF; + regs->gpr[30] = (u64)(saved_regs[30]) & 0xFFFFFFFF; + regs->gpr[31] = (u64)(saved_regs[31]) & 0xFFFFFFFF; + /****************************************************/ + /* restore the non gpr registers */ + /****************************************************/ + regs->msr = (u64)(saved_regs[PT_MSR]) & 0xFFFFFFFF; + + regs->nip = (u64)(saved_regs[PT_NIP]) & 0xFFFFFFFF; + regs->orig_gpr3 = (u64)(saved_regs[PT_ORIG_R3]) & 0xFFFFFFFF; + regs->ctr = (u64)(saved_regs[PT_CTR]) & 0xFFFFFFFF; + regs->link = (u64)(saved_regs[PT_LNK]) & 0xFFFFFFFF; + regs->xer = (u64)(saved_regs[PT_XER]) & 0xFFFFFFFF; + regs->ccr = (u64)(saved_regs[PT_CCR]) & 0xFFFFFFFF; + /* regs->softe is left unchanged (like MSR.EE) */ + /******************************************************/ + /* the DAR and the DSISR are only relevant during a */ + /* data or instruction storage interrupt. The value */ + /* will be set to zero. */ + /******************************************************/ + regs->dar = 0; + regs->dsisr = 0; + regs->result = (u64)(saved_regs[PT_RESULT]) & 0xFFFFFFFF; + ret = regs->result; + } + else /* more signals to go */ + { + udbg_printf("hey should not occur\n"); + regs->gpr[1] = (u64)rt_stack_frame - __SIGNAL_FRAMESIZE32; + if (copy_from_user(&sigctx, &rt_stack_frame->uc.uc_mcontext,sizeof(sigctx))) + { + goto badframe; + } + signalregs = (struct sigregs32 *) (u64)sigctx.regs; + /* first parm to signal handler is the signal number */ + regs->gpr[3] = ret = sigctx.signal; + /* second parm is a pointer to sig info */ + get_user(regs->gpr[4], &rt_stack_frame->pinfo); + /* third parm is a pointer to the ucontext */ + get_user(regs->gpr[5], &rt_stack_frame->puc); + /* fourth parm is the stack frame */ + regs->gpr[6] = (u64)rt_stack_frame; + /* Set up link register to return to sigreturn when the */ + /* signal handler completes */ + regs->link = (u64)&signalregs->tramp; + /* Set next instruction to the start fo the signal handler */ + regs->nip = sigctx.handler; + /* Set the reg1 to look like a call to the signal handler */ + if (get_user(previous_stack,&signalregs->gp_regs[PT_R1]) + || put_user(previous_stack, (unsigned long *)regs->gpr[1])) + { + goto badframe; + } + + } + + return ret; + + badframe: + do_exit(SIGSEGV); +} + + + +asmlinkage long sys32_rt_sigaction(int sig, const struct sigaction32 *act, struct sigaction32 *oact, size_t sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + sigset32_t set32; + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigaction - entered - sig=%x \n", sig); + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset32_t)) + return -EINVAL; + + if (act) { + ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); + ret |= __copy_from_user(&set32, &act->sa_mask, + sizeof(sigset32_t)); + switch (_NSIG_WORDS) { + case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] + | (((long)set32.sig[7]) << 32); + case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] + | (((long)set32.sig[5]) << 32); + case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] + | (((long)set32.sig[3]) << 32); + case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] + | (((long)set32.sig[1]) << 32); + } + + ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + + if (ret) + return -EFAULT; + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + switch (_NSIG_WORDS) { + case 4: + set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); + set32.sig[6] = old_ka.sa.sa_mask.sig[3]; + case 3: + set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); + set32.sig[4] = old_ka.sa.sa_mask.sig[2]; + case 2: + set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); + set32.sig[2] = old_ka.sa.sa_mask.sig[1]; + case 1: + set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); + set32.sig[0] = old_ka.sa.sa_mask.sig[0]; + } + ret = put_user((long)old_ka.sa.sa_handler, &oact->sa_handler); + ret |= __copy_to_user(&oact->sa_mask, &set32, + sizeof(sigset32_t)); + ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + } + + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigaction - exiting - sig=%x \n", sig); + return ret; +} + + +extern asmlinkage long sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, + size_t sigsetsize); + +/* Note: it is necessary to treat how as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_rt_sigprocmask(u32 how, sigset32_t *set, sigset32_t *oset, size_t sigsetsize) +{ + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigprocmask - entered how=%x \n", (int)how); + + if (set) { + if (copy_from_user (&s32, set, sizeof(sigset32_t))) + return -EFAULT; + + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + } + + set_fs (KERNEL_DS); + ret = sys_rt_sigprocmask((int)how, set ? &s : NULL, oset ? &s : NULL, + sigsetsize); + set_fs (old_fs); + if (ret) return ret; + if (oset) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (oset, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return 0; +} + + +extern asmlinkage long sys_rt_sigpending(sigset_t *set, size_t sigsetsize); + + + +asmlinkage long sys32_rt_sigpending(sigset32_t *set, __kernel_size_t32 sigsetsize) +{ + + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_rt_sigpending(&s, sigsetsize); + set_fs (old_fs); + if (!ret) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (set, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return ret; +} + + + +siginfo_t32 * +siginfo64to32(siginfo_t32 *d, siginfo_t *s) +{ + memset (d, 0, sizeof(siginfo_t32)); + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (long)(s->si_addr); + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +extern asmlinkage long +sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, + const struct timespec *uts, size_t sigsetsize); + +asmlinkage long +sys32_rt_sigtimedwait(sigset32_t *uthese, siginfo_t32 *uinfo, + struct timespec32 *uts, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset32_t s32; + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs(); + siginfo_t info; + siginfo_t32 info32; + + if (copy_from_user (&s32, uthese, sizeof(sigset32_t))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + if (uts) { + ret = get_user (t.tv_sec, &uts->tv_sec); + ret |= __get_user (t.tv_nsec, &uts->tv_nsec); + if (ret) + return -EFAULT; + } + set_fs (KERNEL_DS); + if (uts) + { + ret = sys_rt_sigtimedwait(&s, &info, &t, sigsetsize); + } else { + ret = sys_rt_sigtimedwait(&s, &info, (struct timespec *)uts, sigsetsize); + } + + set_fs (old_fs); + if (ret >= 0 && uinfo) { + if (copy_to_user (uinfo, siginfo64to32(&info32, &info), + sizeof(siginfo_t32))) + return -EFAULT; + } + return ret; +} + + + +siginfo_t * +siginfo32to64(siginfo_t *d, siginfo_t32 *s) +{ + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + d->si_int = s->si_int; + + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (void *)A(s->si_addr); + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + + +extern asmlinkage long sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo); + +/* Note: it is necessary to treat pid and sig as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_rt_sigqueueinfo(u32 pid, u32 sig, siginfo_t32 *uinfo) +{ + siginfo_t info; + siginfo_t32 info32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (copy_from_user (&info32, uinfo, sizeof(siginfo_t32))) + return -EFAULT; + /* XXX: Is this correct? */ + siginfo32to64(&info, &info32); + + set_fs (KERNEL_DS); + ret = sys_rt_sigqueueinfo((int)pid, (int)sig, &info); + set_fs (old_fs); + return ret; +} + + +int do_signal(sigset_t *oldset, struct pt_regs *regs); +int sys32_rt_sigsuspend(sigset32_t* unewset, size_t sigsetsize, int p3, int p4, int p6, int p7, struct pt_regs *regs) +{ + sigset_t saveset, newset; + + sigset32_t s32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&s32, unewset, sizeof(s32))) + return -EFAULT; + + /* Swap the 2 words of the 64-bit sigset_t (they are stored in the "wrong" endian in 32-bit user storage). */ + switch (_NSIG_WORDS) + { + case 4: newset.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: newset.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: newset.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: newset.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gpr[3] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return regs->gpr[3]; + } +} + + + + + + + + +/* + * Set up a rt signal frame. + */ +static void +setup_rt_frame32(struct pt_regs *regs, struct sigregs32 *frame, + unsigned int newsp) +{ + unsigned int copyreg4,copyreg5; + struct rt_sigframe_32 * rt_sf = (struct rt_sigframe_32 *) (u64)newsp; + + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + /***************************************************************/ + /* */ + /* Copy the register contents for the pt_regs structure on the */ + /* kernel stack to the elf_gregset_t32 structure on the user */ + /* stack. This is a copy of 64 bit register values to 32 bit */ + /* register values. The high order 32 bits of the 64 bit */ + /* registers are not needed since a 32 bit application is */ + /* running and the saved registers are the contents of the */ + /* user registers at the time of a system call. */ + /* */ + /* The values saved on the user stack will be restored into */ + /* the registers during the signal return processing */ + /* */ + /* Note the +1 is needed in order to get the lower 32 bits */ + /* of 64 bit register */ + /***************************************************************/ + if (__copy_to_user(&frame->gp_regs[0], (u32*)(®s->gpr[0])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[1], (u32*)(®s->gpr[1])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[2], (u32*)(®s->gpr[2])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[3], (u32*)(®s->gpr[3])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[4], (u32*)(®s->gpr[4])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[5], (u32*)(®s->gpr[5])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[6], (u32*)(®s->gpr[6])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[7], (u32*)(®s->gpr[7])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[8], (u32*)(®s->gpr[8])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[9], (u32*)(®s->gpr[9])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[10], (u32*)(®s->gpr[10])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[11], (u32*)(®s->gpr[11])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[12], (u32*)(®s->gpr[12])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[13], (u32*)(®s->gpr[13])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[14], (u32*)(®s->gpr[14])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[15], (u32*)(®s->gpr[15])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[16], (u32*)(®s->gpr[16])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[17], (u32*)(®s->gpr[17])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[18], (u32*)(®s->gpr[18])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[19], (u32*)(®s->gpr[19])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[20], (u32*)(®s->gpr[20])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[21], (u32*)(®s->gpr[21])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[22], (u32*)(®s->gpr[22])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[23], (u32*)(®s->gpr[23])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[24], (u32*)(®s->gpr[24])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[25], (u32*)(®s->gpr[25])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[26], (u32*)(®s->gpr[26])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[27], (u32*)(®s->gpr[27])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[28], (u32*)(®s->gpr[28])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[29], (u32*)(®s->gpr[29])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[30], (u32*)(®s->gpr[30])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[31], (u32*)(®s->gpr[31])+1, sizeof(u32))) + goto badframe; + + /*****************************************************************************/ + /* Copy the non gpr registers to the user stack */ + /*****************************************************************************/ + + if (__copy_to_user(&frame->gp_regs[PT_NIP], (u32*)(®s->gpr[PT_NIP])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_MSR], (u32*)(®s->gpr[PT_MSR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_ORIG_R3], (u32*)(®s->gpr[PT_ORIG_R3])+1, + sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CTR], (u32*)(®s->gpr[PT_CTR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_LNK], (u32*)(®s->gpr[PT_LNK])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_XER], (u32*)(®s->gpr[PT_XER])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CCR], (u32*)(®s->gpr[PT_CCR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_RESULT], (u32*)(®s->gpr[PT_RESULT])+1, + sizeof(u32))) + goto badframe; + + + /*****************************************************************************/ + /* Now copy the floating point registers onto the user stack */ + /* */ + /* Also set up so on the completion of the signal handler, the sys_sigreturn */ + /* will get control to reset the stack */ + /*****************************************************************************/ + + + if (__copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38000000U + __NR_rt_sigreturn, &frame->tramp[0]) /* li r0, __NR_rt_sigreturn */ + || __put_user(0x44000002U, &frame->tramp[1])) /* sc */ + goto badframe; + + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + + /* Retrieve rt_sigframe from stack and + set up registers for signal handler + */ + newsp -= __SIGNAL_FRAMESIZE32; + + + if (put_user((u32)(regs->gpr[1]), (unsigned int *)(u64)newsp) + || get_user(regs->nip, &rt_sf->uc.uc_mcontext.handler) + || get_user(regs->gpr[3], &rt_sf->uc.uc_mcontext.signal) + || get_user(copyreg4, &rt_sf->pinfo) + || get_user(copyreg5, &rt_sf->puc)) + goto badframe; + + regs->gpr[4] = copyreg4; + regs->gpr[5] = copyreg5; + + + regs->gpr[1] = newsp; + regs->gpr[6] = (unsigned long) rt_sf; + + + regs->link = (unsigned long) frame->tramp; + + return; + + + badframe: + udbg_printf("setup_frame32 - badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); PPCDBG_ENTER_DEBUGGER(); +#if DEBUG_SIG + printk("badframe in setup_frame32, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + + +/* + * OK, we're invoking a handler + */ +static void +handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned int *newspp, unsigned int frame) +{ + struct sigcontext32_struct *sc; + struct rt_sigframe_32 *rt_stack_frame; + siginfo_t32 siginfo32bit; + + if (regs->trap == 0x0C00 /* System Call! */ + && ((int)regs->result == -ERESTARTNOHAND || + ((int)regs->result == -ERESTARTSYS && + !(ka->sa.sa_flags & SA_RESTART)))) + regs->result = -EINTR; + + /* Set up the signal frame */ + /* Determine if an real time frame - siginfo required */ + if (ka->sa.sa_flags & SA_SIGINFO) + { + siginfo64to32(&siginfo32bit,info); + *newspp -= sizeof(*rt_stack_frame); + rt_stack_frame = (struct rt_sigframe_32 *) (u64)(*newspp) ; + + if (verify_area(VERIFY_WRITE, rt_stack_frame, sizeof(*rt_stack_frame))) + { + goto badframe; + } + if (__put_user((u32)(u64)ka->sa.sa_handler, &rt_stack_frame->uc.uc_mcontext.handler) + || __put_user((u32)(u64)&rt_stack_frame->info, &rt_stack_frame->pinfo) + || __put_user((u32)(u64)&rt_stack_frame->uc, &rt_stack_frame->puc) + /* put the siginfo on the user stack */ + || __copy_to_user(&rt_stack_frame->info,&siginfo32bit,sizeof(siginfo32bit)) + /* set the ucontext on the user stack */ + || __put_user(0,&rt_stack_frame->uc.uc_flags) + || __put_user(0,&rt_stack_frame->uc.uc_link) + || __put_user(current->sas_ss_sp, &rt_stack_frame->uc.uc_stack.ss_sp) + || __put_user(sas_ss_flags(regs->gpr[1]), + &rt_stack_frame->uc.uc_stack.ss_flags) + || __put_user(current->sas_ss_size, &rt_stack_frame->uc.uc_stack.ss_size) + || __copy_to_user(&rt_stack_frame->uc.uc_sigmask, oldset,sizeof(*oldset)) + /* point the mcontext.regs to the pramble register frame */ + || __put_user(frame, &rt_stack_frame->uc.uc_mcontext.regs) + || __put_user(sig,&rt_stack_frame->uc.uc_mcontext.signal)) + { + goto badframe; + } + } else { + /* Put another sigcontext on the stack */ + *newspp -= sizeof(*sc); + sc = (struct sigcontext32_struct *)(u64)*newspp; + if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) + goto badframe; + + /* Note the upper 32 bits of the signal mask are stored in the */ + /* unused part of the signal stack frame */ + if (__put_user((u32)(u64)ka->sa.sa_handler, &sc->handler) + || __put_user(oldset->sig[0], &sc->oldmask) + || __put_user((oldset->sig[0] >> 32), &sc->_unused[3]) + || __put_user((unsigned int)frame, &sc->regs) + || __put_user(sig, &sc->signal)) + goto badframe; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + } + } + return; + +badframe: +#if DEBUG_SIG + printk("badframe in handle_signal32, regs=%p frame=%lx newsp=%lx\n", + regs, frame, *newspp); + printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset); +#endif + do_exit(SIGSEGV); +} + + +/****************************************************************************/ +/* Start Alternate signal stack support */ +/* */ +/* */ +/* */ +/* System Calls */ +/* sigaltatck sys32_sigaltstack */ +/* */ +/****************************************************************************/ + + +asmlinkage int sys32_sigaltstack(u32 newstack, u32 oldstack, int p3, int p4, int p6, + int p7, struct pt_regs *regs) +{ + stack_t uss, uoss; + int ret; + mm_segment_t old_fs; + unsigned long sp; + + /* set sp to the user stack on entry to the system call */ + /* the system call router sets R9 to the saved registers */ + sp = regs->gpr[1]; + + /* Put new stack info in local 64 bit stack struct */ + if (newstack && (get_user((long)uss.ss_sp, &((stack_32_t *)(long)newstack)->ss_sp) || + __get_user(uss.ss_flags, &((stack_32_t *)(long)newstack)->ss_flags) || + __get_user(uss.ss_size, &((stack_32_t *)(long)newstack)->ss_size))) + return -EFAULT; + + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = do_sigaltstack(newstack ? &uss : NULL, oldstack ? &uoss : NULL, sp); + set_fs(old_fs); + /* Copy the stack information to the user output buffer */ + if (!ret && oldstack && (put_user((long)uoss.ss_sp, &((stack_32_t *)(long)oldstack)->ss_sp) || + __put_user(uoss.ss_flags, &((stack_32_t *)(long)oldstack)->ss_flags) || + __put_user(uoss.ss_size, &((stack_32_t *)(long)oldstack)->ss_size))) + return -EFAULT; + return ret; +} + + + +/****************************************************************************/ +/* Start of do_signal32 routine */ +/* */ +/* This routine gets control when a pemding signal needs to be processed */ +/* in the 32 bit target thread - */ +/* */ +/* It handles both rt and non-rt signals */ +/* */ +/****************************************************************************/ + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ + +int do_signal32(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + struct k_sigaction *ka; + unsigned int frame, newsp; + + if (!oldset) + oldset = ¤t->blocked; + + newsp = frame = 0; + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + ifppcdebug(PPCDBG_SYS32) { + if (signr) + udbg_printf("do_signal32 - processing signal=%2lx - pid=%ld, comm=%s \n", signr, current->pid, current->comm); + } + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sig_exit(signr, exit_code, &info); + /* NOTREACHED */ + } + } + + PPCDBG(PPCDBG_SIGNAL, " do signal :sigaction flags = %lx \n" ,ka->sa.sa_flags); + PPCDBG(PPCDBG_SIGNAL, " do signal :on sig stack = %lx \n" ,on_sig_stack(regs->gpr[1])); + PPCDBG(PPCDBG_SIGNAL, " do signal :reg1 = %lx \n" ,regs->gpr[1]); + PPCDBG(PPCDBG_SIGNAL, " do signal :alt stack = %lx \n" ,current->sas_ss_sp); + PPCDBG(PPCDBG_SIGNAL, " do signal :alt stack size = %lx \n" ,current->sas_ss_size); + + + + if ( (ka->sa.sa_flags & SA_ONSTACK) + && (! on_sig_stack(regs->gpr[1]))) + { + newsp = (current->sas_ss_sp + current->sas_ss_size); + } else + newsp = regs->gpr[1]; + newsp = frame = newsp - sizeof(struct sigregs32); + + /* Whee! Actually deliver the signal. */ + handle_signal32(signr, ka, &info, oldset, regs, &newsp, frame); + break; + } + + if (regs->trap == 0x0C00 /* System Call! */ && + ((int)regs->result == -ERESTARTNOHAND || + (int)regs->result == -ERESTARTSYS || + (int)regs->result == -ERESTARTNOINTR)) { + regs->gpr[3] = regs->orig_gpr3; + regs->nip -= 4; /* Back up & retry system call */ + regs->result = 0; + } + + if (newsp == frame) + { + return 0; /* no signals delivered */ + } + // Invoke correct stack setup routine + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame32(regs, (struct sigregs32*)(u64)frame, newsp); + else + setup_frame32(regs, (struct sigregs32*)(u64)frame, newsp); + + return 1; + +} diff -Nru a/arch/ppc64/kernel/smp.c b/arch/ppc64/kernel/smp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/smp.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,778 @@ +/* + * + * + * SMP support for ppc. + * + * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great + * deal of code from the sparc and intel versions. + * + * Copyright (C) 1999 Cort Dougan + * + * PowerPC-64 Support added by Dave Engebretsen, Peter Bergner, and + * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include +#include +/* #include */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "open_pic.h" +#include + +int smp_threads_ready = 0; +volatile int smp_commenced = 0; +int smp_num_cpus = 1; +int smp_tb_synchronized = 0; +spinlock_t kernel_flag __cacheline_aligned = SPIN_LOCK_UNLOCKED; +cycles_t cacheflush_time; +unsigned long cache_decay_ticks; +static int max_cpus __initdata = NR_CPUS; + +unsigned long cpu_online_map; + +volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; + +#define TB_SYNC_PASSES 4 +volatile unsigned long __initdata tb_sync_flag = 0; +volatile unsigned long __initdata tb_offset = 0; + +extern unsigned char stab_array[]; + +int start_secondary(void *); +extern int cpu_idle(void *unused); +void smp_call_function_interrupt(void); +void smp_message_pass(int target, int msg, unsigned long data, int wait); +static unsigned long iSeries_smp_message[NR_CPUS]; +extern struct Naca *naca; +extern struct Paca xPaca[]; + +void xics_setup_cpu(void); +void xics_cause_IPI(int cpu); + +/* + * XICS only has a single IPI, so encode the messages per CPU + */ +volatile unsigned long xics_ipi_message[NR_CPUS] = {0}; + +#define smp_message_pass(t,m,d,w) ppc_md.smp_message_pass((t),(m),(d),(w)) + +static inline void set_tb(unsigned int upper, unsigned int lower) +{ + mtspr(SPRN_TBWL, 0); + mtspr(SPRN_TBWU, upper); + mtspr(SPRN_TBWL, lower); +} + +void iSeries_smp_message_recv( struct pt_regs * regs ) +{ + int cpu = smp_processor_id(); + int msg; + + if ( smp_num_cpus < 2 ) + return; + + for ( msg = 0; msg < 4; ++msg ) + if ( test_and_clear_bit( msg, &iSeries_smp_message[cpu] ) ) + smp_message_recv( msg, regs ); + +} + +static void smp_iSeries_message_pass(int target, int msg, unsigned long data, int wait) +{ + int i; + for (i = 0; i < smp_num_cpus; ++i) { + if ( (target == MSG_ALL) || + (target == i) || + ((target == MSG_ALL_BUT_SELF) && (i != smp_processor_id())) ) { + set_bit( msg, &iSeries_smp_message[i] ); + HvCall_sendIPI(&(xPaca[i])); + } + } +} + +static int smp_iSeries_numProcs(void) +{ + unsigned np, i; + struct ItLpPaca * lpPaca; + + np = 0; + for (i=0; i < maxPacas; ++i) { + lpPaca = xPaca[i].xLpPacaPtr; + if ( lpPaca->xDynProcStatus < 2 ) { + ++np; + } + } + return np; +} + +static int smp_iSeries_probe(void) +{ + unsigned i; + unsigned np; + struct ItLpPaca * lpPaca; + + np = 0; + for (i=0; i < maxPacas; ++i) { + lpPaca = xPaca[i].xLpPacaPtr; + if ( lpPaca->xDynProcStatus < 2 ) { + ++np; + xPaca[i].next_jiffy_update_tb = xPaca[0].next_jiffy_update_tb; + } + } + + smp_tb_synchronized = 1; + return np; +} + +static void smp_iSeries_kick_cpu(int nr) +{ + struct ItLpPaca * lpPaca; + /* Verify we have a Paca for processor nr */ + if ( ( nr <= 0 ) || + ( nr >= maxPacas ) ) + return; + /* Verify that our partition has a processor nr */ + lpPaca = xPaca[nr].xLpPacaPtr; + if ( lpPaca->xDynProcStatus >= 2 ) + return; + /* The processor is currently spinning, waiting + * for the xProcStart field to become non-zero + * After we set xProcStart, the processor will + * continue on to secondary_start in iSeries_head.S + */ + xPaca[nr].xProcStart = 1; +} + +static void smp_iSeries_setup_cpu(int nr) +{ +} + +/* This is called very early. */ +void smp_init_iSeries(void) +{ + ppc_md.smp_message_pass = smp_iSeries_message_pass; + ppc_md.smp_probe = smp_iSeries_probe; + ppc_md.smp_kick_cpu = smp_iSeries_kick_cpu; + ppc_md.smp_setup_cpu = smp_iSeries_setup_cpu; + + naca->processorCount = smp_iSeries_numProcs(); +} + + +static void +smp_openpic_message_pass(int target, int msg, unsigned long data, int wait) +{ + /* make sure we're sending something that translates to an IPI */ + if ( msg > 0x3 ){ + printk("SMP %d: smp_message_pass: unknown msg %d\n", + smp_processor_id(), msg); + return; + } + switch ( target ) + { + case MSG_ALL: + openpic_cause_IPI(msg, 0xffffffff); + break; + case MSG_ALL_BUT_SELF: + openpic_cause_IPI(msg, + 0xffffffff & ~(1 << smp_processor_id())); + break; + default: + openpic_cause_IPI(msg, 1<processorCount > 1) + openpic_request_IPIs(); + + return naca->processorCount; +} + +static void +smp_kick_cpu(int nr) +{ + /* Verify we have a Paca for processor nr */ + if ( ( nr <= 0 ) || + ( nr >= maxPacas ) ) + return; + + /* The processor is currently spinning, waiting + * for the xProcStart field to become non-zero + * After we set xProcStart, the processor will + * continue on to secondary_start in iSeries_head.S + */ + xPaca[nr].xProcStart = 1; +} + +extern struct gettimeofday_struct do_gtod; + +static void smp_space_timers( unsigned nr ) +{ + unsigned long offset, i; + + offset = tb_ticks_per_jiffy / nr; + for ( i=1; i 0) + xics_setup_cpu(); + } +} + +static void +smp_xics_message_pass(int target, int msg, unsigned long data, int wait) +{ + int i; + + for (i = 0; i < smp_num_cpus; ++i) { + if (target == MSG_ALL || target == i + || (target == MSG_ALL_BUT_SELF + && i != smp_processor_id())) { + set_bit(msg, &xics_ipi_message[i]); + mb(); + xics_cause_IPI(i); + } + } +} + +static int +smp_xics_probe(void) +{ + return naca->processorCount; +} + +/* This is called very early */ +void smp_init_pSeries(void) +{ + if(naca->interrupt_controller == IC_OPEN_PIC) { + ppc_md.smp_message_pass = smp_openpic_message_pass; + ppc_md.smp_probe = smp_chrp_probe; + ppc_md.smp_kick_cpu = smp_kick_cpu; + ppc_md.smp_setup_cpu = smp_chrp_setup_cpu; + } else { + ppc_md.smp_message_pass = smp_xics_message_pass; + ppc_md.smp_probe = smp_xics_probe; + ppc_md.smp_kick_cpu = smp_kick_cpu; + ppc_md.smp_setup_cpu = smp_chrp_setup_cpu; + } +} + + +void smp_local_timer_interrupt(struct pt_regs * regs) +{ + if (!--(get_paca()->prof_counter)) { + update_process_times(user_mode(regs)); + (get_paca()->prof_counter)=get_paca()->prof_multiplier; + } +} + +static spinlock_t migration_lock = SPIN_LOCK_UNLOCKED; +static task_t *new_task; + +/* + * This function sends a 'task migration' IPI to another CPU. + * Must be called from syscall contexts, with interrupts *enabled*. + */ +void smp_migrate_task(int cpu, task_t *p) +{ + /* + * The target CPU will unlock the migration spinlock: + */ + spin_lock(&migration_lock); + new_task = p; + + smp_message_pass(cpu, PPC_MSG_MIGRATE_TASK, 0, 0); +} + +/* + * Task migration callback. + */ +static void smp_task_migration_interrupt(void) +{ + task_t *p; + + /* Should we ACK the IPI interrupt early? */ + p = new_task; + spin_unlock(&migration_lock); + sched_task_migrated(p); +} + +void smp_message_recv(int msg, struct pt_regs *regs) +{ + switch( msg ) { + case PPC_MSG_CALL_FUNCTION: + smp_call_function_interrupt(); + break; + case PPC_MSG_RESCHEDULE: + /* XXX Do we have to do this? */ + set_need_resched(); + break; + case PPC_MSG_MIGRATE_TASK: + smp_task_migration_interrupt(); + break; +#ifdef CONFIG_XMON + case PPC_MSG_XMON_BREAK: + xmon(regs); + break; +#endif /* CONFIG_XMON */ + default: + printk("SMP %d: smp_message_recv(): unknown msg %d\n", + smp_processor_id(), msg); + break; + } +} + +void smp_send_reschedule(int cpu) +{ + /* + * This is only used if `cpu' is running an idle task, + * so it will reschedule itself anyway... + * + * This isn't the case anymore since the other CPU could be + * sleeping and won't reschedule until the next interrupt (such + * as the timer). + * -- Cort + */ + /* This is only used if `cpu' is running an idle task, + so it will reschedule itself anyway... */ + smp_message_pass(cpu, PPC_MSG_RESCHEDULE, 0, 0); +} + +/* + * this function sends a reschedule IPI to all (other) CPUs. + * This should only be used if some 'global' task became runnable, + * such as a RT task, that must be handled now. The first CPU + * that manages to grab the task will run it. + */ +void smp_send_reschedule_all(void) +{ + smp_message_pass(MSG_ALL_BUT_SELF, PPC_MSG_RESCHEDULE, 0, 0); +} + +#ifdef CONFIG_XMON +void smp_send_xmon_break(int cpu) +{ + smp_message_pass(cpu, PPC_MSG_XMON_BREAK, 0, 0); +} +#endif /* CONFIG_XMON */ + +static void stop_this_cpu(void *dummy) +{ + __cli(); + while (1) + ; +} + +void smp_send_stop(void) +{ + smp_call_function(stop_this_cpu, NULL, 1, 0); + smp_num_cpus = 1; +} + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + * Stolen from the i386 version. + */ +static spinlock_t call_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; + +static struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +} *call_data; + +/* + * This function sends a 'generic call function' IPI to all other CPUs + * in the system. + * + * [SUMMARY] Run a function on all other CPUs. + * The function to run. This must be fast and non-blocking. + * An arbitrary pointer to pass to the function. + * currently unused. + * If true, wait (atomically) until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <> or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler, you may call it from a bottom half handler. + */ +int smp_call_function (void (*func) (void *info), void *info, int nonatomic, + int wait) + +{ + struct call_data_struct data; + int ret = -1, cpus = smp_num_cpus-1; + int timeout; + + if (!cpus) + return 0; + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock_bh(&call_lock); + call_data = &data; + /* Send a message to all other CPUs and wait for them to respond */ + smp_message_pass(MSG_ALL_BUT_SELF, PPC_MSG_CALL_FUNCTION, 0, 0); + + /* Wait for response */ + timeout = 8000000; + while (atomic_read(&data.started) != cpus) { + HMT_low(); + if (--timeout == 0) { + printk("smp_call_function on cpu %d: other cpus not responding (%d)\n", + smp_processor_id(), atomic_read(&data.started)); +#ifdef CONFIG_XMON + xmon(0); +#endif +#ifdef CONFIG_PPC_ISERIES + HvCall_terminateMachineSrc(); +#endif + goto out; + } + barrier(); + udelay(1); + } + + if (wait) { + timeout = 1000000; + while (atomic_read(&data.finished) != cpus) { + HMT_low(); + if (--timeout == 0) { + printk("smp_call_function on cpu %d: other cpus not finishing (%d/%d)\n", + smp_processor_id(), atomic_read(&data.finished), atomic_read(&data.started)); +#ifdef CONFIG_PPC_ISERIES + HvCall_terminateMachineSrc(); +#endif + goto out; + } + barrier(); + udelay(1); + } + } + ret = 0; + + out: + HMT_medium(); + spin_unlock_bh(&call_lock); + return ret; +} + +void smp_call_function_interrupt(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function + */ + atomic_inc(&call_data->started); + /* + * At this point the info structure may be out of scope unless wait==1 + */ + (*func)(info); + if (wait) + atomic_inc(&call_data->finished); +} + + +extern unsigned long decr_overclock; + +struct thread_struct *current_set[NR_CPUS] = {&init_thread_union, 0}; + +void __init smp_boot_cpus(void) +{ + struct Paca *paca; + int i, cpu_nr; + struct task_struct *p; + + printk("Entering SMP Mode...\n"); + + smp_num_cpus = 1; + smp_store_cpu_info(0); + cpu_online_map = 1UL; + + /* + * assume for now that the first cpu booted is + * cpu 0, the master -- Cort + */ + cpu_callin_map[0] = 1; + /* XXX buggy - Anton */ + current_thread_info()->cpu = 0; + + for (i = 0; i < NR_CPUS; i++) { + paca = &xPaca[i]; + paca->prof_counter = 1; + paca->prof_multiplier = 1; + if(i != 0) { + /* + * Processor 0's segment table is statically + * initialized to real address 0x5000. The + * Other processor's tables are created and + * initialized here. + */ + paca->xStab_data.virt = (unsigned long)&stab_array[PAGE_SIZE * (i-1)]; + memset((void *)paca->xStab_data.virt, 0, PAGE_SIZE); + paca->xStab_data.real = __v2a(paca->xStab_data.virt); + paca->default_decr = tb_ticks_per_jiffy / decr_overclock; + } + } + + /* + * XXX very rough, assumes 20 bus cycles to read a cache line, + * timebase increments every 4 bus cycles, 32kB L1 data cache. + */ + cacheflush_time = 5 * 1024; + /* XXX - Fix - Anton */ + cache_decay_ticks = 0; + + /* Probe arch for CPUs */ + cpu_nr = ppc_md.smp_probe(); + + printk("Probe found %d CPUs\n", cpu_nr); + + /* + * only check for cpus we know exist. We keep the callin map + * with cpus at the bottom -- Cort + */ + if (cpu_nr > max_cpus) + cpu_nr = max_cpus; + +#ifdef CONFIG_ISERIES + smp_space_timers( cpu_nr ); +#endif + + printk("Waiting for %d CPUs\n", cpu_nr-1); + + for ( i = 1 ; i < cpu_nr; i++ ) { + int c; + struct pt_regs regs; + + /* create a process for the processor */ + /* we don't care about the values in regs since we'll + never reschedule the forked task. */ + /* We DO care about one bit in the pt_regs we + pass to do_fork. That is the MSR_FP bit in + regs.msr. If that bit is on, then do_fork + (via copy_thread) will call giveup_fpu. + giveup_fpu will get a pointer to our (current's) + last register savearea via current->thread.regs + and using that pointer will turn off the MSR_FP, + MSR_FE0 and MSR_FE1 bits. At this point, this + pointer is pointing to some arbitrary point within + our stack */ + + memset(®s, 0, sizeof(struct pt_regs)); + + if (do_fork(CLONE_VM|CLONE_PID, 0, ®s, 0) < 0) + panic("failed fork for CPU %d", i); + p = init_task.prev_task; + if (!p) + panic("No idle task for CPU %d", i); + + init_idle(p, i); + + unhash_process(p); + + xPaca[i].xCurrent = (u64)p; + current_set[i] = p->thread_info; + + /* wake up cpus */ + ppc_md.smp_kick_cpu(i); + + /* + * wait to see if the cpu made a callin (is actually up). + * use this value that I found through experimentation. + * -- Cort + */ + for ( c = 5000; c && !cpu_callin_map[i] ; c-- ) { + udelay(100); + } + + if ( cpu_callin_map[i] ) + { + printk("Processor %d found.\n", i); + /* this sync's the decr's -- Cort */ + smp_num_cpus++; + } else { + printk("Processor %d is stuck.\n", i); + } + } + + /* Setup CPU 0 last (important) */ + ppc_md.smp_setup_cpu(0); + + if (smp_num_cpus < 2) { + tb_last_stamp = get_tb(); + smp_tb_synchronized = 1; + } +} + +void __init smp_commence(void) +{ + /* + * Lets the callin's below out of their loop. + */ + PPCDBG(PPCDBG_SMP, "smp_commence: start\n"); + wmb(); + smp_commenced = 1; +} + +void __init smp_callin(void) +{ + int cpu = smp_processor_id(); + + smp_store_cpu_info(cpu); + set_dec(xPaca[cpu].default_decr); + cpu_callin_map[cpu] = 1; + set_bit(smp_processor_id(), &cpu_online_map); + + ppc_md.smp_setup_cpu(cpu); + + while(!smp_commenced) { + barrier(); + } + __sti(); +} + +/* intel needs this */ +void __init initialize_secondary(void) +{ +} + +/* Activate a secondary processor. */ +int start_secondary(void *unused) +{ + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + smp_callin(); + + return cpu_idle(NULL); +} + +void __init smp_setup(char *str, int *ints) +{ +} + +int __init setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +/* this function is called for each processor + */ +void __init smp_store_cpu_info(int id) +{ + xPaca[id].pvr = _get_PVR(); +} + +static int __init maxcpus(char *str) +{ + get_option(&str, &max_cpus); + return 1; +} + +__setup("maxcpus=", maxcpus); diff -Nru a/arch/ppc64/kernel/stab.c b/arch/ppc64/kernel/stab.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/stab.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,360 @@ +/* + * PowerPC64 Segment Translation Support. + * + * Dave Engebretsen and Mike Corrigan {engebret|mikejc}@us.ibm.com + * Copyright (c) 2001 Dave Engebretsen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +inline int make_ste(unsigned long stab, + unsigned long esid, unsigned long vsid); +inline void make_slbe(unsigned long esid, unsigned long vsid, + int large); +extern struct Naca *naca; + +/* + * Build an entry for the base kernel segment and put it into + * the segment table or SLB. All other segment table or SLB + * entries are faulted in. + */ +void stab_initialize(unsigned long stab) +{ + unsigned long esid, vsid; + + esid = GET_ESID(KERNELBASE); + vsid = get_kernel_vsid(esid << SID_SHIFT); + + if (!__is_processor(PV_POWER4)) { + __asm__ __volatile__("isync; slbia; isync":::"memory"); + make_ste(stab, esid, vsid); + } else { + /* Invalidate the entire SLB & all the ERATS */ + __asm__ __volatile__("isync" : : : "memory"); +#ifndef CONFIG_PPC_ISERIES + __asm__ __volatile__("slbmte %0,%0" + : : "r" (0) : "memory"); + __asm__ __volatile__("isync; slbia; isync":::"memory"); + make_slbe(esid, vsid, 0); +#else + __asm__ __volatile__("isync; slbia; isync":::"memory"); +#endif + } +} + +/* + * Create a segment table entry for the given esid/vsid pair. + */ +inline int +make_ste(unsigned long stab, unsigned long esid, unsigned long vsid) +{ + unsigned long entry, group, old_esid, castout_entry, i; + unsigned int global_entry; + STE *ste, *castout_ste; + + /* Search the primary group first. */ + global_entry = (esid & 0x1f) << 3; + ste = (STE *)(stab | ((esid & 0x1f) << 7)); + + /* + * Find an empty entry, if one exists. + */ + for(group = 0; group < 2; group++) { + for(entry = 0; entry < 8; entry++, ste++) { + if(!(ste->dw0.dw0.v)) { + ste->dw1.dw1.vsid = vsid; + /* Order VSID updte */ + __asm__ __volatile__ ("eieio" : : : "memory"); + ste->dw0.dw0.esid = esid; + ste->dw0.dw0.v = 1; + ste->dw0.dw0.kp = 1; + /* Order update */ + __asm__ __volatile__ ("sync" : : : "memory"); + + return(global_entry | entry); + } + } + /* Now search the secondary group. */ + global_entry = ((~esid) & 0x1f) << 3; + ste = (STE *)(stab | (((~esid) & 0x1f) << 7)); + } + + /* + * Could not find empty entry, pick one with a round robin selection. + * Search all entries in the two groups. Note that the first time + * we get here, we start with entry 1 so the initializer + * can be common with the SLB castout code. + */ + + /* This assumes we never castout when initializing the stab. */ + PMC_SW_PROCESSOR(stab_capacity_castouts); + + castout_entry = get_paca()->xStab_data.next_round_robin; + for(i = 0; i < 16; i++) { + if(castout_entry < 8) { + global_entry = (esid & 0x1f) << 3; + ste = (STE *)(stab | ((esid & 0x1f) << 7)); + castout_ste = ste + castout_entry; + } else { + global_entry = ((~esid) & 0x1f) << 3; + ste = (STE *)(stab | (((~esid) & 0x1f) << 7)); + castout_ste = ste + (castout_entry - 8); + } + + if((((castout_ste->dw0.dw0.esid) >> 32) == 0) || + (((castout_ste->dw0.dw0.esid) & 0xffffffff) > 0)) { + /* Found an entry to castout. It is either a user */ + /* region, or a secondary kernel segment. */ + break; + } + + castout_entry = (castout_entry + 1) & 0xf; + } + + get_paca()->xStab_data.next_round_robin = (castout_entry + 1) & 0xf; + + /* Modify the old entry to the new value. */ + + /* Force previous translations to complete. DRENG */ + __asm__ __volatile__ ("isync" : : : "memory" ); + + castout_ste->dw0.dw0.v = 0; + __asm__ __volatile__ ("sync" : : : "memory" ); /* Order update */ + castout_ste->dw1.dw1.vsid = vsid; + __asm__ __volatile__ ("eieio" : : : "memory" ); /* Order update */ + old_esid = castout_ste->dw0.dw0.esid; + castout_ste->dw0.dw0.esid = esid; + castout_ste->dw0.dw0.v = 1; + castout_ste->dw0.dw0.kp = 1; + __asm__ __volatile__ ("slbie %0" : : "r" (old_esid << SID_SHIFT)); + /* Ensure completion of slbie */ + __asm__ __volatile__ ("sync" : : : "memory" ); + + return(global_entry | (castout_entry & 0x7)); +} + +/* + * Create a segment buffer entry for the given esid/vsid pair. + */ +inline void make_slbe(unsigned long esid, unsigned long vsid, int large) +{ + unsigned long entry, castout_entry; + slb_dword0 castout_esid_data; + union { + unsigned long word0; + slb_dword0 data; + } esid_data; + union { + unsigned long word0; + slb_dword1 data; + } vsid_data; + + /* + * Find an empty entry, if one exists. + */ + for(entry = 0; entry < naca->slb_size; entry++) { + __asm__ __volatile__("slbmfee %0,%1" + : "=r" (esid_data) : "r" (entry)); + if(!esid_data.data.v) { + /* + * Write the new SLB entry. + */ + vsid_data.word0 = 0; + vsid_data.data.vsid = vsid; + vsid_data.data.kp = 1; + if (large) + vsid_data.data.l = 1; + + esid_data.word0 = 0; + esid_data.data.esid = esid; + esid_data.data.v = 1; + esid_data.data.index = entry; + + /* slbie not needed as no previous mapping existed. */ + /* Order update */ + __asm__ __volatile__ ("isync" : : : "memory"); + __asm__ __volatile__ ("slbmte %0,%1" + : : "r" (vsid_data), + "r" (esid_data)); + /* Order update */ + __asm__ __volatile__ ("isync" : : : "memory"); + return; + } + } + + /* + * Could not find empty entry, pick one with a round robin selection. + */ + + PMC_SW_PROCESSOR(stab_capacity_castouts); + + castout_entry = get_paca()->xStab_data.next_round_robin; + __asm__ __volatile__("slbmfee %0,%1" + : "=r" (castout_esid_data) + : "r" (castout_entry)); + + entry = castout_entry; + castout_entry++; + if(castout_entry >= naca->slb_size) { + castout_entry = 1; + } + get_paca()->xStab_data.next_round_robin = castout_entry; + + /* Invalidate the old entry. */ + castout_esid_data.v = 0; /* Set the class to 0 */ + /* slbie not needed as the previous mapping is still valid. */ + __asm__ __volatile__("slbie %0" : : "r" (castout_esid_data)); + + /* + * Write the new SLB entry. + */ + vsid_data.word0 = 0; + vsid_data.data.vsid = vsid; + vsid_data.data.kp = 1; + if (large) + vsid_data.data.l = 1; + + esid_data.word0 = 0; + esid_data.data.esid = esid; + esid_data.data.v = 1; + esid_data.data.index = entry; + + __asm__ __volatile__ ("isync" : : : "memory"); /* Order update */ + __asm__ __volatile__ ("slbmte %0,%1" + : : "r" (vsid_data), "r" (esid_data)); + __asm__ __volatile__ ("isync" : : : "memory" ); /* Order update */ +} + +/* + * Allocate a segment table entry for the given ea. + */ +int ste_allocate ( unsigned long ea, + unsigned long trap) +{ + unsigned long vsid, esid; + int kernel_segment = 0; + + PMC_SW_PROCESSOR(stab_faults); + + /* Check for invalid effective addresses. */ + if (!IS_VALID_EA(ea)) { + return 1; + } + + /* Kernel or user address? */ + if (REGION_ID(ea) >= KERNEL_REGION_ID) { + kernel_segment = 1; + vsid = get_kernel_vsid( ea ); + } else { + struct mm_struct *mm = current->mm; + if ( mm ) { + vsid = get_vsid(mm->context, ea ); + } else { + return 1; + } + } + + esid = GET_ESID(ea); + if (trap == 0x380 || trap == 0x480) { +#ifndef CONFIG_PPC_ISERIES + if (REGION_ID(ea) == KERNEL_REGION_ID) + make_slbe(esid, vsid, 1); + else +#endif + make_slbe(esid, vsid, 0); + } else { + unsigned char top_entry, stab_entry, *segments; + + stab_entry = make_ste(get_paca()->xStab_data.virt, esid, vsid); + PMC_SW_PROCESSOR_A(stab_entry_use, stab_entry & 0xf); + + segments = get_paca()->xSegments; + top_entry = segments[0]; + if(!kernel_segment && top_entry < (STAB_CACHE_SIZE - 1)) { + top_entry++; + segments[top_entry] = stab_entry; + if(top_entry == STAB_CACHE_SIZE - 1) top_entry = 0xff; + segments[0] = top_entry; + } + } + + return(0); +} + +/* + * Flush all entries from the segment table of the current processor. + * Kernel and Bolted entries are not removed as we cannot tolerate + * faults on those addresses. + */ + +#define STAB_PRESSURE 0 + +void flush_stab(void) +{ + STE *stab = (STE *) get_paca()->xStab_data.virt; + unsigned char *segments = get_paca()->xSegments; + unsigned long flags, i; + + if(!__is_processor(PV_POWER4)) { + unsigned long entry; + STE *ste; + + /* Force previous translations to complete. DRENG */ + __asm__ __volatile__ ("isync" : : : "memory"); + + __save_and_cli(flags); + if(segments[0] != 0xff && !STAB_PRESSURE) { + for(i = 1; i <= segments[0]; i++) { + ste = stab + segments[i]; + ste->dw0.dw0.v = 0; + PMC_SW_PROCESSOR(stab_invalidations); + } + } else { + /* Invalidate all entries. */ + ste = stab; + + /* Never flush the first entry. */ + ste += 1; + for(entry = 1; + entry < (PAGE_SIZE / sizeof(STE)); + entry++, ste++) { + unsigned long ea; + ea = ste->dw0.dw0.esid << SID_SHIFT; + if (STAB_PRESSURE || ea < KERNELBASE) { + ste->dw0.dw0.v = 0; + PMC_SW_PROCESSOR(stab_invalidations); + } + } + } + + *((unsigned long *)segments) = 0; + __restore_flags(flags); + + /* Invalidate the SLB. */ + /* Force invals to complete. */ + __asm__ __volatile__ ("sync" : : : "memory"); + /* Flush the SLB. */ + __asm__ __volatile__ ("slbia" : : : "memory"); + /* Force flush to complete. */ + __asm__ __volatile__ ("sync" : : : "memory"); + } else { + unsigned long flags; + + PMC_SW_PROCESSOR(stab_invalidations); + + __save_and_cli(flags); + __asm__ __volatile__("isync; slbia; isync":::"memory"); + __restore_flags(flags); + } +} diff -Nru a/arch/ppc64/kernel/sys32.S b/arch/ppc64/kernel/sys32.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/sys32.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,243 @@ +/* + * sys32.S: I-cache tricks for 32-bit compatability layer simple + * conversions. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) + * Copyright (C) 2000 Ken Aaker (kdaaker@rchland.vnet.ibm.com) + * For PPC ABI convention is parms in Regs 3-10. + * The router in entry.S clears the high 32 bits in the first + * 4 arguments (R3-R6). + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "ppc_asm.h" +#include +#include + +/* NOTE: call as jump breaks return stack, we have to avoid that */ + + .text + +_GLOBAL(sys32_mmap) + clrldi r7, r7, 32 /* int fd parm */ + clrldi r8, r8, 32 /* off_t offset parm */ + b .sys_mmap + +_GLOBAL(sys32_lseek) + extsw r4,r4 /* sign extend off_t offset parm */ + b .sys_lseek + +_GLOBAL(sys32_chmod) +/* Ken Aaker.. hmmm maybe I don't need to do anything here */ + b .sys_chmod + +_GLOBAL(sys32_mknod) +/* Ken Aaker.. hmmm maybe I don't need to do anything here */ + b .sys_mknod + +_GLOBAL(sys32_sendto) + clrldi r7, r7, 32 /* struct sockaddr *addr parm */ + clrldi r8, r8, 32 /* int addr_len parm */ + b .sys_sendto + +_GLOBAL(sys32_recvfrom) + clrldi r7, r7, 32 /* struct sockaddr *addr parm */ + clrldi r8, r8, 32 /* int *addr_len parm */ + b .sys_recvfrom + +_GLOBAL(sys32_getsockopt) + clrldi r7, r7, 32 /* int *optlen parm */ + b .sys_getsockopt + +_GLOBAL(sys32_bdflush) + extsw r4,r4 /* sign extend long data parm */ + b .sys_bdflush + +_GLOBAL(sys32_mmap2) + clrldi r7, r7, 32 /* unsigned long fd parm */ + extsw r8, r8 /* off_t offset */ + b .sys_mmap + +_GLOBAL(sys32_socketcall) /* r3=call, r4=args */ + cmpwi r3, 1 + blt- .do_einval + cmpwi r3, 17 + bgt- .do_einval + subi r3, r3, 1 /* index into socketcall_table vectors and jmp */ + sldi r3, r3, 3 /* each entry is 8 bytes */ + LOADADDR(r10,.socketcall_table_begin) + ldx r10, r10, r3 + mtctr r10 + bctr + +/* Socket function vectored fix ups for 32 bit */ +_STATIC(do_sys_socket) /* sys_socket(int, int, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + b .sys_socket + +_STATIC(do_sys_bind) /* sys_bind(int fd, struct sockaddr *, int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys_bind + +_STATIC(do_sys_connect) /* sys_connect(int, struct sockaddr *, int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys_connect + +_STATIC(do_sys_listen) /* sys_listen(int, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + b .sys_listen + +_STATIC(do_sys_accept) /* sys_accept(int, struct sockaddr *, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + b .sys_accept + +_STATIC(do_sys_getsockname) /* sys_getsockname(int, struct sockaddr *, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + b .sys_getsockname + +_STATIC(do_sys_getpeername) /* sys_getpeername(int, struct sockaddr *, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + b .sys_getpeername + +_STATIC(do_sys_socketpair) /* sys_socketpair(int, int, int, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + lwz r6,12(r10) + b .sys_socketpair + +_STATIC(do_sys_send) /* sys_send(int, void *, size_t, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + b .sys_send + +_STATIC(do_sys_recv) /* sys_recv(int, void *, size_t, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + b .sys_recv + +_STATIC(do_sys_sendto) /* sys32_sendto(int, u32, __kernel_size_t32, unsigned int, u32, int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + lwz r7,16(r10) + lwa r8,20(r10) + b .sys32_sendto + +_STATIC(do_sys_recvfrom) /* sys32_recvfrom(int, u32, __kernel_size_t32, unsigned int, u32, u32) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + lwz r7,16(r10) + lwz r8,20(r10) + b .sys32_recvfrom + +_STATIC(do_sys_shutdown) /* sys_shutdown(int, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + b .sys_shutdown + +_STATIC(do_sys_setsockopt) /* sys32_setsockopt(int, int, int, char *, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + lwz r6,12(r10) + lwa r7,16(r10) + b .sys32_setsockopt + +_STATIC(do_sys_getsockopt) /* sys32_getsockopt(int, int, int, u32, u32) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + lwz r6,12(r10) + lwz r7,16(r10) + b .sys32_getsockopt + +_STATIC(do_sys_sendmsg) /* sys32_sendmsg(int, struct msghdr32 *, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys32_sendmsg + +_STATIC(do_sys_recvmsg) /* sys32_recvmsg(int, struct msghdr32 *, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys32_recvmsg + +_STATIC(do_einval) + li r3,-EINVAL + b .ret_from_syscall_1 +_STATIC(do_efault) + li r3,-EFAULT + b .ret_from_syscall_1 + + .data + .align 8 +_GLOBAL(socketcall_table_begin) + .llong .do_sys_socket + .llong .do_sys_bind + .llong .do_sys_connect + .llong .do_sys_listen + .llong .do_sys_accept + .llong .do_sys_getsockname + .llong .do_sys_getpeername + .llong .do_sys_socketpair + .llong .do_sys_send + .llong .do_sys_recv + .llong .do_sys_sendto + .llong .do_sys_recvfrom + .llong .do_sys_shutdown + .llong .do_sys_setsockopt + .llong .do_sys_getsockopt + .llong .do_sys_sendmsg + .llong .do_sys_recvmsg +_GLOBAL(socketcall_table_end) + .section __ex_table,"a" + .align 3 + .llong .socketcall_table_begin + .llong 0 + .llong .socketcall_table_end + .llong .do_efault + .previous diff -Nru a/arch/ppc64/kernel/sys_ppc32.c b/arch/ppc64/kernel/sys_ppc32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/sys_ppc32.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,4779 @@ +/* + * sys_ppc32.c: Conversion between 32bit and 64bit native syscalls. + * + * Copyright (C) 2001 IBM + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * + * These routines maintain argument size conversion between 32bit and 64bit + * environment. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include + +#include + +#include +#include +#include +#include +#include + +extern unsigned long wall_jiffies; +#define USEC_PER_SEC (1000000) + +/* + * These are the flags in the MSR that the user is allowed to change + * by modifying the saved value of the MSR on the stack. SE and BE + * should not be in this list since gdb may want to change these. I.e, + * you should be able to step out of a signal handler to see what + * instruction executes next after the signal handler completes. + * Alternately, if you stepped into a signal handler, you should be + * able to continue 'til the next breakpoint from within the signal + * handler, even if the handler returns. + */ +#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) + +/* In order to reduce some races, while at the same time doing additional + * checking and hopefully speeding things up, we copy filenames to the + * kernel data space before using them.. + * + * POSIX.1 2.4: an empty pathname is invalid (ENOENT). + */ +static inline int do_getname32(const char *filename, char *page) +{ + int retval; + + /* 32bit pointer will be always far below TASK_SIZE :)) */ + retval = strncpy_from_user((char *)page, (char *)filename, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) + return 0; + return -ENAMETOOLONG; + } else if (!retval) + retval = -ENOENT; + return retval; +} + +char * getname32(const char *filename) +{ + char *tmp, *result; + + result = ERR_PTR(-ENOMEM); + tmp = __getname(); + if (tmp) { + int retval = do_getname32(filename, tmp); + + result = tmp; + if (retval < 0) { + putname(tmp); + result = ERR_PTR(retval); + } + } + return result; +} + + + +extern asmlinkage long sys_utime(char * filename, struct utimbuf * times); + +struct utimbuf32 { + __kernel_time_t32 actime, modtime; +}; + +asmlinkage long sys32_utime(char * filename, struct utimbuf32 *times) +{ + struct utimbuf t; + mm_segment_t old_fs; + int ret; + char *filenam; + + PPCDBG(PPCDBG_SYS32NI, "sys32_utime - running - filename=%s, times=%p - pid=%ld, comm=%s \n", filename, times, current->pid, current->comm); + + if (!times) + return sys_utime(filename, NULL); + if (get_user(t.actime, ×->actime) || __get_user(t.modtime, ×->modtime)) + return -EFAULT; + filenam = getname32(filename); + + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { + old_fs = get_fs(); + set_fs (KERNEL_DS); + ret = sys_utime(filenam, &t); + set_fs (old_fs); + putname (filenam); + } + + return ret; +} + + + +struct iovec32 { u32 iov_base; __kernel_size_t32 iov_len; }; + +typedef ssize_t (*IO_fn_t)(struct file *, char *, size_t, loff_t *); + +static long do_readv_writev32(int type, struct file *file, + const struct iovec32 *vector, u32 count) +{ + unsigned long tot_len; + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov=iovstack, *ivp; + struct inode *inode; + long retval, i; + IO_fn_t fn; + + /* First get the "struct iovec" from user memory and + * verify all the pointers + */ + if (!count) + return 0; + if(verify_area(VERIFY_READ, vector, sizeof(struct iovec32)*count)) + return -EFAULT; + if (count > UIO_MAXIOV) + return -EINVAL; + if (count > UIO_FASTIOV) { + iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); + if (!iov) + return -ENOMEM; + } + + tot_len = 0; + i = count; + ivp = iov; + while(i > 0) { + u32 len; + u32 buf; + + __get_user(len, &vector->iov_len); + __get_user(buf, &vector->iov_base); + tot_len += len; + ivp->iov_base = (void *)A(buf); + ivp->iov_len = (__kernel_size_t) len; + vector++; + ivp++; + i--; + } + + inode = file->f_dentry->d_inode; + /* VERIFY_WRITE actually means a read, as we write to user space */ + retval = locks_verify_area((type == VERIFY_WRITE + ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), + inode, file, file->f_pos, tot_len); + if (retval) { + if (iov != iovstack) + kfree(iov); + return retval; + } + + /* Then do the actual IO. Note that sockets need to be handled + * specially as they have atomicity guarantees and can handle + * iovec's natively + */ + if (inode->i_sock) { + int err; + err = sock_readv_writev(type, inode, file, iov, count, tot_len); + if (iov != iovstack) + kfree(iov); + return err; + } + + if (!file->f_op) { + if (iov != iovstack) + kfree(iov); + return -EINVAL; + } + /* VERIFY_WRITE actually means a read, as we write to user space */ + fn = file->f_op->read; + if (type == VERIFY_READ) + fn = (IO_fn_t) file->f_op->write; + ivp = iov; + while (count > 0) { + void * base; + int len, nr; + + base = ivp->iov_base; + len = ivp->iov_len; + ivp++; + count--; + nr = fn(file, base, len, &file->f_pos); + if (nr < 0) { + if (retval) + break; + retval = nr; + break; + } + retval += nr; + if (nr != len) + break; + } + if (iov != iovstack) + kfree(iov); + return retval; +} + +asmlinkage long sys32_readv(u32 fd, struct iovec32 *vector, u32 count) +{ + struct file *file; + long ret = -EBADF; + + PPCDBG(PPCDBG_SYS32, "sys32_readv - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + file = fget(fd); + if(!file) + goto bad_file; + + if (file->f_op && (file->f_mode & FMODE_READ) && + (file->f_op->readv || file->f_op->read)) + ret = do_readv_writev32(VERIFY_WRITE, file, vector, count); + fput(file); + +bad_file: + PPCDBG(PPCDBG_SYS32, "sys32_readv - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + +asmlinkage long sys32_writev(u32 fd, struct iovec32 *vector, u32 count) +{ + struct file *file; + int ret = -EBADF; + + PPCDBG(PPCDBG_SYS32, "sys32_writev - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + file = fget(fd); + if(!file) + goto bad_file; + if (file->f_op && (file->f_mode & FMODE_WRITE) && + (file->f_op->writev || file->f_op->write)) + ret = do_readv_writev32(VERIFY_READ, file, vector, count); + fput(file); + +bad_file: + PPCDBG(PPCDBG_SYS32, "sys32_writev - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + + + +static inline int get_flock(struct flock *kfl, struct flock32 *ufl) +{ + int err; + + err = get_user(kfl->l_type, &ufl->l_type); + err |= __get_user(kfl->l_whence, &ufl->l_whence); + err |= __get_user(kfl->l_start, &ufl->l_start); + err |= __get_user(kfl->l_len, &ufl->l_len); + err |= __get_user(kfl->l_pid, &ufl->l_pid); + return err; +} + +static inline int put_flock(struct flock *kfl, struct flock32 *ufl) +{ + int err; + + err = __put_user(kfl->l_type, &ufl->l_type); + err |= __put_user(kfl->l_whence, &ufl->l_whence); + err |= __put_user(kfl->l_start, &ufl->l_start); + err |= __put_user(kfl->l_len, &ufl->l_len); + err |= __put_user(kfl->l_pid, &ufl->l_pid); + return err; +} + +extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg); +asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case F_GETLK: + case F_SETLK: + case F_SETLKW: + { + struct flock f; + mm_segment_t old_fs; + long ret; + + if(get_flock(&f, (struct flock32 *)arg)) + return -EFAULT; + old_fs = get_fs(); set_fs (KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs (old_fs); + if(put_flock(&f, (struct flock32 *)arg)) + return -EFAULT; + return ret; + } + default: + return sys_fcntl(fd, cmd, (unsigned long)arg); + } +} + +struct ncp_mount_data32 { + int version; + unsigned int ncp_fd; + __kernel_uid_t32 mounted_uid; + __kernel_pid_t32 wdog_pid; + unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; + unsigned int time_out; + unsigned int retry_count; + unsigned int flags; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; + +static void *do_ncp_super_data_conv(void *raw_data) +{ + struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data; + struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data; + + n->dir_mode = n32->dir_mode; + n->file_mode = n32->file_mode; + n->gid = n32->gid; + n->uid = n32->uid; + memmove (n->mounted_vol, n32->mounted_vol, (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int))); + n->wdog_pid = n32->wdog_pid; + n->mounted_uid = n32->mounted_uid; + return raw_data; +} + +struct smb_mount_data32 { + int version; + __kernel_uid_t32 mounted_uid; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; + +static void *do_smb_super_data_conv(void *raw_data) +{ + struct smb_mount_data *s = (struct smb_mount_data *)raw_data; + struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data; + + s->version = s32->version; + s->mounted_uid = s32->mounted_uid; + s->uid = s32->uid; + s->gid = s32->gid; + s->file_mode = s32->file_mode; + s->dir_mode = s32->dir_mode; + return raw_data; +} + +static int copy_mount_stuff_to_kernel(const void *user, unsigned long *kernel) +{ + int i; + unsigned long page; + struct vm_area_struct *vma; + + *kernel = 0; + if(!user) + return 0; + vma = find_vma(current->mm, (unsigned long)user); + if(!vma || (unsigned long)user < vma->vm_start) + return -EFAULT; + if(!(vma->vm_flags & VM_READ)) + return -EFAULT; + i = vma->vm_end - (unsigned long) user; + if(PAGE_SIZE <= (unsigned long) i) + i = PAGE_SIZE - 1; + if(!(page = __get_free_page(GFP_KERNEL))) + return -ENOMEM; + if(copy_from_user((void *) page, user, i)) { + free_page(page); + return -EFAULT; + } + *kernel = page; + return 0; +} + +#define SMBFS_NAME "smbfs" +#define NCPFS_NAME "ncpfs" + +asmlinkage long sys32_mount(char *dev_name, char *dir_name, char *type, unsigned long new_flags, u32 data) +{ + unsigned long type_page = 0; + unsigned long data_page = 0; + unsigned long dev_page = 0; + unsigned long dir_page = 0; + int err, is_smb, is_ncp; + + PPCDBG(PPCDBG_SYS32, "sys32_mount - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + is_smb = is_ncp = 0; + + err = copy_mount_stuff_to_kernel((const void *)type, &type_page); + if (err) + goto out; + + if (!type_page) { + err = -EINVAL; + goto out; + } + + is_smb = !strcmp((char *)type_page, SMBFS_NAME); + is_ncp = !strcmp((char *)type_page, NCPFS_NAME); + + err = copy_mount_stuff_to_kernel((const void *)AA(data), &data_page); + if (err) + goto type_out; + + err = copy_mount_stuff_to_kernel(dev_name, &dev_page); + if (err) + goto data_out; + + err = copy_mount_stuff_to_kernel(dir_name, &dir_page); + if (err) + goto dev_out; + + if (!is_smb && !is_ncp) { + lock_kernel(); + err = do_mount((char*)dev_page, (char*)dir_page, + (char*)type_page, new_flags, (char*)data_page); + unlock_kernel(); + } else { + if (is_ncp) + do_ncp_super_data_conv((void *)data_page); + else + do_smb_super_data_conv((void *)data_page); + + lock_kernel(); + err = do_mount((char*)dev_page, (char*)dir_page, + (char*)type_page, new_flags, (char*)data_page); + unlock_kernel(); + } + free_page(dir_page); + +dev_out: + free_page(dev_page); + +data_out: + free_page(data_page); + +type_out: + free_page(type_page); + +out: + + PPCDBG(PPCDBG_SYS32, "sys32_mount - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return err; +} + +struct dqblk32 { + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u32 dqb_curblocks; + __u32 dqb_ihardlimit; + __u32 dqb_isoftlimit; + __u32 dqb_curinodes; + __kernel_time_t32 dqb_btime; + __kernel_time_t32 dqb_itime; +}; + + +extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr); + +/* Note: it is necessary to treat cmd and id as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_quotactl(u32 cmd_parm, const char *special, u32 id_parm, unsigned long addr) +{ + int cmd = (int)cmd_parm; + int id = (int)id_parm; + int cmds = cmd >> SUBCMDSHIFT; + int err; + struct dqblk d; + mm_segment_t old_fs; + char *spec; + + PPCDBG(PPCDBG_SYS32, "sys32_quotactl - entered - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + switch (cmds) { + case Q_GETQUOTA: + break; + case Q_SETQUOTA: + case Q_SETUSE: + case Q_SETQLIM: + if (copy_from_user (&d, (struct dqblk32 *)addr, + sizeof (struct dqblk32))) + return -EFAULT; + d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + break; + default: + return sys_quotactl(cmd, special, + id, (caddr_t)addr); + } + spec = getname32 (special); + err = PTR_ERR(spec); + if (IS_ERR(spec)) return err; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + set_fs (old_fs); + putname (spec); + if (cmds == Q_GETQUOTA) { + __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; + ((struct dqblk32 *)&d)->dqb_itime = i; + ((struct dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct dqblk32 *)addr, &d, + sizeof (struct dqblk32))) + return -EFAULT; + } + + PPCDBG(PPCDBG_SYS32, "sys32_quotactl - exited - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + return err; +} + + + +/* readdir & getdents */ +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+sizeof(u32)-1) & ~(sizeof(u32)-1)) + +struct old_linux_dirent32 { + u32 d_ino; + u32 d_offset; + unsigned short d_namlen; + /* unsigned char d_type; */ + char d_name[1]; +}; + +struct readdir_callback32 { + struct old_linux_dirent32 * dirent; + int count; +}; + +static int fillonedir(void * __buf, const char * name, int namlen, + off_t offset, ino_t ino, unsigned int d_type) +{ + struct readdir_callback32 * buf = (struct readdir_callback32 *) __buf; + struct old_linux_dirent32 * dirent; + + if (buf->count) + return -EINVAL; + buf->count++; + dirent = buf->dirent; + put_user(ino, &dirent->d_ino); + put_user(offset, &dirent->d_offset); + put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + return 0; +} + +asmlinkage int old32_readdir(unsigned int fd, struct old_linux_dirent32 *dirent, unsigned int count) +{ + int error = -EBADF; + struct file * file; + struct readdir_callback32 buf; + + file = fget(fd); + if (!file) + goto out; + + buf.count = 0; + buf.dirent = dirent; + + error = vfs_readdir(file, (filldir_t)fillonedir, &buf); + if (error < 0) + goto out_putf; + error = buf.count; + +out_putf: + fput(file); +out: + return error; +} + +#if 0 +struct linux_dirent32 { + u32 d_ino; + u32 d_off; + unsigned short d_reclen; + char d_name[1]; +}; +#else +struct linux_dirent32 { + u32 d_ino; + u32 d_off; + unsigned short d_reclen; + /* unsigned char d_type; */ + char d_name[256]; +}; +#endif + +struct getdents_callback32 { + struct linux_dirent32 * current_dir; + struct linux_dirent32 * previous; + int count; + int error; +}; + +static int +filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino, + unsigned int d_type) +{ + struct linux_dirent32 * dirent; + struct getdents_callback32 * buf = (struct getdents_callback32 *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->current_dir; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(reclen, &dirent->d_reclen); + /* put_user(d_type, &dirent->d_type); */ + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage long sys32_getdents(unsigned int fd, struct linux_dirent32 *dirent, unsigned int count) +{ + struct file * file; + struct linux_dirent32 * lastdirent; + struct getdents_callback32 buf; + int error = -EBADF; + + PPCDBG(PPCDBG_SYS32NI, "sys32_getdents - running - fd=%x, pid=%ld, comm=%s \n", fd, current->pid, current->comm); + + file = fget(fd); + if (!file) + goto out; + + buf.current_dir = dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + error = vfs_readdir(file, (filldir_t)filldir, &buf); + if (error < 0) + goto out_putf; + lastdirent = buf.previous; + error = buf.error; + if(lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = count - buf.count; + } + out_putf: + fput(file); + + out: + return error; +} +/* end of readdir & getdents */ + + + +/* 32-bit timeval and related flotsam. */ + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +struct itimerval32 +{ + struct timeval32 it_interval; + struct timeval32 it_value; +}; + + + + +/* + * Ooo, nasty. We need here to frob 32-bit unsigned longs to + * 64-bit unsigned longs. + */ +static inline int +get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset) +{ + if (ufdset) { + unsigned long odd; + + if (verify_area(VERIFY_WRITE, ufdset, n*sizeof(u32))) + return -EFAULT; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + __get_user(l, ufdset); + __get_user(h, ufdset+1); + ufdset += 2; + *fdset++ = h << 32 | l; + n -= 2; + } + if (odd) + __get_user(*fdset, ufdset); + } else { + /* Tricky, must clear full unsigned long in the + * kernel fdset at the end, this makes sure that + * actually happens. + */ + memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32)); + } + return 0; +} + +static inline void +set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset) +{ + unsigned long odd; + + if (!ufdset) + return; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + l = *fdset++; + h = l >> 32; + __put_user(l, ufdset); + __put_user(h, ufdset+1); + ufdset += 2; + n -= 2; + } + if (odd) + __put_user(*fdset, ufdset); +} + + + +#define MAX_SELECT_SECONDS ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) + +asmlinkage long sys32_select(int n, u32 *inp, u32 *outp, u32 *exp, u32 tvp_x) +{ + fd_set_bits fds; + struct timeval32 *tvp = (struct timeval32 *)AA(tvp_x); + char *bits; + unsigned long nn; + long timeout; + int ret, size; + + PPCDBG(PPCDBG_SYS32X, "sys32_select - entered - n=%x, inp=%p, outp=%p - pid=%ld comm=%s \n", n, inp, outp, current->pid, current->comm); + + timeout = MAX_SCHEDULE_TIMEOUT; + if (tvp) { + time_t sec, usec; + if ((ret = verify_area(VERIFY_READ, tvp, sizeof(*tvp))) + || (ret = __get_user(sec, &tvp->tv_sec)) + || (ret = __get_user(usec, &tvp->tv_usec))) + goto out_nofds; + + ret = -EINVAL; + if(sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); + timeout += sec * (unsigned long) HZ; + } + } + + ret = -EINVAL; + if (n < 0) + goto out_nofds; + if (n > current->files->max_fdset) + n = current->files->max_fdset; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ + ret = -ENOMEM; + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) + goto out_nofds; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); + + nn = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32)); + if ((ret = get_fd_set32(nn, fds.in, inp)) || + (ret = get_fd_set32(nn, fds.out, outp)) || + (ret = get_fd_set32(nn, fds.ex, exp))) + goto out; + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); + + ret = do_select(n, &fds, &timeout); + + if (tvp && !(current->personality & STICKY_TIMEOUTS)) { + time_t sec = 0, usec = 0; + if (timeout) { + sec = timeout / HZ; + usec = timeout % HZ; + usec *= (1000000/HZ); + } + put_user(sec, &tvp->tv_sec); + put_user(usec, &tvp->tv_usec); + } + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) + goto out; + ret = 0; + } + + set_fd_set32(nn, inp, fds.res_in); + set_fd_set32(nn, outp, fds.res_out); + set_fd_set32(nn, exp, fds.res_ex); + +out: + kfree(bits); + +out_nofds: + PPCDBG(PPCDBG_SYS32X, "sys32_select - exited - pid=%ld, comm=%s \n", current->pid, current->comm); + return ret; +} + + + + +/* + * Due to some executables calling the wrong select we sometimes + * get wrong args. This determines how the args are being passed + * (a single ptr to them all args passed) then calls + * sys_select() with the appropriate args. -- Cort + */ +/* Note: it is necessary to treat n as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int ppc32_select(u32 n, u32* inp, u32* outp, u32* exp, u32 tvp_x) +{ + if ((unsigned int)n >= 4096) + panic("ppc32_select - wrong arguments were passed in \n"); + + return sys32_select((int)n, inp, outp, exp, tvp_x); +} + + + +static int cp_new_stat32(struct inode *inode, struct stat32 *statbuf) +{ + unsigned long ino, blksize, blocks; + kdev_t dev, rdev; + umode_t mode; + nlink_t nlink; + uid_t uid; + gid_t gid; + off_t size; + time_t atime, mtime, ctime; + int err; + + /* Stream the loads of inode data into the load buffer, + * then we push it all into the store buffer below. This + * should give optimal cache performance. + */ + ino = inode->i_ino; + dev = inode->i_dev; + mode = inode->i_mode; + nlink = inode->i_nlink; + uid = inode->i_uid; + gid = inode->i_gid; + rdev = inode->i_rdev; + size = inode->i_size; + atime = inode->i_atime; + mtime = inode->i_mtime; + ctime = inode->i_ctime; + blksize = inode->i_blksize; + blocks = inode->i_blocks; + + err = put_user(kdev_t_to_nr(dev), &statbuf->st_dev); + err |= put_user(ino, &statbuf->st_ino); + err |= put_user(mode, &statbuf->st_mode); + err |= put_user(nlink, &statbuf->st_nlink); + err |= put_user(uid, &statbuf->st_uid); + err |= put_user(gid, &statbuf->st_gid); + err |= put_user(kdev_t_to_nr(rdev), &statbuf->st_rdev); + err |= put_user(size, &statbuf->st_size); + err |= put_user(atime, &statbuf->st_atime); + err |= put_user(0, &statbuf->__unused1); + err |= put_user(mtime, &statbuf->st_mtime); + err |= put_user(0, &statbuf->__unused2); + err |= put_user(ctime, &statbuf->st_ctime); + err |= put_user(0, &statbuf->__unused3); + if (blksize) { + err |= put_user(blksize, &statbuf->st_blksize); + err |= put_user(blocks, &statbuf->st_blocks); + } else { + unsigned int tmp_blocks; + +#define D_B 7 +#define I_B (BLOCK_SIZE / sizeof(unsigned short)) + tmp_blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE; + if (tmp_blocks > D_B) { + unsigned int indirect; + + indirect = (tmp_blocks - D_B + I_B - 1) / I_B; + tmp_blocks += indirect; + if (indirect > 1) { + indirect = (indirect - 1 + I_B - 1) / I_B; + tmp_blocks += indirect; + if (indirect > 1) + tmp_blocks++; + } + } + err |= put_user(BLOCK_SIZE, &statbuf->st_blksize); + err |= put_user((BLOCK_SIZE / 512) * tmp_blocks, &statbuf->st_blocks); +#undef D_B +#undef I_B + } + err |= put_user(0, &statbuf->__unused4[0]); + err |= put_user(0, &statbuf->__unused4[1]); + + return err; +} + +static __inline__ int +do_revalidate(struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + if (inode->i_op && inode->i_op->revalidate) + return inode->i_op->revalidate(dentry); + return 0; +} + +asmlinkage long sys32_newstat(char* filename, struct stat32* statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_newstat - running - filename=%s, statbuf=%p, pid=%ld, comm=%s\n", filename, statbuf, current->pid, current->comm); + + error = user_path_walk(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_new_stat32(nd.dentry->d_inode, statbuf); + path_release(&nd); + } + return error; +} + +asmlinkage long sys32_newlstat(char * filename, struct stat32 *statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_newlstat - running - fn=%s, pid=%ld, comm=%s\n", filename, current->pid, current->comm); + + error = user_path_walk_link(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_new_stat32(nd.dentry->d_inode, statbuf); + + path_release(&nd); + } + return error; +} + +asmlinkage long sys32_newfstat(unsigned int fd, struct stat32 *statbuf) +{ + struct file *f; + int err = -EBADF; + + PPCDBG(PPCDBG_SYS32X, "sys32_newfstat - running - fd=%x, pid=%ld, comm=%s\n", fd, current->pid, current->comm); + + f = fget(fd); + if (f) { + struct dentry * dentry = f->f_dentry; + + err = do_revalidate(dentry); + if (!err) + err = cp_new_stat32(dentry->d_inode, statbuf); + fput(f); + } + return err; +} + +static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) +{ + int err; + + err = put_user (kbuf->f_type, &ubuf->f_type); + err |= __put_user (kbuf->f_bsize, &ubuf->f_bsize); + err |= __put_user (kbuf->f_blocks, &ubuf->f_blocks); + err |= __put_user (kbuf->f_bfree, &ubuf->f_bfree); + err |= __put_user (kbuf->f_bavail, &ubuf->f_bavail); + err |= __put_user (kbuf->f_files, &ubuf->f_files); + err |= __put_user (kbuf->f_ffree, &ubuf->f_ffree); + err |= __put_user (kbuf->f_namelen, &ubuf->f_namelen); + err |= __put_user (kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]); + err |= __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]); + return err; +} + +extern asmlinkage int sys_statfs(const char * path, struct statfs * buf); + +asmlinkage long sys32_statfs(const char * path, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + char *pth; + + PPCDBG(PPCDBG_SYS32X, "sys32_statfs - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + pth = getname32 (path); + ret = PTR_ERR(pth); + if (!IS_ERR(pth)) { + set_fs (KERNEL_DS); + ret = sys_statfs((const char *)pth, &s); + set_fs (old_fs); + putname (pth); + if (put_statfs(buf, &s)) + return -EFAULT; + } + + PPCDBG(PPCDBG_SYS32X, "sys32_statfs - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return ret; +} + +extern asmlinkage long sys_fstatfs(unsigned int fd, struct statfs * buf); + +asmlinkage long sys32_fstatfs(unsigned int fd, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32X, "sys32_fstatfs - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_fstatfs(fd, &s); + set_fs (old_fs); + if (put_statfs(buf, &s)) + return -EFAULT; + + PPCDBG(PPCDBG_SYS32X, "sys32_fstatfs - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return ret; +} + + + +extern asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2); + +/* Note: it is necessary to treat option as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sysfs(u32 option, u32 arg1, u32 arg2) +{ + PPCDBG(PPCDBG_SYS32, "sys32_sysfs - running - pid=%ld, comm=%s\n", current->pid, current->comm); + return sys_sysfs((int)option, arg1, arg2); +} + + + + +extern unsigned long do_mremap(unsigned long addr, + unsigned long old_len, unsigned long new_len, + unsigned long flags, unsigned long new_addr); + +asmlinkage unsigned long sys32_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, + unsigned long flags, u32 __new_addr) +{ + unsigned long ret = -EINVAL; + unsigned long new_addr = AA(__new_addr); + + PPCDBG(PPCDBG_SYS32, "sys32_mremap - entered - pid=%ld current=%lx comm=%s\n", + current->pid, current, current->comm); + + + if (old_len > 0xf0000000UL || new_len > 0xf0000000UL) + goto out; + if (addr > 0xf0000000UL - old_len) + goto out; + down_write(¤t->mm->mmap_sem); + if (flags & MREMAP_FIXED) { + if (new_addr > 0xf0000000UL - new_len) + goto out_sem; + } else if (addr > 0xf0000000UL - new_len) { + ret = -ENOMEM; + if (!(flags & MREMAP_MAYMOVE)) + goto out_sem; + new_addr = get_unmapped_area (NULL, addr, new_len, 0, 0); + if (!new_addr) + goto out_sem; + flags |= MREMAP_FIXED; + } + ret = do_mremap(addr, old_len, new_len, flags, new_addr); +out_sem: + up_write(¤t->mm->mmap_sem); +out: + + PPCDBG(PPCDBG_SYS32, "sys32_mremap - exited - pid=%ld current=%lx comm=%s\n", + current->pid, current, current->comm); + + return ret; +} + + + +/* Handle adjtimex compatability. */ +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct timeval32 time; + s32 tick; + s32 ppsfreq, jitter, shift, stabil; + s32 jitcnt, calcnt, errcnt, stbcnt; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; +}; + +extern int do_adjtimex(struct timex *); +extern void ppc_adjtimex(void); + +asmlinkage long sys32_adjtimex(struct timex32 *utp) +{ + struct timex txc; + int ret; + + PPCDBG(PPCDBG_SYS32, "sys32_adjtimex - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + memset(&txc, 0, sizeof(struct timex)); + + if(get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + /* adjust the conversion of TB to time of day to track adjtimex */ + ppc_adjtimex(); + + if(put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + + return ret; +} + + + +#ifdef CONFIG_MODULES + +extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size); + +asmlinkage unsigned long sys32_create_module(const char *name_user, __kernel_size_t32 size) +{ + + PPCDBG(PPCDBG_SYS32M, "sys32_create_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_create_module(name_user, (size_t)size); +} + + + +extern asmlinkage long sys_init_module(const char *name_user, struct module *mod_user); + +asmlinkage long sys32_init_module(const char *name_user, struct module *mod_user) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_init_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_init_module(name_user, mod_user); +} + + + +extern asmlinkage long sys_delete_module(const char *name_user); + +asmlinkage long sys32_delete_module(const char *name_user) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_delete_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_delete_module(name_user); +} + + + +struct module_info32 { + u32 addr; + u32 size; + u32 flags; + s32 usecount; +}; + +/* Query various bits about modules. */ + +static inline long +get_mod_name(const char *user_name, char **buf) +{ + unsigned long page; + long retval; + + if ((unsigned long)user_name >= TASK_SIZE + && !segment_eq(get_fs (), KERNEL_DS)) + return -EFAULT; + + page = __get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) { + *buf = (char *)page; + return retval; + } + retval = -ENAMETOOLONG; + } else if (!retval) + retval = -EINVAL; + + free_page(page); + return retval; +} + +static inline void +put_mod_name(char *buf) +{ + free_page((unsigned long)buf); +} + +static __inline__ struct module *find_module(const char *name) +{ + struct module *mod; + + for (mod = module_list; mod ; mod = mod->next) { + if (mod->flags & MOD_DELETED) + continue; + if (!strcmp(mod->name, name)) + break; + } + + return mod; +} + +static int +qm_modules(char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + struct module *mod; + size_t nmod, space, len; + + nmod = space = 0; + + for (mod = module_list; mod->next != NULL; mod = mod->next, ++nmod) { + len = strlen(mod->name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, mod->name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(nmod, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while ((mod = mod->next)->next != NULL) + space += strlen(mod->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static int +qm_deps(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t i, space, len; + + if (mod->next == NULL) + return -EINVAL; + if (!MOD_CAN_QUERY(mod)) + return put_user(0, ret); + + space = 0; + for (i = 0; i < mod->ndeps; ++i) { + const char *dep_name = mod->deps[i].dep->name; + + len = strlen(dep_name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, dep_name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + return put_user(i, ret); + +calc_space_needed: + space += len; + while (++i < mod->ndeps) + space += strlen(mod->deps[i].dep->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static int +qm_refs(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t nrefs, space, len; + struct module_ref *ref; + + if (mod->next == NULL) + return -EINVAL; + if (!MOD_CAN_QUERY(mod)) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = 0; + for (nrefs = 0, ref = mod->refs; ref ; ++nrefs, ref = ref->next_ref) { + const char *ref_name = ref->ref->name; + + len = strlen(ref_name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, ref_name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(nrefs, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while ((ref = ref->next_ref) != NULL) + space += strlen(ref->ref->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static inline int +qm_symbols(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t i, space, len; + struct module_symbol *s; + char *strings; + unsigned *vals; + + if (!MOD_CAN_QUERY(mod)) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = mod->nsyms * 2*sizeof(u32); + + i = len = 0; + s = mod->syms; + + if (space > bufsize) + goto calc_space_needed; + + if (!access_ok(VERIFY_WRITE, buf, space)) + return -EFAULT; + + bufsize -= space; + vals = (unsigned *)buf; + strings = buf+space; + + for (; i < mod->nsyms ; ++i, ++s, vals += 2) { + len = strlen(s->name)+1; + if (len > bufsize) + goto calc_space_needed; + + if (copy_to_user(strings, s->name, len) + || __put_user(s->value, vals+0) + || __put_user(space, vals+1)) + return -EFAULT; + + strings += len; + bufsize -= len; + space += len; + } + + if (put_user(i, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + for (; i < mod->nsyms; ++i, ++s) + space += strlen(s->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static inline int +qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + int error = 0; + + if (mod->next == NULL) + return -EINVAL; + + if (sizeof(struct module_info32) <= bufsize) { + struct module_info32 info; + info.addr = (unsigned long)mod; + info.size = mod->size; + info.flags = mod->flags; + info.usecount = + ((mod_member_present(mod, can_unload) + && mod->can_unload) + ? -1 : atomic_read(&mod->uc.usecount)); + + if (copy_to_user(buf, &info, sizeof(struct module_info32))) + return -EFAULT; + } else + error = -ENOSPC; + + if (put_user(sizeof(struct module_info32), ret)) + return -EFAULT; + + return error; +} + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_query_module(char *name_user, u32 which, char *buf, __kernel_size_t32 bufsize, u32 ret) +{ + struct module *mod; + int err; + + PPCDBG(PPCDBG_SYS32M, "sys32_query_module - entered - pid=%ld current=%lx comm=%s\n", + current->pid, current, current->comm); + + lock_kernel(); + if (name_user == 0) { + /* This finds "kernel_module" which is not exported. */ + for(mod = module_list; mod->next != NULL; mod = mod->next) + ; + } else { + long namelen; + char *name; + + if ((namelen = get_mod_name(name_user, &name)) < 0) { + err = namelen; + goto out; + } + err = -ENOENT; + if (namelen == 0) { + /* This finds "kernel_module" which is not exported. */ + for(mod = module_list; mod->next != NULL; mod = mod->next) + ; + } else if ((mod = find_module(name)) == NULL) { + put_mod_name(name); + goto out; + } + put_mod_name(name); + } + + switch ((int)which) + { + case 0: + err = 0; + break; + case QM_MODULES: + err = qm_modules(buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_DEPS: + err = qm_deps(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_REFS: + err = qm_refs(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_SYMBOLS: + err = qm_symbols(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_INFO: + err = qm_info(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + default: + err = -EINVAL; + break; + } +out: + unlock_kernel(); + + PPCDBG(PPCDBG_SYS32, "sys32_query_module - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return err; +} + + + +struct kernel_sym32 { + u32 value; + char name[60]; +}; + +extern asmlinkage long sys_get_kernel_syms(struct kernel_sym *table); + +asmlinkage long sys32_get_kernel_syms(struct kernel_sym32 *table) +{ + int len, i; + struct kernel_sym *tbl; + mm_segment_t old_fs; + + PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + + len = sys_get_kernel_syms(NULL); + if (!table) return len; + tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL); + if (!tbl) return -ENOMEM; + old_fs = get_fs(); + set_fs (KERNEL_DS); + sys_get_kernel_syms(tbl); + set_fs (old_fs); + for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) { + if (put_user (tbl[i].value, &table->value) || + copy_to_user (table->name, tbl[i].name, 60)) + break; + } + kfree (tbl); + + PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return i; +} + +#else /* CONFIG_MODULES */ + +asmlinkage unsigned long sys32_create_module(const char *name_user, size_t size) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_create_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +asmlinkage long sys32_init_module(const char *name_user, struct module *mod_user) +{ + PPCDBG(PPCDBG_SYS32, "sys32_init_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +asmlinkage long sys32_delete_module(const char *name_user) +{ + PPCDBG(PPCDBG_SYS32, "sys32_delete_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_query_module(const char *name_user, u32 which, char *buf, size_t bufsize, size_t *ret) +{ + PPCDBG(PPCDBG_SYS32, "sys32_query_module - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + /* Let the program know about the new interface. Not that it'll do them much good. */ + if ((int)which == 0) + return 0; + + PPCDBG(PPCDBG_SYS32, "sys32_query_module - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return -ENOSYS; +} + +asmlinkage long sys32_get_kernel_syms(struct kernel_sym *table) +{ + PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +#endif /* CONFIG_MODULES */ + + + +/* Stuff for NFS server syscalls... */ +struct nfsctl_svc32 { + u16 svc32_port; + s32 svc32_nthreads; +}; + +struct nfsctl_client32 { + s8 cl32_ident[NFSCLNT_IDMAX+1]; + s32 cl32_naddr; + struct in_addr cl32_addrlist[NFSCLNT_ADDRMAX]; + s32 cl32_fhkeytype; + s32 cl32_fhkeylen; + u8 cl32_fhkey[NFSCLNT_KEYMAX]; +}; + +struct nfsctl_export32 { + s8 ex32_client[NFSCLNT_IDMAX+1]; + s8 ex32_path[NFS_MAXPATHLEN+1]; + __kernel_dev_t32 ex32_dev; + __kernel_ino_t32 ex32_ino; + s32 ex32_flags; + __kernel_uid_t32 ex32_anon_uid; + __kernel_gid_t32 ex32_anon_gid; +}; + +struct nfsctl_uidmap32 { + u32 ug32_ident; /* char * */ + __kernel_uid_t32 ug32_uidbase; + s32 ug32_uidlen; + u32 ug32_udimap; /* uid_t * */ + __kernel_uid_t32 ug32_gidbase; + s32 ug32_gidlen; + u32 ug32_gdimap; /* gid_t * */ +}; + +struct nfsctl_fhparm32 { + struct sockaddr gf32_addr; + __kernel_dev_t32 gf32_dev; + __kernel_ino_t32 gf32_ino; + s32 gf32_version; +}; + +struct nfsctl_fdparm32 { + struct sockaddr gd32_addr; + s8 gd32_path[NFS_MAXPATHLEN+1]; + s32 gd32_version; +}; + +struct nfsctl_fsparm32 { + struct sockaddr gd32_addr; + s8 gd32_path[NFS_MAXPATHLEN+1]; + s32 gd32_maxlen; +}; + +struct nfsctl_arg32 { + s32 ca32_version; /* safeguard */ + union { + struct nfsctl_svc32 u32_svc; + struct nfsctl_client32 u32_client; + struct nfsctl_export32 u32_export; + struct nfsctl_uidmap32 u32_umap; + struct nfsctl_fhparm32 u32_getfh; + struct nfsctl_fdparm32 u32_getfd; + struct nfsctl_fsparm32 u32_getfs; + } u; +#define ca32_svc u.u32_svc +#define ca32_client u.u32_client +#define ca32_export u.u32_export +#define ca32_umap u.u32_umap +#define ca32_getfh u.u32_getfh +#define ca32_getfd u.u32_getfd +#define ca32_getfs u.u32_getfs +#define ca32_authd u.u32_authd +}; + +union nfsctl_res32 { + __u8 cr32_getfh[NFS_FHSIZE]; + struct knfsd_fh cr32_getfs; +}; + +static int nfs_svc32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= __get_user(karg->ca_svc.svc_port, &arg32->ca32_svc.svc32_port); + err |= __get_user(karg->ca_svc.svc_nthreads, &arg32->ca32_svc.svc32_nthreads); + return err; +} + +static int nfs_clnt32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_client.cl_ident[0], + &arg32->ca32_client.cl32_ident[0], + NFSCLNT_IDMAX); + err |= __get_user(karg->ca_client.cl_naddr, &arg32->ca32_client.cl32_naddr); + err |= copy_from_user(&karg->ca_client.cl_addrlist[0], + &arg32->ca32_client.cl32_addrlist[0], + (sizeof(struct in_addr) * NFSCLNT_ADDRMAX)); + err |= __get_user(karg->ca_client.cl_fhkeytype, + &arg32->ca32_client.cl32_fhkeytype); + err |= __get_user(karg->ca_client.cl_fhkeylen, + &arg32->ca32_client.cl32_fhkeylen); + err |= copy_from_user(&karg->ca_client.cl_fhkey[0], + &arg32->ca32_client.cl32_fhkey[0], + NFSCLNT_KEYMAX); + + if(err) return -EFAULT; + return 0; +} + +static int nfs_exp32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_export.ex_client[0], + &arg32->ca32_export.ex32_client[0], + NFSCLNT_IDMAX); + err |= copy_from_user(&karg->ca_export.ex_path[0], + &arg32->ca32_export.ex32_path[0], + NFS_MAXPATHLEN); + err |= __get_user(karg->ca_export.ex_dev, + &arg32->ca32_export.ex32_dev); + err |= __get_user(karg->ca_export.ex_ino, + &arg32->ca32_export.ex32_ino); + err |= __get_user(karg->ca_export.ex_flags, + &arg32->ca32_export.ex32_flags); + err |= __get_user(karg->ca_export.ex_anon_uid, + &arg32->ca32_export.ex32_anon_uid); + err |= __get_user(karg->ca_export.ex_anon_gid, + &arg32->ca32_export.ex32_anon_gid); + karg->ca_export.ex_anon_uid = karg->ca_export.ex_anon_uid; + karg->ca_export.ex_anon_gid = karg->ca_export.ex_anon_gid; + + if(err) return -EFAULT; + return 0; +} + +static int nfs_uud32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + u32 uaddr; + int i; + int err; + + memset(karg, 0, sizeof(*karg)); + if(__get_user(karg->ca_version, &arg32->ca32_version)) + return -EFAULT; + karg->ca_umap.ug_ident = (char *)get_free_page(GFP_USER); + if(!karg->ca_umap.ug_ident) + return -ENOMEM; + err = __get_user(uaddr, &arg32->ca32_umap.ug32_ident); + if(strncpy_from_user(karg->ca_umap.ug_ident, + (char *)A(uaddr), PAGE_SIZE) <= 0) + return -EFAULT; + err |= __get_user(karg->ca_umap.ug_uidbase, + &arg32->ca32_umap.ug32_uidbase); + err |= __get_user(karg->ca_umap.ug_uidlen, + &arg32->ca32_umap.ug32_uidlen); + err |= __get_user(uaddr, &arg32->ca32_umap.ug32_udimap); + if (err) + return -EFAULT; + karg->ca_umap.ug_udimap = kmalloc((sizeof(uid_t) * karg->ca_umap.ug_uidlen), + GFP_USER); + if(!karg->ca_umap.ug_udimap) + return -ENOMEM; + for(i = 0; i < karg->ca_umap.ug_uidlen; i++) + err |= __get_user(karg->ca_umap.ug_udimap[i], + &(((__kernel_uid_t32 *)A(uaddr))[i])); + err |= __get_user(karg->ca_umap.ug_gidbase, + &arg32->ca32_umap.ug32_gidbase); + err |= __get_user(karg->ca_umap.ug_uidlen, + &arg32->ca32_umap.ug32_gidlen); + err |= __get_user(uaddr, &arg32->ca32_umap.ug32_gdimap); + if (err) + return -EFAULT; + karg->ca_umap.ug_gdimap = kmalloc((sizeof(gid_t) * karg->ca_umap.ug_uidlen), + GFP_USER); + if(!karg->ca_umap.ug_gdimap) + return -ENOMEM; + for(i = 0; i < karg->ca_umap.ug_gidlen; i++) + err |= __get_user(karg->ca_umap.ug_gdimap[i], + &(((__kernel_gid_t32 *)A(uaddr))[i])); + + return err; +} + +static int nfs_getfh32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfh.gf_addr, + &arg32->ca32_getfh.gf32_addr, + (sizeof(struct sockaddr))); + err |= __get_user(karg->ca_getfh.gf_dev, + &arg32->ca32_getfh.gf32_dev); + err |= __get_user(karg->ca_getfh.gf_ino, + &arg32->ca32_getfh.gf32_ino); + err |= __get_user(karg->ca_getfh.gf_version, + &arg32->ca32_getfh.gf32_version); + + if(err) return -EFAULT; + return 0; +} + +static int nfs_getfd32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfd.gd_addr, + &arg32->ca32_getfd.gd32_addr, + (sizeof(struct sockaddr))); + err |= copy_from_user(&karg->ca_getfd.gd_path, + &arg32->ca32_getfd.gd32_path, + (NFS_MAXPATHLEN+1)); + err |= __get_user(karg->ca_getfd.gd_version, + &arg32->ca32_getfd.gd32_version); + + if(err) return -EFAULT; + return 0; +} + +static int nfs_getfs32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfs.gd_addr, + &arg32->ca32_getfs.gd32_addr, + (sizeof(struct sockaddr))); + err |= copy_from_user(&karg->ca_getfs.gd_path, + &arg32->ca32_getfs.gd32_path, + (NFS_MAXPATHLEN+1)); + err |= __get_user(karg->ca_getfs.gd_maxlen, + &arg32->ca32_getfs.gd32_maxlen); + + if(err) return -EFAULT; + return 0; +} + +/* This really doesn't need translations, we are only passing + * back a union which contains opaque nfs file handle data. + */ +static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32) +{ + int err; + + err = copy_to_user(res32, kres, sizeof(*res32)); + + if(err) return -EFAULT; + return 0; +} + +/* Note: it is necessary to treat cmd_parm as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +int asmlinkage sys32_nfsservctl(u32 cmd_parm, struct nfsctl_arg32 *arg32, union nfsctl_res32 *res32) +{ + int cmd = (int)cmd_parm; + struct nfsctl_arg *karg = NULL; + union nfsctl_res *kres = NULL; + mm_segment_t oldfs; + int err; + + karg = kmalloc(sizeof(*karg), GFP_USER); + if(!karg) + return -ENOMEM; + if(res32) { + kres = kmalloc(sizeof(*kres), GFP_USER); + if(!kres) { + kfree(karg); + return -ENOMEM; + } + } + switch(cmd) { + case NFSCTL_SVC: + err = nfs_svc32_trans(karg, arg32); + break; + case NFSCTL_ADDCLIENT: + err = nfs_clnt32_trans(karg, arg32); + break; + case NFSCTL_DELCLIENT: + err = nfs_clnt32_trans(karg, arg32); + break; + case NFSCTL_EXPORT: + case NFSCTL_UNEXPORT: + err = nfs_exp32_trans(karg, arg32); + break; + /* This one is unimplemented, be we're ready for it. */ + case NFSCTL_UGIDUPDATE: + err = nfs_uud32_trans(karg, arg32); + break; + case NFSCTL_GETFH: + err = nfs_getfh32_trans(karg, arg32); + break; + case NFSCTL_GETFD: + err = nfs_getfd32_trans(karg, arg32); + break; + case NFSCTL_GETFS: + err = nfs_getfs32_trans(karg, arg32); + break; + default: + err = -EINVAL; + break; + } + if(err) + goto done; + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_nfsservctl(cmd, karg, kres); + set_fs(oldfs); + + if (err) + goto done; + + if((cmd == NFSCTL_GETFH) || + (cmd == NFSCTL_GETFD) || + (cmd == NFSCTL_GETFS)) + err = nfs_getfh32_res_trans(kres, res32); + +done: + if(karg) { + if(cmd == NFSCTL_UGIDUPDATE) { + if(karg->ca_umap.ug_ident) + kfree(karg->ca_umap.ug_ident); + if(karg->ca_umap.ug_udimap) + kfree(karg->ca_umap.ug_udimap); + if(karg->ca_umap.ug_gdimap) + kfree(karg->ca_umap.ug_gdimap); + } + kfree(karg); + } + if(kres) + kfree(kres); + return err; +} + + + +struct timespec32 { + s32 tv_sec; + s32 tv_nsec; +}; + +extern asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp); + +asmlinkage long sys32_nanosleep(struct timespec32 *rqtp, struct timespec32 *rmtp) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32NI, "sys32_nanosleep - running - pid=%ld, comm=%s \n", current->pid, current->comm); + + if (get_user (t.tv_sec, &rqtp->tv_sec) || + __get_user (t.tv_nsec, &rqtp->tv_nsec)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_nanosleep(&t, rmtp ? &t : NULL); + set_fs (old_fs); + if (rmtp && ret == -EINTR) { + if (__put_user (t.tv_sec, &rmtp->tv_sec) || + __put_user (t.tv_nsec, &rmtp->tv_nsec)) + return -EFAULT; + } + + return ret; +} + + + + +/* These are here just in case some old sparc32 binary calls it. */ +asmlinkage long sys32_pause(void) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_pause - running - pid=%ld, comm=%s \n", current->pid, current->comm); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + + return -ERESTARTNOHAND; +} + + + +static inline long get_it32(struct itimerval *o, struct itimerval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) | + __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) | + __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) | + __get_user(o->it_value.tv_usec, &i->it_value.tv_usec))); +} + +static inline long put_it32(struct itimerval32 *o, struct itimerval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) | + __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) | + __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) | + __put_user(i->it_value.tv_usec, &o->it_value.tv_usec))); +} + +static inline long get_tv32(struct timeval *o, struct timeval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->tv_sec, &i->tv_sec) | + __get_user(o->tv_usec, &i->tv_usec))); +} + +static inline long put_tv32(struct timeval32 *o, struct timeval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->tv_sec, &o->tv_sec) | + __put_user(i->tv_usec, &o->tv_usec))); +} + + + + +extern int do_getitimer(int which, struct itimerval *value); + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getitimer(u32 which, struct itimerval32 *it) +{ + struct itimerval kit; + int error; + + PPCDBG(PPCDBG_SYS32, "sys32_getitimer - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = do_getitimer((int)which, &kit); + if (!error && put_it32(it, &kit)) + error = -EFAULT; + + + PPCDBG(PPCDBG_SYS32, "sys32_getitimer - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return error; +} + + + +extern int do_setitimer(int which, struct itimerval *, struct itimerval *); + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setitimer(u32 which, struct itimerval32 *in, struct itimerval32 *out) +{ + struct itimerval kin, kout; + int error; + + PPCDBG(PPCDBG_SYS32, "sys32_setitimer - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (in) { + if (get_it32(&kin, in)) + return -EFAULT; + } else + memset(&kin, 0, sizeof(kin)); + + error = do_setitimer((int)which, &kin, out ? &kout : NULL); + if (error || !out) + return error; + if (put_it32(out, &kout)) + return -EFAULT; + + + PPCDBG(PPCDBG_SYS32, "sys32_setitimer - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return 0; +} + +#define RLIM_INFINITY32 0xffffffff +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) + +struct rlimit32 { + u32 rlim_cur; + u32 rlim_max; +}; + +extern asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit *rlim); +asmlinkage long sys32_getrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getrlimit(resource, &r); + set_fs(old_fs); + if (!ret) { + ret = put_user(RESOURCE32(r.rlim_cur), &rlim->rlim_cur); + ret |= __put_user(RESOURCE32(r.rlim_max), &rlim->rlim_max); + } + + return ret; +} + +/* Back compatibility for getrlimit. Needed for some apps. */ +asmlinkage long sys32_old_getrlimit(unsigned int resource, struct rlimit32* rlim) +{ + struct rlimit x; // 64-bit version of the resource limits. + struct rlimit32 x32; // 32-bit version of the resource limits. + long rc = 0; + + if (resource >= RLIM_NLIMITS) { + PPCDBG(PPCDBG_SYS32, "sys32_old_getrlimit - specified resource is too large (%x) - pid=%ld, comm=%s\n", resource, current->pid, current->comm); + return -EINVAL; + } + + memcpy(&x, current->rlim+resource, sizeof(struct rlimit)); + + if(x.rlim_cur > RLIM_INFINITY32) + x32.rlim_cur = RLIM_INFINITY32; + else + x32.rlim_cur = x.rlim_cur; + + if(x.rlim_max > RLIM_INFINITY32) + x32.rlim_max = RLIM_INFINITY32; + else + x32.rlim_max = x.rlim_max; + + rc = (copy_to_user(rlim, &x32, sizeof(x32))) ? (-EFAULT) : 0; + if (rc == 0) { + PPCDBG(PPCDBG_SYS32, "sys32_old_getrlimit - current=%x, maximum=%x - pid=%ld, comm=%s\n", x32.rlim_cur, x32.rlim_max, current->pid, current->comm); + } else { + PPCDBG(PPCDBG_SYS32, "sys32_old_getrlimit - unable to copy into user's storage - pid=%ld, comm=%s\n", current->pid, current->comm); + } + return rc; +} + +extern asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim); +asmlinkage long sys32_setrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + long ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32, "sys32_setrlimit - entered - resource=%x, rlim=%p - pid=%ld, comm=%s\n", resource, rlim, current->pid, current->comm); + + if (resource >= RLIM_NLIMITS) return -EINVAL; + if (get_user (r.rlim_cur, &rlim->rlim_cur) || + __get_user (r.rlim_max, &rlim->rlim_max)) + return -EFAULT; + if (r.rlim_cur >= RLIM_INFINITY32) + r.rlim_cur = RLIM_INFINITY; + if (r.rlim_max >= RLIM_INFINITY32) + r.rlim_max = RLIM_INFINITY; + set_fs (KERNEL_DS); + ret = sys_setrlimit(resource, &r); + set_fs (old_fs); + + PPCDBG(PPCDBG_SYS32, "sys32_setrlimit - exited w/ ret=%x - pid=%ld, comm=%s\n", ret, current->pid, current->comm); + return ret; +} + + +struct rusage32 { + struct timeval32 ru_utime; + struct timeval32 ru_stime; + s32 ru_maxrss; + s32 ru_ixrss; + s32 ru_idrss; + s32 ru_isrss; + s32 ru_minflt; + s32 ru_majflt; + s32 ru_nswap; + s32 ru_inblock; + s32 ru_oublock; + s32 ru_msgsnd; + s32 ru_msgrcv; + s32 ru_nsignals; + s32 ru_nvcsw; + s32 ru_nivcsw; +}; + +static int put_rusage (struct rusage32 *ru, struct rusage *r) +{ + int err; + + err = put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec); + err |= __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec); + err |= __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec); + err |= __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec); + err |= __put_user (r->ru_maxrss, &ru->ru_maxrss); + err |= __put_user (r->ru_ixrss, &ru->ru_ixrss); + err |= __put_user (r->ru_idrss, &ru->ru_idrss); + err |= __put_user (r->ru_isrss, &ru->ru_isrss); + err |= __put_user (r->ru_minflt, &ru->ru_minflt); + err |= __put_user (r->ru_majflt, &ru->ru_majflt); + err |= __put_user (r->ru_nswap, &ru->ru_nswap); + err |= __put_user (r->ru_inblock, &ru->ru_inblock); + err |= __put_user (r->ru_oublock, &ru->ru_oublock); + err |= __put_user (r->ru_msgsnd, &ru->ru_msgsnd); + err |= __put_user (r->ru_msgrcv, &ru->ru_msgrcv); + err |= __put_user (r->ru_nsignals, &ru->ru_nsignals); + err |= __put_user (r->ru_nvcsw, &ru->ru_nvcsw); + err |= __put_user (r->ru_nivcsw, &ru->ru_nivcsw); + return err; +} + + +extern asmlinkage long sys_getrusage(int who, struct rusage *ru); + +/* Note: it is necessary to treat who as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getrusage(u32 who, struct rusage32 *ru) +{ + struct rusage r; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32X, "sys32_getrusage - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + set_fs (KERNEL_DS); + ret = sys_getrusage((int)who, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) + return -EFAULT; + + return ret; +} + + + + +struct sysinfo32 { + s32 uptime; + u32 loads[3]; + u32 totalram; + u32 freeram; + u32 sharedram; + u32 bufferram; + u32 totalswap; + u32 freeswap; + unsigned short procs; + char _f[22]; +}; + +extern asmlinkage long sys_sysinfo(struct sysinfo *info); + +asmlinkage long sys32_sysinfo(struct sysinfo32 *info) +{ + struct sysinfo s; + int ret, err; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32, "sys32_sysinfo - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_sysinfo(&s); + set_fs (old_fs); + err = put_user (s.uptime, &info->uptime); + err |= __put_user (s.loads[0], &info->loads[0]); + err |= __put_user (s.loads[1], &info->loads[1]); + err |= __put_user (s.loads[2], &info->loads[2]); + err |= __put_user (s.totalram, &info->totalram); + err |= __put_user (s.freeram, &info->freeram); + err |= __put_user (s.sharedram, &info->sharedram); + err |= __put_user (s.bufferram, &info->bufferram); + err |= __put_user (s.totalswap, &info->totalswap); + err |= __put_user (s.freeswap, &info->freeswap); + err |= __put_user (s.procs, &info->procs); + if (err) + return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sysinfo - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return ret; +} + + + + +/* Translations due to time_t size differences. Which affects all + sorts of things, like timeval and itimerval. */ +extern struct timezone sys_tz; +extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); + +asmlinkage long sys32_gettimeofday(struct timeval32 *tv, struct timezone *tz) +{ + + PPCDBG(PPCDBG_SYS32X, "sys32_gettimeofday - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32(tv, &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + + return 0; +} + + + +asmlinkage long sys32_settimeofday(struct timeval32 *tv, struct timezone *tz) +{ + struct timeval ktv; + struct timezone ktz; + + PPCDBG(PPCDBG_SYS32, "sys32_settimeofday - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + if (tv) { + if (get_tv32(&ktv, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(ktz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL); +} + + + + +struct tms32 { + __kernel_clock_t32 tms_utime; + __kernel_clock_t32 tms_stime; + __kernel_clock_t32 tms_cutime; + __kernel_clock_t32 tms_cstime; +}; + +extern asmlinkage long sys_times(struct tms * tbuf); + +asmlinkage long sys32_times(struct tms32 *tbuf) +{ + struct tms t; + long ret; + mm_segment_t old_fs = get_fs (); + int err; + + PPCDBG(PPCDBG_SYS32, "sys32_times - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_times(tbuf ? &t : NULL); + set_fs (old_fs); + if (tbuf) { + err = put_user (t.tms_utime, &tbuf->tms_utime); + err |= __put_user (t.tms_stime, &tbuf->tms_stime); + err |= __put_user (t.tms_cutime, &tbuf->tms_cutime); + err |= __put_user (t.tms_cstime, &tbuf->tms_cstime); + if (err) + ret = -EFAULT; + } + + PPCDBG(PPCDBG_SYS32, "sys32_times - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return ret; +} + +struct msgbuf32 { s32 mtype; char mtext[1]; }; + +struct semid_ds32 { + struct ipc_perm sem_perm; + __kernel_time_t32 sem_otime; + __kernel_time_t32 sem_ctime; + u32 sem_base; + u32 sem_pending; + u32 sem_pending_last; + u32 undo; + unsigned short sem_nsems; +}; + +struct semid64_ds32 { + struct ipc64_perm sem_perm; + unsigned int __unused1; + __kernel_time_t32 sem_otime; + unsigned int __unused2; + __kernel_time_t32 sem_ctime; + u32 sem_nsems; + u32 __unused3; + u32 __unused4; +}; + +struct msqid_ds32 +{ + struct ipc_perm msg_perm; + u32 msg_first; + u32 msg_last; + __kernel_time_t32 msg_stime; + __kernel_time_t32 msg_rtime; + __kernel_time_t32 msg_ctime; + u32 msg_lcbytes; + u32 msg_lqbytes; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + __kernel_ipc_pid_t32 msg_lspid; + __kernel_ipc_pid_t32 msg_lrpid; +}; + +struct msqid64_ds32 { + struct ipc64_perm msg_perm; + unsigned int __unused1; + __kernel_time_t32 msg_stime; + unsigned int __unused2; + __kernel_time_t32 msg_rtime; + unsigned int __unused3; + __kernel_time_t32 msg_ctime; + unsigned int msg_cbytes; + unsigned int msg_qnum; + unsigned int msg_qbytes; + __kernel_pid_t32 msg_lspid; + __kernel_pid_t32 msg_lrpid; + unsigned int __unused4; + unsigned int __unused5; +}; + +struct shmid_ds32 { + struct ipc_perm shm_perm; + int shm_segsz; + __kernel_time_t32 shm_atime; + __kernel_time_t32 shm_dtime; + __kernel_time_t32 shm_ctime; + __kernel_ipc_pid_t32 shm_cpid; + __kernel_ipc_pid_t32 shm_lpid; + unsigned short shm_nattch; + unsigned short __unused; + unsigned int __unused2; + unsigned int __unused3; +}; + +struct shmid64_ds32 { + struct ipc64_perm shm_perm; + unsigned int __unused1; + __kernel_time_t32 shm_atime; + unsigned int __unused2; + __kernel_time_t32 shm_dtime; + unsigned int __unused3; + __kernel_time_t32 shm_ctime; + unsigned int __unused4; + __kernel_size_t32 shm_segsz; + __kernel_pid_t32 shm_cpid; + __kernel_pid_t32 shm_lpid; + unsigned int shm_nattch; + unsigned int __unused5; + unsigned int __unused6; +}; + +/* + * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit + * emulation.. + * + * This is really horribly ugly. + */ +static long do_sys32_semctl(int first, int second, int third, void *uptr) +{ + union semun fourth; + u32 pad; + int err, err2; + mm_segment_t old_fs; + + if (!uptr) + return -EINVAL; + err = -EFAULT; + if (get_user(pad, (u32 *)uptr)) + return err; + if (third == SETVAL) + fourth.val = (int)pad; + else + fourth.__pad = (void *)A(pad); + switch (third & (~IPC_64)) { + + case IPC_INFO: + case IPC_RMID: + case SEM_INFO: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case GETALL: + case SETALL: + case SETVAL: + err = sys_semctl(first, second, third, fourth); + break; + + case IPC_STAT: + case SEM_STAT: + if (third & IPC_64) { + struct semid64_ds s64; + struct semid64_ds32 *usp; + + usp = (struct semid64_ds32 *)A(pad); + fourth.__pad = &s64; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_semctl(first, second, third, fourth); + set_fs(old_fs); + err2 = copy_to_user(&usp->sem_perm, &s64.sem_perm, + sizeof(struct ipc64_perm)); + err2 |= __put_user(s64.sem_otime, &usp->sem_otime); + err2 |= __put_user(s64.sem_ctime, &usp->sem_ctime); + err2 |= __put_user(s64.sem_nsems, &usp->sem_nsems); + if (err2) + err = -EFAULT; + } else { + struct semid_ds s; + struct semid_ds32 *usp; + + usp = (struct semid_ds32 *)A(pad); + fourth.__pad = &s; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_semctl(first, second, third, fourth); + set_fs(old_fs); + err2 = copy_to_user(&usp->sem_perm, &s.sem_perm, + sizeof(struct ipc_perm)); + err2 |= __put_user(s.sem_otime, &usp->sem_otime); + err2 |= __put_user(s.sem_ctime, &usp->sem_ctime); + err2 |= __put_user(s.sem_nsems, &usp->sem_nsems); + if (err2) + err = -EFAULT; + } + break; + + case IPC_SET: + if (third & IPC_64) { + struct semid64_ds s64; + struct semid64_ds32 *usp; + + usp = (struct semid64_ds32 *)A(pad); + + err = get_user(s64.sem_perm.uid, &usp->sem_perm.uid); + err |= __get_user(s64.sem_perm.gid, + &usp->sem_perm.gid); + err |= __get_user(s64.sem_perm.mode, + &usp->sem_perm.mode); + if (err) + goto out; + fourth.__pad = &s64; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_semctl(first, second, third, fourth); + set_fs(old_fs); + + } else { + struct semid_ds s; + struct semid_ds32 *usp; + + usp = (struct semid_ds32 *)A(pad); + + err = get_user(s.sem_perm.uid, &usp->sem_perm.uid); + err |= __get_user(s.sem_perm.gid, + &usp->sem_perm.gid); + err |= __get_user(s.sem_perm.mode, + &usp->sem_perm.mode); + if (err) + goto out; + fourth.__pad = &s; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_semctl(first, second, third, fourth); + set_fs(old_fs); + } + break; + } +out: + return err; +} + +static int +do_sys32_msgsnd(int first, int second, int third, void *uptr) +{ + struct msgbuf *p; + struct msgbuf32 *up = (struct msgbuf32 *)uptr; + mm_segment_t old_fs; + int err; + + if (second < 0) + return -EINVAL; + + p = kmalloc(second + sizeof(struct msgbuf) + 4, GFP_USER); + if (!p) + return -ENOMEM; + err = get_user(p->mtype, &up->mtype); + err |= __copy_from_user(p->mtext, &up->mtext, second); + if (err) { + err = -EFAULT; + goto out; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgsnd(first, p, second, third); + set_fs(old_fs); +out: + kfree(p); + return err; +} + +static int +do_sys32_msgrcv(int first, int second, int msgtyp, int third, + int version, void *uptr) +{ + struct msgbuf32 *up; + struct msgbuf *p; + mm_segment_t old_fs; + int err; + + if (second < 0) + return -EINVAL; + + if (!version) { + struct ipc_kludge *uipck = (struct ipc_kludge *)uptr; + struct ipc_kludge ipck; + + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user(&ipck, uipck, sizeof(struct ipc_kludge))) + goto out; + uptr = (void *)A(ipck.msgp); + msgtyp = ipck.msgtyp; + } + err = -ENOMEM; + p = kmalloc(second + sizeof (struct msgbuf) + 4, GFP_USER); + if (!p) + goto out; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgrcv(first, p, second + 4, msgtyp, third); + set_fs(old_fs); + if (err < 0) + goto free_then_out; + up = (struct msgbuf32 *)uptr; + if (put_user(p->mtype, &up->mtype) || + __copy_to_user(&up->mtext, p->mtext, err)) + err = -EFAULT; +free_then_out: + kfree(p); +out: + return err; +} + +static int +do_sys32_msgctl(int first, int second, void *uptr) +{ + int err = -EINVAL, err2; + mm_segment_t old_fs; + + switch (second & (~IPC_64)) { + + case IPC_INFO: + case IPC_RMID: + case MSG_INFO: + err = sys_msgctl(first, second, (struct msqid_ds *)uptr); + break; + + case IPC_SET: + if (second & IPC_64) { + struct msqid64_ds m64; + struct msqid64_ds32 *up = (struct msqid64_ds32 *)uptr; + + err2 = copy_from_user(&m64.msg_perm, &up->msg_perm, + sizeof(struct ipc64_perm)); + err2 |= __get_user(m64.msg_qbytes, &up->msg_qbytes); + if (err2) { + err = -EFAULT; + break; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgctl(first, second, + (struct msqid_ds *)&m64); + set_fs(old_fs); + } else { + struct msqid_ds m; + struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; + + err2 = copy_from_user(&m.msg_perm, &up->msg_perm, + sizeof(struct ipc_perm)); + err2 |= __get_user(m.msg_qbytes, &up->msg_qbytes); + if (err2) { + err = -EFAULT; + break; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgctl(first, second, &m); + set_fs(old_fs); + } + break; + + case IPC_STAT: + case MSG_STAT: + if (second & IPC_64) { + struct msqid64_ds m64; + struct msqid64_ds32 *up = (struct msqid64_ds32 *)uptr; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgctl(first, second, + (struct msqid_ds *)&m64); + set_fs(old_fs); + + err2 = copy_to_user(&up->msg_perm, &m64.msg_perm, + sizeof(struct ipc64_perm)); + err2 |= __put_user(m64.msg_stime, &up->msg_stime); + err2 |= __put_user(m64.msg_rtime, &up->msg_rtime); + err2 |= __put_user(m64.msg_ctime, &up->msg_ctime); + err2 |= __put_user(m64.msg_cbytes, &up->msg_cbytes); + err2 |= __put_user(m64.msg_qnum, &up->msg_qnum); + err2 |= __put_user(m64.msg_qbytes, &up->msg_qbytes); + err2 |= __put_user(m64.msg_lspid, &up->msg_lspid); + err2 |= __put_user(m64.msg_lrpid, &up->msg_lrpid); + if (err2) + err = -EFAULT; + } else { + struct msqid64_ds m; + struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgctl(first, second, (struct msqid_ds *)&m); + set_fs(old_fs); + + err2 = copy_to_user(&up->msg_perm, &m.msg_perm, + sizeof(struct ipc_perm)); + err2 |= __put_user(m.msg_stime, &up->msg_stime); + err2 |= __put_user(m.msg_rtime, &up->msg_rtime); + err2 |= __put_user(m.msg_ctime, &up->msg_ctime); + err2 |= __put_user(m.msg_cbytes, &up->msg_cbytes); + err2 |= __put_user(m.msg_qnum, &up->msg_qnum); + err2 |= __put_user(m.msg_qbytes, &up->msg_qbytes); + err2 |= __put_user(m.msg_lspid, &up->msg_lspid); + err2 |= __put_user(m.msg_lrpid, &up->msg_lrpid); + if (err2) + err = -EFAULT; + } + break; + } + return err; +} + +static int +do_sys32_shmat(int first, int second, int third, int version, void *uptr) +{ + unsigned long raddr; + u32 *uaddr = (u32 *)A((u32)third); + int err = -EINVAL; + + if (version == 1) + return err; + err = sys_shmat(first, uptr, second, &raddr); + if (err) + return err; + err = put_user(raddr, uaddr); + return err; +} + +static int +do_sys32_shmctl(int first, int second, void *uptr) +{ + int err = -EFAULT, err2; + mm_segment_t old_fs; + + switch (second & (~IPC_64)) { + + case IPC_INFO: + case IPC_RMID: + case SHM_LOCK: + case SHM_UNLOCK: + err = sys_shmctl(first, second, (struct shmid_ds *)uptr); + break; + case IPC_SET: + if (second & IPC_64) { + struct shmid64_ds32 *up = (struct shmid64_ds32 *)uptr; + struct shmid64_ds s64; + + err = get_user(s64.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user(s64.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user(s64.shm_perm.mode, + &up->shm_perm.mode); + if (err) + break; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_shmctl(first, second, + (struct shmid_ds *)&s64); + set_fs(old_fs); + } else { + struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; + struct shmid_ds s; + + err = get_user(s.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user(s.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user(s.shm_perm.mode, &up->shm_perm.mode); + if (err) + break; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_shmctl(first, second, &s); + set_fs(old_fs); + } + break; + + case IPC_STAT: + case SHM_STAT: + if (second & IPC_64) { + struct shmid64_ds32 *up = (struct shmid64_ds32 *)uptr; + struct shmid64_ds s64; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_shmctl(first, second, + (struct shmid_ds *)&s64); + set_fs(old_fs); + if (err < 0) + break; + + err2 = copy_to_user(&up->shm_perm, &s64.shm_perm, + sizeof(struct ipc64_perm)); + err2 |= __put_user(s64.shm_atime, &up->shm_atime); + err2 |= __put_user(s64.shm_dtime, &up->shm_dtime); + err2 |= __put_user(s64.shm_ctime, &up->shm_ctime); + err2 |= __put_user(s64.shm_segsz, &up->shm_segsz); + err2 |= __put_user(s64.shm_nattch, &up->shm_nattch); + err2 |= __put_user(s64.shm_cpid, &up->shm_cpid); + err2 |= __put_user(s64.shm_lpid, &up->shm_lpid); + if (err2) + err = -EFAULT; + } else { + struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; + struct shmid_ds s; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_shmctl(first, second, &s); + set_fs(old_fs); + if (err < 0) + break; + + err2 = copy_to_user(&up->shm_perm, &s.shm_perm, + sizeof(struct ipc_perm)); + err2 |= __put_user (s.shm_atime, &up->shm_atime); + err2 |= __put_user (s.shm_dtime, &up->shm_dtime); + err2 |= __put_user (s.shm_ctime, &up->shm_ctime); + err2 |= __put_user (s.shm_segsz, &up->shm_segsz); + err2 |= __put_user (s.shm_nattch, &up->shm_nattch); + err2 |= __put_user (s.shm_cpid, &up->shm_cpid); + err2 |= __put_user (s.shm_lpid, &up->shm_lpid); + if (err2) + err = -EFAULT; + } + break; + + case SHM_INFO: { + struct shm_info si; + struct shm_info32 { + int used_ids; + u32 shm_tot, shm_rss, shm_swp; + u32 swap_attempts, swap_successes; + } *uip = (struct shm_info32 *)uptr; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_shmctl(first, second, (struct shmid_ds *)&si); + set_fs(old_fs); + if (err < 0) + break; + err2 = put_user(si.used_ids, &uip->used_ids); + err2 |= __put_user(si.shm_tot, &uip->shm_tot); + err2 |= __put_user(si.shm_rss, &uip->shm_rss); + err2 |= __put_user(si.shm_swp, &uip->shm_swp); + err2 |= __put_user(si.swap_attempts, &uip->swap_attempts); + err2 |= __put_user(si.swap_successes, &uip->swap_successes); + if (err2) + err = -EFAULT; + break; + } + } + return err; +} + +/* + * Note: it is necessary to treat first_parm, second_parm, and + * third_parm as unsigned ints, with the corresponding cast to a + * signed int to insure that the proper conversion (sign extension) + * between the register representation of a signed int (msr in 32-bit + * mode) and the register representation of a signed int (msr in + * 64-bit mode) is performed. + */ +asmlinkage long sys32_ipc(u32 call, u32 first_parm, u32 second_parm, u32 third_parm, u32 ptr, u32 fifth) +{ + int first = (int)first_parm; + int second = (int)second_parm; + int third = (int)third_parm; + int version, err; + + PPCDBG(PPCDBG_SYS32, "sys32_ipc - entered - call=%x, parm1=%x, parm2=%x, parm3=%x, parm4=%x, parm5=%x \n", + call, first_parm, second_parm, third_parm, ptr, fifth); + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + + case SEMOP: + /* struct sembuf is the same on 32 and 64bit :)) */ + err = sys_semop(first, (struct sembuf *)AA(ptr), + second); + break; + case SEMGET: + err = sys_semget(first, second, third); + break; + case SEMCTL: + err = do_sys32_semctl(first, second, third, + (void *)AA(ptr)); + break; + + case MSGSND: + err = do_sys32_msgsnd(first, second, third, + (void *)AA(ptr)); + break; + case MSGRCV: + err = do_sys32_msgrcv(first, second, fifth, third, + version, (void *)AA(ptr)); + break; + case MSGGET: + err = sys_msgget((key_t)first, second); + break; + case MSGCTL: + err = do_sys32_msgctl(first, second, (void *)AA(ptr)); + break; + + case SHMAT: + err = do_sys32_shmat(first, second, third, + version, (void *)AA(ptr)); + break; + case SHMDT: + err = sys_shmdt((char *)AA(ptr)); + break; + case SHMGET: + err = sys_shmget(first, second, third); + break; + case SHMCTL: + err = do_sys32_shmctl(first, second, (void *)AA(ptr)); + break; + default: + err = -EINVAL; + break; + } + + + PPCDBG(PPCDBG_SYS32, "sys32_ipc - exited w/ %d/0x%x \n", err, err); + return err; +} + +/* stat syscall methods. */ +extern asmlinkage int sys_stat(char* filename, struct __old_kernel_stat* statbuf); + +static int cp_old_stat32(struct inode* inode, struct __old_kernel_stat32* statbuf) +{ + static int warncount = 5; + struct __old_kernel_stat32 tmp; + + if (warncount) { + warncount--; + printk("VFS: Warning: %s using old stat() call. Recompile your binary.\n", + current->comm); + } + + tmp.st_dev = kdev_t_to_nr(inode->i_dev); + tmp.st_ino = inode->i_ino; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlink; + SET_OLDSTAT_UID(tmp, inode->i_uid); + SET_OLDSTAT_GID(tmp, inode->i_gid); + tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); + tmp.st_size = inode->i_size; + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} + +asmlinkage long sys32_stat(char* filename, struct __old_kernel_stat32* statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_stat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = user_path_walk(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_old_stat32(nd.dentry->d_inode, statbuf); + path_release(&nd); + } + + PPCDBG(PPCDBG_SYS32X, "sys32_stat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return error; +} + +asmlinkage long sys32_fstat(unsigned int fd, struct __old_kernel_stat32* statbuf) +{ + struct file *f; + int err = -EBADF; + + PPCDBG(PPCDBG_SYS32X, "sys32_fstat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + f = fget(fd); + if (f) { + struct dentry * dentry = f->f_dentry; + + err = do_revalidate(dentry); + if (!err) + err = cp_old_stat32(dentry->d_inode, statbuf); + fput(f); + } + + PPCDBG(PPCDBG_SYS32X, "sys32_fstat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return err; +} + +asmlinkage long sys32_lstat(char* filename, struct __old_kernel_stat32* statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_lstat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = user_path_walk_link(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_old_stat32(nd.dentry->d_inode, statbuf); + + path_release(&nd); + } + + PPCDBG(PPCDBG_SYS32X, "sys32_lstat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return error; +} + +extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t* offset, size_t count); + +/* Note: it is necessary to treat out_fd and in_fd as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sendfile(u32 out_fd, u32 in_fd, __kernel_off_t32* offset, u32 count) +{ + mm_segment_t old_fs = get_fs(); + int ret; + off_t of; + + if (offset && get_user(of, offset)) + return -EFAULT; + + set_fs(KERNEL_DS); + ret = sys_sendfile((int)out_fd, (int)in_fd, offset ? &of : NULL, count); + set_fs(old_fs); + + if (offset && put_user(of, offset)) + return -EFAULT; + + return ret; +} + +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen); + +asmlinkage long sys32_setsockopt(int fd, int level, int optname, char* optval, int optlen) +{ + + PPCDBG(PPCDBG_SYS32,"sys32_setsockopt - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + if (optname == SO_ATTACH_FILTER) { + struct sock_fprog32 { + __u16 len; + __u32 filter; + } *fprog32 = (struct sock_fprog32 *)optval; + struct sock_fprog kfprog; + struct sock_filter *kfilter; + unsigned int fsize; + mm_segment_t old_fs; + __u32 uptr; + int ret; + + if (get_user(kfprog.len, &fprog32->len) || + __get_user(uptr, &fprog32->filter)) + return -EFAULT; + kfprog.filter = (struct sock_filter *)A(uptr); + fsize = kfprog.len * sizeof(struct sock_filter); + kfilter = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL); + if (kfilter == NULL) + return -ENOMEM; + if (copy_from_user(kfilter, kfprog.filter, fsize)) { + kfree(kfilter); + return -EFAULT; + } + kfprog.filter = kfilter; + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, + (char *)&kfprog, sizeof(kfprog)); + set_fs(old_fs); + kfree(kfilter); + return ret; + } + return sys_setsockopt(fd, level, optname, optval, optlen); +} + + + + +#define MAX_SOCK_ADDR 128 /* 108 for Unix domain - 16 for IP, 16 for IPX, 24 for IPv6, about 80 for AX.25 */ +#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen)) +#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen)) + +#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) ) + +#define CMSG32_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32)))) +#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len)) +#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len)) +#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \ + (struct cmsghdr32 *)(ctl) : \ + (struct cmsghdr32 *)NULL) +#define CMSG32_FIRSTHDR(msg) __CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) + +struct msghdr32 +{ + u32 msg_name; + int msg_namelen; + u32 msg_iov; + __kernel_size_t32 msg_iovlen; + u32 msg_control; + __kernel_size_t32 msg_controllen; + unsigned msg_flags; +}; + +struct cmsghdr32 +{ + __kernel_size_t32 cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size, + struct cmsghdr32 *__cmsg, int __cmsg_len) +{ + struct cmsghdr32 * __ptr; + + __ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) + + CMSG32_ALIGN(__cmsg_len)); + if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size) + return NULL; + + return __ptr; +} + +__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg, + struct cmsghdr32 *__cmsg, + int __cmsg_len) +{ + return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen, + __cmsg, __cmsg_len); +} + +extern struct socket *sockfd_lookup(int fd, int *err); + +extern __inline__ void sockfd_put(struct socket *sock) +{ + fput(sock->file); +} + +static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, struct msghdr32 *umsg) +{ + u32 tmp1, tmp2, tmp3; + int err; + + err = get_user(tmp1, &umsg->msg_name); + err |= __get_user(tmp2, &umsg->msg_iov); + err |= __get_user(tmp3, &umsg->msg_control); + if (err) + return -EFAULT; + + kmsg->msg_name = (void *)A(tmp1); + kmsg->msg_iov = (struct iovec *)A(tmp2); + kmsg->msg_control = (void *)A(tmp3); + + err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); + err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen); + err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); + err |= get_user(kmsg->msg_flags, &umsg->msg_flags); + + return err; +} + +static inline int iov_from_user32_to_kern(struct iovec *kiov, + struct iovec32 *uiov32, + int niov) +{ + int tot_len = 0; + + while(niov > 0) { + u32 len, buf; + + if(get_user(len, &uiov32->iov_len) || + get_user(buf, &uiov32->iov_base)) { + tot_len = -EFAULT; + break; + } + tot_len += len; + kiov->iov_base = (void *)A(buf); + kiov->iov_len = (__kernel_size_t) len; + uiov32++; + kiov++; + niov--; + } + return tot_len; +} + +/* I've named the args so it is easy to tell whose space the pointers are in. */ +static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, + char *kern_address, int mode) +{ + int tot_len; + + if(kern_msg->msg_namelen) { + if(mode==VERIFY_READ) { + int err = move_addr_to_kernel(kern_msg->msg_name, + kern_msg->msg_namelen, + kern_address); + if(err < 0) + return err; + } + kern_msg->msg_name = kern_address; + } else + kern_msg->msg_name = NULL; + + if(kern_msg->msg_iovlen > UIO_FASTIOV) { + kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), + GFP_KERNEL); + if(!kern_iov) + return -ENOMEM; + } + + tot_len = iov_from_user32_to_kern(kern_iov, + (struct iovec32 *)kern_msg->msg_iov, + kern_msg->msg_iovlen); + if(tot_len >= 0) + kern_msg->msg_iov = kern_iov; + else if(kern_msg->msg_iovlen > UIO_FASTIOV) + kfree(kern_iov); + + return tot_len; +} + +/* There is a lot of hair here because the alignment rules (and + * thus placement) of cmsg headers and length are different for + * 32-bit apps. -DaveM + */ +static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg, + unsigned char *stackbuf, int stackbuf_size) +{ + struct cmsghdr32 *ucmsg; + struct cmsghdr *kcmsg, *kcmsg_base; + __kernel_size_t32 ucmlen; + __kernel_size_t kcmlen, tmp; + + kcmlen = 0; + kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + if(get_user(ucmlen, &ucmsg->cmsg_len)) + return -EFAULT; + + /* Catch bogons. */ + if(CMSG32_ALIGN(ucmlen) < + CMSG32_ALIGN(sizeof(struct cmsghdr32))) + return -EINVAL; + if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) + + ucmlen) > kmsg->msg_controllen) + return -EINVAL; + + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmlen += tmp; + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + if (kcmlen == 0) + return -EINVAL; + + /* The kcmlen holds the 64-bit version of the control length. + * It may not be modified as we do not stick it into the kmsg + * until we have successfully copied over all of the data + * from the user. + */ + if (kcmlen > stackbuf_size) + kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); + if (kcmsg == NULL) + return -ENOBUFS; + + /* Now copy them over neatly. */ + memset(kcmsg, 0, kcmlen); + ucmsg = CMSG32_FIRSTHDR(kmsg); + while (ucmsg != NULL) { + __get_user(ucmlen, &ucmsg->cmsg_len); + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmsg->cmsg_len = tmp; + __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); + + /* Copy over the data. */ + if(copy_from_user(CMSG_DATA(kcmsg), + CMSG32_DATA(ucmsg), + (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))))) + goto out_free_efault; + + /* Advance. */ + kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + + /* Ok, looks like we made it. Hook it up and return success. */ + kmsg->msg_control = kcmsg_base; + kmsg->msg_controllen = kcmlen; + return 0; + +out_free_efault: + if(kcmsg_base != (struct cmsghdr *)stackbuf) + kfree(kcmsg_base); + return -EFAULT; +} + +asmlinkage long sys32_sendmsg(int fd, struct msghdr32* user_msg, unsigned int user_flags) +{ + struct socket *sock; + char address[MAX_SOCK_ADDR]; + struct iovec iov[UIO_FASTIOV]; + unsigned char ctl[sizeof(struct cmsghdr) + 20]; + unsigned char *ctl_buf = ctl; + struct msghdr kern_msg; + int err, total_len; + + PPCDBG(PPCDBG_SYS32, "sys32_sendmsg - entered - fd=%x, user_msg@=%p, user_flags=%x \n", fd, user_msg, user_flags); + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); + if (err < 0) + goto out; + total_len = err; + + if(kern_msg.msg_controllen) { + err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl)); + if(err) + goto out_freeiov; + ctl_buf = kern_msg.msg_control; + } + kern_msg.msg_flags = user_flags; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + kern_msg.msg_flags |= MSG_DONTWAIT; + err = sock_sendmsg(sock, &kern_msg, total_len); + sockfd_put(sock); + } + + /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ + if(ctl_buf != ctl) + kfree(ctl_buf); +out_freeiov: + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + + PPCDBG(PPCDBG_SYS32, "sys32_sendmsg - exited w/ %lx \n", err); + return err; +} + +static void put_cmsg32(struct msghdr *kmsg, int level, int type, + int len, void *data) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + struct cmsghdr32 cmhdr; + int cmlen = CMSG32_LEN(len); + + if (cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { + kmsg->msg_flags |= MSG_CTRUNC; + return; + } + + if (kmsg->msg_controllen < cmlen) { + kmsg->msg_flags |= MSG_CTRUNC; + cmlen = kmsg->msg_controllen; + } + cmhdr.cmsg_level = level; + cmhdr.cmsg_type = type; + cmhdr.cmsg_len = cmlen; + + if (copy_to_user(cm, &cmhdr, sizeof cmhdr)) + return; + if (copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32))) + return; + cmlen = CMSG32_SPACE(len); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; +} + + +static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int); + int fdnum = scm->fp->count; + struct file **fp = scm->fp->fp; + int *cmfptr; + int err = 0, i; + + if (fdnum < fdmax) + fdmax = fdnum; + + for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) { + int new_fd; + err = get_unused_fd(); + if (err < 0) + break; + new_fd = err; + err = put_user(new_fd, cmfptr); + if (err) { + put_unused_fd(new_fd); + break; + } + /* Bump the usage count and install the file. */ + get_file(fp[i]); + fd_install(new_fd, fp[i]); + } + + if (i > 0) { + int cmlen = CMSG32_LEN(i * sizeof(int)); + if (!err) + err = put_user(SOL_SOCKET, &cm->cmsg_level); + if (!err) + err = put_user(SCM_RIGHTS, &cm->cmsg_type); + if (!err) + err = put_user(cmlen, &cm->cmsg_len); + if (!err) { + cmlen = CMSG32_SPACE(i * sizeof(int)); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; + } + } + if (i < fdnum) + kmsg->msg_flags |= MSG_CTRUNC; + + /* + * All of the files that fit in the message have had their + * usage counts incremented, so we just free the list. + */ + __scm_destroy(scm); +} + +/* In these cases we (currently) can just copy to data over verbatim + * because all CMSGs created by the kernel have well defined types which + * have the same layout in both the 32-bit and 64-bit API. One must add + * some special cased conversions here if we start sending control messages + * with incompatible types. + * + * SCM_RIGHTS and SCM_CREDENTIALS are done by hand in recvmsg32 right after + * we do our work. The remaining cases are: + * + * SOL_IP IP_PKTINFO struct in_pktinfo 32-bit clean + * IP_TTL int 32-bit clean + * IP_TOS __u8 32-bit clean + * IP_RECVOPTS variable length 32-bit clean + * IP_RETOPTS variable length 32-bit clean + * (these last two are clean because the types are defined + * by the IPv4 protocol) + * IP_RECVERR struct sock_extended_err + + * struct sockaddr_in 32-bit clean + * SOL_IPV6 IPV6_RECVERR struct sock_extended_err + + * struct sockaddr_in6 32-bit clean + * IPV6_PKTINFO struct in6_pktinfo 32-bit clean + * IPV6_HOPLIMIT int 32-bit clean + * IPV6_FLOWINFO u32 32-bit clean + * IPV6_HOPOPTS ipv6 hop exthdr 32-bit clean + * IPV6_DSTOPTS ipv6 dst exthdr(s) 32-bit clean + * IPV6_RTHDR ipv6 routing exthdr 32-bit clean + * IPV6_AUTHHDR ipv6 auth exthdr 32-bit clean + */ +static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_uptr) +{ + unsigned char *workbuf, *wp; + unsigned long bufsz, space_avail; + struct cmsghdr *ucmsg; + + bufsz = ((unsigned long)kmsg->msg_control) - orig_cmsg_uptr; + space_avail = kmsg->msg_controllen + bufsz; + wp = workbuf = kmalloc(bufsz, GFP_KERNEL); + if(workbuf == NULL) + goto fail; + + /* To make this more sane we assume the kernel sends back properly + * formatted control messages. Because of how the kernel will truncate + * the cmsg_len for MSG_TRUNC cases, we need not check that case either. + */ + ucmsg = (struct cmsghdr *) orig_cmsg_uptr; + while(((unsigned long)ucmsg) <= + (((unsigned long)kmsg->msg_control) - sizeof(struct cmsghdr))) { + struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp; + int clen64, clen32; + + /* UCMSG is the 64-bit format CMSG entry in user-space. + * KCMSG32 is within the kernel space temporary buffer + * we use to convert into a 32-bit style CMSG. + */ + __get_user(kcmsg32->cmsg_len, &ucmsg->cmsg_len); + __get_user(kcmsg32->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg32->cmsg_type, &ucmsg->cmsg_type); + + clen64 = kcmsg32->cmsg_len; + copy_from_user(CMSG32_DATA(kcmsg32), CMSG_DATA(ucmsg), + clen64 - CMSG_ALIGN(sizeof(*ucmsg))); + clen32 = ((clen64 - CMSG_ALIGN(sizeof(*ucmsg))) + + CMSG32_ALIGN(sizeof(struct cmsghdr32))); + kcmsg32->cmsg_len = clen32; + + switch (kcmsg32->cmsg_type) { + /* + * The timestamp type's data needs to be converted + * from 64-bit time values to 32-bit time values + */ + case SO_TIMESTAMP: { + __kernel_time_t32* ptr_time32 = CMSG32_DATA(kcmsg32); + __kernel_time_t* ptr_time = CMSG_DATA(ucmsg); + *ptr_time32 = *ptr_time; + *(ptr_time32+1) = *(ptr_time+1); + kcmsg32->cmsg_len -= 2*(sizeof(__kernel_time_t) - + sizeof(__kernel_time_t32)); + } + default:; + } + + ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64)); + wp = (((char *)kcmsg32) + CMSG32_ALIGN(kcmsg32->cmsg_len)); + } + + /* Copy back fixed up data, and adjust pointers. */ + bufsz = (wp - workbuf); + copy_to_user((void *)orig_cmsg_uptr, workbuf, bufsz); + + kmsg->msg_control = (struct cmsghdr *) + (((char *)orig_cmsg_uptr) + bufsz); + kmsg->msg_controllen = space_avail - bufsz; + + kfree(workbuf); + return; + +fail: + /* If we leave the 64-bit format CMSG chunks in there, + * the application could get confused and crash. So to + * ensure greater recovery, we report no CMSGs. + */ + kmsg->msg_controllen += bufsz; + kmsg->msg_control = (void *) orig_cmsg_uptr; +} + +asmlinkage long sys32_recvmsg(int fd, struct msghdr32* user_msg, unsigned int user_flags) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct msghdr kern_msg; + char addr[MAX_SOCK_ADDR]; + struct socket *sock; + struct iovec *iov = iovstack; + struct sockaddr *uaddr; + int *uaddr_len; + unsigned long cmsg_ptr; + int err, total_len, len = 0; + + PPCDBG(PPCDBG_SYS32, "sys32_recvmsg - entered - fd=%x, user_msg@=%p, user_flags=%x \n", fd, user_msg, user_flags); + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + + uaddr = kern_msg.msg_name; + uaddr_len = &user_msg->msg_namelen; + err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); + if (err < 0) + goto out; + total_len = err; + + cmsg_ptr = (unsigned long) kern_msg.msg_control; + kern_msg.msg_flags = 0; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + struct scm_cookie scm; + + if (sock->file->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; + memset(&scm, 0, sizeof(scm)); + err = sock->ops->recvmsg(sock, &kern_msg, total_len, + user_flags, &scm); + if(err >= 0) { + len = err; + if(!kern_msg.msg_control) { + if(sock->passcred || scm.fp) + kern_msg.msg_flags |= MSG_CTRUNC; + if(scm.fp) + __scm_destroy(&scm); + } else { + /* If recvmsg processing itself placed some + * control messages into user space, it's is + * using 64-bit CMSG processing, so we need + * to fix it up before we tack on more stuff. + */ + if((unsigned long) kern_msg.msg_control != cmsg_ptr) + cmsg32_recvmsg_fixup(&kern_msg, cmsg_ptr); + + /* Wheee... */ + if(sock->passcred) + put_cmsg32(&kern_msg, + SOL_SOCKET, SCM_CREDENTIALS, + sizeof(scm.creds), &scm.creds); + if(scm.fp != NULL) + scm_detach_fds32(&kern_msg, &scm); + } + } + sockfd_put(sock); + } + + if (uaddr != NULL && err >= 0 && kern_msg.msg_namelen) + err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); + if(cmsg_ptr != 0 && err >= 0) { + unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control); + __kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr - cmsg_ptr); + err |= __put_user(uclen, &user_msg->msg_controllen); + } + if(err >= 0) + err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + if(err < 0) + return err; + + PPCDBG(PPCDBG_SYS32, "sys32_recvmsg - exited w/ %lx \n", len); + return len; +} + +/* + * count32() counts the number of arguments/envelopes + */ +static int count32(u32 * argv, int max) +{ + int i = 0; + + if (argv != NULL) { + for (;;) { + u32 p; int error; + + error = get_user(p,argv); + if (error) + return error; + if (!p) + break; + argv++; + if (++i > max) + return -E2BIG; + } + } + return i; +} + +/* + * 'copy_string32()' copies argument/envelope strings from user + * memory to free pages in kernel mem. These are in a format ready + * to be put directly into the top of new user memory. + */ +static int copy_strings32(int argc, u32 * argv, struct linux_binprm *bprm) +{ + while (argc-- > 0) { + u32 str; + int len; + unsigned long pos; + + if (get_user(str, argv + argc) || + !str || + !(len = strnlen_user((char *)A(str), bprm->p))) + return -EFAULT; + + if (bprm->p < len) + return -E2BIG; + + bprm->p -= len; + + pos = bprm->p; + while (len) { + char *kaddr; + struct page *page; + int offset, bytes_to_copy, new, err; + + offset = pos % PAGE_SIZE; + page = bprm->page[pos / PAGE_SIZE]; + new = 0; + if (!page) { + page = alloc_page(GFP_USER); + bprm->page[pos / PAGE_SIZE] = page; + if (!page) + return -ENOMEM; + new = 1; + } + kaddr = (char *)kmap(page); + + if (new && offset) + memset(kaddr, 0, offset); + bytes_to_copy = PAGE_SIZE - offset; + if (bytes_to_copy > len) { + bytes_to_copy = len; + if (new) + memset(kaddr+offset+len, 0, + PAGE_SIZE-offset-len); + } + + err = copy_from_user(kaddr + offset, (char *)A(str), + bytes_to_copy); + flush_page_to_ram(page); + kunmap((unsigned long)kaddr); + + if (err) + return -EFAULT; + + pos += bytes_to_copy; + str += bytes_to_copy; + len -= bytes_to_copy; + } + } + return 0; +} + +/* + * sys32_execve() executes a new program. + */ +static int do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) +{ + struct linux_binprm bprm; + struct file * file; + int retval; + int i; + + bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); + memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0])); + + file = open_exec(filename); + + retval = PTR_ERR(file); + if (IS_ERR(file)) + return retval; + + bprm.file = file; + bprm.filename = filename; + bprm.sh_bang = 0; + bprm.loader = 0; + bprm.exec = 0; + if ((bprm.argc = count32(argv, bprm.p / sizeof(u32))) < 0) { + allow_write_access(file); + fput(file); + return bprm.argc; + } + if ((bprm.envc = count32(envp, bprm.p / sizeof(u32))) < 0) { + allow_write_access(file); + fput(file); + return bprm.argc; + } + + retval = prepare_binprm(&bprm); + if (retval < 0) + goto out; + + retval = copy_strings_kernel(1, &bprm.filename, &bprm); + if (retval < 0) + goto out; + + bprm.exec = bprm.p; + retval = copy_strings32(bprm.envc, envp, &bprm); + if (retval < 0) + goto out; + + retval = copy_strings32(bprm.argc, argv, &bprm); + if (retval < 0) + goto out; + + retval = search_binary_handler(&bprm, regs); + if (retval >= 0) + /* execve success */ + return retval; + +out: + /* Something went wrong, return the inode and free the argument pages*/ + allow_write_access(bprm.file); + if (bprm.file) + fput(bprm.file); + + for (i=0 ; ipid, current->comm); + //PPCDBG(PPCDBG_SYS32NI, " a0=%lx, a1=%lx, a2=%lx, a3=%lx, a4=%lx, a5=%lx, regs=%p \n", a0, a1, a2, a3, a4, a5, regs); + } + + filename = getname((char *) a0); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + if (regs->msr & MSR_FP) + giveup_fpu(current); + + error = do_execve32(filename, (u32*) a1, (u32*) a2, regs); + + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); + +out: + ifppcdebug(PPCDBG_SYS32) { + udbg_printf("sys32_execve - exited - returning %x - pid=%ld \n", error, current->pid); + //udbg_printf("sys32_execve - at exit - regs->gpr[1]=%lx, gpr[3]=%lx, gpr[4]=%lx, gpr[5]=%lx, gpr[6]=%lx \n", regs->gpr[1], regs->gpr[3], regs->gpr[4], regs->gpr[5], regs->gpr[6]); + } + return error; +} + +/* Set up a thread for executing a new program. */ +void start_thread32(struct pt_regs* regs, unsigned long nip, unsigned long sp) +{ + set_fs(USER_DS); + memset(regs->gpr, 0, sizeof(regs->gpr)); + memset(®s->ctr, 0, 4 * sizeof(regs->ctr)); + regs->nip = nip; + regs->gpr[1] = sp; + regs->msr = MSR_USER32; + if (last_task_used_math == current) + last_task_used_math = 0; + current->thread.fpscr = 0; +} + +extern asmlinkage int sys_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5); + +/* Note: it is necessary to treat option as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_prctl(u32 option, u32 arg2, u32 arg3, u32 arg4, u32 arg5) +{ + PPCDBG(PPCDBG_SYS32, "sys32_prctl - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_prctl((int)option, + (unsigned long) arg2, + (unsigned long) arg3, + (unsigned long) arg4, + (unsigned long) arg5); +} + +extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, struct timespec *interval); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_sched_rr_get_interval(u32 pid, struct timespec32 *interval) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32, "sys32_sched_rr_get_interval - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_sched_rr_get_interval((int)pid, &t); + set_fs (old_fs); + if (put_user (t.tv_sec, &interval->tv_sec) || + __put_user (t.tv_nsec, &interval->tv_nsec)) + return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sched_rr_get_interval - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + +extern asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn, unsigned long off, + unsigned long len, unsigned char *buf); + +asmlinkage int sys32_pciconfig_read(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_pciconfig_read - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_pciconfig_read((unsigned long) bus, + (unsigned long) dfn, + (unsigned long) off, + (unsigned long) len, + (unsigned char *)AA(ubuf)); +} + + + + +extern asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn, unsigned long off, + unsigned long len, unsigned char *buf); + +asmlinkage int sys32_pciconfig_write(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_pciconfig_write - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return sys_pciconfig_write((unsigned long) bus, + (unsigned long) dfn, + (unsigned long) off, + (unsigned long) len, + (unsigned char *)AA(ubuf)); +} + +extern asmlinkage int sys_newuname(struct new_utsname * name); + +asmlinkage int ppc64_newuname(struct new_utsname * name) +{ + int errno = sys_newuname(name); + + if (current->personality == PER_LINUX32 && !errno) { + if(copy_to_user(name->machine, "ppc\0\0", 8)) { + errno = -EFAULT; + } + } + return errno; +} + +extern asmlinkage long sys_personality(unsigned long); + +asmlinkage int sys32_personality(unsigned long personality) +{ + int ret; + if (current->personality == PER_LINUX32 && personality == PER_LINUX) + personality = PER_LINUX32; + ret = sys_personality(personality); + if (ret == PER_LINUX32) + ret = PER_LINUX; + return ret; +} + + + +extern asmlinkage long sys_access(const char * filename, int mode); + +/* Note: it is necessary to treat mode as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_access(const char * filename, u32 mode) +{ + return sys_access(filename, (int)mode); +} + + +extern asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs); + +/* Note: it is necessary to treat p1, p2, p3, p4, p5, p7, and regs as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_clone(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, struct pt_regs *regs) +{ + return sys_clone((int)p1, (int)p2, (int)p3, (int)p4, (int)p5, (int)p6, regs); +} + + +extern asmlinkage long sys_creat(const char * pathname, int mode); + +/* Note: it is necessary to treat mode as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_creat(const char * pathname, u32 mode) +{ + return sys_creat(pathname, (int)mode); +} + + +extern asmlinkage long sys_exit(int error_code); + +/* Note: it is necessary to treat error_code as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_exit(u32 error_code) +{ + return sys_exit((int)error_code); +} + + +extern asmlinkage long sys_wait4(pid_t pid, unsigned int * stat_addr, int options, struct rusage * ru); + +/* Note: it is necessary to treat pid and options as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_wait4(u32 pid, unsigned int * stat_addr, u32 options, struct rusage * ru) +{ + PPCDBG(PPCDBG_SYS32, "sys32_wait4 - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + if (!ru) + return sys_wait4((int)pid, stat_addr, options, NULL); + else { + struct rusage r; + int ret; + unsigned int status; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_wait4((int)pid, stat_addr ? &status : NULL, options, &r); + set_fs (old_fs); + if (put_rusage ((struct rusage32 *)ru, &r)) return -EFAULT; + if (stat_addr && put_user (status, stat_addr)) + return -EFAULT; + return ret; + } + +} + + +extern asmlinkage long sys_waitpid(pid_t pid, unsigned int * stat_addr, int options); + +/* Note: it is necessary to treat pid and options as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_waitpid(u32 pid, unsigned int * stat_addr, u32 options) +{ + return sys_waitpid((int)pid, stat_addr, (int)options); +} + + +extern asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs); + +/* Note: it is necessary to treat p1, p2, p3, p4, p5, and p6 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_fork(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, struct pt_regs *regs) +{ + return sys_fork((int)p1, (int)p2, (int)p3, (int)p4, (int)p5, (int)p6, regs); +} + + +extern asmlinkage long sys_getgroups(int gidsetsize, gid_t *grouplist); + +/* Note: it is necessary to treat gidsetsize as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getgroups(u32 gidsetsize, gid_t *grouplist) +{ + return sys_getgroups((int)gidsetsize, grouplist); +} + + +extern asmlinkage long sys_getpgid(pid_t pid); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getpgid(u32 pid) +{ + return sys_getpgid((int)pid); +} + + +extern asmlinkage long sys_getpriority(int which, int who); + +/* Note: it is necessary to treat which and who as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getpriority(u32 which, u32 who) +{ + return sys_getpriority((int)which, (int)who); +} + + +extern asmlinkage long sys_getsid(pid_t pid); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getsid(u32 pid) +{ + return sys_getsid((int)pid); +} + + +extern asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); + +/* Note: it is necessary to treat on as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_ioperm(unsigned long from, unsigned long num, u32 on) +{ + return sys_ioperm(from, num, (int)on); +} + + +extern asmlinkage int sys_iopl(int a1, int a2, int a3, int a4); + +/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_iopl(u32 a1, u32 a2, u32 a3, u32 a4) +{ + return sys_iopl((int)a1, (int)a2, (int)a3, (int)a4); +} + + +extern asmlinkage long sys_kill(int pid, int sig); + +/* Note: it is necessary to treat pid and sig as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_kill(u32 pid, u32 sig) +{ + return sys_kill((int)pid, (int)sig); +} + + +extern asmlinkage long sys_mkdir(const char * pathname, int mode); + +/* Note: it is necessary to treat mode as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_mkdir(const char * pathname, u32 mode) +{ + return sys_mkdir(pathname, (int)mode); +} + + +extern asmlinkage long sys_mlockall(int flags); + +/* Note: it is necessary to treat flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_mlockall(u32 flags) +{ + return sys_mlockall((int)flags); +} + + +extern asmlinkage int sys_modify_ldt(int a1, int a2, int a3, int a4); + +/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_modify_ldt(u32 a1, u32 a2, u32 a3, u32 a4) +{ + return sys_modify_ldt((int)a1, (int)a2, (int)a3, (int)a4); +} + + +extern asmlinkage long sys_msync(unsigned long start, size_t len, int flags); + +/* Note: it is necessary to treat flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_msync(unsigned long start, size_t len, u32 flags) +{ + return sys_msync(start, len, (int)flags); +} + + +extern asmlinkage long sys_nice(int increment); + +/* Note: it is necessary to treat increment as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_nice(u32 increment) +{ + return sys_nice((int)increment); +} + + +extern asmlinkage long sys_open(const char * filename, int flags, int mode); + +/* Note: it is necessary to treat flags and mode as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_open(const char * filename, int flags, int mode) +{ + return sys_open(filename, (int)flags, (int)mode); +} + + +extern asmlinkage long sys_readlink(const char * path, char * buf, int bufsiz); + +/* Note: it is necessary to treat bufsiz as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_readlink(const char * path, char * buf, u32 bufsiz) +{ + return sys_readlink(path, buf, (int)bufsiz); +} + + +extern asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void * arg); + +/* Note: it is necessary to treat magic1 and magic2 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_reboot(u32 magic1, u32 magic2, unsigned int cmd, void * arg) +{ + return sys_reboot((int)magic1, (int)magic2, cmd, arg); +} + + +extern asmlinkage long sys_sched_get_priority_max(int policy); + +/* Note: it is necessary to treat option as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_get_priority_max(u32 policy) +{ + return sys_sched_get_priority_max((int)policy); +} + + +extern asmlinkage long sys_sched_get_priority_min(int policy); + +/* Note: it is necessary to treat policy as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_get_priority_min(u32 policy) +{ + return sys_sched_get_priority_min((int)policy); +} + + +extern asmlinkage long sys_sched_getparam(pid_t pid, struct sched_param *param); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_getparam(u32 pid, struct sched_param *param) +{ + return sys_sched_getparam((int)pid, param); +} + + +extern asmlinkage long sys_sched_getscheduler(pid_t pid); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_getscheduler(u32 pid) +{ + return sys_sched_getscheduler((int)pid); +} + + +extern asmlinkage long sys_sched_setparam(pid_t pid, struct sched_param *param); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_setparam(u32 pid, struct sched_param *param) +{ + return sys_sched_setparam((int)pid, param); +} + + +extern asmlinkage long sys_sched_setscheduler(pid_t pid, int policy, struct sched_param *param); + +/* Note: it is necessary to treat pid and policy as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_setscheduler(u32 pid, u32 policy, struct sched_param *param) +{ + return sys_sched_setscheduler((int)pid, (int)policy, param); +} + + +extern asmlinkage long sys_setdomainname(char *name, int len); + +/* Note: it is necessary to treat len as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setdomainname(char *name, u32 len) +{ + return sys_setdomainname(name, (int)len); +} + + +extern asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist); + +/* Note: it is necessary to treat gidsetsize as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setgroups(u32 gidsetsize, gid_t *grouplist) +{ + return sys_setgroups((int)gidsetsize, grouplist); +} + + +extern asmlinkage long sys_sethostname(char *name, int len); + +/* Note: it is necessary to treat len as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sethostname(char *name, u32 len) +{ + return sys_sethostname(name, (int)len); +} + + +extern asmlinkage long sys_setpgid(pid_t pid, pid_t pgid); + +/* Note: it is necessary to treat pid and pgid as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setpgid(u32 pid, u32 pgid) +{ + return sys_setpgid((int)pid, (int)pgid); +} + + +extern asmlinkage long sys_setpriority(int which, int who, int niceval); + +/* Note: it is necessary to treat which, who, and niceval as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setpriority(u32 which, u32 who, u32 niceval) +{ + return sys_setpriority((int)which, (int)who, (int)niceval); +} + + +extern asmlinkage long sys_ssetmask(int newmask); + +/* Note: it is necessary to treat newmask as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_ssetmask(u32 newmask) +{ + return sys_ssetmask((int) newmask); +} + + +extern asmlinkage long sys_swapon(const char * specialfile, int swap_flags); + +/* Note: it is necessary to treat swap_flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_swapon(const char * specialfile, u32 swap_flags) +{ + return sys_swapon(specialfile, (int)swap_flags); +} + + +extern asmlinkage long sys_syslog(int type, char * buf, int len); + +/* Note: it is necessary to treat type and len as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_syslog(u32 type, char * buf, u32 len) +{ + return sys_syslog((int)type, buf, (int)len); +} + + +extern asmlinkage long sys_umask(int mask); + +/* Note: it is necessary to treat mask as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_umask(u32 mask) +{ + return sys_umask((int)mask); +} + + +extern asmlinkage long sys_umount(char * name, int flags); + +/* Note: it is necessary to treat flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_umount(char * name, u32 flags) +{ + return sys_umount(name, (int)flags); +} + + +extern asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs); + +/* Note: it is necessary to treat p1, p2, p3, p4, p5, and p6 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_vfork(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, struct pt_regs *regs) +{ + return sys_vfork((int)p1, (int)p2, (int)p3, (int)p4, (int)p5, (int)p6, regs); +} + + +extern asmlinkage int sys_vm86(int a1, int a2, int a3, int a4); + +/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_vm86(u32 a1, u32 a2, u32 a3, u32 a4) +{ + return sys_vm86((int)a1, (int)a2, (int)a3, (int)a4); +} + + + + + +extern asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, + size_t count, loff_t pos); + +extern asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, + size_t count, loff_t pos); + +typedef __kernel_ssize_t32 ssize_t32; + +asmlinkage ssize_t32 sys32_pread(unsigned int fd, char *ubuf, + __kernel_size_t32 count, u32 reg6, u32 poshi, u32 poslo) +{ + return sys_pread(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + +asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, char *ubuf, + __kernel_size_t32 count, u32 reg6 ,u32 poshi, u32 poslo) +{ + return sys_pwrite(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + + +extern asmlinkage long sys_truncate(const char * path, unsigned long length); +extern asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length); + +asmlinkage int sys32_truncate64(const char * path, u32 reg4, unsigned long high, unsigned long low) +{ + if ((int)high < 0) + return -EINVAL; + else + return sys_truncate(path, (high << 32) | low); +} + +asmlinkage int sys32_ftruncate64(unsigned int fd, u32 reg4, unsigned long high, unsigned long low) +{ + if ((int)high < 0) + return -EINVAL; + else + return sys_ftruncate(fd, (high << 32) | low); +} + + + +asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + if (cmd >= F_GETLK64 && cmd <= F_SETLKW64) + return sys_fcntl(fd, cmd + F_GETLK - F_GETLK64, arg); + return sys32_fcntl(fd, cmd, arg); +} + + + + +struct __sysctl_args32 { + u32 name; + int nlen; + u32 oldval; + u32 oldlenp; + u32 newval; + u32 newlen; + u32 __unused[4]; +}; + +extern asmlinkage long sys32_sysctl(struct __sysctl_args32 *args) +{ + struct __sysctl_args32 tmp; + int error; + size_t oldlen, *oldlenp = NULL; + unsigned long addr = (((long)&args->__unused[0]) + 7) & ~7; + + if (copy_from_user(&tmp, args, sizeof(tmp))) + return -EFAULT; + + if (tmp.oldval && tmp.oldlenp) { + /* Duh, this is ugly and might not work if sysctl_args + is in read-only memory, but do_sysctl does indirectly + a lot of uaccess in both directions and we'd have to + basically copy the whole sysctl.c here, and + glibc's __sysctl uses rw memory for the structure + anyway. */ + if (get_user(oldlen, (u32 *)A(tmp.oldlenp)) || + put_user(oldlen, (size_t *)addr)) + return -EFAULT; + oldlenp = (size_t *)addr; + } + + lock_kernel(); + error = do_sysctl((int *)A(tmp.name), tmp.nlen, (void *)A(tmp.oldval), + oldlenp, (void *)A(tmp.newval), tmp.newlen); + unlock_kernel(); + if (oldlenp) { + if (!error) { + if (get_user(oldlen, (size_t *)addr) || + put_user(oldlen, (u32 *)A(tmp.oldlenp))) + error = -EFAULT; + } + copy_to_user(args->__unused, tmp.__unused, sizeof(tmp.__unused)); + } + return error; +} + +asmlinkage long sys32_time(__kernel_time_t32* tloc) +{ + __kernel_time_t32 secs; + + struct timeval tv; + + do_gettimeofday( &tv ); + secs = tv.tv_sec; + + if (tloc) { + if (put_user(secs,tloc)) + secs = -EFAULT; + } + + return secs; +} diff -Nru a/arch/ppc64/kernel/syscalls.c b/arch/ppc64/kernel/syscalls.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/syscalls.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,316 @@ +/* + * linux/arch/ppc/kernel/sys_ppc.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/sys_i386.c" + * Adapted from the i386 version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au). + * + * This file contains various random system calls that + * have a non-standard calling sequence on the Linux/PPC + * platform. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +extern unsigned long wall_jiffies; +#define USEC_PER_SEC (1000000) + +void +check_bugs(void) +{ +} + +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) +{ + printk(KERN_ERR "sys_ioperm()\n"); + return -EIO; +} + +int sys_iopl(int a1, int a2, int a3, int a4) +{ + printk(KERN_ERR "sys_iopl(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + return (-ENOSYS); +} + +int sys_vm86(int a1, int a2, int a3, int a4) +{ + printk(KERN_ERR "sys_vm86(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + return (-ENOSYS); +} + +int sys_modify_ldt(int a1, int a2, int a3, int a4) +{ + printk(KERN_ERR "sys_modify_ldt(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + return (-ENOSYS); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage int +sys_ipc (uint call, int first, int second, long third, void *ptr, long fifth) +{ + int version, ret; + + PPCDBG(PPCDBG_SYS64X, "sys_ipc - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + ret = -EINVAL; + switch (call) { + case SEMOP: + ret = sys_semop (first, (struct sembuf *)ptr, second); + break; + case SEMGET: + ret = sys_semget (first, second, third); + break; + case SEMCTL: { + union semun fourth; + + if (!ptr) + break; + if ((ret = verify_area (VERIFY_READ, ptr, sizeof(long))) + || (ret = get_user(fourth.__pad, (void **)ptr))) + break; + ret = sys_semctl (first, second, third, fourth); + break; + } + case MSGSND: + ret = sys_msgsnd (first, (struct msgbuf *) ptr, second, third); + break; + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + + if (!ptr) + break; + if ((ret = verify_area (VERIFY_READ, ptr, sizeof(tmp))) + || (ret = copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp)))) + break; + ret = sys_msgrcv (first, (struct msgbuf *)(unsigned long)tmp.msgp, + second, tmp.msgtyp, third); + break; + } + default: + ret = sys_msgrcv (first, (struct msgbuf *) ptr, + second, fifth, third); + break; + } + break; + case MSGGET: + ret = sys_msgget ((key_t) first, second); + break; + case MSGCTL: + ret = sys_msgctl (first, second, (struct msqid_ds *) ptr); + break; + case SHMAT: + switch (version) { + default: { + ulong raddr; + + if ((ret = verify_area(VERIFY_WRITE, (ulong*) third, + sizeof(ulong)))) + break; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + break; + ret = put_user (raddr, (ulong *) third); + break; + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + break; + ret = sys_shmat (first, (char *) ptr, second, + (ulong *) third); + break; + } + break; + case SHMDT: + ret = sys_shmdt ((char *)ptr); + break; + case SHMGET: + ret = sys_shmget (first, second, third); + break; + case SHMCTL: + ret = sys_shmctl (first, second, (struct shmid_ds *) ptr); + break; + } + + PPCDBG(PPCDBG_SYS64X, "sys_ipc - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way unix traditionally does this, though. + */ +asmlinkage int sys_pipe(int *fildes) +{ + int fd[2]; + int error; + + PPCDBG(PPCDBG_SYS64X, "sys_pipe - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + + PPCDBG(PPCDBG_SYS64X, "sys_pipe - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return error; +} + +asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, off_t offset) +{ + struct file * file = NULL; + unsigned long ret = -EBADF; + + PPCDBG(PPCDBG_SYS64X, "sys_mmap - entered - addr=%lx, len=%lx - pid=%ld, comm=%s \n", addr, len, current->pid, current->comm); + + if (!(flags & MAP_ANONYMOUS)) { + if (!(file = fget(fd))) + goto out; + } + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + down_write(¤t->mm->mmap_sem); + ret = do_mmap(file, addr, len, prot, flags, offset); + up_write(¤t->mm->mmap_sem); + if (file) + fput(file); + +out: + + PPCDBG(PPCDBG_SYS64X, "sys_mmap - exited - ret=%x \n", ret); + + return ret; +} + +asmlinkage int sys_pause(void) +{ + + PPCDBG(PPCDBG_SYS64X, "sys_pause - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + current->state = TASK_INTERRUPTIBLE; + schedule(); + + PPCDBG(PPCDBG_SYS64X, "sys_pause - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return -ERESTARTNOHAND; +} + +static int __init set_fakeppc(char *str) +{ + if (*str) + return 0; + init_task.personality = PER_LINUX32; + return 1; +} +__setup("fakeppc", set_fakeppc); + +asmlinkage int sys_uname(struct old_utsname * name) +{ + int err = -EFAULT; + + PPCDBG(PPCDBG_SYS64X, "sys_uname - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + down_read(&uts_sem); + if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) + err = 0; + up_read(&uts_sem); + + PPCDBG(PPCDBG_SYS64X, "sys_uname - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return err; +} + +asmlinkage int sys_olduname(struct oldold_utsname * name) +{ + int error; + + PPCDBG(PPCDBG_SYS64X, "sys_olduname - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down_read(&uts_sem); + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + error -= __put_user(0,name->sysname+__OLD_UTS_LEN); + error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error -= __put_user(0,name->nodename+__OLD_UTS_LEN); + error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error -= __put_user(0,name->release+__OLD_UTS_LEN); + error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error -= __put_user(0,name->version+__OLD_UTS_LEN); + error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error = __put_user(0,name->machine+__OLD_UTS_LEN); + up_read(&uts_sem); + + error = error ? -EFAULT : 0; + + PPCDBG(PPCDBG_SYS64X, "sys_olduname - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return error; +} + +asmlinkage time_t sys64_time(time_t* tloc) +{ + time_t secs; + time_t usecs; + + long tb_delta = tb_ticks_since(tb_last_stamp); + tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy; + + secs = xtime.tv_sec; + usecs = xtime.tv_usec + tb_delta / tb_ticks_per_usec; + while (usecs >= USEC_PER_SEC) { + ++secs; + usecs -= USEC_PER_SEC; + } + + if (tloc) { + if (put_user(secs,tloc)) + secs = -EFAULT; + } + + return secs; +} diff -Nru a/arch/ppc64/kernel/time.c b/arch/ppc64/kernel/time.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/time.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,664 @@ +/* + * + * Common time routines among all ppc machines. + * + * Written by Cort Dougan (cort@cs.nmt.edu) to merge + * Paul Mackerras' version and mine for PReP and Pmac. + * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net). + * Converted for 64-bit by Mike Corrigan (mikejc@us.ibm.com) + * + * First round of bugfixes by Gabriel Paubert (paubert@iram.es) + * to make clock more stable (2.4.0-test5). The only thing + * that this code assumes is that the timebases have been synchronized + * by firmware on SMP and are never stopped (never do sleep + * on SMP then, nap and doze are OK). + * + * Speeded up do_gettimeofday by getting rid of references to + * xtime (which required locks for consistency). (mikejc@us.ibm.com) + * + * TODO (not necessarily in this file): + * - improve precision and reproducibility of timebase frequency + * measurement at boot time. (for iSeries, we calibrate the timebase + * against the Titan chip's clock.) + * - for astronomical applications: add a new function to get + * non ambiguous timestamps even around leap seconds. This needs + * a new timestamp format and a good name. + * + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PPC_ISERIES +#include +#endif +#include + +#include +#include + +void smp_local_timer_interrupt(struct pt_regs *); + +/* keep track of when we need to update the rtc */ +time_t last_rtc_update; +extern rwlock_t xtime_lock; +extern int piranha_simulator; +#ifdef CONFIG_PPC_ISERIES +unsigned long iSeries_recal_titan = 0; +unsigned long iSeries_recal_tb = 0; +static unsigned long first_settimeofday = 1; +#endif + +#define XSEC_PER_SEC (1024*1024) +#define USEC_PER_SEC (1000000) + +unsigned long tb_ticks_per_jiffy; +unsigned long tb_ticks_per_usec; +unsigned long tb_ticks_per_sec; +unsigned long next_xtime_sync_tb; +unsigned long xtime_sync_interval; +unsigned long tb_to_xs; +unsigned tb_to_us; +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; + +struct gettimeofday_struct do_gtod; + +extern unsigned long wall_jiffies; +extern unsigned long lpEvent_count; +extern int smp_tb_synchronized; + +extern unsigned long prof_cpu_mask; +extern unsigned int * prof_buffer; +extern unsigned long prof_len; +extern unsigned long prof_shift; +extern char _stext; + +static inline void ppc_do_profile (unsigned long nip) +{ + if (!prof_buffer) + return; + + /* + * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. + * (default is all CPUs.) + */ + if (!((1<>= prof_shift; + /* + * Don't ignore out-of-bounds EIP values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (nip > prof_len-1) + nip = prof_len-1; + atomic_inc((atomic_t *)&prof_buffer[nip]); +} + + +static __inline__ void timer_check_rtc(void) +{ + /* + * update the rtc when needed, this should be performed on the + * right fraction of a second. Half or full second ? + * Full second works on mk48t59 clocks, others need testing. + * Note that this update is basically only used through + * the adjtimex system calls. Setting the HW clock in + * any other way is a /dev/rtc and userland business. + * This is still wrong by -0.5/+1.5 jiffies because of the + * timer interrupt resolution and possible delay, but here we + * hit a quantization limit which can only be solved by higher + * resolution timers and decoupling time management from timer + * interrupts. This is also wrong on the clocks + * which require being written at the half second boundary. + * We should have an rtc call that only sets the minutes and + * seconds like on Intel to avoid problems with non UTC clocks. + */ + if ( (time_status & STA_UNSYNC) == 0 && + xtime.tv_sec - last_rtc_update >= 659 && + abs(xtime.tv_usec - (1000000-1000000/HZ)) < 500000/HZ && + jiffies - wall_jiffies == 1) { + struct rtc_time tm; + to_tm(xtime.tv_sec+1, &tm); + tm.tm_year -= 1900; + tm.tm_mon -= 1; + if (ppc_md.set_rtc_time(&tm) == 0) + last_rtc_update = xtime.tv_sec+1; + else + /* Try again one minute later */ + last_rtc_update += 60; + } +} + +/* Synchronize xtime with do_gettimeofday */ + +static __inline__ void timer_sync_xtime( unsigned long cur_tb ) +{ + struct timeval my_tv; + + if ( cur_tb > next_xtime_sync_tb ) { + next_xtime_sync_tb = cur_tb + xtime_sync_interval; + do_gettimeofday( &my_tv ); + if ( xtime.tv_sec <= my_tv.tv_sec ) { + xtime.tv_sec = my_tv.tv_sec; + xtime.tv_usec = my_tv.tv_usec; + } + } +} + +#ifdef CONFIG_PPC_ISERIES + +/* + * This function recalibrates the timebase based on the 49-bit time-of-day value in the Titan chip. + * The Titan is much more accurate than the value returned by the service processor for the + * timebase frequency. + */ + +static void iSeries_tb_recal(void) +{ + struct div_result divres; + unsigned long titan, tb; + tb = get_tb(); + titan = HvCallXm_loadTod(); + if ( iSeries_recal_titan ) { + unsigned long tb_ticks = tb - iSeries_recal_tb; + unsigned long titan_usec = (titan - iSeries_recal_titan) >> 12; + unsigned long new_tb_ticks_per_sec = (tb_ticks * USEC_PER_SEC)/titan_usec; + unsigned long new_tb_ticks_per_jiffy = (new_tb_ticks_per_sec+(HZ/2))/HZ; + long tick_diff = new_tb_ticks_per_jiffy - tb_ticks_per_jiffy; + char sign = '+'; + /* make sure tb_ticks_per_sec and tb_ticks_per_jiffy are consistent */ + new_tb_ticks_per_sec = new_tb_ticks_per_jiffy * HZ; + + if ( tick_diff < 0 ) { + tick_diff = -tick_diff; + sign = '-'; + } + if ( tick_diff ) { + if ( tick_diff < tb_ticks_per_jiffy/25 ) { + printk( "Titan recalibrate: new tb_ticks_per_jiffy = %lu (%c%ld)\n", + new_tb_ticks_per_jiffy, sign, tick_diff ); + tb_ticks_per_jiffy = new_tb_ticks_per_jiffy; + tb_ticks_per_sec = new_tb_ticks_per_sec; + div128_by_32( XSEC_PER_SEC, 0, tb_ticks_per_sec, &divres ); + do_gtod.tb_ticks_per_sec = tb_ticks_per_sec; + tb_to_xs = divres.result_low; + do_gtod.varp->tb_to_xs = tb_to_xs; + } + else { + printk( "Titan recalibrate: FAILED (difference > 4 percent)\n" + " new tb_ticks_per_jiffy = %lu\n" + " old tb_ticks_per_jiffy = %lu\n", + new_tb_ticks_per_jiffy, tb_ticks_per_jiffy ); + } + } + } + iSeries_recal_titan = titan; + iSeries_recal_tb = tb; +} +#endif + +/* + * For iSeries shared processors, we have to let the hypervisor + * set the hardware decrementer. We set a virtual decrementer + * in the ItLpPaca and call the hypervisor if the virtual + * decrementer is less than the current value in the hardware + * decrementer. (almost always the new decrementer value will + * be greater than the current hardware decementer so the hypervisor + * call will not be needed) + */ + +unsigned long tb_last_stamp=0; + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + */ +int timer_interrupt(struct pt_regs * regs) +{ + int next_dec; + unsigned long cur_tb; + struct Paca * paca = (struct Paca *)mfspr(SPRG3); + unsigned long cpu = paca->xPacaIndex; + struct ItLpQueue * lpq; + + irq_enter(cpu); + +#ifndef CONFIG_PPC_ISERIES + if (!user_mode(regs)) + ppc_do_profile(instruction_pointer(regs)); +#endif + + paca->xLpPaca.xIntDword.xFields.xDecrInt = 0; + + while (paca->next_jiffy_update_tb <= (cur_tb = get_tb())) { + +#ifdef CONFIG_SMP + smp_local_timer_interrupt(regs); +#endif + if (cpu == 0) { + write_lock(&xtime_lock); + tb_last_stamp = paca->next_jiffy_update_tb; + do_timer(regs); + timer_sync_xtime( cur_tb ); + timer_check_rtc(); + write_unlock(&xtime_lock); + } + paca->next_jiffy_update_tb += tb_ticks_per_jiffy; + } + + next_dec = paca->next_jiffy_update_tb - cur_tb; + if (next_dec > paca->default_decr) + next_dec = paca->default_decr; + set_dec(next_dec); + + lpq = paca->lpQueuePtr; + if (lpq && ItLpQueue_isLpIntPending(lpq)) + lpEvent_count += ItLpQueue_process(lpq, regs); + + irq_exit(cpu); + + if (softirq_pending(cpu)) + do_softirq(); + + return 1; +} + + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long sec, usec, tb_ticks; + unsigned long xsec, tb_xsec; + struct gettimeofday_vars * temp_varp; + unsigned long temp_tb_to_xs, temp_stamp_xsec; + + /* These calculations are faster (gets rid of divides) + * if done in units of 1/2^20 rather than microseconds. + * The conversion to microseconds at the end is done + * without a divide (and in fact, without a multiply) */ + tb_ticks = get_tb() - do_gtod.tb_orig_stamp; + temp_varp = do_gtod.varp; + temp_tb_to_xs = temp_varp->tb_to_xs; + temp_stamp_xsec = temp_varp->stamp_xsec; + tb_xsec = mulhdu( tb_ticks, temp_tb_to_xs ); + xsec = temp_stamp_xsec + tb_xsec; + sec = xsec / XSEC_PER_SEC; + xsec -= sec * XSEC_PER_SEC; + usec = (xsec * USEC_PER_SEC)/XSEC_PER_SEC; + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +void do_settimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long delta_xsec; + long int tb_delta, new_usec, new_sec; + unsigned long new_xsec; + + write_lock_irqsave(&xtime_lock, flags); + /* Updating the RTC is not the job of this code. If the time is + * stepped under NTP, the RTC will be update after STA_UNSYNC + * is cleared. Tool like clock/hwclock either copy the RTC + * to the system time, in which case there is no point in writing + * to the RTC again, or write to the RTC but then they don't call + * settimeofday to perform this operation. + */ +#ifdef CONFIG_PPC_ISERIES + if ( first_settimeofday ) { + iSeries_tb_recal(); + first_settimeofday = 0; + } +#endif + tb_delta = tb_ticks_since(tb_last_stamp); + tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy; + + new_sec = tv->tv_sec; + new_usec = tv->tv_usec - tb_delta / tb_ticks_per_usec; + while (new_usec <0) { + new_sec--; + new_usec += USEC_PER_SEC; + } + xtime.tv_usec = new_usec; + xtime.tv_sec = new_sec; + + /* 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. + */ + last_rtc_update = new_sec - 658; + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + + delta_xsec = mulhdu( (tb_last_stamp-do_gtod.tb_orig_stamp), do_gtod.varp->tb_to_xs ); + new_xsec = (new_usec * XSEC_PER_SEC) / USEC_PER_SEC; + new_xsec += new_sec * XSEC_PER_SEC; + if ( new_xsec > delta_xsec ) { + do_gtod.varp->stamp_xsec = new_xsec - delta_xsec; + } + else { + /* This is only for the case where the user is setting the time + * way back to a time such that the boot time would have been + * before 1970 ... eg. we booted ten days ago, and we are setting + * the time to Jan 5, 1970 */ + do_gtod.varp->stamp_xsec = new_xsec; + do_gtod.tb_orig_stamp = tb_last_stamp; + } + + write_unlock_irqrestore(&xtime_lock, flags); +} + +/* + * This function is a copy of the architecture independent function + * but which calls do_settimeofday rather than setting the xtime + * fields itself. This way, the fields which are used for + * do_settimeofday get updated too. + */ + +long ppc64_sys_stime(int * tptr) +{ + int value; + struct timeval myTimeval; + + PPCDBG(PPCDBG_SYS32, "ppc64_sys_stime - entered - tptr=%p, *tptr=0x%x \n", tptr, *tptr); + + if (!capable(CAP_SYS_TIME)) + return -EPERM; + + if (get_user(value, tptr)) + return -EFAULT; + + myTimeval.tv_sec = value; + myTimeval.tv_usec = 0; + + do_settimeofday(&myTimeval); + + PPCDBG(PPCDBG_SYS32, "ppc64_sys_stime - exiting w/ 0 \n"); + return 0; +} + +void __init time_init(void) +{ + /* This function is only called on the boot processor */ + unsigned long flags; + struct rtc_time tm; + + ppc_md.calibrate_decr(); + + if ( ! piranha_simulator ) { + ppc_md.get_boot_time(&tm); + } + write_lock_irqsave(&xtime_lock, flags); + xtime.tv_sec = mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + tb_last_stamp = get_tb(); + do_gtod.tb_orig_stamp = tb_last_stamp; + do_gtod.varp = &do_gtod.vars[0]; + do_gtod.varp->stamp_xsec = xtime.tv_sec * XSEC_PER_SEC; + do_gtod.tb_ticks_per_sec = tb_ticks_per_sec; + do_gtod.varp->tb_to_xs = tb_to_xs; + do_gtod.tb_to_us = tb_to_us; + + xtime_sync_interval = tb_ticks_per_sec - (tb_ticks_per_sec/8); + next_xtime_sync_tb = tb_last_stamp + xtime_sync_interval; + + xtime.tv_usec = 0; + last_rtc_update = xtime.tv_sec; + write_unlock_irqrestore(&xtime_lock, flags); + +#ifdef CONFIG_PPC_ISERIES + /* HACK HACK This allows the iSeries profiling to use /proc/profile */ + prof_shift = 0; +#endif + + /* Not exact, but the timer interrupt takes care of this */ + set_dec(tb_ticks_per_jiffy); +} + +/* + * After adjtimex is called, adjust the conversion of tb ticks + * to microseconds to keep do_gettimeofday synchronized + * with ntpd. + + * Use the time_freq and time_offset computed by adjtimex to + * adjust the frequency. +*/ + +void ppc_adjtimex(void) +{ + unsigned long den, new_tb_ticks_per_sec, tb_ticks, old_xsec, new_tb_to_xs, new_xsec, new_stamp_xsec; + unsigned long tb_ticks_per_sec_delta; + long delta_freq, ltemp; + struct div_result divres; + unsigned long flags; + struct gettimeofday_vars * temp_varp; + + if ( time_offset < 0 ) { + ltemp = -time_offset; + ltemp <<= SHIFT_USEC - SHIFT_UPDATE; + ltemp >>= SHIFT_KG + time_constant; + ltemp = -ltemp; + } + else { + ltemp = time_offset; + ltemp <<= SHIFT_USEC - SHIFT_UPDATE; + ltemp >>= SHIFT_KG + time_constant; + } + delta_freq = time_freq + ltemp; + + den = 1000000 * (1 << (SHIFT_USEC - 8)); + if ( delta_freq < 0 ) { + tb_ticks_per_sec_delta = ( tb_ticks_per_sec * ( (-delta_freq) >> (SHIFT_USEC - 8))) / den; + new_tb_ticks_per_sec = tb_ticks_per_sec + tb_ticks_per_sec_delta; + } + else { + tb_ticks_per_sec_delta = ( tb_ticks_per_sec * ( delta_freq >> (SHIFT_USEC - 8))) / den; + new_tb_ticks_per_sec = tb_ticks_per_sec - tb_ticks_per_sec_delta; + } + tb_ticks = get_tb() - do_gtod.tb_orig_stamp; + div128_by_32( 1024*1024, 0, new_tb_ticks_per_sec, &divres ); + new_tb_to_xs = divres.result_low; + new_xsec = mulhdu( tb_ticks, new_tb_to_xs ); + + write_lock_irqsave( &xtime_lock, flags ); + old_xsec = mulhdu( tb_ticks, do_gtod.varp->tb_to_xs ); + new_stamp_xsec = do_gtod.varp->stamp_xsec + old_xsec - new_xsec; + + if (do_gtod.varp == &do_gtod.vars[0]) + temp_varp = &do_gtod.vars[1]; + else + temp_varp = &do_gtod.vars[0]; + temp_varp->tb_to_xs = new_tb_to_xs; + temp_varp->stamp_xsec = new_stamp_xsec; + mb(); + do_gtod.varp = temp_varp; + + write_unlock_irqrestore( &xtime_lock, flags ); + +} + + +#define TICK_SIZE tick +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) +#define leapyear(year) ((year) % 4 == 0) +#define days_in_year(a) (leapyear(a) ? 366 : 365) +#define days_in_month(a) (month_days[(a) - 1]) + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) + */ +void GregorianDay(struct rtc_time * tm) +{ + int leapsToDate; + int lastYear; + int day; + int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + lastYear=tm->tm_year-1; + + /* + * Number of leap corrections to apply up to end of last year + */ + leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; + + /* + * This year is a leap year if it is divisible by 4 except when it is + * divisible by 100 unless it is divisible by 400 + * + * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be + */ + if((tm->tm_year%4==0) && + ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && + (tm->tm_mon>2)) + { + /* + * We are past Feb. 29 in a leap year + */ + day=1; + } + else + { + day=0; + } + + day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + + tm->tm_mday; + + tm->tm_wday=day%7; +} + +void to_tm(int tim, struct rtc_time * tm) +{ + register int i; + register long hms, day; + + day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) + day -= days_in_year(i); + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) + days_in_month(FEBRUARY) = 29; + for (i = 1; day >= days_in_month(i); i++) + day -= days_in_month(i); + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i; + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; + + /* + * Determine the day of week + */ + GregorianDay(tm); +} + +/* Auxiliary function to compute scaling factors */ +/* Actually the choice of a timebase running at 1/4 the of the bus + * frequency giving resolution of a few tens of nanoseconds is quite nice. + * It makes this computation very precise (27-28 bits typically) which + * is optimistic considering the stability of most processor clock + * oscillators and the precision with which the timebase frequency + * is measured but does not harm. + */ +unsigned mulhwu_scale_factor(unsigned inscale, unsigned outscale) { + unsigned mlt=0, tmp, err; + /* No concern for performance, it's done once: use a stupid + * but safe and compact method to find the multiplier. + */ + + for (tmp = 1U<<31; tmp != 0; tmp >>= 1) { + if (mulhwu(inscale, mlt|tmp) < outscale) mlt|=tmp; + } + + /* We might still be off by 1 for the best approximation. + * A side effect of this is that if outscale is too large + * the returned value will be zero. + * Many corner cases have been checked and seem to work, + * some might have been forgotten in the test however. + */ + + err = inscale*(mlt+1); + if (err <= inscale/2) mlt++; + return mlt; + } + +/* + * Divide a 128-bit dividend by a 32-bit divisor, leaving a 128 bit + * result. + */ + +void div128_by_32( unsigned long dividend_high, unsigned long dividend_low, + unsigned divisor, struct div_result *dr ) +{ + unsigned long a,b,c,d, w,x,y,z, ra,rb,rc; + + a = dividend_high >> 32; + b = dividend_high & 0xffffffff; + c = dividend_low >> 32; + d = dividend_low & 0xffffffff; + + w = a/divisor; + ra = (a - (w * divisor)) << 32; + + x = (ra + b)/divisor; + rb = ((ra + b) - (x * divisor)) << 32; + + y = (rb + c)/divisor; + rc = ((rb + b) - (y * divisor)) << 32; + + z = (rc + d)/divisor; + + dr->result_high = (w << 32) + x; + dr->result_low = (y << 32) + z; + +} + diff -Nru a/arch/ppc64/kernel/traps.c b/arch/ppc64/kernel/traps.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/traps.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,224 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +extern int fix_alignment(struct pt_regs *); +extern void bad_page_fault(struct pt_regs *, unsigned long); + +#ifdef CONFIG_XMON +extern void xmon(struct pt_regs *regs); +extern int xmon_bpt(struct pt_regs *regs); +extern int xmon_sstep(struct pt_regs *regs); +extern int xmon_iabr_match(struct pt_regs *regs); +extern int xmon_dabr_match(struct pt_regs *regs); +extern void (*xmon_fault_handler)(struct pt_regs *regs); +#endif + +#ifdef CONFIG_XMON +void (*debugger)(struct pt_regs *regs) = xmon; +int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt; +int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep; +int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match; +int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match; +void (*debugger_fault_handler)(struct pt_regs *regs); +#else +#ifdef CONFIG_KGDB +void (*debugger)(struct pt_regs *regs); +int (*debugger_bpt)(struct pt_regs *regs); +int (*debugger_sstep)(struct pt_regs *regs); +int (*debugger_iabr_match)(struct pt_regs *regs); +int (*debugger_dabr_match)(struct pt_regs *regs); +void (*debugger_fault_handler)(struct pt_regs *regs); +#endif +#endif +/* + * Trap & Exception support + */ + +void +_exception(int signr, struct pt_regs *regs) +{ + if (!user_mode(regs)) + { + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +#if defined(CONFIG_PPCDBG) && (defined(CONFIG_XMON) || defined(CONFIG_KGDB)) + /* Allow us to catch SIGILLs for 64-bit app/glibc debugging. -Peter */ + } else if (signr == SIGILL) { + ifppcdebug(PPCDBG_SIGNALXMON) + debugger(regs); +#endif + } + force_sig(signr, current); +} + +void +SystemResetException(struct pt_regs *regs) +{ + udbg_printf("System Reset in kernel mode.\n"); + printk("System Reset in kernel mode.\n"); +#if defined(CONFIG_XMON) + xmon(regs); +#else + for(;;); +#endif +} + +void +MachineCheckException(struct pt_regs *regs) +{ + if ( !user_mode(regs) ) + { +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_fault_handler) { + debugger_fault_handler(regs); + return; + } +#endif + printk("Machine check in kernel mode.\n"); + printk("Caused by (from SRR1=%lx): ", regs->msr); + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); + } + _exception(SIGSEGV, regs); +} + +void +SMIException(struct pt_regs *regs) +{ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + { + debugger(regs); + return; + } +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("System Management Interrupt"); +} + +void +UnknownException(struct pt_regs *regs) +{ + printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(SIGTRAP, regs); +} + +void +InstructionBreakpointException(struct pt_regs *regs) +{ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_iabr_match(regs)) + return; +#endif + _exception(SIGTRAP, regs); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ + if (regs->msr & 0x100000) { + /* IEEE FP exception */ + _exception(SIGFPE, regs); + } else if (regs->msr & 0x20000) { + /* trap exception */ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_bpt(regs)) + return; +#endif + _exception(SIGTRAP, regs); + } else { + _exception(SIGILL, regs); + } +} + +void +SingleStepException(struct pt_regs *regs) +{ + regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_sstep(regs)) + return; +#endif + _exception(SIGTRAP, regs); +} + +/* Dummy handler for Performance Monitor */ + +void +PerformanceMonitorException(struct pt_regs *regs) +{ + _exception(SIGTRAP, regs); +} + +void +AlignmentException(struct pt_regs *regs) +{ + int fixed; + + fixed = fix_alignment(regs); + if (fixed == 1) { + ifppcdebug(PPCDBG_ALIGNFIXUP) + if (!user_mode(regs)) + PPCDBG(PPCDBG_ALIGNFIXUP, "fix alignment at %lx\n", regs->nip); + regs->nip += 4; /* skip over emulated instruction */ + return; + } + if (fixed == -EFAULT) { + /* fixed == -EFAULT means the operand address was bad */ + if (user_mode(regs)) + force_sig(SIGSEGV, current); + else + bad_page_fault(regs, regs->dar); + return; + } + _exception(SIGBUS, regs); +} + +void __init trap_init(void) +{ +} diff -Nru a/arch/ppc64/kernel/udbg.c b/arch/ppc64/kernel/udbg.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/udbg.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,253 @@ +/* + * NS16550 Serial Port (uart) debugging stuff. + * + * c 2001 PPC 64 Team, IBM Corp + * + * NOTE: I am trying to make this code avoid any static data references to + * simplify debugging early boot. We'll see how that goes... + * + * To use this call udbg_init() first. It will init the uart to 9600 8N1. + * You may need to update the COM1 define if your uart is at a different addr. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#define WANT_PPCDBG_TAB /* Only defined here */ +#include +#include +#include +#include +#include + +extern struct Naca *naca; +extern int _machine; + +struct NS16550 { + /* this struct must be packed */ + unsigned char rbr; /* 0 */ + unsigned char ier; /* 1 */ + unsigned char fcr; /* 2 */ + unsigned char lcr; /* 3 */ + unsigned char mcr; /* 4 */ + unsigned char lsr; /* 5 */ + unsigned char msr; /* 6 */ + unsigned char scr; /* 7 */ +}; + +#define thr rbr +#define iir fcr +#define dll rbr +#define dlm ier +#define dlab lcr + +#define LSR_DR 0x01 /* Data ready */ +#define LSR_OE 0x02 /* Overrun */ +#define LSR_PE 0x04 /* Parity error */ +#define LSR_FE 0x08 /* Framing error */ +#define LSR_BI 0x10 /* Break */ +#define LSR_THRE 0x20 /* Xmit holding register empty */ +#define LSR_TEMT 0x40 /* Xmitter empty */ +#define LSR_ERR 0x80 /* Error */ + +volatile struct NS16550 *udbg_comport; + +spinlock_t udbg_lock = SPIN_LOCK_UNLOCKED; + +void +udbg_init_uart(void *comport) +{ + if (comport) { + udbg_comport = (struct NS16550 *)comport; + udbg_comport->lcr = 0x00; eieio(); + udbg_comport->ier = 0xFF; eieio(); + udbg_comport->ier = 0x00; eieio(); + udbg_comport->lcr = 0x80; eieio(); /* Access baud rate */ + udbg_comport->dll = 12; eieio(); /* 1 = 115200, 2 = 57600, 3 = 38400, 12 = 9600 baud */ + udbg_comport->dlm = 0; eieio(); /* dll >> 8 which should be zero for fast rates; */ + udbg_comport->lcr = 0x03; eieio(); /* 8 data, 1 stop, no parity */ + udbg_comport->mcr = 0x03; eieio(); /* RTS/DTR */ + udbg_comport->fcr = 0x07; eieio(); /* Clear & enable FIFOs */ + } +} + +void +udbg_putc(unsigned char c) +{ + if ( udbg_comport ) { + while ((udbg_comport->lsr & LSR_THRE) == 0) + /* wait for idle */; + udbg_comport->thr = c; eieio(); + if (c == '\n') { + /* Also put a CR. This is for convenience. */ + while ((udbg_comport->lsr & LSR_THRE) == 0) + /* wait for idle */; + udbg_comport->thr = '\r'; eieio(); + } + } else if ( _machine == _MACH_iSeries ) { + /* ToDo: switch this via ppc_md */ + printk("%c", c); + } +} + +int udbg_getc_poll(void) +{ + if (udbg_comport) { + if ((udbg_comport->lsr & LSR_DR) != 0) + return udbg_comport->rbr; + else + return -1; + } + return -1; +} + +unsigned char +udbg_getc(void) +{ + if ( udbg_comport ) { + while ((udbg_comport->lsr & LSR_DR) == 0) + /* wait for char */; + return udbg_comport->rbr; + } + return 0; +} + +void +udbg_puts(const char *s) +{ + if (ppc_md.udbg_putc) { + char c; + + if (s && *s != '\0') { + while ((c = *s++) != '\0') + ppc_md.udbg_putc(c); + } else { + udbg_puts("NULL"); + } + } else { + printk("%s", s); + } +} + +int +udbg_write(const char *s, int n) +{ + int remain = n; + char c; + if (!ppc_md.udbg_putc) + for (;;); /* stop here for cpuctl */ + if ( s && *s != '\0' ) { + while ( (( c = *s++ ) != '\0') && (remain-- > 0)) { + ppc_md.udbg_putc(c); + } + } else + udbg_puts("NULL"); + return n - remain; +} + +int +udbg_read(char *buf, int buflen) { + char c, *p = buf; + int i; + if (!ppc_md.udbg_putc) + for (;;); /* stop here for cpuctl */ + for (i = 0; i < buflen; ++i) { + do { + c = ppc_md.udbg_getc(); + } while (c == 0x11 || c == 0x13); + *p++ = c; + } + return i; +} + +void +udbg_puthex(unsigned long val) +{ + int i, nibbles = sizeof(val)*2; + unsigned char buf[sizeof(val)*2+1]; + for (i = nibbles-1; i >= 0; i--) { + buf[i] = (val & 0xf) + '0'; + if (buf[i] > '9') + buf[i] += ('a'-'0'-10); + val >>= 4; + } + buf[nibbles] = '\0'; + udbg_puts(buf); +} + +void +udbg_printSP(const char *s) +{ + if (_machine == _MACH_pSeries) { + unsigned long sp; + asm("mr %0,1" : "=r" (sp) :); + if (s) + udbg_puts(s); + udbg_puthex(sp); + } +} + +void +udbg_printf(const char *fmt, ...) +{ + unsigned long flags; + unsigned char buf[256]; + + va_list args; + va_start(args, fmt); + + spin_lock_irqsave(&udbg_lock, flags); + vsprintf(buf, fmt, args); + udbg_puts(buf); + spin_unlock_irqrestore(&udbg_lock, flags); + + va_end(args); +} + +/* Special print used by PPCDBG() macro */ +void +udbg_ppcdbg(unsigned long flags, const char *fmt, ...) +{ + unsigned long flags; + unsigned long active_debugs = flags & naca->debug_switch; + + if ( active_debugs ) { + va_list ap; + unsigned char buf[256]; + unsigned long i, len = 0; + + spin_lock_irqsave(&udbg_lock, flags); + for(i=0; i < PPCDBG_NUM_FLAGS ;i++) { + if (((1U << i) & active_debugs) && + trace_names[i]) { + len += strlen(trace_names[i]); + udbg_puts(trace_names[i]); + break; + } + } + sprintf(buf, " [%s]: ", current->comm); + len += strlen(buf); + udbg_puts(buf); + + while(len < 18) { + udbg_puts(" "); + len++; + } + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + udbg_puts(buf); + spin_unlock_irqrestore(&udbg_lock, flags); + + va_end(ap); + } +} + +unsigned long +udbg_ifdebug(unsigned long flags) +{ + return (flags & naca->debug_switch); +} diff -Nru a/arch/ppc64/kernel/xics.c b/arch/ppc64/kernel/xics.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/xics.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,473 @@ +/* + * arch/ppc/kernel/xics.c + * + * Copyright 2000 IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i8259.h" +#include "xics.h" +#include + +extern struct Naca *naca; + +void xics_enable_irq(u_int irq); +void xics_disable_irq(u_int irq); +void xics_mask_and_ack_irq(u_int irq); +void xics_end_irq(u_int irq); +void xics_set_affinity(unsigned int irq_nr, unsigned long cpumask); + +struct hw_interrupt_type xics_pic = { + " XICS ", + NULL, + NULL, + xics_enable_irq, + xics_disable_irq, + xics_mask_and_ack_irq, + xics_end_irq, + xics_set_affinity +}; + +struct hw_interrupt_type xics_8259_pic = { + " XICS/8259", + NULL, + NULL, + NULL, + NULL, + xics_mask_and_ack_irq, + NULL +}; + +#define XICS_IPI 2 +#define XICS_IRQ_OFFSET 0x10 +#define XICS_IRQ_SPURIOUS 0 + +/* Want a priority other than 0. Various HW issues require this. */ +#define DEFAULT_PRIORITY 5 + +struct xics_ipl { + union { + u32 word; + u8 bytes[4]; + } xirr_poll; + union { + u32 word; + u8 bytes[4]; + } xirr; + u32 dummy; + union { + u32 word; + u8 bytes[4]; + } qirr; +}; + +struct xics_info { + volatile struct xics_ipl * per_cpu[NR_CPUS]; +}; + +struct xics_info xics_info; + +unsigned long long intr_base = 0; +int xics_irq_8259_cascade = 0; +int xics_irq_8259_cascade_real = 0; +unsigned int default_server = 0; +unsigned int default_distrib_server = 0; + +/* RTAS service tokens */ +int ibm_get_xive; +int ibm_set_xive; +int ibm_int_off; + +struct xics_interrupt_node { + unsigned long long addr; + unsigned long long size; +} inodes[NR_CPUS*2]; + +typedef struct { + int (*xirr_info_get)(int cpu); + void (*xirr_info_set)(int cpu, int val); + void (*cppr_info)(int cpu, u8 val); + void (*qirr_info)(int cpu, u8 val); +} xics_ops; + + +static int pSeries_xirr_info_get(int n_cpu) +{ + return (xics_info.per_cpu[n_cpu]->xirr.word); +} + +static void pSeries_xirr_info_set(int n_cpu, int value) +{ + xics_info.per_cpu[n_cpu]->xirr.word = value; +} + +static void pSeries_cppr_info(int n_cpu, u8 value) +{ + xics_info.per_cpu[n_cpu]->xirr.bytes[0] = value; +} + +static void pSeries_qirr_info(int n_cpu , u8 value) +{ + xics_info.per_cpu[n_cpu]->qirr.bytes[0] = value; +} + +static xics_ops pSeries_ops = { + pSeries_xirr_info_get, + pSeries_xirr_info_set, + pSeries_cppr_info, + pSeries_qirr_info +}; + +static xics_ops *ops = &pSeries_ops; +extern xics_ops pSeriesLP_ops; + + +void +xics_enable_irq( + u_int virq + ) +{ + u_int irq; + unsigned long status; + long call_status; + + virq -= XICS_IRQ_OFFSET; + irq = virt_irq_to_real(virq); + if (irq == XICS_IPI) + return; +#ifdef CONFIG_IRQ_ALL_CPUS + call_status = rtas_call(ibm_set_xive, 3, 1, (unsigned long*)&status, + irq, smp_threads_ready ? default_distrib_server : default_server, DEFAULT_PRIORITY); +#else + call_status = rtas_call(ibm_set_xive, 3, 1, (unsigned long*)&status, + irq, default_server, DEFAULT_PRIORITY); +#endif + if( call_status != 0 ) { + printk("xics_enable_irq: irq=%x: rtas_call failed; retn=%lx, status=%lx\n", + irq, call_status, status); + return; + } +} + +void +xics_disable_irq( + u_int virq + ) +{ + u_int irq; + unsigned long status; + long call_status; + + virq -= XICS_IRQ_OFFSET; + irq = virt_irq_to_real(virq); + call_status = rtas_call(ibm_int_off, 1, 1, (unsigned long*)&status, + irq); + if( call_status != 0 ) { + printk("xics_disable_irq: irq=%x: rtas_call failed, retn=%lx\n", + irq, call_status); + return; + } +} + +void +xics_end_irq( + u_int irq + ) +{ + int cpu = smp_processor_id(); + + ops->cppr_info(cpu, 0); /* actually the value overwritten by ack */ + iosync(); + ops->xirr_info_set(cpu, ((0xff<<24) | (virt_irq_to_real(irq-XICS_IRQ_OFFSET)))); + iosync(); +} + +void +xics_mask_and_ack_irq( + u_int irq + ) +{ + int cpu = smp_processor_id(); + + if( irq < XICS_IRQ_OFFSET ) { + i8259_pic.ack(irq); + iosync(); + ops->xirr_info_set(cpu, ((0xff<<24) | xics_irq_8259_cascade_real)); + iosync(); + } + else { + ops->cppr_info(cpu, 0xff); + iosync(); + } +} + +int +xics_get_irq(struct pt_regs *regs) +{ + u_int cpu = smp_processor_id(); + u_int vec; + int irq; + + vec = ops->xirr_info_get(cpu); + /* (vec >> 24) == old priority */ + vec &= 0x00ffffff; + /* for sanity, this had better be < NR_IRQS - 16 */ + if( vec == xics_irq_8259_cascade_real ) { + irq = i8259_irq(cpu); + if(irq == -1) { + /* Spurious cascaded interrupt. Still must ack xics */ + xics_end_irq(XICS_IRQ_OFFSET + xics_irq_8259_cascade); + irq = -1; + } + } else if( vec == XICS_IRQ_SPURIOUS ) { + irq = -1; + printk("spurious PPC interrupt!\n"); + } else + irq = real_irq_to_virt(vec) + XICS_IRQ_OFFSET; + return irq; +} + + +#ifdef CONFIG_SMP +void xics_ipi_action(int irq, void *dev_id, struct pt_regs *regs) +{ + extern volatile unsigned long xics_ipi_message[]; + int cpu = smp_processor_id(); + + ops->qirr_info(cpu, 0xff); + while (xics_ipi_message[cpu]) { + if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION, &xics_ipi_message[cpu])) { + mb(); + smp_message_recv(PPC_MSG_CALL_FUNCTION, regs); + } + if (test_and_clear_bit(PPC_MSG_RESCHEDULE, &xics_ipi_message[cpu])) { + mb(); + smp_message_recv(PPC_MSG_RESCHEDULE, regs); + } + if (test_and_clear_bit(PPC_MSG_MIGRATE_TASK, &xics_ipi_message[cpu])) { + mb(); + smp_message_recv(PPC_MSG_MIGRATE_TASK, regs); + } + } +} + +void xics_cause_IPI(int cpu) +{ + ops->qirr_info(cpu,0) ; +} + +void xics_setup_cpu(void) +{ + int cpu = smp_processor_id(); + + ops->cppr_info(cpu, 0xff); + iosync(); +} +#endif /* CONFIG_SMP */ + +void +xics_init_IRQ( void ) +{ + int i; + unsigned long intr_size = 0; + struct device_node *np; + uint *ireg, ilen, indx=0; + + ibm_get_xive = rtas_token("ibm,get-xive"); + ibm_set_xive = rtas_token("ibm,set-xive"); + ibm_int_off = rtas_token("ibm,int-off"); + + np = find_type_devices("PowerPC-External-Interrupt-Presentation"); + if (!np) { + printk(KERN_WARNING "Can't find Interrupt Presentation\n"); + udbg_printf("Can't find Interrupt Presentation\n"); + while (1); + } +nextnode: + ireg = (uint *)get_property(np, "ibm,interrupt-server-ranges", 0); + if (ireg) { + /* + * set node starting index for this node + */ + indx = *ireg; + } + + ireg = (uint *)get_property(np, "reg", &ilen); + if (!ireg) { + printk(KERN_WARNING "Can't find Interrupt Reg Property\n"); + udbg_printf("Can't find Interrupt Reg Property\n"); + while (1); + } + + while (ilen) { + inodes[indx].addr = (unsigned long long)*ireg++ << 32; + ilen -= sizeof(uint); + inodes[indx].addr |= *ireg++; + ilen -= sizeof(uint); + inodes[indx].size = (unsigned long long)*ireg++ << 32; + ilen -= sizeof(uint); + inodes[indx].size |= *ireg++; + ilen -= sizeof(uint); + indx++; + if (indx >= NR_CPUS) break; + } + + np = np->next; + if ((indx < NR_CPUS) && np) goto nextnode; + + /* Find the server numbers for the boot cpu. */ + for (np = find_type_devices("cpu"); np; np = np->next) { + ireg = (uint *)get_property(np, "reg", &ilen); + if (ireg && ireg[0] == hard_smp_processor_id()) { + ireg = (uint *)get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); + i = ilen / sizeof(int); + if (ireg && i > 0) { + default_server = ireg[0]; + default_distrib_server = ireg[i-1]; /* take last element */ + } + break; + } + } + + intr_base = inodes[0].addr; + intr_size = (ulong)inodes[0].size; + + np = find_type_devices("interrupt-controller"); + if (!np) { + printk(KERN_WARNING "xics: no ISA Interrupt Controller\n"); + xics_irq_8259_cascade = -1; + } else { + ireg = (uint *) get_property(np, "interrupts", 0); + if (!ireg) { + printk(KERN_WARNING "Can't find ISA Interrupts Property\n"); + udbg_printf("Can't find ISA Interrupts Property\n"); + while (1); + } + xics_irq_8259_cascade_real = *ireg; + xics_irq_8259_cascade = virt_irq_create_mapping(xics_irq_8259_cascade_real); + } + + if (_machine == _MACH_pSeries) { +#ifdef CONFIG_SMP + for (i = 0; i < naca->processorCount; ++i) { + xics_info.per_cpu[i] = + __ioremap((ulong)inodes[get_hard_smp_processor_id(i)].addr, + (ulong)inodes[get_hard_smp_processor_id(i)].size, _PAGE_NO_CACHE); + } +#else + xics_info.per_cpu[0] = __ioremap((ulong)intr_base, intr_size, _PAGE_NO_CACHE); +#endif /* CONFIG_SMP */ +#ifdef CONFIG_PPC_PSERIES + /* actually iSeries does not use any of xics...but it has link dependencies + * for now, except this new one... + */ + } else if (_machine == _MACH_pSeriesLP) { + ops = &pSeriesLP_ops; +#endif + } + + xics_8259_pic.enable = i8259_pic.enable; + xics_8259_pic.disable = i8259_pic.disable; + for (i = 0; i < 16; ++i) + irq_desc[i].handler = &xics_8259_pic; + for (; i < NR_IRQS; ++i) + irq_desc[i].handler = &xics_pic; + + ops->cppr_info(0, 0xff); + iosync(); + if (xics_irq_8259_cascade != -1) { + if (request_irq(xics_irq_8259_cascade + XICS_IRQ_OFFSET, no_action, + 0, "8259 cascade", 0)) + printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n"); + i8259_init(); + } + +#ifdef CONFIG_SMP + real_irq_to_virt_map[XICS_IPI] = virt_irq_to_real_map[XICS_IPI] = XICS_IPI; + request_irq(XICS_IPI + XICS_IRQ_OFFSET, xics_ipi_action, 0, "IPI", 0); + irq_desc[XICS_IPI+XICS_IRQ_OFFSET].status |= IRQ_PER_CPU; +#endif +} + +void xics_isa_init(void) +{ + return; + if (request_irq(xics_irq_8259_cascade + XICS_IRQ_OFFSET, no_action, + 0, "8259 cascade", 0)) + printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n"); + i8259_init(); +} + +/* + * Find first logical cpu and return its physical cpu number + */ +static inline u32 physmask(u32 cpumask) +{ + int i; + + for (i = 0; i < smp_num_cpus; ++i, cpumask >>= 1) { + if (cpumask & 1) + return get_hard_smp_processor_id(i); + } + + printk(KERN_ERR "xics_set_affinity: invalid irq mask\n"); + + return default_distrib_server; +} + +void xics_set_affinity(unsigned int virq, unsigned long cpumask) +{ + irq_desc_t *desc = irq_desc + virq; + unsigned int irq; + unsigned long flags; + long status; + unsigned long xics_status[2]; + u32 newmask; + + virq -= XICS_IRQ_OFFSET; + irq = virt_irq_to_real(virq); + if (irq == XICS_IPI) + return; + + spin_lock_irqsave(&desc->lock, flags); + + status = rtas_call(ibm_get_xive, 1, 3, (void *)&xics_status, irq); + + if (status) { + printk("xics_set_affinity: irq=%d ibm,get-xive returns %ld\n", + irq, status); + goto out; + } + + /* For the moment only implement delivery to all cpus or one cpu */ + if (cpumask == 0xffffffff) + newmask = default_distrib_server; + else + newmask = physmask(cpumask); + + status = rtas_call(ibm_set_xive, 3, 1, NULL, + irq, newmask, xics_status[1]); + + if (status) { + printk("xics_set_affinity irq=%d ibm,set-xive returns %ld\n", + irq, status); + goto out; + } + +out: + spin_unlock_irqrestore(&desc->lock, flags); +} diff -Nru a/arch/ppc64/kernel/xics.h b/arch/ppc64/kernel/xics.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/kernel/xics.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,24 @@ +/* + * arch/ppc/kernel/xics.h + * + * Copyright 2000 IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _PPC_KERNEL_XICS_H +#define _PPC_KERNEL_XICS_H + +#include "local_irq.h" + +extern struct hw_interrupt_type xics_pic; +extern struct hw_interrupt_type xics_8259_pic; + +void xics_init_IRQ(void); +int xics_get_irq(struct pt_regs *); +void xics_isa_init(void); + +#endif /* _PPC_KERNEL_XICS_H */ diff -Nru a/arch/ppc64/lib/Makefile b/arch/ppc64/lib/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/lib/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,11 @@ +# +# Makefile for ppc64-specific library files.. +# + +USE_STANDARD_AS_RULE := true + +O_TARGET = lib.o + +obj-y := checksum.o dec_and_lock.o string.o strcase.o + +include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc64/lib/checksum.S b/arch/ppc64/lib/checksum.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/lib/checksum.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,231 @@ +/* + * This file contains assembly-language implementations + * of IP-style 1's complement checksum routines. + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Severely hacked about by Paul Mackerras (paulus@cs.anu.edu.au). + */ + +#include +#include +#include +#include "../kernel/ppc_asm.tmpl" + + .text + +/* + * ip_fast_csum(r3=buf, r4=len) -- Optimized for IP header + * len is in words and is always >= 5. + * + * In practice len == 5, but this is not guaranteed. So this code does not + * attempt to use doubleword instructions. + */ +_GLOBAL(ip_fast_csum) + lwz r0,0(r3) + lwzu r5,4(r3) + addic. r4,r4,-2 + addc r0,r0,r5 + mtctr r4 + blelr- +1: lwzu r4,4(r3) + adde r0,r0,r4 + bdnz 1b + addze r0,r0 /* add in final carry */ + rldicl r4,r0,32,0 /* fold two 32-bit halves together */ + add r0,r0,r4 + srdi r0,r0,32 + rlwinm r3,r0,16,0,31 /* fold two halves together */ + add r3,r0,r3 + not r3,r3 + srwi r3,r3,16 + blr + +/* + * Compute checksum of TCP or UDP pseudo-header: + * csum_tcpudp_magic(r3=saddr, r4=daddr, r5=len, r6=proto, r7=sum) + * No real gain trying to do this specially for 64 bit, but + * the 32 bit addition may spill into the upper bits of + * the doubleword so we still must fold it down from 64. + */ +_GLOBAL(csum_tcpudp_magic) + rlwimi r5,r6,16,0,15 /* put proto in upper half of len */ + addc r0,r3,r4 /* add 4 32-bit words together */ + adde r0,r0,r5 + adde r0,r0,r7 + rldicl r4,r0,32,0 /* fold 64 bit value */ + add r0,r4,r0 + srdi r0,r0,32 + rlwinm r3,r0,16,0,31 /* fold two halves together */ + add r3,r0,r3 + not r3,r3 + srwi r3,r3,16 + blr + +/* + * Computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit). + * + * This code assumes at least halfword alignment, though the length + * can be any number of bytes. The sum is accumulated in r5. + * + * csum_partial(r3=buff, r4=len, r5=sum) + */ +_GLOBAL(csum_partial) + subi r3,r3,8 /* we'll offset by 8 for the loads */ + srdi. r6,r4,3 /* divide by 8 for doubleword count */ + addic r5,r5,0 /* clear carry */ + beq 3f /* if we're doing < 8 bytes */ + andi. r0,r3,2 /* aligned on a word boundary already? */ + beq+ 1f + lhz r6,8(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r4,r4,2 + addc r5,r5,r6 + srdi. r6,r4,3 /* recompute number of doublewords */ + beq 3f /* any left? */ +1: mtctr r6 +2: ldu r6,8(r3) /* main sum loop */ + adde r5,r5,r6 + bdnz 2b + andi. r4,r4,7 /* compute bytes left to sum after doublewords */ +3: cmpi 0,r4,4 /* is at least a full word left? */ + blt 4f + lwz r6,8(r3) /* sum this word */ + addi r3,r3,4 + subi r4,r4,4 + adde r5,r5,r6 +4: cmpi 0,r4,2 /* is at least a halfword left? */ + blt+ 5f + lhz r6,8(r3) /* sum this halfword */ + addi r3,r3,2 + subi r4,r4,2 + adde r5,r5,r6 +5: cmpi 0,r4,1 /* is at least a byte left? */ + bne+ 6f + lbz r6,8(r3) /* sum this byte */ + slwi r6,r6,8 /* this byte is assumed to be the upper byte of a halfword */ + adde r5,r5,r6 +6: addze r5,r5 /* add in final carry */ + rldicl r4,r5,32,0 /* fold two 32-bit halves together */ + add r3,r4,r5 + srdi r3,r3,32 + blr + +/* + * Computes the checksum of a memory block at src, length len, + * and adds in "sum" (32-bit), while copying the block to dst. + * If an access exception occurs on src or dst, it stores -EFAULT + * to *src_err or *dst_err respectively, and (for an error on + * src) zeroes the rest of dst. + * + * This code needs to be reworked to take advantage of 64 bit sum+copy. + * However, due to tokenring halfword alignment problems this will be very + * tricky. For now we'll leave it until we instrument it somehow. + * + * csum_partial_copy_generic(r3=src, r4=dst, r5=len, r6=sum, r7=src_err, r8=dst_err) + */ +_GLOBAL(csum_partial_copy_generic) + addic r0,r6,0 + subi r3,r3,4 + subi r4,r4,4 + srwi. r6,r5,2 + beq 3f /* if we're doing < 4 bytes */ + andi. r9,r4,2 /* Align dst to longword boundary */ + beq+ 1f +81: lhz r6,4(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r5,r5,2 +91: sth r6,4(r4) + addi r4,r4,2 + addc r0,r0,r6 + srwi. r6,r5,2 /* # words to do */ + beq 3f +1: mtctr r6 +82: lwzu r6,4(r3) /* the bdnz has zero overhead, so it should */ +92: stwu r6,4(r4) /* be unnecessary to unroll this loop */ + adde r0,r0,r6 + bdnz 82b + andi. r5,r5,3 +3: cmpi 0,r5,2 + blt+ 4f +83: lhz r6,4(r3) + addi r3,r3,2 + subi r5,r5,2 +93: sth r6,4(r4) + addi r4,r4,2 + adde r0,r0,r6 +4: cmpi 0,r5,1 + bne+ 5f +84: lbz r6,4(r3) +94: stb r6,4(r4) + slwi r6,r6,8 /* Upper byte of word */ + adde r0,r0,r6 +5: addze r3,r0 /* add in final carry (unlikely with 64-bit regs) */ + rldicl r4,r3,32,0 /* fold 64 bit value */ + add r3,r4,r3 + srdi r3,r3,32 + blr + +/* These shouldn't go in the fixup section, since that would + cause the ex_table addresses to get out of order. */ + + .globl src_error_1 +src_error_1: + li r6,0 + subi r5,r5,2 +95: sth r6,4(r4) + addi r4,r4,2 + srwi. r6,r5,2 + beq 3f + mtctr r6 + .globl src_error_2 +src_error_2: + li r6,0 +96: stwu r6,4(r4) + bdnz 96b +3: andi. r5,r5,3 + beq src_error + .globl src_error_3 +src_error_3: + li r6,0 + mtctr r5 + addi r4,r4,3 +97: stbu r6,1(r4) + bdnz 97b + .globl src_error +src_error: + cmpi 0,r7,0 + beq 1f + li r6,-EFAULT + stw r6,0(r7) +1: addze r3,r0 + blr + + .globl dst_error +dst_error: + cmpi 0,r8,0 + beq 1f + li r6,-EFAULT + stw r6,0(r8) +1: addze r3,r0 + blr + +.section __ex_table,"a" + .align 3 + .llong 81b,src_error_1 + .llong 91b,dst_error + .llong 82b,src_error_2 + .llong 92b,dst_error + .llong 83b,src_error_3 + .llong 93b,dst_error + .llong 84b,src_error_3 + .llong 94b,dst_error + .llong 95b,dst_error + .llong 96b,dst_error + .llong 97b,dst_error diff -Nru a/arch/ppc64/lib/dec_and_lock.c b/arch/ppc64/lib/dec_and_lock.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/lib/dec_and_lock.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,38 @@ +/* + * ppc64 version of atomic_dec_and_lock() using cmpxchg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include + +int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) +{ + int counter; + int newcount; + +repeat: + counter = atomic_read(atomic); + newcount = counter-1; + + if (!newcount) + goto slow_path; + + newcount = cmpxchg(&atomic->counter, counter, newcount); + + if (newcount != counter) + goto repeat; + return 0; + +slow_path: + spin_lock(lock); + if (atomic_dec_and_test(atomic)) + return 1; + spin_unlock(lock); + return 0; +} diff -Nru a/arch/ppc64/lib/strcase.c b/arch/ppc64/lib/strcase.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/lib/strcase.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,31 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include + +int strcasecmp(const char *s1, const char *s2) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while (c1 == c2 && c1 != 0); + return c1 - c2; +} + +int strncasecmp(const char *s1, const char *s2, int n) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while ((--n > 0) && c1 == c2 && c1 != 0); + return c1 - c2; +} diff -Nru a/arch/ppc64/lib/string.S b/arch/ppc64/lib/string.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/lib/string.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,660 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include "../kernel/ppc_asm.tmpl" +#include +#include + +#define CACHE_LINE_SIZE 128 +#define LG_CACHE_LINE_SIZE 7 +#define MAX_COPY_PREFETCH 1 + +#define COPY_16_BYTES \ + lwz r7,4(r4); \ + lwz r8,8(r4); \ + lwz r9,12(r4); \ + lwzu r10,16(r4); \ + stw r7,4(r6); \ + stw r8,8(r6); \ + stw r9,12(r6); \ + stwu r10,16(r6) + +#define COPY_16_BYTES_WITHEX(n) \ +8 ## n ## 0: \ + lwz r7,4(r4); \ +8 ## n ## 1: \ + lwz r8,8(r4); \ +8 ## n ## 2: \ + lwz r9,12(r4); \ +8 ## n ## 3: \ + lwzu r10,16(r4); \ +8 ## n ## 4: \ + stw r7,4(r6); \ +8 ## n ## 5: \ + stw r8,8(r6); \ +8 ## n ## 6: \ + stw r9,12(r6); \ +8 ## n ## 7: \ + stwu r10,16(r6) + +#define COPY_16_BYTES_EXCODE(n) \ +9 ## n ## 0: \ + addi r5,r5,-(16 * n); \ + b 104f; \ +9 ## n ## 1: \ + addi r5,r5,-(16 * n); \ + b 105f; \ +.section __ex_table,"a"; \ + .align 3; \ + .llong 8 ## n ## 0b,9 ## n ## 0b; \ + .llong 8 ## n ## 1b,9 ## n ## 0b; \ + .llong 8 ## n ## 2b,9 ## n ## 0b; \ + .llong 8 ## n ## 3b,9 ## n ## 0b; \ + .llong 8 ## n ## 4b,9 ## n ## 1b; \ + .llong 8 ## n ## 5b,9 ## n ## 1b; \ + .llong 8 ## n ## 6b,9 ## n ## 1b; \ + .llong 8 ## n ## 7b,9 ## n ## 1b; \ +.text + +CACHELINE_BYTES = CACHE_LINE_SIZE +LG_CACHELINE_BYTES = LG_CACHE_LINE_SIZE +CACHELINE_MASK = (CACHE_LINE_SIZE-1) + +_GLOBAL(strcpy) + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + +_GLOBAL(strncpy) + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + +_GLOBAL(strcat) + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + +_GLOBAL(strcmp) + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + +_GLOBAL(strlen) + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + +/* + * Use dcbz on the complete cache lines in the destination + * to set them to zero. This requires that the destination + * area is cacheable. -- paulus + */ +_GLOBAL(cacheable_memzero) + mr r5,r4 + li r4,0 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + clrlwi r7,r6,32-LG_CACHELINE_BYTES + add r8,r7,r5 + srwi r9,r8,LG_CACHELINE_BYTES + addic. r9,r9,-1 /* total number of complete cachelines */ + ble 2f + xori r0,r7,CACHELINE_MASK & ~3 + srwi. r0,r0,2 + beq 3f + mtctr r0 +4: stwu r4,4(r6) + bdnz 4b +3: mtctr r9 + li r7,4 +10: dcbz r7,r6 + addi r6,r6,CACHELINE_BYTES + bdnz 10b + clrlwi r5,r8,32-LG_CACHELINE_BYTES + addi r5,r5,4 +2: srwi r0,r5,2 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + +_GLOBAL(memset) + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + srwi r0,r5,2 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + +_GLOBAL(bcopy) + mr r6,r3 + mr r3,r4 + mr r4,r6 + b .memcpy + +/* + * This version uses dcbz on the complete cache lines in the + * destination area to reduce memory traffic. This requires that + * the destination area is cacheable. + * We only use this version if the source and dest don't overlap. + * -- paulus. + */ +_GLOBAL(cacheable_memcpy) + add r7,r3,r5 /* test if the src & dst overlap */ + add r8,r4,r5 + cmplw 0,r4,r7 + cmplw 1,r3,r8 + crand 0,0,4 /* cr0.lt &= cr1.lt */ + blt .memcpy /* if regions overlap */ + + addi r4,r4,-4 + addi r6,r3,-4 + neg r0,r3 + andi. r0,r0,CACHELINE_MASK /* # bytes to start of cache line */ + beq 58f + + cmplw 0,r5,r0 /* is this more than total to do? */ + blt 63f /* if not much to do */ + andi. r8,r0,3 /* get it word-aligned first */ + subf r5,r0,r5 + mtctr r8 + beq+ 61f +70: lbz r9,4(r4) /* do some bytes */ + stb r9,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 70b +61: srwi. r0,r0,2 + mtctr r0 + beq 58f +72: lwzu r9,4(r4) /* do some words */ + stwu r9,4(r6) + bdnz 72b + +58: srwi. r0,r5,LG_CACHELINE_BYTES /* # complete cachelines */ + clrlwi r5,r5,32-LG_CACHELINE_BYTES + li r11,4 + mtctr r0 + beq 63f +53: + dcbz r11,r6 + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES +#endif +#endif +#endif + bdnz 53b + +63: srwi. r0,r5,2 + mtctr r0 + beq 64f +30: lwzu r0,4(r4) + stwu r0,4(r6) + bdnz 30b + +64: andi. r0,r5,3 + mtctr r0 + beq+ 65f +40: lbz r0,4(r4) + stb r0,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 40b +65: blr + +_GLOBAL(memmove) + cmplw 0,r3,r4 + bgt .backwards_memcpy + /* fall through */ + +_GLOBAL(memcpy) + srwi. r7,r5,3 + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + +_GLOBAL(backwards_memcpy) + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + +_GLOBAL(memcmp) + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr +2: li r3,0 + blr + +_GLOBAL(memchr) + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r3,r3,-1 +1: lbzu r0,1(r3) + cmpw 0,r0,r4 + bdnzf 2,1b + beqlr +2: li r3,0 + blr + +_GLOBAL(__copy_tofrom_user) + addi r4,r4,-4 + addi r6,r3,-4 + neg r0,r3 + andi. r0,r0,CACHELINE_MASK /* # bytes to start of cache line */ + beq 58f + + cmplw 0,r5,r0 /* is this more than total to do? */ + blt 63f /* if not much to do */ + andi. r8,r0,3 /* get it word-aligned first */ + mtctr r8 + beq+ 61f +70: lbz r9,4(r4) /* do some bytes */ +71: stb r9,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 70b +61: subf r5,r0,r5 + srwi. r0,r0,2 + mtctr r0 + beq 58f +72: lwzu r9,4(r4) /* do some words */ +73: stwu r9,4(r6) + bdnz 72b + +58: srwi. r0,r5,LG_CACHELINE_BYTES /* # complete cachelines */ + clrlwi r5,r5,32-LG_CACHELINE_BYTES + li r11,4 + beq 63f + + /* Here we decide how far ahead to prefetch the source */ +#if MAX_COPY_PREFETCH > 1 + /* Heuristically, for large transfers we prefetch + MAX_COPY_PREFETCH cachelines ahead. For small transfers + we prefetch 1 cacheline ahead. */ + cmpwi r0,MAX_COPY_PREFETCH + li r7,1 + li r3,4 + ble 111f + li r7,MAX_COPY_PREFETCH +111: mtctr r7 +112: dcbt r3,r4 + addi r3,r3,CACHELINE_BYTES + bdnz 112b +#else /* MAX_COPY_PREFETCH == 1 */ + li r3,CACHELINE_BYTES + 4 + dcbt r11,r4 +#endif /* MAX_COPY_PREFETCH */ + + mtctr r0 +53: + dcbt r3,r4 + dcbz r11,r6 +/* had to move these to keep extable in order */ + .section __ex_table,"a" + .align 3 + .llong 70b,100f + .llong 71b,101f + .llong 72b,102f + .llong 73b,103f + .llong 53b,105f + .text +/* the main body of the cacheline loop */ + COPY_16_BYTES_WITHEX(0) +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES_WITHEX(1) +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES_WITHEX(2) + COPY_16_BYTES_WITHEX(3) +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES_WITHEX(4) + COPY_16_BYTES_WITHEX(5) + COPY_16_BYTES_WITHEX(6) + COPY_16_BYTES_WITHEX(7) +#endif +#endif +#endif + bdnz 53b + +63: srwi. r0,r5,2 + mtctr r0 + beq 64f +30: lwzu r0,4(r4) +31: stwu r0,4(r6) + bdnz 30b + +64: andi. r0,r5,3 + mtctr r0 + beq+ 65f +40: lbz r0,4(r4) +41: stb r0,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 40b +65: li r3,0 + blr + +/* read fault, initial single-byte copy */ +100: li r4,0 + b 90f +/* write fault, initial single-byte copy */ +101: li r4,1 +90: subf r5,r8,r5 + li r3,0 + b 99f +/* read fault, initial word copy */ +102: li r4,0 + b 91f +/* write fault, initial word copy */ +103: li r4,1 +91: li r3,2 + b 99f + +/* + * this stuff handles faults in the cacheline loop and branches to either + * 104f (if in read part) or 105f (if in write part), after updating r5 + */ + COPY_16_BYTES_EXCODE(0) +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES_EXCODE(1) +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES_EXCODE(2) + COPY_16_BYTES_EXCODE(3) +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES_EXCODE(4) + COPY_16_BYTES_EXCODE(5) + COPY_16_BYTES_EXCODE(6) + COPY_16_BYTES_EXCODE(7) +#endif +#endif +#endif + +/* read fault in cacheline loop */ +104: li r4,0 + b 92f +/* fault on dcbz (effectively a write fault) */ +/* or write fault in cacheline loop */ +105: li r4,1 +92: li r3,LG_CACHELINE_BYTES + b 99f +/* read fault in final word loop */ +108: li r4,0 + b 93f +/* write fault in final word loop */ +109: li r4,1 +93: andi. r5,r5,3 + li r3,2 + b 99f +/* read fault in final byte loop */ +110: li r4,0 + b 94f +/* write fault in final byte loop */ +111: li r4,1 +94: li r5,0 + li r3,0 +/* + * At this stage the number of bytes not copied is + * r5 + (ctr << r3), and r4 is 0 for read or 1 for write. + */ +99: mfctr r0 + slw r3,r0,r3 + add r3,r3,r5 + cmpwi 0,r4,0 + bne 120f +/* for read fault, clear out the destination: r3 bytes starting at 4(r6) */ + srwi. r0,r3,2 + li r9,0 + mtctr r0 + beq 113f +112: stwu r9,4(r6) + bdnz 112b +113: andi. r0,r3,3 + mtctr r0 + beq 120f +114: stb r9,4(r6) + addi r6,r6,1 + bdnz 114b +120: blr + + .section __ex_table,"a" + .align 3 + .llong 30b,108b + .llong 31b,109b + .llong 40b,110b + .llong 41b,111b + .llong 112b,120b + .llong 114b,120b + .text + +_GLOBAL(__clear_user) + addi r6,r3,-4 + li r3,0 + li r5,0 + cmplwi 0,r4,4 + blt 7f + /* clear a single word */ +11: stwu r5,4(r6) + beqlr + /* clear word sized chunks */ + andi. r0,r6,3 + add r4,r0,r4 + subf r6,r0,r6 + srwi r0,r4,2 + mtctr r0 + bdz 6f +1: stwu r5,4(r6) + bdnz 1b +6: andi. r4,r4,3 + /* clear byte sized chunks */ +7: cmpwi 0,r4,0 + beqlr + mtctr r4 + addi r6,r6,3 +8: stbu r5,1(r6) + bdnz 8b + blr +99: li r3,-EFAULT + blr + + .section __ex_table,"a" + .align 3 + .llong 11b,99b + .llong 1b,99b + .llong 8b,99b + .text + +_GLOBAL(__strncpy_from_user) + addi r6,r3,-1 + addi r4,r4,-1 + cmpwi 0,r5,0 + beq 2f + mtctr r5 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + beq 3f +2: addi r6,r6,1 +3: subf r3,r3,r6 + blr +99: li r3,-EFAULT + blr + + .section __ex_table,"a" + .align 3 + .llong 1b,99b + .text + +/* r3 = str, r4 = len (> 0), r5 = top (highest addr) */ +_GLOBAL(__strnlen_user) + addi r7,r3,-1 + subf r6,r7,r5 /* top+1 - str */ + cmplw 0,r4,r6 + bge 0f + mr r6,r4 +0: mtctr r6 /* ctr = min(len, top - str) */ +1: lbzu r0,1(r7) /* get next byte */ + cmpwi 0,r0,0 + bdnzf 2,1b /* loop if --ctr != 0 && byte != 0 */ + addi r7,r7,1 + subf r3,r3,r7 /* number of bytes we have looked at */ + beqlr /* return if we found a 0 byte */ + cmpw 0,r3,r4 /* did we look at all len bytes? */ + blt 99f /* if not, must have hit top */ + addi r3,r4,1 /* return len + 1 to indicate no null found */ + blr +99: li r3,0 /* bad address, return 0 */ + blr + + .section __ex_table,"a" + .align 3 + .llong 1b,99b diff -Nru a/arch/ppc64/mm/Makefile b/arch/ppc64/mm/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/mm/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,16 @@ +# +# Makefile for the linux ppc-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +EXTRA_CFLAGS = -mno-minimal-toc + +O_TARGET := mm.o + +obj-y := fault.o init.o extable.o imalloc.o + +include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc64/mm/extable.c b/arch/ppc64/mm/extable.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/mm/extable.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,48 @@ +/* + * linux/arch/ppc/mm/extable.c + * + * from linux/arch/i386/mm/extable.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + if (ret) return ret; + + return 0; +} diff -Nru a/arch/ppc64/mm/fault.c b/arch/ppc64/mm/fault.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/mm/fault.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,231 @@ +/* + * arch/ppc/mm/fault.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/mm/fault.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * Modified by Cort Dougan and Paul Mackerras. + * + * Modified for PPC64 by Dave Engebretsen (engebret@ibm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) +extern void (*debugger)(struct pt_regs *); +extern void (*debugger_fault_handler)(struct pt_regs *); +extern int (*debugger_dabr_match)(struct pt_regs *); +int debugger_kernel_faults = 1; +#endif + +extern void die_if_kernel(char *, struct pt_regs *, long); +void bad_page_fault(struct pt_regs *, unsigned long); +void do_page_fault(struct pt_regs *, unsigned long, unsigned long); + +#ifdef CONFIG_PPCDBG +extern unsigned long get_srr0(void); +extern unsigned long get_srr1(void); +#endif + +/* + * For 600- and 800-family processors, the error_code parameter is DSISR + * for a data fault, SRR1 for an instruction fault. + */ +void do_page_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code) +{ + struct vm_area_struct * vma; + struct mm_struct *mm = current->mm; + siginfo_t info; + unsigned long code = SEGV_MAPERR; + unsigned long is_write = error_code & 0x02000000; + unsigned long mm_fault_return; + + PPCDBG(PPCDBG_MM, "Entering do_page_fault: addr = 0x%16.16lx, error_code = %lx\n\tregs_trap = %lx, srr0 = %lx, srr1 = %lx\n", address, error_code, regs->trap, get_srr0(), get_srr1()); + /* + * Fortunately the bit assignments in SRR1 for an instruction + * fault and DSISR for a data fault are mostly the same for the + * bits we are interested in. But there are some bits which + * indicate errors in DSISR but can validly be set in SRR1. + */ + if (regs->trap == 0x400) + error_code &= 0x48200000; + +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_fault_handler && (regs->trap == 0x300 || + regs->trap == 0x380)) { + debugger_fault_handler(regs); + return; + } + + if (error_code & 0x00400000) { + /* DABR match */ + if (debugger_dabr_match(regs)) + return; + } +#endif /* CONFIG_XMON || CONFIG_KGDB */ + + if (in_interrupt() || mm == NULL) { + bad_page_fault(regs, address); + return; + } + down_read(&mm->mmap_sem); + vma = find_vma(mm, address); + PPCDBG(PPCDBG_MM, "\tdo_page_fault: vma = 0x%16.16lx\n", vma); + if (!vma) { + PPCDBG(PPCDBG_MM, "\tdo_page_fault: !vma\n"); + goto bad_area; + } + PPCDBG(PPCDBG_MM, "\tdo_page_fault: vma->vm_start = 0x%16.16lx, vma->vm_flags = 0x%16.16lx\n", vma->vm_start, vma->vm_flags); + if (vma->vm_start <= address) { + goto good_area; + } + if (!(vma->vm_flags & VM_GROWSDOWN)) { + PPCDBG(PPCDBG_MM, "\tdo_page_fault: vma->vm_flags = %lx, %lx\n", vma->vm_flags, VM_GROWSDOWN); + goto bad_area; + } + if (expand_stack(vma, address)) { + PPCDBG(PPCDBG_MM, "\tdo_page_fault: expand_stack\n"); + goto bad_area; + } + +good_area: + code = SEGV_ACCERR; + + /* a write */ + if (is_write) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + /* a read */ + } else { + /* protection fault */ + if (error_code & 0x08000000) + goto bad_area; + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + PPCDBG(PPCDBG_MM, "\tdo_page_fault: calling handle_mm_fault\n"); + mm_fault_return = handle_mm_fault(mm, vma, address, is_write); + PPCDBG(PPCDBG_MM, "\tdo_page_fault: handle_mm_fault = 0x%lx\n", + mm_fault_return); + switch(mm_fault_return) { + case 1: + current->min_flt++; + break; + case 2: + current->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; + } + + up_read(&mm->mmap_sem); + return; + +bad_area: + up_read(&mm->mmap_sem); + + /* User mode accesses cause a SIGSEGV */ + if (user_mode(regs)) { + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = code; + info.si_addr = (void *) address; + PPCDBG(PPCDBG_SIGNAL, "Bad addr in user: 0x%lx\n", address); +#ifdef CONFIG_XMON + ifppcdebug(PPCDBG_SIGNALXMON) + PPCDBG_ENTER_DEBUGGER_REGS(regs); +#endif + + force_sig_info(SIGSEGV, &info, current); + return; + } + + bad_page_fault(regs, address); + return; + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + printk("VM: killing process %s\n", current->comm); + if (user_mode(regs)) + do_exit(SIGKILL); + bad_page_fault(regs, address); + return; + +do_sigbus: + up_read(&mm->mmap_sem); + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info (SIGBUS, &info, current); + if (!user_mode(regs)) + bad_page_fault(regs, address); +} + +/* + * bad_page_fault is called when we have a bad access from the kernel. + * It is called from do_page_fault above and from some of the procedures + * in traps.c. + */ +void +bad_page_fault(struct pt_regs *regs, unsigned long address) +{ + unsigned long fixup; + + /* Are we prepared to handle this fault? */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + + /* kernel has accessed a bad area */ + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_kernel_faults) + debugger(regs); +#endif + print_backtrace( (unsigned long *)regs->gpr[1] ); + panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d", + regs->nip,regs->link,address,current->comm,current->pid); +} + diff -Nru a/arch/ppc64/mm/imalloc.c b/arch/ppc64/mm/imalloc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/mm/imalloc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,71 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include + +#include +#include + +rwlock_t imlist_lock = RW_LOCK_UNLOCKED; +struct vm_struct * imlist = NULL; + +struct vm_struct *get_im_area(unsigned long size) +{ + unsigned long addr; + struct vm_struct **p, *tmp, *area; + + area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) + return NULL; + addr = IMALLOC_START; + write_lock(&imlist_lock); + for (p = &imlist; (tmp = *p) ; p = &tmp->next) { + if (size + addr < (unsigned long) tmp->addr) + break; + addr = tmp->size + (unsigned long) tmp->addr; + if (addr > IMALLOC_END-size) { + write_unlock(&imlist_lock); + kfree(area); + return NULL; + } + } + area->flags = 0; + area->addr = (void *)addr; + area->size = size; + area->next = *p; + *p = area; + write_unlock(&imlist_lock); + return area; +} + +void ifree(void * addr) +{ + struct vm_struct **p, *tmp; + + if (!addr) + return; + if ((PAGE_SIZE-1) & (unsigned long) addr) { + printk(KERN_ERR "Trying to ifree() bad address (%p)\n", addr); + return; + } + write_lock(&imlist_lock); + for (p = &imlist ; (tmp = *p) ; p = &tmp->next) { + if (tmp->addr == addr) { + *p = tmp->next; + kfree(tmp); + write_unlock(&imlist_lock); + return; + } + } + write_unlock(&imlist_lock); + printk(KERN_ERR "Trying to ifree() nonexistent area (%p)\n", addr); +} + diff -Nru a/arch/ppc64/mm/init.c b/arch/ppc64/mm/init.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/mm/init.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,663 @@ +/* + * + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * Dave Engebretsen + * Rework for PPC64 port. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BLK_DEV_INITRD +#include /* for initrd_* */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PPC_EEH +#include +#endif + +#include + +#define PGTOKB(pages) (((pages) * PAGE_SIZE) >> 10) + +#ifdef CONFIG_PPC_ISERIES +#include +#endif + +struct mmu_context_queue_t mmu_context_queue; +int mem_init_done; +unsigned long ioremap_bot = IMALLOC_BASE; + +static int boot_mapsize; +static unsigned long totalram_pages; + +extern pgd_t swapper_pg_dir[]; +extern char __init_begin, __init_end; +extern char __chrp_begin, __chrp_end; +extern char __openfirmware_begin, __openfirmware_end; +extern struct _of_tce_table of_tce_table[]; +extern char _start[], _end[]; +extern char _stext[], etext[]; +extern struct task_struct *current_set[NR_CPUS]; +extern struct Naca *naca; + +void mm_init_ppc64(void); + +unsigned long *pmac_find_end_of_memory(void); +extern unsigned long *find_end_of_memory(void); + +extern pgd_t ioremap_dir[]; +pgd_t * ioremap_pgd = (pgd_t *)&ioremap_dir; + +static void map_io_page(unsigned long va, unsigned long pa, int flags); +extern void die_if_kernel(char *,struct pt_regs *,long); + +unsigned long klimit = (unsigned long)_end; + +HPTE *Hash=0; +unsigned long Hash_size=0; +unsigned long _SDR1=0; +unsigned long _ASR=0; + +/* max amount of RAM to use */ +unsigned long __max_memory; + +/* This is declared as we are using the more or less generic + * include/asm-ppc64/tlb.h file -- tgall + */ +mmu_gather_t mmu_gathers[NR_CPUS]; + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + + if (pgtable_cache_size > high) { + do { + if (pgd_quicklist) + free_page((unsigned long)pgd_alloc_one_fast(0)), ++freed; + if (pmd_quicklist) + free_page((unsigned long)pmd_alloc_one_fast(0, 0)), ++freed; + if (pte_quicklist) + free_page((unsigned long)pte_alloc_one_fast(0, 0)), ++freed; + } while (pgtable_cache_size > low); + } + return freed; +} + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0, cached = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (!atomic_read(&mem_map[i].count)) + free++; + else + shared += atomic_read(&mem_map[i].count) - 1; + } + printk("%d pages of RAM\n",total); + printk("%d free pages\n",free); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%d pages in page table cache\n",(int)pgtable_cache_size); + show_buffers(); +} + +void si_meminfo(struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; +} + +void * +ioremap(unsigned long addr, unsigned long size) +{ +#ifdef CONFIG_PPC_ISERIES + return (void*)addr; +#else +#ifdef CONFIG_PPC_EEH + if(mem_init_done && (addr >> 60UL)) { + if (IS_EEH_TOKEN_DISABLED(addr)) + return IO_TOKEN_TO_ADDR(addr); + return (void*)addr; /* already mapped address or EEH token. */ + } +#endif + return __ioremap(addr, size, _PAGE_NO_CACHE); +#endif +} + +extern struct vm_struct * get_im_area( unsigned long size ); + +void * +__ioremap(unsigned long addr, unsigned long size, unsigned long flags) +{ + unsigned long pa, ea, i; + + /* + * Choose an address to map it to. + * Once the imalloc system is running, we use it. + * Before that, we map using addresses going + * up from ioremap_bot. imalloc will use + * the addresses from ioremap_bot through + * IMALLOC_END (0xE000001fffffffff) + * + */ + pa = addr & PAGE_MASK; + size = PAGE_ALIGN(addr + size) - pa; + + if (size == 0) + return NULL; + + if (mem_init_done) { + struct vm_struct *area; + area = get_im_area(size); + if (area == 0) + return NULL; + ea = (unsigned long)(area->addr); + } + else { + ea = ioremap_bot; + ioremap_bot += size; + } + + if ((flags & _PAGE_PRESENT) == 0) + flags |= pgprot_val(PAGE_KERNEL); + if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU)) + flags |= _PAGE_GUARDED; + + for (i = 0; i < size; i += PAGE_SIZE) { + map_io_page(ea+i, pa+i, flags); + } + + return (void *) (ea + (addr & ~PAGE_MASK)); +} + +void iounmap(void *addr) +{ +#ifdef CONFIG_PPC_ISERIES + /* iSeries I/O Remap is a noop */ + return; +#else + /* DRENG / PPPBBB todo */ + return; +#endif +} + +/* + * map_io_page currently only called by __ioremap + * map_io_page adds an entry to the ioremap page table + * and adds an entry to the HPT, possibly bolting it + */ +static void map_io_page(unsigned long ea, unsigned long pa, int flags) +{ + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + unsigned long vsid; + + if (mem_init_done) { + spin_lock(&ioremap_mm.page_table_lock); + pgdp = pgd_offset_i(ea); + pmdp = pmd_alloc(&ioremap_mm, pgdp, ea); + ptep = pte_alloc(&ioremap_mm, pmdp, ea); + + pa = absolute_to_phys(pa); + set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); + spin_unlock(&ioremap_mm.page_table_lock); + } else { + /* If the mm subsystem is not fully up, we cannot create a + * linux page table entry for this mapping. Simply bolt an + * entry in the hardware page table. + */ + vsid = get_kernel_vsid(ea); + make_pte(htab_data.htab, + (vsid << 28) | (ea & 0xFFFFFFF), // va (NOT the ea) + pa, + _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX, + htab_data.htab_hash_mask, 0); + } +} + +void +local_flush_tlb_all(void) +{ + /* Implemented to just flush the vmalloc area. + * vmalloc is the only user of flush_tlb_all. + */ + local_flush_tlb_range( NULL, VMALLOC_START, VMALLOC_END ); +} + +void +local_flush_tlb_mm(struct mm_struct *mm) +{ + if ( mm->map_count ) { + struct vm_area_struct *mp; + for ( mp = mm->mmap; mp != NULL; mp = mp->vm_next ) + local_flush_tlb_range( mm, mp->vm_start, mp->vm_end ); + } + else /* MIKEC: It is not clear why this is needed */ + /* paulus: it is needed to clear out stale HPTEs + * when an address space (represented by an mm_struct) + * is being destroyed. */ + local_flush_tlb_range( mm, USER_START, USER_END ); +} + + +/* + * Callers should hold the mm->page_table_lock + */ +void +local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) +{ + unsigned long context = 0; + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep; + pte_t pte; + + switch( REGION_ID(vmaddr) ) { + case VMALLOC_REGION_ID: + pgd = pgd_offset_k( vmaddr ); + break; + case IO_REGION_ID: + pgd = pgd_offset_i( vmaddr ); + break; + case USER_REGION_ID: + pgd = pgd_offset( vma->vm_mm, vmaddr ); + context = vma->vm_mm->context; + break; + default: + panic("local_flush_tlb_page: invalid region 0x%016lx", vmaddr); + + } + + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, vmaddr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, vmaddr); + /* Check if HPTE might exist and flush it if so */ + pte = __pte(pte_update(ptep, _PAGE_HPTEFLAGS, 0)); + if ( pte_val(pte) & _PAGE_HASHPTE ) { + flush_hash_page(context, vmaddr, pte); + } + } + } +} + +void +local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep; + pte_t pte; + unsigned long pgd_end, pmd_end; + unsigned long context; + + if ( start >= end ) + panic("flush_tlb_range: start (%016lx) greater than end (%016lx)\n", start, end ); + + if ( REGION_ID(start) != REGION_ID(end) ) + panic("flush_tlb_range: start (%016lx) and end (%016lx) not in same region\n", start, end ); + + context = 0; + + switch( REGION_ID(start) ) { + case VMALLOC_REGION_ID: + pgd = pgd_offset_k( start ); + break; + case IO_REGION_ID: + pgd = pgd_offset_i( start ); + break; + case USER_REGION_ID: + pgd = pgd_offset( mm, start ); + context = mm->context; + break; + default: + panic("flush_tlb_range: invalid region for start (%016lx) and end (%016lx)\n", start, end); + + } + + do { + pgd_end = (start + PGDIR_SIZE) & PGDIR_MASK; + if ( pgd_end > end ) + pgd_end = end; + if ( !pgd_none( *pgd ) ) { + pmd = pmd_offset( pgd, start ); + do { + pmd_end = ( start + PMD_SIZE ) & PMD_MASK; + if ( pmd_end > end ) + pmd_end = end; + if ( !pmd_none( *pmd ) ) { + ptep = pte_offset( pmd, start ); + do { + if ( pte_val(*ptep) & _PAGE_HASHPTE ) { + pte = __pte(pte_update(ptep, _PAGE_HPTEFLAGS, 0)); + if ( pte_val(pte) & _PAGE_HASHPTE ) + flush_hash_page( context, start, pte ); + } + start += PAGE_SIZE; + ++ptep; + } while ( start < pmd_end ); + } + else + start = pmd_end; + ++pmd; + } while ( start < pgd_end ); + } + else + start = pgd_end; + ++pgd; + } while ( start < end ); +} + + +void __init free_initmem(void) +{ + unsigned long a; + unsigned long num_freed_pages = 0; +#define FREESEC(START,END,CNT) do { \ + a = (unsigned long)(&START); \ + for (; a < (unsigned long)(&END); a += PAGE_SIZE) { \ + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); \ + set_page_count(mem_map+MAP_NR(a), 1); \ + free_page(a); \ + CNT++; \ + } \ +} while (0) + + FREESEC(__init_begin,__init_end,num_freed_pages); + + printk ("Freeing unused kernel memory: %ldk init\n", + PGTOKB(num_freed_pages)); +} + +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + unsigned long xstart = start; + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(mem_map + MAP_NR(start)); + set_page_count(mem_map+MAP_NR(start), 1); + free_page(start); + totalram_pages++; + } + printk ("Freeing initrd memory: %ldk freed\n", (end - xstart) >> 10); +} +#endif + + + +/* + * Do very early mm setup. + */ +void __init mm_init_ppc64(void) { + struct Paca *paca; + unsigned long guard_page, index; + + ppc_md.progress("MM:init", 0); + + /* Reserve all contexts < FIRST_USER_CONTEXT for kernel use. + * The range of contexts [FIRST_USER_CONTEXT, NUM_USER_CONTEXT) + * are stored on a stack/queue for easy allocation and deallocation. + */ + mmu_context_queue.lock = SPIN_LOCK_UNLOCKED; + mmu_context_queue.head = 0; + mmu_context_queue.tail = NUM_USER_CONTEXT-1; + mmu_context_queue.size = NUM_USER_CONTEXT; + for(index=0; index < NUM_USER_CONTEXT ;index++) { + mmu_context_queue.elements[index] = index+FIRST_USER_CONTEXT; + } + + /* Setup guard pages for the Paca's */ + for (index = 0; index < NR_CPUS; index++) { + paca = &xPaca[index]; + guard_page = ((unsigned long)paca) + 0x1000; + ppc_md.hpte_updateboltedpp(PP_RXRX, guard_page); + } + + ppc_md.progress("MM:exit", 0x211); +} + + + +/* + * Initialize the bootmem system and give it all the memory we + * have available. + */ +void __init do_init_bootmem(void) +{ + unsigned long i; + unsigned long start, bootmap_pages; + unsigned long total_pages = lmb_end_of_DRAM() >> PAGE_SHIFT; + + PPCDBG(PPCDBG_MMINIT, "do_init_bootmem: start\n"); + /* + * Find an area to use for the bootmem bitmap. Calculate the size of + * bitmap required as (Total Memory) / PAGE_SIZE / BITS_PER_BYTE. + * Add 1 additional page in case the address isn't page-aligned. + */ + bootmap_pages = bootmem_bootmap_pages(total_pages); + + start = (unsigned long)__a2p(lmb_alloc(bootmap_pages<physicalMemorySize); + + boot_mapsize = init_bootmem(start >> PAGE_SHIFT, total_pages); + PPCDBG(PPCDBG_MMINIT, "\tboot_mapsize = 0x%lx\n", boot_mapsize); + + /* add all physical memory to the bootmem map */ + for (i=0; i < lmb.memory.cnt ;i++) { + unsigned long physbase = lmb.memory.region[i].physbase; + unsigned long size = lmb.memory.region[i].size; + free_bootmem(physbase, size); + } + /* reserve the sections we're already using */ + for (i=0; i < lmb.reserved.cnt ;i++) { + unsigned long physbase = lmb.reserved.region[i].physbase; + unsigned long size = lmb.reserved.region[i].size; +#if 0 /* PPPBBB */ + if ( (physbase == 0) && (size < (16<<20)) ) { + size = 16 << 20; + } +#endif + reserve_bootmem(physbase, size); + } + + PPCDBG(PPCDBG_MMINIT, "do_init_bootmem: end\n"); +} + +/* + * paging_init() sets up the page tables - in fact we've already done this. + */ +void __init paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES], i; + + /* + * All pages are DMA-able so we put them all in the DMA zone. + */ + zones_size[0] = lmb_end_of_DRAM() >> PAGE_SHIFT; + for (i = 1; i < MAX_NR_ZONES; i++) + zones_size[i] = 0; + free_area_init(zones_size); +} + +extern unsigned long prof_shift; +extern unsigned long prof_len; +extern unsigned int * prof_buffer; +extern unsigned long dprof_shift; +extern unsigned long dprof_len; +extern unsigned int * dprof_buffer; + +void __init mem_init(void) +{ + extern char *sysmap; + extern unsigned long sysmap_size; + unsigned long addr; + int codepages = 0; + int datapages = 0; + int initpages = 0; + unsigned long va_rtas_base = (unsigned long)__va(rtas.base); + + max_mapnr = max_low_pfn; + high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); + num_physpages = max_mapnr; /* RAM is assumed contiguous */ + max_pfn = max_low_pfn; + + totalram_pages += free_all_bootmem(); + + ifppcdebug(PPCDBG_MMINIT) { + udbg_printf("mem_init: totalram_pages = 0x%lx\n", totalram_pages); + udbg_printf("mem_init: va_rtas_base = 0x%lx\n", va_rtas_base); + udbg_printf("mem_init: va_rtas_end = 0x%lx\n", PAGE_ALIGN(va_rtas_base+rtas.size)); + udbg_printf("mem_init: pinned start = 0x%lx\n", __va(0)); + udbg_printf("mem_init: pinned end = 0x%lx\n", PAGE_ALIGN(klimit)); + } + + if ( sysmap_size ) + for (addr = (unsigned long)sysmap; + addr < PAGE_ALIGN((unsigned long)sysmap+sysmap_size) ; + addr += PAGE_SIZE) + SetPageReserved(mem_map + MAP_NR(addr)); + + for (addr = KERNELBASE; addr <= (unsigned long)__va(lmb_end_of_DRAM()); + addr += PAGE_SIZE) { + if (!PageReserved(mem_map + MAP_NR(addr))) + continue; + if (addr < (ulong) etext) + codepages++; + + else if (addr >= (unsigned long)&__init_begin + && addr < (unsigned long)&__init_end) + initpages++; + else if (addr < klimit) + datapages++; + } + + printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n", + (unsigned long)nr_free_pages()<< (PAGE_SHIFT-10), + codepages<< (PAGE_SHIFT-10), datapages<< (PAGE_SHIFT-10), + initpages<< (PAGE_SHIFT-10), + PAGE_OFFSET, (unsigned long)__va(lmb_end_of_DRAM())); + mem_init_done = 1; + + /* set the last page of each hardware interrupt stack to be protected */ + initialize_paca_hardware_interrupt_stack(); + +#ifdef CONFIG_PPC_ISERIES + create_virtual_bus_tce_table(); + /* HACK HACK This allows the iSeries profiling to use /proc/profile */ + prof_shift = dprof_shift; + prof_len = dprof_len; + prof_buffer = dprof_buffer; +#endif +} + +/* + * This is called when a page has been modified by the kernel. + * It just marks the page as not i-cache clean. We do the i-cache + * flush later when the page is given to a user process, if necessary. + */ +void flush_dcache_page(struct page *page) +{ + clear_bit(PG_arch_1, &page->flags); +} + +void flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + if (page->mapping && !PageReserved(page) + && !test_bit(PG_arch_1, &page->flags)) { + __flush_dcache_icache(page_address(page)); + set_bit(PG_arch_1, &page->flags); + } +} + +void clear_user_page(void *page, unsigned long vaddr) +{ + clear_page(page); +} + +void copy_user_page(void *vto, void *vfrom, unsigned long vaddr) +{ + copy_page(vto, vfrom); + __flush_dcache_icache(vto); +} + +void flush_icache_user_range(struct vm_area_struct *vma, struct page *page, + unsigned long addr, int len) +{ + unsigned long maddr; + + maddr = (unsigned long)page_address(page) + (addr & ~PAGE_MASK); + flush_icache_range(maddr, maddr + len); +} diff -Nru a/arch/ppc64/vmlinux.lds b/arch/ppc64/vmlinux.lds --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/vmlinux.lds Tue Feb 19 18:09:00 2002 @@ -0,0 +1,150 @@ +OUTPUT_ARCH(powerpc) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } +/* .init : { *(.init) } =0*/ + .plt : { *(.plt) } + .text : + { + *(.text) + *(.fixup) + *(.got1) + } + . = ALIGN(4096); + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.rodata) + *(.rodata1) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFFFFFFFFFF000; + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + CONSTRUCTORS + } + . = ALIGN(4096); + _edata = .; + PROVIDE (edata = .); + + .fixup : { *(.fixup) } + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + + . = ALIGN(16384); /* init_task */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(4096); + .data.page_aligned : { *(.data.page_aligned) } + + . = ALIGN(128); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { + *(.data.init); + __vtop_table_begin = .; + *(.vtop_fixup); + __vtop_table_end = .; + __ptov_table_begin = .; + *(.ptov_fixup); + __ptov_table_end = .; + } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { + *(.initcall1.init) + *(.initcall2.init) + *(.initcall3.init) + *(.initcall4.init) + *(.initcall5.init) + *(.initcall6.init) + *(.initcall7.init) + } + __initcall_end = .; + + + . = ALIGN(4096); + __init_end = .; + + __chrp_begin = .; + .text.chrp : { *(.text.chrp) } + .data.chrp : { *(.data.chrp) } + . = ALIGN(4096); + __chrp_end = .; + + . = ALIGN(4096); + __openfirmware_begin = .; + .text.openfirmware : { *(.text.openfirmware) } + .data.openfirmware : { *(.data.openfirmware) } + . = ALIGN(4096); + __openfirmware_end = .; + + __toc_start = .; + .toc : + { + *(.toc) + } + . = ALIGN(4096); + __toc_end = .; + + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + + . = ALIGN(4096); + _end = . ; + PROVIDE (end = .); +} diff -Nru a/arch/ppc64/xmon/Makefile b/arch/ppc64/xmon/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,9 @@ +# Makefile for xmon + +EXTRA_CFLAGS = -mno-minimal-toc + +O_TARGET = x.o + +obj-y := start.o xmon.o ppc-dis.o ppc-opc.o subr_prf.o setjmp.o + +include $(TOPDIR)/Rules.make diff -Nru a/arch/ppc64/xmon/ansidecl.h b/arch/ppc64/xmon/ansidecl.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/ansidecl.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,141 @@ +/* ANSI and traditional C compatability macros + Copyright 1991, 1992 Free Software Foundation, Inc. + This file is part of the GNU C Library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* ANSI and traditional C compatibility macros + + ANSI C is assumed if __STDC__ is #defined. + + Macro ANSI C definition Traditional C definition + ----- ---- - ---------- ----------- - ---------- + PTR `void *' `char *' + LONG_DOUBLE `long double' `double' + VOLATILE `volatile' `' + SIGNED `signed' `' + PTRCONST `void *const' `char *' + ANSI_PROTOTYPES 1 not defined + + CONST is also defined, but is obsolete. Just use const. + + DEFUN (name, arglist, args) + + Defines function NAME. + + ARGLIST lists the arguments, separated by commas and enclosed in + parentheses. ARGLIST becomes the argument list in traditional C. + + ARGS list the arguments with their types. It becomes a prototype in + ANSI C, and the type declarations in traditional C. Arguments should + be separated with `AND'. For functions with a variable number of + arguments, the last thing listed should be `DOTS'. + + DEFUN_VOID (name) + + Defines a function NAME, which takes no arguments. + + obsolete -- EXFUN (name, (prototype)) -- obsolete. + + Replaced by PARAMS. Do not use; will disappear someday soon. + Was used in external function declarations. + In ANSI C it is `NAME PROTOTYPE' (so PROTOTYPE should be enclosed in + parentheses). In traditional C it is `NAME()'. + For a function that takes no arguments, PROTOTYPE should be `(void)'. + + PARAMS ((args)) + + We could use the EXFUN macro to handle prototype declarations, but + the name is misleading and the result is ugly. So we just define a + simple macro to handle the parameter lists, as in: + + static int foo PARAMS ((int, char)); + + This produces: `static int foo();' or `static int foo (int, char);' + + EXFUN would have done it like this: + + static int EXFUN (foo, (int, char)); + + but the function is not external...and it's hard to visually parse + the function name out of the mess. EXFUN should be considered + obsolete; new code should be written to use PARAMS. + + For example: + extern int printf PARAMS ((CONST char *format DOTS)); + int DEFUN(fprintf, (stream, format), + FILE *stream AND CONST char *format DOTS) { ... } + void DEFUN_VOID(abort) { ... } +*/ + +#ifndef _ANSIDECL_H + +#define _ANSIDECL_H 1 + + +/* Every source file includes this file, + so they will all get the switch for lint. */ +/* LINTLIBRARY */ + + +#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32) +/* All known AIX compilers implement these things (but don't always + define __STDC__). The RISC/OS MIPS compiler defines these things + in SVR4 mode, but does not define __STDC__. */ + +#define PTR void * +#define PTRCONST void *CONST +#define LONG_DOUBLE long double + +#define AND , +#define NOARGS void +#define CONST const +#define VOLATILE volatile +#define SIGNED signed +#define DOTS , ... + +#define EXFUN(name, proto) name proto +#define DEFUN(name, arglist, args) name(args) +#define DEFUN_VOID(name) name(void) + +#define PROTO(type, name, arglist) type name arglist +#define PARAMS(paramlist) paramlist +#define ANSI_PROTOTYPES 1 + +#else /* Not ANSI C. */ + +#define PTR char * +#define PTRCONST PTR +#define LONG_DOUBLE double + +#define AND ; +#define NOARGS +#define CONST +#ifndef const /* some systems define it in header files for non-ansi mode */ +#define const +#endif +#define VOLATILE +#define SIGNED +#define DOTS + +#define EXFUN(name, proto) name() +#define DEFUN(name, arglist, args) name arglist args; +#define DEFUN_VOID(name) name() +#define PROTO(type, name, arglist) type name () +#define PARAMS(paramlist) () + +#endif /* ANSI C. */ + +#endif /* ansidecl.h */ diff -Nru a/arch/ppc64/xmon/nonstdio.h b/arch/ppc64/xmon/nonstdio.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/nonstdio.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,22 @@ +typedef int FILE; +extern FILE *xmon_stdin, *xmon_stdout; +#define EOF (-1) +#define stdin xmon_stdin +#define stdout xmon_stdout +#define printf xmon_printf +#define fprintf xmon_fprintf +#define fputs xmon_fputs +#define fgets xmon_fgets +#define putchar xmon_putchar +#define getchar xmon_getchar +#define putc xmon_putc +#define getc xmon_getc +#define fopen(n, m) NULL +#define fflush(f) do {} while (0) +#define fclose(f) do {} while (0) +extern char *fgets(char *, int, void *); +extern void xmon_printf(const char *, ...); +extern void xmon_fprintf(void *, const char *, ...); +extern void xmon_sprintf(char *, const char *, ...); + +#define perror(s) printf("%s: no files!\n", (s)) diff -Nru a/arch/ppc64/xmon/ppc-dis.c b/arch/ppc64/xmon/ppc-dis.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/ppc-dis.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,190 @@ +/* ppc-dis.c -- Disassemble PowerPC instructions + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "nonstdio.h" +#include "ansidecl.h" +#include "ppc.h" + +static int print_insn_powerpc PARAMS ((FILE *, unsigned long insn, + unsigned long memaddr, int dialect)); + +extern void print_address PARAMS((unsigned long memaddr)); + +/* Print a big endian PowerPC instruction. For convenience, also + disassemble instructions supported by the Motorola PowerPC 601. */ + +int +print_insn_big_powerpc (FILE *out, unsigned long insn, unsigned long memaddr) +{ + return print_insn_powerpc (out, insn, memaddr, + PPC_OPCODE_PPC | PPC_OPCODE_601); +} + +/* Print a PowerPC or POWER instruction. */ + +static int +print_insn_powerpc (FILE *out, unsigned long insn, unsigned long memaddr, + int dialect) +{ + const struct powerpc_opcode *opcode; + const struct powerpc_opcode *opcode_end; + unsigned long op; + + /* Get the major opcode of the instruction. */ + op = PPC_OP (insn); + + /* Find the first match in the opcode table. We could speed this up + a bit by doing a binary search on the major opcode. */ + opcode_end = powerpc_opcodes + powerpc_num_opcodes; + for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) + { + unsigned long table_op; + const unsigned char *opindex; + const struct powerpc_operand *operand; + int invalid; + int need_comma; + int need_paren; + + table_op = PPC_OP (opcode->opcode); + if (op < table_op) + break; + if (op > table_op) + continue; + + if ((insn & opcode->mask) != opcode->opcode + || (opcode->flags & dialect) == 0) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + invalid = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + operand = powerpc_operands + *opindex; + if (operand->extract) + (*operand->extract) (insn, &invalid); + } + if (invalid) + continue; + + /* The instruction is valid. */ + fprintf(out, "%s", opcode->name); + if (opcode->operands[0] != 0) + fprintf(out, "\t"); + + /* Now extract and print the operands. */ + need_comma = 0; + need_paren = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + long value; + + operand = powerpc_operands + *opindex; + + /* Operands that are marked FAKE are simply ignored. We + already made sure that the extract function considered + the instruction to be valid. */ + if ((operand->flags & PPC_OPERAND_FAKE) != 0) + continue; + + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, (int *) 0); + else + { + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0 + && (value & (1 << (operand->bits - 1))) != 0) + value -= 1 << operand->bits; + } + + /* If the operand is optional, and the value is zero, don't + print anything. */ + if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 + && (operand->flags & PPC_OPERAND_NEXT) == 0 + && value == 0) + continue; + + if (need_comma) + { + fprintf(out, ","); + need_comma = 0; + } + + /* Print the operand as directed by the flags. */ + if ((operand->flags & PPC_OPERAND_GPR) != 0) + fprintf(out, "r%ld", value); + else if ((operand->flags & PPC_OPERAND_FPR) != 0) + fprintf(out, "f%ld", value); + else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) + print_address (memaddr + value); + else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) + print_address (value & 0xffffffff); + else if ((operand->flags & PPC_OPERAND_CR) == 0 + || (dialect & PPC_OPCODE_PPC) == 0) + fprintf(out, "%ld", value); + else + { + if (operand->bits == 3) + fprintf(out, "cr%d", value); + else + { + static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; + int cr; + int cc; + + cr = value >> 2; + if (cr != 0) + fprintf(out, "4*cr%d", cr); + cc = value & 3; + if (cc != 0) + { + if (cr != 0) + fprintf(out, "+"); + fprintf(out, "%s", cbnames[cc]); + } + } + } + + if (need_paren) + { + fprintf(out, ")"); + need_paren = 0; + } + + if ((operand->flags & PPC_OPERAND_PARENS) == 0) + need_comma = 1; + else + { + fprintf(out, "("); + need_paren = 1; + } + } + + /* We have found and printed an instruction; return. */ + return 4; + } + + /* We could not find a match. */ + fprintf(out, ".long 0x%lx", insn); + + return 4; +} diff -Nru a/arch/ppc64/xmon/ppc-opc.c b/arch/ppc64/xmon/ppc-opc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/ppc-opc.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,2816 @@ +/* ppc-opc.c -- PowerPC opcode list + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include +#include "ansidecl.h" +#include "ppc.h" + +/* This file holds the PowerPC opcode table. The opcode table + includes almost all of the extended instruction mnemonics. This + permits the disassembler to use them, and simplifies the assembler + logic, at the cost of increasing the table size. The table is + strictly constant data, so the compiler should be able to put it in + the .text section. + + This file also holds the operand table. All knowledge about + inserting operands into instructions and vice-versa is kept in this + file. */ + +/* Local insertion and extraction functions. */ + +static unsigned long insert_bat PARAMS ((unsigned long, long, const char **)); +static long extract_bat PARAMS ((unsigned long, int *)); +static unsigned long insert_bba PARAMS ((unsigned long, long, const char **)); +static long extract_bba PARAMS ((unsigned long, int *)); +static unsigned long insert_bd PARAMS ((unsigned long, long, const char **)); +static long extract_bd PARAMS ((unsigned long, int *)); +static unsigned long insert_bdm PARAMS ((unsigned long, long, const char **)); +static long extract_bdm PARAMS ((unsigned long, int *)); +static unsigned long insert_bdp PARAMS ((unsigned long, long, const char **)); +static long extract_bdp PARAMS ((unsigned long, int *)); +static unsigned long insert_bo PARAMS ((unsigned long, long, const char **)); +static long extract_bo PARAMS ((unsigned long, int *)); +static unsigned long insert_boe PARAMS ((unsigned long, long, const char **)); +static long extract_boe PARAMS ((unsigned long, int *)); +static unsigned long insert_ds PARAMS ((unsigned long, long, const char **)); +static long extract_ds PARAMS ((unsigned long, int *)); +static unsigned long insert_li PARAMS ((unsigned long, long, const char **)); +static long extract_li PARAMS ((unsigned long, int *)); +static unsigned long insert_mbe PARAMS ((unsigned long, long, const char **)); +static long extract_mbe PARAMS ((unsigned long, int *)); +static unsigned long insert_mb6 PARAMS ((unsigned long, long, const char **)); +static long extract_mb6 PARAMS ((unsigned long, int *)); +static unsigned long insert_nb PARAMS ((unsigned long, long, const char **)); +static long extract_nb PARAMS ((unsigned long, int *)); +static unsigned long insert_nsi PARAMS ((unsigned long, long, const char **)); +static long extract_nsi PARAMS ((unsigned long, int *)); +static unsigned long insert_ral PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ram PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ras PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_rbs PARAMS ((unsigned long, long, const char **)); +static long extract_rbs PARAMS ((unsigned long, int *)); +static unsigned long insert_sh6 PARAMS ((unsigned long, long, const char **)); +static long extract_sh6 PARAMS ((unsigned long, int *)); +static unsigned long insert_spr PARAMS ((unsigned long, long, const char **)); +static long extract_spr PARAMS ((unsigned long, int *)); +static unsigned long insert_tbr PARAMS ((unsigned long, long, const char **)); +static long extract_tbr PARAMS ((unsigned long, int *)); + +/* The operands table. + + The fields are bits, shift, signed, insert, extract, flags. */ + +const struct powerpc_operand powerpc_operands[] = +{ + /* The zero index is used to indicate the end of the list of + operands. */ +#define UNUSED (0) + { 0, 0, 0, 0, 0 }, + + /* The BA field in an XL form instruction. */ +#define BA (1) +#define BA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BA field in an XL form instruction when it must be the same + as the BT field in the same instruction. */ +#define BAT (2) + { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE }, + + /* The BB field in an XL form instruction. */ +#define BB (3) +#define BB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_CR }, + + /* The BB field in an XL form instruction when it must be the same + as the BA field in the same instruction. */ +#define BBA (4) + { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE }, + + /* The BD field in a B form instruction. The lower two bits are + forced to zero. */ +#define BD (5) + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when absolute addressing is + used. */ +#define BDA (6) + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDM (7) + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used + and absolute address is used. */ +#define BDMA (8) + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDP (9) + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used + and absolute addressing is used. */ +#define BDPA (10) + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BF field in an X or XL form instruction. */ +#define BF (11) + { 3, 23, 0, 0, PPC_OPERAND_CR }, + + /* An optional BF field. This is used for comparison instructions, + in which an omitted BF field is taken as zero. */ +#define OBF (12) + { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The BFA field in an X or XL form instruction. */ +#define BFA (13) + { 3, 18, 0, 0, PPC_OPERAND_CR }, + + /* The BI field in a B form or XL form instruction. */ +#define BI (14) +#define BI_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BO field in a B form instruction. Certain values are + illegal. */ +#define BO (15) +#define BO_MASK (0x1f << 21) + { 5, 21, insert_bo, extract_bo, 0 }, + + /* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. */ +#define BOE (16) + { 5, 21, insert_boe, extract_boe, 0 }, + + /* The BT field in an X or XL form instruction. */ +#define BT (17) + { 5, 21, 0, 0, PPC_OPERAND_CR }, + + /* The condition register number portion of the BI field in a B form + or XL form instruction. This is used for the extended + conditional branch mnemonics, which set the lower two bits of the + BI field. This field is optional. */ +#define CR (18) + { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The D field in a D form instruction. This is a displacement off + a register, and implies that the next operand is a register in + parentheses. */ +#define D (19) + { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ +#define DS (20) + { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The FL1 field in a POWER SC form instruction. */ +#define FL1 (21) + { 4, 12, 0, 0, 0 }, + + /* The FL2 field in a POWER SC form instruction. */ +#define FL2 (22) + { 3, 2, 0, 0, 0 }, + + /* The FLM field in an XFL form instruction. */ +#define FLM (23) + { 8, 17, 0, 0, 0 }, + + /* The FRA field in an X or A form instruction. */ +#define FRA (24) +#define FRA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_FPR }, + + /* The FRB field in an X or A form instruction. */ +#define FRB (25) +#define FRB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_FPR }, + + /* The FRC field in an A form instruction. */ +#define FRC (26) +#define FRC_MASK (0x1f << 6) + { 5, 6, 0, 0, PPC_OPERAND_FPR }, + + /* The FRS field in an X form instruction or the FRT field in a D, X + or A form instruction. */ +#define FRS (27) +#define FRT (FRS) + { 5, 21, 0, 0, PPC_OPERAND_FPR }, + + /* The FXM field in an XFX instruction. */ +#define FXM (28) +#define FXM_MASK (0xff << 12) + { 8, 12, 0, 0, 0 }, + + /* The L field in a D or X form instruction. */ +#define L (29) + { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL }, + + /* The LEV field in a POWER SC form instruction. */ +#define LEV (30) + { 7, 5, 0, 0, 0 }, + + /* The LI field in an I form instruction. The lower two bits are + forced to zero. */ +#define LI (31) + { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The LI field in an I form instruction when used as an absolute + address. */ +#define LIA (32) + { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The MB field in an M form instruction. */ +#define MB (33) +#define MB_MASK (0x1f << 6) + { 5, 6, 0, 0, 0 }, + + /* The ME field in an M form instruction. */ +#define ME (34) +#define ME_MASK (0x1f << 1) + { 5, 1, 0, 0, 0 }, + + /* The MB and ME fields in an M form instruction expressed a single + operand which is a bitmask indicating which bits to select. This + is a two operand form using PPC_OPERAND_NEXT. See the + description in opcode/ppc.h for what this means. */ +#define MBE (35) + { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT }, + { 32, 0, insert_mbe, extract_mbe, 0 }, + + /* The MB or ME field in an MD or MDS form instruction. The high + bit is wrapped to the low end. */ +#define MB6 (37) +#define ME6 (MB6) +#define MB6_MASK (0x3f << 5) + { 6, 5, insert_mb6, extract_mb6, 0 }, + + /* The NB field in an X form instruction. The value 32 is stored as + 0. */ +#define NB (38) + { 6, 11, insert_nb, extract_nb, 0 }, + + /* The NSI field in a D form instruction. This is the same as the + SI field, only negated. */ +#define NSI (39) + { 16, 0, insert_nsi, extract_nsi, + PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED }, + + /* The RA field in an D, DS, X, XO, M, or MDS form instruction. */ +#define RA (40) +#define RA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ +#define RAL (41) + { 5, 16, insert_ral, 0, PPC_OPERAND_GPR }, + + /* The RA field in an lmw instruction, which has special value + restrictions. */ +#define RAM (42) + { 5, 16, insert_ram, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ +#define RAS (43) + { 5, 16, insert_ras, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X, XO, M, or MDS form instruction. */ +#define RB (44) +#define RB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. */ +#define RBS (45) + { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE }, + + /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form + instruction or the RT field in a D, DS, X, XFX or XO form + instruction. */ +#define RS (46) +#define RT (RS) +#define RT_MASK (0x1f << 21) + { 5, 21, 0, 0, PPC_OPERAND_GPR }, + + /* The SH field in an X or M form instruction. */ +#define SH (47) +#define SH_MASK (0x1f << 11) + { 5, 11, 0, 0, 0 }, + + /* The SH field in an MD form instruction. This is split. */ +#define SH6 (48) +#define SH6_MASK ((0x1f << 11) | (1 << 1)) + { 6, 1, insert_sh6, extract_sh6, 0 }, + + /* The SI field in a D form instruction. */ +#define SI (49) + { 16, 0, 0, 0, PPC_OPERAND_SIGNED }, + + /* The SI field in a D form instruction when we accept a wide range + of positive values. */ +#define SISIGNOPT (50) + { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT }, + + /* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ +#define SPR (51) +#define SPR_MASK (0x3ff << 11) + { 10, 11, insert_spr, extract_spr, 0 }, + + /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */ +#define SPRBAT (52) +#define SPRBAT_MASK (0x3 << 17) + { 2, 17, 0, 0, 0 }, + + /* The SPRG register number in an XFX form m[ft]sprg instruction. */ +#define SPRG (53) +#define SPRG_MASK (0x3 << 16) + { 2, 16, 0, 0, 0 }, + + /* The SR field in an X form instruction. */ +#define SR (54) + { 4, 16, 0, 0, 0 }, + + /* The SV field in a POWER SC form instruction. */ +#define SV (55) + { 14, 2, 0, 0, 0 }, + + /* The TBR field in an XFX form instruction. This is like the SPR + field, but it is optional. */ +#define TBR (56) + { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL }, + + /* The TO field in a D or X form instruction. */ +#define TO (57) +#define TO_MASK (0x1f << 21) + { 5, 21, 0, 0, 0 }, + + /* The U field in an X form instruction. */ +#define U (58) + { 4, 12, 0, 0, 0 }, + + /* The UI field in a D form instruction. */ +#define UI (59) + { 16, 0, 0, 0, 0 }, +}; + +/* The functions used to insert and extract complicated operands. */ + +/* The BA field in an XL form instruction when it must be the same as + the BT field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BT field into the BA field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bat (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 21) & 0x1f) << 16); +} + +static long +extract_bat (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BB field in an XL form instruction when it must be the same as + the BA field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BA field into the BB field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bba (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 16) & 0x1f) << 11); +} + +static long +extract_bba (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BD field in a B form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_bd (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_bd (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the - modifier is used. + This modifier means that the branch is not expected to be taken. + We must set the y bit of the BO field to 1 if the offset is + negative. When extracting, we require that the y bit be 1 and that + the offset be positive, since if the y bit is 0 we just want to + print the normal form of the instruction. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdm (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((value & 0x8000) != 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdm (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) == 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the + modifier is used. + This is like BDM, above, except that the branch is expected to be + taken. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdp (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((value & 0x8000) == 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdp (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) != 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* Check for legal values of a BO field. */ + +static int +valid_bo (long value) +{ + /* Certain encodings have bits that are required to be zero. These + are (z must be zero, y may be anything): + 001zy + 011zy + 1z00y + 1z01y + 1z1zz + */ + switch (value & 0x14) + { + default: + case 0: + return 1; + case 0x4: + return (value & 0x2) == 0; + case 0x10: + return (value & 0x8) == 0; + case 0x14: + return value == 0x14; + } +} + +/* The BO field in a B form instruction. Warn about attempts to set + the field to an illegal value. */ + +static unsigned long +insert_bo (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL + && ! valid_bo (value)) + *errmsg = "invalid conditional option"; + return insn | ((value & 0x1f) << 21); +} + +static long +extract_bo (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value; +} + +/* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. When + extracting it, we force it to be even. */ + +static unsigned long +insert_boe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL) + { + if (! valid_bo (value)) + *errmsg = "invalid conditional option"; + else if ((value & 1) != 0) + *errmsg = "attempt to set y bit when using + or - modifier"; + } + return insn | ((value & 0x1f) << 21); +} + +static long +extract_boe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value & 0x1e; +} + +/* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_ds (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_ds (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The LI field in an I form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_li (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0x3fffffc); +} + +/*ARGSUSED*/ +static long +extract_li (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x2000000) != 0) + return (insn & 0x3fffffc) - 0x4000000; + else + return insn & 0x3fffffc; +} + +/* The MB and ME fields in an M form instruction expressed as a single + operand which is itself a bitmask. The extraction function always + marks it as invalid, since we never want to recognize an + instruction which uses a field of this type. */ + +static unsigned long +insert_mbe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + unsigned long uval; + int mb, me; + + uval = value; + + if (uval == 0) + { + if (errmsg != (const char **) NULL) + *errmsg = "illegal bitmask"; + return insn; + } + + me = 31; + while ((uval & 1) == 0) + { + uval >>= 1; + --me; + } + + mb = me; + uval >>= 1; + while ((uval & 1) != 0) + { + uval >>= 1; + --mb; + } + + if (uval != 0) + { + if (errmsg != (const char **) NULL) + *errmsg = "illegal bitmask"; + } + + return insn | (mb << 6) | (me << 1); +} + +static long +extract_mbe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + int mb, me; + int i; + + if (invalid != (int *) NULL) + *invalid = 1; + + ret = 0; + mb = (insn >> 6) & 0x1f; + me = (insn >> 1) & 0x1f; + for (i = mb; i < me; i++) + ret |= 1 << (31 - i); + return ret; +} + +/* The MB or ME field in an MD or MDS form instruction. The high bit + is wrapped to the low end. */ + +/*ARGSUSED*/ +static unsigned long +insert_mb6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 6) | (value & 0x20); +} + +/*ARGSUSED*/ +static long +extract_mb6 (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 6) & 0x1f) | (insn & 0x20); +} + +/* The NB field in an X form instruction. The value 32 is stored as + 0. */ + +static unsigned long +insert_nb (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value < 0 || value > 32) + *errmsg = "value out of range"; + if (value == 32) + value = 0; + return insn | ((value & 0x1f) << 11); +} + +/*ARGSUSED*/ +static long +extract_nb (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + + ret = (insn >> 11) & 0x1f; + if (ret == 0) + ret = 32; + return ret; +} + +/* The NSI field in a D form instruction. This is the same as the SI + field, only negated. The extraction function always marks it as + invalid, since we never want to recognize an instruction which uses + a field of this type. */ + +/*ARGSUSED*/ +static unsigned long +insert_nsi (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((- value) & 0xffff); +} + +static long +extract_nsi (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL) + *invalid = 1; + if ((insn & 0x8000) != 0) + return - ((insn & 0xffff) - 0x10000); + else + return - (insn & 0xffff); +} + +/* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ + +static unsigned long +insert_ral (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0 + || value == ((insn >> 21) & 0x1f)) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in an lmw instruction, which has special value + restrictions. */ + +static unsigned long +insert_ram (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value >= ((insn >> 21) & 0x1f)) + *errmsg = "index register in load range"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ + +static unsigned long +insert_ras (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. This operand is marked FAKE. The insertion + function just copies the BT field into the BA field, and the + extraction function just checks that the fields are the same. */ + +/*ARGSUSED*/ +static unsigned long +insert_rbs (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 21) & 0x1f) << 11); +} + +static long +extract_rbs (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The SH field in an MD form instruction. This is split. */ + +/*ARGSUSED*/ +static unsigned long +insert_sh6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4); +} + +/*ARGSUSED*/ +static long +extract_sh6 (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20); +} + +/* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ + +static unsigned long +insert_spr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_spr (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); +} + +/* The TBR field in an XFX instruction. This is just like SPR, but it + is optional. When TBR is omitted, it must be inserted as 268 (the + magic number of the TB register). These functions treat 0 + (indicating an omitted optional operand) as 268. This means that + ``mftb 4,0'' is not handled correctly. This does not matter very + much, since the architecture manual does not define mftb as + accepting any values other than 268 or 269. */ + +#define TB (268) + +static unsigned long +insert_tbr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0) + value = TB; + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_tbr (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + + ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); + if (ret == TB) + ret = 0; + return ret; +} + +/* Macros used to form opcodes. */ + +/* The main opcode. */ +#define OP(x) (((x) & 0x3f) << 26) +#define OP_MASK OP (0x3f) + +/* The main opcode combined with a trap code in the TO field of a D + form instruction. Used for extended mnemonics for the trap + instructions. */ +#define OPTO(x,to) (OP (x) | (((to) & 0x1f) << 21)) +#define OPTO_MASK (OP_MASK | TO_MASK) + +/* The main opcode combined with a comparison size bit in the L field + of a D form or X form instruction. Used for extended mnemonics for + the comparison instructions. */ +#define OPL(x,l) (OP (x) | (((l) & 1) << 21)) +#define OPL_MASK OPL (0x3f,1) + +/* An A form instruction. */ +#define A(op, xop, rc) (OP (op) | (((xop) & 0x1f) << 1) | ((rc) & 1)) +#define A_MASK A (0x3f, 0x1f, 1) + +/* An A_MASK with the FRB field fixed. */ +#define AFRB_MASK (A_MASK | FRB_MASK) + +/* An A_MASK with the FRC field fixed. */ +#define AFRC_MASK (A_MASK | FRC_MASK) + +/* An A_MASK with the FRA and FRC fields fixed. */ +#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK) + +/* A B form instruction. */ +#define B(op, aa, lk) (OP (op) | (((aa) & 1) << 1) | ((lk) & 1)) +#define B_MASK B (0x3f, 1, 1) + +/* A B form instruction setting the BO field. */ +#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | (((bo) & 0x1f) << 21)) +#define BBO_MASK BBO (0x3f, 0x1f, 1, 1) + +/* A BBO_MASK with the y bit of the BO field removed. This permits + matching a conditional branch regardless of the setting of the y + bit. */ +#define Y_MASK (1 << 21) +#define BBOY_MASK (BBO_MASK &~ Y_MASK) + +/* A B form instruction setting the BO field and the condition bits of + the BI field. */ +#define BBOCB(op, bo, cb, aa, lk) \ + (BBO ((op), (bo), (aa), (lk)) | (((cb) & 0x3) << 16)) +#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1) + +/* A BBOCB_MASK with the y bit of the BO field removed. */ +#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK) + +/* A BBOYCB_MASK in which the BI field is fixed. */ +#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK) + +/* The main opcode mask with the RA field clear. */ +#define DRA_MASK (OP_MASK | RA_MASK) + +/* A DS form instruction. */ +#define DSO(op, xop) (OP (op) | ((xop) & 0x3)) +#define DS_MASK DSO (0x3f, 3) + +/* An M form instruction. */ +#define M(op, rc) (OP (op) | ((rc) & 1)) +#define M_MASK M (0x3f, 1) + +/* An M form instruction with the ME field specified. */ +#define MME(op, me, rc) (M ((op), (rc)) | (((me) & 0x1f) << 1)) + +/* An M_MASK with the MB and ME fields fixed. */ +#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK) + +/* An M_MASK with the SH and ME fields fixed. */ +#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK) + +/* An MD form instruction. */ +#define MD(op, xop, rc) (OP (op) | (((xop) & 0x7) << 2) | ((rc) & 1)) +#define MD_MASK MD (0x3f, 0x7, 1) + +/* An MD_MASK with the MB field fixed. */ +#define MDMB_MASK (MD_MASK | MB6_MASK) + +/* An MD_MASK with the SH field fixed. */ +#define MDSH_MASK (MD_MASK | SH6_MASK) + +/* An MDS form instruction. */ +#define MDS(op, xop, rc) (OP (op) | (((xop) & 0xf) << 1) | ((rc) & 1)) +#define MDS_MASK MDS (0x3f, 0xf, 1) + +/* An MDS_MASK with the MB field fixed. */ +#define MDSMB_MASK (MDS_MASK | MB6_MASK) + +/* An SC form instruction. */ +#define SC(op, sa, lk) (OP (op) | (((sa) & 1) << 1) | ((lk) & 1)) +#define SC_MASK (OP_MASK | (0x3ff << 16) | (1 << 1) | 1) + +/* An X form instruction. */ +#define X(op, xop) (OP (op) | (((xop) & 0x3ff) << 1)) + +/* An X form instruction with the RC bit specified. */ +#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1)) + +/* The mask for an X form instruction. */ +#define X_MASK XRC (0x3f, 0x3ff, 1) + +/* An X_MASK with the RA field fixed. */ +#define XRA_MASK (X_MASK | RA_MASK) + +/* An X_MASK with the RB field fixed. */ +#define XRB_MASK (X_MASK | RB_MASK) + +/* An X_MASK with the RT field fixed. */ +#define XRT_MASK (X_MASK | RT_MASK) + +/* An X_MASK with the RA and RB fields fixed. */ +#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK) + +/* An X_MASK with the RT and RA fields fixed. */ +#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK) + +/* An X form comparison instruction. */ +#define XCMPL(op, xop, l) (X ((op), (xop)) | (((l) & 1) << 21)) + +/* The mask for an X form comparison instruction. */ +#define XCMP_MASK (X_MASK | (1 << 22)) + +/* The mask for an X form comparison instruction with the L field + fixed. */ +#define XCMPL_MASK (XCMP_MASK | (1 << 21)) + +/* An X form trap instruction with the TO field specified. */ +#define XTO(op, xop, to) (X ((op), (xop)) | (((to) & 0x1f) << 21)) +#define XTO_MASK (X_MASK | TO_MASK) + +/* An XFL form instruction. */ +#define XFL(op, xop, rc) (OP (op) | (((xop) & 0x3ff) << 1) | ((rc) & 1)) +#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (1 << 25) | (1 << 16)) + +/* An XL form instruction with the LK field set to 0. */ +#define XL(op, xop) (OP (op) | (((xop) & 0x3ff) << 1)) + +/* An XL form instruction which uses the LK field. */ +#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1)) + +/* The mask for an XL form instruction. */ +#define XL_MASK XLLK (0x3f, 0x3ff, 1) + +/* An XL form instruction which explicitly sets the BO field. */ +#define XLO(op, bo, xop, lk) \ + (XLLK ((op), (xop), (lk)) | (((bo) & 0x1f) << 21)) +#define XLO_MASK (XL_MASK | BO_MASK) + +/* An XL form instruction which explicitly sets the y bit of the BO + field. */ +#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | (((y) & 1) << 21)) +#define XLYLK_MASK (XL_MASK | Y_MASK) + +/* An XL form instruction which sets the BO field and the condition + bits of the BI field. */ +#define XLOCB(op, bo, cb, xop, lk) \ + (XLO ((op), (bo), (xop), (lk)) | (((cb) & 3) << 16)) +#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1) + +/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */ +#define XLBB_MASK (XL_MASK | BB_MASK) +#define XLYBB_MASK (XLYLK_MASK | BB_MASK) +#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK) + +/* An XL_MASK with the BO and BB fields fixed. */ +#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK) + +/* An XL_MASK with the BO, BI and BB fields fixed. */ +#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK) + +/* An XO form instruction. */ +#define XO(op, xop, oe, rc) \ + (OP (op) | (((xop) & 0x1ff) << 1) | (((oe) & 1) << 10) | ((rc) & 1)) +#define XO_MASK XO (0x3f, 0x1ff, 1, 1) + +/* An XO_MASK with the RB field fixed. */ +#define XORB_MASK (XO_MASK | RB_MASK) + +/* An XS form instruction. */ +#define XS(op, xop, rc) (OP (op) | (((xop) & 0x1ff) << 2) | ((rc) & 1)) +#define XS_MASK XS (0x3f, 0x1ff, 1) + +/* A mask for the FXM version of an XFX form instruction. */ +#define XFXFXM_MASK (X_MASK | (1 << 20) | (1 << 11)) + +/* An XFX form instruction with the FXM field filled in. */ +#define XFXM(op, xop, fxm) \ + (X ((op), (xop)) | (((fxm) & 0xff) << 12)) + +/* An XFX form instruction with the SPR field filled in. */ +#define XSPR(op, xop, spr) \ + (X ((op), (xop)) | (((spr) & 0x1f) << 16) | (((spr) & 0x3e0) << 6)) +#define XSPR_MASK (X_MASK | SPR_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRBAT field. */ +#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRG field. */ +#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK) + +/* The BO encodings used in extended conditional branch mnemonics. */ +#define BODNZF (0x0) +#define BODNZFP (0x1) +#define BODZF (0x2) +#define BODZFP (0x3) +#define BOF (0x4) +#define BOFP (0x5) +#define BODNZT (0x8) +#define BODNZTP (0x9) +#define BODZT (0xa) +#define BODZTP (0xb) +#define BOT (0xc) +#define BOTP (0xd) +#define BODNZ (0x10) +#define BODNZP (0x11) +#define BODZ (0x12) +#define BODZP (0x13) +#define BOU (0x14) + +/* The BI condition bit encodings used in extended conditional branch + mnemonics. */ +#define CBLT (0) +#define CBGT (1) +#define CBEQ (2) +#define CBSO (3) + +/* The TO encodings used in extended trap mnemonics. */ +#define TOLGT (0x1) +#define TOLLT (0x2) +#define TOEQ (0x4) +#define TOLGE (0x5) +#define TOLNL (0x5) +#define TOLLE (0x6) +#define TOLNG (0x6) +#define TOGT (0x8) +#define TOGE (0xc) +#define TONL (0xc) +#define TOLT (0x10) +#define TOLE (0x14) +#define TONG (0x14) +#define TONE (0x18) +#define TOU (0x1f) + +/* Smaller names for the flags so each entry in the opcodes table will + fit on a single line. */ +#undef PPC +#define PPC PPC_OPCODE_PPC +#define POWER PPC_OPCODE_POWER +#define POWER2 PPC_OPCODE_POWER2 +#define B32 PPC_OPCODE_32 +#define B64 PPC_OPCODE_64 +#define M601 PPC_OPCODE_601 + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK FLAGS { OPERANDS } + + NAME is the name of the instruction. + OPCODE is the instruction opcode. + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + FLAGS are flags indicated what processors support the instruction. + OPERANDS is the list of operands. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. It is also + sorted by major opcode. */ + +const struct powerpc_opcode powerpc_opcodes[] = { +{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdi", OP(2), OP_MASK, PPC|B64, { TO, RA, SI } }, + +{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPC, { RA, SI } }, +{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, POWER, { RA, SI } }, +{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPC, { RA, SI } }, +{ "tllti", OPTO(3,TOLLT), OPTO_MASK, POWER, { RA, SI } }, +{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPC, { RA, SI } }, +{ "teqi", OPTO(3,TOEQ), OPTO_MASK, POWER, { RA, SI } }, +{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPC, { RA, SI } }, +{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, POWER, { RA, SI } }, +{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPC, { RA, SI } }, +{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, POWER, { RA, SI } }, +{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPC, { RA, SI } }, +{ "tllei", OPTO(3,TOLLE), OPTO_MASK, POWER, { RA, SI } }, +{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPC, { RA, SI } }, +{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, POWER, { RA, SI } }, +{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPC, { RA, SI } }, +{ "tgti", OPTO(3,TOGT), OPTO_MASK, POWER, { RA, SI } }, +{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPC, { RA, SI } }, +{ "tgei", OPTO(3,TOGE), OPTO_MASK, POWER, { RA, SI } }, +{ "twnli", OPTO(3,TONL), OPTO_MASK, PPC, { RA, SI } }, +{ "tnli", OPTO(3,TONL), OPTO_MASK, POWER, { RA, SI } }, +{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPC, { RA, SI } }, +{ "tlti", OPTO(3,TOLT), OPTO_MASK, POWER, { RA, SI } }, +{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPC, { RA, SI } }, +{ "tlei", OPTO(3,TOLE), OPTO_MASK, POWER, { RA, SI } }, +{ "twngi", OPTO(3,TONG), OPTO_MASK, PPC, { RA, SI } }, +{ "tngi", OPTO(3,TONG), OPTO_MASK, POWER, { RA, SI } }, +{ "twnei", OPTO(3,TONE), OPTO_MASK, PPC, { RA, SI } }, +{ "tnei", OPTO(3,TONE), OPTO_MASK, POWER, { RA, SI } }, +{ "twi", OP(3), OP_MASK, PPC, { TO, RA, SI } }, +{ "ti", OP(3), OP_MASK, POWER, { TO, RA, SI } }, + +{ "mulli", OP(7), OP_MASK, PPC, { RT, RA, SI } }, +{ "muli", OP(7), OP_MASK, POWER, { RT, RA, SI } }, + +{ "subfic", OP(8), OP_MASK, PPC, { RT, RA, SI } }, +{ "sfi", OP(8), OP_MASK, POWER, { RT, RA, SI } }, + +{ "dozi", OP(9), OP_MASK, POWER|M601, { RT, RA, SI } }, + +{ "cmplwi", OPL(10,0), OPL_MASK, PPC, { OBF, RA, UI } }, +{ "cmpldi", OPL(10,1), OPL_MASK, PPC|B64, { OBF, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PPC, { BF, L, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, POWER, { BF, RA, UI } }, + +{ "cmpwi", OPL(11,0), OPL_MASK, PPC, { OBF, RA, SI } }, +{ "cmpdi", OPL(11,1), OPL_MASK, PPC|B64, { OBF, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PPC, { BF, L, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, POWER, { BF, RA, SI } }, + +{ "addic", OP(12), OP_MASK, PPC, { RT, RA, SI } }, +{ "ai", OP(12), OP_MASK, POWER, { RT, RA, SI } }, +{ "subic", OP(12), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "addic.", OP(13), OP_MASK, PPC, { RT, RA, SI } }, +{ "ai.", OP(13), OP_MASK, POWER, { RT, RA, SI } }, +{ "subic.", OP(13), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "li", OP(14), DRA_MASK, PPC, { RT, SI } }, +{ "lil", OP(14), DRA_MASK, POWER, { RT, SI } }, +{ "addi", OP(14), OP_MASK, PPC, { RT, RA, SI } }, +{ "cal", OP(14), OP_MASK, POWER, { RT, D, RA } }, +{ "subi", OP(14), OP_MASK, PPC, { RT, RA, NSI } }, +{ "la", OP(14), OP_MASK, PPC, { RT, D, RA } }, + +{ "lis", OP(15), DRA_MASK, PPC, { RT, SISIGNOPT } }, +{ "liu", OP(15), DRA_MASK, POWER, { RT, SISIGNOPT } }, +{ "addis", OP(15), OP_MASK, PPC, { RT,RA,SISIGNOPT } }, +{ "cau", OP(15), OP_MASK, POWER, { RT,RA,SISIGNOPT } }, +{ "subis", OP(15), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "bdnz-", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDM } }, +{ "bdnz+", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDP } }, +{ "bdnz", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BD } }, +{ "bdn", BBO(16,BODNZ,0,0), BBOYBI_MASK, POWER, { BD } }, +{ "bdnzl-", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDM } }, +{ "bdnzl+", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDP } }, +{ "bdnzl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BD } }, +{ "bdnl", BBO(16,BODNZ,0,1), BBOYBI_MASK, POWER, { BD } }, +{ "bdnza-", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdnza+", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdnza", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDA } }, +{ "bdna", BBO(16,BODNZ,1,0), BBOYBI_MASK, POWER, { BDA } }, +{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdnzla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDA } }, +{ "bdnla", BBO(16,BODNZ,1,1), BBOYBI_MASK, POWER, { BDA } }, +{ "bdz-", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDM } }, +{ "bdz+", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDP } }, +{ "bdz", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC|POWER, { BD } }, +{ "bdzl-", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDM } }, +{ "bdzl+", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDP } }, +{ "bdzl", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC|POWER, { BD } }, +{ "bdza-", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdza+", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdza", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC|POWER, { BDA } }, +{ "bdzla-", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdzla+", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdzla", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC|POWER, { BDA } }, +{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } }, +{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bt-", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bt+", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bt", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bbt", BBO(16,BOT,0,0), BBOY_MASK, POWER, { BI, BD } }, +{ "btl-", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "btl+", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "btl", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bbtl", BBO(16,BOT,0,1), BBOY_MASK, POWER, { BI, BD } }, +{ "bta-", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bta+", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bta", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbta", BBO(16,BOT,1,0), BBOY_MASK, POWER, { BI, BDA } }, +{ "btla-", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "btla+", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "btla", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbtla", BBO(16,BOT,1,1), BBOY_MASK, POWER, { BI, BDA } }, +{ "bf-", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bf+", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bf", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bbf", BBO(16,BOF,0,0), BBOY_MASK, POWER, { BI, BD } }, +{ "bfl-", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bfl+", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bfl", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bbfl", BBO(16,BOF,0,1), BBOY_MASK, POWER, { BI, BD } }, +{ "bfa-", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bfa+", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bfa", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbfa", BBO(16,BOF,1,0), BBOY_MASK, POWER, { BI, BDA } }, +{ "bfla-", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bfla+", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bfla", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbfla", BBO(16,BOF,1,1), BBOY_MASK, POWER, { BI, BDA } }, +{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bc-", B(16,0,0), B_MASK, PPC, { BOE, BI, BDM } }, +{ "bc+", B(16,0,0), B_MASK, PPC, { BOE, BI, BDP } }, +{ "bc", B(16,0,0), B_MASK, PPC|POWER, { BO, BI, BD } }, +{ "bcl-", B(16,0,1), B_MASK, PPC, { BOE, BI, BDM } }, +{ "bcl+", B(16,0,1), B_MASK, PPC, { BOE, BI, BDP } }, +{ "bcl", B(16,0,1), B_MASK, PPC|POWER, { BO, BI, BD } }, +{ "bca-", B(16,1,0), B_MASK, PPC, { BOE, BI, BDMA } }, +{ "bca+", B(16,1,0), B_MASK, PPC, { BOE, BI, BDPA } }, +{ "bca", B(16,1,0), B_MASK, PPC|POWER, { BO, BI, BDA } }, +{ "bcla-", B(16,1,1), B_MASK, PPC, { BOE, BI, BDMA } }, +{ "bcla+", B(16,1,1), B_MASK, PPC, { BOE, BI, BDPA } }, +{ "bcla", B(16,1,1), B_MASK, PPC|POWER, { BO, BI, BDA } }, + +{ "sc", SC(17,1,0), 0xffffffff, PPC, { 0 } }, +{ "svc", SC(17,0,0), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svcl", SC(17,0,1), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svca", SC(17,1,0), SC_MASK, POWER, { SV } }, +{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } }, + +{ "b", B(18,0,0), B_MASK, PPC|POWER, { LI } }, +{ "bl", B(18,0,1), B_MASK, PPC|POWER, { LI } }, +{ "ba", B(18,1,0), B_MASK, PPC|POWER, { LIA } }, +{ "bla", B(18,1,1), B_MASK, PPC|POWER, { LIA } }, + +{ "mcrf", XL(19,0), XLBB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } }, + +{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, POWER, { 0 } }, +{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, POWER, { 0 } }, +{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, POWER, { BI } }, +{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, POWER, { BI } }, +{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, POWER, { BI } }, +{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, POWER, { BI } }, +{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bclr", XLLK(19,16,0), XLYBB_MASK, PPC, { BO, BI } }, +{ "bclrl", XLLK(19,16,1), XLYBB_MASK, PPC, { BO, BI } }, +{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcr", XLLK(19,16,0), XLBB_MASK, POWER, { BO, BI } }, +{ "bcrl", XLLK(19,16,1), XLBB_MASK, POWER, { BO, BI } }, + +{ "crnot", XL(19,33), XL_MASK, PPC, { BT, BA, BBA } }, +{ "crnor", XL(19,33), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "rfi", XL(19,50), 0xffffffff, PPC|POWER, { 0 } }, +{ "rfci", XL(19,51), 0xffffffff, PPC, { 0 } }, + +{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } }, + +{ "crandc", XL(19,129), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "isync", XL(19,150), 0xffffffff, PPC, { 0 } }, +{ "ics", XL(19,150), 0xffffffff, POWER, { 0 } }, + +{ "crclr", XL(19,193), XL_MASK, PPC, { BT, BAT, BBA } }, +{ "crxor", XL(19,193), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crnand", XL(19,225), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crand", XL(19,257), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crset", XL(19,289), XL_MASK, PPC, { BT, BAT, BBA } }, +{ "creqv", XL(19,289), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crorc", XL(19,417), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crmove", XL(19,449), XL_MASK, PPC, { BT, BA, BBA } }, +{ "cror", XL(19,449), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, PPC|POWER, { 0 } }, +{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, PPC|POWER, { 0 } }, +{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bcctr", XLLK(19,528,0), XLYBB_MASK, PPC, { BO, BI } }, +{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctrl", XLLK(19,528,1), XLYBB_MASK, PPC, { BO, BI } }, +{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcc", XLLK(19,528,0), XLBB_MASK, POWER, { BO, BI } }, +{ "bccl", XLLK(19,528,1), XLBB_MASK, POWER, { BO, BI } }, + +{ "rlwimi", M(20,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlimi", M(20,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rlwimi.", M(20,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlimi.", M(20,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rotlwi", MME(21,31,0), MMBME_MASK, PPC, { RA, RS, SH } }, +{ "clrlwi", MME(21,31,0), MSHME_MASK, PPC, { RA, RS, MB } }, +{ "rlwinm", M(21,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlinm", M(21,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, +{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPC, { RA,RS,SH } }, +{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPC, { RA, RS, MB } }, +{ "rlwinm.", M(21,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlinm.", M(21,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rlmi", M(22,0), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } }, +{ "rlmi.", M(22,1), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } }, + +{ "rotlw", MME(23,31,0), MMBME_MASK, PPC, { RA, RS, RB } }, +{ "rlwnm", M(23,0), M_MASK, PPC, { RA,RS,RB,MBE,ME } }, +{ "rlnm", M(23,0), M_MASK, POWER, { RA,RS,RB,MBE,ME } }, +{ "rotlw.", MME(23,31,1), MMBME_MASK, PPC, { RA, RS, RB } }, +{ "rlwnm.", M(23,1), M_MASK, PPC, { RA,RS,RB,MBE,ME } }, +{ "rlnm.", M(23,1), M_MASK, POWER, { RA,RS,RB,MBE,ME } }, + +{ "nop", OP(24), 0xffffffff, PPC, { 0 } }, +{ "ori", OP(24), OP_MASK, PPC, { RA, RS, UI } }, +{ "oril", OP(24), OP_MASK, POWER, { RA, RS, UI } }, + +{ "oris", OP(25), OP_MASK, PPC, { RA, RS, UI } }, +{ "oriu", OP(25), OP_MASK, POWER, { RA, RS, UI } }, + +{ "xori", OP(26), OP_MASK, PPC, { RA, RS, UI } }, +{ "xoril", OP(26), OP_MASK, POWER, { RA, RS, UI } }, + +{ "xoris", OP(27), OP_MASK, PPC, { RA, RS, UI } }, +{ "xoriu", OP(27), OP_MASK, POWER, { RA, RS, UI } }, + +{ "andi.", OP(28), OP_MASK, PPC, { RA, RS, UI } }, +{ "andil.", OP(28), OP_MASK, POWER, { RA, RS, UI } }, + +{ "andis.", OP(29), OP_MASK, PPC, { RA, RS, UI } }, +{ "andiu.", OP(29), OP_MASK, POWER, { RA, RS, UI } }, + +{ "rotldi", MD(30,0,0), MDMB_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "clrldi", MD(30,0,0), MDSH_MASK, PPC|B64, { RA, RS, MB6 } }, +{ "rldicl", MD(30,0,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC|B64, { RA, RS, MB6 } }, +{ "rldicl.", MD(30,0,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rldicr", MD(30,1,0), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } }, +{ "rldicr.", MD(30,1,1), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } }, + +{ "rldic", MD(30,2,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rldic.", MD(30,2,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rldimi", MD(30,3,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rldimi.", MD(30,3,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC|B64, { RA, RS, RB } }, +{ "rldcl", MDS(30,8,0), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } }, +{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC|B64, { RA, RS, RB } }, +{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } }, + +{ "rldcr", MDS(30,9,0), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } }, +{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } }, + +{ "cmpw", XCMPL(31,0,0), XCMPL_MASK, PPC, { OBF, RA, RB } }, +{ "cmpd", XCMPL(31,0,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } }, +{ "cmp", X(31,0), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmp", X(31,0), XCMPL_MASK, POWER, { BF, RA, RB } }, + +{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPC, { RA, RB } }, +{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, POWER, { RA, RB } }, +{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPC, { RA, RB } }, +{ "tllt", XTO(31,4,TOLLT), XTO_MASK, POWER, { RA, RB } }, +{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPC, { RA, RB } }, +{ "teq", XTO(31,4,TOEQ), XTO_MASK, POWER, { RA, RB } }, +{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPC, { RA, RB } }, +{ "tlge", XTO(31,4,TOLGE), XTO_MASK, POWER, { RA, RB } }, +{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPC, { RA, RB } }, +{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, POWER, { RA, RB } }, +{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPC, { RA, RB } }, +{ "tlle", XTO(31,4,TOLLE), XTO_MASK, POWER, { RA, RB } }, +{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPC, { RA, RB } }, +{ "tlng", XTO(31,4,TOLNG), XTO_MASK, POWER, { RA, RB } }, +{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPC, { RA, RB } }, +{ "tgt", XTO(31,4,TOGT), XTO_MASK, POWER, { RA, RB } }, +{ "twge", XTO(31,4,TOGE), XTO_MASK, PPC, { RA, RB } }, +{ "tge", XTO(31,4,TOGE), XTO_MASK, POWER, { RA, RB } }, +{ "twnl", XTO(31,4,TONL), XTO_MASK, PPC, { RA, RB } }, +{ "tnl", XTO(31,4,TONL), XTO_MASK, POWER, { RA, RB } }, +{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPC, { RA, RB } }, +{ "tlt", XTO(31,4,TOLT), XTO_MASK, POWER, { RA, RB } }, +{ "twle", XTO(31,4,TOLE), XTO_MASK, PPC, { RA, RB } }, +{ "tle", XTO(31,4,TOLE), XTO_MASK, POWER, { RA, RB } }, +{ "twng", XTO(31,4,TONG), XTO_MASK, PPC, { RA, RB } }, +{ "tng", XTO(31,4,TONG), XTO_MASK, POWER, { RA, RB } }, +{ "twne", XTO(31,4,TONE), XTO_MASK, PPC, { RA, RB } }, +{ "tne", XTO(31,4,TONE), XTO_MASK, POWER, { RA, RB } }, +{ "trap", XTO(31,4,TOU), 0xffffffff, PPC, { 0 } }, +{ "tw", X(31,4), X_MASK, PPC, { TO, RA, RB } }, +{ "t", X(31,4), X_MASK, POWER, { TO, RA, RB } }, + +{ "subfc", XO(31,8,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sf", XO(31,8,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sf.", XO(31,8,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco", XO(31,8,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfo", XO(31,8,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfo.", XO(31,8,1,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "addc", XO(31,10,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "a", XO(31,10,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addc.", XO(31,10,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "a.", XO(31,10,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addco", XO(31,10,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "ao", XO(31,10,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addco.", XO(31,10,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "ao.", XO(31,10,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfcr", X(31,19), XRARB_MASK, POWER|PPC, { RT } }, + +{ "lwarx", X(31,20), X_MASK, PPC, { RT, RA, RB } }, + +{ "ldx", X(31,21), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "lwzx", X(31,23), X_MASK, PPC, { RT, RA, RB } }, +{ "lx", X(31,23), X_MASK, POWER, { RT, RA, RB } }, + +{ "slw", XRC(31,24,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sl", XRC(31,24,0), X_MASK, POWER, { RA, RS, RB } }, +{ "slw.", XRC(31,24,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sl.", XRC(31,24,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "cntlzw", XRC(31,26,0), XRB_MASK, PPC, { RA, RS } }, +{ "cntlz", XRC(31,26,0), XRB_MASK, POWER, { RA, RS } }, +{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPC, { RA, RS } }, +{ "cntlz.", XRC(31,26,1), XRB_MASK, POWER, { RA, RS } }, + +{ "sld", XRC(31,27,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "sld.", XRC(31,27,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "and", XRC(31,28,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "and.", XRC(31,28,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "maskg", XRC(31,29,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "maskg.", XRC(31,29,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "cmplw", XCMPL(31,32,0), XCMPL_MASK, PPC, { OBF, RA, RB } }, +{ "cmpld", XCMPL(31,32,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } }, +{ "cmpl", X(31,32), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmpl", X(31,32), XCMPL_MASK, POWER, { BF, RA, RB } }, + +{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "ldux", X(31,53), X_MASK, PPC|B64, { RT, RAL, RB } }, + +{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } }, + +{ "lwzux", X(31,55), X_MASK, PPC, { RT, RAL, RB } }, +{ "lux", X(31,55), X_MASK, POWER, { RT, RA, RB } }, + +{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC|B64, { RA, RS } }, +{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC|B64, { RA, RS } }, + +{ "andc", XRC(31,60,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "andc.", XRC(31,60,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "td", X(31,68), X_MASK, PPC|B64, { TO, RA, RB } }, + +{ "mulhd", XO(31,73,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfmsr", X(31,83), XRARB_MASK, PPC|POWER, { RT } }, + +{ "ldarx", X(31,84), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "dcbf", X(31,86), XRT_MASK, PPC, { RA, RB } }, + +{ "lbzx", X(31,87), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "neg", XO(31,104,0,0), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "neg.", XO(31,104,0,1), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "nego", XO(31,104,1,0), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "nego.", XO(31,104,1,1), XORB_MASK, PPC|POWER, { RT, RA } }, + +{ "mul", XO(31,107,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mul.", XO(31,107,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mulo", XO(31,107,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mulo.", XO(31,107,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "clf", X(31,118), XRB_MASK, POWER, { RT, RA } }, + +{ "lbzux", X(31,119), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "not", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "nor", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "not.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "nor.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "subfe", XO(31,136,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfe", XO(31,136,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfe.", XO(31,136,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfe.", XO(31,136,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfeo", XO(31,136,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfeo", XO(31,136,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfeo.", XO(31,136,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfeo.", XO(31,136,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "adde", XO(31,138,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "ae", XO(31,138,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "adde.", XO(31,138,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "ae.", XO(31,138,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addeo", XO(31,138,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "aeo", XO(31,138,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addeo.", XO(31,138,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "aeo.", XO(31,138,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mtcr", XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, PPC|POWER, { RS }}, +{ "mtcrf", X(31,144), XFXFXM_MASK, PPC|POWER, { FXM, RS } }, + +{ "mtmsr", X(31,146), XRARB_MASK, PPC|POWER, { RS } }, +{ "mtmsrd", X(31,178), XRARB_MASK, PPC|POWER, { RS } }, + +{ "stdx", X(31,149), X_MASK, PPC|B64, { RS, RA, RB } }, + +{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA, RB } }, + +{ "stwx", X(31,151), X_MASK, PPC, { RS, RA, RB } }, +{ "stx", X(31,151), X_MASK, POWER, { RS, RA, RB } }, + +{ "slq", XRC(31,152,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "slq.", XRC(31,152,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sle", XRC(31,153,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sle.", XRC(31,153,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stdux", X(31,181), X_MASK, PPC|B64, { RS, RAS, RB } }, + +{ "stwux", X(31,183), X_MASK, PPC, { RS, RAS, RB } }, +{ "stux", X(31,183), X_MASK, POWER, { RS, RA, RB } }, + +{ "sliq", XRC(31,184,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sliq.", XRC(31,184,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "subfze", XO(31,200,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfze", XO(31,200,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfze.", XO(31,200,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfze.", XO(31,200,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfzeo", XO(31,200,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfzeo.", XO(31,200,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "addze", XO(31,202,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "aze", XO(31,202,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "addze.", XO(31,202,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "aze.", XO(31,202,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "addzeo", XO(31,202,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "azeo", XO(31,202,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "azeo.", XO(31,202,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mtsr", X(31,210), XRB_MASK|(1<<20), PPC|POWER|B32, { SR, RS } }, + +{ "stdcx.", XRC(31,214,1), X_MASK, PPC|B64, { RS, RA, RB } }, + +{ "stbx", X(31,215), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "sllq", XRC(31,216,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sllq.", XRC(31,216,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sleq", XRC(31,217,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sleq.", XRC(31,217,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "subfme", XO(31,232,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfme", XO(31,232,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfme.", XO(31,232,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfme.", XO(31,232,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfmeo", XO(31,232,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfmeo.", XO(31,232,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mulld", XO(31,233,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulld.", XO(31,233,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulldo", XO(31,233,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "addme", XO(31,234,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "ame", XO(31,234,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "addme.", XO(31,234,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "ame.", XO(31,234,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "addmeo", XO(31,234,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "ameo", XO(31,234,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "ameo.", XO(31,234,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mullw", XO(31,235,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "muls", XO(31,235,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullw.", XO(31,235,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "muls.", XO(31,235,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullwo", XO(31,235,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulso", XO(31,235,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullwo.", XO(31,235,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulso.", XO(31,235,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mtsrin", X(31,242), XRA_MASK, PPC|B32, { RS, RB } }, +{ "mtsri", X(31,242), XRA_MASK, POWER|B32, { RS, RB } }, + +{ "dcbtst", X(31,246), XRT_MASK, PPC, { RA, RB } }, + +{ "stbux", X(31,247), X_MASK, PPC|POWER, { RS, RAS, RB } }, + +{ "slliq", XRC(31,248,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "slliq.", XRC(31,248,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "doz", XO(31,264,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "doz.", XO(31,264,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "dozo", XO(31,264,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "dozo.", XO(31,264,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "add", XO(31,266,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "cax", XO(31,266,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "add.", XO(31,266,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "cax.", XO(31,266,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addo", XO(31,266,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "caxo", XO(31,266,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addo.", XO(31,266,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "caxo.", XO(31,266,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "lscbx", XRC(31,277,0), X_MASK, POWER|M601, { RT, RA, RB } }, +{ "lscbx.", XRC(31,277,1), X_MASK, POWER|M601, { RT, RA, RB } }, + +{ "dcbt", X(31,278), XRT_MASK, PPC, { RA, RB } }, + +{ "lhzx", X(31,279), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "icbt", X(31,262), XRT_MASK, PPC, { RA, RB } }, + +{ "eqv", XRC(31,284,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "eqv.", XRC(31,284,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "tlbie", X(31,306), XRTRA_MASK, PPC, { RB } }, +{ "tlbi", X(31,306), XRTRA_MASK, POWER, { RB } }, + +{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } }, + +{ "lhzux", X(31,311), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "xor", XRC(31,316,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "xor.", XRC(31,316,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "mfdcr", X(31,323), X_MASK, PPC, { RT, SPR } }, + +{ "div", XO(31,331,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "div.", XO(31,331,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divo", XO(31,331,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divo.", XO(31,331,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "mfmq", XSPR(31,339,0), XSPR_MASK, POWER|M601, { RT } }, +{ "mfxer", XSPR(31,339,1), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdec", XSPR(31,339,6), XSPR_MASK, POWER|M601, { RT } }, +{ "mflr", XSPR(31,339,8), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfctr", XSPR(31,339,9), XSPR_MASK, PPC|POWER, { RT } }, +{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } }, +{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdar", XSPR(31,339,19), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdec", XSPR(31,339,22), XSPR_MASK, PPC, { RT } }, +{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } }, +{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsprg", XSPR(31,339,272), XSPRG_MASK, PPC, { RT, SPRG } }, +{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC|B64, { RT } }, +{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } }, +{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } }, +{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfspr", X(31,339), X_MASK, PPC|POWER, { RT, SPR } }, + +{ "lwax", X(31,341), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "lhax", X(31,343), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "dccci", X(31,454), XRT_MASK, PPC, { RA, RB } }, + +{ "abs", XO(31,360,0,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abs.", XO(31,360,0,1), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abso", XO(31,360,1,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abso.", XO(31,360,1,1), XORB_MASK, POWER|M601, { RT, RA } }, + +{ "divs", XO(31,363,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divs.", XO(31,363,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divso", XO(31,363,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divso.", XO(31,363,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } }, + +{ "mftbu", XSPR(31,371,269), XSPR_MASK, PPC, { RT } }, +{ "mftb", X(31,371), X_MASK, PPC, { RT, TBR } }, + +{ "lwaux", X(31,373), X_MASK, PPC|B64, { RT, RAL, RB } }, + +{ "lhaux", X(31,375), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "sthx", X(31,407), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "orc", XRC(31,412,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "orc.", XRC(31,412,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "sradi", XS(31,413,0), XS_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "sradi.", XS(31,413,1), XS_MASK, PPC|B64, { RA, RS, SH6 } }, + +{ "slbie", X(31,434), XRTRA_MASK, PPC|B64, { RB } }, + +{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } }, + +{ "sthux", X(31,439), X_MASK, PPC|POWER, { RS, RAS, RB } }, + +{ "mr", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "or", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "mr.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "or.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "mtdcr", X(31,451), X_MASK, PPC, { SPR, RS } }, + +{ "divdu", XO(31,457,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdu.", XO(31,457,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divduo", XO(31,457,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divduo.", XO(31,457,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mtmq", XSPR(31,467,0), XSPR_MASK, POWER|M601, { RS } }, +{ "mtxer", XSPR(31,467,1), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtlr", XSPR(31,467,8), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtctr", XSPR(31,467,9), XSPR_MASK, PPC|POWER, { RS } }, +{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } }, +{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtdar", XSPR(31,467,19), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtdec", XSPR(31,467,22), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } }, +{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsprg", XSPR(31,467,272), XSPRG_MASK, PPC, { SPRG, RS } }, +{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC|B64, { RS } }, +{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } }, +{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } }, +{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } }, +{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtspr", X(31,467), X_MASK, PPC|POWER, { SPR, RS } }, + +{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } }, + +{ "nand", XRC(31,476,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "nand.", XRC(31,476,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "nabs", XO(31,488,0,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabs.", XO(31,488,0,1), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabso", XO(31,488,1,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabso.", XO(31,488,1,1), XORB_MASK, POWER|M601, { RT, RA } }, + +{ "divd", XO(31,489,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divd.", XO(31,489,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdo", XO(31,489,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdo.", XO(31,489,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "slbia", X(31,498), 0xffffffff, PPC|B64, { 0 } }, + +{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } }, + +{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), PPC|POWER, { BF } }, + +{ "clcs", X(31,531), XRB_MASK, POWER|M601, { RT, RA } }, + +{ "lswx", X(31,533), X_MASK, PPC, { RT, RA, RB } }, +{ "lsx", X(31,533), X_MASK, POWER, { RT, RA, RB } }, + +{ "lwbrx", X(31,534), X_MASK, PPC, { RT, RA, RB } }, +{ "lbrx", X(31,534), X_MASK, POWER, { RT, RA, RB } }, + +{ "lfsx", X(31,535), X_MASK, PPC|POWER, { FRT, RA, RB } }, + +{ "srw", XRC(31,536,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sr", XRC(31,536,0), X_MASK, POWER, { RA, RS, RB } }, +{ "srw.", XRC(31,536,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sr.", XRC(31,536,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "rrib", XRC(31,537,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "rrib.", XRC(31,537,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "srd", XRC(31,539,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "srd.", XRC(31,539,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "maskir", XRC(31,541,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "maskir.", XRC(31,541,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } }, + +{ "lfsux", X(31,567), X_MASK, PPC|POWER, { FRT, RAS, RB } }, + +{ "mfsr", X(31,595), XRB_MASK|(1<<20), PPC|POWER|B32, { RT, SR } }, + +{ "lswi", X(31,597), X_MASK, PPC, { RT, RA, NB } }, +{ "lsi", X(31,597), X_MASK, POWER, { RT, RA, NB } }, + +{ "sync", X(31,598), 0xffffffff, PPC, { 0 } }, +{ "dcs", X(31,598), 0xffffffff, POWER, { 0 } }, + +{ "lfdx", X(31,599), X_MASK, PPC|POWER, { FRT, RA, RB } }, + +{ "mfsri", X(31,627), X_MASK, POWER, { RT, RA, RB } }, + +{ "dclst", X(31,630), XRB_MASK, POWER, { RS, RA } }, + +{ "lfdux", X(31,631), X_MASK, PPC|POWER, { FRT, RAS, RB } }, + +{ "mfsrin", X(31,659), XRA_MASK, PPC|B32, { RT, RB } }, + +{ "stswx", X(31,661), X_MASK, PPC, { RS, RA, RB } }, +{ "stsx", X(31,661), X_MASK, POWER, { RS, RA, RB } }, + +{ "stwbrx", X(31,662), X_MASK, PPC, { RS, RA, RB } }, +{ "stbrx", X(31,662), X_MASK, POWER, { RS, RA, RB } }, + +{ "stfsx", X(31,663), X_MASK, PPC|POWER, { FRS, RA, RB } }, + +{ "srq", XRC(31,664,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srq.", XRC(31,664,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sre", XRC(31,665,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sre.", XRC(31,665,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stfsux", X(31,695), X_MASK, PPC|POWER, { FRS, RAS, RB } }, + +{ "sriq", XRC(31,696,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sriq.", XRC(31,696,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "stswi", X(31,725), X_MASK, PPC, { RS, RA, NB } }, +{ "stsi", X(31,725), X_MASK, POWER, { RS, RA, NB } }, + +{ "stfdx", X(31,727), X_MASK, PPC|POWER, { FRS, RA, RB } }, + +{ "srlq", XRC(31,728,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srlq.", XRC(31,728,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sreq", XRC(31,729,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sreq.", XRC(31,729,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stfdux", X(31,759), X_MASK, PPC|POWER, { FRS, RAS, RB } }, + +{ "srliq", XRC(31,760,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "srliq.", XRC(31,760,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "lhbrx", X(31,790), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "sraw", XRC(31,792,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sra", XRC(31,792,0), X_MASK, POWER, { RA, RS, RB } }, +{ "sraw.", XRC(31,792,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sra.", XRC(31,792,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "srad", XRC(31,794,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "srad.", XRC(31,794,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "rac", X(31,818), X_MASK, POWER, { RT, RA, RB } }, + +{ "srawi", XRC(31,824,0), X_MASK, PPC, { RA, RS, SH } }, +{ "srai", XRC(31,824,0), X_MASK, POWER, { RA, RS, SH } }, +{ "srawi.", XRC(31,824,1), X_MASK, PPC, { RA, RS, SH } }, +{ "srai.", XRC(31,824,1), X_MASK, POWER, { RA, RS, SH } }, + +{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } }, + +{ "sthbrx", X(31,918), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "sraq", XRC(31,920,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sraq.", XRC(31,920,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "srea", XRC(31,921,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srea.", XRC(31,921,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "extsh", XRC(31,922,0), XRB_MASK, PPC, { RA, RS } }, +{ "exts", XRC(31,922,0), XRB_MASK, POWER, { RA, RS } }, +{ "extsh.", XRC(31,922,1), XRB_MASK, PPC, { RA, RS } }, +{ "exts.", XRC(31,922,1), XRB_MASK, POWER, { RA, RS } }, + +{ "sraiq", XRC(31,952,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sraiq.", XRC(31,952,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} }, +{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} }, + +{ "iccci", X(31,966), XRT_MASK, PPC, { RA, RB } }, + +{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } }, + +{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA, RB } }, + +{ "extsw", XRC(31,986,0), XRB_MASK, PPC, { RA, RS } }, +{ "extsw.", XRC(31,986,1), XRB_MASK, PPC, { RA, RS } }, + +{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, +{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, + +{ "lwz", OP(32), OP_MASK, PPC, { RT, D, RA } }, +{ "l", OP(32), OP_MASK, POWER, { RT, D, RA } }, + +{ "lwzu", OP(33), OP_MASK, PPC, { RT, D, RAL } }, +{ "lu", OP(33), OP_MASK, POWER, { RT, D, RA } }, + +{ "lbz", OP(34), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lbzu", OP(35), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "stw", OP(36), OP_MASK, PPC, { RS, D, RA } }, +{ "st", OP(36), OP_MASK, POWER, { RS, D, RA } }, + +{ "stwu", OP(37), OP_MASK, PPC, { RS, D, RAS } }, +{ "stu", OP(37), OP_MASK, POWER, { RS, D, RA } }, + +{ "stb", OP(38), OP_MASK, PPC|POWER, { RS, D, RA } }, + +{ "stbu", OP(39), OP_MASK, PPC|POWER, { RS, D, RAS } }, + +{ "lhz", OP(40), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lhzu", OP(41), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "lha", OP(42), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lhau", OP(43), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "sth", OP(44), OP_MASK, PPC|POWER, { RS, D, RA } }, + +{ "sthu", OP(45), OP_MASK, PPC|POWER, { RS, D, RAS } }, + +{ "lmw", OP(46), OP_MASK, PPC, { RT, D, RAM } }, +{ "lm", OP(46), OP_MASK, POWER, { RT, D, RA } }, + +{ "stmw", OP(47), OP_MASK, PPC, { RS, D, RA } }, +{ "stm", OP(47), OP_MASK, POWER, { RS, D, RA } }, + +{ "lfs", OP(48), OP_MASK, PPC|POWER, { FRT, D, RA } }, + +{ "lfsu", OP(49), OP_MASK, PPC|POWER, { FRT, D, RAS } }, + +{ "lfd", OP(50), OP_MASK, PPC|POWER, { FRT, D, RA } }, + +{ "lfdu", OP(51), OP_MASK, PPC|POWER, { FRT, D, RAS } }, + +{ "stfs", OP(52), OP_MASK, PPC|POWER, { FRS, D, RA } }, + +{ "stfsu", OP(53), OP_MASK, PPC|POWER, { FRS, D, RAS } }, + +{ "stfd", OP(54), OP_MASK, PPC|POWER, { FRS, D, RA } }, + +{ "stfdu", OP(55), OP_MASK, PPC|POWER, { FRS, D, RAS } }, + +{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "ld", DSO(58,0), DS_MASK, PPC|B64, { RT, DS, RA } }, + +{ "ldu", DSO(58,1), DS_MASK, PPC|B64, { RT, DS, RAL } }, + +{ "lwa", DSO(58,2), DS_MASK, PPC|B64, { RT, DS, RA } }, + +{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fres", A(59,24,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fres.", A(59,24,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, + +{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "std", DSO(62,0), DS_MASK, PPC|B64, { RS, DS, RA } }, + +{ "stdu", DSO(62,1), DS_MASK, PPC|B64, { RS, DS, RAS } }, + +{ "fcmpu", X(63,0), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } }, + +{ "frsp", XRC(63,12,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "frsp.", XRC(63,12,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "fctiw", XRC(63,14,0), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiw.", XRC(63,14,1), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fctiwz", XRC(63,15,0), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fdiv", A(63,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fd", A(63,18,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fdiv.", A(63,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fd.", A(63,18,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fsub", A(63,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fs", A(63,20,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fsub.", A(63,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fs.", A(63,20,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fadd", A(63,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fa", A(63,21,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fadd.", A(63,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fa.", A(63,21,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } }, +{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } }, + +{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmul", A(63,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fm", A(63,25,0), AFRB_MASK, POWER, { FRT, FRA, FRC } }, +{ "fmul.", A(63,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fm.", A(63,25,1), AFRB_MASK, POWER, { FRT, FRA, FRC } }, + +{ "frsqrte", A(63,26,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "frsqrte.",A(63,26,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmsub", A(63,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fms", A(63,28,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fmsub.", A(63,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fms.", A(63,28,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fmadd", A(63,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fma", A(63,29,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fmadd.", A(63,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fma.", A(63,29,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fnmsub", A(63,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnms", A(63,30,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fnmsub.", A(63,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnms.", A(63,30,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fnmadd", A(63,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnma", A(63,31,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fnmadd.", A(63,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnma.", A(63,31,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fcmpo", X(63,30), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } }, + +{ "mtfsb1", XRC(63,38,0), XRARB_MASK, PPC|POWER, { BT } }, +{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, PPC|POWER, { BT } }, + +{ "fneg", XRC(63,40,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fneg.", XRC(63,40,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } }, + +{ "mtfsb0", XRC(63,70,0), XRARB_MASK, PPC|POWER, { BT } }, +{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, PPC|POWER, { BT } }, + +{ "fmr", XRC(63,72,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fmr.", XRC(63,72,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mtfsfi", XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } }, +{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } }, + +{ "fnabs", XRC(63,136,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fnabs.", XRC(63,136,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "fabs", XRC(63,264,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fabs.", XRC(63,264,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mffs", XRC(63,583,0), XRARB_MASK, PPC|POWER, { FRT } }, +{ "mffs.", XRC(63,583,1), XRARB_MASK, PPC|POWER, { FRT } }, + +{ "mtfsf", XFL(63,711,0), XFL_MASK, PPC|POWER, { FLM, FRB } }, +{ "mtfsf.", XFL(63,711,1), XFL_MASK, PPC|POWER, { FLM, FRB } }, + +{ "fctid", XRC(63,814,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fctid.", XRC(63,814,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +{ "fctidz", XRC(63,815,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +{ "fcfid", XRC(63,846,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +}; + +const int powerpc_num_opcodes = + sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]); + +/* The macro table. This is only used by the assembler. */ + +const struct powerpc_macro powerpc_macros[] = { +{ "extldi", 4, PPC|B64, "rldicr %0,%1,%3,(%2)-1" }, +{ "extldi.", 4, PPC|B64, "rldicr. %0,%1,%3,(%2)-1" }, +{ "extrdi", 4, PPC|B64, "rldicl %0,%1,(%2)+(%3),64-(%2)" }, +{ "extrdi.", 4, PPC|B64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" }, +{ "insrdi", 4, PPC|B64, "rldimi %0,%1,64-((%2)+(%3)),%3" }, +{ "insrdi.", 4, PPC|B64, "rldimi. %0,%1,64-((%2)+(%3)),%3" }, +{ "rotrdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),0" }, +{ "rotrdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),0" }, +{ "sldi", 3, PPC|B64, "rldicr %0,%1,%2,63-(%2)" }, +{ "sldi.", 3, PPC|B64, "rldicr. %0,%1,%2,63-(%2)" }, +{ "srdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),%2" }, +{ "srdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),%2" }, +{ "clrrdi", 3, PPC|B64, "rldicr %0,%1,0,63-(%2)" }, +{ "clrrdi.", 3, PPC|B64, "rldicr. %0,%1,0,63-(%2)" }, +{ "clrlsldi",4, PPC|B64, "rldic %0,%1,%3,(%2)-(%3)" }, +{ "clrlsldi.",4, PPC|B64, "rldic. %0,%1,%3,(%2)-(%3)" }, + +{ "extlwi", 4, PPC, "rlwinm %0,%1,%3,0,(%2)-1" }, +{ "extlwi.", 4, PPC, "rlwinm. %0,%1,%3,0,(%2)-1" }, +{ "extrwi", 4, PPC, "rlwinm %0,%1,(%2)+(%3),32-(%2),31" }, +{ "extrwi.", 4, PPC, "rlwinm. %0,%1,(%2)+(%3),32-(%2),31" }, +{ "inslwi", 4, PPC, "rlwimi %0,%1,32-(%3),%3,(%2)+(%3)-1" }, +{ "inslwi.", 4, PPC, "rlwimi. %0,%1,32-(%3),%3,(%2)+(%3)-1" }, +{ "insrwi", 4, PPC, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" }, +{ "insrwi.", 4, PPC, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"}, +{ "rotrwi", 3, PPC, "rlwinm %0,%1,32-(%2),0,31" }, +{ "rotrwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),0,31" }, +{ "slwi", 3, PPC, "rlwinm %0,%1,%2,0,31-(%2)" }, +{ "sli", 3, POWER, "rlinm %0,%1,%2,0,31-(%2)" }, +{ "slwi.", 3, PPC, "rlwinm. %0,%1,%2,0,31-(%2)" }, +{ "sli.", 3, POWER, "rlinm. %0,%1,%2,0,31-(%2)" }, +{ "srwi", 3, PPC, "rlwinm %0,%1,32-(%2),%2,31" }, +{ "sri", 3, POWER, "rlinm %0,%1,32-(%2),%2,31" }, +{ "srwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),%2,31" }, +{ "sri.", 3, POWER, "rlinm. %0,%1,32-(%2),%2,31" }, +{ "clrrwi", 3, PPC, "rlwinm %0,%1,0,0,31-(%2)" }, +{ "clrrwi.", 3, PPC, "rlwinm. %0,%1,0,0,31-(%2)" }, +{ "clrlslwi",4, PPC, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" }, +{ "clrlslwi.",4, PPC, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" }, + +}; + +const int powerpc_num_macros = + sizeof (powerpc_macros) / sizeof (powerpc_macros[0]); diff -Nru a/arch/ppc64/xmon/ppc.h b/arch/ppc64/xmon/ppc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/ppc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,240 @@ +/* ppc.h -- Header file for PowerPC opcode table + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef PPC_H +#define PPC_H + +/* The opcode table is an array of struct powerpc_opcode. */ + +struct powerpc_opcode +{ + /* The opcode name. */ + const char *name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned long opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned long mask; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The defined values + are listed below. */ + unsigned long flags; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[8]; +}; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +extern const struct powerpc_opcode powerpc_opcodes[]; +extern const int powerpc_num_opcodes; + +/* Values defined for the flags field of a struct powerpc_opcode. */ + +/* Opcode is defined for the PowerPC architecture. */ +#define PPC_OPCODE_PPC (01) + +/* Opcode is defined for the POWER (RS/6000) architecture. */ +#define PPC_OPCODE_POWER (02) + +/* Opcode is defined for the POWER2 (Rios 2) architecture. */ +#define PPC_OPCODE_POWER2 (04) + +/* Opcode is only defined on 32 bit architectures. */ +#define PPC_OPCODE_32 (010) + +/* Opcode is only defined on 64 bit architectures. */ +#define PPC_OPCODE_64 (020) + +/* Opcode is supported by the Motorola PowerPC 601 processor. The 601 + is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions, + but it also supports many additional POWER instructions. */ +#define PPC_OPCODE_601 (040) + +/* A macro to extract the major opcode from an instruction. */ +#define PPC_OP(i) (((i) >> 26) & 0x3f) + +/* The operands table is an array of struct powerpc_operand. */ + +struct powerpc_operand +{ + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + + /* Insertion function. This is used by the assembler. To insert an + operand value into an instruction, check this field. + + If it is NULL, execute + i |= (op & ((1 << o->bits) - 1)) << o->shift; + (i is the instruction which we are filling in, o is a pointer to + this structure, and op is the opcode value; this assumes twos + complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction and the operand value. It will return the new value + of the instruction. If the ERRMSG argument is not NULL, then if + the operand value is illegal, *ERRMSG will be set to a warning + string (the operand will be inserted in any case). If the + operand value is legal, *ERRMSG will be unchanged (most operands + can accept any value). */ + unsigned long (*insert) PARAMS ((unsigned long instruction, long op, + const char **errmsg)); + + /* Extraction function. This is used by the disassembler. To + extract this operand type from an instruction, check this field. + + If it is NULL, compute + op = ((i) >> o->shift) & ((1 << o->bits) - 1); + if ((o->flags & PPC_OPERAND_SIGNED) != 0 + && (op & (1 << (o->bits - 1))) != 0) + op -= 1 << o->bits; + (i is the instruction, o is a pointer to this structure, and op + is the result; this assumes twos complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction value. It will return the value of the operand. If + the INVALID argument is not NULL, *INVALID will be set to + non-zero if this operand type can not actually be extracted from + this operand (i.e., the instruction does not match). If the + operand is valid, *INVALID will not be changed. */ + long (*extract) PARAMS ((unsigned long instruction, int *invalid)); + + /* One bit syntax flags. */ + unsigned long flags; +}; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the powerpc_opcodes table. */ + +extern const struct powerpc_operand powerpc_operands[]; + +/* Values defined for the flags field of a struct powerpc_operand. */ + +/* This operand takes signed values. */ +#define PPC_OPERAND_SIGNED (01) + +/* This operand takes signed values, but also accepts a full positive + range of values when running in 32 bit mode. That is, if bits is + 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode, + this flag is ignored. */ +#define PPC_OPERAND_SIGNOPT (02) + +/* This operand does not actually exist in the assembler input. This + is used to support extended mnemonics such as mr, for which two + operands fields are identical. The assembler should call the + insert function with any op value. The disassembler should call + the extract function, ignore the return value, and check the value + placed in the valid argument. */ +#define PPC_OPERAND_FAKE (04) + +/* The next operand should be wrapped in parentheses rather than + separated from this one by a comma. This is used for the load and + store instructions which want their operands to look like + reg,displacement(reg) + */ +#define PPC_OPERAND_PARENS (010) + +/* This operand may use the symbolic names for the CR fields, which + are + lt 0 gt 1 eq 2 so 3 un 3 + cr0 0 cr1 1 cr2 2 cr3 3 + cr4 4 cr5 5 cr6 6 cr7 7 + These may be combined arithmetically, as in cr2*4+gt. These are + only supported on the PowerPC, not the POWER. */ +#define PPC_OPERAND_CR (020) + +/* This operand names a register. The disassembler uses this to print + register names with a leading 'r'. */ +#define PPC_OPERAND_GPR (040) + +/* This operand names a floating point register. The disassembler + prints these with a leading 'f'. */ +#define PPC_OPERAND_FPR (0100) + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_RELATIVE (0200) + +/* This operand is an absolute branch address. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_ABSOLUTE (0400) + +/* This operand is optional, and is zero if omitted. This is used for + the optional BF and L fields in the comparison instructions. The + assembler must count the number of operands remaining on the line, + and the number of operands remaining for the opcode, and decide + whether this operand is present or not. The disassembler should + print this operand out only if it is not zero. */ +#define PPC_OPERAND_OPTIONAL (01000) + +/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand + is omitted, then for the next operand use this operand value plus + 1, ignoring the next operand field for the opcode. This wretched + hack is needed because the Power rotate instructions can take + either 4 or 5 operands. The disassembler should print this operand + out regardless of the PPC_OPERAND_OPTIONAL field. */ +#define PPC_OPERAND_NEXT (02000) + +/* This operand should be regarded as a negative number for the + purposes of overflow checking (i.e., the normal most negative + number is disallowed and one more than the normal most positive + number is allowed). This flag will only be set for a signed + operand. */ +#define PPC_OPERAND_NEGATIVE (04000) + +/* The POWER and PowerPC assemblers use a few macros. We keep them + with the operands table for simplicity. The macro table is an + array of struct powerpc_macro. */ + +struct powerpc_macro +{ + /* The macro name. */ + const char *name; + + /* The number of operands the macro takes. */ + unsigned int operands; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The values are the + same as those for the struct powerpc_opcode flags field. */ + unsigned long flags; + + /* A format string to turn the macro into a normal instruction. + Each %N in the string is replaced with operand number N (zero + based). */ + const char *format; +}; + +extern const struct powerpc_macro powerpc_macros[]; +extern const int powerpc_num_macros; + +#endif /* PPC_H */ diff -Nru a/arch/ppc64/xmon/privinst.h b/arch/ppc64/xmon/privinst.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/privinst.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,94 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include + +#define GETREG(reg) \ + static inline unsigned long get_ ## reg (void) \ + { unsigned long ret; asm volatile ("mf" #reg " %0" : "=r" (ret) :); return ret; } + +#define SETREG(reg) \ + static inline void set_ ## reg (unsigned long val) \ + { asm volatile ("mt" #reg " %0" : : "r" (val)); } + +GETREG(msr) +SETREG(msrd) +GETREG(cr) + +#define GSETSPR(n, name) \ + static inline long get_ ## name (void) \ + { long ret; asm volatile ("mfspr %0," #n : "=r" (ret) : ); return ret; } \ + static inline void set_ ## name (long val) \ + { asm volatile ("mtspr " #n ",%0" : : "r" (val)); } + +GSETSPR(0, mq) +GSETSPR(1, xer) +GSETSPR(4, rtcu) +GSETSPR(5, rtcl) +GSETSPR(8, lr) +GSETSPR(9, ctr) +GSETSPR(18, dsisr) +GSETSPR(19, dar) +GSETSPR(22, dec) +GSETSPR(25, sdr1) +GSETSPR(26, srr0) +GSETSPR(27, srr1) +GSETSPR(272, sprg0) +GSETSPR(273, sprg1) +GSETSPR(274, sprg2) +GSETSPR(275, sprg3) +GSETSPR(282, ear) +GSETSPR(287, pvr) +GSETSPR(528, bat0u) +GSETSPR(529, bat0l) +GSETSPR(530, bat1u) +GSETSPR(531, bat1l) +GSETSPR(532, bat2u) +GSETSPR(533, bat2l) +GSETSPR(534, bat3u) +GSETSPR(535, bat3l) +GSETSPR(1008, hid0) +GSETSPR(1009, hid1) +GSETSPR(1010, iabr) +GSETSPR(1013, dabr) +GSETSPR(1023, pir) + +static inline int get_sr(int n) +{ + int ret; + +#if 0 + // DRENG does not assemble + asm (" mfsrin %0,%1" : "=r" (ret) : "r" (n << 28)); +#endif + return ret; +} + +static inline void set_sr(int n, int val) +{ +#if 0 + // DRENG does not assemble + asm ("mtsrin %0,%1" : : "r" (val), "r" (n << 28)); +#endif +} + +static inline void store_inst(void *p) +{ + asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p)); +} + +static inline void cflush(void *p) +{ + asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p)); +} + +static inline void cinval(void *p) +{ + asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p)); +} + diff -Nru a/arch/ppc64/xmon/setjmp.c b/arch/ppc64/xmon/setjmp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/setjmp.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,77 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * 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. + * + * NB this file must be compiled with -O2. + */ + +int +xmon_setjmp(long *buf) /* NOTE: assert(sizeof(buf) > 184) */ +{ + /* XXX should save fp regs as well */ + asm volatile ( + "mflr 0; std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + mfcr 0; std 0,24(%0)\n\ + std 13,32(%0)\n\ + std 14,40(%0)\n\ + std 15,48(%0)\n\ + std 16,56(%0)\n\ + std 17,64(%0)\n\ + std 18,72(%0)\n\ + std 19,80(%0)\n\ + std 20,88(%0)\n\ + std 21,96(%0)\n\ + std 22,104(%0)\n\ + std 23,112(%0)\n\ + std 24,120(%0)\n\ + std 25,128(%0)\n\ + std 26,136(%0)\n\ + std 27,144(%0)\n\ + std 28,152(%0)\n\ + std 29,160(%0)\n\ + std 30,168(%0)\n\ + std 31,176(%0)\n\ + " : : "r" (buf)); + return 0; +} + +void +xmon_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm volatile ( + "ld 13,32(%0)\n\ + ld 14,40(%0)\n\ + ld 15,48(%0)\n\ + ld 16,56(%0)\n\ + ld 17,64(%0)\n\ + ld 18,72(%0)\n\ + ld 19,80(%0)\n\ + ld 20,88(%0)\n\ + ld 21,96(%0)\n\ + ld 22,104(%0)\n\ + ld 23,112(%0)\n\ + ld 24,120(%0)\n\ + ld 25,128(%0)\n\ + ld 26,136(%0)\n\ + ld 27,144(%0)\n\ + ld 28,152(%0)\n\ + ld 29,160(%0)\n\ + ld 30,168(%0)\n\ + ld 31,176(%0)\n\ + ld 0,24(%0)\n\ + mtcrf 0x38,0\n\ + ld 0,0(%0)\n\ + ld 1,8(%0)\n\ + ld 2,16(%0)\n\ + mtlr 0\n\ + mr 3,%1\n\ + " : : "r" (buf), "r" (val)); +} diff -Nru a/arch/ppc64/xmon/start.c b/arch/ppc64/xmon/start.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/start.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,335 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Transition to udbg isn't quite done yet...but very close. */ +#define USE_UDBG +#ifdef USE_UDBG +#include +#endif + +#ifndef USE_UDBG +static volatile unsigned char *sccc, *sccd; +#endif +unsigned long TXRDY, RXRDY; +extern void xmon_printf(const char *fmt, ...); +static int xmon_expect(const char *str, unsigned int timeout); + +#ifndef USE_UDBG +static int console = 0; +#endif +static int via_modem = 0; +/* static int xmon_use_sccb = 0; --Unused */ + +#define TB_SPEED 25000000 + +extern void *comport1; +static inline unsigned int readtb(void) +{ + unsigned int ret; + + asm volatile("mftb %0" : "=r" (ret) :); + return ret; +} + +#ifndef USE_UDBG +void buf_access(void) +{ + sccd[3] &= ~0x80; /* reset DLAB */ +} +#endif + +static void sysrq_handle_xmon(int key, struct pt_regs *pt_regs, struct kbd_struct *kbd, struct tty_struct *tty) +{ + xmon(pt_regs); +} +static struct sysrq_key_op sysrq_xmon_op = +{ + handler: sysrq_handle_xmon, + help_msg: "xmon", + action_msg: "Entering xmon\n", +}; + +void +xmon_map_scc(void) +{ + /* This maybe isn't the best place to register sysrq 'x' */ + __sysrq_put_key_op('x', &sysrq_xmon_op); +#ifndef USE_UDBG + /* should already be mapped by the kernel boot */ + sccd = (volatile unsigned char *) (((unsigned long)comport1)); + sccc = (volatile unsigned char *) (((unsigned long)comport1)+5); + TXRDY = 0x20; + RXRDY = 1; +#endif +} + +static int scc_initialized = 0; + +void xmon_init_scc(void); +extern void pmu_poll(void); + +int +xmon_write(void *handle, void *ptr, int nb) +{ +#ifdef USE_UDBG + return udbg_write(ptr, nb); +#else + char *p = ptr; + int i, c, ct; + + if (!scc_initialized) + xmon_init_scc(); + ct = 0; + for (i = 0; i < nb; ++i) { + while ((*sccc & TXRDY) == 0) { + } + c = p[i]; + if (c == '\n' && !ct) { + c = '\r'; + ct = 1; + --i; + } else { + if (console) + printk("%c", c); + ct = 0; + } + buf_access(); + *sccd = c; + } + return i; +#endif +} + +int xmon_wants_key; + +int +xmon_read(void *handle, void *ptr, int nb) +{ +#ifdef USE_UDBG + return udbg_read(ptr, nb); +#else + char *p = ptr; + int i, c; + + if (!scc_initialized) + xmon_init_scc(); + for (i = 0; i < nb; ++i) { + do { + while ((*sccc & RXRDY) == 0) + ; + buf_access(); + c = *sccd; + } while (c == 0x11 || c == 0x13); + *p++ = c; + } + return i; +#endif +} + +int +xmon_read_poll(void) +{ +#ifdef USE_UDBG + return udbg_getc_poll(); +#else + if ((*sccc & RXRDY) == 0) { + return -1; + } + buf_access(); + return *sccd; +#endif +} + +void +xmon_init_scc() +{ +#ifndef USE_UDBG + sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ + sccd[0] = 12; eieio(); /* DLL = 9600 baud */ + sccd[1] = 0; eieio(); + sccd[2] = 0; eieio(); /* FCR = 0 */ + sccd[3] = 3; eieio(); /* LCR = 8N1 */ + sccd[1] = 0; eieio(); /* IER = 0 */ +#endif + + scc_initialized = 1; + if (via_modem) { + for (;;) { + xmon_write(0, "ATE1V1\r", 7); + if (xmon_expect("OK", 5)) { + xmon_write(0, "ATA\r", 4); + if (xmon_expect("CONNECT", 40)) + break; + } + xmon_write(0, "+++", 3); + xmon_expect("OK", 3); + } + } +} + +void *xmon_stdin; +void *xmon_stdout; +void *xmon_stderr; + +void +xmon_init(void) +{ +} + +int +xmon_putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + xmon_putc('\r', f); + return xmon_write(f, &ch, 1) == 1? c: -1; +} + +int +xmon_putchar(int c) +{ + return xmon_putc(c, xmon_stdout); +} + +int +xmon_fputs(char *str, void *f) +{ + int n = strlen(str); + + return xmon_write(f, str, n) == n? 0: -1; +} + +int +xmon_readchar(void) +{ + char ch; + + for (;;) { + switch (xmon_read(xmon_stdin, &ch, 1)) { + case 1: + return ch; + case -1: + xmon_printf("read(stdin) returned -1\r\n", 0, 0); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int xmon_expect(const char *str, unsigned int timeout) +{ + int c; + unsigned int t0; + + timeout *= TB_SPEED; + t0 = readtb(); + do { + lineptr = line; + for (;;) { + c = xmon_read_poll(); + if (c == -1) { + if (readtb() - t0 > timeout) + return 0; + continue; + } + if (c == '\n') + break; + if (c != '\r' && lineptr < &line[sizeof(line) - 1]) + *lineptr++ = c; + } + *lineptr = 0; + } while (strstr(line, str) == NULL); + return 1; +} + +int +xmon_getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = xmon_readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + xmon_putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + xmon_putchar('\a'); + else { + xmon_putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +char * +xmon_fgets(char *str, int nb, void *f) +{ + char *p; + int c; + + for (p = str; p < str + nb - 1; ) { + c = xmon_getchar(); + if (c == -1) { + if (p == str) + return 0; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + return str; +} diff -Nru a/arch/ppc64/xmon/subr_prf.c b/arch/ppc64/xmon/subr_prf.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/subr_prf.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,55 @@ +/* + * Written by Cort Dougan to replace the version originally used + * by Paul Mackerras, which came from NetBSD and thus had copyright + * conflicts with Linux. + * + * This file makes liberal use of the standard linux utility + * routines to reduce the size of the binary. We assume we can + * trust some parts of Linux inside the debugger. + * -- Cort (cort@cs.nmt.edu) + * + * Copyright (C) 1999 Cort Dougan. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include "nonstdio.h" + +extern int xmon_write(void *, void *, int); + +void +xmon_vfprintf(void *f, const char *fmt, va_list ap) +{ + static char xmon_buf[2048]; + int n; + + n = vsprintf(xmon_buf, fmt, ap); + xmon_write(f, xmon_buf, n); +} + +void +xmon_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(stdout, fmt, ap); + va_end(ap); +} + +void +xmon_fprintf(void *f, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(f, fmt, ap); + va_end(ap); +} + diff -Nru a/arch/ppc64/xmon/xmon.c b/arch/ppc64/xmon/xmon.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ppc64/xmon/xmon.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2953 @@ +/* + * Routines providing a simple monitor for use on the PowerMac. + * + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nonstdio.h" +#include "privinst.h" +#include + +#include + +#include + +#define scanhex xmon_scanhex +#define skipbl xmon_skipbl + +#ifdef CONFIG_SMP +static unsigned long cpus_in_xmon = 0; +static unsigned long got_xmon = 0; +static volatile int take_xmon = -1; +#endif /* CONFIG_SMP */ + +static unsigned long adrs; +static int size = 1; +static unsigned long ndump = 64; +static unsigned long nidump = 16; +static unsigned long ncsum = 4096; +static int termch; + +static u_int bus_error_jmp[100]; +#define setjmp xmon_setjmp +#define longjmp xmon_longjmp + +#define memlist_entry list_entry +#define memlist_next(x) ((x)->next) +#define memlist_prev(x) ((x)->prev) + + +/* Max number of stack frames we are willing to produce on a backtrace. */ +#define MAXFRAMECOUNT 50 + +/* Breakpoint stuff */ +struct bpt { + unsigned long address; + unsigned instr; + unsigned long count; + unsigned char enabled; + char funcname[64]; /* function name for humans */ +}; + +#define NBPTS 16 +static struct bpt bpts[NBPTS]; +static struct bpt dabr; +static struct bpt iabr; +static unsigned bpinstr = 0x7fe00008; /* trap */ + +/* Prototypes */ +extern void (*debugger_fault_handler)(struct pt_regs *); +static int cmds(struct pt_regs *); +static int mread(unsigned long, void *, int); +static int mwrite(unsigned long, void *, int); +static void handle_fault(struct pt_regs *); +static void byterev(unsigned char *, int); +static void memex(void); +static int bsesc(void); +static void dump(void); +static void prdump(unsigned long, long); +#ifdef __MWERKS__ +static void prndump(unsigned, int); +static int nvreadb(unsigned); +#endif +static int ppc_inst_dump(unsigned long, long); +void print_address(unsigned long); +static int getsp(void); +static void dump_hash_table(void); +static void backtrace(struct pt_regs *); +static void excprint(struct pt_regs *); +static void prregs(struct pt_regs *); +static void memops(int); +static void memlocate(void); +static void memzcan(void); +static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned); +int skipbl(void); +int scanhex(unsigned long *valp); +static void scannl(void); +static int hexdigit(int); +void getstring(char *, int); +static void flush_input(void); +static int inchar(void); +static void take_input(char *); +/* static void openforth(void); */ +static unsigned long read_spr(int); +static void write_spr(int, unsigned long); +static void super_regs(void); +static void print_sysmap(void); +static void remove_bpts(void); +static void insert_bpts(void); +static struct bpt *at_breakpoint(unsigned long pc); +static void bpt_cmds(void); +static void cacheflush(void); +#ifdef CONFIG_SMP +static void cpu_cmd(void); +#endif /* CONFIG_SMP */ +static void csum(void); +static void mem_translate(void); +static void mem_check(void); +static void mem_find_real(void); +static void mem_find_vsid(void); +static void mem_check_full_group(void); +static void mem_check_pagetable_vsids (void); + +static void mem_map_check_slab(void); +static void mem_map_lock_pages(void); +static void mem_map_check_hash(void); +static void mem_check_dup_rpn (void); +static void show_task(struct task_struct * p); +static void xmon_show_state(void); +static void debug_trace(void); + +extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned long); +extern void printf(const char *fmt, ...); +extern void xmon_vfprintf(void *f, const char *fmt, va_list ap); +extern int xmon_putc(int c, void *f); +extern int putchar(int ch); +extern int xmon_read_poll(void); +extern int setjmp(u_int *); +extern void longjmp(u_int *, int); +extern unsigned long _ASR; +extern struct Naca *naca; + +pte_t *find_linux_pte(pgd_t *pgdir, unsigned long va); /* from htab.c */ + +#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3]) + +static char *help_string = "\ +Commands:\n\ + b show breakpoints\n\ + bd set data breakpoint\n\ + bi set instruction breakpoint\n\ + bc clear breakpoint\n\ + d dump bytes\n\ + di dump instructions\n\ + df dump float values\n\ + dd dump double values\n\ + e print exception information\n\ + f flush cache\n\ + h dump hash table\n\ + m examine/change memory\n\ + mm move a block of memory\n\ + ms set a block of memory\n\ + md compare two blocks of memory\n\ + ml locate a block of memory\n\ + mz zero a block of memory\n\ + mx translation information for an effective address\n\ + mi show information about memory allocation\n\ + M print System.map\n\ + p show the task list\n\ + r print registers\n\ + s single step\n\ + S print special registers\n\ + t print backtrace\n\ + T Enable/Disable PPCDBG flags\n\ + x exit monitor\n\ + z reboot\n\ +"; + +static int xmon_trace[NR_CPUS]; +#define SSTEP 1 /* stepping because of 's' command */ +#define BRSTEP 2 /* stepping over breakpoint */ + +/* + * Stuff for reading and writing memory safely + */ +extern inline void sync(void) +{ + asm volatile("sync; isync"); +} + +extern inline void __delay(unsigned int loops) +{ + if (loops != 0) + __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : : + "r" (loops) : "ctr"); +} + +/* (Ref: 64-bit PowerPC ELF ABI Spplement; Ian Lance Taylor, Zembu Labs). + A PPC stack frame looks like this: + + High Address + Back Chain + FP reg save area + GP reg save area + Local var space + Parameter save area (SP+48) + TOC save area (SP+40) + link editor doubleword (SP+32) + compiler doubleword (SP+24) + LR save (SP+16) + CR save (SP+8) + Back Chain (SP+0) + + Note that the LR (ret addr) may not be saved in the current frame if + no functions have been called from the current function. + */ + +/* + A traceback table typically follows each function. + The find_tb_table() func will fill in this struct. Note that the struct + is not an exact match with the encoded table defined by the ABI. It is + defined here more for programming convenience. + */ +struct tbtable { + unsigned long flags; /* flags: */ +#define TBTAB_FLAGSGLOBALLINK (1L<<47) +#define TBTAB_FLAGSISEPROL (1L<<46) +#define TBTAB_FLAGSHASTBOFF (1L<<45) +#define TBTAB_FLAGSINTPROC (1L<<44) +#define TBTAB_FLAGSHASCTL (1L<<43) +#define TBTAB_FLAGSTOCLESS (1L<<42) +#define TBTAB_FLAGSFPPRESENT (1L<<41) +#define TBTAB_FLAGSNAMEPRESENT (1L<<38) +#define TBTAB_FLAGSUSESALLOCA (1L<<37) +#define TBTAB_FLAGSSAVESCR (1L<<33) +#define TBTAB_FLAGSSAVESLR (1L<<32) +#define TBTAB_FLAGSSTORESBC (1L<<31) +#define TBTAB_FLAGSFIXUP (1L<<30) +#define TBTAB_FLAGSPARMSONSTK (1L<<0) + unsigned char fp_saved; /* num fp regs saved f(32-n)..f31 */ + unsigned char gpr_saved; /* num gpr's saved */ + unsigned char fixedparms; /* num fixed point parms */ + unsigned char floatparms; /* num float parms */ + unsigned char parminfo[32]; /* types of args. null terminated */ +#define TBTAB_PARMFIXED 1 +#define TBTAB_PARMSFLOAT 2 +#define TBTAB_PARMDFLOAT 3 + unsigned int tb_offset; /* offset from start of func */ + unsigned long funcstart; /* addr of start of function */ + char name[64]; /* name of function (null terminated)*/ +}; +static int find_tb_table(unsigned long codeaddr, struct tbtable *tab); + +#define SURVEILLANCE_TOKEN 9000 + +static inline void disable_surveillance(void) +{ + rtas_call(rtas_token("set-indicator"), 3, 1, NULL, SURVEILLANCE_TOKEN, + 0, 0); +} + +void +xmon(struct pt_regs *excp) +{ + struct pt_regs regs; + int cmd; + unsigned long msr; + + if (excp == NULL) { + /* Ok, grab regs as they are now. + This won't do a particularily good job because the + prologue has already been executed. + ToDo: We could reach back into the callers save + area to do a better job of representing the + caller's state. + */ + asm volatile ("std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + std 3,24(%0)\n\ + std 4,32(%0)\n\ + std 5,40(%0)\n\ + std 6,48(%0)\n\ + std 7,56(%0)\n\ + std 8,64(%0)\n\ + std 9,72(%0)\n\ + std 10,80(%0)\n\ + std 11,88(%0)\n\ + std 12,96(%0)\n\ + std 13,104(%0)\n\ + std 14,112(%0)\n\ + std 15,120(%0)\n\ + std 16,128(%0)\n\ + std 17,136(%0)\n\ + std 18,144(%0)\n\ + std 19,152(%0)\n\ + std 20,160(%0)\n\ + std 21,168(%0)\n\ + std 22,176(%0)\n\ + std 23,184(%0)\n\ + std 24,192(%0)\n\ + std 25,200(%0)\n\ + std 26,208(%0)\n\ + std 27,216(%0)\n\ + std 28,224(%0)\n\ + std 29,232(%0)\n\ + std 30,240(%0)\n\ + std 31,248(%0)" : : "b" (®s)); + printf("xmon called\n"); + /* Fetch the link reg for this stack frame. + NOTE: the prev printf fills in the lr. */ + regs.nip = regs.link = ((unsigned long *)(regs.gpr[1]))[2]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + excp = ®s; + } + + msr = get_msr(); + set_msrd(msr & ~MSR_EE); /* disable interrupts */ + excprint(excp); +#ifdef CONFIG_SMP + if (test_and_set_bit(smp_processor_id(), &cpus_in_xmon)) + for (;;) + ; + while (test_and_set_bit(0, &got_xmon)) { + if (take_xmon == smp_processor_id()) { + take_xmon = -1; + break; + } + } + /* + * XXX: breakpoints are removed while any cpu is in xmon + */ +#endif /* CONFIG_SMP */ + remove_bpts(); + disable_surveillance(); + cmd = cmds(excp); + if (cmd == 's') { + xmon_trace[smp_processor_id()] = SSTEP; + excp->msr |= 0x400; + } else if (at_breakpoint(excp->nip)) { + xmon_trace[smp_processor_id()] = BRSTEP; + excp->msr |= 0x400; + } else { + xmon_trace[smp_processor_id()] = 0; + insert_bpts(); + } +#ifdef CONFIG_SMP + clear_bit(0, &got_xmon); + clear_bit(smp_processor_id(), &cpus_in_xmon); +#endif /* CONFIG_SMP */ + set_msrd(msr); /* restore interrupt enable */ +} + +/* Code can call this to get a backtrace and continue. */ +void +xmon_backtrace(const char *fmt, ...) +{ + va_list ap; + struct pt_regs regs; + + + /* Ok, grab regs as they are now. + This won't do a particularily good job because the + prologue has already been executed. + ToDo: We could reach back into the callers save + area to do a better job of representing the + caller's state. + */ + asm volatile ("std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + std 3,24(%0)\n\ + std 4,32(%0)\n\ + std 5,40(%0)\n\ + std 6,48(%0)\n\ + std 7,56(%0)\n\ + std 8,64(%0)\n\ + std 9,72(%0)\n\ + std 10,80(%0)\n\ + std 11,88(%0)\n\ + std 12,96(%0)\n\ + std 13,104(%0)\n\ + std 14,112(%0)\n\ + std 15,120(%0)\n\ + std 16,128(%0)\n\ + std 17,136(%0)\n\ + std 18,144(%0)\n\ + std 19,152(%0)\n\ + std 20,160(%0)\n\ + std 21,168(%0)\n\ + std 22,176(%0)\n\ + std 23,184(%0)\n\ + std 24,192(%0)\n\ + std 25,200(%0)\n\ + std 26,208(%0)\n\ + std 27,216(%0)\n\ + std 28,224(%0)\n\ + std 29,232(%0)\n\ + std 30,240(%0)\n\ + std 31,248(%0)" : : "b" (®s)); + /* Fetch the link reg for this stack frame. + NOTE: the prev printf fills in the lr. */ + regs.nip = regs.link = ((unsigned long *)(regs.gpr[1]))[2]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + + va_start(ap, fmt); + xmon_vfprintf(stdout, fmt, ap); + xmon_putc('\n', stdout); + va_end(ap); + take_input("\n"); + backtrace(®s); +} + +/* Call this to poll for ^C during busy operations. + * Returns true if the user has hit ^C. + */ +int +xmon_interrupted(void) +{ + int ret = xmon_read_poll(); + if (ret == 3) { + printf("\n^C interrupted.\n"); + return 1; + } + return 0; +} + + +void +xmon_irq(int irq, void *d, struct pt_regs *regs) +{ + unsigned long flags; + __save_flags(flags); + __cli(); + printf("Keyboard interrupt\n"); + xmon(regs); + __restore_flags(flags); +} + +int +xmon_bpt(struct pt_regs *regs) +{ + struct bpt *bp; + + bp = at_breakpoint(regs->nip); + if (!bp) + return 0; + if (bp->count) { + --bp->count; + remove_bpts(); + excprint(regs); + xmon_trace[smp_processor_id()] = BRSTEP; + regs->msr |= 0x400; + } else { + printf("Stopped at breakpoint %x (%lx %s)\n", (bp - bpts)+1, bp->address, bp->funcname); + xmon(regs); + } + return 1; +} + +int +xmon_sstep(struct pt_regs *regs) +{ + if (!xmon_trace[smp_processor_id()]) + return 0; + if (xmon_trace[smp_processor_id()] == BRSTEP) { + xmon_trace[smp_processor_id()] = 0; + insert_bpts(); + } else { + xmon(regs); + } + return 1; +} + +int +xmon_dabr_match(struct pt_regs *regs) +{ + if (dabr.enabled && dabr.count) { + --dabr.count; + remove_bpts(); + excprint(regs); + xmon_trace[smp_processor_id()] = BRSTEP; + regs->msr |= 0x400; + } else { + dabr.instr = regs->nip; + xmon(regs); + } + return 1; +} + +int +xmon_iabr_match(struct pt_regs *regs) +{ + if (iabr.enabled && iabr.count) { + --iabr.count; + remove_bpts(); + excprint(regs); + xmon_trace[smp_processor_id()] = BRSTEP; + regs->msr |= 0x400; + } else { + xmon(regs); + } + return 1; +} + +static struct bpt * +at_breakpoint(unsigned long pc) +{ + int i; + struct bpt *bp; + + if (dabr.enabled && pc == dabr.instr) + return &dabr; + if (iabr.enabled && pc == iabr.address) + return &iabr; + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) + if (bp->enabled && pc == bp->address) + return bp; + return 0; +} + +static void +insert_bpts() +{ + int i; + struct bpt *bp; + + if (_machine != _MACH_pSeries) + return; + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if (!bp->enabled) + continue; + if (mread(bp->address, &bp->instr, 4) != 4 + || mwrite(bp->address, &bpinstr, 4) != 4) { + printf("Couldn't insert breakpoint at %x, disabling\n", + bp->address); + bp->enabled = 0; + } else { + store_inst((void *)bp->address); + } + } + + if (!__is_processor(PV_POWER4)) { + if (dabr.enabled) + set_dabr(dabr.address); + if (iabr.enabled) + set_iabr(iabr.address); + } +} + +static void +remove_bpts() +{ + int i; + struct bpt *bp; + unsigned instr; + + if (_machine != _MACH_pSeries) + return; + if (!__is_processor(PV_POWER4)) { + set_dabr(0); + set_iabr(0); + } + + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if (!bp->enabled) + continue; + if (mread(bp->address, &instr, 4) == 4 + && instr == bpinstr + && mwrite(bp->address, &bp->instr, 4) != 4) + printf("Couldn't remove breakpoint at %x\n", + bp->address); + else + store_inst((void *)bp->address); + } +} + +static char *last_cmd; + +/* Command interpreting routine */ +static int +cmds(struct pt_regs *excp) +{ + int cmd; + + last_cmd = NULL; + for(;;) { +#ifdef CONFIG_SMP + printf("%d:", smp_processor_id()); +#endif /* CONFIG_SMP */ + printf("mon> "); + fflush(stdout); + flush_input(); + termch = 0; + cmd = skipbl(); + if( cmd == '\n' ) { + if (last_cmd == NULL) + continue; + take_input(last_cmd); + last_cmd = NULL; + cmd = inchar(); + } + switch (cmd) { + case 'z': + machine_restart(NULL); + break; + case 'm': + cmd = inchar(); + switch (cmd) { + case 'm': + case 's': + case 'd': + memops(cmd); + break; + case 'l': + memlocate(); + break; + case 'z': + memzcan(); + break; + case 'x': + mem_translate(); + break; + case 'c': + mem_check(); + break; + case 'g': + mem_check_full_group(); + break; + case 'j': + mem_map_check_slab(); + break; + case 'h': + mem_map_check_hash(); + break; + case 'f': + mem_find_real(); + break; + case 'e': + mem_find_vsid(); + break; + case 'r': + mem_check_dup_rpn(); + break; + case 'i': + show_mem(); + break; + case 'o': + mem_check_pagetable_vsids (); + break; + case 'q': + mem_map_lock_pages() ; + break; + default: + termch = cmd; + memex(); + } + break; + case 'd': + dump(); + break; + case 'r': + if (excp != NULL) + prregs(excp); /* print regs */ + break; + case 'e': + if (excp == NULL) + printf("No exception information\n"); + else + excprint(excp); + break; + case 'M': + print_sysmap(); + break; + case 'S': + super_regs(); + break; + case 't': + backtrace(excp); + break; + case 'f': + cacheflush(); + break; + case 'h': + dump_hash_table(); + break; + case 's': + case 'x': + case EOF: + return cmd; + case '?': + printf(help_string); + break; + case 'p': + xmon_show_state(); + break; + case 'b': + bpt_cmds(); + break; + case 'C': + csum(); + break; +#ifdef CONFIG_SMP + case 'c': + cpu_cmd(); + break; +#endif /* CONFIG_SMP */ + case 'T': + debug_trace(); + break; + default: + printf("Unrecognized command: "); + do { + if( ' ' < cmd && cmd <= '~' ) + putchar(cmd); + else + printf("\\x%x", cmd); + cmd = inchar(); + } while (cmd != '\n'); + printf(" (type ? for help)\n"); + break; + } + } +} + +#ifdef CONFIG_SMP +static void cpu_cmd(void) +{ + unsigned long cpu; + int timeout; + int cmd; + + cmd = inchar(); + if (cmd == 'i') { + printf("stopping all cpus\n"); + /* interrupt other cpu(s) */ + cpu = MSG_ALL_BUT_SELF; + smp_send_xmon_break(cpu); + return; + } + termch = cmd; + if (!scanhex(&cpu)) { + /* print cpus waiting or in xmon */ + printf("cpus stopped:"); + for (cpu = 0; cpu < NR_CPUS; ++cpu) { + if (test_bit(cpu, &cpus_in_xmon)) { + printf(" %d", cpu); + if (cpu == smp_processor_id()) + printf("*", cpu); + } + } + printf("\n"); + return; + } + /* try to switch to cpu specified */ + take_xmon = cpu; + timeout = 10000000; + while (take_xmon >= 0) { + if (--timeout == 0) { + /* yes there's a race here */ + take_xmon = -1; + printf("cpu %u didn't take control\n", cpu); + return; + } + } + /* now have to wait to be given control back */ + while (test_and_set_bit(0, &got_xmon)) { + if (take_xmon == smp_processor_id()) { + take_xmon = -1; + break; + } + } +} +#endif /* CONFIG_SMP */ + +static unsigned short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +static void +csum(void) +{ + unsigned int i; + unsigned short fcs; + unsigned char v; + + if (!scanhex(&adrs)) + return; + if (!scanhex(&ncsum)) + return; + fcs = 0xffff; + for (i = 0; i < ncsum; ++i) { + if (mread(adrs+i, &v, 1) == 0) { + printf("csum stopped at %x\n", adrs+i); + break; + } + fcs = FCS(fcs, v); + } + printf("%x\n", fcs); +} + +static char *breakpoint_help_string = + "Breakpoint command usage:\n" + "b show breakpoints\n" + "b [cnt] set breakpoint at given instr addr\n" + "bc clear all breakpoints\n" + "bc clear breakpoint number n or at addr\n" + "bi [cnt] set hardware instr breakpoint (broken?)\n" + "bd [cnt] set hardware data breakpoint (broken?)\n" + ""; + +static void +bpt_cmds(void) +{ + int cmd; + unsigned long a; + int mode, i; + struct bpt *bp; + struct tbtable tab; + + cmd = inchar(); + switch (cmd) { + case 'd': /* bd - hardware data breakpoint */ + if (__is_processor(PV_POWER4)) { + printf("Not implemented on POWER4\n"); + break; + } + mode = 7; + cmd = inchar(); + if (cmd == 'r') + mode = 5; + else if (cmd == 'w') + mode = 6; + else + termch = cmd; + dabr.address = 0; + dabr.count = 0; + dabr.enabled = scanhex(&dabr.address); + scanhex(&dabr.count); + if (dabr.enabled) + dabr.address = (dabr.address & ~7) | mode; + break; + case 'i': /* bi - hardware instr breakpoint */ + if (__is_processor(PV_POWER4)) { + printf("Not implemented on POWER4\n"); + break; + } + iabr.address = 0; + iabr.count = 0; + iabr.enabled = scanhex(&iabr.address); + if (iabr.enabled) + iabr.address |= 3; + scanhex(&iabr.count); + break; + case 'c': + if (!scanhex(&a)) { + /* clear all breakpoints */ + for (i = 0; i < NBPTS; ++i) + bpts[i].enabled = 0; + iabr.enabled = 0; + dabr.enabled = 0; + printf("All breakpoints cleared\n"); + } else { + if (a <= NBPTS && a >= 1) { + /* assume a breakpoint number */ + --a; /* bp nums are 1 based */ + bp = &bpts[a]; + } else { + /* assume a breakpoint address */ + bp = at_breakpoint(a); + } + if (bp == 0) { + printf("No breakpoint at %x\n", a); + } else { + printf("Cleared breakpoint %x (%lx %s)\n", (bp - bpts)+1, bp->address, bp->funcname); + bp->enabled = 0; + } + } + break; + case '?': + printf(breakpoint_help_string); + break; + default: + termch = cmd; + cmd = skipbl(); + if (cmd == '?') { + printf(breakpoint_help_string); + break; + } + termch = cmd; + if (!scanhex(&a)) { + /* print all breakpoints */ + int bpnum; + + printf(" type address count\n"); + if (dabr.enabled) { + printf(" data %.16lx %8x [", dabr.address & ~7, + dabr.count); + if (dabr.address & 1) + printf("r"); + if (dabr.address & 2) + printf("w"); + printf("]\n"); + } + if (iabr.enabled) + printf(" inst %.16lx %8x\n", iabr.address & ~3, + iabr.count); + for (bp = bpts, bpnum = 1; bp < &bpts[NBPTS]; ++bp, ++bpnum) + if (bp->enabled) + printf("%2x trap %.16lx %8x %s\n", bpnum, bp->address, bp->count, bp->funcname); + break; + } + bp = at_breakpoint(a); + if (bp == 0) { + for (bp = bpts; bp < &bpts[NBPTS]; ++bp) + if (!bp->enabled) + break; + if (bp >= &bpts[NBPTS]) { + printf("Sorry, no free breakpoints. Please clear one first.\n"); + break; + } + } + bp->enabled = 1; + bp->address = a; + bp->count = 0; + scanhex(&bp->count); + /* Find the function name just once. */ + bp->funcname[0] = '\0'; + if (find_tb_table(bp->address, &tab) && tab.name[0]) { + /* Got a nice name for it. */ + int delta = bp->address - tab.funcstart; + sprintf(bp->funcname, "%s+0x%x", tab.name, delta); + } + printf("Set breakpoint %2x trap %.16lx %8x %s\n", (bp-bpts)+1, bp->address, bp->count, bp->funcname); + break; + } +} + +/* Very cheap human name for vector lookup. */ +static +const char *getvecname(unsigned long vec) +{ + char *ret; + switch (vec) { + case 0x100: ret = "(System Reset)"; break; + case 0x200: ret = "(Machine Check)"; break; + case 0x300: ret = "(Data Access)"; break; + case 0x400: ret = "(Instruction Access)"; break; + case 0x500: ret = "(Hardware Interrupt)"; break; + case 0x600: ret = "(Alignment)"; break; + case 0x700: ret = "(Program Check)"; break; + case 0x800: ret = "(FPU Unavailable)"; break; + case 0x900: ret = "(Decrementer)"; break; + case 0xc00: ret = "(System Call)"; break; + case 0xd00: ret = "(Single Step)"; break; + case 0xf00: ret = "(Performance Monitor)"; break; + default: ret = ""; + } + return ret; +} + +static void +backtrace(struct pt_regs *excp) +{ + unsigned long sp; + unsigned long lr; + unsigned long stack[3]; + struct pt_regs regs; + struct tbtable tab; + int framecount; + char *funcname; + /* declare these as raw ptrs so we don't get func descriptors */ + extern void *ret_from_except, *ret_from_syscall_1; + + if (excp != NULL) { + lr = excp->link; + sp = excp->gpr[1]; + } else { + /* Use care not to call any function before this point + so the saved lr has a chance of being good. */ + asm volatile ("mflr %0" : "=r" (lr) :); + sp = getsp(); + } + scanhex(&sp); + scannl(); + for (framecount = 0; + sp != 0 && framecount < MAXFRAMECOUNT; + sp = stack[0], framecount++) { + if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) + break; +#if 0 + if (lr != 0) { + stack[2] = lr; /* fake out the first saved lr. It may not be saved yet. */ + lr = 0; + } +#endif + printf("%.16lx %.16lx", sp, stack[2]); + /* TAI -- for now only the ones cast to unsigned long will match. + * Need to test the rest... + */ + if ((stack[2] == (unsigned long)ret_from_except && + (funcname = "ret_from_except")) + || (stack[2] == (unsigned long)ret_from_syscall_1 && + (funcname = "ret_from_syscall_1")) +#if 0 + || stack[2] == (unsigned) &ret_from_syscall_2 + || stack[2] == (unsigned) &do_signal_ret +#endif + ) { + printf(" %s\n", funcname); + if (mread(sp+112, ®s, sizeof(regs)) != sizeof(regs)) + break; + printf("exception: %lx %s regs %lx\n", regs.trap, getvecname(regs.trap), sp+112); + printf(" %.16lx", regs.nip); + if ((regs.nip & 0xffffffff00000000UL) && + find_tb_table(regs.nip, &tab)) { + int delta = regs.nip-tab.funcstart; + if (delta < 0) + printf(" "); + else + printf(" %s+0x%x", tab.name, delta); + } + printf("\n"); + if (regs.gpr[1] < sp) { + printf("\n", regs.gpr[1]); + break; + } + + sp = regs.gpr[1]; + if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) + break; + } else { + if (stack[2] && find_tb_table(stack[2], &tab)) { + int delta = stack[2]-tab.funcstart; + if (delta < 0) + printf(" "); + else + printf(" %s+0x%x", tab.name, delta); + } + printf("\n"); + } + if (stack[0] && stack[0] <= sp) { + if ((stack[0] & 0xffffffff00000000UL) == 0) + printf("\n", stack[0]); + else + printf("\n", stack[0]); + break; + } + } + if (framecount >= MAXFRAMECOUNT) + printf("\n"); +} + +int +getsp() +{ + int x; + + asm("mr %0,1" : "=r" (x) :); + return x; +} + +spinlock_t exception_print_lock = SPIN_LOCK_UNLOCKED; + +void +excprint(struct pt_regs *fp) +{ + struct task_struct *c; + struct tbtable tab; + unsigned long flags; + + spin_lock_irqsave(&exception_print_lock, flags); + +#ifdef CONFIG_SMP + printf("cpu %d: ", smp_processor_id()); +#endif /* CONFIG_SMP */ + + printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(fp->trap), fp); + printf(" pc: %lx", fp->nip); + if (find_tb_table(fp->nip, &tab) && tab.name[0]) { + /* Got a nice name for it */ + int delta = fp->nip - tab.funcstart; + printf(" (%s+0x%x)", tab.name, delta); + } + printf("\n"); + printf(" lr: %lx", fp->link); + if (find_tb_table(fp->link, &tab) && tab.name[0]) { + /* Got a nice name for it */ + int delta = fp->link - tab.funcstart; + printf(" (%s+0x%x)", tab.name, delta); + } + printf("\n"); + printf(" sp: %lx\n", fp->gpr[1]); + printf(" msr: %lx\n", fp->msr); + + if (fp->trap == 0x300 || fp->trap == 0x600) { + printf(" dar: %lx\n", fp->dar); + printf(" dsisr: %lx\n", fp->dsisr); + } + + /* XXX: need to copy current or we die. Why? */ + c = current; + printf(" current = 0x%lx\n", c); + printf(" paca = 0x%lx\n", get_paca()); + if (c) { + printf(" current = %lx, pid = %ld, comm = %s\n", + c, c->pid, c->comm); + } + + spin_unlock_irqrestore(&exception_print_lock, flags); +} + +void +prregs(struct pt_regs *fp) +{ + int n; + unsigned long base; + + if (scanhex((void *)&base)) + fp = (struct pt_regs *) base; + for (n = 0; n < 16; ++n) + printf("R%.2ld = %.16lx R%.2ld = %.16lx\n", n, fp->gpr[n], + n+16, fp->gpr[n+16]); + printf("pc = %.16lx msr = %.16lx\nlr = %.16lx cr = %.16lx\n", + fp->nip, fp->msr, fp->link, fp->ccr); + printf("ctr = %.16lx xer = %.16lx trap = %8lx\n", + fp->ctr, fp->xer, fp->trap); +} + +void +cacheflush(void) +{ + int cmd; + unsigned long nflush; + + cmd = inchar(); + if (cmd != 'i') + termch = cmd; + scanhex((void *)&adrs); + if (termch != '\n') + termch = 0; + nflush = 1; + scanhex(&nflush); + nflush = (nflush + 31) / 32; + if (cmd != 'i') { + for (; nflush > 0; --nflush, adrs += 0x20) + cflush((void *) adrs); + } else { + for (; nflush > 0; --nflush, adrs += 0x20) + cinval((void *) adrs); + } +} + +unsigned long +read_spr(int n) +{ + unsigned int instrs[2]; + unsigned long (*code)(void); + unsigned long opd[3]; + + instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + opd[0] = instrs; + opd[1] = 0; + opd[2] = 0; + store_inst(instrs); + store_inst(instrs+1); + code = (unsigned long (*)(void)) opd; + + return code(); +} + +void +write_spr(int n, unsigned long val) +{ + unsigned int instrs[2]; + unsigned long (*code)(unsigned long); + unsigned long opd[3]; + + instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + opd[0] = instrs; + opd[1] = 0; + opd[2] = 0; + store_inst(instrs); + store_inst(instrs+1); + code = (unsigned long (*)(unsigned long)) opd; + + code(val); +} + +static unsigned long regno; +extern char exc_prolog; +extern char dec_exc; + +void +print_sysmap(void) +{ + extern char *sysmap; + if ( sysmap ) + printf("System.map: \n%s", sysmap); +} + +void +super_regs() +{ + int i, cmd; + unsigned long val; + struct Paca* ptrPaca = NULL; + struct ItLpPaca* ptrLpPaca = NULL; + struct ItLpRegSave* ptrLpRegSave = NULL; + + cmd = skipbl(); + if (cmd == '\n') { + unsigned long sp, toc; + asm("mr %0,1" : "=r" (sp) :); + asm("mr %0,2" : "=r" (toc) :); + + printf("msr = %.16lx sprg0= %.16lx\n", get_msr(), get_sprg0()); + printf("pvr = %.16lx sprg1= %.16lx\n", get_pvr(), get_sprg1()); + printf("dec = %.16lx sprg2= %.16lx\n", get_dec(), get_sprg2()); + printf("sp = %.16lx sprg3= %.16lx\n", sp, get_sprg3()); + printf("toc = %.16lx dar = %.16lx\n", toc, get_dar()); + printf("srr0 = %.16lx srr1 = %.16lx\n", get_srr0(), get_srr1()); + printf("asr = %.16lx\n", mfasr()); + for (i = 0; i < 8; ++i) + printf("sr%.2ld = %.16lx sr%.2ld = %.16lx\n", i, get_sr(i), i+8, get_sr(i+8)); + + // Dump out relevant Paca data areas. + printf("Paca: \n"); + ptrPaca = (struct Paca*)get_sprg3(); + + printf(" Local Processor Control Area (LpPaca): \n"); + ptrLpPaca = ptrPaca->xLpPacaPtr; + printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n", ptrLpPaca->xSavedSrr0, ptrLpPaca->xSavedSrr1); + printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n", ptrLpPaca->xSavedGpr3, ptrLpPaca->xSavedGpr4); + printf(" Saved Gpr5=%.16lx \n", ptrLpPaca->xSavedGpr5); + + printf(" Local Processor Register Save Area (LpRegSave): \n"); + ptrLpRegSave = ptrPaca->xLpRegSavePtr; + printf(" Saved Sprg0=%.16lx Saved Sprg1=%.16lx \n", ptrLpRegSave->xSPRG0, ptrLpRegSave->xSPRG0); + printf(" Saved Sprg2=%.16lx Saved Sprg3=%.16lx \n", ptrLpRegSave->xSPRG2, ptrLpRegSave->xSPRG3); + printf(" Saved Msr =%.16lx Saved Nia =%.16lx \n", ptrLpRegSave->xMSR, ptrLpRegSave->xNIA); + + return; + } + + scanhex(®no); + switch (cmd) { + case 'w': + val = read_spr(regno); + scanhex(&val); + write_spr(regno, val); + /* fall through */ + case 'r': + printf("spr %lx = %lx\n", regno, read_spr(regno)); + break; + case 's': + val = get_sr(regno); + scanhex(&val); + set_sr(regno, val); + break; + case 'm': + val = get_msr(); + scanhex(&val); + set_msrd(val); + break; + } + scannl(); +} + +#if 0 +static void +openforth() +{ + int c; + char *p; + char cmd[1024]; + int args[5]; + extern int (*prom_entry)(int *); + + p = cmd; + c = skipbl(); + while (c != '\n') { + *p++ = c; + c = inchar(); + } + *p = 0; + args[0] = (int) "interpret"; + args[1] = 1; + args[2] = 1; + args[3] = (int) cmd; + (*prom_entry)(args); + printf("\n"); + if (args[4] != 0) + printf("error %x\n", args[4]); +} +#endif + +#ifndef CONFIG_PPC64BRIDGE +static void +dump_hash_table_seg(unsigned seg, unsigned start, unsigned end) +{ + extern void *Hash; + extern unsigned long Hash_size; + unsigned *htab = Hash; + unsigned hsize = Hash_size; + unsigned v, hmask, va, last_va; + int found, last_found, i; + unsigned *hg, w1, last_w2, last_va0; + + last_found = 0; + hmask = hsize / 64 - 1; + va = start; + start = (start >> 12) & 0xffff; + end = (end >> 12) & 0xffff; + for (v = start; v < end; ++v) { + found = 0; + hg = htab + (((v ^ seg) & hmask) * 16); + w1 = 0x80000000 | (seg << 7) | (v >> 10); + for (i = 0; i < 8; ++i, hg += 2) { + if (*hg == w1) { + found = 1; + break; + } + } + if (!found) { + w1 ^= 0x40; + hg = htab + ((~(v ^ seg) & hmask) * 16); + for (i = 0; i < 8; ++i, hg += 2) { + if (*hg == w1) { + found = 1; + break; + } + } + } + if (!(last_found && found && (hg[1] & ~0x180) == last_w2 + 4096)) { + if (last_found) { + if (last_va != last_va0) + printf(" ... %x", last_va); + printf("\n"); + } + if (found) { + printf("%x to %x", va, hg[1]); + last_va0 = va; + } + last_found = found; + } + if (found) { + last_w2 = hg[1] & ~0x180; + last_va = va; + } + va += 4096; + } + if (last_found) + printf(" ... %x\n", last_va); +} + +#else /* CONFIG_PPC64BRIDGE */ +static void +dump_hash_table_seg(unsigned seg, unsigned start, unsigned end) +{ + extern void *Hash; + extern unsigned long Hash_size; + unsigned *htab = Hash; + unsigned hsize = Hash_size; + unsigned v, hmask, va, last_va; + int found, last_found, i; + unsigned *hg, w1, last_w2, last_va0; + + last_found = 0; + hmask = hsize / 128 - 1; + va = start; + start = (start >> 12) & 0xffff; + end = (end >> 12) & 0xffff; + for (v = start; v < end; ++v) { + found = 0; + hg = htab + (((v ^ seg) & hmask) * 32); + w1 = 1 | (seg << 12) | ((v & 0xf800) >> 4); + for (i = 0; i < 8; ++i, hg += 4) { + if (hg[1] == w1) { + found = 1; + break; + } + } + if (!found) { + w1 ^= 2; + hg = htab + ((~(v ^ seg) & hmask) * 32); + for (i = 0; i < 8; ++i, hg += 4) { + if (hg[1] == w1) { + found = 1; + break; + } + } + } + if (!(last_found && found && (hg[3] & ~0x180) == last_w2 + 4096)) { + if (last_found) { + if (last_va != last_va0) + printf(" ... %x", last_va); + printf("\n"); + } + if (found) { + printf("%x to %x", va, hg[3]); + last_va0 = va; + } + last_found = found; + } + if (found) { + last_w2 = hg[3] & ~0x180; + last_va = va; + } + va += 4096; + } + if (last_found) + printf(" ... %x\n", last_va); +} +#endif /* CONFIG_PPC64BRIDGE */ + +static unsigned long hash_ctx; +static unsigned long hash_start; +static unsigned long hash_end; + +static void +dump_hash_table() +{ + int seg; + unsigned seg_start, seg_end; + + hash_ctx = 0; + hash_start = 0; + hash_end = 0xfffff000; + scanhex(&hash_ctx); + scanhex(&hash_start); + scanhex(&hash_end); + printf("Mappings for context %x\n", hash_ctx); + seg_start = hash_start; + for (seg = hash_start >> 28; seg <= hash_end >> 28; ++seg) { + seg_end = (seg << 28) | 0x0ffff000; + if (seg_end > hash_end) + seg_end = hash_end; + dump_hash_table_seg((hash_ctx << 4) + seg, seg_start, seg_end); + seg_start = seg_end + 0x1000; + } +} + +int +mread(unsigned long adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if( setjmp(bus_error_jmp) == 0 ){ + debugger_fault_handler = handle_fault; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: *(short *)q = *(short *)p; break; + case 4: *(int *)q = *(int *)p; break; + default: + for( ; n < size; ++n ) { + *q++ = *p++; + sync(); + } + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } + debugger_fault_handler = 0; + return n; +} + +int +mwrite(unsigned long adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if( setjmp(bus_error_jmp) == 0 ){ + debugger_fault_handler = handle_fault; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: *(short *)p = *(short *)q; break; + case 4: *(int *)p = *(int *)q; break; + default: + for( ; n < size; ++n ) { + *p++ = *q++; + sync(); + } + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } else { + printf("*** Error writing address %x\n", adrs + n); + } + debugger_fault_handler = 0; + return n; +} + +static int fault_type; +static char *fault_chars[] = { "--", "**", "##" }; + +static void +handle_fault(struct pt_regs *regs) +{ + fault_type = regs->trap == 0x200? 0: regs->trap == 0x300? 1: 2; + longjmp(bus_error_jmp, 1); +} + +#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t)) + +void +byterev(unsigned char *val, int size) +{ + int t; + + switch (size) { + case 2: + SWAP(val[0], val[1], t); + break; + case 4: + SWAP(val[0], val[3], t); + SWAP(val[1], val[2], t); + break; + case 8: /* is there really any use for this? */ + SWAP(val[0], val[7], t); + SWAP(val[1], val[6], t); + SWAP(val[2], val[5], t); + SWAP(val[3], val[4], t); + break; + } +} + +static int brev; +static int mnoread; + +static char *memex_help_string = + "Memory examine command usage:\n" + "m [addr] [flags] examine/change memory\n" + " addr is optional. will start where left off.\n" + " flags may include chars from this set:\n" + " b modify by bytes (default)\n" + " w modify by words (2 byte)\n" + " l modify by longs (4 byte)\n" + " d modify by doubleword (8 byte)\n" + " r toggle reverse byte order mode\n" + " n do not read memory (for i/o spaces)\n" + " . ok to read (default)\n" + "NOTE: flags are saved as defaults\n" + ""; + +static char *memex_subcmd_help_string = + "Memory examine subcommands:\n" + " hexval write this val to current location\n" + " 'string' write chars from string to this location\n" + " ' increment address\n" + " ^ decrement address\n" + " / increment addr by 0x10. //=0x100, ///=0x1000, etc\n" + " \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n" + " ` clear no-read flag\n" + " ; stay at this addr\n" + " v change to byte mode\n" + " w change to word (2 byte) mode\n" + " l change to long (4 byte) mode\n" + " u change to doubleword (8 byte) mode\n" + " m addr change current addr\n" + " n toggle no-read flag\n" + " r toggle byte reverse flag\n" + " < count back up count bytes\n" + " > count skip forward count bytes\n" + " x exit this mode\n" + ""; + +void +memex() +{ + int cmd, inc, i, nslash; + unsigned long n; + unsigned char val[16]; + + scanhex((void *)&adrs); + cmd = skipbl(); + if (cmd == '?') { + printf(memex_help_string); + return; + } else { + termch = cmd; + } + last_cmd = "m\n"; + while ((cmd = skipbl()) != '\n') { + switch( cmd ){ + case 'b': size = 1; break; + case 'w': size = 2; break; + case 'l': size = 4; break; + case 'd': size = 8; break; + case 'r': brev = !brev; break; + case 'n': mnoread = 1; break; + case '.': mnoread = 0; break; + } + } + if( size <= 0 ) + size = 1; + else if( size > 8 ) + size = 8; + for(;;){ + if (!mnoread) + n = mread(adrs, val, size); + printf("%.16x%c", adrs, brev? 'r': ' '); + if (!mnoread) { + if (brev) + byterev(val, size); + putchar(' '); + for (i = 0; i < n; ++i) + printf("%.2x", val[i]); + for (; i < size; ++i) + printf("%s", fault_chars[fault_type]); + } + putchar(' '); + inc = size; + nslash = 0; + for(;;){ + if( scanhex(&n) ){ + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + inc = size; + } + cmd = skipbl(); + if (cmd == '\n') + break; + inc = 0; + switch (cmd) { + case '\'': + for(;;){ + n = inchar(); + if( n == '\\' ) + n = bsesc(); + else if( n == '\'' ) + break; + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + adrs += size; + } + adrs -= size; + inc = size; + break; + case ',': + adrs += size; + break; + case '.': + mnoread = 0; + break; + case ';': + break; + case 'x': + case EOF: + scannl(); + return; + case 'b': + case 'v': + size = 1; + break; + case 'w': + size = 2; + break; + case 'l': + size = 4; + break; + case 'u': + size = 8; + break; + case '^': + adrs -= size; + break; + break; + case '/': + if (nslash > 0) + adrs -= 1 << nslash; + else + nslash = 0; + nslash += 4; + adrs += 1 << nslash; + break; + case '\\': + if (nslash < 0) + adrs += 1 << -nslash; + else + nslash = 0; + nslash -= 4; + adrs -= 1 << -nslash; + break; + case 'm': + scanhex((void *)&adrs); + break; + case 'n': + mnoread = 1; + break; + case 'r': + brev = !brev; + break; + case '<': + n = size; + scanhex(&n); + adrs -= n; + break; + case '>': + n = size; + scanhex(&n); + adrs += n; + break; + case '?': + printf(memex_subcmd_help_string); + break; + } + } + adrs += inc; + } +} + +int +bsesc() +{ + int c; + + c = inchar(); + switch( c ){ + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 'b': c = '\b'; break; + case 't': c = '\t'; break; + } + return c; +} + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) +void +dump() +{ + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&adrs); + if( termch != '\n') + termch = 0; + if( c == 'i' ){ + scanhex(&nidump); + if( nidump == 0 ) + nidump = 16; + adrs += ppc_inst_dump(adrs, nidump); + last_cmd = "di\n"; + } else { + scanhex(&ndump); + if( ndump == 0 ) + ndump = 64; + prdump(adrs, ndump); + adrs += ndump; + last_cmd = "d\n"; + } +} + +void +prdump(unsigned long adrs, long ndump) +{ + long n, m, c, r, nr; + unsigned char temp[16]; + + for( n = ndump; n > 0; ){ + printf("%.16lx", adrs); + putchar(' '); + r = n < 16? n: 16; + nr = mread(adrs, temp, r); + adrs += nr; + for( m = 0; m < r; ++m ){ + if ((m & 7) == 0 && m > 0) + putchar(' '); + if( m < nr ) + printf("%.2x", temp[m]); + else + printf("%s", fault_chars[fault_type]); + } + for(; m < 16; ++m ) + printf(" "); + printf(" |"); + for( m = 0; m < r; ++m ){ + if( m < nr ){ + c = temp[m]; + putchar(' ' <= c && c <= '~'? c: '.'); + } else + putchar(' '); + } + n -= r; + for(; m < 16; ++m ) + putchar(' '); + printf("|\n"); + if( nr < r ) + break; + } +} + +int +ppc_inst_dump(unsigned long adr, long count) +{ + int nr, dotted; + unsigned long first_adr; + unsigned long inst, last_inst; + unsigned char val[4]; + + dotted = 0; + for (first_adr = adr; count > 0; --count, adr += 4){ + nr = mread(adr, val, 4); + if( nr == 0 ){ + const char *x = fault_chars[fault_type]; + printf("%.16lx %s%s%s%s\n", adr, x, x, x, x); + break; + } + inst = GETWORD(val); + if (adr > first_adr && inst == last_inst) { + if (!dotted) { + printf(" ...\n"); + dotted = 1; + } + continue; + } + dotted = 0; + last_inst = inst; + printf("%.16lx ", adr); + printf("%.8x\t", inst); + print_insn_big_powerpc(stdout, inst, adr); /* always returns 4 */ + printf("\n"); + } + return adr - first_adr; +} + +void +print_address(unsigned long addr) +{ + printf("0x%lx", addr); +} + +/* + * Memory operations - move, set, print differences + */ +static unsigned long mdest; /* destination address */ +static unsigned long msrc; /* source address */ +static unsigned long mval; /* byte value to set memory to */ +static unsigned long mcount; /* # bytes to affect */ +static unsigned long mdiffs; /* max # differences to print */ + +void +memops(int cmd) +{ + scanhex((void *)&mdest); + if( termch != '\n' ) + termch = 0; + scanhex((void *)(cmd == 's'? &mval: &msrc)); + if( termch != '\n' ) + termch = 0; + scanhex((void *)&mcount); + switch( cmd ){ + case 'm': + memmove((void *)mdest, (void *)msrc, mcount); + break; + case 's': + memset((void *)mdest, mval, mcount); + break; + case 'd': + if( termch != '\n' ) + termch = 0; + scanhex((void *)&mdiffs); + memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs); + break; + } +} + +void +memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr) +{ + unsigned n, prt; + + prt = 0; + for( n = nb; n > 0; --n ) + if( *p1++ != *p2++ ) + if( ++prt <= maxpr ) + printf("%.16x %.2x # %.16x %.2x\n", p1 - 1, + p1[-1], p2 - 1, p2[-1]); + if( prt > maxpr ) + printf("Total of %d differences\n", prt); +} + +static unsigned mend; +static unsigned mask; + +void +memlocate() +{ + unsigned a, n; + unsigned char val[4]; + + last_cmd = "ml"; + scanhex((void *)&mdest); + if (termch != '\n') { + termch = 0; + scanhex((void *)&mend); + if (termch != '\n') { + termch = 0; + scanhex((void *)&mval); + mask = ~0; + if (termch != '\n') termch = 0; + scanhex((void *)&mask); + } + } + n = 0; + for (a = mdest; a < mend; a += 4) { + if (mread(a, val, 4) == 4 + && ((GETWORD(val) ^ mval) & mask) == 0) { + printf("%.16x: %.16x\n", a, GETWORD(val)); + if (++n >= 10) + break; + } + } +} + +static unsigned long mskip = 0x1000; +static unsigned long mlim = 0xffffffff; + +void +memzcan() +{ + unsigned char v; + unsigned a; + int ok, ook; + + scanhex(&mdest); + if (termch != '\n') termch = 0; + scanhex(&mskip); + if (termch != '\n') termch = 0; + scanhex(&mlim); + ook = 0; + for (a = mdest; a < mlim; a += mskip) { + ok = mread(a, &v, 1); + if (ok && !ook) { + printf("%.8x .. ", a); + fflush(stdout); + } else if (!ok && ook) + printf("%.8x\n", a - mskip); + ook = ok; + if (a + mskip < a) + break; + } + if (ook) + printf("%.8x\n", a - mskip); +} + +/* Input scanning routines */ +int +skipbl() +{ + int c; + + if( termch != 0 ){ + c = termch; + termch = 0; + } else + c = inchar(); + while( c == ' ' || c == '\t' ) + c = inchar(); + return c; +} + +int +scanhex(vp) +unsigned long *vp; +{ + int c, d; + unsigned long v; + + c = skipbl(); + d = hexdigit(c); + if( d == EOF ){ + termch = c; + return 0; + } + v = 0; + do { + v = (v << 4) + d; + c = inchar(); + d = hexdigit(c); + } while( d != EOF ); + termch = c; + *vp = v; + return 1; +} + +void +scannl() +{ + int c; + + c = termch; + termch = 0; + while( c != '\n' ) + c = inchar(); +} + +int +hexdigit(int c) +{ + if( '0' <= c && c <= '9' ) + return c - '0'; + if( 'A' <= c && c <= 'F' ) + return c - ('A' - 10); + if( 'a' <= c && c <= 'f' ) + return c - ('a' - 10); + return EOF; +} + +void +getstring(char *s, int size) +{ + int c; + + c = skipbl(); + do { + if( size > 1 ){ + *s++ = c; + --size; + } + c = inchar(); + } while( c != ' ' && c != '\t' && c != '\n' ); + termch = c; + *s = 0; +} + +static char line[256]; +static char *lineptr; + +void +flush_input() +{ + lineptr = NULL; +} + +int +inchar() +{ + if (lineptr == NULL || *lineptr == 0) { + if (fgets(line, sizeof(line), stdin) == NULL) { + lineptr = NULL; + return EOF; + } + lineptr = line; + } + return *lineptr++; +} + +void +take_input(str) +char *str; +{ + lineptr = str; +} + + +/* Starting at codeaddr scan forward for a tbtable and fill in the + given table. Return non-zero if successful at doing something. + */ +static int +find_tb_table(unsigned long codeaddr, struct tbtable *tab) +{ + unsigned long codeaddr_max; + unsigned long tbtab_start; + int nr; + int instr; + int num_parms; + + if (tab == NULL) + return 0; + memset(tab, 0, sizeof(tab)); + + /* Scan instructions starting at codeaddr for 128k max */ + for (codeaddr_max = codeaddr + 128*1024*4; + codeaddr < codeaddr_max; + codeaddr += 4) { + nr = mread(codeaddr, &instr, 4); + if (nr != 4) + return 0; /* Bad read. Give up promptly. */ + if (instr == 0) { + /* table should follow. */ + int version; + unsigned long flags; + tbtab_start = codeaddr; /* save it to compute func start addr */ + codeaddr += 4; + nr = mread(codeaddr, &flags, 8); + if (nr != 8) + return 0; /* Bad read or no tb table. */ + tab->flags = flags; + version = (flags >> 56) & 0xff; + if (version != 0) + continue; /* No tb table here. */ + /* Now, like the version, some of the flags are values + that are more conveniently extracted... */ + tab->fp_saved = (flags >> 24) & 0x3f; + tab->gpr_saved = (flags >> 16) & 0x3f; + tab->fixedparms = (flags >> 8) & 0xff; + tab->floatparms = (flags >> 1) & 0x7f; + codeaddr += 8; + num_parms = tab->fixedparms + tab->floatparms; + if (num_parms) { + unsigned int parminfo; + int parm; + if (num_parms > 32) + return 1; /* incomplete */ + nr = mread(codeaddr, &parminfo, 4); + if (nr != 4) + return 1; /* incomplete */ + /* decode parminfo...32 bits. + A zero means fixed. A one means float and the + following bit determines single (0) or double (1). + */ + for (parm = 0; parm < num_parms; parm++) { + if (parminfo & 0x80000000) { + parminfo <<= 1; + if (parminfo & 0x80000000) + tab->parminfo[parm] = TBTAB_PARMDFLOAT; + else + tab->parminfo[parm] = TBTAB_PARMSFLOAT; + } else { + tab->parminfo[parm] = TBTAB_PARMFIXED; + } + parminfo <<= 1; + } + codeaddr += 4; + } + if (flags & TBTAB_FLAGSHASTBOFF) { + nr = mread(codeaddr, &tab->tb_offset, 4); + if (nr != 4) + return 1; /* incomplete */ + if (tab->tb_offset > 0) { + tab->funcstart = tbtab_start - tab->tb_offset; + } + codeaddr += 4; + } + /* hand_mask appears to be always be omitted. */ + if (flags & TBTAB_FLAGSHASCTL) { + /* Assume this will never happen for C or asm */ + return 1; /* incomplete */ + } + if (flags & TBTAB_FLAGSNAMEPRESENT) { + short namlen; + nr = mread(codeaddr, &namlen, 2); + if (nr != 2) + return 1; /* incomplete */ + if (namlen >= sizeof(tab->name)) + namlen = sizeof(tab->name)-1; + codeaddr += 2; + nr = mread(codeaddr, tab->name, namlen); + tab->name[namlen] = '\0'; + codeaddr += namlen; + } + return 1; + } + } + return 0; /* hit max...sorry. */ +} + +void +mem_translate() +{ + int c; + unsigned long ea, va, vsid, vpn, page, hpteg_slot_primary, hpteg_slot_secondary, primary_hash, i, *steg, esid, stabl; + HPTE * hpte; + struct mm_struct * mm; + pte_t *ptep = NULL; + void * pgdir; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&ea); + + if ((ea >= KRANGE_START) && (ea <= (KRANGE_START + (1UL<<60)))) { + ptep = 0; + vsid = get_kernel_vsid(ea); + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + } else { + // if in vmalloc range, use the vmalloc page directory + if ( ( ea >= VMALLOC_START ) && ( ea <= VMALLOC_END ) ) { + mm = &init_mm; + vsid = get_kernel_vsid( ea ); + } + // if in ioremap range, use the ioremap page directory + else if ( ( ea >= IMALLOC_START ) && ( ea <= IMALLOC_END ) ) { + mm = &ioremap_mm; + vsid = get_kernel_vsid( ea ); + } + // if in user range, use the current task's page directory + else if ( ( ea >= USER_START ) && ( ea <= USER_END ) ) { + mm = current->mm; + vsid = get_vsid(mm->context, ea ); + } + pgdir = mm->pgd; + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + ptep = find_linux_pte( pgdir, ea ); + } + + vpn = ((vsid << 28) | (((ea) & 0xFFFF000))) >> 12; + page = vpn & 0xffff; + esid = (ea >> 28) & 0xFFFFFFFFF; + + // Search the primary group for an available slot + primary_hash = ( vsid & 0x7fffffffff ) ^ page; + hpteg_slot_primary = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hpteg_slot_secondary = ( ~primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + + printf("ea : %.16lx\n", ea); + printf("esid : %.16lx\n", esid); + printf("vsid : %.16lx\n", vsid); + + printf("\nSoftware Page Table\n-------------------\n"); + printf("ptep : %.16lx\n", ((unsigned long *)ptep)); + if(ptep) { + printf("*ptep : %.16lx\n", *((unsigned long *)ptep)); + } + + hpte = htab_data.htab + hpteg_slot_primary; + printf("\nHardware Page Table\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("slot primary : %.16lx\n", hpteg_slot_primary); + printf("slot secondary : %.16lx\n", hpteg_slot_secondary); + printf("\nPrimary Group\n"); + for (i=0; i<8; ++i) { + if ( hpte->dw0.dw0.v != 0 ) { + printf("%d: (hpte)%.16lx %.16lx\n", i, hpte->dw0.dword0, hpte->dw1.dword1); + printf(" vsid: %.13lx api: %.2lx hash: %.1lx\n", + (hpte->dw0.dw0.avpn)>>5, + (hpte->dw0.dw0.avpn) & 0x1f, + (hpte->dw0.dw0.h)); + printf(" rpn: %.13lx \n", (hpte->dw1.dw1.rpn)); + printf(" pp: %.1lx \n", + ((hpte->dw1.dw1.pp0)<<2)|(hpte->dw1.dw1.pp)); + printf(" wimgn: %.2lx reference: %.1lx change: %.1lx\n", + ((hpte->dw1.dw1.w)<<4)| + ((hpte->dw1.dw1.i)<<3)| + ((hpte->dw1.dw1.m)<<2)| + ((hpte->dw1.dw1.g)<<1)| + ((hpte->dw1.dw1.n)<<0), + hpte->dw1.dw1.r, hpte->dw1.dw1.c); + } + hpte++; + } + + printf("\nSecondary Group\n"); + // Search the secondary group + hpte = htab_data.htab + hpteg_slot_secondary; + for (i=0; i<8; ++i) { + if(hpte->dw0.dw0.v) { + printf("%d: (hpte)%.16lx %.16lx\n", i, hpte->dw0.dword0, hpte->dw1.dword1); + printf(" vsid: %.13lx api: %.2lx hash: %.1lx\n", + (hpte->dw0.dw0.avpn)>>5, + (hpte->dw0.dw0.avpn) & 0x1f, + (hpte->dw0.dw0.h)); + printf(" rpn: %.13lx \n", (hpte->dw1.dw1.rpn)); + printf(" pp: %.1lx \n", + ((hpte->dw1.dw1.pp0)<<2)|(hpte->dw1.dw1.pp)); + printf(" wimgn: %.2lx reference: %.1lx change: %.1lx\n", + ((hpte->dw1.dw1.w)<<4)| + ((hpte->dw1.dw1.i)<<3)| + ((hpte->dw1.dw1.m)<<2)| + ((hpte->dw1.dw1.g)<<1)| + ((hpte->dw1.dw1.n)<<0), + hpte->dw1.dw1.r, hpte->dw1.dw1.c); + } + hpte++; + } + + printf("\nHardware Segment Table\n-----------------------\n"); + stabl = (unsigned long)(KERNELBASE+(_ASR&0xFFFFFFFFFFFFFFFE)); + steg = (unsigned long *)((stabl) | ((esid & 0x1f) << 7)); + + printf("stab base : %.16lx\n", stabl); + printf("slot : %.16lx\n", steg); + + for (i=0; i<8; ++i) { + printf("%d: (ste) %.16lx %.16lx\n", i, + *((unsigned long *)(steg+i*2)),*((unsigned long *)(steg+i*2+1)) ); + } +} + +void mem_check() +{ + unsigned long htab_size_bytes; + unsigned long htab_end; + unsigned long last_rpn; + HPTE *hpte1, *hpte2; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nHardware Page Table Check\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + +#if 1 + for(hpte1 = htab_data.htab; hpte1 < (HPTE *)htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( hpte1->dw1.dw1.rpn <= last_rpn ) { + for(hpte2 = hpte1+1; hpte2 < (HPTE *)htab_end; hpte2++) { + if ( hpte2->dw0.dw0.v != 0 ) { + if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { + printf(" Duplicate rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte1: %16.16lx *hpte1: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + printf(" hpte2: %16.16lx *hpte2: %16.16lx %16.16lx\n", + hpte2, hpte2->dw0.dword0, hpte2->dw1.dword1); + } + } + } + } else { + printf(" Bogus rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte: %16.16lx *hpte: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + } +#endif + printf("\nDone -------------------\n"); +} + +void mem_find_real() +{ + unsigned long htab_size_bytes; + unsigned long htab_end; + unsigned long last_rpn; + HPTE *hpte1; + unsigned long pa, rpn; + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&pa); + rpn = pa >> 12; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nMem Find RPN\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + for(hpte1 = htab_data.htab; hpte1 < (HPTE *)htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( hpte1->dw1.dw1.rpn == rpn ) { + printf(" Found rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte: %16.16lx *hpte1: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + } + printf("\nDone -------------------\n"); +} + +void mem_find_vsid() +{ + unsigned long htab_size_bytes; + unsigned long htab_end; + HPTE *hpte1; + unsigned long vsid; + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&vsid); + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + + printf("\nMem Find VSID\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + for(hpte1 = htab_data.htab; hpte1 < (HPTE *)htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( ((hpte1->dw0.dw0.avpn)>>5) == vsid ) { + printf(" Found vsid: %.16lx \n", ((hpte1->dw0.dw0.avpn) >> 5)); + printf(" hpte: %16.16lx *hpte1: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + } + printf("\nDone -------------------\n"); +} + +void mem_map_check_slab() +{ + int i, slab_count; + + i = max_mapnr; + slab_count = 0; + + while (i-- > 0) { + if (PageSlab(mem_map+i)){ + printf(" slab entry - mem_map entry =%p \n", mem_map+i); + slab_count ++; + } + } + + printf(" count of pages for slab = %d \n", slab_count); +} + +void mem_map_lock_pages() +{ + int i, lock_count; + + i = max_mapnr; + lock_count = 0; + + while (i-- > 0) { + if (PageLocked(mem_map+i)){ + printf(" locked entry - mem_map entry =%p \n", mem_map+i); + lock_count ++; + } + } + + printf(" count of locked pages = %d \n", lock_count); +} + + + +void mem_map_check_hash() +{ + int i = max_mapnr; + + while (i-- > 0) { + /* skip the reserved */ + if (!PageReserved(mem_map+i)) { + if (((mem_map+i)->next_hash) != NULL) { + if ( REGION_ID((mem_map+i)->next_hash) != KERNEL_REGION_ID ) { + printf(" mem_map check hash - non c0 entry - " + "address/value = %p %lx\n", mem_map+i,(mem_map+i)->next_hash); + } + if ((unsigned long)((mem_map+i)->next_hash) == KERNELBASE){ + printf(" mem_map check hash - 0x%lx entry = %p \n", + KERNELBASE, mem_map+i); + } + } + } else { + if (page_count(mem_map+i) < 0) { + printf(" reserved page with negative count- entry = %lx \n", mem_map+i); + } + } + } + printf(" mem_map check hash completed \n"); +} + +void mem_check_dup_rpn () +{ + unsigned long htab_size_bytes; + unsigned long htab_end; + unsigned long last_rpn; + HPTE *hpte1, *hpte2; + int dup_count; + struct task_struct *p; + unsigned long kernel_vsid_c0,kernel_vsid_c1,kernel_vsid_c2,kernel_vsid_c3; + unsigned long kernel_vsid_c4,kernel_vsid_c5,kernel_vsid_d,kernel_vsid_e; + unsigned long kernel_vsid_f; + unsigned long vsid0,vsid1,vsidB,vsid2; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nHardware Page Table Check\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + + for(hpte1 = htab_data.htab; hpte1 < (HPTE *)htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( hpte1->dw1.dw1.rpn <= last_rpn ) { + dup_count = 0; + for(hpte2 = hpte1+1; hpte2 < (HPTE *)htab_end; hpte2++) { + if ( hpte2->dw0.dw0.v != 0 ) { + if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { + dup_count++; + } + } + } + if(dup_count > 5) { + printf(" Duplicate rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" mem map array entry %p count = %d \n", + (mem_map+(hpte1->dw1.dw1.rpn)), (mem_map+(hpte1->dw1.dw1.rpn))->count); + for(hpte2 = hpte1+1; hpte2 < (HPTE *)htab_end; hpte2++) { + if ( hpte2->dw0.dw0.v != 0 ) { + if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { + printf(" hpte2: %16.16lx *hpte2: %16.16lx %16.16lx\n", + hpte2, hpte2->dw0.dword0, hpte2->dw1.dword1); + } + } + } + } + } else { + printf(" Bogus rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte: %16.16lx *hpte: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + if (xmon_interrupted()) + return; + } + + + + // print the kernel vsids + kernel_vsid_c0 = get_kernel_vsid(0xC000000000000000); + kernel_vsid_c1 = get_kernel_vsid(0xC000000010000000); + kernel_vsid_c2 = get_kernel_vsid(0xC000000020000000); + kernel_vsid_c3 = get_kernel_vsid(0xC000000030000000); + kernel_vsid_c4 = get_kernel_vsid(0xC000000040000000); + kernel_vsid_c5 = get_kernel_vsid(0xC000000050000000); + kernel_vsid_d = get_kernel_vsid(0xD000000000000000); + kernel_vsid_e = get_kernel_vsid(0xE000000000000000); + kernel_vsid_f = get_kernel_vsid(0xF000000000000000); + + printf(" kernel vsid - seg c0 = %lx\n", kernel_vsid_c0 ); + printf(" kernel vsid - seg c1 = %lx\n", kernel_vsid_c1 ); + printf(" kernel vsid - seg c2 = %lx\n", kernel_vsid_c2 ); + printf(" kernel vsid - seg c3 = %lx\n", kernel_vsid_c3 ); + printf(" kernel vsid - seg c4 = %lx\n", kernel_vsid_c4 ); + printf(" kernel vsid - seg c5 = %lx\n", kernel_vsid_c5 ); + printf(" kernel vsid - seg d = %lx\n", kernel_vsid_d ); + printf(" kernel vsid - seg e = %lx\n", kernel_vsid_e ); + printf(" kernel vsid - seg f = %lx\n", kernel_vsid_f ); + + + // print a list of valid vsids for the tasks + read_lock(&tasklist_lock); + for_each_task(p) + if(p->mm) { + struct mm_struct *mm = p->mm; + printf(" task = %p mm = %lx pgd %lx\n", + p, mm, mm->pgd); + vsid0 = get_vsid( mm->context, 0 ); + vsid1 = get_vsid( mm->context, 0x10000000 ); + vsid2 = get_vsid( mm->context, 0x20000000 ); + vsidB = get_vsid( mm->context, 0xB0000000 ); + printf(" context = %lx vsid seg 0 = %lx\n", mm->context, vsid0 ); + printf(" vsid seg 1 = %lx\n", vsid1 ); + printf(" vsid seg 2 = %lx\n", vsid2 ); + printf(" vsid seg 2 = %lx\n", vsidB ); + + printf("\n"); + }; + read_unlock(&tasklist_lock); + + printf("\nDone -------------------\n"); +} + + + +void mem_check_pagetable_vsids () +{ + unsigned long htab_size_bytes; + unsigned long htab_end; + unsigned long last_rpn; + struct task_struct *p; + unsigned long valid_table_count,invalid_table_count,bogus_rpn_count; + int found; + unsigned long user_address_table_count,kernel_page_table_count; + unsigned long pt_vsid; + HPTE *hpte1; + + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nHardware Page Table Check\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + valid_table_count = 0; + invalid_table_count = 0; + bogus_rpn_count = 0; + user_address_table_count = 0; + kernel_page_table_count = 0; + for(hpte1 = htab_data.htab; hpte1 < (HPTE *)htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + valid_table_count++; + if ( hpte1->dw1.dw1.rpn <= last_rpn ) { + pt_vsid = (hpte1->dw0.dw0.avpn) >> 5; + if ((pt_vsid == get_kernel_vsid(0xC000000000000000)) | + (pt_vsid == get_kernel_vsid(0xC000000010000000)) | + (pt_vsid == get_kernel_vsid(0xC000000020000000)) | + (pt_vsid == get_kernel_vsid(0xC000000030000000)) | + (pt_vsid == get_kernel_vsid(0xC000000040000000)) | + (pt_vsid == get_kernel_vsid(0xC000000050000000)) | + (pt_vsid == get_kernel_vsid(0xD000000000000000)) | + (pt_vsid == get_kernel_vsid(0xE000000000000000)) | + (pt_vsid == get_kernel_vsid(0xF000000000000000)) ) { + kernel_page_table_count ++; + } else { + read_lock(&tasklist_lock); + found = 0; + for_each_task(p) { + if(p->mm && (found == 0)) { + struct mm_struct *mm = p->mm; + + if ((pt_vsid == get_vsid( mm->context, 0 )) | + (pt_vsid == get_vsid( mm->context, 0x10000000 )) | + (pt_vsid == get_vsid( mm->context, 0x20000000 )) | + (pt_vsid == get_vsid( mm->context, 0x30000000 )) | + (pt_vsid == get_vsid( mm->context, 0x40000000 )) | + (pt_vsid == get_vsid( mm->context, 0x50000000 )) | + (pt_vsid == get_vsid( mm->context, 0x60000000 )) | + (pt_vsid == get_vsid( mm->context, 0x70000000 )) | + (pt_vsid == get_vsid( mm->context, 0x80000000 )) | + (pt_vsid == get_vsid( mm->context, 0x90000000 )) | + (pt_vsid == get_vsid( mm->context, 0xA0000000 )) | + (pt_vsid == get_vsid( mm->context, 0xB0000000 ))) { + user_address_table_count ++; + found = 1; + } + } + } + read_unlock(&tasklist_lock); + if (found == 0) + { + printf(" vsid not found vsid = %lx, hpte = %p \n", + pt_vsid,hpte1); + printf(" rpn in entry = %lx \n", hpte1->dw1.dw1.rpn); + printf(" mem map address = %lx \n", mem_map + (hpte1->dw1.dw1.rpn)); + + } else // found + { + } + + } // good rpn + + } else { + bogus_rpn_count ++; + } + } else { + invalid_table_count++; + } + } + + + printf(" page table valid counts - valid entries = %lx invalid entries = %lx \n", + valid_table_count, invalid_table_count); + + printf(" bogus rpn entries ( probably io) = %lx \n", bogus_rpn_count); + + + + printf(" page table counts - kernel entries = %lx user entries = %lx \n", + kernel_page_table_count, user_address_table_count); + + printf("\nDone -------------------\n"); + +} + + +void mem_check_full_group() +{ + unsigned long htab_size_bytes; + unsigned count; + unsigned count_array[] = {0,0,0,0,0,0,0,0,0}; + unsigned i; + unsigned long htab_end; + HPTE *hpte1, *hpte2, *hpte3; + u64 rpn = 0; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + + printf("\nHardware Page Find full groups \n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + for (hpte1 = htab_data.htab; (unsigned long)hpte1 < htab_end; hpte1= hpte1 + 8) + { + count = 0; + hpte2 = hpte1; + for (i=0; i<8; ++i) + { + if ( hpte2->dw0.dw0.v != 0 ) + { + count++; + } + hpte2++; + } + if (count == 8 ) + { + printf(" full group starting with entry %lx \n", hpte1); + hpte3 = hpte1; + for (i=0; i<8; ++i) + { + if ( hpte3->dw0.dw0.v != 0 ) + { + printf(" entry number %d \n",i); + printf(" vsid: %.13lx api: %.2lx hash: %.1lx\n", + (hpte3->dw0.dw0.avpn)>>5, + (hpte3->dw0.dw0.avpn) & 0x1f, + (hpte3->dw0.dw0.h)); + printf(" rpn: %.13lx \n", (hpte3->dw1.dw1.rpn)); + // Dump out the memmap array entry address, corresponding virtual address, and reference count. + rpn = hpte3->dw1.dw1.rpn; + printf(" mem_map+rpn=%p, virtual@=%p, count=%lx \n", mem_map+rpn, (mem_map+rpn)->virtual, (mem_map+rpn)->count); + } + hpte3++; + } + if (xmon_interrupted()) + return; + } + + count_array[count]++; + } + for (i=1; i<9; ++i) + { + printf(" group count for size %i = %lx \n", i, count_array[i]); + } + + printf("\nDone -------------------\n"); +} + + + +static void show_task(struct task_struct * p) +{ + /* unsigned long free = 0; --Unused */ + int state; + static const char * stat_nam[] = { "R", "S", "D", "Z", "T", "W" }; + + printf("--------------------------------------------------------------------------\n"); + printf("%-11.11s pid: %5.5lx ppid: %5.5lx state: ", + p->comm, p->pid, p->p_pptr->pid); + state = p->state ? ffz(~p->state) + 1 : 0; + if (((unsigned) state) < sizeof(stat_nam)/sizeof(char *)) + printf(stat_nam[state]); + else + printf(" "); + if (p == current) + printf(" pc: current task "); + else + printf(" pc: 0x%16.16lx ", thread_saved_pc(&p->thread)); + + if (p->p_cptr) + printf("%5d ", p->p_cptr->pid); + else + printf(" "); + if (!p->mm) + printf(" (L-TLB) "); + else + printf(" (NOTLB) "); + if (p->p_ysptr) + printf("%7d", p->p_ysptr->pid); + else + printf(" "); + if (p->p_osptr) + printf(" %5d\n", p->p_osptr->pid); + else + printf("\n"); + + { + struct sigqueue *q; + char s[sizeof(sigset_t)*2+1], b[sizeof(sigset_t)*2+1]; + + render_sigset_t(&p->pending.signal, s); + render_sigset_t(&p->blocked, b); + printf(" sig: %d %s %s :", signal_pending(p), s, b); + for (q = p->pending.head; q ; q = q->next) + printf(" %d", q->info.si_signo); + printf(" X\n"); + } + + printf(" pers : %lx current : %lx", + p->personality, p); + printf("\n"); + + printf(" thread : 0x%16.16lx ksp : 0x%16.16lx\n", + &(p->thread), (p->thread.ksp)); + printf(" pgdir : 0x%16.16lx\n", (p->thread.pgdir)); + printf(" regs : 0x%16.16lx sysc : 0x%16.16lx\n", + (p->thread.regs), (p->thread.last_syscall)); + if(p->thread.regs) { + printf(" nip : 0x%16.16lx msr : 0x%16.16lx\n", + ((p->thread.regs)->nip), ((p->thread.regs)->msr)); + printf(" ctr : 0x%16.16lx link : 0x%16.16lx\n", + ((p->thread.regs)->ctr), ((p->thread.regs)->link)); + printf(" xer : 0x%16.16lx ccr : 0x%16.16lx\n", + ((p->thread.regs)->xer), ((p->thread.regs)->ccr)); + printf(" trap : 0x%16.16lx\n", + ((p->thread.regs)->trap)); + printf(" dar : 0x%16.16lx dsis : 0x%16.16lx\n", + ((p->thread.regs)->dar), ((p->thread.regs)->dsisr)); + printf(" rslt : 0x%16.16lx org3 : 0x%16.16lx\n", + ((p->thread.regs)->result), (p->thread.regs->orig_gpr3)); + } + + if(p->mm) { + struct mm_struct *mm = p->mm; + printf(" mm : 0x%16.16lx pgd : 0x%16.16lx\n", + mm, mm->pgd); + printf(" context: 0x%16.16lx mmap : 0x%16.16lx\n", + mm->context, mm->mmap); + + printf("\n"); + } + +} + +static void xmon_show_state(void) +{ + struct task_struct *p; + +#if (BITS_PER_LONG == 32) + printf("\n" + " free sibling\n"); + printf("task name st PC stack pid father child younger older\n"); +#else + printf("\n" + " free sibling\n"); + printf(" task PC stack pid father child younger older\n"); +#endif + read_lock(&tasklist_lock); + for_each_task(p) + show_task(p); + read_unlock(&tasklist_lock); +} + +static void debug_trace(void) { + unsigned long val, cmd, on; + + cmd = skipbl(); + if (cmd == '\n') { + /* show current state */ + unsigned long i; + printf("naca->debug_switch = 0x%lx\n", naca->debug_switch); + for (i = 0; i < PPCDBG_NUM_FLAGS ;i++) { + on = PPCDBG_BITVAL(i) & naca->debug_switch; + printf("%02x %s %12s ", i, on ? "on " : "off", trace_names[i] ? trace_names[i] : ""); + if (((i+1) % 3) == 0) + printf("\n"); + } + printf("\n"); + return; + } + while (cmd != '\n') { + on = 1; /* default if no sign given */ + while (cmd == '+' || cmd == '-') { + on = (cmd == '+'); + cmd = inchar(); + if (cmd == ' ' || cmd == '\n') { /* Turn on or off based on + or - */ + naca->debug_switch = on ? PPCDBG_ALL:PPCDBG_NONE; + printf("Setting all values to %s...\n", on ? "on" : "off"); + if (cmd == '\n') return; + else cmd = skipbl(); + } + else + termch = cmd; + } + termch = cmd; /* not +/- ... let scanhex see it */ + scanhex((void *)&val); + if (val >= 64) { + printf("Value %x out of range:\n", val); + return; + } + if (on) { + naca->debug_switch |= PPCDBG_BITVAL(val); + printf("enable debug %x %s\n", val, trace_names[val] ? trace_names[val] : ""); + } else { + naca->debug_switch &= ~PPCDBG_BITVAL(val); + printf("disable debug %x %s\n", val, trace_names[val] ? trace_names[val] : ""); + } + cmd = skipbl(); + } +} diff -Nru a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c --- a/arch/s390/kernel/signal.c Tue Feb 19 18:08:58 2002 +++ b/arch/s390/kernel/signal.c Tue Feb 19 18:08:58 2002 @@ -96,7 +96,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->gprs[2] = -EINTR; @@ -124,7 +124,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->gprs[2] = -EINTR; @@ -225,7 +225,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigregs(regs, &frame->sregs)) @@ -251,7 +251,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigregs(regs, &frame->uc.uc_mcontext)) @@ -445,7 +445,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/s390x/kernel/linux32.c b/arch/s390x/kernel/linux32.c --- a/arch/s390x/kernel/linux32.c Tue Feb 19 18:08:58 2002 +++ b/arch/s390x/kernel/linux32.c Tue Feb 19 18:08:58 2002 @@ -1473,7 +1473,9 @@ static int cp_new_stat32(struct kstat *stat, struct stat32 *statbuf) { - err = put_user(stat->dev, &statbuf->st_dev); + int err; + + err = put_user(stat->dev, &statbuf->st_dev); err |= put_user(stat->ino, &statbuf->st_ino); err |= put_user(stat->mode, &statbuf->st_mode); err |= put_user(stat->nlink, &statbuf->st_nlink); @@ -1976,7 +1978,7 @@ in so that we'll be awakened when they arrive. */ sigset_t oldblocked = current->blocked; sigandsets(¤t->blocked, ¤t->blocked, &these); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); timeout = MAX_SCHEDULE_TIMEOUT; @@ -1990,7 +1992,7 @@ spin_lock_irq(¤t->sigmask_lock); sig = dequeue_signal(&these, &info); current->blocked = oldblocked; - recalc_sigpending(current); + recalc_sigpending(); } spin_unlock_irq(¤t->sigmask_lock); diff -Nru a/arch/s390x/kernel/signal.c b/arch/s390x/kernel/signal.c --- a/arch/s390x/kernel/signal.c Tue Feb 19 18:08:57 2002 +++ b/arch/s390x/kernel/signal.c Tue Feb 19 18:08:57 2002 @@ -96,7 +96,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->gprs[2] = -EINTR; @@ -124,7 +124,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->gprs[2] = -EINTR; @@ -225,7 +225,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigregs(regs, &frame->sregs)) @@ -251,7 +251,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigregs(regs, &frame->uc.uc_mcontext)) @@ -445,7 +445,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/s390x/kernel/signal32.c b/arch/s390x/kernel/signal32.c --- a/arch/s390x/kernel/signal32.c Tue Feb 19 18:08:59 2002 +++ b/arch/s390x/kernel/signal32.c Tue Feb 19 18:08:59 2002 @@ -112,7 +112,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->gprs[2] = -EINTR; @@ -147,7 +147,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->gprs[2] = -EINTR; @@ -351,7 +351,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigregs32(regs, &frame->sregs)) @@ -380,7 +380,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) @@ -584,7 +584,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/sh/config.in b/arch/sh/config.in --- a/arch/sh/config.in Tue Feb 19 18:09:00 2002 +++ b/arch/sh/config.in Tue Feb 19 18:09:00 2002 @@ -356,7 +356,7 @@ tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in + source sound/Config.in fi endmenu diff -Nru a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c --- a/arch/sh/kernel/signal.c Tue Feb 19 18:09:00 2002 +++ b/arch/sh/kernel/signal.c Tue Feb 19 18:09:00 2002 @@ -83,7 +83,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs.regs[0] = -EINTR; @@ -112,7 +112,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs.regs[0] = -EINTR; @@ -283,7 +283,7 @@ spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->sc, &r0)) @@ -313,7 +313,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &r0)) @@ -558,7 +558,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c --- a/arch/sparc/kernel/signal.c Tue Feb 19 18:08:58 2002 +++ b/arch/sparc/kernel/signal.c Tue Feb 19 18:08:58 2002 @@ -142,7 +142,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, set); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->pc = regs->npc; @@ -199,7 +199,7 @@ spin_lock_irq(¤t->sigmask_lock); oldset = current->blocked; current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->pc = regs->npc; @@ -304,7 +304,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return; @@ -351,7 +351,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->pc = pc; @@ -421,7 +421,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return; segv: @@ -1022,7 +1022,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->pc = pc; regs->npc = npc | 1; @@ -1063,7 +1063,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked, signr); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c --- a/arch/sparc/kernel/sys_sunos.c Tue Feb 19 18:08:59 2002 +++ b/arch/sparc/kernel/sys_sunos.c Tue Feb 19 18:08:59 2002 @@ -283,7 +283,7 @@ spin_lock_irq(¤t->sigmask_lock); old = current->blocked.sig[0]; current->blocked.sig[0] |= (blk_mask & _BLOCKABLE); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return old; } @@ -295,7 +295,7 @@ spin_lock_irq(¤t->sigmask_lock); retval = current->blocked.sig[0]; current->blocked.sig[0] = (newmask & _BLOCKABLE); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return retval; } diff -Nru a/arch/sparc64/config.in b/arch/sparc64/config.in --- a/arch/sparc64/config.in Tue Feb 19 18:08:59 2002 +++ b/arch/sparc64/config.in Tue Feb 19 18:08:59 2002 @@ -262,7 +262,7 @@ tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - source drivers/sound/Config.in + source sound/Config.in fi endmenu diff -Nru a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c --- a/arch/sparc64/kernel/ioctl32.c Tue Feb 19 18:08:59 2002 +++ b/arch/sparc64/kernel/ioctl32.c Tue Feb 19 18:08:59 2002 @@ -3997,8 +3997,6 @@ COMPATIBLE_IOCTL(BLKROGET) COMPATIBLE_IOCTL(BLKRRPART) COMPATIBLE_IOCTL(BLKFLSBUF) -COMPATIBLE_IOCTL(BLKRASET) -COMPATIBLE_IOCTL(BLKFRASET) COMPATIBLE_IOCTL(BLKSECTSET) COMPATIBLE_IOCTL(BLKSSZGET) COMPATIBLE_IOCTL(BLKBSZGET) @@ -4626,10 +4624,8 @@ HANDLE_IOCTL(SIOCRTMSG, ret_einval) HANDLE_IOCTL(SIOCGSTAMP, do_siocgstamp) HANDLE_IOCTL(HDIO_GETGEO, hdio_getgeo) -HANDLE_IOCTL(BLKRAGET, w_long) HANDLE_IOCTL(BLKGETSIZE, w_long) HANDLE_IOCTL(0x1260, broken_blkgetsize) -HANDLE_IOCTL(BLKFRAGET, w_long) HANDLE_IOCTL(BLKSECTGET, w_long) HANDLE_IOCTL(BLKPG, blkpg_ioctl_trans) HANDLE_IOCTL(FBIOPUTCMAP32, fbiogetputcmap) diff -Nru a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c --- a/arch/sparc64/kernel/signal.c Tue Feb 19 18:08:57 2002 +++ b/arch/sparc64/kernel/signal.c Tue Feb 19 18:08:57 2002 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -105,7 +106,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } if (test_thread_flag(TIF_32BIT)) { @@ -293,7 +294,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, set); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (test_thread_flag(TIF_32BIT)) { @@ -353,7 +354,7 @@ spin_lock_irq(¤t->sigmask_lock); oldset = current->blocked; current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (test_thread_flag(TIF_32BIT)) { @@ -459,7 +460,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return; segv: @@ -601,7 +602,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,signr); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c --- a/arch/sparc64/kernel/signal32.c Tue Feb 19 18:08:57 2002 +++ b/arch/sparc64/kernel/signal32.c Tue Feb 19 18:08:57 2002 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -151,7 +152,7 @@ spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, set); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->tpc = regs->tnpc; @@ -206,7 +207,7 @@ spin_lock_irq(¤t->sigmask_lock); oldset = current->blocked; current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->tpc = regs->tnpc; @@ -318,7 +319,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return; @@ -365,7 +366,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (test_thread_flag(TIF_32BIT)) { @@ -463,7 +464,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return; segv: @@ -1086,7 +1087,7 @@ sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); regs->tpc = pc; regs->tnpc = npc | 1; @@ -1275,7 +1276,7 @@ spin_lock_irq(¤t->sigmask_lock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,signr); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } diff -Nru a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c --- a/arch/sparc64/kernel/sys_sparc32.c Tue Feb 19 18:08:57 2002 +++ b/arch/sparc64/kernel/sys_sparc32.c Tue Feb 19 18:08:57 2002 @@ -1965,7 +1965,7 @@ in so that we'll be awakened when they arrive. */ sigset_t oldblocked = current->blocked; sigandsets(¤t->blocked, ¤t->blocked, &these); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); timeout = MAX_SCHEDULE_TIMEOUT; @@ -1979,7 +1979,7 @@ spin_lock_irq(¤t->sigmask_lock); sig = dequeue_signal(&these, &info); current->blocked = oldblocked; - recalc_sigpending(current); + recalc_sigpending(); } spin_unlock_irq(¤t->sigmask_lock); diff -Nru a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c --- a/arch/sparc64/kernel/sys_sunos32.c Tue Feb 19 18:08:57 2002 +++ b/arch/sparc64/kernel/sys_sunos32.c Tue Feb 19 18:08:57 2002 @@ -238,7 +238,7 @@ spin_lock_irq(¤t->sigmask_lock); old = (u32) current->blocked.sig[0]; current->blocked.sig[0] |= (blk_mask & _BLOCKABLE); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return old; } @@ -250,7 +250,7 @@ spin_lock_irq(¤t->sigmask_lock); retval = (u32) current->blocked.sig[0]; current->blocked.sig[0] = (newmask & _BLOCKABLE); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return retval; } diff -Nru a/arch/sparc64/solaris/signal.c b/arch/sparc64/solaris/signal.c --- a/arch/sparc64/solaris/signal.c Tue Feb 19 18:09:00 2002 +++ b/arch/sparc64/solaris/signal.c Tue Feb 19 18:09:00 2002 @@ -100,13 +100,13 @@ if (arg != 2) /* HOLD */ { spin_lock_irq(¤t->sigmask_lock); sigdelsetmask(¤t->blocked, _S(sig)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return sig_handler (sig, arg, 0); } else { spin_lock_irq(¤t->sigmask_lock); sigaddsetmask(¤t->blocked, (_S(sig) & ~_BLOCKABLE)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return 0; } @@ -121,7 +121,7 @@ { spin_lock_irq(¤t->sigmask_lock); sigdelsetmask(¤t->blocked, _S(sig)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return 0; } @@ -312,7 +312,7 @@ case 1: /* sigpending */ spin_lock_irq(¤t->sigmask_lock); sigandsets(&s, ¤t->blocked, ¤t->pending.signal); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); break; case 2: /* sigfillset - I just set signals which have linux equivalents */ diff -Nru a/arch/x86_64/Config.help b/arch/x86_64/Config.help --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/Config.help Tue Feb 19 18:09:00 2002 @@ -0,0 +1,531 @@ +CONFIG_SMP + This enables support for systems with more than one CPU. If you have + a system with only one CPU, like most personal computers, say N. If + you have a system with more than one CPU, say Y. + + If you say N here, the kernel will run on single and multiprocessor + machines, but will use only one CPU of a multiprocessor machine. If + you say Y here, the kernel will run on many, but not all, + singleprocessor machines. On a singleprocessor machine, the kernel + will run faster if you say N here. + + Note that if you say Y here and choose architecture "586" or + "Pentium" under "Processor family", the kernel will not work on 486 + architectures. Similarly, multiprocessor kernels for the "PPro" + architecture may not work on all Pentium based boards. + + People using multiprocessor machines who say Y here should also say + Y to "Enhanced Real Time Clock Support", below. The "Advanced Power + Management" code will be disabled if you say Y here. + + See also the , + , , + and the SMP-HOWTO available at + . + + If you don't know what to do here, say N. + +CONFIG_X86 + This is Linux's home port. Linux was originally native to the Intel + 386, and runs on all the later x86 processors including the Intel + 486, 586, Pentiums, and various instruction-set-compatible chips by + AMD, Cyrix, and others. + +CONFIG_X86_64 + Port to the x86-64 architecture. x86-64 is an 64bit extension to the + classical 32bit x86 architecture. For details see http://www.x86-64.org + +CONFIG_X86_UP_IOAPIC + An IO-APIC (I/O Advanced Programmable Interrupt Controller) is an + SMP-capable replacement for PC-style interrupt controllers. Most + SMP systems and a small number of uniprocessor systems have one. + If you have a single-CPU system with an IO-APIC, you can say Y here + to use it. If you say Y here even though your machine doesn't have + an IO-APIC, then the kernel will still run with no slowdown at all. + + If you have a system with several CPUs, you do not need to say Y + here: the IO-APIC will be used automatically. + +CONFIG_X86_UP_APIC + A local APIC (Advanced Programmable Interrupt Controller) is an + integrated interrupt controller in the CPU. If you have a single-CPU + system which has a processor with a local APIC, you can say Y here to + enable and use it. If you say Y here even though your machine doesn't + have a local APIC, then the kernel will still run with no slowdown at + all. The local APIC supports CPU-generated self-interrupts (timer, + performance counters), and the NMI watchdog which detects hard lockups. + + If you have a system with several CPUs, you do not need to say Y + here: the local APIC will be used automatically. + +CONFIG_IDE + If you say Y here, your kernel will be able to manage low cost mass + storage units such as ATA/(E)IDE and ATAPI units. The most common + cases are IDE hard drives and ATAPI CD-ROM drives. + + If your system is pure SCSI and doesn't use these interfaces, you + can say N here. + + Integrated Disk Electronics (IDE aka ATA-1) is a connecting standard + for mass storage units such as hard disks. It was designed by + Western Digital and Compaq Computer in 1984. It was then named + ST506. Quite a number of disks use the IDE interface. + + AT Attachment (ATA) is the superset of the IDE specifications. + ST506 was also called ATA-1. + + Fast-IDE is ATA-2 (also named Fast ATA), Enhanced IDE (EIDE) is + ATA-3. It provides support for larger disks (up to 8.4GB by means of + the LBA standard), more disks (4 instead of 2) and for other mass + storage units such as tapes and cdrom. UDMA/33 (aka UltraDMA/33) is + ATA-4 and provides faster (and more CPU friendly) transfer modes + than previous PIO (Programmed processor Input/Output) from previous + ATA/IDE standards by means of fast DMA controllers. + + ATA Packet Interface (ATAPI) is a protocol used by EIDE tape and + CD-ROM drives, similar in many respects to the SCSI protocol. + + SMART IDE (Self Monitoring, Analysis and Reporting Technology) was + designed in order to prevent data corruption and disk crash by + detecting pre hardware failure conditions (heat, access time, and + the like...). Disks built since June 1995 may follow this standard. + The kernel itself don't manage this; however there are quite a + number of user programs such as smart that can query the status of + SMART parameters disk. + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called ide.o. + + For further information, please read . + + If unsure, say Y. + +CONFIG_ISA + Find out whether you have ISA slots on your motherboard. ISA is the + name of a bus system, i.e. the way the CPU talks to the other stuff + inside your box. Other bus systems are PCI, EISA, MicroChannel + (MCA) or VESA. ISA is an older system, now being displaced by PCI; + newer boards don't support it. If you have ISA, say Y, otherwise N. + +CONFIG_PCI + Find out whether you have a PCI motherboard. PCI is the name of a + bus system, i.e. the way the CPU talks to the other stuff inside + your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or + VESA. If you have PCI, say Y, otherwise N. + + The PCI-HOWTO, available from + , contains valuable + information about which PCI hardware does work under Linux and which + doesn't. + +CONFIG_HOTPLUG_PCI + Say Y here if you have a motherboard with a PCI Hotplug controller. + This allows you to add and remove PCI cards while the machine is + powered up and running. The file system pcihpfs must be mounted + in order to interact with any PCI Hotplug controllers. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called pci_hotplug.o. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + +CONFIG_HOTPLUG + Say Y here if you want to plug devices into your computer while + the system is running, and be able to use them quickly. In many + cases, the devices can likewise be unplugged at any time too. + + One well known example of this is PCMCIA- or PC-cards, credit-card + size devices such as network cards, modems or hard drives which are + plugged into slots found on all modern laptop computers. Another + example, used on modern desktops as well as laptops, is USB. + + Enable HOTPLUG and KMOD, and build a modular kernel. Get agent + software (at ) and install it. + Then your kernel will automatically call out to a user mode "policy + agent" (/sbin/hotplug) to load modules and set up software needed + to use devices as you hotplug them. + +CONFIG_PCMCIA + Say Y here if you want to attach PCMCIA- or PC-cards to your Linux + computer. These are credit-card size devices such as network cards, + modems or hard drives often used with laptops computers. There are + actually two varieties of these cards: the older 16 bit PCMCIA cards + and the newer 32 bit CardBus cards. If you want to use CardBus + cards, you need to say Y here and also to "CardBus support" below. + + To use your PC-cards, you will need supporting software from David + Hinds' pcmcia-cs package (see the file + for location). Please also read the PCMCIA-HOWTO, available from + . + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + When compiled this way, there will be modules called pcmcia_core.o + and ds.o. If you want to compile it as a module, say M here and + read . + +CONFIG_KCORE_ELF + If you enabled support for /proc file system then the file + /proc/kcore will contain the kernel core image. This can be used + in gdb: + + $ cd /usr/src/linux ; gdb vmlinux /proc/kcore + + You have two choices here: ELF and A.OUT. Selecting ELF will make + /proc/kcore appear in ELF core format as defined by the Executable + and Linking Format specification. Selecting A.OUT will choose the + old "a.out" format which may be necessary for some old versions + of binutils or on some architectures. + + This is especially useful if you have compiled the kernel with the + "-g" option to preserve debugging information. It is mainly used + for examining kernel data structures on the live kernel so if you + don't understand what this means or are not a kernel hacker, just + leave it at its default value ELF. + +CONFIG_BINFMT_ELF + ELF (Executable and Linkable Format) is a format for libraries and + executables used across different architectures and operating + systems. Saying Y here will enable your kernel to run ELF binaries + and enlarge it by about 13 KB. ELF support under Linux has now all + but replaced the traditional Linux a.out formats (QMAGIC and ZMAGIC) + because it is portable (this does *not* mean that you will be able + to run executables from different architectures or operating systems + however) and makes building run-time libraries very easy. Many new + executables are distributed solely in ELF format. You definitely + want to say Y here. + + Information about ELF is contained in the ELF HOWTO available from + . + + If you find that after upgrading from Linux kernel 1.2 and saying Y + here, you still can't run any ELF binaries (they just crash), then + you'll have to install the newest ELF runtime libraries, including + ld.so (check the file for location and + latest version). + + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called binfmt_elf.o. Saying M or N here is dangerous because + some crucial programs on your system might be in ELF format. + +CONFIG_BINFMT_MISC + If you say Y here, it will be possible to plug wrapper-driven binary + formats into the kernel. You will like this especially when you use + programs that need an interpreter to run like Java, Python or + Emacs-Lisp. It's also useful if you often run DOS executables under + the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO, available from + ). Once you have + registered such a binary class with the kernel, you can start one of + those programs simply by typing in its name at a shell prompt; Linux + will automatically feed it to the correct interpreter. + + You can do other nice things, too. Read the file + to learn how to use this + feature, and for information about how + to include Java support. + + You must say Y to "/proc file system support" (CONFIG_PROC_FS) to + use this part of the kernel. + + You may say M here for module support and later load the module when + you have use for it; the module is called binfmt_misc.o. If you + don't know what to answer at this point, say Y. + +CONFIG_MK8 + Support for AMD Clawhammer/Sledgehammer CPUs. Only choice for x86-64 + currently so you should chose this if you want a x86-64 kernel. In fact + you will have no other choice than to chose this. + +CONFIG_VGA_CONSOLE + Saying Y here will allow you to use Linux in text mode through a + display that complies with the generic VGA standard. Virtually + everyone wants that. + + The program SVGATextMode can be used to utilize SVGA video cards to + their full potential in text mode. Download it from + . + + Say Y. + +CONFIG_VIDEO_SELECT + This enables support for text mode selection on kernel startup. If + you want to take advantage of some high-resolution text mode your + card's BIOS offers, but the traditional Linux utilities like + SVGATextMode don't, you can say Y here and set the mode using the + "vga=" option from your boot loader (lilo or loadlin) or set + "vga=ask" which brings up a video mode menu on kernel startup. (Try + "man bootparam" or see the documentation of your boot loader about + how to pass options to the kernel.) + + Read the file for more information + about the Video mode selection support. If unsure, say N. + +CONFIG_MDA_CONSOLE + Say Y here if you have an old MDA or monochrome Hercules graphics + adapter in your system acting as a second head ( = video card). You + will then be able to use two monitors with your Linux system. Do not + say Y here if your MDA card is the primary card in your system; the + normal VGA driver will handle it. + + This driver is also available as a module ( = code which can be + inserted and removed from the running kernel whenever you want). + The module will be called mdacon.o. If you want to compile it as + a module, say M here and read . + + If unsure, say N. + +CONFIG_SCSI + If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or + any other SCSI device under Linux, say Y and make sure that you know + the name of your SCSI host adapter (the card inside your computer + that "speaks" the SCSI protocol, also called SCSI controller), + because you will be asked for it. + + You also need to say Y here if you want support for the parallel + port version of the 100 MB IOMEGA ZIP drive. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called scsi_mod.o. If you want to compile it as + a module, say M here and read and + . However, do not compile this as a + module if your root file system (the one containing the directory /) + is located on a SCSI device. + +CONFIG_NETDEVICES + You can say N here if you don't intend to connect your Linux box to + any other computer at all or if all your connections will be over a + telephone line with a modem either via UUCP (UUCP is a protocol to + forward mail and news between unix hosts over telephone lines; read + the UUCP-HOWTO, available from + ) or dialing up a shell + account or a BBS, even using term (term is a program which gives you + almost full Internet connectivity if you have a regular dial up + shell account on some Internet connected Unix computer. Read + ). + + You'll have to say Y if your computer contains a network card that + you want to use under Linux (make sure you know its name because you + will be asked for it and read the Ethernet-HOWTO (especially if you + plan to use more than one network card under Linux)) or if you want + to use SLIP (Serial Line Internet Protocol is the protocol used to + send Internet traffic over telephone lines or null modem cables) or + CSLIP (compressed SLIP) or PPP (Point to Point Protocol, a better + and newer replacement for SLIP) or PLIP (Parallel Line Internet + Protocol is mainly used to create a mini network by connecting the + parallel ports of two local machines) or AX.25/KISS (protocol for + sending Internet traffic over amateur radio links). + + Make sure to read the NET-3-HOWTO. Eventually, you will have to read + Olaf Kirch's excellent and free book "Network Administrator's + Guide", to be found in . If + unsure, say Y. + +CONFIG_CD_NO_IDESCSI + If you have a CD-ROM drive that is neither SCSI nor IDE/ATAPI, say Y + here, otherwise N. Read the CD-ROM-HOWTO, available from + . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about these CD-ROM drives. If you are unsure what you + have, say Y and find out whether you have one of the following + drives. + + For each of these drivers, a file Documentation/cdrom/{driver_name} + exists. Especially in cases where you do not know exactly which kind + of drive you have you should read there. Most of these drivers use a + file drivers/cdrom/{driver_name}.h where you can define your + interface parameters and switch some internal goodies. + + All these CD-ROM drivers are also usable as a module ( = code which + can be inserted in and removed from the running kernel whenever you + want). If you want to compile them as module, say M instead of Y and + read . + + If you want to use any of these CD-ROM drivers, you also have to + answer Y or M to "ISO 9660 CD-ROM file system support" below (this + answer will get "defaulted" for you if you enable any of the Linux + CD-ROM drivers). + +CONFIG_MTRR + On Intel P6 family processors (Pentium Pro, Pentium II and later) + the Memory Type Range Registers (MTRRs) may be used to control + processor access to memory ranges. This is most useful if you have + a video (VGA) card on a PCI or AGP bus. Enabling write-combining + allows bus write transfers to be combined into a larger transfer + before bursting over the PCI/AGP bus. This can increase performance + of image write operations 2.5 times or more. Saying Y here creates a + /proc/mtrr file which may be used to manipulate your processor's + MTRRs. Typically the X server should use this. + + This code has a reasonably generic interface so that similar + control registers on other processors can be easily supported + as well: + + Saying Y here also fixes a problem with buggy SMP BIOSes which only + set the MTRRs for the boot CPU and not for the secondary CPUs. This + can lead to all sorts of problems, so it's good to say Y here. + + Just say Y here, all x86-64 machines support MTRRs. + + See for more information. + +CONFIG_PM + "Power Management" means that parts of your computer are shut + off or put into a power conserving "sleep" mode if they are not + being used. There are two competing standards for doing this: APM + and ACPI. If you want to use either one, say Y here and then also + to the requisite support below. + + Power Management is most important for battery powered laptop + computers; if you have a laptop, check out the Linux Laptop home + page on the WWW at + and the + Battery Powered Linux mini-HOWTO, available from + . + + Note that, even if you say N here, Linux on the x86 architecture + will issue the hlt instruction if nothing is to be done, thereby + sending the processor to sleep and saving power. + +CONFIG_ACPI + ACPI/OSPM support for Linux is currently under development. As such, + this support is preliminary and EXPERIMENTAL. Configuring ACPI + support enables kernel interfaces that allow higher level software + (OSPM) to manipulate ACPI defined hardware and software interfaces, + including the evaluation of ACPI control methods. If unsure, choose + N here. Note, this option will enlarge your kernel by about 120K. + + This support requires an ACPI compliant platform (hardware/firmware). + If both ACPI and Advanced Power Management (APM) support are + configured, whichever is loaded first shall be used. + + This code DOES NOT currently provide a complete OSPM implementation + -- it has not yet reached APM's level of functionality. When fully + implemented, Linux ACPI/OSPM will provide a more robust functional + replacement for legacy configuration and power management + interfaces, including the Plug-and-Play BIOS specification (PnP + BIOS), the Multi-Processor Specification (MPS), and the Advanced + Power Management specification (APM). + + Linux support for ACPI/OSPM is based on Intel Corporation's ACPI + Component Architecture (ACPI CA). The latest ACPI CA source code, + documentation, debug builds, and implementation status information + can be downloaded from: + . + + The ACPI Sourceforge project may also be of interest: + + +CONFIG_X86_MSR + This device gives privileged processes access to the x86 + Model-Specific Registers (MSRs). It is a character device with + major 202 and minors 0 to 31 for /dev/cpu/0/msr to /dev/cpu/31/msr. + MSR accesses are directed to a specific CPU on multi-processor + systems. + +CONFIG_X86_CPUID + This device gives processes access to the x86 CPUID instruction to + be executed on a specific processor. It is a character device + with major 203 and minors 0 to 31 for /dev/cpu/0/cpuid to + /dev/cpu/31/cpuid. + +CONFIG_SOUND + If you have a sound card in your computer, i.e. if it can say more + than an occasional beep, say Y. Be sure to have all the information + about your sound card and its configuration down (I/O port, + interrupt and DMA channel), because you will be asked for it. + + You want to read the Sound-HOWTO, available from + . General information about + the modular sound system is contained in the files + . The file + contains some slightly + outdated but still useful information as well. + + If you have a PnP sound card and you want to configure it at boot + time using the ISA PnP tools (read + ), then you need to + compile the sound card support as a module ( = code which can be + inserted in and removed from the running kernel whenever you want) + and load that module after the PnP configuration is finished. To do + this, say M here and read as well + as ; the module will be + called soundcore.o. + + I'm told that even without a sound card, you can make your computer + say more than an occasional beep, by programming the PC speaker. + Kernel patches and supporting utilities to do that are in the pcsp + package, available at . + +CONFIG_PREEMPT + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + This allows applications to run more reliably even when the system is + under load. On contrary it may also break your drivers and add + priority inheritance problems to your system. Don't select it if + you rely on a stable system or have slightly obscure hardware. + It's also not very well tested on x86-64 currently. + You have been warned. + + Say Y here if you are feeling brave and building a kernel for a + desktop, embedded or real-time system. Say N if you are unsure. + +CONFIG_MAGIC_SYSRQ + If you say Y here, you will have some control over the system even + if the system crashes for example during kernel debugging (e.g., you + will be able to flush the buffer cache to disk, reboot the system + immediately or dump some status information). This is accomplished + by pressing various keys while holding SysRq (Alt+PrintScreen). It + also works on a serial console (on PC hardware at least), if you + send a BREAK and then within 5 seconds a command keypress. The + keys are documented in . Don't say Y + unless you really know what this hack does. + +CONFIG_DEBUG_KERNEL + Say Y here if you are developing drivers or trying to debug and + identify kernel problems. + +CONFIG_DEBUG_SLAB + Say Y here to have the kernel do limited verification on memory + allocation as well as poisoning memory on free to catch use of freed + memory. + +CONFIG_DEBUG_IOVIRT + Say Y here to get warned whenever an attempt is made to do I/O on + obviously invalid addresses such as those generated when ioremap() + calls are forgotten. Memory mapped I/O will go through an extra + check to catch access to unmapped ISA addresses, an access method + that can still be used by old drivers that are being ported from + 2.0/2.2. + +CONFIG_DEBUG_SPINLOCK + Say Y here and build SMP to catch missing spinlock initialization + and certain other kinds of spinlock errors commonly made. This is + best used in conjunction with the NMI watchdog so that spinlock + deadlocks are also debuggable. + +CONFIG_CHECKING + Enables some internal consistency checks for kernel debugging. + You should normally say N. + +CONFIG_SIMNOW + Disable some time consuming optional things for slow CPU simulators. + Say N unless you're running on a slow simulator like Simics or SimNow. + +CONFIG_EARLY_PRINTK + Write kernel log output directly into the VGA buffer. This is useful + for kernel debugging when your machine crashes very early before + the console code is initialized. For normal operation it is not + recommended because it looks ugly and doesn't cooperate with + klogd/syslogd or the X server.You should normally N here, unless + you want to debug such a crash. + diff -Nru a/arch/x86_64/Makefile b/arch/x86_64/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,130 @@ +# +# x86_64/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture +# +# 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 archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# +# 19990713 Artur Skawina +# Added '-march' and '-mpreferred-stack-boundary' support +# 20000913 Pavel Machek +# Converted for x86_64 architecture +# 20010105 Andi Kleen, add IA32 compiler. +# +# $Id: Makefile,v 1.28 2001/06/29 17:47:43 aj Exp $ + + +# +# boot system currently needs IA32 tools to link (to be fixed) +# +# Change this to your i386 compiler/binutils +IA32_PREFIX := /usr/bin/ +IA32_CC := $(IA32_PREFIX)gcc -O2 -fomit-frame-pointer -nostdinc -I $(HPATH) +IA32_LD := $(IA32_PREFIX)ld +IA32_AS := $(IA32_PREFIX)gcc -D__ASSEMBLY__ -traditional -c -nostdinc -I $(HPATH) +IA32_OBJCOPY := $(IA32_PREFIX)objcopy +IA32_CPP := $(IA32_PREFIX)gcc -E +export IA32_CC IA32_LD IA32_AS IA32_OBJCOPY IA32_CPP + + +LD=$(CROSS_COMPILE)ld -m elf_x86_64 +OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S +LDFLAGS=-e stext +LINKFLAGS =-T $(TOPDIR)/arch/x86_64/vmlinux.lds $(LDFLAGS) + +CFLAGS += $(shell if $(CC) -mno-red-zone -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mno-red-zone"; fi ) +CFLAGS += -mcmodel=kernel +CFLAGS += -pipe +# generates worse code, but makes the assembly much more readable: +CFLAGS += -fno-reorder-blocks +# work around early gcc 3.1 bugs. Later snapshots should this already fixed. +CFLAGS += -fno-strength-reduce +# make sure all inline functions are inlined +CFLAGS += -finline-limit=3000 + +#CFLAGS += -g + +# prevent gcc from keeping the stack 16 byte aligned (FIXME) +#CFLAGS += -mpreferred-stack-boundary=2 + +HEAD := arch/x86_64/kernel/head.o arch/x86_64/kernel/head64.o arch/x86_64/kernel/init_task.o + +SUBDIRS := arch/x86_64/tools $(SUBDIRS) arch/x86_64/kernel arch/x86_64/mm arch/x86_64/lib +CORE_FILES := arch/x86_64/kernel/kernel.o $(CORE_FILES) +CORE_FILES += arch/x86_64/mm/mm.o +LIBS := $(TOPDIR)/arch/x86_64/lib/lib.a $(LIBS) + +CLEAN_FILES += include/asm-x86_64/offset.h + +ifdef CONFIG_IA32_EMULATION +SUBDIRS += arch/x86_64/ia32 +CORE_FILES += arch/x86_64/ia32/ia32.o +endif + +ifdef CONFIG_HOSTFS +SUBDIRS += arch/x86_64/hostfs +core-$(CONFIG_HOSTFS) += arch/x86_64/hostfs/hostfs.o +endif + +CORE_FILES += $(core-y) + +arch/x86_64/tools: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/x86_64/tools + +arch/x86_64/kernel: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/x86_64/kernel + +arch/x86_64/mm: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/x86_64/mm + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +vmlinux: arch/x86_64/vmlinux.lds + +checkoffset: FORCE + make -C arch/$(ARCH)/tools $(TOPDIR)/include/asm-x86_64/offset.h + +FORCE: ; + +.PHONY: zImage bzImage compressed zlilo bzlilo zdisk bzdisk install \ + clean archclean archmrproper archdep checkoffset + +bzImage: checkoffset vmlinux + @$(MAKEBOOT) bzImage + +bzImage-padded: checkoffset vmlinux + @$(MAKEBOOT) bzImage-padded + +tmp: + @$(MAKEBOOT) BOOTIMAGE=bzImage zlilo +bzlilo: checkoffset vmlinux + @$(MAKEBOOT) BOOTIMAGE=bzImage zlilo + +zdisk: checkoffset vmlinux + @$(MAKEBOOT) BOOTIMAGE=zImage zdisk + +bzdisk: checkoffset vmlinux + @$(MAKEBOOT) BOOTIMAGE=bzImage zdisk + +install: checkoffset vmlinux + @$(MAKEBOOT) BOOTIMAGE=bzImage install + +archclean: + @$(MAKEBOOT) clean + $(MAKE) -C $(TOPDIR)/arch/x86_64/tools clean + +archmrproper: + rm -f $(TOPDIR)/arch/x86_64/tools/offset.h + rm -f $(TOPDIR)/arch/x86_64/tools/offset.tmp + rm -f $(TOPDIR)/include/asm-x86_64/offset.h + +archdep: + @$(MAKE) -C $(TOPDIR)/arch/x86_64/tools all + @$(MAKEBOOT) dep diff -Nru a/arch/x86_64/boot/Makefile b/arch/x86_64/boot/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/boot/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,92 @@ +# +# arch/x86_64/boot/Makefile +# +# 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 archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# + +BOOT_INCL = $(TOPDIR)/include/linux/config.h \ + $(TOPDIR)/include/linux/autoconf.h \ + $(TOPDIR)/include/asm/boot.h + +zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build + $(OBJCOPY) compressed/vmlinux compressed/vmlinux.out + tools/build bootsect setup compressed/vmlinux.out $(ROOT_DEV) > zImage + +bzImage: $(CONFIGURE) bbootsect bsetup compressed/bvmlinux tools/build + $(OBJCOPY) compressed/bvmlinux compressed/bvmlinux.out + tools/build -b bbootsect bsetup compressed/bvmlinux.out $(ROOT_DEV) > bzImage + +bzImage-padded: bzImage + dd if=/dev/zero bs=1k count=70 >> bzImage + +compressed/vmlinux: $(TOPDIR)/vmlinux + @$(MAKE) -C compressed vmlinux + +compressed/bvmlinux: $(TOPDIR)/vmlinux + @$(MAKE) -C compressed bvmlinux + +zdisk: $(BOOTIMAGE) + dd bs=8192 if=$(BOOTIMAGE) of=/dev/fd0 + +zlilo: $(CONFIGURE) $(BOOTIMAGE) + if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi + if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi + cat $(BOOTIMAGE) > $(INSTALL_PATH)/vmlinuz + cp $(TOPDIR)/System.map $(INSTALL_PATH)/ + if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi + +install: $(CONFIGURE) $(BOOTIMAGE) + sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map "$(INSTALL_PATH)" + +tools/build: tools/build.c + $(HOSTCC) $(HOSTCFLAGS) -o $@ $< + +bootsect: bootsect.o + $(IA32_LD) -Ttext 0x0 -s --oformat binary -o $@ $< + +bootsect.o: bootsect.s + $(IA32_AS) -o $@ $< + +bootsect.s: bootsect.S Makefile $(BOOT_INCL) + $(IA32_CPP) $(CPPFLAGS) -traditional -D__ASSEMBLY__ $(SVGA_MODE) $(RAMDISK) $< -o $@ + +bbootsect: bbootsect.o + $(IA32_LD) -Ttext 0x0 -s --oformat binary $< -o $@ + +bbootsect.o: bbootsect.s + $(IA32_AS) -o $@ $< + +bbootsect.s: bootsect.S Makefile $(BOOT_INCL) + $(IA32_CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -D__ASSEMBLY__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + +setup: setup.o + $(IA32_LD) -Ttext 0x0 -s --oformat binary -e begtext -o $@ $< + +setup.o: setup.s + $(IA32_AS) -o $@ $< + +setup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h + $(IA32_CPP) $(CPPFLAGS) -traditional -D__ASSEMBLY__ $(SVGA_MODE) $(RAMDISK) $< -o $@ + +bsetup: bsetup.o + $(IA32_LD) -Ttext 0x0 -s --oformat binary -e begtext -o $@ $< + +bsetup.o: bsetup.s + $(IA32_AS) -o $@ $< + +bsetup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h + $(IA32_CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -D__ASSEMBLY__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + +dep: + +clean: + rm -f tools/build + rm -f setup bootsect zImage compressed/vmlinux.out + rm -f bsetup bbootsect bzImage compressed/bvmlinux.out + @$(MAKE) -C compressed clean + + diff -Nru a/arch/x86_64/boot/bootsect.S b/arch/x86_64/boot/bootsect.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/boot/bootsect.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,416 @@ +/* + * bootsect.S Copyright (C) 1991, 1992 Linus Torvalds + * + * modified by Drew Eckhardt + * modified by Bruce Evans (bde) + * modified by Chris Noe (May 1999) (as86 -> gas) + * + * 360k/720k disk support: Andrzej Krzysztofowicz + * + * BIG FAT NOTE: We're in real mode using 64k segments. Therefore segment + * addresses must be multiplied by 16 to obtain their respective linear + * addresses. To avoid confusion, linear addresses are written using leading + * hex while segment addresses are written as segment:offset. + * + * bde - should not jump blindly, there may be systems with only 512K low + * memory. Use int 0x12 to get the top of memory, etc. + * + * It then loads 'setup' directly after itself (0x90200), and the system + * at 0x10000, using BIOS interrupts. + * + * NOTE! currently system is at most (8*65536-4096) bytes long. This should + * be no problem, even in the future. I want to keep it simple. This 508 kB + * kernel size should be enough, especially as this doesn't contain the + * buffer cache as in minix (and especially now that the kernel is + * compressed :-) + * + * The loader has been made as simple as possible, and continuous + * read errors will result in a unbreakable loop. Reboot by hand. It + * loads pretty fast by getting whole tracks at a time whenever possible. + */ + +#include + +SETUPSECTS = 4 /* default nr of setup-sectors */ +BOOTSEG = 0x07C0 /* original address of boot-sector */ +INITSEG = DEF_INITSEG /* we move boot here - out of the way */ +SETUPSEG = DEF_SETUPSEG /* setup starts here */ +SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */ +SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */ + /* to be loaded */ +ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */ +SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */ + +#ifndef SVGA_MODE +#define SVGA_MODE ASK_VGA +#endif + +#ifndef RAMDISK +#define RAMDISK 0 +#endif + +#ifndef ROOT_RDONLY +#define ROOT_RDONLY 1 +#endif + +.code16 +.text + +.global _start +_start: + +# First things first. Move ourself from 0x7C00 -> 0x90000 and jump there. + + movw $BOOTSEG, %ax + movw %ax, %ds # %ds = BOOTSEG + movw $INITSEG, %ax + movw %ax, %es # %ax = %es = INITSEG + movw $256, %cx + subw %si, %si + subw %di, %di + cld + rep + movsw + ljmp $INITSEG, $go + +# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We +# wouldn't have to worry about this if we checked the top of memory. Also +# my BIOS can be configured to put the wini drive tables in high memory +# instead of in the vector table. The old stack might have clobbered the +# drive table. + +go: movw $0x4000-12, %di # 0x4000 is an arbitrary value >= + # length of bootsect + length of + # setup + room for stack; + # 12 is disk parm size. + movw %ax, %ds # %ax and %es already contain INITSEG + movw %ax, %ss + movw %di, %sp # put stack at INITSEG:0x4000-12. + +# Many BIOS's default disk parameter tables will not recognize +# multi-sector reads beyond the maximum sector number specified +# in the default diskette parameter tables - this may mean 7 +# sectors in some cases. +# +# Since single sector reads are slow and out of the question, +# we must take care of this by creating new parameter tables +# (for the first disk) in RAM. We will set the maximum sector +# count to 36 - the most we will encounter on an ED 2.88. +# +# High doesn't hurt. Low does. +# +# Segments are as follows: %cs = %ds = %es = %ss = INITSEG, %fs = 0, +# and %gs is unused. + + movw %cx, %fs # %fs = 0 + movw $0x78, %bx # %fs:%bx is parameter table address + pushw %ds + ldsw %fs:(%bx), %si # %ds:%si is source + movb $6, %cl # copy 12 bytes + pushw %di # %di = 0x4000-12. + rep # don't worry about cld + movsw # already done above + popw %di + popw %ds + movb $36, 0x4(%di) # patch sector count + movw %di, %fs:(%bx) + movw %es, %fs:2(%bx) + +# Get disk drive parameters, specifically number of sectors/track. + +# It seems that there is no BIOS call to get the number of sectors. +# Guess 36 sectors if sector 36 can be read, 18 sectors if sector 18 +# can be read, 15 if sector 15 can be read. Otherwise guess 9. +# Note that %cx = 0 from rep movsw above. + + movw $disksizes, %si # table of sizes to try +probe_loop: + lodsb + cbtw # extend to word + movw %ax, sectors + cmpw $disksizes+4, %si + jae got_sectors # If all else fails, try 9 + + xchgw %cx, %ax # %cx = track and sector + xorw %dx, %dx # drive 0, head 0 + movw $0x0200, %bx # address = 512, in INITSEG (%es = %cs) + movw $0x0201, %ax # service 2, 1 sector + int $0x13 + jc probe_loop # try next value + +got_sectors: + movb $0x03, %ah # read cursor pos + xorb %bh, %bh + int $0x10 + movw $9, %cx + movb $0x07, %bl # page 0, attribute 7 (normal) + # %bh is set above; int10 doesn't + # modify it + movw $msg1, %bp + movw $0x1301, %ax # write string, move cursor + int $0x10 # tell the user we're loading.. + +# Load the setup-sectors directly after the moved bootblock (at 0x90200). +# We should know the drive geometry to do it, as setup may exceed first +# cylinder (for 9-sector 360K and 720K floppies). + + movw $0x0001, %ax # set sread (sector-to-read) to 1 as + movw $sread, %si # the boot sector has already been read + movw %ax, (%si) + + call kill_motor # reset FDC + movw $0x0200, %bx # address = 512, in INITSEG +next_step: + movb setup_sects, %al + movw sectors, %cx + subw (%si), %cx # (%si) = sread + cmpb %cl, %al + jbe no_cyl_crossing + movw sectors, %ax + subw (%si), %ax # (%si) = sread +no_cyl_crossing: + call read_track + pushw %ax # save it + call set_next # set %bx properly; it uses %ax,%cx,%dx + popw %ax # restore + subb %al, setup_sects # rest - for next step + jnz next_step + + pushw $SYSSEG + popw %es # %es = SYSSEG + call read_it + call kill_motor + call print_nl + +# After that we check which root-device to use. If the device is +# defined (!= 0), nothing is done and the given device is used. +# Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8) +# depending on the number of sectors we pretend to know we have. + +# Segments are as follows: %cs = %ds = %ss = INITSEG, +# %es = SYSSEG, %fs = 0, %gs is unused. + + movw root_dev, %ax + orw %ax, %ax + jne root_defined + + movw sectors, %bx + movw $0x0208, %ax # /dev/ps0 - 1.2Mb + cmpw $15, %bx + je root_defined + + movb $0x1c, %al # /dev/PS0 - 1.44Mb + cmpw $18, %bx + je root_defined + + movb $0x20, %al # /dev/fd0H2880 - 2.88Mb + cmpw $36, %bx + je root_defined + + movb $0, %al # /dev/fd0 - autodetect +root_defined: + movw %ax, root_dev + +# After that (everything loaded), we jump to the setup-routine +# loaded directly after the bootblock: + + ljmp $SETUPSEG, $0 + +# These variables are addressed via %si register as it gives shorter code. + +sread: .word 0 # sectors read of current track +head: .word 0 # current head +track: .word 0 # current track + +# This routine loads the system at address SYSSEG, making sure +# no 64kB boundaries are crossed. We try to load it as fast as +# possible, loading whole tracks whenever we can. + +read_it: + movw %es, %ax # %es = SYSSEG when called + testw $0x0fff, %ax +die: jne die # %es must be at 64kB boundary + xorw %bx, %bx # %bx is starting address within segment +rp_read: +#ifdef __BIG_KERNEL__ # look in setup.S for bootsect_kludge + bootsect_kludge = 0x220 # 0x200 + 0x20 which is the size of the + lcall *bootsect_kludge # bootsector + bootsect_kludge offset +#else + movw %es, %ax + subw $SYSSEG, %ax + movw %bx, %cx + shr $4, %cx + add %cx, %ax # check offset +#endif + cmpw syssize, %ax # have we loaded everything yet? + jbe ok1_read + + ret + +ok1_read: + movw sectors, %ax + subw (%si), %ax # (%si) = sread + movw %ax, %cx + shlw $9, %cx + addw %bx, %cx + jnc ok2_read + + je ok2_read + + xorw %ax, %ax + subw %bx, %ax + shrw $9, %ax +ok2_read: + call read_track + call set_next + jmp rp_read + +read_track: + pusha + pusha + movw $0xe2e, %ax # loading... message 2e = . + movw $7, %bx + int $0x10 + popa + +# Accessing head, track, sread via %si gives shorter code. + + movw 4(%si), %dx # 4(%si) = track + movw (%si), %cx # (%si) = sread + incw %cx + movb %dl, %ch + movw 2(%si), %dx # 2(%si) = head + movb %dl, %dh + andw $0x0100, %dx + movb $2, %ah + pushw %dx # save for error dump + pushw %cx + pushw %bx + pushw %ax + int $0x13 + jc bad_rt + + addw $8, %sp + popa + ret + +set_next: + movw %ax, %cx + addw (%si), %ax # (%si) = sread + cmp sectors, %ax + jne ok3_set + movw $0x0001, %ax + xorw %ax, 2(%si) # change head + jne ok4_set + incw 4(%si) # next track +ok4_set: + xorw %ax, %ax +ok3_set: + movw %ax, (%si) # set sread + shlw $9, %cx + addw %cx, %bx + jnc set_next_fin + movw %es, %ax + addb $0x10, %ah + movw %ax, %es + xorw %bx, %bx +set_next_fin: + ret + +bad_rt: + pushw %ax # save error code + call print_all # %ah = error, %al = read + xorb %ah, %ah + xorb %dl, %dl + int $0x13 + addw $10, %sp + popa + jmp read_track + +# print_all is for debugging purposes. +# +# it will print out all of the registers. The assumption is that this is +# called from a routine, with a stack frame like +# +# %dx +# %cx +# %bx +# %ax +# (error) +# ret <- %sp + +print_all: + movw $5, %cx # error code + 4 registers + movw %sp, %bp +print_loop: + pushw %cx # save count remaining + call print_nl # <-- for readability + cmpb $5, %cl + jae no_reg # see if register name is needed + + movw $0xe05 + 'A' - 1, %ax + subb %cl, %al + int $0x10 + movb $'X', %al + int $0x10 + movb $':', %al + int $0x10 +no_reg: + addw $2, %bp # next register + call print_hex # print it + popw %cx + loop print_loop + ret + +print_nl: + movw $0xe0d, %ax # CR + int $0x10 + movb $0xa, %al # LF + int $0x10 + ret + +# print_hex is for debugging purposes, and prints the word +# pointed to by %ss:%bp in hexadecimal. + +print_hex: + movw $4, %cx # 4 hex digits + movw (%bp), %dx # load word into %dx +print_digit: + rolw $4, %dx # rotate to use low 4 bits + movw $0xe0f, %ax # %ah = request + andb %dl, %al # %al = mask for nybble + addb $0x90, %al # convert %al to ascii hex + daa # in only four instructions! + adc $0x40, %al + daa + int $0x10 + loop print_digit + ret + +# This procedure turns off the floppy drive motor, so +# that we enter the kernel in a known state, and +# don't have to worry about it later. +# NOTE: Doesn't save %ax or %dx; do it yourself if you need to. + +kill_motor: + movw $0x3f2, %dx + xorb %al, %al + outb %al, %dx + ret + +sectors: .word 0 +disksizes: .byte 36, 18, 15, 9 +msg1: .byte 13, 10 + .ascii "Loading" + +# XXX: This is a fairly snug fit. + +.org 497 +setup_sects: .byte SETUPSECTS +root_flags: .word ROOT_RDONLY +syssize: .word SYSSIZE +swap_dev: .word SWAP_DEV +ram_size: .word RAMDISK +vid_mode: .word SVGA_MODE +root_dev: .word ROOT_DEV +boot_flag: .word 0xAA55 diff -Nru a/arch/x86_64/boot/compressed/Makefile b/arch/x86_64/boot/compressed/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/boot/compressed/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,43 @@ +# +# linux/arch/i386/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux +# + +HEAD = head.o +SYSTEM = $(TOPDIR)/vmlinux + +OBJECTS = $(HEAD) misc.o + +IA32_CFLAGS := -O2 -DSTDC_HEADERS + +# +# ZIMAGE_OFFSET is the load offset of the compression loader +# BZIMAGE_OFFSET is the load offset of the high loaded compression loader +# +BZIMAGE_OFFSET = 0x100000 + +BZLINKFLAGS = -Ttext $(BZIMAGE_OFFSET) $(ZLDFLAGS) + +all: vmlinux + +bvmlinux: piggy.o $(OBJECTS) + $(IA32_LD) $(BZLINKFLAGS) -o bvmlinux $(OBJECTS) piggy.o + +head.o: head.S + $(IA32_AS) -c head.S + +misc.o: misc.c + $(IA32_CC) $(IA32_CFLAGS) -c misc.c + +piggy.o: $(SYSTEM) + tmppiggy=_tmp_$$$$piggy; \ + rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ + $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ + gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ + echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ + $(IA32_LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \ + rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk + +clean: + rm -f vmlinux bvmlinux _tmp_* diff -Nru a/arch/x86_64/boot/compressed/head.S b/arch/x86_64/boot/compressed/head.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/boot/compressed/head.S Tue Feb 19 18:09:01 2002 @@ -0,0 +1,142 @@ +/* + * linux/boot/head.S + * + * Copyright (C) 1991, 1992, 1993 Linus Torvalds + * + * $Id: head.S,v 1.3 2001/04/20 00:59:28 ak Exp $ + */ + +/* + * head.S contains the 32-bit startup code. + * + * NOTE!!! Startup happens at absolute address 0x00001000, which is also where + * the page directory will exist. The startup code will be overwritten by + * the page directory. [According to comments etc elsewhere on a compressed + * kernel it will end up at 0x1000 + 1Mb I hope so as I assume this. - AC] + * + * Page 0 is deliberately kept safe, since System Management Mode code in + * laptops may need to access the BIOS data stored there. This is also + * useful for future device drivers that either access the BIOS via VM86 + * mode. + */ + +/* + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + */ +.code32 +.text + +#include +#include + + .code32 + .globl startup_32 + +startup_32: + cld + cli + movl $(__KERNEL_DS),%eax + movl %eax,%ds + movl %eax,%es + movl %eax,%fs + movl %eax,%gs + + lss SYMBOL_NAME(stack_start),%esp + xorl %eax,%eax +1: incl %eax # check that A20 really IS enabled + movl %eax,0x000000 # loop forever if it isn't + cmpl %eax,0x100000 + je 1b + +/* + * Initialize eflags. Some BIOS's leave bits like NT set. This would + * confuse the debugger if this code is traced. + * XXX - best to initialize before switching to protected mode. + */ + pushl $0 + popfl +/* + * Clear BSS + */ + xorl %eax,%eax + movl $ SYMBOL_NAME(_edata),%edi + movl $ SYMBOL_NAME(_end),%ecx + subl %edi,%ecx + cld + rep + stosb +/* + * Do the decompression, and jump to the new kernel.. + */ + subl $16,%esp # place for structure on the stack + movl %esp,%eax + pushl %esi # real mode pointer as second arg + pushl %eax # address of structure as first arg + call SYMBOL_NAME(decompress_kernel) + orl %eax,%eax + jnz 3f + addl $8,%esp + xorl %ebx,%ebx + ljmp $(__KERNEL_CS), $0x100000 + +/* + * We come here, if we were loaded high. + * We need to move the move-in-place routine down to 0x1000 + * and then start it with the buffer addresses in registers, + * which we got from the stack. + */ +3: + movl %esi,%ebx + movl $move_routine_start,%esi + movl $0x1000,%edi + movl $move_routine_end,%ecx + subl %esi,%ecx + addl $3,%ecx + shrl $2,%ecx + cld + rep + movsl + + popl %esi # discard the address + addl $4,%esp # real mode pointer + popl %esi # low_buffer_start + popl %ecx # lcount + popl %edx # high_buffer_start + popl %eax # hcount + movl $0x100000,%edi + cli # make sure we don't get interrupted + ljmp $(__KERNEL_CS), $0x1000 # and jump to the move routine + +/* + * Routine (template) for moving the decompressed kernel in place, + * if we were high loaded. This _must_ PIC-code ! + */ +move_routine_start: + movl %ecx,%ebp + shrl $2,%ecx + rep + movsl + movl %ebp,%ecx + andl $3,%ecx + rep + movsb + movl %edx,%esi + movl %eax,%ecx # NOTE: rep movsb won't move if %ecx == 0 + addl $3,%ecx + shrl $2,%ecx + rep + movsl + movl %ebx,%esi # Restore setup pointer + xorl %ebx,%ebx + ljmp $(__KERNEL_CS), $0x100000 +move_routine_end: + + +/* Stack for uncompression */ + .align 32 +user_stack: + .fill 4096,4,0 +stack_start: + .long user_stack+4096 + .word __KERNEL_DS + diff -Nru a/arch/x86_64/boot/compressed/misc.c b/arch/x86_64/boot/compressed/misc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/boot/compressed/misc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,431 @@ +/* + * misc.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * puts by Nick Holloway 1993, better puts by Martin Mares 1995 + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + */ + +#include "miscsetup.h" +#include + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy +#define memzero(s, n) memset ((s), 0, (n)) + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* Sliding window buffer */ + +static unsigned insize = 0; /* valid bytes in inbuf */ +static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ +static unsigned outcnt = 0; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +/* + * This is set up by the setup-routine at boot-time + */ +static unsigned char *real_mode; /* Pointer to real-mode data */ + +#define EXT_MEM_K (*(unsigned short *)(real_mode + 0x2)) +#ifndef STANDARD_MEMORY_BIOS_CALL +#define ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0)) +#endif +#define SCREEN_INFO (*(struct screen_info *)(real_mode+0)) + +extern char input_data[]; +extern int input_len; + +static long bytes_out = 0; +static uch *output_data; +static unsigned long output_ptr = 0; + + +static void *malloc(int size); +static void free(void *where); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +static void puts(const char *); + +extern int end; +static long free_mem_ptr = (long)&end; +static long free_mem_end_ptr; + +#define INPLACE_MOVE_ROUTINE 0x1000 +#define LOW_BUFFER_START 0x2000 +#define LOW_BUFFER_MAX 0x90000 +#define HEAP_SIZE 0x3000 +static unsigned int low_buffer_end, low_buffer_size; +static int high_loaded =0; +static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; + +static char *vidmem = (char *)0xb8000; +static int vidport; +static int lines, cols; + +#include "../../../../lib/inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size <0) error("Malloc error\n"); + if (free_mem_ptr <= 0) error("Memory error\n"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + if (free_mem_ptr >= free_mem_end_ptr) + error("\nOut of memory\n"); + + return p; +} + +static void free(void *where) +{ /* Don't care */ +} + +static void gzip_mark(void **ptr) +{ + *ptr = (void *) free_mem_ptr; +} + +static void gzip_release(void **ptr) +{ + free_mem_ptr = (long) *ptr; +} + +static void scroll(void) +{ + int i; + + memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 ); + for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 ) + vidmem[i] = ' '; +} + +static void puts(const char *s) +{ + int x,y,pos; + char c; + + x = SCREEN_INFO.orig_x; + y = SCREEN_INFO.orig_y; + + while ( ( c = *s++ ) != '\0' ) { + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } + } + } + + SCREEN_INFO.orig_x = x; + SCREEN_INFO.orig_y = y; + + pos = (x + cols * y) * 2; /* Update cursor position */ + outb_p(14, vidport); + outb_p(0xff & (pos >> 9), vidport+1); + outb_p(15, vidport); + outb_p(0xff & (pos >> 1), vidport+1); +} + +void* memset(void* s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window_high(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, ch; + in = window; + for (n = 0; n < outcnt; n++) { + ch = *output_data++ = *in++; + if ((ulg)output_data == low_buffer_end) output_data=high_buffer_start; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window(void) +{ + if (high_loaded) flush_window_high(); + else flush_window_low(); +} + +static void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +void setup_normal_output_buffer(void) +{ +#ifdef STANDARD_MEMORY_BIOS_CALL + if (EXT_MEM_K < 1024) error("Less than 2MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) error("Less than 2MB of memory.\n"); +#endif + output_data = (char *)0x100000; /* Points to 1M */ + free_mem_end_ptr = (long)real_mode; +} + +struct moveparams { + uch *low_buffer_start; int lcount; + uch *high_buffer_start; int hcount; +}; + +void setup_output_buffer_if_we_run_high(struct moveparams *mv) +{ + high_buffer_start = (uch *)(((ulg)&end) + HEAP_SIZE); +#ifdef STANDARD_MEMORY_BIOS_CALL + if (EXT_MEM_K < (3*1024)) error("Less than 4MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3*1024)) error("Less than 4MB of memory.\n"); +#endif + mv->low_buffer_start = output_data = (char *)LOW_BUFFER_START; + low_buffer_end = ((unsigned int)real_mode > LOW_BUFFER_MAX + ? LOW_BUFFER_MAX : (unsigned int)real_mode) & ~0xfff; + low_buffer_size = low_buffer_end - LOW_BUFFER_START; + high_loaded = 1; + free_mem_end_ptr = (long)high_buffer_start; + if ( (0x100000 + low_buffer_size) > ((ulg)high_buffer_start)) { + high_buffer_start = (uch *)(0x100000 + low_buffer_size); + mv->hcount = 0; /* say: we need not to move high_buffer */ + } + else mv->hcount = -1; + mv->high_buffer_start = high_buffer_start; +} + +void close_output_buffer_if_we_run_high(struct moveparams *mv) +{ + if (bytes_out > low_buffer_size) { + mv->lcount = low_buffer_size; + if (mv->hcount) + mv->hcount = bytes_out - low_buffer_size; + } else { + mv->lcount = bytes_out; + mv->hcount = 0; + } +} + +void check_cpu(void) +{ + int res = 0; + asm volatile( " \n\ + movl $3,%%edx # at least 386 \n\ + pushfl # push EFLAGS \n\ + popl %%eax # get EFLAGS \n\ + movl %%eax,%%ecx # save original EFLAGS \n\ + xorl $0x40000,%%eax # flip AC bit in EFLAGS \n\ + pushl %%eax # copy to EFLAGS \n\ + popfl # set EFLAGS \n\ + pushfl # get new EFLAGS \n\ + popl %%eax # put it in eax \n\ + xorl %%ecx,%%eax # change in flags \n\ + andl $0x40000,%%eax # check if AC bit changed \n\ + je 1f \n\ +\n\ + movl $4,%%edx # at least 486 \n\ + movl %%ecx,%%eax \n\ + xorl $0x200000,%%eax # check ID flag \n\ + pushl %%eax \n\ + popfl # if we are on a straight 486DX, SX, or \n\ + pushfl # 487SX we can't change it \n\ + popl %%eax \n\ + xorl %%ecx,%%eax \n\ + pushl %%ecx # restore original EFLAGS \n\ + popfl \n\ + andl $0x200000,%%eax \n\ + je 1f \n\ +\n\ + /* get vendor info */ \n\ +# xorl %%eax,%%eax # call CPUID with 0 -> return vendor ID \n\ +# cpuid \n\ +# movl $5, %%edx \n\ +# cmpl $0x41757468,%%ebx # check thats amd \n\ +# jne 1f \n\ +\n\ + mov $0x80000000,%%eax # Is extended cpuid supported?\n\ + cpuid\n\ + test $0x80000000,%%eax\n\ + movl $5, %%edx \n\ + jz 1f\n\ +\n\ + movl $0x80000001,%%eax \n\ + cpuid \n\ + andl $0x20000000,%%edx \n\ + movl $6, %%edx \n\ + jz 1f \n\ +\n\ + movl $7, %%edx \n\ +1:" : "=d" (res) : : "eax", "ebx", "ecx" ); + + switch (res) { + case 3: puts( "386" ); + break; + case 4: puts( "486" ); + break; + case 5: puts( "no extended cpuid" ); + break; + case 6: puts( "non-64bit 586+" ); + break; + case 7: puts( "64bit" ); + break; + default:puts( "internal error" ); + break; + } + if (res !=7) + error( "Sorry, your CPU is not capable of running 64-bit kernel." ); +} + +int decompress_kernel(struct moveparams *mv, void *rmode) +{ + real_mode = rmode; + + if (SCREEN_INFO.orig_video_mode == 7) { + vidmem = (char *) 0xb0000; + vidport = 0x3b4; + } else { + vidmem = (char *) 0xb8000; + vidport = 0x3d4; + } + + lines = SCREEN_INFO.orig_video_lines; + cols = SCREEN_INFO.orig_video_cols; + + if (free_mem_ptr < 0x100000) setup_normal_output_buffer(); + else setup_output_buffer_if_we_run_high(mv); + + makecrc(); + puts("Checking CPU type..."); + check_cpu(); + puts(".\nDecompressing Linux..."); + gunzip(); + puts("done.\nBooting the kernel.\n"); + if (high_loaded) close_output_buffer_if_we_run_high(mv); + return high_loaded; +} diff -Nru a/arch/x86_64/boot/compressed/miscsetup.h b/arch/x86_64/boot/compressed/miscsetup.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/boot/compressed/miscsetup.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,39 @@ +#define NULL 0 +typedef unsigned int size_t; + + +struct screen_info { + unsigned char orig_x; /* 0x00 */ + unsigned char orig_y; /* 0x01 */ + unsigned short dontuse1; /* 0x02 -- EXT_MEM_K sits here */ + unsigned short orig_video_page; /* 0x04 */ + unsigned char orig_video_mode; /* 0x06 */ + unsigned char orig_video_cols; /* 0x07 */ + unsigned short unused2; /* 0x08 */ + unsigned short orig_video_ega_bx; /* 0x0a */ + unsigned short unused3; /* 0x0c */ + unsigned char orig_video_lines; /* 0x0e */ + unsigned char orig_video_isVGA; /* 0x0f */ + unsigned short orig_video_points; /* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + unsigned short lfb_width; /* 0x12 */ + unsigned short lfb_height; /* 0x14 */ + unsigned short lfb_depth; /* 0x16 */ + unsigned long lfb_base; /* 0x18 */ + unsigned long lfb_size; /* 0x1c */ + unsigned short dontuse2, dontuse3; /* 0x20 -- CL_MAGIC and CL_OFFSET here */ + unsigned short lfb_linelength; /* 0x24 */ + unsigned char red_size; /* 0x26 */ + unsigned char red_pos; /* 0x27 */ + unsigned char green_size; /* 0x28 */ + unsigned char green_pos; /* 0x29 */ + unsigned char blue_size; /* 0x2a */ + unsigned char blue_pos; /* 0x2b */ + unsigned char rsvd_size; /* 0x2c */ + unsigned char rsvd_pos; /* 0x2d */ + unsigned short vesapm_seg; /* 0x2e */ + unsigned short vesapm_off; /* 0x30 */ + unsigned short pages; /* 0x32 */ + /* 0x34 -- 0x3f reserved for future expansion */ +}; diff -Nru a/arch/x86_64/boot/install.sh b/arch/x86_64/boot/install.sh --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/boot/install.sh Tue Feb 19 18:09:00 2002 @@ -0,0 +1,39 @@ +#!/bin/sh +# +# arch/i386/boot/install.sh +# +# 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 archive +# for more details. +# +# Copyright (C) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# +# "make install" script for i386 architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install path (blank if root directory) +# + +# User may have a custom install script + +if [ -x /sbin/installkernel ]; then exec /sbin/installkernel "$@"; fi + +# Default install - same as make zlilo + +if [ -f $4/vmlinuz ]; then + mv $4/vmlinuz $4/vmlinuz.old +fi + +if [ -f $4/System.map ]; then + mv $4/System.map $4/System.old +fi + +cat $2 > $4/vmlinuz +cp $3 $4/System.map + +if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi diff -Nru a/arch/x86_64/boot/setup.S b/arch/x86_64/boot/setup.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/boot/setup.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,955 @@ +/* + * setup.S Copyright (C) 1991, 1992 Linus Torvalds + * + * setup.s is responsible for getting the system data from the BIOS, + * and putting them into the appropriate places in system memory. + * both setup.s and system has been loaded by the bootblock. + * + * This code asks the bios for memory/disk/other parameters, and + * puts them in a "safe" place: 0x90000-0x901FF, ie where the + * boot-block used to be. It is then up to the protected mode + * system to read them from there before the area is overwritten + * for buffer-blocks. + * + * Move PS/2 aux init code to psaux.c + * (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92 + * + * some changes and additional features by Christoph Niemann, + * March 1993/June 1994 (Christoph.Niemann@linux.org) + * + * add APM BIOS checking by Stephen Rothwell, May 1994 + * (sfr@canb.auug.org.au) + * + * High load stuff, initrd support and position independency + * by Hans Lermen & Werner Almesberger, February 1996 + * , + * + * Video handling moved to video.S by Martin Mares, March 1996 + * + * + * Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david + * parsons) to avoid loadlin confusion, July 1997 + * + * Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999. + * + * + * Fix to work around buggy BIOSes which dont use carry bit correctly + * and/or report extended memory in CX/DX for e801h memory size detection + * call. As a result the kernel got wrong figures. The int15/e801h docs + * from Ralf Brown interrupt list seem to indicate AX/BX should be used + * anyway. So to avoid breaking many machines (presumably there was a reason + * to orginally use CX/DX instead of AX/BX), we do a kludge to see + * if CX/DX have been changed in the e801 call and if so use AX/BX . + * Michael Miller, April 2001 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Signature words to ensure LILO loaded us right */ +#define SIG1 0xAA55 +#define SIG2 0x5A5A + +INITSEG = DEF_INITSEG # 0x9000, we move boot here, out of the way +SYSSEG = DEF_SYSSEG # 0x1000, system loaded at 0x10000 (65536). +SETUPSEG = DEF_SETUPSEG # 0x9020, this is the current segment + # ... and the former contents of CS + +DELTA_INITSEG = SETUPSEG - INITSEG # 0x0020 + +.code16 +.globl begtext, begdata, begbss, endtext, enddata, endbss + +.text +begtext: +.data +begdata: +.bss +begbss: +.text + +start: + jmp trampoline + +# This is the setup header, and it must start at %cs:2 (old 0x9020:2) + + .ascii "HdrS" # header signature + .word 0x0203 # header version number (>= 0x0105) + # or else old loadlin-1.5 will fail) +realmode_swtch: .word 0, 0 # default_switch, SETUPSEG +start_sys_seg: .word SYSSEG + .word kernel_version # pointing to kernel version string + # above section of header is compatible + # with loadlin-1.5 (header v1.5). Don't + # change it. + +type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin, + # Bootlin, SYSLX, bootsect...) + # See Documentation/i386/boot.txt for + # assigned ids + +# flags, unused bits must be zero (RFU) bit within loadflags +loadflags: +LOADED_HIGH = 1 # If set, the kernel is loaded high +CAN_USE_HEAP = 0x80 # If set, the loader also has set + # heap_end_ptr to tell how much + # space behind setup.S can be used for + # heap purposes. + # Only the loader knows what is free +#ifndef __BIG_KERNEL__ + .byte 0 +#else + .byte LOADED_HIGH +#endif + +setup_move_size: .word 0x8000 # size to move, when setup is not + # loaded at 0x90000. We will move setup + # to 0x90000 then just before jumping + # into the kernel. However, only the + # loader knows how much data behind + # us also needs to be loaded. + +code32_start: # here loaders can put a different + # start address for 32-bit code. +#ifndef __BIG_KERNEL__ + .long 0x1000 # 0x1000 = default for zImage +#else + .long 0x100000 # 0x100000 = default for big kernel +#endif + +ramdisk_image: .long 0 # address of loaded ramdisk image + # Here the loader puts the 32-bit + # address where it loaded the image. + # This only will be read by the kernel. + +ramdisk_size: .long 0 # its size in bytes + +bootsect_kludge: + .word bootsect_helper, SETUPSEG + +heap_end_ptr: .word modelist+1024 # (Header version 0x0201 or later) + # space from here (exclusive) down to + # end of setup code can be used by setup + # for local heap purposes. + +pad1: .word 0 +cmd_line_ptr: .long 0 # (Header version 0x0202 or later) + # If nonzero, a 32-bit pointer + # to the kernel command line. + # The command line should be + # located between the start of + # setup and the end of low + # memory (0xa0000), or it may + # get overwritten before it + # gets read. If this field is + # used, there is no longer + # anything magical about the + # 0x90000 segment; the setup + # can be located anywhere in + # low memory 0x10000 or higher. + +ramdisk_max: .long 0xffffffff + +trampoline: call start_of_setup + .space 1024 +# End of setup header ##################################################### + +start_of_setup: +# Bootlin depends on this being done early + movw $0x01500, %ax + movb $0x81, %dl + int $0x13 + +#ifdef SAFE_RESET_DISK_CONTROLLER +# Reset the disk controller. + movw $0x0000, %ax + movb $0x80, %dl + int $0x13 +#endif + +# Set %ds = %cs, we know that SETUPSEG = %cs at this point + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds +# Check signature at end of setup + cmpw $SIG1, setup_sig1 + jne bad_sig + + cmpw $SIG2, setup_sig2 + jne bad_sig + + jmp good_sig1 + +# Routine to print asciiz string at ds:si +prtstr: + lodsb + andb %al, %al + jz fin + + call prtchr + jmp prtstr + +fin: ret + +# Space printing +prtsp2: call prtspc # Print double space +prtspc: movb $0x20, %al # Print single space (note: fall-thru) + +# Part of above routine, this one just prints ascii al +prtchr: pushw %ax + pushw %cx + xorb %bh, %bh + movw $0x01, %cx + movb $0x0e, %ah + int $0x10 + popw %cx + popw %ax + ret + +beep: movb $0x07, %al + jmp prtchr + +no_sig_mess: .string "No setup signature found ..." + +good_sig1: + jmp good_sig + +# We now have to find the rest of the setup code/data +bad_sig: + movw %cs, %ax # SETUPSEG + subw $DELTA_INITSEG, %ax # INITSEG + movw %ax, %ds + xorb %bh, %bh + movb (497), %bl # get setup sect from bootsect + subw $4, %bx # LILO loads 4 sectors of setup + shlw $8, %bx # convert to words (1sect=2^8 words) + movw %bx, %cx + shrw $3, %bx # convert to segment + addw $SYSSEG, %bx + movw %bx, %cs:start_sys_seg +# Move rest of setup code/data to here + movw $2048, %di # four sectors loaded by LILO + subw %si, %si + movw %cs, %ax # aka SETUPSEG + movw %ax, %es + movw $SYSSEG, %ax + movw %ax, %ds + rep + movsw + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds + cmpw $SIG1, setup_sig1 + jne no_sig + + cmpw $SIG2, setup_sig2 + jne no_sig + + jmp good_sig + +no_sig: + lea no_sig_mess, %si + call prtstr + +no_sig_loop: + jmp no_sig_loop + +good_sig: + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ax, %ds +# Check if an old loader tries to load a big-kernel + testb $LOADED_HIGH, %cs:loadflags # Do we have a big kernel? + jz loader_ok # No, no danger for old loaders. + + cmpb $0, %cs:type_of_loader # Do we have a loader that + # can deal with us? + jnz loader_ok # Yes, continue. + + pushw %cs # No, we have an old loader, + popw %ds # die. + lea loader_panic_mess, %si + call prtstr + + jmp no_sig_loop + +loader_panic_mess: .string "Wrong loader, giving up..." + +loader_ok: +# Get memory size (extended mem, kB) + + xorl %eax, %eax + movl %eax, (0x1e0) +#ifndef STANDARD_MEMORY_BIOS_CALL + movb %al, (E820NR) +# Try three different memory detection schemes. First, try +# e820h, which lets us assemble a memory map, then try e801h, +# which returns a 32-bit memory size, and finally 88h, which +# returns 0-64m + +# method E820H: +# the memory map from hell. e820h returns memory classified into +# a whole bunch of different types, and allows memory holes and +# everything. We scan through this memory map and build a list +# of the first 32 memory areas, which we return at [E820MAP]. +# This is documented at http://www.teleport.com/~acpi/acpihtml/topic245.htm + +#define SMAP 0x534d4150 + +meme820: + xorl %ebx, %ebx # continuation counter + movw $E820MAP, %di # point into the whitelist + # so we can have the bios + # directly write into it. + +jmpe820: + movl $0x0000e820, %eax # e820, upper word zeroed + movl $SMAP, %edx # ascii 'SMAP' + movl $20, %ecx # size of the e820rec + pushw %ds # data record. + popw %es + int $0x15 # make the call + jc bail820 # fall to e801 if it fails + + cmpl $SMAP, %eax # check the return is `SMAP' + jne bail820 # fall to e801 if it fails + +# cmpl $1, 16(%di) # is this usable memory? +# jne again820 + + # If this is usable memory, we save it by simply advancing %di by + # sizeof(e820rec). + # +good820: + movb (E820NR), %al # up to 32 entries + cmpb $E820MAX, %al + jnl bail820 + + incb (E820NR) + movw %di, %ax + addw $20, %ax + movw %ax, %di +again820: + cmpl $0, %ebx # check to see if + jne jmpe820 # %ebx is set to EOF +bail820: + + +# method E801H: +# memory size is in 1k chunksizes, to avoid confusing loadlin. +# we store the 0xe801 memory size in a completely different place, +# because it will most likely be longer than 16 bits. +# (use 1e0 because that's what Larry Augustine uses in his +# alternative new memory detection scheme, and it's sensible +# to write everything into the same place.) + +meme801: + stc # fix to work around buggy + xorw %cx,%cx # BIOSes which dont clear/set + xorw %dx,%dx # carry on pass/error of + # e801h memory size call + # or merely pass cx,dx though + # without changing them. + movw $0xe801, %ax + int $0x15 + jc mem88 + + cmpw $0x0, %cx # Kludge to handle BIOSes + jne e801usecxdx # which report their extended + cmpw $0x0, %dx # memory in AX/BX rather than + jne e801usecxdx # CX/DX. The spec I have read + movw %ax, %cx # seems to indicate AX/BX + movw %bx, %dx # are more reasonable anyway... + +e801usecxdx: + andl $0xffff, %edx # clear sign extend + shll $6, %edx # and go from 64k to 1k chunks + movl %edx, (0x1e0) # store extended memory size + andl $0xffff, %ecx # clear sign extend + addl %ecx, (0x1e0) # and add lower memory into + # total size. + +# Ye Olde Traditional Methode. Returns the memory size (up to 16mb or +# 64mb, depending on the bios) in ax. +mem88: + +#endif + movb $0x88, %ah + int $0x15 + movw %ax, (2) + +# Set the keyboard repeat rate to the max + movw $0x0305, %ax + xorw %bx, %bx + int $0x16 + +# Check for video adapter and its parameters and allow the +# user to browse video modes. + call video # NOTE: we need %ds pointing + # to bootsector + +# Get hd0 data... + xorw %ax, %ax + movw %ax, %ds + ldsw (4 * 0x41), %si + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + pushw %ax + movw %ax, %es + movw $0x0080, %di + movw $0x10, %cx + pushw %cx + cld + rep + movsb +# Get hd1 data... + xorw %ax, %ax + movw %ax, %ds + ldsw (4 * 0x46), %si + popw %cx + popw %es + movw $0x0090, %di + rep + movsb +# Check that there IS a hd1 :-) + movw $0x01500, %ax + movb $0x81, %dl + int $0x13 + jc no_disk1 + + cmpb $3, %ah + je is_disk1 + +no_disk1: + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ax, %es + movw $0x0090, %di + movw $0x10, %cx + xorw %ax, %ax + cld + rep + stosb +is_disk1: +# check for Micro Channel (MCA) bus + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ax, %ds + xorw %ax, %ax + movw %ax, (0xa0) # set table length to 0 + movb $0xc0, %ah + stc + int $0x15 # moves feature table to es:bx + jc no_mca + + pushw %ds + movw %es, %ax + movw %ax, %ds + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ax, %es + movw %bx, %si + movw $0xa0, %di + movw (%si), %cx + addw $2, %cx # table length is a short + cmpw $0x10, %cx + jc sysdesc_ok + + movw $0x10, %cx # we keep only first 16 bytes +sysdesc_ok: + rep + movsb + popw %ds +no_mca: +# Check for PS/2 pointing device + movw %cs, %ax # aka SETUPSEG + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ax, %ds + movw $0, (0x1ff) # default is no pointing device + int $0x11 # int 0x11: equipment list + testb $0x04, %al # check if mouse installed + jz no_psmouse + + movw $0xAA, (0x1ff) # device present +no_psmouse: + +#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) +# Then check for an APM BIOS... + # %ds points to the bootsector + movw $0, 0x40 # version = 0 means no APM BIOS + movw $0x05300, %ax # APM BIOS installation check + xorw %bx, %bx + int $0x15 + jc done_apm_bios # Nope, no APM BIOS + + cmpw $0x0504d, %bx # Check for "PM" signature + jne done_apm_bios # No signature, no APM BIOS + + andw $0x02, %cx # Is 32 bit supported? + je done_apm_bios # No 32-bit, no (good) APM BIOS + + movw $0x05304, %ax # Disconnect first just in case + xorw %bx, %bx + int $0x15 # ignore return code + movw $0x05303, %ax # 32 bit connect + xorl %ebx, %ebx + xorw %cx, %cx # paranoia :-) + xorw %dx, %dx # ... + xorl %esi, %esi # ... + xorw %di, %di # ... + int $0x15 + jc no_32_apm_bios # Ack, error. + + movw %ax, (66) # BIOS code segment + movl %ebx, (68) # BIOS entry point offset + movw %cx, (72) # BIOS 16 bit code segment + movw %dx, (74) # BIOS data segment + movl %esi, (78) # BIOS code segment lengths + movw %di, (82) # BIOS data segment length +# Redo the installation check as the 32 bit connect +# modifies the flags returned on some BIOSs + movw $0x05300, %ax # APM BIOS installation check + xorw %bx, %bx + xorw %cx, %cx # paranoia + int $0x15 + jc apm_disconnect # error -> shouldn't happen + + cmpw $0x0504d, %bx # check for "PM" signature + jne apm_disconnect # no sig -> shouldn't happen + + movw %ax, (64) # record the APM BIOS version + movw %cx, (76) # and flags + jmp done_apm_bios + +apm_disconnect: # Tidy up + movw $0x05304, %ax # Disconnect + xorw %bx, %bx + int $0x15 # ignore return code + + jmp done_apm_bios + +no_32_apm_bios: + andw $0xfffd, (76) # remove 32 bit support bit +done_apm_bios: +#endif + +# Now we want to move to protected mode ... + cmpw $0, %cs:realmode_swtch + jz rmodeswtch_normal + + lcall *%cs:realmode_swtch + + jmp rmodeswtch_end + +rmodeswtch_normal: + pushw %cs + call default_switch + +rmodeswtch_end: +# we get the code32 start address and modify the below 'jmpi' +# (loader may have changed it) + movl %cs:code32_start, %eax + movl %eax, %cs:code32 + +# Now we move the system to its rightful place ... but we check if we have a +# big-kernel. In that case we *must* not move it ... + testb $LOADED_HIGH, %cs:loadflags + jz do_move0 # .. then we have a normal low + # loaded zImage + # .. or else we have a high + # loaded bzImage + jmp end_move # ... and we skip moving + +do_move0: + movw $0x100, %ax # start of destination segment + movw %cs, %bp # aka SETUPSEG + subw $DELTA_INITSEG, %bp # aka INITSEG + movw %cs:start_sys_seg, %bx # start of source segment + cld +do_move: + movw %ax, %es # destination segment + incb %ah # instead of add ax,#0x100 + movw %bx, %ds # source segment + addw $0x100, %bx + subw %di, %di + subw %si, %si + movw $0x800, %cx + rep + movsw + cmpw %bp, %bx # assume start_sys_seg > 0x200, + # so we will perhaps read one + # page more than needed, but + # never overwrite INITSEG + # because destination is a + # minimum one page below source + jb do_move + +end_move: +# then we load the segment descriptors + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds + +# Check whether we need to be downward compatible with version <=201 + cmpl $0, cmd_line_ptr + jne end_move_self # loader uses version >=202 features + cmpb $0x20, type_of_loader + je end_move_self # bootsect loader, we know of it + +# Boot loader doesnt support boot protocol version 2.02. +# If we have our code not at 0x90000, we need to move it there now. +# We also then need to move the params behind it (commandline) +# Because we would overwrite the code on the current IP, we move +# it in two steps, jumping high after the first one. + movw %cs, %ax + cmpw $SETUPSEG, %ax + je end_move_self + + cli # make sure we really have + # interrupts disabled ! + # because after this the stack + # should not be used + subw $DELTA_INITSEG, %ax # aka INITSEG + movw %ss, %dx + cmpw %ax, %dx + jb move_self_1 + + addw $INITSEG, %dx + subw %ax, %dx # this will go into %ss after + # the move +move_self_1: + movw %ax, %ds + movw $INITSEG, %ax # real INITSEG + movw %ax, %es + movw %cs:setup_move_size, %cx + std # we have to move up, so we use + # direction down because the + # areas may overlap + movw %cx, %di + decw %di + movw %di, %si + subw $move_self_here+0x200, %cx + rep + movsb + ljmp $SETUPSEG, $move_self_here + +move_self_here: + movw $move_self_here+0x200, %cx + rep + movsb + movw $SETUPSEG, %ax + movw %ax, %ds + movw %dx, %ss +end_move_self: # now we are at the right place + lidt idt_48 # load idt with 0,0 + xorl %eax, %eax # Compute gdt_base + movw %ds, %ax # (Convert %ds:gdt to a linear ptr) + shll $4, %eax + addl $gdt, %eax + movl %eax, (gdt_48+2) + lgdt gdt_48 # load gdt with whatever is + # appropriate + +# that was painless, now we enable a20 + call empty_8042 + + movb $0xD1, %al # command write + outb %al, $0x64 + call empty_8042 + + movb $0xDF, %al # A20 on + outb %al, $0x60 + call empty_8042 + +# +# You must preserve the other bits here. Otherwise embarrasing things +# like laptops powering off on boot happen. Corrected version by Kira +# Brown from Linux 2.2 +# + inb $0x92, %al # + orb $02, %al # "fast A20" version + outb %al, $0x92 # some chips have only this + +# wait until a20 really *is* enabled; it can take a fair amount of +# time on certain systems; Toshiba Tecras are known to have this +# problem. The memory location used here (0x200) is the int 0x80 +# vector, which should be safe to use. + + xorw %ax, %ax # segment 0x0000 + movw %ax, %fs + decw %ax # segment 0xffff (HMA) + movw %ax, %gs +a20_wait: + incw %ax # unused memory location <0xfff0 + movw %ax, %fs:(0x200) # we use the "int 0x80" vector + cmpw %gs:(0x210), %ax # and its corresponding HMA addr + je a20_wait # loop until no longer aliased + +# make sure any possible coprocessor is properly reset.. + xorw %ax, %ax + outb %al, $0xf0 + call delay + + outb %al, $0xf1 + call delay + +# well, that went ok, I hope. Now we mask all interrupts - the rest +# is done in init_IRQ(). + movb $0xFF, %al # mask all interrupts for now + outb %al, $0xA1 + call delay + + movb $0xFB, %al # mask all irq's but irq2 which + outb %al, $0x21 # is cascaded + +# Well, that certainly wasn't fun :-(. Hopefully it works, and we don't +# need no steenking BIOS anyway (except for the initial loading :-). +# The BIOS-routine wants lots of unnecessary data, and it's less +# "interesting" anyway. This is how REAL programmers do it. +# +# Well, now's the time to actually move into protected mode. To make +# things as simple as possible, we do no register set-up or anything, +# we let the gnu-compiled 32-bit programs do that. We just jump to +# absolute address 0x1000 (or the loader supplied one), +# in 32-bit protected mode. +# +# Note that the short jump isn't strictly needed, although there are +# reasons why it might be a good idea. It won't hurt in any case. + movw $1, %ax # protected mode (PE) bit + lmsw %ax # This is it! + jmp flush_instr + +flush_instr: + xorw %bx, %bx # Flag to indicate a boot + xorl %esi, %esi # Pointer to real-mode code + movw %cs, %si + subw $DELTA_INITSEG, %si + shll $4, %esi # Convert to 32-bit pointer +# NOTE: For high loaded big kernels we need a +# jmpi 0x100000,__KERNEL_CS +# +# but we yet haven't reloaded the CS register, so the default size +# of the target offset still is 16 bit. +# However, using an operant prefix (0x66), the CPU will properly +# take our 48 bit far pointer. (INTeL 80386 Programmer's Reference +# Manual, Mixing 16-bit and 32-bit code, page 16-6) + + .byte 0x66, 0xea # prefix + jmpi-opcode +code32: .long 0x1000 # will be set to 0x100000 + # for big kernels + .word __KERNEL_CS + +# Here's a bunch of information about your current kernel.. +kernel_version: .ascii UTS_RELEASE + .ascii " (" + .ascii LINUX_COMPILE_BY + .ascii "@" + .ascii LINUX_COMPILE_HOST + .ascii ") " + .ascii UTS_VERSION + .byte 0 + +# This is the default real mode switch routine. +# to be called just before protected mode transition +default_switch: + cli # no interrupts allowed ! + movb $0x80, %al # disable NMI for bootup + # sequence + outb %al, $0x70 + lret + +# This routine only gets called, if we get loaded by the simple +# bootsect loader _and_ have a bzImage to load. +# Because there is no place left in the 512 bytes of the boot sector, +# we must emigrate to code space here. +bootsect_helper: + cmpw $0, %cs:bootsect_es + jnz bootsect_second + + movb $0x20, %cs:type_of_loader + movw %es, %ax + shrw $4, %ax + movb %ah, %cs:bootsect_src_base+2 + movw %es, %ax + movw %ax, %cs:bootsect_es + subw $SYSSEG, %ax + lret # nothing else to do for now + +bootsect_second: + pushw %cx + pushw %si + pushw %bx + testw %bx, %bx # 64K full? + jne bootsect_ex + + movw $0x8000, %cx # full 64K, INT15 moves words + pushw %cs + popw %es + movw $bootsect_gdt, %si + movw $0x8700, %ax + int $0x15 + jc bootsect_panic # this, if INT15 fails + + movw %cs:bootsect_es, %es # we reset %es to always point + incb %cs:bootsect_dst_base+2 # to 0x10000 +bootsect_ex: + movb %cs:bootsect_dst_base+2, %ah + shlb $4, %ah # we now have the number of + # moved frames in %ax + xorb %al, %al + popw %bx + popw %si + popw %cx + lret + +bootsect_gdt: + .word 0, 0, 0, 0 + .word 0, 0, 0, 0 + +bootsect_src: + .word 0xffff + +bootsect_src_base: + .byte 0x00, 0x00, 0x01 # base = 0x010000 + .byte 0x93 # typbyte + .word 0 # limit16,base24 =0 + +bootsect_dst: + .word 0xffff + +bootsect_dst_base: + .byte 0x00, 0x00, 0x10 # base = 0x100000 + .byte 0x93 # typbyte + .word 0 # limit16,base24 =0 + .word 0, 0, 0, 0 # BIOS CS + .word 0, 0, 0, 0 # BIOS DS + +bootsect_es: + .word 0 + +bootsect_panic: + pushw %cs + popw %ds + cld + leaw bootsect_panic_mess, %si + call prtstr + +bootsect_panic_loop: + jmp bootsect_panic_loop + +bootsect_panic_mess: + .string "INT15 refuses to access high mem, giving up." + +# This routine checks that the keyboard command queue is empty +# (after emptying the output buffers) +# +# Some machines have delusions that the keyboard buffer is always full +# with no keyboard attached... +# +# If there is no keyboard controller, we will usually get 0xff +# to all the reads. With each IO taking a microsecond and +# a timeout of 100,000 iterations, this can take about half a +# second ("delay" == outb to port 0x80). That should be ok, +# and should also be plenty of time for a real keyboard controller +# to empty. +# + +empty_8042: + pushl %ecx + movl $100000, %ecx + +empty_8042_loop: + decl %ecx + jz empty_8042_end_loop + + call delay + + inb $0x64, %al # 8042 status port + testb $1, %al # output buffer? + jz no_output + + call delay + inb $0x60, %al # read it + jmp empty_8042_loop + +no_output: + testb $2, %al # is input buffer full? + jnz empty_8042_loop # yes - loop +empty_8042_end_loop: + popl %ecx + ret + +# Read the cmos clock. Return the seconds in al +gettime: + pushw %cx + movb $0x02, %ah + int $0x1a + movb %dh, %al # %dh contains the seconds + andb $0x0f, %al + movb %dh, %ah + movb $0x04, %cl + shrb %cl, %ah + aad + popw %cx + ret + +# Delay is needed after doing I/O +delay: + outb %al,$0x80 + ret + +# Descriptor tables +gdt: + .word 0, 0, 0, 0 # dummy + + .word 0, 0, 0, 0 # unused + + .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) + .word 0 # base address = 0 + .word 0x9A00 # code read/exec + .word 0x00CF # granularity = 4096, 386 + # (+5th nibble of limit) + + .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) + .word 0 # base address = 0 + .word 0x9200 # data read/write + .word 0x00CF # granularity = 4096, 386 + # (+5th nibble of limit) +# this is 64bit descriptor for code + .word 0xFFFF + .word 0 + .word 0x9A00 # code read/exec + .word 0x00AF # as above, but it is long mode and with D=0 + # it does not seem to do the trick. + +idt_48: + .word 0 # idt limit = 0 + .word 0, 0 # idt base = 0L +gdt_48: + .word 0x8000 # gdt limit=2048, + # 256 GDT entries + + .word 0, 0 # gdt base (filled in later) + +# Include video setup & detection code + +#include "video.S" + +# Setup signature -- must be last +setup_sig1: .word SIG1 +setup_sig2: .word SIG2 + +# After this point, there is some free space which is used by the video mode +# handling code to store the temporary mode table (not used by the kernel). + +modelist: + +.text +endtext: +.data +enddata: +.bss +endbss: diff -Nru a/arch/x86_64/boot/tools/build.c b/arch/x86_64/boot/tools/build.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/boot/tools/build.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,189 @@ +/* + * $Id: build.c,v 1.3 2001/06/26 15:14:50 pavel Exp $ + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1997 Martin Mares + */ + +/* + * This file builds a disk-image from three different files: + * + * - bootsect: exactly 512 bytes of 8086 machine code, loads the rest + * - setup: 8086 machine code, sets up system parm + * - system: 80386 code for actual system + * + * It does some checking that all files are of the correct type, and + * just writes the result to stdout, removing headers and padding to + * the right amount. It also writes some system data to stderr. + */ + +/* + * Changes by tytso to allow root device specification + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + * Cross compiling fixes by Gertjan van Wingerde, July 1996 + * Rewritten by Martin Mares, April 1997 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long u32; + +#define DEFAULT_MAJOR_ROOT 0 +#define DEFAULT_MINOR_ROOT 0 + +/* Minimal number of setup sectors (see also bootsect.S) */ +#define SETUP_SECTS 4 + +byte buf[1024]; +int fd; +int is_big_kernel; + +void die(const char * str, ...) +{ + va_list args; + va_start(args, str); + vfprintf(stderr, str, args); + fputc('\n', stderr); + exit(1); +} + +void file_open(const char *name) +{ + if ((fd = open(name, O_RDONLY, 0)) < 0) + die("Unable to open `%s': %m", name); +} + +void usage(void) +{ + die("Usage: build [-b] bootsect setup system [rootdev] [> image]"); +} + +int main(int argc, char ** argv) +{ + unsigned int i, c, sz, setup_sectors; + u32 sys_size; + byte major_root, minor_root; + struct stat sb; + + if (argc > 2 && !strcmp(argv[1], "-b")) + { + is_big_kernel = 1; + argc--, argv++; + } + if ((argc < 4) || (argc > 5)) + usage(); + if (argc > 4) { + if (!strcmp(argv[4], "CURRENT")) { + if (stat("/", &sb)) { + perror("/"); + die("Couldn't stat /"); + } + major_root = major(sb.st_dev); + minor_root = minor(sb.st_dev); + } else if (strcmp(argv[4], "FLOPPY")) { + if (stat(argv[4], &sb)) { + perror(argv[4]); + die("Couldn't stat root device."); + } + major_root = major(sb.st_rdev); + minor_root = minor(sb.st_rdev); + } else { + major_root = 0; + minor_root = 0; + } + } else { + major_root = DEFAULT_MAJOR_ROOT; + minor_root = DEFAULT_MINOR_ROOT; + } + fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); + + file_open(argv[1]); + i = read(fd, buf, sizeof(buf)); + fprintf(stderr,"Boot sector %d bytes.\n",i); + if (i != 512) + die("Boot block must be exactly 512 bytes"); + if (buf[510] != 0x55 || buf[511] != 0xaa) + die("Boot block hasn't got boot flag (0xAA55)"); + buf[508] = minor_root; + buf[509] = major_root; + if (write(1, buf, 512) != 512) + die("Write call failed"); + close (fd); + + file_open(argv[2]); /* Copy the setup code */ + for (i=0 ; (c=read(fd, buf, sizeof(buf)))>0 ; i+=c ) + if (write(1, buf, c) != c) + die("Write call failed"); + if (c != 0) + die("read-error on `setup'"); + close (fd); + + setup_sectors = (i + 511) / 512; /* Pad unused space with zeros */ + /* for compatibility with ancient versions of LILO. */ + if (setup_sectors < SETUP_SECTS) + setup_sectors = SETUP_SECTS; + fprintf(stderr, "Setup is %d bytes.\n", i); + memset(buf, 0, sizeof(buf)); + while (i < setup_sectors * 512) { + c = setup_sectors * 512 - i; + if (c > sizeof(buf)) + c = sizeof(buf); + if (write(1, buf, c) != c) + die("Write call failed"); + i += c; + } + + file_open(argv[3]); + if (fstat (fd, &sb)) + die("Unable to stat `%s': %m", argv[3]); + sz = sb.st_size; + fprintf (stderr, "System is %d kB\n", sz/1024); + sys_size = (sz + 15) / 16; + /* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */ + if (sys_size > (is_big_kernel ? 0x28000 : DEF_SYSSIZE)) + die("System is too big. Try using %smodules.", + is_big_kernel ? "" : "bzImage or "); + if (sys_size > 0xefff) + fprintf(stderr,"warning: kernel is too big for standalone boot " + "from floppy\n"); + while (sz > 0) { + int l, n; + + l = (sz > sizeof(buf)) ? sizeof(buf) : sz; + if ((n=read(fd, buf, l)) != l) { + if (n < 0) + die("Error reading %s: %m", argv[3]); + else + die("%s: Unexpected EOF", argv[3]); + } + if (write(1, buf, l) != l) + die("Write failed"); + sz -= l; + } + close(fd); + + if (lseek(1, 497, SEEK_SET) != 497) /* Write sizes to the bootsector */ + die("Output: seek failed"); + buf[0] = setup_sectors; + if (write(1, buf, 1) != 1) + die("Write of setup sector count failed"); + if (lseek(1, 500, SEEK_SET) != 500) + die("Output: seek failed"); + buf[0] = (sys_size & 0xff); + buf[1] = ((sys_size >> 8) & 0xff); + if (write(1, buf, 2) != 2) + die("Write of image length failed"); + + return 0; /* Everything is OK */ +} diff -Nru a/arch/x86_64/boot/video.S b/arch/x86_64/boot/video.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/boot/video.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1934 @@ +/* video.S + * + * Display adapter & video mode setup, version 2.13 (14-May-99) + * + * Copyright (C) 1995 -- 1998 Martin Mares + * Based on the original setup.S code (C) Linus Torvalds and Mats Anderson + * + * Rewritten to use GNU 'as' by Chris Noe May 1999 + * + * For further information, look at Documentation/svga.txt. + * + */ + +#include /* for CONFIG_VIDEO_* */ + +/* Enable autodetection of SVGA adapters and modes. */ +#undef CONFIG_VIDEO_SVGA + +/* Enable autodetection of VESA modes */ +#define CONFIG_VIDEO_VESA + +/* Enable compacting of mode table */ +#define CONFIG_VIDEO_COMPACT + +/* Retain screen contents when switching modes */ +#define CONFIG_VIDEO_RETAIN + +/* Enable local mode list */ +#undef CONFIG_VIDEO_LOCAL + +/* Force 400 scan lines for standard modes (hack to fix bad BIOS behaviour */ +#undef CONFIG_VIDEO_400_HACK + +/* Hack that lets you force specific BIOS mode ID and specific dimensions */ +#undef CONFIG_VIDEO_GFX_HACK +#define VIDEO_GFX_BIOS_AX 0x4f02 /* 800x600 on ThinkPad */ +#define VIDEO_GFX_BIOS_BX 0x0102 +#define VIDEO_GFX_DUMMY_RESOLUTION 0x6425 /* 100x37 */ + +/* This code uses an extended set of video mode numbers. These include: + * Aliases for standard modes + * NORMAL_VGA (-1) + * EXTENDED_VGA (-2) + * ASK_VGA (-3) + * Video modes numbered by menu position -- NOT RECOMMENDED because of lack + * of compatibility when extending the table. These are between 0x00 and 0xff. + */ +#define VIDEO_FIRST_MENU 0x0000 + +/* Standard BIOS video modes (BIOS number + 0x0100) */ +#define VIDEO_FIRST_BIOS 0x0100 + +/* VESA BIOS video modes (VESA number + 0x0200) */ +#define VIDEO_FIRST_VESA 0x0200 + +/* Video7 special modes (BIOS number + 0x0900) */ +#define VIDEO_FIRST_V7 0x0900 + +/* Special video modes */ +#define VIDEO_FIRST_SPECIAL 0x0f00 +#define VIDEO_80x25 0x0f00 +#define VIDEO_8POINT 0x0f01 +#define VIDEO_80x43 0x0f02 +#define VIDEO_80x28 0x0f03 +#define VIDEO_CURRENT_MODE 0x0f04 +#define VIDEO_80x30 0x0f05 +#define VIDEO_80x34 0x0f06 +#define VIDEO_80x60 0x0f07 +#define VIDEO_GFX_HACK 0x0f08 +#define VIDEO_LAST_SPECIAL 0x0f09 + +/* Video modes given by resolution */ +#define VIDEO_FIRST_RESOLUTION 0x1000 + +/* The "recalculate timings" flag */ +#define VIDEO_RECALC 0x8000 + +/* Positions of various video parameters passed to the kernel */ +/* (see also include/linux/tty.h) */ +#define PARAM_CURSOR_POS 0x00 +#define PARAM_VIDEO_PAGE 0x04 +#define PARAM_VIDEO_MODE 0x06 +#define PARAM_VIDEO_COLS 0x07 +#define PARAM_VIDEO_EGA_BX 0x0a +#define PARAM_VIDEO_LINES 0x0e +#define PARAM_HAVE_VGA 0x0f +#define PARAM_FONT_POINTS 0x10 + +#define PARAM_LFB_WIDTH 0x12 +#define PARAM_LFB_HEIGHT 0x14 +#define PARAM_LFB_DEPTH 0x16 +#define PARAM_LFB_BASE 0x18 +#define PARAM_LFB_SIZE 0x1c +#define PARAM_LFB_LINELENGTH 0x24 +#define PARAM_LFB_COLORS 0x26 +#define PARAM_VESAPM_SEG 0x2e +#define PARAM_VESAPM_OFF 0x30 +#define PARAM_LFB_PAGES 0x32 + + +/* Define DO_STORE according to CONFIG_VIDEO_RETAIN */ +#ifdef CONFIG_VIDEO_RETAIN +#define DO_STORE call store_screen +#else +#define DO_STORE +#endif /* CONFIG_VIDEO_RETAIN */ + +# This is the main entry point called by setup.S +# %ds *must* be pointing to the bootsector +video: pushw %ds # We use different segments + pushw %ds # FS contains original DS + popw %fs + pushw %cs # DS is equal to CS + popw %ds + pushw %cs # ES is equal to CS + popw %es + xorw %ax, %ax + movw %ax, %gs # GS is zero + cld + call basic_detect # Basic adapter type testing (EGA/VGA/MDA/CGA) +#ifdef CONFIG_VIDEO_SELECT + movw %fs:(0x01fa), %ax # User selected video mode + cmpw $ASK_VGA, %ax # Bring up the menu + jz vid2 + + call mode_set # Set the mode + jc vid1 + + leaw badmdt, %si # Invalid mode ID + call prtstr +vid2: call mode_menu +vid1: +#ifdef CONFIG_VIDEO_RETAIN + call restore_screen # Restore screen contents +#endif /* CONFIG_VIDEO_RETAIN */ +#endif /* CONFIG_VIDEO_SELECT */ + call mode_params # Store mode parameters + popw %ds # Restore original DS + ret + +# Detect if we have CGA, MDA, EGA or VGA and pass it to the kernel. +basic_detect: + movb $0, %fs:(PARAM_HAVE_VGA) + movb $0x12, %ah # Check EGA/VGA + movb $0x10, %bl + int $0x10 + movw %bx, %fs:(PARAM_VIDEO_EGA_BX) # Identifies EGA to the kernel + cmpb $0x10, %bl # No, it's a CGA/MDA/HGA card. + je basret + + incb adapter + movw $0x1a00, %ax # Check EGA or VGA? + int $0x10 + cmpb $0x1a, %al # 1a means VGA... + jne basret # anything else is EGA. + + incb %fs:(PARAM_HAVE_VGA) # We've detected a VGA + incb adapter +basret: ret + +# Store the video mode parameters for later usage by the kernel. +# This is done by asking the BIOS except for the rows/columns +# parameters in the default 80x25 mode -- these are set directly, +# because some very obscure BIOSes supply insane values. +mode_params: +#ifdef CONFIG_VIDEO_SELECT + cmpb $0, graphic_mode + jnz mopar_gr +#endif + movb $0x03, %ah # Read cursor position + xorb %bh, %bh + int $0x10 + movw %dx, %fs:(PARAM_CURSOR_POS) + movb $0x0f, %ah # Read page/mode/width + int $0x10 + movw %bx, %fs:(PARAM_VIDEO_PAGE) + movw %ax, %fs:(PARAM_VIDEO_MODE) # Video mode and screen width + cmpb $0x7, %al # MDA/HGA => segment differs + jnz mopar0 + + movw $0xb000, video_segment +mopar0: movw %gs:(0x485), %ax # Font size + movw %ax, %fs:(PARAM_FONT_POINTS) # (valid only on EGA/VGA) + movw force_size, %ax # Forced size? + orw %ax, %ax + jz mopar1 + + movb %ah, %fs:(PARAM_VIDEO_COLS) + movb %al, %fs:(PARAM_VIDEO_LINES) + ret + +mopar1: movb $25, %al + cmpb $0, adapter # If we are on CGA/MDA/HGA, the + jz mopar2 # screen must have 25 lines. + + movb %gs:(0x484), %al # On EGA/VGA, use the EGA+ BIOS + incb %al # location of max lines. +mopar2: movb %al, %fs:(PARAM_VIDEO_LINES) + ret + +#ifdef CONFIG_VIDEO_SELECT +# Fetching of VESA frame buffer parameters +mopar_gr: + leaw modelist+1024, %di + movb $0x23, %fs:(PARAM_HAVE_VGA) + movw 16(%di), %ax + movw %ax, %fs:(PARAM_LFB_LINELENGTH) + movw 18(%di), %ax + movw %ax, %fs:(PARAM_LFB_WIDTH) + movw 20(%di), %ax + movw %ax, %fs:(PARAM_LFB_HEIGHT) + movb 25(%di), %al + movb $0, %ah + movw %ax, %fs:(PARAM_LFB_DEPTH) + movb 29(%di), %al + movb $0, %ah + movw %ax, %fs:(PARAM_LFB_PAGES) + movl 40(%di), %eax + movl %eax, %fs:(PARAM_LFB_BASE) + movl 31(%di), %eax + movl %eax, %fs:(PARAM_LFB_COLORS) + movl 35(%di), %eax + movl %eax, %fs:(PARAM_LFB_COLORS+4) + +# get video mem size + leaw modelist+1024, %di + movw $0x4f00, %ax + int $0x10 + xorl %eax, %eax + movw 18(%di), %ax + movl %eax, %fs:(PARAM_LFB_SIZE) +# get protected mode interface informations + movw $0x4f0a, %ax + xorw %bx, %bx + xorw %di, %di + int $0x10 + cmp $0x004f, %ax + jnz no_pm + + movw %es, %fs:(PARAM_VESAPM_SEG) + movw %di, %fs:(PARAM_VESAPM_OFF) +no_pm: ret + +# The video mode menu +mode_menu: + leaw keymsg, %si # "Return/Space/Timeout" message + call prtstr + call flush +nokey: call getkt + + cmpb $0x0d, %al # ENTER ? + je listm # yes - manual mode selection + + cmpb $0x20, %al # SPACE ? + je defmd1 # no - repeat + + call beep + jmp nokey + +defmd1: ret # No mode chosen? Default 80x25 + +listm: call mode_table # List mode table +listm0: leaw name_bann, %si # Print adapter name + call prtstr + movw card_name, %si + orw %si, %si + jnz an2 + + movb adapter, %al + leaw old_name, %si + orb %al, %al + jz an1 + + leaw ega_name, %si + decb %al + jz an1 + + leaw vga_name, %si + jmp an1 + +an2: call prtstr + leaw svga_name, %si +an1: call prtstr + leaw listhdr, %si # Table header + call prtstr + movb $0x30, %dl # DL holds mode number + leaw modelist, %si +lm1: cmpw $ASK_VGA, (%si) # End? + jz lm2 + + movb %dl, %al # Menu selection number + call prtchr + call prtsp2 + lodsw + call prthw # Mode ID + call prtsp2 + movb 0x1(%si), %al + call prtdec # Rows + movb $0x78, %al # the letter 'x' + call prtchr + lodsw + call prtdec # Columns + movb $0x0d, %al # New line + call prtchr + movb $0x0a, %al + call prtchr + incb %dl # Next character + cmpb $0x3a, %dl + jnz lm1 + + movb $0x61, %dl + jmp lm1 + +lm2: leaw prompt, %si # Mode prompt + call prtstr + leaw edit_buf, %di # Editor buffer +lm3: call getkey + cmpb $0x0d, %al # Enter? + jz lment + + cmpb $0x08, %al # Backspace? + jz lmbs + + cmpb $0x20, %al # Printable? + jc lm3 + + cmpw $edit_buf+4, %di # Enough space? + jz lm3 + + stosb + call prtchr + jmp lm3 + +lmbs: cmpw $edit_buf, %di # Backspace + jz lm3 + + decw %di + movb $0x08, %al + call prtchr + call prtspc + movb $0x08, %al + call prtchr + jmp lm3 + +lment: movb $0, (%di) + leaw crlft, %si + call prtstr + leaw edit_buf, %si + cmpb $0, (%si) # Empty string = default mode + jz lmdef + + cmpb $0, 1(%si) # One character = menu selection + jz mnusel + + cmpw $0x6373, (%si) # "scan" => mode scanning + jnz lmhx + + cmpw $0x6e61, 2(%si) + jz lmscan + +lmhx: xorw %bx, %bx # Else => mode ID in hex +lmhex: lodsb + orb %al, %al + jz lmuse1 + + subb $0x30, %al + jc lmbad + + cmpb $10, %al + jc lmhx1 + + subb $7, %al + andb $0xdf, %al + cmpb $10, %al + jc lmbad + + cmpb $16, %al + jnc lmbad + +lmhx1: shlw $4, %bx + orb %al, %bl + jmp lmhex + +lmuse1: movw %bx, %ax + jmp lmuse + +mnusel: lodsb # Menu selection + xorb %ah, %ah + subb $0x30, %al + jc lmbad + + cmpb $10, %al + jc lmuse + + cmpb $0x61-0x30, %al + jc lmbad + + subb $0x61-0x30-10, %al + cmpb $36, %al + jnc lmbad + +lmuse: call mode_set + jc lmdef + +lmbad: leaw unknt, %si + call prtstr + jmp lm2 +lmscan: cmpb $0, adapter # Scanning only on EGA/VGA + jz lmbad + + movw $0, mt_end # Scanning of modes is + movb $1, scanning # done as new autodetection. + call mode_table + jmp listm0 +lmdef: ret + +# Additional parts of mode_set... (relative jumps, you know) +setv7: # Video7 extended modes + DO_STORE + subb $VIDEO_FIRST_V7>>8, %bh + movw $0x6f05, %ax + int $0x10 + stc + ret + +_setrec: jmp setrec # Ugly... +_set_80x25: jmp set_80x25 + +# Aliases for backward compatibility. +setalias: + movw $VIDEO_80x25, %ax + incw %bx + jz mode_set + + movb $VIDEO_8POINT-VIDEO_FIRST_SPECIAL, %al + incw %bx + jnz setbad # Fall-through! + +# Setting of user mode (AX=mode ID) => CF=success +mode_set: + movw %ax, %bx + cmpb $0xff, %ah + jz setalias + + testb $VIDEO_RECALC>>8, %ah + jnz _setrec + + cmpb $VIDEO_FIRST_RESOLUTION>>8, %ah + jnc setres + + cmpb $VIDEO_FIRST_SPECIAL>>8, %ah + jz setspc + + cmpb $VIDEO_FIRST_V7>>8, %ah + jz setv7 + + cmpb $VIDEO_FIRST_VESA>>8, %ah + jnc check_vesa + + orb %ah, %ah + jz setmenu + + decb %ah + jz setbios + +setbad: clc + movb $0, do_restore # The screen needn't be restored + ret + +setvesa: + DO_STORE + subb $VIDEO_FIRST_VESA>>8, %bh + movw $0x4f02, %ax # VESA BIOS mode set call + int $0x10 + cmpw $0x004f, %ax # AL=4f if implemented + jnz setbad # AH=0 if OK + + stc + ret + +setbios: + DO_STORE + int $0x10 # Standard BIOS mode set call + pushw %bx + movb $0x0f, %ah # Check if really set + int $0x10 + popw %bx + cmpb %bl, %al + jnz setbad + + stc + ret + +setspc: xorb %bh, %bh # Set special mode + cmpb $VIDEO_LAST_SPECIAL-VIDEO_FIRST_SPECIAL, %bl + jnc setbad + + addw %bx, %bx + jmp *spec_inits(%bx) + +setmenu: + orb %al, %al # 80x25 is an exception + jz _set_80x25 + + pushw %bx # Set mode chosen from menu + call mode_table # Build the mode table + popw %ax + shlw $2, %ax + addw %ax, %si + cmpw %di, %si + jnc setbad + + movw (%si), %ax # Fetch mode ID +_m_s: jmp mode_set + +setres: pushw %bx # Set mode chosen by resolution + call mode_table + popw %bx + xchgb %bl, %bh +setr1: lodsw + cmpw $ASK_VGA, %ax # End of the list? + jz setbad + + lodsw + cmpw %bx, %ax + jnz setr1 + + movw -4(%si), %ax # Fetch mode ID + jmp _m_s + +check_vesa: + leaw modelist+1024, %di + subb $VIDEO_FIRST_VESA>>8, %bh + movw %bx, %cx # Get mode information structure + movw $0x4f01, %ax + int $0x10 + addb $VIDEO_FIRST_VESA>>8, %bh + cmpw $0x004f, %ax + jnz setbad + + movb (%di), %al # Check capabilities. + andb $0x19, %al + cmpb $0x09, %al + jz setvesa # This is a text mode + + movb (%di), %al # Check capabilities. + andb $0x99, %al + cmpb $0x99, %al + jnz _setbad # Doh! No linear frame buffer. + + subb $VIDEO_FIRST_VESA>>8, %bh + orw $0x4000, %bx # Use linear frame buffer + movw $0x4f02, %ax # VESA BIOS mode set call + int $0x10 + cmpw $0x004f, %ax # AL=4f if implemented + jnz _setbad # AH=0 if OK + + movb $1, graphic_mode # flag graphic mode + movb $0, do_restore # no screen restore + stc + ret + +_setbad: jmp setbad # Ugly... + +# Recalculate vertical display end registers -- this fixes various +# inconsistencies of extended modes on many adapters. Called when +# the VIDEO_RECALC flag is set in the mode ID. + +setrec: subb $VIDEO_RECALC>>8, %ah # Set the base mode + call mode_set + jnc rct3 + + movw %gs:(0x485), %ax # Font size in pixels + movb %gs:(0x484), %bl # Number of rows + incb %bl + mulb %bl # Number of visible + decw %ax # scan lines - 1 + movw $0x3d4, %dx + movw %ax, %bx + movb $0x12, %al # Lower 8 bits + movb %bl, %ah + outw %ax, %dx + movb $0x07, %al # Bits 8 and 9 in the overflow register + call inidx + xchgb %al, %ah + andb $0xbd, %ah + shrb %bh + jnc rct1 + orb $0x02, %ah +rct1: shrb %bh + jnc rct2 + orb $0x40, %ah +rct2: movb $0x07, %al + outw %ax, %dx + stc +rct3: ret + +# Table of routines for setting of the special modes. +spec_inits: + .word set_80x25 + .word set_8pixel + .word set_80x43 + .word set_80x28 + .word set_current + .word set_80x30 + .word set_80x34 + .word set_80x60 + .word set_gfx + +# Set the 80x25 mode. If already set, do nothing. +set_80x25: + movw $0x5019, force_size # Override possibly broken BIOS +use_80x25: +#ifdef CONFIG_VIDEO_400_HACK + movw $0x1202, %ax # Force 400 scan lines + movb $0x30, %bl + int $0x10 +#else + movb $0x0f, %ah # Get current mode ID + int $0x10 + cmpw $0x5007, %ax # Mode 7 (80x25 mono) is the only one available + jz st80 # on CGA/MDA/HGA and is also available on EGAM + + cmpw $0x5003, %ax # Unknown mode, force 80x25 color + jnz force3 + +st80: cmpb $0, adapter # CGA/MDA/HGA => mode 3/7 is always 80x25 + jz set80 + + movb %gs:(0x0484), %al # This is EGA+ -- beware of 80x50 etc. + orb %al, %al # Some buggy BIOS'es set 0 rows + jz set80 + + cmpb $24, %al # It's hopefully correct + jz set80 +#endif /* CONFIG_VIDEO_400_HACK */ +force3: DO_STORE + movw $0x0003, %ax # Forced set + int $0x10 +set80: stc + ret + +# Set the 80x50/80x43 8-pixel mode. Simple BIOS calls. +set_8pixel: + DO_STORE + call use_80x25 # The base is 80x25 +set_8pt: + movw $0x1112, %ax # Use 8x8 font + xorb %bl, %bl + int $0x10 + movw $0x1200, %ax # Use alternate print screen + movb $0x20, %bl + int $0x10 + movw $0x1201, %ax # Turn off cursor emulation + movb $0x34, %bl + int $0x10 + movb $0x01, %ah # Define cursor scan lines 6-7 + movw $0x0607, %cx + int $0x10 +set_current: + stc + ret + +# Set the 80x28 mode. This mode works on all VGA's, because it's a standard +# 80x25 mode with 14-point fonts instead of 16-point. +set_80x28: + DO_STORE + call use_80x25 # The base is 80x25 +set14: movw $0x1111, %ax # Use 9x14 font + xorb %bl, %bl + int $0x10 + movb $0x01, %ah # Define cursor scan lines 11-12 + movw $0x0b0c, %cx + int $0x10 + stc + ret + +# Set the 80x43 mode. This mode is works on all VGA's. +# It's a 350-scanline mode with 8-pixel font. +set_80x43: + DO_STORE + movw $0x1201, %ax # Set 350 scans + movb $0x30, %bl + int $0x10 + movw $0x0003, %ax # Reset video mode + int $0x10 + jmp set_8pt # Use 8-pixel font + +# Set the 80x30 mode (all VGA's). 480 scanlines, 16-pixel font. +set_80x30: + call use_80x25 # Start with real 80x25 + DO_STORE + movw $0x3cc, %dx # Get CRTC port + inb %dx, %al + movb $0xd4, %dl + rorb %al # Mono or color? + jc set48a + + movb $0xb4, %dl +set48a: movw $0x0c11, %ax # Vertical sync end (also unlocks CR0-7) + call outidx + movw $0x0b06, %ax # Vertical total + call outidx + movw $0x3e07, %ax # (Vertical) overflow + call outidx + movw $0xea10, %ax # Vertical sync start + call outidx + movw $0xdf12, %ax # Vertical display end + call outidx + movw $0xe715, %ax # Vertical blank start + call outidx + movw $0x0416, %ax # Vertical blank end + call outidx + pushw %dx + movb $0xcc, %dl # Misc output register (read) + inb %dx, %al + movb $0xc2, %dl # (write) + andb $0x0d, %al # Preserve clock select bits and color bit + orb $0xe2, %al # Set correct sync polarity + outb %al, %dx + popw %dx + movw $0x501e, force_size + stc # That's all. + ret + +# Set the 80x34 mode (all VGA's). 480 scans, 14-pixel font. +set_80x34: + call set_80x30 # Set 480 scans + call set14 # And 14-pt font + movw $0xdb12, %ax # VGA vertical display end + movw $0x5022, force_size +setvde: call outidx + stc + ret + +# Set the 80x60 mode (all VGA's). 480 scans, 8-pixel font. +set_80x60: + call set_80x30 # Set 480 scans + call set_8pt # And 8-pt font + movw $0xdf12, %ax # VGA vertical display end + movw $0x503c, force_size + jmp setvde + +# Special hack for ThinkPad graphics +set_gfx: +#ifdef CONFIG_VIDEO_GFX_HACK + movw $VIDEO_GFX_BIOS_AX, %ax + movw $VIDEO_GFX_BIOS_BX, %bx + int $0x10 + movw $VIDEO_GFX_DUMMY_RESOLUTION, force_size + stc +#endif + ret + +#ifdef CONFIG_VIDEO_RETAIN + +# Store screen contents to temporary buffer. +store_screen: + cmpb $0, do_restore # Already stored? + jnz stsr + + testb $CAN_USE_HEAP, loadflags # Have we space for storing? + jz stsr + + pushw %ax + pushw %bx + pushw force_size # Don't force specific size + movw $0, force_size + call mode_params # Obtain params of current mode + popw force_size + movb %fs:(PARAM_VIDEO_LINES), %ah + movb %fs:(PARAM_VIDEO_COLS), %al + movw %ax, %bx # BX=dimensions + mulb %ah + movw %ax, %cx # CX=number of characters + addw %ax, %ax # Calculate image size + addw $modelist+1024+4, %ax + cmpw heap_end_ptr, %ax + jnc sts1 # Unfortunately, out of memory + + movw %fs:(PARAM_CURSOR_POS), %ax # Store mode params + leaw modelist+1024, %di + stosw + movw %bx, %ax + stosw + pushw %ds # Store the screen + movw video_segment, %ds + xorw %si, %si + rep + movsw + popw %ds + incb do_restore # Screen will be restored later +sts1: popw %bx + popw %ax +stsr: ret + +# Restore screen contents from temporary buffer. +restore_screen: + cmpb $0, do_restore # Has the screen been stored? + jz res1 + + call mode_params # Get parameters of current mode + movb %fs:(PARAM_VIDEO_LINES), %cl + movb %fs:(PARAM_VIDEO_COLS), %ch + leaw modelist+1024, %si # Screen buffer + lodsw # Set cursor position + movw %ax, %dx + cmpb %cl, %dh + jc res2 + + movb %cl, %dh + decb %dh +res2: cmpb %ch, %dl + jc res3 + + movb %ch, %dl + decb %dl +res3: movb $0x02, %ah + movb $0x00, %bh + int $0x10 + lodsw # Display size + movb %ah, %dl # DL=number of lines + movb $0, %ah # BX=phys. length of orig. line + movw %ax, %bx + cmpb %cl, %dl # Too many? + jc res4 + + pushw %ax + movb %dl, %al + subb %cl, %al + mulb %bl + addw %ax, %si + addw %ax, %si + popw %ax + movb %cl, %dl +res4: cmpb %ch, %al # Too wide? + jc res5 + + movb %ch, %al # AX=width of src. line +res5: movb $0, %cl + xchgb %ch, %cl + movw %cx, %bp # BP=width of dest. line + pushw %es + movw video_segment, %es + xorw %di, %di # Move the data + addw %bx, %bx # Convert BX and BP to _bytes_ + addw %bp, %bp +res6: pushw %si + pushw %di + movw %ax, %cx + rep + movsw + popw %di + popw %si + addw %bp, %di + addw %bx, %si + decb %dl + jnz res6 + + popw %es # Done +res1: ret +#endif /* CONFIG_VIDEO_RETAIN */ + +# Write to indexed VGA register (AL=index, AH=data, DX=index reg. port) +outidx: outb %al, %dx + pushw %ax + movb %ah, %al + incw %dx + outb %al, %dx + decw %dx + popw %ax + ret + +# Build the table of video modes (stored after the setup.S code at the +# `modelist' label. Each video mode record looks like: +# .word MODE-ID (our special mode ID (see above)) +# .byte rows (number of rows) +# .byte columns (number of columns) +# Returns address of the end of the table in DI, the end is marked +# with a ASK_VGA ID. +mode_table: + movw mt_end, %di # Already filled? + orw %di, %di + jnz mtab1x + + leaw modelist, %di # Store standard modes: + movl $VIDEO_80x25 + 0x50190000, %eax # The 80x25 mode (ALL) + stosl + movb adapter, %al # CGA/MDA/HGA -- no more modes + orb %al, %al + jz mtabe + + decb %al + jnz mtabv + + movl $VIDEO_8POINT + 0x502b0000, %eax # The 80x43 EGA mode + stosl + jmp mtabe + +mtab1x: jmp mtab1 + +mtabv: leaw vga_modes, %si # All modes for std VGA + movw $vga_modes_end-vga_modes, %cx + rep # I'm unable to use movsw as I don't know how to store a half + movsb # of the expression above to cx without using explicit shr. + + cmpb $0, scanning # Mode scan requested? + jz mscan1 + + call mode_scan +mscan1: + +#ifdef CONFIG_VIDEO_LOCAL + call local_modes +#endif /* CONFIG_VIDEO_LOCAL */ + +#ifdef CONFIG_VIDEO_VESA + call vesa_modes # Detect VESA VGA modes +#endif /* CONFIG_VIDEO_VESA */ + +#ifdef CONFIG_VIDEO_SVGA + cmpb $0, scanning # Bypass when scanning + jnz mscan2 + + call svga_modes # Detect SVGA cards & modes +mscan2: +#endif /* CONFIG_VIDEO_SVGA */ + +mtabe: + +#ifdef CONFIG_VIDEO_COMPACT + leaw modelist, %si + movw %di, %dx + movw %si, %di +cmt1: cmpw %dx, %si # Scan all modes + jz cmt2 + + leaw modelist, %bx # Find in previous entries + movw 2(%si), %cx +cmt3: cmpw %bx, %si + jz cmt4 + + cmpw 2(%bx), %cx # Found => don't copy this entry + jz cmt5 + + addw $4, %bx + jmp cmt3 + +cmt4: movsl # Copy entry + jmp cmt1 + +cmt5: addw $4, %si # Skip entry + jmp cmt1 + +cmt2: +#endif /* CONFIG_VIDEO_COMPACT */ + + movw $ASK_VGA, (%di) # End marker + movw %di, mt_end +mtab1: leaw modelist, %si # SI=mode list, DI=list end +ret0: ret + +# Modes usable on all standard VGAs +vga_modes: + .word VIDEO_8POINT + .word 0x5032 # 80x50 + .word VIDEO_80x43 + .word 0x502b # 80x43 + .word VIDEO_80x28 + .word 0x501c # 80x28 + .word VIDEO_80x30 + .word 0x501e # 80x30 + .word VIDEO_80x34 + .word 0x5022 # 80x34 + .word VIDEO_80x60 + .word 0x503c # 80x60 +#ifdef CONFIG_VIDEO_GFX_HACK + .word VIDEO_GFX_HACK + .word VIDEO_GFX_DUMMY_RESOLUTION +#endif + +vga_modes_end: +# Detect VESA modes. + +#ifdef CONFIG_VIDEO_VESA +vesa_modes: + cmpb $2, adapter # VGA only + jnz ret0 + + movw %di, %bp # BP=original mode table end + addw $0x200, %di # Buffer space + movw $0x4f00, %ax # VESA Get card info call + int $0x10 + movw %bp, %di + cmpw $0x004f, %ax # Successful? + jnz ret0 + + cmpw $0x4556, 0x200(%di) + jnz ret0 + + cmpw $0x4153, 0x202(%di) + jnz ret0 + + movw $vesa_name, card_name # Set name to "VESA VGA" + pushw %gs + lgsw 0x20e(%di), %si # GS:SI=mode list + movw $128, %cx # Iteration limit +vesa1: +# gas version 2.9.1, using BFD version 2.9.1.0.23 buggers the next inst. +# XXX: lodsw %gs:(%si), %ax # Get next mode in the list + gs; lodsw + cmpw $0xffff, %ax # End of the table? + jz vesar + + cmpw $0x0080, %ax # Check validity of mode ID + jc vesa2 + + orb %ah, %ah # Valid IDs: 0x0000-0x007f/0x0100-0x07ff + jz vesan # Certain BIOSes report 0x80-0xff! + + cmpw $0x0800, %ax + jnc vesae + +vesa2: pushw %cx + movw %ax, %cx # Get mode information structure + movw $0x4f01, %ax + int $0x10 + movw %cx, %bx # BX=mode number + addb $VIDEO_FIRST_VESA>>8, %bh + popw %cx + cmpw $0x004f, %ax + jnz vesan # Don't report errors (buggy BIOSES) + + movb (%di), %al # Check capabilities. We require + andb $0x19, %al # a color text mode. + cmpb $0x09, %al + jnz vesan + + cmpw $0xb800, 8(%di) # Standard video memory address required + jnz vesan + + testb $2, (%di) # Mode characteristics supplied? + movw %bx, (%di) # Store mode number + jz vesa3 + + xorw %dx, %dx + movw 0x12(%di), %bx # Width + orb %bh, %bh + jnz vesan + + movb %bl, 0x3(%di) + movw 0x14(%di), %ax # Height + orb %ah, %ah + jnz vesan + + movb %al, 2(%di) + mulb %bl + cmpw $8193, %ax # Small enough for Linux console driver? + jnc vesan + + jmp vesaok + +vesa3: subw $0x8108, %bx # This mode has no detailed info specified, + jc vesan # so it must be a standard VESA mode. + + cmpw $5, %bx + jnc vesan + + movw vesa_text_mode_table(%bx), %ax + movw %ax, 2(%di) +vesaok: addw $4, %di # The mode is valid. Store it. +vesan: loop vesa1 # Next mode. Limit exceeded => error +vesae: leaw vesaer, %si + call prtstr + movw %bp, %di # Discard already found modes. +vesar: popw %gs + ret + +# Dimensions of standard VESA text modes +vesa_text_mode_table: + .byte 60, 80 # 0108 + .byte 25, 132 # 0109 + .byte 43, 132 # 010A + .byte 50, 132 # 010B + .byte 60, 132 # 010C +#endif /* CONFIG_VIDEO_VESA */ + +# Scan for video modes. A bit dirty, but should work. +mode_scan: + movw $0x0100, %cx # Start with mode 0 +scm1: movb $0, %ah # Test the mode + movb %cl, %al + int $0x10 + movb $0x0f, %ah + int $0x10 + cmpb %cl, %al + jnz scm2 # Mode not set + + movw $0x3c0, %dx # Test if it's a text mode + movb $0x10, %al # Mode bits + call inidx + andb $0x03, %al + jnz scm2 + + movb $0xce, %dl # Another set of mode bits + movb $0x06, %al + call inidx + shrb %al + jc scm2 + + movb $0xd4, %dl # Cursor location + movb $0x0f, %al + call inidx + orb %al, %al + jnz scm2 + + movw %cx, %ax # Ok, store the mode + stosw + movb %gs:(0x484), %al # Number of rows + incb %al + stosb + movw %gs:(0x44a), %ax # Number of columns + stosb +scm2: incb %cl + jns scm1 + + movw $0x0003, %ax # Return back to mode 3 + int $0x10 + ret + +tstidx: outw %ax, %dx # OUT DX,AX and inidx +inidx: outb %al, %dx # Read from indexed VGA register + incw %dx # AL=index, DX=index reg port -> AL=data + inb %dx, %al + decw %dx + ret + +# Try to detect type of SVGA card and supply (usually approximate) video +# mode table for it. + +#ifdef CONFIG_VIDEO_SVGA +svga_modes: + leaw svga_table, %si # Test all known SVGA adapters +dosvga: lodsw + movw %ax, %bp # Default mode table + orw %ax, %ax + jz didsv1 + + lodsw # Pointer to test routine + pushw %si + pushw %di + pushw %es + movw $0xc000, %bx + movw %bx, %es + call *%ax # Call test routine + popw %es + popw %di + popw %si + orw %bp, %bp + jz dosvga + + movw %bp, %si # Found, copy the modes + movb svga_prefix, %ah +cpsvga: lodsb + orb %al, %al + jz didsv + + stosw + movsw + jmp cpsvga + +didsv: movw %si, card_name # Store pointer to card name +didsv1: ret + +# Table of all known SVGA cards. For each card, we store a pointer to +# a table of video modes supported by the card and a pointer to a routine +# used for testing of presence of the card. The video mode table is always +# followed by the name of the card or the chipset. +svga_table: + .word ati_md, ati_test + .word oak_md, oak_test + .word paradise_md, paradise_test + .word realtek_md, realtek_test + .word s3_md, s3_test + .word chips_md, chips_test + .word video7_md, video7_test + .word cirrus5_md, cirrus5_test + .word cirrus6_md, cirrus6_test + .word cirrus1_md, cirrus1_test + .word ahead_md, ahead_test + .word everex_md, everex_test + .word genoa_md, genoa_test + .word trident_md, trident_test + .word tseng_md, tseng_test + .word 0 + +# Test routines and mode tables: + +# S3 - The test algorithm was taken from the SuperProbe package +# for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org +s3_test: + movw $0x0f35, %cx # we store some constants in cl/ch + movw $0x03d4, %dx + movb $0x38, %al + call inidx + movb %al, %bh # store current CRT-register 0x38 + movw $0x0038, %ax + call outidx # disable writing to special regs + movb %cl, %al # check whether we can write special reg 0x35 + call inidx + movb %al, %bl # save the current value of CRT reg 0x35 + andb $0xf0, %al # clear bits 0-3 + movb %al, %ah + movb %cl, %al # and write it to CRT reg 0x35 + call outidx + call inidx # now read it back + andb %ch, %al # clear the upper 4 bits + jz s3_2 # the first test failed. But we have a + + movb %bl, %ah # second chance + movb %cl, %al + call outidx + jmp s3_1 # do the other tests + +s3_2: movw %cx, %ax # load ah with 0xf and al with 0x35 + orb %bl, %ah # set the upper 4 bits of ah with the orig value + call outidx # write ... + call inidx # ... and reread + andb %cl, %al # turn off the upper 4 bits + pushw %ax + movb %bl, %ah # restore old value in register 0x35 + movb %cl, %al + call outidx + popw %ax + cmpb %ch, %al # setting lower 4 bits was successful => bad + je no_s3 # writing is allowed => this is not an S3 + +s3_1: movw $0x4838, %ax # allow writing to special regs by putting + call outidx # magic number into CRT-register 0x38 + movb %cl, %al # check whether we can write special reg 0x35 + call inidx + movb %al, %bl + andb $0xf0, %al + movb %al, %ah + movb %cl, %al + call outidx + call inidx + andb %ch, %al + jnz no_s3 # no, we can't write => no S3 + + movw %cx, %ax + orb %bl, %ah + call outidx + call inidx + andb %ch, %al + pushw %ax + movb %bl, %ah # restore old value in register 0x35 + movb %cl, %al + call outidx + popw %ax + cmpb %ch, %al + jne no_s31 # writing not possible => no S3 + movb $0x30, %al + call inidx # now get the S3 id ... + leaw idS3, %di + movw $0x10, %cx + repne + scasb + je no_s31 + + movb %bh, %ah + movb $0x38, %al + jmp s3rest + +no_s3: movb $0x35, %al # restore CRT register 0x35 + movb %bl, %ah + call outidx +no_s31: xorw %bp, %bp # Detection failed +s3rest: movb %bh, %ah + movb $0x38, %al # restore old value of CRT register 0x38 + jmp outidx + +idS3: .byte 0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95 + .byte 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0 + +s3_md: .byte 0x54, 0x2b, 0x84 + .byte 0x55, 0x19, 0x84 + .byte 0 + .ascii "S3" + .byte 0 + +# ATI cards. +ati_test: + leaw idati, %si + movw $0x31, %di + movw $0x09, %cx + repe + cmpsb + je atiok + + xorw %bp, %bp +atiok: ret + +idati: .ascii "761295520" + +ati_md: .byte 0x23, 0x19, 0x84 + .byte 0x33, 0x2c, 0x84 + .byte 0x22, 0x1e, 0x64 + .byte 0x21, 0x19, 0x64 + .byte 0x58, 0x21, 0x50 + .byte 0x5b, 0x1e, 0x50 + .byte 0 + .ascii "ATI" + .byte 0 + +# AHEAD +ahead_test: + movw $0x200f, %ax + movw $0x3ce, %dx + outw %ax, %dx + incw %dx + inb %dx, %al + cmpb $0x20, %al + je isahed + + cmpb $0x21, %al + je isahed + + xorw %bp, %bp +isahed: ret + +ahead_md: + .byte 0x22, 0x2c, 0x84 + .byte 0x23, 0x19, 0x84 + .byte 0x24, 0x1c, 0x84 + .byte 0x2f, 0x32, 0xa0 + .byte 0x32, 0x22, 0x50 + .byte 0x34, 0x42, 0x50 + .byte 0 + .ascii "Ahead" + .byte 0 + +# Chips & Tech. +chips_test: + movw $0x3c3, %dx + inb %dx, %al + orb $0x10, %al + outb %al, %dx + movw $0x104, %dx + inb %dx, %al + movb %al, %bl + movw $0x3c3, %dx + inb %dx, %al + andb $0xef, %al + outb %al, %dx + cmpb $0xa5, %bl + je cantok + + xorw %bp, %bp +cantok: ret + +chips_md: + .byte 0x60, 0x19, 0x84 + .byte 0x61, 0x32, 0x84 + .byte 0 + .ascii "Chips & Technologies" + .byte 0 + +# Cirrus Logic 5X0 +cirrus1_test: + movw $0x3d4, %dx + movb $0x0c, %al + outb %al, %dx + incw %dx + inb %dx, %al + movb %al, %bl + xorb %al, %al + outb %al, %dx + decw %dx + movb $0x1f, %al + outb %al, %dx + incw %dx + inb %dx, %al + movb %al, %bh + xorb %ah, %ah + shlb $4, %al + movw %ax, %cx + movb %bh, %al + shrb $4, %al + addw %ax, %cx + shlw $8, %cx + addw $6, %cx + movw %cx, %ax + movw $0x3c4, %dx + outw %ax, %dx + incw %dx + inb %dx, %al + andb %al, %al + jnz nocirr + + movb %bh, %al + outb %al, %dx + inb %dx, %al + cmpb $0x01, %al + je iscirr + +nocirr: xorw %bp, %bp +iscirr: movw $0x3d4, %dx + movb %bl, %al + xorb %ah, %ah + shlw $8, %ax + addw $0x0c, %ax + outw %ax, %dx + ret + +cirrus1_md: + .byte 0x1f, 0x19, 0x84 + .byte 0x20, 0x2c, 0x84 + .byte 0x22, 0x1e, 0x84 + .byte 0x31, 0x25, 0x64 + .byte 0 + .ascii "Cirrus Logic 5X0" + .byte 0 + +# Cirrus Logic 54XX +cirrus5_test: + movw $0x3c4, %dx + movb $6, %al + call inidx + movb %al, %bl # BL=backup + movw $6, %ax + call tstidx + cmpb $0x0f, %al + jne c5fail + + movw $0x1206, %ax + call tstidx + cmpb $0x12, %al + jne c5fail + + movb $0x1e, %al + call inidx + movb %al, %bh + movb %bh, %ah + andb $0xc0, %ah + movb $0x1e, %al + call tstidx + andb $0x3f, %al + jne c5xx + + movb $0x1e, %al + movb %bh, %ah + orb $0x3f, %ah + call tstidx + xorb $0x3f, %al + andb $0x3f, %al +c5xx: pushf + movb $0x1e, %al + movb %bh, %ah + outw %ax, %dx + popf + je c5done + +c5fail: xorw %bp, %bp +c5done: movb $6, %al + movb %bl, %ah + outw %ax, %dx + ret + +cirrus5_md: + .byte 0x14, 0x19, 0x84 + .byte 0x54, 0x2b, 0x84 + .byte 0 + .ascii "Cirrus Logic 54XX" + .byte 0 + +# Cirrus Logic 64XX -- no known extra modes, but must be identified, because +# it's misidentified by the Ahead test. +cirrus6_test: + movw $0x3ce, %dx + movb $0x0a, %al + call inidx + movb %al, %bl # BL=backup + movw $0xce0a, %ax + call tstidx + orb %al, %al + jne c2fail + + movw $0xec0a, %ax + call tstidx + cmpb $0x01, %al + jne c2fail + + movb $0xaa, %al + call inidx # 4X, 5X, 7X and 8X are valid 64XX chip ID's. + shrb $4, %al + subb $4, %al + jz c6done + + decb %al + jz c6done + + subb $2, %al + jz c6done + + decb %al + jz c6done + +c2fail: xorw %bp, %bp +c6done: movb $0x0a, %al + movb %bl, %ah + outw %ax, %dx + ret + +cirrus6_md: + .byte 0 + .ascii "Cirrus Logic 64XX" + .byte 0 + +# Everex / Trident +everex_test: + movw $0x7000, %ax + xorw %bx, %bx + int $0x10 + cmpb $0x70, %al + jne noevrx + + shrw $4, %dx + cmpw $0x678, %dx + je evtrid + + cmpw $0x236, %dx + jne evrxok + +evtrid: leaw trident_md, %bp +evrxok: ret + +noevrx: xorw %bp, %bp + ret + +everex_md: + .byte 0x03, 0x22, 0x50 + .byte 0x04, 0x3c, 0x50 + .byte 0x07, 0x2b, 0x64 + .byte 0x08, 0x4b, 0x64 + .byte 0x0a, 0x19, 0x84 + .byte 0x0b, 0x2c, 0x84 + .byte 0x16, 0x1e, 0x50 + .byte 0x18, 0x1b, 0x64 + .byte 0x21, 0x40, 0xa0 + .byte 0x40, 0x1e, 0x84 + .byte 0 + .ascii "Everex/Trident" + .byte 0 + +# Genoa. +genoa_test: + leaw idgenoa, %si # Check Genoa 'clues' + xorw %ax, %ax + movb %es:(0x37), %al + movw %ax, %di + movw $0x04, %cx + decw %si + decw %di +l1: incw %si + incw %di + movb (%si), %al + testb %al, %al + jz l2 + + cmpb %es:(%di), %al +l2: loope l1 + orw %cx, %cx + je isgen + + xorw %bp, %bp +isgen: ret + +idgenoa: .byte 0x77, 0x00, 0x99, 0x66 + +genoa_md: + .byte 0x58, 0x20, 0x50 + .byte 0x5a, 0x2a, 0x64 + .byte 0x60, 0x19, 0x84 + .byte 0x61, 0x1d, 0x84 + .byte 0x62, 0x20, 0x84 + .byte 0x63, 0x2c, 0x84 + .byte 0x64, 0x3c, 0x84 + .byte 0x6b, 0x4f, 0x64 + .byte 0x72, 0x3c, 0x50 + .byte 0x74, 0x42, 0x50 + .byte 0x78, 0x4b, 0x64 + .byte 0 + .ascii "Genoa" + .byte 0 + +# OAK +oak_test: + leaw idoakvga, %si + movw $0x08, %di + movw $0x08, %cx + repe + cmpsb + je isoak + + xorw %bp, %bp +isoak: ret + +idoakvga: .ascii "OAK VGA " + +oak_md: .byte 0x4e, 0x3c, 0x50 + .byte 0x4f, 0x3c, 0x84 + .byte 0x50, 0x19, 0x84 + .byte 0x51, 0x2b, 0x84 + .byte 0 + .ascii "OAK" + .byte 0 + +# WD Paradise. +paradise_test: + leaw idparadise, %si + movw $0x7d, %di + movw $0x04, %cx + repe + cmpsb + je ispara + + xorw %bp, %bp +ispara: ret + +idparadise: .ascii "VGA=" + +paradise_md: + .byte 0x41, 0x22, 0x50 + .byte 0x47, 0x1c, 0x84 + .byte 0x55, 0x19, 0x84 + .byte 0x54, 0x2c, 0x84 + .byte 0 + .ascii "Paradise" + .byte 0 + +# Trident. +trident_test: + movw $0x3c4, %dx + movb $0x0e, %al + outb %al, %dx + incw %dx + inb %dx, %al + xchgb %al, %ah + xorb %al, %al + outb %al, %dx + inb %dx, %al + xchgb %ah, %al + movb %al, %bl # Strange thing ... in the book this wasn't + andb $0x02, %bl # necessary but it worked on my card which + jz setb2 # is a trident. Without it the screen goes + # blurred ... + andb $0xfd, %al + jmp clrb2 + +setb2: orb $0x02, %al +clrb2: outb %al, %dx + andb $0x0f, %ah + cmpb $0x02, %ah + je istrid + + xorw %bp, %bp +istrid: ret + +trident_md: + .byte 0x50, 0x1e, 0x50 + .byte 0x51, 0x2b, 0x50 + .byte 0x52, 0x3c, 0x50 + .byte 0x57, 0x19, 0x84 + .byte 0x58, 0x1e, 0x84 + .byte 0x59, 0x2b, 0x84 + .byte 0x5a, 0x3c, 0x84 + .byte 0 + .ascii "Trident" + .byte 0 + +# Tseng. +tseng_test: + movw $0x3cd, %dx + inb %dx, %al # Could things be this simple ! :-) + movb %al, %bl + movb $0x55, %al + outb %al, %dx + inb %dx, %al + movb %al, %ah + movb %bl, %al + outb %al, %dx + cmpb $0x55, %ah + je istsen + +isnot: xorw %bp, %bp +istsen: ret + +tseng_md: + .byte 0x26, 0x3c, 0x50 + .byte 0x2a, 0x28, 0x64 + .byte 0x23, 0x19, 0x84 + .byte 0x24, 0x1c, 0x84 + .byte 0x22, 0x2c, 0x84 + .byte 0x21, 0x3c, 0x84 + .byte 0 + .ascii "Tseng" + .byte 0 + +# Video7. +video7_test: + movw $0x3cc, %dx + inb %dx, %al + movw $0x3b4, %dx + andb $0x01, %al + jz even7 + + movw $0x3d4, %dx +even7: movb $0x0c, %al + outb %al, %dx + incw %dx + inb %dx, %al + movb %al, %bl + movb $0x55, %al + outb %al, %dx + inb %dx, %al + decw %dx + movb $0x1f, %al + outb %al, %dx + incw %dx + inb %dx, %al + movb %al, %bh + decw %dx + movb $0x0c, %al + outb %al, %dx + incw %dx + movb %bl, %al + outb %al, %dx + movb $0x55, %al + xorb $0xea, %al + cmpb %bh, %al + jne isnot + + movb $VIDEO_FIRST_V7>>8, svga_prefix # Use special mode switching + ret + +video7_md: + .byte 0x40, 0x2b, 0x50 + .byte 0x43, 0x3c, 0x50 + .byte 0x44, 0x3c, 0x64 + .byte 0x41, 0x19, 0x84 + .byte 0x42, 0x2c, 0x84 + .byte 0x45, 0x1c, 0x84 + .byte 0 + .ascii "Video 7" + .byte 0 + +# Realtek VGA +realtek_test: + leaw idrtvga, %si + movw $0x45, %di + movw $0x0b, %cx + repe + cmpsb + je isrt + + xorw %bp, %bp +isrt: ret + +idrtvga: .ascii "REALTEK VGA" + +realtek_md: + .byte 0x1a, 0x3c, 0x50 + .byte 0x1b, 0x19, 0x84 + .byte 0x1c, 0x1e, 0x84 + .byte 0x1d, 0x2b, 0x84 + .byte 0x1e, 0x3c, 0x84 + .byte 0 + .ascii "REALTEK" + .byte 0 + +#endif /* CONFIG_VIDEO_SVGA */ + +# User-defined local mode table (VGA only) +#ifdef CONFIG_VIDEO_LOCAL +local_modes: + leaw local_mode_table, %si +locm1: lodsw + orw %ax, %ax + jz locm2 + + stosw + movsw + jmp locm1 + +locm2: ret + +# This is the table of local video modes which can be supplied manually +# by the user. Each entry consists of mode ID (word) and dimensions +# (byte for column count and another byte for row count). These modes +# are placed before all SVGA and VESA modes and override them if table +# compacting is enabled. The table must end with a zero word followed +# by NUL-terminated video adapter name. +local_mode_table: + .word 0x0100 # Example: 40x25 + .byte 25,40 + .word 0 + .ascii "Local" + .byte 0 +#endif /* CONFIG_VIDEO_LOCAL */ + +# Read a key and return the ASCII code in al, scan code in ah +getkey: xorb %ah, %ah + int $0x16 + ret + +# Read a key with a timeout of 30 seconds. +# The hardware clock is used to get the time. +getkt: call gettime + addb $30, %al # Wait 30 seconds + cmpb $60, %al + jl lminute + + subb $60, %al +lminute: + movb %al, %cl +again: movb $0x01, %ah + int $0x16 + jnz getkey # key pressed, so get it + + call gettime + cmpb %cl, %al + jne again + + movb $0x20, %al # timeout, return `space' + ret + +# Flush the keyboard buffer +flush: movb $0x01, %ah + int $0x16 + jz empty + + xorb %ah, %ah + int $0x16 + jmp flush + +empty: ret + +# Print hexadecimal number. +prthw: pushw %ax + movb %ah, %al + call prthb + popw %ax +prthb: pushw %ax + shrb $4, %al + call prthn + popw %ax + andb $0x0f, %al +prthn: cmpb $0x0a, %al + jc prth1 + + addb $0x07, %al +prth1: addb $0x30, %al + jmp prtchr + +# Print decimal number in al +prtdec: pushw %ax + pushw %cx + xorb %ah, %ah + movb $0x0a, %cl + idivb %cl + cmpb $0x09, %al + jbe lt100 + + call prtdec + jmp skip10 + +lt100: addb $0x30, %al + call prtchr +skip10: movb %ah, %al + addb $0x30, %al + call prtchr + popw %cx + popw %ax + ret + +# VIDEO_SELECT-only variables +mt_end: .word 0 # End of video mode table if built +edit_buf: .space 6 # Line editor buffer +card_name: .word 0 # Pointer to adapter name +scanning: .byte 0 # Performing mode scan +do_restore: .byte 0 # Screen contents altered during mode change +svga_prefix: .byte VIDEO_FIRST_BIOS>>8 # Default prefix for BIOS modes +graphic_mode: .byte 0 # Graphic mode with a linear frame buffer + +# Status messages +keymsg: .ascii "Press to see video modes available, " + .ascii " to continue or wait 30 secs" + .byte 0x0d, 0x0a, 0 + +listhdr: .byte 0x0d, 0x0a + .ascii "Mode: COLSxROWS:" + +crlft: .byte 0x0d, 0x0a, 0 + +prompt: .byte 0x0d, 0x0a + .asciz "Enter mode number or `scan': " + +unknt: .asciz "Unknown mode ID. Try again." + +badmdt: .ascii "You passed an undefined mode number." + .byte 0x0d, 0x0a, 0 + +vesaer: .ascii "Error: Scanning of VESA modes failed. Please " + .ascii "report to ." + .byte 0x0d, 0x0a, 0 + +old_name: .asciz "CGA/MDA/HGA" + +ega_name: .asciz "EGA" + +svga_name: .ascii " " + +vga_name: .asciz "VGA" + +vesa_name: .asciz "VESA" + +name_bann: .asciz "Video adapter: " +#endif /* CONFIG_VIDEO_SELECT */ + +# Other variables: +adapter: .byte 0 # Video adapter: 0=CGA/MDA/HGA,1=EGA,2=VGA +video_segment: .word 0xb800 # Video memory segment +force_size: .word 0 # Use this size instead of the one in BIOS vars diff -Nru a/arch/x86_64/config.in b/arch/x86_64/config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/config.in Tue Feb 19 18:09:00 2002 @@ -0,0 +1,240 @@ +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/config-language.txt. +# +mainmenu_name "Linux Kernel Configuration" + +define_bool CONFIG_X86_64 y + +define_bool CONFIG_X86 y +define_bool CONFIG_ISA y +define_bool CONFIG_SBUS n + +define_bool CONFIG_UID16 y +define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n +define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y + + +source init/Config.in + +mainmenu_option next_comment +comment 'Processor type and features' +choice 'Processor family' \ + "Clawhammer CONFIG_MK8" Clawhammer + +# +# Define implied options from the CPU selection here +# +define_int CONFIG_X86_L1_CACHE_BYTES 64 +define_int CONFIG_X86_L1_CACHE_SHIFT 6 +define_bool CONFIG_X86_TSC y +define_bool CONFIG_X86_GOOD_APIC y +define_bool CONFIG_X86_CMPXCHG + +tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR +tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID + +define_bool CONFIG_MATH_EMULATION n +define_bool CONFIG_MCA n +define_bool CONFIG_EISA n + +bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR +bool 'Symmetric multi-processing support' CONFIG_SMP +bool 'Preemptible Kernel' CONFIG_PREEMPT +# currently doesn't boot without hacks. probably simulator bug. +#if [ "$CONFIG_SMP" != "y" ]; then +# bool 'APIC and IO-APIC support on uniprocessors' CONFIG_X86_UP_IOAPIC +# if [ "$CONFIG_X86_UP_IOAPIC" = "y" ]; then +# define_bool CONFIG_X86_IO_APIC y +# define_bool CONFIG_X86_LOCAL_APIC y +# fi +#fi +if [ "$CONFIG_SMP" = "y" -a "$CONFIG_X86_CMPXCHG" = "y" ]; then + define_bool CONFIG_HAVE_DEC_LOCK y +fi +endmenu + +mainmenu_option next_comment +comment 'General options' + +if [ "$CONFIG_SMP" = "y" ]; then + define_bool CONFIG_X86_IO_APIC y + define_bool CONFIG_X86_LOCAL_APIC y +fi +bool 'PCI support' CONFIG_PCI +if [ "$CONFIG_PCI" = "y" ]; then + define_bool CONFIG_PCI_DIRECT y +fi + +source drivers/pci/Config.in + +bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG + +if [ "$CONFIG_HOTPLUG" = "y" ] ; then + source drivers/pcmcia/Config.in +else + define_bool CONFIG_PCMCIA n +fi + +if [ "$CONFIG_PROC_FS" = "y" ]; then + define_bool CONFIG_KCORE_ELF y +fi +# We probably are not going to support a.out, are we? Or should we support a.out in i386 compatibility mode? +#tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC + +bool 'Power Management support' CONFIG_PM + +bool 'IA32 Emulation' CONFIG_IA32_EMULATION + +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_bool ' ACPI support' CONFIG_ACPI $CONFIG_PM + if [ "$CONFIG_ACPI" != "n" ]; then + source drivers/acpi/Config.in + fi +fi + +endmenu + +source drivers/mtd/Config.in + +source drivers/parport/Config.in + +source drivers/pnp/Config.in + +source drivers/block/Config.in + +source drivers/md/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +source drivers/telephony/Config.in + +mainmenu_option next_comment +comment 'ATA/IDE/MFM/RLL support' + +tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE + +if [ "$CONFIG_IDE" != "n" ]; then + source drivers/ide/Config.in +else + define_bool CONFIG_BLK_DEV_IDE_MODES n + define_bool CONFIG_BLK_DEV_HD n +fi +endmenu + +mainmenu_option next_comment +comment 'SCSI support' + +tristate 'SCSI support' CONFIG_SCSI + +if [ "$CONFIG_SCSI" != "n" ]; then + source drivers/scsi/Config.in +fi +endmenu + +source drivers/message/fusion/Config.in + +source drivers/ieee1394/Config.in + +#Currently not 64bit safe +#source drivers/message/i2o/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' + + bool 'Network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then + source drivers/net/Config.in + if [ "$CONFIG_ATM" = "y" ]; then + source drivers/atm/Config.in + fi + fi + endmenu +fi + +source net/ax25/Config.in + +source net/irda/Config.in + +mainmenu_option next_comment +comment 'ISDN subsystem' +if [ "$CONFIG_NET" != "n" ]; then + tristate 'ISDN support' CONFIG_ISDN + if [ "$CONFIG_ISDN" != "n" ]; then + source drivers/isdn/Config.in + fi +fi +endmenu + +mainmenu_option next_comment +comment 'Old CD-ROM drivers (not SCSI, not IDE)' + +bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI +if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/cdrom/Config.in +fi +endmenu + +# +# input before char - char/joystick depends on it. As does USB. +# +source drivers/input/Config.in +source drivers/char/Config.in + +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + source net/bluetooth/Config.in +fi + +source drivers/misc/Config.in + +source drivers/media/Config.in + +source fs/Config.in + +if [ "$CONFIG_VT" = "y" ]; then + mainmenu_option next_comment + comment 'Console drivers' + bool 'VGA text console' CONFIG_VGA_CONSOLE + bool 'Video mode selection support' CONFIG_VIDEO_SELECT + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'MDA text console (dual-headed) (EXPERIMENTAL)' CONFIG_MDA_CONSOLE + source drivers/video/Config.in + fi + endmenu +fi + +mainmenu_option next_comment +comment 'Sound' + +tristate 'Sound card support' CONFIG_SOUND +if [ "$CONFIG_SOUND" != "n" ]; then + source sound/Config.in +fi +endmenu + +source drivers/usb/Config.in + +mainmenu_option next_comment +comment 'Kernel hacking' + +bool 'Kernel debugging' CONFIG_DEBUG_KERNEL +if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then + bool ' Debug memory allocations' CONFIG_DEBUG_SLAB +# bool ' Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT + bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ + bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK +# bool ' Early printk' CONFIG_EARLY_PRINTK + bool ' Additional run-time checks' CONFIG_CHECKING +fi +bool 'Simnow environment (disables time-consuming things)' CONFIG_SIMNOW +#if [ "$CONFIG_SERIAL_CONSOLE" = "y" ]; then +# bool 'Early serial console (ttyS0)' CONFIG_EARLY_SERIAL_CONSOLE +#fi +endmenu + +source lib/Config.in diff -Nru a/arch/x86_64/defconfig b/arch/x86_64/defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/defconfig Tue Feb 19 18:09:01 2002 @@ -0,0 +1,568 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_X86_64=y +CONFIG_X86=y +CONFIG_ISA=y +# CONFIG_SBUS is not set +CONFIG_UID16=y +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General setup +# +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KMOD is not set + +# +# Processor type and features +# +CONFIG_MK8=y +CONFIG_X86_L1_CACHE_BYTES=64 +CONFIG_X86_L1_CACHE_SHIFT=6 +CONFIG_X86_TSC=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +# CONFIG_MATH_EMULATION is not set +# CONFIG_MCA is not set +# CONFIG_EISA is not set +CONFIG_MTRR=y +# CONFIG_SMP is not set +# CONFIG_PREEMPT is not set + +# +# General options +# +CONFIG_PCI=y +CONFIG_PCI_DIRECT=y +# CONFIG_PCI_NAMES is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_PM=y +CONFIG_IA32_EMULATION=y +CONFIG_ACPI=y +CONFIG_ACPI_DEBUG=y +CONFIG_ACPI_BUSMGR=y +CONFIG_ACPI_SYS=y +CONFIG_ACPI_CPU=y +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_AC=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_CMBATT=y +CONFIG_ACPI_THERMAL=y + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_CISS_SCSI_TAPE is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set +# CONFIG_PHONE_IXJ is not set +# CONFIG_PHONE_IXJ_PCMCIA is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_IDEDISK_STROKE is not set +# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set +# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set +# CONFIG_BLK_DEV_IDEDISK_IBM is not set +# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set +# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set +# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set +# CONFIG_BLK_DEV_IDEDISK_WD is not set +# CONFIG_BLK_DEV_COMMERIAL is not set +# CONFIG_BLK_DEV_TIVO is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_DMA_NONPCI is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +# CONFIG_NETDEVICES is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Input device support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +# CONFIG_GAMEPORT_NS558 is not set +# CONFIG_GAMEPORT_L4 is not set +# CONFIG_INPUT_EMU10K1 is not set +# CONFIG_GAMEPORT_PCIGAME is not set +# CONFIG_GAMEPORT_FM801 is not set +# CONFIG_GAMEPORT_CS461x is not set +# CONFIG_SERIO is not set +# CONFIG_SERIO_SERPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +CONFIG_MOUSE=y +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_MWAVE is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set +CONFIG_SIMICSFS=y + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Console drivers +# +CONFIG_VGA_CONSOLE=y +# CONFIG_VIDEO_SELECT is not set +# CONFIG_MDA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set + +# +# SCSI support is needed for USB Storage +# +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# + +# +# Input core support is needed for USB HID +# + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_AUERSWALD is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_CHECKING is not set +CONFIG_SIMNOW=y + +# +# Library routines +# +# CONFIG_CRC32 is not set +# CONFIG_ZLIB_INFLATE is not set +# CONFIG_ZLIB_DEFLATE is not set diff -Nru a/arch/x86_64/ia32/Makefile b/arch/x86_64/ia32/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/ia32/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,18 @@ +# +# Makefile for the ia32 kernel emulation subsystem. +# + +.S.s: + $(CPP) $(AFLAGS) -o $*.s $< +.S.o: + $(CC) $(AFLAGS) -c -o $*.o $< + +all: ia32.o + +O_TARGET := ia32.o +obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_ioctl.o ia32_signal.o ia32_binfmt.o \ + socket32.o ptrace32.o + +clean:: + +include $(TOPDIR)/Rules.make diff -Nru a/arch/x86_64/ia32/ia32_binfmt.c b/arch/x86_64/ia32/ia32_binfmt.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/ia32/ia32_binfmt.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,165 @@ +/* + * Written 2000 by Andi Kleen. + * + * Losely based on the sparc64 and IA64 32bit emulation loaders. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define IA32_EMULATOR 1 + +#define IA32_PAGE_OFFSET 0xE0000000 +#define IA32_STACK_TOP IA32_PAGE_OFFSET +#define ELF_ET_DYN_BASE (IA32_PAGE_OFFSET/3 + 0x1000000) + +#undef ELF_ARCH +#define ELF_ARCH EM_386 + +#undef ELF_CLASS +#define ELF_CLASS ELFCLASS32 + +#define ELF_DATA ELFDATA2LSB +//#define USE_ELF_CORE_DUMP + +#define __ASM_X86_64_ELF_H 1 +#include +#include + +typedef __u32 elf_greg_t; + +typedef elf_greg_t elf_gregset_t[8]; + +/* FIXME -- wrong */ +typedef struct user_i387_ia32_struct elf_fpregset_t; +typedef struct user_i387_struct elf_fpxregset_t; + +#undef elf_check_arch +#define elf_check_arch(x) \ + ((x)->e_machine == EM_386) + +#define ELF_EXEC_PAGESIZE PAGE_SIZE +#define ELF_HWCAP (boot_cpu_data.x86_capability[0]) +#define ELF_PLATFORM ("i686") +#define SET_PERSONALITY(ex, ibcs2) \ +do { \ + set_personality((ibcs2)?PER_SVR4:current->personality); \ +} while (0) + +/* Override some function names */ +#define elf_format elf32_format + +#define init_elf_binfmt init_elf32_binfmt +#define exit_elf_binfmt exit_elf32_binfmt + +#define load_elf_binary load_elf32_binary + +#undef CONFIG_BINFMT_ELF +#ifdef CONFIG_BINFMT_ELF32 +# define CONFIG_BINFMT_ELF CONFIG_BINFMT_ELF32 +#endif + +#undef CONFIG_BINFMT_ELF_MODULE +#ifdef CONFIG_BINFMT_ELF32_MODULE +# define CONFIG_BINFMT_ELF_MODULE CONFIG_BINFMT_ELF32_MODULE +#endif + +#define ELF_PLAT_INIT(r) elf32_init(r) +#define setup_arg_pages(bprm) ia32_setup_arg_pages(bprm) + +#undef start_thread +#define start_thread(regs,new_rip,new_rsp) do { \ + __asm__("movl %0,%%fs": :"r" (0)); \ + __asm__("movl %0,%%es; movl %0,%%ds": :"r" (__USER32_DS)); \ + wrmsrl(MSR_KERNEL_GS_BASE, 0); \ + set_thread_flag(TIF_IA32); \ + (regs)->rip = (new_rip); \ + (regs)->rsp = (new_rsp); \ + (regs)->eflags = 0x200; \ + (regs)->cs = __USER32_CS; \ + (regs)->ss = __USER32_DS; \ + set_fs(USER_DS); \ +} while(0) + + +MODULE_DESCRIPTION("Binary format loader for compatibility with IA32 ELF binaries."); +MODULE_AUTHOR("Eric Youngdale, Andi Kleen"); + +#undef MODULE_DESCRIPTION +#undef MODULE_AUTHOR + +#define elf_addr_t __u32 +#define elf_caddr_t __u32 + +static void elf32_init(struct pt_regs *); + +#include "../../../fs/binfmt_elf.c" + +static void elf32_init(struct pt_regs *regs) +{ + regs->rdi = 0; + regs->rsi = 0; + regs->rdx = 0; + regs->rcx = 0; + regs->rax = 0; + regs->rbx = 0; + regs->rbp = 0; + current->thread.fs = 0; current->thread.gs = 0; + current->thread.fsindex = 0; current->thread.gsindex = 0; + current->thread.ds = __USER_DS; current->thread.es == __USER_DS; +} + +extern void put_dirty_page(struct task_struct * tsk, struct page *page, unsigned long address); + + +int ia32_setup_arg_pages(struct linux_binprm *bprm) +{ + unsigned long stack_base; + struct vm_area_struct *mpnt; + int i; + + stack_base = IA32_STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE; + + bprm->p += stack_base; + if (bprm->loader) + bprm->loader += stack_base; + bprm->exec += stack_base; + + mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!mpnt) + return -ENOMEM; + + down_write(¤t->mm->mmap_sem); + { + mpnt->vm_mm = current->mm; + mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; + mpnt->vm_end = IA32_STACK_TOP; + mpnt->vm_page_prot = PAGE_COPY; + mpnt->vm_flags = VM_STACK_FLAGS; + mpnt->vm_ops = NULL; + mpnt->vm_pgoff = 0; + mpnt->vm_file = NULL; + mpnt->vm_private_data = (void *) 0; + insert_vm_struct(current->mm, mpnt); + current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; + } + + for (i = 0 ; i < MAX_ARG_PAGES ; i++) { + struct page *page = bprm->page[i]; + if (page) { + bprm->page[i] = NULL; + current->mm->rss++; + put_dirty_page(current,page,stack_base); + } + stack_base += PAGE_SIZE; + } + up_write(¤t->mm->mmap_sem); + + return 0; +} + diff -Nru a/arch/x86_64/ia32/ia32_ioctl.c b/arch/x86_64/ia32/ia32_ioctl.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/ia32/ia32_ioctl.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,3843 @@ +/* $Id: ia32_ioctl.c,v 1.2 2001/07/05 06:28:42 ak Exp $ + * ioctl32.c: Conversion between 32bit and 64bit native ioctls. + * + * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 2001 Andi Kleen, SuSE Labs + * + * These routines maintain argument size conversion between 32bit and 64bit + * ioctls. + */ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* Ugh. This header really is not clean */ +#define min min +#define max max +#include +#endif /* LVM */ + +#include +/* Ugly hack. */ +#undef __KERNEL__ +#include +#define __KERNEL__ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define A(__x) ((void *)(unsigned long)(__x)) +#define AA(__x) A(__x) + +/* Aiee. Someone does not find a difference between int and long */ +#define EXT2_IOC32_GETFLAGS _IOR('f', 1, int) +#define EXT2_IOC32_SETFLAGS _IOW('f', 2, int) +#define EXT2_IOC32_GETVERSION _IOR('v', 1, int) +#define EXT2_IOC32_SETVERSION _IOW('v', 2, int) + +extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +static int w_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)arg)) + return -EFAULT; + return err; +} + +static int rw_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + if(get_user(val, (u32 *)arg)) + return -EFAULT; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)arg)) + return -EFAULT; + return err; +} + +static int do_ext2_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* These are just misnamed, they actually get/put from/to user an int */ + switch (cmd) { + case EXT2_IOC32_GETFLAGS: cmd = EXT2_IOC_GETFLAGS; break; + case EXT2_IOC32_SETFLAGS: cmd = EXT2_IOC_SETFLAGS; break; + case EXT2_IOC32_GETVERSION: cmd = EXT2_IOC_GETVERSION; break; + case EXT2_IOC32_SETVERSION: cmd = EXT2_IOC_SETVERSION; break; + } + return sys_ioctl(fd, cmd, arg); +} + +struct video_tuner32 { + s32 tuner; + u8 name[32]; + u32 rangelow, rangehigh; + u32 flags; + u16 mode, signal; +}; + +static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if(get_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __get_user(kp->name[i], &up->name[i]); + __get_user(kp->rangelow, &up->rangelow); + __get_user(kp->rangehigh, &up->rangehigh); + __get_user(kp->flags, &up->flags); + __get_user(kp->mode, &up->mode); + __get_user(kp->signal, &up->signal); + return 0; +} + +static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if(put_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __put_user(kp->name[i], &up->name[i]); + __put_user(kp->rangelow, &up->rangelow); + __put_user(kp->rangehigh, &up->rangehigh); + __put_user(kp->flags, &up->flags); + __put_user(kp->mode, &up->mode); + __put_user(kp->signal, &up->signal); + return 0; +} + +struct video_buffer32 { + /* void * */ u32 base; + s32 height, width, depth, bytesperline; +}; + +static int get_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp; + + if(get_user(tmp, &up->base)) + return -EFAULT; + kp->base = (void *) ((unsigned long)tmp); + __get_user(kp->height, &up->height); + __get_user(kp->width, &up->width); + __get_user(kp->depth, &up->depth); + __get_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp = (u32)((unsigned long)kp->base); + + if(put_user(tmp, &up->base)) + return -EFAULT; + __put_user(kp->height, &up->height); + __put_user(kp->width, &up->width); + __put_user(kp->depth, &up->depth); + __put_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +struct video_clip32 { + s32 x, y, width, height; + /* struct video_clip32 * */ u32 next; +}; + +struct video_window32 { + u32 x, y, width, height, chromakey, flags; + /* struct video_clip32 * */ u32 clips; + s32 clipcount; +}; + +static void free_kvideo_clips(struct video_window *kp) +{ + struct video_clip *cp; + + cp = kp->clips; + if(cp != NULL) + kfree(cp); +} + +static int get_video_window32(struct video_window *kp, struct video_window32 *up) +{ + struct video_clip32 *ucp; + struct video_clip *kcp; + int nclips, err, i; + u32 tmp; + + if(get_user(kp->x, &up->x)) + return -EFAULT; + __get_user(kp->y, &up->y); + __get_user(kp->width, &up->width); + __get_user(kp->height, &up->height); + __get_user(kp->chromakey, &up->chromakey); + __get_user(kp->flags, &up->flags); + __get_user(kp->clipcount, &up->clipcount); + __get_user(tmp, &up->clips); + ucp = (struct video_clip32 *)A(tmp); + kp->clips = NULL; + + nclips = kp->clipcount; + if(nclips == 0) + return 0; + + if(ucp == 0) + return -EINVAL; + + /* Peculiar interface... */ + if(nclips < 0) + nclips = VIDEO_CLIPMAP_SIZE; + + kcp = kmalloc(nclips * sizeof(struct video_clip), GFP_KERNEL); + err = -ENOMEM; + if(kcp == NULL) + goto cleanup_and_err; + + kp->clips = kcp; + for(i = 0; i < nclips; i++) { + __get_user(kcp[i].x, &ucp[i].x); + __get_user(kcp[i].y, &ucp[i].y); + __get_user(kcp[i].width, &ucp[i].width); + __get_user(kcp[i].height, &ucp[i].height); + kcp[nclips].next = NULL; + } + + return 0; + +cleanup_and_err: + free_kvideo_clips(kp); + return err; +} + +/* You get back everything except the clips... */ +static int put_video_window32(struct video_window *kp, struct video_window32 *up) +{ + if(put_user(kp->x, &up->x)) + return -EFAULT; + __put_user(kp->y, &up->y); + __put_user(kp->width, &up->width); + __put_user(kp->height, &up->height); + __put_user(kp->chromakey, &up->chromakey); + __put_user(kp->flags, &up->flags); + __put_user(kp->clipcount, &up->clipcount); + return 0; +} + +#define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32) +#define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32) +#define VIDIOCGWIN32 _IOR('v',9, struct video_window32) +#define VIDIOCSWIN32 _IOW('v',10, struct video_window32) +#define VIDIOCGFBUF32 _IOR('v',11, struct video_buffer32) +#define VIDIOCSFBUF32 _IOW('v',12, struct video_buffer32) +#define VIDIOCGFREQ32 _IOR('v',14, u32) +#define VIDIOCSFREQ32 _IOW('v',15, u32) + +static int do_video_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + union { + struct video_tuner vt; + struct video_buffer vb; + struct video_window vw; + unsigned long vx; + } karg; + mm_segment_t old_fs = get_fs(); + void *up = (void *)arg; + int err = 0; + + /* First, convert the command. */ + switch(cmd) { + case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; + case VIDIOCSWIN32: cmd = VIDIOCSWIN; break; + case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + }; + + switch(cmd) { + case VIDIOCSTUNER: + case VIDIOCGTUNER: + err = get_video_tuner32(&karg.vt, up); + break; + + case VIDIOCSWIN: + err = get_video_window32(&karg.vw, up); + break; + + case VIDIOCSFBUF: + err = get_video_buffer32(&karg.vb, up); + break; + + case VIDIOCSFREQ: + err = get_user(karg.vx, (u32 *)up); + break; + }; + if(err) + goto out; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&karg); + set_fs(old_fs); + + if(cmd == VIDIOCSWIN) + free_kvideo_clips(&karg.vw); + + if(err == 0) { + switch(cmd) { + case VIDIOCGTUNER: + err = put_video_tuner32(&karg.vt, up); + break; + + case VIDIOCGWIN: + err = put_video_window32(&karg.vw, up); + break; + + case VIDIOCGFBUF: + err = put_video_buffer32(&karg.vb, up); + break; + + case VIDIOCGFREQ: + err = put_user(((u32)karg.vx), (u32 *)up); + break; + }; + } +out: + return err; +} + +struct timeval32 { + int tv_sec; + int tv_usec; +}; + +static int do_siocgstamp(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct timeval32 *up = (struct timeval32 *)arg; + struct timeval ktv; + mm_segment_t old_fs = get_fs(); + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&ktv); + set_fs(old_fs); + if(!err) { + err = put_user(ktv.tv_sec, &up->tv_sec); + err |= __put_user(ktv.tv_usec, &up->tv_usec); + } + return err; +} + +struct ifmap32 { + u32 mem_start; + u32 mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +struct ifreq32 { +#define IFHWADDRLEN 6 +#define IFNAMSIZ 16 + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap32 ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + char ifru_newname[IFNAMSIZ]; + __kernel_caddr_t32 ifru_data; + } ifr_ifru; +}; + +struct ifconf32 { + int ifc_len; /* size of buffer */ + __kernel_caddr_t32 ifcbuf; +}; + +static int dev_ifname32(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct net_device *dev; + struct ifreq32 ifr32; + int err; + + if (copy_from_user(&ifr32, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + + dev = dev_get_by_index(ifr32.ifr_ifindex); + if (!dev) + return -ENODEV; + + strncpy(ifr32.ifr_name, dev->name, sizeof(ifr32.ifr_name)-1); + ifr32.ifr_name[sizeof(ifr32.ifr_name)-1] = 0; + + err = copy_to_user((struct ifreq32 *)arg, &ifr32, sizeof(struct ifreq32)); + return (err ? -EFAULT : 0); +} + +static int dev_ifconf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifconf32 ifc32; + struct ifconf ifc; + struct ifreq32 *ifr32; + struct ifreq *ifr; + mm_segment_t old_fs; + unsigned int i, j; + int err; + + if (copy_from_user(&ifc32, (struct ifconf32 *)arg, sizeof(struct ifconf32))) + return -EFAULT; + + if(ifc32.ifcbuf == 0) { + ifc32.ifc_len = 0; + ifc.ifc_len = 0; + ifc.ifc_buf = NULL; + } else { + ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) * + sizeof (struct ifreq); + ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL); + if (!ifc.ifc_buf) + return -ENOMEM; + } + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0; i < ifc32.ifc_len; i += sizeof (struct ifreq32)) { + if (copy_from_user(ifr, ifr32, sizeof (struct ifreq32))) { + kfree (ifc.ifc_buf); + return -EFAULT; + } + ifr++; + ifr32++; + } + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, SIOCGIFCONF, (unsigned long)&ifc); + set_fs (old_fs); + if (!err) { + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0, j = 0; i < ifc32.ifc_len && j < ifc.ifc_len; + i += sizeof (struct ifreq32), j += sizeof (struct ifreq)) { + int k = copy_to_user(ifr32, ifr, sizeof (struct ifreq32)); + ifr32++; + ifr++; + if (k) { + err = -EFAULT; + break; + } + + } + if (!err) { + if (ifc32.ifcbuf == 0) { + /* Translate from 64-bit structure multiple to + * a 32-bit one. + */ + i = ifc.ifc_len; + i = ((i / sizeof(struct ifreq)) * sizeof(struct ifreq32)); + ifc32.ifc_len = i; + } else { + if (i <= ifc32.ifc_len) + ifc32.ifc_len = i; + else + ifc32.ifc_len = i - sizeof (struct ifreq32); + } + if (copy_to_user((struct ifconf32 *)arg, &ifc32, sizeof(struct ifconf32))) + err = -EFAULT; + } + } + if(ifc.ifc_buf != NULL) + kfree (ifc.ifc_buf); + return err; +} + +static int ethtool_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err, len; + u32 data, ethcmd; + + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); + if (!ifr.ifr_data) + return -EAGAIN; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + + if (get_user(ethcmd, (u32 *)A(data))) { + err = -EFAULT; + goto out; + } + switch (ethcmd) { + case ETHTOOL_GDRVINFO: len = sizeof(struct ethtool_drvinfo); break; + case ETHTOOL_GSET: + case ETHTOOL_SSET: + default: len = sizeof(struct ethtool_cmd); break; + } + + if (copy_from_user(ifr.ifr_data, (char *)A(data), len)) { + err = -EFAULT; + goto out; + } + + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + u32 data; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + len = copy_to_user((char *)A(data), ifr.ifr_data, len); + if (len) + err = -EFAULT; + } + +out: + free_page((unsigned long)ifr.ifr_data); + return err; +} + +static int dev_ifsioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err; + + switch (cmd) { + case SIOCSIFMAP: + err = copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(ifr.ifr_name)); + err |= __get_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_start)); + err |= __get_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_end)); + err |= __get_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.base_addr)); + err |= __get_user(ifr.ifr_map.irq, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.irq)); + err |= __get_user(ifr.ifr_map.dma, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.dma)); + err |= __get_user(ifr.ifr_map.port, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.port)); + if (err) + return -EFAULT; + break; + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); + if (!ifr.ifr_data) + return -EAGAIN; + break; + default: + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + break; + } + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + switch (cmd) { + case SIOCGIFFLAGS: + case SIOCGIFMETRIC: + case SIOCGIFMTU: + case SIOCGIFMEM: + case SIOCGIFHWADDR: + case SIOCGIFINDEX: + case SIOCGIFADDR: + case SIOCGIFBRDADDR: + case SIOCGIFDSTADDR: + case SIOCGIFNETMASK: + case SIOCGIFTXQLEN: + if (copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(struct ifreq32))) + return -EFAULT; + break; + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + { + u32 data; + int len; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + if(cmd == SIOCGPPPVER) + len = strlen((char *)ifr.ifr_data) + 1; + else if(cmd == SIOCGPPPCSTATS) + len = sizeof(struct ppp_comp_stats); + else + len = sizeof(struct ppp_stats); + + len = copy_to_user((char *)A(data), ifr.ifr_data, len); + free_page((unsigned long)ifr.ifr_data); + if(len) + return -EFAULT; + break; + } + case SIOCGIFMAP: + err = copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(ifr.ifr_name)); + err |= __put_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_start)); + err |= __put_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_end)); + err |= __put_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.base_addr)); + err |= __put_user(ifr.ifr_map.irq, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.irq)); + err |= __put_user(ifr.ifr_map.dma, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.dma)); + err |= __put_user(ifr.ifr_map.port, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.port)); + if (err) + err = -EFAULT; + break; + } + } else { + switch (cmd) { + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + free_page((unsigned long)ifr.ifr_data); + break; + } + } + return err; +} + +struct rtentry32 { + u32 rt_pad1; + struct sockaddr rt_dst; /* target address */ + struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */ + struct sockaddr rt_genmask; /* target network mask (IP) */ + unsigned short rt_flags; + short rt_pad2; + u32 rt_pad3; + unsigned char rt_tos; + unsigned char rt_class; + short rt_pad4; + short rt_metric; /* +1 for binary compatibility! */ + /* char * */ u32 rt_dev; /* forcing the device at add */ + u32 rt_mtu; /* per route MTU/Window */ + u32 rt_window; /* Window clamping */ + unsigned short rt_irtt; /* Initial RTT */ + +}; + +struct in6_rtmsg32 { + struct in6_addr rtmsg_dst; + struct in6_addr rtmsg_src; + struct in6_addr rtmsg_gateway; + u32 rtmsg_type; + u16 rtmsg_dst_len; + u16 rtmsg_src_len; + u32 rtmsg_metric; + u32 rtmsg_info; + u32 rtmsg_flags; + s32 rtmsg_ifindex; +}; + +extern struct socket *sockfd_lookup(int fd, int *err); + +static int routing_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + int ret; + void *r = NULL; + struct in6_rtmsg r6; + struct rtentry r4; + char devname[16]; + u32 rtdev; + mm_segment_t old_fs = get_fs(); + + struct socket *mysock = sockfd_lookup(fd, &ret); + + if (mysock && mysock->sk && mysock->sk->family == AF_INET6) { /* ipv6 */ + ret = copy_from_user (&r6.rtmsg_dst, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst), + 3 * sizeof(struct in6_addr)); + ret |= __get_user (r6.rtmsg_type, &(((struct in6_rtmsg32 *)arg)->rtmsg_type)); + ret |= __get_user (r6.rtmsg_dst_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst_len)); + ret |= __get_user (r6.rtmsg_src_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_src_len)); + ret |= __get_user (r6.rtmsg_metric, &(((struct in6_rtmsg32 *)arg)->rtmsg_metric)); + ret |= __get_user (r6.rtmsg_info, &(((struct in6_rtmsg32 *)arg)->rtmsg_info)); + ret |= __get_user (r6.rtmsg_flags, &(((struct in6_rtmsg32 *)arg)->rtmsg_flags)); + ret |= __get_user (r6.rtmsg_ifindex, &(((struct in6_rtmsg32 *)arg)->rtmsg_ifindex)); + + r = (void *) &r6; + } else { /* ipv4 */ + ret = copy_from_user (&r4.rt_dst, &(((struct rtentry32 *)arg)->rt_dst), 3 * sizeof(struct sockaddr)); + ret |= __get_user (r4.rt_flags, &(((struct rtentry32 *)arg)->rt_flags)); + ret |= __get_user (r4.rt_metric, &(((struct rtentry32 *)arg)->rt_metric)); + ret |= __get_user (r4.rt_mtu, &(((struct rtentry32 *)arg)->rt_mtu)); + ret |= __get_user (r4.rt_window, &(((struct rtentry32 *)arg)->rt_window)); + ret |= __get_user (r4.rt_irtt, &(((struct rtentry32 *)arg)->rt_irtt)); + ret |= __get_user (rtdev, &(((struct rtentry32 *)arg)->rt_dev)); + if (rtdev) { + ret |= copy_from_user (devname, (char *)A(rtdev), 15); + r4.rt_dev = devname; devname[15] = 0; + } else + r4.rt_dev = 0; + + r = (void *) &r4; + } + + if (ret) + return -EFAULT; + + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, cmd, (long) r); + set_fs (old_fs); + + return ret; +} + +struct hd_geometry32 { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + u32 start; +}; + +static int hdio_getgeo(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct hd_geometry geo; + int err; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, HDIO_GETGEO, (unsigned long)&geo); + set_fs (old_fs); + if (!err) { + err = copy_to_user ((struct hd_geometry32 *)arg, &geo, 4); + err |= __put_user (geo.start, &(((struct hd_geometry32 *)arg)->start)); + } + return err ? -EFAULT : 0; +} + +struct fbcmap32 { + int index; /* first element (0 origin) */ + int count; + u32 red; + u32 green; + u32 blue; +}; + +struct fb_fix_screeninfo32 { + char id[16]; + __kernel_caddr_t32 smem_start; + __u32 smem_len; + __u32 type; + __u32 type_aux; + __u32 visual; + __u16 xpanstep; + __u16 ypanstep; + __u16 ywrapstep; + __u32 line_length; + __kernel_caddr_t32 mmio_start; + __u32 mmio_len; + __u32 accel; + __u16 reserved[3]; +}; + +struct fb_cmap32 { + __u32 start; + __u32 len; + __kernel_caddr_t32 red; + __kernel_caddr_t32 green; + __kernel_caddr_t32 blue; + __kernel_caddr_t32 transp; +}; + +static int fb_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + u32 red = 0, green = 0, blue = 0, transp = 0; + struct fb_fix_screeninfo fix; + struct fb_cmap cmap; + void *karg; + int err = 0; + + memset(&cmap, 0, sizeof(cmap)); + switch (cmd) { + case FBIOGET_FSCREENINFO: + karg = &fix; + break; + case FBIOGETCMAP: + case FBIOPUTCMAP: + karg = &cmap; + err = __get_user(cmap.start, &((struct fb_cmap32 *)arg)->start); + err |= __get_user(cmap.len, &((struct fb_cmap32 *)arg)->len); + err |= __get_user(red, &((struct fb_cmap32 *)arg)->red); + err |= __get_user(green, &((struct fb_cmap32 *)arg)->green); + err |= __get_user(blue, &((struct fb_cmap32 *)arg)->blue); + err |= __get_user(transp, &((struct fb_cmap32 *)arg)->transp); + if (err) { + err = -EFAULT; + goto out; + } + err = -ENOMEM; + cmap.red = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL); + if (!cmap.red) + goto out; + cmap.green = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL); + if (!cmap.green) + goto out; + cmap.blue = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL); + if (!cmap.blue) + goto out; + if (transp) { + cmap.transp = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL); + if (!cmap.transp) + goto out; + } + + if (cmd == FBIOGETCMAP) + break; + + err = __copy_from_user(cmap.red, (char *)A(red), cmap.len * sizeof(__u16)); + err |= __copy_from_user(cmap.green, (char *)A(green), cmap.len * sizeof(__u16)); + err |= __copy_from_user(cmap.blue, (char *)A(blue), cmap.len * sizeof(__u16)); + if (cmap.transp) err |= __copy_from_user(cmap.transp, (char *)A(transp), cmap.len * sizeof(__u16)); + if (err) { + err = -EFAULT; + goto out; + } + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("%s: Unknown fb ioctl cmd fd(%d) " + "cmd(%08x) arg(%08lx)\n", + __FUNCTION__, fd, cmd, arg); + } while(0); + return -ENOSYS; + } + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)karg); + set_fs(old_fs); + if (err) + goto out; + switch (cmd) { + case FBIOGET_FSCREENINFO: + err = __copy_to_user((char *)((struct fb_fix_screeninfo32 *)arg)->id, (char *)fix.id, sizeof(fix.id)); + err |= __put_user((__u32)(unsigned long)fix.smem_start, &((struct fb_fix_screeninfo32 *)arg)->smem_start); + err |= __put_user(fix.smem_len, &((struct fb_fix_screeninfo32 *)arg)->smem_len); + err |= __put_user(fix.type, &((struct fb_fix_screeninfo32 *)arg)->type); + err |= __put_user(fix.type_aux, &((struct fb_fix_screeninfo32 *)arg)->type_aux); + err |= __put_user(fix.visual, &((struct fb_fix_screeninfo32 *)arg)->visual); + err |= __put_user(fix.xpanstep, &((struct fb_fix_screeninfo32 *)arg)->xpanstep); + err |= __put_user(fix.ypanstep, &((struct fb_fix_screeninfo32 *)arg)->ypanstep); + err |= __put_user(fix.ywrapstep, &((struct fb_fix_screeninfo32 *)arg)->ywrapstep); + err |= __put_user(fix.line_length, &((struct fb_fix_screeninfo32 *)arg)->line_length); + err |= __put_user((__u32)(unsigned long)fix.mmio_start, &((struct fb_fix_screeninfo32 *)arg)->mmio_start); + err |= __put_user(fix.mmio_len, &((struct fb_fix_screeninfo32 *)arg)->mmio_len); + err |= __put_user(fix.accel, &((struct fb_fix_screeninfo32 *)arg)->accel); + err |= __copy_to_user((char *)((struct fb_fix_screeninfo32 *)arg)->reserved, (char *)fix.reserved, sizeof(fix.reserved)); + break; + case FBIOGETCMAP: + err = __copy_to_user((char *)A(red), cmap.red, cmap.len * sizeof(__u16)); + err |= __copy_to_user((char *)A(green), cmap.blue, cmap.len * sizeof(__u16)); + err |= __copy_to_user((char *)A(blue), cmap.blue, cmap.len * sizeof(__u16)); + if (cmap.transp) + err |= __copy_to_user((char *)A(transp), cmap.transp, cmap.len * sizeof(__u16)); + break; + case FBIOPUTCMAP: + break; + } + if (err) + err = -EFAULT; + +out: if (cmap.red) kfree(cmap.red); + if (cmap.green) kfree(cmap.green); + if (cmap.blue) kfree(cmap.blue); + if (cmap.transp) kfree(cmap.transp); + return err; +} + +static int hdio_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + unsigned long kval; + unsigned int *uvp; + int error; + + set_fs(KERNEL_DS); + error = sys_ioctl(fd, cmd, (long)&kval); + set_fs(old_fs); + + if(error == 0) { + uvp = (unsigned int *)arg; + if(put_user(kval, uvp)) + error = -EFAULT; + } + return error; +} + +struct floppy_struct32 { + unsigned int size; + unsigned int sect; + unsigned int head; + unsigned int track; + unsigned int stretch; + unsigned char gap; + unsigned char rate; + unsigned char spec1; + unsigned char fmt_gap; + const __kernel_caddr_t32 name; +}; + +struct floppy_drive_params32 { + char cmos; + u32 max_dtr; + u32 hlt; + u32 hut; + u32 srt; + u32 spinup; + u32 spindown; + unsigned char spindown_offset; + unsigned char select_delay; + unsigned char rps; + unsigned char tracks; + u32 timeout; + unsigned char interleave_sect; + struct floppy_max_errors max_errors; + char flags; + char read_track; + short autodetect[8]; + int checkfreq; + int native_format; +}; + +struct floppy_drive_struct32 { + signed char flags; + u32 spinup_date; + u32 select_date; + u32 first_read_date; + short probed_format; + short track; + short maxblock; + short maxtrack; + int generation; + int keep_data; + int fd_ref; + int fd_device; + int last_checked; + __kernel_caddr_t32 dmabuf; + int bufblocks; +}; + +struct floppy_fdc_state32 { + int spec1; + int spec2; + int dtr; + unsigned char version; + unsigned char dor; + u32 address; + unsigned int rawcmd:2; + unsigned int reset:1; + unsigned int need_configure:1; + unsigned int perp_mode:2; + unsigned int has_fifo:1; + unsigned int driver_version; + unsigned char track[4]; +}; + +struct floppy_write_errors32 { + unsigned int write_errors; + u32 first_error_sector; + int first_error_generation; + u32 last_error_sector; + int last_error_generation; + unsigned int badness; +}; + +#define FDSETPRM32 _IOW(2, 0x42, struct floppy_struct32) +#define FDDEFPRM32 _IOW(2, 0x43, struct floppy_struct32) +#define FDGETPRM32 _IOR(2, 0x04, struct floppy_struct32) +#define FDSETDRVPRM32 _IOW(2, 0x90, struct floppy_drive_params32) +#define FDGETDRVPRM32 _IOR(2, 0x11, struct floppy_drive_params32) +#define FDGETDRVSTAT32 _IOR(2, 0x12, struct floppy_drive_struct32) +#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct floppy_drive_struct32) +#define FDGETFDCSTAT32 _IOR(2, 0x15, struct floppy_fdc_state32) +#define FDWERRORGET32 _IOR(2, 0x17, struct floppy_write_errors32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} fd_ioctl_trans_table[] = { + { FDSETPRM32, FDSETPRM }, + { FDDEFPRM32, FDDEFPRM }, + { FDGETPRM32, FDGETPRM }, + { FDSETDRVPRM32, FDSETDRVPRM }, + { FDGETDRVPRM32, FDGETDRVPRM }, + { FDGETDRVSTAT32, FDGETDRVSTAT }, + { FDPOLLDRVSTAT32, FDPOLLDRVSTAT }, + { FDGETFDCSTAT32, FDGETFDCSTAT }, + { FDWERRORGET32, FDWERRORGET } +}; + +#define NR_FD_IOCTL_TRANS (sizeof(fd_ioctl_trans_table)/sizeof(fd_ioctl_trans_table[0])) + +static int fd_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + void *karg = NULL; + unsigned int kcmd = 0; + int i, err; + + for (i = 0; i < NR_FD_IOCTL_TRANS; i++) + if (cmd == fd_ioctl_trans_table[i].cmd32) { + kcmd = fd_ioctl_trans_table[i].cmd; + break; + } + if (!kcmd) + return -EINVAL; + + switch (cmd) { + case FDSETPRM32: + case FDDEFPRM32: + case FDGETPRM32: + { + struct floppy_struct *f; + + f = karg = kmalloc(sizeof(struct floppy_struct), GFP_KERNEL); + if (!karg) + return -ENOMEM; + if (cmd == FDGETPRM32) + break; + err = __get_user(f->size, &((struct floppy_struct32 *)arg)->size); + err |= __get_user(f->sect, &((struct floppy_struct32 *)arg)->sect); + err |= __get_user(f->head, &((struct floppy_struct32 *)arg)->head); + err |= __get_user(f->track, &((struct floppy_struct32 *)arg)->track); + err |= __get_user(f->stretch, &((struct floppy_struct32 *)arg)->stretch); + err |= __get_user(f->gap, &((struct floppy_struct32 *)arg)->gap); + err |= __get_user(f->rate, &((struct floppy_struct32 *)arg)->rate); + err |= __get_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); + err |= __get_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); + err |= __get_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + if (err) { + err = -EFAULT; + goto out; + } + break; + } + case FDSETDRVPRM32: + case FDGETDRVPRM32: + { + struct floppy_drive_params *f; + + f = karg = kmalloc(sizeof(struct floppy_drive_params), GFP_KERNEL); + if (!karg) + return -ENOMEM; + if (cmd == FDGETDRVPRM32) + break; + err = __get_user(f->cmos, &((struct floppy_drive_params32 *)arg)->cmos); + err |= __get_user(f->max_dtr, &((struct floppy_drive_params32 *)arg)->max_dtr); + err |= __get_user(f->hlt, &((struct floppy_drive_params32 *)arg)->hlt); + err |= __get_user(f->hut, &((struct floppy_drive_params32 *)arg)->hut); + err |= __get_user(f->srt, &((struct floppy_drive_params32 *)arg)->srt); + err |= __get_user(f->spinup, &((struct floppy_drive_params32 *)arg)->spinup); + err |= __get_user(f->spindown, &((struct floppy_drive_params32 *)arg)->spindown); + err |= __get_user(f->spindown_offset, &((struct floppy_drive_params32 *)arg)->spindown_offset); + err |= __get_user(f->select_delay, &((struct floppy_drive_params32 *)arg)->select_delay); + err |= __get_user(f->rps, &((struct floppy_drive_params32 *)arg)->rps); + err |= __get_user(f->tracks, &((struct floppy_drive_params32 *)arg)->tracks); + err |= __get_user(f->timeout, &((struct floppy_drive_params32 *)arg)->timeout); + err |= __get_user(f->interleave_sect, &((struct floppy_drive_params32 *)arg)->interleave_sect); + err |= __copy_from_user(&f->max_errors, &((struct floppy_drive_params32 *)arg)->max_errors, sizeof(f->max_errors)); + err |= __get_user(f->flags, &((struct floppy_drive_params32 *)arg)->flags); + err |= __get_user(f->read_track, &((struct floppy_drive_params32 *)arg)->read_track); + err |= __copy_from_user(f->autodetect, ((struct floppy_drive_params32 *)arg)->autodetect, sizeof(f->autodetect)); + err |= __get_user(f->checkfreq, &((struct floppy_drive_params32 *)arg)->checkfreq); + err |= __get_user(f->native_format, &((struct floppy_drive_params32 *)arg)->native_format); + if (err) { + err = -EFAULT; + goto out; + } + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + karg = kmalloc(sizeof(struct floppy_drive_struct), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + case FDGETFDCSTAT32: + karg = kmalloc(sizeof(struct floppy_fdc_state), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + case FDWERRORGET32: + karg = kmalloc(sizeof(struct floppy_write_errors), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + default: + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case FDGETPRM32: + { + struct floppy_struct *f = karg; + + err = __put_user(f->size, &((struct floppy_struct32 *)arg)->size); + err |= __put_user(f->sect, &((struct floppy_struct32 *)arg)->sect); + err |= __put_user(f->head, &((struct floppy_struct32 *)arg)->head); + err |= __put_user(f->track, &((struct floppy_struct32 *)arg)->track); + err |= __put_user(f->stretch, &((struct floppy_struct32 *)arg)->stretch); + err |= __put_user(f->gap, &((struct floppy_struct32 *)arg)->gap); + err |= __put_user(f->rate, &((struct floppy_struct32 *)arg)->rate); + err |= __put_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); + err |= __put_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); + err |= __put_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + break; + } + case FDGETDRVPRM32: + { + struct floppy_drive_params *f = karg; + + err = __put_user(f->cmos, &((struct floppy_drive_params32 *)arg)->cmos); + err |= __put_user(f->max_dtr, &((struct floppy_drive_params32 *)arg)->max_dtr); + err |= __put_user(f->hlt, &((struct floppy_drive_params32 *)arg)->hlt); + err |= __put_user(f->hut, &((struct floppy_drive_params32 *)arg)->hut); + err |= __put_user(f->srt, &((struct floppy_drive_params32 *)arg)->srt); + err |= __put_user(f->spinup, &((struct floppy_drive_params32 *)arg)->spinup); + err |= __put_user(f->spindown, &((struct floppy_drive_params32 *)arg)->spindown); + err |= __put_user(f->spindown_offset, &((struct floppy_drive_params32 *)arg)->spindown_offset); + err |= __put_user(f->select_delay, &((struct floppy_drive_params32 *)arg)->select_delay); + err |= __put_user(f->rps, &((struct floppy_drive_params32 *)arg)->rps); + err |= __put_user(f->tracks, &((struct floppy_drive_params32 *)arg)->tracks); + err |= __put_user(f->timeout, &((struct floppy_drive_params32 *)arg)->timeout); + err |= __put_user(f->interleave_sect, &((struct floppy_drive_params32 *)arg)->interleave_sect); + err |= __copy_to_user(&((struct floppy_drive_params32 *)arg)->max_errors, &f->max_errors, sizeof(f->max_errors)); + err |= __put_user(f->flags, &((struct floppy_drive_params32 *)arg)->flags); + err |= __put_user(f->read_track, &((struct floppy_drive_params32 *)arg)->read_track); + err |= __copy_to_user(((struct floppy_drive_params32 *)arg)->autodetect, f->autodetect, sizeof(f->autodetect)); + err |= __put_user(f->checkfreq, &((struct floppy_drive_params32 *)arg)->checkfreq); + err |= __put_user(f->native_format, &((struct floppy_drive_params32 *)arg)->native_format); + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + { + struct floppy_drive_struct *f = karg; + + err = __put_user(f->flags, &((struct floppy_drive_struct32 *)arg)->flags); + err |= __put_user(f->spinup_date, &((struct floppy_drive_struct32 *)arg)->spinup_date); + err |= __put_user(f->select_date, &((struct floppy_drive_struct32 *)arg)->select_date); + err |= __put_user(f->first_read_date, &((struct floppy_drive_struct32 *)arg)->first_read_date); + err |= __put_user(f->probed_format, &((struct floppy_drive_struct32 *)arg)->probed_format); + err |= __put_user(f->track, &((struct floppy_drive_struct32 *)arg)->track); + err |= __put_user(f->maxblock, &((struct floppy_drive_struct32 *)arg)->maxblock); + err |= __put_user(f->maxtrack, &((struct floppy_drive_struct32 *)arg)->maxtrack); + err |= __put_user(f->generation, &((struct floppy_drive_struct32 *)arg)->generation); + err |= __put_user(f->keep_data, &((struct floppy_drive_struct32 *)arg)->keep_data); + err |= __put_user(f->fd_ref, &((struct floppy_drive_struct32 *)arg)->fd_ref); + err |= __put_user(f->fd_device, &((struct floppy_drive_struct32 *)arg)->fd_device); + err |= __put_user(f->last_checked, &((struct floppy_drive_struct32 *)arg)->last_checked); + err |= __put_user((u64)f->dmabuf, &((struct floppy_drive_struct32 *)arg)->dmabuf); + err |= __put_user((u64)f->bufblocks, &((struct floppy_drive_struct32 *)arg)->bufblocks); + break; + } + case FDGETFDCSTAT32: + { + struct floppy_fdc_state *f = karg; + + err = __put_user(f->spec1, &((struct floppy_fdc_state32 *)arg)->spec1); + err |= __put_user(f->spec2, &((struct floppy_fdc_state32 *)arg)->spec2); + err |= __put_user(f->dtr, &((struct floppy_fdc_state32 *)arg)->dtr); + err |= __put_user(f->version, &((struct floppy_fdc_state32 *)arg)->version); + err |= __put_user(f->dor, &((struct floppy_fdc_state32 *)arg)->dor); + err |= __put_user(f->address, &((struct floppy_fdc_state32 *)arg)->address); + err |= __copy_to_user((char *)&((struct floppy_fdc_state32 *)arg)->address + + sizeof(((struct floppy_fdc_state32 *)arg)->address), + (char *)&f->address + sizeof(f->address), sizeof(int)); + err |= __put_user(f->driver_version, &((struct floppy_fdc_state32 *)arg)->driver_version); + err |= __copy_to_user(((struct floppy_fdc_state32 *)arg)->track, f->track, sizeof(f->track)); + break; + } + case FDWERRORGET32: + { + struct floppy_write_errors *f = karg; + + err = __put_user(f->write_errors, &((struct floppy_write_errors32 *)arg)->write_errors); + err |= __put_user(f->first_error_sector, &((struct floppy_write_errors32 *)arg)->first_error_sector); + err |= __put_user(f->first_error_generation, &((struct floppy_write_errors32 *)arg)->first_error_generation); + err |= __put_user(f->last_error_sector, &((struct floppy_write_errors32 *)arg)->last_error_sector); + err |= __put_user(f->last_error_generation, &((struct floppy_write_errors32 *)arg)->last_error_generation); + err |= __put_user(f->badness, &((struct floppy_write_errors32 *)arg)->badness); + break; + } + default: + break; + } + if (err) + err = -EFAULT; + +out: if (karg) kfree(karg); + return err; +} + +struct ppp_option_data32 { + __kernel_caddr_t32 ptr; + __u32 length; + int transmit; +}; +#define PPPIOCSCOMPRESS32 _IOW('t', 77, struct ppp_option_data32) + +struct ppp_idle32 { + __kernel_time_t32 xmit_idle; + __kernel_time_t32 recv_idle; +}; +#define PPPIOCGIDLE32 _IOR('t', 63, struct ppp_idle32) + +static int ppp_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct ppp_option_data32 data32; + struct ppp_option_data data; + struct ppp_idle32 idle32; + struct ppp_idle idle; + unsigned int kcmd; + void *karg; + int err = 0; + + switch (cmd) { + case PPPIOCGIDLE32: + kcmd = PPPIOCGIDLE; + karg = &idle; + break; + case PPPIOCSCOMPRESS32: + if (copy_from_user(&data32, (struct ppp_option_data32 *)arg, sizeof(struct ppp_option_data32))) + return -EFAULT; + data.ptr = kmalloc (data32.length, GFP_KERNEL); + if (!data.ptr) + return -ENOMEM; + if (copy_from_user(data.ptr, (__u8 *)A(data32.ptr), data32.length)) { + kfree(data.ptr); + return -EFAULT; + } + data.length = data32.length; + data.transmit = data32.transmit; + kcmd = PPPIOCSCOMPRESS; + karg = &data; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("ppp_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + switch (cmd) { + case PPPIOCGIDLE32: + if (err) + return err; + idle32.xmit_idle = idle.xmit_idle; + idle32.recv_idle = idle.recv_idle; + if (copy_to_user((struct ppp_idle32 *)arg, &idle32, sizeof(struct ppp_idle32))) + return -EFAULT; + break; + case PPPIOCSCOMPRESS32: + kfree(data.ptr); + break; + default: + break; + } + return err; +} + + +struct mtget32 { + __u32 mt_type; + __u32 mt_resid; + __u32 mt_dsreg; + __u32 mt_gstat; + __u32 mt_erreg; + __kernel_daddr_t32 mt_fileno; + __kernel_daddr_t32 mt_blkno; +}; +#define MTIOCGET32 _IOR('m', 2, struct mtget32) + +struct mtpos32 { + __u32 mt_blkno; +}; +#define MTIOCPOS32 _IOR('m', 3, struct mtpos32) + +struct mtconfiginfo32 { + __u32 mt_type; + __u32 ifc_type; + __u16 irqnr; + __u16 dmanr; + __u16 port; + __u32 debug; + __u32 have_dens:1; + __u32 have_bsf:1; + __u32 have_fsr:1; + __u32 have_bsr:1; + __u32 have_eod:1; + __u32 have_seek:1; + __u32 have_tell:1; + __u32 have_ras1:1; + __u32 have_ras2:1; + __u32 have_ras3:1; + __u32 have_qfa:1; + __u32 pad1:5; + char reserved[10]; +}; +#define MTIOCGETCONFIG32 _IOR('m', 4, struct mtconfiginfo32) +#define MTIOCSETCONFIG32 _IOW('m', 5, struct mtconfiginfo32) + +static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct mtconfiginfo info; + struct mtget get; + struct mtpos pos; + unsigned long kcmd; + void *karg; + int err = 0; + + switch(cmd) { + case MTIOCPOS32: + kcmd = MTIOCPOS; + karg = &pos; + break; + case MTIOCGET32: + kcmd = MTIOCGET; + karg = &get; + break; + case MTIOCGETCONFIG32: + kcmd = MTIOCGETCONFIG; + karg = &info; + break; + case MTIOCSETCONFIG32: + kcmd = MTIOCSETCONFIG; + karg = &info; + err = __get_user(info.mt_type, &((struct mtconfiginfo32 *)arg)->mt_type); + err |= __get_user(info.ifc_type, &((struct mtconfiginfo32 *)arg)->ifc_type); + err |= __get_user(info.irqnr, &((struct mtconfiginfo32 *)arg)->irqnr); + err |= __get_user(info.dmanr, &((struct mtconfiginfo32 *)arg)->dmanr); + err |= __get_user(info.port, &((struct mtconfiginfo32 *)arg)->port); + err |= __get_user(info.debug, &((struct mtconfiginfo32 *)arg)->debug); + err |= __copy_from_user((char *)&info.debug + sizeof(info.debug), + (char *)&((struct mtconfiginfo32 *)arg)->debug + + sizeof(((struct mtconfiginfo32 *)arg)->debug), sizeof(__u32)); + if (err) + return -EFAULT; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("mt_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + return err; + switch (cmd) { + case MTIOCPOS32: + err = __put_user(pos.mt_blkno, &((struct mtpos32 *)arg)->mt_blkno); + break; + case MTIOCGET32: + err = __put_user(get.mt_type, &((struct mtget32 *)arg)->mt_type); + err |= __put_user(get.mt_resid, &((struct mtget32 *)arg)->mt_resid); + err |= __put_user(get.mt_dsreg, &((struct mtget32 *)arg)->mt_dsreg); + err |= __put_user(get.mt_gstat, &((struct mtget32 *)arg)->mt_gstat); + err |= __put_user(get.mt_erreg, &((struct mtget32 *)arg)->mt_erreg); + err |= __put_user(get.mt_fileno, &((struct mtget32 *)arg)->mt_fileno); + err |= __put_user(get.mt_blkno, &((struct mtget32 *)arg)->mt_blkno); + break; + case MTIOCGETCONFIG32: + err = __put_user(info.mt_type, &((struct mtconfiginfo32 *)arg)->mt_type); + err |= __put_user(info.ifc_type, &((struct mtconfiginfo32 *)arg)->ifc_type); + err |= __put_user(info.irqnr, &((struct mtconfiginfo32 *)arg)->irqnr); + err |= __put_user(info.dmanr, &((struct mtconfiginfo32 *)arg)->dmanr); + err |= __put_user(info.port, &((struct mtconfiginfo32 *)arg)->port); + err |= __put_user(info.debug, &((struct mtconfiginfo32 *)arg)->debug); + err |= __copy_to_user((char *)&((struct mtconfiginfo32 *)arg)->debug + + sizeof(((struct mtconfiginfo32 *)arg)->debug), + (char *)&info.debug + sizeof(info.debug), sizeof(__u32)); + break; + case MTIOCSETCONFIG32: + break; + } + return err ? -EFAULT: 0; +} + +struct cdrom_read32 { + int cdread_lba; + __kernel_caddr_t32 cdread_bufaddr; + int cdread_buflen; +}; + +struct cdrom_read_audio32 { + union cdrom_addr addr; + u_char addr_format; + int nframes; + __kernel_caddr_t32 buf; +}; + +struct cdrom_generic_command32 { + unsigned char cmd[CDROM_PACKET_SIZE]; + __kernel_caddr_t32 buffer; + unsigned int buflen; + int stat; + __kernel_caddr_t32 sense; + __kernel_caddr_t32 reserved[3]; +}; + +static int cdrom_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct cdrom_read cdread; + struct cdrom_read_audio cdreadaudio; + struct cdrom_generic_command cgc; + __kernel_caddr_t32 addr; + char *data = 0; + void *karg; + int err = 0; + + switch(cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + karg = &cdread; + err = __get_user(cdread.cdread_lba, &((struct cdrom_read32 *)arg)->cdread_lba); + err |= __get_user(addr, &((struct cdrom_read32 *)arg)->cdread_bufaddr); + err |= __get_user(cdread.cdread_buflen, &((struct cdrom_read32 *)arg)->cdread_buflen); + if (err) + return -EFAULT; + data = kmalloc(cdread.cdread_buflen, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdread.cdread_bufaddr = data; + break; + case CDROMREADAUDIO: + karg = &cdreadaudio; + err = copy_from_user(&cdreadaudio.addr, &((struct cdrom_read_audio32 *)arg)->addr, sizeof(cdreadaudio.addr)); + err |= __get_user(cdreadaudio.addr_format, &((struct cdrom_read_audio32 *)arg)->addr_format); + err |= __get_user(cdreadaudio.nframes, &((struct cdrom_read_audio32 *)arg)->nframes); + err |= __get_user(addr, &((struct cdrom_read_audio32 *)arg)->buf); + if (err) + return -EFAULT; + data = kmalloc(cdreadaudio.nframes * 2352, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdreadaudio.buf = data; + break; + case CDROM_SEND_PACKET: + karg = &cgc; + err = copy_from_user(cgc.cmd, &((struct cdrom_generic_command32 *)arg)->cmd, sizeof(cgc.cmd)); + err |= __get_user(addr, &((struct cdrom_generic_command32 *)arg)->buffer); + err |= __get_user(cgc.buflen, &((struct cdrom_generic_command32 *)arg)->buflen); + if (err) + return -EFAULT; + if ((data = kmalloc(cgc.buflen, GFP_KERNEL)) == NULL) + return -ENOMEM; + cgc.buffer = data; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("cdrom_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + err = copy_to_user((char *)A(addr), data, cdread.cdread_buflen); + break; + case CDROMREADAUDIO: + err = copy_to_user((char *)A(addr), data, cdreadaudio.nframes * 2352); + break; + case CDROM_SEND_PACKET: + err = copy_to_user((char *)A(addr), data, cgc.buflen); + break; + default: + break; + } +out: if (data) + kfree(data); + return err ? -EFAULT : 0; +} + +struct loop_info32 { + int lo_number; /* ioctl r/o */ + __kernel_dev_t32 lo_device; /* ioctl r/o */ + unsigned int lo_inode; /* ioctl r/o */ + __kernel_dev_t32 lo_rdevice; /* ioctl r/o */ + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; /* ioctl w/o */ + int lo_flags; /* ioctl r/o */ + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ + unsigned int lo_init[2]; + char reserved[4]; +}; + +static int loop_status(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct loop_info l; + int err = -EINVAL; + + switch(cmd) { + case LOOP_SET_STATUS: + err = get_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __get_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __get_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __get_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_from_user((char *)&l.lo_offset, (char *)&((struct loop_info32 *)arg)->lo_offset, + 8 + (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) { + err = -EFAULT; + } else { + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + } + break; + case LOOP_GET_STATUS: + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + if (!err) { + err = put_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __put_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __put_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __put_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_to_user((char *)&((struct loop_info32 *)arg)->lo_offset, + (char *)&l.lo_offset, (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) + err = -EFAULT; + } + break; + default: { + static int count = 0; + if (++count <= 20) + printk("%s: Unknown loop ioctl cmd, fd(%d) " + "cmd(%08x) arg(%08lx)\n", + __FUNCTION__, fd, cmd, arg); + } + } + return err; +} + +extern int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); + +static int vt_check(struct file *file) +{ + struct tty_struct *tty; + struct inode *inode = file->f_dentry->d_inode; + + if (file->f_op->ioctl != tty_ioctl) + return -EINVAL; + + tty = (struct tty_struct *)file->private_data; + if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) + return -EINVAL; + + if (tty->driver.ioctl != vt_ioctl) + return -EINVAL; + + /* + * To have permissions to do most of the vt ioctls, we either have + * to be the owner of the tty, or super-user. + */ + if (current->tty == tty || suser()) + return 1; + return 0; +} + +struct consolefontdesc32 { + unsigned short charcount; /* characters in font (256 or 512) */ + unsigned short charheight; /* scan lines per character (1-32) */ + u32 chardata; /* font data in expanded form */ +}; + +static int do_fontx_ioctl(unsigned int fd, int cmd, struct consolefontdesc32 *user_cfd, struct file *file) +{ + struct consolefontdesc cfdarg; + struct console_font_op op; + int i, perm; + + perm = vt_check(file); + if (perm < 0) return perm; + + if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc32))) + return -EFAULT; + + cfdarg.chardata = (unsigned char *)A(((struct consolefontdesc32 *)&cfdarg)->chardata); + + switch (cmd) { + case PIO_FONTX: + if (!perm) + return -EPERM; + op.op = KD_FONT_OP_SET; + op.flags = 0; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + return con_font_op(fg_console, &op); + case GIO_FONTX: + if (!cfdarg.chardata) + return 0; + op.op = KD_FONT_OP_GET; + op.flags = 0; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + i = con_font_op(fg_console, &op); + if (i) + return i; + cfdarg.charheight = op.height; + cfdarg.charcount = op.charcount; + ((struct consolefontdesc32 *)&cfdarg)->chardata = (unsigned long)cfdarg.chardata; + if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc32))) + return -EFAULT; + return 0; + } + return -EINVAL; +} + +struct console_font_op32 { + unsigned int op; /* operation code KD_FONT_OP_* */ + unsigned int flags; /* KD_FONT_FLAG_* */ + unsigned int width, height; /* font size */ + unsigned int charcount; + u32 data; /* font data with height fixed to 32 */ +}; + +static int do_kdfontop_ioctl(unsigned int fd, unsigned int cmd, struct console_font_op32 *fontop, struct file *file) +{ + struct console_font_op op; + int perm = vt_check(file), i; + struct vt_struct *vt; + + if (perm < 0) return perm; + + if (copy_from_user(&op, (void *) fontop, sizeof(struct console_font_op32))) + return -EFAULT; + if (!perm && op.op != KD_FONT_OP_GET) + return -EPERM; + op.data = (unsigned char *)A(((struct console_font_op32 *)&op)->data); + op.flags |= KD_FONT_FLAG_OLD; + vt = (struct vt_struct *)((struct tty_struct *)file->private_data)->driver_data; + i = con_font_op(vt->vc_num, &op); + if (i) return i; + ((struct console_font_op32 *)&op)->data = (unsigned long)op.data; + if (copy_to_user((void *) fontop, &op, sizeof(struct console_font_op32))) + return -EFAULT; + return 0; +} + +struct unimapdesc32 { + unsigned short entry_ct; + u32 entries; +}; + +static int do_unimap_ioctl(unsigned int fd, unsigned int cmd, struct unimapdesc32 *user_ud, struct file *file) +{ + struct unimapdesc32 tmp; + int perm = vt_check(file); + + if (perm < 0) return perm; + if (copy_from_user(&tmp, user_ud, sizeof tmp)) + return -EFAULT; + switch (cmd) { + case PIO_UNIMAP: + if (!perm) return -EPERM; + return con_set_unimap(fg_console, tmp.entry_ct, (struct unipair *)A(tmp.entries)); + case GIO_UNIMAP: + return con_get_unimap(fg_console, tmp.entry_ct, &(user_ud->entry_ct), (struct unipair *)A(tmp.entries)); + } + return 0; +} + +static int do_smb_getmountuid(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + __kernel_uid_t kuid; + int err; + + cmd = SMB_IOC_GETMOUNTUID; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&kuid); + set_fs(old_fs); + + if (err >= 0) + err = put_user(kuid, (__kernel_uid_t32 *)arg); + + return err; +} + +struct atmif_sioc32 { + int number; + int length; + __kernel_caddr_t32 arg; +}; + +struct atm_iobuf32 { + int length; + __kernel_caddr_t32 buffer; +}; + +#define ATM_GETLINKRATE32 _IOW('a', ATMIOC_ITF+1, struct atmif_sioc32) +#define ATM_GETNAMES32 _IOW('a', ATMIOC_ITF+3, struct atm_iobuf32) +#define ATM_GETTYPE32 _IOW('a', ATMIOC_ITF+4, struct atmif_sioc32) +#define ATM_GETESI32 _IOW('a', ATMIOC_ITF+5, struct atmif_sioc32) +#define ATM_GETADDR32 _IOW('a', ATMIOC_ITF+6, struct atmif_sioc32) +#define ATM_RSTADDR32 _IOW('a', ATMIOC_ITF+7, struct atmif_sioc32) +#define ATM_ADDADDR32 _IOW('a', ATMIOC_ITF+8, struct atmif_sioc32) +#define ATM_DELADDR32 _IOW('a', ATMIOC_ITF+9, struct atmif_sioc32) +#define ATM_GETCIRANGE32 _IOW('a', ATMIOC_ITF+10, struct atmif_sioc32) +#define ATM_SETCIRANGE32 _IOW('a', ATMIOC_ITF+11, struct atmif_sioc32) +#define ATM_SETESI32 _IOW('a', ATMIOC_ITF+12, struct atmif_sioc32) +#define ATM_SETESIF32 _IOW('a', ATMIOC_ITF+13, struct atmif_sioc32) +#define ATM_GETSTAT32 _IOW('a', ATMIOC_SARCOM+0, struct atmif_sioc32) +#define ATM_GETSTATZ32 _IOW('a', ATMIOC_SARCOM+1, struct atmif_sioc32) +#define ATM_GETLOOP32 _IOW('a', ATMIOC_SARCOM+2, struct atmif_sioc32) +#define ATM_SETLOOP32 _IOW('a', ATMIOC_SARCOM+3, struct atmif_sioc32) +#define ATM_QUERYLOOP32 _IOW('a', ATMIOC_SARCOM+4, struct atmif_sioc32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} atm_ioctl_map[] = { + { ATM_GETLINKRATE32, ATM_GETLINKRATE }, + { ATM_GETNAMES32, ATM_GETNAMES }, + { ATM_GETTYPE32, ATM_GETTYPE }, + { ATM_GETESI32, ATM_GETESI }, + { ATM_GETADDR32, ATM_GETADDR }, + { ATM_RSTADDR32, ATM_RSTADDR }, + { ATM_ADDADDR32, ATM_ADDADDR }, + { ATM_DELADDR32, ATM_DELADDR }, + { ATM_GETCIRANGE32, ATM_GETCIRANGE }, + { ATM_SETCIRANGE32, ATM_SETCIRANGE }, + { ATM_SETESI32, ATM_SETESI }, + { ATM_SETESIF32, ATM_SETESIF }, + { ATM_GETSTAT32, ATM_GETSTAT }, + { ATM_GETSTATZ32, ATM_GETSTATZ }, + { ATM_GETLOOP32, ATM_GETLOOP }, + { ATM_SETLOOP32, ATM_SETLOOP }, + { ATM_QUERYLOOP32, ATM_QUERYLOOP } +}; + +#define NR_ATM_IOCTL (sizeof(atm_ioctl_map)/sizeof(atm_ioctl_map[0])) + + +static int do_atm_iobuf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atm_iobuf32 iobuf32; + struct atm_iobuf iobuf = { 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&iobuf32, (struct atm_iobuf32*)arg, + sizeof(struct atm_iobuf32)); + if (err) + return -EFAULT; + + iobuf.length = iobuf32.length; + + if (iobuf32.buffer == (__kernel_caddr_t32) NULL || iobuf32.length == 0) { + iobuf.buffer = (void*)(unsigned long)iobuf32.buffer; + } else { + iobuf.buffer = kmalloc(iobuf.length, GFP_KERNEL); + if (iobuf.buffer == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(iobuf.buffer, A(iobuf32.buffer), iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&iobuf); + set_fs (old_fs); + if(err) + goto out; + + if(iobuf.buffer && iobuf.length > 0) { + err = copy_to_user(A(iobuf32.buffer), iobuf.buffer, iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(iobuf.length, &(((struct atm_iobuf32*)arg)->length)); + + out: + if(iobuf32.buffer && iobuf32.length > 0) + kfree(iobuf.buffer); + + return err; +} + + +static int do_atmif_sioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atmif_sioc32 sioc32; + struct atmif_sioc sioc = { 0, 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&sioc32, (struct atmif_sioc32*)arg, + sizeof(struct atmif_sioc32)); + if (err) + return -EFAULT; + + sioc.number = sioc32.number; + sioc.length = sioc32.length; + + if (sioc32.arg == (__kernel_caddr_t32) NULL || sioc32.length == 0) { + sioc.arg = (void*)(unsigned long)sioc32.arg; + } else { + sioc.arg = kmalloc(sioc.length, GFP_KERNEL); + if (sioc.arg == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(sioc.arg, A(sioc32.arg), sioc32.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&sioc); + set_fs (old_fs); + if(err) { + goto out; + } + + if(sioc.arg && sioc.length > 0) { + err = copy_to_user(A(sioc32.arg), sioc.arg, sioc.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(sioc.length, &(((struct atmif_sioc32*)arg)->length)); + + out: + if(sioc32.arg && sioc32.length > 0) + kfree(sioc.arg); + + return err; +} + + +static int do_atm_ioctl(unsigned int fd, unsigned int cmd32, unsigned long arg) +{ + int i; + unsigned int cmd = 0; + + switch (cmd32) { + case SONET_GETSTAT: + case SONET_GETSTATZ: + case SONET_GETDIAG: + case SONET_SETDIAG: + case SONET_CLRDIAG: + case SONET_SETFRAMING: + case SONET_GETFRAMING: + case SONET_GETFRSENSE: + return do_atmif_sioc(fd, cmd32, arg); + } + + for (i = 0; i < NR_ATM_IOCTL; i++) { + if (cmd32 == atm_ioctl_map[i].cmd32) { + cmd = atm_ioctl_map[i].cmd; + break; + } + } + if (i == NR_ATM_IOCTL) { + return -EINVAL; + } + + switch (cmd) { + case ATM_GETNAMES: + return do_atm_iobuf(fd, cmd, arg); + + case ATM_GETLINKRATE: + case ATM_GETTYPE: + case ATM_GETESI: + case ATM_GETADDR: + case ATM_RSTADDR: + case ATM_ADDADDR: + case ATM_DELADDR: + case ATM_GETCIRANGE: + case ATM_SETCIRANGE: + case ATM_SETESI: + case ATM_SETESIF: + case ATM_GETSTAT: + case ATM_GETSTATZ: + case ATM_GETLOOP: + case ATM_SETLOOP: + case ATM_QUERYLOOP: + return do_atmif_sioc(fd, cmd, arg); + } + + return -EINVAL; +} + +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* Ugh, LVM. Pitty it was not cleaned up before accepted :((. */ +typedef struct { + uint8_t vg_name[NAME_LEN]; + uint32_t vg_number; + uint32_t vg_access; + uint32_t vg_status; + uint32_t lv_max; + uint32_t lv_cur; + uint32_t lv_open; + uint32_t pv_max; + uint32_t pv_cur; + uint32_t pv_act; + uint32_t dummy; + uint32_t vgda; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pvg_total; + u32 proc; + u32 pv[ABS_MAX_PV + 1]; + u32 lv[ABS_MAX_LV + 1]; + uint8_t vg_uuid[UUID_LEN+1]; /* volume group UUID */ +} vg32_t; + +typedef struct { + uint8_t id[2]; + uint16_t version; + lvm_disk_data_t pv_on_disk; + lvm_disk_data_t vg_on_disk; + lvm_disk_data_t pv_namelist_on_disk; + lvm_disk_data_t lv_on_disk; + lvm_disk_data_t pe_on_disk; + uint8_t pv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint8_t system_id[NAME_LEN]; + kdev_t pv_dev; + uint32_t pv_number; + uint32_t pv_status; + uint32_t pv_allocatable; + uint32_t pv_size; + uint32_t lv_cur; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pe_stale; + u32 pe; + u32 inode; + uint8_t pv_uuid[UUID_LEN+1]; +} pv32_t; + +typedef struct { + char lv_name[NAME_LEN]; + u32 lv; +} lv_req32_t; + +typedef struct { + u32 lv_index; + u32 lv; + /* Transfer size because user space and kernel space differ */ + uint16_t size; +} lv_status_byindex_req32_t; + +typedef struct { + dev_t dev; + u32 lv; +} lv_status_bydev_req32_t; + +typedef struct { + uint8_t lv_name[NAME_LEN]; + kdev_t old_dev; + kdev_t new_dev; + u32 old_pe; + u32 new_pe; +} le_remap_req32_t; + +typedef struct { + char pv_name[NAME_LEN]; + u32 pv; +} pv_status_req32_t; + +typedef struct { + uint8_t lv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint32_t lv_access; + uint32_t lv_status; + uint32_t lv_open; + kdev_t lv_dev; + uint32_t lv_number; + uint32_t lv_mirror_copies; + uint32_t lv_recovery; + uint32_t lv_schedule; + uint32_t lv_size; + u32 lv_current_pe; + uint32_t lv_current_le; + uint32_t lv_allocated_le; + uint32_t lv_stripes; + uint32_t lv_stripesize; + uint32_t lv_badblock; + uint32_t lv_allocation; + uint32_t lv_io_timeout; + uint32_t lv_read_ahead; + /* delta to version 1 starts here */ + u32 lv_snapshot_org; + u32 lv_snapshot_prev; + u32 lv_snapshot_next; + u32 lv_block_exception; + uint32_t lv_remap_ptr; + uint32_t lv_remap_end; + uint32_t lv_chunk_size; + uint32_t lv_snapshot_minor; + char dummy[200]; +} lv32_t; + +typedef struct { + u32 hash[2]; + u32 rsector_org; + kdev_t rdev_org; + u32 rsector_new; + kdev_t rdev_new; +} lv_block_exception32_t; + +static void put_lv_t(lv_t *l) +{ + if (l->lv_current_pe) vfree(l->lv_current_pe); + if (l->lv_block_exception) vfree(l->lv_block_exception); + kfree(l); +} + +static lv_t *get_lv_t(u32 p, int *errp) +{ + int err, i; + u32 ptr1, ptr2; + size_t size; + lv_block_exception32_t *lbe32; + lv_block_exception_t *lbe; + lv32_t *ul = (lv32_t *)A(p); + lv_t *l = (lv_t *)kmalloc(sizeof(lv_t), GFP_KERNEL); + if (!l) { + *errp = -ENOMEM; + return NULL; + } + memset(l, 0, sizeof(lv_t)); + err = copy_from_user(l, ul, (long)&((lv32_t *)0)->lv_current_pe); + err |= __copy_from_user(&l->lv_current_le, &ul->lv_current_le, + ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le)); + err |= __copy_from_user(&l->lv_remap_ptr, &ul->lv_remap_ptr, + ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); + err |= __get_user(ptr1, &ul->lv_current_pe); + err |= __get_user(ptr2, &ul->lv_block_exception); + if (err) { + kfree(l); + *errp = -EFAULT; + return NULL; + } + if (ptr1) { + size = l->lv_allocated_le * sizeof(pe_t); + l->lv_current_pe = vmalloc(size); + if (l->lv_current_pe) + err = copy_from_user(l->lv_current_pe, (void *)A(ptr1), size); + } + if (!err && ptr2) { + size = l->lv_remap_end * sizeof(lv_block_exception_t); + l->lv_block_exception = lbe = vmalloc(size); + if (l->lv_block_exception) { + lbe32 = (lv_block_exception32_t *)A(ptr2); + memset(lbe, 0, size); + for (i = 0; i < l->lv_remap_end; i++, lbe++, lbe32++) { + err |= get_user(lbe->rsector_org, &lbe32->rsector_org); + err |= __get_user(lbe->rdev_org, &lbe32->rdev_org); + err |= __get_user(lbe->rsector_new, &lbe32->rsector_new); + err |= __get_user(lbe->rdev_new, &lbe32->rdev_new); + + } + } + } + if (err || (ptr1 && !l->lv_current_pe) || (ptr2 && !l->lv_block_exception)) { + if (!err) + *errp = -ENOMEM; + else + *errp = -EFAULT; + put_lv_t(l); + return NULL; + } + return l; +} + +static int copy_lv_t(u32 ptr, lv_t *l) +{ + int err; + lv32_t *ul = (lv32_t *)A(ptr); + u32 ptr1; + size_t size; + + err = get_user(ptr1, &ul->lv_current_pe); + if (err) + return -EFAULT; + err = copy_to_user(ul, l, (long)&((lv32_t *)0)->lv_current_pe); + err |= __copy_to_user(&ul->lv_current_le, &l->lv_current_le, + ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le)); + err |= __copy_to_user(&ul->lv_remap_ptr, &l->lv_remap_ptr, + ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); + size = l->lv_allocated_le * sizeof(pe_t); + if (ptr1) + err |= __copy_to_user((void *)A(ptr1), l->lv_current_pe, size); + return err ? -EFAULT : 0; +} + +static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + vg_t *v; + union { + lv_req_t lv_req; + le_remap_req_t le_remap; + lv_status_byindex_req_t lv_byindex; + lv_status_bydev_req_t lv_bydev; + pv_status_req_t pv_status; + } u; + pv_t p; + int err; + u32 ptr = 0; + int i; + mm_segment_t old_fs; + void *karg = &u; + + switch (cmd) { + case VG_STATUS: + v = kmalloc(sizeof(vg_t), GFP_KERNEL); + if (!v) return -ENOMEM; + karg = v; + break; + case VG_CREATE: + v = kmalloc(sizeof(vg_t), GFP_KERNEL); + if (!v) return -ENOMEM; + if (copy_from_user(v, (void *)arg, (long)&((vg32_t *)0)->proc) || + __get_user(v->proc, &((vg32_t *)arg)->proc)) { + kfree(v); + return -EFAULT; + } + if (copy_from_user(v->vg_uuid, ((vg32_t *)arg)->vg_uuid, UUID_LEN+1)) { + kfree(v); + return -EFAULT; + } + + karg = v; + memset(v->pv, 0, sizeof(v->pv) + sizeof(v->lv)); + if (v->pv_max > ABS_MAX_PV || v->lv_max > ABS_MAX_LV) + return -EPERM; + for (i = 0; i < v->pv_max; i++) { + err = __get_user(ptr, &((vg32_t *)arg)->pv[i]); + if (err) break; + if (ptr) { + v->pv[i] = kmalloc(sizeof(pv_t), GFP_KERNEL); + if (!v->pv[i]) { + err = -ENOMEM; + break; + } + err = copy_from_user(v->pv[i], (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + err = copy_from_user(v->pv[i]->pv_uuid, ((pv32_t *)A(ptr))->pv_uuid, UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + + + v->pv[i]->pe = NULL; v->pv[i]->inode = NULL; + } + } + if (!err) { + for (i = 0; i < v->lv_max; i++) { + err = __get_user(ptr, &((vg32_t *)arg)->lv[i]); + if (err) break; + if (ptr) { + v->lv[i] = get_lv_t(ptr, &err); + if (err) break; + } + } + } + break; + case LV_CREATE: + case LV_EXTEND: + case LV_REDUCE: + case LV_REMOVE: + case LV_RENAME: + case LV_STATUS_BYNAME: + err = copy_from_user(&u.pv_status, arg, sizeof(u.pv_status.pv_name)); + if (err) return -EFAULT; + if (cmd != LV_REMOVE) { + err = __get_user(ptr, &((lv_req32_t *)arg)->lv); + if (err) return err; + u.lv_req.lv = get_lv_t(ptr, &err); + } else + u.lv_req.lv = NULL; + break; + + + case LV_STATUS_BYINDEX: + err = get_user(u.lv_byindex.lv_index, &((lv_status_byindex_req32_t *)arg)->lv_index); + err |= __get_user(ptr, &((lv_status_byindex_req32_t *)arg)->lv); + if (err) return err; + u.lv_byindex.lv = get_lv_t(ptr, &err); + break; + case LV_STATUS_BYDEV: + err = get_user(u.lv_bydev.dev, &((lv_status_bydev_req32_t *)arg)->dev); + u.lv_bydev.lv = get_lv_t(ptr, &err); + if (err) return err; + u.lv_bydev.lv = &p; + p.pe = NULL; p.inode = NULL; + break; + case VG_EXTEND: + err = copy_from_user(&p, (void *)arg, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + err = copy_from_user(p.pv_uuid, ((pv32_t *)arg)->pv_uuid, UUID_LEN+1); + if (err) return -EFAULT; + p.pe = NULL; p.inode = NULL; + karg = &p; + break; + case PV_CHANGE: + case PV_STATUS: + err = copy_from_user(&u.pv_status, arg, sizeof(u.lv_req.lv_name)); + if (err) return -EFAULT; + err = __get_user(ptr, &((pv_status_req32_t *)arg)->pv); + if (err) return err; + u.pv_status.pv = &p; + if (cmd == PV_CHANGE) { + err = copy_from_user(&p, (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + p.pe = NULL; p.inode = NULL; + } + break; + } + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + switch (cmd) { + case VG_STATUS: + if (!err) { + if (copy_to_user((void *)arg, v, (long)&((vg32_t *)0)->proc) || + clear_user(&((vg32_t *)arg)->proc, sizeof(vg32_t) - (long)&((vg32_t *)0)->proc)) + err = -EFAULT; + } + if (copy_to_user(((vg32_t *)arg)->vg_uuid, v->vg_uuid, UUID_LEN+1)) { + err = -EFAULT; + } + kfree(v); + break; + case VG_CREATE: + for (i = 0; i < v->pv_max; i++) + if (v->pv[i]) kfree(v->pv[i]); + for (i = 0; i < v->lv_max; i++) + if (v->lv[i]) put_lv_t(v->lv[i]); + kfree(v); + break; + case LV_STATUS_BYNAME: + if (!err && u.lv_req.lv) err = copy_lv_t(ptr, u.lv_req.lv); + /* Fall through */ + case LV_CREATE: + case LV_EXTEND: + case LV_REDUCE: + if (u.lv_req.lv) put_lv_t(u.lv_req.lv); + break; + case LV_STATUS_BYINDEX: + if (u.lv_byindex.lv) { + if (!err) err = copy_lv_t(ptr, u.lv_byindex.lv); + put_lv_t(u.lv_byindex.lv); + } + break; + case PV_STATUS: + if (!err) { + err = copy_to_user((void *)A(ptr), &p, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + err = copy_to_user(((pv_t *)A(ptr))->pv_uuid, p.pv_uuid, UUID_LEN + 1); + if (err) return -EFAULT; + } + break; + case LV_STATUS_BYDEV: + if (!err) { + if (!err) err = copy_lv_t(ptr, u.lv_bydev.lv); + put_lv_t(u.lv_byindex.lv); + } + break; + } + return err; +} +#endif + +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +/* This really belongs in include/linux/drm.h -DaveM */ +#include "../../../drivers/char/drm/drm.h" + +typedef struct drm32_version { + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel;/* Patch level */ + int name_len; /* Length of name buffer */ + u32 name; /* Name of driver */ + int date_len; /* Length of date buffer */ + u32 date; /* User-space buffer to hold date */ + int desc_len; /* Length of desc buffer */ + u32 desc; /* User-space buffer to hold desc */ +} drm32_version_t; +#define DRM32_IOCTL_VERSION DRM_IOWR(0x00, drm32_version_t) + +static int drm32_version(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_version_t *uversion = (drm32_version_t *)arg; + char *name_ptr, *date_ptr, *desc_ptr; + u32 tmp1, tmp2, tmp3; + drm_version_t kversion; + mm_segment_t old_fs; + int ret; + + memset(&kversion, 0, sizeof(kversion)); + if (get_user(kversion.name_len, &uversion->name_len) || + get_user(kversion.date_len, &uversion->date_len) || + get_user(kversion.desc_len, &uversion->desc_len) || + get_user(tmp1, &uversion->name) || + get_user(tmp2, &uversion->date) || + get_user(tmp3, &uversion->desc)) + return -EFAULT; + + name_ptr = (char *) A(tmp1); + date_ptr = (char *) A(tmp2); + desc_ptr = (char *) A(tmp3); + + ret = -ENOMEM; + if (kversion.name_len && name_ptr) { + kversion.name = kmalloc(kversion.name_len, GFP_KERNEL); + if (!kversion.name) + goto out; + } + if (kversion.date_len && date_ptr) { + kversion.date = kmalloc(kversion.date_len, GFP_KERNEL); + if (!kversion.date) + goto out; + } + if (kversion.desc_len && desc_ptr) { + kversion.desc = kmalloc(kversion.desc_len, GFP_KERNEL); + if (!kversion.desc) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl (fd, DRM_IOCTL_VERSION, (unsigned long)&kversion); + set_fs(old_fs); + + if (!ret) { + if ((kversion.name && + copy_to_user(name_ptr, kversion.name, kversion.name_len)) || + (kversion.date && + copy_to_user(date_ptr, kversion.date, kversion.date_len)) || + (kversion.desc && + copy_to_user(desc_ptr, kversion.desc, kversion.desc_len))) + ret = -EFAULT; + if (put_user(kversion.version_major, &uversion->version_major) || + put_user(kversion.version_minor, &uversion->version_minor) || + put_user(kversion.version_patchlevel, &uversion->version_patchlevel) || + put_user(kversion.name_len, &uversion->name_len) || + put_user(kversion.date_len, &uversion->date_len) || + put_user(kversion.desc_len, &uversion->desc_len)) + ret = -EFAULT; + } + +out: + if (kversion.name) + kfree(kversion.name); + if (kversion.date) + kfree(kversion.date); + if (kversion.desc) + kfree(kversion.desc); + return ret; +} + +typedef struct drm32_unique { + int unique_len; /* Length of unique */ + u32 unique; /* Unique name for driver instantiation */ +} drm32_unique_t; +#define DRM32_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm32_unique_t) +#define DRM32_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm32_unique_t) + +static int drm32_getsetunique(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_unique_t *uarg = (drm32_unique_t *)arg; + drm_unique_t karg; + mm_segment_t old_fs; + char *uptr; + u32 tmp; + int ret; + + if (get_user(karg.unique_len, &uarg->unique_len)) + return -EFAULT; + karg.unique = NULL; + + if (get_user(tmp, &uarg->unique)) + return -EFAULT; + + uptr = (char *) A(tmp); + + if (uptr) { + karg.unique = kmalloc(karg.unique_len, GFP_KERNEL); + if (!karg.unique) + return -ENOMEM; + if (cmd == DRM32_IOCTL_SET_UNIQUE && + copy_from_user(karg.unique, uptr, karg.unique_len)) { + kfree(karg.unique); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + if (cmd == DRM32_IOCTL_GET_UNIQUE) + ret = sys_ioctl (fd, DRM_IOCTL_GET_UNIQUE, (unsigned long)&karg); + else + ret = sys_ioctl (fd, DRM_IOCTL_SET_UNIQUE, (unsigned long)&karg); + set_fs(old_fs); + + if (!ret) { + if (cmd == DRM32_IOCTL_GET_UNIQUE && + uptr != NULL && + copy_to_user(uptr, karg.unique, karg.unique_len)) + ret = -EFAULT; + if (put_user(karg.unique_len, &uarg->unique_len)) + ret = -EFAULT; + } + + if (karg.unique != NULL) + kfree(karg.unique); + + return ret; +} + +typedef struct drm32_map { + u32 offset; /* Requested physical address (0 for SAREA)*/ + u32 size; /* Requested physical size (bytes) */ + drm_map_type_t type; /* Type of memory to map */ + drm_map_flags_t flags; /* Flags */ + u32 handle; /* User-space: "Handle" to pass to mmap */ + /* Kernel-space: kernel-virtual address */ + int mtrr; /* MTRR slot used */ + /* Private data */ +} drm32_map_t; +#define DRM32_IOCTL_ADD_MAP DRM_IOWR(0x15, drm32_map_t) + +static int drm32_addmap(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_map_t *uarg = (drm32_map_t *) arg; + drm_map_t karg; + mm_segment_t old_fs; + u32 tmp; + int ret; + + ret = get_user(karg.offset, &uarg->offset); + ret |= get_user(karg.size, &uarg->size); + ret |= get_user(karg.type, &uarg->type); + ret |= get_user(karg.flags, &uarg->flags); + ret |= get_user(tmp, &uarg->handle); + ret |= get_user(karg.mtrr, &uarg->mtrr); + if (ret) + return -EFAULT; + + karg.handle = (void *) A(tmp); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_ADD_MAP, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + ret = put_user(karg.offset, &uarg->offset); + ret |= put_user(karg.size, &uarg->size); + ret |= put_user(karg.type, &uarg->type); + ret |= put_user(karg.flags, &uarg->flags); + tmp = (u32) (long)karg.handle; + ret |= put_user(tmp, &uarg->handle); + ret |= put_user(karg.mtrr, &uarg->mtrr); + if (ret) + ret = -EFAULT; + } + + return ret; +} + +typedef struct drm32_buf_info { + int count; /* Entries in list */ + u32 list; /* (drm_buf_desc_t *) */ +} drm32_buf_info_t; +#define DRM32_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm32_buf_info_t) + +static int drm32_info_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_info_t *uarg = (drm32_buf_info_t *)arg; + drm_buf_desc_t *ulist; + drm_buf_info_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (drm_buf_desc_t *) A(tmp); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_desc_t), GFP_KERNEL); + if (!karg.list) + return -EFAULT; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_INFO_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (karg.count <= orig_count && + (copy_to_user(ulist, karg.list, + karg.count * sizeof(drm_buf_desc_t)))) + ret = -EFAULT; + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_free { + int count; + u32 list; /* (int *) */ +} drm32_buf_free_t; +#define DRM32_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm32_buf_free_t) + +static int drm32_free_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_free_t *uarg = (drm32_buf_free_t *)arg; + drm_buf_free_t karg; + mm_segment_t old_fs; + int *ulist; + int ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (int *) A(tmp); + + karg.list = kmalloc(karg.count * sizeof(int), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + if (copy_from_user(karg.list, ulist, (karg.count * sizeof(int)))) + goto out; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_FREE_BUFS, (unsigned long) &karg); + set_fs(old_fs); + +out: + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_pub { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int used; /* Amount of buffer in use (for DMA) */ + u32 address; /* Address of buffer (void *) */ +} drm32_buf_pub_t; + +typedef struct drm32_buf_map { + int count; /* Length of buflist */ + u32 virtual; /* Mmaped area in user-virtual (void *) */ + u32 list; /* Buffer information (drm_buf_pub_t *) */ +} drm32_buf_map_t; +#define DRM32_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm32_buf_map_t) + +static int drm32_map_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_map_t *uarg = (drm32_buf_map_t *)arg; + drm32_buf_pub_t *ulist; + drm_buf_map_t karg; + mm_segment_t old_fs; + int orig_count, ret, i; + u32 tmp1, tmp2; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp1, &uarg->virtual) || + get_user(tmp2, &uarg->list)) + return -EFAULT; + + karg.virtual = (void *) A(tmp1); + ulist = (drm32_buf_pub_t *) A(tmp2); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_pub_t), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + for (i = 0; i < karg.count; i++) { + if (get_user(karg.list[i].idx, &ulist[i].idx) || + get_user(karg.list[i].total, &ulist[i].total) || + get_user(karg.list[i].used, &ulist[i].used) || + get_user(tmp1, &ulist[i].address)) + goto out; + + karg.list[i].address = (void *) A(tmp1); + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_MAP_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + for (i = 0; i < orig_count; i++) { + tmp1 = (u32) (long) karg.list[i].address; + if (put_user(karg.list[i].idx, &ulist[i].idx) || + put_user(karg.list[i].total, &ulist[i].total) || + put_user(karg.list[i].used, &ulist[i].used) || + put_user(tmp1, &ulist[i].address)) { + ret = -EFAULT; + goto out; + } + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + +out: + kfree(karg.list); + return ret; +} + +typedef struct drm32_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 */ + u32 send_indices; /* List of handles to buffers (int *) */ + u32 send_sizes; /* Lengths of data to send (int *) */ + drm_dma_flags_t flags; /* Flags */ + int request_count; /* Number of buffers requested */ + int request_size; /* Desired size for buffers */ + u32 request_indices; /* Buffer information (int *) */ + u32 request_sizes; /* (int *) */ + int granted_count; /* Number of buffers granted */ +} drm32_dma_t; +#define DRM32_IOCTL_DMA DRM_IOWR(0x29, drm32_dma_t) + +/* RED PEN The DRM layer blindly dereferences the send/request + * indice/size arrays even though they are userland + * pointers. -DaveM + */ +static int drm32_dma(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_dma_t *uarg = (drm32_dma_t *) arg; + int *u_si, *u_ss, *u_ri, *u_rs; + drm_dma_t karg; + mm_segment_t old_fs; + int ret; + u32 tmp1, tmp2, tmp3, tmp4; + + karg.send_indices = karg.send_sizes = NULL; + karg.request_indices = karg.request_sizes = NULL; + + if (get_user(karg.context, &uarg->context) || + get_user(karg.send_count, &uarg->send_count) || + get_user(tmp1, &uarg->send_indices) || + get_user(tmp2, &uarg->send_sizes) || + get_user(karg.flags, &uarg->flags) || + get_user(karg.request_count, &uarg->request_count) || + get_user(karg.request_size, &uarg->request_size) || + get_user(tmp3, &uarg->request_indices) || + get_user(tmp4, &uarg->request_sizes) || + get_user(karg.granted_count, &uarg->granted_count)) + return -EFAULT; + + u_si = (int *) A(tmp1); + u_ss = (int *) A(tmp2); + u_ri = (int *) A(tmp3); + u_rs = (int *) A(tmp4); + + if (karg.send_count) { + karg.send_indices = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + karg.send_sizes = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.send_indices || !karg.send_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.send_indices, u_si, + (karg.send_count * sizeof(int))) || + copy_from_user(karg.send_sizes, u_ss, + (karg.send_count * sizeof(int)))) + goto out; + } + + if (karg.request_count) { + karg.request_indices = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + karg.request_sizes = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.request_indices || !karg.request_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.request_indices, u_ri, + (karg.request_count * sizeof(int))) || + copy_from_user(karg.request_sizes, u_rs, + (karg.request_count * sizeof(int)))) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_DMA, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (put_user(karg.context, &uarg->context) || + put_user(karg.send_count, &uarg->send_count) || + put_user(karg.flags, &uarg->flags) || + put_user(karg.request_count, &uarg->request_count) || + put_user(karg.request_size, &uarg->request_size) || + put_user(karg.granted_count, &uarg->granted_count)) + ret = -EFAULT; + + if (karg.send_count) { + if (copy_to_user(u_si, karg.send_indices, + (karg.send_count * sizeof(int))) || + copy_to_user(u_ss, karg.send_sizes, + (karg.send_count * sizeof(int)))) + ret = -EFAULT; + } + if (karg.request_count) { + if (copy_to_user(u_ri, karg.request_indices, + (karg.request_count * sizeof(int))) || + copy_to_user(u_rs, karg.request_sizes, + (karg.request_count * sizeof(int)))) + ret = -EFAULT; + } + } + +out: + if (karg.send_indices) + kfree(karg.send_indices); + if (karg.send_sizes) + kfree(karg.send_sizes); + if (karg.request_indices) + kfree(karg.request_indices); + if (karg.request_sizes) + kfree(karg.request_sizes); + + return ret; +} + +typedef struct drm32_ctx_res { + int count; + u32 contexts; /* (drm_ctx_t *) */ +} drm32_ctx_res_t; +#define DRM32_IOCTL_RES_CTX DRM_IOWR(0x26, drm32_ctx_res_t) + +static int drm32_res_ctx(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_ctx_res_t *uarg = (drm32_ctx_res_t *) arg; + drm_ctx_t *ulist; + drm_ctx_res_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + karg.contexts = NULL; + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->contexts)) + return -EFAULT; + + ulist = (drm_ctx_t *) A(tmp); + + orig_count = karg.count; + if (karg.count && ulist) { + karg.contexts = kmalloc((karg.count * sizeof(drm_ctx_t)), GFP_KERNEL); + if (!karg.contexts) + return -ENOMEM; + if (copy_from_user(karg.contexts, ulist, + (karg.count * sizeof(drm_ctx_t)))) { + kfree(karg.contexts); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_RES_CTX, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (orig_count) { + if (copy_to_user(ulist, karg.contexts, + (orig_count * sizeof(drm_ctx_t)))) + ret = -EFAULT; + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + if (karg.contexts) + kfree(karg.contexts); + + return ret; +} + +#endif + +static int ret_einval(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return -EINVAL; +} + +static int broken_blkgetsize(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* The mkswap binary hard codes it to Intel value :-((( */ + return w_long(fd, BLKGETSIZE, arg); +} + +struct blkpg_ioctl_arg32 { + int op; + int flags; + int datalen; + u32 data; +}; + +static int blkpg_ioctl_trans(unsigned int fd, unsigned int cmd, struct blkpg_ioctl_arg32 *arg) +{ + struct blkpg_ioctl_arg a; + struct blkpg_partition p; + int err; + mm_segment_t old_fs = get_fs(); + + err = get_user(a.op, &arg->op); + err |= __get_user(a.flags, &arg->flags); + err |= __get_user(a.datalen, &arg->datalen); + err |= __get_user((long)a.data, &arg->data); + if (err) return err; + switch (a.op) { + case BLKPG_ADD_PARTITION: + case BLKPG_DEL_PARTITION: + if (a.datalen < sizeof(struct blkpg_partition)) + return -EINVAL; + if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) + return -EFAULT; + a.data = &p; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&a); + set_fs (old_fs); + default: + return -EINVAL; + } + return err; +} + +static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return rw_long(fd, AUTOFS_IOC_SETTIMEOUT, arg); +} + +struct ioctl_trans { + unsigned long cmd; + unsigned long handler; + struct ioctl_trans *next; +}; + +#define REF_SYMBOL(handler) if (0) (void)handler; +#define HANDLE_IOCTL2(cmd,handler) REF_SYMBOL(handler); asm volatile(".quad %c0, " #handler ",0"::"i" (cmd)); +#define HANDLE_IOCTL(cmd,handler) HANDLE_IOCTL2(cmd,handler) +#define COMPATIBLE_IOCTL(cmd) HANDLE_IOCTL(cmd,sys_ioctl) +#define IOCTL_TABLE_START void ioctl_dummy(void) { asm volatile("\nioctl_start:\n\t" ); +#define IOCTL_TABLE_END asm volatile("\nioctl_end:"); } + +IOCTL_TABLE_START +/* List here exlicitly which ioctl's are known to have + * compatable types passed or none at all... + */ +/* Big T */ +COMPATIBLE_IOCTL(TCGETA) +COMPATIBLE_IOCTL(TCSETA) +COMPATIBLE_IOCTL(TCSETAW) +COMPATIBLE_IOCTL(TCSETAF) +COMPATIBLE_IOCTL(TCSBRK) +COMPATIBLE_IOCTL(TCXONC) +COMPATIBLE_IOCTL(TCFLSH) +COMPATIBLE_IOCTL(TCGETS) +COMPATIBLE_IOCTL(TCSETS) +COMPATIBLE_IOCTL(TCSETSW) +COMPATIBLE_IOCTL(TCSETSF) +COMPATIBLE_IOCTL(TIOCLINUX) +/* Little t */ +COMPATIBLE_IOCTL(TIOCGETD) +COMPATIBLE_IOCTL(TIOCSETD) +COMPATIBLE_IOCTL(TIOCEXCL) +COMPATIBLE_IOCTL(TIOCNXCL) +COMPATIBLE_IOCTL(TIOCCONS) +COMPATIBLE_IOCTL(TIOCGSOFTCAR) +COMPATIBLE_IOCTL(TIOCSSOFTCAR) +COMPATIBLE_IOCTL(TIOCSWINSZ) +COMPATIBLE_IOCTL(TIOCGWINSZ) +COMPATIBLE_IOCTL(TIOCMGET) +COMPATIBLE_IOCTL(TIOCMBIC) +COMPATIBLE_IOCTL(TIOCMBIS) +COMPATIBLE_IOCTL(TIOCMSET) +COMPATIBLE_IOCTL(TIOCPKT) +COMPATIBLE_IOCTL(TIOCNOTTY) +COMPATIBLE_IOCTL(TIOCSTI) +COMPATIBLE_IOCTL(TIOCOUTQ) +COMPATIBLE_IOCTL(TIOCSPGRP) +COMPATIBLE_IOCTL(TIOCGPGRP) +COMPATIBLE_IOCTL(TIOCSCTTY) +COMPATIBLE_IOCTL(TIOCGPTN) +COMPATIBLE_IOCTL(TIOCSPTLCK) +COMPATIBLE_IOCTL(TIOCGSERIAL) +COMPATIBLE_IOCTL(TIOCSSERIAL) +COMPATIBLE_IOCTL(TIOCSERGETLSR) +COMPATIBLE_IOCTL(FBIOGET_VSCREENINFO) +COMPATIBLE_IOCTL(FBIOPUT_VSCREENINFO) +COMPATIBLE_IOCTL(FBIOPAN_DISPLAY) +COMPATIBLE_IOCTL(FBIOGET_FCURSORINFO) +COMPATIBLE_IOCTL(FBIOGET_VCURSORINFO) +COMPATIBLE_IOCTL(FBIOPUT_VCURSORINFO) +COMPATIBLE_IOCTL(FBIOGET_CURSORSTATE) +COMPATIBLE_IOCTL(FBIOPUT_CURSORSTATE) +COMPATIBLE_IOCTL(FBIOGET_CON2FBMAP) +COMPATIBLE_IOCTL(FBIOPUT_CON2FBMAP) +/* Little f */ +COMPATIBLE_IOCTL(FIOCLEX) +COMPATIBLE_IOCTL(FIONCLEX) +COMPATIBLE_IOCTL(FIOASYNC) +COMPATIBLE_IOCTL(FIONBIO) +COMPATIBLE_IOCTL(FIONREAD) /* This is also TIOCINQ */ +/* 0x00 */ +COMPATIBLE_IOCTL(FIBMAP) +COMPATIBLE_IOCTL(FIGETBSZ) +/* 0x03 -- HD/IDE ioctl's used by hdparm and friends. + * Some need translations, these do not. + */ +COMPATIBLE_IOCTL(HDIO_GET_IDENTITY) +COMPATIBLE_IOCTL(HDIO_SET_DMA) +COMPATIBLE_IOCTL(HDIO_SET_KEEPSETTINGS) +COMPATIBLE_IOCTL(HDIO_SET_UNMASKINTR) +COMPATIBLE_IOCTL(HDIO_SET_NOWERR) +COMPATIBLE_IOCTL(HDIO_SET_32BIT) +COMPATIBLE_IOCTL(HDIO_SET_MULTCOUNT) +COMPATIBLE_IOCTL(HDIO_DRIVE_CMD) +COMPATIBLE_IOCTL(HDIO_SET_PIO_MODE) +COMPATIBLE_IOCTL(HDIO_SCAN_HWIF) +COMPATIBLE_IOCTL(HDIO_SET_NICE) +/* 0x02 -- Floppy ioctls */ +COMPATIBLE_IOCTL(FDMSGON) +COMPATIBLE_IOCTL(FDMSGOFF) +COMPATIBLE_IOCTL(FDSETEMSGTRESH) +COMPATIBLE_IOCTL(FDFLUSH) +COMPATIBLE_IOCTL(FDWERRORCLR) +COMPATIBLE_IOCTL(FDSETMAXERRS) +COMPATIBLE_IOCTL(FDGETMAXERRS) +COMPATIBLE_IOCTL(FDGETDRVTYP) +COMPATIBLE_IOCTL(FDEJECT) +COMPATIBLE_IOCTL(FDCLRPRM) +COMPATIBLE_IOCTL(FDFMTBEG) +COMPATIBLE_IOCTL(FDFMTEND) +COMPATIBLE_IOCTL(FDRESET) +COMPATIBLE_IOCTL(FDTWADDLE) +COMPATIBLE_IOCTL(FDFMTTRK) +COMPATIBLE_IOCTL(FDRAWCMD) +/* 0x12 */ +COMPATIBLE_IOCTL(BLKROSET) +COMPATIBLE_IOCTL(BLKROGET) +COMPATIBLE_IOCTL(BLKRRPART) +COMPATIBLE_IOCTL(BLKFLSBUF) +COMPATIBLE_IOCTL(BLKRASET) +COMPATIBLE_IOCTL(BLKFRASET) +COMPATIBLE_IOCTL(BLKSECTSET) +COMPATIBLE_IOCTL(BLKSSZGET) + +/* RAID */ +COMPATIBLE_IOCTL(RAID_VERSION) +COMPATIBLE_IOCTL(GET_ARRAY_INFO) +COMPATIBLE_IOCTL(GET_DISK_INFO) +COMPATIBLE_IOCTL(PRINT_RAID_DEBUG) +COMPATIBLE_IOCTL(CLEAR_ARRAY) +COMPATIBLE_IOCTL(ADD_NEW_DISK) +COMPATIBLE_IOCTL(HOT_REMOVE_DISK) +COMPATIBLE_IOCTL(SET_ARRAY_INFO) +COMPATIBLE_IOCTL(SET_DISK_INFO) +COMPATIBLE_IOCTL(WRITE_RAID_INFO) +COMPATIBLE_IOCTL(UNPROTECT_ARRAY) +COMPATIBLE_IOCTL(PROTECT_ARRAY) +COMPATIBLE_IOCTL(HOT_ADD_DISK) +COMPATIBLE_IOCTL(SET_DISK_FAULTY) +COMPATIBLE_IOCTL(RUN_ARRAY) +COMPATIBLE_IOCTL(START_ARRAY) +COMPATIBLE_IOCTL(STOP_ARRAY) +COMPATIBLE_IOCTL(STOP_ARRAY_RO) +COMPATIBLE_IOCTL(RESTART_ARRAY_RW) + +/* Big K */ +COMPATIBLE_IOCTL(PIO_FONT) +COMPATIBLE_IOCTL(GIO_FONT) +COMPATIBLE_IOCTL(KDSIGACCEPT) +COMPATIBLE_IOCTL(KDGETKEYCODE) +COMPATIBLE_IOCTL(KDSETKEYCODE) +COMPATIBLE_IOCTL(KIOCSOUND) +COMPATIBLE_IOCTL(KDMKTONE) +COMPATIBLE_IOCTL(KDGKBTYPE) +COMPATIBLE_IOCTL(KDSETMODE) +COMPATIBLE_IOCTL(KDGETMODE) +COMPATIBLE_IOCTL(KDSKBMODE) +COMPATIBLE_IOCTL(KDGKBMODE) +COMPATIBLE_IOCTL(KDSKBMETA) +COMPATIBLE_IOCTL(KDGKBMETA) +COMPATIBLE_IOCTL(KDGKBENT) +COMPATIBLE_IOCTL(KDSKBENT) +COMPATIBLE_IOCTL(KDGKBSENT) +COMPATIBLE_IOCTL(KDSKBSENT) +COMPATIBLE_IOCTL(KDGKBDIACR) +COMPATIBLE_IOCTL(KDSKBDIACR) +COMPATIBLE_IOCTL(KDGKBLED) +COMPATIBLE_IOCTL(KDSKBLED) +COMPATIBLE_IOCTL(KDGETLED) +COMPATIBLE_IOCTL(KDSETLED) +COMPATIBLE_IOCTL(GIO_SCRNMAP) +COMPATIBLE_IOCTL(PIO_SCRNMAP) +COMPATIBLE_IOCTL(GIO_UNISCRNMAP) +COMPATIBLE_IOCTL(PIO_UNISCRNMAP) +COMPATIBLE_IOCTL(PIO_FONTRESET) +COMPATIBLE_IOCTL(PIO_UNIMAPCLR) +/* Big S */ +COMPATIBLE_IOCTL(SCSI_IOCTL_GET_IDLUN) +COMPATIBLE_IOCTL(SCSI_IOCTL_DOORLOCK) +COMPATIBLE_IOCTL(SCSI_IOCTL_DOORUNLOCK) +COMPATIBLE_IOCTL(SCSI_IOCTL_TEST_UNIT_READY) +COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_ENABLE) +COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_DISABLE) +COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER) +COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND) +/* Big V */ +COMPATIBLE_IOCTL(VT_SETMODE) +COMPATIBLE_IOCTL(VT_GETMODE) +COMPATIBLE_IOCTL(VT_GETSTATE) +COMPATIBLE_IOCTL(VT_OPENQRY) +COMPATIBLE_IOCTL(VT_ACTIVATE) +COMPATIBLE_IOCTL(VT_WAITACTIVE) +COMPATIBLE_IOCTL(VT_RELDISP) +COMPATIBLE_IOCTL(VT_DISALLOCATE) +COMPATIBLE_IOCTL(VT_RESIZE) +COMPATIBLE_IOCTL(VT_RESIZEX) +COMPATIBLE_IOCTL(VT_LOCKSWITCH) +COMPATIBLE_IOCTL(VT_UNLOCKSWITCH) +/* Little v, the video4linux ioctls */ +COMPATIBLE_IOCTL(VIDIOCGCAP) +COMPATIBLE_IOCTL(VIDIOCGCHAN) +COMPATIBLE_IOCTL(VIDIOCSCHAN) +COMPATIBLE_IOCTL(VIDIOCGPICT) +COMPATIBLE_IOCTL(VIDIOCSPICT) +COMPATIBLE_IOCTL(VIDIOCCAPTURE) +COMPATIBLE_IOCTL(VIDIOCKEY) +COMPATIBLE_IOCTL(VIDIOCGAUDIO) +COMPATIBLE_IOCTL(VIDIOCSAUDIO) +COMPATIBLE_IOCTL(VIDIOCSYNC) +COMPATIBLE_IOCTL(VIDIOCMCAPTURE) +COMPATIBLE_IOCTL(VIDIOCGMBUF) +COMPATIBLE_IOCTL(VIDIOCGUNIT) +COMPATIBLE_IOCTL(VIDIOCGCAPTURE) +COMPATIBLE_IOCTL(VIDIOCSCAPTURE) +/* BTTV specific... */ +COMPATIBLE_IOCTL(_IOW('v', BASE_VIDIOCPRIVATE+0, char [256])) +COMPATIBLE_IOCTL(_IOR('v', BASE_VIDIOCPRIVATE+1, char [256])) +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)) +COMPATIBLE_IOCTL(_IOW('v' , BASE_VIDIOCPRIVATE+3, char [16])) /* struct bttv_pll_info */ +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+4, int)) +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+5, int)) +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+6, int)) +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+7, int)) +/* Little p (/dev/rtc, /dev/envctrl, etc.) */ +#if 0 +COMPATIBLE_IOCTL(_IOR('p', 20, int[7])) /* RTCGET */ +COMPATIBLE_IOCTL(_IOW('p', 21, int[7])) /* RTCSET */ +#endif +COMPATIBLE_IOCTL(RTC_AIE_ON) +COMPATIBLE_IOCTL(RTC_AIE_OFF) +COMPATIBLE_IOCTL(RTC_UIE_ON) +COMPATIBLE_IOCTL(RTC_UIE_OFF) +COMPATIBLE_IOCTL(RTC_PIE_ON) +COMPATIBLE_IOCTL(RTC_PIE_OFF) +COMPATIBLE_IOCTL(RTC_WIE_ON) +COMPATIBLE_IOCTL(RTC_WIE_OFF) +COMPATIBLE_IOCTL(RTC_ALM_SET) +COMPATIBLE_IOCTL(RTC_ALM_READ) +COMPATIBLE_IOCTL(RTC_RD_TIME) +COMPATIBLE_IOCTL(RTC_SET_TIME) +COMPATIBLE_IOCTL(RTC_WKALM_SET) +COMPATIBLE_IOCTL(RTC_WKALM_RD) +COMPATIBLE_IOCTL(RTC_IRQP_READ) +COMPATIBLE_IOCTL(RTC_IRQP_SET) +COMPATIBLE_IOCTL(RTC_EPOCH_READ) +COMPATIBLE_IOCTL(RTC_EPOCH_SET) +/* Little m */ +COMPATIBLE_IOCTL(MTIOCTOP) +/* Socket level stuff */ +COMPATIBLE_IOCTL(FIOSETOWN) +COMPATIBLE_IOCTL(SIOCSPGRP) +COMPATIBLE_IOCTL(FIOGETOWN) +COMPATIBLE_IOCTL(SIOCGPGRP) +COMPATIBLE_IOCTL(SIOCATMARK) +COMPATIBLE_IOCTL(SIOCSIFLINK) +COMPATIBLE_IOCTL(SIOCSIFENCAP) +COMPATIBLE_IOCTL(SIOCGIFENCAP) +COMPATIBLE_IOCTL(SIOCSIFBR) +COMPATIBLE_IOCTL(SIOCGIFBR) +COMPATIBLE_IOCTL(SIOCSARP) +COMPATIBLE_IOCTL(SIOCGARP) +COMPATIBLE_IOCTL(SIOCDARP) +COMPATIBLE_IOCTL(SIOCSRARP) +COMPATIBLE_IOCTL(SIOCGRARP) +COMPATIBLE_IOCTL(SIOCDRARP) +COMPATIBLE_IOCTL(SIOCADDDLCI) +COMPATIBLE_IOCTL(SIOCDELDLCI) +/* SG stuff */ +COMPATIBLE_IOCTL(SG_SET_TIMEOUT) +COMPATIBLE_IOCTL(SG_GET_TIMEOUT) +COMPATIBLE_IOCTL(SG_EMULATED_HOST) +COMPATIBLE_IOCTL(SG_SET_TRANSFORM) +COMPATIBLE_IOCTL(SG_GET_TRANSFORM) +COMPATIBLE_IOCTL(SG_SET_RESERVED_SIZE) +COMPATIBLE_IOCTL(SG_GET_RESERVED_SIZE) +COMPATIBLE_IOCTL(SG_GET_SCSI_ID) +COMPATIBLE_IOCTL(SG_SET_FORCE_LOW_DMA) +COMPATIBLE_IOCTL(SG_GET_LOW_DMA) +COMPATIBLE_IOCTL(SG_SET_FORCE_PACK_ID) +COMPATIBLE_IOCTL(SG_GET_PACK_ID) +COMPATIBLE_IOCTL(SG_GET_NUM_WAITING) +COMPATIBLE_IOCTL(SG_SET_DEBUG) +COMPATIBLE_IOCTL(SG_GET_SG_TABLESIZE) +COMPATIBLE_IOCTL(SG_GET_COMMAND_Q) +COMPATIBLE_IOCTL(SG_SET_COMMAND_Q) +COMPATIBLE_IOCTL(SG_GET_VERSION_NUM) +COMPATIBLE_IOCTL(SG_NEXT_CMD_LEN) +COMPATIBLE_IOCTL(SG_SCSI_RESET) +COMPATIBLE_IOCTL(SG_IO) +COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE) +COMPATIBLE_IOCTL(SG_SET_KEEP_ORPHAN) +COMPATIBLE_IOCTL(SG_GET_KEEP_ORPHAN) +/* PPP stuff */ +COMPATIBLE_IOCTL(PPPIOCGFLAGS) +COMPATIBLE_IOCTL(PPPIOCSFLAGS) +COMPATIBLE_IOCTL(PPPIOCGASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCSASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCGUNIT) +COMPATIBLE_IOCTL(PPPIOCGRASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCSRASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCGMRU) +COMPATIBLE_IOCTL(PPPIOCSMRU) +COMPATIBLE_IOCTL(PPPIOCSMAXCID) +COMPATIBLE_IOCTL(PPPIOCGXASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCSXASYNCMAP) +COMPATIBLE_IOCTL(PPPIOCXFERUNIT) +COMPATIBLE_IOCTL(PPPIOCGNPMODE) +COMPATIBLE_IOCTL(PPPIOCSNPMODE) +COMPATIBLE_IOCTL(PPPIOCGDEBUG) +COMPATIBLE_IOCTL(PPPIOCSDEBUG) +COMPATIBLE_IOCTL(PPPIOCNEWUNIT) +COMPATIBLE_IOCTL(PPPIOCATTACH) +COMPATIBLE_IOCTL(PPPIOCDETACH) +COMPATIBLE_IOCTL(PPPIOCSMRRU) +COMPATIBLE_IOCTL(PPPIOCCONNECT) +COMPATIBLE_IOCTL(PPPIOCDISCONN) +COMPATIBLE_IOCTL(PPPIOCATTCHAN) +COMPATIBLE_IOCTL(PPPIOCGCHAN) +/* PPPOX */ +COMPATIBLE_IOCTL(PPPOEIOCSFWD); +COMPATIBLE_IOCTL(PPPOEIOCDFWD); +/* CDROM stuff */ +COMPATIBLE_IOCTL(CDROMPAUSE) +COMPATIBLE_IOCTL(CDROMRESUME) +COMPATIBLE_IOCTL(CDROMPLAYMSF) +COMPATIBLE_IOCTL(CDROMPLAYTRKIND) +COMPATIBLE_IOCTL(CDROMREADTOCHDR) +COMPATIBLE_IOCTL(CDROMREADTOCENTRY) +COMPATIBLE_IOCTL(CDROMSTOP) +COMPATIBLE_IOCTL(CDROMSTART) +COMPATIBLE_IOCTL(CDROMEJECT) +COMPATIBLE_IOCTL(CDROMVOLCTRL) +COMPATIBLE_IOCTL(CDROMSUBCHNL) +COMPATIBLE_IOCTL(CDROMEJECT_SW) +COMPATIBLE_IOCTL(CDROMMULTISESSION) +COMPATIBLE_IOCTL(CDROM_GET_MCN) +COMPATIBLE_IOCTL(CDROMRESET) +COMPATIBLE_IOCTL(CDROMVOLREAD) +COMPATIBLE_IOCTL(CDROMSEEK) +COMPATIBLE_IOCTL(CDROMPLAYBLK) +COMPATIBLE_IOCTL(CDROMCLOSETRAY) +COMPATIBLE_IOCTL(CDROM_SET_OPTIONS) +COMPATIBLE_IOCTL(CDROM_CLEAR_OPTIONS) +COMPATIBLE_IOCTL(CDROM_SELECT_SPEED) +COMPATIBLE_IOCTL(CDROM_SELECT_DISC) +COMPATIBLE_IOCTL(CDROM_MEDIA_CHANGED) +COMPATIBLE_IOCTL(CDROM_DRIVE_STATUS) +COMPATIBLE_IOCTL(CDROM_DISC_STATUS) +COMPATIBLE_IOCTL(CDROM_CHANGER_NSLOTS) +COMPATIBLE_IOCTL(CDROM_LOCKDOOR) +COMPATIBLE_IOCTL(CDROM_DEBUG) +COMPATIBLE_IOCTL(CDROM_GET_CAPABILITY) +/* Big L */ +COMPATIBLE_IOCTL(LOOP_SET_FD) +COMPATIBLE_IOCTL(LOOP_CLR_FD) +/* Big Q for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_SEQ_RESET) +COMPATIBLE_IOCTL(SNDCTL_SEQ_SYNC) +COMPATIBLE_IOCTL(SNDCTL_SYNTH_INFO) +COMPATIBLE_IOCTL(SNDCTL_SEQ_CTRLRATE) +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETOUTCOUNT) +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETINCOUNT) +COMPATIBLE_IOCTL(SNDCTL_SEQ_PERCMODE) +COMPATIBLE_IOCTL(SNDCTL_FM_LOAD_INSTR) +COMPATIBLE_IOCTL(SNDCTL_SEQ_TESTMIDI) +COMPATIBLE_IOCTL(SNDCTL_SEQ_RESETSAMPLES) +COMPATIBLE_IOCTL(SNDCTL_SEQ_NRSYNTHS) +COMPATIBLE_IOCTL(SNDCTL_SEQ_NRMIDIS) +COMPATIBLE_IOCTL(SNDCTL_MIDI_INFO) +COMPATIBLE_IOCTL(SNDCTL_SEQ_THRESHOLD) +COMPATIBLE_IOCTL(SNDCTL_SYNTH_MEMAVL) +COMPATIBLE_IOCTL(SNDCTL_FM_4OP_ENABLE) +COMPATIBLE_IOCTL(SNDCTL_SEQ_PANIC) +COMPATIBLE_IOCTL(SNDCTL_SEQ_OUTOFBAND) +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETTIME) +COMPATIBLE_IOCTL(SNDCTL_SYNTH_ID) +COMPATIBLE_IOCTL(SNDCTL_SYNTH_CONTROL) +COMPATIBLE_IOCTL(SNDCTL_SYNTH_REMOVESAMPLE) +/* Big T for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_TMR_TIMEBASE) +COMPATIBLE_IOCTL(SNDCTL_TMR_START) +COMPATIBLE_IOCTL(SNDCTL_TMR_STOP) +COMPATIBLE_IOCTL(SNDCTL_TMR_CONTINUE) +COMPATIBLE_IOCTL(SNDCTL_TMR_TEMPO) +COMPATIBLE_IOCTL(SNDCTL_TMR_SOURCE) +COMPATIBLE_IOCTL(SNDCTL_TMR_METRONOME) +COMPATIBLE_IOCTL(SNDCTL_TMR_SELECT) +/* Little m for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_MIDI_PRETIME) +COMPATIBLE_IOCTL(SNDCTL_MIDI_MPUMODE) +COMPATIBLE_IOCTL(SNDCTL_MIDI_MPUCMD) +/* Big P for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_DSP_RESET) +COMPATIBLE_IOCTL(SNDCTL_DSP_SYNC) +COMPATIBLE_IOCTL(SNDCTL_DSP_SPEED) +COMPATIBLE_IOCTL(SNDCTL_DSP_STEREO) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETBLKSIZE) +COMPATIBLE_IOCTL(SNDCTL_DSP_CHANNELS) +COMPATIBLE_IOCTL(SOUND_PCM_WRITE_FILTER) +COMPATIBLE_IOCTL(SNDCTL_DSP_POST) +COMPATIBLE_IOCTL(SNDCTL_DSP_SUBDIVIDE) +COMPATIBLE_IOCTL(SNDCTL_DSP_SETFRAGMENT) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETFMTS) +COMPATIBLE_IOCTL(SNDCTL_DSP_SETFMT) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETOSPACE) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETISPACE) +COMPATIBLE_IOCTL(SNDCTL_DSP_NONBLOCK) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETCAPS) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETTRIGGER) +COMPATIBLE_IOCTL(SNDCTL_DSP_SETTRIGGER) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETIPTR) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETOPTR) +/* SNDCTL_DSP_MAPINBUF, XXX needs translation */ +/* SNDCTL_DSP_MAPOUTBUF, XXX needs translation */ +COMPATIBLE_IOCTL(SNDCTL_DSP_SETSYNCRO) +COMPATIBLE_IOCTL(SNDCTL_DSP_SETDUPLEX) +COMPATIBLE_IOCTL(SNDCTL_DSP_GETODELAY) +COMPATIBLE_IOCTL(SNDCTL_DSP_PROFILE) +COMPATIBLE_IOCTL(SOUND_PCM_READ_RATE) +COMPATIBLE_IOCTL(SOUND_PCM_READ_CHANNELS) +COMPATIBLE_IOCTL(SOUND_PCM_READ_BITS) +COMPATIBLE_IOCTL(SOUND_PCM_READ_FILTER) +/* Big C for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_COPR_RESET) +COMPATIBLE_IOCTL(SNDCTL_COPR_LOAD) +COMPATIBLE_IOCTL(SNDCTL_COPR_RDATA) +COMPATIBLE_IOCTL(SNDCTL_COPR_RCODE) +COMPATIBLE_IOCTL(SNDCTL_COPR_WDATA) +COMPATIBLE_IOCTL(SNDCTL_COPR_WCODE) +COMPATIBLE_IOCTL(SNDCTL_COPR_RUN) +COMPATIBLE_IOCTL(SNDCTL_COPR_HALT) +COMPATIBLE_IOCTL(SNDCTL_COPR_SENDMSG) +COMPATIBLE_IOCTL(SNDCTL_COPR_RCVMSG) +/* Big M for sound/OSS */ +COMPATIBLE_IOCTL(SOUND_MIXER_READ_VOLUME) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_BASS) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_TREBLE) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_SYNTH) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_PCM) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_SPEAKER) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_MIC) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_CD) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_IMIX) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_ALTPCM) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECLEV) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_IGAIN) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_OGAIN) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE1) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE2) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE3) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL1)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL2)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL3)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_PHONEIN)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_PHONEOUT)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_VIDEO)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_RADIO)) +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_MONITOR)) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_MUTE) +/* SOUND_MIXER_READ_ENHANCE, same value as READ_MUTE */ +/* SOUND_MIXER_READ_LOUD, same value as READ_MUTE */ +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECSRC) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_DEVMASK) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECMASK) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_STEREODEVS) +COMPATIBLE_IOCTL(SOUND_MIXER_READ_CAPS) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_VOLUME) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_BASS) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_TREBLE) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_SYNTH) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_PCM) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_SPEAKER) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_MIC) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_CD) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_IMIX) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_ALTPCM) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_RECLEV) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_IGAIN) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_OGAIN) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE1) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE2) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE3) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL1)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL2)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL3)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_PHONEIN)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_PHONEOUT)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_VIDEO)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_RADIO)) +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_MONITOR)) +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_MUTE) +/* SOUND_MIXER_WRITE_ENHANCE, same value as WRITE_MUTE */ +/* SOUND_MIXER_WRITE_LOUD, same value as WRITE_MUTE */ +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_RECSRC) +COMPATIBLE_IOCTL(SOUND_MIXER_INFO) +COMPATIBLE_IOCTL(SOUND_OLD_MIXER_INFO) +COMPATIBLE_IOCTL(SOUND_MIXER_ACCESS) +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE1) +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE2) +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE3) +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE4) +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE5) +COMPATIBLE_IOCTL(SOUND_MIXER_GETLEVELS) +COMPATIBLE_IOCTL(SOUND_MIXER_SETLEVELS) +COMPATIBLE_IOCTL(OSS_GETVERSION) +/* AUTOFS */ +COMPATIBLE_IOCTL(AUTOFS_IOC_READY) +COMPATIBLE_IOCTL(AUTOFS_IOC_FAIL) +COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC) +COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) +COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) +/* DEVFS */ +COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) +COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK) +COMPATIBLE_IOCTL(DEVFSDIOC_RELEASE_EVENT_QUEUE) +COMPATIBLE_IOCTL(DEVFSDIOC_SET_DEBUG_MASK) +/* Raw devices */ +COMPATIBLE_IOCTL(RAW_SETBIND) +COMPATIBLE_IOCTL(RAW_GETBIND) +/* SMB ioctls which do not need any translations */ +COMPATIBLE_IOCTL(SMB_IOC_NEWCONN) +/* Little a */ +COMPATIBLE_IOCTL(ATMSIGD_CTRL) +COMPATIBLE_IOCTL(ATMARPD_CTRL) +COMPATIBLE_IOCTL(ATMLEC_CTRL) +COMPATIBLE_IOCTL(ATMLEC_MCAST) +COMPATIBLE_IOCTL(ATMLEC_DATA) +COMPATIBLE_IOCTL(ATM_SETSC) +COMPATIBLE_IOCTL(SIOCSIFATMTCP) +COMPATIBLE_IOCTL(SIOCMKCLIP) +COMPATIBLE_IOCTL(ATMARP_MKIP) +COMPATIBLE_IOCTL(ATMARP_SETENTRY) +COMPATIBLE_IOCTL(ATMARP_ENCAP) +COMPATIBLE_IOCTL(ATMTCP_CREATE) +COMPATIBLE_IOCTL(ATMTCP_REMOVE) +COMPATIBLE_IOCTL(ATMMPC_CTRL) +COMPATIBLE_IOCTL(ATMMPC_DATA) +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* 0xfe - lvm */ +COMPATIBLE_IOCTL(VG_SET_EXTENDABLE) +COMPATIBLE_IOCTL(VG_STATUS_GET_COUNT) +COMPATIBLE_IOCTL(VG_STATUS_GET_NAMELIST) +COMPATIBLE_IOCTL(VG_REMOVE) +COMPATIBLE_IOCTL(VG_RENAME) +COMPATIBLE_IOCTL(VG_REDUCE) +COMPATIBLE_IOCTL(PE_LOCK_UNLOCK) +COMPATIBLE_IOCTL(PV_FLUSH) +COMPATIBLE_IOCTL(LVM_LOCK_LVM) +COMPATIBLE_IOCTL(LVM_GET_IOP_VERSION) +#ifdef LVM_TOTAL_RESET +COMPATIBLE_IOCTL(LVM_RESET) +#endif +COMPATIBLE_IOCTL(LV_SET_ACCESS) +COMPATIBLE_IOCTL(LV_SET_STATUS) +COMPATIBLE_IOCTL(LV_SET_ALLOCATION) +COMPATIBLE_IOCTL(LE_REMAP) +COMPATIBLE_IOCTL(LV_BMAP) +COMPATIBLE_IOCTL(LV_SNAPSHOT_USE_RATE) +#endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +COMPATIBLE_IOCTL(DRM_IOCTL_GET_MAGIC) +COMPATIBLE_IOCTL(DRM_IOCTL_IRQ_BUSID) +COMPATIBLE_IOCTL(DRM_IOCTL_AUTH_MAGIC) +COMPATIBLE_IOCTL(DRM_IOCTL_BLOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_UNBLOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_CONTROL) +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_BUFS) +COMPATIBLE_IOCTL(DRM_IOCTL_MARK_BUFS) +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_RM_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_MOD_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_GET_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_SWITCH_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_NEW_CTX) +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_DRAW) +COMPATIBLE_IOCTL(DRM_IOCTL_RM_DRAW) +COMPATIBLE_IOCTL(DRM_IOCTL_LOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_UNLOCK) +COMPATIBLE_IOCTL(DRM_IOCTL_FINISH) +#endif /* DRM */ +/* elevator */ +COMPATIBLE_IOCTL(BLKELVGET) +COMPATIBLE_IOCTL(BLKELVSET) +/* Misc. */ +COMPATIBLE_IOCTL(0x41545900) /* ATYIO_CLKR */ +COMPATIBLE_IOCTL(0x41545901) /* ATYIO_CLKW */ +COMPATIBLE_IOCTL(PCIIOC_CONTROLLER) +COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_IO) +COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_MEM) +COMPATIBLE_IOCTL(PCIIOC_WRITE_COMBINE) +/* And these ioctls need translation */ +HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32) +HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf) +HANDLE_IOCTL(SIOCGIFFLAGS, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFFLAGS, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFMETRIC, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFMETRIC, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFMTU, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFMTU, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFMEM, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFMEM, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFHWADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFHWADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCADDMULTI, dev_ifsioc) +HANDLE_IOCTL(SIOCDELMULTI, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFINDEX, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFMAP, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFMAP, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFBRDADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFBRDADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFDSTADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFDSTADDR, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFNETMASK, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFNETMASK, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFPFLAGS, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFPFLAGS, dev_ifsioc) +HANDLE_IOCTL(SIOCGPPPSTATS, dev_ifsioc) +HANDLE_IOCTL(SIOCGPPPCSTATS, dev_ifsioc) +HANDLE_IOCTL(SIOCGPPPVER, dev_ifsioc) +HANDLE_IOCTL(SIOCGIFTXQLEN, dev_ifsioc) +HANDLE_IOCTL(SIOCSIFTXQLEN, dev_ifsioc) +HANDLE_IOCTL(SIOCETHTOOL, ethtool_ioctl) +HANDLE_IOCTL(SIOCADDRT, routing_ioctl) +HANDLE_IOCTL(SIOCDELRT, routing_ioctl) +/* Note SIOCRTMSG is no longer, so this is safe and * the user would have seen just an -EINVAL anyways. */ +HANDLE_IOCTL(SIOCRTMSG, ret_einval) +HANDLE_IOCTL(SIOCGSTAMP, do_siocgstamp) +HANDLE_IOCTL(HDIO_GETGEO, hdio_getgeo) +HANDLE_IOCTL(BLKRAGET, w_long) +HANDLE_IOCTL(BLKGETSIZE, w_long) +HANDLE_IOCTL(0x1260, broken_blkgetsize) +HANDLE_IOCTL(BLKFRAGET, w_long) +HANDLE_IOCTL(BLKSECTGET, w_long) +HANDLE_IOCTL(BLKPG, blkpg_ioctl_trans) +HANDLE_IOCTL(FBIOGETCMAP, fb_ioctl_trans) +HANDLE_IOCTL(FBIOPUTCMAP, fb_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_KEEPSETTINGS, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_UNMASKINTR, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_DMA, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_32BIT, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_MULTCOUNT, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_NOWERR, hdio_ioctl_trans) +HANDLE_IOCTL(HDIO_GET_NICE, hdio_ioctl_trans) +HANDLE_IOCTL(FDSETPRM32, fd_ioctl_trans) +HANDLE_IOCTL(FDDEFPRM32, fd_ioctl_trans) +HANDLE_IOCTL(FDGETPRM32, fd_ioctl_trans) +HANDLE_IOCTL(FDSETDRVPRM32, fd_ioctl_trans) +HANDLE_IOCTL(FDGETDRVPRM32, fd_ioctl_trans) +HANDLE_IOCTL(FDGETDRVSTAT32, fd_ioctl_trans) +HANDLE_IOCTL(FDPOLLDRVSTAT32, fd_ioctl_trans) +HANDLE_IOCTL(FDGETFDCSTAT32, fd_ioctl_trans) +HANDLE_IOCTL(FDWERRORGET32, fd_ioctl_trans) +HANDLE_IOCTL(PPPIOCGIDLE32, ppp_ioctl_trans) +HANDLE_IOCTL(PPPIOCSCOMPRESS32, ppp_ioctl_trans) +HANDLE_IOCTL(MTIOCGET32, mt_ioctl_trans) +HANDLE_IOCTL(MTIOCPOS32, mt_ioctl_trans) +HANDLE_IOCTL(MTIOCGETCONFIG32, mt_ioctl_trans) +HANDLE_IOCTL(MTIOCSETCONFIG32, mt_ioctl_trans) +HANDLE_IOCTL(CDROMREADMODE2, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROMREADMODE1, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROMREADRAW, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROMREADCOOKED, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROMREADAUDIO, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROMREADALL, cdrom_ioctl_trans) +HANDLE_IOCTL(CDROM_SEND_PACKET, cdrom_ioctl_trans) +HANDLE_IOCTL(LOOP_SET_STATUS, loop_status) +HANDLE_IOCTL(LOOP_GET_STATUS, loop_status) +#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,unsigned int) +HANDLE_IOCTL(AUTOFS_IOC_SETTIMEOUT32, ioc_settimeout) +HANDLE_IOCTL(PIO_FONTX, do_fontx_ioctl) +HANDLE_IOCTL(GIO_FONTX, do_fontx_ioctl) +HANDLE_IOCTL(PIO_UNIMAP, do_unimap_ioctl) +HANDLE_IOCTL(GIO_UNIMAP, do_unimap_ioctl) +HANDLE_IOCTL(KDFONTOP, do_kdfontop_ioctl) +HANDLE_IOCTL(EXT2_IOC32_GETFLAGS, do_ext2_ioctl) +HANDLE_IOCTL(EXT2_IOC32_SETFLAGS, do_ext2_ioctl) +HANDLE_IOCTL(EXT2_IOC32_GETVERSION, do_ext2_ioctl) +HANDLE_IOCTL(EXT2_IOC32_SETVERSION, do_ext2_ioctl) +HANDLE_IOCTL(VIDIOCGTUNER32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCSTUNER32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCGWIN32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCSWIN32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCGFBUF32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCSFBUF32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCGFREQ32, do_video_ioctl) +HANDLE_IOCTL(VIDIOCSFREQ32, do_video_ioctl) +/* One SMB ioctl needs translations. */ +#define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, __kernel_uid_t32) +HANDLE_IOCTL(SMB_IOC_GETMOUNTUID_32, do_smb_getmountuid) +HANDLE_IOCTL(ATM_GETLINKRATE32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETNAMES32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETTYPE32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETESI32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETADDR32, do_atm_ioctl) +HANDLE_IOCTL(ATM_RSTADDR32, do_atm_ioctl) +HANDLE_IOCTL(ATM_ADDADDR32, do_atm_ioctl) +HANDLE_IOCTL(ATM_DELADDR32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETCIRANGE32, do_atm_ioctl) +HANDLE_IOCTL(ATM_SETCIRANGE32, do_atm_ioctl) +HANDLE_IOCTL(ATM_SETESI32, do_atm_ioctl) +HANDLE_IOCTL(ATM_SETESIF32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETSTAT32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETSTATZ32, do_atm_ioctl) +HANDLE_IOCTL(ATM_GETLOOP32, do_atm_ioctl) +HANDLE_IOCTL(ATM_SETLOOP32, do_atm_ioctl) +HANDLE_IOCTL(ATM_QUERYLOOP32, do_atm_ioctl) +HANDLE_IOCTL(SONET_GETSTAT, do_atm_ioctl) +HANDLE_IOCTL(SONET_GETSTATZ, do_atm_ioctl) +HANDLE_IOCTL(SONET_GETDIAG, do_atm_ioctl) +HANDLE_IOCTL(SONET_SETDIAG, do_atm_ioctl) +HANDLE_IOCTL(SONET_CLRDIAG, do_atm_ioctl) +HANDLE_IOCTL(SONET_SETFRAMING, do_atm_ioctl) +HANDLE_IOCTL(SONET_GETFRAMING, do_atm_ioctl) +HANDLE_IOCTL(SONET_GETFRSENSE, do_atm_ioctl) +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +HANDLE_IOCTL(VG_STATUS, do_lvm_ioctl) +HANDLE_IOCTL(VG_CREATE, do_lvm_ioctl) +HANDLE_IOCTL(VG_EXTEND, do_lvm_ioctl) +HANDLE_IOCTL(LV_CREATE, do_lvm_ioctl) +HANDLE_IOCTL(LV_REMOVE, do_lvm_ioctl) +HANDLE_IOCTL(LV_EXTEND, do_lvm_ioctl) +HANDLE_IOCTL(LV_REDUCE, do_lvm_ioctl) +HANDLE_IOCTL(LV_RENAME, do_lvm_ioctl) +HANDLE_IOCTL(LV_STATUS_BYNAME, do_lvm_ioctl) +HANDLE_IOCTL(LV_STATUS_BYINDEX, do_lvm_ioctl) +HANDLE_IOCTL(PV_CHANGE, do_lvm_ioctl) +HANDLE_IOCTL(PV_STATUS, do_lvm_ioctl) +#endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +HANDLE_IOCTL(DRM32_IOCTL_VERSION, drm32_version); +HANDLE_IOCTL(DRM32_IOCTL_GET_UNIQUE, drm32_getsetunique); +HANDLE_IOCTL(DRM32_IOCTL_SET_UNIQUE, drm32_getsetunique); +HANDLE_IOCTL(DRM32_IOCTL_ADD_MAP, drm32_addmap); +HANDLE_IOCTL(DRM32_IOCTL_INFO_BUFS, drm32_info_bufs); +HANDLE_IOCTL(DRM32_IOCTL_FREE_BUFS, drm32_free_bufs); +HANDLE_IOCTL(DRM32_IOCTL_MAP_BUFS, drm32_map_bufs); +HANDLE_IOCTL(DRM32_IOCTL_DMA, drm32_dma); +HANDLE_IOCTL(DRM32_IOCTL_RES_CTX, drm32_res_ctx); +#endif /* DRM */ +IOCTL_TABLE_END + +#define IOCTL_HASHSIZE 256 +struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE]; + +static inline unsigned long ioctl32_hash(unsigned long cmd) +{ + return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE; +} + +static void ioctl32_insert_translation(struct ioctl_trans *trans) +{ + unsigned long hash; + struct ioctl_trans *t; + + hash = ioctl32_hash (trans->cmd); + if (!ioctl32_hash_table[hash]) + ioctl32_hash_table[hash] = trans; + else { + t = ioctl32_hash_table[hash]; + while (t->next) + t = t->next; + trans->next = 0; + t->next = trans; + } +} + +static int __init init_sys32_ioctl(void) +{ + int i; + extern struct ioctl_trans ioctl_start[], ioctl_end[]; + + for (i = 0; &ioctl_start[i] < &ioctl_end[0]; i++) { + if (ioctl_start[i].next != 0) { + printk("ioctl translation %d bad\n",i); + return -1; + } + + ioctl32_insert_translation(&ioctl_start[i]); + } + return 0; +} + +__initcall(init_sys32_ioctl); + +static struct ioctl_trans *additional_ioctls; + +/* Always call these with kernel lock held! */ + +int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)) +{ + int i; + if (!additional_ioctls) { + additional_ioctls = module_map(PAGE_SIZE); + if (!additional_ioctls) + return -ENOMEM; + memset(additional_ioctls, 0, PAGE_SIZE); + } + for (i = 0; i < PAGE_SIZE/sizeof(struct ioctl_trans); i++) + if (!additional_ioctls[i].cmd) + break; + if (i == PAGE_SIZE/sizeof(struct ioctl_trans)) + return -ENOMEM; + additional_ioctls[i].cmd = cmd; + if (!handler) + additional_ioctls[i].handler = (u32)(long)sys_ioctl; + else + additional_ioctls[i].handler = (u32)(long)handler; + ioctl32_insert_translation(&additional_ioctls[i]); + return 0; +} + +int unregister_ioctl32_conversion(unsigned int cmd) +{ + unsigned long hash = ioctl32_hash(cmd); + struct ioctl_trans *t, *t1; + + t = (struct ioctl_trans *)(long)ioctl32_hash_table[hash]; + if (!t) return -EINVAL; + if (t->cmd == cmd && t >= additional_ioctls && + (unsigned long)t < ((unsigned long)additional_ioctls) + PAGE_SIZE) { + ioctl32_hash_table[hash] = t->next; + t->cmd = 0; + return 0; + } else while (t->next) { + t1 = (struct ioctl_trans *)(long)t->next; + if (t1->cmd == cmd && t1 >= additional_ioctls && + (unsigned long)t1 < ((unsigned long)additional_ioctls) + PAGE_SIZE) { + t1->cmd = 0; + t->next = t1->next; + return 0; + } + t = t1; + } + return -EINVAL; +} + +asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + int error = -EBADF; + int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); + struct ioctl_trans *t; + + filp = fget(fd); + if(!filp) + goto out2; + + if (!filp->f_op || !filp->f_op->ioctl) { + error = sys_ioctl (fd, cmd, arg); + goto out; + } + + t = (struct ioctl_trans *)(long)ioctl32_hash_table [ioctl32_hash (cmd)]; + + while (t && t->cmd != cmd) + t = (struct ioctl_trans *)(long)t->next; + if (t) { + handler = (void *)(long)t->handler; + error = handler(fd, cmd, arg, filp); + } else { + static int count = 0; + if (++count <= 50) + printk("sys32_ioctl(%s:%d): Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + current->comm, current->pid, + (int)fd, (unsigned int)cmd, (unsigned int)arg); + error = -EINVAL; + } +out: + fput(filp); +out2: + return error; +} diff -Nru a/arch/x86_64/ia32/ia32_signal.c b/arch/x86_64/ia32/ia32_signal.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/ia32/ia32_signal.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,489 @@ +/* + * linux/arch/x86_64/ia32/ia32_signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes + * 2000-12-* x86-64 compatibility mode signal handling by Andi Kleen + * + * $Id: ia32_signal.c,v 1.15 2001/10/16 23:41:42 ak Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ptr_to_u32(x) ((u32)(u64)(x)) /* avoid gcc warning */ + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); + +static int ia32_copy_siginfo_to_user(siginfo_t32 *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +asmlinkage int +sys32_sigsuspend(int history0, int history1, old_sigset_t mask, struct pt_regs regs) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs.rax = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(®s, &saveset)) + return -EINTR; + } +} + +asmlinkage int +sys32_sigaltstack(const stack_ia32_t *uss_ptr, stack_ia32_t *uoss_ptr, + struct pt_regs regs) +{ + stack_t uss,uoss; + int ret; + mm_segment_t seg; + if (!access_ok(VERIFY_READ,uss_ptr,sizeof(stack_ia32_t)) || + __get_user(ptr_to_u32(uss.ss_sp), &uss_ptr->ss_sp) || + __get_user((u32)uss.ss_flags, &uss_ptr->ss_flags) || + __get_user((u32)uss.ss_size, &uss_ptr->ss_size)) + return -EFAULT; + seg = get_fs(); + set_fs(KERNEL_DS); + ret = do_sigaltstack(&uss, &uoss, regs.rsp); + set_fs(seg); + if (ret >= 0 && uoss_ptr) { + if (!access_ok(VERIFY_WRITE,uss_ptr,sizeof(stack_ia32_t)) || + __put_user(ptr_to_u32(uss.ss_sp), &uss_ptr->ss_sp) || + __put_user((u32)uss.ss_flags, &uss_ptr->ss_flags) || + __put_user((u32)uss.ss_size, &uss_ptr->ss_size)) + ret = -EFAULT; + } + return ret; +} + +/* + * Do a signal return; undo the signal stack. + */ + +struct sigframe +{ + u32 pretcode; + int sig; + struct sigcontext_ia32 sc; + struct _fpstate_ia32 fpstate; + unsigned int extramask[_IA32_NSIG_WORDS-1]; + char retcode[8]; +}; + +struct rt_sigframe +{ + u32 pretcode; + int sig; + u32 pinfo; + u32 puc; + struct siginfo32 info; + struct ucontext_ia32 uc; + struct _fpstate_ia32 fpstate; + char retcode[8]; +}; + +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext_ia32 *sc, unsigned int *peax) +{ + unsigned int err = 0; + +#if DEBUG_SIG + printk("SIG restore_sigcontext: sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n", + sc, sc->err, sc->eip, sc->cs, sc->eflags); +#endif +#define COPY(x) { \ + unsigned int reg; \ + err |= __get_user(reg, &sc->e ##x); \ + regs->r ## x = reg; \ +} + +#define RELOAD_SEG(seg) \ + { unsigned int cur; \ + unsigned short pre; \ + err |= __get_user(pre, &sc->seg); \ + asm volatile("movl %%" #seg ",%0" : "=r" (cur)); \ + if (pre != cur) loadsegment(seg,pre); } + + /* Reload fs and gs if they have changed in the signal handler. + This does not handle long fs/gs base changes in the handler, but does not clobber + them at least in the normal case. */ + RELOAD_SEG(gs); + RELOAD_SEG(fs); + + COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx); + COPY(dx); COPY(cx); COPY(ip); + /* Don't touch extended registers */ + + { + unsigned int tmpflags; + err |= __get_user(tmpflags, &sc->eflags); + regs->eflags = (regs->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); + regs->orig_rax = -1; /* disable syscall checks */ + } + + { + u32 tmp; + struct _fpstate * buf; + err |= __get_user(tmp, &sc->fpstate); + buf = (struct _fpstate *) (u64)tmp; + if (buf) { + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + err |= restore_i387(buf); + } + } + + { + u32 tmp; + err |= __get_user(tmp, &sc->eax); + *peax = tmp; + } + return err; + +badframe: + return 1; +} + +asmlinkage int sys32_sigreturn(struct pt_regs regs) +{ + struct sigframe *frame = (struct sigframe *)(regs.rsp - 8); + sigset_t set; + unsigned int eax; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.oldmask) + || (_IA32_NSIG_WORDS > 1 + && __copy_from_user((((char *) &set.sig) + 4), &frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(®s, &frame->sc, &eax)) + goto badframe; + return eax; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int sys32_rt_sigreturn(struct pt_regs regs) +{ + struct rt_sigframe *frame = (struct rt_sigframe *)(regs.rsp - 4); + sigset_t set; + stack_t st; + unsigned int eax; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &eax)) + goto badframe; + + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + { + mm_segment_t oldds = get_fs(); + set_fs(KERNEL_DS); + do_sigaltstack(&st, NULL, regs.rsp); + set_fs(oldds); + } + + return eax; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + +static int +setup_sigcontext(struct sigcontext_ia32 *sc, struct _fpstate_ia32 *fpstate, + struct pt_regs *regs, unsigned int mask) +{ + int tmp, err = 0; + + tmp = 0; + __asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp)); + err |= __put_user(tmp, (unsigned int *)&sc->gs); + __asm__("movl %%fs,%0" : "=r"(tmp): "0"(tmp)); + err |= __put_user(tmp, (unsigned int *)&sc->fs); + + err |= __put_user((u32)regs->rdi, &sc->edi); + err |= __put_user((u32)regs->rsi, &sc->esi); + err |= __put_user((u32)regs->rbp, &sc->ebp); + err |= __put_user((u32)regs->rsp, &sc->esp); + err |= __put_user((u32)regs->rbx, &sc->ebx); + err |= __put_user((u32)regs->rdx, &sc->edx); + err |= __put_user((u32)regs->rcx, &sc->ecx); + err |= __put_user((u32)regs->rax, &sc->eax); + err |= __put_user(current->thread.trap_no, &sc->trapno); + err |= __put_user(current->thread.error_code, &sc->err); + err |= __put_user((u32)regs->rip, &sc->eip); + err |= __put_user((u32)regs->eflags, &sc->eflags); + err |= __put_user((u32)regs->rsp, &sc->esp_at_signal); + + tmp = save_i387(fpstate); + if (tmp < 0) + err = -EFAULT; + else + err |= __put_user((u32)(u64)(tmp ? fpstate : NULL), &sc->fpstate); + + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + err |= __put_user(current->thread.cr2, &sc->cr2); + + return err; +} + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long rsp; + + /* Default to using normal stack */ + rsp = regs->rsp; + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! on_sig_stack(rsp)) + rsp = current->sas_ss_sp + current->sas_ss_size; + } + + /* This is the legacy signal stack switching. */ + else if ((regs->ss & 0xffff) != __USER_DS && + !(ka->sa.sa_flags & SA_RESTORER) && + ka->sa.sa_restorer) { + rsp = (unsigned long) ka->sa.sa_restorer; + } + + return (void *)((rsp - frame_size) & -8UL); +} + +void ia32_setup_frame(int sig, struct k_sigaction *ka, + sigset32_t *set, struct pt_regs * regs) +{ + struct sigframe *frame; + int err = 0; + struct exec_domain *exec_domain = current_thread_info()->exec_domain; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= __put_user((exec_domain + && exec_domain->signal_invmap + && sig < 32 + ? exec_domain->signal_invmap[sig] + : sig), + &frame->sig); + if (err) + goto give_sigsegv; + + err |= setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]); + if (err) + goto give_sigsegv; + + if (_IA32_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + err |= __put_user((u32)(u64)ka->sa.sa_restorer, &frame->pretcode); + } else { + err |= __put_user((u32)(u64)frame->retcode, &frame->pretcode); + /* This is popl %eax ; movl $,%eax ; int $0x80 */ + err |= __put_user((u16)0xb858, (short *)(frame->retcode+0)); + err |= __put_user((u32)__NR_ia32_sigreturn, (int *)(frame->retcode+2)); + err |= __put_user((u16)0x80cd, (short *)(frame->retcode+6)); + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->rsp = (unsigned long) frame; + regs->rip = (unsigned long) ka->sa.sa_handler; + + set_fs(USER_DS); + // XXX: cs + regs->eflags &= ~TF_MASK; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->rip, frame->pretcode); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +void ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset32_t *set, struct pt_regs * regs) +{ + struct rt_sigframe *frame; + int err = 0; + struct exec_domain *exec_domain = current_thread_info()->exec_domain; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= __put_user((exec_domain + && exec_domain->signal_invmap + && sig < 32 + ? exec_domain->signal_invmap[sig] + : sig), + &frame->sig); + err |= __put_user((u32)(u64)&frame->info, &frame->pinfo); + err |= __put_user((u32)(u64)&frame->uc, &frame->puc); + err |= ia32_copy_siginfo_to_user(&frame->info, info); + if (err) + goto give_sigsegv; + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->rsp), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, + regs, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + err |= __put_user((u32)(u64)ka->sa.sa_restorer, &frame->pretcode); + } else { + err |= __put_user(ptr_to_u32(frame->retcode), &frame->pretcode); + /* This is movl $,%eax ; int $0x80 */ + err |= __put_user(0xb8, (char *)(frame->retcode+0)); + err |= __put_user((u32)__NR_ia32_rt_sigreturn, (int *)(frame->retcode+1)); + err |= __put_user(0x80cd, (short *)(frame->retcode+5)); + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->rsp = (unsigned long) frame; + regs->rip = (unsigned long) ka->sa.sa_handler; + + set_fs(USER_DS); + // XXX: cs + regs->eflags &= ~TF_MASK; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->rip, frame->pretcode); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + diff -Nru a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/ia32/ia32entry.S Tue Feb 19 18:09:01 2002 @@ -0,0 +1,365 @@ +/* + * Compatibility mode system call entry point for x86-64. + * + * Copyright 2000,2001,2002 Andi Kleen, SuSE Labs. + * + * $Id: ia32entry.S,v 1.24 2001/11/11 17:47:47 ak Exp $ + */ + +#include +#include +#include +#include +#include +#include + + .macro IA32_ARG_FIXUP + movl %edi,%r8d + movl %ebp,%r9d + xchg %ecx,%esi + movl %ebx,%edi + movl %edx,%edx /* zero extension */ + .endm + +/* + * 32bit SYSCALL instruction entry. + * It'll probably kill you because it destroys your segments. + * Should coredump here, but the next instruction will likely do + * that anyways. + */ +ENTRY(ia32_cstar_target) + movq $-ENOSYS,%rax + SYSRET32 + +/* + * Emulated IA32 system calls via int 0x80. + * + * Arguments: + * %eax System call number. + * %ebx Arg1 + * %ecx Arg2 + * %edx Arg3 + * %esi Arg4 + * %edi Arg5 + * %ebp Arg6 [note: not saved in the stack frame, should not be touched] + * + * Notes: + * Uses the same stack frame as the x86-64 version. + * All registers except %eax must be saved (but ptrace may violate that) + * Arguments are zero extended. For system calls that want sign extension and + * take long arguments a wrapper is needed. Most calls can just be called + * directly. + * Assumes it is only called from user space and entered with interrups off. + */ + +ENTRY(ia32_syscall) + swapgs + sti + pushq %rax + cld + SAVE_ARGS + GET_THREAD_INFO(%r10) + bt $TIF_SYSCALL_TRACE,threadinfo_flags(%r10) + jc ia32_tracesys +ia32_do_syscall: + cmpl $(IA32_NR_syscalls),%eax + jae ia32_badsys + IA32_ARG_FIXUP + movl $1,%r10d + call *ia32_sys_call_table(,%rax,8) # xxx: rip relative + movq %rax,RAX-ARGOFFSET(%rsp) + jmp int_ret_from_sys_call + +ia32_tracesys: + SAVE_REST + movq $-ENOSYS,RAX(%rsp) /* really needed? */ + movq %rsp,%rdi /* &pt_regs -> arg1 */ + call syscall_trace + LOAD_ARGS ARGOFFSET /* reload args from stack in case ptrace changed it */ + RESTORE_REST + jmp ia32_do_syscall + +ia32_badsys: + movq $-ENOSYS,RAX-ARGOFFSET(%rsp) + jmp int_ret_from_sys_call + +ni_syscall: + movq %rax,%rdi + jmp sys32_ni_syscall + + .macro PTREGSCALL label, func + .globl \label +\label: + leaq \func(%rip),%rax + jmp ia32_ptregs_common + .endm + + PTREGSCALL stub32_rt_sigreturn, sys32_rt_sigreturn + PTREGSCALL stub32_sigreturn, sys32_sigreturn + PTREGSCALL stub32_sigaltstack, sys32_sigaltstack + PTREGSCALL stub32_sigsuspend, sys32_sigsuspend + PTREGSCALL stub32_execve, sys32_execve + PTREGSCALL stub32_fork, sys32_fork + PTREGSCALL stub32_clone, sys32_clone + PTREGSCALL stub32_vfork, sys32_vfork + PTREGSCALL stub32_iopl, sys_iopl + PTREGSCALL stub32_rt_sigsuspend, sys_rt_sigsuspend + +ENTRY(ia32_ptregs_common) + popq %r11 /* save return address outside the stack frame. */ + SAVE_REST + movq %r11, %r15 + call *%rax + movq %r15, %r11 + RESTORE_REST + pushq %r11 + ret + + .data + .align 8 +ia32_sys_call_table: + .quad ni_syscall /* 0 - old "setup" system call*/ + .quad sys_exit + .quad stub32_fork + .quad sys_read + .quad sys_write + .quad sys_open /* 5 */ + .quad sys_close + .quad sys32_waitpid + .quad sys_creat + .quad sys_link + .quad sys_unlink /* 10 */ + .quad stub32_execve + .quad sys_chdir + .quad sys32_time + .quad sys_mknod + .quad sys_chmod /* 15 */ + .quad sys_lchown16 + .quad ni_syscall /* old break syscall holder */ + .quad ni_syscall /* (old)stat */ + .quad sys32_lseek + .quad sys_getpid /* 20 */ + .quad sys_mount /* mount */ + .quad sys_oldumount /* old_umount */ + .quad sys_setuid16 + .quad sys_getuid16 + .quad ni_syscall /* stime */ /* 25 */ + .quad sys32_ptrace /* ptrace */ + .quad sys_alarm /* XXX sign extension??? */ + .quad ni_syscall /* (old)fstat */ + .quad sys_pause + .quad sys32_utime /* 30 */ + .quad ni_syscall /* old stty syscall holder */ + .quad ni_syscall /* old gtty syscall holder */ + .quad sys_access + .quad sys_nice + .quad ni_syscall /* 35 */ /* old ftime syscall holder */ + .quad sys_sync + .quad sys32_kill + .quad sys_rename + .quad sys_mkdir + .quad sys_rmdir /* 40 */ + .quad sys_dup + .quad sys32_pipe + .quad sys32_times + .quad ni_syscall /* old prof syscall holder */ + .quad sys_brk /* 45 */ + .quad sys_setgid16 + .quad sys_getgid16 + .quad ni_syscall /* signal */ + .quad sys_geteuid16 + .quad sys_getegid16 /* 50 */ + .quad sys_acct + .quad sys_umount /* new_umount */ + .quad ni_syscall /* old lock syscall holder */ + .quad sys32_ioctl + .quad sys32_fcntl /* 55 */ + .quad ni_syscall /* old mpx syscall holder */ + .quad sys_setpgid + .quad ni_syscall /* old ulimit syscall holder */ + .quad sys32_olduname + .quad sys_umask /* 60 */ + .quad sys_chroot + .quad sys32_ustat + .quad sys_dup2 + .quad sys_getppid + .quad sys_getpgrp /* 65 */ + .quad sys_setsid + .quad sys32_sigaction + .quad sys_sgetmask + .quad sys_ssetmask + .quad sys_setreuid16 /* 70 */ + .quad sys_setregid16 + .quad stub32_sigsuspend + .quad sys32_sigpending + .quad sys_sethostname + .quad sys32_setrlimit /* 75 */ + .quad sys32_old_getrlimit /* old_getrlimit */ + .quad sys32_getrusage + .quad sys32_gettimeofday + .quad sys32_settimeofday + .quad sys_getgroups16 /* 80 */ + .quad sys_setgroups16 + .quad sys32_old_select + .quad sys_symlink + .quad ni_syscall /* (old)lstat */ + .quad sys_readlink /* 85 */ + .quad sys_uselib + .quad sys_swapon + .quad sys_reboot + .quad sys32_oldreaddir + .quad sys32_mmap /* 90 */ + .quad sys_munmap + .quad sys_truncate + .quad sys_ftruncate + .quad sys_fchmod + .quad sys_fchown16 /* 95 */ + .quad sys_getpriority + .quad sys_setpriority + .quad ni_syscall /* old profil syscall holder */ + .quad sys32_statfs + .quad sys32_fstatfs /* 100 */ + .quad sys_ioperm + .quad sys32_socketcall + .quad sys_syslog + .quad sys32_setitimer + .quad sys32_getitimer /* 105 */ + .quad sys32_newstat + .quad sys32_newlstat + .quad sys32_newfstat + .quad sys32_uname + .quad stub32_iopl /* 110 */ + .quad sys_vhangup + .quad ni_syscall /* old "idle" system call */ + .quad ni_syscall /* vm86old */ + .quad sys32_wait4 + .quad sys_swapoff /* 115 */ + .quad sys32_sysinfo + .quad sys32_ipc + .quad sys_fsync + .quad stub32_sigreturn + .quad stub32_clone /* 120 */ + .quad sys_setdomainname + .quad sys_newuname + .quad ni_syscall /* modify_ldt */ + .quad sys32_adjtimex + .quad sys_mprotect /* 125 */ + .quad sys32_sigprocmask + .quad ni_syscall /* query_module */ + .quad ni_syscall /* init_module */ + .quad ni_syscall /* delete module */ + .quad ni_syscall /* 130 get_kernel_syms */ + .quad ni_syscall /* quotactl */ + .quad sys_getpgid + .quad sys_fchdir + .quad ni_syscall /* bdflush */ + .quad sys_sysfs /* 135 */ + .quad sys_personality + .quad ni_syscall /* for afs_syscall */ + .quad sys_setfsuid16 + .quad sys_setfsgid16 + .quad sys_llseek /* 140 */ + .quad sys32_getdents + .quad sys32_select + .quad sys_flock + .quad sys_msync + .quad sys32_readv /* 145 */ + .quad sys32_writev + .quad sys_getsid + .quad sys_fdatasync + .quad sys32_sysctl /* sysctl */ + .quad sys_mlock /* 150 */ + .quad sys_munlock + .quad sys_mlockall + .quad sys_munlockall + .quad sys_sched_setparam + .quad sys_sched_getparam /* 155 */ + .quad sys_sched_setscheduler + .quad sys_sched_getscheduler + .quad sys_sched_yield + .quad sys_sched_get_priority_max + .quad sys_sched_get_priority_min /* 160 */ + .quad sys_sched_rr_get_interval + .quad sys32_nanosleep + .quad sys_mremap + .quad sys_setresuid16 + .quad sys_getresuid16 /* 165 */ + .quad ni_syscall /* vm86 */ + .quad ni_syscall /* query_module */ + .quad sys_poll + .quad ni_syscall /* nfsserverctl */ + .quad sys_setresgid16 /* 170 */ + .quad sys_getresgid16 + .quad sys_prctl + .quad stub32_rt_sigreturn + .quad sys32_rt_sigaction + .quad sys32_rt_sigprocmask /* 175 */ + .quad sys32_rt_sigpending + .quad sys32_rt_sigtimedwait + .quad sys32_rt_sigqueueinfo + .quad stub32_rt_sigsuspend + .quad sys32_pread /* 180 */ + .quad sys32_pwrite + .quad sys_chown16 + .quad sys_getcwd + .quad ni_syscall /* capget */ + .quad ni_syscall /* capset */ + .quad stub32_sigaltstack + .quad sys32_sendfile + .quad ni_syscall /* streams1 */ + .quad ni_syscall /* streams2 */ + .quad stub32_vfork /* 190 */ + .quad sys32_getrlimit + .quad sys32_mmap2 + .quad sys_truncate + .quad sys_ftruncate + .quad sys32_stat64 /* 195 */ + .quad sys32_lstat64 + .quad sys32_fstat64 + .quad sys_lchown + .quad sys_getuid + .quad sys_getgid /* 200 */ + .quad sys_geteuid + .quad sys_getegid + .quad sys32_setreuid + .quad sys32_setregid + .quad sys32_getgroups /* 205 */ + .quad sys32_setgroups + .quad sys_fchown + .quad sys32_setresuid + .quad sys32_getresuid + .quad sys32_setresgid /* 210 */ + .quad sys32_getresgid + .quad sys_chown + .quad sys_setuid + .quad sys_setgid + .quad sys_setfsuid /* 215 */ + .quad sys_setfsgid + .quad sys_pivot_root + .quad sys_mincore + .quad sys_madvise + .quad sys_getdents64 /* 220 */ + .quad sys32_fcntl64 + .quad sys_ni_syscall /* tux */ + .quad sys_ni_syscall /* security */ + .quad sys_gettid + .quad sys_readahead /* 225 */ + .quad sys_setxattr + .quad sys_lsetxattr + .quad sys_fsetxattr + .quad sys_getxattr + .quad sys_lgetxattr /* 230 */ + .quad sys_fgetxattr + .quad sys_listxattr + .quad sys_llistxattr + .quad sys_flistxattr + .quad sys_removexattr /* 235 */ + .quad sys_lremovexattr + .quad sys_fremovexattr + .quad sys_tkill /* 238 */ +ia32_syscall_end: + .rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8 + .quad ni_syscall + .endr + + diff -Nru a/arch/x86_64/ia32/ptrace32.c b/arch/x86_64/ia32/ptrace32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/ia32/ptrace32.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,312 @@ +/* + * 32bit ptrace for x86-64. + * + * Copyright 2001 Andi Kleen, SuSE Labs. + * Some parts copied from arch/i386/kernel/ptrace.c. See that file for + * earlier copyright. + * + * This allows to access 64bit processes too but there is no way to see + * the extended register contents. + * + * $Id: ptrace32.c,v 1.2 2001/08/15 06:41:13 ak Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define R32(l,q) \ + case offsetof(struct user32, regs.l): stack[offsetof(struct pt_regs, q)/8] = val; break + +static int putreg32(struct task_struct *child, unsigned regno, u32 val) +{ + int i; + __u64 *stack = (__u64 *)(child->thread.rsp0 - sizeof(struct pt_regs)); + + switch (regno) { + case offsetof(struct user32, regs.fs): + child->thread.fs = val; + break; + case offsetof(struct user32, regs.gs): + child->thread.gs = val; + break; + case offsetof(struct user32, regs.ds): + child->thread.ds = val; + break; + case offsetof(struct user32, regs.es): + child->thread.es = val; + break; + + R32(cs, cs); + R32(ss, ss); + R32(ebx, rbx); + R32(ecx, rcx); + R32(edx, rdx); + R32(edi, rdi); + R32(esi, rsi); + R32(ebp, rbp); + R32(eax, rax); + R32(orig_eax, orig_rax); + R32(eip, rip); + R32(esp, rsp); + + case offsetof(struct user32, regs.eflags): + stack[offsetof(struct pt_regs, eflags)/8] = val & 0x44dd5; + break; + + case offsetof(struct user32, u_debugreg[0]) ... offsetof(struct user32, u_debugreg[6]): + child->thread.debugreg[(regno-offsetof(struct user32, u_debugreg[0]))/4] = val; + break; + + case offsetof(struct user32, u_debugreg[7]): + val &= ~DR_CONTROL_RESERVED; + /* You are not expected to understand this ... I don't neither. */ + for(i=0; i<4; i++) + if ((0x5454 >> ((val >> (16 + 4*i)) & 0xf)) & 1) + return -EIO; + child->thread.debugreg[7] = val; + break; + + default: + if (regno > sizeof(struct user32) || (regno & 3)) + return -EIO; + + /* Other dummy fields in the virtual user structure are ignored */ + break; + } + return 0; +} + +#undef R32 + +#define R32(l,q) \ + case offsetof(struct user32, regs.l): *val = stack[offsetof(struct pt_regs, q)/8]; break + +static int getreg32(struct task_struct *child, unsigned regno, u32 *val) +{ + __u64 *stack = (__u64 *)(child->thread.rsp0 - sizeof(struct pt_regs)); + + switch (regno) { + case offsetof(struct user32, regs.fs): + *val = child->thread.fs; + break; + case offsetof(struct user32, regs.gs): + *val = child->thread.gs; + break; + case offsetof(struct user32, regs.ds): + *val = child->thread.ds; + break; + case offsetof(struct user32, regs.es): + *val = child->thread.es; + break; + + R32(cs, cs); + R32(ss, ss); + R32(ebx, rbx); + R32(ecx, rcx); + R32(edx, rdx); + R32(edi, rdi); + R32(esi, rsi); + R32(ebp, rbp); + R32(eax, rax); + R32(orig_eax, orig_rax); + R32(eip, rip); + R32(eflags, eflags); + R32(esp, rsp); + + case offsetof(struct user32, u_debugreg[0]) ... offsetof(struct user32, u_debugreg[7]): + *val = child->thread.debugreg[(regno-offsetof(struct user32, u_debugreg[0]))/4]; + break; + + default: + if (regno > sizeof(struct user32) || (regno & 3)) + return -EIO; + + /* Other dummy fields in the virtual user structure are ignored */ + *val = 0; + break; + } + return 0; +} + +#undef R32 + + +static struct task_struct *find_target(int request, int pid, int *err) +{ + struct task_struct *child; + + *err = -EPERM; + if (pid == 1) + return NULL; + + *err = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (child) { + *err = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out; + } + if (child->p_pptr != current) + goto out; + + return child; + } + out: + put_task_struct(child); + return NULL; + +} + +extern asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, unsigned long data); + +asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data) +{ + struct task_struct *child; + int ret; + __u32 val; + + switch (request) { + case PTRACE_TRACEME: + case PTRACE_ATTACH: + case PTRACE_SYSCALL: + case PTRACE_CONT: + case PTRACE_KILL: + case PTRACE_SINGLESTEP: + case PTRACE_DETACH: + case PTRACE_SETOPTIONS: + ret = sys_ptrace(request, pid, addr, data); + return ret; + + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + case PTRACE_POKEDATA: + case PTRACE_POKETEXT: + case PTRACE_POKEUSR: + case PTRACE_PEEKUSR: + case PTRACE_GETREGS: + case PTRACE_SETREGS: + case PTRACE_SETFPREGS: + case PTRACE_GETFPREGS: + break; + + default: + return -EIO; + } + + child = find_target(request, pid, &ret); + if (!child) + return ret; + + switch (request) { + case PTRACE_PEEKDATA: + case PTRACE_PEEKTEXT: + ret = 0; + if (access_process_vm(child, addr, &val, sizeof(u32), 0) != sizeof(u32)) + ret = -EIO; + else + ret = put_user(val, (unsigned int *)(u64)data); + break; + + case PTRACE_POKEDATA: + case PTRACE_POKETEXT: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(u32), 1) != sizeof(u32)) + ret = -EIO; + break; + + case PTRACE_PEEKUSR: + ret = getreg32(child, addr, &val); + if (ret >= 0) + ret = put_user(val, (__u32 *)(unsigned long) data); + break; + + case PTRACE_POKEUSR: + ret = putreg32(child, addr, data); + break; + + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + int i; + if (!access_ok(VERIFY_WRITE, (unsigned *)(unsigned long)data, FRAME_SIZE)) { + ret = -EIO; + break; + } + ret = 0; + for ( i = 0; i <= 16*4 ; i += sizeof(__u32) ) { + getreg32(child, i, &val); + ret |= __put_user(val,(u32 *) (unsigned long) data); + data += sizeof(u32); + } + break; + } + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + int i; + if (!access_ok(VERIFY_READ, (unsigned *)(unsigned long)data, FRAME_SIZE)) { + ret = -EIO; + break; + } + ret = 0; + for ( i = 0; i <= 16*4; i += sizeof(u32) ) { + ret |= __get_user(tmp, (u32 *) (unsigned long) data); + putreg32(child, i, tmp); + data += sizeof(u32); + } + break; + } + +#if 0 /* to be done. */ + case PTRACE_GETFPREGS: { /* Get the child extended FPU state. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, + sizeof(struct user_i387_struct))) { + ret = -EIO; + break; + } + if ( !child->used_math ) { + /* Simulate an empty FPU. */ + set_fpu_cwd(child, 0x037f); + set_fpu_swd(child, 0x0000); + set_fpu_twd(child, 0xffff); + set_fpu_mxcsr(child, 0x1f80); + } + ret = get_fpregs((struct user_i387_struct *)data, child); + break; + } + + case PTRACE_SETFPREGS: { /* Set the child extended FPU state. */ + if (!access_ok(VERIFY_READ, (unsigned *)data, + sizeof(struct user_i387_struct))) { + ret = -EIO; + break; + } + child->used_math = 1; + ret = set_fpregs(child, (struct user_i387_struct *)data); + break; + +#endif + + default: + ret = -EINVAL; + break; + } + + put_task_struct(child); + return ret; +} + diff -Nru a/arch/x86_64/ia32/socket32.c b/arch/x86_64/ia32/socket32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/ia32/socket32.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,686 @@ +/* + * 32bit Socket syscall emulation. Based on arch/sparc64/kernel/sys_sparc32.c. + * + * Copyright (C) 2000 VA Linux Co + * Copyright (C) 2000 Don Dugger + * Copyright (C) 1999 Arun Sharma + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 2000 Hewlett-Packard Co. + * Copyright (C) 2000 David Mosberger-Tang + * Copyright (C) 2000,2001 Andi Kleen, SuSE Labs + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) ((unsigned long)(__x)) + + +static inline int iov_from_user32_to_kern(struct iovec *kiov, + struct iovec32 *uiov32, + int niov) +{ + int tot_len = 0; + + while(niov > 0) { + u32 len, buf; + + if(get_user(len, &uiov32->iov_len) || + get_user(buf, &uiov32->iov_base)) { + tot_len = -EFAULT; + break; + } + tot_len += len; + kiov->iov_base = (void *)A(buf); + kiov->iov_len = (__kernel_size_t) len; + uiov32++; + kiov++; + niov--; + } + return tot_len; +} + +static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, + struct msghdr32 *umsg) +{ + u32 tmp1, tmp2, tmp3; + int err; + + err = get_user(tmp1, &umsg->msg_name); + err |= __get_user(tmp2, &umsg->msg_iov); + err |= __get_user(tmp3, &umsg->msg_control); + if (err) + return -EFAULT; + + kmsg->msg_name = (void *)A(tmp1); + kmsg->msg_iov = (struct iovec *)A(tmp2); + kmsg->msg_control = (void *)A(tmp3); + + err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); + err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen); + err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); + err |= get_user(kmsg->msg_flags, &umsg->msg_flags); + + return err; +} + +/* I've named the args so it is easy to tell whose space the pointers are in. */ +static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, + char *kern_address, int mode) +{ + int tot_len; + + if(kern_msg->msg_namelen) { + if(mode==VERIFY_READ) { + int err = move_addr_to_kernel(kern_msg->msg_name, + kern_msg->msg_namelen, + kern_address); + if(err < 0) + return err; + } + kern_msg->msg_name = kern_address; + } else + kern_msg->msg_name = NULL; + + if(kern_msg->msg_iovlen > UIO_FASTIOV) { + kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), + GFP_KERNEL); + if(!kern_iov) + return -ENOMEM; + } + + tot_len = iov_from_user32_to_kern(kern_iov, + (struct iovec32 *)kern_msg->msg_iov, + kern_msg->msg_iovlen); + if(tot_len >= 0) + kern_msg->msg_iov = kern_iov; + else if(kern_msg->msg_iovlen > UIO_FASTIOV) + kfree(kern_iov); + + return tot_len; +} + +/* There is a lot of hair here because the alignment rules (and + * thus placement) of cmsg headers and length are different for + * 32-bit apps. -DaveM + */ +static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg, + unsigned char *stackbuf, int stackbuf_size) +{ + struct cmsghdr32 *ucmsg; + struct cmsghdr *kcmsg, *kcmsg_base; + __kernel_size_t32 ucmlen; + __kernel_size_t kcmlen, tmp; + + kcmlen = 0; + kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + if(get_user(ucmlen, &ucmsg->cmsg_len)) + return -EFAULT; + + /* Catch bogons. */ + if(CMSG32_ALIGN(ucmlen) < + CMSG32_ALIGN(sizeof(struct cmsghdr32))) + return -EINVAL; + if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) + + ucmlen) > kmsg->msg_controllen) + return -EINVAL; + + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmlen += tmp; + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + if(kcmlen == 0) + return -EINVAL; + + /* The kcmlen holds the 64-bit version of the control length. + * It may not be modified as we do not stick it into the kmsg + * until we have successfully copied over all of the data + * from the user. + */ + if(kcmlen > stackbuf_size) + kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); + if(kcmsg == NULL) + return -ENOBUFS; + + /* Now copy them over neatly. */ + memset(kcmsg, 0, kcmlen); + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + __get_user(ucmlen, &ucmsg->cmsg_len); + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmsg->cmsg_len = tmp; + __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); + + /* Copy over the data. */ + if(copy_from_user(CMSG_DATA(kcmsg), + CMSG32_DATA(ucmsg), + (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))))) + goto out_free_efault; + + /* Advance. */ + kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + + /* Ok, looks like we made it. Hook it up and return success. */ + kmsg->msg_control = kcmsg_base; + kmsg->msg_controllen = kcmlen; + return 0; + +out_free_efault: + if(kcmsg_base != (struct cmsghdr *)stackbuf) + kfree(kcmsg_base); + return -EFAULT; +} + +static void put_cmsg32(struct msghdr *kmsg, int level, int type, + int len, void *data) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + struct cmsghdr32 cmhdr; + int cmlen = CMSG32_LEN(len); + + if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { + kmsg->msg_flags |= MSG_CTRUNC; + return; + } + + if(kmsg->msg_controllen < cmlen) { + kmsg->msg_flags |= MSG_CTRUNC; + cmlen = kmsg->msg_controllen; + } + cmhdr.cmsg_level = level; + cmhdr.cmsg_type = type; + cmhdr.cmsg_len = cmlen; + + if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) + return; + if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32))) + return; + cmlen = CMSG32_SPACE(len); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; +} + +static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int); + int fdnum = scm->fp->count; + struct file **fp = scm->fp->fp; + int *cmfptr; + int err = 0, i; + + if (fdnum < fdmax) + fdmax = fdnum; + + for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) { + int new_fd; + err = get_unused_fd(); + if (err < 0) + break; + new_fd = err; + err = put_user(new_fd, cmfptr); + if (err) { + put_unused_fd(new_fd); + break; + } + /* Bump the usage count and install the file. */ + get_file(fp[i]); + fd_install(new_fd, fp[i]); + } + + if (i > 0) { + int cmlen = CMSG32_LEN(i * sizeof(int)); + if (!err) + err = put_user(SOL_SOCKET, &cm->cmsg_level); + if (!err) + err = put_user(SCM_RIGHTS, &cm->cmsg_type); + if (!err) + err = put_user(cmlen, &cm->cmsg_len); + if (!err) { + cmlen = CMSG32_SPACE(i * sizeof(int)); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; + } + } + if (i < fdnum) + kmsg->msg_flags |= MSG_CTRUNC; + + /* + * All of the files that fit in the message have had their + * usage counts incremented, so we just free the list. + */ + __scm_destroy(scm); +} + +/* In these cases we (currently) can just copy to data over verbatim + * because all CMSGs created by the kernel have well defined types which + * have the same layout in both the 32-bit and 64-bit API. One must add + * some special cased conversions here if we start sending control messages + * with incompatible types. + * + * SCM_RIGHTS and SCM_CREDENTIALS are done by hand in recvmsg32 right after + * we do our work. The remaining cases are: + * + * SOL_IP IP_PKTINFO struct in_pktinfo 32-bit clean + * IP_TTL int 32-bit clean + * IP_TOS __u8 32-bit clean + * IP_RECVOPTS variable length 32-bit clean + * IP_RETOPTS variable length 32-bit clean + * (these last two are clean because the types are defined + * by the IPv4 protocol) + * IP_RECVERR struct sock_extended_err + + * struct sockaddr_in 32-bit clean + * SOL_IPV6 IPV6_RECVERR struct sock_extended_err + + * struct sockaddr_in6 32-bit clean + * IPV6_PKTINFO struct in6_pktinfo 32-bit clean + * IPV6_HOPLIMIT int 32-bit clean + * IPV6_FLOWINFO u32 32-bit clean + * IPV6_HOPOPTS ipv6 hop exthdr 32-bit clean + * IPV6_DSTOPTS ipv6 dst exthdr(s) 32-bit clean + * IPV6_RTHDR ipv6 routing exthdr 32-bit clean + * IPV6_AUTHHDR ipv6 auth exthdr 32-bit clean + */ +static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_uptr) +{ + unsigned char *workbuf, *wp; + unsigned long bufsz, space_avail; + struct cmsghdr *ucmsg; + + bufsz = ((unsigned long)kmsg->msg_control) - orig_cmsg_uptr; + space_avail = kmsg->msg_controllen + bufsz; + wp = workbuf = kmalloc(bufsz, GFP_KERNEL); + if(workbuf == NULL) + goto fail; + + /* To make this more sane we assume the kernel sends back properly + * formatted control messages. Because of how the kernel will truncate + * the cmsg_len for MSG_TRUNC cases, we need not check that case either. + */ + ucmsg = (struct cmsghdr *) orig_cmsg_uptr; + while(((unsigned long)ucmsg) <= + (((unsigned long)kmsg->msg_control) - sizeof(struct cmsghdr))) { + struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp; + int clen64, clen32; + + /* UCMSG is the 64-bit format CMSG entry in user-space. + * KCMSG32 is within the kernel space temporary buffer + * we use to convert into a 32-bit style CMSG. + */ + __get_user(kcmsg32->cmsg_len, &ucmsg->cmsg_len); + __get_user(kcmsg32->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg32->cmsg_type, &ucmsg->cmsg_type); + + clen64 = kcmsg32->cmsg_len; + copy_from_user(CMSG32_DATA(kcmsg32), CMSG_DATA(ucmsg), + clen64 - CMSG_ALIGN(sizeof(*ucmsg))); + clen32 = ((clen64 - CMSG_ALIGN(sizeof(*ucmsg))) + + CMSG32_ALIGN(sizeof(struct cmsghdr32))); + kcmsg32->cmsg_len = clen32; + + ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64)); + wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32)); + } + + /* Copy back fixed up data, and adjust pointers. */ + bufsz = (wp - workbuf); + copy_to_user((void *)orig_cmsg_uptr, workbuf, bufsz); + + kmsg->msg_control = (struct cmsghdr *) + (((char *)orig_cmsg_uptr) + bufsz); + kmsg->msg_controllen = space_avail - bufsz; + + kfree(workbuf); + return; + +fail: + /* If we leave the 64-bit format CMSG chunks in there, + * the application could get confused and crash. So to + * ensure greater recovery, we report no CMSGs. + */ + kmsg->msg_controllen += bufsz; + kmsg->msg_control = (void *) orig_cmsg_uptr; +} + +asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags) +{ + struct socket *sock; + char address[MAX_SOCK_ADDR]; + struct iovec iov[UIO_FASTIOV]; + unsigned char ctl[sizeof(struct cmsghdr) + 20]; + unsigned char *ctl_buf = ctl; + struct msghdr kern_msg; + int err, total_len; + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); + if (err < 0) + goto out; + total_len = err; + + if(kern_msg.msg_controllen) { + err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl)); + if(err) + goto out_freeiov; + ctl_buf = kern_msg.msg_control; + } + kern_msg.msg_flags = user_flags; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + kern_msg.msg_flags |= MSG_DONTWAIT; + err = sock_sendmsg(sock, &kern_msg, total_len); + sockfd_put(sock); + } + + /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ + if(ctl_buf != ctl) + kfree(ctl_buf); +out_freeiov: + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + return err; +} + +asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int user_flags) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct msghdr kern_msg; + char addr[MAX_SOCK_ADDR]; + struct socket *sock; + struct iovec *iov = iovstack; + struct sockaddr *uaddr; + int *uaddr_len; + unsigned long cmsg_ptr; + int err, total_len, len = 0; + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + + uaddr = kern_msg.msg_name; + uaddr_len = &user_msg->msg_namelen; + err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); + if (err < 0) + goto out; + total_len = err; + + cmsg_ptr = (unsigned long) kern_msg.msg_control; + kern_msg.msg_flags = 0; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + struct scm_cookie scm; + + if (sock->file->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; + memset(&scm, 0, sizeof(scm)); + err = sock->ops->recvmsg(sock, &kern_msg, total_len, + user_flags, &scm); + if(err >= 0) { + len = err; + if(!kern_msg.msg_control) { + if(sock->passcred || scm.fp) + kern_msg.msg_flags |= MSG_CTRUNC; + if(scm.fp) + __scm_destroy(&scm); + } else { + /* If recvmsg processing itself placed some + * control messages into user space, it's is + * using 64-bit CMSG processing, so we need + * to fix it up before we tack on more stuff. + */ + if((unsigned long) kern_msg.msg_control != cmsg_ptr) + cmsg32_recvmsg_fixup(&kern_msg, cmsg_ptr); + + /* Wheee... */ + if(sock->passcred) + put_cmsg32(&kern_msg, + SOL_SOCKET, SCM_CREDENTIALS, + sizeof(scm.creds), &scm.creds); + if(scm.fp != NULL) + scm_detach_fds32(&kern_msg, &scm); + } + } + sockfd_put(sock); + } + + if(uaddr != NULL && err >= 0) + err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); + if(cmsg_ptr != 0 && err >= 0) { + unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control); + __kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr - cmsg_ptr); + err |= __put_user(uclen, &user_msg->msg_controllen); + } + if(err >= 0) + err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + if(err < 0) + return err; + return len; +} + +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); + +static int do_set_attach_filter(int fd, int level, int optname, + char *optval, int optlen) +{ + struct sock_fprog32 { + __u16 len; + __u32 filter; + } *fprog32 = (struct sock_fprog32 *)optval; + struct sock_fprog kfprog; + struct sock_filter *kfilter; + unsigned int fsize; + mm_segment_t old_fs; + __u32 uptr; + int ret; + + if (get_user(kfprog.len, &fprog32->len) || + __get_user(uptr, &fprog32->filter)) + return -EFAULT; + + kfprog.filter = (struct sock_filter *)A(uptr); + fsize = kfprog.len * sizeof(struct sock_filter); + + kfilter = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL); + if (kfilter == NULL) + return -ENOMEM; + + if (copy_from_user(kfilter, kfprog.filter, fsize)) { + kfree(kfilter); + return -EFAULT; + } + + kfprog.filter = kfilter; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, + (char *)&kfprog, sizeof(kfprog)); + set_fs(old_fs); + + kfree(kfilter); + + return ret; +} + +static int do_set_icmpv6_filter(int fd, int level, int optname, + char *optval, int optlen) +{ + struct icmp6_filter kfilter; + mm_segment_t old_fs; + int ret, i; + + if (copy_from_user(&kfilter, optval, sizeof(kfilter))) + return -EFAULT; + + + for (i = 0; i < 8; i += 2) { + u32 tmp = kfilter.data[i]; + + kfilter.data[i] = kfilter.data[i + 1]; + kfilter.data[i + 1] = tmp; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, + (char *) &kfilter, sizeof(kfilter)); + set_fs(old_fs); + + return ret; +} + +asmlinkage int sys32_setsockopt(int fd, int level, int optname, + char *optval, int optlen) +{ + if (optname == SO_ATTACH_FILTER) + return do_set_attach_filter(fd, level, optname, + optval, optlen); + if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER) + return do_set_icmpv6_filter(fd, level, optname, + optval, optlen); + + return sys_setsockopt(fd, level, optname, optval, optlen); +} + + +/* Argument list sizes for sys_socketcall */ +#define AL(x) ((x) * sizeof(u32)) +static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), + AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; +#undef AL + +extern asmlinkage long sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); +extern asmlinkage long sys_connect(int fd, struct sockaddr *uservaddr, + int addrlen); +extern asmlinkage long sys_accept(int fd, struct sockaddr *upeer_sockaddr, + int *upeer_addrlen); +extern asmlinkage long sys_getsockname(int fd, struct sockaddr *usockaddr, + int *usockaddr_len); +extern asmlinkage long sys_getpeername(int fd, struct sockaddr *usockaddr, + int *usockaddr_len); +extern asmlinkage long sys_send(int fd, void *buff, size_t len, unsigned flags); +extern asmlinkage long sys_sendto(int fd, u32 buff, __kernel_size_t32 len, + unsigned flags, u32 addr, int addr_len); +extern asmlinkage long sys_recv(int fd, void *ubuf, size_t size, unsigned flags); +extern asmlinkage long sys_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, + unsigned flags, u32 addr, u32 addr_len); +extern asmlinkage long sys_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen); + +extern asmlinkage long sys_socket(int family, int type, int protocol); +extern asmlinkage long sys_socketpair(int family, int type, int protocol, + int usockvec[2]); +extern asmlinkage long sys_shutdown(int fd, int how); +extern asmlinkage long sys_listen(int fd, int backlog); + +asmlinkage long sys32_socketcall(int call, u32 *args) +{ + int ret; + u32 a[6]; + u32 a0,a1; + + if (callSYS_RECVMSG) + return -EINVAL; + if (copy_from_user(a, args, nas[call])) + return -EFAULT; + a0=a[0]; + a1=a[1]; + + switch(call) + { + case SYS_SOCKET: + ret = sys_socket(a0, a1, a[2]); + break; + case SYS_BIND: + ret = sys_bind(a0, (struct sockaddr *)A(a1), a[2]); + break; + case SYS_CONNECT: + ret = sys_connect(a0, (struct sockaddr *)A(a1), a[2]); + break; + case SYS_LISTEN: + ret = sys_listen(a0, a1); + break; + case SYS_ACCEPT: + ret = sys_accept(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_GETSOCKNAME: + ret = sys_getsockname(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_GETPEERNAME: + ret = sys_getpeername(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_SOCKETPAIR: + ret = sys_socketpair(a0, a1, a[2], (int *)A(a[3])); + break; + case SYS_SEND: + ret = sys_send(a0, (void *)A(a1), a[2], a[3]); + break; + case SYS_SENDTO: + ret = sys_sendto(a0, a1, a[2], a[3], a[4], a[5]); + break; + case SYS_RECV: + ret = sys_recv(a0, (void *)A(a1), a[2], a[3]); + break; + case SYS_RECVFROM: + ret = sys_recvfrom(a0, a1, a[2], a[3], a[4], a[5]); + break; + case SYS_SHUTDOWN: + ret = sys_shutdown(a0,a1); + break; + case SYS_SETSOCKOPT: + ret = sys_setsockopt(a0, a1, a[2], (char *)A(a[3]), + a[4]); + break; + case SYS_GETSOCKOPT: + ret = sys_getsockopt(a0, a1, a[2], a[3], a[4]); + break; + case SYS_SENDMSG: + ret = sys32_sendmsg(a0, (struct msghdr32 *)A(a1), + a[2]); + break; + case SYS_RECVMSG: + ret = sys32_recvmsg(a0, (struct msghdr32 *)A(a1), + a[2]); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} diff -Nru a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/ia32/sys_ia32.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2788 @@ +/* + * sys_ia32.c: Conversion between 32bit and 64bit native syscalls. Based on + * sys_sparc32 + * + * Copyright (C) 2000 VA Linux Co + * Copyright (C) 2000 Don Dugger + * Copyright (C) 1999 Arun Sharma + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 2000 Hewlett-Packard Co. + * Copyright (C) 2000 David Mosberger-Tang + * Copyright (C) 2000,2001 Andi Kleen, SuSE Labs (x86-64 port) + * + * These routines maintain argument size conversion between 32bit and 64bit + * environment. In 2.5 most of this should be moved to a generic directory. + * + * This file assumes that there is a hole at the end of user address space. + */ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) ((unsigned long)(__x)) +#define ROUND_UP(x,a) ((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1))) +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) + +static int +putstat(struct stat32 *ubuf, struct stat *kbuf) +{ + if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct stat32)) || + __put_user (kbuf->st_dev, &ubuf->st_dev) || + __put_user (kbuf->st_ino, &ubuf->st_ino) || + __put_user (kbuf->st_mode, &ubuf->st_mode) || + __put_user (kbuf->st_nlink, &ubuf->st_nlink) || + __put_user (kbuf->st_uid, &ubuf->st_uid) || + __put_user (kbuf->st_gid, &ubuf->st_gid) || + __put_user (kbuf->st_rdev, &ubuf->st_rdev) || + __put_user (kbuf->st_size, &ubuf->st_size) || + __put_user (kbuf->st_atime, &ubuf->st_atime) || + __put_user (kbuf->st_mtime, &ubuf->st_mtime) || + __put_user (kbuf->st_ctime, &ubuf->st_ctime) || + __put_user (kbuf->st_blksize, &ubuf->st_blksize) || + __put_user (kbuf->st_blocks, &ubuf->st_blocks)) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_newstat(char * filename, struct stat * statbuf); + +asmlinkage long +sys32_newstat(char * filename, struct stat32 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newstat(filename, &s); + set_fs (old_fs); + if (putstat (statbuf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_newlstat(char * filename, struct stat * statbuf); + +asmlinkage long +sys32_newlstat(char * filename, struct stat32 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newlstat(filename, &s); + set_fs (old_fs); + if (putstat (statbuf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_newfstat(unsigned int fd, struct stat * statbuf); + +asmlinkage long +sys32_newfstat(unsigned int fd, struct stat32 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newfstat(fd, &s); + set_fs (old_fs); + if (putstat (statbuf, &s)) + return -EFAULT; + return ret; +} + +/* Another set for IA32/LFS -- x86_64 struct stat is different due to + support for 64bit inode numbers. */ + +static int +putstat64(struct stat64 *ubuf, struct stat *kbuf) +{ + if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct stat64)) || + __put_user (kbuf->st_dev, &ubuf->st_dev) || + __put_user (kbuf->st_ino, &ubuf->__st_ino) || + __put_user (kbuf->st_ino, &ubuf->st_ino) || + __put_user (kbuf->st_mode, &ubuf->st_mode) || + __put_user (kbuf->st_nlink, &ubuf->st_nlink) || + __put_user (kbuf->st_uid, &ubuf->st_uid) || + __put_user (kbuf->st_gid, &ubuf->st_gid) || + __put_user (kbuf->st_rdev, &ubuf->st_rdev) || + __put_user (kbuf->st_size, &ubuf->st_size) || + __put_user (kbuf->st_atime, &ubuf->st_atime) || + __put_user (kbuf->st_mtime, &ubuf->st_mtime) || + __put_user (kbuf->st_ctime, &ubuf->st_ctime) || + __put_user (kbuf->st_blksize, &ubuf->st_blksize) || + __put_user (kbuf->st_blocks, &ubuf->st_blocks)) + return -EFAULT; + return 0; +} + +asmlinkage long +sys32_stat64(char * filename, struct stat64 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newstat(filename, &s); + set_fs (old_fs); + if (putstat64 (statbuf, &s)) + return -EFAULT; + return ret; +} + +asmlinkage long +sys32_lstat64(char * filename, struct stat64 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newlstat(filename, &s); + set_fs (old_fs); + if (putstat64 (statbuf, &s)) + return -EFAULT; + return ret; +} + +asmlinkage long +sys32_fstat64(unsigned int fd, struct stat64 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newfstat(fd, &s); + set_fs (old_fs); + if (putstat64 (statbuf, &s)) + return -EFAULT; + return ret; +} + + + +/* + * Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned int addr; + unsigned int len; + unsigned int prot; + unsigned int flags; + unsigned int fd; + unsigned int offset; +}; + +asmlinkage __u32 +sys32_mmap(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + struct file *file = NULL; + unsigned long retval; + struct mm_struct *mm ; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + + if (a.offset & ~PAGE_MASK) + return -EINVAL; + + if (!(a.flags & MAP_ANONYMOUS)) { + file = fget(a.fd); + if (!file) + return -EBADF; + } + + mm = current->mm; + down_write(&mm->mmap_sem); + retval = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, a.offset>>PAGE_SHIFT); + if (file) + fput(file); + + if (retval >= 0xFFFFFFFF) { + do_munmap(mm, retval, a.len); + retval = -ENOMEM; + } + up_write(&mm->mmap_sem); + + + + return retval; +} + +asmlinkage long +sys32_pipe(int *fd) +{ + int retval; + int fds[2]; + + retval = do_pipe(fds); + if (retval) + goto out; + if (copy_to_user(fd, fds, sizeof(fds))) + retval = -EFAULT; + out: + return retval; +} + +asmlinkage long +sys32_rt_sigaction(int sig, struct sigaction32 *act, + struct sigaction32 *oact, unsigned int sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + sigset32_t set32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset32_t)) + return -EINVAL; + + if (act) { + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user((long)new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_flags, &act->sa_flags) || + __get_user((long)new_ka.sa.sa_restorer, &act->sa_restorer)|| + __copy_from_user(&set32, &act->sa_mask, sizeof(sigset32_t))) + return -EFAULT; + + /* FIXME: here we rely on _IA32_NSIG_WORS to be >= than _NSIG_WORDS << 1 */ + switch (_NSIG_WORDS) { + case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] + | (((long)set32.sig[7]) << 32); + case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] + | (((long)set32.sig[5]) << 32); + case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] + | (((long)set32.sig[3]) << 32); + case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] + | (((long)set32.sig[1]) << 32); + } + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + /* FIXME: here we rely on _IA32_NSIG_WORS to be >= than _NSIG_WORDS << 1 */ + switch (_NSIG_WORDS) { + case 4: + set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); + set32.sig[6] = old_ka.sa.sa_mask.sig[3]; + case 3: + set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); + set32.sig[4] = old_ka.sa.sa_mask.sig[2]; + case 2: + set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); + set32.sig[2] = old_ka.sa.sa_mask.sig[1]; + case 1: + set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); + set32.sig[0] = old_ka.sa.sa_mask.sig[0]; + } + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user((long)old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user((long)old_ka.sa.sa_restorer, &oact->sa_restorer) || + __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || + __copy_to_user(&oact->sa_mask, &set32, sizeof(sigset32_t))) + return -EFAULT; + } + + return ret; +} + +asmlinkage long +sys32_sigaction (int sig, struct old_sigaction32 *act, struct old_sigaction32 *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset32_t mask; + + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user((long)new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_flags, &act->sa_flags) || + __get_user((long)new_ka.sa.sa_restorer, &act->sa_restorer) || + __get_user(mask, &act->sa_mask)) + return -EFAULT; + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user((long)old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user((long)old_ka.sa.sa_restorer, &oact->sa_restorer) || + __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) + return -EFAULT; + } + + return ret; +} + +extern asmlinkage long sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, + size_t sigsetsize); + +asmlinkage long +sys32_rt_sigprocmask(int how, sigset32_t *set, sigset32_t *oset, + unsigned int sigsetsize) +{ + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (set) { + if (copy_from_user (&s32, set, sizeof(sigset32_t))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + } + set_fs (KERNEL_DS); + ret = sys_rt_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL, + sigsetsize); + set_fs (old_fs); + if (ret) return ret; + if (oset) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (oset, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return 0; +} + +static int +put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) +{ + if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct statfs32)) || + __put_user (kbuf->f_type, &ubuf->f_type) || + __put_user (kbuf->f_bsize, &ubuf->f_bsize) || + __put_user (kbuf->f_blocks, &ubuf->f_blocks) || + __put_user (kbuf->f_bfree, &ubuf->f_bfree) || + __put_user (kbuf->f_bavail, &ubuf->f_bavail) || + __put_user (kbuf->f_files, &ubuf->f_files) || + __put_user (kbuf->f_ffree, &ubuf->f_ffree) || + __put_user (kbuf->f_namelen, &ubuf->f_namelen) || + __put_user (kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) || + __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1])) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_statfs(const char * path, struct statfs * buf); + +asmlinkage long +sys32_statfs(const char * path, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_statfs((const char *)path, &s); + set_fs (old_fs); + if (put_statfs(buf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_fstatfs(unsigned int fd, struct statfs * buf); + +asmlinkage long +sys32_fstatfs(unsigned int fd, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_fstatfs(fd, &s); + set_fs (old_fs); + if (put_statfs(buf, &s)) + return -EFAULT; + return ret; +} + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +struct itimerval32 +{ + struct timeval32 it_interval; + struct timeval32 it_value; +}; + +static inline long +get_tv32(struct timeval *o, struct timeval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + __get_user(o->tv_sec, &i->tv_sec) || + __get_user(o->tv_usec, &i->tv_usec)); + return ENOSYS; +} + +static inline long +put_tv32(struct timeval32 *o, struct timeval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + __put_user(i->tv_sec, &o->tv_sec) || + __put_user(i->tv_usec, &o->tv_usec)); +} + +static inline long +get_it32(struct itimerval *o, struct itimerval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + __get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) || + __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) || + __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) || + __get_user(o->it_value.tv_usec, &i->it_value.tv_usec)); + return ENOSYS; +} + +static inline long +put_it32(struct itimerval32 *o, struct itimerval *i) +{ + return (!access_ok(VERIFY_WRITE, i, sizeof(*i)) || + __put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) || + __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) || + __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) || + __put_user(i->it_value.tv_usec, &o->it_value.tv_usec)); + return ENOSYS; +} + +extern int do_getitimer(int which, struct itimerval *value); + +asmlinkage long +sys32_getitimer(int which, struct itimerval32 *it) +{ + struct itimerval kit; + int error; + + error = do_getitimer(which, &kit); + if (!error && put_it32(it, &kit)) + error = -EFAULT; + + return error; +} + +extern int do_setitimer(int which, struct itimerval *, struct itimerval *); + +asmlinkage long +sys32_setitimer(int which, struct itimerval32 *in, struct itimerval32 *out) +{ + struct itimerval kin, kout; + int error; + + if (in) { + if (get_it32(&kin, in)) + return -EFAULT; + } else + memset(&kin, 0, sizeof(kin)); + + error = do_setitimer(which, &kin, out ? &kout : NULL); + if (error || !out) + return error; + if (put_it32(out, &kout)) + return -EFAULT; + + return 0; + +} +asmlinkage unsigned long +sys32_alarm(unsigned int seconds) +{ + struct itimerval it_new, it_old; + unsigned int oldalarm; + + it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; + it_new.it_value.tv_sec = seconds; + it_new.it_value.tv_usec = 0; + do_setitimer(ITIMER_REAL, &it_new, &it_old); + oldalarm = it_old.it_value.tv_sec; + /* ehhh.. We can't return 0 if we have an alarm pending.. */ + /* And we'd better return too much than too little anyway */ + if (it_old.it_value.tv_usec) + oldalarm++; + return oldalarm; +} + +/* Translations due to time_t size differences. Which affects all + sorts of things, like timeval and itimerval. */ + +struct utimbuf_32 { + int atime; + int mtime; +}; + +extern asmlinkage long sys_utimes(char * filename, struct timeval * utimes); +extern asmlinkage long sys_gettimeofday (struct timeval *tv, struct timezone *tz); + +asmlinkage long +ia32_utime(char * filename, struct utimbuf_32 *times32) +{ + mm_segment_t old_fs = get_fs(); + struct timeval tv[2]; + long ret; + + if (times32) { + get_user(tv[0].tv_sec, ×32->atime); + tv[0].tv_usec = 0; + get_user(tv[1].tv_sec, ×32->mtime); + tv[1].tv_usec = 0; + set_fs (KERNEL_DS); + } else { + set_fs (KERNEL_DS); + ret = sys_gettimeofday(&tv[0], 0); + if (ret < 0) + goto out; + tv[1] = tv[0]; + } + ret = sys_utimes(filename, tv); + out: + set_fs (old_fs); + return ret; +} + +extern struct timezone sys_tz; +extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); + +asmlinkage long +sys32_gettimeofday(struct timeval32 *tv, struct timezone *tz) +{ + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32(tv, &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + return 0; +} + +asmlinkage long +sys32_settimeofday(struct timeval32 *tv, struct timezone *tz) +{ + struct timeval ktv; + struct timezone ktz; + + if (tv) { + if (get_tv32(&ktv, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(ktz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL); +} + +struct linux32_dirent { + u32 d_ino; + u32 d_off; + u16 d_reclen; + char d_name[1]; +}; + +struct old_linux32_dirent { + u32 d_ino; + u32 d_offset; + u16 d_namlen; + char d_name[1]; +}; + +struct getdents32_callback { + struct linux32_dirent * current_dir; + struct linux32_dirent * previous; + int count; + int error; +}; + +struct readdir32_callback { + struct old_linux32_dirent * dirent; + int count; +}; + +static int +filldir32 (void *__buf, const char *name, int namlen, loff_t offset, ino_t ino, + unsigned int d_type) +{ + struct linux32_dirent * dirent; + struct getdents32_callback * buf = (struct getdents32_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1, 4); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->current_dir; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(reclen, &dirent->d_reclen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage long +sys32_getdents (unsigned int fd, void * dirent, unsigned int count) +{ + struct file * file; + struct linux32_dirent * lastdirent; + struct getdents32_callback buf; + int error; + + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + buf.current_dir = (struct linux32_dirent *) dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + error = vfs_readdir(file, filldir32, &buf); + if (error < 0) + goto out_putf; + error = buf.error; + lastdirent = buf.previous; + if (lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = count - buf.count; + } + +out_putf: + fput(file); +out: + return error; +} + +static int +fillonedir32 (void * __buf, const char * name, int namlen, loff_t offset, ino_t ino, unsigned d_type) +{ + struct readdir32_callback * buf = (struct readdir32_callback *) __buf; + struct old_linux32_dirent * dirent; + + if (buf->count) + return -EINVAL; + buf->count++; + dirent = buf->dirent; + put_user(ino, &dirent->d_ino); + put_user(offset, &dirent->d_offset); + put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + return 0; +} + +asmlinkage long +sys32_oldreaddir (unsigned int fd, void * dirent, unsigned int count) +{ + int error; + struct file * file; + struct readdir32_callback buf; + + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + buf.count = 0; + buf.dirent = dirent; + + error = vfs_readdir(file, fillonedir32, &buf); + if (error >= 0) + error = buf.count; + fput(file); +out: + return error; +} + +/* + * We can actually return ERESTARTSYS instead of EINTR, but I'd + * like to be certain this leads to no problems. So I return + * EINTR just for safety. + * + * Update: ERESTARTSYS breaks at least the xview clock binary, so + * I'm trying ERESTARTNOHAND which restart only when you want to. + */ +#define MAX_SELECT_SECONDS \ + ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) +#define ROUND_UP_TIME(x,y) (((x)+(y)-1)/(y)) + +asmlinkage long +sys32_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval32 *tvp32) +{ + fd_set_bits fds; + char *bits; + long timeout; + int ret, size; + + timeout = MAX_SCHEDULE_TIMEOUT; + if (tvp32) { + time_t sec, usec; + + get_user(sec, &tvp32->tv_sec); + get_user(usec, &tvp32->tv_usec); + + ret = -EINVAL; + if (sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = ROUND_UP_TIME(usec, 1000000/HZ); + timeout += sec * (unsigned long) HZ; + } + } + + ret = -EINVAL; + if (n < 0) + goto out_nofds; + + if (n > current->files->max_fdset) + n = current->files->max_fdset; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ + ret = -ENOMEM; + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) + goto out_nofds; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); + + if ((ret = get_fd_set(n, inp, fds.in)) || + (ret = get_fd_set(n, outp, fds.out)) || + (ret = get_fd_set(n, exp, fds.ex))) + goto out; + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); + + ret = do_select(n, &fds, &timeout); + + if (tvp32 && !(current->personality & STICKY_TIMEOUTS)) { + time_t sec = 0, usec = 0; + if (timeout) { + sec = timeout / HZ; + usec = timeout % HZ; + usec *= (1000000/HZ); + } + put_user(sec, (int *)&tvp32->tv_sec); + put_user(usec, (int *)&tvp32->tv_usec); + } + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) + goto out; + ret = 0; + } + + set_fd_set(n, inp, fds.res_in); + set_fd_set(n, outp, fds.res_out); + set_fd_set(n, exp, fds.res_ex); + +out: + kfree(bits); +out_nofds: + return ret; +} + +struct sel_arg_struct { + unsigned int n; + unsigned int inp; + unsigned int outp; + unsigned int exp; + unsigned int tvp; +}; + +asmlinkage long +sys32_old_select(struct sel_arg_struct *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + return sys32_select(a.n, (fd_set *)A(a.inp), (fd_set *)A(a.outp), (fd_set *)A(a.exp), + (struct timeval32 *)A(a.tvp)); +} + +struct timespec32 { + int tv_sec; + int tv_nsec; +}; + +extern asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp); + +asmlinkage long +sys32_nanosleep(struct timespec32 *rqtp, struct timespec32 *rmtp) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + if (verify_area(VERIFY_READ, rqtp, sizeof(struct timespec32)) || + __get_user (t.tv_sec, &rqtp->tv_sec) || + __get_user (t.tv_nsec, &rqtp->tv_nsec)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_nanosleep(&t, rmtp ? &t : NULL); + set_fs (old_fs); + if (rmtp && ret == -EINTR) { + if (verify_area(VERIFY_WRITE, rmtp, sizeof(struct timespec32)) || + __put_user (t.tv_sec, &rmtp->tv_sec) || + __put_user (t.tv_nsec, &rmtp->tv_nsec)) + return -EFAULT; + } + return ret; +} + +asmlinkage ssize_t sys_readv(unsigned long,const struct iovec *,unsigned long); +asmlinkage ssize_t sys_writev(unsigned long,const struct iovec *,unsigned long); + +struct iovec * +get_iovec32(struct iovec32 *iov32, struct iovec *iov_buf, u32 count, int type) +{ + int i; + u32 buf, len; + struct iovec *ivp, *iov; + + /* Get the "struct iovec" from user memory */ + + if (!count) + return 0; + if(verify_area(VERIFY_READ, iov32, sizeof(struct iovec32)*count)) + return(struct iovec *)0; + if (count > UIO_MAXIOV) + return(struct iovec *)0; + if (count > UIO_FASTIOV) { + iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); + if (!iov) + return((struct iovec *)0); + } else + iov = iov_buf; + + ivp = iov; + for (i = 0; i < count; i++) { + if (__get_user(len, &iov32->iov_len) || + __get_user(buf, &iov32->iov_base)) { + if (iov != iov_buf) + kfree(iov); + return((struct iovec *)0); + } + if (verify_area(type, (void *)A(buf), len)) { + if (iov != iov_buf) + kfree(iov); + return((struct iovec *)0); + } + ivp->iov_base = (void *)A(buf); + ivp->iov_len = (__kernel_size_t)len; + iov32++; + ivp++; + } + return(iov); +} + +asmlinkage long +sys32_readv(int fd, struct iovec32 *vector, u32 count) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov; + int ret; + mm_segment_t old_fs = get_fs(); + + if ((iov = get_iovec32(vector, iovstack, count, VERIFY_WRITE)) == (struct iovec *)0) + return -EFAULT; + set_fs(KERNEL_DS); + ret = sys_readv(fd, iov, count); + set_fs(old_fs); + if (iov != iovstack) + kfree(iov); + return ret; +} + +asmlinkage long +sys32_writev(int fd, struct iovec32 *vector, u32 count) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov; + int ret; + mm_segment_t old_fs = get_fs(); + + if ((iov = get_iovec32(vector, iovstack, count, VERIFY_READ)) == (struct iovec *)0) + return -EFAULT; + set_fs(KERNEL_DS); + ret = sys_writev(fd, iov, count); + set_fs(old_fs); + if (iov != iovstack) + kfree(iov); + return ret; +} + +#define RLIM_INFINITY32 0xffffffff +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) + +struct rlimit32 { + int rlim_cur; + int rlim_max; +}; + +extern asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage long +sys32_getrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_getrlimit(resource, &r); + set_fs(old_fs); + if (!ret) { + if (verify_area(VERIFY_WRITE, rlim, sizeof(struct rlimit32)) || + __put_user(RESOURCE32(r.rlim_cur), &rlim->rlim_cur) || + __put_user(RESOURCE32(r.rlim_max), &rlim->rlim_max)) + ret = -EFAULT; + } + return ret; +} + +extern asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage long +sys32_old_getrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_old_getrlimit(resource, &r); + set_fs(old_fs); + if (!ret) { + if (verify_area(VERIFY_WRITE, rlim, sizeof(struct rlimit32)) || + __put_user(r.rlim_cur, &rlim->rlim_cur) || + __put_user(r.rlim_max, &rlim->rlim_max)) + ret = -EFAULT; + } + return ret; +} + +extern asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage long +sys32_setrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + + if (resource >= RLIM_NLIMITS) return -EINVAL; + if (verify_area(VERIFY_READ, rlim, sizeof(struct rlimit32)) || + __get_user (r.rlim_cur, &rlim->rlim_cur) || + __get_user (r.rlim_max, &rlim->rlim_max)) + return -EFAULT; + if (r.rlim_cur == RLIM_INFINITY32) + r.rlim_cur = RLIM_INFINITY; + if (r.rlim_max == RLIM_INFINITY32) + r.rlim_max = RLIM_INFINITY; + set_fs (KERNEL_DS); + ret = sys_setrlimit(resource, &r); + set_fs (old_fs); + return ret; +} + +/* + * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation.. + * + * This is really horribly ugly. + */ + +struct msgbuf32 { s32 mtype; char mtext[1]; }; + +struct ipc_perm32 +{ + key_t key; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_uid_t32 cuid; + __kernel_gid_t32 cgid; + __kernel_mode_t32 mode; + unsigned short seq; +}; + +struct semid_ds32 { + struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */ + __kernel_time_t32 sem_otime; /* last semop time */ + __kernel_time_t32 sem_ctime; /* last change time */ + u32 sem_base; /* ptr to first semaphore in array */ + u32 sem_pending; /* pending operations to be processed */ + u32 sem_pending_last; /* last pending operation */ + u32 undo; /* undo requests on this array */ + unsigned short sem_nsems; /* no. of semaphores in array */ +}; + +struct msqid_ds32 +{ + struct ipc_perm32 msg_perm; + u32 msg_first; + u32 msg_last; + __kernel_time_t32 msg_stime; + __kernel_time_t32 msg_rtime; + __kernel_time_t32 msg_ctime; + u32 wwait; + u32 rwait; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + __kernel_ipc_pid_t32 msg_lspid; + __kernel_ipc_pid_t32 msg_lrpid; +}; + +struct shmid_ds32 { + struct ipc_perm32 shm_perm; + int shm_segsz; + __kernel_time_t32 shm_atime; + __kernel_time_t32 shm_dtime; + __kernel_time_t32 shm_ctime; + __kernel_ipc_pid_t32 shm_cpid; + __kernel_ipc_pid_t32 shm_lpid; + unsigned short shm_nattch; +}; + +#define IPCOP_MASK(__x) (1UL << (__x)) + +static int +do_sys32_semctl(int first, int second, int third, void *uptr) +{ + union semun fourth; + u32 pad; + int err; + struct semid64_ds s; + struct semid_ds32 *usp; + mm_segment_t old_fs; + + if (!uptr) + return -EINVAL; + err = -EFAULT; + if (get_user (pad, (u32 *)uptr)) + return err; + if(third == SETVAL) + fourth.val = (int)pad; + else + fourth.__pad = (void *)A(pad); + switch (third) { + + case IPC_INFO: + case IPC_RMID: + case IPC_SET: + case SEM_INFO: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case GETALL: + case SETVAL: + case SETALL: + err = sys_semctl (first, second, third, fourth); + break; + + case IPC_STAT: + case SEM_STAT: + usp = (struct semid_ds32 *)A(pad); + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + if (verify_area(VERIFY_WRITE, usp, sizeof(struct semid_ds32)) || + __put_user(s.sem_perm.key, &usp->sem_perm.key) || + __put_user(s.sem_perm.uid, &usp->sem_perm.uid) || + __put_user(s.sem_perm.gid, &usp->sem_perm.gid) || + __put_user(s.sem_perm.cuid, &usp->sem_perm.cuid) || + __put_user (s.sem_perm.cgid, &usp->sem_perm.cgid) || + __put_user (s.sem_perm.mode, &usp->sem_perm.mode) || + __put_user (s.sem_perm.seq, &usp->sem_perm.seq) || + __put_user (s.sem_otime, &usp->sem_otime) || + __put_user (s.sem_ctime, &usp->sem_ctime) || + __put_user (s.sem_nsems, &usp->sem_nsems)) + return -EFAULT; + break; + + } + + return err; +} + +static int +do_sys32_msgsnd (int first, int second, int third, void *uptr) +{ + struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + + 4, GFP_USER); + struct msgbuf32 *up = (struct msgbuf32 *)uptr; + mm_segment_t old_fs; + int err; + + if (!p) + return -ENOMEM; + err = verify_area(VERIFY_READ, up, sizeof(struct msgbuf32)); + if (err) + goto out; + err = __get_user (p->mtype, &up->mtype); + err |= __copy_from_user (p->mtext, &up->mtext, second); + if (err) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgsnd (first, p, second, third); + set_fs (old_fs); +out: + kfree (p); + return err; +} + +static int +do_sys32_msgrcv (int first, int second, int msgtyp, int third, + int version, void *uptr) +{ + struct msgbuf32 *up; + struct msgbuf *p; + mm_segment_t old_fs; + int err; + + if (!version) { + struct ipc_kludge *uipck = (struct ipc_kludge *)uptr; + struct ipc_kludge ipck; + + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge))) + goto out; + uptr = (void *)A(ipck.msgp); + msgtyp = ipck.msgtyp; + } + err = -ENOMEM; + p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER); + if (!p) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgrcv (first, p, second + 4, msgtyp, third); + set_fs (old_fs); + if (err < 0) + goto free_then_out; + up = (struct msgbuf32 *)uptr; + if (verify_area(VERIFY_WRITE, up, sizeof(struct msgbuf32)) || + __put_user (p->mtype, &up->mtype) || + __copy_to_user (&up->mtext, p->mtext, err)) + err = -EFAULT; +free_then_out: + kfree (p); +out: + return err; +} + +static int +do_sys32_msgctl (int first, int second, void *uptr) +{ + int err = -EINVAL; + struct msqid_ds m; + struct msqid64_ds m64; + struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; + mm_segment_t old_fs; + + switch (second) { + + case IPC_INFO: + case IPC_RMID: + case MSG_INFO: + err = sys_msgctl (first, second, (struct msqid_ds *)uptr); + break; + + case IPC_SET: + err = verify_area(VERIFY_READ, up, sizeof(struct msqid_ds32)); + if (err) + break; + err = __get_user (m.msg_perm.uid, &up->msg_perm.uid); + err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); + err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); + err |= __get_user (m.msg_qbytes, &up->msg_qbytes); + if (err) + break; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, &m); + set_fs (old_fs); + break; + + case IPC_STAT: + case MSG_STAT: + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, (void *) &m64); + set_fs (old_fs); + if (verify_area(VERIFY_WRITE, up, sizeof(struct msqid_ds32)) || + __put_user (m64.msg_perm.key, &up->msg_perm.key) || + __put_user(m64.msg_perm.uid, &up->msg_perm.uid) || + __put_user(m64.msg_perm.gid, &up->msg_perm.gid) || + __put_user(m64.msg_perm.cuid, &up->msg_perm.cuid) || + __put_user(m64.msg_perm.cgid, &up->msg_perm.cgid) || + __put_user(m64.msg_perm.mode, &up->msg_perm.mode) || + __put_user(m64.msg_perm.seq, &up->msg_perm.seq) || + __put_user(m64.msg_stime, &up->msg_stime) || + __put_user(m64.msg_rtime, &up->msg_rtime) || + __put_user(m64.msg_ctime, &up->msg_ctime) || + __put_user(m64.msg_cbytes, &up->msg_cbytes) || + __put_user(m64.msg_qnum, &up->msg_qnum) || + __put_user(m64.msg_qbytes, &up->msg_qbytes) || + __put_user(m64.msg_lspid, &up->msg_lspid) || + __put_user(m64.msg_lrpid, &up->msg_lrpid)) + return -EFAULT; + break; + + } + + return err; +} + +static int +do_sys32_shmat (int first, int second, int third, int version, void *uptr) +{ + unsigned long raddr; + u32 *uaddr = (u32 *)A((u32)third); + int err = -EINVAL; + + if (version == 1) + return err; + err = sys_shmat (first, uptr, second, &raddr); + if (err) + return err; + err = put_user (raddr, uaddr); + return err; +} + +static int +do_sys32_shmctl (int first, int second, void *uptr) +{ + int err = -EFAULT; + struct shmid_ds s; + struct shmid64_ds s64; + struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; + mm_segment_t old_fs; + struct shm_info32 { + int used_ids; + u32 shm_tot, shm_rss, shm_swp; + u32 swap_attempts, swap_successes; + } *uip = (struct shm_info32 *)uptr; + struct shm_info si; + + switch (second) { + + case IPC_INFO: + case IPC_RMID: + case SHM_LOCK: + case SHM_UNLOCK: + err = sys_shmctl (first, second, (struct shmid_ds *)uptr); + break; + case IPC_SET: + err = verify_area(VERIFY_READ, up, sizeof(struct shmid_ds32)); + if (err) + break; + err = __get_user (s.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); + if (err) + break; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, &s); + set_fs (old_fs); + break; + + case IPC_STAT: + case SHM_STAT: + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (void *) &s64); + set_fs (old_fs); + if (err < 0) + break; + if (verify_area(VERIFY_WRITE, up, sizeof(struct shmid_ds32)) || + __put_user (s64.shm_perm.key, &up->shm_perm.key) || + __put_user (s64.shm_perm.uid, &up->shm_perm.uid) || + __put_user (s64.shm_perm.gid, &up->shm_perm.gid) || + __put_user (s64.shm_perm.cuid, &up->shm_perm.cuid) || + __put_user (s64.shm_perm.cgid, &up->shm_perm.cgid) || + __put_user (s64.shm_perm.mode, &up->shm_perm.mode) || + __put_user (s64.shm_perm.seq, &up->shm_perm.seq) || + __put_user (s64.shm_atime, &up->shm_atime) || + __put_user (s64.shm_dtime, &up->shm_dtime) || + __put_user (s64.shm_ctime, &up->shm_ctime) || + __put_user (s64.shm_segsz, &up->shm_segsz) || + __put_user (s64.shm_nattch, &up->shm_nattch) || + __put_user (s64.shm_cpid, &up->shm_cpid) || + __put_user (s64.shm_lpid, &up->shm_lpid)) + return -EFAULT; + break; + + case SHM_INFO: + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (void *)&si); + set_fs (old_fs); + if (err < 0) + break; + if (verify_area(VERIFY_WRITE, uip, sizeof(struct shm_info32)) || + __put_user (si.used_ids, &uip->used_ids) || + __put_user (si.shm_tot, &uip->shm_tot) || + __put_user (si.shm_rss, &uip->shm_rss) || + __put_user (si.shm_swp, &uip->shm_swp) || + __put_user (si.swap_attempts, &uip->swap_attempts) || + __put_user (si.swap_successes, &uip->swap_successes)) + return -EFAULT; + break; + + } + return err; +} + +asmlinkage long +sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) +{ + int version, err; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + + case SEMOP: + /* struct sembuf is the same on 32 and 64bit :)) */ + err = sys_semop (first, (struct sembuf *)AA(ptr), + second); + break; + case SEMGET: + err = sys_semget (first, second, third); + break; + case SEMCTL: + err = do_sys32_semctl (first, second, third, + (void *)AA(ptr)); + break; + + case MSGSND: + err = do_sys32_msgsnd (first, second, third, + (void *)AA(ptr)); + break; + case MSGRCV: + err = do_sys32_msgrcv (first, second, fifth, third, + version, (void *)AA(ptr)); + break; + case MSGGET: + err = sys_msgget ((key_t) first, second); + break; + case MSGCTL: + err = do_sys32_msgctl (first, second, (void *)AA(ptr)); + break; + + case SHMAT: + err = do_sys32_shmat (first, second, third, + version, (void *)AA(ptr)); + break; + case SHMDT: + err = sys_shmdt ((char *)AA(ptr)); + break; + case SHMGET: + err = sys_shmget (first, second, third); + break; + case SHMCTL: + err = do_sys32_shmctl (first, second, (void *)AA(ptr)); + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +/* + * sys_time() can be implemented in user-level using + * sys_gettimeofday(). IA64 did this but i386 Linux did not + * so we have to implement this system call here. + */ +asmlinkage long sys32_time(int * tloc) +{ + int i; + + /* SMP: This is fairly trivial. We grab CURRENT_TIME and + stuff it to user space. No side effects */ + i = CURRENT_TIME; + if (tloc) { + if (put_user(i,tloc)) + i = -EFAULT; + } + return i; +} + +struct rusage32 { + struct timeval32 ru_utime; + struct timeval32 ru_stime; + int ru_maxrss; + int ru_ixrss; + int ru_idrss; + int ru_isrss; + int ru_minflt; + int ru_majflt; + int ru_nswap; + int ru_inblock; + int ru_oublock; + int ru_msgsnd; + int ru_msgrcv; + int ru_nsignals; + int ru_nvcsw; + int ru_nivcsw; +}; + +static int +put_rusage (struct rusage32 *ru, struct rusage *r) +{ + if (verify_area(VERIFY_WRITE, ru, sizeof(struct rusage32)) || + __put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec) || + __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec) || + __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec) || + __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec) || + __put_user (r->ru_maxrss, &ru->ru_maxrss) || + __put_user (r->ru_ixrss, &ru->ru_ixrss) || + __put_user (r->ru_idrss, &ru->ru_idrss) || + __put_user (r->ru_isrss, &ru->ru_isrss) || + __put_user (r->ru_minflt, &ru->ru_minflt) || + __put_user (r->ru_majflt, &ru->ru_majflt) || + __put_user (r->ru_nswap, &ru->ru_nswap) || + __put_user (r->ru_inblock, &ru->ru_inblock) || + __put_user (r->ru_oublock, &ru->ru_oublock) || + __put_user (r->ru_msgsnd, &ru->ru_msgsnd) || + __put_user (r->ru_msgrcv, &ru->ru_msgrcv) || + __put_user (r->ru_nsignals, &ru->ru_nsignals) || + __put_user (r->ru_nvcsw, &ru->ru_nvcsw) || + __put_user (r->ru_nivcsw, &ru->ru_nivcsw)) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, + int options, struct rusage * ru); + +asmlinkage long +sys32_wait4(__kernel_pid_t32 pid, unsigned int *stat_addr, int options, + struct rusage32 *ru) +{ + if (!ru) + return sys_wait4(pid, stat_addr, options, NULL); + else { + struct rusage r; + int ret; + unsigned int status; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + if (stat_addr && put_user (status, stat_addr)) + return -EFAULT; + return ret; + } +} + +asmlinkage long +sys32_waitpid(__kernel_pid_t32 pid, unsigned int *stat_addr, int options) +{ + return sys32_wait4(pid, stat_addr, options, NULL); +} + + +extern asmlinkage long +sys_getrusage(int who, struct rusage *ru); + +asmlinkage long +sys32_getrusage(int who, struct rusage32 *ru) +{ + struct rusage r; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getrusage(who, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + return ret; +} + +struct tms32 { + __kernel_clock_t32 tms_utime; + __kernel_clock_t32 tms_stime; + __kernel_clock_t32 tms_cutime; + __kernel_clock_t32 tms_cstime; +}; + +extern asmlinkage long sys_times(struct tms * tbuf); + +asmlinkage long +sys32_times(struct tms32 *tbuf) +{ + struct tms t; + long ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_times(tbuf ? &t : NULL); + set_fs (old_fs); + if (tbuf) { + if (verify_area(VERIFY_WRITE, tbuf, sizeof(struct tms32)) || + __put_user (t.tms_utime, &tbuf->tms_utime) || + __put_user (t.tms_stime, &tbuf->tms_stime) || + __put_user (t.tms_cutime, &tbuf->tms_cutime) || + __put_user (t.tms_cstime, &tbuf->tms_cstime)) + return -EFAULT; + } + return ret; +} + +static inline int +get_flock32(struct flock *kfl, struct flock32 *ufl) +{ + if (verify_area(VERIFY_READ, ufl, sizeof(struct flock32)) || + __get_user(kfl->l_type, &ufl->l_type) || + __get_user(kfl->l_whence, &ufl->l_whence) || + __get_user(kfl->l_start, &ufl->l_start) || + __get_user(kfl->l_len, &ufl->l_len) || + __get_user(kfl->l_pid, &ufl->l_pid)) + return -EFAULT; + return 0; +} + +static inline int +put_flock32(struct flock *kfl, struct flock32 *ufl) +{ + if (verify_area(VERIFY_WRITE, ufl, sizeof(struct flock32)) || + __put_user(kfl->l_type, &ufl->l_type) || + __put_user(kfl->l_whence, &ufl->l_whence) || + __put_user(kfl->l_start, &ufl->l_start) || + __put_user(kfl->l_len, &ufl->l_len) || + __put_user(kfl->l_pid, &ufl->l_pid)) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, + unsigned long arg); + +asmlinkage long +sys32_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct flock f; + mm_segment_t old_fs; + long ret; + + switch (cmd) { + case F_GETLK: + case F_SETLK: + case F_SETLKW: + if(cmd != F_GETLK && get_flock32(&f, (struct flock32 *)((long)arg))) + return -EFAULT; + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs(old_fs); + if(cmd == F_GETLK && put_flock32(&f, (struct flock32 *)((long)arg))) + return -EFAULT; + return ret; + default: + /* + * `sys_fcntl' lies about arg, for the F_SETOWN + * sub-function arg can have a negative value. + */ + return sys_fcntl(fd, cmd, (unsigned long)((long)arg)); + } +} + +static inline int +get_flock64(struct flock *kfl, struct ia32_flock64 *ufl) +{ + if (verify_area(VERIFY_READ, ufl, sizeof(struct ia32_flock64)) || + __get_user(kfl->l_type, &ufl->l_type) || + __get_user(kfl->l_whence, &ufl->l_whence) || + __copy_from_user(&kfl->l_start, &ufl->l_start, 8) || + __copy_from_user(&kfl->l_len, &ufl->l_len, 8) || + __get_user(kfl->l_pid, &ufl->l_pid)) + return -EFAULT; + return 0; +} + +static inline int +put_flock64(struct flock *kfl, struct ia32_flock64 *ufl) +{ + if (verify_area(VERIFY_WRITE, ufl, sizeof(struct ia32_flock64)) || + __put_user(kfl->l_type, &ufl->l_type) || + __put_user(kfl->l_whence, &ufl->l_whence) || + __copy_to_user(&ufl->l_start,&kfl->l_start, 8) || + __copy_to_user(&ufl->l_len,&kfl->l_len, 8) || + __put_user(kfl->l_pid, &ufl->l_pid)) + return -EFAULT; + return 0; +} + +asmlinkage long +sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct flock f; + mm_segment_t old_fs; + long ret; + + /* sys_fcntl() is by default 64 bit and so don't know anything + * about F_xxxx64 commands + */ + switch (cmd) { + case F_GETLK64: + cmd = F_GETLK; + break; + case F_SETLK64: + cmd = F_SETLK; + break; + case F_SETLKW64: + cmd = F_SETLKW; + break; + } + + switch (cmd) { + case F_SETLKW: + case F_SETLK: + if(get_flock64(&f, (struct ia32_flock64 *)arg)) + return -EFAULT; + case F_GETLK: + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs(old_fs); + if(cmd == F_GETLK64 && put_flock64(&f, (struct ia32_flock64 *)((long)arg))) + return -EFAULT; + return ret; + default: + /* + * `sys_fcntl' lies about arg, for the F_SETOWN + * sub-function arg can have a negative value. + */ + return sys_fcntl(fd, cmd, (unsigned long)((long)arg)); + } +} + +int sys32_ni_syscall(int call) +{ + printk(KERN_INFO "IA32 syscall %d from %s not implemented\n", call, + current->comm); + return -ENOSYS; +} + +/* In order to reduce some races, while at the same time doing additional + * checking and hopefully speeding things up, we copy filenames to the + * kernel data space before using them.. + * + * POSIX.1 2.4: an empty pathname is invalid (ENOENT). + */ +static inline int +do_getname32(const char *filename, char *page) +{ + int retval; + + /* 32bit pointer will be always far below TASK_SIZE :)) */ + retval = strncpy_from_user((char *)page, (char *)filename, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) + return 0; + return -ENAMETOOLONG; + } else if (!retval) + retval = -ENOENT; + return retval; +} + +char * +getname32(const char *filename) +{ + char *tmp, *result; + + result = ERR_PTR(-ENOMEM); + tmp = (char *)__get_free_page(GFP_KERNEL); + if (tmp) { + int retval = do_getname32(filename, tmp); + + result = tmp; + if (retval < 0) { + putname(tmp); + result = ERR_PTR(retval); + } + } + return result; +} + +/* 32-bit timeval and related flotsam. */ + +extern asmlinkage long sys_utime(char * filename, struct utimbuf * times); + +struct utimbuf32 { + __kernel_time_t32 actime, modtime; +}; + +asmlinkage long +sys32_utime(char * filename, struct utimbuf32 *times) +{ + struct utimbuf t; + mm_segment_t old_fs; + int ret; + char *filenam; + + if (!times) + return sys_utime(filename, NULL); + if (verify_area(VERIFY_READ, times, sizeof(struct utimbuf32)) || + __get_user (t.actime, ×->actime) || + __get_user (t.modtime, ×->modtime)) + return -EFAULT; + filenam = getname32 (filename); + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { + old_fs = get_fs(); + set_fs (KERNEL_DS); + ret = sys_utime(filenam, &t); + set_fs (old_fs); + putname (filenam); + } + return ret; +} + +/* + * Ooo, nasty. We need here to frob 32-bit unsigned longs to + * 64-bit unsigned longs. + */ + +static inline int +get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset) +{ + if (ufdset) { + unsigned long odd; + + if (verify_area(VERIFY_READ, ufdset, n*sizeof(u32))) + return -EFAULT; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + __get_user(l, ufdset); + __get_user(h, ufdset+1); + ufdset += 2; + *fdset++ = h << 32 | l; + n -= 2; + } + if (odd) + __get_user(*fdset, ufdset); + } else { + /* Tricky, must clear full unsigned long in the + * kernel fdset at the end, this makes sure that + * actually happens. + */ + memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32)); + } + return 0; +} + +extern asmlinkage long sys_sysfs(int option, unsigned long arg1, + unsigned long arg2); + +asmlinkage long +sys32_sysfs(int option, u32 arg1, u32 arg2) +{ + return sys_sysfs(option, arg1, arg2); +} + +extern asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, + unsigned long new_flags, void *data); + +static char *badfs[] = { + "smbfs", "ncpfs", NULL +}; + +static int checktype(char *user_type) +{ + int err = 0; + char **s,*kernel_type = getname32(user_type); + if (!kernel_type) + return -EFAULT; + for (s = badfs; *s; ++s) + if (!strcmp(kernel_type, *s)) { + printk(KERN_ERR "mount32: unsupported fs `%s' -- use 64bit mount\n", *s); + err = -EINVAL; + break; + } + putname(user_type); + return err; +} + +asmlinkage long +sys32_mount(char *dev_name, char *dir_name, char *type, + unsigned long new_flags, u32 data) +{ + int err; + if(!capable(CAP_SYS_ADMIN)) + return -EPERM; + err = checktype(type); + if (err) + return err; + return sys_mount(dev_name, dir_name, type, new_flags, (void *)AA(data)); +} + +struct sysinfo32 { + s32 uptime; + u32 loads[3]; + u32 totalram; + u32 freeram; + u32 sharedram; + u32 bufferram; + u32 totalswap; + u32 freeswap; + unsigned short procs; + char _f[22]; +}; + +extern asmlinkage long sys_sysinfo(struct sysinfo *info); + +asmlinkage long +sys32_sysinfo(struct sysinfo32 *info) +{ + struct sysinfo s; + int ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_sysinfo(&s); + set_fs (old_fs); + if (verify_area(VERIFY_WRITE, info, sizeof(struct sysinfo32)) || + __put_user (s.uptime, &info->uptime) || + __put_user (s.loads[0], &info->loads[0]) || + __put_user (s.loads[1], &info->loads[1]) || + __put_user (s.loads[2], &info->loads[2]) || + __put_user (s.totalram, &info->totalram) || + __put_user (s.freeram, &info->freeram) || + __put_user (s.sharedram, &info->sharedram) || + __put_user (s.bufferram, &info->bufferram) || + __put_user (s.totalswap, &info->totalswap) || + __put_user (s.freeswap, &info->freeswap) || + __put_user (s.procs, &info->procs)) + return -EFAULT; + return 0; +} + +extern asmlinkage long sys_sched_rr_get_interval(pid_t pid, + struct timespec *interval); + +asmlinkage long +sys32_sched_rr_get_interval(__kernel_pid_t32 pid, struct timespec32 *interval) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_sched_rr_get_interval(pid, &t); + set_fs (old_fs); + if (verify_area(VERIFY_WRITE, interval, sizeof(struct timespec32)) || + __put_user (t.tv_sec, &interval->tv_sec) || + __put_user (t.tv_nsec, &interval->tv_nsec)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_sigprocmask(int how, old_sigset_t *set, + old_sigset_t *oset); + +asmlinkage long +sys32_sigprocmask(int how, old_sigset32_t *set, old_sigset32_t *oset) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + if (set && get_user (s, set)) return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL); + set_fs (old_fs); + if (ret) return ret; + if (oset && put_user (s, oset)) return -EFAULT; + return 0; +} + +extern asmlinkage long sys_sigpending(old_sigset_t *set); + +asmlinkage long +sys32_sigpending(old_sigset32_t *set) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_sigpending(&s); + set_fs (old_fs); + if (put_user (s, set)) return -EFAULT; + return ret; +} + +extern asmlinkage long sys_rt_sigpending(sigset_t *set, size_t sigsetsize); + +asmlinkage long +sys32_rt_sigpending(sigset32_t *set, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_rt_sigpending(&s, sigsetsize); + set_fs (old_fs); + if (!ret) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (set, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return ret; +} + +siginfo_t32 * +siginfo64to32(siginfo_t32 *d, siginfo_t *s) +{ + memset (d, 0, sizeof(siginfo_t32)); + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + /* XXX: Ouch, how to find this out??? */ + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (long)(s->si_addr); +// d->si_trapno = s->si_trapno; + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +siginfo_t * +siginfo32to64(siginfo_t *d, siginfo_t32 *s) +{ + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + /* XXX: Ouch, how to find this out??? */ + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (void *)A(s->si_addr); +// d->si_trapno = s->si_trapno; + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +extern asmlinkage long +sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, + const struct timespec *uts, size_t sigsetsize); + +asmlinkage long +sys32_rt_sigtimedwait(sigset32_t *uthese, siginfo_t32 *uinfo, + struct timespec32 *uts, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset32_t s32; + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs(); + siginfo_t info; + siginfo_t32 info32; + + if (copy_from_user (&s32, uthese, sizeof(sigset32_t))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + if (uts) { + if (verify_area(VERIFY_READ, uts, sizeof(struct timespec32)) || + __get_user (t.tv_sec, &uts->tv_sec) || + __get_user (t.tv_nsec, &uts->tv_nsec)) + return -EFAULT; + } + set_fs (KERNEL_DS); + ret = sys_rt_sigtimedwait(&s, &info, &t, sigsetsize); + set_fs (old_fs); + if (ret >= 0 && uinfo) { + if (copy_to_user (uinfo, siginfo64to32(&info32, &info), + sizeof(siginfo_t32))) + return -EFAULT; + } + return ret; +} + +extern asmlinkage long +sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo); + +asmlinkage long +sys32_rt_sigqueueinfo(int pid, int sig, siginfo_t32 *uinfo) +{ + siginfo_t info; + siginfo_t32 info32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (copy_from_user (&info32, uinfo, sizeof(siginfo_t32))) + return -EFAULT; + /* XXX: Is this correct? */ + siginfo32to64(&info, &info32); + set_fs (KERNEL_DS); + ret = sys_rt_sigqueueinfo(pid, sig, &info); + set_fs (old_fs); + return ret; +} + +extern asmlinkage long sys_setreuid(uid_t ruid, uid_t euid); + +asmlinkage long sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid) +{ + uid_t sruid, seuid; + + sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid); + seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid); + return sys_setreuid(sruid, seuid); +} + +extern asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid); + +asmlinkage long +sys32_setresuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid, + __kernel_uid_t32 suid) +{ + uid_t sruid, seuid, ssuid; + + sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid); + seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid); + ssuid = (suid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)suid); + return sys_setresuid(sruid, seuid, ssuid); +} + +extern asmlinkage long sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); + +asmlinkage long +sys32_getresuid(__kernel_uid_t32 *ruid, __kernel_uid_t32 *euid, + __kernel_uid_t32 *suid) +{ + uid_t a, b, c; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getresuid(&a, &b, &c); + set_fs (old_fs); + if (put_user (a, ruid) || put_user (b, euid) || put_user (c, suid)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_setregid(gid_t rgid, gid_t egid); + +asmlinkage long +sys32_setregid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid) +{ + gid_t srgid, segid; + + srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid); + segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid); + return sys_setregid(srgid, segid); +} + +extern asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid); + +asmlinkage long +sys32_setresgid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid, + __kernel_gid_t32 sgid) +{ + gid_t srgid, segid, ssgid; + + srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid); + segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid); + ssgid = (sgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)sgid); + return sys_setresgid(srgid, segid, ssgid); +} + +extern asmlinkage long sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); + +asmlinkage long +sys32_getresgid(__kernel_gid_t32 *rgid, __kernel_gid_t32 *egid, + __kernel_gid_t32 *sgid) +{ + gid_t a, b, c; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getresgid(&a, &b, &c); + set_fs (old_fs); + if (!ret) { + ret = put_user (a, rgid); + ret |= put_user (b, egid); + ret |= put_user (c, sgid); + } + return ret; +} + +extern asmlinkage long sys_getgroups(int gidsetsize, gid_t *grouplist); + +asmlinkage long +sys32_getgroups(int gidsetsize, __kernel_gid_t32 *grouplist) +{ + gid_t gl[NGROUPS]; + int ret, i; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_getgroups(gidsetsize, gl); + set_fs (old_fs); + if (gidsetsize && ret > 0 && ret <= NGROUPS) + for (i = 0; i < ret; i++, grouplist++) + if (put_user (gl[i], grouplist)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist); + +asmlinkage long +sys32_setgroups(int gidsetsize, __kernel_gid_t32 *grouplist) +{ + gid_t gl[NGROUPS]; + int ret, i; + mm_segment_t old_fs = get_fs (); + + if ((unsigned) gidsetsize > NGROUPS) + return -EINVAL; + for (i = 0; i < gidsetsize; i++, grouplist++) + if (get_user (gl[i], grouplist)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_setgroups(gidsetsize, gl); + set_fs (old_fs); + return ret; +} + + +extern void check_pending(int signum); + +asmlinkage long sys_utimes(char *, struct timeval *); + +asmlinkage long +sys32_utimes(char *filename, struct timeval32 *tvs) +{ + char *kfilename; + struct timeval ktvs[2]; + mm_segment_t old_fs; + int ret; + + kfilename = getname32(filename); + ret = PTR_ERR(kfilename); + if (!IS_ERR(kfilename)) { + if (tvs) { + if (get_tv32(&ktvs[0], tvs) || + get_tv32(&ktvs[1], 1+tvs)) + return -EFAULT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_utimes(kfilename, &ktvs[0]); + set_fs(old_fs); + + putname(kfilename); + } + return ret; +} + +/* These are here just in case some old ia32 binary calls it. */ +asmlinkage long +sys32_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + + +struct sysctl_ia32 { + unsigned int name; + int nlen; + unsigned int oldval; + unsigned int oldlenp; + unsigned int newval; + unsigned int newlen; + unsigned int __unused[4]; +}; + + +asmlinkage long +sys32_sysctl(struct sysctl_ia32 *args32) +{ +#ifndef CONFIG_SYSCTL + return -ENOSYS; +#else + struct sysctl_ia32 a32; + mm_segment_t old_fs = get_fs (); + void *oldvalp, *newvalp; + size_t oldlen; + int *namep; + long ret; + extern int do_sysctl(int *name, int nlen, void *oldval, size_t *oldlenp, + void *newval, size_t newlen); + + + if (copy_from_user(&a32, args32, sizeof (a32))) + return -EFAULT; + + /* + * We need to pre-validate these because we have to disable address checking + * before calling do_sysctl() because of OLDLEN but we can't run the risk of the + * user specifying bad addresses here. Well, since we're dealing with 32 bit + * addresses, we KNOW that access_ok() will always succeed, so this is an + * expensive NOP, but so what... + */ + namep = (int *) A(a32.name); + oldvalp = (void *) A(a32.oldval); + newvalp = (void *) A(a32.newval); + + if ((oldvalp && get_user(oldlen, (int *) A(a32.oldlenp))) + || !access_ok(VERIFY_WRITE, namep, 0) + || !access_ok(VERIFY_WRITE, oldvalp, 0) + || !access_ok(VERIFY_WRITE, newvalp, 0)) + return -EFAULT; + + set_fs(KERNEL_DS); + lock_kernel(); + ret = do_sysctl(namep, a32.nlen, oldvalp, &oldlen, newvalp, (size_t) a32.newlen); + unlock_kernel(); + set_fs(old_fs); + + if (oldvalp && put_user (oldlen, (int *) A(a32.oldlenp))) + return -EFAULT; + + return ret; +#endif +} + +extern asmlinkage long sys_newuname(struct new_utsname * name); + +asmlinkage long +sys32_newuname(struct new_utsname * name) +{ + int ret = sys_newuname(name); + + if (current->personality == PER_LINUX32 && !ret) { + ret = copy_to_user(name->machine, "i386\0\0", 8); + } + return ret; +} + +extern asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, + size_t count, loff_t pos); + +extern asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, + size_t count, loff_t pos); + +typedef __kernel_ssize_t32 ssize_t32; + +asmlinkage ssize_t32 +sys32_pread(unsigned int fd, char *ubuf, __kernel_size_t32 count, + u32 poshi, u32 poslo) +{ + return sys_pread(fd, ubuf, count, + ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + +asmlinkage ssize_t32 +sys32_pwrite(unsigned int fd, char *ubuf, __kernel_size_t32 count, + u32 poshi, u32 poslo) +{ + return sys_pwrite(fd, ubuf, count, + ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + + +extern asmlinkage long sys_personality(unsigned long); + +asmlinkage long +sys32_personality(unsigned long personality) +{ + int ret; + if (current->personality == PER_LINUX32 && personality == PER_LINUX) + personality = PER_LINUX32; + ret = sys_personality(personality); + if (ret == PER_LINUX32) + ret = PER_LINUX; + return ret; +} + +extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, + size_t count); + +asmlinkage long +sys32_sendfile(int out_fd, int in_fd, __kernel_off_t32 *offset, s32 count) +{ + mm_segment_t old_fs = get_fs(); + int ret; + off_t of; + + if (offset && get_user(of, offset)) + return -EFAULT; + + set_fs(KERNEL_DS); + ret = sys_sendfile(out_fd, in_fd, offset ? &of : NULL, count); + set_fs(old_fs); + + if (!ret && offset && put_user(of, offset)) + return -EFAULT; + + return ret; +} + +/* Handle adjtimex compatability. */ + +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct timeval32 time; + s32 tick; + s32 ppsfreq, jitter, shift, stabil; + s32 jitcnt, calcnt, errcnt, stbcnt; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; +}; + +extern int do_adjtimex(struct timex *); + +asmlinkage long +sys32_adjtimex(struct timex32 *utp) +{ + struct timex txc; + int ret; + + memset(&txc, 0, sizeof(struct timex)); + + if(verify_area(VERIFY_READ, utp, sizeof(struct timex32)) || + __get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + if(verify_area(VERIFY_WRITE, utp, sizeof(struct timex32)) || + __put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + + return ret; +} + + +/* common code for old and new mmaps */ +static inline long do_mmap2( + unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + int error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + + +asmlinkage int sys32_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down_read(&uts_sem); + + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + __put_user(0,name->sysname+__OLD_UTS_LEN); + __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + __put_user(0,name->nodename+__OLD_UTS_LEN); + __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + __put_user(0,name->release+__OLD_UTS_LEN); + __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + __put_user(0,name->version+__OLD_UTS_LEN); + { + char *arch = current->personality == PER_LINUX32 + ? "i386" : "x86_64"; + + __copy_to_user(&name->machine,arch,strlen(arch)+1); + } + + up_read(&uts_sem); + + error = error ? -EFAULT : 0; + + return error; +} + +int sys32_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +extern int sys_ustat(dev_t, struct ustat *); + +int sys32_ustat(dev_t dev, struct ustat32 *u32p) +{ + struct ustat u; + mm_segment_t seg; + int ret; + + seg = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ustat(dev,&u); + set_fs(seg); + if (ret >= 0) { + if (!access_ok(VERIFY_WRITE,u32p,sizeof(struct ustat32)) || + __put_user((__u32) u.f_tfree, &u32p->f_tfree) || + __put_user((__u32) u.f_tinode, &u32p->f_tfree) || + __copy_to_user(&u32p->f_fname, u.f_fname, sizeof(u.f_fname)) || + __copy_to_user(&u32p->f_fpack, u.f_fpack, sizeof(u.f_fpack))) + ret = -EFAULT; + } + return ret; +} + +static int nargs(u32 src, char **dst) +{ + int cnt; + u32 val; + + cnt = 0; + do { + int ret = get_user(val, (__u32 *)(u64)src); + if (ret) { + return ret; + } + if (dst) + dst[cnt] = (char *)(u64)val; + cnt++; + src += 4; + } while(val && cnt < 1023); // XXX: fix limit. + if (dst) + dst[cnt-1] = 0; + return cnt; +} + +int sys32_execve(char *name, u32 argv, u32 envp, struct pt_regs regs) +{ + mm_segment_t oldseg; + char **buf; + int na,ne; + int ret; + + na = nargs(argv, NULL); + if (na < 0) + return -EFAULT; + ne = nargs(envp, NULL); + if (ne < 0) + return -EFAULT; + + buf = kmalloc((na+ne)*sizeof(char*), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = nargs(argv, buf); + if (ret < 0) + goto free; + + ret = nargs(envp, buf + na); + if (ret < 0) + goto free; + + name = getname(name); + ret = PTR_ERR(name); + if (IS_ERR(name)) + goto free; + + oldseg = get_fs(); + set_fs(KERNEL_DS); + ret = do_execve(name, buf, buf+na, ®s); + set_fs(oldseg); + + if (ret == 0) + current->ptrace &= ~PT_DTRACE; + + putname(name); + +free: + kfree(buf); + return ret; +} + +asmlinkage int sys32_fork(struct pt_regs regs) +{ + return do_fork(SIGCHLD, regs.rsp, ®s, 0); +} + +asmlinkage int sys32_clone(unsigned int clone_flags, unsigned int newsp, struct pt_regs regs) +{ + if (!newsp) + newsp = regs.rsp; + return do_fork(clone_flags, newsp, ®s, 0); +} + +/* + * This is trivial, and on the face of it looks like it + * could equally well be done in user mode. + * + * Not so, for quite unobvious reasons - register pressure. + * In user mode vfork() cannot have a stack frame, and if + * done by calling the "clone()" system call directly, you + * do not have enough call-clobbered registers to hold all + * the information you need. + */ +asmlinkage int sys32_vfork(struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, ®s, 0); +} + +/* + * Some system calls that need sign extended arguments. This could be done by a generic wrapper. + */ + +extern off_t sys_lseek (unsigned int fd, off_t offset, unsigned int origin); + +int sys32_lseek (unsigned int fd, int offset, unsigned int whence) +{ + return sys_lseek(fd, offset, whence); +} + +extern int sys_kill(pid_t pid, int sig); + +int sys32_kill(int pid, int sig) +{ + return sys_kill(pid, sig); +} + diff -Nru a/arch/x86_64/kernel/Makefile b/arch/x86_64/kernel/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,38 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.o: + $(CC) $(AFLAGS) -traditional -c $< -o $*.o + +all: kernel.o head.o head64.o init_task.o + +O_TARGET := kernel.o + + +export-objs := mtrr.o msr.o cpuid.o x8664_ksyms.o + +obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o \ + ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_x86_64.o \ + pci-dma.o x8664_ksyms.o i387.o syscall.o early_printk.o vsyscall.o \ + setup64.o bluesmoke.o + +ifdef CONFIG_PCI +obj-y += pci-x86_64.o +obj-y += pci-pc.o pci-irq.o +endif + +obj-$(CONFIG_MTRR) += mtrr.o +obj-$(CONFIG_X86_MSR) += msr.o +obj-$(CONFIG_X86_CPUID) += cpuid.o +obj-$(CONFIG_SMP) += smp.o smpboot.o trampoline.o +obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o +obj-$(CONFIG_X86_IO_APIC) += io_apic.o mpparse.o + +include $(TOPDIR)/Rules.make + diff -Nru a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/apic.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1160 @@ +/* + * Local APIC handling, local APIC timers + * + * (c) 1999, 2000 Ingo Molnar + * + * Fixes + * Maciej W. Rozycki : Bits for genuine 82489DX APICs; + * thanks to Eric Gilmore + * and Rolf G. Tews + * for testing these extensively. + * Maciej W. Rozycki : Various updates and fixes. + * Mikael Pettersson : Power Management for UP-APIC. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Using APIC to generate smp_local_timer_interrupt? */ +int using_apic_timer = 0; + +int prof_multiplier[NR_CPUS] = { 1, }; +int prof_old_multiplier[NR_CPUS] = { 1, }; +int prof_counter[NR_CPUS] = { 1, }; + +int get_maxlvt(void) +{ + unsigned int v, ver, maxlvt; + + v = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(v); + /* 82489DXs do not report # of LVT entries. */ + maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(v) : 2; + return maxlvt; +} + +void clear_local_APIC(void) +{ + int maxlvt; + unsigned long v; + + maxlvt = get_maxlvt(); + + /* + * Masking an LVT entry on a P6 can trigger a local APIC error + * if the vector is zero. Mask LVTERR first to prevent this. + */ + if (maxlvt >= 3) { + v = ERROR_APIC_VECTOR; /* any non-zero vector will do */ + apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED); + } + /* + * Careful: we have to set masks only first to deassert + * any level-triggered sources. + */ + v = apic_read(APIC_LVTT); + apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); + v = apic_read(APIC_LVT0); + apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED); + v = apic_read(APIC_LVT1); + apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED); + if (maxlvt >= 4) { + v = apic_read(APIC_LVTPC); + apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED); + } + + /* + * Clean APIC state for other OSs: + */ + apic_write_around(APIC_LVTT, APIC_LVT_MASKED); + apic_write_around(APIC_LVT0, APIC_LVT_MASKED); + apic_write_around(APIC_LVT1, APIC_LVT_MASKED); + if (maxlvt >= 3) + apic_write_around(APIC_LVTERR, APIC_LVT_MASKED); + if (maxlvt >= 4) + apic_write_around(APIC_LVTPC, APIC_LVT_MASKED); + v = GET_APIC_VERSION(apic_read(APIC_LVR)); + if (APIC_INTEGRATED(v)) { /* !82489DX */ + if (maxlvt > 3) + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + } +} + +void __init connect_bsp_APIC(void) +{ + if (pic_mode) { + /* + * Do not trust the local APIC being empty at bootup. + */ + clear_local_APIC(); + /* + * PIC mode, enable APIC mode in the IMCR, i.e. + * connect BSP's local APIC to INT and NMI lines. + */ + printk("leaving PIC mode, enabling APIC mode.\n"); + outb(0x70, 0x22); + outb(0x01, 0x23); + } +} + +void disconnect_bsp_APIC(void) +{ + if (pic_mode) { + /* + * Put the board back into PIC mode (has an effect + * only on certain older boards). Note that APIC + * interrupts, including IPIs, won't work beyond + * this point! The only exception are INIT IPIs. + */ + printk("disabling APIC mode, entering PIC mode.\n"); + outb(0x70, 0x22); + outb(0x00, 0x23); + } +} + +void disable_local_APIC(void) +{ + unsigned long value; + + clear_local_APIC(); + + /* + * Disable APIC (implies clearing of registers + * for 82489DX!). + */ + value = apic_read(APIC_SPIV); + value &= ~APIC_SPIV_APIC_ENABLED; + apic_write_around(APIC_SPIV, value); +} + +/* + * This is to verify that we're looking at a real local APIC. + * Check these against your board if the CPUs aren't getting + * started for no apparent reason. + */ +int __init verify_local_APIC(void) +{ + unsigned int reg0, reg1; + + /* + * The version register is read-only in a real APIC. + */ + reg0 = apic_read(APIC_LVR); + Dprintk("Getting VERSION: %x\n", reg0); + apic_write(APIC_LVR, reg0 ^ APIC_LVR_MASK); + reg1 = apic_read(APIC_LVR); + Dprintk("Getting VERSION: %x\n", reg1); + + /* + * The two version reads above should print the same + * numbers. If the second one is different, then we + * poke at a non-APIC. + */ + if (reg1 != reg0) + return 0; + + /* + * Check if the version looks reasonably. + */ + reg1 = GET_APIC_VERSION(reg0); + if (reg1 == 0x00 || reg1 == 0xff) + return 0; + reg1 = get_maxlvt(); + if (reg1 < 0x02 || reg1 == 0xff) + return 0; + + /* + * The ID register is read/write in a real APIC. + */ + reg0 = apic_read(APIC_ID); + Dprintk("Getting ID: %x\n", reg0); + apic_write(APIC_ID, reg0 ^ APIC_ID_MASK); + reg1 = apic_read(APIC_ID); + Dprintk("Getting ID: %x\n", reg1); + apic_write(APIC_ID, reg0); + if (reg1 != (reg0 ^ APIC_ID_MASK)) + return 0; + + /* + * The next two are just to see if we have sane values. + * They're only really relevant if we're in Virtual Wire + * compatibility mode, but most boxes are anymore. + */ + reg0 = apic_read(APIC_LVT0); + Dprintk("Getting LVT0: %x\n", reg0); + reg1 = apic_read(APIC_LVT1); + Dprintk("Getting LVT1: %x\n", reg1); + + return 1; +} + +void __init sync_Arb_IDs(void) +{ + /* + * Wait for idle. + */ + apic_wait_icr_idle(); + + Dprintk("Synchronizing Arb IDs.\n"); + apic_write_around(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG + | APIC_DM_INIT); +} + +extern void __error_in_apic_c (void); + +/* + * An initial setup of the virtual wire mode. + */ +void __init init_bsp_APIC(void) +{ + unsigned long value, ver; + + /* + * Don't do the setup now if we have a SMP BIOS as the + * through-I/O-APIC virtual wire mode might be active. + */ + if (smp_found_config || !cpu_has_apic) + return; + + value = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(value); + + /* + * Do not trust the local APIC being empty at bootup. + */ + clear_local_APIC(); + + /* + * Enable APIC. + */ + value = apic_read(APIC_SPIV); + value &= ~APIC_VECTOR_MASK; + value |= APIC_SPIV_APIC_ENABLED; + value |= APIC_SPIV_FOCUS_DISABLED; + value |= SPURIOUS_APIC_VECTOR; + apic_write_around(APIC_SPIV, value); + + /* + * Set up the virtual wire mode. + */ + apic_write_around(APIC_LVT0, APIC_DM_EXTINT); + value = APIC_DM_NMI; + if (!APIC_INTEGRATED(ver)) /* 82489DX */ + value |= APIC_LVT_LEVEL_TRIGGER; + apic_write_around(APIC_LVT1, value); +} + +void __init setup_local_APIC (void) +{ + unsigned long value, ver, maxlvt; + + /* Pound the ESR really hard over the head with a big hammer - mbligh */ + if (esr_disable) { + apic_write(APIC_ESR, 0); + apic_write(APIC_ESR, 0); + apic_write(APIC_ESR, 0); + apic_write(APIC_ESR, 0); + } + + value = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(value); + + if ((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f) + __error_in_apic_c(); + + /* + * Double-check wether this APIC is really registered. + * This is meaningless in clustered apic mode, so we skip it. + */ + if (!clustered_apic_mode && + !test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map)) + BUG(); + + /* + * Intel recommends to set DFR, LDR and TPR before enabling + * an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel + * document number 292116). So here it goes... + */ + + if (!clustered_apic_mode) { + /* + * In clustered apic mode, the firmware does this for us + * Put the APIC into flat delivery mode. + * Must be "all ones" explicitly for 82489DX. + */ + apic_write_around(APIC_DFR, 0xffffffff); + + /* + * Set up the logical destination ID. + */ + value = apic_read(APIC_LDR); + value &= ~APIC_LDR_MASK; + value |= (1<<(smp_processor_id()+24)); + apic_write_around(APIC_LDR, value); + } + + /* + * Set Task Priority to 'accept all'. We never change this + * later on. + */ + value = apic_read(APIC_TASKPRI); + value &= ~APIC_TPRI_MASK; + apic_write_around(APIC_TASKPRI, value); + + /* + * Now that we are all set up, enable the APIC + */ + value = apic_read(APIC_SPIV); + value &= ~APIC_VECTOR_MASK; + /* + * Enable APIC + */ + value |= APIC_SPIV_APIC_ENABLED; + + /* + * Some unknown Intel IO/APIC (or APIC) errata is biting us with + * certain networking cards. If high frequency interrupts are + * happening on a particular IOAPIC pin, plus the IOAPIC routing + * entry is masked/unmasked at a high rate as well then sooner or + * later IOAPIC line gets 'stuck', no more interrupts are received + * from the device. If focus CPU is disabled then the hang goes + * away, oh well :-( + * + * [ This bug can be reproduced easily with a level-triggered + * PCI Ne2000 networking cards and PII/PIII processors, dual + * BX chipset. ] + */ + /* + * Actually disabling the focus CPU check just makes the hang less + * frequent as it makes the interrupt distributon model be more + * like LRU than MRU (the short-term load is more even across CPUs). + * See also the comment in end_level_ioapic_irq(). --macro + */ +#if 1 + /* Enable focus processor (bit==0) */ + value &= ~APIC_SPIV_FOCUS_DISABLED; +#else + /* Disable focus processor (bit==1) */ + value |= APIC_SPIV_FOCUS_DISABLED; +#endif + /* + * Set spurious IRQ vector + */ + value |= SPURIOUS_APIC_VECTOR; + apic_write_around(APIC_SPIV, value); + + /* + * Set up LVT0, LVT1: + * + * set up through-local-APIC on the BP's LINT0. This is not + * strictly necessery in pure symmetric-IO mode, but sometimes + * we delegate interrupts to the 8259A. + */ + /* + * TODO: set up through-local-APIC from through-I/O-APIC? --macro + */ + value = apic_read(APIC_LVT0) & APIC_LVT_MASKED; + if (!smp_processor_id() && (pic_mode || !value)) { + value = APIC_DM_EXTINT; + printk("enabled ExtINT on CPU#%d\n", smp_processor_id()); + } else { + value = APIC_DM_EXTINT | APIC_LVT_MASKED; + printk("masked ExtINT on CPU#%d\n", smp_processor_id()); + } + apic_write_around(APIC_LVT0, value); + + /* + * only the BP should see the LINT1 NMI signal, obviously. + */ + if (!smp_processor_id()) + value = APIC_DM_NMI; + else + value = APIC_DM_NMI | APIC_LVT_MASKED; + if (!APIC_INTEGRATED(ver)) /* 82489DX */ + value |= APIC_LVT_LEVEL_TRIGGER; + apic_write_around(APIC_LVT1, value); + + if (APIC_INTEGRATED(ver) && !esr_disable) { /* !82489DX */ + maxlvt = get_maxlvt(); + if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ + apic_write(APIC_ESR, 0); + value = apic_read(APIC_ESR); + printk("ESR value before enabling vector: %08lx\n", value); + + value = ERROR_APIC_VECTOR; // enables sending errors + apic_write_around(APIC_LVTERR, value); + /* + * spec says clear errors after enabling vector. + */ + if (maxlvt > 3) + apic_write(APIC_ESR, 0); + value = apic_read(APIC_ESR); + printk("ESR value after enabling vector: %08lx\n", value); + } else { + if (esr_disable) + /* + * Something untraceble is creating bad interrupts on + * secondary quads ... for the moment, just leave the + * ESR disabled - we can't do anything useful with the + * errors anyway - mbligh + */ + printk("Leaving ESR disabled.\n"); + else + printk("No ESR for 82489DX.\n"); + } + + if (nmi_watchdog == NMI_LOCAL_APIC) + setup_apic_nmi_watchdog(); +} + +#ifdef CONFIG_PM + +#include +#include + +static struct { + /* 'active' is true if the local APIC was enabled by us and + not the BIOS; this signifies that we are also responsible + for disabling it before entering apm/acpi suspend */ + int active; + /* 'perfctr_pmdev' is here because the current (2.4.1) PM + callback system doesn't handle hierarchical dependencies */ + struct pm_dev *perfctr_pmdev; + /* r/w apic fields */ + unsigned int apic_id; + unsigned int apic_taskpri; + unsigned int apic_ldr; + unsigned int apic_dfr; + unsigned int apic_spiv; + unsigned int apic_lvtt; + unsigned int apic_lvtpc; + unsigned int apic_lvt0; + unsigned int apic_lvt1; + unsigned int apic_lvterr; + unsigned int apic_tmict; + unsigned int apic_tdcr; +} apic_pm_state; + +static void apic_pm_suspend(void *data) +{ + unsigned int l, h; + unsigned long flags; + + if (apic_pm_state.perfctr_pmdev) + pm_send(apic_pm_state.perfctr_pmdev, PM_SUSPEND, data); + apic_pm_state.apic_id = apic_read(APIC_ID); + apic_pm_state.apic_taskpri = apic_read(APIC_TASKPRI); + apic_pm_state.apic_ldr = apic_read(APIC_LDR); + apic_pm_state.apic_dfr = apic_read(APIC_DFR); + apic_pm_state.apic_spiv = apic_read(APIC_SPIV); + apic_pm_state.apic_lvtt = apic_read(APIC_LVTT); + apic_pm_state.apic_lvtpc = apic_read(APIC_LVTPC); + apic_pm_state.apic_lvt0 = apic_read(APIC_LVT0); + apic_pm_state.apic_lvt1 = apic_read(APIC_LVT1); + apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR); + apic_pm_state.apic_tmict = apic_read(APIC_TMICT); + apic_pm_state.apic_tdcr = apic_read(APIC_TDCR); + __save_flags(flags); + __cli(); + disable_local_APIC(); + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_ENABLE; + wrmsr(MSR_IA32_APICBASE, l, h); + __restore_flags(flags); +} + +static void apic_pm_resume(void *data) +{ + unsigned int l, h; + unsigned long flags; + + __save_flags(flags); + __cli(); + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE; + wrmsr(MSR_IA32_APICBASE, l, h); + apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED); + apic_write(APIC_ID, apic_pm_state.apic_id); + apic_write(APIC_DFR, apic_pm_state.apic_dfr); + apic_write(APIC_LDR, apic_pm_state.apic_ldr); + apic_write(APIC_TASKPRI, apic_pm_state.apic_taskpri); + apic_write(APIC_SPIV, apic_pm_state.apic_spiv); + apic_write(APIC_LVT0, apic_pm_state.apic_lvt0); + apic_write(APIC_LVT1, apic_pm_state.apic_lvt1); + apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc); + apic_write(APIC_LVTT, apic_pm_state.apic_lvtt); + apic_write(APIC_TDCR, apic_pm_state.apic_tdcr); + apic_write(APIC_TMICT, apic_pm_state.apic_tmict); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + __restore_flags(flags); + if (apic_pm_state.perfctr_pmdev) + pm_send(apic_pm_state.perfctr_pmdev, PM_RESUME, data); +} + +static int apic_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + switch (rqst) { + case PM_SUSPEND: + apic_pm_suspend(data); + break; + case PM_RESUME: + apic_pm_resume(data); + break; + } + return 0; +} + +/* perfctr driver should call this instead of pm_register() */ +struct pm_dev *apic_pm_register(pm_dev_t type, + unsigned long id, + pm_callback callback) +{ + struct pm_dev *dev; + + if (!apic_pm_state.active) + return pm_register(type, id, callback); + if (apic_pm_state.perfctr_pmdev) + return NULL; /* we're busy */ + dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL); + if (dev) { + memset(dev, 0, sizeof(*dev)); + dev->type = type; + dev->id = id; + dev->callback = callback; + apic_pm_state.perfctr_pmdev = dev; + } + return dev; +} + +/* perfctr driver should call this instead of pm_unregister() */ +void apic_pm_unregister(struct pm_dev *dev) +{ + if (!apic_pm_state.active) { + pm_unregister(dev); + } else if (dev == apic_pm_state.perfctr_pmdev) { + apic_pm_state.perfctr_pmdev = NULL; + kfree(dev); + } +} + +static void __init apic_pm_init1(void) +{ + /* can't pm_register() at this early stage in the boot process + (causes an immediate reboot), so just set the flag */ + apic_pm_state.active = 1; +} + +static void __init apic_pm_init2(void) +{ + if (apic_pm_state.active) + pm_register(PM_SYS_DEV, 0, apic_pm_callback); +} + +#else /* CONFIG_PM */ + +static inline void apic_pm_init1(void) { } +static inline void apic_pm_init2(void) { } + +#endif /* CONFIG_PM */ + +/* + * Detect and enable local APICs on non-SMP boards. + * Original code written by Keir Fraser. + */ + +static int __init detect_init_APIC (void) +{ + u32 h, l, features; + int needs_pm = 0; + extern void get_cpu_vendor(struct cpuinfo_x86*); + + /* Workaround for us being called before identify_cpu(). */ + get_cpu_vendor(&boot_cpu_data); + + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model > 1) + break; + goto no_apic; + case X86_VENDOR_INTEL: + if (boot_cpu_data.x86 == 6 || + (boot_cpu_data.x86 == 15 && cpu_has_apic) || + (boot_cpu_data.x86 == 5 && cpu_has_apic)) + break; + goto no_apic; + default: + goto no_apic; + } + + if (!cpu_has_apic) { + /* + * Some BIOSes disable the local APIC in the + * APIC_BASE MSR. This can only be done in + * software for Intel P6 and AMD K7 (Model > 1). + */ + rdmsr(MSR_IA32_APICBASE, l, h); + if (!(l & MSR_IA32_APICBASE_ENABLE)) { + printk("Local APIC disabled by BIOS -- reenabling.\n"); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE; + wrmsr(MSR_IA32_APICBASE, l, h); + needs_pm = 1; + } + } + /* + * The APIC feature bit should now be enabled + * in `cpuid' + */ + features = cpuid_edx(1); + if (!(features & (1 << X86_FEATURE_APIC))) { + printk("Could not enable APIC!\n"); + return -1; + } + set_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability); + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; + boot_cpu_id = 0; + if (nmi_watchdog != NMI_NONE) + nmi_watchdog = NMI_LOCAL_APIC; + + printk("Found and enabled local APIC!\n"); + + if (needs_pm) + apic_pm_init1(); + + return 0; + +no_apic: + printk("No local APIC present or hardware disabled\n"); + return -1; +} + +void __init init_apic_mappings(void) +{ + unsigned long apic_phys; + + /* + * If no local APIC can be found then set up a fake all + * zeroes page to simulate the local APIC and another + * one for the IO-APIC. + */ + if (!smp_found_config && detect_init_APIC()) { + apic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); + apic_phys = __pa(apic_phys); + } else + apic_phys = mp_lapic_addr; + + set_fixmap_nocache(FIX_APIC_BASE, apic_phys); + Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys); + + /* + * Fetch the APIC ID of the BSP in case we have a + * default configuration (or the MP table is broken). + */ + if (boot_cpu_id == -1U) + boot_cpu_id = GET_APIC_ID(apic_read(APIC_ID)); + +#ifdef CONFIG_X86_IO_APIC + { + unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0; + int i; + + for (i = 0; i < nr_ioapics; i++) { + if (smp_found_config) { + ioapic_phys = mp_ioapics[i].mpc_apicaddr; + } else { + ioapic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); + ioapic_phys = __pa(ioapic_phys); + } + set_fixmap_nocache(idx, ioapic_phys); + Dprintk("mapped IOAPIC to %08lx (%08lx)\n", + __fix_to_virt(idx), ioapic_phys); + idx++; + } + } +#endif +} + +/* + * This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts + * per second. We assume that the caller has already set up the local + * APIC. + * + * The APIC timer is not exactly sync with the external timer chip, it + * closely follows bus clocks. + */ + +/* + * The timer chip is already set up at HZ interrupts per second here, + * but we do not accept timer interrupts yet. We only allow the BP + * to calibrate. + */ +static unsigned int __init get_8254_timer_count(void) +{ + extern spinlock_t i8253_lock; + unsigned long flags; + + unsigned int count; + + spin_lock_irqsave(&i8253_lock, flags); + + outb_p(0x00, 0x43); + count = inb_p(0x40); + count |= inb_p(0x40) << 8; + + spin_unlock_irqrestore(&i8253_lock, flags); + + return count; +} + +void __init wait_8254_wraparound(void) +{ + unsigned int curr_count, prev_count=~0; + int delta; + + curr_count = get_8254_timer_count(); + + do { + prev_count = curr_count; + curr_count = get_8254_timer_count(); + delta = curr_count-prev_count; + + /* + * This limit for delta seems arbitrary, but it isn't, it's + * slightly above the level of error a buggy Mercury/Neptune + * chipset timer can cause. + */ + + } while (delta < 300); +} + +/* + * This function sets up the local APIC timer, with a timeout of + * 'clocks' APIC bus clock. During calibration we actually call + * this function twice on the boot CPU, once with a bogus timeout + * value, second time for real. The other (noncalibrating) CPUs + * call this function only once, with the real, calibrated value. + * + * We do reads before writes even if unnecessary, to get around the + * P5 APIC double write bug. + */ + +#define APIC_DIVISOR 16 + +void __setup_APIC_LVTT(unsigned int clocks) +{ + unsigned int lvtt1_value, tmp_value; + + lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) | + APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; + apic_write_around(APIC_LVTT, lvtt1_value); + + /* + * Divide PICLK by 16 + */ + tmp_value = apic_read(APIC_TDCR); + apic_write_around(APIC_TDCR, (tmp_value + & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) + | APIC_TDR_DIV_16); + + apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR); +} + +void setup_APIC_timer(void * data) +{ + unsigned long clocks = (unsigned long) data, slice, t0, t1; + unsigned long flags; + int delta; + + __save_flags(flags); + __sti(); + /* + * ok, Intel has some smart code in their APIC that knows + * if a CPU was in 'hlt' lowpower mode, and this increases + * its APIC arbitration priority. To avoid the external timer + * IRQ APIC event being in synchron with the APIC clock we + * introduce an interrupt skew to spread out timer events. + * + * The number of slices within a 'big' timeslice is smp_num_cpus+1 + */ + + slice = clocks / (smp_num_cpus+1); + printk("cpu: %d, clocks: %lu, slice: %lu\n", smp_processor_id(), clocks, slice); + + /* + * Wait for IRQ0's slice: + */ + wait_8254_wraparound(); + + __setup_APIC_LVTT(clocks); + + t0 = apic_read(APIC_TMICT)*APIC_DIVISOR; + /* Wait till TMCCT gets reloaded from TMICT... */ + do { + t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR; + delta = (int)(t0 - t1 - slice*(smp_processor_id()+1)); + } while (delta >= 0); + /* Now wait for our slice for real. */ + do { + t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR; + delta = (int)(t0 - t1 - slice*(smp_processor_id()+1)); + } while (delta < 0); + + __setup_APIC_LVTT(clocks); + + printk("CPU%d\n", smp_processor_id(), t0, t1, delta, slice, clocks); + + __restore_flags(flags); +} + +/* + * In this function we calibrate APIC bus clocks to the external + * timer. Unfortunately we cannot use jiffies and the timer irq + * to calibrate, since some later bootup code depends on getting + * the first irq? Ugh. + * + * We want to do the calibration only once since we + * want to have local timer irqs syncron. CPUs connected + * by the same APIC bus have the very same bus frequency. + * And we want to have irqs off anyways, no accidental + * APIC irq that way. + */ + +int __init calibrate_APIC_clock(void) +{ + unsigned long long t1 = 0, t2 = 0; + long tt1, tt2; + long result; + int i; + const int LOOPS = HZ/10; + + printk("calibrating APIC timer ...\n"); + + /* + * Put whatever arbitrary (but long enough) timeout + * value into the APIC clock, we just want to get the + * counter running for calibration. + */ + __setup_APIC_LVTT(1000000000); + + /* + * The timer chip counts down to zero. Let's wait + * for a wraparound to start exact measurement: + * (the current tick might have been already half done) + */ + + wait_8254_wraparound(); + + /* + * We wrapped around just now. Let's start: + */ + if (cpu_has_tsc) + rdtscll(t1); + tt1 = apic_read(APIC_TMCCT); + + /* + * Let's wait LOOPS wraprounds: + */ + for (i = 0; i < LOOPS; i++) + wait_8254_wraparound(); + + tt2 = apic_read(APIC_TMCCT); + if (cpu_has_tsc) + rdtscll(t2); + + /* + * The APIC bus clock counter is 32 bits only, it + * might have overflown, but note that we use signed + * longs, thus no extra care needed. + * + * underflown to be exact, as the timer counts down ;) + */ + + result = (tt1-tt2)*APIC_DIVISOR/LOOPS; + + if (cpu_has_tsc) + printk("..... CPU clock speed is %ld.%04ld MHz.\n", + ((long)(t2-t1)/LOOPS)/(1000000/HZ), + ((long)(t2-t1)/LOOPS)%(1000000/HZ)); + + printk("..... host bus clock speed is %ld.%04ld MHz.\n", + result/(1000000/HZ), + result%(1000000/HZ)); + + return result; +} + +static unsigned long calibration_result; + +void __init setup_APIC_clocks (void) +{ + printk("Using local APIC timer interrupts.\n"); + using_apic_timer = 1; + + __cli(); + + calibration_result = calibrate_APIC_clock(); + /* + * Now set up the timer for real. + */ + setup_APIC_timer((void *)calibration_result); + + __sti(); + + /* and update all other cpus */ + smp_call_function(setup_APIC_timer, (void *)calibration_result, 1, 1); +} + +void __init disable_APIC_timer(void) +{ + if (using_apic_timer) { + unsigned long v; + + v = apic_read(APIC_LVTT); + apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); + } +} + +void enable_APIC_timer(void) +{ + if (using_apic_timer) { + unsigned long v; + + v = apic_read(APIC_LVTT); + apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED); + } +} + +/* + * the frequency of the profiling timer can be changed + * by writing a multiplier value into /proc/profile. + */ +int setup_profiling_timer(unsigned int multiplier) +{ + int i; + + /* + * Sanity check. [at least 500 APIC cycles should be + * between APIC interrupts as a rule of thumb, to avoid + * irqs flooding us] + */ + if ( (!multiplier) || (calibration_result/multiplier < 500)) + return -EINVAL; + + /* + * Set the new multiplier for each CPU. CPUs don't start using the + * new values until the next timer interrupt in which they do process + * accounting. At that time they also adjust their APIC timers + * accordingly. + */ + for (i = 0; i < NR_CPUS; ++i) + prof_multiplier[i] = multiplier; + + return 0; +} + +#undef APIC_DIVISOR + +/* + * Local timer interrupt handler. It does both profiling and + * process statistics/rescheduling. + * + * We do profiling in every local tick, statistics/rescheduling + * happen only every 'profiling multiplier' ticks. The default + * multiplier is 1 and it can be changed by writing the new multiplier + * value into /proc/profile. + */ + +inline void smp_local_timer_interrupt(struct pt_regs * regs) +{ + int user = user_mode(regs); + int cpu = smp_processor_id(); + + /* + * The profiling function is SMP safe. (nothing can mess + * around with "current", and the profiling counters are + * updated with atomic operations). This is especially + * useful with a profiling multiplier != 1 + */ + if (!user) + x86_do_profile(regs->rip); + + if (--prof_counter[cpu] <= 0) { + /* + * The multiplier may have changed since the last time we got + * to this point as a result of the user writing to + * /proc/profile. In this case we need to adjust the APIC + * timer accordingly. + * + * Interrupts are already masked off at this point. + */ + prof_counter[cpu] = prof_multiplier[cpu]; + if (prof_counter[cpu] != prof_old_multiplier[cpu]) { + __setup_APIC_LVTT(calibration_result/prof_counter[cpu]); + prof_old_multiplier[cpu] = prof_counter[cpu]; + } + +#ifdef CONFIG_SMP + update_process_times(user); +#endif + } + + /* + * We take the 'long' return path, and there every subsystem + * grabs the apropriate locks (kernel lock/ irq lock). + * + * we might want to decouple profiling from the 'long path', + * and do the profiling totally in assembly. + * + * Currently this isn't too much of an issue (performance wise), + * we can take more than 100K local irqs per second on a 100 MHz P5. + */ +} + +/* + * Local APIC timer interrupt. This is the most natural way for doing + * local interrupts, but local timer interrupts can be emulated by + * broadcast interrupts too. [in case the hw doesnt support APIC timers] + * + * [ if a single-CPU system runs an SMP kernel then we call the local + * interrupt as well. Thus we cannot inline the local irq ... ] + */ +unsigned int apic_timer_irqs [NR_CPUS]; + +void smp_apic_timer_interrupt(struct pt_regs regs) +{ + int cpu = smp_processor_id(); + + /* + * the NMI deadlock-detector uses this. + */ + apic_timer_irqs[cpu]++; + + /* + * NOTE! We'd better ACK the irq immediately, + * because timer handling can be slow. + */ + ack_APIC_irq(); + /* + * update_process_times() expects us to have done irq_enter(). + * Besides, if we don't timer interrupts ignore the global + * interrupt lock, which is the WrongThing (tm) to do. + */ + irq_enter(cpu, 0); + smp_local_timer_interrupt(®s); + irq_exit(cpu, 0); + + if (softirq_pending(cpu)) + do_softirq(); +} + +/* + * This interrupt should _never_ happen with our APIC/SMP architecture + */ +asmlinkage void smp_spurious_interrupt(void) +{ + unsigned long v; + + /* + * Check if this really is a spurious interrupt and ACK it + * if it is a vectored one. Just in case... + * Spurious interrupts should not be ACKed. + */ + v = apic_read(APIC_ISR + ((SPURIOUS_APIC_VECTOR & ~0x1f) >> 1)); + if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f))) + ack_APIC_irq(); + + /* see sw-dev-man vol 3, chapter 7.4.13.5 */ + printk(KERN_INFO "spurious APIC interrupt on CPU#%d, should never happen.\n", + smp_processor_id()); +} + +/* + * This interrupt should never happen with our APIC/SMP architecture + */ + +asmlinkage void smp_error_interrupt(void) +{ + unsigned long v, v1; + + /* First tickle the hardware, only then report what went on. -- REW */ + v = apic_read(APIC_ESR); + apic_write(APIC_ESR, 0); + v1 = apic_read(APIC_ESR); + ack_APIC_irq(); + atomic_inc(&irq_err_count); + + /* Here is what the APIC error bits mean: + 0: Send CS error + 1: Receive CS error + 2: Send accept error + 3: Receive accept error + 4: Reserved + 5: Send illegal vector + 6: Received illegal vector + 7: Illegal register address + */ + printk (KERN_ERR "APIC error on CPU%d: %02lx(%02lx)\n", + smp_processor_id(), v , v1); +} + +/* + * This initializes the IO-APIC and APIC hardware if this is + * a UP kernel. + */ +int __init APIC_init_uniprocessor (void) +{ + if (!smp_found_config && !cpu_has_apic) + return -1; + + /* + * Complain if the BIOS pretends there is one. + */ + if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_id])) { + printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n", + boot_cpu_id); + return -1; + } + + verify_local_APIC(); + + connect_bsp_APIC(); + + phys_cpu_present_map = 1; + apic_write_around(APIC_ID, boot_cpu_id); + + apic_pm_init2(); + + setup_local_APIC(); + + if (nmi_watchdog == NMI_LOCAL_APIC) + check_nmi_watchdog(); +#ifdef CONFIG_X86_IO_APIC + if (smp_found_config) + if (!skip_ioapic_setup && nr_ioapics) + setup_IO_APIC(); +#endif + setup_APIC_clocks(); + + return 0; +} diff -Nru a/arch/x86_64/kernel/bluesmoke.c b/arch/x86_64/kernel/bluesmoke.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/bluesmoke.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,174 @@ + +#include +#include +#include +#include +#include +#include + +static int mce_disabled __initdata = 0; + +/* + * Machine Check Handler For PII/PIII/K7 + */ + +static int banks; + +static void intel_machine_check(struct pt_regs * regs, long error_code) +{ + int recover=1; + u32 alow, ahigh, high, low; + u32 mcgstl, mcgsth; + int i; + + rdmsr(MSR_IA32_MCG_STATUS, mcgstl, mcgsth); + if(mcgstl&(1<<0)) /* Recoverable ? */ + recover=0; + + printk(KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x\n", smp_processor_id(), mcgsth, mcgstl); + + for(i=0;ix86_capability) ) + return; + + /* + * Check for PPro style MCA + */ + + if( !test_bit(X86_FEATURE_MCA, &c->x86_capability) ) + return; + + /* Ok machine check is available */ + + machine_check_vector = intel_machine_check; + wmb(); + + if(done==0) + printk(KERN_INFO "Intel machine check architecture supported.\n"); + rdmsr(MSR_IA32_MCG_CAP, l, h); + if(l&(1<<8)) + wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff); + banks = l&0xff; + for(i=1;ix86_vendor) + { + case X86_VENDOR_AMD: + /* + * AMD K7 machine check is Intel like + */ + if(c->x86 == 6) + intel_mcheck_init(c); + break; + case X86_VENDOR_INTEL: + intel_mcheck_init(c); + break; + default: + break; + } +} + +static int __init mcheck_disable(char *str) +{ + mce_disabled = 1; + return 0; +} + +static int __init mcheck_enable(char *str) +{ + mce_disabled = -1; + return 0; +} + +__setup("nomce", mcheck_disable); +__setup("mce", mcheck_enable); diff -Nru a/arch/x86_64/kernel/cpuid.c b/arch/x86_64/kernel/cpuid.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/cpuid.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,177 @@ +#ident "$Id: cpuid.c,v 1.4 2001/10/24 23:58:53 ak Exp $" +/* ----------------------------------------------------------------------- * + * + * Copyright 2000 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + + +/* + * cpuid.c + * + * x86 CPUID access device + * + * This device is accessed by lseek() to the appropriate CPUID level + * and then read in chunks of 16 bytes. A larger size means multiple + * reads of consecutive levels. + * + * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on + * an SMP box will direct the access to CPU %d. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_SMP + +struct cpuid_command { + int cpu; + u32 reg; + u32 *data; +}; + +static void cpuid_smp_cpuid(void *cmd_block) +{ + struct cpuid_command *cmd = (struct cpuid_command *) cmd_block; + + if ( cmd->cpu == smp_processor_id() ) + cpuid(cmd->reg, &cmd->data[0], &cmd->data[1], &cmd->data[2], &cmd->data[3]); +} + +static inline void do_cpuid(int cpu, u32 reg, u32 *data) +{ + struct cpuid_command cmd; + + if ( cpu == smp_processor_id() ) { + cpuid(reg, &data[0], &data[1], &data[2], &data[3]); + } else { + cmd.cpu = cpu; + cmd.reg = reg; + cmd.data = data; + + smp_call_function(cpuid_smp_cpuid, &cmd, 1, 1); + } +} +#else /* ! CONFIG_SMP */ + +static inline void do_cpuid(int cpu, u32 reg, u32 *data) +{ + cpuid(reg, &data[0], &data[1], &data[2], &data[3]); +} + +#endif /* ! CONFIG_SMP */ + +static loff_t cpuid_seek(struct file *file, loff_t offset, int orig) +{ + loff_t ret; + + lock_kernel(); + + switch (orig) { + case 0: + file->f_pos = offset; + ret = file->f_pos; + break; + case 1: + file->f_pos += offset; + ret = file->f_pos; + break; + default: + ret = -EINVAL; + } + + unlock_kernel(); + return ret; +} + +static ssize_t cpuid_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + u32 *tmp = (u32 *)buf; + u32 data[4]; + size_t rv; + u32 reg = *ppos; + int cpu = minor(file->f_dentry->d_inode->i_rdev); + + if ( count % 16 ) + return -EINVAL; /* Invalid chunk size */ + + for ( rv = 0 ; count ; count -= 16 ) { + do_cpuid(cpu, reg, data); + if ( copy_to_user(tmp,&data,16) ) + return -EFAULT; + tmp += 4; + *ppos = reg++; + } + + return ((char *)tmp) - buf; +} + +static int cpuid_open(struct inode *inode, struct file *file) +{ + int cpu = minor(file->f_dentry->d_inode->i_rdev); + struct cpuinfo_x86 *c = &(cpu_data)[cpu]; + + if ( !(cpu_online_map & (1UL << cpu)) ) + return -ENXIO; /* No such CPU */ + if ( c->cpuid_level < 0 ) + return -EIO; /* CPUID not supported */ + + return 0; +} + +/* + * File operations we support + */ +static struct file_operations cpuid_fops = { + owner: THIS_MODULE, + llseek: cpuid_seek, + read: cpuid_read, + open: cpuid_open, +}; + +int __init cpuid_init(void) +{ + if (register_chrdev(CPUID_MAJOR, "cpu/cpuid", &cpuid_fops)) { + printk(KERN_ERR "cpuid: unable to get major %d for cpuid\n", + CPUID_MAJOR); + return -EBUSY; + } + + return 0; +} + +void __exit cpuid_exit(void) +{ + unregister_chrdev(CPUID_MAJOR, "cpu/cpuid"); +} + +module_init(cpuid_init); +module_exit(cpuid_exit) + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("H. Peter Anvin "); +MODULE_DESCRIPTION("x86 generic CPUID driver"); +MODULE_LICENSE("GPL"); diff -Nru a/arch/x86_64/kernel/early_printk.c b/arch/x86_64/kernel/early_printk.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/early_printk.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,77 @@ +#include + +/* This is "wrong" address to access it, we should access it using + 0xffff8000000b8000ul; but 0xffff8000000b8000ul is not available + early at boot. */ +#define VGABASE 0xffffffff800b8000ul + +#define MAX_YPOS 25 +#define MAX_XPOS 80 + +static int current_ypos = 1, current_xpos = 0; /* We want to print before clearing BSS */ + +void +early_clear (void) +{ + int k, i; + for(k = 0; k < MAX_YPOS; k++) + for(i = 0; i < MAX_XPOS; i++) + writew(0, VGABASE + 2*(MAX_XPOS*k + i)); + current_ypos = 0; +} + +void +early_puts (const char *str) +{ + char c; + int i, k, j; + + while ((c = *str++) != '\0') { + if (current_ypos >= MAX_YPOS) { +#if 1 + /* scroll 1 line up */ + for(k = 1, j = 0; k < MAX_YPOS; k++, j++) { + for(i = 0; i < MAX_XPOS; i++) { + writew(readw(VGABASE + 2*(MAX_XPOS*k + i)), + VGABASE + 2*(MAX_XPOS*j + i)); + } + } + for(i = 0; i < MAX_XPOS; i++) { + writew(0x720, VGABASE + 2*(MAX_XPOS*j + i)); + } + current_ypos = MAX_YPOS-1; +#else + /* MUCH faster */ + early_clear(); + current_ypos = 0; +#endif + } + if (c == '\n') { + current_xpos = 0; + current_ypos++; + } else if (c != '\r') { + writew(((0x7 << 8) | (unsigned short) c), + VGABASE + 2*(MAX_XPOS*current_ypos + current_xpos++)); + if (current_xpos >= MAX_XPOS) { + current_xpos = 0; + current_ypos++; + } + } + } +} + +static char buf[1024]; + +int early_printk(const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf)-4 */ + va_end(args); + + early_puts(buf); + + return i; +} diff -Nru a/arch/x86_64/kernel/entry.S b/arch/x86_64/kernel/entry.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/entry.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,640 @@ +/* + * linux/arch/x86_64/entry.S + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2000, 2001, 2002 Andi Kleen SuSE Labs + * Copyright (C) 2000 Pavel Machek + * + * $Id: entry.S,v 1.66 2001/11/11 17:47:47 ak Exp $ + */ + +/* + * entry.S contains the system-call and fault low-level handling routines. + * + * NOTE: This code handles signal-recognition, which happens every time + * after an interrupt and after each system call. + * + * Normal syscalls and interrupts don't save a full stack frame, this is + * only done for syscall tracing, signals or fork/exec et.al. + * + * A note on terminology: + * - top of stack: Architecture defined interrupt frame from SS to RIP + * at the top of the kernel process stack. + * - partial stack frame: partially saved registers upto R11. + * - full stack frame: Like partial stack frame, but all register saved. + * + * TODO: + * - schedule it carefully for the final hardware. + */ + +#define ASSEMBLY 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RIP_SYMBOL_NAME(x) x(%rip) + + .code64 + +#define PDAREF(field) %gs:field + +#ifdef CONFIG_PREEMPT +#define preempt_stop cli +#else +#define preempt_stop +#define retint_kernel retint_restore_args +#endif + +/* + * C code is not supposed to know about undefined top of stack. Every time + * a C function with an pt_regs argument is called from the SYSCALL based + * fast path FIXUP_TOP_OF_STACK is needed. + * RESTORE_TOP_OF_STACK syncs the syscall state after any possible ptregs + * manipulation. + */ + + /* %rsp:at FRAMEEND */ + .macro FIXUP_TOP_OF_STACK tmp + movq PDAREF(pda_oldrsp),\tmp + movq \tmp,RSP(%rsp) + movq $__USER_DS,SS(%rsp) + movq $__USER_CS,CS(%rsp) + movq RCX(%rsp),\tmp /* get return address */ + movq \tmp,RIP(%rsp) + movq R11(%rsp),\tmp /* get eflags */ + movq \tmp,EFLAGS(%rsp) + .endm + + .macro RESTORE_TOP_OF_STACK tmp,offset=0 + movq RSP-\offset(%rsp),\tmp + movq \tmp,PDAREF(pda_oldrsp) + movq RIP-\offset(%rsp),\tmp + movq \tmp,RCX-\offset(%rsp) + movq EFLAGS-\offset(%rsp),\tmp + movq \tmp,R11-\offset(%rsp) + .endm + + .macro FAKE_STACK_FRAME child_rip + /* push in order ss, rsp, eflags, cs, rip */ + xorq %rax, %rax + pushq %rax /* ss */ + pushq %rax /* rsp */ + pushq %rax /* eflags */ + pushq $__KERNEL_CS /* cs */ + pushq \child_rip /* rip */ + pushq %rax /* orig rax */ + .endm + + .macro UNFAKE_STACK_FRAME + addq $8*6, %rsp + .endm + + +/* + * A newly forked process directly context switches into this. + */ +ENTRY(ret_from_fork) + movq %rbx, %rdi + call schedule_tail + GET_THREAD_INFO(%rcx) + bt $TIF_SYSCALL_TRACE,threadinfo_flags(%rcx) + jc rff_trace +rff_action: + RESTORE_REST + cmpq $__KERNEL_CS,CS-ARGOFFSET(%rsp) # from kernel_thread? + je int_ret_from_sys_call + testl $_TIF_IA32,threadinfo_flags(%rcx) + jnz int_ret_from_sys_call + RESTORE_TOP_OF_STACK %rdi,ARGOFFSET + jmp ret_from_sys_call +rff_trace: + movq %rsp,%rdi + call syscall_trace + jmp rff_action + +/* + * System call entry. Upto 6 arguments in registers are supported. + * + * SYSCALL does not save anything on the stack and does not change the + * stack pointer. + */ + +/* + * Register setup: + * rax system call number + * rdi arg0 + * rcx return address for syscall/sysret, C arg3 + * rsi arg1 + * rdx arg2 + * r10 arg4 (--> moved to rcx for C, serves as TOS flag afterwards) + * r8 arg5 + * r9 arg6 + * r11 eflags for syscall/sysret, temporary for C + * r12-r15,rbp,rbx saved by C code, not touched. + * + * Interrupts are off on entry. + * Only called from user space. + * + * XXX need to add a flag for thread_saved_pc/KSTK_*. + */ + +ENTRY(system_call) + swapgs + movq %rsp,PDAREF(pda_oldrsp) + movq PDAREF(pda_kernelstack),%rsp + pushq %rax + sti + SAVE_ARGS + GET_THREAD_INFO(%rcx) + bt $TIF_SYSCALL_TRACE,threadinfo_flags(%rcx) + jc tracesys + cmpq $__NR_syscall_max,%rax + ja badsys + movq %r10,%rcx + call *sys_call_table(,%rax,8) # XXX: rip relative + movq %rax,RAX-ARGOFFSET(%rsp) +/* + * Syscall return path ending with SYSRET (fast path) + * Has incomplete stack frame and undefined top of stack. + */ +ENTRY(ret_from_sys_call) + GET_THREAD_INFO(%rcx) + cli + movl threadinfo_flags(%rcx),%edx + andl $_TIF_WORK_MASK,%edx # tracesys has been already checked. + jnz sysret_careful +sysret_restore_args: + RESTORE_ARGS + movq PDAREF(pda_oldrsp),%rsp + swapgs + SYSRET64 + +sysret_careful: + bt $TIF_NEED_RESCHED,%edx + jnc 1f + call schedule + jmp ret_from_sys_call +1: sti + SAVE_REST + FIXUP_TOP_OF_STACK %rax + xorq %rsi,%rsi # oldset + movq %rsp,%rdi # &ptregs + call do_notify_resume + RESTORE_TOP_OF_STACK %rax + RESTORE_REST + jmp ret_from_sys_call + +tracesys: + SAVE_REST + movq $-ENOSYS,RAX(%rsp) + FIXUP_TOP_OF_STACK %rdi + movq %rsp,%rdi + call syscall_trace + LOAD_ARGS ARGOFFSET /* reload args from stack in case ptrace changed it */ + RESTORE_REST + cmpq $__NR_syscall_max,%rax + ja 1f + movq %r10,%rcx /* fixup for C */ + movl $1,%r10d /* set TOS flag */ + call *sys_call_table(,%rax,8) + movq %rax,RAX-ARGOFFSET(%rsp) + SAVE_REST +1: movq %rsp,%rdi + call syscall_trace + RESTORE_TOP_OF_STACK %rbx + RESTORE_REST + jmp ret_from_sys_call + +badsys: + movq $-ENOSYS,RAX-ARGOFFSET(%rsp) + jmp ret_from_sys_call + +/* + * Syscall return path ending with IRET. + * Has correct top of stack, but partial stack frame. + */ +ENTRY(int_ret_from_sys_call) + cmpq $__KERNEL_CS,CS-ARGOFFSET(%rsp) # in kernel syscall? + je int_restore_args + movl $_TIF_ALLWORK_MASK,%esi +int_with_reschedule: + GET_THREAD_INFO(%rcx) + cli + movl threadinfo_flags(%rcx),%edx + andl %esi,%edx + jnz int_careful + swapgs +int_restore_args: + RESTORE_ARGS + addq $8,%rsp # Remove oldrax + iretq + +int_careful: + sti + bt $TIF_NEED_RESCHED,%edx + jnc int_very_careful + call schedule + movl $_TIF_ALLWORK_MASK,%esi + jmp int_with_reschedule +int_very_careful: + SAVE_REST + leaq syscall_trace(%rip),%rbp + leaq do_notify_resume(%rip),%rbx + bt $TIF_SYSCALL_TRACE,%edx + cmovcq %rbp,%rbx + xorq %rsi,%rsi # oldset -> arg2 + movq %rsp,%rdi # &ptregs -> arg1 + call *%rbx + RESTORE_REST + movl $_TIF_WORK_MASK,%esi + jmp int_with_reschedule + +/* + * Certain special system calls that need to save a complete full stack frame. + */ + + .macro PTREGSCALL label,func + .globl \label +\label: + leaq \func(%rip),%rax + jmp ptregscall_common + .endm + + PTREGSCALL stub_clone, sys_clone + PTREGSCALL stub_fork, sys_fork + PTREGSCALL stub_vfork, sys_vfork + PTREGSCALL stub_rt_sigsuspend, sys_rt_sigsuspend + PTREGSCALL stub_sigaltstack, sys_sigaltstack + PTREGSCALL stub_iopl, sys_iopl + +ENTRY(ptregscall_common) + popq %r11 + SAVE_REST + movq %r11, %r15 + FIXUP_TOP_OF_STACK %r11 + call *%rax + RESTORE_TOP_OF_STACK %r11 + movq %r15, %r11 + RESTORE_REST + pushq %r11 + ret + +ENTRY(stub_execve) + popq %r11 + SAVE_REST + movq %r11, %r15 + FIXUP_TOP_OF_STACK %r11 + call sys_execve + GET_THREAD_INFO(%rcx) + testl $_TIF_IA32,threadinfo_flags(%rcx) + jnz exec_32bit + RESTORE_TOP_OF_STACK %r11 + movq %r15, %r11 + RESTORE_REST + push %r11 + ret + +exec_32bit: + movq %rax,RAX(%rsp) + RESTORE_REST + jmp int_ret_from_sys_call + +/* + * sigreturn is special because it needs to restore all registers on return. + * This cannot be done with SYSRET, so use the IRET return path instead. + */ +ENTRY(stub_rt_sigreturn) + addq $8, %rsp + SAVE_REST + FIXUP_TOP_OF_STACK %r11 + call sys_rt_sigreturn + movq %rax,RAX(%rsp) # fixme, this could be done at the higher layer + RESTORE_REST + jmp int_ret_from_sys_call + +/* + * Interrupt entry/exit. + * + * Interrupt entry points save only callee clobbered registers in fast path. + * + * Entry runs with interrupts off. + */ + +/* 0(%rsp): interrupt number */ +ENTRY(common_interrupt) + cmpq $__KERNEL_CS,16(%rsp) + je 1f + swapgs +1: cld + SAVE_ARGS +#ifdef CONFIG_PREEMPT + GET_THREAD_INFO(%rdx) + incl threadinfo_preempt_count(%rdx) +#endif + leaq -ARGOFFSET(%rsp),%rdi # arg1 for handler + addl $1,PDAREF(pda_irqcount) # XXX: should be merged with irq.c irqcount + movq PDAREF(pda_irqstackptr),%rax + cmoveq %rax,%rsp + pushq %rdi # save old stack + call do_IRQ + /* 0(%rsp): oldrsp-ARGOFFSET */ + .globl ret_from_intr +ret_from_intr: + popq %rdi + cli + subl $1,PDAREF(pda_irqcount) + leaq ARGOFFSET(%rdi),%rsp +exit_intr: + GET_THREAD_INFO(%rcx) +#ifdef CONFIG_PREEMPT + decl threadinfo_preempt_count(%rcx) +#endif + cmpq $__KERNEL_CS,CS-ARGOFFSET(%rsp) + je retint_kernel + + /* Interrupt came from user space */ + /* + * Shared return path for exceptions and interrupts that came from user space. + * Has a correct top of stack, but a partial stack frame + * %rcx: thread info. Interrupts off. + */ +retint_with_reschedule: + testl $_TIF_WORK_MASK,threadinfo_flags(%rcx) + jnz retint_careful +retint_swapgs: + swapgs +retint_restore_args: + RESTORE_ARGS + addq $8,%rsp + iretq + +retint_careful: + movl threadinfo_flags(%rcx),%edx + bt $TIF_NEED_RESCHED,%edx + jnc retint_signal + sti + call schedule +retint_next_try: + GET_THREAD_INFO(%rcx) + cli + jmp retint_with_reschedule +retint_signal: + testl $(_TIF_SIGPENDING|_TIF_NOTIFY_RESUME),%edx + jz retint_swapgs + sti + SAVE_REST + movq $-1,ORIG_RAX(%rsp) + xorq %rsi,%rsi # oldset + movq %rsp,%rdi # &pt_regs + call do_notify_resume + RESTORE_REST + jmp retint_next_try + +#ifdef CONFIG_PREEMPT + /* Returning to kernel space. Check if we need preemption */ + /* rcx: threadinfo. interrupts off. */ + .p2align +retint_kernel: + cmpl $0,threadinfo_preempt_count(%rcx) + jnz retint_restore_args + bt $TIF_NEED_RESCHED,threadinfo_flags(%rcx) + jnc retint_restore_args + movl PDAREF(pda___local_bh_count),%eax + addl PDAREF(pda___local_irq_count),%eax + jnz retint_restore_args + incl threadinfo_preempt_count(%rcx) + sti + call preempt_schedule + cli + jmp exit_intr +#endif + +/* + * Exception entry points. + */ + .macro zeroentry sym + pushq $0 /* push error code/oldrax */ + pushq %rax /* push real oldrax to the rdi slot */ + leaq RIP_SYMBOL_NAME(\sym),%rax + jmp error_entry + .endm + + .macro errorentry sym + pushq %rax + leaq RIP_SYMBOL_NAME(\sym),%rax + jmp error_entry + .endm + +/* + * Exception entry point. This expects an error code/orig_rax on the stack + * and the exception handler in %rax. + */ + ALIGN +error_entry: + cmpq $__KERNEL_CS,24(%rsp) + je error_kernelspace + swapgs +error_kernelspace: + sti + /* rdi slot contains rax, oldrax contains error code */ + pushq %rsi + movq 8(%rsp),%rsi /* load rax */ + pushq %rdx + pushq %rcx + pushq %rsi /* store rax */ + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + cld + SAVE_REST + movq %rdi,RDI(%rsp) + movq %rsp,%rdi + movq ORIG_RAX(%rsp),%rsi /* get error code */ + movq $-1,ORIG_RAX(%rsp) + call *%rax +error_exit: + RESTORE_REST + cli + GET_THREAD_INFO(%rcx) + cmpq $__KERNEL_CS,CS-ARGOFFSET(%rsp) + je retint_kernel + jmp retint_with_reschedule + +/* + * Create a kernel thread. + * + * C extern interface: + * extern long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) + * + * asm input arguments: + * rdi: fn, rsi: arg, rdx: flags + */ +ENTRY(kernel_thread) + FAKE_STACK_FRAME $child_rip + SAVE_ALL + + # rdi: flags, rsi: usp, rdx: will be &pt_regs + movq %rdx,%rdi + orq $CLONE_VM, %rdi + + movq $-1, %rsi + + movq %rsp, %rdx + + # clone now + call do_fork + # save retval on the stack so it's popped before `ret` + movq %rax, RAX(%rsp) + + /* + * It isn't worth to check for reschedule here, + * so internally to the x86_64 port you can rely on kernel_thread() + * not to reschedule the child before returning, this avoids the need + * of hacks for example to fork off the per-CPU idle tasks. + * [Hopefully no generic code relies on the reschedule -AK] + */ + RESTORE_ALL + UNFAKE_STACK_FRAME + ret + +child_rip: + /* + * Here we are in the child and the registers are set as they were + * at kernel_thread() invocation in the parent. + */ + movq %rdi, %rax + movq %rsi, %rdi + call *%rax + # exit + xorq %rdi, %rdi + call do_exit + +/* + * execve(). This function needs to use IRET, not SYSRET, to set up all state properly. + * + * C extern interface: + * extern long execve(char *name, char **argv, char **envp) + * + * asm input arguments: + * rdi: name, rsi: argv, rdx: envp + * + * We want to fallback into: + * extern long sys_execve(char *name, char **argv,char **envp, struct pt_regs regs) + * + * do_sys_execve asm fallback arguments: + * rdi: name, rsi: argv, rdx: envp, fake frame on the stack + */ +ENTRY(execve) + FAKE_STACK_FRAME $0 + SAVE_ALL + call sys_execve + movq %rax, RAX(%rsp) + RESTORE_REST + testq %rax,%rax + je int_ret_from_sys_call + RESTORE_ARGS + UNFAKE_STACK_FRAME + ret + +ENTRY(page_fault) + errorentry do_page_fault + +ENTRY(coprocessor_error) + zeroentry do_coprocessor_error + +ENTRY(simd_coprocessor_error) + zeroentry do_simd_coprocessor_error + + +ENTRY(device_not_available) + cmpq $0,(%rsp) + jl 1f + swapgs +1: pushq $-1 + SAVE_ALL + movq %cr0,%rax + leaq math_state_restore(%rip),%rcx + leaq math_emulate(%rip),%rbx + testl $0x4,%eax + cmoveq %rcx,%rbx + preempt_stop + call *%rbx + jmp error_exit + +ENTRY(debug) + zeroentry do_debug + + /* XXX checkme */ +ENTRY(nmi) + cmpq $0,(%rsp) + jl 1f + swapgs +1: pushq $-1 + SAVE_ALL + movq %rsp,%rdi + call do_nmi + RESTORE_ALL + addq $8,%rsp + cmpq $0,(%rsp) + jl 2f + swapgs +2: iretq + +ENTRY(int3) + zeroentry do_int3 + +ENTRY(overflow) + zeroentry do_overflow + +ENTRY(bounds) + zeroentry do_bounds + +ENTRY(invalid_op) + zeroentry do_invalid_op + +ENTRY(coprocessor_segment_overrun) + zeroentry do_coprocessor_segment_overrun + +ENTRY(reserved) + zeroentry do_reserved + +ENTRY(double_fault) + errorentry do_double_fault + +ENTRY(invalid_TSS) + errorentry do_invalid_TSS + +ENTRY(segment_not_present) + errorentry do_segment_not_present + +ENTRY(stack_segment) + errorentry do_stack_segment + +ENTRY(general_protection) + errorentry do_general_protection + +ENTRY(alignment_check) + errorentry do_alignment_check + +ENTRY(divide_error) + errorentry do_divide_error + +ENTRY(spurious_interrupt_bug) + zeroentry do_spurious_interrupt_bug + +ENTRY(__bad_intr) + pushq $-1 + SAVE_ALL + call bad_intr + RESTORE_ALL + addq $8,%rsp + iretq diff -Nru a/arch/x86_64/kernel/head.S b/arch/x86_64/kernel/head.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/head.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,342 @@ +/* + * linux/arch/x86_64/kernel/head.S -- start in 32bit and switch to 64bit + * + * Copyright (C) 2000 Andrea Arcangeli SuSE + * Copyright (C) 2000 Pavel Machek + * Copyright (C) 2000 Karsten Keil + * Copyright (C) 2001 2002 Andi Kleen + * + * $Id: head.S,v 1.41 2001/07/05 23:43:45 ak Exp $ + */ +.code64 +.text + +#include +#include +#include +#include +#include +#include + +/* we don't able to switch in one step to final KERNEL ADDRESS SPACE + * because we need identity-mapped pages on setup so define __START_KERNEL to + * 0x100000 for this stage + * + */ + + +startup_32: +.code32 + /* + * At this point the CPU runs in 32bit protected mode (CS.D = 1) with + * paging disabled and the point of this file is to switch to 64bit + * long mode with a kernel mapping for kerneland to jump into the + * kernel virtual addresses. + * There is no stack until we set one up. + */ + + /* As first check if extended functions are implemented */ + movl $0x80000000, %eax + cpuid + cmpl $0x80000000, %eax + jbe no_long_mode + /* Check if long mode is implemented */ + mov $0x80000001, %eax + cpuid + btl $29, %edx + jnc no_long_mode + + /* + * Prepare for entering 64bits mode + */ + + /* Enable PAE mode and PGE */ + xorl %eax, %eax + btsl $5, %eax + btsl $7, %eax + movl %eax, %cr4 + + /* Setup early boot stage 4 level pagetables */ + movl $0x101000, %eax + movl %eax, %cr3 + + /* Setup EFER (Extended Feature Enable Register) */ + movl $0xc0000080, %ecx + rdmsr + /* Fool rdmsr and reset %eax to avoid dependences */ + xorl %eax, %eax + /* Enable Long Mode */ + btsl $8, %eax + /* Enable System Call */ + btsl $0, %eax + /* Make changes effective */ + wrmsr + + xorl %eax, %eax + /* Enable paging and in turn activate Long Mode */ + btsl $31, %eax + /* Enable protected mode */ + btsl $0, %eax + /* Enable MP */ + btsl $1, %eax + /* Enable ET */ + btsl $4, %eax + /* Enable NE */ + btsl $5, %eax + /* Enable WP */ + btsl $16, %eax + /* Enable AM */ + btsl $18, %eax + /* Make changes effective */ + movl %eax, %cr0 + jmp reach_compatibility_mode +reach_compatibility_mode: + + /* + * At this point we're in long mode but in 32bit compatibility mode + * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn + * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we load + * the new gdt/idt that has __KERNEL_CS with CS.L = 1. + */ + + /* Load new GDT with the 64bit segment using 32bit descriptor */ + /* to avoid 32bit relocations we use fixed adresses here */ + movl $0x100F00, %eax + lgdt (%eax) + movl $0x100F10, %eax + /* Finally jump in 64bit mode */ + ljmp *(%eax) + +.code64 +reach_long64: + /* + * Where we're running at 0x0000000000100000, and yes, finally + * in 64bit mode. + */ + .globl init_rsp + + /* Setup the first kernel stack (this instruction is modified by smpboot) */ + .byte 0x48, 0xb8 /* movq *init_rsp,%rax */ +init_rsp: + .quad init_thread_union+THREAD_SIZE + movq %rax, %rsp + + /* zero EFLAGS after setting rsp */ + pushq $0 + popfq + + /* + * We must switch to a new descriptor in kernel space for the GDT + * because soon the kernel won't have access anymore to the userspace + * addresses where we're currently running on. We have to do that here + * because in 32bit we couldn't load a 64bit linear address. + */ + lgdt pGDT64 + + /* esi is pointer to real mode structure with interesting info. + pass it to C */ + movl %esi, %edi + + movl $__KERNEL_DS,%eax + movl %eax,%ss + movl %eax,%ds + movl %eax,%es + + /* Finally jump to run C code and to be on real kernel address + * Since we are running on identity-mapped space we have to jump + * to the full 64bit address , this is only possible as indirect + * jump + */ + movq initial_code(%rip),%rax + jmp *%rax + + + /* SMP bootup changes this */ + .globl initial_code +initial_code: + .quad x86_64_start_kernel + +.code32 +ENTRY(no_long_mode) + /* This isn't an x86-64 CPU so hang */ +1: + jmp 1b + +.org 0xf00 +pGDT32: + .word gdt32_end-gdt_table32 + .quad gdt_table32-__START_KERNEL+0x100000 + +.org 0xf10 +ljumpvector: + .long reach_long64-__START_KERNEL+0x100000 + .word __KERNEL_CS + +ENTRY(stext) +ENTRY(_stext) + + /* + * This default setting generates an ident mapping at address 0x100000 + * and a mapping for the kernel that precisely maps virtual address + * 0xffffffff80000000 to physical address 0x000000. (always using + * 2Mbyte large pages provided by PAE mode) + */ +.org 0x1000 +ENTRY(level4_pgt) + .quad 0x0000000000102007 /* -> level3_ident_pgt */ + .fill 255,8,0 + /* __PAGE_OFFSET 0xffff800000000000 */ + .quad 0x000000000010a007 + .fill 254,8,0 + /* (2^48-(2*1024*1024*1024))/(2^39) = 511 */ + .quad 0x0000000000103007 /* -> level3_kernel_pgt */ + +.org 0x2000 +/* Kernel does not "know" about 4-th level of page tables. */ +ENTRY(swapper_pg_dir) +ENTRY(level3_ident_pgt) + .quad 0x0000000000104007 + .fill 511,8,0 + +.org 0x3000 +ENTRY(level3_kernel_pgt) + .fill 510,8,0 + /* (2^48-(2*1024*1024*1024)-((2^39)*511))/(2^30) = 510 */ + .quad 0x0000000000105007 /* -> level2_kernel_pgt */ + .fill 1,8,0 + +.org 0x4000 +ENTRY(level2_ident_pgt) + /* 2 Mbytes are enough, this is necessary only for head.S */ + .quad 0x0000000000000283 + /* .fill 511,8,0 */ + /* Jan needs more than 2Mbytes, so set a 40Mbyte mapping instead */ + .quad 0x0000000000200183 + .quad 0x0000000000400183 + .quad 0x0000000000600183 + .quad 0x0000000000800183 + .quad 0x0000000000A00183 + .quad 0x0000000000C00183 + .quad 0x0000000000E00183 + .quad 0x0000000001000183 + .quad 0x0000000001200183 + .quad 0x0000000001400183 + .quad 0x0000000001600183 + .quad 0x0000000001800183 + .quad 0x0000000001A00183 + .quad 0x0000000001C00183 + .quad 0x0000000001E00183 + .quad 0x0000000002000183 + .quad 0x0000000002200183 + .quad 0x0000000002400183 + .quad 0x0000000002600183 + .fill 492,8,0 + +.org 0x5000 +ENTRY(level2_kernel_pgt) + /* (2^48-(2*1024*1024*1024)-((2^39)*511)-((2^30)*510)) = 0 */ + .quad 0x0000000000000183 + .quad 0x0000000000200183 + .quad 0x0000000000400183 + .quad 0x0000000000600183 + .quad 0x0000000000800183 + .quad 0x0000000000A00183 + .quad 0x0000000000C00183 + .quad 0x0000000000E00183 + .quad 0x0000000001000183 + .quad 0x0000000001200183 + .quad 0x0000000001400183 + .quad 0x0000000001600183 + .quad 0x0000000001800183 + .quad 0x0000000001A00183 + .quad 0x0000000001C00183 + .quad 0x0000000001E00183 + .quad 0x0000000002000183 + .quad 0x0000000002200183 + .quad 0x0000000002400183 + .quad 0x0000000002600183 + /* + * We could go ahead without any downside (except typing programmer + * wise :) but 40Mbyte are just enough for the kernel statically + * linked part (and extending it is trivial). + */ + .fill 492,8,0 + +.org 0x6000 +ENTRY(empty_zero_page) + +.org 0x7000 +ENTRY(empty_bad_page) + +.org 0x8000 +ENTRY(empty_bad_pte_table) + +.org 0x9000 +ENTRY(empty_bad_pmd_table) + +.org 0xa000 +ENTRY(level3_physmem_pgt) + .quad 0x0000000000105007 /* -> level2_kernel_pgt (so that __va works even before pagetable_init) */ + + + +.org 0xb000 + + +.data + +.globl SYMBOL_NAME(gdt) + + .word 0 + .align 16 + .word 0 +pGDT64: + .word gdt_end-gdt_table +SYMBOL_NAME_LABEL(gdt) + .quad gdt_table + + +.align 64 /* cacheline aligned */ +ENTRY(gdt_table32) + .quad 0x0000000000000000 /* This one is magic */ + .quad 0x0000000000000000 /* unused */ + .quad 0x00af9a000000ffff /* __KERNEL_CS */ +gdt32_end: + +/* We need valid kernel segments for data and code in long mode too + * IRET will check the segment types kkeil 2000/10/28 + * Also sysret mandates a special GDT layout + */ + +.align 64 /* cacheline aligned, keep this synchronized with asm/desc.h */ +ENTRY(gdt_table) + .quad 0x0000000000000000 /* This one is magic */ + .quad 0x0000000000000000 /* unused */ + .quad 0x00af9a000000ffff /* __KERNEL_CS */ + .quad 0x00cf92000000ffff /* __KERNEL_DS */ + .quad 0x00cffe000000ffff /* __USER32_CS */ + .quad 0x00cff2000000ffff /* __USER_DS, __USER32_DS */ + .quad 0x00affa000000ffff /* __USER_CS */ + + .globl tss_start +tss_start: + .rept NR_CPUS + .quad 0,0 /* TSS descriptors. filled in later */ + .endr + .globl ldt_start +ldt_start: + .rept NR_CPUS + .quad 0,0 /* LDT descriptors. filled in later */ + .endr +gdt_end: + .globl gdt_end + + .align 64 +ENTRY(idt_table) + .rept 256 + .quad 0 + .quad 0 + .endr + +.section .text.lock +ENTRY(stext_lock) diff -Nru a/arch/x86_64/kernel/head64.c b/arch/x86_64/kernel/head64.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/head64.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,85 @@ +/* + * linux/arch/x86_64/kernel/head64.c -- prepare to run common code + * + * Copyright (C) 2000 Andrea Arcangeli SuSE + * + * $Id: head64.c,v 1.22 2001/07/06 14:28:20 ak Exp $ + */ + +#include +#include +#include +#include +#include + +#include + +/* Don't add a printk in there. printk relies on the PDA which is not initialized + yet. */ +static void __init clear_bss(void) +{ + extern char __bss_start[], __bss_end[]; + memset(__bss_start, 0, + (unsigned long) __bss_end - (unsigned long) __bss_start); +} + +extern char x86_boot_params[2048]; + +#define NEW_CL_POINTER 0x228 /* Relative to real mode data */ +#define OLD_CL_MAGIC_ADDR 0x90020 +#define OLD_CL_MAGIC 0xA33F +#define OLD_CL_BASE_ADDR 0x90000 +#define OLD_CL_OFFSET 0x90022 + +extern char saved_command_line[]; + +static void __init copy_bootdata(char *real_mode_data) +{ + int new_data; + char * command_line; + + memcpy(x86_boot_params, real_mode_data, 2048); + new_data = *(int *) (x86_boot_params + NEW_CL_POINTER); + if (!new_data) { + if (OLD_CL_MAGIC != * (u16 *) OLD_CL_MAGIC_ADDR) { + printk("so old bootloader that it does not support commandline?!\n"); + return; + } + new_data = OLD_CL_BASE_ADDR + * (u16 *) OLD_CL_OFFSET; + printk("old bootloader convention, maybe loadlin?\n"); + } + command_line = (char *) ((u64)(new_data)); + memcpy(saved_command_line, command_line, 2048); + printk("Bootdata ok (command line is %s)\n", saved_command_line); +} + +static void __init setup_boot_cpu_data(void) +{ + int dummy, eax; + + /* get vendor info */ + cpuid(0, &boot_cpu_data.cpuid_level, + (int *)&boot_cpu_data.x86_vendor_id[0], + (int *)&boot_cpu_data.x86_vendor_id[8], + (int *)&boot_cpu_data.x86_vendor_id[4]); + + /* get cpu type */ + cpuid(1, &eax, &dummy, &dummy, &boot_cpu_data.x86_capability[0]); + boot_cpu_data.x86 = (eax >> 8) & 0xf; + boot_cpu_data.x86_model = (eax >> 4) & 0xf; + boot_cpu_data.x86_mask = eax & 0xf; +} + +extern void start_kernel(void), pda_init(int); + +void __init x86_64_start_kernel(char * real_mode_data) +{ + clear_bss(); + pda_init(0); + + copy_bootdata(real_mode_data); + setup_boot_cpu_data(); + + + start_kernel(); +} diff -Nru a/arch/x86_64/kernel/i387.c b/arch/x86_64/kernel/i387.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/i387.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,439 @@ +/* + * linux/arch/x86_64/kernel/i387.c + * + * Copyright (C) 1994 Linus Torvalds + * + * Pentium III FXSR, SSE support + * General FPU state handling cleanups + * Gareth Hughes , May 2000 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HAVE_HWFP 1 + +/* + * The _current_ task is using the FPU for the first time + * so initialize it and set the mxcsr to its default + * value at reset if we support XMM instructions and then + * remeber the current task has used the FPU. + */ +void init_fpu(void) +{ + __asm__("fninit"); + if ( cpu_has_xmm ) + load_mxcsr(0x1f80); + + current->used_math = 1; +} + +/* + * FPU lazy state save handling. + */ + +static inline void __save_init_fpu( struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + asm volatile( "fxsave %0 ; fnclex" + : "=m" (tsk->thread.i387.fxsave) ); + } else { + asm volatile( "fnsave %0 ; fwait" + : "=m" (tsk->thread.i387.fsave) ); + } + clear_tsk_thread_flag(tsk, TIF_USEDFPU); +} + +void save_init_fpu( struct task_struct *tsk ) +{ + __save_init_fpu(tsk); + stts(); +} + +void kernel_fpu_begin(void) +{ + preempt_disable(); + if (test_thread_flag(TIF_USEDFPU)) { + __save_init_fpu(current); + return; + } + clts(); +} + +void restore_fpu( struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + asm volatile( "fxrstor %0" + : : "m" (tsk->thread.i387.fxsave) ); + } else { + asm volatile( "frstor %0" + : : "m" (tsk->thread.i387.fsave) ); + } +} + +/* + * FPU tag word conversions. + */ + +static inline unsigned short twd_i387_to_fxsr( unsigned short twd ) +{ + unsigned int tmp; /* to avoid 16 bit prefixes in the code */ + + /* Transform each pair of bits into 01 (valid) or 00 (empty) */ + tmp = ~twd; + tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ + /* and move the valid bits to the lower byte. */ + tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ + tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ + tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ + return tmp; +} + +static inline u32 twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave ) +{ + struct _fpxreg *st = NULL; + u32 twd = (u32) fxsave->twd; + u32 tag; + u32 ret = 0xffff0000; + int i; + +#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16); + + for ( i = 0 ; i < 8 ; i++ ) { + if ( twd & 0x1 ) { + st = (struct _fpxreg *) FPREG_ADDR( fxsave, i ); + + switch ( st->exponent & 0x7fff ) { + case 0x7fff: + tag = 2; /* Special */ + break; + case 0x0000: + if ( !st->significand[0] && + !st->significand[1] && + !st->significand[2] && + !st->significand[3] ) { + tag = 1; /* Zero */ + } else { + tag = 2; /* Special */ + } + break; + default: + if ( st->significand[3] & 0x8000 ) { + tag = 0; /* Valid */ + } else { + tag = 2; /* Special */ + } + break; + } + } else { + tag = 3; /* Empty */ + } + ret |= (tag << (2 * i)); + twd = twd >> 1; + } + return ret; +} + +/* + * FPU state interaction. + */ + +unsigned short get_fpu_cwd( struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + return tsk->thread.i387.fxsave.cwd; + } else { + return (unsigned short)tsk->thread.i387.fsave.cwd; + } +} + +unsigned short get_fpu_swd( struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + return tsk->thread.i387.fxsave.swd; + } else { + return (unsigned short)tsk->thread.i387.fsave.swd; + } +} + +unsigned short get_fpu_twd( struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + return tsk->thread.i387.fxsave.twd; + } else { + return (unsigned short)tsk->thread.i387.fsave.twd; + } +} + +unsigned short get_fpu_mxcsr( struct task_struct *tsk ) +{ + if ( cpu_has_xmm ) { + return tsk->thread.i387.fxsave.mxcsr; + } else { + return 0x1f80; + } +} + +void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd ) +{ + if ( cpu_has_fxsr ) { + tsk->thread.i387.fxsave.cwd = cwd; + } else { + tsk->thread.i387.fsave.cwd = ((u32)cwd | 0xffff0000); + } +} + +void set_fpu_swd( struct task_struct *tsk, unsigned short swd ) +{ + if ( cpu_has_fxsr ) { + tsk->thread.i387.fxsave.swd = swd; + } else { + tsk->thread.i387.fsave.swd = ((u32)swd | 0xffff0000); + } +} + +void set_fpu_twd( struct task_struct *tsk, unsigned short twd ) +{ + if ( cpu_has_fxsr ) { + tsk->thread.i387.fxsave.twd = twd_i387_to_fxsr(twd); + } else { + tsk->thread.i387.fsave.twd = ((u32)twd | 0xffff0000); + } +} + +void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr ) +{ + if ( cpu_has_xmm ) { + tsk->thread.i387.fxsave.mxcsr = (mxcsr & 0xffbf); + } +} + +/* + * FXSR floating point environment conversions. + */ + +static inline int convert_fxsr_to_user( struct _fpstate *buf, + struct i387_fxsave_struct *fxsave ) +{ + u32 env[7]; + struct _fpreg *to; + struct _fpxreg *from; + int i; + + env[0] = (u32)fxsave->cwd | 0xffff0000; + env[1] = (u32)fxsave->swd | 0xffff0000; + env[2] = twd_fxsr_to_i387(fxsave); + env[3] = fxsave->fip; + env[4] = fxsave->fcs | ((u32)fxsave->fop << 16); + env[5] = fxsave->foo; + env[6] = fxsave->fos; + + if ( __copy_to_user( buf, env, 7 * sizeof(u32) ) ) + return 1; + + to = &buf->_st[0]; + from = (struct _fpxreg *) &fxsave->st_space[0]; + for ( i = 0 ; i < 8 ; i++, to++, from++ ) { + if ( __copy_to_user( to, from, sizeof(*to) ) ) + return 1; + } + return 0; +} + +static inline int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave, + struct _fpstate *buf ) +{ + u32 env[7]; + struct _fpxreg *to; + struct _fpreg *from; + int i; + + if ( __copy_from_user( env, buf, 7 * sizeof(u32) ) ) + return 1; + + fxsave->cwd = (unsigned short)(env[0] & 0xffff); + fxsave->swd = (unsigned short)(env[1] & 0xffff); + fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff)); + fxsave->fip = env[3]; + fxsave->fop = (unsigned short)((env[4] & 0xffff0000) >> 16); + fxsave->fcs = (env[4] & 0xffff); + fxsave->foo = env[5]; + fxsave->fos = env[6]; + + to = (struct _fpxreg *) &fxsave->st_space[0]; + from = &buf->_st[0]; + for ( i = 0 ; i < 8 ; i++, to++, from++ ) { + if ( __copy_from_user( to, from, sizeof(*from) ) ) + return 1; + } + return 0; +} + +/* + * Signal frame handlers. + */ + +static inline int save_i387_fsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + + unlazy_fpu( tsk ); + tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd; + if ( __copy_to_user( buf, &tsk->thread.i387.fsave, + sizeof(struct i387_fsave_struct) ) ) + return -1; + return 1; +} + +static inline int save_i387_fxsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + int err = 0; + + unlazy_fpu( tsk ); + + if ( convert_fxsr_to_user( buf, &tsk->thread.i387.fxsave ) ) + return -1; + + err |= __put_user( tsk->thread.i387.fxsave.swd, &buf->status ); + err |= __put_user( X86_FXSR_MAGIC, &buf->magic ); + if ( err ) + return -1; + + if ( __copy_to_user( &buf->_fxsr_env[0], &tsk->thread.i387.fxsave, + sizeof(struct i387_fxsave_struct) ) ) + return -1; + return 1; +} + +int save_i387( struct _fpstate *buf ) +{ + if ( !current->used_math ) + return 0; + + /* This will cause a "finit" to be triggered by the next + * attempted FPU operation by the 'current' process. + */ + current->used_math = 0; + + if ( HAVE_HWFP ) { + if ( cpu_has_fxsr ) { + return save_i387_fxsave( buf ); + } else { + return save_i387_fsave( buf ); + } + } +} + +static inline int restore_i387_fsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + clear_fpu( tsk ); + return __copy_from_user( &tsk->thread.i387.fsave, buf, + sizeof(struct i387_fsave_struct) ); +} + +static inline int restore_i387_fxsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + clear_fpu( tsk ); + if ( __copy_from_user( &tsk->thread.i387.fxsave, &buf->_fxsr_env[0], + sizeof(struct i387_fxsave_struct) ) ) + return 1; + /* mxcsr bit 6 and 31-16 must be zero for security reasons */ + tsk->thread.i387.fxsave.mxcsr &= 0xffbf; + return convert_fxsr_from_user( &tsk->thread.i387.fxsave, buf ); +} + +int restore_i387( struct _fpstate *buf ) +{ + int err; + + if ( HAVE_HWFP ) { + if ( cpu_has_fxsr ) { + err = restore_i387_fxsave( buf ); + } else { + err = restore_i387_fsave( buf ); + } + } + current->used_math = 1; + return err; +} + +/* + * ptrace request handlers. + */ + +int get_fpregs( struct user_i387_struct *buf, struct task_struct *tsk ) +{ + if ( cpu_has_fxsr ) { + if (__copy_to_user( (void *)buf, &tsk->thread.i387.fxsave, + sizeof(struct user_i387_struct) )) + return -EFAULT; + return 0; + } else { + return -EIO; + } +} + +int set_fpregs( struct task_struct *tsk, struct user_i387_struct *buf ) +{ + if ( cpu_has_fxsr ) { + __copy_from_user( &tsk->thread.i387.fxsave, (void *)buf, + sizeof(struct user_i387_struct) ); + /* mxcsr bit 6 and 31-16 must be zero for security reasons */ + tsk->thread.i387.fxsave.mxcsr &= 0xffbf; + return 0; + } else { + return -EIO; + } +} + +/* + * FPU state for core dumps. + */ + +static inline void copy_fpu_fsave( struct task_struct *tsk, + struct user_i387_struct *fpu ) +{ + memcpy( fpu, &tsk->thread.i387.fsave, + sizeof(struct user_i387_struct) ); +} + +static inline void copy_fpu_fxsave( struct task_struct *tsk, + struct user_i387_struct *fpu ) +{ + unsigned short *to; + unsigned short *from; + int i; + + memcpy( fpu, &tsk->thread.i387.fxsave, 7 * sizeof(u32) ); + + to = (unsigned short *)&fpu->st_space[0]; + from = (unsigned short *)&tsk->thread.i387.fxsave.st_space[0]; + for ( i = 0 ; i < 8 ; i++, to += 5, from += 8 ) { + memcpy( to, from, 5 * sizeof(unsigned short) ); + } +} + +int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu ) +{ + int fpvalid; + struct task_struct *tsk = current; + + fpvalid = tsk->used_math && cpu_has_fxsr; + if ( fpvalid ) { + unlazy_fpu( tsk ); + memcpy( fpu, &tsk->thread.i387.fxsave, + sizeof(struct user_i387_struct) ); + } + + return fpvalid; +} diff -Nru a/arch/x86_64/kernel/i8259.c b/arch/x86_64/kernel/i8259.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/i8259.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,485 @@ +#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 + +/* When we have things working, we can switch to always use + IOAPIC. --pavel */ + +/* + * Common place to define all x86 IRQ vectors + * + * This builds up the IRQ handler stubs using some ugly macros in irq.h + * + * These macros create the low-level assembly IRQ routines that save + * register context and call do_IRQ(). do_IRQ() then does all the + * operations that are needed to keep the AT (or SMP IOAPIC) + * interrupt-controller happy. + */ + +BUILD_COMMON_IRQ() + +#define BI(x,y) \ + BUILD_IRQ(x##y) + +#define BUILD_16_IRQS(x) \ + BI(x,0) BI(x,1) BI(x,2) BI(x,3) \ + BI(x,4) BI(x,5) BI(x,6) BI(x,7) \ + BI(x,8) BI(x,9) BI(x,a) BI(x,b) \ + BI(x,c) BI(x,d) BI(x,e) BI(x,f) + +/* + * ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts: + * (these are usually mapped to vectors 0x20-0x2f) + */ +BUILD_16_IRQS(0x0) + +#ifdef CONFIG_X86_IO_APIC +/* + * The IO-APIC gives us many more interrupt sources. Most of these + * are unused but an SMP system is supposed to have enough memory ... + * sometimes (mostly wrt. hw bugs) we get corrupted vectors all + * across the spectrum, so we really want to be prepared to get all + * of these. Plus, more powerful systems might have more than 64 + * IO-APIC registers. + * + * (these are usually mapped into the 0x30-0xff vector range) + */ + BUILD_16_IRQS(0x1) BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3) +BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7) +BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb) +BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) +#endif + +#undef BUILD_16_IRQS +#undef BI + + +/* + * The following vectors are part of the Linux architecture, there + * is no hardware IRQ pin equivalent for them, they are triggered + * through the ICC by us (IPIs) + */ +#ifdef CONFIG_SMP +BUILD_SMP_INTERRUPT(task_migration_interrupt,TASK_MIGRATION_VECTOR); +BUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR); +BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR); +BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR); +#endif + +/* + * every pentium local APIC has two 'local interrupts', with a + * soft-definable vector attached to both interrupts, one of + * which is a timer interrupt, the other one is error counter + * overflow. Linux uses the local APIC timer interrupt to get + * a much simpler SMP time architecture: + */ +#ifdef CONFIG_X86_LOCAL_APIC +BUILD_SMP_INTERRUPT(apic_timer_interrupt, LOCAL_TIMER_VECTOR); +BUILD_SMP_INTERRUPT(error_interrupt,ERROR_APIC_VECTOR); +BUILD_SMP_INTERRUPT(spurious_interrupt,SPURIOUS_APIC_VECTOR); +#endif + +#define IRQ(x,y) \ + IRQ##x##y##_interrupt + +#define IRQLIST_16(x) \ + IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \ + IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \ + IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \ + IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f) + +void (*interrupt[NR_IRQS])(void) = { + IRQLIST_16(0x0), + +#ifdef CONFIG_X86_IO_APIC + IRQLIST_16(0x1), IRQLIST_16(0x2), IRQLIST_16(0x3), + IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7), + IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb), + IRQLIST_16(0xc), IRQLIST_16(0xd) +#endif +}; + +#undef IRQ +#undef IRQLIST_16 + +/* + * This is the 'legacy' 8259A Programmable Interrupt Controller, + * present in the majority of PC/AT boxes. + * plus some generic x86 specific things if generic specifics makes + * any sense at all. + * this file should become arch/i386/kernel/irq.c when the old irq.c + * moves to arch independent land + */ + +spinlock_t i8259A_lock = SPIN_LOCK_UNLOCKED; + +static void end_8259A_irq (unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_8259A_irq(irq); +} + +#define shutdown_8259A_irq disable_8259A_irq + +void mask_and_ack_8259A(unsigned int); + +static unsigned int startup_8259A_irq(unsigned int irq) +{ + enable_8259A_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type i8259A_irq_type = { + "XT-PIC", + startup_8259A_irq, + shutdown_8259A_irq, + enable_8259A_irq, + disable_8259A_irq, + mask_and_ack_8259A, + end_8259A_irq, + NULL +}; + +/* + * 8259A PIC functions to handle ISA devices: + */ + +/* + * This contains the irq mask for both 8259A irq controllers, + */ +static unsigned int cached_irq_mask = 0xffff; + +#define __byte(x,y) (((unsigned char *)&(y))[x]) +#define cached_21 (__byte(0,cached_irq_mask)) +#define cached_A1 (__byte(1,cached_irq_mask)) + +/* + * Not all IRQs can be routed through the IO-APIC, eg. on certain (older) + * boards the timer interrupt is not really connected to any IO-APIC pin, + * it's fed to the master 8259A's IR0 line only. + * + * Any '1' bit in this mask means the IRQ is routed through the IO-APIC. + * this 'mixed mode' IRQ handling costs nothing because it's only used + * at IRQ setup time. + */ +unsigned long io_apic_irqs; + +void disable_8259A_irq(unsigned int irq) +{ + unsigned int mask = 1 << irq; + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + cached_irq_mask |= mask; + if (irq & 8) + outb(cached_A1,0xA1); + else + outb(cached_21,0x21); + spin_unlock_irqrestore(&i8259A_lock, flags); +} + +void enable_8259A_irq(unsigned int irq) +{ + unsigned int mask = ~(1 << irq); + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + cached_irq_mask &= mask; + if (irq & 8) + outb(cached_A1,0xA1); + else + outb(cached_21,0x21); + spin_unlock_irqrestore(&i8259A_lock, flags); +} + +int i8259A_irq_pending(unsigned int irq) +{ + unsigned int mask = 1<> 8); + spin_unlock_irqrestore(&i8259A_lock, flags); + + return ret; +} + +void make_8259A_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + io_apic_irqs &= ~(1<> 8); + outb(0x0A,0xA0); /* back to the IRR register */ + return value; +} + +/* + * Careful! The 8259A is a fragile beast, it pretty + * much _has_ to be done exactly like this (mask it + * first, _then_ send the EOI, and the order of EOI + * to the two 8259s is important! + */ +void mask_and_ack_8259A(unsigned int irq) +{ + unsigned int irqmask = 1 << irq; + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + /* + * Lightweight spurious IRQ detection. We do not want + * to overdo spurious IRQ handling - it's usually a sign + * of hardware problems, so we only do the checks we can + * do without slowing down good hardware unnecesserily. + * + * Note that IRQ7 and IRQ15 (the two spurious IRQs + * usually resulting from the 8259A-1|2 PICs) occur + * even if the IRQ is masked in the 8259A. Thus we + * can check spurious 8259A IRQs without doing the + * quite slow i8259A_irq_real() call for every IRQ. + * This does not cover 100% of spurious interrupts, + * but should be enough to warn the user that there + * is something bad going on ... + */ + if (cached_irq_mask & irqmask) + goto spurious_8259A_irq; + cached_irq_mask |= irqmask; + +handle_real_irq: + if (irq & 8) { + inb(0xA1); /* DUMMY - (do we need this?) */ + outb(cached_A1,0xA1); + outb(0x60+(irq&7),0xA0);/* 'Specific EOI' to slave */ + outb(0x62,0x20); /* 'Specific EOI' to master-IRQ2 */ + } else { + inb(0x21); /* DUMMY - (do we need this?) */ + outb(cached_21,0x21); + outb(0x60+irq,0x20); /* 'Specific EOI' to master */ + } + spin_unlock_irqrestore(&i8259A_lock, flags); + return; + +spurious_8259A_irq: + /* + * this is the slow path - should happen rarely. + */ + if (i8259A_irq_real(irq)) + /* + * oops, the IRQ _is_ in service according to the + * 8259A - not spurious, go handle it. + */ + goto handle_real_irq; + + { + static int spurious_irq_mask; + /* + * At this point we can be sure the IRQ is spurious, + * lets ACK and report it. [once per IRQ] + */ + if (!(spurious_irq_mask & irqmask)) { + printk("spurious 8259A interrupt: IRQ%d.\n", irq); + spurious_irq_mask |= irqmask; + } + atomic_inc(&irq_err_count); + /* + * Theoretically we do not have to handle this IRQ, + * but in Linux this does not cause problems and is + * simpler for us. + */ + goto handle_real_irq; + } +} + +void __init init_8259A(int auto_eoi) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + + outb(0xff, 0x21); /* mask all of 8259A-1 */ + outb(0xff, 0xA1); /* mask all of 8259A-2 */ + + /* + * outb_p - this has to work on a wide range of PC hardware. + */ + outb_p(0x11, 0x20); /* ICW1: select 8259A-1 init */ + outb_p(0x20 + 0, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */ + outb_p(0x04, 0x21); /* 8259A-1 (the master) has a slave on IR2 */ + if (auto_eoi) + outb_p(0x03, 0x21); /* master does Auto EOI */ + else + outb_p(0x01, 0x21); /* master expects normal EOI */ + + outb_p(0x11, 0xA0); /* ICW1: select 8259A-2 init */ + outb_p(0x20 + 8, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */ + outb_p(0x02, 0xA1); /* 8259A-2 is a slave on master's IR2 */ + outb_p(0x01, 0xA1); /* (slave's support for AEOI in flat mode + is to be investigated) */ + + if (auto_eoi) + /* + * in AEOI mode we just have to mask the interrupt + * when acking. + */ + i8259A_irq_type.ack = disable_8259A_irq; + else + i8259A_irq_type.ack = mask_and_ack_8259A; + + udelay(100); /* wait for 8259A to initialize */ + + outb(cached_21, 0x21); /* restore master IRQ mask */ + outb(cached_A1, 0xA1); /* restore slave IRQ mask */ + + spin_unlock_irqrestore(&i8259A_lock, flags); +} + +/* + * IRQ2 is cascade interrupt to second interrupt controller + */ + +#ifndef CONFIG_VISWS +static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL}; +#endif + + +void __init init_ISA_irqs (void) +{ + int i; + +#ifdef CONFIG_X86_LOCAL_APIC + init_bsp_APIC(); +#endif + init_8259A(0); + + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 1; + + if (i < 16) { + /* + * 16 old-style INTA-cycle interrupts: + */ + irq_desc[i].handler = &i8259A_irq_type; + } else { + /* + * 'high' PCI IRQs filled in on demand + */ + irq_desc[i].handler = &no_irq_type; + } + } +} + +void __init init_IRQ(void) +{ + int i; + +#ifndef CONFIG_X86_VISWS_APIC + init_ISA_irqs(); +#else + init_VISWS_APIC_irqs(); +#endif + /* + * Cover the whole vector space, no vector can escape + * us. (some of these will be overridden and become + * 'special' SMP interrupts) + */ + for (i = 0; i < NR_IRQS; i++) { + int vector = FIRST_EXTERNAL_VECTOR + i; + if (vector != IA32_SYSCALL_VECTOR) + set_intr_gate(vector, interrupt[i]); + } + +#ifdef CONFIG_SMP + /* + * IRQ0 must be given a fixed assignment and initialized, + * because it's used before the IO-APIC is set up. + */ + set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]); + + /* + * The reschedule interrupt is a CPU-to-CPU reschedule-helper + * IPI, driven by wakeup. + */ + set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); + + /* IPI for task migration */ + set_intr_gate(TASK_MIGRATION_VECTOR, task_migration_interrupt); + + /* IPI for invalidation */ + set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); + + /* IPI for generic function call */ + set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt); +#endif + +#ifdef CONFIG_X86_LOCAL_APIC + /* self generated IPI for local APIC timer */ + set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt); + + /* IPI vectors for APIC spurious and error interrupts */ + set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); + set_intr_gate(ERROR_APIC_VECTOR, error_interrupt); +#endif + + /* + * Set the clock to HZ Hz, we already have a valid + * vector now: + */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + +#ifndef CONFIG_VISWS + setup_irq(2, &irq2); +#endif +} diff -Nru a/arch/x86_64/kernel/init_task.c b/arch/x86_64/kernel/init_task.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/init_task.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial thread structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); + +/* + * per-CPU TSS segments. Threads are completely 'soft' on Linux, + * no more per-task TSS's. The TSS size is kept cacheline-aligned + * so they are allowed to end up in the .data.cacheline_aligned + * section. Since TSS's are completely CPU-local, we want them + * on exact cacheline boundaries, to eliminate cacheline ping-pong. + */ +struct tss_struct init_tss[NR_CPUS] __cacheline_aligned = { [0 ... NR_CPUS-1] = INIT_TSS }; diff -Nru a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/io_apic.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1617 @@ +/* + * Intel IO-APIC support for multi-Pentium hosts. + * + * Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar, Hajnalka Szabo + * + * Many thanks to Stig Venaas for trying out countless experimental + * patches and reporting/debugging problems patiently! + * + * (c) 1999, Multiple IO-APIC support, developed by + * Ken-ichi Yaku and + * Hidemi Kishimoto , + * further tested and cleaned up by Zach Brown + * and Ingo Molnar + * + * Fixes + * Maciej W. Rozycki : Bits for genuine 82489DX APICs; + * thanks to Eric Gilmore + * and Rolf G. Tews + * for testing these extensively + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#undef APIC_LOCKUP_DEBUG + +#define APIC_LOCKUP_DEBUG + +static spinlock_t ioapic_lock = SPIN_LOCK_UNLOCKED; + +/* + * # of IRQ routing registers + */ +int nr_ioapic_registers[MAX_IO_APICS]; + +/* + * Rough estimation of how many shared IRQs there are, can + * be changed anytime. + */ +#define MAX_PLUS_SHARED_IRQS NR_IRQS +#define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS) + +/* + * This is performance-critical, we want to do it O(1) + * + * the indexing order of this array favors 1:1 mappings + * between pins and IRQs. + */ + +static struct irq_pin_list { + int apic, pin, next; +} irq_2_pin[PIN_MAP_SIZE]; + +/* + * The common case is 1:1 IRQ<->pin mappings. Sometimes there are + * shared ISA-space IRQs, so we have to support them. We are super + * fast in the common case, and fast for shared ISA-space IRQs. + */ +static void add_pin_to_irq(unsigned int irq, int apic, int pin) +{ + static int first_free_entry = NR_IRQS; + struct irq_pin_list *entry = irq_2_pin + irq; + + while (entry->next) + entry = irq_2_pin + entry->next; + + if (entry->pin != -1) { + entry->next = first_free_entry; + entry = irq_2_pin + entry->next; + if (++first_free_entry >= PIN_MAP_SIZE) + panic("io_apic.c: whoops"); + } + entry->apic = apic; + entry->pin = pin; +} + +#define __DO_ACTION(R, ACTION, FINAL) \ + \ +{ \ + int pin; \ + struct irq_pin_list *entry = irq_2_pin + irq; \ + \ + for (;;) { \ + unsigned int reg; \ + pin = entry->pin; \ + if (pin == -1) \ + break; \ + reg = io_apic_read(entry->apic, 0x10 + R + pin*2); \ + reg ACTION; \ + io_apic_modify(entry->apic, reg); \ + if (!entry->next) \ + break; \ + entry = irq_2_pin + entry->next; \ + } \ + FINAL; \ +} + +#define DO_ACTION(name,R,ACTION, FINAL) \ + \ + static void name##_IO_APIC_irq (unsigned int irq) \ + __DO_ACTION(R, ACTION, FINAL) + +DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic) ) + /* mask = 1 */ +DO_ACTION( __unmask, 0, &= 0xfffeffff, ) + /* mask = 0 */ +DO_ACTION( __mask_and_edge, 0, = (reg & 0xffff7fff) | 0x00010000, ) + /* mask = 1, trigger = 0 */ +DO_ACTION( __unmask_and_level, 0, = (reg & 0xfffeffff) | 0x00008000, ) + /* mask = 0, trigger = 1 */ + +static void mask_IO_APIC_irq (unsigned int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + __mask_IO_APIC_irq(irq); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +static void unmask_IO_APIC_irq (unsigned int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + __unmask_IO_APIC_irq(irq); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +void clear_IO_APIC_pin(unsigned int apic, unsigned int pin) +{ + struct IO_APIC_route_entry entry; + unsigned long flags; + + /* + * Disable it in the IO-APIC irq-routing table: + */ + memset(&entry, 0, sizeof(entry)); + entry.mask = 1; + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry) + 0)); + io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry) + 1)); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +static void clear_IO_APIC (void) +{ + int apic, pin; + + for (apic = 0; apic < nr_ioapics; apic++) + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) + clear_IO_APIC_pin(apic, pin); +} + +/* + * support for broken MP BIOSs, enables hand-redirection of PIRQ0-7 to + * specific CPU-side IRQs. + */ + +#define MAX_PIRQS 8 +int pirq_entries [MAX_PIRQS]; +int pirqs_enabled; +int skip_ioapic_setup; + +static int __init ioapic_setup(char *str) +{ + skip_ioapic_setup = 1; + return 1; +} + +__setup("noapic", ioapic_setup); + +static int __init ioapic_pirq_setup(char *str) +{ + int i, max; + int ints[MAX_PIRQS+1]; + + get_options(str, ARRAY_SIZE(ints), ints); + + for (i = 0; i < MAX_PIRQS; i++) + pirq_entries[i] = -1; + + pirqs_enabled = 1; + printk(KERN_INFO "PIRQ redirection, working around broken MP-BIOS.\n"); + max = MAX_PIRQS; + if (ints[0] < MAX_PIRQS) + max = ints[0]; + + for (i = 0; i < max; i++) { + printk(KERN_DEBUG "... PIRQ%d -> IRQ %d\n", i, ints[i+1]); + /* + * PIRQs are mapped upside down, usually. + */ + pirq_entries[MAX_PIRQS-i-1] = ints[i+1]; + } + return 1; +} + +__setup("pirq=", ioapic_pirq_setup); + +/* + * Find the IRQ entry number of a certain pin. + */ +static int __init find_irq_entry(int apic, int pin, int type) +{ + int i; + + for (i = 0; i < mp_irq_entries; i++) + if (mp_irqs[i].mpc_irqtype == type && + (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid || + mp_irqs[i].mpc_dstapic == MP_APIC_ALL) && + mp_irqs[i].mpc_dstirq == pin) + return i; + + return -1; +} + +/* + * Find the pin to which IRQ[irq] (ISA) is connected + */ +static int __init find_isa_irq_pin(int irq, int type) +{ + int i; + + for (i = 0; i < mp_irq_entries; i++) { + int lbus = mp_irqs[i].mpc_srcbus; + + if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA || + mp_bus_id_to_type[lbus] == MP_BUS_EISA || + mp_bus_id_to_type[lbus] == MP_BUS_MCA) && + (mp_irqs[i].mpc_irqtype == type) && + (mp_irqs[i].mpc_srcbusirq == irq)) + + return mp_irqs[i].mpc_dstirq; + } + return -1; +} + +/* + * Find a specific PCI IRQ entry. + * Not an __init, possibly needed by modules + */ +static int pin_2_irq(int idx, int apic, int pin); + +int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin) +{ + int apic, i, best_guess = -1; + + Dprintk("querying PCI -> IRQ mapping bus:%d, slot:%d, pin:%d.\n", + bus, slot, pin); + if (mp_bus_id_to_pci_bus[bus] == -1) { + printk(KERN_WARNING "PCI BIOS passed nonexistent PCI bus %d!\n", bus); + return -1; + } + for (i = 0; i < mp_irq_entries; i++) { + int lbus = mp_irqs[i].mpc_srcbus; + + for (apic = 0; apic < nr_ioapics; apic++) + if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic || + mp_irqs[i].mpc_dstapic == MP_APIC_ALL) + break; + + if ((mp_bus_id_to_type[lbus] == MP_BUS_PCI) && + !mp_irqs[i].mpc_irqtype && + (bus == lbus) && + (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f))) { + int irq = pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); + + if (!(apic || IO_APIC_IRQ(irq))) + continue; + + if (pin == (mp_irqs[i].mpc_srcbusirq & 3)) + return irq; + /* + * Use the first all-but-pin matching entry as a + * best-guess fuzzy result for broken mptables. + */ + if (best_guess < 0) + best_guess = irq; + } + } + return best_guess; +} + +/* + * EISA Edge/Level control register, ELCR + */ +static int __init EISA_ELCR(unsigned int irq) +{ + if (irq < 16) { + unsigned int port = 0x4d0 + (irq >> 3); + return (inb(port) >> (irq & 7)) & 1; + } + printk(KERN_INFO "Broken MPtable reports ISA irq %d\n", irq); + return 0; +} + +/* EISA interrupts are always polarity zero and can be edge or level + * trigger depending on the ELCR value. If an interrupt is listed as + * EISA conforming in the MP table, that means its trigger type must + * be read in from the ELCR */ + +#define default_EISA_trigger(idx) (EISA_ELCR(mp_irqs[idx].mpc_srcbusirq)) +#define default_EISA_polarity(idx) (0) + +/* ISA interrupts are always polarity zero edge triggered, + * when listed as conforming in the MP table. */ + +#define default_ISA_trigger(idx) (0) +#define default_ISA_polarity(idx) (0) + +/* PCI interrupts are always polarity one level triggered, + * when listed as conforming in the MP table. */ + +#define default_PCI_trigger(idx) (1) +#define default_PCI_polarity(idx) (1) + +/* MCA interrupts are always polarity zero level triggered, + * when listed as conforming in the MP table. */ + +#define default_MCA_trigger(idx) (1) +#define default_MCA_polarity(idx) (0) + +static int __init MPBIOS_polarity(int idx) +{ + int bus = mp_irqs[idx].mpc_srcbus; + int polarity; + + /* + * Determine IRQ line polarity (high active or low active): + */ + switch (mp_irqs[idx].mpc_irqflag & 3) + { + case 0: /* conforms, ie. bus-type dependent polarity */ + { + switch (mp_bus_id_to_type[bus]) + { + case MP_BUS_ISA: /* ISA pin */ + { + polarity = default_ISA_polarity(idx); + break; + } + case MP_BUS_EISA: /* EISA pin */ + { + polarity = default_EISA_polarity(idx); + break; + } + case MP_BUS_PCI: /* PCI pin */ + { + polarity = default_PCI_polarity(idx); + break; + } + case MP_BUS_MCA: /* MCA pin */ + { + polarity = default_MCA_polarity(idx); + break; + } + default: + { + printk(KERN_WARNING "broken BIOS!!\n"); + polarity = 1; + break; + } + } + break; + } + case 1: /* high active */ + { + polarity = 0; + break; + } + case 2: /* reserved */ + { + printk(KERN_WARNING "broken BIOS!!\n"); + polarity = 1; + break; + } + case 3: /* low active */ + { + polarity = 1; + break; + } + default: /* invalid */ + { + printk(KERN_WARNING "broken BIOS!!\n"); + polarity = 1; + break; + } + } + return polarity; +} + +static int __init MPBIOS_trigger(int idx) +{ + int bus = mp_irqs[idx].mpc_srcbus; + int trigger; + + /* + * Determine IRQ trigger mode (edge or level sensitive): + */ + switch ((mp_irqs[idx].mpc_irqflag>>2) & 3) + { + case 0: /* conforms, ie. bus-type dependent */ + { + switch (mp_bus_id_to_type[bus]) + { + case MP_BUS_ISA: /* ISA pin */ + { + trigger = default_ISA_trigger(idx); + break; + } + case MP_BUS_EISA: /* EISA pin */ + { + trigger = default_EISA_trigger(idx); + break; + } + case MP_BUS_PCI: /* PCI pin */ + { + trigger = default_PCI_trigger(idx); + break; + } + case MP_BUS_MCA: /* MCA pin */ + { + trigger = default_MCA_trigger(idx); + break; + } + default: + { + printk(KERN_WARNING "broken BIOS!!\n"); + trigger = 1; + break; + } + } + break; + } + case 1: /* edge */ + { + trigger = 0; + break; + } + case 2: /* reserved */ + { + printk(KERN_WARNING "broken BIOS!!\n"); + trigger = 1; + break; + } + case 3: /* level */ + { + trigger = 1; + break; + } + default: /* invalid */ + { + printk(KERN_WARNING "broken BIOS!!\n"); + trigger = 0; + break; + } + } + return trigger; +} + +static inline int irq_polarity(int idx) +{ + return MPBIOS_polarity(idx); +} + +static inline int irq_trigger(int idx) +{ + return MPBIOS_trigger(idx); +} + +static int pin_2_irq(int idx, int apic, int pin) +{ + int irq, i; + int bus = mp_irqs[idx].mpc_srcbus; + + /* + * Debugging check, we are in big trouble if this message pops up! + */ + if (mp_irqs[idx].mpc_dstirq != pin) + printk(KERN_ERR "broken BIOS or MPTABLE parser, ayiee!!\n"); + + switch (mp_bus_id_to_type[bus]) + { + case MP_BUS_ISA: /* ISA pin */ + case MP_BUS_EISA: + case MP_BUS_MCA: + { + irq = mp_irqs[idx].mpc_srcbusirq; + break; + } + case MP_BUS_PCI: /* PCI pin */ + { + /* + * PCI IRQs are mapped in order + */ + i = irq = 0; + while (i < apic) + irq += nr_ioapic_registers[i++]; + irq += pin; + break; + } + default: + { + printk(KERN_ERR "unknown bus type %d.\n",bus); + irq = 0; + break; + } + } + + /* + * PCI IRQ command line redirection. Yes, limits are hardcoded. + */ + if ((pin >= 16) && (pin <= 23)) { + if (pirq_entries[pin-16] != -1) { + if (!pirq_entries[pin-16]) { + printk(KERN_DEBUG "disabling PIRQ%d\n", pin-16); + } else { + irq = pirq_entries[pin-16]; + printk(KERN_DEBUG "using PIRQ%d -> IRQ %d\n", + pin-16, irq); + } + } + } + return irq; +} + +static inline int IO_APIC_irq_trigger(int irq) +{ + int apic, idx, pin; + + for (apic = 0; apic < nr_ioapics; apic++) { + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { + idx = find_irq_entry(apic,pin,mp_INT); + if ((idx != -1) && (irq == pin_2_irq(idx,apic,pin))) + return irq_trigger(idx); + } + } + /* + * nonexistent IRQs are edge default + */ + return 0; +} + +int irq_vector[NR_IRQS] = { FIRST_DEVICE_VECTOR , 0 }; + +static int __init assign_irq_vector(int irq) +{ + static int current_vector = FIRST_DEVICE_VECTOR, offset = 0; + if (IO_APIC_VECTOR(irq) > 0) + return IO_APIC_VECTOR(irq); +next: + current_vector += 8; + if (current_vector == IA32_SYSCALL_VECTOR) + goto next; + + if (current_vector > FIRST_SYSTEM_VECTOR) { + offset++; + current_vector = FIRST_DEVICE_VECTOR + offset; + } + + if (current_vector == FIRST_SYSTEM_VECTOR) + panic("ran out of interrupt sources!"); + + IO_APIC_VECTOR(irq) = current_vector; + return current_vector; +} + +extern void (*interrupt[NR_IRQS])(void); +static struct hw_interrupt_type ioapic_level_irq_type; +static struct hw_interrupt_type ioapic_edge_irq_type; + +void __init setup_IO_APIC_irqs(void) +{ + struct IO_APIC_route_entry entry; + int apic, pin, idx, irq, first_notcon = 1, vector; + unsigned long flags; + + printk(KERN_DEBUG "init IO_APIC IRQs\n"); + + for (apic = 0; apic < nr_ioapics; apic++) { + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { + + /* + * add it to the IO-APIC irq-routing table: + */ + memset(&entry,0,sizeof(entry)); + + entry.delivery_mode = dest_LowestPrio; + entry.dest_mode = INT_DELIVERY_MODE; + entry.mask = 0; /* enable IRQ */ + entry.dest.logical.logical_dest = TARGET_CPUS; + + idx = find_irq_entry(apic,pin,mp_INT); + if (idx == -1) { + if (first_notcon) { + printk(KERN_DEBUG " IO-APIC (apicid-pin) %d-%d", mp_ioapics[apic].mpc_apicid, pin); + first_notcon = 0; + } else + printk(", %d-%d", mp_ioapics[apic].mpc_apicid, pin); + continue; + } + + entry.trigger = irq_trigger(idx); + entry.polarity = irq_polarity(idx); + + if (irq_trigger(idx)) { + entry.trigger = 1; + entry.mask = 1; + entry.dest.logical.logical_dest = TARGET_CPUS; + } + + irq = pin_2_irq(idx, apic, pin); + add_pin_to_irq(irq, apic, pin); + + if (!apic && !IO_APIC_IRQ(irq)) + continue; + + if (IO_APIC_IRQ(irq)) { + vector = assign_irq_vector(irq); + entry.vector = vector; + + if (IO_APIC_irq_trigger(irq)) + irq_desc[irq].handler = &ioapic_level_irq_type; + else + irq_desc[irq].handler = &ioapic_edge_irq_type; + + set_intr_gate(vector, interrupt[irq]); + + if (!apic && (irq < 16)) + disable_8259A_irq(irq); + } + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); + spin_unlock_irqrestore(&ioapic_lock, flags); + } + } + + if (!first_notcon) + printk(" not connected.\n"); +} + +/* + * Set up the 8259A-master output pin as broadcast to all + * CPUs. + */ +void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) +{ + struct IO_APIC_route_entry entry; + unsigned long flags; + + memset(&entry,0,sizeof(entry)); + + disable_8259A_irq(0); + + /* mask LVT0 */ + apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); + + /* + * We use logical delivery to get the timer IRQ + * to the first CPU. + */ + entry.dest_mode = INT_DELIVERY_MODE; + entry.mask = 0; /* unmask IRQ now */ + entry.dest.logical.logical_dest = TARGET_CPUS; + entry.delivery_mode = dest_LowestPrio; + entry.polarity = 0; + entry.trigger = 0; + entry.vector = vector; + + /* + * The timer IRQ doesnt have to know that behind the + * scene we have a 8259A-master in AEOI mode ... + */ + irq_desc[0].handler = &ioapic_edge_irq_type; + + /* + * Add it to the IO-APIC irq-routing table: + */ + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); + spin_unlock_irqrestore(&ioapic_lock, flags); + + enable_8259A_irq(0); +} + +void __init UNEXPECTED_IO_APIC(void) +{ + printk(KERN_WARNING " WARNING: unexpected IO-APIC, please mail\n"); + printk(KERN_WARNING " to linux-smp@vger.kernel.org\n"); +} + +void __init print_IO_APIC(void) +{ + int apic, i; + struct IO_APIC_reg_00 reg_00; + struct IO_APIC_reg_01 reg_01; + struct IO_APIC_reg_02 reg_02; + unsigned long flags; + + printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries); + for (i = 0; i < nr_ioapics; i++) + printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n", + mp_ioapics[i].mpc_apicid, nr_ioapic_registers[i]); + + /* + * We are a bit conservative about what we expect. We have to + * know about every hardware change ASAP. + */ + printk(KERN_INFO "testing the IO APIC.......................\n"); + + for (apic = 0; apic < nr_ioapics; apic++) { + + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_00 = io_apic_read(apic, 0); + *(int *)®_01 = io_apic_read(apic, 1); + if (reg_01.version >= 0x10) + *(int *)®_02 = io_apic_read(apic, 2); + spin_unlock_irqrestore(&ioapic_lock, flags); + + printk("\n"); + printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mpc_apicid); + printk(KERN_DEBUG ".... register #00: %08X\n", *(int *)®_00); + printk(KERN_DEBUG "....... : physical APIC id: %02X\n", reg_00.ID); + if (reg_00.__reserved_1 || reg_00.__reserved_2) + UNEXPECTED_IO_APIC(); + + printk(KERN_DEBUG ".... register #01: %08X\n", *(int *)®_01); + printk(KERN_DEBUG "....... : max redirection entries: %04X\n", reg_01.entries); + if ( (reg_01.entries != 0x0f) && /* older (Neptune) boards */ + (reg_01.entries != 0x17) && /* typical ISA+PCI boards */ + (reg_01.entries != 0x1b) && /* Compaq Proliant boards */ + (reg_01.entries != 0x1f) && /* dual Xeon boards */ + (reg_01.entries != 0x22) && /* bigger Xeon boards */ + (reg_01.entries != 0x2E) && + (reg_01.entries != 0x3F) + ) + UNEXPECTED_IO_APIC(); + + printk(KERN_DEBUG "....... : PRQ implemented: %X\n", reg_01.PRQ); + printk(KERN_DEBUG "....... : IO APIC version: %04X\n", reg_01.version); + if ( (reg_01.version != 0x01) && /* 82489DX IO-APICs */ + (reg_01.version != 0x02) && /* 82801BA IO-APICs (ICH2) */ + (reg_01.version != 0x10) && /* oldest IO-APICs */ + (reg_01.version != 0x11) && /* Pentium/Pro IO-APICs */ + (reg_01.version != 0x13) && /* Xeon IO-APICs */ + (reg_01.version != 0x20) /* Intel P64H (82806 AA) */ + ) + UNEXPECTED_IO_APIC(); + if (reg_01.__reserved_1 || reg_01.__reserved_2) + UNEXPECTED_IO_APIC(); + + if (reg_01.version >= 0x10) { + printk(KERN_DEBUG ".... register #02: %08X\n", *(int *)®_02); + printk(KERN_DEBUG "....... : arbitration: %02X\n", reg_02.arbitration); + if (reg_02.__reserved_1 || reg_02.__reserved_2) + UNEXPECTED_IO_APIC(); + } + + printk(KERN_DEBUG ".... IRQ redirection table:\n"); + + printk(KERN_DEBUG " NR Log Phy Mask Trig IRR Pol" + " Stat Dest Deli Vect: \n"); + + for (i = 0; i <= reg_01.entries; i++) { + struct IO_APIC_route_entry entry; + + spin_lock_irqsave(&ioapic_lock, flags); + *(((int *)&entry)+0) = io_apic_read(apic, 0x10+i*2); + *(((int *)&entry)+1) = io_apic_read(apic, 0x11+i*2); + spin_unlock_irqrestore(&ioapic_lock, flags); + + printk(KERN_DEBUG " %02x %03X %02X ", + i, + entry.dest.logical.logical_dest, + entry.dest.physical.physical_dest + ); + + printk("%1d %1d %1d %1d %1d %1d %1d %02X\n", + entry.mask, + entry.trigger, + entry.irr, + entry.polarity, + entry.delivery_status, + entry.dest_mode, + entry.delivery_mode, + entry.vector + ); + } + } + printk(KERN_DEBUG "IRQ to pin mappings:\n"); + for (i = 0; i < NR_IRQS; i++) { + struct irq_pin_list *entry = irq_2_pin + i; + if (entry->pin < 0) + continue; + printk(KERN_DEBUG "IRQ%d ", i); + for (;;) { + printk("-> %d:%d", entry->apic, entry->pin); + if (!entry->next) + break; + entry = irq_2_pin + entry->next; + } + printk("\n"); + } + + printk(KERN_INFO ".................................... done.\n"); + + return; +} + +static void print_APIC_bitfield (int base) +{ + unsigned int v; + int i, j; + + printk(KERN_DEBUG "0123456789abcdef0123456789abcdef\n" KERN_DEBUG); + for (i = 0; i < 8; i++) { + v = apic_read(base + i*0x10); + for (j = 0; j < 32; j++) { + if (v & (1< 3) /* Due to the Pentium erratum 3AP. */ + apic_write(APIC_ESR, 0); + v = apic_read(APIC_ESR); + printk(KERN_DEBUG "... APIC ESR: %08x\n", v); + } + + v = apic_read(APIC_ICR); + printk(KERN_DEBUG "... APIC ICR: %08x\n", v); + v = apic_read(APIC_ICR2); + printk(KERN_DEBUG "... APIC ICR2: %08x\n", v); + + v = apic_read(APIC_LVTT); + printk(KERN_DEBUG "... APIC LVTT: %08x\n", v); + + if (maxlvt > 3) { /* PC is LVT#4. */ + v = apic_read(APIC_LVTPC); + printk(KERN_DEBUG "... APIC LVTPC: %08x\n", v); + } + v = apic_read(APIC_LVT0); + printk(KERN_DEBUG "... APIC LVT0: %08x\n", v); + v = apic_read(APIC_LVT1); + printk(KERN_DEBUG "... APIC LVT1: %08x\n", v); + + if (maxlvt > 2) { /* ERR is LVT#3. */ + v = apic_read(APIC_LVTERR); + printk(KERN_DEBUG "... APIC LVTERR: %08x\n", v); + } + + v = apic_read(APIC_TMICT); + printk(KERN_DEBUG "... APIC TMICT: %08x\n", v); + v = apic_read(APIC_TMCCT); + printk(KERN_DEBUG "... APIC TMCCT: %08x\n", v); + v = apic_read(APIC_TDCR); + printk(KERN_DEBUG "... APIC TDCR: %08x\n", v); + printk("\n"); +} + +void print_all_local_APICs (void) +{ + smp_call_function(print_local_APIC, NULL, 1, 1); + print_local_APIC(NULL); +} + +void /*__init*/ print_PIC(void) +{ + extern spinlock_t i8259A_lock; + unsigned int v; + unsigned long flags; + + printk(KERN_DEBUG "\nprinting PIC contents\n"); + + spin_lock_irqsave(&i8259A_lock, flags); + + v = inb(0xa1) << 8 | inb(0x21); + printk(KERN_DEBUG "... PIC IMR: %04x\n", v); + + v = inb(0xa0) << 8 | inb(0x20); + printk(KERN_DEBUG "... PIC IRR: %04x\n", v); + + outb(0x0b,0xa0); + outb(0x0b,0x20); + v = inb(0xa0) << 8 | inb(0x20); + outb(0x0a,0xa0); + outb(0x0a,0x20); + + spin_unlock_irqrestore(&i8259A_lock, flags); + + printk(KERN_DEBUG "... PIC ISR: %04x\n", v); + + v = inb(0x4d1) << 8 | inb(0x4d0); + printk(KERN_DEBUG "... PIC ELCR: %04x\n", v); +} + +static void __init enable_IO_APIC(void) +{ + struct IO_APIC_reg_01 reg_01; + int i; + unsigned long flags; + + for (i = 0; i < PIN_MAP_SIZE; i++) { + irq_2_pin[i].pin = -1; + irq_2_pin[i].next = 0; + } + if (!pirqs_enabled) + for (i = 0; i < MAX_PIRQS; i++) + pirq_entries[i] = -1; + + /* + * The number of IO-APIC IRQ registers (== #pins): + */ + for (i = 0; i < nr_ioapics; i++) { + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_01 = io_apic_read(i, 1); + spin_unlock_irqrestore(&ioapic_lock, flags); + nr_ioapic_registers[i] = reg_01.entries+1; + } + + /* + * Do not trust the IO-APIC being empty at bootup + */ + clear_IO_APIC(); +} + +/* + * Not an __init, needed by the reboot code + */ +void disable_IO_APIC(void) +{ + /* + * Clear the IO-APIC before rebooting: + */ + clear_IO_APIC(); + + disconnect_bsp_APIC(); +} + +/* + * function to set the IO-APIC physical IDs based on the + * values stored in the MPC table. + * + * by Matt Domsch Tue Dec 21 12:25:05 CST 1999 + */ + +static void __init setup_ioapic_ids_from_mpc (void) +{ + struct IO_APIC_reg_00 reg_00; + unsigned long phys_id_present_map = phys_cpu_present_map; + int apic; + int i; + unsigned char old_id; + unsigned long flags; + + /* + * Set the IOAPIC ID to the value stored in the MPC table. + */ + for (apic = 0; apic < nr_ioapics; apic++) { + + /* Read the register 0 value */ + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_00 = io_apic_read(apic, 0); + spin_unlock_irqrestore(&ioapic_lock, flags); + + old_id = mp_ioapics[apic].mpc_apicid; + + if (mp_ioapics[apic].mpc_apicid >= 0xf) { + printk(KERN_ERR "BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n", + apic, mp_ioapics[apic].mpc_apicid); + printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", + reg_00.ID); + mp_ioapics[apic].mpc_apicid = reg_00.ID; + } + + /* + * Sanity check, is the ID really free? Every APIC in a + * system must have a unique ID or we get lots of nice + * 'stuck on smp_invalidate_needed IPI wait' messages. + */ + if (phys_id_present_map & (1 << mp_ioapics[apic].mpc_apicid)) { + printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n", + apic, mp_ioapics[apic].mpc_apicid); + for (i = 0; i < 0xf; i++) + if (!(phys_id_present_map & (1 << i))) + break; + if (i >= 0xf) + panic("Max APIC ID exceeded!\n"); + printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", + i); + phys_id_present_map |= 1 << i; + mp_ioapics[apic].mpc_apicid = i; + } else { + printk("Setting %d in the phys_id_present_map\n", mp_ioapics[apic].mpc_apicid); + phys_id_present_map |= 1 << mp_ioapics[apic].mpc_apicid; + } + + + /* + * We need to adjust the IRQ routing table + * if the ID changed. + */ + if (old_id != mp_ioapics[apic].mpc_apicid) + for (i = 0; i < mp_irq_entries; i++) + if (mp_irqs[i].mpc_dstapic == old_id) + mp_irqs[i].mpc_dstapic + = mp_ioapics[apic].mpc_apicid; + + /* + * Read the right value from the MPC table and + * write it into the ID register. + */ + printk(KERN_INFO "...changing IO-APIC physical APIC ID to %d ...", + mp_ioapics[apic].mpc_apicid); + + reg_00.ID = mp_ioapics[apic].mpc_apicid; + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(apic, 0, *(int *)®_00); + spin_unlock_irqrestore(&ioapic_lock, flags); + + /* + * Sanity check + */ + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_00 = io_apic_read(apic, 0); + spin_unlock_irqrestore(&ioapic_lock, flags); + if (reg_00.ID != mp_ioapics[apic].mpc_apicid) + panic("could not set ID!\n"); + else + printk(" ok.\n"); + } +} + +/* + * There is a nasty bug in some older SMP boards, their mptable lies + * about the timer IRQ. We do the following to work around the situation: + * + * - timer IRQ defaults to IO-APIC IRQ + * - if this function detects that timer IRQs are defunct, then we fall + * back to ISA timer IRQs + */ +static int __init timer_irq_works(void) +{ + unsigned int t1 = jiffies; + + sti(); + /* Let ten ticks pass... */ + mdelay((10 * 1000) / HZ); + + /* + * Expect a few ticks at least, to be sure some possible + * glue logic does not lock up after one or two first + * ticks in a non-ExtINT mode. Also the local APIC + * might have cached one ExtINT interrupt. Finally, at + * least one tick may be lost due to delays. + */ + if (jiffies - t1 > 4) + return 1; + + return 0; +} + +/* + * In the SMP+IOAPIC case it might happen that there are an unspecified + * number of pending IRQ events unhandled. These cases are very rare, + * so we 'resend' these IRQs via IPIs, to the same CPU. It's much + * better to do it this way as thus we do not have to be aware of + * 'pending' interrupts in the IRQ path, except at this point. + */ +/* + * Edge triggered needs to resend any interrupt + * that was delayed but this is now handled in the device + * independent code. + */ +#define enable_edge_ioapic_irq unmask_IO_APIC_irq + +static void disable_edge_ioapic_irq (unsigned int irq) { /* nothing */ } + +/* + * Starting up a edge-triggered IO-APIC interrupt is + * nasty - we need to make sure that we get the edge. + * If it is already asserted for some reason, we need + * return 1 to indicate that is was pending. + * + * This is not complete - we should be able to fake + * an edge even if it isn't on the 8259A... + */ + +static unsigned int startup_edge_ioapic_irq(unsigned int irq) +{ + int was_pending = 0; + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + if (irq < 16) { + disable_8259A_irq(irq); + if (i8259A_irq_pending(irq)) + was_pending = 1; + } + __unmask_IO_APIC_irq(irq); + spin_unlock_irqrestore(&ioapic_lock, flags); + + return was_pending; +} + +#define shutdown_edge_ioapic_irq disable_edge_ioapic_irq + +/* + * Once we have recorded IRQ_PENDING already, we can mask the + * interrupt for real. This prevents IRQ storms from unhandled + * devices. + */ +static void ack_edge_ioapic_irq(unsigned int irq) +{ + if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED)) + == (IRQ_PENDING | IRQ_DISABLED)) + mask_IO_APIC_irq(irq); + ack_APIC_irq(); +} + +static void end_edge_ioapic_irq (unsigned int i) { /* nothing */ } + + +/* + * Level triggered interrupts can just be masked, + * and shutting down and starting up the interrupt + * is the same as enabling and disabling them -- except + * with a startup need to return a "was pending" value. + * + * Level triggered interrupts are special because we + * do not touch any IO-APIC register while handling + * them. We ack the APIC in the end-IRQ handler, not + * in the start-IRQ-handler. Protection against reentrance + * from the same interrupt is still provided, both by the + * generic IRQ layer and by the fact that an unacked local + * APIC does not accept IRQs. + */ +static unsigned int startup_level_ioapic_irq (unsigned int irq) +{ + unmask_IO_APIC_irq(irq); + + return 0; /* don't check for pending */ +} + +#define shutdown_level_ioapic_irq mask_IO_APIC_irq +#define enable_level_ioapic_irq unmask_IO_APIC_irq +#define disable_level_ioapic_irq mask_IO_APIC_irq + +static void end_level_ioapic_irq (unsigned int irq) +{ + unsigned long v; + int i; + +/* + * It appears there is an erratum which affects at least version 0x11 + * of I/O APIC (that's the 82093AA and cores integrated into various + * chipsets). Under certain conditions a level-triggered interrupt is + * erroneously delivered as edge-triggered one but the respective IRR + * bit gets set nevertheless. As a result the I/O unit expects an EOI + * message but it will never arrive and further interrupts are blocked + * from the source. The exact reason is so far unknown, but the + * phenomenon was observed when two consecutive interrupt requests + * from a given source get delivered to the same CPU and the source is + * temporarily disabled in between. + * + * A workaround is to simulate an EOI message manually. We achieve it + * by setting the trigger mode to edge and then to level when the edge + * trigger mode gets detected in the TMR of a local APIC for a + * level-triggered interrupt. We mask the source for the time of the + * operation to prevent an edge-triggered interrupt escaping meanwhile. + * The idea is from Manfred Spraul. --macro + */ + i = IO_APIC_VECTOR(irq); + v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1)); + + ack_APIC_irq(); + + if (!(v & (1 << (i & 0x1f)))) { +#ifdef APIC_LOCKUP_DEBUG + struct irq_pin_list *entry; +#endif + +#ifdef APIC_MISMATCH_DEBUG + atomic_inc(&irq_mis_count); +#endif + spin_lock(&ioapic_lock); + __mask_and_edge_IO_APIC_irq(irq); +#ifdef APIC_LOCKUP_DEBUG + for (entry = irq_2_pin + irq;;) { + unsigned int reg; + + if (entry->pin == -1) + break; + reg = io_apic_read(entry->apic, 0x10 + entry->pin * 2); + if (reg & 0x00004000) + printk(KERN_CRIT "Aieee!!! Remote IRR" + " still set after unlock!\n"); + if (!entry->next) + break; + entry = irq_2_pin + entry->next; + } +#endif + __unmask_and_level_IO_APIC_irq(irq); + spin_unlock(&ioapic_lock); + } +} + +static void mask_and_ack_level_ioapic_irq (unsigned int irq) { /* nothing */ } + +static void set_ioapic_affinity (unsigned int irq, unsigned long mask) +{ + unsigned long flags; + /* + * Only the first 8 bits are valid. + */ + mask = mask << 24; + + spin_lock_irqsave(&ioapic_lock, flags); + __DO_ACTION(1, = mask, ) + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +/* + * Level and edge triggered IO-APIC interrupts need different handling, + * so we use two separate IRQ descriptors. Edge triggered IRQs can be + * handled with the level-triggered descriptor, but that one has slightly + * more overhead. Level-triggered interrupts cannot be handled with the + * edge-triggered handler, without risking IRQ storms and other ugly + * races. + */ + +static struct hw_interrupt_type ioapic_edge_irq_type = { + "IO-APIC-edge", + startup_edge_ioapic_irq, + shutdown_edge_ioapic_irq, + enable_edge_ioapic_irq, + disable_edge_ioapic_irq, + ack_edge_ioapic_irq, + end_edge_ioapic_irq, + set_ioapic_affinity, +}; + +static struct hw_interrupt_type ioapic_level_irq_type = { + "IO-APIC-level", + startup_level_ioapic_irq, + shutdown_level_ioapic_irq, + enable_level_ioapic_irq, + disable_level_ioapic_irq, + mask_and_ack_level_ioapic_irq, + end_level_ioapic_irq, + set_ioapic_affinity, +}; + +static inline void init_IO_APIC_traps(void) +{ + int irq; + + /* + * NOTE! The local APIC isn't very good at handling + * multiple interrupts at the same interrupt level. + * As the interrupt level is determined by taking the + * vector number and shifting that right by 4, we + * want to spread these out a bit so that they don't + * all fall in the same interrupt level. + * + * Also, we've got to be careful not to trash gate + * 0x80, because int 0x80 is hm, kind of importantish. ;) + */ + for (irq = 0; irq < NR_IRQS ; irq++) { + if (IO_APIC_IRQ(irq) && !IO_APIC_VECTOR(irq)) { + /* + * Hmm.. We don't have an entry for this, + * so default to an old-fashioned 8259 + * interrupt if we can.. + */ + if (irq < 16) + make_8259A_irq(irq); + else + /* Strange. Oh, well.. */ + irq_desc[irq].handler = &no_irq_type; + } + } +} + +static void enable_lapic_irq (unsigned int irq) +{ + unsigned long v; + + v = apic_read(APIC_LVT0); + apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED); +} + +static void disable_lapic_irq (unsigned int irq) +{ + unsigned long v; + + v = apic_read(APIC_LVT0); + apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED); +} + +static void ack_lapic_irq (unsigned int irq) +{ + ack_APIC_irq(); +} + +static void end_lapic_irq (unsigned int i) { /* nothing */ } + +static struct hw_interrupt_type lapic_irq_type = { + "local-APIC-edge", + NULL, /* startup_irq() not used for IRQ0 */ + NULL, /* shutdown_irq() not used for IRQ0 */ + enable_lapic_irq, + disable_lapic_irq, + ack_lapic_irq, + end_lapic_irq +}; + +static void enable_NMI_through_LVT0 (void * dummy) +{ + unsigned int v, ver; + + ver = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(ver); + v = APIC_DM_NMI; /* unmask and set to NMI */ + if (!APIC_INTEGRATED(ver)) /* 82489DX */ + v |= APIC_LVT_LEVEL_TRIGGER; + apic_write_around(APIC_LVT0, v); +} + +static void setup_nmi (void) +{ + /* + * Dirty trick to enable the NMI watchdog ... + * We put the 8259A master into AEOI mode and + * unmask on all local APICs LVT0 as NMI. + * + * The idea to use the 8259A in AEOI mode ('8259A Virtual Wire') + * is from Maciej W. Rozycki - so we do not have to EOI from + * the NMI handler or the timer interrupt. + */ + printk(KERN_INFO "activating NMI Watchdog ..."); + + smp_call_function(enable_NMI_through_LVT0, NULL, 1, 1); + enable_NMI_through_LVT0(NULL); + + printk(" done.\n"); +} + +/* + * This looks a bit hackish but it's about the only one way of sending + * a few INTA cycles to 8259As and any associated glue logic. ICR does + * not support the ExtINT mode, unfortunately. We need to send these + * cycles as some i82489DX-based boards have glue logic that keeps the + * 8259A interrupt line asserted until INTA. --macro + */ +static inline void unlock_ExtINT_logic(void) +{ + int pin, i; + struct IO_APIC_route_entry entry0, entry1; + unsigned char save_control, save_freq_select; + unsigned long flags; + + pin = find_isa_irq_pin(8, mp_INT); + if (pin == -1) + return; + + spin_lock_irqsave(&ioapic_lock, flags); + *(((int *)&entry0) + 1) = io_apic_read(0, 0x11 + 2 * pin); + *(((int *)&entry0) + 0) = io_apic_read(0, 0x10 + 2 * pin); + spin_unlock_irqrestore(&ioapic_lock, flags); + clear_IO_APIC_pin(0, pin); + + memset(&entry1, 0, sizeof(entry1)); + + entry1.dest_mode = 0; /* physical delivery */ + entry1.mask = 0; /* unmask IRQ now */ + entry1.dest.physical.physical_dest = hard_smp_processor_id(); + entry1.delivery_mode = dest_ExtINT; + entry1.polarity = entry0.polarity; + entry1.trigger = 0; + entry1.vector = 0; + + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry1) + 1)); + io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry1) + 0)); + spin_unlock_irqrestore(&ioapic_lock, flags); + + save_control = CMOS_READ(RTC_CONTROL); + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); + CMOS_WRITE((save_freq_select & ~RTC_RATE_SELECT) | 0x6, + RTC_FREQ_SELECT); + CMOS_WRITE(save_control | RTC_PIE, RTC_CONTROL); + + i = 100; + while (i-- > 0) { + mdelay(10); + if ((CMOS_READ(RTC_INTR_FLAGS) & RTC_PF) == RTC_PF) + i -= 10; + } + + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + clear_IO_APIC_pin(0, pin); + + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry0) + 1)); + io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry0) + 0)); + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +/* + * This code may look a bit paranoid, but it's supposed to cooperate with + * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ + * is so screwy. Thanks to Brian Perkins for testing/hacking this beast + * fanatically on his truly buggy board. + */ +static inline void check_timer(void) +{ + extern int timer_ack; + int pin1, pin2; + int vector; + + /* + * get/set the timer IRQ vector: + */ + disable_8259A_irq(0); + vector = assign_irq_vector(0); + set_intr_gate(vector, interrupt[0]); + + /* + * Subtle, code in do_timer_interrupt() expects an AEOI + * mode for the 8259A whenever interrupts are routed + * through I/O APICs. Also IRQ0 has to be enabled in + * the 8259A which implies the virtual wire has to be + * disabled in the local APIC. + */ + apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); + init_8259A(1); + timer_ack = 1; + enable_8259A_irq(0); + + pin1 = find_isa_irq_pin(0, mp_INT); + pin2 = find_isa_irq_pin(0, mp_ExtINT); + + printk(KERN_INFO "..TIMER: vector=0x%02X pin1=%d pin2=%d\n", vector, pin1, pin2); + + if (pin1 != -1) { + /* + * Ok, does IRQ0 through the IOAPIC work? + */ + unmask_IO_APIC_irq(0); + if (timer_irq_works()) { + if (nmi_watchdog == NMI_IO_APIC) { + disable_8259A_irq(0); + setup_nmi(); + enable_8259A_irq(0); + check_nmi_watchdog(); + } + return; + } + clear_IO_APIC_pin(0, pin1); + printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"); + } + + printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... "); + if (pin2 != -1) { + printk("\n..... (found pin %d) ...", pin2); + /* + * legacy devices should be connected to IO APIC #0 + */ + setup_ExtINT_IRQ0_pin(pin2, vector); + if (timer_irq_works()) { + printk("works.\n"); + if (nmi_watchdog == NMI_IO_APIC) { + setup_nmi(); + check_nmi_watchdog(); + } + return; + } + /* + * Cleanup, just in case ... + */ + clear_IO_APIC_pin(0, pin2); + } + printk(" failed.\n"); + + if (nmi_watchdog) { + printk(KERN_WARNING "timer doesnt work through the IO-APIC - disabling NMI Watchdog!\n"); + nmi_watchdog = 0; + } + + printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ..."); + + disable_8259A_irq(0); + irq_desc[0].handler = &lapic_irq_type; + apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */ + enable_8259A_irq(0); + + if (timer_irq_works()) { + printk(" works.\n"); + return; + } + apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector); + printk(" failed.\n"); + + printk(KERN_INFO "...trying to set up timer as ExtINT IRQ..."); + + init_8259A(0); + make_8259A_irq(0); + apic_write_around(APIC_LVT0, APIC_DM_EXTINT); + + unlock_ExtINT_logic(); + + if (timer_irq_works()) { + printk(" works.\n"); + return; + } + printk(" failed :(.\n"); + panic("IO-APIC + timer doesn't work! pester mingo@redhat.com"); +} + +/* + * + * IRQ's that are handled by the old PIC in all cases: + * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. + * Linux doesn't really care, as it's not actually used + * for any interrupt handling anyway. + * - There used to be IRQ13 here as well, but all + * MPS-compliant must not use it for FPU coupling and we + * want to use exception 16 anyway. And there are + * systems who connect it to an I/O APIC for other uses. + * Thus we don't mark it special any longer. + * + * Additionally, something is definitely wrong with irq9 + * on PIIX4 boards. + */ +#define PIC_IRQS (1<<2) + +void __init setup_IO_APIC(void) +{ + enable_IO_APIC(); + + io_apic_irqs = ~PIC_IRQS; + printk("ENABLING IO-APIC IRQs\n"); + + /* + * Set up the IO-APIC IRQ routing table by parsing the MP-BIOS + * mptable: + */ + setup_ioapic_ids_from_mpc(); + sync_Arb_IDs(); + setup_IO_APIC_irqs(); + init_IO_APIC_traps(); + check_timer(); + print_IO_APIC(); +} diff -Nru a/arch/x86_64/kernel/ioport.c b/arch/x86_64/kernel/ioport.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/ioport.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,114 @@ +/* + * linux/arch/i386/kernel/ioport.c + * + * This contains the io-permission bitmap code - written by obz, with changes + * by Linus. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ +static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value) +{ + int mask; + unsigned long *bitmap_base = bitmap + (base >> 6); + unsigned short low_index = base & 0x3f; + int length = low_index + extent; + + if (low_index != 0) { + mask = (~0 << low_index); + if (length < 64) + mask &= ~(~0 << length); + if (new_value) + *bitmap_base++ |= mask; + else + *bitmap_base++ &= ~mask; + length -= 32; + } + + mask = (new_value ? ~0 : 0); + while (length >= 64) { + *bitmap_base++ = mask; + length -= 64; + } + + if (length > 0) { + mask = ~(~0 << length); + if (new_value) + *bitmap_base++ |= mask; + else + *bitmap_base++ &= ~mask; + } +} + +/* + * this changes the io permissions bitmap in the current task. + */ +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) +{ + struct thread_struct * t = ¤t->thread; + struct tss_struct * tss = init_tss + smp_processor_id(); + + if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32)) + return -EINVAL; + if (turn_on && !capable(CAP_SYS_RAWIO)) + return -EPERM; + /* + * If it's the first ioperm() call in this thread's lifetime, set the + * IO bitmap up. ioperm() is much less timing critical than clone(), + * this is why we delay this operation until now: + */ + if (!t->ioperm) { + /* + * just in case ... + */ + memset(t->io_bitmap,0xff,(IO_BITMAP_SIZE+1)*4); + t->ioperm = 1; + /* + * this activates it in the TSS + */ + tss->io_map_base = IO_BITMAP_OFFSET; + } + + /* + * do it in the per-thread copy and in the TSS ... + */ + set_bitmap((unsigned long *) t->io_bitmap, from, num, !turn_on); + set_bitmap((unsigned long *) tss->io_bitmap, from, num, !turn_on); + + return 0; +} + +/* + * sys_iopl has to be used when you want to access the IO ports + * beyond the 0x3ff range: to get the full 65536 ports bitmapped + * you'd need 8kB of bitmaps/process, which is a bit excessive. + * + * Here we just change the eflags value on the stack: we allow + * only the super-user to do it. This depends on the stack-layout + * on system-call entry - see also fork() and the signal handling + * code. + */ + +asmlinkage long sys_iopl(unsigned int level, struct pt_regs regs) +{ + unsigned int old = (regs.eflags >> 12) & 3; + + if (level > 3) + return -EINVAL; + /* Trying to gain more privileges? */ + if (level > old) { + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + } + regs.eflags = (regs.eflags & 0xffffffffffffcfff) | (level << 12); + return 0; +} diff -Nru a/arch/x86_64/kernel/irq.c b/arch/x86_64/kernel/irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/irq.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1198 @@ +/* + * linux/arch/x86_64/kernel/irq.c + * + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +/* + * (mostly architecture independent, will move to kernel/irq.c in 2.5.) + * + * IRQs are in fact implemented a bit like signal handlers for the kernel. + * Naturally it's not a 1:1 relation, but there are similarities. + */ + +#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 + + + +/* + * Linux has a controller-independent x86 interrupt architecture. + * every controller has a 'controller-template', that is used + * by the main code to do the right thing. Each driver-visible + * interrupt source is transparently wired to the apropriate + * controller. Thus drivers need not be aware of the + * interrupt-controller. + * + * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, + * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. + * (IO-APICs assumed to be messaging to Pentium local-APICs) + * + * the code is designed to be easily extended with new/different + * interrupt controllers, without having to do assembly magic. + */ + +/* + * Controller mappings for all interrupt sources: + */ +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = + { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}}; + +static void register_irq_proc (unsigned int irq); + +/* + * Special irq handlers. + */ + +void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } + +/* + * Generic no controller code + */ + +static void enable_none(unsigned int irq) { } +static unsigned int startup_none(unsigned int irq) { return 0; } +static void disable_none(unsigned int irq) { } +static void ack_none(unsigned int irq) +{ +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves, it doesnt deserve + * a generic callback i think. + */ +#if CONFIG_X86 + printk("unexpected IRQ trap at vector %02x\n", irq); +#ifdef CONFIG_X86_LOCAL_APIC + /* + * Currently unexpected vectors happen only on SMP and APIC. + * We _must_ ack these because every local APIC has only N + * irq slots per priority level, and a 'hanging, unacked' IRQ + * holds up an irq slot - in excessive cases (when multiple + * unexpected vectors occur) that might lock up the APIC + * completely. + */ + ack_APIC_irq(); +#endif +#endif +} + +/* startup is the same as "enable", shutdown is same as "disable" */ +#define shutdown_none disable_none +#define end_none enable_none + +struct hw_interrupt_type no_irq_type = { + "none", + startup_none, + shutdown_none, + enable_none, + disable_none, + ack_none, + end_none +}; + +atomic_t irq_err_count; +#ifdef CONFIG_X86_IO_APIC +#ifdef APIC_MISMATCH_DEBUG +atomic_t irq_mis_count; +#endif +#endif + +/* + * Generic, controller-independent functions: + */ + +int show_interrupts(struct seq_file *p, void *v) +{ + int i, j; + struct irqaction * action; + + seq_printf(p, " "); + for (j=0; jtypename); + seq_printf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + seq_printf(p, ", %s", action->name); + seq_putc(p, '\n'); + } + seq_printf(p, "NMI: "); + for (j = 0; j < smp_num_cpus; j++) + seq_printf(p, "%10u ", nmi_count(cpu_logical_map(j))); + seq_putc(p, '\n'); +#if CONFIG_X86_LOCAL_APIC + seq_printf(p, "LOC: "); + for (j = 0; j < smp_num_cpus; j++) + seq_printf(p, "%10u ", apic_timer_irqs[cpu_logical_map(j)]); + seq_putc(p, '\n'); +#endif + seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); +#ifdef CONFIG_X86_IO_APIC +#ifdef APIC_MISMATCH_DEBUG + seq_printf(p, "MIS: %10u\n", atomic_read(&irq_mis_count)); +#endif +#endif + return 0; +} + +/* + * Global interrupt locks for SMP. Allow interrupts to come in on any + * CPU, yet make cli/sti act globally to protect critical regions.. + */ + +#ifdef CONFIG_SMP +unsigned char global_irq_holder = NO_PROC_ID; +unsigned volatile long global_irq_lock; /* pendantic: long for set_bit --RR */ + +extern void show_stack(unsigned long* esp); + + +/* XXX: this unfortunately doesn't support irqstacks currently, should check the other PDAs */ +static void show(char * str) +{ + int i; + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [",irqs_running()); + for(i=0;i < smp_num_cpus;i++) + printk(" %d",local_irq_count(i)); + printk(" ]\nbh: %d [",spin_is_locked(&global_bh_lock) ? 1 : 0); + for(i=0;i < smp_num_cpus;i++) + printk(" %d",local_bh_count(i)); + + printk(" ]\nStack dumps:"); + for(i = 0; i < smp_num_cpus; i++) { + unsigned long esp; + if (i == cpu) + continue; + printk("\nCPU %d:",i); + esp = init_tss[i].rsp0; + if (!esp) { + /* tss->esp0 is set to NULL in cpu_init(), + * it's initialized when the cpu returns to user + * space. -- manfreds + */ + printk(" "); + continue; + } + esp &= ~(THREAD_SIZE-1); + esp += sizeof(struct thread_info); + show_stack((void*)esp); + } + printk("\nCPU %d:",cpu); + show_stack(NULL); + printk("\n"); +} + +#define MAXCOUNT 100000000 + +/* + * I had a lockup scenario where a tight loop doing + * spin_unlock()/spin_lock() on CPU#1 was racing with + * spin_lock() on CPU#0. CPU#0 should have noticed spin_unlock(), but + * apparently the spin_unlock() information did not make it + * through to CPU#0 ... nasty, is this by design, do we have to limit + * 'memory update oscillation frequency' artificially like here? + * + * Such 'high frequency update' races can be avoided by careful design, but + * some of our major constructs like spinlocks use similar techniques, + * it would be nice to clarify this issue. Set this define to 0 if you + * want to check whether your system freezes. I suspect the delay done + * by SYNC_OTHER_CORES() is in correlation with 'snooping latency', but + * i thought that such things are guaranteed by design, since we use + * the 'LOCK' prefix. + */ +#define SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND 0 + +#if SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND +# define SYNC_OTHER_CORES(x) udelay(x+1) +#else +/* + * We have to allow irqs to arrive between __sti and __cli + */ +# define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop") +#endif + +static inline void wait_on_irq(int cpu) +{ + int count = MAXCOUNT; + + for (;;) { + + /* + * Wait until all interrupts are gone. Wait + * for bottom half handlers unless we're + * already executing in one.. + */ + if (!irqs_running()) + if (local_bh_count(cpu) || !spin_is_locked(&global_bh_lock)) + break; + + /* Duh, we have to loop. Release the lock to avoid deadlocks */ + clear_bit(0,&global_irq_lock); + + for (;;) { + if (!--count) { + show("wait_on_irq"); + count = ~0; + } + __sti(); + SYNC_OTHER_CORES(cpu); + __cli(); + if (irqs_running()) + continue; + if (global_irq_lock) + continue; + if (!local_bh_count(cpu) && spin_is_locked(&global_bh_lock)) + continue; + if (!test_and_set_bit(0,&global_irq_lock)) + break; + } + } +} + +/* + * This is called when we want to synchronize with + * interrupts. We may for example tell a device to + * stop sending interrupts: but to make sure there + * are no interrupts that are executing on another + * CPU we need to call this function. + */ +void synchronize_irq(void) +{ + if (irqs_running()) { + /* Stupid approach */ + cli(); + sti(); + } +} + +static inline void get_irqlock(int cpu) +{ + if (test_and_set_bit(0,&global_irq_lock)) { + /* do we already hold the lock? */ + if ((unsigned char) cpu == global_irq_holder) + return; + /* Uhhuh.. Somebody else got it. Wait.. */ + do { + do { + rep_nop(); + } while (test_bit(0,&global_irq_lock)); + } while (test_and_set_bit(0,&global_irq_lock)); + } + /* + * We also to make sure that nobody else is running + * in an interrupt context. + */ + wait_on_irq(cpu); + + /* + * Ok, finally.. + */ + global_irq_holder = cpu; +} + +#define EFLAGS_IF_SHIFT 9 + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void __global_cli(void) +{ + unsigned int flags; + + __save_flags(flags); + if (flags & (1 << EFLAGS_IF_SHIFT)) { + int cpu = smp_processor_id(); + __cli(); + if (!local_irq_count(cpu)) + get_irqlock(cpu); + } +} + +void __global_sti(void) +{ + int cpu = smp_processor_id(); + + if (!local_irq_count(cpu)) + release_irqlock(cpu); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long __global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + int cpu = smp_processor_id(); + + __save_flags(flags); + local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!local_irq_count(cpu)) { + if (local_enabled) + retval = 1; + if (global_irq_holder == cpu) + retval = 0; + } + return retval; +} + +void __global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + } +} + +#endif + +/* + * This should really return information about whether + * we should do bottom half handling etc. Right now we + * end up _always_ checking the bottom half, which is a + * waste of time and is not what some drivers would + * prefer. + */ +int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) +{ + int status; + + irq_enter(0, irq); + + status = 1; /* Force the "do bottom halves" bit */ + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + + irq_exit(0, irq); + + return status; +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + +/** + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables and Enables are + * nested. + * Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ + +inline void disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * disable_irq - disable an irq and wait for completion + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Enables and Disables are + * nested. + * This function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ + +void disable_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + + if (!local_irq_count(smp_processor_id())) { + do { + barrier(); + cpu_relax(); + } while (irq_desc[irq].status & IRQ_INPROGRESS); + } +} + +/** + * enable_irq - enable handling of an irq + * @irq: Interrupt to enable + * + * Undoes the effect of one call to disable_irq(). If this + * matches the last disable, processing of interrupts on this + * IRQ line is re-enabled. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq(%u) unbalanced from %p\n", irq, + __builtin_return_address(0)); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +asmlinkage unsigned int do_IRQ(struct pt_regs *regs) +{ + /* + * We ack quickly, we don't want the irq controller + * thinking we're snobs just because some other CPU has + * disabled global interrupts (we have already done the + * INT_ACK cycles, it's too late to try to pretend to the + * controller that we aren't taking the interrupt). + * + * 0 return value means that this irq is already being + * handled by some other CPU. (or is disabled) + */ + int irq = regs->orig_rax & 0xff; /* high bits used in ret_from_ code */ + int cpu = smp_processor_id(); + irq_desc_t *desc = irq_desc + irq; + struct irqaction * action; + unsigned int status; + + kstat.irqs[cpu][irq]++; + spin_lock(&desc->lock); + desc->handler->ack(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + status &= ~IRQ_PENDING; /* we commit to handling */ + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (!action) + goto out; + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); + handle_IRQ_event(irq, regs, action); + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); + + if (softirq_pending(cpu)) + do_softirq(); + return 1; +} + +/** + * request_irq - allocate an interrupt line + * @irq: Interrupt line to allocate + * @handler: Function to be called when the IRQ occurs + * @irqflags: Interrupt type flags + * @devname: An ascii name for the claiming device + * @dev_id: A cookie passed back to the handler function + * + * This call allocates interrupt resources and enables the + * interrupt line and IRQ handling. From the point this + * call is made your handler function may be invoked. Since + * your handler function must clear any interrupt the board + * raises, you must take care both to initialise your hardware + * and to set up the interrupt handler in the right order. + * + * Dev_id must be globally unique. Normally the address of the + * device data structure is used as the cookie. Since the handler + * receives this value it makes sense to use it. + * + * If your interrupt is shared you must pass a non NULL dev_id + * as this is required when freeing the interrupt. + * + * Flags: + * + * SA_SHIRQ Interrupt is shared + * + * SA_INTERRUPT Disable local interrupts while processing + * + * SA_SAMPLE_RANDOM The interrupt can be used for entropy + * + */ + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + +#if 1 + /* + * Sanity-check: shared interrupts should REALLY pass in + * a real dev-ID, otherwise we'll have trouble later trying + * to figure out which interrupt is which (messes up the + * interrupt freeing logic etc). + */ + if (irqflags & SA_SHIRQ) { + if (!dev_id) + printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]); + } +#endif + + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + return -EINVAL; + + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + return retval; +} + +/** + * free_irq - free an interrupt + * @irq: Interrupt line to free + * @dev_id: Device identity to free + * + * Remove an interrupt handler. The handler is removed and if the + * interrupt line is no longer in use by any driver it is disabled. + * On a shared IRQ the caller must ensure the interrupt is disabled + * on the card it drives before calling this function. The function + * does not return until any executing interrupts for this IRQ + * have completed. + * + * This function may be called from interrupt context. + * + * Bugs: Attempting to free an irq in a handler for the same irq hangs + * the machine. + */ + +void free_irq(unsigned int irq, void *dev_id) +{ + irq_desc_t *desc; + struct irqaction **p; + unsigned long flags; + + if (irq >= NR_IRQS) + return; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + desc->handler->shutdown(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + +#ifdef CONFIG_SMP + /* Wait to make sure it's not being used on another CPU */ + while (desc->status & IRQ_INPROGRESS) { + barrier(); + cpu_relax(); + } +#endif + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + return; + } +} + +/* + * IRQ autodetection code.. + * + * This depends on the fact that any interrupt that + * comes in on to an unassigned handler will get stuck + * with "IRQ_WAITING" cleared and the interrupt + * disabled. + */ + +static DECLARE_MUTEX(probe_sem); + +/** + * probe_irq_on - begin an interrupt autodetect + * + * Commence probing for an interrupt. The interrupts are scanned + * and a mask of potential interrupt lines is returned. + * + */ + +unsigned long probe_irq_on(void) +{ + unsigned int i; + irq_desc_t *desc; + unsigned long val; + unsigned long delay; + + down(&probe_sem); + /* + * something may have generated an irq long ago and we want to + * flush such a longstanding irq before considering it as spurious. + */ + for (i = NR_IRQS-1; i > 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!irq_desc[i].action) + irq_desc[i].handler->startup(i); + spin_unlock_irq(&desc->lock); + } + + /* Wait for longstanding interrupts to trigger. */ + for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) + /* about 20ms delay */ synchronize_irq(); + + /* + * enable any unassigned irqs + * (we must startup again here because if a longstanding irq + * happened in the previous stage, it may have masked itself) + */ + for (i = NR_IRQS-1; i > 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!desc->action) { + desc->status |= IRQ_AUTODETECT | IRQ_WAITING; + if (desc->handler->startup(i)) + desc->status |= IRQ_PENDING; + } + spin_unlock_irq(&desc->lock); + } + + /* + * Wait for spurious interrupts to trigger + */ + for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) + /* about 100ms delay */ synchronize_irq(); + + /* + * Now filter out any obviously spurious interrupts + */ + val = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + /* It triggered already - consider it spurious. */ + if (!(status & IRQ_WAITING)) { + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } else + if (i < 32) + val |= 1 << i; + } + spin_unlock_irq(&desc->lock); + } + + return val; +} + +/* + * Return a mask of triggered interrupts (this + * can handle only legacy ISA interrupts). + */ + +/** + * probe_irq_mask - scan a bitmap of interrupt lines + * @val: mask of interrupts to consider + * + * Scan the ISA bus interrupt lines and return a bitmap of + * active interrupts. The interrupt probe logic state is then + * returned to its previous value. + * + * Note: we need to scan all the irq's even though we will + * only return ISA irq numbers - just so that we reset them + * all to a known state. + */ +unsigned int probe_irq_mask(unsigned long val) +{ + int i; + unsigned int mask; + + mask = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (i < 16 && !(status & IRQ_WAITING)) + mask |= 1 << i; + + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + up(&probe_sem); + + return mask & val; +} + +/* + * Return the one interrupt that triggered (this can + * handle any interrupt source). + */ + +/** + * probe_irq_off - end an interrupt autodetect + * @val: mask of potential interrupts (unused) + * + * Scans the unused interrupt lines and returns the line which + * appears to have triggered the interrupt. If no interrupt was + * found then zero is returned. If more than one interrupt is + * found then minus the first candidate is returned to indicate + * their is doubt. + * + * The interrupt probe logic state is returned to its previous + * value. + * + * BUGS: When used in a module (which arguably shouldnt happen) + * nothing prevents two IRQ probe callers from overlapping. The + * results of this are non-optimal. + */ + +int probe_irq_off(unsigned long val) +{ + int i, irq_found, nr_irqs; + + nr_irqs = 0; + irq_found = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (!(status & IRQ_WAITING)) { + if (!nr_irqs) + irq_found = i; + nr_irqs++; + } + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + up(&probe_sem); + + if (nr_irqs > 1) + irq_found = -irq_found; + return irq_found; +} + +/* this was setup_x86_irq but it seems pretty generic */ +int setup_irq(unsigned int irq, struct irqaction * new) +{ + int shared = 0; + unsigned long flags; + struct irqaction *old, **p; + irq_desc_t *desc = irq_desc + irq; + + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); + desc->handler->startup(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); + return 0; +} + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; + +#define HEX_DIGITS 8 + +static unsigned int parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; + int i; + + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; + } +out: + *ret = value; + return 0; +} + +#if CONFIG_SMP + +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", irq_affinity[(long)data]); +} + +static int irq_affinity_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (long) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_online_map)) + return -EINVAL; + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +#endif + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + +#if CONFIG_SMP + { + struct proc_dir_entry *entry; + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + if (entry) { + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + } + + smp_affinity_entry[irq] = entry; + } +#endif +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + if (!entry) + return; + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) + register_irq_proc(i); +} + diff -Nru a/arch/x86_64/kernel/ldt.c b/arch/x86_64/kernel/ldt.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/ldt.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,177 @@ +/* + * linux/kernel/ldt.c + * + * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds + * Copyright (C) 1999 Ingo Molnar + */ + +/* + * FIXME: forbid code segment setting for 64bit mode. doesn't work with SYSCALL + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * read_ldt() is not really atomic - this is not a problem since + * synchronization of reads and writes done to the LDT has to be + * assured by user-space anyway. Writes are atomic, to protect + * the security checks done on new descriptors. + */ +static int read_ldt(void * ptr, unsigned long bytecount) +{ + int err; + unsigned long size; + struct mm_struct * mm = current->mm; + + err = 0; + if (!mm->context.segments) + goto out; + + size = LDT_ENTRIES*LDT_ENTRY_SIZE; + if (size > bytecount) + size = bytecount; + + err = size; + if (copy_to_user(ptr, mm->context.segments, size)) + err = -EFAULT; +out: + return err; +} + +static int read_default_ldt(void * ptr, unsigned long bytecount) +{ + int err; + unsigned long size; + void *address; + + err = 0; + address = &default_ldt[0]; + size = sizeof(struct desc_struct); + if (size > bytecount) + size = bytecount; + + err = size; + if (copy_to_user(ptr, address, size)) + err = -EFAULT; + + return err; +} + +static int write_ldt(void * ptr, unsigned long bytecount, int oldmode) +{ + struct mm_struct * mm = current->mm; + __u32 entry_1, entry_2, *lp; + int error; + struct modify_ldt_ldt_s ldt_info; + + error = -EINVAL; + if (bytecount != sizeof(ldt_info)) + goto out; + error = -EFAULT; + if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info))) + goto out; + + error = -EINVAL; + if (ldt_info.entry_number >= LDT_ENTRIES) + goto out; + if (ldt_info.contents == 3) { + if (oldmode) + goto out; + if (ldt_info.seg_not_present == 0) + goto out; + } + + current->thread.fsindex = 0; + current->thread.gsindex = 0; + + /* + * the GDT index of the LDT is allocated dynamically, and is + * limited by MAX_LDT_DESCRIPTORS. + */ + down_write(&mm->mmap_sem); + if (!mm->context.segments) { + void * segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); + error = -ENOMEM; + if (!segments) + goto out_unlock; + memset(segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE); + wmb(); + mm->context.segments = segments; + mm->context.cpuvalid = 1UL << smp_processor_id(); + load_LDT(mm); + } + + lp = (__u32 *) ((ldt_info.entry_number << 3) + (char *) mm->context.segments); + + /* Allow LDTs to be cleared by the user. */ + if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { + if (oldmode || + (ldt_info.contents == 0 && + ldt_info.read_exec_only == 1 && + ldt_info.seg_32bit == 0 && + ldt_info.limit_in_pages == 0 && + ldt_info.seg_not_present == 1 && + ldt_info.useable == 0 )) { + entry_1 = 0; + entry_2 = 0; + goto install; + } + } + + entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) | + (ldt_info.limit & 0x0ffff); + entry_2 = (ldt_info.base_addr & 0xff000000) | + ((ldt_info.base_addr & 0x00ff0000) >> 16) | + (ldt_info.limit & 0xf0000) | + ((ldt_info.read_exec_only ^ 1) << 9) | + (ldt_info.contents << 10) | + ((ldt_info.seg_not_present ^ 1) << 15) | + (ldt_info.seg_32bit << 22) | + (ldt_info.limit_in_pages << 23) | + 0x7000; + if (!oldmode) + entry_2 |= (ldt_info.useable << 20); + + /* Install the new entry ... */ +install: + *lp = entry_1; + *(lp+1) = entry_2; + error = 0; + +out_unlock: + up_write(&mm->mmap_sem); +out: + return error; +} + +asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount) +{ + int ret = -ENOSYS; + + switch (func) { + case 0: + ret = read_ldt(ptr, bytecount); + break; + case 1: + ret = write_ldt(ptr, bytecount, 1); + break; + case 2: + ret = read_default_ldt(ptr, bytecount); + break; + case 0x11: + ret = write_ldt(ptr, bytecount, 0); + break; + } + return ret; +} diff -Nru a/arch/x86_64/kernel/mpparse.c b/arch/x86_64/kernel/mpparse.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/mpparse.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,670 @@ +/* + * Intel Multiprocessor Specificiation 1.1 and 1.4 + * compliant MP-table parsing routines. + * + * (c) 1995 Alan Cox, Building #3 + * (c) 1998, 1999, 2000 Ingo Molnar + * + * Fixes + * Erich Boleyn : MP v1.4 and additional changes. + * Alan Cox : Added EBDA scanning + * Ingo Molnar : various cleanups and rewrites + * Maciej W. Rozycki : Bits for default MP configurations + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Have we found an MP table */ +int smp_found_config = 0; + +/* + * Various Linux-internal data structures created from the + * MP-table. + */ +int apic_version [MAX_APICS]; +int mp_bus_id_to_type [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; +int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; +int mp_current_pci_id = 0; +/* I/O APIC entries */ +struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS]; + +/* # of MP IRQ source entries */ +struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; + +/* MP IRQ source entries */ +int mp_irq_entries; + +int nr_ioapics; +int pic_mode; +unsigned long mp_lapic_addr = 0; + + + +/* Processor that is doing the boot up */ +unsigned int boot_cpu_id = -1U; +/* Internal processor count */ +static unsigned int num_processors = 0; + +/* Bitmask of physically existing CPUs */ +unsigned long phys_cpu_present_map = 0; + +/* + * Intel MP BIOS table parsing routines: + */ + +#ifndef CONFIG_X86_VISWS_APIC +/* + * Checksum an MP configuration block. + */ + +static int __init mpf_checksum(unsigned char *mp, int len) +{ + int sum = 0; + + while (len--) + sum += *mp++; + + return sum & 0xFF; +} + +/* + * Processor encoding in an MP configuration block + */ + +static char __init *mpc_family(int family,int model) +{ + static char n[32]; + static char *model_defs[]= + { + "80486DX","80486DX", + "80486SX","80486DX/2 or 80487", + "80486SL","80486SX/2", + "Unknown","80486DX/2-WB", + "80486DX/4","80486DX/4-WB" + }; + + switch (family) { + case 0x04: + if (model < 10) + return model_defs[model]; + break; + + case 0x05: + return("Pentium(tm)"); + + case 0x06: + return("Pentium(tm) Pro"); + + case 0x0F: + if (model == 0x0F) + return("Special controller"); + } + sprintf(n,"Unknown CPU [%d:%d]",family, model); + return n; +} + +static void __init MP_processor_info (struct mpc_config_processor *m) +{ + int ver; + + if (!(m->mpc_cpuflag & CPU_ENABLED)) + return; + + printk("Processor #%d %s APIC version %d\n", + m->mpc_apicid, + mpc_family( (m->mpc_cpufeature & CPU_FAMILY_MASK)>>8 , + (m->mpc_cpufeature & CPU_MODEL_MASK)>>4), + m->mpc_apicver); + + if (m->mpc_featureflag&(1<<0)) + Dprintk(" Floating point unit present.\n"); + if (m->mpc_featureflag&(1<<7)) + Dprintk(" Machine Exception supported.\n"); + if (m->mpc_featureflag&(1<<8)) + Dprintk(" 64 bit compare & exchange supported.\n"); + if (m->mpc_featureflag&(1<<9)) + Dprintk(" Internal APIC present.\n"); + + if (m->mpc_cpuflag & CPU_BOOTPROCESSOR) { + Dprintk(" Bootup CPU\n"); + boot_cpu_id = m->mpc_apicid; + } + num_processors++; + + if (m->mpc_apicid > MAX_APICS) { + printk("Processor #%d INVALID. (Max ID: %d).\n", + m->mpc_apicid, MAX_APICS); + return; + } + ver = m->mpc_apicver; + + phys_cpu_present_map |= 1 << m->mpc_apicid; + /* + * Validate version + */ + if (ver == 0x0) { + printk("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->mpc_apicid); + ver = 0x10; + } + apic_version[m->mpc_apicid] = ver; +} + +static void __init MP_bus_info (struct mpc_config_bus *m) +{ + char str[7]; + + memcpy(str, m->mpc_bustype, 6); + str[6] = 0; + Dprintk("Bus #%d is %s\n", m->mpc_busid, str); + + if (strncmp(str, "ISA", 3) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_ISA; + } else if (strncmp(str, "EISA", 4) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_EISA; + } else if (strncmp(str, "PCI", 3) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_PCI; + mp_bus_id_to_pci_bus[m->mpc_busid] = mp_current_pci_id; + mp_current_pci_id++; + } else if (strncmp(str, "MCA", 3) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_MCA; + } else { + printk("Unknown bustype %s\n", str); + panic("cannot handle bus - mail to linux-smp@vger.kernel.org"); + } +} + +static void __init MP_ioapic_info (struct mpc_config_ioapic *m) +{ + if (!(m->mpc_flags & MPC_APIC_USABLE)) + return; + + printk("I/O APIC #%d Version %d at 0x%X.\n", + m->mpc_apicid, m->mpc_apicver, m->mpc_apicaddr); + if (nr_ioapics >= MAX_IO_APICS) { + printk("Max # of I/O APICs (%d) exceeded (found %d).\n", + MAX_IO_APICS, nr_ioapics); + panic("Recompile kernel with bigger MAX_IO_APICS!.\n"); + } + if (!m->mpc_apicaddr) { + printk(KERN_ERR "WARNING: bogus zero I/O APIC address" + " found in MP table, skipping!\n"); + return; + } + mp_ioapics[nr_ioapics] = *m; + nr_ioapics++; +} + +static void __init MP_intsrc_info (struct mpc_config_intsrc *m) +{ + mp_irqs [mp_irq_entries] = *m; + Dprintk("Int: type %d, pol %d, trig %d, bus %d," + " IRQ %02x, APIC ID %x, APIC INT %02x\n", + m->mpc_irqtype, m->mpc_irqflag & 3, + (m->mpc_irqflag >> 2) & 3, m->mpc_srcbus, + m->mpc_srcbusirq, m->mpc_dstapic, m->mpc_dstirq); + if (++mp_irq_entries == MAX_IRQ_SOURCES) + panic("Max # of irq sources exceeded!!\n"); +} + +static void __init MP_lintsrc_info (struct mpc_config_lintsrc *m) +{ + Dprintk("Lint: type %d, pol %d, trig %d, bus %d," + " IRQ %02x, APIC ID %x, APIC LINT %02x\n", + m->mpc_irqtype, m->mpc_irqflag & 3, + (m->mpc_irqflag >> 2) &3, m->mpc_srcbusid, + m->mpc_srcbusirq, m->mpc_destapic, m->mpc_destapiclint); + /* + * Well it seems all SMP boards in existence + * use ExtINT/LVT1 == LINT0 and + * NMI/LVT2 == LINT1 - the following check + * will show us if this assumptions is false. + * Until then we do not have to add baggage. + */ + if ((m->mpc_irqtype == mp_ExtINT) && + (m->mpc_destapiclint != 0)) + BUG(); + if ((m->mpc_irqtype == mp_NMI) && + (m->mpc_destapiclint != 1)) + BUG(); +} + +/* + * Read/parse the MPC + */ + +static int __init smp_read_mpc(struct mp_config_table *mpc) +{ + char str[16]; + int count=sizeof(*mpc); + unsigned char *mpt=((unsigned char *)mpc)+count; + + if (memcmp(mpc->mpc_signature,MPC_SIGNATURE,4)) { + panic("SMP mptable: bad signature [%c%c%c%c]!\n", + mpc->mpc_signature[0], + mpc->mpc_signature[1], + mpc->mpc_signature[2], + mpc->mpc_signature[3]); + return 0; + } + if (mpf_checksum((unsigned char *)mpc,mpc->mpc_length)) { + panic("SMP mptable: checksum error!\n"); + return 0; + } + if (mpc->mpc_spec!=0x01 && mpc->mpc_spec!=0x04) { + printk(KERN_ERR "SMP mptable: bad table version (%d)!!\n", + mpc->mpc_spec); + return 0; + } + if (!mpc->mpc_lapic) { + printk(KERN_ERR "SMP mptable: null local APIC address!\n"); + return 0; + } + memcpy(str,mpc->mpc_oem,8); + str[8]=0; + printk("OEM ID: %s ",str); + + memcpy(str,mpc->mpc_productid,12); + str[12]=0; + printk("Product ID: %s ",str); + + printk("APIC at: 0x%X\n",mpc->mpc_lapic); + + /* save the local APIC address, it might be non-default */ + mp_lapic_addr = mpc->mpc_lapic; + + /* + * Now process the configuration blocks. + */ + while (count < mpc->mpc_length) { + switch(*mpt) { + case MP_PROCESSOR: + { + struct mpc_config_processor *m= + (struct mpc_config_processor *)mpt; + MP_processor_info(m); + mpt += sizeof(*m); + count += sizeof(*m); + break; + } + case MP_BUS: + { + struct mpc_config_bus *m= + (struct mpc_config_bus *)mpt; + MP_bus_info(m); + mpt += sizeof(*m); + count += sizeof(*m); + break; + } + case MP_IOAPIC: + { + struct mpc_config_ioapic *m= + (struct mpc_config_ioapic *)mpt; + MP_ioapic_info(m); + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + case MP_INTSRC: + { + struct mpc_config_intsrc *m= + (struct mpc_config_intsrc *)mpt; + + MP_intsrc_info(m); + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + case MP_LINTSRC: + { + struct mpc_config_lintsrc *m= + (struct mpc_config_lintsrc *)mpt; + MP_lintsrc_info(m); + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + } + } + if (!num_processors) + printk(KERN_ERR "SMP mptable: no processors registered!\n"); + return num_processors; +} + +static int __init ELCR_trigger(unsigned int irq) +{ + unsigned int port; + + port = 0x4d0 + (irq >> 3); + return (inb(port) >> (irq & 7)) & 1; +} + +static void __init construct_default_ioirq_mptable(int mpc_default_type) +{ + struct mpc_config_intsrc intsrc; + int i; + int ELCR_fallback = 0; + + intsrc.mpc_type = MP_INTSRC; + intsrc.mpc_irqflag = 0; /* conforming */ + intsrc.mpc_srcbus = 0; + intsrc.mpc_dstapic = mp_ioapics[0].mpc_apicid; + + intsrc.mpc_irqtype = mp_INT; + + /* + * If true, we have an ISA/PCI system with no IRQ entries + * in the MP table. To prevent the PCI interrupts from being set up + * incorrectly, we try to use the ELCR. The sanity check to see if + * there is good ELCR data is very simple - IRQ0, 1, 2 and 13 can + * never be level sensitive, so we simply see if the ELCR agrees. + * If it does, we assume it's valid. + */ + if (mpc_default_type == 5) { + printk("ISA/PCI bus type with no IRQ information... falling back to ELCR\n"); + + if (ELCR_trigger(0) || ELCR_trigger(1) || ELCR_trigger(2) || ELCR_trigger(13)) + printk("ELCR contains invalid data... not using ELCR\n"); + else { + printk("Using ELCR to identify PCI interrupts\n"); + ELCR_fallback = 1; + } + } + + for (i = 0; i < 16; i++) { + switch (mpc_default_type) { + case 2: + if (i == 0 || i == 13) + continue; /* IRQ0 & IRQ13 not connected */ + /* fall through */ + default: + if (i == 2) + continue; /* IRQ2 is never connected */ + } + + if (ELCR_fallback) { + /* + * If the ELCR indicates a level-sensitive interrupt, we + * copy that information over to the MP table in the + * irqflag field (level sensitive, active high polarity). + */ + if (ELCR_trigger(i)) + intsrc.mpc_irqflag = 13; + else + intsrc.mpc_irqflag = 0; + } + + intsrc.mpc_srcbusirq = i; + intsrc.mpc_dstirq = i ? i : 2; /* IRQ0 to INTIN2 */ + MP_intsrc_info(&intsrc); + } + + intsrc.mpc_irqtype = mp_ExtINT; + intsrc.mpc_srcbusirq = 0; + intsrc.mpc_dstirq = 0; /* 8259A to INTIN0 */ + MP_intsrc_info(&intsrc); +} + +static inline void __init construct_default_ISA_mptable(int mpc_default_type) +{ + struct mpc_config_processor processor; + struct mpc_config_bus bus; + struct mpc_config_ioapic ioapic; + struct mpc_config_lintsrc lintsrc; + int linttypes[2] = { mp_ExtINT, mp_NMI }; + int i; + + /* + * local APIC has default address + */ + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; + + /* + * 2 CPUs, numbered 0 & 1. + */ + processor.mpc_type = MP_PROCESSOR; + /* Either an integrated APIC or a discrete 82489DX. */ + processor.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01; + processor.mpc_cpuflag = CPU_ENABLED; + processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) | + (boot_cpu_data.x86_model << 4) | + boot_cpu_data.x86_mask; + processor.mpc_featureflag = boot_cpu_data.x86_capability[0]; + processor.mpc_reserved[0] = 0; + processor.mpc_reserved[1] = 0; + for (i = 0; i < 2; i++) { + processor.mpc_apicid = i; + MP_processor_info(&processor); + } + + bus.mpc_type = MP_BUS; + bus.mpc_busid = 0; + switch (mpc_default_type) { + default: + printk("???\nUnknown standard configuration %d\n", + mpc_default_type); + /* fall through */ + case 1: + case 5: + memcpy(bus.mpc_bustype, "ISA ", 6); + break; + case 2: + case 6: + case 3: + memcpy(bus.mpc_bustype, "EISA ", 6); + break; + case 4: + case 7: + memcpy(bus.mpc_bustype, "MCA ", 6); + } + MP_bus_info(&bus); + if (mpc_default_type > 4) { + bus.mpc_busid = 1; + memcpy(bus.mpc_bustype, "PCI ", 6); + MP_bus_info(&bus); + } + + ioapic.mpc_type = MP_IOAPIC; + ioapic.mpc_apicid = 2; + ioapic.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01; + ioapic.mpc_flags = MPC_APIC_USABLE; + ioapic.mpc_apicaddr = 0xFEC00000; + MP_ioapic_info(&ioapic); + + /* + * We set up most of the low 16 IO-APIC pins according to MPS rules. + */ + construct_default_ioirq_mptable(mpc_default_type); + + lintsrc.mpc_type = MP_LINTSRC; + lintsrc.mpc_irqflag = 0; /* conforming */ + lintsrc.mpc_srcbusid = 0; + lintsrc.mpc_srcbusirq = 0; + lintsrc.mpc_destapic = MP_APIC_ALL; + for (i = 0; i < 2; i++) { + lintsrc.mpc_irqtype = linttypes[i]; + lintsrc.mpc_destapiclint = i; + MP_lintsrc_info(&lintsrc); + } +} + +static struct intel_mp_floating *mpf_found; + +/* + * Scan the memory blocks for an SMP configuration block. + */ +void __init get_smp_config (void) +{ + struct intel_mp_floating *mpf = mpf_found; + printk("Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification); + if (mpf->mpf_feature2 & (1<<7)) { + printk(" IMCR and PIC compatibility mode.\n"); + pic_mode = 1; + } else { + printk(" Virtual Wire compatibility mode.\n"); + pic_mode = 0; + } + + /* + * Now see if we need to read further. + */ + if (mpf->mpf_feature1 != 0) { + + printk("Default MP configuration #%d\n", mpf->mpf_feature1); + construct_default_ISA_mptable(mpf->mpf_feature1); + + } else if (mpf->mpf_physptr) { + + /* + * Read the physical hardware table. Anything here will + * override the defaults. + */ + if (!smp_read_mpc((void *)(unsigned long)mpf->mpf_physptr)) { + smp_found_config = 0; + printk(KERN_ERR "BIOS bug, MP table errors detected!...\n"); + printk(KERN_ERR "... disabling SMP support. (tell your hw vendor)\n"); + return; + } + /* + * If there are no explicit MP IRQ entries, then we are + * broken. We set up most of the low 16 IO-APIC pins to + * ISA defaults and hope it will work. + */ + if (!mp_irq_entries) { + struct mpc_config_bus bus; + + printk("BIOS bug, no explicit IRQ entries, using default mptable. (tell your hw vendor)\n"); + + bus.mpc_type = MP_BUS; + bus.mpc_busid = 0; + memcpy(bus.mpc_bustype, "ISA ", 6); + MP_bus_info(&bus); + + construct_default_ioirq_mptable(0); + } + + } else + BUG(); + + printk("Processors: %d\n", num_processors); + /* + * Only use the first configuration found. + */ +} + +static int __init smp_scan_config (unsigned long base, unsigned long length) +{ + unsigned long *bp = phys_to_virt(base); + struct intel_mp_floating *mpf; + + Dprintk("Scan SMP from %p for %ld bytes.\n", bp,length); + if (sizeof(*mpf) != 16) + printk("Error: MPF size\n"); + + while (length > 0) { + mpf = (struct intel_mp_floating *)bp; + if ((*bp == SMP_MAGIC_IDENT) && + (mpf->mpf_length == 1) && + !mpf_checksum((unsigned char *)bp, 16) && + ((mpf->mpf_specification == 1) + || (mpf->mpf_specification == 4)) ) { + + smp_found_config = 1; + printk("found SMP MP-table at %08lx\n", + virt_to_phys(mpf)); + reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE); + if (mpf->mpf_physptr) + reserve_bootmem(mpf->mpf_physptr, PAGE_SIZE); + mpf_found = mpf; + return 1; + } + bp += 4; + length -= 16; + } + return 0; +} + +void __init find_intel_smp (void) +{ + unsigned int address; + + /* + * FIXME: Linux assumes you have 640K of base ram.. + * this continues the error... + * + * 1) Scan the bottom 1K for a signature + * 2) Scan the top 1K of base RAM + * 3) Scan the 64K of bios + */ + if (smp_scan_config(0x0,0x400) || + smp_scan_config(639*0x400,0x400) || + smp_scan_config(0xF0000,0x10000)) + return; + /* + * If it is an SMP machine we should know now, unless the + * configuration is in an EISA/MCA bus machine with an + * extended bios data area. + * + * there is a real-mode segmented pointer pointing to the + * 4K EBDA area at 0x40E, calculate and scan it here. + * + * NOTE! There are Linux loaders that will corrupt the EBDA + * area, and as such this kind of SMP config may be less + * trustworthy, simply because the SMP table may have been + * stomped on during early boot. These loaders are buggy and + * should be fixed. + */ + + address = *(unsigned short *)phys_to_virt(0x40E); + address <<= 4; + smp_scan_config(address, 0x1000); + if (smp_found_config) + printk(KERN_WARNING "WARNING: MP table in the EBDA can be UNSAFE, contact linux-smp@vger.kernel.org if you experience SMP problems!\n"); +} + +#else + +/* + * The Visual Workstation is Intel MP compliant in the hardware + * sense, but it doesnt have a BIOS(-configuration table). + * No problem for Linux. + */ +void __init find_visws_smp(void) +{ + smp_found_config = 1; + + phys_cpu_present_map |= 2; /* or in id 1 */ + apic_version[1] |= 0x10; /* integrated APIC */ + apic_version[0] |= 0x10; + + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; +} + +#endif + +/* + * - Intel MP Configuration Table + * - or SGI Visual Workstation configuration + */ +void __init find_smp_config (void) +{ +#ifdef CONFIG_X86_LOCAL_APIC + find_intel_smp(); +#endif +#ifdef CONFIG_VISWS + find_visws_smp(); +#endif +} + diff -Nru a/arch/x86_64/kernel/msr.c b/arch/x86_64/kernel/msr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/msr.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,279 @@ +#ident "$Id: msr.c,v 1.6 2001/10/24 23:58:53 ak Exp $" +/* ----------------------------------------------------------------------- * + * + * Copyright 2000 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * msr.c + * + * x86 MSR access device + * + * This device is accessed by lseek() to the appropriate register number + * and then read/write in chunks of 8 bytes. A larger size means multiple + * reads or writes of the same register. + * + * This driver uses /dev/cpu/%d/msr where %d is the minor number, and on + * an SMP box will direct the access to CPU %d. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Note: "err" is handled in a funny way below. Otherwise one version + of gcc or another breaks. */ + +static inline int wrmsr_eio(u32 reg, u32 eax, u32 edx) +{ + int err; + + asm volatile( + "1: wrmsr\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: movl %4,%0\n" + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .quad 1b,3b\n" + ".previous" + : "=&bDS" (err) + : "a" (eax), "d" (edx), "c" (reg), "i" (-EIO), "0" (0)); + + return err; +} + +static inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx) +{ + int err; + + asm volatile( + "1: rdmsr\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: movl %4,%0\n" + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .quad 1b,3b\n" + ".previous" + : "=&bDS" (err), "=a" (*eax), "=d" (*edx) + : "c" (reg), "i" (-EIO), "0" (0)); + + return err; +} + +#ifdef CONFIG_SMP + +struct msr_command { + int cpu; + int err; + u32 reg; + u32 data[2]; +}; + +static void msr_smp_wrmsr(void *cmd_block) +{ + struct msr_command *cmd = (struct msr_command *) cmd_block; + + if ( cmd->cpu == smp_processor_id() ) + cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]); +} + +static void msr_smp_rdmsr(void *cmd_block) +{ + struct msr_command *cmd = (struct msr_command *) cmd_block; + + if ( cmd->cpu == smp_processor_id() ) + cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]); +} + +static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) +{ + struct msr_command cmd; + + if ( cpu == smp_processor_id() ) { + return wrmsr_eio(reg, eax, edx); + } else { + cmd.cpu = cpu; + cmd.reg = reg; + cmd.data[0] = eax; + cmd.data[1] = edx; + + smp_call_function(msr_smp_wrmsr, &cmd, 1, 1); + return cmd.err; + } +} + +static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx) +{ + struct msr_command cmd; + + if ( cpu == smp_processor_id() ) { + return rdmsr_eio(reg, eax, edx); + } else { + cmd.cpu = cpu; + cmd.reg = reg; + + smp_call_function(msr_smp_rdmsr, &cmd, 1, 1); + + *eax = cmd.data[0]; + *edx = cmd.data[1]; + + return cmd.err; + } +} + +#else /* ! CONFIG_SMP */ + +static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) +{ + return wrmsr_eio(reg, eax, edx); +} + +static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx) +{ + return rdmsr_eio(reg, eax, edx); +} + +#endif /* ! CONFIG_SMP */ + +static loff_t msr_seek(struct file *file, loff_t offset, int orig) +{ + loff_t ret = -EINVAL; + lock_kernel(); + switch (orig) { + case 0: + file->f_pos = offset; + ret = file->f_pos; + break; + case 1: + file->f_pos += offset; + ret = file->f_pos; + } + unlock_kernel(); + return ret; +} + +static ssize_t msr_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + u32 *tmp = (u32 *)buf; + u32 data[2]; + size_t rv; + u32 reg = *ppos; + int cpu = minor(file->f_dentry->d_inode->i_rdev); + int err; + + if ( count % 8 ) + return -EINVAL; /* Invalid chunk size */ + + for ( rv = 0 ; count ; count -= 8 ) { + err = do_rdmsr(cpu, reg, &data[0], &data[1]); + if ( err ) + return err; + if ( copy_to_user(tmp,&data,8) ) + return -EFAULT; + tmp += 2; + } + + return ((char *)tmp) - buf; +} + +static ssize_t msr_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + const u32 *tmp = (const u32 *)buf; + u32 data[2]; + size_t rv; + u32 reg = *ppos; + int cpu = minor(file->f_dentry->d_inode->i_rdev); + int err; + + if ( count % 8 ) + return -EINVAL; /* Invalid chunk size */ + + for ( rv = 0 ; count ; count -= 8 ) { + if ( copy_from_user(&data,tmp,8) ) + return -EFAULT; + err = do_wrmsr(cpu, reg, data[0], data[1]); + if ( err ) + return err; + tmp += 2; + } + + return ((char *)tmp) - buf; +} + +static int msr_open(struct inode *inode, struct file *file) +{ + int cpu = minor(file->f_dentry->d_inode->i_rdev); + struct cpuinfo_x86 *c = &(cpu_data)[cpu]; + + if ( !(cpu_online_map & (1UL << cpu)) ) + return -ENXIO; /* No such CPU */ + if ( !test_bit(X86_FEATURE_MSR, &c->x86_capability) ) + return -EIO; /* MSR not supported */ + + return 0; +} + +/* + * File operations we support + */ +static struct file_operations msr_fops = { + owner: THIS_MODULE, + llseek: msr_seek, + read: msr_read, + write: msr_write, + open: msr_open, +}; + +int __init msr_init(void) +{ + if (register_chrdev(MSR_MAJOR, "cpu/msr", &msr_fops)) { + printk(KERN_ERR "msr: unable to get major %d for msr\n", + MSR_MAJOR); + return -EBUSY; + } + + return 0; +} + +void __exit msr_exit(void) +{ + unregister_chrdev(MSR_MAJOR, "cpu/msr"); +} + +module_init(msr_init); +module_exit(msr_exit) + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("H. Peter Anvin "); +MODULE_DESCRIPTION("x86 generic MSR driver"); +MODULE_LICENSE("GPL"); diff -Nru a/arch/x86_64/kernel/mtrr.c b/arch/x86_64/kernel/mtrr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/mtrr.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2310 @@ +/* Generic MTRR (Memory Type Range Register) driver. + + Copyright (C) 1997-2000 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + Source: "Pentium Pro Family Developer's Manual, Volume 3: + Operating System Writer's Guide" (Intel document number 242692), + section 11.11.7 + + ChangeLog + + Prehistory Martin Tischhäuser + Initial register-setting code (from proform-1.0). + 19971216 Richard Gooch + Original version for /proc/mtrr interface, SMP-safe. + v1.0 + 19971217 Richard Gooch + Bug fix for ioctls()'s. + Added sample code in Documentation/mtrr.txt + v1.1 + 19971218 Richard Gooch + Disallow overlapping regions. + 19971219 Jens Maurer + Register-setting fixups. + v1.2 + 19971222 Richard Gooch + Fixups for kernel 2.1.75. + v1.3 + 19971229 David Wragg + Register-setting fixups and conformity with Intel conventions. + 19971229 Richard Gooch + Cosmetic changes and wrote this ChangeLog ;-) + 19980106 Richard Gooch + Fixups for kernel 2.1.78. + v1.4 + 19980119 David Wragg + Included passive-release enable code (elsewhere in PCI setup). + v1.5 + 19980131 Richard Gooch + Replaced global kernel lock with private spinlock. + v1.6 + 19980201 Richard Gooch + Added wait for other CPUs to complete changes. + v1.7 + 19980202 Richard Gooch + Bug fix in definition of for UP. + v1.8 + 19980319 Richard Gooch + Fixups for kernel 2.1.90. + 19980323 Richard Gooch + Move SMP BIOS fixup before secondary CPUs call + v1.9 + 19980325 Richard Gooch + Fixed test for overlapping regions: confused by adjacent regions + 19980326 Richard Gooch + Added wbinvd in . + 19980401 Richard Gooch + Bug fix for non-SMP compilation. + 19980418 David Wragg + Fixed-MTRR synchronisation for SMP and use atomic operations + instead of spinlocks. + 19980418 Richard Gooch + Differentiate different MTRR register classes for BIOS fixup. + v1.10 + 19980419 David Wragg + Bug fix in variable MTRR synchronisation. + v1.11 + 19980419 Richard Gooch + Fixups for kernel 2.1.97. + v1.12 + 19980421 Richard Gooch + Safer synchronisation across CPUs when changing MTRRs. + v1.13 + 19980423 Richard Gooch + Bugfix for SMP systems without MTRR support. + v1.14 + 19980427 Richard Gooch + Trap calls to and on non-MTRR machines. + v1.15 + 19980427 Richard Gooch + Use atomic bitops for setting SMP change mask. + v1.16 + 19980428 Richard Gooch + Removed spurious diagnostic message. + v1.17 + 19980429 Richard Gooch + Moved register-setting macros into this file. + Moved setup code from init/main.c to i386-specific areas. + v1.18 + 19980502 Richard Gooch + Moved MTRR detection outside conditionals in . + v1.19 + 19980502 Richard Gooch + Documentation improvement: mention Pentium II and AGP. + v1.20 + 19980521 Richard Gooch + Only manipulate interrupt enable flag on local CPU. + Allow enclosed uncachable regions. + v1.21 + 19980611 Richard Gooch + Always define . + v1.22 + 19980901 Richard Gooch + Removed module support in order to tidy up code. + Added sanity check for / before . + Created addition queue for prior to SMP commence. + v1.23 + 19980902 Richard Gooch + Ported patch to kernel 2.1.120-pre3. + v1.24 + 19980910 Richard Gooch + Removed sanity checks and addition queue: Linus prefers an OOPS. + v1.25 + 19981001 Richard Gooch + Fixed harmless compiler warning in include/asm-i386/mtrr.h + Fixed version numbering and history for v1.23 -> v1.24. + v1.26 + 19990118 Richard Gooch + Added devfs support. + v1.27 + 19990123 Richard Gooch + Changed locking to spin with reschedule. + Made use of new . + v1.28 + 19990201 Zoltán Böszörményi + Extended the driver to be able to use Cyrix style ARRs. + 19990204 Richard Gooch + Restructured Cyrix support. + v1.29 + 19990204 Zoltán Böszörményi + Refined ARR support: enable MAPEN in set_mtrr_prepare() + and disable MAPEN in set_mtrr_done(). + 19990205 Richard Gooch + Minor cleanups. + v1.30 + 19990208 Zoltán Böszörményi + Protect plain 6x86s (and other processors without the + Page Global Enable feature) against accessing CR4 in + set_mtrr_prepare() and set_mtrr_done(). + 19990210 Richard Gooch + Turned and into function pointers. + v1.31 + 19990212 Zoltán Böszörményi + Major rewrite of cyrix_arr_init(): do not touch ARRs, + leave them as the BIOS have set them up. + Enable usage of all 8 ARRs. + Avoid multiplications by 3 everywhere and other + code clean ups/speed ups. + 19990213 Zoltán Böszörményi + Set up other Cyrix processors identical to the boot cpu. + Since Cyrix don't support Intel APIC, this is l'art pour l'art. + Weigh ARRs by size: + If size <= 32M is given, set up ARR# we were given. + If size > 32M is given, set up ARR7 only if it is free, + fail otherwise. + 19990214 Zoltán Böszörményi + Also check for size >= 256K if we are to set up ARR7, + mtrr_add() returns the value it gets from set_mtrr() + 19990218 Zoltán Böszörményi + Remove Cyrix "coma bug" workaround from here. + Moved to linux/arch/i386/kernel/setup.c and + linux/include/asm-i386/bugs.h + 19990228 Richard Gooch + Added MTRRIOC_KILL_ENTRY ioctl(2) + Trap for counter underflow in . + Trap for 4 MiB aligned regions for PPro, stepping <= 7. + 19990301 Richard Gooch + Created hook. + 19990305 Richard Gooch + Temporarily disable AMD support now MTRR capability flag is set. + v1.32 + 19990308 Zoltán Böszörményi + Adjust my changes (19990212-19990218) to Richard Gooch's + latest changes. (19990228-19990305) + v1.33 + 19990309 Richard Gooch + Fixed typo in message. + 19990310 Richard Gooch + Support K6-II/III based on Alan Cox's patches. + v1.34 + 19990511 Bart Hartgers + Support Centaur C6 MCR's. + 19990512 Richard Gooch + Minor cleanups. + v1.35 + 19990707 Zoltán Böszörményi + Check whether ARR3 is protected in cyrix_get_free_region() + and mtrr_del(). The code won't attempt to delete or change it + from now on if the BIOS protected ARR3. It silently skips ARR3 + in cyrix_get_free_region() or returns with an error code from + mtrr_del(). + 19990711 Zoltán Böszörményi + Reset some bits in the CCRs in cyrix_arr_init() to disable SMM + if ARR3 isn't protected. This is needed because if SMM is active + and ARR3 isn't protected then deleting and setting ARR3 again + may lock up the processor. With SMM entirely disabled, it does + not happen. + 19990812 Zoltán Böszörményi + Rearrange switch() statements so the driver accomodates to + the fact that the AMD Athlon handles its MTRRs the same way + as Intel does. + 19990814 Zoltán Böszörményi + Double check for Intel in mtrr_add()'s big switch() because + that revision check is only valid for Intel CPUs. + 19990819 Alan Cox + Tested Zoltan's changes on a pre production Athlon - 100% + success. + 19991008 Manfred Spraul + replaced spin_lock_reschedule() with a normal semaphore. + v1.36 + 20000221 Richard Gooch + Compile fix if procfs and devfs not enabled. + Formatting changes. + v1.37 + 20001109 H. Peter Anvin + Use the new centralized CPU feature detects. + + v1.38 + 20010309 Dave Jones + Add support for Cyrix III. + + v1.39 + 20010312 Dave Jones + Ugh, I broke AMD support. + Reworked fix by Troels Walsted Hansen + + v1.40 + 20010327 Dave Jones + Adapted Cyrix III support to include VIA C3. + +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define MTRR_NEED_STRINGS +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MTRR_VERSION "1.40 (20010327)" + +#define TRUE 1 +#define FALSE 0 + +/* + * The code assumes all processors support the same MTRR + * interface. This is generally a good assumption, but could + * potentially be a problem. + */ +enum mtrr_if_type { + MTRR_IF_NONE, /* No MTRRs supported */ + MTRR_IF_INTEL, /* Intel (P6) standard MTRRs */ + MTRR_IF_AMD_K6, /* AMD pre-Athlon MTRRs */ + MTRR_IF_CYRIX_ARR, /* Cyrix ARRs */ + MTRR_IF_CENTAUR_MCR, /* Centaur MCRs */ +} mtrr_if = MTRR_IF_NONE; + +static __initdata char *mtrr_if_name[] = { + "none", "Intel", "AMD K6", "Cyrix ARR", "Centaur MCR" +}; + +#define MTRRcap_MSR 0x0fe +#define MTRRdefType_MSR 0x2ff + +#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg)) +#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1) + +#define NUM_FIXED_RANGES 88 +#define MTRRfix64K_00000_MSR 0x250 +#define MTRRfix16K_80000_MSR 0x258 +#define MTRRfix16K_A0000_MSR 0x259 +#define MTRRfix4K_C0000_MSR 0x268 +#define MTRRfix4K_C8000_MSR 0x269 +#define MTRRfix4K_D0000_MSR 0x26a +#define MTRRfix4K_D8000_MSR 0x26b +#define MTRRfix4K_E0000_MSR 0x26c +#define MTRRfix4K_E8000_MSR 0x26d +#define MTRRfix4K_F0000_MSR 0x26e +#define MTRRfix4K_F8000_MSR 0x26f + +#ifdef CONFIG_SMP +# define MTRR_CHANGE_MASK_FIXED 0x01 +# define MTRR_CHANGE_MASK_VARIABLE 0x02 +# define MTRR_CHANGE_MASK_DEFTYPE 0x04 +#endif + +/* In the Intel processor's MTRR interface, the MTRR type is always held in + an 8 bit field: */ +typedef u8 mtrr_type; + +#define LINE_SIZE 80 +#define JIFFIE_TIMEOUT 100 + +#ifdef CONFIG_SMP +# define set_mtrr(reg,base,size,type) set_mtrr_smp (reg, base, size, type) +#else +# define set_mtrr(reg,base,size,type) (*set_mtrr_up) (reg, base, size, type, \ + TRUE) +#endif + +#if defined(CONFIG_PROC_FS) || defined(CONFIG_DEVFS_FS) +# define USERSPACE_INTERFACE +#endif + +#ifndef USERSPACE_INTERFACE +# define compute_ascii() while (0) +#endif + +#ifdef USERSPACE_INTERFACE +static char *ascii_buffer; +static unsigned int ascii_buf_bytes; +#endif +static unsigned int *usage_table; +static DECLARE_MUTEX(main_lock); + +/* Private functions */ +#ifdef USERSPACE_INTERFACE +static void compute_ascii (void); +#endif + + +struct set_mtrr_context +{ + unsigned long flags; + unsigned long deftype_lo; + unsigned long deftype_hi; + unsigned long cr4val; + unsigned long ccr3; +}; + +static int arr3_protected; + +/* Put the processor into a state where MTRRs can be safely set */ +static void set_mtrr_prepare_save (struct set_mtrr_context *ctxt) +{ + /* Disable interrupts locally */ + __save_flags (ctxt->flags); __cli (); + + if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) + return; + + /* Save value of CR4 and clear Page Global Enable (bit 7) */ + if ( test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability) ) { + ctxt->cr4val = read_cr4(); + write_cr4(ctxt->cr4val & ~(1<<7)); + } + + /* Disable and flush caches. Note that wbinvd flushes the TLBs as + a side-effect */ + + { + long cr0 = read_cr0() | 0x40000000; + wbinvd(); + write_cr0( cr0 ); + wbinvd(); + } + + if ( mtrr_if == MTRR_IF_INTEL ) { + /* Save MTRR state */ + rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + } else { + /* Cyrix ARRs - everything else were excluded at the top */ + ctxt->ccr3 = getCx86 (CX86_CCR3); + } +} /* End Function set_mtrr_prepare_save */ + +static void set_mtrr_cache_disable (struct set_mtrr_context *ctxt) +{ + if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) + return; + + if ( mtrr_if == MTRR_IF_INTEL ) { + /* Disable MTRRs, and set the default type to uncached */ + wrmsr (MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi); + } else { + /* Cyrix ARRs - everything else were excluded at the top */ + setCx86 (CX86_CCR3, (ctxt->ccr3 & 0x0f) | 0x10); + } +} /* End Function set_mtrr_cache_disable */ + +/* Restore the processor after a set_mtrr_prepare */ +static void set_mtrr_done (struct set_mtrr_context *ctxt) +{ + if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) { + __restore_flags (ctxt->flags); + return; + } + + /* Flush caches and TLBs */ + wbinvd(); + + /* Restore MTRRdefType */ + if ( mtrr_if == MTRR_IF_INTEL ) { + /* Intel (P6) standard MTRRs */ + wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + } else { + /* Cyrix ARRs - everything else was excluded at the top */ + setCx86 (CX86_CCR3, ctxt->ccr3); + } + + /* Enable caches */ + write_cr0( read_cr0() & 0xbfffffff ); + + /* Restore value of CR4 */ + if ( test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability) ) + write_cr4(ctxt->cr4val); + + /* Re-enable interrupts locally (if enabled previously) */ + __restore_flags (ctxt->flags); +} /* End Function set_mtrr_done */ + +/* This function returns the number of variable MTRRs */ +static unsigned int get_num_var_ranges (void) +{ + unsigned long config, dummy; + + switch ( mtrr_if ) + { + case MTRR_IF_INTEL: + rdmsr (MTRRcap_MSR, config, dummy); + return (config & 0xff); + case MTRR_IF_AMD_K6: + return 2; + case MTRR_IF_CYRIX_ARR: + return 8; + case MTRR_IF_CENTAUR_MCR: + return 8; + default: + return 0; + } +} /* End Function get_num_var_ranges */ + +/* Returns non-zero if we have the write-combining memory type */ +static int have_wrcomb (void) +{ + unsigned long config, dummy; + struct pci_dev *dev = NULL; + + /* ServerWorks LE chipsets have problems with write-combining + Don't allow it and leave room for other chipsets to be tagged */ + + if ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) != NULL) { + switch(dev->vendor) { + case PCI_VENDOR_ID_SERVERWORKS: + switch (dev->device) { + case PCI_DEVICE_ID_SERVERWORKS_LE: + return 0; + break; + default: + break; + } + break; + default: + break; + } + } + + + switch ( mtrr_if ) + { + case MTRR_IF_INTEL: + rdmsr (MTRRcap_MSR, config, dummy); + return (config & (1<<10)); + return 1; + case MTRR_IF_AMD_K6: + case MTRR_IF_CENTAUR_MCR: + case MTRR_IF_CYRIX_ARR: + return 1; + default: + return 0; + } +} /* End Function have_wrcomb */ + +static u32 size_or_mask, size_and_mask; + +static void intel_get_mtrr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long mask_lo, mask_hi, base_lo, base_hi; + + rdmsr (MTRRphysMask_MSR(reg), mask_lo, mask_hi); + if ( (mask_lo & 0x800) == 0 ) + { + /* Invalid (i.e. free) range */ + *base = 0; + *size = 0; + *type = 0; + return; + } + + rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi); + + /* Work out the shifted address mask. */ + mask_lo = size_or_mask | mask_hi << (32 - PAGE_SHIFT) + | mask_lo >> PAGE_SHIFT; + + /* This works correctly if size is a power of two, i.e. a + contiguous range. */ + *size = -mask_lo; + *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT; + *type = base_lo & 0xff; +} /* End Function intel_get_mtrr */ + +static void cyrix_get_arr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long flags; + unsigned char arr, ccr3, rcr, shift; + + arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ + + /* Save flags and disable interrupts */ + __save_flags (flags); __cli (); + + ccr3 = getCx86 (CX86_CCR3); + setCx86 (CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ + ((unsigned char *) base)[3] = getCx86 (arr); + ((unsigned char *) base)[2] = getCx86 (arr+1); + ((unsigned char *) base)[1] = getCx86 (arr+2); + rcr = getCx86(CX86_RCR_BASE + reg); + setCx86 (CX86_CCR3, ccr3); /* disable MAPEN */ + + /* Enable interrupts if it was enabled previously */ + __restore_flags (flags); + shift = ((unsigned char *) base)[1] & 0x0f; + *base >>= PAGE_SHIFT; + + /* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 + * Note: shift==0xf means 4G, this is unsupported. + */ + if (shift) + *size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1); + else + *size = 0; + + /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ + if (reg < 7) + { + switch (rcr) + { + case 1: *type = MTRR_TYPE_UNCACHABLE; break; + case 8: *type = MTRR_TYPE_WRBACK; break; + case 9: *type = MTRR_TYPE_WRCOMB; break; + case 24: + default: *type = MTRR_TYPE_WRTHROUGH; break; + } + } else + { + switch (rcr) + { + case 0: *type = MTRR_TYPE_UNCACHABLE; break; + case 8: *type = MTRR_TYPE_WRCOMB; break; + case 9: *type = MTRR_TYPE_WRBACK; break; + case 25: + default: *type = MTRR_TYPE_WRTHROUGH; break; + } + } +} /* End Function cyrix_get_arr */ + +static void amd_get_mtrr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long low, high; + + rdmsr (0xC0000085, low, high); + /* Upper dword is region 1, lower is region 0 */ + if (reg == 1) low = high; + /* The base masks off on the right alignment */ + *base = (low & 0xFFFE0000) >> PAGE_SHIFT; + *type = 0; + if (low & 1) *type = MTRR_TYPE_UNCACHABLE; + if (low & 2) *type = MTRR_TYPE_WRCOMB; + if ( !(low & 3) ) + { + *size = 0; + return; + } + /* + * This needs a little explaining. The size is stored as an + * inverted mask of bits of 128K granularity 15 bits long offset + * 2 bits + * + * So to get a size we do invert the mask and add 1 to the lowest + * mask bit (4 as its 2 bits in). This gives us a size we then shift + * to turn into 128K blocks + * + * eg 111 1111 1111 1100 is 512K + * + * invert 000 0000 0000 0011 + * +1 000 0000 0000 0100 + * *128K ... + */ + low = (~low) & 0x1FFFC; + *size = (low + 4) << (15 - PAGE_SHIFT); + return; +} /* End Function amd_get_mtrr */ + +static struct +{ + unsigned long high; + unsigned long low; +} centaur_mcr[8]; + +static u8 centaur_mcr_reserved; +static u8 centaur_mcr_type; /* 0 for winchip, 1 for winchip2 */ + +/* + * Report boot time MCR setups + */ + +void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) +{ + centaur_mcr[mcr].low = lo; + centaur_mcr[mcr].high = hi; +} + +static void centaur_get_mcr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + *base = centaur_mcr[reg].high >> PAGE_SHIFT; + *size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT; + *type = MTRR_TYPE_WRCOMB; /* If it is there, it is write-combining */ + if(centaur_mcr_type==1 && ((centaur_mcr[reg].low&31)&2)) + *type = MTRR_TYPE_UNCACHABLE; + if(centaur_mcr_type==1 && (centaur_mcr[reg].low&31)==25) + *type = MTRR_TYPE_WRBACK; + if(centaur_mcr_type==0 && (centaur_mcr[reg].low&31)==31) + *type = MTRR_TYPE_WRBACK; + +} /* End Function centaur_get_mcr */ + +static void (*get_mtrr) (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type); + +static void intel_set_mtrr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +/* [SUMMARY] Set variable MTRR register on the local CPU. + The register to set. + The base address of the region. + The size of the region. If this is 0 the region is disabled. + The type of the region. + If TRUE, do the change safely. If FALSE, safety measures should + be done externally. + [RETURNS] Nothing. +*/ +{ + struct set_mtrr_context ctxt; + + if (do_safe) { + set_mtrr_prepare_save (&ctxt); + set_mtrr_cache_disable (&ctxt); + } + if (size == 0) + { + /* The invalid bit is kept in the mask, so we simply clear the + relevant mask register to disable a range. */ + wrmsr (MTRRphysMask_MSR (reg), 0, 0); + } + else + { + wrmsr (MTRRphysBase_MSR (reg), base << PAGE_SHIFT | type, + (base & size_and_mask) >> (32 - PAGE_SHIFT)); + wrmsr (MTRRphysMask_MSR (reg), -size << PAGE_SHIFT | 0x800, + (-size & size_and_mask) >> (32 - PAGE_SHIFT)); + } + if (do_safe) set_mtrr_done (&ctxt); +} /* End Function intel_set_mtrr_up */ + +static void cyrix_set_arr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +{ + struct set_mtrr_context ctxt; + unsigned char arr, arr_type, arr_size; + + arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ + + /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ + if (reg >= 7) + size >>= 6; + + size &= 0x7fff; /* make sure arr_size <= 14 */ + for(arr_size = 0; size; arr_size++, size >>= 1); + + if (reg<7) + { + switch (type) { + case MTRR_TYPE_UNCACHABLE: arr_type = 1; break; + case MTRR_TYPE_WRCOMB: arr_type = 9; break; + case MTRR_TYPE_WRTHROUGH: arr_type = 24; break; + default: arr_type = 8; break; + } + } + else + { + switch (type) + { + case MTRR_TYPE_UNCACHABLE: arr_type = 0; break; + case MTRR_TYPE_WRCOMB: arr_type = 8; break; + case MTRR_TYPE_WRTHROUGH: arr_type = 25; break; + default: arr_type = 9; break; + } + } + + if (do_safe) { + set_mtrr_prepare_save (&ctxt); + set_mtrr_cache_disable (&ctxt); + } + base <<= PAGE_SHIFT; + setCx86(arr, ((unsigned char *) &base)[3]); + setCx86(arr+1, ((unsigned char *) &base)[2]); + setCx86(arr+2, (((unsigned char *) &base)[1]) | arr_size); + setCx86(CX86_RCR_BASE + reg, arr_type); + if (do_safe) set_mtrr_done (&ctxt); +} /* End Function cyrix_set_arr_up */ + +static void amd_set_mtrr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +/* [SUMMARY] Set variable MTRR register on the local CPU. + The register to set. + The base address of the region. + The size of the region. If this is 0 the region is disabled. + The type of the region. + If TRUE, do the change safely. If FALSE, safety measures should + be done externally. + [RETURNS] Nothing. +*/ +{ + u32 regs[2]; + struct set_mtrr_context ctxt; + + if (do_safe) { + set_mtrr_prepare_save (&ctxt); + set_mtrr_cache_disable (&ctxt); + } + /* + * Low is MTRR0 , High MTRR 1 + */ + rdmsr (0xC0000085, regs[0], regs[1]); + /* + * Blank to disable + */ + if (size == 0) + regs[reg] = 0; + else + /* Set the register to the base, the type (off by one) and an + inverted bitmask of the size The size is the only odd + bit. We are fed say 512K We invert this and we get 111 1111 + 1111 1011 but if you subtract one and invert you get the + desired 111 1111 1111 1100 mask + + But ~(x - 1) == ~x + 1 == -x. Two's complement rocks! */ + regs[reg] = (-size>>(15-PAGE_SHIFT) & 0x0001FFFC) + | (base<base_lo, vr->base_hi); + rdmsr (MTRRphysMask_MSR (index), vr->mask_lo, vr->mask_hi); +} /* End Function get_mtrr_var_range */ + + +/* Set the MSR pair relating to a var range. Returns TRUE if + changes are made */ +static int __init set_mtrr_var_range_testing (unsigned int index, + struct mtrr_var_range *vr) +{ + unsigned int lo, hi; + int changed = FALSE; + + rdmsr(MTRRphysBase_MSR(index), lo, hi); + if ( (vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL) + || (vr->base_hi & 0xfUL) != (hi & 0xfUL) ) + { + wrmsr (MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); + changed = TRUE; + } + + rdmsr (MTRRphysMask_MSR(index), lo, hi); + + if ( (vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL) + || (vr->mask_hi & 0xfUL) != (hi & 0xfUL) ) + { + wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); + changed = TRUE; + } + return changed; +} /* End Function set_mtrr_var_range_testing */ + +static void __init get_fixed_ranges(mtrr_type *frs) +{ + unsigned long *p = (unsigned long *)frs; + int i; + + rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]); + + for (i = 0; i < 2; i++) + rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); + for (i = 0; i < 8; i++) + rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); +} /* End Function get_fixed_ranges */ + +static int __init set_fixed_ranges_testing(mtrr_type *frs) +{ + unsigned long *p = (unsigned long *)frs; + int changed = FALSE; + int i; + unsigned long lo, hi; + + rdmsr(MTRRfix64K_00000_MSR, lo, hi); + if (p[0] != lo || p[1] != hi) + { + wrmsr (MTRRfix64K_00000_MSR, p[0], p[1]); + changed = TRUE; + } + + for (i = 0; i < 2; i++) + { + rdmsr (MTRRfix16K_80000_MSR + i, lo, hi); + if (p[2 + i*2] != lo || p[3 + i*2] != hi) + { + wrmsr (MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); + changed = TRUE; + } + } + + for (i = 0; i < 8; i++) + { + rdmsr (MTRRfix4K_C0000_MSR + i, lo, hi); + if (p[6 + i*2] != lo || p[7 + i*2] != hi) + { + wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); + changed = TRUE; + } + } + return changed; +} /* End Function set_fixed_ranges_testing */ + +struct mtrr_state +{ + unsigned int num_var_ranges; + struct mtrr_var_range *var_ranges; + mtrr_type fixed_ranges[NUM_FIXED_RANGES]; + unsigned char enabled; + mtrr_type def_type; +}; + + +/* Grab all of the MTRR state for this CPU into *state */ +static void __init get_mtrr_state(struct mtrr_state *state) +{ + unsigned int nvrs, i; + struct mtrr_var_range *vrs; + unsigned long lo, dummy; + + nvrs = state->num_var_ranges = get_num_var_ranges(); + vrs = state->var_ranges + = kmalloc (nvrs * sizeof (struct mtrr_var_range), GFP_KERNEL); + if (vrs == NULL) + nvrs = state->num_var_ranges = 0; + + for (i = 0; i < nvrs; i++) + get_mtrr_var_range (i, &vrs[i]); + get_fixed_ranges (state->fixed_ranges); + + rdmsr (MTRRdefType_MSR, lo, dummy); + state->def_type = (lo & 0xff); + state->enabled = (lo & 0xc00) >> 10; +} /* End Function get_mtrr_state */ + + +/* Free resources associated with a struct mtrr_state */ +static void __init finalize_mtrr_state(struct mtrr_state *state) +{ + if (state->var_ranges) kfree (state->var_ranges); +} /* End Function finalize_mtrr_state */ + + +static unsigned long __init set_mtrr_state (struct mtrr_state *state, + struct set_mtrr_context *ctxt) +/* [SUMMARY] Set the MTRR state for this CPU. + The MTRR state information to read. + Some relevant CPU context. + [NOTE] The CPU must already be in a safe state for MTRR changes. + [RETURNS] 0 if no changes made, else a mask indication what was changed. +*/ +{ + unsigned int i; + unsigned long change_mask = 0; + + for (i = 0; i < state->num_var_ranges; i++) + if ( set_mtrr_var_range_testing (i, &state->var_ranges[i]) ) + change_mask |= MTRR_CHANGE_MASK_VARIABLE; + + if ( set_fixed_ranges_testing(state->fixed_ranges) ) + change_mask |= MTRR_CHANGE_MASK_FIXED; + /* Set_mtrr_restore restores the old value of MTRRdefType, + so to set it we fiddle with the saved value */ + if ( (ctxt->deftype_lo & 0xff) != state->def_type + || ( (ctxt->deftype_lo & 0xc00) >> 10 ) != state->enabled) + { + ctxt->deftype_lo |= (state->def_type | state->enabled << 10); + change_mask |= MTRR_CHANGE_MASK_DEFTYPE; + } + + return change_mask; +} /* End Function set_mtrr_state */ + + +static atomic_t undone_count; +static volatile int wait_barrier_cache_disable = FALSE; +static volatile int wait_barrier_execute = FALSE; +static volatile int wait_barrier_cache_enable = FALSE; + +struct set_mtrr_data +{ + unsigned long smp_base; + unsigned long smp_size; + unsigned int smp_reg; + mtrr_type smp_type; +}; + +static void ipi_handler (void *info) +/* [SUMMARY] Synchronisation handler. Executed by "other" CPUs. + [RETURNS] Nothing. +*/ +{ + struct set_mtrr_data *data = info; + struct set_mtrr_context ctxt; + set_mtrr_prepare_save (&ctxt); + /* Notify master that I've flushed and disabled my cache */ + atomic_dec (&undone_count); + while (wait_barrier_cache_disable) { rep_nop(); barrier(); } + set_mtrr_cache_disable (&ctxt); + /* Notify master that I've flushed and disabled my cache */ + atomic_dec (&undone_count); + while (wait_barrier_execute) { rep_nop(); barrier(); } + /* The master has cleared me to execute */ + (*set_mtrr_up) (data->smp_reg, data->smp_base, data->smp_size, + data->smp_type, FALSE); + /* Notify master CPU that I've executed the function */ + atomic_dec (&undone_count); + /* Wait for master to clear me to enable cache and return */ + while (wait_barrier_cache_enable) { rep_nop(); barrier(); } + set_mtrr_done (&ctxt); +} /* End Function ipi_handler */ + +static void set_mtrr_smp (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type) +{ + struct set_mtrr_data data; + struct set_mtrr_context ctxt; + + data.smp_reg = reg; + data.smp_base = base; + data.smp_size = size; + data.smp_type = type; + wait_barrier_cache_disable = TRUE; + wait_barrier_execute = TRUE; + wait_barrier_cache_enable = TRUE; + atomic_set (&undone_count, smp_num_cpus - 1); + /* Start the ball rolling on other CPUs */ + if (smp_call_function (ipi_handler, &data, 1, 0) != 0) + panic ("mtrr: timed out waiting for other CPUs\n"); + /* Flush and disable the local CPU's cache */ + set_mtrr_prepare_save (&ctxt); + /* Wait for all other CPUs to flush and disable their caches */ + while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); } + /* Set up for completion wait and then release other CPUs to change MTRRs*/ + atomic_set (&undone_count, smp_num_cpus - 1); + wait_barrier_cache_disable = FALSE; + set_mtrr_cache_disable (&ctxt); + + /* Wait for all other CPUs to flush and disable their caches */ + while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); } + /* Set up for completion wait and then release other CPUs to change MTRRs*/ + atomic_set (&undone_count, smp_num_cpus - 1); + wait_barrier_execute = FALSE; + (*set_mtrr_up) (reg, base, size, type, FALSE); + /* Now wait for other CPUs to complete the function */ + while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); } + /* Now all CPUs should have finished the function. Release the barrier to + allow them to re-enable their caches and return from their interrupt, + then enable the local cache and return */ + wait_barrier_cache_enable = FALSE; + set_mtrr_done (&ctxt); +} /* End Function set_mtrr_smp */ + + +/* Some BIOS's are fucked and don't set all MTRRs the same! */ +static void __init mtrr_state_warn(unsigned long mask) +{ + if (!mask) return; + if (mask & MTRR_CHANGE_MASK_FIXED) + printk ("mtrr: your CPUs had inconsistent fixed MTRR settings\n"); + if (mask & MTRR_CHANGE_MASK_VARIABLE) + printk ("mtrr: your CPUs had inconsistent variable MTRR settings\n"); + if (mask & MTRR_CHANGE_MASK_DEFTYPE) + printk ("mtrr: your CPUs had inconsistent MTRRdefType settings\n"); + printk ("mtrr: probably your BIOS does not setup all CPUs\n"); +} /* End Function mtrr_state_warn */ + +#endif /* CONFIG_SMP */ + +static char *attrib_to_str (int x) +{ + return (x <= 6) ? mtrr_strings[x] : "?"; +} /* End Function attrib_to_str */ + +static void init_table (void) +{ + int i, max; + + max = get_num_var_ranges (); + if ( ( usage_table = kmalloc (max * sizeof *usage_table, GFP_KERNEL) ) + == NULL ) + { + printk ("mtrr: could not allocate\n"); + return; + } + for (i = 0; i < max; i++) usage_table[i] = 1; +#ifdef USERSPACE_INTERFACE + if ( ( ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL) ) == NULL ) + { + printk ("mtrr: could not allocate\n"); + return; + } + ascii_buf_bytes = 0; + compute_ascii (); +#endif +} /* End Function init_table */ + +static int generic_get_free_region (unsigned long base, unsigned long size) +/* [SUMMARY] Get a free MTRR. + The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + max = get_num_var_ranges (); + for (i = 0; i < max; ++i) + { + (*get_mtrr) (i, &lbase, &lsize, <ype); + if (lsize == 0) return i; + } + return -ENOSPC; +} /* End Function generic_get_free_region */ + +static int centaur_get_free_region (unsigned long base, unsigned long size) +/* [SUMMARY] Get a free MTRR. + The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + max = get_num_var_ranges (); + for (i = 0; i < max; ++i) + { + if(centaur_mcr_reserved & (1< The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i; + mtrr_type ltype; + unsigned long lbase, lsize; + + /* If we are to set up a region >32M then look at ARR7 immediately */ + if (size > 0x2000) + { + cyrix_get_arr (7, &lbase, &lsize, <ype); + if (lsize == 0) return 7; + /* Else try ARR0-ARR6 first */ + } + else + { + for (i = 0; i < 7; i++) + { + cyrix_get_arr (i, &lbase, &lsize, <ype); + if ((i == 3) && arr3_protected) continue; + if (lsize == 0) return i; + } + /* ARR0-ARR6 isn't free, try ARR7 but its size must be at least 256K */ + cyrix_get_arr (i, &lbase, &lsize, <ype); + if ((lsize == 0) && (size >= 0x40)) return i; + } + return -ENOSPC; +} /* End Function cyrix_get_free_region */ + +static int (*get_free_region) (unsigned long base, + unsigned long size) = generic_get_free_region; + +/** + * mtrr_add_page - Add a memory type region + * @base: Physical base address of region in pages (4 KB) + * @size: Physical size of region in pages (4 KB) + * @type: Type of MTRR desired + * @increment: If this is true do usage counting on the region + * + * Memory type region registers control the caching on newer Intel and + * non Intel processors. This function allows drivers to request an + * MTRR is added. The details and hardware specifics of each processor's + * implementation are hidden from the caller, but nevertheless the + * caller should expect to need to provide a power of two size on an + * equivalent power of two boundary. + * + * If the region cannot be added either because all regions are in use + * or the CPU cannot support it a negative value is returned. On success + * the register number for this entry is returned, but should be treated + * as a cookie only. + * + * On a multiprocessor machine the changes are made to all processors. + * This is required on x86 by the Intel processors. + * + * The available types are + * + * %MTRR_TYPE_UNCACHABLE - No caching + * + * %MTRR_TYPE_WRBACK - Write data back in bursts whenever + * + * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts + * + * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes + * + * BUGS: Needs a quiet flag for the cases where drivers do not mind + * failures and do not wish system log messages to be sent. + */ + +int mtrr_add_page(unsigned long base, unsigned long size, unsigned int type, char increment) +{ +/* [SUMMARY] Add an MTRR entry. + The starting (base, in pages) address of the region. + The size of the region. (in pages) + The type of the new region. + If true and the region already exists, the usage count will be + incremented. + [RETURNS] The MTRR register on success, else a negative number indicating + the error code. + [NOTE] This routine uses a spinlock. +*/ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize, last; + + switch ( mtrr_if ) + { + case MTRR_IF_NONE: + return -ENXIO; /* No MTRRs whatsoever */ + + case MTRR_IF_AMD_K6: + /* Apply the K6 block alignment and size rules + In order + o Uncached or gathering only + o 128K or bigger block + o Power of 2 block + o base suitably aligned to the power + */ + if ( type > MTRR_TYPE_WRCOMB || size < (1 << (17-PAGE_SHIFT)) || + (size & ~(size-1))-size || ( base & (size-1) ) ) + return -EINVAL; + break; + + case MTRR_IF_INTEL: + /* For Intel PPro stepping <= 7, must be 4 MiB aligned + and not touch 0x70000000->0x7003FFFF */ + if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && + boot_cpu_data.x86 == 6 && + boot_cpu_data.x86_model == 1 && + boot_cpu_data.x86_mask <= 7 ) + { + if ( base & ((1 << (22-PAGE_SHIFT))-1) ) + { + printk (KERN_WARNING "mtrr: base(0x%lx000) is not 4 MiB aligned\n", base); + return -EINVAL; + } + if (!(base + size < 0x70000000 || base > 0x7003FFFF) && + (type == MTRR_TYPE_WRCOMB || type == MTRR_TYPE_WRBACK)) + { + printk (KERN_WARNING "mtrr: writable mtrr between 0x70000000 and 0x7003FFFF may hang the CPU.\n"); + return -EINVAL; + } + } + /* Fall through */ + + case MTRR_IF_CYRIX_ARR: + case MTRR_IF_CENTAUR_MCR: + if ( mtrr_if == MTRR_IF_CENTAUR_MCR ) + { + /* + * FIXME: Winchip2 supports uncached + */ + if (type != MTRR_TYPE_WRCOMB && (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) + { + printk (KERN_WARNING "mtrr: only write-combining%s supported\n", + centaur_mcr_type?" and uncacheable are":" is"); + return -EINVAL; + } + } + else if (base + size < 0x100) + { + printk (KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n", + base, size); + return -EINVAL; + } + /* Check upper bits of base and last are equal and lower bits are 0 + for base and 1 for last */ + last = base + size - 1; + for (lbase = base; !(lbase & 1) && (last & 1); + lbase = lbase >> 1, last = last >> 1); + if (lbase != last) + { + printk (KERN_WARNING "mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n", + base, size); + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + if (type >= MTRR_NUM_TYPES) + { + printk ("mtrr: type: %u illegal\n", type); + return -EINVAL; + } + + /* If the type is WC, check that this processor supports it */ + if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () ) + { + printk (KERN_WARNING "mtrr: your processor doesn't support write-combining\n"); + return -ENOSYS; + } + + if ( base & size_or_mask || size & size_or_mask ) + { + printk ("mtrr: base or size exceeds the MTRR width\n"); + return -EINVAL; + } + + increment = increment ? 1 : 0; + max = get_num_var_ranges (); + /* Search for existing MTRR */ + down(&main_lock); + for (i = 0; i < max; ++i) + { + (*get_mtrr) (i, &lbase, &lsize, <ype); + if (base >= lbase + lsize) continue; + if ( (base < lbase) && (base + size <= lbase) ) continue; + /* At this point we know there is some kind of overlap/enclosure */ + if ( (base < lbase) || (base + size > lbase + lsize) ) + { + up(&main_lock); + printk (KERN_WARNING "mtrr: 0x%lx000,0x%lx000 overlaps existing" + " 0x%lx000,0x%lx000\n", + base, size, lbase, lsize); + return -EINVAL; + } + /* New region is enclosed by an existing region */ + if (ltype != type) + { + if (type == MTRR_TYPE_UNCACHABLE) continue; + up(&main_lock); + printk ( "mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n", + base, size, attrib_to_str (ltype), attrib_to_str (type) ); + return -EINVAL; + } + if (increment) ++usage_table[i]; + compute_ascii (); + up(&main_lock); + return i; + } + /* Search for an empty MTRR */ + i = (*get_free_region) (base, size); + if (i < 0) + { + up(&main_lock); + printk ("mtrr: no more MTRRs available\n"); + return i; + } + set_mtrr (i, base, size, type); + usage_table[i] = 1; + compute_ascii (); + up(&main_lock); + return i; +} /* End Function mtrr_add_page */ + +/** + * mtrr_add - Add a memory type region + * @base: Physical base address of region + * @size: Physical size of region + * @type: Type of MTRR desired + * @increment: If this is true do usage counting on the region + * + * Memory type region registers control the caching on newer Intel and + * non Intel processors. This function allows drivers to request an + * MTRR is added. The details and hardware specifics of each processor's + * implementation are hidden from the caller, but nevertheless the + * caller should expect to need to provide a power of two size on an + * equivalent power of two boundary. + * + * If the region cannot be added either because all regions are in use + * or the CPU cannot support it a negative value is returned. On success + * the register number for this entry is returned, but should be treated + * as a cookie only. + * + * On a multiprocessor machine the changes are made to all processors. + * This is required on x86 by the Intel processors. + * + * The available types are + * + * %MTRR_TYPE_UNCACHABLE - No caching + * + * %MTRR_TYPE_WRBACK - Write data back in bursts whenever + * + * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts + * + * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes + * + * BUGS: Needs a quiet flag for the cases where drivers do not mind + * failures and do not wish system log messages to be sent. + */ + +int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char increment) +{ +/* [SUMMARY] Add an MTRR entry. + The starting (base) address of the region. + The size (in bytes) of the region. + The type of the new region. + If true and the region already exists, the usage count will be + incremented. + [RETURNS] The MTRR register on success, else a negative number indicating + the error code. +*/ + + if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type, increment); +} /* End Function mtrr_add */ + +/** + * mtrr_del_page - delete a memory type region + * @reg: Register returned by mtrr_add + * @base: Physical base address + * @size: Size of region + * + * If register is supplied then base and size are ignored. This is + * how drivers should call it. + * + * Releases an MTRR region. If the usage count drops to zero the + * register is freed and the region returns to default state. + * On success the register is returned, on failure a negative error + * code. + */ + +int mtrr_del_page (int reg, unsigned long base, unsigned long size) +/* [SUMMARY] Delete MTRR/decrement usage count. + The register. If this is less than 0 then <> and <> must + be supplied. + The base address of the region. This is ignored if <> is >= 0. + The size of the region. This is ignored if <> is >= 0. + [RETURNS] The register on success, else a negative number indicating + the error code. + [NOTE] This routine uses a spinlock. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + if ( mtrr_if == MTRR_IF_NONE ) return -ENXIO; + + max = get_num_var_ranges (); + down (&main_lock); + if (reg < 0) + { + /* Search for existing MTRR */ + for (i = 0; i < max; ++i) + { + (*get_mtrr) (i, &lbase, &lsize, <ype); + if (lbase == base && lsize == size) + { + reg = i; + break; + } + } + if (reg < 0) + { + up(&main_lock); + printk ("mtrr: no MTRR for %lx000,%lx000 found\n", base, size); + return -EINVAL; + } + } + if (reg >= max) + { + up (&main_lock); + printk ("mtrr: register: %d too big\n", reg); + return -EINVAL; + } + if ( mtrr_if == MTRR_IF_CYRIX_ARR ) + { + if ( (reg == 3) && arr3_protected ) + { + up (&main_lock); + printk ("mtrr: ARR3 cannot be changed\n"); + return -EINVAL; + } + } + (*get_mtrr) (reg, &lbase, &lsize, <ype); + if (lsize < 1) + { + up (&main_lock); + printk ("mtrr: MTRR %d not used\n", reg); + return -EINVAL; + } + if (usage_table[reg] < 1) + { + up (&main_lock); + printk ("mtrr: reg: %d has count=0\n", reg); + return -EINVAL; + } + if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0); + compute_ascii (); + up (&main_lock); + return reg; +} /* End Function mtrr_del_page */ + +/** + * mtrr_del - delete a memory type region + * @reg: Register returned by mtrr_add + * @base: Physical base address + * @size: Size of region + * + * If register is supplied then base and size are ignored. This is + * how drivers should call it. + * + * Releases an MTRR region. If the usage count drops to zero the + * register is freed and the region returns to default state. + * On success the register is returned, on failure a negative error + * code. + */ + +int mtrr_del (int reg, unsigned long base, unsigned long size) +/* [SUMMARY] Delete MTRR/decrement usage count. + The register. If this is less than 0 then <> and <> must + be supplied. + The base address of the region. This is ignored if <> is >= 0. + The size of the region. This is ignored if <> is >= 0. + [RETURNS] The register on success, else a negative number indicating + the error code. +*/ +{ + if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT); +} + +#ifdef USERSPACE_INTERFACE + +static int mtrr_file_add (unsigned long base, unsigned long size, + unsigned int type, char increment, struct file *file, int page) +{ + int reg, max; + unsigned int *fcount = file->private_data; + + max = get_num_var_ranges (); + if (fcount == NULL) + { + if ( ( fcount = kmalloc (max * sizeof *fcount, GFP_KERNEL) ) == NULL ) + { + printk ("mtrr: could not allocate\n"); + return -ENOMEM; + } + memset (fcount, 0, max * sizeof *fcount); + file->private_data = fcount; + } + if (!page) { + if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + base >>= PAGE_SHIFT; + size >>= PAGE_SHIFT; + } + reg = mtrr_add_page (base, size, type, 1); + if (reg >= 0) ++fcount[reg]; + return reg; +} /* End Function mtrr_file_add */ + +static int mtrr_file_del (unsigned long base, unsigned long size, + struct file *file, int page) +{ + int reg; + unsigned int *fcount = file->private_data; + + if (!page) { + if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + base >>= PAGE_SHIFT; + size >>= PAGE_SHIFT; + } + reg = mtrr_del_page (-1, base, size); + if (reg < 0) return reg; + if (fcount == NULL) return reg; + if (fcount[reg] < 1) return -EINVAL; + --fcount[reg]; + return reg; +} /* End Function mtrr_file_del */ + +static ssize_t mtrr_read (struct file *file, char *buf, size_t len, + loff_t *ppos) +{ + if (*ppos >= ascii_buf_bytes) return 0; + if (*ppos + len > ascii_buf_bytes) len = ascii_buf_bytes - *ppos; + if ( copy_to_user (buf, ascii_buffer + *ppos, len) ) return -EFAULT; + *ppos += len; + return len; +} /* End Function mtrr_read */ + +static ssize_t mtrr_write (struct file *file, const char *buf, size_t len, + loff_t *ppos) +/* Format of control line: + "base=%Lx size=%Lx type=%s" OR: + "disable=%d" +*/ +{ + int i, err; + unsigned long reg; + unsigned long long base, size; + char *ptr; + char line[LINE_SIZE]; + + if ( !suser () ) return -EPERM; + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) return -ESPIPE; + memset (line, 0, LINE_SIZE); + if (len > LINE_SIZE) len = LINE_SIZE; + if ( copy_from_user (line, buf, len - 1) ) return -EFAULT; + ptr = line + strlen (line) - 1; + if (*ptr == '\n') *ptr = '\0'; + if ( !strncmp (line, "disable=", 8) ) + { + reg = simple_strtoul (line + 8, &ptr, 0); + err = mtrr_del_page (reg, 0, 0); + if (err < 0) return err; + return len; + } + if ( strncmp (line, "base=", 5) ) + { + printk ("mtrr: no \"base=\" in line: \"%s\"\n", line); + return -EINVAL; + } + base = simple_strtoull (line + 5, &ptr, 0); + for (; isspace (*ptr); ++ptr); + if ( strncmp (ptr, "size=", 5) ) + { + printk ("mtrr: no \"size=\" in line: \"%s\"\n", line); + return -EINVAL; + } + size = simple_strtoull (ptr + 5, &ptr, 0); + if ( (base & 0xfff) || (size & 0xfff) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: 0x%Lx base: 0x%Lx\n", size, base); + return -EINVAL; + } + for (; isspace (*ptr); ++ptr); + if ( strncmp (ptr, "type=", 5) ) + { + printk ("mtrr: no \"type=\" in line: \"%s\"\n", line); + return -EINVAL; + } + ptr += 5; + for (; isspace (*ptr); ++ptr); + for (i = 0; i < MTRR_NUM_TYPES; ++i) + { + if ( strcmp (ptr, mtrr_strings[i]) ) continue; + base >>= PAGE_SHIFT; + size >>= PAGE_SHIFT; + err = mtrr_add_page ((unsigned long)base, (unsigned long)size, i, 1); + if (err < 0) return err; + return len; + } + printk ("mtrr: illegal type: \"%s\"\n", ptr); + return -EINVAL; +} /* End Function mtrr_write */ + +static int mtrr_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err; + mtrr_type type; + struct mtrr_sentry sentry; + struct mtrr_gentry gentry; + + switch (cmd) + { + default: + return -ENOIOCTLCMD; + case MTRRIOC_ADD_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file, 0); + if (err < 0) return err; + break; + case MTRRIOC_SET_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_add (sentry.base, sentry.size, sentry.type, 0); + if (err < 0) return err; + break; + case MTRRIOC_DEL_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_file_del (sentry.base, sentry.size, file, 0); + if (err < 0) return err; + break; + case MTRRIOC_KILL_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_del (-1, sentry.base, sentry.size); + if (err < 0) return err; + break; + case MTRRIOC_GET_ENTRY: + if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) ) + return -EFAULT; + if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL; + (*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type); + + /* Hide entries that go above 4GB */ + if (gentry.base + gentry.size > 0x100000 || gentry.size == 0x100000) + gentry.base = gentry.size = gentry.type = 0; + else { + gentry.base <<= PAGE_SHIFT; + gentry.size <<= PAGE_SHIFT; + gentry.type = type; + } + + if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) ) + return -EFAULT; + break; + case MTRRIOC_ADD_PAGE_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file, 1); + if (err < 0) return err; + break; + case MTRRIOC_SET_PAGE_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_add_page (sentry.base, sentry.size, sentry.type, 0); + if (err < 0) return err; + break; + case MTRRIOC_DEL_PAGE_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_file_del (sentry.base, sentry.size, file, 1); + if (err < 0) return err; + break; + case MTRRIOC_KILL_PAGE_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_del_page (-1, sentry.base, sentry.size); + if (err < 0) return err; + break; + case MTRRIOC_GET_PAGE_ENTRY: + if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) ) + return -EFAULT; + if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL; + (*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type); + gentry.type = type; + + if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) ) + return -EFAULT; + break; + } + return 0; +} /* End Function mtrr_ioctl */ + +static int mtrr_close (struct inode *ino, struct file *file) +{ + int i, max; + unsigned int *fcount = file->private_data; + + if (fcount == NULL) return 0; + max = get_num_var_ranges (); + for (i = 0; i < max; ++i) + { + while (fcount[i] > 0) + { + if (mtrr_del (i, 0, 0) < 0) printk ("mtrr: reg %d not used\n", i); + --fcount[i]; + } + } + kfree (fcount); + file->private_data = NULL; + return 0; +} /* End Function mtrr_close */ + +static struct file_operations mtrr_fops = +{ + owner: THIS_MODULE, + read: mtrr_read, + write: mtrr_write, + ioctl: mtrr_ioctl, + release: mtrr_close, +}; + +# ifdef CONFIG_PROC_FS + +static struct proc_dir_entry *proc_root_mtrr; + +# endif /* CONFIG_PROC_FS */ + +static devfs_handle_t devfs_handle; + +static void compute_ascii (void) +{ + char factor; + int i, max; + mtrr_type type; + unsigned long base, size; + + ascii_buf_bytes = 0; + max = get_num_var_ranges (); + for (i = 0; i < max; i++) + { + (*get_mtrr) (i, &base, &size, &type); + if (size == 0) usage_table[i] = 0; + else + { + if (size < (0x100000 >> PAGE_SHIFT)) + { + /* less than 1MB */ + factor = 'K'; + size <<= PAGE_SHIFT - 10; + } + else + { + factor = 'M'; + size >>= 20 - PAGE_SHIFT; + } + sprintf + (ascii_buffer + ascii_buf_bytes, + "reg%02i: base=0x%05lx000 (%4liMB), size=%4li%cB: %s, count=%d\n", + i, base, base >> (20 - PAGE_SHIFT), size, factor, + attrib_to_str (type), usage_table[i]); + ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes); + } + } + devfs_set_file_size (devfs_handle, ascii_buf_bytes); +# ifdef CONFIG_PROC_FS + if (proc_root_mtrr) + proc_root_mtrr->size = ascii_buf_bytes; +# endif /* CONFIG_PROC_FS */ +} /* End Function compute_ascii */ + +#endif /* USERSPACE_INTERFACE */ + +EXPORT_SYMBOL(mtrr_add); +EXPORT_SYMBOL(mtrr_del); + +#ifdef CONFIG_SMP + +typedef struct +{ + unsigned long base; + unsigned long size; + mtrr_type type; +} arr_state_t; + +arr_state_t arr_state[8] __initdata = +{ + {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, + {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL} +}; + +unsigned char ccr_state[7] __initdata = { 0, 0, 0, 0, 0, 0, 0 }; + +static void __init cyrix_arr_init_secondary(void) +{ + struct set_mtrr_context ctxt; + int i; + + /* flush cache and enable MAPEN */ + set_mtrr_prepare_save (&ctxt); + set_mtrr_cache_disable (&ctxt); + + /* the CCRs are not contiguous */ + for(i=0; i<4; i++) setCx86(CX86_CCR0 + i, ccr_state[i]); + for( ; i<7; i++) setCx86(CX86_CCR4 + i, ccr_state[i]); + for(i=0; i<8; i++) + cyrix_set_arr_up(i, + arr_state[i].base, arr_state[i].size, arr_state[i].type, FALSE); + + set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */ +} /* End Function cyrix_arr_init_secondary */ + +#endif + +/* + * On Cyrix 6x86(MX) and M II the ARR3 is special: it has connection + * with the SMM (System Management Mode) mode. So we need the following: + * Check whether SMI_LOCK (CCR3 bit 0) is set + * if it is set, write a warning message: ARR3 cannot be changed! + * (it cannot be changed until the next processor reset) + * if it is reset, then we can change it, set all the needed bits: + * - disable access to SMM memory through ARR3 range (CCR1 bit 7 reset) + * - disable access to SMM memory (CCR1 bit 2 reset) + * - disable SMM mode (CCR1 bit 1 reset) + * - disable write protection of ARR3 (CCR6 bit 1 reset) + * - (maybe) disable ARR3 + * Just to be sure, we enable ARR usage by the processor (CCR5 bit 5 set) + */ +static void __init cyrix_arr_init(void) +{ + struct set_mtrr_context ctxt; + unsigned char ccr[7]; + int ccrc[7] = { 0, 0, 0, 0, 0, 0, 0 }; +#ifdef CONFIG_SMP + int i; +#endif + + /* flush cache and enable MAPEN */ + set_mtrr_prepare_save (&ctxt); + set_mtrr_cache_disable (&ctxt); + + /* Save all CCRs locally */ + ccr[0] = getCx86 (CX86_CCR0); + ccr[1] = getCx86 (CX86_CCR1); + ccr[2] = getCx86 (CX86_CCR2); + ccr[3] = ctxt.ccr3; + ccr[4] = getCx86 (CX86_CCR4); + ccr[5] = getCx86 (CX86_CCR5); + ccr[6] = getCx86 (CX86_CCR6); + + if (ccr[3] & 1) + { + ccrc[3] = 1; + arr3_protected = 1; + } + else + { + /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and + * access to SMM memory through ARR3 (bit 7). + */ + if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; } + if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; } + if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; } + arr3_protected = 0; + if (ccr[6] & 0x02) { + ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3 */ + setCx86 (CX86_CCR6, ccr[6]); + } + /* Disable ARR3. This is safe now that we disabled SMM. */ + /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */ + } + /* If we changed CCR1 in memory, change it in the processor, too. */ + if (ccrc[1]) setCx86 (CX86_CCR1, ccr[1]); + + /* Enable ARR usage by the processor */ + if (!(ccr[5] & 0x20)) + { + ccr[5] |= 0x20; ccrc[5] = 1; + setCx86 (CX86_CCR5, ccr[5]); + } + +#ifdef CONFIG_SMP + for(i=0; i<7; i++) ccr_state[i] = ccr[i]; + for(i=0; i<8; i++) + cyrix_get_arr(i, + &arr_state[i].base, &arr_state[i].size, &arr_state[i].type); +#endif + + set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */ + + if ( ccrc[5] ) printk ("mtrr: ARR usage was not enabled, enabled manually\n"); + if ( ccrc[3] ) printk ("mtrr: ARR3 cannot be changed\n"); +/* + if ( ccrc[1] & 0x80) printk ("mtrr: SMM memory access through ARR3 disabled\n"); + if ( ccrc[1] & 0x04) printk ("mtrr: SMM memory access disabled\n"); + if ( ccrc[1] & 0x02) printk ("mtrr: SMM mode disabled\n"); +*/ + if ( ccrc[6] ) printk ("mtrr: ARR3 was write protected, unprotected\n"); +} /* End Function cyrix_arr_init */ + +/* + * Initialise the later (saner) Winchip MCR variant. In this version + * the BIOS can pass us the registers it has used (but not their values) + * and the control register is read/write + */ + +static void __init centaur_mcr1_init(void) +{ + unsigned i; + u32 lo, hi; + + /* Unfortunately, MCR's are read-only, so there is no way to + * find out what the bios might have done. + */ + + rdmsr(0x120, lo, hi); + if(((lo>>17)&7)==1) /* Type 1 Winchip2 MCR */ + { + lo&= ~0x1C0; /* clear key */ + lo|= 0x040; /* set key to 1 */ + wrmsr(0x120, lo, hi); /* unlock MCR */ + } + + centaur_mcr_type = 1; + + /* + * Clear any unconfigured MCR's. + */ + + for (i = 0; i < 8; ++i) + { + if(centaur_mcr[i]. high == 0 && centaur_mcr[i].low == 0) + { + if(!(lo & (1<<(9+i)))) + wrmsr (0x110 + i , 0, 0); + else + /* + * If the BIOS set up an MCR we cannot see it + * but we don't wish to obliterate it + */ + centaur_mcr_reserved |= (1<= 0x80000008)) { + u32 phys_addr; + phys_addr = cpuid_eax(0x80000008) & 0xff ; + size_or_mask = ~((1 << (phys_addr - PAGE_SHIFT)) - 1); + size_and_mask = ~size_or_mask & 0xfff00000; + break; + } + size_or_mask = 0xff000000; /* 36 bits */ + size_and_mask = 0x00f00000; + break; + + case X86_VENDOR_CENTAUR: + /* VIA Cyrix family have Intel style MTRRs, but don't support PAE */ + if (boot_cpu_data.x86 == 6) { + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } + break; + + default: + /* Intel, etc. */ + size_or_mask = 0xff000000; /* 36 bits */ + size_and_mask = 0x00f00000; + break; + } + + } else if ( test_bit(X86_FEATURE_K6_MTRR, &boot_cpu_data.x86_capability) ) { + /* Pre-Athlon (K6) AMD CPU MTRRs */ + mtrr_if = MTRR_IF_AMD_K6; + get_mtrr = amd_get_mtrr; + set_mtrr_up = amd_set_mtrr_up; + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } else if ( test_bit(X86_FEATURE_CYRIX_ARR, &boot_cpu_data.x86_capability) ) { + /* Cyrix ARRs */ + mtrr_if = MTRR_IF_CYRIX_ARR; + get_mtrr = cyrix_get_arr; + set_mtrr_up = cyrix_set_arr_up; + get_free_region = cyrix_get_free_region; + cyrix_arr_init(); + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } else if ( test_bit(X86_FEATURE_CENTAUR_MCR, &boot_cpu_data.x86_capability) ) { + /* Centaur MCRs */ + mtrr_if = MTRR_IF_CENTAUR_MCR; + get_mtrr = centaur_get_mcr; + set_mtrr_up = centaur_set_mcr_up; + get_free_region = centaur_get_free_region; + centaur_mcr_init(); + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } else { + /* No supported MTRR interface */ + mtrr_if = MTRR_IF_NONE; + } + + printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n" + "mtrr: detected mtrr type: %s\n", + MTRR_VERSION, mtrr_if_name[mtrr_if]); + + return (mtrr_if != MTRR_IF_NONE); +} /* End Function mtrr_setup */ + +#ifdef CONFIG_SMP + +static volatile unsigned long smp_changes_mask __initdata = 0; +static struct mtrr_state smp_mtrr_state __initdata = {0, 0}; + +void __init mtrr_init_boot_cpu(void) +{ + if ( !mtrr_setup () ) + return; + + if ( mtrr_if == MTRR_IF_INTEL ) { + /* Only for Intel MTRRs */ + get_mtrr_state (&smp_mtrr_state); + } +} /* End Function mtrr_init_boot_cpu */ + +static void __init intel_mtrr_init_secondary_cpu(void) +{ + unsigned long mask, count; + struct set_mtrr_context ctxt; + + /* Note that this is not ideal, since the cache is only flushed/disabled + for this CPU while the MTRRs are changed, but changing this requires + more invasive changes to the way the kernel boots */ + set_mtrr_prepare_save (&ctxt); + set_mtrr_cache_disable (&ctxt); + mask = set_mtrr_state (&smp_mtrr_state, &ctxt); + set_mtrr_done (&ctxt); + /* Use the atomic bitops to update the global mask */ + for (count = 0; count < sizeof mask * 8; ++count) + { + if (mask & 0x01) set_bit (count, &smp_changes_mask); + mask >>= 1; + } +} /* End Function intel_mtrr_init_secondary_cpu */ + +void __init mtrr_init_secondary_cpu(void) +{ + switch ( mtrr_if ) { + case MTRR_IF_INTEL: + /* Intel (P6) standard MTRRs */ + intel_mtrr_init_secondary_cpu(); + break; + case MTRR_IF_CYRIX_ARR: + /* This is _completely theoretical_! + * I assume here that one day Cyrix will support Intel APIC. + * In reality on non-Intel CPUs we won't even get to this routine. + * Hopefully no one will plug two Cyrix processors in a dual P5 board. + * :-) + */ + cyrix_arr_init_secondary (); + break; + default: + /* I see no MTRRs I can support in SMP mode... */ + printk ("mtrr: SMP support incomplete for this vendor\n"); + } +} /* End Function mtrr_init_secondary_cpu */ +#endif /* CONFIG_SMP */ + +int __init mtrr_init(void) +{ +#ifdef CONFIG_SMP + /* mtrr_setup() should already have been called from mtrr_init_boot_cpu() */ + + if ( mtrr_if == MTRR_IF_INTEL ) { + finalize_mtrr_state (&smp_mtrr_state); + mtrr_state_warn (smp_changes_mask); + } +#else + if ( !mtrr_setup() ) + return 0; /* MTRRs not supported? */ +#endif + +#ifdef CONFIG_PROC_FS + proc_root_mtrr = create_proc_entry ("mtrr", S_IWUSR | S_IRUGO, &proc_root); + if (proc_root_mtrr) { + proc_root_mtrr->owner = THIS_MODULE; + proc_root_mtrr->proc_fops = &mtrr_fops; + } +#endif + devfs_handle = devfs_register (NULL, "cpu/mtrr", DEVFS_FL_DEFAULT, 0, 0, + S_IFREG | S_IRUGO | S_IWUSR, + &mtrr_fops, NULL); + init_table (); + return 0; +} /* End Function mtrr_init */ + +/* + * Local Variables: + * mode:c + * c-file-style:"k&r" + * c-basic-offset:4 + * End: + */ diff -Nru a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/nmi.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,272 @@ +/* + * linux/arch/x86_64/nmi.c + * + * NMI watchdog support on APIC systems + * + * Started by Ingo Molnar + * + * Fixes: + * Mikael Pettersson : AMD K7 support for local APIC NMI watchdog. + * Mikael Pettersson : Power Management for local APIC NMI watchdog. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +unsigned int nmi_watchdog = NMI_NONE; +static unsigned int nmi_hz = HZ; +unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ +extern void show_registers(struct pt_regs *regs); + +#define K7_EVNTSEL_ENABLE (1 << 22) +#define K7_EVNTSEL_INT (1 << 20) +#define K7_EVNTSEL_OS (1 << 17) +#define K7_EVNTSEL_USR (1 << 16) +#define K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING 0x76 +#define K7_NMI_EVENT K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING + +#define P6_EVNTSEL0_ENABLE (1 << 22) +#define P6_EVNTSEL_INT (1 << 20) +#define P6_EVNTSEL_OS (1 << 17) +#define P6_EVNTSEL_USR (1 << 16) +#define P6_EVENT_CPU_CLOCKS_NOT_HALTED 0x79 +#define P6_NMI_EVENT P6_EVENT_CPU_CLOCKS_NOT_HALTED + +int __init check_nmi_watchdog (void) +{ + int counts[NR_CPUS]; + int j, cpu; + + printk(KERN_INFO "testing NMI watchdog ... "); + + for (j = 0; j < NR_CPUS; ++j) + counts[j] = cpu_pda[cpu_logical_map(j)].__nmi_count; + sti(); + mdelay((10*1000)/nmi_hz); // wait 10 ticks + + for (j = 0; j < smp_num_cpus; j++) { + cpu = cpu_logical_map(j); + if (nmi_count(cpu) - counts[j] <= 5) { + printk("CPU#%d: NMI appears to be stuck!\n", cpu); + return -1; + } + } + printk("OK.\n"); + + /* now that we know it works we can reduce NMI frequency to + something more reasonable; makes a difference in some configs */ + if (nmi_watchdog == NMI_LOCAL_APIC) + nmi_hz = 1; + + return 0; +} + +static int __init setup_nmi_watchdog(char *str) +{ + int nmi; + + get_option(&str, &nmi); + + if (nmi >= NMI_INVALID) + return 0; + if (nmi == NMI_NONE) + nmi_watchdog = nmi; + /* + * If any other x86 CPU has a local APIC, then + * please test the NMI stuff there and send me the + * missing bits. Right now Intel P6 and AMD K7 only. + */ + if ((nmi == NMI_LOCAL_APIC) && + (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && + (boot_cpu_data.x86 == 6)) + nmi_watchdog = nmi; + if ((nmi == NMI_LOCAL_APIC) && + (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && + (boot_cpu_data.x86 == 6)) + nmi_watchdog = nmi; + /* + * We can enable the IO-APIC watchdog + * unconditionally. + */ + if (nmi == NMI_IO_APIC) + nmi_watchdog = nmi; + return 1; +} + +__setup("nmi_watchdog=", setup_nmi_watchdog); + +#ifdef CONFIG_PM + +#include + +struct pm_dev *nmi_pmdev; + +static void disable_apic_nmi_watchdog(void) +{ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + wrmsr(MSR_K7_EVNTSEL0, 0, 0); + break; + case X86_VENDOR_INTEL: + wrmsr(MSR_IA32_EVNTSEL0, 0, 0); + break; + } +} + +static int nmi_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + switch (rqst) { + case PM_SUSPEND: + disable_apic_nmi_watchdog(); + break; + case PM_RESUME: + setup_apic_nmi_watchdog(); + break; + } + return 0; +} + +static void nmi_pm_init(void) +{ + if (!nmi_pmdev) + nmi_pmdev = apic_pm_register(PM_SYS_DEV, 0, nmi_pm_callback); +} + +#define __pminit /*empty*/ + +#else /* CONFIG_PM */ + +static inline void nmi_pm_init(void) { } + +#define __pminit __init + +#endif /* CONFIG_PM */ + +/* + * Activate the NMI watchdog via the local APIC. + * Original code written by Keith Owens. + */ + +static void __pminit setup_k7_watchdog(void) +{ + int i; + unsigned int evntsel; + + nmi_perfctr_msr = MSR_K7_PERFCTR0; + + for(i = 0; i < 4; ++i) { + wrmsr(MSR_K7_EVNTSEL0+i, 0, 0); + wrmsr(MSR_K7_PERFCTR0+i, 0, 0); + } + + evntsel = K7_EVNTSEL_INT + | K7_EVNTSEL_OS + | K7_EVNTSEL_USR + | K7_NMI_EVENT; + + wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); + Dprintk("setting K7_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_K7_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1); + apic_write(APIC_LVTPC, APIC_DM_NMI); + evntsel |= K7_EVNTSEL_ENABLE; + wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); +} + +void __pminit setup_apic_nmi_watchdog (void) +{ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 != 6) + return; + setup_k7_watchdog(); + break; + default: + return; + } + nmi_pm_init(); +} + +static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED; + +/* + * the best way to detect whether a CPU has a 'hard lockup' problem + * is to check it's local APIC timer IRQ counts. If they are not + * changing then that CPU has some problem. + * + * as these watchdog NMI IRQs are generated on every CPU, we only + * have to check the current processor. + * + * since NMIs dont listen to _any_ locks, we have to be extremely + * careful not to rely on unsafe variables. The printk might lock + * up though, so we have to break up any console locks first ... + * [when there will be more tty-related locks, break them up + * here too!] + */ + +static unsigned int + last_irq_sums [NR_CPUS], + alert_counter [NR_CPUS]; + +void touch_nmi_watchdog (void) +{ + int i; + + /* + * Just reset the alert counters, (other CPUs might be + * spinning on locks we hold): + */ + for (i = 0; i < smp_num_cpus; i++) + alert_counter[i] = 0; +} + +void nmi_watchdog_tick (struct pt_regs * regs) +{ + + /* + * Since current_thread_info()-> is always on the stack, and we + * always switch the stack NMI-atomically, it's safe to use + * smp_processor_id(). + */ + int sum, cpu = smp_processor_id(); + + sum = apic_timer_irqs[cpu]; + + if (last_irq_sums[cpu] == sum) { + /* + * Ayiee, looks like this CPU is stuck ... + * wait a few IRQs (5 seconds) before doing the oops ... + */ + alert_counter[cpu]++; + if (alert_counter[cpu] == 5*nmi_hz) { + spin_lock(&nmi_print_lock); + /* + * We are in trouble anyway, lets at least try + * to get a message out. + */ + bust_spinlocks(1); + printk("NMI Watchdog detected LOCKUP on CPU%d, registers:\n", cpu); + show_registers(regs); + printk("console shuts up ...\n"); + console_silent(); + spin_unlock(&nmi_print_lock); + bust_spinlocks(0); + do_exit(SIGSEGV); + } + } else { + last_irq_sums[cpu] = sum; + alert_counter[cpu] = 0; + } + if (nmi_perfctr_msr) + wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1); +} diff -Nru a/arch/x86_64/kernel/pci-dma.c b/arch/x86_64/kernel/pci-dma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/pci-dma.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,33 @@ +/* + * Dynamic DMA mapping support. + */ + +#include +#include +#include +#include +#include + +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC; + + /* We need to always allocate below 4Gig. We probably need new + GPF mask to say that */ + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_phys(ret); + } + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} diff -Nru a/arch/x86_64/kernel/pci-irq.c b/arch/x86_64/kernel/pci-irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/pci-irq.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,753 @@ +/* + * Low-Level PCI Support for PC -- Routing of Interrupts + * + * (c) 1999--2000 Martin Mares + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pci-x86_64.h" + +#define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) +#define PIRQ_VERSION 0x0100 + +static struct irq_routing_table *pirq_table; + +/* + * Never use: 0, 1, 2 (timer, keyboard, and cascade) + * Avoid using: 13, 14 and 15 (FP error and IDE). + * Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse) + */ +unsigned int pcibios_irq_mask = 0xfff8; + +static int pirq_penalty[16] = { + 1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000, + 0, 0, 0, 0, 1000, 100000, 100000, 100000 +}; + +struct irq_router { + char *name; + u16 vendor, device; + int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq); + int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new); +}; + +/* + * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table. + */ + +static struct irq_routing_table * __init pirq_find_routing_table(void) +{ + u8 *addr; + struct irq_routing_table *rt; + int i; + u8 sum; + + for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) { + rt = (struct irq_routing_table *) addr; + if (rt->signature != PIRQ_SIGNATURE || + rt->version != PIRQ_VERSION || + rt->size % 16 || + rt->size < sizeof(struct irq_routing_table)) + continue; + sum = 0; + for(i=0; isize; i++) + sum += addr[i]; + if (!sum) { + DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt); + return rt; + } + } + return NULL; +} + +/* + * If we have a IRQ routing table, use it to search for peer host + * bridges. It's a gross hack, but since there are no other known + * ways how to get a list of buses, we have to go this way. + * + * [maybe x86-64 architecture should define way to query this info in + more reasonable way?] + */ + +static void __init pirq_peer_trick(void) +{ + struct irq_routing_table *rt = pirq_table; + u8 busmap[256]; + int i; + struct irq_info *e; + + memset(busmap, 0, sizeof(busmap)); + for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) { + e = &rt->slots[i]; +#ifdef DEBUG + { + int j; + DBG("%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot); + for(j=0; j<4; j++) + DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap); + DBG("\n"); + } +#endif + busmap[e->bus] = 1; + } + for(i=1; i<256; i++) + /* + * It might be a secondary bus, but in this case its parent is already + * known (ascending bus order) and therefore pci_scan_bus returns immediately. + */ + if (busmap[i] && pci_scan_bus(i, pci_root_bus->ops, NULL)) + printk(KERN_INFO "PCI: Discovered primary peer bus %02x [IRQ]\n", i); + pcibios_last_bus = -1; +} + +/* + * Code for querying and setting of IRQ routes on various interrupt routers. + */ + +static void eisa_set_level_irq(unsigned int irq) +{ + unsigned char mask = 1 << (irq & 7); + unsigned int port = 0x4d0 + (irq >> 3); + unsigned char val = inb(port); + + if (!(val & mask)) { + DBG(" -> edge"); + outb(val | mask, port); + } +} + +/* + * Common IRQ routing practice: nybbles in config space, + * offset by some magic constant. + */ +static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr) +{ + u8 x; + unsigned reg = offset + (nr >> 1); + + pci_read_config_byte(router, reg, &x); + return (nr & 1) ? (x >> 4) : (x & 0xf); +} + +static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val) +{ + u8 x; + unsigned reg = offset + (nr >> 1); + + pci_read_config_byte(router, reg, &x); + x = (nr & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val); + pci_write_config_byte(router, reg, x); +} + +/* + * ALI pirq entries are damn ugly, and completely undocumented. + * This has been figured out from pirq tables, and it's not a pretty + * picture. + */ +static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + static unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; + + return irqmap[read_config_nybble(router, 0x48, pirq-1)]; +} + +static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + static unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 }; + unsigned int val = irqmap[irq]; + + if (val) { + write_config_nybble(router, 0x48, pirq-1, val); + return 1; + } + return 0; +} + +/* + * The Intel PIIX4 pirq rules are fairly simple: "pirq" is + * just a pointer to the config space. + */ +static int pirq_piix_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + u8 x; + + pci_read_config_byte(router, pirq, &x); + return (x < 16) ? x : 0; +} + +static int pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + pci_write_config_byte(router, pirq, irq); + return 1; +} + +/* + * The VIA pirq rules are nibble-based, like ALI, + * but without the ugly irq number munging. + */ +static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + return read_config_nybble(router, 0x55, pirq); +} + +static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + write_config_nybble(router, 0x55, pirq, irq); + return 1; +} + +/* + * OPTI: high four bits are nibble pointer.. + * I wonder what the low bits do? + */ +static int pirq_opti_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + return read_config_nybble(router, 0xb8, pirq >> 4); +} + +static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + write_config_nybble(router, 0xb8, pirq >> 4, irq); + return 1; +} + +/* + * Cyrix: nibble offset 0x5C + */ +static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + return read_config_nybble(router, 0x5C, pirq-1); +} + +static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + write_config_nybble(router, 0x5C, pirq-1, irq); + return 1; +} + +/* + * PIRQ routing for SiS 85C503 router used in several SiS chipsets + * According to the SiS 5595 datasheet (preliminary V1.0, 12/24/1997) + * the related registers work as follows: + * + * general: one byte per re-routable IRQ, + * bit 7 IRQ mapping enabled (0) or disabled (1) + * bits [6:4] reserved + * bits [3:0] IRQ to map to + * allowed: 3-7, 9-12, 14-15 + * reserved: 0, 1, 2, 8, 13 + * + * individual registers in device config space: + * + * 0x41/0x42/0x43/0x44: PCI INT A/B/C/D - bits as in general case + * + * 0x61: IDEIRQ: bits as in general case - but: + * bits [6:5] must be written 01 + * bit 4 channel-select primary (0), secondary (1) + * + * 0x62: USBIRQ: bits as in general case - but: + * bit 4 OHCI function disabled (0), enabled (1) + * + * 0x6a: ACPI/SCI IRQ - bits as in general case + * + * 0x7e: Data Acq. Module IRQ - bits as in general case + * + * Apparently there are systems implementing PCI routing table using both + * link values 0x01-0x04 and 0x41-0x44 for PCI INTA..D, but register offsets + * like 0x62 as link values for USBIRQ e.g. So there is no simple + * "register = offset + pirq" relation. + * Currently we support PCI INTA..D and USBIRQ and try our best to handle + * both link mappings. + * IDE/ACPI/DAQ mapping is currently unsupported (left untouched as set by BIOS). + */ + +static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + u8 x; + int reg = pirq; + + switch(pirq) { + case 0x01: + case 0x02: + case 0x03: + case 0x04: + reg += 0x40; + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x62: + pci_read_config_byte(router, reg, &x); + if (reg != 0x62) + break; + if (!(x & 0x40)) + return 0; + break; + case 0x61: + case 0x6a: + case 0x7e: + printk(KERN_INFO "SiS pirq: advanced IDE/ACPI/DAQ mapping not yet implemented\n"); + return 0; + default: + printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq); + return 0; + } + return (x & 0x80) ? 0 : (x & 0x0f); +} + +static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + u8 x; + int reg = pirq; + + switch(pirq) { + case 0x01: + case 0x02: + case 0x03: + case 0x04: + reg += 0x40; + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x62: + x = (irq&0x0f) ? (irq&0x0f) : 0x80; + if (reg != 0x62) + break; + /* always mark OHCI enabled, as nothing else knows about this */ + x |= 0x40; + break; + case 0x61: + case 0x6a: + case 0x7e: + printk(KERN_INFO "advanced SiS pirq mapping not yet implemented\n"); + return 0; + default: + printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq); + return 0; + } + pci_write_config_byte(router, reg, x); + + return 1; +} + +/* + * VLSI: nibble offset 0x74 - educated guess due to routing table and + * config space of VLSI 82C534 PCI-bridge/router (1004:0102) + * Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard + * devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6 + * for the busbridge to the docking station. + */ + +static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + if (pirq > 8) { + printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq); + return 0; + } + return read_config_nybble(router, 0x74, pirq-1); +} + +static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + if (pirq > 8) { + printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq); + return 0; + } + write_config_nybble(router, 0x74, pirq-1, irq); + return 1; +} + +/* + * ServerWorks: PCI interrupts mapped to system IRQ lines through Index + * and Redirect I/O registers (0x0c00 and 0x0c01). The Index register + * format is (PCIIRQ## | 0x10), e.g.: PCIIRQ10=0x1a. The Redirect + * register is a straight binary coding of desired PIC IRQ (low nibble). + * + * The 'link' value in the PIRQ table is already in the correct format + * for the Index register. There are some special index values: + * 0x00 for ACPI (SCI), 0x01 for USB, 0x02 for IDE0, 0x04 for IDE1, + * and 0x03 for SMBus. + */ +static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + outb_p(pirq, 0xc00); + return inb(0xc01) & 0xf; +} + +static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + outb_p(pirq, 0xc00); + outb_p(irq, 0xc01); + return 1; +} + +/* Support for AMD756 PCI IRQ Routing + * Jhon H. Caicedo + * Jun/21/2001 0.2.0 Release, fixed to use "nybble" functions... (jhcaiced) + * Jun/19/2001 Alpha Release 0.1.0 (jhcaiced) + * The AMD756 pirq rules are nibble-based + * offset 0x56 0-3 PIRQA 4-7 PIRQB + * offset 0x57 0-3 PIRQC 4-7 PIRQD + */ +static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + u8 irq; + irq = 0; + if (pirq <= 4) + { + irq = read_config_nybble(router, 0x56, pirq - 1); + } + printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n", + dev->vendor, dev->device, pirq, irq); + return irq; +} + +static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n", + dev->vendor, dev->device, pirq, irq); + if (pirq <= 4) + { + write_config_nybble(router, 0x56, pirq - 1, irq); + } + return 1; +} + +#ifdef CONFIG_PCI_BIOS + +static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + struct pci_dev *bridge; + int pin = pci_get_interrupt_pin(dev, &bridge); + return pcibios_set_irq_routing(bridge, pin, irq); +} + +static struct irq_router pirq_bios_router = + { "BIOS", 0, 0, NULL, pirq_bios_set }; + +#endif + +static struct irq_router pirq_routers[] = { + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, pirq_piix_get, pirq_piix_set }, + + { "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pirq_ali_get, pirq_ali_set }, + + { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, pirq_via_get, pirq_via_set }, + { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, pirq_via_get, pirq_via_set }, + { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, pirq_via_get, pirq_via_set }, + + { "OPTI", PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C700, pirq_opti_get, pirq_opti_set }, + + { "NatSemi", PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, pirq_cyrix_get, pirq_cyrix_set }, + { "SIS", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, pirq_sis_get, pirq_sis_set }, + { "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set }, + { "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4, + pirq_serverworks_get, pirq_serverworks_set }, + { "AMD756 VIPER", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B, + pirq_amd756_get, pirq_amd756_set }, + + { "default", 0, 0, NULL, NULL } +}; + +static struct irq_router *pirq_router; +static struct pci_dev *pirq_router_dev; + +static void __init pirq_find_router(void) +{ + struct irq_routing_table *rt = pirq_table; + struct irq_router *r; + +#ifdef CONFIG_PCI_BIOS + if (!rt->signature) { + printk(KERN_INFO "PCI: Using BIOS for IRQ routing\n"); + pirq_router = &pirq_bios_router; + return; + } +#endif + + DBG("PCI: Attempting to find IRQ router for %04x:%04x\n", + rt->rtr_vendor, rt->rtr_device); + + /* fall back to default router if nothing else found */ + pirq_router = &pirq_routers[ARRAY_SIZE(pirq_routers) - 1]; + + pirq_router_dev = pci_find_slot(rt->rtr_bus, rt->rtr_devfn); + if (!pirq_router_dev) { + DBG("PCI: Interrupt router not found at %02x:%02x\n", rt->rtr_bus, rt->rtr_devfn); + return; + } + + for(r=pirq_routers; r->vendor; r++) { + /* Exact match against router table entry? Use it! */ + if (r->vendor == rt->rtr_vendor && r->device == rt->rtr_device) { + pirq_router = r; + break; + } + /* Match against router device entry? Use it as a fallback */ + if (r->vendor == pirq_router_dev->vendor && r->device == pirq_router_dev->device) { + pirq_router = r; + } + } + printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n", + pirq_router->name, + pirq_router_dev->vendor, + pirq_router_dev->device, + pirq_router_dev->slot_name); +} + +static struct irq_info *pirq_get_info(struct pci_dev *dev) +{ + struct irq_routing_table *rt = pirq_table; + int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); + struct irq_info *info; + + for (info = rt->slots; entries--; info++) + if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn)) + return info; + return NULL; +} + +static void pcibios_test_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int pcibios_lookup_irq(struct pci_dev *dev, int assign) +{ + u8 pin; + struct irq_info *info; + int i, pirq, newirq; + int irq = 0; + u32 mask; + struct irq_router *r = pirq_router; + struct pci_dev *dev2; + char *msg = NULL; + + if (!pirq_table) + return 0; + + /* Find IRQ routing entry */ + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (!pin) { + DBG(" -> no interrupt pin\n"); + return 0; + } + pin = pin - 1; + + DBG("IRQ for %s:%d", dev->slot_name, pin); + info = pirq_get_info(dev); + if (!info) { + DBG(" -> not found in routing table\n"); + return 0; + } + pirq = info->irq[pin].link; + mask = info->irq[pin].bitmap; + if (!pirq) { + DBG(" -> not routed\n"); + return 0; + } + DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs); + mask &= pcibios_irq_mask; + + /* + * Find the best IRQ to assign: use the one + * reported by the device if possible. + */ + newirq = dev->irq; + if (!newirq && assign) { + for (i = 0; i < 16; i++) { + if (!(mask & (1 << i))) + continue; + if (pirq_penalty[i] < pirq_penalty[newirq] && + !request_irq(i, pcibios_test_irq_handler, SA_SHIRQ, "pci-test", dev)) { + free_irq(i, dev); + newirq = i; + } + } + } + DBG(" -> newirq=%d", newirq); + + /* Check if it is hardcoded */ + if ((pirq & 0xf0) == 0xf0) { + irq = pirq & 0xf; + DBG(" -> hardcoded IRQ %d\n", irq); + msg = "Hardcoded"; + } else if (r->get && (irq = r->get(pirq_router_dev, dev, pirq))) { + DBG(" -> got IRQ %d\n", irq); + msg = "Found"; + } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { + DBG(" -> assigning IRQ %d", newirq); + if (r->set(pirq_router_dev, dev, pirq, newirq)) { + eisa_set_level_irq(newirq); + DBG(" ... OK\n"); + msg = "Assigned"; + irq = newirq; + } + } + + if (!irq) { + DBG(" ... failed\n"); + if (newirq && mask == (1 << newirq)) { + msg = "Guessed"; + irq = newirq; + } else + return 0; + } + printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, dev->slot_name); + + /* Update IRQ for all devices with the same pirq value */ + pci_for_each_dev(dev2) { + pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin); + if (!pin) + continue; + pin--; + info = pirq_get_info(dev2); + if (!info) + continue; + if (info->irq[pin].link == pirq) { + /* We refuse to override the dev->irq information. Give a warning! */ + if (dev2->irq && dev2->irq != irq) { + printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n", + dev2->slot_name, dev2->irq, irq); + continue; + } + dev2->irq = irq; + pirq_penalty[irq]++; + if (dev != dev2) + printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, dev2->slot_name); + } + } + return 1; +} + +void __init pcibios_irq_init(void) +{ + DBG("PCI: IRQ init\n"); + pirq_table = pirq_find_routing_table(); +#ifdef CONFIG_PCI_BIOS + if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN)) + pirq_table = pcibios_get_irq_routing_table(); +#endif + if (pirq_table) { + pirq_peer_trick(); + pirq_find_router(); + if (pirq_table->exclusive_irqs) { + int i; + for (i=0; i<16; i++) + if (!(pirq_table->exclusive_irqs & (1 << i))) + pirq_penalty[i] += 100; + } + /* If we're using the I/O APIC, avoid using the PCI IRQ routing table */ + if (io_apic_assign_pci_irqs) + pirq_table = NULL; + } +} + +void __init pcibios_fixup_irqs(void) +{ + struct pci_dev *dev; + u8 pin; + + DBG("PCI: IRQ fixup\n"); + pci_for_each_dev(dev) { + /* + * If the BIOS has set an out of range IRQ number, just ignore it. + * Also keep track of which IRQ's are already in use. + */ + if (dev->irq >= 16) { + DBG("%s: ignoring bogus IRQ %d\n", dev->slot_name, dev->irq); + dev->irq = 0; + } + /* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */ + if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000) + pirq_penalty[dev->irq] = 0; + pirq_penalty[dev->irq]++; + } + + pci_for_each_dev(dev) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); +#ifdef CONFIG_X86_IO_APIC + /* + * Recalculate IRQ numbers if we use the I/O APIC. + */ + if (io_apic_assign_pci_irqs) + { + int irq; + + if (pin) { + pin--; /* interrupt pins are numbered starting from 1 */ + irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin); + /* + * Busses behind bridges are typically not listed in the MP-table. + * In this case we have to look up the IRQ based on the parent bus, + * parent slot, and pin number. The SMP code detects such bridged + * busses itself so we should get into this branch reliably. + */ + if (irq < 0 && dev->bus->parent) { /* go back to the bridge */ + struct pci_dev * bridge = dev->bus->self; + + pin = (pin + PCI_SLOT(dev->devfn)) % 4; + irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, + PCI_SLOT(bridge->devfn), pin); + if (irq >= 0) + printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", + bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq); + } + if (irq >= 0) { + printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", + dev->bus->number, PCI_SLOT(dev->devfn), pin, irq); + dev->irq = irq; + } + } + } +#endif + /* + * Still no IRQ? Try to lookup one... + */ + if (pin && !dev->irq) + pcibios_lookup_irq(dev, 0); + } +} + +void pcibios_penalize_isa_irq(int irq) +{ + /* + * If any ISAPnP device reports an IRQ in its list of possible + * IRQ's, we try to avoid assigning it to PCI devices. + */ + pirq_penalty[irq] += 100; +} + +void pcibios_enable_irq(struct pci_dev *dev) +{ + u8 pin; + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) { + char *msg; + if (io_apic_assign_pci_irqs) + msg = " Probably buggy MP table."; + else if (pci_probe & PCI_BIOS_IRQ_SCAN) + msg = ""; + else + msg = " Please try using pci=biosirq."; + printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", + 'A' + pin - 1, dev->slot_name, msg); + } +} diff -Nru a/arch/x86_64/kernel/pci-pc.c b/arch/x86_64/kernel/pci-pc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/pci-pc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,438 @@ +/* + * Low-Level PCI Support for PC + * + * (c) 1999--2000 Martin Mares + * 2001 Andi Kleen. Cleanup for x86-64. Removed PCI-BIOS access and fixups + * for hardware that is unlikely to exist on any Hammer platform. + * + * On x86-64 we don't have any access to the PCI-BIOS in long mode, so we + * cannot sort the pci device table based on what the BIOS did. This might + * change the probing order of some devices compared to an i386 kernel. + * May need to use ACPI to fix this. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pci-x86_64.h" + +unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2; + +int pcibios_last_bus = -1; +struct pci_bus *pci_root_bus; +struct pci_ops *pci_root_ops; + +/* + * Direct access to PCI hardware... + */ + +#ifdef CONFIG_PCI_DIRECT + +/* + * Functions for accessing PCI configuration space with type 1 accesses + */ + +#define CONFIG_CMD(dev, where) (0x80000000 | (dev->bus->number << 16) | (dev->devfn << 8) | (where & ~3)) + +static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + *value = inb(0xCFC + (where&3)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + *value = inw(0xCFC + (where&2)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + *value = inl(0xCFC); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + outb(value, 0xCFC + (where&3)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + outw(value, 0xCFC + (where&2)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + outl(value, 0xCFC); + return PCIBIOS_SUCCESSFUL; +} + +#undef CONFIG_CMD + +static struct pci_ops pci_direct_conf1 = { + pci_conf1_read_config_byte, + pci_conf1_read_config_word, + pci_conf1_read_config_dword, + pci_conf1_write_config_byte, + pci_conf1_write_config_word, + pci_conf1_write_config_dword +}; + +/* + * Functions for accessing PCI configuration space with type 2 accesses + */ + +#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where) +#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0) +#define SET(dev) if (dev->devfn & 0x80) return PCIBIOS_DEVICE_NOT_FOUND; \ + outb(FUNC(dev->devfn), 0xCF8); \ + outb(dev->bus->number, 0xCFA); + +static int pci_conf2_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + SET(dev); + *value = inb(IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + SET(dev); + *value = inw(IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + SET(dev); + *value = inl (IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + SET(dev); + outb (value, IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + SET(dev); + outw (value, IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + SET(dev); + outl (value, IOADDR(dev->devfn,where)); + outb (0, 0xCF8); + return PCIBIOS_SUCCESSFUL; +} + +#undef SET +#undef IOADDR +#undef FUNC + +static struct pci_ops pci_direct_conf2 = { + pci_conf2_read_config_byte, + pci_conf2_read_config_word, + pci_conf2_read_config_dword, + pci_conf2_write_config_byte, + pci_conf2_write_config_word, + pci_conf2_write_config_dword +}; + +/* + * Before we decide to use direct hardware access mechanisms, we try to do some + * trivial checks to ensure it at least _seems_ to be working -- we just test + * whether bus 00 contains a host bridge (this is similar to checking + * techniques used in XFree86, but ours should be more reliable since we + * attempt to make use of direct access hints provided by the PCI BIOS). + * + * This should be close to trivial, but it isn't, because there are buggy + * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. + */ +static int __devinit pci_sanity_check(struct pci_ops *o) +{ + u16 x; + struct pci_bus bus; /* Fake bus and device */ + struct pci_dev dev; + + if (pci_probe & PCI_NO_CHECKS) + return 1; + bus.number = 0; + dev.bus = &bus; + for(dev.devfn=0; dev.devfn < 0x100; dev.devfn++) + if ((!o->read_word(&dev, PCI_CLASS_DEVICE, &x) && + (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) || + (!o->read_word(&dev, PCI_VENDOR_ID, &x) && + (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ))) + return 1; + DBG("PCI: Sanity check failed\n"); + return 0; +} + +static struct pci_ops * __devinit pci_check_direct(void) +{ + unsigned int tmp; + unsigned long flags; + + __save_flags(flags); __cli(); + + /* + * Check if configuration type 1 works. + */ + if (pci_probe & PCI_PROBE_CONF1) { + outb (0x01, 0xCFB); + tmp = inl (0xCF8); + outl (0x80000000, 0xCF8); + if (inl (0xCF8) == 0x80000000 && + pci_sanity_check(&pci_direct_conf1)) { + outl (tmp, 0xCF8); + __restore_flags(flags); + printk("PCI: Using configuration type 1\n"); + request_region(0xCF8, 8, "PCI conf1"); + return &pci_direct_conf1; + } + outl (tmp, 0xCF8); + } + + /* + * Check if configuration type 2 works. + */ + if (pci_probe & PCI_PROBE_CONF2) { + outb (0x00, 0xCFB); + outb (0x00, 0xCF8); + outb (0x00, 0xCFA); + if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00 && + pci_sanity_check(&pci_direct_conf2)) { + __restore_flags(flags); + printk("PCI: Using configuration type 2\n"); + request_region(0xCF8, 4, "PCI conf2"); + return &pci_direct_conf2; + } + } + + __restore_flags(flags); + return NULL; +} + +#endif + + +/* + * Several buggy motherboards address only 16 devices and mirror + * them to next 16 IDs. We try to detect this `feature' on all + * primary buses (those containing host bridges as they are + * expected to be unique) and remove the ghost devices. + */ + +static void __devinit pcibios_fixup_ghosts(struct pci_bus *b) +{ + struct list_head *ln, *mn; + struct pci_dev *d, *e; + int mirror = PCI_DEVFN(16,0); + int seen_host_bridge = 0; + int i; + + DBG("PCI: Scanning for ghost devices on bus %d\n", b->number); + for (ln=b->devices.next; ln != &b->devices; ln=ln->next) { + d = pci_dev_b(ln); + if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST) + seen_host_bridge++; + for (mn=ln->next; mn != &b->devices; mn=mn->next) { + e = pci_dev_b(mn); + if (e->devfn != d->devfn + mirror || + e->vendor != d->vendor || + e->device != d->device || + e->class != d->class) + continue; + for(i=0; iresource[i].start != d->resource[i].start || + e->resource[i].end != d->resource[i].end || + e->resource[i].flags != d->resource[i].flags) + continue; + break; + } + if (mn == &b->devices) + return; + } + if (!seen_host_bridge) + return; + printk("PCI: Ignoring ghost devices on bus %02x\n", b->number); + + ln = &b->devices; + while (ln->next != &b->devices) { + d = pci_dev_b(ln->next); + if (d->devfn >= mirror) { + list_del(&d->global_list); + list_del(&d->bus_list); + kfree(d); + } else + ln = ln->next; + } +} + +/* + * Discover remaining PCI buses in case there are peer host bridges. + * We use the number of last PCI bus provided by the PCI BIOS. + */ +static void __devinit pcibios_fixup_peer_bridges(void) +{ + int n; + struct pci_bus bus; + struct pci_dev dev; + u16 l; + + if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff) + return; + DBG("PCI: Peer bridge fixup\n"); + for (n=0; n <= pcibios_last_bus; n++) { + if (pci_bus_exists(&pci_root_buses, n)) + continue; + bus.number = n; + bus.ops = pci_root_ops; + dev.bus = &bus; + for(dev.devfn=0; dev.devfn<256; dev.devfn += 8) + if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) && + l != 0x0000 && l != 0xffff) { + DBG("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l); + printk("PCI: Discovered peer bus %02x\n", n); + pci_scan_bus(n, pci_root_ops, NULL); + break; + } + } +} + +struct pci_fixup pcibios_fixups[] = { + /* Currently no fixup for hammer systems. May need to readd them + as needed. */ + { 0 } +}; + +/* + * Called after each bus is probed, but before its children + * are examined. + */ + +void __devinit pcibios_fixup_bus(struct pci_bus *b) +{ + pcibios_fixup_ghosts(b); + pci_read_bridge_bases(b); +} + +/* + * Initialization. Try all known PCI access methods. Note that we support + * using both PCI BIOS and direct access: in such cases, we use I/O ports + * to access config space, but we still keep BIOS order of cards to be + * compatible with 2.0.X. This should go away some day. + */ + +void __devinit pcibios_init(void) +{ + struct pci_ops *bios = NULL; + struct pci_ops *dir = NULL; + +#ifdef CONFIG_PCI_DIRECT + if (pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2)) + dir = pci_check_direct(); +#endif + if (dir) + pci_root_ops = dir; + else if (bios) + pci_root_ops = bios; + else { + printk("PCI: No PCI bus detected\n"); + return; + } + + printk("PCI: Probing PCI hardware\n"); + pci_root_bus = pci_scan_bus(0, pci_root_ops, NULL); + + pcibios_irq_init(); + pcibios_fixup_peer_bridges(); + pcibios_fixup_irqs(); + pcibios_resource_survey(); + +} + +char * __devinit pcibios_setup(char *str) +{ + if (!strcmp(str, "off")) { + pci_probe = 0; + return NULL; + } + else if (!strncmp(str, "bios", 4)) { + printk("PCI: No PCI bios access on x86-64. BIOS hint ignored.\n"); + return NULL; + } else if (!strcmp(str, "nobios")) { + pci_probe &= ~PCI_PROBE_BIOS; + return NULL; + } else if (!strcmp(str, "nosort")) { /* Default */ + pci_probe |= PCI_NO_SORT; + return NULL; + } +#ifdef CONFIG_PCI_DIRECT + else if (!strcmp(str, "conf1")) { + pci_probe = PCI_PROBE_CONF1 | PCI_NO_CHECKS; + return NULL; + } + else if (!strcmp(str, "conf2")) { + pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS; + return NULL; + } +#endif + else if (!strcmp(str, "rom")) { + pci_probe |= PCI_ASSIGN_ROMS; + return NULL; + } else if (!strcmp(str, "assign-busses")) { + pci_probe |= PCI_ASSIGN_ALL_BUSSES; + return NULL; + } else if (!strncmp(str, "irqmask=", 8)) { + pcibios_irq_mask = simple_strtol(str+8, NULL, 0); + return NULL; + } else if (!strncmp(str, "lastbus=", 8)) { + pcibios_last_bus = simple_strtol(str+8, NULL, 0); + return NULL; + } + return str; +} + +unsigned int pcibios_assign_all_busses(void) +{ + return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0; +} + +int pcibios_enable_device(struct pci_dev *dev) +{ + int err; + + if ((err = pcibios_enable_resources(dev)) < 0) + return err; + pcibios_enable_irq(dev); + return 0; +} diff -Nru a/arch/x86_64/kernel/pci-x86_64.c b/arch/x86_64/kernel/pci-x86_64.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/pci-x86_64.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,384 @@ +/* + * Low-Level PCI Access for x86-64 machines + * + * Copyright 1993, 1994 Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * Drew@Colorado.EDU + * +1 (303) 786-7975 + * + * Drew's work was sponsored by: + * iX Multiuser Multitasking Magazine + * Hannover, Germany + * hm@ix.de + * + * Copyright 1997--2000 Martin Mares + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * + * CHANGELOG : + * Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION + * Revision 2.0 present on 's ASUS mainboard. + * + * Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic + * Potter, potter@cao-vlsi.ibp.fr + * + * Jan 10, 1995 : Modified to store the information about configured pci + * devices into a list, which can be accessed via /proc/pci by + * Curtis Varner, cvarner@cs.ucr.edu + * + * Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter. + * Alpha version. Intel & UMC chipset support only. + * + * Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code + * moved to drivers/pci/pci.c. + * + * Dec 7, 1996 : Added support for direct configuration access of boards + * with Intel compatible access schemes (tsbogend@alpha.franken.de) + * + * Feb 3, 1997 : Set internal functions to static, save/restore flags + * avoid dead locks reading broken PCI BIOS, werner@suse.de + * + * Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS + * (mj@atrey.karlin.mff.cuni.cz) + * + * May 7, 1997 : Added some missing cli()'s. [mj] + * + * Jun 20, 1997 : Corrected problems in "conf1" type accesses. + * (paubert@iram.es) + * + * Aug 2, 1997 : Split to PCI BIOS handling and direct PCI access parts + * and cleaned it up... Martin Mares + * + * Feb 6, 1998 : No longer using BIOS to find devices and device classes. [mj] + * + * May 1, 1998 : Support for peer host bridges. [mj] + * + * Jun 19, 1998 : Changed to use spinlocks, so that PCI configuration space + * can be accessed from interrupts even on SMP systems. [mj] + * + * August 1998 : Better support for peer host bridges and more paranoid + * checks for direct hardware access. Ugh, this file starts to look as + * a large gallery of common hardware bug workarounds (watch the comments) + * -- the PCI specs themselves are sane, but most implementors should be + * hit hard with \hammer scaled \magstep5. [mj] + * + * Jan 23, 1999 : More improvements to peer host bridge logic. i450NX fixup. [mj] + * + * Feb 8, 1999 : Added UM8886BF I/O address fixup. [mj] + * + * August 1999 : New resource management and configuration access stuff. [mj] + * + * Sep 19, 1999 : Use PCI IRQ routing tables for detection of peer host bridges. + * Based on ideas by Chris Frantz and David Hinds. [mj] + * + * Sep 28, 1999 : Handle unreported/unassigned IRQs. Thanks to Shuu Yamaguchi + * for a lot of patience during testing. [mj] + * + * Oct 8, 1999 : Split to pci-i386.c, pci-pc.c and pci-visws.c. [mj] + */ + +#include +#include +#include +#include +#include +#include + +#include "pci-x86_64.h" + +void +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + u32 new, check; + int reg; + + new = res->start | (res->flags & PCI_REGION_FLAG_MASK); + if (resource < 6) { + reg = PCI_BASE_ADDRESS_0 + 4*resource; + } else if (resource == PCI_ROM_RESOURCE) { + res->flags |= PCI_ROM_ADDRESS_ENABLE; + new |= PCI_ROM_ADDRESS_ENABLE; + reg = dev->rom_base_reg; + } else { + /* Somebody might have asked allocation of a non-standard resource */ + return; + } + + pci_write_config_dword(dev, reg, new); + pci_read_config_dword(dev, reg, &check); + if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { + printk(KERN_ERR "PCI: Error while updating region " + "%s/%d (%08x != %08x)\n", dev->slot_name, resource, + new, check); + } +} + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void +pcibios_align_resource(void *data, struct resource *res, unsigned long size) +{ + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ + +static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct list_head *ln; + struct pci_bus *bus; + struct pci_dev *dev; + int idx; + struct resource *r, *pr; + + /* Depth-First Search on bus tree */ + for (ln=bus_list->next; ln != bus_list; ln=ln->next) { + bus = pci_bus_b(ln); + if ((dev = bus->self)) { + for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { + r = &dev->resource[idx]; + if (!r->start) + continue; + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) + printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, dev->slot_name); + } + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +static void __init pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev; + int idx, disabled; + u16 command; + struct resource *r, *pr; + + pci_for_each_dev(dev) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for(idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->start) /* Address not assigned at all */ + continue; + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + DBG("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n", + r->start, r->end, r->flags, disabled, pass); + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + printk(KERN_ERR "PCI: Cannot allocate resource region %d of device %s\n", idx, dev->slot_name); + /* We'll assign a new address later */ + r->end -= r->start; + r->start = 0; + } + } + } + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & PCI_ROM_ADDRESS_ENABLE) { + /* Turn the ROM off, leave the resource region, but keep it unregistered. */ + u32 reg; + DBG("PCI: Switching off ROM of %s\n", dev->slot_name); + r->flags &= ~PCI_ROM_ADDRESS_ENABLE; + pci_read_config_dword(dev, dev->rom_base_reg, ®); + pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } + } +} + +static void __init pcibios_assign_resources(void) +{ + struct pci_dev *dev; + int idx; + struct resource *r; + + pci_for_each_dev(dev) { + int class = dev->class >> 8; + + /* Don't touch classless devices and host bridges */ + if (!class || class == PCI_CLASS_BRIDGE_HOST) + continue; + + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + + /* + * Don't touch IDE controllers and I/O ports of video cards! + */ + if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) || + (class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO))) + continue; + + /* + * We shall assign a new address to this resource, either because + * the BIOS forgot to do so or because we have decided the old + * address was unusable for some reason. + */ + if (!r->start && r->end) + pci_assign_resource(dev, idx); + } + + if (pci_probe & PCI_ASSIGN_ROMS) { + r = &dev->resource[PCI_ROM_RESOURCE]; + r->end -= r->start; + r->start = 0; + if (r->end) + pci_assign_resource(dev, PCI_ROM_RESOURCE); + } + } +} + +void __init pcibios_resource_survey(void) +{ + DBG("PCI: Allocating resources\n"); + pcibios_allocate_bus_resources(&pci_root_buses); + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); + pcibios_assign_resources(); +} + +int pcibios_enable_resources(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain crappy BIOSes forget to set it properly. + */ +unsigned int pcibios_max_latency = 255; + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + else + return; + printk("PCI: Setting latency timer of device %s to %d\n", dev->slot_name, lat); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} + +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + unsigned long prot; + + /* I/O space cannot be accessed via normal processor loads and + * stores on this platform. + */ + if (mmap_state == pci_mmap_io) + return -EINVAL; + + /* Leave vm_pgoff as-is, the PCI space address is the physical + * address on this platform. + */ + vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO); + + prot = pgprot_val(vma->vm_page_prot); + if (boot_cpu_data.x86 > 3) + prot |= _PAGE_PCD | _PAGE_PWT; + vma->vm_page_prot = __pgprot(prot); + + /* Write-combine setting is ignored, it is changed via the mtrr + * interfaces on this platform. + */ + if (remap_page_range(vma, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} diff -Nru a/arch/x86_64/kernel/pci-x86_64.h b/arch/x86_64/kernel/pci-x86_64.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/pci-x86_64.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,72 @@ +/* + * Low-Level PCI Access for i386 machines. + * + * (c) 1999 Martin Mares + */ + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define PCI_PROBE_BIOS 0x0001 +#define PCI_PROBE_CONF1 0x0002 +#define PCI_PROBE_CONF2 0x0004 +#define PCI_NO_SORT 0x0100 +#define PCI_BIOS_SORT 0x0200 +#define PCI_NO_CHECKS 0x0400 +#define PCI_ASSIGN_ROMS 0x1000 +#define PCI_BIOS_IRQ_SCAN 0x2000 +#define PCI_ASSIGN_ALL_BUSSES 0x4000 + +extern unsigned int pci_probe; + +/* pci-i386.c */ + +extern unsigned int pcibios_max_latency; + +void pcibios_resource_survey(void); +int pcibios_enable_resources(struct pci_dev *); + +/* pci-pc.c */ + +extern int pcibios_last_bus; +extern struct pci_bus *pci_root_bus; +extern struct pci_ops *pci_root_ops; + +struct irq_routing_table *pcibios_get_irq_routing_table(void); +int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); + +/* pci-irq.c */ + +struct irq_info { + u8 bus, devfn; /* Bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__((packed)) irq[4]; + u8 slot; /* Slot number, 0=onboard */ + u8 rfu; +} __attribute__((packed)); + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[0]; +} __attribute__((packed)); + +extern unsigned int pcibios_irq_mask; + +void pcibios_irq_init(void); +void pcibios_fixup_irqs(void); +void pcibios_enable_irq(struct pci_dev *dev); diff -Nru a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/process.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,756 @@ +/* + * linux/arch/x86-64/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + * + * Pentium III FXSR, SSE support + * Gareth Hughes , May 2000 + * + * X86-64 port + * Andi Kleen. + * + * $Id: process.c,v 1.38 2002/01/15 10:08:03 ak Exp $ + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#define __KERNEL_SYSCALLS__ +#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 +#include + +#include +#include + +asmlinkage extern void ret_from_fork(void); + +int hlt_counter; + +/* + * Powermanagement idle function, if any.. + */ +void (*pm_idle)(void); + +/* + * Power off function, if any + */ +void (*pm_power_off)(void); + +void disable_hlt(void) +{ + hlt_counter++; +} + +void enable_hlt(void) +{ + hlt_counter--; +} + +/* + * We use this if we don't have any better + * idle routine.. + */ +static void default_idle(void) +{ + if (!hlt_counter) { + __cli(); + if (!need_resched()) + safe_halt(); + else + __sti(); + } +} + +/* + * On SMP it's slightly faster (but much more power-consuming!) + * to poll the ->need_resched flag instead of waiting for the + * cross-CPU IPI to arrive. Use this option with caution. + */ +static void poll_idle (void) +{ + int oldval; + + __sti(); + + /* + * Deal with another CPU just having chosen a thread to + * run here: + */ + oldval = test_and_clear_thread_flag(TIF_NEED_RESCHED); + + if (!oldval) { + set_thread_flag(TIF_POLLING_NRFLAG); + asm volatile( + "2:" + "testl %0,%1;" + "rep; nop;" + "je 2b;" + : : + "i" (_TIF_NEED_RESCHED), + "m" (current_thread_info()->flags)); + } else { + set_need_resched(); + } +} + +/* + * The idle thread. There's no useful work to be + * done, so just try to conserve power and have a + * low exit latency (ie sit in a loop waiting for + * somebody to say that they'd like to reschedule) + */ +void cpu_idle (void) +{ + /* endless idle loop with no priority at all */ + while (1) { + void (*idle)(void) = pm_idle; + if (!idle) + idle = default_idle; + while (!need_resched()) + idle(); + schedule(); + check_pgt_cache(); + } +} + +static int __init idle_setup (char *str) +{ + if (!strncmp(str, "poll", 4)) { + printk("using polling idle threads.\n"); + pm_idle = poll_idle; + } + + return 1; +} + +__setup("idle=", idle_setup); + +static long no_idt[3]; +static int reboot_mode; +int reboot_thru_bios; + +#ifdef CONFIG_SMP +int reboot_smp = 0; +static int reboot_cpu = -1; +/* shamelessly grabbed from lib/vsprintf.c for readability */ +#define is_digit(c) ((c) >= '0' && (c) <= '9') +#endif +static int __init reboot_setup(char *str) +{ + while(1) { + switch (*str) { + case 'w': /* "warm" reboot (no memory testing etc) */ + reboot_mode = 0x1234; + break; + case 'c': /* "cold" reboot (with memory testing etc) */ + reboot_mode = 0x0; + break; + case 'b': /* "bios" reboot by jumping through the BIOS */ + reboot_thru_bios = 1; + break; + case 'h': /* "hard" reboot by toggling RESET and/or crashing the CPU */ + reboot_thru_bios = 0; + break; +#ifdef CONFIG_SMP + case 's': /* "smp" reboot by executing reset on BSP or other CPU*/ + reboot_smp = 1; + if (is_digit(*(str+1))) { + reboot_cpu = (int) (*(str+1) - '0'); + if (is_digit(*(str+2))) + reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0'); + } + /* we will leave sorting out the final value + when we are ready to reboot, since we might not + have set up boot_cpu_id or smp_num_cpu */ + break; +#endif + } + if((str = strchr(str,',')) != NULL) + str++; + else + break; + } + return 1; +} + +__setup("reboot=", reboot_setup); + +static inline void kb_wait(void) +{ + int i; + + for (i=0; i<0x10000; i++) + if ((inb_p(0x64) & 0x02) == 0) + break; +} + +/* + * Switch to real mode and then execute the code + * specified by the code and length parameters. + * We assume that length will aways be less that 100! + */ +void machine_real_restart(unsigned char *code, int length) +{ + cli(); + + /* This will have to be rewritten for sledgehammer. It would + help if sledgehammer have simple option to reset itself. + */ + + panic( "real_restart is hard to do.\n" ); + while(1); +} + +void machine_restart(char * __unused) +{ +#if CONFIG_SMP + int cpuid; + + cpuid = GET_APIC_ID(apic_read(APIC_ID)); + + if (reboot_smp) { + + /* check to see if reboot_cpu is valid + if its not, default to the BSP */ + if ((reboot_cpu == -1) || + (reboot_cpu > (NR_CPUS -1)) || + !(phys_cpu_present_map & (1<pid, current->comm, print_tainted()); + printk("RIP: %04lx:[<%016lx>]\n", regs->cs & 0xffff, regs->rip); + printk("RSP: %016lx EFLAGS: %08lx\n", regs->rsp, regs->eflags); + printk("RAX: %016lx RBX: %016lx RCX: %016lx\n", + regs->rax, regs->rbx, regs->rcx); + printk("RDX: %016lx RSI: %016lx RDI: %016lx\n", + regs->rdx, regs->rsi, regs->rdi); + printk("RBP: %016lx R08: %016lx R09: %08lx\n", + regs->rbp, regs->r8, regs->r9); + printk("R10: %016lx R11: %016lx R12: %016lx\n", + regs->r10, regs->r11, regs->r12); + printk("R13: %016lx R14: %016lx R15: %016lx\n", + regs->r13, regs->r14, regs->r15); + + asm("movl %%ds,%0" : "=r" (ds)); + asm("movl %%es,%0" : "=r" (es)); + asm("movl %%cs,%0" : "=r" (cs)); + asm("movl %%fs,%0" : "=r" (fsindex)); + asm("movl %%gs,%0" : "=r" (gsindex)); + + rdmsrl(0xc0000100, fs); + rdmsrl(0xc0000101, gs); + + asm("movq %%cr0, %0": "=r" (cr0)); + asm("movq %%cr2, %0": "=r" (cr2)); + asm("movq %%cr3, %0": "=r" (cr3)); + asm("movq %%cr4, %0": "=r" (cr4)); + + printk("FS: %016lx(%04x) GS:%016lx(%04x)\n", fs,fsindex,gs,gsindex); + printk("CS: %04x DS:%04x ES:%04x CR0: %016lx\n", cs, ds, es, cr0); + printk("CR2: %016lx CR3: %016lx CR4: %016lx\n", cr2, cr3, cr4); +} + +/* + * No need to lock the MM as we are the last user + */ +void release_segments(struct mm_struct *mm) +{ + void * ldt = mm->context.segments; + + /* + * free the LDT + */ + if (ldt) { + mm->context.segments = NULL; + clear_LDT(); + vfree(ldt); + } +} + +#define __STR(x) #x +#define __STR2(x) __STR(x) + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ + /* nothing to do ... */ +} + +void flush_thread(void) +{ + struct task_struct *tsk = current; + + memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8); + /* + * Forget coprocessor state.. + */ + clear_fpu(tsk); + tsk->used_math = 0; +} + +void release_thread(struct task_struct *dead_task) +{ + if (dead_task->mm) { + void * ldt = dead_task->mm->context.segments; + + // temporary debugging check + if (ldt) { + printk("WARNING: dead process %8s still has LDT? <%p>\n", + dead_task->comm, ldt); + BUG(); + } + } +} + +/* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. + */ +void copy_segments(struct task_struct *p, struct mm_struct *new_mm) +{ + struct mm_struct * old_mm; + void *old_ldt, *ldt; + + ldt = NULL; + old_mm = current->mm; + if (old_mm && (old_ldt = old_mm->context.segments) != NULL) { + /* + * Completely new LDT, we initialize it from the parent: + */ + ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); + if (!ldt) + printk(KERN_WARNING "ldt allocation failed\n"); + else + memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE); + } + new_mm->context.segments = ldt; + new_mm->context.cpuvalid = 0UL; + return; +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long rsp, + unsigned long unused, + struct task_struct * p, struct pt_regs * regs) +{ + struct pt_regs * childregs; + struct task_struct *me = current; + + childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p->thread_info)) - 1; + + *childregs = *regs; + + childregs->rax = 0; + childregs->rsp = rsp; + if (rsp == ~0) { + childregs->rsp = (unsigned long)childregs; + } + + p->thread.rsp = (unsigned long) childregs; + p->thread.rsp0 = (unsigned long) (childregs+1); + p->thread.userrsp = current->thread.userrsp; + + p->thread.rip = (unsigned long) ret_from_fork; + + p->thread.fs = me->thread.fs; + p->thread.gs = me->thread.gs; + + asm("movl %%gs,%0" : "=m" (p->thread.gsindex)); + asm("movl %%fs,%0" : "=m" (p->thread.fsindex)); + asm("movl %%es,%0" : "=m" (p->thread.es)); + asm("movl %%ds,%0" : "=m" (p->thread.ds)); + + unlazy_fpu(current); + p->thread.i387 = current->thread.i387; + + return 0; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + int i; + +/* changed the size calculations - should hopefully work better. lbt */ + dump->magic = CMAGIC; + dump->start_code = 0; + dump->start_stack = regs->rsp & ~(PAGE_SIZE - 1); + dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; + dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; + dump->u_dsize -= dump->u_tsize; + dump->u_ssize = 0; + for (i = 0; i < 8; i++) + dump->u_debugreg[i] = current->thread.debugreg[i]; + + if (dump->start_stack < TASK_SIZE) + dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; + +#define SAVE(reg) dump->regs.reg = regs->reg + SAVE(rax); + SAVE(rbx); + SAVE(rcx); + SAVE(rdx); + SAVE(rsi); + SAVE(rdi); + SAVE(rbp); + SAVE(r8); + SAVE(r9); + SAVE(r10); + SAVE(r11); + SAVE(r12); + SAVE(r13); + SAVE(r14); + SAVE(r15); + SAVE(orig_rax); + SAVE(rip); +#undef SAVE + + /* FIXME: Should use symbolic names for msr-s! */ + rdmsrl(0xc0000100, dump->regs.fs_base); + rdmsrl(0xc0000101, dump->regs.kernel_gs_base); + + dump->u_fpvalid = dump_fpu (regs, &dump->i387); +} + +/* + * This special macro can be used to load a debugging register + */ +#define loaddebug(thread,register) \ + set_debug(thread->debugreg[register], register) + +/* + * switch_to(x,y) should switch tasks from x to y. + * + * We fsave/fwait so that an exception goes off at the right time + * (as a call from the fsave or fwait in effect) rather than to + * the wrong process. + * + * This could still be optimized: + * - fold all the options into a flag word and test it with a single test. + * - could test fs/gs bitsliced + */ +void __switch_to(struct task_struct *prev_p, struct task_struct *next_p) +{ + struct thread_struct *prev = &prev_p->thread, + *next = &next_p->thread; + struct tss_struct *tss = init_tss + smp_processor_id(); + + unlazy_fpu(prev_p); + + /* + * Reload esp0, LDT and the page table pointer: + */ + tss->rsp0 = next->rsp0; + + /* + * Switch DS and ES. + * XXX: check if this is safe on SMP + */ + asm volatile("movl %%es,%0" : "=m" (prev->es)); + if (unlikely(next->es != prev->es)) + loadsegment(es, next->es); + + asm volatile ("movl %%ds,%0" : "=m" (prev->ds)); + if (unlikely(next->ds != prev->ds)) + loadsegment(ds, next->ds); + + /* + * Switch FS and GS. + */ + { + unsigned int fsindex; + + asm volatile("movl %%fs,%0" : "=g" (fsindex)); + if (unlikely(fsindex != prev->fsindex)) + prev->fs = 0; + if (unlikely((fsindex | next->fsindex) || prev->fs)) + loadsegment(fs, next->fsindex); + /* Should use a shortcut via a GDT entry if next->fs is 32bit */ + if (fsindex != prev->fsindex || next->fs != prev->fs) + wrmsrl(MSR_FS_BASE, next->fs); + prev->fsindex = fsindex; + } + + { + unsigned int gsindex; + + asm volatile("movl %%gs,%0" : "=g" (gsindex)); + if (unlikely(gsindex != prev->gsindex)) + prev->gs = 0; + if (unlikely((gsindex | next->gsindex) || prev->gs)) { + unsigned long flags; + /* could load gs in syscall exit after swapgs instead */ + int nr = smp_processor_id(); + __save_flags(flags); + __cli(); + loadsegment(gs, next->gsindex); + wrmsrl(MSR_GS_BASE, cpu_pda+nr); + __restore_flags(flags); + } + if (gsindex != prev->gsindex || (prev->gs | next->gs)) + wrmsrl(MSR_KERNEL_GS_BASE, next->gs); + prev->gsindex = gsindex; + } + + /* + * Switch the PDA context. + */ + prev->userrsp = read_pda(oldrsp); + write_pda(oldrsp, next->userrsp); + write_pda(pcurrent, next_p); + write_pda(kernelstack, + (unsigned long)next_p->thread_info + THREAD_SIZE - PDA_STACKOFFSET); + + /* + * Now maybe reload the debug registers + */ + if (unlikely(next->debugreg[7])) { + loaddebug(next, 0); + loaddebug(next, 1); + loaddebug(next, 2); + loaddebug(next, 3); + /* no 4 and 5 */ + loaddebug(next, 6); + loaddebug(next, 7); + } + + + /* + * Handle the IO bitmap + */ + if (unlikely(prev->ioperm | next->ioperm)) { + if (next->ioperm) { + /* + * 4 cachelines copy ... not good, but not that + * bad either. Anyone got something better? + * This only affects processes which use ioperm(). + * [Putting the TSSs into 4k-tlb mapped regions + * and playing VM tricks to switch the IO bitmap + * is not really acceptable.] + * On x86-64 we could put multiple bitmaps into + * the GDT and just switch offsets + * This would require ugly special cases on overflow + * though -AK + */ + memcpy(tss->io_bitmap, next->io_bitmap, + IO_BITMAP_SIZE*sizeof(u32)); + tss->io_map_base = IO_BITMAP_OFFSET; + } else { + /* + * a bitmap offset pointing outside of the TSS limit + * causes a nicely controllable SIGSEGV if a process + * tries to use a port IO instruction. The first + * sys_ioperm() call sets up the bitmap properly. + */ + tss->io_map_base = INVALID_IO_BITMAP_OFFSET; + } + } +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage +long sys_execve(char *name, char **argv,char **envp, struct pt_regs regs) +{ + long error; + char * filename; + + filename = getname(name); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + return error; + error = do_execve(filename, argv, envp, ®s); + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); + return error; +} + +void set_personality_64bit(void) +{ + /* inherit personality from parent */ + + /* Make sure to be in 64bit mode */ + clear_thread_flag(TIF_IA32); +} + +asmlinkage long sys_fork(struct pt_regs regs) +{ + return do_fork(SIGCHLD, regs.rsp, ®s, 0); +} + +asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs regs) +{ + if (!newsp) + newsp = regs.rsp; + return do_fork(clone_flags, newsp, ®s, 0); +} + +/* + * This is trivial, and on the face of it looks like it + * could equally well be done in user mode. + * + * Not so, for quite unobvious reasons - register pressure. + * In user mode vfork() cannot have a stack frame, and if + * done by calling the "clone()" system call directly, you + * do not have enough call-clobbered registers to hold all + * the information you need. + */ +asmlinkage long sys_vfork(struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, ®s, 0); +} + +/* + * These bracket the sleeping functions.. + */ +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +/* + * Do a traceback through the scheduler to find where a process sleeps. + * + * Currently sched.c is compiled with -fno-omit-frame-pointer, so we + * just go through the stack frames. + */ +unsigned long get_wchan(struct task_struct *p) +{ + return -1; +} +#undef last_sched +#undef first_sched + +asmlinkage int sys_arch_prctl(int code, unsigned long addr) +{ + int ret = 0; + unsigned long tmp; + switch (code) { + case ARCH_SET_GS: + asm volatile("movw %%gs,%0" : "=g" (current->thread.gsindex)); + current->thread.gs = addr; + ret = checking_wrmsrl(MSR_KERNEL_GS_BASE, addr); + break; + case ARCH_SET_FS: + asm volatile("movw %%fs,%0" : "=g" (current->thread.fsindex)); + current->thread.fs = addr; + ret = checking_wrmsrl(MSR_FS_BASE, addr); + break; + + /* Returned value may not be correct when the user changed fs/gs */ + case ARCH_GET_FS: + rdmsrl(MSR_FS_BASE, tmp); + ret = put_user(tmp, (unsigned long *)addr); + break; + + case ARCH_GET_GS: + rdmsrl(MSR_KERNEL_GS_BASE, tmp); + ret = put_user(tmp, (unsigned long *)addr); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + diff -Nru a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/ptrace.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,435 @@ +/* ptrace.c */ +/* By Ross Biro 1/23/92 */ +/* + * Pentium III FXSR, SSE support + * Gareth Hughes , May 2000 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* determines which flags the user has access to. */ +/* 1 = access 0 = no access */ +#define FLAG_MASK 0x44dd5UL + +/* set's the trap flag. */ +#define TRAP_FLAG 0x100UL + +/* + * eflags and offset of eflags on child stack.. + */ +#define EFLAGS offsetof(struct pt_regs, eflags) +#define EFL_OFFSET ((int)(EFLAGS-sizeof(struct pt_regs))) + +/* + * this routine will get a word off of the processes privileged stack. + * the offset is how far from the base addr as stored in the TSS. + * this routine assumes that all the privileged stacks are in our + * data space. + */ +static inline unsigned long get_stack_long(struct task_struct *task, int offset) +{ + unsigned char *stack; + + stack = (unsigned char *)task->thread.rsp0; + stack += offset; + return (*((unsigned long *)stack)); +} + +/* + * this routine will put a word on the processes privileged stack. + * the offset is how far from the base addr as stored in the TSS. + * this routine assumes that all the privileged stacks are in our + * data space. + */ +static inline long put_stack_long(struct task_struct *task, int offset, + unsigned long data) +{ + unsigned char * stack; + + stack = (unsigned char *) task->thread.rsp0; + stack += offset; + *(unsigned long *) stack = data; + return 0; +} + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure the single step bit is not set. + */ +void ptrace_disable(struct task_struct *child) +{ + long tmp; + + tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; + put_stack_long(child, EFL_OFFSET, tmp); +} + +static int putreg(struct task_struct *child, + unsigned long regno, unsigned long value) +{ + unsigned long tmp; + switch (regno >> 2) { + // XXX: add 64bit setting. + case FS: + if (value && (value & 3) != 3) + return -EIO; + child->thread.fs = value; + return 0; + case GS: + if (value && (value & 3) != 3) + return -EIO; + child->thread.gs = value; + return 0; + case EFLAGS: + value &= FLAG_MASK; + tmp = get_stack_long(child, EFL_OFFSET); + tmp &= ~FLAG_MASK; + value |= tmp; + break; + } + /* assumption about sizes... */ + if (regno > GS*4) + regno -= 2*4; + /* This has to be changes to put_stack_64() */ + /* Hmm, with 32 bit applications being around... this will be + rather funny */ + put_stack_long(child, regno - sizeof(struct pt_regs), value); + return 0; +} + +static unsigned long getreg(struct task_struct *child, + unsigned long regno) +{ + switch (regno >> 3) { + case FS: + return child->thread.fs; + case GS: + return child->thread.gs; + default: + regno = regno - sizeof(struct pt_regs); + return get_stack_long(child, regno); + } + +} + +asmlinkage long sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + struct user * dummy = NULL; + long i, ret; + + /* This lock_kernel fixes a subtle race with suid exec */ + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + tmp = 0; /* Default return condition */ + if(addr < 20*sizeof(long)) + tmp = getreg(child, addr); + if(addr >= (long) &dummy->u_debugreg[0] && + addr <= (long) &dummy->u_debugreg[7]){ + addr -= (long) &dummy->u_debugreg[0]; + addr = addr >> 2; + tmp = child->thread.debugreg[addr]; + } + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + ret = -EIO; + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + if (addr < 20*sizeof(long)) { + ret = putreg(child, addr, data); + break; + } + /* We need to be very careful here. We implicitly + want to modify a portion of the task_struct, and we + have to be selective about what portions we allow someone + to modify. */ + + ret = -EIO; + if(addr >= (long) &dummy->u_debugreg[0] && + addr <= (long) &dummy->u_debugreg[7]){ + + if(addr == (long) &dummy->u_debugreg[4]) break; + if(addr == (long) &dummy->u_debugreg[5]) break; + if(addr < (long) &dummy->u_debugreg[4] && + ((unsigned long) data) >= TASK_SIZE-3) break; + + if(addr == (long) &dummy->u_debugreg[7]) { + data &= ~DR_CONTROL_RESERVED; + for(i=0; i<4; i++) + if ((0x5454 >> ((data >> (16 + 4*i)) & 0xf)) & 1) + goto out_tsk; + } + + addr -= (long) &dummy->u_debugreg; + addr = addr >> 2; + child->thread.debugreg[addr] = data; + ret = 0; + } + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) { + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + else { + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + child->exit_code = data; + /* make sure the single step bit is not set. */ + tmp = get_stack_long(child, EFL_OFFSET); + tmp &= ~TRAP_FLAG; + put_stack_long(child, EFL_OFFSET,tmp); + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + long tmp; + + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; + put_stack_long(child, EFL_OFFSET, tmp); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + if ((child->ptrace & PT_DTRACE) == 0) { + /* Spurious delayed TF traps may occur */ + child->ptrace |= PT_DTRACE; + } + tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG; + put_stack_long(child, EFL_OFFSET, tmp); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: + /* detach a process that was attached. */ + ret = ptrace_detach(child, data); + break; + + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, FRAME_SIZE)) { + ret = -EIO; + break; + } + for ( i = 0; i < FRAME_SIZE; i += sizeof(long) ) { + __put_user(getreg(child, i),(unsigned long *) data); + data += sizeof(long); + } + ret = 0; + break; + } + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + if (!access_ok(VERIFY_READ, (unsigned *)data, FRAME_SIZE)) { + ret = -EIO; + break; + } + for ( i = 0; i < FRAME_SIZE; i += sizeof(long) ) { + __get_user(tmp, (unsigned long *) data); + putreg(child, i, tmp); + data += sizeof(long); + } + ret = 0; + break; + } + + case PTRACE_GETFPREGS: { /* Get the child extended FPU state. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, + sizeof(struct user_i387_struct))) { + ret = -EIO; + break; + } + if ( !child->used_math ) { + /* Simulate an empty FPU. */ + set_fpu_cwd(child, 0x037f); + set_fpu_swd(child, 0x0000); + set_fpu_twd(child, 0xffff); + set_fpu_mxcsr(child, 0x1f80); + } + ret = get_fpregs((struct user_i387_struct *)data, child); + break; + } + + case PTRACE_SETFPREGS: { /* Set the child extended FPU state. */ + if (!access_ok(VERIFY_READ, (unsigned *)data, + sizeof(struct user_i387_struct))) { + ret = -EIO; + break; + } + child->used_math = 1; + ret = set_fpregs(child, (struct user_i387_struct *)data); + break; + } + + case PTRACE_SETOPTIONS: { + if (data & PTRACE_O_TRACESYSGOOD) + child->ptrace |= PT_TRACESYSGOOD; + else + child->ptrace &= ~PT_TRACESYSGOOD; + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +asmlinkage void syscall_trace(struct pt_regs *regs) +{ + +#if 0 + printk("trace %s rip %lx rsp %lx rax %d origrax %d caller %lx tiflags %x ptrace %x\n", + current->comm, + regs->rip, regs->rsp, regs->rax, regs->orig_rax, __builtin_return_address(0), + current_thread_info()->flags, current->ptrace); +#endif + + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + + current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0); + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff -Nru a/arch/x86_64/kernel/semaphore.c b/arch/x86_64/kernel/semaphore.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/semaphore.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,225 @@ +/* + * x86_64 semaphore implementation. + * + * (C) Copyright 1999 Linus Torvalds + * + * Portions Copyright 1999 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * rw semaphores implemented November 1999 by Benjamin LaHaise + */ +#include +#include +#include + +#include + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to acquire the semaphore, while the "sleeping" + * variable is a count of such acquires. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * "sleeping" and the contention routine ordering is + * protected by the semaphore spinlock. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in + * where we want to avoid any extra jumps and calls. + */ + +/* + * Logic: + * - only on a boundary condition do we need to care. When we go + * from a negative count to a non-negative, we wake people up. + * - when we go from a non-negative count to a negative do we + * (a) synchronize with the "sleeper" count and (b) make sure + * that we're on the wakeup list before we synchronize so that + * we cannot lose wakeup events. + */ + +void __up(struct semaphore *sem) +{ + wake_up(&sem->wait); +} + +static spinlock_t semaphore_lock = SPIN_LOCK_UNLOCKED; + +void __down(struct semaphore * sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + tsk->state = TASK_UNINTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + + spin_lock_irq(&semaphore_lock); + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irq(&semaphore_lock); + + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE; + spin_lock_irq(&semaphore_lock); + } + spin_unlock_irq(&semaphore_lock); + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + wake_up(&sem->wait); +} + +int __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + tsk->state = TASK_INTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + + spin_lock_irq(&semaphore_lock); + sem->sleepers ++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * With signals pending, this turns into + * the trylock failure case - we won't be + * sleeping, and we* can't get the lock as + * it has contention. Just correct the count + * and exit. + */ + if (signal_pending(current)) { + retval = -EINTR; + sem->sleepers = 0; + atomic_add(sleepers, &sem->count); + break; + } + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock. The + * "-1" is because we're still hoping to get + * the lock. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irq(&semaphore_lock); + + schedule(); + tsk->state = TASK_INTERRUPTIBLE; + spin_lock_irq(&semaphore_lock); + } + spin_unlock_irq(&semaphore_lock); + tsk->state = TASK_RUNNING; + remove_wait_queue(&sem->wait, &wait); + wake_up(&sem->wait); + return retval; +} + +/* + * Trylock failed - make sure we correct for + * having decremented the count. + * + * We could have done the trylock with a + * single "cmpxchg" without failure cases, + * but then it wouldn't work on a 386. + */ +int __down_trylock(struct semaphore * sem) +{ + int sleepers; + unsigned long flags; + + spin_lock_irqsave(&semaphore_lock, flags); + sleepers = sem->sleepers + 1; + sem->sleepers = 0; + + /* + * Add "everybody else" and us into it. They aren't + * playing, because we own the spinlock. + */ + if (!atomic_add_negative(sleepers, &sem->count)) + wake_up(&sem->wait); + + spin_unlock_irqrestore(&semaphore_lock, flags); + return 1; +} + + +/* + * The semaphore operations have a special calling sequence that + * allow us to do a simpler in-line version of them. These routines + * need to convert that sequence back into the C sequence when + * there is contention on the semaphore. + * + * %rcx contains the semaphore pointer on entry. Save all the callee + * clobbered registers. It would be better if the compiler had a way + * to specify that for the callee. + */ + + +#define PUSH_CLOBBER "pushq %rdi ; pushq %rsi ; pushq %rdx ; pushq %rcx ;" \ + "pushq %rbx ; pushq %r8 ; push %r9\n\t" +#define POP_CLOBBER "popq %r9 ; popq %r8 ; popq %rbx ; popq %rcx ; " \ + "popq %rdx ; popq %rsi ; popq %rdi\n\t" + +#define SEM_ENTRY(label, name) asm( \ + ".p2align\n\t.globl " #label "\n\t" \ + #label ":\n\t" PUSH_CLOBBER "call " #name "\n\t" POP_CLOBBER "ret" ) + +SEM_ENTRY(__down_failed, __down); +SEM_ENTRY(__down_failed_interruptible, __down_interruptible); +SEM_ENTRY(__down_failed_trylock, __down_trylock); +SEM_ENTRY(__up_wakeup, __up); + + +#if defined(CONFIG_SMP) +asm( +".p2align" +"\n.globl __write_lock_failed" +"\n__write_lock_failed:" +"\n " LOCK "addl $" RW_LOCK_BIAS_STR ",(%rax)" +"\n1: rep; nop; cmpl $" RW_LOCK_BIAS_STR ",(%rax)" +"\n jne 1b" + +"\n " LOCK "subl $" RW_LOCK_BIAS_STR ",(%rax)" +"\n jnz __write_lock_failed" +"\n ret" + + +"\n.p2align" +"\n.globl __read_lock_failed" +"\n__read_lock_failed:" +"\n lock ; incl (%rax)" +"\n1: rep; nop; cmpl $1,(%rax)" +"\n js 1b" + +"\n lock ; decl (%rax)" +"\n js __read_lock_failed" +"\n ret" +); +#endif + diff -Nru a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/setup.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1128 @@ +/* + * linux/arch/x86-64/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * + * Nov 2001 Dave Jones + * Forked from i386 setup code. + * + * $Id$ + */ + +/* + * This file handles the architecture-dependent parts of initialization + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BLK_DEV_RAM +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Machine setup.. + */ + +extern void mcheck_init(struct cpuinfo_x86 *c); + +char ignore_irq13; /* set if exception 16 works */ +struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; + +unsigned long mmu_cr4_features; + +/* For PCI or other memory-mapped resources */ +unsigned long pci_mem_start = 0x10000000; + +/* + * Setup options + */ +struct drive_info_struct { char dummy[32]; } drive_info; +struct screen_info screen_info; +struct apm_info apm_info; +struct sys_desc_table_struct { + unsigned short length; + unsigned char table[0]; +}; + +struct e820map e820; + +unsigned char aux_device_present; + +extern int root_mountflags; +extern char _text, _etext, _edata, _end; +extern unsigned long cpu_khz; + +static int disable_x86_fxsr __initdata = 0; + +static char command_line[COMMAND_LINE_SIZE]; + char saved_command_line[COMMAND_LINE_SIZE]; + +struct resource standard_io_resources[] = { + { "dma1", 0x00, 0x1f, IORESOURCE_BUSY }, + { "pic1", 0x20, 0x3f, IORESOURCE_BUSY }, + { "timer", 0x40, 0x5f, IORESOURCE_BUSY }, + { "keyboard", 0x60, 0x6f, IORESOURCE_BUSY }, + { "dma page reg", 0x80, 0x8f, IORESOURCE_BUSY }, + { "pic2", 0xa0, 0xbf, IORESOURCE_BUSY }, + { "dma2", 0xc0, 0xdf, IORESOURCE_BUSY }, + { "fpu", 0xf0, 0xff, IORESOURCE_BUSY } +}; + +#define STANDARD_IO_RESOURCES (sizeof(standard_io_resources)/sizeof(struct resource)) + +static struct resource code_resource = { "Kernel code", 0x100000, 0 }; +static struct resource data_resource = { "Kernel data", 0, 0 }; +static struct resource vram_resource = { "Video RAM area", 0xa0000, 0xbffff, IORESOURCE_BUSY }; + +/* System ROM resources */ +#define MAXROMS 6 +static struct resource rom_resources[MAXROMS] = { + { "System ROM", 0xF0000, 0xFFFFF, IORESOURCE_BUSY }, + { "Video ROM", 0xc0000, 0xc7fff, IORESOURCE_BUSY } +}; + +#define romsignature(x) (*(unsigned short *)(x) == 0xaa55) + +static void __init probe_roms(void) +{ + int roms = 1; + unsigned long base; + unsigned char *romstart; + + request_resource(&iomem_resource, rom_resources+0); + + /* Video ROM is standard at C000:0000 - C7FF:0000, check signature */ + for (base = 0xC0000; base < 0xE0000; base += 2048) { + romstart = isa_bus_to_virt(base); + if (!romsignature(romstart)) + continue; + request_resource(&iomem_resource, rom_resources + roms); + roms++; + break; + } + + /* Extension roms at C800:0000 - DFFF:0000 */ + for (base = 0xC8000; base < 0xE0000; base += 2048) { + unsigned long length; + + romstart = isa_bus_to_virt(base); + if (!romsignature(romstart)) + continue; + length = romstart[2] * 512; + if (length) { + unsigned int i; + unsigned char chksum; + + chksum = 0; + for (i = 0; i < length; i++) + chksum += romstart[i]; + + /* Good checksum? */ + if (!chksum) { + rom_resources[roms].start = base; + rom_resources[roms].end = base + length - 1; + rom_resources[roms].name = "Extension ROM"; + rom_resources[roms].flags = IORESOURCE_BUSY; + + request_resource(&iomem_resource, rom_resources + roms); + roms++; + if (roms >= MAXROMS) + return; + } + } + } + + /* Final check for motherboard extension rom at E000:0000 */ + base = 0xE0000; + romstart = isa_bus_to_virt(base); + + if (romsignature(romstart)) { + rom_resources[roms].start = base; + rom_resources[roms].end = base + 65535; + rom_resources[roms].name = "Extension ROM"; + rom_resources[roms].flags = IORESOURCE_BUSY; + + request_resource(&iomem_resource, rom_resources + roms); + } +} + +void __init add_memory_region(unsigned long long start, + unsigned long long size, int type) +{ + int x = e820.nr_map; + + if (x == E820MAX) { + printk(KERN_ERR "Ooops! Too many entries in the memory map!\n"); + return; + } + + e820.map[x].addr = start; + e820.map[x].size = size; + e820.map[x].type = type; + e820.nr_map++; +} /* add_memory_region */ + +#define E820_DEBUG 1 + +static void __init print_memory_map(char *who) +{ + int i; + + for (i = 0; i < e820.nr_map; i++) { + printk(" %s: %016Lx - %016Lx ", who, + (unsigned long long)e820.map[i].addr, + (unsigned long long)(e820.map[i].addr + e820.map[i].size)); + switch (e820.map[i].type) { + case E820_RAM: printk("(usable)\n"); + break; + case E820_RESERVED: + printk("(reserved)\n"); + break; + case E820_ACPI: + printk("(ACPI data)\n"); + break; + case E820_NVS: + printk("(ACPI NVS)\n"); + break; + default: printk("type %lu\n", (unsigned long)e820.map[i].type); + break; + } + } +} + +/* + * Sanitize the BIOS e820 map. + * + * Some e820 responses include overlapping entries. The following + * replaces the original e820 map with a new one, removing overlaps. + * + */ +static int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map) +{ + struct change_member { + struct e820entry *pbios; /* pointer to original bios entry */ + unsigned long long addr; /* address for this change point */ + }; + struct change_member change_point_list[2*E820MAX]; + struct change_member *change_point[2*E820MAX]; + struct e820entry *overlap_list[E820MAX]; + struct e820entry new_bios[E820MAX]; + struct change_member *change_tmp; + unsigned long current_type, last_type; + unsigned long long last_addr; + int chgidx, still_changing; + int overlap_entries; + int new_bios_entry; + int old_nr, new_nr; + int i; + + /* + Visually we're performing the following (1,2,3,4 = memory types)... + + Sample memory map (w/overlaps): + ____22__________________ + ______________________4_ + ____1111________________ + _44_____________________ + 11111111________________ + ____________________33__ + ___________44___________ + __________33333_________ + ______________22________ + ___________________2222_ + _________111111111______ + _____________________11_ + _________________4______ + + Sanitized equivalent (no overlap): + 1_______________________ + _44_____________________ + ___1____________________ + ____22__________________ + ______11________________ + _________1______________ + __________3_____________ + ___________44___________ + _____________33_________ + _______________2________ + ________________1_______ + _________________4______ + ___________________2____ + ____________________33__ + ______________________4_ + */ + + /* if there's only one memory region, don't bother */ + if (*pnr_map < 2) + return -1; + + old_nr = *pnr_map; + + /* bail out if we find any unreasonable addresses in bios map */ + for (i=0; iaddr = biosmap[i].addr; + change_point[chgidx++]->pbios = &biosmap[i]; + change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size; + change_point[chgidx++]->pbios = &biosmap[i]; + } + + /* sort change-point list by memory addresses (low -> high) */ + still_changing = 1; + while (still_changing) { + still_changing = 0; + for (i=1; i < 2*old_nr; i++) { + /* if > , swap */ + /* or, if current= & last=, swap */ + if ((change_point[i]->addr < change_point[i-1]->addr) || + ((change_point[i]->addr == change_point[i-1]->addr) && + (change_point[i]->addr == change_point[i]->pbios->addr) && + (change_point[i-1]->addr != change_point[i-1]->pbios->addr)) + ) + { + change_tmp = change_point[i]; + change_point[i] = change_point[i-1]; + change_point[i-1] = change_tmp; + still_changing=1; + } + } + } + + /* create a new bios memory map, removing overlaps */ + overlap_entries=0; /* number of entries in the overlap table */ + new_bios_entry=0; /* index for creating new bios map entries */ + last_type = 0; /* start with undefined memory type */ + last_addr = 0; /* start with 0 as last starting address */ + /* loop through change-points, determining affect on the new bios map */ + for (chgidx=0; chgidx < 2*old_nr; chgidx++) + { + /* keep track of all overlapping bios entries */ + if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr) + { + /* add map entry to overlap list (> 1 entry implies an overlap) */ + overlap_list[overlap_entries++]=change_point[chgidx]->pbios; + } + else + { + /* remove entry from list (order independent, so swap with last) */ + for (i=0; ipbios) + overlap_list[i] = overlap_list[overlap_entries-1]; + } + overlap_entries--; + } + /* if there are overlapping entries, decide which "type" to use */ + /* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */ + current_type = 0; + for (i=0; itype > current_type) + current_type = overlap_list[i]->type; + /* continue building up new bios map based on this information */ + if (current_type != last_type) { + if (last_type != 0) { + new_bios[new_bios_entry].size = + change_point[chgidx]->addr - last_addr; + /* move forward only if the new size was non-zero */ + if (new_bios[new_bios_entry].size != 0) + if (++new_bios_entry >= E820MAX) + break; /* no more space left for new bios entries */ + } + if (current_type != 0) { + new_bios[new_bios_entry].addr = change_point[chgidx]->addr; + new_bios[new_bios_entry].type = current_type; + last_addr=change_point[chgidx]->addr; + } + last_type = current_type; + } + } + new_nr = new_bios_entry; /* retain count for new bios entries */ + + /* copy new bios mapping into original location */ + memcpy(biosmap, new_bios, new_nr*sizeof(struct e820entry)); + *pnr_map = new_nr; + + return 0; +} + +/* + * Copy the BIOS e820 map into a safe place. + * + * Sanity-check it while we're at it.. + * + * If we're lucky and live on a modern system, the setup code + * will have given us a memory map that we can use to properly + * set up memory. If we aren't, we'll fake a memory map. + * + * We check to see that the memory map contains at least 2 elements + * before we'll use it, because the detection code in setup.S may + * not be perfect and most every PC known to man has two memory + * regions: one from 0 to 640k, and one from 1mb up. (The IBM + * thinkpad 560x, for example, does not cooperate with the memory + * detection code.) + */ +static int __init copy_e820_map(struct e820entry * biosmap, int nr_map) +{ + /* Only one memory region (or negative)? Ignore it */ + if (nr_map < 2) + return -1; + + do { + unsigned long long start = biosmap->addr; + unsigned long long size = biosmap->size; + unsigned long long end = start + size; + unsigned long type = biosmap->type; + + /* Overflow in 64 bits? Ignore the memory map. */ + if (start > end) + return -1; + + /* + * Some BIOSes claim RAM in the 640k - 1M region. + * Not right. Fix it up. + */ + if (type == E820_RAM) { + if (start < 0x100000ULL && end > 0xA0000ULL) { + if (start < 0xA0000ULL) + add_memory_region(start, 0xA0000ULL-start, type); + if (end <= 0x100000ULL) + continue; + start = 0x100000ULL; + size = end - start; + } + } + add_memory_region(start, size, type); + } while (biosmap++,--nr_map); + return 0; +} + +/* + * Do NOT EVER look at the BIOS memory size location. + * It does not work on many machines. + */ +#define LOWMEMSIZE() (0x9f000) + +void __init setup_memory_region(void) +{ + char *who = "BIOS-e820"; + + /* + * Try to copy the BIOS-supplied E820-map. + * + * Otherwise fake a memory map; one section from 0k->640k, + * the next section from 1mb->appropriate_mem_k + */ + sanitize_e820_map(E820_MAP, &E820_MAP_NR); + if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) { + unsigned long mem_size; + + /* compare results from other methods and take the greater */ + if (ALT_MEM_K < EXT_MEM_K) { + mem_size = EXT_MEM_K; + who = "BIOS-88"; + } else { + mem_size = ALT_MEM_K; + who = "BIOS-e801"; + } + + e820.nr_map = 0; + add_memory_region(0, LOWMEMSIZE(), E820_RAM); + add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM); + } + printk(KERN_INFO "BIOS-provided physical RAM map:\n"); + print_memory_map(who); +} /* setup_memory_region */ + + +static inline void parse_mem_cmdline (char ** cmdline_p) +{ + char c = ' ', *to = command_line, *from = COMMAND_LINE; + int len = 0; + int usermem = 0; + + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + + for (;;) { + /* + * "mem=nopentium" disables the 4MB page tables. + * "mem=XXX[kKmM]" defines a memory region from HIGH_MEM + * to , overriding the bios size. + * "mem=XXX[KkmM]@XXX[KkmM]" defines a memory region from + * to +, overriding the bios size. + */ + if (c == ' ' && !memcmp(from, "mem=", 4)) { + if (to != command_line) + to--; + if (!memcmp(from+4, "nopentium", 9)) { + from += 9+4; + clear_bit(X86_FEATURE_PSE, &boot_cpu_data.x86_capability); + } else if (!memcmp(from+4, "exactmap", 8)) { + from += 8+4; + e820.nr_map = 0; + usermem = 1; + } else { + /* If the user specifies memory size, we + * blow away any automatically generated + * size + */ + unsigned long long start_at, mem_size; + + if (usermem == 0) { + /* first time in: zap the whitelist + * and reinitialize it with the + * standard low-memory region. + */ + e820.nr_map = 0; + usermem = 1; + add_memory_region(0, LOWMEMSIZE(), E820_RAM); + } + mem_size = memparse(from+4, &from); + if (*from == '@') + start_at = memparse(from+1, &from); + else { + start_at = HIGH_MEMORY; + mem_size -= HIGH_MEMORY; + usermem=0; + } + add_memory_region(start_at, mem_size, E820_RAM); + } + } + c = *(from++); + if (!c) + break; + if (COMMAND_LINE_SIZE <= ++len) + break; + *(to++) = c; + } + *to = '\0'; + *cmdline_p = command_line; + if (usermem) { + printk(KERN_INFO "user-defined physical RAM map:\n"); + print_memory_map("user"); + } +} + +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size, low_mem_size; + unsigned long start_pfn, max_pfn, max_low_pfn; + int i; + + ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); + drive_info = DRIVE_INFO; + screen_info = SCREEN_INFO; + apm_info.bios = APM_BIOS_INFO; + aux_device_present = AUX_DEVICE_INFO; + +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); +#endif + setup_memory_region(); + + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; + init_mm.start_code = (unsigned long) &_text; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + + code_resource.start = virt_to_phys(&_text); + code_resource.end = virt_to_phys(&_etext)-1; + data_resource.start = virt_to_phys(&_etext); + data_resource.end = virt_to_phys(&_edata)-1; + + parse_mem_cmdline(cmdline_p); + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + +#define VMALLOC_RESERVE (unsigned long)(4096 << 20) +#define MAXMEM (unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE) +#define MAXMEM_PFN PFN_DOWN(MAXMEM) + + /* + * partially used pages are not usable - thus + * we are rounding upwards: + */ + start_pfn = PFN_UP(__pa(&_end)); + + /* + * Find the highest page frame number we have available + */ + max_pfn = 0; + for (i = 0; i < e820.nr_map; i++) { + unsigned long start, end; + /* RAM? */ + if (e820.map[i].type != E820_RAM) + continue; + start = PFN_UP(e820.map[i].addr); + end = PFN_DOWN(e820.map[i].addr + e820.map[i].size); + if (start >= end) + continue; + if (end > max_pfn) + max_pfn = end; + } + + /* + * Determine low and high memory ranges: + */ + max_low_pfn = max_pfn; + if (max_low_pfn > MAXMEM_PFN) { + max_low_pfn = MAXMEM_PFN; + } + + + /* + * Initialize the boot-time allocator (with low memory only): + */ + bootmap_size = init_bootmem(start_pfn, max_low_pfn); + + /* + * Register fully available low RAM pages with the bootmem allocator. + */ + for (i = 0; i < e820.nr_map; i++) { + unsigned long curr_pfn, last_pfn, size; + /* + * Reserve usable low memory + */ + if (e820.map[i].type != E820_RAM) + continue; + /* + * We are rounding up the start address of usable memory: + */ + curr_pfn = PFN_UP(e820.map[i].addr); + if (curr_pfn >= max_low_pfn) + continue; + /* + * ... and at the end of the usable range downwards: + */ + last_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size); + + if (last_pfn > max_low_pfn) + last_pfn = max_low_pfn; + + /* + * .. finally, did all the rounding and playing + * around just make the area go away? + */ + if (last_pfn <= curr_pfn) + continue; + + size = last_pfn - curr_pfn; + free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); + } + /* + * Reserve the bootmem bitmap itself as well. We do this in two + * steps (first step was init_bootmem()) because this catches + * the (very unlikely) case of us accidentally initializing the + * bootmem allocator with an invalid RAM area. + */ + reserve_bootmem(HIGH_MEMORY, (PFN_PHYS(start_pfn) + + bootmap_size + PAGE_SIZE-1) - (HIGH_MEMORY)); + + /* + * reserve physical page 0 - it's a special BIOS page on many boxes, + * enabling clean reboots, SMP operation, laptop functions. + */ + reserve_bootmem(0, PAGE_SIZE); + +#ifdef CONFIG_SMP + /* + * But first pinch a few for the stack/trampoline stuff + * FIXME: Don't need the extra page at 4K, but need to fix + * trampoline before removing it. (see the GDT stuff) + */ + reserve_bootmem(PAGE_SIZE, PAGE_SIZE); +#endif + +#ifdef CONFIG_X86_LOCAL_APIC + /* + * Find and reserve possible boot-time SMP configuration: + */ + find_smp_config(); +#endif +#ifdef CONFIG_BLK_DEV_INITRD + if (LOADER_TYPE && INITRD_START) { + if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { + reserve_bootmem(INITRD_START, INITRD_SIZE); + initrd_start = + INITRD_START ? INITRD_START + PAGE_OFFSET : 0; + initrd_end = initrd_start+INITRD_SIZE; + } + else { + printk(KERN_ERR "initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + (unsigned long)(INITRD_START + INITRD_SIZE), + (unsigned long)(max_low_pfn << PAGE_SHIFT)); + initrd_start = 0; + } + } +#endif + + /* + * NOTE: before this point _nobody_ is allowed to allocate + * any memory using the bootmem allocator. + */ + +#ifdef CONFIG_SMP + smp_alloc_memory(); /* AP processor realmode stacks in low memory*/ +#endif + paging_init(); +#ifdef CONFIG_X86_LOCAL_APIC + /* + * get boot-time SMP configuration: + */ + if (smp_found_config) + get_smp_config(); + init_apic_mappings(); +#endif + + + /* + * Request address space for all standard RAM and ROM resources + * and also for regions reported as reserved by the e820. + */ + probe_roms(); + for (i = 0; i < e820.nr_map; i++) { + struct resource *res; + if (e820.map[i].addr + e820.map[i].size > 0x100000000ULL) + continue; + res = alloc_bootmem_low(sizeof(struct resource)); + switch (e820.map[i].type) { + case E820_RAM: res->name = "System RAM"; break; + case E820_ACPI: res->name = "ACPI Tables"; break; + case E820_NVS: res->name = "ACPI Non-volatile Storage"; break; + default: res->name = "reserved"; + } + res->start = e820.map[i].addr; + res->end = res->start + e820.map[i].size - 1; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + request_resource(&iomem_resource, res); + if (e820.map[i].type == E820_RAM) { + /* + * We dont't know which RAM region contains kernel data, + * so we try it repeatedly and let the resource manager + * test it. + */ + request_resource(res, &code_resource); + request_resource(res, &data_resource); + } + } + request_resource(&iomem_resource, &vram_resource); + + /* request I/O space for devices used on all i[345]86 PCs */ + for (i = 0; i < STANDARD_IO_RESOURCES; i++) + request_resource(&ioport_resource, standard_io_resources+i); + + /* Tell the PCI layer not to allocate too close to the RAM area.. */ + low_mem_size = ((max_low_pfn << PAGE_SHIFT) + 0xfffff) & ~0xfffff; + if (low_mem_size > pci_mem_start) + pci_mem_start = low_mem_size; + +#ifdef CONFIG_VT +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif +} + +#ifndef CONFIG_X86_TSC +static int tsc_disable __initdata = 0; + +static int __init tsc_setup(char *str) +{ + tsc_disable = 1; + return 1; +} + +__setup("notsc", tsc_setup); +#endif + +static int __init get_model_name(struct cpuinfo_x86 *c) +{ + unsigned int *v; + + if (cpuid_eax(0x80000000) < 0x80000004) + return 0; + + v = (unsigned int *) c->x86_model_id; + cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); + cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); + cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); + c->x86_model_id[48] = 0; + return 1; +} + + +static void __init display_cacheinfo(struct cpuinfo_x86 *c) +{ + unsigned int n, dummy, ecx, edx; + + n = cpuid_eax(0x80000000); + + if (n >= 0x80000005) { + cpuid(0x80000005, &dummy, &dummy, &ecx, &edx); + printk(KERN_INFO "CPU: L1 I Cache: %dK (%d bytes/line), D cache %dK (%d bytes/line)\n", + edx>>24, edx&0xFF, ecx>>24, ecx&0xFF); + c->x86_cache_size=(ecx>>24)+(edx>>24); + } + + if (n < 0x80000006) + return; + + ecx = cpuid_ecx(0x80000006); + c->x86_cache_size = ecx >> 16; + + printk(KERN_INFO "CPU: L2 Cache: %dK (%d bytes/line)\n", + c->x86_cache_size, ecx & 0xFF); +} + + +static int __init init_amd(struct cpuinfo_x86 *c) +{ + int r; + + /* Bit 31 in normal CPUID used for nonstandard 3DNow ID; + 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */ + clear_bit(0*32+31, &c->x86_capability); + + r = get_model_name(c); + display_cacheinfo(c); + return r; +} + + +void __init get_cpu_vendor(struct cpuinfo_x86 *c) +{ + char *v = c->x86_vendor_id; + + if (!strcmp(v, "AuthenticAMD")) + c->x86_vendor = X86_VENDOR_AMD; + else + c->x86_vendor = X86_VENDOR_UNKNOWN; +} + +struct cpu_model_info { + int vendor; + int family; + char *model_names[16]; +}; + +int __init x86_fxsr_setup(char * s) +{ + disable_x86_fxsr = 1; + return 1; +} +__setup("nofxsr", x86_fxsr_setup); + + + +/* + * This does the hard work of actually picking apart the CPU stuff... + */ +void __init identify_cpu(struct cpuinfo_x86 *c) +{ + int junk, i; + u32 xlvl, tfms; + + c->loops_per_jiffy = loops_per_jiffy; + c->x86_cache_size = -1; + c->x86_vendor = X86_VENDOR_UNKNOWN; + c->x86_model = c->x86_mask = 0; /* So far unknown... */ + c->x86_vendor_id[0] = '\0'; /* Unset */ + c->x86_model_id[0] = '\0'; /* Unset */ + memset(&c->x86_capability, 0, sizeof c->x86_capability); + + /* Get vendor name */ + cpuid(0x00000000, &c->cpuid_level, + (int *)&c->x86_vendor_id[0], + (int *)&c->x86_vendor_id[8], + (int *)&c->x86_vendor_id[4]); + + get_cpu_vendor(c); + /* Initialize the standard set of capabilities */ + /* Note that the vendor-specific code below might override */ + + /* Intel-defined flags: level 0x00000001 */ + if ( c->cpuid_level >= 0x00000001 ) { + cpuid(0x00000001, &tfms, &junk, &junk, + &c->x86_capability[0]); + c->x86 = (tfms >> 8) & 15; + c->x86_model = (tfms >> 4) & 15; + c->x86_mask = tfms & 15; + } else { + /* Have CPUID level 0 only - unheard of */ + c->x86 = 4; + } + + /* AMD-defined flags: level 0x80000001 */ + xlvl = cpuid_eax(0x80000000); + if ( (xlvl & 0xffff0000) == 0x80000000 ) { + if ( xlvl >= 0x80000001 ) + c->x86_capability[1] = cpuid_edx(0x80000001); + if ( xlvl >= 0x80000004 ) + get_model_name(c); /* Default name */ + } + + /* Transmeta-defined flags: level 0x80860001 */ + xlvl = cpuid_eax(0x80860000); + if ( (xlvl & 0xffff0000) == 0x80860000 ) { + if ( xlvl >= 0x80860001 ) + c->x86_capability[2] = cpuid_edx(0x80860001); + } + + + printk(KERN_DEBUG "CPU: Before vendor init, caps: %08x %08x %08x, vendor = %d\n", + c->x86_capability[0], + c->x86_capability[1], + c->x86_capability[2], + c->x86_vendor); + + /* + * Vendor-specific initialization. In this section we + * canonicalize the feature flags, meaning if there are + * features a certain CPU supports which CPUID doesn't + * tell us, CPUID claiming incorrect flags, or other bugs, + * we handle them here. + * + * At the end of this section, c->x86_capability better + * indicate the features this CPU genuinely supports! + */ + switch ( c->x86_vendor ) { + + case X86_VENDOR_AMD: + init_amd(c); + break; + + case X86_VENDOR_UNKNOWN: + default: + /* Not much we can do here... */ + break; + } + + printk(KERN_DEBUG "CPU: After vendor init, caps: %08x %08x %08x %08x\n", + c->x86_capability[0], + c->x86_capability[1], + c->x86_capability[2], + c->x86_capability[3]); + + /* + * The vendor-specific functions might have changed features. Now + * we do "generic changes." + */ + + /* TSC disabled? */ +#ifndef CONFIG_X86_TSC + if ( tsc_disable ) + clear_bit(X86_FEATURE_TSC, &c->x86_capability); +#endif + + /* FXSR disabled? */ + if (disable_x86_fxsr) { + clear_bit(X86_FEATURE_FXSR, &c->x86_capability); + clear_bit(X86_FEATURE_XMM, &c->x86_capability); + } + + /* Now the feature flags better reflect actual CPU features! */ + + printk(KERN_DEBUG "CPU: After generic, caps: %08x %08x %08x %08x\n", + c->x86_capability[0], + c->x86_capability[1], + c->x86_capability[2], + c->x86_capability[3]); + + /* + * On SMP, boot_cpu_data holds the common feature set between + * all CPUs; so make sure that we indicate which features are + * common between the CPUs. The first time this routine gets + * executed, c == &boot_cpu_data. + */ + if ( c != &boot_cpu_data ) { + /* AND the already accumulated flags with these */ + for ( i = 0 ; i < NCAPINTS ; i++ ) + boot_cpu_data.x86_capability[i] &= c->x86_capability[i]; + } + + printk(KERN_DEBUG "CPU: Common caps: %08x %08x %08x %08x\n", + boot_cpu_data.x86_capability[0], + boot_cpu_data.x86_capability[1], + boot_cpu_data.x86_capability[2], + boot_cpu_data.x86_capability[3]); +} + + +void __init print_cpu_info(struct cpuinfo_x86 *c) +{ + if (c->x86_model_id[0]) + printk("AMD %s", c->x86_model_id); + + if (c->x86_mask || c->cpuid_level >= 0) + printk(" stepping %02x\n", c->x86_mask); + else + printk("\n"); +} + +/* + * Get CPU information for use by the procfs. + */ + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + struct cpuinfo_x86 *c = v; + int index = c - cpu_data; + + /* + * These flag bits must match the definitions in . + * NULL means this bit is undefined or reserved; either way it doesn't + * have meaning as far as Linux is concerned. Note that it's important + * to realize there is a difference between this table and CPUID -- if + * applications want to get the raw CPUID data, they should access + * /dev/cpu//cpuid instead. + */ + static char *x86_cap_flags[] = { + /* Intel-defined */ + "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", + "cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov", + "pat", "pse36", "pn", "clflush", NULL, "dts", "acpi", "mmx", + "fxsr", "sse", "sse2", "ss", NULL, "tm", "ia64", NULL, + + /* AMD-defined */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, "mmxext", NULL, + NULL, NULL, NULL, NULL, NULL, "lm", "3dnowext", "3dnow", + + /* Transmeta-defined */ + "recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* Other (Linux-defined) */ + "cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + }; + +#ifdef CONFIG_SMP + if (!(cpu_online_map & (1<<(c-cpu_data)))) + return 0; +#endif + + seq_printf(m,"processor\t: %d\n" + "vendor_id\t: %s\n" + "cpu family\t: %d\n" + "model\t\t: %d\n" + "model name\t: %s\n", + index, + c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown", + c->x86, + c->x86_model, + c->x86_model_id[0] ? c->x86_model_id : "unknown"); + + if (c->x86_mask || c->cpuid_level >= 0) + seq_printf(m, "stepping\t: %d\n", c->x86_mask); + else + seq_printf(m, "stepping\t: unknown\n"); + + if ( test_bit(X86_FEATURE_TSC, &c->x86_capability) ) { + seq_printf(m, "cpu MHz\t\t: %lu.%03lu\n", + cpu_khz / 1000, (cpu_khz % 1000)); + } + + /* Cache size */ + if (c->x86_cache_size >= 0) + seq_printf(m, "cache size\t: %d KB\n", c->x86_cache_size); + + seq_printf(m, + "fpu\t\t: yes\n" + "fpu_exception\t: yes\n" + "cpuid level\t: %d\n" + "wp\t\t: yes\n" + "flags\t\t:", + c->cpuid_level); + + { + int i; + for ( i = 0 ; i < 32*NCAPINTS ; i++ ) + if ( test_bit(i, &c->x86_capability) && + x86_cap_flags[i] != NULL ) + seq_printf(m, " %s", x86_cap_flags[i]); + } + + seq_printf(m, "\nbogomips\t: %lu.%02lu\n\n", + c->loops_per_jiffy/(500000/HZ), + (c->loops_per_jiffy/(5000/HZ)) % 100); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? cpu_data + *pos : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { + start: c_start, + next: c_next, + stop: c_stop, + show: show_cpuinfo, +}; diff -Nru a/arch/x86_64/kernel/setup64.c b/arch/x86_64/kernel/setup64.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/setup64.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,142 @@ +/* + * X86-64 specific setup part. + * Copyright (C) 1995 Linus Torvalds + * Copyright 2001 2002 SuSE Labs / Andi Kleen. + * See setup.c for older changelog. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char x86_boot_params[2048] __initdata = {0,}; + +static unsigned long cpu_initialized __initdata = 0; + +struct x8664_pda cpu_pda[NR_CPUS] __cacheline_aligned; + +extern void system_call(void); +extern void ia32_cstar_target(void); + +struct desc_ptr gdt_descr = { 0 /* filled in */, (unsigned long) gdt_table }; +struct desc_ptr idt_descr = { 256 * 16, (unsigned long) idt_table }; + +void pda_init(int cpu) +{ + cpu_pda[cpu].me = &cpu_pda[cpu]; + cpu_pda[cpu].cpunumber = cpu; + cpu_pda[cpu].irqcount = -1; + cpu_pda[cpu].irqstackptr = cpu_pda[cpu].irqstack + sizeof(cpu_pda[0].irqstack); + /* others are initialized in smpboot.c */ + if (cpu == 0) { + cpu_pda[cpu].pcurrent = &init_task; + cpu_pda[cpu].kernelstack = + (unsigned long)&init_thread_union+THREAD_SIZE-PDA_STACKOFFSET; + } + asm volatile("movl %0,%%gs ; movl %0,%%fs" :: "r" (0)); + wrmsrl(MSR_GS_BASE, cpu_pda + cpu); +} + +/* + * cpu_init() initializes state that is per-CPU. Some data is already + * initialized (naturally) in the bootstrap process, such as the GDT + * and IDT. We reload them nevertheless, this function acts as a + * 'CPU state barrier', nothing should get across. + */ +void __init cpu_init (void) +{ +#ifdef CONFIG_SMP + int nr = current_thread_info()->cpu; +#else + int nr = smp_processor_id(); +#endif + struct tss_struct * t = &init_tss[nr]; + unsigned long v; + + /* CPU 0 is initialised in head64.c */ + if (nr != 0) + pda_init(nr); + + if (test_and_set_bit(nr, &cpu_initialized)) { + printk("CPU#%d already initialized!\n", nr); + for (;;) __sti(); + } + printk("Initializing CPU#%d\n", nr); + + if (cpu_has_vme || cpu_has_tsc || cpu_has_de) + clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE); + + gdt_descr.size = (__u8*) gdt_end - (__u8*)gdt_table; + + __asm__ __volatile__("lgdt %0": "=m" (gdt_descr)); + __asm__ __volatile__("lidt %0": "=m" (idt_descr)); + + /* + * Delete NT + */ + + __asm__ volatile("pushfq ; popq %%rax ; btr $14,%%rax ; pushq %%rax ; popfq" :: : "eax"); + + /* + * LSTAR and STAR live in a bit strange symbiosis. + * They both write to the same internal register. STAR allows to set CS/DS + * but only a 32bit target. LSTAR sets the 64bit rip. + */ + wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32); + wrmsrl(MSR_LSTAR, system_call); + +#ifdef CONFIG_IA32_EMULATION + wrmsrl(MSR_CSTAR, ia32_cstar_target); +#endif + + rdmsrl(MSR_EFER, v); + wrmsrl(MSR_EFER, v|1); + + /* Flags to clear on syscall */ + wrmsrl(MSR_SYSCALL_MASK, EF_TF|EF_DF|EF_IE); + + + wrmsrl(MSR_FS_BASE, 0); + wrmsrl(MSR_KERNEL_GS_BASE, 0); + barrier(); + + /* + * set up and load the per-CPU TSS and LDT + */ + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + if(current->mm) + BUG(); + enter_lazy_tlb(&init_mm, current, nr); + + set_tssldt_descriptor((__u8 *)tss_start + (nr*16), (unsigned long) t, + DESC_TSS, + offsetof(struct tss_struct, io_bitmap)); + load_TR(nr); + load_LDT(&init_mm); + + /* + * Clear all 6 debug registers: + */ + + set_debug(0UL, 0); + set_debug(0UL, 1); + set_debug(0UL, 2); + set_debug(0UL, 3); + set_debug(0UL, 6); + set_debug(0UL, 7); + + /* + * Force FPU initialization: + */ + clear_thread_flag(TIF_USEDFPU); + current->used_math = 0; + stts(); +} diff -Nru a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/signal.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,591 @@ +/* + * linux/arch/x86_64/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2000, 2001, 2002 Andi Kleen SuSE Labs + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes + * 2000-2002 x86-64 support by Andi Kleen + * + * $Id: signal.c,v 1.18 2001/10/17 22:30:37 ak Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +void ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs); +void ia32_setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs); + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +asmlinkage long +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, struct pt_regs regs) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); +#if DEBUG_SIG + printk("rt_sigsuspend savset(%lx) newset(%lx) regs(%p) rip(%lx)\n", + saveset, newset, ®s, regs.rip); +#endif + regs.rax = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(®s, &saveset)) + return -EINTR; + } +} + +asmlinkage long +sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct pt_regs regs) +{ + return do_sigaltstack(uss, uoss, regs.rsp); +} + + +/* + * Do a signal return; undo the signal stack. + */ + +struct rt_sigframe +{ + char *pretcode; + struct ucontext uc; + struct siginfo info; + struct _fpstate fpstate; + char retcode[8]; +}; + +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, unsigned long *prax) +{ + unsigned int err = 0; + +#define COPY(x) err |= __get_user(regs->x, &sc->x) + +#define COPY_SEG(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->x##seg = tmp; } + +#define COPY_SEG_STRICT(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->x##seg = tmp|3; } + +#define GET_SEG(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + loadsegment(seg,tmp); } + + /* XXX: rdmsr for 64bits */ + GET_SEG(gs); + GET_SEG(fs); + COPY(rdi); COPY(rsi); COPY(rbp); COPY(rsp); COPY(rbx); + COPY(rdx); COPY(rcx); COPY(rip); + COPY(r8); + COPY(r9); + COPY(r10); + COPY(r11); + COPY(r12); + COPY(r13); + COPY(r14); + COPY(r15); + + + { + unsigned int tmpflags; + err |= __get_user(tmpflags, &sc->eflags); + regs->eflags = (regs->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); + regs->orig_rax = -1; /* disable syscall checks */ + } + + { + struct _fpstate * buf; + err |= __get_user(buf, &sc->fpstate); + if (buf) { + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + err |= restore_i387(buf); + } + } + + err |= __get_user(*prax, &sc->rax); + return err; + +badframe: + return 1; +} + +asmlinkage long sys_rt_sigreturn(struct pt_regs regs) +{ + struct rt_sigframe *frame = (struct rt_sigframe *)(regs.rsp - 8); + sigset_t set; + stack_t st; + long eax; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &eax)) + goto badframe; + +#if DEBUG_SIG + printk("%d sigreturn rip:%lx rsp:%lx frame:%p rax:%lx\n",current->pid,regs.rip,regs.rsp,frame,eax); +#endif + + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs.rsp); + + return eax; + +badframe: +#if DEBUG_SIG + printk("%d bad frame %p\n",current->pid,frame); +#endif + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + +static int +setup_sigcontext(struct sigcontext *sc, struct _fpstate *fpstate, + struct pt_regs *regs, unsigned long mask) +{ + int tmp, err = 0; + + tmp = 0; + __asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp)); + err |= __put_user(tmp, (unsigned int *)&sc->gs); + __asm__("movl %%fs,%0" : "=r"(tmp): "0"(tmp)); + err |= __put_user(tmp, (unsigned int *)&sc->fs); + + err |= __put_user(regs->rdi, &sc->rdi); + err |= __put_user(regs->rsi, &sc->rsi); + err |= __put_user(regs->rbp, &sc->rbp); + err |= __put_user(regs->rsp, &sc->rsp); + err |= __put_user(regs->rbx, &sc->rbx); + err |= __put_user(regs->rdx, &sc->rdx); + err |= __put_user(regs->rcx, &sc->rcx); + err |= __put_user(regs->rax, &sc->rax); + err |= __put_user(regs->r8, &sc->r8); + err |= __put_user(regs->r9, &sc->r9); + err |= __put_user(regs->r10, &sc->r10); + err |= __put_user(regs->r11, &sc->r11); + err |= __put_user(regs->r12, &sc->r12); + err |= __put_user(regs->r13, &sc->r13); + err |= __put_user(regs->r14, &sc->r14); + err |= __put_user(regs->r15, &sc->r15); + err |= __put_user(current->thread.trap_no, &sc->trapno); + err |= __put_user(current->thread.error_code, &sc->err); + err |= __put_user(regs->rip, &sc->rip); + err |= __put_user(regs->eflags, &sc->eflags); + err |= __put_user(regs->rsp, &sc->rsp_at_signal); + + tmp = save_i387(fpstate); + if (tmp < 0) + err = 1; + else + err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate); + + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + err |= __put_user(current->thread.cr2, &sc->cr2); + + return err; +} + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long rsp; + + /* Default to using normal stack - redzone*/ + rsp = regs->rsp - 128; + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! sas_ss_flags(rsp) == 0) + rsp = current->sas_ss_sp + current->sas_ss_size; + } + + return (void *)((rsp - frame_size) & -16UL); +} + +static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + struct thread_info *ti; + struct rt_sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + if (ka->sa.sa_flags & SA_SIGINFO) { + err |= copy_siginfo_to_user(&frame->info, info); + if (err) + goto give_sigsegv; + } + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->rsp), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, + regs, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + /* x86-64 should always use SA_RESTORER. */ + if (ka->sa.sa_flags & SA_RESTORER) { + err |= __put_user(ka->sa.sa_restorer, &frame->pretcode); + } else { + printk("%s forgot to set SA_RESTORER for signal %d.\n", current->comm, sig); + goto give_sigsegv; + } + + if (err) + goto give_sigsegv; + +#if DEBUG_SIG + printk("%d old rip %lx old rsp %lx old rax %lx\n", current->pid,regs->rip,regs->rsp,regs->rax); +#endif + + ti = current_thread_info(); + + /* Set up registers for signal handler */ + regs->rdi = (ti->exec_domain + && ti->exec_domain->signal_invmap + && sig < 32 + ? ti->exec_domain->signal_invmap[sig] + : sig); + regs->rax = 0; /* In case the signal handler was declared without prototypes */ + + + /* This also works for non SA_SIGINFO handlers because they expect the + next argument after the signal number on the stack. */ + regs->rsi = (unsigned long)&frame->info; + regs->rdx = (unsigned long)&frame->uc; + regs->rsp = (unsigned long) frame; + regs->rip = (unsigned long) ka->sa.sa_handler; + + set_fs(USER_DS); + // XXX: cs + regs->eflags &= ~TF_MASK; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->rip, frame->pretcode); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +/* + * OK, we're invoking a handler + */ + +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +{ +#if DEBUG_SIG + printk("handle_signal pid:%d sig:%lu rip:%lx rsp:%lx regs=%p\n", current->pid, sig, + regs->rip, regs->rsp, regs); +#endif + + /* Are we from a system call? */ + if (regs->orig_rax >= 0) { + /* If so, check system call restarting.. */ + switch (regs->rax) { + case -ERESTARTNOHAND: + regs->rax = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->rax = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + regs->rax = regs->orig_rax; + regs->rip -= 2; + } + } + +#ifdef CONFIG_IA32_EMULATION + if (test_thread_flag(TIF_IA32)) { + if (ka->sa.sa_flags & SA_SIGINFO) + ia32_setup_rt_frame(sig, ka, info, oldset, regs); + else + ia32_setup_frame(sig, ka, oldset, regs); + } else +#endif + setup_rt_frame(sig, ka, info, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +int do_signal(struct pt_regs *regs, sigset_t *oldset) +{ + siginfo_t info; + struct k_sigaction *ka; + + /* + * We want the common case to go fast, which + * is why we may in certain cases get here from + * kernel mode. Just return without doing anything + * if so. + */ + if ((regs->cs & 3) != 3) { + return 1; + } + + if (!oldset) + oldset = ¤t->blocked; + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) { + break; + } + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: { + struct signal_struct *sig; + current->state = TASK_STOPPED; + current->exit_code = signr; + sig = current->p_pptr->sig; + if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + } + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sig_exit(signr, exit_code, &info); + /* NOTREACHED */ + } + } + + /* Reenable any watchpoints before delivering the + * signal to user space. The processor register will + * have been cleared if the watchpoint triggered + * inside the kernel. + */ + __asm__("movq %0,%%db7" : : "r" (current->thread.debugreg[7])); + + /* Whee! Actually deliver the signal. */ + handle_signal(signr, ka, &info, oldset, regs); + return 1; + } + + /* Did we come from a system call? */ + if (regs->orig_rax >= 0) { + /* Restart the system call - no handlers present */ + if (regs->rax == -ERESTARTNOHAND || + regs->rax == -ERESTARTSYS || + regs->rax == -ERESTARTNOINTR) { + regs->rax = regs->orig_rax; + regs->rcx -= 2; + } + } + return 0; +} + +void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, __u32 thread_info_flags) +{ +#if DEBUG_SIG + printk("do_notify_resume flags:%x rip:%lx rsp:%lx caller:%lx pending:%lx\n", + thread_info_flags, regs->rip, regs->rsp, __builtin_return_address(0),signal_pending(current)); +#endif + + + /* deal with pending signal delivery */ + if (thread_info_flags & _TIF_SIGPENDING) + do_signal(regs,oldset); +} diff -Nru a/arch/x86_64/kernel/smp.c b/arch/x86_64/kernel/smp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/smp.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,584 @@ +/* + * Intel SMP support routines. + * + * (c) 1995 Alan Cox, Building #3 + * (c) 1998-99, 2000 Ingo Molnar + * + * This code is released under the GNU General Public License version 2 or + * later. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Some notes on x86 processor bugs affecting SMP operation: + * + * Pentium, Pentium Pro, II, III (and all CPUs) have bugs. + * The Linux implications for SMP are handled as follows: + * + * Pentium III / [Xeon] + * None of the E1AP-E3AP errata are visible to the user. + * + * E1AP. see PII A1AP + * E2AP. see PII A2AP + * E3AP. see PII A3AP + * + * Pentium II / [Xeon] + * None of the A1AP-A3AP errata are visible to the user. + * + * A1AP. see PPro 1AP + * A2AP. see PPro 2AP + * A3AP. see PPro 7AP + * + * Pentium Pro + * None of 1AP-9AP errata are visible to the normal user, + * except occasional delivery of 'spurious interrupt' as trap #15. + * This is very rare and a non-problem. + * + * 1AP. Linux maps APIC as non-cacheable + * 2AP. worked around in hardware + * 3AP. fixed in C0 and above steppings microcode update. + * Linux does not use excessive STARTUP_IPIs. + * 4AP. worked around in hardware + * 5AP. symmetric IO mode (normal Linux operation) not affected. + * 'noapic' mode has vector 0xf filled out properly. + * 6AP. 'noapic' mode might be affected - fixed in later steppings + * 7AP. We do not assume writes to the LVT deassering IRQs + * 8AP. We do not enable low power mode (deep sleep) during MP bootup + * 9AP. We do not use mixed mode + * + * Pentium + * There is a marginal case where REP MOVS on 100MHz SMP + * machines with B stepping processors can fail. XXX should provide + * an L1cache=Writethrough or L1cache=off option. + * + * B stepping CPUs may hang. There are hardware work arounds + * for this. We warn about it in case your board doesnt have the work + * arounds. Basically thats so I can tell anyone with a B stepping + * CPU and SMP problems "tough". + * + * Specific items [From Pentium Processor Specification Update] + * + * 1AP. Linux doesn't use remote read + * 2AP. Linux doesn't trust APIC errors + * 3AP. We work around this + * 4AP. Linux never generated 3 interrupts of the same priority + * to cause a lost local interrupt. + * 5AP. Remote read is never used + * 6AP. not affected - worked around in hardware + * 7AP. not affected - worked around in hardware + * 8AP. worked around in hardware - we get explicit CS errors if not + * 9AP. only 'noapic' mode affected. Might generate spurious + * interrupts, we log only the first one and count the + * rest silently. + * 10AP. not affected - worked around in hardware + * 11AP. Linux reads the APIC between writes to avoid this, as per + * the documentation. Make sure you preserve this as it affects + * the C stepping chips too. + * 12AP. not affected - worked around in hardware + * 13AP. not affected - worked around in hardware + * 14AP. we always deassert INIT during bootup + * 15AP. not affected - worked around in hardware + * 16AP. not affected - worked around in hardware + * 17AP. not affected - worked around in hardware + * 18AP. not affected - worked around in hardware + * 19AP. not affected - worked around in BIOS + * + * If this sounds worrying believe me these bugs are either ___RARE___, + * or are signal timing bugs worked around in hardware and there's + * about nothing of note with C stepping upwards. + */ + +/* The 'big kernel lock' */ +spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; + +struct tlb_state cpu_tlbstate[NR_CPUS] __cacheline_aligned = {[0 ... NR_CPUS-1] = { &init_mm, 0, }}; + +/* + * the following functions deal with sending IPIs between CPUs. + * + * We use 'broadcast', CPU->CPU IPIs and self-IPIs too. + */ + +static inline int __prepare_ICR (unsigned int shortcut, int vector) +{ + return APIC_DM_FIXED | shortcut | vector | APIC_DEST_LOGICAL; +} + +static inline int __prepare_ICR2 (unsigned int mask) +{ + return SET_APIC_DEST_FIELD(mask); +} + +static inline void __send_IPI_shortcut(unsigned int shortcut, int vector) +{ + /* + * Subtle. In the case of the 'never do double writes' workaround + * we have to lock out interrupts to be safe. As we don't care + * of the value read we use an atomic rmw access to avoid costly + * cli/sti. Otherwise we use an even cheaper single atomic write + * to the APIC. + */ + unsigned int cfg; + + /* + * Wait for idle. + */ + apic_wait_icr_idle(); + + /* + * No need to touch the target chip field + */ + cfg = __prepare_ICR(shortcut, vector); + + /* + * Send the IPI. The write to APIC_ICR fires this off. + */ + apic_write_around(APIC_ICR, cfg); +} + +static inline void send_IPI_allbutself(int vector) +{ + /* + * if there are no other CPUs in the system then + * we get an APIC send error if we try to broadcast. + * thus we have to avoid sending IPIs in this case. + */ + if (smp_num_cpus > 1) + __send_IPI_shortcut(APIC_DEST_ALLBUT, vector); +} + +static inline void send_IPI_all(int vector) +{ + __send_IPI_shortcut(APIC_DEST_ALLINC, vector); +} + +void send_IPI_self(int vector) +{ + __send_IPI_shortcut(APIC_DEST_SELF, vector); +} + +static inline void send_IPI_mask(int mask, int vector) +{ + unsigned long cfg; + unsigned long flags; + + __save_flags(flags); + __cli(); + + /* + * Wait for idle. + */ + apic_wait_icr_idle(); + + /* + * prepare target chip field + */ + cfg = __prepare_ICR2(mask); + apic_write_around(APIC_ICR2, cfg); + + /* + * program the ICR + */ + cfg = __prepare_ICR(0, vector); + + /* + * Send the IPI. The write to APIC_ICR fires this off. + */ + apic_write_around(APIC_ICR, cfg); + __restore_flags(flags); +} + +/* + * Smarter SMP flushing macros. + * c/o Linus Torvalds. + * + * These mean you can really definitely utterly forget about + * writing to user space from interrupts. (Its not allowed anyway). + * + * Optimizations Manfred Spraul + */ + +static volatile unsigned long flush_cpumask; +static struct mm_struct * flush_mm; +static unsigned long flush_va; +static spinlock_t tlbstate_lock = SPIN_LOCK_UNLOCKED; +#define FLUSH_ALL 0xffffffff + +/* + * We cannot call mmdrop() because we are in interrupt context, + * instead update mm->cpu_vm_mask. + */ +static void inline leave_mm (unsigned long cpu) +{ + if (cpu_tlbstate[cpu].state == TLBSTATE_OK) + BUG(); + clear_bit(cpu, &cpu_tlbstate[cpu].active_mm->cpu_vm_mask); +} + +/* + * + * The flush IPI assumes that a thread switch happens in this order: + * [cpu0: the cpu that switches] + * 1) switch_mm() either 1a) or 1b) + * 1a) thread switch to a different mm + * 1a1) clear_bit(cpu, &old_mm->cpu_vm_mask); + * Stop ipi delivery for the old mm. This is not synchronized with + * the other cpus, but smp_invalidate_interrupt ignore flush ipis + * for the wrong mm, and in the worst case we perform a superflous + * tlb flush. + * 1a2) set cpu_tlbstate to TLBSTATE_OK + * Now the smp_invalidate_interrupt won't call leave_mm if cpu0 + * was in lazy tlb mode. + * 1a3) update cpu_tlbstate[].active_mm + * Now cpu0 accepts tlb flushes for the new mm. + * 1a4) set_bit(cpu, &new_mm->cpu_vm_mask); + * Now the other cpus will send tlb flush ipis. + * 1a4) change cr3. + * 1b) thread switch without mm change + * cpu_tlbstate[].active_mm is correct, cpu0 already handles + * flush ipis. + * 1b1) set cpu_tlbstate to TLBSTATE_OK + * 1b2) test_and_set the cpu bit in cpu_vm_mask. + * Atomically set the bit [other cpus will start sending flush ipis], + * and test the bit. + * 1b3) if the bit was 0: leave_mm was called, flush the tlb. + * 2) switch %%esp, ie current + * + * The interrupt must handle 2 special cases: + * - cr3 is changed before %%esp, ie. it cannot use current->{active_,}mm. + * - the cpu performs speculative tlb reads, i.e. even if the cpu only + * runs in kernel space, the cpu could load tlb entries for user space + * pages. + * + * The good news is that cpu_tlbstate is local to each cpu, no + * write/read ordering problems. + */ + +/* + * TLB flush IPI: + * + * 1) Flush the tlb entries if the cpu uses the mm that's being flushed. + * 2) Leave the mm if we are in the lazy tlb mode. + */ + +asmlinkage void smp_invalidate_interrupt (void) +{ + unsigned long cpu = smp_processor_id(); + + if (!test_bit(cpu, &flush_cpumask)) + return; + /* + * This was a BUG() but until someone can quote me the + * line from the intel manual that guarantees an IPI to + * multiple CPUs is retried _only_ on the erroring CPUs + * its staying as a return + * + * BUG(); + */ + + if (flush_mm == cpu_tlbstate[cpu].active_mm) { + if (cpu_tlbstate[cpu].state == TLBSTATE_OK) { + if (flush_va == FLUSH_ALL) + local_flush_tlb(); + else + __flush_tlb_one(flush_va); + } else + leave_mm(cpu); + } + ack_APIC_irq(); + clear_bit(cpu, &flush_cpumask); +} + +static void flush_tlb_others (unsigned long cpumask, struct mm_struct *mm, + unsigned long va) +{ + /* + * A couple of (to be removed) sanity checks: + * + * - we do not send IPIs to not-yet booted CPUs. + * - current CPU must not be in mask + * - mask must exist :) + */ + if (!cpumask) + BUG(); + if ((cpumask & cpu_online_map) != cpumask) + BUG(); + if (cpumask & (1 << smp_processor_id())) + BUG(); + if (!mm) + BUG(); + + /* + * i'm not happy about this global shared spinlock in the + * MM hot path, but we'll see how contended it is. + * Temporarily this turns IRQs off, so that lockups are + * detected by the NMI watchdog. + */ + spin_lock(&tlbstate_lock); + + flush_mm = mm; + flush_va = va; + atomic_set_mask(cpumask, &flush_cpumask); + /* + * We have to send the IPI only to + * CPUs affected. + */ + send_IPI_mask(cpumask, INVALIDATE_TLB_VECTOR); + + while (flush_cpumask) + /* nothing. lockup detection does not belong here */; + + flush_mm = NULL; + flush_va = 0; + spin_unlock(&tlbstate_lock); +} + +void flush_tlb_current_task(void) +{ + struct mm_struct *mm = current->mm; + unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id()); + + local_flush_tlb(); + if (cpu_mask) + flush_tlb_others(cpu_mask, mm, FLUSH_ALL); +} + +void flush_tlb_mm (struct mm_struct * mm) +{ + unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id()); + + if (current->active_mm == mm) { + if (current->mm) + local_flush_tlb(); + else + leave_mm(smp_processor_id()); + } + if (cpu_mask) + flush_tlb_others(cpu_mask, mm, FLUSH_ALL); +} + +void flush_tlb_page(struct vm_area_struct * vma, unsigned long va) +{ + struct mm_struct *mm = vma->vm_mm; + unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id()); + + if (current->active_mm == mm) { + if(current->mm) + __flush_tlb_one(va); + else + leave_mm(smp_processor_id()); + } + + if (cpu_mask) + flush_tlb_others(cpu_mask, mm, va); +} + +static inline void do_flush_tlb_all_local(void) +{ + unsigned long cpu = smp_processor_id(); + + __flush_tlb_all(); + if (cpu_tlbstate[cpu].state == TLBSTATE_LAZY) + leave_mm(cpu); +} + +static void flush_tlb_all_ipi(void* info) +{ + do_flush_tlb_all_local(); +} + +void flush_tlb_all(void) +{ + smp_call_function (flush_tlb_all_ipi,0,1,1); + + do_flush_tlb_all_local(); +} + +static spinlock_t migration_lock = SPIN_LOCK_UNLOCKED; +static task_t *new_task; + +/* + * This function sends a 'task migration' IPI to another CPU. + * Must be called from syscall contexts, with interrupts *enabled*. + */ +void smp_migrate_task(int cpu, task_t *p) +{ + /* + * The target CPU will unlock the migration spinlock: + */ + _raw_spin_lock(&migration_lock); + new_task = p; + send_IPI_mask(1 << cpu, TASK_MIGRATION_VECTOR); +} + +/* + * Task migration callback. + */ +asmlinkage void smp_task_migration_interrupt(void) +{ + task_t *p; + + ack_APIC_irq(); + p = new_task; + _raw_spin_unlock(&migration_lock); + sched_task_migrated(p); +} +/* + * this function sends a 'reschedule' IPI to another CPU. + * it goes straight through and wastes no time serializing + * anything. Worst case is that we lose a reschedule ... + */ +void smp_send_reschedule(int cpu) +{ + send_IPI_mask(1 << cpu, RESCHEDULE_VECTOR); +} + +/* + * this function sends a reschedule IPI to all (other) CPUs. + * This should only be used if some 'global' task became runnable, + * such as a RT task, that must be handled now. The first CPU + * that manages to grab the task will run it. + */ +void smp_send_reschedule_all(void) +{ + send_IPI_allbutself(RESCHEDULE_VECTOR); +} + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + */ +static spinlock_t call_lock = SPIN_LOCK_UNLOCKED; + +struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +}; + +static struct call_data_struct * call_data; + +/* + * this function sends a 'generic call function' IPI to all other CPUs + * in the system. + */ + +int smp_call_function (void (*func) (void *info), void *info, int nonatomic, + int wait) +/* + * [SUMMARY] Run a function on all other CPUs. + * The function to run. This must be fast and non-blocking. + * An arbitrary pointer to pass to the function. + * currently unused. + * If true, wait (atomically) until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <> or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler, you may call it from a bottom half handler. + */ +{ + struct call_data_struct data; + int cpus = smp_num_cpus-1; + + if (!cpus) + return 0; + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock_bh(&call_lock); + call_data = &data; + wmb(); + /* Send a message to all other CPUs and wait for them to respond */ + send_IPI_allbutself(CALL_FUNCTION_VECTOR); + + /* Wait for response */ + while (atomic_read(&data.started) != cpus) + barrier(); + + if (wait) + while (atomic_read(&data.finished) != cpus) + barrier(); + spin_unlock_bh(&call_lock); + + return 0; +} + +static void stop_this_cpu (void * dummy) +{ + /* + * Remove this CPU: + */ + clear_bit(smp_processor_id(), &cpu_online_map); + __cli(); + disable_local_APIC(); + for(;;) __asm__("hlt"); + for (;;); +} + +/* + * this function calls the 'stop' function on all other CPUs in the system. + */ + +void smp_send_stop(void) +{ + smp_call_function(stop_this_cpu, NULL, 1, 0); + smp_num_cpus = 1; + + __cli(); + disable_local_APIC(); + __sti(); +} + +/* + * Reschedule call back. Nothing to do, + * all the work is done automatically when + * we return from the interrupt. + */ +asmlinkage void smp_reschedule_interrupt(void) +{ + ack_APIC_irq(); +} + +asmlinkage void smp_call_function_interrupt(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + ack_APIC_irq(); + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function + */ + mb(); + atomic_inc(&call_data->started); + /* + * At this point the info structure may be out of scope unless wait==1 + */ + (*func)(info); + if (wait) { + mb(); + atomic_inc(&call_data->finished); + } +} + diff -Nru a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/smpboot.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1023 @@ +/* + * x86 SMP booting functions + * + * (c) 1995 Alan Cox, Building #3 + * (c) 1998, 1999, 2000 Ingo Molnar + * Copyright 2001 Andi Kleen, SuSE Labs. + * + * Much of the core SMP work is based on previous work by Thomas Radke, to + * whom a great many thanks are extended. + * + * Thanks to Intel for making available several different Pentium, + * Pentium Pro and Pentium-II/Xeon MP machines. + * Original development of Linux SMP code supported by Caldera. + * + * This code is released under the GNU General Public License version 2 or + * later. + * + * Fixes + * Felix Koop : NR_CPUS used properly + * Jose Renau : Handle single CPU case. + * Alan Cox : By repeated request 8) - Total BogoMIP report. + * Greg Wright : Fix for kernel stacks panic. + * Erich Boleyn : MP v1.4 and additional changes. + * Matthias Sattler : Changes for 2.1 kernel map. + * Michel Lespinasse : Changes for 2.1 kernel map. + * Michael Chastain : Change trampoline.S to gnu as. + * Alan Cox : Dumb bug: 'B' step PPro's are fine + * Ingo Molnar : Added APIC timers, based on code + * from Jose Renau + * Ingo Molnar : various cleanups and rewrites + * Tigran Aivazian : fixed "0.00 in /proc/uptime on SMP" bug. + * Maciej W. Rozycki : Bits for genuine 82489DX APICs + * Andi Kleen : Changed for SMP boot into long mode. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Set if we find a B stepping CPU */ +static int smp_b_stepping; + +/* Setup configured maximum number of CPUs to activate */ +static int max_cpus = -1; + +/* Total count of live CPUs */ +int smp_num_cpus = 1; + +/* Bitmask of currently online CPUs */ +unsigned long cpu_online_map; + +/* which CPU (physical APIC ID) maps to which logical CPU number */ +volatile int x86_apicid_to_cpu[NR_CPUS]; +/* which logical CPU number maps to which CPU (physical APIC ID) */ +volatile int x86_cpu_to_apicid[NR_CPUS]; + +static volatile unsigned long cpu_callin_map; +static volatile unsigned long cpu_callout_map; + +/* Per CPU bogomips and other parameters */ +struct cpuinfo_x86 cpu_data[NR_CPUS] __cacheline_aligned; + +/* Set when the idlers are all forked */ +int smp_threads_ready; + +/* + * Setup routine for controlling SMP activation + * + * Command-line option of "nosmp" or "maxcpus=0" will disable SMP + * activation entirely (the MPS table probe still happens, though). + * + * Command-line option of "maxcpus=", where is an integer + * greater than 0, limits the maximum number of CPUs activated in + * SMP mode to . + */ + +static int __init nosmp(char *str) +{ + max_cpus = 0; + return 1; +} + +__setup("nosmp", nosmp); + +static int __init maxcpus(char *str) +{ + get_option(&str, &max_cpus); + return 1; +} + +__setup("maxcpus=", maxcpus); + +/* + * Trampoline 80x86 program as an array. + */ + +extern unsigned char trampoline_data []; +extern unsigned char trampoline_end []; +static unsigned char *trampoline_base; + +/* + * Currently trivial. Write the real->protected mode + * bootstrap into the page concerned. The caller + * has made sure it's suitably aligned. + */ + +static unsigned long __init setup_trampoline(void) +{ + extern __u32 tramp_gdt_ptr; + tramp_gdt_ptr = (__u32)virt_to_phys(&gdt_table); + memcpy(trampoline_base, trampoline_data, trampoline_end - trampoline_data); + return virt_to_phys(trampoline_base); +} + +/* + * We are called very early to get the low memory for the + * SMP bootup trampoline page. + */ +void __init smp_alloc_memory(void) +{ + trampoline_base = (void *) alloc_bootmem_low_pages(PAGE_SIZE); + /* + * Has to be in very low memory so we can execute + * real-mode AP code. + */ + if (__pa(trampoline_base) >= 0x9F000) + BUG(); +} + +/* + * The bootstrap kernel entry code has set these up. Save them for + * a given CPU + */ + +void __init smp_store_cpu_info(int id) +{ + struct cpuinfo_x86 *c = cpu_data + id; + + *c = boot_cpu_data; + identify_cpu(c); + /* + * Mask B, Pentium, but not Pentium MMX + */ + if (c->x86_vendor == X86_VENDOR_INTEL && + c->x86 == 5 && + c->x86_mask >= 1 && c->x86_mask <= 4 && + c->x86_model <= 3) + /* + * Remember we have B step Pentia with bugs + */ + smp_b_stepping = 1; +} + +/* + * Architecture specific routine called by the kernel just before init is + * fired off. This allows the BP to have everything in order [we hope]. + * At the end of this all the APs will hit the system scheduling and off + * we go. Each AP will load the system gdt's and jump through the kernel + * init into idle(). At this point the scheduler will one day take over + * and give them jobs to do. smp_callin is a standard routine + * we use to track CPUs as they power up. + */ + +static atomic_t smp_commenced = ATOMIC_INIT(0); + +void __init smp_commence(void) +{ + /* + * Lets the callins below out of their loop. + */ + Dprintk("Setting commenced=1, go go go\n"); + + wmb(); + atomic_set(&smp_commenced,1); +} + +/* + * TSC synchronization. + * + * We first check wether all CPUs have their TSC's synchronized, + * then we print a warning if not, and always resync. + */ + +static atomic_t tsc_start_flag = ATOMIC_INIT(0); +static atomic_t tsc_count_start = ATOMIC_INIT(0); +static atomic_t tsc_count_stop = ATOMIC_INIT(0); +static unsigned long long tsc_values[NR_CPUS]; + +#define NR_LOOPS 5 + +extern unsigned long fast_gettimeoffset_quotient; + +/* + * accurate 64-bit/32-bit division, expanded to 32-bit divisions and 64-bit + * multiplication. Not terribly optimized but we need it at boot time only + * anyway. + * + * result == a / b + * == (a1 + a2*(2^32)) / b + * == a1/b + a2*(2^32/b) + * == a1/b + a2*((2^32-1)/b) + a2/b + (a2*((2^32-1) % b))/b + * ^---- (this multiplication can overflow) + */ + +static unsigned long long div64 (unsigned long long a, unsigned long b0) +{ + unsigned int a1, a2; + unsigned long long res; + + a1 = ((unsigned int*)&a)[0]; + a2 = ((unsigned int*)&a)[1]; + + res = a1/b0 + + (unsigned long long)a2 * (unsigned long long)(0xffffffff/b0) + + a2 / b0 + + (a2 * (0xffffffff % b0)) / b0; + + return res; +} + +static void __init synchronize_tsc_bp (void) +{ + int i; + unsigned long long t0; + unsigned long long sum, avg; + long long delta; + unsigned long one_usec; + int buggy = 0; + + printk("checking TSC synchronization across CPUs: "); + + one_usec = ((1<<30)/fast_gettimeoffset_quotient)*(1<<2); + + atomic_set(&tsc_start_flag, 1); + wmb(); + + /* + * We loop a few times to get a primed instruction cache, + * then the last pass is more or less synchronized and + * the BP and APs set their cycle counters to zero all at + * once. This reduces the chance of having random offsets + * between the processors, and guarantees that the maximum + * delay between the cycle counters is never bigger than + * the latency of information-passing (cachelines) between + * two CPUs. + */ + for (i = 0; i < NR_LOOPS; i++) { + /* + * all APs synchronize but they loop on '== num_cpus' + */ + while (atomic_read(&tsc_count_start) != smp_num_cpus-1) mb(); + atomic_set(&tsc_count_stop, 0); + wmb(); + /* + * this lets the APs save their current TSC: + */ + atomic_inc(&tsc_count_start); + + rdtscll(tsc_values[smp_processor_id()]); + /* + * We clear the TSC in the last loop: + */ + if (i == NR_LOOPS-1) + write_tsc(0, 0); + + /* + * Wait for all APs to leave the synchronization point: + */ + while (atomic_read(&tsc_count_stop) != smp_num_cpus-1) mb(); + atomic_set(&tsc_count_start, 0); + wmb(); + atomic_inc(&tsc_count_stop); + } + + sum = 0; + for (i = 0; i < smp_num_cpus; i++) { + t0 = tsc_values[i]; + sum += t0; + } + avg = div64(sum, smp_num_cpus); + + sum = 0; + for (i = 0; i < smp_num_cpus; i++) { + delta = tsc_values[i] - avg; + if (delta < 0) + delta = -delta; + /* + * We report bigger than 2 microseconds clock differences. + */ + if (delta > 2*one_usec) { + long realdelta; + if (!buggy) { + buggy = 1; + printk("\n"); + } + realdelta = div64(delta, one_usec); + if (tsc_values[i] < avg) + realdelta = -realdelta; + + printk("BIOS BUG: CPU#%d improperly initialized, has %ld usecs TSC skew! FIXED.\n", i, realdelta); + } + + sum += delta; + } + if (!buggy) + printk("passed.\n"); + ; +} + +static void __init synchronize_tsc_ap (void) +{ + int i; + + /* + * smp_num_cpus is not necessarily known at the time + * this gets called, so we first wait for the BP to + * finish SMP initialization: + */ + while (!atomic_read(&tsc_start_flag)) mb(); + + for (i = 0; i < NR_LOOPS; i++) { + atomic_inc(&tsc_count_start); + while (atomic_read(&tsc_count_start) != smp_num_cpus) mb(); + + rdtscll(tsc_values[smp_processor_id()]); + if (i == NR_LOOPS-1) + write_tsc(0, 0); + + atomic_inc(&tsc_count_stop); + while (atomic_read(&tsc_count_stop) != smp_num_cpus) mb(); + } +} +#undef NR_LOOPS + +extern void calibrate_delay(void); + +static atomic_t init_deasserted; + +void __init smp_callin(void) +{ + int cpuid, phys_id; + unsigned long timeout; + + /* + * If waken up by an INIT in an 82489DX configuration + * we may get here before an INIT-deassert IPI reaches + * our local APIC. We have to wait for the IPI or we'll + * lock up on an APIC access. + */ + while (!atomic_read(&init_deasserted)); + + /* + * (This works even if the APIC is not enabled.) + */ + phys_id = GET_APIC_ID(apic_read(APIC_ID)); + cpuid = smp_processor_id(); + if (test_and_set_bit(cpuid, &cpu_online_map)) { + printk("huh, phys CPU#%d, CPU#%d already present??\n", + phys_id, cpuid); + BUG(); + } + Dprintk("CPU#%d (phys ID: %d) waiting for CALLOUT\n", cpuid, phys_id); + + /* + * STARTUP IPIs are fragile beasts as they might sometimes + * trigger some glue motherboard logic. Complete APIC bus + * silence for 1 second, this overestimates the time the + * boot CPU is spending to send the up to 2 STARTUP IPIs + * by a factor of two. This should be enough. + */ + + /* + * Waiting 2s total for startup (udelay is not yet working) + */ + timeout = jiffies + 2*HZ; + while (time_before(jiffies, timeout)) { + /* + * Has the boot CPU finished it's STARTUP sequence? + */ + if (test_bit(cpuid, &cpu_callout_map)) + break; + rep_nop(); + } + + if (!time_before(jiffies, timeout)) { + printk("BUG: CPU%d started up but did not get a callout!\n", + cpuid); + BUG(); + } + + /* + * the boot CPU has finished the init stage and is spinning + * on callin_map until we finish. We are free to set up this + * CPU, first the APIC. (this is probably redundant on most + * boards) + */ + + Dprintk("CALLIN, before setup_local_APIC().\n"); + setup_local_APIC(); + + sti(); + +#ifdef CONFIG_MTRR + /* + * Must be done before calibration delay is computed + */ + mtrr_init_secondary_cpu (); +#endif + /* + * Get our bogomips. + */ + calibrate_delay(); + Dprintk("Stack at about %p\n",&cpuid); + + /* + * Save our processor parameters + */ + smp_store_cpu_info(cpuid); + + disable_APIC_timer(); + /* + * Allow the master to continue. + */ + set_bit(cpuid, &cpu_callin_map); + + /* + * Synchronize the TSC with the BP + */ + if (cpu_has_tsc) + synchronize_tsc_ap(); +} + +int cpucount; + +extern int cpu_idle(void); + +/* + * Activate a secondary processor. + */ +int __init start_secondary(void *unused) +{ + /* + * Dont put anything before smp_callin(), SMP + * booting is too fragile that we want to limit the + * things done here to the most necessary things. + */ + cpu_init(); + smp_callin(); + while (!atomic_read(&smp_commenced)) + rep_nop(); + enable_APIC_timer(); + /* + * low-memory mappings have been cleared, flush them from + * the local TLBs too. + */ + local_flush_tlb(); + + return cpu_idle(); +} + +/* + * Everything has been set up for the secondary + * CPUs - they just need to reload everything + * from the task structure + * This function must not return. + */ +void __init initialize_secondary(void) +{ + /* + * We don't actually need to load the full TSS, + * basically just the stack pointer and the eip. + */ + + asm volatile( + "movq %0,%%rsp\n\t" + "jmp *%1" + : + :"r" (current->thread.rsp),"r" (current->thread.rip)); +} + +extern void *init_rsp; +extern void (*initial_code)(void); + +static int __init fork_by_hand(void) +{ + struct pt_regs regs; + /* + * don't care about the eip and regs settings since + * we'll never reschedule the forked task. + */ + return do_fork(CLONE_VM|CLONE_PID, 0, ®s, 0); +} + +#if APIC_DEBUG +static inline void inquire_remote_apic(int apicid) +{ + int i, regs[] = { APIC_ID >> 4, APIC_LVR >> 4, APIC_SPIV >> 4 }; + char *names[] = { "ID", "VERSION", "SPIV" }; + int timeout, status; + + printk("Inquiring remote APIC #%d...\n", apicid); + + for (i = 0; i < sizeof(regs) / sizeof(*regs); i++) { + printk("... APIC #%d %s: ", apicid, names[i]); + + /* + * Wait for idle. + */ + apic_wait_icr_idle(); + + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + apic_write_around(APIC_ICR, APIC_DM_REMRD | regs[i]); + + timeout = 0; + do { + udelay(100); + status = apic_read(APIC_ICR) & APIC_ICR_RR_MASK; + } while (status == APIC_ICR_RR_INPROG && timeout++ < 1000); + + switch (status) { + case APIC_ICR_RR_VALID: + status = apic_read(APIC_RRR); + printk("%08x\n", status); + break; + default: + printk("failed\n"); + } + } +} +#endif + +static void __init do_boot_cpu (int apicid) +{ + struct task_struct *idle; + unsigned long send_status, accept_status, boot_status, maxlvt; + int timeout, num_starts, j, cpu; + unsigned long start_eip; + + cpu = ++cpucount; + /* + * We can't use kernel_thread since we must avoid to + * reschedule the child. + */ + if (fork_by_hand() < 0) + panic("failed fork for CPU %d", cpu); + + /* + * We remove it from the pidhash and the runqueue + * once we got the process: + */ + idle = init_task.prev_task; + if (!idle) + panic("No idle process for CPU %d", cpu); + + init_idle(idle,cpu); + + x86_cpu_to_apicid[cpu] = apicid; + x86_apicid_to_cpu[apicid] = cpu; + idle->thread.rip = (unsigned long) start_secondary; + + init_rsp = (void *) (THREAD_SIZE + (char *)idle->thread_info); + + unhash_process(idle); + cpu_pda[cpu].pcurrent = idle; + cpu_pda[cpu].kernelstack = init_rsp - PDA_STACKOFFSET; + + /* start_eip had better be page-aligned! */ + start_eip = setup_trampoline(); + + /* So we see what's up */ + printk("Booting processor %d/%d eip %lx\n", cpu, apicid, start_eip); + + initial_code = initialize_secondary; + + /* + * This grunge runs the startup process for + * the targeted processor. + */ + + atomic_set(&init_deasserted, 0); + + Dprintk("Setting warm reset code and vector.\n"); + + CMOS_WRITE(0xa, 0xf); + local_flush_tlb(); + Dprintk("1.\n"); + *((volatile unsigned short *) phys_to_virt(0x469)) = start_eip >> 4; + Dprintk("2.\n"); + *((volatile unsigned short *) phys_to_virt(0x467)) = start_eip & 0xf; + Dprintk("3.\n"); + + /* + * Be paranoid about clearing APIC errors. + */ + if (APIC_INTEGRATED(apic_version[apicid])) { + apic_read_around(APIC_SPIV); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + } + + /* + * Status is now clean + */ + send_status = 0; + accept_status = 0; + boot_status = 0; + + /* + * Starting actual IPI sequence... + */ + + Dprintk("Asserting INIT.\n"); + + /* + * Turn INIT on target chip + */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + + /* + * Send IPI + */ + apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT + | APIC_DM_INIT); + + Dprintk("Waiting for send to finish...\n"); + timeout = 0; + do { + Dprintk("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + + mdelay(10); + + Dprintk("Deasserting INIT.\n"); + + /* Target chip */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + + /* Send IPI */ + apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT); + + Dprintk("Waiting for send to finish...\n"); + timeout = 0; + do { + Dprintk("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + + atomic_set(&init_deasserted, 1); + + /* + * Should we send STARTUP IPIs ? + * + * Determine this based on the APIC version. + * If we don't have an integrated APIC, don't + * send the STARTUP IPIs. + */ + if (APIC_INTEGRATED(apic_version[apicid])) + num_starts = 2; + else + num_starts = 0; + + /* + * Run STARTUP IPI loop. + */ + Dprintk("#startup loops: %d.\n", num_starts); + + maxlvt = get_maxlvt(); + + for (j = 1; j <= num_starts; j++) { + Dprintk("Sending STARTUP #%d.\n",j); + apic_read_around(APIC_SPIV); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + Dprintk("After apic_write.\n"); + + /* + * STARTUP IPI + */ + + /* Target chip */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + + /* Boot on the stack */ + /* Kick the second */ + apic_write_around(APIC_ICR, APIC_DM_STARTUP + | (start_eip >> 12)); + + /* + * Give the other CPU some time to accept the IPI. + */ + udelay(300); + + Dprintk("Startup point 1.\n"); + + Dprintk("Waiting for send to finish...\n"); + timeout = 0; + do { + Dprintk("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + + /* + * Give the other CPU some time to accept the IPI. + */ + udelay(200); + /* + * Due to the Pentium erratum 3AP. + */ + if (maxlvt > 3) { + apic_read_around(APIC_SPIV); + apic_write(APIC_ESR, 0); + } + accept_status = (apic_read(APIC_ESR) & 0xEF); + if (send_status || accept_status) + break; + } + Dprintk("After Startup.\n"); + + if (send_status) + printk("APIC never delivered???\n"); + if (accept_status) + printk("APIC delivery error (%lx).\n", accept_status); + + if (!send_status && !accept_status) { + /* + * allow APs to start initializing. + */ + Dprintk("Before Callout %d.\n", cpu); + set_bit(cpu, &cpu_callout_map); + Dprintk("After Callout %d.\n", cpu); + + /* + * Wait 5s total for a response + */ + for (timeout = 0; timeout < 50000; timeout++) { + if (test_bit(cpu, &cpu_callin_map)) + break; /* It has booted */ + udelay(100); + } + + if (test_bit(cpu, &cpu_callin_map)) { + /* number CPUs logically, starting from 1 (BSP is 0) */ + Dprintk("OK.\n"); + printk("CPU%d: ", cpu); + print_cpu_info(&cpu_data[cpu]); + Dprintk("CPU has booted.\n"); + } else { + boot_status = 1; + if (*((volatile unsigned char *)phys_to_virt(8192)) + == 0xA5) + /* trampoline started but...? */ + printk("Stuck ??\n"); + else + /* trampoline code not run */ + printk("Not responding.\n"); +#if APIC_DEBUG + inquire_remote_apic(apicid); +#endif + } + } + if (send_status || accept_status || boot_status) { + x86_cpu_to_apicid[cpu] = -1; + x86_apicid_to_cpu[apicid] = -1; + cpucount--; + } + + /* mark "stuck" area as not stuck */ + *((volatile unsigned long *)phys_to_virt(8192)) = 0; +} + +cycles_t cacheflush_time; +unsigned long cache_decay_ticks; + +static void smp_tune_scheduling (void) +{ + unsigned long cachesize; /* kB */ + unsigned long bandwidth = 350; /* MB/s */ + /* + * Rough estimation for SMP scheduling, this is the number of + * cycles it takes for a fully memory-limited process to flush + * the SMP-local cache. + * + * (For a P5 this pretty much means we will choose another idle + * CPU almost always at wakeup time (this is due to the small + * L1 cache), on PIIs it's around 50-100 usecs, depending on + * the cache size) + */ + + if (!cpu_khz) { + /* + * this basically disables processor-affinity + * scheduling on SMP without a TSC. + */ + cacheflush_time = 0; + return; + } else { + cachesize = boot_cpu_data.x86_cache_size; + if (cachesize == -1) { + cachesize = 16; /* Pentiums, 2x8kB cache */ + bandwidth = 100; + } + + cacheflush_time = (cpu_khz>>10) * (cachesize<<10) / bandwidth; + } + + cache_decay_ticks = (long)cacheflush_time/cpu_khz * HZ / 1000; + + printk("per-CPU timeslice cutoff: %ld.%02ld usecs.\n", + (long)cacheflush_time/(cpu_khz/1000), + ((long)cacheflush_time*100/(cpu_khz/1000)) % 100); + printk("task migration cache decay timeout: %ld msecs.\n", + (cache_decay_ticks + 1) * 1000 / HZ); +} + +/* + * Cycle through the processors sending APIC IPIs to boot each. + */ + +extern int prof_multiplier[NR_CPUS]; +extern int prof_old_multiplier[NR_CPUS]; +extern int prof_counter[NR_CPUS]; + +void __init smp_boot_cpus(void) +{ + int apicid, cpu; + +#ifdef CONFIG_MTRR + /* Must be done before other processors booted */ + mtrr_init_boot_cpu (); +#endif + /* + * Initialize the logical to physical CPU number mapping + * and the per-CPU profiling counter/multiplier + */ + + for (apicid = 0; apicid < NR_CPUS; apicid++) { + x86_apicid_to_cpu[apicid] = -1; + prof_counter[apicid] = 1; + prof_old_multiplier[apicid] = 1; + prof_multiplier[apicid] = 1; + } + + /* + * Setup boot CPU information + */ + smp_store_cpu_info(0); /* Final full version of the data */ + printk("CPU%d: ", 0); + print_cpu_info(&cpu_data[0]); + + /* + * We have the boot CPU online for sure. + */ + set_bit(0, &cpu_online_map); + x86_apicid_to_cpu[boot_cpu_id] = 0; + x86_cpu_to_apicid[0] = boot_cpu_id; + global_irq_holder = NO_PROC_ID; + current_thread_info()->cpu = 0; + smp_tune_scheduling(); + + /* + * If we couldnt find an SMP configuration at boot time, + * get out of here now! + */ + if (!smp_found_config) { + printk(KERN_NOTICE "SMP motherboard not detected.\n"); + io_apic_irqs = 0; + cpu_online_map = phys_cpu_present_map = 1; + smp_num_cpus = 1; + if (APIC_init_uniprocessor()) + printk(KERN_NOTICE "Local APIC not detected." + " Using dummy APIC emulation.\n"); + goto smp_done; + } + + /* + * Should not be necessary because the MP table should list the boot + * CPU too, but we do it for the sake of robustness anyway. + */ + if (!test_bit(boot_cpu_id, &phys_cpu_present_map)) { + printk("weird, boot CPU (#%d) not listed by the BIOS.\n", + boot_cpu_id); + phys_cpu_present_map |= (1 << hard_smp_processor_id()); + } + + /* + * If we couldn't find a local APIC, then get out of here now! + */ + if (APIC_INTEGRATED(apic_version[boot_cpu_id]) && + !test_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability)) { + printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n", + boot_cpu_id); + printk(KERN_ERR "... forcing use of dummy APIC emulation. (tell your hw vendor)\n"); + io_apic_irqs = 0; + cpu_online_map = phys_cpu_present_map = 1; + smp_num_cpus = 1; + goto smp_done; + } + + verify_local_APIC(); + + /* + * If SMP should be disabled, then really disable it! + */ + if (!max_cpus) { + smp_found_config = 0; + printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n"); + io_apic_irqs = 0; + cpu_online_map = phys_cpu_present_map = 1; + smp_num_cpus = 1; + goto smp_done; + } + + connect_bsp_APIC(); + setup_local_APIC(); + + if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_id) + BUG(); + + /* + * Now scan the CPU present map and fire up the other CPUs. + */ + Dprintk("CPU present map: %lx\n", phys_cpu_present_map); + + for (apicid = 0; apicid < NR_CPUS; apicid++) { + /* + * Don't even attempt to start the boot CPU! + */ + if (apicid == boot_cpu_id) + continue; + + if (!(phys_cpu_present_map & (1 << apicid))) + continue; + if ((max_cpus >= 0) && (max_cpus <= cpucount+1)) + continue; + + do_boot_cpu(apicid); + + /* + * Make sure we unmap all failed CPUs + */ + if ((x86_apicid_to_cpu[apicid] == -1) && + (phys_cpu_present_map & (1 << apicid))) + printk("phys CPU #%d not responding - cannot use it.\n",apicid); + } + + /* + * Cleanup possible dangling ends... + */ + { + /* + * Install writable page 0 entry to set BIOS data area. + */ + local_flush_tlb(); + + /* + * Paranoid: Set warm reset code and vector here back + * to default values. + */ + CMOS_WRITE(0, 0xf); + + *((volatile long *) phys_to_virt(0x467)) = 0; + } + + /* + * Allow the user to impress friends. + */ + + Dprintk("Before bogomips.\n"); + if (!cpucount) { + printk(KERN_ERR "Error: only one processor found.\n"); + } else { + unsigned long bogosum = 0; + for (cpu = 0; cpu < NR_CPUS; cpu++) + if (cpu_online_map & (1< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way Unix traditionally does this, though. + */ +asmlinkage long sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +long sys_mmap(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long off) +{ + long error; + struct file * file; + + error = -EINVAL; + if (off & ~PAGE_MASK) + goto out; + + error = -EBADF; + file = NULL; + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, off >> PAGE_SHIFT); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct vm_area_struct *vma; + unsigned long end = TASK_SIZE; + + if (flags & MAP_32BIT) + end = 0xffffffff; + if (len > TASK_SIZE) + return -ENOMEM; + if (!addr) { + addr = TASK_UNMAPPED_64; + if (test_thread_flag(TIF_IA32) || (flags & MAP_32BIT)) { + addr = TASK_UNMAPPED_32; + } + } + addr = PAGE_ALIGN(addr); + + for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) { + /* At this point: (!vma || addr < vma->vm_end). */ + if (TASK_SIZE - len < addr) + return -ENOMEM; + if (!vma || addr + len <= vma->vm_start) + return addr; + addr = vma->vm_end; + } +} + +/* + * Old cruft + */ +asmlinkage long sys_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +asmlinkage long sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} diff -Nru a/arch/x86_64/kernel/syscall.c b/arch/x86_64/kernel/syscall.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/syscall.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,25 @@ +/* System call table for x86-64. */ + +#include +#include +#include + +#define __NO_STUBS + +#define __SYSCALL(nr, sym) extern asmlinkage void sym(void) ; +#undef _ASM_X86_64_UNISTD_H_ +#include + +#undef __SYSCALL +#define __SYSCALL(nr, sym) [ nr ] = sym, +#undef _ASM_X86_64_UNISTD_H_ + +typedef void (*sys_call_ptr_t)(void); + +extern void sys_ni_syscall(void); + +sys_call_ptr_t sys_call_table[__NR_syscall_max+1] __cacheline_aligned = { + /* Smells like a like a compiler bug -- it doesn't work when the & below is removed. */ + [0 ... __NR_syscall_max] = &sys_ni_syscall, +#include +}; diff -Nru a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/time.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,494 @@ +/* + * linux/arch/x86-64/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * This file contains the PC-specific time handling details: + * reading the RTC at bootup, etc.. + * 1994-07-02 Alan Modra + * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime + * 1995-03-26 Markus Kuhn + * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 + * precision CMOS clock update + * 1996-05-03 Ingo Molnar + * fixed time warps in do_[slow|fast]_gettimeoffset() + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + * 1998-09-05 (Various) + * More robust do_fast_gettimeoffset() algorithm implemented + * (works with APM, Cyrix 6x86MX and Centaur C6), + * monotonic gettimeofday() with fast_get_timeoffset(), + * drift-proof precision TSC calibration on boot + * (C. Scott Ananian , Andrew D. + * Balsa , Philip Gladstone ; + * ported from 2.0.35 Jumbo-9 by Michael Krause ). + * 1998-12-16 Andrea Arcangeli + * Fixed Jumbo-9 code in 2.1.131: do_gettimeofday was missing 1 jiffy + * because was not accounting lost_ticks. + * 1998-12-24 Copyright (C) 1998 Andrea Arcangeli + * Fixed a xtime SMP race (we need the xtime_lock rw spinlock to + * serialize accesses to xtime/lost_ticks). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* + * for x86_do_profile() + */ +#include + + +unsigned long cpu_khz; /* Detected as we calibrate the TSC */ + +/* Number of usecs that the last interrupt was delayed */ +int __delay_at_last_interrupt __section_delay_at_last_interrupt; + +unsigned long __last_tsc_low __section_last_tsc_low; /* lsb 32 bits of Time Stamp Counter */ + +/* Cached *multiplier* to convert TSC counts to microseconds. + * (see the equation below). + * Equal to 2^32 * (1 / (clocks per usec) ). + * Initialized in time_init. + */ +unsigned long __fast_gettimeoffset_quotient __section_fast_gettimeoffset_quotient; + +extern rwlock_t xtime_lock; +struct timeval __xtime __section_xtime; +volatile unsigned long __jiffies __section_jiffies; +unsigned long __wall_jiffies __section_wall_jiffies; +struct timezone __sys_tz __section_sys_tz; + +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; + +static inline unsigned long do_gettimeoffset(void) +{ + register unsigned long eax, edx; + + /* Read the Time Stamp Counter */ + + rdtsc(eax,edx); + + /* .. relative to previous jiffy (32 bits is enough) */ + eax -= last_tsc_low; /* tsc_low delta */ + + /* + * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient + * = (tsc_low delta) * (usecs_per_clock) + * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) + * + * Using a mull instead of a divl saves up to 31 clock cycles + * in the critical path. + */ + + edx = (eax*fast_gettimeoffset_quotient) >> 32; + + /* our adjusted time offset in microseconds */ + return delay_at_last_interrupt + edx; +} + + + + +#define TICK_SIZE tick + +spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED; + +extern spinlock_t i8259A_lock; + +void do_settimeofday(struct timeval *tv) +{ + write_lock_irq(&xtime_lock); + vxtime_lock(); + /* + * This is revolting. We need to set "xtime" correctly. However, the + * value in this location is the value at the most recent update of + * wall time. Discover what correction gettimeofday() would have + * made, and then undo it! + */ + tv->tv_usec -= do_gettimeoffset(); + tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ); + + while (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + vxtime_unlock(); + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_unlock_irq(&xtime_lock); +} + +/* + * In order to set the CMOS clock precisely, set_rtc_mmss has to be + * called 500 ms after the second nowtime has started, because when + * nowtime is written into the registers of the CMOS clock, it will + * jump to the next second precisely 500 ms later. Check the Motorola + * MC146818A or Dallas DS12887 data sheet for details. + * + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you'll only notice that after reboot! + */ +static int set_rtc_mmss(unsigned long nowtime) +{ + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + unsigned char save_control, save_freq_select; + + /* gets recalled with irq locally disabled */ + spin_lock(&rtc_lock); + save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + cmos_minutes = CMOS_READ(RTC_MINUTES); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + BCD_TO_BIN(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(real_seconds); + BIN_TO_BCD(real_minutes); + } + CMOS_WRITE(real_seconds,RTC_SECONDS); + CMOS_WRITE(real_minutes,RTC_MINUTES); + } else { + printk(KERN_WARNING + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + spin_unlock(&rtc_lock); + + return retval; +} + +/* last time the cmos clock got updated */ +static long last_rtc_update; + +int timer_ack; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +#ifdef CONFIG_X86_IO_APIC + if (timer_ack) { + /* + * Subtle, when I/O APICs are used we have to ack timer IRQ + * manually to reset the IRR bit for do_slow_gettimeoffset(). + * This will also deassert NMI lines for the watchdog if run + * on an 82489DX-based system. + */ + spin_lock(&i8259A_lock); + outb(0x0c, 0x20); + /* Ack the IRQ; AEOI will end it automatically. */ + inb(0x20); + spin_unlock(&i8259A_lock); + } +#endif + + do_timer(regs); +/* + * In the SMP case we use the local APIC timer interrupt to do the + * profiling, except when we simulate SMP mode on a uniprocessor + * system, in that case we have to call the local interrupt handler. + */ +#ifndef CONFIG_X86_LOCAL_APIC + if (!user_mode(regs)) + x86_do_profile(regs->rip); +#else + if (!using_apic_timer) + smp_local_timer_interrupt(regs); +#endif + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && + xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + } +} + +static int use_tsc; + +/* + * This is the same as the above, except we _also_ save the current + * Time Stamp Counter value at the time of the timer interrupt, so that + * we later on can estimate the time of day more exactly. + */ +static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int count; + + /* + * Here we are in the timer irq handler. We just have irqs locally + * disabled but we don't know if the timer_bh is running on the other + * CPU. We need to avoid to SMP race with it. NOTE: we don' t need + * the irq version of write_lock because as just said we have irq + * locally disabled. -arca + */ + write_lock(&xtime_lock); + vxtime_lock(); + + if (use_tsc) + { + /* + * It is important that these two operations happen almost at + * the same time. We do the RDTSC stuff first, since it's + * faster. To avoid any inconsistencies, we need interrupts + * disabled locally. + */ + + /* + * Interrupts are just disabled locally since the timer irq + * has the SA_INTERRUPT flag set. -arca + */ + + /* read Pentium cycle counter */ + + rdtscl(last_tsc_low); + + spin_lock(&i8253_lock); + outb_p(0x00, 0x43); /* latch the count ASAP */ + + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + spin_unlock(&i8253_lock); + + count = ((LATCH-1) - count) * TICK_SIZE; + delay_at_last_interrupt = (count + LATCH/2) / LATCH; + } + + do_timer_interrupt(irq, NULL, regs); + + vxtime_unlock(); + write_unlock(&xtime_lock); + +} + +/* not static: needed by APM */ +unsigned long get_cmos_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ +#ifndef CONFIG_SIMNOW + int i; + /* FIXME: This would take eons in emulated environment */ + /* read RTC exactly on falling edge of update flag */ + for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) + break; + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ + if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) + break; +#endif + do { /* Isn't this overkill ? UIP above should guarantee consistency */ + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + } while (sec != CMOS_READ(RTC_SECONDS)); + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + +static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL}; + +/* ------ Calibrate the TSC ------- + * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset(). + * Too much 64-bit arithmetic here to do this cleanly in C, and for + * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2) + * output busy loop as low as possible. We avoid reading the CTC registers + * directly because of the awkward 8-bit access mechanism of the 82C54 + * device. + */ + +#define CALIBRATE_LATCH (5 * LATCH) +#define CALIBRATE_TIME (5 * 1000020/HZ) + +static unsigned long __init calibrate_tsc(void) +{ + /* Set the Gate high, disable speaker */ + outb((inb(0x61) & ~0x02) | 0x01, 0x61); + + /* + * Now let's take care of CTC channel 2 + * + * Set the Gate high, program CTC channel 2 for mode 0, + * (interrupt on terminal count mode), binary count, + * load 5 * LATCH count, (LSB and MSB) to begin countdown. + */ + outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */ + outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */ + + { + unsigned long start; + unsigned long end; + unsigned long count; + + { + int low, high; + rdtsc(low,high); + start = ((u64)high)<<32 | low; + } + count = 0; + do { + count++; + } while ((inb(0x61) & 0x20) == 0); + + { + int low, high; + rdtsc(low,high); + end = ((u64)high)<<32 | low; + last_tsc_low = low; + } + + + /* Error: ECTCNEVERSET */ + if (count <= 1) + goto bad_ctc; + + end -= start; + + /* Error: ECPUTOOSLOW */ + if (end <= CALIBRATE_TIME) + goto bad_ctc; + + end = (((u64)CALIBRATE_TIME)<<32)/end; + return end; + } + + /* + * The CTC wasn't reliable: we got a hit on the very first read, + * or the CPU was so fast/slow that the quotient wouldn't fit in + * 32 bits.. + */ +bad_ctc: + return 0; +} + +void __init time_init(void) +{ + extern int x86_udelay_tsc; + + xtime.tv_sec = get_cmos_time(); + xtime.tv_usec = 0; + +/* + * If we have APM enabled or the CPU clock speed is variable + * (CPU stops clock on HLT or slows clock to save power) + * then the TSC timestamps may diverge by up to 1 jiffy from + * 'real time' but nothing will break. + * The most frequent case is that the CPU is "woken" from a halt + * state by the timer interrupt itself, so we get 0 error. In the + * rare cases where a driver would "wake" the CPU and request a + * timestamp, the maximum error is < 1 jiffy. But timestamps are + * still perfectly ordered. + */ + + if (cpu_has_tsc) { + unsigned long tsc_quotient = calibrate_tsc(); + if (tsc_quotient) { + fast_gettimeoffset_quotient = tsc_quotient; + use_tsc = 1; + /* + * We could be more selective here I suspect + * and just enable this for the next intel chips ? + */ + x86_udelay_tsc = 1; + + /* report CPU clock rate in Hz. + * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = + * clock/second. Our precision is about 100 ppm. + */ + { + cpu_khz = ((1000000*(1UL<<32)) / tsc_quotient); /* FIXME: is it right? */ + printk("Detected %ld Hz processor.\n", cpu_khz); + } + } + } + + setup_irq(0, &irq0); +} diff -Nru a/arch/x86_64/kernel/trampoline.S b/arch/x86_64/kernel/trampoline.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/trampoline.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,72 @@ +/* + * + * Trampoline.S Derived from Setup.S by Linus Torvalds + * + * 4 Jan 1997 Michael Chastain: changed to gnu as. + * + * Entry: CS:IP point to the start of our code, we are + * in real mode with no stack, but the rest of the + * trampoline page to make our stack and everything else + * is a mystery. + * + * In fact we don't actually need a stack so we don't + * set one up. + * + * On entry to trampoline_data, the processor is in real mode + * with 16-bit addressing and 16-bit data. CS has some value + * and IP is zero. Thus, data addresses need to be absolute + * (no relocation) and are taken with regard to r_base. + * + * If you work on this file, check the object module with objdump + * --full-contents --reloc to make sure there are no relocation + * entries. For the GDT entry we do hand relocation in smpboot.c + * because of 64bit linker limitations. + */ + +#include +#include +#include + +.data + +.code16 + +ENTRY(trampoline_data) +r_base = . + + mov %cs, %ax # Code and data in the same place + mov %ax, %ds + + mov $1, %bx # Flag an SMP trampoline + cli # We should be safe anyway + + movl $0xA5A5A5A5, trampoline_data - r_base + # write marker for master knows we're running + + lidt idt_48 - r_base # load idt with 0, 0 + lgdt gdt_48 - r_base # load gdt with whatever is appropriate + + xor %ax, %ax + inc %ax # protected mode (PE) bit + lmsw %ax # into protected mode + jmp flush_instr +flush_instr: + ljmpl $__KERNEL_CS, $0x00100000 + # jump to startup_32 in arch/i386/kernel/head.S + +idt_48: + .word 0 # idt limit = 0 + .word 0, 0 # idt base = 0L + +gdt_48: + .short 0x0800 # gdt limit = 2048, 256 GDT entries + .globl tramp_gdt_ptr +tramp_gdt_ptr: + .long 0 # gdt base = gdt (first SMP CPU) + # this is filled in by C because the 64bit + # linker doesn't support absolute 32bit + # relocations. + + +.globl SYMBOL_NAME(trampoline_end) +SYMBOL_NAME_LABEL(trampoline_end) diff -Nru a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/traps.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,771 @@ +/* + * linux/arch/x86-64/traps.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs + * + * Pentium III FXSR, SSE support + * Gareth Hughes , May 2000 + */ + +/* + * 'traps.c' handles hardware traps and faults after we have saved some + * state in 'entry.S'. + */ +#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 + +asmlinkage int system_call(void); +asmlinkage int kernel_syscall(void); +extern void ia32_syscall(void); + +char doublefault_stack[4*1024]; +#ifndef CONFIG_SMP +char stackfault_stack[4*1024]; +#endif + +/* We really need to get rid of F0 0F workaround. Setting up new idts + is not an option */ + +struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, + { 0, 0 }, { 0, 0 } }; + +extern struct gate_struct idt_table[256]; + +asmlinkage void divide_error(void); +asmlinkage void debug(void); +asmlinkage void nmi(void); +asmlinkage void int3(void); +asmlinkage void overflow(void); +asmlinkage void bounds(void); +asmlinkage void invalid_op(void); +asmlinkage void device_not_available(void); +asmlinkage void double_fault(void); +asmlinkage void coprocessor_segment_overrun(void); +asmlinkage void invalid_TSS(void); +asmlinkage void segment_not_present(void); +asmlinkage void stack_segment(void); +asmlinkage void general_protection(void); +asmlinkage void page_fault(void); +asmlinkage void coprocessor_error(void); +asmlinkage void simd_coprocessor_error(void); +asmlinkage void reserved(void); +asmlinkage void alignment_check(void); +asmlinkage void spurious_interrupt_bug(void); + +struct notifier_block *die_chain; + +int kstack_depth_to_print = 24; + +#ifdef CONFIG_MODULES + +extern struct module *module_list; +extern struct module kernel_module; + +static inline int kernel_text_address(unsigned long addr) +{ + int retval = 0; + struct module *mod; + + if (addr >= (unsigned long) &_stext && + addr <= (unsigned long) &_etext) + return 1; + + for (mod = module_list; mod != &kernel_module; mod = mod->next) { + /* mod_bound tests for addr being inside the vmalloc'ed + * module area. Of course it'd be better to test only + * for the .text subset... */ + if (mod_bound(addr, 0, mod)) { + retval = 1; + break; + } + } + + return retval; +} + +#else + +static inline int kernel_text_address(unsigned long addr) +{ + return (addr >= (unsigned long) &_stext && + addr <= (unsigned long) &_etext); +} + +#endif + +/* + * These constants are for searching for possible module text + * segments. MODULE_RANGE is a guess of how much space is likely + * to be vmalloced. + */ +#define MODULE_RANGE (8*1024*1024) + +void show_trace(unsigned long *stack) +{ + unsigned long addr; + unsigned long *irqstack, *irqstack_end; + /* FIXME: should read the cpuid from the APIC; to still work with bogus %gs */ + const int cpu = smp_processor_id(); + int i; + + printk("\nCall Trace: "); + + irqstack = (unsigned long *) &(cpu_pda[cpu].irqstack); + irqstack_end = (unsigned long *) ((char *)irqstack + sizeof_field(struct x8664_pda, irqstack)); + + i = 1; + if (stack >= irqstack && stack < irqstack_end) { + while (stack < irqstack_end) { + addr = *stack++; + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (kernel_text_address(addr)) { + if (i && ((i % 6) == 0)) + printk("\n "); + printk("[<%016lx>] ", addr); + i++; + } + } + stack = (unsigned long *) (irqstack_end[-1]); + printk(" "); +#if 1 + if (stack < (unsigned long *)current || + (char*)stack > ((char*)current->thread_info)+THREAD_SIZE) + printk("\n" KERN_DEBUG + "no stack at the end of irqstack; stack:%p, cur:%p/%p\n", + stack, current, ((char*)current)+THREAD_SIZE); +#endif + } + + + + while (((long) stack & (THREAD_SIZE-1)) != 0) { + addr = *stack++; + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (kernel_text_address(addr)) { + if (i && ((i % 6) == 0)) + printk("\n "); + printk("[<%016lx>] ", addr); + i++; + } + } + printk("\n"); +} + +void show_trace_task(struct task_struct *tsk) +{ + unsigned long rsp = tsk->thread.rsp; + + /* User space on another CPU? */ + if ((rsp ^ (unsigned long)tsk->thread_info) & (PAGE_MASK<<1)) + return; + show_trace((unsigned long *)rsp); +} + +void show_stack(unsigned long * rsp) +{ + unsigned long *stack; + int i; + + // debugging aid: "show_stack(NULL);" prints the + // back trace for this cpu. + + if(rsp==NULL) + rsp=(unsigned long*)&rsp; + + stack = rsp; + for(i=0; i < kstack_depth_to_print; i++) { + if (((long) stack & (THREAD_SIZE-1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%016lx ", *stack++); + } +} + +extern void dump_pagetable(void); + +void show_registers(struct pt_regs *regs) +{ + int i; + int in_kernel = 1; + unsigned long rsp; +#ifdef CONFIG_SMP + /* For SMP should get the APIC id here, just to protect against corrupted GS */ + const int cpu = smp_processor_id(); +#else + const int cpu = 0; +#endif + struct task_struct *cur = cpu_pda[cpu].pcurrent; + + rsp = (unsigned long) (®s->rsp); + if (regs->rsp < TASK_SIZE) { + in_kernel = 0; + rsp = regs->rsp; + } + printk("CPU %d ", cpu); + show_regs(regs); + printk("Process %s (pid: %d, thread_info:%p task:%p)\n", + cur->comm, cur->pid, cur->thread_info, cur); + + dump_pagetable(); + + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (in_kernel) { + + printk("Stack: "); + show_stack((unsigned long*)rsp); + + printk("\nCode: "); + if(regs->rip < PAGE_OFFSET) + goto bad; + + for(i=0;i<20;i++) + { + unsigned char c; + if(__get_user(c, &((unsigned char*)regs->rip)[i])) { +bad: + printk(" Bad RIP value."); + break; + } + printk("%02x ", c); + } + } + printk("\n"); + show_trace((unsigned long *)rsp); +} + +void handle_BUG(struct pt_regs *regs) +{ + struct bug_frame f; + char tmp; + + if ((regs->cs & 3) || regs->rip < __PAGE_OFFSET) + return; + if (__copy_from_user(&f, (struct bug_frame *) regs->rip, sizeof(struct bug_frame))) + return; + if ((unsigned long)f.filename < __PAGE_OFFSET) + return; + if (__get_user(tmp, f.filename)) + f.filename = "unmapped filename"; + printk("Kernel BUG at %.30s:%d\n", f.filename, f.line); +} + +spinlock_t die_lock = SPIN_LOCK_UNLOCKED; + +void die(const char * str, struct pt_regs * regs, long err) +{ + struct die_args args = { regs, str, err }; + console_verbose(); + notifier_call_chain(&die_chain, DIE_DIE, &args); + spin_lock_irq(&die_lock); + bust_spinlocks(1); + handle_BUG(regs); + printk("%s: %04lx\n", str, err & 0xffff); + show_registers(regs); + bust_spinlocks(0); + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err) +{ + if (!(regs->eflags & VM_MASK) && (regs->rip >= TASK_SIZE)) + die(str, regs, err); +} + +static inline unsigned long get_cr2(void) +{ + unsigned long address; + + /* get the address */ + __asm__("movq %%cr2,%0":"=r" (address)); + return address; +} + +static void inline do_trap(int trapnr, int signr, char *str, int vm86, + struct pt_regs * regs, long error_code, siginfo_t *info) +{ + if ((regs->cs & 3) == 0) + goto kernel_trap; + + +#if 0 + printk("%d/%s trap %d sig %d %s rip:%lx rsp:%lx error_code:%lx\n", + current->pid, current->comm, + trapnr, signr, str, regs->rip, regs->rsp, error_code); +#endif + + { + struct task_struct *tsk = current; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = trapnr; + if (info) + force_sig_info(signr, info, tsk); + else + force_sig(signr, tsk); + return; + } + + kernel_trap: { + unsigned long fixup = search_exception_table(regs->rip); + if (fixup) + regs->rip = fixup; + else + die(str, regs, error_code); + return; + } +} + +#define DO_ERROR(trapnr, signr, str, name) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \ +} + +#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + siginfo_t info; \ + info.si_signo = signr; \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void *)siaddr; \ + do_trap(trapnr, signr, str, 0, regs, error_code, &info); \ +} + +#define DO_VM86_ERROR(trapnr, signr, str, name) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \ +} + +#define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + siginfo_t info; \ + info.si_signo = signr; \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void *)siaddr; \ + do_trap(trapnr, signr, str, 1, regs, error_code, &info); \ +} + +DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->rip) +DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) +DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds) +DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, ILL_ILLOPN, regs->rip) +DO_VM86_ERROR( 7, SIGSEGV, "device not available", device_not_available) +DO_ERROR( 8, SIGSEGV, "double fault", double_fault) +DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun) +DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS) +DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) +DO_ERROR(12, SIGBUS, "stack segment", stack_segment) +DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, get_cr2()) +DO_ERROR(18, SIGSEGV, "reserved", reserved) + +asmlinkage void do_int3(struct pt_regs * regs, long error_code) +{ + struct die_args args = { regs, "int3", error_code }; + notifier_call_chain(&die_chain, DIE_INT3, &args); + do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL); +} + +asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) +{ + if ((regs->cs & 3)==0) + goto gp_in_kernel; + + current->thread.error_code = error_code; + current->thread.trap_no = 13; + force_sig(SIGSEGV, current); + return; + +gp_in_kernel: + { + unsigned long fixup; + fixup = search_exception_table(regs->rip); + if (fixup) { + regs->rip = fixup; + return; + } + die("general protection fault", regs, error_code); + } +} + +static void mem_parity_error(unsigned char reason, struct pt_regs * regs) +{ + printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); + printk("You probably have a hardware problem with your RAM chips\n"); + + /* Clear and disable the memory parity error line. */ + reason = (reason & 0xf) | 4; + outb(reason, 0x61); +} + +static void io_check_error(unsigned char reason, struct pt_regs * regs) +{ + printk("NMI: IOCK error (debug interrupt?)\n"); + show_registers(regs); + + /* Re-enable the IOCK line, wait for a few seconds */ + reason = (reason & 0xf) | 8; + outb(reason, 0x61); + mdelay(2000); + reason &= ~8; + outb(reason, 0x61); +} + +static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) +{ + printk("Uhhuh. NMI received for unknown reason %02x.\n", reason); + printk("Dazed and confused, but trying to continue\n"); + printk("Do you have a strange power saving mode enabled?\n"); +} + +asmlinkage void do_nmi(struct pt_regs * regs) +{ + unsigned char reason = inb(0x61); + + + ++nmi_count(smp_processor_id()); + if (!(reason & 0xc0)) { +#if CONFIG_X86_LOCAL_APIC + /* + * Ok, so this is none of the documented NMI sources, + * so it must be the NMI watchdog. + */ + if (nmi_watchdog) { + nmi_watchdog_tick(regs); + return; + } +#endif + unknown_nmi_error(reason, regs); + return; + } + if (reason & 0x80) + mem_parity_error(reason, regs); + if (reason & 0x40) + io_check_error(reason, regs); + /* + * Reassert NMI in case it became active meanwhile + * as it's edge-triggered. + */ + outb(0x8f, 0x70); + inb(0x71); /* dummy */ + outb(0x0f, 0x70); + inb(0x71); /* dummy */ +} + +asmlinkage void do_debug(struct pt_regs * regs, long error_code) +{ + unsigned long condition; + struct task_struct *tsk = current; + siginfo_t info; + + asm("movq %%db6,%0" : "=r" (condition)); + + /* Mask out spurious debug traps due to lazy DR7 setting */ + if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { + if (!tsk->thread.debugreg[7]) { + goto clear_dr7; + } + } + + tsk->thread.debugreg[6] = condition; + + /* Mask out spurious TF errors due to lazy TF clearing */ + if (condition & DR_STEP) { + /* + * The TF error should be masked out only if the current + * process is not traced and if the TRAP flag has been set + * previously by a tracing process (condition detected by + * the PT_DTRACE flag); remember that the i386 TRAP flag + * can be modified by the process itself in user mode, + * allowing programs to debug themselves without the ptrace() + * interface. + */ + if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE) + goto clear_TF; + } + + /* Ok, finally something we can handle */ + /* XXX: add die_chain here */ + tsk->thread.trap_no = 1; + tsk->thread.error_code = error_code; + info.si_signo = SIGTRAP; + info.si_errno = 0; + info.si_code = TRAP_BRKPT; + info.si_addr = ((regs->cs & 3) == 0) ? (void *)tsk->thread.rip : + (void *)regs->rip; + force_sig_info(SIGTRAP, &info, tsk); +clear_dr7: + asm("movq %0,%%db7"::"r"(0UL)); + return; + +clear_TF: + regs->eflags &= ~TF_MASK; + return; +} + +/* + * Note that we play around with the 'TS' bit in an attempt to get + * the correct behaviour even in the presence of the asynchronous + * IRQ13 behaviour + */ +void math_error(void *eip) +{ + struct task_struct * task; + siginfo_t info; + unsigned short cwd, swd; + + /* + * Save the info for the exception handler and clear the error. + */ + task = current; + save_init_fpu(task); + task->thread.trap_no = 16; + task->thread.error_code = 0; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_code = __SI_FAULT; + info.si_addr = eip; + /* + * (~cwd & swd) will mask out exceptions that are not set to unmasked + * status. 0x3f is the exception bits in these regs, 0x200 is the + * C1 reg you need in case of a stack fault, 0x040 is the stack + * fault bit. We should only be taking one exception at a time, + * so if this combination doesn't produce any single exception, + * then we have a bad program that isn't syncronizing its FPU usage + * and it will suffer the consequences since we won't be able to + * fully reproduce the context of the exception + */ + cwd = get_fpu_cwd(task); + swd = get_fpu_swd(task); + switch (((~cwd) & swd & 0x3f) | (swd & 0x240)) { + case 0x000: + default: + break; + case 0x001: /* Invalid Op */ + case 0x040: /* Stack Fault */ + case 0x240: /* Stack Fault | Direction */ + info.si_code = FPE_FLTINV; + break; + case 0x002: /* Denormalize */ + case 0x010: /* Underflow */ + info.si_code = FPE_FLTUND; + break; + case 0x004: /* Zero Divide */ + info.si_code = FPE_FLTDIV; + break; + case 0x008: /* Overflow */ + info.si_code = FPE_FLTOVF; + break; + case 0x020: /* Precision */ + info.si_code = FPE_FLTRES; + break; + } + force_sig_info(SIGFPE, &info, task); +} + +asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) +{ + ignore_irq13 = 1; + math_error((void *)regs->rip); +} + +asmlinkage void bad_intr(void) +{ + printk("bad interrupt"); +} + +void simd_math_error(void *eip) +{ + struct task_struct * task; + siginfo_t info; + unsigned short mxcsr; + + /* + * Save the info for the exception handler and clear the error. + */ + task = current; + save_init_fpu(task); + task->thread.trap_no = 19; + task->thread.error_code = 0; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_code = __SI_FAULT; + info.si_addr = eip; + /* + * The SIMD FPU exceptions are handled a little differently, as there + * is only a single status/control register. Thus, to determine which + * unmasked exception was caught we must mask the exception mask bits + * at 0x1f80, and then use these to mask the exception bits at 0x3f. + */ + mxcsr = get_fpu_mxcsr(task); + switch (~((mxcsr & 0x1f80) >> 7) & (mxcsr & 0x3f)) { + case 0x000: + default: + break; + case 0x001: /* Invalid Op */ + info.si_code = FPE_FLTINV; + break; + case 0x002: /* Denormalize */ + case 0x010: /* Underflow */ + info.si_code = FPE_FLTUND; + break; + case 0x004: /* Zero Divide */ + info.si_code = FPE_FLTDIV; + break; + case 0x008: /* Overflow */ + info.si_code = FPE_FLTOVF; + break; + case 0x020: /* Precision */ + info.si_code = FPE_FLTRES; + break; + } + force_sig_info(SIGFPE, &info, task); +} + +asmlinkage void do_simd_coprocessor_error(struct pt_regs * regs, + long error_code) +{ + if (cpu_has_xmm) { + /* Handle SIMD FPU exceptions on PIII+ processors. */ + ignore_irq13 = 1; + simd_math_error((void *)regs->rip); + } else { + /* + * Handle strange cache flush from user space exception + * in all other cases. This is undocumented behaviour. + */ + die_if_kernel("cache flush denied", regs, error_code); + current->thread.trap_no = 19; + current->thread.error_code = error_code; + force_sig(SIGSEGV, current); + } +} + +asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs, + long error_code) +{ +#if 0 + /* No need to warn about this any longer. */ + printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n"); +#endif +} + +/* + * 'math_state_restore()' saves the current math information in the + * old math state array, and gets the new ones from the current task + * + * Careful.. There are problems with IBM-designed IRQ13 behaviour. + * Don't touch unless you *really* know how it works. + * + * Must be called with kernel preemption disabled. + */ +asmlinkage void math_state_restore(void) +{ + clts(); /* Allow maths ops (or we recurse) */ + + if (current->used_math) { + restore_fpu(current); + } else { + init_fpu(); + } + set_thread_flag(TIF_USEDFPU); +} + +asmlinkage void math_emulate(void) +{ + printk("math-emulation not enabled and no coprocessor found.\n"); + printk("killing %s.\n",current->comm); + force_sig(SIGFPE,current); + schedule(); +} + +void __init trap_init(void) +{ + set_intr_gate(0,÷_error); + set_intr_gate(1,&debug); + set_intr_gate(2,&nmi); + set_system_gate(3,&int3); /* int3-5 can be called from all */ + set_system_gate(4,&overflow); + set_system_gate(5,&bounds); + set_intr_gate(6,&invalid_op); + set_intr_gate(7,&device_not_available); + set_intr_gate_ist(8,&double_fault, 1); + set_intr_gate(9,&coprocessor_segment_overrun); + set_intr_gate(10,&invalid_TSS); + set_intr_gate(11,&segment_not_present); + set_intr_gate_ist(12,&stack_segment,STACKFAULT_STACK); + set_intr_gate(13,&general_protection); + set_intr_gate(14,&page_fault); + set_intr_gate(15,&spurious_interrupt_bug); + set_intr_gate(16,&coprocessor_error); + set_intr_gate(17,&alignment_check); + set_intr_gate(19,&simd_coprocessor_error); + +#ifdef CONFIG_IA32_EMULATION + set_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall); +#endif + +#if 0 + /* + * default LDT is a single-entry callgate to lcall7 for iBCS + * and a callgate to lcall27 for Solaris/x86 binaries + */ + set_call_gate(&default_ldt[0],lcall7); + set_call_gate(&default_ldt[4],lcall27); +#endif + + /* + * Should be a barrier for any external CPU state. + */ + cpu_init(); +} + diff -Nru a/arch/x86_64/kernel/vsyscall.c b/arch/x86_64/kernel/vsyscall.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/vsyscall.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,190 @@ +/* + * linux/arch/x86_64/kernel/vsyscall.c + * + * Copyright (C) 2001 Andrea Arcangeli SuSE + * + * Thanks to hpa@transmeta.com for some useful hint. + * Special thanks to Ingo Molnar for his early experience with + * a different vsyscall implementation for Linux/IA32 and for the name. + * + * vsyscall 1 is located at -10Mbyte, vsyscall 2 is located + * at virtual address -10Mbyte+1024bytes etc... There are at max 8192 + * vsyscalls. One vsyscall can reserve more than 1 slot to avoid + * jumping out of line if necessary. + * + * $Id: vsyscall.c,v 1.4 2001/09/27 17:58:13 ak Exp $ + */ + +/* + * TODO 2001-03-20: + * + * 1) make page fault handler detect faults on page1-page-last of the vsyscall + * virtual space, and make it increase %rip and write -ENOSYS in %rax (so + * we'll be able to upgrade to a new glibc without upgrading kernel after + * we add more vsyscalls. + * 2) Possibly we need a fixmap table for the vsyscalls too if we want + * to avoid SIGSEGV and we want to return -EFAULT from the vsyscalls as well. + * Can we segfault inside a "syscall"? We can fix this anytime and those fixes + * won't be visible for userspace. Not fixing this is a noop for correct programs, + * broken programs will segfault and there's no security risk until we choose to + * fix it. + * + * These are not urgent things that we need to address only before shipping the first + * production binary kernels. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr))) + +static inline void timeval_normalize(struct timeval * tv) +{ + time_t __sec; + + __sec = tv->tv_usec / 1000000; + if (__sec) + { + tv->tv_usec %= 1000000; + tv->tv_sec += __sec; + } +} + +long __vxtime_sequence[2] __section_vxtime_sequence; + +/* The rest of the kernel knows it as this. */ +extern void do_gettimeofday(struct timeval *tv) __attribute__((alias("do_vgettimeofday"))); + +inline void do_vgettimeofday(struct timeval * tv) +{ + long sequence; + unsigned long usec, sec; + + do { + unsigned long eax, edx; + + sequence = __vxtime_sequence[1]; + rmb(); + + /* Read the Time Stamp Counter */ + rdtsc(eax,edx); + + /* .. relative to previous jiffy (32 bits is enough) */ + eax -= __last_tsc_low; /* tsc_low delta */ + + /* + * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient + * = (tsc_low delta) * (usecs_per_clock) + * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) + * + * Using a mull instead of a divl saves up to 31 clock cycles + * in the critical path. + */ + + edx = (eax*__fast_gettimeoffset_quotient) >> 32; + + /* our adjusted time offset in microseconds */ + usec = __delay_at_last_interrupt + edx; + + { + unsigned long lost = __jiffies - __wall_jiffies; + if (lost) + usec += lost * (1000000 / HZ); + } + sec = __xtime.tv_sec; + usec += __xtime.tv_usec; + + rmb(); + } while (sequence != __vxtime_sequence[0]); + + tv->tv_sec = sec; + tv->tv_usec = usec; + timeval_normalize(tv); +} + +static inline void do_get_tz(struct timezone * tz) +{ + long sequence; + + do { + sequence = __vxtime_sequence[1]; + rmb(); + + *tz = __sys_tz; + + rmb(); + } while (sequence != __vxtime_sequence[0]); +} + +static int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz) +{ + if (tv) + do_vgettimeofday(tv); + if (tz) + do_get_tz(tz); + return 0; +} + +static time_t __vsyscall(1) vtime(time_t * time) +{ + long sequence; + time_t __time; + + do { + sequence = __vxtime_sequence[1]; + rmb(); + + __time = __xtime.tv_sec; + + rmb(); + } while (sequence != __vxtime_sequence[0]); + + if (time) + *time = __time; + return __time; +} + +static long __vsyscall(2) venosys_0(void) +{ + return -ENOSYS; +} + +static long __vsyscall(3) venosys_1(void) +{ + return -ENOSYS; +} + +static void __init map_vsyscall(void) +{ + extern char __vsyscall_0; + unsigned long physaddr_page0 = (unsigned long) &__vsyscall_0 - __START_KERNEL_map; + + __set_fixmap(VSYSCALL_FIRST_PAGE, physaddr_page0, PAGE_KERNEL_VSYSCALL); +} + +static int __init vsyscall_init(void) +{ + printk("VSYSCALL: consistency checks..."); + if ((unsigned long) &vgettimeofday != VSYSCALL_ADDR(__NR_vgettimeofday)) + panic("vgettimeofday link addr broken"); + if ((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime)) + panic("vtime link addr broken"); + if (VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE)) + panic("fixmap first vsyscall %lx should be %lx", __fix_to_virt(VSYSCALL_FIRST_PAGE), + VSYSCALL_ADDR(0)); + printk("passed...mapping..."); + map_vsyscall(); + printk("done.\n"); + + return 0; +} + +__initcall(vsyscall_init); diff -Nru a/arch/x86_64/kernel/x8664_ksyms.c b/arch/x86_64/kernel/x8664_ksyms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/kernel/x8664_ksyms.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,162 @@ +#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 + +extern void dump_thread(struct pt_regs *, struct user *); +extern spinlock_t rtc_lock; + +#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) +extern void machine_real_restart(unsigned char *, int); +EXPORT_SYMBOL(machine_real_restart); +#endif + +#ifdef CONFIG_SMP +extern void FASTCALL( __write_lock_failed(rwlock_t *rw)); +extern void FASTCALL( __read_lock_failed(rwlock_t *rw)); +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE) +extern struct drive_info_struct drive_info; +EXPORT_SYMBOL(drive_info); +#endif + +extern unsigned long get_cmos_time(void); + +/* platform dependent support */ +EXPORT_SYMBOL(boot_cpu_data); +EXPORT_SYMBOL(dump_thread); +EXPORT_SYMBOL(dump_fpu); +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(disable_irq_nosync); +EXPORT_SYMBOL(probe_irq_mask); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(pm_idle); +EXPORT_SYMBOL(pm_power_off); +EXPORT_SYMBOL(get_cmos_time); +EXPORT_SYMBOL(apm_info); + +#ifdef CONFIG_IO_DEBUG +EXPORT_SYMBOL(__io_virt_debug); +#endif + +EXPORT_SYMBOL_NOVERS(__down_failed); +EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); +EXPORT_SYMBOL_NOVERS(__down_failed_trylock); +EXPORT_SYMBOL_NOVERS(__up_wakeup); +/* Networking helper routines. */ +EXPORT_SYMBOL(csum_partial_copy_nocheck); +/* Delay loops */ +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(__delay); +EXPORT_SYMBOL(__const_udelay); + +EXPORT_SYMBOL_NOVERS(__get_user_1); +EXPORT_SYMBOL_NOVERS(__get_user_2); +EXPORT_SYMBOL_NOVERS(__get_user_4); +EXPORT_SYMBOL_NOVERS(__put_user_1); +EXPORT_SYMBOL_NOVERS(__put_user_2); +EXPORT_SYMBOL_NOVERS(__put_user_4); + +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(simple_strtol); +EXPORT_SYMBOL(strstr); + +EXPORT_SYMBOL(strncpy_from_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(clear_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__generic_copy_from_user); +EXPORT_SYMBOL(__generic_copy_to_user); +EXPORT_SYMBOL(strnlen_user); + +EXPORT_SYMBOL(pci_alloc_consistent); +EXPORT_SYMBOL(pci_free_consistent); + +#ifdef CONFIG_PCI +EXPORT_SYMBOL(pcibios_penalize_isa_irq); +EXPORT_SYMBOL(pci_mem_start); +#endif + +#ifdef CONFIG_X86_USE_3DNOW +EXPORT_SYMBOL(_mmx_memcpy); +EXPORT_SYMBOL(mmx_clear_page); +EXPORT_SYMBOL(mmx_copy_page); +#endif + +#ifdef CONFIG_SMP +EXPORT_SYMBOL(cpu_data); +EXPORT_SYMBOL(kernel_flag); +EXPORT_SYMBOL(smp_num_cpus); +EXPORT_SYMBOL(cpu_online_map); +EXPORT_SYMBOL_NOVERS(__write_lock_failed); +EXPORT_SYMBOL_NOVERS(__read_lock_failed); + +/* Global SMP irq stuff */ +EXPORT_SYMBOL(synchronize_irq); +EXPORT_SYMBOL(global_irq_holder); +EXPORT_SYMBOL(__global_cli); +EXPORT_SYMBOL(__global_sti); +EXPORT_SYMBOL(__global_save_flags); +EXPORT_SYMBOL(__global_restore_flags); +EXPORT_SYMBOL(smp_call_function); + +/* TLB flushing */ +EXPORT_SYMBOL(flush_tlb_page); +#endif + +#ifdef CONFIG_MCA +EXPORT_SYMBOL(machine_id); +#endif + +#ifdef CONFIG_VT +EXPORT_SYMBOL(screen_info); +#endif + +EXPORT_SYMBOL(get_wchan); + +EXPORT_SYMBOL(rtc_lock); + +#undef memcpy +#undef memset +extern void * memset(void *,int,__kernel_size_t); +extern void * memcpy(void *,const void *,__kernel_size_t); +EXPORT_SYMBOL_NOVERS(memcpy); +EXPORT_SYMBOL_NOVERS(memset); + +EXPORT_SYMBOL(empty_zero_page); + +#ifdef CONFIG_HAVE_DEC_LOCK +EXPORT_SYMBOL(atomic_dec_and_lock); +#endif + diff -Nru a/arch/x86_64/lib/Makefile b/arch/x86_64/lib/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,17 @@ +# +# Makefile for x86_64-specific library files.. +# + +.S.o: + $(CC) $(AFLAGS) -c $< -o $*.o + +L_TARGET = lib.a +obj-y = generic-checksum.o old-checksum.o delay.o \ + usercopy.o getuser.o putuser.o \ + checksum_copy.o rwsem_thunk.o + +obj-$(CONFIG_IO_DEBUG) += iodebug.o +obj-$(CONFIG_X86_USE_3DNOW) += mmx.o +obj-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o + +include $(TOPDIR)/Rules.make diff -Nru a/arch/x86_64/lib/checksum_copy.S b/arch/x86_64/lib/checksum_copy.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/checksum_copy.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,142 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * IP/TCP/UDP checksumming routines + * + * Authors: Jorge Cwik, + * Arnt Gulbrandsen, + * Tom May, + * Pentium Pro/II routines: + * Alexander Kjeldaas + * Finn Arne Gangstad + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception + * handling. + * Andi Kleen, add zeroing on error + * converted to pure assembler + * Andi Kleen initial raw port to x86-64 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + + +/* Version for PentiumII/PPro ported to x86-64. Still very raw and + does not exploit 64bit. */ + +#define SRC(y...) \ + 9999: y; \ + .section __ex_table, "a"; \ + .quad 9999b, 6001f ; \ + .previous + +#define DST(y...) \ + 9999: y; \ + .section __ex_table, "a"; \ + .quad 9999b, 6002f ; \ + .previous + +#define ROUND1(x) \ + SRC(movl x(%rsi), %ebx ) ; \ + addl %ebx, %eax ; \ + DST(movl %ebx, x(%rdi) ) ; + +#define ROUND(x) \ + SRC(movl x(%rsi), %ebx ) ; \ + adcl %ebx, %eax ; \ + DST(movl %ebx, x(%rdi) ) ; + +#define ARGBASE 0 + +/* + asmlinkage unsigned int csum_partial_copy_generic( const char *src, char *dst, int len, int sum, + int *src_err_ptr, int *dst_err_ptr); + rdi .. src + rsi .. dst (copy in r12) + rdx .. len (copy in r10) + rcx .. sum + r8 .. src_err_ptr + r9 .. dst_err_ptr + + OPTIMIZEME: this routine should take advantage of checksumming 64bits at a time +*/ + + .globl csum_partial_copy_generic +csum_partial_copy_generic: + pushq %r10 + pushq %r12 + pushq %rbx + pushq %rbp + xchgq %rsi, %rdi + + movq %rdx, %r10 + movq %rsi, %r12 + + movq %rcx, %rax + movq %rdx, %rcx # And now it looks like PII case + movl %ecx, %ebx + movl %esi, %edx + shrl $6, %ecx + andl $0x3c, %ebx + negq %rbx + subq %rbx, %rsi + subq %rbx, %rdi + lea 3f(%rbx,%rbx), %rbx + testq %rsi, %rsi + jmp *%rbx +1: addq $64,%rsi + addq $64,%rdi + ROUND1(-64) ROUND(-60) ROUND(-56) ROUND(-52) + ROUND (-48) ROUND(-44) ROUND(-40) ROUND(-36) + ROUND (-32) ROUND(-28) ROUND(-24) ROUND(-20) + ROUND (-16) ROUND(-12) ROUND(-8) ROUND(-4) +3: adcl $0,%eax + addl $64,%edx + dec %ecx + jge 1b +4: movq %r10,%rdx + andl $3, %edx + jz 7f + cmpl $2, %edx + jb 5f +SRC( movw (%rsi), %dx ) + leaq 2(%rsi), %rsi +DST( movw %dx, (%rdi) ) + leaq 2(%rdi), %rdi + je 6f + shll $16,%edx +5: +SRC( movb (%rsi), %dl ) +DST( movb %dl, (%rdi) ) +6: addl %edx, %eax + adcl $0, %eax +7: +.section .fixup, "ax" +6001: + movl $-EFAULT, (%r8) + # zero the complete destination (computing the rest is too much work) + movq %r12,%rdi # dst + movq %r10,%rcx # len + xorl %eax,%eax + rep; stosb + jmp 7b +6002: movl $-EFAULT,(%r9) + jmp 7b +.previous + popq %rbp + popq %rbx + popq %r12 + popq %r10 + ret + +#undef ROUND +#undef ROUND1 diff -Nru a/arch/x86_64/lib/dec_and_lock.c b/arch/x86_64/lib/dec_and_lock.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/dec_and_lock.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,40 @@ +/* + * x86 version of "atomic_dec_and_lock()" using + * the atomic "cmpxchg" instruction. + * + * (For CPU's lacking cmpxchg, we use the slow + * generic version, and this one never even gets + * compiled). + */ + +#include +#include + +int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) +{ + int counter; + int newcount; + +repeat: + counter = atomic_read(atomic); + newcount = counter-1; + + if (!newcount) + goto slow_path; + + asm volatile("lock; cmpxchgl %1,%2" + :"=a" (newcount) + :"r" (newcount), "m" (atomic->counter), "0" (counter)); + + /* If the above failed, "eax" will have changed */ + if (newcount != counter) + goto repeat; + return 0; + +slow_path: + spin_lock(lock); + if (atomic_dec_and_test(atomic)) + return 1; + spin_unlock(lock); + return 0; +} diff -Nru a/arch/x86_64/lib/delay.c b/arch/x86_64/lib/delay.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/delay.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,45 @@ +/* + * Precise Delay Loops for i386 + * + * Copyright (C) 1993 Linus Torvalds + * Copyright (C) 1997 Martin Mares + * + * The __delay function must _NOT_ be inlined as its execution time + * depends wildly on alignment on many x86 processors. + */ + +#include +#include +#include +#include + +#ifdef CONFIG_SMP +#include +#endif + +int x86_udelay_tsc = 0; /* Delay via TSC */ + +void __delay(unsigned long loops) +{ +#ifndef CONFIG_SIMNOW + unsigned long bclock, now; + + rdtscl(bclock); + do + { + rep_nop(); + rdtscl(now); + } + while((now-bclock) < loops); +#endif +} + +inline void __const_udelay(unsigned long xloops) +{ + __delay(((xloops * current_cpu_data.loops_per_jiffy) >> 32) * HZ); +} + +void __udelay(unsigned long usecs) +{ + __const_udelay(usecs * 0x000010c6); /* 2**32 / 1000000 */ +} diff -Nru a/arch/x86_64/lib/generic-checksum.c b/arch/x86_64/lib/generic-checksum.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/generic-checksum.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,124 @@ +/* + * arch/x86_64/lib/checksum.c + * + * This file contains network checksum routines that are better done + * in an architecture-specific manner due to speed.. + */ + +#include +#include + +static inline unsigned short from64to16(unsigned long x) +{ + /* add up 32-bit words for 33 bits */ + x = (x & 0xffffffff) + (x >> 32); + /* add up 16-bit and 17-bit words for 17+c bits */ + x = (x & 0xffff) + (x >> 16); + /* add up 16-bit and 2-bit for 16+c bit */ + x = (x & 0xffff) + (x >> 16); + /* add up carry.. */ + x = (x & 0xffff) + (x >> 16); + return x; +} + +/* + * Do a 64-bit checksum on an arbitrary memory area.. + * + * This isn't a great routine, but it's not _horrible_ either. The + * inner loop could be unrolled a bit further, and there are better + * ways to do the carry, but this is reasonable. + */ +static inline unsigned long do_csum(const unsigned char * buff, int len) +{ + int odd, count; + unsigned long result = 0; + + if (len <= 0) + goto out; + odd = 1 & (unsigned long) buff; + if (odd) { + result = *buff << 8; + len--; + buff++; + } + count = len >> 1; /* nr of 16-bit words.. */ + if (count) { + if (2 & (unsigned long) buff) { + result += *(unsigned short *) buff; + count--; + len -= 2; + buff += 2; + } + count >>= 1; /* nr of 32-bit words.. */ + if (count) { + if (4 & (unsigned long) buff) { + result += *(unsigned int *) buff; + count--; + len -= 4; + buff += 4; + } + count >>= 1; /* nr of 64-bit words.. */ + if (count) { + unsigned long carry = 0; + do { + unsigned long w = *(unsigned long *) buff; + count--; + buff += 8; + result += carry; + result += w; + carry = (w > result); + } while (count); + result += carry; + result = (result & 0xffffffff) + (result >> 32); + } + if (len & 4) { + result += *(unsigned int *) buff; + buff += 4; + } + } + if (len & 2) { + result += *(unsigned short *) buff; + buff += 2; + } + } + if (len & 1) + result += *buff; + result = from64to16(result); + if (odd) + result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); +out: + return result; +} + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * returns a 32-bit number suitable for feeding into itself + * or csum_tcpudp_magic + * + * this function must be called with even lengths, except + * for the last fragment, which may be odd + * + * it's best to have buff aligned on a 32-bit boundary + */ +unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) +{ + unsigned long result = do_csum(buff, len); + + /* add in old sum, and carry.. */ + result += sum; + /* 32+c bits -> 32 bits */ + result = (result & 0xffffffff) + (result >> 32); + return result; +} + +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ +unsigned short ip_compute_csum(unsigned char * buff, int len) +{ + return ~from64to16(do_csum(buff,len)); +} + diff -Nru a/arch/x86_64/lib/getuser.S b/arch/x86_64/lib/getuser.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/getuser.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,90 @@ +/* + * __get_user functions. + * + * (C) Copyright 1998 Linus Torvalds + * + * These functions have a non-standard call interface + * to make them more efficient, especially as they + * return an error value in addition to the "real" + * return value. + */ + +/* + * __get_user_X + * + * Inputs: %rax contains the address + * + * Outputs: %rax is error code (0 or -EFAULT) + * %rdx contains zero-extended value + * + * %rbx is destroyed. + * + * These functions should not modify any other registers, + * as they get called from within inline assembly. + */ + +#include +#include +#include +#include +#include + + .text + .p2align +.globl __get_user_1 +__get_user_1: + GET_THREAD_INFO(%rbx) + cmpq threadinfo_addr_limit(%rbx),%rax + jae bad_get_user +1: movzb (%rax),%edx + xorq %rax,%rax + ret + + .p2align +.globl __get_user_2 +__get_user_2: + GET_THREAD_INFO(%rbx) + addq $1,%rax + jc bad_get_user + cmpq threadinfo_addr_limit(%rbx),%rax + jae bad_get_user +2: movzwl -1(%rax),%edx + xorq %rax,%rax + ret + + .p2align +.globl __get_user_4 +__get_user_4: + GET_THREAD_INFO(%rbx) + addq $3,%rax + jc bad_get_user + cmpq threadinfo_addr_limit(%rbx),%rax + jae bad_get_user +3: movl -3(%rax),%edx + xorq %rax,%rax + ret + + .p2align +.globl __get_user_8 +__get_user_8: + GET_THREAD_INFO(%rbx) + addq $7,%rax + jc bad_get_user + cmpq threadinfo_addr_limit(%rbx),%rax + jae bad_get_user +4: movq -7(%rax),%rdx + xorq %rax,%rax + ret + +ENTRY(bad_get_user) +bad_get_user: + xorq %rdx,%rdx + movq $(-EFAULT),%rax + ret + +.section __ex_table,"a" + .quad 1b,bad_get_user + .quad 2b,bad_get_user + .quad 3b,bad_get_user + .quad 4b,bad_get_user +.previous diff -Nru a/arch/x86_64/lib/iodebug.c b/arch/x86_64/lib/iodebug.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/iodebug.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,11 @@ +#include + +void * __io_virt_debug(unsigned long x, const char *file, int line) +{ + if (x < PAGE_OFFSET) { + printk("io mapaddr 0x%05lx not valid at %s:%d!\n", x, file, line); + return __va(x); + } + return (void *)x; +} + diff -Nru a/arch/x86_64/lib/mmx.c b/arch/x86_64/lib/mmx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/mmx.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,377 @@ +#include +#include +#include +#include + +#include +#include + + +/* + * MMX 3DNow! library helper functions + * + * To do: + * We can use MMX just for prefetch in IRQ's. This may be a win. + * (reported so on K6-III) + * We should use a better code neutral filler for the short jump + * leal ebx. [ebx] is apparently best for K6-2, but Cyrix ?? + * We also want to clobber the filler register so we dont get any + * register forwarding stalls on the filler. + * + * Add *user handling. Checksums are not a win with MMX on any CPU + * tested so far for any MMX solution figured. + * + * 22/09/2000 - Arjan van de Ven + * Improved for non-egineering-sample Athlons + * + */ + +#error Don't use these for now, but we'll have to provide optimized functions in future + +void *_mmx_memcpy(void *to, const void *from, size_t len) +{ + void *p; + int i; + + if (in_interrupt()) + return __memcpy(to, from, len); + + p = to; + i = len >> 6; /* len/64 */ + + kernel_fpu_begin(); + + __asm__ __volatile__ ( + "1: prefetch (%0)\n" /* This set is 28 bytes */ + " prefetch 64(%0)\n" + " prefetch 128(%0)\n" + " prefetch 192(%0)\n" + " prefetch 256(%0)\n" + "2: \n" + ".section .fixup, \"ax\"\n" + "3: movw $0x1AEB, 1b\n" /* jmp on 26 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from) ); + + + for(; i>0; i--) + { + __asm__ __volatile__ ( + "1: prefetch 320(%0)\n" + "2: movq (%0), %%mm0\n" + " movq 8(%0), %%mm1\n" + " movq 16(%0), %%mm2\n" + " movq 24(%0), %%mm3\n" + " movq %%mm0, (%1)\n" + " movq %%mm1, 8(%1)\n" + " movq %%mm2, 16(%1)\n" + " movq %%mm3, 24(%1)\n" + " movq 32(%0), %%mm0\n" + " movq 40(%0), %%mm1\n" + " movq 48(%0), %%mm2\n" + " movq 56(%0), %%mm3\n" + " movq %%mm0, 32(%1)\n" + " movq %%mm1, 40(%1)\n" + " movq %%mm2, 48(%1)\n" + " movq %%mm3, 56(%1)\n" + ".section .fixup, \"ax\"\n" + "3: movw $0x05EB, 1b\n" /* jmp on 5 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from), "r" (to) : "memory"); + from+=64; + to+=64; + } + /* + * Now do the tail of the block + */ + __memcpy(to, from, len&63); + kernel_fpu_end(); + return p; +} + +#ifdef CONFIG_MK7 + +/* + * The K7 has streaming cache bypass load/store. The Cyrix III, K6 and + * other MMX using processors do not. + */ + +static void fast_clear_page(void *page) +{ + int i; + + kernel_fpu_begin(); + + __asm__ __volatile__ ( + " pxor %%mm0, %%mm0\n" : : + ); + + for(i=0;i<4096/64;i++) + { + __asm__ __volatile__ ( + " movntq %%mm0, (%0)\n" + " movntq %%mm0, 8(%0)\n" + " movntq %%mm0, 16(%0)\n" + " movntq %%mm0, 24(%0)\n" + " movntq %%mm0, 32(%0)\n" + " movntq %%mm0, 40(%0)\n" + " movntq %%mm0, 48(%0)\n" + " movntq %%mm0, 56(%0)\n" + : : "r" (page) : "memory"); + page+=64; + } + /* since movntq is weakly-ordered, a "sfence" is needed to become + * ordered again. + */ + __asm__ __volatile__ ( + " sfence \n" : : + ); + kernel_fpu_end(); +} + +static void fast_copy_page(void *to, void *from) +{ + int i; + + kernel_fpu_begin(); + + /* maybe the prefetch stuff can go before the expensive fnsave... + * but that is for later. -AV + */ + __asm__ __volatile__ ( + "1: prefetch (%0)\n" + " prefetch 64(%0)\n" + " prefetch 128(%0)\n" + " prefetch 192(%0)\n" + " prefetch 256(%0)\n" + "2: \n" + ".section .fixup, \"ax\"\n" + "3: movw $0x1AEB, 1b\n" /* jmp on 26 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from) ); + + for(i=0; i<(4096-320)/64; i++) + { + __asm__ __volatile__ ( + "1: prefetch 320(%0)\n" + "2: movq (%0), %%mm0\n" + " movntq %%mm0, (%1)\n" + " movq 8(%0), %%mm1\n" + " movntq %%mm1, 8(%1)\n" + " movq 16(%0), %%mm2\n" + " movntq %%mm2, 16(%1)\n" + " movq 24(%0), %%mm3\n" + " movntq %%mm3, 24(%1)\n" + " movq 32(%0), %%mm4\n" + " movntq %%mm4, 32(%1)\n" + " movq 40(%0), %%mm5\n" + " movntq %%mm5, 40(%1)\n" + " movq 48(%0), %%mm6\n" + " movntq %%mm6, 48(%1)\n" + " movq 56(%0), %%mm7\n" + " movntq %%mm7, 56(%1)\n" + ".section .fixup, \"ax\"\n" + "3: movw $0x05EB, 1b\n" /* jmp on 5 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from), "r" (to) : "memory"); + from+=64; + to+=64; + } + for(i=(4096-320)/64; i<4096/64; i++) + { + __asm__ __volatile__ ( + "2: movq (%0), %%mm0\n" + " movntq %%mm0, (%1)\n" + " movq 8(%0), %%mm1\n" + " movntq %%mm1, 8(%1)\n" + " movq 16(%0), %%mm2\n" + " movntq %%mm2, 16(%1)\n" + " movq 24(%0), %%mm3\n" + " movntq %%mm3, 24(%1)\n" + " movq 32(%0), %%mm4\n" + " movntq %%mm4, 32(%1)\n" + " movq 40(%0), %%mm5\n" + " movntq %%mm5, 40(%1)\n" + " movq 48(%0), %%mm6\n" + " movntq %%mm6, 48(%1)\n" + " movq 56(%0), %%mm7\n" + " movntq %%mm7, 56(%1)\n" + : : "r" (from), "r" (to) : "memory"); + from+=64; + to+=64; + } + /* since movntq is weakly-ordered, a "sfence" is needed to become + * ordered again. + */ + __asm__ __volatile__ ( + " sfence \n" : : + ); + kernel_fpu_end(); +} + +#else + +/* + * Generic MMX implementation without K7 specific streaming + */ + +static void fast_clear_page(void *page) +{ + int i; + + kernel_fpu_begin(); + + __asm__ __volatile__ ( + " pxor %%mm0, %%mm0\n" : : + ); + + for(i=0;i<4096/128;i++) + { + __asm__ __volatile__ ( + " movq %%mm0, (%0)\n" + " movq %%mm0, 8(%0)\n" + " movq %%mm0, 16(%0)\n" + " movq %%mm0, 24(%0)\n" + " movq %%mm0, 32(%0)\n" + " movq %%mm0, 40(%0)\n" + " movq %%mm0, 48(%0)\n" + " movq %%mm0, 56(%0)\n" + " movq %%mm0, 64(%0)\n" + " movq %%mm0, 72(%0)\n" + " movq %%mm0, 80(%0)\n" + " movq %%mm0, 88(%0)\n" + " movq %%mm0, 96(%0)\n" + " movq %%mm0, 104(%0)\n" + " movq %%mm0, 112(%0)\n" + " movq %%mm0, 120(%0)\n" + : : "r" (page) : "memory"); + page+=128; + } + + kernel_fpu_end(); +} + +static void fast_copy_page(void *to, void *from) +{ + int i; + + + kernel_fpu_begin(); + + __asm__ __volatile__ ( + "1: prefetch (%0)\n" + " prefetch 64(%0)\n" + " prefetch 128(%0)\n" + " prefetch 192(%0)\n" + " prefetch 256(%0)\n" + "2: \n" + ".section .fixup, \"ax\"\n" + "3: movw $0x1AEB, 1b\n" /* jmp on 26 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from) ); + + for(i=0; i<4096/64; i++) + { + __asm__ __volatile__ ( + "1: prefetch 320(%0)\n" + "2: movq (%0), %%mm0\n" + " movq 8(%0), %%mm1\n" + " movq 16(%0), %%mm2\n" + " movq 24(%0), %%mm3\n" + " movq %%mm0, (%1)\n" + " movq %%mm1, 8(%1)\n" + " movq %%mm2, 16(%1)\n" + " movq %%mm3, 24(%1)\n" + " movq 32(%0), %%mm0\n" + " movq 40(%0), %%mm1\n" + " movq 48(%0), %%mm2\n" + " movq 56(%0), %%mm3\n" + " movq %%mm0, 32(%1)\n" + " movq %%mm1, 40(%1)\n" + " movq %%mm2, 48(%1)\n" + " movq %%mm3, 56(%1)\n" + ".section .fixup, \"ax\"\n" + "3: movw $0x05EB, 1b\n" /* jmp on 5 bytes */ + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b, 3b\n" + ".previous" + : : "r" (from), "r" (to) : "memory"); + from+=64; + to+=64; + } + kernel_fpu_end(); +} + + +#endif + +/* + * Favour MMX for page clear and copy. + */ + +static void slow_zero_page(void * page) +{ + int d0, d1; + __asm__ __volatile__( \ + "cld\n\t" \ + "rep ; stosl" \ + : "=&c" (d0), "=&D" (d1) + :"a" (0),"1" (page),"0" (1024) + :"memory"); +} + +void mmx_clear_page(void * page) +{ + if(in_interrupt()) + slow_zero_page(page); + else + fast_clear_page(page); +} + +static void slow_copy_page(void *to, void *from) +{ + int d0, d1, d2; + __asm__ __volatile__( \ + "cld\n\t" \ + "rep ; movsl" \ + : "=&c" (d0), "=&D" (d1), "=&S" (d2) \ + : "0" (1024),"1" ((long) to),"2" ((long) from) \ + : "memory"); +} + + +void mmx_copy_page(void *to, void *from) +{ + if(in_interrupt()) + slow_copy_page(to, from); + else + fast_copy_page(to, from); +} diff -Nru a/arch/x86_64/lib/old-checksum.c b/arch/x86_64/lib/old-checksum.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/old-checksum.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,33 @@ +/* + * Temporal C versions of the checksum functions until optimized assembler versions + * can go in. + */ + +#include + +/* + * Copy from userspace and compute checksum. If we catch an exception + * then zero the rest of the buffer. + */ +unsigned int csum_partial_copy_from_user (const char *src, char *dst, + int len, unsigned int sum, + int *err_ptr) +{ + int missing; + + missing = copy_from_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(dst, len, sum); +} + +unsigned int csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum) +{ + memcpy(dst,src,len); + return csum_partial(dst,len,sum); +} + +/* Fallback for csum_and_copy_to_user is currently in include/net/checksum.h */ diff -Nru a/arch/x86_64/lib/putuser.S b/arch/x86_64/lib/putuser.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/putuser.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,88 @@ +/* + * __put_user functions. + * + * (C) Copyright 1998 Linus Torvalds + * + * These functions have a non-standard call interface + * to make them more efficient. + */ + +/* + * __put_user_X + * + * Inputs: %rax contains the address + * %rdx contains the value + * + * Outputs: %rax is error code (0 or -EFAULT) + * %rbx is corrupted (will contain "current_task"). + * + * These functions should not modify any other registers, + * as they get called from within inline assembly. + */ + +/* FIXME: putuser.S should be really merged with getuser.S, and preprocessor should be used to keep code duplication lower */ + +#include +#include +#include +#include +#include + +.text +.p2align +.globl __put_user_1 +__put_user_1: + GET_THREAD_INFO(%rbx) + cmpq threadinfo_addr_limit(%rbx),%rax + jae bad_put_user +1: movb %dl,(%rax) + xorq %rax,%rax + ret + +.p2align +.globl __put_user_2 +__put_user_2: + GET_THREAD_INFO(%rbx) + addq $1,%rax + jc bad_put_user + cmpq threadinfo_addr_limit(%rbx),%rax + jae bad_put_user +2: movw %dx,-1(%rax) + xorq %rax,%rax + ret + +.p2align +.globl __put_user_4 +__put_user_4: + GET_THREAD_INFO(%rbx) + addq $3,%rax + jc bad_put_user + cmpq threadinfo_addr_limit(%rbx),%rax + jae bad_put_user +3: movl %edx,-3(%rax) + xorq %rax,%rax + ret + +.p2align +.globl __put_user_8 +__put_user_8: + GET_THREAD_INFO(%rbx) + addq $7,%rax + jc bad_put_user + cmpq threadinfo_addr_limit(%rbx),%rax + jae bad_put_user +4: movq %rdx,-7(%rax) + xorq %rax,%rax + ret + +ENTRY(bad_put_user) +bad_put_user: + movq $(-EFAULT),%rax + ret + +.section __ex_table,"a" + .quad 1b,bad_put_user + .quad 2b,bad_put_user + .quad 3b,bad_put_user + .quad 4b,bad_put_user +.previous diff -Nru a/arch/x86_64/lib/rwsem_thunk.S b/arch/x86_64/lib/rwsem_thunk.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/rwsem_thunk.S Tue Feb 19 18:09:00 2002 @@ -0,0 +1,27 @@ + #include + + /* + * Save registers for the slow path of semaphores here to avoid + * disturbance of register allocation in fast paths with function calls. + * Written 2001 by Andi Kleen. + */ + + .macro rwsem_thunk name,func + .globl \name +\name: + SAVE_ARGS + movq %rax,%rdi + call \func + jmp restore + .endm + + rwsem_thunk rwsem_down_read_failed_thunk,rwsem_down_read_failed + rwsem_thunk rwsem_down_write_failed_thunk,rwsem_down_write_failed + rwsem_thunk rwsem_wake_thunk,rwsem_wake + /* This does not really belong here, but the macros are so + convenient. */ + rwsem_thunk do_softirq_thunk,do_softirq + +restore: + RESTORE_ARGS + ret diff -Nru a/arch/x86_64/lib/usercopy.c b/arch/x86_64/lib/usercopy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/lib/usercopy.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,147 @@ +/* + * User address space access functions. + * The non inlined parts of asm-i386/uaccess.h are here. + * + * Copyright 1997 Andi Kleen + * Copyright 1997 Linus Torvalds + */ +#include +#include +#include + +unsigned long +__generic_copy_to_user(void *to, const void *from, unsigned long n) +{ + prefetch(from); + if (access_ok(VERIFY_WRITE, to, n)) + __copy_user(to,from,n); + return n; +} + +unsigned long +__generic_copy_from_user(void *to, const void *from, unsigned long n) +{ + prefetchw(to); + if (access_ok(VERIFY_READ, from, n)) + __copy_user_zeroing(to,from,n); + else + memset(to, 0, n); + return n; +} + +/* + * Copy a null terminated string from userspace. + */ + +#define __do_strncpy_from_user(dst,src,count,res) \ +do { \ + long __d0, __d1, __d2; \ + __asm__ __volatile__( \ + " testq %1,%1\n" \ + " jz 2f\n" \ + "0: lodsb\n" \ + " stosb\n" \ + " testb %%al,%%al\n" \ + " jz 1f\n" \ + " decq %1\n" \ + " jnz 0b\n" \ + "1: subq %1,%0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: movq %5,%0\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .quad 0b,3b\n" \ + ".previous" \ + : "=r"(res), "=c"(count), "=&a" (__d0), "=&S" (__d1), \ + "=&D" (__d2) \ + : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), "4"(dst) \ + : "memory"); \ +} while (0) + +long +__strncpy_from_user(char *dst, const char *src, long count) +{ + long res; + __do_strncpy_from_user(dst, src, count, res); + return res; +} + +long +strncpy_from_user(char *dst, const char *src, long count) +{ + long res = -EFAULT; + if (access_ok(VERIFY_READ, src, 1)) + __do_strncpy_from_user(dst, src, count, res); + return res; +} + + +/* + * Zero Userspace + */ + +#define __do_clear_user(addr,size) \ +do { \ + long __d0; \ + __asm__ __volatile__( \ + "cld\n" \ + "0: rep; stosl\n" \ + " movq %2,%0\n" \ + "1: rep; stosb\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: lea 0(%2,%0,4),%0\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .quad 0b,3b\n" \ + " .quad 1b,2b\n" \ + ".previous" \ + : "=&c"(size), "=&D" (__d0) \ + : "r"(size & 3), "0"(size / 4), "1"(addr), "a"(0)); \ +} while (0) + +unsigned long +clear_user(void *to, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __do_clear_user(to, n); + return n; +} + +unsigned long +__clear_user(void *to, unsigned long n) +{ + __do_clear_user(to, n); + return n; +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 on exception, a value greater than N if too long + */ + +long strnlen_user(const char *s, long n) +{ + unsigned long res = 0; + char c; + + if (!access_ok(VERIFY_READ, s, n)) + return 0; + + while (1) { + if (get_user(c, s)) + return 0; + if (!c) + return res+1; + if (res>n) + return n+1; + res++; + s++; + } +} diff -Nru a/arch/x86_64/mm/Makefile b/arch/x86_64/mm/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/mm/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,13 @@ +# +# Makefile for the linux i386-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := mm.o +obj-y := init.o fault.o ioremap.o extable.o + +include $(TOPDIR)/Rules.make diff -Nru a/arch/x86_64/mm/extable.c b/arch/x86_64/mm/extable.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/mm/extable.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,62 @@ +/* + * linux/arch/i386/mm/extable.c + */ + +#include +#include +#include +#include + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +extern spinlock_t modlist_lock; + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret = 0; + unsigned long flags; + +#ifndef CONFIG_MODULES + /* There is only the kernel to search. */ + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + return ret; +#else + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + + spin_lock_irqsave(&modlist_lock, flags); + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING))) + continue; + ret = search_one_table(mp->ex_table_start, + mp->ex_table_end - 1, addr); + if (ret) + break; + } + spin_unlock_irqrestore(&modlist_lock, flags); + return ret; +#endif +} diff -Nru a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/mm/fault.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,324 @@ +/* + * linux/arch/x86-64/mm/fault.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For unblank_screen() */ + +#include +#include +#include +#include + +extern void die(const char *,struct pt_regs *,long); + +asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); +extern unsigned long idt; + +extern spinlock_t console_lock, timerlist_lock; + +void bust_spinlocks(int yes) +{ + spin_lock_init(&timerlist_lock); + if (yes) { + oops_in_progress = 1; +#ifdef CONFIG_SMP + global_irq_lock = 0; /* Many serial drivers do __global_cli() */ +#endif + } else { + int loglevel_save = console_loglevel; +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; + } +} + +void do_BUG(const char *file, int line) +{ + bust_spinlocks(1); + printk("kernel BUG at %s:%d!\n", file, line); +} + + +void dump_pagetable(unsigned long address) +{ + static char *name[] = { "PML4", "PGD", "PDE", "PTE" }; + int i, shift; + unsigned long page; + + asm("movq %%cr3,%0":"=r" (page)); + shift = 9+9+9+12; + address &= ~0xFFFF000000000000UL; + for (i = 0; i < 4; i++) { + page = ((unsigned long *) __va(page))[(address >> shift) & 0x1FFU]; + printk("%s: %016lx ", name[i], page); + if ((page & (1 | (1<<7))) != 1) /* Not present or 2MB page */ + break; + page &= ~0xFFFUL; + shift -= 9; + } + printk("\n"); +} + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * error_code: + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write + * bit 2 == 0 means kernel, 1 means user-mode + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) +{ + struct task_struct *tsk; + struct mm_struct *mm; + struct vm_area_struct * vma; + unsigned long address; + unsigned long fixup; + int write; + siginfo_t info; + + /* get the address */ + __asm__("movq %%cr2,%0":"=r" (address)); + + + tsk = current; + mm = tsk->mm; + info.si_code = SEGV_MAPERR; + + if (address >= TASK_SIZE) + goto vmalloc_fault; + + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || !mm) + goto no_context; + + down_read(&mm->mmap_sem); + + vma = find_vma(mm, address); + +#if 0 + printk("fault at %lx rip:%lx rsp:%lx err:%lx thr:%x ", address,regs->rip,regs->rsp,error_code,tsk->thread.flags); + if (vma) + printk("vma %lx-%lx prot:%lx flags:%lx",vma->vm_start,vma->vm_end, + vma->vm_page_prot,vma->vm_flags); + printk("\n"); +#endif + + + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (error_code & 4) { + // XXX: align red zone size with ABI + if (address + 128 < regs->rsp) + goto bad_area; + } + if (expand_stack(vma, address)) + goto bad_area; +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + info.si_code = SEGV_ACCERR; + write = 0; + switch (error_code & 3) { + default: /* 3: write, present */ + /* fall through */ + case 2: /* write, not present */ + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + write++; + break; + case 1: /* read, present */ + goto bad_area; + case 0: /* read, not present */ + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + +survive: + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + switch (handle_mm_fault(mm, vma, address, write)) { + case 1: + tsk->min_flt++; + break; + case 2: + tsk->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; + } + + up_read(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up_read(&mm->mmap_sem); + +bad_area_nosemaphore: + + /* User mode accesses just cause a SIGSEGV */ + if (error_code & 4) { + + printk(KERN_ERR "%.20s[%d] segfaulted rip:%lx rsp:%lx adr:%lx err:%lx\n", + tsk->comm, tsk->pid, + regs->rip, regs->rsp, address, error_code); + + tsk->thread.cr2 = address; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = 14; + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void *)address; + force_sig_info(SIGSEGV, &info, tsk); + return; + } + +no_context: + + /* Are we prepared to handle this kernel fault? */ + if ((fixup = search_exception_table(regs->rip)) != 0) { + regs->rip = fixup; + return; + } + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + + bust_spinlocks(1); + + if (address < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel paging request"); + printk(" at virtual address %016lx\n",address); + printk(" printing rip:\n"); + printk("%016lx\n", regs->rip); + dump_pagetable(address); + die("Oops", regs, error_code); + bust_spinlocks(0); + do_exit(SIGKILL); + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + if (current->pid == 1) { + yield(); + down_read(&mm->mmap_sem); + goto survive; + } + printk("VM: killing process %s\n", tsk->comm); + if (error_code & 4) + do_exit(SIGKILL); + goto no_context; + +do_sigbus: + up_read(&mm->mmap_sem); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + tsk->thread.cr2 = address; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = 14; + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info(SIGBUS, &info, tsk); + + /* Kernel mode? Handle exceptions or die */ + if (!(error_code & 4)) + goto no_context; + + +vmalloc_fault: + { + /* + * Synchronize the kernel space top level page-table + * with the 'reference' page table. + * Currently it only works for first and last 512 GB of + * kernel memory FIXME + * + */ + level4_t *l4pd = level4_offset_k(address); + int offset = __pgd_offset(address); + pgd_t *pgd, *pgd_k; + pmd_t *pmd, *pmd_k; + + if (! level4_val(*l4pd)) { + printk(KERN_ERR "fatal - no entry in level4_page for %lx\n", + address); + goto bad_area_nosemaphore; + } + pgd = level3_offset_k(l4pd, address); + pgd_k = init_mm.pgd + offset; + + if (!pgd_present(*pgd)) { + if (!pgd_present(*pgd_k)) + goto bad_area_nosemaphore; + set_pgd(pgd, *pgd_k); + return; + } + + pmd = pmd_offset(pgd, address); + pmd_k = pmd_offset(pgd_k, address); + + if (pmd_present(*pmd) || !pmd_present(*pmd_k)) + goto bad_area_nosemaphore; + set_pmd(pmd, *pmd_k); + return; + } +} diff -Nru a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/mm/init.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,387 @@ +/* + * linux/arch/i386/mm/init.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 2000 Pavel Machek + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BLK_DEV_INITRD +#include +#endif +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; + +static unsigned long totalram_pages; + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + if(read_pda(pgtable_cache_sz) > high) { + do { + if (read_pda(pgd_quick)) { + pgd_free_slow(pgd_alloc_one_fast()); + freed++; + } + if (read_pda(pmd_quick)) { + pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); + freed++; + } + if (read_pda(pte_quick)) { + pte_free_slow(pte_alloc_one_fast(NULL, 0)); + freed++; + } + } while(read_pda(pgtable_cache_sz) > low); + } + return freed; +} + +/* + * NOTE: pagetable_init alloc all the fixmap pagetables contiguous on the + * physical space so we can cache the place of the first one and move + * around without checking the pgd every time. + */ + +void show_mem(void) +{ + int i, total = 0, reserved = 0; + int shared = 0, cached = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (page_count(mem_map+i)) + shared += page_count(mem_map+i) - 1; + } + printk("%d pages of RAM\n", total); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%ld pages in page table cache\n",read_pda(pgtable_cache_sz)); + show_buffers(); +} + +/* References to section boundaries */ + +extern char _text, _etext, _edata, __bss_start, _end; +extern char __init_begin, __init_end; + +int after_bootmem; + +static void *spp_getpage(void) +{ + void *ptr; + if (after_bootmem) + ptr = (void *) get_free_page(GFP_ATOMIC); + else + ptr = alloc_bootmem_low(PAGE_SIZE); + if (!ptr) + panic("set_pte_phys: cannot allocate page data %s\n", after_bootmem?"after bootmem":""); + return ptr; +} + +static void set_pte_phys(unsigned long vaddr, + unsigned long phys, pgprot_t prot) +{ + level4_t *level4; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + level4 = level4_offset_k(vaddr); + if (level4_none(*level4)) { + printk("LEVEL4 FIXMAP MISSING, it should be setup in head.S!\n"); + return; + } + pgd = level3_offset_k(level4, vaddr); + if (pgd_none(*pgd)) { + pmd = (pmd_t *) spp_getpage(); + set_pgd(pgd, __pgd(__pa(pmd) + 0x7)); + if (pmd != pmd_offset(pgd, 0)) { + printk("PAGETABLE BUG #01!\n"); + return; + } + } + pmd = pmd_offset(pgd, vaddr); + if (pmd_none(*pmd)) { + pte = (pte_t *) spp_getpage(); + set_pmd(pmd, __pmd(__pa(pte) + 0x7)); + if (pte != pte_offset(pmd, 0)) { + printk("PAGETABLE BUG #02!\n"); + return; + } + } + pte = pte_offset(pmd, vaddr); + if (pte_val(*pte)) + pte_ERROR(*pte); + set_pte(pte, mk_pte_phys(phys, prot)); + + /* + * It's enough to flush this one mapping. + * (PGE mappings get flushed as well) + */ + __flush_tlb_one(vaddr); +} + +/* NOTE: this is meant to be run only at boot */ +void __set_fixmap (enum fixed_addresses idx, unsigned long phys, pgprot_t prot) +{ + unsigned long address = __fix_to_virt(idx); + + if (idx >= __end_of_fixed_addresses) { + printk("Invalid __set_fixmap\n"); + return; + } + set_pte_phys(address, phys, prot); +} + +static void __init pagetable_init (void) +{ + unsigned long paddr, end; + pgd_t *pgd; + int i, j; + pmd_t *pmd; + + /* + * This can be zero as well - no problem, in that case we exit + * the loops anyway due to the PTRS_PER_* conditions. + */ + end = (unsigned long) max_low_pfn*PAGE_SIZE; + if (end > 0x8000000000) { + printk("Temporary supporting only 512G of global RAM\n"); + end = 0x8000000000; + max_low_pfn = 0x8000000000 >> PAGE_SHIFT; + } + + i = __pgd_offset(PAGE_OFFSET); + pgd = level3_physmem_pgt + i; + + for (; i < PTRS_PER_PGD; pgd++, i++) { + paddr = i*PGDIR_SIZE; + if (paddr >= end) + break; + if (i) + pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); + else + pmd = level2_kernel_pgt; + + set_pgd(pgd, __pgd(__pa(pmd) + 0x7)); + for (j = 0; j < PTRS_PER_PMD; pmd++, j++) { + unsigned long __pe; + + paddr = i*PGDIR_SIZE + j*PMD_SIZE; + if (paddr >= end) + break; + + __pe = _KERNPG_TABLE + _PAGE_PSE + paddr + _PAGE_GLOBAL; + set_pmd(pmd, __pmd(__pe)); + } + } + + /* + * Add low memory identity-mappings - SMP needs it when + * starting up on an AP from real-mode. In the non-PAE + * case we already have these mappings through head.S. + * All user-space mappings are explicitly cleared after + * SMP startup. + */ +#ifdef FIXME + pgd_base [0] is not what you think, this needs to be rewritten for SMP. + pgd_base[0] = pgd_base[USER_PTRS_PER_PGD]; +#endif +} + +void __init zap_low_mappings (void) +{ + int i; + /* + * Zap initial low-memory mappings. + * + * Note that "pgd_clear()" doesn't do it for + * us in this case, because pgd_clear() is a + * no-op in the 2-level case (pmd_clear() is + * the thing that clears the page-tables in + * that case). + */ + for (i = 0; i < USER_PTRS_PER_PGD; i++) + pgd_clear(swapper_pg_dir+i); + flush_tlb_all(); +} + +/* + * paging_init() sets up the page tables - note that the first 4MB are + * already mapped by head.S. + * + * This routines also unmaps the page at virtual kernel address 0, so + * that we can trap those pesky NULL-reference errors in the kernel. + */ +void __init paging_init(void) +{ + asm volatile("movq %%cr4,%0" : "=r" (mmu_cr4_features)); + + pagetable_init(); + + __flush_tlb_all(); + + { + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned int max_dma, low; + + max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; + low = max_low_pfn; + + if (low < max_dma) + zones_size[ZONE_DMA] = low; + else { + zones_size[ZONE_DMA] = max_dma; + zones_size[ZONE_NORMAL] = low - max_dma; + } + free_area_init(zones_size); + } + return; +} + + +static inline int page_is_ram (unsigned long pagenr) +{ + int i; + + for (i = 0; i < e820.nr_map; i++) { + unsigned long addr, end; + + if (e820.map[i].type != E820_RAM) /* not usable memory */ + continue; + /* + * !!!FIXME!!! Some BIOSen report areas as RAM that + * are not. Notably the 640->1Mb area. We need a sanity + * check here. + */ + addr = (e820.map[i].addr+PAGE_SIZE-1) >> PAGE_SHIFT; + end = (e820.map[i].addr+e820.map[i].size) >> PAGE_SHIFT; + if ((pagenr >= addr) && (pagenr < end)) + return 1; + } + return 0; +} + +void __init mem_init(void) +{ + int codesize, reservedpages, datasize, initsize; + int tmp; + + if (!mem_map) + BUG(); + + max_mapnr = num_physpages = max_low_pfn; + high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); + + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); + + after_bootmem = 1; + + reservedpages = 0; + for (tmp = 0; tmp < max_low_pfn; tmp++) + /* + * Only count reserved RAM pages + */ + if (page_is_ram(tmp) && PageReserved(mem_map+tmp)) + reservedpages++; + codesize = (unsigned long) &_etext - (unsigned long) &_text; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + max_mapnr << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >> 10, + initsize >> 10); + + /* + * Subtle. SMP is doing it's boot stuff late (because it has to + * fork idle threads) - but it also needs low mappings for the + * protected-mode entry to work. We zap these entries only after + * the WP-bit has been tested. + */ +#ifndef CONFIG_SMP + zap_low_mappings(); +#endif +} + +void free_initmem(void) +{ + unsigned long addr; + + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + set_page_count(virt_to_page(addr), 1); + free_page(addr); + totalram_pages++; + } + printk ("Freeing unused kernel memory: %luk freed\n", (&__init_end - &__init_begin) >> 10); +} + +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + if (start < (unsigned long)&_end) + return; + printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(virt_to_page(start)); + set_page_count(virt_to_page(start), 1); + free_page(start); + totalram_pages++; + } +} +#endif + +void si_meminfo(struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = nr_free_highpages(); + val->mem_unit = PAGE_SIZE; + return; +} diff -Nru a/arch/x86_64/mm/ioremap.c b/arch/x86_64/mm/ioremap.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/mm/ioremap.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,163 @@ +/* + * arch/i386/mm/ioremap.c + * + * Re-map IO memory to kernel address space so that we can access it. + * This is needed for high PCI addresses that aren't mapped in the + * 640k-1MB IO memory area on PC's + * + * (C) Copyright 1995 1996 Linus Torvalds + */ + +#include +#include +#include + +static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + if (address >= end) + BUG(); + do { + if (!pte_none(*pte)) { + printk("remap_area_pte: page already exists\n"); + BUG(); + } + set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | _PAGE_RW | + _PAGE_DIRTY | _PAGE_ACCESSED | flags))); + address += PAGE_SIZE; + phys_addr += PAGE_SIZE; + pte++; + } while (address && (address < end)); +} + +static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + phys_addr -= address; + if (address >= end) + BUG(); + do { + pte_t * pte = pte_alloc(&init_mm, pmd, address); + if (!pte) + return -ENOMEM; + remap_area_pte(pte, address, end - address, address + phys_addr, flags); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address && (address < end)); + return 0; +} + +static int remap_area_pages(unsigned long address, unsigned long phys_addr, + unsigned long size, unsigned long flags) +{ + int error; + pgd_t * dir; + unsigned long end = address + size; + + phys_addr -= address; + dir = pgd_offset(&init_mm, address); + flush_cache_all(); + if (address >= end) + BUG(); + spin_lock(&init_mm.page_table_lock); + do { + pmd_t *pmd; + pmd = pmd_alloc(&init_mm, dir, address); + error = -ENOMEM; + if (!pmd) + break; + if (remap_area_pmd(pmd, address, end - address, + phys_addr + address, flags)) + break; + error = 0; + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (address && (address < end)); + spin_unlock(&init_mm.page_table_lock); + flush_tlb_all(); + return error; +} + +/* + * Generic mapping function (not visible outside): + */ + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + * + * NOTE! We need to allow non-page-aligned mappings too: we will obviously + * have to convert them into an offset in a page-aligned mapping, but the + * caller shouldn't need to know that small detail. + */ +void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + void * addr; + struct vm_struct * area; + unsigned long offset, last_addr; + + /* Don't allow wraparound or zero size */ + last_addr = phys_addr + size - 1; + if (!size || last_addr < phys_addr) + return NULL; + + /* + * Don't remap the low PCI/ISA area, it's always mapped.. + */ + if (phys_addr >= 0xA0000 && last_addr < 0x100000) + return phys_to_virt(phys_addr); + + /* + * Don't allow anybody to remap normal RAM that we're using.. + */ + if (phys_addr < virt_to_phys(high_memory)) { + char *t_addr, *t_end; + struct page *page; + + t_addr = __va(phys_addr); + t_end = t_addr + (size - 1); + + for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++) + if(!PageReserved(page)) + return NULL; + } + + /* + * Mappings have to be page-aligned + */ + offset = phys_addr & ~PAGE_MASK; + phys_addr &= PAGE_MASK; + size = PAGE_ALIGN(last_addr) - phys_addr; + + /* + * Ok, go for it.. + */ + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + addr = area->addr; + if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) { + vfree(addr); + return NULL; + } + return (void *) (offset + (char *)addr); +} + +void iounmap(void *addr) +{ + if (addr > high_memory) + return vfree((void *) (PAGE_MASK & (unsigned long) addr)); +} diff -Nru a/arch/x86_64/tools/Makefile b/arch/x86_64/tools/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/tools/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,31 @@ + +TARGET = $(TOPDIR)/include/asm-x86_64/offset.h + +all: + +mrproper: + +fastdep: $(TARGET) + +.PHONY: all + +$(TARGET): offset.h + cmp -s $^ $@ || (cp $^ $(TARGET).new && mv $(TARGET).new $(TARGET)) + +.PHONY : offset.h all modules modules_install + +offset.h: offset.sed offset.c FORCE_RECOMPILE + $(CC) $(CFLAGS) -S -o offset.tmp offset.c + sed -n -f offset.sed < offset.tmp > offset.h + +FORCE_RECOMPILE: + +clean: + rm -f offset.[hs] $(TARGET).new offset.tmp + +mrproper: + rm -f offset.[hs] $(TARGET) + rm -f $(TARGET) + +include $(TOPDIR)/Rules.make + diff -Nru a/arch/x86_64/tools/offset.c b/arch/x86_64/tools/offset.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/tools/offset.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,70 @@ +/* Written 2000 by Andi Kleen */ +/* This program is never executed, just its assembly is examined for offsets + (this trick is needed to get cross compiling right) */ +/* $Id: offset.c,v 1.13 2002/01/08 15:19:57 ak Exp $ */ +#define ASM_OFFSET_H 1 +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#define output(x) asm volatile ("--- " x) +#define outconst(x,y) asm volatile ("--- " x : : "i" (y)) + +int main(void) +{ + output("/* Auto generated by arch/../tools/offset.c at " __DATE__ ". Do not edit. */\n"); + output("#ifndef ASM_OFFSET_H\n"); + output("#define ASM_OFFSET_H 1\n"); + + // task struct entries needed by entry.S +#define ENTRY(entry) outconst("#define tsk_" #entry " %0", offsetof(struct task_struct, entry)) + ENTRY(state); + ENTRY(flags); + ENTRY(thread); +#undef ENTRY +#define ENTRY(entry) outconst("#define threadinfo_" #entry " %0", offsetof(struct thread_info, entry)) + ENTRY(flags); + ENTRY(addr_limit); + ENTRY(preempt_count); +#undef ENTRY +#define ENTRY(entry) outconst("#define pda_" #entry " %0", offsetof(struct x8664_pda, entry)) + ENTRY(kernelstack); + ENTRY(oldrsp); + ENTRY(pcurrent); + ENTRY(irqrsp); + ENTRY(irqcount); + ENTRY(irqstack); + ENTRY(pgd_quick); + ENTRY(pmd_quick); + ENTRY(pte_quick); + ENTRY(pgtable_cache_sz); + ENTRY(cpunumber); + ENTRY(irqstackptr); + ENTRY(me); + ENTRY(__softirq_pending); + ENTRY(__local_irq_count); + ENTRY(__local_bh_count); + ENTRY(__ksoftirqd_task); +#undef ENTRY + output("#ifdef __ASSEMBLY__"); +#define CONST(t) outconst("#define " #t " %0", t) + CONST(TASK_SIZE); + CONST(SIGCHLD); + CONST(CLONE_VFORK); + CONST(CLONE_VM); +#undef CONST + output("#endif"); + + output("#endif\n"); + + return(0); +} + diff -Nru a/arch/x86_64/tools/offset.sed b/arch/x86_64/tools/offset.sed --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/tools/offset.sed Tue Feb 19 18:09:00 2002 @@ -0,0 +1,7 @@ +/---/ { + s/---// + s/\$// + s/^ // + s/^ // + p +} diff -Nru a/arch/x86_64/vmlinux.lds b/arch/x86_64/vmlinux.lds --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/x86_64/vmlinux.lds Tue Feb 19 18:09:00 2002 @@ -0,0 +1,112 @@ +/* ld script to make x86-64 Linux kernel + * Written by Martin Mares ; + */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS +{ + . = 0xffffffff80100000; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + *(.fixup) + *(.gnu.warning) + } = 0x9090 + .text.lock : { *(.text.lock) } /* out-of-line lock text */ + + _etext = .; /* End of text section */ + + .rodata : { *(.rodata) *(.rodata.*) } + .kstrtab : { *(.kstrtab) } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + + .data : { /* Data */ + *(.data) + CONSTRUCTORS + } + + _edata = .; /* End of data section */ + + __bss_start = .; /* BSS */ + .bss : { + *(.bss) + } + __bss_end = .; + + . = ALIGN(64); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + .vsyscall_0 -10*1024*1024: AT ((LOADADDR(.data.cacheline_aligned) + SIZEOF(.data.cacheline_aligned) + 4095) & ~(4095)) { *(.vsyscall_0) } + __vsyscall_0 = LOADADDR(.vsyscall_0); + . = ALIGN(64); + .vxtime_sequence : AT ((LOADADDR(.vsyscall_0) + SIZEOF(.vsyscall_0) + 63) & ~(63)) { *(.vxtime_sequence) } + vxtime_sequence = LOADADDR(.vxtime_sequence); + .last_tsc_low : AT (LOADADDR(.vxtime_sequence) + SIZEOF(.vxtime_sequence)) { *(.last_tsc_low) } + last_tsc_low = LOADADDR(.last_tsc_low); + .delay_at_last_interrupt : AT (LOADADDR(.last_tsc_low) + SIZEOF(.last_tsc_low)) { *(.delay_at_last_interrupt) } + delay_at_last_interrupt = LOADADDR(.delay_at_last_interrupt); + .fast_gettimeoffset_quotient : AT (LOADADDR(.delay_at_last_interrupt) + SIZEOF(.delay_at_last_interrupt)) { *(.fast_gettimeoffset_quotient) } + fast_gettimeoffset_quotient = LOADADDR(.fast_gettimeoffset_quotient); + .wall_jiffies : AT (LOADADDR(.fast_gettimeoffset_quotient) + SIZEOF(.fast_gettimeoffset_quotient)) { *(.wall_jiffies) } + wall_jiffies = LOADADDR(.wall_jiffies); + .sys_tz : AT (LOADADDR(.wall_jiffies) + SIZEOF(.wall_jiffies)) { *(.sys_tz) } + sys_tz = LOADADDR(.sys_tz); + .jiffies : AT (LOADADDR(.sys_tz) + SIZEOF(.sys_tz)) { *(.jiffies) } + jiffies = LOADADDR(.jiffies); + . = ALIGN(16); + .xtime : AT ((LOADADDR(.jiffies) + SIZEOF(.jiffies) + 15) & ~(15)) { *(.xtime) } + xtime = LOADADDR(.xtime); + .vsyscall_1 ADDR(.vsyscall_0) + 1024: AT (LOADADDR(.vsyscall_0) + 1024) { *(.vsyscall_1) } + . = LOADADDR(.vsyscall_0) + 4096; + + . = ALIGN(8192); /* init_task */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { + *(.initcall1.init) + *(.initcall2.init) + *(.initcall3.init) + *(.initcall4.init) + *(.initcall5.init) + *(.initcall6.init) + *(.initcall7.init) + } + __initcall_end = .; + . = ALIGN(4096); + __init_end = .; + + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.data.exit) + *(.exitcall.exit) + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff -Nru a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile Tue Feb 19 18:08:58 2002 +++ b/drivers/Makefile Tue Feb 19 18:08:58 2002 @@ -10,7 +10,7 @@ message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ fc4 net/hamradio i2c acpi bluetooth input/serio input/gameport -subdir-y := base parport char block net sound misc media cdrom hotplug +subdir-y := base parport char block net misc media cdrom hotplug subdir-m := $(subdir-y) diff -Nru a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c --- a/drivers/acorn/block/mfmhd.c Tue Feb 19 18:08:58 2002 +++ b/drivers/acorn/block/mfmhd.c Tue Feb 19 18:08:58 2002 @@ -1208,15 +1208,6 @@ return -EFAULT; return 0; - case BLKFRASET: - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - max_readahead[major][minor] = arg; - return 0; - - case BLKFRAGET: - return put_user(max_readahead[major][minor], (long *) arg); - case BLKSECTGET: return put_user(max_sectors[major][minor], (long *) arg); @@ -1230,8 +1221,6 @@ case BLKFLSBUF: case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKPG: return blk_ioctl(dev, cmd, arg); @@ -1442,7 +1431,6 @@ hdc63463_irqpollmask = irqmask; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB?) read ahread */ add_gendisk(&mfm_gendisk); diff -Nru a/drivers/base/interface.c b/drivers/base/interface.c --- a/drivers/base/interface.c Tue Feb 19 18:08:57 2002 +++ b/drivers/base/interface.c Tue Feb 19 18:08:57 2002 @@ -99,10 +99,10 @@ device_write_power(struct device * dev, const char * buf, size_t count, loff_t off) { char str_command[20]; - char str_stage[20]; + char str_level[20]; int num_args; u32 state; - u32 int_stage; + u32 int_level; int error = 0; if (off) @@ -111,7 +111,7 @@ if (!dev->driver) goto done; - num_args = sscanf(buf,"%10s %10s %u",str_command,str_stage,&state); + num_args = sscanf(buf,"%10s %10s %u",str_command,str_level,&state); error = -EINVAL; @@ -121,36 +121,36 @@ if (!strnicmp(str_command,"suspend",7)) { if (num_args != 3) goto done; - if (!strnicmp(str_stage,"notify",6)) - int_stage = SUSPEND_NOTIFY; - else if (!strnicmp(str_stage,"save",4)) - int_stage = SUSPEND_SAVE_STATE; - else if (!strnicmp(str_stage,"disable",7)) - int_stage = SUSPEND_DISABLE; - else if (!strnicmp(str_stage,"powerdown",8)) - int_stage = SUSPEND_POWER_DOWN; + if (!strnicmp(str_level,"notify",6)) + int_level = SUSPEND_NOTIFY; + else if (!strnicmp(str_level,"save",4)) + int_level = SUSPEND_SAVE_STATE; + else if (!strnicmp(str_level,"disable",7)) + int_level = SUSPEND_DISABLE; + else if (!strnicmp(str_level,"powerdown",8)) + int_level = SUSPEND_POWER_DOWN; else goto done; if (dev->driver->suspend) - error = dev->driver->suspend(dev,state,int_stage); + error = dev->driver->suspend(dev,state,int_level); else error = 0; } else if (!strnicmp(str_command,"resume",6)) { if (num_args != 2) goto done; - if (!strnicmp(str_stage,"poweron",7)) - int_stage = RESUME_POWER_ON; - else if (!strnicmp(str_stage,"restore",7)) - int_stage = RESUME_RESTORE_STATE; - else if (!strnicmp(str_stage,"enable",6)) - int_stage = RESUME_ENABLE; + if (!strnicmp(str_level,"poweron",7)) + int_level = RESUME_POWER_ON; + else if (!strnicmp(str_level,"restore",7)) + int_level = RESUME_RESTORE_STATE; + else if (!strnicmp(str_level,"enable",6)) + int_level = RESUME_ENABLE; else goto done; if (dev->driver->resume) - error = dev->driver->resume(dev,int_stage); + error = dev->driver->resume(dev,int_level); else error = 0; } diff -Nru a/drivers/block/DAC960.c b/drivers/block/DAC960.c --- a/drivers/block/DAC960.c Tue Feb 19 18:09:00 2002 +++ b/drivers/block/DAC960.c Tue Feb 19 18:09:00 2002 @@ -1964,10 +1964,6 @@ Controller->GenericDiskInfo.sizes = Controller->PartitionSizes; blksize_size[MajorNumber] = Controller->BlockSizes; /* - Initialize Read Ahead to 128 sectors. - */ - read_ahead[MajorNumber] = 128; - /* Complete initialization of the Generic Disk Information structure. */ Controller->GenericDiskInfo.major = MajorNumber; @@ -5399,8 +5395,6 @@ sizeof(DiskGeometry_T)) ? -EFAULT : 0); case BLKGETSIZE: case BLKGETSIZE64: - case BLKRAGET: - case BLKRASET: case BLKFLSBUF: case BLKBSZGET: case BLKBSZSET: diff -Nru a/drivers/block/acsi.c b/drivers/block/acsi.c --- a/drivers/block/acsi.c Tue Feb 19 18:09:00 2002 +++ b/drivers/block/acsi.c Tue Feb 19 18:09:00 2002 @@ -1785,7 +1785,6 @@ STramMask = ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 : 0xff000000; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &acsi_lock); - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ add_gendisk(&acsi_gendisk); #ifdef CONFIG_ATARI_SLM diff -Nru a/drivers/block/ataflop.c b/drivers/block/ataflop.c --- a/drivers/block/ataflop.c Tue Feb 19 18:08:58 2002 +++ b/drivers/block/ataflop.c Tue Feb 19 18:08:58 2002 @@ -1573,8 +1573,6 @@ switch (cmd) { case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKFLSBUF: return blk_ioctl(device, cmd, param); } diff -Nru a/drivers/block/blkpg.c b/drivers/block/blkpg.c --- a/drivers/block/blkpg.c Tue Feb 19 18:08:57 2002 +++ b/drivers/block/blkpg.c Tue Feb 19 18:08:57 2002 @@ -29,7 +29,7 @@ */ #include -#include /* for BLKRASET, ... */ +#include /* for BLKROSET, ... */ #include /* for capable() */ #include /* for set_device_ro() */ #include @@ -205,7 +205,7 @@ request_queue_t *q; struct gendisk *g; u64 ullval = 0; - int intval, *iptr; + int intval; unsigned short usval; if (kdev_none(dev)) @@ -226,31 +226,6 @@ case BLKROGET: intval = (is_read_only(dev) != 0); return put_user(intval, (int *)(arg)); - - case BLKRASET: - if(!capable(CAP_SYS_ADMIN)) - return -EACCES; - if(arg > 0xff) - return -EINVAL; - read_ahead[major(dev)] = arg; - return 0; - case BLKRAGET: - if (!arg) - return -EINVAL; - return put_user(read_ahead[major(dev)], (long *) arg); - - case BLKFRASET: - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - if (!(iptr = max_readahead[major(dev)])) - return -EINVAL; - iptr[minor(dev)] = arg; - return 0; - - case BLKFRAGET: - if (!(iptr = max_readahead[major(dev)])) - return -EINVAL; - return put_user(iptr[minor(dev)], (long *) arg); case BLKSECTGET: if ((q = blk_get_queue(dev)) == NULL) diff -Nru a/drivers/block/cciss.c b/drivers/block/cciss.c --- a/drivers/block/cciss.c Tue Feb 19 18:08:59 2002 +++ b/drivers/block/cciss.c Tue Feb 19 18:08:59 2002 @@ -44,12 +44,12 @@ #include #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "Compaq CISS Driver (v 2.4.5)" -#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,4,5) +#define DRIVER_NAME "Compaq CISS Driver (v 2.5.0)" +#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,5,0) /* Embedded module documentation macros - see modules.h */ MODULE_AUTHOR("Charles M. White III - Compaq Computer Corporation"); -MODULE_DESCRIPTION("Driver for Compaq Smart Array Controller 5300"); +MODULE_DESCRIPTION("Driver for Compaq Smart Array Controller 5xxx v. 2.5.0"); MODULE_LICENSE("GPL"); #include "cciss_cmd.h" @@ -372,21 +372,18 @@ if (ctlr > MAX_CTLR || hba[ctlr] == NULL) return -ENXIO; - - if (!suser() && hba[ctlr]->sizes[minor(inode->i_rdev)] == 0) - return -ENXIO; - /* * Root is allowed to open raw volume zero even if its not configured * so array config can still work. I don't think I really like this, * but I'm already using way to many device nodes to claim another one * for "raw controller". */ - if (suser() - && (hba[ctlr]->sizes[minor(inode->i_rdev)] == 0) - && (minor(inode->i_rdev)!= 0)) - return -ENXIO; - + if (hba[ctlr]->sizes[minor(inode->i_rdev)] == 0) { + if (minor(inode->i_rdev) != 0) + return -ENXIO; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + } hba[ctlr]->drv[dsk].usage_count++; hba[ctlr]->usage_count++; return 0; @@ -471,8 +468,6 @@ case BLKBSZGET: case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKPG: return blk_ioctl(inode->i_rdev, cmd, arg); case CCISS_GETPCIINFO: @@ -647,6 +642,7 @@ char *buff = NULL; u64bit temp64; unsigned long flags; + DECLARE_COMPLETION(wait); if (!arg) return -EINVAL; @@ -712,6 +708,8 @@ c->SG[0].Len = iocommand.buf_size; c->SG[0].Ext = 0; // we are not chaining } + c->waiting = &wait; + /* Put the request on the tail of the request queue */ spin_lock_irqsave(CCISS_LOCK(ctlr), flags); addQ(&h->reqQ, c); @@ -719,9 +717,7 @@ start_io(h); spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); - /* Wait for completion */ - while(c->cmd_type != CMD_IOCTL_DONE) - schedule_timeout(1); + wait_for_completion(&wait); /* unlock the buffers from DMA */ temp64.val32.lower = c->SG[0].Addr.lower; @@ -933,6 +929,7 @@ u64bit buff_dma_handle; unsigned long flags; int return_status = IO_OK; + DECLARE_COMPLETION(wait); if ((c = cmd_alloc(h , 0)) == NULL) { @@ -1026,6 +1023,7 @@ c->SG[0].Len = size; c->SG[0].Ext = 0; // we are not chaining } + c->waiting = &wait; /* Put the request on the tail of the queue and send it */ spin_lock_irqsave(CCISS_LOCK(ctlr), flags); @@ -1034,9 +1032,8 @@ start_io(h); spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); - /* wait for completion */ - while(c->cmd_type != CMD_IOCTL_DONE) - schedule_timeout(1); + wait_for_completion(&wait); + /* unlock the buffers from DMA */ pci_unmap_single( h->pdev, (dma_addr_t) buff_dma_handle.val, size, PCI_DMA_BIDIRECTIONAL); @@ -1128,6 +1125,7 @@ __u32 lunid = 0; unsigned int block_size; unsigned int total_size; + kdev_t kdev; if (!capable(CAP_SYS_RAWIO)) return -EPERM; @@ -1340,7 +1338,7 @@ for(i=max_p-1; i>=0; i--) { int minor = start+i; - kdev_t kdev = mk_kdev(MAJOR_NR + ctlr, minor); + kdev = mk_kdev(MAJOR_NR + ctlr, minor); invalidate_device(kdev, 1); gdev->part[minor].start_sect = 0; gdev->part[minor].nr_sects = 0; @@ -1352,7 +1350,8 @@ ++hba[ctlr]->num_luns; gdev->nr_real = hba[ctlr]->highest_lun + 1; /* setup partitions per disk */ - grok_partitions(dev, hba[ctlr]->drv[logvol].nr_blocks); + kdev = mk_kdev(MAJOR_NR + ctlr, logvol<< gdev->minor_shift); + grok_partitions(kdev, hba[ctlr]->drv[logvol].nr_blocks); kfree(ld_buff); kfree(size_buff); @@ -1672,12 +1671,11 @@ static inline void complete_buffers(struct bio *bio, int status) { while (bio) { - int nsecs = bio_sectors(bio); - struct bio *xbh = bio->bi_next; + bio->bi_next = NULL; - blk_finished_io(nsecs); - bio_endio(bio, status, nsecs); + blk_finished_io(bio_sectors(bio)); + bio_endio(bio, status); bio = xbh; } @@ -1957,7 +1955,7 @@ complete_command(c, 0); cmd_free(h, c, 1); } else if (c->cmd_type == CMD_IOCTL_PEND) { - c->cmd_type = CMD_IOCTL_DONE; + complete(c->waiting); } # ifdef CONFIG_CISS_SCSI_TAPE else if (c->cmd_type == CMD_SCSI) @@ -2466,7 +2464,8 @@ /* make sure the board interrupts are off */ hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF); if( request_irq(hba[i]->intr, do_cciss_intr, - SA_INTERRUPT|SA_SHIRQ, hba[i]->devname, hba[i])) + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, + hba[i]->devname, hba[i])) { printk(KERN_ERR "ciss: Unable to get irq %d for %s\n", hba[i]->intr, hba[i]->devname); @@ -2542,7 +2541,6 @@ /* fill in the other Kernel structs */ blksize_size[MAJOR_NR+i] = hba[i]->blocksizes; - read_ahead[MAJOR_NR+i] = READ_AHEAD; /* Fill in the gendisk data */ hba[i]->gendisk.major = MAJOR_NR + i; diff -Nru a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h --- a/drivers/block/cciss_cmd.h Tue Feb 19 18:08:58 2002 +++ b/drivers/block/cciss_cmd.h Tue Feb 19 18:08:58 2002 @@ -222,7 +222,6 @@ /* Command types */ #define CMD_RWREQ 0x00 #define CMD_IOCTL_PEND 0x01 -#define CMD_IOCTL_DONE 0x02 #define CMD_SCSI 0x03 #define CMD_MSG_DONE 0x04 #define CMD_MSG_TIMEOUT 0x05 @@ -240,6 +239,7 @@ struct _CommandList_struct *prev; struct _CommandList_struct *next; struct request * rq; + struct completion *waiting; #ifdef CONFIG_CISS_SCSI_TAPE void * scsi_cmd; #endif diff -Nru a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c --- a/drivers/block/cciss_scsi.c Tue Feb 19 18:08:58 2002 +++ b/drivers/block/cciss_scsi.c Tue Feb 19 18:08:58 2002 @@ -897,6 +897,7 @@ int direction) { unsigned long flags; + DECLARE_COMPLETION(wait); cp->cmd_type = CMD_IOCTL_PEND; // treat this like an ioctl cp->scsi_cmd = NULL; @@ -922,6 +923,8 @@ (unsigned char *) buf, bufsize, scsi_to_pci_dma_dir(SCSI_DATA_READ)); + cp->waiting = &wait; + /* Put the request on the tail of the request queue */ spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags); addQ(&c->reqQ, cp); @@ -929,9 +932,7 @@ start_io(c); spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags); - /* Wait for the request to complete */ - while(cp->cmd_type != CMD_IOCTL_DONE) - schedule_timeout(1); + wait_for_completion(&wait); /* undo the dma mapping */ cciss_unmap_one(c->pdev, cp, bufsize, @@ -1086,10 +1087,10 @@ cdb[3] = 0; cdb[4] = 0; cdb[5] = 0; - cdb[6] = (sizeof(*buf) >> 24) & 0xFF; //MSB - cdb[7] = (sizeof(*buf) >> 16) & 0xFF; - cdb[8] = (sizeof(*buf) >> 8) & 0xFF; - cdb[9] = sizeof(*buf) & 0xFF; + cdb[6] = (bufsize >> 24) & 0xFF; //MSB + cdb[7] = (bufsize >> 16) & 0xFF; + cdb[8] = (bufsize >> 8) & 0xFF; + cdb[9] = bufsize & 0xFF; cdb[10] = 0; cdb[11] = 0; diff -Nru a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c --- a/drivers/block/cpqarray.c Tue Feb 19 18:08:58 2002 +++ b/drivers/block/cpqarray.c Tue Feb 19 18:08:58 2002 @@ -481,7 +481,6 @@ blk_queue_max_phys_segments(q, SG_MAX); blksize_size[MAJOR_NR+i] = ida_blocksizes + (i*256); - read_ahead[MAJOR_NR+i] = READ_AHEAD; ida_gendisk[i].major = MAJOR_NR + i; ida_gendisk[i].major_name = "ida"; @@ -970,13 +969,11 @@ { struct bio *xbh; while(bio) { - int nsecs = bio_sectors(bio); - xbh = bio->bi_next; bio->bi_next = NULL; - blk_finished_io(nsecs); - bio_endio(bio, ok, nsecs); + blk_finished_io(bio_sectors(bio)); + bio_endio(bio, ok); bio = xbh; } @@ -1181,8 +1178,6 @@ case BLKBSZGET: case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKPG: return blk_ioctl(inode->i_rdev, cmd, arg); diff -Nru a/drivers/block/floppy.c b/drivers/block/floppy.c --- a/drivers/block/floppy.c Tue Feb 19 18:08:58 2002 +++ b/drivers/block/floppy.c Tue Feb 19 18:08:58 2002 @@ -3448,8 +3448,6 @@ switch (cmd) { case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKFLSBUF: return blk_ioctl(device, cmd, param); } diff -Nru a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c --- a/drivers/block/ll_rw_blk.c Tue Feb 19 18:08:57 2002 +++ b/drivers/block/ll_rw_blk.c Tue Feb 19 18:08:57 2002 @@ -54,10 +54,6 @@ */ DECLARE_TASK_QUEUE(tq_disk); -/* This specifies how many sectors to read ahead on the disk. */ - -int read_ahead[MAX_BLKDEV]; - /* blk_dev_struct is: * request_queue * *queue @@ -84,11 +80,6 @@ int * blksize_size[MAX_BLKDEV]; /* - * The following tunes the read-ahead algorithm in mm/filemap.c - */ -int * max_readahead[MAX_BLKDEV]; - -/* * How many reqeusts do we allocate per queue, * and how many do we "batch" on freeing them? */ @@ -1227,7 +1218,7 @@ return 0; end_io: - bio->bi_end_io(bio, nr_sectors); + bio->bi_end_io(bio); return 0; } @@ -1329,7 +1320,7 @@ "generic_make_request: Trying to access nonexistent block-device %s (%Lu)\n", kdevname(bio->bi_dev), (long long) bio->bi_sector); end_io: - bio->bi_end_io(bio, nr_sectors); + bio->bi_end_io(bio); break; } @@ -1350,15 +1341,12 @@ /* * our default bio end_io callback handler for a buffer_head mapping. */ -static int end_bio_bh_io_sync(struct bio *bio, int nr_sectors) +static void end_bio_bh_io_sync(struct bio *bio) { struct buffer_head *bh = bio->bi_private; - BIO_BUG_ON(nr_sectors != (bh->b_size >> 9)); - bh->b_end_io(bh, test_bit(BIO_UPTODATE, &bio->bi_flags)); bio_put(bio); - return 0; } /** @@ -1641,8 +1629,7 @@ if (!bio->bi_size) { req->bio = bio->bi_next; - if (unlikely(bio_endio(bio, uptodate, total_nsect))) - BUG(); + bio_endio(bio, uptodate); total_nsect = 0; } @@ -1689,7 +1676,6 @@ dev->queue = NULL; memset(ro_bits,0,sizeof(ro_bits)); - memset(max_readahead, 0, sizeof(max_readahead)); total_ram = nr_free_pages() << (PAGE_SHIFT - 10); diff -Nru a/drivers/block/loop.c b/drivers/block/loop.c --- a/drivers/block/loop.c Tue Feb 19 18:08:57 2002 +++ b/drivers/block/loop.c Tue Feb 19 18:08:57 2002 @@ -319,7 +319,7 @@ return ret; } -static int loop_end_io_transfer(struct bio *, int); +static void loop_end_io_transfer(struct bio *); static void loop_put_buffer(struct bio *bio) { /* @@ -377,21 +377,19 @@ * bi_end_io context (we don't want to do decrypt of a page with irqs * disabled) */ -static int loop_end_io_transfer(struct bio *bio, int nr_sectors) +static void loop_end_io_transfer(struct bio *bio) { struct bio *rbh = bio->bi_private; struct loop_device *lo = &loop_dev[minor(rbh->bi_dev)]; int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); if (!uptodate || bio_rw(bio) == WRITE) { - bio_endio(rbh, uptodate, nr_sectors); + bio_endio(rbh, uptodate); if (atomic_dec_and_test(&lo->lo_pending)) up(&lo->lo_bh_mutex); loop_put_buffer(bio); } else loop_add_bio(lo, bio); - - return 0; } static struct bio *loop_get_buffer(struct loop_device *lo, struct bio *rbh) @@ -511,13 +509,13 @@ */ if (lo->lo_flags & LO_FLAGS_DO_BMAP) { ret = do_bio_filebacked(lo, bio); - bio_endio(bio, !ret, bio_sectors(bio)); + bio_endio(bio, !ret); } else { struct bio *rbh = bio->bi_private; ret = do_bio_blockbacked(lo, bio, rbh); - bio_endio(rbh, !ret, bio_sectors(rbh)); + bio_endio(rbh, !ret); loop_put_buffer(bio); } } diff -Nru a/drivers/block/nbd.c b/drivers/block/nbd.c --- a/drivers/block/nbd.c Tue Feb 19 18:08:58 2002 +++ b/drivers/block/nbd.c Tue Feb 19 18:08:58 2002 @@ -104,7 +104,7 @@ spin_lock_irqsave(¤t->sigmask_lock, flags); oldset = current->blocked; sigfillset(¤t->blocked); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); @@ -139,7 +139,7 @@ spin_lock_irqsave(¤t->sigmask_lock, flags); current->blocked = oldset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); set_fs(oldfs); diff -Nru a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c --- a/drivers/block/paride/pcd.c Tue Feb 19 18:08:59 2002 +++ b/drivers/block/paride/pcd.c Tue Feb 19 18:08:59 2002 @@ -358,7 +358,6 @@ } blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &pcd_lock); - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ for (i=0;ii_rdev, cmd, arg); diff -Nru a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c --- a/drivers/block/paride/pf.c Tue Feb 19 18:08:58 2002 +++ b/drivers/block/paride/pf.c Tue Feb 19 18:08:58 2002 @@ -363,7 +363,6 @@ blk_init_queue(q, DEVICE_REQUEST, &pf_spin_lock); blk_queue_max_phys_segments(q, cluster); blk_queue_max_hw_segments(q, cluster); - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ for (i=0;ii_rdev, cmd, arg); default: diff -Nru a/drivers/block/ps2esdi.c b/drivers/block/ps2esdi.c --- a/drivers/block/ps2esdi.c Tue Feb 19 18:08:57 2002 +++ b/drivers/block/ps2esdi.c Tue Feb 19 18:08:57 2002 @@ -177,7 +177,6 @@ } /* set up some global information - indicating device specific info */ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &ps2esdi_lock); - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ /* some minor housekeeping - setup the global gendisk structure */ add_gendisk(&ps2esdi_gendisk); @@ -1108,8 +1107,6 @@ case BLKGETSIZE64: case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKFLSBUF: case BLKBSZGET: case BLKBSZSET: diff -Nru a/drivers/block/xd.c b/drivers/block/xd.c --- a/drivers/block/xd.c Tue Feb 19 18:08:58 2002 +++ b/drivers/block/xd.c Tue Feb 19 18:08:58 2002 @@ -171,7 +171,6 @@ } devfs_handle = devfs_mk_dir (NULL, xd_gendisk.major_name, NULL); blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &xd_lock); - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ add_gendisk(&xd_gendisk); xd_geninit(); @@ -355,8 +354,6 @@ case BLKFLSBUF: case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKPG: return blk_ioctl(inode->i_rdev, cmd, arg); diff -Nru a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c --- a/drivers/bluetooth/hci_usb.c Tue Feb 19 18:08:59 2002 +++ b/drivers/bluetooth/hci_usb.c Tue Feb 19 18:08:59 2002 @@ -547,17 +547,17 @@ husb->udev = udev; husb->bulk_out_ep_addr = bulk_out_ep->bEndpointAddress; - if (!(husb->ctrl_urb = usb_alloc_urb(0))) { + if (!(husb->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL))) { ERR("Can't allocate: control URB"); goto probe_error; } - if (!(husb->write_urb = usb_alloc_urb(0))) { + if (!(husb->write_urb = usb_alloc_urb(0, GFP_KERNEL))) { ERR("Can't allocate: write URB"); goto probe_error; } - if (!(husb->read_urb = usb_alloc_urb(0))) { + if (!(husb->read_urb = usb_alloc_urb(0, GFP_KERNEL))) { ERR("Can't allocate: read URB"); goto probe_error; } @@ -578,7 +578,7 @@ pipe = usb_rcvintpipe(udev, ep->bEndpointAddress); size = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); - if (!(husb->intr_urb = usb_alloc_urb(0))) { + if (!(husb->intr_urb = usb_alloc_urb(0, GFP_KERNEL))) { ERR("Can't allocate: interrupt URB"); goto probe_error; } diff -Nru a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c --- a/drivers/cdrom/aztcd.c Tue Feb 19 18:08:59 2002 +++ b/drivers/cdrom/aztcd.c Tue Feb 19 18:08:59 2002 @@ -1927,7 +1927,6 @@ } blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &aztSpin); blksize_size[MAJOR_NR] = aztcd_blocksizes; - read_ahead[MAJOR_NR] = 4; register_disk(NULL, mk_kdev(MAJOR_NR, 0), 1, &azt_fops, 0); if ((azt_port == 0x1f0) || (azt_port == 0x170)) diff -Nru a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c --- a/drivers/cdrom/cdu31a.c Tue Feb 19 18:08:58 2002 +++ b/drivers/cdrom/cdu31a.c Tue Feb 19 18:08:58 2002 @@ -3442,7 +3442,6 @@ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &cdu31a_lock); - read_ahead[MAJOR_NR] = CDU31A_READAHEAD; cdu31a_block_size = 1024; /* 1kB default block size */ /* use 'mount -o block=2048' */ blksize_size[MAJOR_NR] = &cdu31a_block_size; diff -Nru a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c --- a/drivers/cdrom/cm206.c Tue Feb 19 18:08:59 2002 +++ b/drivers/cdrom/cm206.c Tue Feb 19 18:08:59 2002 @@ -1503,7 +1503,6 @@ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &cm206_lock); blksize_size[MAJOR_NR] = cm206_blocksizes; - read_ahead[MAJOR_NR] = 16; /* reads ahead what? */ init_bh(CM206_BH, cm206_bh); memset(cd, 0, sizeof(*cd)); /* give'm some reasonable value */ diff -Nru a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c --- a/drivers/cdrom/gscd.c Tue Feb 19 18:08:57 2002 +++ b/drivers/cdrom/gscd.c Tue Feb 19 18:08:57 2002 @@ -1022,7 +1022,6 @@ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &gscd_lock); blksize_size[MAJOR_NR] = gscd_blocksizes; - read_ahead[MAJOR_NR] = 4; disk_state = 0; gscdPresent = 1; diff -Nru a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c --- a/drivers/cdrom/mcd.c Tue Feb 19 18:08:57 2002 +++ b/drivers/cdrom/mcd.c Tue Feb 19 18:08:57 2002 @@ -1075,7 +1075,6 @@ blksize_size[MAJOR_NR] = mcd_blocksizes; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &mcd_spinlock); - read_ahead[MAJOR_NR] = 4; /* check for card */ diff -Nru a/drivers/cdrom/mcdx.c b/drivers/cdrom/mcdx.c --- a/drivers/cdrom/mcdx.c Tue Feb 19 18:08:59 2002 +++ b/drivers/cdrom/mcdx.c Tue Feb 19 18:08:59 2002 @@ -1184,7 +1184,6 @@ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &mcdx_lock); - read_ahead[MAJOR_NR] = READ_AHEAD; blksize_size[MAJOR_NR] = mcdx_blocksizes; xtrace(INIT, "init() subscribe irq and i/o\n"); diff -Nru a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c --- a/drivers/cdrom/optcd.c Tue Feb 19 18:08:58 2002 +++ b/drivers/cdrom/optcd.c Tue Feb 19 18:08:58 2002 @@ -2062,7 +2062,6 @@ blksize_size[MAJOR_NR] = &blksize; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &optcd_lock); - read_ahead[MAJOR_NR] = 4; request_region(optcd_port, 4, "optcd"); register_disk(NULL, mk_kdev(MAJOR_NR,0), 1, &opt_fops, 0); diff -Nru a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c --- a/drivers/cdrom/sbpcd.c Tue Feb 19 18:08:58 2002 +++ b/drivers/cdrom/sbpcd.c Tue Feb 19 18:08:58 2002 @@ -4531,12 +4531,6 @@ RETURN_UP(0); } /* end of CDROMREADAUDIO */ - case BLKRASET: - if(!capable(CAP_SYS_ADMIN)) RETURN_UP(-EACCES); - if(kdev_none(cdi->dev)) RETURN_UP(-EINVAL); - if(arg > 0xff) RETURN_UP(-EINVAL); - read_ahead[major(cdi->dev)] = arg; - RETURN_UP(0); default: msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd); RETURN_UP(-EINVAL); diff -Nru a/drivers/cdrom/sjcd.c b/drivers/cdrom/sjcd.c --- a/drivers/cdrom/sjcd.c Tue Feb 19 18:08:57 2002 +++ b/drivers/cdrom/sjcd.c Tue Feb 19 18:08:57 2002 @@ -1695,7 +1695,6 @@ } blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,&sjcd_lock); - read_ahead[MAJOR_NR] = 4; register_disk(NULL, mk_kdev(MAJOR_NR, 0), 1, &sjcd_fops, 0); if (check_region(sjcd_base, 4)) { diff -Nru a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c --- a/drivers/cdrom/sonycd535.c Tue Feb 19 18:08:59 2002 +++ b/drivers/cdrom/sonycd535.c Tue Feb 19 18:08:59 2002 @@ -1598,7 +1598,6 @@ } blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &sonycd535_lock); blksize_size[MAJOR_NR] = &sonycd535_block_size; - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ sony_toc = (struct s535_sony_toc *) kmalloc(sizeof *sony_toc, GFP_KERNEL); diff -Nru a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c --- a/drivers/char/agp/agpgart_be.c Tue Feb 19 18:08:58 2002 +++ b/drivers/char/agp/agpgart_be.c Tue Feb 19 18:08:58 2002 @@ -830,7 +830,7 @@ page = virt_to_page(pt); atomic_dec(&page->count); clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + wake_up_page(page); free_page((unsigned long) pt); atomic_dec(&agp_bridge.current_memory_agp); } @@ -2828,7 +2828,7 @@ page = virt_to_page(pt); atomic_dec(&page->count); clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + wake_up_page(page); free_page((unsigned long) pt); atomic_dec(&agp_bridge.current_memory_agp); } diff -Nru a/drivers/char/drm/drm_proc.h b/drivers/char/drm/drm_proc.h --- a/drivers/char/drm/drm_proc.h Tue Feb 19 18:08:57 2002 +++ b/drivers/char/drm/drm_proc.h Tue Feb 19 18:08:57 2002 @@ -449,7 +449,8 @@ for (i = vma->vm_start; i < vma->vm_end; i += PAGE_SIZE) { pgd = pgd_offset(vma->vm_mm, i); pmd = pmd_offset(pgd, i); - pte = pte_offset(pmd, i); + preempt_disable(); + pte = pte_offset_map(pmd, i); if (pte_present(*pte)) { address = __pa(pte_page(*pte)) + (i & (PAGE_SIZE-1)); @@ -465,6 +466,8 @@ } else { DRM_PROC_PRINT(" 0x%08lx\n", i); } + pte_unmap(pte); + preempt_enable(); } #endif } diff -Nru a/drivers/char/drm/drm_scatter.h b/drivers/char/drm/drm_scatter.h --- a/drivers/char/drm/drm_scatter.h Tue Feb 19 18:08:57 2002 +++ b/drivers/char/drm/drm_scatter.h Tue Feb 19 18:08:57 2002 @@ -68,7 +68,7 @@ unsigned long pages, i, j; pgd_t *pgd; pmd_t *pmd; - pte_t *pte; + pte_t *pte, pte_entry; DRM_DEBUG( "%s\n", __FUNCTION__ ); @@ -143,13 +143,18 @@ if ( !pmd_present( *pmd ) ) goto failed; - pte = pte_offset( pmd, i ); - if ( !pte_present( *pte ) ) + preempt_disable(); + pte = pte_offset_map(pmd, i); + pte_entry = *pte; + pte_unmap(pte); + preempt_enable(); + + if (!pte_present(pte_entry)) goto failed; - entry->pagelist[j] = pte_page( *pte ); + entry->pagelist[j] = pte_page(pte_entry); - SetPageReserved( entry->pagelist[j] ); + SetPageReserved(entry->pagelist[j]); } request.handle = entry->handle; diff -Nru a/drivers/char/drm/drm_vm.h b/drivers/char/drm/drm_vm.h --- a/drivers/char/drm/drm_vm.h Tue Feb 19 18:09:00 2002 +++ b/drivers/char/drm/drm_vm.h Tue Feb 19 18:09:00 2002 @@ -154,7 +154,7 @@ unsigned long i; pgd_t *pgd; pmd_t *pmd; - pte_t *pte; + pte_t *pte, entry; struct page *page; if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ @@ -166,13 +166,22 @@ * they need to be virtually contiguous in kernel space. */ pgd = pgd_offset_k( i ); - if( !pgd_present( *pgd ) ) return NOPAGE_OOM; + if (!pgd_present(*pgd)) + goto oom; pmd = pmd_offset( pgd, i ); - if( !pmd_present( *pmd ) ) return NOPAGE_OOM; - pte = pte_offset( pmd, i ); - if( !pte_present( *pte ) ) return NOPAGE_OOM; + if (!pmd_present(*pmd)) + goto oom; - page = pte_page(*pte); + preempt_disable(); + pte = pte_offset_map(pmd, i); + entry = *pte; + pte_unmap(pte); + preempt_enable(); + + if (!pte_present(entry)) + goto oom; + + page = pte_page(entry); get_page(page); DRM_DEBUG("shm_nopage 0x%lx\n", address); @@ -181,6 +190,8 @@ #else return page; #endif +oom: + return NOPAGE_OOM; } /* Special close routine which deletes map information if we are the last diff -Nru a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c --- a/drivers/char/drm/i810_dma.c Tue Feb 19 18:08:59 2002 +++ b/drivers/char/drm/i810_dma.c Tue Feb 19 18:08:59 2002 @@ -294,14 +294,13 @@ static void i810_free_page(drm_device_t *dev, unsigned long page) { - if(page == 0UL) - return; - - atomic_dec(&virt_to_page(page)->count); - clear_bit(PG_locked, &virt_to_page(page)->flags); - wake_up(&virt_to_page(page)->wait); - free_page(page); - return; + if (page) { + struct page *p = virt_to_page(page); + atomic_dec(p); + clear_bit(PG_locked, &p->flags); + wake_up_page(p); + free_page(page); + } } static int i810_dma_cleanup(drm_device_t *dev) diff -Nru a/drivers/char/ftape/lowlevel/fdc-io.c b/drivers/char/ftape/lowlevel/fdc-io.c --- a/drivers/char/ftape/lowlevel/fdc-io.c Tue Feb 19 18:08:58 2002 +++ b/drivers/char/ftape/lowlevel/fdc-io.c Tue Feb 19 18:08:58 2002 @@ -407,7 +407,7 @@ spin_lock_irq(¤t->sigmask_lock); old_sigmask = current->blocked; sigfillset(¤t->blocked); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); current->state = TASK_INTERRUPTIBLE; @@ -418,7 +418,7 @@ spin_lock_irq(¤t->sigmask_lock); current->blocked = old_sigmask; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); remove_wait_queue(&ftape_wait_intr, &wait); diff -Nru a/drivers/char/pcwd.c b/drivers/char/pcwd.c --- a/drivers/char/pcwd.c Tue Feb 19 18:08:57 2002 +++ b/drivers/char/pcwd.c Tue Feb 19 18:08:57 2002 @@ -238,7 +238,7 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int i, cdat, rv; + int cdat, rv; static struct watchdog_info ident= { WDIOF_OVERHEAT|WDIOF_CARDRESET, @@ -251,8 +251,9 @@ return -ENOTTY; case WDIOC_GETSUPPORT: - i = copy_to_user((void*)arg, &ident, sizeof(ident)); - return i ? -EFAULT : 0; + if(copy_to_user((void*)arg, &ident, sizeof(ident))) + return -EFAULT; + return 0; case WDIOC_GETSTATUS: spin_lock(&io_lock); @@ -575,7 +576,6 @@ printk("pcwd: v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER); /* Initial variables */ - set_bit( 0, &open_allowed ); supports_temp = 0; mode_debug = 0; temp_panic = 0; diff -Nru a/drivers/char/serial.c b/drivers/char/serial.c --- a/drivers/char/serial.c Tue Feb 19 18:08:59 2002 +++ b/drivers/char/serial.c Tue Feb 19 18:08:59 2002 @@ -4912,7 +4912,7 @@ static struct pci_driver serial_pci_driver = { name: "serial", probe: serial_init_one, - remove: serial_remove_one, + remove: __devexit_p(serial_remove_one), id_table: serial_pci_tbl, }; diff -Nru a/drivers/hotplug/pci_hotplug_core.c b/drivers/hotplug/pci_hotplug_core.c --- a/drivers/hotplug/pci_hotplug_core.c Tue Feb 19 18:08:58 2002 +++ b/drivers/hotplug/pci_hotplug_core.c Tue Feb 19 18:08:58 2002 @@ -94,6 +94,7 @@ return 0; } +/* SMP-safe */ static struct dentry *pcihpfs_lookup (struct inode *dir, struct dentry *dentry) { d_add(dentry, NULL); @@ -129,6 +130,7 @@ return inode; } +/* SMP-safe */ static int pcihpfs_mknod (struct inode *dir, struct dentry *dentry, int mode, int dev) { struct inode *inode = pcihpfs_get_inode(dir->i_sb, mode, dev); @@ -156,9 +158,6 @@ struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; - - if(S_ISDIR(inode->i_mode)) - return -EPERM; inode->i_nlink++; atomic_inc(&inode->i_count); diff -Nru a/drivers/ide/Makefile b/drivers/ide/Makefile --- a/drivers/ide/Makefile Tue Feb 19 18:08:57 2002 +++ b/drivers/ide/Makefile Tue Feb 19 18:08:57 2002 @@ -11,14 +11,14 @@ O_TARGET := idedriver.o export-objs := ide-taskfile.o ide.o ide-features.o ide-probe.o ataraid.o -list-multi := ide-mod.o ide-probe-mod.o +list-multi := ide-mod.o obj-y := obj-m := ide-obj-y := obj-$(CONFIG_BLK_DEV_HD) += hd.o -obj-$(CONFIG_BLK_DEV_IDE) += ide-mod.o ide-probe-mod.o +obj-$(CONFIG_BLK_DEV_IDE) += ide-mod.o obj-$(CONFIG_BLK_DEV_IDECS) += ide-cs.o obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk.o obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd.o @@ -76,13 +76,9 @@ ide-obj-$(CONFIG_PROC_FS) += ide-proc.o -ide-mod-objs := ide-taskfile.o ide.o ide-features.o $(ide-obj-y) -ide-probe-mod-objs := ide-probe.o ide-geometry.o +ide-mod-objs := ide-taskfile.o ide.o ide-probe.o ide-geometry.o ide-features.o $(ide-obj-y) include $(TOPDIR)/Rules.make ide-mod.o: $(ide-mod-objs) $(LD) -r -o $@ $(ide-mod-objs) - -ide-probe-mod.o: $(ide-probe-mod-objs) - $(LD) -r -o $@ $(ide-probe-mod-objs) diff -Nru a/drivers/ide/ataraid.c b/drivers/ide/ataraid.c --- a/drivers/ide/ataraid.c Tue Feb 19 18:08:59 2002 +++ b/drivers/ide/ataraid.c Tue Feb 19 18:08:59 2002 @@ -289,7 +289,6 @@ hardsect_size[ATAMAJOR] = NULL; blk_size[ATAMAJOR] = NULL; blksize_size[ATAMAJOR] = NULL; - max_readahead[ATAMAJOR] = NULL; del_gendisk(&ataraid_gendisk); diff -Nru a/drivers/ide/hd.c b/drivers/ide/hd.c --- a/drivers/ide/hd.c Tue Feb 19 18:08:58 2002 +++ b/drivers/ide/hd.c Tue Feb 19 18:08:58 2002 @@ -652,8 +652,6 @@ case BLKGETSIZE64: case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKFLSBUF: case BLKPG: return blk_ioctl(inode->i_rdev, cmd, arg); @@ -837,7 +835,6 @@ } blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &hd_lock); blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), 255); - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ add_gendisk(&hd_gendisk); init_timer(&device_timer); device_timer.function = hd_times_out; diff -Nru a/drivers/ide/icside.c b/drivers/ide/icside.c --- a/drivers/ide/icside.c Tue Feb 19 18:08:57 2002 +++ b/drivers/ide/icside.c Tue Feb 19 18:08:57 2002 @@ -341,7 +341,7 @@ rq = HWGROUP(drive)->rq; for (i = rq->nr_sectors; i > 0;) { i -= rq->current_nr_sectors; - ide_end_request(1, HWGROUP(drive)); + ide_end_request(drive, 1); } return ide_stopped; } diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c --- a/drivers/ide/ide-cd.c Tue Feb 19 18:08:59 2002 +++ b/drivers/ide/ide-cd.c Tue Feb 19 18:08:59 2002 @@ -540,7 +540,7 @@ } -static void cdrom_end_request (int uptodate, ide_drive_t *drive) +static void cdrom_end_request(ide_drive_t *drive, int uptodate) { struct request *rq = HWGROUP(drive)->rq; @@ -554,7 +554,7 @@ if ((rq->flags & REQ_CMD) && !rq->current_nr_sectors) uptodate = 1; - ide_end_request(uptodate, HWGROUP(drive)); + ide_end_request(drive, uptodate); } @@ -591,7 +591,7 @@ pc = (struct packet_command *) rq->special; pc->stat = 1; - cdrom_end_request (1, drive); + cdrom_end_request(drive, 1); *startstop = ide_error (drive, "request sense failure", stat); return 1; } else if (rq->flags & (REQ_PC | REQ_BLOCK_PC)) { @@ -627,7 +627,7 @@ } pc->stat = 1; - cdrom_end_request (1, drive); + cdrom_end_request(drive, 1); if ((stat & ERR_STAT) != 0) cdrom_queue_request_sense(drive, wait, pc->sense, pc); @@ -640,7 +640,7 @@ /* Fail the request. */ printk ("%s: tray open\n", drive->name); - cdrom_end_request (0, drive); + cdrom_end_request(drive, 0); } else if (sense_key == UNIT_ATTENTION) { /* Media change. */ cdrom_saw_media_change (drive); @@ -649,13 +649,13 @@ But be sure to give up if we've retried too many times. */ if (++rq->errors > ERROR_MAX) - cdrom_end_request (0, drive); + cdrom_end_request(drive, 0); } else if (sense_key == ILLEGAL_REQUEST || sense_key == DATA_PROTECT) { /* No point in retrying after an illegal request or data protect error.*/ ide_dump_status (drive, "command error", stat); - cdrom_end_request (0, drive); + cdrom_end_request(drive, 0); } else if ((err & ~ABRT_ERR) != 0) { /* Go to the default handler for other errors. */ @@ -663,7 +663,7 @@ return 1; } else if ((++rq->errors > ERROR_MAX)) { /* We've racked up too many retries. Abort. */ - cdrom_end_request (0, drive); + cdrom_end_request(drive, 0); } /* If we got a CHECK_CONDITION status, @@ -879,7 +879,7 @@ drive->name, ireason); } - cdrom_end_request (0, drive); + cdrom_end_request(drive, 0); return -1; } @@ -908,7 +908,7 @@ if (dma) { if (!dma_error) { - __ide_end_request(HWGROUP(drive), 1, rq->nr_sectors); + __ide_end_request(drive, 1, rq->nr_sectors); return ide_stopped; } else return ide_error (drive, "dma error", stat); @@ -925,9 +925,9 @@ if (rq->current_nr_sectors > 0) { printk ("%s: cdrom_read_intr: data underrun (%u blocks)\n", drive->name, rq->current_nr_sectors); - cdrom_end_request (0, drive); + cdrom_end_request(drive, 0); } else - cdrom_end_request (1, drive); + cdrom_end_request(drive, 1); return ide_stopped; } @@ -947,7 +947,7 @@ printk (" Trying to limit transfer sizes\n"); CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1; } - cdrom_end_request (0, drive); + cdrom_end_request(drive, 0); return ide_stopped; } @@ -975,7 +975,7 @@ /* If we've filled the present buffer but there's another chained buffer after it, move on. */ if (rq->current_nr_sectors == 0 && rq->nr_sectors) - cdrom_end_request (1, drive); + cdrom_end_request(drive, 1); /* If the buffers are full, cache the rest of the data in our internal buffer. */ @@ -1027,7 +1027,7 @@ rq->sector >= info->sector_buffered && rq->sector < info->sector_buffered + info->nsectors_buffered) { if (rq->current_nr_sectors == 0) - cdrom_end_request (1, drive); + cdrom_end_request(drive, 1); memcpy (rq->buffer, info->buffer + @@ -1042,13 +1042,13 @@ /* If we've satisfied the current request, terminate it successfully. */ if (rq->nr_sectors == 0) { - cdrom_end_request (1, drive); + cdrom_end_request(drive, 1); return -1; } /* Move on to the next buffer if needed. */ if (rq->current_nr_sectors == 0) - cdrom_end_request (1, drive); + cdrom_end_request(drive, 1); /* If this condition does not hold, then the kluge i use to represent the number of sectors to skip at the start of a transfer @@ -1058,7 +1058,7 @@ (rq->sector % SECTORS_PER_FRAME) != 0) { printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n", drive->name, rq->sector); - cdrom_end_request (0, drive); + cdrom_end_request(drive, 0); return -1; } @@ -1097,7 +1097,7 @@ (rq->sector % CD_FRAMESIZE != 0)) { printk ("%s: cdrom_start_read_continuation: buffer botch (%u)\n", drive->name, rq->current_nr_sectors); - cdrom_end_request (0, drive); + cdrom_end_request(drive, 0); return ide_stopped; } sector -= nskip; @@ -1273,17 +1273,17 @@ } if (pc->buflen == 0) - cdrom_end_request (1, drive); + cdrom_end_request(drive, 1); else { - /* Comment this out, because this always happens - right after a reset occurs, and it is annoying to + /* Comment this out, because this always happens + right after a reset occurs, and it is annoying to always print expected stuff. */ /* printk ("%s: cdrom_pc_intr: data underrun %d\n", drive->name, pc->buflen); */ pc->stat = 1; - cdrom_end_request (1, drive); + cdrom_end_request(drive, 1); } return ide_stopped; } @@ -1460,7 +1460,7 @@ drive->name, ireason); } - cdrom_end_request(0, drive); + cdrom_end_request(drive, 0); return 1; } @@ -1495,7 +1495,7 @@ return ide_error(drive, "dma error", stat); rq = HWGROUP(drive)->rq; - __ide_end_request(HWGROUP(drive), 1, rq->nr_sectors); + __ide_end_request(drive, 1, rq->nr_sectors); return ide_stopped; } @@ -1514,7 +1514,7 @@ drive->name, rq->current_nr_sectors); uptodate = 0; } - cdrom_end_request(uptodate, drive); + cdrom_end_request(drive, uptodate); return ide_stopped; } @@ -1555,7 +1555,7 @@ * current buffer complete, move on */ if (rq->current_nr_sectors == 0 && rq->nr_sectors) - cdrom_end_request (1, drive); + cdrom_end_request(drive, 1); } /* re-arm handler */ @@ -1589,7 +1589,7 @@ * writes *must* be 2kB frame aligned */ if ((rq->nr_sectors & 3) || (rq->sector & 3)) { - cdrom_end_request(0, drive); + cdrom_end_request(drive, 0); return ide_stopped; } @@ -1673,14 +1673,14 @@ /* * right now this can only be a reset... */ - cdrom_end_request(1, drive); + cdrom_end_request(drive, 1); return ide_do_reset(drive); } else if (rq->flags & REQ_BLOCK_PC) { return cdrom_do_block_pc(drive, rq); } blk_dump_rq_flags(rq, "ide-cd bad flags"); - cdrom_end_request(0, drive); + cdrom_end_request(drive, 0); return ide_stopped; } @@ -2659,11 +2659,6 @@ static void ide_cdrom_add_settings(ide_drive_t *drive) { - int major = HWIF(drive)->major; - int minor = drive->select.b.unit << PARTN_BITS; - - ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); - ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL); ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL); } @@ -2911,11 +2906,11 @@ return 0; } +int ide_cdrom_init(void); int ide_cdrom_reinit (ide_drive_t *drive); static ide_driver_t ide_cdrom_driver = { name: "ide-cdrom", - version: IDECD_VERSION, media: ide_cdrom, busy: 0, supports_dma: 1, @@ -2934,17 +2929,10 @@ capacity: ide_cdrom_capacity, special: NULL, proc: NULL, + driver_init: ide_cdrom_init, driver_reinit: ide_cdrom_reinit, }; -int ide_cdrom_init(void); -static ide_module_t ide_cdrom_module = { - IDE_DRIVER_MODULE, - ide_cdrom_init, - &ide_cdrom_driver, - NULL -}; - /* options */ char *ignore = NULL; @@ -2962,7 +2950,7 @@ printk ("%s: Can't allocate a cdrom structure\n", drive->name); return 1; } - if (ide_register_subdriver (drive, &ide_cdrom_driver, IDE_SUBDRIVER_VERSION)) { + if (ide_register_subdriver (drive, &ide_cdrom_driver)) { printk ("%s: Failed to register the driver with ide.c\n", drive->name); kfree (info); return 1; @@ -2979,7 +2967,7 @@ DRIVER(drive)->busy--; failed--; - ide_register_module(&ide_cdrom_module); + ide_register_module(&ide_cdrom_driver); MOD_DEC_USE_COUNT; return 0; } @@ -2994,7 +2982,7 @@ printk ("%s: cleanup_module() called while still busy\n", drive->name); failed++; } - ide_unregister_module (&ide_cdrom_module); + ide_unregister_module (&ide_cdrom_driver); } int ide_cdrom_init(void) @@ -3021,7 +3009,7 @@ printk ("%s: Can't allocate a cdrom structure\n", drive->name); continue; } - if (ide_register_subdriver (drive, &ide_cdrom_driver, IDE_SUBDRIVER_VERSION)) { + if (ide_register_subdriver (drive, &ide_cdrom_driver)) { printk ("%s: Failed to register the driver with ide.c\n", drive->name); kfree (info); continue; @@ -3038,7 +3026,7 @@ DRIVER(drive)->busy--; failed--; } - ide_register_module(&ide_cdrom_module); + ide_register_module(&ide_cdrom_driver); MOD_DEC_USE_COUNT; return 0; } diff -Nru a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c --- a/drivers/ide/ide-disk.c Tue Feb 19 18:08:57 2002 +++ b/drivers/ide/ide-disk.c Tue Feb 19 18:08:57 2002 @@ -123,29 +123,24 @@ */ static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) { - if (rq->flags & REQ_CMD) - goto good_command; - - blk_dump_rq_flags(rq, "do_rw_disk, bad command"); - ide_end_request(0, HWGROUP(drive)); - return ide_stopped; - -good_command: + if (!(rq->flags & REQ_CMD)) { + blk_dump_rq_flags(rq, "do_rw_disk, bad command"); + ide_end_request(drive, 0); + return ide_stopped; + } -#ifdef CONFIG_BLK_DEV_PDC4030 if (IS_PDC4030_DRIVE) { extern ide_startstop_t promise_rw_disk(ide_drive_t *, struct request *, unsigned long); return promise_rw_disk(drive, rq, block); } -#endif /* CONFIG_BLK_DEV_PDC4030 */ if ((drive->id->cfs_enable_2 & 0x0400) && (drive->addressing)) /* 48-bit LBA */ - return lba_48_rw_disk(drive, rq, (unsigned long long) block); + return lba_48_rw_disk(drive, rq, block); if (drive->select.b.lba) /* 28-bit LBA */ - return lba_28_rw_disk(drive, rq, (unsigned long) block); + return lba_28_rw_disk(drive, rq, block); /* 28-bit CHS : DIE DIE DIE piece of legacy crap!!! */ - return chs_rw_disk(drive, rq, (unsigned long) block); + return chs_rw_disk(drive, rq, block); } static task_ioreg_t get_command (ide_drive_t *drive, int cmd) @@ -329,7 +324,6 @@ args.posthandler = NULL; args.rq = (struct request *) rq; args.block = block; - rq->special = NULL; rq->special = (ide_task_t *)&args; return do_rw_taskfile(drive, &args); @@ -906,8 +900,6 @@ static void idedisk_add_settings(ide_drive_t *drive) { struct hd_driveid *id = drive->id; - int major = HWIF(drive)->major; - int minor = drive->select.b.unit << PARTN_BITS; ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->bios_cyl, NULL); ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); @@ -916,8 +908,6 @@ ide_add_setting(drive, "bswap", SETTING_READ, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->bswap, NULL); ide_add_setting(drive, "multcount", id ? SETTING_RW : SETTING_READ, HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, TYPE_BYTE, 0, id ? id->max_multsect : 0, 1, 1, &drive->mult_count, set_multcount); ide_add_setting(drive, "nowerr", SETTING_RW, HDIO_GET_NOWERR, HDIO_SET_NOWERR, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr); - ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 1, &read_ahead[major], NULL); - ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, 4096, PAGE_SIZE, 1024, &max_readahead[major][minor], NULL); ide_add_setting(drive, "lun", SETTING_RW, -1, -1, TYPE_INT, 0, 7, 1, 1, &drive->lun, NULL); ide_add_setting(drive, "wcache", SETTING_RW, HDIO_GET_WCACHE, HDIO_SET_WCACHE, TYPE_BYTE, 0, 1, 1, 1, &drive->wcache, write_cache); ide_add_setting(drive, "acoustic", SETTING_RW, HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, TYPE_BYTE, 0, 254, 1, 1, &drive->acoustic, set_acoustic); @@ -1040,6 +1030,7 @@ return ide_unregister_subdriver(drive); } +int idedisk_init (void); int idedisk_reinit(ide_drive_t *drive); /* @@ -1047,7 +1038,6 @@ */ static ide_driver_t idedisk_driver = { name: "ide-disk", - version: IDEDISK_VERSION, media: ide_disk, busy: 0, supports_dma: 1, @@ -1066,17 +1056,10 @@ capacity: idedisk_capacity, special: idedisk_special, proc: idedisk_proc, + driver_init: idedisk_init, driver_reinit: idedisk_reinit, }; -int idedisk_init (void); -static ide_module_t idedisk_module = { - IDE_DRIVER_MODULE, - idedisk_init, - &idedisk_driver, - NULL -}; - MODULE_DESCRIPTION("ATA DISK Driver"); int idedisk_reinit (ide_drive_t *drive) @@ -1085,7 +1068,7 @@ MOD_INC_USE_COUNT; - if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) { + if (ide_register_subdriver (drive, &idedisk_driver)) { printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); return 1; } @@ -1100,7 +1083,7 @@ DRIVER(drive)->busy--; failed--; - ide_register_module(&idedisk_module); + ide_register_module(&idedisk_driver); MOD_DEC_USE_COUNT; return 0; } @@ -1122,7 +1105,7 @@ ide_remove_proc_entries(drive->proc, idedisk_proc); #endif } - ide_unregister_module(&idedisk_module); + ide_unregister_module(&idedisk_driver); } int idedisk_init (void) @@ -1132,7 +1115,7 @@ MOD_INC_USE_COUNT; while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) { - if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) { + if (ide_register_subdriver (drive, &idedisk_driver)) { printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); continue; } @@ -1147,7 +1130,7 @@ DRIVER(drive)->busy--; failed--; } - ide_register_module(&idedisk_module); + ide_register_module(&idedisk_driver); MOD_DEC_USE_COUNT; return 0; } diff -Nru a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c --- a/drivers/ide/ide-dma.c Tue Feb 19 18:08:58 2002 +++ b/drivers/ide/ide-dma.c Tue Feb 19 18:08:58 2002 @@ -215,7 +215,7 @@ if (!dma_stat) { struct request *rq = HWGROUP(drive)->rq; - __ide_end_request(HWGROUP(drive), 1, rq->nr_sectors); + __ide_end_request(drive, 1, rq->nr_sectors); return ide_stopped; } printk("%s: dma_intr: bad DMA status (dma_stat=%x)\n", @@ -553,7 +553,7 @@ static void ide_toggle_bounce(ide_drive_t *drive, int on) { - dma64_addr_t addr = BLK_BOUNCE_HIGH; + u64 addr = BLK_BOUNCE_HIGH; if (on && drive->media == ide_disk && HWIF(drive)->highmem) { if (!PCI_DMA_BUS_IS_PHYS) diff -Nru a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c --- a/drivers/ide/ide-floppy.c Tue Feb 19 18:08:58 2002 +++ b/drivers/ide/ide-floppy.c Tue Feb 19 18:08:58 2002 @@ -667,11 +667,10 @@ * For read/write requests, we will call ide_end_request to pass to the * next buffer. */ -static void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup) +static int idefloppy_end_request(ide_drive_t *drive, int uptodate) { - ide_drive_t *drive = hwgroup->drive; idefloppy_floppy_t *floppy = drive->driver_data; - struct request *rq = hwgroup->rq; + struct request *rq = HWGROUP(drive)->rq; int error; #if IDEFLOPPY_DEBUG_LOG @@ -687,13 +686,15 @@ floppy->failed_pc = NULL; /* Why does this happen? */ if (!rq) - return; + return 0; if (!(rq->flags & IDEFLOPPY_RQ)) { - ide_end_request (uptodate, hwgroup); - return; + ide_end_request(drive, uptodate); + return 0; } rq->errors = error; ide_end_drive_cmd (drive, 0, 0); + + return 0; } static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount) @@ -706,7 +707,7 @@ if (pc->b_count == bio->bi_size) { rq->sector += rq->current_nr_sectors; rq->nr_sectors -= rq->current_nr_sectors; - idefloppy_end_request (1, HWGROUP(drive)); + idefloppy_end_request(drive, 1); if ((bio = rq->bio) != NULL) pc->b_count = 0; } @@ -731,7 +732,7 @@ if (!pc->b_count) { rq->sector += rq->current_nr_sectors; rq->nr_sectors -= rq->current_nr_sectors; - idefloppy_end_request (1, HWGROUP(drive)); + idefloppy_end_request(drive, 1); if ((bio = rq->bio) != NULL) { pc->b_data = bio_data(bio); pc->b_count = bio->bi_size; @@ -755,7 +756,7 @@ struct bio *bio = rq->bio; while ((bio = rq->bio) != NULL) - idefloppy_end_request (1, HWGROUP(drive)); + idefloppy_end_request(drive, 1); } #endif /* CONFIG_BLK_DEV_IDEDMA */ @@ -818,10 +819,10 @@ #endif /* IDEFLOPPY_DEBUG_LOG */ if (!floppy->pc->error) { idefloppy_analyze_error (drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer); - idefloppy_end_request (1,HWGROUP (drive)); + idefloppy_end_request(drive, 1); } else { printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n"); - idefloppy_end_request (0,HWGROUP (drive)); + idefloppy_end_request(drive, 0); } } @@ -836,7 +837,7 @@ printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_callback\n"); #endif /* IDEFLOPPY_DEBUG_LOG */ - idefloppy_end_request (floppy->pc->error ? 0:1, HWGROUP(drive)); + idefloppy_end_request(drive, floppy->pc->error ? 0:1); } /* @@ -1159,7 +1160,7 @@ printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n"); #endif /* IDEFLOPPY_DEBUG_LOG */ - idefloppy_end_request(1, HWGROUP(drive)); + idefloppy_end_request(drive, 1); return; } @@ -1293,13 +1294,13 @@ drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq); else printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name); - idefloppy_end_request (0, HWGROUP(drive)); + idefloppy_end_request(drive, 0); return ide_stopped; } if (rq->flags & REQ_CMD) { if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) { printk ("%s: unsupported r/w request size\n", drive->name); - idefloppy_end_request (0, HWGROUP(drive)); + idefloppy_end_request(drive, 0); return ide_stopped; } pc = idefloppy_next_pc_storage (drive); @@ -1308,7 +1309,7 @@ pc = (idefloppy_pc_t *) rq->buffer; } else { blk_dump_rq_flags(rq, "ide-floppy: unsupported command in queue"); - idefloppy_end_request (0,HWGROUP (drive)); + idefloppy_end_request(drive, 0); return ide_stopped; } pc->rq = rq; @@ -1962,14 +1963,9 @@ static void idefloppy_add_settings(ide_drive_t *drive) { - int major = HWIF(drive)->major; - int minor = drive->select.b.unit << PARTN_BITS; - ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); - ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); - ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL); } @@ -2050,6 +2046,7 @@ #endif /* CONFIG_PROC_FS */ +int idefloppy_init(void); int idefloppy_reinit(ide_drive_t *drive); /* @@ -2057,7 +2054,6 @@ */ static ide_driver_t idefloppy_driver = { name: "ide-floppy", - version: IDEFLOPPY_VERSION, media: ide_floppy, busy: 0, supports_dma: 1, @@ -2076,17 +2072,10 @@ capacity: idefloppy_capacity, special: NULL, proc: idefloppy_proc, + driver_init: idefloppy_init, driver_reinit: idefloppy_reinit, }; -int idefloppy_init (void); -static ide_module_t idefloppy_module = { - IDE_DRIVER_MODULE, - idefloppy_init, - &idefloppy_driver, - NULL -}; - int idefloppy_reinit (ide_drive_t *drive) { idefloppy_floppy_t *floppy; @@ -2106,7 +2095,7 @@ printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name); continue; } - if (ide_register_subdriver (drive, &idefloppy_driver, IDE_SUBDRIVER_VERSION)) { + if (ide_register_subdriver (drive, &idefloppy_driver)) { printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name); kfree (floppy); continue; @@ -2116,7 +2105,7 @@ DRIVER(drive)->busy--; failed--; } - ide_register_module(&idefloppy_module); + ide_register_module(&idefloppy_driver); MOD_DEC_USE_COUNT; return 0; } @@ -2141,7 +2130,7 @@ ide_remove_proc_entries(drive->proc, idefloppy_proc); #endif } - ide_unregister_module(&idefloppy_module); + ide_unregister_module(&idefloppy_driver); } /* @@ -2168,7 +2157,7 @@ printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name); continue; } - if (ide_register_subdriver (drive, &idefloppy_driver, IDE_SUBDRIVER_VERSION)) { + if (ide_register_subdriver (drive, &idefloppy_driver)) { printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name); kfree (floppy); continue; @@ -2178,7 +2167,7 @@ DRIVER(drive)->busy--; failed--; } - ide_register_module(&idefloppy_module); + ide_register_module(&idefloppy_driver); MOD_DEC_USE_COUNT; return 0; } diff -Nru a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c --- a/drivers/ide/ide-probe.c Tue Feb 19 18:08:57 2002 +++ b/drivers/ide/ide-probe.c Tue Feb 19 18:08:57 2002 @@ -406,22 +406,19 @@ } /* - * probe_for_drive() tests for existence of a given drive using do_probe(). - * - * Returns: 0 no device was found - * 1 device was found (note: drive->present might still be 0) + * Tests for existence of a given drive using do_probe(). */ -static inline byte probe_for_drive (ide_drive_t *drive) +static inline void probe_for_drive (ide_drive_t *drive) { if (drive->noprobe) /* skip probing? */ - return drive->present; + return; if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */ - (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */ + do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */ } if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T")) enable_nest(drive); if (!drive->present) - return 0; /* drive not found */ + return; /* drive not found */ if (drive->id == NULL) { /* identification failed? */ if (drive->media == ide_disk) { printk ("%s: non-IDE drive, CHS=%d/%d/%d\n", @@ -432,7 +429,6 @@ drive->present = 0; /* nuke it */ } } - return 1; /* drive was found */ } /* @@ -548,7 +544,7 @@ */ for (unit = 0; unit < MAX_DRIVES; ++unit) { ide_drive_t *drive = &hwif->drives[unit]; - (void) probe_for_drive (drive); + probe_for_drive (drive); if (drive->present && !hwif->present) { hwif->present = 1; if (hwif->chipset != ide_4drives || !hwif->mate->present) { @@ -788,8 +784,7 @@ static void init_gendisk (ide_hwif_t *hwif) { struct gendisk *gd; - unsigned int unit, units, minors; - int *bs, *max_ra; + unsigned int unit, units, minors, i; extern devfs_handle_t ide_devfs_handle; #if 1 @@ -802,33 +797,25 @@ } #endif - minors = units * (1<sizes = kmalloc (minors * sizeof(int), GFP_KERNEL); if (!gd->sizes) goto err_kmalloc_gd_sizes; - gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL); + gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL); if (!gd->part) goto err_kmalloc_gd_part; - bs = kmalloc (minors*sizeof(int), GFP_KERNEL); - if (!bs) + blksize_size[hwif->major] = kmalloc (minors*sizeof(int), GFP_KERNEL); + if (!blksize_size[hwif->major]) goto err_kmalloc_bs; - max_ra = kmalloc (minors*sizeof(int), GFP_KERNEL); - if (!max_ra) - goto err_kmalloc_max_ra; memset(gd->part, 0, minors * sizeof(struct hd_struct)); - /* cdroms and msdos f/s are examples of non-1024 blocksizes */ - blksize_size[hwif->major] = bs; - max_readahead[hwif->major] = max_ra; - for (unit = 0; unit < minors; ++unit) { - *bs++ = BLOCK_SIZE; - *max_ra++ = MAX_READAHEAD; - } - + for (i = 0; i < minors; ++i) + blksize_size[hwif->major][i] = BLOCK_SIZE; for (unit = 0; unit < units; ++unit) hwif->drives[unit].part = &gd->part[unit << PARTN_BITS]; @@ -875,8 +862,6 @@ } return; -err_kmalloc_max_ra: - kfree(bs); err_kmalloc_bs: kfree(gd->part); err_kmalloc_gd_part: @@ -937,38 +922,16 @@ init_gendisk(hwif); blk_dev[hwif->major].data = hwif; blk_dev[hwif->major].queue = ide_get_queue; - read_ahead[hwif->major] = 8; /* (4kB) */ hwif->present = 1; /* success */ return hwif->present; } -void export_ide_init_queue (ide_drive_t *drive) -{ - ide_init_queue(drive); -} - -byte export_probe_for_drive (ide_drive_t *drive) -{ - return probe_for_drive(drive); -} - -EXPORT_SYMBOL(export_ide_init_queue); -EXPORT_SYMBOL(export_probe_for_drive); - -int ideprobe_init (void); -static ide_module_t ideprobe_module = { - IDE_PROBE_MODULE, - ideprobe_init, - NULL -}; - int ideprobe_init (void) { unsigned int index; int probe[MAX_HWIFS]; - MOD_INC_USE_COUNT; memset(probe, 0, MAX_HWIFS * sizeof(int)); for (index = 0; index < MAX_HWIFS; ++index) probe[index] = !ide_hwifs[index].present; @@ -982,31 +945,5 @@ for (index = 0; index < MAX_HWIFS; ++index) if (probe[index]) hwif_init(&ide_hwifs[index]); - if (!ide_probe) - ide_probe = &ideprobe_module; - MOD_DEC_USE_COUNT; - return 0; -} - -#ifdef MODULE -extern int (*ide_xlate_1024_hook)(kdev_t, int, int, const char *); - -int init_module (void) -{ - unsigned int index; - - for (index = 0; index < MAX_HWIFS; ++index) - ide_unregister(index); - ideprobe_init(); - create_proc_ide_interfaces(); - ide_xlate_1024_hook = ide_xlate_1024; return 0; } - -void cleanup_module (void) -{ - ide_probe = NULL; - ide_xlate_1024_hook = 0; -} -MODULE_LICENSE("GPL"); -#endif /* MODULE */ diff -Nru a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c --- a/drivers/ide/ide-proc.c Tue Feb 19 18:08:57 2002 +++ b/drivers/ide/ide-proc.c Tue Feb 19 18:08:57 2002 @@ -163,7 +163,7 @@ static int proc_ide_write_config (struct file *file, const char *buffer, unsigned long count, void *data) { - ide_hwif_t *hwif = (ide_hwif_t *)data; + ide_hwif_t *hwif = data; int for_real = 0; unsigned long startn = 0, n, flags; const char *start = NULL, *msg = NULL; @@ -338,7 +338,7 @@ int len; #ifdef CONFIG_BLK_DEV_IDEPCI - ide_hwif_t *hwif = (ide_hwif_t *)data; + ide_hwif_t *hwif = data; struct pci_dev *dev = hwif->pci_dev; if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL) && dev && dev->bus) { int reg = 0; @@ -378,14 +378,10 @@ { char *out = page; int len; - ide_module_t *p = ide_modules; - ide_driver_t *driver; + struct ide_driver_s * driver; - while (p) { - driver = (ide_driver_t *) p->info; - if (driver) - out += sprintf(out, "%s version %s\n", driver->name, driver->version); - p = p->next; + for (driver = ide_drivers; driver; driver = driver->next) { + out += sprintf(out, "%s\n",driver->name); } len = out - page; PROC_IDE_READ_RETURN(page,start,off,count,eof,len); @@ -394,7 +390,7 @@ static int proc_ide_read_imodel (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_hwif_t *hwif = (ide_hwif_t *) data; + ide_hwif_t *hwif = data; int len; const char *name; @@ -424,7 +420,7 @@ static int proc_ide_read_mate (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_hwif_t *hwif = (ide_hwif_t *) data; + ide_hwif_t *hwif = data; int len; if (hwif && hwif->mate && hwif->mate->present) @@ -437,7 +433,7 @@ static int proc_ide_read_channel (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_hwif_t *hwif = (ide_hwif_t *) data; + ide_hwif_t *hwif = data; int len; page[0] = hwif->channel ? '1' : '0'; @@ -462,7 +458,7 @@ static int proc_ide_read_identify (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_drive_t *drive = (ide_drive_t *)data; + ide_drive_t *drive = data; int len = 0, i = 0; if (drive && !proc_ide_get_identify(drive, page)) { @@ -483,8 +479,8 @@ static int proc_ide_read_settings (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_drive_t *drive = (ide_drive_t *) data; - ide_settings_t *setting = (ide_settings_t *) drive->settings; + ide_drive_t *drive = data; + ide_settings_t *setting = drive->settings; char *out = page; int len, rc, mul_factor, div_factor; @@ -515,7 +511,7 @@ static int proc_ide_write_settings (struct file *file, const char *buffer, unsigned long count, void *data) { - ide_drive_t *drive = (ide_drive_t *) data; + ide_drive_t *drive = data; char name[MAX_LEN + 1]; int for_real = 0, len; unsigned long n; @@ -573,7 +569,15 @@ --n; ++p; } - setting = ide_find_setting_by_name(drive, name); + + /* Find setting by name */ + setting = drive->settings; + + while (setting) { + if (strcmp(setting->name, name) == 0) + break; + setting = setting->next; + } if (!setting) goto parse_error; @@ -590,21 +594,21 @@ int proc_ide_read_capacity (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_drive_t *drive = (ide_drive_t *) data; - ide_driver_t *driver = (ide_driver_t *) drive->driver; + ide_drive_t *drive = data; + ide_driver_t *driver = drive->driver; int len; if (!driver) len = sprintf(page, "(none)\n"); else - len = sprintf(page,"%llu\n", (unsigned long long) ((ide_driver_t *)drive->driver)->capacity(drive)); + len = sprintf(page,"%llu\n", (unsigned long long) drive->driver->capacity(drive)); PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } int proc_ide_read_geometry (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_drive_t *drive = (ide_drive_t *) data; + ide_drive_t *drive = data; char *out = page; int len; @@ -617,7 +621,7 @@ static int proc_ide_read_dmodel (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_drive_t *drive = (ide_drive_t *) data; + ide_drive_t *drive = data; struct hd_driveid *id = drive->id; int len; @@ -629,20 +633,20 @@ (char *page, char **start, off_t off, int count, int *eof, void *data) { ide_drive_t *drive = (ide_drive_t *) data; - ide_driver_t *driver = (ide_driver_t *) drive->driver; + ide_driver_t *driver = drive->driver; int len; if (!driver) len = sprintf(page, "(none)\n"); else - len = sprintf(page, "%s version %s\n", driver->name, driver->version); + len = sprintf(page, "%s\n", driver->name); PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } static int proc_ide_write_driver (struct file *file, const char *buffer, unsigned long count, void *data) { - ide_drive_t *drive = (ide_drive_t *) data; + ide_drive_t *drive = data; if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -654,7 +658,7 @@ static int proc_ide_read_media (char *page, char **start, off_t off, int count, int *eof, void *data) { - ide_drive_t *drive = (ide_drive_t *) data; + ide_drive_t *drive = data; const char *media; int len; @@ -746,7 +750,6 @@ struct proc_dir_entry *ent; struct proc_dir_entry *parent = hwif->proc; char name[64]; -// ide_driver_t *driver = drive->driver; if (drive->present && !drive->proc) { drive->proc = proc_mkdir(drive->name, parent); @@ -788,7 +791,6 @@ for (d = 0; d < MAX_DRIVES; d++) { ide_drive_t *drive = &hwif->drives[d]; -// ide_driver_t *driver = drive->driver; if (drive->proc) destroy_proc_ide_device(hwif, drive); diff -Nru a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c --- a/drivers/ide/ide-tape.c Tue Feb 19 18:08:58 2002 +++ b/drivers/ide/ide-tape.c Tue Feb 19 18:08:58 2002 @@ -1835,10 +1835,9 @@ * idetape_end_request is used to finish servicing a request, and to * insert a pending pipeline request into the main device queue. */ -static void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup) +static int idetape_end_request(ide_drive_t *drive, int uptodate) { - ide_drive_t *drive = hwgroup->drive; - struct request *rq = hwgroup->rq; + struct request *rq = HWGROUP(drive)->rq; idetape_tape_t *tape = drive->driver_data; unsigned long flags; int error; @@ -1932,6 +1931,8 @@ if (tape->active_data_request == NULL) clear_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags); spin_unlock_irqrestore(&tape->spinlock, flags); + + return 0; } static ide_startstop_t idetape_request_sense_callback (ide_drive_t *drive) @@ -1944,10 +1945,10 @@ #endif /* IDETAPE_DEBUG_LOG */ if (!tape->pc->error) { idetape_analyze_error (drive, (idetape_request_sense_result_t *) tape->pc->buffer); - idetape_end_request (1, HWGROUP (drive)); + idetape_end_request(drive, 1); } else { printk (KERN_ERR "ide-tape: Error in REQUEST SENSE itself - Aborting request!\n"); - idetape_end_request (0, HWGROUP (drive)); + idetape_end_request(drive, 0); } return ide_stopped; } @@ -2012,7 +2013,7 @@ /* * idetape_postpone_request postpones the current request so that * ide.c will be able to service requests from another device on - * the same hwgroup while we are polling for DSC. + * the same interface while we are polling for DSC. */ static void idetape_postpone_request (ide_drive_t *drive) { @@ -2348,7 +2349,7 @@ printk (KERN_INFO "ide-tape: Reached idetape_pc_callback\n"); #endif /* IDETAPE_DEBUG_LOG */ - idetape_end_request (tape->pc->error ? 0 : 1, HWGROUP(drive)); + idetape_end_request(drive, tape->pc->error ? 0 : 1); return ide_stopped; } @@ -2397,7 +2398,7 @@ if (tape->debug_level >= 1) printk(KERN_INFO "ide-tape: buffer fill callback, %d/%d\n", tape->cur_frames, tape->max_frames); #endif - idetape_end_request (tape->pc->error ? 0 : 1, HWGROUP(drive)); + idetape_end_request(drive, tape->pc->error ? 0 : 1); return ide_stopped; } @@ -2508,7 +2509,7 @@ tape->avg_time = jiffies; } -#if IDETAPE_DEBUG_LOG +#if IDETAPE_DEBUG_LOG if (tape->debug_level >= 4) printk (KERN_INFO "ide-tape: Reached idetape_rw_callback\n"); #endif /* IDETAPE_DEBUG_LOG */ @@ -2517,9 +2518,9 @@ rq->current_nr_sectors -= blocks; if (!tape->pc->error) - idetape_end_request (1, HWGROUP (drive)); + idetape_end_request(drive, 1); else - idetape_end_request (tape->pc->error, HWGROUP (drive)); + idetape_end_request(drive, tape->pc->error); return ide_stopped; } @@ -2630,7 +2631,7 @@ * We do not support buffer cache originated requests. */ printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue (%ld)\n", drive->name, rq->flags); - ide_end_request (0, HWGROUP (drive)); /* Let the common code handle it */ + ide_end_request(drive, 0); /* Let the common code handle it */ return ide_stopped; } @@ -2644,7 +2645,7 @@ if (postponed_rq != NULL) if (rq != postponed_rq) { printk (KERN_ERR "ide-tape: ide-tape.c bug - Two DSC requests were queued\n"); - idetape_end_request (0, HWGROUP (drive)); + idetape_end_request(drive, 0); return ide_stopped; } #endif /* IDETAPE_DEBUG_BUGS */ @@ -2772,7 +2773,7 @@ break; case IDETAPE_ABORTED_WRITE_RQ: rq->flags = IDETAPE_WRITE_RQ; - idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive)); + idetape_end_request(drive, IDETAPE_ERROR_EOD); return ide_stopped; case IDETAPE_ABORTED_READ_RQ: #if IDETAPE_DEBUG_LOG @@ -2780,7 +2781,7 @@ printk(KERN_INFO "ide-tape: %s: detected aborted read rq\n", tape->name); #endif rq->flags = IDETAPE_READ_RQ; - idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive)); + idetape_end_request(drive, IDETAPE_ERROR_EOD); return ide_stopped; case IDETAPE_PC_RQ1: pc = (idetape_pc_t *) rq->buffer; @@ -2791,7 +2792,7 @@ return ide_stopped; default: printk (KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n"); - idetape_end_request (0, HWGROUP (drive)); + idetape_end_request(drive, 0); return ide_stopped; } return idetape_issue_packet_command (drive, pc); @@ -3118,7 +3119,7 @@ if (result->bpu) { printk (KERN_INFO "ide-tape: Block location is unknown to the tape\n"); clear_bit (IDETAPE_ADDRESS_VALID, &tape->flags); - idetape_end_request (0, HWGROUP (drive)); + idetape_end_request(drive, 0); } else { #if IDETAPE_DEBUG_LOG if (tape->debug_level >= 2) @@ -3129,10 +3130,10 @@ tape->last_frame_position = ntohl (result->last_block); tape->blocks_in_buffer = result->blocks_in_buffer[2]; set_bit (IDETAPE_ADDRESS_VALID, &tape->flags); - idetape_end_request (1, HWGROUP (drive)); + idetape_end_request(drive, 1); } } else { - idetape_end_request (0, HWGROUP (drive)); + idetape_end_request(drive, 0); } return ide_stopped; } @@ -6136,6 +6137,7 @@ #endif +int idetape_init (void); int idetape_reinit(ide_drive_t *drive); /* @@ -6143,7 +6145,6 @@ */ static ide_driver_t idetape_driver = { name: "ide-tape", - version: IDETAPE_VERSION, media: ide_tape, busy: 1, supports_dma: 1, @@ -6161,17 +6162,10 @@ pre_reset: idetape_pre_reset, capacity: NULL, proc: idetape_proc, + driver_init: idetape_init, driver_reinit: idetape_reinit, }; -int idetape_init (void); -static ide_module_t idetape_module = { - IDE_DRIVER_MODULE, - idetape_init, - &idetape_driver, - NULL -}; - /* * Our character device supporting functions, passed to register_chrdev. */ @@ -6233,7 +6227,7 @@ printk (KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name); continue; } - if (ide_register_subdriver (drive, &idetape_driver, IDE_SUBDRIVER_VERSION)) { + if (ide_register_subdriver (drive, &idetape_driver)) { printk (KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name); kfree (tape); continue; @@ -6283,7 +6277,7 @@ if (drive != NULL && idetape_cleanup (drive)) printk (KERN_ERR "ide-tape: %s: cleanup_module() called while still busy\n", drive->name); } - ide_unregister_module(&idetape_module); + ide_unregister_module(&idetape_driver); } /* @@ -6304,7 +6298,7 @@ idetape_chrdevs[minor].drive = NULL; if ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) == NULL) { - ide_register_module (&idetape_module); + ide_register_module (&idetape_driver); MOD_DEC_USE_COUNT; #if ONSTREAM_DEBUG printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); @@ -6338,7 +6332,7 @@ printk (KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name); continue; } - if (ide_register_subdriver (drive, &idetape_driver, IDE_SUBDRIVER_VERSION)) { + if (ide_register_subdriver (drive, &idetape_driver)) { printk (KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name); kfree (tape); continue; @@ -6363,7 +6357,7 @@ devfs_unregister_chrdev (IDETAPE_MAJOR, "ht"); } else idetape_chrdev_present = 1; - ide_register_module (&idetape_module); + ide_register_module (&idetape_driver); MOD_DEC_USE_COUNT; #if ONSTREAM_DEBUG printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); diff -Nru a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c --- a/drivers/ide/ide-taskfile.c Tue Feb 19 18:09:00 2002 +++ b/drivers/ide/ide-taskfile.c Tue Feb 19 18:09:00 2002 @@ -632,7 +632,7 @@ if (drive->driver != NULL) DRIVER(drive)->end_request(0, HWGROUP(drive)); else - ide_end_request(0, HWGROUP(drive)); + ide_end_request(drive, 0); } else { if ((rq->errors & ERROR_RESET) == ERROR_RESET) { ++rq->errors; @@ -748,7 +748,7 @@ if (--rq->current_nr_sectors <= 0) { /* (hs): swapped next 2 lines */ DTF("Request Ended stat: %02x\n", GET_STAT()); - if (ide_end_request(1, HWGROUP(drive))) { + if (ide_end_request(drive, 1)) { ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL); return ide_started; } @@ -851,7 +851,7 @@ rq->current_nr_sectors -= nsect; stat = altstat_multi_poll(drive, GET_ALTSTAT(), "read"); } - ide_end_request(1, HWGROUP(drive)); + ide_end_request(drive, 1); return ide_stopped; } #endif /* ALTSTAT_SCREW_UP */ @@ -873,7 +873,7 @@ rq->current_nr_sectors -= nsect; msect -= nsect; if (!rq->current_nr_sectors) { - if (!ide_end_request(1, HWGROUP(drive))) + if (!ide_end_request(drive, 1)) return ide_stopped; } } while (msect); @@ -941,7 +941,7 @@ return ide_error(drive, "task_out_intr", stat); if (!rq->current_nr_sectors) - if (!ide_end_request(1, HWGROUP(drive))) + if (!ide_end_request(drive, 1)) return ide_stopped; if ((rq->current_nr_sectors==1) ^ (stat & DRQ_STAT)) { @@ -993,7 +993,7 @@ * there may be more, ide_do_request will restart it if * necessary */ - ide_end_request(1, HWGROUP(drive)); + ide_end_request(drive, 1); return ide_stopped; } @@ -1028,7 +1028,7 @@ rq->current_nr_sectors -= nsect; stat = altstat_multi_poll(drive, GET_ALTSTAT(), "write"); } - ide_end_request(1, HWGROUP(drive)); + ide_end_request(drive, 1); return ide_stopped; } #endif /* ALTSTAT_SCREW_UP */ @@ -1109,7 +1109,7 @@ return startstop; } - __ide_end_request(HWGROUP(drive), 1, rq->hard_nr_sectors); + __ide_end_request(drive, 1, rq->hard_nr_sectors); HWGROUP(drive)->wrq.bio = NULL; return ide_stopped; } diff -Nru a/drivers/ide/ide.c b/drivers/ide/ide.c --- a/drivers/ide/ide.c Tue Feb 19 18:08:59 2002 +++ b/drivers/ide/ide.c Tue Feb 19 18:08:59 2002 @@ -196,10 +196,9 @@ int noautodma = 0; /* - * ide_modules keeps track of the available IDE chipset/probe/driver modules. + * This is the anchor of the single linked list of ide device type drivers. */ -ide_module_t *ide_modules; -ide_module_t *ide_probe; +struct ide_driver_s *ide_drivers; /* * This is declared extern in ide.h, for access by other IDE modules: @@ -372,15 +371,14 @@ return system_bus_speed; } -inline int __ide_end_request(ide_hwgroup_t *hwgroup, int uptodate, int nr_secs) +int __ide_end_request(ide_drive_t *drive, int uptodate, int nr_secs) { - ide_drive_t *drive = hwgroup->drive; struct request *rq; unsigned long flags; int ret = 1; spin_lock_irqsave(&ide_lock, flags); - rq = hwgroup->rq; + rq = HWGROUP(drive)->rq; BUG_ON(!(rq->flags & REQ_STARTED)); @@ -397,13 +395,13 @@ */ if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) { drive->state = 0; - hwgroup->hwif->dmaproc(ide_dma_on, drive); + HWGROUP(drive)->hwif->dmaproc(ide_dma_on, drive); } if (!end_that_request_first(rq, uptodate, nr_secs)) { add_blkdev_randomness(major(rq->rq_dev)); blkdev_dequeue_request(rq); - hwgroup->rq = NULL; + HWGROUP(drive)->rq = NULL; end_that_request_last(rq); ret = 0; } @@ -413,14 +411,6 @@ } /* - * This is our end_request replacement function. - */ -int ide_end_request (byte uptodate, ide_hwgroup_t *hwgroup) -{ - return __ide_end_request(hwgroup, uptodate, 0); -} - -/* * This should get invoked any time we exit the driver to * wait for an interrupt response from a drive. handler() points * at the appropriate code to handle the next interrupt, and a @@ -909,9 +899,9 @@ if (rq->errors >= ERROR_MAX) { if (drive->driver != NULL) - DRIVER(drive)->end_request(0, HWGROUP(drive)); + DRIVER(drive)->end_request(drive, 0); else - ide_end_request(0, HWGROUP(drive)); + ide_end_request(drive, 0); } else { if ((rq->errors & ERROR_RESET) == ERROR_RESET) { ++rq->errors; @@ -1212,9 +1202,9 @@ return do_special(drive); kill_rq: if (drive->driver != NULL) - DRIVER(drive)->end_request(0, HWGROUP(drive)); + DRIVER(drive)->end_request(drive, 0); else - ide_end_request(0, HWGROUP(drive)); + ide_end_request(drive, 0); return ide_stopped; } @@ -1873,30 +1863,23 @@ static void ide_probe_module (void) { - if (!ide_probe) { -#if defined(CONFIG_KMOD) && defined(CONFIG_BLK_DEV_IDE_MODULE) - (void) request_module("ide-probe-mod"); -#endif /* (CONFIG_KMOD) && (CONFIG_BLK_DEV_IDE_MODULE) */ - } else { - (void) ide_probe->init(); - } + ideprobe_init(); revalidate_drives(); } static void ide_driver_module (void) { int index; - ide_module_t *module = ide_modules; + struct ide_driver_s *d; for (index = 0; index < MAX_HWIFS; ++index) if (ide_hwifs[index].present) goto search; ide_probe_module(); search: - while (module) { - (void) module->init(); - module = module->next; - } + for (d = ide_drivers; d != NULL; d = d->next) + d->driver_init(); + revalidate_drives(); } @@ -1974,11 +1957,10 @@ #endif /* - * Note that we only release the standard ports, - * and do not even try to handle any extra ports - * allocated for weird IDE interface chipsets. + * Note that we only release the standard ports, and do not even try to handle + * any extra ports allocated for weird IDE interface chipsets. */ -void hwif_unregister (ide_hwif_t *hwif) +static void hwif_unregister(ide_hwif_t *hwif) { if (hwif->straight8) { ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 8); @@ -2072,11 +2054,6 @@ if (irq_count == 1) free_irq(hwif->irq, hwgroup); - /* - * Note that we only release the standard ports, - * and do not even try to handle any extra ports - * allocated for weird IDE interface chipsets. - */ hwif_unregister(hwif); /* @@ -2126,7 +2103,6 @@ */ unregister_blkdev(hwif->major, hwif->name); kfree(blksize_size[hwif->major]); - kfree(max_readahead[hwif->major]); blk_dev[hwif->major].data = NULL; blk_dev[hwif->major].queue = NULL; blk_clear(hwif->major); @@ -2279,7 +2255,8 @@ void ide_add_setting (ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set) { - ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL; + ide_settings_t **p = &drive->settings; + ide_settings_t *setting = NULL; while ((*p) && strcmp((*p)->name, name) < 0) p = &((*p)->next); @@ -2305,7 +2282,7 @@ void ide_remove_setting (ide_drive_t *drive, char *name) { - ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting; + ide_settings_t **p = &drive->settings, *setting; while ((*p) && strcmp((*p)->name, name)) p = &((*p)->next); @@ -2316,30 +2293,6 @@ kfree(setting); } -static ide_settings_t *ide_find_setting_by_ioctl (ide_drive_t *drive, int cmd) -{ - ide_settings_t *setting = drive->settings; - - while (setting) { - if (setting->read_ioctl == cmd || setting->write_ioctl == cmd) - break; - setting = setting->next; - } - return setting; -} - -ide_settings_t *ide_find_setting_by_name (ide_drive_t *drive, char *name) -{ - ide_settings_t *setting = drive->settings; - - while (setting) { - if (strcmp(setting->name, name) == 0) - break; - setting = setting->next; - } - return setting; -} - static void auto_remove_settings (ide_drive_t *drive) { ide_settings_t *setting; @@ -2614,7 +2567,17 @@ if ((drive = get_info_ptr(inode->i_rdev)) == NULL) return -ENODEV; - if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) { + /* Find setting by ioctl */ + + setting = drive->settings; + + while (setting) { + if (setting->read_ioctl == cmd || setting->write_ioctl == cmd) + break; + setting = setting->next; + } + + if (setting != NULL) { if (cmd == setting->read_ioctl) { err = ide_read_setting(drive, setting); return err >= 0 ? put_user(err, (long *) arg) : err; @@ -3481,15 +3444,16 @@ static ide_startstop_t default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block) { - ide_end_request(0, HWGROUP(drive)); + ide_end_request(drive, 0); return ide_stopped; } - -static void default_end_request (byte uptodate, ide_hwgroup_t *hwgroup) + +/* This is the default end request function as well */ +int ide_end_request(ide_drive_t *drive, int uptodate) { - ide_end_request(uptodate, hwgroup); + return __ide_end_request(drive, uptodate, 0); } - + static int default_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -3536,25 +3500,6 @@ return 0; } -static void setup_driver_defaults (ide_drive_t *drive) -{ - ide_driver_t *d = drive->driver; - - if (d->cleanup == NULL) d->cleanup = default_cleanup; - if (d->standby == NULL) d->standby = default_standby; - if (d->flushcache == NULL) d->flushcache = default_flushcache; - if (d->do_request == NULL) d->do_request = default_do_request; - if (d->end_request == NULL) d->end_request = default_end_request; - if (d->ioctl == NULL) d->ioctl = default_ioctl; - if (d->open == NULL) d->open = default_open; - if (d->release == NULL) d->release = default_release; - if (d->media_change == NULL) d->media_change = default_check_media_change; - if (d->pre_reset == NULL) d->pre_reset = default_pre_reset; - if (d->capacity == NULL) d->capacity = default_capacity; - if (d->special == NULL) d->special = default_special; - if (d->driver_reinit == NULL) d->driver_reinit = default_driver_reinit; -} - ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n) { unsigned int unit, index, i; @@ -3575,18 +3520,49 @@ return NULL; } -int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version) +int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver) { unsigned long flags; - + save_flags(flags); /* all CPUs */ cli(); /* all CPUs */ - if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL || drive->busy || drive->usage) { + if (!drive->present || drive->driver != NULL || drive->busy || drive->usage) { restore_flags(flags); /* all CPUs */ return 1; } + drive->driver = driver; - setup_driver_defaults(drive); + + /* Fill in the default handlers + */ + + if (driver->cleanup == NULL) + driver->cleanup = default_cleanup; + if (driver->standby == NULL) + driver->standby = default_standby; + if (driver->flushcache == NULL) + driver->flushcache = default_flushcache; + if (driver->do_request == NULL) + driver->do_request = default_do_request; + if (driver->end_request == NULL) + driver->end_request = ide_end_request; + if (driver->ioctl == NULL) + driver->ioctl = default_ioctl; + if (driver->open == NULL) + driver->open = default_open; + if (driver->release == NULL) + driver->release = default_release; + if (driver->media_change == NULL) + driver->media_change = default_check_media_change; + if (driver->pre_reset == NULL) + driver->pre_reset = default_pre_reset; + if (driver->capacity == NULL) + driver->capacity = default_capacity; + if (driver->special == NULL) + driver->special = default_special; + if (driver->driver_reinit == NULL) + driver->driver_reinit = default_driver_reinit; + restore_flags(flags); /* all CPUs */ if (drive->autotune != 2) { if (driver->supports_dma && HWIF(drive)->dmaproc != NULL) { @@ -3614,7 +3590,7 @@ int ide_unregister_subdriver (ide_drive_t *drive) { unsigned long flags; - + save_flags(flags); /* all CPUs */ cli(); /* all CPUs */ if (drive->usage || drive->busy || drive->driver == NULL || DRIVER(drive)->busy) { @@ -3634,26 +3610,26 @@ return 0; } -int ide_register_module (ide_module_t *module) +int ide_register_module (struct ide_driver_s *d) { - ide_module_t *p = ide_modules; + struct ide_driver_s *p = ide_drivers; while (p) { - if (p == module) + if (p == d) return 1; p = p->next; } - module->next = ide_modules; - ide_modules = module; + d->next = ide_drivers; + ide_drivers = d; revalidate_drives(); return 0; } -void ide_unregister_module (ide_module_t *module) +void ide_unregister_module (struct ide_driver_s *d) { - ide_module_t **p; + struct ide_driver_s **p; - for (p = &ide_modules; (*p) && (*p) != module; p = &((*p)->next)); + for (p = &ide_drivers; (*p) && (*p) != d; p = &((*p)->next)); if (*p) *p = (*p)->next; } @@ -3678,7 +3654,6 @@ devfs_handle_t ide_devfs_handle; EXPORT_SYMBOL(ide_lock); -EXPORT_SYMBOL(ide_probe); EXPORT_SYMBOL(drive_is_flashcard); EXPORT_SYMBOL(ide_timer_expiry); EXPORT_SYMBOL(ide_intr); @@ -3704,8 +3679,8 @@ EXPORT_SYMBOL(ide_init_drive_cmd); EXPORT_SYMBOL(ide_do_drive_cmd); EXPORT_SYMBOL(ide_end_drive_cmd); -EXPORT_SYMBOL(ide_end_request); EXPORT_SYMBOL(__ide_end_request); +EXPORT_SYMBOL(ide_end_request); EXPORT_SYMBOL(ide_revalidate_drive); EXPORT_SYMBOL(ide_revalidate_disk); EXPORT_SYMBOL(ide_cmd); @@ -3728,7 +3703,6 @@ EXPORT_SYMBOL(ide_register); EXPORT_SYMBOL(ide_unregister); EXPORT_SYMBOL(ide_setup_ports); -EXPORT_SYMBOL(hwif_unregister); EXPORT_SYMBOL(get_info_ptr); EXPORT_SYMBOL(current_capacity); diff -Nru a/drivers/ide/pdc4030.c b/drivers/ide/pdc4030.c --- a/drivers/ide/pdc4030.c Tue Feb 19 18:08:58 2002 +++ b/drivers/ide/pdc4030.c Tue Feb 19 18:08:58 2002 @@ -345,7 +345,7 @@ rq->nr_sectors -= nsect; total_remaining = rq->nr_sectors; if ((rq->current_nr_sectors -= nsect) <= 0) { - ide_end_request(1, HWGROUP(drive)); + ide_end_request(drive, 1); } /* * Now the data has been read in, do the following: @@ -407,7 +407,8 @@ #ifdef DEBUG_WRITE printk(KERN_DEBUG "%s: Write complete - end_request\n", drive->name); #endif - __ide_end_request(hwgroup, 1, rq->nr_sectors); + __ide_end_request(drive, 1, rq->nr_sectors); + return ide_stopped; } @@ -571,7 +572,7 @@ /* Check that it's a regular command. If not, bomb out early. */ if (!(rq->flags & REQ_CMD)) { blk_dump_rq_flags(rq, "pdc4030 bad flags"); - ide_end_request(0, HWGROUP(drive)); + ide_end_request(drive, 0); return ide_stopped; } @@ -633,7 +634,7 @@ default: printk(KERN_ERR "pdc4030: command not READ or WRITE! Huh?\n"); - ide_end_request(0, HWGROUP(drive)); + ide_end_request(drive, 0); return ide_stopped; } } diff -Nru a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c --- a/drivers/ieee1394/dv1394.c Tue Feb 19 18:08:58 2002 +++ b/drivers/ieee1394/dv1394.c Tue Feb 19 18:08:58 2002 @@ -215,8 +215,11 @@ if (!pgd_none(*pgd)) { pmd = pmd_offset(pgd, adr); if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); + preempt_disable(); + ptep = pte_offset_map(pmd, adr); pte = *ptep; + pte_unmap(pte); + preempt_enable(); if(pte_present(pte)) ret = pte_page(pte); } diff -Nru a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c --- a/drivers/ieee1394/pcilynx.c Tue Feb 19 18:08:59 2002 +++ b/drivers/ieee1394/pcilynx.c Tue Feb 19 18:08:59 2002 @@ -748,10 +748,11 @@ } if (newoffs < 0 || newoffs > PCILYNX_MAX_MEMORY + 1) { - lock_kernel(); + unlock_kernel(); return -EINVAL; } + unlock_kernel(); file->f_pos = newoffs; return newoffs; } diff -Nru a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c --- a/drivers/ieee1394/video1394.c Tue Feb 19 18:08:58 2002 +++ b/drivers/ieee1394/video1394.c Tue Feb 19 18:08:58 2002 @@ -173,36 +173,11 @@ * defined way to get at the kernel page tables. */ -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if(pte_present(pte)) { - ret = (unsigned long) - page_address(pte_page(pte)); - ret |= (adr & (PAGE_SIZE - 1)); - } - } - } - MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); - return ret; -} - static inline unsigned long uvirt_to_bus(unsigned long adr) { unsigned long kva, ret; - kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + kva = page_address(vmalloc_to_page(adr)); ret = virt_to_bus((void *)kva); MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); return ret; @@ -213,7 +188,7 @@ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); + kva = page_address(vmalloc_to_page(va)); ret = virt_to_bus((void *)kva); MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); return ret; @@ -228,7 +203,7 @@ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); + kva = page_address(vmalloc_to_page(va)); ret = __pa(kva); MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); return ret; diff -Nru a/drivers/input/joystick/iforce.c b/drivers/input/joystick/iforce.c --- a/drivers/input/joystick/iforce.c Tue Feb 19 18:08:57 2002 +++ b/drivers/input/joystick/iforce.c Tue Feb 19 18:08:57 2002 @@ -1024,18 +1024,18 @@ if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL))) return NULL; memset(iforce, 0, sizeof(struct iforce)); - iforce->irq = usb_alloc_urb(0); + iforce->irq = usb_alloc_urb(0, GFP_KERNEL); if (!iforce->irq) { kfree(iforce); return NULL; } - iforce->out = usb_alloc_urb(0); + iforce->out = usb_alloc_urb(0, GFP_KERNEL); if (!iforce->out) { usb_free_urb(iforce->irq); kfree(iforce); return NULL; } - iforce->ctrl = usb_alloc_urb(0); + iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL); if (!iforce->ctrl) { usb_free_urb(iforce->out); usb_free_urb(iforce->irq); diff -Nru a/drivers/isdn/avmb1/capifs.c b/drivers/isdn/avmb1/capifs.c --- a/drivers/isdn/avmb1/capifs.c Tue Feb 19 18:08:58 2002 +++ b/drivers/isdn/avmb1/capifs.c Tue Feb 19 18:08:58 2002 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -170,10 +171,12 @@ if (tmp == p || *tmp) return NULL; + lock_kernel(); for (i = 0, np = sbi->nccis ; i < sbi->max_ncci; i++, np++) { if (np->used && np->num == num && np->type == type) break; } + unlock_kernel(); if ( i >= sbi->max_ncci ) return NULL; diff -Nru a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c --- a/drivers/isdn/hisax/hfc_pci.c Tue Feb 19 18:08:59 2002 +++ b/drivers/isdn/hisax/hfc_pci.c Tue Feb 19 18:08:59 2002 @@ -90,9 +90,8 @@ pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, 0); /* disable memory mapped ports + busmaster */ #endif /* CONFIG_PCI */ del_timer(&cs->hw.hfcpci.timer); - kfree(cs->hw.hfcpci.share_start); - cs->hw.hfcpci.share_start = NULL; - vfree(cs->hw.hfcpci.pci_io); + iounmap(cs->hw.hfcpci.pci_io); + pci_free_consistent(cs->hw.hfcpci.pdev, 32768, cs->hw.hfcpci.fifos, cs->hw.hfcpci.fifos_dma); } /********************************************************************************/ @@ -244,7 +243,7 @@ cs->hw.hfcpci.fifo_en ^= fifo_state; Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); cs->hw.hfcpci.last_bfifo_cnt[fifo] = 0; - bzr->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1; + bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); bzr->za[MAX_B_FRAMES].z2 = bzr->za[MAX_B_FRAMES].z1; bzr->f1 = MAX_B_FRAMES; bzr->f2 = bzr->f1; /* init F pointers to remain constant */ @@ -270,7 +269,7 @@ if (fifo_state) cs->hw.hfcpci.fifo_en ^= fifo_state; Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); - bzt->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1; + bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); bzt->za[MAX_B_FRAMES].z2 = bzt->za[MAX_B_FRAMES].z1; bzt->f1 = MAX_B_FRAMES; bzt->f2 = bzt->f1; /* init F pointers to remain constant */ @@ -298,18 +297,18 @@ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) debugl1(cs, "hfcpci_empty_fifo"); zp = &bz->za[bz->f2]; /* point to Z-Regs */ - new_z2 = zp->z2 + count; /* new position in fifo */ + new_z2 = le16_to_cpu(zp->z2) + count; /* new position in fifo */ if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z2 -= B_FIFO_SIZE; /* buffer wrap */ new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; if ((count > HSCX_BUFMAX + 3) || (count < 4) || - (*(bdata + (zp->z1 - B_SUB_VAL)))) { + (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count); #ifdef ERROR_STATISTIC bcs->err_inv++; #endif - bz->za[new_f2].z2 = new_z2; + bz->za[new_f2].z2 = cpu_to_le16(new_z2); bz->f2 = new_f2; /* next buffer */ skb = NULL; } else if (!(skb = dev_alloc_skb(count - 3))) @@ -319,12 +318,12 @@ count -= 3; ptr = skb_put(skb, count); - if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL) + if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL) maxlen = count; /* complete transfer */ else - maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ + maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(zp->z2); /* maximum */ - ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ + ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL); /* start of data */ memcpy(ptr, ptr1, maxlen); /* copy data */ count -= maxlen; @@ -333,7 +332,7 @@ ptr1 = bdata; /* start of buffer */ memcpy(ptr, ptr1, count); /* rest */ } - bz->za[new_f2].z2 = new_z2; + bz->za[new_f2].z2 = cpu_to_le16(new_z2); bz->f2 = new_f2; /* next buffer */ } @@ -363,34 +362,34 @@ } while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { zp = &df->za[df->f2 & D_FREG_MASK]; - rcnt = zp->z1 - zp->z2; + rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); if (rcnt < 0) rcnt += D_FIFO_SIZE; rcnt++; if (cs->debug & L1_DEB_ISAC) debugl1(cs, "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)", - df->f1, df->f2, zp->z1, zp->z2, rcnt); + df->f1, df->f2, le16_to_cpu(zp->z1), le16_to_cpu(zp->z2), rcnt); if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || - (df->data[zp->z1])) { + (df->data[le16_to_cpu(zp->z1)])) { if (cs->debug & L1_DEB_WARN) - debugl1(cs, "empty_fifo hfcpci paket inv. len %d or crc %d", rcnt, df->data[zp->z1]); + debugl1(cs, "empty_fifo hfcpci paket inv. len %d or crc %d", rcnt, df->data[le16_to_cpu(zp->z1)]); #ifdef ERROR_STATISTIC cs->err_rx++; #endif df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ - df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + rcnt) & (D_FIFO_SIZE - 1); + df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16((le16_to_cpu(zp->z2) + rcnt) & (D_FIFO_SIZE - 1)); } else if ((skb = dev_alloc_skb(rcnt - 3))) { total = rcnt; rcnt -= 3; ptr = skb_put(skb, rcnt); - if (zp->z2 + rcnt <= D_FIFO_SIZE) + if ((le16_to_cpu(zp->z2) + rcnt) <= D_FIFO_SIZE) maxlen = rcnt; /* complete transfer */ else - maxlen = D_FIFO_SIZE - zp->z2; /* maximum */ + maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2); /* maximum */ - ptr1 = df->data + zp->z2; /* start of data */ + ptr1 = df->data + le16_to_cpu(zp->z2); /* start of data */ memcpy(ptr, ptr1, maxlen); /* copy data */ rcnt -= maxlen; @@ -400,7 +399,7 @@ memcpy(ptr, ptr1, rcnt); /* rest */ } df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ - df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + total) & (D_FIFO_SIZE - 1); + df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16((le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1)); skb_queue_tail(&cs->rq, skb); sched_event_D_pci(cs, D_RCVBUFREADY); @@ -425,7 +424,7 @@ z1r = &bz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ z2r = z1r + 1; - if (!(fcnt = *z1r - *z2r)) + if (!(fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r))) return (0); /* no data avail */ if (fcnt <= 0) @@ -433,7 +432,7 @@ if (fcnt > HFCPCI_BTRANS_THRESHOLD) fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */ - new_z2 = *z2r + fcnt; /* new position in fifo */ + new_z2 = le16_to_cpu(*z2r) + fcnt; /* new position in fifo */ if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z2 -= B_FIFO_SIZE; /* buffer wrap */ @@ -441,12 +440,12 @@ printk(KERN_WARNING "HFCPCI: receive out of memory\n"); else { ptr = skb_put(skb, fcnt); - if (*z2r + fcnt <= B_FIFO_SIZE + B_SUB_VAL) + if (le16_to_cpu(*z2r) + fcnt <= B_FIFO_SIZE + B_SUB_VAL) maxlen = fcnt; /* complete transfer */ else - maxlen = B_FIFO_SIZE + B_SUB_VAL - *z2r; /* maximum */ + maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r); /* maximum */ - ptr1 = bdata + (*z2r - B_SUB_VAL); /* start of data */ + ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL); /* start of data */ memcpy(ptr, ptr1, maxlen); /* copy data */ fcnt -= maxlen; @@ -461,7 +460,7 @@ hfcpci_sched_event(bcs, B_RCVBUFREADY); } - *z2r = new_z2; /* new position */ + *z2r = cpu_to_le16(new_z2); /* new position */ return (1); } /* hfcpci_empty_fifo_trans */ @@ -506,13 +505,13 @@ bcs->channel, bz->f1, bz->f2); zp = &bz->za[bz->f2]; - rcnt = zp->z1 - zp->z2; + rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); if (rcnt < 0) rcnt += B_FIFO_SIZE; rcnt++; if (cs->debug & L1_DEB_HSCX) debugl1(cs, "hfcpci rec %d z1(%x) z2(%x) cnt(%d)", - bcs->channel, zp->z1, zp->z2, rcnt); + bcs->channel, le16_to_cpu(zp->z1), le16_to_cpu(zp->z2), rcnt); if ((skb = hfcpci_empty_fifo(bcs, bz, bdata, rcnt))) { cli(); skb_queue_tail(&bcs->rqueue, skb); @@ -564,7 +563,7 @@ if (cs->debug & L1_DEB_ISAC) debugl1(cs, "hfcpci_fill_Dfifo f1(%d) f2(%d) z1(f1)(%x)", df->f1, df->f2, - df->za[df->f1 & D_FREG_MASK].z1); + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1)); fcnt = df->f1 - df->f2; /* frame count actually buffered */ if (fcnt < 0) fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ @@ -577,7 +576,7 @@ return; } /* now determine free bytes in FIFO buffer */ - count = df->za[df->f1 & D_FREG_MASK].z2 - df->za[df->f1 & D_FREG_MASK].z1; + count = le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z2) - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); if (count <= 0) count += D_FIFO_SIZE; /* count now contains available bytes */ @@ -590,11 +589,11 @@ return; } count = cs->tx_skb->len; /* get frame len */ - new_z1 = (df->za[df->f1 & D_FREG_MASK].z1 + count) & (D_FIFO_SIZE - 1); + new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) & (D_FIFO_SIZE - 1); new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); src = cs->tx_skb->data; /* source pointer */ - dst = df->data + df->za[df->f1 & D_FREG_MASK].z1; - maxlen = D_FIFO_SIZE - df->za[df->f1 & D_FREG_MASK].z1; /* end fifo */ + dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); + maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); /* end fifo */ if (maxlen > count) maxlen = count; /* limit size */ memcpy(dst, src, maxlen); /* first copy */ @@ -607,8 +606,8 @@ } save_flags(flags); cli(); - df->za[new_f1 & D_FREG_MASK].z1 = new_z1; /* for next buffer */ - df->za[df->f1 & D_FREG_MASK].z1 = new_z1; /* new pos actual buffer */ + df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); /* for next buffer */ + df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); /* new pos actual buffer */ df->f1 = new_f1; /* next frame */ restore_flags(flags); @@ -653,8 +652,8 @@ z2t = z1t + 1; if (cs->debug & L1_DEB_HSCX) debugl1(cs, "hfcpci_fill_fifo_trans %d z1(%x) z2(%x)", - bcs->channel, *z1t, *z2t); - fcnt = *z2t - *z1t; + bcs->channel, le16_to_cpu(*z1t), le16_to_cpu(*z2t)); + fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); if (fcnt <= 0) fcnt += B_FIFO_SIZE; /* fcnt contains available bytes in fifo */ fcnt = B_FIFO_SIZE - fcnt; /* remaining bytes to send */ @@ -664,12 +663,12 @@ /* data is suitable for fifo */ count = bcs->tx_skb->len; - new_z1 = *z1t + count; /* new buffer Position */ + new_z1 = le16_to_cpu(*z1t) + count; /* new buffer Position */ if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z1 -= B_FIFO_SIZE; /* buffer wrap */ src = bcs->tx_skb->data; /* source pointer */ - dst = bdata + (*z1t - B_SUB_VAL); - maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t; /* end of fifo */ + dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); /* end of fifo */ if (maxlen > count) maxlen = count; /* limit size */ memcpy(dst, src, maxlen); /* first copy */ @@ -682,7 +681,7 @@ } bcs->tx_cnt -= bcs->tx_skb->len; fcnt += bcs->tx_skb->len; - *z1t = new_z1; /* now send data */ + *z1t = cpu_to_le16(new_z1); /* now send data */ } else if (cs->debug & L1_DEB_HSCX) debugl1(cs, "hfcpci_fill_fifo_trans %d frame length %d discarded", bcs->channel, bcs->tx_skb->len); @@ -699,7 +698,7 @@ if (cs->debug & L1_DEB_HSCX) debugl1(cs, "hfcpci_fill_fifo_hdlc %d f1(%d) f2(%d) z1(f1)(%x)", bcs->channel, bz->f1, bz->f2, - bz->za[bz->f1].z1); + le16_to_cpu(bz->za[bz->f1].z1)); fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ if (fcnt < 0) @@ -711,7 +710,7 @@ return; } /* now determine free bytes in FIFO buffer */ - count = bz->za[bz->f1].z2 - bz->za[bz->f1].z1; + count = le16_to_cpu(bz->za[bz->f1].z2) - le16_to_cpu(bz->za[bz->f1].z1); if (count <= 0) count += B_FIFO_SIZE; /* count now contains available bytes */ @@ -727,14 +726,14 @@ return; } count = bcs->tx_skb->len; /* get frame len */ - new_z1 = bz->za[bz->f1].z1 + count; /* new buffer Position */ + new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count; /* new buffer Position */ if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z1 -= B_FIFO_SIZE; /* buffer wrap */ new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); src = bcs->tx_skb->data; /* source pointer */ - dst = bdata + (bz->za[bz->f1].z1 - B_SUB_VAL); - maxlen = (B_FIFO_SIZE + B_SUB_VAL) - bz->za[bz->f1].z1; /* end fifo */ + dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1); /* end fifo */ if (maxlen > count) maxlen = count; /* limit size */ memcpy(dst, src, maxlen); /* first copy */ @@ -751,7 +750,7 @@ bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len); cli(); - bz->za[new_f1].z1 = new_z1; /* for next buffer */ + bz->za[new_f1].z1 = cpu_to_le16(new_z1); /* for next buffer */ bz->f1 = new_f1; /* next frame */ restore_flags(flags); @@ -892,34 +891,34 @@ bz->f1, bz->f2); zp = &bz->za[bz->f2]; - rcnt = zp->z1 - zp->z2; + rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); if (rcnt < 0) rcnt += B_FIFO_SIZE; rcnt++; if (cs->debug & L1_DEB_ISAC) debugl1(cs, "hfcpci e_rec z1(%x) z2(%x) cnt(%d)", - zp->z1, zp->z2, rcnt); - new_z2 = zp->z2 + rcnt; /* new position in fifo */ + le16_to_cpu(zp->z1), le16_to_cpu(zp->z2), rcnt); + new_z2 = le16_to_cpu(zp->z2) + rcnt; /* new position in fifo */ if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z2 -= B_FIFO_SIZE; /* buffer wrap */ new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; if ((rcnt > 256 + 3) || (count < 4) || - (*(bdata + (zp->z1 - B_SUB_VAL)))) { + (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "hfcpci_empty_echan: incoming packet invalid length %d or crc", rcnt); - bz->za[new_f2].z2 = new_z2; + bz->za[new_f2].z2 = cpu_to_le16(new_z2); bz->f2 = new_f2; /* next buffer */ } else { total = rcnt; rcnt -= 3; ptr = e_buffer; - if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL) + if (le16_to_cpu(zp->z2) <= (B_FIFO_SIZE + B_SUB_VAL)) maxlen = rcnt; /* complete transfer */ else - maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ + maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(zp->z2); /* maximum */ - ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ + ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL); /* start of data */ memcpy(ptr, ptr1, maxlen); /* copy data */ rcnt -= maxlen; @@ -928,7 +927,7 @@ ptr1 = bdata; /* start of buffer */ memcpy(ptr, ptr1, rcnt); /* rest */ } - bz->za[new_f2].z2 = new_z2; + bz->za[new_f2].z2 = cpu_to_le16(new_z2); bz->f2 = new_f2; /* next buffer */ if (cs->debug & DEB_DLOG_HEX) { ptr = cs->dlog; @@ -1688,9 +1687,6 @@ int i; struct pci_dev *tmp_hfcpci = NULL; -#ifdef __BIG_ENDIAN -#error "not running on big endian machines now" -#endif strcpy(tmp, hfcpci_revision); printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp)); #if CONFIG_PCI @@ -1721,6 +1717,7 @@ cs->hw.hfcpci.pci_bus = dev_hfcpci->bus->number; cs->hw.hfcpci.pci_device_fn = dev_hfcpci->devfn; cs->irq = dev_hfcpci->irq; + cs->hw.hfcpci.pdev = tmp_hfcpci; if (!cs->irq) { printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); return (0); @@ -1738,22 +1735,22 @@ /* Allocate memory for FIFOS */ /* Because the HFC-PCI needs a 32K physical alignment, we */ /* need to allocate the double mem and align the address */ - if (!((void *) cs->hw.hfcpci.share_start = kmalloc(65536, GFP_KERNEL))) { + cs->hw.hfcpci.fifos = pci_alloc_consistent(tmp_hfcpci, 32768, &cs->hw.hfcpci.fifos_dma); + if (!cs->hw.hfcpci.fifos) { printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n"); return 0; } - (ulong) cs->hw.hfcpci.fifos = - (((ulong) cs->hw.hfcpci.share_start) & ~0x7FFF) + 0x8000; pcibios_write_config_dword(cs->hw.hfcpci.pci_bus, - cs->hw.hfcpci.pci_device_fn, 0x80, - (u_int) virt_to_bus(cs->hw.hfcpci.fifos)); + cs->hw.hfcpci.pci_device_fn, 0x80, + (u_int)cs->hw.hfcpci.fifos_dma); cs->hw.hfcpci.pci_io = ioremap((ulong) cs->hw.hfcpci.pci_io, 256); printk(KERN_INFO "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n", (u_int) cs->hw.hfcpci.pci_io, (u_int) cs->hw.hfcpci.fifos, - (u_int) virt_to_bus(cs->hw.hfcpci.fifos), + (u_int) cs->hw.hfcpci.fifos_dma, cs->irq, HZ); + printk("ChipID: %x\n", Read_hfc(cs, HFCPCI_CHIP_ID)); pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ cs->hw.hfcpci.int_m2 = 0; /* disable alle interrupts */ cs->hw.hfcpci.int_m1 = 0; diff -Nru a/drivers/isdn/hisax/hfc_pci.h b/drivers/isdn/hisax/hfc_pci.h --- a/drivers/isdn/hisax/hfc_pci.h Tue Feb 19 18:08:57 2002 +++ b/drivers/isdn/hisax/hfc_pci.h Tue Feb 19 18:08:57 2002 @@ -185,7 +185,7 @@ typedef struct { unsigned short z1; /* Z1 pointer 16 Bit */ unsigned short z2; /* Z2 pointer 16 Bit */ - } z_type; + } __attribute__((packed)) z_type; typedef struct { u_char data[D_FIFO_SIZE]; /* FIFO data space */ @@ -194,20 +194,20 @@ u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */ z_type za[MAX_D_FRAMES+1]; /* mask index with D_FREG_MASK for access */ u_char fill3[0x4000-0x2100]; /* align 16K */ - } dfifo_type; + } __attribute__((packed)) dfifo_type; typedef struct { z_type za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */ u_char f1,f2; /* f pointers */ u_char fill[0x2100-0x2082]; /* alignment */ - } bzfifo_type; + } __attribute__((packed)) bzfifo_type; typedef union { struct { dfifo_type d_tx; /* D-send channel */ dfifo_type d_rx; /* D-receive channel */ - } d_chan; + } __attribute__((packed)) d_chan; struct { u_char fill1[0x200]; u_char txdat_b1[B_FIFO_SIZE]; @@ -223,13 +223,15 @@ bzfifo_type rxbz_b2; u_char rxdat_b2[B_FIFO_SIZE]; - } b_chans; + } __attribute__((packed)) b_chans; u_char fill[32768]; - } fifo_area; + } __attribute__((packed)) fifo_area; -#define Write_hfc(a,b,c) (*(((u_char *)a->hw.hfcpci.pci_io)+b) = c) -#define Read_hfc(a,b) (*(((u_char *)a->hw.hfcpci.pci_io)+b)) +//#define Write_hfc(a,b,c) (*(((u_char *)a->hw.hfcpci.pci_io)+b) = c) +//#define Read_hfc(a,b) (*(((u_char *)a->hw.hfcpci.pci_io)+b)) +#define Write_hfc(a,b,c) writeb(c, ((u_char *)a->hw.hfcpci.pci_io)+b) +#define Read_hfc(a,b) readb(((u_char *)a->hw.hfcpci.pci_io)+b) extern void main_irq_hcpci(struct BCState *bcs); extern void inithfcpci(struct IsdnCardState *cs); diff -Nru a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h --- a/drivers/isdn/hisax/hisax.h Tue Feb 19 18:08:57 2002 +++ b/drivers/isdn/hisax/hisax.h Tue Feb 19 18:08:57 2002 @@ -422,10 +422,11 @@ struct tiger_hw { u_int *send; - u_int *s_irq; + dma_addr_t send_dma; u_int *s_end; u_int *sendp; u_int *rec; + dma_addr_t rec_dma; int free; u_char *rcvbuf; u_char *sendbuf; @@ -660,6 +661,7 @@ unsigned char irqmask0; unsigned char irqstat0; unsigned char last_is0; + struct pci_dev *pdev; }; struct hfcPCI_hw { @@ -683,8 +685,9 @@ unsigned char pci_bus; unsigned char pci_device_fn; unsigned char *pci_io; /* start of PCI IO memory */ - void *share_start; /* shared memory for Fifos start */ void *fifos; /* FIFO memory */ + dma_addr_t fifos_dma; + struct pci_dev* pdev; int last_bfifo_cnt[2]; /* marker saving last b-fifo frame count */ struct timer_list timer; }; diff -Nru a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c --- a/drivers/isdn/hisax/netjet.c Tue Feb 19 18:08:58 2002 +++ b/drivers/isdn/hisax/netjet.c Tue Feb 19 18:08:58 2002 @@ -659,7 +659,9 @@ if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) { write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free); } else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) { - p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR)); + p = inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR) + - bcs->hw.tiger.send_dma + + bcs->hw.tiger.send; sp = bcs->hw.tiger.sendp; if (p == bcs->hw.tiger.s_end) p = bcs->hw.tiger.send -1; @@ -680,7 +682,9 @@ write_raw(bcs, p, bcs->hw.tiger.free - cnt); } } else if (test_and_clear_bit(BC_FLG_EMPTY, &bcs->Flag)) { - p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR)); + p = inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR) + - bcs->hw.tiger.send_dma + + bcs->hw.tiger.send; cnt = bcs->hw.tiger.s_end - p; if (cnt < 2) { p = bcs->hw.tiger.send + 1; @@ -935,29 +939,36 @@ void __init inittiger(struct IsdnCardState *cs) { - if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_TXSIZE * sizeof(unsigned int), - GFP_KERNEL | GFP_DMA))) { + cs->bcs[0].hw.tiger.send = + pci_alloc_consistent(cs->hw.njet.pdev, + NETJET_DMA_TXSIZE * sizeof(unsigned int), + &cs->bcs[0].hw.tiger.send_dma); + if (!cs->bcs[0].hw.tiger.send) { printk(KERN_WARNING "HiSax: No memory for tiger.send\n"); return; } - cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE/2 - 1; - cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1; - cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send; - cs->bcs[1].hw.tiger.s_irq = cs->bcs[0].hw.tiger.s_irq; - cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end; + cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1; + + cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send; + cs->bcs[1].hw.tiger.send_dma = cs->bcs[0].hw.tiger.send_dma; + cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end; memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_TXSIZE * sizeof(unsigned int)); debugl1(cs, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send, (u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1)); - outl(virt_to_bus(cs->bcs[0].hw.tiger.send), + outl(cs->bcs[0].hw.tiger.send_dma, cs->hw.njet.base + NETJET_DMA_READ_START); - outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq), + outl(cs->bcs[0].hw.tiger.send_dma + NETJET_DMA_TXSIZE/2 - 1, cs->hw.njet.base + NETJET_DMA_READ_IRQ); - outl(virt_to_bus(cs->bcs[0].hw.tiger.s_end), + outl(cs->bcs[0].hw.tiger.send_dma + NETJET_DMA_TXSIZE - 1, cs->hw.njet.base + NETJET_DMA_READ_END); - if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_RXSIZE * sizeof(unsigned int), - GFP_KERNEL | GFP_DMA))) { + + cs->bcs[0].hw.tiger.rec = + pci_alloc_consistent(cs->hw.njet.pdev, + NETJET_DMA_RXSIZE * sizeof(unsigned int), + &cs->bcs[0].hw.tiger.rec_dma); + if (!cs->bcs[0].hw.tiger.rec) { printk(KERN_WARNING "HiSax: No memory for tiger.rec\n"); return; @@ -965,12 +976,13 @@ debugl1(cs, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec, (u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1)); cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec; + cs->bcs[1].hw.tiger.rec_dma = cs->bcs[0].hw.tiger.rec_dma; memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_RXSIZE * sizeof(unsigned int)); - outl(virt_to_bus(cs->bcs[0].hw.tiger.rec), + outl(cs->bcs[0].hw.tiger.rec_dma, cs->hw.njet.base + NETJET_DMA_WRITE_START); - outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE/2 - 1), + outl(cs->bcs[0].hw.tiger.rec_dma + NETJET_DMA_RXSIZE/2 - 1, cs->hw.njet.base + NETJET_DMA_WRITE_IRQ); - outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1), + outl(cs->bcs[0].hw.tiger.rec_dma + NETJET_DMA_RXSIZE - 1, cs->hw.njet.base + NETJET_DMA_WRITE_END); debugl1(cs, "tiger: dmacfg %x/%x pulse=%d", inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), @@ -987,14 +999,20 @@ releasetiger(struct IsdnCardState *cs) { if (cs->bcs[0].hw.tiger.send) { - kfree(cs->bcs[0].hw.tiger.send); + pci_free_consistent(cs->hw.njet.pdev, + NETJET_DMA_TXSIZE * sizeof(unsigned int), + cs->bcs[0].hw.tiger.send, + cs->bcs[0].hw.tiger.send_dma); cs->bcs[0].hw.tiger.send = NULL; } if (cs->bcs[1].hw.tiger.send) { cs->bcs[1].hw.tiger.send = NULL; } if (cs->bcs[0].hw.tiger.rec) { - kfree(cs->bcs[0].hw.tiger.rec); + pci_free_consistent(cs->hw.njet.pdev, + NETJET_DMA_RXSIZE * sizeof(unsigned int), + cs->bcs[0].hw.tiger.rec, + cs->bcs[0].hw.tiger.rec_dma); cs->bcs[0].hw.tiger.rec = NULL; } if (cs->bcs[1].hw.tiger.rec) { diff -Nru a/drivers/isdn/hisax/nj_s.c b/drivers/isdn/hisax/nj_s.c --- a/drivers/isdn/hisax/nj_s.c Tue Feb 19 18:08:58 2002 +++ b/drivers/isdn/hisax/nj_s.c Tue Feb 19 18:08:58 2002 @@ -184,6 +184,7 @@ printk(KERN_WARNING "NETjet-S: No IO-Adr for PCI card found\n"); return(0); } + cs->hw.njet.pdev = dev_netjet; } else { printk(KERN_WARNING "NETjet-S: No PCI card found\n"); return(0); diff -Nru a/drivers/isdn/hisax/nj_u.c b/drivers/isdn/hisax/nj_u.c --- a/drivers/isdn/hisax/nj_u.c Tue Feb 19 18:08:58 2002 +++ b/drivers/isdn/hisax/nj_u.c Tue Feb 19 18:08:58 2002 @@ -186,6 +186,7 @@ printk(KERN_WARNING "NETspider-U: No IO-Adr for PCI card found\n"); return(0); } + cs->hw.njet.pdev = dev_netjet; } else { printk(KERN_WARNING "NETspider-U: No PCI card found\n"); return(0); diff -Nru a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c --- a/drivers/isdn/hisax/st5481_usb.c Tue Feb 19 18:08:57 2002 +++ b/drivers/isdn/hisax/st5481_usb.c Tue Feb 19 18:08:57 2002 @@ -265,7 +265,7 @@ } // Allocate URB for control endpoint - urb = usb_alloc_urb(0); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { return -ENOMEM; } @@ -280,7 +280,7 @@ fifo_init(&ctrl->msg_fifo.f, ARRAY_SIZE(ctrl->msg_fifo.data)); // Allocate URBs and buffers for interrupt endpoint - urb = usb_alloc_urb(0); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { return -ENOMEM; } @@ -416,7 +416,7 @@ for (j = 0; j < 2; j++) { retval = -ENOMEM; - urb[j] = usb_alloc_urb(num_packets); + urb[j] = usb_alloc_urb(num_packet, GFP_KERNEL); if (!urb[j]) goto err; diff -Nru a/drivers/md/lvm-fs.c b/drivers/md/lvm-fs.c --- a/drivers/md/lvm-fs.c Tue Feb 19 18:08:57 2002 +++ b/drivers/md/lvm-fs.c Tue Feb 19 18:08:57 2002 @@ -170,11 +170,11 @@ devfs_handle_t lvm_fs_create_lv(vg_t *vg_ptr, lv_t *lv) { struct proc_dir_entry *pde; - const char *name = _basename(lv->lv_name); + const char *name = _basename(lv->u.lv_name); - lv_devfs_handle[minor(lv->lv_dev)] = devfs_register( + lv_devfs_handle[minor(lv->u.lv_dev)] = devfs_register( vg_devfs_handle[vg_ptr->vg_number], name, - DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, minor(lv->lv_dev), + DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, minor(lv->u.lv_dev), S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, &lvm_blk_dops, NULL); @@ -183,15 +183,15 @@ pde->read_proc = _proc_read_lv; pde->data = lv; } - return lv_devfs_handle[minor(lv->lv_dev)]; + return lv_devfs_handle[minor(lv->u.lv_dev)]; } void lvm_fs_remove_lv(vg_t *vg_ptr, lv_t *lv) { - devfs_unregister(lv_devfs_handle[minor(lv->lv_dev)]); - lv_devfs_handle[minor(lv->lv_dev)] = NULL; + devfs_unregister(lv_devfs_handle[minor(lv->u.lv_dev)]); + lv_devfs_handle[minor(lv->u.lv_dev)] = NULL; if(vg_ptr->lv_subdir_pde) { - const char *name = _basename(lv->lv_name); + const char *name = _basename(lv->u.lv_name); remove_proc_entry(name, vg_ptr->lv_subdir_pde); } } @@ -269,21 +269,21 @@ int sz = 0; lv_t *lv = data; - sz += sprintf(page + sz, "name: %s\n", lv->lv_name); - sz += sprintf(page + sz, "size: %u\n", lv->lv_size); - sz += sprintf(page + sz, "access: %u\n", lv->lv_access); - sz += sprintf(page + sz, "status: %u\n", lv->lv_status); - sz += sprintf(page + sz, "number: %u\n", lv->lv_number); - sz += sprintf(page + sz, "open: %u\n", lv->lv_open); - sz += sprintf(page + sz, "allocation: %u\n", lv->lv_allocation); - if(lv->lv_stripes > 1) { + sz += sprintf(page + sz, "name: %s\n", lv->u.lv_name); + sz += sprintf(page + sz, "size: %u\n", lv->u.lv_size); + sz += sprintf(page + sz, "access: %u\n", lv->u.lv_access); + sz += sprintf(page + sz, "status: %u\n", lv->u.lv_status); + sz += sprintf(page + sz, "number: %u\n", lv->u.lv_number); + sz += sprintf(page + sz, "open: %u\n", lv->u.lv_open); + sz += sprintf(page + sz, "allocation: %u\n", lv->u.lv_allocation); + if(lv->u.lv_stripes > 1) { sz += sprintf(page + sz, "stripes: %u\n", - lv->lv_stripes); + lv->u.lv_stripes); sz += sprintf(page + sz, "stripesize: %u\n", - lv->lv_stripesize); + lv->u.lv_stripesize); } sz += sprintf(page + sz, "device: %02u:%02u\n", - major(lv->lv_dev), minor(lv->lv_dev)); + major(lv->u.lv_dev), minor(lv->u.lv_dev)); return sz; } @@ -350,13 +350,13 @@ if (vg_ptr->lv_cur > 0) { for (l = 0; l < vg[v]->lv_max; l++) { if ((lv_ptr = vg_ptr->lv[l]) != NULL) { - pe_t_bytes += lv_ptr->lv_allocated_le; + pe_t_bytes += lv_ptr->u.lv_allocated_le; hash_table_bytes += lv_ptr->lv_snapshot_hash_table_size; - if (lv_ptr->lv_block_exception != NULL) - lv_block_exception_t_bytes += lv_ptr->lv_remap_end; - if (lv_ptr->lv_open > 0) { + if (lv_ptr->u.lv_block_exception != NULL) + lv_block_exception_t_bytes += lv_ptr->u.lv_remap_end; + if (lv_ptr->u.lv_open > 0) { lv_open_counter++; - lv_open_total += lv_ptr->lv_open; + lv_open_total += lv_ptr->u.lv_open; } } } @@ -532,16 +532,16 @@ char inactive_flag = 'A', allocation_flag = ' ', stripes_flag = ' ', rw_flag = ' ', *basename; - if (!(lv_ptr->lv_status & LV_ACTIVE)) + if (!(lv_ptr->u.lv_status & LV_ACTIVE)) inactive_flag = 'I'; rw_flag = 'R'; - if (lv_ptr->lv_access & LV_WRITE) + if (lv_ptr->u.lv_access & LV_WRITE) rw_flag = 'W'; allocation_flag = 'D'; - if (lv_ptr->lv_allocation & LV_CONTIGUOUS) + if (lv_ptr->u.lv_allocation & LV_CONTIGUOUS) allocation_flag = 'C'; stripes_flag = 'L'; - if (lv_ptr->lv_stripes > 1) + if (lv_ptr->u.lv_stripes > 1) stripes_flag = 'S'; sz += sprintf(buf+sz, "[%c%c%c%c", @@ -549,29 +549,29 @@ rw_flag, allocation_flag, stripes_flag); - if (lv_ptr->lv_stripes > 1) + if (lv_ptr->u.lv_stripes > 1) sz += sprintf(buf+sz, "%-2d", - lv_ptr->lv_stripes); + lv_ptr->u.lv_stripes); else sz += sprintf(buf+sz, " "); /* FIXME: use _basename */ - basename = strrchr(lv_ptr->lv_name, '/'); - if ( basename == 0) basename = lv_ptr->lv_name; + basename = strrchr(lv_ptr->u.lv_name, '/'); + if ( basename == 0) basename = lv_ptr->u.lv_name; else basename++; sz += sprintf(buf+sz, "] %-25s", basename); if (strlen(basename) > 25) sz += sprintf(buf+sz, "\n "); sz += sprintf(buf+sz, "%9d /%-6d ", - lv_ptr->lv_size >> 1, - lv_ptr->lv_size / vg_ptr->pe_size); + lv_ptr->u.lv_size >> 1, + lv_ptr->u.lv_size / vg_ptr->pe_size); - if (lv_ptr->lv_open == 0) + if (lv_ptr->u.lv_open == 0) sz += sprintf(buf+sz, "close"); else sz += sprintf(buf+sz, "%dx open", - lv_ptr->lv_open); + lv_ptr->u.lv_open); return sz; } diff -Nru a/drivers/md/lvm-snap.c b/drivers/md/lvm-snap.c --- a/drivers/md/lvm-snap.c Tue Feb 19 18:08:58 2002 +++ b/drivers/md/lvm-snap.c Tue Feb 19 18:08:58 2002 @@ -93,7 +93,7 @@ { struct list_head * hash_table = lv->lv_snapshot_hash_table, * next; unsigned long mask = lv->lv_snapshot_hash_mask; - int chunk_size = lv->lv_chunk_size; + int chunk_size = lv->u.lv_chunk_size; lv_block_exception_t * ret; int i = 0; @@ -127,7 +127,7 @@ { struct list_head * hash_table = lv->lv_snapshot_hash_table; unsigned long mask = lv->lv_snapshot_hash_mask; - int chunk_size = lv->lv_chunk_size; + int chunk_size = lv->u.lv_chunk_size; hash_table = &hash_table[hashfn(org_dev, org_start, mask, chunk_size)]; list_add(&exception->hash, hash_table); @@ -139,7 +139,7 @@ int ret; unsigned long pe_off, pe_adjustment, __org_start; kdev_t __org_dev; - int chunk_size = lv->lv_chunk_size; + int chunk_size = lv->u.lv_chunk_size; lv_block_exception_t * exception; pe_off = pe_start % chunk_size; @@ -164,26 +164,26 @@ /* no exception storage space available for this snapshot or error on this snapshot --> release it */ - invalidate_buffers(lv_snap->lv_dev); + invalidate_buffers(lv_snap->u.lv_dev); /* wipe the snapshot since it's inconsistent now */ _disable_snapshot(vg, lv_snap); last_dev = NODEV; - for (i = 0; i < lv_snap->lv_remap_ptr; i++) { - if ( !kdev_same(lv_snap->lv_block_exception[i].rdev_new, + for (i = 0; i < lv_snap->u.lv_remap_ptr; i++) { + if ( !kdev_same(lv_snap->u.lv_block_exception[i].rdev_new, last_dev)) { - last_dev = lv_snap->lv_block_exception[i].rdev_new; + last_dev = lv_snap->u.lv_block_exception[i].rdev_new; invalidate_buffers(last_dev); } } lvm_snapshot_release(lv_snap); - lv_snap->lv_status &= ~LV_ACTIVE; + lv_snap->u.lv_status &= ~LV_ACTIVE; printk(KERN_INFO "%s -- giving up to snapshot %s on %s: %s\n", - lvm_name, lv_snap->lv_snapshot_org->lv_name, lv_snap->lv_name, + lvm_name, lv_snap->u.lv_snapshot_org->u.lv_name, lv_snap->u.lv_name, reason); } @@ -234,7 +234,7 @@ int lvm_snapshot_fill_COW_page(vg_t * vg, lv_t * lv_snap) { uint pvn; - int id = 0, is = lv_snap->lv_remap_ptr; + int id = 0, is = lv_snap->u.lv_remap_ptr; ulong blksize_snap; lv_COW_table_disk_t * lv_COW_table = (lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_iobuf->maplist[0]); @@ -244,13 +244,13 @@ is--; blksize_snap = - block_size(lv_snap->lv_block_exception[is].rdev_new); + block_size(lv_snap->u.lv_block_exception[is].rdev_new); is -= is % (blksize_snap / sizeof(lv_COW_table_disk_t)); memset(lv_COW_table, 0, blksize_snap); - for ( ; is < lv_snap->lv_remap_ptr; is++, id++) { + for ( ; is < lv_snap->u.lv_remap_ptr; is++, id++) { /* store new COW_table entry */ - lv_block_exception_t *be = lv_snap->lv_block_exception + is; + lv_block_exception_t *be = lv_snap->u.lv_block_exception + is; if(_pv_get_number(vg, be->rdev_org, &pvn)) goto bad; @@ -281,7 +281,7 @@ int r; const char *err; if((r = _write_COW_table_block(vg, lv_snap, - lv_snap->lv_remap_ptr - 1, &err))) + lv_snap->u.lv_remap_ptr - 1, &err))) lvm_drop_snapshot(vg, lv_snap, err); return r; } @@ -303,13 +303,13 @@ const char * reason; kdev_t snap_phys_dev; unsigned long org_start, snap_start, virt_start, pe_off; - int idx = lv_snap->lv_remap_ptr, chunk_size = lv_snap->lv_chunk_size; + int idx = lv_snap->u.lv_remap_ptr, chunk_size = lv_snap->u.lv_chunk_size; struct kiobuf * iobuf; int blksize_snap, blksize_org, min_blksize, max_blksize; int max_sectors, nr_sectors; /* check if we are out of snapshot space */ - if (idx >= lv_snap->lv_remap_end) + if (idx >= lv_snap->u.lv_remap_end) goto fail_out_of_space; /* calculate physical boundaries of source chunk */ @@ -318,8 +318,8 @@ virt_start = org_virt_sector - (org_phys_sector - org_start); /* calculate physical boundaries of destination chunk */ - snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; - snap_start = lv_snap->lv_block_exception[idx].rsector_new; + snap_phys_dev = lv_snap->u.lv_block_exception[idx].rdev_new; + snap_start = lv_snap->u.lv_block_exception[idx].rsector_new; #ifdef DEBUG_SNAPSHOT printk(KERN_INFO @@ -371,20 +371,20 @@ #ifdef DEBUG_SNAPSHOT /* invalidate the logical snapshot buffer cache */ - invalidate_snap_cache(virt_start, lv_snap->lv_chunk_size, - lv_snap->lv_dev); + invalidate_snap_cache(virt_start, lv_snap->u.lv_chunk_size, + lv_snap->u.lv_dev); #endif /* the original chunk is now stored on the snapshot volume so update the execption table */ - lv_snap->lv_block_exception[idx].rdev_org = org_phys_dev; - lv_snap->lv_block_exception[idx].rsector_org = org_start; + lv_snap->u.lv_block_exception[idx].rdev_org = org_phys_dev; + lv_snap->u.lv_block_exception[idx].rsector_org = org_start; - lvm_hash_link(lv_snap->lv_block_exception + idx, + lvm_hash_link(lv_snap->u.lv_block_exception + idx, org_phys_dev, org_start, lv_snap); - lv_snap->lv_remap_ptr = idx + 1; + lv_snap->u.lv_remap_ptr = idx + 1; if (lv_snap->lv_snapshot_use_rate > 0) { - if (lv_snap->lv_remap_ptr * 100 / lv_snap->lv_remap_end >= lv_snap->lv_snapshot_use_rate) + if (lv_snap->u.lv_remap_ptr * 100 / lv_snap->u.lv_remap_end >= lv_snap->lv_snapshot_use_rate) wake_up_interruptible(&lv_snap->lv_snapshot_wait); } return 0; @@ -462,7 +462,7 @@ unsigned long buckets, max_buckets, size; struct list_head * hash; - buckets = lv->lv_remap_end; + buckets = lv->u.lv_remap_end; max_buckets = calc_max_buckets(); buckets = min(buckets, max_buckets); while (buckets & (buckets-1)) @@ -531,10 +531,10 @@ void lvm_snapshot_release(lv_t * lv) { - if (lv->lv_block_exception) + if (lv->u.lv_block_exception) { - vfree(lv->lv_block_exception); - lv->lv_block_exception = NULL; + vfree(lv->u.lv_block_exception); + lv->u.lv_block_exception = NULL; } if (lv->lv_snapshot_hash_table) { @@ -578,8 +578,8 @@ COW_entries_per_pe = LVM_GET_COW_TABLE_ENTRIES_PER_PE(vg, lv_snap); /* get physical addresse of destination chunk */ - snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; - snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size; + snap_phys_dev = lv_snap->u.lv_block_exception[idx].rdev_new; + snap_pe_start = lv_snap->u.lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->u.lv_chunk_size; blksize_snap = block_size(snap_phys_dev); @@ -595,7 +595,7 @@ blocks[0] = (snap_pe_start + COW_table_sector_offset) >> (blksize_snap >> 10); /* store new COW_table entry */ - be = lv_snap->lv_block_exception + idx; + be = lv_snap->u.lv_block_exception + idx; if(_pv_get_number(vg, be->rdev_org, &pvn)) goto fail_pv_get_number; @@ -620,15 +620,15 @@ if (idx_COW_table % COW_entries_per_block == COW_entries_per_block - 1 || end_of_table) { /* don't go beyond the end */ - if (idx + 1 >= lv_snap->lv_remap_end) goto out; + if (idx + 1 >= lv_snap->u.lv_remap_end) goto out; memset(lv_COW_table, 0, blksize_snap); if (end_of_table) { idx++; - snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new; - snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size; + snap_phys_dev = lv_snap->u.lv_block_exception[idx].rdev_new; + snap_pe_start = lv_snap->u.lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->u.lv_chunk_size; blksize_snap = block_size(snap_phys_dev); blocks[0] = snap_pe_start >> (blksize_snap >> 10); } else blocks[0]++; @@ -664,7 +664,7 @@ static void _disable_snapshot(vg_t *vg, lv_t *lv) { const char *err; - lv->lv_block_exception[0].rsector_org = LVM_SNAPSHOT_DROPPED_SECTOR; + lv->u.lv_block_exception[0].rsector_org = LVM_SNAPSHOT_DROPPED_SECTOR; if(_write_COW_table_block(vg, lv, 0, &err) < 0) { printk(KERN_ERR "%s -- couldn't disable snapshot: %s\n", lvm_name, err); diff -Nru a/drivers/md/lvm.c b/drivers/md/lvm.c --- a/drivers/md/lvm.c Tue Feb 19 18:08:57 2002 +++ b/drivers/md/lvm.c Tue Feb 19 18:08:57 2002 @@ -182,6 +182,9 @@ * 28/12/2001 - buffer_head -> bio * removed huge allocation of a lv_t on stack * (Anders Gustafsson) + * 07/01/2002 - fixed sizeof(lv_t) differences in user/kernel-space + * removed another huge allocation of a lv_t on stack + * (Anders Gustafsson) * */ @@ -226,10 +229,6 @@ #include "lvm-internal.h" -#define LVM_CORRECT_READ_AHEAD( a) \ - if ( a < LVM_MIN_READ_AHEAD || \ - a > LVM_MAX_READ_AHEAD) a = LVM_MAX_READ_AHEAD; - #ifndef WRITEA # define WRITEA WRITE #endif @@ -270,10 +269,10 @@ static int lvm_do_pv_create(pv_t *, vg_t *, ulong); static int lvm_do_pv_remove(vg_t *, ulong); -static int lvm_do_lv_create(int, char *, lv_t *); -static int lvm_do_lv_extend_reduce(int, char *, lv_t *); +static int lvm_do_lv_create(int, char *, userlv_t *); +static int lvm_do_lv_extend_reduce(int, char *, userlv_t *); static int lvm_do_lv_remove(int, char *, int); -static int lvm_do_lv_rename(vg_t *, lv_req_t *, lv_t *); +static int lvm_do_lv_rename(vg_t *, lv_req_t *, userlv_t *); static int lvm_do_lv_status_byname(vg_t *r, void *); static int lvm_do_lv_status_byindex(vg_t *, void *); static int lvm_do_lv_status_bydev(vg_t *, void *); @@ -549,7 +548,7 @@ int minor = minor(inode->i_rdev); uint extendable, l, v; void *arg = (void *) a; - lv_t lv; + userlv_t ulv; vg_t* vg_ptr = vg[VG_CHR(minor)]; /* otherwise cc will complain about unused variables */ @@ -685,21 +684,21 @@ return -EFAULT; if (command != LV_REMOVE) { - if (copy_from_user(&lv, lv_req.lv, sizeof(lv_t)) != 0) + if (copy_from_user(&ulv, lv_req.lv, sizeof(userlv_t)) != 0) return -EFAULT; } switch (command) { case LV_CREATE: - return lvm_do_lv_create(minor, lv_req.lv_name, &lv); + return lvm_do_lv_create(minor, lv_req.lv_name, &ulv); case LV_EXTEND: case LV_REDUCE: - return lvm_do_lv_extend_reduce(minor, lv_req.lv_name, &lv); + return lvm_do_lv_extend_reduce(minor, lv_req.lv_name, &ulv); case LV_REMOVE: return lvm_do_lv_remove(minor, lv_req.lv_name, -1); case LV_RENAME: - return lvm_do_lv_rename(vg_ptr, &lv_req, &lv); + return lvm_do_lv_rename(vg_ptr, &lv_req, &ulv); } @@ -811,27 +810,27 @@ LV_BLK(minor) < vg_ptr->lv_max) { /* Check parallel LV spindown (LV remove) */ - if (lv_ptr->lv_status & LV_SPINDOWN) return -EPERM; + if (lv_ptr->u.lv_status & LV_SPINDOWN) return -EPERM; /* Check inactive LV and open for read/write */ /* We need to be able to "read" an inactive LV to re-activate it again */ if ((file->f_mode & FMODE_WRITE) && - (!(lv_ptr->lv_status & LV_ACTIVE))) + (!(lv_ptr->u.lv_status & LV_ACTIVE))) return -EPERM; - if (!(lv_ptr->lv_access & LV_WRITE) && + if (!(lv_ptr->u.lv_access & LV_WRITE) && (file->f_mode & FMODE_WRITE)) return -EACCES; /* be sure to increment VG counter */ - if (lv_ptr->lv_open == 0) vg_ptr->lv_open++; - lv_ptr->lv_open++; + if (lv_ptr->u.lv_open == 0) vg_ptr->lv_open++; + lv_ptr->u.lv_open++; MOD_INC_USE_COUNT; - P_DEV("blk_open OK, LV size %d\n", lv_ptr->lv_size); + P_DEV("blk_open OK, LV size %d\n", lv_ptr->u.lv_size); return 0; } @@ -862,13 +861,13 @@ case BLKGETSIZE: /* return device size */ - P_IOCTL("BLKGETSIZE: %u\n", lv_ptr->lv_size); - if (put_user(lv_ptr->lv_size, (unsigned long *)arg)) + P_IOCTL("BLKGETSIZE: %u\n", lv_ptr->u.lv_size); + if (put_user(lv_ptr->u.lv_size, (unsigned long *)arg)) return -EFAULT; break; case BLKGETSIZE64: - if (put_user((u64)lv_ptr->lv_size << 9, (u64 *)arg)) + if (put_user((u64)lv_ptr->u.lv_size << 9, (u64 *)arg)) return -EFAULT; break; @@ -883,29 +882,6 @@ invalidate_buffers(inode->i_rdev); break; - - case BLKRASET: - /* set read ahead for block device */ - if (!capable(CAP_SYS_ADMIN)) return -EACCES; - - P_IOCTL("BLKRASET: %ld sectors for %s\n", - (long) arg, kdevname(inode->i_rdev)); - - if ((long) arg < LVM_MIN_READ_AHEAD || - (long) arg > LVM_MAX_READ_AHEAD) - return -EINVAL; - lv_ptr->lv_read_ahead = (long) arg; - break; - - - case BLKRAGET: - /* get current read ahead setting */ - P_IOCTL("BLKRAGET %d\n", lv_ptr->lv_read_ahead); - if (put_user(lv_ptr->lv_read_ahead, (long *)arg)) - return -EFAULT; - break; - - case HDIO_GETGEO: /* get disk geometry */ P_IOCTL("%s -- lvm_blk_ioctl -- HDIO_GETGEO\n", lvm_name); @@ -915,7 +891,7 @@ unsigned char heads = 64; unsigned char sectors = 32; long start = 0; - short cylinders = lv_ptr->lv_size / heads / sectors; + short cylinders = lv_ptr->u.lv_size / heads / sectors; if (copy_to_user((char *) &hd->heads, &heads, sizeof(heads)) != 0 || @@ -936,26 +912,26 @@ case LV_SET_ACCESS: /* set access flags of a logical volume */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; - lv_ptr->lv_access = (ulong) arg; - if ( lv_ptr->lv_access & LV_WRITE) - set_device_ro(lv_ptr->lv_dev, 0); + lv_ptr->u.lv_access = (ulong) arg; + if ( lv_ptr->u.lv_access & LV_WRITE) + set_device_ro(lv_ptr->u.lv_dev, 0); else - set_device_ro(lv_ptr->lv_dev, 1); + set_device_ro(lv_ptr->u.lv_dev, 1); break; case LV_SET_STATUS: /* set status flags of a logical volume */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (!((ulong) arg & LV_ACTIVE) && lv_ptr->lv_open > 1) + if (!((ulong) arg & LV_ACTIVE) && lv_ptr->u.lv_open > 1) return -EPERM; - lv_ptr->lv_status = (ulong) arg; + lv_ptr->u.lv_status = (ulong) arg; break; case LV_BMAP: /* turn logical block into (dev_t, block). non privileged. */ /* don't bmap a snapshot, since the mapping can change */ - if(lv_ptr->lv_access & LV_SNAPSHOT) + if(lv_ptr->u.lv_access & LV_SNAPSHOT) return -EPERM; return lvm_user_bmap(inode, (struct lv_bmap *) arg); @@ -963,7 +939,7 @@ case LV_SET_ALLOCATION: /* set allocation flags of a logical volume */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; - lv_ptr->lv_allocation = (ulong) arg; + lv_ptr->u.lv_allocation = (ulong) arg; break; case LV_SNAPSHOT_USE_RATE: @@ -992,8 +968,8 @@ P_DEV("blk_close MINOR: %d VG#: %d LV#: %d\n", minor, VG_BLK(minor), LV_BLK(minor)); - if (lv_ptr->lv_open == 1) vg_ptr->lv_open--; - lv_ptr->lv_open--; + if (lv_ptr->u.lv_open == 1) vg_ptr->lv_open--; + lv_ptr->u.lv_open--; MOD_DEC_USE_COUNT; @@ -1004,7 +980,7 @@ { lv_snapshot_use_rate_req_t lv_rate_req; - if (!(lv->lv_access & LV_SNAPSHOT)) + if (!(lv->u.lv_access & LV_SNAPSHOT)) return -EPERM; if (copy_from_user(&lv_rate_req, arg, sizeof(lv_rate_req))) @@ -1016,7 +992,7 @@ switch (lv_rate_req.block) { case 0: lv->lv_snapshot_use_rate = lv_rate_req.rate; - if (lv->lv_remap_ptr * 100 / lv->lv_remap_end < + if (lv->u.lv_remap_ptr * 100 / lv->u.lv_remap_end < lv->lv_snapshot_use_rate) interruptible_sleep_on(&lv->lv_snapshot_wait); break; @@ -1027,7 +1003,7 @@ default: return -EINVAL; } - lv_rate_req.rate = lv->lv_remap_ptr * 100 / lv->lv_remap_end; + lv_rate_req.rate = lv->u.lv_remap_ptr * 100 / lv->u.lv_remap_end; return copy_to_user(arg, &lv_rate_req, sizeof(lv_rate_req)) ? -EFAULT : 0; @@ -1044,7 +1020,7 @@ memset(&bio,0,sizeof(bio)); bio.bi_dev = inode->i_rdev; - bio.bi_size = lvm_get_blksize(bio.bi_dev); /* NEEDED by bio_sectors */ + bio.bi_size = block_size(bio.bi_dev); /* NEEDED by bio_sectors */ bio.bi_sector = block * bio_sectors(&bio); bio.bi_rw = READ; if ((err=lvm_map(&bio)) < 0) { @@ -1134,18 +1110,18 @@ int rw = bio_rw(bi); down_read(&lv->lv_lock); - if (!(lv->lv_status & LV_ACTIVE)) { + if (!(lv->u.lv_status & LV_ACTIVE)) { printk(KERN_ALERT "%s - lvm_map: ll_rw_blk for inactive LV %s\n", - lvm_name, lv->lv_name); + lvm_name, lv->u.lv_name); goto bad; } if ((rw == WRITE || rw == WRITEA) && - !(lv->lv_access & LV_WRITE)) { + !(lv->u.lv_access & LV_WRITE)) { printk(KERN_CRIT "%s - lvm_map: ll_rw_blk write for readonly LV %s\n", - lvm_name, lv->lv_name); + lvm_name, lv->u.lv_name); goto bad; } @@ -1154,7 +1130,7 @@ kdevname(bi->bi_dev), rsector_org, size); - if (rsector_org + size > lv->lv_size) { + if (rsector_org + size > lv->u.lv_size) { printk(KERN_ALERT "%s - lvm_map access beyond end of device; *rsector: " "%lu or size: %lu wrong for minor: %2d\n", @@ -1163,39 +1139,39 @@ } - if (lv->lv_stripes < 2) { /* linear mapping */ + if (lv->u.lv_stripes < 2) { /* linear mapping */ /* get the index */ index = rsector_org / vg_this->pe_size; - pe_start = lv->lv_current_pe[index].pe; - rsector_map = lv->lv_current_pe[index].pe + + pe_start = lv->u.lv_current_pe[index].pe; + rsector_map = lv->u.lv_current_pe[index].pe + (rsector_org % vg_this->pe_size); - rdev_map = lv->lv_current_pe[index].dev; + rdev_map = lv->u.lv_current_pe[index].dev; - P_MAP("lv_current_pe[%ld].pe: %d rdev: %s rsector:%ld\n", - index, lv->lv_current_pe[index].pe, + P_MAP("u.lv_current_pe[%ld].pe: %d rdev: %s rsector:%ld\n", + index, lv->u.lv_current_pe[index].pe, kdevname(rdev_map), rsector_map); } else { /* striped mapping */ ulong stripe_index; ulong stripe_length; - stripe_length = vg_this->pe_size * lv->lv_stripes; + stripe_length = vg_this->pe_size * lv->u.lv_stripes; stripe_index = (rsector_org % stripe_length) / - lv->lv_stripesize; + lv->u.lv_stripesize; index = rsector_org / stripe_length + - (stripe_index % lv->lv_stripes) * - (lv->lv_allocated_le / lv->lv_stripes); - pe_start = lv->lv_current_pe[index].pe; - rsector_map = lv->lv_current_pe[index].pe + + (stripe_index % lv->u.lv_stripes) * + (lv->u.lv_allocated_le / lv->u.lv_stripes); + pe_start = lv->u.lv_current_pe[index].pe; + rsector_map = lv->u.lv_current_pe[index].pe + (rsector_org % stripe_length) - - (stripe_index % lv->lv_stripes) * lv->lv_stripesize - - stripe_index / lv->lv_stripes * - (lv->lv_stripes - 1) * lv->lv_stripesize; - rdev_map = lv->lv_current_pe[index].dev; + (stripe_index % lv->u.lv_stripes) * lv->u.lv_stripesize - + stripe_index / lv->u.lv_stripes * + (lv->u.lv_stripes - 1) * lv->u.lv_stripesize; + rdev_map = lv->u.lv_current_pe[index].dev; - P_MAP("lv_current_pe[%ld].pe: %d rdev: %s rsector:%ld\n" + P_MAP("u.lv_current_pe[%ld].pe: %d rdev: %s rsector:%ld\n" "stripe_length: %ld stripe_index: %ld\n", - index, lv->lv_current_pe[index].pe, kdevname(rdev_map), + index, lv->u.lv_current_pe[index].pe, kdevname(rdev_map), rsector_map, stripe_length, stripe_index); } @@ -1212,16 +1188,16 @@ return 0; } - lv->lv_current_pe[index].writes++; /* statistic */ + lv->u.lv_current_pe[index].writes++; /* statistic */ } else - lv->lv_current_pe[index].reads++; /* statistic */ + lv->u.lv_current_pe[index].reads++; /* statistic */ /* snapshot volume exception handling on physical device address base */ - if (!(lv->lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG))) + if (!(lv->u.lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG))) goto out; - if (lv->lv_access & LV_SNAPSHOT) { /* remap snapshot */ - if (lv->lv_block_exception) + if (lv->u.lv_access & LV_SNAPSHOT) { /* remap snapshot */ + if (lv->u.lv_block_exception) lvm_snapshot_remap_block(&rdev_map, &rsector_map, pe_start, lv); else @@ -1232,10 +1208,10 @@ /* start with first snapshot and loop through all of them */ - for (snap = lv->lv_snapshot_next; snap; - snap = snap->lv_snapshot_next) { + for (snap = lv->u.lv_snapshot_next; snap; + snap = snap->u.lv_snapshot_next) { /* Check for inactive snapshot */ - if (!(snap->lv_status & LV_ACTIVE)) + if (!(snap->u.lv_status & LV_ACTIVE)) continue; /* Serializes the COW with the accesses to the @@ -1274,8 +1250,8 @@ if (vg[VG_BLK(minor)] == NULL || (lv_ptr = vg[VG_BLK(minor)]->lv[LV_BLK(minor)]) == NULL) return; - len = strlen(lv_ptr->lv_name) - 5; - memcpy(buf, &lv_ptr->lv_name[5], len); + len = strlen(lv_ptr->u.lv_name) - 5; + memcpy(buf, &lv_ptr->u.lv_name[5], len); buf[len] = 0; return; } @@ -1307,7 +1283,7 @@ P_DEV("lvm_do_lock_lvm: locked by pid %d ...\n", lock); spin_unlock(&lvm_lock); interruptible_sleep_on(&lvm_wait); - if (current->sigpending != 0) + if (signal_pending(current)) return -EINTR; #ifdef LVM_TOTAL_RESET if (lvm_reset_spindown > 0) @@ -1360,7 +1336,7 @@ return -EBUSY; } - /* Should we do to_kdev_t() on the pv_dev and lv_dev??? */ + /* Should we do to_kdev_t() on the pv_dev and u.lv_dev??? */ pe_lock_req.lock = LOCK_PE; pe_lock_req.data.lv_dev = new_lock.data.lv_dev; pe_lock_req.data.pv_dev = new_lock.data.pv_dev; @@ -1407,16 +1383,16 @@ for (l = 0; l < vg_ptr->lv_max; l++) { lv_ptr = vg_ptr->lv[l]; if (lv_ptr != NULL && - strcmp(lv_ptr->lv_name, + strcmp(lv_ptr->u.lv_name, le_remap_req.lv_name) == 0) { - for (le = 0; le < lv_ptr->lv_allocated_le; le++) { - if (kdev_same(lv_ptr->lv_current_pe[le].dev, + for (le = 0; le < lv_ptr->u.lv_allocated_le; le++) { + if (kdev_same(lv_ptr->u.lv_current_pe[le].dev, le_remap_req.old_dev) && - lv_ptr->lv_current_pe[le].pe == + lv_ptr->u.lv_current_pe[le].pe == le_remap_req.old_pe) { - lv_ptr->lv_current_pe[le].dev = + lv_ptr->u.lv_current_pe[le].dev = le_remap_req.new_dev; - lv_ptr->lv_current_pe[le].pe = + lv_ptr->u.lv_current_pe[le].pe = le_remap_req.new_pe; __update_hardsectsize(lv_ptr); @@ -1532,7 +1508,7 @@ /* user space address */ if ((lvp = vg_ptr->lv[l]) != NULL) { - if (copy_from_user(tmplv, lvp, sizeof(lv_t)) != 0) { + if (copy_from_user(tmplv, lvp, sizeof(userlv_t)) != 0) { P_IOCTL("ERROR: copying LV ptr %p (%d bytes)\n", lvp, sizeof(lv_t)); lvm_do_vg_remove(minor); @@ -1540,7 +1516,7 @@ kfree(tmplv); return -EFAULT; } - if ( tmplv->lv_access & LV_SNAPSHOT) { + if ( tmplv->u.lv_access & LV_SNAPSHOT) { snap_lv_ptr[ls] = lvp; vg_ptr->lv[l] = NULL; ls++; @@ -1548,7 +1524,7 @@ } vg_ptr->lv[l] = NULL; /* only create original logical volumes for now */ - if (lvm_do_lv_create(minor, tmplv->lv_name, tmplv) != 0) { + if (lvm_do_lv_create(minor, tmplv->u.lv_name, &tmplv->u) != 0) { lvm_do_vg_remove(minor); vfree(snap_lv_ptr); kfree(tmplv); @@ -1561,13 +1537,13 @@ in place during first path above */ for (l = 0; l < ls; l++) { lv_t *lvp = snap_lv_ptr[l]; - if (copy_from_user(tmplv, lvp, sizeof(lv_t)) != 0) { + if (copy_from_user(tmplv, lvp, sizeof(userlv_t)) != 0) { lvm_do_vg_remove(minor); vfree(snap_lv_ptr); kfree(tmplv); return -EFAULT; } - if (lvm_do_lv_create(minor, tmplv->lv_name, tmplv) != 0) { + if (lvm_do_lv_create(minor, tmplv->u.lv_name, &tmplv->u) != 0) { lvm_do_vg_remove(minor); vfree(snap_lv_ptr); kfree(tmplv); @@ -1666,15 +1642,15 @@ for ( l = 0; l < vg_ptr->lv_max; l++) { if ((lv_ptr = vg_ptr->lv[l]) == NULL) continue; - strncpy(lv_ptr->vg_name, vg_name, sizeof ( vg_name)); - ptr = strrchr(lv_ptr->lv_name, '/'); - if (ptr == NULL) ptr = lv_ptr->lv_name; + strncpy(lv_ptr->u.vg_name, vg_name, sizeof ( vg_name)); + ptr = strrchr(lv_ptr->u.lv_name, '/'); + if (ptr == NULL) ptr = lv_ptr->u.lv_name; strncpy(lv_name, ptr, sizeof ( lv_name)); len = sizeof(LVM_DIR_PREFIX); - strcpy(lv_ptr->lv_name, LVM_DIR_PREFIX); - strncat(lv_ptr->lv_name, vg_name, NAME_LEN - len); + strcpy(lv_ptr->u.lv_name, LVM_DIR_PREFIX); + strncat(lv_ptr->u.lv_name, vg_name, NAME_LEN - len); len += strlen ( vg_name); - strncat(lv_ptr->lv_name, lv_name, NAME_LEN - len); + strncat(lv_ptr->u.lv_name, lv_name, NAME_LEN - len); } for ( p = 0; p < vg_ptr->pv_max; p++) { @@ -1716,7 +1692,7 @@ /* first free snapshot logical volumes */ for (i = 0; i < vg_ptr->lv_max; i++) { if (vg_ptr->lv[i] != NULL && - vg_ptr->lv[i]->lv_access & LV_SNAPSHOT) { + vg_ptr->lv[i]->u.lv_access & LV_SNAPSHOT) { lvm_do_lv_remove(minor, NULL, i); current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(1); @@ -1819,8 +1795,8 @@ int le, e; int max_hardsectsize = 0, hardsectsize; - for (le = 0; le < lv->lv_allocated_le; le++) { - hardsectsize = get_hardsect_size(lv->lv_current_pe[le].dev); + for (le = 0; le < lv->u.lv_allocated_le; le++) { + hardsectsize = get_hardsect_size(lv->u.lv_current_pe[le].dev); if (hardsectsize == 0) hardsectsize = 512; if (hardsectsize > max_hardsectsize) @@ -1828,10 +1804,10 @@ } /* only perform this operation on active snapshots */ - if ((lv->lv_access & LV_SNAPSHOT) && - (lv->lv_status & LV_ACTIVE)) { - for (e = 0; e < lv->lv_remap_end; e++) { - hardsectsize = get_hardsect_size( lv->lv_block_exception[e].rdev_new); + if ((lv->u.lv_access & LV_SNAPSHOT) && + (lv->u.lv_status & LV_ACTIVE)) { + for (e = 0; e < lv->u.lv_remap_end; e++) { + hardsectsize = get_hardsect_size( lv->u.lv_block_exception[e].rdev_new); if (hardsectsize == 0) hardsectsize = 512; if (hardsectsize > max_hardsectsize) @@ -1843,31 +1819,31 @@ /* * character device support function logical volume create */ -static int lvm_do_lv_create(int minor, char *lv_name, lv_t *lv) +static int lvm_do_lv_create(int minor, char *lv_name, userlv_t *ulv) { int e, ret, l, le, l_new, p, size, activate = 1; ulong lv_status_save; - lv_block_exception_t *lvbe = lv->lv_block_exception; + lv_block_exception_t *lvbe = ulv->lv_block_exception; vg_t *vg_ptr = vg[VG_CHR(minor)]; lv_t *lv_ptr = NULL; pe_t *pep; - if (!(pep = lv->lv_current_pe)) + if (!(pep = ulv->lv_current_pe)) return -EINVAL; - if (_sectors_to_k(lv->lv_chunk_size) > LVM_SNAPSHOT_MAX_CHUNK) + if (_sectors_to_k(ulv->lv_chunk_size) > LVM_SNAPSHOT_MAX_CHUNK) return -EINVAL; for (l = 0; l < vg_ptr->lv_cur; l++) { if (vg_ptr->lv[l] != NULL && - strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0) + strcmp(vg_ptr->lv[l]->u.lv_name, lv_name) == 0) return -EEXIST; } /* in case of lv_remove(), lv_create() pair */ l_new = -1; - if (vg_ptr->lv[lv->lv_number] == NULL) - l_new = lv->lv_number; + if (vg_ptr->lv[ulv->lv_number] == NULL) + l_new = ulv->lv_number; else { for (l = 0; l < vg_ptr->lv_max; l++) { if (vg_ptr->lv[l] == NULL) @@ -1883,16 +1859,16 @@ return -ENOMEM; } /* copy preloaded LV */ - memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t)); + memcpy((char *) lv_ptr, (char *) ulv, sizeof(userlv_t)); - lv_status_save = lv_ptr->lv_status; - lv_ptr->lv_status &= ~LV_ACTIVE; - lv_ptr->lv_snapshot_org = NULL; - lv_ptr->lv_snapshot_prev = NULL; - lv_ptr->lv_snapshot_next = NULL; - lv_ptr->lv_block_exception = NULL; + lv_status_save = lv_ptr->u.lv_status; + lv_ptr->u.lv_status &= ~LV_ACTIVE; + lv_ptr->u.lv_snapshot_org = NULL; + lv_ptr->u.lv_snapshot_prev = NULL; + lv_ptr->u.lv_snapshot_next = NULL; + lv_ptr->u.lv_block_exception = NULL; lv_ptr->lv_iobuf = NULL; - lv_ptr->lv_COW_table_iobuf = NULL; + lv_ptr->lv_COW_table_iobuf = NULL; lv_ptr->lv_snapshot_hash_table = NULL; lv_ptr->lv_snapshot_hash_table_size = 0; lv_ptr->lv_snapshot_hash_mask = 0; @@ -1904,10 +1880,10 @@ /* get the PE structures from user space if this is not a snapshot logical volume */ - if (!(lv_ptr->lv_access & LV_SNAPSHOT)) { - size = lv_ptr->lv_allocated_le * sizeof(pe_t); + if (!(lv_ptr->u.lv_access & LV_SNAPSHOT)) { + size = lv_ptr->u.lv_allocated_le * sizeof(pe_t); - if ((lv_ptr->lv_current_pe = vmalloc(size)) == NULL) { + if ((lv_ptr->u.lv_current_pe = vmalloc(size)) == NULL) { printk(KERN_CRIT "%s -- LV_CREATE: vmalloc error LV_CURRENT_PE of %d Byte " "at line %d\n", @@ -1917,30 +1893,30 @@ vg_ptr->lv[l] = NULL; return -ENOMEM; } - if (copy_from_user(lv_ptr->lv_current_pe, pep, size)) { + if (copy_from_user(lv_ptr->u.lv_current_pe, pep, size)) { P_IOCTL("ERROR: copying PE ptr %p (%d bytes)\n", pep, sizeof(size)); - vfree(lv_ptr->lv_current_pe); + vfree(lv_ptr->u.lv_current_pe); kfree(lv_ptr); vg_ptr->lv[l] = NULL; return -EFAULT; } /* correct the PE count in PVs */ - for (le = 0; le < lv_ptr->lv_allocated_le; le++) { + for (le = 0; le < lv_ptr->u.lv_allocated_le; le++) { vg_ptr->pe_allocated++; for (p = 0; p < vg_ptr->pv_cur; p++) { if (kdev_same(vg_ptr->pv[p]->pv_dev, - lv_ptr->lv_current_pe[le].dev)) + lv_ptr->u.lv_current_pe[le].dev)) vg_ptr->pv[p]->pe_allocated++; } } } else { /* Get snapshot exception data and block list */ if (lvbe != NULL) { - lv_ptr->lv_snapshot_org = - vg_ptr->lv[LV_BLK(lv_ptr->lv_snapshot_minor)]; - if (lv_ptr->lv_snapshot_org != NULL) { - size = lv_ptr->lv_remap_end * sizeof(lv_block_exception_t); + lv_ptr->u.lv_snapshot_org = + vg_ptr->lv[LV_BLK(lv_ptr->u.lv_snapshot_minor)]; + if (lv_ptr->u.lv_snapshot_org != NULL) { + size = lv_ptr->u.lv_remap_end * sizeof(lv_block_exception_t); if(!size) { printk(KERN_WARNING @@ -1950,7 +1926,7 @@ return -EINVAL; } - if ((lv_ptr->lv_block_exception = vmalloc(size)) == NULL) { + if ((lv_ptr->u.lv_block_exception = vmalloc(size)) == NULL) { printk(KERN_CRIT "%s -- lvm_do_lv_create: vmalloc error LV_BLOCK_EXCEPTION " "of %d byte at line %d\n", @@ -1961,14 +1937,14 @@ vg_ptr->lv[l] = NULL; return -ENOMEM; } - if (copy_from_user(lv_ptr->lv_block_exception, lvbe, size)) { - vfree(lv_ptr->lv_block_exception); + if (copy_from_user(lv_ptr->u.lv_block_exception, lvbe, size)) { + vfree(lv_ptr->u.lv_block_exception); kfree(lv_ptr); vg_ptr->lv[l] = NULL; return -EFAULT; } - if(lv_ptr->lv_block_exception[0].rsector_org == + if(lv_ptr->u.lv_block_exception[0].rsector_org == LVM_SNAPSHOT_DROPPED_SECTOR) { printk(KERN_WARNING @@ -1979,36 +1955,36 @@ /* point to the original logical volume */ - lv_ptr = lv_ptr->lv_snapshot_org; + lv_ptr = lv_ptr->u.lv_snapshot_org; - lv_ptr->lv_snapshot_minor = 0; - lv_ptr->lv_snapshot_org = lv_ptr; + lv_ptr->u.lv_snapshot_minor = 0; + lv_ptr->u.lv_snapshot_org = lv_ptr; /* our new one now back points to the previous last in the chain which can be the original logical volume */ lv_ptr = vg_ptr->lv[l]; /* now lv_ptr points to our new last snapshot logical volume */ - lv_ptr->lv_current_pe = lv_ptr->lv_snapshot_org->lv_current_pe; - lv_ptr->lv_allocated_snapshot_le = lv_ptr->lv_allocated_le; - lv_ptr->lv_allocated_le = lv_ptr->lv_snapshot_org->lv_allocated_le; - lv_ptr->lv_current_le = lv_ptr->lv_snapshot_org->lv_current_le; - lv_ptr->lv_size = lv_ptr->lv_snapshot_org->lv_size; - lv_ptr->lv_stripes = lv_ptr->lv_snapshot_org->lv_stripes; - lv_ptr->lv_stripesize = lv_ptr->lv_snapshot_org->lv_stripesize; + lv_ptr->u.lv_current_pe = lv_ptr->u.lv_snapshot_org->u.lv_current_pe; + lv_ptr->lv_allocated_snapshot_le = lv_ptr->u.lv_allocated_le; + lv_ptr->u.lv_allocated_le = lv_ptr->u.lv_snapshot_org->u.lv_allocated_le; + lv_ptr->u.lv_current_le = lv_ptr->u.lv_snapshot_org->u.lv_current_le; + lv_ptr->u.lv_size = lv_ptr->u.lv_snapshot_org->u.lv_size; + lv_ptr->u.lv_stripes = lv_ptr->u.lv_snapshot_org->u.lv_stripes; + lv_ptr->u.lv_stripesize = lv_ptr->u.lv_snapshot_org->u.lv_stripesize; /* Update the VG PE(s) used by snapshot reserve space. */ vg_ptr->pe_allocated += lv_ptr->lv_allocated_snapshot_le; if ((ret = lvm_snapshot_alloc(lv_ptr)) != 0) { - vfree(lv_ptr->lv_block_exception); + vfree(lv_ptr->u.lv_block_exception); kfree(lv_ptr); vg_ptr->lv[l] = NULL; return ret; } - for ( e = 0; e < lv_ptr->lv_remap_ptr; e++) - lvm_hash_link (lv_ptr->lv_block_exception + e, - lv_ptr->lv_block_exception[e].rdev_org, - lv_ptr->lv_block_exception[e].rsector_org, lv_ptr); + for ( e = 0; e < lv_ptr->u.lv_remap_ptr; e++) + lvm_hash_link (lv_ptr->u.lv_block_exception + e, + lv_ptr->u.lv_block_exception[e].rdev_org, + lv_ptr->u.lv_block_exception[e].rsector_org, lv_ptr); /* need to fill the COW exception table data into the page for disk i/o */ if(lvm_snapshot_fill_COW_page(vg_ptr, lv_ptr)) { @@ -2027,62 +2003,61 @@ vg_ptr->lv[l] = NULL; return -EINVAL; } - } /* if ( vg[VG_CHR(minor)]->lv[l]->lv_access & LV_SNAPSHOT) */ + } /* if ( vg[VG_CHR(minor)]->lv[l]->u.lv_access & LV_SNAPSHOT) */ lv_ptr = vg_ptr->lv[l]; - lvm_gendisk.part[minor(lv_ptr->lv_dev)].start_sect = 0; - lvm_gendisk.part[minor(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size; - lvm_size[minor(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1; - vg_lv_map[minor(lv_ptr->lv_dev)].vg_number = vg_ptr->vg_number; - vg_lv_map[minor(lv_ptr->lv_dev)].lv_number = lv_ptr->lv_number; - LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead); + lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].start_sect = 0; + lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].nr_sects = lv_ptr->u.lv_size; + lvm_size[minor(lv_ptr->u.lv_dev)] = lv_ptr->u.lv_size >> 1; + vg_lv_map[minor(lv_ptr->u.lv_dev)].vg_number = vg_ptr->vg_number; + vg_lv_map[minor(lv_ptr->u.lv_dev)].lv_number = lv_ptr->u.lv_number; vg_ptr->lv_cur++; - lv_ptr->lv_status = lv_status_save; + lv_ptr->u.lv_status = lv_status_save; __update_hardsectsize(lv_ptr); /* optionally add our new snapshot LV */ - if (lv_ptr->lv_access & LV_SNAPSHOT) { - lv_t *org = lv_ptr->lv_snapshot_org, *last; + if (lv_ptr->u.lv_access & LV_SNAPSHOT) { + lv_t *org = lv_ptr->u.lv_snapshot_org, *last; /* sync the original logical volume */ - fsync_dev(org->lv_dev); + fsync_dev(org->u.lv_dev); #ifdef LVM_VFS_ENHANCEMENT /* VFS function call to sync and lock the filesystem */ - fsync_dev_lockfs(org->lv_dev); + fsync_dev_lockfs(org->u.lv_dev); #endif down_write(&org->lv_lock); - org->lv_access |= LV_SNAPSHOT_ORG; - lv_ptr->lv_access &= ~LV_SNAPSHOT_ORG; /* this can only hide an userspace bug */ + org->u.lv_access |= LV_SNAPSHOT_ORG; + lv_ptr->u.lv_access &= ~LV_SNAPSHOT_ORG; /* this can only hide an userspace bug */ /* Link in the list of snapshot volumes */ - for (last = org; last->lv_snapshot_next; last = last->lv_snapshot_next); - lv_ptr->lv_snapshot_prev = last; - last->lv_snapshot_next = lv_ptr; + for (last = org; last->u.lv_snapshot_next; last = last->u.lv_snapshot_next); + lv_ptr->u.lv_snapshot_prev = last; + last->u.lv_snapshot_next = lv_ptr; up_write(&org->lv_lock); } /* activate the logical volume */ if(activate) - lv_ptr->lv_status |= LV_ACTIVE; + lv_ptr->u.lv_status |= LV_ACTIVE; else - lv_ptr->lv_status &= ~LV_ACTIVE; + lv_ptr->u.lv_status &= ~LV_ACTIVE; - if ( lv_ptr->lv_access & LV_WRITE) - set_device_ro(lv_ptr->lv_dev, 0); + if ( lv_ptr->u.lv_access & LV_WRITE) + set_device_ro(lv_ptr->u.lv_dev, 0); else - set_device_ro(lv_ptr->lv_dev, 1); + set_device_ro(lv_ptr->u.lv_dev, 1); #ifdef LVM_VFS_ENHANCEMENT /* VFS function call to unlock the filesystem */ - if (lv_ptr->lv_access & LV_SNAPSHOT) - unlockfs(lv_ptr->lv_snapshot_org->lv_dev); + if (lv_ptr->u.lv_access & LV_SNAPSHOT) + unlockfs(lv_ptr->u.lv_snapshot_org->u.lv_dev); #endif lv_ptr->vg = vg_ptr; - lvm_gendisk.part[minor(lv_ptr->lv_dev)].de = + lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].de = lvm_fs_create_lv(vg_ptr, lv_ptr); return 0; @@ -2101,7 +2076,7 @@ if (l == -1) { for (l = 0; l < vg_ptr->lv_max; l++) { if (vg_ptr->lv[l] != NULL && - strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0) { + strcmp(vg_ptr->lv[l]->u.lv_name, lv_name) == 0) { break; } } @@ -2110,38 +2085,38 @@ lv_ptr = vg_ptr->lv[l]; #ifdef LVM_TOTAL_RESET - if (lv_ptr->lv_open > 0 && lvm_reset_spindown == 0) + if (lv_ptr->u.lv_open > 0 && lvm_reset_spindown == 0) #else - if (lv_ptr->lv_open > 0) + if (lv_ptr->u.lv_open > 0) #endif return -EBUSY; /* check for deletion of snapshot source while snapshot volume still exists */ - if ((lv_ptr->lv_access & LV_SNAPSHOT_ORG) && - lv_ptr->lv_snapshot_next != NULL) + if ((lv_ptr->u.lv_access & LV_SNAPSHOT_ORG) && + lv_ptr->u.lv_snapshot_next != NULL) return -EPERM; lvm_fs_remove_lv(vg_ptr, lv_ptr); - if (lv_ptr->lv_access & LV_SNAPSHOT) { + if (lv_ptr->u.lv_access & LV_SNAPSHOT) { /* * Atomically make the the snapshot invisible * to the original lv before playing with it. */ - lv_t * org = lv_ptr->lv_snapshot_org; + lv_t * org = lv_ptr->u.lv_snapshot_org; down_write(&org->lv_lock); /* remove this snapshot logical volume from the chain */ - lv_ptr->lv_snapshot_prev->lv_snapshot_next = lv_ptr->lv_snapshot_next; - if (lv_ptr->lv_snapshot_next != NULL) { - lv_ptr->lv_snapshot_next->lv_snapshot_prev = - lv_ptr->lv_snapshot_prev; + lv_ptr->u.lv_snapshot_prev->u.lv_snapshot_next = lv_ptr->u.lv_snapshot_next; + if (lv_ptr->u.lv_snapshot_next != NULL) { + lv_ptr->u.lv_snapshot_next->u.lv_snapshot_prev = + lv_ptr->u.lv_snapshot_prev; } /* no more snapshots? */ - if (!org->lv_snapshot_next) { - org->lv_access &= ~LV_SNAPSHOT_ORG; + if (!org->u.lv_snapshot_next) { + org->u.lv_access &= ~LV_SNAPSHOT_ORG; } up_write(&org->lv_lock); @@ -2151,41 +2126,41 @@ vg_ptr->pe_allocated -= lv_ptr->lv_allocated_snapshot_le; } - lv_ptr->lv_status |= LV_SPINDOWN; + lv_ptr->u.lv_status |= LV_SPINDOWN; /* sync the buffers */ - fsync_dev(lv_ptr->lv_dev); + fsync_dev(lv_ptr->u.lv_dev); - lv_ptr->lv_status &= ~LV_ACTIVE; + lv_ptr->u.lv_status &= ~LV_ACTIVE; /* invalidate the buffers */ - invalidate_buffers(lv_ptr->lv_dev); + invalidate_buffers(lv_ptr->u.lv_dev); /* reset generic hd */ - lvm_gendisk.part[minor(lv_ptr->lv_dev)].start_sect = -1; - lvm_gendisk.part[minor(lv_ptr->lv_dev)].nr_sects = 0; - lvm_gendisk.part[minor(lv_ptr->lv_dev)].de = 0; - lvm_size[minor(lv_ptr->lv_dev)] = 0; + lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].start_sect = -1; + lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].nr_sects = 0; + lvm_gendisk.part[minor(lv_ptr->u.lv_dev)].de = 0; + lvm_size[minor(lv_ptr->u.lv_dev)] = 0; /* reset VG/LV mapping */ - vg_lv_map[minor(lv_ptr->lv_dev)].vg_number = ABS_MAX_VG; - vg_lv_map[minor(lv_ptr->lv_dev)].lv_number = -1; + vg_lv_map[minor(lv_ptr->u.lv_dev)].vg_number = ABS_MAX_VG; + vg_lv_map[minor(lv_ptr->u.lv_dev)].lv_number = -1; /* correct the PE count in PVs if this is not a snapshot logical volume */ - if (!(lv_ptr->lv_access & LV_SNAPSHOT)) { + if (!(lv_ptr->u.lv_access & LV_SNAPSHOT)) { /* only if this is no snapshot logical volume because - we share the lv_current_pe[] structs with the + we share the u.lv_current_pe[] structs with the original logical volume */ - for (le = 0; le < lv_ptr->lv_allocated_le; le++) { + for (le = 0; le < lv_ptr->u.lv_allocated_le; le++) { vg_ptr->pe_allocated--; for (p = 0; p < vg_ptr->pv_cur; p++) { if (kdev_same(vg_ptr->pv[p]->pv_dev, - lv_ptr->lv_current_pe[le].dev)) + lv_ptr->u.lv_current_pe[le].dev)) vg_ptr->pv[p]->pe_allocated--; } } - vfree(lv_ptr->lv_current_pe); + vfree(lv_ptr->u.lv_current_pe); } P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__); @@ -2203,10 +2178,10 @@ ulong size; lv_block_exception_t *lvbe; - if (!new_lv->lv_block_exception) + if (!new_lv->u.lv_block_exception) return -ENXIO; - size = new_lv->lv_remap_end * sizeof(lv_block_exception_t); + size = new_lv->u.lv_remap_end * sizeof(lv_block_exception_t); if ((lvbe = vmalloc(size)) == NULL) { printk(KERN_CRIT "%s -- lvm_do_lv_extend_reduce: vmalloc " @@ -2215,15 +2190,15 @@ return -ENOMEM; } - if ((new_lv->lv_remap_end > old_lv->lv_remap_end) && - (copy_from_user(lvbe, new_lv->lv_block_exception, size))) { + if ((new_lv->u.lv_remap_end > old_lv->u.lv_remap_end) && + (copy_from_user(lvbe, new_lv->u.lv_block_exception, size))) { vfree(lvbe); return -EFAULT; } - new_lv->lv_block_exception = lvbe; + new_lv->u.lv_block_exception = lvbe; if (lvm_snapshot_alloc_hash_table(new_lv)) { - vfree(new_lv->lv_block_exception); + vfree(new_lv->u.lv_block_exception); return -ENOMEM; } @@ -2235,7 +2210,7 @@ pe_t *pe; /* allocate space for new pe structures */ - size = new_lv->lv_current_le * sizeof(pe_t); + size = new_lv->u.lv_current_le * sizeof(pe_t); if ((pe = vmalloc(size)) == NULL) { printk(KERN_CRIT "%s -- lvm_do_lv_extend_reduce: " @@ -2245,21 +2220,21 @@ } /* get the PE structures from user space */ - if (copy_from_user(pe, new_lv->lv_current_pe, size)) { - if(old_lv->lv_access & LV_SNAPSHOT) + if (copy_from_user(pe, new_lv->u.lv_current_pe, size)) { + if(old_lv->u.lv_access & LV_SNAPSHOT) vfree(new_lv->lv_snapshot_hash_table); vfree(pe); return -EFAULT; } - new_lv->lv_current_pe = pe; + new_lv->u.lv_current_pe = pe; /* reduce allocation counters on PV(s) */ - for (l = 0; l < old_lv->lv_allocated_le; l++) { + for (l = 0; l < old_lv->u.lv_allocated_le; l++) { vg_ptr->pe_allocated--; for (p = 0; p < vg_ptr->pv_cur; p++) { if (kdev_same(vg_ptr->pv[p]->pv_dev, - old_lv->lv_current_pe[l].dev)) { + old_lv->u.lv_current_pe[l].dev)) { vg_ptr->pv[p]->pe_allocated--; break; } @@ -2267,11 +2242,11 @@ } /* extend the PE count in PVs */ - for (l = 0; l < new_lv->lv_allocated_le; l++) { + for (l = 0; l < new_lv->u.lv_allocated_le; l++) { vg_ptr->pe_allocated++; for (p = 0; p < vg_ptr->pv_cur; p++) { if (kdev_same(vg_ptr->pv[p]->pv_dev, - new_lv->lv_current_pe[l].dev)) { + new_lv->u.lv_current_pe[l].dev)) { vg_ptr->pv[p]->pe_allocated++; break; } @@ -2279,30 +2254,30 @@ } /* save availiable i/o statistic data */ - if (old_lv->lv_stripes < 2) { /* linear logical volume */ - end = min(old_lv->lv_current_le, new_lv->lv_current_le); + if (old_lv->u.lv_stripes < 2) { /* linear logical volume */ + end = min(old_lv->u.lv_current_le, new_lv->u.lv_current_le); for (l = 0; l < end; l++) { - new_lv->lv_current_pe[l].reads += - old_lv->lv_current_pe[l].reads; + new_lv->u.lv_current_pe[l].reads += + old_lv->u.lv_current_pe[l].reads; - new_lv->lv_current_pe[l].writes += - old_lv->lv_current_pe[l].writes; + new_lv->u.lv_current_pe[l].writes += + old_lv->u.lv_current_pe[l].writes; } } else { /* striped logical volume */ uint i, j, source, dest, end, old_stripe_size, new_stripe_size; - old_stripe_size = old_lv->lv_allocated_le / old_lv->lv_stripes; - new_stripe_size = new_lv->lv_allocated_le / new_lv->lv_stripes; + old_stripe_size = old_lv->u.lv_allocated_le / old_lv->u.lv_stripes; + new_stripe_size = new_lv->u.lv_allocated_le / new_lv->u.lv_stripes; end = min(old_stripe_size, new_stripe_size); for (i = source = dest = 0; - i < new_lv->lv_stripes; i++) { + i < new_lv->u.lv_stripes; i++) { for (j = 0; j < end; j++) { - new_lv->lv_current_pe[dest + j].reads += - old_lv->lv_current_pe[source + j].reads; - new_lv->lv_current_pe[dest + j].writes += - old_lv->lv_current_pe[source + j].writes; + new_lv->u.lv_current_pe[dest + j].reads += + old_lv->u.lv_current_pe[source + j].reads; + new_lv->u.lv_current_pe[dest + j].writes += + old_lv->u.lv_current_pe[source + j].writes; } source += old_stripe_size; dest += new_stripe_size; @@ -2312,19 +2287,29 @@ return 0; } -static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *new_lv) +static int lvm_do_lv_extend_reduce(int minor, char *lv_name, userlv_t *ulv) { int r; ulong l, e, size; vg_t *vg_ptr = vg[VG_CHR(minor)]; lv_t *old_lv; + lv_t *new_lv; pe_t *pe; - if ((pe = new_lv->lv_current_pe) == NULL) + if((new_lv = kmalloc(sizeof(lv_t),GFP_KERNEL)) == NULL){ + printk(KERN_CRIT + "%s -- LV_EXTEND/REDUCE: kmallor error LV at line %d\n", + lvm_name,__LINE__); + return -ENOMEM; + } + memset(new_lv,0,sizeof(lv_t)); + memcpy(&new_lv->u,ulv,sizeof(userlv_t)); + + if ((pe = new_lv->u.lv_current_pe) == NULL) return -EINVAL; for (l = 0; l < vg_ptr->lv_max; l++) - if (vg_ptr->lv[l] && !strcmp(vg_ptr->lv[l]->lv_name, lv_name)) + if (vg_ptr->lv[l] && !strcmp(vg_ptr->lv[l]->u.lv_name, lv_name)) break; if (l == vg_ptr->lv_max) @@ -2332,9 +2317,9 @@ old_lv = vg_ptr->lv[l]; - if (old_lv->lv_access & LV_SNAPSHOT) { + if (old_lv->u.lv_access & LV_SNAPSHOT) { /* only perform this operation on active snapshots */ - if (old_lv->lv_status & LV_ACTIVE) + if (old_lv->u.lv_status & LV_ACTIVE) r = __extend_reduce_snapshot(vg_ptr, old_lv, new_lv); else r = -EPERM; @@ -2348,15 +2333,15 @@ /* copy relevent fields */ down_write(&old_lv->lv_lock); - if(new_lv->lv_access & LV_SNAPSHOT) { - size = (new_lv->lv_remap_end > old_lv->lv_remap_end) ? - old_lv->lv_remap_ptr : new_lv->lv_remap_end; + if(new_lv->u.lv_access & LV_SNAPSHOT) { + size = (new_lv->u.lv_remap_end > old_lv->u.lv_remap_end) ? + old_lv->u.lv_remap_ptr : new_lv->u.lv_remap_end; size *= sizeof(lv_block_exception_t); - memcpy(new_lv->lv_block_exception, - old_lv->lv_block_exception, size); + memcpy(new_lv->u.lv_block_exception, + old_lv->u.lv_block_exception, size); - old_lv->lv_remap_end = new_lv->lv_remap_end; - old_lv->lv_block_exception = new_lv->lv_block_exception; + old_lv->u.lv_remap_end = new_lv->u.lv_remap_end; + old_lv->u.lv_block_exception = new_lv->u.lv_block_exception; old_lv->lv_snapshot_hash_table = new_lv->lv_snapshot_hash_table; old_lv->lv_snapshot_hash_table_size = @@ -2364,40 +2349,40 @@ old_lv->lv_snapshot_hash_mask = new_lv->lv_snapshot_hash_mask; - for (e = 0; e < new_lv->lv_remap_ptr; e++) - lvm_hash_link(new_lv->lv_block_exception + e, - new_lv->lv_block_exception[e].rdev_org, - new_lv->lv_block_exception[e].rsector_org, + for (e = 0; e < new_lv->u.lv_remap_ptr; e++) + lvm_hash_link(new_lv->u.lv_block_exception + e, + new_lv->u.lv_block_exception[e].rdev_org, + new_lv->u.lv_block_exception[e].rsector_org, new_lv); } else { - vfree(old_lv->lv_current_pe); + vfree(old_lv->u.lv_current_pe); vfree(old_lv->lv_snapshot_hash_table); - old_lv->lv_size = new_lv->lv_size; - old_lv->lv_allocated_le = new_lv->lv_allocated_le; - old_lv->lv_current_le = new_lv->lv_current_le; - old_lv->lv_current_pe = new_lv->lv_current_pe; - lvm_gendisk.part[minor(old_lv->lv_dev)].nr_sects = - old_lv->lv_size; - lvm_size[minor(old_lv->lv_dev)] = old_lv->lv_size >> 1; + old_lv->u.lv_size = new_lv->u.lv_size; + old_lv->u.lv_allocated_le = new_lv->u.lv_allocated_le; + old_lv->u.lv_current_le = new_lv->u.lv_current_le; + old_lv->u.lv_current_pe = new_lv->u.lv_current_pe; + lvm_gendisk.part[minor(old_lv->u.lv_dev)].nr_sects = + old_lv->u.lv_size; + lvm_size[minor(old_lv->u.lv_dev)] = old_lv->u.lv_size >> 1; - if (old_lv->lv_access & LV_SNAPSHOT_ORG) { + if (old_lv->u.lv_access & LV_SNAPSHOT_ORG) { lv_t *snap; - for(snap = old_lv->lv_snapshot_next; snap; - snap = snap->lv_snapshot_next) { + for(snap = old_lv->u.lv_snapshot_next; snap; + snap = snap->u.lv_snapshot_next) { down_write(&snap->lv_lock); - snap->lv_current_pe = old_lv->lv_current_pe; - snap->lv_allocated_le = - old_lv->lv_allocated_le; - snap->lv_current_le = old_lv->lv_current_le; - snap->lv_size = old_lv->lv_size; - - lvm_gendisk.part[minor(snap->lv_dev)].nr_sects - = old_lv->lv_size; - lvm_size[minor(snap->lv_dev)] = - old_lv->lv_size >> 1; + snap->u.lv_current_pe = old_lv->u.lv_current_pe; + snap->u.lv_allocated_le = + old_lv->u.lv_allocated_le; + snap->u.lv_current_le = old_lv->u.lv_current_le; + snap->u.lv_size = old_lv->u.lv_size; + + lvm_gendisk.part[minor(snap->u.lv_dev)].nr_sects + = old_lv->u.lv_size; + lvm_size[minor(snap->u.lv_dev)] = + old_lv->u.lv_size >> 1; __update_hardsectsize(snap); up_write(&snap->lv_lock); } @@ -2431,27 +2416,27 @@ for (l = 0; l < vg_ptr->lv_max; l++) { if ((lv_ptr = vg_ptr->lv[l]) != NULL && - strcmp(lv_ptr->lv_name, + strcmp(lv_ptr->u.lv_name, lv_status_byname_req.lv_name) == 0) { /* Save usermode pointers */ - if (copy_from_user(&saved_ptr1, &lv_status_byname_req.lv->lv_current_pe, sizeof(void*)) != 0) + if (copy_from_user(&saved_ptr1, &lv_status_byname_req.lv->u.lv_current_pe, sizeof(void*)) != 0) return -EFAULT; - if (copy_from_user(&saved_ptr2, &lv_status_byname_req.lv->lv_block_exception, sizeof(void*)) != 0) + if (copy_from_user(&saved_ptr2, &lv_status_byname_req.lv->u.lv_block_exception, sizeof(void*)) != 0) return -EFAULT; if (copy_to_user(lv_status_byname_req.lv, lv_ptr, - sizeof(lv_t)) != 0) + sizeof(userlv_t)) != 0) return -EFAULT; if (saved_ptr1 != NULL) { if (copy_to_user(saved_ptr1, - lv_ptr->lv_current_pe, - lv_ptr->lv_allocated_le * + lv_ptr->u.lv_current_pe, + lv_ptr->u.lv_allocated_le * sizeof(pe_t)) != 0) return -EFAULT; } /* Restore usermode pointers */ - if (copy_to_user(&lv_status_byname_req.lv->lv_current_pe, &saved_ptr1, sizeof(void*)) != 0) + if (copy_to_user(&lv_status_byname_req.lv->u.lv_current_pe, &saved_ptr1, sizeof(void*)) != 0) return -EFAULT; return 0; } @@ -2484,23 +2469,23 @@ return -ENXIO; /* Save usermode pointers */ - if (copy_from_user(&saved_ptr1, &lv_status_byindex_req.lv->lv_current_pe, sizeof(void*)) != 0) + if (copy_from_user(&saved_ptr1, &lv_status_byindex_req.lv->u.lv_current_pe, sizeof(void*)) != 0) return -EFAULT; - if (copy_from_user(&saved_ptr2, &lv_status_byindex_req.lv->lv_block_exception, sizeof(void*)) != 0) + if (copy_from_user(&saved_ptr2, &lv_status_byindex_req.lv->u.lv_block_exception, sizeof(void*)) != 0) return -EFAULT; - if (copy_to_user(lv_status_byindex_req.lv, lv_ptr, sizeof(lv_t)) != 0) + if (copy_to_user(lv_status_byindex_req.lv, lv_ptr, sizeof(userlv_t)) != 0) return -EFAULT; if (saved_ptr1 != NULL) { if (copy_to_user(saved_ptr1, - lv_ptr->lv_current_pe, - lv_ptr->lv_allocated_le * + lv_ptr->u.lv_current_pe, + lv_ptr->u.lv_allocated_le * sizeof(pe_t)) != 0) return -EFAULT; } /* Restore usermode pointers */ - if (copy_to_user(&lv_status_byindex_req.lv->lv_current_pe, &saved_ptr1, sizeof(void *)) != 0) + if (copy_to_user(&lv_status_byindex_req.lv->u.lv_current_pe, &saved_ptr1, sizeof(void *)) != 0) return -EFAULT; return 0; @@ -2524,7 +2509,7 @@ for ( l = 0; l < vg_ptr->lv_max; l++) { if ( vg_ptr->lv[l] == NULL) continue; - if ( kdev_same(vg_ptr->lv[l]->lv_dev, + if ( kdev_same(vg_ptr->lv[l]->u.lv_dev, to_kdev_t(lv_status_bydev_req.dev))) break; } @@ -2533,22 +2518,22 @@ lv_ptr = vg_ptr->lv[l]; /* Save usermode pointers */ - if (copy_from_user(&saved_ptr1, &lv_status_bydev_req.lv->lv_current_pe, sizeof(void*)) != 0) + if (copy_from_user(&saved_ptr1, &lv_status_bydev_req.lv->u.lv_current_pe, sizeof(void*)) != 0) return -EFAULT; - if (copy_from_user(&saved_ptr2, &lv_status_bydev_req.lv->lv_block_exception, sizeof(void*)) != 0) + if (copy_from_user(&saved_ptr2, &lv_status_bydev_req.lv->u.lv_block_exception, sizeof(void*)) != 0) return -EFAULT; if (copy_to_user(lv_status_bydev_req.lv, lv_ptr, sizeof(lv_t)) != 0) return -EFAULT; if (saved_ptr1 != NULL) { if (copy_to_user(saved_ptr1, - lv_ptr->lv_current_pe, - lv_ptr->lv_allocated_le * + lv_ptr->u.lv_current_pe, + lv_ptr->u.lv_allocated_le * sizeof(pe_t)) != 0) return -EFAULT; } /* Restore usermode pointers */ - if (copy_to_user(&lv_status_bydev_req.lv->lv_current_pe, &saved_ptr1, sizeof(void *)) != 0) + if (copy_to_user(&lv_status_bydev_req.lv->u.lv_current_pe, &saved_ptr1, sizeof(void *)) != 0) return -EFAULT; return 0; @@ -2558,7 +2543,7 @@ /* * character device support function rename a logical volume */ -static int lvm_do_lv_rename(vg_t *vg_ptr, lv_req_t *lv_req, lv_t *lv) +static int lvm_do_lv_rename(vg_t *vg_ptr, lv_req_t *lv_req, userlv_t *ulv) { int l = 0; int ret = 0; @@ -2567,10 +2552,10 @@ for (l = 0; l < vg_ptr->lv_max; l++) { if ( (lv_ptr = vg_ptr->lv[l]) == NULL) continue; - if (kdev_same(lv_ptr->lv_dev, lv->lv_dev)) + if (kdev_same(lv_ptr->u.lv_dev, ulv->lv_dev)) { lvm_fs_remove_lv(vg_ptr, lv_ptr); - strncpy(lv_ptr->lv_name, + strncpy(lv_ptr->u.lv_name, lv_req->lv_name, NAME_LEN); lvm_fs_create_lv(vg_ptr, lv_ptr); diff -Nru a/drivers/md/md.c b/drivers/md/md.c --- a/drivers/md/md.c Tue Feb 19 18:08:58 2002 +++ b/drivers/md/md.c Tue Feb 19 18:08:58 2002 @@ -1737,7 +1737,6 @@ register_disk(&md_gendisk, mk_kdev(MAJOR_NR,mdidx(mddev)), 1, &md_fops, md_size[mdidx(mddev)]<<1); - read_ahead[MD_MAJOR] = 1024; return (0); } @@ -2622,8 +2621,6 @@ (u64 *) arg); goto done; - case BLKRAGET: - case BLKRASET: case BLKFLSBUF: case BLKBSZGET: case BLKBSZSET: @@ -3176,13 +3173,6 @@ sz += sprintf(page+sz, "\n"); - - sz += sprintf(page+sz, "read_ahead "); - if (read_ahead[MD_MAJOR] == INT_MAX) - sz += sprintf(page+sz, "not set\n"); - else - sz += sprintf(page+sz, "%d sectors\n", read_ahead[MD_MAJOR]); - ITERATE_MDDEV(mddev,tmp) { sz += sprintf(page + sz, "md%d : %sactive", mdidx(mddev), mddev->pers ? "" : "in"); @@ -3622,7 +3612,6 @@ } blksize_size[MAJOR_NR] = md_blocksizes; blk_size[MAJOR_NR] = md_size; - max_readahead[MAJOR_NR] = md_maxreadahead; dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t)); @@ -3657,9 +3646,6 @@ /* forward all md request to md_make_request */ blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), md_make_request); - - - read_ahead[MAJOR_NR] = INT_MAX; add_gendisk(&md_gendisk); diff -Nru a/drivers/md/raid1.c b/drivers/md/raid1.c --- a/drivers/md/raid1.c Tue Feb 19 18:08:57 2002 +++ b/drivers/md/raid1.c Tue Feb 19 18:08:57 2002 @@ -225,14 +225,12 @@ * operation and are ready to return a success/failure code to the buffer * cache layer. */ -static int raid_end_bio_io(r1bio_t *r1_bio, int uptodate, int nr_sectors) +static void raid_end_bio_io(r1bio_t *r1_bio, int uptodate) { struct bio *bio = r1_bio->master_bio; - bio_endio(bio, uptodate, nr_sectors); + bio_endio(bio, uptodate); free_r1bio(r1_bio); - - return 0; } /* @@ -247,7 +245,7 @@ atomic_dec(&conf->mirrors[disk].nr_pending); } -static int end_request(struct bio *bio, int nr_sectors) +static void end_request(struct bio *bio) { int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); r1bio_t * r1_bio = (r1bio_t *)(bio->bi_private); @@ -278,8 +276,8 @@ * we have only one bio on the read side */ if (uptodate) { - raid_end_bio_io(r1_bio, uptodate, nr_sectors); - return 0; + raid_end_bio_io(r1_bio, uptodate); + return; } /* * oops, read error: @@ -287,7 +285,7 @@ printk(KERN_ERR "raid1: %s: rescheduling sector %lu\n", partition_name(bio->bi_dev), r1_bio->sector); reschedule_retry(r1_bio); - return 0; + return; } if (r1_bio->read_bio) @@ -307,8 +305,7 @@ * already. */ if (atomic_dec_and_test(&r1_bio->remaining)) - raid_end_bio_io(r1_bio, uptodate, nr_sectors); - return 0; + raid_end_bio_io(r1_bio, uptodate); } /* @@ -518,7 +515,7 @@ * If all mirrors are non-operational * then return an IO error: */ - raid_end_bio_io(r1_bio, 0, 0); + raid_end_bio_io(r1_bio, 0); return 0; } atomic_set(&r1_bio->remaining, sum_bios); @@ -930,7 +927,7 @@ #define REDIRECT_SECTOR KERN_ERR \ "raid1: %s: redirecting sector %lu to another mirror\n" -static int end_sync_read(struct bio *bio, int nr_sectors) +static void end_sync_read(struct bio *bio) { int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); r1bio_t * r1_bio = (r1bio_t *)(bio->bi_private); @@ -948,11 +945,9 @@ else set_bit(R1BIO_Uptodate, &r1_bio->state); reschedule_retry(r1_bio); - - return 0; } -static int end_sync_write(struct bio *bio, int nr_sectors) +static void end_sync_write(struct bio *bio) { int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); r1bio_t * r1_bio = (r1bio_t *)(bio->bi_private); @@ -974,7 +969,6 @@ resume_device(conf); put_buf(r1_bio); } - return 0; } static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio) @@ -1100,7 +1094,7 @@ map(mddev, &bio->bi_dev); if (kdev_same(bio->bi_dev, dev)) { printk(IO_ERROR, partition_name(bio->bi_dev), r1_bio->sector); - raid_end_bio_io(r1_bio, 0, 0); + raid_end_bio_io(r1_bio, 0); break; } printk(REDIRECT_SECTOR, diff -Nru a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c --- a/drivers/media/video/bttv-driver.c Tue Feb 19 18:08:58 2002 +++ b/drivers/media/video/bttv-driver.c Tue Feb 19 18:08:58 2002 @@ -141,36 +141,11 @@ * defined way to get at the kernel page tables. */ -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if(pte_present(pte)) { - ret = (unsigned long) page_address(pte_page(pte)); - ret |= (adr & (PAGE_SIZE - 1)); - - } - } - } - MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); - return ret; -} - static inline unsigned long uvirt_to_bus(unsigned long adr) { unsigned long kva, ret; - kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + kva = page_address(vmalloc_to_page(adr)); ret = virt_to_bus((void *)kva); MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); return ret; @@ -181,7 +156,7 @@ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); + kva = page_address(vmalloc_to_page(va)); ret = virt_to_bus((void *)kva); MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); return ret; @@ -196,7 +171,7 @@ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); + kva = page_address(vmalloc_to_page(va)); ret = __pa(kva); MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); return ret; diff -Nru a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c --- a/drivers/media/video/cpia.c Tue Feb 19 18:08:58 2002 +++ b/drivers/media/video/cpia.c Tue Feb 19 18:08:58 2002 @@ -178,40 +178,8 @@ * * Memory management * - * This is a shameless copy from the USB-cpia driver (linux kernel - * version 2.3.29 or so, I have no idea what this code actually does ;). - * Actually it seems to be a copy of a shameless copy of the bttv-driver. - * Or that is a copy of a shameless copy of ... (To the powers: is there - * no generic kernel-function to do this sort of stuff?) - * - * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says - * there will be one, but apparentely not yet - jerdfelt - * **********************************************************************/ -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if (pte_present(pte)) { - ret = (unsigned long) page_address(pte_page(pte)); - ret |= (adr & (PAGE_SIZE-1)); - } - } - } - return ret; -} - /* Here we want the physical address of the memory. * This is used when initializing the contents of the * area and marking the pages as reserved. @@ -221,7 +189,7 @@ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); + kva = page_address(vmalloc_to_page(va)); ret = __pa(kva); return ret; } diff -Nru a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c --- a/drivers/media/video/cpia_usb.c Tue Feb 19 18:08:57 2002 +++ b/drivers/media/video/cpia_usb.c Tue Feb 19 18:08:57 2002 @@ -201,7 +201,7 @@ ucpia->workbuff = ucpia->buffers[1]; /* We double buffer the Iso lists */ - urb = usb_alloc_urb(FRAMES_PER_DESC); + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (!urb) { printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); retval = -ENOMEM; @@ -222,7 +222,7 @@ urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; } - urb = usb_alloc_urb(FRAMES_PER_DESC); + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (!urb) { printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); retval = -ENOMEM; diff -Nru a/drivers/media/video/meye.c b/drivers/media/video/meye.c --- a/drivers/media/video/meye.c Tue Feb 19 18:08:57 2002 +++ b/drivers/media/video/meye.c Tue Feb 19 18:08:57 2002 @@ -118,49 +118,6 @@ #define MDEBUG(x) do {} while (0) /* #define MDEBUG(x) x */ -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) { - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if(pte_present(pte)) { - ret = (unsigned long)page_address(pte_page(pte)); - ret |= (adr & (PAGE_SIZE - 1)); - - } - } - } - MDEBUG(printk("uv2kva(%lx-->%lx)\n", adr, ret)); - return ret; -} - -static inline unsigned long uvirt_to_bus(unsigned long adr) { - unsigned long kva, ret; - - kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("uv2b(%lx-->%lx)\n", adr, ret)); - return ret; -} - -static inline unsigned long kvirt_to_bus(unsigned long adr) { - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("kv2b(%lx-->%lx)\n", adr, ret)); - return ret; -} - /* Here we want the physical address of the memory. * This is used when initializing the contents of the * area and marking the pages as reserved. @@ -169,7 +126,7 @@ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); + kva = page_address(vmalloc_to_page(va)); ret = __pa(kva); MDEBUG(printk("kv2pa(%lx-->%lx)\n", adr, ret)); return ret; @@ -209,31 +166,64 @@ } /* return a page table pointing to N pages of locked memory */ -static void *ptable_alloc(int npages, u32 *pt_addr) { +static int ptable_alloc(void) { + u32 *pt; int i; - void *vmem; - u32 *ptable; - unsigned long adr; - - vmem = rvmalloc((npages + 1) * PAGE_SIZE); - if (!vmem) - return NULL; - - adr = (unsigned long)vmem; - ptable = (u32 *)(vmem + npages * PAGE_SIZE); - for (i = 0; i < npages; i++) { - ptable[i] = (u32) kvirt_to_bus(adr); - adr += PAGE_SIZE; + + memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); + + meye.mchip_ptable[MCHIP_NB_PAGES] = pci_alloc_consistent(meye.mchip_dev, + PAGE_SIZE, + &meye.mchip_dmahandle); + if (!meye.mchip_ptable[MCHIP_NB_PAGES]) + return -1; + + pt = (u32 *)meye.mchip_ptable[MCHIP_NB_PAGES]; + for (i = 0; i < MCHIP_NB_PAGES; i++) { + meye.mchip_ptable[i] = pci_alloc_consistent(meye.mchip_dev, + PAGE_SIZE, + pt); + if (!meye.mchip_ptable[i]) + return -1; + pt++; } + return 0; +} - *pt_addr = (u32) kvirt_to_bus(adr); - return vmem; +static void ptable_free(void) { + u32 *pt; + int i; + + pt = (u32 *)meye.mchip_ptable[MCHIP_NB_PAGES]; + for (i = 0; i < MCHIP_NB_PAGES; i++) + if (meye.mchip_ptable[i]) + pci_free_consistent(meye.mchip_dev, + PAGE_SIZE, + meye.mchip_ptable[i], *pt); + + if (meye.mchip_ptable[MCHIP_NB_PAGES]) + pci_free_consistent(meye.mchip_dev, + PAGE_SIZE, + meye.mchip_ptable[MCHIP_NB_PAGES], + meye.mchip_dmahandle); + + memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); + meye.mchip_dmahandle = 0; } -static void ptable_free(void *vmem, int npages) { - rvfree(vmem, (npages + 1) * PAGE_SIZE); +/* copy data from ptable into buf */ +static void ptable_copy(u8 *buf, int start, int size, int pt_pages) { + int i; + + for (i = 0; i < (size / PAGE_SIZE) * PAGE_SIZE; i += PAGE_SIZE) { + memcpy(buf + i, meye.mchip_ptable[start++], PAGE_SIZE); + if (start >= pt_pages) + start = 0; + } + memcpy(buf + i, meye.mchip_ptable[start], size % PAGE_SIZE); } + /****************************************************************************/ /* JPEG tables at different qualities to load into the VRJ chip */ /****************************************************************************/ @@ -587,29 +577,23 @@ /* setup for DMA transfers - also zeros the framebuffer */ static int mchip_dma_alloc(void) { - if (!meye.mchip_fbuffer) { - meye.mchip_fbuffer = ptable_alloc(MCHIP_NB_PAGES, - &meye.mchip_ptaddr); - if (!meye.mchip_fbuffer) + if (!meye.mchip_dmahandle) + if (ptable_alloc()) return -1; - } return 0; } /* frees the DMA buffer */ static void mchip_dma_free(void) { - if (meye.mchip_fbuffer) { - ptable_free(meye.mchip_fbuffer, MCHIP_NB_PAGES); - meye.mchip_fbuffer = 0; - meye.mchip_ptaddr = 0; - } + if (meye.mchip_dmahandle) + ptable_free(); } /* sets the DMA parameters into the chip */ static void mchip_dma_setup(void) { int i; - mchip_set(MCHIP_MM_PT_ADDR, meye.mchip_ptaddr); + mchip_set(MCHIP_MM_PT_ADDR, meye.mchip_dmahandle); for (i = 0; i < 4; i++) mchip_set(MCHIP_MM_FIR(i), 0); meye.mchip_fnum = 0; @@ -658,59 +642,40 @@ meye.mchip_fnum %= 4; } - /* read one frame from the framebuffer assuming it was captured using a uncompressed transfer */ -static void mchip_cont_read_frame(u32 v, u8 *buf, int size) { +static void mchip_cont_read_frame(u32 v, u8 *buf, int size) { int pt_id; - int avail; pt_id = (v >> 17) & 0x3FF; - avail = MCHIP_NB_PAGES - pt_id; - if (size > avail*PAGE_SIZE) { - memcpy(buf, meye.mchip_fbuffer + pt_id * PAGE_SIZE, - avail * PAGE_SIZE); - memcpy(buf +avail * PAGE_SIZE, meye.mchip_fbuffer, - size - avail * PAGE_SIZE); - } - else - memcpy(buf, meye.mchip_fbuffer + pt_id * PAGE_SIZE, size); + ptable_copy(buf, pt_id, size, MCHIP_NB_PAGES); + } /* read a compressed frame from the framebuffer */ static int mchip_comp_read_frame(u32 v, u8 *buf, int size) { int pt_start, pt_end, trailer; - int fsize, fsize2; + int fsize; int i; pt_start = (v >> 19) & 0xFF; pt_end = (v >> 11) & 0xFF; trailer = (v >> 1) & 0x3FF; - if (pt_end < pt_start) { - fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE; - fsize2 = pt_end * PAGE_SIZE + trailer * 4; - if (fsize + fsize2 > size) { - printk(KERN_WARNING "meye: oversized compressed frame %d %d\n", - fsize, fsize2); - return -1; - } else { - memcpy(buf, meye.mchip_fbuffer + pt_start * PAGE_SIZE, - fsize); - memcpy(buf + fsize, meye.mchip_fbuffer, fsize2); - fsize += fsize2; - } - } else { + if (pt_end < pt_start) + fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE + + pt_end * PAGE_SIZE + trailer * 4; + else fsize = (pt_end - pt_start) * PAGE_SIZE + trailer * 4; - if (fsize > size) { - printk(KERN_WARNING "meye: oversized compressed frame %d\n", - fsize); - return -1; - } else - memcpy(buf, meye.mchip_fbuffer + pt_start * PAGE_SIZE, - fsize); + + if (fsize > size) { + printk(KERN_WARNING "meye: oversized compressed frame %d\n", + fsize); + return -1; } + + ptable_copy(buf, pt_start, fsize, MCHIP_NB_PAGES_MJPEG); #ifdef MEYE_JPEG_CORRECTION diff -Nru a/drivers/media/video/meye.h b/drivers/media/video/meye.h --- a/drivers/media/video/meye.h Tue Feb 19 18:08:57 2002 +++ b/drivers/media/video/meye.h Tue Feb 19 18:08:57 2002 @@ -29,7 +29,7 @@ #define _MEYE_PRIV_H_ #define MEYE_DRIVER_MAJORVERSION 1 -#define MEYE_DRIVER_MINORVERSION 1 +#define MEYE_DRIVER_MINORVERSION 2 /****************************************************************************/ /* Motion JPEG chip registers */ @@ -292,8 +292,8 @@ u8 mchip_fnum; /* current mchip frame number */ unsigned char *mchip_mmregs; /* mchip: memory mapped registers */ - unsigned char *mchip_fbuffer; /* mchip: framebuffer */ - u32 mchip_ptaddr; /* mchip: pointer to framebuffer */ + u8 *mchip_ptable[MCHIP_NB_PAGES+1];/* mchip: ptable + ptable toc */ + dma_addr_t mchip_dmahandle; /* mchip: dma handle to ptable toc */ unsigned char *grab_fbuffer; /* capture framebuffer */ /* list of buffers */ diff -Nru a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c --- a/drivers/media/video/saa5249.c Tue Feb 19 18:08:58 2002 +++ b/drivers/media/video/saa5249.c Tue Feb 19 18:08:58 2002 @@ -282,14 +282,14 @@ spin_lock_irq(¤t->sigmask_lock); sigfillset(¤t->blocked); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); current->state = TASK_INTERRUPTIBLE; schedule_timeout(delay); spin_lock_irq(¤t->sigmask_lock); current->blocked = oldblocked; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } diff -Nru a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c --- a/drivers/message/i2o/i2o_block.c Tue Feb 19 18:08:58 2002 +++ b/drivers/message/i2o/i2o_block.c Tue Feb 19 18:08:58 2002 @@ -1104,8 +1104,6 @@ case BLKFLSBUF: case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKPG: return blk_ioctl(inode->i_rdev, cmd, arg); diff -Nru a/drivers/mtd/devices/blkmtd.c b/drivers/mtd/devices/blkmtd.c --- a/drivers/mtd/devices/blkmtd.c Tue Feb 19 18:08:57 2002 +++ b/drivers/mtd/devices/blkmtd.c Tue Feb 19 18:08:57 2002 @@ -263,7 +263,7 @@ tsk->tty = NULL; spin_lock_irq(&tsk->sigmask_lock); sigfillset(&tsk->blocked); - recalc_sigpending(tsk); + recalc_sigpending(); spin_unlock_irq(&tsk->sigmask_lock); if(alloc_kiovec(1, &iobuf)) diff -Nru a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c --- a/drivers/mtd/mtdblock.c Tue Feb 19 18:08:59 2002 +++ b/drivers/mtd/mtdblock.c Tue Feb 19 18:08:59 2002 @@ -477,7 +477,7 @@ strcpy(tsk->comm, "mtdblockd"); spin_lock_irq(&tsk->sigmask_lock); sigfillset(&tsk->blocked); - recalc_sigpending(tsk); + recalc_sigpending(); spin_unlock_irq(&tsk->sigmask_lock); daemonize(); diff -Nru a/drivers/net/3c505.c b/drivers/net/3c505.c --- a/drivers/net/3c505.c Tue Feb 19 18:09:00 2002 +++ b/drivers/net/3c505.c Tue Feb 19 18:09:00 2002 @@ -1160,6 +1160,87 @@ return &adapter->stats; } +/** + * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls + * @dev: network interface on which out-of-band action is to be performed + * @useraddr: userspace address to which data is to be read and returned + * + * Process the various commands of the SIOCETHTOOL interface. + */ + +static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +{ + u32 ethcmd; + + /* dev_ioctl() in ../../net/core/dev.c has already checked + capable(CAP_NET_ADMIN), so don't bother with that here. */ + + if (get_user(ethcmd, (u32 *)useraddr)) + return -EFAULT; + + switch (ethcmd) { + + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, DRV_NAME); + strcpy (info.version, DRV_VERSION); + sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr); + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; + } + + /* get message-level */ + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = {ETHTOOL_GMSGLVL}; + edata.data = debug; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + /* set message-level */ + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + debug = edata.data; + return 0; + } + + default: + break; + } + + return -EOPNOTSUPP; +} + +/** + * netdev_ioctl: Handle network interface ioctls + * @dev: network interface on which out-of-band action is to be performed + * @rq: user request data + * @cmd: command issued by user + * + * Process the various out-of-band ioctls passed to this driver. + */ + +static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ + int rc = 0; + + switch (cmd) { + case SIOCETHTOOL: + rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); + break; + + default: + rc = -EOPNOTSUPP; + break; + } + + return rc; +} + + /****************************************************** * * close the board diff -Nru a/drivers/net/3c509.c b/drivers/net/3c509.c --- a/drivers/net/3c509.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/3c509.c Tue Feb 19 18:08:57 2002 @@ -43,7 +43,7 @@ v1.18 12Mar2001 Andrew Morton - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz) - Reviewed against 1.18 from scyld.com - v1.18 17Nov2001 Jeff Garzik + v1.18a 17Nov2001 Jeff Garzik - ethtool support */ diff -Nru a/drivers/net/3c59x.c b/drivers/net/3c59x.c --- a/drivers/net/3c59x.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/3c59x.c Tue Feb 19 18:08:57 2002 @@ -2713,18 +2713,15 @@ return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ data->phy_id = phy; case SIOCGMIIREG: /* Read MII PHY register. */ - case SIOCDEVPRIVATE+1: /* for binary compat, remove in 2.5 */ EL3WINDOW(4); data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f); retval = 0; break; case SIOCSMIIREG: /* Write MII PHY register. */ - case SIOCDEVPRIVATE+2: /* for binary compat, remove in 2.5 */ if (!capable(CAP_NET_ADMIN)) { retval = -EPERM; } else { diff -Nru a/drivers/net/8139too.c b/drivers/net/8139too.c --- a/drivers/net/8139too.c Tue Feb 19 18:08:56 2002 +++ b/drivers/net/8139too.c Tue Feb 19 18:08:56 2002 @@ -217,6 +217,7 @@ DFE538TX, DFE690TXD, FE2000VX, + ALLIED8139, RTL8129, } board_t; @@ -235,6 +236,7 @@ { "D-Link DFE-538TX (RealTek RTL8139)", RTL8139_CAPS }, { "D-Link DFE-690TXD (RealTek RTL8139)", RTL8139_CAPS }, { "AboCom FE2000VX (RealTek RTL8139)", RTL8139_CAPS }, + { "Allied Telesyn 8139 CardBus", RTL8139_CAPS }, { "RealTek RTL8129", RTL8129_CAPS }, }; @@ -249,6 +251,7 @@ {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DFE538TX }, {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DFE690TXD }, {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FE2000VX }, + {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALLIED8139 }, #ifdef CONFIG_8139TOO_8129 {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 }, @@ -1557,7 +1560,7 @@ reparent_to_init(); spin_lock_irq(¤t->sigmask_lock); sigemptyset(¤t->blocked); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); strncpy (current->comm, dev->name, sizeof(current->comm) - 1); @@ -1646,23 +1649,24 @@ struct rtl8139_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; unsigned int entry; + unsigned int len = skb->len; /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % NUM_TX_DESC; - if (likely(skb->len < TX_BUF_SIZE)) { + if (likely(len < TX_BUF_SIZE)) { skb_copy_and_csum_dev(skb, tp->tx_buf[entry]); dev_kfree_skb(skb); } else { dev_kfree_skb(skb); tp->stats.tx_dropped++; return 0; - } + } /* Note: the chip doesn't have auto-pad! */ spin_lock_irq(&tp->lock); RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), - tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); + tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); dev->trans_start = jiffies; @@ -1673,8 +1677,8 @@ netif_stop_queue (dev); spin_unlock_irq(&tp->lock); - DPRINTK ("%s: Queued Tx packet at %p size %u to slot %d.\n", - dev->name, skb->data, skb->len, entry); + DPRINTK ("%s: Queued Tx packet size %u to slot %d.\n", + dev->name, len, entry); return 0; } diff -Nru a/drivers/net/Config.in b/drivers/net/Config.in --- a/drivers/net/Config.in Tue Feb 19 18:08:57 2002 +++ b/drivers/net/Config.in Tue Feb 19 18:08:57 2002 @@ -37,7 +37,6 @@ bool ' Use AAUI port instead of TP by default' CONFIG_MACE_AAUI_PORT fi dep_tristate ' BMAC (G3 ethernet) support' CONFIG_BMAC $CONFIG_ALL_PPC - dep_tristate ' GMAC (G4/iBook ethernet) support' CONFIG_GMAC $CONFIG_ALL_PPC tristate ' National DP83902AV (Oak ethernet) support' CONFIG_OAKNET fi if [ "$CONFIG_ZORRO" = "y" ]; then diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile --- a/drivers/net/Makefile Tue Feb 19 18:08:58 2002 +++ b/drivers/net/Makefile Tue Feb 19 18:08:58 2002 @@ -57,7 +57,6 @@ obj-$(CONFIG_MACE) += mace.o obj-$(CONFIG_BMAC) += bmac.o -obj-$(CONFIG_GMAC) += gmac.o obj-$(CONFIG_OAKNET) += oaknet.o 8390.o @@ -65,7 +64,7 @@ obj-$(CONFIG_RCPCI) += rcpci.o obj-$(CONFIG_VORTEX) += 3c59x.o obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o -obj-$(CONFIG_PCNET32) += pcnet32.o +obj-$(CONFIG_PCNET32) += pcnet32.o mii.o obj-$(CONFIG_EEPRO100) += eepro100.o obj-$(CONFIG_TLAN) += tlan.o obj-$(CONFIG_EPIC100) += epic100.o mii.o diff -Nru a/drivers/net/acenic.c b/drivers/net/acenic.c --- a/drivers/net/acenic.c Tue Feb 19 18:08:59 2002 +++ b/drivers/net/acenic.c Tue Feb 19 18:08:59 2002 @@ -1528,7 +1528,7 @@ ace_set_rxtx_parms(dev, 0); if (board_idx == BOARD_IDX_OVERFLOW) { - printk(KERN_WARNING "%s: more then %i NICs detected, " + printk(KERN_WARNING "%s: more than %i NICs detected, " "ignoring module parameters!\n", dev->name, ACE_MAX_MOD_PARMS); } else if (board_idx >= 0) { diff -Nru a/drivers/net/aironet4500_core.c b/drivers/net/aironet4500_core.c --- a/drivers/net/aironet4500_core.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/aironet4500_core.c Tue Feb 19 18:08:57 2002 @@ -2836,7 +2836,7 @@ return 0; final: printk(KERN_ERR "aironet init failed \n"); - return NODEV; + return -ENODEV; }; diff -Nru a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c --- a/drivers/net/arcnet/com90io.c Tue Feb 19 18:08:59 2002 +++ b/drivers/net/arcnet/com90io.c Tue Feb 19 18:08:59 2002 @@ -241,7 +241,10 @@ return -ENODEV; } /* Reserve the I/O region - guaranteed to work by check_region */ - request_region(dev->base_addr, ARCNET_TOTAL_SIZE, "arcnet (COM90xx-IO)"); + if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, "arcnet (COM90xx-IO)")) { + free_irq(dev->irq, dev); + return -EBUSY; + } /* Initialize the rest of the device structure. */ dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL); diff -Nru a/drivers/net/bmac.c b/drivers/net/bmac.c --- a/drivers/net/bmac.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/bmac.c Tue Feb 19 18:08:57 2002 @@ -22,7 +22,8 @@ #include #include #include -#include +#include +#include #ifdef CONFIG_PMAC_PBOOK #include #include @@ -155,7 +156,7 @@ static void bmac_start_chip(struct net_device *dev); static void bmac_init_chip(struct net_device *dev); static void bmac_init_registers(struct net_device *dev); -static void bmac_reset_chip(struct net_device *dev); +static void bmac_enable_and_reset_chip(struct net_device *dev); static int bmac_set_address(struct net_device *dev, void *addr); static void bmac_misc_intr(int irq, void *dev_id, struct pt_regs *regs); static void bmac_txdma_intr(int irq, void *dev_id, struct pt_regs *regs); @@ -229,21 +230,18 @@ } static void -bmac_reset_chip(struct net_device *dev) +bmac_enable_and_reset_chip(struct net_device *dev) { struct bmac_data *bp = (struct bmac_data *) dev->priv; volatile struct dbdma_regs *rd = bp->rx_dma; volatile struct dbdma_regs *td = bp->tx_dma; - dbdma_reset(rd); - dbdma_reset(td); + if (rd) + dbdma_reset(rd); + if (td) + dbdma_reset(td); - feature_set(bp->node, FEATURE_BMac_IO_enable); - udelay(10000); - feature_set(bp->node, FEATURE_BMac_reset); - udelay(10000); - feature_clear(bp->node, FEATURE_BMac_reset); - udelay(10000); + pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 1); } #define MIFDELAY udelay(10) @@ -522,10 +520,7 @@ } } } - feature_set(bp->node, FEATURE_BMac_reset); - mdelay(10); - feature_clear(bp->node, FEATURE_BMac_IO_enable); - mdelay(10); + pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0); break; case PBOOK_WAKE: /* see if this is enough */ @@ -1254,7 +1249,7 @@ unsigned char *data; save_flags(flags); cli(); - bmac_reset_chip(dev); + bmac_enable_and_reset_chip(dev); bmac_init_tx_ring(bp); bmac_init_rx_ring(bp); bmac_init_chip(dev); @@ -1337,14 +1332,30 @@ bmac->full_name); return; } + bp = (struct bmac_data *) dev->priv; SET_MODULE_OWNER(dev); + bp->node = bmac; + if (!request_OF_resource(bmac, 0, " (bmac)")) { + printk(KERN_ERR "BMAC: can't request IO resource !\n"); + goto err_out; + } + if (!request_OF_resource(bmac, 1, " (bmac tx dma)")) { + printk(KERN_ERR "BMAC: can't request TX DMA resource !\n"); + goto err_out; + } + + if (!request_OF_resource(bmac, 2, " (bmac rx dma)")) { + printk(KERN_ERR "BMAC: can't request RX DMA resource !\n"); + goto err_out; + } dev->base_addr = (unsigned long) ioremap(bmac->addrs[0].address, bmac->addrs[0].size); if (!dev->base_addr) goto err_out; dev->irq = bmac->intrs[0].line; + bmac_enable_and_reset_chip(dev); bmwrite(dev, INTDISABLE, DisableAll); printk(KERN_INFO "%s: BMAC%s at", dev->name, (is_bmac_plus? "+": "")); @@ -1356,6 +1367,10 @@ XXDEBUG((", base_addr=%#0lx", dev->base_addr)); printk("\n"); + /* Enable chip without interrupts for now */ + bmac_enable_and_reset_chip(dev); + bmwrite(dev, INTDISABLE, DisableAll); + dev->open = bmac_open; dev->stop = bmac_close; dev->hard_start_xmit = bmac_output; @@ -1367,7 +1382,6 @@ if (bmac_verify_checksum(dev) != 0) goto err_out_iounmap; - bp = (struct bmac_data *) dev->priv; bp->is_bmac_plus = is_bmac_plus; bp->tx_dma = (volatile struct dbdma_regs *) ioremap(bmac->addrs[1].address, bmac->addrs[1].size); @@ -1386,7 +1400,6 @@ bp->queue = (struct sk_buff_head *)(bp->rx_cmds + N_RX_RING + 1); skb_queue_head_init(bp->queue); - bp->node = bmac; memset((char *) bp->tx_cmds, 0, (N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd)); /* init_timer(&bp->tx_timeout); */ @@ -1408,6 +1421,12 @@ goto err_out_irq1; } + /* Mask chip interrupts and disable chip, will be + * re-enabled on open() + */ + disable_irq(dev->irq); + pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0); + bp->next_bmac = bmac_devs; bmac_devs = dev; return; @@ -1423,6 +1442,12 @@ err_out_iounmap: iounmap((void *)dev->base_addr); err_out: + if (bp->node) { + release_OF_resource(bp->node, 0); + release_OF_resource(bp->node, 1); + release_OF_resource(bp->node, 2); + pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0); + } unregister_netdev(dev); kfree(dev); } @@ -1434,6 +1459,7 @@ /* reset the chip */ bp->opened = 1; bmac_reset_and_enable(dev); + enable_irq(dev->irq); dev->flags |= IFF_RUNNING; return 0; } @@ -1446,6 +1472,7 @@ unsigned short config; int i; + bp->sleeping = 1; dev->flags &= ~(IFF_UP | IFF_RUNNING); /* disable rx and tx */ @@ -1479,6 +1506,8 @@ XXDEBUG(("bmac: all bufs freed\n")); bp->opened = 0; + disable_irq(dev->irq); + pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0); return 0; } @@ -1548,7 +1577,7 @@ bmwrite(dev, TXCFG, (config & ~TxMACEnable)); out_le32(&td->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE|ACTIVE|DEAD)); printk(KERN_ERR "bmac: transmit timeout - resetting\n"); - bmac_reset_chip(dev); + bmac_enable_and_reset_chip(dev); /* restart rx dma */ cp = bus_to_virt(ld_le32(&rd->cmdptr)); @@ -1670,11 +1699,15 @@ bp = (struct bmac_data *) dev->priv; bmac_devs = bp->next_bmac; + unregister_netdev(dev); + + release_OF_resource(bp->node, 0); + release_OF_resource(bp->node, 1); + release_OF_resource(bp->node, 2); free_irq(dev->irq, dev); free_irq(bp->tx_dma_intr, dev); free_irq(bp->rx_dma_intr, dev); - unregister_netdev(dev); kfree(dev); } while (bmac_devs != NULL); } diff -Nru a/drivers/net/dmfe.c b/drivers/net/dmfe.c --- a/drivers/net/dmfe.c Tue Feb 19 18:09:00 2002 +++ b/drivers/net/dmfe.c Tue Feb 19 18:09:00 2002 @@ -61,8 +61,8 @@ */ #define DRV_NAME "dmfe" -#define DRV_VERSION "1.36.3" -#define DRV_RELDATE "2001-11-06" +#define DRV_VERSION "1.36.4" +#define DRV_RELDATE "2002-01-17" #include @@ -99,7 +99,6 @@ #define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */ #define PCI_DM9009_ID 0x90091282 /* Davicom DM9009 ID */ -#define DMFE_SUCC 0 #define DM9102_IO_SIZE 0x80 #define DM9102A_IO_SIZE 0x100 #define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */ diff -Nru a/drivers/net/eepro100.c b/drivers/net/eepro100.c --- a/drivers/net/eepro100.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/eepro100.c Tue Feb 19 18:08:57 2002 @@ -28,7 +28,7 @@ */ static const char *version = -"eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n" +"eepro100.c:v1.09j-t 9/29/99 Donald Becker http://www.scyld.com/network/eepro100.html\n" "eepro100.c: $Revision: 1.36 $ 2000/11/17 Modified by Andrey V. Savochkin and others\n"; /* A few user-configurable values that apply to all boards. @@ -1960,11 +1960,9 @@ switch(cmd) { case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ data->phy_id = phy; case SIOCGMIIREG: /* Read MII PHY register. */ - case SIOCDEVPRIVATE+1: /* for binary compat, remove in 2.5 */ /* FIXME: these operations need to be serialized with MDIO access from the timeout handler. They are currently serialized only with MDIO access from the @@ -1978,7 +1976,6 @@ return 0; case SIOCSMIIREG: /* Write MII PHY register. */ - case SIOCDEVPRIVATE+2: /* for binary compat, remove in 2.5 */ if (!capable(CAP_NET_ADMIN)) return -EPERM; saved_acpi = pci_set_power_state(sp->pdev, 0); diff -Nru a/drivers/net/eexpress.c b/drivers/net/eexpress.c --- a/drivers/net/eexpress.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/eexpress.c Tue Feb 19 18:08:57 2002 @@ -1674,7 +1674,6 @@ unregister_netdev(dev); kfree(dev->priv); dev->priv = NULL; - release_region(dev->base_addr, EEXP_IO_EXTENT); } } } diff -Nru a/drivers/net/gmac.c b/drivers/net/gmac.c --- a/drivers/net/gmac.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1699 +0,0 @@ -/* - * Network device driver for the GMAC ethernet controller on - * Apple G4 Powermacs. - * - * Copyright (C) 2000 Paul Mackerras & Ben. Herrenschmidt - * - * portions based on sunhme.c by David S. Miller - * - * Changes: - * Arnaldo Carvalho de Melo - 08/06/2000 - * - check init_etherdev return in gmac_probe1 - * BenH - 03/09/2000 - * - Add support for new PHYs - * - Add some PowerBook sleep code - * BenH - ??/??/???? - * - PHY updates - * BenH - 08/08/2001 - * - Add more PHYs, fixes to sleep code - * Matt Domsch - 11/12/2001 - * - use library crc32 functions - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_PMAC_PBOOK -#include -#include -#include -#endif - -#include "gmac.h" - -#define DEBUG_PHY - -/* Driver version 1.5, kernel 2.4.x */ -#define GMAC_VERSION "v1.5k4" - -#define DUMMY_BUF_LEN RX_BUF_ALLOC_SIZE + RX_OFFSET + GMAC_BUFFER_ALIGN -static unsigned char *dummy_buf; -static struct net_device *gmacs; - -/* Prototypes */ -static int mii_read(struct gmac *gm, int phy, int r); -static int mii_write(struct gmac *gm, int phy, int r, int v); -static void mii_poll_start(struct gmac *gm); -static void mii_poll_stop(struct gmac *gm); -static void mii_interrupt(struct gmac *gm); -static int mii_lookup_and_reset(struct gmac *gm); -static void mii_setup_phy(struct gmac *gm); -static int mii_do_reset_phy(struct gmac *gm, int phy_addr); -static void mii_init_BCM5400(struct gmac *gm); -static void mii_init_BCM5401(struct gmac *gm); - -static void gmac_set_power(struct gmac *gm, int power_up); -static int gmac_powerup_and_reset(struct net_device *dev); -static void gmac_set_gigabit_mode(struct gmac *gm, int gigabit); -static void gmac_set_duplex_mode(struct gmac *gm, int full_duplex); -static void gmac_mac_init(struct gmac *gm, unsigned char *mac_addr); -static void gmac_init_rings(struct gmac *gm, int from_irq); -static void gmac_start_dma(struct gmac *gm); -static void gmac_stop_dma(struct gmac *gm); -static void gmac_set_multicast(struct net_device *dev); -static int gmac_open(struct net_device *dev); -static int gmac_close(struct net_device *dev); -static void gmac_tx_timeout(struct net_device *dev); -static int gmac_xmit_start(struct sk_buff *skb, struct net_device *dev); -static void gmac_tx_cleanup(struct net_device *dev, int force_cleanup); -static void gmac_receive(struct net_device *dev); -static void gmac_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static struct net_device_stats *gmac_stats(struct net_device *dev); -static int gmac_probe(void); -static void gmac_probe1(struct device_node *gmac); - -#ifdef CONFIG_PMAC_PBOOK -int gmac_sleep_notify(struct pmu_sleep_notifier *self, int when); -static struct pmu_sleep_notifier gmac_sleep_notifier = { - gmac_sleep_notify, SLEEP_LEVEL_NET, -}; -#endif - -/* - * Read via the mii interface from a PHY register - */ -static int -mii_read(struct gmac *gm, int phy, int r) -{ - int timeout; - - GM_OUT(GM_MIF_FRAME_CTL_DATA, - (0x01 << GM_MIF_FRAME_START_SHIFT) | - (0x02 << GM_MIF_FRAME_OPCODE_SHIFT) | - GM_MIF_FRAME_TURNAROUND_HI | - (phy << GM_MIF_FRAME_PHY_ADDR_SHIFT) | - (r << GM_MIF_FRAME_REG_ADDR_SHIFT)); - - for (timeout = 1000; timeout > 0; --timeout) { - udelay(20); - if (GM_IN(GM_MIF_FRAME_CTL_DATA) & GM_MIF_FRAME_TURNAROUND_LO) - return GM_IN(GM_MIF_FRAME_CTL_DATA) & GM_MIF_FRAME_DATA_MASK; - } - return -1; -} - -/* - * Write on the mii interface to a PHY register - */ -static int -mii_write(struct gmac *gm, int phy, int r, int v) -{ - int timeout; - - GM_OUT(GM_MIF_FRAME_CTL_DATA, - (0x01 << GM_MIF_FRAME_START_SHIFT) | - (0x01 << GM_MIF_FRAME_OPCODE_SHIFT) | - GM_MIF_FRAME_TURNAROUND_HI | - (phy << GM_MIF_FRAME_PHY_ADDR_SHIFT) | - (r << GM_MIF_FRAME_REG_ADDR_SHIFT) | - (v & GM_MIF_FRAME_DATA_MASK)); - - for (timeout = 1000; timeout > 0; --timeout) { - udelay(20); - if (GM_IN(GM_MIF_FRAME_CTL_DATA) & GM_MIF_FRAME_TURNAROUND_LO) - return 0; - } - return -1; -} - -/* - * Start MIF autopolling of the PHY status register - */ -static void -mii_poll_start(struct gmac *gm) -{ - unsigned int tmp; - - /* Start the MIF polling on the external transceiver. */ - tmp = GM_IN(GM_MIF_CFG); - tmp &= ~(GM_MIF_CFGPR_MASK | GM_MIF_CFGPD_MASK); - tmp |= ((gm->phy_addr & 0x1f) << GM_MIF_CFGPD_SHIFT); - tmp |= (MII_SR << GM_MIF_CFGPR_SHIFT); - tmp |= GM_MIF_CFGPE; - GM_OUT(GM_MIF_CFG, tmp); - - /* Let the bits set. */ - udelay(GM_MIF_POLL_DELAY); - - GM_OUT(GM_MIF_IRQ_MASK, 0xffc0); -} - -/* - * Stop MIF autopolling of the PHY status register - */ -static void -mii_poll_stop(struct gmac *gm) -{ - GM_OUT(GM_MIF_IRQ_MASK, 0xffff); - GM_BIC(GM_MIF_CFG, GM_MIF_CFGPE); - udelay(GM_MIF_POLL_DELAY); -} - -/* - * Called when the MIF detect a change of the PHY status - * - * handles monitoring the link and updating GMAC with the correct - * duplex mode. - * - * Note: Are we missing status changes ? In this case, we'll have to - * a timer and control the autoneg. process more closely. Also, we may - * want to stop rx and tx side when the link is down. - */ - -/* Link modes of the BCM5400 PHY */ -static int phy_BCM5400_link_table[8][3] = { - { 0, 0, 0 }, /* No link */ - { 0, 0, 0 }, /* 10BT Half Duplex */ - { 1, 0, 0 }, /* 10BT Full Duplex */ - { 0, 1, 0 }, /* 100BT Half Duplex */ - { 0, 1, 0 }, /* 100BT Half Duplex */ - { 1, 1, 0 }, /* 100BT Full Duplex*/ - { 1, 0, 1 }, /* 1000BT */ - { 1, 0, 1 }, /* 1000BT */ -}; - -static void -mii_interrupt(struct gmac *gm) -{ - int phy_status; - int lpar_ability; - - mii_poll_stop(gm); - - /* May the status change before polling is re-enabled ? */ - mii_poll_start(gm); - - /* We read the Auxilliary Status Summary register */ - phy_status = mii_read(gm, gm->phy_addr, MII_SR); - if ((phy_status ^ gm->phy_status) & (MII_SR_ASSC | MII_SR_LKS)) { - int full_duplex = 0; - int link_100 = 0; - int gigabit = 0; -#ifdef DEBUG_PHY - printk(KERN_INFO "%s: Link state change, phy_status: 0x%04x\n", - gm->dev->name, phy_status); -#endif - gm->phy_status = phy_status; - - /* Should we enable that in generic mode ? */ - lpar_ability = mii_read(gm, gm->phy_addr, MII_ANLPA); - if (lpar_ability & MII_ANLPA_PAUS) - GM_BIS(GM_MAC_CTRL_CONFIG, GM_MAC_CTRL_CONF_SND_PAUSE_EN); - else - GM_BIC(GM_MAC_CTRL_CONFIG, GM_MAC_CTRL_CONF_SND_PAUSE_EN); - - /* Link ? Check for speed and duplex */ - if ((phy_status & MII_SR_LKS) && (phy_status & MII_SR_ASSC)) { - int restart = 0; - int aux_stat, link; - switch (gm->phy_type) { - case PHY_B5201: - case PHY_B5221: - aux_stat = mii_read(gm, gm->phy_addr, MII_BCM5201_AUXCTLSTATUS); -#ifdef DEBUG_PHY - printk(KERN_INFO "%s: Link up ! BCM5201/5221 aux_stat: 0x%04x\n", - gm->dev->name, aux_stat); -#endif - full_duplex = ((aux_stat & MII_BCM5201_AUXCTLSTATUS_DUPLEX) != 0); - link_100 = ((aux_stat & MII_BCM5201_AUXCTLSTATUS_SPEED) != 0); - break; - case PHY_B5400: - case PHY_B5401: - case PHY_B5411: - aux_stat = mii_read(gm, gm->phy_addr, MII_BCM5400_AUXSTATUS); - link = (aux_stat & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >> - MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT; -#ifdef DEBUG_PHY - printk(KERN_INFO "%s: Link up ! BCM54xx aux_stat: 0x%04x (link mode: %d)\n", - gm->dev->name, aux_stat, link); -#endif - full_duplex = phy_BCM5400_link_table[link][0]; - link_100 = phy_BCM5400_link_table[link][1]; - gigabit = phy_BCM5400_link_table[link][2]; - break; - case PHY_LXT971: - aux_stat = mii_read(gm, gm->phy_addr, MII_LXT971_STATUS2); -#ifdef DEBUG_PHY - printk(KERN_INFO "%s: Link up ! LXT971 stat2: 0x%04x\n", - gm->dev->name, aux_stat); -#endif - full_duplex = ((aux_stat & MII_LXT971_STATUS2_FULLDUPLEX) != 0); - link_100 = ((aux_stat & MII_LXT971_STATUS2_SPEED) != 0); - break; - default: - full_duplex = (lpar_ability & MII_ANLPA_FDAM) != 0; - link_100 = (lpar_ability & MII_ANLPA_100M) != 0; - break; - } -#ifdef DEBUG_PHY - printk(KERN_INFO "%s: Full Duplex: %d, Speed: %s\n", - gm->dev->name, full_duplex, - gigabit ? "1000" : (link_100 ? "100" : "10")); -#endif - if (gigabit != gm->gigabit) { - gm->gigabit = gigabit; - gmac_set_gigabit_mode(gm, gm->gigabit); - restart = 1; - } - if (full_duplex != gm->full_duplex) { - gm->full_duplex = full_duplex; - gmac_set_duplex_mode(gm, gm->full_duplex); - restart = 1; - } - if (restart) - gmac_start_dma(gm); - } else if (!(phy_status & MII_SR_LKS)) { -#ifdef DEBUG_PHY - printk(KERN_INFO "%s: Link down !\n", gm->dev->name); -#endif - } - } -} - -/* Power management: stop PHY chip for suspend mode - * - * TODO: This will have to be modified is WOL is to be supported. - */ -static void -gmac_suspend(struct gmac* gm) -{ - int data, timeout; - unsigned long flags; - - gm->sleeping = 1; - netif_device_detach(gm->dev); - - - spin_lock_irqsave(&gm->lock, flags); - if (gm->opened) { - disable_irq(gm->dev->irq); - /* Stop polling PHY */ - mii_poll_stop(gm); - } - /* Mask out all chips interrupts */ - GM_OUT(GM_IRQ_MASK, 0xffffffff); - spin_unlock_irqrestore(&gm->lock, flags); - - if (gm->opened) { - int i; - /* Empty Tx ring of any remaining gremlins */ - gmac_tx_cleanup(gm->dev, 1); - - /* Empty Rx ring of any remaining gremlins */ - for (i = 0; i < NRX; ++i) { - if (gm->rx_buff[i] != 0) { - dev_kfree_skb_irq(gm->rx_buff[i]); - gm->rx_buff[i] = 0; - } - } - } - - /* Clear interrupts on 5201 */ - if (gm->phy_type == PHY_B5201 || gm->phy_type == PHY_B5221) - mii_write(gm, gm->phy_addr, MII_BCM5201_INTERRUPT, 0); - - /* Drive MDIO high */ - GM_OUT(GM_MIF_CFG, 0); - - /* Unchanged, don't ask me why */ - data = mii_read(gm, gm->phy_addr, MII_ANLPA); - mii_write(gm, gm->phy_addr, MII_ANLPA, data); - - /* Stop everything */ - GM_OUT(GM_MAC_RX_CONFIG, 0); - GM_OUT(GM_MAC_TX_CONFIG, 0); - GM_OUT(GM_MAC_XIF_CONFIG, 0); - GM_OUT(GM_TX_CONF, 0); - GM_OUT(GM_RX_CONF, 0); - - /* Set MAC in reset state */ - GM_OUT(GM_RESET, GM_RESET_TX | GM_RESET_RX); - for (timeout = 100; timeout > 0; --timeout) { - mdelay(10); - if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0) - break; - } - GM_OUT(GM_MAC_TX_RESET, GM_MAC_TX_RESET_NOW); - GM_OUT(GM_MAC_RX_RESET, GM_MAC_RX_RESET_NOW); - - /* Superisolate PHY */ - if (gm->phy_type == PHY_B5201 || gm->phy_type == PHY_B5221) - mii_write(gm, gm->phy_addr, MII_BCM5201_MULTIPHY, - MII_BCM5201_MULTIPHY_SUPERISOLATE); - - /* Put MDIO in sane electric state. According to an obscure - * Apple comment, not doing so may let them drive some current - * during sleep and possibly damage BCM PHYs. - */ - GM_OUT(GM_MIF_CFG, GM_MIF_CFGBB); - GM_OUT(GM_MIF_BB_CLOCK, 0); - GM_OUT(GM_MIF_BB_DATA, 0); - GM_OUT(GM_MIF_BB_OUT_ENABLE, 0); - GM_OUT(GM_MAC_XIF_CONFIG, - GM_MAC_XIF_CONF_GMII_MODE|GM_MAC_XIF_CONF_MII_INT_LOOP); - (void)GM_IN(GM_MAC_XIF_CONFIG); - - /* Unclock the GMAC chip */ - gmac_set_power(gm, 0); -} - -static void -gmac_resume(struct gmac *gm) -{ - int data; - - if (gmac_powerup_and_reset(gm->dev)) { - printk(KERN_ERR "%s: Couldn't revive gmac ethernet !\n", gm->dev->name); - return; - } - - gm->sleeping = 0; - - if (gm->opened) { - /* Create fresh rings */ - gmac_init_rings(gm, 1); - /* re-initialize the MAC */ - gmac_mac_init(gm, gm->dev->dev_addr); - /* re-initialize the multicast tables & promisc mode if any */ - gmac_set_multicast(gm->dev); - } - - /* Early enable Tx and Rx so that we are clocked */ - GM_BIS(GM_TX_CONF, GM_TX_CONF_DMA_EN); - mdelay(20); - GM_BIS(GM_RX_CONF, GM_RX_CONF_DMA_EN); - mdelay(20); - GM_BIS(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE); - mdelay(20); - GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_ENABLE); - mdelay(20); - if (gm->phy_type == PHY_B5201 || gm->phy_type == PHY_B5221) { - data = mii_read(gm, gm->phy_addr, MII_BCM5201_MULTIPHY); - mii_write(gm, gm->phy_addr, MII_BCM5201_MULTIPHY, - data & ~MII_BCM5201_MULTIPHY_SUPERISOLATE); - } - mdelay(1); - - if (gm->opened) { - /* restart polling PHY */ - mii_interrupt(gm); - /* restart DMA operations */ - gmac_start_dma(gm); - netif_device_attach(gm->dev); - enable_irq(gm->dev->irq); - } else { - /* Driver not opened, just leave things off. Note that - * we could be smart and superisolate the PHY when the - * driver is closed, but I won't do that unless I have - * a better understanding of some electrical issues with - * this PHY chip --BenH - */ - GM_OUT(GM_MAC_RX_CONFIG, 0); - GM_OUT(GM_MAC_TX_CONFIG, 0); - GM_OUT(GM_MAC_XIF_CONFIG, 0); - GM_OUT(GM_TX_CONF, 0); - GM_OUT(GM_RX_CONF, 0); - } -} - -static int -mii_do_reset_phy(struct gmac *gm, int phy_addr) -{ - int mii_control, timeout; - - mii_control = mii_read(gm, phy_addr, MII_CR); - mii_write(gm, phy_addr, MII_CR, mii_control | MII_CR_RST); - mdelay(10); - for (timeout = 100; timeout > 0; --timeout) { - mii_control = mii_read(gm, phy_addr, MII_CR); - if (mii_control == -1) { - printk(KERN_ERR "%s PHY died after reset !\n", - gm->dev->name); - return 1; - } - if ((mii_control & MII_CR_RST) == 0) - break; - mdelay(10); - } - if (mii_control & MII_CR_RST) { - printk(KERN_ERR "%s PHY reset timeout !\n", gm->dev->name); - return 1; - } - mii_write(gm, phy_addr, MII_CR, mii_control & ~MII_CR_ISOL); - return 0; -} - -/* Here's a bunch of configuration routines for - * Broadcom PHYs used on various Mac models. Unfortunately, - * except for the 5201, Broadcom never sent me any documentation, - * so this is from my understanding of Apple's Open Firmware - * drivers and Darwin's implementation - */ - -static void -mii_init_BCM5400(struct gmac *gm) -{ - int data; - - /* Configure for gigabit full duplex */ - data = mii_read(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL); - data |= MII_BCM5400_AUXCONTROL_PWR10BASET; - mii_write(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL, data); - - data = mii_read(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL); - data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP; - mii_write(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL, data); - - mdelay(10); - - /* Reset and configure cascaded 10/100 PHY */ - mii_do_reset_phy(gm, 0x1f); - - data = mii_read(gm, 0x1f, MII_BCM5201_MULTIPHY); - data |= MII_BCM5201_MULTIPHY_SERIALMODE; - mii_write(gm, 0x1f, MII_BCM5201_MULTIPHY, data); - - data = mii_read(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL); - data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET; - mii_write(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL, data); -} - -static void -mii_init_BCM5401(struct gmac *gm) -{ - int data; - int rev; - - rev = mii_read(gm, gm->phy_addr, MII_ID1) & 0x000f; - if (rev == 0 || rev == 3) { - /* Some revisions of 5401 appear to need this - * initialisation sequence to disable, according - * to OF, "tap power management" - * - * WARNING ! OF and Darwin don't agree on the - * register addresses. OF seem to interpret the - * register numbers below as decimal - */ - mii_write(gm, gm->phy_addr, 0x18, 0x0c20); - mii_write(gm, gm->phy_addr, 0x17, 0x0012); - mii_write(gm, gm->phy_addr, 0x15, 0x1804); - mii_write(gm, gm->phy_addr, 0x17, 0x0013); - mii_write(gm, gm->phy_addr, 0x15, 0x1204); - mii_write(gm, gm->phy_addr, 0x17, 0x8006); - mii_write(gm, gm->phy_addr, 0x15, 0x0132); - mii_write(gm, gm->phy_addr, 0x17, 0x8006); - mii_write(gm, gm->phy_addr, 0x15, 0x0232); - mii_write(gm, gm->phy_addr, 0x17, 0x201f); - mii_write(gm, gm->phy_addr, 0x15, 0x0a20); - } - - /* Configure for gigabit full duplex */ - data = mii_read(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL); - data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP; - mii_write(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL, data); - - mdelay(10); - - /* Reset and configure cascaded 10/100 PHY */ - mii_do_reset_phy(gm, 0x1f); - - data = mii_read(gm, 0x1f, MII_BCM5201_MULTIPHY); - data |= MII_BCM5201_MULTIPHY_SERIALMODE; - mii_write(gm, 0x1f, MII_BCM5201_MULTIPHY, data); -} - -static void -mii_init_BCM5411(struct gmac *gm) -{ - int data; - - /* Here's some more Apple black magic to setup - * some voltage stuffs. - */ - mii_write(gm, gm->phy_addr, 0x1c, 0x8c23); - mii_write(gm, gm->phy_addr, 0x1c, 0x8ca3); - mii_write(gm, gm->phy_addr, 0x1c, 0x8c23); - - /* Here, Apple seems to want to reset it, do - * it as well - */ - mii_write(gm, gm->phy_addr, MII_CR, MII_CR_RST); - - /* Start autoneg */ - mii_write(gm, gm->phy_addr, MII_CR, - MII_CR_ASSE|MII_CR_FDM| /* Autospeed, full duplex */ - MII_CR_RAN| - MII_CR_SPEEDSEL2 /* chip specific, gigabit enable ? */); - - data = mii_read(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL); - data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP; - mii_write(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL, data); -} - -static int -mii_lookup_and_reset(struct gmac *gm) -{ - int i, mii_status, mii_control; - - gm->phy_addr = -1; - gm->phy_type = PHY_UNKNOWN; - - /* Hard reset the PHY */ - feature_gmac_phy_reset(gm->of_node); - - /* Find the PHY */ - for(i=0; i<=31; i++) { - mii_control = mii_read(gm, i, MII_CR); - mii_status = mii_read(gm, i, MII_SR); - if (mii_control != -1 && mii_status != -1 && - (mii_control != 0xffff || mii_status != 0xffff)) - break; - } - gm->phy_addr = i; - if (gm->phy_addr > 31) - return 0; - - /* Reset it */ - if (mii_do_reset_phy(gm, gm->phy_addr)) - goto fail; - - /* Read the PHY ID */ - gm->phy_id = (mii_read(gm, gm->phy_addr, MII_ID0) << 16) | - mii_read(gm, gm->phy_addr, MII_ID1); -#ifdef DEBUG_PHY - printk(KERN_INFO "%s: PHY ID: 0x%08x\n", gm->dev->name, gm->phy_id); -#endif - if ((gm->phy_id & MII_BCM5400_MASK) == MII_BCM5400_ID) { - gm->phy_type = PHY_B5400; - printk(KERN_INFO "%s: Found Broadcom BCM5400 PHY (Gigabit)\n", - gm->dev->name); - mii_init_BCM5400(gm); - } else if ((gm->phy_id & MII_BCM5401_MASK) == MII_BCM5401_ID) { - gm->phy_type = PHY_B5401; - printk(KERN_INFO "%s: Found Broadcom BCM5401 PHY (Gigabit)\n", - gm->dev->name); - mii_init_BCM5401(gm); - } else if ((gm->phy_id & MII_BCM5411_MASK) == MII_BCM5411_ID) { - gm->phy_type = PHY_B5411; - printk(KERN_INFO "%s: Found Broadcom BCM5411 PHY (Gigabit)\n", - gm->dev->name); - mii_init_BCM5411(gm); - } else if ((gm->phy_id & MII_BCM5201_MASK) == MII_BCM5201_ID) { - gm->phy_type = PHY_B5201; - printk(KERN_INFO "%s: Found Broadcom BCM5201 PHY\n", gm->dev->name); - } else if ((gm->phy_id & MII_BCM5221_MASK) == MII_BCM5221_ID) { - gm->phy_type = PHY_B5221; - printk(KERN_INFO "%s: Found Broadcom BCM5221 PHY\n", gm->dev->name); - } else if ((gm->phy_id & MII_LXT971_MASK) == MII_LXT971_ID) { - gm->phy_type = PHY_LXT971; - printk(KERN_INFO "%s: Found LevelOne LX971 PHY\n", gm->dev->name); - } else { - printk(KERN_WARNING "%s: Warning ! Unknown PHY ID 0x%08x, using generic mode...\n", - gm->dev->name, gm->phy_id); - } - - return 1; - -fail: - gm->phy_addr = -1; - return 0; -} - -/* - * Setup the PHY autonegociation parameters - * - * Code to force the PHY duplex mode and speed should be - * added here - */ -static void -mii_setup_phy(struct gmac *gm) -{ - int data; - - /* Stop auto-negociation */ - data = mii_read(gm, gm->phy_addr, MII_CR); - mii_write(gm, gm->phy_addr, MII_CR, data & ~MII_CR_ASSE); - - /* Set advertisement to 10/100 and Half/Full duplex - * (full capabilities) */ - data = mii_read(gm, gm->phy_addr, MII_ANA); - data |= MII_ANA_TXAM | MII_ANA_FDAM | MII_ANA_10M; - mii_write(gm, gm->phy_addr, MII_ANA, data); - - /* Restart auto-negociation */ - data = mii_read(gm, gm->phy_addr, MII_CR); - data |= MII_CR_ASSE; - mii_write(gm, gm->phy_addr, MII_CR, data); - data |= MII_CR_RAN; - mii_write(gm, gm->phy_addr, MII_CR, data); -} - -/* - * Turn On/Off the gmac cell inside Uni-N - * - * ToDo: Add code to support powering down of the PHY. - */ -static void -gmac_set_power(struct gmac *gm, int power_up) -{ - if (power_up) { - feature_set_gmac_power(gm->of_node, 1); - if (gm->pci_devfn != 0xff) { - u16 cmd; - - /* - * Make sure PCI is correctly configured - * - * We use old pci_bios versions of the function since, by - * default, gmac is not powered up, and so will be absent - * from the kernel initial PCI lookup. - * - * Should be replaced by 2.4 new PCI mecanisms and really - * regiser the device. - */ - pcibios_read_config_word(gm->pci_bus, gm->pci_devfn, - PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; - pcibios_write_config_word(gm->pci_bus, gm->pci_devfn, - PCI_COMMAND, cmd); - pcibios_write_config_byte(gm->pci_bus, gm->pci_devfn, - PCI_LATENCY_TIMER, 16); - pcibios_write_config_byte(gm->pci_bus, gm->pci_devfn, - PCI_CACHE_LINE_SIZE, 8); - } - } else { - feature_set_gmac_power(gm->of_node, 0); - } -} - -/* - * Makes sure the GMAC cell is powered up, and reset it - */ -static int -gmac_powerup_and_reset(struct net_device *dev) -{ - struct gmac *gm = (struct gmac *) dev->priv; - int timeout; - - /* turn on GB clock */ - gmac_set_power(gm, 1); - /* Perform a software reset */ - GM_OUT(GM_RESET, GM_RESET_TX | GM_RESET_RX); - for (timeout = 100; timeout > 0; --timeout) { - mdelay(10); - if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0) { - /* Mask out all chips interrupts */ - GM_OUT(GM_IRQ_MASK, 0xffffffff); - GM_OUT(GM_MAC_TX_RESET, GM_MAC_TX_RESET_NOW); - GM_OUT(GM_MAC_RX_RESET, GM_MAC_RX_RESET_NOW); - return 0; - } - } - printk(KERN_ERR "%s reset failed!\n", dev->name); - gmac_set_power(gm, 0); - gm->phy_type = 0; - return -1; -} - -/* - * Set the MAC duplex mode. - * - * Side effect: stops Tx MAC - */ -static void -gmac_set_duplex_mode(struct gmac *gm, int full_duplex) -{ - /* Stop Tx MAC */ - GM_BIC(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE); - while(GM_IN(GM_MAC_TX_CONFIG) & GM_MAC_TX_CONF_ENABLE) - ; - - if (full_duplex) { - GM_BIS(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_IGNORE_CARRIER - | GM_MAC_TX_CONF_IGNORE_COLL); - GM_BIC(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_DISABLE_ECHO); - } else { - GM_BIC(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_IGNORE_CARRIER - | GM_MAC_TX_CONF_IGNORE_COLL); - GM_BIS(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_DISABLE_ECHO); - } -} - -/* Set the MAC gigabit mode. Side effect: stops Tx MAC */ -static void -gmac_set_gigabit_mode(struct gmac *gm, int gigabit) -{ - /* Stop Tx MAC */ - GM_BIC(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE); - while(GM_IN(GM_MAC_TX_CONFIG) & GM_MAC_TX_CONF_ENABLE) - ; - - if (gigabit) { - GM_BIS(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_GMII_MODE); - } else { - GM_BIC(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_GMII_MODE); - } -} - -/* - * Initialize a bunch of registers to put the chip into a known - * and hopefully happy state - */ -static void -gmac_mac_init(struct gmac *gm, unsigned char *mac_addr) -{ - int i, fifo_size; - - /* Set random seed to low bits of MAC address */ - GM_OUT(GM_MAC_RANDOM_SEED, mac_addr[5] | (mac_addr[4] << 8)); - - /* Configure the data path mode to MII/GII */ - GM_OUT(GM_PCS_DATAPATH_MODE, GM_PCS_DATAPATH_MII); - - /* Configure XIF to MII mode. Full duplex led is set - * by Apple, so... - */ - GM_OUT(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_TX_MII_OUT_EN - | GM_MAC_XIF_CONF_FULL_DPLX_LED); - - /* Mask out all MAC interrupts */ - GM_OUT(GM_MAC_TX_MASK, 0xffff); - GM_OUT(GM_MAC_RX_MASK, 0xffff); - GM_OUT(GM_MAC_CTRLSTAT_MASK, 0xff); - - /* Setup bits of MAC */ - GM_OUT(GM_MAC_SND_PAUSE, GM_MAC_SND_PAUSE_DEFAULT); - GM_OUT(GM_MAC_CTRL_CONFIG, GM_MAC_CTRL_CONF_RCV_PAUSE_EN); - - /* Configure GEM DMA */ - GM_OUT(GM_GCONF, GM_GCONF_BURST_SZ | - (31 << GM_GCONF_TXDMA_LIMIT_SHIFT) | - (31 << GM_GCONF_RXDMA_LIMIT_SHIFT)); - GM_OUT(GM_TX_CONF, - (GM_TX_CONF_FIFO_THR_DEFAULT << GM_TX_CONF_FIFO_THR_SHIFT) | - NTX_CONF); - - /* 34 byte offset for checksum computation. This works because ip_input() will clear out - * the skb->csum and skb->ip_summed fields and recompute the csum if IP options are - * present in the header. 34 == (ethernet header len) + sizeof(struct iphdr) - */ - GM_OUT(GM_RX_CONF, - (RX_OFFSET << GM_RX_CONF_FBYTE_OFF_SHIFT) | - (0x22 << GM_RX_CONF_CHK_START_SHIFT) | - (GM_RX_CONF_DMA_THR_DEFAULT << GM_RX_CONF_DMA_THR_SHIFT) | - NRX_CONF); - - /* Configure other bits of MAC */ - GM_OUT(GM_MAC_INTR_PKT_GAP0, GM_MAC_INTR_PKT_GAP0_DEFAULT); - GM_OUT(GM_MAC_INTR_PKT_GAP1, GM_MAC_INTR_PKT_GAP1_DEFAULT); - GM_OUT(GM_MAC_INTR_PKT_GAP2, GM_MAC_INTR_PKT_GAP2_DEFAULT); - GM_OUT(GM_MAC_MIN_FRAME_SIZE, GM_MAC_MIN_FRAME_SIZE_DEFAULT); - GM_OUT(GM_MAC_MAX_FRAME_SIZE, GM_MAC_MAX_FRAME_SIZE_DEFAULT); - GM_OUT(GM_MAC_PREAMBLE_LEN, GM_MAC_PREAMBLE_LEN_DEFAULT); - GM_OUT(GM_MAC_JAM_SIZE, GM_MAC_JAM_SIZE_DEFAULT); - GM_OUT(GM_MAC_ATTEMPT_LIMIT, GM_MAC_ATTEMPT_LIMIT_DEFAULT); - GM_OUT(GM_MAC_SLOT_TIME, GM_MAC_SLOT_TIME_DEFAULT); - GM_OUT(GM_MAC_CONTROL_TYPE, GM_MAC_CONTROL_TYPE_DEFAULT); - - /* Setup MAC addresses, clear filters, clear hash table */ - GM_OUT(GM_MAC_ADDR_NORMAL0, (mac_addr[4] << 8) + mac_addr[5]); - GM_OUT(GM_MAC_ADDR_NORMAL1, (mac_addr[2] << 8) + mac_addr[3]); - GM_OUT(GM_MAC_ADDR_NORMAL2, (mac_addr[0] << 8) + mac_addr[1]); - GM_OUT(GM_MAC_ADDR_ALT0, 0); - GM_OUT(GM_MAC_ADDR_ALT1, 0); - GM_OUT(GM_MAC_ADDR_ALT2, 0); - GM_OUT(GM_MAC_ADDR_CTRL0, 0x0001); - GM_OUT(GM_MAC_ADDR_CTRL1, 0xc200); - GM_OUT(GM_MAC_ADDR_CTRL2, 0x0180); - GM_OUT(GM_MAC_ADDR_FILTER0, 0); - GM_OUT(GM_MAC_ADDR_FILTER1, 0); - GM_OUT(GM_MAC_ADDR_FILTER2, 0); - GM_OUT(GM_MAC_ADDR_FILTER_MASK1_2, 0); - GM_OUT(GM_MAC_ADDR_FILTER_MASK0, 0); - for (i = 0; i < 27; ++i) - GM_OUT(GM_MAC_ADDR_FILTER_HASH0 + i, 0); - - /* Clear stat counters */ - GM_OUT(GM_MAC_COLLISION_CTR, 0); - GM_OUT(GM_MAC_FIRST_COLLISION_CTR, 0); - GM_OUT(GM_MAC_EXCS_COLLISION_CTR, 0); - GM_OUT(GM_MAC_LATE_COLLISION_CTR, 0); - GM_OUT(GM_MAC_DEFER_TIMER_COUNTER, 0); - GM_OUT(GM_MAC_PEAK_ATTEMPTS, 0); - GM_OUT(GM_MAC_RX_FRAME_CTR, 0); - GM_OUT(GM_MAC_RX_LEN_ERR_CTR, 0); - GM_OUT(GM_MAC_RX_ALIGN_ERR_CTR, 0); - GM_OUT(GM_MAC_RX_CRC_ERR_CTR, 0); - GM_OUT(GM_MAC_RX_CODE_VIOLATION_CTR, 0); - - /* default to half duplex */ - GM_OUT(GM_MAC_TX_CONFIG, 0); - GM_OUT(GM_MAC_RX_CONFIG, 0); - gmac_set_duplex_mode(gm, gm->full_duplex); - - /* Setup pause thresholds */ - fifo_size = GM_IN(GM_RX_FIFO_SIZE); - GM_OUT(GM_RX_PTH, - ((fifo_size - ((GM_MAC_MAX_FRAME_SIZE_ALIGN + 8) * 2 / GM_RX_PTH_UNITS)) - << GM_RX_PTH_OFF_SHIFT) | - ((fifo_size - ((GM_MAC_MAX_FRAME_SIZE_ALIGN + 8) * 3 / GM_RX_PTH_UNITS)) - << GM_RX_PTH_ON_SHIFT)); - - /* Setup interrupt blanking */ - if (GM_IN(GM_BIF_CFG) & GM_BIF_CFG_M66EN) - GM_OUT(GM_RX_BLANK, (5 << GM_RX_BLANK_INTR_PACKETS_SHIFT) - | (8 << GM_RX_BLANK_INTR_TIME_SHIFT)); - else - GM_OUT(GM_RX_BLANK, (5 << GM_RX_BLANK_INTR_PACKETS_SHIFT) - | (4 << GM_RX_BLANK_INTR_TIME_SHIFT)); -} - -/* - * Fill the Rx and Tx rings with good initial values, alloc - * fresh Rx skb's. - */ -static void -gmac_init_rings(struct gmac *gm, int from_irq) -{ - int i; - struct sk_buff *skb; - unsigned char *data; - struct gmac_dma_desc *ring; - int gfp_flags = GFP_KERNEL; - - if (from_irq || in_interrupt()) - gfp_flags = GFP_ATOMIC; - - /* init rx ring */ - ring = (struct gmac_dma_desc *) gm->rxring; - memset(ring, 0, NRX * sizeof(struct gmac_dma_desc)); - for (i = 0; i < NRX; ++i, ++ring) { - data = dummy_buf; - gm->rx_buff[i] = skb = gmac_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags); - if (skb != 0) { - skb->dev = gm->dev; - skb_put(skb, ETH_FRAME_LEN + RX_OFFSET); - skb_reserve(skb, RX_OFFSET); - data = skb->data - RX_OFFSET; - } - st_le32(&ring->lo_addr, virt_to_bus(data)); - st_le32(&ring->size, RX_SZ_OWN | ((RX_BUF_ALLOC_SIZE-RX_OFFSET) << RX_SZ_SHIFT)); - } - - /* init tx ring */ - ring = (struct gmac_dma_desc *) gm->txring; - memset(ring, 0, NTX * sizeof(struct gmac_dma_desc)); - - gm->next_rx = 0; - gm->next_tx = 0; - gm->tx_gone = 0; - - /* set pointers in chip */ - mb(); - GM_OUT(GM_RX_DESC_HI, 0); - GM_OUT(GM_RX_DESC_LO, virt_to_bus(gm->rxring)); - GM_OUT(GM_TX_DESC_HI, 0); - GM_OUT(GM_TX_DESC_LO, virt_to_bus(gm->txring)); -} - -/* - * Start the Tx and Rx DMA engines and enable interrupts - * - * Note: The various mdelay(20); come from Darwin implentation. Some - * tests (doc ?) are needed to replace those with something more intrusive. - */ -static void -gmac_start_dma(struct gmac *gm) -{ - /* Enable Tx and Rx */ - GM_BIS(GM_TX_CONF, GM_TX_CONF_DMA_EN); - mdelay(20); - GM_BIS(GM_RX_CONF, GM_RX_CONF_DMA_EN); - mdelay(20); - GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_ENABLE); - mdelay(20); - GM_BIS(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE); - mdelay(20); - /* Kick the receiver and enable interrupts */ - GM_OUT(GM_RX_KICK, NRX); - GM_BIC(GM_IRQ_MASK, GM_IRQ_TX_INT_ME | - GM_IRQ_TX_ALL | - GM_IRQ_RX_DONE | - GM_IRQ_RX_TAG_ERR | - GM_IRQ_MAC_RX | - GM_IRQ_MIF | - GM_IRQ_BUS_ERROR); -} - -/* - * Stop the Tx and Rx DMA engines after disabling interrupts - * - * Note: The various mdelay(20); come from Darwin implentation. Some - * tests (doc ?) are needed to replace those with something more intrusive. - */ -static void -gmac_stop_dma(struct gmac *gm) -{ - /* disable interrupts */ - GM_OUT(GM_IRQ_MASK, 0xffffffff); - /* Enable Tx and Rx */ - GM_BIC(GM_TX_CONF, GM_TX_CONF_DMA_EN); - mdelay(20); - GM_BIC(GM_RX_CONF, GM_RX_CONF_DMA_EN); - mdelay(20); - GM_BIC(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_ENABLE); - mdelay(20); - GM_BIC(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE); - mdelay(20); -} - -/* - * Configure promisc mode and setup multicast hash table - * filter - */ -static void -gmac_set_multicast(struct net_device *dev) -{ - struct gmac *gm = (struct gmac *) dev->priv; - struct dev_mc_list *dmi = dev->mc_list; - int i,j,k,b; - u32 crc; - int multicast_hash = 0; - int multicast_all = 0; - int promisc = 0; - - if (gm->sleeping) - return; - - /* Lock out others. */ - netif_stop_queue(dev); - - - if (dev->flags & IFF_PROMISC) - promisc = 1; - else if ((dev->flags & IFF_ALLMULTI) /* || (dev->mc_count > XXX) */) { - multicast_all = 1; - } else { - u16 hash_table[16]; - - for(i = 0; i < 16; i++) - hash_table[i] = 0; - - for (i = 0; i < dev->mc_count; i++) { - crc = ether_crc_le(6, dmi->dmi_addr); - j = crc >> 24; /* bit number in multicast_filter */ - hash_table[j >> 4] |= 1 << (15 - (j & 0xf)); - dmi = dmi->next; - } - - for (i = 0; i < 16; i++) - GM_OUT(GM_MAC_ADDR_FILTER_HASH0 + (i*4), hash_table[i]); - GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_HASH_ENABLE); - multicast_hash = 1; - } - - if (promisc) - GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_RX_ALL); - else - GM_BIC(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_RX_ALL); - - if (multicast_hash) - GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_HASH_ENABLE); - else - GM_BIC(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_HASH_ENABLE); - - if (multicast_all) - GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_RX_ALL_MULTI); - else - GM_BIC(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_RX_ALL_MULTI); - - /* Let us get going again. */ - netif_wake_queue(dev); -} - -/* - * Open the interface - */ -static int -gmac_open(struct net_device *dev) -{ - int ret; - struct gmac *gm = (struct gmac *) dev->priv; - - /* Power up and reset chip */ - if (gmac_powerup_and_reset(dev)) - return -EIO; - - /* Get our interrupt */ - ret = request_irq(dev->irq, gmac_interrupt, 0, dev->name, dev); - if (ret) { - printk(KERN_ERR "%s can't get irq %d\n", dev->name, dev->irq); - return ret; - } - - gm->full_duplex = 0; - gm->phy_status = 0; - - /* Find a PHY */ - if (!mii_lookup_and_reset(gm)) - printk(KERN_WARNING "%s WARNING ! Can't find PHY\n", dev->name); - - /* Configure the PHY */ - mii_setup_phy(gm); - - /* Initialize the descriptor rings */ - gmac_init_rings(gm, 0); - - /* Initialize the MAC */ - gmac_mac_init(gm, dev->dev_addr); - - /* Initialize the multicast tables & promisc mode if any */ - gmac_set_multicast(dev); - - /* - * Check out PHY status and start auto-poll - * - * Note: do this before enabling interrutps - */ - mii_interrupt(gm); - - /* Start the chip */ - gmac_start_dma(gm); - - gm->opened = 1; - - return 0; -} - -/* - * Close the interface - */ -static int -gmac_close(struct net_device *dev) -{ - struct gmac *gm = (struct gmac *) dev->priv; - int i; - - gm->opened = 0; - - /* Stop chip and interrupts */ - gmac_stop_dma(gm); - - /* Stop polling PHY */ - mii_poll_stop(gm); - - /* Free interrupt */ - free_irq(dev->irq, dev); - - /* Shut down chip */ - gmac_set_power(gm, 0); - gm->phy_type = 0; - - /* Empty rings of any remaining gremlins */ - for (i = 0; i < NRX; ++i) { - if (gm->rx_buff[i] != 0) { - dev_kfree_skb(gm->rx_buff[i]); - gm->rx_buff[i] = 0; - } - } - for (i = 0; i < NTX; ++i) { - if (gm->tx_buff[i] != 0) { - dev_kfree_skb(gm->tx_buff[i]); - gm->tx_buff[i] = 0; - } - } - - return 0; -} - -#ifdef CONFIG_PMAC_PBOOK -int -gmac_sleep_notify(struct pmu_sleep_notifier *self, int when) -{ - struct gmac *gm; - - /* XXX should handle more than one */ - if (gmacs == NULL) - return PBOOK_SLEEP_OK; - - gm = (struct gmac *) gmacs->priv; - if (!gm->opened) - return PBOOK_SLEEP_OK; - - switch (when) { - case PBOOK_SLEEP_REQUEST: - break; - case PBOOK_SLEEP_REJECT: - break; - case PBOOK_SLEEP_NOW: - gmac_suspend(gm); - break; - case PBOOK_WAKE: - gmac_resume(gm); - break; - } - return PBOOK_SLEEP_OK; -} -#endif /* CONFIG_PMAC_PBOOK */ - -/* - * Handle a transmit timeout - */ -static void -gmac_tx_timeout(struct net_device *dev) -{ - struct gmac *gm = (struct gmac *) dev->priv; - int i, timeout; - unsigned long flags; - - if (gm->sleeping) - return; - - printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name); - - spin_lock_irqsave(&gm->lock, flags); - - /* Stop chip */ - gmac_stop_dma(gm); - /* Empty Tx ring of any remaining gremlins */ - gmac_tx_cleanup(dev, 1); - /* Empty Rx ring of any remaining gremlins */ - for (i = 0; i < NRX; ++i) { - if (gm->rx_buff[i] != 0) { - dev_kfree_skb_irq(gm->rx_buff[i]); - gm->rx_buff[i] = 0; - } - } - /* Perform a software reset */ - GM_OUT(GM_RESET, GM_RESET_TX | GM_RESET_RX); - for (timeout = 100; timeout > 0; --timeout) { - mdelay(10); - if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0) { - /* Mask out all chips interrupts */ - GM_OUT(GM_IRQ_MASK, 0xffffffff); - GM_OUT(GM_MAC_TX_RESET, GM_MAC_TX_RESET_NOW); - GM_OUT(GM_MAC_RX_RESET, GM_MAC_RX_RESET_NOW); - break; - } - } - if (!timeout) - printk(KERN_ERR "%s reset chip failed !\n", dev->name); - /* Create fresh rings */ - gmac_init_rings(gm, 1); - /* re-initialize the MAC */ - gmac_mac_init(gm, dev->dev_addr); - /* re-initialize the multicast tables & promisc mode if any */ - gmac_set_multicast(dev); - /* Restart PHY auto-poll */ - mii_interrupt(gm); - /* Restart chip */ - gmac_start_dma(gm); - - spin_unlock_irqrestore(&gm->lock, flags); - - netif_wake_queue(dev); -} - -/* - * Add a packet to the transmit ring - */ -static int -gmac_xmit_start(struct sk_buff *skb, struct net_device *dev) -{ - struct gmac *gm = (struct gmac *) dev->priv; - volatile struct gmac_dma_desc *dp; - unsigned long flags; - int i; - - if (gm->sleeping) - return 1; - - spin_lock_irqsave(&gm->lock, flags); - - i = gm->next_tx; - if (gm->tx_buff[i] != 0) { - /* - * Buffer is full, can't send this packet at the moment - * - * Can this ever happen in 2.4 ? - */ - netif_stop_queue(dev); - spin_unlock_irqrestore(&gm->lock, flags); - return 1; - } - gm->next_tx = (i + 1) & (NTX - 1); - gm->tx_buff[i] = skb; - - dp = &gm->txring[i]; - /* FIXME: Interrupt on all packet for now, change this to every N packet, - * with N to be adjusted - */ - dp->flags = TX_FL_INTERRUPT; - dp->hi_addr = 0; - st_le32(&dp->lo_addr, virt_to_bus(skb->data)); - mb(); - st_le32(&dp->size, TX_SZ_SOP | TX_SZ_EOP | skb->len); - mb(); - - GM_OUT(GM_TX_KICK, gm->next_tx); - - if (gm->tx_buff[gm->next_tx] != 0) - netif_stop_queue(dev); - - spin_unlock_irqrestore(&gm->lock, flags); - - dev->trans_start = jiffies; - - return 0; -} - -/* - * Handle servicing of the transmit ring by deallocating used - * Tx packets and restoring flow control when necessary - */ -static void -gmac_tx_cleanup(struct net_device *dev, int force_cleanup) -{ - struct gmac *gm = (struct gmac *) dev->priv; - volatile struct gmac_dma_desc *dp; - struct sk_buff *skb; - int gone, i; - - i = gm->tx_gone; - - /* Note: If i==gone, we empty the entire ring. This works because - * if the ring was empty, we wouldn't have received the interrupt - */ - do { - gone = GM_IN(GM_TX_COMP); - skb = gm->tx_buff[i]; - if (skb == NULL) - break; - dp = &gm->txring[i]; - if (force_cleanup) - ++gm->stats.tx_errors; - else { - ++gm->stats.tx_packets; - gm->stats.tx_bytes += skb->len; - } - gm->tx_buff[i] = NULL; - dev_kfree_skb_irq(skb); - if (++i >= NTX) - i = 0; - } while (force_cleanup || i != gone); - gm->tx_gone = i; - - if (!force_cleanup && netif_queue_stopped(dev) && - (gm->tx_buff[gm->next_tx] == 0)) - netif_wake_queue(dev); -} - -/* - * Handle servicing of receive ring - */ -static void -gmac_receive(struct net_device *dev) -{ - struct gmac *gm = (struct gmac *) dev->priv; - int i = gm->next_rx; - volatile struct gmac_dma_desc *dp; - struct sk_buff *skb, *new_skb; - int len, flags, drop, last; - unsigned char *data; - u16 csum; - - last = -1; - for (;;) { - dp = &gm->rxring[i]; - /* Buffer not yet filled, no more Rx buffers to handle */ - if (ld_le32(&dp->size) & RX_SZ_OWN) - break; - /* Get packet length, flags, etc... */ - len = (ld_le32(&dp->size) >> 16) & 0x7fff; - flags = ld_le32(&dp->flags); - skb = gm->rx_buff[i]; - drop = 0; - new_skb = NULL; - csum = ld_le32(&dp->size) & RX_SZ_CKSUM_MASK; - - /* Handle errors */ - if ((len < ETH_ZLEN)||(flags & RX_FL_CRC_ERROR)||(!skb)) { - ++gm->stats.rx_errors; - if (len < ETH_ZLEN) - ++gm->stats.rx_length_errors; - if (flags & RX_FL_CRC_ERROR) - ++gm->stats.rx_crc_errors; - if (!skb) { - ++gm->stats.rx_dropped; - skb = gmac_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); - if (skb) { - gm->rx_buff[i] = skb; - skb->dev = dev; - skb_put(skb, ETH_FRAME_LEN + RX_OFFSET); - skb_reserve(skb, RX_OFFSET); - } - } - drop = 1; - } else { - /* Large packet, alloc a new skb for the ring */ - if (len > RX_COPY_THRESHOLD) { - new_skb = gmac_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); - if(!new_skb) { - printk(KERN_INFO "%s: Out of SKBs in Rx, packet dropped !\n", - dev->name); - drop = 1; - ++gm->stats.rx_dropped; - goto finish; - } - - gm->rx_buff[i] = new_skb; - new_skb->dev = dev; - skb_put(new_skb, ETH_FRAME_LEN + RX_OFFSET); - skb_reserve(new_skb, RX_OFFSET); - skb_trim(skb, len); - } else { - /* Small packet, copy it to a new small skb */ - struct sk_buff *copy_skb = dev_alloc_skb(len + RX_OFFSET); - - if(!copy_skb) { - printk(KERN_INFO "%s: Out of SKBs in Rx, packet dropped !\n", - dev->name); - drop = 1; - ++gm->stats.rx_dropped; - goto finish; - } - - copy_skb->dev = dev; - skb_reserve(copy_skb, RX_OFFSET); - skb_put(copy_skb, len); - memcpy(copy_skb->data, skb->data, len); - - new_skb = skb; - skb = copy_skb; - } - } - finish: - /* Need to drop packet ? */ - if (drop) { - new_skb = skb; - skb = NULL; - } - - /* Put back ring entry */ - data = new_skb ? (new_skb->data - RX_OFFSET) : dummy_buf; - dp->hi_addr = 0; - st_le32(&dp->lo_addr, virt_to_bus(data)); - mb(); - st_le32(&dp->size, RX_SZ_OWN | ((RX_BUF_ALLOC_SIZE-RX_OFFSET) << RX_SZ_SHIFT)); - - /* Got Rx packet ? */ - if (skb) { - /* Yes, baby, keep that hot ;) */ - if(!(csum ^ 0xffff)) - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; - skb->ip_summed = CHECKSUM_NONE; - skb->protocol = eth_type_trans(skb, dev); - gm->stats.rx_bytes += skb->len; - netif_rx(skb); - dev->last_rx = jiffies; - ++gm->stats.rx_packets; - } - - last = i; - if (++i >= NRX) - i = 0; - } - gm->next_rx = i; - if (last >= 0) { - mb(); - GM_OUT(GM_RX_KICK, last & 0xfffffffc); - } -} - -/* - * Service chip interrupts - */ -static void -gmac_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *) dev_id; - struct gmac *gm = (struct gmac *) dev->priv; - unsigned int status; - - status = GM_IN(GM_IRQ_STATUS); - if (status & (GM_IRQ_BUS_ERROR | GM_IRQ_MIF)) - GM_OUT(GM_IRQ_ACK, status & (GM_IRQ_BUS_ERROR | GM_IRQ_MIF)); - - if (status & (GM_IRQ_RX_TAG_ERR | GM_IRQ_BUS_ERROR)) { - printk(KERN_ERR "%s: IRQ Error status: 0x%08x\n", - dev->name, status); - } - - if (status & GM_IRQ_MIF) { - spin_lock(&gm->lock); - mii_interrupt(gm); - spin_unlock(&gm->lock); - } - - if (status & GM_IRQ_RX_DONE) { - spin_lock(&gm->lock); - gmac_receive(dev); - spin_unlock(&gm->lock); - } - - if (status & (GM_IRQ_TX_INT_ME | GM_IRQ_TX_ALL)) { - spin_lock(&gm->lock); - gmac_tx_cleanup(dev, 0); - spin_unlock(&gm->lock); - } -} - -/* - * Retreive some error stats from chip and return them - * to above layer - */ -static struct net_device_stats * -gmac_stats(struct net_device *dev) -{ - struct gmac *gm = (struct gmac *) dev->priv; - struct net_device_stats *stats = &gm->stats; - - if (gm && gm->opened && !gm->sleeping) { - stats->rx_crc_errors += GM_IN(GM_MAC_RX_CRC_ERR_CTR); - GM_OUT(GM_MAC_RX_CRC_ERR_CTR, 0); - - stats->rx_frame_errors += GM_IN(GM_MAC_RX_ALIGN_ERR_CTR); - GM_OUT(GM_MAC_RX_ALIGN_ERR_CTR, 0); - - stats->rx_length_errors += GM_IN(GM_MAC_RX_LEN_ERR_CTR); - GM_OUT(GM_MAC_RX_LEN_ERR_CTR, 0); - - stats->tx_aborted_errors += GM_IN(GM_MAC_EXCS_COLLISION_CTR); - - stats->collisions += - (GM_IN(GM_MAC_EXCS_COLLISION_CTR) + - GM_IN(GM_MAC_LATE_COLLISION_CTR)); - GM_OUT(GM_MAC_EXCS_COLLISION_CTR, 0); - GM_OUT(GM_MAC_LATE_COLLISION_CTR, 0); - } - - return stats; -} - -static int __init -gmac_probe(void) -{ - struct device_node *gmac; - - /* We bump use count during probe since get_free_page can sleep - * which can be a race condition if module is unloaded at this - * point. - */ - MOD_INC_USE_COUNT; - - /* - * We don't use PCI scanning on pmac since the GMAC cell is disabled - * by default, and thus absent from kernel original PCI probing. - */ - for (gmac = find_compatible_devices("network", "gmac"); gmac != 0; - gmac = gmac->next) - gmac_probe1(gmac); - -#ifdef CONFIG_PMAC_PBOOK - if (gmacs) - pmu_register_sleep_notifier(&gmac_sleep_notifier); -#endif - - MOD_DEC_USE_COUNT; - - return gmacs? 0: -ENODEV; -} - -static void -gmac_probe1(struct device_node *gmac) -{ - struct gmac *gm; - unsigned long tx_descpage, rx_descpage; - unsigned char *addr; - struct net_device *dev; - int i; - - if (gmac->n_addrs < 1 || gmac->n_intrs < 1) { - printk(KERN_ERR "can't use GMAC %s: %d addrs and %d intrs\n", - gmac->full_name, gmac->n_addrs, gmac->n_intrs); - return; - } - - addr = get_property(gmac, "local-mac-address", NULL); - if (addr == NULL) { - printk(KERN_ERR "Can't get mac-address for GMAC %s\n", - gmac->full_name); - return; - } - - if (dummy_buf == NULL) { - dummy_buf = kmalloc(DUMMY_BUF_LEN, GFP_KERNEL); - if (dummy_buf == NULL) { - printk(KERN_ERR "GMAC: failed to allocated dummy buffer\n"); - return; - } - } - - tx_descpage = get_free_page(GFP_KERNEL); - if (tx_descpage == 0) { - printk(KERN_ERR "GMAC: can't get a page for tx descriptors\n"); - return; - } - rx_descpage = get_free_page(GFP_KERNEL); - if (rx_descpage == 0) { - printk(KERN_ERR "GMAC: can't get a page for rx descriptors\n"); - goto out_txdesc; - } - - dev = init_etherdev(NULL, sizeof(struct gmac)); - if (!dev) { - printk(KERN_ERR "GMAC: init_etherdev failed, out of memory\n"); - goto out_rxdesc; - } - SET_MODULE_OWNER(dev); - - gm = dev->priv; - dev->base_addr = gmac->addrs[0].address; - gm->regs = (volatile unsigned int *) - ioremap(gmac->addrs[0].address, 0x10000); - if (!gm->regs) { - printk(KERN_ERR "GMAC: unable to map I/O registers\n"); - goto out_unreg; - } - dev->irq = gmac->intrs[0].line; - gm->dev = dev; - gm->of_node = gmac; - - spin_lock_init(&gm->lock); - - if (pci_device_from_OF_node(gmac, &gm->pci_bus, &gm->pci_devfn)) { - gm->pci_bus = gm->pci_devfn = 0xff; - printk(KERN_ERR "Can't locate GMAC PCI entry\n"); - } - - printk(KERN_INFO "%s: GMAC at", dev->name); - for (i = 0; i < 6; ++i) { - dev->dev_addr[i] = addr[i]; - printk("%c%.2x", (i? ':': ' '), addr[i]); - } - printk(", driver " GMAC_VERSION "\n"); - - gm->tx_desc_page = tx_descpage; - gm->rx_desc_page = rx_descpage; - gm->rxring = (volatile struct gmac_dma_desc *) rx_descpage; - gm->txring = (volatile struct gmac_dma_desc *) tx_descpage; - - gm->phy_addr = 0; - gm->opened = 0; - gm->sleeping = 0; - - dev->open = gmac_open; - dev->stop = gmac_close; - dev->hard_start_xmit = gmac_xmit_start; - dev->get_stats = gmac_stats; - dev->set_multicast_list = &gmac_set_multicast; - dev->tx_timeout = &gmac_tx_timeout; - dev->watchdog_timeo = 5*HZ; - - ether_setup(dev); - - gm->next_gmac = gmacs; - gmacs = dev; - return; - -out_unreg: - unregister_netdev(dev); - kfree(dev); -out_rxdesc: - free_page(rx_descpage); -out_txdesc: - free_page(tx_descpage); -} - -MODULE_AUTHOR("Paul Mackerras/Ben Herrenschmidt"); -MODULE_DESCRIPTION("PowerMac GMAC driver."); -MODULE_LICENSE("GPL"); - -static void __exit gmac_cleanup_module(void) -{ - struct gmac *gm; - struct net_device *dev; - -#ifdef CONFIG_PMAC_PBOOK - if (gmacs) - pmu_unregister_sleep_notifier(&gmac_sleep_notifier); -#endif - - while ((dev = gmacs) != NULL) { - gm = (struct gmac *) dev->priv; - unregister_netdev(dev); - iounmap((void *) gm->regs); - free_page(gm->tx_desc_page); - free_page(gm->rx_desc_page); - gmacs = gm->next_gmac; - kfree(dev); - } - if (dummy_buf != NULL) { - kfree(dummy_buf); - dummy_buf = NULL; - } -} - -module_init(gmac_probe); -module_exit(gmac_cleanup_module); diff -Nru a/drivers/net/gmac.h b/drivers/net/gmac.h --- a/drivers/net/gmac.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,946 +0,0 @@ -/* - * Definitions for the GMAC ethernet chip, used in the - * Apple G4 powermac. - */ - - -/* - * GMAC register definitions - * - * Note: We encode the register size the same way Apple does. I didn't copy - * Apple's source as-is to avoid licence issues however. That's really - * painful to re-define all those registers ... - * The constants themselves were partially found in OF code, in Sun - * GEM driver and in Apple's Darwin GMAC driver - */ - -#define REG_SZ_8 0x00000000 -#define REG_SZ_16 0x40000000 -#define REG_SZ_32 0x80000000 -#define REG_MASK 0x0FFFFFFF - - /* - * Global registers - */ - -/* -- 0x0004 RW Global configuration - * d: 0x00000042 - */ -#define GM_GCONF (0x0004 | REG_SZ_16) -#define GM_GCONF_BURST_SZ 0x0001 /* 1: 64 bytes/burst, 0: infinite */ -#define GM_GCONF_TXDMA_LIMIT_MASK 0x003e /* 5-1: No of 64 bytes transfers */ -#define GM_GCONF_TXDMA_LIMIT_SHIFT 1 -#define GM_GCONF_RXDMA_LIMIT_MASK 0x07c0 /* 10-6: No of 64 bytes transfers */ -#define GM_GCONF_RXDMA_LIMIT_SHIFT 6 - -/* -- 0x000C R-C Global Interrupt status. - * d: 0x00000000 bits 0-6 cleared on read (C) - */ -#define GM_IRQ_STATUS (0x000c | REG_SZ_32) -#define GM_IRQ_TX_INT_ME 0x00000001 /* C Frame with INT_ME bit set in fifo */ -#define GM_IRQ_TX_ALL 0x00000002 /* C TX descriptor ring empty */ -#define GM_IRQ_TX_DONE 0x00000004 /* C moved from host to TX fifo */ -#define GM_IRQ_RX_DONE 0x00000010 /* C moved from RX fifo to host */ -#define GM_IRQ_RX_NO_BUF 0x00000020 /* C No RX buffer available */ -#define GM_IRQ_RX_TAG_ERR 0x00000040 /* C RX tag error */ -#define GM_IRQ_PCS 0x00002000 /* PCS interrupt ? */ -#define GM_IRQ_MAC_TX 0x00004000 /* MAC tx register set */ -#define GM_IRQ_MAC_RX 0x00008000 /* MAC rx register set */ -#define GM_IRQ_MAC_CTRL 0x00010000 /* MAC control register set */ -#define GM_IRQ_MIF 0x00020000 /* MIF status register set */ -#define GM_IRQ_BUS_ERROR 0x00040000 /* Bus error status register set */ -#define GM_IRQ_TX_COMP 0xfff80000 /* TX completion mask */ - -/* -- 0x0010 RW Interrupt mask. - * d: 0xFFFFFFFF - */ -#define GM_IRQ_MASK (0x0010 | REG_SZ_32) - -/* -- 0x0014 WO Interrupt ack. - * Ack. "high" interrupts - */ -#define GM_IRQ_ACK (0x0014 | REG_SZ_32) - -/* -- 0x001C WO Alias of status register (no auto-clear of "low" interrupts) - */ -#define GM_IRQ_ALT_STAT (0x001C | REG_SZ_32) - -/* -- 0x1000 R-C PCI Error status register - */ -#define GM_PCI_ERR_STAT (0x1000 | REG_SZ_8) -#define GM_PCI_ERR_BAD_ACK 0x01 /* Bad Ack64 */ -#define GM_PCI_ERR_TIMEOUT 0x02 /* Transaction timeout */ -#define GM_PCI_ERR_OTHER 0x04 /* Any other PCI error */ - -/* -- 0x1004 RW PCI Error mask register - * d: 0xFFFFFFFF - */ -#define GM_PCI_ERR_MASK (0x1004 | REG_SZ_8) - -/* -- 0x1008 RW BIF Configuration - * d: 0x00000000 - */ -#define GM_BIF_CFG (0x1008 | REG_SZ_8) -#define GM_BIF_CFG_SLOWCLK 0x01 /* for parity error timing */ -#define GM_BIF_CFG_HOST_64 0x02 /* 64-bit host */ -#define GM_BIF_CFG_B64D_DIS 0x04 /* no 64-bit wide data cycle */ -#define GM_BIF_CFG_M66EN 0x08 /* Read-only: sense if configured for 66MHz */ - -/* -- 0x100C RW BIF Diagnostic ??? - */ -#define GM_BIF_DIAG (0x100C | REG_SZ_32) -#define GM_BIF_DIAG_BURST_STATE 0x007F0000 -#define GM_BIF_DIAG_STATE_MACH 0xFF000000 - -/* -- 0x1010 RW Software reset - * Lower two bits reset TX and RX, both reset whole gmac. They come back - * to 0 when reset is complete. - * bit 2 force RSTOUT# pin when set (PHY reset) - */ -#define GM_RESET (0x1010 | REG_SZ_8) -#define GM_RESET_TX 0x01 -#define GM_RESET_RX 0x02 -#define GM_RESET_RSTOUT 0x04 /* PHY reset */ - - - /* - * Tx DMA Registers - */ - -/* -- 0x2000 RW Tx Kick - * d: 0x00000000 Written by the host with the last tx descriptor number +1 to send - */ -#define GM_TX_KICK (0x2000 | REG_SZ_16) - -/* -- 0x2004 RW Tx configuration - * d: 0x118010 Controls operation of Tx DMA channel - */ - -#define GM_TX_CONF (0x2004 | REG_SZ_32) -#define GM_TX_CONF_DMA_EN 0x00000001 /* Tx DMA enable */ -#define GM_TX_CONF_RING_SZ_MASK 0x0000001e /* Tx desc ring size */ -#define GM_TX_CONF_RING_SZ_SHIFT 1 /* Tx desc ring size shift */ -#define GM_TX_CONF_FIFO_PIO 0x00000020 /* Tx fifo PIO select ??? */ -#define GM_TX_CONF_FIFO_THR_MASK 0x001ffc00 /* Tx fifo threshold */ -#define GM_TX_CONF_FIFO_THR_SHIFT 10 /* Tx fifo threshold shift */ -#define GM_TX_CONF_FIFO_THR_DEFAULT 0x7ff /* Tx fifo threshold default */ -#define GM_TX_CONF_PACED_MODE 0x00100000 /* 1: tx_all irq after last descriptor */ - /* 0: tx_all irq when tx fifo empty */ -#define GM_TX_RING_SZ_32 (0 << 1) -#define GM_TX_RING_SZ_64 (1 << 1) -#define GM_TX_RING_SZ_128 (2 << 1) -#define GM_TX_RING_SZ_256 (3 << 1) -#define GM_TX_RING_SZ_512 (4 << 1) -#define GM_TX_RING_SZ_1024 (5 << 1) -#define GM_TX_RING_SZ_2048 (6 << 1) -#define GM_TX_RING_SZ_4086 (7 << 1) -#define GM_TX_RING_SZ_8192 (8 << 1) - -/* -- 0x2008 RW Tx descriptor ring base low - * -- 0x200C RW Tx descriptor ring base high - * - * Base of tx ring, must be 2k aligned - */ -#define GM_TX_DESC_LO (0x2008 | REG_SZ_32) -#define GM_TX_DESC_HI (0x200C | REG_SZ_32) - -/* -- 0x2100 RW Tx Completion - * d: 0x00000000 Written by the gmac with the last tx descriptor number +1 sent - */ -#define GM_TX_COMP (0x2100 | REG_SZ_16) - - - /* - * Rx DMA registers - */ - - -/* -- 0x4000 RW Rx configuration - * d: 0x1000010 Controls operation of Rx DMA channel - */ - -#define GM_RX_CONF (0x4000 | REG_SZ_32) -#define GM_RX_CONF_DMA_EN 0x00000001 /* Rx DMA enable */ -#define GM_RX_CONF_RING_SZ_MASK 0x0000001e /* Rx desc ring size */ -#define GM_RX_CONF_RING_SZ_SHIFT 1 -#define GM_RX_CONF_BATCH_DIS 0x00000020 /* Rx batch disable */ -#define GM_RX_CONF_FBYTE_OFF_MASK 0x00001c00 /* First byte offset (10-12) */ -#define GM_RX_CONF_FBYTE_OFF_SHIFT 10 -#define GM_RX_CONF_CHK_START_MASK 0x000FE000 /* Checksum start offset */ -#define GM_RX_CONF_CHK_START_SHIFT 13 -#define GM_RX_CONF_DMA_THR_MASK 0x07000000 /* Rx DMA threshold */ -#define GM_RX_CONF_DMA_THR_SHIFT 24 /* Rx DMA threshold shift */ -#define GM_RX_CONF_DMA_THR_DEFAULT 1 /* Rx DMA threshold default */ - -#define GM_RX_RING_SZ_32 (0 << 1) -#define GM_RX_RING_SZ_64 (1 << 1) -#define GM_RX_RING_SZ_128 (2 << 1) -#define GM_RX_RING_SZ_256 (3 << 1) -#define GM_RX_RING_SZ_512 (4 << 1) -#define GM_RX_RING_SZ_1024 (5 << 1) -#define GM_RX_RING_SZ_2048 (6 << 1) -#define GM_RX_RING_SZ_4086 (7 << 1) -#define GM_RX_RING_SZ_8192 (8 << 1) - -/* -- 0x4004 RW Rx descriptor ring base low - * -- 0x4008 RW Rx descriptor ring base high - * - * Base of rx ring - */ -#define GM_RX_DESC_LO (0x4004 | REG_SZ_32) -#define GM_RX_DESC_HI (0x4008 | REG_SZ_32) - -/* -- 0x4020 RW Rx pause threshold - * d: 0x000000f8 - * - * Two PAUSE thresholds are used to define when PAUSE flow control frames are - * emitted by GEM. The granularity of these thresholds is in 64 byte increments. - * XOFF PAUSE frames use the pause_time value pre-programmed in the - * Send PAUSE MAC Register. - * XON PAUSE frames use a pause_time of 0. - */ -#define GM_RX_PTH (0x4020 | REG_SZ_32) - /* - * 0-8: XOFF PAUSE emitted when RX FIFO - * occupancy rises above this value (times 64 bytes) - */ -#define GM_RX_PTH_OFF_MASK 0x000001ff -#define GM_RX_PTH_OFF_SHIFT 0 - /* - * 12-20: XON PAUSE emitted when RX FIFO - * occupancy falls below this value (times 64 bytes) - */ -#define GM_RX_PTH_ON_MASK 0x001ff000 -#define GM_RX_PTH_ON_SHIFT 12 - -#define GM_RX_PTH_UNITS 64 - -/* -- 0x4100 RW Rx Kick - * d: 0x00000000 The last valid RX descriptor is the one right before the value of the - * register. Initially set to 0 on reset. RX descriptors must be posted - * in multiples of 4. The first descriptor should be cache-line aligned - * for best performance. - */ -#define GM_RX_KICK (0x4100 | REG_SZ_16) - -/* -- 0x4104 RW Rx Completion - * d: 0x00000000 All descriptors upto but excluding the register value are ready to be - * processed by the host. - */ -#define GM_RX_COMP (0x4104 | REG_SZ_16) - -/* -- 0x4108 RW Rx Blanking - * d: 0x00000000 Written by the gmac with the last tx descriptor number +1 sent - * - * Defines the values used for receive interrupt blanking. - * For INTR_TIME field, every count is 2048 PCI clock time. For 66 Mhz, each - * count is about 15 ns. - */ -#define GM_RX_BLANK (0x4108 | REG_SZ_32) - /* - * 0-8:no.of pkts to be recvd since the last RX_DONE - * interrupt, before a new interrupt - */ -#define GM_RX_BLANK_INTR_PACKETS_MASK 0x000001ff -#define GM_RX_BLANK_INTR_PACKETS_SHIFT 0 - /* - * 12-19 : no. of clocks to be counted since the last - * RX_DONE interrupt, before a new interrupt - */ -#define GM_RX_BLANK_INTR_TIME_MASK 0x000ff000 -#define GM_RX_BLANK_INTR_TIME_SHIFT 12 - -#define GM_RX_BLANK_UNITS 2048 - -/* -- 0x4120 RO Rx fifo size - * - * This 11-bit RO register indicates the size, in 64-byte multiples, of the - * RX FIFO. Software should use it to properly configure the PAUSE thresholds. - * The value read is 0x140, indicating a 20kbyte RX FIFO. - * ------------------------------------------------------------------------- - */ -#define GM_RX_FIFO_SIZE (0x4120 | REG_SZ_16) -#define GM_RZ_FIFO_SIZE_UNITS 64 - - - /* - * MAC regisers - */ - -/* -- 0x6000 MAC Tx reset control - */ -#define GM_MAC_TX_RESET (0x6000 | REG_SZ_8) -#define GM_MAC_TX_RESET_NOW 0x01 - -/* -- 0x6004 MAC Rx reset control - */ -#define GM_MAC_RX_RESET (0x6004 | REG_SZ_8) -#define GM_MAC_RX_RESET_NOW 0x01 - -/* -- 0x6008 Send Pause command register - */ -#define GM_MAC_SND_PAUSE (0x6008 | REG_SZ_32) -#define GM_MAC_SND_PAUSE_TIME_MASK 0x0000ffff -#define GM_MAC_SND_PAUSE_TIME_SHIFT 0 -#define GM_MAC_SND_PAUSE_NOW 0x00010000 -#define GM_MAC_SND_PAUSE_DEFAULT 0x00001bf0 - -/* -- 0x6010 MAC transmit status - */ -#define GM_MAC_TX_STATUS (0x6010 | REG_SZ_16) -#define GM_MAC_TX_STAT_SENT 0x0001 -#define GM_MAC_TX_STAT_UNDERRUN 0x0002 -#define GM_MAC_TX_STAT_MAX_PKT_ERR 0x0004 -#define GM_MAC_TX_STAT_NORM_COLL_OVF 0x0008 -#define GM_MAC_TX_STAT_EXCS_COLL_OVF 0x0010 -#define GM_MAC_TX_STAT_LATE_COLL_OVF 0x0020 -#define GM_MAC_TX_STAT_FIRS_COLL_OVF 0x0040 -#define GM_MAC_TX_STAT_DEFER_TIMER_OVF 0x0080 -#define GM_MAC_TX_STAT_PEAK_ATTMP_OVF 0x0100 - -/* -- 0x6014 MAC receive status - */ -#define GM_MAC_RX_STATUS (0x6014 | REG_SZ_16) -#define GM_MAC_RX_STAT_RECEIVED 0x0001 -#define GM_MAC_RX_STAT_FIFO_OVF 0x0002 -#define GM_MAC_RX_STAT_FRAME_CTR_OVF 0x0004 -#define GM_MAC_RX_STAT_ALIGN_ERR_OVF 0x0008 -#define GM_MAC_RX_STAT_CRC_ERR_OVF 0x0010 -#define GM_MAC_RX_STAT_LEN_ERR_OVF 0x0020 -#define GM_MAC_RX_STAT_CODE_ERR_OVF 0x0040 - -/* -- 0x6018 MAC control & status - */ -#define GM_MAC_CTRLSTAT (0x6018 | REG_SZ_32) -#define GM_MAC_CTRLSTAT_PAUSE_RCVD 0x00000001 -#define GM_MAC_CTRLSTAT_PAUSE_STATE 0x00000002 -#define GM_MAC_CTRLSTAT_PAUSE_NOT 0x00000004 -#define GM_MAC_CTRLSTAT_PAUSE_TIM_MASK 0xffff0000 -#define GM_MAC_CTRLSTAT_PAUSE_TIM_SHIFT 16 - -/* -- 0x6020 MAC Tx mask - * Same bits as MAC Tx status - */ -#define GM_MAC_TX_MASK (0x6020 | REG_SZ_16) - -/* -- 0x6024 MAC Rx mask - * Same bits as MAC Rx status - */ -#define GM_MAC_RX_MASK (0x6024 | REG_SZ_16) - -/* -- 0x6028 MAC Control/Status mask - * Same bits as MAC control/status low order byte - */ -#define GM_MAC_CTRLSTAT_MASK (0x6024 | REG_SZ_8) - -/* -- 0x6030 MAC Tx configuration - */ -#define GM_MAC_TX_CONFIG (0x6030 | REG_SZ_16) -#define GM_MAC_TX_CONF_ENABLE 0x0001 -#define GM_MAC_TX_CONF_IGNORE_CARRIER 0x0002 -#define GM_MAC_TX_CONF_IGNORE_COLL 0x0004 -#define GM_MAC_TX_CONF_ENABLE_IPG0 0x0008 -#define GM_MAC_TX_CONF_DONT_GIVEUP 0x0010 -#define GM_MAC_TX_CONF_DONT_GIVEUP_NLMT 0x0020 -#define GM_MAC_TX_CONF_NO_BACKOFF 0x0040 -#define GM_MAC_TX_CONF_SLOWDOWN 0x0080 -#define GM_MAC_TX_CONF_NO_FCS 0x0100 -#define GM_MAC_TX_CONF_CARRIER_EXT 0x0200 - -/* -- 0x6034 MAC Rx configuration - */ -#define GM_MAC_RX_CONFIG (0x6034 | REG_SZ_16) -#define GM_MAC_RX_CONF_ENABLE 0x0001 -#define GM_MAC_RX_CONF_STRIP_PAD 0x0002 -#define GM_MAC_RX_CONF_STIP_FCS 0x0004 -#define GM_MAC_RX_CONF_RX_ALL 0x0008 -#define GM_MAC_RX_CONF_RX_ALL_MULTI 0x0010 -#define GM_MAC_RX_CONF_HASH_ENABLE 0x0020 -#define GM_MAC_RX_CONF_ADDR_FLTR_ENABLE 0x0040 -#define GM_MAC_RX_CONF_PASS_ERROR_FRAM 0x0080 -#define GM_MAC_RX_CONF_CARRIER_EXT 0x0100 - -/* -- 0x6038 MAC control configuration - */ -#define GM_MAC_CTRL_CONFIG (0x6038 | REG_SZ_8) -#define GM_MAC_CTRL_CONF_SND_PAUSE_EN 0x01 -#define GM_MAC_CTRL_CONF_RCV_PAUSE_EN 0x02 -#define GM_MAC_CTRL_CONF_PASS_CTRL_FRAM 0x04 - -/* -- 0x603c MAC XIF configuration */ -#define GM_MAC_XIF_CONFIG (0x603c | REG_SZ_8) -#define GM_MAC_XIF_CONF_TX_MII_OUT_EN 0x01 -#define GM_MAC_XIF_CONF_MII_INT_LOOP 0x02 -#define GM_MAC_XIF_CONF_DISABLE_ECHO 0x04 -#define GM_MAC_XIF_CONF_GMII_MODE 0x08 -#define GM_MAC_XIF_CONF_MII_BUFFER_EN 0x10 -#define GM_MAC_XIF_CONF_LINK_LED 0x20 -#define GM_MAC_XIF_CONF_FULL_DPLX_LED 0x40 - -/* -- 0x6040 MAC inter-packet GAP 0 - */ -#define GM_MAC_INTR_PKT_GAP0 (0x6040 | REG_SZ_8) -#define GM_MAC_INTR_PKT_GAP0_DEFAULT 0x00 - -/* -- 0x6044 MAC inter-packet GAP 1 - */ -#define GM_MAC_INTR_PKT_GAP1 (0x6044 | REG_SZ_8) -#define GM_MAC_INTR_PKT_GAP1_DEFAULT 0x08 - -/* -- 0x6048 MAC inter-packet GAP 2 - */ -#define GM_MAC_INTR_PKT_GAP2 (0x6048 | REG_SZ_8) -#define GM_MAC_INTR_PKT_GAP2_DEFAULT 0x04 - -/* -- 604c MAC slot time - */ -#define GM_MAC_SLOT_TIME (0x604C | REG_SZ_16) -#define GM_MAC_SLOT_TIME_DEFAULT 0x0040 - -/* -- 6050 MAC minimum frame size - */ -#define GM_MAC_MIN_FRAME_SIZE (0x6050 | REG_SZ_16) -#define GM_MAC_MIN_FRAME_SIZE_DEFAULT 0x0040 - -/* -- 6054 MAC maximum frame size - */ -#define GM_MAC_MAX_FRAME_SIZE (0x6054 | REG_SZ_16) -#define GM_MAC_MAX_FRAME_SIZE_DEFAULT 0x05ee -#define GM_MAC_MAX_FRAME_SIZE_ALIGN 0x5f0 - -/* -- 6058 MAC preamble length - */ -#define GM_MAC_PREAMBLE_LEN (0x6058 | REG_SZ_16) -#define GM_MAC_PREAMBLE_LEN_DEFAULT 0x0007 - -/* -- 605c MAC jam size - */ -#define GM_MAC_JAM_SIZE (0x605c | REG_SZ_8) -#define GM_MAC_JAM_SIZE_DEFAULT 0x04 - -/* -- 6060 MAC attempt limit - */ -#define GM_MAC_ATTEMPT_LIMIT (0x6060 | REG_SZ_8) -#define GM_MAC_ATTEMPT_LIMIT_DEFAULT 0x10 - -/* -- 6064 MAC control type - */ -#define GM_MAC_CONTROL_TYPE (0x6064 | REG_SZ_16) -#define GM_MAC_CONTROL_TYPE_DEFAULT 0x8808 - -/* -- 6080 MAC address 15..0 - * -- 6084 MAC address 16..31 - * -- 6088 MAC address 32..47 - */ -#define GM_MAC_ADDR_NORMAL0 (0x6080 | REG_SZ_16) -#define GM_MAC_ADDR_NORMAL1 (0x6084 | REG_SZ_16) -#define GM_MAC_ADDR_NORMAL2 (0x6088 | REG_SZ_16) - -/* -- 608c MAC alternate address 15..0 - * -- 6090 MAC alternate address 16..31 - * -- 6094 MAC alternate address 32..47 - */ -#define GM_MAC_ADDR_ALT0 (0x608c | REG_SZ_16) -#define GM_MAC_ADDR_ALT1 (0x6090 | REG_SZ_16) -#define GM_MAC_ADDR_ALT2 (0x6094 | REG_SZ_16) - -/* -- 6098 MAC control address 15..0 - * -- 609c MAC control address 16..31 - * -- 60a0 MAC control address 32..47 - */ -#define GM_MAC_ADDR_CTRL0 (0x6098 | REG_SZ_16) -#define GM_MAC_ADDR_CTRL1 (0x609c | REG_SZ_16) -#define GM_MAC_ADDR_CTRL2 (0x60a0 | REG_SZ_16) - -/* -- 60a4 MAC address filter (0_0) - * -- 60a8 MAC address filter (0_1) - * -- 60ac MAC address filter (0_2) - */ -#define GM_MAC_ADDR_FILTER0 (0x60a4 | REG_SZ_16) -#define GM_MAC_ADDR_FILTER1 (0x60a8 | REG_SZ_16) -#define GM_MAC_ADDR_FILTER2 (0x60ac | REG_SZ_16) - -/* -- 60b0 MAC address filter mask 1,2 - */ -#define GM_MAC_ADDR_FILTER_MASK1_2 (0x60b0 | REG_SZ_8) - -/* -- 60b4 MAC address filter mask 0 - */ -#define GM_MAC_ADDR_FILTER_MASK0 (0x60b4 | REG_SZ_16) - -/* -- [60c0 .. 60fc] MAC hash table - */ -#define GM_MAC_ADDR_FILTER_HASH0 (0x60c0 | REG_SZ_16) - -/* -- 6100 MAC normal collision counter - */ -#define GM_MAC_COLLISION_CTR (0x6100 | REG_SZ_16) - -/* -- 6104 MAC 1st successful collision counter - */ -#define GM_MAC_FIRST_COLLISION_CTR (0x6104 | REG_SZ_16) - -/* -- 6108 MAC excess collision counter - */ -#define GM_MAC_EXCS_COLLISION_CTR (0x6108 | REG_SZ_16) - -/* -- 610c MAC late collision counter - */ -#define GM_MAC_LATE_COLLISION_CTR (0x610c | REG_SZ_16) - -/* -- 6110 MAC defer timer counter - */ -#define GM_MAC_DEFER_TIMER_COUNTER (0x6110 | REG_SZ_16) - -/* -- 6114 MAC peak attempts - */ -#define GM_MAC_PEAK_ATTEMPTS (0x6114 | REG_SZ_16) - -/* -- 6118 MAC Rx frame counter - */ -#define GM_MAC_RX_FRAME_CTR (0x6118 | REG_SZ_16) - -/* -- 611c MAC Rx length error counter - */ -#define GM_MAC_RX_LEN_ERR_CTR (0x611c | REG_SZ_16) - -/* -- 6120 MAC Rx alignment error counter - */ -#define GM_MAC_RX_ALIGN_ERR_CTR (0x6120 | REG_SZ_16) - -/* -- 6124 MAC Rx CRC error counter - */ -#define GM_MAC_RX_CRC_ERR_CTR (0x6124 | REG_SZ_16) - -/* -- 6128 MAC Rx code violation error counter - */ -#define GM_MAC_RX_CODE_VIOLATION_CTR (0x6128 | REG_SZ_16) - -/* -- 6130 MAC random number seed - */ -#define GM_MAC_RANDOM_SEED (0x6130 | REG_SZ_16) - -/* -- 6134 MAC state machine - */ -#define GM_MAC_STATE_MACHINE (0x6134 | REG_SZ_8) - - - /* - * MIF registers - */ - - -/* -- 0x6200 RW MIF bit bang clock - */ -#define GM_MIF_BB_CLOCK (0x6200 | REG_SZ_8) - -/* -- 0x6204 RW MIF bit bang data - */ -#define GM_MIF_BB_DATA (0x6204 | REG_SZ_8) - -/* -- 0x6208 RW MIF bit bang output enable - */ -#define GM_MIF_BB_OUT_ENABLE (0x6208 | REG_SZ_8) - -/* -- 0x620c RW MIF frame control & data - */ -#define GM_MIF_FRAME_CTL_DATA (0x620c | REG_SZ_32) -#define GM_MIF_FRAME_START_MASK 0xc0000000 -#define GM_MIF_FRAME_START_SHIFT 30 -#define GM_MIF_FRAME_OPCODE_MASK 0x30000000 -#define GM_MIF_FRAME_OPCODE_SHIFT 28 -#define GM_MIF_FRAME_PHY_ADDR_MASK 0x0f800000 -#define GM_MIF_FRAME_PHY_ADDR_SHIFT 23 -#define GM_MIF_FRAME_REG_ADDR_MASK 0x007c0000 -#define GM_MIF_FRAME_REG_ADDR_SHIFT 18 -#define GM_MIF_FRAME_TURNAROUND_HI 0x00020000 -#define GM_MIF_FRAME_TURNAROUND_LO 0x00010000 -#define GM_MIF_FRAME_DATA_MASK 0x0000ffff -#define GM_MIF_FRAME_DATA_SHIFT 0 - -/* -- 0x6210 RW MIF config reg - */ -#define GM_MIF_CFG (0x6210 | REG_SZ_16) -#define GM_MIF_CFGPS 0x00000001 /* PHY Select */ -#define GM_MIF_CFGPE 0x00000002 /* Poll Enable */ -#define GM_MIF_CFGBB 0x00000004 /* Bit Bang Enable */ -#define GM_MIF_CFGPR_MASK 0x000000f8 /* Poll Register address */ -#define GM_MIF_CFGPR_SHIFT 3 -#define GM_MIF_CFGM0 0x00000100 /* MDIO_0 Data / MDIO_0 attached */ -#define GM_MIF_CFGM1 0x00000200 /* MDIO_1 Data / MDIO_1 attached */ -#define GM_MIF_CFGPD_MASK 0x00007c00 /* Poll Device PHY address */ -#define GM_MIF_CFGPD_SHIFT 10 - -#define GM_MIF_POLL_DELAY 200 - -#define GM_INTERNAL_PHYAD 1 /* PHY address for int. transceiver */ -#define GM_EXTERNAL_PHYAD 0 /* PHY address for ext. transceiver */ - -/* -- 0x6214 RW MIF interrupt mask reg - * same as basic/status Register - */ -#define GM_MIF_IRQ_MASK (0x6214 | REG_SZ_16) - -/* -- 0x6218 RW MIF basic/status reg - * The Basic portion of this register indicates the last - * value of the register read indicated in the POLL REG field - * of the Configuration Register. - * The Status portion indicates bit(s) that have changed. - * The MIF Mask register is corresponding to this register in - * terms of the bit(s) that need to be masked for generating - * interrupt on the MIF Interrupt Bit of the Global Status Rgister. - */ -#define GM_MIF_STATUS (0x6218 | REG_SZ_32) - -#define GM_MIF_STATUS_MASK 0x0000ffff /* 0-15 : Status */ -#define GM_MIF_BASIC_MASK 0xffff0000 /* 16-31 : Basic register */ - - /* - * PCS link registers - */ - -/* -- 0x9000 RW PCS mii control reg - */ -#define GM_PCS_CONTROL (0x9000 | REG_SZ_16) - -/* -- 0x9004 RW PCS mii status reg - */ -#define GM_PCS_STATUS (0x9004 | REG_SZ_16) - -/* -- 0x9008 RW PCS mii advertisement - */ -#define GM_PCS_ADVERTISEMENT (0x9008 | REG_SZ_16) - -/* -- 0x900c RW PCS mii LP ability - */ -#define GM_PCS_ABILITY (0x900c | REG_SZ_16) - -/* -- 0x9010 RW PCS config - */ -#define GM_PCS_CONFIG (0x9010 | REG_SZ_8) - -/* -- 0x9014 RW PCS state machine - */ -#define GM_PCS_STATE_MACHINE (0x9014 | REG_SZ_32) - -/* -- 0x9018 RW PCS interrupt status - */ -#define GM_PCS_IRQ_STATUS (0x9018 | REG_SZ_8) - -/* -- 0x9050 RW PCS datapath mode - */ -#define GM_PCS_DATAPATH_MODE (0x9050 | REG_SZ_8) -#define GM_PCS_DATAPATH_INTERNAL 0x01 /* Internal serial link */ -#define GM_PCS_DATAPATH_SERDES 0x02 /* 10-bit Serdes interface */ -#define GM_PCS_DATAPATH_MII 0x04 /* Select mii/gmii mode */ -#define GM_PCS_DATAPATH_GMII_OUT 0x08 /* serial mode only, copy data to gmii */ - -/* -- 0x9054 RW PCS serdes control - */ -#define GM_PCS_SERDES_CTRL (0x9054 | REG_SZ_8) - -/* -- 0x9058 RW PCS serdes output select - */ -#define GM_PCS_SERDES_SELECT (0x9058 | REG_SZ_8) - -/* -- 0x905c RW PCS serdes state - */ -#define GM_PCS_SERDES_STATE (0x905c | REG_SZ_8) - - - /* - * PHY registers - */ - -/* - * Standard PHY registers (from de4x5.h) - */ -#define MII_CR 0x00 /* MII Management Control Register */ -#define MII_SR 0x01 /* MII Management Status Register */ -#define MII_ID0 0x02 /* PHY Identifier Register 0 */ -#define MII_ID1 0x03 /* PHY Identifier Register 1 */ -#define MII_ANA 0x04 /* Auto Negotiation Advertisement */ -#define MII_ANLPA 0x05 /* Auto Negotiation Link Partner Ability */ -#define MII_ANE 0x06 /* Auto Negotiation Expansion */ -#define MII_ANP 0x07 /* Auto Negotiation Next Page TX */ - -/* -** MII Management Control Register -*/ -#define MII_CR_RST 0x8000 /* RESET the PHY chip */ -#define MII_CR_LPBK 0x4000 /* Loopback enable */ -#define MII_CR_SPD 0x2000 /* 0: 10Mb/s; 1: 100Mb/s */ -#define MII_CR_10 0x0000 /* Set 10Mb/s */ -#define MII_CR_100 0x2000 /* Set 100Mb/s */ -#define MII_CR_ASSE 0x1000 /* Auto Speed Select Enable */ -#define MII_CR_PD 0x0800 /* Power Down */ -#define MII_CR_ISOL 0x0400 /* Isolate Mode */ -#define MII_CR_RAN 0x0200 /* Restart Auto Negotiation */ -#define MII_CR_FDM 0x0100 /* Full Duplex Mode */ -#define MII_CR_CTE 0x0080 /* Collision Test Enable */ -#define MII_CR_SPEEDSEL2 0x0040 /* Speed selection 2 on BCM */ -/* -** MII Management Status Register -*/ -#define MII_SR_T4C 0x8000 /* 100BASE-T4 capable */ -#define MII_SR_TXFD 0x4000 /* 100BASE-TX Full Duplex capable */ -#define MII_SR_TXHD 0x2000 /* 100BASE-TX Half Duplex capable */ -#define MII_SR_TFD 0x1000 /* 10BASE-T Full Duplex capable */ -#define MII_SR_THD 0x0800 /* 10BASE-T Half Duplex capable */ -#define MII_SR_ASSC 0x0020 /* Auto Speed Selection Complete*/ -#define MII_SR_RFD 0x0010 /* Remote Fault Detected */ -#define MII_SR_ANC 0x0008 /* Auto Negotiation capable */ -#define MII_SR_LKS 0x0004 /* Link Status */ -#define MII_SR_JABD 0x0002 /* Jabber Detect */ -#define MII_SR_XC 0x0001 /* Extended Capabilities */ - -/* -** MII Management Auto Negotiation Advertisement Register -*/ -#define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ -#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ -#define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ -#define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ -#define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ -#define MII_ANA_100M 0x0380 /* 100Mb Technology Ability Mask */ -#define MII_ANA_10M 0x0060 /* 10Mb Technology Ability Mask */ -#define MII_ANA_CSMA 0x0001 /* CSMA-CD Capable */ - -/* -** MII Management Auto Negotiation Remote End Register -*/ -#define MII_ANLPA_NP 0x8000 /* Next Page (Enable) */ -#define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ -#define MII_ANLPA_RF 0x2000 /* Remote Fault */ -#define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ -#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ -#define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ -#define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ -#define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ -#define MII_ANLPA_100M 0x0380 /* 100Mb Technology Ability Mask */ -#define MII_ANLPA_10M 0x0060 /* 10Mb Technology Ability Mask */ -#define MII_ANLPA_CSMA 0x0001 /* CSMA-CD Capable */ -#define MII_ANLPA_PAUS 0x0400 - -/* Generic PHYs - * - * These GENERIC values assumes that the PHY devices follow 802.3u and - * allow parallel detection to set the link partner ability register. - * Detection of 100Base-TX [H/F Duplex] and 100Base-T4 is supported. - */ - -/* - * Model-specific PHY registers - * - * Note: Only the BCM5201 is described here for now. I'll add the 5400 once - * I see a machine using it in real world. - */ - -/* Supported PHYs (phy_type field ) */ -#define PHY_B5400 0x5400 -#define PHY_B5401 0x5401 -#define PHY_B5411 0x5411 -#define PHY_B5201 0x5201 -#define PHY_B5221 0x5221 -#define PHY_LXT971 0x0971 -#define PHY_UNKNOWN 0 - -/* Identification (for multi-PHY) */ -#define MII_BCM5201_OUI 0x001018 -#define MII_BCM5201_MODEL 0x21 -#define MII_BCM5201_REV 0x01 -#define MII_BCM5201_ID ((MII_BCM5201_OUI << 10) | (MII_BCM5201_MODEL << 4)) -#define MII_BCM5201_MASK 0xfffffff0 -#define MII_BCM5221_OUI 0x001018 -#define MII_BCM5221_MODEL 0x1e -#define MII_BCM5221_REV 0x00 -#define MII_BCM5221_ID ((MII_BCM5221_OUI << 10) | (MII_BCM5221_MODEL << 4)) -#define MII_BCM5221_MASK 0xfffffff0 -#define MII_BCM5400_OUI 0x000818 -#define MII_BCM5400_MODEL 0x04 -#define MII_BCM5400_REV 0x01 -#define MII_BCM5400_ID ((MII_BCM5400_OUI << 10) | (MII_BCM5400_MODEL << 4)) -#define MII_BCM5400_MASK 0xfffffff0 -#define MII_BCM5401_OUI 0x000818 -#define MII_BCM5401_MODEL 0x05 -#define MII_BCM5401_REV 0x01 -#define MII_BCM5401_ID ((MII_BCM5401_OUI << 10) | (MII_BCM5401_MODEL << 4)) -#define MII_BCM5401_MASK 0xfffffff0 -#define MII_BCM5411_OUI 0x000818 -#define MII_BCM5411_MODEL 0x07 -#define MII_BCM5411_REV 0x01 -#define MII_BCM5411_ID ((MII_BCM5411_OUI << 10) | (MII_BCM5411_MODEL << 4)) -#define MII_BCM5411_MASK 0xfffffff0 -#define MII_LXT971_OUI 0x0004de -#define MII_LXT971_MODEL 0x0e -#define MII_LXT971_REV 0x00 -#define MII_LXT971_ID ((MII_LXT971_OUI << 10) | (MII_LXT971_MODEL << 4)) -#define MII_LXT971_MASK 0xfffffff0 - -/* BCM5201 AUX STATUS register */ -#define MII_BCM5201_AUXCTLSTATUS 0x18 -#define MII_BCM5201_AUXCTLSTATUS_DUPLEX 0x0001 -#define MII_BCM5201_AUXCTLSTATUS_SPEED 0x0002 - -/* MII BCM5201 MULTIPHY interrupt register */ -#define MII_BCM5201_INTERRUPT 0x1A -#define MII_BCM5201_INTERRUPT_INTENABLE 0x4000 - -#define MII_BCM5201_AUXMODE2 0x1B -#define MII_BCM5201_AUXMODE2_LOWPOWER 0x0008 - -#define MII_BCM5201_MULTIPHY 0x1E - -/* MII BCM5201 MULTIPHY register bits */ -#define MII_BCM5201_MULTIPHY_SERIALMODE 0x0002 -#define MII_BCM5201_MULTIPHY_SUPERISOLATE 0x0008 - -/* MII BCM5400 1000-BASET Control register */ -#define MII_BCM5400_GB_CONTROL 0x09 -#define MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP 0x0200 - -/* MII BCM5400 AUXCONTROL register */ -#define MII_BCM5400_AUXCONTROL 0x18 -#define MII_BCM5400_AUXCONTROL_PWR10BASET 0x0004 - -/* MII BCM5400 AUXSTATUS register */ -#define MII_BCM5400_AUXSTATUS 0x19 -#define MII_BCM5400_AUXSTATUS_LINKMODE_MASK 0x0700 -#define MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT 8 - -/* MII LXT971 STATUS2 register */ -#define MII_LXT971_STATUS2 0x11 -#define MII_LXT971_STATUS2_SPEED 0x4000 -#define MII_LXT971_STATUS2_LINK 0x0400 -#define MII_LXT971_STATUS2_FULLDUPLEX 0x0200 -#define MII_LXT971_STATUS2_AUTONEG_COMPLETE 0x0080 - - - /* - * DMA descriptors - */ - - -/* - * Descriptor counts and buffer sizes - */ -#define NTX 64 /* must be power of 2 */ -#define NTX_CONF GM_TX_RING_SZ_64 -#define NRX 64 /* must be power of 2 */ -#define NRX_CONF GM_RX_RING_SZ_64 -#define RX_COPY_THRESHOLD 256 -#define GMAC_BUFFER_ALIGN 32 /* Align on a cache line */ -#define RX_BUF_ALLOC_SIZE (ETH_FRAME_LEN + GMAC_BUFFER_ALIGN + 2) -#define RX_OFFSET 2 - -/* - * Definitions of Rx and Tx descriptors - */ - -struct gmac_dma_desc { - unsigned int size; /* data size and OWN bit */ - unsigned int flags; /* flags */ - unsigned int lo_addr; /* phys addr, low 32 bits */ - unsigned int hi_addr; -}; - -/* - * Rx bits - */ - -/* Bits in size */ -#define RX_SZ_OWN 0x80000000 /* 1 = owned by chip */ -#define RX_SZ_MASK 0x7FFF0000 -#define RX_SZ_SHIFT 16 -#define RX_SZ_CKSUM_MASK 0x0000FFFF - -/* Bits in flags */ -#define RX_FL_CRC_ERROR 0x40000000 -#define RX_FL_ALT_ADDR 0x20000000 /* Packet rcv. from alt MAC address */ - -/* - * Tx bits - */ - -/* Bits in size */ -#define TX_SZ_MASK 0x00007FFF -#define TX_SZ_CRC_MASK 0x00FF8000 -#define TX_SZ_CRC_STUFF 0x1F000000 -#define TX_SZ_CRC_ENABLE 0x20000000 -#define TX_SZ_EOP 0x40000000 -#define TX_SZ_SOP 0x80000000 -/* Bits in flags */ -#define TX_FL_INTERRUPT 0x00000001 -#define TX_FL_NO_CRC 0x00000002 - - /* - * Other stuffs - */ - -struct gmac { - volatile unsigned int *regs; /* hardware registers, virtual addr */ - struct net_device *dev; - struct device_node *of_node; - unsigned long tx_desc_page; /* page for DMA descriptors */ - unsigned long rx_desc_page; /* page for DMA descriptors */ - volatile struct gmac_dma_desc *rxring; - struct sk_buff *rx_buff[NRX]; - int next_rx; - volatile struct gmac_dma_desc *txring; - struct sk_buff *tx_buff[NTX]; - int next_tx; - int tx_gone; - int phy_addr; - unsigned int phy_id; - int phy_type; - int phy_status; /* Cached PHY status */ - int full_duplex; /* Current set to full duplex */ - int gigabit; /* Current set to 1000BT */ - struct net_device_stats stats; - u8 pci_bus; - u8 pci_devfn; - spinlock_t lock; - int opened; - int sleeping; - struct net_device *next_gmac; -}; - - -/* Register access macros. We hope the preprocessor will be smart enough - * to optimize them into one single access instruction - */ -#define GM_OUT(reg, v) (((reg) & REG_SZ_32) ? out_le32(gm->regs + \ - (((reg) & REG_MASK)>>2), (v)) \ - : (((reg) & REG_SZ_16) ? out_le16((volatile u16 *) \ - (gm->regs + (((reg) & REG_MASK)>>2)), (v)) \ - : out_8((volatile u8 *)(gm->regs + \ - (((reg) & REG_MASK)>>2)), (v)))) -#define GM_IN(reg) (((reg) & REG_SZ_32) ? in_le32(gm->regs + \ - (((reg) & REG_MASK)>>2)) \ - : (((reg) & REG_SZ_16) ? in_le16((volatile u16 *) \ - (gm->regs + (((reg) & REG_MASK)>>2))) \ - : in_8((volatile u8 *)(gm->regs + \ - (((reg) & REG_MASK)>>2))))) -#define GM_BIS(r, v) GM_OUT((r), GM_IN(r) | (v)) -#define GM_BIC(r, v) GM_OUT((r), GM_IN(r) & ~(v)) - -/* Wrapper to alloc_skb to test various alignements */ -#define GMAC_ALIGNED_RX_SKB_ADDR(addr) \ - ((((unsigned long)(addr) + GMAC_BUFFER_ALIGN - 1) & \ - ~(GMAC_BUFFER_ALIGN - 1)) - (unsigned long)(addr)) - -static inline struct sk_buff * -gmac_alloc_skb(unsigned int length, int gfp_flags) -{ - struct sk_buff *skb; - - skb = alloc_skb(length + GMAC_BUFFER_ALIGN, gfp_flags); - if(skb) { - int offset = GMAC_ALIGNED_RX_SKB_ADDR(skb->data); - - if(offset) - skb_reserve(skb, offset); - } - return skb; -} - diff -Nru a/drivers/net/mace.c b/drivers/net/mace.c --- a/drivers/net/mace.c Tue Feb 19 18:08:58 2002 +++ b/drivers/net/mace.c Tue Feb 19 18:08:58 2002 @@ -25,9 +25,6 @@ static struct net_device *mace_devs; static int port_aaui = -1; -MODULE_PARM(port_aaui, "i"); -MODULE_PARM_DESC(port_aaui, "MACE uses AAUI port (0-1)"); - #define N_RX_RING 8 #define N_TX_RING 6 #define MAX_TX_ACTIVE 1 @@ -35,6 +32,9 @@ #define RX_BUFLEN (ETH_FRAME_LEN + 8) #define TX_TIMEOUT HZ /* 1 second */ +/* Chip rev needs workaround on HW & multicast addr change */ +#define BROKEN_ADDRCHG_REV 0x0941 + /* Bits in transmit DMA status */ #define TX_DMA_ERR 0x80 @@ -60,6 +60,8 @@ struct timer_list tx_timeout; int timeout_active; int port_aaui; + int chipid; + struct device_node* of_node; struct net_device *next_mace; }; @@ -153,6 +155,22 @@ SET_MODULE_OWNER(dev); mp = dev->priv; + mp->of_node = mace; + + if (!request_OF_resource(mace, 0, " (mace)")) { + printk(KERN_ERR "MACE: can't request IO resource !\n"); + goto err_out; + } + if (!request_OF_resource(mace, 1, " (mace tx dma)")) { + printk(KERN_ERR "MACE: can't request TX DMA resource !\n"); + goto err_out; + } + + if (!request_OF_resource(mace, 2, " (mace tx dma)")) { + printk(KERN_ERR "MACE: can't request RX DMA resource !\n"); + goto err_out; + } + dev->base_addr = mace->addrs[0].address; mp->mace = (volatile struct mace *) ioremap(mace->addrs[0].address, 0x1000); @@ -164,8 +182,10 @@ dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j]; printk("%c%.2x", (j? ':': ' '), dev->dev_addr[j]); } - printk(", chip revision %d.%d\n", - in_8(&mp->mace->chipid_hi), in_8(&mp->mace->chipid_lo)); + mp->chipid = (in_8(&mp->mace->chipid_hi) << 8) | + in_8(&mp->mace->chipid_lo); + printk(", chip revision %d.%d\n", mp->chipid >> 8, mp->chipid & 0xff); + mp = (struct mace_data *) dev->priv; mp->maccc = ENXMT | ENRCV; @@ -222,6 +242,16 @@ mp->next_mace = mace_devs; mace_devs = dev; + return; + +err_out: + unregister_netdev(dev); + if (mp->of_node) { + release_OF_resource(mp->of_node, 0); + release_OF_resource(mp->of_node, 1); + release_OF_resource(mp->of_node, 2); + } + kfree(dev); } static void dbdma_reset(volatile struct dbdma_regs *dma) @@ -274,14 +304,19 @@ __mace_set_address(dev, dev->dev_addr); /* clear the multicast filter */ - out_8(&mb->iac, ADDRCHG | LOGADDR); - while ((in_8(&mb->iac) & ADDRCHG) != 0) - ; - for (i = 0; i < 8; ++i) { - out_8(&mb->ladrf, 0); + if (mp->chipid == BROKEN_ADDRCHG_REV) + out_8(&mb->iac, LOGADDR); + else { + out_8(&mb->iac, ADDRCHG | LOGADDR); + while ((in_8(&mb->iac) & ADDRCHG) != 0) + ; } + for (i = 0; i < 8; ++i) + out_8(&mb->ladrf, 0); + /* done changing address */ - out_8(&mb->iac, 0); + if (mp->chipid != BROKEN_ADDRCHG_REV) + out_8(&mb->iac, 0); if (mp->port_aaui) out_8(&mb->plscc, PORTSEL_AUI + ENPLSIO); @@ -291,16 +326,23 @@ static void __mace_set_address(struct net_device *dev, void *addr) { - volatile struct mace *mb = ((struct mace_data *) dev->priv)->mace; + struct mace_data *mp = (struct mace_data *) dev->priv; + volatile struct mace *mb = mp->mace; unsigned char *p = addr; int i; /* load up the hardware address */ - out_8(&mb->iac, ADDRCHG | PHYADDR); - while ((in_8(&mb->iac) & ADDRCHG) != 0) - ; + if (mp->chipid == BROKEN_ADDRCHG_REV) + out_8(&mb->iac, PHYADDR); + else { + out_8(&mb->iac, ADDRCHG | PHYADDR); + while ((in_8(&mb->iac) & ADDRCHG) != 0) + ; + } for (i = 0; i < 6; ++i) out_8(&mb->padr, dev->dev_addr[i] = p[i]); + if (mp->chipid != BROKEN_ADDRCHG_REV) + out_8(&mb->iac, 0); } static int mace_set_address(struct net_device *dev, void *addr) @@ -313,7 +355,6 @@ __mace_set_address(dev, addr); - out_8(&mb->iac, 0); /* note: setting ADDRCHG clears ENRCV */ out_8(&mb->maccc, mp->maccc); @@ -543,12 +584,17 @@ printk("\n"); #endif - out_8(&mb->iac, ADDRCHG | LOGADDR); - while ((in_8(&mb->iac) & ADDRCHG) != 0) - ; - for (i = 0; i < 8; ++i) { - out_8(&mb->ladrf, multicast_filter[i]); + if (mp->chipid == BROKEN_ADDRCHG_REV) + out_8(&mb->iac, LOGADDR); + else { + out_8(&mb->iac, ADDRCHG | LOGADDR); + while ((in_8(&mb->iac) & ADDRCHG) != 0) + ; } + for (i = 0; i < 8; ++i) + out_8(&mb->ladrf, multicast_filter[i]); + if (mp->chipid != BROKEN_ADDRCHG_REV) + out_8(&mb->iac, 0); } /* reset maccc */ out_8(&mb->maccc, mp->maccc); @@ -899,7 +945,10 @@ MODULE_AUTHOR("Paul Mackerras"); MODULE_DESCRIPTION("PowerMac MACE driver."); +MODULE_PARM(port_aaui, "i"); +MODULE_PARM_DESC(port_aaui, "MACE uses AAUI port (0-1)"); MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; static void __exit mace_cleanup (void) { @@ -907,19 +956,23 @@ struct mace_data *mp; while ((dev = mace_devs) != 0) { - mp = (struct mace_data *) mace_devs->priv; - mace_devs = mp->next_mace; + mp = (struct mace_data *) mace_devs->priv; + mace_devs = mp->next_mace; - free_irq(dev->irq, dev); - free_irq(mp->tx_dma_intr, dev); - free_irq(mp->rx_dma_intr, dev); + unregister_netdev(dev); + free_irq(dev->irq, dev); + free_irq(mp->tx_dma_intr, dev); + free_irq(mp->rx_dma_intr, dev); + + release_OF_resource(mp->of_node, 0); + release_OF_resource(mp->of_node, 1); + release_OF_resource(mp->of_node, 2); - unregister_netdev(dev); - kfree(dev); + kfree(dev); } if (dummy_buf != NULL) { - kfree(dummy_buf); - dummy_buf = NULL; + kfree(dummy_buf); + dummy_buf = NULL; } } diff -Nru a/drivers/net/ne.c b/drivers/net/ne.c --- a/drivers/net/ne.c Tue Feb 19 18:08:59 2002 +++ b/drivers/net/ne.c Tue Feb 19 18:08:59 2002 @@ -76,6 +76,9 @@ #endif static struct isapnp_device_id isapnp_clone_list[] __initdata = { + { ISAPNP_CARD_ID('A','X','E',0x2011), + ISAPNP_VENDOR('A','X','E'), ISAPNP_FUNCTION(0x2011), + (long) "NetGear EA201" }, { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('E','D','I'), ISAPNP_FUNCTION(0x0216), (long) "NN NE2000" }, diff -Nru a/drivers/net/ni52.c b/drivers/net/ni52.c --- a/drivers/net/ni52.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/ni52.c Tue Feb 19 18:08:57 2002 @@ -40,7 +40,7 @@ * The internal sysbus seems to be slow. So we often lose packets because of * overruns while receiving from a fast remote host. * This can slow down TCP connections. Maybe the newer ni5210 cards are better. - * my experience is, that if a machine sends with more then about 500-600K/s + * my experience is, that if a machine sends with more than about 500-600K/s * the fifo/sysbus overflows. * * IMPORTANT NOTE: diff -Nru a/drivers/net/ns83820.c b/drivers/net/ns83820.c --- a/drivers/net/ns83820.c Tue Feb 19 18:08:59 2002 +++ b/drivers/net/ns83820.c Tue Feb 19 18:08:59 2002 @@ -1,7 +1,7 @@ -#define VERSION "0.14" -/* ns83820.c by Benjamin LaHaise +#define _VERSION "0.15" +/* ns83820.c by Benjamin LaHaise with contributions. * - * $Revision: 1.34.2.8 $ + * $Revision: 1.34.2.12 $ * * Copyright 2001 Benjamin LaHaise. * Copyright 2001 Red Hat. @@ -45,7 +45,12 @@ * 0.12 - add statistics counters * - add allmulti/promisc support * 20011009 0.13 - hotplug support, other smaller pci api cleanups - * 20011117 0.14 - ethtool GDRVINFO, GLINK support + * 20011204 0.13a - optical transceiver support added + * by Michael Clark + * 20011205 0.13b - call register_netdev earlier in initialization + * suppress duplicate link status messages + * 20011117 0.14 - ethtool GDRVINFO, GLINK support + * 20011204 0.15 get ppc (big endian) working * * Driver Overview * =============== @@ -66,6 +71,7 @@ * D-Link DGE-500T * PureData PDP8023Z-TG * SMC SMC9452TX SMC9462TX + * Netgear GA621 * * Special thanks to SMC for providing hardware to test this driver on. * @@ -98,14 +104,14 @@ #define Dprintk dprintk #ifdef CONFIG_HIGHMEM64G -#define USE_64BIT_ADDR +#define USE_64BIT_ADDR "+" #elif defined(__ia64__) -#define USE_64BIT_ADDR +#define USE_64BIT_ADDR "+" #endif /* Tell davem to fix the pci dma api. Grrr. */ /* stolen from acenic.c */ -#ifdef CONFIG_HIGHMEM +#if 0 //def CONFIG_HIGHMEM #if defined(CONFIG_X86) #define DMAADDR_OFFSET 0 #if defined(CONFIG_HIGHMEM64G) @@ -141,6 +147,12 @@ } #endif +#if defined(USE_64BIT_ADDR) +#define VERSION _VERSION USE_64BIT_ADDR +#else +#define VERSION _VERSION +#endif + /* tunables */ #define RX_BUF_SIZE 6144 /* 8192 */ #define NR_RX_DESC 256 @@ -216,6 +228,7 @@ #define CFG_DUPSTS 0x10000000 #define CFG_TBI_EN 0x01000000 #define CFG_MODE_1000 0x00400000 +#define CFG_AUTO_1000 0x00200000 #define CFG_PINT_CTL 0x001c0000 #define CFG_PINT_DUPSTS 0x00100000 #define CFG_PINT_LNKSTS 0x00080000 @@ -319,6 +332,36 @@ #define VDR 0xc4 #define CCSR 0xcc +#define TBICR 0xe0 +#define TBISR 0xe4 +#define TANAR 0xe8 +#define TANLPAR 0xec +#define TANER 0xf0 +#define TESR 0xf4 + +#define TBICR_MR_AN_ENABLE 0x00001000 +#define TBICR_MR_RESTART_AN 0x00000200 + +#define TBISR_MR_LINK_STATUS 0x00000020 +#define TBISR_MR_AN_COMPLETE 0x00000004 + +#define TANAR_PS2 0x00000100 +#define TANAR_PS1 0x00000080 +#define TANAR_HALF_DUP 0x00000040 +#define TANAR_FULL_DUP 0x00000020 + +#define GPIOR_GP5_OE 0x00000200 +#define GPIOR_GP4_OE 0x00000100 +#define GPIOR_GP3_OE 0x00000080 +#define GPIOR_GP2_OE 0x00000040 +#define GPIOR_GP1_OE 0x00000020 +#define GPIOR_GP3_OUT 0x00000004 +#define GPIOR_GP1_OUT 0x00000001 + +#define LINK_AUTONEGOTIATE 0x01 +#define LINK_DOWN 0x02 +#define LINK_UP 0x04 + #define __kick_rx(dev) writel(CR_RXE, dev->base + CR) #define kick_rx(dev) do { \ @@ -393,6 +436,7 @@ u32 IMR_cache; struct eeprom ee; + unsigned linkstate; spinlock_t tx_lock; @@ -444,11 +488,11 @@ static inline void build_rx_desc32(struct ns83820 *dev, u32 *desc, u32 link, u32 buf, u32 cmdsts, u32 extsts) { - desc[0] = link; - desc[1] = buf; - desc[3] = extsts; + desc[0] = cpu_to_le32(link); + desc[1] = cpu_to_le32(buf); + desc[3] = cpu_to_le32(extsts); mb(); - desc[2] = cmdsts; + desc[2] = cpu_to_le32(cmdsts); } #define build_rx_desc build_rx_desc32 @@ -489,7 +533,7 @@ build_rx_desc(dev, sg, 0, buf, cmdsts, 0); /* update link of previous rx */ if (next_empty != dev->rx_info.next_rx) - dev->rx_info.descs[((NR_RX_DESC + next_empty - 1) % NR_RX_DESC) * DESC_SIZE] = dev->rx_info.phy_descs + (next_empty * DESC_SIZE * 4); + dev->rx_info.descs[((NR_RX_DESC + next_empty - 1) % NR_RX_DESC) * DESC_SIZE] = cpu_to_le32(dev->rx_info.phy_descs + (next_empty * DESC_SIZE * 4)); return 0; } @@ -548,39 +592,93 @@ static void phy_intr(struct ns83820 *dev) { - static char *speeds[] = { "10", "100", "1000", "1000(?)" }; + static char *speeds[] = { "10", "100", "1000", "1000(?)", "1000F" }; u32 cfg, new_cfg; + u32 tbisr, tanar, tanlpar; + int speed, fullduplex, newlinkstate; - new_cfg = dev->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS); cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; - if (cfg & CFG_SPDSTS1) - new_cfg |= CFG_MODE_1000 | CFG_SB; - else - new_cfg &= ~CFG_MODE_1000 | CFG_SB; + if (dev->CFG_cache & CFG_TBI_EN) { - if ((cfg & CFG_LNKSTS) && ((new_cfg ^ dev->CFG_cache) & CFG_MODE_1000)) { - writel(new_cfg, dev->base + CFG); - dev->CFG_cache = new_cfg; - } + /* we have an optical transceiver */ + tbisr = readl(dev->base + TBISR); + tanar = readl(dev->base + TANAR); + tanlpar = readl(dev->base + TANLPAR); + dprintk("phy_intr: tbisr=%08x, tanar=%08x, tanlpar=%08x\n", + tbisr, tanar, tanlpar); + + if ( (fullduplex = (tanlpar & TANAR_FULL_DUP) + && (tanar & TANAR_FULL_DUP)) ) { + + /* both of us are full duplex */ + writel(readl(dev->base + TXCFG) + | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP, + dev->base + TXCFG); + writel(readl(dev->base + RXCFG) | RXCFG_RX_FD, + dev->base + RXCFG); + /* Light up full duplex LED */ + writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT, + dev->base + GPIOR); + + } else if(((tanlpar & TANAR_HALF_DUP) + && (tanar & TANAR_HALF_DUP)) + || ((tanlpar & TANAR_FULL_DUP) + && (tanar & TANAR_HALF_DUP)) + || ((tanlpar & TANAR_HALF_DUP) + && (tanar & TANAR_FULL_DUP))) { + + /* one or both of us are half duplex */ + writel((readl(dev->base + TXCFG) + & ~(TXCFG_CSI | TXCFG_HBI)) | TXCFG_ATP, + dev->base + TXCFG); + writel(readl(dev->base + RXCFG) & ~RXCFG_RX_FD, + dev->base + RXCFG); + /* Turn off full duplex LED */ + writel(readl(dev->base + GPIOR) & ~GPIOR_GP1_OUT, + dev->base + GPIOR); + } - dev->CFG_cache &= ~CFG_SPDSTS; - dev->CFG_cache |= cfg & CFG_SPDSTS; + speed = 4; /* 1000F */ - if (cfg & CFG_LNKSTS) { - netif_start_queue(&dev->net_dev); - netif_wake_queue(&dev->net_dev); } else { - netif_stop_queue(&dev->net_dev); + /* we have a copper transceiver */ + new_cfg = dev->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS); + + if (cfg & CFG_SPDSTS1) + new_cfg |= CFG_MODE_1000 | CFG_SB; + else + new_cfg &= ~CFG_MODE_1000 | CFG_SB; + + if ((cfg & CFG_LNKSTS) && ((new_cfg ^ dev->CFG_cache) & CFG_MODE_1000)) { + writel(new_cfg, dev->base + CFG); + dev->CFG_cache = new_cfg; + } + + dev->CFG_cache &= ~CFG_SPDSTS; + dev->CFG_cache |= cfg & CFG_SPDSTS; + + speed = ((cfg / CFG_SPDSTS0) & 3); + fullduplex = (cfg & CFG_DUPSTS); } - if (cfg & CFG_LNKSTS) + newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN; + + if (newlinkstate & LINK_UP + && dev->linkstate != newlinkstate) { + netif_start_queue(&dev->net_dev); + netif_wake_queue(&dev->net_dev); printk(KERN_INFO "%s: link now %s mbps, %s duplex and up.\n", dev->net_dev.name, - speeds[((cfg / CFG_SPDSTS0) & 3)], - (cfg & CFG_DUPSTS) ? "full" : "half"); - else + speeds[speed], + fullduplex ? "full" : "half"); + } else if (newlinkstate & LINK_DOWN + && dev->linkstate != newlinkstate) { + netif_stop_queue(&dev->net_dev); printk(KERN_INFO "%s: link now down.\n", dev->net_dev.name); + } + + dev->linkstate = newlinkstate; } static int ns83820_setup_rx(struct ns83820 *dev) @@ -701,15 +799,15 @@ dprintk("walking descs\n"); next_rx = info->next_rx; desc = info->descs + (DESC_SIZE * next_rx); - while ((CMDSTS_OWN & (cmdsts = desc[CMDSTS])) && + while ((CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[CMDSTS]))) && (cmdsts != CMDSTS_OWN)) { struct sk_buff *skb; - u32 extsts = desc[EXTSTS]; - dmaaddr_high_t bufptr = *(hw_addr_t *)(desc + BUFPTR); + u32 extsts = le32_to_cpu(desc[EXTSTS]); + dmaaddr_high_t bufptr = le32_to_cpu(desc[BUFPTR]); dprintk("cmdsts: %08x\n", cmdsts); - dprintk("link: %08x\n", desc[LINK]); - dprintk("extsts: %08x\n", desc[EXTSTS]); + dprintk("link: %08x\n", cpu_to_le32(desc[LINK])); + dprintk("extsts: %08x\n", extsts); skb = info->skbs[next_rx]; info->skbs[next_rx] = NULL; @@ -721,14 +819,14 @@ pci_unmap_single(dev->pci_dev, bufptr, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); if (CMDSTS_OK & cmdsts) { -#ifndef __i386__ +#if 0 //ndef __i386__ struct sk_buff *tmp; #endif int len = cmdsts & 0xffff; if (!skb) BUG(); skb_put(skb, len); -#ifndef __i386__ /* I hate the network stack sometimes */ +#if 0 //ndef __i386__ /* I hate the network stack sometimes */ tmp = __dev_alloc_skb(RX_BUF_SIZE+16, GFP_ATOMIC); if (!tmp) goto done; @@ -750,7 +848,7 @@ skb->protocol = eth_type_trans(skb, &dev->net_dev); if (NET_RX_DROP == netif_rx(skb)) dev->stats.rx_dropped ++; -#ifndef __i386__ +#if 0 //ndef __i386__ done:; #endif } else { @@ -791,9 +889,9 @@ desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n", - tx_done_idx, dev->tx_free_idx, desc[CMDSTS]); + tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[CMDSTS])); while ((tx_done_idx != dev->tx_free_idx) && - !(CMDSTS_OWN & (cmdsts = desc[CMDSTS])) ) { + !(CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[CMDSTS]))) ) { struct sk_buff *skb; if (cmdsts & CMDSTS_ERR) @@ -804,13 +902,13 @@ dev->stats.tx_bytes += cmdsts & 0xffff; dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n", - tx_done_idx, dev->tx_free_idx, desc[CMDSTS]); + tx_done_idx, dev->tx_free_idx, cmdsts); skb = dev->tx_skbs[tx_done_idx]; dev->tx_skbs[tx_done_idx] = NULL; dprintk("done(%p)\n", skb); if (skb) { pci_unmap_single(dev->pci_dev, - *(hw_addr_t *)(desc + BUFPTR), + le32_to_cpu(desc[BUFPTR]), skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(skb); @@ -818,7 +916,7 @@ tx_done_idx = (tx_done_idx + 1) % NR_TX_DESC; dev->tx_done_idx = tx_done_idx; - desc[CMDSTS] = 0; + desc[CMDSTS] = cpu_to_le32(0); barrier(); desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); } @@ -939,17 +1037,17 @@ } #endif - dprintk("frag[%3u]: %4u @ 0x%x%08Lx\n", free_idx, len, + dprintk("frag[%3u]: %4u @ 0x%08Lx\n", free_idx, len, (unsigned long long)buf); free_idx = (free_idx + 1) % NR_TX_DESC; - desc[LINK] = dev->tx_phy_descs + (free_idx * DESC_SIZE * 4); - *(hw_addr_t *)(desc + BUFPTR) = buf; - desc[EXTSTS] = extsts; + desc[LINK] = cpu_to_le32(dev->tx_phy_descs + (free_idx * DESC_SIZE * 4)); + desc[BUFPTR] = cpu_to_le32(buf); + desc[EXTSTS] = cpu_to_le32(extsts); cmdsts = ((nr_frags|residue) ? CMDSTS_MORE : do_intr ? CMDSTS_INTR : 0); cmdsts |= (desc == first_desc) ? 0 : CMDSTS_OWN; cmdsts |= len; - desc[CMDSTS] = cmdsts; + desc[CMDSTS] = cpu_to_le32(cmdsts); if (residue) { buf += len; @@ -960,7 +1058,8 @@ if (!nr_frags) break; - buf = pci_map_single_high(dev->pci_dev, frag->page, 0, + buf = pci_map_single_high(dev->pci_dev, frag->page, + frag->page_offset, frag->size, PCI_DMA_TODEVICE); dprintk("frag: buf=%08Lx page=%08lx\n", (long long)buf, (long)(frag->page - mem_map)); @@ -970,7 +1069,7 @@ } dprintk("done pkt\n"); dev->tx_skbs[free_idx] = skb; - first_desc[CMDSTS] |= CMDSTS_OWN; + first_desc[CMDSTS] |= cpu_to_le32(CMDSTS_OWN); dev->tx_free_idx = free_idx; kick_tx(dev); @@ -1014,24 +1113,24 @@ { u32 ethcmd; - if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) + if (copy_from_user(ðcmd, useraddr, sizeof (ethcmd))) return -EFAULT; switch (ethcmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, "ns83820"); - strcpy (info.version, VERSION); - strcpy (info.bus_info, dev->pci_dev->slot_name); - if (copy_to_user (useraddr, &info, sizeof (info))) + strcpy(info.driver, "ns83820"); + strcpy(info.version, VERSION); + strcpy(info.bus_info, dev->pci_dev->slot_name); + if (copy_to_user(useraddr, &info, sizeof (info))) return -EFAULT; return 0; } /* get link status */ case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; + struct ethtool_value edata = { ETHTOOL_GLINK }; u32 cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; if (cfg & CFG_LNKSTS) @@ -1104,10 +1203,14 @@ Dprintk("BAD\n"); } - if (ISR_RXSOVR & isr) - Dprintk("overrun\n"); - if (ISR_RXORN & isr) - Dprintk("overrun\n"); + if (unlikely(ISR_RXSOVR & isr)) { + Dprintk("overrun: rxsovr\n"); + dev->stats.rx_over_errors ++; + } + if (unlikely(ISR_RXORN & isr)) { + Dprintk("overrun: rxorn\n"); + dev->stats.rx_over_errors ++; + } if ((ISR_RXRCMP & isr) && dev->rx_info.up) writel(CR_RXE, dev->base + CR); @@ -1206,9 +1309,10 @@ memset(dev->tx_descs, 0, 4 * NR_TX_DESC * DESC_SIZE); for (i=0; itx_descs + (i * DESC_SIZE) + LINK) - = dev->tx_phy_descs - + ((i+1) % NR_TX_DESC) * DESC_SIZE * 4; + dev->tx_descs[(i * DESC_SIZE) + LINK] + = cpu_to_le32( + dev->tx_phy_descs + + ((i+1) % NR_TX_DESC) * DESC_SIZE * 4); } dev->tx_idx = 0; @@ -1246,6 +1350,9 @@ #if 0 /* I've left this in as an example of how to use eeprom.h */ data = eeprom_readw(&dev->ee, 0xa + 2 - i); #else + /* Read from the perfect match memory: this is loaded by + * the chip from the EEPROM via the EELOAD self test. + */ writel(i*2, dev->base + RFCR); data = readl(dev->base + RFDR); #endif @@ -1343,6 +1450,8 @@ goto out_unmap; } + if(register_netdev(&dev->net_dev)) goto out_unmap; + dev->net_dev.open = ns83820_open; dev->net_dev.stop = ns83820_stop; dev->net_dev.hard_start_xmit = ns83820_hard_start_xmit; @@ -1375,12 +1484,15 @@ dev->CFG_cache = readl(dev->base + CFG); if ((dev->CFG_cache & CFG_PCI64_DET)) { - printk("%s: enabling 64 bit PCI.\n", dev->net_dev.name); + printk("%s: enabling 64 bit PCI addressing.\n", + dev->net_dev.name); dev->CFG_cache |= CFG_T64ADDR | CFG_DATA64_EN; - } else { - printk("%s: disabling 64 bit PCI.\n", dev->net_dev.name); +#if defined(USE_64BIT_ADDR) + dev->net_dev.features |= NETIF_F_HIGHDMA; +#endif + } else dev->CFG_cache &= ~(CFG_T64ADDR | CFG_DATA64_EN); - } + dev->CFG_cache &= (CFG_TBI_EN | CFG_MRM_DIS | CFG_MWI_DIS | CFG_T64ADDR | CFG_DATA64_EN | CFG_EXT_125 | CFG_M64ADDR); @@ -1390,15 +1502,28 @@ dev->CFG_cache |= CFG_POW; #ifdef USE_64BIT_ADDR dev->CFG_cache |= CFG_M64ADDR; - printk("using 64 bit addressing\n"); #endif -#ifdef __LITTLE_ENDIAN + /* Big endian mode does not seem to do what the docs suggest */ dev->CFG_cache &= ~CFG_BEM; -#elif defined(__BIG_ENDIAN) - dev->CFG_cache |= CFG_BEM; -#else -#error This driver only works for big or little endian!!! -#endif + + /* setup optical transceiver if we have one */ + if (dev->CFG_cache & CFG_TBI_EN) { + printk("%s: enabling optical transceiver\n", dev->net_dev.name); + writel(readl(dev->base + GPIOR) | 0x3e8, dev->base + GPIOR); + + /* setup auto negotiation feature advertisement */ + writel(readl(dev->base + TANAR) + | TANAR_HALF_DUP | TANAR_FULL_DUP, + dev->base + TANAR); + + /* start auto negotiation */ + writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN, + dev->base + TBICR); + writel(TBICR_MR_AN_ENABLE, dev->base + TBICR); + dev->linkstate = LINK_AUTONEGOTIATE; + + dev->CFG_cache |= CFG_MODE_1000; + } writel(dev->CFG_cache, dev->base + CFG); dprintk("CFG: %08x\n", dev->CFG_cache); @@ -1454,15 +1579,15 @@ dev->net_dev.features |= NETIF_F_HIGHDMA; #endif - register_netdev(&dev->net_dev); - - printk(KERN_INFO "%s: ns83820.c v" VERSION ": DP83820 %02x:%02x:%02x:%02x:%02x:%02x pciaddr=0x%08lx irq=%d rev 0x%x\n", + printk(KERN_INFO "%s: ns83820 v" VERSION ": DP83820 v%u.%u: %02x:%02x:%02x:%02x:%02x:%02x io=0x%08lx irq=%d f=%s\n", dev->net_dev.name, + (unsigned)readl(dev->base + SRR) >> 8, + (unsigned)readl(dev->base + SRR) & 0xff, dev->net_dev.dev_addr[0], dev->net_dev.dev_addr[1], dev->net_dev.dev_addr[2], dev->net_dev.dev_addr[3], dev->net_dev.dev_addr[4], dev->net_dev.dev_addr[5], addr, pci_dev->irq, - (unsigned)readl(dev->base + SRR) + (dev->net_dev.features & NETIF_F_HIGHDMA) ? "sg" : "h,sg" ); return 0; diff -Nru a/drivers/net/pcmcia/Config.help b/drivers/net/pcmcia/Config.help --- a/drivers/net/pcmcia/Config.help Tue Feb 19 18:08:59 2002 +++ b/drivers/net/pcmcia/Config.help Tue Feb 19 18:08:59 2002 @@ -93,6 +93,18 @@ as a module, say M here and read . If unsure, say N. +CONFIG_PCMCIA_AXNET + Say Y here if you intend to attach an Asix AX88190-based PCMCIA + (PC-card) Fast Ethernet card to your computer. These cards are + nearly NE2000 compatible but need a separate driver due to a few + misfeatures. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called axnet_cs.o. If you want to compile it as + a module, say M here and read . If + unsure, say N. + CONFIG_ARCNET_COM20020_CS Say Y here if you intend to attach this type of ARCnet PCMCIA card to your computer. @@ -112,6 +124,18 @@ inserted in and removed from the running kernel whenever you want). The module will be called ibmtr_cs.o. If you want to compile it as a module, say M here and read . + +CONFIG_PCMCIA_XIRCOM + This driver is for the Digital "Tulip" Ethernet CardBus adapters. + It should work with most DEC 21*4*-based chips/ethercards, as well + as with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and + ASIX. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called xircom_tulip_cb.o. If you want to compile + it as a module, say M here and read + . If unsure, say N. CONFIG_PCMCIA_XIRTULIP This driver is for the Digital "Tulip" Ethernet CardBus adapters. diff -Nru a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c --- a/drivers/net/pcnet32.c Tue Feb 19 18:08:58 2002 +++ b/drivers/net/pcnet32.c Tue Feb 19 18:08:58 2002 @@ -21,7 +21,12 @@ * *************************************************************************/ -static const char *version = "pcnet32.c:v1.25kf 26.9.1999 tsbogend@alpha.franken.de\n"; +#define DRV_NAME "pcnet32" +#define DRV_VERSION "1.25kf" +#define DRV_RELDATE "17.11.2001" + +static const char *version = +DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " tsbogend@alpha.franken.de\n"; #include @@ -36,10 +41,13 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include @@ -283,11 +291,11 @@ int shared_irq:1, /* shared irq possible */ ltint:1, #ifdef DO_DXSUFLO - dxsuflo:1, /* disable transmit stop on uflo */ + dxsuflo:1, /* disable transmit stop on uflo */ #endif - full_duplex:1, /* full duplex possible */ mii:1; /* mii port available */ struct net_device *next; + struct mii_if_info mii_if; }; static int pcnet32_probe_vlbus(int cards_found); @@ -302,9 +310,9 @@ static int pcnet32_close(struct net_device *); static struct net_device_stats *pcnet32_get_stats(struct net_device *); static void pcnet32_set_multicast_list(struct net_device *); -#ifdef HAVE_PRIVATE_IOCTL -static int pcnet32_mii_ioctl(struct net_device *, struct ifreq *, int); -#endif +static int pcnet32_ioctl(struct net_device *, struct ifreq *, int); +static int mdio_read(struct net_device *dev, int phy_id, int reg_num); +static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val); enum pci_flags_bit { PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, @@ -647,6 +655,13 @@ #if defined(__i386__) printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name); memcpy(dev->dev_addr, promaddr, 6); +#elif defined(__powerpc__) + if (!is_valid_ether_addr(dev->dev_addr) + && is_valid_ether_addr(promaddr)) { + printk("\n" KERN_WARNING "%s: using PROM address:", + dev->name); + memcpy(dev->dev_addr, promaddr, 6); + } #endif } } @@ -702,7 +717,7 @@ dev->priv = lp; lp->name = chipname; lp->shared_irq = shared; - lp->full_duplex = fdx; + lp->mii_if.full_duplex = fdx; #ifdef DO_DXSUFLO lp->dxsuflo = dxsuflo; #endif @@ -712,6 +727,9 @@ lp->options = PCNET32_PORT_ASEL; else lp->options = options_mapping[options[card_idx]]; + lp->mii_if.dev = dev; + lp->mii_if.mdio_read = mdio_read; + lp->mii_if.mdio_write = mdio_write; if (fdx && !(lp->options & PCNET32_PORT_ASEL) && full_duplex[card_idx]) lp->options |= PCNET32_PORT_FD; @@ -781,9 +799,7 @@ dev->stop = &pcnet32_close; dev->get_stats = &pcnet32_get_stats; dev->set_multicast_list = &pcnet32_set_multicast_list; -#ifdef HAVE_PRIVATE_IOCTL - dev->do_ioctl = &pcnet32_mii_ioctl; -#endif + dev->do_ioctl = &pcnet32_ioctl; dev->tx_timeout = pcnet32_tx_timeout; dev->watchdog_timeo = (HZ >> 1); @@ -834,7 +850,7 @@ lp->a.write_bcr (ioaddr, 2, val); /* handle full duplex setting */ - if (lp->full_duplex) { + if (lp->mii_if.full_duplex) { val = lp->a.read_bcr (ioaddr, 9) & ~3; if (lp->options & PCNET32_PORT_FD) { val |= 1; @@ -1476,29 +1492,154 @@ pcnet32_restart(dev, 0x0042); /* Resume normal operation */ } -#ifdef HAVE_PRIVATE_IOCTL -static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +static int mdio_read(struct net_device *dev, int phy_id, int reg_num) +{ + struct pcnet32_private *lp = dev->priv; + unsigned long ioaddr = dev->base_addr; + u16 val_out; + int phyaddr; + + if (!lp->mii) + return 0; + + phyaddr = lp->a.read_bcr(ioaddr, 33); + + lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f)); + val_out = lp->a.read_bcr(ioaddr, 34); + lp->a.write_bcr(ioaddr, 33, phyaddr); + + return val_out; +} + +static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val) +{ + struct pcnet32_private *lp = dev->priv; + unsigned long ioaddr = dev->base_addr; + int phyaddr; + + if (!lp->mii) + return; + + phyaddr = lp->a.read_bcr(ioaddr, 33); + + lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f)); + lp->a.write_bcr(ioaddr, 34, val); + lp->a.write_bcr(ioaddr, 33, phyaddr); +} + +static int pcnet32_ethtool_ioctl (struct net_device *dev, void *useraddr) +{ + struct pcnet32_private *lp = dev->priv; + u32 ethcmd; + int phyaddr = 0; + int phy_id = 0; + unsigned long ioaddr = dev->base_addr; + + if (lp->mii) { + phyaddr = lp->a.read_bcr (ioaddr, 33); + phy_id = (phyaddr >> 5) & 0x1f; + lp->mii_if.phy_id = phy_id; + } + + if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, DRV_NAME); + strcpy (info.version, DRV_VERSION); + if (lp->pci_dev) + strcpy (info.bus_info, lp->pci_dev->slot_name); + else + sprintf(info.bus_info, "VLB 0x%lx", dev->base_addr); + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; + } + + /* get settings */ + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + spin_lock_irq(&lp->lock); + mii_ethtool_gset(&lp->mii_if, &ecmd); + spin_unlock_irq(&lp->lock); + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + /* set settings */ + case ETHTOOL_SSET: { + int r; + struct ethtool_cmd ecmd; + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + spin_lock_irq(&lp->lock); + r = mii_ethtool_sset(&lp->mii_if, &ecmd); + spin_unlock_irq(&lp->lock); + return r; + } + /* restart autonegotiation */ + case ETHTOOL_NWAY_RST: { + return mii_nway_restart(&lp->mii_if); + } + /* get link status */ + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + edata.data = mii_link_ok(&lp->mii_if); + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + + /* get message-level */ + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = {ETHTOOL_GMSGLVL}; + edata.data = pcnet32_debug; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + /* set message-level */ + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + pcnet32_debug = edata.data; + return 0; + } + default: + break; + } + + return -EOPNOTSUPP; +} + +static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { unsigned long ioaddr = dev->base_addr; struct pcnet32_private *lp = dev->priv; - u16 *data = (u16 *)&rq->ifr_data; + struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; int phyaddr = lp->a.read_bcr (ioaddr, 33); + if (cmd == SIOCETHTOOL) + return pcnet32_ethtool_ioctl(dev, (void *) rq->ifr_data); + if (lp->mii) { switch(cmd) { - case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ - data[0] = (phyaddr >> 5) & 0x1f; + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + data->phy_id = (phyaddr >> 5) & 0x1f; /* Fall Through */ - case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ - lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f)); - data[3] = lp->a.read_bcr (ioaddr, 34); + case SIOCGMIIREG: /* Read MII PHY register. */ + lp->a.write_bcr (ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f)); + data->val_out = lp->a.read_bcr (ioaddr, 34); lp->a.write_bcr (ioaddr, 33, phyaddr); return 0; - case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + case SIOCSMIIREG: /* Write MII PHY register. */ if (!capable(CAP_NET_ADMIN)) return -EPERM; - lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f)); - lp->a.write_bcr (ioaddr, 34, data[2]); + lp->a.write_bcr (ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f)); + lp->a.write_bcr (ioaddr, 34, data->val_in); lp->a.write_bcr (ioaddr, 33, phyaddr); return 0; default: @@ -1507,13 +1648,12 @@ } return -EOPNOTSUPP; } -#endif /* HAVE_PRIVATE_IOCTL */ static struct pci_driver pcnet32_driver = { - name: "pcnet32", - probe: pcnet32_probe_pci, - remove: NULL, - id_table: pcnet32_pci_tbl, + name: DRV_NAME, + probe: pcnet32_probe_pci, + remove: NULL, + id_table: pcnet32_pci_tbl, }; MODULE_PARM(debug, "i"); diff -Nru a/drivers/net/pppoe.c b/drivers/net/pppoe.c --- a/drivers/net/pppoe.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/pppoe.c Tue Feb 19 18:08:57 2002 @@ -7,6 +7,7 @@ * * Version: 0.6.9 * + * 220102 : Fix module use count on failure in pppoe_create, pppox_sk -acme * 030700 : Fixed connect logic to allow for disconnect. * 270700 : Fixed potential SMP problems; we must protect against * simultaneous invocation of ppp_input @@ -34,7 +35,7 @@ * * Author: Michal Ostrowski * Contributors: - * Arnaldo Carvalho de Melo + * Arnaldo Carvalho de Melo * David S. Miller (davem@redhat.com) * * License: @@ -127,7 +128,8 @@ return hash & ( PPPOE_HASH_SIZE - 1 ); } -static struct pppox_opt *item_hash_table[PPPOE_HASH_SIZE] = { 0, }; +/* zeroed because its in .bss */ +static struct pppox_opt *item_hash_table[PPPOE_HASH_SIZE]; /********************************************************************** * @@ -340,7 +342,7 @@ ***********************************************************************/ int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb) { - struct pppox_opt *po = sk->protinfo.pppox; + struct pppox_opt *po = pppox_sk(sk); struct pppox_opt *relay_po = NULL; if (sk->state & PPPOX_BOUND) { @@ -468,8 +470,10 @@ **********************************************************************/ void pppoe_sock_destruct(struct sock *sk) { - if (sk->protinfo.destruct_hook) - kfree(sk->protinfo.destruct_hook); + struct pppox_opt *po = pppox_sk(sk); + + if (po) + kfree(po); MOD_DEC_USE_COUNT; } @@ -481,14 +485,15 @@ **********************************************************************/ static int pppoe_create(struct socket *sock) { - int error = 0; + int error = -ENOMEM; struct sock *sk; + struct pppox_opt *po; MOD_INC_USE_COUNT; - sk = sk_alloc(PF_PPPOX, GFP_KERNEL, 1); + sk = sk_alloc(PF_PPPOX, GFP_KERNEL, 1, NULL); if (!sk) - return -ENOMEM; + goto decmod; sock_init_data(sock, sk); @@ -505,24 +510,17 @@ sk->type = SOCK_STREAM; sk->destruct = pppoe_sock_destruct; - sk->protinfo.pppox = kmalloc(sizeof(struct pppox_opt), GFP_KERNEL); - if (!sk->protinfo.pppox) { - error = -ENOMEM; - goto free_sk; - } - - memset((void *) sk->protinfo.pppox, 0, sizeof(struct pppox_opt)); - sk->protinfo.pppox->sk = sk; - - /* Delete the protinfo when it is time to do so. */ - sk->protinfo.destruct_hook = sk->protinfo.pppox; + po = pppox_sk(sk) = kmalloc(sizeof(*po), GFP_KERNEL); + if (!po) + goto frees; + memset(po, 0, sizeof(*po)); + po->sk = sk; + error = 0; sock->sk = sk; - - return 0; - -free_sk: - sk_free(sk); - return error; +out: return error; +frees: sk_free(sk); +decmod: MOD_DEC_USE_COUNT; + goto out; } int pppoe_release(struct socket *sock) @@ -542,7 +540,7 @@ /* Signal the death of the socket. */ sk->state = PPPOX_DEAD; - po = sk->protinfo.pppox; + po = pppox_sk(sk); if (po->pppoe_pa.sid) { delete_item(po->pppoe_pa.sid, po->pppoe_pa.remote); } @@ -568,7 +566,7 @@ struct sock *sk = sock->sk; struct net_device *dev = NULL; struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr; - struct pppox_opt *po = sk->protinfo.pppox; + struct pppox_opt *po = pppox_sk(sk); int error; lock_sock(sk); @@ -657,7 +655,7 @@ sp.sa_family = AF_PPPOX; sp.sa_protocol = PX_PROTO_OE; - memcpy(&sp.sa_addr.pppoe, &sock->sk->protinfo.pppox->pppoe_pa, + memcpy(&sp.sa_addr.pppoe, &pppox_sk(sock->sk)->pppoe_pa, sizeof(struct pppoe_addr)); memcpy(uaddr, &sp, len); @@ -672,11 +670,10 @@ unsigned long arg) { struct sock *sk = sock->sk; - struct pppox_opt *po; + struct pppox_opt *po = pppox_sk(sk); int val = 0; int err = 0; - po = sk->protinfo.pppox; switch (cmd) { case PPPIOCGMRU: err = -ENXIO; @@ -776,6 +773,7 @@ { struct sk_buff *skb = NULL; struct sock *sk = sock->sk; + struct pppox_opt *po = pppox_sk(sk); int error = 0; struct pppoe_hdr hdr; struct pppoe_hdr *ph; @@ -794,7 +792,7 @@ lock_sock(sk); - dev = sk->protinfo.pppox->pppoe_dev; + dev = po->pppoe_dev; error = -EMSGSIZE; if (total_len > (dev->mtu + dev->hard_header_len)) @@ -829,8 +827,7 @@ error = total_len; dev->hard_header(skb, dev, ETH_P_PPP_SES, - sk->protinfo.pppox->pppoe_pa.remote, - NULL, total_len); + po->pppoe_pa.remote, NULL, total_len); memcpy(ph, &hdr, sizeof(struct pppoe_hdr)); @@ -851,7 +848,8 @@ ***********************************************************************/ int __pppoe_xmit(struct sock *sk, struct sk_buff *skb) { - struct net_device *dev = sk->protinfo.pppox->pppoe_dev; + struct pppox_opt *po = pppox_sk(sk); + struct net_device *dev = po->pppoe_dev; struct pppoe_hdr hdr; struct pppoe_hdr *ph; int headroom = skb_headroom(skb); @@ -897,8 +895,7 @@ skb2->dev = dev; dev->hard_header(skb2, dev, ETH_P_PPP_SES, - sk->protinfo.pppox->pppoe_pa.remote, - NULL, data_len); + po->pppoe_pa.remote, NULL, data_len); /* We're transmitting skb2, and assuming that dev_queue_xmit * will free it. The generic ppp layer however, is expecting diff -Nru a/drivers/net/pppox.c b/drivers/net/pppox.c --- a/drivers/net/pppox.c Tue Feb 19 18:08:59 2002 +++ b/drivers/net/pppox.c Tue Feb 19 18:08:59 2002 @@ -68,7 +68,7 @@ /* Clear connection to ppp device, if attached. */ if (sk->state & PPPOX_BOUND) { - ppp_unregister_channel(&sk->protinfo.pppox->chan); + ppp_unregister_channel(&pppox_sk(sk)->chan); sk->state &= ~PPPOX_BOUND; } } @@ -81,10 +81,8 @@ unsigned long arg) { struct sock *sk = sock->sk; - struct pppox_opt *po; + struct pppox_opt *po = pppox_sk(sk); int err = 0; - - po = sk->protinfo.pppox; lock_sock(sk); diff -Nru a/drivers/net/sb1000.c b/drivers/net/sb1000.c --- a/drivers/net/sb1000.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/sb1000.c Tue Feb 19 18:08:57 2002 @@ -204,7 +204,12 @@ /* * Ok set it up. */ - + if (!request_region(ioaddr[0], 16, dev->name)) + continue; + if (!request_region(ioaddr[1], 16, dev->name)) { + release_region(ioaddr[0], 16); + continue; + } dev->base_addr = ioaddr[0]; /* rmem_end holds the second I/O address - fv */ @@ -262,9 +267,6 @@ /* Lock resources */ - request_region(ioaddr[0], 16, dev->name); - request_region(ioaddr[1], 16, dev->name); - return 0; } } @@ -962,8 +964,6 @@ /* rmem_end holds the second I/O address - fv */ ioaddr[1] = dev->rmem_end; name = dev->name; - request_region(ioaddr[0], SB1000_IO_EXTENT, "sb1000"); - request_region(ioaddr[1], SB1000_IO_EXTENT, "sb1000"); /* initialize sb1000 */ if ((status = sb1000_reset(ioaddr, name))) diff -Nru a/drivers/net/sis900.c b/drivers/net/sis900.c --- a/drivers/net/sis900.c Tue Feb 19 18:08:59 2002 +++ b/drivers/net/sis900.c Tue Feb 19 18:08:59 2002 @@ -18,7 +18,8 @@ preliminary Rev. 1.0 Jan. 18, 1998 http://www.sis.com.tw/support/databook.htm - Rev 1.08.02 Jan. 4 2002 Matt Domsch update to use library crc32 function + Rev 1.08.02 Nov. 30 2001 Hui-Fen Hsu workaround for EDB & bug fix for dhcp problem + Jan. 4 2002 Matt Domsch update to use library crc32 function Rev 1.08.01 Aug. 25 2001 Hui-Fen Hsu update for 630ET & workaround for ICS1893 PHY Rev 1.08.00 Jun. 11 2001 Hui-Fen Hsu workaround for RTL8201 PHY and some bug fix Rev 1.07.11 Apr. 2 2001 Hui-Fen Hsu updates PCI drivers to use the new pci_set_dma_mask for kernel 2.4.3 @@ -56,19 +57,24 @@ #include #include #include - #include #include +#include +#include +#include + #include /* Processor type for cache alignment. */ #include #include -#include -#include +#include /* User space memory access functions */ #include "sis900.h" +#define SIS900_MODULE_NAME "sis900" +#define SIS900_DRV_VERSION "v1.08.02 1/4/2002" + static char version[] __devinitdata = -KERN_INFO "sis900.c: v1.08.02 1/4/2002\n"; +KERN_INFO "sis900.c: " SIS900_DRV_VERSION "\n"; static int max_interrupt_work = 40; static int multicast_filter_limit = 128; @@ -872,6 +878,9 @@ netif_start_queue(net_dev); + /* Workaround for EDB */ + sis900_set_mode(ioaddr, HW_SPEED_10_MBPS, FDX_CAPABLE_HALF_SELECTED); + /* Enable all known interrupts by setting the interrupt mask. */ outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr); outl(RxENA | inl(ioaddr + cr), ioaddr + cr); @@ -1128,6 +1137,7 @@ sis900_set_mode(net_dev->base_addr, speed, duplex); pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); sis630_set_eq(net_dev, revision); + netif_start_queue(net_dev); } sis_priv->timer.expires = jiffies + HZ; @@ -1411,6 +1421,12 @@ unsigned int entry; unsigned long flags; + /* Don't transmit data before the complete of auto-negotiation */ + if(!sis_priv->autong_complete){ + netif_stop_queue(net_dev); + return 1; + } + spin_lock_irqsave(&sis_priv->lock, flags); /* Calculate the next Tx descriptor entry. */ @@ -1769,6 +1785,40 @@ } /** + * netdev_ethtool_ioctl: - For the basic support of ethtool + * @net_dev: the net device to command for + * @useraddr: start address of interface request + * + * Process ethtool command such as "ehtool -i" to show information + */ + +static int netdev_ethtool_ioctl (struct net_device *net_dev, void *useraddr) +{ + struct sis900_private *sis_priv = net_dev->priv; + u32 ethcmd; + + if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO: + { + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, SIS900_MODULE_NAME); + strcpy (info.version, SIS900_DRV_VERSION); + strcpy (info.bus_info, sis_priv->pci_dev->slot_name); + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; + } + default: + break; + } + + return -EOPNOTSUPP; +} + +/** * mii_ioctl: - process MII i/o control command * @net_dev: the net device to command for * @rq: parameter for command @@ -1783,18 +1833,18 @@ struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; switch(cmd) { + case SIOCETHTOOL: + return netdev_ethtool_ioctl(net_dev, (void *) rq->ifr_data); + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ data->phy_id = sis_priv->mii->phy_addr; /* Fall Through */ case SIOCGMIIREG: /* Read MII PHY register. */ - case SIOCDEVPRIVATE+1: /* for binary compat, remove in 2.5 */ data->val_out = mdio_read(net_dev, data->phy_id & 0x1f, data->reg_num & 0x1f); return 0; case SIOCSMIIREG: /* Write MII PHY register. */ - case SIOCDEVPRIVATE+2: /* for binary compat, remove in 2.5 */ if (!capable(CAP_NET_ADMIN)) return -EPERM; mdio_write(net_dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); @@ -2074,8 +2124,6 @@ pci_release_regions(pci_dev); pci_set_drvdata(pci_dev, NULL); } - -#define SIS900_MODULE_NAME "sis900" static struct pci_driver sis900_pci_driver = { name: SIS900_MODULE_NAME, diff -Nru a/drivers/net/skfp/h/cmtdef.h b/drivers/net/skfp/h/cmtdef.h --- a/drivers/net/skfp/h/cmtdef.h Tue Feb 19 18:08:58 2002 +++ b/drivers/net/skfp/h/cmtdef.h Tue Feb 19 18:08:58 2002 @@ -171,7 +171,7 @@ /* WARNING : * EVENT_PCM* must be last in the above list - * if more then two ports are used, EVENT_PCM .. EVENT_PCMA+NUM_PHYS-1 + * if more than two ports are used, EVENT_PCM .. EVENT_PCMA+NUM_PHYS-1 * are used ! */ diff -Nru a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c --- a/drivers/net/tokenring/ibmtr.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/tokenring/ibmtr.c Tue Feb 19 18:08:57 2002 @@ -236,6 +236,10 @@ printk("\n"); } +/* We have to ioremap every checked address, because isa_readb is + * going away. + */ + static void __devinit find_turbo_adapters(int *iolist) { int ram_addr; int index=0; @@ -243,7 +247,8 @@ int found_turbo=0; unsigned char *tchanid, ctemp; int i,j; - + void *ram_mapped ; + if (turbo_searched == 1) return; turbo_searched=1; for (ram_addr=0xC0000; ram_addr < 0xE0000; ram_addr+=0x2000) { @@ -251,37 +256,40 @@ __u32 intf_tbl=0; found_turbo=1; - chanid=(CHANNEL_ID + ram_addr); + ram_mapped = ioremap((u32)ram_addr,0x1fff) ; + if (ram_mapped==NULL) + continue ; + chanid=(CHANNEL_ID + ram_mapped); tchanid=pcchannelid; - ctemp=isa_readb(chanid) & 0x0f; + ctemp=readb(chanid) & 0x0f; if (ctemp != *tchanid) continue; for (i=2,j=1; i<=46; i=i+2,j++) { - if ((isa_readb(chanid+i) & 0x0f) != tchanid[j]){ + if ((readb(chanid+i) & 0x0f) != tchanid[j]){ found_turbo=0; break; } } if (!found_turbo) continue; - isa_writeb(0x90, ram_addr+0x1E01); + writeb(0x90, ram_mapped+0x1E01); for(i=2; i<0x0f; i++) { - isa_writeb(0x00, ram_addr+0x1E01+i); + writeb(0x00, ram_mapped+0x1E01+i); } - isa_writeb(0x00, ram_addr+0x1E01); + writeb(0x00, ram_mapped+0x1E01); for(i=jiffies+TR_BUSY_INTERVAL; time_before_eq(jiffies,i);); - intf_tbl=ntohs(isa_readw(ram_addr+ACA_OFFSET+ACA_RW+WRBR_EVEN)); + intf_tbl=ntohs(readw(ram_mapped+ACA_OFFSET+ACA_RW+WRBR_EVEN)); if (intf_tbl) { #if IBMTR_DEBUG_MESSAGES printk("ibmtr::find_turbo_adapters, Turbo found at " "ram_addr %x\n",ram_addr); printk("ibmtr::find_turbo_adapters, interface_table "); for(i=0; i<6; i++) { - printk("%x:",isa_readb(ram_addr+intf_tbl+i)); + printk("%x:",readb(ram_addr+intf_tbl+i)); } printk("\n"); #endif - turbo_io[index]=ntohs(isa_readw(ram_addr+intf_tbl+4)); - turbo_irq[index]=isa_readb(ram_addr+intf_tbl+3); + turbo_io[index]=ntohs(readw(ram_mapped+intf_tbl+4)); + turbo_irq[index]=readb(ram_mapped+intf_tbl+3); outb(0, turbo_io[index] + ADAPTRESET); for(i=jiffies+TR_RST_TIME;time_before_eq(jiffies,i);); outb(0, turbo_io[index] + ADAPTRESETREL); @@ -292,7 +300,8 @@ printk("ibmtr::find_turbo_adapters, ibmtr card found at" " %x but not a Turbo model\n",ram_addr); #endif - } + iounmap(ram_addr) ; + } /* for */ for(i=0; iirq, &olympic_interrupt, SA_SHIRQ , "olympic", dev)) { return -EAGAIN; } @@ -441,8 +450,12 @@ writew(swab16(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON), init_srb+8); else writew(swab16(OPEN_ADAPTER_ENABLE_FDX), init_srb+8); + + /* Test OR of first 3 bytes as its totally possible for + * someone to set the first 2 bytes to be zero, although this + * is an error, the first byte must have bit 6 set to 1 */ - if (olympic_priv->olympic_laa[0]) { + if (olympic_priv->olympic_laa[0] | olympic_priv->olympic_laa[1] | olympic_priv->olympic_laa[2]) { writeb(olympic_priv->olympic_laa[0],init_srb+12); writeb(olympic_priv->olympic_laa[1],init_srb+13); writeb(olympic_priv->olympic_laa[2],init_srb+14); @@ -458,8 +471,12 @@ writel(LISR_SRB_CMD,olympic_mmio+LISR_SUM); t = jiffies ; + + add_wait_queue(&olympic_priv->srb_wait,&wait) ; + set_current_state(TASK_INTERRUPTIBLE) ; + while(olympic_priv->srb_queued) { - interruptible_sleep_on_timeout(&olympic_priv->srb_wait, 60*HZ); + schedule() ; if(signal_pending(current)) { printk(KERN_WARNING "%s: Signal received in open.\n", dev->name); @@ -474,7 +491,11 @@ olympic_priv->srb_queued=0; break ; } + set_current_state(TASK_INTERRUPTIBLE) ; } + remove_wait_queue(&olympic_priv->srb_wait,&wait) ; + set_current_state(TASK_RUNNING) ; + restore_flags(flags); #if OLYMPIC_DEBUG printk("init_srb(%p): ",init_srb); @@ -515,6 +536,17 @@ return -EIO ; } /* if autosense && open_finished */ + } else if (init_srb[2] == 0x32) { + printk(KERN_WARNING "%s: Invalid LAA: %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, + olympic_priv->olympic_laa[0], + olympic_priv->olympic_laa[1], + olympic_priv->olympic_laa[2], + olympic_priv->olympic_laa[3], + olympic_priv->olympic_laa[4], + olympic_priv->olympic_laa[5]) ; + free_irq(dev->irq,dev) ; + return -EIO ; } else { printk(KERN_WARNING "%s: Bad OPEN response: %x\n", dev->name,init_srb[2]); free_irq(dev->irq, dev); @@ -634,7 +666,10 @@ olympic_priv->tx_ring_free=0; /* next entry in tx ring to use */ olympic_priv->tx_ring_last_status=OLYMPIC_TX_RING_SIZE-1; /* last processed tx status */ - writel(SISR_TX1_EOF | SISR_ADAPTER_CHECK | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_ASB_FREE,olympic_mmio+SISR_MASK_SUM); + writel(0xffffffff, olympic_mmio+EISR_RWM) ; /* clean the eisr */ + writel(0,olympic_mmio+EISR) ; + writel(EISR_MASK_OPTIONS,olympic_mmio+EISR_MASK) ; /* enables most of the TX error interrupts */ + writel(SISR_TX1_EOF | SISR_ADAPTER_CHECK | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_ASB_FREE | SISR_ERR,olympic_mmio+SISR_MASK_SUM); #if OLYMPIC_DEBUG printk("BMCTL: %x\n",readl(olympic_mmio+BMCTL_SUM)); @@ -822,6 +857,35 @@ } +static void olympic_freemem(struct net_device *dev) +{ + struct olympic_private *olympic_priv=(struct olympic_private *)dev->priv; + int i; + + for(i=0;irx_ring_skb[olympic_priv->rx_status_last_received]); + if (olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer != 0xdeadbeef) { + pci_unmap_single(olympic_priv->pdev, + le32_to_cpu(olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer), + olympic_priv->pkt_buf_sz, PCI_DMA_FROMDEVICE); + } + olympic_priv->rx_status_last_received++; + olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; + } + /* unmap rings */ + pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_status_ring_dma_addr, + sizeof(struct olympic_rx_status) * OLYMPIC_RX_RING_SIZE, PCI_DMA_FROMDEVICE); + pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_ring_dma_addr, + sizeof(struct olympic_rx_desc) * OLYMPIC_RX_RING_SIZE, PCI_DMA_TODEVICE); + + pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_status_ring_dma_addr, + sizeof(struct olympic_tx_status) * OLYMPIC_TX_RING_SIZE, PCI_DMA_FROMDEVICE); + pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_ring_dma_addr, + sizeof(struct olympic_tx_desc) * OLYMPIC_TX_RING_SIZE, PCI_DMA_TODEVICE); + + return ; +} + static void olympic_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev= (struct net_device *)dev_id; @@ -842,9 +906,33 @@ spin_lock(&olympic_priv->olympic_lock); + /* Hotswap gives us this on removal */ + if (sisr == 0xffffffff) { + printk(KERN_WARNING "%s: Hotswap adapter removal.\n",dev->name) ; + olympic_freemem(dev) ; + free_irq(dev->irq, dev) ; + dev->stop = NULL ; + spin_unlock(&olympic_priv->olympic_lock) ; + return ; + } + if (sisr & (SISR_SRB_REPLY | SISR_TX1_EOF | SISR_RX_STATUS | SISR_ADAPTER_CHECK | - SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_RX_NOBUF)) { + SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_RX_NOBUF | SISR_ERR)) { + /* If we ever get this the adapter is seriously dead. Only a reset is going to + * bring it back to life. We're talking pci bus errors and such like :( */ + if((sisr & SISR_ERR) && (readl(olympic_mmio+EISR) & EISR_MASK_OPTIONS)) { + printk(KERN_ERR "Olympic: EISR Error, EISR=%08x\n",readl(olympic_mmio+EISR)) ; + printk(KERN_ERR "The adapter must be reset to clear this condition.\n") ; + printk(KERN_ERR "Please report this error to the driver maintainer and/\n") ; + printk(KERN_ERR "or the linux-tr mailing list.\n") ; + olympic_freemem(dev) ; + free_irq(dev->irq, dev) ; + dev->stop = NULL ; + spin_unlock(&olympic_priv->olympic_lock) ; + return ; + } /* SISR_ERR */ + if(sisr & SISR_SRB_REPLY) { if(olympic_priv->srb_queued==1) { wake_up_interruptible(&olympic_priv->srb_wait); @@ -878,34 +966,12 @@ } /* SISR_RX_STATUS */ if (sisr & SISR_ADAPTER_CHECK) { - int i ; netif_stop_queue(dev); printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, 8 bytes of information follow:\n", dev->name); - writel(readl(olympic_mmio+LAPWWO),olympic_mmio+LAPA); - adapter_check_area = (u8 *)(olympic_mmio+LAPWWO) ; + writel(readl(olympic_mmio+LAPWWC),olympic_mmio+LAPA); + adapter_check_area = olympic_priv->olympic_lap + ((readl(olympic_mmio+LAPWWC)) & (~0xf800)) ; printk(KERN_WARNING "%s: Bytes %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",dev->name, readb(adapter_check_area+0), readb(adapter_check_area+1), readb(adapter_check_area+2), readb(adapter_check_area+3), readb(adapter_check_area+4), readb(adapter_check_area+5), readb(adapter_check_area+6), readb(adapter_check_area+7)) ; - /* The adapter is effectively dead, clean up and exit */ - for(i=0;irx_ring_skb[olympic_priv->rx_status_last_received]); - if (olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer != 0xdeadbeef) { - pci_unmap_single(olympic_priv->pdev, - le32_to_cpu(olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer), - olympic_priv->pkt_buf_sz, PCI_DMA_FROMDEVICE); - } - olympic_priv->rx_status_last_received++; - olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; - } - /* unmap rings */ - pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_status_ring_dma_addr, - sizeof(struct olympic_rx_status) * OLYMPIC_RX_RING_SIZE, PCI_DMA_FROMDEVICE); - pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_ring_dma_addr, - sizeof(struct olympic_rx_desc) * OLYMPIC_RX_RING_SIZE, PCI_DMA_TODEVICE); - - pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_status_ring_dma_addr, - sizeof(struct olympic_tx_status) * OLYMPIC_TX_RING_SIZE, PCI_DMA_FROMDEVICE); - pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_ring_dma_addr, - sizeof(struct olympic_tx_desc) * OLYMPIC_TX_RING_SIZE, PCI_DMA_TODEVICE); - + olympic_freemem(dev) ; free_irq(dev->irq, dev) ; dev->stop = NULL ; spin_unlock(&olympic_priv->olympic_lock) ; @@ -980,7 +1046,8 @@ struct olympic_private *olympic_priv=(struct olympic_private *)dev->priv; u8 *olympic_mmio=olympic_priv->olympic_mmio,*srb; unsigned long t,flags; - int i; + + DECLARE_WAITQUEUE(wait,current) ; netif_stop_queue(dev); @@ -999,8 +1066,12 @@ writel(LISR_SRB_CMD,olympic_mmio+LISR_SUM); t = jiffies ; + + add_wait_queue(&olympic_priv->srb_wait,&wait) ; + set_current_state(TASK_INTERRUPTIBLE) ; + while(olympic_priv->srb_queued) { - interruptible_sleep_on_timeout(&olympic_priv->srb_wait, jiffies+60*HZ); + schedule() ; if(signal_pending(current)) { printk(KERN_WARNING "%s: SRB timed out.\n",dev->name); printk(KERN_WARNING "SISR=%x MISR=%x\n",readl(olympic_mmio+SISR),readl(olympic_mmio+LISR)); @@ -1012,32 +1083,16 @@ olympic_priv->srb_queued=0; break ; } + set_current_state(TASK_INTERRUPTIBLE) ; } + remove_wait_queue(&olympic_priv->srb_wait,&wait) ; + set_current_state(TASK_RUNNING) ; restore_flags(flags) ; olympic_priv->rx_status_last_received++; olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; - - for(i=0;irx_ring_skb[olympic_priv->rx_status_last_received]); - if (olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer != 0xdeadbeef) { - pci_unmap_single(olympic_priv->pdev, - le32_to_cpu(olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer), - olympic_priv->pkt_buf_sz, PCI_DMA_FROMDEVICE); - } - olympic_priv->rx_status_last_received++; - olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; - } - /* unmap rings */ - pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_status_ring_dma_addr, - sizeof(struct olympic_rx_status) * OLYMPIC_RX_RING_SIZE, PCI_DMA_FROMDEVICE); - pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_ring_dma_addr, - sizeof(struct olympic_rx_desc) * OLYMPIC_RX_RING_SIZE, PCI_DMA_TODEVICE); - pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_status_ring_dma_addr, - sizeof(struct olympic_tx_status) * OLYMPIC_TX_RING_SIZE, PCI_DMA_FROMDEVICE); - pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_ring_dma_addr, - sizeof(struct olympic_tx_desc) * OLYMPIC_TX_RING_SIZE, PCI_DMA_TODEVICE); + olympic_freemem(dev) ; /* reset tx/rx fifo's and busmaster logic */ diff -Nru a/drivers/net/tokenring/olympic.h b/drivers/net/tokenring/olympic.h --- a/drivers/net/tokenring/olympic.h Tue Feb 19 18:08:59 2002 +++ b/drivers/net/tokenring/olympic.h Tue Feb 19 18:08:59 2002 @@ -79,6 +79,7 @@ #define EISR 0x34 #define EISR_RWM 0x38 #define EISR_MASK 0x3c +#define EISR_MASK_OPTIONS 0x001FFF7F #define LAPA 0x60 #define LAPWWO 0x64 diff -Nru a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c --- a/drivers/net/tulip/tulip_core.c Tue Feb 19 18:08:57 2002 +++ b/drivers/net/tulip/tulip_core.c Tue Feb 19 18:08:57 2002 @@ -880,7 +880,6 @@ return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ if (tp->mii_cnt) data->phy_id = phy; else if (tp->flags & HAS_NWAY) @@ -891,7 +890,6 @@ return -ENODEV; case SIOCGMIIREG: /* Read MII PHY register. */ - case SIOCDEVPRIVATE+1: /* for binary compat, remove in 2.5 */ if (data->phy_id == 32 && (tp->flags & HAS_NWAY)) { int csr12 = inl (ioaddr + CSR12); int csr14 = inl (ioaddr + CSR14); @@ -927,7 +925,6 @@ return 0; case SIOCSMIIREG: /* Write MII PHY register. */ - case SIOCDEVPRIVATE+2: /* for binary compat, remove in 2.5 */ if (!capable (CAP_NET_ADMIN)) return -EPERM; if (regnum & ~0x1f) @@ -1762,7 +1759,7 @@ name: DRV_NAME, id_table: tulip_pci_tbl, probe: tulip_init_one, - remove: tulip_remove_one, + remove: __devexit_p(tulip_remove_one), #ifdef CONFIG_PM suspend: tulip_suspend, resume: tulip_resume, diff -Nru a/drivers/net/tun.c b/drivers/net/tun.c --- a/drivers/net/tun.c Tue Feb 19 18:08:59 2002 +++ b/drivers/net/tun.c Tue Feb 19 18:08:59 2002 @@ -181,12 +181,16 @@ } /* Get packet from user space buffer(already verified) */ -static __inline__ ssize_t tun_get_user(struct tun_struct *tun, const char *buf, size_t count) +static __inline__ ssize_t +tun_get_user(struct tun_struct *tun, const struct iovec *iov, + unsigned long count, size_t total) { struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) }; - register const char *ptr = buf; - register int len = count; + const struct iovec *vector = &iov[0]; + register const char *ptr = vector->iov_base; + register int len = vector->iov_len; struct sk_buff *skb; + ssize_t sent = 0; if (!(tun->flags & TUN_NO_PI)) { if ((len -= sizeof(pi)) < 0) @@ -194,15 +198,33 @@ copy_from_user(&pi, ptr, sizeof(pi)); ptr += sizeof(pi); + + if (len == 0) { + vector++; + count--; + + ptr = vector->iov_base; + len = vector->iov_len; + } } - if (!(skb = alloc_skb(len + 2, GFP_KERNEL))) { + if (!(skb = alloc_skb(total + 2, GFP_KERNEL))) { tun->stats.rx_dropped++; return -ENOMEM; } skb_reserve(skb, 2); - copy_from_user(skb_put(skb, len), ptr, len); + + while (count > 0) { + copy_from_user(skb_put(skb, len), ptr, len); + sent += len; + + vector++; + count--; + + ptr = vector->iov_base; + len = vector->iov_len; + } skb->dev = &tun->dev; switch (tun->flags & TUN_TYPE_MASK) { @@ -221,9 +243,9 @@ netif_rx_ni(skb); tun->stats.rx_packets++; - tun->stats.rx_bytes += len; + tun->stats.rx_bytes += sent; - return count; + return total; } /* Write */ @@ -231,6 +253,7 @@ size_t count, loff_t *pos) { struct tun_struct *tun = (struct tun_struct *)file->private_data; + struct iovec iov; if (!tun) return -EBADFD; @@ -240,7 +263,30 @@ if (verify_area(VERIFY_READ, buf, count)) return -EFAULT; - return tun_get_user(tun, buf, count); + iov.iov_base = (char *) buf; + iov.iov_len = count; + + return tun_get_user(tun, &iov, 1, count); +} + +/* Writev */ +static ssize_t tun_chr_writev(struct file * file, const struct iovec *iov, + unsigned long count, loff_t *pos) +{ + struct tun_struct *tun = (struct tun_struct *)file->private_data; + size_t total = 0; + unsigned long i; + + if (!tun) + return -EBADFD; + + for (i = 0; i < count; i++) { + if (verify_area(VERIFY_READ, iov[i].iov_base, iov[i].iov_len)) + return -EFAULT; + total += iov[i].iov_len; + } + + return tun_get_user(tun, iov, count, total); } /* Put packet to user space buffer(already verified) */ @@ -547,6 +593,7 @@ llseek: no_llseek, read: tun_chr_read, write: tun_chr_write, + writev: tun_chr_writev, poll: tun_chr_poll, ioctl: tun_chr_ioctl, open: tun_chr_open, diff -Nru a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c --- a/drivers/net/via-rhine.c Tue Feb 19 18:08:59 2002 +++ b/drivers/net/via-rhine.c Tue Feb 19 18:08:59 2002 @@ -81,11 +81,14 @@ - Add ethtool support - Replace some MII-related magic numbers with constants + LK1.1.14 (jgarzik): + - Merge new PCI id from 'linuxfet' driver. + */ #define DRV_NAME "via-rhine" -#define DRV_VERSION "1.1.13" -#define DRV_RELDATE "Nov-17-2001" +#define DRV_VERSION "1.1.14" +#define DRV_RELDATE "Feb-12-2002" /* A few user-configurable values. @@ -347,6 +350,8 @@ CanHaveMII | HasWOL }, { "VIA VT3043 Rhine", RHINE_IOTYPE, 128, CanHaveMII | ReqTxAlign } + { "VIA VT6105 Rhine-III", RHINE_IOTYPE, 256, + CanHaveMII | HasWOL }, }; static struct pci_device_id via_rhine_pci_tbl[] __devinitdata = @@ -354,6 +359,7 @@ {0x1106, 0x6100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT86C100A}, {0x1106, 0x3065, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT6102}, {0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT3043}, + {0x1106, 0x3106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT6105}, {0,} /* terminate list */ }; MODULE_DEVICE_TABLE(pci, via_rhine_pci_tbl); diff -Nru a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c --- a/drivers/net/wan/comx.c Tue Feb 19 18:08:59 2002 +++ b/drivers/net/wan/comx.c Tue Feb 19 18:08:59 2002 @@ -64,6 +64,7 @@ #include #include #include +#include #ifdef CONFIG_KMOD #include @@ -793,6 +794,7 @@ } memset(dev, 0, sizeof(struct net_device)); + lock_kernel(); if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR, comx_root_dir)) == NULL) { goto cleanup_dev; @@ -852,6 +854,7 @@ ch->lineup_delay = DEFAULT_LINEUP_DELAY; MOD_INC_USE_COUNT; + unlock_kernel(); return 0; cleanup_if_ptr: kfree(ch->if_ptr); @@ -871,23 +874,29 @@ remove_proc_entry(dentry->d_name.name, comx_root_dir); cleanup_dev: kfree(dev); + unlock_kernel(); return ret; } static int comx_rmdir(struct inode *dir, struct dentry *dentry) { struct proc_dir_entry *entry = PDE(dentry->d_inode); - struct net_device *dev = entry->data; - struct comx_channel *ch = dev->priv; + struct net_device *dev; + struct comx_channel *ch; int ret; + lock_kernel(); + dev = entry->data; + ch = dev->priv; if (dev->flags & IFF_UP) { printk(KERN_ERR "%s: down interface before removing it\n", dev->name); + unlock_kernel(); return -EBUSY; } if (ch->protocol && ch->protocol->line_exit && (ret=ch->protocol->line_exit(dev))) { + unlock_kernel(); return ret; } if (ch->hardware && ch->hardware->hw_exit && @@ -895,6 +904,7 @@ if(ch->protocol && ch->protocol->line_init) { ch->protocol->line_init(dev); } + unlock_kernel(); return ret; } ch->protocol = NULL; @@ -920,6 +930,7 @@ remove_proc_entry(dentry->d_name.name, comx_root_dir); MOD_DEC_USE_COUNT; + unlock_kernel(); return 0; } @@ -928,6 +939,7 @@ struct proc_dir_entry *de; struct inode *inode = NULL; + lock_kernel(); if ((de = PDE(dir)) != NULL) { for (de = de->subdir ; de ; de = de->next) { if ((de && de->low_ino) && @@ -937,12 +949,14 @@ if ((inode = proc_get_inode(dir->i_sb, de->low_ino, de)) == NULL) { printk(KERN_ERR "COMX: lookup error\n"); + unlock_kernel(); return ERR_PTR(-EINVAL); } break; } } } + unlock_kernel(); dentry->d_op = &comx_dentry_operations; d_add(dentry, inode); return NULL; diff -Nru a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c --- a/drivers/net/yellowfin.c Tue Feb 19 18:08:59 2002 +++ b/drivers/net/yellowfin.c Tue Feb 19 18:08:59 2002 @@ -40,12 +40,16 @@ LK1.1.5 (val@nmt.edu): * Fix forced full-duplex bug I introduced + + LK1.1.6 (val@nmt.edu): + * Only print warning on truly "oversized" packets + * Fix theoretical bug on gigabit cards - return to 1.1.3 behavior */ #define DRV_NAME "yellowfin" -#define DRV_VERSION "1.05+LK1.1.5" -#define DRV_RELDATE "May 10, 2001" +#define DRV_VERSION "1.05+LK1.1.6" +#define DRV_RELDATE "Feb 11, 2002" #define PFX DRV_NAME ": " @@ -177,8 +181,8 @@ I. Board Compatibility This device driver is designed for the Packet Engines "Yellowfin" Gigabit -Ethernet adapter. The only PCA currently supported is the G-NIC 64-bit -PCI card. +Ethernet adapter. The G-NIC 64-bit PCI card is supported, as well as the +Symbios 53C885E dual function chip. II. Board-specific settings @@ -260,7 +264,8 @@ }; enum capability_flags { HasMII=1, FullTxStatus=2, IsGigabit=4, HasMulticastBug=8, FullRxStatus=16, - HasMACAddrBug=32, DontUseEeprom=64, /* Only on early revs. */ + HasMACAddrBug=32, /* Only on early revs. */ + DontUseEeprom=64, /* Don't read the MAC from the EEPROm. */ }; /* The PCI I/O space extent. */ #define YELLOWFIN_SIZE 0x100 @@ -284,7 +289,7 @@ static struct pci_id_info pci_id_tbl[] = { {"Yellowfin G-NIC Gigabit Ethernet", { 0x07021000, 0xffffffff}, PCI_IOTYPE, YELLOWFIN_SIZE, - FullTxStatus | IsGigabit | HasMulticastBug | HasMACAddrBug}, + FullTxStatus | IsGigabit | HasMulticastBug | HasMACAddrBug | DontUseEeprom}, {"Symbios SYM83C885", { 0x07011000, 0xffffffff}, PCI_IOTYPE, YELLOWFIN_SIZE, HasMII | DontUseEeprom }, {0,}, @@ -1126,8 +1131,9 @@ if (--boguscnt < 0) break; if ( ! (desc_status & RX_EOP)) { - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned multiple buffers," - " status %4.4x!\n", dev->name, desc_status); + if (data_size != 0) + printk(KERN_WARNING "%s: Oversized Ethernet frame spanned multiple buffers," + " status %4.4x, data_size %d!\n", dev->name, desc_status, data_size); yp->stats.rx_length_errors++; } else if ((yp->drv_flags & IsGigabit) && (frame_status & 0x0038)) { /* There was a error. */ diff -Nru a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c --- a/drivers/pci/pci-driver.c Tue Feb 19 18:08:58 2002 +++ b/drivers/pci/pci-driver.c Tue Feb 19 18:08:58 2002 @@ -5,26 +5,26 @@ #include -static int pci_device_suspend(struct device * dev, u32 stage, u32 state) +static int pci_device_suspend(struct device * dev, u32 state, u32 level) { struct pci_dev * pci_dev = (struct pci_dev *)list_entry(dev,struct pci_dev,dev); int error = 0; if (pci_dev->driver) { - if (stage == SUSPEND_SAVE_STATE && pci_dev->driver->save_state) + if (level == SUSPEND_SAVE_STATE && pci_dev->driver->save_state) error = pci_dev->driver->save_state(pci_dev,state); - else if (stage == SUSPEND_POWER_DOWN && pci_dev->driver->suspend) + else if (level == SUSPEND_POWER_DOWN && pci_dev->driver->suspend) error = pci_dev->driver->suspend(pci_dev,state); } return error; } -static int pci_device_resume(struct device * dev, u32 stage) +static int pci_device_resume(struct device * dev, u32 level) { struct pci_dev * pci_dev = (struct pci_dev *)list_entry(dev,struct pci_dev,dev); if (pci_dev->driver) { - if (stage == RESUME_POWER_ON && pci_dev->driver->resume) + if (level == RESUME_POWER_ON && pci_dev->driver->resume) pci_dev->driver->resume(pci_dev); } return 0; diff -Nru a/drivers/pci/pci.c b/drivers/pci/pci.c --- a/drivers/pci/pci.c Tue Feb 19 18:08:58 2002 +++ b/drivers/pci/pci.c Tue Feb 19 18:08:58 2002 @@ -1585,10 +1585,10 @@ switch (rqst) { case PM_SAVE_STATE: - error = pci_pm_save_state((u32)data); + error = pci_pm_save_state((unsigned long)data); break; case PM_SUSPEND: - error = pci_pm_suspend((u32)data); + error = pci_pm_suspend((unsigned long)data); break; case PM_RESUME: error = pci_pm_resume(); diff -Nru a/drivers/pci/pci.ids b/drivers/pci/pci.ids --- a/drivers/pci/pci.ids Tue Feb 19 18:08:56 2002 +++ b/drivers/pci/pci.ids Tue Feb 19 18:08:57 2002 @@ -1551,9 +1551,14 @@ 0001 i960 PCI bus interface 1076 VScom 800 8 port serial adaptor 1077 VScom 400 4 port serial adaptor + 9030 PCI <-> IOBus Bridge (Hot Swap) + 15ed 1002 Macrolink MCCS 8-port Serial (Hot Swap) + 15ed 1003 Macrolink MCCS 16-port Serial (Hot Swap) 9036 9036 9050 PCI <-> IOBus Bridge 10b5 2273 SH-ARC SoHard ARCnet card + 15ed 1000 Macrolink MCCS 8-port Serial + 15ed 1001 Macrolink MCCS 16-port Serial d84d 4006 EX-4006 1P d84d 4008 EX-4008 1P EPP/ECP d84d 4014 EX-4014 2P @@ -3941,6 +3946,12 @@ 1413 Addonics 1414 Microsoft Corporation 1415 Oxford Semiconductor Ltd + 9501 16PCI954 Function 0 + 15ed 2000 Macrolink MCCR Serial p0-3 of 8 + 15ed 2001 Macrolink MCCR Serial p0-3 of 16 + 9511 16PCI954 Function 1 + 15ed 2000 Macrolink MCCR Serial p4-7 of 8 + 15ed 2001 Macrolink MCCR Serial p4-15 of 16 1416 Multiwave Innovation pte Ltd 1417 Convergenet Technologies Inc 1418 Kyushu electronics systems Inc diff -Nru a/drivers/pci/proc.c b/drivers/pci/proc.c --- a/drivers/pci/proc.c Tue Feb 19 18:08:57 2002 +++ b/drivers/pci/proc.c Tue Feb 19 18:08:57 2002 @@ -376,7 +376,7 @@ static ssize_t pci_show_irq(struct device * dev, char * buf, size_t count, loff_t off) { struct pci_dev * pci_dev = list_entry(dev,struct pci_dev,dev); - return off ? 0 : sprintf(buf,"%u",pci_dev->irq); + return off ? 0 : sprintf(buf,"%u\n",pci_dev->irq); } static struct driver_file_entry pci_irq_entry = { diff -Nru a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c --- a/drivers/s390/block/dasd.c Tue Feb 19 18:08:59 2002 +++ b/drivers/s390/block/dasd.c Tue Feb 19 18:08:59 2002 @@ -2489,8 +2489,6 @@ case BLKSSZGET: case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKFLSBUF: case BLKPG: case BLKELVGET: diff -Nru a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c --- a/drivers/s390/block/xpram.c Tue Feb 19 18:08:58 2002 +++ b/drivers/s390/block/xpram.c Tue Feb 19 18:08:58 2002 @@ -163,12 +163,11 @@ static int major = XPRAM_MAJOR; static int devs = XPRAM_DEVS; -static int rahead = XPRAM_RAHEAD; static int sizes[XPRAM_MAX_DEVS] = { 0, }; static int blksize = XPRAM_BLKSIZE; static int hardsect = XPRAM_HARDSECT; -int xpram_devs, xpram_rahead; +int xpram_devs; int xpram_blksize, xpram_hardsect; int xpram_mem_avail = 0; unsigned long xpram_sizes[XPRAM_MAX_DEVS]; @@ -659,26 +658,9 @@ if ( capable(CAP_SYS_ADMIN) )invalidate_buffers(inode->i_rdev); return 0; - case BLKRAGET: /* return the readahead value, 0x1263 */ - if (!arg) return -EINVAL; - err = 0; /* verify_area_20(VERIFY_WRITE, (long *) arg, sizeof(long)); - * if (err) return err; - */ - put_user(read_ahead[MAJOR(inode->i_rdev)], (long *)arg); - - return 0; - - case BLKRASET: /* set the readahead value, 0x1262 */ - if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (arg > 0xff) return -EINVAL; /* limit it */ - read_ahead[MAJOR(inode->i_rdev)] = arg; - atomic_eieio(); - return 0; - case BLKRRPART: /* re-read partition table: can't do it, 0x1259 */ return -EINVAL; - #if (XPRAM_VERSION == 22) RO_IOCTLS(inode->i_rdev, arg); /* the default RO operations * BLKROSET @@ -940,7 +922,6 @@ * snoozing with a debugger. */ - xpram_rahead = rahead; xpram_blksize = blksize; xpram_hardsect = hardsect; @@ -1029,7 +1010,7 @@ PRINT_INFO(" %d kB expanded memory found.\n",xpram_mem_avail ); /* - * Assign the other needed values: request, rahead, size, blksize, + * Assign the other needed values: request, size, blksize, * hardsect. All the minor devices feature the same value. * Note that `xpram' defines all of them to allow testing non-default * values. A real device could well avoid setting values in global @@ -1042,7 +1023,6 @@ q = BLK_DEFAULT_QUEUE (major); blk_init_queue (q, xpram_request); #endif /* V22/V24 */ - read_ahead[major] = xpram_rahead; /* we want to have XPRAM_UNUSED blocks security buffer between devices */ mem_usable=xpram_mem_avail-(XPRAM_UNUSED*(xpram_devs-1)); @@ -1181,7 +1161,6 @@ kfree(xpram_hardsects); hardsect_size[major] = NULL; fail_malloc: - read_ahead[major] = 0; #if (XPRAM_VERSION == 22) blk_dev[major].request_fn = NULL; #endif /* V22 */ diff -Nru a/drivers/s390/char/tapeblock.c b/drivers/s390/char/tapeblock.c --- a/drivers/s390/char/tapeblock.c Tue Feb 19 18:08:59 2002 +++ b/drivers/s390/char/tapeblock.c Tue Feb 19 18:08:59 2002 @@ -101,7 +101,6 @@ } if (tapeblock_major == 0) tapeblock_major = result; /* accept dynamic major number*/ INIT_BLK_DEV(tapeblock_major,tape_request_fn,tapeblock_getqueue,NULL); - read_ahead[tapeblock_major]=TAPEBLOCK_READAHEAD; PRINT_WARN(KERN_ERR " tape gets major %d for block device\n", result); blk_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC); memset(blk_size[tapeblock_major],0,256*sizeof(int)); diff -Nru a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c --- a/drivers/scsi/aha1542.c Tue Feb 19 18:08:56 2002 +++ b/drivers/scsi/aha1542.c Tue Feb 19 18:08:56 2002 @@ -112,8 +112,6 @@ static int setup_busoff[MAXBOARDS]; static int setup_dmaspeed[MAXBOARDS] __initdata = { -1, -1, -1, -1 }; -static char *setup_str[MAXBOARDS] __initdata; - /* * LILO/Module params: aha1542=[,,[,]] * @@ -962,6 +960,7 @@ } #ifndef MODULE +static char *setup_str[MAXBOARDS] __initdata; static int setup_idx = 0; void __init aha1542_setup(char *str, int *ints) diff -Nru a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c --- a/drivers/scsi/ide-scsi.c Tue Feb 19 18:08:57 2002 +++ b/drivers/scsi/ide-scsi.c Tue Feb 19 18:08:57 2002 @@ -259,11 +259,10 @@ printk("]\n"); } -static void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup) +static int idescsi_end_request(ide_drive_t *drive, int uptodate) { - ide_drive_t *drive = hwgroup->drive; idescsi_scsi_t *scsi = drive->driver_data; - struct request *rq = hwgroup->rq; + struct request *rq = HWGROUP(drive)->rq; idescsi_pc_t *pc = (idescsi_pc_t *) rq->special; int log = test_bit(IDESCSI_LOG_CMD, &scsi->log); struct Scsi_Host *host; @@ -271,8 +270,8 @@ unsigned long flags; if (!(rq->flags & REQ_SPECIAL)) { - ide_end_request (uptodate, hwgroup); - return; + ide_end_request(drive, uptodate); + return 0; } ide_end_drive_cmd (drive, 0, 0); if (rq->errors >= ERROR_MAX) { @@ -302,6 +301,8 @@ idescsi_free_bio (rq->bio); kfree(pc); kfree(rq); scsi->pc = NULL; + + return 0; } static inline unsigned long get_timeout(idescsi_pc_t *pc) @@ -341,7 +342,7 @@ ide__sti(); if (status & ERR_STAT) rq->errors++; - idescsi_end_request (1, HWGROUP(drive)); + idescsi_end_request(drive, 1); return ide_stopped; } bcount = IN_BYTE (IDE_BCOUNTH_REG) << 8 | IN_BYTE (IDE_BCOUNTL_REG); @@ -470,7 +471,7 @@ return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->special); } blk_dump_rq_flags(rq, "ide-scsi: unsup command"); - idescsi_end_request (0,HWGROUP (drive)); + idescsi_end_request(drive, 0); return ide_stopped; } @@ -534,6 +535,7 @@ return 0; } +int idescsi_init(void); int idescsi_reinit(ide_drive_t *drive); /* @@ -541,7 +543,6 @@ */ static ide_driver_t idescsi_driver = { name: "ide-scsi", - version: IDESCSI_VERSION, media: ide_scsi, busy: 0, supports_dma: 1, @@ -560,17 +561,10 @@ capacity: NULL, special: NULL, proc: NULL, + driver_init: idescsi_init, driver_reinit: idescsi_reinit, }; -int idescsi_init (void); -static ide_module_t idescsi_module = { - IDE_DRIVER_MODULE, - idescsi_init, - &idescsi_driver, - NULL -}; - int idescsi_reinit (ide_drive_t *drive) { #if 0 @@ -592,7 +586,7 @@ printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name); continue; } - if (ide_register_subdriver (drive, &idescsi_driver, IDE_SUBDRIVER_VERSION)) { + if (ide_register_subdriver (drive, &idescsi_driver)) { printk (KERN_ERR "ide-scsi: %s: Failed to register the driver with ide.c\n", drive->name); kfree (scsi); continue; @@ -632,7 +626,7 @@ printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name); continue; } - if (ide_register_subdriver (drive, &idescsi_driver, IDE_SUBDRIVER_VERSION)) { + if (ide_register_subdriver (drive, &idescsi_driver)) { printk (KERN_ERR "ide-scsi: %s: Failed to register the driver with ide.c\n", drive->name); kfree (scsi); continue; @@ -642,7 +636,7 @@ failed--; } } - ide_register_module(&idescsi_module); + ide_register_module(&idescsi_driver); MOD_DEC_USE_COUNT; return 0; } @@ -912,7 +906,7 @@ failed++; } } - ide_unregister_module(&idescsi_module); + ide_unregister_module(&idescsi_driver); } module_init(init_idescsi_module); diff -Nru a/drivers/scsi/scsi_merge.c b/drivers/scsi/scsi_merge.c --- a/drivers/scsi/scsi_merge.c Tue Feb 19 18:08:59 2002 +++ b/drivers/scsi/scsi_merge.c Tue Feb 19 18:08:59 2002 @@ -123,7 +123,7 @@ { struct Scsi_Host *SHpnt = SDpnt->host; request_queue_t *q = &SDpnt->request_queue; - dma64_addr_t bounce_limit; + u64 bounce_limit; /* * The generic merging functions work just fine for us. diff -Nru a/drivers/scsi/sd.c b/drivers/scsi/sd.c --- a/drivers/scsi/sd.c Tue Feb 19 18:08:58 2002 +++ b/drivers/scsi/sd.c Tue Feb 19 18:08:58 2002 @@ -133,7 +133,7 @@ if (dp->device != NULL && dp->device->host == host && dp->device->id == tgt) return MKDEV_SD(i); - return 0; + return NODEV; } #endif @@ -228,8 +228,6 @@ case BLKGETSIZE64: case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKFLSBUF: case BLKSSZGET: case BLKPG: @@ -1190,18 +1188,6 @@ rscsi_disks[i].has_part_table = 1; } } - /* If our host adapter is capable of scatter-gather, then we increase - * the read-ahead to 60 blocks (120 sectors). If not, we use - * a two block (4 sector) read ahead. We can only respect this with the - * granularity of every 16 disks (one device major). - */ - for (i = 0; i < N_USED_SD_MAJORS; i++) { - read_ahead[SD_MAJOR(i)] = - (rscsi_disks[i * SCSI_DISKS_PER_MAJOR].device - && rscsi_disks[i * SCSI_DISKS_PER_MAJOR].device->host->sg_tablesize) - ? 120 /* 120 sector read-ahead */ - : 4; /* 4 sector read-ahead */ - } return; } diff -Nru a/drivers/scsi/sr.c b/drivers/scsi/sr.c --- a/drivers/scsi/sr.c Tue Feb 19 18:08:58 2002 +++ b/drivers/scsi/sr.c Tue Feb 19 18:08:58 2002 @@ -785,16 +785,6 @@ &sr_bdops, NULL); register_cdrom(&scsi_CDs[i].cdi); } - - - /* If our host adapter is capable of scatter-gather, then we increase - * the read-ahead to 16 blocks (32 sectors). If not, we use - * a two block (4 sector) read ahead. */ - if (scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize) - read_ahead[MAJOR_NR] = 32; /* 32 sector read-ahead. Always removable. */ - else - read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */ - } static void sr_detach(Scsi_Device * SDp) @@ -846,7 +836,6 @@ kfree(sr_blocksizes); sr_blocksizes = NULL; } - read_ahead[MAJOR_NR] = 0; blk_clear(MAJOR_NR); sr_template.dev_max = 0; diff -Nru a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c --- a/drivers/scsi/sr_ioctl.c Tue Feb 19 18:08:57 2002 +++ b/drivers/scsi/sr_ioctl.c Tue Feb 19 18:08:57 2002 @@ -550,8 +550,6 @@ return put_user((u64)scsi_CDs[target].capacity << 9, (u64 *)arg); case BLKROSET: case BLKROGET: - case BLKRASET: - case BLKRAGET: case BLKFLSBUF: case BLKSSZGET: return blk_ioctl(cdi->dev, cmd, arg); diff -Nru a/drivers/sgi/char/graphics.c b/drivers/sgi/char/graphics.c --- a/drivers/sgi/char/graphics.c Tue Feb 19 18:08:57 2002 +++ b/drivers/sgi/char/graphics.c Tue Feb 19 18:08:57 2002 @@ -219,6 +219,7 @@ int board = GRAPHICS_CARD (vma->vm_dentry->d_inode->i_rdev); unsigned long virt_add, phys_add; + struct page * page; #ifdef DEBUG printk ("Got a page fault for board %d address=%lx guser=%lx\n", board, @@ -245,8 +246,10 @@ pgd = pgd_offset(current->mm, address); pmd = pmd_offset(pgd, address); - pte = pte_offset(pmd, address); - return pte_page(*pte); + pte = pte_kmap_offset(pmd, address); + page = pte_page(*pte); + pte_kunmap(pte); + return page; } /* diff -Nru a/drivers/sound/724hwmcode.h b/drivers/sound/724hwmcode.h --- a/drivers/sound/724hwmcode.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1575 +0,0 @@ -//============================================================================= -// Copyright (c) 1997-1999 Yamaha Corporation. All Rights Reserved. -// -// Title: -// hwmcode.c -// Desc: -// micro-code for CTRL & DSP -//============================================================================= -#ifndef _HWMCODE_ -#define _HWMCODE_ - -static unsigned long int DspInst[] __initdata = { - 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, - 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, - 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, - 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000 -}; - -static unsigned long int CntrlInst[] __initdata = { - 0x000007, 0x240007, 0x0C0007, 0x1C0007, - 0x060007, 0x700002, 0x000020, 0x030040, - 0x007104, 0x004286, 0x030040, 0x000F0D, - 0x000810, 0x20043A, 0x000282, 0x00020D, - 0x000810, 0x20043A, 0x001282, 0x200E82, - 0x001A82, 0x032D0D, 0x000810, 0x10043A, - 0x02D38D, 0x000810, 0x18043A, 0x00010D, - 0x020015, 0x0000FD, 0x000020, 0x038860, - 0x039060, 0x038060, 0x038040, 0x038040, - 0x038040, 0x018040, 0x000A7D, 0x038040, - 0x038040, 0x018040, 0x200402, 0x000882, - 0x08001A, 0x000904, 0x015986, 0x000007, - 0x260007, 0x000007, 0x000007, 0x018A06, - 0x000007, 0x030C8D, 0x000810, 0x18043A, - 0x260007, 0x00087D, 0x018042, 0x00160A, - 0x04A206, 0x000007, 0x00218D, 0x000810, - 0x08043A, 0x21C206, 0x000007, 0x0007FD, - 0x018042, 0x08000A, 0x000904, 0x029386, - 0x000195, 0x090D04, 0x000007, 0x000820, - 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, - 0x032206, 0x018040, 0x000A7D, 0x038042, - 0x13804A, 0x18000A, 0x001820, 0x059060, - 0x058860, 0x018040, 0x0000FD, 0x018042, - 0x70000A, 0x000115, 0x071144, 0x032386, - 0x030000, 0x007020, 0x034A06, 0x018040, - 0x00348D, 0x000810, 0x08043A, 0x21EA06, - 0x000007, 0x02D38D, 0x000810, 0x18043A, - 0x018206, 0x000007, 0x240007, 0x000F8D, - 0x000810, 0x00163A, 0x002402, 0x005C02, - 0x0028FD, 0x000020, 0x018040, 0x08000D, - 0x000815, 0x510984, 0x000007, 0x00004D, - 0x000E5D, 0x000E02, 0x00418D, 0x000810, - 0x08043A, 0x2C8A06, 0x000007, 0x00008D, - 0x000924, 0x000F02, 0x00458D, 0x000810, - 0x08043A, 0x2C8A06, 0x000007, 0x00387D, - 0x018042, 0x08000A, 0x001015, 0x010984, - 0x018386, 0x000007, 0x01AA06, 0x000007, - 0x0008FD, 0x018042, 0x18000A, 0x001904, - 0x218086, 0x280007, 0x001810, 0x28043A, - 0x280C02, 0x00000D, 0x000810, 0x28143A, - 0x08808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00020D, 0x189904, 0x000007, - 0x00402D, 0x0000BD, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x055A86, 0x000007, - 0x000100, 0x000A20, 0x00047D, 0x018040, - 0x018042, 0x20000A, 0x003015, 0x012144, - 0x034986, 0x000007, 0x002104, 0x034986, - 0x000007, 0x000F8D, 0x000810, 0x280C3A, - 0x023944, 0x06C986, 0x000007, 0x001810, - 0x28043A, 0x08810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x002810, 0x78003A, - 0x00688D, 0x000810, 0x08043A, 0x288A06, - 0x000007, 0x00400D, 0x001015, 0x189904, - 0x292904, 0x393904, 0x000007, 0x060206, - 0x000007, 0x0004F5, 0x00007D, 0x000020, - 0x00008D, 0x010860, 0x018040, 0x00047D, - 0x038042, 0x21804A, 0x18000A, 0x021944, - 0x215886, 0x000007, 0x004075, 0x71F104, - 0x000007, 0x010042, 0x28000A, 0x002904, - 0x212086, 0x000007, 0x003C0D, 0x30A904, - 0x000007, 0x00077D, 0x018042, 0x08000A, - 0x000904, 0x07DA86, 0x00057D, 0x002820, - 0x03B060, 0x07F206, 0x018040, 0x003020, - 0x03A860, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x07FA86, 0x000007, - 0x00057D, 0x018042, 0x28040A, 0x000E8D, - 0x000810, 0x280C3A, 0x00000D, 0x000810, - 0x28143A, 0x09000D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x003DFD, 0x000020, - 0x018040, 0x00107D, 0x008D8D, 0x000810, - 0x08043A, 0x288A06, 0x000007, 0x000815, - 0x08001A, 0x010984, 0x095186, 0x00137D, - 0x200500, 0x280F20, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x038A60, 0x018040, 0x007FBD, 0x383DC4, - 0x000007, 0x001A7D, 0x001375, 0x018042, - 0x09004A, 0x10000A, 0x0B8D04, 0x139504, - 0x000007, 0x000820, 0x019060, 0x001104, - 0x212086, 0x010040, 0x0017FD, 0x018042, - 0x08000A, 0x000904, 0x212286, 0x000007, - 0x00197D, 0x038042, 0x09804A, 0x10000A, - 0x000924, 0x001664, 0x0011FD, 0x038042, - 0x2B804A, 0x19804A, 0x00008D, 0x218944, - 0x000007, 0x002244, 0x0AE186, 0x000007, - 0x001A64, 0x002A24, 0x00197D, 0x080102, - 0x100122, 0x000820, 0x039060, 0x018040, - 0x003DFD, 0x00008D, 0x000820, 0x018040, - 0x001375, 0x001A7D, 0x010042, 0x09804A, - 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, - 0x309144, 0x000007, 0x00060D, 0x000A15, - 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, - 0x000464, 0x01B3E4, 0x0232E4, 0x000464, - 0x000464, 0x000464, 0x000464, 0x00040D, - 0x08B1C4, 0x000007, 0x000820, 0x000BF5, - 0x030040, 0x00197D, 0x038042, 0x09804A, - 0x000A24, 0x08000A, 0x080E64, 0x000007, - 0x100122, 0x000820, 0x031060, 0x010040, - 0x0064AC, 0x00027D, 0x000020, 0x018040, - 0x00107D, 0x018042, 0x0011FD, 0x3B804A, - 0x09804A, 0x20000A, 0x000095, 0x1A1144, - 0x00A144, 0x0D2086, 0x00040D, 0x00B984, - 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, - 0x09804A, 0x28000A, 0x000095, 0x010924, - 0x002A64, 0x0D1186, 0x000007, 0x002904, - 0x0D2286, 0x000007, 0x0D2A06, 0x080002, - 0x00008D, 0x00387D, 0x000820, 0x018040, - 0x00127D, 0x018042, 0x10000A, 0x003904, - 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, - 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, - 0x000015, 0x00082D, 0x02C78D, 0x000820, - 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, - 0x0E7186, 0x400025, 0x00008D, 0x110944, - 0x000007, 0x00018D, 0x109504, 0x000007, - 0x009164, 0x000424, 0x000424, 0x000424, - 0x100102, 0x280002, 0x02C68D, 0x000820, - 0x0EC206, 0x00018D, 0x00042D, 0x00008D, - 0x109504, 0x000007, 0x00020D, 0x109184, - 0x000007, 0x02C70D, 0x000820, 0x00008D, - 0x0038FD, 0x018040, 0x003BFD, 0x001020, - 0x03A860, 0x000815, 0x313184, 0x212184, - 0x000007, 0x03B060, 0x03A060, 0x018040, - 0x0022FD, 0x000095, 0x010924, 0x000424, - 0x000424, 0x001264, 0x100102, 0x000820, - 0x039060, 0x018040, 0x001924, 0x00FB8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x000424, - 0x000424, 0x00117D, 0x018042, 0x08000A, - 0x000A24, 0x280502, 0x280C02, 0x09800D, - 0x000820, 0x0002FD, 0x018040, 0x200007, - 0x0022FD, 0x018042, 0x08000A, 0x000095, - 0x280DC4, 0x011924, 0x00197D, 0x018042, - 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, - 0x113144, 0x0A8D04, 0x000007, 0x080A44, - 0x129504, 0x000007, 0x0023FD, 0x001020, - 0x038040, 0x101244, 0x000007, 0x000820, - 0x039060, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x10FA86, 0x000007, - 0x003BFD, 0x000100, 0x000A10, 0x0B807A, - 0x13804A, 0x090984, 0x000007, 0x000095, - 0x013D04, 0x118086, 0x10000A, 0x100002, - 0x090984, 0x000007, 0x038042, 0x11804A, - 0x090D04, 0x000007, 0x10000A, 0x090D84, - 0x000007, 0x00257D, 0x000820, 0x018040, - 0x00010D, 0x000810, 0x28143A, 0x00127D, - 0x018042, 0x20000A, 0x00197D, 0x018042, - 0x00117D, 0x31804A, 0x10000A, 0x003124, - 0x01280D, 0x00397D, 0x000820, 0x058040, - 0x038042, 0x09844A, 0x000606, 0x08040A, - 0x300102, 0x003124, 0x000424, 0x000424, - 0x001224, 0x280502, 0x001A4C, 0x130186, - 0x700002, 0x00002D, 0x030000, 0x00387D, - 0x018042, 0x10000A, 0x132A06, 0x002124, - 0x0000AD, 0x100002, 0x00010D, 0x000924, - 0x006B24, 0x01368D, 0x00397D, 0x000820, - 0x058040, 0x038042, 0x09844A, 0x000606, - 0x08040A, 0x003264, 0x00008D, 0x000A24, - 0x001020, 0x00227D, 0x018040, 0x013C0D, - 0x000810, 0x08043A, 0x29D206, 0x000007, - 0x002820, 0x00207D, 0x018040, 0x00117D, - 0x038042, 0x13804A, 0x33800A, 0x00387D, - 0x018042, 0x08000A, 0x000904, 0x163A86, - 0x000007, 0x00008D, 0x030964, 0x01478D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x380102, - 0x000424, 0x000424, 0x001224, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x14A286, - 0x000007, 0x280502, 0x001A4C, 0x163986, - 0x000007, 0x032164, 0x00632C, 0x003DFD, - 0x018042, 0x08000A, 0x000095, 0x090904, - 0x000007, 0x000820, 0x001A4C, 0x156186, - 0x018040, 0x030000, 0x157A06, 0x002124, - 0x00010D, 0x000924, 0x006B24, 0x015B8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x003A64, - 0x000095, 0x001224, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x15DA86, 0x000007, - 0x01628D, 0x000810, 0x08043A, 0x29D206, - 0x000007, 0x14D206, 0x000007, 0x007020, - 0x08010A, 0x10012A, 0x0020FD, 0x038860, - 0x039060, 0x018040, 0x00227D, 0x018042, - 0x003DFD, 0x08000A, 0x31844A, 0x000904, - 0x16D886, 0x18008B, 0x00008D, 0x189904, - 0x00312C, 0x17AA06, 0x000007, 0x00324C, - 0x173386, 0x000007, 0x001904, 0x173086, - 0x000007, 0x000095, 0x199144, 0x00222C, - 0x003124, 0x00636C, 0x000E3D, 0x001375, - 0x000BFD, 0x010042, 0x09804A, 0x10000A, - 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, - 0x000007, 0x00008D, 0x189904, 0x00226C, - 0x00322C, 0x30050A, 0x301DAB, 0x002083, - 0x0018FD, 0x018042, 0x08000A, 0x018924, - 0x300502, 0x001083, 0x001875, 0x010042, - 0x10000A, 0x00008D, 0x010924, 0x001375, - 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, - 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, - 0x305C8B, 0x006083, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x187A86, 0x000007, - 0x001E2D, 0x0005FD, 0x018042, 0x08000A, - 0x028924, 0x280502, 0x00060D, 0x000810, - 0x280C3A, 0x00008D, 0x000810, 0x28143A, - 0x0A808D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x001275, 0x030042, 0x21004A, - 0x00008D, 0x1A0944, 0x000007, 0x01980D, - 0x000810, 0x08043A, 0x2B2206, 0x000007, - 0x0001F5, 0x030042, 0x0D004A, 0x10000A, - 0x089144, 0x000007, 0x000820, 0x010040, - 0x0025F5, 0x0A3144, 0x000007, 0x000820, - 0x032860, 0x030040, 0x00217D, 0x038042, - 0x0B804A, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x00008D, 0x000124, 0x00012C, - 0x000E64, 0x001A64, 0x00636C, 0x08010A, - 0x10012A, 0x000820, 0x031060, 0x030040, - 0x0020FD, 0x018042, 0x08000A, 0x00227D, - 0x018042, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x00197D, 0x018042, 0x08000A, - 0x0022FD, 0x038042, 0x10000A, 0x000820, - 0x031060, 0x030040, 0x090D04, 0x000007, - 0x000820, 0x030040, 0x038042, 0x0B804A, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x038042, 0x13804A, 0x19804A, 0x110D04, - 0x198D04, 0x000007, 0x08000A, 0x001020, - 0x031860, 0x030860, 0x030040, 0x00008D, - 0x0B0944, 0x000007, 0x000820, 0x010040, - 0x0005F5, 0x030042, 0x08000A, 0x000820, - 0x010040, 0x0000F5, 0x010042, 0x08000A, - 0x000904, 0x1C6086, 0x001E75, 0x030042, - 0x01044A, 0x000C0A, 0x1C7206, 0x000007, - 0x000402, 0x000C02, 0x00177D, 0x001AF5, - 0x018042, 0x03144A, 0x031C4A, 0x03244A, - 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, - 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, - 0x030042, 0x0B004A, 0x1B804A, 0x13804A, - 0x20000A, 0x089144, 0x19A144, 0x0389E4, - 0x0399EC, 0x005502, 0x005D0A, 0x030042, - 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, - 0x089144, 0x19A144, 0x0389E4, 0x0399EC, - 0x006502, 0x006D0A, 0x030042, 0x0B004A, - 0x19004A, 0x2B804A, 0x13804A, 0x21804A, - 0x30000A, 0x089144, 0x19A144, 0x2AB144, - 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, - 0x03A9E4, 0x000702, 0x00107D, 0x000415, - 0x018042, 0x08000A, 0x0109E4, 0x000F02, - 0x002AF5, 0x0019FD, 0x010042, 0x09804A, - 0x10000A, 0x000934, 0x001674, 0x0029F5, - 0x010042, 0x10000A, 0x00917C, 0x002075, - 0x010042, 0x08000A, 0x000904, 0x1ED286, - 0x0026F5, 0x0027F5, 0x030042, 0x09004A, - 0x10000A, 0x000A3C, 0x00167C, 0x001A75, - 0x000BFD, 0x010042, 0x51804A, 0x48000A, - 0x160007, 0x001075, 0x010042, 0x282C0A, - 0x281D12, 0x282512, 0x001F32, 0x1E0007, - 0x0E0007, 0x001975, 0x010042, 0x002DF5, - 0x0D004A, 0x10000A, 0x009144, 0x1FB286, - 0x010042, 0x28340A, 0x000E5D, 0x00008D, - 0x000375, 0x000820, 0x010040, 0x05D2F4, - 0x54D104, 0x00735C, 0x205386, 0x000007, - 0x0C0007, 0x080007, 0x0A0007, 0x02040D, - 0x000810, 0x08043A, 0x332206, 0x000007, - 0x205A06, 0x000007, 0x080007, 0x002275, - 0x010042, 0x20000A, 0x002104, 0x212086, - 0x001E2D, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x209286, 0x000007, 0x002010, - 0x30043A, 0x00057D, 0x0180C3, 0x08000A, - 0x028924, 0x280502, 0x280C02, 0x0A810D, - 0x000820, 0x0002F5, 0x010040, 0x220007, - 0x0004FD, 0x018042, 0x70000A, 0x030000, - 0x007020, 0x06FA06, 0x018040, 0x02180D, - 0x000810, 0x08043A, 0x2B2206, 0x000007, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x218A86, 0x000007, 0x01F206, 0x000007, - 0x000875, 0x0009FD, 0x00010D, 0x220A06, - 0x000295, 0x000B75, 0x00097D, 0x00000D, - 0x000515, 0x010042, 0x18000A, 0x001904, - 0x287886, 0x0006F5, 0x001020, 0x010040, - 0x0004F5, 0x000820, 0x010040, 0x000775, - 0x010042, 0x09804A, 0x10000A, 0x001124, - 0x000904, 0x22BA86, 0x000815, 0x080102, - 0x101204, 0x22DA06, 0x000575, 0x081204, - 0x000007, 0x100102, 0x000575, 0x000425, - 0x021124, 0x100102, 0x000820, 0x031060, - 0x010040, 0x001924, 0x287886, 0x00008D, - 0x000464, 0x009D04, 0x278886, 0x180102, - 0x000575, 0x010042, 0x28040A, 0x00018D, - 0x000924, 0x280D02, 0x00000D, 0x000924, - 0x281502, 0x10000D, 0x000820, 0x0002F5, - 0x010040, 0x200007, 0x001175, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x23C286, - 0x000007, 0x000100, 0x080B20, 0x130B60, - 0x1B0B60, 0x030A60, 0x010040, 0x050042, - 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, - 0x0006F5, 0x010042, 0x28140A, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x24CA86, 0x004015, 0x000095, 0x010D04, - 0x24B886, 0x100022, 0x10002A, 0x24E206, - 0x000007, 0x333104, 0x2AA904, 0x000007, - 0x032124, 0x280502, 0x001124, 0x000424, - 0x000424, 0x003224, 0x00292C, 0x00636C, - 0x25F386, 0x000007, 0x02B164, 0x000464, - 0x000464, 0x00008D, 0x000A64, 0x280D02, - 0x10008D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x00008D, 0x38B904, 0x000007, - 0x03296C, 0x30010A, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x25BA86, 0x000007, - 0x02312C, 0x28050A, 0x00008D, 0x01096C, - 0x280D0A, 0x10010D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x001124, 0x000424, - 0x000424, 0x003224, 0x300102, 0x032944, - 0x267A86, 0x000007, 0x300002, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x26C086, 0x003124, 0x000464, 0x300102, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x26CA86, 0x000007, 0x003124, 0x300502, - 0x003924, 0x300583, 0x000883, 0x0005F5, - 0x010042, 0x28040A, 0x00008D, 0x008124, - 0x280D02, 0x00008D, 0x008124, 0x281502, - 0x10018D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x001025, 0x000575, 0x030042, - 0x09004A, 0x10000A, 0x0A0904, 0x121104, - 0x000007, 0x001020, 0x050860, 0x050040, - 0x0006FD, 0x018042, 0x09004A, 0x10000A, - 0x0000A5, 0x0A0904, 0x121104, 0x000007, - 0x000820, 0x019060, 0x010040, 0x0002F5, - 0x010042, 0x08000A, 0x000904, 0x284286, - 0x000007, 0x230A06, 0x000007, 0x000606, - 0x000007, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x289286, 0x000007, 0x000100, - 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, - 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, - 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, - 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, - 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, - 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, - 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, - 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, - 0x000606, 0x018040, 0x00008D, 0x000A64, - 0x280D02, 0x000A24, 0x00027D, 0x018042, - 0x10000A, 0x001224, 0x0003FD, 0x018042, - 0x08000A, 0x000904, 0x2A8286, 0x000007, - 0x00018D, 0x000A24, 0x000464, 0x000464, - 0x080102, 0x000924, 0x000424, 0x000424, - 0x100102, 0x02000D, 0x009144, 0x2AD986, - 0x000007, 0x0001FD, 0x018042, 0x08000A, - 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, - 0x000820, 0x0002FD, 0x018040, 0x200007, - 0x00027D, 0x001020, 0x000606, 0x018040, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x2B2A86, 0x000007, 0x00037D, 0x018042, - 0x08000A, 0x000904, 0x2B5A86, 0x000007, - 0x000075, 0x002E7D, 0x010042, 0x0B804A, - 0x000020, 0x000904, 0x000686, 0x010040, - 0x31844A, 0x30048B, 0x000883, 0x00008D, - 0x000810, 0x28143A, 0x00008D, 0x000810, - 0x280C3A, 0x000675, 0x010042, 0x08000A, - 0x003815, 0x010924, 0x280502, 0x0B000D, - 0x000820, 0x0002F5, 0x010040, 0x000606, - 0x220007, 0x000464, 0x000464, 0x000606, - 0x000007, 0x000134, 0x007F8D, 0x00093C, - 0x281D12, 0x282512, 0x001F32, 0x0E0007, - 0x00010D, 0x00037D, 0x000820, 0x018040, - 0x05D2F4, 0x000007, 0x080007, 0x00037D, - 0x018042, 0x08000A, 0x000904, 0x2D0286, - 0x000007, 0x000606, 0x000007, 0x000007, - 0x000012, 0x100007, 0x320007, 0x600007, - 0x100080, 0x48001A, 0x004904, 0x2D6186, - 0x000007, 0x001210, 0x58003A, 0x000145, - 0x5C5D04, 0x000007, 0x000080, 0x48001A, - 0x004904, 0x2DB186, 0x000007, 0x001210, - 0x50003A, 0x005904, 0x2E0886, 0x000045, - 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, - 0x004224, 0x500102, 0x200502, 0x000082, - 0x40001A, 0x004104, 0x2E3986, 0x000007, - 0x003865, 0x40001A, 0x004020, 0x00104D, - 0x04C184, 0x301B86, 0x000040, 0x040007, - 0x000165, 0x000145, 0x004020, 0x000040, - 0x000765, 0x080080, 0x40001A, 0x004104, - 0x2EC986, 0x000007, 0x001210, 0x40003A, - 0x004104, 0x2F2286, 0x00004D, 0x0000CD, - 0x004810, 0x20043A, 0x000882, 0x40001A, - 0x004104, 0x2F3186, 0x000007, 0x004820, - 0x005904, 0x300886, 0x000040, 0x0007E5, - 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, - 0x4216E0, 0x021260, 0x000040, 0x000032, - 0x400075, 0x00007D, 0x07D574, 0x200512, - 0x000082, 0x40001A, 0x004104, 0x2FE186, - 0x000007, 0x037206, 0x640007, 0x060007, - 0x0000E5, 0x000020, 0x000040, 0x000A65, - 0x000020, 0x020040, 0x020040, 0x000040, - 0x000165, 0x000042, 0x70000A, 0x007104, - 0x30A286, 0x000007, 0x018206, 0x640007, - 0x050000, 0x007020, 0x000040, 0x037206, - 0x640007, 0x000007, 0x00306D, 0x028860, - 0x029060, 0x08000A, 0x028860, 0x008040, - 0x100012, 0x00100D, 0x009184, 0x314186, - 0x000E0D, 0x009184, 0x325186, 0x000007, - 0x300007, 0x001020, 0x003B6D, 0x008040, - 0x000080, 0x08001A, 0x000904, 0x316186, - 0x000007, 0x001220, 0x000DED, 0x008040, - 0x008042, 0x10000A, 0x40000D, 0x109544, - 0x000007, 0x001020, 0x000DED, 0x008040, - 0x008042, 0x20040A, 0x000082, 0x08001A, - 0x000904, 0x31F186, 0x000007, 0x003B6D, - 0x008042, 0x08000A, 0x000E15, 0x010984, - 0x329B86, 0x600007, 0x08001A, 0x000C15, - 0x010984, 0x328386, 0x000020, 0x1A0007, - 0x0002ED, 0x008040, 0x620007, 0x00306D, - 0x028042, 0x0A804A, 0x000820, 0x0A804A, - 0x000606, 0x10804A, 0x000007, 0x282512, - 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, - 0x000786, 0x000007, 0x0C0007, 0x0A0007, - 0x1C0007, 0x003465, 0x020040, 0x004820, - 0x025060, 0x40000A, 0x024060, 0x000040, - 0x454944, 0x000007, 0x004020, 0x003AE5, - 0x000040, 0x0028E5, 0x000042, 0x48000A, - 0x004904, 0x386886, 0x002C65, 0x000042, - 0x40000A, 0x0000D5, 0x454104, 0x000007, - 0x000655, 0x054504, 0x34F286, 0x0001D5, - 0x054504, 0x34F086, 0x002B65, 0x000042, - 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, - 0x000007, 0x454504, 0x000007, 0x0000CD, - 0x444944, 0x000007, 0x454504, 0x000007, - 0x00014D, 0x554944, 0x000007, 0x045144, - 0x34E986, 0x002C65, 0x000042, 0x48000A, - 0x4CD104, 0x000007, 0x04C144, 0x34F386, - 0x000007, 0x160007, 0x002CE5, 0x040042, - 0x40000A, 0x004020, 0x000040, 0x002965, - 0x000042, 0x40000A, 0x004104, 0x356086, - 0x000007, 0x002402, 0x36A206, 0x005C02, - 0x0025E5, 0x000042, 0x40000A, 0x004274, - 0x002AE5, 0x000042, 0x40000A, 0x004274, - 0x500112, 0x0029E5, 0x000042, 0x40000A, - 0x004234, 0x454104, 0x000007, 0x004020, - 0x000040, 0x003EE5, 0x000020, 0x000040, - 0x002DE5, 0x400152, 0x50000A, 0x045144, - 0x364A86, 0x0000C5, 0x003EE5, 0x004020, - 0x000040, 0x002BE5, 0x000042, 0x40000A, - 0x404254, 0x000007, 0x002AE5, 0x004020, - 0x000040, 0x500132, 0x040134, 0x005674, - 0x0029E5, 0x020042, 0x42000A, 0x000042, - 0x50000A, 0x05417C, 0x0028E5, 0x000042, - 0x48000A, 0x0000C5, 0x4CC144, 0x371086, - 0x0026E5, 0x0027E5, 0x020042, 0x40004A, - 0x50000A, 0x00423C, 0x00567C, 0x0028E5, - 0x004820, 0x000040, 0x281D12, 0x282512, - 0x001F72, 0x002965, 0x000042, 0x40000A, - 0x004104, 0x37AA86, 0x0E0007, 0x160007, - 0x1E0007, 0x003EE5, 0x000042, 0x40000A, - 0x004104, 0x37E886, 0x002D65, 0x000042, - 0x28340A, 0x003465, 0x020042, 0x42004A, - 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, - 0x54D104, 0x00735C, 0x385186, 0x000007, - 0x000606, 0x080007, 0x0C0007, 0x080007, - 0x0A0007, 0x0001E5, 0x020045, 0x004020, - 0x000060, 0x000365, 0x000040, 0x002E65, - 0x001A20, 0x0A1A60, 0x000040, 0x003465, - 0x020042, 0x42004A, 0x004020, 0x4A004A, - 0x000606, 0x50004A, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000 -}; - -// -------------------------------------------- -// DS-1E Controller InstructionRAM Code -// 1999/06/21 -// Buf441 slot is Enabled. -// -------------------------------------------- -// 04/09?@creat -// 04/12 stop nise fix -// 06/21?@WorkingOff timming -static unsigned long int CntrlInst1E[] __initdata = { - 0x000007, 0x240007, 0x0C0007, 0x1C0007, - 0x060007, 0x700002, 0x000020, 0x030040, - 0x007104, 0x004286, 0x030040, 0x000F0D, - 0x000810, 0x20043A, 0x000282, 0x00020D, - 0x000810, 0x20043A, 0x001282, 0x200E82, - 0x00800D, 0x000810, 0x20043A, 0x001A82, - 0x03460D, 0x000810, 0x10043A, 0x02EC0D, - 0x000810, 0x18043A, 0x00010D, 0x020015, - 0x0000FD, 0x000020, 0x038860, 0x039060, - 0x038060, 0x038040, 0x038040, 0x038040, - 0x018040, 0x000A7D, 0x038040, 0x038040, - 0x018040, 0x200402, 0x000882, 0x08001A, - 0x000904, 0x017186, 0x000007, 0x260007, - 0x400007, 0x000007, 0x03258D, 0x000810, - 0x18043A, 0x260007, 0x284402, 0x00087D, - 0x018042, 0x00160A, 0x05A206, 0x000007, - 0x440007, 0x00230D, 0x000810, 0x08043A, - 0x22FA06, 0x000007, 0x0007FD, 0x018042, - 0x08000A, 0x000904, 0x02AB86, 0x000195, - 0x090D04, 0x000007, 0x000820, 0x0000F5, - 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, - 0x018040, 0x000A7D, 0x038042, 0x13804A, - 0x18000A, 0x001820, 0x059060, 0x058860, - 0x018040, 0x0000FD, 0x018042, 0x70000A, - 0x000115, 0x071144, 0x033B86, 0x030000, - 0x007020, 0x036206, 0x018040, 0x00360D, - 0x000810, 0x08043A, 0x232206, 0x000007, - 0x02EC0D, 0x000810, 0x18043A, 0x019A06, - 0x000007, 0x240007, 0x000F8D, 0x000810, - 0x00163A, 0x002402, 0x005C02, 0x0028FD, - 0x000020, 0x018040, 0x08000D, 0x000815, - 0x510984, 0x000007, 0x00004D, 0x000E5D, - 0x000E02, 0x00430D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x00008D, 0x000924, - 0x000F02, 0x00470D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x480480, 0x001210, - 0x28043A, 0x00778D, 0x000810, 0x280C3A, - 0x00068D, 0x000810, 0x28143A, 0x284402, - 0x03258D, 0x000810, 0x18043A, 0x07FF8D, - 0x000820, 0x0002FD, 0x018040, 0x260007, - 0x200007, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x051286, 0x000007, 0x240007, - 0x02EC0D, 0x000810, 0x18043A, 0x00387D, - 0x018042, 0x08000A, 0x001015, 0x010984, - 0x019B86, 0x000007, 0x01B206, 0x000007, - 0x0008FD, 0x018042, 0x18000A, 0x001904, - 0x22B886, 0x280007, 0x001810, 0x28043A, - 0x280C02, 0x00000D, 0x000810, 0x28143A, - 0x08808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00020D, 0x189904, 0x000007, - 0x00402D, 0x0000BD, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x065A86, 0x000007, - 0x000100, 0x000A20, 0x00047D, 0x018040, - 0x018042, 0x20000A, 0x003015, 0x012144, - 0x036186, 0x000007, 0x002104, 0x036186, - 0x000007, 0x000F8D, 0x000810, 0x280C3A, - 0x023944, 0x07C986, 0x000007, 0x001810, - 0x28043A, 0x08810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x002810, 0x78003A, - 0x00788D, 0x000810, 0x08043A, 0x2A1206, - 0x000007, 0x00400D, 0x001015, 0x189904, - 0x292904, 0x393904, 0x000007, 0x070206, - 0x000007, 0x0004F5, 0x00007D, 0x000020, - 0x00008D, 0x010860, 0x018040, 0x00047D, - 0x038042, 0x21804A, 0x18000A, 0x021944, - 0x229086, 0x000007, 0x004075, 0x71F104, - 0x000007, 0x010042, 0x28000A, 0x002904, - 0x225886, 0x000007, 0x003C0D, 0x30A904, - 0x000007, 0x00077D, 0x018042, 0x08000A, - 0x000904, 0x08DA86, 0x00057D, 0x002820, - 0x03B060, 0x08F206, 0x018040, 0x003020, - 0x03A860, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x08FA86, 0x000007, - 0x00057D, 0x018042, 0x28040A, 0x000E8D, - 0x000810, 0x280C3A, 0x00000D, 0x000810, - 0x28143A, 0x09000D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x003DFD, 0x000020, - 0x018040, 0x00107D, 0x009D8D, 0x000810, - 0x08043A, 0x2A1206, 0x000007, 0x000815, - 0x08001A, 0x010984, 0x0A5186, 0x00137D, - 0x200500, 0x280F20, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x038A60, 0x018040, 0x00107D, 0x018042, - 0x08000A, 0x000215, 0x010984, 0x3A8186, - 0x000007, 0x007FBD, 0x383DC4, 0x000007, - 0x001A7D, 0x001375, 0x018042, 0x09004A, - 0x10000A, 0x0B8D04, 0x139504, 0x000007, - 0x000820, 0x019060, 0x001104, 0x225886, - 0x010040, 0x0017FD, 0x018042, 0x08000A, - 0x000904, 0x225A86, 0x000007, 0x00197D, - 0x038042, 0x09804A, 0x10000A, 0x000924, - 0x001664, 0x0011FD, 0x038042, 0x2B804A, - 0x19804A, 0x00008D, 0x218944, 0x000007, - 0x002244, 0x0C1986, 0x000007, 0x001A64, - 0x002A24, 0x00197D, 0x080102, 0x100122, - 0x000820, 0x039060, 0x018040, 0x003DFD, - 0x00008D, 0x000820, 0x018040, 0x001375, - 0x001A7D, 0x010042, 0x09804A, 0x10000A, - 0x00021D, 0x0189E4, 0x2992E4, 0x309144, - 0x000007, 0x00060D, 0x000A15, 0x000C1D, - 0x001025, 0x00A9E4, 0x012BE4, 0x000464, - 0x01B3E4, 0x0232E4, 0x000464, 0x000464, - 0x000464, 0x000464, 0x00040D, 0x08B1C4, - 0x000007, 0x000820, 0x000BF5, 0x030040, - 0x00197D, 0x038042, 0x09804A, 0x000A24, - 0x08000A, 0x080E64, 0x000007, 0x100122, - 0x000820, 0x031060, 0x010040, 0x0064AC, - 0x00027D, 0x000020, 0x018040, 0x00107D, - 0x018042, 0x0011FD, 0x3B804A, 0x09804A, - 0x20000A, 0x000095, 0x1A1144, 0x00A144, - 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, - 0x0018FD, 0x018042, 0x0010FD, 0x09804A, - 0x28000A, 0x000095, 0x010924, 0x002A64, - 0x0E4986, 0x000007, 0x002904, 0x0E5A86, - 0x000007, 0x0E6206, 0x080002, 0x00008D, - 0x00387D, 0x000820, 0x018040, 0x00127D, - 0x018042, 0x10000A, 0x003904, 0x0F0986, - 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, - 0x000025, 0x0FB206, 0x00002D, 0x000015, - 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, - 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, - 0x400025, 0x00008D, 0x110944, 0x000007, - 0x00018D, 0x109504, 0x000007, 0x009164, - 0x000424, 0x000424, 0x000424, 0x100102, - 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, - 0x00018D, 0x00042D, 0x00008D, 0x109504, - 0x000007, 0x00020D, 0x109184, 0x000007, - 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, - 0x018040, 0x003BFD, 0x001020, 0x03A860, - 0x000815, 0x313184, 0x212184, 0x000007, - 0x03B060, 0x03A060, 0x018040, 0x0022FD, - 0x000095, 0x010924, 0x000424, 0x000424, - 0x001264, 0x100102, 0x000820, 0x039060, - 0x018040, 0x001924, 0x010F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x000424, 0x000424, - 0x00117D, 0x018042, 0x08000A, 0x000A24, - 0x280502, 0x280C02, 0x09800D, 0x000820, - 0x0002FD, 0x018040, 0x200007, 0x0022FD, - 0x018042, 0x08000A, 0x000095, 0x280DC4, - 0x011924, 0x00197D, 0x018042, 0x0011FD, - 0x09804A, 0x10000A, 0x0000B5, 0x113144, - 0x0A8D04, 0x000007, 0x080A44, 0x129504, - 0x000007, 0x0023FD, 0x001020, 0x038040, - 0x101244, 0x000007, 0x000820, 0x039060, - 0x018040, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x123286, 0x000007, 0x003BFD, - 0x000100, 0x000A10, 0x0B807A, 0x13804A, - 0x090984, 0x000007, 0x000095, 0x013D04, - 0x12B886, 0x10000A, 0x100002, 0x090984, - 0x000007, 0x038042, 0x11804A, 0x090D04, - 0x000007, 0x10000A, 0x090D84, 0x000007, - 0x00257D, 0x000820, 0x018040, 0x00010D, - 0x000810, 0x28143A, 0x00127D, 0x018042, - 0x20000A, 0x00197D, 0x018042, 0x00117D, - 0x31804A, 0x10000A, 0x003124, 0x013B8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x300102, - 0x003124, 0x000424, 0x000424, 0x001224, - 0x280502, 0x001A4C, 0x143986, 0x700002, - 0x00002D, 0x030000, 0x00387D, 0x018042, - 0x10000A, 0x146206, 0x002124, 0x0000AD, - 0x100002, 0x00010D, 0x000924, 0x006B24, - 0x014A0D, 0x00397D, 0x000820, 0x058040, - 0x038042, 0x09844A, 0x000606, 0x08040A, - 0x003264, 0x00008D, 0x000A24, 0x001020, - 0x00227D, 0x018040, 0x014F8D, 0x000810, - 0x08043A, 0x2B5A06, 0x000007, 0x002820, - 0x00207D, 0x018040, 0x00117D, 0x038042, - 0x13804A, 0x33800A, 0x00387D, 0x018042, - 0x08000A, 0x000904, 0x177286, 0x000007, - 0x00008D, 0x030964, 0x015B0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x380102, 0x000424, - 0x000424, 0x001224, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x15DA86, 0x000007, - 0x280502, 0x001A4C, 0x177186, 0x000007, - 0x032164, 0x00632C, 0x003DFD, 0x018042, - 0x08000A, 0x000095, 0x090904, 0x000007, - 0x000820, 0x001A4C, 0x169986, 0x018040, - 0x030000, 0x16B206, 0x002124, 0x00010D, - 0x000924, 0x006B24, 0x016F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x003A64, 0x000095, - 0x001224, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x171286, 0x000007, 0x01760D, - 0x000810, 0x08043A, 0x2B5A06, 0x000007, - 0x160A06, 0x000007, 0x007020, 0x08010A, - 0x10012A, 0x0020FD, 0x038860, 0x039060, - 0x018040, 0x00227D, 0x018042, 0x003DFD, - 0x08000A, 0x31844A, 0x000904, 0x181086, - 0x18008B, 0x00008D, 0x189904, 0x00312C, - 0x18E206, 0x000007, 0x00324C, 0x186B86, - 0x000007, 0x001904, 0x186886, 0x000007, - 0x000095, 0x199144, 0x00222C, 0x003124, - 0x00636C, 0x000E3D, 0x001375, 0x000BFD, - 0x010042, 0x09804A, 0x10000A, 0x038AEC, - 0x0393EC, 0x00224C, 0x18E186, 0x000007, - 0x00008D, 0x189904, 0x00226C, 0x00322C, - 0x30050A, 0x301DAB, 0x002083, 0x0018FD, - 0x018042, 0x08000A, 0x018924, 0x300502, - 0x001083, 0x001875, 0x010042, 0x10000A, - 0x00008D, 0x010924, 0x001375, 0x330542, - 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, - 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, - 0x006083, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x19B286, 0x000007, 0x001E2D, - 0x0005FD, 0x018042, 0x08000A, 0x028924, - 0x280502, 0x00060D, 0x000810, 0x280C3A, - 0x00008D, 0x000810, 0x28143A, 0x0A808D, - 0x000820, 0x0002F5, 0x010040, 0x220007, - 0x001275, 0x030042, 0x21004A, 0x00008D, - 0x1A0944, 0x000007, 0x01AB8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, - 0x030042, 0x0D004A, 0x10000A, 0x089144, - 0x000007, 0x000820, 0x010040, 0x0025F5, - 0x0A3144, 0x000007, 0x000820, 0x032860, - 0x030040, 0x00217D, 0x038042, 0x0B804A, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00008D, 0x000124, 0x00012C, 0x000E64, - 0x001A64, 0x00636C, 0x08010A, 0x10012A, - 0x000820, 0x031060, 0x030040, 0x0020FD, - 0x018042, 0x08000A, 0x00227D, 0x018042, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00197D, 0x018042, 0x08000A, 0x0022FD, - 0x038042, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x090D04, 0x000007, 0x000820, - 0x030040, 0x038042, 0x0B804A, 0x10000A, - 0x000820, 0x031060, 0x030040, 0x038042, - 0x13804A, 0x19804A, 0x110D04, 0x198D04, - 0x000007, 0x08000A, 0x001020, 0x031860, - 0x030860, 0x030040, 0x00008D, 0x0B0944, - 0x000007, 0x000820, 0x010040, 0x0005F5, - 0x030042, 0x08000A, 0x000820, 0x010040, - 0x0000F5, 0x010042, 0x08000A, 0x000904, - 0x1D9886, 0x001E75, 0x030042, 0x01044A, - 0x000C0A, 0x1DAA06, 0x000007, 0x000402, - 0x000C02, 0x00177D, 0x001AF5, 0x018042, - 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, - 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, - 0x00043D, 0x0013F5, 0x001AFD, 0x030042, - 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, - 0x089144, 0x19A144, 0x0389E4, 0x0399EC, - 0x005502, 0x005D0A, 0x030042, 0x0B004A, - 0x1B804A, 0x13804A, 0x20000A, 0x089144, - 0x19A144, 0x0389E4, 0x0399EC, 0x006502, - 0x006D0A, 0x030042, 0x0B004A, 0x19004A, - 0x2B804A, 0x13804A, 0x21804A, 0x30000A, - 0x089144, 0x19A144, 0x2AB144, 0x0389E4, - 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, - 0x000702, 0x00107D, 0x000415, 0x018042, - 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, - 0x0019FD, 0x010042, 0x09804A, 0x10000A, - 0x000934, 0x001674, 0x0029F5, 0x010042, - 0x10000A, 0x00917C, 0x002075, 0x010042, - 0x08000A, 0x000904, 0x200A86, 0x0026F5, - 0x0027F5, 0x030042, 0x09004A, 0x10000A, - 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, - 0x010042, 0x51804A, 0x48000A, 0x160007, - 0x001075, 0x010042, 0x282C0A, 0x281D12, - 0x282512, 0x001F32, 0x1E0007, 0x0E0007, - 0x001975, 0x010042, 0x002DF5, 0x0D004A, - 0x10000A, 0x009144, 0x20EA86, 0x010042, - 0x28340A, 0x000E5D, 0x00008D, 0x000375, - 0x000820, 0x010040, 0x05D2F4, 0x54D104, - 0x00735C, 0x218B86, 0x000007, 0x0C0007, - 0x080007, 0x0A0007, 0x02178D, 0x000810, - 0x08043A, 0x34B206, 0x000007, 0x219206, - 0x000007, 0x080007, 0x002275, 0x010042, - 0x20000A, 0x002104, 0x225886, 0x001E2D, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x21CA86, 0x000007, 0x002010, 0x30043A, - 0x00057D, 0x0180C3, 0x08000A, 0x028924, - 0x280502, 0x280C02, 0x0A810D, 0x000820, - 0x0002F5, 0x010040, 0x220007, 0x0004FD, - 0x018042, 0x70000A, 0x030000, 0x007020, - 0x07FA06, 0x018040, 0x022B8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x22C286, - 0x000007, 0x020206, 0x000007, 0x000875, - 0x0009FD, 0x00010D, 0x234206, 0x000295, - 0x000B75, 0x00097D, 0x00000D, 0x000515, - 0x010042, 0x18000A, 0x001904, 0x2A0086, - 0x0006F5, 0x001020, 0x010040, 0x0004F5, - 0x000820, 0x010040, 0x000775, 0x010042, - 0x09804A, 0x10000A, 0x001124, 0x000904, - 0x23F286, 0x000815, 0x080102, 0x101204, - 0x241206, 0x000575, 0x081204, 0x000007, - 0x100102, 0x000575, 0x000425, 0x021124, - 0x100102, 0x000820, 0x031060, 0x010040, - 0x001924, 0x2A0086, 0x00008D, 0x000464, - 0x009D04, 0x291086, 0x180102, 0x000575, - 0x010042, 0x28040A, 0x00018D, 0x000924, - 0x280D02, 0x00000D, 0x000924, 0x281502, - 0x10000D, 0x000820, 0x0002F5, 0x010040, - 0x200007, 0x001175, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x24FA86, 0x000007, - 0x000100, 0x080B20, 0x130B60, 0x1B0B60, - 0x030A60, 0x010040, 0x050042, 0x3D004A, - 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, - 0x010042, 0x28140A, 0x0004F5, 0x010042, - 0x08000A, 0x000315, 0x010D04, 0x260286, - 0x004015, 0x000095, 0x010D04, 0x25F086, - 0x100022, 0x10002A, 0x261A06, 0x000007, - 0x333104, 0x2AA904, 0x000007, 0x032124, - 0x280502, 0x284402, 0x001124, 0x400102, - 0x000424, 0x000424, 0x003224, 0x00292C, - 0x00636C, 0x277386, 0x000007, 0x02B164, - 0x000464, 0x000464, 0x00008D, 0x000A64, - 0x280D02, 0x10008D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x00008D, 0x38B904, - 0x000007, 0x03296C, 0x30010A, 0x0002F5, - 0x010042, 0x08000A, 0x000904, 0x270286, - 0x000007, 0x00212C, 0x28050A, 0x00316C, - 0x00046C, 0x00046C, 0x28450A, 0x001124, - 0x006B64, 0x100102, 0x00008D, 0x01096C, - 0x280D0A, 0x10010D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x004124, 0x000424, - 0x000424, 0x003224, 0x300102, 0x032944, - 0x27FA86, 0x000007, 0x300002, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x284086, 0x003124, 0x000464, 0x300102, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x284A86, 0x000007, 0x284402, 0x003124, - 0x300502, 0x003924, 0x300583, 0x000883, - 0x0005F5, 0x010042, 0x28040A, 0x00008D, - 0x008124, 0x280D02, 0x00008D, 0x008124, - 0x281502, 0x10018D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x001025, 0x000575, - 0x030042, 0x09004A, 0x10000A, 0x0A0904, - 0x121104, 0x000007, 0x001020, 0x050860, - 0x050040, 0x0006FD, 0x018042, 0x09004A, - 0x10000A, 0x0000A5, 0x0A0904, 0x121104, - 0x000007, 0x000820, 0x019060, 0x010040, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x29CA86, 0x000007, 0x244206, 0x000007, - 0x000606, 0x000007, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x2A1A86, 0x000007, - 0x000100, 0x080B20, 0x138B60, 0x1B8B60, - 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, - 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, - 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, - 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, - 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, - 0x038A60, 0x000606, 0x018040, 0x00008D, - 0x000A64, 0x280D02, 0x000A24, 0x00027D, - 0x018042, 0x10000A, 0x001224, 0x0003FD, - 0x018042, 0x08000A, 0x000904, 0x2C0A86, - 0x000007, 0x00018D, 0x000A24, 0x000464, - 0x000464, 0x080102, 0x000924, 0x000424, - 0x000424, 0x100102, 0x02000D, 0x009144, - 0x2C6186, 0x000007, 0x0001FD, 0x018042, - 0x08000A, 0x000A44, 0x2C4386, 0x018042, - 0x0A000D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00027D, 0x001020, 0x000606, - 0x018040, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x2CB286, 0x000007, 0x00037D, - 0x018042, 0x08000A, 0x000904, 0x2CE286, - 0x000007, 0x000075, 0x002E7D, 0x010042, - 0x0B804A, 0x000020, 0x000904, 0x000686, - 0x010040, 0x31844A, 0x30048B, 0x000883, - 0x00008D, 0x000810, 0x28143A, 0x00008D, - 0x000810, 0x280C3A, 0x000675, 0x010042, - 0x08000A, 0x003815, 0x010924, 0x280502, - 0x0B000D, 0x000820, 0x0002F5, 0x010040, - 0x000606, 0x220007, 0x000464, 0x000464, - 0x000606, 0x000007, 0x000134, 0x007F8D, - 0x00093C, 0x281D12, 0x282512, 0x001F32, - 0x0E0007, 0x00010D, 0x00037D, 0x000820, - 0x018040, 0x05D2F4, 0x000007, 0x080007, - 0x00037D, 0x018042, 0x08000A, 0x000904, - 0x2E8A86, 0x000007, 0x000606, 0x000007, - 0x000007, 0x000012, 0x100007, 0x320007, - 0x600007, 0x460007, 0x100080, 0x48001A, - 0x004904, 0x2EF186, 0x000007, 0x001210, - 0x58003A, 0x000145, 0x5C5D04, 0x000007, - 0x000080, 0x48001A, 0x004904, 0x2F4186, - 0x000007, 0x001210, 0x50003A, 0x005904, - 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, - 0x7FFF7D, 0x07D524, 0x004224, 0x500102, - 0x200502, 0x000082, 0x40001A, 0x004104, - 0x2FC986, 0x000007, 0x003865, 0x40001A, - 0x004020, 0x00104D, 0x04C184, 0x31AB86, - 0x000040, 0x040007, 0x000165, 0x000145, - 0x004020, 0x000040, 0x000765, 0x080080, - 0x40001A, 0x004104, 0x305986, 0x000007, - 0x001210, 0x40003A, 0x004104, 0x30B286, - 0x00004D, 0x0000CD, 0x004810, 0x20043A, - 0x000882, 0x40001A, 0x004104, 0x30C186, - 0x000007, 0x004820, 0x005904, 0x319886, - 0x000040, 0x0007E5, 0x200480, 0x2816A0, - 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, - 0x000040, 0x000032, 0x400075, 0x00007D, - 0x07D574, 0x200512, 0x000082, 0x40001A, - 0x004104, 0x317186, 0x000007, 0x038A06, - 0x640007, 0x0000E5, 0x000020, 0x000040, - 0x000A65, 0x000020, 0x020040, 0x020040, - 0x000040, 0x000165, 0x000042, 0x70000A, - 0x007104, 0x323286, 0x000007, 0x060007, - 0x019A06, 0x640007, 0x050000, 0x007020, - 0x000040, 0x038A06, 0x640007, 0x000007, - 0x00306D, 0x028860, 0x029060, 0x08000A, - 0x028860, 0x008040, 0x100012, 0x00100D, - 0x009184, 0x32D186, 0x000E0D, 0x009184, - 0x33E186, 0x000007, 0x300007, 0x001020, - 0x003B6D, 0x008040, 0x000080, 0x08001A, - 0x000904, 0x32F186, 0x000007, 0x001220, - 0x000DED, 0x008040, 0x008042, 0x10000A, - 0x40000D, 0x109544, 0x000007, 0x001020, - 0x000DED, 0x008040, 0x008042, 0x20040A, - 0x000082, 0x08001A, 0x000904, 0x338186, - 0x000007, 0x003B6D, 0x008042, 0x08000A, - 0x000E15, 0x010984, 0x342B86, 0x600007, - 0x08001A, 0x000C15, 0x010984, 0x341386, - 0x000020, 0x1A0007, 0x0002ED, 0x008040, - 0x620007, 0x00306D, 0x028042, 0x0A804A, - 0x000820, 0x0A804A, 0x000606, 0x10804A, - 0x000007, 0x282512, 0x001F32, 0x05D2F4, - 0x54D104, 0x00735C, 0x000786, 0x000007, - 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, - 0x020040, 0x004820, 0x025060, 0x40000A, - 0x024060, 0x000040, 0x454944, 0x000007, - 0x004020, 0x003AE5, 0x000040, 0x0028E5, - 0x000042, 0x48000A, 0x004904, 0x39F886, - 0x002C65, 0x000042, 0x40000A, 0x0000D5, - 0x454104, 0x000007, 0x000655, 0x054504, - 0x368286, 0x0001D5, 0x054504, 0x368086, - 0x002B65, 0x000042, 0x003AE5, 0x50004A, - 0x40000A, 0x45C3D4, 0x000007, 0x454504, - 0x000007, 0x0000CD, 0x444944, 0x000007, - 0x454504, 0x000007, 0x00014D, 0x554944, - 0x000007, 0x045144, 0x367986, 0x002C65, - 0x000042, 0x48000A, 0x4CD104, 0x000007, - 0x04C144, 0x368386, 0x000007, 0x160007, - 0x002CE5, 0x040042, 0x40000A, 0x004020, - 0x000040, 0x002965, 0x000042, 0x40000A, - 0x004104, 0x36F086, 0x000007, 0x002402, - 0x383206, 0x005C02, 0x0025E5, 0x000042, - 0x40000A, 0x004274, 0x002AE5, 0x000042, - 0x40000A, 0x004274, 0x500112, 0x0029E5, - 0x000042, 0x40000A, 0x004234, 0x454104, - 0x000007, 0x004020, 0x000040, 0x003EE5, - 0x000020, 0x000040, 0x002DE5, 0x400152, - 0x50000A, 0x045144, 0x37DA86, 0x0000C5, - 0x003EE5, 0x004020, 0x000040, 0x002BE5, - 0x000042, 0x40000A, 0x404254, 0x000007, - 0x002AE5, 0x004020, 0x000040, 0x500132, - 0x040134, 0x005674, 0x0029E5, 0x020042, - 0x42000A, 0x000042, 0x50000A, 0x05417C, - 0x0028E5, 0x000042, 0x48000A, 0x0000C5, - 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, - 0x020042, 0x40004A, 0x50000A, 0x00423C, - 0x00567C, 0x0028E5, 0x004820, 0x000040, - 0x281D12, 0x282512, 0x001F72, 0x002965, - 0x000042, 0x40000A, 0x004104, 0x393A86, - 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, - 0x000042, 0x40000A, 0x004104, 0x397886, - 0x002D65, 0x000042, 0x28340A, 0x003465, - 0x020042, 0x42004A, 0x004020, 0x4A004A, - 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, - 0x39E186, 0x000007, 0x000606, 0x080007, - 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, - 0x020045, 0x004020, 0x000060, 0x000365, - 0x000040, 0x002E65, 0x001A20, 0x0A1A60, - 0x000040, 0x003465, 0x020042, 0x42004A, - 0x004020, 0x4A004A, 0x000606, 0x50004A, - 0x0017FD, 0x018042, 0x08000A, 0x000904, - 0x225A86, 0x000007, 0x00107D, 0x018042, - 0x0011FD, 0x33804A, 0x19804A, 0x20000A, - 0x000095, 0x2A1144, 0x01A144, 0x3B9086, - 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, - 0x018042, 0x0010FD, 0x09804A, 0x38000A, - 0x000095, 0x010924, 0x003A64, 0x3B8186, - 0x000007, 0x003904, 0x3B9286, 0x000007, - 0x3B9A06, 0x00000D, 0x00008D, 0x000820, - 0x00387D, 0x018040, 0x700002, 0x00117D, - 0x018042, 0x00197D, 0x29804A, 0x30000A, - 0x380002, 0x003124, 0x000424, 0x000424, - 0x002A24, 0x280502, 0x00068D, 0x000810, - 0x28143A, 0x00750D, 0x00B124, 0x002264, - 0x3D0386, 0x284402, 0x000810, 0x280C3A, - 0x0B800D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00758D, 0x00B124, 0x100102, - 0x012144, 0x3E4986, 0x001810, 0x10003A, - 0x00387D, 0x018042, 0x08000A, 0x000904, - 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, - 0x00008D, 0x023164, 0x000A64, 0x280D02, - 0x0B808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00387D, 0x018042, 0x08000A, - 0x000904, 0x3E3286, 0x030000, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x3D8286, - 0x000007, 0x002810, 0x28043A, 0x00750D, - 0x030924, 0x002264, 0x280D02, 0x02316C, - 0x28450A, 0x0B810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x00008D, 0x000A24, - 0x3E4A06, 0x100102, 0x001810, 0x10003A, - 0x0000BD, 0x003810, 0x30043A, 0x00187D, - 0x018042, 0x0018FD, 0x09804A, 0x20000A, - 0x0000AD, 0x028924, 0x07212C, 0x001010, - 0x300583, 0x300D8B, 0x3014BB, 0x301C83, - 0x002083, 0x00137D, 0x038042, 0x33844A, - 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, - 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, - 0x001E0D, 0x0005FD, 0x018042, 0x20000A, - 0x020924, 0x00068D, 0x00A96C, 0x00009D, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x3F6A86, 0x000007, 0x280502, 0x280D0A, - 0x284402, 0x001810, 0x28143A, 0x0C008D, - 0x000820, 0x0002FD, 0x018040, 0x220007, - 0x003904, 0x225886, 0x001E0D, 0x00057D, - 0x018042, 0x20000A, 0x020924, 0x0000A5, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x402A86, 0x000007, 0x280502, 0x280C02, - 0x002010, 0x28143A, 0x0C010D, 0x000820, - 0x0002FD, 0x018040, 0x225A06, 0x220007, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000 -}; - -#endif //_HWMCODE_ - - diff -Nru a/drivers/sound/CHANGELOG b/drivers/sound/CHANGELOG --- a/drivers/sound/CHANGELOG Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,369 +0,0 @@ -Note these changes relate to Hannu's code and don't include the changes -made outside of this for modularising the sound - -Changelog for version 3.8o --------------------------- - -Since 3.8h -- Included support for OPL3-SA1 and SoftOSS - -Since 3.8 -- Fixed SNDCTL_DSP_GETOSPACE -- Compatibility fixes for Linux 2.1.47 - -Since 3.8-beta21 -- Fixed all known bugs (I think). - -Since 3.8-beta8 -- Lot of fixes to audio playback code in dmabuf.c - -Since 3.8-beta6 -- Fixed the famous Quake delay bug. - -Since 3.8-beta5 -- Fixed many bugs in audio playback. - -Since 3.8-beta4 -- Just minor changes. - -Since 3.8-beta1 -- Major rewrite of audio playback handling. -- Added AWE32 support by Takashi Iwai (in ./lowlevel/). - -Since 3.7-beta# -- Passing of ioctl() parameters between soundcard.c and other modules has been -changed so that arg always points to kernel space. -- Some bugfixes. - -Since 3.7-beta5 -- Disabled MIDI input with GUS PnP (Interwave). There seems to be constant -stream of received 0x00 bytes when the MIDI receiver is enabled. - -Since 3.5 -- Changes almost everywhere. -- Support for OPTi 82C924-based sound cards. - -Since 3.5.4-beta8 -- Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode - with GUS. -- Limited minimum fragment size with some audio devices (GUS=512 and - SB=32). These devices require more time to "recover" from processing - of each fragment. - -Since 3.5.4-beta6/7 -- There seems to be problems in the OPTi 82C930 so cards based on this - chip don't necessarily work yet. There are problems in detecting the - MIDI interface. Also mixer volumes may be seriously wrong on some systems. - You can safely use this driver version with C930 if it looks to work. - However please don't complain if you have problems with it. C930 support - should be fixed in future releases. -- Got initialization of GUS PnP to work. With this version GUS PnP should - work in GUS compatible mode after initialization using isapnptools. -- Fixed a bug in handling of full duplex cards in write only mode. This has - been causing "audio device opening" errors with RealAudio player. - -Since 3.5.4.beta5 -- Changes to OPTi 82C930 driver. -- Major changes to the Soundscape driver. The driver requires now just one - DMA channel. The extra audio/dsp device (the "Not functional" one) used - for code download in the earlier versions has been eliminated. There is now - just one /dev/dsp# device which is used both for code download and audio. - -Since 3.5.4.beta4 -- Minor changes. - -Since 3.5.4-beta2 -- Fixed silent playback with ESS 688/1688. -- Got SB16 to work without the 16 bit DMA channel (only the 8 bit one - is required for 8 and 16 bit modes). -- Added the "lowlevel" subdirectory for additional low level drivers that - are not part of USS core. See lowlevel/README for more info. -- Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in - miroPCM sound cards. See lowlevel/aci.readme for more info. -- Support for Aztech Washington chipset (AZT2316 ASIC). - -Since 3.5.4-beta1 -- Reduced clicking with AD1848. -- Support for OPTi 82C930. Only half duplex at this time. 16 bit playback - is sometimes just white noise (occurs randomly). - -Since 3.5.2 -- Major changes to the SB/Jazz16/ESS driver (most parts rewritten). - The most noticeable new feature is support for multiple SB cards at the same - time. -- Renamed sb16_midi.c to uart401.c. Also modified it to work also with - other MPU401 UART compatible cards than SB16/ESS/Jazz. -- Some changes which reduce clicking in audio playback. -- Copying policy is now GPL. - -Since 3.5.1 -- TB Maui initialization support -Since 3.5 -- Improved handling of playback underrun situations. - -Since 3.5-beta10 -- Bug fixing - -Since 3.5-beta9 -- Fixed for compatibility with Linux 1.3.70 and later. -- Changed boot time passing of 16 bit DMA channel number to SB driver. - -Since 3.5-beta8 -- Minor changes - -Since 3.5-beta7 -- enhancements to configure program (by Jeff Tranter): - - prompts are in same format as 1.3.x Linux kernel config program - - on-line help for each question - - fixed some compile warnings detected by gcc/g++ -Wall - - minor grammatical changes to prompts - -Since 3.5-beta6 -- Fixed bugs in mmap() support. -- Minor changes to Maui driver. - -Since 3.5-beta5 -- Fixed crash after recording with ESS688. It's generally a good - idea to stop inbound DMA transfers before freeing the memory - buffer. -- Fixed handling of AD1845 codec (for example Shuttle Sound System). -- Few other fixes. - -Since 3.5-beta4 -- Fixed bug in handling of uninitialized instruments with GUS. - -Since 3.5-beta3 -- Few changes which decrease popping at end/beginning of audio playback. - -Since 3.5-beta2 -- Removed MAD16+CS4231 hack made in previous version since it didn't - help. -- Fixed the above bug in proper way and in proper place. Many thanks - to James Hightower. - -Since 3.5-beta1 -- Bug fixes. -- Full duplex audio with MAD16+CS4231 may work now. The driver configures - SB DMA of MAD16 so that it doesn't conflict with codec's DMA channels. - The side effect is that all 8 bit DMA channels (0,1,3) are populated in - duplex mode. - -Since 3.5-alpha9 -- Bug fixes (mostly in Jazz16 and ESS1688/688 supports). -- Temporarily disabled recording with ESS1688/688 since it causes crash. -- Changed audio buffer partitioning algorithm so that it selects - smaller fragment size than earlier. This improves real time capabilities - of the driver and makes recording to disk to work better. Unfortunately - this change breaks some programs which assume that fragments cannot be - shorter than 4096 bytes. - -Since 3.5-alpha8 -- Bug fixes - -Since 3.5-alpha7 -- Linux kernel compatible configuration (_EXPERIMENTAL_). Enable - using command "cd /linux/drivers/sound;make script" and then - just run kernel's make config normally. -- Minor fixes to the SB support. Hopefully the driver works with - all SB models now. -- Added support for ESS ES1688 "AudioDrive" based cards. - -Since 3.5-alpha6 -- SB Pro and SB16 supports are no longer separately selectable options. - Enabling SB enables them too. -- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified -configure to handle this. -- Removed initialization messages from the -modularized version. They can be enabled by using init_trace=1 in -the insmod command line (insmod sound init_trace=1). -- More AIX stuff. -- Added support for synchronizing dsp/audio devices with /dev/sequencer. -- mmap() support for dsp/audio devices. - -Since 3.5-alpha5 -- AIX port. -- Changed some xxx_PATCH macros in soundcard.h to work with - big endian machines. - -Since 3.5-alpha4 -- Removed the 'setfx' stuff from the version distributed with kernel - sources. Running 'setfx' is required again. - -Since 3.5-alpha3 -- Moved stuff from the 'setfx' program to the AudioTrix Pro driver. - -Since 3.5-alpha2 -- Modifications to makefile and configure.c. Unnecessary sources - are no longer compiled. Newly created local.h is also copied to - /etc/soundconf. "make oldconfig" reads /etc/soundconf and produces - new local.h which is compatible with current version of the driver. -- Some fixes to the SB16 support. -- Fixed random protection fault in gus_wave.c - -Since 3.5-alpha1 -- Modified to work with Linux-1.3.33 and later -- Some minor changes - -Since 3.0.2 -- Support for CS4232 based PnP cards (AcerMagic S23 etc). -- Full duplex support for some CS4231, CS4232 and AD1845 based cards -(GUS MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards -having a codec mentioned above). -- Almost fully rewritten loadable modules support. -- Fixed some bugs. -- Huge amount of testing (more testing is still required). -- mmap() support (works with some cards). Requires much more testing. -- Sample/patch/program loading for TB Maui/Tropez. No initialization -since TB doesn't allow me to release that code. -- Using CS4231 compatible codecs as timer for /dev/music. - -Since 3.0.1 -- Added allocation of I/O ports, DMA channels and interrupts -to the initialization code. This may break modules support since -the driver may not free some resources on unload. Should be fixed soon. - -Since 3.0 -- Some important bug fixes. -- select() for /dev/dsp and /dev/audio (Linux only). -(To use select() with read, you have to call read() to start -the recording. Calling write() kills recording immediately so -use select() carefully when you are writing a half duplex app. -Full duplex mode is not implemented yet.) Select works also with -/dev/sequencer and /dev/music. Maybe with /dev/midi## too. - -Since 3.0-beta2 -- Minor fixes. -- Added Readme.cards - -Since 3.0-beta1 -- Minor fixes to the modules support. -- Eliminated call to sb_free_irq() in ad1848.c -- Rewritten MAD16&Mozart support (not tested with MAD16 Pro). -- Fix to DMA initialization of PSS cards. -- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.) -- Fixed some bugs in the PSS driver which caused I/O errors with - the MSS mode (/dev/dsp). - -Since 3.0-950506 -- Recording with GUS MAX fixed. It works when the driver is configured - to use two DMA channels with GUS MAX (16 bit ones recommended). - -Since 3.0-94xxxx -- Too many changes - -Since 3.0-940818 -- Fixes for Linux 1.1.4x. -- Disables Disney Sound System with SG NX Pro 16 (less noise). - -Since 2.90-2 -- Fixes to soundcard.h -- Non blocking mode to /dev/sequencer -- Experimental detection code for Ensoniq Soundscape. - -Since 2.90 -- Minor and major bug fixes - -Since pre-3.0-940712 -- GUS MAX support -- Partially working MSS/WSS support (could work with some cards). -- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs - (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and - GUS MAX, but it doesn't work yet. -Since pre-3.0-940426 -- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc). -This codec chip is used in various sound cards. This version is developed -for the 16 bit daughtercard of GUS. It should work with other cards also -if the following requirements are met: - - The I/O, IRQ and DMA settings are jumper selectable or - the card is initialized by booting DOS before booting Linux (etc.). - - You add the IO, IRQ and DMA settings manually to the local.h. - (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that - the base address bust be the base address of the codec chip not the - card itself. For the GUS16 these are the same but most MSS compatible - cards have the codec located at card_base+4. -- Some minor changes - -Since 2.5 (******* MAJOR REWRITE ***********) - -This version is based on v2.3. I have tried to maintain two versions -together so that this one should have the same features than v2.5. -Something may still be missing. If you notice such things, please let me -know. - -The Readme.v30 contains more details. - -- /dev/midi## devices. -- /dev/sequencer2 - -Since 2.5-beta2 -- Some fine tuning to the GUS v3.7 mixer code. -- Fixed speed limits for the plain SB (1.0 to 2.0). - -Since 2.5-beta -- Fixed OPL-3 detection with SB. Caused problems with PAS16. -- GUS v3.7 mixer support. - -Since 2.4 -- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h). -- Fixed truncated sound on /dev/dsp when the device is closed. -- Linear volume mode for GUS -- Pitch bends larger than +/- 2 octaves. -- MIDI recording for SB and SB Pro. (Untested). -- Some other fixes. -- SB16 MIDI and DSP drivers only initialized if SB16 actually installed. -- Implemented better detection for OPL-3. This should be useful if you - have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3. -- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested). - -Since 2.3b -- Fixed bug which made it impossible to make long recordings to disk. - Recording was not restarted after a buffer overflow situation. -- Limited mixer support for GUS. -- Numerous improvements to the GUS driver by Andrew Robinson. Including - some click removal etc. - -Since 2.3 -- Fixed some minor bugs in the SB16 driver. - -Since 2.2b -- Full SB16 DSP support. 8/16 bit, mono/stereo -- The SCO and FreeBSD versions should be in sync now. There are some - problems with SB16 and GUS in the FreeBSD versions. - The DMA buffer allocation of the SCO version has been polished but - there could still be some problems. At least it hogs memory. - The DMA channel - configuration method used in the SCO/System is a hack. -- Support for the MPU emulation of the SB16. -- Some big arrays are now allocated boot time. This makes the BSS segment - smaller which makes it possible to use the full driver with - NetBSD. These arrays are not allocated if no suitable sound card is available. -- Fixed a bug in the compute_and_set_volume in gus_wave.c -- Fixed the too fast mono playback problem of SB Pro and PAS16. - -Since 2.2 -- Stereo recording for SB Pro. Somehow it was missing and nobody - had noticed it earlier. -- Minor polishing. -- Interpreting of boot time arguments (sound=) for Linux. -- Breakup of sb_dsp.c. Parts of the code has been moved to - sb_mixer.c and sb_midi.c - -Since 2.1 -- Preliminary support for SB16. - - The SB16 mixer is supported in its native mode. - - Digitized voice capability up to 44.1 kHz/8 bit/mono - (16 bit and stereo support coming in the next release). -- Fixed some bugs in the digitized voice driver for PAS16. -- Proper initialization of the SB emulation of latest PAS16 models. - -- Significantly improved /dev/dsp and /dev/audio support. - - Now supports half duplex mode. It's now possible to record and - playback without closing and reopening the device. - - It's possible to use smaller buffers than earlier. There is a new - ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4. - This call instructs the driver to use smaller buffers. The default - buffer size (0.5 to 1.0 seconds) is divided by n. Should be called - immediately after opening the device. - -Since 2.0 -Just cosmetic changes. diff -Nru a/drivers/sound/COPYING b/drivers/sound/COPYING --- a/drivers/sound/COPYING Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff -Nru a/drivers/sound/Config.help b/drivers/sound/Config.help --- a/drivers/sound/Config.help Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,723 +0,0 @@ -CONFIG_INPUT_GAMEPORT - Gameport support is for the standard 15-pin PC gameport. If you - have a joystick, gamepad, gameport card, a soundcard with a gameport - or anything else that uses the gameport, say Y or M here and also to - at least one of the hardware specific drivers. - Please read the file which - contains more information and the location of the joystick package - that you'll need if you use the gameport with a joystick. - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called gameport.o. If you want to compile it as - a module, say M here and read . - -CONFIG_SOUND_OSS - OSS is the Open Sound System suite of sound card drivers. They make - sound programming easier since they provide a common API. Say Y or - M here (the module will be called sound.o) if you haven't found a - driver for your sound card above, then pick your driver from the - list below. - -CONFIG_SOUND_DMAP - Linux can often have problems allocating DMA buffers for ISA sound - cards on machines with more than 16MB of RAM. This is because ISA - DMA buffers must exist below the 16MB boundary and it is quite - possible that a large enough free block in this region cannot be - found after the machine has been running for a while. If you say Y - here the DMA buffers (64Kb) will be allocated at boot time and kept - until the shutdown. This option is only useful if you said Y to - "OSS sound modules", above. If you said M to "OSS sound modules" - then you can get the persistent DMA buffer functionality by passing - the command-line argument "dmabuf=1" to the sound.o module. - - Say Y unless you have 16MB or more RAM or a PCI sound card. - -CONFIG_SOUND_SGALAXY - This module initializes the older non Plug and Play sound galaxy - cards from Aztech. It supports the Waverider Pro 32 - 3D and the - Galaxy Washington 16. - - If you compile the driver into the kernel, you have to add - "sgalaxy=,,,," to the kernel command - line. - -CONFIG_SOUND_AD1816 - Say M here if you have a sound card based on the Analog Devices - AD1816(A) chip. - - If you compile the driver into the kernel, you have to add - "ad1816=,,," to the kernel command line. - -CONFIG_SOUND_OPL3SA1 - Say Y or M if you have a Yamaha OPL3-SA1 sound chip, which is - usually built into motherboards. Read - for details. - - If you compile the driver into the kernel, you have to add - "opl3sa=,,,,," to the kernel - command line. - -CONFIG_SOUND_PAS - Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio - 16 or Logitech SoundMan 16 sound card. Answer N if you have some - other card made by Media Vision or Logitech since those are not - PAS16 compatible. Please read . - It is not necessary to add Sound Blaster support separately; it - is included in PAS support. - - If you compile the driver into the kernel, you have to add - "pas2=,,,,,,, - to the kernel command line. - -CONFIG_PAS_JOYSTICK - Say Y here to enable the Pro Audio Spectrum 16's auxiliary joystick - port. - -CONFIG_SOUND_SB - Answer Y if you have an original Sound Blaster card made by Creative - Labs or a 100% hardware compatible clone (like the Thunderboard or - SM Games). For an unknown card you may answer Y if the card claims - to be Sound Blaster-compatible. - - Please read the file . - - You should also say Y here for cards based on the Avance Logic - ALS-007 and ALS-1X0 chips (read ) and - for cards based on ESS chips (read - and - ). If you have an SB AWE 32 or SB AWE - 64, say Y here and also to "AWE32 synth" below and read - . If you have an IBM Mwave - card, say Y here and read . - - If you compile the driver into the kernel and don't want to use - isapnp, you have to add "sb=,,," to the kernel - command line. - - You can say M here to compile this driver as a module; the module is - called sb.o. - -CONFIG_SOUND_GUS - Say Y here for any type of Gravis Ultrasound card, including the GUS - or GUS MAX. See also for more - information on configuring this card with modules. - - If you compile the driver into the kernel, you have to add - "gus=,,," to the kernel command line. - -CONFIG_SOUND_MPU401 - Be careful with this question. The MPU401 interface is supported by - all sound cards. However, some natively supported cards have their - own driver for MPU401. Enabling this MPU401 option with these cards - will cause a conflict. Also, enabling MPU401 on a system that - doesn't really have a MPU401 could cause some trouble. If your card - was in the list of supported cards, look at the card specific - instructions in the file. It - is safe to answer Y if you have a true MPU401 MIDI interface card. - - If you compile the driver into the kernel, you have to add - "mpu401=," to the kernel command line. - -CONFIG_SOUND_UART6850 - This option enables support for MIDI interfaces based on the 6850 - UART chip. This interface is rarely found on sound cards. It's safe - to answer N to this question. - - If you compile the driver into the kernel, you have to add - "uart6850=," to the kernel command line. - -CONFIG_SOUND_PSS - Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven - ADSP-16 or some other card based on the PSS chipset (AD1848 codec + - ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on - how to compile it into the kernel or as a module see the file - . - - If you compile the driver into the kernel, you have to add - "pss=,,,,," to the kernel - command line. - -CONFIG_PSS_MIXER - Answer Y for Beethoven ADSP-16. You may try to say Y also for other - cards if they have master volume, bass, treble, and you can't - control it under Linux. If you answer N for Beethoven ADSP-16, you - can't control master volume, bass, treble and synth volume. - - If you said M to "PSS support" above, you may enable or disable this - PSS mixer with the module parameter pss_mixer. For more information - see the file . - -CONFIG_PSS_HAVE_BOOT - If you have the DSPxxx.LD file or SYNTH.LD file for you card, say Y - to include this file. Without this file the synth device (OPL) may - not work. - -CONFIG_PSS_BOOT_FILE - Enter the full pathname of your DSPxxx.LD file or SYNTH.LD file, - starting from /. - -CONFIG_SOUND_MSS - Again think carefully before answering Y to this question. It's - safe to answer Y if you have the original Windows Sound System card - made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). Also you may - say Y in case your card is NOT among these: - - ATI Stereo F/X, AdLib, Audio Excell DSP16, Cardinal DSP16, - Ensoniq SoundScape (and compatibles made by Reveal and Spea), - Gravis Ultrasound, Gravis Ultrasound ACE, Gravis Ultrasound Max, - Gravis Ultrasound with 16 bit option, Logitech Sound Man 16, - Logitech SoundMan Games, Logitech SoundMan Wave, MAD16 Pro (OPTi - 82C929), Media Vision Jazz16, MediaTriX AudioTriX Pro, Microsoft - Windows Sound System (MSS/WSS), Mozart (OAK OTI-601), Orchid - SW32, Personal Sound System (PSS), Pro Audio Spectrum 16, Pro - Audio Studio 16, Pro Sonic 16, Roland MPU-401 MIDI interface, - Sound Blaster 1.0, Sound Blaster 16, Sound Blaster 16ASP, Sound - Blaster 2.0, Sound Blaster AWE32, Sound Blaster Pro, TI TM4000M - notebook, ThunderBoard, Turtle Beach Tropez, Yamaha FM - synthesizers (OPL2, OPL3 and OPL4), 6850 UART MIDI Interface. - - For cards having native support in VoxWare, consult the card - specific instructions in . - Some drivers have their own MSS support and saying Y to this option - will cause a conflict. - - If you compile the driver into the kernel, you have to add - "ad1848=,,,[,]" to the kernel command - line. - -CONFIG_SOUND_VWSND - Say Y or M if you have an SGI Visual Workstation and you want to be - able to use its on-board audio. Read - for more info on this driver's - capabilities. - -CONFIG_SOUND_SSCAPE - Answer Y if you have a sound card based on the Ensoniq SoundScape - chipset. Such cards are being manufactured at least by Ensoniq, Spea - and Reveal (Reveal makes also other cards). - - If you compile the driver into the kernel, you have to add - "sscape=,,,," to the kernel command - line. - -CONFIG_SOUND_TRIX - Answer Y if you have the AudioTriX Pro sound card manufactured - by MediaTrix. - -CONFIG_TRIX_HAVE_BOOT - The MediaTrix AudioTrix Pro has an on-board microcontroller which - needs to be initialized by downloading the code from the file - TRXPRO.HEX in the DOS driver directory. If you don't have the - TRXPRO.HEX file handy you may skip this step. However, the SB and - MPU-401 modes of AudioTrix Pro will not work without this file! - -CONFIG_TRIX_BOOT_FILE - Enter the full pathname of your TRXPRO.HEX file, starting from /. - -CONFIG_SOUND_MAD16 - Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi - 82C928 or 82C929 or 82C931) audio interface chip. These chips are - quite common so it's possible that many no-name cards have one of - them. In addition the MAD16 chip is used in some cards made by known - manufacturers such as Turtle Beach (Tropez), Reveal (some models) - and Diamond (latest ones). Note however that the Tropez sound cards - have their own driver; if you have one of those, say N here and Y or - M to "Full support for Turtle Beach WaveFront", below. - - If you compile the driver into the kernel, you have to add - "mad16=,,,,," to the - kernel command line. - - See also and - for more information on setting - these cards up as modules. - -CONFIG_SOUND_WAVEFRONT - Answer Y or M if you have a Tropez Plus, Tropez or Maui sound card - and read the files and - . - -CONFIG_MAD16_OLDCARD - Answer Y (or M) if you have an older card based on the C928 or - Mozart chipset and you want to have MIDI support. If you enable this - option you also need to enable support for Sound Blaster. - -CONFIG_SOUND_CS4232 - Say Y here if you have a card based on the Crystal CS4232 chip set, - which uses its own Plug and Play protocol. - - If you compile the driver into the kernel, you have to add - "cs4232=,,,,," to the kernel - command line. - - See for more information on - configuring this card. - -CONFIG_SOUND_OPL3SA2 - Say Y or M if you have a card based on one of these Yamaha sound - chipsets or the "SAx", which is actually a SA3. Read - for more information on - configuring these cards. - - If you compile the driver into the kernel and do not also - configure in the optional ISA PnP support, you will have to add - "opl3sa2=,,,,," to the kernel - command line. - -CONFIG_SOUND_MAUI - Say Y here if you have a Turtle Beach Wave Front, Maui, or Tropez - sound card. - - If you compile the driver into the kernel, you have to add - "maui=," to the kernel command line. - -CONFIG_MAUI_HAVE_BOOT - Turtle Beach Maui and Tropez sound cards have a microcontroller - which needs to be initialized prior to use. OSWF.MOT is a file - distributed with the card's DOS/Windows drivers. Answer Y if you - have this file. - -CONFIG_MAUI_BOOT_FILE - Enter the full pathname of your OSWF.MOT file, starting from /. - -CONFIG_SOUND_MSNDCLAS - Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or - Monterey (not for the Pinnacle or Fiji). - - See for important information - about this driver. Note that it has been discontinued, but the - Voyetra Turtle Beach knowledge base entry for it is still available - at . - -CONFIG_MSNDCLAS_IO - I/O port address for the MultiSound Classic and related cards. - -CONFIG_MSNDCLAS_IRQ - Interrupt Request line for the MultiSound Classic and related cards. - -CONFIG_MSNDCLAS_MEM - Memory-mapped I/O base address for the MultiSound Classic and - related cards. - -CONFIG_MSNDCLAS_INIT_FILE - The MultiSound cards have two firmware files which are required for - operation, and are not currently included. These files can be - obtained from Turtle Beach. See - for information on how to - obtain this. - -CONFIG_MSNDCLAS_PERM_FILE - The MultiSound cards have two firmware files which are required for - operation, and are not currently included. These files can be - obtained from Turtle Beach. See - for information on how to - obtain this. - -CONFIG_SOUND_MSNDPIN - Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji. - See for important information - about this driver. Note that it has been discontinued, but the - Voyetra Turtle Beach knowledge base entry for it is still available - at . - -CONFIG_MSNDPIN_IDE_IO0 - CD-ROM drive 0 memory-mapped I/O base address for the MultiSound - Pinnacle and Fiji sound cards. - -CONFIG_MSNDPIN_IDE_IO1 - CD-ROM drive 1 memory-mapped I/O base address for the MultiSound - Pinnacle and Fiji sound cards. - -CONFIG_MSNDPIN_IDE_IRQ - Interrupt request number for the IDE CD-ROM interface on the - MultiSound Pinnacle and Fiji sound cards. - -CONFIG_MSNDPIN_IO - Memory-mapped I/O base address for the primary synthesizer on - MultiSound Pinnacle and Fiji sound cards. - -CONFIG_MSNDPIN_MPU_IO - Memory-mapped I/O base address for the Kurzweil daughterboard - synthesizer on MultiSound Pinnacle and Fiji sound cards. - -CONFIG_MSNDPIN_MPU_IRQ - Iinterrupt request number for the Kurzweil daughterboard - synthesizer on MultiSound Pinnacle and Fiji sound cards. - -CONFIG_MSNDPIN_IRQ - Interrupt request line for the primary synthesizer on MultiSound - Pinnacle and Fiji sound cards. - -CONFIG_MSNDPIN_JOYSTICK_IO - Memory-mapped I/O base address for the joystick port on MultiSound - Pinnacle and Fiji sound cards. - -CONFIG_MSNDPIN_MEM - Memory-mapped I/O base address for the primary synthesizer on - MultiSound Pinnacle and Fiji sound cards. - -CONFIG_MSNDPIN_INIT_FILE - The MultiSound cards have two firmware files which are required - for operation, and are not currently included. These files can be - obtained from Turtle Beach. See - for information on how to - obtain this. - -CONFIG_MSNDPIN_PERM_FILE - The MultiSound cards have two firmware files which are required for - operation, and are not currently included. These files can be - obtained from Turtle Beach. See - for information on how to - obtain this. - -CONFIG_MSNDPIN_DIGITAL - If you have the S/PDIF daughter board for the Pinnacle or Fiji, - answer Y here; otherwise, say N. If you have this, you will be able - to play and record from the S/PDIF port (digital signal). See - for information on how to make - use of this capability. - -CONFIG_MSNDPIN_NONPNP - The Pinnacle and Fiji card resources can be configured either with - PnP, or through a configuration port. Say Y here if your card is NOT - in PnP mode. For the Pinnacle, configuration in non-PnP mode allows - use of the IDE and joystick peripherals on the card as well; these - do not show up when the card is in PnP mode. Specifying zero for any - resource of a device will disable the device. If you are running the - card in PnP mode, you must say N here and use isapnptools to - configure the card's resources. - -CONFIG_MSNDPIN_CFG - This is the port which the Pinnacle and Fiji uses to configure the - card's resources when not in PnP mode. If your card is in PnP mode, - then be sure to say N to the previous option, "MSND Pinnacle Non-PnP - Mode". - -CONFIG_MSND_FIFOSIZE - Configures the size of each audio buffer, in kilobytes, for - recording and playing in the MultiSound drivers (both the Classic - and Pinnacle). Larger values reduce the chance of data overruns at - the expense of overall latency. If unsure, use the default. - -CONFIG_SOUND_YM3812 - Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering Y is usually a safe and recommended choice, however some - cards may have software (TSR) FM emulation. Enabling FM support with - these cards may cause trouble (I don't currently know of any such - cards, however). Please read the file - if your card has an OPL3 chip. - - If you compile the driver into the kernel, you have to add - "opl3=" to the kernel command line. - - If unsure, say Y. - -CONFIG_SOUND_ACI_MIXER - ACI (Audio Command Interface) is a protocol used to communicate with - the microcontroller on some sound cards produced by miro and - Cardinal Technologies. The main function of the ACI is to control - the mixer and to get a product identification. - - This VoxWare ACI driver currently supports the ACI functions on the - miroSOUND PCM1-pro, PCM12 and PCM20 radio. On the PCM20 radio, ACI - also controls the radio tuner. This is supported in the video4linux - miropcm20 driver (say M or Y here and go back to "Multimedia - devices" -> "Radio Adapters"). - - This driver is also available as a module and will be called aci.o. - -CONFIG_SOUND_AWE32_SYNTH - Say Y here if you have a Sound Blaster SB32, AWE32-PnP, SB AWE64 or - similar sound card. See , - and the Soundblaster-AWE - mini-HOWTO, available from - for more info. - -CONFIG_SOUND_AEDSP16 - Answer Y if you have a Gallant's Audio Excel DSP 16 card. This - driver supports Audio Excel DSP 16 but not the III nor PnP versions - of this card. - - The Gallant's Audio Excel DSP 16 card can emulate either an SBPro or - a Microsoft Sound System card, so you should have said Y to either - "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support" - or "Microsoft Sound System support", above, and you need to answer - the "MSS emulation" and "SBPro emulation" questions below - accordingly. You should say Y to one and only one of these two - questions. - - Read the file and the head of - as well as - to get more information - about this driver and its configuration. - -CONFIG_AEDSP16_SBPRO - Answer Y if you want your audio card to emulate Sound Blaster Pro. - You should then say Y to "100% Sound Blaster compatibles - (SB16/32/64, ESS, Jazz16) support" and N to "Audio Excel DSP 16 (MSS - emulation)". - - If you compile the driver into the kernel, you have to add - "aedsp16=,,,,," to the kernel - command line. - -CONFIG_AEDSP16_MSS - Answer Y if you want your audio card to emulate Microsoft Sound - System. You should then say Y to "Microsoft Sound System support" - and say N to "Audio Excel DSP 16 (SBPro emulation)". - -CONFIG_SC6600 - The SC6600 is the new version of DSP mounted on the Audio Excel DSP - 16 cards. Find in the manual the FCC ID of your audio card and - answer Y if you have an SC6600 DSP. - -CONFIG_SC6600_JOY - Say Y here in order to use the joystick interface of the Audio Excel - DSP 16 card. - -CONFIG_SC6600_CDROMBASE - Base I/O port address for the CD-ROM interface of the Audio Excel - DSP 16 card. - -CONFIG_AEDSP16_MPU401 - Answer Y if you want your audio card to emulate the MPU-401 midi - interface. You should then also say Y to "MPU-401 support". - - Note that the I/O base for MPU-401 support of aedsp16 is the same - you have selected for "MPU-401 support". If you are using this - driver as a module you have to specify the MPU I/O base address with - the parameter 'mpu_base=0xNNN'. - -CONFIG_SOUND_CMPCI - Say Y or M if you have a PCI sound card using the CMI8338 - or the CMI8378 chipset. Data on these chips are available at - . - - A userspace utility to control some internal registers of these - chips is available at - . - -CONFIG_SOUND_CMPCI_CM8738 - Say Y or M if you have a PCI sound card using the CMI8338 - or the CMI8378 chipset. Data on this chip is available at - . - - A userspace utility to control some internal registers of these - chips is available at - . - -CONFIG_SOUND_CMPCI_JOYSTICK - Say here in order to enable the joystick port on a sound crd using - the CMI8338 or the CMI8738 chipset. Data on these chips are - available at . - -CONFIG_SOUND_CMPCI_SPEAKERS - Specify the number of speaker channels you want the card to drive, - as an integer. - -CONFIG_SOUND_CMPCI_SPDIFLOOP - Enable loopback from SPDIF in to SPDIF out. For discussion, see - "The 8738 Audio SPDIF In/Out Technical Data" on the technical - support page at . - - A userspace utility to control even more internal registers of these - chips is available at - . - This package will among other things help you enable SPDIF - out/in/loop/monitor. - -CONFIG_SOUND_EMU10K1 - Say Y or M if you have a PCI sound card using the EMU10K1 chipset, - such as the Creative SBLive!, SB PCI512 or Emu-APS. - - For more information on this driver and the degree of support for the - different card models please check . - - It is now possible to load dsp microcode patches into the EMU10K1 - chip. These patches are used to implement real time sound - processing effects which include for example: signal routing, - bass/treble control, AC3 passthrough, ... - Userspace tools to create new patches and load/unload them can be - found at . - -CONFIG_MIDI_EMU10K1 - Say Y if you want to be able to use the OSS /dev/sequencer - interface. This code is still experimental. - -CONFIG_SOUND_FUSION - This module drives the Crystal SoundFusion devices (CS4280/46xx - series) when wired as native sound drivers with AC97 codecs. If - this driver does not work try the CS4232 driver. - -CONFIG_SOUND_ES1370 - Say Y or M if you have a PCI sound card utilizing the Ensoniq - ES1370 chipset, such as Ensoniq's AudioPCI (non-97). To find - out if your sound card uses an ES1370 without removing your - computer's cover, use lspci -n and look for the PCI ID - 1274:5000. Since Ensoniq was bought by Creative Labs, - Sound Blaster 64/PCI models are either ES1370 or ES1371 based. - This driver differs slightly from OSS/Free, so PLEASE READ - . - -CONFIG_SOUND_ES1371 - Say Y or M if you have a PCI sound card utilizing the Ensoniq - ES1371 chipset, such as Ensoniq's AudioPCI97. To find out if - your sound card uses an ES1371 without removing your computer's - cover, use lspci -n and look for the PCI ID 1274:1371. Since - Ensoniq was bought by Creative Labs, Sound Blaster 64/PCI - models are either ES1370 or ES1371 based. This driver differs - slightly from OSS/Free, so PLEASE READ - . - -CONFIG_SOUND_ESSSOLO1 - Say Y or M if you have a PCI sound card utilizing the ESS Technology - Solo1 chip. To find out if your sound card uses a - Solo1 chip without removing your computer's cover, use - lspci -n and look for the PCI ID 125D:1969. This driver - differs slightly from OSS/Free, so PLEASE READ - . - -CONFIG_SOUND_SONICVIBES - Say Y or M if you have a PCI sound card utilizing the S3 - SonicVibes chipset. To find out if your sound card uses a - SonicVibes chip without removing your computer's cover, use - lspci -n and look for the PCI ID 5333:CA00. This driver - differs slightly from OSS/Free, so PLEASE READ - . - -CONFIG_SOUND_TRIDENT - Say Y or M if you have a PCI sound card utilizing the Trident - 4DWave-DX/NX chipset or your mother board chipset has SiS 7018 - or ALi 5451 built-in. The SiS 7018 PCI Audio Core is embedded - in SiS960 Super South Bridge and SiS540/630 Single Chipset. - The ALi 5451 PCI Audio Core is embedded in ALi M1535, M1535D, - M1535+ or M1535D+ South Bridge. - - Use lspci -n to find out if your sound card or chipset uses - Trident 4DWave or SiS 7018. PCI ID 1023:2000 or 1023:2001 stands - for Trident 4Dwave. PCI ID 1039:7018 stands for SiS7018. PCI ID - 10B9:5451 stands for ALi5451. - - This driver supports S/PDIF in/out (record/playback) for ALi 5451 - embedded in ALi M1535+ and M1535D+. Note that they aren't all - enabled by default; you can enable them by saying Y to "/proc file - system support" and "Sysctl support", and after the /proc file - system has been mounted, executing the command - - command what is enabled - - echo 0>/proc/ALi5451 pcm out is also set to S/PDIF out. (Default). - - echo 1>/proc/ALi5451 use S/PDIF out to output pcm data. - - echo 2>/proc/ALi5451 use S/PDIF out to output non-pcm data. - (AC3...). - - echo 3>/proc/ALi5451 record from Ac97 in(MIC, Line in...). - (Default). - - echo 4>/proc/ALi5451 no matter Ac97 settings, record from S/PDIF - in. - - - This driver differs slightly from OSS/Free, so PLEASE READ the - comments at the top of . - -CONFIG_SOUND_WAVEARTIST - Say Y here to include support for the Rockwell WaveArtist sound - system. This driver is mainly for the NetWinder. - -CONFIG_SOUND_VIA82CXXX - Say Y here to include support for the audio codec found on VIA - 82Cxxx-based chips. Typically these are built into a motherboard. - - DO NOT select Sound Blaster or Adlib with this driver, unless - you have a Sound Blaster or Adlib card in addition to your VIA - audio chip. - -CONFIG_MIDI_VIA82CXXX - Answer Y to use the MIDI interface of the Via686. You may need to - enable this in the BIOS before it will work. This is for connection - to external MIDI hardware, and is not required for software playback - of MIDI files. - -CONFIG_SOUND_NM256 - Say M here to include audio support for the NeoMagic 256AV/256ZX - chipsets. These are the audio chipsets found in the Sony - Z505S/SX/DX, some Sony F-series, and the Dell Latitude CPi and CPt - laptops. It includes support for an AC97-compatible mixer and an - apparently proprietary sound engine. - - See for further information. - -CONFIG_SOUND_MAESTRO - Say Y or M if you have a sound system driven by ESS's Maestro line - of PCI sound chips. These include the Maestro 1, Maestro 2, and - Maestro 2E. See for more - details. - -CONFIG_SOUND_MAESTRO3 - Say Y or M if you have a sound system driven by ESS's Maestro 3 - PCI sound chip. - -CONFIG_SOUND_ADLIB - Includes ASB 64 4D. Information on programming AdLib cards is - available at . - -CONFIG_SOUND_CS4281 - Picture and feature list at - . - -CONFIG_SOUND_GUS16 - Support for Gravis Ulstrasound (GUS) cards (other than the GUS), - sampling at 16-bit width. - -CONFIG_SOUND_GUSMAX - Support for Gravis Ulstrasound MAX. - -CONFIG_SOUND_ICH - Support for integral audio in Intel's I/O Controller Hub (ICH) - chipset, as used on the 810/820/840 motherboards. - -CONFIG_SOUND_TRACEINIT - Verbose soundcard initialization -- affects the format of autoprobe - and initialization messages at boot time. - -CONFIG_SOUND_TVMIXER - Support for audio mixer facilities on the BT848 TV frame-grabber - card. - -CONFIG_SOUND_VIDC - 16-bit support for the VIDC onboard sound hardware found on Acorn - machines. - -CONFIG_SOUND_VMIDI - Support for MIDI loopback on port 1 or 2. - -CONFIG_SOUND_YMFPCI - Support for Yamaha cards including the YMF711, YMF715, YMF718, - YMF719, YMF724, Waveforce 192XG, and Waveforce 192 Digital. - -CONFIG_SOUND_YMFPCI_LEGACY - Support for YMF7xx PCI cards emulating an MP401. - -CONFIG_SOUND_RME96XX - Say Y or M if you have a Hammerfall, Hammerfall light or Hammerfall - DSP card from RME. - -CONFIG_SOUND_BT878 - Audio DMA support for bt878 based grabber boards. As you might have - already noticed, bt878 is listed with two functions in /proc/pci. - Function 0 does the video stuff (bt848 compatible), function 1 does - the same for audio data. This is a driver for the audio part of - the chip. If you say 'Y' here you get a oss-compatible dsp device - where you can record from. If you want just watch TV you probably - don't need this driver as most TV cards handle sound with a short - cable from the TV card to your sound card's line-in. - - This driver is available as a module called btaudio.o ( = code - which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read . - diff -Nru a/drivers/sound/Config.in b/drivers/sound/Config.in --- a/drivers/sound/Config.in Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,213 +0,0 @@ -# drivers/sound/Config.in -# -# 18 Apr 1998, Michael Elizabeth Chastain, -# More hacking for modularisation. -# - -# Prompt user for primary drivers. - -dep_tristate ' BT878 audio dma' CONFIG_SOUND_BT878 $CONFIG_SOUND -dep_tristate ' C-Media PCI (CMI8338/8738)' CONFIG_SOUND_CMPCI $CONFIG_SOUND $CONFIG_PCI -if [ "$CONFIG_SOUND_CMPCI" = "y" -o "$CONFIG_SOUND_CMPCI" = "m" ]; then - bool ' Enable legacy FM' CONFIG_SOUND_CMPCI_FM - if [ "$CONFIG_SOUND_CMPCI_FM" = "y" ]; then - define_hex CONFIG_SOUND_CMPCI_FMIO 388 - hex ' FM I/O 388, 3C8, 3E0, 3E8' CONFIG_SOUND_CMPCI_FMIO 388 - fi - bool ' Enable legacy MPU-401' CONFIG_SOUND_CMPCI_MIDI - if [ "$CONFIG_SOUND_CMPCI_MIDI" = "y" ]; then - hex ' MPU-401 I/O 330, 320, 310, 300' CONFIG_SOUND_CMPCI_MPUIO 330 - fi - bool ' Enable joystick' CONFIG_SOUND_CMPCI_JOYSTICK - bool ' Support CMI8738 based audio cards' CONFIG_SOUND_CMPCI_CM8738 - if [ "$CONFIG_SOUND_CMPCI_CM8738" = "y" ]; then - bool ' Inverse S/PDIF in for CMI8738' CONFIG_SOUND_CMPCI_SPDIFINVERSE - bool ' Enable S/PDIF loop for CMI8738' CONFIG_SOUND_CMPCI_SPDIFLOOP - int ' Number of speakers 2, 4, 5, 6' CONFIG_SOUND_CMPCI_SPEAKERS 2 - if [ "$CONFIG_SOUND_CMPCI_SPEAKERS" != "2" ]; then - bool ' Use Line-in as Read-out' CONFIG_SOUND_CMPCI_LINE_REAR - bool ' Use Line-in as Bass' CONFIG_SOUND_CMPCI_LINE_BASS - fi - fi -fi -dep_tristate ' Creative SBLive! (EMU10K1)' CONFIG_SOUND_EMU10K1 $CONFIG_SOUND $CONFIG_PCI -dep_mbool ' Creative SBLive! MIDI' CONFIG_MIDI_EMU10K1 $CONFIG_SOUND_EMU10K1 $CONFIG_EXPERIMENTAL -dep_tristate ' Crystal SoundFusion (CS4280/461x)' CONFIG_SOUND_FUSION $CONFIG_SOUND -dep_tristate ' Crystal Sound CS4281' CONFIG_SOUND_CS4281 $CONFIG_SOUND -dep_tristate ' Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND $CONFIG_PCI $CONFIG_SOUND_GAMEPORT -dep_tristate ' Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ES1371 $CONFIG_SOUND $CONFIG_PCI $CONFIG_SOUND_GAMEPORT -dep_tristate ' ESS Technology Solo1' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND $CONFIG_SOUND_GAMEPORT -dep_tristate ' ESS Maestro, Maestro2, Maestro2E driver' CONFIG_SOUND_MAESTRO $CONFIG_SOUND -dep_tristate ' ESS Maestro3/Allegro driver (EXPERIMENTAL)' CONFIG_SOUND_MAESTRO3 $CONFIG_SOUND $CONFIG_PCI $CONFIG_EXPERIMENTAL -dep_tristate ' Intel ICH (i8xx) audio support' CONFIG_SOUND_ICH $CONFIG_PCI -dep_tristate ' RME Hammerfall (RME96XX) support' CONFIG_SOUND_RME96XX $CONFIG_SOUND $CONFIG_PCI $CONFIG_EXPERIMENTAL -dep_tristate ' S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND $CONFIG_SOUND_GAMEPORT -if [ "$CONFIG_VISWS" = "y" ]; then - dep_tristate ' SGI Visual Workstation Sound' CONFIG_SOUND_VWSND $CONFIG_SOUND -fi -if [ "$CONFIG_DDB5477" = "y" ]; then - dep_tristate ' NEC Vrc5477 AC97 sound' CONFIG_SOUND_VRC5477 $CONFIG_SOUND -fi -dep_tristate ' Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core' CONFIG_SOUND_TRIDENT $CONFIG_SOUND - -dep_tristate ' Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND -if [ "$CONFIG_SOUND_MSNDCLAS" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "m" ]; then - if [ "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then - comment ' Compiled-in MSND Classic support requires firmware during compilation.' - define_bool CONFIG_MSNDCLAS_HAVE_BOOT y - else - define_bool CONFIG_MSNDCLAS_HAVE_BOOT n - fi - string 'Full pathname of MSNDINIT.BIN firmware file' CONFIG_MSNDCLAS_INIT_FILE "/etc/sound/msndinit.bin" - string 'Full pathname of MSNDPERM.BIN firmware file' CONFIG_MSNDCLAS_PERM_FILE "/etc/sound/msndperm.bin" -fi -if [ "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then - int ' MSND Classic IRQ 5, 7, 9, 10, 11, 12' CONFIG_MSNDCLAS_IRQ 5 - hex ' MSND Classic memory B0000, C8000, D0000, D8000, E0000, E8000' CONFIG_MSNDCLAS_MEM D0000 - hex ' MSND Classic I/O 210, 220, 230, 240, 250, 260, 290, 3E0' CONFIG_MSNDCLAS_IO 290 -fi - -dep_tristate ' Support for Turtle Beach MultiSound Pinnacle, Fiji' CONFIG_SOUND_MSNDPIN $CONFIG_SOUND -if [ "$CONFIG_SOUND_MSNDPIN" = "y" -o "$CONFIG_SOUND_MSNDPIN" = "m" ]; then - if [ "$CONFIG_SOUND_MSNDPIN" = "y" ]; then - comment 'Compiled-in MSND Pinnacle support requires firmware during compilation.' - define_bool CONFIG_MSNDPIN_HAVE_BOOT y - else - define_bool CONFIG_MSNDPIN_HAVE_BOOT n - fi - string ' Full pathname of PNDSPINI.BIN firmware file' CONFIG_MSNDPIN_INIT_FILE "/etc/sound/pndspini.bin" - string ' Full pathname of PNDSPERM.BIN firmware file' CONFIG_MSNDPIN_PERM_FILE "/etc/sound/pndsperm.bin" -fi -if [ "$CONFIG_SOUND_MSNDPIN" = "y" ]; then - int ' MSND Pinnacle IRQ 5, 7, 9, 10, 11, 12' CONFIG_MSNDPIN_IRQ 5 - hex ' MSND Pinnacle memory B0000, C8000, D0000, D8000, E0000, E8000' CONFIG_MSNDPIN_MEM D0000 - hex 'MSND Pinnacle I/O 210, 220, 230, 240, 250, 260, 290, 3E0' CONFIG_MSNDPIN_IO 290 - bool ' MSND Pinnacle has S/PDIF I/O' CONFIG_MSNDPIN_DIGITAL - bool ' MSND Pinnacle non-PnP Mode' CONFIG_MSNDPIN_NONPNP - if [ "$CONFIG_MSNDPIN_NONPNP" = "y" ]; then - comment 'MSND Pinnacle DSP section will be configured to above parameters.' - hex 'MSND Pinnacle config port 250,260,270' CONFIG_MSNDPIN_CFG 250 - comment 'Pinnacle-specific Device Configuration (0 disables)' - hex 'MSND Pinnacle MPU I/O (e.g. 330)' CONFIG_MSNDPIN_MPU_IO 0 - int 'MSND Pinnacle MPU IRQ (e.g. 9)' CONFIG_MSNDPIN_MPU_IRQ 0 - hex 'MSND Pinnacle IDE I/O 0 (e.g. 170)' CONFIG_MSNDPIN_IDE_IO0 0 - hex 'MSND Pinnacle IDE I/O 1 (e.g. 376)' CONFIG_MSNDPIN_IDE_IO1 0 - int 'MSND Pinnacle IDE IRQ (e.g. 15)' CONFIG_MSNDPIN_IDE_IRQ 0 - hex 'MSND Pinnacle joystick I/O (e.g. 200)' CONFIG_MSNDPIN_JOYSTICK_IO 0 - fi -fi -if [ "$CONFIG_SOUND_MSNDPIN" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then - int 'MSND buffer size (kB)' CONFIG_MSND_FIFOSIZE 128 -fi - -dep_tristate ' VIA 82C686 Audio Codec' CONFIG_SOUND_VIA82CXXX $CONFIG_PCI -dep_mbool ' VIA 82C686 MIDI' CONFIG_MIDI_VIA82CXXX $CONFIG_SOUND_VIA82CXXX - -dep_tristate ' OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND - -if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then - bool ' Verbose initialisation' CONFIG_SOUND_TRACEINIT - bool ' Persistent DMA buffers' CONFIG_SOUND_DMAP - - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - dep_tristate ' AD1816(A) based cards (EXPERIMENTAL)' CONFIG_SOUND_AD1816 $CONFIG_SOUND_OSS - fi - dep_tristate ' Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_SGALAXY $CONFIG_SOUND_OSS - dep_tristate ' Adlib Cards' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS - dep_tristate ' ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20)' CONFIG_SOUND_ACI_MIXER $CONFIG_SOUND_OSS - dep_tristate ' Crystal CS4232 based (PnP) cards' CONFIG_SOUND_CS4232 $CONFIG_SOUND_OSS - dep_tristate ' Ensoniq SoundScape support' CONFIG_SOUND_SSCAPE $CONFIG_SOUND_OSS - dep_tristate ' Gravis Ultrasound support' CONFIG_SOUND_GUS $CONFIG_SOUND_OSS - if [ "$CONFIG_SOUND_GUS" != "n" ]; then - bool ' 16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_SOUND_GUS16 - bool ' GUS MAX support' CONFIG_SOUND_GUSMAX - fi - dep_tristate ' Loopback MIDI device support' CONFIG_SOUND_VMIDI $CONFIG_SOUND_OSS - dep_tristate ' MediaTrix AudioTrix Pro support' CONFIG_SOUND_TRIX $CONFIG_SOUND_OSS - if [ "$CONFIG_SOUND_TRIX" = "y" ]; then - bool ' Have TRXPRO.HEX firmware file' CONFIG_TRIX_HAVE_BOOT - if [ "$CONFIG_TRIX_HAVE_BOOT" = "y" ]; then - string ' Full pathname of TRXPRO.HEX firmware file' CONFIG_TRIX_BOOT_FILE /etc/sound/trxpro.hex - fi - fi - - dep_tristate ' Microsoft Sound System support' CONFIG_SOUND_MSS $CONFIG_SOUND_OSS - dep_tristate ' MPU-401 support (NOT for SB16)' CONFIG_SOUND_MPU401 $CONFIG_SOUND_OSS - dep_tristate ' NM256AV/NM256ZX audio support' CONFIG_SOUND_NM256 $CONFIG_SOUND_OSS - dep_tristate ' OPTi MAD16 and/or Mozart based cards' CONFIG_SOUND_MAD16 $CONFIG_SOUND_OSS - if [ "$CONFIG_SOUND_MAD16" = "y" -o "$CONFIG_SOUND_MAD16" = "m" ]; then - bool ' Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD - fi - dep_tristate ' ProAudioSpectrum 16 support' CONFIG_SOUND_PAS $CONFIG_SOUND_OSS - dep_bool ' Enable PAS16 joystick port' CONFIG_PAS_JOYSTICK $CONFIG_SOUND_PAS - - dep_tristate ' PSS (AD1848, ADSP-2115, ESC614) support' CONFIG_SOUND_PSS $CONFIG_SOUND_OSS - if [ "$CONFIG_SOUND_PSS" = "y" -o "$CONFIG_SOUND_PSS" = "m" ]; then - bool ' Enable PSS mixer (Beethoven ADSP-16 and other compatibile)' CONFIG_PSS_MIXER - bool ' Have DSPxxx.LD firmware file' CONFIG_PSS_HAVE_BOOT - if [ "$CONFIG_PSS_HAVE_BOOT" = "y" ]; then - string ' Full pathname of DSPxxx.LD firmware file' CONFIG_PSS_BOOT_FILE /etc/sound/dsp001.ld - fi - fi - - dep_tristate ' 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SOUND_SB $CONFIG_SOUND_OSS - dep_tristate ' AWE32 synth' CONFIG_SOUND_AWE32_SYNTH $CONFIG_SOUND_OSS - dep_tristate ' Full support for Turtle Beach WaveFront (Tropez Plus, Tropez, Maui) synth/soundcards' CONFIG_SOUND_WAVEFRONT $CONFIG_SOUND_OSS m - dep_tristate ' Limited support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_SOUND_MAUI $CONFIG_SOUND_OSS - if [ "$CONFIG_SOUND_MAUI" = "y" ]; then - bool ' Have OSWF.MOT firmware file' CONFIG_MAUI_HAVE_BOOT - if [ "$CONFIG_MAUI_HAVE_BOOT" = "y" ]; then - string ' Full pathname of OSWF.MOT firmware file' CONFIG_MAUI_BOOT_FILE /etc/sound/oswf.mot - fi - fi - - dep_tristate ' Yamaha FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_YM3812 $CONFIG_SOUND_OSS - dep_tristate ' Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_OPL3SA1 $CONFIG_SOUND_OSS - dep_tristate ' Yamaha OPL3-SA2 and SA3 based PnP cards' CONFIG_SOUND_OPL3SA2 $CONFIG_SOUND_OSS - dep_tristate ' Yamaha YMF7xx PCI audio (native mode)' CONFIG_SOUND_YMFPCI $CONFIG_SOUND_OSS $CONFIG_PCI - dep_mbool ' Yamaha PCI legacy ports support' CONFIG_SOUND_YMFPCI_LEGACY $CONFIG_SOUND_YMFPCI - dep_tristate ' 6850 UART support' CONFIG_SOUND_UART6850 $CONFIG_SOUND_OSS - - dep_tristate ' Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_SOUND_AEDSP16 $CONFIG_SOUND_OSS - if [ "$CONFIG_SOUND_AEDSP16" = "y" -o "$CONFIG_SOUND_AEDSP16" = "m" ]; then - bool ' SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600 - if [ "$CONFIG_SC6600" = "y" ]; then - bool ' Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY - int ' SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' CONFIG_SC6600_CDROM 4 - hex ' SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0 - fi - if [ "$CONFIG_SOUND_SB" = "y" -o "$CONFIG_SOUND_SB" = "m" ]; then - if [ "$CONFIG_AEDSP16_MSS" != "y" ]; then - bool ' Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO - fi - fi - if [ "$CONFIG_SOUND_MSS" = "y" -o "$CONFIG_SOUND_MSS" = "m" ]; then - if [ "$CONFIG_AEDSP16_SBPRO" != "y" ]; then - bool ' Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS - fi - fi - if [ "$CONFIG_SOUND_MPU401" = "y" -o "$CONFIG_SOUND_MPU401" = "m" ]; then - bool ' Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401 - fi - fi - - if [ "$CONFIG_ARM" = "y" ]; then - if [ "$CONFIG_ARCH_ACORN" = "y" -o "$CONFIG_ARCH_CLPS7500" = "y" ]; then - dep_tristate ' VIDC 16-bit sound' CONFIG_SOUND_VIDC $CONFIG_SOUND_OSS - fi - dep_tristate ' Netwinder WaveArtist' CONFIG_SOUND_WAVEARTIST $CONFIG_SOUND_OSS $CONFIG_ARCH_NETWINDER - fi - -fi - -dep_tristate ' TV card (bt848) mixer support' CONFIG_SOUND_TVMIXER $CONFIG_SOUND $CONFIG_I2C - -# A cross directory dependence. The sound modules will need gameport.o compiled in, -# but it resides in the drivers/char/joystick directory. This define_tristate takes -# care of that. --Vojtech - -if [ "$CONFIG_INPUT_GAMEPORT" != "n" ]; then - if [ "$CONFIG_SOUND_ESSSOLO1" = "y" -o "$CONFIG_SOUND_ES1370" = "y" -o "$CONFIG_SOUND_ES1371" = "y" -o "$CONFIG_SOUND_SONICVIBES" = "y" ]; then - define_tristate CONFIG_INPUT_GAMEPORT y - fi -fi diff -Nru a/drivers/sound/Hwmcode.h b/drivers/sound/Hwmcode.h --- a/drivers/sound/Hwmcode.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,804 +0,0 @@ -//============================================================================= -// Copyright (c) 1997 Yamaha Corporation. All Rights Reserved. -// -// Title: -// hwmcode.c -// Desc: -// micro-code for CTRL & DSP -// HISTORY: -// April 03, 1997: 1st try by M. Mukojima -//============================================================================= -#define YDSXG_DSPLENGTH 0x0080 -#define YDSXG_CTRLLENGTH 0x3000 - - -static unsigned long int gdwDSPCode[YDSXG_DSPLENGTH >> 2] = { - 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, - 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, - 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, - 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000 -}; - - -// -------------------------------------------- -// DS-1E Controller InstructionRAM Code -// 1999/06/21 -// Buf441 slot is Enabled. -// -------------------------------------------- -// 04/09?@creat -// 04/12 stop nise fix -// 06/21?@WorkingOff timming -static unsigned long gdwCtrl1eCode[YDSXG_CTRLLENGTH >> 2] = { - 0x000007, 0x240007, 0x0C0007, 0x1C0007, - 0x060007, 0x700002, 0x000020, 0x030040, - 0x007104, 0x004286, 0x030040, 0x000F0D, - 0x000810, 0x20043A, 0x000282, 0x00020D, - 0x000810, 0x20043A, 0x001282, 0x200E82, - 0x00800D, 0x000810, 0x20043A, 0x001A82, - 0x03460D, 0x000810, 0x10043A, 0x02EC0D, - 0x000810, 0x18043A, 0x00010D, 0x020015, - 0x0000FD, 0x000020, 0x038860, 0x039060, - 0x038060, 0x038040, 0x038040, 0x038040, - 0x018040, 0x000A7D, 0x038040, 0x038040, - 0x018040, 0x200402, 0x000882, 0x08001A, - 0x000904, 0x017186, 0x000007, 0x260007, - 0x400007, 0x000007, 0x03258D, 0x000810, - 0x18043A, 0x260007, 0x284402, 0x00087D, - 0x018042, 0x00160A, 0x05A206, 0x000007, - 0x440007, 0x00230D, 0x000810, 0x08043A, - 0x22FA06, 0x000007, 0x0007FD, 0x018042, - 0x08000A, 0x000904, 0x02AB86, 0x000195, - 0x090D04, 0x000007, 0x000820, 0x0000F5, - 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, - 0x018040, 0x000A7D, 0x038042, 0x13804A, - 0x18000A, 0x001820, 0x059060, 0x058860, - 0x018040, 0x0000FD, 0x018042, 0x70000A, - 0x000115, 0x071144, 0x033B86, 0x030000, - 0x007020, 0x036206, 0x018040, 0x00360D, - 0x000810, 0x08043A, 0x232206, 0x000007, - 0x02EC0D, 0x000810, 0x18043A, 0x019A06, - 0x000007, 0x240007, 0x000F8D, 0x000810, - 0x00163A, 0x002402, 0x005C02, 0x0028FD, - 0x000020, 0x018040, 0x08000D, 0x000815, - 0x510984, 0x000007, 0x00004D, 0x000E5D, - 0x000E02, 0x00430D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x00008D, 0x000924, - 0x000F02, 0x00470D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x480480, 0x001210, - 0x28043A, 0x00778D, 0x000810, 0x280C3A, - 0x00068D, 0x000810, 0x28143A, 0x284402, - 0x03258D, 0x000810, 0x18043A, 0x07FF8D, - 0x000820, 0x0002FD, 0x018040, 0x260007, - 0x200007, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x051286, 0x000007, 0x240007, - 0x02EC0D, 0x000810, 0x18043A, 0x00387D, - 0x018042, 0x08000A, 0x001015, 0x010984, - 0x019B86, 0x000007, 0x01B206, 0x000007, - 0x0008FD, 0x018042, 0x18000A, 0x001904, - 0x22B886, 0x280007, 0x001810, 0x28043A, - 0x280C02, 0x00000D, 0x000810, 0x28143A, - 0x08808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00020D, 0x189904, 0x000007, - 0x00402D, 0x0000BD, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x065A86, 0x000007, - 0x000100, 0x000A20, 0x00047D, 0x018040, - 0x018042, 0x20000A, 0x003015, 0x012144, - 0x036186, 0x000007, 0x002104, 0x036186, - 0x000007, 0x000F8D, 0x000810, 0x280C3A, - 0x023944, 0x07C986, 0x000007, 0x001810, - 0x28043A, 0x08810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x002810, 0x78003A, - 0x00788D, 0x000810, 0x08043A, 0x2A1206, - 0x000007, 0x00400D, 0x001015, 0x189904, - 0x292904, 0x393904, 0x000007, 0x070206, - 0x000007, 0x0004F5, 0x00007D, 0x000020, - 0x00008D, 0x010860, 0x018040, 0x00047D, - 0x038042, 0x21804A, 0x18000A, 0x021944, - 0x229086, 0x000007, 0x004075, 0x71F104, - 0x000007, 0x010042, 0x28000A, 0x002904, - 0x225886, 0x000007, 0x003C0D, 0x30A904, - 0x000007, 0x00077D, 0x018042, 0x08000A, - 0x000904, 0x08DA86, 0x00057D, 0x002820, - 0x03B060, 0x08F206, 0x018040, 0x003020, - 0x03A860, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x08FA86, 0x000007, - 0x00057D, 0x018042, 0x28040A, 0x000E8D, - 0x000810, 0x280C3A, 0x00000D, 0x000810, - 0x28143A, 0x09000D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x003DFD, 0x000020, - 0x018040, 0x00107D, 0x009D8D, 0x000810, - 0x08043A, 0x2A1206, 0x000007, 0x000815, - 0x08001A, 0x010984, 0x0A5186, 0x00137D, - 0x200500, 0x280F20, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x038A60, 0x018040, 0x00107D, 0x018042, - 0x08000A, 0x000215, 0x010984, 0x3A8186, - 0x000007, 0x007FBD, 0x383DC4, 0x000007, - 0x001A7D, 0x001375, 0x018042, 0x09004A, - 0x10000A, 0x0B8D04, 0x139504, 0x000007, - 0x000820, 0x019060, 0x001104, 0x225886, - 0x010040, 0x0017FD, 0x018042, 0x08000A, - 0x000904, 0x225A86, 0x000007, 0x00197D, - 0x038042, 0x09804A, 0x10000A, 0x000924, - 0x001664, 0x0011FD, 0x038042, 0x2B804A, - 0x19804A, 0x00008D, 0x218944, 0x000007, - 0x002244, 0x0C1986, 0x000007, 0x001A64, - 0x002A24, 0x00197D, 0x080102, 0x100122, - 0x000820, 0x039060, 0x018040, 0x003DFD, - 0x00008D, 0x000820, 0x018040, 0x001375, - 0x001A7D, 0x010042, 0x09804A, 0x10000A, - 0x00021D, 0x0189E4, 0x2992E4, 0x309144, - 0x000007, 0x00060D, 0x000A15, 0x000C1D, - 0x001025, 0x00A9E4, 0x012BE4, 0x000464, - 0x01B3E4, 0x0232E4, 0x000464, 0x000464, - 0x000464, 0x000464, 0x00040D, 0x08B1C4, - 0x000007, 0x000820, 0x000BF5, 0x030040, - 0x00197D, 0x038042, 0x09804A, 0x000A24, - 0x08000A, 0x080E64, 0x000007, 0x100122, - 0x000820, 0x031060, 0x010040, 0x0064AC, - 0x00027D, 0x000020, 0x018040, 0x00107D, - 0x018042, 0x0011FD, 0x3B804A, 0x09804A, - 0x20000A, 0x000095, 0x1A1144, 0x00A144, - 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, - 0x0018FD, 0x018042, 0x0010FD, 0x09804A, - 0x28000A, 0x000095, 0x010924, 0x002A64, - 0x0E4986, 0x000007, 0x002904, 0x0E5A86, - 0x000007, 0x0E6206, 0x080002, 0x00008D, - 0x00387D, 0x000820, 0x018040, 0x00127D, - 0x018042, 0x10000A, 0x003904, 0x0F0986, - 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, - 0x000025, 0x0FB206, 0x00002D, 0x000015, - 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, - 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, - 0x400025, 0x00008D, 0x110944, 0x000007, - 0x00018D, 0x109504, 0x000007, 0x009164, - 0x000424, 0x000424, 0x000424, 0x100102, - 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, - 0x00018D, 0x00042D, 0x00008D, 0x109504, - 0x000007, 0x00020D, 0x109184, 0x000007, - 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, - 0x018040, 0x003BFD, 0x001020, 0x03A860, - 0x000815, 0x313184, 0x212184, 0x000007, - 0x03B060, 0x03A060, 0x018040, 0x0022FD, - 0x000095, 0x010924, 0x000424, 0x000424, - 0x001264, 0x100102, 0x000820, 0x039060, - 0x018040, 0x001924, 0x010F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x000424, 0x000424, - 0x00117D, 0x018042, 0x08000A, 0x000A24, - 0x280502, 0x280C02, 0x09800D, 0x000820, - 0x0002FD, 0x018040, 0x200007, 0x0022FD, - 0x018042, 0x08000A, 0x000095, 0x280DC4, - 0x011924, 0x00197D, 0x018042, 0x0011FD, - 0x09804A, 0x10000A, 0x0000B5, 0x113144, - 0x0A8D04, 0x000007, 0x080A44, 0x129504, - 0x000007, 0x0023FD, 0x001020, 0x038040, - 0x101244, 0x000007, 0x000820, 0x039060, - 0x018040, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x123286, 0x000007, 0x003BFD, - 0x000100, 0x000A10, 0x0B807A, 0x13804A, - 0x090984, 0x000007, 0x000095, 0x013D04, - 0x12B886, 0x10000A, 0x100002, 0x090984, - 0x000007, 0x038042, 0x11804A, 0x090D04, - 0x000007, 0x10000A, 0x090D84, 0x000007, - 0x00257D, 0x000820, 0x018040, 0x00010D, - 0x000810, 0x28143A, 0x00127D, 0x018042, - 0x20000A, 0x00197D, 0x018042, 0x00117D, - 0x31804A, 0x10000A, 0x003124, 0x013B8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x300102, - 0x003124, 0x000424, 0x000424, 0x001224, - 0x280502, 0x001A4C, 0x143986, 0x700002, - 0x00002D, 0x030000, 0x00387D, 0x018042, - 0x10000A, 0x146206, 0x002124, 0x0000AD, - 0x100002, 0x00010D, 0x000924, 0x006B24, - 0x014A0D, 0x00397D, 0x000820, 0x058040, - 0x038042, 0x09844A, 0x000606, 0x08040A, - 0x003264, 0x00008D, 0x000A24, 0x001020, - 0x00227D, 0x018040, 0x014F8D, 0x000810, - 0x08043A, 0x2B5A06, 0x000007, 0x002820, - 0x00207D, 0x018040, 0x00117D, 0x038042, - 0x13804A, 0x33800A, 0x00387D, 0x018042, - 0x08000A, 0x000904, 0x177286, 0x000007, - 0x00008D, 0x030964, 0x015B0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x380102, 0x000424, - 0x000424, 0x001224, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x15DA86, 0x000007, - 0x280502, 0x001A4C, 0x177186, 0x000007, - 0x032164, 0x00632C, 0x003DFD, 0x018042, - 0x08000A, 0x000095, 0x090904, 0x000007, - 0x000820, 0x001A4C, 0x169986, 0x018040, - 0x030000, 0x16B206, 0x002124, 0x00010D, - 0x000924, 0x006B24, 0x016F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x003A64, 0x000095, - 0x001224, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x171286, 0x000007, 0x01760D, - 0x000810, 0x08043A, 0x2B5A06, 0x000007, - 0x160A06, 0x000007, 0x007020, 0x08010A, - 0x10012A, 0x0020FD, 0x038860, 0x039060, - 0x018040, 0x00227D, 0x018042, 0x003DFD, - 0x08000A, 0x31844A, 0x000904, 0x181086, - 0x18008B, 0x00008D, 0x189904, 0x00312C, - 0x18E206, 0x000007, 0x00324C, 0x186B86, - 0x000007, 0x001904, 0x186886, 0x000007, - 0x000095, 0x199144, 0x00222C, 0x003124, - 0x00636C, 0x000E3D, 0x001375, 0x000BFD, - 0x010042, 0x09804A, 0x10000A, 0x038AEC, - 0x0393EC, 0x00224C, 0x18E186, 0x000007, - 0x00008D, 0x189904, 0x00226C, 0x00322C, - 0x30050A, 0x301DAB, 0x002083, 0x0018FD, - 0x018042, 0x08000A, 0x018924, 0x300502, - 0x001083, 0x001875, 0x010042, 0x10000A, - 0x00008D, 0x010924, 0x001375, 0x330542, - 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, - 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, - 0x006083, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x19B286, 0x000007, 0x001E2D, - 0x0005FD, 0x018042, 0x08000A, 0x028924, - 0x280502, 0x00060D, 0x000810, 0x280C3A, - 0x00008D, 0x000810, 0x28143A, 0x0A808D, - 0x000820, 0x0002F5, 0x010040, 0x220007, - 0x001275, 0x030042, 0x21004A, 0x00008D, - 0x1A0944, 0x000007, 0x01AB8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, - 0x030042, 0x0D004A, 0x10000A, 0x089144, - 0x000007, 0x000820, 0x010040, 0x0025F5, - 0x0A3144, 0x000007, 0x000820, 0x032860, - 0x030040, 0x00217D, 0x038042, 0x0B804A, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00008D, 0x000124, 0x00012C, 0x000E64, - 0x001A64, 0x00636C, 0x08010A, 0x10012A, - 0x000820, 0x031060, 0x030040, 0x0020FD, - 0x018042, 0x08000A, 0x00227D, 0x018042, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00197D, 0x018042, 0x08000A, 0x0022FD, - 0x038042, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x090D04, 0x000007, 0x000820, - 0x030040, 0x038042, 0x0B804A, 0x10000A, - 0x000820, 0x031060, 0x030040, 0x038042, - 0x13804A, 0x19804A, 0x110D04, 0x198D04, - 0x000007, 0x08000A, 0x001020, 0x031860, - 0x030860, 0x030040, 0x00008D, 0x0B0944, - 0x000007, 0x000820, 0x010040, 0x0005F5, - 0x030042, 0x08000A, 0x000820, 0x010040, - 0x0000F5, 0x010042, 0x08000A, 0x000904, - 0x1D9886, 0x001E75, 0x030042, 0x01044A, - 0x000C0A, 0x1DAA06, 0x000007, 0x000402, - 0x000C02, 0x00177D, 0x001AF5, 0x018042, - 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, - 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, - 0x00043D, 0x0013F5, 0x001AFD, 0x030042, - 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, - 0x089144, 0x19A144, 0x0389E4, 0x0399EC, - 0x005502, 0x005D0A, 0x030042, 0x0B004A, - 0x1B804A, 0x13804A, 0x20000A, 0x089144, - 0x19A144, 0x0389E4, 0x0399EC, 0x006502, - 0x006D0A, 0x030042, 0x0B004A, 0x19004A, - 0x2B804A, 0x13804A, 0x21804A, 0x30000A, - 0x089144, 0x19A144, 0x2AB144, 0x0389E4, - 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, - 0x000702, 0x00107D, 0x000415, 0x018042, - 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, - 0x0019FD, 0x010042, 0x09804A, 0x10000A, - 0x000934, 0x001674, 0x0029F5, 0x010042, - 0x10000A, 0x00917C, 0x002075, 0x010042, - 0x08000A, 0x000904, 0x200A86, 0x0026F5, - 0x0027F5, 0x030042, 0x09004A, 0x10000A, - 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, - 0x010042, 0x51804A, 0x48000A, 0x160007, - 0x001075, 0x010042, 0x282C0A, 0x281D12, - 0x282512, 0x001F32, 0x1E0007, 0x0E0007, - 0x001975, 0x010042, 0x002DF5, 0x0D004A, - 0x10000A, 0x009144, 0x20EA86, 0x010042, - 0x28340A, 0x000E5D, 0x00008D, 0x000375, - 0x000820, 0x010040, 0x05D2F4, 0x54D104, - 0x00735C, 0x218B86, 0x000007, 0x0C0007, - 0x080007, 0x0A0007, 0x02178D, 0x000810, - 0x08043A, 0x34B206, 0x000007, 0x219206, - 0x000007, 0x080007, 0x002275, 0x010042, - 0x20000A, 0x002104, 0x225886, 0x001E2D, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x21CA86, 0x000007, 0x002010, 0x30043A, - 0x00057D, 0x0180C3, 0x08000A, 0x028924, - 0x280502, 0x280C02, 0x0A810D, 0x000820, - 0x0002F5, 0x010040, 0x220007, 0x0004FD, - 0x018042, 0x70000A, 0x030000, 0x007020, - 0x07FA06, 0x018040, 0x022B8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x22C286, - 0x000007, 0x020206, 0x000007, 0x000875, - 0x0009FD, 0x00010D, 0x234206, 0x000295, - 0x000B75, 0x00097D, 0x00000D, 0x000515, - 0x010042, 0x18000A, 0x001904, 0x2A0086, - 0x0006F5, 0x001020, 0x010040, 0x0004F5, - 0x000820, 0x010040, 0x000775, 0x010042, - 0x09804A, 0x10000A, 0x001124, 0x000904, - 0x23F286, 0x000815, 0x080102, 0x101204, - 0x241206, 0x000575, 0x081204, 0x000007, - 0x100102, 0x000575, 0x000425, 0x021124, - 0x100102, 0x000820, 0x031060, 0x010040, - 0x001924, 0x2A0086, 0x00008D, 0x000464, - 0x009D04, 0x291086, 0x180102, 0x000575, - 0x010042, 0x28040A, 0x00018D, 0x000924, - 0x280D02, 0x00000D, 0x000924, 0x281502, - 0x10000D, 0x000820, 0x0002F5, 0x010040, - 0x200007, 0x001175, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x24FA86, 0x000007, - 0x000100, 0x080B20, 0x130B60, 0x1B0B60, - 0x030A60, 0x010040, 0x050042, 0x3D004A, - 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, - 0x010042, 0x28140A, 0x0004F5, 0x010042, - 0x08000A, 0x000315, 0x010D04, 0x260286, - 0x004015, 0x000095, 0x010D04, 0x25F086, - 0x100022, 0x10002A, 0x261A06, 0x000007, - 0x333104, 0x2AA904, 0x000007, 0x032124, - 0x280502, 0x284402, 0x001124, 0x400102, - 0x000424, 0x000424, 0x003224, 0x00292C, - 0x00636C, 0x277386, 0x000007, 0x02B164, - 0x000464, 0x000464, 0x00008D, 0x000A64, - 0x280D02, 0x10008D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x00008D, 0x38B904, - 0x000007, 0x03296C, 0x30010A, 0x0002F5, - 0x010042, 0x08000A, 0x000904, 0x270286, - 0x000007, 0x00212C, 0x28050A, 0x00316C, - 0x00046C, 0x00046C, 0x28450A, 0x001124, - 0x006B64, 0x100102, 0x00008D, 0x01096C, - 0x280D0A, 0x10010D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x004124, 0x000424, - 0x000424, 0x003224, 0x300102, 0x032944, - 0x27FA86, 0x000007, 0x300002, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x284086, 0x003124, 0x000464, 0x300102, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x284A86, 0x000007, 0x284402, 0x003124, - 0x300502, 0x003924, 0x300583, 0x000883, - 0x0005F5, 0x010042, 0x28040A, 0x00008D, - 0x008124, 0x280D02, 0x00008D, 0x008124, - 0x281502, 0x10018D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x001025, 0x000575, - 0x030042, 0x09004A, 0x10000A, 0x0A0904, - 0x121104, 0x000007, 0x001020, 0x050860, - 0x050040, 0x0006FD, 0x018042, 0x09004A, - 0x10000A, 0x0000A5, 0x0A0904, 0x121104, - 0x000007, 0x000820, 0x019060, 0x010040, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x29CA86, 0x000007, 0x244206, 0x000007, - 0x000606, 0x000007, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x2A1A86, 0x000007, - 0x000100, 0x080B20, 0x138B60, 0x1B8B60, - 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, - 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, - 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, - 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, - 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, - 0x038A60, 0x000606, 0x018040, 0x00008D, - 0x000A64, 0x280D02, 0x000A24, 0x00027D, - 0x018042, 0x10000A, 0x001224, 0x0003FD, - 0x018042, 0x08000A, 0x000904, 0x2C0A86, - 0x000007, 0x00018D, 0x000A24, 0x000464, - 0x000464, 0x080102, 0x000924, 0x000424, - 0x000424, 0x100102, 0x02000D, 0x009144, - 0x2C6186, 0x000007, 0x0001FD, 0x018042, - 0x08000A, 0x000A44, 0x2C4386, 0x018042, - 0x0A000D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00027D, 0x001020, 0x000606, - 0x018040, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x2CB286, 0x000007, 0x00037D, - 0x018042, 0x08000A, 0x000904, 0x2CE286, - 0x000007, 0x000075, 0x002E7D, 0x010042, - 0x0B804A, 0x000020, 0x000904, 0x000686, - 0x010040, 0x31844A, 0x30048B, 0x000883, - 0x00008D, 0x000810, 0x28143A, 0x00008D, - 0x000810, 0x280C3A, 0x000675, 0x010042, - 0x08000A, 0x003815, 0x010924, 0x280502, - 0x0B000D, 0x000820, 0x0002F5, 0x010040, - 0x000606, 0x220007, 0x000464, 0x000464, - 0x000606, 0x000007, 0x000134, 0x007F8D, - 0x00093C, 0x281D12, 0x282512, 0x001F32, - 0x0E0007, 0x00010D, 0x00037D, 0x000820, - 0x018040, 0x05D2F4, 0x000007, 0x080007, - 0x00037D, 0x018042, 0x08000A, 0x000904, - 0x2E8A86, 0x000007, 0x000606, 0x000007, - 0x000007, 0x000012, 0x100007, 0x320007, - 0x600007, 0x460007, 0x100080, 0x48001A, - 0x004904, 0x2EF186, 0x000007, 0x001210, - 0x58003A, 0x000145, 0x5C5D04, 0x000007, - 0x000080, 0x48001A, 0x004904, 0x2F4186, - 0x000007, 0x001210, 0x50003A, 0x005904, - 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, - 0x7FFF7D, 0x07D524, 0x004224, 0x500102, - 0x200502, 0x000082, 0x40001A, 0x004104, - 0x2FC986, 0x000007, 0x003865, 0x40001A, - 0x004020, 0x00104D, 0x04C184, 0x31AB86, - 0x000040, 0x040007, 0x000165, 0x000145, - 0x004020, 0x000040, 0x000765, 0x080080, - 0x40001A, 0x004104, 0x305986, 0x000007, - 0x001210, 0x40003A, 0x004104, 0x30B286, - 0x00004D, 0x0000CD, 0x004810, 0x20043A, - 0x000882, 0x40001A, 0x004104, 0x30C186, - 0x000007, 0x004820, 0x005904, 0x319886, - 0x000040, 0x0007E5, 0x200480, 0x2816A0, - 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, - 0x000040, 0x000032, 0x400075, 0x00007D, - 0x07D574, 0x200512, 0x000082, 0x40001A, - 0x004104, 0x317186, 0x000007, 0x038A06, - 0x640007, 0x0000E5, 0x000020, 0x000040, - 0x000A65, 0x000020, 0x020040, 0x020040, - 0x000040, 0x000165, 0x000042, 0x70000A, - 0x007104, 0x323286, 0x000007, 0x060007, - 0x019A06, 0x640007, 0x050000, 0x007020, - 0x000040, 0x038A06, 0x640007, 0x000007, - 0x00306D, 0x028860, 0x029060, 0x08000A, - 0x028860, 0x008040, 0x100012, 0x00100D, - 0x009184, 0x32D186, 0x000E0D, 0x009184, - 0x33E186, 0x000007, 0x300007, 0x001020, - 0x003B6D, 0x008040, 0x000080, 0x08001A, - 0x000904, 0x32F186, 0x000007, 0x001220, - 0x000DED, 0x008040, 0x008042, 0x10000A, - 0x40000D, 0x109544, 0x000007, 0x001020, - 0x000DED, 0x008040, 0x008042, 0x20040A, - 0x000082, 0x08001A, 0x000904, 0x338186, - 0x000007, 0x003B6D, 0x008042, 0x08000A, - 0x000E15, 0x010984, 0x342B86, 0x600007, - 0x08001A, 0x000C15, 0x010984, 0x341386, - 0x000020, 0x1A0007, 0x0002ED, 0x008040, - 0x620007, 0x00306D, 0x028042, 0x0A804A, - 0x000820, 0x0A804A, 0x000606, 0x10804A, - 0x000007, 0x282512, 0x001F32, 0x05D2F4, - 0x54D104, 0x00735C, 0x000786, 0x000007, - 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, - 0x020040, 0x004820, 0x025060, 0x40000A, - 0x024060, 0x000040, 0x454944, 0x000007, - 0x004020, 0x003AE5, 0x000040, 0x0028E5, - 0x000042, 0x48000A, 0x004904, 0x39F886, - 0x002C65, 0x000042, 0x40000A, 0x0000D5, - 0x454104, 0x000007, 0x000655, 0x054504, - 0x368286, 0x0001D5, 0x054504, 0x368086, - 0x002B65, 0x000042, 0x003AE5, 0x50004A, - 0x40000A, 0x45C3D4, 0x000007, 0x454504, - 0x000007, 0x0000CD, 0x444944, 0x000007, - 0x454504, 0x000007, 0x00014D, 0x554944, - 0x000007, 0x045144, 0x367986, 0x002C65, - 0x000042, 0x48000A, 0x4CD104, 0x000007, - 0x04C144, 0x368386, 0x000007, 0x160007, - 0x002CE5, 0x040042, 0x40000A, 0x004020, - 0x000040, 0x002965, 0x000042, 0x40000A, - 0x004104, 0x36F086, 0x000007, 0x002402, - 0x383206, 0x005C02, 0x0025E5, 0x000042, - 0x40000A, 0x004274, 0x002AE5, 0x000042, - 0x40000A, 0x004274, 0x500112, 0x0029E5, - 0x000042, 0x40000A, 0x004234, 0x454104, - 0x000007, 0x004020, 0x000040, 0x003EE5, - 0x000020, 0x000040, 0x002DE5, 0x400152, - 0x50000A, 0x045144, 0x37DA86, 0x0000C5, - 0x003EE5, 0x004020, 0x000040, 0x002BE5, - 0x000042, 0x40000A, 0x404254, 0x000007, - 0x002AE5, 0x004020, 0x000040, 0x500132, - 0x040134, 0x005674, 0x0029E5, 0x020042, - 0x42000A, 0x000042, 0x50000A, 0x05417C, - 0x0028E5, 0x000042, 0x48000A, 0x0000C5, - 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, - 0x020042, 0x40004A, 0x50000A, 0x00423C, - 0x00567C, 0x0028E5, 0x004820, 0x000040, - 0x281D12, 0x282512, 0x001F72, 0x002965, - 0x000042, 0x40000A, 0x004104, 0x393A86, - 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, - 0x000042, 0x40000A, 0x004104, 0x397886, - 0x002D65, 0x000042, 0x28340A, 0x003465, - 0x020042, 0x42004A, 0x004020, 0x4A004A, - 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, - 0x39E186, 0x000007, 0x000606, 0x080007, - 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, - 0x020045, 0x004020, 0x000060, 0x000365, - 0x000040, 0x002E65, 0x001A20, 0x0A1A60, - 0x000040, 0x003465, 0x020042, 0x42004A, - 0x004020, 0x4A004A, 0x000606, 0x50004A, - 0x0017FD, 0x018042, 0x08000A, 0x000904, - 0x225A86, 0x000007, 0x00107D, 0x018042, - 0x0011FD, 0x33804A, 0x19804A, 0x20000A, - 0x000095, 0x2A1144, 0x01A144, 0x3B9086, - 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, - 0x018042, 0x0010FD, 0x09804A, 0x38000A, - 0x000095, 0x010924, 0x003A64, 0x3B8186, - 0x000007, 0x003904, 0x3B9286, 0x000007, - 0x3B9A06, 0x00000D, 0x00008D, 0x000820, - 0x00387D, 0x018040, 0x700002, 0x00117D, - 0x018042, 0x00197D, 0x29804A, 0x30000A, - 0x380002, 0x003124, 0x000424, 0x000424, - 0x002A24, 0x280502, 0x00068D, 0x000810, - 0x28143A, 0x00750D, 0x00B124, 0x002264, - 0x3D0386, 0x284402, 0x000810, 0x280C3A, - 0x0B800D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00758D, 0x00B124, 0x100102, - 0x012144, 0x3E4986, 0x001810, 0x10003A, - 0x00387D, 0x018042, 0x08000A, 0x000904, - 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, - 0x00008D, 0x023164, 0x000A64, 0x280D02, - 0x0B808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00387D, 0x018042, 0x08000A, - 0x000904, 0x3E3286, 0x030000, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x3D8286, - 0x000007, 0x002810, 0x28043A, 0x00750D, - 0x030924, 0x002264, 0x280D02, 0x02316C, - 0x28450A, 0x0B810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x00008D, 0x000A24, - 0x3E4A06, 0x100102, 0x001810, 0x10003A, - 0x0000BD, 0x003810, 0x30043A, 0x00187D, - 0x018042, 0x0018FD, 0x09804A, 0x20000A, - 0x0000AD, 0x028924, 0x07212C, 0x001010, - 0x300583, 0x300D8B, 0x3014BB, 0x301C83, - 0x002083, 0x00137D, 0x038042, 0x33844A, - 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, - 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, - 0x001E0D, 0x0005FD, 0x018042, 0x20000A, - 0x020924, 0x00068D, 0x00A96C, 0x00009D, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x3F6A86, 0x000007, 0x280502, 0x280D0A, - 0x284402, 0x001810, 0x28143A, 0x0C008D, - 0x000820, 0x0002FD, 0x018040, 0x220007, - 0x003904, 0x225886, 0x001E0D, 0x00057D, - 0x018042, 0x20000A, 0x020924, 0x0000A5, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x402A86, 0x000007, 0x280502, 0x280C02, - 0x002010, 0x28143A, 0x0C010D, 0x000820, - 0x0002FD, 0x018040, 0x225A06, 0x220007, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000 -}; diff -Nru a/drivers/sound/Makefile b/drivers/sound/Makefile --- a/drivers/sound/Makefile Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,294 +0,0 @@ -# Makefile for the Linux sound card driver -# -# 18 Apr 1998, Michael Elizabeth Chastain, -# Rewritten to use lists instead of if-statements. - - -# All of the (potential) objects that export symbols. -# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. - -export-objs := ad1848.o audio_syms.o midi_syms.o mpu401.o \ - msnd.o opl3.o sb_common.o sequencer_syms.o \ - sound_core.o sound_syms.o uart401.o \ - nm256_audio.o ac97.o ac97_codec.o aci.o - -# Each configuration option enables a list of files. - -obj-$(CONFIG_SOUND) += soundcore.o -obj-$(CONFIG_SOUND_OSS) += sound.o -obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o - -# Please leave it as is, cause the link order is significant ! - -obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o -obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o -obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o -obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o -obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o -obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb_lib.o uart401.o -obj-$(CONFIG_SOUND_CS4232) += cs4232.o uart401.o -obj-$(CONFIG_SOUND_OPL3SA2) += opl3sa2.o ad1848.o mpu401.o -obj-$(CONFIG_SOUND_MSS) += ad1848.o -obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o -obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o -obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o -obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o -obj-$(CONFIG_SOUND_MPU401) += mpu401.o -obj-$(CONFIG_SOUND_UART6850) += uart6850.o -obj-$(CONFIG_SOUND_GUS) += gus.o ad1848.o -obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o -obj-$(CONFIG_SOUND_YM3812) += opl3.o -obj-$(CONFIG_SOUND_VMIDI) += v_midi.o -obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o -obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o -obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o -obj-$(CONFIG_SOUND_AD1816) += ad1816.o -obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o -obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o - -obj-$(CONFIG_SOUND_VIA82CXXX) += via82cxxx_audio.o ac97_codec.o -ifeq ($(CONFIG_MIDI_VIA82CXXX),y) - obj-$(CONFIG_SOUND_VIA82CXXX) += sound.o uart401.o -endif -obj-$(CONFIG_SOUND_YMFPCI) += ymfpci.o ac97_codec.o -ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y) - obj-$(CONFIG_SOUND_YMFPCI) += opl3.o uart401.o -endif -obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o -obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o -obj-$(CONFIG_SOUND_VWSND) += vwsnd.o -obj-$(CONFIG_SOUND_NM256) += nm256_audio.o ac97.o -obj-$(CONFIG_SOUND_ICH) += i810_audio.o ac97_codec.o -obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o -obj-$(CONFIG_SOUND_CMPCI) += cmpci.o -obj-$(CONFIG_SOUND_ES1370) += es1370.o -obj-$(CONFIG_SOUND_ES1371) += es1371.o ac97_codec.o -obj-$(CONFIG_SOUND_VRC5477) += nec_vrc5477.o ac97_codec.o -obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o -obj-$(CONFIG_SOUND_FUSION) += cs46xx.o ac97_codec.o -obj-$(CONFIG_SOUND_MAESTRO) += maestro.o -obj-$(CONFIG_SOUND_MAESTRO3) += maestro3.o ac97_codec.o -obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o -obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o -obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o -obj-$(CONFIG_SOUND_BT878) += btaudio.o -obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o - -ifeq ($(CONFIG_MIDI_EMU10K1),y) - obj-$(CONFIG_SOUND_EMU10K1) += sound.o -endif - -subdir-$(CONFIG_SOUND_EMU10K1) += emu10k1 -subdir-$(CONFIG_SOUND_CS4281) += cs4281 - -ifeq ($(CONFIG_SOUND_EMU10K1),y) - obj-y += emu10k1/emu10k1.o -endif - -ifeq ($(CONFIG_SOUND_CS4281),y) - obj-y += cs4281/cs4281.o -endif - -subdir-$(CONFIG_DMASOUND) += dmasound - -ifeq ($(CONFIG_DMASOUND),y) - obj-y += dmasound/dmasound.o -endif - - -# Declare multi-part drivers. - -list-multi := sound.o gus.o pas2.o sb.o sb_lib.o vidc_mod.o \ - soundcore.o wavefront.o - -sound-objs := \ - dev_table.o soundcard.o sound_syms.o \ - audio.o audio_syms.o dmabuf.o \ - midi_syms.o midi_synth.o midibuf.o \ - sequencer.o sequencer_syms.o sound_timer.o sys_timer.o - -soundcore-objs := sound_core.o sound_firmware.o - -gus-objs := gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o -pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o -sb-objs := sb_card.o -sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o -vidc_mod-objs := vidc.o vidc_fill.o -wavefront-objs := wavfront.o wf_midi.o yss225.o - - -O_TARGET := sounddrivers.o - -include $(TOPDIR)/Rules.make - - - -# Link rules for multi-part drivers. - -sound.o: $(sound-objs) - $(LD) -r -o $@ $(sound-objs) - -soundcore.o: $(soundcore-objs) - $(LD) -r -o $@ $(soundcore-objs) - -gus.o: $(gus-objs) - $(LD) -r -o $@ $(gus-objs) - -pas2.o: $(pas2-objs) - $(LD) -r -o $@ $(pas2-objs) - -sb.o: $(sb-objs) - $(LD) -r -o $@ $(sb-objs) - -sb_lib.o: $(sb_lib-objs) - $(LD) -r -o $@ $(sb_lib-objs) - -vidc_mod.o: $(vidc_mod-objs) - $(LD) -r -o $@ $(vidc_mod-objs) - -wavefront.o: $(wavefront-objs) - $(LD) -r -o $@ $(wavefront-objs) - -# Firmware files that need translation -# -# The translated files are protected by a file that keeps track -# of what name was used to build them. If the name changes, they -# will be forced to be remade. -# -# First make the utilities. - -bin2hex: bin2hex.c - $(HOSTCC) $(HOSTCFLAGS) -o bin2hex bin2hex.c - -hex2hex: hex2hex.c - $(HOSTCC) $(HOSTCFLAGS) -o hex2hex hex2hex.c - - - - -# Turtle Beach Maui / Tropez - -maui.o: maui_boot.h - -ifeq ($(CONFIG_MAUI_HAVE_BOOT),y) - maui_boot.h: $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) bin2hex - ./bin2hex -i maui_os < $(CONFIG_MAUI_BOOT_FILE) > $@ -else - maui_boot.h: - ( \ - echo 'static unsigned char * maui_os = NULL;'; \ - echo 'static int maui_osLen = 0;'; \ - ) > $@ -endif - @ ( \ - echo 'ifeq ($(strip $(CONFIG_MAUI_HAVE_BOOT) $(CONFIG_MAUI_BOOT_FILE)),$$(strip $$(CONFIG_MAUI_HAVE_BOOT) $$(CONFIG_MAUI_BOOT_FILE)))'; \ - echo 'FILES_BOOT_UP_TO_DATE += $@'; \ - echo 'endif' \ - ) > .$@.boot - - - -# Turtle Beach MultiSound - -ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y) - msnd_classic.o: msndperm.c msndinit.c - - msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) bin2hex - ./bin2hex msndperm < $(CONFIG_MSNDCLAS_PERM_FILE) > $@ - @ ( \ - echo 'ifeq ($(strip $(CONFIG_MSNDCLAS_HAVE_BOOT) $(CONFIG_MSNDCLAS_PERM_FILE)),$$(strip $$(CONFIG_MSNDCLAS_HAVE_BOOT) $$(CONFIG_MSNDCLAS_PERM_FILE)))'; \ - echo 'FILES_BOOT_UP_TO_DATE += $@'; \ - echo 'endif' \ - ) > .$@.boot - - msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) bin2hex - ./bin2hex msndinit < $(CONFIG_MSNDCLAS_INIT_FILE) > $@ - @ ( \ - echo 'ifeq ($(strip $(CONFIG_MSNDCLAS_HAVE_BOOT) $(CONFIG_MSNDCLAS_INIT_FILE)),$$(strip $$(CONFIG_MSNDCLAS_HAVE_BOOT) $$(CONFIG_MSNDCLAS_INIT_FILE)))'; \ - echo 'FILES_BOOT_UP_TO_DATE += $@'; \ - echo 'endif' \ - ) > .$@.boot -endif - -ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y) - msnd_pinnacle.o: pndsperm.c pndspini.c - - pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) bin2hex - ./bin2hex pndsperm < $(CONFIG_MSNDPIN_PERM_FILE) > $@ - @ ( \ - echo 'ifeq ($(strip $(CONFIG_MSNDPIN_HAVE_BOOT) $(CONFIG_MSNDPIN_PERM_FILE)),$$(strip $$(CONFIG_MSNDPIN_HAVE_BOOT) $$(CONFIG_MSNDPIN_PERM_FILE)))'; \ - echo 'FILES_BOOT_UP_TO_DATE += $@'; \ - echo 'endif' \ - ) > .$@.boot - - pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) bin2hex - ./bin2hex pndspini < $(CONFIG_MSNDPIN_INIT_FILE) > $@ - @ ( \ - echo 'ifeq ($(strip $(CONFIG_MSNDPIN_HAVE_BOOT) $(CONFIG_MSNDPIN_INIT_FILE)),$$(strip $$(CONFIG_MSNDPIN_HAVE_BOOT) $$(CONFIG_MSNDPIN_INIT_FILE)))'; \ - echo 'FILES_BOOT_UP_TO_DATE += $@'; \ - echo 'endif' \ - ) > .$@.boot -endif - - - -# PSS (ECHO-ADI2111) - -pss.o: pss_boot.h - -ifeq ($(CONFIG_PSS_HAVE_BOOT),y) - pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) bin2hex - ./bin2hex pss_synth < $(CONFIG_PSS_BOOT_FILE) > $@ -else - pss_boot.h: - ( \ - echo 'static unsigned char * pss_synth = NULL;'; \ - echo 'static int pss_synthLen = 0;'; \ - ) > $@ -endif - @ ( \ - echo 'ifeq ($(strip $(CONFIG_PSS_HAVE_BOOT) $(CONFIG_PSS_BOOT_FILE)),$$(strip $$(CONFIG_PSS_HAVE_BOOT) $$(CONFIG_PSS_BOOT_FILE)))'; \ - echo 'FILES_BOOT_UP_TO_DATE += $@'; \ - echo 'endif' \ - ) > .$@.boot - - - -# MediaTrix AudioTrix Pro - -trix.o: trix_boot.h - -ifeq ($(CONFIG_TRIX_HAVE_BOOT),y) - trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) hex2hex - ./hex2hex -i trix_boot < $(CONFIG_TRIX_BOOT_FILE) > $@ -else - trix_boot.h: - ( \ - echo 'static unsigned char * trix_boot = NULL;'; \ - echo 'static int trix_boot_len = 0;'; \ - ) > $@ -endif - @ ( \ - echo 'ifeq ($(strip $(CONFIG_TRIX_HAVE_BOOT) $(CONFIG_TRIX_BOOT_FILE)),$$(strip $$(CONFIG_TRIX_HAVE_BOOT) $$(CONFIG_TRIX_BOOT_FILE)))'; \ - echo 'FILES_BOOT_UP_TO_DATE += $@'; \ - echo 'endif' \ - ) > .$@.boot - - - -# Find boot files whose source file names have changed and force rebuild. - -FILES_BOOT_UP_TO_DATE := - -FILES_BOOT_EXIST := $(wildcard .*.boot) -ifneq ($(FILES_BOOT_EXIST),) -include $(FILES_BOOT_EXIST) -endif - -FILES_BOOT_CHANGED := $(strip \ - $(filter-out $(FILES_BOOT_UP_TO_DATE), \ - maui_boot.h pss_boot.h trix_boot.h)) - -ifneq ($(FILES_BOOT_CHANGED),) -$(FILES_BOOT_CHANGED): dummy -endif diff -Nru a/drivers/sound/README.FIRST b/drivers/sound/README.FIRST --- a/drivers/sound/README.FIRST Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,6 +0,0 @@ -The modular sound driver patches were funded by Red Hat Software -(www.redhat.com). The sound driver here is thus a modified version of -Hannu's code. Please bear that in mind when considering the appropriate -forums for bug reporting. - -Alan Cox diff -Nru a/drivers/sound/ac97.c b/drivers/sound/ac97.c --- a/drivers/sound/ac97.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,455 +0,0 @@ -#include -#include -#include -#include "ac97.h" - -/* Flag for mono controls. */ -#define MO 0 -/* And for stereo. */ -#define ST 1 - -/* Whether or not the bits in the channel are inverted. */ -#define INV 1 -#define NINV 0 - -static struct ac97_chn_desc { - int ac97_regnum; - int oss_channel; - int maxval; - int is_stereo; - int oss_mask; - int recordNum; - u16 regmask; - int is_inverted; -} mixerRegs[] = { - { AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME, 0x3f, ST, SOUND_MASK_VOLUME, 5, 0x0000, INV }, - { AC97_MASTER_VOL_MONO, SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000, INV }, - { AC97_MASTER_TONE, SOUND_MIXER_TREBLE, 0x0f, MO, SOUND_MASK_TREBLE, -1, 0x00ff, INV }, - { AC97_MASTER_TONE, SOUND_MIXER_BASS, 0x0f, MO, SOUND_MASK_BASS, -1, 0xff00, INV }, - { AC97_PCBEEP_VOL, SOUND_MIXER_SPEAKER, 0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e, INV }, - { AC97_PHONE_VOL, SOUND_MIXER_PHONEIN, 0x1f, MO, SOUND_MASK_PHONEIN, 7, 0x0000, INV }, - { AC97_MIC_VOL, SOUND_MIXER_MIC, 0x1f, MO, SOUND_MASK_MIC, 0, 0x0000, INV }, - { AC97_LINEIN_VOL, SOUND_MIXER_LINE, 0x1f, ST, SOUND_MASK_LINE, 4, 0x0000, INV }, - { AC97_CD_VOL, SOUND_MIXER_CD, 0x1f, ST, SOUND_MASK_CD, 1, 0x0000, INV }, - { AC97_VIDEO_VOL, SOUND_MIXER_VIDEO, 0x1f, ST, SOUND_MASK_VIDEO, 2, 0x0000, INV }, - { AC97_AUX_VOL, SOUND_MIXER_LINE1, 0x1f, ST, SOUND_MASK_LINE1, 3, 0x0000, INV }, - { AC97_PCMOUT_VOL, SOUND_MIXER_PCM, 0x1f, ST, SOUND_MASK_PCM, -1, 0x0000, INV }, - { AC97_RECORD_GAIN, SOUND_MIXER_IGAIN, 0x0f, ST, SOUND_MASK_IGAIN, -1, 0x0000, NINV }, - { -1, -1, 0xff, 0, 0, -1, 0x0000, 0 }, -}; - -static struct ac97_chn_desc * -ac97_find_chndesc (struct ac97_hwint *dev, int oss_channel) -{ - int x; - - for (x = 0; mixerRegs[x].oss_channel != -1; x++) { - if (mixerRegs[x].oss_channel == oss_channel) - return mixerRegs + x; - } - - return NULL; -} - -static inline int -ac97_is_valid_channel (struct ac97_hwint *dev, struct ac97_chn_desc *chn) -{ - return (dev->last_written_mixer_values[chn->ac97_regnum / 2] - != AC97_REG_UNSUPPORTED); -} - -int -ac97_init (struct ac97_hwint *dev) -{ - int x; - int reg0; - - /* Clear out the arrays of cached values. */ - for (x = 0; x < AC97_REG_CNT; x++) - dev->last_written_mixer_values[x] = AC97_REGVAL_UNKNOWN; - - for (x = 0; x < SOUND_MIXER_NRDEVICES; x++) - dev->last_written_OSS_values[x] = AC97_REGVAL_UNKNOWN; - - /* Clear the device masks. */ - dev->mixer_devmask = 0; - dev->mixer_stereomask = 0; - dev->mixer_recmask = 0; - - /* ??? Do a "standard reset" via register 0? */ - - /* Hardware-dependent reset. */ - if (dev->reset_device (dev)) - return -1; - - /* Check the mixer device capabilities. */ - reg0 = dev->read_reg (dev, AC97_RESET); - - if (reg0 < 0) - return -1; - - /* Check for support for treble/bass controls. */ - if (! (reg0 & 4)) { - dev->last_written_mixer_values[AC97_MASTER_TONE / 2] - = AC97_REG_UNSUPPORTED; - } - - /* ??? There may be other tests here? */ - - /* Fill in the device masks. */ - for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { - if (ac97_is_valid_channel (dev, mixerRegs + x)) { - dev->mixer_devmask |= mixerRegs[x].oss_mask; - - if (mixerRegs[x].is_stereo) - dev->mixer_stereomask |= mixerRegs[x].oss_mask; - - if (mixerRegs[x].recordNum != -1) - dev->mixer_recmask |= mixerRegs[x].oss_mask; - } - } - - return 0; -} - -/* Reset the mixer to the currently saved settings. */ -int -ac97_reset (struct ac97_hwint *dev) -{ - int x; - - if (dev->reset_device (dev)) - return -1; - - /* Now set the registers back to their last-written values. */ - for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { - int regnum = mixerRegs[x].ac97_regnum; - int value = dev->last_written_mixer_values [regnum / 2]; - if (value >= 0) - ac97_put_register (dev, regnum, value); - } - return 0; -} - -/* Return the contents of register REG; use the cache if the value in it - is valid. Returns a negative error code on failure. */ -int -ac97_get_register (struct ac97_hwint *dev, u8 reg) -{ - if (reg > 127 || (reg & 1)) - return -EINVAL; - - /* See if it's in the cache, or if it's just plain invalid. */ - switch (dev->last_written_mixer_values[reg / 2]) { - case AC97_REG_UNSUPPORTED: - return -EINVAL; - break; - case AC97_REGVAL_UNKNOWN: - dev->last_written_mixer_values[reg / 2] = dev->read_reg (dev, reg); - break; - default: - break; - } - return dev->last_written_mixer_values[reg / 2]; -} - -/* Write VALUE to AC97 register REG, and cache its value in the last-written - cache. Returns a negative error code on failure, or 0 on success. */ -int -ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value) -{ - if (reg > 127 || (reg & 1)) - return -EINVAL; - - if (dev->last_written_mixer_values[reg / 2] == AC97_REG_UNSUPPORTED) - return -EINVAL; - else { - int res = dev->write_reg (dev, reg, value); - if (res >= 0) { - dev->last_written_mixer_values[reg / 2] = value; - return 0; - } - else - return res; - } -} - -/* Scale VALUE (a value fro 0 to MAXVAL) to a value from 0-100. If - IS_STEREO is set, VALUE is a stereo value; the left channel value - is in the lower 8 bits, and the right channel value is in the upper - 8 bits. - - A negative error code is returned on failure, or the unsigned - scaled value on success. */ - -static int -ac97_scale_to_oss_val (int value, int maxval, int is_stereo, int inv) -{ - /* Muted? */ - if (value & AC97_MUTE) - return 0; - - if (is_stereo) - return (ac97_scale_to_oss_val (value & 255, maxval, 0, inv) << 8) - | (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); - else { - int i; - - /* Inverted. */ - if (inv) - value = maxval - value; - - i = (value * 100 + (maxval / 2)) / maxval; - if (i > 100) - i = 100; - if (i < 0) - i = 0; - return i; - } -} - -static int -ac97_scale_from_oss_val (int value, int maxval, int is_stereo, int inv) -{ - if (is_stereo) - return (ac97_scale_from_oss_val (value & 255, maxval, 0, inv) << 8) - | (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); - else { - int i = ((value & 255) * maxval + 50) / 100; - if (inv) - i = maxval - i; - if (i < 0) - i = 0; - if (i > maxval) - i = maxval; - return i; - } -} - -int -ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, u16 oss_value) -{ - int scaled_value; - struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel); - int result; - - if (channel == NULL) - return -ENODEV; - if (! ac97_is_valid_channel (dev, channel)) - return -ENODEV; - scaled_value = ac97_scale_from_oss_val (oss_value, channel->maxval, - channel->is_stereo, - channel->is_inverted); - if (scaled_value < 0) - return scaled_value; - - if (channel->regmask != 0) { - int mv; - - int oldval = ac97_get_register (dev, channel->ac97_regnum); - if (oldval < 0) - return oldval; - - for (mv = channel->regmask; ! (mv & 1); mv >>= 1) - scaled_value <<= 1; - - scaled_value &= channel->regmask; - scaled_value |= (oldval & ~channel->regmask); - } - result = ac97_put_register (dev, channel->ac97_regnum, scaled_value); - if (result == 0) - dev->last_written_OSS_values[oss_channel] = oss_value; - return result; -} - -int -ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel) -{ - struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel); - int regval; - - if (channel == NULL) - return -ENODEV; - - if (! ac97_is_valid_channel (dev, channel)) - return -ENODEV; - - regval = ac97_get_register (dev, channel->ac97_regnum); - - if (regval < 0) - return regval; - - if (channel->regmask != 0) { - int mv; - - regval &= channel->regmask; - - for (mv = channel->regmask; ! (mv & 1); mv >>= 1) - regval >>= 1; - } - return ac97_scale_to_oss_val (regval, channel->maxval, - channel->is_stereo, - channel->is_inverted); -} - -int -ac97_get_recmask (struct ac97_hwint *dev) -{ - int recReg = ac97_get_register (dev, AC97_RECORD_SELECT); - - if (recReg < 0) - return recReg; - else { - int x; - for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { - if (mixerRegs[x].recordNum == (recReg & 7)) - return mixerRegs[x].oss_mask; - } - return -ENODEV; - } -} - -int -ac97_set_recmask (struct ac97_hwint *dev, int oss_recmask) -{ - int x; - - if (oss_recmask == 0) - oss_recmask = SOUND_MIXER_MIC; - - for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { - if ((mixerRegs[x].recordNum >= 0) - && (oss_recmask & mixerRegs[x].oss_mask)) - break; - } - if (mixerRegs[x].ac97_regnum < 0) - return -ENODEV; - else { - int regval = (mixerRegs[x].recordNum << 8) | mixerRegs[x].recordNum; - int res = ac97_put_register (dev, AC97_RECORD_SELECT, regval); - if (res == 0) - return ac97_get_recmask (dev); - else - return res; - } -} - -/* Set the mixer DEV to the list of values in VALUE_LIST. Return 0 on - success, or a negative error code. */ -int -ac97_set_values (struct ac97_hwint *dev, - struct ac97_mixer_value_list *value_list) -{ - int x; - - for (x = 0; value_list[x].oss_channel != -1; x++) { - int chnum = value_list[x].oss_channel; - struct ac97_chn_desc *chent = ac97_find_chndesc (dev, chnum); - if (chent != NULL) { - u16 val; - int res; - - if (chent->is_stereo) - val = (value_list[x].value.stereo.right << 8) - | value_list[x].value.stereo.left; - else { - /* We do this so the returned value looks OK in the - mixer app. It's not necessary otherwise. */ - val = (value_list[x].value.mono << 8) - | value_list[x].value.mono; - } - res = ac97_set_mixer (dev, chnum, val); - if (res < 0) - return res; - } - else - return -ENODEV; - } - return 0; -} - -int -ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, caddr_t arg) -{ - int ret; - - switch (cmd) { - case SOUND_MIXER_READ_RECSRC: - ret = ac97_get_recmask (dev); - break; - - case SOUND_MIXER_WRITE_RECSRC: - { - if (get_user (ret, (int *) arg)) - ret = -EFAULT; - else - ret = ac97_set_recmask (dev, ret); - } - break; - - case SOUND_MIXER_READ_CAPS: - ret = SOUND_CAP_EXCL_INPUT; - break; - - case SOUND_MIXER_READ_DEVMASK: - ret = dev->mixer_devmask; - break; - - case SOUND_MIXER_READ_RECMASK: - ret = dev->mixer_recmask; - break; - - case SOUND_MIXER_READ_STEREODEVS: - ret = dev->mixer_stereomask; - break; - - default: - /* Read or write request. */ - ret = -EINVAL; - if (_IOC_TYPE (cmd) == 'M') { - int dir = _SIOC_DIR (cmd); - int channel = _IOC_NR (cmd); - - if (channel >= 0 && channel < SOUND_MIXER_NRDEVICES) { - ret = 0; - if (dir & _SIOC_WRITE) { - int val; - if (get_user (val, (int *) arg) == 0) - ret = ac97_set_mixer (dev, channel, val); - else - ret = -EFAULT; - } - if (ret >= 0 && (dir & _SIOC_READ)) { - if (dev->last_written_OSS_values[channel] - == AC97_REGVAL_UNKNOWN) - dev->last_written_OSS_values[channel] - = ac97_get_mixer_scaled (dev, channel); - ret = dev->last_written_OSS_values[channel]; - } - } - } - break; - } - - if (ret < 0) - return ret; - else - return put_user(ret, (int *) arg); -} - -EXPORT_SYMBOL(ac97_init); -EXPORT_SYMBOL(ac97_set_values); -EXPORT_SYMBOL(ac97_set_mixer); -EXPORT_SYMBOL(ac97_get_register); -EXPORT_SYMBOL(ac97_put_register); -EXPORT_SYMBOL(ac97_get_mixer_scaled); -EXPORT_SYMBOL(ac97_mixer_ioctl); -EXPORT_SYMBOL(ac97_reset); -MODULE_LICENSE("GPL"); - - -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ diff -Nru a/drivers/sound/ac97.h b/drivers/sound/ac97.h --- a/drivers/sound/ac97.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,220 +0,0 @@ -/* - * ac97.h - * - * definitions for the AC97, Intel's Audio Codec 97 Spec - * also includes support for a generic AC97 interface - */ - -#ifndef _AC97_H_ -#define _AC97_H_ -#include "sound_config.h" -#include "sound_calls.h" - -#define AC97_RESET 0x0000 // -#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out -#define AC97_HEADPHONE_VOL 0x0004 // -#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output -#define AC97_MASTER_TONE 0x0008 // -#define AC97_PCBEEP_VOL 0x000a // none -#define AC97_PHONE_VOL 0x000c // TAD Input (mono) -#define AC97_MIC_VOL 0x000e // MIC Input (mono) -#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo) -#define AC97_CD_VOL 0x0012 // CD Input (stereo) -#define AC97_VIDEO_VOL 0x0014 // none -#define AC97_AUX_VOL 0x0016 // Aux Input (stereo) -#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo) -#define AC97_RECORD_SELECT 0x001a // -#define AC97_RECORD_GAIN 0x001c -#define AC97_RECORD_GAIN_MIC 0x001e -#define AC97_GENERAL_PURPOSE 0x0020 -#define AC97_3D_CONTROL 0x0022 -#define AC97_MODEM_RATE 0x0024 -#define AC97_POWER_CONTROL 0x0026 - -/* registers 0x0028 - 0x0058 are reserved */ - -/* AC'97 2.0 */ -#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ -#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ -#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ -#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ -#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ -#define AC97_PCM_LR_ADC_RATE 0x0032 /* PCM LR DAC Rate */ -#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ -#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ -#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ -#define AC97_RESERVED_3A 0x003A /* Reserved */ -/* range 0x3c-0x58 - MODEM */ - -/* registers 0x005a - 0x007a are vendor reserved */ - -#define AC97_VENDOR_ID1 0x007c -#define AC97_VENDOR_ID2 0x007e - -/* volume control bit defines */ - -#define AC97_MUTE 0x8000 -#define AC97_MICBOOST 0x0040 -#define AC97_LEFTVOL 0x3f00 -#define AC97_RIGHTVOL 0x003f - -/* record mux defines */ - -#define AC97_RECMUX_MIC 0x0000 -#define AC97_RECMUX_CD 0x0101 -#define AC97_RECMUX_VIDEO 0x0202 /* not used */ -#define AC97_RECMUX_AUX 0x0303 -#define AC97_RECMUX_LINE 0x0404 -#define AC97_RECMUX_STEREO_MIX 0x0505 -#define AC97_RECMUX_MONO_MIX 0x0606 -#define AC97_RECMUX_PHONE 0x0707 - - -/* general purpose register bit defines */ - -#define AC97_GP_LPBK 0x0080 /* Loopback mode */ -#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */ -#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */ -#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */ -#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */ -#define AC97_GP_LD 0x1000 /* Loudness 1=on */ -#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */ -#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */ -#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */ - - -/* powerdown control and status bit defines */ - -/* status */ -#define AC97_PWR_MDM 0x0010 /* Modem section ready */ -#define AC97_PWR_REF 0x0008 /* Vref nominal */ -#define AC97_PWR_ANL 0x0004 /* Analog section ready */ -#define AC97_PWR_DAC 0x0002 /* DAC section ready */ -#define AC97_PWR_ADC 0x0001 /* ADC section ready */ - -/* control */ -#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */ -#define AC97_PWR_PR1 0x0200 /* DAC powerdown */ -#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */ -#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */ -#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */ -#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */ -#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */ -#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */ - -/* useful power states */ -#define AC97_PWR_D0 0x0000 /* everything on */ -#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4 -#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 -#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 -#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */ - -/* Total number of defined registers. */ -#define AC97_REG_CNT 64 - -/* Generic AC97 mixer interface. */ - -/* Structure describing access to the hardware. */ -struct ac97_hwint -{ - /* Perform any hardware-specific reset and initialization. Returns - 0 on success, or a negative error code. */ - int (*reset_device) (struct ac97_hwint *dev); - - /* Returns the contents of the specified register REG. The caller - should check to see if the desired contents are available in - the cache first, if applicable. Returns a positive unsigned value - representing the contents of the register, or a negative error - code. */ - int (*read_reg) (struct ac97_hwint *dev, u8 reg); - - /* Writes VALUE to register REG. Returns 0 on success, or a - negative error code. */ - int (*write_reg) (struct ac97_hwint *dev, u8 reg, u16 value); - - /* Hardware-specific information. */ - void *driver_private; - - /* Three OSS masks. */ - int mixer_devmask; - int mixer_stereomask; - int mixer_recmask; - - /* The mixer cache. The indices correspond to the AC97 hardware register - number / 2, since the register numbers are always an even number. - - Unknown values are set to -1; unsupported registers contain a - -2. */ - int last_written_mixer_values[AC97_REG_CNT]; - - /* A cache of values written via OSS; we need these so we can return - the values originally written by the user. - - Why the original user values? Because the real-world hardware - has less precision, and some existing applications assume that - they will get back the exact value that they wrote (aumix). - - A -1 value indicates that no value has been written to this mixer - channel via OSS. */ - int last_written_OSS_values[SOUND_MIXER_NRDEVICES]; -}; - -/* Values stored in the register cache. */ -#define AC97_REGVAL_UNKNOWN -1 -#define AC97_REG_UNSUPPORTED -2 - -struct ac97_mixer_value_list -{ - /* Mixer channel to set. List is terminated by a value of -1. */ - int oss_channel; - /* The scaled value to set it to; values generally range from 0-100. */ - union { - struct { - u8 left, right; - } stereo; - u8 mono; - } value; -}; - -/* Initialize the ac97 mixer by resetting it. */ -extern int ac97_init (struct ac97_hwint *dev); - -/* Sets the mixer DEV to the values in VALUE_LIST. Returns 0 on success, - or a negative error code. */ -extern int ac97_set_values (struct ac97_hwint *dev, - struct ac97_mixer_value_list *value_list); - -/* Sets one mixer channel OSS_CHANNEL to the scaled value OSS_VALUE. - Returns the resulting (rescaled) value, or a negative value - representing an error code. - - Stereo channels have two values in OSS_VALUE (the left value is in the - lower 8 bits, the right value is in the upper 8 bits). */ -extern int ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, - u16 oss_value); - -/* Return the contents of the specified AC97 register REG; it uses the - last-written value if it is available. */ -extern int ac97_get_register (struct ac97_hwint *dev, u8 reg); - -/* Writes the specified VALUE to the AC97 register REG in the mixer. - Takes care of setting the last-written cache as well. */ -extern int ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value); - -/* Returns the last OSS value written to the OSS_CHANNEL mixer channel. */ -extern int ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel); - -/* Default ioctl. */ -extern int ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, - caddr_t arg); - -/* Do a complete reset on the AC97 mixer, restoring all mixer registers to - the current values. Normally used after an APM resume event. */ -extern int ac97_reset (struct ac97_hwint *dev); -#endif - -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ diff -Nru a/drivers/sound/ac97_codec.c b/drivers/sound/ac97_codec.c --- a/drivers/sound/ac97_codec.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1049 +0,0 @@ - -/* - * ac97_codec.c: Generic AC97 mixer/modem module - * - * Derived from ac97 mixer in maestro and trident driver. - * - * Copyright 2000 Silicon Integrated System Corporation - * - * 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. - * - ************************************************************************** - * - * The Intel Audio Codec '97 specification is available at the Intel - * audio homepage: http://developer.intel.com/ial/scalableplatforms/audio/ - * - * The specification itself is currently available at: - * ftp://download.intel.com/ial/scalableplatforms/ac97r22.pdf - * - ************************************************************************** - * - * History - * v0.4 Mar 15 2000 Ollie Lho - * dual codecs support verified with 4 channels output - * v0.3 Feb 22 2000 Ollie Lho - * bug fix for record mask setting - * v0.2 Feb 10 2000 Ollie Lho - * add ac97_read_proc for /proc/driver/{vendor}/ac97 - * v0.1 Jan 14 2000 Ollie Lho - * Isolated from trident.c to support multiple ac97 codec - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel); -static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, - unsigned int left, unsigned int right); -static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ); -static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask); -static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg); - -static int ac97_init_mixer(struct ac97_codec *codec); - -static int wolfson_init(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); -static int sigmatel_9721_init(struct ac97_codec *codec); -static int sigmatel_9744_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); - - -/* - * AC97 operations. - * - * If you are adding a codec then you should be able to use - * eapd_ops - any codec that supports EAPD amp control (most) - * null_ops - any ancient codec that supports nothing - * - * The three functions are - * init - used for non AC97 standard initialisation - * amplifier - used to do amplifier control (1=on 0=off) - * digital - switch to digital modes (0 = analog) - * - * Not all codecs support all features, not all drivers use all the - * operations yet - */ - -static struct ac97_ops null_ops = { NULL, NULL, NULL }; -static struct ac97_ops default_ops = { NULL, eapd_control, NULL }; -static struct ac97_ops wolfson_ops = { wolfson_init, 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 }; -static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL }; -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 }; - -/* sorted by vendor/device id */ -static const struct { - u32 id; - char *name; - struct ac97_ops *ops; -} ac97_codec_ids[] = { - {0x41445303, "Analog Devices AD1819", &null_ops}, - {0x41445340, "Analog Devices AD1881", &null_ops}, - {0x41445348, "Analog Devices AD1881A", &null_ops}, - {0x41445360, "Analog Devices AD1885", &default_ops}, - {0x41445460, "Analog Devices AD1885", &default_ops}, - {0x414B4D00, "Asahi Kasei AK4540", &null_ops}, - {0x414B4D01, "Asahi Kasei AK4542", &null_ops}, - {0x414B4D02, "Asahi Kasei AK4543", &null_ops}, - {0x414C4710, "ALC200/200P", &null_ops}, - {0x43525900, "Cirrus Logic CS4297", &default_ops}, - {0x43525903, "Cirrus Logic CS4297", &default_ops}, - {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops}, - {0x43525914, "Cirrus Logic CS4297A rev B", &default_ops}, - {0x43525923, "Cirrus Logic CS4298", &null_ops}, - {0x4352592B, "Cirrus Logic CS4294", &null_ops}, - {0x4352592D, "Cirrus Logic CS4294", &null_ops}, - {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}, - {0x45838308, "ESS Allegro ES1988", &null_ops}, - {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */ - {0x4e534331, "National Semiconductor LM4549", &null_ops}, - {0x53494c22, "Silicon Laboratory Si3036", &null_ops}, - {0x53494c23, "Silicon Laboratory Si3038", &null_ops}, - {0x545200FF, "TriTech TR?????", &tritech_m_ops}, - {0x54524102, "TriTech TR28022", &null_ops}, - {0x54524103, "TriTech TR28023", &null_ops}, - {0x54524106, "TriTech TR28026", &null_ops}, - {0x54524108, "TriTech TR28028", &tritech_ops}, - {0x54524123, "TriTech TR A5", &null_ops}, - {0x574D4C00, "Wolfson WM9704", &wolfson_ops}, - {0x574D4C03, "Wolfson WM9703/9704", &wolfson_ops}, - {0x574D4C04, "Wolfson WM9704 (quad)", &wolfson_ops}, - {0x83847600, "SigmaTel STAC????", &null_ops}, - {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops}, - {0x83847605, "SigmaTel STAC9704", &null_ops}, - {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops}, - {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops}, - {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops}, - {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops}, - {0x83847684, "SigmaTel STAC9783/84?", &null_ops}, - {0x57454301, "Winbond 83971D", &null_ops}, -}; - -static const char *ac97_stereo_enhancements[] = -{ - /* 0 */ "No 3D Stereo Enhancement", - /* 1 */ "Analog Devices Phat Stereo", - /* 2 */ "Creative Stereo Enhancement", - /* 3 */ "National Semi 3D Stereo Enhancement", - /* 4 */ "YAMAHA Ymersion", - /* 5 */ "BBE 3D Stereo Enhancement", - /* 6 */ "Crystal Semi 3D Stereo Enhancement", - /* 7 */ "Qsound QXpander", - /* 8 */ "Spatializer 3D Stereo Enhancement", - /* 9 */ "SRS 3D Stereo Enhancement", - /* 10 */ "Platform Tech 3D Stereo Enhancement", - /* 11 */ "AKM 3D Audio", - /* 12 */ "Aureal Stereo Enhancement", - /* 13 */ "Aztech 3D Enhancement", - /* 14 */ "Binaura 3D Audio Enhancement", - /* 15 */ "ESS Technology Stereo Enhancement", - /* 16 */ "Harman International VMAx", - /* 17 */ "Nvidea 3D Stereo Enhancement", - /* 18 */ "Philips Incredible Sound", - /* 19 */ "Texas Instruments 3D Stereo Enhancement", - /* 20 */ "VLSI Technology 3D Stereo Enhancement", - /* 21 */ "TriTech 3D Stereo Enhancement", - /* 22 */ "Realtek 3D Stereo Enhancement", - /* 23 */ "Samsung 3D Stereo Enhancement", - /* 24 */ "Wolfson Microelectronics 3D Enhancement", - /* 25 */ "Delta Integration 3D Enhancement", - /* 26 */ "SigmaTel 3D Enhancement", - /* 27 */ "Winbond 3D Stereo Enhancement", - /* 28 */ "Rockwell 3D Stereo Enhancement", - /* 29 */ "Reserved 29", - /* 30 */ "Reserved 30", - /* 31 */ "Reserved 31" -}; - -/* this table has default mixer values for all OSS mixers. */ -static struct mixer_defaults { - int mixer; - unsigned int value; -} mixer_defaults[SOUND_MIXER_NRDEVICES] = { - /* all values 0 -> 100 in bytes */ - {SOUND_MIXER_VOLUME, 0x4343}, - {SOUND_MIXER_BASS, 0x4343}, - {SOUND_MIXER_TREBLE, 0x4343}, - {SOUND_MIXER_PCM, 0x4343}, - {SOUND_MIXER_SPEAKER, 0x4343}, - {SOUND_MIXER_LINE, 0x4343}, - {SOUND_MIXER_MIC, 0x0000}, - {SOUND_MIXER_CD, 0x4343}, - {SOUND_MIXER_ALTPCM, 0x4343}, - {SOUND_MIXER_IGAIN, 0x4343}, - {SOUND_MIXER_LINE1, 0x4343}, - {SOUND_MIXER_PHONEIN, 0x4343}, - {SOUND_MIXER_PHONEOUT, 0x4343}, - {SOUND_MIXER_VIDEO, 0x4343}, - {-1,0} -}; - -/* table to scale scale from OSS mixer value to AC97 mixer register value */ -static struct ac97_mixer_hw { - unsigned char offset; - int scale; -} ac97_hw[SOUND_MIXER_NRDEVICES]= { - [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,64}, - [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 16}, - [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 16}, - [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 32}, - [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 16}, - [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 32}, - [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 32}, - [SOUND_MIXER_CD] = {AC97_CD_VOL, 32}, - [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 64}, - [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 16}, - [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 32}, - [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 32}, - [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 64}, - [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 32}, -}; - -/* the following tables allow us to go from OSS <-> ac97 quickly. */ -enum ac97_recsettings { - AC97_REC_MIC=0, - AC97_REC_CD, - AC97_REC_VIDEO, - AC97_REC_AUX, - AC97_REC_LINE, - AC97_REC_STEREO, /* combination of all enabled outputs.. */ - AC97_REC_MONO, /*.. or the mono equivalent */ - AC97_REC_PHONE -}; - -static const unsigned int ac97_rm2oss[] = { - [AC97_REC_MIC] = SOUND_MIXER_MIC, - [AC97_REC_CD] = SOUND_MIXER_CD, - [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO, - [AC97_REC_AUX] = SOUND_MIXER_LINE1, - [AC97_REC_LINE] = SOUND_MIXER_LINE, - [AC97_REC_STEREO]= SOUND_MIXER_IGAIN, - [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN -}; - -/* indexed by bit position */ -static const unsigned int ac97_oss_rm[] = { - [SOUND_MIXER_MIC] = AC97_REC_MIC, - [SOUND_MIXER_CD] = AC97_REC_CD, - [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, - [SOUND_MIXER_LINE1] = AC97_REC_AUX, - [SOUND_MIXER_LINE] = AC97_REC_LINE, - [SOUND_MIXER_IGAIN] = AC97_REC_STEREO, - [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE -}; - -/* 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) -{ - u16 val; - int ret = 0; - int scale; - struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; - - val = codec->codec_read(codec , mh->offset); - - if (val & AC97_MUTE) { - ret = 0; - } else if (AC97_STEREO_MASK & (1 << oss_channel)) { - /* nice stereo mixers .. */ - int left,right; - - left = (val >> 8) & 0x7f; - right = val & 0x7f; - - if (oss_channel == SOUND_MIXER_IGAIN) { - right = (right * 100) / mh->scale; - left = (left * 100) / mh->scale; - } else { - /* these may have 5 or 6 bit resolution */ - if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM) - scale = (1 << codec->bit_resolution); - else - scale = mh->scale; - - right = 100 - ((right * 100) / scale); - left = 100 - ((left * 100) / scale); - } - ret = left | (right << 8); - } else if (oss_channel == SOUND_MIXER_SPEAKER) { - ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); - } else if (oss_channel == SOUND_MIXER_PHONEIN) { - ret = 100 - (((val & 0x1f) * 100) / mh->scale); - } else if (oss_channel == SOUND_MIXER_PHONEOUT) { - scale = (1 << codec->bit_resolution); - ret = 100 - (((val & 0x1f) * 100) / scale); - } else if (oss_channel == SOUND_MIXER_MIC) { - ret = 100 - (((val & 0x1f) * 100) / mh->scale); - /* the low bit is optional in the tone sliders and masking - it lets us avoid the 0xf 'bypass'.. */ - } else if (oss_channel == SOUND_MIXER_BASS) { - ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); - } else if (oss_channel == SOUND_MIXER_TREBLE) { - ret = 100 - (((val & 0xe) * 100) / mh->scale); - } - -#ifdef DEBUG - printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), " - "0x%04x -> 0x%04x\n", - oss_channel, codec->id ? "Secondary" : "Primary", - mh->offset, val, ret); -#endif - - return ret; -} - -/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to - make sure all is well in arg land, call with spinlock held */ -static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, - unsigned int left, unsigned int right) -{ - u16 val = 0; - int scale; - struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; - -#ifdef DEBUG - printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), " - "left vol:%2d, right vol:%2d:", - oss_channel, codec->id ? "Secondary" : "Primary", - mh->offset, left, right); -#endif - - if (AC97_STEREO_MASK & (1 << oss_channel)) { - /* stereo mixers */ - if (left == 0 && right == 0) { - val = AC97_MUTE; - } else { - if (oss_channel == SOUND_MIXER_IGAIN) { - right = (right * mh->scale) / 100; - left = (left * mh->scale) / 100; - if (right >= mh->scale) - right = mh->scale-1; - if (left >= mh->scale) - left = mh->scale-1; - } else { - /* these may have 5 or 6 bit resolution */ - if (oss_channel == SOUND_MIXER_VOLUME || - oss_channel == SOUND_MIXER_ALTPCM) - scale = (1 << codec->bit_resolution); - else - scale = mh->scale; - - right = ((100 - right) * scale) / 100; - left = ((100 - left) * scale) / 100; - if (right >= scale) - right = scale-1; - if (left >= scale) - left = scale-1; - } - val = (left << 8) | right; - } - } else if (oss_channel == SOUND_MIXER_BASS) { - val = codec->codec_read(codec , mh->offset) & ~0x0f00; - left = ((100 - left) * mh->scale) / 100; - if (left >= mh->scale) - left = mh->scale-1; - val |= (left << 8) & 0x0e00; - } else if (oss_channel == SOUND_MIXER_TREBLE) { - val = codec->codec_read(codec , mh->offset) & ~0x000f; - left = ((100 - left) * mh->scale) / 100; - if (left >= mh->scale) - left = mh->scale-1; - val |= left & 0x000e; - } else if(left == 0) { - val = AC97_MUTE; - } else if (oss_channel == SOUND_MIXER_SPEAKER) { - left = ((100 - left) * mh->scale) / 100; - if (left >= mh->scale) - left = mh->scale-1; - val = left << 1; - } else if (oss_channel == SOUND_MIXER_PHONEIN) { - left = ((100 - left) * mh->scale) / 100; - if (left >= mh->scale) - left = mh->scale-1; - val = left; - } else if (oss_channel == SOUND_MIXER_PHONEOUT) { - scale = (1 << codec->bit_resolution); - left = ((100 - left) * scale) / 100; - if (left >= mh->scale) - left = mh->scale-1; - val = left; - } else if (oss_channel == SOUND_MIXER_MIC) { - val = codec->codec_read(codec , mh->offset) & ~0x801f; - left = ((100 - left) * mh->scale) / 100; - if (left >= mh->scale) - left = mh->scale-1; - val |= left; - /* the low bit is optional in the tone sliders and masking - it lets us avoid the 0xf 'bypass'.. */ - } -#ifdef DEBUG - printk(" 0x%04x", val); -#endif - - codec->codec_write(codec, mh->offset, val); - -#ifdef DEBUG - val = codec->codec_read(codec, mh->offset); - printk(" -> 0x%04x\n", val); -#endif -} - -/* a thin wrapper for write_mixer */ -static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ) -{ - unsigned int left,right; - - /* cleanse input a little */ - right = ((val >> 8) & 0xff) ; - left = (val & 0xff) ; - - if (right > 100) right = 100; - if (left > 100) left = 100; - - codec->mixer_state[oss_mixer] = (right << 8) | left; - codec->write_mixer(codec, oss_mixer, left, right); -} - -/* read or write the recmask, the ac97 can really have left and right recording - 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) -{ - unsigned int val; - - if (rw) { - /* read it from the card */ - val = codec->codec_read(codec, AC97_RECORD_SELECT); -#ifdef DEBUG - printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val); -#endif - return (1 << ac97_rm2oss[val & 0x07]); - } - - /* else, write the first set in the mask as the - output */ - /* clear out current set value first (AC97 supports only 1 input!) */ - val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]); - if (mask != val) - mask &= ~val; - - val = ffs(mask); - val = ac97_oss_rm[val-1]; - val |= val << 8; /* set both channels */ - -#ifdef DEBUG - printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val); -#endif - - codec->codec_write(codec, AC97_RECORD_SELECT, val); - - return 0; -}; - -static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) -{ - int i, val = 0; - - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - 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; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - 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; - } - - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *)arg); - - if (_SIOC_DIR(cmd) == _SIOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* give them the current record source */ - if (!codec->recmask_io) { - val = 0; - } else { - val = codec->recmask_io(codec, 1, 0); - } - break; - - case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ - val = codec->supported_mixers; - break; - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - val = codec->record_sources; - break; - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - val = codec->stereo_mixers; - break; - - case SOUND_MIXER_CAPS: - val = SOUND_CAP_EXCL_INPUT; - break; - - default: /* read a specific mixer */ - i = _IOC_NR(cmd); - - if (!supported_mixer(codec, i)) - return -EINVAL; - - /* do we ever want to touch the hardware? */ - /* val = codec->read_mixer(codec, i); */ - val = codec->mixer_state[i]; - break; - } - return put_user(val, (int *)arg); - } - - if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) { - codec->modcnt++; - if (get_user(val, (int *)arg)) - return -EFAULT; - - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - if (!codec->recmask_io) return -EINVAL; - if (!val) return 0; - if (!(val &= codec->record_sources)) return -EINVAL; - - codec->recmask_io(codec, 0, val); - - return 0; - default: /* write a specific mixer */ - i = _IOC_NR(cmd); - - if (!supported_mixer(codec, i)) - return -EINVAL; - - ac97_set_mixer(codec, i, val); - - return 0; - } - } - return -EINVAL; -} - -/* entry point for /proc/driver/controller_vendor/ac97/%d */ -int ac97_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = 0, cap, extid, val, id1, id2; - struct ac97_codec *codec; - int is_ac97_20 = 0; - - if ((codec = data) == NULL) - return -ENODEV; - - id1 = codec->codec_read(codec, AC97_VENDOR_ID1); - id2 = codec->codec_read(codec, AC97_VENDOR_ID2); - len += sprintf (page+len, "Vendor name : %s\n", codec->name); - len += sprintf (page+len, "Vendor id : %04X %04X\n", id1, id2); - - extid = codec->codec_read(codec, AC97_EXTENDED_ID); - extid &= ~((1<<2)|(1<<4)|(1<<5)|(1<<10)|(1<<11)|(1<<12)|(1<<13)); - len += sprintf (page+len, "AC97 Version : %s\n", - extid ? "2.0 or later" : "1.0"); - if (extid) is_ac97_20 = 1; - - cap = codec->codec_read(codec, AC97_RESET); - len += sprintf (page+len, "Capabilities :%s%s%s%s%s%s\n", - cap & 0x0001 ? " -dedicated MIC PCM IN channel-" : "", - cap & 0x0002 ? " -reserved1-" : "", - cap & 0x0004 ? " -bass & treble-" : "", - cap & 0x0008 ? " -simulated stereo-" : "", - cap & 0x0010 ? " -headphone out-" : "", - cap & 0x0020 ? " -loudness-" : ""); - val = cap & 0x00c0; - len += sprintf (page+len, "DAC resolutions :%s%s%s\n", - " -16-bit-", - val & 0x0040 ? " -18-bit-" : "", - val & 0x0080 ? " -20-bit-" : ""); - val = cap & 0x0300; - len += sprintf (page+len, "ADC resolutions :%s%s%s\n", - " -16-bit-", - val & 0x0100 ? " -18-bit-" : "", - val & 0x0200 ? " -20-bit-" : ""); - len += sprintf (page+len, "3D enhancement : %s\n", - ac97_stereo_enhancements[(cap >> 10) & 0x1f]); - - val = codec->codec_read(codec, AC97_GENERAL_PURPOSE); - len += sprintf (page+len, "POP path : %s 3D\n" - "Sim. stereo : %s\n" - "3D enhancement : %s\n" - "Loudness : %s\n" - "Mono output : %s\n" - "MIC select : %s\n" - "ADC/DAC loopback : %s\n", - val & 0x8000 ? "post" : "pre", - val & 0x4000 ? "on" : "off", - val & 0x2000 ? "on" : "off", - val & 0x1000 ? "on" : "off", - val & 0x0200 ? "MIC" : "MIX", - val & 0x0100 ? "MIC2" : "MIC1", - val & 0x0080 ? "on" : "off"); - - extid = codec->codec_read(codec, AC97_EXTENDED_ID); - cap = extid; - len += sprintf (page+len, "Ext Capabilities :%s%s%s%s%s%s%s\n", - cap & 0x0001 ? " -var rate PCM audio-" : "", - cap & 0x0002 ? " -2x PCM audio out-" : "", - cap & 0x0008 ? " -var rate MIC in-" : "", - cap & 0x0040 ? " -PCM center DAC-" : "", - cap & 0x0080 ? " -PCM surround DAC-" : "", - cap & 0x0100 ? " -PCM LFE DAC-" : "", - cap & 0x0200 ? " -slot/DAC mappings-" : ""); - if (is_ac97_20) { - len += sprintf (page+len, "Front DAC rate : %d\n", - codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)); - } - - return len; -} - -/** - * ac97_probe_codec - Initialize and setup AC97-compatible codec - * @codec: (in/out) Kernel info for a single AC97 codec - * - * Reset the AC97 codec, then initialize the mixer and - * the rest of the @codec structure. - * - * The codec_read and codec_write fields of @codec are - * required to be setup and working when this function - * is called. All other fields are set by this function. - * - * codec_wait field of @codec can optionally be provided - * when calling this function. If codec_wait is not %NULL, - * this function will call codec_wait any time it is - * necessary to wait for the audio chip to reach the - * codec-ready state. If codec_wait is %NULL, then - * the default behavior is to call schedule_timeout. - * Currently codec_wait is used to wait for AC97 codec - * reset to complete. - * - * Returns 1 (true) on success, or 0 (false) on failure. - */ - -int ac97_probe_codec(struct ac97_codec *codec) -{ - u16 id1, id2; - u16 audio, modem; - int i; - - /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should - * be read zero. - * - * FIXME: is the following comment outdated? -jgarzik - * Probing of AC97 in this way is not reliable, it is not even SAFE !! - */ - codec->codec_write(codec, AC97_RESET, 0L); - - /* also according to spec, we wait for codec-ready state */ - if (codec->codec_wait) - codec->codec_wait(codec); - else - udelay(10); - - if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) { - printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n", - codec->id ? "Secondary" : "Primary"); - return 0; - } - - /* probe for Modem Codec */ - codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); - modem = codec->codec_read(codec, AC97_EXTENDED_MODEM_ID); - - codec->name = NULL; - codec->codec_ops = &null_ops; - - id1 = codec->codec_read(codec, AC97_VENDOR_ID1); - id2 = codec->codec_read(codec, AC97_VENDOR_ID2); - for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) { - if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) { - codec->type = ac97_codec_ids[i].id; - codec->name = ac97_codec_ids[i].name; - codec->codec_ops = ac97_codec_ids[i].ops; - break; - } - } - if (codec->name == NULL) - codec->name = "Unknown"; - printk(KERN_INFO "ac97_codec: AC97 %s codec, id: 0x%04x:" - "0x%04x (%s)\n", audio ? "Audio" : (modem ? "Modem" : ""), - id1, id2, codec->name); - - return ac97_init_mixer(codec); -} - -static int ac97_init_mixer(struct ac97_codec *codec) -{ - u16 cap; - int i; - - cap = codec->codec_read(codec, AC97_RESET); - - /* mixer masks */ - codec->supported_mixers = AC97_SUPPORTED_MASK; - codec->stereo_mixers = AC97_STEREO_MASK; - codec->record_sources = AC97_RECORD_MASK; - if (!(cap & 0x04)) - codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); - 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) == 0x1f1f) - codec->bit_resolution = 5; - else - codec->bit_resolution = 6; - - /* generic OSS to AC97 wrapper */ - codec->read_mixer = ac97_read_mixer; - codec->write_mixer = ac97_write_mixer; - codec->recmask_io = ac97_recmask_io; - codec->mixer_ioctl = ac97_mixer_ioctl; - - /* codec specific initialization for 4-6 channel output or secondary codec stuff */ - if (codec->codec_ops->init != NULL) { - codec->codec_ops->init(codec); - } - - /* initialize mixer channel volumes */ - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - struct mixer_defaults *md = &mixer_defaults[i]; - if (md->mixer == -1) - break; - if (!supported_mixer(codec, md->mixer)) - continue; - ac97_set_mixer(codec, md->mixer, md->value); - } - - return 1; -} - -#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */ -#define AC97_SIGMATEL_DAC2INVERT 0x6e -#define AC97_SIGMATEL_BIAS1 0x70 -#define AC97_SIGMATEL_BIAS2 0x72 -#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */ -#define AC97_SIGMATEL_CIC1 0x76 -#define AC97_SIGMATEL_CIC2 0x78 - - -static int sigmatel_9708_init(struct ac97_codec * codec) -{ - u16 codec72, codec6c; - - codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000; - codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG); - - if ((codec72==0) && (codec6c==0)) { - codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); - codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000); - codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba); - codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007); - } else if ((codec72==0x8000) && (codec6c==0)) { - codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); - codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001); - codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008); - } else if ((codec72==0x8000) && (codec6c==0x0080)) { - /* nothing */ - } - codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000); - return 0; -} - - -static int sigmatel_9721_init(struct ac97_codec * codec) -{ - /* Only set up secondary codec */ - if (codec->id == 0) - return 0; - - codec->codec_write(codec, AC97_SURROUND_MASTER, 0L); - - /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link - sloc 3,4 = 0x01, slot 7,8 = 0x00, */ - codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00); - - /* we don't have the crystal when we are on an AMR card, so use - BIT_CLK as our clock source. Write the magic word ABBA and read - back to enable register 0x78 */ - codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); - codec->codec_read(codec, AC97_SIGMATEL_CIC1); - - /* sync all the clocks*/ - codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802); - - return 0; -} - - -static int sigmatel_9744_init(struct ac97_codec * codec) -{ - // patch for SigmaTel - codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); - codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk - codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba); - codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002); - codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000); - return 0; -} - - -static int wolfson_init(struct ac97_codec * codec) -{ - 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); - return 0; -} - - -static int tritech_init(struct ac97_codec * codec) -{ - codec->codec_write(codec, 0x26, 0x0300); - codec->codec_write(codec, 0x26, 0x0000); - codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); - codec->codec_write(codec, AC97_RESERVED_3A, 0x0000); - return 0; -} - - -/* copied from drivers/sound/maestro.c */ -static int tritech_maestro_init(struct ac97_codec * codec) -{ - /* no idea what this does */ - codec->codec_write(codec, 0x2A, 0x0001); - codec->codec_write(codec, 0x2C, 0x0000); - codec->codec_write(codec, 0x2C, 0XFFFF); - return 0; -} - - -/* - * This is basically standard AC97. It should work as a default for - * almost all modern codecs. Note that some cards wire EAPD *backwards* - * That side of it is up to the card driver not us to cope with. - * - */ - -static int eapd_control(struct ac97_codec * codec, int on) -{ - if(on) - codec->codec_write(codec, AC97_POWER_CONTROL, - codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000); - else - codec->codec_write(codec, AC97_POWER_CONTROL, - codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000); - return 0; -} - -/* - * Crystal digital audio control (CS4299 - */ - -static int crystal_digital_control(struct ac97_codec *codec, int mode) -{ - u16 cv; - - switch(mode) - { - case 0: cv = 0x0; break; /* SPEN off */ - case 1: cv = 0x8004; break; /* 48KHz digital */ - case 2: cv = 0x8104; break; /* 44.1KHz digital */ - default: - return -1; /* Not supported yet(eg AC3) */ - } - codec->codec_write(codec, 0x68, 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 :) */ -static int pt101_init(struct ac97_codec * codec) -{ - printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); - /* who knows.. */ - codec->codec_write(codec, 0x2A, 0x0001); - codec->codec_write(codec, 0x2C, 0x0000); - codec->codec_write(codec, 0x2C, 0xFFFF); - codec->codec_write(codec, 0x10, 0x9F1F); - codec->codec_write(codec, 0x12, 0x0808); - codec->codec_write(codec, 0x14, 0x9F1F); - codec->codec_write(codec, 0x16, 0x9F1F); - codec->codec_write(codec, 0x18, 0x0404); - codec->codec_write(codec, 0x1A, 0x0000); - codec->codec_write(codec, 0x1C, 0x0000); - codec->codec_write(codec, 0x02, 0x0404); - codec->codec_write(codec, 0x04, 0x0808); - codec->codec_write(codec, 0x0C, 0x801F); - codec->codec_write(codec, 0x0E, 0x801F); - return 0; -} -#endif - - -EXPORT_SYMBOL(ac97_read_proc); -EXPORT_SYMBOL(ac97_probe_codec); - -/* - * AC97 library support routines - */ - -/** - * ac97_set_dac_rate - set codec rate adaption - * @codec: ac97 code - * @rate: rate in hertz - * - * Set the DAC rate. Assumes the codec supports VRA. The caller is - * expected to have checked this little detail. - */ - -unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate) -{ - unsigned int new_rate = rate; - u32 dacp; - u32 mast_vol, phone_vol, mono_vol, pcm_vol; - u32 mute_vol = 0x8000; /* The mute volume? */ - - if(rate != codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)) - { - /* Mute several registers */ - mast_vol = codec->codec_read(codec, AC97_MASTER_VOL_STEREO); - mono_vol = codec->codec_read(codec, AC97_MASTER_VOL_MONO); - phone_vol = codec->codec_read(codec, AC97_HEADPHONE_VOL); - pcm_vol = codec->codec_read(codec, AC97_PCMOUT_VOL); - codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mute_vol); - codec->codec_write(codec, AC97_MASTER_VOL_MONO, mute_vol); - codec->codec_write(codec, AC97_HEADPHONE_VOL, mute_vol); - codec->codec_write(codec, AC97_PCMOUT_VOL, mute_vol); - - /* Power down the DAC */ - dacp=codec->codec_read(codec, AC97_POWER_CONTROL); - codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0200); - /* Load the rate and read the effective rate */ - codec->codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate); - new_rate=codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE); - /* Power it back up */ - codec->codec_write(codec, AC97_POWER_CONTROL, dacp); - - /* Restore volumes */ - codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mast_vol); - codec->codec_write(codec, AC97_MASTER_VOL_MONO, mono_vol); - codec->codec_write(codec, AC97_HEADPHONE_VOL, phone_vol); - codec->codec_write(codec, AC97_PCMOUT_VOL, pcm_vol); - } - return new_rate; -} - -EXPORT_SYMBOL(ac97_set_dac_rate); - -/** - * ac97_set_adc_rate - set codec rate adaption - * @codec: ac97 code - * @rate: rate in hertz - * - * Set the ADC rate. Assumes the codec supports VRA. The caller is - * expected to have checked this little detail. - */ - -unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate) -{ - unsigned int new_rate = rate; - u32 dacp; - - if(rate != codec->codec_read(codec, AC97_PCM_LR_ADC_RATE)) - { - /* Power down the ADC */ - dacp=codec->codec_read(codec, AC97_POWER_CONTROL); - codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0100); - /* Load the rate and read the effective rate */ - codec->codec_write(codec, AC97_PCM_LR_ADC_RATE, rate); - new_rate=codec->codec_read(codec, AC97_PCM_LR_ADC_RATE); - /* Power it back up */ - codec->codec_write(codec, AC97_POWER_CONTROL, dacp); - } - return new_rate; -} - -EXPORT_SYMBOL(ac97_set_adc_rate); - -int ac97_save_state(struct ac97_codec *codec) -{ - return 0; -} - -EXPORT_SYMBOL(ac97_save_state); - -int ac97_restore_state(struct ac97_codec *codec) -{ - int i; - unsigned int left, right, val; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!supported_mixer(codec, i)) - continue; - - val = codec->mixer_state[i]; - right = val >> 8; - left = val & 0xff; - codec->write_mixer(codec, i, left, right); - } - return 0; -} - -EXPORT_SYMBOL(ac97_restore_state); - -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/aci.c b/drivers/sound/aci.c --- a/drivers/sound/aci.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,711 +0,0 @@ -/* - * Audio Command Interface (ACI) driver (sound/aci.c) - * - * ACI is a protocol used to communicate with the microcontroller on - * some sound cards produced by miro, e.g. the miroSOUND PCM12 and - * PCM20. The ACI has been developed for miro by Norberto Pellicci - * . Special thanks to both him and miro for - * providing the ACI specification. - * - * The main function of the ACI is to control the mixer and to get a - * product identification. On the PCM20, ACI also controls the radio - * tuner on this card, this is supported in the Video for Linux - * miropcm20 driver. - * - - * This is a fullfeatured implementation. Unsupported features - * are bugs... (: - * - * It is not longer necessary to load the mad16 module first. The - * user is currently responsible to set the mad16 mixer correctly. - * - * To toggle the solo mode for full duplex operation just use the OSS - * record switch for the pcm ('wave') controller. Robert - * - - * - * Revision history: - * - * 1995-11-10 Markus Kuhn - * First version written. - * 1995-12-31 Markus Kuhn - * Second revision, general code cleanup. - * 1996-05-16 Hannu Savolainen - * Integrated with other parts of the driver. - * 1996-05-28 Markus Kuhn - * Initialize CS4231A mixer, make ACI first mixer, - * use new private mixer API for solo mode. - * 1998-08-18 Ruurd Reitsma - * Small modification to export ACI functions and - * complete modularisation. - * 2000-06-20 Robert Siemer - * Don't initialize the CS4231A mixer anymore, so the code is - * working again, and other small changes to fit in todays - * kernels. - * 2000-08-26 Robert Siemer - * Clean up and rewrite for 2.4.x. Maybe it's SMP safe now... (: - * ioctl bugfix, and integration of solo-mode into OSS-API, - * added (OSS-limited) equalizer support, return value bugfix, - * changed param aci_reset to reset, new params: ide, wss. - * 2001-04-20 Robert Siemer - * even more cleanups... - * 2001-10-08 Arnaldo Carvalho de Melo - * Get rid of check_region, .bss optimizations, use set_current_state - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "sound_config.h" - -int aci_port; /* as determined by bit 4 in the OPTi 929 MC4 register */ -int aci_idcode[2]; /* manufacturer and product ID */ -int aci_version; /* ACI firmware version */ - -EXPORT_SYMBOL(aci_port); -EXPORT_SYMBOL(aci_idcode); -EXPORT_SYMBOL(aci_version); - -#include "aci.h" - - -static int aci_solo; /* status bit of the card that can't be * - * checked with ACI versions prior to 0xb0 */ -static int aci_amp; /* status bit for power-amp/line-out level - but I have no docs about what is what... */ -static int aci_micpreamp=3; /* microphone preamp-level that can't be * - * checked with ACI versions prior to 0xb0 */ - -static int mixer_device; -static struct semaphore aci_sem; - -#ifdef MODULE -static int reset; -MODULE_PARM(reset,"i"); -MODULE_PARM_DESC(reset,"When set to 1, reset aci mixer."); -#else -static int reset = 1; -#endif - -static int ide=-1; -MODULE_PARM(ide,"i"); -MODULE_PARM_DESC(ide,"1 enable, 0 disable ide-port - untested" - " default: do nothing"); -static int wss=-1; -MODULE_PARM(wss,"i"); -MODULE_PARM_DESC(wss,"change between ACI/WSS-mixer; use 0 and 1 - untested" - " default: do nothing; for PCM1-pro only"); - -#if DEBUG -static void print_bits(unsigned char c) -{ - int j; - printk(KERN_DEBUG "aci: "); - - for (j=7; j>=0; j--) { - printk("%d", (c >> j) & 0x1); - } - - printk("\n"); -} -#endif - -/* - * This busy wait code normally requires less than 15 loops and - * practically always less than 100 loops on my i486/DX2 66 MHz. - * - * Warning: Waiting on the general status flag after reseting the MUTE - * function can take a VERY long time, because the PCM12 does some kind - * of fade-in effect. For this reason, access to the MUTE function has - * not been implemented at all. - * - * - The OSS interface has no mute option. It takes about 3 seconds to - * fade-in on my PCM20. busy_wait() handles it great now... Robert - */ - -static int busy_wait(void) -{ - #define MINTIME 500 - long timeout; - unsigned char byte; - - for (timeout = 1; timeout <= MINTIME+30; timeout++) { - if (((byte=inb(BUSY_REGISTER)) & 1) == 0) { - if (timeout >= MINTIME) - printk(KERN_DEBUG "aci: Got READYFLAG in round %ld.\n", timeout-MINTIME); - return byte; - } - if (timeout >= MINTIME) { - long out=10*HZ; - switch (timeout-MINTIME) { - case 0 ... 9: - out /= 10; - case 10 ... 19: - out /= 10; - case 20 ... 30: - out /= 10; - default: - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(out); - break; - } - } - } - printk(KERN_WARNING "aci: busy_wait() time out.\n"); - return -EBUSY; -} - -/* The four ACI command types are fucked up. [-: - * implied is: 1w - special case for INIT - * write is: 2w1r - * read is: x(1w1r) where x is 1 or 2 (1 CHECK_SIG, 1 CHECK_STER, - * 1 VERSION, 2 IDCODE) - * the command is only in the first write, rest is protocol overhead - * - * indexed is technically a write and used for STATUS - * and the special case for TUNE is: 3w1r - * - * Here the new general sheme: TUNE --> aci_rw_cmd(x, y, z) - * indexed and write --> aci_rw_cmd(x, y, -1) - * implied and read (x=1) --> aci_rw_cmd(x, -1, -1) - * - * Read (x>=2) is not implemented (only used during initialization). - * Use aci_idcode[2] and aci_version... Robert - */ - -/* Some notes for error detection: theoretically it is possible. - * But it doubles the I/O-traffic from ww(r) to wwwrw(r) in the normal - * case and doesn't seem to be designed for that... Robert - */ - -static inline int aci_rawwrite(unsigned char byte) -{ - if (busy_wait() >= 0) { -#if DEBUG - printk(KERN_DEBUG "aci_rawwrite(%d)\n", byte); -#endif - outb(byte, COMMAND_REGISTER); - return 0; - } else - return -EBUSY; -} - -static inline int aci_rawread(void) -{ - unsigned char byte; - - if (busy_wait() >= 0) { - byte=inb(STATUS_REGISTER); -#if DEBUG - printk(KERN_DEBUG "%d = aci_rawread()\n", byte); -#endif - return byte; - } else - return -EBUSY; -} - - -int aci_rw_cmd(int write1, int write2, int write3) -{ - int write[] = {write1, write2, write3}; - int read = -EINTR, i; - - if (down_interruptible(&aci_sem)) - goto out; - - for (i=0; i<3; i++) { - if (write[i]< 0 || write[i] > 255) - break; - else { - read = aci_rawwrite(write[i]); - if (read < 0) - goto out_up; - } - - } - - read = aci_rawread(); -out_up: up(&aci_sem); -out: return read; -} - -EXPORT_SYMBOL(aci_rw_cmd); - -static int setvolume(caddr_t arg, - unsigned char left_index, unsigned char right_index) -{ - int vol, ret, uservol, buf; - - __get_user(uservol, (int *)arg); - - /* left channel */ - vol = uservol & 0xff; - if (vol > 100) - vol = 100; - vol = SCALE(100, 0x20, vol); - if ((buf=aci_write_cmd(left_index, 0x20 - vol))<0) - return buf; - ret = SCALE(0x20, 100, vol); - - - /* right channel */ - vol = (uservol >> 8) & 0xff; - if (vol > 100) - vol = 100; - vol = SCALE(100, 0x20, vol); - if ((buf=aci_write_cmd(right_index, 0x20 - vol))<0) - return buf; - ret |= SCALE(0x20, 100, vol) << 8; - - __put_user(ret, (int *)arg); - - return 0; -} - -static int getvolume(caddr_t arg, - unsigned char left_index, unsigned char right_index) -{ - int vol; - int buf; - - /* left channel */ - if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0) - return buf; - vol = SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0); - - /* right channel */ - if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0) - return buf; - vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8; - - __put_user(vol, (int *)arg); - - return 0; -} - - -/* The equalizer is somewhat strange on the ACI. From -12dB to +12dB - * write: 0xff..down.to..0x80==0x00..up.to..0x7f - */ - -static inline unsigned int eq_oss2aci(unsigned int vol) -{ - int boost=0; - unsigned int ret; - - if (vol > 100) - vol = 100; - if (vol > 50) { - vol -= 51; - boost=1; - } - if (boost) - ret=SCALE(49, 0x7e, vol)+1; - else - ret=0xff - SCALE(50, 0x7f, vol); - return ret; -} - -static inline unsigned int eq_aci2oss(unsigned int vol) -{ - if (vol < 0x80) - return SCALE(0x7f, 50, vol) + 50; - else - return SCALE(0x7f, 50, 0xff-vol); -} - - -static int setequalizer(caddr_t arg, - unsigned char left_index, unsigned char right_index) -{ - int buf; - unsigned int vol; - - __get_user(vol, (int *)arg); - - /* left channel */ - if ((buf=aci_write_cmd(left_index, eq_oss2aci(vol & 0xff)))<0) - return buf; - - /* right channel */ - if ((buf=aci_write_cmd(right_index, eq_oss2aci((vol>>8) & 0xff)))<0) - return buf; - - /* the ACI equalizer is more precise */ - return 0; -} - -static int getequalizer(caddr_t arg, - unsigned char left_index, unsigned char right_index) -{ - int buf; - unsigned int vol; - - /* left channel */ - if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0) - return buf; - vol = eq_aci2oss(buf); - - /* right channel */ - if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0) - return buf; - vol |= eq_aci2oss(buf) << 8; - - __put_user(vol, (int *)arg); - - return 0; -} - -static int aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) -{ - int vol, buf; - - switch (cmd) { - case SOUND_MIXER_WRITE_VOLUME: - return setvolume(arg, 0x01, 0x00); - case SOUND_MIXER_WRITE_CD: - return setvolume(arg, 0x3c, 0x34); - case SOUND_MIXER_WRITE_MIC: - return setvolume(arg, 0x38, 0x30); - case SOUND_MIXER_WRITE_LINE: - return setvolume(arg, 0x39, 0x31); - case SOUND_MIXER_WRITE_SYNTH: - return setvolume(arg, 0x3b, 0x33); - case SOUND_MIXER_WRITE_PCM: - return setvolume(arg, 0x3a, 0x32); - case MIXER_WRITE(SOUND_MIXER_RADIO): /* fall through */ - case SOUND_MIXER_WRITE_LINE1: /* AUX1 or radio */ - return setvolume(arg, 0x3d, 0x35); - case SOUND_MIXER_WRITE_LINE2: /* AUX2 */ - return setvolume(arg, 0x3e, 0x36); - case SOUND_MIXER_WRITE_BASS: /* set band one and two */ - if (aci_idcode[1]=='C') { - if ((buf=setequalizer(arg, 0x48, 0x40)) || - (buf=setequalizer(arg, 0x49, 0x41))); - return buf; - } - break; - case SOUND_MIXER_WRITE_TREBLE: /* set band six and seven */ - if (aci_idcode[1]=='C') { - if ((buf=setequalizer(arg, 0x4d, 0x45)) || - (buf=setequalizer(arg, 0x4e, 0x46))); - return buf; - } - break; - case SOUND_MIXER_WRITE_IGAIN: /* MIC pre-amp */ - if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { - __get_user(vol, (int *)arg); - vol = vol & 0xff; - if (vol > 100) - vol = 100; - vol = SCALE(100, 3, vol); - if ((buf=aci_write_cmd(ACI_WRITE_IGAIN, vol))<0) - return buf; - aci_micpreamp = vol; - vol = SCALE(3, 100, vol); - vol |= (vol << 8); - __put_user(vol, (int *)arg); - return 0; - } - break; - case SOUND_MIXER_WRITE_OGAIN: /* Power-amp/line-out level */ - if (aci_idcode[1]=='A' || aci_idcode[1]=='B') { - __get_user(buf, (int *)arg); - buf = buf & 0xff; - if (buf > 50) - vol = 1; - else - vol = 0; - if ((buf=aci_write_cmd(ACI_SET_POWERAMP, vol))<0) - return buf; - aci_amp = vol; - if (aci_amp) - buf = (100 || 100<<8); - else - buf = 0; - __put_user(buf, (int *)arg); - return 0; - } - break; - case SOUND_MIXER_WRITE_RECSRC: - /* handle solo mode control */ - __get_user(buf, (int *)arg); - /* unset solo when RECSRC for PCM is requested */ - if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { - vol = !(buf & SOUND_MASK_PCM); - if ((buf=aci_write_cmd(ACI_SET_SOLOMODE, vol))<0) - return buf; - aci_solo = vol; - } - buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE| - SOUND_MASK_SYNTH| SOUND_MASK_LINE2); - if (aci_idcode[1] == 'C') /* PCM20 radio */ - buf |= SOUND_MASK_RADIO; - else - buf |= SOUND_MASK_LINE1; - if (!aci_solo) - buf |= SOUND_MASK_PCM; - __put_user(buf, (int *)arg); - return 0; - case SOUND_MIXER_READ_DEVMASK: - buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD | - SOUND_MASK_MIC | SOUND_MASK_LINE | - SOUND_MASK_SYNTH | SOUND_MASK_PCM | - SOUND_MASK_LINE2); - switch (aci_idcode[1]) { - case 'C': /* PCM20 radio */ - buf |= (SOUND_MASK_RADIO | SOUND_MASK_IGAIN | - SOUND_MASK_BASS | SOUND_MASK_TREBLE); - break; - case 'B': /* PCM12 */ - buf |= (SOUND_MASK_LINE1 | SOUND_MASK_IGAIN | - SOUND_MASK_OGAIN); - break; - case 'A': /* PCM1-pro */ - buf |= (SOUND_MASK_LINE1 | SOUND_MASK_OGAIN); - break; - default: - buf |= SOUND_MASK_LINE1; - } - __put_user(buf, (int *)arg); - return 0; - case SOUND_MIXER_READ_STEREODEVS: - buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD | - SOUND_MASK_MIC | SOUND_MASK_LINE | - SOUND_MASK_SYNTH | SOUND_MASK_PCM | - SOUND_MASK_LINE2); - switch (aci_idcode[1]) { - case 'C': /* PCM20 radio */ - buf |= (SOUND_MASK_RADIO | - SOUND_MASK_BASS | SOUND_MASK_TREBLE); - break; - default: - buf |= SOUND_MASK_LINE1; - } - __put_user(buf, (int *)arg); - return 0; - case SOUND_MIXER_READ_RECMASK: - buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE| - SOUND_MASK_SYNTH| SOUND_MASK_LINE2| SOUND_MASK_PCM); - if (aci_idcode[1] == 'C') /* PCM20 radio */ - buf |= SOUND_MASK_RADIO; - else - buf |= SOUND_MASK_LINE1; - - __put_user(buf, (int *)arg); - return 0; - case SOUND_MIXER_READ_RECSRC: - buf = (SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | - SOUND_MASK_SYNTH | SOUND_MASK_LINE2); - /* do we need aci_solo or can I get it from the ACI? */ - switch (aci_idcode[1]) { - case 'B': /* PCM12 */ - case 'C': /* PCM20 radio */ - if (aci_version >= 0xb0) { - if ((vol=aci_rw_cmd(ACI_STATUS, - ACI_S_GENERAL, -1))<0) - return vol; - if (vol & 0x20) - buf |= SOUND_MASK_PCM; - } - else - if (!aci_solo) - buf |= SOUND_MASK_PCM; - break; - default: - buf |= SOUND_MASK_PCM; - } - if (aci_idcode[1] == 'C') /* PCM20 radio */ - buf |= SOUND_MASK_RADIO; - else - buf |= SOUND_MASK_LINE1; - - __put_user(buf, (int *)arg); - return 0; - case SOUND_MIXER_READ_CAPS: - __put_user(0, (int *)arg); - return 0; - case SOUND_MIXER_READ_VOLUME: - return getvolume(arg, 0x04, 0x03); - case SOUND_MIXER_READ_CD: - return getvolume(arg, 0x0a, 0x09); - case SOUND_MIXER_READ_MIC: - return getvolume(arg, 0x06, 0x05); - case SOUND_MIXER_READ_LINE: - return getvolume(arg, 0x08, 0x07); - case SOUND_MIXER_READ_SYNTH: - return getvolume(arg, 0x0c, 0x0b); - case SOUND_MIXER_READ_PCM: - return getvolume(arg, 0x0e, 0x0d); - case MIXER_READ(SOUND_MIXER_RADIO): /* fall through */ - case SOUND_MIXER_READ_LINE1: /* AUX1 */ - return getvolume(arg, 0x11, 0x10); - case SOUND_MIXER_READ_LINE2: /* AUX2 */ - return getvolume(arg, 0x13, 0x12); - case SOUND_MIXER_READ_BASS: /* get band one */ - if (aci_idcode[1]=='C') { - return getequalizer(arg, 0x23, 0x22); - } - break; - case SOUND_MIXER_READ_TREBLE: /* get band seven */ - if (aci_idcode[1]=='C') { - return getequalizer(arg, 0x2f, 0x2e); - } - break; - case SOUND_MIXER_READ_IGAIN: /* MIC pre-amp */ - if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { - /* aci_micpreamp or ACI? */ - if (aci_version >= 0xb0) { - if ((buf=aci_indexed_cmd(ACI_STATUS, - ACI_S_READ_IGAIN))<0) - return buf; - } - else - buf=aci_micpreamp; - vol = SCALE(3, 100, buf <= 3 ? buf : 3); - vol |= vol << 8; - __put_user(vol, (int *)arg); - return 0; - } - break; - case SOUND_MIXER_READ_OGAIN: - if (aci_amp) - buf = (100 || 100<<8); - else - buf = 0; - __put_user(buf, (int *)arg); - return 0; - } - return -EINVAL; -} - -static struct mixer_operations aci_mixer_operations = -{ - owner: THIS_MODULE, - id: "ACI", - ioctl: aci_mixer_ioctl -}; - -/* - * There is also an internal mixer in the codec (CS4231A or AD1845), - * that deserves no purpose in an ACI based system which uses an - * external ACI controlled stereo mixer. Make sure that this codec - * mixer has the AUX1 input selected as the recording source, that the - * input gain is set near maximum and that the other channels going - * from the inputs to the codec output are muted. - */ - -static int __init attach_aci(void) -{ - char *boardname; - int i, rc = -EBUSY; - - init_MUTEX(&aci_sem); - - outb(0xE3, 0xf8f); /* Write MAD16 password */ - aci_port = (inb(0xf90) & 0x10) ? - 0x344: 0x354; /* Get aci_port from MC4_PORT */ - - if (!request_region(aci_port, 3, "sound mixer (ACI)")) { - printk(KERN_NOTICE - "aci: I/O area 0x%03x-0x%03x already used.\n", - aci_port, aci_port+2); - goto out; - } - - /* force ACI into a known state */ - rc = -EFAULT; - for (i=0; i<3; i++) - if (aci_rw_cmd(ACI_ERROR_OP, -1, -1)<0) - goto out_release_region; - - /* official this is one aci read call: */ - rc = -EFAULT; - if ((aci_idcode[0]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0 || - (aci_idcode[1]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0) { - printk(KERN_ERR "aci: Failed to read idcode on 0x%03x.\n", - aci_port); - goto out_release_region; - } - - if ((aci_version=aci_rw_cmd(ACI_READ_VERSION, -1, -1))<0) { - printk(KERN_ERR "aci: Failed to read version on 0x%03x.\n", - aci_port); - goto out_release_region; - } - - if (aci_idcode[0] == 'm') { - /* It looks like a miro sound card. */ - switch (aci_idcode[1]) { - case 'A': - boardname = "PCM1 pro / early PCM12"; - break; - case 'B': - boardname = "PCM12"; - break; - case 'C': - boardname = "PCM20 radio"; - break; - default: - boardname = "unknown miro"; - } - } else { - printk(KERN_WARNING "aci: Warning: unsupported card! - " - "no hardware, no specs...\n"); - boardname = "unknown Cardinal Technologies"; - } - - printk(KERN_INFO " at 0x%03x\n", - aci_version, - aci_idcode[0], aci_idcode[1], - aci_idcode[0], aci_idcode[1], - boardname, aci_port); - - rc = -EBUSY; - if (reset) { - /* first write()s after reset fail with my PCM20 */ - if (aci_rw_cmd(ACI_INIT, -1, -1)<0 || - aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0 || - aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0) - goto out_release_region; - } - - /* the PCM20 is muted after reset (and reboot) */ - if (aci_rw_cmd(ACI_SET_MUTE, 0x00, -1)<0) - goto out_release_region; - - if (ide>=0) - if (aci_rw_cmd(ACI_SET_IDE, !ide, -1)<0) - goto out_release_region; - - if (wss>=0 && aci_idcode[1]=='A') - if (aci_rw_cmd(ACI_SET_WSS, !!wss, -1)<0) - goto out_release_region; - - mixer_device = sound_install_mixer(MIXER_DRIVER_VERSION, boardname, - &aci_mixer_operations, - sizeof(aci_mixer_operations), NULL); - rc = 0; - if (mixer_device < 0) { - printk(KERN_ERR "aci: Failed to install mixer.\n"); - rc = mixer_device; - goto out_release_region; - } /* else Maybe initialize the CS4231A mixer here... */ -out: return rc; -out_release_region: - release_region(aci_port, 3); - goto out; -} - -static void __exit unload_aci(void) -{ - sound_unload_mixerdev(mixer_device); - release_region(aci_port, 3); -} - -module_init(attach_aci); -module_exit(unload_aci); -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/aci.h b/drivers/sound/aci.h --- a/drivers/sound/aci.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,58 +0,0 @@ -#ifndef _ACI_H_ -#define _ACI_H_ - -extern int aci_port; -extern int aci_idcode[2]; /* manufacturer and product ID */ -extern int aci_version; /* ACI firmware version */ -extern int aci_rw_cmd(int write1, int write2, int write3); - -#define aci_indexed_cmd(a, b) aci_rw_cmd(a, b, -1) -#define aci_write_cmd(a, b) aci_rw_cmd(a, b, -1) -#define aci_read_cmd(a) aci_rw_cmd(a,-1, -1) - -#define COMMAND_REGISTER (aci_port) /* write register */ -#define STATUS_REGISTER (aci_port + 1) /* read register */ -#define BUSY_REGISTER (aci_port + 2) /* also used for rds */ - -#define RDS_REGISTER BUSY_REGISTER - -#define ACI_SET_MUTE 0x0d -#define ACI_SET_POWERAMP 0x0f -#define ACI_SET_TUNERMUTE 0xa3 -#define ACI_SET_TUNERMONO 0xa4 -#define ACI_SET_IDE 0xd0 -#define ACI_SET_WSS 0xd1 -#define ACI_SET_SOLOMODE 0xd2 -#define ACI_WRITE_IGAIN 0x03 -#define ACI_WRITE_TUNE 0xa7 -#define ACI_READ_TUNERSTEREO 0xa8 -#define ACI_READ_TUNERSTATION 0xa9 -#define ACI_READ_VERSION 0xf1 -#define ACI_READ_IDCODE 0xf2 -#define ACI_INIT 0xff -#define ACI_STATUS 0xf0 -#define ACI_S_GENERAL 0x00 -#define ACI_S_READ_IGAIN 0x21 -#define ACI_ERROR_OP 0xdf - -/* - * The following macro SCALE can be used to scale one integer volume - * value into another one using only integer arithmetic. If the input - * value x is in the range 0 <= x <= xmax, then the result will be in - * the range 0 <= SCALE(xmax,ymax,x) <= ymax. - * - * This macro has for all xmax, ymax > 0 and all 0 <= x <= xmax the - * following nice properties: - * - * - SCALE(xmax,ymax,xmax) = ymax - * - SCALE(xmax,ymax,0) = 0 - * - SCALE(xmax,ymax,SCALE(ymax,xmax,SCALE(xmax,ymax,x))) = SCALE(xmax,ymax,x) - * - * In addition, the rounding error is minimal and nicely distributed. - * The proofs are left as an exercise to the reader. - */ - -#define SCALE(xmax,ymax,x) (((x)*(ymax)+(xmax)/2)/(xmax)) - - -#endif /* _ACI_H_ */ diff -Nru a/drivers/sound/ad1816.c b/drivers/sound/ad1816.c --- a/drivers/sound/ad1816.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1478 +0,0 @@ -/* - * - * AD1816 lowlevel sound driver for Linux 2.2.0 and above - * - * Copyright (C) 1998 by Thorsten Knabe - * - * Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996 - * - * - * version: 1.3.1 - * status: experimental - * date: 1999/4/18 - * - * Changes: - * Oleg Drokin: Some cleanup of load/unload functions. 1998/11/24 - * - * Thorsten Knabe: attach and unload rewritten, - * some argument checks added 1998/11/30 - * - * Thorsten Knabe: Buggy isa bridge workaround added 1999/01/16 - * - * David Moews/Thorsten Knabe: Introduced options - * parameter. Added slightly modified patch from - * David Moews to disable dsp audio sources by setting - * bit 0 of options parameter. This seems to be - * required by some Aztech/Newcom SC-16 cards. 1999/04/18 - * - * Christoph Hellwig: Adapted to module_init/module_exit. 2000/03/03 - * - * Christoph Hellwig: Added isapnp support 2000/03/15 - * - * Arnaldo Carvalho de Melo: get rid of check_region 2001/10/07 - */ - -#include -#include -#include -#include -#include - -#include "sound_config.h" - -#define DEBUGNOISE(x) -#define DEBUGINFO(x) -#define DEBUGLOG(x) -#define DEBUGWARN(x) - -#define CHECK_FOR_POWER { int timeout=100; \ - while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\ - timeout--; \ - } \ - if (timeout==0) {\ - printk(KERN_WARNING "ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \ - } \ -} - -/* structure to hold device specific information */ -typedef struct -{ - int base; /* set in attach */ - int irq; - int dma_playback; - int dma_capture; - - int speed; /* open */ - int channels; - int audio_format; - unsigned char format_bits; - int audio_mode; - int opened; - - int recmask; /* setup */ - int supported_devices; - int supported_rec_devices; - unsigned short levels[SOUND_MIXER_NRDEVICES]; - int dev_no; /* this is the # in audio_devs and NOT - in ad1816_info */ - int irq_ok; - int *osp; - -} ad1816_info; - -static int nr_ad1816_devs; -static int ad1816_clockfreq = 33000; -static int options; - -/* for backward mapping of irq to sound device */ - -static volatile char irq2dev[17] = {-1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1}; - - -/* supported audio formats */ -static int ad_format_mask = -AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW; - -/* array of device info structures */ -static ad1816_info dev_info[MAX_AUDIO_DEV]; - - -/* ------------------------------------------------------------------- */ - -/* functions for easier access to inderect registers */ - -static int ad_read (ad1816_info * devc, int reg) -{ - unsigned long flags; - int result; - - CHECK_FOR_POWER; - - save_flags (flags); /* make register access atomic */ - cli (); - outb ((unsigned char) (reg & 0x3f), devc->base+0); - result = inb(devc->base+2); - result+= inb(devc->base+3)<<8; - restore_flags (flags); - - return (result); -} - - -static void ad_write (ad1816_info * devc, int reg, int data) -{ - unsigned long flags; - - CHECK_FOR_POWER; - - save_flags (flags); /* make register access atomic */ - cli (); - outb ((unsigned char) (reg & 0xff), devc->base+0); - outb ((unsigned char) (data & 0xff),devc->base+2); - outb ((unsigned char) ((data>>8)&0xff),devc->base+3); - restore_flags (flags); - -} - -/* ------------------------------------------------------------------- */ - -/* function interface required by struct audio_driver */ - -static void ad1816_halt_input (int dev) -{ - unsigned long flags; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - unsigned char buffer; - - DEBUGINFO (printk("ad1816: halt_input called\n")); - - save_flags (flags); - cli (); - - if(!isa_dma_bridge_buggy) { - disable_dma(audio_devs[dev]->dmap_in->dma); - } - - buffer=inb(devc->base+9); - if (buffer & 0x01) { - /* disable capture */ - outb(buffer & ~0x01,devc->base+9); - } - - if(!isa_dma_bridge_buggy) { - enable_dma(audio_devs[dev]->dmap_in->dma); - } - - /* Clear interrupt status */ - outb (~0x40, devc->base+1); - - devc->audio_mode &= ~PCM_ENABLE_INPUT; - restore_flags (flags); -} - -static void ad1816_halt_output (int dev) -{ - unsigned long flags; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - unsigned char buffer; - - DEBUGINFO (printk("ad1816: halt_output called!\n")); - - save_flags (flags); - cli (); - /* Mute pcm output */ - ad_write(devc, 4, ad_read(devc,4)|0x8080); - - if(!isa_dma_bridge_buggy) { - disable_dma(audio_devs[dev]->dmap_out->dma); - } - - buffer=inb(devc->base+8); - if (buffer & 0x01) { - /* disable capture */ - outb(buffer & ~0x01,devc->base+8); - } - - if(!isa_dma_bridge_buggy) { - enable_dma(audio_devs[dev]->dmap_out->dma); - } - - /* Clear interrupt status */ - outb ((unsigned char)~0x80, devc->base+1); - - devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - restore_flags (flags); -} - -static void ad1816_output_block (int dev, unsigned long buf, - int count, int intrflag) -{ - unsigned long flags; - unsigned long cnt; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - DEBUGINFO(printk("ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); - - cnt = count/4 - 1; - - save_flags (flags); - cli (); - - /* set transfer count */ - ad_write (devc, 8, cnt & 0xffff); - - devc->audio_mode |= PCM_ENABLE_OUTPUT; - restore_flags (flags); -} - - -static void ad1816_start_input (int dev, unsigned long buf, int count, - int intrflag) -{ - unsigned long flags; - unsigned long cnt; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - DEBUGINFO(printk("ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); - - cnt = count/4 - 1; - - save_flags (flags); /* make register access atomic */ - cli (); - - /* set transfer count */ - ad_write (devc, 10, cnt & 0xffff); - - devc->audio_mode |= PCM_ENABLE_INPUT; - restore_flags (flags); -} - -static int ad1816_prepare_for_input (int dev, int bsize, int bcount) -{ - unsigned long flags; - unsigned int freq; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - unsigned char fmt_bits; - - DEBUGINFO (printk ("ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount)); - - save_flags (flags); - cli (); - - fmt_bits= (devc->format_bits&0x7)<<3; - - /* set mono/stereo mode */ - if (devc->channels > 1) { - fmt_bits |=0x4; - } - - /* set Mono/Stereo in playback/capture register */ - outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); - outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); - - /* If compiled into kernel, AD1816_CLOCK is defined, so use it */ -#ifdef AD1816_CLOCK - ad1816_clockfreq=AD1816_CLOCK; -#endif - - /* capture/playback frequency correction for soundcards - with clock chips != 33MHz (allowed range 5 - 100 kHz) */ - - if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { - ad1816_clockfreq=33000; - } - - freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; - - /* write playback/capture speeds */ - ad_write (devc, 2, freq & 0xffff); - ad_write (devc, 3, freq & 0xffff); - - restore_flags (flags); - - ad1816_halt_input(dev); - return 0; -} - -static int ad1816_prepare_for_output (int dev, int bsize, int bcount) -{ - unsigned long flags; - unsigned int freq; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - unsigned char fmt_bits; - - DEBUGINFO (printk ("ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount)); - - save_flags (flags); /* make register access atomic */ - cli (); - - fmt_bits= (devc->format_bits&0x7)<<3; - /* set mono/stereo mode */ - if (devc->channels > 1) { - fmt_bits |=0x4; - } - - /* write format bits to playback/capture registers */ - outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); - outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); - -#ifdef AD1816_CLOCK - ad1816_clockfreq=AD1816_CLOCK; -#endif - - /* capture/playback frequency correction for soundcards - with clock chips != 33MHz (allowed range 5 - 100 kHz)*/ - - if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { - ad1816_clockfreq=33000; - } - - freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; - - /* write playback/capture speeds */ - ad_write (devc, 2, freq & 0xffff); - ad_write (devc, 3, freq & 0xffff); - - restore_flags (flags); - - ad1816_halt_output(dev); - return 0; - -} - -static void ad1816_trigger (int dev, int state) -{ - unsigned long flags; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - DEBUGINFO (printk("ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base)); - - /* mode may have changed */ - - save_flags (flags); /* make register access atomic */ - cli (); - - /* mask out modes not specified on open call */ - state &= devc->audio_mode; - - /* setup soundchip to new io-mode */ - if (state & PCM_ENABLE_INPUT) { - /* enable capture */ - outb(inb(devc->base+9)|0x01, devc->base+9); - } else { - /* disable capture */ - outb(inb(devc->base+9)&~0x01, devc->base+9); - } - - if (state & PCM_ENABLE_OUTPUT) { - /* enable playback */ - outb(inb(devc->base+8)|0x01, devc->base+8); - /* unmute pcm output */ - ad_write(devc, 4, ad_read(devc,4)&~0x8080); - } else { - /* mute pcm output */ - ad_write(devc, 4, ad_read(devc,4)|0x8080); - /* disable capture */ - outb(inb(devc->base+8)&~0x01, devc->base+8); - } - restore_flags (flags); -} - - -/* halt input & output */ -static void ad1816_halt (int dev) -{ - ad1816_halt_input(dev); - ad1816_halt_output(dev); -} - -static void ad1816_reset (int dev) -{ - ad1816_halt (dev); -} - -/* set playback speed */ -static int ad1816_set_speed (int dev, int arg) -{ - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - if (arg == 0) { - return devc->speed; - } - /* range checking */ - if (arg < 4000) { - arg = 4000; - } - if (arg > 55000) { - arg = 55000; - } - - devc->speed = arg; - return devc->speed; - -} - -static unsigned int ad1816_set_bits (int dev, unsigned int arg) -{ - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - static struct format_tbl { - int format; - unsigned char bits; - } format2bits[] = { - { 0, 0 }, - { AFMT_MU_LAW, 1 }, - { AFMT_A_LAW, 3 }, - { AFMT_IMA_ADPCM, 0 }, - { AFMT_U8, 0 }, - { AFMT_S16_LE, 2 }, - { AFMT_S16_BE, 6 }, - { AFMT_S8, 0 }, - { AFMT_U16_LE, 0 }, - { AFMT_U16_BE, 0 } - }; - - int i, n = sizeof (format2bits) / sizeof (struct format_tbl); - - /* return current format */ - if (arg == 0) - return devc->audio_format; - - devc->audio_format = arg; - - /* search matching format bits */ - for (i = 0; i < n; i++) - if (format2bits[i].format == arg) { - devc->format_bits = format2bits[i].bits; - devc->audio_format = arg; - return arg; - } - - /* Still hanging here. Something must be terribly wrong */ - devc->format_bits = 0; - return devc->audio_format = AFMT_U8; -} - -static short ad1816_set_channels (int dev, short arg) -{ - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - if (arg != 1 && arg != 2) - return devc->channels; - - devc->channels = arg; - return arg; -} - -/* open device */ -static int ad1816_open (int dev, int mode) -{ - ad1816_info *devc = NULL; - unsigned long flags; - - /* is device number valid ? */ - if (dev < 0 || dev >= num_audiodevs) - return -(ENXIO); - - /* get device info of this dev */ - devc = (ad1816_info *) audio_devs[dev]->devc; - - /* make check if device already open atomic */ - save_flags (flags); - cli (); - - if (devc->opened) { - restore_flags (flags); - return -(EBUSY); - } - - /* mark device as open */ - devc->opened = 1; - - devc->audio_mode = 0; - devc->speed = 8000; - devc->audio_format=AFMT_U8; - devc->channels=1; - - ad1816_reset(devc->dev_no); /* halt all pending output */ - restore_flags (flags); - return 0; -} - -static void ad1816_close (int dev) /* close device */ -{ - unsigned long flags; - ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; - - save_flags (flags); - cli (); - - /* halt all pending output */ - ad1816_reset(devc->dev_no); - - devc->opened = 0; - devc->audio_mode = 0; - devc->speed = 8000; - devc->audio_format=AFMT_U8; - devc->format_bits = 0; - - - restore_flags (flags); -} - - -/* ------------------------------------------------------------------- */ - -/* Audio driver structure */ - -static struct audio_driver ad1816_audio_driver = -{ - owner: THIS_MODULE, - open: ad1816_open, - close: ad1816_close, - output_block: ad1816_output_block, - start_input: ad1816_start_input, - prepare_for_input: ad1816_prepare_for_input, - prepare_for_output: ad1816_prepare_for_output, - halt_io: ad1816_halt, - halt_input: ad1816_halt_input, - halt_output: ad1816_halt_output, - trigger: ad1816_trigger, - set_speed: ad1816_set_speed, - set_bits: ad1816_set_bits, - set_channels: ad1816_set_channels, -}; - - -/* ------------------------------------------------------------------- */ - -/* Interrupt handler */ - - -static void ad1816_interrupt (int irq, void *dev_id, struct pt_regs *dummy) -{ - unsigned char status; - ad1816_info *devc; - int dev; - unsigned long flags; - - - if (irq < 0 || irq > 15) { - printk(KERN_WARNING "ad1816: Got bogus interrupt %d\n", irq); - return; - } - - dev = irq2dev[irq]; - - if (dev < 0 || dev >= num_audiodevs) { - printk(KERN_WARNING "ad1816: IRQ2AD1816-mapping failed for " - "irq %d device %d\n", irq,dev); - return; - } - - devc = (ad1816_info *) audio_devs[dev]->devc; - - save_flags(flags); - cli(); - - /* read interrupt register */ - status = inb (devc->base+1); - /* Clear all interrupt */ - outb (~status, devc->base+1); - - DEBUGNOISE (printk("ad1816: Got interrupt subclass %d\n",status)); - - devc->irq_ok=1; - - if (status == 0) - DEBUGWARN(printk ("ad1816: interrupt: Got interrupt, but no reason?\n")); - - if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64)) - DMAbuf_inputintr (dev); - - if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128)) - DMAbuf_outputintr (dev, 1); - - restore_flags(flags); -} - -/* ------------------------------------------------------------------- */ - -/* Mixer stuff */ - -struct mixer_def { - unsigned int regno: 7; - unsigned int polarity:1; /* 0=normal, 1=reversed */ - unsigned int bitpos:4; - unsigned int nbits:4; -}; - -static char mix_cvt[101] = { - 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, - 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, - 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, - 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, - 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, - 100 -}; - -typedef struct mixer_def mixer_ent; - -/* - * Most of the mixer entries work in backwards. Setting the polarity field - * makes them to work correctly. - * - * The channel numbering used by individual soundcards is not fixed. Some - * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs. - * The current version doesn't try to compensate this. - */ - -#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r) \ - {{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}} - - -mixer_ent mix_devices[SOUND_MIXER_NRDEVICES][2] = { -MIX_ENT(SOUND_MIXER_VOLUME, 14, 1, 8, 5, 14, 1, 0, 5), -MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 5, 1, 8, 6, 5, 1, 0, 6), -MIX_ENT(SOUND_MIXER_PCM, 4, 1, 8, 6, 4, 1, 0, 6), -MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 18, 1, 8, 5, 18, 1, 0, 5), -MIX_ENT(SOUND_MIXER_MIC, 19, 1, 8, 5, 19, 1, 0, 5), -MIX_ENT(SOUND_MIXER_CD, 15, 1, 8, 5, 15, 1, 0, 5), -MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 20, 0, 8, 4, 20, 0, 0, 4), -MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 17, 1, 8, 5, 17, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE2, 16, 1, 8, 5, 16, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE3, 39, 0, 9, 4, 39, 1, 0, 5) -}; - - -static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] = -{ - 0x4343, /* Master Volume */ - 0x3232, /* Bass */ - 0x3232, /* Treble */ - 0x0000, /* FM */ - 0x4343, /* PCM */ - 0x0000, /* PC Speaker */ - 0x0000, /* Ext Line */ - 0x0000, /* Mic */ - 0x0000, /* CD */ - 0x0000, /* Recording monitor */ - 0x0000, /* SB PCM */ - 0x0000, /* Recording level */ - 0x0000, /* Input gain */ - 0x0000, /* Output gain */ - 0x0000, /* Line1 */ - 0x0000, /* Line2 */ - 0x0000 /* Line3 (usually line in)*/ -}; - -#define LEFT_CHN 0 -#define RIGHT_CHN 1 - - - -static int -ad1816_set_recmask (ad1816_info * devc, int mask) -{ - unsigned char recdev; - int i, n; - - mask &= devc->supported_rec_devices; - - n = 0; - /* Count selected device bits */ - for (i = 0; i < 32; i++) - if (mask & (1 << i)) - n++; - - if (n == 0) - mask = SOUND_MASK_MIC; - else if (n != 1) { /* Too many devices selected */ - /* Filter out active settings */ - mask &= ~devc->recmask; - - n = 0; - /* Count selected device bits */ - for (i = 0; i < 32; i++) - if (mask & (1 << i)) - n++; - - if (n != 1) - mask = SOUND_MASK_MIC; - } - - switch (mask) { - case SOUND_MASK_MIC: - recdev = 5; - break; - - case SOUND_MASK_LINE: - recdev = 0; - break; - - case SOUND_MASK_CD: - recdev = 2; - break; - - case SOUND_MASK_LINE1: - recdev = 4; - break; - - case SOUND_MASK_LINE2: - recdev = 3; - break; - - case SOUND_MASK_VOLUME: - recdev = 1; - break; - - default: - mask = SOUND_MASK_MIC; - recdev = 5; - } - - recdev <<= 4; - ad_write (devc, 20, - (ad_read (devc, 20) & 0x8f8f) | recdev | (recdev<<8)); - - devc->recmask = mask; - return mask; -} - -static void -change_bits (int *regval, int dev, int chn, int newval) -{ - unsigned char mask; - int shift; - - /* Reverse polarity*/ - - if (mix_devices[dev][chn].polarity == 1) - newval = 100 - newval; - - mask = (1 << mix_devices[dev][chn].nbits) - 1; - shift = mix_devices[dev][chn].bitpos; - /* Scale it */ - newval = (int) ((newval * mask) + 50) / 100; - /* Clear bits */ - *regval &= ~(mask << shift); - /* Set new value */ - *regval |= (newval & mask) << shift; -} - -static int -ad1816_mixer_get (ad1816_info * devc, int dev) -{ - DEBUGINFO(printk("ad1816: mixer_get called!\n")); - - /* range check + supported mixer check */ - if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) - return (-(EINVAL)); - if (!((1 << dev) & devc->supported_devices)) - return -(EINVAL); - - return devc->levels[dev]; -} - -static int -ad1816_mixer_set (ad1816_info * devc, int dev, int value) -{ - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int retvol; - - int regoffs; - int val; - int valmute; - - DEBUGINFO(printk("ad1816: mixer_set called!\n")); - - if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) - return -(EINVAL); - - if (left > 100) - left = 100; - if (left < 0) - left = 0; - if (right > 100) - right = 100; - if (right < 0) - right = 0; - - /* Mono control */ - if (mix_devices[dev][RIGHT_CHN].nbits == 0) - right = left; - retvol = left | (right << 8); - - /* Scale it */ - - left = mix_cvt[left]; - right = mix_cvt[right]; - - /* reject all mixers that are not supported */ - if (!(devc->supported_devices & (1 << dev))) - return -(EINVAL); - - /* sanity check */ - if (mix_devices[dev][LEFT_CHN].nbits == 0) - return -(EINVAL); - - /* keep precise volume internal */ - devc->levels[dev] = retvol; - - /* Set the left channel */ - regoffs = mix_devices[dev][LEFT_CHN].regno; - val = ad_read (devc, regoffs); - change_bits (&val, dev, LEFT_CHN, left); - - valmute=val; - - /* Mute bit masking on some registers */ - if ( regoffs==5 || regoffs==14 || regoffs==15 || - regoffs==16 || regoffs==17 || regoffs==18 || - regoffs==19 || regoffs==39) { - if (left==0) - valmute |= 0x8000; - else - valmute &= ~0x8000; - } - ad_write (devc, regoffs, valmute); /* mute */ - - /* - * Set the right channel - */ - - /* Was just a mono channel */ - if (mix_devices[dev][RIGHT_CHN].nbits == 0) - return retvol; - - regoffs = mix_devices[dev][RIGHT_CHN].regno; - val = ad_read (devc, regoffs); - change_bits (&val, dev, RIGHT_CHN, right); - - valmute=val; - if ( regoffs==5 || regoffs==14 || regoffs==15 || - regoffs==16 || regoffs==17 || regoffs==18 || - regoffs==19 || regoffs==39) { - if (right==0) - valmute |= 0x80; - else - valmute &= ~0x80; - } - ad_write (devc, regoffs, valmute); /* mute */ - - return retvol; -} - -#define MIXER_DEVICES ( SOUND_MASK_VOLUME | \ - SOUND_MASK_SYNTH | \ - SOUND_MASK_PCM | \ - SOUND_MASK_LINE | \ - SOUND_MASK_LINE1 | \ - SOUND_MASK_LINE2 | \ - SOUND_MASK_LINE3 | \ - SOUND_MASK_MIC | \ - SOUND_MASK_CD | \ - SOUND_MASK_RECLEV \ - ) -#define REC_DEVICES ( SOUND_MASK_LINE2 |\ - SOUND_MASK_LINE |\ - SOUND_MASK_LINE1 |\ - SOUND_MASK_MIC |\ - SOUND_MASK_CD |\ - SOUND_MASK_VOLUME \ - ) - -static void -ad1816_mixer_reset (ad1816_info * devc) -{ - int i; - - devc->supported_devices = MIXER_DEVICES; - - devc->supported_rec_devices = REC_DEVICES; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (devc->supported_devices & (1 << i)) - ad1816_mixer_set (devc, i, default_mixer_levels[i]); - ad1816_set_recmask (devc, SOUND_MASK_MIC); -} - -static int -ad1816_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) -{ - ad1816_info *devc = mixer_devs[dev]->devc; - int val; - - DEBUGINFO(printk("ad1816: mixer_ioctl called!\n")); - - /* Mixer ioctl */ - if (((cmd >> 8) & 0xff) == 'M') { - - /* set ioctl */ - if (_SIOC_DIR (cmd) & _SIOC_WRITE) { - switch (cmd & 0xff){ - case SOUND_MIXER_RECSRC: - - if (get_user(val, (int *)arg)) - return -EFAULT; - val=ad1816_set_recmask (devc, val); - return put_user(val, (int *)arg); - break; - - default: - if (get_user(val, (int *)arg)) - return -EFAULT; - if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0) - return val; - else - return put_user(val, (int *)arg); - } - } else { - /* read ioctl */ - switch (cmd & 0xff) { - - case SOUND_MIXER_RECSRC: - val=devc->recmask; - return put_user(val, (int *)arg); - break; - - case SOUND_MIXER_DEVMASK: - val=devc->supported_devices; - return put_user(val, (int *)arg); - break; - - case SOUND_MIXER_STEREODEVS: - val=devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); - return put_user(val, (int *)arg); - break; - - case SOUND_MIXER_RECMASK: - val=devc->supported_rec_devices; - return put_user(val, (int *)arg); - break; - - case SOUND_MIXER_CAPS: - val=SOUND_CAP_EXCL_INPUT; - return put_user(val, (int *)arg); - break; - - default: - if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0) - return val; - else - return put_user(val, (int *)arg); - } - } - } else - /* not for mixer */ - return -(EINVAL); -} - -/* ------------------------------------------------------------------- */ - -/* Mixer structure */ - -static struct mixer_operations ad1816_mixer_operations = { - owner: THIS_MODULE, - id: "AD1816", - name: "AD1816 Mixer", - ioctl: ad1816_mixer_ioctl -}; - - -/* ------------------------------------------------------------------- */ - -/* stuff for card recognition, init and unloading */ - - -/* replace with probe routine */ -static int __init probe_ad1816 ( struct address_info *hw_config ) -{ - ad1816_info *devc = &dev_info[nr_ad1816_devs]; - int io_base=hw_config->io_base; - int *osp=hw_config->osp; - int tmp; - - printk(KERN_INFO "ad1816: AD1816 sounddriver " - "Copyright (C) 1998 by Thorsten Knabe\n"); - printk(KERN_INFO "ad1816: io=0x%x, irq=%d, dma=%d, dma2=%d, " - "clockfreq=%d, options=%d isadmabug=%d\n", - hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma2, - ad1816_clockfreq, - options, - isa_dma_bridge_buggy); - - if (!request_region(io_base, 16, "AD1816 Sound")) { - printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n", - io_base); - goto err; - } - - DEBUGLOG(printk ("ad1816: detect(%x)\n", io_base)); - - if (nr_ad1816_devs >= MAX_AUDIO_DEV) { - printk(KERN_WARNING "ad1816: detect error - step 0\n"); - goto out_release_region; - } - - devc->base = io_base; - devc->irq_ok = 0; - devc->irq = 0; - devc->opened = 0; - devc->osp = osp; - - /* base+0: bit 1 must be set but not 255 */ - tmp=inb(devc->base); - if ( (tmp&0x80)==0 || tmp==255 ) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 or chip is not active (Test 0)\n")); - goto out_release_region; - } - - - /* writes to ireg 8 are copied to ireg 9 */ - ad_write(devc,8,12345); - if (ad_read(devc,9)!=12345) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 1)\n")); - goto out_release_region; - } - - /* writes to ireg 8 are copied to ireg 9 */ - ad_write(devc,8,54321); - if (ad_read(devc,9)!=54321) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 2)\n")); - goto out_release_region; - } - - - /* writes to ireg 10 are copied to ireg 11 */ - ad_write(devc,10,54321); - if (ad_read(devc,11)!=54321) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 3)\n")); - goto out_release_region; - } - - /* writes to ireg 10 are copied to ireg 11 */ - ad_write(devc,10,12345); - if (ad_read(devc,11)!=12345) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 4)\n")); - goto out_release_region; - } - - /* bit in base +1 cannot be set to 1 */ - tmp=inb(devc->base+1); - outb(0xff,devc->base+1); - if (inb(devc->base+1)!=tmp) { - DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 5)\n")); - goto out_release_region; - } - - - DEBUGLOG (printk ("ad1816: detect() - Detected OK\n")); - DEBUGLOG (printk ("ad1816: AD1816 Version: %d\n",ad_read(devc,45))); - - /* detection was successful */ - return 1; -out_release_region: - release_region(io_base, 16); - /* detection was NOT successful */ -err: return 0; -} - - -/* allocate resources from the kernel. If any allocation fails, free - all allocated resources and exit attach. - - */ - -static void __init attach_ad1816 (struct address_info *hw_config) -{ - int my_dev; - char dev_name[100]; - ad1816_info *devc = &dev_info[nr_ad1816_devs]; - - devc->base = hw_config->io_base; - - /* disable all interrupts */ - ad_write(devc,1,0); - - /* Clear pending interrupts */ - outb (0, devc->base+1); - - /* allocate irq */ - if (hw_config->irq < 0 || hw_config->irq > 15) - goto out_release_region; - if (request_irq(hw_config->irq, ad1816_interrupt,0, - "SoundPort", hw_config->osp) < 0) { - printk(KERN_WARNING "ad1816: IRQ in use\n"); - goto out_release_region; - } - devc->irq=hw_config->irq; - - /* DMA stuff */ - if (sound_alloc_dma (hw_config->dma, "Sound System")) { - printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", - hw_config->dma); - goto out_free_irq; - } - devc->dma_playback=hw_config->dma; - - if ( hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) { - if (sound_alloc_dma(hw_config->dma2, - "Sound System (capture)")) { - printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", - hw_config->dma2); - goto out_free_dma; - } - devc->dma_capture=hw_config->dma2; - devc->audio_mode=DMA_AUTOMODE|DMA_DUPLEX; - } else { - devc->dma_capture=-1; - devc->audio_mode=DMA_AUTOMODE; - } - - sprintf (dev_name,"AD1816 audio driver"); - - conf_printf2 (dev_name, - devc->base, devc->irq, hw_config->dma, hw_config->dma2); - - /* register device */ - if ((my_dev = sound_install_audiodrv (AUDIO_DRIVER_VERSION, - dev_name, - &ad1816_audio_driver, - sizeof (struct audio_driver), - devc->audio_mode, - ad_format_mask, - devc, - hw_config->dma, - hw_config->dma2)) < 0) { - printk(KERN_WARNING "ad1816: Can't install sound driver\n"); - goto out_free_dma_2; - } - - /* fill rest of structure with reasonable default values */ - irq2dev[hw_config->irq] = devc->dev_no = my_dev; - devc->opened = 0; - devc->irq_ok = 0; - devc->osp = hw_config->osp; - nr_ad1816_devs++; - - ad_write(devc,32,0x80f0); /* sound system mode */ - if (options&1) { - ad_write(devc,33,0); /* disable all audiosources for dsp */ - } else { - ad_write(devc,33,0x03f8); /* enable all audiosources for dsp */ - } - ad_write(devc,4,0x8080); /* default values for volumes (muted)*/ - ad_write(devc,5,0x8080); - ad_write(devc,6,0x8080); - ad_write(devc,7,0x8080); - ad_write(devc,15,0x8888); - ad_write(devc,16,0x8888); - ad_write(devc,17,0x8888); - ad_write(devc,18,0x8888); - ad_write(devc,19,0xc888); /* +20db mic active */ - ad_write(devc,14,0x0000); /* Master volume unmuted */ - ad_write(devc,39,0x009f); /* 3D effect on 0% phone out muted */ - ad_write(devc,44,0x0080); /* everything on power, 3d enabled for d/a */ - outb(0x10,devc->base+8); /* set dma mode */ - outb(0x10,devc->base+9); - - /* enable capture + playback interrupt */ - ad_write(devc,1,0xc000); - - /* set mixer defaults */ - ad1816_mixer_reset (devc); - - /* register mixer */ - if ((audio_devs[my_dev]->mixer_dev=sound_install_mixer( - MIXER_DRIVER_VERSION, - dev_name, - &ad1816_mixer_operations, - sizeof (struct mixer_operations), - devc)) >= 0) { - audio_devs[my_dev]->min_fragment = 0; - } -out: return; -out_free_dma_2: - if (devc->dma_capture >= 0) - sound_free_dma(hw_config->dma2); -out_free_dma: - sound_free_dma(hw_config->dma); -out_free_irq: - free_irq(hw_config->irq,hw_config->osp); -out_release_region: - release_region(hw_config->io_base, 16); - goto out; -} - -static void __exit unload_card(ad1816_info *devc) -{ - int mixer, dev = 0; - - if (devc != NULL) { - DEBUGLOG (printk("ad1816: Unloading card at base=%x\n",devc->base)); - - dev = devc->dev_no; - mixer = audio_devs[dev]->mixer_dev; - - /* unreg mixer*/ - if(mixer>=0) { - sound_unload_mixerdev(mixer); - } - sound_unload_audiodev(dev); - - /* free dma channels */ - if (devc->dma_capture>=0) { - sound_free_dma(devc->dma_capture); - } - - /* card wont get added if resources could not be allocated - thus we need not ckeck if allocation was successful */ - sound_free_dma (devc->dma_playback); - free_irq(devc->irq, devc->osp); - release_region (devc->base, 16); - - DEBUGLOG (printk("ad1816: Unloading card at base=%x was successful\n",devc->base)); - - } else - printk(KERN_WARNING "ad1816: no device/card specified\n"); -} - -static struct address_info cfg; - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; - -#ifdef __ISAPNP__ -struct pci_dev *ad1816_dev = NULL; - -static int activated = 1; - -static int isapnp = 1; -static int isapnpjump = 0; - -MODULE_PARM(isapnp, "i"); -MODULE_PARM(isapnpjump, "i"); - -#else -static int isapnp = 0; -#endif - -MODULE_PARM(io,"i"); -MODULE_PARM(irq,"i"); -MODULE_PARM(dma,"i"); -MODULE_PARM(dma2,"i"); -MODULE_PARM(ad1816_clockfreq,"i"); -MODULE_PARM(options,"i"); - -#ifdef __ISAPNP__ - -static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) -{ - int err; - - if(dev->active) { - activated = 0; - return(dev); - } - - if((err = dev->activate(dev)) < 0) { - printk(KERN_ERR "ad1816: %s %s config failed (out of resources?)[%d]\n", - devname, resname, err); - dev->deactivate(dev); - return(NULL); - } - - return(dev); -} - -static struct pci_dev *ad1816_init_generic(struct pci_bus *bus, struct pci_dev *card, - struct address_info *hw_config) -{ - if((ad1816_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) { - ad1816_dev->prepare(ad1816_dev); - - if((ad1816_dev = activate_dev("Analog Devices 1816(A)", "ad1816", ad1816_dev))) { - hw_config->io_base = ad1816_dev->resource[2].start; - hw_config->irq = ad1816_dev->irq_resource[0].start; - hw_config->dma = ad1816_dev->dma_resource[0].start; - hw_config->dma2 = ad1816_dev->dma_resource[1].start; - } - } - - return(ad1816_dev); -} - -static struct ad1816_data { - struct pci_dev * (*initfunc)(struct pci_bus*, struct pci_dev *, struct address_info *); - char *name; -} ad1816_pnp_data[] __initdata = { - { &ad1816_init_generic, "Analog Devices 1815" }, - { &ad1816_init_generic, "Analog Devices 1816A" } -}; - -static struct { - unsigned short card_vendor, card_device; - unsigned short vendor; - unsigned short function; - struct ad1816_data *data; -} isapnp_ad1816_list[] __initdata = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150), - &ad1816_pnp_data[0] }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180), - &ad1816_pnp_data[1] }, - {0} -}; - -MODULE_DEVICE_TABLE(isapnp, isapnp_ad1816_list); - -static int __init ad1816_init_isapnp(struct address_info *hw_config, - struct pci_bus *bus, struct pci_dev *card, int slot) -{ - struct pci_dev *idev = NULL; - - /* You missed the init func? That's bad. */ - if(isapnp_ad1816_list[slot].data->initfunc) { - char *busname = bus->name[0] ? bus->name : isapnp_ad1816_list[slot].data->name; - - printk(KERN_INFO "ad1816: %s detected\n", busname); - - /* Initialize this baby. */ - if((idev = isapnp_ad1816_list[slot].data->initfunc(bus, card, hw_config))) { - /* We got it. */ - - printk(KERN_NOTICE "ad1816: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", - busname, - hw_config->io_base, hw_config->irq, hw_config->dma, - hw_config->dma2); - return 1; - } else - printk(KERN_INFO "ad1816: Failed to initialize %s\n", busname); - } else - printk(KERN_ERR "ad1816: Bad entry in ad1816.c PnP table\n"); - - return 0; -} - -/* - * Actually this routine will detect and configure only the first card with successful - * initialization. isapnpjump could be used to jump to a specific entry. - * Please always add entries at the end of the array. - * Should this be fixed? - azummo - */ - -int __init ad1816_probe_isapnp(struct address_info *hw_config) -{ - int i; - - /* Count entries in isapnp_ad1816_list */ - for (i = 0; isapnp_ad1816_list[i].vendor != 0; i++) - ; - /* Check and adjust isapnpjump */ - if( isapnpjump < 0 || isapnpjump > ( i - 1 ) ) { - printk(KERN_ERR "ad1816: Valid range for isapnpjump is 0-%d. Adjusted to 0.\n", i-1); - isapnpjump = 0; - } - - for (i = isapnpjump; isapnp_ad1816_list[i].vendor != 0; i++) { - struct pci_dev *card = NULL; - - while ((card = isapnp_find_dev(NULL, isapnp_ad1816_list[i].vendor, - isapnp_ad1816_list[i].function, card))) - if(ad1816_init_isapnp(hw_config, card->bus, card, i)) - return 0; - } - - return -ENODEV; -} -#endif - -static int __init init_ad1816(void) -{ - -#ifdef __ISAPNP__ - if(isapnp && (ad1816_probe_isapnp(&cfg) < 0) ) { - printk(KERN_NOTICE "ad1816: No ISAPnP cards found, trying standard ones...\n"); - isapnp = 0; - } -#endif - - if( isapnp == 0) { - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - } - - if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.dma2 == -1) { - printk(KERN_INFO "ad1816: dma, dma2, irq and io must be set.\n"); - return -EINVAL; - } - - if (probe_ad1816(&cfg) == 0) { - return -ENODEV; - } - - attach_ad1816(&cfg); - - return 0; -} - -static void __exit cleanup_ad1816 (void) -{ - int i; - ad1816_info *devc = NULL; - - /* remove any soundcard */ - for (i = 0; i < nr_ad1816_devs; i++) { - devc = &dev_info[i]; - unload_card(devc); - } - nr_ad1816_devs=0; - -#ifdef __ISAPNP__ - if(activated) - if(ad1816_dev) - ad1816_dev->deactivate(ad1816_dev); -#endif -} - -module_init(init_ad1816); -module_exit(cleanup_ad1816); - -#ifndef MODULE -static int __init setup_ad1816(char *str) -{ - /* io, irq, dma, dma2 */ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - - return 1; -} - -__setup("ad1816=", setup_ad1816); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/ad1848.c b/drivers/sound/ad1848.c --- a/drivers/sound/ad1848.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3177 +0,0 @@ -/* - * sound/ad1848.c - * - * The low level driver for the AD1848/CS4248 codec chip which - * is used for example in the MS Sound System. - * - * The CS4231 which is used in the GUS MAX and some other cards is - * upwards compatible with AD1848 and this driver is able to drive it. - * - * CS4231A and AD1845 are upward compatible with CS4231. However - * the new features of these chips are different. - * - * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU). - * CS4232A is an improved version of CS4232. - * - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * general sleep/wakeup clean up. - * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free - * of irqs. Use dev_id. - * Christoph Hellwig : adapted to module_init/module_exit - * Aki Laukkanen : added power management support - * Arnaldo C. de Melo : added missing restore_flags in ad1848_resume - * Miguel Freitas : added ISA PnP support - * Alan Cox : Added CS4236->4239 identification - * Daniel T. Cobra : Alernate config/mixer for later chips - * Alan Cox : Merged chip idents and config code - * - * TODO - * APM save restore assist code on IBM thinkpad - * - * Status: - * Tested. Believed fully functional. - */ - -#include -#include -#include -#include -#include -#include - -#define DEB(x) -#define DEB1(x) -#include "sound_config.h" - -#include "ad1848.h" -#include "ad1848_mixer.h" - -typedef struct -{ - int base; - int irq; - int dma1, dma2; - int dual_dma; /* 1, when two DMA channels allocated */ - int subtype; - unsigned char MCE_bit; - unsigned char saved_regs[64]; /* Includes extended register space */ - int debug_flag; - - int audio_flags; - int record_dev, playback_dev; - - int xfer_count; - int audio_mode; - int open_mode; - int intr_active; - char *chip_name, *name; - int model; -#define MD_1848 1 -#define MD_4231 2 -#define MD_4231A 3 -#define MD_1845 4 -#define MD_4232 5 -#define MD_C930 6 -#define MD_IWAVE 7 -#define MD_4235 8 /* Crystal Audio CS4235 */ -#define MD_1845_SSCAPE 9 /* Ensoniq Soundscape PNP*/ -#define MD_4236 10 /* 4236 and higher */ -#define MD_42xB 11 /* CS 42xB */ -#define MD_4239 12 /* CS4239 */ - - /* Mixer parameters */ - int recmask; - int supported_devices, orig_devices; - int supported_rec_devices, orig_rec_devices; - int *levels; - short mixer_reroute[32]; - int dev_no; - volatile unsigned long timer_ticks; - int timer_running; - int irq_ok; - mixer_ents *mix_devices; - int mixer_output_port; - - /* Power management */ - struct pm_dev *pmdev; -} ad1848_info; - -typedef struct ad1848_port_info -{ - int open_mode; - int speed; - unsigned char speed_bits; - int channels; - int audio_format; - unsigned char format_bits; -} -ad1848_port_info; - -static struct address_info cfg; -static int nr_ad1848_devs; - -int deskpro_xl; -int deskpro_m; -int soundpro; - -static volatile signed char irq2dev[17] = { - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1 -}; - -#ifndef EXCLUDE_TIMERS -static int timer_installed = -1; -#endif - -static int loaded; - -static int ad_format_mask[13 /*devc->model */ ] = -{ - 0, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */ - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE /* CS4235 */, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW /* Ensoniq Soundscape*/, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM -}; - -static ad1848_info adev_info[MAX_AUDIO_DEV]; - -#define io_Index_Addr(d) ((d)->base) -#define io_Indexed_Data(d) ((d)->base+1) -#define io_Status(d) ((d)->base+2) -#define io_Polled_IO(d) ((d)->base+3) - -static struct { - unsigned char flags; -#define CAP_F_TIMER 0x01 -} capabilities [10 /*devc->model */ ] = { - {0} - ,{0} /* MD_1848 */ - ,{CAP_F_TIMER} /* MD_4231 */ - ,{CAP_F_TIMER} /* MD_4231A */ - ,{CAP_F_TIMER} /* MD_1845 */ - ,{CAP_F_TIMER} /* MD_4232 */ - ,{0} /* MD_C930 */ - ,{CAP_F_TIMER} /* MD_IWAVE */ - ,{0} /* MD_4235 */ - ,{CAP_F_TIMER} /* MD_1845_SSCAPE */ -}; - -#ifdef __ISAPNP__ -static int isapnp = 1; -static int isapnpjump = 0; -static int reverse = 0; - -static int audio_activated = 0; -#else -static int isapnp = 0; -#endif - - - -static int ad1848_open(int dev, int mode); -static void ad1848_close(int dev); -static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag); -static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag); -static int ad1848_prepare_for_output(int dev, int bsize, int bcount); -static int ad1848_prepare_for_input(int dev, int bsize, int bcount); -static void ad1848_halt(int dev); -static void ad1848_halt_input(int dev); -static void ad1848_halt_output(int dev); -static void ad1848_trigger(int dev, int bits); -static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data); - -#ifndef EXCLUDE_TIMERS -static int ad1848_tmr_install(int dev); -static void ad1848_tmr_reprogram(int dev); -#endif - -static int ad_read(ad1848_info * devc, int reg) -{ - unsigned long flags; - int x; - int timeout = 900000; - - while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ - timeout--; - - save_flags(flags); - cli(); - - if(reg < 32) - { - outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); - x = inb(io_Indexed_Data(devc)); - } - else - { - int xreg, xra; - - xreg = (reg & 0xff) - 32; - xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2); - outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); - outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc)); - x = inb(io_Indexed_Data(devc)); - } - restore_flags(flags); - - return x; -} - -static void ad_write(ad1848_info * devc, int reg, int data) -{ - unsigned long flags; - int timeout = 900000; - - while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */ - timeout--; - - save_flags(flags); - cli(); - - if(reg < 32) - { - outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); - outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc)); - } - else - { - int xreg, xra; - - xreg = (reg & 0xff) - 32; - xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2); - outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); - outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc)); - outb((unsigned char) (data & 0xff), io_Indexed_Data(devc)); - } - restore_flags(flags); -} - -static void wait_for_calibration(ad1848_info * devc) -{ - int timeout = 0; - - /* - * Wait until the auto calibration process has finished. - * - * 1) Wait until the chip becomes ready (reads don't return 0x80). - * 2) Wait until the ACI bit of I11 gets on and then off. - */ - - timeout = 100000; - while (timeout > 0 && inb(devc->base) == 0x80) - timeout--; - if (inb(devc->base) & 0x80) - printk(KERN_WARNING "ad1848: Auto calibration timed out(1).\n"); - - timeout = 100; - while (timeout > 0 && !(ad_read(devc, 11) & 0x20)) - timeout--; - if (!(ad_read(devc, 11) & 0x20)) - return; - - timeout = 80000; - while (timeout > 0 && (ad_read(devc, 11) & 0x20)) - timeout--; - if (ad_read(devc, 11) & 0x20) - if ( (devc->model != MD_1845) || (devc->model != MD_1845_SSCAPE)) - printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n"); -} - -static void ad_mute(ad1848_info * devc) -{ - int i; - unsigned char prev; - - /* - * Save old register settings and mute output channels - */ - - for (i = 6; i < 8; i++) - { - prev = devc->saved_regs[i] = ad_read(devc, i); - } - -} - -static void ad_unmute(ad1848_info * devc) -{ -} - -static void ad_enter_MCE(ad1848_info * devc) -{ - unsigned long flags; - int timeout = 1000; - unsigned short prev; - - while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ - timeout--; - - save_flags(flags); - cli(); - - devc->MCE_bit = 0x40; - prev = inb(io_Index_Addr(devc)); - if (prev & 0x40) - { - restore_flags(flags); - return; - } - outb((devc->MCE_bit), io_Index_Addr(devc)); - restore_flags(flags); -} - -static void ad_leave_MCE(ad1848_info * devc) -{ - unsigned long flags; - unsigned char prev, acal; - int timeout = 1000; - - while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ - timeout--; - - save_flags(flags); - cli(); - - acal = ad_read(devc, 9); - - devc->MCE_bit = 0x00; - prev = inb(io_Index_Addr(devc)); - outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ - - if ((prev & 0x40) == 0) /* Not in MCE mode */ - { - restore_flags(flags); - return; - } - outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ - if (acal & 0x08) /* Auto calibration is enabled */ - wait_for_calibration(devc); - restore_flags(flags); -} - -static int ad1848_set_recmask(ad1848_info * devc, int mask) -{ - unsigned char recdev; - int i, n; - - mask &= devc->supported_rec_devices; - - /* Rename the mixer bits if necessary */ - for (i = 0; i < 32; i++) - { - if (devc->mixer_reroute[i] != i) - { - if (mask & (1 << i)) - { - mask &= ~(1 << i); - mask |= (1 << devc->mixer_reroute[i]); - } - } - } - - n = 0; - for (i = 0; i < 32; i++) /* Count selected device bits */ - if (mask & (1 << i)) - n++; - - if (!soundpro) { - if (n == 0) - mask = SOUND_MASK_MIC; - else if (n != 1) { /* Too many devices selected */ - mask &= ~devc->recmask; /* Filter out active settings */ - - n = 0; - for (i = 0; i < 32; i++) /* Count selected device bits */ - if (mask & (1 << i)) - n++; - - if (n != 1) - mask = SOUND_MASK_MIC; - } - switch (mask) { - case SOUND_MASK_MIC: - recdev = 2; - break; - - case SOUND_MASK_LINE: - case SOUND_MASK_LINE3: - recdev = 0; - break; - - case SOUND_MASK_CD: - case SOUND_MASK_LINE1: - recdev = 1; - break; - - case SOUND_MASK_IMIX: - recdev = 3; - break; - - default: - mask = SOUND_MASK_MIC; - recdev = 2; - } - - recdev <<= 6; - ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev); - ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev); - } else { /* soundpro */ - unsigned char val; - int set_rec_bit; - int j; - - for (i = 0; i < 32; i++) { /* For each bit */ - if ((devc->supported_rec_devices & (1 << i)) == 0) - continue; /* Device not supported */ - - for (j = LEFT_CHN; j <= RIGHT_CHN; j++) { - if (devc->mix_devices[i][j].nbits == 0) /* Inexistent channel */ - continue; - - /* - * This is tricky: - * set_rec_bit becomes 1 if the corresponding bit in mask is set - * then it gets flipped if the polarity is inverse - */ - set_rec_bit = ((mask & (1 << i)) != 0) ^ devc->mix_devices[i][j].recpol; - - val = ad_read(devc, devc->mix_devices[i][j].recreg); - val &= ~(1 << devc->mix_devices[i][j].recpos); - val |= (set_rec_bit << devc->mix_devices[i][j].recpos); - ad_write(devc, devc->mix_devices[i][j].recreg, val); - } - } - } - - /* Rename the mixer bits back if necessary */ - for (i = 0; i < 32; i++) - { - if (devc->mixer_reroute[i] != i) - { - if (mask & (1 << devc->mixer_reroute[i])) - { - mask &= ~(1 << devc->mixer_reroute[i]); - mask |= (1 << i); - } - } - } - devc->recmask = mask; - return mask; -} - -static void change_bits(ad1848_info * devc, unsigned char *regval, - unsigned char *muteval, int dev, int chn, int newval) -{ - unsigned char mask; - int shift; - int mute; - int mutemask; - int set_mute_bit; - - set_mute_bit = (newval == 0) ^ devc->mix_devices[dev][chn].mutepol; - - if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */ - newval = 100 - newval; - - mask = (1 << devc->mix_devices[dev][chn].nbits) - 1; - shift = devc->mix_devices[dev][chn].bitpos; - - if (devc->mix_devices[dev][chn].mutepos == 8) - { /* if there is no mute bit */ - mute = 0; /* No mute bit; do nothing special */ - mutemask = ~0; /* No mute bit; do nothing special */ - } - else - { - mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos); - mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos); - } - - newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ - *regval &= ~(mask << shift); /* Clear bits */ - *regval |= (newval & mask) << shift; /* Set new value */ - - *muteval &= mutemask; - *muteval |= mute; -} - -static int ad1848_mixer_get(ad1848_info * devc, int dev) -{ - if (!((1 << dev) & devc->supported_devices)) - return -EINVAL; - - dev = devc->mixer_reroute[dev]; - - return devc->levels[dev]; -} - -static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int channel) -{ - int regoffs, muteregoffs; - unsigned char val, muteval; - - regoffs = devc->mix_devices[dev][channel].regno; - muteregoffs = devc->mix_devices[dev][channel].mutereg; - val = ad_read(devc, regoffs); - - if (muteregoffs != regoffs) { - muteval = ad_read(devc, muteregoffs); - change_bits(devc, &val, &muteval, dev, channel, value); - } - else - change_bits(devc, &val, &val, dev, channel, value); - - ad_write(devc, regoffs, val); - devc->saved_regs[regoffs] = val; - if (muteregoffs != regoffs) { - ad_write(devc, muteregoffs, muteval); - devc->saved_regs[muteregoffs] = muteval; - } -} - -static int ad1848_mixer_set(ad1848_info * devc, int dev, int value) -{ - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int retvol; - - if (dev > 31) - return -EINVAL; - - if (!(devc->supported_devices & (1 << dev))) - return -EINVAL; - - dev = devc->mixer_reroute[dev]; - - if (devc->mix_devices[dev][LEFT_CHN].nbits == 0) - return -EINVAL; - - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */ - right = left; - - retvol = left | (right << 8); - - /* Scale volumes */ - left = mix_cvt[left]; - right = mix_cvt[right]; - - devc->levels[dev] = retvol; - - /* - * Set the left channel - */ - ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN); - - /* - * Set the right channel - */ - if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) - goto out; - ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN); - - out: - return retvol; -} - -static void ad1848_mixer_reset(ad1848_info * devc) -{ - int i; - char name[32]; - - devc->mix_devices = &(ad1848_mix_devices[0]); - - sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs); - - for (i = 0; i < 32; i++) - devc->mixer_reroute[i] = i; - - devc->supported_rec_devices = MODE1_REC_DEVICES; - - switch (devc->model) - { - case MD_4231: - case MD_4231A: - case MD_1845: - case MD_1845_SSCAPE: - devc->supported_devices = MODE2_MIXER_DEVICES; - break; - - case MD_C930: - devc->supported_devices = C930_MIXER_DEVICES; - devc->mix_devices = &(c930_mix_devices[0]); - break; - - case MD_IWAVE: - devc->supported_devices = MODE3_MIXER_DEVICES; - devc->mix_devices = &(iwave_mix_devices[0]); - break; - - case MD_42xB: - case MD_4239: - devc->mix_devices = &(cs42xb_mix_devices[0]); - devc->supported_devices = MODE3_MIXER_DEVICES; - break; - case MD_4232: - case MD_4236: - devc->supported_devices = MODE3_MIXER_DEVICES; - break; - - case MD_1848: - if (soundpro) { - devc->supported_devices = SPRO_MIXER_DEVICES; - devc->supported_rec_devices = SPRO_REC_DEVICES; - devc->mix_devices = &(spro_mix_devices[0]); - break; - } - - default: - devc->supported_devices = MODE1_MIXER_DEVICES; - } - - devc->orig_devices = devc->supported_devices; - devc->orig_rec_devices = devc->supported_rec_devices; - - devc->levels = load_mixer_volumes(name, default_mixer_levels, 1); - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - { - if (devc->supported_devices & (1 << i)) - ad1848_mixer_set(devc, i, devc->levels[i]); - } - - ad1848_set_recmask(devc, SOUND_MASK_MIC); - - devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT; - - if (!soundpro) { - if (devc->mixer_output_port & AUDIO_SPEAKER) - ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ - else - ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ - } else { - /* - * From the "wouldn't it be nice if the mixer API had (better) - * support for custom stuff" category - */ - /* Enable surround mode and SB16 mixer */ - ad_write(devc, 16, 0x60); - } -} - -static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - ad1848_info *devc = mixer_devs[dev]->devc; - int val; - - if (cmd == SOUND_MIXER_PRIVATE1) - { - if (get_user(val, (int *)arg)) - return -EFAULT; - - if (val != 0xffff) - { - val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT); - devc->mixer_output_port = val; - val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */ - devc->mixer_output_port = val; - if (val & AUDIO_SPEAKER) - ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ - else - ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ - } - val = devc->mixer_output_port; - return put_user(val, (int *)arg); - } - if (cmd == SOUND_MIXER_PRIVATE2) - { - if (get_user(val, (int *)arg)) - return -EFAULT; - return(ad1848_control(AD1848_MIXER_REROUTE, val)); - } - if (((cmd >> 8) & 0xff) == 'M') - { - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - { - switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = ad1848_set_recmask(devc, val); - break; - - default: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = ad1848_mixer_set(devc, cmd & 0xff, val); - break; - } - return put_user(val, (int *)arg); - } - else - { - switch (cmd & 0xff) - { - /* - * Return parameters - */ - - case SOUND_MIXER_RECSRC: - val = devc->recmask; - break; - - case SOUND_MIXER_DEVMASK: - val = devc->supported_devices; - break; - - case SOUND_MIXER_STEREODEVS: - val = devc->supported_devices; - if (devc->model != MD_C930) - val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); - break; - - case SOUND_MIXER_RECMASK: - val = devc->supported_rec_devices; - break; - - case SOUND_MIXER_CAPS: - val=SOUND_CAP_EXCL_INPUT; - break; - - default: - val = ad1848_mixer_get(devc, cmd & 0xff); - break; - } - return put_user(val, (int *)arg); - } - } - else - return -EINVAL; -} - -static int ad1848_set_speed(int dev, int arg) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - /* - * The sampling speed is encoded in the least significant nibble of I8. The - * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other - * three bits select the divisor (indirectly): - * - * The available speeds are in the following table. Keep the speeds in - * the increasing order. - */ - typedef struct - { - int speed; - unsigned char bits; - } - speed_struct; - - static speed_struct speed_table[] = - { - {5510, (0 << 1) | 1}, - {5510, (0 << 1) | 1}, - {6620, (7 << 1) | 1}, - {8000, (0 << 1) | 0}, - {9600, (7 << 1) | 0}, - {11025, (1 << 1) | 1}, - {16000, (1 << 1) | 0}, - {18900, (2 << 1) | 1}, - {22050, (3 << 1) | 1}, - {27420, (2 << 1) | 0}, - {32000, (3 << 1) | 0}, - {33075, (6 << 1) | 1}, - {37800, (4 << 1) | 1}, - {44100, (5 << 1) | 1}, - {48000, (6 << 1) | 0} - }; - - int i, n, selected = -1; - - n = sizeof(speed_table) / sizeof(speed_struct); - - if (arg <= 0) - return portc->speed; - - if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* AD1845 has different timer than others */ - { - if (arg < 4000) - arg = 4000; - if (arg > 50000) - arg = 50000; - - portc->speed = arg; - portc->speed_bits = speed_table[3].bits; - return portc->speed; - } - if (arg < speed_table[0].speed) - selected = 0; - if (arg > speed_table[n - 1].speed) - selected = n - 1; - - for (i = 1 /*really */ ; selected == -1 && i < n; i++) - { - if (speed_table[i].speed == arg) - selected = i; - else if (speed_table[i].speed > arg) - { - int diff1, diff2; - - diff1 = arg - speed_table[i - 1].speed; - diff2 = speed_table[i].speed - arg; - - if (diff1 < diff2) - selected = i - 1; - else - selected = i; - } - } - if (selected == -1) - { - printk(KERN_WARNING "ad1848: Can't find speed???\n"); - selected = 3; - } - portc->speed = speed_table[selected].speed; - portc->speed_bits = speed_table[selected].bits; - return portc->speed; -} - -static short ad1848_set_channels(int dev, short arg) -{ - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - if (arg != 1 && arg != 2) - return portc->channels; - - portc->channels = arg; - return arg; -} - -static unsigned int ad1848_set_bits(int dev, unsigned int arg) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - static struct format_tbl - { - int format; - unsigned char bits; - } - format2bits[] = - { - { - 0, 0 - } - , - { - AFMT_MU_LAW, 1 - } - , - { - AFMT_A_LAW, 3 - } - , - { - AFMT_IMA_ADPCM, 5 - } - , - { - AFMT_U8, 0 - } - , - { - AFMT_S16_LE, 2 - } - , - { - AFMT_S16_BE, 6 - } - , - { - AFMT_S8, 0 - } - , - { - AFMT_U16_LE, 0 - } - , - { - AFMT_U16_BE, 0 - } - }; - int i, n = sizeof(format2bits) / sizeof(struct format_tbl); - - if (arg == 0) - return portc->audio_format; - - if (!(arg & ad_format_mask[devc->model])) - arg = AFMT_U8; - - portc->audio_format = arg; - - for (i = 0; i < n; i++) - if (format2bits[i].format == arg) - { - if ((portc->format_bits = format2bits[i].bits) == 0) - return portc->audio_format = AFMT_U8; /* Was not supported */ - - return arg; - } - /* Still hanging here. Something must be terribly wrong */ - portc->format_bits = 0; - return portc->audio_format = AFMT_U8; -} - -static struct audio_driver ad1848_audio_driver = -{ - owner: THIS_MODULE, - open: ad1848_open, - close: ad1848_close, - output_block: ad1848_output_block, - start_input: ad1848_start_input, - prepare_for_input: ad1848_prepare_for_input, - prepare_for_output: ad1848_prepare_for_output, - halt_io: ad1848_halt, - halt_input: ad1848_halt_input, - halt_output: ad1848_halt_output, - trigger: ad1848_trigger, - set_speed: ad1848_set_speed, - set_bits: ad1848_set_bits, - set_channels: ad1848_set_channels -}; - -static struct mixer_operations ad1848_mixer_operations = -{ - owner: THIS_MODULE, - id: "SOUNDPORT", - name: "AD1848/CS4248/CS4231", - ioctl: ad1848_mixer_ioctl -}; - -static int ad1848_open(int dev, int mode) -{ - ad1848_info *devc = NULL; - ad1848_port_info *portc; - unsigned long flags; - - if (dev < 0 || dev >= num_audiodevs) - return -ENXIO; - - devc = (ad1848_info *) audio_devs[dev]->devc; - portc = (ad1848_port_info *) audio_devs[dev]->portc; - - save_flags(flags); - cli(); - if (portc->open_mode || (devc->open_mode & mode)) - { - restore_flags(flags); - return -EBUSY; - } - devc->dual_dma = 0; - - if (audio_devs[dev]->flags & DMA_DUPLEX) - { - devc->dual_dma = 1; - } - devc->intr_active = 0; - devc->audio_mode = 0; - devc->open_mode |= mode; - portc->open_mode = mode; - ad1848_trigger(dev, 0); - - if (mode & OPEN_READ) - devc->record_dev = dev; - if (mode & OPEN_WRITE) - devc->playback_dev = dev; - restore_flags(flags); -/* - * Mute output until the playback really starts. This decreases clicking (hope so). - */ - ad_mute(devc); - - return 0; -} - -static void ad1848_close(int dev) -{ - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - DEB(printk("ad1848_close(void)\n")); - - save_flags(flags); - cli(); - - devc->intr_active = 0; - ad1848_halt(dev); - - devc->audio_mode = 0; - devc->open_mode &= ~portc->open_mode; - portc->open_mode = 0; - - ad_unmute(devc); - restore_flags(flags); -} - -static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag) -{ - unsigned long flags, cnt; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - cnt = count; - - if (portc->audio_format == AFMT_IMA_ADPCM) - { - cnt /= 4; - } - else - { - if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ - cnt >>= 1; - } - if (portc->channels > 1) - cnt >>= 1; - cnt--; - - if ((devc->audio_mode & PCM_ENABLE_OUTPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) && - intrflag && - cnt == devc->xfer_count) - { - devc->audio_mode |= PCM_ENABLE_OUTPUT; - devc->intr_active = 1; - return; /* - * Auto DMA mode on. No need to react - */ - } - save_flags(flags); - cli(); - - ad_write(devc, 15, (unsigned char) (cnt & 0xff)); - ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); - - devc->xfer_count = cnt; - devc->audio_mode |= PCM_ENABLE_OUTPUT; - devc->intr_active = 1; - restore_flags(flags); -} - -static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag) -{ - unsigned long flags, cnt; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - cnt = count; - if (portc->audio_format == AFMT_IMA_ADPCM) - { - cnt /= 4; - } - else - { - if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ - cnt >>= 1; - } - if (portc->channels > 1) - cnt >>= 1; - cnt--; - - if ((devc->audio_mode & PCM_ENABLE_INPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) && - intrflag && - cnt == devc->xfer_count) - { - devc->audio_mode |= PCM_ENABLE_INPUT; - devc->intr_active = 1; - return; /* - * Auto DMA mode on. No need to react - */ - } - save_flags(flags); - cli(); - - if (devc->model == MD_1848) - { - ad_write(devc, 15, (unsigned char) (cnt & 0xff)); - ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); - } - else - { - ad_write(devc, 31, (unsigned char) (cnt & 0xff)); - ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff)); - } - - ad_unmute(devc); - - devc->xfer_count = cnt; - devc->audio_mode |= PCM_ENABLE_INPUT; - devc->intr_active = 1; - restore_flags(flags); -} - -static int ad1848_prepare_for_output(int dev, int bsize, int bcount) -{ - int timeout; - unsigned char fs, old_fs, tmp = 0; - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - ad_mute(devc); - - save_flags(flags); - cli(); - fs = portc->speed_bits | (portc->format_bits << 5); - - if (portc->channels > 1) - fs |= 0x10; - - ad_enter_MCE(devc); /* Enables changes to the format select reg */ - - if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* Use alternate speed select registers */ - { - fs &= 0xf0; /* Mask off the rate select bits */ - - ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */ - ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */ - } - old_fs = ad_read(devc, 8); - - if (devc->model == MD_4232 || devc->model >= MD_4236) - { - tmp = ad_read(devc, 16); - ad_write(devc, 16, tmp | 0x30); - } - if (devc->model == MD_IWAVE) - ad_write(devc, 17, 0xc2); /* Disable variable frequency select */ - - ad_write(devc, 8, fs); - - /* - * Write to I8 starts resynchronization. Wait until it completes. - */ - - timeout = 0; - while (timeout < 100 && inb(devc->base) != 0x80) - timeout++; - timeout = 0; - while (timeout < 10000 && inb(devc->base) == 0x80) - timeout++; - - if (devc->model >= MD_4232) - ad_write(devc, 16, tmp & ~0x30); - - ad_leave_MCE(devc); /* - * Starts the calibration process. - */ - restore_flags(flags); - devc->xfer_count = 0; - -#ifndef EXCLUDE_TIMERS - if (dev == timer_installed && devc->timer_running) - if ((fs & 0x01) != (old_fs & 0x01)) - { - ad1848_tmr_reprogram(dev); - } -#endif - ad1848_halt_output(dev); - return 0; -} - -static int ad1848_prepare_for_input(int dev, int bsize, int bcount) -{ - int timeout; - unsigned char fs, old_fs, tmp = 0; - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - if (devc->audio_mode) - return 0; - - save_flags(flags); - cli(); - fs = portc->speed_bits | (portc->format_bits << 5); - - if (portc->channels > 1) - fs |= 0x10; - - ad_enter_MCE(devc); /* Enables changes to the format select reg */ - - if ((devc->model == MD_1845) || (devc->model == MD_1845_SSCAPE)) /* Use alternate speed select registers */ - { - fs &= 0xf0; /* Mask off the rate select bits */ - - ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */ - ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */ - } - if (devc->model == MD_4232) - { - tmp = ad_read(devc, 16); - ad_write(devc, 16, tmp | 0x30); - } - if (devc->model == MD_IWAVE) - ad_write(devc, 17, 0xc2); /* Disable variable frequency select */ - - /* - * If mode >= 2 (CS4231), set I28. It's the capture format register. - */ - - if (devc->model != MD_1848) - { - old_fs = ad_read(devc, 28); - ad_write(devc, 28, fs); - - /* - * Write to I28 starts resynchronization. Wait until it completes. - */ - - timeout = 0; - while (timeout < 100 && inb(devc->base) != 0x80) - timeout++; - - timeout = 0; - while (timeout < 10000 && inb(devc->base) == 0x80) - timeout++; - - if (devc->model != MD_1848 && devc->model != MD_1845 && devc->model != MD_1845_SSCAPE) - { - /* - * CS4231 compatible devices don't have separate sampling rate selection - * register for recording an playback. The I8 register is shared so we have to - * set the speed encoding bits of it too. - */ - unsigned char tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0); - - ad_write(devc, 8, tmp); - /* - * Write to I8 starts resynchronization. Wait until it completes. - */ - timeout = 0; - while (timeout < 100 && inb(devc->base) != 0x80) - timeout++; - - timeout = 0; - while (timeout < 10000 && inb(devc->base) == 0x80) - timeout++; - } - } - else - { /* For AD1848 set I8. */ - - old_fs = ad_read(devc, 8); - ad_write(devc, 8, fs); - /* - * Write to I8 starts resynchronization. Wait until it completes. - */ - timeout = 0; - while (timeout < 100 && inb(devc->base) != 0x80) - timeout++; - timeout = 0; - while (timeout < 10000 && inb(devc->base) == 0x80) - timeout++; - } - - if (devc->model == MD_4232) - ad_write(devc, 16, tmp & ~0x30); - - ad_leave_MCE(devc); /* - * Starts the calibration process. - */ - restore_flags(flags); - devc->xfer_count = 0; - -#ifndef EXCLUDE_TIMERS - if (dev == timer_installed && devc->timer_running) - { - if ((fs & 0x01) != (old_fs & 0x01)) - { - ad1848_tmr_reprogram(dev); - } - } -#endif - ad1848_halt_input(dev); - return 0; -} - -static void ad1848_halt(int dev) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - unsigned char bits = ad_read(devc, 9); - - if (bits & 0x01 && (portc->open_mode & OPEN_WRITE)) - ad1848_halt_output(dev); - - if (bits & 0x02 && (portc->open_mode & OPEN_READ)) - ad1848_halt_input(dev); - devc->audio_mode = 0; -} - -static void ad1848_halt_input(int dev) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - unsigned long flags; - - if (!(ad_read(devc, 9) & 0x02)) - return; /* Capture not enabled */ - - save_flags(flags); - cli(); - - ad_mute(devc); - - { - int tmout; - - if(!isa_dma_bridge_buggy) - disable_dma(audio_devs[dev]->dmap_in->dma); - - for (tmout = 0; tmout < 100000; tmout++) - if (ad_read(devc, 11) & 0x10) - break; - ad_write(devc, 9, ad_read(devc, 9) & ~0x02); /* Stop capture */ - - if(!isa_dma_bridge_buggy) - enable_dma(audio_devs[dev]->dmap_in->dma); - devc->audio_mode &= ~PCM_ENABLE_INPUT; - } - - outb(0, io_Status(devc)); /* Clear interrupt status */ - outb(0, io_Status(devc)); /* Clear interrupt status */ - - devc->audio_mode &= ~PCM_ENABLE_INPUT; - - restore_flags(flags); -} - -static void ad1848_halt_output(int dev) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - unsigned long flags; - - if (!(ad_read(devc, 9) & 0x01)) - return; /* Playback not enabled */ - - save_flags(flags); - cli(); - - ad_mute(devc); - { - int tmout; - - if(!isa_dma_bridge_buggy) - disable_dma(audio_devs[dev]->dmap_out->dma); - - for (tmout = 0; tmout < 100000; tmout++) - if (ad_read(devc, 11) & 0x10) - break; - ad_write(devc, 9, ad_read(devc, 9) & ~0x01); /* Stop playback */ - - if(!isa_dma_bridge_buggy) - enable_dma(audio_devs[dev]->dmap_out->dma); - - devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - } - - outb((0), io_Status(devc)); /* Clear interrupt status */ - outb((0), io_Status(devc)); /* Clear interrupt status */ - - devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - - restore_flags(flags); -} - -static void ad1848_trigger(int dev, int state) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - unsigned long flags; - unsigned char tmp, old; - - save_flags(flags); - cli(); - state &= devc->audio_mode; - - tmp = old = ad_read(devc, 9); - - if (portc->open_mode & OPEN_READ) - { - if (state & PCM_ENABLE_INPUT) - tmp |= 0x02; - else - tmp &= ~0x02; - } - if (portc->open_mode & OPEN_WRITE) - { - if (state & PCM_ENABLE_OUTPUT) - tmp |= 0x01; - else - tmp &= ~0x01; - } - /* ad_mute(devc); */ - if (tmp != old) - { - ad_write(devc, 9, tmp); - ad_unmute(devc); - } - restore_flags(flags); -} - -static void ad1848_init_hw(ad1848_info * devc) -{ - int i; - int *init_values; - - /* - * Initial values for the indirect registers of CS4248/AD1848. - */ - static int init_values_a[] = - { - 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00, - - /* Positions 16 to 31 just for CS4231/2 and ad1845 */ - 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - static int init_values_b[] = - { - /* - Values for the newer chips - Some of the register initialization values were changed. In - order to get rid of the click that preceded PCM playback, - calibration was disabled on the 10th byte. On that same byte, - dual DMA was enabled; on the 11th byte, ADC dithering was - enabled, since that is theoretically desirable; on the 13th - byte, Mode 3 was selected, to enable access to extended - registers. - */ - 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x00, 0xe0, 0x01, 0x00, 0x00, - 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - /* - * Select initialisation data - */ - - init_values = init_values_a; - if(devc->model >= MD_4236) - init_values = init_values_b; - - for (i = 0; i < 16; i++) - ad_write(devc, i, init_values[i]); - - - ad_mute(devc); /* Initialize some variables */ - ad_unmute(devc); /* Leave it unmuted now */ - - if (devc->model > MD_1848) - { - if (devc->model == MD_1845_SSCAPE) - ad_write(devc, 12, ad_read(devc, 12) | 0x50); - else - ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ - - if (devc->model == MD_IWAVE) - ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ - - if (devc->model != MD_1845_SSCAPE) - for (i = 16; i < 32; i++) - ad_write(devc, i, init_values[i]); - - if (devc->model == MD_IWAVE) - ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ - } - if (devc->model > MD_1848) - { - if (devc->audio_flags & DMA_DUPLEX) - ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */ - else - ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ - - if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) - ad_write(devc, 27, ad_read(devc, 27) | 0x08); /* Alternate freq select enabled */ - - if (devc->model == MD_IWAVE) - { /* Some magic Interwave specific initialization */ - ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ - ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ - ad_write(devc, 17, 0xc2); /* Alternate feature enable */ - } - } - else - { - devc->audio_flags &= ~DMA_DUPLEX; - ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ - if (soundpro) - ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ - } - - outb((0), io_Status(devc)); /* Clear pending interrupts */ - - /* - * Toggle the MCE bit. It completes the initialization phase. - */ - - ad_enter_MCE(devc); /* In case the bit was off */ - ad_leave_MCE(devc); - - ad1848_mixer_reset(devc); -} - -int ad1848_detect(int io_base, int *ad_flags, int *osp) -{ - unsigned char tmp; - ad1848_info *devc = &adev_info[nr_ad1848_devs]; - unsigned char tmp1 = 0xff, tmp2 = 0xff; - int optiC930 = 0; /* OPTi 82C930 flag */ - int interwave = 0; - int ad1847_flag = 0; - int cs4248_flag = 0; - int sscape_flag = 0; - - int i; - - DDB(printk("ad1848_detect(%x)\n", io_base)); - - if (ad_flags) - { - if (*ad_flags == 0x12345678) - { - interwave = 1; - *ad_flags = 0; - } - - if (*ad_flags == 0x87654321) - { - sscape_flag = 1; - *ad_flags = 0; - } - - if (*ad_flags == 0x12345677) - { - cs4248_flag = 1; - *ad_flags = 0; - } - } - if (nr_ad1848_devs >= MAX_AUDIO_DEV) - { - printk(KERN_ERR "ad1848 - Too many audio devices\n"); - return 0; - } - if (check_region(io_base, 4)) - { - printk(KERN_ERR "ad1848.c: Port %x not free.\n", io_base); - return 0; - } - devc->base = io_base; - devc->irq_ok = 0; - devc->timer_running = 0; - devc->MCE_bit = 0x40; - devc->irq = 0; - devc->open_mode = 0; - devc->chip_name = devc->name = "AD1848"; - devc->model = MD_1848; /* AD1848 or CS4248 */ - devc->levels = NULL; - devc->debug_flag = 0; - - /* - * Check that the I/O address is in use. - * - * The bit 0x80 of the base I/O port is known to be 0 after the - * chip has performed its power on initialization. Just assume - * this has happened before the OS is starting. - * - * If the I/O address is unused, it typically returns 0xff. - */ - - if (inb(devc->base) == 0xff) - { - DDB(printk("ad1848_detect: The base I/O address appears to be dead\n")); - } - - /* - * Wait for the device to stop initialization - */ - - DDB(printk("ad1848_detect() - step 0\n")); - - for (i = 0; i < 10000000; i++) - { - unsigned char x = inb(devc->base); - - if (x == 0xff || !(x & 0x80)) - break; - } - - DDB(printk("ad1848_detect() - step A\n")); - - if (inb(devc->base) == 0x80) /* Not ready. Let's wait */ - ad_leave_MCE(devc); - - if ((inb(devc->base) & 0x80) != 0x00) /* Not a AD1848 */ - { - DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base))); - return 0; - } - - /* - * Test if it's possible to change contents of the indirect registers. - * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only - * so try to avoid using it. - */ - - DDB(printk("ad1848_detect() - step B\n")); - ad_write(devc, 0, 0xaa); - ad_write(devc, 1, 0x45); /* 0x55 with bit 0x10 clear */ - - if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45) - { - if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */ - ad1847_flag = 1; - else - { - DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2)); - return 0; - } - } - DDB(printk("ad1848_detect() - step C\n")); - ad_write(devc, 0, 0x45); - ad_write(devc, 1, 0xaa); - - if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa) - { - if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */ - ad1847_flag = 1; - else - { - DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2)); - return 0; - } - } - - /* - * The indirect register I12 has some read only bits. Let's - * try to change them. - */ - - DDB(printk("ad1848_detect() - step D\n")); - tmp = ad_read(devc, 12); - ad_write(devc, 12, (~tmp) & 0x0f); - - if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f)) - { - DDB(printk("ad1848 detect error - step D (%x)\n", tmp1)); - return 0; - } - - /* - * NOTE! Last 4 bits of the reg I12 tell the chip revision. - * 0x01=RevB and 0x0A=RevC. - */ - - /* - * The original AD1848/CS4248 has just 15 indirect registers. This means - * that I0 and I16 should return the same value (etc.). - * However this doesn't work with CS4248. Actually it seems to be impossible - * to detect if the chip is a CS4231 or CS4248. - * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails - * with CS4231. - */ - - /* - * OPTi 82C930 has mode2 control bit in another place. This test will fail - * with it. Accept this situation as a possible indication of this chip. - */ - - DDB(printk("ad1848_detect() - step F\n")); - ad_write(devc, 12, 0); /* Mode2=disabled */ - - for (i = 0; i < 16; i++) - { - if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) - { - DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2)); - if (!ad1847_flag) - optiC930 = 1; - break; - } - } - - /* - * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40). - * The bit 0x80 is always 1 in CS4248 and CS4231. - */ - - DDB(printk("ad1848_detect() - step G\n")); - - if (ad_flags && *ad_flags == 400) - *ad_flags = 0; - else - ad_write(devc, 12, 0x40); /* Set mode2, clear 0x80 */ - - - if (ad_flags) - *ad_flags = 0; - - tmp1 = ad_read(devc, 12); - if (tmp1 & 0x80) - { - if (ad_flags) - *ad_flags |= AD_F_CS4248; - - devc->chip_name = "CS4248"; /* Our best knowledge just now */ - } - if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40)) - { - /* - * CS4231 detected - is it? - * - * Verify that setting I0 doesn't change I16. - */ - - DDB(printk("ad1848_detect() - step H\n")); - ad_write(devc, 16, 0); /* Set I16 to known value */ - - ad_write(devc, 0, 0x45); - if ((tmp1 = ad_read(devc, 16)) != 0x45) /* No change -> CS4231? */ - { - ad_write(devc, 0, 0xaa); - if ((tmp1 = ad_read(devc, 16)) == 0xaa) /* Rotten bits? */ - { - DDB(printk("ad1848 detect error - step H(%x)\n", tmp1)); - return 0; - } - - /* - * Verify that some bits of I25 are read only. - */ - - DDB(printk("ad1848_detect() - step I\n")); - tmp1 = ad_read(devc, 25); /* Original bits */ - ad_write(devc, 25, ~tmp1); /* Invert all bits */ - if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7)) - { - int id; - - /* - * It's at least CS4231 - */ - - devc->chip_name = "CS4231"; - devc->model = MD_4231; - - /* - * It could be an AD1845 or CS4231A as well. - * CS4231 and AD1845 report the same revision info in I25 - * while the CS4231A reports different. - */ - - id = ad_read(devc, 25); - if ((id & 0xe7) == 0x80) /* Device busy??? */ - id = ad_read(devc, 25); - if ((id & 0xe7) == 0x80) /* Device still busy??? */ - id = ad_read(devc, 25); - DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25))); - - if ((id & 0xe7) == 0x80) { - /* - * It must be a CS4231 or AD1845. The register I23 of - * CS4231 is undefined and it appears to be read only. - * AD1845 uses I23 for setting sample rate. Assume - * the chip is AD1845 if I23 is changeable. - */ - - unsigned char tmp = ad_read(devc, 23); - ad_write(devc, 23, ~tmp); - - if (interwave) - { - devc->model = MD_IWAVE; - devc->chip_name = "IWave"; - } - else if (ad_read(devc, 23) != tmp) /* AD1845 ? */ - { - devc->chip_name = "AD1845"; - devc->model = MD_1845; - } - else if (cs4248_flag) - { - if (ad_flags) - *ad_flags |= AD_F_CS4248; - devc->chip_name = "CS4248"; - devc->model = MD_1848; - ad_write(devc, 12, ad_read(devc, 12) & ~0x40); /* Mode2 off */ - } - ad_write(devc, 23, tmp); /* Restore */ - } - else - { - switch (id & 0x1f) { - case 3: /* CS4236/CS4235/CS42xB/CS4239 */ - { - int xid; - ad_write(devc, 12, ad_read(devc, 12) | 0x60); /* switch to mode 3 */ - ad_write(devc, 23, 0x9c); /* select extended register 25 */ - xid = inb(io_Indexed_Data(devc)); - ad_write(devc, 12, ad_read(devc, 12) & ~0x60); /* back to mode 0 */ - switch (xid & 0x1f) - { - case 0x00: - devc->chip_name = "CS4237B(B)"; - devc->model = MD_42xB; - break; - case 0x08: - /* Seems to be a 4238 ?? */ - devc->chip_name = "CS4238"; - devc->model = MD_42xB; - break; - case 0x09: - devc->chip_name = "CS4238B"; - devc->model = MD_42xB; - break; - case 0x0b: - devc->chip_name = "CS4236B"; - devc->model = MD_4236; - break; - case 0x10: - devc->chip_name = "CS4237B"; - devc->model = MD_42xB; - break; - case 0x1d: - devc->chip_name = "CS4235"; - devc->model = MD_4235; - break; - case 0x1e: - devc->chip_name = "CS4239"; - devc->model = MD_4239; - break; - default: - printk("Chip ident is %X.\n", xid&0x1F); - devc->chip_name = "CS42xx"; - devc->model = MD_4232; - break; - } - } - break; - - case 2: /* CS4232/CS4232A */ - devc->chip_name = "CS4232"; - devc->model = MD_4232; - break; - - case 0: - if ((id & 0xe0) == 0xa0) - { - devc->chip_name = "CS4231A"; - devc->model = MD_4231A; - } - else - { - devc->chip_name = "CS4321"; - devc->model = MD_4231; - } - break; - - default: /* maybe */ - DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7)); - if (optiC930) - { - devc->chip_name = "82C930"; - devc->model = MD_C930; - } - else - { - devc->chip_name = "CS4231"; - devc->model = MD_4231; - } - } - } - } - ad_write(devc, 25, tmp1); /* Restore bits */ - - DDB(printk("ad1848_detect() - step K\n")); - } - } else if (tmp1 == 0x0a) { - /* - * Is it perhaps a SoundPro CMI8330? - * If so, then we should be able to change indirect registers - * greater than I15 after activating MODE2, even though reading - * back I12 does not show it. - */ - - /* - * Let's try comparing register values - */ - for (i = 0; i < 16; i++) { - if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) { - DDB(printk("ad1848 detect step H(%d/%x/%x) - SoundPro chip?\n", i, tmp1, tmp2)); - soundpro = 1; - devc->chip_name = "SoundPro CMI 8330"; - break; - } - } - } - - DDB(printk("ad1848_detect() - step L\n")); - if (ad_flags) - { - if (devc->model != MD_1848) - *ad_flags |= AD_F_CS4231; - } - DDB(printk("ad1848_detect() - Detected OK\n")); - - if (devc->model == MD_1848 && ad1847_flag) - devc->chip_name = "AD1847"; - - - if (sscape_flag == 1) - devc->model = MD_1845_SSCAPE; - - return 1; -} - -int ad1848_init (char *name, int io_base, int irq, int dma_playback, - int dma_capture, int share_dma, int *osp, struct module *owner) -{ - /* - * NOTE! If irq < 0, there is another driver which has allocated the IRQ - * so that this driver doesn't need to allocate/deallocate it. - * The actually used IRQ is ABS(irq). - */ - - int my_dev; - char dev_name[100]; - int e; - - ad1848_info *devc = &adev_info[nr_ad1848_devs]; - - ad1848_port_info *portc = NULL; - - devc->irq = (irq > 0) ? irq : 0; - devc->open_mode = 0; - devc->timer_ticks = 0; - devc->dma1 = dma_playback; - devc->dma2 = dma_capture; - devc->subtype = cfg.card_subtype; - devc->audio_flags = DMA_AUTOMODE; - devc->playback_dev = devc->record_dev = 0; - if (name != NULL) - devc->name = name; - - if (name != NULL && name[0] != 0) - sprintf(dev_name, - "%s (%s)", name, devc->chip_name); - else - sprintf(dev_name, - "Generic audio codec (%s)", devc->chip_name); - - request_region(devc->base, 4, devc->name); - - conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture); - - if (devc->model == MD_1848 || devc->model == MD_C930) - devc->audio_flags |= DMA_HARDSTOP; - - if (devc->model > MD_1848) - { - if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1) - devc->audio_flags &= ~DMA_DUPLEX; - else - devc->audio_flags |= DMA_DUPLEX; - } - - portc = (ad1848_port_info *) kmalloc(sizeof(ad1848_port_info), GFP_KERNEL); - if(portc==NULL) - return -1; - - if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, - dev_name, - &ad1848_audio_driver, - sizeof(struct audio_driver), - devc->audio_flags, - ad_format_mask[devc->model], - devc, - dma_playback, - dma_capture)) < 0) - { - kfree(portc); - portc=NULL; - return -1; - } - - audio_devs[my_dev]->portc = portc; - audio_devs[my_dev]->mixer_dev = -1; - if (owner) - audio_devs[my_dev]->d->owner = owner; - memset((char *) portc, 0, sizeof(*portc)); - - nr_ad1848_devs++; - - devc->pmdev = pm_register(PM_ISA_DEV, my_dev, ad1848_pm_callback); - if (devc->pmdev) - devc->pmdev->data = devc; - - ad1848_init_hw(devc); - - if (irq > 0) - { - devc->dev_no = my_dev; - if (request_irq(devc->irq, adintr, 0, devc->name, (void *)my_dev) < 0) - { - printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n"); - /* Don't free it either then.. */ - devc->irq = 0; - } - if (capabilities[devc->model].flags & CAP_F_TIMER) - { -#ifndef CONFIG_SMP - int x; - unsigned char tmp = ad_read(devc, 16); -#endif - - devc->timer_ticks = 0; - - ad_write(devc, 21, 0x00); /* Timer MSB */ - ad_write(devc, 20, 0x10); /* Timer LSB */ -#ifndef CONFIG_SMP - ad_write(devc, 16, tmp | 0x40); /* Enable timer */ - for (x = 0; x < 100000 && devc->timer_ticks == 0; x++); - ad_write(devc, 16, tmp & ~0x40); /* Disable timer */ - - if (devc->timer_ticks == 0) - printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", irq); - else - { - DDB(printk("Interrupt test OK\n")); - devc->irq_ok = 1; - } -#else - devc->irq_ok = 1; -#endif - } - else - devc->irq_ok = 1; /* Couldn't test. assume it's OK */ - } else if (irq < 0) - irq2dev[-irq] = devc->dev_no = my_dev; - -#ifndef EXCLUDE_TIMERS - if ((capabilities[devc->model].flags & CAP_F_TIMER) && - devc->irq_ok) - ad1848_tmr_install(my_dev); -#endif - - if (!share_dma) - { - if (sound_alloc_dma(dma_playback, devc->name)) - printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_playback); - - if (dma_capture != dma_playback) - if (sound_alloc_dma(dma_capture, devc->name)) - printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_capture); - } - - if ((e = sound_install_mixer(MIXER_DRIVER_VERSION, - dev_name, - &ad1848_mixer_operations, - sizeof(struct mixer_operations), - devc)) >= 0) - { - audio_devs[my_dev]->mixer_dev = e; - if (owner) - mixer_devs[e]->owner = owner; - } - return my_dev; -} - -int ad1848_control(int cmd, int arg) -{ - ad1848_info *devc; - - if (nr_ad1848_devs < 1) - return -ENODEV; - - devc = &adev_info[nr_ad1848_devs - 1]; - - switch (cmd) - { - case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */ - if (devc->model != MD_1845 || devc->model != MD_1845_SSCAPE) - return -EINVAL; - ad_enter_MCE(devc); - ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5)); - ad_leave_MCE(devc); - break; - - case AD1848_MIXER_REROUTE: - { - int o = (arg >> 8) & 0xff; - int n = arg & 0xff; - - if (o < 0 || o >= SOUND_MIXER_NRDEVICES) - return -EINVAL; - - if (!(devc->supported_devices & (1 << o)) && - !(devc->supported_rec_devices & (1 << o))) - return -EINVAL; - - if (n == SOUND_MIXER_NONE) - { /* Just hide this control */ - ad1848_mixer_set(devc, o, 0); /* Shut up it */ - devc->supported_devices &= ~(1 << o); - devc->supported_rec_devices &= ~(1 << o); - break; - } - - /* Make the mixer control identified by o to appear as n */ - if (n < 0 || n >= SOUND_MIXER_NRDEVICES) - return -EINVAL; - - devc->mixer_reroute[n] = o; /* Rename the control */ - if (devc->supported_devices & (1 << o)) - devc->supported_devices |= (1 << n); - if (devc->supported_rec_devices & (1 << o)) - devc->supported_rec_devices |= (1 << n); - - devc->supported_devices &= ~(1 << o); - devc->supported_rec_devices &= ~(1 << o); - } - break; - } - return 0; -} - -void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma) -{ - int i, mixer, dev = 0; - ad1848_info *devc = NULL; - - for (i = 0; devc == NULL && i < nr_ad1848_devs; i++) - { - if (adev_info[i].base == io_base) - { - devc = &adev_info[i]; - dev = devc->dev_no; - } - } - - if (devc != NULL) - { - if(audio_devs[dev]->portc!=NULL) - kfree(audio_devs[dev]->portc); - release_region(devc->base, 4); - - if (!share_dma) - { - if (devc->irq > 0) /* There is no point in freeing irq, if it wasn't allocated */ - free_irq(devc->irq, (void *)devc->dev_no); - - sound_free_dma(dma_playback); - - if (dma_playback != dma_capture) - sound_free_dma(dma_capture); - - } - mixer = audio_devs[devc->dev_no]->mixer_dev; - if(mixer>=0) - sound_unload_mixerdev(mixer); - - if (devc->pmdev) - pm_unregister(devc->pmdev); - - nr_ad1848_devs--; - for ( ; i < nr_ad1848_devs ; i++) - adev_info[i] = adev_info[i+1]; - } - else - printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base); -} - -void adintr(int irq, void *dev_id, struct pt_regs *dummy) -{ - unsigned char status; - ad1848_info *devc; - int dev; - int alt_stat = 0xff; - unsigned char c930_stat = 0; - int cnt = 0; - - dev = (int)dev_id; - devc = (ad1848_info *) audio_devs[dev]->devc; - -interrupt_again: /* Jump back here if int status doesn't reset */ - - status = inb(io_Status(devc)); - - if (status == 0x80) - printk(KERN_DEBUG "adintr: Why?\n"); - if (devc->model == MD_1848) - outb((0), io_Status(devc)); /* Clear interrupt status */ - - if (status & 0x01) - { - if (devc->model == MD_C930) - { /* 82C930 has interrupt status register in MAD16 register MC11 */ - unsigned long flags; - - save_flags(flags); - cli(); - - /* 0xe0e is C930 address port - * 0xe0f is C930 data port - */ - outb(11, 0xe0e); - c930_stat = inb(0xe0f); - outb((~c930_stat), 0xe0f); - - restore_flags(flags); - - alt_stat = (c930_stat << 2) & 0x30; - } - else if (devc->model != MD_1848) - { - alt_stat = ad_read(devc, 24); - ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */ - } - - if ((devc->open_mode & OPEN_READ) && (devc->audio_mode & PCM_ENABLE_INPUT) && (alt_stat & 0x20)) - { - DMAbuf_inputintr(devc->record_dev); - } - if ((devc->open_mode & OPEN_WRITE) && (devc->audio_mode & PCM_ENABLE_OUTPUT) && - (alt_stat & 0x10)) - { - DMAbuf_outputintr(devc->playback_dev, 1); - } - if (devc->model != MD_1848 && (alt_stat & 0x40)) /* Timer interrupt */ - { - devc->timer_ticks++; -#ifndef EXCLUDE_TIMERS - if (timer_installed == dev && devc->timer_running) - sound_timer_interrupt(); -#endif - } - } -/* - * Sometimes playback or capture interrupts occur while a timer interrupt - * is being handled. The interrupt will not be retriggered if we don't - * handle it now. Check if an interrupt is still pending and restart - * the handler in this case. - */ - if (inb(io_Status(devc)) & 0x01 && cnt++ < 4) - { - goto interrupt_again; - } -} - -/* - * Experimental initialization sequence for the integrated sound system - * of the Compaq Deskpro M. - */ - -static int init_deskpro_m(struct address_info *hw_config) -{ - unsigned char tmp; - - if ((tmp = inb(0xc44)) == 0xff) - { - DDB(printk("init_deskpro_m: Dead port 0xc44\n")); - return 0; - } - - outb(0x10, 0xc44); - outb(0x40, 0xc45); - outb(0x00, 0xc46); - outb(0xe8, 0xc47); - outb(0x14, 0xc44); - outb(0x40, 0xc45); - outb(0x00, 0xc46); - outb(0xe8, 0xc47); - outb(0x10, 0xc44); - - return 1; -} - -/* - * Experimental initialization sequence for the integrated sound system - * of Compaq Deskpro XL. - */ - -static int init_deskpro(struct address_info *hw_config) -{ - unsigned char tmp; - - if ((tmp = inb(0xc44)) == 0xff) - { - DDB(printk("init_deskpro: Dead port 0xc44\n")); - return 0; - } - outb((tmp | 0x04), 0xc44); /* Select bank 1 */ - if (inb(0xc44) != 0x04) - { - DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n")); - return 0; - } - /* - * OK. It looks like a Deskpro so let's proceed. - */ - - /* - * I/O port 0xc44 Audio configuration register. - * - * bits 0xc0: Audio revision bits - * 0x00 = Compaq Business Audio - * 0x40 = MS Sound System Compatible (reset default) - * 0x80 = Reserved - * 0xc0 = Reserved - * bit 0x20: No Wait State Enable - * 0x00 = Disabled (reset default, DMA mode) - * 0x20 = Enabled (programmed I/O mode) - * bit 0x10: MS Sound System Decode Enable - * 0x00 = Decoding disabled (reset default) - * 0x10 = Decoding enabled - * bit 0x08: FM Synthesis Decode Enable - * 0x00 = Decoding Disabled (reset default) - * 0x08 = Decoding enabled - * bit 0x04 Bank select - * 0x00 = Bank 0 - * 0x04 = Bank 1 - * bits 0x03 MSS Base address - * 0x00 = 0x530 (reset default) - * 0x01 = 0x604 - * 0x02 = 0xf40 - * 0x03 = 0xe80 - */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc44 (before): "); - outb((tmp & ~0x04), 0xc44); - printk("%02x ", inb(0xc44)); - outb((tmp | 0x04), 0xc44); - printk("%02x\n", inb(0xc44)); -#endif - - /* Set bank 1 of the register */ - tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */ - - switch (hw_config->io_base) - { - case 0x530: - tmp |= 0x00; - break; - case 0x604: - tmp |= 0x01; - break; - case 0xf40: - tmp |= 0x02; - break; - case 0xe80: - tmp |= 0x03; - break; - default: - DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base)); - return 0; - } - outb((tmp & ~0x04), 0xc44); /* Write to bank=0 */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc44 (after): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc44)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc44)); -#endif - - /* - * I/O port 0xc45 FM Address Decode/MSS ID Register. - * - * bank=0, bits 0xfe: FM synthesis Decode Compare bits 7:1 (default=0x88) - * bank=0, bit 0x01: SBIC Power Control Bit - * 0x00 = Powered up - * 0x01 = Powered down - * bank=1, bits 0xfc: MSS ID (default=0x40) - */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc45 (before): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc45)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc45)); -#endif - - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - outb((0x88), 0xc45); /* FM base 7:0 = 0x88 */ - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - outb((0x10), 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc45 (after): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc45)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc45)); -#endif - - - /* - * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register. - * - * bank=0, bits 0xff: FM synthesis Decode Compare bits 15:8 (default=0x03) - * bank=1, bits 0xff: Audio addressing ASIC id - */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc46 (before): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc46)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc46)); -#endif - - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - outb((0x03), 0xc46); /* FM base 15:8 = 0x03 */ - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - outb((0x11), 0xc46); /* ASIC ID = 0x11 */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc46 (after): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc46)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc46)); -#endif - - /* - * I/O port 0xc47 FM Address Decode Register. - * - * bank=0, bits 0xff: Decode enable selection for various FM address bits - * bank=1, bits 0xff: Reserved - */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc47 (before): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc47)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc47)); -#endif - - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - outb((0x7c), 0xc47); /* FM decode enable bits = 0x7c */ - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - outb((0x00), 0xc47); /* Reserved bank1 = 0x00 */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc47 (after): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc47)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc47)); -#endif - - /* - * I/O port 0xc6f = Audio Disable Function Register - */ - -#ifdef DEBUGXL - printk("Port 0xc6f (before) = %02x\n", inb(0xc6f)); -#endif - - outb((0x80), 0xc6f); - -#ifdef DEBUGXL - printk("Port 0xc6f (after) = %02x\n", inb(0xc6f)); -#endif - - return 1; -} - -int probe_ms_sound(struct address_info *hw_config) -{ - unsigned char tmp; - - DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype)); - - if (check_region(hw_config->io_base, 8)) - { - printk(KERN_ERR "MSS: I/O port conflict\n"); - return 0; - } - if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ - { - /* check_opl3(0x388, hw_config); */ - return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); - } - - if (deskpro_xl && hw_config->card_subtype == 2) /* Compaq Deskpro XL */ - { - if (!init_deskpro(hw_config)) - return 0; - } - - if (deskpro_m) /* Compaq Deskpro M */ - { - if (!init_deskpro_m(hw_config)) - return 0; - } - - /* - * Check if the IO port returns valid signature. The original MS Sound - * system returns 0x04 while some cards (AudioTrix Pro for example) - * return 0x00 or 0x0f. - */ - - if ((tmp = inb(hw_config->io_base + 3)) == 0xff) /* Bus float */ - { - int ret; - - DDB(printk("I/O address is inactive (%x)\n", tmp)); - if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp))) - return 0; - return 1; - } - DDB(printk("MSS signature = %x\n", tmp & 0x3f)); - if ((tmp & 0x3f) != 0x04 && - (tmp & 0x3f) != 0x0f && - (tmp & 0x3f) != 0x00) - { - int ret; - - MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3))); - DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n")); - if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp))) - return 0; - - hw_config->card_subtype = 1; - return 1; - } - if ((hw_config->irq != 5) && - (hw_config->irq != 7) && - (hw_config->irq != 9) && - (hw_config->irq != 10) && - (hw_config->irq != 11) && - (hw_config->irq != 12)) - { - printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); - return 0; - } - if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) - { - printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma); - return 0; - } - /* - * Check that DMA0 is not in use with a 8 bit board. - */ - - if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) - { - printk(KERN_ERR "MSS: Can't use DMA0 with a 8 bit card/slot\n"); - return 0; - } - if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) - { - printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); - return 0; - } - return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); -} - -void attach_ms_sound(struct address_info *hw_config, struct module *owner) -{ - static signed char interrupt_bits[12] = - { - -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 - }; - signed char bits; - char dma2_bit = 0; - - static char dma_bits[4] = - { - 1, 2, 0, 3 - }; - - int config_port = hw_config->io_base + 0; - int version_port = hw_config->io_base + 3; - int dma = hw_config->dma; - int dma2 = hw_config->dma2; - - if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ - { - hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - hw_config->dma2, 0, - hw_config->osp, - owner); - request_region(hw_config->io_base, 4, "WSS config"); - return; - } - /* - * Set the IRQ and DMA addresses. - */ - - bits = interrupt_bits[hw_config->irq]; - if (bits == -1) - { - printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); - return; - } - outb((bits | 0x40), config_port); - if ((inb(version_port) & 0x40) == 0) - printk(KERN_ERR "[MSS: IRQ Conflict?]\n"); - -/* - * Handle the capture DMA channel - */ - - if (dma2 != -1 && dma2 != dma) - { - if (!((dma == 0 && dma2 == 1) || - (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0))) - { /* Unsupported combination. Try to swap channels */ - int tmp = dma; - - dma = dma2; - dma2 = tmp; - } - if ((dma == 0 && dma2 == 1) || - (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0)) - { - dma2_bit = 0x04; /* Enable capture DMA */ - } - else - { - printk(KERN_WARNING "MSS: Invalid capture DMA\n"); - dma2 = dma; - } - } - else - { - dma2 = dma; - } - - hw_config->dma = dma; - hw_config->dma2 = dma2; - - outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ - - hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, - hw_config->irq, - dma, dma2, 0, - hw_config->osp, - THIS_MODULE); - request_region(hw_config->io_base, 4, "WSS config"); -} - -void unload_ms_sound(struct address_info *hw_config) -{ - ad1848_unload(hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - hw_config->dma2, 0); - sound_unload_audiodev(hw_config->slots[0]); - release_region(hw_config->io_base, 4); -} - -#ifndef EXCLUDE_TIMERS - -/* - * Timer stuff (for /dev/music). - */ - -static unsigned int current_interval = 0; - -static unsigned int ad1848_tmr_start(int dev, unsigned int usecs) -{ - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */ - unsigned long divider; - - save_flags(flags); - cli(); - - /* - * Length of the timer interval (in nanoseconds) depends on the - * selected crystal oscillator. Check this from bit 0x01 of I8. - * - * AD1845 has just one oscillator which has cycle time of 10.050 us - * (when a 24.576 MHz xtal oscillator is used). - * - * Convert requested interval to nanoseconds before computing - * the timer divider. - */ - - if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) - xtal_nsecs = 10050; - else if (ad_read(devc, 8) & 0x01) - xtal_nsecs = 9920; - else - xtal_nsecs = 9969; - - divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs; - - if (divider < 100) /* Don't allow shorter intervals than about 1ms */ - divider = 100; - - if (divider > 65535) /* Overflow check */ - divider = 65535; - - ad_write(devc, 21, (divider >> 8) & 0xff); /* Set upper bits */ - ad_write(devc, 20, divider & 0xff); /* Set lower bits */ - ad_write(devc, 16, ad_read(devc, 16) | 0x40); /* Start the timer */ - devc->timer_running = 1; - restore_flags(flags); - - return current_interval = (divider * xtal_nsecs + 500) / 1000; -} - -static void ad1848_tmr_reprogram(int dev) -{ - /* - * Audio driver has changed sampling rate so that a different xtal - * oscillator was selected. We have to reprogram the timer rate. - */ - - ad1848_tmr_start(dev, current_interval); - sound_timer_syncinterval(current_interval); -} - -static void ad1848_tmr_disable(int dev) -{ - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - save_flags(flags); - cli(); - ad_write(devc, 16, ad_read(devc, 16) & ~0x40); - devc->timer_running = 0; - restore_flags(flags); -} - -static void ad1848_tmr_restart(int dev) -{ - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - if (current_interval == 0) - return; - - save_flags(flags); - cli(); - ad_write(devc, 16, ad_read(devc, 16) | 0x40); - devc->timer_running = 1; - restore_flags(flags); -} - -static struct sound_lowlev_timer ad1848_tmr = -{ - 0, - 2, - ad1848_tmr_start, - ad1848_tmr_disable, - ad1848_tmr_restart -}; - -static int ad1848_tmr_install(int dev) -{ - if (timer_installed != -1) - return 0; /* Don't install another timer */ - - timer_installed = ad1848_tmr.dev = dev; - sound_timer_init(&ad1848_tmr, audio_devs[dev]->name); - - return 1; -} -#endif /* EXCLUDE_TIMERS */ - -static int ad1848_suspend(ad1848_info *devc) -{ - unsigned long flags; - - save_flags(flags); - cli(); - - ad_mute(devc); - - restore_flags(flags); - return 0; -} - -static int ad1848_resume(ad1848_info *devc) -{ - unsigned long flags; - int mixer_levels[32], i; - - save_flags(flags); - cli(); - - /* Thinkpad is a bit more of PITA than normal. The BIOS tends to - restore it in a different config to the one we use. Need to - fix this somehow */ - - /* store old mixer levels */ - memcpy(mixer_levels, devc->levels, sizeof (mixer_levels)); - ad1848_init_hw(devc); - - /* restore mixer levels */ - for (i = 0; i < 32; i++) - ad1848_mixer_set(devc, devc->dev_no, mixer_levels[i]); - - if (!devc->subtype) { - static signed char interrupt_bits[12] = { -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 }; - static char dma_bits[4] = { 1, 2, 0, 3 }; - - signed char bits; - char dma2_bit = 0; - - int config_port = devc->base + 0; - - bits = interrupt_bits[devc->irq]; - if (bits == -1) { - printk(KERN_ERR "MSS: Bad IRQ %d\n", devc->irq); - restore_flags(flags); - return -1; - } - - outb((bits | 0x40), config_port); - - if (devc->dma2 != -1 && devc->dma2 != devc->dma1) - if ( (devc->dma1 == 0 && devc->dma2 == 1) || - (devc->dma1 == 1 && devc->dma2 == 0) || - (devc->dma1 == 3 && devc->dma2 == 0)) - dma2_bit = 0x04; - - outb((bits | dma_bits[devc->dma1] | dma2_bit), config_port); - } - - restore_flags(flags); - return 0; -} - -static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - ad1848_info *devc = dev->data; - if (devc) { - DEB(printk("ad1848: pm event received: 0x%x\n", rqst)); - - switch (rqst) { - case PM_SUSPEND: - ad1848_suspend(devc); - break; - case PM_RESUME: - ad1848_resume(devc); - break; - } - } - return 0; -} - - -EXPORT_SYMBOL(ad1848_detect); -EXPORT_SYMBOL(ad1848_init); -EXPORT_SYMBOL(ad1848_unload); -EXPORT_SYMBOL(ad1848_control); -EXPORT_SYMBOL(adintr); -EXPORT_SYMBOL(probe_ms_sound); -EXPORT_SYMBOL(attach_ms_sound); -EXPORT_SYMBOL(unload_ms_sound); - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; -static int __initdata type = 0; - -MODULE_PARM(io, "i"); /* I/O for a raw AD1848 card */ -MODULE_PARM(irq, "i"); /* IRQ to use */ -MODULE_PARM(dma, "i"); /* First DMA channel */ -MODULE_PARM(dma2, "i"); /* Second DMA channel */ -MODULE_PARM(type, "i"); /* Card type */ -MODULE_PARM(deskpro_xl, "i"); /* Special magic for Deskpro XL boxen */ -MODULE_PARM(deskpro_m, "i"); /* Special magic for Deskpro M box */ -MODULE_PARM(soundpro, "i"); /* More special magic for SoundPro chips */ - -#ifdef __ISAPNP__ -MODULE_PARM(isapnp, "i"); -MODULE_PARM(isapnpjump, "i"); -MODULE_PARM(reverse, "i"); -MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled"); -MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke."); -MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order"); - -struct pci_dev *ad1848_dev = NULL; - -/* Please add new entries at the end of the table */ -static struct { - char *name; - unsigned short card_vendor, card_device, - vendor, function; - short mss_io, irq, dma, dma2; /* index into isapnp table */ - int type; -} ad1848_isapnp_list[] __initdata = { - {"CMI 8330 SoundPRO", - ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), - 0, 0, 0,-1, 0}, - {"CS4232 based card", - ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), - 0, 0, 0, 1, 0}, - {"CS4232 based card", - ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), - 0, 0, 0, 1, 0}, - {"OPL3-SA2 WSS mode", - ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), - 1, 0, 0, 1, 1}, - {"Advanced Gravis InterWave Audio", - ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), - 0, 0, 0, 1, 0}, - {0} -}; - -static struct isapnp_device_id id_table[] __devinitdata = { - { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), 0 }, - { ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), 0 }, - {0} -}; - -MODULE_DEVICE_TABLE(isapnp, id_table); - -static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) -{ - int err; - - /* Device already active? Let's use it */ - if(dev->active) - return(dev); - - if((err = dev->activate(dev)) < 0) { - printk(KERN_ERR "ad1848: %s %s config failed (out of resources?)[%d]\n", devname, resname, err); - - dev->deactivate(dev); - - return(NULL); - } - return(dev); -} - -static struct pci_dev *ad1848_init_generic(struct pci_bus *bus, struct address_info *hw_config, int slot) -{ - - /* Configure Audio device */ - if((ad1848_dev = isapnp_find_dev(bus, ad1848_isapnp_list[slot].vendor, ad1848_isapnp_list[slot].function, NULL))) - { - int ret; - ret = ad1848_dev->prepare(ad1848_dev); - /* If device is active, assume configured with /proc/isapnp - * and use anyway. Some other way to check this? */ - if(ret && ret != -EBUSY) { - printk(KERN_ERR "ad1848: ISAPnP found device that could not be autoconfigured.\n"); - return(NULL); - } - if(ret == -EBUSY) - audio_activated = 1; - - if((ad1848_dev = activate_dev(ad1848_isapnp_list[slot].name, "ad1848", ad1848_dev))) - { - hw_config->io_base = ad1848_dev->resource[ad1848_isapnp_list[slot].mss_io].start; - hw_config->irq = ad1848_dev->irq_resource[ad1848_isapnp_list[slot].irq].start; - hw_config->dma = ad1848_dev->dma_resource[ad1848_isapnp_list[slot].dma].start; - if(ad1848_isapnp_list[slot].dma2 != -1) - hw_config->dma2 = ad1848_dev->dma_resource[ad1848_isapnp_list[slot].dma2].start; - else - hw_config->dma2 = -1; - hw_config->card_subtype = ad1848_isapnp_list[slot].type; - } else - return(NULL); - } else - return(NULL); - - return(ad1848_dev); -} - -static int __init ad1848_isapnp_init(struct address_info *hw_config, struct pci_bus *bus, int slot) -{ - char *busname = bus->name[0] ? bus->name : ad1848_isapnp_list[slot].name; - - printk(KERN_INFO "ad1848: %s detected\n", busname); - - /* Initialize this baby. */ - - if(ad1848_init_generic(bus, hw_config, slot)) { - /* We got it. */ - - printk(KERN_NOTICE "ad1848: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", - busname, - hw_config->io_base, hw_config->irq, hw_config->dma, - hw_config->dma2); - return 1; - } - else - printk(KERN_INFO "ad1848: Failed to initialize %s\n", busname); - - return 0; -} - -static int __init ad1848_isapnp_probe(struct address_info *hw_config) -{ - static int first = 1; - int i; - - /* Count entries in sb_isapnp_list */ - for (i = 0; ad1848_isapnp_list[i].card_vendor != 0; i++); - i--; - - /* Check and adjust isapnpjump */ - if( isapnpjump < 0 || isapnpjump > i) { - isapnpjump = reverse ? i : 0; - printk(KERN_ERR "ad1848: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump); - } - - if(!first || !reverse) - i = isapnpjump; - first = 0; - while(ad1848_isapnp_list[i].card_vendor != 0) { - static struct pci_bus *bus = NULL; - - while ((bus = isapnp_find_card( - ad1848_isapnp_list[i].card_vendor, - ad1848_isapnp_list[i].card_device, - bus))) { - - if(ad1848_isapnp_init(hw_config, bus, i)) { - isapnpjump = i; /* start next search from here */ - return 0; - } - } - i += reverse ? -1 : 1; - } - - return -ENODEV; -} -#endif - - -static int __init init_ad1848(void) -{ - printk(KERN_INFO "ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - -#ifdef __ISAPNP__ - if(isapnp && (ad1848_isapnp_probe(&cfg) < 0) ) { - printk(KERN_NOTICE "ad1848: No ISAPnP cards found, trying standard ones...\n"); - isapnp = 0; - } -#endif - - if(io != -1) { - if( isapnp == 0 ) - { - if(irq == -1 || dma == -1) { - printk(KERN_WARNING "ad1848: must give I/O , IRQ and DMA.\n"); - return -EINVAL; - } - - cfg.irq = irq; - cfg.io_base = io; - cfg.dma = dma; - cfg.dma2 = dma2; - cfg.card_subtype = type; - } - - if(!probe_ms_sound(&cfg)) - return -ENODEV; - attach_ms_sound(&cfg, THIS_MODULE); - loaded = 1; - } - return 0; -} - -static void __exit cleanup_ad1848(void) -{ - if(loaded) - unload_ms_sound(&cfg); - -#ifdef __ISAPNP__ - if(audio_activated) - if(ad1848_dev) - ad1848_dev->deactivate(ad1848_dev); -#endif -} - -module_init(init_ad1848); -module_exit(cleanup_ad1848); - -#ifndef MODULE -static int __init setup_ad1848(char *str) -{ - /* io, irq, dma, dma2, type */ - int ints[6]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - type = ints[5]; - - return 1; -} - -__setup("ad1848=", setup_ad1848); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/ad1848.h b/drivers/sound/ad1848.h --- a/drivers/sound/ad1848.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,28 +0,0 @@ -/* - * ad1848.c - * - * Copyright: Christoph Hellwig - */ - -#define AD_F_CS4231 0x0001 /* Returned if a CS4232 (or compatible) detected */ -#define AD_F_CS4248 0x0001 /* Returned if a CS4248 (or compatible) detected */ - -#define AD1848_SET_XTAL 1 -#define AD1848_MIXER_REROUTE 2 - -#define AD1848_REROUTE(oldctl, newctl) \ - ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl)) - - -int ad1848_init(char *name, int io_base, int irq, int dma_playback, - int dma_capture, int share_dma, int *osp, struct module *owner); -void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma); - -int ad1848_detect (int io_base, int *flags, int *osp); -int ad1848_control(int cmd, int arg); - -void adintr(int irq, void *dev_id, struct pt_regs * dummy); -void attach_ms_sound(struct address_info * hw_config, struct module * owner); - -int probe_ms_sound(struct address_info *hw_config); -void unload_ms_sound(struct address_info *hw_info); diff -Nru a/drivers/sound/ad1848_mixer.h b/drivers/sound/ad1848_mixer.h --- a/drivers/sound/ad1848_mixer.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,253 +0,0 @@ -/* - * sound/ad1848_mixer.h - * - * Definitions for the mixer of AD1848 and compatible codecs. - */ - -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - - -/* - * The AD1848 codec has generic input lines called Line, Aux1 and Aux2. - * Sound card manufacturers have connected actual inputs (CD, synth, line, - * etc) to these inputs in different order. Therefore it's difficult - * to assign mixer channels to these inputs correctly. The following - * contains two alternative mappings. The first one is for GUS MAX and - * the second is just a generic one (line1, line2 and line3). - * (Actually this is not a mapping but rather some kind of interleaving - * solution). - */ -#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \ - SOUND_MASK_LINE1 | SOUND_MASK_IMIX) - -#define SPRO_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_LINE1) - -#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \ - SOUND_MASK_LINE2 | \ - SOUND_MASK_IGAIN | \ - SOUND_MASK_PCM | SOUND_MASK_IMIX) - -#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \ - SOUND_MASK_MIC | \ - SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \ - SOUND_MASK_IGAIN | \ - SOUND_MASK_PCM | SOUND_MASK_IMIX) - -#define MODE3_MIXER_DEVICES (MODE2_MIXER_DEVICES | SOUND_MASK_VOLUME) - -/* OPTi 82C930 has no IMIX level control, but it can still be selected as an - * input - */ -#define C930_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \ - SOUND_MASK_MIC | SOUND_MASK_VOLUME | \ - SOUND_MASK_LINE3 | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM) - -#define SPRO_MIXER_DEVICES (SOUND_MASK_VOLUME | SOUND_MASK_PCM | \ - SOUND_MASK_LINE | SOUND_MASK_SYNTH | \ - SOUND_MASK_CD | SOUND_MASK_MIC | \ - SOUND_MASK_SPEAKER | SOUND_MASK_LINE1 | \ - SOUND_MASK_OGAIN) - -struct mixer_def { - unsigned int regno:6; /* register number for volume */ - unsigned int polarity:1; /* volume polarity: 0=normal, 1=reversed */ - unsigned int bitpos:3; /* position of bits in register for volume */ - unsigned int nbits:3; /* number of bits in register for volume */ - unsigned int mutereg:6; /* register number for mute bit */ - unsigned int mutepol:1; /* mute polarity: 0=normal, 1=reversed */ - unsigned int mutepos:4; /* position of mute bit in register */ - unsigned int recreg:6; /* register number for recording bit */ - unsigned int recpol:1; /* recording polarity: 0=normal, 1=reversed */ - unsigned int recpos:4; /* position of recording bit in register */ -}; - -static char mix_cvt[101] = { - 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, - 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, - 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, - 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, - 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, - 100 -}; - -typedef struct mixer_def mixer_ent; -typedef mixer_ent mixer_ents[2]; - -/* - * Most of the mixer entries work in backwards. Setting the polarity field - * makes them to work correctly. - * - * The channel numbering used by individual sound cards is not fixed. Some - * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs. - * The current version doesn't try to compensate this. - */ - -#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit) \ - [name] = {{reg_l, pola_l, pos_l, len_l, reg_l, 0, mute_bit, 0, 0, 8}, \ - {reg_r, pola_r, pos_r, len_r, reg_r, 0, mute_bit, 0, 0, 8}} - -#define MIX_ENT2(name, reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \ - rec_reg_l, rec_pola_l, rec_pos_l, \ - reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \ - rec_reg_r, rec_pola_r, rec_pos_r) \ - [name] = {{reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \ - rec_reg_l, rec_pola_l, rec_pos_l}, \ - {reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \ - rec_reg_r, rec_pola_r, rec_pos_r}} - -static mixer_ents ad1848_mix_devices[32] = { - MIX_ENT(SOUND_MIXER_VOLUME, 27, 1, 0, 4, 29, 1, 0, 4, 8), - MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), - MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8), - MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), - MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7) -}; - -static mixer_ents iwave_mix_devices[32] = { - MIX_ENT(SOUND_MIXER_VOLUME, 25, 1, 0, 5, 27, 1, 0, 5, 8), - MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), - MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8), - MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_IMIX, 16, 1, 0, 5, 17, 1, 0, 5, 8), - MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), - MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7) -}; - -static mixer_ents cs42xb_mix_devices[32] = { - /* Digital master volume actually has seven bits, but we only use - six to avoid the discontinuity when the analog gain kicks in. */ - MIX_ENT(SOUND_MIXER_VOLUME, 46, 1, 0, 6, 47, 1, 0, 6, 7), - MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), - MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_MIC, 34, 1, 0, 5, 35, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), - /* For the IMIX entry, it was not possible to use the MIX_ENT macro - because the mute bit is in different positions for the two - channels and requires reverse polarity. */ - [SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8}, - {42, 1, 0, 6, 42, 1, 7, 0, 0, 8}}, - MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), - MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE3, 38, 1, 0, 6, 39, 1, 0, 6, 7) -}; - -/* OPTi 82C930 has somewhat different port addresses. - * Note: VOLUME == SPEAKER, SYNTH == LINE2, LINE == LINE3, CD == LINE1 - * VOLUME, SYNTH, LINE, CD are not enabled above. - * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc. - */ -static mixer_ents c930_mix_devices[32] = { - MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5, 7), - MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_SPEAKER, 22, 1, 1, 5, 23, 1, 1, 5, 7), - MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_MIC, 20, 1, 1, 4, 21, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), - MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 1, 4, 3, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 1, 4, 5, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 1, 4, 19, 1, 1, 4, 7) -}; - -static mixer_ents spro_mix_devices[32] = { - MIX_ENT (SOUND_MIXER_VOLUME, 19, 0, 4, 4, 19, 0, 0, 4, 8), - MIX_ENT (SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT (SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT2(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 23, 0, 3, 0, 0, 8, - 5, 1, 1, 4, 23, 0, 3, 0, 0, 8), - MIX_ENT (SOUND_MIXER_PCM, 6, 1, 1, 4, 7, 1, 1, 4, 8), - MIX_ENT (SOUND_MIXER_SPEAKER, 18, 0, 3, 2, 0, 0, 0, 0, 8), - MIX_ENT2(SOUND_MIXER_LINE, 20, 0, 4, 4, 17, 1, 4, 16, 0, 2, - 20, 0, 0, 4, 17, 1, 3, 16, 0, 1), - MIX_ENT2(SOUND_MIXER_MIC, 18, 0, 0, 3, 17, 1, 0, 16, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - MIX_ENT2(SOUND_MIXER_CD, 21, 0, 4, 4, 17, 1, 2, 16, 0, 4, - 21, 0, 0, 4, 17, 1, 1, 16, 0, 3), - MIX_ENT (SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT (SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT (SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT (SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT (SOUND_MIXER_OGAIN, 17, 1, 6, 1, 0, 0, 0, 0, 8), - /* This is external wavetable */ - MIX_ENT2(SOUND_MIXER_LINE1, 22, 0, 4, 4, 23, 1, 1, 23, 0, 4, - 22, 0, 0, 4, 23, 1, 0, 23, 0, 5), -}; - -static int default_mixer_levels[32] = -{ - 0x3232, /* Master Volume */ - 0x3232, /* Bass */ - 0x3232, /* Treble */ - 0x4b4b, /* FM */ - 0x3232, /* PCM */ - 0x1515, /* PC Speaker */ - 0x2020, /* Ext Line */ - 0x1010, /* Mic */ - 0x4b4b, /* CD */ - 0x0000, /* Recording monitor */ - 0x4b4b, /* Second PCM */ - 0x4b4b, /* Recording level */ - 0x4b4b, /* Input gain */ - 0x4b4b, /* Output gain */ - 0x2020, /* Line1 */ - 0x2020, /* Line2 */ - 0x1515 /* Line3 (usually line in)*/ -}; - -#define LEFT_CHN 0 -#define RIGHT_CHN 1 - -/* - * Channel enable bits for ioctl(SOUND_MIXER_PRIVATE1) - */ - -#ifndef AUDIO_SPEAKER -#define AUDIO_SPEAKER 0x01 /* Enable mono output */ -#define AUDIO_HEADPHONE 0x02 /* Sparc only */ -#define AUDIO_LINE_OUT 0x04 /* Sparc only */ -#endif diff -Nru a/drivers/sound/adlib_card.c b/drivers/sound/adlib_card.c --- a/drivers/sound/adlib_card.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,73 +0,0 @@ -/* - * sound/adlib_card.c - * - * Detection routine for the AdLib card. - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - -#include -#include - -#include "sound_config.h" - -#include "opl3.h" - -static void __init attach_adlib_card(struct address_info *hw_config) -{ - hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp, THIS_MODULE); -} - -static int __init probe_adlib(struct address_info *hw_config) -{ - return opl3_detect(hw_config->io_base, hw_config->osp); -} - -static struct address_info cfg; - -static int __initdata io = -1; - -MODULE_PARM(io, "i"); - -static int __init init_adlib(void) -{ - cfg.io_base = io; - - if (cfg.io_base == -1) { - printk(KERN_ERR "adlib: must specify I/O address.\n"); - return -EINVAL; - } - if (probe_adlib(&cfg) == 0) - return -ENODEV; - attach_adlib_card(&cfg); - - return 0; -} - -static void __exit cleanup_adlib(void) -{ - sound_unload_synthdev(cfg.slots[0]); - -} - -module_init(init_adlib); -module_exit(cleanup_adlib); - -#ifndef MODULE -static int __init setup_adlib(char *str) -{ - /* io */ - int ints[2]; - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - - return 1; -} -__setup("adlib=", setup_adlib); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/aedsp16.c b/drivers/sound/aedsp16.c --- a/drivers/sound/aedsp16.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1381 +0,0 @@ -/* - drivers/sound/lowlevel/aedsp16.c - - Audio Excel DSP 16 software configuration routines - Copyright (C) 1995,1996,1997,1998 Riccardo Facchetti (fizban@tin.it) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - */ -/* - * Include the main OSS Lite header file. It include all the os, OSS Lite, etc - * headers needed by this source. - */ -#include -#include -#include -#include -#include "sound_config.h" - -/* - * Sanity checks - */ - -#if defined(CONFIG_SOUND_AEDSP16_SBPRO) && defined(CONFIG_SOUND_AEDSP16_MSS) -#error You have to enable only one of the MSS and SBPRO emulations. -#endif - -/* - - READ THIS - - This module started to configure the Audio Excel DSP 16 Sound Card. - Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards. - - NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this - audio card and want to see the kernel support for it, please contact me. - - Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401 - compatible card. - It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq), - so before this module, the only way to configure the DSP under linux was - boot the MS-DOS loading the sound.sys device driver (this driver soft- - configure the sound board hardware by massaging someone of its registers), - and then ctrl-alt-del to boot linux with the DSP configured by the DOS - driver. - - This module works configuring your Audio Excel DSP 16's irq, dma and - mpu-401-irq. The OSS Lite routines rely on the fact that if the - hardware is there, they can detect it. The problem with AEDSP16 is - that no hardware can be found by the probe routines if the sound card - is not configured properly. Sometimes the kernel probe routines can find - an SBPRO even when the card is not configured (this is the standard setup - of the card), but the SBPRO emulation don't work well if the card is not - properly initialized. For this reason - - aedsp16_init_board() - - routine is called before the OSS Lite probe routines try to detect the - hardware. - - NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS) - - NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards - have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They - have to be configured by software. - - NOTE: The driver is merged with the new OSS Lite sound driver. It works - as a lowlevel driver. - - The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS; - the OSS Lite sound driver can be configured for SBPRO and MSS cards - at the same time, but the aedsp16 can't be two cards!! - When we configure it, we have to choose the SBPRO or the MSS emulation - for AEDSP16. We also can install a *REAL* card of the other type (see [1]). - - NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO - please let me know if it works. - - The MPU-401 support can be compiled in together with one of the other - two operating modes. - - NOTE: This is something like plug-and-play: we have only to plug - the AEDSP16 board in the socket, and then configure and compile - a kernel that uses the AEDSP16 software configuration capability. - No jumper setting is needed! - - For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3 - you have just to make config the OSS Lite package, configuring - the AEDSP16 sound card, then activating the SBPro emulation mode - and at last configuring IRQ and DMA. - Compile the kernel and run it. - - NOTE: This means for SC-6000 cards that you can choose irq and dma, - but not the I/O addresses. To change I/O addresses you have to set - them with jumpers. For SC-6600 cards you have no jumpers so you have - to set up your full card configuration in the make config. - - You can change the irq/dma/mirq settings WITHOUT THE NEED to open - your computer and massage the jumpers (there are no irq/dma/mirq - jumpers to be configured anyway, only I/O BASE values have to be - configured with jumpers) - - For some ununderstandable reason, the card default of irq 7, dma 1, - don't work for me. Seems to be an IRQ or DMA conflict. Under heavy - HDD work, the kernel start to erupt out a lot of messages like: - - 'Sound: DMA timed out - IRQ/DRQ config error?' - - For what I can say, I have NOT any conflict at irq 7 (under linux I'm - using the lp polling driver), and dma line 1 is unused as stated by - /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so - I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows! - Anyway a setting of irq 10, dma 3 works really fine. - - NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know - the emulation mode, all the installed hardware and the hardware - configuration (irq and dma settings of all the hardware). - - This init module should work with SBPRO+MSS, when one of the two is - the AEDSP16 emulation and the other the real card. (see [1]) - For example: - - AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other - AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other - - MPU401 should work. (see [2]) - - [1] - --- - Date: Mon, 29 Jul 1997 08:35:40 +0100 - From: Mr S J Greenaway - - [...] - Just to let you know got my Audio Excel (emulating a MSS) working - with my original SB16, thanks for the driver! - [...] - --- - - [2] Not tested by me for lack of hardware. - - TODO, WISHES AND TECH - - - About I/O ports allocation - - - Request the 2x0h region (port base) in any case if we are using this card. - - NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16 - port base region (see code) does not mean necessarily that we are emulating - sbpro. Even if this region is the sbpro I/O ports region, we use this - region to access the control registers of the card, and if emulating - sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro - registers are not used, in no way, to emulate an sbpro: they are - used only for configuration purposes. - - Started Fri Mar 17 16:13:18 MET 1995 - - v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c) - - Initial code. - v0.2 (ALPHA) - - Cleanups. - - Integrated with Linux voxware v 2.90-2 kernel sound driver. - - SoundBlaster Pro mode configuration. - - Microsoft Sound System mode configuration. - - MPU-401 mode configuration. - v0.3 (ALPHA) - - Cleanups. - - Rearranged the code to let aedsp16_init_board be more general. - - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h - inclusion too. We rely on os.h - - Used the to get a variable - len string (we are not sure about the len of Copyright string). - This works with any SB and compatible. - - Added the code to request_region at device init (should go in - the main body of voxware). - v0.4 (BETA) - - Better configure.c patch for aedsp16 configuration (better - logic of inclusion of AEDSP16 support) - - Modified the conditional compilation to better support more than - one sound card of the emulated type (read the NOTES above) - - Moved the sb init routine from the attach to the very first - probe in sb_card.c - - Rearrangements and cleanups - - Wiped out some unnecessary code and variables: this is kernel - code so it is better save some TEXT and DATA - - Fixed the request_region code. We must allocate the aedsp16 (sbpro) - I/O ports in any case because they are used to access the DSP - configuration registers and we can not allow anyone to get them. - v0.5 - - cleanups on comments - - prep for diffs against v3.0-proto-950402 - v0.6 - - removed the request_region()s when compiling the MODULE sound.o - because we are not allowed (by the actual voxware structure) to - release_region() - v0.7 (pre ALPHA, not distributed) - - started porting this module to kernel 1.3.84. Dummy probe/attach - routines. - v0.8 (ALPHA) - - attached all the init routines. - v0.9 (BETA) - - Integrated with linux-pre2.0.7 - - Integrated with configuration scripts. - - Cleaned up and beautyfied the code. - v0.9.9 (BETA) - - Thanks to Piercarlo Grandi: corrected the conditonal compilation code. - Now only the code configured is compiled in, with some memory saving. - v0.9.10 - - Integration into the sound/lowlevel/ section of the sound driver. - - Re-organized the code. - v0.9.11 (not distributed) - - Rewritten the init interface-routines to initialize the AEDSP16 in - one shot. - - More cosmetics. - - SC-6600 support. - - More soft/hard configuration. - v0.9.12 - - Refined the v0.9.11 code with conditional compilation to distinguish - between SC-6000 and SC-6600 code. - v1.0.0 - - Prep for merging with OSS Lite and Linux kernel 2.1.13 - - Corrected a bug in request/check/release region calls (thanks to the - new kernel exception handling). - v1.1 - - Revamped for integration with new modularized sound drivers: to enhance - the flexibility of modular version, I have removed all the conditional - compilation for SBPRO, MPU and MSS code. Now it is all managed with - the ae_config structure. - v1.2 - - Module informations added. - - Removed aedsp16_delay_10msec(), now using mdelay(10) - - All data and funcs moved to .*.init section. - v1.3 - Arnaldo Carvalho de Melo - 2000/09/27 - - got rid of check_region - - Known Problems: - - Audio Excel DSP 16 III don't work with this driver. - - Credits: - Many thanks to Gerald Britton . He helped me a - lot in testing the 0.9.11 and 0.9.12 versions of this driver. - - */ - - -#define VERSION "1.3" /* Version of Audio Excel DSP 16 driver */ - -#undef AEDSP16_DEBUG /* Define this to 1 to enable debug code */ -#undef AEDSP16_DEBUG_MORE /* Define this to 1 to enable more debug */ -#undef AEDSP16_INFO /* Define this to 1 to enable info code */ - -#if defined(AEDSP16_DEBUG) -# define DBG(x) printk x -# if defined(AEDSP16_DEBUG_MORE) -# define DBG1(x) printk x -# else -# define DBG1(x) -# endif -#else -# define DBG(x) -# define DBG1(x) -#endif - -/* - * Misc definitions - */ -#define TRUE 1 -#define FALSE 0 - -/* - * Region Size for request/check/release region. - */ -#define IOBASE_REGION_SIZE 0x10 - -/* - * Hardware related defaults - */ -#define DEF_AEDSP16_IOB 0x220 /* 0x220(default) 0x240 */ -#define DEF_AEDSP16_IRQ 7 /* 5 7(default) 9 10 11 */ -#define DEF_AEDSP16_MRQ 0 /* 5 7 9 10 0(default), 0 means disable */ -#define DEF_AEDSP16_DMA 1 /* 0 1(default) 3 */ - -/* - * Commands of AEDSP16's DSP (SBPRO+special). - * Some of them are COMMAND_xx, in the future they may change. - */ -#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */ -#define COMMAND_52 0x52 /* */ -#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */ -#define COMMAND_5C 0x5c /* */ -#define COMMAND_60 0x60 /* */ -#define COMMAND_66 0x66 /* */ -#define COMMAND_6C 0x6c /* */ -#define COMMAND_6E 0x6e /* */ -#define COMMAND_88 0x88 /* */ -#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */ -#define COMMAND_C5 0xc5 /* */ -#define GET_DSP_VERSION 0xe1 /* Get DSP Version */ -#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */ - -/* - * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port - * to have the actual I/O port. - * Register permissions are: - * (wo) == Write Only - * (ro) == Read Only - * (w-) == Write - * (r-) == Read - */ -#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */ -#define DSP_READ 0x0a /* offset of DSP READ (ro) */ -#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */ -#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */ -#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */ -#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */ - - -#define RETRY 10 /* Various retry values on I/O opera- */ -#define STATUSRETRY 1000 /* tions. Sometimes we have to */ -#define HARDRETRY 500000 /* wait for previous cmd to complete */ - -/* - * Size of character arrays that store name and version of sound card - */ -#define CARDNAMELEN 15 /* Size of the card's name in chars */ -#define CARDVERLEN 2 /* Size of the card's version in chars */ - -#if defined(CONFIG_SC6600) -/* - * Bitmapped flags of hard configuration - */ -/* - * Decode macros (xl == low byte, xh = high byte) - */ -#define IOBASE(xl) ((xl & 0x01)?0x240:0x220) -#define JOY(xl) (xl & 0x02) -#define MPUADDR(xl) ( \ - (xl & 0x0C)?0x330: \ - (xl & 0x08)?0x320: \ - (xl & 0x04)?0x310: \ - 0x300) -#define WSSADDR(xl) ((xl & 0x10)?0xE80:0x530) -#define CDROM(xh) (xh & 0x20) -#define CDROMADDR(xh) (((xh & 0x1F) << 4) + 0x200) -/* - * Encode macros - */ -#define BLDIOBASE(xl, val) { \ - xl &= ~0x01; \ - if (val == 0x240) \ - xl |= 0x01; \ - } -#define BLDJOY(xl, val) { \ - xl &= ~0x02; \ - if (val == 1) \ - xl |= 0x02; \ - } -#define BLDMPUADDR(xl, val) { \ - xl &= ~0x0C; \ - switch (val) { \ - case 0x330: \ - xl |= 0x0C; \ - break; \ - case 0x320: \ - xl |= 0x08; \ - break; \ - case 0x310: \ - xl |= 0x04; \ - break; \ - case 0x300: \ - xl |= 0x00; \ - break; \ - default: \ - xl |= 0x00; \ - break; \ - } \ - } -#define BLDWSSADDR(xl, val) { \ - xl &= ~0x10; \ - if (val == 0xE80) \ - xl |= 0x10; \ - } -#define BLDCDROM(xh, val) { \ - xh &= ~0x20; \ - if (val == 1) \ - xh |= 0x20; \ - } -#define BLDCDROMADDR(xh, val) { \ - int tmp = val; \ - tmp -= 0x200; \ - tmp >>= 4; \ - tmp &= 0x1F; \ - xh |= tmp; \ - xh &= 0x7F; \ - xh |= 0x40; \ - } -#endif /* CONFIG_SC6600 */ - -/* - * Bit mapped flags for calling aedsp16_init_board(), and saving the current - * emulation mode. - */ -#define INIT_NONE (0 ) -#define INIT_SBPRO (1<<0) -#define INIT_MSS (1<<1) -#define INIT_MPU401 (1<<2) - -static int soft_cfg __initdata = 0; /* bitmapped config */ -static int soft_cfg_mss __initdata = 0; /* bitmapped mss config */ -static int ver[CARDVERLEN] __initdata = {0, 0}; /* DSP Ver: - hi->ver[0] lo->ver[1] */ - -#if defined(CONFIG_SC6600) -static int hard_cfg[2] /* lo<-hard_cfg[0] hi<-hard_cfg[1] */ - __initdata = { 0, 0}; -#endif /* CONFIG_SC6600 */ - -#if defined(CONFIG_SC6600) -/* Decoded hard configuration */ -struct d_hcfg { - int iobase; - int joystick; - int mpubase; - int wssbase; - int cdrom; - int cdrombase; -}; - -struct d_hcfg decoded_hcfg __initdata = {0, }; - -#endif /* CONFIG_SC6600 */ - -/* orVals contain the values to be or'ed */ -struct orVals { - int val; /* irq|mirq|dma */ - int or; /* soft_cfg |= TheStruct.or */ -}; - -/* aedsp16_info contain the audio card configuration */ -struct aedsp16_info { - int base_io; /* base I/O address for accessing card */ - int irq; /* irq value for DSP I/O */ - int mpu_irq; /* irq for mpu401 interface I/O */ - int dma; /* dma value for DSP I/O */ - int mss_base; /* base I/O for Microsoft Sound System */ - int mpu_base; /* base I/O for MPU-401 emulation */ - int init; /* Initialization status of the card */ -}; - -/* - * Magic values that the DSP will eat when configuring irq/mirq/dma - */ -/* DSP IRQ conversion array */ -static struct orVals orIRQ[] __initdata = { - {0x05, 0x28}, - {0x07, 0x08}, - {0x09, 0x10}, - {0x0a, 0x18}, - {0x0b, 0x20}, - {0x00, 0x00} -}; - -/* MPU-401 IRQ conversion array */ -static struct orVals orMIRQ[] __initdata = { - {0x05, 0x04}, - {0x07, 0x44}, - {0x09, 0x84}, - {0x0a, 0xc4}, - {0x00, 0x00} -}; - -/* DMA Channels conversion array */ -static struct orVals orDMA[] __initdata = { - {0x00, 0x01}, - {0x01, 0x02}, - {0x03, 0x03}, - {0x00, 0x00} -}; - -static struct aedsp16_info ae_config __initdata = { - DEF_AEDSP16_IOB, - DEF_AEDSP16_IRQ, - DEF_AEDSP16_MRQ, - DEF_AEDSP16_DMA, - -1, - -1, - INIT_NONE -}; - -/* - * Buffers to store audio card informations - */ -static char DSPCopyright[CARDNAMELEN + 1] __initdata = {0, }; -static char DSPVersion[CARDVERLEN + 1] __initdata = {0, }; - -static int __init aedsp16_wait_data(int port) -{ - int loop = STATUSRETRY; - unsigned char ret = 0; - - DBG1(("aedsp16_wait_data (0x%x): ", port)); - - do { - ret = inb(port + DSP_DATAVAIL); - /* - * Wait for data available (bit 7 of ret == 1) - */ - } while (!(ret & 0x80) && loop--); - - if (ret & 0x80) { - DBG1(("success.\n")); - return TRUE; - } - - DBG1(("failure.\n")); - return FALSE; -} - -static int __init aedsp16_read(int port) -{ - int inbyte; - - DBG((" Read DSP Byte (0x%x): ", port)); - - if (aedsp16_wait_data(port) == FALSE) { - DBG(("failure.\n")); - return -1; - } - - inbyte = inb(port + DSP_READ); - - DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte)); - - return inbyte; -} - -static int __init aedsp16_test_dsp(int port) -{ - return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE); -} - -static int __init aedsp16_dsp_reset(int port) -{ - /* - * Reset DSP - */ - - DBG(("Reset DSP:\n")); - - outb(1, (port + DSP_RESET)); - udelay(10); - outb(0, (port + DSP_RESET)); - udelay(10); - udelay(10); - if (aedsp16_test_dsp(port) == TRUE) { - DBG(("success.\n")); - return TRUE; - } else - DBG(("failure.\n")); - return FALSE; -} - -static int __init aedsp16_write(int port, int cmd) -{ - unsigned char ret; - int loop = HARDRETRY; - - DBG((" Write DSP Byte (0x%x) [0x%x]: ", port, cmd)); - - do { - ret = inb(port + DSP_STATUS); - /* - * DSP ready to receive data if bit 7 of ret == 0 - */ - if (!(ret & 0x80)) { - outb(cmd, port + DSP_COMMAND); - DBG(("success.\n")); - return 0; - } - } while (loop--); - - DBG(("timeout.\n")); - printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd); - - return -1; -} - -#if defined(CONFIG_SC6600) - -#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) -void __init aedsp16_pinfo(void) { - DBG(("\n Base address: %x\n", decoded_hcfg.iobase)); - DBG((" Joystick : %s present\n", decoded_hcfg.joystick?"":" not")); - DBG((" WSS addr : %x\n", decoded_hcfg.wssbase)); - DBG((" MPU-401 addr: %x\n", decoded_hcfg.mpubase)); - DBG((" CDROM : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not")); - DBG((" CDROMADDR : %x\n\n", decoded_hcfg.cdrombase)); -} -#endif - -void __init aedsp16_hard_decode(void) { - - DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); - -/* - * Decode Cfg Bytes. - */ - decoded_hcfg.iobase = IOBASE(hard_cfg[0]); - decoded_hcfg.joystick = JOY(hard_cfg[0]); - decoded_hcfg.wssbase = WSSADDR(hard_cfg[0]); - decoded_hcfg.mpubase = MPUADDR(hard_cfg[0]); - decoded_hcfg.cdrom = CDROM(hard_cfg[1]); - decoded_hcfg.cdrombase = CDROMADDR(hard_cfg[1]); - -#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) - printk(" Original sound card configuration:\n"); - aedsp16_pinfo(); -#endif - -/* - * Now set up the real kernel configuration. - */ - decoded_hcfg.iobase = ae_config.base_io; - decoded_hcfg.wssbase = ae_config.mss_base; - decoded_hcfg.mpubase = ae_config.mpu_base; - -#if defined(CONFIG_SC6600_JOY) - decoded_hcfg.joystick = CONFIG_SC6600_JOY; /* Enable */ -#endif -#if defined(CONFIG_SC6600_CDROM) - decoded_hcfg.cdrom = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */ -#endif -#if defined(CONFIG_SC6600_CDROMBASE) - decoded_hcfg.cdrombase = CONFIG_SC6600_CDROMBASE; /* 0 Disable */ -#endif - -#if defined(AEDSP16_DEBUG) - DBG((" New Values:\n")); - aedsp16_pinfo(); -#endif - - DBG(("success.\n")); -} - -void __init aedsp16_hard_encode(void) { - - DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); - - hard_cfg[0] = 0; - hard_cfg[1] = 0; - - hard_cfg[0] |= 0x20; - - BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase); - BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase); - BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase); - BLDJOY(hard_cfg[0], decoded_hcfg.joystick); - BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom); - BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase); - -#if defined(AEDSP16_DEBUG) - aedsp16_pinfo(); -#endif - - DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); - DBG(("success.\n")); - -} - -static int __init aedsp16_hard_write(int port) { - - DBG(("aedsp16_hard_write:\n")); - - if (aedsp16_write(port, COMMAND_6C)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C); - DBG(("failure.\n")); - return FALSE; - } - if (aedsp16_write(port, COMMAND_5C)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); - DBG(("failure.\n")); - return FALSE; - } - if (aedsp16_write(port, hard_cfg[0])) { - printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]); - DBG(("failure.\n")); - return FALSE; - } - if (aedsp16_write(port, hard_cfg[1])) { - printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]); - DBG(("failure.\n")); - return FALSE; - } - if (aedsp16_write(port, COMMAND_C5)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5); - DBG(("failure.\n")); - return FALSE; - } - - DBG(("success.\n")); - - return TRUE; -} - -static int __init aedsp16_hard_read(int port) { - - DBG(("aedsp16_hard_read:\n")); - - if (aedsp16_write(port, READ_HARD_CFG)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG); - DBG(("failure.\n")); - return FALSE; - } - - if ((hard_cfg[0] = aedsp16_read(port)) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", - READ_HARD_CFG); - DBG(("failure.\n")); - return FALSE; - } - if ((hard_cfg[1] = aedsp16_read(port)) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", - READ_HARD_CFG); - DBG(("failure.\n")); - return FALSE; - } - if (aedsp16_read(port) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", - READ_HARD_CFG); - DBG(("failure.\n")); - return FALSE; - } - - DBG(("success.\n")); - - return TRUE; -} - -static int __init aedsp16_ext_cfg_write(int port) { - - int extcfg, val; - - if (aedsp16_write(port, COMMAND_66)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66); - return FALSE; - } - - extcfg = 7; - if (decoded_hcfg.cdrom != 2) - extcfg = 0x0F; - if ((decoded_hcfg.cdrom == 4) || - (decoded_hcfg.cdrom == 3)) - extcfg &= ~2; - if (decoded_hcfg.cdrombase == 0) - extcfg &= ~2; - if (decoded_hcfg.mpubase == 0) - extcfg &= ~1; - - if (aedsp16_write(port, extcfg)) { - printk("[AEDSP16] Write extcfg: failed!\n"); - return FALSE; - } - if (aedsp16_write(port, 0)) { - printk("[AEDSP16] Write extcfg: failed!\n"); - return FALSE; - } - if (decoded_hcfg.cdrom == 3) { - if (aedsp16_write(port, COMMAND_52)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52); - return FALSE; - } - if ((val = aedsp16_read(port)) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n" - , COMMAND_52); - return FALSE; - } - val &= 0x7F; - if (aedsp16_write(port, COMMAND_60)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60); - return FALSE; - } - if (aedsp16_write(port, val)) { - printk("[AEDSP16] Write val: failed!\n"); - return FALSE; - } - } - - return TRUE; -} - -#endif /* CONFIG_SC6600 */ - -static int __init aedsp16_cfg_write(int port) { - if (aedsp16_write(port, WRITE_MDIRQ_CFG)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); - return FALSE; - } - if (aedsp16_write(port, soft_cfg)) { - printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n"); - return FALSE; - } - return TRUE; -} - -static int __init aedsp16_init_mss(int port) -{ - DBG(("aedsp16_init_mss:\n")); - - mdelay(10); - - if (aedsp16_write(port, DSP_INIT_MSS)) { - printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n", - DSP_INIT_MSS); - DBG(("failure.\n")); - return FALSE; - } - - mdelay(10); - - if (aedsp16_cfg_write(port) == FALSE) - return FALSE; - - outb(soft_cfg_mss, ae_config.mss_base); - - DBG(("success.\n")); - - return TRUE; -} - -static int __init aedsp16_setup_board(int port) { - int loop = RETRY; - -#if defined(CONFIG_SC6600) - int val = 0; - - if (aedsp16_hard_read(port) == FALSE) { - printk("[AEDSP16] aedsp16_hard_read: failed!\n"); - return FALSE; - } - - if (aedsp16_write(port, COMMAND_52)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52); - return FALSE; - } - - if ((val = aedsp16_read(port)) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", - COMMAND_52); - return FALSE; - } -#endif - - do { - if (aedsp16_write(port, COMMAND_88)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88); - return FALSE; - } - mdelay(10); - } while ((aedsp16_wait_data(port) == FALSE) && loop--); - - if (aedsp16_read(port) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", - COMMAND_88); - return FALSE; - } - -#if !defined(CONFIG_SC6600) - if (aedsp16_write(port, COMMAND_5C)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); - return FALSE; - } -#endif - - if (aedsp16_cfg_write(port) == FALSE) - return FALSE; - -#if defined(CONFIG_SC6600) - if (aedsp16_write(port, COMMAND_60)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60); - return FALSE; - } - if (aedsp16_write(port, val)) { - printk("[AEDSP16] DATA 0x%x: failed!\n", val); - return FALSE; - } - if (aedsp16_write(port, COMMAND_6E)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E); - return FALSE; - } - if (aedsp16_write(port, ver[0])) { - printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]); - return FALSE; - } - if (aedsp16_write(port, ver[1])) { - printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]); - return FALSE; - } - - if (aedsp16_hard_write(port) == FALSE) { - printk("[AEDSP16] aedsp16_hard_write: failed!\n"); - return FALSE; - } - - if (aedsp16_write(port, COMMAND_5C)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); - return FALSE; - } - -#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET) - if (aedsp16_cfg_write(port) == FALSE) - return FALSE; -#endif - -#endif - - return TRUE; -} - -static int __init aedsp16_stdcfg(int port) { - if (aedsp16_write(port, WRITE_MDIRQ_CFG)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); - return FALSE; - } - /* - * 0x0A == (IRQ 7, DMA 1, MIRQ 0) - */ - if (aedsp16_write(port, 0x0A)) { - printk("[AEDSP16] aedsp16_stdcfg: failed!\n"); - return FALSE; - } - return TRUE; -} - -static int __init aedsp16_dsp_version(int port) -{ - int len = 0; - int ret; - - DBG(("Get DSP Version:\n")); - - if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION); - DBG(("failed.\n")); - return FALSE; - } - - do { - if ((ret = aedsp16_read(port)) == -1) { - DBG(("failed.\n")); - return FALSE; - } - /* - * We already know how many int are stored (2), so we know when the - * string is finished. - */ - ver[len++] = ret; - } while (len < CARDVERLEN); - sprintf(DSPVersion, "%d.%d", ver[0], ver[1]); - - DBG(("success.\n")); - - return TRUE; -} - -static int __init aedsp16_dsp_copyright(int port) -{ - int len = 0; - int ret; - - DBG(("Get DSP Copyright:\n")); - - if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT); - DBG(("failed.\n")); - return FALSE; - } - - do { - if ((ret = aedsp16_read(port)) == -1) { - /* - * If no more data available, return to the caller, no error if len>0. - * We have no other way to know when the string is finished. - */ - if (len) - break; - else { - DBG(("failed.\n")); - return FALSE; - } - } - - DSPCopyright[len++] = ret; - - } while (len < CARDNAMELEN); - - DBG(("success.\n")); - - return TRUE; -} - -static void __init aedsp16_init_tables(void) -{ - int i = 0; - - memset(DSPCopyright, 0, CARDNAMELEN + 1); - memset(DSPVersion, 0, CARDVERLEN + 1); - - for (i = 0; orIRQ[i].or; i++) - if (orIRQ[i].val == ae_config.irq) { - soft_cfg |= orIRQ[i].or; - soft_cfg_mss |= orIRQ[i].or; - } - - for (i = 0; orMIRQ[i].or; i++) - if (orMIRQ[i].or == ae_config.mpu_irq) - soft_cfg |= orMIRQ[i].or; - - for (i = 0; orDMA[i].or; i++) - if (orDMA[i].val == ae_config.dma) { - soft_cfg |= orDMA[i].or; - soft_cfg_mss |= orDMA[i].or; - } -} - -static int __init aedsp16_init_board(void) -{ - aedsp16_init_tables(); - - if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_dsp_reset: failed!\n"); - return FALSE; - } - if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n"); - return FALSE; - } - - /* - * My AEDSP16 card return SC-6000 in DSPCopyright, so - * if we have something different, we have to be warned. - */ - if (strcmp("SC-6000", DSPCopyright)) - printk("[AEDSP16] Warning: non SC-6000 audio card!\n"); - - if (aedsp16_dsp_version(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_dsp_version: failed!\n"); - return FALSE; - } - - if (aedsp16_stdcfg(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_stdcfg: failed!\n"); - return FALSE; - } - -#if defined(CONFIG_SC6600) - if (aedsp16_hard_read(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_hard_read: failed!\n"); - return FALSE; - } - - aedsp16_hard_decode(); - - aedsp16_hard_encode(); - - if (aedsp16_hard_write(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_hard_write: failed!\n"); - return FALSE; - } - - if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n"); - return FALSE; - } -#endif /* CONFIG_SC6600 */ - - if (aedsp16_setup_board(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_setup_board: failed!\n"); - return FALSE; - } - - if (ae_config.mss_base != -1) { - if (ae_config.init & INIT_MSS) { - if (aedsp16_init_mss(ae_config.base_io) == FALSE) { - printk("[AEDSP16] Can not initialize" - "Microsoft Sound System mode.\n"); - return FALSE; - } - } - } - -#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) - - printk("Audio Excel DSP 16 init v%s (%s %s) [", - VERSION, DSPCopyright, - DSPVersion); - - if (ae_config.mpu_base != -1) { - if (ae_config.init & INIT_MPU401) { - printk("MPU401"); - if ((ae_config.init & INIT_MSS) || - (ae_config.init & INIT_SBPRO)) - printk(" "); - } - } - - if (ae_config.mss_base == -1) { - if (ae_config.init & INIT_SBPRO) { - printk("SBPro"); - if (ae_config.init & INIT_MSS) - printk(" "); - } - } - - if (ae_config.mss_base != -1) - if (ae_config.init & INIT_MSS) - printk("MSS"); - - printk("]\n"); -#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */ - - mdelay(10); - - return TRUE; -} - -static int __init init_aedsp16_sb(void) -{ - DBG(("init_aedsp16_sb: ")); - -/* - * If the card is already init'ed MSS, we can not init it to SBPRO too - * because the board can not emulate simultaneously MSS and SBPRO. - */ - if (ae_config.init & INIT_MSS) - return FALSE; - if (ae_config.init & INIT_SBPRO) - return FALSE; - - ae_config.init |= INIT_SBPRO; - - DBG(("done.\n")); - - return TRUE; -} - -static void __init uninit_aedsp16_sb(void) -{ - DBG(("uninit_aedsp16_sb: ")); - - ae_config.init &= ~INIT_SBPRO; - - DBG(("done.\n")); -} - -static int __init init_aedsp16_mss(void) -{ - DBG(("init_aedsp16_mss: ")); - -/* - * If the card is already init'ed SBPRO, we can not init it to MSS too - * because the board can not emulate simultaneously MSS and SBPRO. - */ - if (ae_config.init & INIT_SBPRO) - return FALSE; - if (ae_config.init & INIT_MSS) - return FALSE; -/* - * We must allocate the CONFIG_AEDSP16_BASE region too because these are the - * I/O ports to access card's control registers. - */ - if (!(ae_config.init & INIT_MPU401)) { - if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE, - "aedsp16 (base)")) { - printk( - "AEDSP16 BASE I/O port region is already in use.\n"); - return FALSE; - } - } - - ae_config.init |= INIT_MSS; - - DBG(("done.\n")); - - return TRUE; -} - -static void __init uninit_aedsp16_mss(void) -{ - DBG(("uninit_aedsp16_mss: ")); - - if ((!(ae_config.init & INIT_MPU401)) && - (ae_config.init & INIT_MSS)) { - release_region(ae_config.base_io, IOBASE_REGION_SIZE); - DBG(("AEDSP16 base region released.\n")); - } - - ae_config.init &= ~INIT_MSS; - DBG(("done.\n")); -} - -static int __init init_aedsp16_mpu(void) -{ - DBG(("init_aedsp16_mpu: ")); - - if (ae_config.init & INIT_MPU401) - return FALSE; - -/* - * We must request the CONFIG_AEDSP16_BASE region too because these are the I/O - * ports to access card's control registers. - */ - if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) { - if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE, - "aedsp16 (base)")) { - printk( - "AEDSP16 BASE I/O port region is already in use.\n"); - return FALSE; - } - } - - ae_config.init |= INIT_MPU401; - - DBG(("done.\n")); - - return TRUE; -} - -static void __init uninit_aedsp16_mpu(void) -{ - DBG(("uninit_aedsp16_mpu: ")); - - if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) && - (ae_config.init & INIT_MPU401)) { - release_region(ae_config.base_io, IOBASE_REGION_SIZE); - DBG(("AEDSP16 base region released.\n")); - } - - ae_config.init &= ~INIT_MPU401; - - DBG(("done.\n")); -} - -int __init init_aedsp16(void) -{ - int initialized = FALSE; - - DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n", - ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq)); - - if (ae_config.mss_base == -1) { - if (init_aedsp16_sb() == FALSE) { - uninit_aedsp16_sb(); - } else { - initialized = TRUE; - } - } - - if (ae_config.mpu_base != -1) { - if (init_aedsp16_mpu() == FALSE) { - uninit_aedsp16_mpu(); - } else { - initialized = TRUE; - } - } - -/* - * In the sequence of init routines, the MSS init MUST be the last! - * This because of the special register programming the MSS mode needs. - * A board reset would disable the MSS mode restoring the default SBPRO - * mode. - */ - if (ae_config.mss_base != -1) { - if (init_aedsp16_mss() == FALSE) { - uninit_aedsp16_mss(); - } else { - initialized = TRUE; - } - } - - if (initialized) - initialized = aedsp16_init_board(); - return initialized; -} - -void __init uninit_aedsp16(void) -{ - if (ae_config.mss_base != -1) - uninit_aedsp16_mss(); - else - uninit_aedsp16_sb(); - if (ae_config.mpu_base != -1) - uninit_aedsp16_mpu(); -} - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata mpu_irq = -1; -static int __initdata mss_base = -1; -static int __initdata mpu_base = -1; - -MODULE_PARM(io, "i"); -MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)"); -MODULE_PARM(irq, "i"); -MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)"); -MODULE_PARM(dma, "i"); -MODULE_PARM_DESC(dma, "dma line (0 1 3)"); -MODULE_PARM(mpu_irq, "i"); -MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)"); -MODULE_PARM(mss_base, "i"); -MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)"); -MODULE_PARM(mpu_base, "i"); -MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)"); -MODULE_AUTHOR("Riccardo Facchetti "); -MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION); -MODULE_LICENSE("GPL"); - -static int __init do_init_aedsp16(void) { - printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n"); - if (io == -1 || dma == -1 || irq == -1) { - printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n"); - return -EINVAL; - } - - ae_config.base_io = io; - ae_config.irq = irq; - ae_config.dma = dma; - - ae_config.mss_base = mss_base; - ae_config.mpu_base = mpu_base; - ae_config.mpu_irq = mpu_irq; - - if (init_aedsp16() == FALSE) { - printk(KERN_ERR "aedsp16: initialization failed\n"); - /* - * XXX - * What error should we return here ? - */ - return -EINVAL; - } - return 0; -} - -static void __exit cleanup_aedsp16(void) { - uninit_aedsp16(); -} - -module_init(do_init_aedsp16); -module_exit(cleanup_aedsp16); - -#ifndef MODULE -static int __init setup_aedsp16(char *str) -{ - /* io, irq, dma, mss_io, mpu_io, mpu_irq */ - int ints[7]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - mss_base = ints[4]; - mpu_base = ints[5]; - mpu_irq = ints[6]; - return 1; -} - -__setup("aedsp16=", setup_aedsp16); -#endif diff -Nru a/drivers/sound/audio.c b/drivers/sound/audio.c --- a/drivers/sound/audio.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,987 +0,0 @@ -/* - * sound/audio.c - * - * Device file manager for /dev/audio - */ - -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Thomas Sailer : moved several static variables into struct audio_operations - * (which is grossly misnamed btw.) because they have the same - * lifetime as the rest in there and dynamic allocation saves - * 12k or so - * Thomas Sailer : use more logical O_NONBLOCK semantics - * Daniel Rodriksson: reworked the use of the device specific copy_user - * still generic - * Horst von Brand: Add missing #include - * Chris Rankin : Update the module-usage counter for the coprocessor, - * and decrement the counters again if we cannot open - * the audio device. - */ - -#include -#include -#include - -#include "sound_config.h" -#include "ulaw.h" -#include "coproc.h" - -#define NEUTRAL8 0x80 -#define NEUTRAL16 0x00 - - -int dma_ioctl(int dev, unsigned int cmd, caddr_t arg); - -static int set_format(int dev, int fmt) -{ - if (fmt != AFMT_QUERY) - { - audio_devs[dev]->local_conversion = 0; - - if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ - { - if (fmt == AFMT_MU_LAW) - { - fmt = AFMT_U8; - audio_devs[dev]->local_conversion = CNV_MU_LAW; - } - else - fmt = AFMT_U8; /* This is always supported */ - } - audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt); - audio_devs[dev]->local_format = fmt; - } - else - return audio_devs[dev]->local_format; - - if (audio_devs[dev]->local_conversion) - return audio_devs[dev]->local_conversion; - else - return audio_devs[dev]->local_format; -} - -int audio_open(int dev, struct file *file) -{ - int ret; - int bits; - int dev_type = dev & 0x0f; - int mode = translate_mode(file); - const struct audio_driver *driver; - const struct coproc_operations *coprocessor; - - dev = dev >> 4; - - if (dev_type == SND_DEV_DSP16) - bits = 16; - else - bits = 8; - - if (dev < 0 || dev >= num_audiodevs) - return -ENXIO; - - driver = audio_devs[dev]->d; - if (driver->owner) - __MOD_INC_USE_COUNT(driver->owner); - - if ((ret = DMAbuf_open(dev, mode)) < 0) - goto error_1; - - if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) { - if (coprocessor->owner) - __MOD_INC_USE_COUNT(coprocessor->owner); - - if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) { - printk(KERN_WARNING "Sound: Can't access coprocessor device\n"); - goto error_2; - } - } - - audio_devs[dev]->local_conversion = 0; - - if (dev_type == SND_DEV_AUDIO) - set_format(dev, AFMT_MU_LAW); - else - set_format(dev, bits); - - audio_devs[dev]->audio_mode = AM_NONE; - - return 0; - - /* - * Clean-up stack: this is what needs (un)doing if - * we can't open the audio device ... - */ - error_2: - if (coprocessor->owner) - __MOD_DEC_USE_COUNT(coprocessor->owner); - DMAbuf_release(dev, mode); - - error_1: - if (driver->owner) - __MOD_DEC_USE_COUNT(driver->owner); - - return ret; -} - -static void sync_output(int dev) -{ - int p, i; - int l; - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - - if (dmap->fragment_size <= 0) - return; - dmap->flags |= DMA_POST; - - /* Align the write pointer with fragment boundaries */ - - if ((l = dmap->user_counter % dmap->fragment_size) > 0) - { - int len; - unsigned long offs = dmap->user_counter % dmap->bytes_in_use; - - len = dmap->fragment_size - l; - memset(dmap->raw_buf + offs, dmap->neutral_byte, len); - DMAbuf_move_wrpointer(dev, len); - } - - /* - * Clean all unused buffer fragments. - */ - - p = dmap->qtail; - dmap->flags |= DMA_POST; - - for (i = dmap->qlen + 1; i < dmap->nbufs; i++) - { - p = (p + 1) % dmap->nbufs; - if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) > - (dmap->raw_buf + dmap->buffsize)) - printk(KERN_ERR "audio: Buffer error 2\n"); - - memset(dmap->raw_buf + p * dmap->fragment_size, - dmap->neutral_byte, - dmap->fragment_size); - } - - dmap->flags |= DMA_DIRTY; -} - -void audio_release(int dev, struct file *file) -{ - const struct coproc_operations *coprocessor; - int mode = translate_mode(file); - - dev = dev >> 4; - - /* - * We do this in DMAbuf_release(). Why are we doing it - * here? Why don't we test the file mode before setting - * both flags? DMAbuf_release() does. - * ...pester...pester...pester... - */ - audio_devs[dev]->dmap_out->closing = 1; - audio_devs[dev]->dmap_in->closing = 1; - - /* - * We need to make sure we allocated the dmap_out buffer - * before we go mucking around with it in sync_output(). - */ - if (mode & OPEN_WRITE) - sync_output(dev); - - if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) { - coprocessor->close(coprocessor->devc, COPR_PCM); - - if (coprocessor->owner) - __MOD_DEC_USE_COUNT(coprocessor->owner); - } - DMAbuf_release(dev, mode); - - if (audio_devs[dev]->d->owner) - __MOD_DEC_USE_COUNT (audio_devs[dev]->d->owner); -} - -static void translate_bytes(const unsigned char *table, unsigned char *buff, int n) -{ - unsigned long i; - - if (n <= 0) - return; - - for (i = 0; i < n; ++i) - buff[i] = table[buff[i]]; -} - -int audio_write(int dev, struct file *file, const char *buf, int count) -{ - int c, p, l, buf_size, used, returned; - int err; - char *dma_buf; - - dev = dev >> 4; - - p = 0; - c = count; - - if(count < 0) - return -EINVAL; - - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EPERM; - - if (audio_devs[dev]->flags & DMA_DUPLEX) - audio_devs[dev]->audio_mode |= AM_WRITE; - else - audio_devs[dev]->audio_mode = AM_WRITE; - - if (!count) /* Flush output */ - { - sync_output(dev); - return 0; - } - - while (c) - { - if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0) - { - /* Handle nonblocking mode */ - if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN) - return p? p : -EAGAIN; /* No more space. Return # of accepted bytes */ - return err; - } - l = c; - - if (l > buf_size) - l = buf_size; - - returned = l; - used = l; - if (!audio_devs[dev]->d->copy_user) - { - if ((dma_buf + l) > - (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize)) - { - printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize); - return -EDOM; - } - if (dma_buf < audio_devs[dev]->dmap_out->raw_buf) - { - printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf); - return -EDOM; - } - if(copy_from_user(dma_buf, &(buf)[p], l)) - return -EFAULT; - } - else audio_devs[dev]->d->copy_user (dev, - dma_buf, 0, - buf, p, - c, buf_size, - &used, &returned, - l); - l = returned; - - if (audio_devs[dev]->local_conversion & CNV_MU_LAW) - { - /* - * This just allows interrupts while the conversion is running - */ - sti(); - translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l); - } - c -= used; - p += used; - DMAbuf_move_wrpointer(dev, l); - - } - - return count; -} - -int audio_read(int dev, struct file *file, char *buf, int count) -{ - int c, p, l; - char *dmabuf; - int buf_no; - - dev = dev >> 4; - p = 0; - c = count; - - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return -EPERM; - - if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - sync_output(dev); - - if (audio_devs[dev]->flags & DMA_DUPLEX) - audio_devs[dev]->audio_mode |= AM_READ; - else - audio_devs[dev]->audio_mode = AM_READ; - - while(c) - { - if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0) - { - /* - * Nonblocking mode handling. Return current # of bytes - */ - - if (p > 0) /* Avoid throwing away data */ - return p; /* Return it instead */ - - if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN) - return -EAGAIN; - - return buf_no; - } - if (l > c) - l = c; - - /* - * Insert any local processing here. - */ - - if (audio_devs[dev]->local_conversion & CNV_MU_LAW) - { - /* - * This just allows interrupts while the conversion is running - */ - sti(); - - translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l); - } - - { - char *fixit = dmabuf; - - if(copy_to_user(&(buf)[p], fixit, l)) - return -EFAULT; - }; - - DMAbuf_rmchars(dev, buf_no, l); - - p += l; - c -= l; - } - - return count - c; -} - -int audio_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg) -{ - int val, count; - unsigned long flags; - struct dma_buffparms *dmap; - - dev = dev >> 4; - - if (_IOC_TYPE(cmd) == 'C') { - if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ - return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0); - /* else - printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */ - return -ENXIO; - } - else switch (cmd) - { - case SNDCTL_DSP_SYNC: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return 0; - if (audio_devs[dev]->dmap_out->fragment_size == 0) - return 0; - sync_output(dev); - DMAbuf_sync(dev); - DMAbuf_reset(dev); - return 0; - - case SNDCTL_DSP_POST: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return 0; - if (audio_devs[dev]->dmap_out->fragment_size == 0) - return 0; - audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY; - sync_output(dev); - dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0); - return 0; - - case SNDCTL_DSP_RESET: - audio_devs[dev]->audio_mode = AM_NONE; - DMAbuf_reset(dev); - return 0; - - case SNDCTL_DSP_GETFMTS: - val = audio_devs[dev]->format_mask | AFMT_MU_LAW; - break; - - case SNDCTL_DSP_SETFMT: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = set_format(dev, val); - break; - - case SNDCTL_DSP_GETISPACE: - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return 0; - if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - return -EBUSY; - return dma_ioctl(dev, cmd, arg); - - case SNDCTL_DSP_GETOSPACE: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EPERM; - if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - return -EBUSY; - return dma_ioctl(dev, cmd, arg); - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETCAPS: - val = 1 | DSP_CAP_MMAP; /* Revision level of this ioctl() */ - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode == OPEN_READWRITE) - val |= DSP_CAP_DUPLEX; - if (audio_devs[dev]->coproc) - val |= DSP_CAP_COPROC; - if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ - val |= DSP_CAP_BATCH; - if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */ - val |= DSP_CAP_TRIGGER; - break; - - case SOUND_PCM_WRITE_RATE: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = audio_devs[dev]->d->set_speed(dev, val); - break; - - case SOUND_PCM_READ_RATE: - val = audio_devs[dev]->d->set_speed(dev, 0); - break; - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val > 1 || val < 0) - return -EINVAL; - val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1; - break; - - case SOUND_PCM_WRITE_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = audio_devs[dev]->d->set_channels(dev, val); - break; - - case SOUND_PCM_READ_CHANNELS: - val = audio_devs[dev]->d->set_channels(dev, 0); - break; - - case SOUND_PCM_READ_BITS: - val = audio_devs[dev]->d->set_bits(dev, 0); - break; - - case SNDCTL_DSP_SETDUPLEX: - if (audio_devs[dev]->open_mode != OPEN_READWRITE) - return -EPERM; - return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO; - - case SNDCTL_DSP_PROFILE: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (audio_devs[dev]->open_mode & OPEN_WRITE) - audio_devs[dev]->dmap_out->applic_profile = val; - if (audio_devs[dev]->open_mode & OPEN_READ) - audio_devs[dev]->dmap_in->applic_profile = val; - return 0; - - case SNDCTL_DSP_GETODELAY: - dmap = audio_devs[dev]->dmap_out; - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - if (!(dmap->flags & DMA_ALLOC_DONE)) - { - val=0; - break; - } - - save_flags (flags); - cli(); - /* Compute number of bytes that have been played */ - count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT); - if (count < dmap->fragment_size && dmap->qhead != 0) - count += dmap->bytes_in_use; /* Pointer wrap not handled yet */ - count += dmap->byte_counter; - - /* Substract current count from the number of bytes written by app */ - count = dmap->user_counter - count; - if (count < 0) - count = 0; - restore_flags (flags); - val = count; - break; - - default: - return dma_ioctl(dev, cmd, arg); - } - return put_user(val, (int *)arg); -} - -void audio_init_devices(void) -{ - /* - * NOTE! This routine could be called several times during boot. - */ -} - -void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording) -{ - /* - * This routine breaks the physical device buffers to logical ones. - */ - - struct audio_operations *dsp_dev = audio_devs[dev]; - - unsigned i, n; - unsigned sr, nc, sz, bsz; - - sr = dsp_dev->d->set_speed(dev, 0); - nc = dsp_dev->d->set_channels(dev, 0); - sz = dsp_dev->d->set_bits(dev, 0); - - if (sz == 8) - dmap->neutral_byte = NEUTRAL8; - else - dmap->neutral_byte = NEUTRAL16; - - if (sr < 1 || nc < 1 || sz < 1) - { -/* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/ - sr = DSP_DEFAULT_SPEED; - nc = 1; - sz = 8; - } - - sz = sr * nc * sz; - - sz /= 8; /* #bits -> #bytes */ - dmap->data_rate = sz; - - if (!dmap->needs_reorg) - return; - dmap->needs_reorg = 0; - - if (dmap->fragment_size == 0) - { - /* Compute the fragment size using the default algorithm */ - - /* - * Compute a buffer size for time not exceeding 1 second. - * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds - * of sound (using the current speed, sample size and #channels). - */ - - bsz = dmap->buffsize; - while (bsz > sz) - bsz /= 2; - - if (bsz == dmap->buffsize) - bsz /= 2; /* Needs at least 2 buffers */ - - /* - * Split the computed fragment to smaller parts. After 3.5a9 - * the default subdivision is 4 which should give better - * results when recording. - */ - - if (dmap->subdivision == 0) /* Not already set */ - { - dmap->subdivision = 4; /* Init to the default value */ - - if ((bsz / dmap->subdivision) > 4096) - dmap->subdivision *= 2; - if ((bsz / dmap->subdivision) < 4096) - dmap->subdivision = 1; - } - bsz /= dmap->subdivision; - - if (bsz < 16) - bsz = 16; /* Just a sanity check */ - - dmap->fragment_size = bsz; - } - else - { - /* - * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or - * the buffer size computation has already been done. - */ - if (dmap->fragment_size > (dmap->buffsize / 2)) - dmap->fragment_size = (dmap->buffsize / 2); - bsz = dmap->fragment_size; - } - - if (audio_devs[dev]->min_fragment) - if (bsz < (1 << audio_devs[dev]->min_fragment)) - bsz = 1 << audio_devs[dev]->min_fragment; - if (audio_devs[dev]->max_fragment) - if (bsz > (1 << audio_devs[dev]->max_fragment)) - bsz = 1 << audio_devs[dev]->max_fragment; - bsz &= ~0x07; /* Force size which is multiple of 8 bytes */ -#ifdef OS_DMA_ALIGN_CHECK - OS_DMA_ALIGN_CHECK(bsz); -#endif - - n = dmap->buffsize / bsz; - if (n > MAX_SUB_BUFFERS) - n = MAX_SUB_BUFFERS; - if (n > dmap->max_fragments) - n = dmap->max_fragments; - - if (n < 2) - { - n = 2; - bsz /= 2; - } - dmap->nbufs = n; - dmap->bytes_in_use = n * bsz; - dmap->fragment_size = bsz; - dmap->max_byte_counter = (dmap->data_rate * 60 * 60) + - dmap->bytes_in_use; /* Approximately one hour */ - - if (dmap->raw_buf) - { - memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use); - } - - for (i = 0; i < dmap->nbufs; i++) - { - dmap->counts[i] = 0; - } - - dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY; -} - -static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact) -{ - if (fact == 0) - { - fact = dmap->subdivision; - if (fact == 0) - fact = 1; - return fact; - } - if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */ - return -EINVAL; - - if (fact > MAX_REALTIME_FACTOR) - return -EINVAL; - - if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) - return -EINVAL; - - dmap->subdivision = fact; - return fact; -} - -static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact) -{ - int bytes, count; - - if (fact == 0) - return -EIO; - - if (dmap->subdivision != 0 || - dmap->fragment_size) /* Too late to change */ - return -EINVAL; - - bytes = fact & 0xffff; - count = (fact >> 16) & 0x7fff; - - if (count == 0) - count = MAX_SUB_BUFFERS; - else if (count < MAX_SUB_BUFFERS) - count++; - - if (bytes < 4 || bytes > 17) /* <16 || > 512k */ - return -EINVAL; - - if (count < 2) - return -EINVAL; - - if (audio_devs[dev]->min_fragment > 0) - if (bytes < audio_devs[dev]->min_fragment) - bytes = audio_devs[dev]->min_fragment; - - if (audio_devs[dev]->max_fragment > 0) - if (bytes > audio_devs[dev]->max_fragment) - bytes = audio_devs[dev]->max_fragment; - -#ifdef OS_DMA_MINBITS - if (bytes < OS_DMA_MINBITS) - bytes = OS_DMA_MINBITS; -#endif - - dmap->fragment_size = (1 << bytes); - dmap->max_fragments = count; - - if (dmap->fragment_size > dmap->buffsize) - dmap->fragment_size = dmap->buffsize; - - if (dmap->fragment_size == dmap->buffsize && - audio_devs[dev]->flags & DMA_AUTOMODE) - dmap->fragment_size /= 2; /* Needs at least 2 buffers */ - - dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ - return bytes | ((count - 1) << 16); -} - -int dma_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out; - struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in; - struct dma_buffparms *dmap; - audio_buf_info info; - count_info cinfo; - int fact, ret, changed, bits, count, err; - unsigned long flags; - - switch (cmd) - { - case SNDCTL_DSP_SUBDIVIDE: - ret = 0; - if (get_user(fact, (int *)arg)) - return -EFAULT; - if (audio_devs[dev]->open_mode & OPEN_WRITE) - ret = dma_subdivide(dev, dmap_out, fact); - if (ret < 0) - return ret; - if (audio_devs[dev]->open_mode != OPEN_WRITE || - (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ)) - ret = dma_subdivide(dev, dmap_in, fact); - if (ret < 0) - return ret; - break; - - case SNDCTL_DSP_GETISPACE: - case SNDCTL_DSP_GETOSPACE: - dmap = dmap_out; - if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ)) - return -EINVAL; - if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX) - dmap = dmap_in; - if (dmap->mapping_flags & DMA_MAP_MAPPED) - return -EINVAL; - if (!(dmap->flags & DMA_ALLOC_DONE)) - reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE)); - info.fragstotal = dmap->nbufs; - if (cmd == SNDCTL_DSP_GETISPACE) - info.fragments = dmap->qlen; - else - { - if (!DMAbuf_space_in_queue(dev)) - info.fragments = 0; - else - { - info.fragments = DMAbuf_space_in_queue(dev); - if (audio_devs[dev]->d->local_qlen) - { - int tmp = audio_devs[dev]->d->local_qlen(dev); - if (tmp && info.fragments) - tmp--; /* - * This buffer has been counted twice - */ - info.fragments -= tmp; - } - } - } - if (info.fragments < 0) - info.fragments = 0; - else if (info.fragments > dmap->nbufs) - info.fragments = dmap->nbufs; - - info.fragsize = dmap->fragment_size; - info.bytes = info.fragments * dmap->fragment_size; - - if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen) - info.bytes -= dmap->counts[dmap->qhead]; - else - { - info.fragments = info.bytes / dmap->fragment_size; - info.bytes -= dmap->user_counter % dmap->fragment_size; - } - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(bits, (int *)arg)) - return -EFAULT; - bits &= audio_devs[dev]->open_mode; - if (audio_devs[dev]->d->trigger == NULL) - return -EINVAL; - if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) && - (bits & PCM_ENABLE_OUTPUT)) - return -EINVAL; - save_flags(flags); - cli(); - changed = audio_devs[dev]->enable_bits ^ bits; - if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) - { - reorganize_buffers(dev, dmap_in, 1); - if ((err = audio_devs[dev]->d->prepare_for_input(dev, - dmap_in->fragment_size, dmap_in->nbufs)) < 0) { - restore_flags(flags); - return -err; - } - dmap_in->dma_mode = DMODE_INPUT; - audio_devs[dev]->enable_bits = bits; - DMAbuf_activate_recording(dev, dmap_in); - } - if ((changed & bits) & PCM_ENABLE_OUTPUT && - (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) && - audio_devs[dev]->go) - { - if (!(dmap_out->flags & DMA_ALLOC_DONE)) - reorganize_buffers(dev, dmap_out, 0); - dmap_out->dma_mode = DMODE_OUTPUT; - audio_devs[dev]->enable_bits = bits; - dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; - DMAbuf_launch_output(dev, dmap_out); - } - audio_devs[dev]->enable_bits = bits; -#if 0 - if (changed && audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go); -#endif - restore_flags(flags); - /* Falls through... */ - - case SNDCTL_DSP_GETTRIGGER: - ret = audio_devs[dev]->enable_bits; - break; - - case SNDCTL_DSP_SETSYNCRO: - if (!audio_devs[dev]->d->trigger) - return -EINVAL; - audio_devs[dev]->d->trigger(dev, 0); - audio_devs[dev]->go = 0; - return 0; - - case SNDCTL_DSP_GETIPTR: - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return -EINVAL; - save_flags(flags); - cli(); - cinfo.bytes = dmap_in->byte_counter; - cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3; - if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0) - cinfo.bytes += dmap_in->bytes_in_use; /* Pointer wrap not handled yet */ - cinfo.blocks = dmap_in->qlen; - cinfo.bytes += cinfo.ptr; - if (dmap_in->mapping_flags & DMA_MAP_MAPPED) - dmap_in->qlen = 0; /* Reset interrupt counter */ - restore_flags(flags); - if (copy_to_user(arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - - save_flags(flags); - cli(); - cinfo.bytes = dmap_out->byte_counter; - cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3; - if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0) - cinfo.bytes += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */ - cinfo.blocks = dmap_out->qlen; - cinfo.bytes += cinfo.ptr; - if (dmap_out->mapping_flags & DMA_MAP_MAPPED) - dmap_out->qlen = 0; /* Reset interrupt counter */ - restore_flags(flags); - if (copy_to_user(arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - if (!(dmap_out->flags & DMA_ALLOC_DONE)) - { - ret=0; - break; - } - save_flags(flags); - cli(); - /* Compute number of bytes that have been played */ - count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT); - if (count < dmap_out->fragment_size && dmap_out->qhead != 0) - count += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */ - count += dmap_out->byte_counter; - /* Substract current count from the number of bytes written by app */ - count = dmap_out->user_counter - count; - if (count < 0) - count = 0; - restore_flags (flags); - ret = count; - break; - - case SNDCTL_DSP_POST: - if (audio_devs[dev]->dmap_out->qlen > 0) - if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE)) - DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out); - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - dmap = dmap_out; - if (audio_devs[dev]->open_mode & OPEN_WRITE) - reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ)); - if (audio_devs[dev]->open_mode == OPEN_READ || - (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ)) - reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ)); - if (audio_devs[dev]->open_mode == OPEN_READ) - dmap = dmap_in; - ret = dmap->fragment_size; - break; - - case SNDCTL_DSP_SETFRAGMENT: - ret = 0; - if (get_user(fact, (int *)arg)) - return -EFAULT; - if (audio_devs[dev]->open_mode & OPEN_WRITE) - ret = dma_set_fragment(dev, dmap_out, fact); - if (ret < 0) - return ret; - if (audio_devs[dev]->open_mode == OPEN_READ || - (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ)) - ret = dma_set_fragment(dev, dmap_in, fact); - if (ret < 0) - return ret; - if (!arg) /* don't know what this is good for, but preserve old semantics */ - return 0; - break; - - default: - if (!audio_devs[dev]->d->ioctl) - return -EINVAL; - return audio_devs[dev]->d->ioctl(dev, cmd, arg); - } - return put_user(ret, (int *)arg); -} diff -Nru a/drivers/sound/audio_syms.c b/drivers/sound/audio_syms.c --- a/drivers/sound/audio_syms.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,21 +0,0 @@ -/* - * Exported symbols for audio driver. - * __NO_VERSION__ because this is still part of sound.o. - */ - -#define __NO_VERSION__ -#include - -char audio_syms_symbol; - -#include "sound_config.h" -#include "sound_calls.h" - -EXPORT_SYMBOL(DMAbuf_start_dma); -EXPORT_SYMBOL(DMAbuf_open_dma); -EXPORT_SYMBOL(DMAbuf_close_dma); -EXPORT_SYMBOL(DMAbuf_inputintr); -EXPORT_SYMBOL(DMAbuf_outputintr); -EXPORT_SYMBOL(dma_ioctl); -EXPORT_SYMBOL(audio_open); -EXPORT_SYMBOL(audio_release); diff -Nru a/drivers/sound/awe_hw.h b/drivers/sound/awe_hw.h --- a/drivers/sound/awe_hw.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,99 +0,0 @@ -/* - * sound/awe_hw.h - * - * Access routines and definitions for the low level driver for the - * Creative AWE32/SB32/AWE64 wave table synth. - * version 0.4.4; Jan. 4, 2000 - * - * Copyright (C) 1996-2000 Takashi Iwai - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef AWE_HW_H_DEF -#define AWE_HW_H_DEF - -/* - * Emu-8000 control registers - * name(channel) reg, port - */ - -#define awe_cmd_idx(reg,ch) (((reg)<< 5) | (ch)) - -#define Data0 0 /* 0x620: doubleword r/w */ -#define Data1 1 /* 0xA20: doubleword r/w */ -#define Data2 2 /* 0xA22: word r/w */ -#define Data3 3 /* 0xE20: word r/w */ -#define Pointer 4 /* 0xE22 register pointer r/w */ - -#define AWE_CPF(ch) awe_cmd_idx(0,ch), Data0 /* DW: current pitch and fractional address */ -#define AWE_PTRX(ch) awe_cmd_idx(1,ch), Data0 /* DW: pitch target and reverb send */ -#define AWE_CVCF(ch) awe_cmd_idx(2,ch), Data0 /* DW: current volume and filter cutoff */ -#define AWE_VTFT(ch) awe_cmd_idx(3,ch), Data0 /* DW: volume and filter cutoff targets */ -#define AWE_0080(ch) awe_cmd_idx(4,ch), Data0 /* DW: ?? */ -#define AWE_00A0(ch) awe_cmd_idx(5,ch), Data0 /* DW: ?? */ -#define AWE_PSST(ch) awe_cmd_idx(6,ch), Data0 /* DW: pan send and loop start address */ -#define AWE_CSL(ch) awe_cmd_idx(7,ch), Data0 /* DW: chorus send and loop end address */ -#define AWE_CCCA(ch) awe_cmd_idx(0,ch), Data1 /* DW: Q, control bits, and current address */ -#define AWE_HWCF4 awe_cmd_idx(1,9), Data1 /* DW: config dw 4 */ -#define AWE_HWCF5 awe_cmd_idx(1,10), Data1 /* DW: config dw 5 */ -#define AWE_HWCF6 awe_cmd_idx(1,13), Data1 /* DW: config dw 6 */ -#define AWE_HWCF7 awe_cmd_idx(1,14), Data1 /* DW: config dw 7? (not documented) */ -#define AWE_SMALR awe_cmd_idx(1,20), Data1 /* DW: sound memory address for left read */ -#define AWE_SMARR awe_cmd_idx(1,21), Data1 /* DW: for right read */ -#define AWE_SMALW awe_cmd_idx(1,22), Data1 /* DW: sound memory address for left write */ -#define AWE_SMARW awe_cmd_idx(1,23), Data1 /* DW: for right write */ -#define AWE_SMLD awe_cmd_idx(1,26), Data1 /* W: sound memory left data */ -#define AWE_SMRD awe_cmd_idx(1,26), Data2 /* W: right data */ -#define AWE_WC awe_cmd_idx(1,27), Data2 /* W: sample counter */ -#define AWE_WC_Cmd awe_cmd_idx(1,27) -#define AWE_WC_Port Data2 -#define AWE_HWCF1 awe_cmd_idx(1,29), Data1 /* W: config w 1 */ -#define AWE_HWCF2 awe_cmd_idx(1,30), Data1 /* W: config w 2 */ -#define AWE_HWCF3 awe_cmd_idx(1,31), Data1 /* W: config w 3 */ -#define AWE_INIT1(ch) awe_cmd_idx(2,ch), Data1 /* W: init array 1 */ -#define AWE_INIT2(ch) awe_cmd_idx(2,ch), Data2 /* W: init array 2 */ -#define AWE_INIT3(ch) awe_cmd_idx(3,ch), Data1 /* W: init array 3 */ -#define AWE_INIT4(ch) awe_cmd_idx(3,ch), Data2 /* W: init array 4 */ -#define AWE_ENVVOL(ch) awe_cmd_idx(4,ch), Data1 /* W: volume envelope delay */ -#define AWE_DCYSUSV(ch) awe_cmd_idx(5,ch), Data1 /* W: volume envelope sustain and decay */ -#define AWE_ENVVAL(ch) awe_cmd_idx(6,ch), Data1 /* W: modulation envelope delay */ -#define AWE_DCYSUS(ch) awe_cmd_idx(7,ch), Data1 /* W: modulation envelope sustain and decay */ -#define AWE_ATKHLDV(ch) awe_cmd_idx(4,ch), Data2 /* W: volume envelope attack and hold */ -#define AWE_LFO1VAL(ch) awe_cmd_idx(5,ch), Data2 /* W: LFO#1 Delay */ -#define AWE_ATKHLD(ch) awe_cmd_idx(6,ch), Data2 /* W: modulation envelope attack and hold */ -#define AWE_LFO2VAL(ch) awe_cmd_idx(7,ch), Data2 /* W: LFO#2 Delay */ -#define AWE_IP(ch) awe_cmd_idx(0,ch), Data3 /* W: initial pitch */ -#define AWE_IFATN(ch) awe_cmd_idx(1,ch), Data3 /* W: initial filter cutoff and attenuation */ -#define AWE_PEFE(ch) awe_cmd_idx(2,ch), Data3 /* W: pitch and filter envelope heights */ -#define AWE_FMMOD(ch) awe_cmd_idx(3,ch), Data3 /* W: vibrato and filter modulation freq */ -#define AWE_TREMFRQ(ch) awe_cmd_idx(4,ch), Data3 /* W: LFO#1 tremolo amount and freq */ -#define AWE_FM2FRQ2(ch) awe_cmd_idx(5,ch), Data3 /* W: LFO#2 vibrato amount and freq */ - -/* used during detection (returns ROM version?; not documented in ADIP) */ -#define AWE_U1 0xE0, Data3 /* (R)(W) used in initialization */ -#define AWE_U2(ch) 0xC0+(ch), Data3 /* (W)(W) used in init envelope */ - - -#define AWE_MAX_VOICES 32 -#define AWE_NORMAL_VOICES 30 /*30&31 are reserved for DRAM refresh*/ - -#define AWE_MAX_CHANNELS 32 /* max midi channels (must >= voices) */ -#define AWE_MAX_LAYERS AWE_MAX_VOICES /* maximum number of multiple layers */ - -#define AWE_DRAM_OFFSET 0x200000 -#define AWE_MAX_DRAM_SIZE (28 * 1024) /* 28 MB is max onboard memory */ - -#endif diff -Nru a/drivers/sound/awe_wave.c b/drivers/sound/awe_wave.c --- a/drivers/sound/awe_wave.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,6159 +0,0 @@ -/* - * sound/awe_wave.c - * - * The low level driver for the AWE32/SB32/AWE64 wave table synth. - * version 0.4.4; Jan. 4, 2000 - * - * Copyright (C) 1996-2000 Takashi Iwai - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include "sound_config.h" - -#include "awe_wave.h" -#include "awe_hw.h" - -#ifdef AWE_HAS_GUS_COMPATIBILITY -#include "tuning.h" -#include -#endif - -/* - * debug message - */ - -#ifdef AWE_DEBUG_ON -#define DEBUG(LVL,XXX) {if (ctrls[AWE_MD_DEBUG_MODE] > LVL) { XXX; }} -#define ERRMSG(XXX) {if (ctrls[AWE_MD_DEBUG_MODE]) { XXX; }} -#define FATALERR(XXX) XXX -#else -#define DEBUG(LVL,XXX) /**/ -#define ERRMSG(XXX) XXX -#define FATALERR(XXX) XXX -#endif - -/* - * bank and voice record - */ - -typedef struct _sf_list sf_list; -typedef struct _awe_voice_list awe_voice_list; -typedef struct _awe_sample_list awe_sample_list; - -/* soundfont record */ -struct _sf_list { - unsigned short sf_id; /* id number */ - unsigned short type; /* lock & shared flags */ - int num_info; /* current info table index */ - int num_sample; /* current sample table index */ - int mem_ptr; /* current word byte pointer */ - awe_voice_list *infos, *last_infos; /* instruments */ - awe_sample_list *samples, *last_samples; /* samples */ -#ifdef AWE_ALLOW_SAMPLE_SHARING - sf_list *shared; /* shared list */ - unsigned char name[AWE_PATCH_NAME_LEN]; /* sharing id */ -#endif - sf_list *next, *prev; -}; - -/* instrument list */ -struct _awe_voice_list { - awe_voice_info v; /* instrument information */ - sf_list *holder; /* parent sf_list of this record */ - unsigned char bank, instr; /* preset number information */ - char type, disabled; /* type=normal/mapped, disabled=boolean */ - awe_voice_list *next; /* linked list with same sf_id */ - awe_voice_list *next_instr; /* instrument list */ - awe_voice_list *next_bank; /* hash table list */ -}; - -/* voice list type */ -#define V_ST_NORMAL 0 -#define V_ST_MAPPED 1 - -/* sample list */ -struct _awe_sample_list { - awe_sample_info v; /* sample information */ - sf_list *holder; /* parent sf_list of this record */ - awe_sample_list *next; /* linked list with same sf_id */ -}; - -/* sample and information table */ -static int current_sf_id = 0; /* current number of fonts */ -static int locked_sf_id = 0; /* locked position */ -static sf_list *sfhead = NULL, *sftail = NULL; /* linked-lists */ - -#define awe_free_mem_ptr() (sftail ? sftail->mem_ptr : 0) -#define awe_free_info() (sftail ? sftail->num_info : 0) -#define awe_free_sample() (sftail ? sftail->num_sample : 0) - -#define AWE_MAX_PRESETS 256 -#define AWE_DEFAULT_PRESET 0 -#define AWE_DEFAULT_BANK 0 -#define AWE_DEFAULT_DRUM 0 -#define AWE_DRUM_BANK 128 - -#define MAX_LAYERS AWE_MAX_VOICES - -/* preset table index */ -static awe_voice_list *preset_table[AWE_MAX_PRESETS]; - -/* - * voice table - */ - -/* effects table */ -typedef struct FX_Rec { /* channel effects */ - unsigned char flags[AWE_FX_END]; - short val[AWE_FX_END]; -} FX_Rec; - - -/* channel parameters */ -typedef struct _awe_chan_info { - int channel; /* channel number */ - int bank; /* current tone bank */ - int instr; /* current program */ - int bender; /* midi pitchbend (-8192 - 8192) */ - int bender_range; /* midi bender range (x100) */ - int panning; /* panning (0-127) */ - int main_vol; /* channel volume (0-127) */ - int expression_vol; /* midi expression (0-127) */ - int chan_press; /* channel pressure */ - int sustained; /* sustain status in MIDI */ - FX_Rec fx; /* effects */ - FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */ -} awe_chan_info; - -/* voice parameters */ -typedef struct _voice_info { - int state; -#define AWE_ST_OFF (1<<0) /* no sound */ -#define AWE_ST_ON (1<<1) /* playing */ -#define AWE_ST_STANDBY (1<<2) /* stand by for playing */ -#define AWE_ST_SUSTAINED (1<<3) /* sustained */ -#define AWE_ST_MARK (1<<4) /* marked for allocation */ -#define AWE_ST_DRAM (1<<5) /* DRAM read/write */ -#define AWE_ST_FM (1<<6) /* reserved for FM */ -#define AWE_ST_RELEASED (1<<7) /* released */ - - int ch; /* midi channel */ - int key; /* internal key for search */ - int layer; /* layer number (for channel mode only) */ - int time; /* allocated time */ - awe_chan_info *cinfo; /* channel info */ - - int note; /* midi key (0-127) */ - int velocity; /* midi velocity (0-127) */ - int sostenuto; /* sostenuto on/off */ - awe_voice_info *sample; /* assigned voice */ - - /* EMU8000 parameters */ - int apitch; /* pitch parameter */ - int avol; /* volume parameter */ - int apan; /* panning parameter */ - int acutoff; /* cutoff parameter */ - short aaux; /* aux word */ -} voice_info; - -/* voice information */ -static voice_info voices[AWE_MAX_VOICES]; - -#define IS_NO_SOUND(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED)) -#define IS_NO_EFFECT(v) (voices[v].state != AWE_ST_ON) -#define IS_PLAYING(v) (voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED)) -#define IS_EMPTY(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_MARK|AWE_ST_DRAM|AWE_ST_FM)) - - -/* MIDI channel effects information (for hw control) */ -static awe_chan_info channels[AWE_MAX_CHANNELS]; - - -/* - * global variables - */ - -#ifndef AWE_DEFAULT_BASE_ADDR -#define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */ -#endif - -#ifndef AWE_DEFAULT_MEM_SIZE -#define AWE_DEFAULT_MEM_SIZE -1 /* autodetect */ -#endif - -int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */ -int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */ -#ifdef __ISAPNP__ -static int isapnp = -1; -#else -static int isapnp = 0; -#endif - -MODULE_AUTHOR("Takashi Iwai "); -MODULE_DESCRIPTION("SB AWE32/64 WaveTable driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM(io, "i"); -MODULE_PARM_DESC(io, "base i/o port of Emu8000"); -MODULE_PARM(memsize, "i"); -MODULE_PARM_DESC(memsize, "onboard DRAM size in Kbytes"); -MODULE_PARM(isapnp, "i"); -MODULE_PARM_DESC(isapnp, "use ISAPnP detection"); -EXPORT_NO_SYMBOLS; - -/* DRAM start offset */ -static int awe_mem_start = AWE_DRAM_OFFSET; - -/* maximum channels for playing */ -static int awe_max_voices = AWE_MAX_VOICES; - -static int patch_opened = 0; /* sample already loaded? */ - -static char atten_relative = FALSE; -static short atten_offset = 0; - -static int awe_present = FALSE; /* awe device present? */ -static int awe_busy = FALSE; /* awe device opened? */ - -static int my_dev = -1; - -#define DEFAULT_DRUM_FLAGS ((1 << 9) | (1 << 25)) -#define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c))) -#define DRUM_CHANNEL_ON(c) (drum_flags |= (1 << (c))) -#define DRUM_CHANNEL_OFF(c) (drum_flags &= ~(1 << (c))) -static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */ - -static int playing_mode = AWE_PLAY_INDIRECT; -#define SINGLE_LAYER_MODE() (playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT) -#define MULTI_LAYER_MODE() (playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2) - -static int current_alloc_time = 0; /* voice allocation index for channel mode */ - -static struct synth_info awe_info = { - "AWE32 Synth", /* name */ - 0, /* device */ - SYNTH_TYPE_SAMPLE, /* synth_type */ - SAMPLE_TYPE_AWE32, /* synth_subtype */ - 0, /* perc_mode (obsolete) */ - AWE_MAX_VOICES, /* nr_voices */ - 0, /* nr_drums (obsolete) */ - 400 /* instr_bank_size */ -}; - - -static struct voice_alloc_info *voice_alloc; /* set at initialization */ - - -/* - * function prototypes - */ - -static int awe_check_port(void); -static void awe_request_region(void); -static void awe_release_region(void); - -static void awe_reset_samples(void); -/* emu8000 chip i/o access */ -static void setup_ports(int p1, int p2, int p3); -static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data); -static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data); -static unsigned short awe_peek(unsigned short cmd, unsigned short port); -static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port); -static void awe_wait(unsigned short delay); - -/* initialize emu8000 chip */ -static int _attach_awe(void); -static void _unload_awe(void); -static void awe_initialize(void); - -/* set voice parameters */ -static void awe_init_ctrl_parms(int init_all); -static void awe_init_voice_info(awe_voice_info *vp); -static void awe_init_voice_parm(awe_voice_parm *pp); -#ifdef AWE_HAS_GUS_COMPATIBILITY -static int freq_to_note(int freq); -static int calc_rate_offset(int Hz); -/*static int calc_parm_delay(int msec);*/ -static int calc_parm_hold(int msec); -static int calc_parm_attack(int msec); -static int calc_parm_decay(int msec); -static int calc_parm_search(int msec, short *table); -#endif /* gus compat */ - -/* turn on/off note */ -static void awe_note_on(int voice); -static void awe_note_off(int voice); -static void awe_terminate(int voice); -static void awe_exclusive_off(int voice); -static void awe_note_off_all(int do_sustain); - -/* calculate voice parameters */ -typedef void (*fx_affect_func)(int voice, int forced); -static void awe_set_pitch(int voice, int forced); -static void awe_set_voice_pitch(int voice, int forced); -static void awe_set_volume(int voice, int forced); -static void awe_set_voice_vol(int voice, int forced); -static void awe_set_pan(int voice, int forced); -static void awe_fx_fmmod(int voice, int forced); -static void awe_fx_tremfrq(int voice, int forced); -static void awe_fx_fm2frq2(int voice, int forced); -static void awe_fx_filterQ(int voice, int forced); -static void awe_calc_pitch(int voice); -#ifdef AWE_HAS_GUS_COMPATIBILITY -static void awe_calc_pitch_from_freq(int voice, int freq); -#endif -static void awe_calc_volume(int voice); -static void awe_update_volume(void); -static void awe_change_master_volume(short val); -static void awe_voice_init(int voice, int init_all); -static void awe_channel_init(int ch, int init_all); -static void awe_fx_init(int ch); -static void awe_send_effect(int voice, int layer, int type, int val); -static void awe_modwheel_change(int voice, int value); - -/* sequencer interface */ -static int awe_open(int dev, int mode); -static void awe_close(int dev); -static int awe_ioctl(int dev, unsigned int cmd, caddr_t arg); -static int awe_kill_note(int dev, int voice, int note, int velocity); -static int awe_start_note(int dev, int v, int note_num, int volume); -static int awe_set_instr(int dev, int voice, int instr_no); -static int awe_set_instr_2(int dev, int voice, int instr_no); -static void awe_reset(int dev); -static void awe_hw_control(int dev, unsigned char *event); -static int awe_load_patch(int dev, int format, const char *addr, - int offs, int count, int pmgr_flag); -static void awe_aftertouch(int dev, int voice, int pressure); -static void awe_controller(int dev, int voice, int ctrl_num, int value); -static void awe_panning(int dev, int voice, int value); -static void awe_volume_method(int dev, int mode); -static void awe_bender(int dev, int voice, int value); -static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc); -static void awe_setup_voice(int dev, int voice, int chn); - -#define awe_key_pressure(dev,voice,key,press) awe_start_note(dev,voice,(key)+128,press) - -/* hardware controls */ -#ifdef AWE_HAS_GUS_COMPATIBILITY -static void awe_hw_gus_control(int dev, int cmd, unsigned char *event); -#endif -static void awe_hw_awe_control(int dev, int cmd, unsigned char *event); -static void awe_voice_change(int voice, fx_affect_func func); -static void awe_sostenuto_on(int voice, int forced); -static void awe_sustain_off(int voice, int forced); -static void awe_terminate_and_init(int voice, int forced); - -/* voice search */ -static int awe_search_key(int bank, int preset, int note); -static awe_voice_list *awe_search_instr(int bank, int preset, int note); -static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist); -static void awe_alloc_multi_voices(int ch, int note, int velocity, int key); -static void awe_alloc_one_voice(int voice, int note, int velocity); -static int awe_clear_voice(void); - -/* load / remove patches */ -static int awe_open_patch(awe_patch_info *patch, const char *addr, int count); -static int awe_close_patch(awe_patch_info *patch, const char *addr, int count); -static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count); -static int awe_load_info(awe_patch_info *patch, const char *addr, int count); -static int awe_remove_info(awe_patch_info *patch, const char *addr, int count); -static int awe_load_data(awe_patch_info *patch, const char *addr, int count); -static int awe_replace_data(awe_patch_info *patch, const char *addr, int count); -static int awe_load_map(awe_patch_info *patch, const char *addr, int count); -#ifdef AWE_HAS_GUS_COMPATIBILITY -static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag); -#endif -/*static int awe_probe_info(awe_patch_info *patch, const char *addr, int count);*/ -static int awe_probe_data(awe_patch_info *patch, const char *addr, int count); -static sf_list *check_patch_opened(int type, char *name); -static int awe_write_wave_data(const char *addr, int offset, awe_sample_list *sp, int channels); -static int awe_create_sf(int type, char *name); -static void awe_free_sf(sf_list *sf); -static void add_sf_info(sf_list *sf, awe_voice_list *rec); -static void add_sf_sample(sf_list *sf, awe_sample_list *smp); -static void purge_old_list(awe_voice_list *rec, awe_voice_list *next); -static void add_info_list(awe_voice_list *rec); -static void awe_remove_samples(int sf_id); -static void rebuild_preset_list(void); -static short awe_set_sample(awe_voice_list *rec); -static awe_sample_list *search_sample_index(sf_list *sf, int sample); - -static int is_identical_holder(sf_list *sf1, sf_list *sf2); -#ifdef AWE_ALLOW_SAMPLE_SHARING -static int is_identical_name(unsigned char *name, sf_list *p); -static int is_shared_sf(unsigned char *name); -static int info_duplicated(sf_list *sf, awe_voice_list *rec); -#endif /* allow sharing */ - -/* lowlevel functions */ -static void awe_init_audio(void); -static void awe_init_dma(void); -static void awe_init_array(void); -static void awe_send_array(unsigned short *data); -static void awe_tweak_voice(int voice); -static void awe_tweak(void); -static void awe_init_fm(void); -static int awe_open_dram_for_write(int offset, int channels); -static void awe_open_dram_for_check(void); -static void awe_close_dram(void); -/*static void awe_write_dram(unsigned short c);*/ -static int awe_detect_base(int addr); -static int awe_detect(void); -static void awe_check_dram(void); -static int awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count); -static void awe_set_chorus_mode(int mode); -static void awe_update_chorus_mode(void); -static int awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count); -static void awe_set_reverb_mode(int mode); -static void awe_update_reverb_mode(void); -static void awe_equalizer(int bass, int treble); -static void awe_update_equalizer(void); - -#ifdef CONFIG_AWE32_MIXER -static void attach_mixer(void); -static void unload_mixer(void); -#endif - -#ifdef CONFIG_AWE32_MIDIEMU -static void attach_midiemu(void); -static void unload_midiemu(void); -#endif - -#define limitvalue(x, a, b) if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b) - -/* - * control parameters - */ - - -#ifdef AWE_USE_NEW_VOLUME_CALC -#define DEF_VOLUME_CALC TRUE -#else -#define DEF_VOLUME_CALC FALSE -#endif /* new volume */ - -#define DEF_ZERO_ATTEN 32 /* 12dB below */ -#define DEF_MOD_SENSE 18 -#define DEF_CHORUS_MODE 2 -#define DEF_REVERB_MODE 4 -#define DEF_BASS_LEVEL 5 -#define DEF_TREBLE_LEVEL 9 - -static struct CtrlParmsDef { - int value; - int init_each_time; - void (*update)(void); -} ctrl_parms[AWE_MD_END] = { - {0,0, NULL}, {0,0, NULL}, /* <-- not used */ - {AWE_VERSION_NUMBER, FALSE, NULL}, - {TRUE, FALSE, NULL}, /* exclusive */ - {TRUE, FALSE, NULL}, /* realpan */ - {AWE_DEFAULT_BANK, FALSE, NULL}, /* gusbank */ - {FALSE, TRUE, NULL}, /* keep effect */ - {DEF_ZERO_ATTEN, FALSE, awe_update_volume}, /* zero_atten */ - {FALSE, FALSE, NULL}, /* chn_prior */ - {DEF_MOD_SENSE, FALSE, NULL}, /* modwheel sense */ - {AWE_DEFAULT_PRESET, FALSE, NULL}, /* def_preset */ - {AWE_DEFAULT_BANK, FALSE, NULL}, /* def_bank */ - {AWE_DEFAULT_DRUM, FALSE, NULL}, /* def_drum */ - {FALSE, FALSE, NULL}, /* toggle_drum_bank */ - {DEF_VOLUME_CALC, FALSE, awe_update_volume}, /* new_volume_calc */ - {DEF_CHORUS_MODE, FALSE, awe_update_chorus_mode}, /* chorus mode */ - {DEF_REVERB_MODE, FALSE, awe_update_reverb_mode}, /* reverb mode */ - {DEF_BASS_LEVEL, FALSE, awe_update_equalizer}, /* bass level */ - {DEF_TREBLE_LEVEL, FALSE, awe_update_equalizer}, /* treble level */ - {0, FALSE, NULL}, /* debug mode */ - {FALSE, FALSE, NULL}, /* pan exchange */ -}; - -static int ctrls[AWE_MD_END]; - - -/* - * synth operation table - */ - -static struct synth_operations awe_operations = -{ - owner: THIS_MODULE, - id: "EMU8K", - info: &awe_info, - midi_dev: 0, - synth_type: SYNTH_TYPE_SAMPLE, - synth_subtype: SAMPLE_TYPE_AWE32, - open: awe_open, - close: awe_close, - ioctl: awe_ioctl, - kill_note: awe_kill_note, - start_note: awe_start_note, - set_instr: awe_set_instr_2, - reset: awe_reset, - hw_control: awe_hw_control, - load_patch: awe_load_patch, - aftertouch: awe_aftertouch, - controller: awe_controller, - panning: awe_panning, - volume_method: awe_volume_method, - bender: awe_bender, - alloc_voice: awe_alloc, - setup_voice: awe_setup_voice -}; - - -/* - * General attach / unload interface - */ - -static int __init _attach_awe(void) -{ - if (awe_present) return 0; /* for OSS38.. called twice? */ - - /* check presence of AWE32 card */ - if (! awe_detect()) { - printk(KERN_ERR "AWE32: not detected\n"); - return 0; - } - - /* check AWE32 ports are available */ - if (awe_check_port()) { - printk(KERN_ERR "AWE32: I/O area already used.\n"); - return 0; - } - - /* set buffers to NULL */ - sfhead = sftail = NULL; - - my_dev = sound_alloc_synthdev(); - if (my_dev == -1) { - printk(KERN_ERR "AWE32 Error: too many synthesizers\n"); - return 0; - } - - voice_alloc = &awe_operations.alloc; - voice_alloc->max_voice = awe_max_voices; - synth_devs[my_dev] = &awe_operations; - -#ifdef CONFIG_AWE32_MIXER - attach_mixer(); -#endif -#ifdef CONFIG_AWE32_MIDIEMU - attach_midiemu(); -#endif - - /* reserve I/O ports for awedrv */ - awe_request_region(); - - /* clear all samples */ - awe_reset_samples(); - - /* intialize AWE32 hardware */ - awe_initialize(); - - sprintf(awe_info.name, "AWE32-%s (RAM%dk)", - AWEDRV_VERSION, memsize/1024); - printk(KERN_INFO "\n", memsize/1024); - - awe_present = TRUE; - - return 1; -} - - -static void free_tables(void) -{ - if (sftail) { - sf_list *p, *prev; - for (p = sftail; p; p = prev) { - prev = p->prev; - awe_free_sf(p); - } - } - sfhead = sftail = NULL; -} - - -static void __exit _unload_awe(void) -{ - if (awe_present) { - awe_reset_samples(); - awe_release_region(); - free_tables(); -#ifdef CONFIG_AWE32_MIXER - unload_mixer(); -#endif -#ifdef CONFIG_AWE32_MIDIEMU - unload_midiemu(); -#endif - sound_unload_synthdev(my_dev); - awe_present = FALSE; - } -} - - -/* - * clear sample tables - */ - -static void -awe_reset_samples(void) -{ - /* free all bank tables */ - memset(preset_table, 0, sizeof(preset_table)); - free_tables(); - - current_sf_id = 0; - locked_sf_id = 0; - patch_opened = 0; -} - - -/* - * EMU register access - */ - -/* select a given AWE32 pointer */ -static int awe_ports[5]; -static int port_setuped = FALSE; -static int awe_cur_cmd = -1; -#define awe_set_cmd(cmd) \ -if (awe_cur_cmd != cmd) { outw(cmd, awe_ports[Pointer]); awe_cur_cmd = cmd; } - -/* store values to i/o port array */ -static void setup_ports(int port1, int port2, int port3) -{ - awe_ports[0] = port1; - if (port2 == 0) - port2 = port1 + 0x400; - awe_ports[1] = port2; - awe_ports[2] = port2 + 2; - if (port3 == 0) - port3 = port1 + 0x800; - awe_ports[3] = port3; - awe_ports[4] = port3 + 2; - - port_setuped = TRUE; -} - -/* write 16bit data */ -static void -awe_poke(unsigned short cmd, unsigned short port, unsigned short data) -{ - awe_set_cmd(cmd); - outw(data, awe_ports[port]); -} - -/* write 32bit data */ -static void -awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data) -{ - unsigned short addr = awe_ports[port]; - awe_set_cmd(cmd); - outw(data, addr); /* write lower 16 bits */ - outw(data >> 16, addr + 2); /* write higher 16 bits */ -} - -/* read 16bit data */ -static unsigned short -awe_peek(unsigned short cmd, unsigned short port) -{ - unsigned short k; - awe_set_cmd(cmd); - k = inw(awe_ports[port]); - return k; -} - -/* read 32bit data */ -static unsigned int -awe_peek_dw(unsigned short cmd, unsigned short port) -{ - unsigned int k1, k2; - unsigned short addr = awe_ports[port]; - awe_set_cmd(cmd); - k1 = inw(addr); - k2 = inw(addr + 2); - k1 |= k2 << 16; - return k1; -} - -/* wait delay number of AWE32 44100Hz clocks */ -#ifdef WAIT_BY_LOOP /* wait by loop -- that's not good.. */ -static void -awe_wait(unsigned short delay) -{ - unsigned short clock, target; - unsigned short port = awe_ports[AWE_WC_Port]; - int counter; - - /* sample counter */ - awe_set_cmd(AWE_WC_Cmd); - clock = (unsigned short)inw(port); - target = clock + delay; - counter = 0; - if (target < clock) { - for (; (unsigned short)inw(port) > target; counter++) - if (counter > 65536) - break; - } - for (; (unsigned short)inw(port) < target; counter++) - if (counter > 65536) - break; -} -#else - -static void awe_wait(unsigned short delay) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout((HZ*(unsigned long)delay + 44099)/44100); -} -/* -static void awe_wait(unsigned short delay) -{ - udelay(((unsigned long)delay * 1000000L + 44099) / 44100); -} -*/ -#endif /* wait by loop */ - -/* write a word data */ -#define awe_write_dram(c) awe_poke(AWE_SMLD, c) - - -/* - * port check / request - * 0x620-623, 0xA20-A23, 0xE20-E23 - */ - -static int __init -awe_check_port(void) -{ - if (! port_setuped) return 0; - return (check_region(awe_ports[0], 4) || - check_region(awe_ports[1], 4) || - check_region(awe_ports[3], 4)); -} - -static void __init -awe_request_region(void) -{ - if (! port_setuped) return; - request_region(awe_ports[0], 4, "sound driver (AWE32)"); - request_region(awe_ports[1], 4, "sound driver (AWE32)"); - request_region(awe_ports[3], 4, "sound driver (AWE32)"); -} - -static void __exit -awe_release_region(void) -{ - if (! port_setuped) return; - release_region(awe_ports[0], 4); - release_region(awe_ports[1], 4); - release_region(awe_ports[3], 4); -} - - -/* - * initialization of AWE driver - */ - -static void -awe_initialize(void) -{ - DEBUG(0,printk("AWE32: initializing..\n")); - - /* initialize hardware configuration */ - awe_poke(AWE_HWCF1, 0x0059); - awe_poke(AWE_HWCF2, 0x0020); - - /* disable audio; this seems to reduce a clicking noise a bit.. */ - awe_poke(AWE_HWCF3, 0); - - /* initialize audio channels */ - awe_init_audio(); - - /* initialize DMA */ - awe_init_dma(); - - /* initialize init array */ - awe_init_array(); - - /* check DRAM memory size */ - awe_check_dram(); - - /* initialize the FM section of the AWE32 */ - awe_init_fm(); - - /* set up voice envelopes */ - awe_tweak(); - - /* enable audio */ - awe_poke(AWE_HWCF3, 0x0004); - - /* set default values */ - awe_init_ctrl_parms(TRUE); - - /* set equalizer */ - awe_update_equalizer(); - - /* set reverb & chorus modes */ - awe_update_reverb_mode(); - awe_update_chorus_mode(); -} - - -/* - * AWE32 voice parameters - */ - -/* initialize voice_info record */ -static void -awe_init_voice_info(awe_voice_info *vp) -{ - vp->sample = 0; - vp->rate_offset = 0; - - vp->start = 0; - vp->end = 0; - vp->loopstart = 0; - vp->loopend = 0; - vp->mode = 0; - vp->root = 60; - vp->tune = 0; - vp->low = 0; - vp->high = 127; - vp->vellow = 0; - vp->velhigh = 127; - - vp->fixkey = -1; - vp->fixvel = -1; - vp->fixpan = -1; - vp->pan = -1; - - vp->exclusiveClass = 0; - vp->amplitude = 127; - vp->attenuation = 0; - vp->scaleTuning = 100; - - awe_init_voice_parm(&vp->parm); -} - -/* initialize voice_parm record: - * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. - * Vibrato and Tremolo effects are zero. - * Cutoff is maximum. - * Chorus and Reverb effects are zero. - */ -static void -awe_init_voice_parm(awe_voice_parm *pp) -{ - pp->moddelay = 0x8000; - pp->modatkhld = 0x7f7f; - pp->moddcysus = 0x7f7f; - pp->modrelease = 0x807f; - pp->modkeyhold = 0; - pp->modkeydecay = 0; - - pp->voldelay = 0x8000; - pp->volatkhld = 0x7f7f; - pp->voldcysus = 0x7f7f; - pp->volrelease = 0x807f; - pp->volkeyhold = 0; - pp->volkeydecay = 0; - - pp->lfo1delay = 0x8000; - pp->lfo2delay = 0x8000; - pp->pefe = 0; - - pp->fmmod = 0; - pp->tremfrq = 0; - pp->fm2frq2 = 0; - - pp->cutoff = 0xff; - pp->filterQ = 0; - - pp->chorus = 0; - pp->reverb = 0; -} - - -#ifdef AWE_HAS_GUS_COMPATIBILITY - -/* convert frequency mHz to abstract cents (= midi key * 100) */ -static int -freq_to_note(int mHz) -{ - /* abscents = log(mHz/8176) / log(2) * 1200 */ - unsigned int max_val = (unsigned int)0xffffffff / 10000; - int i, times; - unsigned int base; - unsigned int freq; - int note, tune; - - if (mHz == 0) - return 0; - if (mHz < 0) - return 12799; /* maximum */ - - freq = mHz; - note = 0; - for (base = 8176 * 2; freq >= base; base *= 2) { - note += 12; - if (note >= 128) /* over maximum */ - return 12799; - } - base /= 2; - - /* to avoid overflow... */ - times = 10000; - while (freq > max_val) { - max_val *= 10; - times /= 10; - base /= 10; - } - - freq = freq * times / base; - for (i = 0; i < 12; i++) { - if (freq < semitone_tuning[i+1]) - break; - note++; - } - - tune = 0; - freq = freq * 10000 / semitone_tuning[i]; - for (i = 0; i < 100; i++) { - if (freq < cent_tuning[i+1]) - break; - tune++; - } - - return note * 100 + tune; -} - - -/* convert Hz to AWE32 rate offset: - * sample pitch offset for the specified sample rate - * rate=44100 is no offset, each 4096 is 1 octave (twice). - * eg, when rate is 22050, this offset becomes -4096. - */ -static int -calc_rate_offset(int Hz) -{ - /* offset = log(Hz / 44100) / log(2) * 4096 */ - int freq, base, i; - - /* maybe smaller than max (44100Hz) */ - if (Hz <= 0 || Hz >= 44100) return 0; - - base = 0; - for (freq = Hz * 2; freq < 44100; freq *= 2) - base++; - base *= 1200; - - freq = 44100 * 10000 / (freq/2); - for (i = 0; i < 12; i++) { - if (freq < semitone_tuning[i+1]) - break; - base += 100; - } - freq = freq * 10000 / semitone_tuning[i]; - for (i = 0; i < 100; i++) { - if (freq < cent_tuning[i+1]) - break; - base++; - } - return -base * 4096 / 1200; -} - - -/* - * convert envelope time parameter to AWE32 raw parameter - */ - -/* attack & decay/release time table (msec) */ -static short attack_time_tbl[128] = { -32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816, -707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, -361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, -180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, -90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, -45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, -22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, -11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0, -}; - -static short decay_time_tbl[128] = { -32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082, -2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507, -1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722, -691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361, -345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180, -172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90, -86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45, -43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22, -}; - -#define calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725); - -/* delay time = 0x8000 - msec/92 */ -static int -calc_parm_hold(int msec) -{ - int val = (0x7f * 92 - msec) / 92; - if (val < 1) val = 1; - if (val > 127) val = 127; - return val; -} - -/* attack time: search from time table */ -static int -calc_parm_attack(int msec) -{ - return calc_parm_search(msec, attack_time_tbl); -} - -/* decay/release time: search from time table */ -static int -calc_parm_decay(int msec) -{ - return calc_parm_search(msec, decay_time_tbl); -} - -/* search an index for specified time from given time table */ -static int -calc_parm_search(int msec, short *table) -{ - int left = 1, right = 127, mid; - while (left < right) { - mid = (left + right) / 2; - if (msec < (int)table[mid]) - left = mid + 1; - else - right = mid; - } - return left; -} -#endif /* AWE_HAS_GUS_COMPATIBILITY */ - - -/* - * effects table - */ - -/* set an effect value */ -#define FX_FLAG_OFF 0 -#define FX_FLAG_SET 1 -#define FX_FLAG_ADD 2 - -#define FX_SET(rec,type,value) \ - ((rec)->flags[type] = FX_FLAG_SET, (rec)->val[type] = (value)) -#define FX_ADD(rec,type,value) \ - ((rec)->flags[type] = FX_FLAG_ADD, (rec)->val[type] = (value)) -#define FX_UNSET(rec,type) \ - ((rec)->flags[type] = FX_FLAG_OFF, (rec)->val[type] = 0) - -/* check the effect value is set */ -#define FX_ON(rec,type) ((rec)->flags[type]) - -#define PARM_BYTE 0 -#define PARM_WORD 1 -#define PARM_SIGN 2 - -static struct PARM_DEFS { - int type; /* byte or word */ - int low, high; /* value range */ - fx_affect_func realtime; /* realtime paramater change */ -} parm_defs[] = { - {PARM_WORD, 0, 0x8000, NULL}, /* env1 delay */ - {PARM_BYTE, 1, 0x7f, NULL}, /* env1 attack */ - {PARM_BYTE, 0, 0x7e, NULL}, /* env1 hold */ - {PARM_BYTE, 1, 0x7f, NULL}, /* env1 decay */ - {PARM_BYTE, 1, 0x7f, NULL}, /* env1 release */ - {PARM_BYTE, 0, 0x7f, NULL}, /* env1 sustain */ - {PARM_BYTE, 0, 0xff, NULL}, /* env1 pitch */ - {PARM_BYTE, 0, 0xff, NULL}, /* env1 cutoff */ - - {PARM_WORD, 0, 0x8000, NULL}, /* env2 delay */ - {PARM_BYTE, 1, 0x7f, NULL}, /* env2 attack */ - {PARM_BYTE, 0, 0x7e, NULL}, /* env2 hold */ - {PARM_BYTE, 1, 0x7f, NULL}, /* env2 decay */ - {PARM_BYTE, 1, 0x7f, NULL}, /* env2 release */ - {PARM_BYTE, 0, 0x7f, NULL}, /* env2 sustain */ - - {PARM_WORD, 0, 0x8000, NULL}, /* lfo1 delay */ - {PARM_BYTE, 0, 0xff, awe_fx_tremfrq}, /* lfo1 freq */ - {PARM_SIGN, -128, 127, awe_fx_tremfrq}, /* lfo1 volume */ - {PARM_SIGN, -128, 127, awe_fx_fmmod}, /* lfo1 pitch */ - {PARM_BYTE, 0, 0xff, awe_fx_fmmod}, /* lfo1 cutoff */ - - {PARM_WORD, 0, 0x8000, NULL}, /* lfo2 delay */ - {PARM_BYTE, 0, 0xff, awe_fx_fm2frq2}, /* lfo2 freq */ - {PARM_SIGN, -128, 127, awe_fx_fm2frq2}, /* lfo2 pitch */ - - {PARM_WORD, 0, 0xffff, awe_set_voice_pitch}, /* initial pitch */ - {PARM_BYTE, 0, 0xff, NULL}, /* chorus */ - {PARM_BYTE, 0, 0xff, NULL}, /* reverb */ - {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial cutoff */ - {PARM_BYTE, 0, 15, awe_fx_filterQ}, /* initial resonance */ - - {PARM_WORD, 0, 0xffff, NULL}, /* sample start */ - {PARM_WORD, 0, 0xffff, NULL}, /* loop start */ - {PARM_WORD, 0, 0xffff, NULL}, /* loop end */ - {PARM_WORD, 0, 0xffff, NULL}, /* coarse sample start */ - {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop start */ - {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop end */ - {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial attenuation */ -}; - - -static unsigned char -FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value) -{ - int effect = 0; - int on = 0; - if (lay && (on = FX_ON(lay, type)) != 0) - effect = lay->val[type]; - if (!on && (on = FX_ON(rec, type)) != 0) - effect = rec->val[type]; - if (on == FX_FLAG_ADD) { - if (parm_defs[type].type == PARM_SIGN) { - if (value > 0x7f) - effect += (int)value - 0x100; - else - effect += (int)value; - } else { - effect += (int)value; - } - } - if (on) { - if (effect < parm_defs[type].low) - effect = parm_defs[type].low; - else if (effect > parm_defs[type].high) - effect = parm_defs[type].high; - return (unsigned char)effect; - } - return value; -} - -/* get word effect value */ -static unsigned short -FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value) -{ - int effect = 0; - int on = 0; - if (lay && (on = FX_ON(lay, type)) != 0) - effect = lay->val[type]; - if (!on && (on = FX_ON(rec, type)) != 0) - effect = rec->val[type]; - if (on == FX_FLAG_ADD) - effect += (int)value; - if (on) { - if (effect < parm_defs[type].low) - effect = parm_defs[type].low; - else if (effect > parm_defs[type].high) - effect = parm_defs[type].high; - return (unsigned short)effect; - } - return value; -} - -/* get word (upper=type1/lower=type2) effect value */ -static unsigned short -FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value) -{ - unsigned short tmp; - tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8)); - tmp <<= 8; - tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff)); - return tmp; -} - -/* address offset */ -static int -FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode) -{ - int addr = 0; - if (lay && FX_ON(lay, hi)) - addr = (short)lay->val[hi]; - else if (FX_ON(rec, hi)) - addr = (short)rec->val[hi]; - addr = addr << 15; - if (lay && FX_ON(lay, lo)) - addr += (short)lay->val[lo]; - else if (FX_ON(rec, lo)) - addr += (short)rec->val[lo]; - if (!(mode & AWE_SAMPLE_8BITS)) - addr /= 2; - return addr; -} - - -/* - * turn on/off sample - */ - -/* table for volume target calculation */ -static unsigned short voltarget[16] = { - 0xEAC0, 0XE0C8, 0XD740, 0XCE20, 0XC560, 0XBD08, 0XB500, 0XAD58, - 0XA5F8, 0X9EF0, 0X9830, 0X91C0, 0X8B90, 0X85A8, 0X8000, 0X7A90 -}; - -static void -awe_note_on(int voice) -{ - unsigned int temp; - int addr; - int vtarget, ftarget, ptarget, pitch; - awe_voice_info *vp; - awe_voice_parm_block *parm; - FX_Rec *fx = &voices[voice].cinfo->fx; - FX_Rec *fx_lay = NULL; - if (voices[voice].layer < MAX_LAYERS) - fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; - - /* A voice sample must assigned before calling */ - if ((vp = voices[voice].sample) == NULL || vp->index == 0) - return; - - parm = (awe_voice_parm_block*)&vp->parm; - - /* channel to be silent and idle */ - awe_poke(AWE_DCYSUSV(voice), 0x0080); - awe_poke(AWE_VTFT(voice), 0x0000FFFF); - awe_poke(AWE_CVCF(voice), 0x0000FFFF); - awe_poke(AWE_PTRX(voice), 0); - awe_poke(AWE_CPF(voice), 0); - - /* set pitch offset */ - awe_set_pitch(voice, TRUE); - - /* modulation & volume envelope */ - if (parm->modatk >= 0x80 && parm->moddelay >= 0x8000) { - awe_poke(AWE_ENVVAL(voice), 0xBFFF); - pitch = (parm->env1pit<<4) + voices[voice].apitch; - if (pitch > 0xffff) pitch = 0xffff; - /* calculate filter target */ - ftarget = parm->cutoff + parm->env1fc; - limitvalue(ftarget, 0, 255); - ftarget <<= 8; - } else { - awe_poke(AWE_ENVVAL(voice), - FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, parm->moddelay)); - ftarget = parm->cutoff; - ftarget <<= 8; - pitch = voices[voice].apitch; - } - - /* calcualte pitch target */ - if (pitch != 0xffff) { - ptarget = 1 << (pitch >> 12); - if (pitch & 0x800) ptarget += (ptarget*0x102e)/0x2710; - if (pitch & 0x400) ptarget += (ptarget*0x764)/0x2710; - if (pitch & 0x200) ptarget += (ptarget*0x389)/0x2710; - ptarget += (ptarget>>1); - if (ptarget > 0xffff) ptarget = 0xffff; - - } else ptarget = 0xffff; - if (parm->modatk >= 0x80) - awe_poke(AWE_ATKHLD(voice), - FX_BYTE(fx, fx_lay, AWE_FX_ENV1_HOLD, parm->modhld) << 8 | 0x7f); - else - awe_poke(AWE_ATKHLD(voice), - FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK, - vp->parm.modatkhld)); - awe_poke(AWE_DCYSUS(voice), - FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY, - vp->parm.moddcysus)); - - if (parm->volatk >= 0x80 && parm->voldelay >= 0x8000) { - awe_poke(AWE_ENVVOL(voice), 0xBFFF); - vtarget = voltarget[voices[voice].avol%0x10]>>(voices[voice].avol>>4); - } else { - awe_poke(AWE_ENVVOL(voice), - FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay)); - vtarget = 0; - } - if (parm->volatk >= 0x80) - awe_poke(AWE_ATKHLDV(voice), - FX_BYTE(fx, fx_lay, AWE_FX_ENV2_HOLD, parm->volhld) << 8 | 0x7f); - else - awe_poke(AWE_ATKHLDV(voice), - FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK, - vp->parm.volatkhld)); - /* decay/sustain parameter for volume envelope must be set at last */ - - /* cutoff and volume */ - awe_set_volume(voice, TRUE); - - /* modulation envelope heights */ - awe_poke(AWE_PEFE(voice), - FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF, - vp->parm.pefe)); - - /* lfo1/2 delay */ - awe_poke(AWE_LFO1VAL(voice), - FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay)); - awe_poke(AWE_LFO2VAL(voice), - FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay)); - - /* lfo1 pitch & cutoff shift */ - awe_fx_fmmod(voice, TRUE); - /* lfo1 volume & freq */ - awe_fx_tremfrq(voice, TRUE); - /* lfo2 pitch & freq */ - awe_fx_fm2frq2(voice, TRUE); - /* pan & loop start */ - awe_set_pan(voice, TRUE); - - /* chorus & loop end (chorus 8bit, MSB) */ - addr = vp->loopend - 1; - addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END, - AWE_FX_COARSE_LOOP_END, vp->mode); - temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus); - temp = (temp <<24) | (unsigned int)addr; - awe_poke_dw(AWE_CSL(voice), temp); - DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr)); - - /* Q & current address (Q 4bit value, MSB) */ - addr = vp->start - 1; - addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START, - AWE_FX_COARSE_SAMPLE_START, vp->mode); - temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ); - temp = (temp<<28) | (unsigned int)addr; - awe_poke_dw(AWE_CCCA(voice), temp); - DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr)); - - /* clear unknown registers */ - awe_poke_dw(AWE_00A0(voice), 0); - awe_poke_dw(AWE_0080(voice), 0); - - /* reset volume */ - awe_poke_dw(AWE_VTFT(voice), (vtarget<<16)|ftarget); - awe_poke_dw(AWE_CVCF(voice), (vtarget<<16)|ftarget); - - /* set reverb */ - temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb); - temp = (temp << 8) | (ptarget << 16) | voices[voice].aaux; - awe_poke_dw(AWE_PTRX(voice), temp); - awe_poke_dw(AWE_CPF(voice), ptarget << 16); - /* turn on envelope */ - awe_poke(AWE_DCYSUSV(voice), - FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY, - vp->parm.voldcysus)); - - voices[voice].state = AWE_ST_ON; - - /* clear voice position for the next note on this channel */ - if (SINGLE_LAYER_MODE()) { - FX_UNSET(fx, AWE_FX_SAMPLE_START); - FX_UNSET(fx, AWE_FX_COARSE_SAMPLE_START); - } -} - - -/* turn off the voice */ -static void -awe_note_off(int voice) -{ - awe_voice_info *vp; - unsigned short tmp; - FX_Rec *fx = &voices[voice].cinfo->fx; - FX_Rec *fx_lay = NULL; - if (voices[voice].layer < MAX_LAYERS) - fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; - - if ((vp = voices[voice].sample) == NULL) { - voices[voice].state = AWE_ST_OFF; - return; - } - - tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE, - (unsigned char)vp->parm.modrelease); - awe_poke(AWE_DCYSUS(voice), tmp); - tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE, - (unsigned char)vp->parm.volrelease); - awe_poke(AWE_DCYSUSV(voice), tmp); - voices[voice].state = AWE_ST_RELEASED; -} - -/* force to terminate the voice (no releasing echo) */ -static void -awe_terminate(int voice) -{ - awe_poke(AWE_DCYSUSV(voice), 0x807F); - awe_tweak_voice(voice); - voices[voice].state = AWE_ST_OFF; -} - -/* turn off other voices with the same exclusive class (for drums) */ -static void -awe_exclusive_off(int voice) -{ - int i, exclass; - - if (voices[voice].sample == NULL) - return; - if ((exclass = voices[voice].sample->exclusiveClass) == 0) - return; /* not exclusive */ - - /* turn off voices with the same class */ - for (i = 0; i < awe_max_voices; i++) { - if (i != voice && IS_PLAYING(i) && - voices[i].sample && voices[i].ch == voices[voice].ch && - voices[i].sample->exclusiveClass == exclass) { - DEBUG(4,printk("AWE32: [exoff(%d)]\n", i)); - awe_terminate(i); - awe_voice_init(i, TRUE); - } - } -} - - -/* - * change the parameters of an audible voice - */ - -/* change pitch */ -static void -awe_set_pitch(int voice, int forced) -{ - if (IS_NO_EFFECT(voice) && !forced) return; - awe_poke(AWE_IP(voice), voices[voice].apitch); - DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch)); -} - -/* calculate & change pitch */ -static void -awe_set_voice_pitch(int voice, int forced) -{ - awe_calc_pitch(voice); - awe_set_pitch(voice, forced); -} - -/* change volume & cutoff */ -static void -awe_set_volume(int voice, int forced) -{ - awe_voice_info *vp; - unsigned short tmp2; - FX_Rec *fx = &voices[voice].cinfo->fx; - FX_Rec *fx_lay = NULL; - if (voices[voice].layer < MAX_LAYERS) - fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; - - if (!IS_PLAYING(voice) && !forced) return; - if ((vp = voices[voice].sample) == NULL || vp->index == 0) - return; - - tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF, - (unsigned char)voices[voice].acutoff); - tmp2 = (tmp2 << 8); - tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN, - (unsigned char)voices[voice].avol); - awe_poke(AWE_IFATN(voice), tmp2); -} - -/* calculate & change volume */ -static void -awe_set_voice_vol(int voice, int forced) -{ - if (IS_EMPTY(voice)) - return; - awe_calc_volume(voice); - awe_set_volume(voice, forced); -} - - -/* change pan; this could make a click noise.. */ -static void -awe_set_pan(int voice, int forced) -{ - unsigned int temp; - int addr; - awe_voice_info *vp; - FX_Rec *fx = &voices[voice].cinfo->fx; - FX_Rec *fx_lay = NULL; - if (voices[voice].layer < MAX_LAYERS) - fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; - - if (IS_NO_EFFECT(voice) && !forced) return; - if ((vp = voices[voice].sample) == NULL || vp->index == 0) - return; - - /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */ - if (vp->fixpan > 0) /* 0-127 */ - temp = 255 - (int)vp->fixpan * 2; - else { - int pos = 0; - if (vp->pan >= 0) /* 0-127 */ - pos = (int)vp->pan * 2 - 128; - pos += voices[voice].cinfo->panning; /* -128 - 127 */ - temp = 127 - pos; - } - limitvalue(temp, 0, 255); - if (ctrls[AWE_MD_PAN_EXCHANGE]) { - temp = 255 - temp; - } - if (forced || temp != voices[voice].apan) { - voices[voice].apan = temp; - if (temp == 0) - voices[voice].aaux = 0xff; - else - voices[voice].aaux = (-temp) & 0xff; - addr = vp->loopstart - 1; - addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START, - AWE_FX_COARSE_LOOP_START, vp->mode); - temp = (temp<<24) | (unsigned int)addr; - awe_poke_dw(AWE_PSST(voice), temp); - DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr)); - } -} - -/* effects change during playing */ -static void -awe_fx_fmmod(int voice, int forced) -{ - awe_voice_info *vp; - FX_Rec *fx = &voices[voice].cinfo->fx; - FX_Rec *fx_lay = NULL; - if (voices[voice].layer < MAX_LAYERS) - fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; - - if (IS_NO_EFFECT(voice) && !forced) return; - if ((vp = voices[voice].sample) == NULL || vp->index == 0) - return; - awe_poke(AWE_FMMOD(voice), - FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF, - vp->parm.fmmod)); -} - -/* set tremolo (lfo1) volume & frequency */ -static void -awe_fx_tremfrq(int voice, int forced) -{ - awe_voice_info *vp; - FX_Rec *fx = &voices[voice].cinfo->fx; - FX_Rec *fx_lay = NULL; - if (voices[voice].layer < MAX_LAYERS) - fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; - - if (IS_NO_EFFECT(voice) && !forced) return; - if ((vp = voices[voice].sample) == NULL || vp->index == 0) - return; - awe_poke(AWE_TREMFRQ(voice), - FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ, - vp->parm.tremfrq)); -} - -/* set lfo2 pitch & frequency */ -static void -awe_fx_fm2frq2(int voice, int forced) -{ - awe_voice_info *vp; - FX_Rec *fx = &voices[voice].cinfo->fx; - FX_Rec *fx_lay = NULL; - if (voices[voice].layer < MAX_LAYERS) - fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; - - if (IS_NO_EFFECT(voice) && !forced) return; - if ((vp = voices[voice].sample) == NULL || vp->index == 0) - return; - awe_poke(AWE_FM2FRQ2(voice), - FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ, - vp->parm.fm2frq2)); -} - - -/* Q & current address (Q 4bit value, MSB) */ -static void -awe_fx_filterQ(int voice, int forced) -{ - unsigned int addr; - awe_voice_info *vp; - FX_Rec *fx = &voices[voice].cinfo->fx; - FX_Rec *fx_lay = NULL; - if (voices[voice].layer < MAX_LAYERS) - fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; - - if (IS_NO_EFFECT(voice) && !forced) return; - if ((vp = voices[voice].sample) == NULL || vp->index == 0) - return; - - addr = awe_peek_dw(AWE_CCCA(voice)) & 0xffffff; - addr |= (FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ) << 28); - awe_poke_dw(AWE_CCCA(voice), addr); -} - -/* - * calculate pitch offset - * - * 0xE000 is no pitch offset at 44100Hz sample. - * Every 4096 is one octave. - */ - -static void -awe_calc_pitch(int voice) -{ - voice_info *vp = &voices[voice]; - awe_voice_info *ap; - awe_chan_info *cp = voices[voice].cinfo; - int offset; - - /* search voice information */ - if ((ap = vp->sample) == NULL) - return; - if (ap->index == 0) { - DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); - if (awe_set_sample((awe_voice_list*)ap) == 0) - return; - } - - /* calculate offset */ - if (ap->fixkey >= 0) { - DEBUG(3,printk("AWE32: p-> fixkey(%d) tune(%d)\n", ap->fixkey, ap->tune)); - offset = (ap->fixkey - ap->root) * 4096 / 12; - } else { - DEBUG(3,printk("AWE32: p(%d)-> root(%d) tune(%d)\n", vp->note, ap->root, ap->tune)); - offset = (vp->note - ap->root) * 4096 / 12; - DEBUG(4,printk("AWE32: p-> ofs=%d\n", offset)); - } - offset = (offset * ap->scaleTuning) / 100; - DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset)); - offset += ap->tune * 4096 / 1200; - DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset)); - if (cp->bender != 0) { - DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender)); - /* (819200: 1 semitone) ==> (4096: 12 semitones) */ - offset += cp->bender * cp->bender_range / 2400; - } - - /* add initial pitch correction */ - if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH)) - offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH]; - else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) - offset += cp->fx.val[AWE_FX_INIT_PITCH]; - - /* 0xe000: root pitch */ - vp->apitch = 0xe000 + ap->rate_offset + offset; - DEBUG(4,printk("AWE32: p-> sum aofs=%x, rate_ofs=%d\n", vp->apitch, ap->rate_offset)); - if (vp->apitch > 0xffff) - vp->apitch = 0xffff; - if (vp->apitch < 0) - vp->apitch = 0; -} - - -#ifdef AWE_HAS_GUS_COMPATIBILITY -/* calculate MIDI key and semitone from the specified frequency */ -static void -awe_calc_pitch_from_freq(int voice, int freq) -{ - voice_info *vp = &voices[voice]; - awe_voice_info *ap; - FX_Rec *fx = &voices[voice].cinfo->fx; - FX_Rec *fx_lay = NULL; - int offset; - int note; - - if (voices[voice].layer < MAX_LAYERS) - fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; - - /* search voice information */ - if ((ap = vp->sample) == NULL) - return; - if (ap->index == 0) { - DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); - if (awe_set_sample((awe_voice_list*)ap) == 0) - return; - } - note = freq_to_note(freq); - offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200; - offset = (offset * ap->scaleTuning) / 100; - if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH)) - offset += fx_lay->val[AWE_FX_INIT_PITCH]; - else if (FX_ON(fx, AWE_FX_INIT_PITCH)) - offset += fx->val[AWE_FX_INIT_PITCH]; - vp->apitch = 0xe000 + ap->rate_offset + offset; - if (vp->apitch > 0xffff) - vp->apitch = 0xffff; - if (vp->apitch < 0) - vp->apitch = 0; -} -#endif /* AWE_HAS_GUS_COMPATIBILITY */ - - -/* - * calculate volume attenuation - * - * Voice volume is controlled by volume attenuation parameter. - * So volume becomes maximum when avol is 0 (no attenuation), and - * minimum when 255 (-96dB or silence). - */ - -static int vol_table[128] = { - 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, - 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, - 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, - 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, - 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, - 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, - 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, - 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, -}; - -/* tables for volume->attenuation calculation */ -static unsigned char voltab1[128] = { - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, - 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, - 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, - 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, - 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, - 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static unsigned char voltab2[128] = { - 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a, - 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21, - 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, - 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15, - 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, - 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, - 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, - 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static unsigned char expressiontab[128] = { - 0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42, - 0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30, - 0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, - 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e, - 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, - 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, - 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, - 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, - 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, - 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, - 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static void -awe_calc_volume(int voice) -{ - voice_info *vp = &voices[voice]; - awe_voice_info *ap; - awe_chan_info *cp = voices[voice].cinfo; - int vol; - - /* search voice information */ - if ((ap = vp->sample) == NULL) - return; - - ap = vp->sample; - if (ap->index == 0) { - DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); - if (awe_set_sample((awe_voice_list*)ap) == 0) - return; - } - - if (ctrls[AWE_MD_NEW_VOLUME_CALC]) { - int main_vol = cp->main_vol * ap->amplitude / 127; - limitvalue(vp->velocity, 0, 127); - limitvalue(main_vol, 0, 127); - limitvalue(cp->expression_vol, 0, 127); - - vol = voltab1[main_vol] + voltab2[vp->velocity]; - vol = (vol * 8) / 3; - vol += ap->attenuation; - if (cp->expression_vol < 127) - vol += ((0x100 - vol) * expressiontab[cp->expression_vol])/128; - vol += atten_offset; - if (atten_relative) - vol += ctrls[AWE_MD_ZERO_ATTEN]; - limitvalue(vol, 0, 255); - vp->avol = vol; - - } else { - /* 0 - 127 */ - vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127); - vol = vol * ap->amplitude / 127; - - if (vol < 0) vol = 0; - if (vol > 127) vol = 127; - - /* calc to attenuation */ - vol = vol_table[vol]; - vol += (int)ap->attenuation; - vol += atten_offset; - if (atten_relative) - vol += ctrls[AWE_MD_ZERO_ATTEN]; - if (vol > 255) vol = 255; - - vp->avol = vol; - } - if (cp->bank != AWE_DRUM_BANK && ((awe_voice_parm_block*)(&ap->parm))->volatk < 0x7d) { - int atten; - if (vp->velocity < 70) atten = 70; - else atten = vp->velocity; - vp->acutoff = (atten * ap->parm.cutoff + 0xa0) >> 7; - } else { - vp->acutoff = ap->parm.cutoff; - } - DEBUG(3,printk("AWE32: [-- voice(%d) vol=%x]\n", voice, vol)); -} - -/* change master volume */ -static void -awe_change_master_volume(short val) -{ - limitvalue(val, 0, 127); - atten_offset = vol_table[val]; - atten_relative = TRUE; - awe_update_volume(); -} - -/* update volumes of all available channels */ -static void awe_update_volume(void) -{ - int i; - for (i = 0; i < awe_max_voices; i++) - awe_set_voice_vol(i, TRUE); -} - -/* set sostenuto on */ -static void awe_sostenuto_on(int voice, int forced) -{ - if (IS_NO_EFFECT(voice) && !forced) return; - voices[voice].sostenuto = 127; -} - - -/* drop sustain */ -static void awe_sustain_off(int voice, int forced) -{ - if (voices[voice].state == AWE_ST_SUSTAINED) { - awe_note_off(voice); - awe_fx_init(voices[voice].ch); - awe_voice_init(voice, FALSE); - } -} - - -/* terminate and initialize voice */ -static void awe_terminate_and_init(int voice, int forced) -{ - awe_terminate(voice); - awe_fx_init(voices[voice].ch); - awe_voice_init(voice, TRUE); -} - - -/* - * synth operation routines - */ - -#define AWE_VOICE_KEY(v) (0x8000 | (v)) -#define AWE_CHAN_KEY(c,n) (((c) << 8) | ((n) + 1)) -#define KEY_CHAN_MATCH(key,c) (((key) >> 8) == (c)) - -/* initialize the voice */ -static void -awe_voice_init(int voice, int init_all) -{ - voice_info *vp = &voices[voice]; - - /* reset voice search key */ - if (playing_mode == AWE_PLAY_DIRECT) - vp->key = AWE_VOICE_KEY(voice); - else - vp->key = 0; - - /* clear voice mapping */ - voice_alloc->map[voice] = 0; - - /* touch the timing flag */ - vp->time = current_alloc_time; - - /* initialize other parameters if necessary */ - if (init_all) { - vp->note = -1; - vp->velocity = 0; - vp->sostenuto = 0; - - vp->sample = NULL; - vp->cinfo = &channels[voice]; - vp->ch = voice; - vp->state = AWE_ST_OFF; - - /* emu8000 parameters */ - vp->apitch = 0; - vp->avol = 255; - vp->apan = -1; - } -} - -/* clear effects */ -static void awe_fx_init(int ch) -{ - if (SINGLE_LAYER_MODE() && !ctrls[AWE_MD_KEEP_EFFECT]) { - memset(&channels[ch].fx, 0, sizeof(channels[ch].fx)); - memset(&channels[ch].fx_layer, 0, sizeof(&channels[ch].fx_layer)); - } -} - -/* initialize channel info */ -static void awe_channel_init(int ch, int init_all) -{ - awe_chan_info *cp = &channels[ch]; - cp->channel = ch; - if (init_all) { - cp->panning = 0; /* zero center */ - cp->bender_range = 200; /* sense * 100 */ - cp->main_vol = 127; - if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) { - cp->instr = ctrls[AWE_MD_DEF_DRUM]; - cp->bank = AWE_DRUM_BANK; - } else { - cp->instr = ctrls[AWE_MD_DEF_PRESET]; - cp->bank = ctrls[AWE_MD_DEF_BANK]; - } - } - - cp->bender = 0; /* zero tune skew */ - cp->expression_vol = 127; - cp->chan_press = 0; - cp->sustained = 0; - - if (! ctrls[AWE_MD_KEEP_EFFECT]) { - memset(&cp->fx, 0, sizeof(cp->fx)); - memset(&cp->fx_layer, 0, sizeof(cp->fx_layer)); - } -} - - -/* change the voice parameters; voice = channel */ -static void awe_voice_change(int voice, fx_affect_func func) -{ - int i; - switch (playing_mode) { - case AWE_PLAY_DIRECT: - func(voice, FALSE); - break; - case AWE_PLAY_INDIRECT: - for (i = 0; i < awe_max_voices; i++) - if (voices[i].key == AWE_VOICE_KEY(voice)) - func(i, FALSE); - break; - default: - for (i = 0; i < awe_max_voices; i++) - if (KEY_CHAN_MATCH(voices[i].key, voice)) - func(i, FALSE); - break; - } -} - - -/* - * device open / close - */ - -/* open device: - * reset status of all voices, and clear sample position flag - */ -static int -awe_open(int dev, int mode) -{ - if (awe_busy) - return -EBUSY; - - awe_busy = TRUE; - - /* set default mode */ - awe_init_ctrl_parms(FALSE); - atten_relative = TRUE; - atten_offset = 0; - drum_flags = DEFAULT_DRUM_FLAGS; - playing_mode = AWE_PLAY_INDIRECT; - - /* reset voices & channels */ - awe_reset(dev); - - patch_opened = 0; - - return 0; -} - - -/* close device: - * reset all voices again (terminate sounds) - */ -static void -awe_close(int dev) -{ - awe_reset(dev); - awe_busy = FALSE; -} - - -/* set miscellaneous mode parameters - */ -static void -awe_init_ctrl_parms(int init_all) -{ - int i; - for (i = 0; i < AWE_MD_END; i++) { - if (init_all || ctrl_parms[i].init_each_time) - ctrls[i] = ctrl_parms[i].value; - } -} - - -/* sequencer I/O control: - */ -static int -awe_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - switch (cmd) { - case SNDCTL_SYNTH_INFO: - if (playing_mode == AWE_PLAY_DIRECT) - awe_info.nr_voices = awe_max_voices; - else - awe_info.nr_voices = AWE_MAX_CHANNELS; - memcpy((char*)arg, &awe_info, sizeof(awe_info)); - return 0; - break; - - case SNDCTL_SEQ_RESETSAMPLES: - awe_reset(dev); - awe_reset_samples(); - return 0; - break; - - case SNDCTL_SEQ_PERCMODE: - /* what's this? */ - return 0; - break; - - case SNDCTL_SYNTH_MEMAVL: - return memsize - awe_free_mem_ptr() * 2; - - default: - printk(KERN_WARNING "AWE32: unsupported ioctl %d\n", cmd); - return -EINVAL; - } -} - - -static int voice_in_range(int voice) -{ - if (playing_mode == AWE_PLAY_DIRECT) { - if (voice < 0 || voice >= awe_max_voices) - return FALSE; - } else { - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return FALSE; - } - return TRUE; -} - -static void release_voice(int voice, int do_sustain) -{ - if (IS_NO_SOUND(voice)) - return; - if (do_sustain && (voices[voice].cinfo->sustained == 127 || - voices[voice].sostenuto == 127)) - voices[voice].state = AWE_ST_SUSTAINED; - else { - awe_note_off(voice); - awe_fx_init(voices[voice].ch); - awe_voice_init(voice, FALSE); - } -} - -/* release all notes */ -static void awe_note_off_all(int do_sustain) -{ - int i; - for (i = 0; i < awe_max_voices; i++) - release_voice(i, do_sustain); -} - -/* kill a voice: - * not terminate, just release the voice. - */ -static int -awe_kill_note(int dev, int voice, int note, int velocity) -{ - int i, v2, key; - - DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity)); - if (! voice_in_range(voice)) - return -EINVAL; - - switch (playing_mode) { - case AWE_PLAY_DIRECT: - case AWE_PLAY_INDIRECT: - key = AWE_VOICE_KEY(voice); - break; - - case AWE_PLAY_MULTI2: - v2 = voice_alloc->map[voice] >> 8; - voice_alloc->map[voice] = 0; - voice = v2; - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return -EINVAL; - /* continue to below */ - default: - key = AWE_CHAN_KEY(voice, note); - break; - } - - for (i = 0; i < awe_max_voices; i++) { - if (voices[i].key == key) - release_voice(i, TRUE); - } - return 0; -} - - -static void start_or_volume_change(int voice, int velocity) -{ - voices[voice].velocity = velocity; - awe_calc_volume(voice); - if (voices[voice].state == AWE_ST_STANDBY) - awe_note_on(voice); - else if (voices[voice].state == AWE_ST_ON) - awe_set_volume(voice, FALSE); -} - -static void set_and_start_voice(int voice, int state) -{ - /* calculate pitch & volume parameters */ - voices[voice].state = state; - awe_calc_pitch(voice); - awe_calc_volume(voice); - if (state == AWE_ST_ON) - awe_note_on(voice); -} - -/* start a voice: - * if note is 255, identical with aftertouch function. - * Otherwise, start a voice with specified not and volume. - */ -static int -awe_start_note(int dev, int voice, int note, int velocity) -{ - int i, key, state, volonly; - - DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity)); - if (! voice_in_range(voice)) - return -EINVAL; - - if (velocity == 0) - state = AWE_ST_STANDBY; /* stand by for playing */ - else - state = AWE_ST_ON; /* really play */ - volonly = FALSE; - - switch (playing_mode) { - case AWE_PLAY_DIRECT: - case AWE_PLAY_INDIRECT: - key = AWE_VOICE_KEY(voice); - if (note == 255) - volonly = TRUE; - break; - - case AWE_PLAY_MULTI2: - voice = voice_alloc->map[voice] >> 8; - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return -EINVAL; - /* continue to below */ - default: - if (note >= 128) { /* key volume mode */ - note -= 128; - volonly = TRUE; - } - key = AWE_CHAN_KEY(voice, note); - break; - } - - /* dynamic volume change */ - if (volonly) { - for (i = 0; i < awe_max_voices; i++) { - if (voices[i].key == key) - start_or_volume_change(i, velocity); - } - return 0; - } - - /* if the same note still playing, stop it */ - if (playing_mode != AWE_PLAY_DIRECT || ctrls[AWE_MD_EXCLUSIVE_SOUND]) { - for (i = 0; i < awe_max_voices; i++) - if (voices[i].key == key) { - if (voices[i].state == AWE_ST_ON) { - awe_note_off(i); - awe_voice_init(i, FALSE); - } else if (voices[i].state == AWE_ST_STANDBY) - awe_voice_init(i, TRUE); - } - } - - /* allocate voices */ - if (playing_mode == AWE_PLAY_DIRECT) - awe_alloc_one_voice(voice, note, velocity); - else - awe_alloc_multi_voices(voice, note, velocity, key); - - /* turn off other voices exlusively (for drums) */ - for (i = 0; i < awe_max_voices; i++) - if (voices[i].key == key) - awe_exclusive_off(i); - - /* set up pitch and volume parameters */ - for (i = 0; i < awe_max_voices; i++) { - if (voices[i].key == key && voices[i].state == AWE_ST_OFF) - set_and_start_voice(i, state); - } - - return 0; -} - - -/* calculate hash key */ -static int -awe_search_key(int bank, int preset, int note) -{ - unsigned int key; - -#if 1 /* new hash table */ - if (bank == AWE_DRUM_BANK) - key = preset + note + 128; - else - key = bank + preset; -#else - key = preset; -#endif - key %= AWE_MAX_PRESETS; - - return (int)key; -} - - -/* search instrument from hash table */ -static awe_voice_list * -awe_search_instr(int bank, int preset, int note) -{ - awe_voice_list *p; - int key, key2; - - key = awe_search_key(bank, preset, note); - for (p = preset_table[key]; p; p = p->next_bank) { - if (p->instr == preset && p->bank == bank) - return p; - } - key2 = awe_search_key(bank, preset, 0); /* search default */ - if (key == key2) - return NULL; - for (p = preset_table[key2]; p; p = p->next_bank) { - if (p->instr == preset && p->bank == bank) - return p; - } - return NULL; -} - - -/* assign the instrument to a voice */ -static int -awe_set_instr_2(int dev, int voice, int instr_no) -{ - if (playing_mode == AWE_PLAY_MULTI2) { - voice = voice_alloc->map[voice] >> 8; - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return -EINVAL; - } - return awe_set_instr(dev, voice, instr_no); -} - -/* assign the instrument to a channel; voice is the channel number */ -static int -awe_set_instr(int dev, int voice, int instr_no) -{ - awe_chan_info *cinfo; - - if (! voice_in_range(voice)) - return -EINVAL; - - if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS) - return -EINVAL; - - cinfo = &channels[voice]; - cinfo->instr = instr_no; - DEBUG(2,printk("AWE32: [program(%d) %d]\n", voice, instr_no)); - - return 0; -} - - -/* reset all voices; terminate sounds and initialize parameters */ -static void -awe_reset(int dev) -{ - int i; - current_alloc_time = 0; - /* don't turn off voice 31 and 32. they are used also for FM voices */ - for (i = 0; i < awe_max_voices; i++) { - awe_terminate(i); - awe_voice_init(i, TRUE); - } - for (i = 0; i < AWE_MAX_CHANNELS; i++) - awe_channel_init(i, TRUE); - for (i = 0; i < 16; i++) { - awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127; - awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127; - } - awe_init_fm(); - awe_tweak(); -} - - -/* hardware specific control: - * GUS specific and AWE32 specific controls are available. - */ -static void -awe_hw_control(int dev, unsigned char *event) -{ - int cmd = event[2]; - if (cmd & _AWE_MODE_FLAG) - awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event); -#ifdef AWE_HAS_GUS_COMPATIBILITY - else - awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event); -#endif -} - - -#ifdef AWE_HAS_GUS_COMPATIBILITY - -/* GUS compatible controls */ -static void -awe_hw_gus_control(int dev, int cmd, unsigned char *event) -{ - int voice, i, key; - unsigned short p1; - short p2; - int plong; - - if (MULTI_LAYER_MODE()) - return; - if (cmd == _GUS_NUMVOICES) - return; - - voice = event[3]; - if (! voice_in_range(voice)) - return; - - p1 = *(unsigned short *) &event[4]; - p2 = *(short *) &event[6]; - plong = *(int*) &event[4]; - - switch (cmd) { - case _GUS_VOICESAMPLE: - awe_set_instr(dev, voice, p1); - return; - - case _GUS_VOICEBALA: - /* 0 to 15 --> -128 to 127 */ - awe_panning(dev, voice, ((int)p1 << 4) - 128); - return; - - case _GUS_VOICEVOL: - case _GUS_VOICEVOL2: - /* not supported yet */ - return; - - case _GUS_RAMPRANGE: - case _GUS_RAMPRATE: - case _GUS_RAMPMODE: - case _GUS_RAMPON: - case _GUS_RAMPOFF: - /* volume ramping not supported */ - return; - - case _GUS_VOLUME_SCALE: - return; - - case _GUS_VOICE_POS: - FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START, - (short)(plong & 0x7fff)); - FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START, - (plong >> 15) & 0xffff); - return; - } - - key = AWE_VOICE_KEY(voice); - for (i = 0; i < awe_max_voices; i++) { - if (voices[i].key == key) { - switch (cmd) { - case _GUS_VOICEON: - awe_note_on(i); - break; - - case _GUS_VOICEOFF: - awe_terminate(i); - awe_fx_init(voices[i].ch); - awe_voice_init(i, TRUE); - break; - - case _GUS_VOICEFADE: - awe_note_off(i); - awe_fx_init(voices[i].ch); - awe_voice_init(i, FALSE); - break; - - case _GUS_VOICEFREQ: - awe_calc_pitch_from_freq(i, plong); - break; - } - } - } -} - -#endif /* gus_compat */ - - -/* AWE32 specific controls */ -static void -awe_hw_awe_control(int dev, int cmd, unsigned char *event) -{ - int voice; - unsigned short p1; - short p2; - int i; - - voice = event[3]; - if (! voice_in_range(voice)) - return; - - if (playing_mode == AWE_PLAY_MULTI2) { - voice = voice_alloc->map[voice] >> 8; - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return; - } - - p1 = *(unsigned short *) &event[4]; - p2 = *(short *) &event[6]; - - switch (cmd) { - case _AWE_DEBUG_MODE: - ctrls[AWE_MD_DEBUG_MODE] = p1; - printk(KERN_DEBUG "AWE32: debug mode = %d\n", ctrls[AWE_MD_DEBUG_MODE]); - break; - case _AWE_REVERB_MODE: - ctrls[AWE_MD_REVERB_MODE] = p1; - awe_update_reverb_mode(); - break; - - case _AWE_CHORUS_MODE: - ctrls[AWE_MD_CHORUS_MODE] = p1; - awe_update_chorus_mode(); - break; - - case _AWE_REMOVE_LAST_SAMPLES: - DEBUG(0,printk("AWE32: remove last samples\n")); - awe_reset(0); - if (locked_sf_id > 0) - awe_remove_samples(locked_sf_id); - break; - - case _AWE_INITIALIZE_CHIP: - awe_initialize(); - break; - - case _AWE_SEND_EFFECT: - i = -1; - if (p1 >= 0x100) { - i = (p1 >> 8); - if (i < 0 || i >= MAX_LAYERS) - break; - } - awe_send_effect(voice, i, p1, p2); - break; - - case _AWE_RESET_CHANNEL: - awe_channel_init(voice, !p1); - break; - - case _AWE_TERMINATE_ALL: - awe_reset(0); - break; - - case _AWE_TERMINATE_CHANNEL: - awe_voice_change(voice, awe_terminate_and_init); - break; - - case _AWE_RELEASE_ALL: - awe_note_off_all(FALSE); - break; - case _AWE_NOTEOFF_ALL: - awe_note_off_all(TRUE); - break; - - case _AWE_INITIAL_VOLUME: - DEBUG(0,printk("AWE32: init attenuation %d\n", p1)); - atten_relative = (char)p2; - atten_offset = (short)p1; - awe_update_volume(); - break; - - case _AWE_CHN_PRESSURE: - channels[voice].chan_press = p1; - awe_modwheel_change(voice, p1); - break; - - case _AWE_CHANNEL_MODE: - DEBUG(0,printk("AWE32: channel mode = %d\n", p1)); - playing_mode = p1; - awe_reset(0); - break; - - case _AWE_DRUM_CHANNELS: - DEBUG(0,printk("AWE32: drum flags = %x\n", p1)); - drum_flags = *(unsigned int*)&event[4]; - break; - - case _AWE_MISC_MODE: - DEBUG(0,printk("AWE32: ctrl parms = %d %d\n", p1, p2)); - if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END) { - ctrls[p1] = p2; - if (ctrl_parms[p1].update) - ctrl_parms[p1].update(); - } - break; - - case _AWE_EQUALIZER: - ctrls[AWE_MD_BASS_LEVEL] = p1; - ctrls[AWE_MD_TREBLE_LEVEL] = p2; - awe_update_equalizer(); - break; - - default: - DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice)); - break; - } -} - - -/* change effects */ -static void -awe_send_effect(int voice, int layer, int type, int val) -{ - awe_chan_info *cinfo; - FX_Rec *fx; - int mode; - - cinfo = &channels[voice]; - if (layer >= 0 && layer < MAX_LAYERS) - fx = &cinfo->fx_layer[layer]; - else - fx = &cinfo->fx; - - if (type & 0x40) - mode = FX_FLAG_OFF; - else if (type & 0x80) - mode = FX_FLAG_ADD; - else - mode = FX_FLAG_SET; - type &= 0x3f; - - if (type >= 0 && type < AWE_FX_END) { - DEBUG(2,printk("AWE32: effects (%d) %d %d\n", voice, type, val)); - if (mode == FX_FLAG_SET) - FX_SET(fx, type, val); - else if (mode == FX_FLAG_ADD) - FX_ADD(fx, type, val); - else - FX_UNSET(fx, type); - if (mode != FX_FLAG_OFF && parm_defs[type].realtime) { - DEBUG(2,printk("AWE32: fx_realtime (%d)\n", voice)); - awe_voice_change(voice, parm_defs[type].realtime); - } - } -} - - -/* change modulation wheel; voice is already mapped on multi2 mode */ -static void -awe_modwheel_change(int voice, int value) -{ - int i; - awe_chan_info *cinfo; - - cinfo = &channels[voice]; - i = value * ctrls[AWE_MD_MOD_SENSE] / 1200; - FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i); - awe_voice_change(voice, awe_fx_fmmod); - FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i); - awe_voice_change(voice, awe_fx_fm2frq2); -} - - -/* voice pressure change */ -static void -awe_aftertouch(int dev, int voice, int pressure) -{ - int note; - - DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure)); - if (! voice_in_range(voice)) - return; - - switch (playing_mode) { - case AWE_PLAY_DIRECT: - case AWE_PLAY_INDIRECT: - awe_start_note(dev, voice, 255, pressure); - break; - case AWE_PLAY_MULTI2: - note = (voice_alloc->map[voice] & 0xff) - 1; - awe_key_pressure(dev, voice, note + 0x80, pressure); - break; - } -} - - -/* voice control change */ -static void -awe_controller(int dev, int voice, int ctrl_num, int value) -{ - awe_chan_info *cinfo; - - if (! voice_in_range(voice)) - return; - - if (playing_mode == AWE_PLAY_MULTI2) { - voice = voice_alloc->map[voice] >> 8; - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return; - } - - cinfo = &channels[voice]; - - switch (ctrl_num) { - case CTL_BANK_SELECT: /* MIDI control #0 */ - DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value)); - if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice) && - !ctrls[AWE_MD_TOGGLE_DRUM_BANK]) - break; - if (value < 0 || value > 255) - break; - cinfo->bank = value; - if (cinfo->bank == AWE_DRUM_BANK) - DRUM_CHANNEL_ON(cinfo->channel); - else - DRUM_CHANNEL_OFF(cinfo->channel); - awe_set_instr(dev, voice, cinfo->instr); - break; - - case CTL_MODWHEEL: /* MIDI control #1 */ - DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value)); - awe_modwheel_change(voice, value); - break; - - case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */ - DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value)); - /* zero centered */ - cinfo->bender = value; - awe_voice_change(voice, awe_set_voice_pitch); - break; - - case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */ - DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value)); - /* value = sense x 100 */ - cinfo->bender_range = value; - /* no audible pitch change yet.. */ - break; - - case CTL_EXPRESSION: /* MIDI control #11 */ - if (SINGLE_LAYER_MODE()) - value /= 128; - case CTRL_EXPRESSION: /* SEQ1 V2 control */ - DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value)); - /* 0 - 127 */ - cinfo->expression_vol = value; - awe_voice_change(voice, awe_set_voice_vol); - break; - - case CTL_PAN: /* MIDI control #10 */ - DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value)); - /* (0-127) -> signed 8bit */ - cinfo->panning = value * 2 - 128; - if (ctrls[AWE_MD_REALTIME_PAN]) - awe_voice_change(voice, awe_set_pan); - break; - - case CTL_MAIN_VOLUME: /* MIDI control #7 */ - if (SINGLE_LAYER_MODE()) - value = (value * 100) / 16383; - case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */ - DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value)); - /* 0 - 127 */ - cinfo->main_vol = value; - awe_voice_change(voice, awe_set_voice_vol); - break; - - case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */ - DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value)); - FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2); - break; - - case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */ - DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value)); - FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2); - break; - - case 120: /* all sounds off */ - awe_note_off_all(FALSE); - break; - case 123: /* all notes off */ - awe_note_off_all(TRUE); - break; - - case CTL_SUSTAIN: /* MIDI control #64 */ - cinfo->sustained = value; - if (value != 127) - awe_voice_change(voice, awe_sustain_off); - break; - - case CTL_SOSTENUTO: /* MIDI control #66 */ - if (value == 127) - awe_voice_change(voice, awe_sostenuto_on); - else - awe_voice_change(voice, awe_sustain_off); - break; - - default: - DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n", - voice, ctrl_num, value)); - break; - } -} - - -/* voice pan change (value = -128 - 127) */ -static void -awe_panning(int dev, int voice, int value) -{ - awe_chan_info *cinfo; - - if (! voice_in_range(voice)) - return; - - if (playing_mode == AWE_PLAY_MULTI2) { - voice = voice_alloc->map[voice] >> 8; - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return; - } - - cinfo = &channels[voice]; - cinfo->panning = value; - DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning)); - if (ctrls[AWE_MD_REALTIME_PAN]) - awe_voice_change(voice, awe_set_pan); -} - - -/* volume mode change */ -static void -awe_volume_method(int dev, int mode) -{ - /* not impremented */ - DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode)); -} - - -/* pitch wheel change: 0-16384 */ -static void -awe_bender(int dev, int voice, int value) -{ - awe_chan_info *cinfo; - - if (! voice_in_range(voice)) - return; - - if (playing_mode == AWE_PLAY_MULTI2) { - voice = voice_alloc->map[voice] >> 8; - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return; - } - - /* convert to zero centered value */ - cinfo = &channels[voice]; - cinfo->bender = value - 8192; - DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender)); - awe_voice_change(voice, awe_set_voice_pitch); -} - - -/* - * load a sound patch: - * three types of patches are accepted: AWE, GUS, and SYSEX. - */ - -static int -awe_load_patch(int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) -{ - awe_patch_info patch; - int rc = 0; - -#ifdef AWE_HAS_GUS_COMPATIBILITY - if (format == GUS_PATCH) { - return awe_load_guspatch(addr, offs, count, pmgr_flag); - } else -#endif - if (format == SYSEX_PATCH) { - /* no system exclusive message supported yet */ - return 0; - } else if (format != AWE_PATCH) { - printk(KERN_WARNING "AWE32 Error: Invalid patch format (key) 0x%x\n", format); - return -EINVAL; - } - - if (count < AWE_PATCH_INFO_SIZE) { - printk(KERN_WARNING "AWE32 Error: Patch header too short\n"); - return -EINVAL; - } - if (copy_from_user(((char*)&patch) + offs, addr + offs, - AWE_PATCH_INFO_SIZE - offs)) - return -EFAULT; - - count -= AWE_PATCH_INFO_SIZE; - if (count < patch.len) { - printk(KERN_WARNING "AWE32: sample: Patch record too short (%d<%d)\n", - count, patch.len); - return -EINVAL; - } - - switch (patch.type) { - case AWE_LOAD_INFO: - rc = awe_load_info(&patch, addr, count); - break; - case AWE_LOAD_DATA: - rc = awe_load_data(&patch, addr, count); - break; - case AWE_OPEN_PATCH: - rc = awe_open_patch(&patch, addr, count); - break; - case AWE_CLOSE_PATCH: - rc = awe_close_patch(&patch, addr, count); - break; - case AWE_UNLOAD_PATCH: - rc = awe_unload_patch(&patch, addr, count); - break; - case AWE_REPLACE_DATA: - rc = awe_replace_data(&patch, addr, count); - break; - case AWE_MAP_PRESET: - rc = awe_load_map(&patch, addr, count); - break; - /* case AWE_PROBE_INFO: - rc = awe_probe_info(&patch, addr, count); - break;*/ - case AWE_PROBE_DATA: - rc = awe_probe_data(&patch, addr, count); - break; - case AWE_REMOVE_INFO: - rc = awe_remove_info(&patch, addr, count); - break; - case AWE_LOAD_CHORUS_FX: - rc = awe_load_chorus_fx(&patch, addr, count); - break; - case AWE_LOAD_REVERB_FX: - rc = awe_load_reverb_fx(&patch, addr, count); - break; - - default: - printk(KERN_WARNING "AWE32 Error: unknown patch format type %d\n", - patch.type); - rc = -EINVAL; - } - - return rc; -} - - -/* create an sf list record */ -static int -awe_create_sf(int type, char *name) -{ - sf_list *rec; - - /* terminate sounds */ - awe_reset(0); - rec = (sf_list *)kmalloc(sizeof(*rec), GFP_KERNEL); - if (rec == NULL) - return 1; /* no memory */ - rec->sf_id = current_sf_id + 1; - rec->type = type; - if (/*current_sf_id == 0 ||*/ (type & AWE_PAT_LOCKED) != 0) - locked_sf_id = current_sf_id + 1; - rec->num_info = awe_free_info(); - rec->num_sample = awe_free_sample(); - rec->mem_ptr = awe_free_mem_ptr(); - rec->infos = rec->last_infos = NULL; - rec->samples = rec->last_samples = NULL; - - /* add to linked-list */ - rec->next = NULL; - rec->prev = sftail; - if (sftail) - sftail->next = rec; - else - sfhead = rec; - sftail = rec; - current_sf_id++; - -#ifdef AWE_ALLOW_SAMPLE_SHARING - rec->shared = NULL; - if (name) - memcpy(rec->name, name, AWE_PATCH_NAME_LEN); - else - strcpy(rec->name, "*TEMPORARY*"); - if (current_sf_id > 1 && name && (type & AWE_PAT_SHARED) != 0) { - /* is the current font really a shared font? */ - if (is_shared_sf(rec->name)) { - /* check if the shared font is already installed */ - sf_list *p; - for (p = rec->prev; p; p = p->prev) { - if (is_identical_name(rec->name, p)) { - rec->shared = p; - break; - } - } - } - } -#endif /* allow sharing */ - - return 0; -} - - -#ifdef AWE_ALLOW_SAMPLE_SHARING - -/* check if the given name is a valid shared name */ -#define ASC_TO_KEY(c) ((c) - 'A' + 1) -static int is_shared_sf(unsigned char *name) -{ - static unsigned char id_head[4] = { - ASC_TO_KEY('A'), ASC_TO_KEY('W'), ASC_TO_KEY('E'), - AWE_MAJOR_VERSION, - }; - if (memcmp(name, id_head, 4) == 0) - return TRUE; - return FALSE; -} - -/* check if the given name matches to the existing list */ -static int is_identical_name(unsigned char *name, sf_list *p) -{ - char *id = p->name; - if (is_shared_sf(id) && memcmp(id, name, AWE_PATCH_NAME_LEN) == 0) - return TRUE; - return FALSE; -} - -/* check if the given voice info exists */ -static int info_duplicated(sf_list *sf, awe_voice_list *rec) -{ - /* search for all sharing lists */ - for (; sf; sf = sf->shared) { - awe_voice_list *p; - for (p = sf->infos; p; p = p->next) { - if (p->type == V_ST_NORMAL && - p->bank == rec->bank && - p->instr == rec->instr && - p->v.low == rec->v.low && - p->v.high == rec->v.high && - p->v.sample == rec->v.sample) - return TRUE; - } - } - return FALSE; -} - -#endif /* AWE_ALLOW_SAMPLE_SHARING */ - - -/* free sf_list record */ -/* linked-list in this function is not cared */ -static void -awe_free_sf(sf_list *sf) -{ - if (sf->infos) { - awe_voice_list *p, *next; - for (p = sf->infos; p; p = next) { - next = p->next; - kfree(p); - } - } - if (sf->samples) { - awe_sample_list *p, *next; - for (p = sf->samples; p; p = next) { - next = p->next; - kfree(p); - } - } - kfree(sf); -} - - -/* open patch; create sf list and set opened flag */ -static int -awe_open_patch(awe_patch_info *patch, const char *addr, int count) -{ - awe_open_parm parm; - int shared; - - if (copy_from_user(&parm, addr + AWE_PATCH_INFO_SIZE, sizeof(parm))) - return -EFAULT; - shared = FALSE; - -#ifdef AWE_ALLOW_SAMPLE_SHARING - if (sftail && (parm.type & AWE_PAT_SHARED) != 0) { - /* is the previous font the same font? */ - if (is_identical_name(parm.name, sftail)) { - /* then append to the previous */ - shared = TRUE; - awe_reset(0); - if (parm.type & AWE_PAT_LOCKED) - locked_sf_id = current_sf_id; - } - } -#endif /* allow sharing */ - if (! shared) { - if (awe_create_sf(parm.type, parm.name)) { - printk(KERN_ERR "AWE32: can't open: failed to alloc new list\n"); - return -ENOMEM; - } - } - patch_opened = TRUE; - return current_sf_id; -} - -/* check if the patch is already opened */ -static sf_list * -check_patch_opened(int type, char *name) -{ - if (! patch_opened) { - if (awe_create_sf(type, name)) { - printk(KERN_ERR "AWE32: failed to alloc new list\n"); - return NULL; - } - patch_opened = TRUE; - return sftail; - } - return sftail; -} - -/* close the patch; if no voice is loaded, remove the patch */ -static int -awe_close_patch(awe_patch_info *patch, const char *addr, int count) -{ - if (patch_opened && sftail) { - /* if no voice is loaded, release the current patch */ - if (sftail->infos == NULL) { - awe_reset(0); - awe_remove_samples(current_sf_id - 1); - } - } - patch_opened = 0; - return 0; -} - - -/* remove the latest patch */ -static int -awe_unload_patch(awe_patch_info *patch, const char *addr, int count) -{ - if (current_sf_id > 0 && current_sf_id > locked_sf_id) { - awe_reset(0); - awe_remove_samples(current_sf_id - 1); - } - return 0; -} - -/* allocate voice info list records */ -static awe_voice_list * -alloc_new_info(void) -{ - awe_voice_list *newlist; - - newlist = (awe_voice_list *)kmalloc(sizeof(*newlist), GFP_KERNEL); - if (newlist == NULL) { - printk(KERN_ERR "AWE32: can't alloc info table\n"); - return NULL; - } - return newlist; -} - -/* allocate sample info list records */ -static awe_sample_list * -alloc_new_sample(void) -{ - awe_sample_list *newlist; - - newlist = (awe_sample_list *)kmalloc(sizeof(*newlist), GFP_KERNEL); - if (newlist == NULL) { - printk(KERN_ERR "AWE32: can't alloc sample table\n"); - return NULL; - } - return newlist; -} - -/* load voice map */ -static int -awe_load_map(awe_patch_info *patch, const char *addr, int count) -{ - awe_voice_map map; - awe_voice_list *rec, *p; - sf_list *sf; - - /* get the link info */ - if (count < sizeof(map)) { - printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); - return -EINVAL; - } - if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map))) - return -EFAULT; - - /* check if the identical mapping already exists */ - p = awe_search_instr(map.map_bank, map.map_instr, map.map_key); - for (; p; p = p->next_instr) { - if (p->type == V_ST_MAPPED && - p->v.start == map.src_instr && - p->v.end == map.src_bank && - p->v.fixkey == map.src_key) - return 0; /* already present! */ - } - - if ((sf = check_patch_opened(AWE_PAT_TYPE_MAP, NULL)) == NULL) - return -ENOMEM; - - if ((rec = alloc_new_info()) == NULL) - return -ENOMEM; - - rec->bank = map.map_bank; - rec->instr = map.map_instr; - rec->type = V_ST_MAPPED; - rec->disabled = FALSE; - awe_init_voice_info(&rec->v); - if (map.map_key >= 0) { - rec->v.low = map.map_key; - rec->v.high = map.map_key; - } - rec->v.start = map.src_instr; - rec->v.end = map.src_bank; - rec->v.fixkey = map.src_key; - add_sf_info(sf, rec); - add_info_list(rec); - - return 0; -} - -#if 0 -/* probe preset in the current list -- nothing to be loaded */ -static int -awe_probe_info(awe_patch_info *patch, const char *addr, int count) -{ -#ifdef AWE_ALLOW_SAMPLE_SHARING - awe_voice_map map; - awe_voice_list *p; - - if (! patch_opened) - return -EINVAL; - - /* get the link info */ - if (count < sizeof(map)) { - printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); - return -EINVAL; - } - if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map))) - return -EFAULT; - - /* check if the identical mapping already exists */ - if (sftail == NULL) - return -EINVAL; - p = awe_search_instr(map.src_bank, map.src_instr, map.src_key); - for (; p; p = p->next_instr) { - if (p->type == V_ST_NORMAL && - is_identical_holder(p->holder, sftail) && - p->v.low <= map.src_key && - p->v.high >= map.src_key) - return 0; /* already present! */ - } -#endif /* allow sharing */ - return -EINVAL; -} -#endif - -/* probe sample in the current list -- nothing to be loaded */ -static int -awe_probe_data(awe_patch_info *patch, const char *addr, int count) -{ -#ifdef AWE_ALLOW_SAMPLE_SHARING - if (! patch_opened) - return -EINVAL; - - /* search the specified sample by optarg */ - if (search_sample_index(sftail, patch->optarg) != NULL) - return 0; -#endif /* allow sharing */ - return -EINVAL; -} - - -/* remove the present instrument layers */ -static int -remove_info(sf_list *sf, int bank, int instr) -{ - awe_voice_list *prev, *next, *p; - int removed = 0; - - prev = NULL; - for (p = sf->infos; p; p = next) { - next = p->next; - if (p->type == V_ST_NORMAL && - p->bank == bank && p->instr == instr) { - /* remove this layer */ - if (prev) - prev->next = next; - else - sf->infos = next; - if (p == sf->last_infos) - sf->last_infos = prev; - sf->num_info--; - removed++; - kfree(p); - } else - prev = p; - } - if (removed) - rebuild_preset_list(); - return removed; -} - -/* load voice information data */ -static int -awe_load_info(awe_patch_info *patch, const char *addr, int count) -{ - int offset; - awe_voice_rec_hdr hdr; - int i; - int total_size; - sf_list *sf; - awe_voice_list *rec; - - if (count < AWE_VOICE_REC_SIZE) { - printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); - return -EINVAL; - } - - offset = AWE_PATCH_INFO_SIZE; - if (copy_from_user((char*)&hdr, addr + offset, AWE_VOICE_REC_SIZE)) - return -EFAULT; - offset += AWE_VOICE_REC_SIZE; - - if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { - printk(KERN_WARNING "AWE32 Error: Invalid voice number %d\n", hdr.nvoices); - return -EINVAL; - } - total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices; - if (count < total_size) { - printk(KERN_WARNING "AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n", - count, hdr.nvoices); - return -EINVAL; - } - - if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL) - return -ENOMEM; - - switch (hdr.write_mode) { - case AWE_WR_EXCLUSIVE: - /* exclusive mode - if the instrument already exists, - return error */ - for (rec = sf->infos; rec; rec = rec->next) { - if (rec->type == V_ST_NORMAL && - rec->bank == hdr.bank && - rec->instr == hdr.instr) - return -EINVAL; - } - break; - case AWE_WR_REPLACE: - /* replace mode - remove the instrument if it already exists */ - remove_info(sf, hdr.bank, hdr.instr); - break; - } - - /* append new layers */ - for (i = 0; i < hdr.nvoices; i++) { - rec = alloc_new_info(); - if (rec == NULL) - return -ENOMEM; - - rec->bank = hdr.bank; - rec->instr = hdr.instr; - rec->type = V_ST_NORMAL; - rec->disabled = FALSE; - - /* copy awe_voice_info parameters */ - if (copy_from_user(&rec->v, addr + offset, AWE_VOICE_INFO_SIZE)) { - kfree(rec); - return -EFAULT; - } - offset += AWE_VOICE_INFO_SIZE; -#ifdef AWE_ALLOW_SAMPLE_SHARING - if (sf && sf->shared) { - if (info_duplicated(sf, rec)) { - kfree(rec); - continue; - } - } -#endif /* allow sharing */ - if (rec->v.mode & AWE_MODE_INIT_PARM) - awe_init_voice_parm(&rec->v.parm); - add_sf_info(sf, rec); - awe_set_sample(rec); - add_info_list(rec); - } - - return 0; -} - - -/* remove instrument layers */ -static int -awe_remove_info(awe_patch_info *patch, const char *addr, int count) -{ - unsigned char bank, instr; - sf_list *sf; - - if (! patch_opened || (sf = sftail) == NULL) { - printk(KERN_WARNING "AWE32: remove_info: patch not opened\n"); - return -EINVAL; - } - - bank = ((unsigned short)patch->optarg >> 8) & 0xff; - instr = (unsigned short)patch->optarg & 0xff; - if (! remove_info(sf, bank, instr)) - return -EINVAL; - return 0; -} - - -/* load wave sample data */ -static int -awe_load_data(awe_patch_info *patch, const char *addr, int count) -{ - int offset, size; - int rc; - awe_sample_info tmprec; - awe_sample_list *rec; - sf_list *sf; - - if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL) - return -ENOMEM; - - size = (count - AWE_SAMPLE_INFO_SIZE) / 2; - offset = AWE_PATCH_INFO_SIZE; - if (copy_from_user(&tmprec, addr + offset, AWE_SAMPLE_INFO_SIZE)) - return -EFAULT; - offset += AWE_SAMPLE_INFO_SIZE; - if (size != tmprec.size) { - printk(KERN_WARNING "AWE32: load: sample size differed (%d != %d)\n", - tmprec.size, size); - return -EINVAL; - } - - if (search_sample_index(sf, tmprec.sample) != NULL) { -#ifdef AWE_ALLOW_SAMPLE_SHARING - /* if shared sample, skip this data */ - if (sf->type & AWE_PAT_SHARED) - return 0; -#endif /* allow sharing */ - DEBUG(1,printk("AWE32: sample data %d already present\n", tmprec.sample)); - return -EINVAL; - } - - if ((rec = alloc_new_sample()) == NULL) - return -ENOMEM; - - memcpy(&rec->v, &tmprec, sizeof(tmprec)); - - if (rec->v.size > 0) { - if ((rc = awe_write_wave_data(addr, offset, rec, -1)) < 0) { - kfree(rec); - return rc; - } - sf->mem_ptr += rc; - } - - add_sf_sample(sf, rec); - return 0; -} - - -/* replace wave sample data */ -static int -awe_replace_data(awe_patch_info *patch, const char *addr, int count) -{ - int offset; - int size; - int rc; - int channels; - awe_sample_info cursmp; - int save_mem_ptr; - sf_list *sf; - awe_sample_list *rec; - - if (! patch_opened || (sf = sftail) == NULL) { - printk(KERN_WARNING "AWE32: replace: patch not opened\n"); - return -EINVAL; - } - - size = (count - AWE_SAMPLE_INFO_SIZE) / 2; - offset = AWE_PATCH_INFO_SIZE; - if (copy_from_user(&cursmp, addr + offset, AWE_SAMPLE_INFO_SIZE)) - return -EFAULT; - offset += AWE_SAMPLE_INFO_SIZE; - if (cursmp.size == 0 || size != cursmp.size) { - printk(KERN_WARNING "AWE32: replace: invalid sample size (%d!=%d)\n", - cursmp.size, size); - return -EINVAL; - } - channels = patch->optarg; - if (channels <= 0 || channels > AWE_NORMAL_VOICES) { - printk(KERN_WARNING "AWE32: replace: invalid channels %d\n", channels); - return -EINVAL; - } - - for (rec = sf->samples; rec; rec = rec->next) { - if (rec->v.sample == cursmp.sample) - break; - } - if (rec == NULL) { - printk(KERN_WARNING "AWE32: replace: cannot find existing sample data %d\n", - cursmp.sample); - return -EINVAL; - } - - if (rec->v.size != cursmp.size) { - printk(KERN_WARNING "AWE32: replace: exiting size differed (%d!=%d)\n", - rec->v.size, cursmp.size); - return -EINVAL; - } - - save_mem_ptr = awe_free_mem_ptr(); - sftail->mem_ptr = rec->v.start - awe_mem_start; - memcpy(&rec->v, &cursmp, sizeof(cursmp)); - rec->v.sf_id = current_sf_id; - if ((rc = awe_write_wave_data(addr, offset, rec, channels)) < 0) - return rc; - sftail->mem_ptr = save_mem_ptr; - - return 0; -} - - -/*----------------------------------------------------------------*/ - -static const char *readbuf_addr; -static int readbuf_offs; -static int readbuf_flags; - -/* initialize read buffer */ -static int -readbuf_init(const char *addr, int offset, awe_sample_info *sp) -{ - readbuf_addr = addr; - readbuf_offs = offset; - readbuf_flags = sp->mode_flags; - return 0; -} - -/* read directly from user buffer */ -static unsigned short -readbuf_word(int pos) -{ - unsigned short c; - /* read from user buffer */ - if (readbuf_flags & AWE_SAMPLE_8BITS) { - unsigned char cc; - get_user(cc, (unsigned char*)(readbuf_addr + readbuf_offs + pos)); - c = (unsigned short)cc << 8; /* convert 8bit -> 16bit */ - } else { - get_user(c, (unsigned short*)(readbuf_addr + readbuf_offs + pos * 2)); - } - if (readbuf_flags & AWE_SAMPLE_UNSIGNED) - c ^= 0x8000; /* unsigned -> signed */ - return c; -} - -#define readbuf_word_cache readbuf_word -#define readbuf_end() /**/ - -/*----------------------------------------------------------------*/ - -#define BLANK_LOOP_START 8 -#define BLANK_LOOP_END 40 -#define BLANK_LOOP_SIZE 48 - -/* loading onto memory - return the actual written size */ -static int -awe_write_wave_data(const char *addr, int offset, awe_sample_list *list, int channels) -{ - int i, truesize, dram_offset; - awe_sample_info *sp = &list->v; - int rc; - - /* be sure loop points start < end */ - if (sp->loopstart > sp->loopend) { - int tmp = sp->loopstart; - sp->loopstart = sp->loopend; - sp->loopend = tmp; - } - - /* compute true data size to be loaded */ - truesize = sp->size; - if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) - truesize += sp->loopend - sp->loopstart; - if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) - truesize += BLANK_LOOP_SIZE; - if (awe_free_mem_ptr() + truesize >= memsize/2) { - DEBUG(-1,printk("AWE32 Error: Sample memory full\n")); - return -ENOSPC; - } - - /* recalculate address offset */ - sp->end -= sp->start; - sp->loopstart -= sp->start; - sp->loopend -= sp->start; - - dram_offset = awe_free_mem_ptr() + awe_mem_start; - sp->start = dram_offset; - sp->end += dram_offset; - sp->loopstart += dram_offset; - sp->loopend += dram_offset; - - /* set the total size (store onto obsolete checksum value) */ - if (sp->size == 0) - sp->checksum = 0; - else - sp->checksum = truesize; - - if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0) - return rc; - - if (readbuf_init(addr, offset, sp) < 0) - return -ENOSPC; - - for (i = 0; i < sp->size; i++) { - unsigned short c; - c = readbuf_word(i); - awe_write_dram(c); - if (i == sp->loopend && - (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) { - int looplen = sp->loopend - sp->loopstart; - /* copy reverse loop */ - int k; - for (k = 1; k <= looplen; k++) { - c = readbuf_word_cache(i - k); - awe_write_dram(c); - } - if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) { - sp->end += looplen; - } else { - sp->start += looplen; - sp->end += looplen; - } - } - } - readbuf_end(); - - /* if no blank loop is attached in the sample, add it */ - if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) { - for (i = 0; i < BLANK_LOOP_SIZE; i++) - awe_write_dram(0); - if (sp->mode_flags & AWE_SAMPLE_SINGLESHOT) { - sp->loopstart = sp->end + BLANK_LOOP_START; - sp->loopend = sp->end + BLANK_LOOP_END; - } - } - - awe_close_dram(); - - /* initialize FM */ - awe_init_fm(); - - return truesize; -} - - -/*----------------------------------------------------------------*/ - -#ifdef AWE_HAS_GUS_COMPATIBILITY - -/* calculate GUS envelope time: - * is this correct? i have no idea.. - */ -static int -calc_gus_envelope_time(int rate, int start, int end) -{ - int r, p, t; - r = (3 - ((rate >> 6) & 3)) * 3; - p = rate & 0x3f; - t = end - start; - if (t < 0) t = -t; - if (13 > r) - t = t << (13 - r); - else - t = t >> (r - 13); - return (t * 10) / (p * 441); -} - -#define calc_gus_sustain(val) (0x7f - vol_table[(val)/2]) -#define calc_gus_attenuation(val) vol_table[(val)/2] - -/* load GUS patch */ -static int -awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag) -{ - struct patch_info patch; - awe_voice_info *rec; - awe_sample_info *smp; - awe_voice_list *vrec; - awe_sample_list *smprec; - int sizeof_patch; - int note, rc; - sf_list *sf; - - sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */ - if (size < sizeof_patch) { - printk(KERN_WARNING "AWE32 Error: Patch header too short\n"); - return -EINVAL; - } - if (copy_from_user(((char*)&patch) + offs, addr + offs, sizeof_patch - offs)) - return -EFAULT; - size -= sizeof_patch; - if (size < patch.len) { - printk(KERN_WARNING "AWE32 Error: Patch record too short (%d<%d)\n", - size, patch.len); - return -EINVAL; - } - if ((sf = check_patch_opened(AWE_PAT_TYPE_GUS, NULL)) == NULL) - return -ENOMEM; - if ((smprec = alloc_new_sample()) == NULL) - return -ENOMEM; - if ((vrec = alloc_new_info()) == NULL) { - kfree(smprec); - return -ENOMEM; - } - - smp = &smprec->v; - smp->sample = sf->num_sample; - smp->start = 0; - smp->end = patch.len; - smp->loopstart = patch.loop_start; - smp->loopend = patch.loop_end; - smp->size = patch.len; - - /* set up mode flags */ - smp->mode_flags = 0; - if (!(patch.mode & WAVE_16_BITS)) - smp->mode_flags |= AWE_SAMPLE_8BITS; - if (patch.mode & WAVE_UNSIGNED) - smp->mode_flags |= AWE_SAMPLE_UNSIGNED; - smp->mode_flags |= AWE_SAMPLE_NO_BLANK; - if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) - smp->mode_flags |= AWE_SAMPLE_SINGLESHOT; - if (patch.mode & WAVE_BIDIR_LOOP) - smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP; - if (patch.mode & WAVE_LOOP_BACK) - smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP; - - DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags)); - if (patch.mode & WAVE_16_BITS) { - /* convert to word offsets */ - smp->size /= 2; - smp->end /= 2; - smp->loopstart /= 2; - smp->loopend /= 2; - } - smp->checksum_flag = 0; - smp->checksum = 0; - - if ((rc = awe_write_wave_data(addr, sizeof_patch, smprec, -1)) < 0) - return rc; - sf->mem_ptr += rc; - add_sf_sample(sf, smprec); - - /* set up voice info */ - rec = &vrec->v; - awe_init_voice_info(rec); - rec->sample = sf->num_info; /* the last sample */ - rec->rate_offset = calc_rate_offset(patch.base_freq); - note = freq_to_note(patch.base_note); - rec->root = note / 100; - rec->tune = -(note % 100); - rec->low = freq_to_note(patch.low_note) / 100; - rec->high = freq_to_note(patch.high_note) / 100; - DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n", - rec->rate_offset, note, - rec->low, rec->high, - patch.low_note, patch.high_note)); - /* panning position; -128 - 127 => 0-127 */ - rec->pan = (patch.panning + 128) / 2; - - /* detuning is ignored */ - /* 6points volume envelope */ - if (patch.mode & WAVE_ENVELOPES) { - int attack, hold, decay, release; - attack = calc_gus_envelope_time - (patch.env_rate[0], 0, patch.env_offset[0]); - hold = calc_gus_envelope_time - (patch.env_rate[1], patch.env_offset[0], - patch.env_offset[1]); - decay = calc_gus_envelope_time - (patch.env_rate[2], patch.env_offset[1], - patch.env_offset[2]); - release = calc_gus_envelope_time - (patch.env_rate[3], patch.env_offset[1], - patch.env_offset[4]); - release += calc_gus_envelope_time - (patch.env_rate[4], patch.env_offset[3], - patch.env_offset[4]); - release += calc_gus_envelope_time - (patch.env_rate[5], patch.env_offset[4], - patch.env_offset[5]); - rec->parm.volatkhld = (calc_parm_hold(hold) << 8) | - calc_parm_attack(attack); - rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | - calc_parm_decay(decay); - rec->parm.volrelease = 0x8000 | calc_parm_decay(release); - DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release)); - rec->attenuation = calc_gus_attenuation(patch.env_offset[0]); - } - - /* tremolo effect */ - if (patch.mode & WAVE_TREMOLO) { - int rate = (patch.tremolo_rate * 1000 / 38) / 42; - rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; - DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n", - patch.tremolo_rate, patch.tremolo_depth, - rec->parm.tremfrq)); - } - /* vibrato effect */ - if (patch.mode & WAVE_VIBRATO) { - int rate = (patch.vibrato_rate * 1000 / 38) / 42; - rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; - DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n", - patch.tremolo_rate, patch.tremolo_depth, - rec->parm.tremfrq)); - } - - /* scale_freq, scale_factor, volume, and fractions not implemented */ - - /* append to the tail of the list */ - vrec->bank = ctrls[AWE_MD_GUS_BANK]; - vrec->instr = patch.instr_no; - vrec->disabled = FALSE; - vrec->type = V_ST_NORMAL; - - add_sf_info(sf, vrec); - add_info_list(vrec); - - /* set the voice index */ - awe_set_sample(vrec); - - return 0; -} - -#endif /* AWE_HAS_GUS_COMPATIBILITY */ - -/* - * sample and voice list handlers - */ - -/* append this to the current sf list */ -static void add_sf_info(sf_list *sf, awe_voice_list *rec) -{ - if (sf == NULL) - return; - rec->holder = sf; - rec->v.sf_id = sf->sf_id; - if (sf->last_infos) - sf->last_infos->next = rec; - else - sf->infos = rec; - sf->last_infos = rec; - rec->next = NULL; - sf->num_info++; -} - -/* prepend this sample to sf list */ -static void add_sf_sample(sf_list *sf, awe_sample_list *rec) -{ - if (sf == NULL) - return; - rec->holder = sf; - rec->v.sf_id = sf->sf_id; - if (sf->last_samples) - sf->last_samples->next = rec; - else - sf->samples = rec; - sf->last_samples = rec; - rec->next = NULL; - sf->num_sample++; -} - -/* purge the old records which don't belong with the same file id */ -static void purge_old_list(awe_voice_list *rec, awe_voice_list *next) -{ - rec->next_instr = next; - if (rec->bank == AWE_DRUM_BANK) { - /* remove samples with the same note range */ - awe_voice_list *cur, *prev = rec; - int low = rec->v.low; - int high = rec->v.high; - for (cur = next; cur; cur = cur->next_instr) { - if (cur->v.low == low && - cur->v.high == high && - ! is_identical_holder(cur->holder, rec->holder)) - prev->next_instr = cur->next_instr; - else - prev = cur; - } - } else { - if (! is_identical_holder(next->holder, rec->holder)) - /* remove all samples */ - rec->next_instr = NULL; - } -} - -/* prepend to top of the preset table */ -static void add_info_list(awe_voice_list *rec) -{ - awe_voice_list *prev, *cur; - int key; - - if (rec->disabled) - return; - - key = awe_search_key(rec->bank, rec->instr, rec->v.low); - prev = NULL; - for (cur = preset_table[key]; cur; cur = cur->next_bank) { - /* search the first record with the same bank number */ - if (cur->instr == rec->instr && cur->bank == rec->bank) { - /* replace the list with the new record */ - rec->next_bank = cur->next_bank; - if (prev) - prev->next_bank = rec; - else - preset_table[key] = rec; - purge_old_list(rec, cur); - return; - } - prev = cur; - } - - /* this is the first bank record.. just add this */ - rec->next_instr = NULL; - rec->next_bank = preset_table[key]; - preset_table[key] = rec; -} - -/* remove samples later than the specified sf_id */ -static void -awe_remove_samples(int sf_id) -{ - sf_list *p, *prev; - - if (sf_id <= 0) { - awe_reset_samples(); - return; - } - /* already removed? */ - if (current_sf_id <= sf_id) - return; - - for (p = sftail; p; p = prev) { - if (p->sf_id <= sf_id) - break; - prev = p->prev; - awe_free_sf(p); - } - sftail = p; - if (sftail) { - sf_id = sftail->sf_id; - sftail->next = NULL; - } else { - sf_id = 0; - sfhead = NULL; - } - current_sf_id = sf_id; - if (locked_sf_id > sf_id) - locked_sf_id = sf_id; - - rebuild_preset_list(); -} - -/* rebuild preset search list */ -static void rebuild_preset_list(void) -{ - sf_list *p; - awe_voice_list *rec; - - memset(preset_table, 0, sizeof(preset_table)); - - for (p = sfhead; p; p = p->next) { - for (rec = p->infos; rec; rec = rec->next) - add_info_list(rec); - } -} - -/* compare the given sf_id pair */ -static int is_identical_holder(sf_list *sf1, sf_list *sf2) -{ - if (sf1 == NULL || sf2 == NULL) - return FALSE; - if (sf1 == sf2) - return TRUE; -#ifdef AWE_ALLOW_SAMPLE_SHARING - { - /* compare with the sharing id */ - sf_list *p; - int counter = 0; - if (sf1->sf_id < sf2->sf_id) { /* make sure id1 > id2 */ - sf_list *tmp; tmp = sf1; sf1 = sf2; sf2 = tmp; - } - for (p = sf1->shared; p; p = p->shared) { - if (counter++ > current_sf_id) - break; /* strange sharing loop.. quit */ - if (p == sf2) - return TRUE; - } - } -#endif /* allow sharing */ - return FALSE; -} - -/* search the sample index matching with the given sample id */ -static awe_sample_list * -search_sample_index(sf_list *sf, int sample) -{ - awe_sample_list *p; -#ifdef AWE_ALLOW_SAMPLE_SHARING - int counter = 0; - while (sf) { - for (p = sf->samples; p; p = p->next) { - if (p->v.sample == sample) - return p; - } - sf = sf->shared; - if (counter++ > current_sf_id) - break; /* strange sharing loop.. quit */ - } -#else - if (sf) { - for (p = sf->samples; p; p = p->next) { - if (p->v.sample == sample) - return p; - } - } -#endif - return NULL; -} - -/* search the specified sample */ -/* non-zero = found */ -static short -awe_set_sample(awe_voice_list *rec) -{ - awe_sample_list *smp; - awe_voice_info *vp = &rec->v; - - vp->index = 0; - if ((smp = search_sample_index(rec->holder, vp->sample)) == NULL) - return 0; - - /* set the actual sample offsets */ - vp->start += smp->v.start; - vp->end += smp->v.end; - vp->loopstart += smp->v.loopstart; - vp->loopend += smp->v.loopend; - /* copy mode flags */ - vp->mode = smp->v.mode_flags; - /* set flag */ - vp->index = 1; - - return 1; -} - - -/* - * voice allocation - */ - -/* look for all voices associated with the specified note & velocity */ -static int -awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, - awe_voice_info **vlist) -{ - int nvoices; - - nvoices = 0; - for (; rec; rec = rec->next_instr) { - if (note >= rec->v.low && - note <= rec->v.high && - velocity >= rec->v.vellow && - velocity <= rec->v.velhigh) { - if (rec->type == V_ST_MAPPED) { - /* mapper */ - vlist[0] = &rec->v; - return -1; - } - vlist[nvoices++] = &rec->v; - if (nvoices >= AWE_MAX_VOICES) - break; - } - } - return nvoices; -} - -/* store the voice list from the specified note and velocity. - if the preset is mapped, seek for the destination preset, and rewrite - the note number if necessary. - */ -static int -really_alloc_voices(int bank, int instr, int *note, int velocity, awe_voice_info **vlist) -{ - int nvoices; - awe_voice_list *vrec; - int level = 0; - - for (;;) { - vrec = awe_search_instr(bank, instr, *note); - nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); - if (nvoices == 0) { - if (bank == AWE_DRUM_BANK) - /* search default drumset */ - vrec = awe_search_instr(bank, ctrls[AWE_MD_DEF_DRUM], *note); - else - /* search default preset */ - vrec = awe_search_instr(ctrls[AWE_MD_DEF_BANK], instr, *note); - nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); - } - if (nvoices == 0) { - if (bank == AWE_DRUM_BANK && ctrls[AWE_MD_DEF_DRUM] != 0) - /* search default drumset */ - vrec = awe_search_instr(bank, 0, *note); - else if (bank != AWE_DRUM_BANK && ctrls[AWE_MD_DEF_BANK] != 0) - /* search default preset */ - vrec = awe_search_instr(0, instr, *note); - nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); - } - if (nvoices < 0) { /* mapping */ - int key = vlist[0]->fixkey; - instr = vlist[0]->start; - bank = vlist[0]->end; - if (level++ > 5) { - printk(KERN_ERR "AWE32: too deep mapping level\n"); - return 0; - } - if (key >= 0) - *note = key; - } else - break; - } - - return nvoices; -} - -/* allocate voices corresponding note and velocity; supports multiple insts. */ -static void -awe_alloc_multi_voices(int ch, int note, int velocity, int key) -{ - int i, v, nvoices, bank; - awe_voice_info *vlist[AWE_MAX_VOICES]; - - if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) - bank = AWE_DRUM_BANK; /* always search drumset */ - else - bank = channels[ch].bank; - - /* check the possible voices; note may be changeable if mapped */ - nvoices = really_alloc_voices(bank, channels[ch].instr, - ¬e, velocity, vlist); - - /* set the voices */ - current_alloc_time++; - for (i = 0; i < nvoices; i++) { - v = awe_clear_voice(); - voices[v].key = key; - voices[v].ch = ch; - voices[v].note = note; - voices[v].velocity = velocity; - voices[v].time = current_alloc_time; - voices[v].cinfo = &channels[ch]; - voices[v].sample = vlist[i]; - voices[v].state = AWE_ST_MARK; - voices[v].layer = nvoices - i - 1; /* in reverse order */ - } - - /* clear the mark in allocated voices */ - for (i = 0; i < awe_max_voices; i++) { - if (voices[i].state == AWE_ST_MARK) - voices[i].state = AWE_ST_OFF; - - } -} - - -/* search an empty voice. - if no empty voice is found, at least terminate a voice - */ -static int -awe_clear_voice(void) -{ - enum { - OFF=0, RELEASED, SUSTAINED, PLAYING, END - }; - struct voice_candidate_t { - int best; - int time; - int vtarget; - } candidate[END]; - int i, type, vtarget; - - vtarget = 0xffff; - for (type = OFF; type < END; type++) { - candidate[type].best = -1; - candidate[type].time = current_alloc_time + 1; - candidate[type].vtarget = vtarget; - } - - for (i = 0; i < awe_max_voices; i++) { - if (voices[i].state & AWE_ST_OFF) - type = OFF; - else if (voices[i].state & AWE_ST_RELEASED) - type = RELEASED; - else if (voices[i].state & AWE_ST_SUSTAINED) - type = SUSTAINED; - else if (voices[i].state & ~AWE_ST_MARK) - type = PLAYING; - else - continue; -#ifdef AWE_CHECK_VTARGET - /* get current volume */ - vtarget = (awe_peek_dw(AWE_VTFT(i)) >> 16) & 0xffff; -#endif - if (candidate[type].best < 0 || - vtarget < candidate[type].vtarget || - (vtarget == candidate[type].vtarget && - voices[i].time < candidate[type].time)) { - candidate[type].best = i; - candidate[type].time = voices[i].time; - candidate[type].vtarget = vtarget; - } - } - - for (type = OFF; type < END; type++) { - if ((i = candidate[type].best) >= 0) { - if (voices[i].state != AWE_ST_OFF) - awe_terminate(i); - awe_voice_init(i, TRUE); - return i; - } - } - return 0; -} - - -/* search sample for the specified note & velocity and set it on the voice; - * note that voice is the voice index (not channel index) - */ -static void -awe_alloc_one_voice(int voice, int note, int velocity) -{ - int ch, nvoices, bank; - awe_voice_info *vlist[AWE_MAX_VOICES]; - - ch = voices[voice].ch; - if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice)) - bank = AWE_DRUM_BANK; /* always search drumset */ - else - bank = voices[voice].cinfo->bank; - - nvoices = really_alloc_voices(bank, voices[voice].cinfo->instr, - ¬e, velocity, vlist); - if (nvoices > 0) { - voices[voice].time = ++current_alloc_time; - voices[voice].sample = vlist[0]; /* use the first one */ - voices[voice].layer = 0; - voices[voice].note = note; - voices[voice].velocity = velocity; - } -} - - -/* - * sequencer2 functions - */ - -/* search an empty voice; used by sequencer2 */ -static int -awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) -{ - playing_mode = AWE_PLAY_MULTI2; - awe_info.nr_voices = AWE_MAX_CHANNELS; - return awe_clear_voice(); -} - - -/* set up voice; used by sequencer2 */ -static void -awe_setup_voice(int dev, int voice, int chn) -{ - struct channel_info *info; - if (synth_devs[dev] == NULL || - (info = &synth_devs[dev]->chn_info[chn]) == NULL) - return; - - if (voice < 0 || voice >= awe_max_voices) - return; - - DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn)); - channels[chn].expression_vol = info->controllers[CTL_EXPRESSION]; - channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME]; - channels[chn].panning = - info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */ - channels[chn].bender = info->bender_value; /* zero center */ - channels[chn].bank = info->controllers[CTL_BANK_SELECT]; - channels[chn].sustained = info->controllers[CTL_SUSTAIN]; - if (info->controllers[CTL_EXT_EFF_DEPTH]) { - FX_SET(&channels[chn].fx, AWE_FX_REVERB, - info->controllers[CTL_EXT_EFF_DEPTH] * 2); - } - if (info->controllers[CTL_CHORUS_DEPTH]) { - FX_SET(&channels[chn].fx, AWE_FX_CHORUS, - info->controllers[CTL_CHORUS_DEPTH] * 2); - } - awe_set_instr(dev, chn, info->pgm_num); -} - - -#ifdef CONFIG_AWE32_MIXER -/* - * AWE32 mixer device control - */ - -static int awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg); - -static int my_mixerdev = -1; - -static struct mixer_operations awe_mixer_operations = { - owner: THIS_MODULE, - id: "AWE", - name: "AWE32 Equalizer", - ioctl: awe_mixer_ioctl, -}; - -static void __init attach_mixer(void) -{ - if ((my_mixerdev = sound_alloc_mixerdev()) >= 0) { - mixer_devs[my_mixerdev] = &awe_mixer_operations; - } -} - -static void __exit unload_mixer(void) -{ - if (my_mixerdev >= 0) - sound_unload_mixerdev(my_mixerdev); -} - -static int -awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int i, level, value; - - if (((cmd >> 8) & 0xff) != 'M') - return -EINVAL; - - level = *(int*)arg; - level = ((level & 0xff) + (level >> 8)) / 2; - DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level)); - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - switch (cmd & 0xff) { - case SOUND_MIXER_BASS: - value = level * 12 / 100; - if (value >= 12) - value = 11; - ctrls[AWE_MD_BASS_LEVEL] = value; - awe_update_equalizer(); - break; - case SOUND_MIXER_TREBLE: - value = level * 12 / 100; - if (value >= 12) - value = 11; - ctrls[AWE_MD_TREBLE_LEVEL] = value; - awe_update_equalizer(); - break; - case SOUND_MIXER_VOLUME: - level = level * 127 / 100; - if (level >= 128) level = 127; - atten_relative = FALSE; - atten_offset = vol_table[level]; - awe_update_volume(); - break; - } - } - switch (cmd & 0xff) { - case SOUND_MIXER_BASS: - level = ctrls[AWE_MD_BASS_LEVEL] * 100 / 24; - level = (level << 8) | level; - break; - case SOUND_MIXER_TREBLE: - level = ctrls[AWE_MD_TREBLE_LEVEL] * 100 / 24; - level = (level << 8) | level; - break; - case SOUND_MIXER_VOLUME: - value = atten_offset; - if (atten_relative) - value += ctrls[AWE_MD_ZERO_ATTEN]; - for (i = 127; i > 0; i--) { - if (value <= vol_table[i]) - break; - } - level = i * 100 / 127; - level = (level << 8) | level; - break; - case SOUND_MIXER_DEVMASK: - level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME; - break; - default: - level = 0; - break; - } - return *(int*)arg = level; -} -#endif /* CONFIG_AWE32_MIXER */ - - -/* - * initialization of Emu8000 - */ - -/* intiailize audio channels */ -static void -awe_init_audio(void) -{ - int ch; - - /* turn off envelope engines */ - for (ch = 0; ch < AWE_MAX_VOICES; ch++) { - awe_poke(AWE_DCYSUSV(ch), 0x80); - } - - /* reset all other parameters to zero */ - for (ch = 0; ch < AWE_MAX_VOICES; ch++) { - awe_poke(AWE_ENVVOL(ch), 0); - awe_poke(AWE_ENVVAL(ch), 0); - awe_poke(AWE_DCYSUS(ch), 0); - awe_poke(AWE_ATKHLDV(ch), 0); - awe_poke(AWE_LFO1VAL(ch), 0); - awe_poke(AWE_ATKHLD(ch), 0); - awe_poke(AWE_LFO2VAL(ch), 0); - awe_poke(AWE_IP(ch), 0); - awe_poke(AWE_IFATN(ch), 0); - awe_poke(AWE_PEFE(ch), 0); - awe_poke(AWE_FMMOD(ch), 0); - awe_poke(AWE_TREMFRQ(ch), 0); - awe_poke(AWE_FM2FRQ2(ch), 0); - awe_poke_dw(AWE_PTRX(ch), 0); - awe_poke_dw(AWE_VTFT(ch), 0); - awe_poke_dw(AWE_PSST(ch), 0); - awe_poke_dw(AWE_CSL(ch), 0); - awe_poke_dw(AWE_CCCA(ch), 0); - } - - for (ch = 0; ch < AWE_MAX_VOICES; ch++) { - awe_poke_dw(AWE_CPF(ch), 0); - awe_poke_dw(AWE_CVCF(ch), 0); - } -} - - -/* initialize DMA address */ -static void -awe_init_dma(void) -{ - awe_poke_dw(AWE_SMALR, 0); - awe_poke_dw(AWE_SMARR, 0); - awe_poke_dw(AWE_SMALW, 0); - awe_poke_dw(AWE_SMARW, 0); -} - - -/* initialization arrays; from ADIP */ - -static unsigned short init1[128] = { - 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, - 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, - 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, - 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, - - 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, - 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, - 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, - 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, - - 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, - 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, - 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, - 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, - - 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, - 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, - 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, - 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, -}; - -static unsigned short init2[128] = { - 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, - 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, - 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, - 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, - - 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, - 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, - 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, - 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, - - 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, - 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, - 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, - 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, - - 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, - 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, - 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, - 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, -}; - -static unsigned short init3[128] = { - 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, - 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, - 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, - 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, - - 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, - 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, - 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, - 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, - - 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, - 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, - 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, - 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, - - 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, - 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, - 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, - 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, -}; - -static unsigned short init4[128] = { - 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, - 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, - 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, - 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, - - 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, - 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, - 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, - 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, - - 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, - 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, - 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, - 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, - - 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, - 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, - 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, - 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, -}; - - -/* send initialization arrays to start up */ -static void -awe_init_array(void) -{ - awe_send_array(init1); - awe_wait(1024); - awe_send_array(init2); - awe_send_array(init3); - awe_poke_dw(AWE_HWCF4, 0); - awe_poke_dw(AWE_HWCF5, 0x83); - awe_poke_dw(AWE_HWCF6, 0x8000); - awe_send_array(init4); -} - -/* send an initialization array */ -static void -awe_send_array(unsigned short *data) -{ - int i; - unsigned short *p; - - p = data; - for (i = 0; i < AWE_MAX_VOICES; i++, p++) - awe_poke(AWE_INIT1(i), *p); - for (i = 0; i < AWE_MAX_VOICES; i++, p++) - awe_poke(AWE_INIT2(i), *p); - for (i = 0; i < AWE_MAX_VOICES; i++, p++) - awe_poke(AWE_INIT3(i), *p); - for (i = 0; i < AWE_MAX_VOICES; i++, p++) - awe_poke(AWE_INIT4(i), *p); -} - - -/* - * set up awe32 channels to some known state. - */ - -/* set the envelope & LFO parameters to the default values; see ADIP */ -static void -awe_tweak_voice(int i) -{ - /* set all mod/vol envelope shape to minimum */ - awe_poke(AWE_ENVVOL(i), 0x8000); - awe_poke(AWE_ENVVAL(i), 0x8000); - awe_poke(AWE_DCYSUS(i), 0x7F7F); - awe_poke(AWE_ATKHLDV(i), 0x7F7F); - awe_poke(AWE_ATKHLD(i), 0x7F7F); - awe_poke(AWE_PEFE(i), 0); /* mod envelope height to zero */ - awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */ - awe_poke(AWE_LFO2VAL(i), 0x8000); - awe_poke(AWE_IP(i), 0xE000); /* no pitch shift */ - awe_poke(AWE_IFATN(i), 0xFF00); /* volume to minimum */ - awe_poke(AWE_FMMOD(i), 0); - awe_poke(AWE_TREMFRQ(i), 0); - awe_poke(AWE_FM2FRQ2(i), 0); -} - -static void -awe_tweak(void) -{ - int i; - /* reset all channels */ - for (i = 0; i < awe_max_voices; i++) - awe_tweak_voice(i); -} - - -/* - * initializes the FM section of AWE32; - * see Vince Vu's unofficial AWE32 programming guide - */ - -static void -awe_init_fm(void) -{ -#ifndef AWE_ALWAYS_INIT_FM - /* if no extended memory is on board.. */ - if (memsize <= 0) - return; -#endif - DEBUG(3,printk("AWE32: initializing FM\n")); - - /* Initialize the last two channels for DRAM refresh and producing - the reverb and chorus effects for Yamaha OPL-3 synthesizer */ - - /* 31: FM left channel, 0xffffe0-0xffffe8 */ - awe_poke(AWE_DCYSUSV(30), 0x80); - awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */ - awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 | - (DEF_FM_CHORUS_DEPTH << 24)); - awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8)); - awe_poke_dw(AWE_CPF(30), 0); - awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3); - - /* 32: FM right channel, 0xfffff0-0xfffff8 */ - awe_poke(AWE_DCYSUSV(31), 0x80); - awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */ - awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 | - (DEF_FM_CHORUS_DEPTH << 24)); - awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8)); - awe_poke_dw(AWE_CPF(31), 0x8000); - awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3); - - /* skew volume & cutoff */ - awe_poke_dw(AWE_VTFT(30), 0x8000FFFF); - awe_poke_dw(AWE_VTFT(31), 0x8000FFFF); - - voices[30].state = AWE_ST_FM; - voices[31].state = AWE_ST_FM; - - /* change maximum channels to 30 */ - awe_max_voices = AWE_NORMAL_VOICES; - if (playing_mode == AWE_PLAY_DIRECT) - awe_info.nr_voices = awe_max_voices; - else - awe_info.nr_voices = AWE_MAX_CHANNELS; - voice_alloc->max_voice = awe_max_voices; -} - -/* - * AWE32 DRAM access routines - */ - -/* open DRAM write accessing mode */ -static int -awe_open_dram_for_write(int offset, int channels) -{ - int vidx[AWE_NORMAL_VOICES]; - int i; - - if (channels < 0 || channels >= AWE_NORMAL_VOICES) { - channels = AWE_NORMAL_VOICES; - for (i = 0; i < AWE_NORMAL_VOICES; i++) - vidx[i] = i; - } else { - for (i = 0; i < channels; i++) { - vidx[i] = awe_clear_voice(); - voices[vidx[i]].state = AWE_ST_MARK; - } - } - - /* use all channels for DMA transfer */ - for (i = 0; i < channels; i++) { - if (vidx[i] < 0) continue; - awe_poke(AWE_DCYSUSV(vidx[i]), 0x80); - awe_poke_dw(AWE_VTFT(vidx[i]), 0); - awe_poke_dw(AWE_CVCF(vidx[i]), 0); - awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000); - awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000); - awe_poke_dw(AWE_PSST(vidx[i]), 0); - awe_poke_dw(AWE_CSL(vidx[i]), 0); - awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000); - voices[vidx[i]].state = AWE_ST_DRAM; - } - /* point channels 31 & 32 to ROM samples for DRAM refresh */ - awe_poke_dw(AWE_VTFT(30), 0); - awe_poke_dw(AWE_PSST(30), 0x1d8); - awe_poke_dw(AWE_CSL(30), 0x1e0); - awe_poke_dw(AWE_CCCA(30), 0x1d8); - awe_poke_dw(AWE_VTFT(31), 0); - awe_poke_dw(AWE_PSST(31), 0x1d8); - awe_poke_dw(AWE_CSL(31), 0x1e0); - awe_poke_dw(AWE_CCCA(31), 0x1d8); - voices[30].state = AWE_ST_FM; - voices[31].state = AWE_ST_FM; - - /* if full bit is on, not ready to write on */ - if (awe_peek_dw(AWE_SMALW) & 0x80000000) { - for (i = 0; i < channels; i++) { - awe_poke_dw(AWE_CCCA(vidx[i]), 0); - voices[vidx[i]].state = AWE_ST_OFF; - } - printk("awe: not ready to write..\n"); - return -EPERM; - } - - /* set address to write */ - awe_poke_dw(AWE_SMALW, offset); - - return 0; -} - -/* open DRAM for RAM size detection */ -static void -awe_open_dram_for_check(void) -{ - int i; - for (i = 0; i < AWE_NORMAL_VOICES; i++) { - awe_poke(AWE_DCYSUSV(i), 0x80); - awe_poke_dw(AWE_VTFT(i), 0); - awe_poke_dw(AWE_CVCF(i), 0); - awe_poke_dw(AWE_PTRX(i), 0x40000000); - awe_poke_dw(AWE_CPF(i), 0x40000000); - awe_poke_dw(AWE_PSST(i), 0); - awe_poke_dw(AWE_CSL(i), 0); - if (i & 1) /* DMA write */ - awe_poke_dw(AWE_CCCA(i), 0x06000000); - else /* DMA read */ - awe_poke_dw(AWE_CCCA(i), 0x04000000); - voices[i].state = AWE_ST_DRAM; - } -} - - -/* close dram access */ -static void -awe_close_dram(void) -{ - int i; - /* wait until FULL bit in SMAxW register be false */ - for (i = 0; i < 10000; i++) { - if (!(awe_peek_dw(AWE_SMALW) & 0x80000000)) - break; - awe_wait(10); - } - - for (i = 0; i < AWE_NORMAL_VOICES; i++) { - if (voices[i].state == AWE_ST_DRAM) { - awe_poke_dw(AWE_CCCA(i), 0); - awe_poke(AWE_DCYSUSV(i), 0x807F); - voices[i].state = AWE_ST_OFF; - } - } -} - - -/* - * detect presence of AWE32 and check memory size - */ - -/* detect emu8000 chip on the specified address; from VV's guide */ - -static int __init -awe_detect_base(int addr) -{ - setup_ports(addr, 0, 0); - if ((awe_peek(AWE_U1) & 0x000F) != 0x000C) - return 0; - if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058) - return 0; - if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003) - return 0; - DEBUG(0,printk("AWE32 found at %x\n", addr)); - return 1; -} - -#ifdef __ISAPNP__ -static struct { - unsigned short card_vendor, card_device; - unsigned short vendor; - unsigned short function; - char *name; -} isapnp_awe_list[] __initdata = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0021), - "AWE32 WaveTable" }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0022), - "AWE64 WaveTable" }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0023), - "AWE64 Gold WaveTable" }, - {0} -}; - -MODULE_DEVICE_TABLE(isapnp, isapnp_awe_list); - -static struct pci_dev *idev = NULL; - -static int __init awe_probe_isapnp(int *port) -{ - int i; - - for (i = 0; isapnp_awe_list[i].vendor != 0; i++) { - while ((idev = isapnp_find_dev(NULL, - isapnp_awe_list[i].vendor, - isapnp_awe_list[i].function, - idev))) { - if (idev->prepare(idev) < 0) - continue; - if (idev->activate(idev) < 0 || - !idev->resource[0].start) { - idev->deactivate(idev); - idev->deactivate(idev); - continue; - } - *port = idev->resource[0].start; - break; - } - if (!idev) - continue; - printk(KERN_INFO "ISAPnP reports %s at i/o %#x\n", - isapnp_awe_list[i].name, *port); - return 0; - } - return -ENODEV; -} - -static void __exit awe_deactivate_isapnp(void) -{ -#if 1 - if (idev) { - idev->deactivate(idev); - idev = NULL; - } -#endif -} - -#endif - -static int __init -awe_detect(void) -{ - int base; - -#ifdef __ISAPNP__ - if (isapnp) { - if (awe_probe_isapnp(&io) < 0) { - printk(KERN_ERR "AWE32: No ISAPnP cards found\n"); - if (isapnp != -1) - return 0; - } else { - setup_ports(io, 0, 0); - return 1; - } - } -#endif /* isapnp */ - - if (io) /* use default i/o port value */ - setup_ports(io, 0, 0); - else { /* probe it */ - for (base = 0x620; base <= 0x680; base += 0x20) - if (awe_detect_base(base)) - return 1; - DEBUG(0,printk("AWE32 not found\n")); - return 0; - } - - return 1; -} - - -/* - * check dram size on AWE board - */ - -/* any three numbers you like */ -#define UNIQUE_ID1 0x1234 -#define UNIQUE_ID2 0x4321 -#define UNIQUE_ID3 0xABCD - -static void __init -awe_check_dram(void) -{ - if (awe_present) /* already initialized */ - return; - - if (memsize >= 0) { /* given by config file or module option */ - memsize *= 1024; /* convert to Kbytes */ - return; - } - - awe_open_dram_for_check(); - - memsize = 0; - - /* set up unique two id numbers */ - awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET); - awe_poke(AWE_SMLD, UNIQUE_ID1); - awe_poke(AWE_SMLD, UNIQUE_ID2); - - while (memsize < AWE_MAX_DRAM_SIZE) { - awe_wait(5); - /* read a data on the DRAM start address */ - awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET); - awe_peek(AWE_SMLD); /* discard stale data */ - if (awe_peek(AWE_SMLD) != UNIQUE_ID1) - break; - if (awe_peek(AWE_SMLD) != UNIQUE_ID2) - break; - memsize += 512; /* increment 512kbytes */ - /* Write a unique data on the test address; - * if the address is out of range, the data is written on - * 0x200000(=AWE_DRAM_OFFSET). Then the two id words are - * broken by this data. - */ - awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + memsize*512L); - awe_poke(AWE_SMLD, UNIQUE_ID3); - awe_wait(5); - /* read a data on the just written DRAM address */ - awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + memsize*512L); - awe_peek(AWE_SMLD); /* discard stale data */ - if (awe_peek(AWE_SMLD) != UNIQUE_ID3) - break; - } - awe_close_dram(); - - DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", memsize)); - - /* convert to Kbytes */ - memsize *= 1024; -} - - -/*----------------------------------------------------------------*/ - -/* - * chorus and reverb controls; from VV's guide - */ - -/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ -static char chorus_defined[AWE_CHORUS_NUMBERS]; -static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = { - {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ - {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ - {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ - {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ - {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ - {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ - {0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */ - {0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */ -}; - -static int -awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count) -{ - if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) { - printk(KERN_WARNING "AWE32 Error: invalid chorus mode %d for uploading\n", patch->optarg); - return -EINVAL; - } - if (count < sizeof(awe_chorus_fx_rec)) { - printk(KERN_WARNING "AWE32 Error: too short chorus fx parameters\n"); - return -EINVAL; - } - if (copy_from_user(&chorus_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE, - sizeof(awe_chorus_fx_rec))) - return -EFAULT; - chorus_defined[patch->optarg] = TRUE; - return 0; -} - -static void -awe_set_chorus_mode(int effect) -{ - if (effect < 0 || effect >= AWE_CHORUS_NUMBERS || - (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect])) - return; - awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback); - awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset); - awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth); - awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay); - awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq); - awe_poke_dw(AWE_HWCF6, 0x8000); - awe_poke_dw(AWE_HWCF7, 0x0000); -} - -static void -awe_update_chorus_mode(void) -{ - awe_set_chorus_mode(ctrls[AWE_MD_CHORUS_MODE]); -} - -/*----------------------------------------------------------------*/ - -/* reverb mode settings; write the following 28 data of 16 bit length - * on the corresponding ports in the reverb_cmds array - */ -static char reverb_defined[AWE_CHORUS_NUMBERS]; -static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = { -{{ /* room 1 */ - 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, - 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, - 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, - 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, -}}, -{{ /* room 2 */ - 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, - 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, - 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, - 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, -}}, -{{ /* room 3 */ - 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, - 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, - 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, - 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, -}}, -{{ /* hall 1 */ - 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, - 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, - 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, - 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, -}}, -{{ /* hall 2 */ - 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, - 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, - 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, - 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, -}}, -{{ /* plate */ - 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, - 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, - 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, - 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, -}}, -{{ /* delay */ - 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, - 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, - 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, - 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, -}}, -{{ /* panning delay */ - 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, - 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, - 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, - 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, -}}, -}; - -static struct ReverbCmdPair { - unsigned short cmd, port; -} reverb_cmds[28] = { - {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)}, - {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)}, - {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)}, - {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)}, - {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)}, - {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)}, - {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, -}; - -static int -awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count) -{ - if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) { - printk(KERN_WARNING "AWE32 Error: invalid reverb mode %d for uploading\n", patch->optarg); - return -EINVAL; - } - if (count < sizeof(awe_reverb_fx_rec)) { - printk(KERN_WARNING "AWE32 Error: too short reverb fx parameters\n"); - return -EINVAL; - } - if (copy_from_user(&reverb_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE, - sizeof(awe_reverb_fx_rec))) - return -EFAULT; - reverb_defined[patch->optarg] = TRUE; - return 0; -} - -static void -awe_set_reverb_mode(int effect) -{ - int i; - if (effect < 0 || effect >= AWE_REVERB_NUMBERS || - (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect])) - return; - for (i = 0; i < 28; i++) - awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port, - reverb_parm[effect].parms[i]); -} - -static void -awe_update_reverb_mode(void) -{ - awe_set_reverb_mode(ctrls[AWE_MD_REVERB_MODE]); -} - -/* - * treble/bass equalizer control - */ - -static unsigned short bass_parm[12][3] = { - {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ - {0xD25B, 0xD35B, 0x0000}, /* -8 */ - {0xD24C, 0xD34C, 0x0000}, /* -6 */ - {0xD23D, 0xD33D, 0x0000}, /* -4 */ - {0xD21F, 0xD31F, 0x0000}, /* -2 */ - {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ - {0xC219, 0xC319, 0x0001}, /* +2 */ - {0xC22A, 0xC32A, 0x0001}, /* +4 */ - {0xC24C, 0xC34C, 0x0001}, /* +6 */ - {0xC26E, 0xC36E, 0x0001}, /* +8 */ - {0xC248, 0xC348, 0x0002}, /* +10 */ - {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ -}; - -static unsigned short treble_parm[12][9] = { - {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ - {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, - {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, - {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, - {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, - {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, - {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, - {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, - {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, - {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ - {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, - {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */ -}; - - -/* - * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] - */ -static void -awe_equalizer(int bass, int treble) -{ - unsigned short w; - - if (bass < 0 || bass > 11 || treble < 0 || treble > 11) - return; - awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]); - awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]); - awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]); - awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]); - awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]); - awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]); - awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]); - awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]); - awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]); - awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]); - w = bass_parm[bass][2] + treble_parm[treble][8]; - awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262)); - awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362)); -} - -static void awe_update_equalizer(void) -{ - awe_equalizer(ctrls[AWE_MD_BASS_LEVEL], ctrls[AWE_MD_TREBLE_LEVEL]); -} - - -/*----------------------------------------------------------------*/ - -#ifdef CONFIG_AWE32_MIDIEMU - -/* - * Emu8000 MIDI Emulation - */ - -/* - * midi queue record - */ - -/* queue type */ -enum { Q_NONE, Q_VARLEN, Q_READ, Q_SYSEX, }; - -#define MAX_MIDIBUF 64 - -/* midi status */ -typedef struct MidiStatus { - int queue; /* queue type */ - int qlen; /* queue length */ - int read; /* chars read */ - int status; /* current status */ - int chan; /* current channel */ - unsigned char buf[MAX_MIDIBUF]; -} MidiStatus; - -/* MIDI mode type */ -enum { MODE_GM, MODE_GS, MODE_XG, }; - -/* NRPN / CC -> Emu8000 parameter converter */ -typedef struct { - int control; - int awe_effect; - unsigned short (*convert)(int val); -} ConvTable; - - -/* - * prototypes - */ - -static int awe_midi_open(int dev, int mode, void (*input)(int,unsigned char), void (*output)(int)); -static void awe_midi_close(int dev); -static int awe_midi_ioctl(int dev, unsigned cmd, caddr_t arg); -static int awe_midi_outputc(int dev, unsigned char midi_byte); - -static void init_midi_status(MidiStatus *st); -static void clear_rpn(void); -static void get_midi_char(MidiStatus *st, int c); -/*static void queue_varlen(MidiStatus *st, int c);*/ -static void special_event(MidiStatus *st, int c); -static void queue_read(MidiStatus *st, int c); -static void midi_note_on(MidiStatus *st); -static void midi_note_off(MidiStatus *st); -static void midi_key_pressure(MidiStatus *st); -static void midi_channel_pressure(MidiStatus *st); -static void midi_pitch_wheel(MidiStatus *st); -static void midi_program_change(MidiStatus *st); -static void midi_control_change(MidiStatus *st); -static void midi_select_bank(MidiStatus *st, int val); -static void midi_nrpn_event(MidiStatus *st); -static void midi_rpn_event(MidiStatus *st); -static void midi_detune(int chan, int coarse, int fine); -static void midi_system_exclusive(MidiStatus *st); -static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val); -static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val); -static int xg_control_change(MidiStatus *st, int cmd, int val); - -#define numberof(ary) (sizeof(ary)/sizeof(ary[0])) - - -/* - * OSS Midi device record - */ - -static struct midi_operations awe_midi_operations = -{ - owner: THIS_MODULE, - info: {"AWE Midi Emu", 0, 0, SNDCARD_SB}, - in_info: {0}, - open: awe_midi_open, /*open*/ - close: awe_midi_close, /*close*/ - ioctl: awe_midi_ioctl, /*ioctl*/ - outputc: awe_midi_outputc, /*outputc*/ -}; - -static int my_mididev = -1; - -static void __init attach_midiemu(void) -{ - if ((my_mididev = sound_alloc_mididev()) < 0) - printk ("Sound: Too many midi devices detected\n"); - else - midi_devs[my_mididev] = &awe_midi_operations; -} - -static void __exit unload_midiemu(void) -{ - if (my_mididev >= 0) - sound_unload_mididev(my_mididev); -} - - -/* - * open/close midi device - */ - -static int midi_opened = FALSE; - -static int midi_mode; -static int coarsetune = 0, finetune = 0; - -static int xg_mapping = TRUE; -static int xg_bankmode = 0; - -/* effect sensitivity */ - -#define FX_CUTOFF 0 -#define FX_RESONANCE 1 -#define FX_ATTACK 2 -#define FX_RELEASE 3 -#define FX_VIBRATE 4 -#define FX_VIBDEPTH 5 -#define FX_VIBDELAY 6 -#define FX_NUMS 7 - -#define DEF_FX_CUTOFF 170 -#define DEF_FX_RESONANCE 6 -#define DEF_FX_ATTACK 50 -#define DEF_FX_RELEASE 50 -#define DEF_FX_VIBRATE 30 -#define DEF_FX_VIBDEPTH 4 -#define DEF_FX_VIBDELAY 1500 - -/* effect sense: */ -static int gs_sense[] = -{ - DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, - DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY -}; -static int xg_sense[] = -{ - DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, - DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY -}; - - -/* current status */ -static MidiStatus curst; - - -static int -awe_midi_open (int dev, int mode, - void (*input)(int,unsigned char), - void (*output)(int)) -{ - if (midi_opened) - return -EBUSY; - - midi_opened = TRUE; - - midi_mode = MODE_GM; - - curst.queue = Q_NONE; - curst.qlen = 0; - curst.read = 0; - curst.status = 0; - curst.chan = 0; - memset(curst.buf, 0, sizeof(curst.buf)); - - init_midi_status(&curst); - - return 0; -} - -static void -awe_midi_close (int dev) -{ - midi_opened = FALSE; -} - - -static int -awe_midi_ioctl (int dev, unsigned cmd, caddr_t arg) -{ - return -EPERM; -} - -static int -awe_midi_outputc (int dev, unsigned char midi_byte) -{ - if (! midi_opened) - return 1; - - /* force to change playing mode */ - playing_mode = AWE_PLAY_MULTI; - - get_midi_char(&curst, midi_byte); - return 1; -} - - -/* - * initialize - */ - -static void init_midi_status(MidiStatus *st) -{ - clear_rpn(); - coarsetune = 0; - finetune = 0; -} - - -/* - * RPN & NRPN - */ - -#define MAX_MIDI_CHANNELS 16 - -/* RPN & NRPN */ -static unsigned char nrpn[MAX_MIDI_CHANNELS]; /* current event is NRPN? */ -static int msb_bit; /* current event is msb for RPN/NRPN */ -/* RPN & NRPN indeces */ -static unsigned char rpn_msb[MAX_MIDI_CHANNELS], rpn_lsb[MAX_MIDI_CHANNELS]; -/* RPN & NRPN values */ -static int rpn_val[MAX_MIDI_CHANNELS]; - -static void clear_rpn(void) -{ - int i; - for (i = 0; i < MAX_MIDI_CHANNELS; i++) { - nrpn[i] = 0; - rpn_msb[i] = 127; - rpn_lsb[i] = 127; - rpn_val[i] = 0; - } - msb_bit = 0; -} - - -/* - * process midi queue - */ - -/* status event types */ -typedef void (*StatusEvent)(MidiStatus *st); -static struct StatusEventList { - StatusEvent process; - int qlen; -} status_event[8] = { - {midi_note_off, 2}, - {midi_note_on, 2}, - {midi_key_pressure, 2}, - {midi_control_change, 2}, - {midi_program_change, 1}, - {midi_channel_pressure, 1}, - {midi_pitch_wheel, 2}, - {NULL, 0}, -}; - - -/* read a char from fifo and process it */ -static void get_midi_char(MidiStatus *st, int c) -{ - if (c == 0xfe) { - /* ignore active sense */ - st->queue = Q_NONE; - return; - } - - switch (st->queue) { - /* case Q_VARLEN: queue_varlen(st, c); break;*/ - case Q_READ: - case Q_SYSEX: - queue_read(st, c); - break; - case Q_NONE: - st->read = 0; - if ((c & 0xf0) == 0xf0) { - special_event(st, c); - } else if (c & 0x80) { /* status change */ - st->status = (c >> 4) & 0x07; - st->chan = c & 0x0f; - st->queue = Q_READ; - st->qlen = status_event[st->status].qlen; - if (st->qlen == 0) - st->queue = Q_NONE; - } - break; - } -} - -/* 0xfx events */ -static void special_event(MidiStatus *st, int c) -{ - switch (c) { - case 0xf0: /* system exclusive */ - st->queue = Q_SYSEX; - st->qlen = 0; - break; - case 0xf1: /* MTC quarter frame */ - case 0xf3: /* song select */ - st->queue = Q_READ; - st->qlen = 1; - break; - case 0xf2: /* song position */ - st->queue = Q_READ; - st->qlen = 2; - break; - } -} - -#if 0 -/* read variable length value */ -static void queue_varlen(MidiStatus *st, int c) -{ - st->qlen += (c & 0x7f); - if (c & 0x80) { - st->qlen <<= 7; - return; - } - if (st->qlen <= 0) { - st->qlen = 0; - st->queue = Q_NONE; - } - st->queue = Q_READ; - st->read = 0; -} -#endif - - -/* read a char */ -static void queue_read(MidiStatus *st, int c) -{ - if (st->read < MAX_MIDIBUF) { - if (st->queue != Q_SYSEX) - c &= 0x7f; - st->buf[st->read] = (unsigned char)c; - } - st->read++; - if (st->queue == Q_SYSEX && c == 0xf7) { - midi_system_exclusive(st); - st->queue = Q_NONE; - } else if (st->queue == Q_READ && st->read >= st->qlen) { - if (status_event[st->status].process) - status_event[st->status].process(st); - st->queue = Q_NONE; - } -} - - -/* - * status events - */ - -/* note on */ -static void midi_note_on(MidiStatus *st) -{ - DEBUG(2,printk("midi: note_on (%d) %d %d\n", st->chan, st->buf[0], st->buf[1])); - if (st->buf[1] == 0) - midi_note_off(st); - else - awe_start_note(0, st->chan, st->buf[0], st->buf[1]); -} - -/* note off */ -static void midi_note_off(MidiStatus *st) -{ - DEBUG(2,printk("midi: note_off (%d) %d %d\n", st->chan, st->buf[0], st->buf[1])); - awe_kill_note(0, st->chan, st->buf[0], st->buf[1]); -} - -/* key pressure change */ -static void midi_key_pressure(MidiStatus *st) -{ - awe_key_pressure(0, st->chan, st->buf[0], st->buf[1]); -} - -/* channel pressure change */ -static void midi_channel_pressure(MidiStatus *st) -{ - channels[st->chan].chan_press = st->buf[0]; - awe_modwheel_change(st->chan, st->buf[0]); -} - -/* pitch wheel change */ -static void midi_pitch_wheel(MidiStatus *st) -{ - int val = (int)st->buf[1] * 128 + st->buf[0]; - awe_bender(0, st->chan, val); -} - -/* program change */ -static void midi_program_change(MidiStatus *st) -{ - int preset; - preset = st->buf[0]; - if (midi_mode == MODE_GS && IS_DRUM_CHANNEL(st->chan) && preset == 127) - preset = 0; - else if (midi_mode == MODE_XG && xg_mapping && IS_DRUM_CHANNEL(st->chan)) - preset += 64; - - awe_set_instr(0, st->chan, preset); -} - -#define send_effect(chan,type,val) awe_send_effect(chan,-1,type,val) -#define add_effect(chan,type,val) awe_send_effect(chan,-1,(type)|0x80,val) -#define unset_effect(chan,type) awe_send_effect(chan,-1,(type)|0x40,0) - -/* midi control change */ -static void midi_control_change(MidiStatus *st) -{ - int cmd = st->buf[0]; - int val = st->buf[1]; - - DEBUG(2,printk("midi: control (%d) %d %d\n", st->chan, cmd, val)); - if (midi_mode == MODE_XG) { - if (xg_control_change(st, cmd, val)) - return; - } - - /* controls #31 - #64 are LSB of #0 - #31 */ - msb_bit = 1; - if (cmd >= 0x20 && cmd < 0x40) { - msb_bit = 0; - cmd -= 0x20; - } - - switch (cmd) { - case CTL_SOFT_PEDAL: - if (val == 127) - add_effect(st->chan, AWE_FX_CUTOFF, -160); - else - unset_effect(st->chan, AWE_FX_CUTOFF); - break; - - case CTL_BANK_SELECT: - midi_select_bank(st, val); - break; - - /* set RPN/NRPN parameter */ - case CTL_REGIST_PARM_NUM_MSB: - nrpn[st->chan]=0; rpn_msb[st->chan]=val; - break; - case CTL_REGIST_PARM_NUM_LSB: - nrpn[st->chan]=0; rpn_lsb[st->chan]=val; - break; - case CTL_NONREG_PARM_NUM_MSB: - nrpn[st->chan]=1; rpn_msb[st->chan]=val; - break; - case CTL_NONREG_PARM_NUM_LSB: - nrpn[st->chan]=1; rpn_lsb[st->chan]=val; - break; - - /* send RPN/NRPN entry */ - case CTL_DATA_ENTRY: - if (msb_bit) - rpn_val[st->chan] = val * 128; - else - rpn_val[st->chan] |= val; - if (nrpn[st->chan]) - midi_nrpn_event(st); - else - midi_rpn_event(st); - break; - - /* increase/decrease data entry */ - case CTL_DATA_INCREMENT: - rpn_val[st->chan]++; - midi_rpn_event(st); - break; - case CTL_DATA_DECREMENT: - rpn_val[st->chan]--; - midi_rpn_event(st); - break; - - /* default */ - default: - awe_controller(0, st->chan, cmd, val); - break; - } -} - -/* tone bank change */ -static void midi_select_bank(MidiStatus *st, int val) -{ - if (midi_mode == MODE_XG && msb_bit) { - xg_bankmode = val; - /* XG MSB value; not normal bank selection */ - switch (val) { - case 127: /* remap to drum channel */ - awe_controller(0, st->chan, CTL_BANK_SELECT, 128); - break; - default: /* remap to normal channel */ - awe_controller(0, st->chan, CTL_BANK_SELECT, val); - break; - } - return; - } else if (midi_mode == MODE_GS && !msb_bit) - /* ignore LSB bank in GS mode (used for mapping) */ - return; - - /* normal bank controls; accept both MSB and LSB */ - if (! IS_DRUM_CHANNEL(st->chan)) { - if (midi_mode == MODE_XG) { - if (xg_bankmode) return; - if (val == 64 || val == 126) - val = 0; - } else if (midi_mode == MODE_GS && val == 127) - val = 0; - awe_controller(0, st->chan, CTL_BANK_SELECT, val); - } -} - - -/* - * RPN events - */ - -static void midi_rpn_event(MidiStatus *st) -{ - int type; - type = (rpn_msb[st->chan]<<8) | rpn_lsb[st->chan]; - switch (type) { - case 0x0000: /* Pitch bend sensitivity */ - /* MSB only / 1 semitone per 128 */ - if (msb_bit) { - channels[st->chan].bender_range = - rpn_val[st->chan] * 100 / 128; - } - break; - - case 0x0001: /* fine tuning: */ - /* MSB/LSB, 8192=center, 100/8192 cent step */ - finetune = rpn_val[st->chan] - 8192; - midi_detune(st->chan, coarsetune, finetune); - break; - - case 0x0002: /* coarse tuning */ - /* MSB only / 8192=center, 1 semitone per 128 */ - if (msb_bit) { - coarsetune = rpn_val[st->chan] - 8192; - midi_detune(st->chan, coarsetune, finetune); - } - break; - - case 0x7F7F: /* "lock-in" RPN */ - break; - } -} - - -/* tuning: - * coarse = -8192 to 8192 (100 cent per 128) - * fine = -8192 to 8192 (max=100cent) - */ -static void midi_detune(int chan, int coarse, int fine) -{ - /* 4096 = 1200 cents in AWE parameter */ - int val; - val = coarse * 4096 / (12 * 128); - val += fine / 24; - if (val) - send_effect(chan, AWE_FX_INIT_PITCH, val); - else - unset_effect(chan, AWE_FX_INIT_PITCH); -} - - -/* - * system exclusive message - * GM/GS/XG macros are accepted - */ - -static void midi_system_exclusive(MidiStatus *st) -{ - /* GM on */ - static unsigned char gm_on_macro[] = { - 0x7e,0x7f,0x09,0x01, - }; - /* XG on */ - static unsigned char xg_on_macro[] = { - 0x43,0x10,0x4c,0x00,0x00,0x7e,0x00, - }; - /* GS prefix - * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off - * reverb mode: XX=0x01, YY=0x30, ZZ=0-7 - * chorus mode: XX=0x01, YY=0x38, ZZ=0-7 - */ - static unsigned char gs_pfx_macro[] = { - 0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/ - }; - -#if 0 - /* SC88 system mode set - * single module mode: XX=1 - * double module mode: XX=0 - */ - static unsigned char gs_mode_macro[] = { - 0x41,0x10,0x42,0x12,0x00,0x00,0x7F,/*ZZ*/ - }; - /* SC88 display macro: XX=01:bitmap, 00:text - */ - static unsigned char gs_disp_macro[] = { - 0x41,0x10,0x45,0x12,0x10,/*XX,00*/ - }; -#endif - - /* GM on */ - if (memcmp(st->buf, gm_on_macro, sizeof(gm_on_macro)) == 0) { - if (midi_mode != MODE_GS && midi_mode != MODE_XG) - midi_mode = MODE_GM; - init_midi_status(st); - } - - /* GS macros */ - else if (memcmp(st->buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) { - if (midi_mode != MODE_GS && midi_mode != MODE_XG) - midi_mode = MODE_GS; - - if (st->buf[5] == 0x00 && st->buf[6] == 0x7f && st->buf[7] == 0x00) { - /* GS reset */ - init_midi_status(st); - } - - else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x15) { - /* drum pattern */ - int p = st->buf[5] & 0x0f; - if (p == 0) p = 9; - else if (p < 10) p--; - if (st->buf[7] == 0) - DRUM_CHANNEL_OFF(p); - else - DRUM_CHANNEL_ON(p); - - } else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x21) { - /* program */ - int p = st->buf[5] & 0x0f; - if (p == 0) p = 9; - else if (p < 10) p--; - if (! IS_DRUM_CHANNEL(p)) - awe_set_instr(0, p, st->buf[7]); - - } else if (st->buf[5] == 0x01 && st->buf[6] == 0x30) { - /* reverb mode */ - awe_set_reverb_mode(st->buf[7]); - - } else if (st->buf[5] == 0x01 && st->buf[6] == 0x38) { - /* chorus mode */ - awe_set_chorus_mode(st->buf[7]); - - } else if (st->buf[5] == 0x00 && st->buf[6] == 0x04) { - /* master volume */ - awe_change_master_volume(st->buf[7]); - - } - } - - /* XG on */ - else if (memcmp(st->buf, xg_on_macro, sizeof(xg_on_macro)) == 0) { - midi_mode = MODE_XG; - xg_mapping = TRUE; - xg_bankmode = 0; - } -} - - -/*----------------------------------------------------------------*/ - -/* - * convert NRPN/control values - */ - -static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val) -{ - int i, cval; - for (i = 0; i < num_tables; i++) { - if (table[i].control == type) { - cval = table[i].convert(val); - send_effect(st->chan, table[i].awe_effect, cval); - return TRUE; - } - } - return FALSE; -} - -static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val) -{ - int i, cval; - for (i = 0; i < num_tables; i++) { - if (table[i].control == type) { - cval = table[i].convert(val); - add_effect(st->chan, table[i].awe_effect|0x80, cval); - return TRUE; - } - } - return FALSE; -} - - -/* - * AWE32 NRPN effects - */ - -static unsigned short fx_delay(int val); -static unsigned short fx_attack(int val); -static unsigned short fx_hold(int val); -static unsigned short fx_decay(int val); -static unsigned short fx_the_value(int val); -static unsigned short fx_twice_value(int val); -static unsigned short fx_conv_pitch(int val); -static unsigned short fx_conv_Q(int val); - -/* function for each NRPN */ /* [range] units */ -#define fx_env1_delay fx_delay /* [0,5900] 4msec */ -#define fx_env1_attack fx_attack /* [0,5940] 1msec */ -#define fx_env1_hold fx_hold /* [0,8191] 1msec */ -#define fx_env1_decay fx_decay /* [0,5940] 4msec */ -#define fx_env1_release fx_decay /* [0,5940] 4msec */ -#define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */ -#define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */ -#define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */ - -#define fx_env2_delay fx_delay /* [0,5900] 4msec */ -#define fx_env2_attack fx_attack /* [0,5940] 1msec */ -#define fx_env2_hold fx_hold /* [0,8191] 1msec */ -#define fx_env2_decay fx_decay /* [0,5940] 4msec */ -#define fx_env2_release fx_decay /* [0,5940] 4msec */ -#define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */ - -#define fx_lfo1_delay fx_delay /* [0,5900] 4msec */ -#define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */ -#define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */ -#define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */ -#define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */ - -#define fx_lfo2_delay fx_delay /* [0,5900] 4msec */ -#define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */ -#define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */ - -#define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */ -#define fx_chorus fx_the_value /* [0,255] -- */ -#define fx_reverb fx_the_value /* [0,255] -- */ -#define fx_cutoff fx_twice_value /* [0,127] 62Hz */ -#define fx_filterQ fx_conv_Q /* [0,127] -- */ - -static unsigned short fx_delay(int val) -{ - return (unsigned short)calc_parm_delay(val); -} - -static unsigned short fx_attack(int val) -{ - return (unsigned short)calc_parm_attack(val); -} - -static unsigned short fx_hold(int val) -{ - return (unsigned short)calc_parm_hold(val); -} - -static unsigned short fx_decay(int val) -{ - return (unsigned short)calc_parm_decay(val); -} - -static unsigned short fx_the_value(int val) -{ - return (unsigned short)(val & 0xff); -} - -static unsigned short fx_twice_value(int val) -{ - return (unsigned short)((val * 2) & 0xff); -} - -static unsigned short fx_conv_pitch(int val) -{ - return (short)(val * 4096 / 1200); -} - -static unsigned short fx_conv_Q(int val) -{ - return (unsigned short)((val / 8) & 0xff); -} - - -static ConvTable awe_effects[] = -{ - { 0, AWE_FX_LFO1_DELAY, fx_lfo1_delay}, - { 1, AWE_FX_LFO1_FREQ, fx_lfo1_freq}, - { 2, AWE_FX_LFO2_DELAY, fx_lfo2_delay}, - { 3, AWE_FX_LFO2_FREQ, fx_lfo2_freq}, - - { 4, AWE_FX_ENV1_DELAY, fx_env1_delay}, - { 5, AWE_FX_ENV1_ATTACK,fx_env1_attack}, - { 6, AWE_FX_ENV1_HOLD, fx_env1_hold}, - { 7, AWE_FX_ENV1_DECAY, fx_env1_decay}, - { 8, AWE_FX_ENV1_SUSTAIN, fx_env1_sustain}, - { 9, AWE_FX_ENV1_RELEASE, fx_env1_release}, - - {10, AWE_FX_ENV2_DELAY, fx_env2_delay}, - {11, AWE_FX_ENV2_ATTACK, fx_env2_attack}, - {12, AWE_FX_ENV2_HOLD, fx_env2_hold}, - {13, AWE_FX_ENV2_DECAY, fx_env2_decay}, - {14, AWE_FX_ENV2_SUSTAIN, fx_env2_sustain}, - {15, AWE_FX_ENV2_RELEASE, fx_env2_release}, - - {16, AWE_FX_INIT_PITCH, fx_init_pitch}, - {17, AWE_FX_LFO1_PITCH, fx_lfo1_pitch}, - {18, AWE_FX_LFO2_PITCH, fx_lfo2_pitch}, - {19, AWE_FX_ENV1_PITCH, fx_env1_pitch}, - {20, AWE_FX_LFO1_VOLUME, fx_lfo1_volume}, - {21, AWE_FX_CUTOFF, fx_cutoff}, - {22, AWE_FX_FILTERQ, fx_filterQ}, - {23, AWE_FX_LFO1_CUTOFF, fx_lfo1_cutoff}, - {24, AWE_FX_ENV1_CUTOFF, fx_env1_cutoff}, - {25, AWE_FX_CHORUS, fx_chorus}, - {26, AWE_FX_REVERB, fx_reverb}, -}; - -static int num_awe_effects = numberof(awe_effects); - - -/* - * GS(SC88) NRPN effects; still experimental - */ - -/* cutoff: quarter semitone step, max=255 */ -static unsigned short gs_cutoff(int val) -{ - return (val - 64) * gs_sense[FX_CUTOFF] / 50; -} - -/* resonance: 0 to 15(max) */ -static unsigned short gs_filterQ(int val) -{ - return (val - 64) * gs_sense[FX_RESONANCE] / 50; -} - -/* attack: */ -static unsigned short gs_attack(int val) -{ - return -(val - 64) * gs_sense[FX_ATTACK] / 50; -} - -/* decay: */ -static unsigned short gs_decay(int val) -{ - return -(val - 64) * gs_sense[FX_RELEASE] / 50; -} - -/* release: */ -static unsigned short gs_release(int val) -{ - return -(val - 64) * gs_sense[FX_RELEASE] / 50; -} - -/* vibrato freq: 0.042Hz step, max=255 */ -static unsigned short gs_vib_rate(int val) -{ - return (val - 64) * gs_sense[FX_VIBRATE] / 50; -} - -/* vibrato depth: max=127, 1 octave */ -static unsigned short gs_vib_depth(int val) -{ - return (val - 64) * gs_sense[FX_VIBDEPTH] / 50; -} - -/* vibrato delay: -0.725msec step */ -static unsigned short gs_vib_delay(int val) -{ - return -(val - 64) * gs_sense[FX_VIBDELAY] / 50; -} - -static ConvTable gs_effects[] = -{ - {32, AWE_FX_CUTOFF, gs_cutoff}, - {33, AWE_FX_FILTERQ, gs_filterQ}, - {99, AWE_FX_ENV2_ATTACK, gs_attack}, - {100, AWE_FX_ENV2_DECAY, gs_decay}, - {102, AWE_FX_ENV2_RELEASE, gs_release}, - {8, AWE_FX_LFO1_FREQ, gs_vib_rate}, - {9, AWE_FX_LFO1_VOLUME, gs_vib_depth}, - {10, AWE_FX_LFO1_DELAY, gs_vib_delay}, -}; - -static int num_gs_effects = numberof(gs_effects); - - -/* - * NRPN events: accept as AWE32/SC88 specific controls - */ - -static void midi_nrpn_event(MidiStatus *st) -{ - if (rpn_msb[st->chan] == 127 && rpn_lsb[st->chan] <= 26) { - if (! msb_bit) /* both MSB/LSB necessary */ - send_converted_effect(awe_effects, num_awe_effects, - st, rpn_lsb[st->chan], - rpn_val[st->chan] - 8192); - } else if (rpn_msb[st->chan] == 1) { - if (msb_bit) /* only MSB is valid */ - add_converted_effect(gs_effects, num_gs_effects, - st, rpn_lsb[st->chan], - rpn_val[st->chan] / 128); - } -} - - -/* - * XG control effects; still experimental - */ - -/* cutoff: quarter semitone step, max=255 */ -static unsigned short xg_cutoff(int val) -{ - return (val - 64) * xg_sense[FX_CUTOFF] / 64; -} - -/* resonance: 0(open) to 15(most nasal) */ -static unsigned short xg_filterQ(int val) -{ - return (val - 64) * xg_sense[FX_RESONANCE] / 64; -} - -/* attack: */ -static unsigned short xg_attack(int val) -{ - return -(val - 64) * xg_sense[FX_ATTACK] / 64; -} - -/* release: */ -static unsigned short xg_release(int val) -{ - return -(val - 64) * xg_sense[FX_RELEASE] / 64; -} - -static ConvTable xg_effects[] = -{ - {71, AWE_FX_CUTOFF, xg_cutoff}, - {74, AWE_FX_FILTERQ, xg_filterQ}, - {72, AWE_FX_ENV2_RELEASE, xg_release}, - {73, AWE_FX_ENV2_ATTACK, xg_attack}, -}; - -static int num_xg_effects = numberof(xg_effects); - -static int xg_control_change(MidiStatus *st, int cmd, int val) -{ - return add_converted_effect(xg_effects, num_xg_effects, st, cmd, val); -} - -#endif /* CONFIG_AWE32_MIDIEMU */ - - -/*----------------------------------------------------------------*/ - -/* - * device / lowlevel (module) interface - */ - -int __init attach_awe(void) -{ - return _attach_awe() ? 0 : -ENODEV; -} - -void __exit unload_awe(void) -{ - _unload_awe(); -#ifdef __ISAPNP__ - if (isapnp) - awe_deactivate_isapnp(); -#endif /* isapnp */ -} - - -module_init(attach_awe); -module_exit(unload_awe); - -#ifndef MODULE -static int __init setup_awe(char *str) -{ - /* io, memsize, isapnp */ - int ints[4]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - memsize = ints[2]; - isapnp = ints[3]; - - return 1; -} - -__setup("awe=", setup_awe); -#endif diff -Nru a/drivers/sound/awe_wave.h b/drivers/sound/awe_wave.h --- a/drivers/sound/awe_wave.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,77 +0,0 @@ -/* - * sound/awe_config.h - * - * Configuration of AWE32/SB32/AWE64 wave table synth driver. - * version 0.4.4; Jan. 4, 2000 - * - * Copyright (C) 1996-1998 Takashi Iwai - * - * 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. - */ - -/* - * chorus & reverb effects send for FM chip: from 0 to 0xff - * larger numbers often cause weird sounds. - */ - -#define DEF_FM_CHORUS_DEPTH 0x10 -#define DEF_FM_REVERB_DEPTH 0x10 - - -/* - * other compile conditions - */ - -/* initialize FM passthrough even without extended RAM */ -#undef AWE_ALWAYS_INIT_FM - -/* debug on */ -#define AWE_DEBUG_ON - -/* GUS compatible mode */ -#define AWE_HAS_GUS_COMPATIBILITY - -/* add MIDI emulation by wavetable */ -#define CONFIG_AWE32_MIDIEMU - -/* add mixer control of emu8000 equalizer */ -#undef CONFIG_AWE32_MIXER - -/* use new volume calculation method as default */ -#define AWE_USE_NEW_VOLUME_CALC - -/* check current volume target for searching empty voices */ -#define AWE_CHECK_VTARGET - -/* allow sample sharing */ -#define AWE_ALLOW_SAMPLE_SHARING - -/* - * AWE32 card configuration: - * uncomment the following lines *ONLY* when auto detection doesn't - * work properly on your machine. - */ - -/*#define AWE_DEFAULT_BASE_ADDR 0x620*/ /* base port address */ -/*#define AWE_DEFAULT_MEM_SIZE 512*/ /* kbytes */ - -/* - * AWE driver version number - */ -#define AWE_MAJOR_VERSION 0 -#define AWE_MINOR_VERSION 4 -#define AWE_TINY_VERSION 4 -#define AWE_VERSION_NUMBER ((AWE_MAJOR_VERSION<<16)|(AWE_MINOR_VERSION<<8)|AWE_TINY_VERSION) -#define AWEDRV_VERSION "0.4.4" diff -Nru a/drivers/sound/bin2hex.c b/drivers/sound/bin2hex.c --- a/drivers/sound/bin2hex.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,38 +0,0 @@ -#include -#include - -int main( int argc, const char * argv [] ) -{ - const char * varname; - int i = 0; - int c; - int id = 0; - - if(argv[1] && strcmp(argv[1],"-i")==0) - { - argv++; - argc--; - id=1; - } - - if(argc==1) - { - fprintf(stderr, "bin2hex: [-i] firmware\n"); - exit(1); - } - - varname = argv[1]; - printf( "/* automatically generated by bin2hex */\n" ); - printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":""); - - while ( ( c = getchar( ) ) != EOF ) - { - if ( i != 0 && i % 10 == 0 ) - printf( "\n" ); - printf( "0x%02lx,", c & 0xFFl ); - i++; - } - - printf( "};\nstatic int %sLen = %d;\n", varname, i ); - return 0; -} diff -Nru a/drivers/sound/btaudio.c b/drivers/sound/btaudio.c --- a/drivers/sound/btaudio.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1072 +0,0 @@ -/* - btaudio - bt878 audio dma driver for linux 2.4.x - - (c) 2000 Gerd Knorr - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* mmio access */ -#define btwrite(dat,adr) writel((dat), (bta->mmio+(adr))) -#define btread(adr) readl(bta->mmio+(adr)) - -#define btand(dat,adr) btwrite((dat) & btread(adr), adr) -#define btor(dat,adr) btwrite((dat) | btread(adr), adr) -#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) - -/* registers (shifted because bta->mmio is long) */ -#define REG_INT_STAT (0x100 >> 2) -#define REG_INT_MASK (0x104 >> 2) -#define REG_GPIO_DMA_CTL (0x10c >> 2) -#define REG_PACKET_LEN (0x110 >> 2) -#define REG_RISC_STRT_ADD (0x114 >> 2) -#define REG_RISC_COUNT (0x120 >> 2) - -/* IRQ bits - REG_INT_(STAT|MASK) */ -#define IRQ_SCERR (1 << 19) -#define IRQ_OCERR (1 << 18) -#define IRQ_PABORT (1 << 17) -#define IRQ_RIPERR (1 << 16) -#define IRQ_PPERR (1 << 15) -#define IRQ_FDSR (1 << 14) -#define IRQ_FTRGT (1 << 13) -#define IRQ_FBUS (1 << 12) -#define IRQ_RISCI (1 << 11) -#define IRQ_OFLOW (1 << 3) - -#define IRQ_BTAUDIO (IRQ_SCERR | IRQ_OCERR | IRQ_PABORT | IRQ_RIPERR |\ - IRQ_PPERR | IRQ_FDSR | IRQ_FTRGT | IRQ_FBUS |\ - IRQ_RISCI) - -/* REG_GPIO_DMA_CTL bits */ -#define DMA_CTL_A_PWRDN (1 << 26) -#define DMA_CTL_DA_SBR (1 << 14) -#define DMA_CTL_DA_ES2 (1 << 13) -#define DMA_CTL_ACAP_EN (1 << 4) -#define DMA_CTL_RISC_EN (1 << 1) -#define DMA_CTL_FIFO_EN (1 << 0) - -/* RISC instructions */ -#define RISC_WRITE (0x01 << 28) -#define RISC_JUMP (0x07 << 28) -#define RISC_SYNC (0x08 << 28) - -/* RISC bits */ -#define RISC_WR_SOL (1 << 27) -#define RISC_WR_EOL (1 << 26) -#define RISC_IRQ (1 << 24) -#define RISC_SYNC_RESYNC (1 << 15) -#define RISC_SYNC_FM1 0x06 -#define RISC_SYNC_VRO 0x0c - -#define HWBASE_AD (448000) - -/* -------------------------------------------------------------- */ - -struct btaudio { - /* linked list */ - struct btaudio *next; - - /* device info */ - int dsp_digital; - int dsp_analog; - int mixer_dev; - struct pci_dev *pci; - unsigned int irq; - unsigned long mem; - unsigned long *mmio; - - /* locking */ - int users; - struct semaphore lock; - - /* risc instructions */ - unsigned int risc_size; - unsigned long *risc_cpu; - dma_addr_t risc_dma; - - /* audio data */ - unsigned int buf_size; - unsigned char *buf_cpu; - dma_addr_t buf_dma; - - /* buffer setup */ - int line_bytes; - int line_count; - int block_bytes; - int block_count; - - /* read fifo management */ - int recording; - int dma_block; - int read_offset; - int read_count; - wait_queue_head_t readq; - - /* settings */ - int gain[3]; - int source; - int bits; - int decimation; - int mixcount; - int sampleshift; - int channels; - int analog; -}; - -static struct btaudio *btaudios = NULL; -static unsigned int dsp1 = -1; -static unsigned int dsp2 = -1; -static unsigned int mixer = -1; -static unsigned int debug = 0; -static unsigned int irq_debug = 0; -static int digital = 1; -static int analog = 1; -static int rate = 32000; - -/* -------------------------------------------------------------- */ - -#define BUF_DEFAULT 128*1024 -#define BUF_MIN 8192 - -static int alloc_buffer(struct btaudio *bta) -{ - if (NULL == bta->buf_cpu) { - for (bta->buf_size = BUF_DEFAULT; bta->buf_size >= BUF_MIN; - bta->buf_size = bta->buf_size >> 1) { - bta->buf_cpu = pci_alloc_consistent - (bta->pci, bta->buf_size, &bta->buf_dma); - if (NULL != bta->buf_cpu) - break; - } - if (NULL == bta->buf_cpu) - return -ENOMEM; - memset(bta->buf_cpu,0,bta->buf_size); - } - if (NULL == bta->risc_cpu) { - bta->risc_size = PAGE_SIZE; - bta->risc_cpu = pci_alloc_consistent - (bta->pci, bta->risc_size, &bta->risc_dma); - if (NULL == bta->risc_cpu) - return -ENOMEM; - } - return 0; -} - -static void free_buffer(struct btaudio *bta) -{ - if (NULL != bta->buf_cpu) { - pci_free_consistent(bta->pci, bta->buf_size, - bta->buf_cpu, bta->buf_dma); - bta->buf_cpu = NULL; - } - if (NULL != bta->risc_cpu) { - pci_free_consistent(bta->pci, bta->risc_size, - bta->risc_cpu, bta->risc_dma); - bta->risc_cpu = NULL; - } -} - -static int make_risc(struct btaudio *bta) -{ - int rp, bp, line, block; - unsigned long risc; - - bta->block_bytes = bta->buf_size >> 4; - bta->block_count = 1 << 4; - bta->line_bytes = bta->block_bytes; - bta->line_count = bta->block_count; - while (bta->line_bytes > 4095) { - bta->line_bytes >>= 1; - bta->line_count <<= 1; - } - if (bta->line_count > 255) - return -EINVAL; - if (debug) - printk(KERN_DEBUG - "btaudio: bufsize=%d - bs=%d bc=%d - ls=%d, lc=%d\n", - bta->buf_size,bta->block_bytes,bta->block_count, - bta->line_bytes,bta->line_count); - rp = 0; bp = 0; - block = 0; - bta->risc_cpu[rp++] = cpu_to_le32(RISC_SYNC|RISC_SYNC_FM1); - bta->risc_cpu[rp++] = cpu_to_le32(0); - for (line = 0; line < bta->line_count; line++) { - risc = RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL; - risc |= bta->line_bytes; - if (0 == (bp & (bta->block_bytes-1))) { - risc |= RISC_IRQ; - risc |= (block & 0x0f) << 16; - risc |= (~block & 0x0f) << 20; - block++; - } - bta->risc_cpu[rp++] = cpu_to_le32(risc); - bta->risc_cpu[rp++] = cpu_to_le32(bta->buf_dma + bp); - bp += bta->line_bytes; - } - bta->risc_cpu[rp++] = cpu_to_le32(RISC_SYNC|RISC_SYNC_VRO); - bta->risc_cpu[rp++] = cpu_to_le32(0); - bta->risc_cpu[rp++] = cpu_to_le32(RISC_JUMP); - bta->risc_cpu[rp++] = cpu_to_le32(bta->risc_dma); - return 0; -} - -static int start_recording(struct btaudio *bta) -{ - int ret; - - if (0 != (ret = alloc_buffer(bta))) - return ret; - if (0 != (ret = make_risc(bta))) - return ret; - - btwrite(bta->risc_dma, REG_RISC_STRT_ADD); - btwrite((bta->line_count << 16) | bta->line_bytes, - REG_PACKET_LEN); - btwrite(IRQ_BTAUDIO, REG_INT_MASK); - if (bta->analog) { - btwrite(DMA_CTL_ACAP_EN | - DMA_CTL_RISC_EN | - DMA_CTL_FIFO_EN | - DMA_CTL_DA_ES2 | - ((bta->bits == 8) ? DMA_CTL_DA_SBR : 0) | - (bta->gain[bta->source] << 28) | - (bta->source << 24) | - (bta->decimation << 8), - REG_GPIO_DMA_CTL); - } else { - btwrite(DMA_CTL_ACAP_EN | - DMA_CTL_RISC_EN | - DMA_CTL_FIFO_EN | - DMA_CTL_DA_ES2 | - DMA_CTL_A_PWRDN | - (1 << 6) | - ((bta->bits == 8) ? DMA_CTL_DA_SBR : 0) | - (bta->gain[bta->source] << 28) | - (bta->source << 24) | - (bta->decimation << 8), - REG_GPIO_DMA_CTL); - } - bta->dma_block = 0; - bta->read_offset = 0; - bta->read_count = 0; - bta->recording = 1; - if (debug) - printk(KERN_DEBUG "btaudio: recording started\n"); - return 0; -} - -static void stop_recording(struct btaudio *bta) -{ - btand(~15, REG_GPIO_DMA_CTL); - bta->recording = 0; - if (debug) - printk(KERN_DEBUG "btaudio: recording stopped\n"); -} - - -/* -------------------------------------------------------------- */ - -static int btaudio_mixer_open(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct btaudio *bta; - - for (bta = btaudios; bta != NULL; bta = bta->next) - if (bta->mixer_dev == minor) - break; - if (NULL == bta) - return -ENODEV; - - if (debug) - printk("btaudio: open mixer [%d]\n",minor); - file->private_data = bta; - return 0; -} - -static int btaudio_mixer_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static int btaudio_mixer_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct btaudio *bta = file->private_data; - int ret,val=0,i=0; - - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - memset(&info,0,sizeof(info)); - strncpy(info.id,"bt878",sizeof(info.id)-1); - strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)-1); - info.modify_counter = bta->mixcount; - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - memset(&info,0,sizeof(info)); - strncpy(info.id,"bt878",sizeof(info.id)-1); - strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)-1); - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *)arg); - - /* read */ - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - if (get_user(val, (int *)arg)) - return -EFAULT; - - switch (cmd) { - case MIXER_READ(SOUND_MIXER_CAPS): - ret = SOUND_CAP_EXCL_INPUT; - break; - case MIXER_READ(SOUND_MIXER_STEREODEVS): - ret = 0; - break; - case MIXER_READ(SOUND_MIXER_RECMASK): - case MIXER_READ(SOUND_MIXER_DEVMASK): - ret = SOUND_MASK_LINE1|SOUND_MASK_LINE2|SOUND_MASK_LINE3; - break; - - case MIXER_WRITE(SOUND_MIXER_RECSRC): - if (val & SOUND_MASK_LINE1 && bta->source != 0) - bta->source = 0; - else if (val & SOUND_MASK_LINE2 && bta->source != 1) - bta->source = 1; - else if (val & SOUND_MASK_LINE3 && bta->source != 2) - bta->source = 2; - btaor((bta->gain[bta->source] << 28) | - (bta->source << 24), - 0x0cffffff, REG_GPIO_DMA_CTL); - case MIXER_READ(SOUND_MIXER_RECSRC): - switch (bta->source) { - case 0: ret = SOUND_MASK_LINE1; break; - case 1: ret = SOUND_MASK_LINE2; break; - case 2: ret = SOUND_MASK_LINE3; break; - default: ret = 0; - } - break; - - case MIXER_WRITE(SOUND_MIXER_LINE1): - case MIXER_WRITE(SOUND_MIXER_LINE2): - case MIXER_WRITE(SOUND_MIXER_LINE3): - if (MIXER_WRITE(SOUND_MIXER_LINE1) == cmd) - i = 0; - if (MIXER_WRITE(SOUND_MIXER_LINE2) == cmd) - i = 1; - if (MIXER_WRITE(SOUND_MIXER_LINE3) == cmd) - i = 2; - bta->gain[i] = (val & 0xff) * 15 / 100; - if (bta->gain[i] > 15) bta->gain[i] = 15; - if (bta->gain[i] < 0) bta->gain[i] = 0; - if (i == bta->source) - btaor((bta->gain[bta->source]<<28), - 0x0fffffff, REG_GPIO_DMA_CTL); - ret = bta->gain[i] * 100 / 15; - ret |= ret << 8; - break; - - case MIXER_READ(SOUND_MIXER_LINE1): - case MIXER_READ(SOUND_MIXER_LINE2): - case MIXER_READ(SOUND_MIXER_LINE3): - if (MIXER_READ(SOUND_MIXER_LINE1) == cmd) - i = 0; - if (MIXER_READ(SOUND_MIXER_LINE2) == cmd) - i = 1; - if (MIXER_READ(SOUND_MIXER_LINE3) == cmd) - i = 2; - ret = bta->gain[i] * 100 / 15; - ret |= ret << 8; - break; - - default: - return -EINVAL; - } - if (put_user(ret, (int *)arg)) - return -EFAULT; - return 0; -} - -static struct file_operations btaudio_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - open: btaudio_mixer_open, - release: btaudio_mixer_release, - ioctl: btaudio_mixer_ioctl, -}; - -/* -------------------------------------------------------------- */ - -static int btaudio_dsp_open(struct inode *inode, struct file *file, - struct btaudio *bta, int analog) -{ - down(&bta->lock); - if (bta->users) - goto busy; - bta->users++; - file->private_data = bta; - - bta->analog = analog; - bta->dma_block = 0; - bta->read_offset = 0; - bta->read_count = 0; - bta->sampleshift = 0; - - up(&bta->lock); - return 0; - - busy: - up(&bta->lock); - return -EBUSY; -} - -static int btaudio_dsp_open_digital(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct btaudio *bta; - - for (bta = btaudios; bta != NULL; bta = bta->next) - if (bta->dsp_digital == minor) - break; - if (NULL == bta) - return -ENODEV; - - if (debug) - printk("btaudio: open digital dsp [%d]\n",minor); - return btaudio_dsp_open(inode,file,bta,0); -} - -static int btaudio_dsp_open_analog(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct btaudio *bta; - - for (bta = btaudios; bta != NULL; bta = bta->next) - if (bta->dsp_analog == minor) - break; - if (NULL == bta) - return -ENODEV; - - if (debug) - printk("btaudio: open analog dsp [%d]\n",minor); - return btaudio_dsp_open(inode,file,bta,1); -} - -static int btaudio_dsp_release(struct inode *inode, struct file *file) -{ - struct btaudio *bta = file->private_data; - - down(&bta->lock); - if (bta->recording) - stop_recording(bta); - bta->users--; - up(&bta->lock); - return 0; -} - -static ssize_t btaudio_dsp_read(struct file *file, char *buffer, - size_t swcount, loff_t *ppos) -{ - struct btaudio *bta = file->private_data; - int hwcount = swcount << bta->sampleshift; - int nsrc, ndst, err, ret = 0; - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&bta->readq, &wait); - down(&bta->lock); - while (swcount > 0) { - if (0 == bta->read_count) { - if (!bta->recording) { - if (0 != (err = start_recording(bta))) { - if (0 == ret) - ret = err; - break; - } - } - if (file->f_flags & O_NONBLOCK) { - if (0 == ret) - ret = -EAGAIN; - break; - } - up(&bta->lock); - current->state = TASK_INTERRUPTIBLE; - schedule(); - down(&bta->lock); - if(signal_pending(current)) { - if (0 == ret) - ret = -EINTR; - break; - } - } - nsrc = (bta->read_count < hwcount) ? bta->read_count : hwcount; - if (nsrc > bta->buf_size - bta->read_offset) - nsrc = bta->buf_size - bta->read_offset; - ndst = nsrc >> bta->sampleshift; - - if ((bta->analog && 0 == bta->sampleshift) || - (!bta->analog && 2 == bta->channels)) { - /* just copy */ - if (copy_to_user(buffer + ret, bta->buf_cpu + bta->read_offset, nsrc)) { - if (0 == ret) - ret = -EFAULT; - break; - } - - } else if (!bta->analog) { - /* stereo => mono (digital audio) */ - __s16 *src = (__s16*)(bta->buf_cpu + bta->read_offset); - __s16 *dst = (__s16*)(buffer + ret); - __s16 avg; - int n = ndst>>1; - if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { - if (0 == ret) - ret = -EFAULT; - break; - } - for (; n; n--, dst++) { - avg = (__s16)le16_to_cpu(*src) / 2; src++; - avg += (__s16)le16_to_cpu(*src) / 2; src++; - __put_user(cpu_to_le16(avg),(__u16*)(dst)); - } - - } else if (8 == bta->bits) { - /* copy + byte downsampling (audio A/D) */ - __u8 *src = bta->buf_cpu + bta->read_offset; - __u8 *dst = buffer + ret; - int n = ndst; - if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { - if (0 == ret) - ret = -EFAULT; - break; - } - for (; n; n--, src += (1 << bta->sampleshift), dst++) - __put_user(*src,(__u8*)(dst)); - - } else { - /* copy + word downsampling (audio A/D) */ - __u16 *src = (__u16*)(bta->buf_cpu + bta->read_offset); - __u16 *dst = (__u16*)(buffer + ret); - int n = ndst>>1; - if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { - if (0 == ret) - ret = -EFAULT; - break; - } - for (; n; n--, src += (1 << bta->sampleshift), dst++) - __put_user(*src,(__u16*)(dst)); - } - - ret += ndst; - swcount -= ndst; - hwcount -= nsrc; - bta->read_count -= nsrc; - bta->read_offset += nsrc; - if (bta->read_offset == bta->buf_size) - bta->read_offset = 0; - } - up(&bta->lock); - remove_wait_queue(&bta->readq, &wait); - current->state = TASK_RUNNING; - return ret; -} - -static ssize_t btaudio_dsp_write(struct file *file, const char *buffer, - size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static int btaudio_dsp_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct btaudio *bta = file->private_data; - int s, i, ret, val = 0; - - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *)arg); - case SNDCTL_DSP_GETCAPS: - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int*)arg)) - return -EFAULT; - if (bta->analog) { - for (s = 0; s < 16; s++) - if (val << s >= HWBASE_AD*4/15) - break; - for (i = 15; i >= 5; i--) - if (val << s <= HWBASE_AD*4/i) - break; - bta->sampleshift = s; - bta->decimation = i; - if (debug) - printk(KERN_DEBUG "btaudio: rate: req=%d " - "dec=%d shift=%d hwrate=%d swrate=%d\n", - val,i,s,(HWBASE_AD*4/i),(HWBASE_AD*4/i)>>s); - } else { - bta->sampleshift = (bta->channels == 2) ? 0 : 1; - bta->decimation = 0; - } - if (bta->recording) { - down(&bta->lock); - stop_recording(bta); - start_recording(bta); - up(&bta->lock); - } - /* fall through */ - case SOUND_PCM_READ_RATE: - if (bta->analog) { - return put_user(HWBASE_AD*4/bta->decimation>>bta->sampleshift, (int*)arg); - } else { - return put_user(rate, (int*)arg); - } - - case SNDCTL_DSP_STEREO: - if (!bta->analog) { - if (get_user(val, (int*)arg)) - return -EFAULT; - bta->channels = (val > 0) ? 2 : 1; - bta->sampleshift = (bta->channels == 2) ? 0 : 1; - if (debug) - printk(KERN_INFO - "btaudio: stereo=%d channels=%d\n", - val,bta->channels); - } else { - if (val == 1) - return -EFAULT; - else { - bta->channels = 1; - if (debug) - printk(KERN_INFO - "btaudio: stereo=0 channels=1\n"); - } - } - return put_user((bta->channels)-1, (int *)arg); - - case SNDCTL_DSP_CHANNELS: - if (!analog) { - if (get_user(val, (int*)arg)) - return -EFAULT; - bta->channels = (val > 1) ? 2 : 1; - bta->sampleshift = (bta->channels == 2) ? 0 : 1; - if (debug) - printk(KERN_DEBUG - "btaudio: val=%d channels=%d\n", - val,bta->channels); - } - /* fall through */ - case SOUND_PCM_READ_CHANNELS: - return put_user(bta->channels, (int *)arg); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - if (analog) - return put_user(AFMT_S16_LE|AFMT_S8, (int*)arg); - else - return put_user(AFMT_S16_LE, (int*)arg); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, (int*)arg)) - return -EFAULT; - if (val != AFMT_QUERY) { - if (analog) - bta->bits = (val == AFMT_S8) ? 8 : 16; - else - bta->bits = 16; - if (bta->recording) { - down(&bta->lock); - stop_recording(bta); - start_recording(bta); - up(&bta->lock); - } - } - if (debug) - printk(KERN_DEBUG "btaudio: fmt: bits=%d\n",bta->bits); - return put_user((bta->bits==16) ? AFMT_S16_LE : AFMT_S8, - (int*)arg); - break; - case SOUND_PCM_READ_BITS: - return put_user(bta->bits, (int*)arg); - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_RESET: - if (bta->recording) { - down(&bta->lock); - stop_recording(bta); - up(&bta->lock); - } - return 0; - case SNDCTL_DSP_GETBLKSIZE: - if (!bta->recording) { - if (0 != (ret = alloc_buffer(bta))) - return ret; - if (0 != (ret = make_risc(bta))) - return ret; - } - return put_user(bta->block_bytes>>bta->sampleshift,(int*)arg); - - case SNDCTL_DSP_SYNC: - /* NOP */ - return 0; - case SNDCTL_DSP_GETISPACE: - { - audio_buf_info info; - if (!bta->recording) - return -EINVAL; - info.fragsize = bta->block_bytes>>bta->sampleshift; - info.fragstotal = bta->block_count; - info.bytes = bta->read_count; - info.fragments = info.bytes / info.fragsize; - if (debug) - printk(KERN_DEBUG "btaudio: SNDCTL_DSP_GETISPACE " - "returns %d/%d/%d/%d\n", - info.fragsize, info.fragstotal, - info.bytes, info.fragments); - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } -#if 0 /* TODO */ - case SNDCTL_DSP_GETTRIGGER: - case SNDCTL_DSP_SETTRIGGER: - case SNDCTL_DSP_SETFRAGMENT: -#endif - default: - return -EINVAL; - } -} - -static unsigned int btaudio_dsp_poll(struct file *file, struct poll_table_struct *wait) -{ - struct btaudio *bta = file->private_data; - unsigned int mask = 0; - - poll_wait(file, &bta->readq, wait); - - if (0 == bta->read_count) - mask |= (POLLIN | POLLRDNORM); - - return mask; -} - -static struct file_operations btaudio_digital_dsp_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - open: btaudio_dsp_open_digital, - release: btaudio_dsp_release, - read: btaudio_dsp_read, - write: btaudio_dsp_write, - ioctl: btaudio_dsp_ioctl, - poll: btaudio_dsp_poll, -}; - -static struct file_operations btaudio_analog_dsp_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - open: btaudio_dsp_open_analog, - release: btaudio_dsp_release, - read: btaudio_dsp_read, - write: btaudio_dsp_write, - ioctl: btaudio_dsp_ioctl, - poll: btaudio_dsp_poll, -}; - -/* -------------------------------------------------------------- */ - -static char *irq_name[] = { "", "", "", "OFLOW", "", "", "", "", "", "", "", - "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR", - "RIPERR", "PABORT", "OCERR", "SCERR" }; - -static void btaudio_irq(int irq, void *dev_id, struct pt_regs * regs) -{ - int count = 0; - u32 stat,astat; - struct btaudio *bta = dev_id; - - for (;;) { - count++; - stat = btread(REG_INT_STAT); - astat = stat & btread(REG_INT_MASK); - if (!astat) - return; - btwrite(astat,REG_INT_STAT); - - if (irq_debug) { - int i; - printk(KERN_DEBUG "btaudio: irq loop=%d risc=%x, bits:", - count, stat>>28); - for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) { - if (stat & (1 << i)) - printk(" %s",irq_name[i]); - if (astat & (1 << i)) - printk("*"); - } - printk("\n"); - } - if (stat & IRQ_RISCI) { - int blocks; - blocks = (stat >> 28) - bta->dma_block; - if (blocks < 0) - blocks += bta->block_count; - bta->dma_block = stat >> 28; - if (bta->read_count + 2*bta->block_bytes > bta->buf_size) { - stop_recording(bta); - printk(KERN_INFO "btaudio: buffer overrun\n"); - } - if (blocks > 0) { - bta->read_count += blocks * bta->block_bytes; - wake_up_interruptible(&bta->readq); - } - } - if (count > 10) { - printk(KERN_WARNING - "btaudio: Oops - irq mask cleared\n"); - btwrite(0, REG_INT_MASK); - } - } - return; -} - -/* -------------------------------------------------------------- */ - -static int __devinit btaudio_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) -{ - struct btaudio *bta; - unsigned char revision,latency; - int rc = -EBUSY; - - if (pci_enable_device(pci_dev)) - return -EIO; - if (!request_mem_region(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0), - "btaudio")) { - return -EBUSY; - } - - bta = kmalloc(sizeof(*bta),GFP_ATOMIC); - memset(bta,0,sizeof(*bta)); - - bta->pci = pci_dev; - bta->irq = pci_dev->irq; - bta->mem = pci_resource_start(pci_dev,0); - bta->mmio = ioremap(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0)); - - bta->source = 1; - bta->bits = 8; - bta->channels = 1; - if (bta->analog) { - bta->decimation = 15; - } else { - bta->decimation = 0; - bta->sampleshift = 1; - } - - init_MUTEX(&bta->lock); - init_waitqueue_head(&bta->readq); - - pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); - pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &latency); - printk(KERN_INFO "btaudio: Bt%x (rev %d) at %02x:%02x.%x, ", - pci_dev->device,revision,pci_dev->bus->number, - PCI_SLOT(pci_dev->devfn),PCI_FUNC(pci_dev->devfn)); - printk("irq: %d, latency: %d, memory: 0x%lx\n", - bta->irq, latency, bta->mem); - - /* init hw */ - btwrite(0, REG_GPIO_DMA_CTL); - btwrite(0, REG_INT_MASK); - btwrite(~0x0UL, REG_INT_STAT); - pci_set_master(pci_dev); - - if ((rc = request_irq(bta->irq, btaudio_irq, SA_SHIRQ|SA_INTERRUPT, - "btaudio",(void *)bta)) < 0) { - printk(KERN_WARNING - "btaudio: can't request irq (rc=%d)\n",rc); - goto fail1; - } - - /* register devices */ - if (digital) { - rc = bta->dsp_digital = - register_sound_dsp(&btaudio_digital_dsp_fops,dsp1); - if (rc < 0) { - printk(KERN_WARNING - "btaudio: can't register digital dsp (rc=%d)\n",rc); - goto fail2; - } - } - if (analog) { - rc = bta->dsp_analog = - register_sound_dsp(&btaudio_analog_dsp_fops,dsp2); - if (rc < 0) { - printk(KERN_WARNING - "btaudio: can't register analog dsp (rc=%d)\n",rc); - goto fail3; - } - rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops,mixer); - if (rc < 0) { - printk(KERN_WARNING - "btaudio: can't register mixer (rc=%d)\n",rc); - goto fail4; - } - } - if (debug) - printk(KERN_DEBUG "btaudio: minors: digital=%d, analog=%d, mixer=%d\n", - bta->dsp_digital, bta->dsp_analog, bta->mixer_dev); - - /* hook into linked list */ - bta->next = btaudios; - btaudios = bta; - - pci_set_drvdata(pci_dev,bta); - return 0; - - fail4: - unregister_sound_dsp(bta->dsp_analog); - fail3: - if (digital) - unregister_sound_dsp(bta->dsp_digital); - fail2: - free_irq(bta->irq,bta); - fail1: - release_mem_region(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0)); - kfree(bta); - return rc; -} - -static void __devexit btaudio_remove(struct pci_dev *pci_dev) -{ - struct btaudio *bta = pci_get_drvdata(pci_dev); - struct btaudio *walk; - - /* turn off all DMA / IRQs */ - btand(~15, REG_GPIO_DMA_CTL); - btwrite(0, REG_INT_MASK); - btwrite(~0x0UL, REG_INT_STAT); - - /* unregister devices */ - if (digital) { - unregister_sound_dsp(bta->dsp_digital); - } - if (analog) { - unregister_sound_dsp(bta->dsp_analog); - unregister_sound_mixer(bta->mixer_dev); - } - - /* free resources */ - free_buffer(bta); - free_irq(bta->irq,bta); - release_mem_region(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0)); - - /* remove from linked list */ - if (bta == btaudios) { - btaudios = NULL; - } else { - for (walk = btaudios; walk->next != bta; walk = walk->next) - ; /* if (NULL == walk->next) BUG(); */ - walk->next = bta->next; - } - - pci_set_drvdata(pci_dev, NULL); - kfree(bta); - return; -} - -/* -------------------------------------------------------------- */ - -static struct pci_device_id btaudio_pci_tbl[] __devinitdata = { - { PCI_VENDOR_ID_BROOKTREE, 0x0878, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { PCI_VENDOR_ID_BROOKTREE, 0x0879, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0,} -}; - -static struct pci_driver btaudio_pci_driver = { - name: "btaudio", - id_table: btaudio_pci_tbl, - probe: btaudio_probe, - remove: btaudio_remove, -}; - -int btaudio_init_module(void) -{ - printk(KERN_INFO "btaudio: driver version 0.6 loaded [%s%s%s]\n", - analog ? "analog" : "", - analog && digital ? "+" : "", - digital ? "digital" : ""); - return pci_module_init(&btaudio_pci_driver); -} - -void btaudio_cleanup_module(void) -{ - pci_unregister_driver(&btaudio_pci_driver); - return; -} - -module_init(btaudio_init_module); -module_exit(btaudio_cleanup_module); - -MODULE_PARM(dsp1,"i"); -MODULE_PARM(dsp2,"i"); -MODULE_PARM(mixer,"i"); -MODULE_PARM(debug,"i"); -MODULE_PARM(irq_debug,"i"); -MODULE_PARM(digital,"i"); -MODULE_PARM(analog,"i"); -MODULE_PARM(rate,"i"); - -MODULE_DEVICE_TABLE(pci, btaudio_pci_tbl); -MODULE_DESCRIPTION("bt878 audio dma driver"); -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff -Nru a/drivers/sound/cmpci.c b/drivers/sound/cmpci.c --- a/drivers/sound/cmpci.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3195 +0,0 @@ -/*****************************************************************************/ -/* - * cmpci.c -- C-Media PCI audio driver. - * - * Copyright (C) 1999 ChenLi Tien (cltien@cmedia.com.tw) - * C-media support (support@cmedia.com.tw) - * - * Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * For update, visit: - * http://members.home.net/puresoft/cmedia.html - * http://www.cmedia.com.tw - * - * 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. - * - * Special thanks to David C. Niemi, Jan Pfeifer - * - * - * Module command line parameters: - * none so far - * - * - * 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 - * - * The card has both an FM and a Wavetable synth, but I have to figure - * out first how to drive them... - * - * Revision history - * 06.05.98 0.1 Initial release - * 10.05.98 0.2 Fixed many bugs, esp. ADC rate calculation - * First stab at a simple midi interface (no bells&whistles) - * 13.05.98 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of - * set_dac_rate in the FMODE_WRITE case in cm_open - * Fix hwptr out of bounds (now mpg123 works) - * 14.05.98 0.4 Don't allow excessive interrupt rates - * 08.06.98 0.5 First release using Alan Cox' soundcore instead of miscdevice - * 03.08.98 0.6 Do not include modversions.h - * Now mixer behaviour can basically be selected between - * "OSS documented" and "OSS actual" behaviour - * 31.08.98 0.7 Fix realplayer problems - dac.count issues - * 10.12.98 0.8 Fix drain_dac trying to wait on not yet initialized DMA - * 16.12.98 0.9 Fix a few f_file & FMODE_ bugs - * 06.01.99 0.10 remove the silly SA_INTERRUPT flag. - * hopefully killed the egcs section type conflict - * 12.03.99 0.11 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 18.08.99 1.5 Only deallocate DMA buffer when unloading. - * 02.09.99 1.6 Enable SPDIF LOOP - * Change the mixer read back - * 21.09.99 2.33 Use RCS version as driver version. - * Add support for modem, S/PDIF loop and 4 channels. - * (8738 only) - * Fix bug cause x11amp cannot play. - * - * Fixes: - * Arnaldo Carvalho de Melo - * 18/05/2001 - .bss nitpicks, fix a bug in set_dac_channels where it - * was calling prog_dmabuf with s->lock held, call missing - * unlock_kernel in cm_midi_release - * 08/10/2001 - use set_current_state in some more places - * - * Carlos Eduardo Gorges - * Fri May 25 2001 - * - SMP support ( spin[un]lock* revision ) - * - speaker mixer support - * Mon Aug 13 2001 - * - optimizations and cleanups - * - */ -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dm.h" - -/* --------------------------------------------------------------------- */ -#undef OSS_DOCUMENTED_MIXER_SEMANTICS -#undef DMABYTEIO -/* --------------------------------------------------------------------- */ - -#define CM_MAGIC ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A) - -/* CM8338 registers definition ****************/ - -#define CODEC_CMI_FUNCTRL0 (0x00) -#define CODEC_CMI_FUNCTRL1 (0x04) -#define CODEC_CMI_CHFORMAT (0x08) -#define CODEC_CMI_INT_HLDCLR (0x0C) -#define CODEC_CMI_INT_STATUS (0x10) -#define CODEC_CMI_LEGACY_CTRL (0x14) -#define CODEC_CMI_MISC_CTRL (0x18) -#define CODEC_CMI_TDMA_POS (0x1C) -#define CODEC_CMI_MIXER (0x20) -#define CODEC_SB16_DATA (0x22) -#define CODEC_SB16_ADDR (0x23) -#define CODEC_CMI_MIXER1 (0x24) -#define CODEC_CMI_MIXER2 (0x25) -#define CODEC_CMI_AUX_VOL (0x26) -#define CODEC_CMI_MISC (0x27) -#define CODEC_CMI_AC97 (0x28) - -#define CODEC_CMI_CH0_FRAME1 (0x80) -#define CODEC_CMI_CH0_FRAME2 (0x84) -#define CODEC_CMI_CH1_FRAME1 (0x88) -#define CODEC_CMI_CH1_FRAME2 (0x8C) - -#define CODEC_CMI_EXT_REG (0xF0) - -/* Mixer registers for SB16 ******************/ - -#define DSP_MIX_DATARESETIDX ((unsigned char)(0x00)) - -#define DSP_MIX_MASTERVOLIDX_L ((unsigned char)(0x30)) -#define DSP_MIX_MASTERVOLIDX_R ((unsigned char)(0x31)) -#define DSP_MIX_VOICEVOLIDX_L ((unsigned char)(0x32)) -#define DSP_MIX_VOICEVOLIDX_R ((unsigned char)(0x33)) -#define DSP_MIX_FMVOLIDX_L ((unsigned char)(0x34)) -#define DSP_MIX_FMVOLIDX_R ((unsigned char)(0x35)) -#define DSP_MIX_CDVOLIDX_L ((unsigned char)(0x36)) -#define DSP_MIX_CDVOLIDX_R ((unsigned char)(0x37)) -#define DSP_MIX_LINEVOLIDX_L ((unsigned char)(0x38)) -#define DSP_MIX_LINEVOLIDX_R ((unsigned char)(0x39)) - -#define DSP_MIX_MICVOLIDX ((unsigned char)(0x3A)) -#define DSP_MIX_SPKRVOLIDX ((unsigned char)(0x3B)) - -#define DSP_MIX_OUTMIXIDX ((unsigned char)(0x3C)) - -#define DSP_MIX_ADCMIXIDX_L ((unsigned char)(0x3D)) -#define DSP_MIX_ADCMIXIDX_R ((unsigned char)(0x3E)) - -#define DSP_MIX_INGAINIDX_L ((unsigned char)(0x3F)) -#define DSP_MIX_INGAINIDX_R ((unsigned char)(0x40)) -#define DSP_MIX_OUTGAINIDX_L ((unsigned char)(0x41)) -#define DSP_MIX_OUTGAINIDX_R ((unsigned char)(0x42)) - -#define DSP_MIX_AGCIDX ((unsigned char)(0x43)) - -#define DSP_MIX_TREBLEIDX_L ((unsigned char)(0x44)) -#define DSP_MIX_TREBLEIDX_R ((unsigned char)(0x45)) -#define DSP_MIX_BASSIDX_L ((unsigned char)(0x46)) -#define DSP_MIX_BASSIDX_R ((unsigned char)(0x47)) - -#define CM_CH0_RESET 0x04 -#define CM_CH1_RESET 0x08 -#define CM_EXTENT_CODEC 0x100 -#define CM_EXTENT_MIDI 0x2 -#define CM_EXTENT_SYNTH 0x4 -#define CM_INT_CH0 1 -#define CM_INT_CH1 2 - -#define CM_CFMT_STEREO 0x01 -#define CM_CFMT_16BIT 0x02 -#define CM_CFMT_MASK 0x03 -#define CM_CFMT_DACSHIFT 2 -#define CM_CFMT_ADCSHIFT 0 - -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -#define CM_ENABLE_CH1 0x2 -#define CM_ENABLE_CH0 0x1 - -/* MIDI buffer sizes **************************/ - -#define MIDIINBUF 256 -#define MIDIOUTBUF 256 - -#define FMODE_MIDI_SHIFT 2 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -#define FMODE_DMFM 0x10 - -#define SND_DEV_DSP16 5 - -#define NR_DEVICE 3 /* maximum number of devices */ - -/*********************************************/ - -struct cm_state { - unsigned int magic; /* magic */ - struct cm_state *next; /* we keep cm cards in a linked list */ - - int dev_audio; /* soundcore stuff */ - int dev_mixer; - int dev_midi; - int dev_dmfm; - - unsigned int iosb, iobase, iosynth, - iomidi, iogame, irq; /* hardware resources */ - unsigned short deviceid; /* pci_id */ - - struct { /* mixer stuff */ - unsigned int modcnt; - unsigned short vol[13]; - } mix; - - unsigned int rateadc, ratedac; /* wave stuff */ - unsigned char fmt, enable; - - spinlock_t lock; - struct semaphore open_sem; - mode_t open_mode; - wait_queue_head_t open_wait; - - struct dmabuf { - void *rawbuf; - unsigned rawphys; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; - - unsigned fragsize; /* redundant, but makes calculations easier */ - unsigned dmasize; - unsigned fragsamples; - unsigned dmasamples; - - unsigned mapped:1; /* OSS stuff */ - unsigned ready:1; - unsigned endcleared:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac, dma_adc; - - struct { /* midi stuff */ - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t iwait; - wait_queue_head_t owait; - struct timer_list timer; - unsigned char ibuf[MIDIINBUF]; - unsigned char obuf[MIDIOUTBUF]; - } midi; - - int chip_version; - int max_channels; - int curr_channels; - int speakers; /* number of speakers */ - int capability; /* HW capability, various for chip versions */ - - int status; /* HW or SW state */ - - int spdif_counter; /* spdif frame counter */ -}; - -/* flags used for capability */ -#define CAN_AC3_HW 0x00000001 /* 037 or later */ -#define CAN_AC3_SW 0x00000002 /* 033 or later */ -#define CAN_AC3 (CAN_AC3_HW | CAN_AC3_SW) -#define CAN_DUAL_DAC 0x00000004 /* 033 or later */ -#define CAN_MULTI_CH_HW 0x00000008 /* 039 or later */ -#define CAN_MULTI_CH (CAN_MULTI_CH_HW | CAN_DUAL_DAC) -#define CAN_LINE_AS_REAR 0x00000010 /* 033 or later */ -#define CAN_LINE_AS_BASS 0x00000020 /* 039 or later */ -#define CAN_MIC_AS_BASS 0x00000040 /* 039 or later */ - -/* flags used for status */ -#define DO_AC3_HW 0x00000001 -#define DO_AC3_SW 0x00000002 -#define DO_AC3 (DO_AC3_HW | DO_AC3_SW) -#define DO_DUAL_DAC 0x00000004 -#define DO_MULTI_CH_HW 0x00000008 -#define DO_MULTI_CH (DO_MULTI_CH_HW | DO_DUAL_DAC) -#define DO_LINE_AS_REAR 0x00000010 /* 033 or later */ -#define DO_LINE_AS_BASS 0x00000020 /* 039 or later */ -#define DO_MIC_AS_BASS 0x00000040 /* 039 or later */ -#define DO_SPDIF_OUT 0x00000100 -#define DO_SPDIF_IN 0x00000200 -#define DO_SPDIF_LOOP 0x00000400 - -static struct cm_state *devs; -static unsigned long wavetable_mem; - -/* --------------------------------------------------------------------- */ - -static inline unsigned ld2(unsigned int x) -{ - unsigned exp=16,l=5,r=0; - static const unsigned num[]={0x2,0x4,0x10,0x100,0x10000}; - - /* num: 2, 4, 16, 256, 65536 */ - /* exp: 1, 2, 4, 8, 16 */ - - while(l--) { - if( x >= num[l] ) { - if(num[l]>2) x >>= exp; - r+=exp; - } - exp>>=1; - } - - return r; -} - -/* --------------------------------------------------------------------- */ - -static void maskb(unsigned int addr, unsigned int mask, unsigned int value) -{ - outb((inb(addr) & mask) | value, addr); -} - -static void maskw(unsigned int addr, unsigned int mask, unsigned int value) -{ - outw((inw(addr) & mask) | value, addr); -} - -static void maskl(unsigned int addr, unsigned int mask, unsigned int value) -{ - outl((inl(addr) & mask) | value, addr); -} - -static void set_dmadac1(struct cm_state *s, unsigned int addr, unsigned int count) -{ - if (addr) - outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); - outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2); - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~1, 0); -} - -static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count) -{ - outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); - outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2); - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 1); -} - -static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count) -{ - outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1); - outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2); - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 0); - if (s->status & DO_DUAL_DAC) - set_dmadac1(s, 0, count); -} - -static void set_countadc(struct cm_state *s, unsigned count) -{ - outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2); -} - -static void set_countdac(struct cm_state *s, unsigned count) -{ - outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2); - if (s->status & DO_DUAL_DAC) - set_countadc(s, count); -} - -static inline unsigned get_dmadac(struct cm_state *s) -{ - unsigned int curr_addr; - - curr_addr = inw(s->iobase + CODEC_CMI_CH1_FRAME2) + 1; - curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; - curr_addr = s->dma_dac.dmasize - curr_addr; - - return curr_addr; -} - -static inline unsigned get_dmaadc(struct cm_state *s) -{ - unsigned int curr_addr; - - curr_addr = inw(s->iobase + CODEC_CMI_CH0_FRAME2) + 1; - curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]; - curr_addr = s->dma_adc.dmasize - curr_addr; - - return curr_addr; -} - -static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data) -{ - outb(idx, s->iobase + CODEC_SB16_ADDR); - udelay(10); - outb(data, s->iobase + CODEC_SB16_DATA); - udelay(10); -} - -static unsigned char rdmixer(struct cm_state *s, unsigned char idx) -{ - unsigned char v; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - outb(idx, s->iobase + CODEC_SB16_ADDR); - udelay(10); - v = inb(s->iobase + CODEC_SB16_DATA); - udelay(10); - spin_unlock_irqrestore(&s->lock, flags); - return v; -} - -static void set_fmt_unlocked(struct cm_state *s, unsigned char mask, unsigned char data) -{ - if (mask) - { - s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT); - udelay(10); - } - s->fmt = (s->fmt & mask) | data; - outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT); - udelay(10); -} - -static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - set_fmt_unlocked(s,mask,data); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data) -{ - outb(idx, s->iobase + CODEC_SB16_ADDR); - udelay(10); - outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA); - udelay(10); -} - -static struct { - unsigned rate; - unsigned lower; - unsigned upper; - unsigned char freq; -} rate_lookup[] = -{ - { 5512, (0 + 5512) / 2, (5512 + 8000) / 2, 0 }, - { 8000, (5512 + 8000) / 2, (8000 + 11025) / 2, 4 }, - { 11025, (8000 + 11025) / 2, (11025 + 16000) / 2, 1 }, - { 16000, (11025 + 16000) / 2, (16000 + 22050) / 2, 5 }, - { 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2 }, - { 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6 }, - { 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3 }, - { 48000, (44100 + 48000) / 2, 48000, 7 } -}; - -static void set_spdifout_unlocked(struct cm_state *s, unsigned rate) -{ - if (rate == 48000 || rate == 44100) { - // SPDIFI48K SPDF_ACc97 - maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~0x01008000, rate == 48000 ? 0x01008000 : 0); - // ENSPDOUT - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0, 0x80); - // SPDF_1 SPD2DAC - maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x240); - // CDPLAY - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1); - s->status |= DO_SPDIF_OUT; - } else { - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0x80, 0); - maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0x240, 0); - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0); - s->status &= ~DO_SPDIF_OUT; - } -} - -static void set_spdifout(struct cm_state *s, unsigned rate) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - set_spdifout_unlocked(s,rate); - spin_unlock_irqrestore(&s->lock, flags); -} - -/* find parity for bit 4~30 */ -static unsigned parity(unsigned data) -{ - unsigned parity = 0; - int counter = 4; - - data >>= 4; // start from bit 4 - while (counter <= 30) { - if (data & 1) - parity++; - data >>= 1; - counter++; - } - return parity & 1; -} - -static void set_ac3_unlocked(struct cm_state *s, unsigned rate) -{ - /* enable AC3 */ - if (rate == 48000 || rate == 44100) { - // mute DAC - maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x40); - // AC3EN for 037, 0x10 - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x10); - // AC3EN for 039, 0x04 - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x04); - if (s->capability & CAN_AC3_HW) { - // SPD24SEL for 037, 0x02 - // SPD24SEL for 039, 0x20, but cannot be set - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x02); - s->status |= DO_AC3_HW; - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0); - } else { - // SPD32SEL for 037 & 039, 0x20 - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x20); - // set 176K sample rate to fix 033 HW bug - if (s->chip_version == 33) { - if (rate == 48000) - maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0, 0x08); - else - maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); - } - s->status |= DO_AC3_SW; - } - } else { - maskb(s->iobase + CODEC_CMI_MIXER1, ~0x40, 0); - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0x32, 0); - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x24, 0); - maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); - if (s->chip_version == 33) - maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1); - s->status &= ~DO_AC3; - } - s->spdif_counter = 0; - -} - -static void set_ac3(struct cm_state *s, unsigned rate) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - set_spdifout_unlocked(s, rate); - set_ac3_unlocked(s,rate); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void trans_ac3(struct cm_state *s, void *dest, const char *source, int size) -{ - int i = size / 2; - unsigned long data; - unsigned long *dst = (unsigned long *) dest; - unsigned short *src = (unsigned short *)source; - - do { - data = (unsigned long) *src++; - data <<= 12; // ok for 16-bit data - if (s->spdif_counter == 2 || s->spdif_counter == 3) - data |= 0x40000000; // indicate AC-3 raw data - if (parity(data)) - data |= 0x80000000; // parity - if (s->spdif_counter == 0) - data |= 3; // preamble 'M' - else if (s->spdif_counter & 1) - data |= 5; // odd, 'W' - else - data |= 9; // even, 'M' - *dst++ = data; - s->spdif_counter++; - if (s->spdif_counter == 384) - s->spdif_counter = 0; - } while (--i); -} - -static void set_adc_rate_unlocked(struct cm_state *s, unsigned rate) -{ - unsigned char freq = 4; - int i; - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { - if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { - rate = rate_lookup[i].rate; - freq = rate_lookup[i].freq; - break; - } - } - s->rateadc = rate; - freq <<= 2; - - maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq); -} - -static void set_adc_rate(struct cm_state *s, unsigned rate) -{ - unsigned long flags; - unsigned char freq = 4; - int i; - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { - if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { - rate = rate_lookup[i].rate; - freq = rate_lookup[i].freq; - break; - } - } - s->rateadc = rate; - freq <<= 2; - - spin_lock_irqsave(&s->lock, flags); - maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void set_dac_rate(struct cm_state *s, unsigned rate) -{ - unsigned long flags; - unsigned char freq = 4; - int i; - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { - if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { - rate = rate_lookup[i].rate; - freq = rate_lookup[i].freq; - break; - } - } - s->ratedac = rate; - freq <<= 5; - - spin_lock_irqsave(&s->lock, flags); - maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0xe0, freq); - - - if (s->curr_channels <= 2) - set_spdifout_unlocked(s, rate); - if (s->status & DO_DUAL_DAC) - set_adc_rate_unlocked(s, rate); - - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ -static inline void reset_adc(struct cm_state *s) -{ - /* reset bus master */ - outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - udelay(10); - outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); -} - -static inline void reset_dac(struct cm_state *s) -{ - /* reset bus master */ - outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - if (s->status & DO_DUAL_DAC) - reset_adc(s); -} - -static inline void pause_adc(struct cm_state *s) -{ - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 4); -} - -static inline void pause_dac(struct cm_state *s) -{ - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 8); - if (s->status & DO_DUAL_DAC) - pause_adc(s); -} - -static inline void disable_adc(struct cm_state *s) -{ - /* disable channel */ - s->enable &= ~CM_ENABLE_CH0; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - reset_adc(s); -} - -static inline void disable_dac(struct cm_state *s) -{ - /* disable channel */ - s->enable &= ~CM_ENABLE_CH1; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - reset_dac(s); - if (s->status & DO_DUAL_DAC) - disable_adc(s); -} - -static inline void enable_adc(struct cm_state *s) -{ - if (!(s->enable & CM_ENABLE_CH0)) { - /* enable channel */ - s->enable |= CM_ENABLE_CH0; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - } - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~4, 0); -} - -static inline void enable_dac_unlocked(struct cm_state *s) -{ - if (!(s->enable & CM_ENABLE_CH1)) { - /* enable channel */ - s->enable |= CM_ENABLE_CH1; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - } - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~8, 0); - - if (s->status & DO_DUAL_DAC) - enable_adc(s); -} - -static inline void enable_dac(struct cm_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - enable_dac_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_adc_unlocked(struct cm_state *s) -{ - if (s->enable & CM_ENABLE_CH0) { - /* disable interrupt */ - maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~1, 0); - disable_adc(s); - } -} - -static inline void stop_adc(struct cm_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - stop_adc_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); - -} - -static inline void stop_dac_unlocked(struct cm_state *s) -{ - if (s->enable & CM_ENABLE_CH1) { - /* disable interrupt */ - maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~2, 0); - disable_dac(s); - } - if (s->status & DO_DUAL_DAC) - stop_adc_unlocked(s); -} - -static inline void stop_dac(struct cm_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - stop_dac_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_adc_unlocked(struct cm_state *s) -{ - if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - /* enable interrupt */ - maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1); - enable_adc(s); - } -} - -static void start_adc(struct cm_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - start_adc_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac1_unlocked(struct cm_state *s) -{ - if ((s->dma_adc.mapped || s->dma_adc.count > 0) && s->dma_adc.ready) { - /* enable interrupt */ -// maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1); - enable_dac_unlocked(s); - } -} - -static void start_dac_unlocked(struct cm_state *s) -{ - if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { - /* enable interrupt */ - maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 2); - enable_dac_unlocked(s); - } - if (s->status & DO_DUAL_DAC) - start_dac1_unlocked(s); -} - -static void start_dac(struct cm_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - start_dac_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); -} - -static int prog_dmabuf(struct cm_state *s, unsigned rec); - -static int set_dac_channels(struct cm_state *s, int channels) -{ - unsigned long flags; - spin_lock_irqsave(&s->lock, flags); - - if ((channels > 2) && (channels <= s->max_channels) - && (((s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK) == (CM_CFMT_STEREO | CM_CFMT_16BIT))) { - set_spdifout_unlocked(s, 0); - if (s->capability & CAN_MULTI_CH_HW) { - // NXCHG - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0, 0x80); - // CHB3D or CHB3D5C - maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, channels > 4 ? 0x80 : 0x20); - // CHB3D6C - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, channels == 6 ? 0x80 : 0); - // ENCENTER - maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~0x80, channels == 6 ? 0x80 : 0); - s->status |= DO_MULTI_CH_HW; - } else if (s->capability & CAN_DUAL_DAC) { - unsigned char fmtm = ~0, fmts = 0; - ssize_t ret; - - // ENDBDAC, turn on double DAC mode - // XCHGDAC, CH0 -> back, CH1->front - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0xC0); - s->status |= DO_DUAL_DAC; - // prepare secondary buffer - - spin_unlock_irqrestore(&s->lock, flags); - ret = prog_dmabuf(s, 1); - if (ret) return ret; - spin_lock_irqsave(&s->lock, flags); - - // copy the hw state - fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); - fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); - // the HW only support 16-bit stereo - fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; - fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; - fmts |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; - fmts |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - - set_fmt_unlocked(s, fmtm, fmts); - set_adc_rate_unlocked(s, s->ratedac); - - } - - if (s->speakers > 2) - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0x04, 0); - s->curr_channels = channels; - } else { - if (s->status & DO_MULTI_CH_HW) { - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x80, 0); - maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, 0); - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, 0); - } else if (s->status & DO_DUAL_DAC) { - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x80, 0); - } - // N4SPK3D, enable 4 speaker mode (analog duplicate) - if (s->speakers > 2) - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0, 0x04); - s->status &= ~DO_MULTI_CH; - s->curr_channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; - } - - spin_unlock_irqrestore(&s->lock, flags); - return s->curr_channels; -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -static void dealloc_dmabuf(struct dmabuf *db) -{ - struct page *pstart, *pend; - - if (db->rawbuf) { - /* undo marking the pages as reserved */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) - mem_map_unreserve(pstart); - free_pages((unsigned long)db->rawbuf, db->buforder); - } - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - -/* Ch1 is used for playback, Ch0 is used for recording */ - -static int prog_dmabuf(struct cm_state *s, unsigned rec) -{ - struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; - unsigned rate = rec ? s->rateadc : s->ratedac; - int order; - unsigned bytepersec; - unsigned bufs; - struct page *pstart, *pend; - unsigned char fmt; - unsigned long flags; - - fmt = s->fmt; - if (rec) { - stop_adc(s); - fmt >>= CM_CFMT_ADCSHIFT; - } else { - stop_dac(s); - fmt >>= CM_CFMT_DACSHIFT; - } - - fmt &= CM_CFMT_MASK; - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - if (!db->rawbuf) { - db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) - if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order))) - break; - if (!db->rawbuf) - return -ENOMEM; - db->buforder = order; - db->rawphys = virt_to_bus(db->rawbuf); - if ((db->rawphys ^ (db->rawphys + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) - printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", - (long) db->rawphys, PAGE_SIZE << db->buforder); - if ((db->rawphys + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) - printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", - (long) db->rawphys, PAGE_SIZE << db->buforder); - /* 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 (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) - mem_map_reserve(pstart); - } - 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--; - db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - /* to make fragsize >= 4096 */ - db->fragsamples = db->fragsize >> sample_shift[fmt]; - db->dmasize = db->numfrag << db->fragshift; - db->dmasamples = db->dmasize >> sample_shift[fmt]; - memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize); - spin_lock_irqsave(&s->lock, flags); - if (rec) { - if (s->status & DO_DUAL_DAC) - set_dmadac1(s, db->rawphys, db->dmasize >> sample_shift[fmt]); - else - set_dmaadc(s, db->rawphys, db->dmasize >> sample_shift[fmt]); - /* program sample counts */ - set_countdac(s, db->fragsamples); - } else { - set_dmadac(s, db->rawphys, db->dmasize >> sample_shift[fmt]); - /* program sample counts */ - set_countdac(s, db->fragsamples); - } - spin_unlock_irqrestore(&s->lock, flags); - db->ready = 1; - return 0; -} - -static inline void clear_advance(struct cm_state *s) -{ - unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80; - unsigned char *buf = s->dma_dac.rawbuf; - unsigned char *buf1 = s->dma_adc.rawbuf; - unsigned bsize = s->dma_dac.dmasize; - unsigned bptr = s->dma_dac.swptr; - unsigned len = s->dma_dac.fragsize; - - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(buf + bptr, c, x); - if (s->status & DO_DUAL_DAC) - memset(buf1 + bptr, c, x); - bptr = 0; - len -= x; - } - memset(buf + bptr, c, len); - if (s->status & DO_DUAL_DAC) - memset(buf1 + bptr, c, len); -} - -/* call with spinlock held! */ -static void cm_update_ptr(struct cm_state *s) -{ - unsigned hwptr; - int diff; - - /* update ADC pointer */ - if (s->dma_adc.ready) { - if (s->status & DO_DUAL_DAC) { - hwptr = get_dmaadc(s) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - if (s->dma_adc.mapped) { - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - } else { - s->dma_adc.count -= diff; - if (s->dma_adc.count <= 0) { - pause_adc(s); - s->dma_adc.error++; - } else if (s->dma_adc.count <= (signed)s->dma_adc.fragsize && !s->dma_adc.endcleared) { - clear_advance(s); - s->dma_adc.endcleared = 1; - } - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) - wake_up(&s->dma_adc.wait); - } - } else { - hwptr = get_dmaadc(s) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - pause_adc(s); - s->dma_adc.error++; - } - } - } - } - /* update DAC pointer */ - if (s->dma_dac.ready) { - hwptr = get_dmadac(s) % s->dma_dac.dmasize; - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - wake_up(&s->dma_dac.wait); - } else { - s->dma_dac.count -= diff; - if (s->dma_dac.count <= 0) { - pause_dac(s); - s->dma_dac.error++; - } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { - clear_advance(s); - s->dma_dac.endcleared = 1; - } - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) - wake_up(&s->dma_dac.wait); - } - } -} - -#ifdef CONFIG_SOUND_CMPCI_MIDI -/* hold spinlock for the following! */ -static void cm_handle_midi(struct cm_state *s) -{ - unsigned char ch; - int wake; - - wake = 0; - while (!(inb(s->iomidi+1) & 0x80)) { - ch = inb(s->iomidi); - if (s->midi.icnt < MIDIINBUF) { - s->midi.ibuf[s->midi.iwr] = ch; - s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; - s->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&s->midi.iwait); - wake = 0; - while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { - outb(s->midi.obuf[s->midi.ord], s->iomidi); - s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; - s->midi.ocnt--; - if (s->midi.ocnt < MIDIOUTBUF-16) - wake = 1; - } - if (wake) - wake_up(&s->midi.owait); -} -#endif - -static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct cm_state *s = (struct cm_state *)dev_id; - unsigned int intsrc, intstat; - unsigned char mask = 0; - - /* fastpath out, to ease interrupt sharing */ - intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS); - if (!(intsrc & 0x80000000)) - return; - spin_lock(&s->lock); - intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2); - /* acknowledge interrupt */ - if (intsrc & CM_INT_CH0) - mask |= 1; - if (intsrc & CM_INT_CH1) - mask |= 2; - outb(intstat & ~mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - outb(intstat | mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - cm_update_ptr(s); -#ifdef CONFIG_SOUND_CMPCI_MIDI - cm_handle_midi(s); -#endif - spin_unlock(&s->lock); -} - -#ifdef CONFIG_SOUND_CMPCI_MIDI -static void cm_midi_timer(unsigned long data) -{ - struct cm_state *s = (struct cm_state *)data; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - cm_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - s->midi.timer.expires = jiffies+1; - add_timer(&s->midi.timer); -} -#endif - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n"; - -#ifdef CONFIG_SOUND_CMPCI /* support multiple chips */ -#define VALIDATE_STATE(s) -#else -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != CM_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) -#endif - -/* --------------------------------------------------------------------- */ - -#define MT_4 1 -#define MT_5MUTE 2 -#define MT_4MUTEMONO 3 -#define MT_6MUTE 4 -#define MT_5MUTEMONO 5 - -static const struct { - unsigned left; - unsigned right; - unsigned type; - unsigned rec; - unsigned play; -} mixtable[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_CD] = { DSP_MIX_CDVOLIDX_L, DSP_MIX_CDVOLIDX_R, MT_5MUTE, 0x04, 0x02 }, - [SOUND_MIXER_LINE] = { DSP_MIX_LINEVOLIDX_L, DSP_MIX_LINEVOLIDX_R, MT_5MUTE, 0x10, 0x08 }, - [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, DSP_MIX_MICVOLIDX, MT_5MUTEMONO, 0x01, 0x01 }, - [SOUND_MIXER_SYNTH] = { DSP_MIX_FMVOLIDX_L, DSP_MIX_FMVOLIDX_R, MT_5MUTE, 0x40, 0x00 }, - [SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, - [SOUND_MIXER_PCM] = { DSP_MIX_VOICEVOLIDX_L, DSP_MIX_VOICEVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, - [SOUND_MIXER_SPEAKER]= { DSP_MIX_SPKRVOLIDX, DSP_MIX_SPKRVOLIDX, MT_5MUTEMONO, 0x01, 0x01 } -}; - -static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = -{ - [SOUND_MIXER_CD] = 1, - [SOUND_MIXER_LINE] = 2, - [SOUND_MIXER_MIC] = 3, - [SOUND_MIXER_SYNTH] = 4, - [SOUND_MIXER_VOLUME] = 5, - [SOUND_MIXER_PCM] = 6, - [SOUND_MIXER_SPEAKER]= 7 -}; - -static unsigned mixer_recmask(struct cm_state *s) -{ - int i, j, k; - - j = rdmixer(s, DSP_MIX_ADCMIXIDX_L); - j &= 0x7f; - for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (j & mixtable[i].rec) - k |= 1 << i; - return k; -} - -static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - int i, val, j; - unsigned char l, r, rl, rr; - - VALIDATE_STATE(s); - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - 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; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - 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; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *)arg); - if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - if (_IOC_DIR(cmd) == _IOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - return put_user(mixer_recmask(s), (int *)arg); - - case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ - return put_user(mixer_recmask(s), (int *)arg);//need fix - - case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].type) - val |= 1 << i; - return put_user(val, (int *)arg); - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].rec) - val |= 1 << i; - return put_user(val, (int *)arg); - - case SOUND_MIXER_OUTMASK: /* Arg contains a bit for each supported recording source */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].play) - val |= 1 << i; - return put_user(val, (int *)arg); - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) - val |= 1 << i; - return put_user(val, (int *)arg); - - case SOUND_MIXER_CAPS: - return put_user(0, (int *)arg); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) - return -EINVAL; - if (!volidx[i]) - return -EINVAL; - return put_user(s->mix.vol[volidx[i]-1], (int *)arg); - } - } - if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) - return -EINVAL; - s->mix.modcnt++; - 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 = generic_hweight32(val); - for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!(val & (1 << i))) - continue; - if (!mixtable[i].rec) { - val &= ~(1 << i); - continue; - } - j |= mixtable[i].rec; - } - spin_lock_irqsave(&s->lock, flags); - wrmixer(s, DSP_MIX_ADCMIXIDX_L, j); - wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | (j>>1)); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ - if (get_user(val, (int *)arg)) - return -EFAULT; - for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!(val & (1 << i))) - continue; - if (!mixtable[i].play) { - val &= ~(1 << i); - continue; - } - j |= mixtable[i].play; - } - spin_lock_irqsave(&s->lock, flags); - frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, j); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - l = val & 0xff; - r = (val >> 8) & 0xff; - if (l > 100) - l = 100; - if (r > 100) - r = 100; - spin_lock_irqsave(&s->lock, flags); - switch (mixtable[i].type) { - case MT_4: - if (l >= 10) - l -= 10; - if (r >= 10) - r -= 10; - frobindir(s, mixtable[i].left, 0xf0, l / 6); - frobindir(s, mixtable[i].right, 0xf0, l / 6); - break; - - case MT_4MUTEMONO: - rl = (l < 4 ? 0 : (l - 5) / 3) & 31; - rr = (rl >> 2) & 7; - wrmixer(s, mixtable[i].left, rl<<3); - maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); - break; - - case MT_5MUTEMONO: - r = l; - rl = l < 4 ? 0 : (l - 5) / 3; - rr = rl >> 2; - wrmixer(s, mixtable[i].left, rl<<3); - maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); - break; - - case MT_5MUTE: - rl = l < 4 ? 0 : (l - 5) / 3; - rr = r < 4 ? 0 : (r - 5) / 3; - wrmixer(s, mixtable[i].left, rl<<3); - wrmixer(s, mixtable[i].right, rr<<3); - break; - - case MT_6MUTE: - if (l < 6) - rl = 0x00; - else - rl = l * 2 / 3; - if (r < 6) - rr = 0x00; - else - rr = r * 2 / 3; - wrmixer(s, mixtable[i].left, rl); - wrmixer(s, mixtable[i].right, rr); - break; - } - spin_unlock_irqrestore(&s->lock, flags); - - if (!volidx[i]) - return -EINVAL; - s->mix.vol[volidx[i]-1] = val; - return put_user(s->mix.vol[volidx[i]-1], (int *)arg); - } -} - -/* --------------------------------------------------------------------- */ - -static int cm_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct cm_state *s = devs; - - while (s && s->dev_mixer != minor) - s = s->next; - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - return 0; -} - -static int cm_release_mixdev(struct inode *inode, struct file *file) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - - VALIDATE_STATE(s); - return 0; -} - -static int cm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - return mixer_ioctl((struct cm_state *)file->private_data, cmd, arg); -} - -static /*const*/ struct file_operations cm_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: cm_ioctl_mixdev, - open: cm_open_mixdev, - release: cm_release_mixdev, -}; - - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct cm_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&s->dma_dac.wait, &wait); - 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) { - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; - tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; - if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "cmpci: dma timed out??\n"); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static ssize_t cm_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - 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(s, 1))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - start_adc(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { - printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, - s->dma_adc.hwptr, s->dma_adc.swptr); - spin_lock_irqsave(&s->lock, flags); - stop_adc_unlocked(s); - set_dmaadc(s, s->dma_adc.rawphys, s->dma_adc.dmasamples); - /* program sample counts */ - set_countadc(s, s->dma_adc.fragsamples); - s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) - return ret ? ret : -EFAULT; - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - count -= cnt; - buffer += cnt; - ret += cnt; - start_adc_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); - } - return ret; -} - -static ssize_t cm_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - 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(s, 0))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - if (s->status & DO_DUAL_DAC) { - if (s->dma_adc.mapped) - return -ENXIO; - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - } - ret = 0; - - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - if (s->status & DO_DUAL_DAC) { - s->dma_adc.swptr = s->dma_dac.swptr; - s->dma_adc.count = s->dma_dac.count; - s->dma_adc.endcleared = s->dma_dac.endcleared; - } - swptr = s->dma_dac.swptr; - cnt = s->dma_dac.dmasize-swptr; - if (s->status & DO_AC3_SW) { - if (s->dma_dac.count + 2 * cnt > s->dma_dac.dmasize) - cnt = (s->dma_dac.dmasize - s->dma_dac.count) / 2; - } else { - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - } - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if ((s->status & DO_DUAL_DAC) && (cnt > count / 2)) - cnt = count / 2; - if (cnt <= 0) { - start_dac(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { - printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, - s->dma_dac.hwptr, s->dma_dac.swptr); - spin_lock_irqsave(&s->lock, flags); - stop_dac_unlocked(s); - set_dmadac(s, s->dma_dac.rawphys, s->dma_dac.dmasamples); - /* program sample counts */ - set_countdac(s, s->dma_dac.fragsamples); - s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; - if (s->status & DO_DUAL_DAC) { - set_dmadac1(s, s->dma_adc.rawphys, s->dma_adc.dmasamples); - s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; - } - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (s->status & DO_AC3_SW) { - // clip exceeded data, caught by 033 and 037 - if (swptr + 2 * cnt > s->dma_dac.dmasize) - cnt = (s->dma_dac.dmasize - swptr) / 2; - trans_ac3(s, s->dma_dac.rawbuf + swptr, buffer, cnt); - swptr = (swptr + 2 * cnt) % s->dma_dac.dmasize; - } else if (s->status & DO_DUAL_DAC) { - int i; - unsigned long *src, *dst0, *dst1; - - src = (unsigned long *) buffer; - dst0 = (unsigned long *) (s->dma_dac.rawbuf + swptr); - dst1 = (unsigned long *) (s->dma_adc.rawbuf + swptr); - // copy left/right sample at one time - for (i = 0; i <= cnt / 4; i++) { - *dst0++ = *src++; - *dst1++ = *src++; - } - swptr = (swptr + cnt) % s->dma_dac.dmasize; - } else { - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) - return ret ? ret : -EFAULT; - swptr = (swptr + cnt) % s->dma_dac.dmasize; - } - spin_lock_irqsave(&s->lock, flags); - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - if (s->status & DO_AC3_SW) - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->status & DO_DUAL_DAC) { - count -= cnt; - buffer += cnt; - ret += cnt; - } - start_dac(s); - } - return ret; -} - -static unsigned int cm_poll(struct file *file, struct poll_table_struct *wait) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - 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); - cm_update_ptr(s); - 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 cm_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - struct dmabuf *db; - int ret = -EINVAL; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 0)) != 0) - goto out; - db = &s->dma_dac; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 1)) != 0) - goto out; - db = &s->dma_adc; - } else - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) - goto out; - ret = -EINVAL; - if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) - goto out; - db->mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int cm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret; - unsigned char fmtm, fmtd; - - 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: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_SYNC: - 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 | DSP_CAP_BIND, (int *)arg); - - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(); - s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; - if (s->status & DO_DUAL_DAC) - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val >= 0) { - if (file->f_mode & FMODE_READ) { - spin_lock_irqsave(&s->lock, flags); - stop_adc_unlocked(s); - s->dma_adc.ready = 0; - set_adc_rate_unlocked(s, val); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (s->status & DO_DUAL_DAC) - s->dma_adc.ready = 0; - set_dac_rate(s, val); - } - } - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val) - fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val) - fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); - if (s->status & DO_DUAL_DAC) { - s->dma_adc.ready = 0; - if (val) - fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); - } - } - set_fmt(s, fmtm, fmtd); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val >= 2) - fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val >= 2) - fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); - if (s->status & DO_DUAL_DAC) { - s->dma_adc.ready = 0; - if (val >= 2) - fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); - } - } - set_fmt(s, fmtm, fmtd); - if ((s->capability & CAN_MULTI_CH) - && (file->f_mode & FMODE_WRITE)) { - val = set_dac_channels(s, val); - return put_user(val, (int *)arg); - } - } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) - : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_LE|AFMT_U8|AFMT_AC3, (int *)arg); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != AFMT_QUERY) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_ADCSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val == AFMT_S16_LE || val == AFMT_AC3) - fmtd |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; - else - fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_DACSHIFT); - if (val == AFMT_AC3) { - fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; - set_ac3(s, s->ratedac); - } else - set_ac3(s, 0); - if (s->status & DO_DUAL_DAC) { - s->dma_adc.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); - } - } - set_fmt(s, fmtm, fmtd); - } - if (s->status & DO_AC3) return put_user(AFMT_AC3, (int *)arg); - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) - : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (s->status & DO_DUAL_DAC) { - if (file->f_mode & FMODE_WRITE && - (s->enable & CM_ENABLE_CH1) && - (s->enable & CM_ENABLE_CH0)) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, (int *)arg); - } - if (file->f_mode & FMODE_READ && s->enable & CM_ENABLE_CH0) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->enable & CM_ENABLE_CH1) - 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(s, 1))) - 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(s, 0))) - return ret; - if (s->status & DO_DUAL_DAC) { - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - 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->enable & CM_ENABLE_CH1) && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - 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->enable & CM_ENABLE_CH0) && (val = prog_dmabuf(s, 1)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - 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; - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - 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; - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; - 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; - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - 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; - if (s->status & DO_DUAL_DAC) { - 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_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf(s, 0))) - return val; - if (s->status & DO_DUAL_DAC) { - if ((val = prog_dmabuf(s, 1))) - return val; - return put_user(2 * s->dma_dac.fragsize, (int *)arg); - } - return put_user(s->dma_dac.fragsize, (int *)arg); - } - if ((val = prog_dmabuf(s, 1))) - return val; - 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) { - 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 (file->f_mode & FMODE_WRITE) { - 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 (s->status & DO_DUAL_DAC) { - s->dma_adc.ossfragshift = s->dma_dac.ossfragshift; - s->dma_adc.ossmaxfrags = s->dma_dac.ossmaxfrags; - } - } - 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) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) { - s->dma_dac.subdivision = val; - if (s->status & DO_DUAL_DAC) - s->dma_adc.subdivision = val; - } - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg); - - case SOUND_PCM_READ_BITS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? 16 : 8, (int *)arg); - - case SOUND_PCM_READ_FILTER: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - - case SNDCTL_DSP_GETCHANNELMASK: - return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE|DSP_BIND_SPDIF, (int *)arg); - - case SNDCTL_DSP_BIND_CHANNEL: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val == DSP_BIND_QUERY) { - val = DSP_BIND_FRONT; - if (s->status & DO_SPDIF_OUT) - val |= DSP_BIND_SPDIF; - else { - if (s->curr_channels == 4) - val |= DSP_BIND_SURR; - if (s->curr_channels > 4) - val |= DSP_BIND_CENTER_LFE; - } - } else { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val & DSP_BIND_SPDIF) { - set_spdifout(s, s->ratedac); - set_dac_channels(s, s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1); - if (!(s->status & DO_SPDIF_OUT)) - val &= ~DSP_BIND_SPDIF; - } else { - int channels; - int mask; - - 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: - channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; - break; - } - set_dac_channels(s, channels); - } - } - } - return put_user(val, (int *)arg); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_MAPINBUF: - case SNDCTL_DSP_MAPOUTBUF: - case SNDCTL_DSP_SETSYNCRO: - return -EINVAL; - - } - return mixer_ioctl(s, cmd, arg); -} - -static int cm_open(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct cm_state *s = devs; - unsigned char fmtm = ~0, fmts = 0; - - while (s && ((s->dev_audio ^ minor) & ~0xf)) - s = s->next; - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - 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; - } - up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - down(&s->open_sem); - } - if (file->f_mode & FMODE_READ) { - fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - set_adc_rate(s, 8000); - } - if (file->f_mode & FMODE_WRITE) { - fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; - set_dac_rate(s, 8000); - // clear previous multichannel, spdif, ac3 state - set_spdifout(s, 0); - if (s->deviceid == PCI_DEVICE_ID_CMEDIA_CM8738) { - set_ac3(s, 0); - set_dac_channels(s, 1); - } - } - set_fmt(s, fmtm, fmts); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - up(&s->open_sem); - return 0; -} - -static int cm_release(struct inode *inode, struct file *file) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - - VALIDATE_STATE(s); - 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->dma_dac); - if (s->status & DO_DUAL_DAC) - dealloc_dmabuf(&s->dma_adc); - - if (s->status & DO_MULTI_CH) - set_dac_channels(s, 0); - if (s->status & DO_AC3) - set_ac3(s, 0); - if (s->status & DO_SPDIF_OUT) - set_spdifout(s, 0); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - dealloc_dmabuf(&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 cm_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: cm_read, - write: cm_write, - poll: cm_poll, - ioctl: cm_ioctl, - mmap: cm_mmap, - open: cm_open, - release: cm_release, -}; - -#ifdef CONFIG_SOUND_CMPCI_MIDI -/* --------------------------------------------------------------------- */ - -static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - add_wait_queue(&s->midi.iwait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.ird; - cnt = MIDIINBUF - ptr; - if (s->midi.icnt < cnt) - cnt = s->midi.icnt; - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - { - if (!ret) - ret = -EAGAIN; - break; - } - __set_current_state(TASK_INTERRUPTIBLE); - schedule(); - if (signal_pending(current)) - { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIINBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.ird = ptr; - s->midi.icnt -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - break; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.iwait, &wait); - return ret; -} - -static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.owait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.owr; - cnt = MIDIOUTBUF - ptr; - if (s->midi.ocnt + cnt > MIDIOUTBUF) - cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) - cm_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - { - if (!ret) - ret = -EAGAIN; - break; - } - __set_current_state(TASK_INTERRUPTIBLE); - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIOUTBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.owr = ptr; - s->midi.ocnt += cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&s->lock, flags); - cm_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.owait, &wait); - return ret; -} - -static unsigned int cm_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &s->midi.owait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &s->midi.iwait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - if (s->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->midi.ocnt < MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int cm_midi_open(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct cm_state *s = devs; - unsigned long flags; - - while (s && s->dev_midi != minor) - s = s->next; - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - if (file->f_flags & O_NONBLOCK) { - up(&s->open_sem); - return -EBUSY; - } - up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - down(&s->open_sem); - } - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - /* enable MPU-401 */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 4); - outb(0xff, s->iomidi+1); /* reset command */ - if (!(inb(s->iomidi+1) & 0x80)) - inb(s->iomidi); - outb(0x3f, s->iomidi+1); /* uart command */ - if (!(inb(s->iomidi+1) & 0x80)) - inb(s->iomidi); - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - init_timer(&s->midi.timer); - s->midi.timer.expires = jiffies+1; - s->midi.timer.data = (unsigned long)s; - s->midi.timer.function = cm_midi_timer; - add_timer(&s->midi.timer); - } - if (file->f_mode & FMODE_READ) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - } - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); - up(&s->open_sem); - return 0; -} - -static int cm_midi_release(struct inode *inode, struct file *file) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - VALIDATE_STATE(s); - lock_kernel(); - - if (file->f_mode & FMODE_WRITE) { - __set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&s->midi.owait, &wait); - for (;;) { - spin_lock_irqsave(&s->lock, flags); - count = s->midi.ocnt; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - 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; - } - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "cmpci: midi timed out??\n"); - } - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - } - down(&s->open_sem); - s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - del_timer(&s->midi.timer); - outb(0xff, s->iomidi+1); /* reset command */ - if (!(inb(s->iomidi+1) & 0x80)) - inb(s->iomidi); - /* disable MPU-401 */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~4, 0); - } - spin_unlock_irqrestore(&s->lock, flags); - up(&s->open_sem); - wake_up(&s->open_wait); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations cm_midi_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: cm_midi_read, - write: cm_midi_write, - poll: cm_midi_poll, - open: cm_midi_open, - release: cm_midi_release, -}; -#endif - -/* --------------------------------------------------------------------- */ - -#ifdef CONFIG_SOUND_CMPCI_FM -static int cm_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - static const unsigned char op_offset[18] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 - }; - struct cm_state *s = (struct cm_state *)file->private_data; - struct dm_fm_voice v; - struct dm_fm_note n; - struct dm_fm_params p; - unsigned int io; - unsigned int regb; - - switch (cmd) { - case FM_IOCTL_RESET: - for (regb = 0xb0; regb < 0xb9; regb++) { - outb(regb, s->iosynth); - outb(0, s->iosynth+1); - outb(regb, s->iosynth+2); - outb(0, s->iosynth+3); - } - return 0; - - case FM_IOCTL_PLAY_NOTE: - if (copy_from_user(&n, (void *)arg, sizeof(n))) - return -EFAULT; - if (n.voice >= 18) - return -EINVAL; - if (n.voice >= 9) { - regb = n.voice - 9; - io = s->iosynth+2; - } else { - regb = n.voice; - io = s->iosynth; - } - outb(0xa0 + regb, io); - outb(n.fnum & 0xff, io+1); - outb(0xb0 + regb, io); - outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); - return 0; - - case FM_IOCTL_SET_VOICE: - if (copy_from_user(&v, (void *)arg, sizeof(v))) - return -EFAULT; - if (v.voice >= 18) - return -EINVAL; - regb = op_offset[v.voice]; - io = s->iosynth + ((v.op & 1) << 1); - outb(0x20 + regb, io); - outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | - ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); - outb(0x40 + regb, io); - outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); - outb(0x60 + regb, io); - outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); - outb(0x80 + regb, io); - outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); - outb(0xe0 + regb, io); - outb(v.waveform & 0x7, io+1); - if (n.voice >= 9) { - regb = n.voice - 9; - io = s->iosynth+2; - } else { - regb = n.voice; - io = s->iosynth; - } - outb(0xc0 + regb, io); - outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | - (v.connection & 1), io+1); - return 0; - - case FM_IOCTL_SET_PARAMS: - if (copy_from_user(&p, (void *)arg, sizeof(p))) - return -EFAULT; - outb(0x08, s->iosynth); - outb((p.kbd_split & 1) << 6, s->iosynth+1); - outb(0xbd, s->iosynth); - outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | - ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1); - return 0; - - case FM_IOCTL_SET_OPL: - outb(4, s->iosynth+2); - outb(arg, s->iosynth+3); - return 0; - - case FM_IOCTL_SET_MODE: - outb(5, s->iosynth+2); - outb(arg & 1, s->iosynth+3); - return 0; - } - return -EINVAL; -} - -static int cm_dmfm_open(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct cm_state *s = devs; - - while (s && s->dev_dmfm != minor) - s = s->next; - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & FMODE_DMFM) { - if (file->f_flags & O_NONBLOCK) { - up(&s->open_sem); - return -EBUSY; - } - up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - down(&s->open_sem); - } - /* init the stuff */ - outb(1, s->iosynth); - outb(0x20, s->iosynth+1); /* enable waveforms */ - outb(4, s->iosynth+2); - outb(0, s->iosynth+3); /* no 4op enabled */ - outb(5, s->iosynth+2); - outb(1, s->iosynth+3); /* enable OPL3 */ - s->open_mode |= FMODE_DMFM; - up(&s->open_sem); - return 0; -} - -static int cm_dmfm_release(struct inode *inode, struct file *file) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - unsigned int regb; - - VALIDATE_STATE(s); - lock_kernel(); - down(&s->open_sem); - s->open_mode &= ~FMODE_DMFM; - for (regb = 0xb0; regb < 0xb9; regb++) { - outb(regb, s->iosynth); - outb(0, s->iosynth+1); - outb(regb, s->iosynth+2); - outb(0, s->iosynth+3); - } - up(&s->open_sem); - wake_up(&s->open_wait); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations cm_dmfm_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: cm_dmfm_ioctl, - open: cm_dmfm_open, - release: cm_dmfm_release, -}; -#endif /* CONFIG_SOUND_CMPCI_FM */ - - - -static struct initvol { - int mixch; - int vol; -} initvol[] __initdata = { - { SOUND_MIXER_WRITE_CD, 0x4f4f }, - { SOUND_MIXER_WRITE_LINE, 0x4f4f }, - { SOUND_MIXER_WRITE_MIC, 0x4f4f }, - { SOUND_MIXER_WRITE_SYNTH, 0x4f4f }, - { SOUND_MIXER_WRITE_VOLUME, 0x4f4f }, - { SOUND_MIXER_WRITE_PCM, 0x4f4f } -}; - -/* check chip version and capability */ -static int query_chip(struct cm_state *s) -{ - int ChipVersion = -1; - unsigned char RegValue; - - // check reg 0Ch, bit 24-31 - RegValue = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 3); - if (RegValue == 0) { - // check reg 08h, bit 24-28 - RegValue = inb(s->iobase + CODEC_CMI_CHFORMAT + 3); - RegValue &= 0x1f; - if (RegValue == 0) { - ChipVersion = 33; - s->max_channels = 4; - s->capability |= CAN_AC3_SW; - s->capability |= CAN_DUAL_DAC; - } else { - ChipVersion = 37; - s->max_channels = 4; - s->capability |= CAN_AC3_HW; - s->capability |= CAN_DUAL_DAC; - } - } else { - // check reg 0Ch, bit 26 - if (RegValue & (1 << (26-24))) { - ChipVersion = 39; - if (RegValue & (1 << (24-24))) - s->max_channels = 6; - else - s->max_channels = 4; - s->capability |= CAN_AC3_HW; - s->capability |= CAN_DUAL_DAC; - s->capability |= CAN_MULTI_CH_HW; - } else { - ChipVersion = 55; // 4 or 6 channels - s->max_channels = 6; - s->capability |= CAN_AC3_HW; - s->capability |= CAN_DUAL_DAC; - s->capability |= CAN_MULTI_CH_HW; - } - } - // still limited to number of speakers - if (s->max_channels > s->speakers) - s->max_channels = s->speakers; - return ChipVersion; -} - -#ifdef CONFIG_SOUND_CMPCI_MIDI -static int mpuio = CONFIG_SOUND_CMPCI_MPUIO; -#else -static int mpuio; -#endif -#ifdef CONFIG_SOUND_CMPCI_FM -static int fmio = CONFIG_SOUND_CMPCI_FMIO; -#else -static int fmio; -#endif -#ifdef CONFIG_SOUND_CMPCI_SPDIFINVERSE -static int spdif_inverse = 1; -#else -static int spdif_inverse; -#endif -#ifdef CONFIG_SOUND_CMPCI_SPDIFLOOP -static int spdif_loop = 1; -#else -static int spdif_loop; -#endif -#ifdef CONFIG_SOUND_CMPCI_SPEAKERS -static int speakers = CONFIG_SOUND_CMPCI_SPEAKERS; -#else -static int speakers = 2; -#endif -#ifdef CONFIG_SOUND_CMPCI_LINE_REAR -static int use_line_as_rear = 1; -#else -static int use_line_as_rear; -#endif -#ifdef CONFIG_SOUND_CMPCI_LINE_BASS -static int use_line_as_bass = 1; -#else -static int use_line_as_bass; -#endif -#ifdef CONFIG_SOUND_CMPCI_JOYSTICK -static int joystick = 1; -#else -static int joystick; -#endif -MODULE_PARM(mpuio, "i"); -MODULE_PARM(fmio, "i"); -MODULE_PARM(spdif_inverse, "i"); -MODULE_PARM(spdif_loop, "i"); -MODULE_PARM(speakers, "i"); -MODULE_PARM(use_line_as_rear, "i"); -MODULE_PARM(use_line_as_bass, "i"); -MODULE_PARM(joystick, "i"); -MODULE_PARM_DESC(mpuio, "(0x330, 0x320, 0x310, 0x300) Base of MPU-401, 0 to disable"); -MODULE_PARM_DESC(fmio, "(0x388, 0x3C8, 0x3E0) Base of OPL3, 0 to disable"); -MODULE_PARM_DESC(spdif_inverse, "(1/0) Invert S/PDIF-in signal"); -MODULE_PARM_DESC(spdif_loop, "(1/0) Route S/PDIF-in to S/PDIF-out directly"); -MODULE_PARM_DESC(speakers, "(2-6) Number of speakers you connect"); -MODULE_PARM_DESC(use_line_as_rear, "(1/0) Use line-in jack as rear-out"); -MODULE_PARM_DESC(use_line_as_bass, "(1/0) Use line-in jack as bass/center"); -MODULE_PARM_DESC(joystick, "(1/0) Enable joystick interface, still need joystick driver"); - -static struct pci_device_id cmpci_pci_tbl[] = { - { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, cmpci_pci_tbl); - -void initialize_chip(struct pci_dev *pcidev) -{ - struct cm_state *s; - mm_segment_t fs; - int i, val; -#if defined(CONFIG_SOUND_CMPCI_MIDI) || defined(CONFIG_SOUND_CMPCI_FM) - unsigned char reg_mask = 0; -#endif - struct { - unsigned short deviceid; - char *devicename; - } devicetable[] = - { - { PCI_DEVICE_ID_CMEDIA_CM8338A, "CM8338A" }, - { PCI_DEVICE_ID_CMEDIA_CM8338B, "CM8338B" }, - { PCI_DEVICE_ID_CMEDIA_CM8738, "CM8738" }, - { PCI_DEVICE_ID_CMEDIA_CM8738B, "CM8738B" }, - }; - char *devicename = "unknown"; - { - if (pci_enable_device(pcidev)) - return; - if (pcidev->irq == 0) - return; - s = kmalloc(sizeof(*s), GFP_KERNEL); - if (!s) { - printk(KERN_WARNING "cmpci: out of memory\n"); - return; - } - /* search device name */ - for (i = 0; i < sizeof(devicetable) / sizeof(devicetable[0]); i++) { - if (devicetable[i].deviceid == pcidev->device) - { - devicename = devicetable[i].devicename; - break; - } - } - memset(s, 0, sizeof(struct cm_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->midi.iwait); - init_waitqueue_head(&s->midi.owait); - init_MUTEX(&s->open_sem); - spin_lock_init(&s->lock); - s->magic = CM_MAGIC; - s->iobase = pci_resource_start(pcidev, 0); - s->iosynth = fmio; - s->iomidi = mpuio; - s->status = 0; - /* range check */ - if (speakers < 2) - speakers = 2; - else if (speakers > 6) - speakers = 6; - s->speakers = speakers; - if (s->iobase == 0) - return; - s->irq = pcidev->irq; - - if (!request_region(s->iobase, CM_EXTENT_CODEC, "cmpci")) { - printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1); - goto err_region5; - } -#ifdef CONFIG_SOUND_CMPCI_MIDI - /* disable MPU-401 */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0); - if (s->iomidi) { - if (!request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi")) { - printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1); - s->iomidi = 0; - } else { - /* set IO based at 0x330 */ - switch (s->iomidi) { - case 0x330: - reg_mask = 0; - break; - case 0x320: - reg_mask = 0x20; - break; - case 0x310: - reg_mask = 0x40; - break; - case 0x300: - reg_mask = 0x60; - break; - default: - s->iomidi = 0; - break; - } - outb((inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60) | reg_mask, s->iobase + CODEC_CMI_LEGACY_CTRL + 3); - /* enable MPU-401 */ - if (s->iomidi) { - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); - } - } - } -#endif -#ifdef CONFIG_SOUND_CMPCI_FM - /* disable FM */ - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0); - if (s->iosynth) { - if (!request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM")) { - printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1); - s->iosynth = 0; - } else { - /* set IO based at 0x388 */ - switch (s->iosynth) { - case 0x388: - reg_mask = 0; - break; - case 0x3C8: - reg_mask = 0x01; - break; - case 0x3E0: - reg_mask = 0x02; - break; - case 0x3E8: - reg_mask = 0x03; - break; - default: - s->iosynth = 0; - break; - } - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x03, reg_mask); - /* enable FM */ - if (s->iosynth) { - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 8); - } - } - } -#endif - /* enable joystick */ - if (joystick) - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x02); - else - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x02, 0); - /* initialize codec registers */ - outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ - outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ - /* reset mixer */ - wrmixer(s, DSP_MIX_DATARESETIDX, 0); - - /* request irq */ - if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) { - printk(KERN_ERR "cmpci: irq %u in use\n", s->irq); - goto err_irq; - } - printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n", - devicename, s->iobase, s->irq); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0) - goto err_dev1; - if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0) - goto err_dev2; -#ifdef CONFIG_SOUND_CMPCI_MIDI - if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0) - goto err_dev3; -#endif -#ifdef CONFIG_SOUND_CMPCI_FM - if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0) - goto err_dev4; -#endif - pci_set_master(pcidev); /* enable bus mastering */ - /* initialize the chips */ - fs = get_fs(); - set_fs(KERNEL_DS); - /* set mixer output */ - frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, 0x1f); - /* set mixer input */ - val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD|SOUND_MASK_MIC; - 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); - } - /* use channel 0 for record, channel 1 for play */ - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 1); - s->deviceid = pcidev->device; - - if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738) { - - /* chip version and hw capability check */ - s->chip_version = query_chip(s); - printk(KERN_INFO "cmpci: chip version = 0%d\n", s->chip_version); - - /* seet SPDIF-in inverse before enable SPDIF loop */ - if (spdif_inverse) { - /* turn on spdif-in inverse */ - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 1); - printk(KERN_INFO "cmpci: Inverse SPDIF-in\n"); - } else { - /* turn off spdif-ininverse */ - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~1, 0); - } - - /* enable SPDIF loop */ - if (spdif_loop) { - s->status |= DO_SPDIF_LOOP; - /* turn on spdif-in to spdif-out */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x80); - printk(KERN_INFO "cmpci: Enable SPDIF loop\n"); - } else { - s->status &= ~DO_SPDIF_LOOP; - /* turn off spdif-in to spdif-out */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x80, 0); - } - if (use_line_as_rear) { - s->capability |= CAN_LINE_AS_REAR; - s->status |= DO_LINE_AS_REAR; - maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x20); - } else - maskb(s->iobase + CODEC_CMI_MIXER1, ~0x20, 0); - if (s->chip_version >= 39) { - if (use_line_as_bass) { - s->capability |= CAN_LINE_AS_BASS; - s->status |= DO_LINE_AS_BASS; - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0, 0x60); - } else - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x60, 0); - } - } else { - /* 8338 will fall here */ - s->max_channels = 2; - } - /* queue it for later freeing */ - s->next = devs; - devs = s; - return; - -#ifdef CONFIG_SOUND_CMPCI_FM - unregister_sound_special(s->dev_dmfm); - err_dev4: -#endif -#ifdef CONFIG_SOUND_CMPCI_MIDI - unregister_sound_midi(s->dev_midi); - err_dev3: -#endif - unregister_sound_mixer(s->dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR "cmpci: cannot register misc device\n"); - free_irq(s->irq, s); - err_irq: -#ifdef CONFIG_SOUND_CMPCI_FM - if (s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH); -#endif -#ifdef CONFIG_SOUND_CMPCI_MIDI - if (s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI); -#endif - release_region(s->iobase, CM_EXTENT_CODEC); - err_region5: - kfree(s); - } - if (!devs) { - if (wavetable_mem) - free_pages(wavetable_mem, 20-PAGE_SHIFT); - return; - } - return; -} - -static int __init init_cmpci(void) -{ - struct pci_dev *pcidev = NULL; - int index = 0; - -#ifdef CONFIG_PCI - if (!pci_present()) /* No PCI bus in this machine! */ -#endif - return -ENODEV; - printk(KERN_INFO "cmpci: version $Revision: 5.64 $ time " __TIME__ " " __DATE__ "\n"); - - while (index < NR_DEVICE && ( - (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, pcidev)))) { - initialize_chip(pcidev); - index++; - } - while (index < NR_DEVICE && ( - (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)))) { - initialize_chip(pcidev); - index++; - } - while (index < NR_DEVICE && ( - (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, pcidev)))) { - initialize_chip(pcidev); - index++; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("ChenLi Tien, cltien@cmedia.com.tw"); -MODULE_DESCRIPTION("CM8x38 Audio Driver"); -MODULE_LICENSE("GPL"); - - -static void __exit cleanup_cmpci(void) -{ - struct cm_state *s; - - while ((s = devs)) { - devs = devs->next; - outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ - synchronize_irq(); - outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ - free_irq(s->irq, s); - - /* reset mixer */ - wrmixer(s, DSP_MIX_DATARESETIDX, 0); - - release_region(s->iobase, CM_EXTENT_CODEC); -#ifdef CONFIG_SOUND_CMPCI_MIDI - if (s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI); -#endif -#ifdef CONFIG_SOUND_CMPCI_FM - if (s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH); -#endif - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->dev_mixer); -#ifdef CONFIG_SOUND_CMPCI_MIDI - unregister_sound_midi(s->dev_midi); -#endif -#ifdef CONFIG_SOUND_CMPCI_FM - unregister_sound_special(s->dev_dmfm); -#endif - kfree(s); - } - if (wavetable_mem) - free_pages(wavetable_mem, 20-PAGE_SHIFT); - printk(KERN_INFO "cmpci: unloading\n"); -} - -module_init(init_cmpci); -module_exit(cleanup_cmpci); diff -Nru a/drivers/sound/coproc.h b/drivers/sound/coproc.h --- a/drivers/sound/coproc.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -/* - * Definitions for various on board processors on the sound cards. For - * example DSP processors. - */ - -/* - * Coprocessor access types - */ -#define COPR_CUSTOM 0x0001 /* Custom applications */ -#define COPR_MIDI 0x0002 /* MIDI (MPU-401) emulation */ -#define COPR_PCM 0x0004 /* Digitized voice applications */ -#define COPR_SYNTH 0x0008 /* Music synthesis */ diff -Nru a/drivers/sound/cs4232.c b/drivers/sound/cs4232.c --- a/drivers/sound/cs4232.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,505 +0,0 @@ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * cs4232.c - * - * The low level driver for Crystal CS4232 based cards. The CS4232 is - * a PnP compatible chip which contains a CS4231A codec, SB emulation, - * a MPU401 compatible MIDI port, joystick and synthesizer and IDE CD-ROM - * interfaces. This is just a temporary driver until full PnP support - * gets implemented. Just the WSS codec, FM synth and the MIDI ports are - * supported. Other interfaces are left uninitialized. - * - * ifdef ...WAVEFRONT... - * - * Support is provided for initializing the WaveFront synth - * interface as well, which is logical device #4. Note that if - * you have a Tropez+ card, you probably don't need to setup - * the CS4232-supported MIDI interface, since it corresponds to - * the internal 26-pin header that's hard to access. Using this - * requires an additional IRQ, a resource none too plentiful in - * this environment. Just don't set module parameters mpuio and - * mpuirq, and the MIDI port will be left uninitialized. You can - * still use the ICS2115 hosted MIDI interface which corresponds - * to the 9-pin D connector on the back of the card. - * - * endif ...WAVEFRONT... - * - * Supported chips are: - * CS4232 - * CS4236 - * CS4236B - * - * Note: You will need a PnP config setup to initialise some CS4232 boards - * anyway. - * - * Changes - * Alan Cox Modularisation, Basic cleanups. - * Paul Barton-Davis Separated MPU configuration, added - * Tropez+ (WaveFront) support - * Christoph Hellwig Adapted to module_init/module_exit, - * simple cleanups - * Arnaldo C. de Melo got rid of attach_uart401 - * Bartlomiej Zolnierkiewicz - * Added some __init/__initdata/__exit - * Marcus Meissner Added ISA PnP support. - */ - -#include -#include -#include -#include - -#include "sound_config.h" - -#include "cs4232.h" -#include "ad1848.h" -#include "mpu401.h" - -#define KEY_PORT 0x279 /* Same as LPT1 status port */ -#define CSN_NUM 0x99 /* Just a random number */ - -static void CS_OUT(unsigned char a) -{ - outb(a, KEY_PORT); -} - -#define CS_OUT2(a, b) {CS_OUT(a);CS_OUT(b);} -#define CS_OUT3(a, b, c) {CS_OUT(a);CS_OUT(b);CS_OUT(c);} - -static int mpu_base = 0, mpu_irq = 0; -static int synth_base = 0, synth_irq = 0; -static int mpu_detected = 0; - -int __init probe_cs4232_mpu(struct address_info *hw_config) -{ - /* - * Just write down the config values. - */ - - mpu_base = hw_config->io_base; - mpu_irq = hw_config->irq; - - return 1; -} - -static unsigned char crystal_key[] __initdata = /* A 32 byte magic key sequence */ -{ - 0x96, 0x35, 0x9a, 0xcd, 0xe6, 0xf3, 0x79, 0xbc, - 0x5e, 0xaf, 0x57, 0x2b, 0x15, 0x8a, 0xc5, 0xe2, - 0xf1, 0xf8, 0x7c, 0x3e, 0x9f, 0x4f, 0x27, 0x13, - 0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a -}; - -static void sleep(unsigned howlong) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(howlong); -} - -int __init probe_cs4232(struct address_info *hw_config, int isapnp_configured) -{ - int i, n; - int base = hw_config->io_base, irq = hw_config->irq; - int dma1 = hw_config->dma, dma2 = hw_config->dma2; - - /* - * Verify that the I/O port range is free. - */ - - if (check_region(base, 4)) - { - printk(KERN_ERR "cs4232.c: I/O port 0x%03x not free\n", base); - return 0; - } - if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) { - return 1; /* The card is already active */ - } - if (isapnp_configured) { - printk(KERN_ERR "cs4232.c: ISA PnP configured, but not detected?\n"); - return 0; - } - - /* - * This version of the driver doesn't use the PnP method when configuring - * the card but a simplified method defined by Crystal. This means that - * just one CS4232 compatible device can exist on the system. Also this - * method conflicts with possible PnP support in the OS. For this reason - * driver is just a temporary kludge. - * - * Also the Cirrus/Crystal method doesnt always work. Try ISA PnP first ;) - */ - - /* - * Repeat initialization few times since it doesn't always succeed in - * first time. - */ - - for (n = 0; n < 4; n++) - { - /* - * Wake up the card by sending a 32 byte Crystal key to the key port. - */ - - for (i = 0; i < 32; i++) - CS_OUT(crystal_key[i]); - - sleep(HZ / 10); - - /* - * Now set the CSN (Card Select Number). - */ - - CS_OUT2(0x06, CSN_NUM); - - /* - * Then set some config bytes. First logical device 0 - */ - - CS_OUT2(0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */ - CS_OUT3(0x47, (base >> 8) & 0xff, base & 0xff); /* WSS base */ - - if (check_region(0x388, 4)) /* Not free */ - CS_OUT3(0x48, 0x00, 0x00) /* FM base off */ - else - CS_OUT3(0x48, 0x03, 0x88); /* FM base 0x388 */ - - CS_OUT3(0x42, 0x00, 0x00); /* SB base off */ - CS_OUT2(0x22, irq); /* SB+WSS IRQ */ - CS_OUT2(0x2a, dma1); /* SB+WSS DMA */ - - if (dma2 != -1) - CS_OUT2(0x25, dma2) /* WSS DMA2 */ - else - CS_OUT2(0x25, 4); /* No WSS DMA2 */ - - CS_OUT2(0x33, 0x01); /* Activate logical dev 0 */ - - sleep(HZ / 10); - - /* - * Initialize logical device 3 (MPU) - */ - - if (mpu_base != 0 && mpu_irq != 0) - { - CS_OUT2(0x15, 0x03); /* Select logical device 3 (MPU) */ - CS_OUT3(0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPU base */ - CS_OUT2(0x22, mpu_irq); /* MPU IRQ */ - CS_OUT2(0x33, 0x01); /* Activate logical dev 3 */ - } - - if(synth_base != 0) - { - CS_OUT2 (0x15, 0x04); /* logical device 4 (WaveFront) */ - CS_OUT3 (0x47, (synth_base >> 8) & 0xff, - synth_base & 0xff); /* base */ - CS_OUT2 (0x22, synth_irq); /* IRQ */ - CS_OUT2 (0x33, 0x01); /* Activate logical dev 4 */ - } - - /* - * Finally activate the chip - */ - - CS_OUT(0x79); - - sleep(HZ / 5); - - /* - * Then try to detect the codec part of the chip - */ - - if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) - return 1; - - sleep(HZ); - } - return 0; -} - -void __init attach_cs4232(struct address_info *hw_config) -{ - int base = hw_config->io_base, - irq = hw_config->irq, - dma1 = hw_config->dma, - dma2 = hw_config->dma2; - - if (base == -1 || irq == -1 || dma1 == -1) { - printk(KERN_ERR "cs4232: dma, irq and io must be set.\n"); - return; - } - - if (dma2 == -1) - dma2 = dma1; - - hw_config->slots[0] = ad1848_init("Crystal audio controller", base, - irq, - dma1, /* Playback DMA */ - dma2, /* Capture DMA */ - 0, - hw_config->osp, - THIS_MODULE); - - if (hw_config->slots[0] != -1 && - audio_devs[hw_config->slots[0]]->mixer_dev!=-1) - { - /* Assume the mixer map is as suggested in the CS4232 databook */ - AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); - AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); - AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM synth */ - } - if (mpu_base != 0 && mpu_irq != 0) - { - static struct address_info hw_config2 = { - 0 - }; /* Ensure it's initialized */ - - hw_config2.io_base = mpu_base; - hw_config2.irq = mpu_irq; - hw_config2.dma = -1; - hw_config2.dma2 = -1; - hw_config2.always_detect = 0; - hw_config2.name = NULL; - hw_config2.driver_use_1 = 0; - hw_config2.driver_use_2 = 0; - hw_config2.card_subtype = 0; - - if (probe_uart401(&hw_config2, THIS_MODULE)) - { - mpu_detected = 1; - } - else - { - mpu_base = mpu_irq = 0; - } - hw_config->slots[1] = hw_config2.slots[1]; - } -} - -void __exit unload_cs4232(struct address_info *hw_config) -{ - int base = hw_config->io_base, irq = hw_config->irq; - int dma1 = hw_config->dma, dma2 = hw_config->dma2; - - if (dma2 == -1) - dma2 = dma1; - - ad1848_unload(base, - irq, - dma1, /* Playback DMA */ - dma2, /* Capture DMA */ - 0); - - sound_unload_audiodev(hw_config->slots[0]); - if (mpu_base != 0 && mpu_irq != 0 && mpu_detected) - { - static struct address_info hw_config2 = - { - 0 - }; /* Ensure it's initialized */ - - hw_config2.io_base = mpu_base; - hw_config2.irq = mpu_irq; - hw_config2.dma = -1; - hw_config2.dma2 = -1; - hw_config2.always_detect = 0; - hw_config2.name = NULL; - hw_config2.driver_use_1 = 0; - hw_config2.driver_use_2 = 0; - hw_config2.card_subtype = 0; - hw_config2.slots[1] = hw_config->slots[1]; - - unload_uart401(&hw_config2); - } -} - -static struct address_info cfg; -static struct address_info cfg_mpu; - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; -static int __initdata mpuio = -1; -static int __initdata mpuirq = -1; -static int __initdata synthio = -1; -static int __initdata synthirq = -1; -static int __initdata isapnp = 1; - -MODULE_DESCRIPTION("CS4232 based soundcard driver"); -MODULE_AUTHOR("Hannu Savolainen, Paul Barton-Davis"); -MODULE_LICENSE("GPL"); - -MODULE_PARM(io,"i"); -MODULE_PARM_DESC(io,"base I/O port for AD1848"); -MODULE_PARM(irq,"i"); -MODULE_PARM_DESC(irq,"IRQ for AD1848 chip"); -MODULE_PARM(dma,"i"); -MODULE_PARM_DESC(dma,"8 bit DMA for AD1848 chip"); -MODULE_PARM(dma2,"i"); -MODULE_PARM_DESC(dma2,"16 bit DMA for AD1848 chip"); -MODULE_PARM(mpuio,"i"); -MODULE_PARM_DESC(mpuio,"MPU 401 base address"); -MODULE_PARM(mpuirq,"i"); -MODULE_PARM_DESC(mpuirq,"MPU 401 IRQ"); -MODULE_PARM(synthio,"i"); -MODULE_PARM_DESC(synthio,"Maui WaveTable base I/O port"); -MODULE_PARM(synthirq,"i"); -MODULE_PARM_DESC(synthirq,"Maui WaveTable IRQ"); -MODULE_PARM(isapnp,"i"); -MODULE_PARM_DESC(isapnp,"Enable ISAPnP probing (default 1)"); - -/* - * Install a CS4232 based card. Need to have ad1848 and mpu401 - * loaded ready. - */ - -/* All cs4232 based cards have the main ad1848 card either as CSC0000 or - * CSC0100. */ -struct isapnp_device_id isapnp_cs4232_list[] __initdata = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), - 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), - 0 }, - /* Guillemot Turtlebeach something appears to be cs4232 compatible - * (untested) */ - { ISAPNP_VENDOR('C','S','C'), ISAPNP_ANY_ID, - ISAPNP_VENDOR('G','I','M'), ISAPNP_FUNCTION(0x0100), - 0 }, - {0} -}; - -MODULE_DEVICE_TABLE(isapnp, isapnp_cs4232_list); - -int cs4232_isapnp_probe(struct pci_dev *dev, const struct isapnp_device_id *id) -{ - int ret; - struct address_info *isapnpcfg; - - isapnpcfg=(struct address_info*)kmalloc(sizeof(*isapnpcfg),GFP_KERNEL); - if (!isapnpcfg) - return -ENOMEM; - /* - * If device is active, assume configured with /proc/isapnp - * and use anyway. Any other way to check this? - */ - ret = dev->prepare(dev); - if(ret && ret != -EBUSY) { - printk(KERN_ERR "cs4232: ISA PnP found device that could not be autoconfigured.\n"); - kfree(isapnpcfg); - return -ENODEV; - } - if(ret != -EBUSY) { - if(dev->activate(dev) < 0) { - printk(KERN_WARNING "cs4232: ISA PnP activate failed\n"); - kfree(isapnpcfg); - return -ENODEV; - } - } /* else subfunction is already activated */ - - isapnpcfg->irq = dev->irq_resource[0].start; - isapnpcfg->dma = dev->dma_resource[0].start; - isapnpcfg->dma2 = dev->dma_resource[1].start; - isapnpcfg->io_base = dev->resource[0].start; - if (probe_cs4232(isapnpcfg,TRUE) == 0) { - printk(KERN_ERR "cs4232: ISA PnP card found, but not detected?\n"); - return -ENODEV; - } - attach_cs4232(isapnpcfg); - pci_set_drvdata(dev,isapnpcfg); - return 0; -} - -static int __init init_cs4232(void) -{ -#ifdef CONFIG_SOUND_WAVEFRONT_MODULE - if(synthio == -1) - printk(KERN_INFO "cs4232: set synthio and synthirq to use the wavefront facilities.\n"); - else { - synth_base = synthio; - synth_irq = synthirq; - } -#else - if(synthio != -1) - printk(KERN_WARNING "cs4232: wavefront support not enabled in this driver.\n"); -#endif - cfg.irq = -1; - - if (isapnp && - (isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_probe) > 0) - ) - return 0; - - if(io==-1||irq==-1||dma==-1) - { - printk(KERN_ERR "cs4232: Must set io, irq and dma.\n"); - return -ENODEV; - } - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - - cfg_mpu.io_base = -1; - cfg_mpu.irq = -1; - - if (mpuio != -1 && mpuirq != -1) { - cfg_mpu.io_base = mpuio; - cfg_mpu.irq = mpuirq; - probe_cs4232_mpu(&cfg_mpu); /* Bug always returns 0 not OK -- AC */ - } - - if (probe_cs4232(&cfg,FALSE) == 0) - return -ENODEV; - attach_cs4232(&cfg); - - return 0; -} - -int cs4232_isapnp_remove(struct pci_dev *dev, const struct isapnp_device_id *id) -{ - struct address_info *cfg = (struct address_info*)pci_get_drvdata(dev); - if (cfg) unload_cs4232(cfg); - pci_set_drvdata(dev,NULL); - dev->deactivate(dev); - return 0; -} - -static void __exit cleanup_cs4232(void) -{ - isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_remove); - if (cfg.irq != -1) - unload_cs4232(&cfg); /* Unloads global MPU as well, if needed */ -} - -module_init(init_cs4232); -module_exit(cleanup_cs4232); - -#ifndef MODULE -static int __init setup_cs4232(char *str) -{ - /* io, irq, dma, dma2 mpuio, mpuirq*/ - int ints[7]; - - /* If we have isapnp cards, no need for options */ - if (isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_probe) > 0) - return 1; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - mpuio = ints[5]; - mpuirq = ints[6]; - - return 1; -} - -__setup("cs4232=", setup_cs4232); -#endif diff -Nru a/drivers/sound/cs4232.h b/drivers/sound/cs4232.h --- a/drivers/sound/cs4232.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -/* - * cs4232.h - * - * Copyright: Christoph Hellwig - * - */ - -int probe_cs4232 (struct address_info *hw_config,int useisapnp); -void attach_cs4232 (struct address_info *hw_config); -int probe_cs4232_mpu (struct address_info *hw_config); -void attach_cs4232_mpu (struct address_info *hw_config); diff -Nru a/drivers/sound/cs4281/Makefile b/drivers/sound/cs4281/Makefile --- a/drivers/sound/cs4281/Makefile Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -# Makefile for Cirrus Logic-Crystal CS4281 -# - -O_TARGET := cs4281.o - -obj-y := cs4281m.o -obj-m := $(O_TARGET) - -include $(TOPDIR)/Rules.make - -clean: - rm -f core *.o *.a *.s diff -Nru a/drivers/sound/cs4281/cs4281_hwdefs.h b/drivers/sound/cs4281/cs4281_hwdefs.h --- a/drivers/sound/cs4281/cs4281_hwdefs.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1234 +0,0 @@ -//**************************************************************************** -// -// HWDEFS.H - Definitions of the registers and data structures used by the -// CS4281 -// -// Copyright (c) 1999,2000,2001 Crystal Semiconductor Corp. -// -//**************************************************************************** - -#ifndef _H_HWDEFS -#define _H_HWDEFS - -//**************************************************************************** -// -// The following define the offsets of the registers located in the PCI -// configuration space of the CS4281 part. -// -//**************************************************************************** -#define PCICONFIG_DEVID_VENID 0x00000000L -#define PCICONFIG_STATUS_COMMAND 0x00000004L -#define PCICONFIG_CLASS_REVISION 0x00000008L -#define PCICONFIG_LATENCY_TIMER 0x0000000CL -#define PCICONFIG_BA0 0x00000010L -#define PCICONFIG_BA1 0x00000014L -#define PCICONFIG_SUBSYSID_SUBSYSVENID 0x0000002CL -#define PCICONFIG_INTERRUPT 0x0000003CL - -//**************************************************************************** -// -// The following define the offsets of the registers accessed via base address -// register zero on the CS4281 part. -// -//**************************************************************************** -#define BA0_HISR 0x00000000L -#define BA0_HICR 0x00000008L -#define BA0_HIMR 0x0000000CL -#define BA0_IIER 0x00000010L -#define BA0_HDSR0 0x000000F0L -#define BA0_HDSR1 0x000000F4L -#define BA0_HDSR2 0x000000F8L -#define BA0_HDSR3 0x000000FCL -#define BA0_DCA0 0x00000110L -#define BA0_DCC0 0x00000114L -#define BA0_DBA0 0x00000118L -#define BA0_DBC0 0x0000011CL -#define BA0_DCA1 0x00000120L -#define BA0_DCC1 0x00000124L -#define BA0_DBA1 0x00000128L -#define BA0_DBC1 0x0000012CL -#define BA0_DCA2 0x00000130L -#define BA0_DCC2 0x00000134L -#define BA0_DBA2 0x00000138L -#define BA0_DBC2 0x0000013CL -#define BA0_DCA3 0x00000140L -#define BA0_DCC3 0x00000144L -#define BA0_DBA3 0x00000148L -#define BA0_DBC3 0x0000014CL -#define BA0_DMR0 0x00000150L -#define BA0_DCR0 0x00000154L -#define BA0_DMR1 0x00000158L -#define BA0_DCR1 0x0000015CL -#define BA0_DMR2 0x00000160L -#define BA0_DCR2 0x00000164L -#define BA0_DMR3 0x00000168L -#define BA0_DCR3 0x0000016CL -#define BA0_DLMR 0x00000170L -#define BA0_DLSR 0x00000174L -#define BA0_FCR0 0x00000180L -#define BA0_FCR1 0x00000184L -#define BA0_FCR2 0x00000188L -#define BA0_FCR3 0x0000018CL -#define BA0_FPDR0 0x00000190L -#define BA0_FPDR1 0x00000194L -#define BA0_FPDR2 0x00000198L -#define BA0_FPDR3 0x0000019CL -#define BA0_FCHS 0x0000020CL -#define BA0_FSIC0 0x00000210L -#define BA0_FSIC1 0x00000214L -#define BA0_FSIC2 0x00000218L -#define BA0_FSIC3 0x0000021CL -#define BA0_PCICFG00 0x00000300L -#define BA0_PCICFG04 0x00000304L -#define BA0_PCICFG08 0x00000308L -#define BA0_PCICFG0C 0x0000030CL -#define BA0_PCICFG10 0x00000310L -#define BA0_PCICFG14 0x00000314L -#define BA0_PCICFG18 0x00000318L -#define BA0_PCICFG1C 0x0000031CL -#define BA0_PCICFG20 0x00000320L -#define BA0_PCICFG24 0x00000324L -#define BA0_PCICFG28 0x00000328L -#define BA0_PCICFG2C 0x0000032CL -#define BA0_PCICFG30 0x00000330L -#define BA0_PCICFG34 0x00000334L -#define BA0_PCICFG38 0x00000338L -#define BA0_PCICFG3C 0x0000033CL -#define BA0_PCICFG40 0x00000340L -#define BA0_PMCS 0x00000344L -#define BA0_CWPR 0x000003E0L -#define BA0_EPPMC 0x000003E4L -#define BA0_GPIOR 0x000003E8L -#define BA0_SPMC 0x000003ECL -#define BA0_CFLR 0x000003F0L -#define BA0_IISR 0x000003F4L -#define BA0_TMS 0x000003F8L -#define BA0_SSVID 0x000003FCL -#define BA0_CLKCR1 0x00000400L -#define BA0_FRR 0x00000410L -#define BA0_SLT12O 0x0000041CL -#define BA0_SERMC 0x00000420L -#define BA0_SERC1 0x00000428L -#define BA0_SERC2 0x0000042CL -#define BA0_SLT12M 0x0000045CL -#define BA0_ACCTL 0x00000460L -#define BA0_ACSTS 0x00000464L -#define BA0_ACOSV 0x00000468L -#define BA0_ACCAD 0x0000046CL -#define BA0_ACCDA 0x00000470L -#define BA0_ACISV 0x00000474L -#define BA0_ACSAD 0x00000478L -#define BA0_ACSDA 0x0000047CL -#define BA0_JSPT 0x00000480L -#define BA0_JSCTL 0x00000484L -#define BA0_MIDCR 0x00000490L -#define BA0_MIDCMD 0x00000494L -#define BA0_MIDSR 0x00000494L -#define BA0_MIDWP 0x00000498L -#define BA0_MIDRP 0x0000049CL -#define BA0_AODSD1 0x000004A8L -#define BA0_AODSD2 0x000004ACL -#define BA0_CFGI 0x000004B0L -#define BA0_SLT12M2 0x000004DCL -#define BA0_ACSTS2 0x000004E4L -#define BA0_ACISV2 0x000004F4L -#define BA0_ACSAD2 0x000004F8L -#define BA0_ACSDA2 0x000004FCL -#define BA0_IOTGP 0x00000500L -#define BA0_IOTSB 0x00000504L -#define BA0_IOTFM 0x00000508L -#define BA0_IOTDMA 0x0000050CL -#define BA0_IOTAC0 0x00000500L -#define BA0_IOTAC1 0x00000504L -#define BA0_IOTAC2 0x00000508L -#define BA0_IOTAC3 0x0000050CL -#define BA0_IOTPCP 0x0000052CL -#define BA0_IOTCC 0x00000530L -#define BA0_IOTCR 0x0000058CL -#define BA0_PCPRR 0x00000600L -#define BA0_PCPGR 0x00000604L -#define BA0_PCPCR 0x00000608L -#define BA0_PCPCIEN 0x00000608L -#define BA0_SBMAR 0x00000700L -#define BA0_SBMDR 0x00000704L -#define BA0_SBRR 0x00000708L -#define BA0_SBRDP 0x0000070CL -#define BA0_SBWDP 0x00000710L -#define BA0_SBWBS 0x00000710L -#define BA0_SBRBS 0x00000714L -#define BA0_FMSR 0x00000730L -#define BA0_B0AP 0x00000730L -#define BA0_FMDP 0x00000734L -#define BA0_B1AP 0x00000738L -#define BA0_B1DP 0x0000073CL -#define BA0_SSPM 0x00000740L -#define BA0_DACSR 0x00000744L -#define BA0_ADCSR 0x00000748L -#define BA0_SSCR 0x0000074CL -#define BA0_FMLVC 0x00000754L -#define BA0_FMRVC 0x00000758L -#define BA0_SRCSA 0x0000075CL -#define BA0_PPLVC 0x00000760L -#define BA0_PPRVC 0x00000764L -#define BA0_PASR 0x00000768L -#define BA0_CASR 0x0000076CL - -//**************************************************************************** -// -// The following define the offsets of the AC97 shadow registers, which appear -// as a virtual extension to the base address register zero memory range. -// -//**************************************************************************** -#define AC97_REG_OFFSET_MASK 0x0000007EL -#define AC97_CODEC_NUMBER_MASK 0x00003000L - -#define BA0_AC97_RESET 0x00001000L -#define BA0_AC97_MASTER_VOLUME 0x00001002L -#define BA0_AC97_HEADPHONE_VOLUME 0x00001004L -#define BA0_AC97_MASTER_VOLUME_MONO 0x00001006L -#define BA0_AC97_MASTER_TONE 0x00001008L -#define BA0_AC97_PC_BEEP_VOLUME 0x0000100AL -#define BA0_AC97_PHONE_VOLUME 0x0000100CL -#define BA0_AC97_MIC_VOLUME 0x0000100EL -#define BA0_AC97_LINE_IN_VOLUME 0x00001010L -#define BA0_AC97_CD_VOLUME 0x00001012L -#define BA0_AC97_VIDEO_VOLUME 0x00001014L -#define BA0_AC97_AUX_VOLUME 0x00001016L -#define BA0_AC97_PCM_OUT_VOLUME 0x00001018L -#define BA0_AC97_RECORD_SELECT 0x0000101AL -#define BA0_AC97_RECORD_GAIN 0x0000101CL -#define BA0_AC97_RECORD_GAIN_MIC 0x0000101EL -#define BA0_AC97_GENERAL_PURPOSE 0x00001020L -#define BA0_AC97_3D_CONTROL 0x00001022L -#define BA0_AC97_MODEM_RATE 0x00001024L -#define BA0_AC97_POWERDOWN 0x00001026L -#define BA0_AC97_EXT_AUDIO_ID 0x00001028L -#define BA0_AC97_EXT_AUDIO_POWER 0x0000102AL -#define BA0_AC97_PCM_FRONT_DAC_RATE 0x0000102CL -#define BA0_AC97_PCM_SURR_DAC_RATE 0x0000102EL -#define BA0_AC97_PCM_LFE_DAC_RATE 0x00001030L -#define BA0_AC97_PCM_LR_ADC_RATE 0x00001032L -#define BA0_AC97_MIC_ADC_RATE 0x00001034L -#define BA0_AC97_6CH_VOL_C_LFE 0x00001036L -#define BA0_AC97_6CH_VOL_SURROUND 0x00001038L -#define BA0_AC97_RESERVED_3A 0x0000103AL -#define BA0_AC97_EXT_MODEM_ID 0x0000103CL -#define BA0_AC97_EXT_MODEM_POWER 0x0000103EL -#define BA0_AC97_LINE1_CODEC_RATE 0x00001040L -#define BA0_AC97_LINE2_CODEC_RATE 0x00001042L -#define BA0_AC97_HANDSET_CODEC_RATE 0x00001044L -#define BA0_AC97_LINE1_CODEC_LEVEL 0x00001046L -#define BA0_AC97_LINE2_CODEC_LEVEL 0x00001048L -#define BA0_AC97_HANDSET_CODEC_LEVEL 0x0000104AL -#define BA0_AC97_GPIO_PIN_CONFIG 0x0000104CL -#define BA0_AC97_GPIO_PIN_TYPE 0x0000104EL -#define BA0_AC97_GPIO_PIN_STICKY 0x00001050L -#define BA0_AC97_GPIO_PIN_WAKEUP 0x00001052L -#define BA0_AC97_GPIO_PIN_STATUS 0x00001054L -#define BA0_AC97_MISC_MODEM_AFE_STAT 0x00001056L -#define BA0_AC97_RESERVED_58 0x00001058L -#define BA0_AC97_CRYSTAL_REV_N_FAB_ID 0x0000105AL -#define BA0_AC97_TEST_AND_MISC_CTRL 0x0000105CL -#define BA0_AC97_AC_MODE 0x0000105EL -#define BA0_AC97_MISC_CRYSTAL_CONTROL 0x00001060L -#define BA0_AC97_LINE1_HYPRID_CTRL 0x00001062L -#define BA0_AC97_VENDOR_RESERVED_64 0x00001064L -#define BA0_AC97_VENDOR_RESERVED_66 0x00001066L -#define BA0_AC97_SPDIF_CONTROL 0x00001068L -#define BA0_AC97_VENDOR_RESERVED_6A 0x0000106AL -#define BA0_AC97_VENDOR_RESERVED_6C 0x0000106CL -#define BA0_AC97_VENDOR_RESERVED_6E 0x0000106EL -#define BA0_AC97_VENDOR_RESERVED_70 0x00001070L -#define BA0_AC97_VENDOR_RESERVED_72 0x00001072L -#define BA0_AC97_VENDOR_RESERVED_74 0x00001074L -#define BA0_AC97_CAL_ADDRESS 0x00001076L -#define BA0_AC97_CAL_DATA 0x00001078L -#define BA0_AC97_VENDOR_RESERVED_7A 0x0000107AL -#define BA0_AC97_VENDOR_ID1 0x0000107CL -#define BA0_AC97_VENDOR_ID2 0x0000107EL - -//**************************************************************************** -// -// The following define the offsets of the registers and memories accessed via -// base address register one on the CS4281 part. -// -//**************************************************************************** - -//**************************************************************************** -// -// The following defines are for the flags in the PCI device ID/vendor ID -// register. -// -//**************************************************************************** -#define PDV_VENID_MASK 0x0000FFFFL -#define PDV_DEVID_MASK 0xFFFF0000L -#define PDV_VENID_SHIFT 0L -#define PDV_DEVID_SHIFT 16L -#define VENID_CIRRUS_LOGIC 0x1013L -#define DEVID_CS4281 0x6005L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI status and command -// register. -// -//**************************************************************************** -#define PSC_IO_SPACE_ENABLE 0x00000001L -#define PSC_MEMORY_SPACE_ENABLE 0x00000002L -#define PSC_BUS_MASTER_ENABLE 0x00000004L -#define PSC_SPECIAL_CYCLES 0x00000008L -#define PSC_MWI_ENABLE 0x00000010L -#define PSC_VGA_PALETTE_SNOOP 0x00000020L -#define PSC_PARITY_RESPONSE 0x00000040L -#define PSC_WAIT_CONTROL 0x00000080L -#define PSC_SERR_ENABLE 0x00000100L -#define PSC_FAST_B2B_ENABLE 0x00000200L -#define PSC_UDF_MASK 0x007F0000L -#define PSC_FAST_B2B_CAPABLE 0x00800000L -#define PSC_PARITY_ERROR_DETECTED 0x01000000L -#define PSC_DEVSEL_TIMING_MASK 0x06000000L -#define PSC_TARGET_ABORT_SIGNALLED 0x08000000L -#define PSC_RECEIVED_TARGET_ABORT 0x10000000L -#define PSC_RECEIVED_MASTER_ABORT 0x20000000L -#define PSC_SIGNALLED_SERR 0x40000000L -#define PSC_DETECTED_PARITY_ERROR 0x80000000L -#define PSC_UDF_SHIFT 16L -#define PSC_DEVSEL_TIMING_SHIFT 25L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI class/revision ID -// register. -// -//**************************************************************************** -#define PCR_REVID_MASK 0x000000FFL -#define PCR_INTERFACE_MASK 0x0000FF00L -#define PCR_SUBCLASS_MASK 0x00FF0000L -#define PCR_CLASS_MASK 0xFF000000L -#define PCR_REVID_SHIFT 0L -#define PCR_INTERFACE_SHIFT 8L -#define PCR_SUBCLASS_SHIFT 16L -#define PCR_CLASS_SHIFT 24L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI latency timer register. -// -//**************************************************************************** -#define PLT_CACHE_LINE_SIZE_MASK 0x000000FFL -#define PLT_LATENCY_TIMER_MASK 0x0000FF00L -#define PLT_HEADER_TYPE_MASK 0x00FF0000L -#define PLT_BIST_MASK 0xFF000000L -#define PLT_CACHE_LINE_SIZE_SHIFT 0L -#define PLT_LATENCY_TIMER_SHIFT 8L -#define PLT_HEADER_TYPE_SHIFT 16L -#define PLT_BIST_SHIFT 24L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI base address registers. -// -//**************************************************************************** -#define PBAR_MEMORY_SPACE_INDICATOR 0x00000001L -#define PBAR_LOCATION_TYPE_MASK 0x00000006L -#define PBAR_NOT_PREFETCHABLE 0x00000008L -#define PBAR_ADDRESS_MASK 0xFFFFFFF0L -#define PBAR_LOCATION_TYPE_SHIFT 1L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI subsystem ID/subsystem -// vendor ID register. -// -//**************************************************************************** -#define PSS_SUBSYSTEM_VENDOR_ID_MASK 0x0000FFFFL -#define PSS_SUBSYSTEM_ID_MASK 0xFFFF0000L -#define PSS_SUBSYSTEM_VENDOR_ID_SHIFT 0L -#define PSS_SUBSYSTEM_ID_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI interrupt register. -// -//**************************************************************************** -#define PI_LINE_MASK 0x000000FFL -#define PI_PIN_MASK 0x0000FF00L -#define PI_MIN_GRANT_MASK 0x00FF0000L -#define PI_MAX_LATENCY_MASK 0xFF000000L -#define PI_LINE_SHIFT 0L -#define PI_PIN_SHIFT 8L -#define PI_MIN_GRANT_SHIFT 16L -#define PI_MAX_LATENCY_SHIFT 24L - -//**************************************************************************** -// -// The following defines are for the flags in the host interrupt status -// register. -// -//**************************************************************************** -#define HISR_HVOLMASK 0x00000003L -#define HISR_VDNI 0x00000001L -#define HISR_VUPI 0x00000002L -#define HISR_GP1I 0x00000004L -#define HISR_GP3I 0x00000008L -#define HISR_GPSI 0x00000010L -#define HISR_GPPI 0x00000020L -#define HISR_DMAI 0x00040000L -#define HISR_FIFOI 0x00100000L -#define HISR_HVOL 0x00200000L -#define HISR_MIDI 0x00400000L -#define HISR_SBINT 0x00800000L -#define HISR_INTENA 0x80000000L -#define HISR_DMA_MASK 0x00000F00L -#define HISR_FIFO_MASK 0x0000F000L -#define HISR_DMA_SHIFT 8L -#define HISR_FIFO_SHIFT 12L -#define HISR_FIFO0 0x00001000L -#define HISR_FIFO1 0x00002000L -#define HISR_FIFO2 0x00004000L -#define HISR_FIFO3 0x00008000L -#define HISR_DMA0 0x00000100L -#define HISR_DMA1 0x00000200L -#define HISR_DMA2 0x00000400L -#define HISR_DMA3 0x00000800L -#define HISR_RESERVED 0x40000000L - -//**************************************************************************** -// -// The following defines are for the flags in the host interrupt control -// register. -// -//**************************************************************************** -#define HICR_IEV 0x00000001L -#define HICR_CHGM 0x00000002L - -//**************************************************************************** -// -// The following defines are for the flags in the DMA Mode Register n -// (DMRn) -// -//**************************************************************************** -#define DMRn_TR_MASK 0x0000000CL -#define DMRn_TR_SHIFT 2L -#define DMRn_AUTO 0x00000010L -#define DMRn_TR_READ 0x00000008L -#define DMRn_TR_WRITE 0x00000004L -#define DMRn_TYPE_MASK 0x000000C0L -#define DMRn_TYPE_SHIFT 6L -#define DMRn_SIZE8 0x00010000L -#define DMRn_MONO 0x00020000L -#define DMRn_BEND 0x00040000L -#define DMRn_USIGN 0x00080000L -#define DMRn_SIZE20 0x00100000L -#define DMRn_SWAPC 0x00400000L -#define DMRn_CBC 0x01000000L -#define DMRn_TBC 0x02000000L -#define DMRn_POLL 0x10000000L -#define DMRn_DMA 0x20000000L -#define DMRn_FSEL_MASK 0xC0000000L -#define DMRn_FSEL_SHIFT 30L -#define DMRn_FSEL0 0x00000000L -#define DMRn_FSEL1 0x40000000L -#define DMRn_FSEL2 0x80000000L -#define DMRn_FSEL3 0xC0000000L - -//**************************************************************************** -// -// The following defines are for the flags in the DMA Command Register n -// (DCRn) -// -//**************************************************************************** -#define DCRn_HTCIE 0x00020000L -#define DCRn_TCIE 0x00010000L -#define DCRn_MSK 0x00000001L - -//**************************************************************************** -// -// The following defines are for the flags in the FIFO Control -// register n.(FCRn) -// -//**************************************************************************** -#define FCRn_OF_MASK 0x0000007FL -#define FCRn_OF_SHIFT 0L -#define FCRn_SZ_MASK 0x00007F00L -#define FCRn_SZ_SHIFT 8L -#define FCRn_LS_MASK 0x001F0000L -#define FCRn_LS_SHIFT 16L -#define FCRn_RS_MASK 0x1F000000L -#define FCRn_RS_SHIFT 24L -#define FCRn_FEN 0x80000000L -#define FCRn_PSH 0x20000000L -#define FCRn_DACZ 0x40000000L - -//**************************************************************************** -// -// The following defines are for the flags in the serial port Power Management -// control register.(SPMC) -// -//**************************************************************************** -#define SPMC_RSTN 0x00000001L -#define SPMC_ASYN 0x00000002L -#define SPMC_WUP1 0x00000004L -#define SPMC_WUP2 0x00000008L -#define SPMC_ASDI2E 0x00000100L -#define SPMC_ESSPD 0x00000200L -#define SPMC_GISPEN 0x00004000L -#define SPMC_GIPPEN 0x00008000L - -//**************************************************************************** -// -// The following defines are for the flags in the Configuration Load register. -// (CFLR) -// -//**************************************************************************** -#define CFLR_CLOCK_SOURCE_MASK 0x00000003L -#define CFLR_CLOCK_SOURCE_AC97 0x00000001L - -#define CFLR_CB0_MASK 0x000000FFL -#define CFLR_CB1_MASK 0x0000FF00L -#define CFLR_CB2_MASK 0x00FF0000L -#define CFLR_CB3_MASK 0xFF000000L -#define CFLR_CB0_SHIFT 0L -#define CFLR_CB1_SHIFT 8L -#define CFLR_CB2_SHIFT 16L -#define CFLR_CB3_SHIFT 24L - -#define IOTCR_DMA0 0x00000000L -#define IOTCR_DMA1 0x00000400L -#define IOTCR_DMA2 0x00000800L -#define IOTCR_DMA3 0x00000C00L -#define IOTCR_CCLS 0x00000100L -#define IOTCR_PCPCI 0x00000200L -#define IOTCR_DDMA 0x00000300L - -#define SBWBS_WBB 0x00000080L - -//**************************************************************************** -// -// The following defines are for the flags in the SRC Slot Assignment Register -// (SRCSA) -// -//**************************************************************************** -#define SRCSA_PLSS_MASK 0x0000001FL -#define SRCSA_PLSS_SHIFT 0L -#define SRCSA_PRSS_MASK 0x00001F00L -#define SRCSA_PRSS_SHIFT 8L -#define SRCSA_CLSS_MASK 0x001F0000L -#define SRCSA_CLSS_SHIFT 16L -#define SRCSA_CRSS_MASK 0x1F000000L -#define SRCSA_CRSS_SHIFT 24L - -//**************************************************************************** -// -// The following defines are for the flags in the Sound System Power Management -// register.(SSPM) -// -//**************************************************************************** -#define SSPM_FPDN 0x00000080L -#define SSPM_MIXEN 0x00000040L -#define SSPM_CSRCEN 0x00000020L -#define SSPM_PSRCEN 0x00000010L -#define SSPM_JSEN 0x00000008L -#define SSPM_ACLEN 0x00000004L -#define SSPM_FMEN 0x00000002L - -//**************************************************************************** -// -// The following defines are for the flags in the Sound System Control -// Register. (SSCR) -// -//**************************************************************************** -#define SSCR_SB 0x00000004L -#define SSCR_HVC 0x00000008L -#define SSCR_LPFIFO 0x00000040L -#define SSCR_LPSRC 0x00000080L -#define SSCR_XLPSRC 0x00000100L -#define SSCR_MVMD 0x00010000L -#define SSCR_MVAD 0x00020000L -#define SSCR_MVLD 0x00040000L -#define SSCR_MVCS 0x00080000L - -//**************************************************************************** -// -// The following defines are for the flags in the Clock Control Register 1. -// (CLKCR1) -// -//**************************************************************************** -#define CLKCR1_DLLSS_MASK 0x0000000CL -#define CLKCR1_DLLSS_SHIFT 2L -#define CLKCR1_DLLP 0x00000010L -#define CLKCR1_SWCE 0x00000020L -#define CLKCR1_DLLOS 0x00000040L -#define CLKCR1_CKRA 0x00010000L -#define CLKCR1_CKRN 0x00020000L -#define CLKCR1_DLLRDY 0x01000000L -#define CLKCR1_CLKON 0x02000000L - -//**************************************************************************** -// -// The following defines are for the flags in the Sound Blaster Read Buffer -// Status.(SBRBS) -// -//**************************************************************************** -#define SBRBS_RD_MASK 0x0000007FL -#define SBRBS_RD_SHIFT 0L -#define SBRBS_RBF 0x00000080L - -//**************************************************************************** -// -// The following defines are for the flags in the serial port master control -// register.(SERMC) -// -//**************************************************************************** -#define SERMC_MSPE 0x00000001L -#define SERMC_PTC_MASK 0x0000000EL -#define SERMC_PTC_SHIFT 1L -#define SERMC_PTC_AC97 0x00000002L -#define SERMC_PLB 0x00000010L -#define SERMC_PXLB 0x00000020L -#define SERMC_LOFV 0x00080000L -#define SERMC_SLB 0x00100000L -#define SERMC_SXLB 0x00200000L -#define SERMC_ODSEN1 0x01000000L -#define SERMC_ODSEN2 0x02000000L - -//**************************************************************************** -// -// The following defines are for the flags in the General Purpose I/O Register. -// (GPIOR) -// -//**************************************************************************** -#define GPIOR_VDNS 0x00000001L -#define GPIOR_VUPS 0x00000002L -#define GPIOR_GP1S 0x00000004L -#define GPIOR_GP3S 0x00000008L -#define GPIOR_GPSS 0x00000010L -#define GPIOR_GPPS 0x00000020L -#define GPIOR_GP1D 0x00000400L -#define GPIOR_GP3D 0x00000800L -#define GPIOR_VDNLT 0x00010000L -#define GPIOR_VDNPO 0x00020000L -#define GPIOR_VDNST 0x00040000L -#define GPIOR_VDNW 0x00080000L -#define GPIOR_VUPLT 0x00100000L -#define GPIOR_VUPPO 0x00200000L -#define GPIOR_VUPST 0x00400000L -#define GPIOR_VUPW 0x00800000L -#define GPIOR_GP1OE 0x01000000L -#define GPIOR_GP1PT 0x02000000L -#define GPIOR_GP1ST 0x04000000L -#define GPIOR_GP1W 0x08000000L -#define GPIOR_GP3OE 0x10000000L -#define GPIOR_GP3PT 0x20000000L -#define GPIOR_GP3ST 0x40000000L -#define GPIOR_GP3W 0x80000000L - -//**************************************************************************** -// -// The following defines are for the flags in the clock control register 1. -// -//**************************************************************************** -#define CLKCR1_PLLSS_MASK 0x0000000CL -#define CLKCR1_PLLSS_SERIAL 0x00000000L -#define CLKCR1_PLLSS_CRYSTAL 0x00000004L -#define CLKCR1_PLLSS_PCI 0x00000008L -#define CLKCR1_PLLSS_RESERVED 0x0000000CL -#define CLKCR1_PLLP 0x00000010L -#define CLKCR1_SWCE 0x00000020L -#define CLKCR1_PLLOS 0x00000040L - -//**************************************************************************** -// -// The following defines are for the flags in the feature reporting register. -// -//**************************************************************************** -#define FRR_FAB_MASK 0x00000003L -#define FRR_MASK_MASK 0x0000001CL -#define FRR_ID_MASK 0x00003000L -#define FRR_FAB_SHIFT 0L -#define FRR_MASK_SHIFT 2L -#define FRR_ID_SHIFT 12L - -//**************************************************************************** -// -// The following defines are for the flags in the serial port 1 configuration -// register. -// -//**************************************************************************** -#define SERC1_VALUE 0x00000003L -#define SERC1_SO1EN 0x00000001L -#define SERC1_SO1F_MASK 0x0000000EL -#define SERC1_SO1F_CS423X 0x00000000L -#define SERC1_SO1F_AC97 0x00000002L -#define SERC1_SO1F_DAC 0x00000004L -#define SERC1_SO1F_SPDIF 0x00000006L - -//**************************************************************************** -// -// The following defines are for the flags in the serial port 2 configuration -// register. -// -//**************************************************************************** -#define SERC2_VALUE 0x00000003L -#define SERC2_SI1EN 0x00000001L -#define SERC2_SI1F_MASK 0x0000000EL -#define SERC2_SI1F_CS423X 0x00000000L -#define SERC2_SI1F_AC97 0x00000002L -#define SERC2_SI1F_ADC 0x00000004L -#define SERC2_SI1F_SPDIF 0x00000006L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 control register. -// -//**************************************************************************** -#define ACCTL_ESYN 0x00000002L -#define ACCTL_VFRM 0x00000004L -#define ACCTL_DCV 0x00000008L -#define ACCTL_CRW 0x00000010L -#define ACCTL_TC 0x00000040L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status register. -// -//**************************************************************************** -#define ACSTS_CRDY 0x00000001L -#define ACSTS_VSTS 0x00000002L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 output slot valid -// register. -// -//**************************************************************************** -#define ACOSV_SLV3 0x00000001L -#define ACOSV_SLV4 0x00000002L -#define ACOSV_SLV5 0x00000004L -#define ACOSV_SLV6 0x00000008L -#define ACOSV_SLV7 0x00000010L -#define ACOSV_SLV8 0x00000020L -#define ACOSV_SLV9 0x00000040L -#define ACOSV_SLV10 0x00000080L -#define ACOSV_SLV11 0x00000100L -#define ACOSV_SLV12 0x00000200L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 command address -// register. -// -//**************************************************************************** -#define ACCAD_CI_MASK 0x0000007FL -#define ACCAD_CI_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 command data register. -// -//**************************************************************************** -#define ACCDA_CD_MASK 0x0000FFFFL -#define ACCDA_CD_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 input slot valid -// register. -// -//**************************************************************************** -#define ACISV_ISV3 0x00000001L -#define ACISV_ISV4 0x00000002L -#define ACISV_ISV5 0x00000004L -#define ACISV_ISV6 0x00000008L -#define ACISV_ISV7 0x00000010L -#define ACISV_ISV8 0x00000020L -#define ACISV_ISV9 0x00000040L -#define ACISV_ISV10 0x00000080L -#define ACISV_ISV11 0x00000100L -#define ACISV_ISV12 0x00000200L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status address -// register. -// -//**************************************************************************** -#define ACSAD_SI_MASK 0x0000007FL -#define ACSAD_SI_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status data register. -// -//**************************************************************************** -#define ACSDA_SD_MASK 0x0000FFFFL -#define ACSDA_SD_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the I/O trap address and control -// registers (all 12). -// -//**************************************************************************** -#define IOTAC_SA_MASK 0x0000FFFFL -#define IOTAC_MSK_MASK 0x000F0000L -#define IOTAC_IODC_MASK 0x06000000L -#define IOTAC_IODC_16_BIT 0x00000000L -#define IOTAC_IODC_10_BIT 0x02000000L -#define IOTAC_IODC_12_BIT 0x04000000L -#define IOTAC_WSPI 0x08000000L -#define IOTAC_RSPI 0x10000000L -#define IOTAC_WSE 0x20000000L -#define IOTAC_WE 0x40000000L -#define IOTAC_RE 0x80000000L -#define IOTAC_SA_SHIFT 0L -#define IOTAC_MSK_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the PC/PCI master enable -// register. -// -//**************************************************************************** -#define PCPCIEN_EN 0x00000001L - -//**************************************************************************** -// -// The following defines are for the flags in the joystick poll/trigger -// register. -// -//**************************************************************************** -#define JSPT_CAX 0x00000001L -#define JSPT_CAY 0x00000002L -#define JSPT_CBX 0x00000004L -#define JSPT_CBY 0x00000008L -#define JSPT_BA1 0x00000010L -#define JSPT_BA2 0x00000020L -#define JSPT_BB1 0x00000040L -#define JSPT_BB2 0x00000080L - -//**************************************************************************** -// -// The following defines are for the flags in the joystick control register. -// The TBF bit has been moved from MIDSR register to JSCTL register bit 8. -// -//**************************************************************************** -#define JSCTL_SP_MASK 0x00000003L -#define JSCTL_SP_SLOW 0x00000000L -#define JSCTL_SP_MEDIUM_SLOW 0x00000001L -#define JSCTL_SP_MEDIUM_FAST 0x00000002L -#define JSCTL_SP_FAST 0x00000003L -#define JSCTL_ARE 0x00000004L -#define JSCTL_TBF 0x00000100L - - -//**************************************************************************** -// -// The following defines are for the flags in the MIDI control register. -// -//**************************************************************************** -#define MIDCR_TXE 0x00000001L -#define MIDCR_RXE 0x00000002L -#define MIDCR_RIE 0x00000004L -#define MIDCR_TIE 0x00000008L -#define MIDCR_MLB 0x00000010L -#define MIDCR_MRST 0x00000020L - -//**************************************************************************** -// -// The following defines are for the flags in the MIDI status register. -// -//**************************************************************************** -#define MIDSR_RBE 0x00000080L -#define MIDSR_RDA 0x00008000L - -//**************************************************************************** -// -// The following defines are for the flags in the MIDI write port register. -// -//**************************************************************************** -#define MIDWP_MWD_MASK 0x000000FFL -#define MIDWP_MWD_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the MIDI read port register. -// -//**************************************************************************** -#define MIDRP_MRD_MASK 0x000000FFL -#define MIDRP_MRD_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the configuration interface -// register. -// -//**************************************************************************** -#define CFGI_CLK 0x00000001L -#define CFGI_DOUT 0x00000002L -#define CFGI_DIN_EEN 0x00000004L -#define CFGI_EELD 0x00000008L - -//**************************************************************************** -// -// The following defines are for the flags in the subsystem ID and vendor ID -// register. -// -//**************************************************************************** -#define SSVID_VID_MASK 0x0000FFFFL -#define SSVID_SID_MASK 0xFFFF0000L -#define SSVID_VID_SHIFT 0L -#define SSVID_SID_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the GPIO pin interface register. -// -//**************************************************************************** -#define GPIOR_VOLDN 0x00000001L -#define GPIOR_VOLUP 0x00000002L -#define GPIOR_SI2D 0x00000004L -#define GPIOR_SI2OE 0x00000008L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status register 2. -// -//**************************************************************************** -#define ACSTS2_CRDY 0x00000001L -#define ACSTS2_VSTS 0x00000002L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 input slot valid -// register 2. -// -//**************************************************************************** -#define ACISV2_ISV3 0x00000001L -#define ACISV2_ISV4 0x00000002L -#define ACISV2_ISV5 0x00000004L -#define ACISV2_ISV6 0x00000008L -#define ACISV2_ISV7 0x00000010L -#define ACISV2_ISV8 0x00000020L -#define ACISV2_ISV9 0x00000040L -#define ACISV2_ISV10 0x00000080L -#define ACISV2_ISV11 0x00000100L -#define ACISV2_ISV12 0x00000200L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status address -// register 2. -// -//**************************************************************************** -#define ACSAD2_SI_MASK 0x0000007FL -#define ACSAD2_SI_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status data register 2. -// -//**************************************************************************** -#define ACSDA2_SD_MASK 0x0000FFFFL -#define ACSDA2_SD_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the I/O trap control register. -// -//**************************************************************************** -#define IOTCR_ITD 0x00000001L -#define IOTCR_HRV 0x00000002L -#define IOTCR_SRV 0x00000004L -#define IOTCR_DTI 0x00000008L -#define IOTCR_DFI 0x00000010L -#define IOTCR_DDP 0x00000020L -#define IOTCR_JTE 0x00000040L -#define IOTCR_PPE 0x00000080L - -//**************************************************************************** -// -// The following defines are for the flags in the I/O trap address and control -// registers for Hardware Master Volume. -// -//**************************************************************************** -#define IOTGP_SA_MASK 0x0000FFFFL -#define IOTGP_MSK_MASK 0x000F0000L -#define IOTGP_IODC_MASK 0x06000000L -#define IOTGP_IODC_16_BIT 0x00000000L -#define IOTGP_IODC_10_BIT 0x02000000L -#define IOTGP_IODC_12_BIT 0x04000000L -#define IOTGP_WSPI 0x08000000L -#define IOTGP_RSPI 0x10000000L -#define IOTGP_WSE 0x20000000L -#define IOTGP_WE 0x40000000L -#define IOTGP_RE 0x80000000L -#define IOTGP_SA_SHIFT 0L -#define IOTGP_MSK_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the I/O trap address and control -// registers for Sound Blaster -// -//**************************************************************************** -#define IOTSB_SA_MASK 0x0000FFFFL -#define IOTSB_MSK_MASK 0x000F0000L -#define IOTSB_IODC_MASK 0x06000000L -#define IOTSB_IODC_16_BIT 0x00000000L -#define IOTSB_IODC_10_BIT 0x02000000L -#define IOTSB_IODC_12_BIT 0x04000000L -#define IOTSB_WSPI 0x08000000L -#define IOTSB_RSPI 0x10000000L -#define IOTSB_WSE 0x20000000L -#define IOTSB_WE 0x40000000L -#define IOTSB_RE 0x80000000L -#define IOTSB_SA_SHIFT 0L -#define IOTSB_MSK_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the I/O trap address and control -// registers for FM. -// -//**************************************************************************** -#define IOTFM_SA_MASK 0x0000FFFFL -#define IOTFM_MSK_MASK 0x000F0000L -#define IOTFM_IODC_MASK 0x06000000L -#define IOTFM_IODC_16_BIT 0x00000000L -#define IOTFM_IODC_10_BIT 0x02000000L -#define IOTFM_IODC_12_BIT 0x04000000L -#define IOTFM_WSPI 0x08000000L -#define IOTFM_RSPI 0x10000000L -#define IOTFM_WSE 0x20000000L -#define IOTFM_WE 0x40000000L -#define IOTFM_RE 0x80000000L -#define IOTFM_SA_SHIFT 0L -#define IOTFM_MSK_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the PC/PCI request register. -// -//**************************************************************************** -#define PCPRR_RDC_MASK 0x00000007L -#define PCPRR_REQ 0x00008000L -#define PCPRR_RDC_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the PC/PCI grant register. -// -//**************************************************************************** -#define PCPGR_GDC_MASK 0x00000007L -#define PCPGR_VL 0x00008000L -#define PCPGR_GDC_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the PC/PCI Control Register. -// -//**************************************************************************** -#define PCPCR_EN 0x00000001L - -//**************************************************************************** -// -// The following defines are for the flags in the debug index register. -// -//**************************************************************************** -#define DREG_REGID_MASK 0x0000007FL -#define DREG_DEBUG 0x00000080L -#define DREG_RGBK_MASK 0x00000700L -#define DREG_TRAP 0x00000800L -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_TRAPX 0x00001000L -#endif -#endif -#define DREG_REGID_SHIFT 0L -#define DREG_RGBK_SHIFT 8L -#define DREG_RGBK_REGID_MASK 0x0000077FL -#define DREG_REGID_R0 0x00000010L -#define DREG_REGID_R1 0x00000011L -#define DREG_REGID_R2 0x00000012L -#define DREG_REGID_R3 0x00000013L -#define DREG_REGID_R4 0x00000014L -#define DREG_REGID_R5 0x00000015L -#define DREG_REGID_R6 0x00000016L -#define DREG_REGID_R7 0x00000017L -#define DREG_REGID_R8 0x00000018L -#define DREG_REGID_R9 0x00000019L -#define DREG_REGID_RA 0x0000001AL -#define DREG_REGID_RB 0x0000001BL -#define DREG_REGID_RC 0x0000001CL -#define DREG_REGID_RD 0x0000001DL -#define DREG_REGID_RE 0x0000001EL -#define DREG_REGID_RF 0x0000001FL -#define DREG_REGID_RA_BUS_LOW 0x00000020L -#define DREG_REGID_RA_BUS_HIGH 0x00000038L -#define DREG_REGID_YBUS_LOW 0x00000050L -#define DREG_REGID_YBUS_HIGH 0x00000058L -#define DREG_REGID_TRAP_0 0x00000100L -#define DREG_REGID_TRAP_1 0x00000101L -#define DREG_REGID_TRAP_2 0x00000102L -#define DREG_REGID_TRAP_3 0x00000103L -#define DREG_REGID_TRAP_4 0x00000104L -#define DREG_REGID_TRAP_5 0x00000105L -#define DREG_REGID_TRAP_6 0x00000106L -#define DREG_REGID_TRAP_7 0x00000107L -#define DREG_REGID_INDIRECT_ADDRESS 0x0000010EL -#define DREG_REGID_TOP_OF_STACK 0x0000010FL -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_REGID_TRAP_8 0x00000110L -#define DREG_REGID_TRAP_9 0x00000111L -#define DREG_REGID_TRAP_10 0x00000112L -#define DREG_REGID_TRAP_11 0x00000113L -#define DREG_REGID_TRAP_12 0x00000114L -#define DREG_REGID_TRAP_13 0x00000115L -#define DREG_REGID_TRAP_14 0x00000116L -#define DREG_REGID_TRAP_15 0x00000117L -#define DREG_REGID_TRAP_16 0x00000118L -#define DREG_REGID_TRAP_17 0x00000119L -#define DREG_REGID_TRAP_18 0x0000011AL -#define DREG_REGID_TRAP_19 0x0000011BL -#define DREG_REGID_TRAP_20 0x0000011CL -#define DREG_REGID_TRAP_21 0x0000011DL -#define DREG_REGID_TRAP_22 0x0000011EL -#define DREG_REGID_TRAP_23 0x0000011FL -#endif -#endif -#define DREG_REGID_RSA0_LOW 0x00000200L -#define DREG_REGID_RSA0_HIGH 0x00000201L -#define DREG_REGID_RSA1_LOW 0x00000202L -#define DREG_REGID_RSA1_HIGH 0x00000203L -#define DREG_REGID_RSA2 0x00000204L -#define DREG_REGID_RSA3 0x00000205L -#define DREG_REGID_RSI0_LOW 0x00000206L -#define DREG_REGID_RSI0_HIGH 0x00000207L -#define DREG_REGID_RSI1 0x00000208L -#define DREG_REGID_RSI2 0x00000209L -#define DREG_REGID_SAGUSTATUS 0x0000020AL -#define DREG_REGID_RSCONFIG01_LOW 0x0000020BL -#define DREG_REGID_RSCONFIG01_HIGH 0x0000020CL -#define DREG_REGID_RSCONFIG23_LOW 0x0000020DL -#define DREG_REGID_RSCONFIG23_HIGH 0x0000020EL -#define DREG_REGID_RSDMA01E 0x0000020FL -#define DREG_REGID_RSDMA23E 0x00000210L -#define DREG_REGID_RSD0_LOW 0x00000211L -#define DREG_REGID_RSD0_HIGH 0x00000212L -#define DREG_REGID_RSD1_LOW 0x00000213L -#define DREG_REGID_RSD1_HIGH 0x00000214L -#define DREG_REGID_RSD2_LOW 0x00000215L -#define DREG_REGID_RSD2_HIGH 0x00000216L -#define DREG_REGID_RSD3_LOW 0x00000217L -#define DREG_REGID_RSD3_HIGH 0x00000218L -#define DREG_REGID_SRAR_HIGH 0x0000021AL -#define DREG_REGID_SRAR_LOW 0x0000021BL -#define DREG_REGID_DMA_STATE 0x0000021CL -#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021DL -#define DREG_REGID_NEXT_DMA_STREAM 0x0000021EL -#define DREG_REGID_CPU_STATUS 0x00000300L -#define DREG_REGID_MAC_MODE 0x00000301L -#define DREG_REGID_STACK_AND_REPEAT 0x00000302L -#define DREG_REGID_INDEX0 0x00000304L -#define DREG_REGID_INDEX1 0x00000305L -#define DREG_REGID_DMA_STATE_0_3 0x00000400L -#define DREG_REGID_DMA_STATE_4_7 0x00000404L -#define DREG_REGID_DMA_STATE_8_11 0x00000408L -#define DREG_REGID_DMA_STATE_12_15 0x0000040CL -#define DREG_REGID_DMA_STATE_16_19 0x00000410L -#define DREG_REGID_DMA_STATE_20_23 0x00000414L -#define DREG_REGID_DMA_STATE_24_27 0x00000418L -#define DREG_REGID_DMA_STATE_28_31 0x0000041CL -#define DREG_REGID_DMA_STATE_32_35 0x00000420L -#define DREG_REGID_DMA_STATE_36_39 0x00000424L -#define DREG_REGID_DMA_STATE_40_43 0x00000428L -#define DREG_REGID_DMA_STATE_44_47 0x0000042CL -#define DREG_REGID_DMA_STATE_48_51 0x00000430L -#define DREG_REGID_DMA_STATE_52_55 0x00000434L -#define DREG_REGID_DMA_STATE_56_59 0x00000438L -#define DREG_REGID_DMA_STATE_60_63 0x0000043CL -#define DREG_REGID_DMA_STATE_64_67 0x00000440L -#define DREG_REGID_DMA_STATE_68_71 0x00000444L -#define DREG_REGID_DMA_STATE_72_75 0x00000448L -#define DREG_REGID_DMA_STATE_76_79 0x0000044CL -#define DREG_REGID_DMA_STATE_80_83 0x00000450L -#define DREG_REGID_DMA_STATE_84_87 0x00000454L -#define DREG_REGID_DMA_STATE_88_91 0x00000458L -#define DREG_REGID_DMA_STATE_92_95 0x0000045CL -#define DREG_REGID_TRAP_SELECT 0x00000500L -#define DREG_REGID_TRAP_WRITE_0 0x00000500L -#define DREG_REGID_TRAP_WRITE_1 0x00000501L -#define DREG_REGID_TRAP_WRITE_2 0x00000502L -#define DREG_REGID_TRAP_WRITE_3 0x00000503L -#define DREG_REGID_TRAP_WRITE_4 0x00000504L -#define DREG_REGID_TRAP_WRITE_5 0x00000505L -#define DREG_REGID_TRAP_WRITE_6 0x00000506L -#define DREG_REGID_TRAP_WRITE_7 0x00000507L -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_REGID_TRAP_WRITE_8 0x00000510L -#define DREG_REGID_TRAP_WRITE_9 0x00000511L -#define DREG_REGID_TRAP_WRITE_10 0x00000512L -#define DREG_REGID_TRAP_WRITE_11 0x00000513L -#define DREG_REGID_TRAP_WRITE_12 0x00000514L -#define DREG_REGID_TRAP_WRITE_13 0x00000515L -#define DREG_REGID_TRAP_WRITE_14 0x00000516L -#define DREG_REGID_TRAP_WRITE_15 0x00000517L -#define DREG_REGID_TRAP_WRITE_16 0x00000518L -#define DREG_REGID_TRAP_WRITE_17 0x00000519L -#define DREG_REGID_TRAP_WRITE_18 0x0000051AL -#define DREG_REGID_TRAP_WRITE_19 0x0000051BL -#define DREG_REGID_TRAP_WRITE_20 0x0000051CL -#define DREG_REGID_TRAP_WRITE_21 0x0000051DL -#define DREG_REGID_TRAP_WRITE_22 0x0000051EL -#define DREG_REGID_TRAP_WRITE_23 0x0000051FL -#endif -#endif -#define DREG_REGID_MAC0_ACC0_LOW 0x00000600L -#define DREG_REGID_MAC0_ACC1_LOW 0x00000601L -#define DREG_REGID_MAC0_ACC2_LOW 0x00000602L -#define DREG_REGID_MAC0_ACC3_LOW 0x00000603L -#define DREG_REGID_MAC1_ACC0_LOW 0x00000604L -#define DREG_REGID_MAC1_ACC1_LOW 0x00000605L -#define DREG_REGID_MAC1_ACC2_LOW 0x00000606L -#define DREG_REGID_MAC1_ACC3_LOW 0x00000607L -#define DREG_REGID_MAC0_ACC0_MID 0x00000608L -#define DREG_REGID_MAC0_ACC1_MID 0x00000609L -#define DREG_REGID_MAC0_ACC2_MID 0x0000060AL -#define DREG_REGID_MAC0_ACC3_MID 0x0000060BL -#define DREG_REGID_MAC1_ACC0_MID 0x0000060CL -#define DREG_REGID_MAC1_ACC1_MID 0x0000060DL -#define DREG_REGID_MAC1_ACC2_MID 0x0000060EL -#define DREG_REGID_MAC1_ACC3_MID 0x0000060FL -#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610L -#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611L -#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612L -#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613L -#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614L -#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615L -#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616L -#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617L -#define DREG_REGID_RSHOUT_LOW 0x00000620L -#define DREG_REGID_RSHOUT_MID 0x00000628L -#define DREG_REGID_RSHOUT_HIGH 0x00000630L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 S/PDIF Control register. -// -//**************************************************************************** -#define SPDIF_CONTROL_SPDIF_EN 0x00008000L -#define SPDIF_CONTROL_VAL 0x00004000L -#define SPDIF_CONTROL_COPY 0x00000004L -#define SPDIF_CONTROL_CC0 0x00000010L -#define SPDIF_CONTROL_CC1 0x00000020L -#define SPDIF_CONTROL_CC2 0x00000040L -#define SPDIF_CONTROL_CC3 0x00000080L -#define SPDIF_CONTROL_CC4 0x00000100L -#define SPDIF_CONTROL_CC5 0x00000200L -#define SPDIF_CONTROL_CC6 0x00000400L -#define SPDIF_CONTROL_L 0x00000800L - -#endif // _H_HWDEFS diff -Nru a/drivers/sound/cs4281/cs4281_wrapper-24.c b/drivers/sound/cs4281/cs4281_wrapper-24.c --- a/drivers/sound/cs4281/cs4281_wrapper-24.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,42 +0,0 @@ -/******************************************************************************* -* -* "cs4281_wrapper.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. -* -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- tom woller (twoller@crystal.cirrus.com) or -* (audio@crystal.cirrus.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. -* -* 12/20/00 trw - new file. -* -*******************************************************************************/ - -#include - -void cs4281_null(struct pci_dev *pcidev) { return; } -#define cs4x_mem_map_reserve(page) mem_map_reserve(page) -#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page) - -#define free_dmabuf(state, dmabuf) \ - pci_free_consistent(state->pcidev, \ - PAGE_SIZE << (dmabuf)->buforder, \ - (dmabuf)->rawbuf, (dmabuf)->dmaaddr); -#define free_dmabuf2(state, dmabuf) \ - pci_free_consistent((state)->pcidev, \ - PAGE_SIZE << (state)->buforder_tmpbuff, \ - (state)->tmpbuff, (state)->dmaaddr_tmpbuff); -#define cs4x_pgoff(vma) ((vma)->vm_pgoff) - diff -Nru a/drivers/sound/cs4281/cs4281m.c b/drivers/sound/cs4281/cs4281m.c --- a/drivers/sound/cs4281/cs4281m.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,4528 +0,0 @@ -/******************************************************************************* -* -* "cs4281.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. -* -* 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). -* -* 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 cs4281_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. -* -*******************************************************************************/ - -/* uncomment the following line to disable building PM support into the driver */ -//#define NOT_CS4281_PM 1 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -//#include "cs_dm.h" -#include "cs4281_hwdefs.h" -#include "cs4281pm.h" - -struct cs4281_state; -EXPORT_NO_SYMBOLS; - -static void stop_dac(struct cs4281_state *s); -static void stop_adc(struct cs4281_state *s); -static void start_dac(struct cs4281_state *s); -static void start_adc(struct cs4281_state *s); -#undef OSS_DOCUMENTED_MIXER_SEMANTICS - -// --------------------------------------------------------------------- - -#ifndef PCI_VENDOR_ID_CIRRUS -#define PCI_VENDOR_ID_CIRRUS 0x1013 -#endif -#ifndef PCI_DEVICE_ID_CRYSTAL_CS4281 -#define PCI_DEVICE_ID_CRYSTAL_CS4281 0x6005 -#endif - -#define CS4281_MAGIC ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS) -#define CS4281_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */ - -// 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 1 -#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_MIDI_WRITE 0x00000040 // write information for midi -#define CS_MIDI_READ 0x00000080 // read information for midi -#define CS_MPU401_WRITE 0x00000100 // write information for mpu401 -#define CS_MPU401_READ 0x00000200 // read information for mpu401 -#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_PM 0x00004000 // power management -#define CS_TMP 0x10000000 // tmp debug mask bit - -#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend -#define CS_IOCTL_CMD_RESUME 0x2 // resume -// -// 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 = 1; // levels range from 1-9 -static unsigned long cs_debugmask = CS_INIT | CS_ERROR; // use CS_DBGOUT with various mask values -MODULE_PARM(cs_debuglevel, "i"); -MODULE_PARM(cs_debugmask, "i"); -#endif -#define CS_TRUE 1 -#define CS_FALSE 0 - -// MIDI buffer sizes -#define MIDIINBUF 500 -#define MIDIOUTBUF 500 - -#define FMODE_MIDI_SHIFT 3 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -#define CS4281_MAJOR_VERSION 1 -#define CS4281_MINOR_VERSION 13 -#ifdef __ia64__ -#define CS4281_ARCH 64 //architecture key -#else -#define CS4281_ARCH 32 //architecture key -#endif - -#define CS_TYPE_ADC 0 -#define CS_TYPE_DAC 1 - - -static const char invalid_magic[] = - KERN_CRIT "cs4281: invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != CS4281_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -//LIST_HEAD(cs4281_devs); -struct list_head cs4281_devs = { &cs4281_devs, &cs4281_devs }; - -struct cs4281_state; - -#include "cs4281_wrapper-24.c" - -struct cs4281_state { - // magic - unsigned int magic; - - // we keep the cards in a linked list - struct cs4281_state *next; - - // pcidev is needed to turn off the DDMA controller at driver shutdown - struct pci_dev *pcidev; - struct list_head list; - - // soundcore stuff - int dev_audio; - int dev_mixer; - int dev_midi; - - // hardware resources - unsigned int pBA0phys, pBA1phys; - char *pBA0, *pBA1; - unsigned int irq; - - // 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; - unsigned char clkdiv; - } prop_dac, prop_adc; - unsigned conversion:1; // conversion from 16 to 8 bit in progress - void *tmpbuff; // tmp buffer for sample conversions - 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_tmpbuff; - unsigned buforder_tmpbuff; // Log base 2 of 'rawbuf' size in bytes.. - struct dmabuf { - void *rawbuf; // Physical address of - dma_addr_t dmaaddr; - unsigned buforder; // Log base 2 of 'rawbuf' 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; - // redundant, but makes calculations easier - unsigned fragsize; // 2**fragshift.. - unsigned dmasize; // 2**buforder. - unsigned fragsamples; - // OSS stuff - unsigned mapped:1; // Buffer mapped in cs4281_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; - } dma_dac, dma_adc; - - // midi stuff - struct { - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t iwait; - wait_queue_head_t owait; - struct timer_list timer; - unsigned char ibuf[MIDIINBUF]; - unsigned char obuf[MIDIOUTBUF]; - } midi; - - struct cs4281_pm pm; - struct cs4281_pipeline pl[CS4281_NUMBER_OF_PIPELINES]; -}; - -#include "cs4281pm-24.c" - -#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) - -#define SOUND_MIXER_CS_APM _SIOWR('M',124, 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 prog_dmabuf_adc(struct cs4281_state *s); -static void prog_codec(struct cs4281_state *s, unsigned type); - -// --------------------------------------------------------------------- -// -// Hardware Interfaces For the CS4281 -// - - -//****************************************************************************** -// "delayus()-- Delay for the specified # of microseconds. -//****************************************************************************** -static void delayus(struct cs4281_state *s, u32 delay) -{ - u32 j; - if ((delay > 9999) && (s->pm.flags & CS4281_PM_IDLE)) { - j = (delay * HZ) / 1000000; /* calculate delay in jiffies */ - if (j < 1) - j = 1; /* minimum one jiffy. */ - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(j); - } else - udelay(delay); - return; -} - - -//****************************************************************************** -// "cs4281_read_ac97" -- Reads a word from the specified location in the -// CS4281's address space(based on the BA0 register). -// -// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address -// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 register, -// 0h for reads. -// 3. Write ACCTL = Control Register = 460h for initiating the write -// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h -// 5. if DCV not cleared, break and return error -// 6. Read ACSTS = Status Register = 464h, check VSTS bit -//**************************************************************************** -static int cs4281_read_ac97(struct cs4281_state *card, u32 offset, - u32 * value) -{ - u32 count, status; - - // Make sure that there is not data sitting - // around from a previous uncompleted access. - // ACSDA = Status Data Register = 47Ch - status = readl(card->pBA0 + BA0_ACSDA); - - // Setup the AC97 control registers on the CS4281 to send the - // appropriate command to the AC97 to perform the read. - // ACCAD = Command Address Register = 46Ch - // ACCDA = Command Data Register = 470h - // ACCTL = Control Register = 460h - // bit DCV - will clear when process completed - // bit CRW - Read command - // bit VFRM - valid frame enabled - // bit ESYN - ASYNC generation enabled - - // Get the actual AC97 register from the offset - writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); - writel(0, card->pBA0 + BA0_ACCDA); - writel(ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN, - card->pBA0 + BA0_ACCTL); - - // Wait for the read to occur. - for (count = 0; count < 10; count++) { - // First, we want to wait for a short time. - udelay(25); - - // Now, check to see if the read has completed. - // ACCTL = 460h, DCV should be reset by now and 460h = 17h - if (!(readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV)) - break; - } - - // Make sure the read completed. - if (readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV) - return 1; - - // Wait for the valid status bit to go active. - for (count = 0; count < 10; count++) { - // Read the AC97 status register. - // ACSTS = Status Register = 464h - status = readl(card->pBA0 + BA0_ACSTS); - - // See if we have valid status. - // VSTS - Valid Status - if (status & ACSTS_VSTS) - break; - // Wait for a short while. - udelay(25); - } - - // Make sure we got valid status. - if (!(status & ACSTS_VSTS)) - return 1; - - // Read the data returned from the AC97 register. - // ACSDA = Status Data Register = 474h - *value = readl(card->pBA0 + BA0_ACSDA); - - // Success. - return (0); -} - - -//**************************************************************************** -// -// "cs4281_write_ac97()"-- writes a word to the specified location in the -// CS461x's address space (based on the part's base address zero register). -// -// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address -// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 reg. -// 3. Write ACCTL = Control Register = 460h for initiating the write -// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h -// 5. if DCV not cleared, break and return error -// -//**************************************************************************** -static int cs4281_write_ac97(struct cs4281_state *card, u32 offset, - u32 value) -{ - u32 count, status=0; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cs_4281_write_ac97()+ \n")); - - // Setup the AC97 control registers on the CS4281 to send the - // appropriate command to the AC97 to perform the read. - // ACCAD = Command Address Register = 46Ch - // ACCDA = Command Data Register = 470h - // ACCTL = Control Register = 460h - // set DCV - will clear when process completed - // reset CRW - Write command - // set VFRM - valid frame enabled - // set ESYN - ASYNC generation enabled - // set RSTN - ARST# inactive, AC97 codec not reset - - // Get the actual AC97 register from the offset - - writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); - writel(value, card->pBA0 + BA0_ACCDA); - writel(ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN, - card->pBA0 + BA0_ACCTL); - - // Wait for the write to occur. - for (count = 0; count < 100; count++) { - // First, we want to wait for a short time. - udelay(25); - // Now, check to see if the write has completed. - // ACCTL = 460h, DCV should be reset by now and 460h = 07h - status = readl(card->pBA0 + BA0_ACCTL); - if (!(status & ACCTL_DCV)) - break; - } - - // Make sure the write completed. - if (status & ACCTL_DCV) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs4281: cs_4281_write_ac97()- unable to write. ACCTL_DCV active\n")); - return 1; - } - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cs_4281_write_ac97()- 0\n")); - // Success. - return 0; -} - - -//****************************************************************************** -// "Init4281()" -- Bring up the part. -//****************************************************************************** -static __devinit int cs4281_hw_init(struct cs4281_state *card) -{ - u32 ac97_slotid; - u32 temp1, temp2; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cs4281_hw_init()+ \n")); -#ifndef NOT_CS4281_PM - if(!card) - return 1; -#endif - temp2 = readl(card->pBA0 + BA0_CFLR); - CS_DBGOUT(CS_INIT | CS_ERROR | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_hw_init() CFLR 0x%x\n", temp2)); - if(temp2 != CS4281_CFLR_DEFAULT) - { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO - "cs4281: cs4281_hw_init() CFLR invalid - resetting from 0x%x to 0x%x\n", - temp2,CS4281_CFLR_DEFAULT)); - writel(CS4281_CFLR_DEFAULT, card->pBA0 + BA0_CFLR); - temp2 = readl(card->pBA0 + BA0_CFLR); - if(temp2 != CS4281_CFLR_DEFAULT) - { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO - "cs4281: cs4281_hw_init() Invalid hardware - unable to configure CFLR\n")); - return 1; - } - } - - //***************************************7 - // Set up the Sound System Configuration - //*************************************** - - // Set the 'Configuration Write Protect' register - // to 4281h. Allows vendor-defined configuration - // space between 0e4h and 0ffh to be written. - - writel(0x4281, card->pBA0 + BA0_CWPR); // (3e0h) - - // (0), Blast the clock control register to zero so that the - // PLL starts out in a known state, and blast the master serial - // port control register to zero so that the serial ports also - // start out in a known state. - - writel(0, card->pBA0 + BA0_CLKCR1); // (400h) - writel(0, card->pBA0 + BA0_SERMC); // (420h) - - - // (1), Make ESYN go to zero to turn off - // the Sync pulse on the AC97 link. - - writel(0, card->pBA0 + BA0_ACCTL); - udelay(50); - - - // (2) Drive the ARST# pin low for a minimum of 1uS (as defined in - // the AC97 spec) and then drive it high. This is done for non - // AC97 modes since there might be logic external to the CS461x - // that uses the ARST# line for a reset. - - writel(0, card->pBA0 + BA0_SPMC); // (3ech) - udelay(100); - writel(SPMC_RSTN, card->pBA0 + BA0_SPMC); - delayus(card,50000); // Wait 50 ms for ABITCLK to become stable. - - // (3) Turn on the Sound System Clocks. - writel(CLKCR1_PLLP, card->pBA0 + BA0_CLKCR1); // (400h) - delayus(card,50000); // Wait for the PLL to stabilize. - // Turn on clocking of the core (CLKCR1(400h) = 0x00000030) - writel(CLKCR1_PLLP | CLKCR1_SWCE, card->pBA0 + BA0_CLKCR1); - - // (4) Power on everything for now.. - writel(0x7E, card->pBA0 + BA0_SSPM); // (740h) - - // (5) Wait for clock stabilization. - for (temp1 = 0; temp1 < 1000; temp1++) { - udelay(1000); - if (readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY) - break; - } - if (!(readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY)) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: DLLRDY failed!\n")); - return -EIO; - } - // (6) Enable ASYNC generation. - writel(ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) - - // Now wait 'for a short while' to allow the AC97 - // part to start generating bit clock. (so we don't - // Try to start the PLL without an input clock.) - delayus(card,50000); - - // Set the serial port timing configuration, so that the - // clock control circuit gets its clock from the right place. - writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. - - // (7) Wait for the codec ready signal from the AC97 codec. - - for (temp1 = 0; temp1 < 1000; temp1++) { - // Delay a mil to let things settle out and - // to prevent retrying the read too quickly. - udelay(1000); - if (readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY) // If ready, (464h) - break; // exit the 'for' loop. - } - if (!(readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY)) // If never came ready, - { - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR - "cs4281: ACSTS never came ready!\n")); - return -EIO; // exit initialization. - } - // (8) Assert the 'valid frame' signal so we can - // begin sending commands to the AC97 codec. - writel(ACCTL_VFRM | ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) - - // (9), Wait until CODEC calibration is finished. - // Print an error message if it doesn't. - for (temp1 = 0; temp1 < 1000; temp1++) { - delayus(card,10000); - // Read the AC97 Powerdown Control/Status Register. - cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp2); - if ((temp2 & 0x0000000F) == 0x0000000F) - break; - } - if ((temp2 & 0x0000000F) != 0x0000000F) { - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR - "cs4281: Codec failed to calibrate. Status = %.8x.\n", - temp2)); - return -EIO; - } - // (10), Set the serial port timing configuration, so that the - // clock control circuit gets its clock from the right place. - writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. - - - // (11) Wait until we've sampled input slots 3 & 4 as valid, meaning - // that the codec is pumping ADC data across the AC link. - for (temp1 = 0; temp1 < 1000; temp1++) { - // Delay a mil to let things settle out and - // to prevent retrying the read too quickly. - delayus(card,1000); //(test) - - // Read the input slot valid register; See - // if input slots 3 and 4 are valid yet. - if ( - (readl(card->pBA0 + BA0_ACISV) & - (ACISV_ISV3 | ACISV_ISV4)) == - (ACISV_ISV3 | ACISV_ISV4)) break; // Exit the 'for' if slots are valid. - } - // If we never got valid data, exit initialization. - if ((readl(card->pBA0 + BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) - != (ACISV_ISV3 | ACISV_ISV4)) { - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_ERR - "cs4281: Never got valid data!\n")); - return -EIO; // If no valid data, exit initialization. - } - // (12), Start digital data transfer of audio data to the codec. - writel(ACOSV_SLV3 | ACOSV_SLV4, card->pBA0 + BA0_ACOSV); // (468h) - - - //************************************** - // Unmute the Master and Alternate - // (headphone) volumes. Set to max. - //************************************** - cs4281_write_ac97(card, BA0_AC97_HEADPHONE_VOLUME, 0); - cs4281_write_ac97(card, BA0_AC97_MASTER_VOLUME, 0); - - //****************************************** - // Power on the DAC(AddDACUser()from main()) - //****************************************** - cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); - cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfdff); - - // Wait until we sample a DAC ready state. - for (temp2 = 0; temp2 < 32; temp2++) { - // Let's wait a mil to let things settle. - delayus(card,1000); - // Read the current state of the power control reg. - cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); - // If the DAC ready state bit is set, stop waiting. - if (temp1 & 0x2) - break; - } - - //****************************************** - // Power on the ADC(AddADCUser()from main()) - //****************************************** - cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); - cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfeff); - - // Wait until we sample ADC ready state. - for (temp2 = 0; temp2 < 32; temp2++) { - // Let's wait a mil to let things settle. - delayus(card,1000); - // Read the current state of the power control reg. - cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); - // If the ADC ready state bit is set, stop waiting. - if (temp1 & 0x1) - break; - } - // Set up 4281 Register contents that - // don't change for boot duration. - - // For playback, we map AC97 slot 3 and 4(Left - // & Right PCM playback) to DMA Channel 0. - // Set the fifo to be 15 bytes at offset zero. - - ac97_slotid = 0x01000f00; // FCR0.RS[4:0]=1(=>slot4, right PCM playback). - // FCR0.LS[4:0]=0(=>slot3, left PCM playback). - // FCR0.SZ[6-0]=15; FCR0.OF[6-0]=0. - writel(ac97_slotid, card->pBA0 + BA0_FCR0); // (180h) - writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR0); // Turn on FIFO Enable. - - // For capture, we map AC97 slot 10 and 11(Left - // and Right PCM Record) to DMA Channel 1. - // Set the fifo to be 15 bytes at offset sixteen. - ac97_slotid = 0x0B0A0f10; // FCR1.RS[4:0]=11(=>slot11, right PCM record). - // FCR1.LS[4:0]=10(=>slot10, left PCM record). - // FCR1.SZ[6-0]=15; FCR1.OF[6-0]=16. - writel(ac97_slotid | FCRn_PSH, card->pBA0 + BA0_FCR1); // (184h) - writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR1); // Turn on FIFO Enable. - - // Map the Playback SRC to the same AC97 slots(3 & 4-- - // --Playback left & right)as DMA channel 0. - // Map the record SRC to the same AC97 slots(10 & 11-- - // -- Record left & right) as DMA channel 1. - - ac97_slotid = 0x0b0a0100; // SCRSA.PRSS[4:0]=1(=>slot4, right PCM playback). - // SCRSA.PLSS[4:0]=0(=>slot3, left PCM playback). - // SCRSA.CRSS[4:0]=11(=>slot11, right PCM record) - // SCRSA.CLSS[4:0]=10(=>slot10, left PCM record). - writel(ac97_slotid, card->pBA0 + BA0_SRCSA); // (75ch) - - // Set 'Half Terminal Count Interrupt Enable' and 'Terminal - // Count Interrupt Enable' in DMA Control Registers 0 & 1. - // Set 'MSK' flag to 1 to keep the DMA engines paused. - temp1 = (DCRn_HTCIE | DCRn_TCIE | DCRn_MSK); // (00030001h) - writel(temp1, card->pBA0 + BA0_DCR0); // (154h - writel(temp1, card->pBA0 + BA0_DCR1); // (15ch) - - // Set 'Auto-Initialize Control' to 'enabled'; For playback, - // set 'Transfer Type Control'(TR[1:0]) to 'read transfer', - // for record, set Transfer Type Control to 'write transfer'. - // All other bits set to zero; Some will be changed @ transfer start. - temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_READ); // (20000018h) - writel(temp1, card->pBA0 + BA0_DMR0); // (150h) - temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE); // (20000014h) - writel(temp1, card->pBA0 + BA0_DMR1); // (158h) - - // Enable DMA interrupts generally, and - // DMA0 & DMA1 interrupts specifically. - temp1 = readl(card->pBA0 + BA0_HIMR) & 0xfffbfcff; - writel(temp1, card->pBA0 + BA0_HIMR); - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cs4281_hw_init()- 0\n")); - return 0; -} - -#ifndef NOT_CS4281_PM -static void printpm(struct cs4281_state *s) -{ - CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); - CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n", - (unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue)); - CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n", - s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue)); - CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n", - s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue)); - CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n", - s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue)); - CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n", - s->pm.u32SSCR,s->pm.u32SRCSA)); - CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n", - s->pm.u32DacASR,s->pm.u32AdcASR)); - CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n", - s->pm.u32DacSR,s->pm.u32AdcSR)); - CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n", - s->pm.u32MIDCR_Save)); - -} -static void printpipe(struct cs4281_pipeline *pl) -{ - - CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); - CS_DBGOUT(CS_PM, 9, printk("flags:0x%x number: 0%x\n", - (unsigned)pl->flags,pl->number)); - CS_DBGOUT(CS_PM, 9, printk("u32DBAnValue: 0%x u32DBCnValue: 0x%x\n", - pl->u32DBAnValue,pl->u32DBCnValue)); - CS_DBGOUT(CS_PM, 9, printk("u32DMRnValue: 0x%x u32DCRnValue: 0x%x\n", - pl->u32DMRnValue,pl->u32DCRnValue)); - CS_DBGOUT(CS_PM, 9, printk("u32DBAnAddress: 0x%x u32DBCnAddress: 0x%x\n", - pl->u32DBAnAddress,pl->u32DBCnAddress)); - CS_DBGOUT(CS_PM, 9, printk("u32DCAnAddress: 0x%x u32DCCnAddress: 0x%x\n", - pl->u32DCCnAddress,pl->u32DCCnAddress)); - CS_DBGOUT(CS_PM, 9, printk("u32DMRnAddress: 0x%x u32DCRnAddress: 0x%x\n", - pl->u32DMRnAddress,pl->u32DCRnAddress)); - CS_DBGOUT(CS_PM, 9, printk("u32HDSRnAddress: 0x%x u32DBAn_Save: 0x%x\n", - pl->u32HDSRnAddress,pl->u32DBAn_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32DBCn_Save: 0x%x u32DMRn_Save: 0x%x\n", - pl->u32DBCn_Save,pl->u32DMRn_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32DCRn_Save: 0x%x u32DCCn_Save: 0x%x\n", - pl->u32DCRn_Save,pl->u32DCCn_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32DCAn_Save: 0x%x\n", - pl->u32DCAn_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32FCRn_Save: 0x%x u32FSICn_Save: 0x%x\n", - pl->u32FCRn_Save,pl->u32FSICn_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32FCRnValue: 0x%x u32FSICnValue: 0x%x\n", - pl->u32FCRnValue,pl->u32FSICnValue)); - CS_DBGOUT(CS_PM, 9, printk("u32FCRnAddress: 0x%x u32FSICnAddress: 0x%x\n", - pl->u32FCRnAddress,pl->u32FSICnAddress)); - CS_DBGOUT(CS_PM, 9, printk("u32FPDRnValue: 0x%x u32FPDRnAddress: 0x%x\n", - pl->u32FPDRnValue,pl->u32FPDRnAddress)); -} -static void printpipelines(struct cs4281_state *s) -{ - int i; - for(i=0;ipl[i].flags & CS4281_PIPELINE_VALID) - { - printpipe(&s->pl[i]); - } - } -} -/**************************************************************************** -* -* Suspend - save the ac97 regs, mute the outputs and power down the part. -* -****************************************************************************/ -void cs4281_ac97_suspend(struct cs4281_state *s) -{ - int Count,i; - - CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()+\n")); -/* -* change the state, save the current hwptr, then stop the dac/adc -*/ - s->pm.flags &= ~CS4281_PM_IDLE; - s->pm.flags |= CS4281_PM_SUSPENDING; - s->pm.u32hwptr_playback = readl(s->pBA0 + BA0_DCA0); - s->pm.u32hwptr_capture = readl(s->pBA0 + BA0_DCA1); - stop_dac(s); - stop_adc(s); - - for(Count = 0x2, i=0; (Count <= CS4281_AC97_HIGHESTREGTORESTORE) - && (i < CS4281_AC97_NUMBER_RESTORE_REGS); - Count += 2, i++) - { - cs4281_read_ac97(s, BA0_AC97_RESET + Count, &s->pm.ac97[i]); - } -/* -* Save the ac97 volume registers as well as the current powerdown state. -* Now, mute the all the outputs (master, headphone, and mono), as well -* as the PCM volume, in preparation for powering down the entire part. -*/ - cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME, &s->pm.u32AC97_master_volume); - cs4281_read_ac97(s, BA0_AC97_HEADPHONE_VOLUME, &s->pm.u32AC97_headphone_volume); - cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, &s->pm.u32AC97_master_volume_mono); - cs4281_read_ac97(s, BA0_AC97_PCM_OUT_VOLUME, &s->pm.u32AC97_pcm_out_volume); - - cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, 0x8000); - cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, 0x8000); - cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, 0x8000); - cs4281_write_ac97(s, BA0_AC97_PCM_OUT_VOLUME, 0x8000); - - cs4281_read_ac97(s, BA0_AC97_POWERDOWN, &s->pm.u32AC97_powerdown); - cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, &s->pm.u32AC97_general_purpose); - -/* -* And power down everything on the AC97 codec. -*/ - cs4281_write_ac97(s, BA0_AC97_POWERDOWN, 0xff00); - CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()-\n")); -} - -/**************************************************************************** -* -* Resume - power up the part and restore its registers.. -* -****************************************************************************/ -void cs4281_ac97_resume(struct cs4281_state *s) -{ - int Count,i; - - CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()+\n")); - -/* do not save the power state registers at this time - // - // If we saved away the power control registers, write them into the - // shadows so those saved values get restored instead of the current - // shadowed value. - // - if( bPowerStateSaved ) - { - PokeShadow( 0x26, ulSaveReg0x26 ); - bPowerStateSaved = FALSE; - } -*/ - -// -// First, we restore the state of the general purpose register. This -// contains the mic select (mic1 or mic2) and if we restore this after -// we restore the mic volume/boost state and mic2 was selected at -// suspend time, we will end up with a brief period of time where mic1 -// is selected with the volume/boost settings for mic2, causing -// acoustic feedback. So we restore the general purpose register -// first, thereby getting the correct mic selected before we restore -// the mic volume/boost. -// - cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, s->pm.u32AC97_general_purpose); - -// -// Now, while the outputs are still muted, restore the state of power -// on the AC97 part. -// - cs4281_write_ac97(s, BA0_AC97_POWERDOWN, s->pm.u32AC97_powerdown); - -/* -* Restore just the first set of registers, from register number -* 0x02 to the register number that ulHighestRegToRestore specifies. -*/ - for( Count = 0x2, i=0; - (Count <= CS4281_AC97_HIGHESTREGTORESTORE) - && (i < CS4281_AC97_NUMBER_RESTORE_REGS); - Count += 2, i++) - { - cs4281_write_ac97(s, BA0_AC97_RESET + Count, s->pm.ac97[i]); - } - CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()-\n")); -} - -/* do not save the power state registers at this time -**************************************************************************** -* -* SavePowerState - Save the power registers away. -* -**************************************************************************** -void -HWAC97codec::SavePowerState(void) -{ - ENTRY(TM_OBJECTCALLS, "HWAC97codec::SavePowerState()\r\n"); - - ulSaveReg0x26 = PeekShadow(0x26); - - // - // Note that we have saved registers that need to be restored during a - // resume instead of ulAC97Regs[]. - // - bPowerStateSaved = TRUE; - -} // SavePowerState -*/ - -void cs4281_SuspendFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) -{ - /* - * We need to save the contents of the BASIC FIFO Registers. - */ - pl->u32FCRn_Save = readl(s->pBA0 + pl->u32FCRnAddress); - pl->u32FSICn_Save = readl(s->pBA0 + pl->u32FSICnAddress); -} -void cs4281_ResumeFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) -{ - /* - * We need to restore the contents of the BASIC FIFO Registers. - */ - writel(pl->u32FCRn_Save,s->pBA0 + pl->u32FCRnAddress); - writel(pl->u32FSICn_Save,s->pBA0 + pl->u32FSICnAddress); -} -void cs4281_SuspendDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) -{ - // - // We need to save the contents of the BASIC DMA Registers. - // - pl->u32DBAn_Save = readl(s->pBA0 + pl->u32DBAnAddress); - pl->u32DBCn_Save = readl(s->pBA0 + pl->u32DBCnAddress); - pl->u32DMRn_Save = readl(s->pBA0 + pl->u32DMRnAddress); - pl->u32DCRn_Save = readl(s->pBA0 + pl->u32DCRnAddress); - pl->u32DCCn_Save = readl(s->pBA0 + pl->u32DCCnAddress); - pl->u32DCAn_Save = readl(s->pBA0 + pl->u32DCAnAddress); -} -void cs4281_ResumeDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) -{ - // - // We need to save the contents of the BASIC DMA Registers. - // - writel( pl->u32DBAn_Save, s->pBA0 + pl->u32DBAnAddress); - writel( pl->u32DBCn_Save, s->pBA0 + pl->u32DBCnAddress); - writel( pl->u32DMRn_Save, s->pBA0 + pl->u32DMRnAddress); - writel( pl->u32DCRn_Save, s->pBA0 + pl->u32DCRnAddress); - writel( pl->u32DCCn_Save, s->pBA0 + pl->u32DCCnAddress); - writel( pl->u32DCAn_Save, s->pBA0 + pl->u32DCAnAddress); -} - -int cs4281_suspend(struct cs4281_state *s) -{ - int i; - u32 u32CLKCR1; - struct cs4281_pm *pm = &s->pm; - CS_DBGOUT(CS_PM | CS_FUNCTION, 9, - printk("cs4281: cs4281_suspend()+ flags=%d\n", - (unsigned)s->pm.flags)); -/* -* check the current state, only suspend if IDLE -*/ - if(!(s->pm.flags & CS4281_PM_IDLE)) - { - CS_DBGOUT(CS_PM | CS_ERROR, 2, - printk("cs4281: cs4281_suspend() unable to suspend, not IDLE\n")); - return 1; - } - s->pm.flags &= ~CS4281_PM_IDLE; - s->pm.flags |= CS4281_PM_SUSPENDING; - -// -// Gershwin CLKRUN - Set CKRA -// - u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); - - pm->u32CLKCR1_SAVE = u32CLKCR1; - if(!(u32CLKCR1 & 0x00010000 ) ) - writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); - -// -// First, turn on the clocks (yikes) to the devices, so that they will -// respond when we try to save their state. -// - if(!(u32CLKCR1 & CLKCR1_SWCE)) - { - writel(u32CLKCR1 | CLKCR1_SWCE , s->pBA0 + BA0_CLKCR1); - } - - // - // Save the power state - // - pm->u32SSPMValue = readl(s->pBA0 + BA0_SSPM); - - // - // Disable interrupts. - // - writel(HICR_CHGM, s->pBA0 + BA0_HICR); - - // - // Save the PCM Playback Left and Right Volume Control. - // - pm->u32PPLVCvalue = readl(s->pBA0 + BA0_PPLVC); - pm->u32PPRVCvalue = readl(s->pBA0 + BA0_PPRVC); - - // - // Save the FM Synthesis Left and Right Volume Control. - // - pm->u32FMLVCvalue = readl(s->pBA0 + BA0_FMLVC); - pm->u32FMRVCvalue = readl(s->pBA0 + BA0_FMRVC); - - // - // Save the GPIOR value. - // - pm->u32GPIORvalue = readl(s->pBA0 + BA0_GPIOR); - - // - // Save the JSCTL value. - // - pm->u32JSCTLvalue = readl(s->pBA0 + BA0_GPIOR); - - // - // Save Sound System Control Register - // - pm->u32SSCR = readl(s->pBA0 + BA0_SSCR); - - // - // Save SRC Slot Assinment register - // - pm->u32SRCSA = readl(s->pBA0 + BA0_SRCSA); - - // - // Save sample rate - // - pm->u32DacASR = readl(s->pBA0 + BA0_PASR); - pm->u32AdcASR = readl(s->pBA0 + BA0_CASR); - pm->u32DacSR = readl(s->pBA0 + BA0_DACSR); - pm->u32AdcSR = readl(s->pBA0 + BA0_ADCSR); - - // - // Loop through all of the PipeLines - // - for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) - { - if(s->pl[i].flags & CS4281_PIPELINE_VALID) - { - // - // Ask the DMAengines and FIFOs to Suspend. - // - cs4281_SuspendDMAengine(s,&s->pl[i]); - cs4281_SuspendFIFO(s,&s->pl[i]); - } - } - // - // We need to save the contents of the Midi Control Register. - // - pm->u32MIDCR_Save = readl(s->pBA0 + BA0_MIDCR); -/* -* save off the AC97 part information -*/ - cs4281_ac97_suspend(s); - - // - // Turn off the serial ports. - // - writel(0, s->pBA0 + BA0_SERMC); - - // - // Power off FM, Joystick, AC link, - // - writel(0, s->pBA0 + BA0_SSPM); - - // - // DLL off. - // - writel(0, s->pBA0 + BA0_CLKCR1); - - // - // AC link off. - // - writel(0, s->pBA0 + BA0_SPMC); - - // - // Put the chip into D3(hot) state. - // - // PokeBA0(BA0_PMCS, 0x00000003); - - // - // Gershwin CLKRUN - Clear CKRA - // - u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); - writel(u32CLKCR1 & 0xFFFEFFFF, s->pBA0 + BA0_CLKCR1); - -#ifdef CSDEBUG - printpm(s); - printpipelines(s); -#endif - - s->pm.flags &= ~CS4281_PM_SUSPENDING; - s->pm.flags |= CS4281_PM_SUSPENDED; - - CS_DBGOUT(CS_PM | CS_FUNCTION, 9, - printk("cs4281: cs4281_suspend()- flags=%d\n", - (unsigned)s->pm.flags)); - return 0; -} - -int cs4281_resume(struct cs4281_state *s) -{ - int i; - unsigned temp1; - u32 u32CLKCR1; - struct cs4281_pm *pm = &s->pm; - CS_DBGOUT(CS_PM | CS_FUNCTION, 4, - printk( "cs4281: cs4281_resume()+ flags=%d\n", - (unsigned)s->pm.flags)); - if(!(s->pm.flags & CS4281_PM_SUSPENDED)) - { - CS_DBGOUT(CS_PM | CS_ERROR, 2, - printk("cs4281: cs4281_resume() unable to resume, not SUSPENDED\n")); - return 1; - } - s->pm.flags &= ~CS4281_PM_SUSPENDED; - s->pm.flags |= CS4281_PM_RESUMING; - -// -// Gershwin CLKRUN - Set CKRA -// - u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); - writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); - - // - // set the power state. - // - //old PokeBA0(BA0_PMCS, 0); - - // - // Program the clock circuit and serial ports. - // - temp1 = cs4281_hw_init(s); - if (temp1) { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, - printk(KERN_ERR - "cs4281: resume cs4281_hw_init() error.\n")); - return -1; - } - - // - // restore the Power state - // - writel(pm->u32SSPMValue, s->pBA0 + BA0_SSPM); - - // - // Set post SRC mix setting (FM or ALT48K) - // - writel(pm->u32SSPM_BITS, s->pBA0 + BA0_SSPM); - - // - // Loop through all of the PipeLines - // - for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) - { - if(s->pl[i].flags & CS4281_PIPELINE_VALID) - { - // - // Ask the DMAengines and FIFOs to Resume. - // - cs4281_ResumeDMAengine(s,&s->pl[i]); - cs4281_ResumeFIFO(s,&s->pl[i]); - } - } - // - // We need to restore the contents of the Midi Control Register. - // - writel(pm->u32MIDCR_Save, s->pBA0 + BA0_MIDCR); - - cs4281_ac97_resume(s); - // - // Restore the PCM Playback Left and Right Volume Control. - // - writel(pm->u32PPLVCvalue, s->pBA0 + BA0_PPLVC); - writel(pm->u32PPRVCvalue, s->pBA0 + BA0_PPRVC); - - // - // Restore the FM Synthesis Left and Right Volume Control. - // - writel(pm->u32FMLVCvalue, s->pBA0 + BA0_FMLVC); - writel(pm->u32FMRVCvalue, s->pBA0 + BA0_FMRVC); - - // - // Restore the JSCTL value. - // - writel(pm->u32JSCTLvalue, s->pBA0 + BA0_JSCTL); - - // - // Restore the GPIOR register value. - // - writel(pm->u32GPIORvalue, s->pBA0 + BA0_GPIOR); - - // - // Restore Sound System Control Register - // - writel(pm->u32SSCR, s->pBA0 + BA0_SSCR); - - // - // Restore SRC Slot Assignment register - // - writel(pm->u32SRCSA, s->pBA0 + BA0_SRCSA); - - // - // Restore sample rate - // - writel(pm->u32DacASR, s->pBA0 + BA0_PASR); - writel(pm->u32AdcASR, s->pBA0 + BA0_CASR); - writel(pm->u32DacSR, s->pBA0 + BA0_DACSR); - writel(pm->u32AdcSR, s->pBA0 + BA0_ADCSR); - - // - // Restore CFL1/2 registers we saved to compensate for OEM bugs. - // - // PokeBA0(BA0_CFLR, ulConfig); - - // - // Gershwin CLKRUN - Clear CKRA - // - writel(pm->u32CLKCR1_SAVE, s->pBA0 + BA0_CLKCR1); - - // - // Enable interrupts on the part. - // - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); - -#ifdef CSDEBUG - printpm(s); - printpipelines(s); -#endif -/* -* change the state, restore the current hwptrs, then stop the dac/adc -*/ - s->pm.flags |= CS4281_PM_IDLE; - s->pm.flags &= ~(CS4281_PM_SUSPENDING | CS4281_PM_SUSPENDED - | CS4281_PM_RESUMING | CS4281_PM_RESUMED); - - writel(s->pm.u32hwptr_playback, s->pBA0 + BA0_DCA0); - writel(s->pm.u32hwptr_capture, s->pBA0 + BA0_DCA1); - start_dac(s); - start_adc(s); - - CS_DBGOUT(CS_PM | CS_FUNCTION, 9, printk("cs4281: cs4281_resume()- flags=%d\n", - (unsigned)s->pm.flags)); - return 0; -} - -#endif - -//****************************************************************************** -// "cs4281_play_rate()" -- -//****************************************************************************** -static void cs4281_play_rate(struct cs4281_state *card, u32 playrate) -{ - u32 DACSRvalue = 1; - - // Based on the sample rate, program the DACSR register. - if (playrate == 8000) - DACSRvalue = 5; - if (playrate == 11025) - DACSRvalue = 4; - else if (playrate == 22050) - DACSRvalue = 2; - else if (playrate == 44100) - DACSRvalue = 1; - else if ((playrate <= 48000) && (playrate >= 6023)) - DACSRvalue = 24576000 / (playrate * 16); - else if (playrate < 6023) - // Not allowed by open. - return; - else if (playrate > 48000) - // Not allowed by open. - return; - CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 2, printk(KERN_INFO - "cs4281: cs4281_play_rate(): DACSRvalue=0x%.8x playrate=%d\n", - DACSRvalue, playrate)); - // Write the 'sample rate select code' - // to the 'DAC Sample Rate' register. - writel(DACSRvalue, card->pBA0 + BA0_DACSR); // (744h) -} - -//****************************************************************************** -// "cs4281_record_rate()" -- Initialize the record sample rate converter. -//****************************************************************************** -static void cs4281_record_rate(struct cs4281_state *card, u32 outrate) -{ - u32 ADCSRvalue = 1; - - // - // Based on the sample rate, program the ADCSR register - // - if (outrate == 8000) - ADCSRvalue = 5; - if (outrate == 11025) - ADCSRvalue = 4; - else if (outrate == 22050) - ADCSRvalue = 2; - else if (outrate == 44100) - ADCSRvalue = 1; - else if ((outrate <= 48000) && (outrate >= 6023)) - ADCSRvalue = 24576000 / (outrate * 16); - else if (outrate < 6023) { - // Not allowed by open. - return; - } else if (outrate > 48000) { - // Not allowed by open. - return; - } - CS_DBGOUT(CS_WAVE_READ | CS_PARMS, 2, printk(KERN_INFO - "cs4281: cs4281_record_rate(): ADCSRvalue=0x%.8x outrate=%d\n", - ADCSRvalue, outrate)); - // Write the 'sample rate select code - // to the 'ADC Sample Rate' register. - writel(ADCSRvalue, card->pBA0 + BA0_ADCSR); // (748h) -} - - - -static void stop_dac(struct cs4281_state *s) -{ - unsigned long flags; - unsigned temp1; - - CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4281: stop_dac():\n")); - spin_lock_irqsave(&s->lock, flags); - s->ena &= ~FMODE_WRITE; - temp1 = readl(s->pBA0 + BA0_DCR0) | DCRn_MSK; - writel(temp1, s->pBA0 + BA0_DCR0); - - spin_unlock_irqrestore(&s->lock, flags); -} - - -static void start_dac(struct cs4281_state *s) -{ - unsigned long flags; - unsigned temp1; - - CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4281: 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)) -#ifndef NOT_CS4281_PM - && (s->pm.flags & CS4281_PM_IDLE)) -#else -) -#endif - { - s->ena |= FMODE_WRITE; - temp1 = readl(s->pBA0 + BA0_DCR0) & ~DCRn_MSK; // Clear DMA0 channel mask. - writel(temp1, s->pBA0 + BA0_DCR0); // Start DMA'ing. - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. - - writel(7, s->pBA0 + BA0_PPRVC); - writel(7, s->pBA0 + BA0_PPLVC); - CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO - "cs4281: start_dac(): writel 0x%x start dma\n", temp1)); - - } - spin_unlock_irqrestore(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION, 3, - printk(KERN_INFO "cs4281: start_dac()-\n")); -} - - -static void stop_adc(struct cs4281_state *s) -{ - unsigned long flags; - unsigned temp1; - - CS_DBGOUT(CS_FUNCTION, 3, - printk(KERN_INFO "cs4281: 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; - } - temp1 = readl(s->pBA0 + BA0_DCR1) | DCRn_MSK; - writel(temp1, s->pBA0 + BA0_DCR1); - spin_unlock_irqrestore(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION, 3, - printk(KERN_INFO "cs4281: stop_adc()-\n")); -} - - -static void start_adc(struct cs4281_state *s) -{ - unsigned long flags; - unsigned temp1; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: start_adc()+\n")); - - if (!(s->ena & FMODE_READ) && - (s->dma_adc.mapped || s->dma_adc.count <= - (signed) (s->dma_adc.dmasize - 2 * s->dma_adc.fragsize)) - && s->dma_adc.ready -#ifndef NOT_CS4281_PM - && (s->pm.flags & CS4281_PM_IDLE)) -#else -) -#endif - { - 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); - - if (prog_dmabuf_adc(s) != 0) { - CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO - "cs4281: start_adc(): error in prog_dmabuf_adc\n")); - } - s->conversion = 1; - } - spin_lock_irqsave(&s->lock, flags); - s->ena |= FMODE_READ; - temp1 = readl(s->pBA0 + BA0_DCR1) & ~DCRn_MSK; // Clear DMA1 channel mask bit. - writel(temp1, s->pBA0 + BA0_DCR1); // Start recording - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. - spin_unlock_irqrestore(&s->lock, flags); - - CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO - "cs4281: start_adc(): writel 0x%x \n", temp1)); - } - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: start_adc()-\n")); - -} - - -// --------------------------------------------------------------------- - -#define DMABUF_MINORDER 1 // ==> min buffer size = 8K. - - -extern void dealloc_dmabuf(struct cs4281_state *s, struct dmabuf *db) -{ - struct page *map, *mapend; - - if (db->rawbuf) { - // Undo prog_dmabuf()'s marking the pages as reserved - mapend = - virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - - 1); - for (map = virt_to_page(db->rawbuf); map <= mapend; map++) - cs4x_mem_map_unreserve(map); - free_dmabuf(s, db); - } - if (s->tmpbuff && (db->type == CS_TYPE_ADC)) { - // Undo prog_dmabuf()'s marking the pages as reserved - mapend = - virt_to_page(s->tmpbuff + - (PAGE_SIZE << s->buforder_tmpbuff) - 1); - for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) - cs4x_mem_map_unreserve(map); - free_dmabuf2(s, db); - } - s->tmpbuff = NULL; - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - -static int prog_dmabuf(struct cs4281_state *s, struct dmabuf *db) -{ - int order; - unsigned bytespersec, temp1; - unsigned bufs, sample_shift = 0; - struct page *map, *mapend; - unsigned long df; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: prog_dmabuf()+\n")); - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = - db->endcleared = db->blocks = db->wakeup = db->underrun = 0; -/* -* check for order within limits, but do not overwrite value, check -* later for a fractional defaultorder (i.e. 100+). -*/ - if((defaultorder > 0) && (defaultorder < 12)) - df = defaultorder; - else - df = 1; - - if (!db->rawbuf) { - db->ready = db->mapped = 0; - for (order = df; order >= DMABUF_MINORDER; order--) - if ( (db->rawbuf = (void *) pci_alloc_consistent( - s->pcidev, PAGE_SIZE << order, &db-> dmaaddr))) - break; - if (!db->rawbuf) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: prog_dmabuf(): unable to allocate rawbuf\n")); - return -ENOMEM; - } - db->buforder = order; - // Now mark the pages as reserved; otherwise the - // remap_page_range() in cs4281_mmap doesn't work. - // 1. get index to last page in mem_map array for rawbuf. - mapend = virt_to_page(db->rawbuf + - (PAGE_SIZE << db->buforder) - 1); - - // 2. mark each physical page in range as 'reserved'. - for (map = virt_to_page(db->rawbuf); map <= mapend; map++) - cs4x_mem_map_reserve(map); - } - if (!s->tmpbuff && (db->type == CS_TYPE_ADC)) { - for (order = df; order >= DMABUF_MINORDER; - order--) - if ( (s->tmpbuff = (void *) pci_alloc_consistent( - s->pcidev, PAGE_SIZE << order, - &s->dmaaddr_tmpbuff))) - break; - if (!s->tmpbuff) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: prog_dmabuf(): unable to allocate tmpbuff\n")); - return -ENOMEM; - } - s->buforder_tmpbuff = order; - // Now mark the pages as reserved; otherwise the - // remap_page_range() in cs4281_mmap doesn't work. - // 1. get index to last page in mem_map array for rawbuf. - mapend = virt_to_page(s->tmpbuff + - (PAGE_SIZE << s->buforder_tmpbuff) - 1); - - // 2. mark each physical page in range as 'reserved'. - for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) - cs4x_mem_map_reserve(map); - } - if (db->type == CS_TYPE_DAC) { - if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) - sample_shift++; - if (s->prop_dac.channels > 1) - sample_shift++; - bytespersec = s->prop_dac.rate << sample_shift; - } else // CS_TYPE_ADC - { - if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) - sample_shift++; - if (s->prop_adc.channels > 1) - sample_shift++; - bytespersec = s->prop_adc.rate << sample_shift; - } - bufs = PAGE_SIZE << db->buforder; - -/* -* 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. -*/ - if(defaultorder >= 100) - { - bufs = 1 << (defaultorder-100); - } - -#define INTERRUPT_RATE_MS 100 // Interrupt rate in milliseconds. - db->numfrag = 2; -/* -* Nominal frag size(bytes/interrupt) -*/ - temp1 = bytespersec / (1000 / INTERRUPT_RATE_MS); - db->fragshift = 8; // Min 256 bytes. - while (1 << db->fragshift < temp1) // Calc power of 2 frag size. - db->fragshift += 1; - db->fragsize = 1 << db->fragshift; - db->dmasize = db->fragsize * 2; - db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. - -// If the calculated size is larger than the allocated -// buffer, divide the allocated buffer into 2 fragments. - if (db->dmasize > bufs) { - - db->numfrag = 2; // Two fragments. - db->fragsize = bufs >> 1; // Each 1/2 the alloc'ed buffer. - db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. - db->dmasize = bufs; // Use all the alloc'ed buffer. - - db->fragshift = 0; // Calculate 'fragshift'. - temp1 = db->fragsize; // update_ptr() uses it - while ((temp1 >>= 1) > 1) // to calc 'total-bytes' - db->fragshift += 1; // returned in DSP_GETI/OPTR. - } - CS_DBGOUT(CS_PARMS, 3, printk(KERN_INFO - "cs4281: prog_dmabuf(): numfrag=%d fragsize=%d fragsamples=%d fragshift=%d bufs=%d fmt=0x%x ch=%d\n", - db->numfrag, db->fragsize, db->fragsamples, - db->fragshift, bufs, - (db->type == CS_TYPE_DAC) ? s->prop_dac.fmt : - s->prop_adc.fmt, - (db->type == CS_TYPE_DAC) ? s->prop_dac.channels : - s->prop_adc.channels)); - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: prog_dmabuf()-\n")); - return 0; -} - - -static int prog_dmabuf_adc(struct cs4281_state *s) -{ - unsigned long va; - unsigned count; - int c; - stop_adc(s); - s->dma_adc.type = CS_TYPE_ADC; - if ((c = prog_dmabuf(s, &s->dma_adc))) - return c; - - if (s->dma_adc.rawbuf) { - memset(s->dma_adc.rawbuf, - (s->prop_adc. - fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, - s->dma_adc.dmasize); - } - if (s->tmpbuff) { - memset(s->tmpbuff, - (s->prop_adc. - fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, - PAGE_SIZE << s->buforder_tmpbuff); - } - - va = virt_to_bus(s->dma_adc.rawbuf); - - count = s->dma_adc.dmasize; - - if (s->prop_adc. - fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) - count /= 2; // 16-bit. - - if (s->prop_adc.channels > 1) - count /= 2; // Assume stereo. - - CS_DBGOUT(CS_WAVE_READ, 3, printk(KERN_INFO - "cs4281: prog_dmabuf_adc(): count=%d va=0x%.8x\n", - count, (unsigned) va)); - - writel(va, s->pBA0 + BA0_DBA1); // Set buffer start address. - writel(count - 1, s->pBA0 + BA0_DBC1); // Set count. - s->dma_adc.ready = 1; - return 0; -} - - -static int prog_dmabuf_dac(struct cs4281_state *s) -{ - unsigned long va; - unsigned count; - int c; - stop_dac(s); - s->dma_dac.type = CS_TYPE_DAC; - if ((c = prog_dmabuf(s, &s->dma_dac))) - return c; - memset(s->dma_dac.rawbuf, - (s->prop_dac.fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, - s->dma_dac.dmasize); - - va = virt_to_bus(s->dma_dac.rawbuf); - - count = s->dma_dac.dmasize; - if (s->prop_dac. - fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) - count /= 2; // 16-bit. - - if (s->prop_dac.channels > 1) - count /= 2; // Assume stereo. - - writel(va, s->pBA0 + BA0_DBA0); // Set buffer start address. - writel(count - 1, s->pBA0 + BA0_DBC0); // Set count. - - CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO - "cs4281: prog_dmabuf_dac(): count=%d va=0x%.8x\n", - count, (unsigned) va)); - - 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 - "cs4281: clear_advance(): memset %d at 0x%.8x for %d size \n", - (unsigned)c, (unsigned)((char *) buf) + bptr, len)); - memset(((char *) buf) + bptr, c, len); -} - - - -// call with spinlock held! -static void cs4281_update_ptr(struct cs4281_state *s, int intflag) -{ - int diff; - unsigned hwptr, va; - - // update ADC pointer - if (s->ena & FMODE_READ) { - hwptr = readl(s->pBA0 + BA0_DCA1); // Read capture DMA address. - va = virt_to_bus(s->dma_adc.rawbuf); - hwptr -= (unsigned) va; - diff = - (s->dma_adc.dmasize + hwptr - - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count > s->dma_adc.dmasize) - s->dma_adc.count = s->dma_adc.dmasize; - if (s->dma_adc.mapped) { - if (s->dma_adc.count >= - (signed) s->dma_adc.fragsize) wake_up(&s-> - dma_adc. - wait); - } else { - if (s->dma_adc.count > 0) - wake_up(&s->dma_adc.wait); - } - CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", - (unsigned)s, s->dma_adc.hwptr, - s->dma_adc.total_bytes, s->dma_adc.count)); - } - // 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) { - hwptr = readl(s->pBA0 + BA0_DCA0); // Read play DMA address. - va = virt_to_bus(s->dma_dac.rawbuf); - hwptr -= (unsigned) va; - diff = (s->dma_dac.dmasize + hwptr - - s->dma_dac.hwptr) % s->dma_dac.dmasize; - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= s->dma_dac.fragsize) { - s->dma_dac.wakeup = 1; - wake_up(&s->dma_dac.wait); - if (s->dma_dac.count > s->dma_dac.dmasize) - s->dma_dac.count &= - s->dma_dac.dmasize - 1; - } - } else { - s->dma_dac.count -= diff; - if (s->dma_dac.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 - "cs4281: cs4281_update_ptr(): memset %d at 0x%.8x for %d size \n", - (unsigned)(s->prop_dac.fmt & - (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, - (unsigned)s->dma_dac.rawbuf, - s->dma_dac.dmasize)); - memset(s->dma_dac.rawbuf, - (s->prop_dac. - fmt & (AFMT_U8 | AFMT_U16_LE)) ? - 0x80 : 0, s->dma_dac.dmasize); - if (s->dma_dac.count < 0) { - s->dma_dac.underrun = 1; - s->dma_dac.count = 0; - CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): underrun\n")); - } - } else if (s->dma_dac.count <= - (signed) s->dma_dac.fragsize - && !s->dma_dac.endcleared) { - clear_advance(s->dma_dac.rawbuf, - s->dma_dac.dmasize, - s->dma_dac.swptr, - s->dma_dac.fragsize, - (s->prop_dac. - fmt & (AFMT_U8 | - AFMT_U16_LE)) ? 0x80 - : 0); - s->dma_dac.endcleared = 1; - } - if ( (s->dma_dac.count <= (signed) s->dma_dac.dmasize/2) || - intflag) - { - wake_up(&s->dma_dac.wait); - } - } - CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", - (unsigned) s, s->dma_dac.hwptr, - s->dma_dac.total_bytes, s->dma_dac.count)); - } -} - - -// --------------------------------------------------------------------- - -static void prog_codec(struct cs4281_state *s, unsigned type) -{ - unsigned long flags; - unsigned temp1, format; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: prog_codec()+ \n")); - - spin_lock_irqsave(&s->lock, flags); - if (type == CS_TYPE_ADC) { - temp1 = readl(s->pBA0 + BA0_DCR1); - writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR1); // Stop capture DMA, if active. - - // program sampling rates - // Note, for CS4281, capture & play rates can be set independently. - cs4281_record_rate(s, s->prop_adc.rate); - - // program ADC parameters - format = DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE; - if (s->prop_adc. - fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit - if (s->prop_adc.fmt & (AFMT_S16_BE | AFMT_U16_BE)) // Big-endian? - format |= DMRn_BEND; - if (s->prop_adc.fmt & (AFMT_U16_LE | AFMT_U16_BE)) - format |= DMRn_USIGN; // Unsigned. - } else - format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned - if (s->prop_adc.channels < 2) - format |= DMRn_MONO; - - writel(format, s->pBA0 + BA0_DMR1); - - CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO - "cs4281: prog_codec(): adc %s %s %s rate=%d DMR0 format=0x%.8x\n", - (format & DMRn_SIZE8) ? "8" : "16", - (format & DMRn_USIGN) ? "Unsigned" : "Signed", - (format & DMRn_MONO) ? "Mono" : "Stereo", - s->prop_adc.rate, format)); - - s->ena &= ~FMODE_READ; // not capturing data yet - } - - - if (type == CS_TYPE_DAC) { - temp1 = readl(s->pBA0 + BA0_DCR0); - writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR0); // Stop play DMA, if active. - - // program sampling rates - // Note, for CS4281, capture & play rates can be set independently. - cs4281_play_rate(s, s->prop_dac.rate); - - // program DAC parameters - format = DMRn_DMA | DMRn_AUTO | DMRn_TR_READ; - if (s->prop_dac. - fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit - if (s->prop_dac.fmt & (AFMT_S16_BE | AFMT_U16_BE)) - format |= DMRn_BEND; // Big Endian. - if (s->prop_dac.fmt & (AFMT_U16_LE | AFMT_U16_BE)) - format |= DMRn_USIGN; // Unsigned. - } else - format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned - - if (s->prop_dac.channels < 2) - format |= DMRn_MONO; - - writel(format, s->pBA0 + BA0_DMR0); - - - CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO - "cs4281: prog_codec(): dac %s %s %s rate=%d DMR0 format=0x%.8x\n", - (format & DMRn_SIZE8) ? "8" : "16", - (format & DMRn_USIGN) ? "Unsigned" : "Signed", - (format & DMRn_MONO) ? "Mono" : "Stereo", - s->prop_dac.rate, format)); - - s->ena &= ~FMODE_WRITE; // not capturing data yet - - } - spin_unlock_irqrestore(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: prog_codec()- \n")); -} - - -static int mixer_ioctl(struct cs4281_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[] = { - BA0_AC97_PCM_OUT_VOLUME, - BA0_AC97_AUX_VOLUME, - BA0_AC97_CD_VOLUME, - BA0_AC97_LINE_IN_VOLUME - }; - 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 - "cs4281: 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) || - (cmd == SOUND_MIXER_CS_APM)) - { - 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; -#ifndef NOT_CS4281_PM - case SOUND_MIXER_CS_APM: - if (get_user(val, (unsigned long *) arg)) - return -EFAULT; - if(val == CS_IOCTL_CMD_SUSPEND) - cs4281_suspend(s); - else if(val == CS_IOCTL_CMD_RESUME) - cs4281_resume(s); - else - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs4281: mixer_ioctl(): invalid APM cmd (%d)\n", - val)); - } - return 0; -#endif - default: - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs4281: mixer_ioctl(): ERROR unknown debug cmd\n")); - return 0; - } - } -#endif - - if (cmd == SOUND_MIXER_PRIVATE1) { - // enable/disable/query mixer preamp - if (get_user(val, (int *) arg)) - return -EFAULT; - if (val != -1) { - cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); - temp1 = val ? (temp1 | 0x40) : (temp1 & 0xffbf); - cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); - } - cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); - val = (temp1 & 0x40) ? 1 : 0; - return put_user(val, (int *) arg); - } - if (cmd == SOUND_MIXER_PRIVATE2) { - // enable/disable/query spatializer - if (get_user(val, (int *) arg)) - return -EFAULT; - if (val != -1) { - temp1 = (val & 0x3f) >> 2; - cs4281_write_ac97(s, BA0_AC97_3D_CONTROL, temp1); - cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, - &temp1); - cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, - temp1 | 0x2000); - } - cs4281_read_ac97(s, BA0_AC97_3D_CONTROL, &temp1); - return put_user((temp1 << 2) | 3, (int *) arg); - } - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - strncpy(info.id, "CS4281", sizeof(info.id)); - strncpy(info.name, "Crystal CS4281", 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, "CS4281", sizeof(info.id)); - strncpy(info.name, "Crystal CS4281", 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 - cs4281_read_ac97(s, BA0_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_SYNTH | - SOUND_MASK_CD | SOUND_MASK_LINE | - SOUND_MASK_LINE1 | SOUND_MASK_MIC | - SOUND_MASK_VOLUME | - SOUND_MASK_RECLEV | - SOUND_MASK_SPEAKER, (int *) arg); - - case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source - return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_CD | SOUND_MASK_VOLUME | - SOUND_MASK_LINE1, (int *) arg); - - case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo - return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | - SOUND_MASK_CD | SOUND_MASK_LINE | - SOUND_MASK_LINE1 | SOUND_MASK_MIC | - 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; - cs4281_write_ac97(s, - BA0_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; - - cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, temp1); - cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, 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; - cs4281_write_ac97(s, BA0_AC97_PC_BEEP_VOLUME, 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; - cs4281_write_ac97(s, BA0_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; - } - cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); - temp1 &= 0x40; // Isolate 20db gain bit. - if (rl < 3) { - temp1 |= 0x8000; - rl = 0; - } - rl = 31 - rl; // Convert volume to attenuation. - temp1 |= rl; - cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, 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 + BA0_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 + BA0_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 - "cs4281: 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; - cs4281_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 -#ifndef NOT_CS4281_PM - CS_DBGOUT(CS_PM, 9, printk(KERN_INFO - "write ac97 mixreg[%d]=0x%x mix.vol[]=0x%x\n", - vidx-1,temp1,s->mix.vol[vidx-1])); -#endif - return put_user(s->mix.vol[vidx - 1], (int *) arg); - } -} - - -// --------------------------------------------------------------------- - -static int cs4281_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct cs4281_state *s=NULL; - struct list_head *entry; - - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, - printk(KERN_INFO "cs4281: cs4281_open_mixdev()+\n")); - - list_for_each(entry, &cs4281_devs) - { - s = list_entry(entry, struct cs4281_state, list); - if(s->dev_mixer == minor) - break; - } - if (!s) - { - CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, - printk(KERN_INFO "cs4281: cs4281_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 "cs4281: cs4281_open_mixdev()- 0\n")); - - return 0; -} - - -static int cs4281_release_mixdev(struct inode *inode, struct file *file) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - - VALIDATE_STATE(s); - MOD_DEC_USE_COUNT; - return 0; -} - - -static int cs4281_ioctl_mixdev(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return mixer_ioctl((struct cs4281_state *) file->private_data, cmd, - arg); -} - - -// ****************************************************************************************** -// Mixer file operations struct. -// ****************************************************************************************** -static /*const */ struct file_operations cs4281_mixer_fops = { - llseek:no_llseek, - ioctl:cs4281_ioctl_mixdev, - open:cs4281_open_mixdev, - release:cs4281_release_mixdev, -}; - -// --------------------------------------------------------------------- - - -static int drain_adc(struct cs4281_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count; - unsigned tmo; - - if (s->dma_adc.mapped) - return 0; - add_wait_queue(&s->dma_adc.wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_adc.count; - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: drain_adc() %d\n", count)); - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) { - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: drain_adc() count<0\n")); - break; - } - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_adc.wait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - tmo = - 3 * HZ * (count + - s->dma_adc.fragsize) / 2 / s->prop_adc.rate; - if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) - tmo >>= 1; - if (s->prop_adc.channels > 1) - tmo >>= 1; - if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "cs4281: dma timed out??\n"); - } - remove_wait_queue(&s->dma_adc.wait, &wait); - current->state = TASK_RUNNING; - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -static int drain_dac(struct cs4281_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count; - unsigned tmo; - - if (s->dma_dac.mapped) - return 0; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - 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) { - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - tmo = - 3 * HZ * (count + - s->dma_dac.fragsize) / 2 / s->prop_dac.rate; - if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) - tmo >>= 1; - if (s->prop_dac.channels > 1) - tmo >>= 1; - if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "cs4281: dma timed out??\n"); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -//**************************************************************************** -// -// CopySamples copies 16-bit stereo samples from the source to the -// destination, possibly converting down to either 8-bit or mono or both. -// count specifies the number of output bytes to write. -// -// Arguments: -// -// dst - Pointer to a destination buffer. -// src - Pointer to a source buffer -// count - The number of bytes to copy into the destination buffer. -// iChannels - Stereo - 2 -// Mono - 1 -// fmt - AFMT_xxx (soundcard.h formats) -// -// NOTES: only call this routine for conversion to 8bit from 16bit -// -//**************************************************************************** -static void CopySamples(char *dst, char *src, int count, int iChannels, - unsigned fmt) -{ - - unsigned short *psSrc; - long lAudioSample; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: CopySamples()+ ")); - CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " dst=0x%x src=0x%x count=%d iChannels=%d fmt=0x%x\n", - (unsigned) dst, (unsigned) src, (unsigned) count, - (unsigned) iChannels, (unsigned) fmt)); - - // Gershwin does format conversion in hardware so normally - // we don't do any host based coversion. The data formatter - // truncates 16 bit data to 8 bit and that causes some hiss. - // We have already forced the HW to do 16 bit sampling and - // 2 channel so that we can use software to round instead - // of truncate - - // - // See if the data should be output as 8-bit unsigned stereo. - // or if the data should be output at 8-bit unsigned mono. - // - if ( ((iChannels == 2) && (fmt & AFMT_U8)) || - ((iChannels == 1) && (fmt & AFMT_U8)) ) { - // - // Convert each 16-bit unsigned stereo sample to 8-bit unsigned - // stereo using rounding. - // - psSrc = (unsigned short *) src; - count = count / 2; - while (count--) { - lAudioSample = (long) psSrc[count] + (long) 0x80; - if (lAudioSample > 0xffff) { - lAudioSample = 0xffff; - } - dst[count] = (char) (lAudioSample >> 8); - } - } - // - // check for 8-bit signed stereo. - // - else if ((iChannels == 2) && (fmt & AFMT_S8)) { - // - // Convert each 16-bit stereo sample to 8-bit stereo using rounding. - // - psSrc = (short *) src; - while (count--) { - lAudioSample = - (((long) psSrc[0] + (long) psSrc[1]) / 2); - psSrc += 2; - *dst++ = (char) ((short) lAudioSample >> 8); - } - } - // - // Otherwise, the data should be output as 8-bit signed mono. - // - else if ((iChannels == 1) && (fmt & AFMT_S8)) { - // - // Convert each 16-bit signed mono sample to 8-bit signed mono - // using rounding. - // - psSrc = (short *) src; - count = count / 2; - while (count--) { - lAudioSample = - (((long) psSrc[0] + (long) psSrc[1]) / 2); - if (lAudioSample > 0x7fff) { - lAudioSample = 0x7fff; - } - psSrc += 2; - *dst++ = (char) ((short) lAudioSample >> 8); - } - } -} - -// -// cs_copy_to_user() -// replacement for the standard copy_to_user, to allow for a conversion from -// 16 bit to 8 bit if the record conversion is active. the cs4281 has some -// issues with 8 bit capture, so the driver always captures data in 16 bit -// and then if the user requested 8 bit, converts from 16 to 8 bit. -// -static unsigned cs_copy_to_user(struct cs4281_state *s, void *dest, - unsigned *hwsrc, unsigned cnt, - unsigned *copied) -{ - void *src = hwsrc; //default to the standard destination buffer addr - - CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO - "cs_copy_to_user()+ fmt=0x%x fmt_o=0x%x cnt=%d dest=0x%.8x\n", - s->prop_adc.fmt, s->prop_adc.fmt_original, - (unsigned) cnt, (unsigned) dest)); - - if (cnt > s->dma_adc.dmasize) { - cnt = s->dma_adc.dmasize; - } - if (!cnt) { - *copied = 0; - return 0; - } - if (s->conversion) { - if (!s->tmpbuff) { - *copied = cnt / 2; - return 0; - } - CopySamples(s->tmpbuff, (void *) hwsrc, cnt, - (unsigned) s->prop_adc.channels, - s->prop_adc.fmt_original); - src = s->tmpbuff; - cnt = cnt / 2; - } - - if (copy_to_user(dest, src, cnt)) { - *copied = 0; - return -EFAULT; - } - *copied = cnt; - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: cs_copy_to_user()- copied bytes is %d \n", cnt)); - return 0; -} - -// --------------------------------------------------------------------- - -static ssize_t cs4281_read(struct file *file, char *buffer, size_t count, - loff_t * ppos) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - unsigned copied = 0; - - CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, - printk(KERN_INFO "cs4281: cs4281_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); - - // get the current copy point of the sw buffer - swptr = s->dma_adc.swptr; - - // cnt is the amount of unread bytes from the end of the - // hw buffer to the current sw pointer - cnt = s->dma_adc.dmasize - swptr; - - // 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 (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - 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 > (count * 2)) - cnt = (count * 2); - } else { - if (cnt > count) - cnt = 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 rawbuf+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, count)); - CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " .dmasize=%d .count=%d buffer=0x%.8x ret=%d\n", - s->dma_adc.dmasize, s->dma_adc.count, - (unsigned) buffer, ret)); - - if (cs_copy_to_user - (s, buffer, s->dma_adc.rawbuf + swptr, cnt, &copied)) - return ret ? ret : -EFAULT; - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - 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 "cs4281: cs4281_read()- %d\n", ret)); - return ret; -} - - -static ssize_t cs4281_write(struct file *file, const char *buffer, - size_t count, loff_t * ppos) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr, hwptr, busaddr; - int cnt; - - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, - printk(KERN_INFO "cs4281: cs4281_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) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - if (s->dma_dac.underrun) { - s->dma_dac.underrun = 0; - hwptr = readl(s->pBA0 + BA0_DCA0); - busaddr = virt_to_bus(s->dma_dac.rawbuf); - hwptr -= (unsigned) busaddr; - s->dma_dac.swptr = s->dma_dac.hwptr = hwptr; - } - swptr = s->dma_dac.swptr; - cnt = s->dma_dac.dmasize - swptr; - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.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(&s->dma_dac.wait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) - return ret ? ret : -EFAULT; - swptr = (swptr + cnt) % s->dma_dac.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - s->dma_dac.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 "cs4281: cs4281_write()- %d\n", ret)); - return ret; -} - - -static unsigned int cs4281_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cs4281_state *s = - (struct cs4281_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 "cs4281: cs4281_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 - "cs4281: cs4281_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 - "cs4281: cs4281_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); - cs4281_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.dmasize/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 "cs4281: cs4281_poll()- 0x%.8x\n", - mask)); - return mask; -} - - -static int cs4281_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - struct dmabuf *db; - int ret; - unsigned long size; - - CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, - printk(KERN_INFO "cs4281: cs4281_mmap()+\n")); - - VALIDATE_STATE(s); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf_dac(s)) != 0) - return ret; - db = &s->dma_dac; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf_adc(s)) != 0) - return ret; - db = &s->dma_adc; - } else - return -EINVAL; -// -// only support PLAYBACK for now -// - db = &s->dma_dac; - - if (cs4x_pgoff(vma) != 0) - return -EINVAL; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) - return -EINVAL; - if (remap_page_range - (vma, vma->vm_start, virt_to_phys(db->rawbuf), size, - vma->vm_page_prot)) return -EAGAIN; - db->mapped = 1; - - CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, - printk(KERN_INFO "cs4281: cs4281_mmap()- 0 size=%d\n", - (unsigned) size)); - - return 0; -} - - -static int cs4281_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret; - - CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO - "cs4281: cs4281_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 - "cs4281: cs4281_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 - "cs4281: cs4281_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 - "cs4281: cs4281_ioctl(): DSP_RESET\n")); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(); - s->dma_dac.swptr = s->dma_dac.hwptr = - s->dma_dac.count = s->dma_dac.total_bytes = - s->dma_dac.blocks = s->dma_dac.wakeup = 0; - prog_codec(s, CS_TYPE_DAC); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(); - s->dma_adc.swptr = s->dma_adc.hwptr = - s->dma_adc.count = s->dma_adc.total_bytes = - s->dma_adc.blocks = s->dma_dac.wakeup = 0; - prog_codec(s, CS_TYPE_ADC); - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *) arg)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): DSP_SPEED val=%d\n", val)); - // - // support independent capture and playback channels - // assume that the file mode bit determines the - // direction of the data flow. - // - if (file->f_mode & FMODE_READ) { - if (val >= 0) { - stop_adc(s); - s->dma_adc.ready = 0; - // program sampling rates - if (val > 48000) - val = 48000; - if (val < 6300) - val = 6300; - s->prop_adc.rate = val; - prog_codec(s, CS_TYPE_ADC); - } - } - if (file->f_mode & FMODE_WRITE) { - if (val >= 0) { - stop_dac(s); - s->dma_dac.ready = 0; - // program sampling rates - if (val > 48000) - val = 48000; - if (val < 6300) - val = 6300; - s->prop_dac.rate = val; - prog_codec(s, CS_TYPE_DAC); - } - } - - if (file->f_mode & FMODE_WRITE) - val = s->prop_dac.rate; - else if (file->f_mode & FMODE_READ) - val = s->prop_adc.rate; - - 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 - "cs4281: cs4281_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; - prog_codec(s, CS_TYPE_ADC); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - s->prop_dac.channels = val ? 2 : 1; - prog_codec(s, CS_TYPE_DAC); - } - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *) arg)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_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; - prog_codec(s, CS_TYPE_ADC); - } - 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; - prog_codec(s, CS_TYPE_DAC); - } - } - - 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 - "cs4281: cs4281_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 - "cs4281: cs4281_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; - prog_codec(s, CS_TYPE_ADC); - } - 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; - prog_codec(s, CS_TYPE_DAC); - } - } 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 - "cs4281: cs4281_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 - "cs4281: cs4281_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); - cs4281_update_ptr(s,CS_FALSE); - abinfo.fragsize = s->dma_dac.fragsize; - if (s->dma_dac.mapped) - abinfo.bytes = s->dma_dac.dmasize; - else - abinfo.bytes = - s->dma_dac.dmasize - 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 - "cs4281: cs4281_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); - cs4281_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); - cs4281_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); - cs4281_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); - cs4281_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 cs4281_release(struct inode *inode, struct file *file) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - - CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO - "cs4281: cs4281_release(): inode=0x%.8x file=0x%.8x f_mode=%d\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 cs4281_open(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct cs4281_state *s=NULL; - struct list_head *entry; - - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs4281: cs4281_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n", - (unsigned) inode, (unsigned) file, file->f_mode)); - - list_for_each(entry, &cs4281_devs) - { - s = list_entry(entry, struct cs4281_state, list); - - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - if (entry == &cs4281_devs) - return -ENODEV; - if (!s) { - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs4281: cs4281_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 - "cs4281: cs4281_open(): Error - must open READ and/or WRITE\n")); - return -ENODEV; - } - if (file->f_mode & FMODE_WRITE) { - 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)) - 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)) - 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_U8; - s->prop_adc.fmt_original = s->prop_adc.fmt; - s->prop_adc.channels = 1; - s->prop_adc.rate = 8000; - s->prop_adc.clkdiv = 96 | 0x80; - 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 - "cs4281: adc Program dmabufs failed.\n")); - cs4281_release(inode, file); - return -ENOMEM; - } - prog_codec(s, CS_TYPE_ADC); - } - if (file->f_mode & FMODE_WRITE) { - s->prop_dac.fmt = AFMT_U8; - s->prop_dac.fmt_original = s->prop_dac.fmt; - s->prop_dac.channels = 1; - s->prop_dac.rate = 8000; - s->prop_dac.clkdiv = 96 | 0x80; - 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 - "cs4281: dac Program dmabufs failed.\n")); - cs4281_release(inode, file); - return -ENOMEM; - } - prog_codec(s, CS_TYPE_DAC); - } - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, - printk(KERN_INFO "cs4281: cs4281_open()- 0\n")); - return 0; -} - - -// ****************************************************************************************** -// Wave (audio) file operations struct. -// ****************************************************************************************** -static /*const */ struct file_operations cs4281_audio_fops = { - llseek:no_llseek, - read:cs4281_read, - write:cs4281_write, - poll:cs4281_poll, - ioctl:cs4281_ioctl, - mmap:cs4281_mmap, - open:cs4281_open, - release:cs4281_release, -}; - -// --------------------------------------------------------------------- - -// hold spinlock for the following! -static void cs4281_handle_midi(struct cs4281_state *s) -{ - unsigned char ch; - int wake; - unsigned temp1; - - wake = 0; - while (!(readl(s->pBA0 + BA0_MIDSR) & 0x80)) { - ch = readl(s->pBA0 + BA0_MIDRP); - if (s->midi.icnt < MIDIINBUF) { - s->midi.ibuf[s->midi.iwr] = ch; - s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; - s->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&s->midi.iwait); - wake = 0; - while (!(readl(s->pBA0 + BA0_MIDSR) & 0x40) && s->midi.ocnt > 0) { - temp1 = (s->midi.obuf[s->midi.ord]) & 0x000000ff; - writel(temp1, s->pBA0 + BA0_MIDWP); - s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; - s->midi.ocnt--; - if (s->midi.ocnt < MIDIOUTBUF - 16) - wake = 1; - } - if (wake) - wake_up(&s->midi.owait); -} - - - -static void cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct cs4281_state *s = (struct cs4281_state *) dev_id; - unsigned int temp1; - - // fastpath out, to ease interrupt sharing - temp1 = readl(s->pBA0 + BA0_HISR); // Get Int Status reg. - - CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO - "cs4281: cs4281_interrupt() BA0_HISR=0x%.8x\n", temp1)); -/* -* If not DMA or MIDI interrupt, then just return. -*/ - if (!(temp1 & (HISR_DMA0 | HISR_DMA1 | HISR_MIDI))) { - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); - CS_DBGOUT(CS_INTERRUPT, 9, printk(KERN_INFO - "cs4281: cs4281_interrupt(): returning not cs4281 interrupt.\n")); - return; - } - - if (temp1 & HISR_DMA0) // If play interrupt, - readl(s->pBA0 + BA0_HDSR0); // clear the source. - - if (temp1 & HISR_DMA1) // Same for play. - readl(s->pBA0 + BA0_HDSR1); - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Local EOI - - spin_lock(&s->lock); - cs4281_update_ptr(s,CS_TRUE); - cs4281_handle_midi(s); - spin_unlock(&s->lock); -} - -// ************************************************************************** - -static void cs4281_midi_timer(unsigned long data) -{ - struct cs4281_state *s = (struct cs4281_state *) data; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - cs4281_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - s->midi.timer.expires = jiffies + 1; - add_timer(&s->midi.timer); -} - - -// --------------------------------------------------------------------- - -static ssize_t cs4281_midi_read(struct file *file, char *buffer, - size_t count, loff_t * ppos) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.ird; - cnt = MIDIINBUF - ptr; - if (s->midi.icnt < cnt) - cnt = s->midi.icnt; - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; - ptr = (ptr + cnt) % MIDIINBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.ird = ptr; - s->midi.icnt -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - } - return ret; -} - - -static ssize_t cs4281_midi_write(struct file *file, const char *buffer, - size_t count, loff_t * ppos) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.owr; - cnt = MIDIOUTBUF - ptr; - if (s->midi.ocnt + cnt > MIDIOUTBUF) - cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) - cs4281_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; - ptr = (ptr + cnt) % MIDIOUTBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.owr = ptr; - s->midi.ocnt += cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&s->lock, flags); - cs4281_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - } - return ret; -} - - -static unsigned int cs4281_midi_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_flags & FMODE_WRITE) - poll_wait(file, &s->midi.owait, wait); - if (file->f_flags & FMODE_READ) - poll_wait(file, &s->midi.iwait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_flags & FMODE_READ) { - if (s->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_flags & FMODE_WRITE) { - if (s->midi.ocnt < MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - - -static int cs4281_midi_open(struct inode *inode, struct file *file) -{ - unsigned long flags, temp1; - unsigned int minor = minor(inode->i_rdev); - struct cs4281_state *s=NULL; - struct list_head *entry; - list_for_each(entry, &cs4281_devs) - { - s = list_entry(entry, struct cs4281_state, list); - - if (s->dev_midi == minor) - break; - } - - if (entry == &cs4281_devs) - return -ENODEV; - if (!s) - { - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs4281: cs4281_open(): Error - unable to find audio state struct\n")); - return -ENODEV; - } - VALIDATE_STATE(s); - file->private_data = s; - // wait for device to become free - down(&s->open_sem); - while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - if (file->f_flags & O_NONBLOCK) { - up(&s->open_sem); - return -EBUSY; - } - up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - down(&s->open_sem); - } - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - writel(1, s->pBA0 + BA0_MIDCR); // Reset the interface. - writel(0, s->pBA0 + BA0_MIDCR); // Return to normal mode. - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - writel(0x0000000f, s->pBA0 + BA0_MIDCR); // Enable transmit, record, ints. - temp1 = readl(s->pBA0 + BA0_HIMR); - writel(temp1 & 0xffbfffff, s->pBA0 + BA0_HIMR); // Enable midi int. recognition. - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts - init_timer(&s->midi.timer); - s->midi.timer.expires = jiffies + 1; - s->midi.timer.data = (unsigned long) s; - s->midi.timer.function = cs4281_midi_timer; - add_timer(&s->midi.timer); - } - if (file->f_mode & FMODE_READ) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - } - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= - (file-> - f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | - FMODE_MIDI_WRITE); - up(&s->open_sem); - MOD_INC_USE_COUNT; - return 0; -} - - -static int cs4281_midi_release(struct inode *inode, struct file *file) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - VALIDATE_STATE(s); - - if (file->f_mode & FMODE_WRITE) { - add_wait_queue(&s->midi.owait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->midi.ocnt; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG - "cs4281: midi timed out??\n"); - } - remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; - } - down(&s->open_sem); - s->open_mode &= - (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ | - FMODE_MIDI_WRITE); - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - writel(0, s->pBA0 + BA0_MIDCR); // Disable Midi interrupts. - del_timer(&s->midi.timer); - } - spin_unlock_irqrestore(&s->lock, flags); - up(&s->open_sem); - wake_up(&s->open_wait); - MOD_DEC_USE_COUNT; - return 0; -} - -// ****************************************************************************************** -// Midi file operations struct. -// ****************************************************************************************** -static /*const */ struct file_operations cs4281_midi_fops = { - llseek:no_llseek, - read:cs4281_midi_read, - write:cs4281_midi_write, - poll:cs4281_midi_poll, - open:cs4281_midi_open, - release:cs4281_midi_release, -}; - - -// --------------------------------------------------------------------- - -// maximum number of devices -#define NR_DEVICE 8 // Only eight devices supported currently. - -// --------------------------------------------------------------------- - -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} -}; - - -#ifndef NOT_CS4281_PM -void __devinit cs4281_BuildFIFO( - struct cs4281_pipeline *p, - struct cs4281_state *s) -{ - switch(p->number) - { - case 0: /* playback */ - { - p->u32FCRnAddress = BA0_FCR0; - p->u32FSICnAddress = BA0_FSIC0; - p->u32FPDRnAddress = BA0_FPDR0; - break; - } - case 1: /* capture */ - { - p->u32FCRnAddress = BA0_FCR1; - p->u32FSICnAddress = BA0_FSIC1; - p->u32FPDRnAddress = BA0_FPDR1; - break; - } - - case 2: - { - p->u32FCRnAddress = BA0_FCR2; - p->u32FSICnAddress = BA0_FSIC2; - p->u32FPDRnAddress = BA0_FPDR2; - break; - } - case 3: - { - p->u32FCRnAddress = BA0_FCR3; - p->u32FSICnAddress = BA0_FSIC3; - p->u32FPDRnAddress = BA0_FPDR3; - break; - } - default: - break; - } - // - // first read the hardware to initialize the member variables - // - p->u32FCRnValue = readl(s->pBA0 + p->u32FCRnAddress); - p->u32FSICnValue = readl(s->pBA0 + p->u32FSICnAddress); - p->u32FPDRnValue = readl(s->pBA0 + p->u32FPDRnAddress); - -} - -void __devinit cs4281_BuildDMAengine( - struct cs4281_pipeline *p, - struct cs4281_state *s) -{ -/* -* initialize all the addresses of this pipeline dma info. -*/ - switch(p->number) - { - case 0: /* playback */ - { - p->u32DBAnAddress = BA0_DBA0; - p->u32DCAnAddress = BA0_DCA0; - p->u32DBCnAddress = BA0_DBC0; - p->u32DCCnAddress = BA0_DCC0; - p->u32DMRnAddress = BA0_DMR0; - p->u32DCRnAddress = BA0_DCR0; - p->u32HDSRnAddress = BA0_HDSR0; - break; - } - - case 1: /* capture */ - { - p->u32DBAnAddress = BA0_DBA1; - p->u32DCAnAddress = BA0_DCA1; - p->u32DBCnAddress = BA0_DBC1; - p->u32DCCnAddress = BA0_DCC1; - p->u32DMRnAddress = BA0_DMR1; - p->u32DCRnAddress = BA0_DCR1; - p->u32HDSRnAddress = BA0_HDSR1; - break; - } - - case 2: - { - p->u32DBAnAddress = BA0_DBA2; - p->u32DCAnAddress = BA0_DCA2; - p->u32DBCnAddress = BA0_DBC2; - p->u32DCCnAddress = BA0_DCC2; - p->u32DMRnAddress = BA0_DMR2; - p->u32DCRnAddress = BA0_DCR2; - p->u32HDSRnAddress = BA0_HDSR2; - break; - } - - case 3: - { - p->u32DBAnAddress = BA0_DBA3; - p->u32DCAnAddress = BA0_DCA3; - p->u32DBCnAddress = BA0_DBC3; - p->u32DCCnAddress = BA0_DCC3; - p->u32DMRnAddress = BA0_DMR3; - p->u32DCRnAddress = BA0_DCR3; - p->u32HDSRnAddress = BA0_HDSR3; - break; - } - default: - break; - } - -// -// Initialize the dma values for this pipeline -// - p->u32DBAnValue = readl(s->pBA0 + p->u32DBAnAddress); - p->u32DBCnValue = readl(s->pBA0 + p->u32DBCnAddress); - p->u32DMRnValue = readl(s->pBA0 + p->u32DMRnAddress); - p->u32DCRnValue = readl(s->pBA0 + p->u32DCRnAddress); - -} - -void __devinit cs4281_InitPM(struct cs4281_state *s) -{ - int i; - struct cs4281_pipeline *p; - - for(i=0;ipl[i]; - p->number = i; - cs4281_BuildDMAengine(p,s); - cs4281_BuildFIFO(p,s); - /* - * currently only 2 pipelines are used - * so, only set the valid bit on the playback and capture. - */ - if( (i == CS4281_PLAYBACK_PIPELINE_NUMBER) || - (i == CS4281_CAPTURE_PIPELINE_NUMBER)) - p->flags |= CS4281_PIPELINE_VALID; - } - s->pm.u32SSPM_BITS = 0x7e; /* rev c, use 0x7c for rev a or b */ -} -#endif - -static int __devinit cs4281_probe(struct pci_dev *pcidev, - const struct pci_device_id *pciid) -{ -#ifndef NOT_CS4281_PM - struct pm_dev *pmdev; -#endif - struct cs4281_state *s; - dma_addr_t dma_mask; - mm_segment_t fs; - int i, val; - unsigned int temp1, temp2; - - CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, - printk(KERN_INFO "cs4281: probe()+\n")); - - if (pci_enable_device(pcidev)) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4281: pci_enable_device() failed\n")); - return -1; - } - if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM) || - !(pci_resource_flags(pcidev, 1) & IORESOURCE_MEM)) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe()- Memory region not assigned\n")); - return -ENODEV; - } - if (pcidev->irq == 0) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() IRQ not assigned\n")); - return -ENODEV; - } - dma_mask = 0xffffffff; /* this enables playback and recording */ - i = pci_set_dma_mask(pcidev, dma_mask); - if (i) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() architecture does not support 32bit PCI busmaster DMA\n")); - return i; - } - if (!(s = kmalloc(sizeof(struct cs4281_state), GFP_KERNEL))) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() no memory for state struct.\n")); - return -1; - } - memset(s, 0, sizeof(struct cs4281_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->open_wait_adc); - init_waitqueue_head(&s->open_wait_dac); - init_waitqueue_head(&s->midi.iwait); - init_waitqueue_head(&s->midi.owait); - init_MUTEX(&s->open_sem); - init_MUTEX(&s->open_sem_adc); - init_MUTEX(&s->open_sem_dac); - spin_lock_init(&s->lock); - s->pBA0phys = pci_resource_start(pcidev, 0); - s->pBA1phys = pci_resource_start(pcidev, 1); - - /* Convert phys to linear. */ - s->pBA0 = ioremap_nocache(s->pBA0phys, 4096); - if (!s->pBA0) { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR - "cs4281: BA0 I/O mapping failed. Skipping part.\n")); - goto err_free; - } - s->pBA1 = ioremap_nocache(s->pBA1phys, 65536); - if (!s->pBA1) { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR - "cs4281: BA1 I/O mapping failed. Skipping part.\n")); - goto err_unmap; - } - - temp1 = readl(s->pBA0 + BA0_PCICFG00); - temp2 = readl(s->pBA0 + BA0_PCICFG04); - - CS_DBGOUT(CS_INIT, 2, - printk(KERN_INFO - "cs4281: probe() BA0=0x%.8x BA1=0x%.8x pBA0=0x%.8x pBA1=0x%.8x \n", - (unsigned) temp1, (unsigned) temp2, - (unsigned) s->pBA0, (unsigned) s->pBA1)); - - CS_DBGOUT(CS_INIT, 2, - printk(KERN_INFO - "cs4281: probe() pBA0phys=0x%.8x pBA1phys=0x%.8x\n", - (unsigned) s->pBA0phys, (unsigned) s->pBA1phys)); - -#ifndef NOT_CS4281_PM - s->pm.flags = CS4281_PM_IDLE; -#endif - temp1 = cs4281_hw_init(s); - if (temp1) { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR - "cs4281: cs4281_hw_init() failed. Skipping part.\n")); - goto err_irq; - } - s->magic = CS4281_MAGIC; - s->pcidev = pcidev; - s->irq = pcidev->irq; - if (request_irq - (s->irq, cs4281_interrupt, SA_SHIRQ, "Crystal CS4281", s)) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, - printk(KERN_ERR "cs4281: irq %u in use\n", s->irq)); - goto err_irq; - } - if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1)) < - 0) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() register_sound_dsp() failed.\n")); - goto err_dev1; - } - if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1)) < - 0) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() register_sound_mixer() failed.\n")); - goto err_dev2; - } - if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1)) < 0) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() register_sound_midi() failed.\n")); - goto err_dev3; - } -#ifndef NOT_CS4281_PM - cs4281_InitPM(s); - pmdev = cs_pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), cs4281_pm_callback); - if (pmdev) - { - CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO - "cs4281: probe() pm_register() succeeded (0x%x).\n", - (unsigned)pmdev)); - pmdev->data = s; - } - else - { - CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 0, printk(KERN_INFO - "cs4281: probe() pm_register() failed (0x%x).\n", - (unsigned)pmdev)); - s->pm.flags |= CS4281_PM_NOT_REGISTERED; - } -#endif - - pci_set_master(pcidev); // enable bus mastering - - fs = get_fs(); - set_fs(KERNEL_DS); - 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); - } - val = 1; // enable mic preamp - mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long) &val); - set_fs(fs); - - pci_set_drvdata(pcidev, s); - list_add(&s->list, &cs4281_devs); - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: probe()- device allocated successfully\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: - iounmap(s->pBA1); - err_unmap: - iounmap(s->pBA0); - err_free: - kfree(s); - - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO - "cs4281: probe()- no device allocated\n")); - return -ENODEV; -} // probe_cs4281 - - -// --------------------------------------------------------------------- - -static void __devinit cs4281_remove(struct pci_dev *pci_dev) -{ - struct cs4281_state *s = pci_get_drvdata(pci_dev); - // stop DMA controller - synchronize_irq(); - free_irq(s->irq, s); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->dev_mixer); - unregister_sound_midi(s->dev_midi); - iounmap(s->pBA1); - iounmap(s->pBA0); - pci_set_drvdata(pci_dev,NULL); - list_del(&s->list); - kfree(s); - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: cs4281_remove()-: remove successful\n")); -} - -static struct pci_device_id cs4281_pci_tbl[] __devinitdata = { - {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CRYSTAL_CS4281, - PCI_ANY_ID, PCI_ANY_ID, 0, 0}, - {0,} -}; - -MODULE_DEVICE_TABLE(pci, cs4281_pci_tbl); - -struct pci_driver cs4281_pci_driver = { - name:"cs4281", - id_table:cs4281_pci_tbl, - probe:cs4281_probe, - remove:cs4281_remove, - suspend:CS4281_SUSPEND_TBL, - resume:CS4281_RESUME_TBL, -}; - -int __init cs4281_init_module(void) -{ - int rtn = 0; - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: cs4281_init_module()+ \n")); - if (!pci_present()) { /* No PCI bus in this machine! */ - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: cs4281_init_module()- no pci bus found\n")); - return -ENODEV; - } - printk(KERN_INFO "cs4281: version v%d.%02d.%d time " __TIME__ " " - __DATE__ "\n", CS4281_MAJOR_VERSION, CS4281_MINOR_VERSION, - CS4281_ARCH); - rtn = pci_module_init(&cs4281_pci_driver); - - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cs4281_init_module()- (%d)\n",rtn)); - return rtn; -} - -void __exit cs4281_cleanup_module(void) -{ - pci_unregister_driver(&cs4281_pci_driver); -#ifndef NOT_CS4281_PM - cs_pm_unregister_all(cs4281_pm_callback); -#endif - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cleanup_cs4281() finished\n")); -} -// --------------------------------------------------------------------- - -MODULE_AUTHOR("gw boynton, audio@crystal.cirrus.com"); -MODULE_DESCRIPTION("Cirrus Logic CS4281 Driver"); -MODULE_LICENSE("GPL"); - -// --------------------------------------------------------------------- - -module_init(cs4281_init_module); -module_exit(cs4281_cleanup_module); - -#ifndef MODULE -int __init init_cs4281(void) -{ - return cs4281_init_module(); -} -#endif diff -Nru a/drivers/sound/cs4281/cs4281pm-24.c b/drivers/sound/cs4281/cs4281pm-24.c --- a/drivers/sound/cs4281/cs4281pm-24.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,84 +0,0 @@ -/******************************************************************************* -* -* "cs4281pm.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. -* -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- tom woller (twoller@crystal.cirrus.com) or -* (audio@crystal.cirrus.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. -* -* 12/22/00 trw - new file. -* -*******************************************************************************/ - -#ifndef NOT_CS4281_PM -#include - -#define cs_pm_register(a, b, c) pm_register((a), (b), (c)); -#define cs_pm_unregister_all(a) pm_unregister_all((a)); - -int cs4281_suspend(struct cs4281_state *s); -int cs4281_resume(struct cs4281_state *s); -/* -* for now (12/22/00) only enable the pm_register PM support. -* allow these table entries to be null. -#define CS4281_SUSPEND_TBL cs4281_suspend_tbl -#define CS4281_RESUME_TBL cs4281_resume_tbl -*/ -#define CS4281_SUSPEND_TBL cs4281_null -#define CS4281_RESUME_TBL cs4281_null - -int cs4281_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - struct cs4281_state *state; - - CS_DBGOUT(CS_PM, 2, printk(KERN_INFO - "cs4281: cs4281_pm_callback dev=0x%x rqst=0x%x state=%d\n", - (unsigned)dev,(unsigned)rqst,(unsigned)data)); - state = (struct cs4281_state *) dev->data; - if (state) { - switch(rqst) { - case PM_SUSPEND: - CS_DBGOUT(CS_PM, 2, printk(KERN_INFO - "cs4281: PM suspend request\n")); - if(cs4281_suspend(state)) - { - CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO - "cs4281: PM suspend request refused\n")); - return 1; - } - break; - case PM_RESUME: - CS_DBGOUT(CS_PM, 2, printk(KERN_INFO - "cs4281: PM resume request\n")); - if(cs4281_resume(state)) - { - CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO - "cs4281: PM resume request refused\n")); - return 1; - } - break; - } - } - - return 0; -} - -#else /* CS4281_PM */ -#define CS4281_SUSPEND_TBL cs4281_null -#define CS4281_RESUME_TBL cs4281_null -#endif /* CS4281_PM */ - diff -Nru a/drivers/sound/cs4281/cs4281pm.h b/drivers/sound/cs4281/cs4281pm.h --- a/drivers/sound/cs4281/cs4281pm.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,74 +0,0 @@ -#ifndef NOT_CS4281_PM -/******************************************************************************* -* -* "cs4281pm.h" -- Cirrus Logic-Crystal CS4281 linux audio driver. -* -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- tom woller (twoller@crystal.cirrus.com) or -* (audio@crystal.cirrus.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. -* -* 12/22/00 trw - new file. -* -*******************************************************************************/ -/* general pm definitions */ -#define CS4281_AC97_HIGHESTREGTORESTORE 0x26 -#define CS4281_AC97_NUMBER_RESTORE_REGS (CS4281_AC97_HIGHESTREGTORESTORE/2-1) - -/* pipeline definitions */ -#define CS4281_NUMBER_OF_PIPELINES 4 -#define CS4281_PIPELINE_VALID 0x0001 -#define CS4281_PLAYBACK_PIPELINE_NUMBER 0x0000 -#define CS4281_CAPTURE_PIPELINE_NUMBER 0x0001 - -/* PM state defintions */ -#define CS4281_PM_NOT_REGISTERED 0x1000 -#define CS4281_PM_IDLE 0x0001 -#define CS4281_PM_SUSPENDING 0x0002 -#define CS4281_PM_SUSPENDED 0x0004 -#define CS4281_PM_RESUMING 0x0008 -#define CS4281_PM_RESUMED 0x0010 - -struct cs4281_pm { - unsigned long flags; - u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue; - u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR; - u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save; - u32 u32SSPM_BITS; - u32 ac97[CS4281_AC97_NUMBER_RESTORE_REGS]; - u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono; - u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose; - u32 u32hwptr_playback,u32hwptr_capture; -}; - -struct cs4281_pipeline { - unsigned flags; - unsigned number; - u32 u32DBAnValue,u32DBCnValue,u32DMRnValue,u32DCRnValue; - u32 u32DBAnAddress,u32DCAnAddress,u32DBCnAddress,u32DCCnAddress; - u32 u32DMRnAddress,u32DCRnAddress,u32HDSRnAddress; - u32 u32DBAn_Save,u32DBCn_Save,u32DMRn_Save,u32DCRn_Save; - u32 u32DCCn_Save,u32DCAn_Save; -/* -* technically, these are fifo variables, but just map the -* first fifo with the first pipeline and then use the fifo -* variables inside of the pipeline struct. -*/ - u32 u32FCRn_Save,u32FSICn_Save; - u32 u32FCRnValue,u32FCRnAddress,u32FSICnValue,u32FSICnAddress; - u32 u32FPDRnValue,u32FPDRnAddress; -}; -#endif diff -Nru a/drivers/sound/cs461x.h b/drivers/sound/cs461x.h --- a/drivers/sound/cs461x.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1691 +0,0 @@ -#ifndef __CS461X_H -#define __CS461X_H - -/* - * Copyright (c) by Cirrus Logic Corporation - * Copyright (c) by Jaroslav Kysela - * Definitions for Cirrus Logic CS461x chips - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#ifndef PCI_VENDOR_ID_CIRRUS -#define PCI_VENDOR_ID_CIRRUS 0x1013 -#endif -#ifndef PCI_DEVICE_ID_CIRRUS_4610 -#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 -#endif -#ifndef PCI_DEVICE_ID_CIRRUS_4612 -#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 -#endif -#ifndef PCI_DEVICE_ID_CIRRUS_4615 -#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 -#endif - -/* - * Direct registers - */ - -/* - * The following define the offsets of the registers accessed via base address - * register zero on the CS461x part. - */ -#define BA0_HISR 0x00000000 -#define BA0_HSR0 0x00000004 -#define BA0_HICR 0x00000008 -#define BA0_DMSR 0x00000100 -#define BA0_HSAR 0x00000110 -#define BA0_HDAR 0x00000114 -#define BA0_HDMR 0x00000118 -#define BA0_HDCR 0x0000011C -#define BA0_PFMC 0x00000200 -#define BA0_PFCV1 0x00000204 -#define BA0_PFCV2 0x00000208 -#define BA0_PCICFG00 0x00000300 -#define BA0_PCICFG04 0x00000304 -#define BA0_PCICFG08 0x00000308 -#define BA0_PCICFG0C 0x0000030C -#define BA0_PCICFG10 0x00000310 -#define BA0_PCICFG14 0x00000314 -#define BA0_PCICFG18 0x00000318 -#define BA0_PCICFG1C 0x0000031C -#define BA0_PCICFG20 0x00000320 -#define BA0_PCICFG24 0x00000324 -#define BA0_PCICFG28 0x00000328 -#define BA0_PCICFG2C 0x0000032C -#define BA0_PCICFG30 0x00000330 -#define BA0_PCICFG34 0x00000334 -#define BA0_PCICFG38 0x00000338 -#define BA0_PCICFG3C 0x0000033C -#define BA0_CLKCR1 0x00000400 -#define BA0_CLKCR2 0x00000404 -#define BA0_PLLM 0x00000408 -#define BA0_PLLCC 0x0000040C -#define BA0_FRR 0x00000410 -#define BA0_CFL1 0x00000414 -#define BA0_CFL2 0x00000418 -#define BA0_SERMC1 0x00000420 -#define BA0_SERMC2 0x00000424 -#define BA0_SERC1 0x00000428 -#define BA0_SERC2 0x0000042C -#define BA0_SERC3 0x00000430 -#define BA0_SERC4 0x00000434 -#define BA0_SERC5 0x00000438 -#define BA0_SERBSP 0x0000043C -#define BA0_SERBST 0x00000440 -#define BA0_SERBCM 0x00000444 -#define BA0_SERBAD 0x00000448 -#define BA0_SERBCF 0x0000044C -#define BA0_SERBWP 0x00000450 -#define BA0_SERBRP 0x00000454 -#ifndef NO_CS4612 -#define BA0_ASER_FADDR 0x00000458 -#endif -#define BA0_ACCTL 0x00000460 -#define BA0_ACSTS 0x00000464 -#define BA0_ACOSV 0x00000468 -#define BA0_ACCAD 0x0000046C -#define BA0_ACCDA 0x00000470 -#define BA0_ACISV 0x00000474 -#define BA0_ACSAD 0x00000478 -#define BA0_ACSDA 0x0000047C -#define BA0_JSPT 0x00000480 -#define BA0_JSCTL 0x00000484 -#define BA0_JSC1 0x00000488 -#define BA0_JSC2 0x0000048C -#define BA0_MIDCR 0x00000490 -#define BA0_MIDSR 0x00000494 -#define BA0_MIDWP 0x00000498 -#define BA0_MIDRP 0x0000049C -#define BA0_JSIO 0x000004A0 -#ifndef NO_CS4612 -#define BA0_ASER_MASTER 0x000004A4 -#endif -#define BA0_CFGI 0x000004B0 -#define BA0_SSVID 0x000004B4 -#define BA0_GPIOR 0x000004B8 -#ifndef NO_CS4612 -#define BA0_EGPIODR 0x000004BC -#define BA0_EGPIOPTR 0x000004C0 -#define BA0_EGPIOTR 0x000004C4 -#define BA0_EGPIOWR 0x000004C8 -#define BA0_EGPIOSR 0x000004CC -#define BA0_SERC6 0x000004D0 -#define BA0_SERC7 0x000004D4 -#define BA0_SERACC 0x000004D8 -#define BA0_ACCTL2 0x000004E0 -#define BA0_ACSTS2 0x000004E4 -#define BA0_ACOSV2 0x000004E8 -#define BA0_ACCAD2 0x000004EC -#define BA0_ACCDA2 0x000004F0 -#define BA0_ACISV2 0x000004F4 -#define BA0_ACSAD2 0x000004F8 -#define BA0_ACSDA2 0x000004FC -#define BA0_IOTAC0 0x00000500 -#define BA0_IOTAC1 0x00000504 -#define BA0_IOTAC2 0x00000508 -#define BA0_IOTAC3 0x0000050C -#define BA0_IOTAC4 0x00000510 -#define BA0_IOTAC5 0x00000514 -#define BA0_IOTAC6 0x00000518 -#define BA0_IOTAC7 0x0000051C -#define BA0_IOTAC8 0x00000520 -#define BA0_IOTAC9 0x00000524 -#define BA0_IOTAC10 0x00000528 -#define BA0_IOTAC11 0x0000052C -#define BA0_IOTFR0 0x00000540 -#define BA0_IOTFR1 0x00000544 -#define BA0_IOTFR2 0x00000548 -#define BA0_IOTFR3 0x0000054C -#define BA0_IOTFR4 0x00000550 -#define BA0_IOTFR5 0x00000554 -#define BA0_IOTFR6 0x00000558 -#define BA0_IOTFR7 0x0000055C -#define BA0_IOTFIFO 0x00000580 -#define BA0_IOTRRD 0x00000584 -#define BA0_IOTFP 0x00000588 -#define BA0_IOTCR 0x0000058C -#define BA0_DPCID 0x00000590 -#define BA0_DPCIA 0x00000594 -#define BA0_DPCIC 0x00000598 -#define BA0_PCPCIR 0x00000600 -#define BA0_PCPCIG 0x00000604 -#define BA0_PCPCIEN 0x00000608 -#define BA0_EPCIPMC 0x00000610 -#endif - -/* - * The following define the offsets of the registers and memories accessed via - * base address register one on the CS461x part. - */ -#define BA1_SP_DMEM0 0x00000000 -#define BA1_SP_DMEM1 0x00010000 -#define BA1_SP_PMEM 0x00020000 -#define BA1_SP_REG 0x00030000 -#define BA1_SPCR 0x00030000 -#define BA1_DREG 0x00030004 -#define BA1_DSRWP 0x00030008 -#define BA1_TWPR 0x0003000C -#define BA1_SPWR 0x00030010 -#define BA1_SPIR 0x00030014 -#define BA1_FGR1 0x00030020 -#define BA1_SPCS 0x00030028 -#define BA1_SDSR 0x0003002C -#define BA1_FRMT 0x00030030 -#define BA1_FRCC 0x00030034 -#define BA1_FRSC 0x00030038 -#define BA1_OMNI_MEM 0x000E0000 - -/* - * The following defines are for the flags in the host interrupt status - * register. - */ -#define HISR_VC_MASK 0x0000FFFF -#define HISR_VC0 0x00000001 -#define HISR_VC1 0x00000002 -#define HISR_VC2 0x00000004 -#define HISR_VC3 0x00000008 -#define HISR_VC4 0x00000010 -#define HISR_VC5 0x00000020 -#define HISR_VC6 0x00000040 -#define HISR_VC7 0x00000080 -#define HISR_VC8 0x00000100 -#define HISR_VC9 0x00000200 -#define HISR_VC10 0x00000400 -#define HISR_VC11 0x00000800 -#define HISR_VC12 0x00001000 -#define HISR_VC13 0x00002000 -#define HISR_VC14 0x00004000 -#define HISR_VC15 0x00008000 -#define HISR_INT0 0x00010000 -#define HISR_INT1 0x00020000 -#define HISR_DMAI 0x00040000 -#define HISR_FROVR 0x00080000 -#define HISR_MIDI 0x00100000 -#ifdef NO_CS4612 -#define HISR_RESERVED 0x0FE00000 -#else -#define HISR_SBINT 0x00200000 -#define HISR_RESERVED 0x0FC00000 -#endif -#define HISR_H0P 0x40000000 -#define HISR_INTENA 0x80000000 - -/* - * The following defines are for the flags in the host signal register 0. - */ -#define HSR0_VC_MASK 0xFFFFFFFF -#define HSR0_VC16 0x00000001 -#define HSR0_VC17 0x00000002 -#define HSR0_VC18 0x00000004 -#define HSR0_VC19 0x00000008 -#define HSR0_VC20 0x00000010 -#define HSR0_VC21 0x00000020 -#define HSR0_VC22 0x00000040 -#define HSR0_VC23 0x00000080 -#define HSR0_VC24 0x00000100 -#define HSR0_VC25 0x00000200 -#define HSR0_VC26 0x00000400 -#define HSR0_VC27 0x00000800 -#define HSR0_VC28 0x00001000 -#define HSR0_VC29 0x00002000 -#define HSR0_VC30 0x00004000 -#define HSR0_VC31 0x00008000 -#define HSR0_VC32 0x00010000 -#define HSR0_VC33 0x00020000 -#define HSR0_VC34 0x00040000 -#define HSR0_VC35 0x00080000 -#define HSR0_VC36 0x00100000 -#define HSR0_VC37 0x00200000 -#define HSR0_VC38 0x00400000 -#define HSR0_VC39 0x00800000 -#define HSR0_VC40 0x01000000 -#define HSR0_VC41 0x02000000 -#define HSR0_VC42 0x04000000 -#define HSR0_VC43 0x08000000 -#define HSR0_VC44 0x10000000 -#define HSR0_VC45 0x20000000 -#define HSR0_VC46 0x40000000 -#define HSR0_VC47 0x80000000 - -/* - * The following defines are for the flags in the host interrupt control - * register. - */ -#define HICR_IEV 0x00000001 -#define HICR_CHGM 0x00000002 - -/* - * The following defines are for the flags in the DMA status register. - */ -#define DMSR_HP 0x00000001 -#define DMSR_HR 0x00000002 -#define DMSR_SP 0x00000004 -#define DMSR_SR 0x00000008 - -/* - * The following defines are for the flags in the host DMA source address - * register. - */ -#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF -#define HSAR_DSP_ADDR_MASK 0x0000FFFF -#define HSAR_MEMID_MASK 0x000F0000 -#define HSAR_MEMID_SP_DMEM0 0x00000000 -#define HSAR_MEMID_SP_DMEM1 0x00010000 -#define HSAR_MEMID_SP_PMEM 0x00020000 -#define HSAR_MEMID_SP_DEBUG 0x00030000 -#define HSAR_MEMID_OMNI_MEM 0x000E0000 -#define HSAR_END 0x40000000 -#define HSAR_ERR 0x80000000 - -/* - * The following defines are for the flags in the host DMA destination address - * register. - */ -#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF -#define HDAR_DSP_ADDR_MASK 0x0000FFFF -#define HDAR_MEMID_MASK 0x000F0000 -#define HDAR_MEMID_SP_DMEM0 0x00000000 -#define HDAR_MEMID_SP_DMEM1 0x00010000 -#define HDAR_MEMID_SP_PMEM 0x00020000 -#define HDAR_MEMID_SP_DEBUG 0x00030000 -#define HDAR_MEMID_OMNI_MEM 0x000E0000 -#define HDAR_END 0x40000000 -#define HDAR_ERR 0x80000000 - -/* - * The following defines are for the flags in the host DMA control register. - */ -#define HDMR_AC_MASK 0x0000F000 -#define HDMR_AC_8_16 0x00001000 -#define HDMR_AC_M_S 0x00002000 -#define HDMR_AC_B_L 0x00004000 -#define HDMR_AC_S_U 0x00008000 - -/* - * The following defines are for the flags in the host DMA control register. - */ -#define HDCR_COUNT_MASK 0x000003FF -#define HDCR_DONE 0x00004000 -#define HDCR_OPT 0x00008000 -#define HDCR_WBD 0x00400000 -#define HDCR_WBS 0x00800000 -#define HDCR_DMS_MASK 0x07000000 -#define HDCR_DMS_LINEAR 0x00000000 -#define HDCR_DMS_16_DWORDS 0x01000000 -#define HDCR_DMS_32_DWORDS 0x02000000 -#define HDCR_DMS_64_DWORDS 0x03000000 -#define HDCR_DMS_128_DWORDS 0x04000000 -#define HDCR_DMS_256_DWORDS 0x05000000 -#define HDCR_DMS_512_DWORDS 0x06000000 -#define HDCR_DMS_1024_DWORDS 0x07000000 -#define HDCR_DH 0x08000000 -#define HDCR_SMS_MASK 0x70000000 -#define HDCR_SMS_LINEAR 0x00000000 -#define HDCR_SMS_16_DWORDS 0x10000000 -#define HDCR_SMS_32_DWORDS 0x20000000 -#define HDCR_SMS_64_DWORDS 0x30000000 -#define HDCR_SMS_128_DWORDS 0x40000000 -#define HDCR_SMS_256_DWORDS 0x50000000 -#define HDCR_SMS_512_DWORDS 0x60000000 -#define HDCR_SMS_1024_DWORDS 0x70000000 -#define HDCR_SH 0x80000000 -#define HDCR_COUNT_SHIFT 0 - -/* - * The following defines are for the flags in the performance monitor control - * register. - */ -#define PFMC_C1SS_MASK 0x0000001F -#define PFMC_C1EV 0x00000020 -#define PFMC_C1RS 0x00008000 -#define PFMC_C2SS_MASK 0x001F0000 -#define PFMC_C2EV 0x00200000 -#define PFMC_C2RS 0x80000000 -#define PFMC_C1SS_SHIFT 0 -#define PFMC_C2SS_SHIFT 16 -#define PFMC_BUS_GRANT 0 -#define PFMC_GRANT_AFTER_REQ 1 -#define PFMC_TRANSACTION 2 -#define PFMC_DWORD_TRANSFER 3 -#define PFMC_SLAVE_READ 4 -#define PFMC_SLAVE_WRITE 5 -#define PFMC_PREEMPTION 6 -#define PFMC_DISCONNECT_RETRY 7 -#define PFMC_INTERRUPT 8 -#define PFMC_BUS_OWNERSHIP 9 -#define PFMC_TRANSACTION_LAG 10 -#define PFMC_PCI_CLOCK 11 -#define PFMC_SERIAL_CLOCK 12 -#define PFMC_SP_CLOCK 13 - -/* - * The following defines are for the flags in the performance counter value 1 - * register. - */ -#define PFCV1_PC1V_MASK 0xFFFFFFFF -#define PFCV1_PC1V_SHIFT 0 - -/* - * The following defines are for the flags in the performance counter value 2 - * register. - */ -#define PFCV2_PC2V_MASK 0xFFFFFFFF -#define PFCV2_PC2V_SHIFT 0 - -/* - * The following defines are for the flags in the clock control register 1. - */ -#define CLKCR1_OSCS 0x00000001 -#define CLKCR1_OSCP 0x00000002 -#define CLKCR1_PLLSS_MASK 0x0000000C -#define CLKCR1_PLLSS_SERIAL 0x00000000 -#define CLKCR1_PLLSS_CRYSTAL 0x00000004 -#define CLKCR1_PLLSS_PCI 0x00000008 -#define CLKCR1_PLLSS_RESERVED 0x0000000C -#define CLKCR1_PLLP 0x00000010 -#define CLKCR1_SWCE 0x00000020 -#define CLKCR1_PLLOS 0x00000040 - -/* - * The following defines are for the flags in the clock control register 2. - */ -#define CLKCR2_PDIVS_MASK 0x0000000F -#define CLKCR2_PDIVS_1 0x00000001 -#define CLKCR2_PDIVS_2 0x00000002 -#define CLKCR2_PDIVS_4 0x00000004 -#define CLKCR2_PDIVS_7 0x00000007 -#define CLKCR2_PDIVS_8 0x00000008 -#define CLKCR2_PDIVS_16 0x00000000 - -/* - * The following defines are for the flags in the PLL multiplier register. - */ -#define PLLM_MASK 0x000000FF -#define PLLM_SHIFT 0 - -/* - * The following defines are for the flags in the PLL capacitor coefficient - * register. - */ -#define PLLCC_CDR_MASK 0x00000007 -#ifndef NO_CS4610 -#define PLLCC_CDR_240_350_MHZ 0x00000000 -#define PLLCC_CDR_184_265_MHZ 0x00000001 -#define PLLCC_CDR_144_205_MHZ 0x00000002 -#define PLLCC_CDR_111_160_MHZ 0x00000003 -#define PLLCC_CDR_87_123_MHZ 0x00000004 -#define PLLCC_CDR_67_96_MHZ 0x00000005 -#define PLLCC_CDR_52_74_MHZ 0x00000006 -#define PLLCC_CDR_45_58_MHZ 0x00000007 -#endif -#ifndef NO_CS4612 -#define PLLCC_CDR_271_398_MHZ 0x00000000 -#define PLLCC_CDR_227_330_MHZ 0x00000001 -#define PLLCC_CDR_167_239_MHZ 0x00000002 -#define PLLCC_CDR_150_215_MHZ 0x00000003 -#define PLLCC_CDR_107_154_MHZ 0x00000004 -#define PLLCC_CDR_98_140_MHZ 0x00000005 -#define PLLCC_CDR_73_104_MHZ 0x00000006 -#define PLLCC_CDR_63_90_MHZ 0x00000007 -#endif -#define PLLCC_LPF_MASK 0x000000F8 -#ifndef NO_CS4610 -#define PLLCC_LPF_23850_60000_KHZ 0x00000000 -#define PLLCC_LPF_7960_26290_KHZ 0x00000008 -#define PLLCC_LPF_4160_10980_KHZ 0x00000018 -#define PLLCC_LPF_1740_4580_KHZ 0x00000038 -#define PLLCC_LPF_724_1910_KHZ 0x00000078 -#define PLLCC_LPF_317_798_KHZ 0x000000F8 -#endif -#ifndef NO_CS4612 -#define PLLCC_LPF_25580_64530_KHZ 0x00000000 -#define PLLCC_LPF_14360_37270_KHZ 0x00000008 -#define PLLCC_LPF_6100_16020_KHZ 0x00000018 -#define PLLCC_LPF_2540_6690_KHZ 0x00000038 -#define PLLCC_LPF_1050_2780_KHZ 0x00000078 -#define PLLCC_LPF_450_1160_KHZ 0x000000F8 -#endif - -/* - * The following defines are for the flags in the feature reporting register. - */ -#define FRR_FAB_MASK 0x00000003 -#define FRR_MASK_MASK 0x0000001C -#ifdef NO_CS4612 -#define FRR_CFOP_MASK 0x000000E0 -#else -#define FRR_CFOP_MASK 0x00000FE0 -#endif -#define FRR_CFOP_NOT_DVD 0x00000020 -#define FRR_CFOP_A3D 0x00000040 -#define FRR_CFOP_128_PIN 0x00000080 -#ifndef NO_CS4612 -#define FRR_CFOP_CS4280 0x00000800 -#endif -#define FRR_FAB_SHIFT 0 -#define FRR_MASK_SHIFT 2 -#define FRR_CFOP_SHIFT 5 - -/* - * The following defines are for the flags in the configuration load 1 - * register. - */ -#define CFL1_CLOCK_SOURCE_MASK 0x00000003 -#define CFL1_CLOCK_SOURCE_CS423X 0x00000000 -#define CFL1_CLOCK_SOURCE_AC97 0x00000001 -#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002 -#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003 -#define CFL1_VALID_DATA_MASK 0x000000FF - -/* - * The following defines are for the flags in the configuration load 2 - * register. - */ -#define CFL2_VALID_DATA_MASK 0x000000FF - -/* - * The following defines are for the flags in the serial port master control - * register 1. - */ -#define SERMC1_MSPE 0x00000001 -#define SERMC1_PTC_MASK 0x0000000E -#define SERMC1_PTC_CS423X 0x00000000 -#define SERMC1_PTC_AC97 0x00000002 -#define SERMC1_PTC_DAC 0x00000004 -#define SERMC1_PLB 0x00000010 -#define SERMC1_XLB 0x00000020 - -/* - * The following defines are for the flags in the serial port master control - * register 2. - */ -#define SERMC2_LROE 0x00000001 -#define SERMC2_MCOE 0x00000002 -#define SERMC2_MCDIV 0x00000004 - -/* - * The following defines are for the flags in the serial port 1 configuration - * register. - */ -#define SERC1_SO1EN 0x00000001 -#define SERC1_SO1F_MASK 0x0000000E -#define SERC1_SO1F_CS423X 0x00000000 -#define SERC1_SO1F_AC97 0x00000002 -#define SERC1_SO1F_DAC 0x00000004 -#define SERC1_SO1F_SPDIF 0x00000006 - -/* - * The following defines are for the flags in the serial port 2 configuration - * register. - */ -#define SERC2_SI1EN 0x00000001 -#define SERC2_SI1F_MASK 0x0000000E -#define SERC2_SI1F_CS423X 0x00000000 -#define SERC2_SI1F_AC97 0x00000002 -#define SERC2_SI1F_ADC 0x00000004 -#define SERC2_SI1F_SPDIF 0x00000006 - -/* - * The following defines are for the flags in the serial port 3 configuration - * register. - */ -#define SERC3_SO2EN 0x00000001 -#define SERC3_SO2F_MASK 0x00000006 -#define SERC3_SO2F_DAC 0x00000000 -#define SERC3_SO2F_SPDIF 0x00000002 - -/* - * The following defines are for the flags in the serial port 4 configuration - * register. - */ -#define SERC4_SO3EN 0x00000001 -#define SERC4_SO3F_MASK 0x00000006 -#define SERC4_SO3F_DAC 0x00000000 -#define SERC4_SO3F_SPDIF 0x00000002 - -/* - * The following defines are for the flags in the serial port 5 configuration - * register. - */ -#define SERC5_SI2EN 0x00000001 -#define SERC5_SI2F_MASK 0x00000006 -#define SERC5_SI2F_ADC 0x00000000 -#define SERC5_SI2F_SPDIF 0x00000002 - -/* - * The following defines are for the flags in the serial port backdoor sample - * pointer register. - */ -#define SERBSP_FSP_MASK 0x0000000F -#define SERBSP_FSP_SHIFT 0 - -/* - * The following defines are for the flags in the serial port backdoor status - * register. - */ -#define SERBST_RRDY 0x00000001 -#define SERBST_WBSY 0x00000002 - -/* - * The following defines are for the flags in the serial port backdoor command - * register. - */ -#define SERBCM_RDC 0x00000001 -#define SERBCM_WRC 0x00000002 - -/* - * The following defines are for the flags in the serial port backdoor address - * register. - */ -#ifdef NO_CS4612 -#define SERBAD_FAD_MASK 0x000000FF -#else -#define SERBAD_FAD_MASK 0x000001FF -#endif -#define SERBAD_FAD_SHIFT 0 - -/* - * The following defines are for the flags in the serial port backdoor - * configuration register. - */ -#define SERBCF_HBP 0x00000001 - -/* - * The following defines are for the flags in the serial port backdoor write - * port register. - */ -#define SERBWP_FWD_MASK 0x000FFFFF -#define SERBWP_FWD_SHIFT 0 - -/* - * The following defines are for the flags in the serial port backdoor read - * port register. - */ -#define SERBRP_FRD_MASK 0x000FFFFF -#define SERBRP_FRD_SHIFT 0 - -/* - * The following defines are for the flags in the async FIFO address register. - */ -#ifndef NO_CS4612 -#define ASER_FADDR_A1_MASK 0x000001FF -#define ASER_FADDR_EN1 0x00008000 -#define ASER_FADDR_A2_MASK 0x01FF0000 -#define ASER_FADDR_EN2 0x80000000 -#define ASER_FADDR_A1_SHIFT 0 -#define ASER_FADDR_A2_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the AC97 control register. - */ -#define ACCTL_RSTN 0x00000001 -#define ACCTL_ESYN 0x00000002 -#define ACCTL_VFRM 0x00000004 -#define ACCTL_DCV 0x00000008 -#define ACCTL_CRW 0x00000010 -#define ACCTL_ASYN 0x00000020 -#ifndef NO_CS4612 -#define ACCTL_TC 0x00000040 -#endif - -/* - * The following defines are for the flags in the AC97 status register. - */ -#define ACSTS_CRDY 0x00000001 -#define ACSTS_VSTS 0x00000002 -#ifndef NO_CS4612 -#define ACSTS_WKUP 0x00000004 -#endif - -/* - * The following defines are for the flags in the AC97 output slot valid - * register. - */ -#define ACOSV_SLV3 0x00000001 -#define ACOSV_SLV4 0x00000002 -#define ACOSV_SLV5 0x00000004 -#define ACOSV_SLV6 0x00000008 -#define ACOSV_SLV7 0x00000010 -#define ACOSV_SLV8 0x00000020 -#define ACOSV_SLV9 0x00000040 -#define ACOSV_SLV10 0x00000080 -#define ACOSV_SLV11 0x00000100 -#define ACOSV_SLV12 0x00000200 - -/* - * The following defines are for the flags in the AC97 command address - * register. - */ -#define ACCAD_CI_MASK 0x0000007F -#define ACCAD_CI_SHIFT 0 - -/* - * The following defines are for the flags in the AC97 command data register. - */ -#define ACCDA_CD_MASK 0x0000FFFF -#define ACCDA_CD_SHIFT 0 - -/* - * The following defines are for the flags in the AC97 input slot valid - * register. - */ -#define ACISV_ISV3 0x00000001 -#define ACISV_ISV4 0x00000002 -#define ACISV_ISV5 0x00000004 -#define ACISV_ISV6 0x00000008 -#define ACISV_ISV7 0x00000010 -#define ACISV_ISV8 0x00000020 -#define ACISV_ISV9 0x00000040 -#define ACISV_ISV10 0x00000080 -#define ACISV_ISV11 0x00000100 -#define ACISV_ISV12 0x00000200 - -/* - * The following defines are for the flags in the AC97 status address - * register. - */ -#define ACSAD_SI_MASK 0x0000007F -#define ACSAD_SI_SHIFT 0 - -/* - * The following defines are for the flags in the AC97 status data register. - */ -#define ACSDA_SD_MASK 0x0000FFFF -#define ACSDA_SD_SHIFT 0 - -/* - * The following defines are for the flags in the joystick poll/trigger - * register. - */ -#define JSPT_CAX 0x00000001 -#define JSPT_CAY 0x00000002 -#define JSPT_CBX 0x00000004 -#define JSPT_CBY 0x00000008 -#define JSPT_BA1 0x00000010 -#define JSPT_BA2 0x00000020 -#define JSPT_BB1 0x00000040 -#define JSPT_BB2 0x00000080 - -/* - * The following defines are for the flags in the joystick control register. - */ -#define JSCTL_SP_MASK 0x00000003 -#define JSCTL_SP_SLOW 0x00000000 -#define JSCTL_SP_MEDIUM_SLOW 0x00000001 -#define JSCTL_SP_MEDIUM_FAST 0x00000002 -#define JSCTL_SP_FAST 0x00000003 -#define JSCTL_ARE 0x00000004 - -/* - * The following defines are for the flags in the joystick coordinate pair 1 - * readback register. - */ -#define JSC1_Y1V_MASK 0x0000FFFF -#define JSC1_X1V_MASK 0xFFFF0000 -#define JSC1_Y1V_SHIFT 0 -#define JSC1_X1V_SHIFT 16 - -/* - * The following defines are for the flags in the joystick coordinate pair 2 - * readback register. - */ -#define JSC2_Y2V_MASK 0x0000FFFF -#define JSC2_X2V_MASK 0xFFFF0000 -#define JSC2_Y2V_SHIFT 0 -#define JSC2_X2V_SHIFT 16 - -/* - * The following defines are for the flags in the MIDI control register. - */ -#define MIDCR_TXE 0x00000001 /* Enable transmitting. */ -#define MIDCR_RXE 0x00000002 /* Enable receiving. */ -#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */ -#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */ -#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */ -#define MIDCR_MRST 0x00000020 /* Reset interface. */ - -/* - * The following defines are for the flags in the MIDI status register. - */ -#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */ -#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */ - -/* - * The following defines are for the flags in the MIDI write port register. - */ -#define MIDWP_MWD_MASK 0x000000FF -#define MIDWP_MWD_SHIFT 0 - -/* - * The following defines are for the flags in the MIDI read port register. - */ -#define MIDRP_MRD_MASK 0x000000FF -#define MIDRP_MRD_SHIFT 0 - -/* - * The following defines are for the flags in the joystick GPIO register. - */ -#define JSIO_DAX 0x00000001 -#define JSIO_DAY 0x00000002 -#define JSIO_DBX 0x00000004 -#define JSIO_DBY 0x00000008 -#define JSIO_AXOE 0x00000010 -#define JSIO_AYOE 0x00000020 -#define JSIO_BXOE 0x00000040 -#define JSIO_BYOE 0x00000080 - -/* - * The following defines are for the flags in the master async/sync serial - * port enable register. - */ -#ifndef NO_CS4612 -#define ASER_MASTER_ME 0x00000001 -#endif - -/* - * The following defines are for the flags in the configuration interface - * register. - */ -#define CFGI_CLK 0x00000001 -#define CFGI_DOUT 0x00000002 -#define CFGI_DIN_EEN 0x00000004 -#define CFGI_EELD 0x00000008 - -/* - * The following defines are for the flags in the subsystem ID and vendor ID - * register. - */ -#define SSVID_VID_MASK 0x0000FFFF -#define SSVID_SID_MASK 0xFFFF0000 -#define SSVID_VID_SHIFT 0 -#define SSVID_SID_SHIFT 16 - -/* - * The following defines are for the flags in the GPIO pin interface register. - */ -#define GPIOR_VOLDN 0x00000001 -#define GPIOR_VOLUP 0x00000002 -#define GPIOR_SI2D 0x00000004 -#define GPIOR_SI2OE 0x00000008 - -/* - * The following defines are for the flags in the extended GPIO pin direction - * register. - */ -#ifndef NO_CS4612 -#define EGPIODR_GPOE0 0x00000001 -#define EGPIODR_GPOE1 0x00000002 -#define EGPIODR_GPOE2 0x00000004 -#define EGPIODR_GPOE3 0x00000008 -#define EGPIODR_GPOE4 0x00000010 -#define EGPIODR_GPOE5 0x00000020 -#define EGPIODR_GPOE6 0x00000040 -#define EGPIODR_GPOE7 0x00000080 -#define EGPIODR_GPOE8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO pin polarity/ - * type register. - */ -#ifndef NO_CS4612 -#define EGPIOPTR_GPPT0 0x00000001 -#define EGPIOPTR_GPPT1 0x00000002 -#define EGPIOPTR_GPPT2 0x00000004 -#define EGPIOPTR_GPPT3 0x00000008 -#define EGPIOPTR_GPPT4 0x00000010 -#define EGPIOPTR_GPPT5 0x00000020 -#define EGPIOPTR_GPPT6 0x00000040 -#define EGPIOPTR_GPPT7 0x00000080 -#define EGPIOPTR_GPPT8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO pin sticky - * register. - */ -#ifndef NO_CS4612 -#define EGPIOTR_GPS0 0x00000001 -#define EGPIOTR_GPS1 0x00000002 -#define EGPIOTR_GPS2 0x00000004 -#define EGPIOTR_GPS3 0x00000008 -#define EGPIOTR_GPS4 0x00000010 -#define EGPIOTR_GPS5 0x00000020 -#define EGPIOTR_GPS6 0x00000040 -#define EGPIOTR_GPS7 0x00000080 -#define EGPIOTR_GPS8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO ping wakeup - * register. - */ -#ifndef NO_CS4612 -#define EGPIOWR_GPW0 0x00000001 -#define EGPIOWR_GPW1 0x00000002 -#define EGPIOWR_GPW2 0x00000004 -#define EGPIOWR_GPW3 0x00000008 -#define EGPIOWR_GPW4 0x00000010 -#define EGPIOWR_GPW5 0x00000020 -#define EGPIOWR_GPW6 0x00000040 -#define EGPIOWR_GPW7 0x00000080 -#define EGPIOWR_GPW8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO pin status - * register. - */ -#ifndef NO_CS4612 -#define EGPIOSR_GPS0 0x00000001 -#define EGPIOSR_GPS1 0x00000002 -#define EGPIOSR_GPS2 0x00000004 -#define EGPIOSR_GPS3 0x00000008 -#define EGPIOSR_GPS4 0x00000010 -#define EGPIOSR_GPS5 0x00000020 -#define EGPIOSR_GPS6 0x00000040 -#define EGPIOSR_GPS7 0x00000080 -#define EGPIOSR_GPS8 0x00000100 -#endif - -/* - * The following defines are for the flags in the serial port 6 configuration - * register. - */ -#ifndef NO_CS4612 -#define SERC6_ASDO2EN 0x00000001 -#endif - -/* - * The following defines are for the flags in the serial port 7 configuration - * register. - */ -#ifndef NO_CS4612 -#define SERC7_ASDI2EN 0x00000001 -#define SERC7_POSILB 0x00000002 -#define SERC7_SIPOLB 0x00000004 -#define SERC7_SOSILB 0x00000008 -#define SERC7_SISOLB 0x00000010 -#endif - -/* - * The following defines are for the flags in the serial port AC link - * configuration register. - */ -#ifndef NO_CS4612 -#define SERACC_CODEC_TYPE_MASK 0x00000001 -#define SERACC_CODEC_TYPE_1_03 0x00000000 -#define SERACC_CODEC_TYPE_2_0 0x00000001 -#define SERACC_TWO_CODECS 0x00000002 -#define SERACC_MDM 0x00000004 -#define SERACC_HSP 0x00000008 -#endif - -/* - * The following defines are for the flags in the AC97 control register 2. - */ -#ifndef NO_CS4612 -#define ACCTL2_RSTN 0x00000001 -#define ACCTL2_ESYN 0x00000002 -#define ACCTL2_VFRM 0x00000004 -#define ACCTL2_DCV 0x00000008 -#define ACCTL2_CRW 0x00000010 -#define ACCTL2_ASYN 0x00000020 -#endif - -/* - * The following defines are for the flags in the AC97 status register 2. - */ -#ifndef NO_CS4612 -#define ACSTS2_CRDY 0x00000001 -#define ACSTS2_VSTS 0x00000002 -#endif - -/* - * The following defines are for the flags in the AC97 output slot valid - * register 2. - */ -#ifndef NO_CS4612 -#define ACOSV2_SLV3 0x00000001 -#define ACOSV2_SLV4 0x00000002 -#define ACOSV2_SLV5 0x00000004 -#define ACOSV2_SLV6 0x00000008 -#define ACOSV2_SLV7 0x00000010 -#define ACOSV2_SLV8 0x00000020 -#define ACOSV2_SLV9 0x00000040 -#define ACOSV2_SLV10 0x00000080 -#define ACOSV2_SLV11 0x00000100 -#define ACOSV2_SLV12 0x00000200 -#endif - -/* - * The following defines are for the flags in the AC97 command address - * register 2. - */ -#ifndef NO_CS4612 -#define ACCAD2_CI_MASK 0x0000007F -#define ACCAD2_CI_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the AC97 command data register - * 2. - */ -#ifndef NO_CS4612 -#define ACCDA2_CD_MASK 0x0000FFFF -#define ACCDA2_CD_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the AC97 input slot valid - * register 2. - */ -#ifndef NO_CS4612 -#define ACISV2_ISV3 0x00000001 -#define ACISV2_ISV4 0x00000002 -#define ACISV2_ISV5 0x00000004 -#define ACISV2_ISV6 0x00000008 -#define ACISV2_ISV7 0x00000010 -#define ACISV2_ISV8 0x00000020 -#define ACISV2_ISV9 0x00000040 -#define ACISV2_ISV10 0x00000080 -#define ACISV2_ISV11 0x00000100 -#define ACISV2_ISV12 0x00000200 -#endif - -/* - * The following defines are for the flags in the AC97 status address - * register 2. - */ -#ifndef NO_CS4612 -#define ACSAD2_SI_MASK 0x0000007F -#define ACSAD2_SI_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the AC97 status data register 2. - */ -#ifndef NO_CS4612 -#define ACSDA2_SD_MASK 0x0000FFFF -#define ACSDA2_SD_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the I/O trap address and control - * registers (all 12). - */ -#ifndef NO_CS4612 -#define IOTAC_SA_MASK 0x0000FFFF -#define IOTAC_MSK_MASK 0x000F0000 -#define IOTAC_IODC_MASK 0x06000000 -#define IOTAC_IODC_16_BIT 0x00000000 -#define IOTAC_IODC_10_BIT 0x02000000 -#define IOTAC_IODC_12_BIT 0x04000000 -#define IOTAC_WSPI 0x08000000 -#define IOTAC_RSPI 0x10000000 -#define IOTAC_WSE 0x20000000 -#define IOTAC_WE 0x40000000 -#define IOTAC_RE 0x80000000 -#define IOTAC_SA_SHIFT 0 -#define IOTAC_MSK_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the I/O trap fast read registers - * (all 8). - */ -#ifndef NO_CS4612 -#define IOTFR_D_MASK 0x0000FFFF -#define IOTFR_A_MASK 0x000F0000 -#define IOTFR_R_MASK 0x0F000000 -#define IOTFR_ALL 0x40000000 -#define IOTFR_VL 0x80000000 -#define IOTFR_D_SHIFT 0 -#define IOTFR_A_SHIFT 16 -#define IOTFR_R_SHIFT 24 -#endif - -/* - * The following defines are for the flags in the I/O trap FIFO register. - */ -#ifndef NO_CS4612 -#define IOTFIFO_BA_MASK 0x00003FFF -#define IOTFIFO_S_MASK 0x00FF0000 -#define IOTFIFO_OF 0x40000000 -#define IOTFIFO_SPIOF 0x80000000 -#define IOTFIFO_BA_SHIFT 0 -#define IOTFIFO_S_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the I/O trap retry read data - * register. - */ -#ifndef NO_CS4612 -#define IOTRRD_D_MASK 0x0000FFFF -#define IOTRRD_RDV 0x80000000 -#define IOTRRD_D_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the I/O trap FIFO pointer - * register. - */ -#ifndef NO_CS4612 -#define IOTFP_CA_MASK 0x00003FFF -#define IOTFP_PA_MASK 0x3FFF0000 -#define IOTFP_CA_SHIFT 0 -#define IOTFP_PA_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the I/O trap control register. - */ -#ifndef NO_CS4612 -#define IOTCR_ITD 0x00000001 -#define IOTCR_HRV 0x00000002 -#define IOTCR_SRV 0x00000004 -#define IOTCR_DTI 0x00000008 -#define IOTCR_DFI 0x00000010 -#define IOTCR_DDP 0x00000020 -#define IOTCR_JTE 0x00000040 -#define IOTCR_PPE 0x00000080 -#endif - -/* - * The following defines are for the flags in the direct PCI data register. - */ -#ifndef NO_CS4612 -#define DPCID_D_MASK 0xFFFFFFFF -#define DPCID_D_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the direct PCI address register. - */ -#ifndef NO_CS4612 -#define DPCIA_A_MASK 0xFFFFFFFF -#define DPCIA_A_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the direct PCI command register. - */ -#ifndef NO_CS4612 -#define DPCIC_C_MASK 0x0000000F -#define DPCIC_C_IOREAD 0x00000002 -#define DPCIC_C_IOWRITE 0x00000003 -#define DPCIC_BE_MASK 0x000000F0 -#endif - -/* - * The following defines are for the flags in the PC/PCI request register. - */ -#ifndef NO_CS4612 -#define PCPCIR_RDC_MASK 0x00000007 -#define PCPCIR_C_MASK 0x00007000 -#define PCPCIR_REQ 0x00008000 -#define PCPCIR_RDC_SHIFT 0 -#define PCPCIR_C_SHIFT 12 -#endif - -/* - * The following defines are for the flags in the PC/PCI grant register. - */ -#ifndef NO_CS4612 -#define PCPCIG_GDC_MASK 0x00000007 -#define PCPCIG_VL 0x00008000 -#define PCPCIG_GDC_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the PC/PCI master enable - * register. - */ -#ifndef NO_CS4612 -#define PCPCIEN_EN 0x00000001 -#endif - -/* - * The following defines are for the flags in the extended PCI power - * management control register. - */ -#ifndef NO_CS4612 -#define EPCIPMC_GWU 0x00000001 -#define EPCIPMC_FSPC 0x00000002 -#endif - -/* - * The following defines are for the flags in the SP control register. - */ -#define SPCR_RUN 0x00000001 -#define SPCR_STPFR 0x00000002 -#define SPCR_RUNFR 0x00000004 -#define SPCR_TICK 0x00000008 -#define SPCR_DRQEN 0x00000020 -#define SPCR_RSTSP 0x00000040 -#define SPCR_OREN 0x00000080 -#ifndef NO_CS4612 -#define SPCR_PCIINT 0x00000100 -#define SPCR_OINTD 0x00000200 -#define SPCR_CRE 0x00008000 -#endif - -/* - * The following defines are for the flags in the debug index register. - */ -#define DREG_REGID_MASK 0x0000007F -#define DREG_DEBUG 0x00000080 -#define DREG_RGBK_MASK 0x00000700 -#define DREG_TRAP 0x00000800 -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_TRAPX 0x00001000 -#endif -#endif -#define DREG_REGID_SHIFT 0 -#define DREG_RGBK_SHIFT 8 -#define DREG_RGBK_REGID_MASK 0x0000077F -#define DREG_REGID_R0 0x00000010 -#define DREG_REGID_R1 0x00000011 -#define DREG_REGID_R2 0x00000012 -#define DREG_REGID_R3 0x00000013 -#define DREG_REGID_R4 0x00000014 -#define DREG_REGID_R5 0x00000015 -#define DREG_REGID_R6 0x00000016 -#define DREG_REGID_R7 0x00000017 -#define DREG_REGID_R8 0x00000018 -#define DREG_REGID_R9 0x00000019 -#define DREG_REGID_RA 0x0000001A -#define DREG_REGID_RB 0x0000001B -#define DREG_REGID_RC 0x0000001C -#define DREG_REGID_RD 0x0000001D -#define DREG_REGID_RE 0x0000001E -#define DREG_REGID_RF 0x0000001F -#define DREG_REGID_RA_BUS_LOW 0x00000020 -#define DREG_REGID_RA_BUS_HIGH 0x00000038 -#define DREG_REGID_YBUS_LOW 0x00000050 -#define DREG_REGID_YBUS_HIGH 0x00000058 -#define DREG_REGID_TRAP_0 0x00000100 -#define DREG_REGID_TRAP_1 0x00000101 -#define DREG_REGID_TRAP_2 0x00000102 -#define DREG_REGID_TRAP_3 0x00000103 -#define DREG_REGID_TRAP_4 0x00000104 -#define DREG_REGID_TRAP_5 0x00000105 -#define DREG_REGID_TRAP_6 0x00000106 -#define DREG_REGID_TRAP_7 0x00000107 -#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E -#define DREG_REGID_TOP_OF_STACK 0x0000010F -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_REGID_TRAP_8 0x00000110 -#define DREG_REGID_TRAP_9 0x00000111 -#define DREG_REGID_TRAP_10 0x00000112 -#define DREG_REGID_TRAP_11 0x00000113 -#define DREG_REGID_TRAP_12 0x00000114 -#define DREG_REGID_TRAP_13 0x00000115 -#define DREG_REGID_TRAP_14 0x00000116 -#define DREG_REGID_TRAP_15 0x00000117 -#define DREG_REGID_TRAP_16 0x00000118 -#define DREG_REGID_TRAP_17 0x00000119 -#define DREG_REGID_TRAP_18 0x0000011A -#define DREG_REGID_TRAP_19 0x0000011B -#define DREG_REGID_TRAP_20 0x0000011C -#define DREG_REGID_TRAP_21 0x0000011D -#define DREG_REGID_TRAP_22 0x0000011E -#define DREG_REGID_TRAP_23 0x0000011F -#endif -#endif -#define DREG_REGID_RSA0_LOW 0x00000200 -#define DREG_REGID_RSA0_HIGH 0x00000201 -#define DREG_REGID_RSA1_LOW 0x00000202 -#define DREG_REGID_RSA1_HIGH 0x00000203 -#define DREG_REGID_RSA2 0x00000204 -#define DREG_REGID_RSA3 0x00000205 -#define DREG_REGID_RSI0_LOW 0x00000206 -#define DREG_REGID_RSI0_HIGH 0x00000207 -#define DREG_REGID_RSI1 0x00000208 -#define DREG_REGID_RSI2 0x00000209 -#define DREG_REGID_SAGUSTATUS 0x0000020A -#define DREG_REGID_RSCONFIG01_LOW 0x0000020B -#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C -#define DREG_REGID_RSCONFIG23_LOW 0x0000020D -#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E -#define DREG_REGID_RSDMA01E 0x0000020F -#define DREG_REGID_RSDMA23E 0x00000210 -#define DREG_REGID_RSD0_LOW 0x00000211 -#define DREG_REGID_RSD0_HIGH 0x00000212 -#define DREG_REGID_RSD1_LOW 0x00000213 -#define DREG_REGID_RSD1_HIGH 0x00000214 -#define DREG_REGID_RSD2_LOW 0x00000215 -#define DREG_REGID_RSD2_HIGH 0x00000216 -#define DREG_REGID_RSD3_LOW 0x00000217 -#define DREG_REGID_RSD3_HIGH 0x00000218 -#define DREG_REGID_SRAR_HIGH 0x0000021A -#define DREG_REGID_SRAR_LOW 0x0000021B -#define DREG_REGID_DMA_STATE 0x0000021C -#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D -#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E -#define DREG_REGID_CPU_STATUS 0x00000300 -#define DREG_REGID_MAC_MODE 0x00000301 -#define DREG_REGID_STACK_AND_REPEAT 0x00000302 -#define DREG_REGID_INDEX0 0x00000304 -#define DREG_REGID_INDEX1 0x00000305 -#define DREG_REGID_DMA_STATE_0_3 0x00000400 -#define DREG_REGID_DMA_STATE_4_7 0x00000404 -#define DREG_REGID_DMA_STATE_8_11 0x00000408 -#define DREG_REGID_DMA_STATE_12_15 0x0000040C -#define DREG_REGID_DMA_STATE_16_19 0x00000410 -#define DREG_REGID_DMA_STATE_20_23 0x00000414 -#define DREG_REGID_DMA_STATE_24_27 0x00000418 -#define DREG_REGID_DMA_STATE_28_31 0x0000041C -#define DREG_REGID_DMA_STATE_32_35 0x00000420 -#define DREG_REGID_DMA_STATE_36_39 0x00000424 -#define DREG_REGID_DMA_STATE_40_43 0x00000428 -#define DREG_REGID_DMA_STATE_44_47 0x0000042C -#define DREG_REGID_DMA_STATE_48_51 0x00000430 -#define DREG_REGID_DMA_STATE_52_55 0x00000434 -#define DREG_REGID_DMA_STATE_56_59 0x00000438 -#define DREG_REGID_DMA_STATE_60_63 0x0000043C -#define DREG_REGID_DMA_STATE_64_67 0x00000440 -#define DREG_REGID_DMA_STATE_68_71 0x00000444 -#define DREG_REGID_DMA_STATE_72_75 0x00000448 -#define DREG_REGID_DMA_STATE_76_79 0x0000044C -#define DREG_REGID_DMA_STATE_80_83 0x00000450 -#define DREG_REGID_DMA_STATE_84_87 0x00000454 -#define DREG_REGID_DMA_STATE_88_91 0x00000458 -#define DREG_REGID_DMA_STATE_92_95 0x0000045C -#define DREG_REGID_TRAP_SELECT 0x00000500 -#define DREG_REGID_TRAP_WRITE_0 0x00000500 -#define DREG_REGID_TRAP_WRITE_1 0x00000501 -#define DREG_REGID_TRAP_WRITE_2 0x00000502 -#define DREG_REGID_TRAP_WRITE_3 0x00000503 -#define DREG_REGID_TRAP_WRITE_4 0x00000504 -#define DREG_REGID_TRAP_WRITE_5 0x00000505 -#define DREG_REGID_TRAP_WRITE_6 0x00000506 -#define DREG_REGID_TRAP_WRITE_7 0x00000507 -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_REGID_TRAP_WRITE_8 0x00000510 -#define DREG_REGID_TRAP_WRITE_9 0x00000511 -#define DREG_REGID_TRAP_WRITE_10 0x00000512 -#define DREG_REGID_TRAP_WRITE_11 0x00000513 -#define DREG_REGID_TRAP_WRITE_12 0x00000514 -#define DREG_REGID_TRAP_WRITE_13 0x00000515 -#define DREG_REGID_TRAP_WRITE_14 0x00000516 -#define DREG_REGID_TRAP_WRITE_15 0x00000517 -#define DREG_REGID_TRAP_WRITE_16 0x00000518 -#define DREG_REGID_TRAP_WRITE_17 0x00000519 -#define DREG_REGID_TRAP_WRITE_18 0x0000051A -#define DREG_REGID_TRAP_WRITE_19 0x0000051B -#define DREG_REGID_TRAP_WRITE_20 0x0000051C -#define DREG_REGID_TRAP_WRITE_21 0x0000051D -#define DREG_REGID_TRAP_WRITE_22 0x0000051E -#define DREG_REGID_TRAP_WRITE_23 0x0000051F -#endif -#endif -#define DREG_REGID_MAC0_ACC0_LOW 0x00000600 -#define DREG_REGID_MAC0_ACC1_LOW 0x00000601 -#define DREG_REGID_MAC0_ACC2_LOW 0x00000602 -#define DREG_REGID_MAC0_ACC3_LOW 0x00000603 -#define DREG_REGID_MAC1_ACC0_LOW 0x00000604 -#define DREG_REGID_MAC1_ACC1_LOW 0x00000605 -#define DREG_REGID_MAC1_ACC2_LOW 0x00000606 -#define DREG_REGID_MAC1_ACC3_LOW 0x00000607 -#define DREG_REGID_MAC0_ACC0_MID 0x00000608 -#define DREG_REGID_MAC0_ACC1_MID 0x00000609 -#define DREG_REGID_MAC0_ACC2_MID 0x0000060A -#define DREG_REGID_MAC0_ACC3_MID 0x0000060B -#define DREG_REGID_MAC1_ACC0_MID 0x0000060C -#define DREG_REGID_MAC1_ACC1_MID 0x0000060D -#define DREG_REGID_MAC1_ACC2_MID 0x0000060E -#define DREG_REGID_MAC1_ACC3_MID 0x0000060F -#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610 -#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611 -#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612 -#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613 -#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614 -#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615 -#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616 -#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617 -#define DREG_REGID_RSHOUT_LOW 0x00000620 -#define DREG_REGID_RSHOUT_MID 0x00000628 -#define DREG_REGID_RSHOUT_HIGH 0x00000630 - -/* - * The following defines are for the flags in the DMA stream requestor write - */ -#define DSRWP_DSR_MASK 0x0000000F -#define DSRWP_DSR_BG_RQ 0x00000001 -#define DSRWP_DSR_PRIORITY_MASK 0x00000006 -#define DSRWP_DSR_PRIORITY_0 0x00000000 -#define DSRWP_DSR_PRIORITY_1 0x00000002 -#define DSRWP_DSR_PRIORITY_2 0x00000004 -#define DSRWP_DSR_PRIORITY_3 0x00000006 -#define DSRWP_DSR_RQ_PENDING 0x00000008 - -/* - * The following defines are for the flags in the trap write port register. - */ -#define TWPR_TW_MASK 0x0000FFFF -#define TWPR_TW_SHIFT 0 - -/* - * The following defines are for the flags in the stack pointer write - * register. - */ -#define SPWR_STKP_MASK 0x0000000F -#define SPWR_STKP_SHIFT 0 - -/* - * The following defines are for the flags in the SP interrupt register. - */ -#define SPIR_FRI 0x00000001 -#define SPIR_DOI 0x00000002 -#define SPIR_GPI2 0x00000004 -#define SPIR_GPI3 0x00000008 -#define SPIR_IP0 0x00000010 -#define SPIR_IP1 0x00000020 -#define SPIR_IP2 0x00000040 -#define SPIR_IP3 0x00000080 - -/* - * The following defines are for the flags in the functional group 1 register. - */ -#define FGR1_F1S_MASK 0x0000FFFF -#define FGR1_F1S_SHIFT 0 - -/* - * The following defines are for the flags in the SP clock status register. - */ -#define SPCS_FRI 0x00000001 -#define SPCS_DOI 0x00000002 -#define SPCS_GPI2 0x00000004 -#define SPCS_GPI3 0x00000008 -#define SPCS_IP0 0x00000010 -#define SPCS_IP1 0x00000020 -#define SPCS_IP2 0x00000040 -#define SPCS_IP3 0x00000080 -#define SPCS_SPRUN 0x00000100 -#define SPCS_SLEEP 0x00000200 -#define SPCS_FG 0x00000400 -#define SPCS_ORUN 0x00000800 -#define SPCS_IRQ 0x00001000 -#define SPCS_FGN_MASK 0x0000E000 -#define SPCS_FGN_SHIFT 13 - -/* - * The following defines are for the flags in the SP DMA requestor status - * register. - */ -#define SDSR_DCS_MASK 0x000000FF -#define SDSR_DCS_SHIFT 0 -#define SDSR_DCS_NONE 0x00000007 - -/* - * The following defines are for the flags in the frame timer register. - */ -#define FRMT_FTV_MASK 0x0000FFFF -#define FRMT_FTV_SHIFT 0 - -/* - * The following defines are for the flags in the frame timer current count - * register. - */ -#define FRCC_FCC_MASK 0x0000FFFF -#define FRCC_FCC_SHIFT 0 - -/* - * The following defines are for the flags in the frame timer save count - * register. - */ -#define FRSC_FCS_MASK 0x0000FFFF -#define FRSC_FCS_SHIFT 0 - -/* - * The following define the various flags stored in the scatter/gather - * descriptors. - */ -#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8 -#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000 -#define DMA_SG_SAMPLE_END_FLAG 0x10000000 -#define DMA_SG_LOOP_END_FLAG 0x20000000 -#define DMA_SG_SIGNAL_END_FLAG 0x40000000 -#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000 -#define DMA_SG_NEXT_ENTRY_SHIFT 3 -#define DMA_SG_SAMPLE_END_SHIFT 16 - -/* - * The following define the offsets of the fields within the on-chip generic - * DMA requestor. - */ -#define DMA_RQ_CONTROL1 0x00000000 -#define DMA_RQ_CONTROL2 0x00000004 -#define DMA_RQ_SOURCE_ADDR 0x00000008 -#define DMA_RQ_DESTINATION_ADDR 0x0000000C -#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010 -#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014 -#define DMA_RQ_LOOP_START_ADDR 0x00000018 -#define DMA_RQ_POST_LOOP_ADDR 0x0000001C -#define DMA_RQ_PAGE_MAP_ADDR 0x00000020 - -/* - * The following defines are for the flags in the first control word of the - * on-chip generic DMA requestor. - */ -#define DMA_RQ_C1_COUNT_MASK 0x000003FF -#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000 -#define DMA_RQ_C1_SOURCE_GATHER 0x00002000 -#define DMA_RQ_C1_DONE_FLAG 0x00004000 -#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000 -#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000 -#define DMA_RQ_C1_FULL_PAGE 0x00000000 -#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000 -#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000 -#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000 -#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000 -#define DMA_RQ_C1_NOT_LOOP_END 0x00000000 -#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000 -#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000 -#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000 -#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000 -#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000 -#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000 -#define DMA_RQ_C1_PM_RESERVED 0x00200000 -#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000 -#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000 -#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000 -#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000 -#define DMA_RQ_C1_DEST_LINEAR 0x00000000 -#define DMA_RQ_C1_DEST_MOD16 0x01000000 -#define DMA_RQ_C1_DEST_MOD32 0x02000000 -#define DMA_RQ_C1_DEST_MOD64 0x03000000 -#define DMA_RQ_C1_DEST_MOD128 0x04000000 -#define DMA_RQ_C1_DEST_MOD256 0x05000000 -#define DMA_RQ_C1_DEST_MOD512 0x06000000 -#define DMA_RQ_C1_DEST_MOD1024 0x07000000 -#define DMA_RQ_C1_DEST_ON_HOST 0x08000000 -#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000 -#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000 -#define DMA_RQ_C1_SOURCE_MOD16 0x10000000 -#define DMA_RQ_C1_SOURCE_MOD32 0x20000000 -#define DMA_RQ_C1_SOURCE_MOD64 0x30000000 -#define DMA_RQ_C1_SOURCE_MOD128 0x40000000 -#define DMA_RQ_C1_SOURCE_MOD256 0x50000000 -#define DMA_RQ_C1_SOURCE_MOD512 0x60000000 -#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000 -#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000 -#define DMA_RQ_C1_COUNT_SHIFT 0 - -/* - * The following defines are for the flags in the second control word of the - * on-chip generic DMA requestor. - */ -#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F -#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300 -#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000 -#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100 -#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200 -#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300 -#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000 -#define DMA_RQ_C2_AC_NONE 0x00000000 -#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000 -#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000 -#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000 -#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000 -#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000 -#define DMA_RQ_C2_LOOP_MASK 0x30000000 -#define DMA_RQ_C2_NO_LOOP 0x00000000 -#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000 -#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000 -#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000 -#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000 -#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000 -#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0 -#define DMA_RQ_C2_LOOP_END_SHIFT 16 - -/* - * The following defines are for the flags in the source and destination words - * of the on-chip generic DMA requestor. - */ -#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF -#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000 -#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000 -#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000 -#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000 -#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000 -#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000 -#define DMA_RQ_SD_END_FLAG 0x40000000 -#define DMA_RQ_SD_ERROR_FLAG 0x80000000 -#define DMA_RQ_SD_ADDRESS_SHIFT 0 - -/* - * The following defines are for the flags in the page map address word of the - * on-chip generic DMA requestor. - */ -#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8 -#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000 -#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3 -#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12 - -#define BA1_VARIDEC_BUF_1 0x000 - -#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */ -#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */ -#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */ -#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */ -#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */ -#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */ -#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */ - -#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */ -#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */ -#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */ -#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */ -#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */ -#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */ -#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */ -#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */ - -#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */ -#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */ -#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ -#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ - -/* - * - */ - -#define CS461X_MODE_OUTPUT (1<<0) /* MIDI UART - output */ -#define CS461X_MODE_INPUT (1<<1) /* MIDI UART - input */ - -//**************************************************************************** -// -// The following define the offsets of the AC97 shadow registers, which appear -// as a virtual extension to the base address register zero memory range. -// -//**************************************************************************** -#define AC97_REG_OFFSET_MASK 0x0000007EL -#define AC97_CODEC_NUMBER_MASK 0x00003000L - -#define BA0_AC97_RESET 0x00001000L -#define BA0_AC97_MASTER_VOLUME 0x00001002L -#define BA0_AC97_HEADPHONE_VOLUME 0x00001004L -#define BA0_AC97_MASTER_VOLUME_MONO 0x00001006L -#define BA0_AC97_MASTER_TONE 0x00001008L -#define BA0_AC97_PC_BEEP_VOLUME 0x0000100AL -#define BA0_AC97_PHONE_VOLUME 0x0000100CL -#define BA0_AC97_MIC_VOLUME 0x0000100EL -#define BA0_AC97_LINE_IN_VOLUME 0x00001010L -#define BA0_AC97_CD_VOLUME 0x00001012L -#define BA0_AC97_VIDEO_VOLUME 0x00001014L -#define BA0_AC97_AUX_VOLUME 0x00001016L -#define BA0_AC97_PCM_OUT_VOLUME 0x00001018L -#define BA0_AC97_RECORD_SELECT 0x0000101AL -#define BA0_AC97_RECORD_GAIN 0x0000101CL -#define BA0_AC97_RECORD_GAIN_MIC 0x0000101EL -#define BA0_AC97_GENERAL_PURPOSE 0x00001020L -#define BA0_AC97_3D_CONTROL 0x00001022L -#define BA0_AC97_MODEM_RATE 0x00001024L -#define BA0_AC97_POWERDOWN 0x00001026L -#define BA0_AC97_EXT_AUDIO_ID 0x00001028L -#define BA0_AC97_EXT_AUDIO_POWER 0x0000102AL -#define BA0_AC97_PCM_FRONT_DAC_RATE 0x0000102CL -#define BA0_AC97_PCM_SURR_DAC_RATE 0x0000102EL -#define BA0_AC97_PCM_LFE_DAC_RATE 0x00001030L -#define BA0_AC97_PCM_LR_ADC_RATE 0x00001032L -#define BA0_AC97_MIC_ADC_RATE 0x00001034L -#define BA0_AC97_6CH_VOL_C_LFE 0x00001036L -#define BA0_AC97_6CH_VOL_SURROUND 0x00001038L -#define BA0_AC97_RESERVED_3A 0x0000103AL -#define BA0_AC97_EXT_MODEM_ID 0x0000103CL -#define BA0_AC97_EXT_MODEM_POWER 0x0000103EL -#define BA0_AC97_LINE1_CODEC_RATE 0x00001040L -#define BA0_AC97_LINE2_CODEC_RATE 0x00001042L -#define BA0_AC97_HANDSET_CODEC_RATE 0x00001044L -#define BA0_AC97_LINE1_CODEC_LEVEL 0x00001046L -#define BA0_AC97_LINE2_CODEC_LEVEL 0x00001048L -#define BA0_AC97_HANDSET_CODEC_LEVEL 0x0000104AL -#define BA0_AC97_GPIO_PIN_CONFIG 0x0000104CL -#define BA0_AC97_GPIO_PIN_TYPE 0x0000104EL -#define BA0_AC97_GPIO_PIN_STICKY 0x00001050L -#define BA0_AC97_GPIO_PIN_WAKEUP 0x00001052L -#define BA0_AC97_GPIO_PIN_STATUS 0x00001054L -#define BA0_AC97_MISC_MODEM_AFE_STAT 0x00001056L -#define BA0_AC97_RESERVED_58 0x00001058L -#define BA0_AC97_CRYSTAL_REV_N_FAB_ID 0x0000105AL -#define BA0_AC97_TEST_AND_MISC_CTRL 0x0000105CL -#define BA0_AC97_AC_MODE 0x0000105EL -#define BA0_AC97_MISC_CRYSTAL_CONTROL 0x00001060L -#define BA0_AC97_LINE1_HYPRID_CTRL 0x00001062L -#define BA0_AC97_VENDOR_RESERVED_64 0x00001064L -#define BA0_AC97_VENDOR_RESERVED_66 0x00001066L -#define BA0_AC97_SPDIF_CONTROL 0x00001068L -#define BA0_AC97_VENDOR_RESERVED_6A 0x0000106AL -#define BA0_AC97_VENDOR_RESERVED_6C 0x0000106CL -#define BA0_AC97_VENDOR_RESERVED_6E 0x0000106EL -#define BA0_AC97_VENDOR_RESERVED_70 0x00001070L -#define BA0_AC97_VENDOR_RESERVED_72 0x00001072L -#define BA0_AC97_VENDOR_RESERVED_74 0x00001074L -#define BA0_AC97_CAL_ADDRESS 0x00001076L -#define BA0_AC97_CAL_DATA 0x00001078L -#define BA0_AC97_VENDOR_RESERVED_7A 0x0000107AL -#define BA0_AC97_VENDOR_ID1 0x0000107CL -#define BA0_AC97_VENDOR_ID2 0x0000107EL -#endif /* __CS461X_H */ diff -Nru a/drivers/sound/cs461x_image.h b/drivers/sound/cs461x_image.h --- a/drivers/sound/cs461x_image.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,322 +0,0 @@ -/**************************************************************************** - * "CWCIMAGE.H"-- For CS46XX. Ver 1.04 - * Copyright 1998-2001 (c) Cirrus Logic Corp. - * Version 1.04 - **************************************************************************** - */ -#ifndef __CS_IMAGE_H -#define __CS_IMAGE_H - -#define CLEAR__COUNT 3 -#define FILL__COUNT 4 -#define BA1__DWORD_SIZE 13*1024+512 - -static struct -{ - unsigned BA1__DestByteOffset; - unsigned BA1__SourceSize; -} ClrStat[CLEAR__COUNT] ={ {0x00000000, 0x00003000 }, - {0x00010000, 0x00003800 }, - {0x00020000, 0x00007000 } }; - -static u32 FillArray1[]={ -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000163,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00200040,0x00008010,0x00000000, -0x00000000,0x80000001,0x00000001,0x00060000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00900080,0x00000173,0x00000000, -0x00000000,0x00000010,0x00800000,0x00900000, -0xf2c0000f,0x00000200,0x00000000,0x00010600, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000163,0x330300c2, -0x06000000,0x00000000,0x80008000,0x80008000, -0x3fc0000f,0x00000301,0x00010400,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00b00000,0x00d0806d,0x330480c3, -0x04800000,0x00000001,0x00800001,0x0000ffff, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x066a0600,0x06350070,0x0000929d,0x929d929d, -0x00000000,0x0000735a,0x00000600,0x00000000, -0x929d735a,0x00000000,0x00010000,0x735a735a, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x0000804f,0x000000c3, -0x05000000,0x00a00010,0x00000000,0x80008000, -0x00000000,0x00000000,0x00000700,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000080,0x00a00000,0x0000809a,0x000000c2, -0x07400000,0x00000000,0x80008000,0xffffffff, -0x00c80028,0x00005555,0x00000000,0x000107a0, -0x00c80028,0x000000c2,0x06800000,0x00000000, -0x06e00080,0x00300000,0x000080bb,0x000000c9, -0x07a00000,0x04000000,0x80008000,0xffffffff, -0x00c80028,0x00005555,0x00000000,0x00000780, -0x00c80028,0x000000c5,0xff800000,0x00000000, -0x00640080,0x00c00000,0x00008197,0x000000c9, -0x07800000,0x04000000,0x80008000,0xffffffff, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x0000805e,0x000000c1, -0x00000000,0x00800000,0x80008000,0x80008000, -0x00020000,0x0000ffff,0x00000000,0x00000000}; - -static u32 FillArray2[]={ -0x929d0600,0x929d929d,0x929d929d,0x929d0000, -0x929d929d,0x929d929d,0x929d929d,0x929d929d, -0x929d929d,0x00100635,0x060b013f,0x00000004, -0x00000001,0x007a0002,0x00000000,0x066e0610, -0x0105929d,0x929d929d,0x929d929d,0x929d929d, -0x929d929d,0xa431ac75,0x0001735a,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051, -0x00000000,0x929d929d,0x929d929d,0x929d929d, -0x929d929d,0x929d929d,0x929d929d,0x929d929d, -0x929d929d,0x929d929d,0x00000000,0x06400136, -0x0000270f,0x00010000,0x007a0000,0x00000000, -0x068e0645,0x0105929d,0x929d929d,0x929d929d, -0x929d929d,0x929d929d,0xa431ac75,0x0001735a, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, -0x735a0100,0x00000000,0x00000000,0x00000000}; - -static u32 FillArray3[]={ -0x00000000,0x00000000,0x00000000,0x00010004}; - -static u32 FillArray4[]={ -0x00040730,0x00001002,0x000f619e,0x00001003, -0x00001705,0x00001400,0x000a411e,0x00001003, -0x00040730,0x00001002,0x000f619e,0x00001003, -0x00009705,0x00001400,0x000a411e,0x00001003, -0x00040730,0x00001002,0x000f619e,0x00001003, -0x00011705,0x00001400,0x000a411e,0x00001003, -0x00040730,0x00001002,0x000f619e,0x00001003, -0x00019705,0x00001400,0x000a411e,0x00001003, -0x00040730,0x00001002,0x000f619e,0x00001003, -0x00021705,0x00001400,0x000a411e,0x00001003, -0x00040730,0x00001002,0x000f619e,0x00001003, -0x00029705,0x00001400,0x000a411e,0x00001003, -0x00040730,0x00001002,0x000f619e,0x00001003, -0x00031705,0x00001400,0x000a411e,0x00001003, -0x00040730,0x00001002,0x000f619e,0x00001003, -0x00039705,0x00001400,0x000a411e,0x00001003, -0x000fe19e,0x00001003,0x0009c730,0x00001003, -0x0008e19c,0x00001003,0x000083c1,0x00093040, -0x00098730,0x00001002,0x000ee19e,0x00001003, -0x00009705,0x00001400,0x000a211e,0x00001003, -0x00098730,0x00001002,0x000ee19e,0x00001003, -0x00011705,0x00001400,0x000a211e,0x00001003, -0x00098730,0x00001002,0x000ee19e,0x00001003, -0x00019705,0x00001400,0x000a211e,0x00001003, -0x00098730,0x00001002,0x000ee19e,0x00001003, -0x00021705,0x00001400,0x000a211e,0x00001003, -0x00098730,0x00001002,0x000ee19e,0x00001003, -0x00029705,0x00001400,0x000a211e,0x00001003, -0x00098730,0x00001002,0x000ee19e,0x00001003, -0x00031705,0x00001400,0x000a211e,0x00001003, -0x00098730,0x00001002,0x000ee19e,0x00001003, -0x00039705,0x00001400,0x000a211e,0x00001003, -0x0000a730,0x00001008,0x000e2730,0x00001002, -0x0000a731,0x00001002,0x0000a731,0x00001002, -0x0000a731,0x00001002,0x0000a731,0x00001002, -0x0000a731,0x00001002,0x0000a731,0x00001002, -0x00000000,0x00000000,0x000f619c,0x00001003, -0x0007f801,0x000c0000,0x00000037,0x00001000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x000c0000,0x00000000,0x00000000, -0x0000373c,0x00001000,0x00000000,0x00000000, -0x000ee19c,0x00001003,0x0007f801,0x000c0000, -0x00000037,0x00001000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x0000273c,0x00001000, -0x00000033,0x00001000,0x000e679e,0x00001003, -0x00007705,0x00001400,0x000ac71e,0x00001003, -0x00087fc1,0x000c3be0,0x0007f801,0x000c0000, -0x00000037,0x00001000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x0000a730,0x00001003, -0x00000033,0x00001000,0x0007f801,0x000c0000, -0x00000037,0x00001000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x000c0000, -0x00000032,0x00001000,0x0000273d,0x00001000, -0x0004a730,0x00001003,0x00000f41,0x00097140, -0x0000a841,0x0009b240,0x0000a0c1,0x0009f040, -0x0001c641,0x00093540,0x0001cec1,0x0009b5c0, -0x00000000,0x00000000,0x0001bf05,0x0003fc40, -0x00002725,0x000aa400,0x00013705,0x00093a00, -0x0000002e,0x0009d6c0,0x00038630,0x00001004, -0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000, -0x00000000,0x000c70e0,0x0007d182,0x0002c640, -0x00000630,0x00001004,0x000799b8,0x0002c6c0, -0x00031705,0x00092240,0x00039f05,0x000932c0, -0x0003520a,0x00000000,0x00040731,0x0000100b, -0x00010705,0x000b20c0,0x00000000,0x000eba44, -0x00032108,0x000c60c4,0x00065208,0x000c2917, -0x000406b0,0x00001007,0x00012f05,0x00036880, -0x0002818e,0x000c0000,0x0004410a,0x00000000, -0x00040630,0x00001007,0x00029705,0x000c0000, -0x00000000,0x00000000,0x00003fc1,0x0003fc40, -0x000037c1,0x00091b40,0x00003fc1,0x000911c0, -0x000037c1,0x000957c0,0x00003fc1,0x000951c0, -0x000037c1,0x00000000,0x00003fc1,0x000991c0, -0x000037c1,0x00000000,0x00003fc1,0x0009d1c0, -0x000037c1,0x00000000,0x0001ccc1,0x000915c0, -0x0001c441,0x0009d800,0x0009cdc1,0x00091240, -0x0001c541,0x00091d00,0x0009cfc1,0x00095240, -0x0001c741,0x00095c80,0x000e8ca9,0x00099240, -0x000e85ad,0x00095640,0x00069ca9,0x00099d80, -0x000e952d,0x00099640,0x000eaca9,0x0009d6c0, -0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80, -0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0, -0x000ec5ad,0x0009da40,0x000edca9,0x0009d300, -0x000a6e0a,0x00001000,0x000ed52d,0x00091e40, -0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40, -0x0006fca9,0x00002500,0x000fb208,0x000c59a0, -0x000ef52d,0x0009de40,0x00068ca9,0x000912c1, -0x000683ad,0x00095241,0x00020f05,0x000991c1, -0x00000000,0x00000000,0x00086f88,0x00001000, -0x0009cf81,0x000b5340,0x0009c701,0x000b92c0, -0x0009de81,0x000bd300,0x0009d601,0x000b1700, -0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0, -0x000a0f81,0x000bd740,0x00020701,0x000b5c80, -0x000a1681,0x000b97c0,0x00021601,0x00002500, -0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0, -0x00021681,0x00002d00,0x00020f81,0x000bd800, -0x000a0701,0x000b5bc0,0x00021601,0x00003500, -0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0, -0x00021681,0x00003d00,0x00020f81,0x000b1d00, -0x000a0701,0x000b1fc0,0x00021601,0x00020500, -0x00020f81,0x000b1341,0x000a0701,0x000b9fc0, -0x00021681,0x00020d00,0x00020f81,0x000bde80, -0x000a0701,0x000bdfc0,0x00021601,0x00021500, -0x00020f81,0x000b9341,0x00020701,0x000b53c1, -0x00021681,0x00021d00,0x000a0f81,0x000d0380, -0x0000b601,0x000b15c0,0x00007b01,0x00000000, -0x00007b81,0x000bd1c0,0x00007b01,0x00000000, -0x00007b81,0x000b91c0,0x00007b01,0x000b57c0, -0x00007b81,0x000b51c0,0x00007b01,0x000b1b40, -0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0, -0x0007e488,0x000d7e45,0x00000000,0x000d7a44, -0x0007e48a,0x00000000,0x00011f05,0x00084080, -0x00000000,0x00000000,0x00001705,0x000b3540, -0x00008a01,0x000bf040,0x00007081,0x000bb5c0, -0x00055488,0x00000000,0x0000d482,0x0003fc40, -0x0003fc88,0x00000000,0x0001e401,0x000b3a00, -0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784, -0x000c86b0,0x00001007,0x00008281,0x000bb240, -0x0000b801,0x000b7140,0x00007888,0x00000000, -0x0000073c,0x00001000,0x0007f188,0x000c0000, -0x00000000,0x00000000,0x00055288,0x000c555c, -0x0005528a,0x000c0000,0x0009fa88,0x000c5d00, -0x0000fa88,0x00000000,0x00000032,0x00001000, -0x0000073d,0x00001000,0x0007f188,0x000c0000, -0x00000000,0x00000000,0x0008c01c,0x00001003, -0x00002705,0x00001008,0x0008b201,0x000c1392, -0x0000ba01,0x00000000,0x00008731,0x00001400, -0x0004c108,0x000fe0c4,0x00057488,0x00000000, -0x000a6388,0x00001001,0x0008b334,0x000bc141, -0x0003020e,0x00000000,0x000886b0,0x00001008, -0x00003625,0x000c5dfa,0x000a638a,0x00001001, -0x0008020e,0x00001002,0x0008a6b0,0x00001008, -0x0007f301,0x00000000,0x00000000,0x00000000, -0x00002725,0x000a8c40,0x000000ae,0x00000000, -0x000d8630,0x00001008,0x00000000,0x000c74e0, -0x0007d182,0x0002d640,0x000a8630,0x00001008, -0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5, -0x0007420a,0x000c0000,0x00062208,0x000c4117, -0x00070630,0x00001009,0x00000000,0x000c0000, -0x0001022e,0x00000000,0x0003a630,0x00001009, -0x00000000,0x000c0000,0x00000036,0x00001000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x0002a730,0x00001008,0x0007f801,0x000c0000, -0x00000037,0x00001000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x0002a730,0x00001008, -0x00000033,0x00001000,0x0002a705,0x00001008, -0x00007a01,0x000c0000,0x000e6288,0x000d550a, -0x0006428a,0x00000000,0x00060730,0x0000100a, -0x00000000,0x000c0000,0x00000000,0x00000000, -0x0007aab0,0x00034880,0x00078fb0,0x0000100b, -0x00057488,0x00000000,0x00033b94,0x00081140, -0x000183ae,0x00000000,0x000786b0,0x0000100b, -0x00022f05,0x000c3545,0x0000eb8a,0x00000000, -0x00042731,0x00001003,0x0007aab0,0x00034880, -0x00048fb0,0x0000100a,0x00057488,0x00000000, -0x00033b94,0x00081140,0x000183ae,0x00000000, -0x000806b0,0x0000100b,0x00022f05,0x00000000, -0x00007401,0x00091140,0x00048f05,0x000951c0, -0x00042731,0x00001003,0x0000473d,0x00001000, -0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, -0x000fe19e,0x00001003,0x00000000,0x00000000, -0x0008e19c,0x00001003,0x000083c1,0x00093040, -0x00000f41,0x00097140,0x0000a841,0x0009b240, -0x0000a0c1,0x0009f040,0x0001c641,0x00093540, -0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44, -0x00055208,0x00000000,0x00010705,0x000a2880, -0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5, -0x0004ef0a,0x000c0000,0x00012f05,0x00036880, -0x00065308,0x000c2997,0x000d86b0,0x0000100a, -0x0004410a,0x000d40c7,0x00000000,0x00000000, -0x00080730,0x00001004,0x00056f0a,0x000ea105, -0x00000000,0x00000000,0x0000473d,0x00001000, -0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, -0x0000273d,0x00001000,0x00000000,0x000eba44, -0x00048f05,0x0000f440,0x00007401,0x0000f7c0, -0x00000734,0x00001000,0x00010705,0x000a6880, -0x00006a88,0x000c75c4,0x00000000,0x000e5084, -0x00000000,0x000eba44,0x00087401,0x000e4782, -0x00000734,0x00001000,0x00010705,0x000a6880, -0x00006a88,0x000c75c4,0x0007c108,0x000c0000, -0x0007e721,0x000bed40,0x00005f25,0x000badc0, -0x0003ba97,0x000beb80,0x00065590,0x000b2e00, -0x00033217,0x00003ec0,0x00065590,0x000b8e40, -0x0003ed80,0x000491c0,0x00073fb0,0x00074c80, -0x000283a0,0x0000100c,0x000ee388,0x00042970, -0x00008301,0x00021ef2,0x000b8f14,0x0000000f, -0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6, -0x000032ac,0x000c3916,0x0004edc2,0x00074c80, -0x00078898,0x00001000,0x00038894,0x00000032, -0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6, -0x0004edc2,0x000c1956,0x0000722c,0x00034a00, -0x00041705,0x0009ed40,0x00058730,0x00001400, -0x000d7488,0x000c3a00,0x00048f05,0x00000000}; - -static struct -{ u32 Offset; - u32 Size; - u32 *pFill; -} FillStat[FILL__COUNT] = { - {0x00000000, sizeof(FillArray1), FillArray1}, - {0x00001800, sizeof(FillArray2), FillArray2}, - {0x000137f0, sizeof(FillArray3), FillArray3}, - {0x00020000, sizeof(FillArray4), FillArray4} - }; - - -#endif diff -Nru a/drivers/sound/cs46xx.c b/drivers/sound/cs46xx.c --- a/drivers/sound/cs46xx.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,5727 +0,0 @@ -/* - * Crystal SoundFusion CS46xx driver - * - * Copyright 1998-2001 Cirrus Logic Corporation - * - * Copyright 1999-2000 Jaroslav Kysela - * Copyright 2000 Alan Cox - * - * The core of this code is taken from the ALSA project driver by - * Jaroslav. Please send Jaroslav the credit for the driver and - * report bugs in this port to - * - * 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. - * Current maintainers: - * Cirrus Logic Corporation, Thomas Woller (tw) - * - * Nils Faerber (nf) - * - * Thanks to David Pollard for testing. - * - * Changes: - * 20000909-nf Changed cs_read, cs_write and drain_dac - * 20001025-tw Separate Playback/Capture structs and buffers. - * Added Scatter/Gather support for Playback. - * Added Capture. - * 20001027-nf Port to kernel 2.4.0-test9, some clean-ups - * Start of powermanagement support (CS46XX_PM). - * 20001128-tw Add module parm for default buffer order. - * added DMA_GFP flag to kmalloc dma buffer allocs. - * backfill silence to eliminate stuttering on - * underruns. - * 20001201-tw add resyncing of swptr on underruns. - * 20001205-tw-nf fixed GETOSPACE ioctl() after open() - * 20010113-tw patch from Hans Grobler general cleanup. - * 20010117-tw 2.4.0 pci cleanup, wrapper code for 2.2.16-2.4.0 - * 20010118-tw basic PM support for 2.2.16+ and 2.4.0/2.4.2. - * 20010228-dh patch from David Huggins - cs_update_ptr recursion. - * 20010409-tw add hercules game theatre XP amp code. - * 20010420-tw cleanup powerdown/up code. - * 20010521-tw eliminate pops, and fixes for powerdown. - * 20010525-tw added fixes for thinkpads with powerdown logic. - * 20010723-sh patch from Horms (Simon Horman) - - * SOUND_PCM_READ_BITS returns bits as set in driver - * rather than a logical or of the possible values. - * Various ioctls handle the case where the device - * is open for reading or writing but not both better. - * - * Status: - * Playback/Capture supported from 8k-48k. - * 16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported. - * - * APM/PM - 2.2.x APM is enabled and functioning fine. APM can also - * be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro - * definition. - * - * Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp, - * so, use the drain/polarity to enable. - * hercules_egpio_disable set to 1, will force a 0 to EGPIODR. - * - * VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control - * the external amplifier for the "back" speakers, since we do not - * support the secondary codec then this external amp is also not - * turned on. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cs46xxpm-24.h" -#include "cs46xx_wrapper-24.h" - -#include "cs461x.h" - -/* MIDI buffer sizes */ -#define CS_MIDIINBUF 500 -#define CS_MIDIOUTBUF 500 - -#define ADC_RUNNING 1 -#define DAC_RUNNING 2 - -#define CS_FMT_16BIT 1 /* These are fixed in fact */ -#define CS_FMT_STEREO 2 -#define CS_FMT_MASK 3 - -#define CS_TYPE_ADC 1 -#define CS_TYPE_DAC 2 - -#define CS_TRUE 1 -#define CS_FALSE 0 - -#define CS_INC_USE_COUNT(m) (atomic_inc(m)) -#define CS_DEC_USE_COUNT(m) (atomic_dec(m)) -#define CS_DEC_AND_TEST(m) (atomic_dec_and_test(m)) -#define CS_IN_USE(m) (atomic_read(m) != 0) - -#define CS_DBGBREAKPOINT {__asm__("INT $3");} -/* - * CS461x definitions - */ - -#define CS461X_BA0_SIZE 0x2000 -#define CS461X_BA1_DATA0_SIZE 0x3000 -#define CS461X_BA1_DATA1_SIZE 0x3800 -#define CS461X_BA1_PRG_SIZE 0x7000 -#define CS461X_BA1_REG_SIZE 0x0100 - -#define GOF_PER_SEC 200 - -#define CSDEBUG_INTERFACE 1 -#define CSDEBUG 1 -/* - * Turn on/off debugging compilation by using 1/0 respectively for CSDEBUG - * - * - * 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 -/* - * 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_MIDI_WRITE 0x00000040 /* write information for midi */ -#define CS_MIDI_READ 0x00000080 /* read information for midi */ -#define CS_MPU401_WRITE 0x00000100 /* write information for mpu401 */ -#define CS_MPU401_READ 0x00000200 /* read information for mpu401 */ -#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_PM 0x00004000 /* PM */ -#define CS_TMP 0x10000000 /* tmp debug mask bit */ - -#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend -#define CS_IOCTL_CMD_RESUME 0x2 // resume - -#if CSDEBUG -static unsigned long cs_debuglevel=1; /* levels range from 1-9 */ -MODULE_PARM(cs_debuglevel, "i"); -static unsigned long cs_debugmask=CS_INIT | CS_ERROR; /* use CS_DBGOUT with various mask values */ -MODULE_PARM(cs_debugmask, "i"); -#endif -static unsigned long hercules_egpio_disable=0; /* if non-zero set all EGPIO to 0 */ -MODULE_PARM(hercules_egpio_disable, "i"); -static unsigned long initdelay=700; /* PM delay in millisecs */ -MODULE_PARM(initdelay, "i"); -static unsigned long powerdown=-1; /* turn on/off powerdown processing in driver */ -MODULE_PARM(powerdown, "i"); -#define DMABUF_DEFAULTORDER 3 -static unsigned long defaultorder=DMABUF_DEFAULTORDER; -MODULE_PARM(defaultorder, "i"); - -static int external_amp; -MODULE_PARM(external_amp, "i"); -static int thinkpad; -MODULE_PARM(thinkpad, "i"); - -/* -* set the powerdown module parm to 0 to disable all -* powerdown. also set thinkpad to 1 to disable powerdown, -* but also to enable the clkrun functionality. -*/ -static unsigned cs_powerdown=1; -static unsigned cs_laptop_wait=1; - -/* An instance of the 4610 channel */ -struct cs_channel -{ - int used; - int num; - void *state; -}; - -#define CS46XX_MAJOR_VERSION "1" -#define CS46XX_MINOR_VERSION "28" - -#ifdef __ia64__ -#define CS46XX_ARCH "64" //architecture key -#else -#define CS46XX_ARCH "32" //architecture key -#endif - -struct list_head cs46xx_devs = { &cs46xx_devs, &cs46xx_devs }; - -/* magic numbers to protect our data structures */ -#define CS_CARD_MAGIC 0x43525553 /* "CRUS" */ -#define CS_STATE_MAGIC 0x4c4f4749 /* "LOGI" */ -#define NR_HW_CH 3 - -/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ -#define NR_AC97 2 - -static const unsigned sample_size[] = { 1, 2, 2, 4 }; -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -/* "software" or virtual channel, an instance of opened /dev/dsp */ -struct cs_state { - unsigned int magic; - struct cs_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; - - struct dmabuf { - /* wave sample stuff */ - unsigned int rate; - unsigned char fmt, enable; - - /* hardware channel */ - struct cs_channel *channel; - int pringbuf; /* Software ring slot */ - void *pbuf; /* 4K hardware DMA buffer */ - - /* OSS buffer management stuff */ - void *rawbuf; - dma_addr_t dma_handle; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - unsigned divisor; - unsigned type; - void *tmpbuff; /* tmp buffer for sample conversions */ - dma_addr_t dmaaddr; - dma_addr_t dmaaddr_tmpbuff; - unsigned buforder_tmpbuff; /* Log base 2 of size in bytes.. */ - - /* 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 comsumed or been generated by dma machine */ - unsigned total_bytes; /* total bytes dmaed by hardware */ - unsigned blocks; /* total blocks */ - - unsigned error; /* number of over/underruns */ - unsigned underrun; /* underrun pending before next write has occurred */ - wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ - - /* redundant, but makes calculations easier */ - unsigned fragsize; - unsigned dmasize; - unsigned fragsamples; - - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned endcleared:1; - unsigned SGok:1; - unsigned update_flag; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dmabuf; - /* Guard against mmap/write/read races */ - struct semaphore sem; -}; - -struct cs_card { - struct cs_channel channel[2]; - unsigned int magic; - - /* We keep cs461x cards in a linked list */ - struct cs_card *next; - - /* The cs461x has a certain amount of cross channel interaction - so we use a single per card lock */ - spinlock_t lock; - - /* mixer use count */ - atomic_t mixer_use_cnt; - - /* PCI device stuff */ - struct pci_dev * pci_dev; - struct list_head list; - - unsigned int pctl, cctl; /* Hardware DMA flag sets */ - - /* soundcore stuff */ - int dev_audio; - int dev_midi; - - /* structures for abstraction of hardware facilities, codecs, banks and channels*/ - struct ac97_codec *ac97_codec[NR_AC97]; - struct cs_state *states[2]; - - u16 ac97_features; - - int amplifier; /* Amplifier control */ - void (*amplifier_ctrl)(struct cs_card *, int); - void (*amp_init)(struct cs_card *); - - int active; /* Active clocking */ - void (*active_ctrl)(struct cs_card *, int); - - /* hardware resources */ - unsigned long ba0_addr; - unsigned long ba1_addr; - u32 irq; - - /* mappings */ - void *ba0; - union - { - struct - { - u8 *data0; - u8 *data1; - u8 *pmem; - u8 *reg; - } name; - u8 *idx[4]; - } ba1; - - /* Function support */ - struct cs_channel *(*alloc_pcm_channel)(struct cs_card *); - struct cs_channel *(*alloc_rec_pcm_channel)(struct cs_card *); - void (*free_pcm_channel)(struct cs_card *, int chan); - - /* /dev/midi stuff */ - struct { - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t open_wait; - wait_queue_head_t iwait; - wait_queue_head_t owait; - spinlock_t lock; - unsigned char ibuf[CS_MIDIINBUF]; - unsigned char obuf[CS_MIDIOUTBUF]; - mode_t open_mode; - struct semaphore open_sem; - } midi; - struct cs46xx_pm pm; -}; - -static int cs_open_mixdev(struct inode *inode, struct file *file); -static int cs_release_mixdev(struct inode *inode, struct file *file); -static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg); -static int cs_hardware_init(struct cs_card *card); -static int cs46xx_powerup(struct cs_card *card, unsigned int type); -static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag); -static void cs461x_clear_serial_FIFOs(struct cs_card *card, int type); -static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state); -static int cs46xx_resume_tbl(struct pci_dev *pcidev); - -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; -} - -#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) -#define SOUND_MIXER_CS_APM _SIOWR('M',124, int) - -void printioctl(unsigned int x) -{ - unsigned int i; - unsigned char vidx; - /* these values are incorrect for the ac97 driver, fix. - * 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: ") ); - break; - case SOUND_MIXER_CS_GETDBGLEVEL: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGLEVEL: ") ); - break; - case SOUND_MIXER_CS_SETDBGMASK: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGMASK: ") ); - break; - case SOUND_MIXER_CS_SETDBGLEVEL: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGLEVEL: ") ); - break; - case OSS_GETVERSION: - CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION: ") ); - break; - case SNDCTL_DSP_SYNC: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC: ") ); - break; - case SNDCTL_DSP_SETDUPLEX: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX: ") ); - break; - case SNDCTL_DSP_GETCAPS: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS: ") ); - break; - case SNDCTL_DSP_RESET: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET: ") ); - break; - case SNDCTL_DSP_SPEED: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED: ") ); - break; - case SNDCTL_DSP_STEREO: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO: ") ); - break; - case SNDCTL_DSP_CHANNELS: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS: ") ); - break; - case SNDCTL_DSP_GETFMTS: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS: ") ); - break; - case SNDCTL_DSP_SETFMT: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT: ") ); - break; - case SNDCTL_DSP_POST: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST: ") ); - break; - case SNDCTL_DSP_GETTRIGGER: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER: ") ); - break; - case SNDCTL_DSP_SETTRIGGER: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER: ") ); - break; - case SNDCTL_DSP_GETOSPACE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE: ") ); - break; - case SNDCTL_DSP_GETISPACE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE: ") ); - break; - case SNDCTL_DSP_NONBLOCK: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK: ") ); - break; - case SNDCTL_DSP_GETODELAY: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY: ") ); - break; - case SNDCTL_DSP_GETIPTR: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR: ") ); - break; - case SNDCTL_DSP_GETOPTR: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR: ") ); - break; - case SNDCTL_DSP_GETBLKSIZE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE: ") ); - break; - case SNDCTL_DSP_SETFRAGMENT: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFRAGMENT: ") ); - break; - case SNDCTL_DSP_SUBDIVIDE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE: ") ); - break; - case SOUND_PCM_READ_RATE: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE: ") ); - break; - case SOUND_PCM_READ_CHANNELS: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_CHANNELS: ") ); - break; - case SOUND_PCM_READ_BITS: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS: ") ); - break; - case SOUND_PCM_WRITE_FILTER: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_WRITE_FILTER: ") ); - break; - case SNDCTL_DSP_SETSYNCRO: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO: ") ); - break; - case SOUND_PCM_READ_FILTER: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER: ") ); - break; - - case SOUND_MIXER_PRIVATE1: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1: ") ); - break; - case SOUND_MIXER_PRIVATE2: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2: ") ); - break; - case SOUND_MIXER_PRIVATE3: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3: ") ); - break; - case SOUND_MIXER_PRIVATE4: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4: ") ); - break; - case SOUND_MIXER_PRIVATE5: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5: ") ); - break; - case SOUND_MIXER_INFO: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO: ") ); - break; - case SOUND_OLD_MIXER_INFO: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO: ") ); - break; - - default: - switch (_IOC_NR(x)) - { - case SOUND_MIXER_VOLUME: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_VOLUME: ") ); - break; - case SOUND_MIXER_SPEAKER: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SPEAKER: ") ); - break; - case SOUND_MIXER_RECLEV: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECLEV: ") ); - break; - case SOUND_MIXER_MIC: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_MIC: ") ); - break; - case SOUND_MIXER_SYNTH: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SYNTH: ") ); - break; - case SOUND_MIXER_RECSRC: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECSRC: ") ); - break; - case SOUND_MIXER_DEVMASK: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_DEVMASK: ") ); - break; - case SOUND_MIXER_RECMASK: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECMASK: ") ); - break; - case SOUND_MIXER_STEREODEVS: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_STEREODEVS: ") ); - break; - case SOUND_MIXER_CAPS: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:") ); - 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 ",x,i) ); - } - else - { - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d ", - x,i) ); - } - break; - } - } - CS_DBGOUT(CS_IOCTL, 4, printk("command = 0x%x IOC_NR=%d\n",x, _IOC_NR(x)) ); -} -#endif - -/* - * common I/O routines - */ - -static void cs461x_poke(struct cs_card *codec, unsigned long reg, unsigned int val) -{ - writel(val, codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff)); -} - -static unsigned int cs461x_peek(struct cs_card *codec, unsigned long reg) -{ - return readl(codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff)); -} - -static void cs461x_pokeBA0(struct cs_card *codec, unsigned long reg, unsigned int val) -{ - writel(val, codec->ba0+reg); -} - -static unsigned int cs461x_peekBA0(struct cs_card *codec, unsigned long reg) -{ - return readl(codec->ba0+reg); -} - - -static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg); -static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); - -static struct cs_channel *cs_alloc_pcm_channel(struct cs_card *card) -{ - if(card->channel[1].used==1) - return NULL; - card->channel[1].used=1; - card->channel[1].num=1; - return &card->channel[1]; -} - -static struct cs_channel *cs_alloc_rec_pcm_channel(struct cs_card *card) -{ - if(card->channel[0].used==1) - return NULL; - card->channel[0].used=1; - card->channel[0].num=0; - return &card->channel[0]; -} - -static void cs_free_pcm_channel(struct cs_card *card, int channel) -{ - card->channel[channel].state = NULL; - card->channel[channel].used=0; -} - -/* - * setup a divisor value to help with conversion from - * 16bit Stereo, down to 8bit stereo/mono or 16bit mono. - * assign a divisor of 1 if using 16bit Stereo as that is - * the only format that the static image will capture. - */ -static void cs_set_divisor(struct dmabuf *dmabuf) -{ - if(dmabuf->type == CS_TYPE_DAC) - dmabuf->divisor = 1; - else if( !(dmabuf->fmt & CS_FMT_STEREO) && - (dmabuf->fmt & CS_FMT_16BIT)) - dmabuf->divisor = 2; - else if( (dmabuf->fmt & CS_FMT_STEREO) && - !(dmabuf->fmt & CS_FMT_16BIT)) - dmabuf->divisor = 2; - else if( !(dmabuf->fmt & CS_FMT_STEREO) && - !(dmabuf->fmt & CS_FMT_16BIT)) - dmabuf->divisor = 4; - else - dmabuf->divisor = 1; - - CS_DBGOUT(CS_PARMS | CS_FUNCTION, 8, printk( - "cs46xx: cs_set_divisor()- %s %d\n", - (dmabuf->type == CS_TYPE_ADC) ? "ADC" : "DAC", - dmabuf->divisor) ); -} - -/* -* mute some of the more prevalent registers to avoid popping. -*/ -static void cs_mute(struct cs_card *card, int state) -{ - struct ac97_codec *dev=card->ac97_codec[0]; - - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()+ %s\n", - (state == CS_TRUE) ? "Muting" : "UnMuting") ); - - if(state == CS_TRUE) - { - /* - * fix pops when powering up on thinkpads - */ - card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev, - (u8)BA0_AC97_MASTER_VOLUME); - card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev, - (u8)BA0_AC97_HEADPHONE_VOLUME); - card->pm.u32AC97_master_volume_mono = (u32)cs_ac97_get(dev, - (u8)BA0_AC97_MASTER_VOLUME_MONO); - card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev, - (u8)BA0_AC97_PCM_OUT_VOLUME); - - cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000); - cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000); - cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000); - cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000); - } - else - { - cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, card->pm.u32AC97_master_volume); - cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, card->pm.u32AC97_headphone_volume); - cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, card->pm.u32AC97_master_volume_mono); - cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, card->pm.u32AC97_pcm_out_volume); - } - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()-\n")); -} - -/* set playback sample rate */ -static unsigned int cs_set_dac_rate(struct cs_state * state, unsigned int rate) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned int tmp1, tmp2; - unsigned int phiIncr; - unsigned int correctionPerGOF, correctionPerSec; - unsigned long flags; - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()+ %d\n",rate) ); - - /* - * Compute the values used to drive the actual sample rate conversion. - * The following formulas are being computed, using inline assembly - * since we need to use 64 bit arithmetic to compute the values: - * - * phiIncr = floor((Fs,in * 2^26) / Fs,out) - * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / - * GOF_PER_SEC) - * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M - * GOF_PER_SEC * correctionPerGOF - * - * i.e. - * - * phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out) - * correctionPerGOF:correctionPerSec = - * dividend:remainder(ulOther / GOF_PER_SEC) - */ - tmp1 = rate << 16; - phiIncr = tmp1 / 48000; - tmp1 -= phiIncr * 48000; - tmp1 <<= 10; - phiIncr <<= 10; - tmp2 = tmp1 / 48000; - phiIncr += tmp2; - tmp1 -= tmp2 * 48000; - correctionPerGOF = tmp1 / GOF_PER_SEC; - tmp1 -= correctionPerGOF * GOF_PER_SEC; - correctionPerSec = tmp1; - - /* - * Fill in the SampleRateConverter control block. - */ - - spin_lock_irqsave(&state->card->lock, flags); - cs461x_poke(state->card, BA1_PSRC, - ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); - cs461x_poke(state->card, BA1_PPI, phiIncr); - spin_unlock_irqrestore(&state->card->lock, flags); - dmabuf->rate = rate; - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()- %d\n",rate) ); - return rate; -} - -/* set recording sample rate */ -static unsigned int cs_set_adc_rate(struct cs_state * state, unsigned int rate) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct cs_card *card = state->card; - unsigned int phiIncr, coeffIncr, tmp1, tmp2; - unsigned int correctionPerGOF, correctionPerSec, initialDelay; - unsigned int frameGroupLength, cnt; - unsigned long flags; - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()+ %d\n",rate) ); - - /* - * We can only decimate by up to a factor of 1/9th the hardware rate. - * Correct the value if an attempt is made to stray outside that limit. - */ - if ((rate * 9) < 48000) - rate = 48000 / 9; - - /* - * We can not capture at at rate greater than the Input Rate (48000). - * Return an error if an attempt is made to stray outside that limit. - */ - if (rate > 48000) - rate = 48000; - - /* - * Compute the values used to drive the actual sample rate conversion. - * The following formulas are being computed, using inline assembly - * since we need to use 64 bit arithmetic to compute the values: - * - * coeffIncr = -floor((Fs,out * 2^23) / Fs,in) - * phiIncr = floor((Fs,in * 2^26) / Fs,out) - * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / - * GOF_PER_SEC) - * correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - - * GOF_PER_SEC * correctionPerGOF - * initialDelay = ceil((24 * Fs,in) / Fs,out) - * - * i.e. - * - * coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in)) - * phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) - * correctionPerGOF:correctionPerSec = - * dividend:remainder(ulOther / GOF_PER_SEC) - * initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out) - */ - - tmp1 = rate << 16; - coeffIncr = tmp1 / 48000; - tmp1 -= coeffIncr * 48000; - tmp1 <<= 7; - coeffIncr <<= 7; - coeffIncr += tmp1 / 48000; - coeffIncr ^= 0xFFFFFFFF; - coeffIncr++; - tmp1 = 48000 << 16; - phiIncr = tmp1 / rate; - tmp1 -= phiIncr * rate; - tmp1 <<= 10; - phiIncr <<= 10; - tmp2 = tmp1 / rate; - phiIncr += tmp2; - tmp1 -= tmp2 * rate; - correctionPerGOF = tmp1 / GOF_PER_SEC; - tmp1 -= correctionPerGOF * GOF_PER_SEC; - correctionPerSec = tmp1; - initialDelay = ((48000 * 24) + rate - 1) / rate; - - /* - * Fill in the VariDecimate control block. - */ - spin_lock_irqsave(&card->lock, flags); - cs461x_poke(card, BA1_CSRC, - ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); - cs461x_poke(card, BA1_CCI, coeffIncr); - cs461x_poke(card, BA1_CD, - (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80); - cs461x_poke(card, BA1_CPI, phiIncr); - spin_unlock_irqrestore(&card->lock, flags); - - /* - * Figure out the frame group length for the write back task. Basically, - * this is just the factors of 24000 (2^6*3*5^3) that are not present in - * the output sample rate. - */ - frameGroupLength = 1; - for (cnt = 2; cnt <= 64; cnt *= 2) { - if (((rate / cnt) * cnt) != rate) - frameGroupLength *= 2; - } - if (((rate / 3) * 3) != rate) { - frameGroupLength *= 3; - } - for (cnt = 5; cnt <= 125; cnt *= 5) { - if (((rate / cnt) * cnt) != rate) - frameGroupLength *= 5; - } - - /* - * Fill in the WriteBack control block. - */ - spin_lock_irqsave(&card->lock, flags); - cs461x_poke(card, BA1_CFG1, frameGroupLength); - cs461x_poke(card, BA1_CFG2, (0x00800000 | frameGroupLength)); - cs461x_poke(card, BA1_CCST, 0x0000FFFF); - cs461x_poke(card, BA1_CSPB, ((65536 * rate) / 24000)); - cs461x_poke(card, (BA1_CSPB + 4), 0x0000FFFF); - spin_unlock_irqrestore(&card->lock, flags); - dmabuf->rate = rate; - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()- %d\n",rate) ); - return rate; -} - -/* prepare channel attributes for playback */ -static void cs_play_setup(struct cs_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct cs_card *card = state->card; - unsigned int tmp, Count, playFormat; - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()+\n") ); - cs461x_poke(card, BA1_PVOL, 0x80008000); - if(!dmabuf->SGok) - cs461x_poke(card, BA1_PBA, virt_to_bus(dmabuf->pbuf)); - - Count = 4; - playFormat=cs461x_peek(card, BA1_PFIE); - if ((dmabuf->fmt & CS_FMT_STEREO)) { - playFormat &= ~DMA_RQ_C2_AC_MONO_TO_STEREO; - Count *= 2; - } - else - playFormat |= DMA_RQ_C2_AC_MONO_TO_STEREO; - - if ((dmabuf->fmt & CS_FMT_16BIT)) { - playFormat &= ~(DMA_RQ_C2_AC_8_TO_16_BIT - | DMA_RQ_C2_AC_SIGNED_CONVERT); - Count *= 2; - } - else - playFormat |= (DMA_RQ_C2_AC_8_TO_16_BIT - | DMA_RQ_C2_AC_SIGNED_CONVERT); - - cs461x_poke(card, BA1_PFIE, playFormat); - - tmp = cs461x_peek(card, BA1_PDTC); - tmp &= 0xfffffe00; - cs461x_poke(card, BA1_PDTC, tmp | --Count); - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()-\n") ); - -} - -struct InitStruct -{ - u32 long off; - u32 long val; -} InitArray[] = { {0x00000040, 0x3fc0000f}, - {0x0000004c, 0x04800000}, - - {0x000000b3, 0x00000780}, - {0x000000b7, 0x00000000}, - {0x000000bc, 0x07800000}, - - {0x000000cd, 0x00800000}, - }; - -/* - * "SetCaptureSPValues()" -- Initialize record task values before each - * capture startup. - */ -void SetCaptureSPValues(struct cs_card *card) -{ - unsigned i, offset; - CS_DBGOUT(CS_FUNCTION, 8, printk("cs46xx: SetCaptureSPValues()+\n") ); - for(i=0; icard; - struct dmabuf *dmabuf = &state->dmabuf; - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()+\n") ); - - SetCaptureSPValues(card); - - /* - * set the attenuation to 0dB - */ - cs461x_poke(card, BA1_CVOL, 0x80008000); - - /* - * set the physical address of the capture buffer into the SP - */ - cs461x_poke(card, BA1_CBA, virt_to_bus(dmabuf->rawbuf)); - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()-\n") ); -} - - -/* get current playback/recording dma buffer pointer (byte offset from LBA), - called with spinlock held! */ - -static inline unsigned cs_get_dma_addr(struct cs_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - u32 offset; - - if ( (!(dmabuf->enable & DAC_RUNNING)) && - (!(dmabuf->enable & ADC_RUNNING) ) ) - { - CS_DBGOUT(CS_ERROR, 2, printk( - "cs46xx: ERROR cs_get_dma_addr(): not enabled \n") ); - return 0; - } - - /* - * ganularity is byte boundry, good part. - */ - if(dmabuf->enable & DAC_RUNNING) - { - offset = cs461x_peek(state->card, BA1_PBA); - } - else /* ADC_RUNNING must be set */ - { - offset = cs461x_peek(state->card, BA1_CBA); - } - CS_DBGOUT(CS_PARMS | CS_FUNCTION, 9, - printk("cs46xx: cs_get_dma_addr() %d\n",offset) ); - offset = (u32)bus_to_virt((unsigned long)offset) - (u32)dmabuf->rawbuf; - CS_DBGOUT(CS_PARMS | CS_FUNCTION, 8, - printk("cs46xx: cs_get_dma_addr()- %d\n",offset) ); - return offset; -} - -static void resync_dma_ptrs(struct cs_state *state) -{ - struct dmabuf *dmabuf; - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()+ \n") ); - if(state) - { - dmabuf = &state->dmabuf; - dmabuf->hwptr=dmabuf->swptr = 0; - dmabuf->pringbuf = 0; - } - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()- \n") ); -} - -/* Stop recording (lock held) */ -static inline void __stop_adc(struct cs_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct cs_card *card = state->card; - unsigned int tmp; - - dmabuf->enable &= ~ADC_RUNNING; - - tmp = cs461x_peek(card, BA1_CCTL); - tmp &= 0xFFFF0000; - cs461x_poke(card, BA1_CCTL, tmp ); -} - -static void stop_adc(struct cs_state *state) -{ - unsigned long flags; - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_adc()+ \n") ); - spin_lock_irqsave(&state->card->lock, flags); - __stop_adc(state); - spin_unlock_irqrestore(&state->card->lock, flags); - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_adc()- \n") ); -} - -static void start_adc(struct cs_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct cs_card *card = state->card; - unsigned long flags; - unsigned int tmp; - - spin_lock_irqsave(&card->lock, flags); - if (!(dmabuf->enable & ADC_RUNNING) && - ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) - && dmabuf->ready) && - ((card->pm.flags & CS46XX_PM_IDLE) || - (card->pm.flags & CS46XX_PM_RESUMED)) ) - { - dmabuf->enable |= ADC_RUNNING; - cs_set_divisor(dmabuf); - tmp = cs461x_peek(card, BA1_CCTL); - tmp &= 0xFFFF0000; - tmp |= card->cctl; - CS_DBGOUT(CS_FUNCTION, 2, printk( - "cs46xx: start_adc() poke 0x%x \n",tmp) ); - cs461x_poke(card, BA1_CCTL, tmp); - } - spin_unlock_irqrestore(&card->lock, flags); -} - -/* stop playback (lock held) */ -static inline void __stop_dac(struct cs_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct cs_card *card = state->card; - unsigned int tmp; - - dmabuf->enable &= ~DAC_RUNNING; - - tmp=cs461x_peek(card, BA1_PCTL); - tmp&=0xFFFF; - cs461x_poke(card, BA1_PCTL, tmp); -} - -static void stop_dac(struct cs_state *state) -{ - unsigned long flags; - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_dac()+ \n") ); - spin_lock_irqsave(&state->card->lock, flags); - __stop_dac(state); - spin_unlock_irqrestore(&state->card->lock, flags); - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_dac()- \n") ); -} - -static void start_dac(struct cs_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct cs_card *card = state->card; - unsigned long flags; - int tmp; - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()+ \n") ); - spin_lock_irqsave(&card->lock, flags); - if (!(dmabuf->enable & DAC_RUNNING) && - ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) && - ((card->pm.flags & CS46XX_PM_IDLE) || - (card->pm.flags & CS46XX_PM_RESUMED)) ) - { - dmabuf->enable |= DAC_RUNNING; - tmp = cs461x_peek(card, BA1_PCTL); - tmp &= 0xFFFF; - tmp |= card->pctl; - CS_DBGOUT(CS_PARMS, 6, printk( - "cs46xx: start_dac() poke card=0x%.08x tmp=0x%.08x addr=0x%.08x \n", - (unsigned)card, (unsigned)tmp, - (unsigned)card->ba1.idx[(BA1_PCTL >> 16) & 3]+(BA1_PCTL&0xffff) ) ); - cs461x_poke(card, BA1_PCTL, tmp); - } - spin_unlock_irqrestore(&card->lock, flags); - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()- \n") ); -} - -#define DMABUF_MINORDER 1 - -/* - * allocate DMA buffer, playback and recording buffers are separate. - */ -static int alloc_dmabuf(struct cs_state *state) -{ - - struct cs_card *card=state->card; - struct dmabuf *dmabuf = &state->dmabuf; - void *rawbuf = NULL; - void *tmpbuff = NULL; - int order; - struct page *map, *mapend; - unsigned long df; - - dmabuf->ready = dmabuf->mapped = 0; - dmabuf->SGok = 0; -/* -* check for order within limits, but do not overwrite value. -*/ - if((defaultorder > 1) && (defaultorder < 12)) - df = defaultorder; - else - df = 2; - - for (order = df; order >= DMABUF_MINORDER; order--) - if ( (rawbuf = (void *) pci_alloc_consistent( - card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr))) - break; - if (!rawbuf) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs46xx: alloc_dmabuf(): unable to allocate rawbuf\n")); - return -ENOMEM; - } - dmabuf->buforder = order; - dmabuf->rawbuf = rawbuf; - // Now mark the pages as reserved; otherwise the - // remap_page_range() in cs46xx_mmap doesn't work. - // 1. get index to last page in mem_map array for rawbuf. - mapend = virt_to_page(dmabuf->rawbuf + - (PAGE_SIZE << dmabuf->buforder) - 1); - - // 2. mark each physical page in range as 'reserved'. - for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; 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) ); - -/* -* only allocate the conversion buffer for the ADC -*/ - if(dmabuf->type == CS_TYPE_DAC) - { - dmabuf->tmpbuff = NULL; - dmabuf->buforder_tmpbuff = 0; - return 0; - } -/* - * now the temp buffer for 16/8 conversions - */ - - tmpbuff = (void *) pci_alloc_consistent( - card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr_tmpbuff); - - if (!tmpbuff) - return -ENOMEM; - CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: allocated %ld (order = %d) bytes at %p\n", - PAGE_SIZE << order, order, tmpbuff) ); - - dmabuf->tmpbuff = tmpbuff; - dmabuf->buforder_tmpbuff = order; - - // Now mark the pages as reserved; otherwise the - // remap_page_range() in cs46xx_mmap doesn't work. - // 1. get index to last page in mem_map array for rawbuf. - mapend = virt_to_page(dmabuf->tmpbuff + - (PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1); - - // 2. mark each physical page in range as 'reserved'. - for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++) - cs4x_mem_map_reserve(map); - return 0; -} - -/* free DMA buffer */ -static void dealloc_dmabuf(struct cs_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct page *map, *mapend; - - if (dmabuf->rawbuf) { - // Undo prog_dmabuf()'s marking the pages as reserved - mapend = virt_to_page(dmabuf->rawbuf + - (PAGE_SIZE << dmabuf->buforder) - 1); - for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) - cs4x_mem_map_unreserve(map); - free_dmabuf(state->card, dmabuf); - } - - if (dmabuf->tmpbuff) { - // Undo prog_dmabuf()'s marking the pages as reserved - mapend = virt_to_page(dmabuf->tmpbuff + - (PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1); - for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++) - cs4x_mem_map_unreserve(map); - free_dmabuf2(state->card, dmabuf); - } - - dmabuf->rawbuf = NULL; - dmabuf->tmpbuff = NULL; - dmabuf->mapped = dmabuf->ready = 0; - dmabuf->SGok = 0; -} - -static int __prog_dmabuf(struct cs_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - unsigned long allocated_pages, allocated_bytes; - unsigned long tmp1, tmp2, fmt=0; - unsigned long *ptmp = (unsigned long *) dmabuf->pbuf; - unsigned long SGarray[9], nSGpages=0; - int ret; - - CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()+ \n")); -/* - * check for CAPTURE and use only non-sg for initial release - */ - if(dmabuf->type == CS_TYPE_ADC) - { - CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() ADC\n")); - /* - * add in non-sg support for capture. - */ - spin_lock_irqsave(&state->card->lock, flags); - /* add code to reset the rawbuf memory. TRW */ - resync_dma_ptrs(state); - dmabuf->total_bytes = dmabuf->blocks = 0; - dmabuf->count = dmabuf->error = dmabuf->underrun = 0; - - dmabuf->SGok = 0; - - spin_unlock_irqrestore(&state->card->lock, flags); - - /* allocate DMA buffer if not allocated yet */ - if (!dmabuf->rawbuf || !dmabuf->tmpbuff) - if ((ret = alloc_dmabuf(state))) - return ret; - /* - * static image only supports 16Bit signed, stereo - hard code fmt - */ - fmt = CS_FMT_16BIT | CS_FMT_STEREO; - - dmabuf->numfrag = 2; - dmabuf->fragsize = 2048; - dmabuf->fragsamples = 2048 >> sample_shift[fmt]; - dmabuf->dmasize = 4096; - dmabuf->fragshift = 11; - - memset(dmabuf->rawbuf, (fmt & CS_FMT_16BIT) ? 0 : 0x80, - dmabuf->dmasize); - memset(dmabuf->tmpbuff, (fmt & CS_FMT_16BIT) ? 0 : 0x80, - PAGE_SIZE<buforder_tmpbuff); - - /* - * Now set up the ring - */ - - spin_lock_irqsave(&state->card->lock, flags); - cs_rec_setup(state); - spin_unlock_irqrestore(&state->card->lock, flags); - - /* set the ready flag for the dma buffer */ - dmabuf->ready = 1; - - CS_DBGOUT(CS_PARMS, 4, printk( - "cs46xx: prog_dmabuf(): CAPTURE rate=%d fmt=0x%x numfrag=%d " - "fragsize=%d dmasize=%d\n", - dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, - dmabuf->fragsize, dmabuf->dmasize) ); - - CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- 0 \n")); - return 0; - } - else if (dmabuf->type == CS_TYPE_DAC) - { - /* - * Must be DAC - */ - CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() DAC\n")); - spin_lock_irqsave(&state->card->lock, flags); - resync_dma_ptrs(state); - dmabuf->total_bytes = dmabuf->blocks = 0; - dmabuf->count = dmabuf->error = dmabuf->underrun = 0; - - dmabuf->SGok = 0; - - spin_unlock_irqrestore(&state->card->lock, flags); - - /* allocate DMA buffer if not allocated yet */ - if (!dmabuf->rawbuf) - if ((ret = alloc_dmabuf(state))) - return ret; - - allocated_pages = 1 << dmabuf->buforder; - allocated_bytes = allocated_pages*PAGE_SIZE; - - if(allocated_pages < 2) - { - CS_DBGOUT(CS_FUNCTION, 4, printk( - "cs46xx: prog_dmabuf() Error: allocated_pages too small (%d)\n", - (unsigned)allocated_pages)); - return -ENOMEM; - } - - /* Use all the pages allocated, fragsize 4k. */ - /* Use 'pbuf' for S/G page map table. */ - dmabuf->SGok = 1; /* Use S/G. */ - - nSGpages = allocated_bytes/4096; /* S/G pages always 4k. */ - - /* Set up S/G variables. */ - *ptmp = virt_to_bus(dmabuf->rawbuf); - *(ptmp+1) = 0x00000008; - for(tmp1= 1; tmp1 < nSGpages; tmp1++) { - *(ptmp+2*tmp1) = virt_to_bus( (dmabuf->rawbuf)+4096*tmp1); - if( tmp1 == nSGpages-1) - tmp2 = 0xbfff0000; - else - tmp2 = 0x80000000+8*(tmp1+1); - *(ptmp+2*tmp1+1) = tmp2; - } - SGarray[0] = 0x82c0200d; - SGarray[1] = 0xffff0000; - SGarray[2] = *ptmp; - SGarray[3] = 0x00010600; - SGarray[4] = *(ptmp+2); - SGarray[5] = 0x80000010; - SGarray[6] = *ptmp; - SGarray[7] = *(ptmp+2); - SGarray[8] = (virt_to_bus(dmabuf->pbuf) & 0xffff000) | 0x10; - - if (dmabuf->SGok) { - dmabuf->numfrag = nSGpages; - dmabuf->fragsize = 4096; - dmabuf->fragsamples = 4096 >> sample_shift[dmabuf->fmt]; - dmabuf->fragshift = 12; - dmabuf->dmasize = dmabuf->numfrag*4096; - } - else { - SGarray[0] = 0xf2c0000f; - SGarray[1] = 0x00000200; - SGarray[2] = 0; - SGarray[3] = 0x00010600; - SGarray[4]=SGarray[5]=SGarray[6]=SGarray[7]=SGarray[8] = 0; - dmabuf->numfrag = 2; - dmabuf->fragsize = 2048; - dmabuf->fragsamples = 2048 >> sample_shift[dmabuf->fmt]; - dmabuf->dmasize = 4096; - dmabuf->fragshift = 11; - } - for(tmp1 = 0; tmp1 < sizeof(SGarray)/4; tmp1++) - cs461x_poke( state->card, BA1_PDTC+tmp1*4, SGarray[tmp1]); - - memset(dmabuf->rawbuf, (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, - dmabuf->dmasize); - - /* - * Now set up the ring - */ - - spin_lock_irqsave(&state->card->lock, flags); - cs_play_setup(state); - spin_unlock_irqrestore(&state->card->lock, flags); - - /* set the ready flag for the dma buffer */ - dmabuf->ready = 1; - - CS_DBGOUT(CS_PARMS, 4, printk( - "cs46xx: prog_dmabuf(): PLAYBACK rate=%d fmt=0x%x numfrag=%d " - "fragsize=%d dmasize=%d\n", - dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, - dmabuf->fragsize, dmabuf->dmasize) ); - - CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- \n")); - return 0; - } - else - { - CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- Invalid Type %d\n", - dmabuf->type)); - } - return 1; -} - -static int prog_dmabuf(struct cs_state *state) -{ - int ret; - - down(&state->sem); - ret = __prog_dmabuf(state); - up(&state->sem); - - return ret; -} - -static void cs_clear_tail(struct cs_state *state) -{ -} - -static int drain_dac(struct cs_state *state, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - struct dmabuf *dmabuf = &state->dmabuf; - struct cs_card *card=state->card; - unsigned long flags; - unsigned long tmo; - int count; - - CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()+ \n")); - if (dmabuf->mapped || !dmabuf->ready) - { - CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- 0, not ready\n")); - return 0; - } - - add_wait_queue(&dmabuf->wait, &wait); - for (;;) { - /* It seems that we have to set the current state to TASK_INTERRUPTIBLE - every time to make the process really go to sleep */ - current->state = TASK_INTERRUPTIBLE; - - spin_lock_irqsave(&state->card->lock, flags); - count = dmabuf->count; - spin_unlock_irqrestore(&state->card->lock, flags); - - if (count <= 0) - break; - - if (signal_pending(current)) - break; - - if (nonblock) { - remove_wait_queue(&dmabuf->wait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - - tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; - tmo >>= sample_shift[dmabuf->fmt]; - tmo += (2048*HZ)/dmabuf->rate; - - if (!schedule_timeout(tmo ? tmo : 1) && tmo){ - printk(KERN_ERR "cs46xx: drain_dac, dma timeout? %d\n", count); - break; - } - } - remove_wait_queue(&dmabuf->wait, &wait); - current->state = TASK_RUNNING; - if (signal_pending(current)) - { - CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- -ERESTARTSYS\n")); - /* - * set to silence and let that clear the fifos. - */ - cs461x_clear_serial_FIFOs(card, CS_TYPE_DAC); - return -ERESTARTSYS; - } - - CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- 0\n")); - return 0; -} - - -/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ -static void cs_update_ptr(struct cs_card *card, int wake) -{ - struct cs_state *state; - struct dmabuf *dmabuf; - unsigned hwptr; - int diff; - - /* error handling and process wake up for ADC */ - state = card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - if (dmabuf->enable & ADC_RUNNING) { - /* update hardware pointer */ - hwptr = cs_get_dma_addr(state); - - diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; - CS_DBGOUT(CS_PARMS, 9, printk( - "cs46xx: cs_update_ptr()+ ADC hwptr=%d diff=%d\n", - hwptr,diff) ); - 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 >= (signed)dmabuf->fragsize) - wake_up(&dmabuf->wait); - } else - { - if (wake && dmabuf->count > 0) - wake_up(&dmabuf->wait); - } - } - } - -/* - * Now the DAC - */ - state = card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - /* error handling and process wake up for DAC */ - if (dmabuf->enable & DAC_RUNNING) { - /* update hardware pointer */ - hwptr = cs_get_dma_addr(state); - - diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; - CS_DBGOUT(CS_PARMS, 9, printk( - "cs46xx: cs_update_ptr()+ DAC hwptr=%d diff=%d\n", - hwptr,diff) ); - dmabuf->hwptr = hwptr; - dmabuf->total_bytes += diff; - if (dmabuf->mapped) { - dmabuf->count += diff; - if (wake && dmabuf->count >= (signed)dmabuf->fragsize) - wake_up(&dmabuf->wait); - /* - * other drivers use fragsize, but don't see any sense - * in that, since dmasize is the buffer asked for - * via mmap. - */ - if( dmabuf->count > dmabuf->dmasize) - dmabuf->count &= dmabuf->dmasize-1; - } else { - dmabuf->count -= diff; - /* - * backfill with silence and clear out the last - * "diff" number of bytes. - */ - if(hwptr >= diff) - { - memset(dmabuf->rawbuf + hwptr - diff, - (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, diff); - } - else - { - memset(dmabuf->rawbuf, - (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, - (unsigned)hwptr); - memset((void *)((unsigned)dmabuf->rawbuf + - dmabuf->dmasize + hwptr - diff), - (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, - diff - hwptr); - } - - if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { - CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO - "cs46xx: ERROR DAC count<0 or count > dmasize (%d)\n", - dmabuf->count)); - /* - * buffer underrun or buffer overrun, reset the - * count of bytes written back to 0. - */ - if(dmabuf->count < 0) - dmabuf->underrun=1; - dmabuf->count = 0; - dmabuf->error++; - } - if (wake && dmabuf->count < (signed)dmabuf->dmasize/2) - wake_up(&dmabuf->wait); - } - } - } -} - - -/* hold spinlock for the following! */ -static void cs_handle_midi(struct cs_card *card) -{ - unsigned char ch; - int wake; - unsigned temp1; - - wake = 0; - while (!(cs461x_peekBA0(card, BA0_MIDSR) & MIDSR_RBE)) { - ch = cs461x_peekBA0(card, BA0_MIDRP); - if (card->midi.icnt < CS_MIDIINBUF) { - card->midi.ibuf[card->midi.iwr] = ch; - card->midi.iwr = (card->midi.iwr + 1) % CS_MIDIINBUF; - card->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&card->midi.iwait); - wake = 0; - while (!(cs461x_peekBA0(card, BA0_MIDSR) & MIDSR_TBF) && card->midi.ocnt > 0) { - temp1 = ( card->midi.obuf[card->midi.ord] ) & 0x000000ff; - cs461x_pokeBA0(card, BA0_MIDWP,temp1); - card->midi.ord = (card->midi.ord + 1) % CS_MIDIOUTBUF; - card->midi.ocnt--; - if (card->midi.ocnt < CS_MIDIOUTBUF-16) - wake = 1; - } - if (wake) - wake_up(&card->midi.owait); -} - -static void cs_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct cs_card *card = (struct cs_card *)dev_id; - /* Single channel card */ - struct cs_state *recstate = card->channel[0].state; - struct cs_state *playstate = card->channel[1].state; - u32 status; - - CS_DBGOUT(CS_INTERRUPT, 9, printk("cs46xx: cs_interrupt()+ \n")); - - spin_lock(&card->lock); - - status = cs461x_peekBA0(card, BA0_HISR); - - if ((status & 0x7fffffff) == 0) - { - cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV); - spin_unlock(&card->lock); - return; - } - - /* - * check for playback or capture interrupt only - */ - if( ((status & HISR_VC0) && playstate && playstate->dmabuf.ready) || - (((status & HISR_VC1) && recstate && recstate->dmabuf.ready)) ) - { - CS_DBGOUT(CS_INTERRUPT, 8, printk( - "cs46xx: cs_interrupt() interrupt bit(s) set (0x%x)\n",status)); - cs_update_ptr(card, CS_TRUE); - } - - if( status & HISR_MIDI ) - cs_handle_midi(card); - - /* clear 'em */ - cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV); - spin_unlock(&card->lock); - CS_DBGOUT(CS_INTERRUPT, 9, printk("cs46xx: cs_interrupt()- \n")); -} - - -/**********************************************************************/ - -static ssize_t cs_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct cs_card *card = (struct cs_card *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - while (count > 0) { - spin_lock_irqsave(&card->lock, flags); - ptr = card->midi.ird; - cnt = CS_MIDIINBUF - ptr; - if (card->midi.icnt < cnt) - cnt = card->midi.icnt; - spin_unlock_irqrestore(&card->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&card->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (copy_to_user(buffer, card->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; - ptr = (ptr + cnt) % CS_MIDIINBUF; - spin_lock_irqsave(&card->lock, flags); - card->midi.ird = ptr; - card->midi.icnt -= cnt; - spin_unlock_irqrestore(&card->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - } - return ret; -} - - -static ssize_t cs_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct cs_card *card = (struct cs_card *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - while (count > 0) { - spin_lock_irqsave(&card->lock, flags); - ptr = card->midi.owr; - cnt = CS_MIDIOUTBUF - ptr; - if (card->midi.ocnt + cnt > CS_MIDIOUTBUF) - cnt = CS_MIDIOUTBUF - card->midi.ocnt; - if (cnt <= 0) - cs_handle_midi(card); - spin_unlock_irqrestore(&card->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&card->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (copy_from_user(card->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; - ptr = (ptr + cnt) % CS_MIDIOUTBUF; - spin_lock_irqsave(&card->lock, flags); - card->midi.owr = ptr; - card->midi.ocnt += cnt; - spin_unlock_irqrestore(&card->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&card->lock, flags); - cs_handle_midi(card); - spin_unlock_irqrestore(&card->lock, flags); - } - return ret; -} - - -static unsigned int cs_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct cs_card *card = (struct cs_card *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - if (file->f_flags & FMODE_WRITE) - poll_wait(file, &card->midi.owait, wait); - if (file->f_flags & FMODE_READ) - poll_wait(file, &card->midi.iwait, wait); - spin_lock_irqsave(&card->lock, flags); - if (file->f_flags & FMODE_READ) { - if (card->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_flags & FMODE_WRITE) { - if (card->midi.ocnt < CS_MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&card->lock, flags); - return mask; -} - - -static int cs_midi_open(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct cs_card *card=NULL; - unsigned long flags; - struct list_head *entry; - - list_for_each(entry, &cs46xx_devs) - { - card = list_entry(entry, struct cs_card, list); - if (card->dev_midi == minor) - break; - } - - if (entry == &cs46xx_devs) - return -ENODEV; - if (!card) - { - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs46xx: cs46xx_midi_open(): Error - unable to find card struct\n")); - return -ENODEV; - } - - file->private_data = card; - /* wait for device to become free */ - down(&card->midi.open_sem); - while (card->midi.open_mode & file->f_mode) { - if (file->f_flags & O_NONBLOCK) { - up(&card->midi.open_sem); - return -EBUSY; - } - up(&card->midi.open_sem); - interruptible_sleep_on(&card->midi.open_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - down(&card->midi.open_sem); - } - spin_lock_irqsave(&card->midi.lock, flags); - if (!(card->midi.open_mode & (FMODE_READ | FMODE_WRITE))) { - card->midi.ird = card->midi.iwr = card->midi.icnt = 0; - card->midi.ord = card->midi.owr = card->midi.ocnt = 0; - card->midi.ird = card->midi.iwr = card->midi.icnt = 0; - cs461x_pokeBA0(card, BA0_MIDCR, 0x0000000f); /* Enable xmit, rcv. */ - cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM); /* Enable interrupts */ - } - if (file->f_mode & FMODE_READ) { - card->midi.ird = card->midi.iwr = card->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - card->midi.ord = card->midi.owr = card->midi.ocnt = 0; - } - spin_unlock_irqrestore(&card->midi.lock, flags); - card->midi.open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE)); - up(&card->midi.open_sem); - MOD_INC_USE_COUNT; /* for 2.2 */ - return 0; -} - - -static int cs_midi_release(struct inode *inode, struct file *file) -{ - struct cs_card *card = (struct cs_card *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - if (file->f_mode & FMODE_WRITE) { - current->state = TASK_INTERRUPTIBLE; - add_wait_queue(&card->midi.owait, &wait); - for (;;) { - spin_lock_irqsave(&card->midi.lock, flags); - count = card->midi.ocnt; - spin_unlock_irqrestore(&card->midi.lock, flags); - if (count <= 0) - 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; - } - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "cs46xx: midi timed out??\n"); - } - remove_wait_queue(&card->midi.owait, &wait); - current->state = TASK_RUNNING; - } - down(&card->midi.open_sem); - card->midi.open_mode &= (~(file->f_mode & (FMODE_READ | FMODE_WRITE))); - up(&card->midi.open_sem); - wake_up(&card->midi.open_wait); - MOD_DEC_USE_COUNT; /* for 2.2 */ - return 0; -} - -/* - * Midi file operations struct. - */ -static /*const*/ struct file_operations cs_midi_fops = { - CS_OWNER CS_THIS_MODULE - llseek: no_llseek, - read: cs_midi_read, - write: cs_midi_write, - poll: cs_midi_poll, - open: cs_midi_open, - release: cs_midi_release, -}; - -/* - * - * CopySamples copies 16-bit stereo signed samples from the source to the - * destination, possibly converting down to unsigned 8-bit and/or mono. - * count specifies the number of output bytes to write. - * - * Arguments: - * - * dst - Pointer to a destination buffer. - * src - Pointer to a source buffer - * count - The number of bytes to copy into the destination buffer. - * fmt - CS_FMT_16BIT and/or CS_FMT_STEREO bits - * dmabuf - pointer to the dma buffer structure - * - * NOTES: only call this routine if the output desired is not 16 Signed Stereo - * - * - */ -static void CopySamples(char *dst, char *src, int count, unsigned fmt, - struct dmabuf *dmabuf) -{ - - s32 s32AudioSample; - s16 *psSrc=(s16 *)src; - s16 *psDst=(s16 *)dst; - u8 *pucDst=(u8 *)dst; - - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: CopySamples()+ ") ); - CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " dst=0x%x src=0x%x count=%d fmt=0x%x\n", - (unsigned)dst,(unsigned)src,(unsigned)count,(unsigned)fmt) ); - - /* - * See if the data should be output as 8-bit unsigned stereo. - */ - if((fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) - { - /* - * Convert each 16-bit signed stereo sample to 8-bit unsigned - * stereo using rounding. - */ - psSrc = (s16 *)src; - count = count/2; - while(count--) - { - *(pucDst++) = (u8)(((s16)(*psSrc++) + (s16)0x8000) >> 8); - } - } - /* - * See if the data should be output at 8-bit unsigned mono. - */ - else if(!(fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) - { - /* - * Convert each 16-bit signed stereo sample to 8-bit unsigned - * mono using averaging and rounding. - */ - psSrc = (s16 *)src; - count = count/2; - while(count--) - { - s32AudioSample = ((*psSrc)+(*(psSrc + 1)))/2 + (s32)0x80; - if(s32AudioSample > 0x7fff) - s32AudioSample = 0x7fff; - *(pucDst++) = (u8)(((s16)s32AudioSample + (s16)0x8000) >> 8); - psSrc += 2; - } - } - /* - * See if the data should be output at 16-bit signed mono. - */ - else if(!(fmt & CS_FMT_STEREO) && (fmt & CS_FMT_16BIT)) - { - /* - * Convert each 16-bit signed stereo sample to 16-bit signed - * mono using averaging. - */ - psSrc = (s16 *)src; - count = count/2; - while(count--) - { - *(psDst++) = (s16)((*psSrc)+(*(psSrc + 1)))/2; - psSrc += 2; - } - } -} - -/* - * cs_copy_to_user() - * replacement for the standard copy_to_user, to allow for a conversion from - * 16 bit to 8 bit and from stereo to mono, if the record conversion is active. - * The current CS46xx/CS4280 static image only records in 16bit unsigned Stereo, - * so we convert from any of the other format combinations. - */ -static unsigned cs_copy_to_user( - struct cs_state *s, - void *dest, - void *hwsrc, - unsigned cnt, - unsigned *copied) -{ - struct dmabuf *dmabuf = &s->dmabuf; - void *src = hwsrc; /* default to the standard destination buffer addr */ - - CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO - "cs_copy_to_user()+ fmt=0x%x cnt=%d dest=0x%.8x\n", - dmabuf->fmt,(unsigned)cnt,(unsigned)dest) ); - - if(cnt > dmabuf->dmasize) - { - cnt = dmabuf->dmasize; - } - if(!cnt) - { - *copied = 0; - return 0; - } - if(dmabuf->divisor != 1) - { - if(!dmabuf->tmpbuff) - { - *copied = cnt/dmabuf->divisor; - return 0; - } - - CopySamples((char *)dmabuf->tmpbuff, (char *)hwsrc, cnt, - dmabuf->fmt, dmabuf); - src = dmabuf->tmpbuff; - cnt = cnt/dmabuf->divisor; - } - if (copy_to_user(dest, src, cnt)) - { - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR - "cs46xx: cs_copy_to_user()- fault dest=0x%x src=0x%x cnt=%d\n", - (unsigned)dest,(unsigned)src,cnt) ); - *copied = 0; - return -EFAULT; - } - *copied = cnt; - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO - "cs46xx: cs_copy_to_user()- copied bytes is %d \n",cnt) ); - return 0; -} - -/* 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 cs_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct cs_card *card = (struct cs_card *) file->private_data; - struct cs_state *state; - DECLARE_WAITQUEUE(wait, current); - struct dmabuf *dmabuf; - ssize_t ret = 0; - unsigned long flags; - unsigned swptr; - int cnt; - unsigned copied=0; - - CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, - printk("cs46xx: cs_read()+ %d\n",count) ); - state = (struct cs_state *)card->states[0]; - if(!state) - return -ENODEV; - dmabuf = &state->dmabuf; - - if (ppos != &file->f_pos) - return -ESPIPE; - if (dmabuf->mapped) - return -ENXIO; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - - down(&state->sem); - if (!dmabuf->ready && (ret = __prog_dmabuf(state))) - goto out; - - add_wait_queue(&state->dmabuf.wait, &wait); - while (count > 0) { - while(!(card->pm.flags & CS46XX_PM_IDLE)) - { - schedule(); - if (signal_pending(current)) { - if(!ret) ret = -ERESTARTSYS; - goto out; - } - } - spin_lock_irqsave(&state->card->lock, flags); - swptr = dmabuf->swptr; - cnt = dmabuf->dmasize - swptr; - if (dmabuf->count < cnt) - cnt = dmabuf->count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&state->card->lock, flags); - - if (cnt > (count * dmabuf->divisor)) - cnt = count * dmabuf->divisor; - if (cnt <= 0) { - /* buffer is empty, start the dma machine and wait for data to be - recorded */ - start_adc(state); - if (file->f_flags & O_NONBLOCK) { - if (!ret) ret = -EAGAIN; - goto out; - } - up(&state->sem); - schedule(); - if (signal_pending(current)) { - if(!ret) ret = -ERESTARTSYS; - goto out; - } - down(&state->sem); - if (dmabuf->mapped) - { - if(!ret) - ret = -ENXIO; - goto out; - } - continue; - } - - CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO - "_read() copy_to cnt=%d count=%d ", cnt,count) ); - CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " .dmasize=%d .count=%d buffer=0x%.8x ret=%d\n", - dmabuf->dmasize,dmabuf->count,(unsigned)buffer,ret) ); - - if (cs_copy_to_user(state, buffer, - (void *)((unsigned)dmabuf->rawbuf + swptr), cnt, &copied)) - { - if (!ret) ret = -EFAULT; - goto out; - } - swptr = (swptr + cnt) % dmabuf->dmasize; - spin_lock_irqsave(&card->lock, flags); - dmabuf->swptr = swptr; - dmabuf->count -= cnt; - spin_unlock_irqrestore(&card->lock, flags); - count -= copied; - buffer += copied; - ret += copied; - start_adc(state); - } -out: - up(&state->sem); - remove_wait_queue(&state->dmabuf.wait, &wait); - set_current_state(TASK_RUNNING); - CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, - printk("cs46xx: cs_read()- %d\n",ret) ); - 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 cs_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct cs_card *card = (struct cs_card *) file->private_data; - struct cs_state *state; - DECLARE_WAITQUEUE(wait, current); - struct dmabuf *dmabuf; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 4, - printk("cs46xx: cs_write called, count = %d\n", count) ); - state = (struct cs_state *)card->states[1]; - if(!state) - return -ENODEV; - dmabuf = &state->dmabuf; - - if (ppos != &file->f_pos) - return -ESPIPE; - - down(&state->sem); - if (dmabuf->mapped) - { - ret = -ENXIO; - goto out; - } - - 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; -/* -* Start the loop to read from the user's buffer and write to the dma buffer. -* check for PM events and underrun/overrun in the loop. -*/ - while (count > 0) { - while(!(card->pm.flags & CS46XX_PM_IDLE)) - { - schedule(); - if (signal_pending(current)) { - if(!ret) ret = -ERESTARTSYS; - goto out; - } - } - spin_lock_irqsave(&state->card->lock, flags); - if (dmabuf->count < 0) { - /* buffer underrun, we are recovering from sleep_on_timeout, - resync hwptr and swptr */ - dmabuf->count = 0; - dmabuf->swptr = dmabuf->hwptr; - } - if (dmabuf->underrun) - { - dmabuf->underrun = 0; - dmabuf->hwptr = cs_get_dma_addr(state); - dmabuf->swptr = dmabuf->hwptr; - } - - swptr = dmabuf->swptr; - cnt = dmabuf->dmasize - swptr; - if (dmabuf->count + cnt > dmabuf->dmasize) - cnt = dmabuf->dmasize - dmabuf->count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&state->card->lock, flags); - - if (cnt > count) - cnt = count; - if (cnt <= 0) { - /* buffer is full, start the dma machine and wait for data to be - played */ - start_dac(state); - if (file->f_flags & O_NONBLOCK) { - if (!ret) ret = -EAGAIN; - goto out; - } - up(&state->sem); - schedule(); - if (signal_pending(current)) { - if(!ret) ret = -ERESTARTSYS; - goto out; - } - down(&state->sem); - if (dmabuf->mapped) - { - if(!ret) - ret = -ENXIO; - goto out; - } - continue; - } - if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { - if (!ret) ret = -EFAULT; - goto out; - } - spin_lock_irqsave(&state->card->lock, flags); - swptr = (swptr + cnt) % dmabuf->dmasize; - dmabuf->swptr = swptr; - dmabuf->count += cnt; - if(dmabuf->count > dmabuf->dmasize) - { - CS_DBGOUT(CS_WAVE_WRITE | CS_ERROR, 2, printk( - "cs46xx: cs_write() d->count > dmasize - resetting\n")); - dmabuf->count = dmabuf->dmasize; - } - dmabuf->endcleared = 0; - spin_unlock_irqrestore(&state->card->lock, flags); - - count -= cnt; - buffer += cnt; - ret += cnt; - start_dac(state); - } -out: - up(&state->sem); - remove_wait_queue(&state->dmabuf.wait, &wait); - set_current_state(TASK_RUNNING); - - CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 2, - printk("cs46xx: cs_write()- ret=0x%x\n", ret) ); - return ret; -} - -static unsigned int cs_poll(struct file *file, struct poll_table_struct *wait) -{ - struct cs_card *card = (struct cs_card *)file->private_data; - struct dmabuf *dmabuf; - struct cs_state *state; - - unsigned long flags; - unsigned int mask = 0; - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()+ \n")); - if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) - { - return -EINVAL; - } - if (file->f_mode & FMODE_WRITE) - { - state = card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - poll_wait(file, &dmabuf->wait, wait); - } - } - if (file->f_mode & FMODE_READ) - { - state = card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - poll_wait(file, &dmabuf->wait, wait); - } - } - - spin_lock_irqsave(&card->lock, flags); - cs_update_ptr(card, CS_FALSE); - if (file->f_mode & FMODE_READ) { - state = card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - if (dmabuf->count >= (signed)dmabuf->fragsize) - mask |= POLLIN | POLLRDNORM; - } - } - if (file->f_mode & FMODE_WRITE) { - state = card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - if (dmabuf->mapped) { - if (dmabuf->count >= (signed)dmabuf->fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)dmabuf->dmasize >= dmabuf->count - + (signed)dmabuf->fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - } - spin_unlock_irqrestore(&card->lock, flags); - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()- (0x%x) \n", - mask)); - return mask; -} - -/* - * We let users mmap the ring buffer. Its not the real DMA buffer but - * that side of the code is hidden in the IRQ handling. We do a software - * emulation of DMA from a 64K or so buffer into a 2K FIFO. - * (the hardware probably deserves a moan here but Crystal send me nice - * toys ;)). - */ - -static int cs_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cs_card *card = (struct cs_card *)file->private_data; - struct cs_state *state; - struct dmabuf *dmabuf; - int ret = 0; - unsigned long size; - - CS_DBGOUT(CS_FUNCTION | CS_PARMS, 2, printk("cs46xx: cs_mmap()+ file=0x%x %s %s\n", - (unsigned)file, vma->vm_flags & VM_WRITE ? "VM_WRITE" : "", - vma->vm_flags & VM_READ ? "VM_READ" : "") ); - - if (vma->vm_flags & VM_WRITE) { - state = card->states[1]; - if(state) - { - CS_DBGOUT(CS_OPEN, 2, printk( - "cs46xx: cs_mmap() VM_WRITE - state TRUE prog_dmabuf DAC\n") ); - if ((ret = prog_dmabuf(state)) != 0) - return ret; - } - } else if (vma->vm_flags & VM_READ) { - state = card->states[0]; - if(state) - { - CS_DBGOUT(CS_OPEN, 2, printk( - "cs46xx: cs_mmap() VM_READ - state TRUE prog_dmabuf ADC\n") ); - if ((ret = prog_dmabuf(state)) != 0) - return ret; - } - } else { - CS_DBGOUT(CS_ERROR, 2, printk( - "cs46xx: cs_mmap() return -EINVAL\n") ); - return -EINVAL; - } - -/* - * For now ONLY support playback, but seems like the only way to use - * mmap() is to open an FD with RDWR, just read or just write access - * does not function, get an error back from the kernel. - * Also, QuakeIII opens with RDWR! So, there must be something - * to needing read/write access mapping. So, allow read/write but - * use the DAC only. - */ - state = card->states[1]; - if(!(unsigned)state) - { - ret = -EINVAL; - goto out; - } - - down(&state->sem); - dmabuf = &state->dmabuf; - if (cs4x_pgoff(vma) != 0) - { - ret = -EINVAL; - goto out; - } - size = vma->vm_end - vma->vm_start; - - CS_DBGOUT(CS_PARMS, 2, printk("cs46xx: cs_mmap(): size=%d\n",(unsigned)size) ); - - if (size > (PAGE_SIZE << dmabuf->buforder)) - { - ret = -EINVAL; - goto out; - } - if (remap_page_range(vma, vma->vm_start, virt_to_phys(dmabuf->rawbuf), - size, vma->vm_page_prot)) - { - ret = -EAGAIN; - goto out; - } - dmabuf->mapped = 1; - - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_mmap()-\n") ); -out: - up(&state->sem); - return ret; -} - -static int cs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct cs_card *card = (struct cs_card *)file->private_data; - struct cs_state *state; - struct dmabuf *dmabuf=0; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, valsave, mapped, ret; - - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - mapped = (file->f_mode & FMODE_READ) && dmabuf->mapped; - } - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - mapped |= (file->f_mode & FMODE_WRITE) && dmabuf->mapped; - } - -#if CSDEBUG - printioctl(cmd); -#endif - - switch (cmd) - { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_RESET: - /* FIXME: spin_lock ? */ - if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - stop_dac(state); - synchronize_irq(); - dmabuf->ready = 0; - resync_dma_ptrs(state); - dmabuf->swptr = dmabuf->hwptr = 0; - dmabuf->count = dmabuf->total_bytes = 0; - dmabuf->blocks = 0; - dmabuf->SGok = 0; - } - } - if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - stop_adc(state); - synchronize_irq(); - resync_dma_ptrs(state); - dmabuf->ready = 0; - dmabuf->swptr = dmabuf->hwptr = 0; - dmabuf->count = dmabuf->total_bytes = 0; - dmabuf->blocks = 0; - dmabuf->SGok = 0; - } - } - CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_RESET()-\n") ); - return 0; - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - return drain_dac(state, file->f_flags & O_NONBLOCK); - return 0; - - case SNDCTL_DSP_SPEED: /* set sample rate */ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val >= 0) { - if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - stop_adc(state); - dmabuf->ready = 0; - dmabuf->SGok = 0; - cs_set_adc_rate(state, val); - cs_set_divisor(dmabuf); - } - } - if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - stop_dac(state); - dmabuf->ready = 0; - dmabuf->SGok = 0; - cs_set_dac_rate(state, val); - cs_set_divisor(dmabuf); - } - } - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( - "cs46xx: cs_ioctl() DSP_SPEED %s %s %d\n", - file->f_mode & FMODE_WRITE ? "DAC" : "", - file->f_mode & FMODE_READ ? "ADC" : "", - dmabuf->rate ) ); - return put_user(dmabuf->rate, (int *)arg); - } - return put_user(0, (int *)arg); - - case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - stop_dac(state); - dmabuf->ready = 0; - dmabuf->SGok = 0; - if(val) - dmabuf->fmt |= CS_FMT_STEREO; - else - dmabuf->fmt &= ~CS_FMT_STEREO; - cs_set_divisor(dmabuf); - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( - "cs46xx: DSP_STEREO() DAC %s\n", - (dmabuf->fmt & CS_FMT_STEREO) ? - "STEREO":"MONO") ); - } - } - if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - stop_adc(state); - dmabuf->ready = 0; - dmabuf->SGok = 0; - if(val) - dmabuf->fmt |= CS_FMT_STEREO; - else - dmabuf->fmt &= ~CS_FMT_STEREO; - cs_set_divisor(dmabuf); - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( - "cs46xx: DSP_STEREO() ADC %s\n", - (dmabuf->fmt & CS_FMT_STEREO) ? - "STEREO":"MONO") ); - } - } - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - if ((val = prog_dmabuf(state))) - return val; - return put_user(dmabuf->fragsize, (int *)arg); - } - } - if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - if ((val = prog_dmabuf(state))) - return val; - return put_user(dmabuf->fragsize/dmabuf->divisor, - (int *)arg); - } - } - return put_user(0, (int *)arg); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ - return put_user(AFMT_S16_LE | AFMT_U8, (int *)arg); - - case SNDCTL_DSP_SETFMT: /* Select sample format */ - if (get_user(val, (int *)arg)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( - "cs46xx: cs_ioctl() DSP_SETFMT %s %s %s %s\n", - file->f_mode & FMODE_WRITE ? "DAC" : "", - file->f_mode & FMODE_READ ? "ADC" : "", - val == AFMT_S16_LE ? "16Bit Signed" : "", - val == AFMT_U8 ? "8Bit Unsigned" : "") ); - valsave = val; - if (val != AFMT_QUERY) { - if(val==AFMT_S16_LE || val==AFMT_U8) - { - if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - stop_dac(state); - dmabuf->ready = 0; - dmabuf->SGok = 0; - if(val==AFMT_S16_LE) - dmabuf->fmt |= CS_FMT_16BIT; - else - dmabuf->fmt &= ~CS_FMT_16BIT; - cs_set_divisor(dmabuf); - if((ret = prog_dmabuf(state))) - return ret; - } - } - if (file->f_mode & FMODE_READ) { - val = valsave; - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - stop_adc(state); - dmabuf->ready = 0; - dmabuf->SGok = 0; - if(val==AFMT_S16_LE) - dmabuf->fmt |= CS_FMT_16BIT; - else - dmabuf->fmt &= ~CS_FMT_16BIT; - cs_set_divisor(dmabuf); - if((ret = prog_dmabuf(state))) - return ret; - } - } - } - else - { - CS_DBGOUT(CS_IOCTL | CS_ERROR, 2, printk( - "cs46xx: DSP_SETFMT() Unsupported format (0x%x)\n", - valsave) ); - } - } - else - { - if(file->f_mode & FMODE_WRITE) - { - state = (struct cs_state *)card->states[1]; - if(state) - dmabuf = &state->dmabuf; - } - else if(file->f_mode & FMODE_READ) - { - state = (struct cs_state *)card->states[0]; - if(state) - dmabuf = &state->dmabuf; - } - } - if(dmabuf) - { - if(dmabuf->fmt & CS_FMT_16BIT) - return put_user(AFMT_S16_LE, (int *)arg); - else - return put_user(AFMT_U8, (int *)arg); - } - return put_user(0, (int *)arg); - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) { - if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - stop_dac(state); - dmabuf->ready = 0; - dmabuf->SGok = 0; - if(val>1) - dmabuf->fmt |= CS_FMT_STEREO; - else - dmabuf->fmt &= ~CS_FMT_STEREO; - cs_set_divisor(dmabuf); - if (prog_dmabuf(state)) - return 0; - } - } - if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - stop_adc(state); - dmabuf->ready = 0; - dmabuf->SGok = 0; - if(val>1) - dmabuf->fmt |= CS_FMT_STEREO; - else - dmabuf->fmt &= ~CS_FMT_STEREO; - cs_set_divisor(dmabuf); - if (prog_dmabuf(state)) - return 0; - } - } - } - return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1, - (int *)arg); - - case SNDCTL_DSP_POST: - /* - * There will be a longer than normal pause in the data. - * so... do nothing, because there is nothing that we can do. - */ - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - if (dmabuf->subdivision) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 1 && val != 2) - return -EINVAL; - dmabuf->subdivision = val; - } - } - if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - if (dmabuf->subdivision) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 1 && val != 2) - return -EINVAL; - dmabuf->subdivision = val; - } - } - return 0; - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) - return -EFAULT; - - if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - dmabuf->ossfragshift = val & 0xffff; - dmabuf->ossmaxfrags = (val >> 16) & 0xffff; - } - } - if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - dmabuf->ossfragshift = val & 0xffff; - dmabuf->ossmaxfrags = (val >> 16) & 0xffff; - } - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - spin_lock_irqsave(&state->card->lock, flags); - cs_update_ptr(card, CS_TRUE); - abinfo.fragsize = dmabuf->fragsize; - abinfo.fragstotal = dmabuf->numfrag; - /* - * for mmap we always have total space available - */ - if (dmabuf->mapped) - abinfo.bytes = dmabuf->dmasize; - else - abinfo.bytes = dmabuf->dmasize - dmabuf->count; - - abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; - spin_unlock_irqrestore(&state->card->lock, flags); - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - } - return -ENODEV; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - spin_lock_irqsave(&state->card->lock, flags); - cs_update_ptr(card, CS_TRUE); - abinfo.fragsize = dmabuf->fragsize/dmabuf->divisor; - abinfo.bytes = dmabuf->count/dmabuf->divisor; - abinfo.fragstotal = dmabuf->numfrag; - abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; - spin_unlock_irqrestore(&state->card->lock, flags); - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - } - return -ENODEV; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, - (int *)arg); - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()+\n") ); - if (file->f_mode & FMODE_WRITE) - { - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - if(dmabuf->enable & DAC_RUNNING) - val |= PCM_ENABLE_INPUT; - } - } - if (file->f_mode & FMODE_READ) - { - if(state) - { - state = (struct cs_state *)card->states[0]; - dmabuf = &state->dmabuf; - if(dmabuf->enable & ADC_RUNNING) - val |= PCM_ENABLE_OUTPUT; - } - } - CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()- val=0x%x\n",val) ); - return put_user(val, (int *)arg); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - if (val & PCM_ENABLE_INPUT) { - if (!dmabuf->ready && (ret = prog_dmabuf(state))) - return ret; - start_adc(state); - } else - stop_adc(state); - } - } - if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - if (val & PCM_ENABLE_OUTPUT) { - if (!dmabuf->ready && (ret = prog_dmabuf(state))) - return ret; - start_dac(state); - } else - stop_dac(state); - } - } - return 0; - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - state = (struct cs_state *)card->states[0]; - if(state) - { - dmabuf = &state->dmabuf; - spin_lock_irqsave(&state->card->lock, flags); - cs_update_ptr(card, CS_TRUE); - cinfo.bytes = dmabuf->total_bytes/dmabuf->divisor; - cinfo.blocks = dmabuf->count/dmabuf->divisor >> dmabuf->fragshift; - cinfo.ptr = dmabuf->hwptr/dmabuf->divisor; - spin_unlock_irqrestore(&state->card->lock, flags); - return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); - } - return -ENODEV; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - spin_lock_irqsave(&state->card->lock, flags); - cs_update_ptr(card, CS_TRUE); - cinfo.bytes = dmabuf->total_bytes; - if (dmabuf->mapped) - { - cinfo.blocks = (cinfo.bytes >> dmabuf->fragshift) - - dmabuf->blocks; - CS_DBGOUT(CS_PARMS, 8, - printk("total_bytes=%d blocks=%d dmabuf->blocks=%d\n", - cinfo.bytes,cinfo.blocks,dmabuf->blocks) ); - dmabuf->blocks = cinfo.bytes >> dmabuf->fragshift; - } - else - { - cinfo.blocks = dmabuf->count >> dmabuf->fragshift; - } - cinfo.ptr = dmabuf->hwptr; - - CS_DBGOUT(CS_PARMS, 4, printk( - "cs46xx: GETOPTR bytes=%d blocks=%d ptr=%d\n", - cinfo.bytes,cinfo.blocks,cinfo.ptr) ); - spin_unlock_irqrestore(&state->card->lock, flags); - return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); - } - return -ENODEV; - - case SNDCTL_DSP_SETDUPLEX: - return -EINVAL; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - spin_lock_irqsave(&state->card->lock, flags); - cs_update_ptr(card, CS_TRUE); - val = dmabuf->count; - spin_unlock_irqrestore(&state->card->lock, flags); - } - else - val = 0; - return put_user(val, (int *)arg); - - case SOUND_PCM_READ_RATE: - if(file->f_mode & FMODE_READ) - state = (struct cs_state *)card->states[0]; - else - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - return put_user(dmabuf->rate, (int *)arg); - } - return put_user(0, (int *)arg); - - - case SOUND_PCM_READ_CHANNELS: - if(file->f_mode & FMODE_READ) - state = (struct cs_state *)card->states[0]; - else - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1, - (int *)arg); - } - return put_user(0, (int *)arg); - - case SOUND_PCM_READ_BITS: - if(file->f_mode & FMODE_READ) - state = (struct cs_state *)card->states[0]; - else - state = (struct cs_state *)card->states[1]; - if(state) - { - dmabuf = &state->dmabuf; - return put_user((dmabuf->fmt & CS_FMT_16BIT) ? - AFMT_S16_LE : AFMT_U8, (int *)arg); - - } - return put_user(0, (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; -} - - -/* - * AMP control - null AMP - */ - -static void amp_none(struct cs_card *card, int change) -{ -} - -/* - * Crystal EAPD mode - */ - -static void amp_voyetra(struct cs_card *card, int change) -{ - /* Manage the EAPD bit on the Crystal 4297 - and the Analog AD1885 */ - - int old=card->amplifier; - - card->amplifier+=change; - if(card->amplifier && !old) - { - /* Turn the EAPD amp on */ - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, - cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) | - 0x8000); - } - else if(old && !card->amplifier) - { - /* Turn the EAPD amp off */ - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, - cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - ~0x8000); - } -} - - -/* - * Game Theatre XP card - EGPIO[2] is used to enable the external amp. - */ - -static void amp_hercules(struct cs_card *card, int change) -{ - int old=card->amplifier; - if(!card) - { - CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO - "cs46xx: amp_hercules() called before initialized.\n")); - return; - } - card->amplifier+=change; - if( (card->amplifier && !old) && !(hercules_egpio_disable)) - { - CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO - "cs46xx: amp_hercules() external amp enabled\n")); - cs461x_pokeBA0(card, BA0_EGPIODR, - EGPIODR_GPOE2); /* enable EGPIO2 output */ - cs461x_pokeBA0(card, BA0_EGPIOPTR, - EGPIOPTR_GPPT2); /* open-drain on output */ - } - else if(old && !card->amplifier) - { - CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO - "cs46xx: amp_hercules() external amp disabled\n")); - cs461x_pokeBA0(card, BA0_EGPIODR, 0); /* disable */ - cs461x_pokeBA0(card, BA0_EGPIOPTR, 0); /* disable */ - } -} - -/* - * Handle the CLKRUN on a thinkpad. We must disable CLKRUN support - * whenever we need to beat on the chip. - * - * The original idea and code for this hack comes from David Kaiser at - * Linuxcare. Perhaps one day Crystal will document their chips well - * enough to make them useful. - */ - -static void clkrun_hack(struct cs_card *card, int change) -{ - struct pci_dev *acpi_dev; - u16 control; - u8 pp; - unsigned long port; - int old=card->active; - - card->active+=change; - - acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); - if(acpi_dev == NULL) - return; /* Not a thinkpad thats for sure */ - - /* Find the control port */ - pci_read_config_byte(acpi_dev, 0x41, &pp); - port=pp<<8; - - /* Read ACPI port */ - control=inw(port+0x10); - - /* Flip CLKRUN off while running */ - if(!card->active && old) - { - CS_DBGOUT(CS_PARMS , 9, printk( KERN_INFO - "cs46xx: clkrun() enable clkrun - change=%d active=%d\n", - change,card->active)); - outw(control|0x2000, port+0x10); - } - else - { - /* - * sometimes on a resume the bit is set, so always reset the bit. - */ - CS_DBGOUT(CS_PARMS , 9, printk( KERN_INFO - "cs46xx: clkrun() disable clkrun - change=%d active=%d\n", - change,card->active)); - outw(control&~0x2000, port+0x10); - } -} - - -static int cs_open(struct inode *inode, struct file *file) -{ - struct cs_card *card = (struct cs_card *)file->private_data; - struct cs_state *state = NULL; - struct dmabuf *dmabuf = NULL; - struct list_head *entry; - unsigned int minor = minor(inode->i_rdev); - int ret=0; - unsigned int tmp; - - CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()+ file=0x%x %s %s\n", - (unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "", - file->f_mode & FMODE_READ ? "FMODE_READ" : "") ); - - list_for_each(entry, &cs46xx_devs) - { - card = list_entry(entry, struct cs_card, list); - - if (!((card->dev_audio ^ minor) & ~0xf)) - break; - } - if (entry == &cs46xx_devs) - return -ENODEV; - if (!card) { - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs46xx: cs_open(): Error - unable to find audio card struct\n")); - return -ENODEV; - } - - /* - * hardcode state[0] for capture, [1] for playback - */ - if(file->f_mode & FMODE_READ) - { - CS_DBGOUT(CS_WAVE_READ, 2, printk("cs46xx: cs_open() FMODE_READ\n") ); - if (card->states[0] == NULL) { - state = card->states[0] = (struct cs_state *) - kmalloc(sizeof(struct cs_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - memset(state, 0, sizeof(struct cs_state)); - init_MUTEX(&state->sem); - dmabuf = &state->dmabuf; - dmabuf->pbuf = (void *)get_free_page(GFP_KERNEL | GFP_DMA); - if(dmabuf->pbuf==NULL) - { - kfree(state); - card->states[0]=NULL; - return -ENOMEM; - } - } - else - { - state = card->states[0]; - if(state->open_mode & FMODE_READ) - return -EBUSY; - } - dmabuf->channel = card->alloc_rec_pcm_channel(card); - - if (dmabuf->channel == NULL) { - kfree (card->states[0]); - card->states[0] = NULL;; - return -ENODEV; - } - - /* Now turn on external AMP if needed */ - state->card = card; - state->card->active_ctrl(state->card,1); - state->card->amplifier_ctrl(state->card,1); - - if( (tmp = cs46xx_powerup(card, CS_POWER_ADC)) ) - { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs46xx_powerup of ADC failed (0x%x)\n",tmp) ); - return -EIO; - } - - dmabuf->channel->state = state; - /* initialize the virtual channel */ - state->virt = 0; - state->magic = CS_STATE_MAGIC; - init_waitqueue_head(&dmabuf->wait); - init_MUTEX(&state->open_sem); - file->private_data = card; - - down(&state->open_sem); - - /* 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 */ - - /* Default input is 8bit mono */ - dmabuf->fmt &= ~CS_FMT_MASK; - dmabuf->type = CS_TYPE_ADC; - dmabuf->ossfragshift = 0; - dmabuf->ossmaxfrags = 0; - dmabuf->subdivision = 0; - cs_set_adc_rate(state, 8000); - cs_set_divisor(dmabuf); - - state->open_mode |= FMODE_READ; - up(&state->open_sem); - } - if(file->f_mode & FMODE_WRITE) - { - CS_DBGOUT(CS_OPEN, 2, printk("cs46xx: cs_open() FMODE_WRITE\n") ); - if (card->states[1] == NULL) { - state = card->states[1] = (struct cs_state *) - kmalloc(sizeof(struct cs_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - memset(state, 0, sizeof(struct cs_state)); - init_MUTEX(&state->sem); - dmabuf = &state->dmabuf; - dmabuf->pbuf = (void *)get_free_page(GFP_KERNEL | GFP_DMA); - if(dmabuf->pbuf==NULL) - { - kfree(state); - card->states[1]=NULL; - return -ENOMEM; - } - } - else - { - state = card->states[1]; - if(state->open_mode & FMODE_WRITE) - return -EBUSY; - } - dmabuf->channel = card->alloc_pcm_channel(card); - - if (dmabuf->channel == NULL) { - kfree (card->states[1]); - card->states[1] = NULL;; - return -ENODEV; - } - - /* Now turn on external AMP if needed */ - state->card = card; - state->card->active_ctrl(state->card,1); - state->card->amplifier_ctrl(state->card,1); - - if( (tmp = cs46xx_powerup(card, CS_POWER_DAC)) ) - { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs46xx_powerup of DAC failed (0x%x)\n",tmp) ); - return -EIO; - } - - dmabuf->channel->state = state; - /* initialize the virtual channel */ - state->virt = 1; - state->magic = CS_STATE_MAGIC; - init_waitqueue_head(&dmabuf->wait); - init_MUTEX(&state->open_sem); - file->private_data = card; - - down(&state->open_sem); - - /* 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 */ - - /* Default output is 8bit mono. */ - dmabuf->fmt &= ~CS_FMT_MASK; - dmabuf->type = CS_TYPE_DAC; - dmabuf->ossfragshift = 0; - dmabuf->ossmaxfrags = 0; - dmabuf->subdivision = 0; - cs_set_dac_rate(state, 8000); - cs_set_divisor(dmabuf); - - state->open_mode |= FMODE_WRITE; - up(&state->open_sem); - if((ret = prog_dmabuf(state))) - return ret; - } - MOD_INC_USE_COUNT; /* for 2.2 */ - CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()- 0\n") ); - return 0; -} - -static int cs_release(struct inode *inode, struct file *file) -{ - struct cs_card *card = (struct cs_card *)file->private_data; - struct dmabuf *dmabuf; - struct cs_state *state; - unsigned int tmp; - CS_DBGOUT(CS_RELEASE | CS_FUNCTION, 2, printk("cs46xx: cs_release()+ file=0x%x %s %s\n", - (unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "", - file->f_mode & FMODE_READ ? "FMODE_READ" : "") ); - - if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) - { - return -EINVAL; - } - state = card->states[1]; - if(state) - { - if ( (state->open_mode & FMODE_WRITE) & (file->f_mode & FMODE_WRITE) ) - { - CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_WRITE\n") ); - dmabuf = &state->dmabuf; - cs_clear_tail(state); - drain_dac(state, file->f_flags & O_NONBLOCK); - /* stop DMA state machine and free DMA buffers/channels */ - down(&state->open_sem); - stop_dac(state); - dealloc_dmabuf(state); - state->card->free_pcm_channel(state->card, dmabuf->channel->num); - free_page((unsigned long)state->dmabuf.pbuf); - - /* we're covered by the open_sem */ - up(&state->open_sem); - state->card->states[state->virt] = NULL; - state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - - if( (tmp = cs461x_powerdown(card, CS_POWER_DAC, CS_FALSE )) ) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs46xx: cs_release_mixdev() powerdown DAC failure (0x%x)\n",tmp) ); - } - - /* Now turn off external AMP if needed */ - state->card->amplifier_ctrl(state->card, -1); - state->card->active_ctrl(state->card, -1); - - kfree(state); - } - } - - state = card->states[0]; - if(state) - { - if ( (state->open_mode & FMODE_READ) & (file->f_mode & FMODE_READ) ) - { - CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_READ\n") ); - dmabuf = &state->dmabuf; - down(&state->open_sem); - stop_adc(state); - dealloc_dmabuf(state); - state->card->free_pcm_channel(state->card, dmabuf->channel->num); - free_page((unsigned long)state->dmabuf.pbuf); - - /* we're covered by the open_sem */ - up(&state->open_sem); - state->card->states[state->virt] = NULL; - state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - - if( (tmp = cs461x_powerdown(card, CS_POWER_ADC, CS_FALSE )) ) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs46xx: cs_release_mixdev() powerdown ADC failure (0x%x)\n",tmp) ); - } - - /* Now turn off external AMP if needed */ - state->card->amplifier_ctrl(state->card, -1); - state->card->active_ctrl(state->card, -1); - - kfree(state); - } - } - - CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk("cs46xx: cs_release()- 0\n") ); - MOD_DEC_USE_COUNT; /* For 2.2 */ - return 0; -} - -static void printpm(struct cs_card *s) -{ - CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); - CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n", - (unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue)); - CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n", - s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue)); - CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n", - s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue)); - CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n", - s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue)); - CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n", - s->pm.u32SSCR,s->pm.u32SRCSA)); - CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n", - s->pm.u32DacASR,s->pm.u32AdcASR)); - CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n", - s->pm.u32DacSR,s->pm.u32AdcSR)); - CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n", - s->pm.u32MIDCR_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32AC97_powerdown: 0x%x _general_purpose 0x%x\n", - s->pm.u32AC97_powerdown,s->pm.u32AC97_general_purpose)); - CS_DBGOUT(CS_PM, 9, printk("u32AC97_master_volume: 0x%x\n", - s->pm.u32AC97_master_volume)); - CS_DBGOUT(CS_PM, 9, printk("u32AC97_headphone_volume: 0x%x\n", - s->pm.u32AC97_headphone_volume)); - CS_DBGOUT(CS_PM, 9, printk("u32AC97_master_volume_mono: 0x%x\n", - s->pm.u32AC97_master_volume_mono)); - CS_DBGOUT(CS_PM, 9, printk("u32AC97_pcm_out_volume: 0x%x\n", - s->pm.u32AC97_pcm_out_volume)); - CS_DBGOUT(CS_PM, 9, printk("dmabuf_swptr_play: 0x%x dmabuf_count_play: %d\n", - s->pm.dmabuf_swptr_play,s->pm.dmabuf_count_play)); - CS_DBGOUT(CS_PM, 9, printk("dmabuf_swptr_capture: 0x%x dmabuf_count_capture: %d\n", - s->pm.dmabuf_swptr_capture,s->pm.dmabuf_count_capture)); - -} - -/**************************************************************************** -* -* Suspend - save the ac97 regs, mute the outputs and power down the part. -* -****************************************************************************/ -void cs46xx_ac97_suspend(struct cs_card *card) -{ - int Count,i; - struct ac97_codec *dev=card->ac97_codec[0]; - unsigned int tmp; - - CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()+\n")); - - if(card->states[1]) - { - stop_dac(card->states[1]); - resync_dma_ptrs(card->states[1]); - } - if(card->states[0]) - { - stop_adc(card->states[0]); - resync_dma_ptrs(card->states[0]); - } - - for(Count = 0x2, i=0; (Count <= CS46XX_AC97_HIGHESTREGTORESTORE) - && (i < CS46XX_AC97_NUMBER_RESTORE_REGS); - Count += 2, i++) - { - card->pm.ac97[i] = cs_ac97_get(dev, BA0_AC97_RESET + Count); - } -/* -* Save the ac97 volume registers as well as the current powerdown state. -* Now, mute the all the outputs (master, headphone, and mono), as well -* as the PCM volume, in preparation for powering down the entire part. - card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev, - (u8)BA0_AC97_MASTER_VOLUME); - card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev, - (u8)BA0_AC97_HEADPHONE_VOLUME); - card->pm.u32AC97_master_volume_mono = (u32)cs_ac97_get(dev, - (u8)BA0_AC97_MASTER_VOLUME_MONO); - card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev, - (u8)BA0_AC97_PCM_OUT_VOLUME); -*/ -/* -* mute the outputs -*/ - cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000); - cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000); - cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000); - cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000); - -/* -* save the registers that cause pops -*/ - card->pm.u32AC97_powerdown = (u32)cs_ac97_get(dev, (u8)AC97_POWER_CONTROL); - card->pm.u32AC97_general_purpose = (u32)cs_ac97_get(dev, (u8)BA0_AC97_GENERAL_PURPOSE); -/* -* And power down everything on the AC97 codec. -* well, for now, only power down the DAC/ADC and MIXER VREFON components. -* trouble with removing VREF. -*/ - if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | - CS_POWER_MIXVON, CS_TRUE )) ) - { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs46xx_ac97_suspend() failure (0x%x)\n",tmp) ); - } - - CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()-\n")); -} - -/**************************************************************************** -* -* Resume - power up the part and restore its registers.. -* -****************************************************************************/ -void cs46xx_ac97_resume(struct cs_card *card) -{ - int Count,i; - struct ac97_codec *dev=card->ac97_codec[0]; - - CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_resume()+\n")); - -/* -* First, we restore the state of the general purpose register. This -* contains the mic select (mic1 or mic2) and if we restore this after -* we restore the mic volume/boost state and mic2 was selected at -* suspend time, we will end up with a brief period of time where mic1 -* is selected with the volume/boost settings for mic2, causing -* acoustic feedback. So we restore the general purpose register -* first, thereby getting the correct mic selected before we restore -* the mic volume/boost. -*/ - cs_ac97_set(dev, (u8)BA0_AC97_GENERAL_PURPOSE, - (u16)card->pm.u32AC97_general_purpose); -/* -* Now, while the outputs are still muted, restore the state of power -* on the AC97 part. -*/ - cs_ac97_set(dev, (u8)BA0_AC97_POWERDOWN, (u16)card->pm.u32AC97_powerdown); - mdelay(5 * cs_laptop_wait); -/* -* Restore just the first set of registers, from register number -* 0x02 to the register number that ulHighestRegToRestore specifies. -*/ - for( Count = 0x2, i=0; - (Count <= CS46XX_AC97_HIGHESTREGTORESTORE) - && (i < CS46XX_AC97_NUMBER_RESTORE_REGS); - Count += 2, i++) - { - cs_ac97_set(dev, (u8)(BA0_AC97_RESET + Count), (u16)card->pm.ac97[i]); - } - - /* Check if we have to init the amplifier */ - if(card->amp_init) - card->amp_init(card); - - CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_resume()-\n")); -} - - -static int cs46xx_restart_part(struct cs_card *card) -{ - struct dmabuf *dmabuf; - CS_DBGOUT(CS_PM | CS_FUNCTION, 4, - printk( "cs46xx: cs46xx_restart_part()+\n")); - if(card->states[1]) - { - dmabuf = &card->states[1]->dmabuf; - dmabuf->ready = 0; - resync_dma_ptrs(card->states[1]); - cs_set_divisor(dmabuf); - if(__prog_dmabuf(card->states[1])) - { - CS_DBGOUT(CS_PM | CS_ERROR, 1, - printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() dac error\n")); - return -1; - } - cs_set_dac_rate(card->states[1], dmabuf->rate); - } - if(card->states[0]) - { - dmabuf = &card->states[0]->dmabuf; - dmabuf->ready = 0; - resync_dma_ptrs(card->states[0]); - cs_set_divisor(dmabuf); - if(__prog_dmabuf(card->states[0])) - { - CS_DBGOUT(CS_PM | CS_ERROR, 1, - printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() adc error\n")); - return -1; - } - cs_set_adc_rate(card->states[0], dmabuf->rate); - } - card->pm.flags |= CS46XX_PM_RESUMED; - if(card->states[0]) - start_adc(card->states[0]); - if(card->states[1]) - start_dac(card->states[1]); - - card->pm.flags |= CS46XX_PM_IDLE; - card->pm.flags &= ~(CS46XX_PM_SUSPENDING | CS46XX_PM_SUSPENDED - | CS46XX_PM_RESUMING | CS46XX_PM_RESUMED); - if(card->states[0]) - wake_up(&card->states[0]->dmabuf.wait); - if(card->states[1]) - wake_up(&card->states[1]->dmabuf.wait); - - CS_DBGOUT(CS_PM | CS_FUNCTION, 4, - printk( "cs46xx: cs46xx_restart_part()-\n")); - return 0; -} - - -static void cs461x_reset(struct cs_card *card); -static void cs461x_proc_stop(struct cs_card *card); -static int cs46xx_suspend(struct cs_card *card, u32 state) -{ - unsigned int tmp; - CS_DBGOUT(CS_PM | CS_FUNCTION, 4, - printk("cs46xx: cs46xx_suspend()+ flags=0x%x s=0x%x\n", - (unsigned)card->pm.flags,(unsigned)card)); -/* -* check the current state, only suspend if IDLE -*/ - if(!(card->pm.flags & CS46XX_PM_IDLE)) - { - CS_DBGOUT(CS_PM | CS_ERROR, 2, - printk("cs46xx: cs46xx_suspend() unable to suspend, not IDLE\n")); - return 1; - } - card->pm.flags &= ~CS46XX_PM_IDLE; - card->pm.flags |= CS46XX_PM_SUSPENDING; - - card->active_ctrl(card,1); - - tmp = cs461x_peek(card, BA1_PFIE); - tmp &= ~0x0000f03f; - tmp |= 0x00000010; - cs461x_poke(card, BA1_PFIE, tmp); /* playback interrupt disable */ - - tmp = cs461x_peek(card, BA1_CIE); - tmp &= ~0x0000003f; - tmp |= 0x00000011; - cs461x_poke(card, BA1_CIE, tmp); /* capture interrupt disable */ - - /* - * Stop playback DMA. - */ - tmp = cs461x_peek(card, BA1_PCTL); - cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff); - - /* - * Stop capture DMA. - */ - tmp = cs461x_peek(card, BA1_CCTL); - cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); - - if(card->states[1]) - { - card->pm.dmabuf_swptr_play = card->states[1]->dmabuf.swptr; - card->pm.dmabuf_count_play = card->states[1]->dmabuf.count; - } - if(card->states[0]) - { - card->pm.dmabuf_swptr_capture = card->states[0]->dmabuf.swptr; - card->pm.dmabuf_count_capture = card->states[0]->dmabuf.count; - } - - cs46xx_ac97_suspend(card); - - /* - * Reset the processor. - */ - cs461x_reset(card); - - cs461x_proc_stop(card); - - /* - * Power down the DAC and ADC. For now leave the other areas on. - */ - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, 0x0300); - - /* - * Power down the PLL. - */ - cs461x_pokeBA0(card, BA0_CLKCR1, 0); - - /* - * Turn off the Processor by turning off the software clock enable flag in - * the clock control register. - */ - tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE; - cs461x_pokeBA0(card, BA0_CLKCR1, tmp); - - card->active_ctrl(card,-1); - - card->pm.flags &= ~CS46XX_PM_SUSPENDING; - card->pm.flags |= CS46XX_PM_SUSPENDED; - - printpm(card); - - CS_DBGOUT(CS_PM | CS_FUNCTION, 4, - printk("cs46xx: cs46xx_suspend()- flags=0x%x\n", - (unsigned)card->pm.flags)); - return 0; -} - -static int cs46xx_resume(struct cs_card *card) -{ - int i; - - CS_DBGOUT(CS_PM | CS_FUNCTION, 4, - printk( "cs46xx: cs46xx_resume()+ flags=0x%x\n", - (unsigned)card->pm.flags)); - if(!(card->pm.flags & CS46XX_PM_SUSPENDED)) - { - CS_DBGOUT(CS_PM | CS_ERROR, 2, - printk("cs46xx: cs46xx_resume() unable to resume, not SUSPENDED\n")); - return 1; - } - card->pm.flags |= CS46XX_PM_RESUMING; - card->pm.flags &= ~CS46XX_PM_SUSPENDED; - printpm(card); - card->active_ctrl(card, 1); - - for(i=0;i<5;i++) - { - if (cs_hardware_init(card) != 0) - { - CS_DBGOUT(CS_PM | CS_ERROR, 4, printk( - "cs46xx: cs46xx_resume()- ERROR in cs_hardware_init()\n")); - mdelay(10 * cs_laptop_wait); - cs461x_reset(card); - continue; - } - break; - } - if(i>=4) - { - CS_DBGOUT(CS_PM | CS_ERROR, 1, printk( - "cs46xx: cs46xx_resume()- cs_hardware_init() failed, retried %d times.\n",i)); - return 0; - } - - if(cs46xx_restart_part(card)) - { - CS_DBGOUT(CS_PM | CS_ERROR, 4, printk( - "cs46xx: cs46xx_resume(): cs46xx_restart_part() returned error\n")); - } - - card->active_ctrl(card, -1); - - CS_DBGOUT(CS_PM | CS_FUNCTION, 4, printk("cs46xx: cs46xx_resume()- flags=0x%x\n", - (unsigned)card->pm.flags)); - return 0; -} - -static /*const*/ struct file_operations cs461x_fops = { - CS_OWNER CS_THIS_MODULE - llseek: no_llseek, - read: cs_read, - write: cs_write, - poll: cs_poll, - ioctl: cs_ioctl, - mmap: cs_mmap, - open: cs_open, - release: cs_release, -}; - -/* Write AC97 codec registers */ - - -static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg) -{ - struct cs_card *card = dev->private_data; - int count,loopcnt; - unsigned int tmp; - - /* - * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address - * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 - * 3. Write ACCTL = Control Register = 460h for initiating the write - * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h - * 5. if DCV not cleared, break and return error - * 6. Read ACSTS = Status Register = 464h, check VSTS bit - */ - - - cs461x_peekBA0(card, BA0_ACSDA); - - /* - * Setup the AC97 control registers on the CS461x to send the - * appropriate command to the AC97 to perform the read. - * ACCAD = Command Address Register = 46Ch - * ACCDA = Command Data Register = 470h - * ACCTL = Control Register = 460h - * set DCV - will clear when process completed - * set CRW - Read command - * set VFRM - valid frame enabled - * set ESYN - ASYNC generation enabled - * set RSTN - ARST# inactive, AC97 codec not reset - */ - - cs461x_pokeBA0(card, BA0_ACCAD, reg); - cs461x_pokeBA0(card, BA0_ACCDA, 0); - cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW | - ACCTL_VFRM | ACCTL_ESYN | - ACCTL_RSTN); - - - /* - * Wait for the read to occur. - */ - if(!(card->pm.flags & CS46XX_PM_IDLE)) - loopcnt = 2000; - else - loopcnt = 500 * cs_laptop_wait; - loopcnt *= cs_laptop_wait; - for (count = 0; count < loopcnt; count++) { - /* - * First, we want to wait for a short time. - */ - udelay(10 * cs_laptop_wait); - /* - * Now, check to see if the read has completed. - * ACCTL = 460h, DCV should be reset by now and 460h = 17h - */ - if (!(cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV)) - break; - } - - /* - * Make sure the read completed. - */ - if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING - "cs46xx: AC'97 read problem (ACCTL_DCV), reg = 0x%x returning 0xffff\n", reg)); - return 0xffff; - } - - /* - * Wait for the valid status bit to go active. - */ - - if(!(card->pm.flags & CS46XX_PM_IDLE)) - loopcnt = 2000; - else - loopcnt = 1000; - loopcnt *= cs_laptop_wait; - for (count = 0; count < loopcnt; count++) { - /* - * Read the AC97 status register. - * ACSTS = Status Register = 464h - * VSTS - Valid Status - */ - if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_VSTS) - break; - udelay(10 * cs_laptop_wait); - } - - /* - * Make sure we got valid status. - */ - if (!( (tmp=cs461x_peekBA0(card, BA0_ACSTS)) & ACSTS_VSTS)) { - CS_DBGOUT(CS_ERROR, 2, printk(KERN_WARNING - "cs46xx: AC'97 read problem (ACSTS_VSTS), reg = 0x%x val=0x%x 0xffff \n", - reg, tmp)); - return 0xffff; - } - - /* - * Read the data returned from the AC97 register. - * ACSDA = Status Data Register = 474h - */ - CS_DBGOUT(CS_FUNCTION, 9, printk(KERN_INFO - "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)); -} - -static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val) -{ - struct cs_card *card = dev->private_data; - int count; - int val2 = 0; - - if(reg == 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 - * 3. Write ACCTL = Control Register = 460h for initiating the write - * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h - * 5. if DCV not cleared, break and return error - */ - - /* - * Setup the AC97 control registers on the CS461x to send the - * appropriate command to the AC97 to perform the read. - * ACCAD = Command Address Register = 46Ch - * ACCDA = Command Data Register = 470h - * ACCTL = Control Register = 460h - * set DCV - will clear when process completed - * reset CRW - Write command - * set VFRM - valid frame enabled - * set ESYN - ASYNC generation enabled - * set RSTN - ARST# inactive, AC97 codec not reset - */ - cs461x_pokeBA0(card, BA0_ACCAD, reg); - cs461x_pokeBA0(card, BA0_ACCDA, val); - cs461x_peekBA0(card, BA0_ACCTL); - cs461x_pokeBA0(card, BA0_ACCTL, 0 | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); - cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM | - ACCTL_ESYN | ACCTL_RSTN); - for (count = 0; count < 1000; count++) { - /* - * First, we want to wait for a short time. - */ - udelay(10 * cs_laptop_wait); - /* - * Now, check to see if the write has completed. - * ACCTL = 460h, DCV should be reset by now and 460h = 07h - */ - if (!(cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV)) - break; - } - /* - * Make sure the write completed. - */ - if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING - "cs46xx: AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val)); - } - - /* - * Adjust power if the mixer is selected/deselected according - * to the CD. - * - * IF the CD is a valid input source (mixer or direct) AND - * the CD is not muted THEN power is needed - * - * We do two things. When record select changes the input to - * add/remove the CD we adjust the power count if the CD is - * unmuted. - * - * When the CD mute changes we adjust the power level if the - * CD was a valid input. - * - * We also check for CD volume != 0, as the CD mute isn't - * normally tweaked from userspace. - */ - - /* CD mute change ? */ - - if(reg==AC97_CD_VOL) - { - /* Mute bit change ? */ - if((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) - { - /* This is a hack but its cleaner than the alternatives. - Right now card->ac97_codec[0] might be NULL as we are - still doing codec setup. This does an early assignment - to avoid the problem if it occurs */ - - if(card->ac97_codec[0]==NULL) - card->ac97_codec[0]=dev; - - /* Mute on */ - if(val&0x8000 || val == 0x1f1f) - card->amplifier_ctrl(card, -1); - else /* Mute off power on */ - { - if(card->amp_init) - card->amp_init(card); - card->amplifier_ctrl(card, 1); - } - } - } -} - - -/* OSS /dev/mixer file operation methods */ - -static int cs_open_mixdev(struct inode *inode, struct file *file) -{ - int i=0; - unsigned int minor = minor(inode->i_rdev); - struct cs_card *card=NULL; - struct list_head *entry; - unsigned int tmp; - - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, - printk(KERN_INFO "cs46xx: cs_open_mixdev()+\n")); - - list_for_each(entry, &cs46xx_devs) - { - card = list_entry(entry, struct cs_card, list); - for (i = 0; i < NR_AC97; i++) - if (card->ac97_codec[i] != NULL && - card->ac97_codec[i]->dev_mixer == minor) - goto match; - } - if (!card) - { - CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, - printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n")); - return -ENODEV; - } - match: - if(!card->ac97_codec[i]) - return -ENODEV; - file->private_data = card->ac97_codec[i]; - - card->active_ctrl(card,1); - if(!CS_IN_USE(&card->mixer_use_cnt)) - { - if( (tmp = cs46xx_powerup(card, CS_POWER_MIXVON )) ) - { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs_open_mixdev() powerup failure (0x%x)\n",tmp) ); - return -EIO; - } - } - card->amplifier_ctrl(card, 1); - CS_INC_USE_COUNT(&card->mixer_use_cnt); - MOD_INC_USE_COUNT; /* for 2.2 */ - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, - printk(KERN_INFO "cs46xx: cs_open_mixdev()- 0\n")); - return 0; -} - -static int cs_release_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct cs_card *card=NULL; - struct list_head *entry; - int i; - unsigned int tmp; - - CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4, - printk(KERN_INFO "cs46xx: cs_release_mixdev()+\n")); - list_for_each(entry, &cs46xx_devs) - { - card = list_entry(entry, struct cs_card, list); - for (i = 0; i < NR_AC97; i++) - if (card->ac97_codec[i] != NULL && - card->ac97_codec[i]->dev_mixer == minor) - goto match; - } - if (!card) - { - CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, - printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n")); - return -ENODEV; - } -match: - MOD_DEC_USE_COUNT; /* for 2.2 */ - if(!CS_DEC_AND_TEST(&card->mixer_use_cnt)) - { - CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4, - printk(KERN_INFO "cs46xx: cs_release_mixdev()- no powerdown, usecnt>0\n")); - card->active_ctrl(card, -1); - card->amplifier_ctrl(card, -1); - return 0; - } -/* -* ok, no outstanding mixer opens, so powerdown. -*/ - if( (tmp = cs461x_powerdown(card, CS_POWER_MIXVON, CS_FALSE )) ) - { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs_release_mixdev() powerdown MIXVON failure (0x%x)\n",tmp) ); - card->active_ctrl(card, -1); - card->amplifier_ctrl(card, -1); - return -EIO; - } - card->active_ctrl(card, -1); - card->amplifier_ctrl(card, -1); - CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4, - printk(KERN_INFO "cs46xx: cs_release_mixdev()- 0\n")); - return 0; -} - -void __exit cs46xx_cleanup_module(void); -static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct ac97_codec *codec = (struct ac97_codec *)file->private_data; - struct cs_card *card=NULL; - struct list_head *entry; - -#if CSDEBUG_INTERFACE - int val; - - if( (cmd == SOUND_MIXER_CS_GETDBGMASK) || - (cmd == SOUND_MIXER_CS_SETDBGMASK) || - (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || - (cmd == SOUND_MIXER_CS_SETDBGLEVEL) || - (cmd == SOUND_MIXER_CS_APM)) - { - 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; - - case SOUND_MIXER_CS_APM: - if (get_user(val, (unsigned long *) arg)) - return -EFAULT; - if(val == CS_IOCTL_CMD_SUSPEND) - { - list_for_each(entry, &cs46xx_devs) - { - card = list_entry(entry, struct cs_card, list); - cs46xx_suspend(card, 0); - } - - } - else if(val == CS_IOCTL_CMD_RESUME) - { - list_for_each(entry, &cs46xx_devs) - { - card = list_entry(entry, struct cs_card, list); - cs46xx_resume(card); - } - } - else - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs46xx: mixer_ioctl(): invalid APM cmd (%d)\n", - val)); - } - return 0; - - default: - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs46xx: mixer_ioctl(): ERROR unknown debug cmd\n") ); - return 0; - } - } -#endif - return codec->mixer_ioctl(codec, cmd, arg); -} - -static /*const*/ struct file_operations cs_mixer_fops = { - CS_OWNER CS_THIS_MODULE - llseek: no_llseek, - ioctl: cs_ioctl_mixdev, - open: cs_open_mixdev, - release: cs_release_mixdev, -}; - -/* AC97 codec initialisation. */ -static int __init cs_ac97_init(struct cs_card *card) -{ - int num_ac97 = 0; - int ready_2nd = 0; - struct ac97_codec *codec; - u16 eid; - - CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO - "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) - return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); - - /* 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 = cs_ac97_get; - codec->codec_write = cs_ac97_set; - - if (ac97_probe_codec(codec) == 0) - { - CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO - "cs46xx: cs_ac97_init()- codec number %d not found\n", - num_ac97) ); - card->ac97_codec[num_ac97] = 0; - break; - } - CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO - "cs46xx: cs_ac97_init() found codec %d\n",num_ac97) ); - - eid = cs_ac97_get(codec, AC97_EXTENDED_ID); - - if(eid==0xFFFFFF) - { - printk(KERN_WARNING "cs46xx: codec %d not present\n",num_ac97); - kfree(codec); - break; - } - - card->ac97_features = eid; - - if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) { - printk(KERN_ERR "cs46xx: couldn't register mixer!\n"); - kfree(codec); - break; - } - card->ac97_codec[num_ac97] = codec; - - CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO - "cs46xx: cs_ac97_init() ac97_codec[%d] set to 0x%x\n", - (unsigned int)num_ac97, - (unsigned int)codec)); - /* if there is no secondary codec at all, don't probe any more */ - if (!ready_2nd) - { - num_ac97 += 1; - break; - } - } - CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO - "cs46xx: cs_ac97_init()- %d\n", (unsigned int)num_ac97)); - return num_ac97; -} - -/* - * load the static image into the DSP - */ -#include "cs461x_image.h" -static void cs461x_download_image(struct cs_card *card) -{ - unsigned i, j, temp1, temp2, offset, count; - unsigned char *pBA1 = ioremap(card->ba1_addr, 0x40000); - for( i=0; i < CLEAR__COUNT; i++) - { - offset = ClrStat[i].BA1__DestByteOffset; - count = ClrStat[i].BA1__SourceSize; - for( temp1 = offset; temp1<(offset+count); temp1+=4 ); - writel(0, pBA1+temp1); - } - - for(i=0; iac97_codec[0], AC97_POWER_CONTROL); - CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO - "cs46xx: cs461x_powerdown() powerdown reg=0x%x\n",tmp)); -/* -* if powering down only the VREF, and not powering down the DAC/ADC, -* then do not power down the VREF, UNLESS both the DAC and ADC are not -* currently powered down. If powering down DAC and ADC, then -* it is possible to power down the VREF (ON). -*/ - if ( ((type & CS_POWER_MIXVON) && - (!(type & CS_POWER_ADC) || (!(type & CS_POWER_DAC))) ) - && - ((tmp & CS_AC97_POWER_CONTROL_ADC_ON) || - (tmp & CS_AC97_POWER_CONTROL_DAC_ON) ) ) - { - CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO - "cs46xx: cs461x_powerdown()- 0 unable to powerdown. tmp=0x%x\n",tmp)); - return 0; - } -/* -* for now, always keep power to the mixer block. -* not sure why it's a problem but it seems to be if we power off. -*/ - type &= ~CS_POWER_MIXVON; - type &= ~CS_POWER_MIXVOFF; - - /* - * Power down indicated areas. - */ - if(type & CS_POWER_MIXVOFF) - { - - CS_DBGOUT(CS_FUNCTION, 4, - printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVOFF\n")); - /* - * Power down the MIXER (VREF ON) on the AC97 card. - */ - tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON) - { - if(!muted) - { - cs_mute(card, CS_TRUE); - muted=1; - } - tmp |= CS_AC97_POWER_CONTROL_MIXVOFF; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); - /* - * Now, we wait until we sample a ready state. - */ - for (count = 0; count < 32; count++) { - /* - * First, lets wait a short while to let things settle out a - * bit, and to prevent retrying the read too quickly. - */ - udelay(500); - - /* - * Read the current state of the power control register. - */ - if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVOFF_ON)) - break; - } - - /* - * Check the status.. - */ - if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVOFF_ON) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING - "cs46xx: powerdown MIXVOFF failed\n")); - return 1; - } - } - } - if(type & CS_POWER_MIXVON) - { - - CS_DBGOUT(CS_FUNCTION, 4, - printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVON\n")); - /* - * Power down the MIXER (VREF ON) on the AC97 card. - */ - tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (tmp & CS_AC97_POWER_CONTROL_MIXVON_ON) - { - if(!muted) - { - cs_mute(card, CS_TRUE); - muted=1; - } - tmp |= CS_AC97_POWER_CONTROL_MIXVON; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); - /* - * Now, we wait until we sample a ready state. - */ - for (count = 0; count < 32; count++) { - /* - * First, lets wait a short while to let things settle out a - * bit, and to prevent retrying the read too quickly. - */ - udelay(500); - - /* - * Read the current state of the power control register. - */ - if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVON_ON)) - break; - } - - /* - * Check the status.. - */ - if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVON_ON) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING - "cs46xx: powerdown MIXVON failed\n")); - return 1; - } - } - } - if(type & CS_POWER_ADC) - { - /* - * Power down the ADC on the AC97 card. - */ - CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs461x_powerdown()+ ADC\n")); - tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (tmp & CS_AC97_POWER_CONTROL_ADC_ON) - { - if(!muted) - { - cs_mute(card, CS_TRUE); - muted=1; - } - tmp |= CS_AC97_POWER_CONTROL_ADC; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); - - /* - * Now, we wait until we sample a ready state. - */ - for (count = 0; count < 32; count++) { - /* - * First, lets wait a short while to let things settle out a - * bit, and to prevent retrying the read too quickly. - */ - udelay(500); - - /* - * Read the current state of the power control register. - */ - if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_ADC_ON)) - break; - } - - /* - * Check the status.. - */ - if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_ADC_ON) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING - "cs46xx: powerdown ADC failed\n")); - return 1; - } - } - } - if(type & CS_POWER_DAC) - { - /* - * Power down the DAC on the AC97 card. - */ - - CS_DBGOUT(CS_FUNCTION, 4, - printk(KERN_INFO "cs46xx: cs461x_powerdown()+ DAC\n")); - tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (tmp & CS_AC97_POWER_CONTROL_DAC_ON) - { - if(!muted) - { - cs_mute(card, CS_TRUE); - muted=1; - } - tmp |= CS_AC97_POWER_CONTROL_DAC; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); - /* - * Now, we wait until we sample a ready state. - */ - for (count = 0; count < 32; count++) { - /* - * First, lets wait a short while to let things settle out a - * bit, and to prevent retrying the read too quickly. - */ - udelay(500); - - /* - * Read the current state of the power control register. - */ - if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_DAC_ON)) - break; - } - - /* - * Check the status.. - */ - if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_DAC_ON) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING - "cs46xx: powerdown DAC failed\n")); - return 1; - } - } - } - tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if(muted) - cs_mute(card, CS_FALSE); - CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO - "cs46xx: cs461x_powerdown()- 0 tmp=0x%x\n",tmp)); - return 0; -} - -static int cs46xx_powerup(struct cs_card *card, unsigned int type) -{ - int count; - unsigned int tmp=0,muted=0; - - CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO - "cs46xx: cs46xx_powerup()+ type=0x%x\n",type)); - /* - * check for VREF and powerup if need to. - */ - if(type & CS_POWER_MIXVON) - type |= CS_POWER_MIXVOFF; - if(type & (CS_POWER_DAC | CS_POWER_ADC)) - type |= CS_POWER_MIXVON | CS_POWER_MIXVOFF; - - /* - * Power up indicated areas. - */ - if(type & CS_POWER_MIXVOFF) - { - - CS_DBGOUT(CS_FUNCTION, 4, - printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVOFF\n")); - /* - * Power up the MIXER (VREF ON) on the AC97 card. - */ - tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (!(tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON)) - { - if(!muted) - { - cs_mute(card, CS_TRUE); - muted=1; - } - tmp &= ~CS_AC97_POWER_CONTROL_MIXVOFF; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); - /* - * Now, we wait until we sample a ready state. - */ - for (count = 0; count < 32; count++) { - /* - * First, lets wait a short while to let things settle out a - * bit, and to prevent retrying the read too quickly. - */ - udelay(500); - - /* - * Read the current state of the power control register. - */ - if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVOFF_ON) - break; - } - - /* - * Check the status.. - */ - if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVOFF_ON)) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING - "cs46xx: powerup MIXVOFF failed\n")); - return 1; - } - } - } - if(type & CS_POWER_MIXVON) - { - - CS_DBGOUT(CS_FUNCTION, 4, - printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVON\n")); - /* - * Power up the MIXER (VREF ON) on the AC97 card. - */ - tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (!(tmp & CS_AC97_POWER_CONTROL_MIXVON_ON)) - { - if(!muted) - { - cs_mute(card, CS_TRUE); - muted=1; - } - tmp &= ~CS_AC97_POWER_CONTROL_MIXVON; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); - /* - * Now, we wait until we sample a ready state. - */ - for (count = 0; count < 32; count++) { - /* - * First, lets wait a short while to let things settle out a - * bit, and to prevent retrying the read too quickly. - */ - udelay(500); - - /* - * Read the current state of the power control register. - */ - if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVON_ON) - break; - } - - /* - * Check the status.. - */ - if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVON_ON)) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING - "cs46xx: powerup MIXVON failed\n")); - return 1; - } - } - } - if(type & CS_POWER_ADC) - { - /* - * Power up the ADC on the AC97 card. - */ - CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs46xx_powerup()+ ADC\n")); - tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (!(tmp & CS_AC97_POWER_CONTROL_ADC_ON)) - { - if(!muted) - { - cs_mute(card, CS_TRUE); - muted=1; - } - tmp &= ~CS_AC97_POWER_CONTROL_ADC; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); - - /* - * Now, we wait until we sample a ready state. - */ - for (count = 0; count < 32; count++) { - /* - * First, lets wait a short while to let things settle out a - * bit, and to prevent retrying the read too quickly. - */ - udelay(500); - - /* - * Read the current state of the power control register. - */ - if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_ADC_ON) - break; - } - - /* - * Check the status.. - */ - if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_ADC_ON)) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING - "cs46xx: powerup ADC failed\n")); - return 1; - } - } - } - if(type & CS_POWER_DAC) - { - /* - * Power up the DAC on the AC97 card. - */ - - CS_DBGOUT(CS_FUNCTION, 4, - printk(KERN_INFO "cs46xx: cs46xx_powerup()+ DAC\n")); - tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (!(tmp & CS_AC97_POWER_CONTROL_DAC_ON)) - { - if(!muted) - { - cs_mute(card, CS_TRUE); - muted=1; - } - tmp &= ~CS_AC97_POWER_CONTROL_DAC; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); - /* - * Now, we wait until we sample a ready state. - */ - for (count = 0; count < 32; count++) { - /* - * First, lets wait a short while to let things settle out a - * bit, and to prevent retrying the read too quickly. - */ - udelay(500); - - /* - * Read the current state of the power control register. - */ - if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_DAC_ON) - break; - } - - /* - * Check the status.. - */ - if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_DAC_ON)) - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING - "cs46xx: powerup DAC failed\n")); - return 1; - } - } - } - tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if(muted) - cs_mute(card, CS_FALSE); - CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO - "cs46xx: cs46xx_powerup()- 0 tmp=0x%x\n",tmp)); - return 0; -} - - -static void cs461x_proc_start(struct cs_card *card) -{ - int cnt; - - /* - * Set the frame timer to reflect the number of cycles per frame. - */ - cs461x_poke(card, BA1_FRMT, 0xadf); - /* - * Turn on the run, run at frame, and DMA enable bits in the local copy of - * the SP control register. - */ - cs461x_poke(card, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN); - /* - * Wait until the run at frame bit resets itself in the SP control - * register. - */ - for (cnt = 0; cnt < 25; cnt++) { - udelay(50); - if (!(cs461x_peek(card, BA1_SPCR) & SPCR_RUNFR)) - break; - } - - if (cs461x_peek(card, BA1_SPCR) & SPCR_RUNFR) - printk(KERN_WARNING "cs46xx: SPCR_RUNFR never reset\n"); -} - -static void cs461x_proc_stop(struct cs_card *card) -{ - /* - * Turn off the run, run at frame, and DMA enable bits in the local copy of - * the SP control register. - */ - cs461x_poke(card, BA1_SPCR, 0); -} - -static int cs_hardware_init(struct cs_card *card) -{ - unsigned long end_time; - unsigned int tmp,count; - - CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO - "cs46xx: cs_hardware_init()+\n") ); - /* - * First, blast the clock control register to zero so that the PLL starts - * out in a known state, and blast the master serial port control register - * to zero so that the serial ports also start out in a known state. - */ - cs461x_pokeBA0(card, BA0_CLKCR1, 0); - cs461x_pokeBA0(card, BA0_SERMC1, 0); - - /* - * If we are in AC97 mode, then we must set the part to a host controlled - * AC-link. Otherwise, we won't be able to bring up the link. - */ - cs461x_pokeBA0(card, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_1_03); /* 1.03 card */ - /* cs461x_pokeBA0(card, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_2_0); */ /* 2.00 card */ - - /* - * Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 - * spec) and then drive it high. This is done for non AC97 modes since - * there might be logic external to the CS461x that uses the ARST# line - * for a reset. - */ - cs461x_pokeBA0(card, BA0_ACCTL, 1); - udelay(50); - cs461x_pokeBA0(card, BA0_ACCTL, 0); - udelay(50); - cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_RSTN); - - /* - * The first thing we do here is to enable sync generation. As soon - * as we start receiving bit clock, we'll start producing the SYNC - * signal. - */ - cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN); - - /* - * Now wait for a short while to allow the AC97 part to start - * generating bit clock (so we don't try to start the PLL without an - * input clock). - */ - mdelay(5 * cs_laptop_wait); /* 1 should be enough ?? (and pigs might fly) */ - - /* - * Set the serial port timing configuration, so that - * the clock control circuit gets its clock from the correct place. - */ - cs461x_pokeBA0(card, BA0_SERMC1, SERMC1_PTC_AC97); - - /* - * The part seems to not be ready for a while after a resume. - * so, if we are resuming, then wait for 700 mils. Note that 600 mils - * is not enough for some platforms! tested on an IBM Thinkpads and - * reference cards. - */ - if(!(card->pm.flags & CS46XX_PM_IDLE)) - mdelay(initdelay); - /* - * Write the selected clock control setup to the hardware. Do not turn on - * SWCE yet (if requested), so that the devices clocked by the output of - * PLL are not clocked until the PLL is stable. - */ - cs461x_pokeBA0(card, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ); - cs461x_pokeBA0(card, BA0_PLLM, 0x3a); - cs461x_pokeBA0(card, BA0_CLKCR2, CLKCR2_PDIVS_8); - - /* - * Power up the PLL. - */ - cs461x_pokeBA0(card, BA0_CLKCR1, CLKCR1_PLLP); - - /* - * Wait until the PLL has stabilized. - */ - mdelay(5 * cs_laptop_wait); /* Again 1 should be enough ?? */ - - /* - * Turn on clocking of the core so that we can setup the serial ports. - */ - tmp = cs461x_peekBA0(card, BA0_CLKCR1) | CLKCR1_SWCE; - cs461x_pokeBA0(card, BA0_CLKCR1, tmp); - - /* - * Fill the serial port FIFOs with silence. - */ - cs461x_clear_serial_FIFOs(card,CS_TYPE_DAC | CS_TYPE_ADC); - - /* - * Set the serial port FIFO pointer to the first sample in the FIFO. - */ - /* cs461x_pokeBA0(card, BA0_SERBSP, 0); */ - - /* - * Write the serial port configuration to the part. The master - * enable bit is not set until all other values have been written. - */ - cs461x_pokeBA0(card, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN); - cs461x_pokeBA0(card, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN); - cs461x_pokeBA0(card, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE); - - - mdelay(5 * cs_laptop_wait); /* Shouldnt be needed ?? */ - -/* -* If we are resuming under 2.2.x then we can not schedule a timeout. -* so, just spin the CPU. -*/ - if(card->pm.flags & CS46XX_PM_IDLE) - { - /* - * Wait for the card ready signal from the AC97 card. - */ - end_time = jiffies + 3 * (HZ >> 2); - do { - /* - * Read the AC97 status register to see if we've seen a CODEC READY - * signal from the AC97 card. - */ - if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY) - break; - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(1); - } while (time_before(jiffies, end_time)); - } - else - { - for (count = 0; count < 100; count++) { - // First, we want to wait for a short time. - udelay(25 * cs_laptop_wait); - - if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY) - break; - } - } - - /* - * Make sure CODEC is READY. - */ - if (!(cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)) { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING - "cs46xx: create - never read card ready from AC'97\n")); - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING - "cs46xx: probably not a bug, try using the CS4232 driver,\n")); - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING - "cs46xx: or turn off any automatic Power Management support in the BIOS.\n")); - return -EIO; - } - - /* - * Assert the vaid frame signal so that we can start sending commands - * to the AC97 card. - */ - cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); - - if(card->pm.flags & CS46XX_PM_IDLE) - { - /* - * Wait until we've sampled input slots 3 and 4 as valid, meaning that - * the card is pumping ADC data across the AC-link. - */ - end_time = jiffies + 3 * (HZ >> 2); - do { - /* - * Read the input slot valid register and see if input slots 3 and - * 4 are valid yet. - */ - if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) - break; - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(1); - } while (time_before(jiffies, end_time)); - } - else - { - for (count = 0; count < 100; count++) { - // First, we want to wait for a short time. - udelay(25 * cs_laptop_wait); - - if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) - break; - } - } - /* - * Make sure input slots 3 and 4 are valid. If not, then return - * an error. - */ - if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) != (ACISV_ISV3 | ACISV_ISV4)) { - printk(KERN_WARNING "cs46xx: create - never read ISV3 & ISV4 from AC'97\n"); - return -EIO; - } - - /* - * Now, assert valid frame and the slot 3 and 4 valid bits. This will - * commense the transfer of digital audio data to the AC97 card. - */ - cs461x_pokeBA0(card, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4); - - /* - * Turn off the Processor by turning off the software clock enable flag in - * the clock control register. - */ - /* tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE; */ - /* cs461x_pokeBA0(card, BA0_CLKCR1, tmp); */ - - /* - * Reset the processor. - */ - cs461x_reset(card); - - /* - * Download the image to the processor. - */ - - cs461x_download_image(card); - - /* - * Stop playback DMA. - */ - tmp = cs461x_peek(card, BA1_PCTL); - card->pctl = tmp & 0xffff0000; - cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff); - - /* - * Stop capture DMA. - */ - tmp = cs461x_peek(card, BA1_CCTL); - card->cctl = tmp & 0x0000ffff; - cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); - - /* initialize AC97 codec and register /dev/mixer */ - if(card->pm.flags & CS46XX_PM_IDLE) - { - if (cs_ac97_init(card) <= 0) - { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs_ac97_init() failure\n") ); - return -EIO; - } - } - else - { - cs46xx_ac97_resume(card); - } - - cs461x_proc_start(card); - - /* - * Enable interrupts on the part. - */ - cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM); - - tmp = cs461x_peek(card, BA1_PFIE); - tmp &= ~0x0000f03f; - cs461x_poke(card, BA1_PFIE, tmp); /* playback interrupt enable */ - - tmp = cs461x_peek(card, BA1_CIE); - tmp &= ~0x0000003f; - tmp |= 0x00000001; - cs461x_poke(card, BA1_CIE, tmp); /* capture interrupt enable */ - - /* - * If IDLE then Power down the part. We will power components up - * when we need them. - */ - if(card->pm.flags & CS46XX_PM_IDLE) - { - if(!cs_powerdown) - { - if( (tmp = cs46xx_powerup(card, CS_POWER_DAC | CS_POWER_ADC | - CS_POWER_MIXVON )) ) - { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs461x_powerup() failure (0x%x)\n",tmp) ); - return -EIO; - } - } - else - { - if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | - CS_POWER_MIXVON, CS_FALSE )) ) - { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) ); - return -EIO; - } - } - } - CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO - "cs46xx: cs_hardware_init()- 0\n")); - return 0; -} - -/* 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) */ - -/* - * Card subid table - */ - -struct cs_card_type -{ - u16 vendor; - u16 id; - char *name; - void (*amp)(struct cs_card *, int); - void (*amp_init)(struct cs_card *); - void (*active)(struct cs_card *, int); -}; - -static struct cs_card_type cards[]={ - {0x1489, 0x7001, "Genius Soundmaker 128 value", amp_none, NULL, NULL}, - {0x5053, 0x3357, "Voyetra", amp_voyetra, NULL, NULL}, - {0x1071, 0x6003, "Mitac MI6020/21", amp_voyetra, NULL, NULL}, - {0x14AF, 0x0050, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, - {0x1681, 0x0050, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, - {0x1681, 0x0051, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, - {0x1681, 0x0052, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, - {0x1681, 0x0053, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, - {0x1681, 0x0054, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, - /* Not sure if the 570 needs the clkrun hack */ - {PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", amp_none, NULL, clkrun_hack}, - {PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, NULL, clkrun_hack}, - {PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL}, - {0, 0, "Card without SSID set", NULL, NULL, NULL }, - {0, 0, NULL, NULL, NULL} -}; - -MODULE_AUTHOR("Alan Cox , Jaroslav Kysela, "); -MODULE_DESCRIPTION("Crystal SoundFusion Audio Support"); -MODULE_LICENSE("GPL"); - - -static const char cs46xx_banner[] = KERN_INFO "Crystal 4280/46xx + AC97 Audio, version " CS46XX_MAJOR_VERSION "." CS46XX_MINOR_VERSION "." CS46XX_ARCH ", " __TIME__ " " __DATE__ "\n"; -static const char fndmsg[] = KERN_INFO "cs46xx: Found %d audio device(s).\n"; - -static int __devinit cs46xx_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pciid) -{ - struct pm_dev *pmdev; - int i,j; - u16 ss_card, ss_vendor; - struct cs_card *card; - dma_addr_t dma_mask; - struct cs_card_type *cp = &cards[0]; - - CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, - printk(KERN_INFO "cs46xx: probe()+\n")); - - dma_mask = 0xffffffff; /* this enables playback and recording */ - if (pci_enable_device(pci_dev)) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs46xx: pci_enable_device() failed\n")); - return -1; - } - if (!RSRCISMEMORYREGION(pci_dev, 0) || - !RSRCISMEMORYREGION(pci_dev, 1)) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs46xx: probe()- Memory region not assigned\n")); - return -1; - } - if (pci_dev->irq == 0) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs46xx: probe() IRQ not assigned\n")); - return -1; - } - if (!pci_dma_supported(pci_dev, 0xffffffff)) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs46xx: probe() architecture does not support 32bit PCI busmaster DMA\n")); - return -1; - } - pci_read_config_word(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor); - pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &ss_card); - - if ((card = kmalloc(sizeof(struct cs_card), GFP_KERNEL)) == NULL) { - printk(KERN_ERR "cs46xx: out of memory\n"); - return -ENOMEM; - } - memset(card, 0, sizeof(*card)); - card->ba0_addr = RSRCADDRESS(pci_dev, 0); - card->ba1_addr = RSRCADDRESS(pci_dev, 1); - card->pci_dev = pci_dev; - card->irq = pci_dev->irq; - card->magic = CS_CARD_MAGIC; - spin_lock_init(&card->lock); - - pci_set_master(pci_dev); - - printk(cs46xx_banner); - printk(KERN_INFO "cs46xx: Card found at 0x%08lx and 0x%08lx, IRQ %d\n", - card->ba0_addr, card->ba1_addr, card->irq); - - card->alloc_pcm_channel = cs_alloc_pcm_channel; - card->alloc_rec_pcm_channel = cs_alloc_rec_pcm_channel; - card->free_pcm_channel = cs_free_pcm_channel; - card->amplifier_ctrl = amp_none; - card->active_ctrl = amp_none; - - while (cp->name) - { - if(cp->vendor == ss_vendor && cp->id == ss_card) - { - card->amplifier_ctrl = cp->amp; - if(cp->active) - card->active_ctrl = cp->active; - if(cp->amp_init) - card->amp_init = cp->amp_init; - break; - } - cp++; - } - if (cp->name==NULL) - { - printk(KERN_INFO "cs46xx: Unknown card (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n", - ss_vendor, ss_card, card->ba0_addr, card->ba1_addr, card->irq); - } - else - { - printk(KERN_INFO "cs46xx: %s (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n", - cp->name, ss_vendor, ss_card, card->ba0_addr, card->ba1_addr, card->irq); - } - - if (card->amplifier_ctrl==NULL) - { - card->amplifier_ctrl = amp_none; - card->active_ctrl = clkrun_hack; - } - - if (external_amp == 1) - { - printk(KERN_INFO "cs46xx: Crystal EAPD support forced on.\n"); - card->amplifier_ctrl = amp_voyetra; - } - - if (thinkpad == 1) - { - printk(KERN_INFO "cs46xx: Activating CLKRUN hack for Thinkpad.\n"); - card->active_ctrl = clkrun_hack; - } -/* -* The thinkpads don't work well without runtime updating on their kernel -* delay values (or any laptop with variable CPU speeds really). -* so, just to be safe set the init delay to 2100. Eliminates -* failures on T21 Thinkpads. remove this code when the udelay -* and mdelay kernel code is replaced by a pm timer, or the delays -* work well for battery and/or AC power both. -*/ - if(card->active_ctrl == clkrun_hack) - { - initdelay = 2100; - cs_laptop_wait = 5; - } - if((card->active_ctrl == clkrun_hack) && !(powerdown == 1)) - { -/* -* for some currently unknown reason, powering down the DAC and ADC component -* blocks on thinkpads causes some funky behavior... distoorrrtion and ac97 -* codec access problems. probably the serial clock becomes unsynced. -* added code to sync the chips back up, but only helped about 70% the time. -*/ - cs_powerdown = 0; - } - if(powerdown == 0) - cs_powerdown = 0; - card->active_ctrl(card, 1); - - /* claim our iospace and irq */ - - card->ba0 = ioremap_nocache(card->ba0_addr, CS461X_BA0_SIZE); - card->ba1.name.data0 = ioremap_nocache(card->ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE); - card->ba1.name.data1 = ioremap_nocache(card->ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE); - card->ba1.name.pmem = ioremap_nocache(card->ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE); - card->ba1.name.reg = ioremap_nocache(card->ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE); - - CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO - "cs46xx: card=0x%x card->ba0=0x%.08x\n",(unsigned)card,(unsigned)card->ba0) ); - CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO - "cs46xx: card->ba1=0x%.08x 0x%.08x 0x%.08x 0x%.08x\n", - (unsigned)card->ba1.name.data0, - (unsigned)card->ba1.name.data1, - (unsigned)card->ba1.name.pmem, - (unsigned)card->ba1.name.reg) ); - - if(card->ba0 == 0 || card->ba1.name.data0 == 0 || - card->ba1.name.data1 == 0 || card->ba1.name.pmem == 0 || - card->ba1.name.reg == 0) - goto fail2; - - if (request_irq(card->irq, &cs_interrupt, SA_SHIRQ, "cs46xx", card)) { - printk(KERN_ERR "cs46xx: unable to allocate irq %d\n", card->irq); - goto fail2; - } - /* register /dev/dsp */ - if ((card->dev_audio = register_sound_dsp(&cs461x_fops, -1)) < 0) { - printk(KERN_ERR "cs46xx: unable to register dsp\n"); - goto fail; - } - - /* register /dev/midi */ - if((card->dev_midi = register_sound_midi(&cs_midi_fops, -1)) < 0) - printk(KERN_ERR "cs46xx: unable to register midi\n"); - - card->pm.flags |= CS46XX_PM_IDLE; - for(i=0;i<5;i++) - { - if (cs_hardware_init(card) != 0) - { - CS_DBGOUT(CS_ERROR, 4, printk( - "cs46xx: ERROR in cs_hardware_init()... retrying\n")); - 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]); - } - mdelay(10 * cs_laptop_wait); - continue; - } - break; - } - if(i>=4) - { - CS_DBGOUT(CS_PM | CS_ERROR, 1, printk( - "cs46xx: cs46xx_probe()- cs_hardware_init() failed, retried %d times.\n",i)); - unregister_sound_dsp(card->dev_audio); - if(card->dev_midi) - unregister_sound_midi(card->dev_midi); - goto fail; - } - - init_waitqueue_head(&card->midi.open_wait); - init_MUTEX(&card->midi.open_sem); - init_waitqueue_head(&card->midi.iwait); - init_waitqueue_head(&card->midi.owait); - cs461x_pokeBA0(card, BA0_MIDCR, MIDCR_MRST); - cs461x_pokeBA0(card, BA0_MIDCR, 0); - - /* - * Check if we have to init the amplifier, but probably already done - * since the CD logic in the ac97 init code will turn on the ext amp. - */ - if(cp->amp_init) - cp->amp_init(card); - card->active_ctrl(card, -1); - - PCI_SET_DRIVER_DATA(pci_dev, card); - PCI_SET_DMA_MASK(pci_dev, dma_mask); - list_add(&card->list, &cs46xx_devs); - - pmdev = cs_pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev), cs46xx_pm_callback); - if (pmdev) - { - CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO - "cs46xx: probe() pm_register() succeeded (0x%x).\n", - (unsigned)pmdev)); - pmdev->data = card; - } - else - { - CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 2, printk(KERN_INFO - "cs46xx: probe() pm_register() failed (0x%x).\n", - (unsigned)pmdev)); - card->pm.flags |= CS46XX_PM_NOT_REGISTERED; - } - - CS_DBGOUT(CS_PM, 9, printk(KERN_INFO "cs46xx: pm.flags=0x%x card=0x%x\n", - (unsigned)card->pm.flags,(unsigned)card)); - - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs46xx: probe()- device allocated successfully\n")); - return 0; - -fail: - free_irq(card->irq, card); -fail2: - if(card->ba0) - iounmap(card->ba0); - if(card->ba1.name.data0) - iounmap(card->ba1.name.data0); - if(card->ba1.name.data1) - iounmap(card->ba1.name.data1); - if(card->ba1.name.pmem) - iounmap(card->ba1.name.pmem); - if(card->ba1.name.reg) - iounmap(card->ba1.name.reg); - kfree(card); - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO - "cs46xx: probe()- no device allocated\n")); - return -ENODEV; -} // probe_cs46xx - -// --------------------------------------------------------------------- - -static void __devinit cs46xx_remove(struct pci_dev *pci_dev) -{ - struct cs_card *card = PCI_GET_DRIVER_DATA(pci_dev); - int i; - unsigned int tmp; - - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs46xx: cs46xx_remove()+\n")); - - card->active_ctrl(card,1); - - tmp = cs461x_peek(card, BA1_PFIE); - tmp &= ~0x0000f03f; - tmp |= 0x00000010; - cs461x_poke(card, BA1_PFIE, tmp); /* playback interrupt disable */ - - tmp = cs461x_peek(card, BA1_CIE); - tmp &= ~0x0000003f; - tmp |= 0x00000011; - cs461x_poke(card, BA1_CIE, tmp); /* capture interrupt disable */ - - /* - * Stop playback DMA. - */ - tmp = cs461x_peek(card, BA1_PCTL); - cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff); - - /* - * Stop capture DMA. - */ - tmp = cs461x_peek(card, BA1_CCTL); - cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); - - /* - * Reset the processor. - */ - cs461x_reset(card); - - cs461x_proc_stop(card); - - /* - * Power down the DAC and ADC. We will power them up (if) when we need - * them. - */ - if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | - CS_POWER_MIXVON, CS_TRUE )) ) - { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) ); - } - - /* - * Power down the PLL. - */ - cs461x_pokeBA0(card, BA0_CLKCR1, 0); - - /* - * Turn off the Processor by turning off the software clock enable flag in - * the clock control register. - */ - tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE; - cs461x_pokeBA0(card, BA0_CLKCR1, tmp); - - card->active_ctrl(card,-1); - - /* free hardware resources */ - free_irq(card->irq, card); - iounmap(card->ba0); - iounmap(card->ba1.name.data0); - iounmap(card->ba1.name.data1); - iounmap(card->ba1.name.pmem); - iounmap(card->ba1.name.reg); - - /* 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); - kfree (card->ac97_codec[i]); - } - unregister_sound_dsp(card->dev_audio); - if(card->dev_midi) - unregister_sound_midi(card->dev_midi); - list_del(&card->list); - kfree(card); - PCI_SET_DRIVER_DATA(pci_dev,NULL); - - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs46xx: cs46xx_remove()-: remove successful\n")); -} - -enum { - CS46XX_4610 = 0, - CS46XX_4612, /* same as 4630 */ - CS46XX_4615, /* same as 4624 */ -}; - -static struct pci_device_id cs46xx_pci_tbl[] __devinitdata = { - - {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4610}, - {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4612}, - {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4615}, - {0,} -}; - -MODULE_DEVICE_TABLE(pci, cs46xx_pci_tbl); - -struct pci_driver cs46xx_pci_driver = { - name:"cs46xx", - id_table:cs46xx_pci_tbl, - probe:cs46xx_probe, - remove:cs46xx_remove, - suspend:CS46XX_SUSPEND_TBL, - resume:CS46XX_RESUME_TBL, -}; - -int __init cs46xx_init_module(void) -{ - int rtn = 0; - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs46xx: cs46xx_init_module()+ \n")); - if (!pci_present()) { /* No PCI bus in this machine! */ - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs46xx: cs46xx_init_module()- no pci bus found\n")); - return -ENODEV; - } - rtn = pci_module_init(&cs46xx_pci_driver); - - if(rtn == -ENODEV) - { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk( - "cs46xx: Unable to detect valid cs46xx device\n")); - } - - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, - printk(KERN_INFO "cs46xx: cs46xx_init_module()- (%d)\n",rtn)); - return rtn; -} - -void __exit cs46xx_cleanup_module(void) -{ - pci_unregister_driver(&cs46xx_pci_driver); - cs_pm_unregister_all(cs46xx_pm_callback); - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, - printk(KERN_INFO "cs46xx: cleanup_cs46xx() finished\n")); -} - -module_init(cs46xx_init_module); -module_exit(cs46xx_cleanup_module); - -int cs46xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - struct cs_card *card; - - CS_DBGOUT(CS_PM, 2, printk(KERN_INFO - "cs46xx: cs46xx_pm_callback dev=0x%x rqst=0x%x card=%d\n", - (unsigned)dev,(unsigned)rqst,(unsigned)data)); - card = (struct cs_card *) dev->data; - if (card) { - switch(rqst) { - case PM_SUSPEND: - CS_DBGOUT(CS_PM, 2, printk(KERN_INFO - "cs46xx: PM suspend request\n")); - if(cs46xx_suspend(card, 0)) - { - CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO - "cs46xx: PM suspend request refused\n")); - return 1; - } - break; - case PM_RESUME: - CS_DBGOUT(CS_PM, 2, printk(KERN_INFO - "cs46xx: PM resume request\n")); - if(cs46xx_resume(card)) - { - CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO - "cs46xx: PM resume request refused\n")); - return 1; - } - break; - } - } - - return 0; -} - -#if CS46XX_ACPI_SUPPORT -static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state) -{ - struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev); - CS_DBGOUT(CS_PM | CS_FUNCTION, 2, - printk(KERN_INFO "cs46xx: cs46xx_suspend_tbl request\n")); - cs46xx_suspend(s, 0); - return 0; -} - -static int cs46xx_resume_tbl(struct pci_dev *pcidev) -{ - struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev); - CS_DBGOUT(CS_PM | CS_FUNCTION, 2, - printk(KERN_INFO "cs46xx: cs46xx_resume_tbl request\n")); - cs46xx_resume(s); - return 0; -} -#endif diff -Nru a/drivers/sound/cs46xx_wrapper-24.h b/drivers/sound/cs46xx_wrapper-24.h --- a/drivers/sound/cs46xx_wrapper-24.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,56 +0,0 @@ -/******************************************************************************* -* -* "cs46xx_wrapper.c" -- Cirrus Logic-Crystal CS46XX linux audio driver. -* -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- tom woller (twoller@crystal.cirrus.com) or -* (pcaudio@crystal.cirrus.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. -* -* 01/11/2001 trw - new file from cs4281 wrapper code. -* -*******************************************************************************/ -#ifndef __CS46XX_WRAPPER24_H -#define __CS46XX_WRAPPER24_H - -#include - -#define CS_OWNER owner: -#define CS_THIS_MODULE THIS_MODULE, -void cs46xx_null(struct pci_dev *pcidev) { return; } -#define cs4x_mem_map_reserve(page) mem_map_reserve(page) -#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page) - -#define free_dmabuf(card, dmabuf) \ - pci_free_consistent((card)->pci_dev, \ - PAGE_SIZE << (dmabuf)->buforder, \ - (dmabuf)->rawbuf, (dmabuf)->dmaaddr); -#define free_dmabuf2(card, dmabuf) \ - pci_free_consistent((card)->pci_dev, \ - PAGE_SIZE << (dmabuf)->buforder_tmpbuff, \ - (dmabuf)->tmpbuff, (dmabuf)->dmaaddr_tmpbuff); -#define cs4x_pgoff(vma) ((vma)->vm_pgoff) - -#define RSRCISIOREGION(dev,num) ((dev)->resource[(num)].start != 0 && \ - ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) -#define RSRCISMEMORYREGION(dev,num) ((dev)->resource[(num)].start != 0 && \ - ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) -#define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start) -#define PCI_GET_DRIVER_DATA pci_get_drvdata -#define PCI_SET_DRIVER_DATA pci_set_drvdata -#define PCI_SET_DMA_MASK(pcidev,mask) pcidev->dma_mask = mask - -#endif diff -Nru a/drivers/sound/cs46xxpm-24.h b/drivers/sound/cs46xxpm-24.h --- a/drivers/sound/cs46xxpm-24.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,53 +0,0 @@ -/******************************************************************************* -* -* "cs46xxpm-24.h" -- Cirrus Logic-Crystal CS46XX linux audio driver. -* -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- tom woller (twoller@crystal.cirrus.com) or -* (pcaudio@crystal.cirrus.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. -* -* 12/22/00 trw - new file. -* -*******************************************************************************/ -#ifndef __CS46XXPM24_H -#define __CS46XXPM24_H - -#include -#include "cs46xxpm.h" - - -#define CS46XX_ACPI_SUPPORT 1 -#ifdef CS46XX_ACPI_SUPPORT -/* -* for now (12/22/00) only enable the pm_register PM support. -* allow these table entries to be null. -*/ -static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state); -static int cs46xx_resume_tbl(struct pci_dev *pcidev); -#define cs_pm_register(a, b, c) 0 -#define cs_pm_unregister_all(a) -#define CS46XX_SUSPEND_TBL cs46xx_suspend_tbl -#define CS46XX_RESUME_TBL cs46xx_resume_tbl -#else -#define cs_pm_register(a, b, c) pm_register((a), (b), (c)); -#define cs_pm_unregister_all(a) pm_unregister_all((a)); -#define CS46XX_SUSPEND_TBL cs46xx_null -#define CS46XX_RESUME_TBL cs46xx_null -#endif -int cs46xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data); - -#endif diff -Nru a/drivers/sound/cs46xxpm.h b/drivers/sound/cs46xxpm.h --- a/drivers/sound/cs46xxpm.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,70 +0,0 @@ -/******************************************************************************* -* -* "cs46xxpm.h" -- Cirrus Logic-Crystal CS46XX linux audio driver. -* -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- tom woller (twoller@crystal.cirrus.com) or -* (pcaudio@crystal.cirrus.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. -* -* 12/22/00 trw - new file. -* -*******************************************************************************/ -#ifndef __CS46XXPM_H -#define __CS46XXPM_H - -#define CS46XX_AC97_HIGHESTREGTORESTORE 0x26 -#define CS46XX_AC97_NUMBER_RESTORE_REGS (CS46XX_AC97_HIGHESTREGTORESTORE/2-1) - -/* PM state defintions */ -#define CS46XX_PM_NOT_REGISTERED 0x1000 -#define CS46XX_PM_IDLE 0x0001 -#define CS46XX_PM_SUSPENDING 0x0002 -#define CS46XX_PM_SUSPENDED 0x0004 -#define CS46XX_PM_RESUMING 0x0008 -#define CS46XX_PM_RESUMED 0x0010 - -#define CS_POWER_DAC 0x0001 -#define CS_POWER_ADC 0x0002 -#define CS_POWER_MIXVON 0x0004 -#define CS_POWER_MIXVOFF 0x0008 -#define CS_AC97_POWER_CONTROL_ON 0xf000 /* always on bits (inverted) */ -#define CS_AC97_POWER_CONTROL_ADC 0x0100 -#define CS_AC97_POWER_CONTROL_DAC 0x0200 -#define CS_AC97_POWER_CONTROL_MIXVON 0x0400 -#define CS_AC97_POWER_CONTROL_MIXVOFF 0x0800 -#define CS_AC97_POWER_CONTROL_ADC_ON 0x0001 -#define CS_AC97_POWER_CONTROL_DAC_ON 0x0002 -#define CS_AC97_POWER_CONTROL_MIXVON_ON 0x0004 -#define CS_AC97_POWER_CONTROL_MIXVOFF_ON 0x0008 - -struct cs46xx_pm { - unsigned long flags; - u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue; - u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR; - u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save; - u32 u32SSPM_BITS; - u32 ac97[CS46XX_AC97_NUMBER_RESTORE_REGS]; - u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono; - u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose; - u32 u32hwptr_playback,u32hwptr_capture; - unsigned dmabuf_swptr_play; - int dmabuf_count_play; - unsigned dmabuf_swptr_capture; - int dmabuf_count_capture; -}; - -#endif diff -Nru a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c --- a/drivers/sound/dev_table.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,221 +0,0 @@ -/* - * sound/dev_table.c - * - * Device call tables. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - -#include - -#define _DEV_TABLE_C_ -#include "sound_config.h" - -int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, - int driver_size, int flags, unsigned int format_mask, - void *devc, int dma1, int dma2) -{ - struct audio_driver *d; - struct audio_operations *op; - int l, num; - - if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) { - printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name); - return -(EINVAL); - } - num = sound_alloc_audiodev(); - - if (num == -1) { - printk(KERN_ERR "sound: Too many audio drivers\n"); - return -(EBUSY); - } - d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver))); - - if (sound_nblocks < 1024) - sound_nblocks++; - - op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_operations))); - - if (sound_nblocks < 1024) - sound_nblocks++; - if (d == NULL || op == NULL) { - printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name); - sound_unload_audiodev(num); - return -(ENOMEM); - } - memset((char *) op, 0, sizeof(struct audio_operations)); - init_waitqueue_head(&op->in_sleeper); - init_waitqueue_head(&op->out_sleeper); - init_waitqueue_head(&op->poll_sleeper); - if (driver_size < sizeof(struct audio_driver)) - memset((char *) d, 0, sizeof(struct audio_driver)); - - memcpy((char *) d, (char *) driver, driver_size); - - op->d = d; - l = strlen(name) + 1; - if (l > sizeof(op->name)) - l = sizeof(op->name); - strncpy(op->name, name, l); - op->name[l - 1] = 0; - op->flags = flags; - op->format_mask = format_mask; - op->devc = devc; - - /* - * Hardcoded defaults - */ - audio_devs[num] = op; - - DMAbuf_init(num, dma1, dma2); - - audio_init_devices(); - return num; -} - -int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, - int driver_size, void *devc) -{ - struct mixer_operations *op; - int l; - - int n = sound_alloc_mixerdev(); - - if (n == -1) { - printk(KERN_ERR "Sound: Too many mixer drivers\n"); - return -EBUSY; - } - if (vers != MIXER_DRIVER_VERSION || - driver_size > sizeof(struct mixer_operations)) { - printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name); - return -EINVAL; - } - - /* FIXME: This leaks a mixer_operations struct every time its called - until you unload sound! */ - - op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct mixer_operations))); - - if (sound_nblocks < 1024) - sound_nblocks++; - if (op == NULL) { - printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name); - return -ENOMEM; - } - memset((char *) op, 0, sizeof(struct mixer_operations)); - memcpy((char *) op, (char *) driver, driver_size); - - l = strlen(name) + 1; - if (l > sizeof(op->name)) - l = sizeof(op->name); - strncpy(op->name, name, l); - op->name[l - 1] = 0; - op->devc = devc; - - mixer_devs[n] = op; - return n; -} - -void sound_unload_audiodev(int dev) -{ - if (dev != -1) { - DMAbuf_deinit(dev); - audio_devs[dev] = NULL; - unregister_sound_dsp((dev<<4)+3); - } -} - -int sound_alloc_audiodev(void) -{ - int i = register_sound_dsp(&oss_sound_fops, -1); - if(i==-1) - return i; - i>>=4; - if(i>=num_audiodevs) - num_audiodevs = i + 1; - return i; -} - -int sound_alloc_mididev(void) -{ - int i = register_sound_midi(&oss_sound_fops, -1); - if(i==-1) - return i; - i>>=4; - if(i>=num_midis) - num_midis = i + 1; - return i; -} - -int sound_alloc_synthdev(void) -{ - int i; - - for (i = 0; i < MAX_SYNTH_DEV; i++) { - if (synth_devs[i] == NULL) { - if (i >= num_synths) - num_synths++; - return i; - } - } - return -1; -} - -int sound_alloc_mixerdev(void) -{ - int i = register_sound_mixer(&oss_sound_fops, -1); - if(i==-1) - return -1; - i>>=4; - if(i>=num_mixers) - num_mixers = i + 1; - return i; -} - -int sound_alloc_timerdev(void) -{ - int i; - - for (i = 0; i < MAX_TIMER_DEV; i++) { - if (sound_timer_devs[i] == NULL) { - if (i >= num_sound_timers) - num_sound_timers++; - return i; - } - } - return -1; -} - -void sound_unload_mixerdev(int dev) -{ - if (dev != -1) { - mixer_devs[dev] = NULL; - unregister_sound_mixer(dev<<4); - num_mixers--; - } -} - -void sound_unload_mididev(int dev) -{ - if (dev != -1) { - midi_devs[dev] = NULL; - unregister_sound_midi((dev<<4)+2); - } -} - -void sound_unload_synthdev(int dev) -{ - if (dev != -1) - synth_devs[dev] = NULL; -} - -void sound_unload_timerdev(int dev) -{ - if (dev != -1) - sound_timer_devs[dev] = NULL; -} diff -Nru a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h --- a/drivers/sound/dev_table.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,403 +0,0 @@ -/* - * dev_table.h - * - * Global definitions for device call tables - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - - -#ifndef _DEV_TABLE_H_ -#define _DEV_TABLE_H_ - -/* - * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h) - * Numbers 1000 to N are reserved for driver's internal use. - */ - -#define SNDCARD_DESKPROXL 27 /* Compaq Deskpro XL */ -#define SNDCARD_VIDC 28 /* ARMs VIDC */ -#define SNDCARD_SBPNP 29 -#define SNDCARD_SOFTOSS 36 -#define SNDCARD_VMIDI 37 -#define SNDCARD_OPL3SA1 38 /* Note: clash in msnd.h */ -#define SNDCARD_OPL3SA1_SB 39 -#define SNDCARD_OPL3SA1_MPU 40 -#define SNDCARD_WAVEFRONT 41 -#define SNDCARD_OPL3SA2 42 -#define SNDCARD_OPL3SA2_MPU 43 -#define SNDCARD_WAVEARTIST 44 /* Waveartist */ -#define SNDCARD_OPL3SA2_MSS 45 /* Originally missed */ -#define SNDCARD_AD1816 88 - -/* - * NOTE! NOTE! NOTE! NOTE! - * - * If you modify this file, please check the dev_table.c also. - * - * NOTE! NOTE! NOTE! NOTE! - */ - -struct driver_info -{ - char *driver_id; - int card_subtype; /* Driver specific. Usually 0 */ - int card_type; /* From soundcard.h */ - char *name; - void (*attach) (struct address_info *hw_config); - int (*probe) (struct address_info *hw_config); - void (*unload) (struct address_info *hw_config); -}; - -struct card_info -{ - int card_type; /* Link (search key) to the driver list */ - struct address_info config; - int enabled; - void *for_driver_use; -}; - - -/* - * Device specific parameters (used only by dmabuf.c) - */ -#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR) - -#define DMODE_NONE 0 -#define DMODE_OUTPUT PCM_ENABLE_OUTPUT -#define DMODE_INPUT PCM_ENABLE_INPUT - -struct dma_buffparms -{ - int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */ - int closing; - - /* - * Pointers to raw buffers - */ - - char *raw_buf; - unsigned long raw_buf_phys; - int buffsize; - - /* - * Device state tables - */ - - unsigned long flags; -#define DMA_BUSY 0x00000001 -#define DMA_RESTART 0x00000002 -#define DMA_ACTIVE 0x00000004 -#define DMA_STARTED 0x00000008 -#define DMA_EMPTY 0x00000010 -#define DMA_ALLOC_DONE 0x00000020 -#define DMA_SYNCING 0x00000040 -#define DMA_DIRTY 0x00000080 -#define DMA_POST 0x00000100 -#define DMA_NODMA 0x00000200 -#define DMA_NOTIMEOUT 0x00000400 - - int open_mode; - - /* - * Queue parameters. - */ - int qlen; - int qhead; - int qtail; - int cfrag; /* Current incomplete fragment (write) */ - - int nbufs; - int counts[MAX_SUB_BUFFERS]; - int subdivision; - - int fragment_size; - int needs_reorg; - int max_fragments; - - int bytes_in_use; - - int underrun_count; - unsigned long byte_counter; - unsigned long user_counter; - unsigned long max_byte_counter; - int data_rate; /* Bytes/second */ - - int mapping_flags; -#define DMA_MAP_MAPPED 0x00000001 - char neutral_byte; - int dma; /* DMA channel */ - - int applic_profile; /* Application profile (APF_*) */ - /* Interrupt callback stuff */ - void (*audio_callback) (int dev, int parm); - int callback_parm; - - int buf_flags[MAX_SUB_BUFFERS]; -#define BUFF_EOF 0x00000001 /* Increment eof count */ -#define BUFF_DIRTY 0x00000002 /* Buffer written */ -}; - -/* - * Structure for use with various microcontrollers and DSP processors - * in the recent sound cards. - */ -typedef struct coproc_operations -{ - char name[64]; - struct module *owner; - int (*open) (void *devc, int sub_device); - void (*close) (void *devc, int sub_device); - int (*ioctl) (void *devc, unsigned int cmd, caddr_t arg, int local); - void (*reset) (void *devc); - - void *devc; /* Driver specific info */ -} coproc_operations; - -struct audio_driver -{ - struct module *owner; - int (*open) (int dev, int mode); - void (*close) (int dev); - void (*output_block) (int dev, unsigned long buf, - int count, int intrflag); - void (*start_input) (int dev, unsigned long buf, - int count, int intrflag); - int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); - int (*prepare_for_input) (int dev, int bufsize, int nbufs); - int (*prepare_for_output) (int dev, int bufsize, int nbufs); - void (*halt_io) (int dev); - int (*local_qlen)(int dev); - void (*copy_user) (int dev, - char *localbuf, int localoffs, - const char *userbuf, int useroffs, - int max_in, int max_out, - int *used, int *returned, - int len); - void (*halt_input) (int dev); - void (*halt_output) (int dev); - void (*trigger) (int dev, int bits); - int (*set_speed)(int dev, int speed); - unsigned int (*set_bits)(int dev, unsigned int bits); - short (*set_channels)(int dev, short channels); - void (*postprocess_write)(int dev); /* Device spesific postprocessing for written data */ - void (*preprocess_read)(int dev); /* Device spesific preprocessing for read data */ - void (*mmap)(int dev); -}; - -struct audio_operations -{ - char name[128]; - int flags; -#define NOTHING_SPECIAL 0x00 -#define NEEDS_RESTART 0x01 -#define DMA_AUTOMODE 0x02 -#define DMA_DUPLEX 0x04 -#define DMA_PSEUDO_AUTOMODE 0x08 -#define DMA_HARDSTOP 0x10 -#define DMA_EXACT 0x40 -#define DMA_NORESET 0x80 - int format_mask; /* Bitmask for supported audio formats */ - void *devc; /* Driver specific info */ - struct audio_driver *d; - void *portc; /* Driver spesific info */ - struct dma_buffparms *dmap_in, *dmap_out; - struct coproc_operations *coproc; - int mixer_dev; - int enable_bits; - int open_mode; - int go; - int min_fragment; /* 0 == unlimited */ - int max_fragment; /* 0 == unlimited */ - int parent_dev; /* 0 -> no parent, 1 to n -> parent=parent_dev+1 */ - - /* fields formerly in dmabuf.c */ - wait_queue_head_t in_sleeper; - wait_queue_head_t out_sleeper; - wait_queue_head_t poll_sleeper; - - /* fields formerly in audio.c */ - int audio_mode; - -#define AM_NONE 0 -#define AM_WRITE OPEN_WRITE -#define AM_READ OPEN_READ - - int local_format; - int audio_format; - int local_conversion; -#define CNV_MU_LAW 0x00000001 - - /* large structures at the end to keep offsets small */ - struct dma_buffparms dmaps[2]; -}; - -int *load_mixer_volumes(char *name, int *levels, int present); - -struct mixer_operations -{ - struct module *owner; - char id[16]; - char name[64]; - int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); - - void *devc; - int modify_counter; -}; - -struct synth_operations -{ - struct module *owner; - char *id; /* Unique identifier (ASCII) max 29 char */ - struct synth_info *info; - int midi_dev; - int synth_type; - int synth_subtype; - - int (*open) (int dev, int mode); - void (*close) (int dev); - int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); - int (*kill_note) (int dev, int voice, int note, int velocity); - int (*start_note) (int dev, int voice, int note, int velocity); - int (*set_instr) (int dev, int voice, int instr); - void (*reset) (int dev); - void (*hw_control) (int dev, unsigned char *event); - int (*load_patch) (int dev, int format, const char *addr, - int offs, int count, int pmgr_flag); - void (*aftertouch) (int dev, int voice, int pressure); - void (*controller) (int dev, int voice, int ctrl_num, int value); - void (*panning) (int dev, int voice, int value); - void (*volume_method) (int dev, int mode); - void (*bender) (int dev, int chn, int value); - int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc); - void (*setup_voice) (int dev, int voice, int chn); - int (*send_sysex)(int dev, unsigned char *bytes, int len); - - struct voice_alloc_info alloc; - struct channel_info chn_info[16]; - int emulation; -#define EMU_GM 1 /* General MIDI */ -#define EMU_XG 2 /* Yamaha XG */ -#define MAX_SYSEX_BUF 64 - unsigned char sysex_buf[MAX_SYSEX_BUF]; - int sysex_ptr; -}; - -struct midi_input_info -{ - /* MIDI input scanner variables */ -#define MI_MAX 10 - int m_busy; - unsigned char m_buf[MI_MAX]; - unsigned char m_prev_status; /* For running status */ - int m_ptr; -#define MST_INIT 0 -#define MST_DATA 1 -#define MST_SYSEX 2 - int m_state; - int m_left; -}; - -struct midi_operations -{ - struct module *owner; - struct midi_info info; - struct synth_operations *converter; - struct midi_input_info in_info; - int (*open) (int dev, int mode, - void (*inputintr)(int dev, unsigned char data), - void (*outputintr)(int dev) - ); - void (*close) (int dev); - int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); - int (*outputc) (int dev, unsigned char data); - int (*start_read) (int dev); - int (*end_read) (int dev); - void (*kick)(int dev); - int (*command) (int dev, unsigned char *data); - int (*buffer_status) (int dev); - int (*prefix_cmd) (int dev, unsigned char status); - struct coproc_operations *coproc; - void *devc; -}; - -struct sound_lowlev_timer -{ - int dev; - int priority; - unsigned int (*tmr_start)(int dev, unsigned int usecs); - void (*tmr_disable)(int dev); - void (*tmr_restart)(int dev); -}; - -struct sound_timer_operations -{ - struct module *owner; - struct sound_timer_info info; - int priority; - int devlink; - int (*open)(int dev, int mode); - void (*close)(int dev); - int (*event)(int dev, unsigned char *ev); - unsigned long (*get_time)(int dev); - int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); - void (*arm_timer)(int dev, long time); -}; - -#ifdef _DEV_TABLE_C_ -struct audio_operations *audio_devs[MAX_AUDIO_DEV]; -int num_audiodevs; -struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; -int num_mixers; -struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; -int num_synths; -struct midi_operations *midi_devs[MAX_MIDI_DEV]; -int num_midis; - -extern struct sound_timer_operations default_sound_timer; -struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { - &default_sound_timer, NULL -}; -int num_sound_timers = 1; -#else -extern struct audio_operations *audio_devs[MAX_AUDIO_DEV]; -extern int num_audiodevs; -extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; -extern int num_mixers; -extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; -extern int num_synths; -extern struct midi_operations *midi_devs[MAX_MIDI_DEV]; -extern int num_midis; -extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV]; -extern int num_sound_timers; -#endif /* _DEV_TABLE_C_ */ - -extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info); -void sound_timer_init (struct sound_lowlev_timer *t, char *name); -void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan); - -#define AUDIO_DRIVER_VERSION 2 -#define MIXER_DRIVER_VERSION 2 -int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, - int driver_size, int flags, unsigned int format_mask, - void *devc, int dma1, int dma2); -int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, - int driver_size, void *devc); - -void sound_unload_audiodev(int dev); -void sound_unload_mixerdev(int dev); -void sound_unload_mididev(int dev); -void sound_unload_synthdev(int dev); -void sound_unload_timerdev(int dev); -int sound_alloc_audiodev(void); -int sound_alloc_mixerdev(void); -int sound_alloc_timerdev(void); -int sound_alloc_synthdev(void); -int sound_alloc_mididev(void); -#endif /* _DEV_TABLE_H_ */ - diff -Nru a/drivers/sound/dm.h b/drivers/sound/dm.h --- a/drivers/sound/dm.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,79 +0,0 @@ -#ifndef _DRIVERS_SOUND_DM_H -#define _DRIVERS_SOUND_DM_H - -/* - * Definitions of the 'direct midi sound' interface used - * by the newer commercial OSS package. We should export - * this to userland somewhere in glibc later. - */ - -/* - * Data structure composing an FM "note" or sound event. - */ - -struct dm_fm_voice -{ - u8 op; - u8 voice; - u8 am; - u8 vibrato; - u8 do_sustain; - u8 kbd_scale; - u8 harmonic; - u8 scale_level; - u8 volume; - u8 attack; - u8 decay; - u8 sustain; - u8 release; - u8 feedback; - u8 connection; - u8 left; - u8 right; - u8 waveform; -}; - -/* - * This describes an FM note by its voice, octave, frequency number (10bit) - * and key on/off. - */ - -struct dm_fm_note -{ - u8 voice; - u8 octave; - u32 fnum; - u8 key_on; -}; - -/* - * FM parameters that apply globally to all voices, and thus are not "notes" - */ - -struct dm_fm_params -{ - u8 am_depth; - u8 vib_depth; - u8 kbd_split; - u8 rhythm; - - /* This block is the percussion instrument data */ - u8 bass; - u8 snare; - u8 tomtom; - u8 cymbal; - u8 hihat; -}; - -/* - * FM mode ioctl settings - */ - -#define FM_IOCTL_RESET 0x20 -#define FM_IOCTL_PLAY_NOTE 0x21 -#define FM_IOCTL_SET_VOICE 0x22 -#define FM_IOCTL_SET_PARAMS 0x23 -#define FM_IOCTL_SET_MODE 0x24 -#define FM_IOCTL_SET_OPL 0x25 - -#endif diff -Nru a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c --- a/drivers/sound/dmabuf.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1296 +0,0 @@ -/* - * sound/dmabuf.c - * - * The DMA buffer manager for digitized voice applications - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Thomas Sailer : moved several static variables into struct audio_operations - * (which is grossly misnamed btw.) because they have the same - * lifetime as the rest in there and dynamic allocation saves - * 12k or so - * Thomas Sailer : remove {in,out}_sleep_flag. It was used for the sleeper to - * determine if it was woken up by the expiring timeout or by - * an explicit wake_up. The return value from schedule_timeout - * can be used instead; if 0, the wakeup was due to the timeout. - * - * Rob Riggs Added persistent DMA buffers (1998/10/17) - */ - -#define BE_CONSERVATIVE -#define SAMPLE_ROUNDUP 0 - -#include "sound_config.h" -#include - -#define DMAP_FREE_ON_CLOSE 0 -#define DMAP_KEEP_ON_CLOSE 1 -extern int sound_dmap_flag; - -static void dma_reset_output(int dev); -static void dma_reset_input(int dev); -static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode); - - - -static int debugmem = 0; /* switched off by default */ -static int dma_buffsize = DSP_BUFFSIZE; - -static long dmabuf_timeout(struct dma_buffparms *dmap) -{ - long tmout; - - tmout = (dmap->fragment_size * HZ) / dmap->data_rate; - tmout += HZ / 5; /* Some safety distance */ - if (tmout < (HZ / 2)) - tmout = HZ / 2; - if (tmout > 20 * HZ) - tmout = 20 * HZ; - return tmout; -} - -static int sound_alloc_dmap(struct dma_buffparms *dmap) -{ - char *start_addr, *end_addr; - int dma_pagesize; - int sz, size; - struct page *page; - - dmap->mapping_flags &= ~DMA_MAP_MAPPED; - - if (dmap->raw_buf != NULL) - return 0; /* Already done */ - if (dma_buffsize < 4096) - dma_buffsize = 4096; - dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024); - - /* - * Now check for the Cyrix problem. - */ - - if(isa_dma_bridge_buggy==2) - dma_pagesize=32768; - - dmap->raw_buf = NULL; - dmap->buffsize = dma_buffsize; - if (dmap->buffsize > dma_pagesize) - dmap->buffsize = dma_pagesize; - start_addr = NULL; - /* - * Now loop until we get a free buffer. Try to get smaller buffer if - * it fails. Don't accept smaller than 8k buffer for performance - * reasons. - */ - while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) { - for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); - dmap->buffsize = PAGE_SIZE * (1 << sz); - start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz); - if (start_addr == NULL) - dmap->buffsize /= 2; - } - - if (start_addr == NULL) { - printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n"); - return -ENOMEM; - } else { - /* make some checks */ - end_addr = start_addr + dmap->buffsize - 1; - - if (debugmem) - printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr); - - /* now check if it fits into the same dma-pagesize */ - - if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1)) - || end_addr >= (char *) (MAX_DMA_ADDRESS)) { - printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize); - return -EFAULT; - } - } - dmap->raw_buf = start_addr; - dmap->raw_buf_phys = virt_to_bus(start_addr); - - for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) - mem_map_reserve(page); - return 0; -} - -static void sound_free_dmap(struct dma_buffparms *dmap) -{ - int sz, size; - struct page *page; - unsigned long start_addr, end_addr; - - if (dmap->raw_buf == NULL) - return; - if (dmap->mapping_flags & DMA_MAP_MAPPED) - return; /* Don't free mmapped buffer. Will use it next time */ - for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); - - start_addr = (unsigned long) dmap->raw_buf; - end_addr = start_addr + dmap->buffsize; - - for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) - mem_map_unreserve(page); - - free_pages((unsigned long) dmap->raw_buf, sz); - dmap->raw_buf = NULL; -} - - -/* Intel version !!!!!!!!! */ - -static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode) -{ - unsigned long flags; - int chan = dmap->dma; - - /* printk( "Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */ - - flags = claim_dma_lock(); - disable_dma(chan); - clear_dma_ff(chan); - set_dma_mode(chan, dma_mode); - set_dma_addr(chan, physaddr); - set_dma_count(chan, count); - enable_dma(chan); - release_dma_lock(flags); - - return 0; -} - -static void dma_init_buffers(struct dma_buffparms *dmap) -{ - dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; - dmap->byte_counter = 0; - dmap->max_byte_counter = 8000 * 60 * 60; - dmap->bytes_in_use = dmap->buffsize; - - dmap->dma_mode = DMODE_NONE; - dmap->mapping_flags = 0; - dmap->neutral_byte = 0x80; - dmap->data_rate = 8000; - dmap->cfrag = -1; - dmap->closing = 0; - dmap->nbufs = 1; - dmap->flags = DMA_BUSY; /* Other flags off */ -} - -static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap) -{ - int err; - - if (dmap->flags & DMA_BUSY) - return -EBUSY; - if ((err = sound_alloc_dmap(dmap)) < 0) - return err; - - if (dmap->raw_buf == NULL) { - printk(KERN_WARNING "Sound: DMA buffers not available\n"); - return -ENOSPC; /* Memory allocation failed during boot */ - } - if (dmap->dma >= 0 && sound_open_dma(dmap->dma, adev->name)) { - printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma); - return -EBUSY; - } - dma_init_buffers(dmap); - dmap->open_mode = mode; - dmap->subdivision = dmap->underrun_count = 0; - dmap->fragment_size = 0; - dmap->max_fragments = 65536; /* Just a large value */ - dmap->byte_counter = 0; - dmap->max_byte_counter = 8000 * 60 * 60; - dmap->applic_profile = APF_NORMAL; - dmap->needs_reorg = 1; - dmap->audio_callback = NULL; - dmap->callback_parm = 0; - return 0; -} - -static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap) -{ - unsigned long flags; - - if (dmap->dma >= 0) { - sound_close_dma(dmap->dma); - flags=claim_dma_lock(); - disable_dma(dmap->dma); - release_dma_lock(flags); - } - if (dmap->flags & DMA_BUSY) - dmap->dma_mode = DMODE_NONE; - dmap->flags &= ~DMA_BUSY; - - if (sound_dmap_flag == DMAP_FREE_ON_CLOSE) - sound_free_dmap(dmap); -} - - -static unsigned int default_set_bits(int dev, unsigned int bits) -{ - mm_segment_t fs = get_fs(); - - set_fs(get_ds()); - audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (caddr_t)&bits); - set_fs(fs); - return bits; -} - -static int default_set_speed(int dev, int speed) -{ - mm_segment_t fs = get_fs(); - - set_fs(get_ds()); - audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (caddr_t)&speed); - set_fs(fs); - return speed; -} - -static short default_set_channels(int dev, short channels) -{ - int c = channels; - mm_segment_t fs = get_fs(); - - set_fs(get_ds()); - audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (caddr_t)&c); - set_fs(fs); - return c; -} - -static void check_driver(struct audio_driver *d) -{ - if (d->set_speed == NULL) - d->set_speed = default_set_speed; - if (d->set_bits == NULL) - d->set_bits = default_set_bits; - if (d->set_channels == NULL) - d->set_channels = default_set_channels; -} - -int DMAbuf_open(int dev, int mode) -{ - struct audio_operations *adev = audio_devs[dev]; - int retval; - struct dma_buffparms *dmap_in = NULL; - struct dma_buffparms *dmap_out = NULL; - - if (!adev) - return -ENXIO; - if (!(adev->flags & DMA_DUPLEX)) - adev->dmap_in = adev->dmap_out; - check_driver(adev->d); - - if ((retval = adev->d->open(dev, mode)) < 0) - return retval; - dmap_out = adev->dmap_out; - dmap_in = adev->dmap_in; - if (dmap_in == dmap_out) - adev->flags &= ~DMA_DUPLEX; - - if (mode & OPEN_WRITE) { - if ((retval = open_dmap(adev, mode, dmap_out)) < 0) { - adev->d->close(dev); - return retval; - } - } - adev->enable_bits = mode; - - if (mode == OPEN_READ || (mode != OPEN_WRITE && (adev->flags & DMA_DUPLEX))) { - if ((retval = open_dmap(adev, mode, dmap_in)) < 0) { - adev->d->close(dev); - if (mode & OPEN_WRITE) - close_dmap(adev, dmap_out); - return retval; - } - } - adev->open_mode = mode; - adev->go = 1; - - adev->d->set_bits(dev, 8); - adev->d->set_channels(dev, 1); - adev->d->set_speed(dev, DSP_DEFAULT_SPEED); - if (adev->dmap_out->dma_mode == DMODE_OUTPUT) - memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, - adev->dmap_out->bytes_in_use); - return 0; -} - -void DMAbuf_reset(int dev) -{ - if (audio_devs[dev]->open_mode & OPEN_WRITE) - dma_reset_output(dev); - - if (audio_devs[dev]->open_mode & OPEN_READ) - dma_reset_input(dev); -} - -static void dma_reset_output(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags,f ; - struct dma_buffparms *dmap = adev->dmap_out; - - if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */ - return; - - /* - * First wait until the current fragment has been played completely - */ - save_flags(flags); - cli(); - adev->dmap_out->flags |= DMA_SYNCING; - - adev->dmap_out->underrun_count = 0; - if (!signal_pending(current) && adev->dmap_out->qlen && - adev->dmap_out->underrun_count == 0) - interruptible_sleep_on_timeout(&adev->out_sleeper, - dmabuf_timeout(dmap)); - adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); - - /* - * Finally shut the device off - */ - if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output) - adev->d->halt_io(dev); - else - adev->d->halt_output(dev); - adev->dmap_out->flags &= ~DMA_STARTED; - - f=claim_dma_lock(); - clear_dma_ff(dmap->dma); - disable_dma(dmap->dma); - release_dma_lock(f); - - restore_flags(flags); - dmap->byte_counter = 0; - reorganize_buffers(dev, adev->dmap_out, 0); - dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; -} - -static void dma_reset_input(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - struct dma_buffparms *dmap = adev->dmap_in; - - save_flags(flags); - cli(); - if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input) - adev->d->halt_io(dev); - else - adev->d->halt_input(dev); - adev->dmap_in->flags &= ~DMA_STARTED; - restore_flags(flags); - - dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; - dmap->byte_counter = 0; - reorganize_buffers(dev, adev->dmap_in, 1); -} - -void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap) -{ - struct audio_operations *adev = audio_devs[dev]; - - if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT)) - return; /* Don't start DMA yet */ - dmap->dma_mode = DMODE_OUTPUT; - - if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { - if (!(dmap->flags & DMA_STARTED)) { - reorganize_buffers(dev, dmap, 0); - if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs)) - return; - if (!(dmap->flags & DMA_NODMA)) - local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE); - dmap->flags |= DMA_STARTED; - } - if (dmap->counts[dmap->qhead] == 0) - dmap->counts[dmap->qhead] = dmap->fragment_size; - dmap->dma_mode = DMODE_OUTPUT; - adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size, - dmap->counts[dmap->qhead], 1); - if (adev->d->trigger) - adev->d->trigger(dev,adev->enable_bits * adev->go); - } - dmap->flags |= DMA_ACTIVE; -} - -int DMAbuf_sync(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - int n = 0; - struct dma_buffparms *dmap; - - if (!adev->go && !(adev->enable_bits & PCM_ENABLE_OUTPUT)) - return 0; - - if (adev->dmap_out->dma_mode == DMODE_OUTPUT) { - dmap = adev->dmap_out; - save_flags(flags); - cli(); - if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE)) - DMAbuf_launch_output(dev, dmap); - adev->dmap_out->flags |= DMA_SYNCING; - adev->dmap_out->underrun_count = 0; - while (!signal_pending(current) && n++ <= adev->dmap_out->nbufs && - adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) { - long t = dmabuf_timeout(dmap); - t = interruptible_sleep_on_timeout(&adev->out_sleeper, - t); - if (!t) { - adev->dmap_out->flags &= ~DMA_SYNCING; - restore_flags(flags); - return adev->dmap_out->qlen; - } - } - adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); - restore_flags(flags); - - /* - * Some devices such as GUS have huge amount of on board RAM for the - * audio data. We have to wait until the device has finished playing. - */ - - save_flags(flags); - cli(); - if (adev->d->local_qlen) { /* Device has hidden buffers */ - while (!signal_pending(current) && - adev->d->local_qlen(dev)) - interruptible_sleep_on_timeout(&adev->out_sleeper, - dmabuf_timeout(dmap)); - } - restore_flags(flags); - } - adev->dmap_out->dma_mode = DMODE_NONE; - return adev->dmap_out->qlen; -} - -int DMAbuf_release(int dev, int mode) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - - if (adev->open_mode & OPEN_WRITE) - adev->dmap_out->closing = 1; - if (adev->open_mode & OPEN_READ) - adev->dmap_in->closing = 1; - - if (adev->open_mode & OPEN_WRITE) - if (!(adev->dmap_out->mapping_flags & DMA_MAP_MAPPED)) - if (!signal_pending(current) && (adev->dmap_out->dma_mode == DMODE_OUTPUT)) - DMAbuf_sync(dev); - if (adev->dmap_out->dma_mode == DMODE_OUTPUT) - memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use); - save_flags(flags); - cli(); - - DMAbuf_reset(dev); - adev->d->close(dev); - - if (adev->open_mode & OPEN_WRITE) - close_dmap(adev, adev->dmap_out); - - if (adev->open_mode == OPEN_READ || - (adev->open_mode != OPEN_WRITE && - (adev->flags & DMA_DUPLEX))) - close_dmap(adev, adev->dmap_in); - adev->open_mode = 0; - restore_flags(flags); - return 0; -} - -int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap) -{ - struct audio_operations *adev = audio_devs[dev]; - int err; - - if (!(adev->open_mode & OPEN_READ)) - return 0; - if (!(adev->enable_bits & PCM_ENABLE_INPUT)) - return 0; - if (dmap->dma_mode == DMODE_OUTPUT) { /* Direction change */ - DMAbuf_sync(dev); - DMAbuf_reset(dev); - dmap->dma_mode = DMODE_NONE; - } - if (!dmap->dma_mode) { - reorganize_buffers(dev, dmap, 1); - if ((err = adev->d->prepare_for_input(dev, - dmap->fragment_size, dmap->nbufs)) < 0) - return err; - dmap->dma_mode = DMODE_INPUT; - } - if (!(dmap->flags & DMA_ACTIVE)) { - if (dmap->needs_reorg) - reorganize_buffers(dev, dmap, 0); - local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); - adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, - dmap->fragment_size, 0); - dmap->flags |= DMA_ACTIVE; - if (adev->d->trigger) - adev->d->trigger(dev, adev->enable_bits * adev->go); - } - return 0; -} - -int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - int err = 0, n = 0; - struct dma_buffparms *dmap = adev->dmap_in; - int go; - - if (!(adev->open_mode & OPEN_READ)) - return -EIO; - if (dmap->needs_reorg) - reorganize_buffers(dev, dmap, 0); - save_flags(flags); - cli(); - if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) { -/* printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/ - restore_flags(flags); - return -EINVAL; - } else while (dmap->qlen <= 0 && n++ < 10) { - long timeout = MAX_SCHEDULE_TIMEOUT; - if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) { - restore_flags(flags); - return -EAGAIN; - } - if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) { - restore_flags(flags); - return err; - } - /* Wait for the next block */ - - if (dontblock) { - restore_flags(flags); - return -EAGAIN; - } - if ((go = adev->go)) - timeout = dmabuf_timeout(dmap); - timeout = interruptible_sleep_on_timeout(&adev->in_sleeper, - timeout); - if (!timeout) { - /* FIXME: include device name */ - err = -EIO; - printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); - dma_reset_input(dev); - } else - err = -EINTR; - } - restore_flags(flags); - - if (dmap->qlen <= 0) - return err ? err : -EINTR; - *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]]; - *len = dmap->fragment_size - dmap->counts[dmap->qhead]; - - return dmap->qhead; -} - -int DMAbuf_rmchars(int dev, int buff_no, int c) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_in; - int p = dmap->counts[dmap->qhead] + c; - - if (dmap->mapping_flags & DMA_MAP_MAPPED) - { -/* printk("Sound: Can't read from mmapped device (2)\n");*/ - return -EINVAL; - } - else if (dmap->qlen <= 0) - return -EIO; - else if (p >= dmap->fragment_size) { /* This buffer is completely empty */ - dmap->counts[dmap->qhead] = 0; - dmap->qlen--; - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - } - else dmap->counts[dmap->qhead] = p; - - return 0; -} - -int DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction) -{ - /* - * Try to approximate the active byte position of the DMA pointer within the - * buffer area as well as possible. - */ - - int pos; - unsigned long flags; - unsigned long f; - - save_flags(flags); - cli(); - if (!(dmap->flags & DMA_ACTIVE)) - pos = 0; - else { - int chan = dmap->dma; - - f=claim_dma_lock(); - clear_dma_ff(chan); - - if(!isa_dma_bridge_buggy) - disable_dma(dmap->dma); - - pos = get_dma_residue(chan); - - pos = dmap->bytes_in_use - pos; - - if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) { - if (direction == DMODE_OUTPUT) { - if (dmap->qhead == 0) - if (pos > dmap->fragment_size) - pos = 0; - } else { - if (dmap->qtail == 0) - if (pos > dmap->fragment_size) - pos = 0; - } - } - if (pos < 0) - pos = 0; - if (pos >= dmap->bytes_in_use) - pos = 0; - - if(!isa_dma_bridge_buggy) - enable_dma(dmap->dma); - - release_dma_lock(f); - } - restore_flags(flags); - /* printk( "%04x ", pos); */ - - return pos; -} - -/* - * DMAbuf_start_devices() is called by the /dev/music driver to start - * one or more audio devices at desired moment. - */ - -void DMAbuf_start_devices(unsigned int devmask) -{ - struct audio_operations *adev; - int dev; - - for (dev = 0; dev < num_audiodevs; dev++) { - if (!(devmask & (1 << dev))) - continue; - if (!(adev = audio_devs[dev])) - continue; - if (adev->open_mode == 0) - continue; - if (adev->go) - continue; - /* OK to start the device */ - adev->go = 1; - if (adev->d->trigger) - adev->d->trigger(dev,adev->enable_bits * adev->go); - } -} - -int DMAbuf_space_in_queue(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - int len, max, tmp; - struct dma_buffparms *dmap = adev->dmap_out; - int lim = dmap->nbufs; - - if (lim < 2) - lim = 2; - - if (dmap->qlen >= lim) /* No space at all */ - return 0; - - /* - * Verify that there are no more pending buffers than the limit - * defined by the process. - */ - - max = dmap->max_fragments; - if (max > lim) - max = lim; - len = dmap->qlen; - - if (adev->d->local_qlen) { - tmp = adev->d->local_qlen(dev); - if (tmp && len) - tmp--; /* This buffer has been counted twice */ - len += tmp; - } - if (dmap->byte_counter % dmap->fragment_size) /* There is a partial fragment */ - len = len + 1; - - if (len >= max) - return 0; - return max - len; -} - -static int output_sleep(int dev, int dontblock) -{ - struct audio_operations *adev = audio_devs[dev]; - int err = 0; - struct dma_buffparms *dmap = adev->dmap_out; - long timeout; - long timeout_value; - - if (dontblock) - return -EAGAIN; - if (!(adev->enable_bits & PCM_ENABLE_OUTPUT)) - return -EAGAIN; - - /* - * Wait for free space - */ - if (signal_pending(current)) - return -EINTR; - timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT)); - if (timeout) - timeout_value = dmabuf_timeout(dmap); - else - timeout_value = MAX_SCHEDULE_TIMEOUT; - timeout_value = interruptible_sleep_on_timeout(&adev->out_sleeper, - timeout_value); - if (timeout != MAX_SCHEDULE_TIMEOUT && !timeout_value) { - printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); - dma_reset_output(dev); - } else { - if (signal_pending(current)) - err = -EINTR; - } - return err; -} - -static int find_output_space(int dev, char **buf, int *size) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_out; - unsigned long flags; - unsigned long active_offs; - long len, offs; - int maxfrags; - int occupied_bytes = (dmap->user_counter % dmap->fragment_size); - - *buf = dmap->raw_buf; - if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes) - return 0; - save_flags(flags); - cli(); - -#ifdef BE_CONSERVATIVE - active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size; -#else - active_offs = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT); - /* Check for pointer wrapping situation */ - if (active_offs < 0 || active_offs >= dmap->bytes_in_use) - active_offs = 0; - active_offs += dmap->byte_counter; -#endif - - offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP; - if (offs < 0 || offs >= dmap->bytes_in_use) { - restore_flags(flags); - printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs); - printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use); - return 0; - } - *buf = dmap->raw_buf + offs; - - len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */ - - if ((offs + len) > dmap->bytes_in_use) - len = dmap->bytes_in_use - offs; - if (len < 0) { - restore_flags(flags); - return 0; - } - if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes)) - len = (maxfrags * dmap->fragment_size) - occupied_bytes; - *size = len & ~SAMPLE_ROUNDUP; - restore_flags(flags); - return (*size > 0); -} - -int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - int err = -EIO; - struct dma_buffparms *dmap = adev->dmap_out; - - if (dmap->needs_reorg) - reorganize_buffers(dev, dmap, 0); - - if (dmap->mapping_flags & DMA_MAP_MAPPED) { -/* printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/ - return -EINVAL; - } - if (dmap->dma_mode == DMODE_INPUT) { /* Direction change */ - DMAbuf_reset(dev); - dmap->dma_mode = DMODE_NONE; - } - dmap->dma_mode = DMODE_OUTPUT; - - save_flags(flags); - cli(); - while (find_output_space(dev, buf, size) <= 0) { - if ((err = output_sleep(dev, dontblock)) < 0) { - restore_flags(flags); - return err; - } - } - restore_flags(flags); - - return 0; -} - -int DMAbuf_move_wrpointer(int dev, int l) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_out; - unsigned long ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; - unsigned long end_ptr, p; - int post = (dmap->flags & DMA_POST); - - dmap->flags &= ~DMA_POST; - dmap->cfrag = -1; - dmap->user_counter += l; - dmap->flags |= DMA_DIRTY; - - if (dmap->byte_counter >= dmap->max_byte_counter) { - /* Wrap the byte counters */ - long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); - decr -= dmap->byte_counter; - dmap->user_counter -= decr; - } - end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; - - p = (dmap->user_counter - 1) % dmap->bytes_in_use; - dmap->neutral_byte = dmap->raw_buf[p]; - - /* Update the fragment based bookkeeping too */ - while (ptr < end_ptr) { - dmap->counts[dmap->qtail] = dmap->fragment_size; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - dmap->qlen++; - ptr += dmap->fragment_size; - } - - dmap->counts[dmap->qtail] = dmap->user_counter - ptr; - - /* - * Let the low level driver to perform some postprocessing to - * the written data. - */ - if (adev->d->postprocess_write) - adev->d->postprocess_write(dev); - - if (!(dmap->flags & DMA_ACTIVE)) - if (dmap->qlen > 1 || (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1))) - DMAbuf_launch_output(dev, dmap); - return 0; -} - -int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in; - - if (dmap->raw_buf == NULL) { - printk(KERN_ERR "sound: DMA buffer(1) == NULL\n"); - printk("Device %d, chn=%s\n", dev, (dmap == adev->dmap_out) ? "out" : "in"); - return 0; - } - if (dmap->dma < 0) - return 0; - sound_start_dma(dmap, physaddr, count, dma_mode); - return count; -} - -static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode) -{ - struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in; - - if (dmap->raw_buf == NULL) { - printk(KERN_ERR "sound: DMA buffer(2) == NULL\n"); - printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in"); - return 0; - } - if (dmap->flags & DMA_NODMA) - return 1; - if (dmap->dma < 0) - return 0; - sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT); - dmap->flags |= DMA_STARTED; - return count; -} - -static void finish_output_interrupt(int dev, struct dma_buffparms *dmap) -{ - struct audio_operations *adev = audio_devs[dev]; - - if (dmap->audio_callback != NULL) - dmap->audio_callback(dev, dmap->callback_parm); - wake_up(&adev->out_sleeper); - wake_up(&adev->poll_sleeper); -} - -static void do_outputintr(int dev, int dummy) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - struct dma_buffparms *dmap = adev->dmap_out; - int this_fragment; - - if (dmap->raw_buf == NULL) { - printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev); - return; - } - if (dmap->mapping_flags & DMA_MAP_MAPPED) { /* Virtual memory mapped access */ - /* mmapped access */ - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - if (dmap->qhead == 0) { /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; - if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ - long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); - decr -= dmap->byte_counter; - dmap->user_counter -= decr; - } - } - dmap->qlen++; /* Yes increment it (don't decrement) */ - if (!(adev->flags & DMA_AUTOMODE)) - dmap->flags &= ~DMA_ACTIVE; - dmap->counts[dmap->qhead] = dmap->fragment_size; - DMAbuf_launch_output(dev, dmap); - finish_output_interrupt(dev, dmap); - return; - } - save_flags(flags); - cli(); - - dmap->qlen--; - this_fragment = dmap->qhead; - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - - if (dmap->qhead == 0) { /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; - if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ - long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); - decr -= dmap->byte_counter; - dmap->user_counter -= decr; - } - } - if (!(adev->flags & DMA_AUTOMODE)) - dmap->flags &= ~DMA_ACTIVE; - - /* - * This is dmap->qlen <= 0 except when closing when - * dmap->qlen < 0 - */ - - while (dmap->qlen <= -dmap->closing) { - dmap->underrun_count++; - dmap->qlen++; - if ((dmap->flags & DMA_DIRTY) && dmap->applic_profile != APF_CPUINTENS) { - dmap->flags &= ~DMA_DIRTY; - memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, - adev->dmap_out->buffsize); - } - dmap->user_counter += dmap->fragment_size; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - } - if (dmap->qlen > 0) - DMAbuf_launch_output(dev, dmap); - restore_flags(flags); - finish_output_interrupt(dev, dmap); -} - -void DMAbuf_outputintr(int dev, int notify_only) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - struct dma_buffparms *dmap = adev->dmap_out; - - save_flags(flags); - cli(); - if (!(dmap->flags & DMA_NODMA)) { - int chan = dmap->dma, pos, n; - unsigned long f; - - f=claim_dma_lock(); - - if(!isa_dma_bridge_buggy) - disable_dma(dmap->dma); - clear_dma_ff(chan); - pos = dmap->bytes_in_use - get_dma_residue(chan); - if(!isa_dma_bridge_buggy) - enable_dma(dmap->dma); - release_dma_lock(f); - - pos = pos / dmap->fragment_size; /* Actual qhead */ - if (pos < 0 || pos >= dmap->nbufs) - pos = 0; - n = 0; - while (dmap->qhead != pos && n++ < dmap->nbufs) - do_outputintr(dev, notify_only); - } - else - do_outputintr(dev, notify_only); - restore_flags(flags); -} - -static void do_inputintr(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_in; - - if (dmap->raw_buf == NULL) { - printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n"); - return; - } - if (dmap->mapping_flags & DMA_MAP_MAPPED) { - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - if (dmap->qtail == 0) { /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; - if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ - long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; - decr -= dmap->byte_counter; - dmap->user_counter -= decr; - } - } - dmap->qlen++; - - if (!(adev->flags & DMA_AUTOMODE)) { - if (dmap->needs_reorg) - reorganize_buffers(dev, dmap, 0); - local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ); - adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, - dmap->fragment_size, 1); - if (adev->d->trigger) - adev->d->trigger(dev, adev->enable_bits * adev->go); - } - dmap->flags |= DMA_ACTIVE; - } else if (dmap->qlen >= (dmap->nbufs - 1)) { - printk(KERN_WARNING "Sound: Recording overrun\n"); - dmap->underrun_count++; - - /* Just throw away the oldest fragment but keep the engine running */ - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - } else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) { - dmap->qlen++; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - if (dmap->qtail == 0) { /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; - if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ - long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; - decr -= dmap->byte_counter; - dmap->user_counter -= decr; - } - } - } - if (!(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { - local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); - adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1); - if (adev->d->trigger) - adev->d->trigger(dev,adev->enable_bits * adev->go); - } - dmap->flags |= DMA_ACTIVE; - if (dmap->qlen > 0) - { - wake_up(&adev->in_sleeper); - wake_up(&adev->poll_sleeper); - } -} - -void DMAbuf_inputintr(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_in; - unsigned long flags; - - save_flags(flags); - cli(); - - if (!(dmap->flags & DMA_NODMA)) { - int chan = dmap->dma, pos, n; - unsigned long f; - - f=claim_dma_lock(); - if(!isa_dma_bridge_buggy) - disable_dma(dmap->dma); - clear_dma_ff(chan); - pos = dmap->bytes_in_use - get_dma_residue(chan); - if(!isa_dma_bridge_buggy) - enable_dma(dmap->dma); - release_dma_lock(f); - - pos = pos / dmap->fragment_size; /* Actual qhead */ - if (pos < 0 || pos >= dmap->nbufs) - pos = 0; - - n = 0; - while (dmap->qtail != pos && ++n < dmap->nbufs) - do_inputintr(dev); - } else - do_inputintr(dev); - restore_flags(flags); -} - -int DMAbuf_open_dma(int dev) -{ - /* - * NOTE! This routine opens only the primary DMA channel (output). - */ - struct audio_operations *adev = audio_devs[dev]; - int err; - - if ((err = open_dmap(adev, OPEN_READWRITE, adev->dmap_out)) < 0) - return -EBUSY; - dma_init_buffers(adev->dmap_out); - adev->dmap_out->flags |= DMA_ALLOC_DONE; - adev->dmap_out->fragment_size = adev->dmap_out->buffsize; - - if (adev->dmap_out->dma >= 0) { - unsigned long flags; - - flags=claim_dma_lock(); - clear_dma_ff(adev->dmap_out->dma); - disable_dma(adev->dmap_out->dma); - release_dma_lock(flags); - } - return 0; -} - -void DMAbuf_close_dma(int dev) -{ - close_dmap(audio_devs[dev], audio_devs[dev]->dmap_out); -} - -void DMAbuf_init(int dev, int dma1, int dma2) -{ - struct audio_operations *adev = audio_devs[dev]; - /* - * NOTE! This routine could be called several times. - */ - - /* drag in audio_syms.o */ - { - extern char audio_syms_symbol; - audio_syms_symbol = 0; - } - - if (adev && adev->dmap_out == NULL) { - if (adev->d == NULL) - panic("OSS: audio_devs[%d]->d == NULL\n", dev); - - if (adev->parent_dev) { /* Use DMA map of the parent dev */ - int parent = adev->parent_dev - 1; - adev->dmap_out = audio_devs[parent]->dmap_out; - adev->dmap_in = audio_devs[parent]->dmap_in; - } else { - adev->dmap_out = adev->dmap_in = &adev->dmaps[0]; - adev->dmap_out->dma = dma1; - if (adev->flags & DMA_DUPLEX) { - adev->dmap_in = &adev->dmaps[1]; - adev->dmap_in->dma = dma2; - } - } - /* Persistent DMA buffers allocated here */ - if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) { - if (adev->dmap_in->raw_buf == NULL) - sound_alloc_dmap(adev->dmap_in); - if (adev->dmap_out->raw_buf == NULL) - sound_alloc_dmap(adev->dmap_out); - } - } -} - -/* No kernel lock - DMAbuf_activate_recording protected by global cli/sti */ -static unsigned int poll_input(struct file * file, int dev, poll_table *wait) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_in; - - if (!(adev->open_mode & OPEN_READ)) - return 0; - if (dmap->mapping_flags & DMA_MAP_MAPPED) { - if (dmap->qlen) - return POLLIN | POLLRDNORM; - return 0; - } - if (dmap->dma_mode != DMODE_INPUT) { - if (dmap->dma_mode == DMODE_NONE && - adev->enable_bits & PCM_ENABLE_INPUT && - !dmap->qlen && adev->go) { - unsigned long flags; - - save_flags(flags); - cli(); - DMAbuf_activate_recording(dev, dmap); - restore_flags(flags); - } - return 0; - } - if (!dmap->qlen) - return 0; - return POLLIN | POLLRDNORM; -} - -static unsigned int poll_output(struct file * file, int dev, poll_table *wait) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_out; - - if (!(adev->open_mode & OPEN_WRITE)) - return 0; - if (dmap->mapping_flags & DMA_MAP_MAPPED) { - if (dmap->qlen) - return POLLOUT | POLLWRNORM; - return 0; - } - if (dmap->dma_mode == DMODE_INPUT) - return 0; - if (dmap->dma_mode == DMODE_NONE) - return POLLOUT | POLLWRNORM; - if (!DMAbuf_space_in_queue(dev)) - return 0; - return POLLOUT | POLLWRNORM; -} - -unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait) -{ - struct audio_operations *adev = audio_devs[dev]; - poll_wait(file, &adev->poll_sleeper, wait); - return poll_input(file, dev, wait) | poll_output(file, dev, wait); -} - -void DMAbuf_deinit(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - /* This routine is called when driver is being unloaded */ - if (!adev) - return; - - /* Persistent DMA buffers deallocated here */ - if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) { - sound_free_dmap(adev->dmap_out); - if (adev->flags & DMA_DUPLEX) - sound_free_dmap(adev->dmap_in); - } -} diff -Nru a/drivers/sound/dmasound/Config.help b/drivers/sound/dmasound/Config.help --- a/drivers/sound/dmasound/Config.help Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,47 +0,0 @@ -CONFIG_DMASOUND - Support built-in audio chips accessible by DMA on various machines - that have them. Note that this symbol does not affect the kernel - directly; rather, it controls whether configuration questions - enabling DMA sound drivers for various specific machine - architectures will be used. - -CONFIG_DMASOUND_ATARI - If you want to use the internal audio of your Atari in Linux, answer - Y to this question. This will provide a Sun-like /dev/audio, - compatible with the Linux/i386 sound system. Otherwise, say N. - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you - want). If you want to compile it as a module, say M here and read - . - -CONFIG_DMASOUND_AWACS - If you want to use the internal audio of your PowerMac in Linux, - answer Y to this question. This will provide a Sun-like /dev/audio, - compatible with the Linux/i386 sound system. Otherwise, say N. - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you - want). If you want to compile it as a module, say M here and read - . - -CONFIG_DMASOUND_PAULA - If you want to use the internal audio of your Amiga in Linux, answer - Y to this question. This will provide a Sun-like /dev/audio, - compatible with the Linux/i386 sound system. Otherwise, say N. - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you - want). If you want to compile it as a module, say M here and read - . - -CONFIG_DMASOUND_Q40 - If you want to use the internal audio of your Q40 in Linux, answer - Y to this question. This will provide a Sun-like /dev/audio, - compatible with the Linux/i386 sound system. Otherwise, say N. - - This driver is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you - want). If you want to compile it as a module, say M here and read - . - diff -Nru a/drivers/sound/dmasound/Config.in b/drivers/sound/dmasound/Config.in --- a/drivers/sound/dmasound/Config.in Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,27 +0,0 @@ -# drivers/sound/dmasound/Config.in - -if [ "$CONFIG_ATARI" = "y" ]; then - dep_tristate ' Atari DMA sound support' CONFIG_DMASOUND_ATARI $CONFIG_SOUND -fi -if [ "$CONFIG_ALL_PPC" = "y" ]; then - dep_tristate ' PowerMac DMA sound support' CONFIG_DMASOUND_AWACS $CONFIG_SOUND -fi -if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_APUS" = "y" ]; then - dep_tristate ' Amiga DMA sound support' CONFIG_DMASOUND_PAULA $CONFIG_SOUND -fi -if [ "$CONFIG_Q40" = "y" ]; then - dep_tristate ' Q40 sound support' CONFIG_DMASOUND_Q40 $CONFIG_SOUND -fi -if [ "$CONFIG_DMASOUND_ATARI" = "y" -o \ - "$CONFIG_DMASOUND_AWACS" = "y" -o \ - "$CONFIG_DMASOUND_PAULA" = "y" -o \ - "$CONFIG_DMASOUND_Q40" = "y" ]; then - define_tristate CONFIG_DMASOUND y -else - if [ "$CONFIG_DMASOUND_ATARI" = "m" -o \ - "$CONFIG_DMASOUND_AWACS" = "m" -o \ - "$CONFIG_DMASOUND_PAULA" = "m" -o \ - "$CONFIG_DMASOUND_Q40" = "m" ]; then - define_tristate CONFIG_DMASOUND m - fi -fi diff -Nru a/drivers/sound/dmasound/Makefile b/drivers/sound/dmasound/Makefile --- a/drivers/sound/dmasound/Makefile Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,21 +0,0 @@ -# -# Makefile for the DMA sound driver -# -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now in the main makefile... - -export-objs := dmasound_core.o - -obj-$(CONFIG_DMASOUND_ATARI) += dmasound_core.o dmasound_atari.o -obj-$(CONFIG_DMASOUND_AWACS) += dmasound_core.o dmasound_awacs.o -obj-$(CONFIG_DMASOUND_PAULA) += dmasound_core.o dmasound_paula.o -obj-$(CONFIG_DMASOUND_Q40) += dmasound_core.o dmasound_q40.o - -ifeq ($(CONFIG_DMASOUND),y) - O_TARGET = dmasound.o -endif - -include $(TOPDIR)/Rules.make diff -Nru a/drivers/sound/dmasound/awacs_defs.h b/drivers/sound/dmasound/awacs_defs.h --- a/drivers/sound/dmasound/awacs_defs.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,229 +0,0 @@ -/*********************************************************/ -/* This file was written by someone, somewhere, sometime */ -/* And is released into the Public Domain */ -/*********************************************************/ - -#ifndef _AWACS_DEFS_H_ -#define _AWACS_DEFS_H_ - -/*******************************/ -/* AWACs Audio Register Layout */ -/*******************************/ - -struct awacs_regs { - unsigned control; /* Audio control register */ - unsigned pad0[3]; - unsigned codec_ctrl; /* Codec control register */ - unsigned pad1[3]; - unsigned codec_stat; /* Codec status register */ - unsigned pad2[3]; - unsigned clip_count; /* Clipping count register */ - unsigned pad3[3]; - unsigned byteswap; /* Data is little-endian if 1 */ -}; - -/*******************/ -/* Audio Bit Masks */ -/*******************/ - -/* Audio Control Reg Bit Masks */ -/* ----- ------- --- --- ----- */ -#define MASK_ISFSEL (0xf) /* Input SubFrame Select */ -#define MASK_OSFSEL (0xf << 4) /* Output SubFrame Select */ -#define MASK_RATE (0x7 << 8) /* Sound Rate */ -#define MASK_CNTLERR (0x1 << 11) /* Error */ -#define MASK_PORTCHG (0x1 << 12) /* Port Change */ -#define MASK_IEE (0x1 << 13) /* Enable Interrupt on Error */ -#define MASK_IEPC (0x1 << 14) /* Enable Interrupt on Port Change */ -#define MASK_SSFSEL (0x3 << 15) /* Status SubFrame Select */ - -/* Audio Codec Control Reg Bit Masks */ -/* ----- ----- ------- --- --- ----- */ -#define MASK_NEWECMD (0x1 << 24) /* Lock: don't write to reg when 1 */ -#define MASK_EMODESEL (0x3 << 22) /* Send info out on which frame? */ -#define MASK_EXMODEADDR (0x3ff << 12) /* Extended Mode Address -- 10 bits */ -#define MASK_EXMODEDATA (0xfff) /* Extended Mode Data -- 12 bits */ - -/* Audio Codec Control Address Values / Masks */ -/* ----- ----- ------- ------- ------ - ----- */ -#define MASK_ADDR0 (0x0 << 12) /* Expanded Data Mode Address 0 */ -#define MASK_ADDR_MUX MASK_ADDR0 /* Mux Control */ -#define MASK_ADDR_GAIN MASK_ADDR0 - -#define MASK_ADDR1 (0x1 << 12) /* Expanded Data Mode Address 1 */ -#define MASK_ADDR_MUTE MASK_ADDR1 -#define MASK_ADDR_RATE MASK_ADDR1 - -#define MASK_ADDR2 (0x2 << 12) /* Expanded Data Mode Address 2 */ -#define MASK_ADDR_VOLA MASK_ADDR2 /* Volume Control A -- Headphones */ -#define MASK_ADDR_VOLHD MASK_ADDR2 - -#define MASK_ADDR4 (0x4 << 12) /* Expanded Data Mode Address 4 */ -#define MASK_ADDR_VOLC MASK_ADDR4 /* Volume Control C -- Speaker */ -#define MASK_ADDR_VOLSPK MASK_ADDR4 - -/* additional registers of screamer */ -#define MASK_ADDR5 (0x5 << 12) /* Expanded Data Mode Address 5 */ -#define MASK_ADDR6 (0x6 << 12) /* Expanded Data Mode Address 6 */ -#define MASK_ADDR7 (0x7 << 12) /* Expanded Data Mode Address 7 */ - -/* Address 0 Bit Masks & Macros */ -/* ------- - --- ----- - ------ */ -#define MASK_GAINRIGHT (0xf) /* Gain Right Mask */ -#define MASK_GAINLEFT (0xf << 4) /* Gain Left Mask */ -#define MASK_GAINLINE (0x1 << 8) /* Change Gain for Line??? */ -#define MASK_GAINMIC (0x0 << 8) /* Change Gain for Mic??? */ - -#define MASK_MUX_CD (0x1 << 9) /* Select CD in MUX */ -#define MASK_MUX_AUDIN (0x1 << 10) /* Select Audio In in MUX */ -#define MASK_MUX_MIC (0x1 << 11) /* Select Mic in MUX */ -#define MASK_MUX_LINE MASK_MUX_AUDIN - -#define GAINRIGHT(x) ((x) & MASK_GAINRIGHT) -#define GAINLEFT(x) (((x) << 4) & MASK_GAINLEFT) - -/* Address 1 Bit Masks */ -/* ------- - --- ----- */ -#define MASK_ADDR1RES1 (0x3) /* Reserved */ -#define MASK_RECALIBRATE (0x1 << 2) /* Recalibrate */ -#define MASK_SAMPLERATE (0x7 << 3) /* Sample Rate: */ -#define MASK_LOOPTHRU (0x1 << 6) /* Loopthrough Enable */ -#define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */ -#define MASK_SPKMUTE MASK_CMUTE -#define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */ -#define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */ -#define MASK_HDMUTE MASK_AMUTE -#define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */ - -#define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */ -#define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */ -#define SAMPLERATE_24000 (0x2 << 3) /* 24 or 22.05 kHz */ -#define SAMPLERATE_19200 (0x3 << 3) /* 19.2 or 17.64 kHz */ -#define SAMPLERATE_16000 (0x4 << 3) /* 16 or 14.7 kHz */ -#define SAMPLERATE_12000 (0x5 << 3) /* 12 or 11.025 kHz */ -#define SAMPLERATE_9600 (0x6 << 3) /* 9.6 or 8.82 kHz */ -#define SAMPLERATE_8000 (0x7 << 3) /* 8 or 7.35 kHz */ - -/* Address 2 & 4 Bit Masks & Macros */ -/* ------- - - - --- ----- - ------ */ -#define MASK_OUTVOLRIGHT (0xf) /* Output Right Volume */ -#define MASK_ADDR2RES1 (0x2 << 4) /* Reserved */ -#define MASK_ADDR4RES1 MASK_ADDR2RES1 -#define MASK_OUTVOLLEFT (0xf << 6) /* Output Left Volume */ -#define MASK_ADDR2RES2 (0x2 << 10) /* Reserved */ -#define MASK_ADDR4RES2 MASK_ADDR2RES2 - -#define VOLRIGHT(x) (((~(x)) & MASK_OUTVOLRIGHT)) -#define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT) - -/* Audio Codec Status Reg Bit Masks */ -/* ----- ----- ------ --- --- ----- */ -#define MASK_EXTEND (0x1 << 23) /* Extend */ -#define MASK_VALID (0x1 << 22) /* Valid Data? */ -#define MASK_OFLEFT (0x1 << 21) /* Overflow Left */ -#define MASK_OFRIGHT (0x1 << 20) /* Overflow Right */ -#define MASK_ERRCODE (0xf << 16) /* Error Code */ -#define MASK_REVISION (0xf << 12) /* Revision Number */ -#define MASK_MFGID (0xf << 8) /* Mfg. ID */ -#define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */ -#define MASK_INPPORT (0xf) /* Input Port */ -#define MASK_HDPCONN 8 /* headphone plugged in */ - -/* Clipping Count Reg Bit Masks */ -/* -------- ----- --- --- ----- */ -#define MASK_CLIPLEFT (0xff << 7) /* Clipping Count, Left Channel */ -#define MASK_CLIPRIGHT (0xff) /* Clipping Count, Right Channel */ - -/* DBDMA ChannelStatus Bit Masks */ -/* ----- ------------- --- ----- */ -#define MASK_CSERR (0x1 << 7) /* Error */ -#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */ -#define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */ -#define MASK_WAIT (0x1) /* Wait */ - -/* Various Rates */ -/* ------- ----- */ -#define RATE_48000 (0x0 << 8) /* 48 kHz */ -#define RATE_44100 (0x0 << 8) /* 44.1 kHz */ -#define RATE_32000 (0x1 << 8) /* 32 kHz */ -#define RATE_29400 (0x1 << 8) /* 29.4 kHz */ -#define RATE_24000 (0x2 << 8) /* 24 kHz */ -#define RATE_22050 (0x2 << 8) /* 22.05 kHz */ -#define RATE_19200 (0x3 << 8) /* 19.2 kHz */ -#define RATE_17640 (0x3 << 8) /* 17.64 kHz */ -#define RATE_16000 (0x4 << 8) /* 16 kHz */ -#define RATE_14700 (0x4 << 8) /* 14.7 kHz */ -#define RATE_12000 (0x5 << 8) /* 12 kHz */ -#define RATE_11025 (0x5 << 8) /* 11.025 kHz */ -#define RATE_9600 (0x6 << 8) /* 9.6 kHz */ -#define RATE_8820 (0x6 << 8) /* 8.82 kHz */ -#define RATE_8000 (0x7 << 8) /* 8 kHz */ -#define RATE_7350 (0x7 << 8) /* 7.35 kHz */ - -#define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */ - - -/* Burgundy values */ - -#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12) -#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12) - -#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12) -#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12) -#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12) -#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12) - -#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12) -#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12) -#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12) -#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12) - -#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12) -#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12) - -#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12) - -#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12) - -#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12) -#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12) -#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12) - -#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1) -#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2) -#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3) -#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4) - -#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1) -#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2) -#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3) -#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4) - - -/* These are all default values for the burgundy */ -#define DEF_BURGUNDY_INPSEL21 (0xAA) -#define DEF_BURGUNDY_INPSEL3 (0x0A) - -#define DEF_BURGUNDY_GAINCD (0x33) -#define DEF_BURGUNDY_GAINLINE (0x44) -#define DEF_BURGUNDY_GAINMIC (0x44) -#define DEF_BURGUNDY_GAINMODEM (0x06) - -/* Remember: lowest volume here is 0x9b */ -#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC) -#define DEF_BURGUNDY_VOLLINE (0x00000000) -#define DEF_BURGUNDY_VOLMIC (0x00000000) -#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC) - -#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f) -#define DEF_BURGUNDY_OUTPUTENABLES (0x0A) - -#define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) - -#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E) - -#define DEF_BURGUNDY_ATTENSPEAKER (0x44) -#define DEF_BURGUNDY_ATTENLINEOUT (0xCC) -#define DEF_BURGUNDY_ATTENHP (0xCC) - -#endif /* _AWACS_DEFS_H_ */ diff -Nru a/drivers/sound/dmasound/dmasound.h b/drivers/sound/dmasound/dmasound.h --- a/drivers/sound/dmasound/dmasound.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,248 +0,0 @@ - -/* - * linux/drivers/sound/dmasound/dmasound.h - * - * - * Minor numbers for the sound driver. - * - * Unfortunately Creative called the codec chip of SB as a DSP. For this - * reason the /dev/dsp is reserved for digitized audio use. There is a - * device for true DSP processors but it will be called something else. - * In v3.0 it's /dev/sndproc but this could be a temporary solution. - */ - - -#include - - -#define SND_NDEVS 256 /* Number of supported devices */ -#define SND_DEV_CTL 0 /* Control port /dev/mixer */ -#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM - synthesizer and MIDI output) */ -#define SND_DEV_MIDIN 2 /* Raw midi access */ -#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ -#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ -#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ -#define SND_DEV_STATUS 6 /* /dev/sndstat */ -/* #7 not in use now. Was in 2.4. Free for use after v3.0. */ -#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ -#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ -#define SND_DEV_PSS SND_DEV_SNDPROC - -#define DSP_DEFAULT_SPEED 8000 - -#define ON 1 -#define OFF 0 - -#define MAX_AUDIO_DEV 5 -#define MAX_MIXER_DEV 2 -#define MAX_SYNTH_DEV 3 -#define MAX_MIDI_DEV 6 -#define MAX_TIMER_DEV 3 - - -#define MAX_CATCH_RADIUS 10 -#define MIN_BUFFERS 4 -#define MIN_BUFSIZE 4 /* in KB */ -#define MAX_BUFSIZE 128 /* Limit for Amiga in KB */ - - -#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) -#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) - -#define IOCTL_IN(arg, ret) \ - do { int error = get_user(ret, (int *)(arg)); \ - if (error) return error; \ - } while (0) -#define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) - -static inline int ioctl_return(int *addr, int value) -{ - return value < 0 ? value : put_user(value, addr); -} - - - /* - * Configuration - */ - -#undef HAS_8BIT_TABLES -#undef HAS_14BIT_TABLES -#undef HAS_16BIT_TABLES -#undef HAS_RECORD - -#if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\ - defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\ - defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE) -#define HAS_8BIT_TABLES -#endif -#if defined(CONFIG_DMASOUND_AWACS) || defined(CONFIG_DMASOUND_AWACS_MODULE) -#define HAS_16BIT_TABLES -#define HAS_RECORD -#endif - - - /* - * Initialization - */ - -extern int dmasound_init(void); -#ifdef MODULE -extern void dmasound_deinit(void); -#else -#define dmasound_deinit() do { } while (0) -#endif - - - /* - * Machine definitions - */ - -typedef struct { - const char *name; - const char *name2; - void (*open)(void); - void (*release)(void); - void *(*dma_alloc)(unsigned int, int); - void (*dma_free)(void *, unsigned int); - int (*irqinit)(void); -#ifdef MODULE - void (*irqcleanup)(void); -#endif - void (*init)(void); - void (*silence)(void); - int (*setFormat)(int); - int (*setVolume)(int); - int (*setBass)(int); - int (*setTreble)(int); - int (*setGain)(int); - void (*play)(void); - void (*record)(void); /* optional */ - void (*mixer_init)(void); /* optional */ - int (*mixer_ioctl)(u_int, u_long); /* optional */ - void (*write_sq_setup)(void); /* optional */ - void (*read_sq_setup)(void); /* optional */ - void (*sq_open)(void); /* optional */ - int (*state_info)(char *); /* optional */ - void (*abort_read)(void); /* optional */ - int min_dsp_speed; -} MACHINE; - - - /* - * Low level stuff - */ - -typedef struct { - int format; /* AFMT_* */ - int stereo; /* 0 = mono, 1 = stereo */ - int size; /* 8/16 bit*/ - int speed; /* speed */ -} SETTINGS; - -typedef struct { - ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); -} TRANS; - -struct sound_settings { - MACHINE mach; /* machine dependent things */ - SETTINGS hard; /* hardware settings */ - SETTINGS soft; /* software settings */ - SETTINGS dsp; /* /dev/dsp default settings */ - TRANS *trans_write; /* supported translations */ -#ifdef HAS_RECORD - TRANS *trans_read; /* supported translations */ -#endif - int volume_left; /* volume (range is machine dependent) */ - int volume_right; - int bass; /* tone (range is machine dependent) */ - int treble; - int gain; - int minDev; /* minor device number currently open */ -}; - -extern struct sound_settings dmasound; - -extern char dmasound_ulaw2dma8[]; -extern char dmasound_alaw2dma8[]; -extern short dmasound_ulaw2dma16[]; -extern short dmasound_alaw2dma16[]; - - - /* - * Mid level stuff - */ - -static inline int dmasound_set_volume(int volume) -{ - return dmasound.mach.setVolume(volume); -} - -static inline int dmasound_set_bass(int bass) -{ - return dmasound.mach.setBass ? dmasound.mach.setBass(bass) : 50; -} - -static inline int dmasound_set_treble(int treble) -{ - return dmasound.mach.setTreble ? dmasound.mach.setTreble(treble) : 50; -} - -static inline int dmasound_set_gain(int gain) -{ - return dmasound.mach.setGain ? dmasound.mach.setGain(gain) : 100; -} - - - /* - * Sound queue stuff, the heart of the driver - */ - -struct sound_queue { - /* buffers allocated for this queue */ - int numBufs; - int bufSize; /* in bytes */ - char **buffers; - - /* current parameters */ - int max_count; - int block_size; /* in bytes */ - int max_active; - - /* it shouldn't be necessary to declare any of these volatile */ - int front, rear, count; - int rear_size; - /* - * The use of the playing field depends on the hardware - * - * Atari, PMac: The number of frames that are loaded/playing - * - * Amiga: Bit 0 is set: a frame is loaded - * Bit 1 is set: a frame is playing - */ - int active; - wait_queue_head_t action_queue, open_queue, sync_queue; - int open_mode; - int busy, syncing; -}; - -#define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ) -#define WAKE_UP(queue) (wake_up_interruptible(&queue)) - -extern struct sound_queue dmasound_write_sq; -extern struct sound_queue dmasound_read_sq; - -#define write_sq dmasound_write_sq -#define read_sq dmasound_read_sq - -extern int dmasound_catchRadius; - -#define catchRadius dmasound_catchRadius - diff -Nru a/drivers/sound/dmasound/dmasound_atari.c b/drivers/sound/dmasound/dmasound_atari.c --- a/drivers/sound/dmasound/dmasound_atari.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1562 +0,0 @@ - -/* - * linux/drivers/sound/dmasound/dmasound_atari.c - * - * Atari TT and Falcon DMA Sound Driver - * - * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits - */ - - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "dmasound.h" - - -extern void atari_microwire_cmd(int cmd); - - -static int is_falcon; -static int write_sq_ignore_int = 0; /* ++TeSche: used for Falcon */ - -static int expand_bal; /* Balance factor for expanding (not volume!) */ -static int expand_data; /* Data for expanding */ - - -/*** Translations ************************************************************/ - - -/* ++TeSche: radically changed for new expanding purposes... - * - * These two routines now deal with copying/expanding/translating the samples - * from user space into our buffer at the right frequency. They take care about - * how much data there's actually to read, how much buffer space there is and - * to convert samples into the right frequency/encoding. They will only work on - * complete samples so it may happen they leave some bytes in the input stream - * if the user didn't write a multiple of the current sample size. They both - * return the number of bytes they've used from both streams so you may detect - * such a situation. Luckily all programs should be able to cope with that. - * - * I think I've optimized anything as far as one can do in plain C, all - * variables should fit in registers and the loops are really short. There's - * one loop for every possible situation. Writing a more generalized and thus - * parameterized loop would only produce slower code. Feel free to optimize - * this in assembler if you like. :) - * - * I think these routines belong here because they're not yet really hardware - * independent, especially the fact that the Falcon can play 16bit samples - * only in stereo is hardcoded in both of them! - * - * ++geert: split in even more functions (one per format) - */ - -static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); - - -/*** Low level stuff *********************************************************/ - - -static void AtaOpen(void); -static void AtaRelease(void); -static void *AtaAlloc(unsigned int size, int flags); -static void AtaFree(void *, unsigned int size); -static int AtaIrqInit(void); -#ifdef MODULE -static void AtaIrqCleanUp(void); -#endif /* MODULE */ -static int AtaSetBass(int bass); -static int AtaSetTreble(int treble); -static void TTSilence(void); -static void TTInit(void); -static int TTSetFormat(int format); -static int TTSetVolume(int volume); -static int TTSetGain(int gain); -static void FalconSilence(void); -static void FalconInit(void); -static int FalconSetFormat(int format); -static int FalconSetVolume(int volume); -static void AtaPlayNextFrame(int index); -static void AtaPlay(void); -static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp); - -/*** Mid level stuff *********************************************************/ - -static void TTMixerInit(void); -static void FalconMixerInit(void); -static int AtaMixerIoctl(u_int cmd, u_long arg); -static int TTMixerIoctl(u_int cmd, u_long arg); -static int FalconMixerIoctl(u_int cmd, u_long arg); -static void AtaWriteSqSetup(void); -static void AtaSqOpen(void); -static int TTStateInfo(char *buffer); -static int FalconStateInfo(char *buffer); - - -/*** Translations ************************************************************/ - - -static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8 - : dmasound_alaw2dma8; - ssize_t count, used; - u_char *p = &frame[*frameUsed]; - - count = min_t(unsigned long, userCount, frameLeft); - if (dmasound.soft.stereo) - count &= ~1; - used = count; - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - *p++ = table[data]; - count--; - } - *frameUsed += used; - return used; -} - - -static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - void *p = &frame[*frameUsed]; - - count = min_t(unsigned long, userCount, frameLeft); - if (dmasound.soft.stereo) - count &= ~1; - used = count; - if (copy_from_user(p, userPtr, count)) - return -EFAULT; - *frameUsed += used; - return used; -} - - -static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - - if (!dmasound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft); - used = count; - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - *p++ = data ^ 0x80; - count--; - } - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - *p++ = data ^ 0x8080; - count--; - } - } - *frameUsed += used; - return used; -} - - -static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - - if (!dmasound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - *p++ = data; - *p++ = data; - count--; - } - *frameUsed += used*2; - } else { - void *p = (u_short *)&frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft) & ~3; - used = count; - if (copy_from_user(p, userPtr, count)) - return -EFAULT; - *frameUsed += used; - } - return used; -} - - -static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - - if (!dmasound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data ^= 0x8000; - *p++ = data; - *p++ = data; - count--; - } - *frameUsed += used*2; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft)>>2; - used = count*4; - while (count > 0) { - u_long data; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - *p++ = data ^ 0x80008000; - count--; - } - *frameUsed += used; - } - return used; -} - - -static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - - count = frameLeft; - if (!dmasound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data); - *p++ = data; - *p++ = data; - count--; - } - *frameUsed += used*2; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft)>>2; - used = count*4; - while (count > 0) { - u_long data; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - data = le2be16dbl(data); - *p++ = data; - count--; - } - *frameUsed += used; - } - return used; -} - - -static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - - count = frameLeft; - if (!dmasound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft)>>1; - used = count*2; - while (count > 0) { - u_short data; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data) ^ 0x8000; - *p++ = data; - *p++ = data; - } - *frameUsed += used*2; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft)>>2; - used = count; - while (count > 0) { - u_long data; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - data = le2be16dbl(data) ^ 0x80008000; - *p++ = data; - count--; - } - *frameUsed += used; - } - return used; -} - - -static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8 - : dmasound_alaw2dma8; - /* this should help gcc to stuff everything into registers */ - long bal = expand_bal; - long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!dmasound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - u_char data = expand_data; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (!userCount) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = table[c]; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_data = data; - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = expand_data; - while (frameLeft >= 2) { - u_char c; - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = table[c] << 8; - if (get_user(c, userPtr++)) - return -EFAULT; - data |= table[c]; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 2; - bal -= sSpeed; - } - expand_data = data; - } - expand_bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return used; -} - - -static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = expand_bal; - long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!dmasound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - u_char data = expand_data; - while (frameLeft) { - if (bal < 0) { - if (!userCount) - break; - if (get_user(data, userPtr++)) - return -EFAULT; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_data = data; - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = expand_data; - while (frameLeft >= 2) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 2; - bal -= sSpeed; - } - expand_data = data; - } - expand_bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return used; -} - - -static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = expand_bal; - long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!dmasound.soft.stereo) { - u_char *p = &frame[*frameUsed]; - u_char data = expand_data; - while (frameLeft) { - if (bal < 0) { - if (!userCount) - break; - if (get_user(data, userPtr++)) - return -EFAULT; - data ^= 0x80; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_data = data; - } else { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = expand_data; - while (frameLeft >= 2) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data ^= 0x8080; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 2; - bal -= sSpeed; - } - expand_data = data; - } - expand_bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return used; -} - - -static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = expand_bal; - long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!dmasound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = expand_data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - expand_data = data; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - u_long data = expand_data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - expand_data = data; - } - expand_bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return used; -} - - -static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = expand_bal; - long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!dmasound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = expand_data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data ^= 0x8000; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - expand_data = data; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - u_long data = expand_data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - data ^= 0x80008000; - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - expand_data = data; - } - expand_bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return used; -} - - -static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = expand_bal; - long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!dmasound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = expand_data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data); - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - expand_data = data; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - u_long data = expand_data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - data = le2be16dbl(data); - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - expand_data = data; - } - expand_bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return used; -} - - -static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - /* this should help gcc to stuff everything into registers */ - long bal = expand_bal; - long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - ssize_t used, usedf; - - used = userCount; - usedf = frameLeft; - if (!dmasound.soft.stereo) { - u_short *p = (u_short *)&frame[*frameUsed]; - u_short data = expand_data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 2) - break; - if (get_user(data, ((u_short *)userPtr)++)) - return -EFAULT; - data = le2be16(data) ^ 0x8000; - userCount -= 2; - bal += hSpeed; - } - *p++ = data; - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - expand_data = data; - } else { - u_long *p = (u_long *)&frame[*frameUsed]; - u_long data = expand_data; - while (frameLeft >= 4) { - if (bal < 0) { - if (userCount < 4) - break; - if (get_user(data, ((u_int *)userPtr)++)) - return -EFAULT; - data = le2be16dbl(data) ^ 0x80008000; - userCount -= 4; - bal += hSpeed; - } - *p++ = data; - frameLeft -= 4; - bal -= sSpeed; - } - expand_data = data; - } - expand_bal = bal; - used -= userCount; - *frameUsed += usedf-frameLeft; - return used; -} - - -static TRANS transTTNormal = { - ct_ulaw: ata_ct_law, - ct_alaw: ata_ct_law, - ct_s8: ata_ct_s8, - ct_u8: ata_ct_u8, -}; - -static TRANS transTTExpanding = { - ct_ulaw: ata_ctx_law, - ct_alaw: ata_ctx_law, - ct_s8: ata_ctx_s8, - ct_u8: ata_ctx_u8, -}; - -static TRANS transFalconNormal = { - ct_ulaw: ata_ct_law, - ct_alaw: ata_ct_law, - ct_s8: ata_ct_s8, - ct_u8: ata_ct_u8, - ct_s16be: ata_ct_s16be, - ct_u16be: ata_ct_u16be, - ct_s16le: ata_ct_s16le, - ct_u16le: ata_ct_u16le -}; - -static TRANS transFalconExpanding = { - ct_ulaw: ata_ctx_law, - ct_alaw: ata_ctx_law, - ct_s8: ata_ctx_s8, - ct_u8: ata_ctx_u8, - ct_s16be: ata_ctx_s16be, - ct_u16be: ata_ctx_u16be, - ct_s16le: ata_ctx_s16le, - ct_u16le: ata_ctx_u16le, -}; - - -/*** Low level stuff *********************************************************/ - - - -/* - * Atari (TT/Falcon) - */ - -static void AtaOpen(void) -{ - MOD_INC_USE_COUNT; -} - -static void AtaRelease(void) -{ - MOD_DEC_USE_COUNT; -} - -static void *AtaAlloc(unsigned int size, int flags) -{ - return atari_stram_alloc(size, "dmasound"); -} - -static void AtaFree(void *obj, unsigned int size) -{ - atari_stram_free( obj ); -} - -static int __init AtaIrqInit(void) -{ - /* Set up timer A. Timer A - will receive a signal upon end of playing from the sound - hardware. Furthermore Timer A is able to count events - and will cause an interrupt after a programmed number - of events. So all we need to keep the music playing is - to provide the sound hardware with new data upon - an interrupt from timer A. */ - mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */ - mfp.tim_dt_a = 1; /* Cause interrupt after first event. */ - mfp.tim_ct_a = 8; /* Turn on event counting. */ - /* Register interrupt handler. */ - request_irq(IRQ_MFP_TIMA, AtaInterrupt, IRQ_TYPE_SLOW, "DMA sound", - AtaInterrupt); - mfp.int_en_a |= 0x20; /* Turn interrupt on. */ - mfp.int_mk_a |= 0x20; - return 1; -} - -#ifdef MODULE -static void AtaIrqCleanUp(void) -{ - mfp.tim_ct_a = 0; /* stop timer */ - mfp.int_en_a &= ~0x20; /* turn interrupt off */ - free_irq(IRQ_MFP_TIMA, AtaInterrupt); -} -#endif /* MODULE */ - - -#define TONE_VOXWARE_TO_DB(v) \ - (((v) < 0) ? -12 : ((v) > 100) ? 12 : ((v) - 50) * 6 / 25) -#define TONE_DB_TO_VOXWARE(v) (((v) * 25 + ((v) > 0 ? 5 : -5)) / 6 + 50) - - -static int AtaSetBass(int bass) -{ - dmasound.bass = TONE_VOXWARE_TO_DB(bass); - atari_microwire_cmd(MW_LM1992_BASS(dmasound.bass)); - return TONE_DB_TO_VOXWARE(dmasound.bass); -} - - -static int AtaSetTreble(int treble) -{ - dmasound.treble = TONE_VOXWARE_TO_DB(treble); - atari_microwire_cmd(MW_LM1992_TREBLE(dmasound.treble)); - return TONE_DB_TO_VOXWARE(dmasound.treble); -} - - - -/* - * TT - */ - - -static void TTSilence(void) -{ - tt_dmasnd.ctrl = DMASND_CTRL_OFF; - atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */ -} - - -static void TTInit(void) -{ - int mode, i, idx; - const int freq[4] = {50066, 25033, 12517, 6258}; - - /* search a frequency that fits into the allowed error range */ - - idx = -1; - for (i = 0; i < ARRAY_SIZE(freq); i++) - /* this isn't as much useful for a TT than for a Falcon, but - * then it doesn't hurt very much to implement it for a TT too. - */ - if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius) - idx = i; - if (idx > -1) { - dmasound.soft.speed = freq[idx]; - dmasound.trans_write = &transTTNormal; - } else - dmasound.trans_write = &transTTExpanding; - - TTSilence(); - dmasound.hard = dmasound.soft; - - if (dmasound.hard.speed > 50066) { - /* we would need to squeeze the sound, but we won't do that */ - dmasound.hard.speed = 50066; - mode = DMASND_MODE_50KHZ; - dmasound.trans_write = &transTTNormal; - } else if (dmasound.hard.speed > 25033) { - dmasound.hard.speed = 50066; - mode = DMASND_MODE_50KHZ; - } else if (dmasound.hard.speed > 12517) { - dmasound.hard.speed = 25033; - mode = DMASND_MODE_25KHZ; - } else if (dmasound.hard.speed > 6258) { - dmasound.hard.speed = 12517; - mode = DMASND_MODE_12KHZ; - } else { - dmasound.hard.speed = 6258; - mode = DMASND_MODE_6KHZ; - } - - tt_dmasnd.mode = (dmasound.hard.stereo ? - DMASND_MODE_STEREO : DMASND_MODE_MONO) | - DMASND_MODE_8BIT | mode; - - expand_bal = -dmasound.soft.speed; -} - - -static int TTSetFormat(int format) -{ - /* TT sound DMA supports only 8bit modes */ - - switch (format) { - case AFMT_QUERY: - return dmasound.soft.format; - case AFMT_MU_LAW: - case AFMT_A_LAW: - case AFMT_S8: - case AFMT_U8: - break; - default: - format = AFMT_S8; - } - - dmasound.soft.format = format; - dmasound.soft.size = 8; - if (dmasound.minDev == SND_DEV_DSP) { - dmasound.dsp.format = format; - dmasound.dsp.size = 8; - } - TTInit(); - - return format; -} - - -#define VOLUME_VOXWARE_TO_DB(v) \ - (((v) < 0) ? -40 : ((v) > 100) ? 0 : ((v) * 2) / 5 - 40) -#define VOLUME_DB_TO_VOXWARE(v) ((((v) + 40) * 5 + 1) / 2) - - -static int TTSetVolume(int volume) -{ - dmasound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff); - atari_microwire_cmd(MW_LM1992_BALLEFT(dmasound.volume_left)); - dmasound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8); - atari_microwire_cmd(MW_LM1992_BALRIGHT(dmasound.volume_right)); - return VOLUME_DB_TO_VOXWARE(dmasound.volume_left) | - (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8); -} - - -#define GAIN_VOXWARE_TO_DB(v) \ - (((v) < 0) ? -80 : ((v) > 100) ? 0 : ((v) * 4) / 5 - 80) -#define GAIN_DB_TO_VOXWARE(v) ((((v) + 80) * 5 + 1) / 4) - -static int TTSetGain(int gain) -{ - dmasound.gain = GAIN_VOXWARE_TO_DB(gain); - atari_microwire_cmd(MW_LM1992_VOLUME(dmasound.gain)); - return GAIN_DB_TO_VOXWARE(dmasound.gain); -} - - - -/* - * Falcon - */ - - -static void FalconSilence(void) -{ - /* stop playback, set sample rate 50kHz for PSG sound */ - tt_dmasnd.ctrl = DMASND_CTRL_OFF; - tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT; - tt_dmasnd.int_div = 0; /* STE compatible divider */ - tt_dmasnd.int_ctrl = 0x0; - tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */ - tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */ - tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */ - tt_dmasnd.adc_src = 3; /* ADC Input = PSG */ -} - - -static void FalconInit(void) -{ - int divider, i, idx; - const int freq[8] = {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195}; - - /* search a frequency that fits into the allowed error range */ - - idx = -1; - for (i = 0; i < ARRAY_SIZE(freq); i++) - /* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would - * be playable without expanding, but that now a kernel runtime - * option - */ - if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius) - idx = i; - if (idx > -1) { - dmasound.soft.speed = freq[idx]; - dmasound.trans_write = &transFalconNormal; - } else - dmasound.trans_write = &transFalconExpanding; - - FalconSilence(); - dmasound.hard = dmasound.soft; - - if (dmasound.hard.size == 16) { - /* the Falcon can play 16bit samples only in stereo */ - dmasound.hard.stereo = 1; - } - - if (dmasound.hard.speed > 49170) { - /* we would need to squeeze the sound, but we won't do that */ - dmasound.hard.speed = 49170; - divider = 1; - dmasound.trans_write = &transFalconNormal; - } else if (dmasound.hard.speed > 32780) { - dmasound.hard.speed = 49170; - divider = 1; - } else if (dmasound.hard.speed > 24585) { - dmasound.hard.speed = 32780; - divider = 2; - } else if (dmasound.hard.speed > 19668) { - dmasound.hard.speed = 24585; - divider = 3; - } else if (dmasound.hard.speed > 16390) { - dmasound.hard.speed = 19668; - divider = 4; - } else if (dmasound.hard.speed > 12292) { - dmasound.hard.speed = 16390; - divider = 5; - } else if (dmasound.hard.speed > 9834) { - dmasound.hard.speed = 12292; - divider = 7; - } else if (dmasound.hard.speed > 8195) { - dmasound.hard.speed = 9834; - divider = 9; - } else { - dmasound.hard.speed = 8195; - divider = 11; - } - tt_dmasnd.int_div = divider; - - /* Setup Falcon sound DMA for playback */ - tt_dmasnd.int_ctrl = 0x4; /* Timer A int at play end */ - tt_dmasnd.track_select = 0x0; /* play 1 track, track 1 */ - tt_dmasnd.cbar_src = 0x0001; /* DMA(25MHz) --> DAC */ - tt_dmasnd.cbar_dst = 0x0000; - tt_dmasnd.rec_track_select = 0; - tt_dmasnd.dac_src = 2; /* connect matrix to DAC */ - tt_dmasnd.adc_src = 0; /* ADC Input = Mic */ - - tt_dmasnd.mode = (dmasound.hard.stereo ? - DMASND_MODE_STEREO : DMASND_MODE_MONO) | - ((dmasound.hard.size == 8) ? - DMASND_MODE_8BIT : DMASND_MODE_16BIT) | - DMASND_MODE_6KHZ; - - expand_bal = -dmasound.soft.speed; -} - - -static int FalconSetFormat(int format) -{ - int size; - /* Falcon sound DMA supports 8bit and 16bit modes */ - - switch (format) { - case AFMT_QUERY: - return dmasound.soft.format; - case AFMT_MU_LAW: - case AFMT_A_LAW: - case AFMT_U8: - case AFMT_S8: - size = 8; - break; - case AFMT_S16_BE: - case AFMT_U16_BE: - case AFMT_S16_LE: - case AFMT_U16_LE: - size = 16; - break; - default: /* :-) */ - size = 8; - format = AFMT_S8; - } - - dmasound.soft.format = format; - dmasound.soft.size = size; - if (dmasound.minDev == SND_DEV_DSP) { - dmasound.dsp.format = format; - dmasound.dsp.size = dmasound.soft.size; - } - - FalconInit(); - - return format; -} - - -/* This is for the Falcon output *attenuation* in 1.5dB steps, - * i.e. output level from 0 to -22.5dB in -1.5dB steps. - */ -#define VOLUME_VOXWARE_TO_ATT(v) \ - ((v) < 0 ? 15 : (v) > 100 ? 0 : 15 - (v) * 3 / 20) -#define VOLUME_ATT_TO_VOXWARE(v) (100 - (v) * 20 / 3) - - -static int FalconSetVolume(int volume) -{ - dmasound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff); - dmasound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8); - tt_dmasnd.output_atten = dmasound.volume_left << 8 | dmasound.volume_right << 4; - return VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) | - VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8; -} - - -static void AtaPlayNextFrame(int index) -{ - char *start, *end; - - /* used by AtaPlay() if all doubts whether there really is something - * to be played are already wiped out. - */ - start = write_sq.buffers[write_sq.front]; - end = start+((write_sq.count == index) ? write_sq.rear_size - : write_sq.block_size); - /* end might not be a legal virtual address. */ - DMASNDSetEnd(virt_to_phys(end - 1) + 1); - DMASNDSetBase(virt_to_phys(start)); - /* Since only an even number of samples per frame can - be played, we might lose one byte here. (TO DO) */ - write_sq.front = (write_sq.front+1) % write_sq.max_count; - write_sq.active++; - tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT; -} - - -static void AtaPlay(void) -{ - /* ++TeSche: Note that write_sq.active is no longer just a flag but - * holds the number of frames the DMA is currently programmed for - * instead, may be 0, 1 (currently being played) or 2 (pre-programmed). - * - * Changes done to write_sq.count and write_sq.active are a bit more - * subtle again so now I must admit I also prefer disabling the irq - * here rather than considering all possible situations. But the point - * is that disabling the irq doesn't have any bad influence on this - * version of the driver as we benefit from having pre-programmed the - * DMA wherever possible: There's no need to reload the DMA at the - * exact time of an interrupt but only at some time while the - * pre-programmed frame is playing! - */ - atari_disable_irq(IRQ_MFP_TIMA); - - if (write_sq.active == 2 || /* DMA is 'full' */ - write_sq.count <= 0) { /* nothing to do */ - atari_enable_irq(IRQ_MFP_TIMA); - return; - } - - if (write_sq.active == 0) { - /* looks like there's nothing 'in' the DMA yet, so try - * to put two frames into it (at least one is available). - */ - if (write_sq.count == 1 && - write_sq.rear_size < write_sq.block_size && - !write_sq.syncing) { - /* hmmm, the only existing frame is not - * yet filled and we're not syncing? - */ - atari_enable_irq(IRQ_MFP_TIMA); - return; - } - AtaPlayNextFrame(1); - if (write_sq.count == 1) { - /* no more frames */ - atari_enable_irq(IRQ_MFP_TIMA); - return; - } - if (write_sq.count == 2 && - write_sq.rear_size < write_sq.block_size && - !write_sq.syncing) { - /* hmmm, there were two frames, but the second - * one is not yet filled and we're not syncing? - */ - atari_enable_irq(IRQ_MFP_TIMA); - return; - } - AtaPlayNextFrame(2); - } else { - /* there's already a frame being played so we may only stuff - * one new into the DMA, but even if this may be the last - * frame existing the previous one is still on write_sq.count. - */ - if (write_sq.count == 2 && - write_sq.rear_size < write_sq.block_size && - !write_sq.syncing) { - /* hmmm, the only existing frame is not - * yet filled and we're not syncing? - */ - atari_enable_irq(IRQ_MFP_TIMA); - return; - } - AtaPlayNextFrame(2); - } - atari_enable_irq(IRQ_MFP_TIMA); -} - - -static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp) -{ -#if 0 - /* ++TeSche: if you should want to test this... */ - static int cnt = 0; - if (write_sq.active == 2) - if (++cnt == 10) { - /* simulate losing an interrupt */ - cnt = 0; - return; - } -#endif - - if (write_sq_ignore_int && is_falcon) { - /* ++TeSche: Falcon only: ignore first irq because it comes - * immediately after starting a frame. after that, irqs come - * (almost) like on the TT. - */ - write_sq_ignore_int = 0; - return; - } - - if (!write_sq.active) { - /* playing was interrupted and sq_reset() has already cleared - * the sq variables, so better don't do anything here. - */ - WAKE_UP(write_sq.sync_queue); - return; - } - - /* Probably ;) one frame is finished. Well, in fact it may be that a - * pre-programmed one is also finished because there has been a long - * delay in interrupt delivery and we've completely lost one, but - * there's no way to detect such a situation. In such a case the last - * frame will be played more than once and the situation will recover - * as soon as the irq gets through. - */ - write_sq.count--; - write_sq.active--; - - if (!write_sq.active) { - tt_dmasnd.ctrl = DMASND_CTRL_OFF; - write_sq_ignore_int = 1; - } - - WAKE_UP(write_sq.action_queue); - /* At least one block of the queue is free now - so wake up a writing process blocked because - of a full queue. */ - - if ((write_sq.active != 1) || (write_sq.count != 1)) - /* We must be a bit carefully here: write_sq.count indicates the - * number of buffers used and not the number of frames to be - * played. If write_sq.count==1 and write_sq.active==1 that - * means the only remaining frame was already programmed - * earlier (and is currently running) so we mustn't call - * AtaPlay() here, otherwise we'll play one frame too much. - */ - AtaPlay(); - - if (!write_sq.active) WAKE_UP(write_sq.sync_queue); - /* We are not playing after AtaPlay(), so there - is nothing to play any more. Wake up a process - waiting for audio output to drain. */ -} - - -/*** Mid level stuff *********************************************************/ - - -/* - * /dev/mixer abstraction - */ - -#define RECLEVEL_VOXWARE_TO_GAIN(v) \ - ((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20) -#define RECLEVEL_GAIN_TO_VOXWARE(v) (((v) * 20 + 2) / 3) - - -static void __init TTMixerInit(void) -{ - atari_microwire_cmd(MW_LM1992_VOLUME(0)); - dmasound.volume_left = 0; - atari_microwire_cmd(MW_LM1992_BALLEFT(0)); - dmasound.volume_right = 0; - atari_microwire_cmd(MW_LM1992_BALRIGHT(0)); - atari_microwire_cmd(MW_LM1992_TREBLE(0)); - atari_microwire_cmd(MW_LM1992_BASS(0)); -} - -static void __init FalconMixerInit(void) -{ - dmasound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8; - dmasound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4; -} - -static int AtaMixerIoctl(u_int cmd, u_long arg) -{ - int data; - switch (cmd) { - case SOUND_MIXER_READ_SPEAKER: - if (is_falcon || MACH_IS_TT) { - int porta; - cli(); - sound_ym.rd_data_reg_sel = 14; - porta = sound_ym.rd_data_reg_sel; - sti(); - return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); - } - break; - case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, dmasound_set_volume(data)); - case SOUND_MIXER_WRITE_SPEAKER: - if (is_falcon || MACH_IS_TT) { - int porta; - IOCTL_IN(arg, data); - cli(); - sound_ym.rd_data_reg_sel = 14; - porta = (sound_ym.rd_data_reg_sel & ~0x40) | - (data < 50 ? 0x40 : 0); - sound_ym.wd_data = porta; - sti(); - return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); - } - } - return -EINVAL; -} - - -static int TTMixerIoctl(u_int cmd, u_long arg) -{ - int data; - switch (cmd) { - case SOUND_MIXER_READ_RECMASK: - return IOCTL_OUT(arg, 0); - case SOUND_MIXER_READ_DEVMASK: - return IOCTL_OUT(arg, - SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS | - (MACH_IS_TT ? SOUND_MASK_SPEAKER : 0)); - case SOUND_MIXER_READ_STEREODEVS: - return IOCTL_OUT(arg, SOUND_MASK_VOLUME); - case SOUND_MIXER_READ_VOLUME: - return IOCTL_OUT(arg, - VOLUME_DB_TO_VOXWARE(dmasound.volume_left) | - (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8)); - case SOUND_MIXER_READ_BASS: - return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.bass)); - case SOUND_MIXER_READ_TREBLE: - return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.treble)); - case SOUND_MIXER_READ_OGAIN: - return IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(dmasound.gain)); - case SOUND_MIXER_WRITE_BASS: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, dmasound_set_bass(data)); - case SOUND_MIXER_WRITE_TREBLE: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, dmasound_set_treble(data)); - case SOUND_MIXER_WRITE_OGAIN: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, dmasound_set_gain(data)); - } - return AtaMixerIoctl(cmd, arg); -} - -static int FalconMixerIoctl(u_int cmd, u_long arg) -{ - int data; - switch (cmd) { - case SOUND_MIXER_READ_RECMASK: - return IOCTL_OUT(arg, SOUND_MASK_MIC); - case SOUND_MIXER_READ_DEVMASK: - return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER); - case SOUND_MIXER_READ_STEREODEVS: - return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC); - case SOUND_MIXER_READ_VOLUME: - return IOCTL_OUT(arg, - VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) | - VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8); - case SOUND_MIXER_READ_CAPS: - return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT); - case SOUND_MIXER_WRITE_MIC: - IOCTL_IN(arg, data); - tt_dmasnd.input_gain = - RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 | - RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff); - /* fall thru, return set value */ - case SOUND_MIXER_READ_MIC: - return IOCTL_OUT(arg, - RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) | - RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8); - } - return AtaMixerIoctl(cmd, arg); -} - -static void AtaWriteSqSetup(void) -{ - write_sq_ignore_int = 0; -} - -static void AtaSqOpen(void) -{ - write_sq_ignore_int = 1; -} - -static int TTStateInfo(char *buffer) -{ - int len = 0; - len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n", - dmasound.volume_left); - len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n", - dmasound.volume_right); - len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n", - dmasound.bass); - len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n", - dmasound.treble); - return len; -} - -static int FalconStateInfo(char *buffer) -{ - int len = 0; - len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n", - dmasound.volume_left); - len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n", - dmasound.volume_right); - return len; -} - - -/*** Machine definitions *****************************************************/ - - -static MACHINE machTT = { - name: "Atari", - name2: "TT", - open: AtaOpen, - release: AtaRelease, - dma_alloc: AtaAlloc, - dma_free: AtaFree, - irqinit: AtaIrqInit, -#ifdef MODULE - irqcleanup: AtaIrqCleanUp, -#endif /* MODULE */ - init: TTInit, - silence: TTSilence, - setFormat: TTSetFormat, - setVolume: TTSetVolume, - setBass: AtaSetBass, - setTreble: AtaSetTreble, - setGain: TTSetGain, - play: AtaPlay, - mixer_init: TTMixerInit, - mixer_ioctl: TTMixerIoctl, - write_sq_setup: AtaWriteSqSetup, - sq_open: AtaSqOpen, - state_info: TTStateInfo, - min_dsp_speed: 6258, -}; - -static MACHINE machFalcon = { - name: "Atari", - name2: "FALCON", - dma_alloc: AtaAlloc, - dma_free: AtaFree, - irqinit: AtaIrqInit, -#ifdef MODULE - irqcleanup: AtaIrqCleanUp, -#endif /* MODULE */ - init: FalconInit, - silence: FalconSilence, - setFormat: FalconSetFormat, - setVolume: FalconSetVolume, - setBass: AtaSetBass, - setTreble: AtaSetTreble, - play: AtaPlay, - mixer_init: FalconMixerInit, - mixer_ioctl: FalconMixerIoctl, - write_sq_setup: AtaWriteSqSetup, - sq_open: AtaSqOpen, - state_info: FalconStateInfo, - min_dsp_speed: 8195, -}; - - -/*** Config & Setup **********************************************************/ - - -static int __init dmasound_atari_init(void) -{ - if (MACH_IS_ATARI && ATARIHW_PRESENT(PCM_8BIT)) { - if (ATARIHW_PRESENT(CODEC)) { - dmasound.mach = machFalcon; - is_falcon = 1; - } else if (ATARIHW_PRESENT(MICROWIRE)) { - dmasound.mach = machTT; - is_falcon = 0; - } else - return -ENODEV; - if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0) - return dmasound_init(); - else { - printk("DMA sound driver: Timer A interrupt already in use\n"); - return -EBUSY; - } - } - return -ENODEV; -} - -static void __exit dmasound_atari_cleanup(void) -{ - dmasound_deinit(); -} - -module_init(dmasound_atari_init); -module_exit(dmasound_atari_cleanup); -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/dmasound/dmasound_awacs.c b/drivers/sound/dmasound/dmasound_awacs.c --- a/drivers/sound/dmasound/dmasound_awacs.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2183 +0,0 @@ - -/* - * linux/drivers/sound/dmasound/dmasound_awacs.c - * - * PowerMac `AWACS' and `Burgundy' DMA Sound Driver - * - * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_ADB_CUDA -#include -#endif -#ifdef CONFIG_ADB_PMU -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "awacs_defs.h" -#include "dmasound.h" - - -/* - * Interrupt numbers and addresses, obtained from the device tree. - */ -static int awacs_irq, awacs_tx_irq, awacs_rx_irq; -static volatile struct awacs_regs *awacs; -static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma; -static int awacs_rate_index; -static int awacs_subframe; -static int awacs_spkr_vol; -static struct device_node* awacs_node; - -static char awacs_name[64]; -static int awacs_revision; -int awacs_is_screamer = 0; -int awacs_device_id = 0; -int awacs_has_iic = 0; -#define AWACS_BURGUNDY 100 /* fake revision # for burgundy */ - -/* - * Space for the DBDMA command blocks. - */ -static void *awacs_tx_cmd_space; -static volatile struct dbdma_cmd *awacs_tx_cmds; - -static void *awacs_rx_cmd_space; -static volatile struct dbdma_cmd *awacs_rx_cmds; - -/* - * Cached values of AWACS registers (we can't read them). - * Except on the burgundy. XXX - */ -int awacs_reg[8]; - -#define HAS_16BIT_TABLES -#undef HAS_8BIT_TABLES - -/* - * Stuff for outputting a beep. The values range from -327 to +327 - * so we can multiply by an amplitude in the range 0..100 to get a - * signed short value to put in the output buffer. - */ -static short beep_wform[256] = { - 0, 40, 79, 117, 153, 187, 218, 245, - 269, 288, 304, 316, 323, 327, 327, 324, - 318, 310, 299, 288, 275, 262, 249, 236, - 224, 213, 204, 196, 190, 186, 183, 182, - 182, 183, 186, 189, 192, 196, 200, 203, - 206, 208, 209, 209, 209, 207, 204, 201, - 197, 193, 188, 183, 179, 174, 170, 166, - 163, 161, 160, 159, 159, 160, 161, 162, - 164, 166, 168, 169, 171, 171, 171, 170, - 169, 167, 163, 159, 155, 150, 144, 139, - 133, 128, 122, 117, 113, 110, 107, 105, - 103, 103, 103, 103, 104, 104, 105, 105, - 105, 103, 101, 97, 92, 86, 78, 68, - 58, 45, 32, 18, 3, -11, -26, -41, - -55, -68, -79, -88, -95, -100, -102, -102, - -99, -93, -85, -75, -62, -48, -33, -16, - 0, 16, 33, 48, 62, 75, 85, 93, - 99, 102, 102, 100, 95, 88, 79, 68, - 55, 41, 26, 11, -3, -18, -32, -45, - -58, -68, -78, -86, -92, -97, -101, -103, - -105, -105, -105, -104, -104, -103, -103, -103, - -103, -105, -107, -110, -113, -117, -122, -128, - -133, -139, -144, -150, -155, -159, -163, -167, - -169, -170, -171, -171, -171, -169, -168, -166, - -164, -162, -161, -160, -159, -159, -160, -161, - -163, -166, -170, -174, -179, -183, -188, -193, - -197, -201, -204, -207, -209, -209, -209, -208, - -206, -203, -200, -196, -192, -189, -186, -183, - -182, -182, -183, -186, -190, -196, -204, -213, - -224, -236, -249, -262, -275, -288, -299, -310, - -318, -324, -327, -327, -323, -316, -304, -288, - -269, -245, -218, -187, -153, -117, -79, -40, -}; - -#define BEEP_SRATE 22050 /* 22050 Hz sample rate */ -#define BEEP_BUFLEN 512 -#define BEEP_VOLUME 15 /* 0 - 100 */ - -static int beep_volume = BEEP_VOLUME; -static int beep_playing = 0; -static int awacs_beep_state = 0; -static short *beep_buf; -static volatile struct dbdma_cmd *beep_dbdma_cmd; -static void (*orig_mksound)(unsigned int, unsigned int); -static int is_pbook_3400; -static unsigned char *latch_base; -static int is_pbook_G3; -static unsigned char *macio_base; - -/* Burgundy functions */ -static void awacs_burgundy_wcw(unsigned addr,unsigned newval); -static unsigned awacs_burgundy_rcw(unsigned addr); -static void awacs_burgundy_write_volume(unsigned address, int volume); -static int awacs_burgundy_read_volume(unsigned address); -static void awacs_burgundy_write_mvolume(unsigned address, int volume); -static int awacs_burgundy_read_mvolume(unsigned address); - -#ifdef CONFIG_PMAC_PBOOK -/* - * Stuff for restoring after a sleep. - */ -static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when); -struct pmu_sleep_notifier awacs_sleep_notifier = { - awacs_sleep_notify, SLEEP_LEVEL_SOUND, -}; -#endif /* CONFIG_PMAC_PBOOK */ - -static int expand_bal; /* Balance factor for expanding (not volume!) */ -static int expand_data; /* Data for expanding */ - - -/*** Translations ************************************************************/ - - -/* ++TeSche: radically changed for new expanding purposes... - * - * These two routines now deal with copying/expanding/translating the samples - * from user space into our buffer at the right frequency. They take care about - * how much data there's actually to read, how much buffer space there is and - * to convert samples into the right frequency/encoding. They will only work on - * complete samples so it may happen they leave some bytes in the input stream - * if the user didn't write a multiple of the current sample size. They both - * return the number of bytes they've used from both streams so you may detect - * such a situation. Luckily all programs should be able to cope with that. - * - * I think I've optimized anything as far as one can do in plain C, all - * variables should fit in registers and the loops are really short. There's - * one loop for every possible situation. Writing a more generalized and thus - * parameterized loop would only produce slower code. Feel free to optimize - * this in assembler if you like. :) - * - * I think these routines belong here because they're not yet really hardware - * independent, especially the fact that the Falcon can play 16bit samples - * only in stereo is hardcoded in both of them! - * - * ++geert: split in even more functions (one per format) - */ - -static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); -static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft); - - -/*** Low level stuff *********************************************************/ - - -static void PMacOpen(void); -static void PMacRelease(void); -static void *PMacAlloc(unsigned int size, int flags); -static void PMacFree(void *ptr, unsigned int size); -static int PMacIrqInit(void); -#ifdef MODULE -static void PMacIrqCleanup(void); -#endif -static void PMacSilence(void); -static void PMacInit(void); -static int PMacSetFormat(int format); -static int PMacSetVolume(int volume); -static void PMacPlay(void); -static void PMacRecord(void); -static void pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs); -static void pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs); -static void pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs); -static void awacs_write(int val); -static int awacs_get_volume(int reg, int lshift); -static int awacs_volume_setter(int volume, int n, int mute, int lshift); -static void awacs_mksound(unsigned int hz, unsigned int ticks); -static void awacs_nosound(unsigned long xx); - - -/*** Mid level stuff **********************************************************/ - - -static int PMacMixerIoctl(u_int cmd, u_long arg); -static void PMacWriteSqSetup(void); -static void PMacReadSqSetup(void); -static void PMacAbortRead(void); - - -/*** Translations ************************************************************/ - - -static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - short *table = dmasound.soft.format == AFMT_MU_LAW - ? dmasound_ulaw2dma16 : dmasound_alaw2dma16; - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = dmasound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min_t(unsigned long, userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = table[data]; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = table[data]; - } - *p++ = val; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = dmasound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min_t(unsigned long, userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = data << 8; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = data << 8; - } - *p++ = val; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = dmasound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min_t(unsigned long, userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = (data ^ 0x80) << 8; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = (data ^ 0x80) << 8; - } - *p++ = val; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int stereo = dmasound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min_t(unsigned long, userCount, frameLeft); - if (!stereo) { - short *up = (short *) userPtr; - while (count > 0) { - short data; - if (get_user(data, up++)) - return -EFAULT; - *fp++ = data; - *fp++ = data; - count--; - } - } else { - if (copy_from_user(fp, userPtr, count * 4)) - return -EFAULT; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - -static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); - int stereo = dmasound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - short *up = (short *) userPtr; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min_t(unsigned long, userCount, frameLeft); - while (count > 0) { - int data; - if (get_user(data, up++)) - return -EFAULT; - data ^= mask; - *fp++ = data; - if (stereo) { - if (get_user(data, up++)) - return -EFAULT; - data ^= mask; - } - *fp++ = data; - count--; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - - -static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned short *table = (unsigned short *) - (dmasound.soft.format == AFMT_MU_LAW - ? dmasound_ulaw2dma16 : dmasound_alaw2dma16); - unsigned int data = expand_data; - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - int bal = expand_bal; - int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - int utotal, ftotal; - int stereo = dmasound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = table[c]; - if (stereo) { - if (get_user(c, userPtr++)) - return -EFAULT; - data = (data << 16) + table[c]; - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 2: utotal; -} - - -static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = expand_data; - int bal = expand_bal; - int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - int stereo = dmasound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = c << 8; - if (stereo) { - if (get_user(c, userPtr++)) - return -EFAULT; - data = (data << 16) + (c << 8); - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 2: utotal; -} - - -static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = expand_data; - int bal = expand_bal; - int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - int stereo = dmasound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = (c ^ 0x80) << 8; - if (stereo) { - if (get_user(c, userPtr++)) - return -EFAULT; - data = (data << 16) + ((c ^ 0x80) << 8); - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 2: utotal; -} - - -static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = expand_data; - unsigned short *up = (unsigned short *) userPtr; - int bal = expand_bal; - int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - int stereo = dmasound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - unsigned short c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(data, up++)) - return -EFAULT; - if (stereo) { - if (get_user(c, up++)) - return -EFAULT; - data = (data << 16) + c; - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 4: utotal * 2; -} - - -static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); - unsigned int *p = (unsigned int *) &frame[*frameUsed]; - unsigned int data = expand_data; - unsigned short *up = (unsigned short *) userPtr; - int bal = expand_bal; - int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - int stereo = dmasound.soft.stereo; - int utotal, ftotal; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - unsigned short c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(data, up++)) - return -EFAULT; - data ^= mask; - if (stereo) { - if (get_user(c, up++)) - return -EFAULT; - data = (data << 16) + (c ^ mask); - } else - data = (data << 16) + data; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) * 4; - utotal -= userCount; - return stereo? utotal * 4: utotal * 2; -} - -static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = dmasound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min_t(unsigned long, userCount, frameLeft); - while (count > 0) { - u_char data; - - val = *p++; - data = val >> 8; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - if (stereo) { - val = *p; - data = val >> 8; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - } - p++; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - short *p = (short *) &frame[*frameUsed]; - int val, stereo = dmasound.soft.stereo; - - frameLeft >>= 2; - if (stereo) - userCount >>= 1; - used = count = min_t(unsigned long, userCount, frameLeft); - while (count > 0) { - u_char data; - - val = *p++; - data = (val >> 8) ^ 0x80; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - if (stereo) { - val = *p; - data = (val >> 8) ^ 0x80; - if (put_user(data, (u_char *)userPtr++)) - return -EFAULT; - } - p++; - count--; - } - *frameUsed += used * 4; - return stereo? used * 2: used; -} - - -static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int stereo = dmasound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min_t(unsigned long, userCount, frameLeft); - if (!stereo) { - short *up = (short *) userPtr; - while (count > 0) { - short data; - data = *fp; - if (put_user(data, up++)) - return -EFAULT; - fp+=2; - count--; - } - } else { - if (copy_to_user((u_char *)userPtr, fp, count * 4)) - return -EFAULT; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - -static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); - int stereo = dmasound.soft.stereo; - short *fp = (short *) &frame[*frameUsed]; - short *up = (short *) userPtr; - - frameLeft >>= 2; - userCount >>= (stereo? 2: 1); - used = count = min_t(unsigned long, userCount, frameLeft); - while (count > 0) { - int data; - - data = *fp++; - data ^= mask; - if (put_user(data, up++)) - return -EFAULT; - if (stereo) { - data = *fp; - data ^= mask; - if (put_user(data, up++)) - return -EFAULT; - } - fp++; - count--; - } - *frameUsed += used * 4; - return stereo? used * 4: used * 2; -} - - -static TRANS transAwacsNormal = { - ct_ulaw: pmac_ct_law, - ct_alaw: pmac_ct_law, - ct_s8: pmac_ct_s8, - ct_u8: pmac_ct_u8, - ct_s16be: pmac_ct_s16, - ct_u16be: pmac_ct_u16, - ct_s16le: pmac_ct_s16, - ct_u16le: pmac_ct_u16, -}; - -static TRANS transAwacsExpand = { - ct_ulaw: pmac_ctx_law, - ct_alaw: pmac_ctx_law, - ct_s8: pmac_ctx_s8, - ct_u8: pmac_ctx_u8, - ct_s16be: pmac_ctx_s16, - ct_u16be: pmac_ctx_u16, - ct_s16le: pmac_ctx_s16, - ct_u16le: pmac_ctx_u16, -}; - -static TRANS transAwacsNormalRead = { - ct_s8: pmac_ct_s8_read, - ct_u8: pmac_ct_u8_read, - ct_s16be: pmac_ct_s16_read, - ct_u16be: pmac_ct_u16_read, - ct_s16le: pmac_ct_s16_read, - ct_u16le: pmac_ct_u16_read, -}; - -/*** Low level stuff *********************************************************/ - - - -/* - * PCI PowerMac, with AWACS and DBDMA. - */ - -static void PMacOpen(void) -{ - MOD_INC_USE_COUNT; -} - -static void PMacRelease(void) -{ - MOD_DEC_USE_COUNT; -} - -static void *PMacAlloc(unsigned int size, int flags) -{ - return kmalloc(size, flags); -} - -static void PMacFree(void *ptr, unsigned int size) -{ - kfree(ptr); -} - -static int __init PMacIrqInit(void) -{ - if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0) - || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0) - || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0)) - return 0; - return 1; -} - -#ifdef MODULE -static void PMacIrqCleanup(void) -{ - /* turn off output dma */ - out_le32(&awacs_txdma->control, RUN<<16); - /* disable interrupts from awacs interface */ - out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff); -#ifdef CONFIG_PMAC_PBOOK - if (is_pbook_G3) { - feature_clear(awacs_node, FEATURE_Sound_power); - feature_clear(awacs_node, FEATURE_Sound_CLK_enable); - } -#endif - free_irq(awacs_irq, 0); - free_irq(awacs_tx_irq, 0); - free_irq(awacs_rx_irq, 0); - kfree(awacs_tx_cmd_space); - if (awacs_rx_cmd_space) - kfree(awacs_rx_cmd_space); - if (beep_buf) - kfree(beep_buf); - kd_mksound = orig_mksound; -#ifdef CONFIG_PMAC_PBOOK - pmu_unregister_sleep_notifier(&awacs_sleep_notifier); -#endif -} -#endif /* MODULE */ - -static void PMacSilence(void) -{ - /* turn off output dma */ - out_le32(&awacs_txdma->control, RUN<<16); -} - -static int awacs_freqs[8] = { - 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 -}; -static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; - -static void PMacInit(void) -{ - int i, tolerance; - - switch (dmasound.soft.format) { - case AFMT_S16_LE: - case AFMT_U16_LE: - dmasound.hard.format = AFMT_S16_LE; - break; - default: - dmasound.hard.format = AFMT_S16_BE; - break; - } - dmasound.hard.stereo = 1; - dmasound.hard.size = 16; - - /* - * If we have a sample rate which is within catchRadius percent - * of the requested value, we don't have to expand the samples. - * Otherwise choose the next higher rate. - * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz. - */ - i = 8; - do { - tolerance = catchRadius * awacs_freqs[--i] / 100; - if (awacs_freqs_ok[i] - && dmasound.soft.speed <= awacs_freqs[i] + tolerance) - break; - } while (i > 0); - if (dmasound.soft.speed >= awacs_freqs[i] - tolerance) - dmasound.trans_write = &transAwacsNormal; - else - dmasound.trans_write = &transAwacsExpand; - dmasound.trans_read = &transAwacsNormalRead; - dmasound.hard.speed = awacs_freqs[i]; - awacs_rate_index = i; - - /* XXX disable error interrupt on burgundy for now */ - out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11 - | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); - awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3); - awacs_write(awacs_reg[1] | MASK_ADDR1); - out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); - - /* We really want to execute a DMA stop command, after the AWACS - * is initialized. - * For reasons I don't understand, it stops the hissing noise - * common to many PowerBook G3 systems (like mine :-). - */ - out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); - st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); - out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); - out_le32(&awacs_txdma->control, RUN | (RUN << 16)); - - expand_bal = -dmasound.soft.speed; -} - -static int PMacSetFormat(int format) -{ - int size; - - switch (format) { - case AFMT_QUERY: - return dmasound.soft.format; - case AFMT_MU_LAW: - case AFMT_A_LAW: - case AFMT_U8: - case AFMT_S8: - size = 8; - break; - case AFMT_S16_BE: - case AFMT_U16_BE: - case AFMT_S16_LE: - case AFMT_U16_LE: - size = 16; - break; - default: /* :-) */ - printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", - format); - size = 8; - format = AFMT_U8; - } - - dmasound.soft.format = format; - dmasound.soft.size = size; - if (dmasound.minDev == SND_DEV_DSP) { - dmasound.dsp.format = format; - dmasound.dsp.size = size; - } - - PMacInit(); - - return format; -} - -#define AWACS_VOLUME_TO_MASK(x) (15 - ((((x) - 1) * 15) / 99)) -#define AWACS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 15)) - -static int awacs_get_volume(int reg, int lshift) -{ - int volume; - - volume = AWACS_MASK_TO_VOLUME((reg >> lshift) & 0xf); - volume |= AWACS_MASK_TO_VOLUME(reg & 0xf) << 8; - return volume; -} - -static int awacs_volume_setter(int volume, int n, int mute, int lshift) -{ - int r1, rn; - - if (mute && volume == 0) { - r1 = awacs_reg[1] | mute; - } else { - r1 = awacs_reg[1] & ~mute; - rn = awacs_reg[n] & ~(0xf | (0xf << lshift)); - rn |= ((AWACS_VOLUME_TO_MASK(volume & 0xff) & 0xf) << lshift); - rn |= AWACS_VOLUME_TO_MASK((volume >> 8) & 0xff) & 0xf; - awacs_reg[n] = rn; - awacs_write((n << 12) | rn); - volume = awacs_get_volume(rn, lshift); - } - if (r1 != awacs_reg[1]) { - awacs_reg[1] = r1; - awacs_write(r1 | MASK_ADDR1); - } - return volume; -} - -static int PMacSetVolume(int volume) -{ - return awacs_volume_setter(volume, 2, MASK_AMUTE, 6); -} - -static void PMacPlay(void) -{ - volatile struct dbdma_cmd *cp; - int i, count; - unsigned long flags; - - save_flags(flags); cli(); - if (awacs_beep_state) { - /* sound takes precedence over beeps */ - out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); - out_le32(&awacs->control, - (in_le32(&awacs->control) & ~0x1f00) - | (awacs_rate_index << 8)); - out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); - out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(write_sq.front+write_sq.active) % write_sq.max_count]))); - - beep_playing = 0; - awacs_beep_state = 0; - } - i = write_sq.front + write_sq.active; - if (i >= write_sq.max_count) - i -= write_sq.max_count; - while (write_sq.active < 2 && write_sq.active < write_sq.count) { - count = (write_sq.count == write_sq.active + 1)?write_sq.rear_size:write_sq.block_size; - if (count < write_sq.block_size && !write_sq.syncing) - /* last block not yet filled, and we're not syncing. */ - break; - cp = &awacs_tx_cmds[i]; - st_le16(&cp->req_count, count); - st_le16(&cp->xfer_status, 0); - if (++i >= write_sq.max_count) - i = 0; - out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP); - out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS); - if (write_sq.active == 0) - out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp)); - out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); - ++write_sq.active; - } - restore_flags(flags); -} - - -static void PMacRecord(void) -{ - unsigned long flags; - - if (read_sq.active) - return; - - save_flags(flags); cli(); - - /* This is all we have to do......Just start it up. - */ - out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); - read_sq.active = 1; - - restore_flags(flags); -} - - -static void -pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs) -{ - int i = write_sq.front; - int stat; - volatile struct dbdma_cmd *cp; - - while (write_sq.active > 0) { - cp = &awacs_tx_cmds[i]; - stat = ld_le16(&cp->xfer_status); - if ((stat & ACTIVE) == 0) - break; /* this frame is still going */ - --write_sq.count; - --write_sq.active; - if (++i >= write_sq.max_count) - i = 0; - } - if (i != write_sq.front) - WAKE_UP(write_sq.action_queue); - write_sq.front = i; - - PMacPlay(); - - if (!write_sq.active) - WAKE_UP(write_sq.sync_queue); -} - - -static void -pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs) -{ - - /* For some reason on my PowerBook G3, I get one interrupt - * when the interrupt vector is installed (like something is - * pending). This happens before the dbdma is initialize by - * us, so I just check the command pointer and if it is zero, - * just blow it off. - */ - if (in_le32(&awacs_rxdma->cmdptr) == 0) - return; - - /* We also want to blow 'em off when shutting down. - */ - if (read_sq.active == 0) - return; - - /* Check multiple buffers in case we were held off from - * interrupt processing for a long time. Geeze, I really hope - * this doesn't happen. - */ - while (awacs_rx_cmds[read_sq.rear].xfer_status) { - - /* Clear status and move on to next buffer. - */ - awacs_rx_cmds[read_sq.rear].xfer_status = 0; - read_sq.rear++; - - /* Wrap the buffer ring. - */ - if (read_sq.rear >= read_sq.max_active) - read_sq.rear = 0; - - /* If we have caught up to the front buffer, bump it. - * This will cause weird (but not fatal) results if the - * read loop is currently using this buffer. The user is - * behind in this case anyway, so weird things are going - * to happen. - */ - if (read_sq.rear == read_sq.front) { - read_sq.front++; - if (read_sq.front >= read_sq.max_active) - read_sq.front = 0; - } - } - - WAKE_UP(read_sq.action_queue); -} - - -static void -pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs) -{ - int ctrl = in_le32(&awacs->control); - - if (ctrl & MASK_PORTCHG) { - /* do something when headphone is plugged/unplugged? */ - } - if (ctrl & MASK_CNTLERR) { - int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16; - if (err != 0 && awacs_revision < AWACS_BURGUNDY) - printk(KERN_ERR "AWACS: error %x\n", err); - } - /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ - out_le32(&awacs->control, ctrl); -} - -static void -awacs_write(int val) -{ - if (awacs_revision >= AWACS_BURGUNDY) - return; - while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) - ; /* XXX should have timeout */ - out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22)); -} - -static void awacs_nosound(unsigned long xx) -{ - unsigned long flags; - - save_flags(flags); cli(); - if (beep_playing) { - st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); - out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); - out_le32(&awacs->control, - (in_le32(&awacs->control) & ~0x1f00) - | (awacs_rate_index << 8)); - out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); - beep_playing = 0; - } - restore_flags(flags); -} - -static struct timer_list beep_timer = { - function: awacs_nosound -}; - -static void awacs_mksound(unsigned int hz, unsigned int ticks) -{ - unsigned long flags; - int beep_speed = 0; - int srate; - int period, ncycles, nsamples; - int i, j, f; - short *p; - static int beep_hz_cache; - static int beep_nsamples_cache; - static int beep_volume_cache; - - for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i) - if (awacs_freqs_ok[i]) - beep_speed = i; - srate = awacs_freqs[beep_speed]; - - if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { -#if 1 - /* this is a hack for broken X server code */ - hz = 750; - ticks = 12; -#else - /* cancel beep currently playing */ - awacs_nosound(0); - return; -#endif - } - save_flags(flags); cli(); - del_timer(&beep_timer); - if (ticks) { - beep_timer.expires = jiffies + ticks; - add_timer(&beep_timer); - } - if (beep_playing || write_sq.active || beep_buf == NULL) { - restore_flags(flags); - return; /* too hard, sorry :-( */ - } - beep_playing = 1; - st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); - restore_flags(flags); - - if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { - nsamples = beep_nsamples_cache; - } else { - period = srate * 256 / hz; /* fixed point */ - ncycles = BEEP_BUFLEN * 256 / period; - nsamples = (period * ncycles) >> 8; - f = ncycles * 65536 / nsamples; - j = 0; - p = beep_buf; - for (i = 0; i < nsamples; ++i, p += 2) { - p[0] = p[1] = beep_wform[j >> 8] * beep_volume; - j = (j + f) & 0xffff; - } - beep_hz_cache = hz; - beep_volume_cache = beep_volume; - beep_nsamples_cache = nsamples; - } - - st_le16(&beep_dbdma_cmd->req_count, nsamples*4); - st_le16(&beep_dbdma_cmd->xfer_status, 0); - st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); - st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); - awacs_beep_state = 1; - - save_flags(flags); cli(); - if (beep_playing) { /* i.e. haven't been terminated already */ - out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); - out_le32(&awacs->control, - (in_le32(&awacs->control) & ~0x1f00) - | (beep_speed << 8)); - out_le32(&awacs->byteswap, 0); - out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); - out_le32(&awacs_txdma->control, RUN | (RUN << 16)); - } - restore_flags(flags); -} - -#ifdef CONFIG_PMAC_PBOOK -/* - * Save state when going to sleep, restore it afterwards. - */ -static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when) -{ - switch (when) { - case PBOOK_SLEEP_NOW: - /* XXX we should stop any dma in progress when going to sleep - and restart it when we wake. */ - PMacSilence(); - disable_irq(awacs_irq); - disable_irq(awacs_tx_irq); - if (is_pbook_G3) { - feature_clear(awacs_node, FEATURE_Sound_CLK_enable); - feature_clear(awacs_node, FEATURE_Sound_power); - } - break; - case PBOOK_WAKE: - /* There is still a problem on wake. Sound seems to work fine - if I launch mpg123 and resumes fine if mpg123 was playing, - but the console beep is dead until I do something with the - mixer. Probably yet another timing issue */ - if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable) - || !feature_test(awacs_node, FEATURE_Sound_power)) { - /* these aren't present on the 3400 AFAIK -- paulus */ - feature_set(awacs_node, FEATURE_Sound_CLK_enable); - feature_set(awacs_node, FEATURE_Sound_power); - mdelay(1000); - } - out_le32(&awacs->control, MASK_IEPC - | (awacs_rate_index << 8) | 0x11 - | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); - awacs_write(awacs_reg[0] | MASK_ADDR0); - awacs_write(awacs_reg[1] | MASK_ADDR1); - awacs_write(awacs_reg[2] | MASK_ADDR2); - awacs_write(awacs_reg[4] | MASK_ADDR4); - if (awacs_is_screamer) { - awacs_write(awacs_reg[5] + MASK_ADDR5); - awacs_write(awacs_reg[6] + MASK_ADDR6); - awacs_write(awacs_reg[7] + MASK_ADDR7); - } - out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); - enable_irq(awacs_irq); - enable_irq(awacs_tx_irq); - if (awacs_revision == 3) { - mdelay(100); - awacs_write(0x6000); - mdelay(2); - awacs_write(awacs_reg[1] | MASK_ADDR1); - } - /* enable CD sound input */ - if (macio_base && is_pbook_G3) { - out_8(macio_base + 0x37, 3); - } else if (is_pbook_3400) { - feature_set(awacs_node, FEATURE_IOBUS_enable); - udelay(10); - in_8(latch_base + 0x190); - } - /* Resume pending sounds. */ - PMacPlay(); - } - return PBOOK_SLEEP_OK; -} -#endif /* CONFIG_PMAC_PBOOK */ - - -/* All the burgundy functions: */ - -/* Waits for busy flag to clear */ -inline static void -awacs_burgundy_busy_wait(void) -{ - while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) - ; -} - -inline static void -awacs_burgundy_extend_wait(void) -{ - while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) - ; - while (in_le32(&awacs->codec_stat) & MASK_EXTEND) - ; -} - -static void -awacs_burgundy_wcw(unsigned addr, unsigned val) -{ - out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); - awacs_burgundy_busy_wait(); - out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); - awacs_burgundy_busy_wait(); - out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); - awacs_burgundy_busy_wait(); - out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); - awacs_burgundy_busy_wait(); -} - -static unsigned -awacs_burgundy_rcw(unsigned addr) -{ - unsigned val = 0; - unsigned long flags; - - /* should have timeouts here */ - save_flags(flags); cli(); - - out_le32(&awacs->codec_ctrl, addr + 0x100000); - awacs_burgundy_busy_wait(); - awacs_burgundy_extend_wait(); - val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; - - out_le32(&awacs->codec_ctrl, addr + 0x100100); - awacs_burgundy_busy_wait(); - awacs_burgundy_extend_wait(); - val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8; - - out_le32(&awacs->codec_ctrl, addr + 0x100200); - awacs_burgundy_busy_wait(); - awacs_burgundy_extend_wait(); - val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16; - - out_le32(&awacs->codec_ctrl, addr + 0x100300); - awacs_burgundy_busy_wait(); - awacs_burgundy_extend_wait(); - val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24; - - restore_flags(flags); - - return val; -} - - -static void -awacs_burgundy_wcb(unsigned addr, unsigned val) -{ - out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); - awacs_burgundy_busy_wait(); -} - -static unsigned -awacs_burgundy_rcb(unsigned addr) -{ - unsigned val = 0; - unsigned long flags; - - /* should have timeouts here */ - save_flags(flags); cli(); - - out_le32(&awacs->codec_ctrl, addr + 0x100000); - awacs_burgundy_busy_wait(); - awacs_burgundy_extend_wait(); - val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; - - restore_flags(flags); - - return val; -} - -static int -awacs_burgundy_check(void) -{ - /* Checks to see the chip is alive and kicking */ - int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE; - - return error == 0xf0000; -} - -static int -awacs_burgundy_init(void) -{ - if (awacs_burgundy_check()) { - printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n"); - return 1; - } - - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES, - DEF_BURGUNDY_OUTPUTENABLES); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - DEF_BURGUNDY_MORE_OUTPUTENABLES); - awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS, - DEF_BURGUNDY_OUTPUTSELECTS); - - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21, - DEF_BURGUNDY_INPSEL21); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3, - DEF_BURGUNDY_INPSEL3); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD, - DEF_BURGUNDY_GAINCD); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE, - DEF_BURGUNDY_GAINLINE); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC, - DEF_BURGUNDY_GAINMIC); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM, - DEF_BURGUNDY_GAINMODEM); - - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, - DEF_BURGUNDY_ATTENSPEAKER); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT, - DEF_BURGUNDY_ATTENLINEOUT); - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP, - DEF_BURGUNDY_ATTENHP); - - awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME, - DEF_BURGUNDY_MASTER_VOLUME); - awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD, - DEF_BURGUNDY_VOLCD); - awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE, - DEF_BURGUNDY_VOLLINE); - awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC, - DEF_BURGUNDY_VOLMIC); - return 0; -} - -static void -awacs_burgundy_write_volume(unsigned address, int volume) -{ - int hardvolume,lvolume,rvolume; - - lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0; - rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0; - - hardvolume = lvolume + (rvolume << 16); - - awacs_burgundy_wcw(address, hardvolume); -} - -static int -awacs_burgundy_read_volume(unsigned address) -{ - int softvolume,wvolume; - - wvolume = awacs_burgundy_rcw(address); - - softvolume = (wvolume & 0xff) - 155; - softvolume += (((wvolume >> 16) & 0xff) - 155)<<8; - - return softvolume > 0 ? softvolume : 0; -} - - - - -static int -awacs_burgundy_read_mvolume(unsigned address) -{ - int lvolume,rvolume,wvolume; - - wvolume = awacs_burgundy_rcw(address); - - wvolume &= 0xffff; - - rvolume = (wvolume & 0xff) - 155; - lvolume = ((wvolume & 0xff00)>>8) - 155; - - return lvolume + (rvolume << 8); -} - - -static void -awacs_burgundy_write_mvolume(unsigned address, int volume) -{ - int lvolume,rvolume,hardvolume; - - lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0; - rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0; - - hardvolume = lvolume + (rvolume << 8); - hardvolume += (hardvolume << 16); - - awacs_burgundy_wcw(address, hardvolume); -} - -/* End burgundy functions */ - - - - - -/* Turn on sound output, needed on G3 desktop powermacs */ -static void -awacs_enable_amp(int spkr_vol) -{ - struct adb_request req; - - awacs_spkr_vol = spkr_vol; - if (sys_ctrler != SYS_CTRLER_CUDA) - return; - -#ifdef CONFIG_ADB_CUDA - /* turn on headphones */ - cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, - 0x8a, 4, 0); - while (!req.complete) cuda_poll(); - cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, - 0x8a, 6, 0); - while (!req.complete) cuda_poll(); - - /* turn on speaker */ - cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, - 0x8a, 3, (100 - (spkr_vol & 0xff)) * 32 / 100); - while (!req.complete) cuda_poll(); - cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, - 0x8a, 5, (100 - ((spkr_vol >> 8) & 0xff)) * 32 / 100); - while (!req.complete) cuda_poll(); - - cuda_request(&req, NULL, 5, CUDA_PACKET, - CUDA_GET_SET_IIC, 0x8a, 1, 0x29); - while (!req.complete) cuda_poll(); -#endif /* CONFIG_ADB_CUDA */ -} - - -/*** Mid level stuff *********************************************************/ - - -/* - * /dev/mixer abstraction - */ - -static int awacs_mixer_ioctl(u_int cmd, u_long arg) -{ - int data; - - switch (cmd) { - case SOUND_MIXER_READ_DEVMASK: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD | SOUND_MASK_RECLEV - | SOUND_MASK_ALTPCM - | SOUND_MASK_MONITOR; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECMASK: - data = SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECSRC: - data = 0; - if (awacs_reg[0] & MASK_MUX_AUDIN) - data |= SOUND_MASK_LINE; - if (awacs_reg[0] & MASK_MUX_MIC) - data |= SOUND_MASK_MIC; - if (awacs_reg[0] & MASK_MUX_CD) - data |= SOUND_MASK_CD; - if (awacs_reg[1] & MASK_LOOPTHRU) - data |= SOUND_MASK_MONITOR; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECSRC: - IOCTL_IN(arg, data); - data &= (SOUND_MASK_LINE - | SOUND_MASK_MIC | SOUND_MASK_CD - | SOUND_MASK_MONITOR); - awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC - | MASK_MUX_AUDIN); - awacs_reg[1] &= ~MASK_LOOPTHRU; - if (data & SOUND_MASK_LINE) - awacs_reg[0] |= MASK_MUX_AUDIN; - if (data & SOUND_MASK_MIC) - awacs_reg[0] |= MASK_MUX_MIC; - if (data & SOUND_MASK_CD) - awacs_reg[0] |= MASK_MUX_CD; - if (data & SOUND_MASK_MONITOR) - awacs_reg[1] |= MASK_LOOPTHRU; - awacs_write(awacs_reg[0] | MASK_ADDR0); - awacs_write(awacs_reg[1] | MASK_ADDR1); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_STEREODEVS: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_RECLEV; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_CAPS: - return IOCTL_OUT(arg, 0); - case SOUND_MIXER_READ_VOLUME: - data = (awacs_reg[1] & MASK_AMUTE)? 0: - awacs_get_volume(awacs_reg[2], 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, PMacSetVolume(data)); - case SOUND_MIXER_READ_SPEAKER: - if (awacs_revision == 3 - && sys_ctrler == SYS_CTRLER_CUDA) - data = awacs_spkr_vol; - else - data = (awacs_reg[1] & MASK_CMUTE)? 0: - awacs_get_volume(awacs_reg[4], 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_SPEAKER: - IOCTL_IN(arg, data); - if (awacs_revision == 3 - && sys_ctrler == SYS_CTRLER_CUDA) - awacs_enable_amp(data); - else - data = awacs_volume_setter(data, 4, MASK_CMUTE, 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ - IOCTL_IN(arg, data); - beep_volume = data & 0xff; - /* fall through */ - case SOUND_MIXER_READ_ALTPCM: - return IOCTL_OUT(arg, beep_volume); - case SOUND_MIXER_WRITE_LINE: - IOCTL_IN(arg, data); - awacs_reg[0] &= ~MASK_MUX_AUDIN; - if ((data & 0xff) >= 50) - awacs_reg[0] |= MASK_MUX_AUDIN; - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_LINE: - data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_MIC: - IOCTL_IN(arg, data); - data &= 0xff; - awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); - if (data >= 25) { - awacs_reg[0] |= MASK_MUX_MIC; - if (data >= 75) - awacs_reg[0] |= MASK_GAINLINE; - } - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_MIC: - data = (awacs_reg[0] & MASK_MUX_MIC)? - (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_CD: - IOCTL_IN(arg, data); - awacs_reg[0] &= ~MASK_MUX_CD; - if ((data & 0xff) >= 50) - awacs_reg[0] |= MASK_MUX_CD; - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_CD: - data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECLEV: - IOCTL_IN(arg, data); - data = awacs_volume_setter(data, 0, 0, 4); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECLEV: - data = awacs_get_volume(awacs_reg[0], 4); - return IOCTL_OUT(arg, data); - case MIXER_WRITE(SOUND_MIXER_MONITOR): - IOCTL_IN(arg, data); - awacs_reg[1] &= ~MASK_LOOPTHRU; - if ((data & 0xff) >= 50) - awacs_reg[1] |= MASK_LOOPTHRU; - awacs_write(MASK_ADDR1 | awacs_reg[1]); - /* fall through */ - case MIXER_READ(SOUND_MIXER_MONITOR): - data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0; - return IOCTL_OUT(arg, data); - } - return -EINVAL; -} - -static int burgundy_mixer_ioctl(u_int cmd, u_long arg) -{ - int data; - - /* We are, we are, we are... Burgundy or better */ - switch(cmd) { - case SOUND_MIXER_READ_DEVMASK: - data = SOUND_MASK_VOLUME | SOUND_MASK_CD | - SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECMASK: - data = SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECSRC: - data = 0; - if (awacs_reg[0] & MASK_MUX_AUDIN) - data |= SOUND_MASK_LINE; - if (awacs_reg[0] & MASK_MUX_MIC) - data |= SOUND_MASK_MIC; - if (awacs_reg[0] & MASK_MUX_CD) - data |= SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECSRC: - IOCTL_IN(arg, data); - data &= (SOUND_MASK_LINE - | SOUND_MASK_MIC | SOUND_MASK_CD); - awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC - | MASK_MUX_AUDIN); - if (data & SOUND_MASK_LINE) - awacs_reg[0] |= MASK_MUX_AUDIN; - if (data & SOUND_MASK_MIC) - awacs_reg[0] |= MASK_MUX_MIC; - if (data & SOUND_MASK_CD) - awacs_reg[0] |= MASK_MUX_CD; - awacs_write(awacs_reg[0] | MASK_ADDR0); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_STEREODEVS: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_RECLEV | SOUND_MASK_CD - | SOUND_MASK_LINE; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_CAPS: - return IOCTL_OUT(arg, 0); - case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data); - /* Fall through */ - case SOUND_MIXER_READ_VOLUME: - return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME)); - case SOUND_MIXER_WRITE_SPEAKER: - IOCTL_IN(arg, data); - - if (!(data & 0xff)) { - /* Mute the left speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2); - } else { - /* Unmute the left speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2); - } - if (!(data & 0xff00)) { - /* Mute the right speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4); - } else { - /* Unmute the right speaker */ - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, - awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4); - } - - data = (((data&0xff)*16)/100 > 0xf ? 0xf : - (((data&0xff)*16)/100)) + - ((((data>>8)*16)/100 > 0xf ? 0xf : - ((((data>>8)*16)/100)))<<4); - - awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data); - /* Fall through */ - case SOUND_MIXER_READ_SPEAKER: - data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER); - data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8); - return IOCTL_OUT(arg, ~data); - case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ - IOCTL_IN(arg, data); - beep_volume = data & 0xff; - /* fall through */ - case SOUND_MIXER_READ_ALTPCM: - return IOCTL_OUT(arg, beep_volume); - case SOUND_MIXER_WRITE_LINE: - IOCTL_IN(arg, data); - awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data); - - /* fall through */ - case SOUND_MIXER_READ_LINE: - data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_MIC: - IOCTL_IN(arg, data); - /* Mic is mono device */ - data = (data << 8) + (data << 24); - awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data); - /* fall through */ - case SOUND_MIXER_READ_MIC: - data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC); - data <<= 24; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_CD: - IOCTL_IN(arg, data); - awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data); - /* fall through */ - case SOUND_MIXER_READ_CD: - data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECLEV: - IOCTL_IN(arg, data); - data = awacs_volume_setter(data, 0, 0, 4); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECLEV: - data = awacs_get_volume(awacs_reg[0], 4); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_OUTMASK: - break; - case SOUND_MIXER_OUTSRC: - break; - } - return -EINVAL; -} - -static int PMacMixerIoctl(u_int cmd, u_long arg) -{ - /* Different IOCTLS for burgundy*/ - if (awacs_revision >= AWACS_BURGUNDY) - return burgundy_mixer_ioctl(cmd, arg); - return awacs_mixer_ioctl(cmd, arg); -} - - -static void PMacWriteSqSetup(void) -{ - int i; - volatile struct dbdma_cmd *cp; - - cp = awacs_tx_cmds; - memset((void *)cp, 0, (write_sq.numBufs+1) * sizeof(struct dbdma_cmd)); - for (i = 0; i < write_sq.numBufs; ++i, ++cp) { - st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i])); - } - st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); - st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds)); - out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); - out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds)); -} - -static void PMacReadSqSetup(void) -{ - int i; - volatile struct dbdma_cmd *cp; - - cp = awacs_rx_cmds; - memset((void *)cp, 0, (read_sq.numBufs+1) * sizeof(struct dbdma_cmd)); - - /* Set dma buffers up in a loop */ - for (i = 0; i < read_sq.numBufs; i++,cp++) { - st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i])); - st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS); - st_le16(&cp->req_count, read_sq.block_size); - st_le16(&cp->xfer_status, 0); - } - - /* The next two lines make the thing loop around. - */ - st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); - st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds)); - - /* Don't start until the first read is done. - * This will also abort any operations in progress if the DMA - * happens to be running (and it shouldn't). - */ - out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); - out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds)); - -} - -static void PMacAbortRead(void) -{ - int i; - volatile struct dbdma_cmd *cp; - - cp = awacs_rx_cmds; - for (i = 0; i < read_sq.numBufs; i++,cp++) - st_le16(&cp->command, DBDMA_STOP); - /* - * We should probably wait for the thing to stop before we - * release the memory - */ -} - - -/*** Machine definitions *****************************************************/ - - -static MACHINE machPMac = { - name: awacs_name, - name2: "AWACS", - open: PMacOpen, - release: PMacRelease, - dma_alloc: PMacAlloc, - dma_free: PMacFree, - irqinit: PMacIrqInit, -#ifdef MODULE - irqcleanup: PMacIrqCleanup, -#endif /* MODULE */ - init: PMacInit, - silence: PMacSilence, - setFormat: PMacSetFormat, - setVolume: PMacSetVolume, - play: PMacPlay, - record: PMacRecord, - mixer_ioctl: PMacMixerIoctl, - write_sq_setup: PMacWriteSqSetup, - read_sq_setup: PMacReadSqSetup, - abort_read: PMacAbortRead, - min_dsp_speed: 8000 -}; - - -/*** Config & Setup **********************************************************/ - - -int __init dmasound_awacs_init(void) -{ - struct device_node *np; - - if (_machine != _MACH_Pmac) - return -ENODEV; - - awacs_subframe = 0; - awacs_revision = 0; - np = find_devices("awacs"); - if (np == 0) { - /* - * powermac G3 models have a node called "davbus" - * with a child called "sound". - */ - struct device_node *sound; - np = find_devices("davbus"); - sound = find_devices("sound"); - if (sound != 0 && sound->parent == np) { - unsigned int *prop, l, i; - prop = (unsigned int *) - get_property(sound, "sub-frame", 0); - if (prop != 0 && *prop >= 0 && *prop < 16) - awacs_subframe = *prop; - if (device_is_compatible(sound, "burgundy")) - awacs_revision = AWACS_BURGUNDY; - /* This should be verified on older screamers */ - if (device_is_compatible(sound, "screamer")) - awacs_is_screamer = 1; - prop = (unsigned int *)get_property(sound, "device-id", 0); - if (prop != 0) - awacs_device_id = *prop; - awacs_has_iic = (find_devices("perch") != NULL); - - /* look for a property saying what sample rates - are available */ - for (i = 0; i < 8; ++i) - awacs_freqs_ok[i] = 0; - prop = (unsigned int *) get_property - (sound, "sample-rates", &l); - if (prop == 0) - prop = (unsigned int *) get_property - (sound, "output-frame-rates", &l); - if (prop != 0) { - for (l /= sizeof(int); l > 0; --l) { - /* sometimes the rate is in the - high-order 16 bits (?) */ - unsigned int r = *prop++; - if (r >= 0x10000) - r >>= 16; - for (i = 0; i < 8; ++i) { - if (r == awacs_freqs[i]) { - awacs_freqs_ok[i] = 1; - break; - } - } - } - } else { - /* assume just 44.1k is OK */ - awacs_freqs_ok[0] = 1; - } - } - } - if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) { - int vol; - dmasound.mach = machPMac; - - awacs = (volatile struct awacs_regs *) - ioremap(np->addrs[0].address, 0x80); - awacs_txdma = (volatile struct dbdma_regs *) - ioremap(np->addrs[1].address, 0x100); - awacs_rxdma = (volatile struct dbdma_regs *) - ioremap(np->addrs[2].address, 0x100); - - awacs_irq = np->intrs[0].line; - awacs_tx_irq = np->intrs[1].line; - awacs_rx_irq = np->intrs[2].line; - - awacs_tx_cmd_space = kmalloc((write_sq.numBufs + 4) * sizeof(struct dbdma_cmd), - GFP_KERNEL); - if (awacs_tx_cmd_space == NULL) { - printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n"); - return -ENOMEM; - } - awacs_node = np; -#ifdef CONFIG_PMAC_PBOOK - if (machine_is_compatible("PowerBook1,1") - || machine_is_compatible("AAPL,PowerBook1998")) { - pmu_suspend(); - feature_set(np, FEATURE_Sound_CLK_enable); - feature_set(np, FEATURE_Sound_power); - /* Shorter delay will not work */ - mdelay(1000); - pmu_resume(); - } -#endif - awacs_tx_cmds = (volatile struct dbdma_cmd *) - DBDMA_ALIGN(awacs_tx_cmd_space); - - - awacs_rx_cmd_space = kmalloc((read_sq.numBufs + 4) * sizeof(struct dbdma_cmd), - GFP_KERNEL); - if (awacs_rx_cmd_space == NULL) { - printk("DMA sound driver: No memory for input"); - } - awacs_rx_cmds = (volatile struct dbdma_cmd *) - DBDMA_ALIGN(awacs_rx_cmd_space); - - - - awacs_reg[0] = MASK_MUX_CD; - /* FIXME: Only machines with external SRS module need MASK_PAROUT */ - awacs_reg[1] = MASK_LOOPTHRU; - if (awacs_has_iic || awacs_device_id == 0x5 || /*awacs_device_id == 0x8 - || */awacs_device_id == 0xb) - awacs_reg[1] |= MASK_PAROUT; - /* get default volume from nvram */ - vol = (~nvram_read_byte(0x1308) & 7) << 1; - awacs_reg[2] = vol + (vol << 6); - awacs_reg[4] = vol + (vol << 6); - awacs_reg[5] = 0; - awacs_reg[6] = 0; - awacs_reg[7] = 0; - out_le32(&awacs->control, 0x11); - awacs_write(awacs_reg[0] + MASK_ADDR0); - awacs_write(awacs_reg[1] + MASK_ADDR1); - awacs_write(awacs_reg[2] + MASK_ADDR2); - awacs_write(awacs_reg[4] + MASK_ADDR4); - if (awacs_is_screamer) { - awacs_write(awacs_reg[5] + MASK_ADDR5); - awacs_write(awacs_reg[6] + MASK_ADDR6); - awacs_write(awacs_reg[7] + MASK_ADDR7); - } - - /* Initialize recent versions of the awacs */ - if (awacs_revision == 0) { - awacs_revision = - (in_le32(&awacs->codec_stat) >> 12) & 0xf; - if (awacs_revision == 3) { - mdelay(100); - awacs_write(0x6000); - mdelay(2); - awacs_write(awacs_reg[1] + MASK_ADDR1); - awacs_enable_amp(100 * 0x101); - } - } - if (awacs_revision >= AWACS_BURGUNDY) - awacs_burgundy_init(); - - /* Initialize beep stuff */ - beep_dbdma_cmd = awacs_tx_cmds + (write_sq.numBufs + 1); - orig_mksound = kd_mksound; - kd_mksound = awacs_mksound; - beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); - if (beep_buf == NULL) - printk(KERN_WARNING "dmasound: no memory for " - "beep buffer\n"); -#ifdef CONFIG_PMAC_PBOOK - pmu_register_sleep_notifier(&awacs_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ - - /* Powerbooks have odd ways of enabling inputs such as - an expansion-bay CD or sound from an internal modem - or a PC-card modem. */ - if (machine_is_compatible("AAPL,3400/2400") - || machine_is_compatible("AAPL,3500")) { - is_pbook_3400 = 1; - /* - * Enable CD and PC-card sound inputs. - * This is done by reading from address - * f301a000, + 0x10 to enable the expansion-bay - * CD sound input, + 0x80 to enable the PC-card - * sound input. The 0x100 enables the SCSI bus - * terminator power. - */ - latch_base = (unsigned char *) ioremap - (0xf301a000, 0x1000); - in_8(latch_base + 0x190); - } else if (machine_is_compatible("PowerBook1,1") - || machine_is_compatible("AAPL,PowerBook1998")) { - struct device_node* mio; - macio_base = 0; - is_pbook_G3 = 1; - for (mio = np->parent; mio; mio = mio->parent) { - if (strcmp(mio->name, "mac-io") == 0 - && mio->n_addrs > 0) { - macio_base = (unsigned char *) ioremap - (mio->addrs[0].address, 0x40); - break; - } - } - /* - * Enable CD sound input. - * The relevant bits for writing to this byte are 0x8f. - * I haven't found out what the 0x80 bit does. - * For the 0xf bits, writing 3 or 7 enables the CD - * input, any other value disables it. Values - * 1, 3, 5, 7 enable the microphone. Values 0, 2, - * 4, 6, 8 - f enable the input from the modem. - */ - if (macio_base) - out_8(macio_base + 0x37, 3); - } - sprintf(awacs_name, "PowerMac (AWACS rev %d) ", - awacs_revision); - return dmasound_init(); - } - return -ENODEV; -} - -static void __exit dmasound_awacs_cleanup(void) -{ - dmasound_deinit(); -} - -module_init(dmasound_awacs_init); -module_exit(dmasound_awacs_cleanup); -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/dmasound/dmasound_core.c b/drivers/sound/dmasound/dmasound_core.c --- a/drivers/sound/dmasound/dmasound_core.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1313 +0,0 @@ - -/* - * linux/drivers/sound/dmasound/dmasound_core.c - * - * - * OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for - * Linux/m68k - * Extended to support Power Macintosh for Linux/ppc by Paul Mackerras - * - * (c) 1995 by Michael Schlueter & Michael Marte - * - * Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS - * interface and the u-law to signed byte conversion. - * - * Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue, - * /dev/mixer, /dev/sndstat and complemented the VFS interface. He would like - * to thank: - * - Michael Schlueter for initial ideas and documentation on the MFP and - * the DMA sound hardware. - * - Therapy? for their CD 'Troublegum' which really made me rock. - * - * /dev/sndstat is based on code by Hannu Savolainen, the author of the - * VoxWare family of drivers. - * - * 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 archive - * for more details. - * - * History: - * - * 1995/8/25 First release - * - * 1995/9/02 Roman Hodek: - * - Fixed atari_stram_alloc() call, the timer - * programming and several race conditions - * 1995/9/14 Roman Hodek: - * - After some discussion with Michael Schlueter, - * revised the interrupt disabling - * - Slightly speeded up U8->S8 translation by using - * long operations where possible - * - Added 4:3 interpolation for /dev/audio - * - * 1995/9/20 Torsten Scherer: - * - Fixed a bug in sq_write and changed /dev/audio - * converting to play at 12517Hz instead of 6258Hz. - * - * 1995/9/23 Torsten Scherer: - * - Changed sq_interrupt() and sq_play() to pre-program - * the DMA for another frame while there's still one - * running. This allows the IRQ response to be - * arbitrarily delayed and playing will still continue. - * - * 1995/10/14 Guenther Kelleter, Torsten Scherer: - * - Better support for Falcon audio (the Falcon doesn't - * raise an IRQ at the end of a frame, but at the - * beginning instead!). uses 'if (codec_dma)' in lots - * of places to simply switch between Falcon and TT - * code. - * - * 1995/11/06 Torsten Scherer: - * - Started introducing a hardware abstraction scheme - * (may perhaps also serve for Amigas?) - * - Can now play samples at almost all frequencies by - * means of a more generalized expand routine - * - Takes a good deal of care to cut data only at - * sample sizes - * - Buffer size is now a kernel runtime option - * - Implemented fsync() & several minor improvements - * Guenther Kelleter: - * - Useful hints and bug fixes - * - Cross-checked it for Falcons - * - * 1996/3/9 Geert Uytterhoeven: - * - Support added for Amiga, A-law, 16-bit little - * endian. - * - Unification to drivers/sound/dmasound.c. - * - * 1996/4/6 Martin Mitchell: - * - Updated to 1.3 kernel. - * - * 1996/6/13 Topi Kanerva: - * - Fixed things that were broken (mainly the amiga - * 14-bit routines) - * - /dev/sndstat shows now the real hardware frequency - * - The lowpass filter is disabled by default now - * - * 1996/9/25 Geert Uytterhoeven: - * - Modularization - * - * 1998/6/10 Andreas Schwab: - * - Converted to use sound_core - * - * 1999/12/28 Richard Zidlicky: - * - Added support for Q40 - * - * 2000/2/27 Geert Uytterhoeven: - * - Clean up and split the code into 4 parts: - * o dmasound_core: machine-independent code - * o dmasound_atari: Atari TT and Falcon support - * o dmasound_awacs: Apple PowerMac support - * o dmasound_paula: Amiga support - * - * 2000/3/25 Geert Uytterhoeven: - * - Integration of dmasound_q40 - * - Small clean ups - */ - - -#include -#include -#include -#include -#include -#include - -#include - -#include "dmasound.h" - - - /* - * Declarations - */ - -int dmasound_catchRadius = 0; -static unsigned int numWriteBufs = 4; -static unsigned int writeBufSize = 32; /* in KB! */ -#ifdef HAS_RECORD -static unsigned int numReadBufs = 4; -static unsigned int readBufSize = 32; /* in KB! */ -#endif - -MODULE_PARM(dmasound_catchRadius, "i"); -MODULE_PARM(numWriteBufs, "i"); -MODULE_PARM(writeBufSize, "i"); -MODULE_PARM(numReadBufs, "i"); -MODULE_PARM(readBufSize, "i"); -MODULE_LICENSE("GPL"); - -#ifdef MODULE -static int sq_unit = -1; -static int mixer_unit = -1; -static int state_unit = -1; -static int irq_installed = 0; -#endif /* MODULE */ - - - /* - * Conversion tables - */ - -#ifdef HAS_8BIT_TABLES -/* 8 bit mu-law */ - -char dmasound_ulaw2dma8[] = { - -126, -122, -118, -114, -110, -106, -102, -98, - -94, -90, -86, -82, -78, -74, -70, -66, - -63, -61, -59, -57, -55, -53, -51, -49, - -47, -45, -43, -41, -39, -37, -35, -33, - -31, -30, -29, -28, -27, -26, -25, -24, - -23, -22, -21, -20, -19, -18, -17, -16, - -16, -15, -15, -14, -14, -13, -13, -12, - -12, -11, -11, -10, -10, -9, -9, -8, - -8, -8, -7, -7, -7, -7, -6, -6, - -6, -6, -5, -5, -5, -5, -4, -4, - -4, -4, -4, -4, -3, -3, -3, -3, - -3, -3, -3, -3, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 0, - 125, 121, 117, 113, 109, 105, 101, 97, - 93, 89, 85, 81, 77, 73, 69, 65, - 62, 60, 58, 56, 54, 52, 50, 48, - 46, 44, 42, 40, 38, 36, 34, 32, - 30, 29, 28, 27, 26, 25, 24, 23, - 22, 21, 20, 19, 18, 17, 16, 15, - 15, 14, 14, 13, 13, 12, 12, 11, - 11, 10, 10, 9, 9, 8, 8, 7, - 7, 7, 6, 6, 6, 6, 5, 5, - 5, 5, 4, 4, 4, 4, 3, 3, - 3, 3, 3, 3, 2, 2, 2, 2, - 2, 2, 2, 2, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* 8 bit A-law */ - -char dmasound_alaw2dma8[] = { - -22, -21, -24, -23, -18, -17, -20, -19, - -30, -29, -32, -31, -26, -25, -28, -27, - -11, -11, -12, -12, -9, -9, -10, -10, - -15, -15, -16, -16, -13, -13, -14, -14, - -86, -82, -94, -90, -70, -66, -78, -74, - -118, -114, -126, -122, -102, -98, -110, -106, - -43, -41, -47, -45, -35, -33, -39, -37, - -59, -57, -63, -61, -51, -49, -55, -53, - -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -6, -6, -6, -6, -5, -5, -5, -5, - -8, -8, -8, -8, -7, -7, -7, -7, - -3, -3, -3, -3, -3, -3, -3, -3, - -4, -4, -4, -4, -4, -4, -4, -4, - 21, 20, 23, 22, 17, 16, 19, 18, - 29, 28, 31, 30, 25, 24, 27, 26, - 10, 10, 11, 11, 8, 8, 9, 9, - 14, 14, 15, 15, 12, 12, 13, 13, - 86, 82, 94, 90, 70, 66, 78, 74, - 118, 114, 126, 122, 102, 98, 110, 106, - 43, 41, 47, 45, 35, 33, 39, 37, - 59, 57, 63, 61, 51, 49, 55, 53, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 4, 4, 4, 4, - 7, 7, 7, 7, 6, 6, 6, 6, - 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3 -}; -#endif /* HAS_8BIT_TABLES */ - -#ifdef HAS_16BIT_TABLES - -/* 16 bit mu-law */ - -short dmasound_ulaw2dma16[] = { - -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, - -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, - -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, - -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0, -}; - -/* 16 bit A-law */ - -short dmasound_alaw2dma16[] = { - -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, - -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, - -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, - -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, - -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, - -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, - -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, - -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, - -344, -328, -376, -360, -280, -264, -312, -296, - -472, -456, -504, -488, -408, -392, -440, -424, - -88, -72, -120, -104, -24, -8, -56, -40, - -216, -200, -248, -232, -152, -136, -184, -168, - -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, - -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, - -688, -656, -752, -720, -560, -528, -624, -592, - -944, -912, -1008, -976, -816, -784, -880, -848, - 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, - 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, - 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, - 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, - 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, - 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, - 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, - 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, - 344, 328, 376, 360, 280, 264, 312, 296, - 472, 456, 504, 488, 408, 392, 440, 424, - 88, 72, 120, 104, 24, 8, 56, 40, - 216, 200, 248, 232, 152, 136, 184, 168, - 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, - 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, - 688, 656, 752, 720, 560, 528, 624, 592, - 944, 912, 1008, 976, 816, 784, 880, 848, -}; -#endif /* HAS_16BIT_TABLES */ - - -#ifdef HAS_14BIT_TABLES - - /* - * Unused for now. Where are the MSB parts anyway?? - */ - -/* 14 bit mu-law (LSB) */ - -char dmasound_ulaw2dma14l[] = { - 33, 33, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33, 33, 33, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 49, 17, 49, 17, 49, 17, 49, 17, - 49, 17, 49, 17, 49, 17, 49, 17, - 41, 57, 9, 25, 41, 57, 9, 25, - 41, 57, 9, 25, 41, 57, 9, 25, - 37, 45, 53, 61, 5, 13, 21, 29, - 37, 45, 53, 61, 5, 13, 21, 29, - 35, 39, 43, 47, 51, 55, 59, 63, - 3, 7, 11, 15, 19, 23, 27, 31, - 34, 36, 38, 40, 42, 44, 46, 48, - 50, 52, 54, 56, 58, 60, 62, 0, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, - 15, 47, 15, 47, 15, 47, 15, 47, - 15, 47, 15, 47, 15, 47, 15, 47, - 23, 7, 55, 39, 23, 7, 55, 39, - 23, 7, 55, 39, 23, 7, 55, 39, - 27, 19, 11, 3, 59, 51, 43, 35, - 27, 19, 11, 3, 59, 51, 43, 35, - 29, 25, 21, 17, 13, 9, 5, 1, - 61, 57, 53, 49, 45, 41, 37, 33, - 30, 28, 26, 24, 22, 20, 18, 16, - 14, 12, 10, 8, 6, 4, 2, 0 -}; - -/* 14 bit A-law (LSB) */ - -char dmasound_alaw2dma14l[] = { - 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, - 16, 48, 16, 48, 16, 48, 16, 48, - 16, 48, 16, 48, 16, 48, 16, 48, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 42, 46, 34, 38, 58, 62, 50, 54, - 10, 14, 2, 6, 26, 30, 18, 22, - 42, 46, 34, 38, 58, 62, 50, 54, - 10, 14, 2, 6, 26, 30, 18, 22, - 40, 56, 8, 24, 40, 56, 8, 24, - 40, 56, 8, 24, 40, 56, 8, 24, - 20, 28, 4, 12, 52, 60, 36, 44, - 20, 28, 4, 12, 52, 60, 36, 44, - 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, - 48, 16, 48, 16, 48, 16, 48, 16, - 48, 16, 48, 16, 48, 16, 48, 16, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 22, 18, 30, 26, 6, 2, 14, 10, - 54, 50, 62, 58, 38, 34, 46, 42, - 22, 18, 30, 26, 6, 2, 14, 10, - 54, 50, 62, 58, 38, 34, 46, 42, - 24, 8, 56, 40, 24, 8, 56, 40, - 24, 8, 56, 40, 24, 8, 56, 40, - 44, 36, 60, 52, 12, 4, 28, 20, - 44, 36, 60, 52, 12, 4, 28, 20 -}; -#endif /* HAS_14BIT_TABLES */ - - - /* - * Mid level stuff - */ - -struct sound_settings dmasound; - -static inline void sound_silence(void) -{ - /* update hardware settings one more */ - dmasound.mach.init(); - - dmasound.mach.silence(); -} - -static inline void sound_init(void) -{ - dmasound.mach.init(); -} - -static inline int sound_set_format(int format) -{ - return dmasound.mach.setFormat(format); -} - -static int sound_set_speed(int speed) -{ - if (speed < 0) - return dmasound.soft.speed; - - dmasound.soft.speed = speed; - dmasound.mach.init(); - if (dmasound.minDev == SND_DEV_DSP) - dmasound.dsp.speed = dmasound.soft.speed; - - return dmasound.soft.speed; -} - -static int sound_set_stereo(int stereo) -{ - if (stereo < 0) - return dmasound.soft.stereo; - - stereo = !!stereo; /* should be 0 or 1 now */ - - dmasound.soft.stereo = stereo; - if (dmasound.minDev == SND_DEV_DSP) - dmasound.dsp.stereo = stereo; - dmasound.mach.init(); - - return stereo; -} - -static ssize_t sound_copy_translate(TRANS *trans, const u_char *userPtr, - size_t userCount, u_char frame[], - ssize_t *frameUsed, ssize_t frameLeft) -{ - ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); - - switch (dmasound.soft.format) { - case AFMT_MU_LAW: - ct_func = trans->ct_ulaw; - break; - case AFMT_A_LAW: - ct_func = trans->ct_alaw; - break; - case AFMT_S8: - ct_func = trans->ct_s8; - break; - case AFMT_U8: - ct_func = trans->ct_u8; - break; - case AFMT_S16_BE: - ct_func = trans->ct_s16be; - break; - case AFMT_U16_BE: - ct_func = trans->ct_u16be; - break; - case AFMT_S16_LE: - ct_func = trans->ct_s16le; - break; - case AFMT_U16_LE: - ct_func = trans->ct_u16le; - break; - default: - return 0; - } - return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); -} - - - /* - * /dev/mixer abstraction - */ - -static struct { - int busy; - int modify_counter; -} mixer; - -static int mixer_open(struct inode *inode, struct file *file) -{ - dmasound.mach.open(); - mixer.busy = 1; - return 0; -} - -static int mixer_release(struct inode *inode, struct file *file) -{ - lock_kernel(); - mixer.busy = 0; - dmasound.mach.release(); - unlock_kernel(); - return 0; -} -static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg) -{ - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - mixer.modify_counter++; - switch (cmd) { - case OSS_GETVERSION: - return IOCTL_OUT(arg, SOUND_VERSION); - case SOUND_MIXER_INFO: - { - mixer_info info; - strncpy(info.id, dmasound.mach.name2, sizeof(info.id)); - strncpy(info.name, dmasound.mach.name2, sizeof(info.name)); - info.name[sizeof(info.name)-1] = 0; - info.modify_counter = mixer.modify_counter; - if (copy_to_user((int *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - if (dmasound.mach.mixer_ioctl) - return dmasound.mach.mixer_ioctl(cmd, arg); - return -EINVAL; -} - -static struct file_operations mixer_fops = -{ - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: mixer_ioctl, - open: mixer_open, - release: mixer_release, -}; - -static void __init mixer_init(void) -{ -#ifndef MODULE - int mixer_unit; -#endif - mixer_unit = register_sound_mixer(&mixer_fops, -1); - if (mixer_unit < 0) - return; - - mixer.busy = 0; - dmasound.treble = 0; - dmasound.bass = 0; - if (dmasound.mach.mixer_init) - dmasound.mach.mixer_init(); -} - - - /* - * Sound queue stuff, the heart of the driver - */ - -struct sound_queue dmasound_write_sq; -#ifdef HAS_RECORD -struct sound_queue dmasound_read_sq; -#endif - -static int sq_allocate_buffers(struct sound_queue *sq, int num, int size) -{ - int i; - - if (sq->buffers) - return 0; - sq->numBufs = num; - sq->bufSize = size; - sq->buffers = kmalloc (num * sizeof(char *), GFP_KERNEL); - if (!sq->buffers) - return -ENOMEM; - for (i = 0; i < num; i++) { - sq->buffers[i] = dmasound.mach.dma_alloc(size, GFP_KERNEL); - if (!sq->buffers[i]) { - while (i--) - dmasound.mach.dma_free(sq->buffers[i], size); - kfree(sq->buffers); - sq->buffers = 0; - return -ENOMEM; - } - } - return 0; -} - -static void sq_release_buffers(struct sound_queue *sq) -{ - int i; - - if (sq->buffers) { - if (sq != &write_sq && dmasound.mach.abort_read) - dmasound.mach.abort_read(); - for (i = 0; i < sq->numBufs; i++) - dmasound.mach.dma_free(sq->buffers[i], sq->bufSize); - kfree(sq->buffers); - sq->buffers = NULL; - } -} - -static void sq_setup(struct sound_queue *sq, int max_count, int max_active, - int block_size) -{ - void (*setup_func)(void); - - sq->max_count = max_count; - sq->max_active = max_active; - sq->block_size = block_size; - - sq->front = sq->count = sq->rear_size = 0; - sq->syncing = 0; - sq->active = 0; - - if (sq == &write_sq) { - sq->rear = -1; - setup_func = dmasound.mach.write_sq_setup; - } else { - sq->rear = 0; - setup_func = dmasound.mach.read_sq_setup; - } - if (setup_func) - setup_func(); -} - -static inline void sq_play(void) -{ - dmasound.mach.play(); -} - -static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, - loff_t *ppos) -{ - ssize_t uWritten = 0; - u_char *dest; - ssize_t uUsed, bUsed, bLeft; - - /* ++TeSche: Is something like this necessary? - * Hey, that's an honest question! Or does any other part of the - * filesystem already checks this situation? I really don't know. - */ - if (uLeft == 0) - return 0; - - /* The interrupt doesn't start to play the last, incomplete frame. - * Thus we can append to it without disabling the interrupts! (Note - * also that write_sq.rear isn't affected by the interrupt.) - */ - - if (write_sq.count > 0 && - (bLeft = write_sq.block_size-write_sq.rear_size) > 0) { - dest = write_sq.buffers[write_sq.rear]; - bUsed = write_sq.rear_size; - uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, - dest, &bUsed, bLeft); - if (uUsed <= 0) - return uUsed; - src += uUsed; - uWritten += uUsed; - uLeft -= uUsed; - write_sq.rear_size = bUsed; - } - - do { - while (write_sq.count == write_sq.max_active) { - sq_play(); - if (write_sq.open_mode & O_NONBLOCK) - return uWritten > 0 ? uWritten : -EAGAIN; - SLEEP(write_sq.action_queue); - if (signal_pending(current)) - return uWritten > 0 ? uWritten : -EINTR; - } - - /* Here, we can avoid disabling the interrupt by first - * copying and translating the data, and then updating - * the write_sq variables. Until this is done, the interrupt - * won't see the new frame and we can work on it - * undisturbed. - */ - - dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count]; - bUsed = 0; - bLeft = write_sq.block_size; - uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, - dest, &bUsed, bLeft); - if (uUsed <= 0) - break; - src += uUsed; - uWritten += uUsed; - uLeft -= uUsed; - if (bUsed) { - write_sq.rear = (write_sq.rear+1) % write_sq.max_count; - write_sq.rear_size = bUsed; - write_sq.count++; - } - } while (bUsed); /* uUsed may have been 0 */ - - sq_play(); - - return uUsed < 0? uUsed: uWritten; -} - -#ifdef HAS_RECORD - /* - * Here is how the values are used for reading. - * The value 'active' simply indicates the DMA is running. This is done - * so the driver semantics are DMA starts when the first read is posted. - * The value 'front' indicates the buffer we should next send to the user. - * The value 'rear' indicates the buffer the DMA is currently filling. - * When 'front' == 'rear' the buffer "ring" is empty (we always have an - * empty available). The 'rear_size' is used to track partial offsets - * into the current buffer. Right now, I just keep the DMA running. If - * the reader can't keep up, the interrupt tosses the oldest buffer. We - * could also shut down the DMA in this case. - */ - -static ssize_t sq_read(struct file *file, char *dst, size_t uLeft, - loff_t *ppos) -{ - - ssize_t uRead, bLeft, bUsed, uUsed; - - if (uLeft == 0) - return 0; - - if (!read_sq.active && dmasound.mach.record) - dmasound.mach.record(); /* Kick off the record process. */ - - uRead = 0; - - /* Move what the user requests, depending upon other options. - */ - while (uLeft > 0) { - - /* When front == rear, the DMA is not done yet. - */ - while (read_sq.front == read_sq.rear) { - if (read_sq.open_mode & O_NONBLOCK) { - return uRead > 0 ? uRead : -EAGAIN; - } - SLEEP(read_sq.action_queue); - if (signal_pending(current)) - return uRead > 0 ? uRead : -EINTR; - } - - /* The amount we move is either what is left in the - * current buffer or what the user wants. - */ - bLeft = read_sq.block_size - read_sq.rear_size; - bUsed = read_sq.rear_size; - uUsed = sound_copy_translate(dmasound.trans_read, dst, uLeft, - read_sq.buffers[read_sq.front], - &bUsed, bLeft); - if (uUsed <= 0) - return uUsed; - dst += uUsed; - uRead += uUsed; - uLeft -= uUsed; - read_sq.rear_size += bUsed; - if (read_sq.rear_size >= read_sq.block_size) { - read_sq.rear_size = 0; - read_sq.front++; - if (read_sq.front >= read_sq.max_active) - read_sq.front = 0; - } - } - return uRead; -} -#endif /* HAS_RECORD */ - -static inline void sq_init_waitqueue(struct sound_queue *sq) -{ - init_waitqueue_head(&sq->action_queue); - init_waitqueue_head(&sq->open_queue); - init_waitqueue_head(&sq->sync_queue); - sq->busy = 0; -} - -static inline void sq_wake_up(struct sound_queue *sq, struct file *file, - mode_t mode) -{ - if (file->f_mode & mode) { - sq->busy = 0; - WAKE_UP(sq->open_queue); - } -} - -static int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode, - int numbufs, int bufsize) -{ - int rc = 0; - - if (file->f_mode & mode) { - if (sq->busy) { - rc = -EBUSY; - if (file->f_flags & O_NONBLOCK) - return rc; - rc = -EINTR; - while (sq->busy) { - SLEEP(sq->open_queue); - if (signal_pending(current)) - return rc; - } - rc = 0; - } - sq->busy = 1; /* Let's play spot-the-race-condition */ - - if (sq_allocate_buffers(sq, numbufs, bufsize)) { - sq_wake_up(sq, file, mode); - return rc; - } - - sq_setup(sq, numbufs, numbufs, bufsize); - sq->open_mode = file->f_mode; - } - return rc; -} - -#define write_sq_init_waitqueue() sq_init_waitqueue(&write_sq) -#define write_sq_wake_up(file) sq_wake_up(&write_sq, file, FMODE_WRITE) -#define write_sq_release_buffers() sq_release_buffers(&write_sq) -#define write_sq_open(file) \ - sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize << 10) - -#ifdef HAS_RECORD -#define read_sq_init_waitqueue() sq_init_waitqueue(&read_sq) -#define read_sq_wake_up(file) sq_wake_up(&read_sq, file, FMODE_READ) -#define read_sq_release_buffers() sq_release_buffers(&read_sq) -#define read_sq_open(file) \ - sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize << 10) -#else /* !HAS_RECORD */ -#define read_sq_init_waitqueue() do {} while (0) -#define read_sq_wake_up(file) do {} while (0) -#define read_sq_release_buffers() do {} while (0) -#define read_sq_open(file) (0) -#endif /* !HAS_RECORD */ - -static int sq_open(struct inode *inode, struct file *file) -{ - int rc; - - dmasound.mach.open(); - if ((rc = write_sq_open(file)) || (rc = read_sq_open(file))) { - dmasound.mach.release(); - return rc; - } - - if (dmasound.mach.sq_open) - dmasound.mach.sq_open(); - dmasound.minDev = MINOR(inode->i_rdev) & 0x0f; - dmasound.soft = dmasound.dsp; - dmasound.hard = dmasound.dsp; - sound_init(); - if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) { - sound_set_speed(8000); - sound_set_stereo(0); - sound_set_format(AFMT_MU_LAW); - } - -#if 0 - if (file->f_mode == FMODE_READ && dmasound.mach.record) { - /* Start dma'ing straight away */ - dmasound.mach.record(); - } -#endif - - return 0; -} - -static void sq_reset(void) -{ - sound_silence(); - write_sq.active = 0; - write_sq.count = 0; - write_sq.front = (write_sq.rear+1) % write_sq.max_count; -} - -static int sq_fsync(struct file *filp, struct dentry *dentry) -{ - int rc = 0; - - write_sq.syncing = 1; - sq_play(); /* there may be an incomplete frame waiting */ - - while (write_sq.active) { - SLEEP(write_sq.sync_queue); - if (signal_pending(current)) { - /* While waiting for audio output to drain, an - * interrupt occurred. Stop audio output immediately - * and clear the queue. */ - sq_reset(); - rc = -EINTR; - break; - } - } - - write_sq.syncing = 0; - return rc; -} - -static int sq_release(struct inode *inode, struct file *file) -{ - int rc = 0; - - lock_kernel(); - if (write_sq.busy) - rc = sq_fsync(file, file->f_dentry); - dmasound.soft = dmasound.dsp; - dmasound.hard = dmasound.dsp; - sound_silence(); - - write_sq_release_buffers(); - read_sq_release_buffers(); - dmasound.mach.release(); - - /* There is probably a DOS atack here. They change the mode flag. */ - /* XXX add check here */ - read_sq_wake_up(file); - write_sq_wake_up(file); - - /* Wake up a process waiting for the queue being released. - * Note: There may be several processes waiting for a call - * to open() returning. */ - unlock_kernel(); - - return rc; -} - -static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg) -{ - int val; - u_long fmt; - int data; - int size, nbufs; - audio_buf_info info; - - switch (cmd) { - case SNDCTL_DSP_RESET: - sq_reset(); - return 0; - case SNDCTL_DSP_POST: - case SNDCTL_DSP_SYNC: - return sq_fsync(file, file->f_dentry); - - /* ++TeSche: before changing any of these it's - * probably wise to wait until sound playing has - * settled down. */ - case SNDCTL_DSP_SPEED: - sq_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_speed(data)); - case SNDCTL_DSP_STEREO: - sq_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_stereo(data)); - case SOUND_PCM_WRITE_CHANNELS: - sq_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); - case SNDCTL_DSP_SETFMT: - sq_fsync(file, file->f_dentry); - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_format(data)); - case SNDCTL_DSP_GETFMTS: - fmt = 0; - if (dmasound.trans_write) { - if (dmasound.trans_write->ct_ulaw) - fmt |= AFMT_MU_LAW; - if (dmasound.trans_write->ct_alaw) - fmt |= AFMT_A_LAW; - if (dmasound.trans_write->ct_s8) - fmt |= AFMT_S8; - if (dmasound.trans_write->ct_u8) - fmt |= AFMT_U8; - if (dmasound.trans_write->ct_s16be) - fmt |= AFMT_S16_BE; - if (dmasound.trans_write->ct_u16be) - fmt |= AFMT_U16_BE; - if (dmasound.trans_write->ct_s16le) - fmt |= AFMT_S16_LE; - if (dmasound.trans_write->ct_u16le) - fmt |= AFMT_U16_LE; - } - return IOCTL_OUT(arg, fmt); - case SNDCTL_DSP_GETBLKSIZE: - size = write_sq.block_size - * dmasound.soft.size * (dmasound.soft.stereo + 1) - / (dmasound.hard.size * (dmasound.hard.stereo + 1)); - return IOCTL_OUT(arg, size); - case SNDCTL_DSP_SUBDIVIDE: - break; - case SNDCTL_DSP_SETFRAGMENT: - if (write_sq.count || write_sq.active || write_sq.syncing) - return -EINVAL; - IOCTL_IN(arg, size); - nbufs = size >> 16; - if (nbufs < 2 || nbufs > write_sq.numBufs) - nbufs = write_sq.numBufs; - size &= 0xffff; - if (size >= 8 && size <= 29) { - size = 1 << size; - size *= dmasound.hard.size * (dmasound.hard.stereo + 1); - size /= dmasound.soft.size * (dmasound.soft.stereo + 1); - if (size > write_sq.bufSize) - size = write_sq.bufSize; - } else - size = write_sq.bufSize; - sq_setup(&write_sq, write_sq.numBufs, nbufs, size); - return IOCTL_OUT(arg,write_sq.bufSize | write_sq.numBufs << 16); - case SNDCTL_DSP_GETOSPACE: - info.fragments = write_sq.max_active - write_sq.count; - info.fragstotal = write_sq.max_active; - info.fragsize = write_sq.block_size; - info.bytes = info.fragments * info.fragsize; - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - case SNDCTL_DSP_GETCAPS: - val = 1; /* Revision level of this ioctl() */ - return IOCTL_OUT(arg,val); - - default: - return mixer_ioctl(inode, file, cmd, arg); - } - return -EINVAL; -} - -static struct file_operations sq_fops = -{ - owner: THIS_MODULE, - llseek: no_llseek, - write: sq_write, - ioctl: sq_ioctl, - open: sq_open, - release: sq_release, -#ifdef HAS_RECORD - read: sq_read, -#endif -}; - -static void __init sq_init(void) -{ -#ifndef MODULE - int sq_unit; -#endif - sq_unit = register_sound_dsp(&sq_fops, -1); - if (sq_unit < 0) - return; - - write_sq_init_waitqueue(); - read_sq_init_waitqueue(); - - /* whatever you like as startup mode for /dev/dsp, - * (/dev/audio hasn't got a startup mode). note that - * once changed a new open() will *not* restore these! - */ - dmasound.dsp.format = AFMT_U8; - dmasound.dsp.stereo = 0; - dmasound.dsp.size = 8; - - /* set minimum rate possible without expanding */ - dmasound.dsp.speed = dmasound.mach.min_dsp_speed; - - /* before the first open to /dev/dsp this wouldn't be set */ - dmasound.soft = dmasound.dsp; - dmasound.hard = dmasound.dsp; - - sound_silence(); -} - - - /* - * /dev/sndstat - */ - -static struct { - int busy; - char buf[512]; /* state.buf should not overflow! */ - int len, ptr; -} state; - -static int state_open(struct inode *inode, struct file *file) -{ - char *buffer = state.buf; - int len = 0; - - if (state.busy) - return -EBUSY; - - dmasound.mach.open(); - state.ptr = 0; - state.busy = 1; - - len += sprintf(buffer+len, "%sDMA sound driver:\n", dmasound.mach.name); - - len += sprintf(buffer+len, "\tsound.format = 0x%x", - dmasound.soft.format); - switch (dmasound.soft.format) { - case AFMT_MU_LAW: - len += sprintf(buffer+len, " (mu-law)"); - break; - case AFMT_A_LAW: - len += sprintf(buffer+len, " (A-law)"); - break; - case AFMT_U8: - len += sprintf(buffer+len, " (unsigned 8 bit)"); - break; - case AFMT_S8: - len += sprintf(buffer+len, " (signed 8 bit)"); - break; - case AFMT_S16_BE: - len += sprintf(buffer+len, " (signed 16 bit big)"); - break; - case AFMT_U16_BE: - len += sprintf(buffer+len, " (unsigned 16 bit big)"); - break; - case AFMT_S16_LE: - len += sprintf(buffer+len, " (signed 16 bit little)"); - break; - case AFMT_U16_LE: - len += sprintf(buffer+len, " (unsigned 16 bit little)"); - break; - } - len += sprintf(buffer+len, "\n"); - len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n", - dmasound.soft.speed, dmasound.hard.speed); - len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", - dmasound.soft.stereo, - dmasound.soft.stereo ? "stereo" : "mono"); - if (dmasound.mach.state_info) - len += dmasound.mach.state_info(buffer); - len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d" - " sq.max_active = %d\n", - write_sq.block_size, write_sq.max_count, - write_sq.max_active); - len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", - write_sq.count, write_sq.rear_size); - len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n", - write_sq.active, write_sq.syncing); - state.len = len; - return 0; -} - -static int state_release(struct inode *inode, struct file *file) -{ - lock_kernel(); - state.busy = 0; - dmasound.mach.release(); - unlock_kernel(); - return 0; -} - -static ssize_t state_read(struct file *file, char *buf, size_t count, - loff_t *ppos) -{ - int n = state.len - state.ptr; - if (n > count) - n = count; - if (n <= 0) - return 0; - if (copy_to_user(buf, &state.buf[state.ptr], n)) - return -EFAULT; - state.ptr += n; - return n; -} - -static struct file_operations state_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: state_read, - open: state_open, - release: state_release, -}; - -static void __init state_init(void) -{ -#ifndef MODULE - int state_unit; -#endif - state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); - if (state_unit < 0) - return; - state.busy = 0; -} - - - /* - * Config & Setup - * - * This function is called by _one_ chipset-specific driver - */ - -int __init dmasound_init(void) -{ -#ifdef MODULE - if (irq_installed) - return -EBUSY; -#endif - - /* Set up sound queue, /dev/audio and /dev/dsp. */ - - /* Set default settings. */ - sq_init(); - - /* Set up /dev/sndstat. */ - state_init(); - - /* Set up /dev/mixer. */ - mixer_init(); - - if (!dmasound.mach.irqinit()) { - printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n"); - return -ENODEV; - } -#ifdef MODULE - irq_installed = 1; -#endif - - printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", - numWriteBufs, writeBufSize); - - return 0; -} - -#ifdef MODULE - -void dmasound_deinit(void) -{ - if (irq_installed) { - sound_silence(); - dmasound.mach.irqcleanup(); - } - - write_sq_release_buffers(); - read_sq_release_buffers(); - - if (mixer_unit >= 0) - unregister_sound_mixer(mixer_unit); - if (state_unit >= 0) - unregister_sound_special(state_unit); - if (sq_unit >= 0) - unregister_sound_dsp(sq_unit); -} - -#else /* !MODULE */ - -static int __init dmasound_setup(char *str) -{ - int ints[6]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - /* check the bootstrap parameter for "dmasound=" */ - - switch (ints[0]) { - case 3: - if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS)) - printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius); - else - catchRadius = ints[3]; - /* fall through */ - case 2: - if (ints[1] < MIN_BUFFERS) - printk("dmasound_setup: illegal number of buffers, using default = %d\n", numWriteBufs); - else - numWriteBufs = ints[1]; - if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE) - printk("dmasound_setup: illegal buffer size, using default = %dK\n", writeBufSize); - else - writeBufSize = ints[2]; - break; - case 0: - break; - default: - printk("dmasound_setup: illegal number of arguments\n"); - return 0; - } - return 1; -} - -__setup("dmasound=", dmasound_setup); - -#endif /* !MODULE */ - - - /* - * Visible symbols for modules - */ - -EXPORT_SYMBOL(dmasound); -EXPORT_SYMBOL(dmasound_init); -#ifdef MODULE -EXPORT_SYMBOL(dmasound_deinit); -#endif -EXPORT_SYMBOL(dmasound_write_sq); -#ifdef HAS_RECORD -EXPORT_SYMBOL(dmasound_read_sq); -#endif -EXPORT_SYMBOL(dmasound_catchRadius); -#ifdef HAS_8BIT_TABLES -EXPORT_SYMBOL(dmasound_ulaw2dma8); -EXPORT_SYMBOL(dmasound_alaw2dma8); -#endif -#ifdef HAS_16BIT_TABLES -EXPORT_SYMBOL(dmasound_ulaw2dma16); -EXPORT_SYMBOL(dmasound_alaw2dma16); -#endif -#ifdef HAS_14BIT_TABLES -EXPORT_SYMBOL(dmasound_ulaw2dma14l); -EXPORT_SYMBOL(dmasound_ulaw2dma14h); -EXPORT_SYMBOL(dmasound_alaw2dma14l); -EXPORT_SYMBOL(dmasound_alaw2dma14h); -#endif - diff -Nru a/drivers/sound/dmasound/dmasound_paula.c b/drivers/sound/dmasound/dmasound_paula.c --- a/drivers/sound/dmasound/dmasound_paula.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,723 +0,0 @@ - -/* - * linux/drivers/sound/dmasound/dmasound_paula.c - * - * Amiga `Paula' DMA Sound Driver - * - * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits - */ - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "dmasound.h" - - - /* - * The minimum period for audio depends on htotal (for OCS/ECS/AGA) - * (Imported from arch/m68k/amiga/amisound.c) - */ - -extern volatile u_short amiga_audio_min_period; - - - /* - * amiga_mksound() should be able to restore the period after beeping - * (Imported from arch/m68k/amiga/amisound.c) - */ - -extern u_short amiga_audio_period; - - - /* - * Audio DMA masks - */ - -#define AMI_AUDIO_OFF (DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3) -#define AMI_AUDIO_8 (DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1) -#define AMI_AUDIO_14 (AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3) - - - /* - * Helper pointers for 16(14)-bit sound - */ - -static int write_sq_block_size_half, write_sq_block_size_quarter; - - -/*** Low level stuff *********************************************************/ - - -static void AmiOpen(void); -static void AmiRelease(void); -static void *AmiAlloc(unsigned int size, int flags); -static void AmiFree(void *obj, unsigned int size); -static int AmiIrqInit(void); -#ifdef MODULE -static void AmiIrqCleanUp(void); -#endif -static void AmiSilence(void); -static void AmiInit(void); -static int AmiSetFormat(int format); -static int AmiSetVolume(int volume); -static int AmiSetTreble(int treble); -static void AmiPlayNextFrame(int index); -static void AmiPlay(void); -static void AmiInterrupt(int irq, void *dummy, struct pt_regs *fp); - -#ifdef CONFIG_HEARTBEAT - - /* - * Heartbeat interferes with sound since the 7 kHz low-pass filter and the - * power LED are controlled by the same line. - */ - -#ifdef CONFIG_APUS -#define mach_heartbeat ppc_md.heartbeat -#endif - -static void (*saved_heartbeat)(int) = NULL; - -static inline void disable_heartbeat(void) -{ - if (mach_heartbeat) { - saved_heartbeat = mach_heartbeat; - mach_heartbeat = NULL; - } - AmiSetTreble(dmasound.treble); -} - -static inline void enable_heartbeat(void) -{ - if (saved_heartbeat) - mach_heartbeat = saved_heartbeat; -} -#else /* !CONFIG_HEARTBEAT */ -#define disable_heartbeat() do { } while (0) -#define enable_heartbeat() do { } while (0) -#endif /* !CONFIG_HEARTBEAT */ - - -/*** Mid level stuff *********************************************************/ - -static void AmiMixerInit(void); -static int AmiMixerIoctl(u_int cmd, u_long arg); -static void AmiWriteSqSetup(void); -static int AmiStateInfo(char *buffer); - - -/*** Translations ************************************************************/ - -/* ++TeSche: radically changed for new expanding purposes... - * - * These two routines now deal with copying/expanding/translating the samples - * from user space into our buffer at the right frequency. They take care about - * how much data there's actually to read, how much buffer space there is and - * to convert samples into the right frequency/encoding. They will only work on - * complete samples so it may happen they leave some bytes in the input stream - * if the user didn't write a multiple of the current sample size. They both - * return the number of bytes they've used from both streams so you may detect - * such a situation. Luckily all programs should be able to cope with that. - * - * I think I've optimized anything as far as one can do in plain C, all - * variables should fit in registers and the loops are really short. There's - * one loop for every possible situation. Writing a more generalized and thus - * parameterized loop would only produce slower code. Feel free to optimize - * this in assembler if you like. :) - * - * I think these routines belong here because they're not yet really hardware - * independent, especially the fact that the Falcon can play 16bit samples - * only in stereo is hardcoded in both of them! - * - * ++geert: split in even more functions (one per format) - */ - - - /* - * Native format - */ - -static ssize_t ami_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, ssize_t frameLeft) -{ - ssize_t count, used; - - if (!dmasound.soft.stereo) { - void *p = &frame[*frameUsed]; - count = min_t(unsigned long, userCount, frameLeft) & ~1; - used = count; - if (copy_from_user(p, userPtr, count)) - return -EFAULT; - } else { - u_char *left = &frame[*frameUsed>>1]; - u_char *right = left+write_sq_block_size_half; - count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1; - used = count*2; - while (count > 0) { - if (get_user(*left++, userPtr++) - || get_user(*right++, userPtr++)) - return -EFAULT; - count--; - } - } - *frameUsed += used; - return used; -} - - - /* - * Copy and convert 8 bit data - */ - -#define GENERATE_AMI_CT8(funcname, convsample) \ -static ssize_t funcname(const u_char *userPtr, size_t userCount, \ - u_char frame[], ssize_t *frameUsed, \ - ssize_t frameLeft) \ -{ \ - ssize_t count, used; \ - \ - if (!dmasound.soft.stereo) { \ - u_char *p = &frame[*frameUsed]; \ - count = min_t(size_t, userCount, frameLeft) & ~1; \ - used = count; \ - while (count > 0) { \ - u_char data; \ - if (get_user(data, userPtr++)) \ - return -EFAULT; \ - *p++ = convsample(data); \ - count--; \ - } \ - } else { \ - u_char *left = &frame[*frameUsed>>1]; \ - u_char *right = left+write_sq_block_size_half; \ - count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \ - used = count*2; \ - while (count > 0) { \ - u_char data; \ - if (get_user(data, userPtr++)) \ - return -EFAULT; \ - *left++ = convsample(data); \ - if (get_user(data, userPtr++)) \ - return -EFAULT; \ - *right++ = convsample(data); \ - count--; \ - } \ - } \ - *frameUsed += used; \ - return used; \ -} - -#define AMI_CT_ULAW(x) (dmasound_ulaw2dma8[(x)]) -#define AMI_CT_ALAW(x) (dmasound_alaw2dma8[(x)]) -#define AMI_CT_U8(x) ((x) ^ 0x80) - -GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW) -GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW) -GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8) - - - /* - * Copy and convert 16 bit data - */ - -#define GENERATE_AMI_CT_16(funcname, convsample) \ -static ssize_t funcname(const u_char *userPtr, size_t userCount, \ - u_char frame[], ssize_t *frameUsed, \ - ssize_t frameLeft) \ -{ \ - ssize_t count, used; \ - u_short data; \ - \ - if (!dmasound.soft.stereo) { \ - u_char *high = &frame[*frameUsed>>1]; \ - u_char *low = high+write_sq_block_size_half; \ - count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \ - used = count*2; \ - while (count > 0) { \ - if (get_user(data, ((u_short *)userPtr)++)) \ - return -EFAULT; \ - data = convsample(data); \ - *high++ = data>>8; \ - *low++ = (data>>2) & 0x3f; \ - count--; \ - } \ - } else { \ - u_char *lefth = &frame[*frameUsed>>2]; \ - u_char *leftl = lefth+write_sq_block_size_quarter; \ - u_char *righth = lefth+write_sq_block_size_half; \ - u_char *rightl = righth+write_sq_block_size_quarter; \ - count = min_t(size_t, userCount, frameLeft)>>2 & ~1; \ - used = count*4; \ - while (count > 0) { \ - if (get_user(data, ((u_short *)userPtr)++)) \ - return -EFAULT; \ - data = convsample(data); \ - *lefth++ = data>>8; \ - *leftl++ = (data>>2) & 0x3f; \ - if (get_user(data, ((u_short *)userPtr)++)) \ - return -EFAULT; \ - data = convsample(data); \ - *righth++ = data>>8; \ - *rightl++ = (data>>2) & 0x3f; \ - count--; \ - } \ - } \ - *frameUsed += used; \ - return used; \ -} - -#define AMI_CT_S16BE(x) (x) -#define AMI_CT_U16BE(x) ((x) ^ 0x8000) -#define AMI_CT_S16LE(x) (le2be16((x))) -#define AMI_CT_U16LE(x) (le2be16((x)) ^ 0x8000) - -GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE) -GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE) -GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE) -GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE) - - -static TRANS transAmiga = { - ct_ulaw: ami_ct_ulaw, - ct_alaw: ami_ct_alaw, - ct_s8: ami_ct_s8, - ct_u8: ami_ct_u8, - ct_s16be: ami_ct_s16be, - ct_u16be: ami_ct_u16be, - ct_s16le: ami_ct_s16le, - ct_u16le: ami_ct_u16le, -}; - -/*** Low level stuff *********************************************************/ - - -static void AmiOpen(void) -{ - MOD_INC_USE_COUNT; -} - -static void AmiRelease(void) -{ - MOD_DEC_USE_COUNT; -} - -static inline void StopDMA(void) -{ - custom.aud[0].audvol = custom.aud[1].audvol = 0; - custom.aud[2].audvol = custom.aud[3].audvol = 0; - custom.dmacon = AMI_AUDIO_OFF; - enable_heartbeat(); -} - -static void *AmiAlloc(unsigned int size, int flags) -{ - return amiga_chip_alloc((long)size, "dmasound [Paula]"); -} - -static void AmiFree(void *obj, unsigned int size) -{ - amiga_chip_free (obj); -} - -static int __init AmiIrqInit(void) -{ - /* turn off DMA for audio channels */ - StopDMA(); - - /* Register interrupt handler. */ - if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound", - AmiInterrupt)) - return 0; - return 1; -} - -#ifdef MODULE -static void AmiIrqCleanUp(void) -{ - /* turn off DMA for audio channels */ - StopDMA(); - /* release the interrupt */ - free_irq(IRQ_AMIGA_AUD0, AmiInterrupt); -} -#endif /* MODULE */ - -static void AmiSilence(void) -{ - /* turn off DMA for audio channels */ - StopDMA(); -} - - -static void AmiInit(void) -{ - int period, i; - - AmiSilence(); - - if (dmasound.soft.speed) - period = amiga_colorclock/dmasound.soft.speed-1; - else - period = amiga_audio_min_period; - dmasound.hard = dmasound.soft; - dmasound.trans_write = &transAmiga; - - if (period < amiga_audio_min_period) { - /* we would need to squeeze the sound, but we won't do that */ - period = amiga_audio_min_period; - } else if (period > 65535) { - period = 65535; - } - dmasound.hard.speed = amiga_colorclock/(period+1); - - for (i = 0; i < 4; i++) - custom.aud[i].audper = period; - amiga_audio_period = period; -} - - -static int AmiSetFormat(int format) -{ - int size; - - /* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */ - - switch (format) { - case AFMT_QUERY: - return dmasound.soft.format; - case AFMT_MU_LAW: - case AFMT_A_LAW: - case AFMT_U8: - case AFMT_S8: - size = 8; - break; - case AFMT_S16_BE: - case AFMT_U16_BE: - case AFMT_S16_LE: - case AFMT_U16_LE: - size = 16; - break; - default: /* :-) */ - size = 8; - format = AFMT_S8; - } - - dmasound.soft.format = format; - dmasound.soft.size = size; - if (dmasound.minDev == SND_DEV_DSP) { - dmasound.dsp.format = format; - dmasound.dsp.size = dmasound.soft.size; - } - AmiInit(); - - return format; -} - - -#define VOLUME_VOXWARE_TO_AMI(v) \ - (((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100) -#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64) - -static int AmiSetVolume(int volume) -{ - dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff); - custom.aud[0].audvol = dmasound.volume_left; - dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8); - custom.aud[1].audvol = dmasound.volume_right; - if (dmasound.hard.size == 16) { - if (dmasound.volume_left == 64 && dmasound.volume_right == 64) { - custom.aud[2].audvol = 1; - custom.aud[3].audvol = 1; - } else { - custom.aud[2].audvol = 0; - custom.aud[3].audvol = 0; - } - } - return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) | - (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8); -} - -static int AmiSetTreble(int treble) -{ - dmasound.treble = treble; - if (treble < 50) - ciaa.pra &= ~0x02; - else - ciaa.pra |= 0x02; - return treble; -} - - -#define AMI_PLAY_LOADED 1 -#define AMI_PLAY_PLAYING 2 -#define AMI_PLAY_MASK 3 - - -static void AmiPlayNextFrame(int index) -{ - u_char *start, *ch0, *ch1, *ch2, *ch3; - u_long size; - - /* used by AmiPlay() if all doubts whether there really is something - * to be played are already wiped out. - */ - start = write_sq.buffers[write_sq.front]; - size = (write_sq.count == index ? write_sq.rear_size - : write_sq.block_size)>>1; - - if (dmasound.hard.stereo) { - ch0 = start; - ch1 = start+write_sq_block_size_half; - size >>= 1; - } else { - ch0 = start; - ch1 = start; - } - - disable_heartbeat(); - custom.aud[0].audvol = dmasound.volume_left; - custom.aud[1].audvol = dmasound.volume_right; - if (dmasound.hard.size == 8) { - custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0); - custom.aud[0].audlen = size; - custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1); - custom.aud[1].audlen = size; - custom.dmacon = AMI_AUDIO_8; - } else { - size >>= 1; - custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0); - custom.aud[0].audlen = size; - custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1); - custom.aud[1].audlen = size; - if (dmasound.volume_left == 64 && dmasound.volume_right == 64) { - /* We can play pseudo 14-bit only with the maximum volume */ - ch3 = ch0+write_sq_block_size_quarter; - ch2 = ch1+write_sq_block_size_quarter; - custom.aud[2].audvol = 1; /* we are being affected by the beeps */ - custom.aud[3].audvol = 1; /* restoring volume here helps a bit */ - custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2); - custom.aud[2].audlen = size; - custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3); - custom.aud[3].audlen = size; - custom.dmacon = AMI_AUDIO_14; - } else { - custom.aud[2].audvol = 0; - custom.aud[3].audvol = 0; - custom.dmacon = AMI_AUDIO_8; - } - } - write_sq.front = (write_sq.front+1) % write_sq.max_count; - write_sq.active |= AMI_PLAY_LOADED; -} - - -static void AmiPlay(void) -{ - int minframes = 1; - - custom.intena = IF_AUD0; - - if (write_sq.active & AMI_PLAY_LOADED) { - /* There's already a frame loaded */ - custom.intena = IF_SETCLR | IF_AUD0; - return; - } - - if (write_sq.active & AMI_PLAY_PLAYING) - /* Increase threshold: frame 1 is already being played */ - minframes = 2; - - if (write_sq.count < minframes) { - /* Nothing to do */ - custom.intena = IF_SETCLR | IF_AUD0; - return; - } - - if (write_sq.count <= minframes && - write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { - /* hmmm, the only existing frame is not - * yet filled and we're not syncing? - */ - custom.intena = IF_SETCLR | IF_AUD0; - return; - } - - AmiPlayNextFrame(minframes); - - custom.intena = IF_SETCLR | IF_AUD0; -} - - -static void AmiInterrupt(int irq, void *dummy, struct pt_regs *fp) -{ - int minframes = 1; - - custom.intena = IF_AUD0; - - if (!write_sq.active) { - /* Playing was interrupted and sq_reset() has already cleared - * the sq variables, so better don't do anything here. - */ - WAKE_UP(write_sq.sync_queue); - return; - } - - if (write_sq.active & AMI_PLAY_PLAYING) { - /* We've just finished a frame */ - write_sq.count--; - WAKE_UP(write_sq.action_queue); - } - - if (write_sq.active & AMI_PLAY_LOADED) - /* Increase threshold: frame 1 is already being played */ - minframes = 2; - - /* Shift the flags */ - write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK; - - if (!write_sq.active) - /* No frame is playing, disable audio DMA */ - StopDMA(); - - custom.intena = IF_SETCLR | IF_AUD0; - - if (write_sq.count >= minframes) - /* Try to play the next frame */ - AmiPlay(); - - if (!write_sq.active) - /* Nothing to play anymore. - Wake up a process waiting for audio output to drain. */ - WAKE_UP(write_sq.sync_queue); -} - -/*** Mid level stuff *********************************************************/ - - -/* - * /dev/mixer abstraction - */ - -static void __init AmiMixerInit(void) -{ - dmasound.volume_left = 64; - dmasound.volume_right = 64; - custom.aud[0].audvol = dmasound.volume_left; - custom.aud[3].audvol = 1; /* For pseudo 14bit */ - custom.aud[1].audvol = dmasound.volume_right; - custom.aud[2].audvol = 1; /* For pseudo 14bit */ - dmasound.treble = 50; -} - -static int AmiMixerIoctl(u_int cmd, u_long arg) -{ - int data; - switch (cmd) { - case SOUND_MIXER_READ_DEVMASK: - return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE); - case SOUND_MIXER_READ_RECMASK: - return IOCTL_OUT(arg, 0); - case SOUND_MIXER_READ_STEREODEVS: - return IOCTL_OUT(arg, SOUND_MASK_VOLUME); - case SOUND_MIXER_READ_VOLUME: - return IOCTL_OUT(arg, - VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) | - VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8); - case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, dmasound_set_volume(data)); - case SOUND_MIXER_READ_TREBLE: - return IOCTL_OUT(arg, dmasound.treble); - case SOUND_MIXER_WRITE_TREBLE: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, dmasound_set_treble(data)); - } - return -EINVAL; -} - - -static void AmiWriteSqSetup(void) -{ - write_sq_block_size_half = write_sq.block_size>>1; - write_sq_block_size_quarter = write_sq_block_size_half>>1; -} - - -static int AmiStateInfo(char *buffer) -{ - int len = 0; - len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n", - dmasound.volume_left); - len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n", - dmasound.volume_right); - return len; -} - - -/*** Machine definitions *****************************************************/ - - -static MACHINE machAmiga = { - name: "Amiga", - name2: "AMIGA", - open: AmiOpen, - release: AmiRelease, - dma_alloc: AmiAlloc, - dma_free: AmiFree, - irqinit: AmiIrqInit, -#ifdef MODULE - irqcleanup: AmiIrqCleanUp, -#endif /* MODULE */ - init: AmiInit, - silence: AmiSilence, - setFormat: AmiSetFormat, - setVolume: AmiSetVolume, - setTreble: AmiSetTreble, - play: AmiPlay, - mixer_init: AmiMixerInit, - mixer_ioctl: AmiMixerIoctl, - write_sq_setup: AmiWriteSqSetup, - state_info: AmiStateInfo, - min_dsp_speed: 8000 -}; - - -/*** Config & Setup **********************************************************/ - - -int __init dmasound_paula_init(void) -{ - int err; - - if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_AUDIO)) { - if (!request_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40, - "dmasound [Paula]")) - return -EBUSY; - dmasound.mach = machAmiga; - err = dmasound_init(); - if (err) - release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40); - return err; - } else - return -ENODEV; -} - -static void __exit dmasound_paula_cleanup(void) -{ - dmasound_deinit(); - release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40); -} - -module_init(dmasound_paula_init); -module_exit(dmasound_paula_cleanup); -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/dmasound/dmasound_q40.c b/drivers/sound/dmasound/dmasound_q40.c --- a/drivers/sound/dmasound/dmasound_q40.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,588 +0,0 @@ - -/* - * linux/drivers/sound/dmasound/dmasound_q40.c - * - * Q40 DMA Sound Driver - * - * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits - */ - - -#include -#include -#include -#include - -#include -#include - -#include "dmasound.h" - - -static int expand_bal; /* Balance factor for expanding (not volume!) */ -static int expand_data; /* Data for expanding */ - - -/*** Low level stuff *********************************************************/ - - -static void Q40Open(void); -static void Q40Release(void); -static void *Q40Alloc(unsigned int size, int flags); -static void Q40Free(void *, unsigned int); -static int Q40IrqInit(void); -#ifdef MODULE -static void Q40IrqCleanUp(void); -#endif -static void Q40Silence(void); -static void Q40Init(void); -static int Q40SetFormat(int format); -static int Q40SetVolume(int volume); -static void Q40PlayNextFrame(int index); -static void Q40Play(void); -static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp); -static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp); -static void Q40Interrupt(void); - - -/*** Mid level stuff *********************************************************/ - - -#if 1 -/* userCount, frameUsed, frameLeft == byte counts */ -static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8; - ssize_t count, used; - u_char *p = (u_char *) &frame[*frameUsed]; - - used = count = min_t(size_t, userCount, frameLeft); - if (copy_from_user(p,userPtr,count)) - return -EFAULT; - while (count > 0) { - *p = table[*p]+128; - p++; - count--; - } - *frameUsed += used ; - return used; -} -#else -static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8; - ssize_t count, used; - u_char *p = (u_char *) &frame[*frameUsed]; - u_char val; - int stereo = sound.soft.stereo; - - - frameLeft >>= 1; - if (stereo) - userCount >>= 1; - used = count = min_t(size_t, userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = table[data]+128; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = table[data]+128; - } - *p++ = val; - count--; - } - *frameUsed += used * 2; - return stereo? used * 2: used; -} -#endif - -#if 1 -static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - u_char *p = (u_char *) &frame[*frameUsed]; - - used = count = min_t(size_t, userCount, frameLeft); - if (copy_from_user(p,userPtr,count)) - return -EFAULT; - while (count > 0) { - *p = *p + 128; - p++; - count--; - } - *frameUsed += used; - return used; -} -#else -static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - u_char *p = (u_char *) &frame[*frameUsed]; - u_char val; - int stereo = dmasound.soft.stereo; - - frameLeft >>= 1; - if (stereo) - userCount >>= 1; - used = count = min_t(size_t, userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = data + 128; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = data + 128; - } - *p++ = val; - count--; - } - *frameUsed += used * 2; - return stereo? used * 2: used; -} -#endif - -#if 1 -static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - u_char *p = (u_char *) &frame[*frameUsed]; - - used = count = min_t(size_t, userCount, frameLeft); - if (copy_from_user(p,userPtr,count)) - return -EFAULT; - *frameUsed += used; - return used; -} -#else -static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - ssize_t count, used; - u_char *p = (u_char *) &frame[*frameUsed]; - u_char val; - int stereo = dmasound.soft.stereo; - - - frameLeft >>= 1; - if (stereo) - userCount >>= 1; - used = count = min_t(size_t, userCount, frameLeft); - while (count > 0) { - u_char data; - if (get_user(data, userPtr++)) - return -EFAULT; - val = data; - *p++ = val; - if (stereo) { - if (get_user(data, userPtr++)) - return -EFAULT; - val = data; - } - *p++ = val; - count--; - } - *frameUsed += used * 2; - return stereo? used * 2: used; -} -#endif - -/* a bit too complicated to optimise right now ..*/ -static ssize_t q40_ctx_law(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - unsigned char *table = (unsigned char *) - (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8); - unsigned int data = expand_data; - u_char *p = (u_char *) &frame[*frameUsed]; - int bal = expand_bal; - int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - int utotal, ftotal; - - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = table[c]; - data += 0x80; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft); - utotal -= userCount; - return utotal; -} - - -static ssize_t q40_ctx_s8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - u_char *p = (u_char *) &frame[*frameUsed]; - unsigned int data = expand_data; - int bal = expand_bal; - int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - int utotal, ftotal; - - - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = c ; - data += 0x80; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft); - utotal -= userCount; - return utotal; -} - - -static ssize_t q40_ctx_u8(const u_char *userPtr, size_t userCount, - u_char frame[], ssize_t *frameUsed, - ssize_t frameLeft) -{ - u_char *p = (u_char *) &frame[*frameUsed]; - unsigned int data = expand_data; - int bal = expand_bal; - int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; - int utotal, ftotal; - - ftotal = frameLeft; - utotal = userCount; - while (frameLeft) { - u_char c; - if (bal < 0) { - if (userCount == 0) - break; - if (get_user(c, userPtr++)) - return -EFAULT; - data = c ; - userCount--; - bal += hSpeed; - } - *p++ = data; - frameLeft--; - bal -= sSpeed; - } - expand_bal = bal; - expand_data = data; - *frameUsed += (ftotal - frameLeft) ; - utotal -= userCount; - return utotal; -} - - -static TRANS transQ40Normal = { - q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL -}; - -static TRANS transQ40Expanding = { - q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL -}; - - -/*** Low level stuff *********************************************************/ - - -static void Q40Open(void) -{ - MOD_INC_USE_COUNT; -} - -static void Q40Release(void) -{ - MOD_DEC_USE_COUNT; -} - - -static void *Q40Alloc(unsigned int size, int flags) -{ - return kmalloc(size, flags); /* change to vmalloc */ -} - -static void Q40Free(void *ptr, unsigned int size) -{ - kfree(ptr); -} - -static int __init Q40IrqInit(void) -{ - /* Register interrupt handler. */ - request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, - "DMA sound", Q40Interrupt); - - return(1); -} - - -#ifdef MODULE -static void Q40IrqCleanUp(void) -{ - master_outb(0,SAMPLE_ENABLE_REG); - free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); -} -#endif /* MODULE */ - - -static void Q40Silence(void) -{ - master_outb(0,SAMPLE_ENABLE_REG); - *DAC_LEFT=*DAC_RIGHT=0; -} - -static char *q40_pp=NULL; -static unsigned int q40_sc=0; - -static void Q40PlayNextFrame(int index) -{ - u_char *start; - u_long size; - u_char speed; - - /* used by Q40Play() if all doubts whether there really is something - * to be played are already wiped out. - */ - start = write_sq.buffers[write_sq.front]; - size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size); - - q40_pp=start; - q40_sc=size; - - write_sq.front = (write_sq.front+1) % write_sq.max_count; - write_sq.active++; - - speed=(dmasound.hard.speed==10000 ? 0 : 1); - - master_outb( 0,SAMPLE_ENABLE_REG); - free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); - if (dmasound.soft.stereo) - request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, - "Q40 sound", Q40Interrupt); - else - request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0, - "Q40 sound", Q40Interrupt); - - master_outb( speed, SAMPLE_RATE_REG); - master_outb( 1,SAMPLE_CLEAR_REG); - master_outb( 1,SAMPLE_ENABLE_REG); -} - -static void Q40Play(void) -{ - unsigned long flags; - - if (write_sq.active || write_sq.count<=0 ) { - /* There's already a frame loaded */ - return; - } - - /* nothing in the queue */ - if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { - /* hmmm, the only existing frame is not - * yet filled and we're not syncing? - */ - return; - } - save_flags(flags); cli(); - Q40PlayNextFrame(1); - restore_flags(flags); -} - -static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp) -{ - if (q40_sc>1){ - *DAC_LEFT=*q40_pp++; - *DAC_RIGHT=*q40_pp++; - q40_sc -=2; - master_outb(1,SAMPLE_CLEAR_REG); - }else Q40Interrupt(); -} -static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp) -{ - if (q40_sc>0){ - *DAC_LEFT=*q40_pp; - *DAC_RIGHT=*q40_pp++; - q40_sc --; - master_outb(1,SAMPLE_CLEAR_REG); - }else Q40Interrupt(); -} -static void Q40Interrupt(void) -{ - if (!write_sq.active) { - /* playing was interrupted and sq_reset() has already cleared - * the sq variables, so better don't do anything here. - */ - WAKE_UP(write_sq.sync_queue); - master_outb(0,SAMPLE_ENABLE_REG); /* better safe */ - goto exit; - } else write_sq.active=0; - write_sq.count--; - Q40Play(); - - if (q40_sc<2) - { /* there was nothing to play, disable irq */ - master_outb(0,SAMPLE_ENABLE_REG); - *DAC_LEFT=*DAC_RIGHT=0; - } - WAKE_UP(write_sq.action_queue); - - exit: - master_outb(1,SAMPLE_CLEAR_REG); -} - - -static void Q40Init(void) -{ - int i, idx; - const int freq[] = {10000, 20000}; - - /* search a frequency that fits into the allowed error range */ - - idx = -1; - for (i = 0; i < 2; i++) - if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius) - idx = i; - - dmasound.hard = dmasound.soft; - /*sound.hard.stereo=1;*/ /* no longer true */ - dmasound.hard.size=8; - - if (idx > -1) { - dmasound.soft.speed = freq[idx]; - dmasound.trans_write = &transQ40Normal; - } else - dmasound.trans_write = &transQ40Expanding; - - Q40Silence(); - - if (dmasound.hard.speed > 20000) { - /* we would need to squeeze the sound, but we won't do that */ - dmasound.hard.speed = 20000; - dmasound.trans_write = &transQ40Normal; - } else if (dmasound.hard.speed > 10000) { - dmasound.hard.speed = 20000; - } else { - dmasound.hard.speed = 10000; - } - expand_bal = -dmasound.soft.speed; -} - - -static int Q40SetFormat(int format) -{ - /* Q40 sound supports only 8bit modes */ - - switch (format) { - case AFMT_QUERY: - return(dmasound.soft.format); - case AFMT_MU_LAW: - case AFMT_A_LAW: - case AFMT_S8: - case AFMT_U8: - break; - default: - format = AFMT_S8; - } - - dmasound.soft.format = format; - dmasound.soft.size = 8; - if (dmasound.minDev == SND_DEV_DSP) { - dmasound.dsp.format = format; - dmasound.dsp.size = 8; - } - Q40Init(); - - return(format); -} - -static int Q40SetVolume(int volume) -{ - return 0; -} - - -/*** Machine definitions *****************************************************/ - - -static MACHINE machQ40 = { - name: "Q40", - name2: "Q40", - open: Q40Open, - release: Q40Release, - dma_alloc: Q40Alloc, - dma_free: Q40Free, - irqinit: Q40IrqInit, -#ifdef MODULE - irqcleanup: Q40IrqCleanUp, -#endif /* MODULE */ - init: Q40Init, - silence: Q40Silence, - setFormat: Q40SetFormat, - setVolume: Q40SetVolume, - play: Q40Play -}; - - -/*** Config & Setup **********************************************************/ - - -int __init dmasound_q40_init(void) -{ - if (MACH_IS_Q40) { - dmasound.mach = machQ40; - return dmasound_init(); - } else - return -ENODEV; -} - -static void __exit dmasound_q40_cleanup(void) -{ - dmasound_deinit(); -} - -module_init(dmasound_q40_init); -module_exit(dmasound_q40_cleanup); -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/emu10k1/8010.h b/drivers/sound/emu10k1/8010.h --- a/drivers/sound/emu10k1/8010.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,624 +0,0 @@ -/* - ********************************************************************** - * 8010.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * November 2, 1999 Alan Cox Cleaned of 8bit chars, DOS - * line endings - * December 8, 1999 Jon Taylor Added lots of new register info - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - * - ********************************************************************** - */ - - -#ifndef _8010_H -#define _8010_H - -#include - -/************************************************************************************************/ -/* PCI function 0 registers, address = + PCIBASE0 */ -/************************************************************************************************/ - -#define PTR 0x00 /* Indexed register set pointer register */ - /* NOTE: The CHANNELNUM and ADDRESS words can */ - /* be modified independently of each other. */ -#define PTR_CHANNELNUM_MASK 0x0000003f /* For each per-channel register, indicates the */ - /* channel number of the register to be */ - /* accessed. For non per-channel registers the */ - /* value should be set to zero. */ -#define PTR_ADDRESS_MASK 0x07ff0000 /* Register index */ - -#define DATA 0x04 /* Indexed register set data register */ - -#define IPR 0x08 /* Global interrupt pending register */ - /* Clear pending interrupts by writing a 1 to */ - /* the relevant bits and zero to the other bits */ -#define IPR_SAMPLERATETRACKER 0x01000000 /* Sample rate tracker lock status change */ -#define IPR_FXDSP 0x00800000 /* Enable FX DSP interrupts */ -#define IPR_FORCEINT 0x00400000 /* Force Sound Blaster interrupt */ -#define IPR_PCIERROR 0x00200000 /* PCI bus error */ -#define IPR_VOLINCR 0x00100000 /* Volume increment button pressed */ -#define IPR_VOLDECR 0x00080000 /* Volume decrement button pressed */ -#define IPR_MUTE 0x00040000 /* Mute button pressed */ -#define IPR_MICBUFFULL 0x00020000 /* Microphone buffer full */ -#define IPR_MICBUFHALFFULL 0x00010000 /* Microphone buffer half full */ -#define IPR_ADCBUFFULL 0x00008000 /* ADC buffer full */ -#define IPR_ADCBUFHALFFULL 0x00004000 /* ADC buffer half full */ -#define IPR_EFXBUFFULL 0x00002000 /* Effects buffer full */ -#define IPR_EFXBUFHALFFULL 0x00001000 /* Effects buffer half full */ -#define IPR_GPSPDIFSTATUSCHANGE 0x00000800 /* GPSPDIF channel status change */ -#define IPR_CDROMSTATUSCHANGE 0x00000400 /* CD-ROM channel status change */ -#define IPR_INTERVALTIMER 0x00000200 /* Interval timer terminal count */ -#define IPR_MIDITRANSBUFEMPTY 0x00000100 /* MIDI UART transmit buffer empty */ -#define IPR_MIDIRECVBUFEMPTY 0x00000080 /* MIDI UART receive buffer empty */ -#define IPR_CHANNELLOOP 0x00000040 /* One or more channel loop interrupts pending */ -#define IPR_CHANNELNUMBERMASK 0x0000003f /* When IPR_CHANNELLOOP is set, indicates the */ - /* Highest set channel in CLIPL or CLIPH. When */ - /* IP is written with CL set, the bit in CLIPL */ - /* or CLIPH corresponding to the CIN value */ - /* written will be cleared. */ - -#define INTE 0x0c /* Interrupt enable register */ -#define INTE_VIRTUALSB_MASK 0xc0000000 /* Virtual Soundblaster I/O port capture */ -#define INTE_VIRTUALSB_220 0x00000000 /* Capture at I/O base address 0x220-0x22f */ -#define INTE_VIRTUALSB_240 0x40000000 /* Capture at I/O base address 0x240 */ -#define INTE_VIRTUALSB_260 0x80000000 /* Capture at I/O base address 0x260 */ -#define INTE_VIRTUALSB_280 0xc0000000 /* Capture at I/O base address 0x280 */ -#define INTE_VIRTUALMPU_MASK 0x30000000 /* Virtual MPU I/O port capture */ -#define INTE_VIRTUALMPU_300 0x00000000 /* Capture at I/O base address 0x300-0x301 */ -#define INTE_VIRTUALMPU_310 0x10000000 /* Capture at I/O base address 0x310 */ -#define INTE_VIRTUALMPU_320 0x20000000 /* Capture at I/O base address 0x320 */ -#define INTE_VIRTUALMPU_330 0x30000000 /* Capture at I/O base address 0x330 */ -#define INTE_MASTERDMAENABLE 0x08000000 /* Master DMA emulation at 0x000-0x00f */ -#define INTE_SLAVEDMAENABLE 0x04000000 /* Slave DMA emulation at 0x0c0-0x0df */ -#define INTE_MASTERPICENABLE 0x02000000 /* Master PIC emulation at 0x020-0x021 */ -#define INTE_SLAVEPICENABLE 0x01000000 /* Slave PIC emulation at 0x0a0-0x0a1 */ -#define INTE_VSBENABLE 0x00800000 /* Enable virtual Soundblaster */ -#define INTE_ADLIBENABLE 0x00400000 /* Enable AdLib emulation at 0x388-0x38b */ -#define INTE_MPUENABLE 0x00200000 /* Enable virtual MPU */ -#define INTE_FORCEINT 0x00100000 /* Continuously assert INTAN */ - -#define INTE_MRHANDENABLE 0x00080000 /* Enable the "Mr. Hand" logic */ - /* NOTE: There is no reason to use this under */ - /* Linux, and it will cause odd hardware */ - /* behavior and possibly random segfaults and */ - /* lockups if enabled. */ - -#define INTE_SAMPLERATETRACKER 0x00002000 /* Enable sample rate tracker interrupts */ - /* NOTE: This bit must always be enabled */ -#define INTE_FXDSPENABLE 0x00001000 /* Enable FX DSP interrupts */ -#define INTE_PCIERRORENABLE 0x00000800 /* Enable PCI bus error interrupts */ -#define INTE_VOLINCRENABLE 0x00000400 /* Enable volume increment button interrupts */ -#define INTE_VOLDECRENABLE 0x00000200 /* Enable volume decrement button interrupts */ -#define INTE_MUTEENABLE 0x00000100 /* Enable mute button interrupts */ -#define INTE_MICBUFENABLE 0x00000080 /* Enable microphone buffer interrupts */ -#define INTE_ADCBUFENABLE 0x00000040 /* Enable ADC buffer interrupts */ -#define INTE_EFXBUFENABLE 0x00000020 /* Enable Effects buffer interrupts */ -#define INTE_GPSPDIFENABLE 0x00000010 /* Enable GPSPDIF status interrupts */ -#define INTE_CDSPDIFENABLE 0x00000008 /* Enable CDSPDIF status interrupts */ -#define INTE_INTERVALTIMERENB 0x00000004 /* Enable interval timer interrupts */ -#define INTE_MIDITXENABLE 0x00000002 /* Enable MIDI transmit-buffer-empty interrupts */ -#define INTE_MIDIRXENABLE 0x00000001 /* Enable MIDI receive-buffer-empty interrupts */ - -#define WC 0x10 /* Wall Clock register */ -#define WC_SAMPLECOUNTER_MASK 0x03FFFFC0 /* Sample periods elapsed since reset */ -#define WC_SAMPLECOUNTER 0x14060010 -#define WC_CURRENTCHANNEL 0x0000003F /* Channel [0..63] currently being serviced */ - /* NOTE: Each channel takes 1/64th of a sample */ - /* period to be serviced. */ - -#define HCFG 0x14 /* Hardware config register */ - /* NOTE: There is no reason to use the legacy */ - /* SoundBlaster emulation stuff described below */ - /* under Linux, and all kinds of weird hardware */ - /* behavior can result if you try. Don't. */ -#define HCFG_LEGACYFUNC_MASK 0xe0000000 /* Legacy function number */ -#define HCFG_LEGACYFUNC_MPU 0x00000000 /* Legacy MPU */ -#define HCFG_LEGACYFUNC_SB 0x40000000 /* Legacy SB */ -#define HCFG_LEGACYFUNC_AD 0x60000000 /* Legacy AD */ -#define HCFG_LEGACYFUNC_MPIC 0x80000000 /* Legacy MPIC */ -#define HCFG_LEGACYFUNC_MDMA 0xa0000000 /* Legacy MDMA */ -#define HCFG_LEGACYFUNC_SPCI 0xc0000000 /* Legacy SPCI */ -#define HCFG_LEGACYFUNC_SDMA 0xe0000000 /* Legacy SDMA */ -#define HCFG_IOCAPTUREADDR 0x1f000000 /* The 4 LSBs of the captured I/O address. */ -#define HCFG_LEGACYWRITE 0x00800000 /* 1 = write, 0 = read */ -#define HCFG_LEGACYWORD 0x00400000 /* 1 = word, 0 = byte */ -#define HCFG_LEGACYINT 0x00200000 /* 1 = legacy event captured. Write 1 to clear. */ - /* NOTE: The rest of the bits in this register */ - /* _are_ relevant under Linux. */ -#define HCFG_CODECFORMAT_MASK 0x00070000 /* CODEC format */ -#define HCFG_CODECFORMAT_AC97 0x00000000 /* AC97 CODEC format -- Primary Output */ -#define HCFG_CODECFORMAT_I2S 0x00010000 /* I2S CODEC format -- Secondary (Rear) Output */ -#define HCFG_GPINPUT0 0x00004000 /* External pin112 */ -#define HCFG_GPINPUT1 0x00002000 /* External pin110 */ - -#define HCFG_GPOUTPUT_MASK 0x00001c00 /* External pins which may be controlled */ -#define HCFG_GPOUT0 0x00001000 /* set to enable digital out on 5.1 cards */ - -#define HCFG_JOYENABLE 0x00000200 /* Internal joystick enable */ -#define HCFG_PHASETRACKENABLE 0x00000100 /* Phase tracking enable */ - /* 1 = Force all 3 async digital inputs to use */ - /* the same async sample rate tracker (ZVIDEO) */ -#define HCFG_AC3ENABLE_MASK 0x0x0000e0 /* AC3 async input control - Not implemented */ -#define HCFG_AC3ENABLE_ZVIDEO 0x00000080 /* Channels 0 and 1 replace ZVIDEO */ -#define HCFG_AC3ENABLE_CDSPDIF 0x00000040 /* Channels 0 and 1 replace CDSPDIF */ -#define HCFG_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */ -#define HCFG_AUTOMUTE 0x00000010 /* When set, the async sample rate convertors */ - /* will automatically mute their output when */ - /* they are not rate-locked to the external */ - /* async audio source */ -#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ - /* NOTE: This should generally never be used. */ -#define HCFG_LOCKTANKCACHE_MASK 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ - /* NOTE: This should generally never be used. */ -#define HCFG_LOCKTANKCACHE 0x01020014 -#define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Master mute button sets AUDIOENABLE = 0. */ - /* NOTE: This is a 'cheap' way to implement a */ - /* master mute function on the mute button, and */ - /* in general should not be used unless a more */ - /* sophisticated master mute function has not */ - /* been written. */ -#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ - /* Should be set to 1 when the EMU10K1 is */ - /* completely initialized. */ - -#define MUDATA 0x18 /* MPU401 data register (8 bits) */ - -#define MUCMD 0x19 /* MPU401 command register (8 bits) */ -#define MUCMD_RESET 0xff /* RESET command */ -#define MUCMD_ENTERUARTMODE 0x3f /* Enter_UART_mode command */ - /* NOTE: All other commands are ignored */ - -#define MUSTAT MUCMD /* MPU401 status register (8 bits) */ -#define MUSTAT_IRDYN 0x80 /* 0 = MIDI data or command ACK */ -#define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ - -#define TIMER 0x1a /* Timer terminal count register */ - /* NOTE: After the rate is changed, a maximum */ - /* of 1024 sample periods should be allowed */ - /* before the new rate is guaranteed accurate. */ -#define TIMER_RATE_MASK 0x000003ff /* Timer interrupt rate in sample periods */ - /* 0 == 1024 periods, [1..4] are not useful */ -#define TIMER_RATE 0x0a00001a - -#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ - -#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ -#define AC97ADDRESS_READY 0x80 /* Read-only bit, reflects CODEC READY signal */ -#define AC97ADDRESS_ADDRESS 0x7f /* Address of indexed AC97 register */ - -/********************************************************************************************************/ -/* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers */ -/********************************************************************************************************/ - -#define CPF 0x00 /* Current pitch and fraction register */ -#define CPF_CURRENTPITCH_MASK 0xffff0000 /* Current pitch (linear, 0x4000 == unity pitch shift) */ -#define CPF_CURRENTPITCH 0x10100000 -#define CPF_STEREO_MASK 0x00008000 /* 1 = Even channel interleave, odd channel locked */ -#define CPF_STOP_MASK 0x00004000 /* 1 = Current pitch forced to 0 */ -#define CPF_FRACADDRESS_MASK 0x00003fff /* Linear fractional address of the current channel */ - -#define PTRX 0x01 /* Pitch target and send A/B amounts register */ -#define PTRX_PITCHTARGET_MASK 0xffff0000 /* Pitch target of specified channel */ -#define PTRX_PITCHTARGET 0x10100001 -#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00 /* Linear level of channel output sent to FX send bus A */ -#define PTRX_FXSENDAMOUNT_A 0x08080001 -#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff /* Linear level of channel output sent to FX send bus B */ -#define PTRX_FXSENDAMOUNT_B 0x08000001 - -#define CVCF 0x02 /* Current volume and filter cutoff register */ -#define CVCF_CURRENTVOL_MASK 0xffff0000 /* Current linear volume of specified channel */ -#define CVCF_CURRENTVOL 0x10100002 -#define CVCF_CURRENTFILTER_MASK 0x0000ffff /* Current filter cutoff frequency of specified channel */ -#define CVCF_CURRENTFILTER 0x10000002 - -#define VTFT 0x03 /* Volume target and filter cutoff target register */ -#define VTFT_VOLUMETARGET_MASK 0xffff0000 /* Volume target of specified channel */ -#define VTFT_FILTERTARGET_MASK 0x0000ffff /* Filter cutoff target of specified channel */ - -#define Z1 0x05 /* Filter delay memory 1 register */ - -#define Z2 0x04 /* Filter delay memory 2 register */ - -#define PSST 0x06 /* Send C amount and loop start address register */ -#define PSST_FXSENDAMOUNT_C_MASK 0xff000000 /* Linear level of channel output sent to FX send bus C */ - -#define PSST_FXSENDAMOUNT_C 0x08180006 - -#define PSST_LOOPSTARTADDR_MASK 0x00ffffff /* Loop start address of the specified channel */ -#define PSST_LOOPSTARTADDR 0x18000006 - -#define DSL 0x07 /* Send D amount and loop start address register */ -#define DSL_FXSENDAMOUNT_D_MASK 0xff000000 /* Linear level of channel output sent to FX send bus D */ - -#define DSL_FXSENDAMOUNT_D 0x08180007 - -#define DSL_LOOPENDADDR_MASK 0x00ffffff /* Loop end address of the specified channel */ -#define DSL_LOOPENDADDR 0x18000007 - -#define CCCA 0x08 /* Filter Q, interp. ROM, byte size, cur. addr register */ -#define CCCA_RESONANCE 0xf0000000 /* Lowpass filter resonance (Q) height */ -#define CCCA_INTERPROMMASK 0x0e000000 /* Selects passband of interpolation ROM */ - /* 1 == full band, 7 == lowpass */ - /* ROM 0 is used when pitch shifting downward or less */ - /* then 3 semitones upward. Increasingly higher ROM */ - /* numbers are used, typically in steps of 3 semitones, */ - /* as upward pitch shifting is performed. */ -#define CCCA_INTERPROM_0 0x00000000 /* Select interpolation ROM 0 */ -#define CCCA_INTERPROM_1 0x02000000 /* Select interpolation ROM 1 */ -#define CCCA_INTERPROM_2 0x04000000 /* Select interpolation ROM 2 */ -#define CCCA_INTERPROM_3 0x06000000 /* Select interpolation ROM 3 */ -#define CCCA_INTERPROM_4 0x08000000 /* Select interpolation ROM 4 */ -#define CCCA_INTERPROM_5 0x0a000000 /* Select interpolation ROM 5 */ -#define CCCA_INTERPROM_6 0x0c000000 /* Select interpolation ROM 6 */ -#define CCCA_INTERPROM_7 0x0e000000 /* Select interpolation ROM 7 */ -#define CCCA_8BITSELECT 0x01000000 /* 1 = Sound memory for this channel uses 8-bit samples */ -#define CCCA_CURRADDR_MASK 0x00ffffff /* Current address of the selected channel */ -#define CCCA_CURRADDR 0x18000008 - -#define CCR 0x09 /* Cache control register */ -#define CCR_CACHEINVALIDSIZE 0x07190009 -#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000 /* Number of invalid samples cache for this channel */ -#define CCR_CACHELOOPFLAG 0x01000000 /* 1 = Cache has a loop service pending */ -#define CCR_INTERLEAVEDSAMPLES 0x00800000 /* 1 = A cache service will fetch interleaved samples */ -#define CCR_WORDSIZEDSAMPLES 0x00400000 /* 1 = A cache service will fetch word sized samples */ -#define CCR_READADDRESS 0x06100009 -#define CCR_READADDRESS_MASK 0x003f0000 /* Location of cache just beyond current cache service */ -#define CCR_LOOPINVALSIZE 0x0000fe00 /* Number of invalid samples in cache prior to loop */ - /* NOTE: This is valid only if CACHELOOPFLAG is set */ -#define CCR_LOOPFLAG 0x00000100 /* Set for a single sample period when a loop occurs */ -#define CCR_CACHELOOPADDRHI 0x000000ff /* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */ - -#define CLP 0x0a /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */ - /* NOTE: This register is normally not used */ -#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address (DSL_LOOPSTARTADDR [0..15]) */ - -#define FXRT 0x0b /* Effects send routing register */ - /* NOTE: It is illegal to assign the same routing to */ - /* two effects sends. */ -#define FXRT_CHANNELA 0x000f0000 /* Effects send bus number for channel's effects send A */ -#define FXRT_CHANNELB 0x00f00000 /* Effects send bus number for channel's effects send B */ -#define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */ -#define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */ - -#define MAPA 0x0c /* Cache map A */ - -#define MAPB 0x0d /* Cache map B */ - -#define MAP_PTE_MASK 0xffffe000 /* The 19 MSBs of the PTE indexed by the PTI */ -#define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */ - -#define ENVVOL 0x10 /* Volume envelope register */ -#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ - /* 0x8000-n == 666*n usec delay */ - -#define ATKHLDV 0x11 /* Volume envelope hold and attack register */ -#define ATKHLDV_PHASE0 0x00008000 /* 0 = Begin attack phase */ -#define ATKHLDV_HOLDTIME_MASK 0x00007f00 /* Envelope hold time (127-n == n*88.2msec) */ -#define ATKHLDV_ATTACKTIME_MASK 0x0000007f /* Envelope attack time, log encoded */ - /* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec */ - -#define DCYSUSV 0x12 /* Volume envelope sustain and decay register */ -#define DCYSUSV_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ -#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ -#define DCYSUSV_CHANNELENABLE_MASK 0x00000080 /* 1 = Inhibit envelope engine from writing values in */ - /* this channel and from writing to pitch, filter and */ - /* volume targets. */ -#define DCYSUSV_DECAYTIME_MASK 0x0000007f /* Volume envelope decay time, log encoded */ - /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ - -#define LFOVAL1 0x13 /* Modulation LFO value */ -#define LFOVAL_MASK 0x0000ffff /* Current value of modulation LFO state variable */ - /* 0x8000-n == 666*n usec delay */ - -#define ENVVAL 0x14 /* Modulation envelope register */ -#define ENVVAL_MASK 0x0000ffff /* Current value of modulation envelope state variable */ - /* 0x8000-n == 666*n usec delay */ - -#define ATKHLDM 0x15 /* Modulation envelope hold and attack register */ -#define ATKHLDM_PHASE0 0x00008000 /* 0 = Begin attack phase */ -#define ATKHLDM_HOLDTIME 0x00007f00 /* Envelope hold time (127-n == n*42msec) */ -#define ATKHLDM_ATTACKTIME 0x0000007f /* Envelope attack time, log encoded */ - /* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec */ - -#define DCYSUSM 0x16 /* Modulation envelope decay and sustain register */ -#define DCYSUSM_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ -#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ -#define DCYSUSM_DECAYTIME_MASK 0x0000007f /* Envelope decay time, log encoded */ - /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ - -#define LFOVAL2 0x17 /* Vibrato LFO register */ -#define LFOVAL2_MASK 0x0000ffff /* Current value of vibrato LFO state variable */ - /* 0x8000-n == 666*n usec delay */ - -#define IP 0x18 /* Initial pitch register */ -#define IP_MASK 0x0000ffff /* Exponential initial pitch shift */ - /* 4 bits of octave, 12 bits of fractional octave */ -#define IP_UNITY 0x0000e000 /* Unity pitch shift */ - -#define IFATN 0x19 /* Initial filter cutoff and attenuation register */ -#define IFATN_FILTERCUTOFF_MASK 0x0000ff00 /* Initial filter cutoff frequency in exponential units */ - /* 6 most significant bits are semitones */ - /* 2 least significant bits are fractions */ -#define IFATN_FILTERCUTOFF 0x08080019 -#define IFATN_ATTENUATION_MASK 0x000000ff /* Initial attenuation in 0.375dB steps */ -#define IFATN_ATTENUATION 0x08000019 - - -#define PEFE 0x1a /* Pitch envelope and filter envelope amount register */ -#define PEFE_PITCHAMOUNT_MASK 0x0000ff00 /* Pitch envlope amount */ - /* Signed 2's complement, +/- one octave peak extremes */ -#define PEFE_PITCHAMOUNT 0x0808001a -#define PEFE_FILTERAMOUNT_MASK 0x000000ff /* Filter envlope amount */ - /* Signed 2's complement, +/- six octaves peak extremes */ -#define PEFE_FILTERAMOUNT 0x0800001a -#define FMMOD 0x1b /* Vibrato/filter modulation from LFO register */ -#define FMMOD_MODVIBRATO 0x0000ff00 /* Vibrato LFO modulation depth */ - /* Signed 2's complement, +/- one octave extremes */ -#define FMMOD_MOFILTER 0x000000ff /* Filter LFO modulation depth */ - /* Signed 2's complement, +/- three octave extremes */ - - -#define TREMFRQ 0x1c /* Tremolo amount and modulation LFO frequency register */ -#define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */ - /* Signed 2's complement, with +/- 12dB extremes */ - -#define FM2FRQ2 0x1d /* Vibrato amount and vibrato LFO frequency register */ -#define FM2FRQ2_DEPTH 0x0000ff00 /* Vibrato LFO vibrato depth */ - /* Signed 2's complement, +/- one octave extremes */ -#define FM2FRQ2_FREQUENCY 0x000000ff /* Vibrato LFO frequency */ - /* 0.039Hz steps, maximum of 9.85 Hz. */ - -#define TEMPENV 0x1e /* Tempory envelope register */ -#define TEMPENV_MASK 0x0000ffff /* 16-bit value */ - /* NOTE: All channels contain internal variables; do */ - /* not write to these locations. */ - -#define CD0 0x20 /* Cache data 0 register */ -#define CD1 0x21 /* Cache data 1 register */ -#define CD2 0x22 /* Cache data 2 register */ -#define CD3 0x23 /* Cache data 3 register */ -#define CD4 0x24 /* Cache data 4 register */ -#define CD5 0x25 /* Cache data 5 register */ -#define CD6 0x26 /* Cache data 6 register */ -#define CD7 0x27 /* Cache data 7 register */ -#define CD8 0x28 /* Cache data 8 register */ -#define CD9 0x29 /* Cache data 9 register */ -#define CDA 0x2a /* Cache data A register */ -#define CDB 0x2b /* Cache data B register */ -#define CDC 0x2c /* Cache data C register */ -#define CDD 0x2d /* Cache data D register */ -#define CDE 0x2e /* Cache data E register */ -#define CDF 0x2f /* Cache data F register */ - -#define PTB 0x40 /* Page table base register */ -#define PTB_MASK 0xfffff000 /* Physical address of the page table in host memory */ - -#define TCB 0x41 /* Tank cache base register */ -#define TCB_MASK 0xfffff000 /* Physical address of the bottom of host based TRAM */ - -#define ADCCR 0x42 /* ADC sample rate/stereo control register */ -#define ADCCR_RCHANENABLE 0x00000010 /* Enables right channel for writing to the host */ -#define ADCCR_LCHANENABLE 0x00000008 /* Enables left channel for writing to the host */ - /* NOTE: To guarantee phase coherency, both channels */ - /* must be disabled prior to enabling both channels. */ -#define ADCCR_SAMPLERATE_MASK 0x00000007 /* Sample rate convertor output rate */ -#define ADCCR_SAMPLERATE_48 0x00000000 /* 48kHz sample rate */ -#define ADCCR_SAMPLERATE_44 0x00000001 /* 44.1kHz sample rate */ -#define ADCCR_SAMPLERATE_32 0x00000002 /* 32kHz sample rate */ -#define ADCCR_SAMPLERATE_24 0x00000003 /* 24kHz sample rate */ -#define ADCCR_SAMPLERATE_22 0x00000004 /* 22.05kHz sample rate */ -#define ADCCR_SAMPLERATE_16 0x00000005 /* 16kHz sample rate */ -#define ADCCR_SAMPLERATE_11 0x00000006 /* 11.025kHz sample rate */ -#define ADCCR_SAMPLERATE_8 0x00000007 /* 8kHz sample rate */ - -#define FXWC 0x43 /* FX output write channels register */ - /* When set, each bit enables the writing of the */ - /* corresponding FX output channel into host memory */ - -#define TCBS 0x44 /* Tank cache buffer size register */ -#define TCBS_MASK 0x00000007 /* Tank cache buffer size field */ -#define TCBS_BUFFSIZE_16K 0x00000000 -#define TCBS_BUFFSIZE_32K 0x00000001 -#define TCBS_BUFFSIZE_64K 0x00000002 -#define TCBS_BUFFSIZE_128K 0x00000003 -#define TCBS_BUFFSIZE_256K 0x00000004 -#define TCBS_BUFFSIZE_512K 0x00000005 -#define TCBS_BUFFSIZE_1024K 0x00000006 -#define TCBS_BUFFSIZE_2048K 0x00000007 - -#define MICBA 0x45 /* AC97 microphone buffer address register */ -#define MICBA_MASK 0xfffff000 /* 20 bit base address */ - -#define ADCBA 0x46 /* ADC buffer address register */ -#define ADCBA_MASK 0xfffff000 /* 20 bit base address */ - -#define FXBA 0x47 /* FX Buffer Address */ -#define FXBA_MASK 0xfffff000 /* 20 bit base address */ - -#define MICBS 0x49 /* Microphone buffer size register */ - -#define ADCBS 0x4a /* ADC buffer size register */ - -#define FXBS 0x4b /* FX buffer size register */ - -/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */ -#define ADCBS_BUFSIZE_NONE 0x00000000 -#define ADCBS_BUFSIZE_384 0x00000001 -#define ADCBS_BUFSIZE_448 0x00000002 -#define ADCBS_BUFSIZE_512 0x00000003 -#define ADCBS_BUFSIZE_640 0x00000004 -#define ADCBS_BUFSIZE_768 0x00000005 -#define ADCBS_BUFSIZE_896 0x00000006 -#define ADCBS_BUFSIZE_1024 0x00000007 -#define ADCBS_BUFSIZE_1280 0x00000008 -#define ADCBS_BUFSIZE_1536 0x00000009 -#define ADCBS_BUFSIZE_1792 0x0000000a -#define ADCBS_BUFSIZE_2048 0x0000000b -#define ADCBS_BUFSIZE_2560 0x0000000c -#define ADCBS_BUFSIZE_3072 0x0000000d -#define ADCBS_BUFSIZE_3584 0x0000000e -#define ADCBS_BUFSIZE_4096 0x0000000f -#define ADCBS_BUFSIZE_5120 0x00000010 -#define ADCBS_BUFSIZE_6144 0x00000011 -#define ADCBS_BUFSIZE_7168 0x00000012 -#define ADCBS_BUFSIZE_8192 0x00000013 -#define ADCBS_BUFSIZE_10240 0x00000014 -#define ADCBS_BUFSIZE_12288 0x00000015 -#define ADCBS_BUFSIZE_14366 0x00000016 -#define ADCBS_BUFSIZE_16384 0x00000017 -#define ADCBS_BUFSIZE_20480 0x00000018 -#define ADCBS_BUFSIZE_24576 0x00000019 -#define ADCBS_BUFSIZE_28672 0x0000001a -#define ADCBS_BUFSIZE_32768 0x0000001b -#define ADCBS_BUFSIZE_40960 0x0000001c -#define ADCBS_BUFSIZE_49152 0x0000001d -#define ADCBS_BUFSIZE_57344 0x0000001e -#define ADCBS_BUFSIZE_65536 0x0000001f - - -#define CDCS 0x50 /* CD-ROM digital channel status register */ - -#define GPSCS 0x51 /* General Purpose SPDIF channel status register*/ - -#define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ - -/* definitions for debug register - taken from the alsa drivers */ -#define DBG_ZC 0x80000000 /* zero tram counter */ -#define DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ -#define DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ -#define DBG_SINGLE_STEP 0x00008000 /* single step mode */ -#define DBG_STEP 0x00004000 /* start single step */ -#define DBG_CONDITION_CODE 0x00003e00 /* condition code */ -#define DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ - - -#define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ - -#define SPCS0 0x54 /* SPDIF output Channel Status 0 register */ - -#define SPCS1 0x55 /* SPDIF output Channel Status 1 register */ - -#define SPCS2 0x56 /* SPDIF output Channel Status 2 register */ - -#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */ -#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */ -#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */ -#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */ -#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */ -#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */ -#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */ -#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */ -#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */ -#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */ -#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */ -#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */ -#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */ -#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */ -#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */ -#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */ -#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */ -#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */ -#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */ -#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */ -#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */ -#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */ -#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */ - -/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status */ -#define CLIEL 0x58 /* Channel loop interrupt enable low register */ - -#define CLIEH 0x59 /* Channel loop interrupt enable high register */ - -#define CLIPL 0x5a /* Channel loop interrupt pending low register */ - -#define CLIPH 0x5b /* Channel loop interrupt pending high register */ - -#define SOLEL 0x5c /* Stop on loop enable low register */ - -#define SOLEH 0x5d /* Stop on loop enable high register */ - -#define SPBYPASS 0x5e /* SPDIF BYPASS mode register */ -#define SPBYPASS_ENABLE 0x00000001 /* Enable SPDIF bypass mode */ - -#define AC97SLOT 0x5f /* additional AC97 slots enable bits */ -#define AC97SLOT_CNTR 0x10 /* Center enable */ -#define AC97SLOT_LFE 0x20 /* LFE enable */ - -#define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */ - -#define GPSRCS 0x61 /* General Purpose SPDIF sample rate cvt status */ - -#define ZVSRCS 0x62 /* ZVideo sample rate converter status */ - /* NOTE: This one has no SPDIFLOCKED field */ - /* Assumes sample lock */ - -/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS. */ -#define SRCS_SPDIFLOCKED 0x02000000 /* SPDIF stream locked */ -#define SRCS_RATELOCKED 0x01000000 /* Sample rate locked */ -#define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */ - -#define MICIDX 0x63 /* Microphone recording buffer index register */ -#define MICIDX_MASK 0x0000ffff /* 16-bit value */ -#define MICIDX_IDX 0x10000063 - -#define ADCIDX 0x64 /* ADC recording buffer index register */ -#define ADCIDX_MASK 0x0000ffff /* 16 bit index field */ -#define ADCIDX_IDX 0x10000064 - -#define FXIDX 0x65 /* FX recording buffer index register */ -#define FXIDX_MASK 0x0000ffff /* 16-bit value */ -#define FXIDX_IDX 0x10000065 - -/* Each FX general purpose register is 32 bits in length, all bits are used */ -#define FXGPREGBASE 0x100 /* FX general purpose registers base */ - -/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */ -/* decompressed back to 20 bits on a read. There are a total of 160 locations, the last 32 */ -/* locations are for external TRAM. */ -#define TANKMEMDATAREGBASE 0x200 /* Tank memory data registers base */ -#define TANKMEMDATAREG_MASK 0x000fffff /* 20 bit tank audio data field */ - -/* Combined address field and memory opcode or flag field. 160 locations, last 32 are external */ -#define TANKMEMADDRREGBASE 0x300 /* Tank memory address registers base */ -#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ -#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ -#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ -#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ -#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ - -#define MICROCODEBASE 0x400 /* Microcode data base address */ - -/* Each DSP microcode instruction is mapped into 2 doublewords */ -/* NOTE: When writing, always write the LO doubleword first. Reads can be in either order. */ -#define LOWORD_OPX_MASK 0x000ffc00 /* Instruction operand X */ -#define LOWORD_OPY_MASK 0x000003ff /* Instruction operand Y */ -#define HIWORD_OPCODE_MASK 0x00f00000 /* Instruction opcode */ -#define HIWORD_RESULT_MASK 0x000ffc00 /* Instruction result */ -#define HIWORD_OPA_MASK 0x000003ff /* Instruction operand A */ - -#endif /* _8010_H */ diff -Nru a/drivers/sound/emu10k1/Makefile b/drivers/sound/emu10k1/Makefile --- a/drivers/sound/emu10k1/Makefile Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,23 +0,0 @@ -# Makefile for Creative Labs EMU10K1 -# -# 12 Apr 2000 Rui Sousa - -O_TARGET := emu10k1.o - -obj-y := audio.o cardmi.o cardmo.o cardwi.o cardwo.o ecard.o \ - efxmgr.o emuadxmg.o hwaccess.o irqmgr.o main.o midi.o \ - mixer.o passthrough.o recmgr.o timer.o voicemgr.o -obj-m := $(O_TARGET) - -ifdef DEBUG - EXTRA_CFLAGS += -DEMU10K1_DEBUG -endif - -ifdef CONFIG_MIDI_EMU10K1 - EXTRA_CFLAGS += -DEMU10K1_SEQUENCER -endif - -include $(TOPDIR)/Rules.make - -clean: - rm -f core *.o *.a *.s diff -Nru a/drivers/sound/emu10k1/audio.c b/drivers/sound/emu10k1/audio.c --- a/drivers/sound/emu10k1/audio.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1570 +0,0 @@ -/* - ********************************************************************** - * audio.c -- /dev/dsp interface for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * November 2, 1999 Alan Cox cleaned up types/leaks - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#define __NO_VERSION__ -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "hwaccess.h" -#include "cardwo.h" -#include "cardwi.h" -#include "recmgr.h" -#include "irqmgr.h" -#include "audio.h" -#include "8010.h" - -static void calculate_ofrag(struct woinst *); -static void calculate_ifrag(struct wiinst *); - -/* Audio file operations */ -static ssize_t emu10k1_audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos) -{ - struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; - struct wiinst *wiinst = wave_dev->wiinst; - ssize_t ret = 0; - unsigned long flags; - - DPD(3, "emu10k1_audio_read(), buffer=%p, count=%d\n", buffer, (u32) count); - - if (ppos != &file->f_pos) - return -ESPIPE; - - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - - spin_lock_irqsave(&wiinst->lock, flags); - - if (wiinst->mmapped) { - spin_unlock_irqrestore(&wiinst->lock, flags); - return -ENXIO; - } - - if (wiinst->state == WAVE_STATE_CLOSED) { - calculate_ifrag(wiinst); - - while (emu10k1_wavein_open(wave_dev) < 0) { - spin_unlock_irqrestore(&wiinst->lock, flags); - - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - - interruptible_sleep_on(&wave_dev->card->open_wait); - - if (signal_pending(current)) - return -ERESTARTSYS; - - spin_lock_irqsave(&wiinst->lock, flags); - } - } - - spin_unlock_irqrestore(&wiinst->lock, flags); - - while (count > 0) { - u32 bytestocopy; - - spin_lock_irqsave(&wiinst->lock, flags); - - if (!(wiinst->state & WAVE_STATE_STARTED) - && (wave_dev->enablebits & PCM_ENABLE_INPUT)) - emu10k1_wavein_start(wave_dev); - - emu10k1_wavein_update(wave_dev->card, wiinst); - emu10k1_wavein_getxfersize(wiinst, &bytestocopy); - - spin_unlock_irqrestore(&wiinst->lock, flags); - - DPD(3, "bytestocopy --> %d\n", bytestocopy); - - if ((bytestocopy >= wiinst->buffer.fragment_size) - || (bytestocopy >= count)) { - bytestocopy = min_t(u32, bytestocopy, count); - - emu10k1_wavein_xferdata(wiinst, (u8 *) buffer, &bytestocopy); - - count -= bytestocopy; - buffer += bytestocopy; - ret += bytestocopy; - } - - if (count > 0) { - if ((file->f_flags & O_NONBLOCK) - || (!(wave_dev->enablebits & PCM_ENABLE_INPUT))) - return (ret ? ret : -EAGAIN); - - interruptible_sleep_on(&wiinst->wait_queue); - - if (signal_pending(current)) - return (ret ? ret : -ERESTARTSYS); - - } - } - - DPD(3, "bytes copied -> %d\n", (u32) ret); - - return ret; -} - -static ssize_t emu10k1_audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) -{ - struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; - struct woinst *woinst = wave_dev->woinst; - ssize_t ret; - unsigned long flags; - - DPD(3, "emu10k1_audio_write(), buffer=%p, count=%d\n", buffer, (u32) count); - - if (ppos != &file->f_pos) - return -ESPIPE; - - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - - spin_lock_irqsave(&woinst->lock, flags); - - if (woinst->mmapped) { - spin_unlock_irqrestore(&woinst->lock, flags); - return -ENXIO; - } - - if (woinst->format.passthrough) { - int r; - - woinst->buffer.ossfragshift = PT_BLOCKSIZE_LOG2; - woinst->buffer.numfrags = PT_BLOCKCOUNT; - calculate_ofrag(woinst); - - r = emu10k1_pt_write(file, buffer, count); - spin_unlock_irqrestore(&woinst->lock, flags); - return r; - } - - if (woinst->state == WAVE_STATE_CLOSED) { - calculate_ofrag(woinst); - - while (emu10k1_waveout_open(wave_dev) < 0) { - spin_unlock_irqrestore(&woinst->lock, flags); - - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - - interruptible_sleep_on(&wave_dev->card->open_wait); - - if (signal_pending(current)) - return -ERESTARTSYS; - - spin_lock_irqsave(&woinst->lock, flags); - } - } - - spin_unlock_irqrestore(&woinst->lock, flags); - - ret = 0; - if (count % woinst->format.bytespersample) - return -EINVAL; - - count /= woinst->num_voices; - - while (count > 0) { - u32 bytestocopy; - - spin_lock_irqsave(&woinst->lock, flags); - emu10k1_waveout_update(woinst); - emu10k1_waveout_getxfersize(woinst, &bytestocopy); - spin_unlock_irqrestore(&woinst->lock, flags); - - DPD(3, "bytestocopy --> %d\n", bytestocopy); - - if ((bytestocopy >= woinst->buffer.fragment_size) - || (bytestocopy >= count)) { - - bytestocopy = min_t(u32, bytestocopy, count); - - emu10k1_waveout_xferdata(woinst, (u8 *) buffer, &bytestocopy); - - count -= bytestocopy; - buffer += bytestocopy * woinst->num_voices; - ret += bytestocopy * woinst->num_voices; - - spin_lock_irqsave(&woinst->lock, flags); - woinst->total_copied += bytestocopy; - - if (!(woinst->state & WAVE_STATE_STARTED) - && (wave_dev->enablebits & PCM_ENABLE_OUTPUT) - && (woinst->total_copied >= woinst->buffer.fragment_size)) - emu10k1_waveout_start(wave_dev); - - spin_unlock_irqrestore(&woinst->lock, flags); - } - - if (count > 0) { - if ((file->f_flags & O_NONBLOCK) - || (!(wave_dev->enablebits & PCM_ENABLE_OUTPUT))) - return (ret ? ret : -EAGAIN); - - interruptible_sleep_on(&woinst->wait_queue); - - if (signal_pending(current)) - return (ret ? ret : -ERESTARTSYS); - } - } - - DPD(3, "bytes copied -> %d\n", (u32) ret); - - return ret; -} - -static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; - struct woinst *woinst = NULL; - struct wiinst *wiinst = NULL; - int val = 0; - u32 bytestocopy; - unsigned long flags; - - DPF(4, "emu10k1_audio_ioctl()\n"); - - if (file->f_mode & FMODE_WRITE) - woinst = wave_dev->woinst; - - if (file->f_mode & FMODE_READ) - wiinst = wave_dev->wiinst; - - switch (cmd) { - case OSS_GETVERSION: - DPF(2, "OSS_GETVERSION:\n"); - return put_user(SOUND_VERSION, (int *) arg); - - case SNDCTL_DSP_RESET: - DPF(2, "SNDCTL_DSP_RESET:\n"); - wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT; - - if (file->f_mode & FMODE_WRITE) { - spin_lock_irqsave(&woinst->lock, flags); - - if (woinst->state & WAVE_STATE_OPEN) { - emu10k1_waveout_close(wave_dev); - } - - woinst->mmapped = 0; - woinst->total_copied = 0; - woinst->total_played = 0; - woinst->blocks = 0; - - spin_unlock_irqrestore(&woinst->lock, flags); - } - - if (file->f_mode & FMODE_READ) { - spin_lock_irqsave(&wiinst->lock, flags); - - if (wiinst->state & WAVE_STATE_OPEN) { - emu10k1_wavein_close(wave_dev); - } - - wiinst->mmapped = 0; - wiinst->total_recorded = 0; - wiinst->blocks = 0; - spin_unlock_irqrestore(&wiinst->lock, flags); - } - - break; - - case SNDCTL_DSP_SYNC: - DPF(2, "SNDCTL_DSP_SYNC:\n"); - - if (file->f_mode & FMODE_WRITE) { - - spin_lock_irqsave(&woinst->lock, flags); - - if (woinst->state & WAVE_STATE_OPEN) { - - if (woinst->state & WAVE_STATE_STARTED) - while ((woinst->total_played < woinst->total_copied) - && !signal_pending(current)) { - spin_unlock_irqrestore(&woinst->lock, flags); - interruptible_sleep_on(&woinst->wait_queue); - spin_lock_irqsave(&woinst->lock, flags); - } - emu10k1_waveout_close(wave_dev); - } - - woinst->mmapped = 0; - woinst->total_copied = 0; - woinst->total_played = 0; - woinst->blocks = 0; - - spin_unlock_irqrestore(&woinst->lock, flags); - } - - if (file->f_mode & FMODE_READ) { - spin_lock_irqsave(&wiinst->lock, flags); - - if (wiinst->state & WAVE_STATE_OPEN) { - emu10k1_wavein_close(wave_dev); - } - - wiinst->mmapped = 0; - wiinst->total_recorded = 0; - wiinst->blocks = 0; - spin_unlock_irqrestore(&wiinst->lock, flags); - } - - break; - - case SNDCTL_DSP_SETDUPLEX: - DPF(2, "SNDCTL_DSP_SETDUPLEX:\n"); - break; - - case SNDCTL_DSP_GETCAPS: - DPF(2, "SNDCTL_DSP_GETCAPS:\n"); - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_COPROC, (int *) arg); - - case SNDCTL_DSP_SPEED: - DPF(2, "SNDCTL_DSP_SPEED:\n"); - - if (get_user(val, (int *) arg)) - return -EFAULT; - - DPD(2, "val is %d\n", val); - - if (val > 0) { - if (file->f_mode & FMODE_READ) { - struct wave_format format; - - spin_lock_irqsave(&wiinst->lock, flags); - - format = wiinst->format; - format.samplingrate = val; - - if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { - spin_unlock_irqrestore(&wiinst->lock, flags); - return -EINVAL; - } - - val = wiinst->format.samplingrate; - - spin_unlock_irqrestore(&wiinst->lock, flags); - - DPD(2, "set recording sampling rate -> %d\n", val); - } - - if (file->f_mode & FMODE_WRITE) { - struct wave_format format; - - spin_lock_irqsave(&woinst->lock, flags); - - format = woinst->format; - format.samplingrate = val; - - if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { - spin_unlock_irqrestore(&woinst->lock, flags); - return -EINVAL; - } - - val = woinst->format.samplingrate; - - spin_unlock_irqrestore(&woinst->lock, flags); - - DPD(2, "set playback sampling rate -> %d\n", val); - } - - return put_user(val, (int *) arg); - } else { - if (file->f_mode & FMODE_READ) - val = wiinst->format.samplingrate; - else if (file->f_mode & FMODE_WRITE) - val = woinst->format.samplingrate; - - return put_user(val, (int *) arg); - } - break; - - case SNDCTL_DSP_STEREO: - DPF(2, "SNDCTL_DSP_STEREO:\n"); - - if (get_user(val, (int *) arg)) - return -EFAULT; - - DPD(2, " val is %d\n", val); - - if (file->f_mode & FMODE_READ) { - struct wave_format format; - - spin_lock_irqsave(&wiinst->lock, flags); - - format = wiinst->format; - format.channels = val ? 2 : 1; - - if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { - spin_unlock_irqrestore(&wiinst->lock, flags); - return -EINVAL; - } - - val = wiinst->format.channels - 1; - - spin_unlock_irqrestore(&wiinst->lock, flags); - DPD(2, "set recording stereo -> %d\n", val); - } - - if (file->f_mode & FMODE_WRITE) { - struct wave_format format; - - spin_lock_irqsave(&woinst->lock, flags); - - format = woinst->format; - format.channels = val ? 2 : 1; - - if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { - spin_unlock_irqrestore(&woinst->lock, flags); - return -EINVAL; - } - - val = woinst->format.channels - 1; - - spin_unlock_irqrestore(&woinst->lock, flags); - - DPD(2, "set playback stereo -> %d\n", val); - } - - return put_user(val, (int *) arg); - - break; - - case SNDCTL_DSP_CHANNELS: - DPF(2, "SNDCTL_DSP_CHANNELS:\n"); - - if (get_user(val, (int *) arg)) - return -EFAULT; - - DPD(2, " val is %d\n", val); - - if (val > 0) { - if (file->f_mode & FMODE_READ) { - struct wave_format format; - - spin_lock_irqsave(&wiinst->lock, flags); - - format = wiinst->format; - format.channels = val; - - if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { - spin_unlock_irqrestore(&wiinst->lock, flags); - return -EINVAL; - } - val = wiinst->format.channels; - - spin_unlock_irqrestore(&wiinst->lock, flags); - DPD(2, "set recording number of channels -> %d\n", val); - } - - if (file->f_mode & FMODE_WRITE) { - struct wave_format format; - - spin_lock_irqsave(&woinst->lock, flags); - - format = woinst->format; - format.channels = val; - - if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { - spin_unlock_irqrestore(&woinst->lock, flags); - return -EINVAL; - } - - val = woinst->format.channels; - - spin_unlock_irqrestore(&woinst->lock, flags); - DPD(2, "set playback number of channels -> %d\n", val); - } - - return put_user(val, (int *) arg); - } else { - if (file->f_mode & FMODE_READ) - val = wiinst->format.channels; - else if (file->f_mode & FMODE_WRITE) - val = woinst->format.channels; - - return put_user(val, (int *) arg); - } - break; - - case SNDCTL_DSP_GETFMTS: - DPF(2, "SNDCTL_DSP_GETFMTS:\n"); - - if (file->f_mode & FMODE_READ) - val = AFMT_S16_LE; - else if (file->f_mode & FMODE_WRITE) { - val = AFMT_S16_LE | AFMT_U8; - if (emu10k1_find_control_gpr(&wave_dev->card->mgr, - wave_dev->card->pt.patch_name, - wave_dev->card->pt.enable_gpr_name) >= 0) - val |= AFMT_AC3; - } - return put_user(val, (int *) arg); - - case SNDCTL_DSP_SETFMT: /* Same as SNDCTL_DSP_SAMPLESIZE */ - DPF(2, "SNDCTL_DSP_SETFMT:\n"); - - if (get_user(val, (int *) arg)) - return -EFAULT; - - DPD(2, " val is %d\n", val); - - if (val != AFMT_QUERY) { - if (file->f_mode & FMODE_READ) { - struct wave_format format; - - spin_lock_irqsave(&wiinst->lock, flags); - - format = wiinst->format; - format.id = val; - - if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { - spin_unlock_irqrestore(&wiinst->lock, flags); - return -EINVAL; - } - - val = wiinst->format.id; - - spin_unlock_irqrestore(&wiinst->lock, flags); - DPD(2, "set recording format -> %d\n", val); - } - - if (file->f_mode & FMODE_WRITE) { - struct wave_format format; - - spin_lock_irqsave(&woinst->lock, flags); - - format = woinst->format; - format.id = val; - - if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { - spin_unlock_irqrestore(&woinst->lock, flags); - return -EINVAL; - } - - val = woinst->format.id; - - spin_unlock_irqrestore(&woinst->lock, flags); - DPD(2, "set playback format -> %d\n", val); - } - - return put_user(val, (int *) arg); - } else { - if (file->f_mode & FMODE_READ) - val = wiinst->format.id; - else if (file->f_mode & FMODE_WRITE) - val = woinst->format.id; - - return put_user(val, (int *) arg); - } - break; - - case SOUND_PCM_READ_BITS: - - if (file->f_mode & FMODE_READ) - val = wiinst->format.bitsperchannel; - else if (file->f_mode & FMODE_WRITE) - val = woinst->format.bitsperchannel; - - return put_user(val, (int *) arg); - - case SOUND_PCM_READ_RATE: - - if (file->f_mode & FMODE_READ) - val = wiinst->format.samplingrate; - else if (file->f_mode & FMODE_WRITE) - val = woinst->format.samplingrate; - - return put_user(val, (int *) arg); - - case SOUND_PCM_READ_CHANNELS: - - if (file->f_mode & FMODE_READ) - val = wiinst->format.channels; - else if (file->f_mode & FMODE_WRITE) - val = woinst->format.channels; - - return put_user(val, (int *) arg); - - case SOUND_PCM_WRITE_FILTER: - DPF(2, "SOUND_PCM_WRITE_FILTER: not implemented\n"); - break; - - case SOUND_PCM_READ_FILTER: - DPF(2, "SOUND_PCM_READ_FILTER: not implemented\n"); - break; - - case SNDCTL_DSP_SETSYNCRO: - DPF(2, "SNDCTL_DSP_SETSYNCRO: not implemented\n"); - break; - - case SNDCTL_DSP_GETTRIGGER: - DPF(2, "SNDCTL_DSP_GETTRIGGER:\n"); - - if (file->f_mode & FMODE_WRITE && (wave_dev->enablebits & PCM_ENABLE_OUTPUT)) - val |= PCM_ENABLE_OUTPUT; - - if (file->f_mode & FMODE_READ && (wave_dev->enablebits & PCM_ENABLE_INPUT)) - val |= PCM_ENABLE_INPUT; - - return put_user(val, (int *) arg); - - case SNDCTL_DSP_SETTRIGGER: - DPF(2, "SNDCTL_DSP_SETTRIGGER:\n"); - - if (get_user(val, (int *) arg)) - return -EFAULT; - - if (file->f_mode & FMODE_WRITE) { - spin_lock_irqsave(&woinst->lock, flags); - - if (val & PCM_ENABLE_OUTPUT) { - wave_dev->enablebits |= PCM_ENABLE_OUTPUT; - if (woinst->state & WAVE_STATE_OPEN) - emu10k1_waveout_start(wave_dev); - } else { - wave_dev->enablebits &= ~PCM_ENABLE_OUTPUT; - if (woinst->state & WAVE_STATE_STARTED) - emu10k1_waveout_stop(wave_dev); - } - - spin_unlock_irqrestore(&woinst->lock, flags); - } - - if (file->f_mode & FMODE_READ) { - spin_lock_irqsave(&wiinst->lock, flags); - - if (val & PCM_ENABLE_INPUT) { - wave_dev->enablebits |= PCM_ENABLE_INPUT; - if (wiinst->state & WAVE_STATE_OPEN) - emu10k1_wavein_start(wave_dev); - } else { - wave_dev->enablebits &= ~PCM_ENABLE_INPUT; - if (wiinst->state & WAVE_STATE_STARTED) - emu10k1_wavein_stop(wave_dev); - } - - spin_unlock_irqrestore(&wiinst->lock, flags); - } - break; - - case SNDCTL_DSP_GETOSPACE: - { - audio_buf_info info; - - DPF(4, "SNDCTL_DSP_GETOSPACE:\n"); - - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - - spin_lock_irqsave(&woinst->lock, flags); - - if (woinst->state & WAVE_STATE_OPEN) { - emu10k1_waveout_update(woinst); - emu10k1_waveout_getxfersize(woinst, &bytestocopy); - info.bytes = bytestocopy; - } else { - calculate_ofrag(woinst); - info.bytes = woinst->buffer.size; - } - spin_unlock_irqrestore(&woinst->lock, flags); - - info.bytes *= woinst->num_voices; - info.fragsize = woinst->buffer.fragment_size * woinst->num_voices; - info.fragstotal = woinst->buffer.numfrags * woinst->num_voices; - info.fragments = info.bytes / info.fragsize; - - if (copy_to_user((int *) arg, &info, sizeof(info))) - return -EFAULT; - } - break; - - case SNDCTL_DSP_GETISPACE: - { - audio_buf_info info; - - DPF(4, "SNDCTL_DSP_GETISPACE:\n"); - - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - - spin_lock_irqsave(&wiinst->lock, flags); - if (wiinst->state & WAVE_STATE_OPEN) { - emu10k1_wavein_update(wave_dev->card, wiinst); - emu10k1_wavein_getxfersize(wiinst, &bytestocopy); - info.bytes = bytestocopy; - } else { - calculate_ifrag(wiinst); - info.bytes = 0; - } - spin_unlock_irqrestore(&wiinst->lock, flags); - - info.fragstotal = wiinst->buffer.numfrags; - info.fragments = info.bytes / wiinst->buffer.fragment_size; - info.fragsize = wiinst->buffer.fragment_size; - - if (copy_to_user((int *) arg, &info, sizeof(info))) - return -EFAULT; - } - break; - - case SNDCTL_DSP_NONBLOCK: - DPF(2, "SNDCTL_DSP_NONBLOCK:\n"); - - file->f_flags |= O_NONBLOCK; - break; - - case SNDCTL_DSP_GETODELAY: - DPF(4, "SNDCTL_DSP_GETODELAY:\n"); - - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - - spin_lock_irqsave(&woinst->lock, flags); - if (woinst->state & WAVE_STATE_OPEN) { - emu10k1_waveout_update(woinst); - emu10k1_waveout_getxfersize(woinst, &bytestocopy); - val = woinst->buffer.size - bytestocopy; - } else - val = 0; - - val *= woinst->num_voices; - spin_unlock_irqrestore(&woinst->lock, flags); - - return put_user(val, (int *) arg); - - case SNDCTL_DSP_GETIPTR: - { - count_info cinfo; - - DPF(4, "SNDCTL_DSP_GETIPTR: \n"); - - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - - spin_lock_irqsave(&wiinst->lock, flags); - - if (wiinst->state & WAVE_STATE_OPEN) { - emu10k1_wavein_update(wave_dev->card, wiinst); - cinfo.ptr = wiinst->buffer.hw_pos; - cinfo.bytes = cinfo.ptr + wiinst->total_recorded - wiinst->total_recorded % wiinst->buffer.size; - cinfo.blocks = cinfo.bytes / wiinst->buffer.fragment_size - wiinst->blocks; - wiinst->blocks = cinfo.bytes / wiinst->buffer.fragment_size; - } else { - cinfo.ptr = 0; - cinfo.bytes = 0; - cinfo.blocks = 0; - } - - if(wiinst->mmapped) - wiinst->buffer.bytestocopy %= wiinst->buffer.fragment_size; - - spin_unlock_irqrestore(&wiinst->lock, flags); - - if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - } - break; - - case SNDCTL_DSP_GETOPTR: - { - count_info cinfo; - - DPF(4, "SNDCTL_DSP_GETOPTR:\n"); - - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - - spin_lock_irqsave(&woinst->lock, flags); - - if (woinst->state & WAVE_STATE_OPEN || - (woinst->format.passthrough && wave_dev->card->pt.state)) { - int num_fragments; - if (woinst->format.passthrough) { - emu10k1_pt_waveout_update(wave_dev); - cinfo.bytes = woinst->total_played; - } else { - emu10k1_waveout_update(woinst); - cinfo.bytes = woinst->total_played; - } - cinfo.ptr = woinst->buffer.hw_pos; - num_fragments = cinfo.bytes / woinst->buffer.fragment_size; - cinfo.blocks = num_fragments - woinst->blocks; - woinst->blocks = num_fragments; - - cinfo.bytes *= woinst->num_voices; - cinfo.ptr *= woinst->num_voices; - } else { - cinfo.ptr = 0; - cinfo.bytes = 0; - cinfo.blocks = 0; - } - - if (woinst->mmapped) - woinst->buffer.free_bytes %= woinst->buffer.fragment_size; - - spin_unlock_irqrestore(&woinst->lock, flags); - - if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - } - break; - - case SNDCTL_DSP_GETBLKSIZE: - DPF(2, "SNDCTL_DSP_GETBLKSIZE:\n"); - - if (file->f_mode & FMODE_WRITE) { - spin_lock_irqsave(&woinst->lock, flags); - - calculate_ofrag(woinst); - val = woinst->buffer.fragment_size * woinst->num_voices; - - spin_unlock_irqrestore(&woinst->lock, flags); - } - - if (file->f_mode & FMODE_READ) { - spin_lock_irqsave(&wiinst->lock, flags); - - calculate_ifrag(wiinst); - val = wiinst->buffer.fragment_size; - - spin_unlock_irqrestore(&wiinst->lock, flags); - } - - return put_user(val, (int *) arg); - - break; - - case SNDCTL_DSP_POST: - if (file->f_mode & FMODE_WRITE) { - spin_lock_irqsave(&woinst->lock, flags); - - if (!(woinst->state & WAVE_STATE_STARTED) - && (wave_dev->enablebits & PCM_ENABLE_OUTPUT) - && (woinst->total_copied > 0)) - emu10k1_waveout_start(wave_dev); - - spin_unlock_irqrestore(&woinst->lock, flags); - } - - break; - - case SNDCTL_DSP_SUBDIVIDE: - DPF(2, "SNDCTL_DSP_SUBDIVIDE: not implemented\n"); - break; - - case SNDCTL_DSP_SETFRAGMENT: - DPF(2, "SNDCTL_DSP_SETFRAGMENT:\n"); - - if (get_user(val, (int *) arg)) - return -EFAULT; - - DPD(2, "val is %#x\n", val); - - if (val == 0) - return -EIO; - - if (file->f_mode & FMODE_WRITE) { - /* digital pass-through fragment count and size are fixed values */ - if (woinst->state & WAVE_STATE_OPEN || woinst->format.passthrough) - return -EINVAL; /* too late to change */ - - woinst->buffer.ossfragshift = val & 0xffff; - woinst->buffer.numfrags = (val >> 16) & 0xffff; - } - - if (file->f_mode & FMODE_READ) { - if (wiinst->state & WAVE_STATE_OPEN) - return -EINVAL; /* too late to change */ - - wiinst->buffer.ossfragshift = val & 0xffff; - wiinst->buffer.numfrags = (val >> 16) & 0xffff; - } - - break; - - case SNDCTL_COPR_LOAD: - { - copr_buffer *buf; - u32 i; - - DPF(4, "SNDCTL_COPR_LOAD:\n"); - - buf = kmalloc(sizeof(copr_buffer), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, (copr_buffer *) arg, sizeof(copr_buffer))) { - kfree (buf); - return -EFAULT; - } - - if ((buf->command != CMD_READ) && (buf->command != CMD_WRITE)) { - kfree (buf); - return -EINVAL; - } -#ifdef DBGEMU - if ( (buf->offs < 0) || (buf->offs + buf->len > 0x800) || (buf->len > 1000)) { -#else - if ( ((buf->offs < 0x100 ) || (buf->offs + buf->len > 0x800) || (buf->len > 1000)) - && !( ( buf->offs == DBG) && (buf->len ==1) )){ -#endif - kfree(buf); - return -EINVAL; - } - - if (buf->command == CMD_READ) { - for (i = 0; i < buf->len; i++) - ((u32 *) buf->data)[i] = sblive_readptr(wave_dev->card, buf->offs + i, 0); - - if (copy_to_user((copr_buffer *) arg, buf, sizeof(copr_buffer))) { - kfree(buf); - return -EFAULT; - } - } else { - for (i = 0; i < buf->len; i++) - sblive_writeptr(wave_dev->card, buf->offs + i, 0, ((u32 *) buf->data)[i]); - } - - kfree (buf); - break; - } - - default: /* Default is unrecognized command */ - DPD(2, "default: %#x\n", cmd); - return -EINVAL; - } - return 0; -} - -static struct page *emu10k1_mm_nopage (struct vm_area_struct * vma, unsigned long address, int write_access) -{ - struct emu10k1_wavedevice *wave_dev = vma->vm_private_data; - struct woinst *woinst = wave_dev->woinst; - struct wiinst *wiinst = wave_dev->wiinst; - struct page *dmapage; - unsigned long pgoff; - int rd, wr; - - DPF(4, "emu10k1_mm_nopage()\n"); - DPD(4, "addr: %#lx\n", address); - - if (address > vma->vm_end) { - DPF(2, "EXIT, returning NOPAGE_SIGBUS\n"); - return NOPAGE_SIGBUS; /* Disallow mremap */ - } - - pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT); - if (woinst != NULL) - wr = woinst->mmapped; - else - wr = 0; - - if (wiinst != NULL) - rd = wiinst->mmapped; - else - rd = 0; - - /* if full-duplex (read+write) and we have two sets of bufs, - * then the playback buffers come first, sez soundcard.c */ - if (wr) { - if (pgoff >= woinst->buffer.pages) { - pgoff -= woinst->buffer.pages; - dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE); - } else - dmapage = virt_to_page (woinst->buffer.mem[0].addr[pgoff]); - } else { - dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE); - } - - get_page (dmapage); - - DPD(4, "page: %#lx\n", dmapage); - return dmapage; -} - -struct vm_operations_struct emu10k1_mm_ops = { - nopage: emu10k1_mm_nopage, -}; - -static int emu10k1_audio_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; - unsigned long max_pages, n_pages, pgoffset; - struct woinst *woinst = NULL; - struct wiinst *wiinst = NULL; - unsigned long flags; - - DPF(2, "emu10k1_audio_mmap()\n"); - - max_pages = 0; - if (vma->vm_flags & VM_WRITE) { - woinst = wave_dev->woinst; - - spin_lock_irqsave(&woinst->lock, flags); - - /* No m'mapping possible for multichannel */ - if (woinst->num_voices > 1) { - spin_unlock_irqrestore(&woinst->lock, flags); - return -EINVAL; - } - - if (woinst->state == WAVE_STATE_CLOSED) { - calculate_ofrag(woinst); - - if (emu10k1_waveout_open(wave_dev) < 0) { - spin_unlock_irqrestore(&woinst->lock, flags); - ERROR(); - return -EINVAL; - } - } - - woinst->mmapped = 1; - max_pages += woinst->buffer.pages; - spin_unlock_irqrestore(&woinst->lock, flags); - } - - if (vma->vm_flags & VM_READ) { - wiinst = wave_dev->wiinst; - - spin_lock_irqsave(&wiinst->lock, flags); - if (wiinst->state == WAVE_STATE_CLOSED) { - calculate_ifrag(wiinst); - - if (emu10k1_wavein_open(wave_dev) < 0) { - spin_unlock_irqrestore(&wiinst->lock, flags); - ERROR(); - return -EINVAL; - } - } - - wiinst->mmapped = 1; - max_pages += wiinst->buffer.pages; - spin_unlock_irqrestore(&wiinst->lock, flags); - } - - n_pages = ((vma->vm_end - vma->vm_start) + PAGE_SIZE - 1) >> PAGE_SHIFT; - pgoffset = vma->vm_pgoff; - - DPD(3, "vma_start: %#lx, vma_end: %#lx, vma_offset: %d\n", vma->vm_start, vma->vm_end, pgoffset); - DPD(3, "n_pages: %d, max_pages: %d\n", n_pages, max_pages); - - if (pgoffset + n_pages > max_pages) - return -EINVAL; - - vma->vm_flags |= VM_RESERVED; - vma->vm_ops = &emu10k1_mm_ops; - vma->vm_private_data = wave_dev; - - return 0; -} - -static int emu10k1_audio_open(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct emu10k1_card *card = NULL; - struct list_head *entry; - struct emu10k1_wavedevice *wave_dev; - - DPF(2, "emu10k1_audio_open()\n"); - - /* Check for correct device to open */ - - list_for_each(entry, &emu10k1_devs) { - card = list_entry(entry, struct emu10k1_card, list); - - if (!((card->audio_dev ^ minor) & ~0xf) || !((card->audio_dev1 ^ minor) & ~0xf)) - goto match; - } - - return -ENODEV; - -match: - - wave_dev = (struct emu10k1_wavedevice *) kmalloc(sizeof(struct emu10k1_wavedevice), GFP_KERNEL); - - if (wave_dev == NULL) { - ERROR(); - return -ENOMEM; - } - - wave_dev->card = card; - wave_dev->wiinst = NULL; - wave_dev->woinst = NULL; - wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT; /* Default */ - - if (file->f_mode & FMODE_READ) { - /* Recording */ - struct wiinst *wiinst; - - if ((wiinst = (struct wiinst *) kmalloc(sizeof(struct wiinst), GFP_KERNEL)) == NULL) { - ERROR(); - return -ENODEV; - } - - wiinst->recsrc = card->wavein.recsrc; - wiinst->fxwc = card->wavein.fxwc; - - switch (wiinst->recsrc) { - case WAVERECORD_AC97: - wiinst->format.id = AFMT_S16_LE; - wiinst->format.samplingrate = 8000; - wiinst->format.bitsperchannel = 16; - wiinst->format.channels = 1; - break; - case WAVERECORD_MIC: - wiinst->format.id = AFMT_S16_LE; - wiinst->format.samplingrate = 8000; - wiinst->format.bitsperchannel = 16; - wiinst->format.channels = 1; - break; - case WAVERECORD_FX: - wiinst->format.id = AFMT_S16_LE; - wiinst->format.samplingrate = 48000; - wiinst->format.bitsperchannel = 16; - wiinst->format.channels = hweight32(wiinst->fxwc); - break; - default: - BUG(); - break; - } - - wiinst->state = WAVE_STATE_CLOSED; - - wiinst->buffer.ossfragshift = 0; - wiinst->buffer.fragment_size = 0; - wiinst->buffer.numfrags = 0; - - init_waitqueue_head(&wiinst->wait_queue); - - wiinst->mmapped = 0; - wiinst->total_recorded = 0; - wiinst->blocks = 0; - wiinst->lock = SPIN_LOCK_UNLOCKED; - tasklet_init(&wiinst->timer.tasklet, emu10k1_wavein_bh, (unsigned long) wave_dev); - wave_dev->wiinst = wiinst; - emu10k1_wavein_setformat(wave_dev, &wiinst->format); - } - - if (file->f_mode & FMODE_WRITE) { - struct woinst *woinst; - int i; - - if ((woinst = (struct woinst *) kmalloc(sizeof(struct woinst), GFP_KERNEL)) == NULL) { - ERROR(); - return -ENODEV; - } - - if (wave_dev->wiinst != NULL) { - woinst->format = wave_dev->wiinst->format; - } else { - woinst->format.id = AFMT_U8; - woinst->format.samplingrate = 8000; - woinst->format.bitsperchannel = 8; - woinst->format.channels = 1; - } - - woinst->state = WAVE_STATE_CLOSED; - - woinst->buffer.fragment_size = 0; - woinst->buffer.ossfragshift = 0; - woinst->buffer.numfrags = 0; - woinst->device = (card->audio_dev1 == minor); - woinst->timer.state = TIMER_STATE_UNINSTALLED; - woinst->num_voices = 1; - for (i = 0; i < WAVEOUT_MAXVOICES; i++) { - woinst->voice[i].usage = VOICE_USAGE_FREE; - woinst->buffer.mem[i].emupageindex = -1; - } - - init_waitqueue_head(&woinst->wait_queue); - - woinst->mmapped = 0; - woinst->total_copied = 0; - woinst->total_played = 0; - woinst->blocks = 0; - woinst->lock = SPIN_LOCK_UNLOCKED; - tasklet_init(&woinst->timer.tasklet, emu10k1_waveout_bh, (unsigned long) wave_dev); - wave_dev->woinst = woinst; - emu10k1_waveout_setformat(wave_dev, &woinst->format); - } - - file->private_data = (void *) wave_dev; - - return 0; -} - -static int emu10k1_audio_release(struct inode *inode, struct file *file) -{ - struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; - struct emu10k1_card *card; - unsigned long flags; - - card = wave_dev->card; - - DPF(2, "emu10k1_audio_release()\n"); - - if (file->f_mode & FMODE_WRITE) { - struct woinst *woinst = wave_dev->woinst; - - spin_lock_irqsave(&woinst->lock, flags); - - if (woinst->format.passthrough && card->pt.state != PT_STATE_INACTIVE) { - spin_lock(&card->pt.lock); - emu10k1_pt_stop(card); - spin_unlock(&card->pt.lock); - } - if (woinst->state & WAVE_STATE_OPEN) { - if (woinst->state & WAVE_STATE_STARTED) { - if (!(file->f_flags & O_NONBLOCK)) { - while (!signal_pending(current) - && (woinst->total_played < woinst->total_copied)) { - DPF(4, "Buffer hasn't been totally played, sleep....\n"); - spin_unlock_irqrestore(&woinst->lock, flags); - interruptible_sleep_on(&woinst->wait_queue); - spin_lock_irqsave(&woinst->lock, flags); - } - } - } - emu10k1_waveout_close(wave_dev); - } - - spin_unlock_irqrestore(&woinst->lock, flags); - /* remove the tasklet */ - tasklet_kill(&woinst->timer.tasklet); - kfree(wave_dev->woinst); - } - - if (file->f_mode & FMODE_READ) { - struct wiinst *wiinst = wave_dev->wiinst; - - spin_lock_irqsave(&wiinst->lock, flags); - - if (wiinst->state & WAVE_STATE_OPEN) { - emu10k1_wavein_close(wave_dev); - } - - spin_unlock_irqrestore(&wiinst->lock, flags); - tasklet_kill(&wiinst->timer.tasklet); - kfree(wave_dev->wiinst); - } - - kfree(wave_dev); - - if (waitqueue_active(&card->open_wait)) - wake_up_interruptible(&card->open_wait); - - return 0; -} - -/* FIXME sort out poll() + mmap() */ -static unsigned int emu10k1_audio_poll(struct file *file, struct poll_table_struct *wait) -{ - struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; - struct woinst *woinst = wave_dev->woinst; - struct wiinst *wiinst = wave_dev->wiinst; - unsigned int mask = 0; - u32 bytestocopy; - unsigned long flags; - - DPF(4, "emu10k1_audio_poll()\n"); - - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &woinst->wait_queue, wait); - - if (file->f_mode & FMODE_READ) - poll_wait(file, &wiinst->wait_queue, wait); - - if (file->f_mode & FMODE_WRITE) { - spin_lock_irqsave(&woinst->lock, flags); - - if (woinst->state & WAVE_STATE_OPEN) { - emu10k1_waveout_update(woinst); - emu10k1_waveout_getxfersize(woinst, &bytestocopy); - - if (bytestocopy >= woinst->buffer.fragment_size) - mask |= POLLOUT | POLLWRNORM; - } else - mask |= POLLOUT | POLLWRNORM; - - spin_unlock_irqrestore(&woinst->lock, flags); - } - - if (file->f_mode & FMODE_READ) { - spin_lock_irqsave(&wiinst->lock, flags); - - if (wiinst->state == WAVE_STATE_CLOSED) { - calculate_ifrag(wiinst); - if (emu10k1_wavein_open(wave_dev) < 0) { - spin_unlock_irqrestore(&wiinst->lock, flags); - return (mask |= POLLERR); - } - } - - if (!(wiinst->state & WAVE_STATE_STARTED)) { - wave_dev->enablebits |= PCM_ENABLE_INPUT; - emu10k1_wavein_start(wave_dev); - } - emu10k1_wavein_update(wave_dev->card, wiinst); - emu10k1_wavein_getxfersize(wiinst, &bytestocopy); - - if (bytestocopy >= wiinst->buffer.fragment_size) - mask |= POLLIN | POLLRDNORM; - - spin_unlock_irqrestore(&wiinst->lock, flags); - } - - return mask; -} - -static void calculate_ofrag(struct woinst *woinst) -{ - struct waveout_buffer *buffer = &woinst->buffer; - u32 fragsize; - - if (buffer->fragment_size) - return; - - if (!buffer->ossfragshift) { - fragsize = (woinst->format.bytespervoicesample * woinst->format.samplingrate * WAVEOUT_DEFAULTFRAGLEN) / 1000 - 1; - - while (fragsize) { - fragsize >>= 1; - buffer->ossfragshift++; - } - } - - if (buffer->ossfragshift < WAVEOUT_MINFRAGSHIFT) - buffer->ossfragshift = WAVEOUT_MINFRAGSHIFT; - - buffer->fragment_size = 1 << buffer->ossfragshift; - - if (!buffer->numfrags) { - u32 numfrags; - - numfrags = (woinst->format.bytespervoicesample * woinst->format.samplingrate * WAVEOUT_DEFAULTBUFLEN) / - (buffer->fragment_size * 1000) - 1; - - buffer->numfrags = 1; - - while (numfrags) { - numfrags >>= 1; - buffer->numfrags <<= 1; - } - } - - if (buffer->numfrags < MINFRAGS) - buffer->numfrags = MINFRAGS; - - if (buffer->numfrags * buffer->fragment_size > WAVEOUT_MAXBUFSIZE) { - buffer->numfrags = WAVEOUT_MAXBUFSIZE / buffer->fragment_size; - - if (buffer->numfrags < MINFRAGS) { - buffer->numfrags = MINFRAGS; - buffer->fragment_size = WAVEOUT_MAXBUFSIZE / MINFRAGS; - } - - } else if (buffer->numfrags * buffer->fragment_size < WAVEOUT_MINBUFSIZE) - buffer->numfrags = WAVEOUT_MINBUFSIZE / buffer->fragment_size; - - buffer->size = buffer->fragment_size * buffer->numfrags; - buffer->pages = buffer->size / PAGE_SIZE + ((buffer->size % PAGE_SIZE) ? 1 : 0); - - DPD(2, " calculated playback fragment_size -> %d\n", buffer->fragment_size); - DPD(2, " calculated playback numfrags -> %d\n", buffer->numfrags); - - return; -} - -static void calculate_ifrag(struct wiinst *wiinst) -{ - struct wavein_buffer *buffer = &wiinst->buffer; - u32 fragsize, bufsize, size[4]; - int i, j; - - if (buffer->fragment_size) - return; - - if (!buffer->ossfragshift) { - fragsize = (wiinst->format.bytespersec * WAVEIN_DEFAULTFRAGLEN) / 1000 - 1; - - while (fragsize) { - fragsize >>= 1; - buffer->ossfragshift++; - } - } - - if (buffer->ossfragshift < WAVEIN_MINFRAGSHIFT) - buffer->ossfragshift = WAVEIN_MINFRAGSHIFT; - - buffer->fragment_size = 1 << buffer->ossfragshift; - - if (!buffer->numfrags) - buffer->numfrags = (wiinst->format.bytespersec * WAVEIN_DEFAULTBUFLEN) / (buffer->fragment_size * 1000) - 1; - - if (buffer->numfrags < MINFRAGS) - buffer->numfrags = MINFRAGS; - - if (buffer->numfrags * buffer->fragment_size > WAVEIN_MAXBUFSIZE) { - buffer->numfrags = WAVEIN_MAXBUFSIZE / buffer->fragment_size; - - if (buffer->numfrags < MINFRAGS) { - buffer->numfrags = MINFRAGS; - buffer->fragment_size = WAVEIN_MAXBUFSIZE / MINFRAGS; - } - } else if (buffer->numfrags * buffer->fragment_size < WAVEIN_MINBUFSIZE) - buffer->numfrags = WAVEIN_MINBUFSIZE / buffer->fragment_size; - - bufsize = buffer->fragment_size * buffer->numfrags; - - if (bufsize >= 0x10000) { - buffer->size = 0x10000; - buffer->sizeregval = 0x1f; - } else { - buffer->size = 0; - size[0] = 384; - size[1] = 448; - size[2] = 512; - size[3] = 640; - - for (i = 0; i < 8; i++) - for (j = 0; j < 4; j++) - if (bufsize >= size[j]) { - buffer->size = size[j]; - size[j] *= 2; - buffer->sizeregval = i * 4 + j + 1; - } else - goto exitloop; - exitloop: - if (buffer->size == 0) { - buffer->size = 384; - buffer->sizeregval = 0x01; - } - } - - buffer->numfrags = buffer->size / buffer->fragment_size; - buffer->pages = buffer->size / PAGE_SIZE + ((buffer->size % PAGE_SIZE) ? 1 : 0); - if (buffer->size % buffer->fragment_size) - BUG(); - - DPD(2, " calculated recording fragment_size -> %d\n", buffer->fragment_size); - DPD(2, " calculated recording numfrags -> %d\n", buffer->numfrags); - DPD(2, " buffer size register -> %#04x\n", buffer->sizeregval); - - return; -} - -void emu10k1_wavein_bh(unsigned long refdata) -{ - struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) refdata; - struct wiinst *wiinst = wave_dev->wiinst; - u32 bytestocopy; - unsigned long flags; - - if (!wiinst) - return; - - spin_lock_irqsave(&wiinst->lock, flags); - - if (!(wiinst->state & WAVE_STATE_STARTED)) { - spin_unlock_irqrestore(&wiinst->lock, flags); - return; - } - - emu10k1_wavein_update(wave_dev->card, wiinst); - emu10k1_wavein_getxfersize(wiinst, &bytestocopy); - - spin_unlock_irqrestore(&wiinst->lock, flags); - - if (bytestocopy >= wiinst->buffer.fragment_size) { - if (waitqueue_active(&wiinst->wait_queue)) - wake_up_interruptible(&wiinst->wait_queue); - } else - DPD(3, "Not enough transfer size, %d\n", bytestocopy); - - return; -} - -void emu10k1_waveout_bh(unsigned long refdata) -{ - struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) refdata; - struct woinst *woinst = wave_dev->woinst; - u32 bytestocopy; - unsigned long flags; - - if (!woinst) - return; - - spin_lock_irqsave(&woinst->lock, flags); - - if (!(woinst->state & WAVE_STATE_STARTED)) { - spin_unlock_irqrestore(&woinst->lock, flags); - return; - } - - emu10k1_waveout_update(woinst); - emu10k1_waveout_getxfersize(woinst, &bytestocopy); - - if (woinst->buffer.fill_silence) { - spin_unlock_irqrestore(&woinst->lock, flags); - emu10k1_waveout_fillsilence(woinst); - } else - spin_unlock_irqrestore(&woinst->lock, flags); - - if (bytestocopy >= woinst->buffer.fragment_size) { - if (waitqueue_active(&woinst->wait_queue)) - wake_up_interruptible(&woinst->wait_queue); - } else - DPD(3, "Not enough transfer size -> %d\n", bytestocopy); - - return; -} - -struct file_operations emu10k1_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: emu10k1_audio_read, - write: emu10k1_audio_write, - poll: emu10k1_audio_poll, - ioctl: emu10k1_audio_ioctl, - mmap: emu10k1_audio_mmap, - open: emu10k1_audio_open, - release: emu10k1_audio_release, -}; diff -Nru a/drivers/sound/emu10k1/audio.h b/drivers/sound/emu10k1/audio.h --- a/drivers/sound/emu10k1/audio.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,49 +0,0 @@ -/* - ********************************************************************** - * audio.c -- /dev/dsp interface for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * November 2, 1999 Alan Cox cleaned up types/leaks - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _AUDIO_H -#define _AUDIO_H - -#define MINFRAGS 2 /* _don't_ got bellow 2 */ - -struct emu10k1_wavedevice -{ - struct emu10k1_card *card; - struct wiinst *wiinst; - struct woinst *woinst; - u16 enablebits; -}; - -void emu10k1_waveout_bh(unsigned long); -void emu10k1_wavein_bh(unsigned long); - -#endif /* _AUDIO_H */ diff -Nru a/drivers/sound/emu10k1/cardmi.c b/drivers/sound/emu10k1/cardmi.c --- a/drivers/sound/emu10k1/cardmi.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,817 +0,0 @@ -/* - ********************************************************************** - * sblive_mi.c - MIDI UART input HAL for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * November 2, 1999 Alan Cox clean up - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include -#include - -#include "hwaccess.h" -#include "8010.h" -#include "cardmi.h" -#include "irqmgr.h" - -static struct { - int (*Fn) (struct emu10k1_mpuin *, u8); -} midistatefn[] = { - - { - sblive_miStateParse}, { - sblive_miState3Byte}, /* 0x8n, 0x9n, 0xAn, 0xBn, 0xEn */ - { - sblive_miState3ByteKey}, /* Byte 1 */ - { - sblive_miState3ByteVel}, /* Byte 2 */ - { - sblive_miState2Byte}, /* 0xCn, 0xDn */ - { - sblive_miState2ByteKey}, /* Byte 1 */ - { - sblive_miStateSysCommon2}, /* 0xF1 , 0xF3 */ - { - sblive_miStateSysCommon2Key}, /* 0xF1 , 0xF3, Byte 1 */ - { - sblive_miStateSysCommon3}, /* 0xF2 */ - { - sblive_miStateSysCommon3Key}, /* 0xF2 , Byte 1 */ - { - sblive_miStateSysCommon3Vel}, /* 0xF2 , Byte 2 */ - { - sblive_miStateSysExNorm}, /* 0xF0, 0xF7, Normal mode */ - { - sblive_miStateSysReal} /* 0xF4 - 0xF6 ,0xF8 - 0xFF */ -}; - -/* Installs the IRQ handler for the MPU in port */ - -/* and initialize parameters */ - -int emu10k1_mpuin_open(struct emu10k1_card *card, struct midi_openinfo *openinfo) -{ - struct emu10k1_mpuin *card_mpuin = card->mpuin; - - DPF(2, "emu10k1_mpuin_open\n"); - - if (!(card_mpuin->status & FLAGS_AVAILABLE)) - return -1; - - /* Copy open info and mark channel as in use */ - card_mpuin->openinfo = *openinfo; - card_mpuin->status &= ~FLAGS_AVAILABLE; /* clear */ - card_mpuin->status |= FLAGS_READY; /* set */ - card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ - card_mpuin->firstmidiq = NULL; - card_mpuin->lastmidiq = NULL; - card_mpuin->qhead = 0; - card_mpuin->qtail = 0; - - sblive_miStateInit(card_mpuin); - - emu10k1_mpu_reset(card); - emu10k1_mpu_acquire(card); - - return 0; -} - -int emu10k1_mpuin_close(struct emu10k1_card *card) -{ - struct emu10k1_mpuin *card_mpuin = card->mpuin; - - DPF(2, "emu10k1_mpuin_close()\n"); - - /* Check if there are pending input SysEx buffers */ - if (card_mpuin->firstmidiq != NULL) { - ERROR(); - return -1; - } - - /* Disable RX interrupt */ - emu10k1_irq_disable(card, INTE_MIDIRXENABLE); - - emu10k1_mpu_release(card); - - card_mpuin->status |= FLAGS_AVAILABLE; /* set */ - card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ - - return 0; -} - -/* Adds MIDI buffer to local queue list */ - -int emu10k1_mpuin_add_buffer(struct emu10k1_mpuin *card_mpuin, struct midi_hdr *midihdr) -{ - struct midi_queue *midiq; - unsigned long flags; - - DPF(2, "emu10k1_mpuin_add_buffer()\n"); - - /* Update MIDI buffer flags */ - midihdr->flags |= MIDIBUF_INQUEUE; /* set */ - midihdr->flags &= ~MIDIBUF_DONE; /* clear */ - - if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_ATOMIC)) == NULL) { - /* Message lost */ - return -1; - } - - midiq->next = NULL; - midiq->qtype = 1; - midiq->length = midihdr->bufferlength; - midiq->sizeLeft = midihdr->bufferlength; - midiq->midibyte = midihdr->data; - midiq->refdata = (unsigned long) midihdr; - - spin_lock_irqsave(&card_mpuin->lock, flags); - - if (card_mpuin->firstmidiq == NULL) { - card_mpuin->firstmidiq = midiq; - card_mpuin->lastmidiq = midiq; - } else { - (card_mpuin->lastmidiq)->next = midiq; - card_mpuin->lastmidiq = midiq; - } - - spin_unlock_irqrestore(&card_mpuin->lock, flags); - - return 0; -} - -/* First set the Time Stamp if MIDI IN has not started. */ - -/* Then enable RX Irq. */ - -int emu10k1_mpuin_start(struct emu10k1_card *card) -{ - struct emu10k1_mpuin *card_mpuin = card->mpuin; - u8 dummy; - - DPF(2, "emu10k1_mpuin_start()\n"); - - /* Set timestamp if not set */ - if (card_mpuin->status & FLAGS_MIDM_STARTED) { - DPF(2, "Time Stamp not changed\n"); - } else { - while (!emu10k1_mpu_read_data(card, &dummy)); - - card_mpuin->status |= FLAGS_MIDM_STARTED; /* set */ - - /* Set new time stamp */ - card_mpuin->timestart = (jiffies * 1000) / HZ; - DPD(2, "New Time Stamp = %d\n", card_mpuin->timestart); - - card_mpuin->qhead = 0; - card_mpuin->qtail = 0; - - emu10k1_irq_enable(card, INTE_MIDIRXENABLE); - } - - return 0; -} - -/* Disable the RX Irq. If a partial recorded buffer */ - -/* exist, send it up to IMIDI level. */ - -int emu10k1_mpuin_stop(struct emu10k1_card *card) -{ - struct emu10k1_mpuin *card_mpuin = card->mpuin; - struct midi_queue *midiq; - unsigned long flags; - - DPF(2, "emu10k1_mpuin_stop()\n"); - - emu10k1_irq_disable(card, INTE_MIDIRXENABLE); - - card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ - - if (card_mpuin->firstmidiq) { - spin_lock_irqsave(&card_mpuin->lock, flags); - - midiq = card_mpuin->firstmidiq; - if (midiq != NULL) { - if (midiq->sizeLeft == midiq->length) - midiq = NULL; - else { - card_mpuin->firstmidiq = midiq->next; - if (card_mpuin->firstmidiq == NULL) - card_mpuin->lastmidiq = NULL; - } - } - - spin_unlock_irqrestore(&card_mpuin->lock, flags); - - if (midiq) { - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0); - kfree(midiq); - } - } - - return 0; -} - -/* Disable the RX Irq. If any buffer */ - -/* exist, send it up to IMIDI level. */ -int emu10k1_mpuin_reset(struct emu10k1_card *card) -{ - struct emu10k1_mpuin *card_mpuin = card->mpuin; - struct midi_queue *midiq; - - DPF(2, "emu10k1_mpuin_reset()\n"); - - emu10k1_irq_disable(card, INTE_MIDIRXENABLE); - - while (card_mpuin->firstmidiq) { - midiq = card_mpuin->firstmidiq; - card_mpuin->firstmidiq = midiq->next; - - if (midiq->sizeLeft == midiq->length) - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0); - else - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0); - - kfree(midiq); - } - - card_mpuin->lastmidiq = NULL; - card_mpuin->status &= ~FLAGS_MIDM_STARTED; - - return 0; -} - -/* Passes the message with the data back to the client */ - -/* via IRQ & DPC callbacks to Ring 3 */ -int emu10k1_mpuin_callback(struct emu10k1_mpuin *card_mpuin, u32 msg, unsigned long data, u32 bytesvalid) -{ - unsigned long timein; - struct midi_queue *midiq; - unsigned long callback_msg[3]; - struct midi_hdr *midihdr; - - /* Called during ISR. The data & code touched are: - * 1. card_mpuin - * 2. The function to be called - */ - - timein = card_mpuin->timein; - if (card_mpuin->timestart <= timein) - callback_msg[0] = timein - card_mpuin->timestart; - else - callback_msg[0] = (~0x0L - card_mpuin->timestart) + timein; - - if (msg == ICARDMIDI_INDATA || msg == ICARDMIDI_INDATAERROR) { - callback_msg[1] = data; - callback_msg[2] = bytesvalid; - DPD(2, "emu10k1_mpuin_callback: midimsg = %#lx\n", data); - } else { - midiq = (struct midi_queue *) data; - midihdr = (struct midi_hdr *) midiq->refdata; - - callback_msg[1] = midiq->length - midiq->sizeLeft; - callback_msg[2] = midiq->refdata; - midihdr->flags &= ~MIDIBUF_INQUEUE; - midihdr->flags |= MIDIBUF_DONE; - - midihdr->bytesrecorded = midiq->length - midiq->sizeLeft; - } - - /* Notify client that Sysex buffer has been sent */ - emu10k1_midi_callback(msg, card_mpuin->openinfo.refdata, callback_msg); - - return 0; -} - -void emu10k1_mpuin_bh(unsigned long refdata) -{ - u8 data; - unsigned idx; - struct emu10k1_mpuin *card_mpuin = (struct emu10k1_mpuin *) refdata; - unsigned long flags; - - while (card_mpuin->qhead != card_mpuin->qtail) { - spin_lock_irqsave(&card_mpuin->lock, flags); - idx = card_mpuin->qhead; - data = card_mpuin->midiq[idx].data; - card_mpuin->timein = card_mpuin->midiq[idx].timein; - idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE; - card_mpuin->qhead = idx; - spin_unlock_irqrestore(&card_mpuin->lock, flags); - - sblive_miStateEntry(card_mpuin, data); - } - - return; -} - -/* IRQ callback handler routine for the MPU in port */ - -int emu10k1_mpuin_irqhandler(struct emu10k1_card *card) -{ - unsigned idx; - unsigned count; - u8 MPUIvalue; - struct emu10k1_mpuin *card_mpuin = card->mpuin; - - /* IRQ service routine. The data and code touched are: - * 1. card_mpuin - */ - - count = 0; - idx = card_mpuin->qtail; - - while (1) { - if (emu10k1_mpu_read_data(card, &MPUIvalue) < 0) { - break; - } else { - ++count; - card_mpuin->midiq[idx].data = MPUIvalue; - card_mpuin->midiq[idx].timein = (jiffies * 1000) / HZ; - idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE; - } - } - - if (count) { - card_mpuin->qtail = idx; - - tasklet_hi_schedule(&card_mpuin->tasklet); - } - - return 0; -} - -/*****************************************************************************/ - -/* Supporting functions for Midi-In Interpretation State Machine */ - -/*****************************************************************************/ - -/* FIXME: This should be a macro */ -int sblive_miStateInit(struct emu10k1_mpuin *card_mpuin) -{ - card_mpuin->status = 0; /* For MIDI running status */ - card_mpuin->fstatus = 0; /* For 0xFn status only */ - card_mpuin->curstate = STIN_PARSE; - card_mpuin->laststate = STIN_PARSE; - card_mpuin->data = 0; - card_mpuin->timestart = 0; - card_mpuin->timein = 0; - - return 0; -} - -/* FIXME: This should be a macro */ -int sblive_miStateEntry(struct emu10k1_mpuin *card_mpuin, u8 data) -{ - return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data); -} - -int sblive_miStateParse(struct emu10k1_mpuin *card_mpuin, u8 data) -{ - switch (data & 0xf0) { - case 0x80: - case 0x90: - case 0xA0: - case 0xB0: - case 0xE0: - card_mpuin->curstate = STIN_3BYTE; - break; - - case 0xC0: - case 0xD0: - card_mpuin->curstate = STIN_2BYTE; - break; - - case 0xF0: - /* System messages do not affect the previous running status! */ - switch (data & 0x0f) { - case 0x0: - card_mpuin->laststate = card_mpuin->curstate; - card_mpuin->curstate = STIN_SYS_EX_NORM; - - if (card_mpuin->firstmidiq) { - struct midi_queue *midiq; - - midiq = card_mpuin->firstmidiq; - *midiq->midibyte = data; - --midiq->sizeLeft; - ++midiq->midibyte; - } - - return CTSTATUS_NEXT_BYTE; - - case 0x7: - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, 0xf7, 0); - return -1; - - case 0x2: - card_mpuin->laststate = card_mpuin->curstate; - card_mpuin->curstate = STIN_SYS_COMMON_3; - break; - - case 0x1: - case 0x3: - card_mpuin->laststate = card_mpuin->curstate; - card_mpuin->curstate = STIN_SYS_COMMON_2; - break; - - default: - /* includes 0xF4 - 0xF6, 0xF8 - 0xFF */ - return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); - } - - break; - - default: - DPF(2, "BUG: default case hit\n"); - return -1; - } - - return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data); -} - -int sblive_miState3Byte(struct emu10k1_mpuin *card_mpuin, u8 data) -{ - u8 temp = data & 0xf0; - - if (temp < 0x80) { - return midistatefn[STIN_3BYTE_KEY].Fn(card_mpuin, data); - } else if (temp <= 0xe0 && temp != 0xc0 && temp != 0xd0) { - card_mpuin->status = data; - card_mpuin->curstate = STIN_3BYTE_KEY; - - return CTSTATUS_NEXT_BYTE; - } - - return midistatefn[STIN_PARSE].Fn(card_mpuin, data); -} - -int sblive_miState3ByteKey(struct emu10k1_mpuin *card_mpuin, u8 data) - -/* byte 1 */ -{ - unsigned long tmp; - - if (data > 0x7f) { - /* Real-time messages check */ - if (data > 0xf7) - return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); - - /* Invalid data! */ - DPF(2, "Invalid data!\n"); - - card_mpuin->curstate = STIN_PARSE; - tmp = ((unsigned long) data) << 8; - tmp |= (unsigned long) card_mpuin->status; - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); - - return -1; - } - - card_mpuin->data = data; - card_mpuin->curstate = STIN_3BYTE_VEL; - - return CTSTATUS_NEXT_BYTE; -} - -int sblive_miState3ByteVel(struct emu10k1_mpuin *card_mpuin, u8 data) - -/* byte 2 */ -{ - unsigned long tmp; - - if (data > 0x7f) { - /* Real-time messages check */ - if (data > 0xf7) - return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); - - /* Invalid data! */ - DPF(2, "Invalid data!\n"); - - card_mpuin->curstate = STIN_PARSE; - tmp = ((unsigned long) data) << 8; - tmp |= card_mpuin->data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->status; - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); - - return -1; - } - - card_mpuin->curstate = STIN_3BYTE; - tmp = (unsigned long) data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->status; - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3); - - return 0; -} - -int sblive_miState2Byte(struct emu10k1_mpuin *card_mpuin, u8 data) -{ - u8 temp = data & 0xf0; - - if ((temp == 0xc0) || (temp == 0xd0)) { - card_mpuin->status = data; - card_mpuin->curstate = STIN_2BYTE_KEY; - - return CTSTATUS_NEXT_BYTE; - } - - if (temp < 0x80) - return midistatefn[STIN_2BYTE_KEY].Fn(card_mpuin, data); - - return midistatefn[STIN_PARSE].Fn(card_mpuin, data); -} - -int sblive_miState2ByteKey(struct emu10k1_mpuin *card_mpuin, u8 data) - -/* byte 1 */ -{ - unsigned long tmp; - - if (data > 0x7f) { - /* Real-time messages check */ - if (data > 0xf7) - return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); - - /* Invalid data! */ - DPF(2, "Invalid data!\n"); - - card_mpuin->curstate = STIN_PARSE; - tmp = (unsigned long) data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->status; - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); - - return -1; - } - - card_mpuin->curstate = STIN_2BYTE; - tmp = (unsigned long) data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->status; - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2); - - return 0; -} - -int sblive_miStateSysCommon2(struct emu10k1_mpuin *card_mpuin, u8 data) -{ - card_mpuin->fstatus = data; - card_mpuin->curstate = STIN_SYS_COMMON_2_KEY; - - return CTSTATUS_NEXT_BYTE; -} - -int sblive_miStateSysCommon2Key(struct emu10k1_mpuin *card_mpuin, u8 data) - -/* byte 1 */ -{ - unsigned long tmp; - - if (data > 0x7f) { - /* Real-time messages check */ - if (data > 0xf7) - return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); - - /* Invalid data! */ - DPF(2, "Invalid data!\n"); - - card_mpuin->curstate = card_mpuin->laststate; - tmp = (unsigned long) data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->fstatus; - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); - - return -1; - } - - card_mpuin->curstate = card_mpuin->laststate; - tmp = (unsigned long) data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->fstatus; - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2); - - return 0; -} - -int sblive_miStateSysCommon3(struct emu10k1_mpuin *card_mpuin, u8 data) -{ - card_mpuin->fstatus = data; - card_mpuin->curstate = STIN_SYS_COMMON_3_KEY; - - return CTSTATUS_NEXT_BYTE; -} - -int sblive_miStateSysCommon3Key(struct emu10k1_mpuin *card_mpuin, u8 data) - -/* byte 1 */ -{ - unsigned long tmp; - - if (data > 0x7f) { - /* Real-time messages check */ - if (data > 0xf7) - return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); - - /* Invalid data! */ - DPF(2, "Invalid data!\n"); - - card_mpuin->curstate = card_mpuin->laststate; - tmp = (unsigned long) data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->fstatus; - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); - - return -1; - } - - card_mpuin->data = data; - card_mpuin->curstate = STIN_SYS_COMMON_3_VEL; - - return CTSTATUS_NEXT_BYTE; -} - -int sblive_miStateSysCommon3Vel(struct emu10k1_mpuin *card_mpuin, u8 data) - -/* byte 2 */ -{ - unsigned long tmp; - - if (data > 0x7f) { - /* Real-time messages check */ - if (data > 0xf7) - return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); - - /* Invalid data! */ - DPF(2, "Invalid data!\n"); - - card_mpuin->curstate = card_mpuin->laststate; - tmp = (unsigned long) data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->fstatus; - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); - - return -1; - } - - card_mpuin->curstate = card_mpuin->laststate; - tmp = (unsigned long) data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->data; - tmp = tmp << 8; - tmp |= (unsigned long) card_mpuin->fstatus; - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3); - - return 0; -} - -int sblive_miStateSysExNorm(struct emu10k1_mpuin *card_mpuin, u8 data) -{ - unsigned long flags; - - if ((data > 0x7f) && (data != 0xf7)) { - /* Real-time messages check */ - if (data > 0xf7) - return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); - - /* Invalid Data! */ - DPF(2, "Invalid data!\n"); - - card_mpuin->curstate = card_mpuin->laststate; - - if (card_mpuin->firstmidiq) { - struct midi_queue *midiq; - - midiq = card_mpuin->firstmidiq; - *midiq->midibyte = data; - --midiq->sizeLeft; - ++midiq->midibyte; - - spin_lock_irqsave(&card_mpuin->lock, flags); - - card_mpuin->firstmidiq = midiq->next; - if (card_mpuin->firstmidiq == NULL) - card_mpuin->lastmidiq = NULL; - - spin_unlock_irqrestore(&card_mpuin->lock, flags); - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0); - - kfree(midiq); - } - - return -1; - } - - if (card_mpuin->firstmidiq) { - struct midi_queue *midiq; - - midiq = card_mpuin->firstmidiq; - *midiq->midibyte = data; - --midiq->sizeLeft; - ++midiq->midibyte; - } - - if (data == 0xf7) { - /* End of Sysex buffer */ - /* Send down the buffer */ - - card_mpuin->curstate = card_mpuin->laststate; - - if (card_mpuin->firstmidiq) { - struct midi_queue *midiq; - - midiq = card_mpuin->firstmidiq; - - spin_lock_irqsave(&card_mpuin->lock, flags); - - card_mpuin->firstmidiq = midiq->next; - if (card_mpuin->firstmidiq == NULL) - card_mpuin->lastmidiq = NULL; - - spin_unlock_irqrestore(&card_mpuin->lock, flags); - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0); - - kfree(midiq); - } - - return 0; - } - - if (card_mpuin->firstmidiq) { - struct midi_queue *midiq; - - midiq = card_mpuin->firstmidiq; - - if (midiq->sizeLeft == 0) { - /* Special case */ - - spin_lock_irqsave(&card_mpuin->lock, flags); - - card_mpuin->firstmidiq = midiq->next; - if (card_mpuin->firstmidiq == NULL) - card_mpuin->lastmidiq = NULL; - - spin_unlock_irqrestore(&card_mpuin->lock, flags); - - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0); - - kfree(midiq); - - return CTSTATUS_NEXT_BYTE; - } - } - - return CTSTATUS_NEXT_BYTE; -} - -int sblive_miStateSysReal(struct emu10k1_mpuin *card_mpuin, u8 data) -{ - emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, data, 1); - - return CTSTATUS_NEXT_BYTE; -} diff -Nru a/drivers/sound/emu10k1/cardmi.h b/drivers/sound/emu10k1/cardmi.h --- a/drivers/sound/emu10k1/cardmi.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,114 +0,0 @@ -/* - ********************************************************************** - * sblive_mi.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * November 2, 1999 Alan Cox cleaned up - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _CARDMI_H -#define _CARDMI_H - -#include "icardmid.h" -#include - -typedef enum -{ - STIN_PARSE = 0, - STIN_3BYTE, /* 0x80, 0x90, 0xA0, 0xB0, 0xE0 */ - STIN_3BYTE_KEY, /* Byte 1 */ - STIN_3BYTE_VEL, /* Byte 1 */ - STIN_2BYTE, /* 0xC0, 0xD0 */ - STIN_2BYTE_KEY, /* Byte 1 */ - STIN_SYS_COMMON_2, /* 0xF1, 0xF3 */ - STIN_SYS_COMMON_2_KEY, - STIN_SYS_COMMON_3, /* 0xF2 */ - STIN_SYS_COMMON_3_KEY, - STIN_SYS_COMMON_3_VEL, - STIN_SYS_EX_NORM, /* 0xF0, Normal mode */ - STIN_SYS_REAL -} midi_in_state; - - -/* flags for card MIDI in object */ -#define FLAGS_MIDM_STARTED 0x00001000 // Data has started to come in after Midm Start -#define MIDIIN_MAX_BUFFER_SIZE 200 // Definition for struct emu10k1_mpuin - -struct midi_data -{ - u8 data; - u32 timein; -}; - -struct emu10k1_mpuin -{ - spinlock_t lock; - struct midi_queue *firstmidiq; - struct midi_queue *lastmidiq; - unsigned qhead, qtail; - struct midi_data midiq[MIDIIN_MAX_BUFFER_SIZE]; - struct tasklet_struct tasklet; - struct midi_openinfo openinfo; - - /* For MIDI state machine */ - u8 status; /* For MIDI running status */ - u8 fstatus; /* For 0xFn status only */ - midi_in_state curstate; - midi_in_state laststate; - u32 timestart; - u32 timein; - u8 data; -}; - -int emu10k1_mpuin_open(struct emu10k1_card *, struct midi_openinfo *); -int emu10k1_mpuin_close(struct emu10k1_card *); -int emu10k1_mpuin_add_buffer(struct emu10k1_mpuin *, struct midi_hdr *); -int emu10k1_mpuin_start(struct emu10k1_card *); -int emu10k1_mpuin_stop(struct emu10k1_card *); -int emu10k1_mpuin_reset(struct emu10k1_card *); - -int sblive_miStateInit(struct emu10k1_mpuin *); -int sblive_miStateEntry(struct emu10k1_mpuin *, u8); -int sblive_miStateParse(struct emu10k1_mpuin *, u8); -int sblive_miState3Byte(struct emu10k1_mpuin *, u8); -int sblive_miState3ByteKey(struct emu10k1_mpuin *, u8); -int sblive_miState3ByteVel(struct emu10k1_mpuin *, u8); -int sblive_miState2Byte(struct emu10k1_mpuin *, u8); -int sblive_miState2ByteKey(struct emu10k1_mpuin *, u8); -int sblive_miStateSysCommon2(struct emu10k1_mpuin *, u8); -int sblive_miStateSysCommon2Key(struct emu10k1_mpuin *, u8); -int sblive_miStateSysCommon3(struct emu10k1_mpuin *, u8); -int sblive_miStateSysCommon3Key(struct emu10k1_mpuin *, u8); -int sblive_miStateSysCommon3Vel(struct emu10k1_mpuin *, u8); -int sblive_miStateSysExNorm(struct emu10k1_mpuin *, u8); -int sblive_miStateSysReal(struct emu10k1_mpuin *, u8); - -int emu10k1_mpuin_irqhandler(struct emu10k1_card *); -void emu10k1_mpuin_bh(unsigned long); -int emu10k1_mpuin_callback(struct emu10k1_mpuin *card_mpuin, u32 msg, unsigned long data, u32 bytesvalid); - -#endif /* _CARDMI_H */ diff -Nru a/drivers/sound/emu10k1/cardmo.c b/drivers/sound/emu10k1/cardmo.c --- a/drivers/sound/emu10k1/cardmo.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,229 +0,0 @@ -/* - ********************************************************************** - * cardmo.c - MIDI UART output HAL for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * November 2, 1999 Alan Cox cleaned up - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include - -#include "hwaccess.h" -#include "8010.h" -#include "cardmo.h" -#include "irqmgr.h" - -/* Installs the IRQ handler for the MPU out port * - * and initialize parameters */ - -int emu10k1_mpuout_open(struct emu10k1_card *card, struct midi_openinfo *openinfo) -{ - struct emu10k1_mpuout *card_mpuout = card->mpuout; - - DPF(2, "emu10k1_mpuout_open()\n"); - - if (!(card_mpuout->status & FLAGS_AVAILABLE)) - return -1; - - /* Copy open info and mark channel as in use */ - card_mpuout->intr = 0; - card_mpuout->openinfo = *openinfo; - card_mpuout->status &= ~FLAGS_AVAILABLE; - card_mpuout->laststatus = 0x80; - card_mpuout->firstmidiq = NULL; - card_mpuout->lastmidiq = NULL; - - emu10k1_mpu_reset(card); - emu10k1_mpu_acquire(card); - - return 0; -} - -int emu10k1_mpuout_close(struct emu10k1_card *card) -{ - struct emu10k1_mpuout *card_mpuout = card->mpuout; - struct midi_queue *midiq; - struct midi_hdr *midihdr; - unsigned long flags; - - DPF(2, "emu10k1_mpuout_close()\n"); - - emu10k1_irq_disable(card, INTE_MIDITXENABLE); - - spin_lock_irqsave(&card_mpuout->lock, flags); - - while (card_mpuout->firstmidiq != NULL) { - midiq = card_mpuout->firstmidiq; - midihdr = (struct midi_hdr *) midiq->refdata; - - card_mpuout->firstmidiq = midiq->next; - - kfree(midihdr->data); - kfree(midihdr); - kfree(midiq); - } - - card_mpuout->lastmidiq = NULL; - - emu10k1_mpu_release(card); - - card_mpuout->status |= FLAGS_AVAILABLE; - - spin_unlock_irqrestore(&card_mpuout->lock, flags); - - return 0; -} - -/* If there isn't enough buffer space, reject Midi Buffer. * -* Otherwise, disable TX, create object to hold Midi * -* uffer, update buffer flags and other parameters * -* before enabling TX again. */ - -int emu10k1_mpuout_add_buffer(struct emu10k1_card *card, struct midi_hdr *midihdr) -{ - struct emu10k1_mpuout *card_mpuout = card->mpuout; - struct midi_queue *midiq; - unsigned long flags; - - DPF(2, "emu10k1_mpuout_add_buffer()\n"); - - if (card_mpuout->state == CARDMIDIOUT_STATE_SUSPEND) - return 0; - - midihdr->flags |= MIDIBUF_INQUEUE; - midihdr->flags &= ~MIDIBUF_DONE; - - if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_KERNEL)) == NULL) { - /* Message lost */ - return -1; - } - - midiq->next = NULL; - midiq->qtype = 1; - midiq->length = midihdr->bufferlength; - midiq->sizeLeft = midihdr->bufferlength; - midiq->midibyte = midihdr->data; - - midiq->refdata = (unsigned long) midihdr; - - spin_lock_irqsave(&card_mpuout->lock, flags); - - if (card_mpuout->firstmidiq == NULL) { - card_mpuout->firstmidiq = midiq; - card_mpuout->lastmidiq = midiq; - } else { - (card_mpuout->lastmidiq)->next = midiq; - card_mpuout->lastmidiq = midiq; - } - - card_mpuout->intr = 0; - - emu10k1_irq_enable(card, INTE_MIDITXENABLE); - - spin_unlock_irqrestore(&card_mpuout->lock, flags); - - return 0; -} - -void emu10k1_mpuout_bh(unsigned long refdata) -{ - struct emu10k1_card *card = (struct emu10k1_card *) refdata; - struct emu10k1_mpuout *card_mpuout = card->mpuout; - int cByteSent = 0; - struct midi_queue *midiq; - struct midi_queue *doneq = NULL; - unsigned long flags; - - spin_lock_irqsave(&card_mpuout->lock, flags); - - while (card_mpuout->firstmidiq != NULL) { - midiq = card_mpuout->firstmidiq; - - while (cByteSent < 4 && midiq->sizeLeft) { - if (emu10k1_mpu_write_data(card, *midiq->midibyte) < 0) { - DPF(2, "emu10k1_mpuoutDpcCallback error!!\n"); - } else { - ++cByteSent; - --midiq->sizeLeft; - ++midiq->midibyte; - } - } - - if (midiq->sizeLeft == 0) { - if (doneq == NULL) - doneq = midiq; - card_mpuout->firstmidiq = midiq->next; - } else - break; - } - - if (card_mpuout->firstmidiq == NULL) - card_mpuout->lastmidiq = NULL; - - if (doneq != NULL) { - while (doneq != card_mpuout->firstmidiq) { - unsigned long callback_msg[3]; - - midiq = doneq; - doneq = midiq->next; - - if (midiq->qtype) { - callback_msg[0] = 0; - callback_msg[1] = midiq->length; - callback_msg[2] = midiq->refdata; - - emu10k1_midi_callback(ICARDMIDI_OUTLONGDATA, card_mpuout->openinfo.refdata, callback_msg); - } else if (((u8) midiq->refdata) < 0xF0 && ((u8) midiq->refdata) > 0x7F) - card_mpuout->laststatus = (u8) midiq->refdata; - - kfree(midiq); - } - } - - if ((card_mpuout->firstmidiq != NULL) || cByteSent) { - card_mpuout->intr = 0; - emu10k1_irq_enable(card, INTE_MIDITXENABLE); - } - - spin_unlock_irqrestore(&card_mpuout->lock, flags); - - return; -} - -int emu10k1_mpuout_irqhandler(struct emu10k1_card *card) -{ - struct emu10k1_mpuout *card_mpuout = card->mpuout; - - DPF(4, "emu10k1_mpuout_irqhandler\n"); - - card_mpuout->intr = 1; - emu10k1_irq_disable(card, INTE_MIDITXENABLE); - - tasklet_hi_schedule(&card_mpuout->tasklet); - - return 0; -} diff -Nru a/drivers/sound/emu10k1/cardmo.h b/drivers/sound/emu10k1/cardmo.h --- a/drivers/sound/emu10k1/cardmo.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,62 +0,0 @@ -/* - ********************************************************************** - * cardmo.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * November 2, 1999 Alan Cox cleaned up - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _CARDMO_H -#define _CARDMO_H - -#include "icardmid.h" -#include - -#define CARDMIDIOUT_STATE_DEFAULT 0x00000000 -#define CARDMIDIOUT_STATE_SUSPEND 0x00000001 - -struct emu10k1_mpuout -{ - u32 status; - u32 state; - volatile int intr; - struct midi_queue *firstmidiq; - struct midi_queue *lastmidiq; - u8 laststatus; - struct tasklet_struct tasklet; - spinlock_t lock; - struct midi_openinfo openinfo; -}; - -int emu10k1_mpuout_open(struct emu10k1_card *, struct midi_openinfo *); -int emu10k1_mpuout_close(struct emu10k1_card *); -int emu10k1_mpuout_add_buffer(struct emu10k1_card *, struct midi_hdr *); - -int emu10k1_mpuout_irqhandler(struct emu10k1_card *); -void emu10k1_mpuout_bh(unsigned long); - -#endif /* _CARDMO_H */ diff -Nru a/drivers/sound/emu10k1/cardwi.c b/drivers/sound/emu10k1/cardwi.c --- a/drivers/sound/emu10k1/cardwi.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,371 +0,0 @@ -/* - ********************************************************************** - * cardwi.c - PCM input HAL for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include -#include "hwaccess.h" -#include "timer.h" -#include "recmgr.h" -#include "audio.h" -#include "cardwi.h" - -/** - * query_format - returns a valid sound format - * - * This function will return a valid sound format as close - * to the requested one as possible. - */ -void query_format(int recsrc, struct wave_format *wave_fmt) -{ - - switch (recsrc) { - case WAVERECORD_AC97: - - if ((wave_fmt->channels != 1) && (wave_fmt->channels != 2)) - wave_fmt->channels = 2; - - if (wave_fmt->samplingrate >= (0xBB80 + 0xAC44) / 2) - wave_fmt->samplingrate = 0xBB80; - else if (wave_fmt->samplingrate >= (0xAC44 + 0x7D00) / 2) - wave_fmt->samplingrate = 0xAC44; - else if (wave_fmt->samplingrate >= (0x7D00 + 0x5DC0) / 2) - wave_fmt->samplingrate = 0x7D00; - else if (wave_fmt->samplingrate >= (0x5DC0 + 0x5622) / 2) - wave_fmt->samplingrate = 0x5DC0; - else if (wave_fmt->samplingrate >= (0x5622 + 0x3E80) / 2) - wave_fmt->samplingrate = 0x5622; - else if (wave_fmt->samplingrate >= (0x3E80 + 0x2B11) / 2) - wave_fmt->samplingrate = 0x3E80; - else if (wave_fmt->samplingrate >= (0x2B11 + 0x1F40) / 2) - wave_fmt->samplingrate = 0x2B11; - else - wave_fmt->samplingrate = 0x1F40; - - switch (wave_fmt->id) { - case AFMT_S16_LE: - wave_fmt->bitsperchannel = 16; - break; - case AFMT_U8: - wave_fmt->bitsperchannel = 8; - break; - default: - wave_fmt->id = AFMT_S16_LE; - wave_fmt->bitsperchannel = 16; - break; - } - - break; - - /* these can't be changed from the original values */ - case WAVERECORD_MIC: - case WAVERECORD_FX: - break; - - default: - BUG(); - break; - } - - wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3; - wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel; - wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate; -} - -static int alloc_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer) -{ - buffer->addr = pci_alloc_consistent(card->pci_dev, buffer->size * buffer->cov, - &buffer->dma_handle); - if (buffer->addr == NULL) - return -1; - - return 0; -} - -static void free_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer) -{ - if (buffer->addr != NULL) - pci_free_consistent(card->pci_dev, buffer->size * buffer->cov, - buffer->addr, buffer->dma_handle); -} - -int emu10k1_wavein_open(struct emu10k1_wavedevice *wave_dev) -{ - struct emu10k1_card *card = wave_dev->card; - struct wiinst *wiinst = wave_dev->wiinst; - struct wiinst **wiinst_tmp = NULL; - u32 delay; - unsigned long flags; - - DPF(2, "emu10k1_wavein_open()\n"); - - switch (wiinst->recsrc) { - case WAVERECORD_AC97: - wiinst_tmp = &card->wavein.ac97; - break; - case WAVERECORD_MIC: - wiinst_tmp = &card->wavein.mic; - break; - case WAVERECORD_FX: - wiinst_tmp = &card->wavein.fx; - break; - default: - BUG(); - break; - } - - spin_lock_irqsave(&card->lock, flags); - if (*wiinst_tmp != NULL) { - spin_unlock_irqrestore(&card->lock, flags); - return -1; - } - - *wiinst_tmp = wiinst; - spin_unlock_irqrestore(&card->lock, flags); - - /* handle 8 bit recording */ - if (wiinst->format.bytesperchannel == 1) { - if (wiinst->buffer.size > 0x8000) { - wiinst->buffer.size = 0x8000; - wiinst->buffer.sizeregval = 0x1f; - } else - wiinst->buffer.sizeregval += 4; - - wiinst->buffer.cov = 2; - } else - wiinst->buffer.cov = 1; - - if (alloc_buffer(card, &wiinst->buffer) < 0) { - ERROR(); - emu10k1_wavein_close(wave_dev); - return -1; - } - - emu10k1_set_record_src(card, wiinst); - - delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec; - - emu10k1_timer_install(card, &wiinst->timer, delay / 2); - - wiinst->state = WAVE_STATE_OPEN; - - return 0; -} - -void emu10k1_wavein_close(struct emu10k1_wavedevice *wave_dev) -{ - struct emu10k1_card *card = wave_dev->card; - struct wiinst *wiinst = wave_dev->wiinst; - unsigned long flags; - - DPF(2, "emu10k1_wavein_close()\n"); - - emu10k1_wavein_stop(wave_dev); - - emu10k1_timer_uninstall(card, &wiinst->timer); - - free_buffer(card, &wiinst->buffer); - - spin_lock_irqsave(&card->lock, flags); - switch (wave_dev->wiinst->recsrc) { - case WAVERECORD_AC97: - card->wavein.ac97 = NULL; - break; - case WAVERECORD_MIC: - card->wavein.mic = NULL; - break; - case WAVERECORD_FX: - card->wavein.fx = NULL; - break; - default: - BUG(); - break; - } - spin_unlock_irqrestore(&card->lock, flags); - - wiinst->state = WAVE_STATE_CLOSED; -} - -void emu10k1_wavein_start(struct emu10k1_wavedevice *wave_dev) -{ - struct emu10k1_card *card = wave_dev->card; - struct wiinst *wiinst = wave_dev->wiinst; - - DPF(2, "emu10k1_wavein_start()\n"); - - emu10k1_start_record(card, &wiinst->buffer); - emu10k1_timer_enable(wave_dev->card, &wiinst->timer); - - wiinst->buffer.hw_pos = 0; - wiinst->buffer.pos = 0; - wiinst->buffer.bytestocopy = 0; - - wiinst->state |= WAVE_STATE_STARTED; -} - -void emu10k1_wavein_stop(struct emu10k1_wavedevice *wave_dev) -{ - struct emu10k1_card *card = wave_dev->card; - struct wiinst *wiinst = wave_dev->wiinst; - - DPF(2, "emu10k1_wavein_stop()\n"); - - if (!(wiinst->state & WAVE_STATE_STARTED)) - return; - - emu10k1_timer_disable(card, &wiinst->timer); - emu10k1_stop_record(card, &wiinst->buffer); - - wiinst->state &= ~WAVE_STATE_STARTED; -} - -int emu10k1_wavein_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format) -{ - struct emu10k1_card *card = wave_dev->card; - struct wiinst *wiinst = wave_dev->wiinst; - u32 delay; - - DPF(2, "emu10k1_wavein_setformat()\n"); - - if (wiinst->state & WAVE_STATE_STARTED) - return -1; - - query_format(wiinst->recsrc, format); - - if ((wiinst->format.samplingrate != format->samplingrate) - || (wiinst->format.bitsperchannel != format->bitsperchannel) - || (wiinst->format.channels != format->channels)) { - - wiinst->format = *format; - - if (wiinst->state == WAVE_STATE_CLOSED) - return 0; - - wiinst->buffer.size *= wiinst->buffer.cov; - - if (wiinst->format.bytesperchannel == 1) { - wiinst->buffer.cov = 2; - wiinst->buffer.size /= wiinst->buffer.cov; - } else - wiinst->buffer.cov = 1; - - emu10k1_timer_uninstall(card, &wiinst->timer); - - delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec; - - emu10k1_timer_install(card, &wiinst->timer, delay / 2); - } - - return 0; -} - -void emu10k1_wavein_getxfersize(struct wiinst *wiinst, u32 * size) -{ - struct wavein_buffer *buffer = &wiinst->buffer; - - *size = buffer->bytestocopy; - - if (wiinst->mmapped) - return; - - if (*size > buffer->size) { - *size = buffer->size; - buffer->pos = buffer->hw_pos; - buffer->bytestocopy = buffer->size; - DPF(1, "buffer overrun\n"); - } -} - -static void copy_block(u8 *dst, u8 * src, u32 str, u32 len, u8 cov) -{ - if (cov == 1) - __copy_to_user(dst, src + str, len); - else { - u8 byte; - u32 i; - - src += 1 + 2 * str; - - for (i = 0; i < len; i++) { - byte = src[2 * i] ^ 0x80; - __copy_to_user(dst + i, &byte, 1); - } - } -} - -void emu10k1_wavein_xferdata(struct wiinst *wiinst, u8 * data, u32 * size) -{ - struct wavein_buffer *buffer = &wiinst->buffer; - u32 sizetocopy, sizetocopy_now, start; - unsigned long flags; - - sizetocopy = min_t(u32, buffer->size, *size); - *size = sizetocopy; - - if (!sizetocopy) - return; - - spin_lock_irqsave(&wiinst->lock, flags); - start = buffer->pos; - buffer->pos += sizetocopy; - buffer->pos %= buffer->size; - buffer->bytestocopy -= sizetocopy; - sizetocopy_now = buffer->size - start; - - spin_unlock_irqrestore(&wiinst->lock, flags); - - if (sizetocopy > sizetocopy_now) { - sizetocopy -= sizetocopy_now; - - copy_block(data, buffer->addr, start, sizetocopy_now, buffer->cov); - copy_block(data + sizetocopy_now, buffer->addr, 0, sizetocopy, buffer->cov); - } else { - copy_block(data, buffer->addr, start, sizetocopy, buffer->cov); - } -} - -void emu10k1_wavein_update(struct emu10k1_card *card, struct wiinst *wiinst) -{ - u32 hw_pos; - u32 diff; - - /* There is no actual start yet */ - if (!(wiinst->state & WAVE_STATE_STARTED)) { - hw_pos = wiinst->buffer.hw_pos; - } else { - /* hw_pos in byte units */ - hw_pos = sblive_readptr(card, wiinst->buffer.idxreg, 0) / wiinst->buffer.cov; - } - - diff = (wiinst->buffer.size + hw_pos - wiinst->buffer.hw_pos) % wiinst->buffer.size; - wiinst->total_recorded += diff; - wiinst->buffer.bytestocopy += diff; - - wiinst->buffer.hw_pos = hw_pos; -} diff -Nru a/drivers/sound/emu10k1/cardwi.h b/drivers/sound/emu10k1/cardwi.h --- a/drivers/sound/emu10k1/cardwi.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,90 +0,0 @@ -/* - ********************************************************************** - * cardwi.h -- header file for card wave input functions - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ -#ifndef _CARDWI_H -#define _CARDWI_H - -#include "icardwav.h" -#include "audio.h" -#include "timer.h" - -struct wavein_buffer { - u16 ossfragshift; - u32 fragment_size; - u32 numfrags; - u32 hw_pos; /* hardware cursor position */ - u32 pos; /* software cursor position */ - u32 bytestocopy; /* bytes of recorded data available */ - u32 size; - u32 pages; - u32 sizereg; - u32 sizeregval; - u32 addrreg; - u32 idxreg; - u32 adcctl; - void *addr; - u8 cov; - dma_addr_t dma_handle; -}; - -struct wiinst -{ - u8 state; - struct emu_timer timer; - struct wave_format format; - struct wavein_buffer buffer; - wait_queue_head_t wait_queue; - u8 mmapped; - u32 total_recorded; /* total bytes read() from device */ - u32 blocks; - spinlock_t lock; - u8 recsrc; - u16 fxwc; -}; - -#define WAVEIN_MAXBUFSIZE 65536 -#define WAVEIN_MINBUFSIZE 368 - -#define WAVEIN_DEFAULTFRAGLEN 100 -#define WAVEIN_DEFAULTBUFLEN 1000 - -#define WAVEIN_MINFRAGSHIFT 8 - -int emu10k1_wavein_open(struct emu10k1_wavedevice *); -void emu10k1_wavein_close(struct emu10k1_wavedevice *); -void emu10k1_wavein_start(struct emu10k1_wavedevice *); -void emu10k1_wavein_stop(struct emu10k1_wavedevice *); -void emu10k1_wavein_getxfersize(struct wiinst *, u32 *); -void emu10k1_wavein_xferdata(struct wiinst *, u8 *, u32 *); -int emu10k1_wavein_setformat(struct emu10k1_wavedevice *, struct wave_format *); -void emu10k1_wavein_update(struct emu10k1_card *, struct wiinst *); - - -#endif /* _CARDWI_H */ diff -Nru a/drivers/sound/emu10k1/cardwo.c b/drivers/sound/emu10k1/cardwo.c --- a/drivers/sound/emu10k1/cardwo.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,693 +0,0 @@ -/* - ********************************************************************** - * cardwo.c - PCM output HAL for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include -#include "hwaccess.h" -#include "8010.h" -#include "voicemgr.h" -#include "cardwo.h" -#include "audio.h" - -static u32 samplerate_to_linearpitch(u32 samplingrate) -{ - samplingrate = (samplingrate << 8) / 375; - return (samplingrate >> 1) + (samplingrate & 1); -} - -static void query_format(struct emu10k1_wavedevice *wave_dev, struct wave_format *wave_fmt) -{ - int i, j, do_passthrough = 0, is_ac3 = 0; - struct emu10k1_card *card = wave_dev->card; - struct woinst *woinst = wave_dev->woinst; - - if ((wave_fmt->channels > 2) && (wave_fmt->id != AFMT_S16_LE) && (wave_fmt->id != AFMT_U8)) - wave_fmt->channels = 2; - - if ((wave_fmt->channels < 1) || (wave_fmt->channels > WAVEOUT_MAXVOICES)) - wave_fmt->channels = 2; - - if (wave_fmt->channels == 2) - woinst->num_voices = 1; - else - woinst->num_voices = wave_fmt->channels; - - if (wave_fmt->samplingrate >= 0x2ee00) - wave_fmt->samplingrate = 0x2ee00; - - wave_fmt->passthrough = 0; - do_passthrough = is_ac3 = 0; - - if (card->pt.selected) - do_passthrough = 1; - - switch (wave_fmt->id) { - case AFMT_S16_LE: - wave_fmt->bitsperchannel = 16; - break; - case AFMT_U8: - wave_fmt->bitsperchannel = 8; - break; - case AFMT_AC3: - do_passthrough = 1; - is_ac3 = 1; - break; - default: - wave_fmt->id = AFMT_S16_LE; - wave_fmt->bitsperchannel = 16; - break; - } - if (do_passthrough) { - i = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.intr_gpr_name); - j = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.enable_gpr_name); - /* currently only one waveout instance may use pass-through */ - if (i < 0 || j < 0 || woinst->state != WAVE_STATE_CLOSED || - card->pt.state != PT_STATE_INACTIVE || - (wave_fmt->samplingrate != 48000 && !is_ac3) || - (wave_fmt->samplingrate != 48000 && !is_ac3)) { - DPF(2, "unable to set pass-through mode\n"); - } else { - wave_fmt->samplingrate = 48000; - wave_fmt->channels = 2; - wave_fmt->passthrough = 1; - card->pt.intr_gpr = i; - card->pt.enable_gpr = j; - card->pt.state = PT_STATE_INACTIVE; - card->pt.pos_gpr = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.pos_gpr_name); - DPD(2, "is_ac3 is %d\n", is_ac3); - card->pt.ac3data = is_ac3; - wave_fmt->bitsperchannel = 16; - } - } - - wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3; - - if (wave_fmt->channels == 2) - wave_fmt->bytespervoicesample = wave_fmt->channels * wave_fmt->bytesperchannel; - else - wave_fmt->bytespervoicesample = wave_fmt->bytesperchannel; - - wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel; - wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate; -} - -/** - * alloc_buffer - - * - * allocates the memory buffer for a voice. Two page tables are kept for each buffer. - * One (dma_handle) keeps track of the host memory pages used and the other (virtualpagetable) - * is passed to the device so that it can do DMA to host memory. - * - */ -static int alloc_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer, unsigned int voicenum) -{ - u32 pageindex, pagecount; - unsigned long busaddx; - int i; - - DPD(2, "requested pages is: %d\n", buffer->pages); - - if ((buffer->mem[voicenum].emupageindex = - emu10k1_addxmgr_alloc(buffer->pages * PAGE_SIZE, card)) < 0) - return -1; - - /* Fill in virtual memory table */ - for (pagecount = 0; pagecount < buffer->pages; pagecount++) { - if ((buffer->mem[voicenum].addr[pagecount] = - pci_alloc_consistent(card->pci_dev, PAGE_SIZE, - &buffer->mem[voicenum].dma_handle[pagecount])) == NULL) { - buffer->pages = pagecount; - return -1; - } - - DPD(2, "Virtual Addx: %p\n", buffer->mem[voicenum].addr[pagecount]); - - for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) { - busaddx = buffer->mem[voicenum].dma_handle[pagecount] + i * EMUPAGESIZE; - - DPD(3, "Bus Addx: %#lx\n", busaddx); - - pageindex = buffer->mem[voicenum].emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i; - - ((u32 *) card->virtualpagetable.addr)[pageindex] = cpu_to_le32((busaddx * 2) | pageindex); - } - } - - return 0; -} - -/** - * free_buffer - - * - * frees the memory buffer for a voice. - */ -static void free_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer, unsigned int voicenum) -{ - u32 pagecount, pageindex; - int i; - - if (buffer->mem[voicenum].emupageindex < 0) - return; - - for (pagecount = 0; pagecount < buffer->pages; pagecount++) { - pci_free_consistent(card->pci_dev, PAGE_SIZE, - buffer->mem[voicenum].addr[pagecount], - buffer->mem[voicenum].dma_handle[pagecount]); - - for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) { - pageindex = buffer->mem[voicenum].emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i; - ((u32 *) card->virtualpagetable.addr)[pageindex] = - cpu_to_le32((card->silentpage.dma_handle * 2) | pageindex); - } - } - - emu10k1_addxmgr_free(card, buffer->mem[voicenum].emupageindex); - buffer->mem[voicenum].emupageindex = -1; -} - -static int get_voice(struct emu10k1_card *card, struct woinst *woinst, unsigned int voicenum) -{ - struct emu_voice *voice = &woinst->voice[voicenum]; - /* Allocate voices here, if no voices available, return error. - * Init voice_allocdesc first.*/ - - voice->usage = VOICE_USAGE_PLAYBACK; - - voice->flags = 0; - - if (woinst->format.channels == 2) - voice->flags |= VOICE_FLAGS_STEREO; - - if (woinst->format.bitsperchannel == 16) - voice->flags |= VOICE_FLAGS_16BIT; - - if (emu10k1_voice_alloc(card, voice) < 0) { - voice->usage = VOICE_USAGE_FREE; - return -1; - } - - /* Calculate pitch */ - voice->initial_pitch = (u16) (srToPitch(woinst->format.samplingrate) >> 8); - voice->pitch_target = samplerate_to_linearpitch(woinst->format.samplingrate); - - DPD(2, "Initial pitch --> %#x\n", voice->initial_pitch); - - voice->startloop = (woinst->buffer.mem[voicenum].emupageindex << 12) / - woinst->format.bytespervoicesample; - voice->endloop = voice->startloop + woinst->buffer.size / woinst->format.bytespervoicesample; - voice->start = voice->startloop; - - if (voice->flags & VOICE_FLAGS_STEREO) { - voice->params[0].send_a = card->waveout.send_a[1]; - voice->params[0].send_b = card->waveout.send_b[1]; - voice->params[0].send_c = card->waveout.send_c[1]; - voice->params[0].send_d = card->waveout.send_d[1]; - - if (woinst->device) - voice->params[0].send_routing = 0x7654; - else - voice->params[0].send_routing = card->waveout.send_routing[1]; - - voice->params[0].volume_target = 0xffff; - voice->params[0].initial_fc = 0xff; - voice->params[0].initial_attn = 0x00; - voice->params[0].byampl_env_sustain = 0x7f; - voice->params[0].byampl_env_decay = 0x7f; - - voice->params[1].send_a = card->waveout.send_a[2]; - voice->params[1].send_b = card->waveout.send_b[2]; - voice->params[1].send_c = card->waveout.send_c[2]; - voice->params[1].send_d = card->waveout.send_d[2]; - - if (woinst->device) - voice->params[1].send_routing = 0x7654; - else - voice->params[1].send_routing = card->waveout.send_routing[2]; - - voice->params[1].volume_target = 0xffff; - voice->params[1].initial_fc = 0xff; - voice->params[1].initial_attn = 0x00; - voice->params[1].byampl_env_sustain = 0x7f; - voice->params[1].byampl_env_decay = 0x7f; - } else { - if (woinst->num_voices > 1) { - voice->params[0].send_a = 0xff; - voice->params[0].send_b = 0; - voice->params[0].send_c = 0; - voice->params[0].send_d = 0; - - voice->params[0].send_routing = - 0xfff0 + card->mchannel_fx + voicenum; - } else { - voice->params[0].send_a = card->waveout.send_a[0]; - voice->params[0].send_b = card->waveout.send_b[0]; - voice->params[0].send_c = card->waveout.send_c[0]; - voice->params[0].send_d = card->waveout.send_d[0]; - - if (woinst->device) - voice->params[0].send_routing = 0x7654; - else - voice->params[0].send_routing = card->waveout.send_routing[0]; - } - - voice->params[0].volume_target = 0xffff; - voice->params[0].initial_fc = 0xff; - voice->params[0].initial_attn = 0x00; - voice->params[0].byampl_env_sustain = 0x7f; - voice->params[0].byampl_env_decay = 0x7f; - } - - DPD(2, "voice: startloop=%#x, endloop=%#x\n", voice->startloop, voice->endloop); - - emu10k1_voice_playback_setup(voice); - - return 0; -} - -int emu10k1_waveout_open(struct emu10k1_wavedevice *wave_dev) -{ - struct emu10k1_card *card = wave_dev->card; - struct woinst *woinst = wave_dev->woinst; - struct waveout_buffer *buffer = &woinst->buffer; - unsigned int voicenum; - u32 delay; - - DPF(2, "emu10k1_waveout_open()\n"); - - for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) { - if (alloc_buffer(card, buffer, voicenum) < 0) { - ERROR(); - emu10k1_waveout_close(wave_dev); - return -1; - } - - if (get_voice(card, woinst, voicenum) < 0) { - ERROR(); - emu10k1_waveout_close(wave_dev); - return -1; - } - } - - buffer->fill_silence = 0; - buffer->silence_bytes = 0; - buffer->silence_pos = 0; - buffer->hw_pos = 0; - buffer->free_bytes = woinst->buffer.size; - - delay = (48000 * woinst->buffer.fragment_size) / - (woinst->format.samplingrate * woinst->format.bytespervoicesample); - - emu10k1_timer_install(card, &woinst->timer, delay / 2); - - woinst->state = WAVE_STATE_OPEN; - - return 0; -} - -void emu10k1_waveout_close(struct emu10k1_wavedevice *wave_dev) -{ - struct emu10k1_card *card = wave_dev->card; - struct woinst *woinst = wave_dev->woinst; - unsigned int voicenum; - - DPF(2, "emu10k1_waveout_close()\n"); - - emu10k1_waveout_stop(wave_dev); - - emu10k1_timer_uninstall(card, &woinst->timer); - - for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) { - emu10k1_voice_free(&woinst->voice[voicenum]); - free_buffer(card, &woinst->buffer, voicenum); - } - - woinst->state = WAVE_STATE_CLOSED; -} - -void emu10k1_waveout_start(struct emu10k1_wavedevice *wave_dev) -{ - struct emu10k1_card *card = wave_dev->card; - struct woinst *woinst = wave_dev->woinst; - - DPF(2, "emu10k1_waveout_start()\n"); - - /* Actual start */ - emu10k1_voices_start(woinst->voice, woinst->num_voices, woinst->total_played); - - emu10k1_timer_enable(card, &woinst->timer); - - woinst->state |= WAVE_STATE_STARTED; -} - -int emu10k1_waveout_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format) -{ - struct emu10k1_card *card = wave_dev->card; - struct woinst *woinst = wave_dev->woinst; - unsigned int voicenum; - u32 delay; - - DPF(2, "emu10k1_waveout_setformat()\n"); - - if (woinst->state & WAVE_STATE_STARTED) - return -1; - - query_format(wave_dev, format); - - if (woinst->format.samplingrate != format->samplingrate || - woinst->format.channels != format->channels || - woinst->format.bitsperchannel != format->bitsperchannel) { - - woinst->format = *format; - - if (woinst->state == WAVE_STATE_CLOSED) - return 0; - - emu10k1_timer_uninstall(card, &woinst->timer); - - for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) { - emu10k1_voice_free(&woinst->voice[voicenum]); - - if (get_voice(card, woinst, voicenum) < 0) { - ERROR(); - emu10k1_waveout_close(wave_dev); - return -1; - } - } - - delay = (48000 * woinst->buffer.fragment_size) / - (woinst->format.samplingrate * woinst->format.bytespervoicesample); - - emu10k1_timer_install(card, &woinst->timer, delay / 2); - } - - return 0; -} - -void emu10k1_waveout_stop(struct emu10k1_wavedevice *wave_dev) -{ - struct emu10k1_card *card = wave_dev->card; - struct woinst *woinst = wave_dev->woinst; - - DPF(2, "emu10k1_waveout_stop()\n"); - - if (!(woinst->state & WAVE_STATE_STARTED)) - return; - - emu10k1_timer_disable(card, &woinst->timer); - - /* Stop actual voices */ - emu10k1_voices_stop(woinst->voice, woinst->num_voices); - - emu10k1_waveout_update(woinst); - - woinst->state &= ~WAVE_STATE_STARTED; -} - -/** - * emu10k1_waveout_getxfersize - - * - * gives the total free bytes on the voice buffer, including silence bytes - * (basically: total_free_bytes = free_bytes + silence_bytes). - * - */ -void emu10k1_waveout_getxfersize(struct woinst *woinst, u32 *total_free_bytes) -{ - struct waveout_buffer *buffer = &woinst->buffer; - int pending_bytes; - - if (woinst->mmapped) { - *total_free_bytes = buffer->free_bytes; - return; - } - - pending_bytes = buffer->size - buffer->free_bytes; - - buffer->fill_silence = (pending_bytes < (signed) buffer->fragment_size) ? 1 : 0; - - if (pending_bytes > (signed) buffer->silence_bytes) { - *total_free_bytes = (buffer->free_bytes + buffer->silence_bytes); - } else { - *total_free_bytes = buffer->size; - buffer->silence_bytes = pending_bytes; - if (pending_bytes < 0) { - buffer->silence_pos = buffer->hw_pos; - buffer->silence_bytes = 0; - buffer->free_bytes = buffer->size; - DPF(1, "buffer underrun\n"); - } - } -} - -/** - * copy_block - - * - * copies a block of pcm data to a voice buffer. - * Notice that the voice buffer is actually a set of disjointed memory pages. - * - */ -static void copy_block(void **dst, u32 str, u8 *src, u32 len) -{ - unsigned int pg; - unsigned int pgoff; - unsigned int k; - - pg = str / PAGE_SIZE; - pgoff = str % PAGE_SIZE; - - if (len > PAGE_SIZE - pgoff) { - k = PAGE_SIZE - pgoff; - __copy_from_user((u8 *)dst[pg] + pgoff, src, k); - len -= k; - while (len > PAGE_SIZE) { - __copy_from_user(dst[++pg], src + k, PAGE_SIZE); - k += PAGE_SIZE; - len -= PAGE_SIZE; - } - __copy_from_user(dst[++pg], src + k, len); - - } else - __copy_from_user((u8 *)dst[pg] + pgoff, src, len); -} - -/** - * copy_ilv_block - - * - * copies a block of pcm data containing n interleaved channels to n mono voice buffers. - * Notice that the voice buffer is actually a set of disjointed memory pages. - * - */ -static void copy_ilv_block(struct woinst *woinst, u32 str, u8 *src, u32 len) -{ - unsigned int pg; - unsigned int pgoff; - unsigned int voice_num; - struct waveout_mem *mem = woinst->buffer.mem; - - pg = str / PAGE_SIZE; - pgoff = str % PAGE_SIZE; - - while (len) { - for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) { - __copy_from_user((u8 *)(mem[voice_num].addr[pg]) + pgoff, src, woinst->format.bytespervoicesample); - src += woinst->format.bytespervoicesample; - } - - len -= woinst->format.bytespervoicesample; - - pgoff += woinst->format.bytespervoicesample; - if (pgoff >= PAGE_SIZE) { - pgoff = 0; - pg++; - } - } -} - -/** - * fill_block - - * - * fills a set voice buffers with a block of a given sample. - * - */ -static void fill_block(struct woinst *woinst, u32 str, u8 data, u32 len) -{ - unsigned int pg; - unsigned int pgoff; - unsigned int voice_num; - struct waveout_mem *mem = woinst->buffer.mem; - unsigned int k; - - pg = str / PAGE_SIZE; - pgoff = str % PAGE_SIZE; - - if (len > PAGE_SIZE - pgoff) { - k = PAGE_SIZE - pgoff; - for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) - memset((u8 *)mem[voice_num].addr[pg] + pgoff, data, k); - len -= k; - while (len > PAGE_SIZE) { - pg++; - for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) - memset(mem[voice_num].addr[pg], data, PAGE_SIZE); - - len -= PAGE_SIZE; - } - pg++; - for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) - memset(mem[voice_num].addr[pg], data, len); - - } else { - for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) - memset((u8 *)mem[voice_num].addr[pg] + pgoff, data, len); - } -} - -/** - * emu10k1_waveout_xferdata - - * - * copies pcm data to the voice buffer. Silence samples - * previously added to the buffer are overwritten. - * - */ -void emu10k1_waveout_xferdata(struct woinst *woinst, u8 *data, u32 *size) -{ - struct waveout_buffer *buffer = &woinst->buffer; - u32 sizetocopy, sizetocopy_now, start; - unsigned long flags; - - sizetocopy = min_t(u32, buffer->size, *size); - *size = sizetocopy; - - if (!sizetocopy) - return; - - spin_lock_irqsave(&woinst->lock, flags); - start = (buffer->size + buffer->silence_pos - buffer->silence_bytes) % buffer->size; - - if (sizetocopy > buffer->silence_bytes) { - buffer->silence_pos += sizetocopy - buffer->silence_bytes; - buffer->free_bytes -= sizetocopy - buffer->silence_bytes; - buffer->silence_bytes = 0; - } else - buffer->silence_bytes -= sizetocopy; - - spin_unlock_irqrestore(&woinst->lock, flags); - - sizetocopy_now = buffer->size - start; - if (sizetocopy > sizetocopy_now) { - sizetocopy -= sizetocopy_now; - if (woinst->num_voices > 1) { - copy_ilv_block(woinst, start, data, sizetocopy_now); - copy_ilv_block(woinst, 0, data + sizetocopy_now * woinst->num_voices, sizetocopy); - } else { - copy_block(buffer->mem[0].addr, start, data, sizetocopy_now); - copy_block(buffer->mem[0].addr, 0, data + sizetocopy_now, sizetocopy); - } - } else { - if (woinst->num_voices > 1) - copy_ilv_block(woinst, start, data, sizetocopy); - else - copy_block(buffer->mem[0].addr, start, data, sizetocopy); - } -} - -/** - * emu10k1_waveout_fillsilence - - * - * adds samples of silence to the voice buffer so that we - * don't loop over stale pcm data. - * - */ -void emu10k1_waveout_fillsilence(struct woinst *woinst) -{ - struct waveout_buffer *buffer = &woinst->buffer; - u32 sizetocopy, sizetocopy_now, start; - u8 filldata; - unsigned long flags; - - sizetocopy = buffer->fragment_size; - - if (woinst->format.bitsperchannel == 16) - filldata = 0x00; - else - filldata = 0x80; - - spin_lock_irqsave(&woinst->lock, flags); - buffer->silence_bytes += sizetocopy; - buffer->free_bytes -= sizetocopy; - buffer->silence_pos %= buffer->size; - start = buffer->silence_pos; - buffer->silence_pos += sizetocopy; - spin_unlock_irqrestore(&woinst->lock, flags); - - sizetocopy_now = buffer->size - start; - - if (sizetocopy > sizetocopy_now) { - sizetocopy -= sizetocopy_now; - fill_block(woinst, start, filldata, sizetocopy_now); - fill_block(woinst, 0, filldata, sizetocopy); - } else { - fill_block(woinst, start, filldata, sizetocopy); - } -} - -/** - * emu10k1_waveout_update - - * - * updates the position of the voice buffer hardware pointer (hw_pos) - * and the number of free bytes on the buffer (free_bytes). - * The free bytes _don't_ include silence bytes that may have been - * added to the buffer. - * - */ -void emu10k1_waveout_update(struct woinst *woinst) -{ - u32 hw_pos; - u32 diff; - - /* There is no actual start yet */ - if (!(woinst->state & WAVE_STATE_STARTED)) { - hw_pos = woinst->buffer.hw_pos; - } else { - /* hw_pos in sample units */ - hw_pos = sblive_readptr(woinst->voice[0].card, CCCA_CURRADDR, woinst->voice[0].num); - - if(hw_pos < woinst->voice[0].start) - hw_pos += woinst->buffer.size / woinst->format.bytespervoicesample - woinst->voice[0].start; - else - hw_pos -= woinst->voice[0].start; - - hw_pos *= woinst->format.bytespervoicesample; - } - - diff = (woinst->buffer.size + hw_pos - woinst->buffer.hw_pos) % woinst->buffer.size; - woinst->total_played += diff; - woinst->buffer.free_bytes += diff; - woinst->buffer.hw_pos = hw_pos; -} diff -Nru a/drivers/sound/emu10k1/cardwo.h b/drivers/sound/emu10k1/cardwo.h --- a/drivers/sound/emu10k1/cardwo.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,98 +0,0 @@ -/* - ********************************************************************** - * cardwo.h -- header file for card wave out functions - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _CARDWO_H -#define _CARDWO_H - -#include "icardwav.h" -#include "audio.h" -#include "voicemgr.h" -#include "timer.h" - -/* setting this to other than a power of two may break some applications */ -#define WAVEOUT_MAXBUFSIZE MAXBUFSIZE -#define WAVEOUT_MINBUFSIZE 64 - -#define WAVEOUT_DEFAULTFRAGLEN 20 /* Time to play a fragment in ms (latency) */ -#define WAVEOUT_DEFAULTBUFLEN 500 /* Time to play the entire buffer in ms */ - -#define WAVEOUT_MINFRAGSHIFT 6 -#define WAVEOUT_MAXVOICES 6 - -/* waveout_mem is cardwo internal */ -struct waveout_mem { - int emupageindex; - void *addr[BUFMAXPAGES]; - dma_addr_t dma_handle[BUFMAXPAGES]; -}; - -struct waveout_buffer { - u16 ossfragshift; - u32 numfrags; - u32 fragment_size; /* in bytes units */ - u32 size; /* in bytes units */ - u32 pages; /* buffer size in page units*/ - struct waveout_mem mem[WAVEOUT_MAXVOICES]; - u32 silence_pos; /* software cursor position (including silence bytes) */ - u32 hw_pos; /* hardware cursor position */ - u32 free_bytes; /* free bytes available on the buffer (not including silence bytes) */ - u8 fill_silence; - u32 silence_bytes; /* silence bytes on the buffer */ -}; - -struct woinst -{ - u8 state; - u8 num_voices; - struct emu_voice voice[WAVEOUT_MAXVOICES]; - struct emu_timer timer; - struct wave_format format; - struct waveout_buffer buffer; - wait_queue_head_t wait_queue; - u8 mmapped; - u32 total_copied; /* total number of bytes written() to the buffer (excluding silence) */ - u32 total_played; /* total number of bytes played including silence */ - u32 blocks; - u8 device; - spinlock_t lock; -}; - -int emu10k1_waveout_open(struct emu10k1_wavedevice *); -void emu10k1_waveout_close(struct emu10k1_wavedevice *); -void emu10k1_waveout_start(struct emu10k1_wavedevice *); -void emu10k1_waveout_stop(struct emu10k1_wavedevice *); -void emu10k1_waveout_getxfersize(struct woinst*, u32 *); -void emu10k1_waveout_xferdata(struct woinst*, u8*, u32 *); -void emu10k1_waveout_fillsilence(struct woinst*); -int emu10k1_waveout_setformat(struct emu10k1_wavedevice*, struct wave_format*); -void emu10k1_waveout_update(struct woinst*); - -#endif /* _CARDWO_H */ diff -Nru a/drivers/sound/emu10k1/ecard.c b/drivers/sound/emu10k1/ecard.c --- a/drivers/sound/emu10k1/ecard.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,157 +0,0 @@ -/* - ********************************************************************** - * ecard.c - E-card initialization code - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include "ecard.h" -#include "hwaccess.h" - -/* Private routines */ -static void ecard_setadcgain(struct emu10k1_card *, struct ecard_state *, u16); -static void ecard_write(struct emu10k1_card *, u32); - -/************************************************************************** - * @func Set the gain of the ECARD's CS3310 Trim/gain controller. The - * trim value consists of a 16bit value which is composed of two - * 8 bit gain/trim values, one for the left channel and one for the - * right channel. The following table maps from the Gain/Attenuation - * value in decibels into the corresponding bit pattern for a single - * channel. - */ - -static void ecard_setadcgain(struct emu10k1_card *card, struct ecard_state *ecard, u16 gain) -{ - u32 currbit; - ecard->adc_gain = gain; - - /* Enable writing to the TRIM registers */ - ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN); - - /* Do it again to insure that we meet hold time requirements */ - ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN); - - for (currbit = (1L << 15); currbit; currbit >>= 1) { - - u32 value = ecard->control_bits & ~(EC_TRIM_CSN|EC_TRIM_SDATA); - - if (gain & currbit) - value |= EC_TRIM_SDATA; - - /* Clock the bit */ - ecard_write(card, value); - ecard_write(card, value | EC_TRIM_SCLK); - ecard_write(card, value); - } - - ecard_write(card, ecard->control_bits); -} - -/************************************************************************** - * @func Clock bits into the Ecard's control latch. The Ecard uses a - * control latch will is loaded bit-serially by toggling the Modem control - * lines from function 2 on the E8010. This function hides these details - * and presents the illusion that we are actually writing to a distinct - * register. - */ -static void ecard_write(struct emu10k1_card *card, u32 value) -{ - u16 count; - u32 data, hcvalue; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - - hcvalue = inl(card->iobase + HCFG) & ~(HOOKN_BIT|HANDN_BIT|PULSEN_BIT); - - outl(card->iobase + HCFG, hcvalue); - - for (count = 0 ; count < EC_NUM_CONTROL_BITS; count++) { - - /* Set up the value */ - data = ((value & 0x1) ? PULSEN_BIT : 0); - value >>= 1; - - outl(card->iobase + HCFG, hcvalue | data); - - /* Clock the shift register */ - outl(card->iobase + HCFG, hcvalue | data | HANDN_BIT); - outl(card->iobase + HCFG, hcvalue | data); - } - - /* Latch the bits */ - outl(card->iobase + HCFG, hcvalue | HOOKN_BIT); - outl(card->iobase + HCFG, hcvalue); - - spin_unlock_irqrestore(&card->lock, flags); -} - -void __devinit emu10k1_ecard_init(struct emu10k1_card *card) -{ - u32 hcvalue; - struct ecard_state ecard; - - /* Set up the initial settings */ - ecard.mux0_setting = EC_DEFAULT_SPDIF0_SEL; - ecard.mux1_setting = EC_DEFAULT_SPDIF1_SEL; - ecard.mux2_setting = 0; - ecard.adc_gain = EC_DEFAULT_ADC_GAIN; - ecard.control_bits = EC_RAW_RUN_MODE | - EC_SPDIF0_SELECT(ecard.mux0_setting) | - EC_SPDIF1_SELECT(ecard.mux1_setting); - - - /* Step 0: Set the codec type in the hardware control register - * and enable audio output */ - hcvalue = emu10k1_readfn0(card, HCFG); - emu10k1_writefn0(card, HCFG, hcvalue | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S); - - /* Step 1: Turn off the led and deassert TRIM_CS */ - ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); - - /* Step 2: Calibrate the ADC and DAC */ - ecard_write(card, EC_DACCAL | EC_LEDN | EC_TRIM_CSN); - - /* Step 3: Wait for awhile; FIXME: Is this correct? */ - - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ); - - /* Step 4: Switch off the DAC and ADC calibration. Note - * That ADC_CAL is actually an inverted signal, so we assert - * it here to stop calibration. */ - ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); - - /* Step 4: Switch into run mode */ - ecard_write(card, ecard.control_bits); - - /* Step 5: Set the analog input gain */ - ecard_setadcgain(card, &ecard, ecard.adc_gain); -} - - diff -Nru a/drivers/sound/emu10k1/ecard.h b/drivers/sound/emu10k1/ecard.h --- a/drivers/sound/emu10k1/ecard.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,113 +0,0 @@ -/* - ********************************************************************** - * ecard.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _ECARD_H -#define _ECARD_H - -#include "8010.h" -#include "hwaccess.h" -#include - -/* In A1 Silicon, these bits are in the HC register */ -#define HOOKN_BIT (1L << 12) -#define HANDN_BIT (1L << 11) -#define PULSEN_BIT (1L << 10) - -#define EC_GDI1 (1 << 13) -#define EC_GDI0 (1 << 14) - -#define EC_NUM_CONTROL_BITS 20 - -#define EC_AC3_DATA_SELN 0x0001L -#define EC_EE_DATA_SEL 0x0002L -#define EC_EE_CNTRL_SELN 0x0004L -#define EC_EECLK 0x0008L -#define EC_EECS 0x0010L -#define EC_EESDO 0x0020L -#define EC_TRIM_CSN 0x0040L -#define EC_TRIM_SCLK 0x0080L -#define EC_TRIM_SDATA 0x0100L -#define EC_TRIM_MUTEN 0x0200L -#define EC_ADCCAL 0x0400L -#define EC_ADCRSTN 0x0800L -#define EC_DACCAL 0x1000L -#define EC_DACMUTEN 0x2000L -#define EC_LEDN 0x4000L - -#define EC_SPDIF0_SEL_SHIFT 15 -#define EC_SPDIF1_SEL_SHIFT 17 -#define EC_SPDIF0_SEL_MASK (0x3L << EC_SPDIF0_SEL_SHIFT) -#define EC_SPDIF1_SEL_MASK (0x7L << EC_SPDIF1_SEL_SHIFT) -#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK) -#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK) -#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory. This should - * be incremented any time the EEPROM's - * format is changed. */ - -#define EC_EEPROM_SIZE 0x40 /* ECARD EEPROM has 64 16-bit words */ - -/* Addresses for special values stored in to EEPROM */ -#define EC_PROM_VERSION_ADDR 0x20 /* Address of the current prom version */ -#define EC_BOARDREV0_ADDR 0x21 /* LSW of board rev */ -#define EC_BOARDREV1_ADDR 0x22 /* MSW of board rev */ - -#define EC_LAST_PROMFILE_ADDR 0x2f - -#define EC_SERIALNUM_ADD 0x30 /* First word of serial number. The number - * can be up to 30 characters in length - * and is stored as a NULL-terminated - * ASCII string. Any unused bytes must be - * filled with zeros */ -#define EC_CHECKSUM_ADDR 0x3f /* Location at which checksum is stored */ - - - -/* Most of this stuff is pretty self-evident. According to the hardware - * dudes, we need to leave the ADCCAL bit low in order to avoid a DC - * offset problem. Weird. - */ -#define EC_RAW_RUN_MODE (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | EC_TRIM_CSN) - - -#define EC_DEFAULT_ADC_GAIN 0xC4C4 -#define EC_DEFAULT_SPDIF0_SEL 0x0 -#define EC_DEFAULT_SPDIF1_SEL 0x4 - -#define HC_EA 0x01L - -/* ECARD state structure. This structure maintains the state - * for various portions of the ECARD's onboard hardware. - */ -struct ecard_state { - u32 control_bits; - u16 adc_gain; - u16 mux0_setting; - u16 mux1_setting; - u16 mux2_setting; -}; - -void emu10k1_ecard_init(struct emu10k1_card *) __devinit; - -#endif /* _ECARD_H */ diff -Nru a/drivers/sound/emu10k1/efxmgr.c b/drivers/sound/emu10k1/efxmgr.c --- a/drivers/sound/emu10k1/efxmgr.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,218 +0,0 @@ -/* - ********************************************************************** - * efxmgr.c - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include -#include "hwaccess.h" -#include "efxmgr.h" - -int emu10k1_find_control_gpr(struct patch_manager *mgr, const char *patch_name, const char *gpr_name) -{ - struct dsp_patch *patch; - struct dsp_rpatch *rpatch; - char s[PATCH_NAME_SIZE + 4]; - u32 *gpr_used; - int i; - - DPD(2, "emu10k1_find_control_gpr(): %s %s\n", patch_name, gpr_name); - - rpatch = &mgr->rpatch; - if (!strcmp(rpatch->name, patch_name)) { - gpr_used = rpatch->gpr_used; - goto match; - } - - for (i = 0; i < mgr->current_pages * PATCHES_PER_PAGE; i++) { - patch = PATCH(mgr, i); - sprintf(s,"%s", patch->name); - - if (!strcmp(s, patch_name)) { - gpr_used = patch->gpr_used; - goto match; - } - } - - return -1; - - match: - for (i = 0; i < NUM_GPRS; i++) - if (mgr->gpr[i].type == GPR_TYPE_CONTROL && - test_bit(i, gpr_used) && - !strcmp(mgr->gpr[i].name, gpr_name)) - return i; - - return -1; -} - -void emu10k1_set_control_gpr(struct emu10k1_card *card, int addr, s32 val, int flag) -{ - struct patch_manager *mgr = &card->mgr; - - DPD(2, "emu10k1_set_control_gpr(): %d %x\n", addr, val); - - if (addr < 0 || addr >= NUM_GPRS) - return; - - if (flag) - val += sblive_readptr(card, GPR_BASE + addr, 0); - - if (val > mgr->gpr[addr].max) - val = mgr->gpr[addr].max; - else if (val < mgr->gpr[addr].min) - val = mgr->gpr[addr].min; - - sblive_writeptr(card, GPR_BASE + addr, 0, val); -} - -//TODO: make this configurable: -#define VOLCTRL_CHANNEL SOUND_MIXER_VOLUME -#define VOLCTRL_STEP_SIZE 5 - -//An internal function for setting OSS mixer controls. -void emu10k1_set_oss_vol(struct emu10k1_card *card, int oss_mixer, - unsigned int left, unsigned int right) -{ - extern char volume_params[SOUND_MIXER_NRDEVICES]; - - card->ac97.mixer_state[oss_mixer] = (right << 8) | left; - - if (!card->isaps) - 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]); - - emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, - volume_params[oss_mixer]); -} - -//FIXME: mute should unmute when pressed a second time -void emu10k1_mute_irqhandler(struct emu10k1_card *card) -{ - int oss_channel = VOLCTRL_CHANNEL; - int left, right; - static int val = 0; - - if (val) { - left = val & 0xff; - right = (val >> 8) & 0xff; - val = 0; - } else { - val = card->ac97.mixer_state[oss_channel]; - left = 0; - right = 0; - } - - emu10k1_set_oss_vol(card, oss_channel, left, right); -} - -void emu10k1_volincr_irqhandler(struct emu10k1_card *card) -{ - 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; - - if ((left += VOLCTRL_STEP_SIZE) > 100) - left = 100; - - if ((right += VOLCTRL_STEP_SIZE) > 100) - right = 100; - - emu10k1_set_oss_vol(card, oss_channel, left, right); -} - -void emu10k1_voldecr_irqhandler(struct emu10k1_card *card) -{ - 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; - - if ((left -= VOLCTRL_STEP_SIZE) < 0) - left = 0; - - if ((right -= VOLCTRL_STEP_SIZE) < 0) - right = 0; - - emu10k1_set_oss_vol(card, oss_channel, left, right); -} - -void emu10k1_set_volume_gpr(struct emu10k1_card *card, int addr, s32 vol, int scale) -{ - struct patch_manager *mgr = &card->mgr; - unsigned long flags; - int muting; - - const s32 log2lin[5] ={ // attenuation (dB) - 0x7fffffff, // 0.0 - 0x7fffffff * 0.840896415253715 , // 1.5 - 0x7fffffff * 0.707106781186548, // 3.0 - 0x7fffffff * 0.594603557501361 , // 4.5 - }; - - if (addr < 0) - return; - - muting = (scale == 0x10) ? 0x7f: scale; - - vol = (100 - vol ) * scale / 100; - - // Thanks to the comp.dsp newsgroup for this neat trick: - vol = (vol >= muting) ? 0 : (log2lin[vol & 3] >> (vol >> 2)); - - spin_lock_irqsave(&mgr->lock, flags); - emu10k1_set_control_gpr(card, addr, vol, 0); - spin_unlock_irqrestore(&mgr->lock, flags); -} - -void emu10k1_dsp_irqhandler(struct emu10k1_card *card) -{ - unsigned long flags; - - if (card->pt.state != PT_STATE_INACTIVE) { - u32 bc; - bc = sblive_readptr(card, GPR_BASE + card->pt.intr_gpr, 0); - if (bc != 0) { - DPD(3, "pt interrupt, bc = %d\n", bc); - spin_lock_irqsave(&card->pt.lock, flags); - card->pt.blocks_played = bc; - if (card->pt.blocks_played >= card->pt.blocks_copied) { - DPF(1, "buffer underrun in passthrough playback\n"); - emu10k1_pt_stop(card); - } - wake_up_interruptible(&card->pt.wait); - spin_unlock_irqrestore(&card->pt.lock, flags); - } - } -} - diff -Nru a/drivers/sound/emu10k1/efxmgr.h b/drivers/sound/emu10k1/efxmgr.h --- a/drivers/sound/emu10k1/efxmgr.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,254 +0,0 @@ -/* - ********************************************************************** - * sblive_fx.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _EFXMGR_H -#define _EFXMGR_H - -#define WRITE_EFX(a, b, c) sblive_writeptr((a), MICROCODEBASE + (b), 0, (c)) - -#define OP(op, z, w, x, y) \ - do { WRITE_EFX(card, (pc) * 2, ((x) << 10) | (y)); \ - WRITE_EFX(card, (pc) * 2 + 1, ((op) << 20) | ((z) << 10) | (w)); \ - ++pc; } while (0) - -#define NUM_INPUTS 0x20 -#define NUM_OUTPUTS 0x20 -#define NUM_GPRS 0x100 -#define GPR_NAME_SIZE 32 -#define PATCH_NAME_SIZE 32 - -struct dsp_rpatch { - char name[PATCH_NAME_SIZE]; - u16 code_start; - u16 code_size; - - u32 gpr_used[NUM_GPRS / 32]; - u32 gpr_input[NUM_GPRS / 32]; - u32 route[NUM_OUTPUTS]; - u32 route_v[NUM_OUTPUTS]; -}; - -struct dsp_patch { - char name[PATCH_NAME_SIZE]; - u8 id; - u32 input; /* bitmap of the lines used as inputs */ - u32 output; /* bitmap of the lines used as outputs */ - u16 code_start; - u16 code_size; - - u32 gpr_used[NUM_GPRS / 32]; /* bitmap of used gprs */ - u32 gpr_input[NUM_GPRS / 32]; - u8 traml_istart; /* starting address of the internal tram lines used */ - u8 traml_isize; /* number of internal tram lines used */ - - u8 traml_estart; - u8 traml_esize; - - u16 tramb_istart; /* starting address of the internal tram memory used */ - u16 tramb_isize; /* amount of internal memory used */ - u32 tramb_estart; - u32 tramb_esize; -}; - -struct dsp_gpr { - u8 type; /* gpr type, STATIC, DYNAMIC, INPUT, OUTPUT, CONTROL */ - char name[GPR_NAME_SIZE]; /* gpr value, only valid for control gprs */ - s32 min, max; /* value range for this gpr, only valid for control gprs */ - u8 line; /* which input/output line is the gpr attached, only valid for input/output gprs */ - u8 usage; -}; - -enum { - GPR_TYPE_NULL = 0, - GPR_TYPE_IO, - GPR_TYPE_STATIC, - GPR_TYPE_DYNAMIC, - GPR_TYPE_CONTROL, - GPR_TYPE_CONSTANT -}; - -#define GPR_BASE 0x100 -#define OUTPUT_BASE 0x20 - -#define MAX_PATCHES_PAGES 32 - -struct patch_manager { - void *patch[MAX_PATCHES_PAGES]; - int current_pages; - struct dsp_rpatch rpatch; - struct dsp_gpr gpr[NUM_GPRS]; /* gpr usage table */ - spinlock_t lock; - s16 ctrl_gpr[SOUND_MIXER_NRDEVICES][2]; -}; - - -#define PATCHES_PER_PAGE (PAGE_SIZE / sizeof(struct dsp_patch)) - -#define PATCH(mgr, i) ((struct dsp_patch *) (mgr)->patch[(i) / PATCHES_PER_PAGE] + (i) % PATCHES_PER_PAGE) - -/* PCM volume control */ -#define TMP_PCM_L 0x100 //temp PCM L (after the vol control) -#define TMP_PCM_R 0x101 -#define VOL_PCM_L 0x102 //vol PCM -#define VOL_PCM_R 0x103 - -/* Routing patch */ -#define TMP_AC_L 0x104 //tmp ac97 out -#define TMP_AC_R 0x105 -#define TMP_REAR_L 0x106 //output - Temp Rear -#define TMP_REAR_R 0x107 -#define TMP_DIGI_L 0x108 //output - Temp digital -#define TMP_DIGI_R 0x109 -#define DSP_VOL_L 0x10a // main dsp volume -#define DSP_VOL_R 0x10b - -/* hw inputs */ -#define PCM_IN_L 0x00 -#define PCM_IN_R 0x01 - -#define PCM1_IN_L 0x04 -#define PCM1_IN_R 0x05 -//mutilchannel playback stream appear here: - -#define MULTI_FRONT_L 0x08 -#define MULTI_FRONT_R 0x09 -#define MULTI_REAR_L 0x0a -#define MULTI_REAR_R 0x0b -#define MULTI_CENTER 0x0c -#define MULTI_LFE 0x0d - -#define AC97_IN_L 0x10 -#define AC97_IN_R 0x11 -#define SPDIF_CD_L 0x12 -#define SPDIF_CD_R 0x13 - -/* hw outputs */ -#define AC97_FRONT_L 0x20 -#define AC97_FRONT_R 0x21 -#define DIGITAL_OUT_L 0x22 -#define DIGITAL_OUT_R 0x23 -#define DIGITAL_CENTER 0x24 -#define DIGITAL_LFE 0x25 - -#define ANALOG_REAR_L 0x28 -#define ANALOG_REAR_R 0x29 -#define ADC_REC_L 0x2a -#define ADC_REC_R 0x2b - -#define ANALOG_CENTER 0x31 -#define ANALOG_LFE 0x32 - - -#define INPUT_PATCH_START(patch, nm, ln, i) \ -do { \ - patch = PATCH(mgr, patch_n); \ - strcpy(patch->name, nm); \ - patch->code_start = pc * 2; \ - patch->input = (1<<(0x1f&ln)); \ - patch->output= (1<<(0x1f&ln)); \ - patch->id = i; \ -} while(0) - -#define INPUT_PATCH_END(patch) \ -do { \ - patch->code_size = pc * 2 - patch->code_start; \ - patch_n++; \ -} while(0) - - -#define ROUTING_PATCH_START(patch, nm) \ -do { \ - patch = &mgr->rpatch; \ - strcpy(patch->name, nm); \ - patch->code_start = pc * 2; \ -} while(0) - -#define ROUTING_PATCH_END(patch) \ -do { \ - patch->code_size = pc * 2 - patch->code_start; \ -} while(0) - -#define CONNECT(input, output) set_bit(input, &rpatch->route[(output) - OUTPUT_BASE]); - -#define CONNECT_V(input, output) set_bit(input, &rpatch->route_v[(output) - OUTPUT_BASE]); - -#define OUTPUT_PATCH_START(patch, nm, ln, i) \ -do { \ - patch = PATCH(mgr, patch_n); \ - strcpy(patch->name, nm); \ - patch->code_start = pc * 2; \ - patch->input = (1<<(0x1f&ln)); \ - patch->output= (1<<(0x1f&ln)); \ - patch->id = i; \ -} while(0) - -#define OUTPUT_PATCH_END(patch) \ -do { \ - patch->code_size = pc * 2 - patch->code_start; \ - patch_n++; \ -} while(0) - -#define GET_OUTPUT_GPR(patch, g, ln) \ -do { \ - mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_IO; \ - mgr->gpr[(g) - GPR_BASE].usage++; \ - mgr->gpr[(g) - GPR_BASE].line = ln; \ - set_bit((g) - GPR_BASE, patch->gpr_used); \ -} while(0) - -#define GET_INPUT_GPR(patch, g, ln) \ -do { \ - mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_IO; \ - mgr->gpr[(g) - GPR_BASE].usage++; \ - mgr->gpr[(g) - GPR_BASE].line = ln; \ - set_bit((g) - GPR_BASE, patch->gpr_used); \ - set_bit((g) - GPR_BASE, patch->gpr_input); \ -} while(0) - -#define GET_DYNAMIC_GPR(patch, g) \ -do { \ - mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_DYNAMIC; \ - mgr->gpr[(g) - GPR_BASE].usage++; \ - set_bit((g) - GPR_BASE, patch->gpr_used); \ -} while(0) - -#define GET_CONTROL_GPR(patch, g, nm, a, b) \ -do { \ - strcpy(mgr->gpr[(g) - GPR_BASE].name, nm); \ - mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_CONTROL; \ - mgr->gpr[(g) - GPR_BASE].usage++; \ - mgr->gpr[(g) - GPR_BASE].min = a; \ - mgr->gpr[(g) - GPR_BASE].max = b; \ - sblive_writeptr(card, g, 0, b); \ - set_bit((g) - GPR_BASE, patch->gpr_used); \ -} while(0) - -#endif /* _EFXMGR_H */ diff -Nru a/drivers/sound/emu10k1/emuadxmg.c b/drivers/sound/emu10k1/emuadxmg.c --- a/drivers/sound/emu10k1/emuadxmg.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,104 +0,0 @@ - -/* - ********************************************************************** - * emuadxmg.c - Address space manager for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include "hwaccess.h" - -/* Allocates emu address space */ - -int emu10k1_addxmgr_alloc(u32 size, struct emu10k1_card *card) -{ - u16 *pagetable = card->emupagetable; - u16 index = 0; - u16 numpages; - unsigned long flags; - - /* Convert bytes to pages */ - numpages = (size / EMUPAGESIZE) + ((size % EMUPAGESIZE) ? 1 : 0); - - spin_lock_irqsave(&card->lock, flags); - - while (index < (MAXPAGES - 1)) { - if (pagetable[index] & 0x8000) { - /* This block of pages is in use, jump to the start of the next block. */ - index += (pagetable[index] & 0x7fff); - } else { - /* Found free block */ - if (pagetable[index] >= numpages) { - - /* Block is large enough */ - - /* If free block is larger than the block requested - * then adjust the size of the block remaining */ - if (pagetable[index] > numpages) - pagetable[index + numpages] = pagetable[index] - numpages; - - pagetable[index] = (numpages | 0x8000); /* Mark block as used */ - - spin_unlock_irqrestore(&card->lock, flags); - - return index; - } else { - /* Block too small, jump to the start of the next block */ - index += pagetable[index]; - } - } - } - - spin_unlock_irqrestore(&card->lock, flags); - - return -1; -} - -/* Frees a previously allocated emu address space. */ - -void emu10k1_addxmgr_free(struct emu10k1_card *card, int index) -{ - u16 *pagetable = card->emupagetable; - u16 origsize = 0; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - - if (pagetable[index] & 0x8000) { - /* Block is allocated - mark block as free */ - origsize = pagetable[index] & 0x7fff; - pagetable[index] = origsize; - - /* If next block is free, we concat both blocks */ - if (!(pagetable[index + origsize] & 0x8000)) - pagetable[index] += pagetable[index + origsize] & 0x7fff; - } - - spin_unlock_irqrestore(&card->lock, flags); - - return; -} diff -Nru a/drivers/sound/emu10k1/hwaccess.c b/drivers/sound/emu10k1/hwaccess.c --- a/drivers/sound/emu10k1/hwaccess.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,480 +0,0 @@ -/* - ********************************************************************** - * hwaccess.c -- Hardware access layer - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * December 9, 1999 Jon Taylor rewrote the I/O subsystem - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include - -#include "hwaccess.h" -#include "8010.h" -#include "icardmid.h" - -/************************************************************************* -* Function : srToPitch * -* Input : sampleRate - sampling rate * -* Return : pitch value * -* About : convert sampling rate to pitch * -* Note : for 8010, sampling rate is at 48kHz, this function should * -* be changed. * -*************************************************************************/ -u32 srToPitch(u32 sampleRate) -{ - int i; - - /* FIXME: These tables should be defined in a headerfile */ - static u32 logMagTable[128] = { - 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, - 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, - 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, - 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, - 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, - 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, - 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, - 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, - 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, - 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, - 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, - 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, - 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, - 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, - 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, - 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df - }; - - static char logSlopeTable[128] = { - 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, - 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, - 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, - 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, - 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, - 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, - 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, - 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, - 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, - 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, - 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, - 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f - }; - - if (sampleRate == 0) - return 0; /* Bail out if no leading "1" */ - - sampleRate *= 11185; /* Scale 48000 to 0x20002380 */ - - for (i = 31; i > 0; i--) { - if (sampleRate & 0x80000000) { /* Detect leading "1" */ - return (u32) (((s32) (i - 15) << 20) + - logMagTable[0x7f & (sampleRate >> 24)] + - (0x7f & (sampleRate >> 17)) * logSlopeTable[0x7f & (sampleRate >> 24)]); - } - sampleRate = sampleRate << 1; - } - - DPF(2, "srToPitch: BUG!\n"); - return 0; /* Should never reach this point */ -} - -/* Returns an attenuation based upon a cumulative volume value */ - -/* Algorithm calculates 0x200 - 0x10 log2 (input) */ -u8 sumVolumeToAttenuation(u32 value) -{ - u16 count = 16; - s16 ans; - - if (value == 0) - return 0xFF; - - /* Find first SET bit. This is the integer part of the value */ - while ((value & 0x10000) == 0) { - value <<= 1; - count--; - } - - /* The REST of the data is the fractional part. */ - ans = (s16) (0x110 - ((count << 4) + ((value & 0x0FFFFL) >> 12))); - if (ans > 0xFF) - ans = 0xFF; - - return (u8) ans; -} - -/******************************************* -* write/read PCI function 0 registers * -********************************************/ -void emu10k1_writefn0(struct emu10k1_card *card, u32 reg, u32 data) -{ - unsigned long flags; - - if (reg & 0xff000000) { - u32 mask; - u8 size, offset; - - size = (reg >> 24) & 0x3f; - offset = (reg >> 16) & 0x1f; - mask = ((1 << size) - 1) << offset; - data = (data << offset) & mask; - reg &= 0x7f; - - spin_lock_irqsave(&card->lock, flags); - data |= inl(card->iobase + reg) & ~mask; - outl(data, card->iobase + reg); - spin_unlock_irqrestore(&card->lock, flags); - } else { - spin_lock_irqsave(&card->lock, flags); - outl(data, card->iobase + reg); - spin_unlock_irqrestore(&card->lock, flags); - } - - return; -} - -u32 emu10k1_readfn0(struct emu10k1_card * card, u32 reg) -{ - u32 val; - unsigned long flags; - - if (reg & 0xff000000) { - u32 mask; - u8 size, offset; - - size = (reg >> 24) & 0x3f; - offset = (reg >> 16) & 0x1f; - mask = ((1 << size) - 1) << offset; - reg &= 0x7f; - - spin_lock_irqsave(&card->lock, flags); - val = inl(card->iobase + reg); - spin_unlock_irqrestore(&card->lock, flags); - - return (val & mask) >> offset; - } else { - spin_lock_irqsave(&card->lock, flags); - val = inl(card->iobase + reg); - spin_unlock_irqrestore(&card->lock, flags); - return val; - } -} - -/************************************************************************ -* write/read Emu10k1 pointer-offset register set, accessed through * -* the PTR and DATA registers * -*************************************************************************/ -void sblive_writeptr(struct emu10k1_card *card, u32 reg, u32 channel, u32 data) -{ - u32 regptr; - unsigned long flags; - - regptr = ((reg << 16) & PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK); - - if (reg & 0xff000000) { - u32 mask; - u8 size, offset; - - size = (reg >> 24) & 0x3f; - offset = (reg >> 16) & 0x1f; - mask = ((1 << size) - 1) << offset; - data = (data << offset) & mask; - - spin_lock_irqsave(&card->lock, flags); - outl(regptr, card->iobase + PTR); - data |= inl(card->iobase + DATA) & ~mask; - outl(data, card->iobase + DATA); - spin_unlock_irqrestore(&card->lock, flags); - } else { - spin_lock_irqsave(&card->lock, flags); - outl(regptr, card->iobase + PTR); - outl(data, card->iobase + DATA); - spin_unlock_irqrestore(&card->lock, flags); - } -} - -/* ... : data, reg, ... , TAGLIST_END */ -void sblive_writeptr_tag(struct emu10k1_card *card, u32 channel, ...) -{ - va_list args; - - unsigned long flags; - u32 reg; - - va_start(args, channel); - - spin_lock_irqsave(&card->lock, flags); - while ((reg = va_arg(args, u32)) != TAGLIST_END) { - u32 data = va_arg(args, u32); - u32 regptr = (((reg << 16) & PTR_ADDRESS_MASK) - | (channel & PTR_CHANNELNUM_MASK)); - outl(regptr, card->iobase + PTR); - if (reg & 0xff000000) { - int size = (reg >> 24) & 0x3f; - int offset = (reg >> 16) & 0x1f; - u32 mask = ((1 << size) - 1) << offset; - data = (data << offset) & mask; - - data |= inl(card->iobase + DATA) & ~mask; - } - outl(data, card->iobase + DATA); - } - spin_unlock_irqrestore(&card->lock, flags); - - va_end(args); - - return; -} - -u32 sblive_readptr(struct emu10k1_card * card, u32 reg, u32 channel) -{ - u32 regptr, val; - unsigned long flags; - - regptr = ((reg << 16) & PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK); - - if (reg & 0xff000000) { - u32 mask; - u8 size, offset; - - size = (reg >> 24) & 0x3f; - offset = (reg >> 16) & 0x1f; - mask = ((1 << size) - 1) << offset; - - spin_lock_irqsave(&card->lock, flags); - outl(regptr, card->iobase + PTR); - val = inl(card->iobase + DATA); - spin_unlock_irqrestore(&card->lock, flags); - - return (val & mask) >> offset; - } else { - spin_lock_irqsave(&card->lock, flags); - outl(regptr, card->iobase + PTR); - val = inl(card->iobase + DATA); - spin_unlock_irqrestore(&card->lock, flags); - - return val; - } -} - -void emu10k1_irq_enable(struct emu10k1_card *card, u32 irq_mask) -{ - u32 val; - unsigned long flags; - - DPF(2,"emu10k1_irq_enable()\n"); - - spin_lock_irqsave(&card->lock, flags); - val = inl(card->iobase + INTE) | irq_mask; - outl(val, card->iobase + INTE); - spin_unlock_irqrestore(&card->lock, flags); - return; -} - -void emu10k1_irq_disable(struct emu10k1_card *card, u32 irq_mask) -{ - u32 val; - unsigned long flags; - - DPF(2,"emu10k1_irq_disable()\n"); - - spin_lock_irqsave(&card->lock, flags); - val = inl(card->iobase + INTE) & ~irq_mask; - outl(val, card->iobase + INTE); - spin_unlock_irqrestore(&card->lock, flags); - return; -} - -void emu10k1_set_stop_on_loop(struct emu10k1_card *card, u32 voicenum) -{ - /* Voice interrupt */ - if (voicenum >= 32) - sblive_writeptr(card, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 1); - else - sblive_writeptr(card, SOLEL | ((0x0100 | voicenum) << 16), 0, 1); - - return; -} - -void emu10k1_clear_stop_on_loop(struct emu10k1_card *card, u32 voicenum) -{ - /* Voice interrupt */ - if (voicenum >= 32) - sblive_writeptr(card, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 0); - else - sblive_writeptr(card, SOLEL | ((0x0100 | voicenum) << 16), 0, 0); - - return; -} - -static void sblive_wcwait(struct emu10k1_card *card, u32 wait) -{ - volatile unsigned uCount; - u32 newtime = 0, curtime; - - curtime = emu10k1_readfn0(card, WC_SAMPLECOUNTER); - while (wait--) { - uCount = 0; - while (uCount++ < TIMEOUT) { - newtime = emu10k1_readfn0(card, WC_SAMPLECOUNTER); - if (newtime != curtime) - break; - } - - if (uCount >= TIMEOUT) - break; - - curtime = newtime; - } -} - -u16 emu10k1_ac97_read(struct ac97_codec *codec, u8 reg) -{ - struct emu10k1_card *card = codec->private_data; - u16 data; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - - outb(reg, card->iobase + AC97ADDRESS); - data = inw(card->iobase + AC97DATA); - - spin_unlock_irqrestore(&card->lock, flags); - - return data; -} - -void emu10k1_ac97_write(struct ac97_codec *codec, u8 reg, u16 value) -{ - struct emu10k1_card *card = codec->private_data; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - - outb(reg, card->iobase + AC97ADDRESS); - outw(value, card->iobase + AC97DATA); - - spin_unlock_irqrestore(&card->lock, flags); -} - -/********************************************************* -* MPU access functions * -**********************************************************/ - -int emu10k1_mpu_write_data(struct emu10k1_card *card, u8 data) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&card->lock, flags); - - if ((inb(card->iobase + MUSTAT) & MUSTAT_ORDYN) == 0) { - outb(data, card->iobase + MUDATA); - ret = 0; - } else - ret = -1; - - spin_unlock_irqrestore(&card->lock, flags); - - return ret; -} - -int emu10k1_mpu_read_data(struct emu10k1_card *card, u8 * data) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&card->lock, flags); - - if ((inb(card->iobase + MUSTAT) & MUSTAT_IRDYN) == 0) { - *data = inb(card->iobase + MUDATA); - ret = 0; - } else - ret = -1; - - spin_unlock_irqrestore(&card->lock, flags); - - return ret; -} - -int emu10k1_mpu_reset(struct emu10k1_card *card) -{ - u8 status; - unsigned long flags; - - DPF(2, "emu10k1_mpu_reset()\n"); - - if (card->mpuacqcount == 0) { - spin_lock_irqsave(&card->lock, flags); - outb(MUCMD_RESET, card->iobase + MUCMD); - spin_unlock_irqrestore(&card->lock, flags); - - sblive_wcwait(card, 8); - - spin_lock_irqsave(&card->lock, flags); - outb(MUCMD_RESET, card->iobase + MUCMD); - spin_unlock_irqrestore(&card->lock, flags); - - sblive_wcwait(card, 8); - - spin_lock_irqsave(&card->lock, flags); - outb(MUCMD_ENTERUARTMODE, card->iobase + MUCMD); - spin_unlock_irqrestore(&card->lock, flags); - - sblive_wcwait(card, 8); - - spin_lock_irqsave(&card->lock, flags); - status = inb(card->iobase + MUDATA); - spin_unlock_irqrestore(&card->lock, flags); - - if (status == 0xfe) - return 0; - else - return -1; - } - - return 0; -} - -int emu10k1_mpu_acquire(struct emu10k1_card *card) -{ - /* FIXME: This should be a macro */ - ++card->mpuacqcount; - - return 0; -} - -int emu10k1_mpu_release(struct emu10k1_card *card) -{ - /* FIXME: this should be a macro */ - --card->mpuacqcount; - - return 0; -} diff -Nru a/drivers/sound/emu10k1/hwaccess.h b/drivers/sound/emu10k1/hwaccess.h --- a/drivers/sound/emu10k1/hwaccess.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,239 +0,0 @@ -/* - ********************************************************************** - * hwaccess.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _HWACCESS_H -#define _HWACCESS_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "efxmgr.h" -#include "passthrough.h" -#include "midi.h" - -#define EMUPAGESIZE 4096 /* don't change */ -#define NUM_G 64 /* use all channels */ -#define NUM_FXSENDS 4 /* don't change */ -/* setting this to other than a power of two may break some applications */ -#define MAXBUFSIZE 65536 -#define MAXPAGES 8192 -#define BUFMAXPAGES (MAXBUFSIZE / PAGE_SIZE) - -#define FLAGS_AVAILABLE 0x0001 -#define FLAGS_READY 0x0002 - -struct memhandle -{ - dma_addr_t dma_handle; - void *addr; - u32 size; -}; - -#define DEBUG_LEVEL 2 - -#ifdef EMU10K1_DEBUG -# define DPD(level,x,y...) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ , y );} while(0) -# define DPF(level,x) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ );} while(0) -#else -# define DPD(level,x,y...) do { } while (0) /* not debugging: nothing */ -# define DPF(level,x) do { } while (0) -#endif /* EMU10K1_DEBUG */ - -#define ERROR() DPF(1,"error\n") - -/* DATA STRUCTURES */ - -struct emu10k1_waveout -{ - u16 send_routing[3]; - - u8 send_a[3]; - u8 send_b[3]; - u8 send_c[3]; - u8 send_d[3]; -}; - -struct emu10k1_wavein -{ - struct wiinst *ac97; - struct wiinst *mic; - struct wiinst *fx; - - u8 recsrc; - u32 fxwc; -}; - -#define CMD_READ 1 -#define CMD_WRITE 2 - -struct mixer_private_ioctl { - u32 cmd; - u32 val[90]; -}; - -/* bogus ioctls numbers to escape from OSS mixer limitations */ -#define CMD_WRITEFN0 _IOW('D', 0, struct mixer_private_ioctl) -#define CMD_READFN0 _IOR('D', 1, struct mixer_private_ioctl) -#define CMD_WRITEPTR _IOW('D', 2, struct mixer_private_ioctl) -#define CMD_READPTR _IOR('D', 3, struct mixer_private_ioctl) -#define CMD_SETRECSRC _IOW('D', 4, struct mixer_private_ioctl) -#define CMD_GETRECSRC _IOR('D', 5, struct mixer_private_ioctl) -#define CMD_GETVOICEPARAM _IOR('D', 6, struct mixer_private_ioctl) -#define CMD_SETVOICEPARAM _IOW('D', 7, struct mixer_private_ioctl) -#define CMD_GETPATCH _IOR('D', 8, struct mixer_private_ioctl) -#define CMD_GETGPR _IOR('D', 9, struct mixer_private_ioctl) -#define CMD_GETCTLGPR _IOR('D', 10, struct mixer_private_ioctl) -#define CMD_SETPATCH _IOW('D', 11, struct mixer_private_ioctl) -#define CMD_SETGPR _IOW('D', 12, struct mixer_private_ioctl) -#define CMD_SETCTLGPR _IOW('D', 13, struct mixer_private_ioctl) -#define CMD_SETGPOUT _IOW('D', 14, struct mixer_private_ioctl) -#define CMD_GETGPR2OSS _IOR('D', 15, struct mixer_private_ioctl) -#define CMD_SETGPR2OSS _IOW('D', 16, struct mixer_private_ioctl) -#define CMD_SETMCH_FX _IOW('D', 17, struct mixer_private_ioctl) -#define CMD_SETPASSTHROUGH _IOW('D', 18, struct mixer_private_ioctl) -#define CMD_PRIVATE3_VERSION _IOW('D', 19, struct mixer_private_ioctl) - -//up this number when breaking compatibility -#define PRIVATE3_VERSION 1 - -struct emu10k1_card -{ - struct list_head list; - - struct memhandle virtualpagetable; - struct memhandle tankmem; - struct memhandle silentpage; - - spinlock_t lock; - - u8 voicetable[NUM_G]; - u16 emupagetable[MAXPAGES]; - - struct list_head timers; - unsigned timer_delay; - spinlock_t timer_lock; - - struct pci_dev *pci_dev; - unsigned long iobase; - unsigned long length; - unsigned short model; - unsigned int irq; - - int audio_dev; - int audio_dev1; - int midi_dev; -#ifdef EMU10K1_SEQUENCER - int seq_dev; - struct emu10k1_mididevice *seq_mididev; -#endif - - struct ac97_codec ac97; - int ac97_supported_mixers; - int ac97_stereo_mixers; - - /* Number of first fx voice for multichannel output */ - u8 mchannel_fx; - struct emu10k1_waveout waveout; - struct emu10k1_wavein wavein; - struct emu10k1_mpuout *mpuout; - struct emu10k1_mpuin *mpuin; - - struct semaphore open_sem; - mode_t open_mode; - wait_queue_head_t open_wait; - - u32 mpuacqcount; // Mpu acquire count - u32 has_toslink; // TOSLink detection - - u8 chiprev; /* Chip revision */ - - int isaps; - - struct patch_manager mgr; - struct pt_data pt; -}; - -int emu10k1_addxmgr_alloc(u32, struct emu10k1_card *); -void emu10k1_addxmgr_free(struct emu10k1_card *, int); - - - -int emu10k1_find_control_gpr(struct patch_manager *, const char *, const char *); -void emu10k1_set_control_gpr(struct emu10k1_card *, int , s32, int ); - -void emu10k1_set_volume_gpr(struct emu10k1_card *, int, s32, int); - - -#define VOL_6BIT 0x40 -#define VOL_5BIT 0x20 -#define VOL_4BIT 0x10 - -#define TIMEOUT 16384 - -u32 srToPitch(u32); -u8 sumVolumeToAttenuation(u32); - -extern struct list_head emu10k1_devs; - -/* Hardware Abstraction Layer access functions */ - -void emu10k1_writefn0(struct emu10k1_card *, u32 , u32 ); -u32 emu10k1_readfn0(struct emu10k1_card *, u32 ); - -void sblive_writeptr(struct emu10k1_card *, u32 , u32 , u32 ); -void sblive_writeptr_tag(struct emu10k1_card *card, u32 channel, ...); -#define TAGLIST_END 0 - -u32 sblive_readptr(struct emu10k1_card *, u32 , u32 ); - -void emu10k1_irq_enable(struct emu10k1_card *, u32); -void emu10k1_irq_disable(struct emu10k1_card *, u32); -void emu10k1_set_stop_on_loop(struct emu10k1_card *, u32); -void emu10k1_clear_stop_on_loop(struct emu10k1_card *, u32); - -/* AC97 Codec register access function */ -u16 emu10k1_ac97_read(struct ac97_codec *, u8); -void emu10k1_ac97_write(struct ac97_codec *, u8, u16); - -/* MPU access function*/ -int emu10k1_mpu_write_data(struct emu10k1_card *, u8); -int emu10k1_mpu_read_data(struct emu10k1_card *, u8 *); -int emu10k1_mpu_reset(struct emu10k1_card *); -int emu10k1_mpu_acquire(struct emu10k1_card *); -int emu10k1_mpu_release(struct emu10k1_card *); - -#endif /* _HWACCESS_H */ diff -Nru a/drivers/sound/emu10k1/icardmid.h b/drivers/sound/emu10k1/icardmid.h --- a/drivers/sound/emu10k1/icardmid.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,163 +0,0 @@ -/* - ********************************************************************** - * isblive_mid.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _ICARDMIDI_H -#define _ICARDMIDI_H - -/* MIDI defines */ -#define MIDI_DATA_FIRST 0x00 -#define MIDI_DATA_LAST 0x7F -#define MIDI_STATUS_FIRST 0x80 -#define MIDI_STATUS_LAST 0xFF - -/* Channel status bytes */ -#define MIDI_STATUS_CHANNEL_FIRST 0x80 -#define MIDI_STATUS_CHANNEL_LAST 0xE0 -#define MIDI_STATUS_CHANNEL_MASK 0xF0 - -/* Channel voice messages */ -#define MIDI_VOICE_NOTE_OFF 0x80 -#define MIDI_VOICE_NOTE_ON 0x90 -#define MIDI_VOICE_POLY_PRESSURE 0xA0 -#define MIDI_VOICE_CONTROL_CHANGE 0xB0 -#define MIDI_VOICE_PROGRAM_CHANGE 0xC0 -#define MIDI_VOICE_CHANNEL_PRESSURE 0xD0 -#define MIDI_VOICE_PITCH_BEND 0xE0 - -/* Channel mode messages */ -#define MIDI_MODE_CHANNEL MIDI_VOICE_CONTROL_CHANGE - -/* System status bytes */ -#define MIDI_STATUS_SYSTEM_FIRST 0xF0 -#define MIDI_STATUS_SYSTEM_LAST 0xFF - -/* System exclusive messages */ -#define MIDI_SYSEX_BEGIN 0xF0 -#define MIDI_SYSEX_EOX 0xF7 - -/* System common messages */ -#define MIDI_COMMON_TCQF 0xF1 /* Time code quarter frame */ -#define MIDI_COMMON_SONG_POSITION 0xF2 -#define MIDI_COMMON_SONG_SELECT 0xF3 -#define MIDI_COMMON_UNDEFINED_F4 0xF4 -#define MIDI_COMMON_UNDEFINED_F5 0xF5 -#define MIDI_COMMON_TUNE_REQUEST 0xF6 - -/* System real-time messages */ -#define MIDI_RTIME_TIMING_CLOCK 0xF8 -#define MIDI_RTIME_UNDEFINED_F9 0xF9 -#define MIDI_RTIME_START 0xFA -#define MIDI_RTIME_CONTINUE 0xFB -#define MIDI_RTIME_STOP 0xFC -#define MIDI_RTIME_UNDEFINED_FD 0xFD -#define MIDI_RTIME_ACTIVE_SENSING 0xFE -#define MIDI_RTIME_SYSTEM_RESET 0xFF - -/* Flags for flags parm of midiOutCachePatches(), midiOutCacheDrumPatches() */ -#define MIDI_CACHE_ALL 1 -#define MIDI_CACHE_BESTFIT 2 -#define MIDI_CACHE_QUERY 3 -#define MIDI_UNCACHE 4 - -/* Event declarations for MPU IRQ Callbacks */ -#define ICARDMIDI_INLONGDATA 0x00000001 /* MIM_LONGDATA */ -#define ICARDMIDI_INLONGERROR 0x00000002 /* MIM_LONGERROR */ -#define ICARDMIDI_OUTLONGDATA 0x00000004 /* MOM_DONE for MPU OUT buffer */ -#define ICARDMIDI_INDATA 0x00000010 /* MIM_DATA */ -#define ICARDMIDI_INDATAERROR 0x00000020 /* MIM_ERROR */ - -/* Declaration for flags in CARDMIDIBUFFERHDR */ -/* Make it the same as MHDR_DONE, MHDR_INQUEUE in mmsystem.h */ -#define MIDIBUF_DONE 0x00000001 -#define MIDIBUF_INQUEUE 0x00000004 - -/* Declaration for msg parameter in midiCallbackFn */ -#define ICARDMIDI_OUTBUFFEROK 0x00000001 -#define ICARDMIDI_INMIDIOK 0x00000002 - -/* Declaration for technology in struct midi_caps */ -#define MT_MIDIPORT 0x00000001 /* In original MIDIOUTCAPS structure */ -#define MT_FMSYNTH 0x00000004 /* In original MIDIOUTCAPS structure */ -#define MT_AWESYNTH 0x00001000 -#define MT_PCISYNTH 0x00002000 -#define MT_PCISYNTH64 0x00004000 -#define CARDMIDI_AWEMASK 0x0000F000 - -enum LocalErrorCode -{ - CTSTATUS_NOTENABLED = 0x7000, - CTSTATUS_READY, - CTSTATUS_BUSY, - CTSTATUS_DATAAVAIL, - CTSTATUS_NODATA, - CTSTATUS_NEXT_BYTE -}; - -/* MIDI data block header */ -struct midi_hdr -{ - u8 *reserved; /* Pointer to original locked data block */ - u32 bufferlength; /* Length of data in data block */ - u32 bytesrecorded; /* Used for input only */ - u32 user; /* For client's use */ - u32 flags; /* Assorted flags (see defines) */ - struct list_head list; /* Reserved for driver */ - u8 *data; /* Second copy of first pointer */ -}; - -/* Enumeration for SetControl */ -enum -{ - MIDIOBJVOLUME = 0x1, - MIDIQUERYACTIVEINST -}; - -struct midi_queue -{ - struct midi_queue *next; - u32 qtype; /* 0 = short message, 1 = long data */ - u32 length; - u32 sizeLeft; - u8 *midibyte; - unsigned long refdata; -}; - -struct midi_openinfo -{ - u32 cbsize; - u32 flags; - unsigned long refdata; - u32 streamid; -}; - -int emu10k1_midi_callback(unsigned long , unsigned long, unsigned long *); - -#endif /* _ICARDMIDI_H */ diff -Nru a/drivers/sound/emu10k1/icardwav.h b/drivers/sound/emu10k1/icardwav.h --- a/drivers/sound/emu10k1/icardwav.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,53 +0,0 @@ -/* - ********************************************************************** - * icardwav.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _ICARDWAV_H -#define _ICARDWAV_H - -struct wave_format -{ - int id; - int samplingrate; - u8 bitsperchannel; - u8 channels; /* 1 = Mono, 2 = Stereo, 3, ... = Multichannel */ - u8 bytesperchannel; - u8 bytespervoicesample; - u8 bytespersample; - int bytespersec; - u8 passthrough; -}; - -/* emu10k1_wave states */ -#define WAVE_STATE_OPEN 0x01 -#define WAVE_STATE_STARTED 0x02 -#define WAVE_STATE_CLOSED 0x04 - -#endif /* _ICARDWAV_H */ diff -Nru a/drivers/sound/emu10k1/irqmgr.c b/drivers/sound/emu10k1/irqmgr.c --- a/drivers/sound/emu10k1/irqmgr.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,104 +0,0 @@ - -/* - ********************************************************************** - * irqmgr.c - IRQ manager for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include "hwaccess.h" -#include "8010.h" -#include "cardmi.h" -#include "cardmo.h" -#include "irqmgr.h" - -/* Interrupt handler */ - -void emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct emu10k1_card *card = (struct emu10k1_card *) dev_id; - u32 irqstatus; - - DPD(4, "emu10k1_interrupt called, irq = %u\n", irq); - - /* - ** NOTE : - ** We do a 'while loop' here cos on certain machines, with both - ** playback and recording going on at the same time, IRQs will - ** stop coming in after a while. Checking IPND indeed shows that - ** there are interrupts pending but the PIC says no IRQs pending. - ** I suspect that some boards need edge-triggered IRQs but are not - ** getting that condition if we don't completely clear the IPND - ** (make sure no more interrupts are pending). - ** - Eric - */ - - while ((irqstatus = inl(card->iobase + IPR))) { - DPD(4, "irq status %#x\n", irqstatus); - - /* acknowledge interrupt */ - outl(irqstatus, card->iobase + IPR); - - if (irqstatus & IRQTYPE_TIMER) { - emu10k1_timer_irqhandler(card); - irqstatus &= ~IRQTYPE_TIMER; - } - - if (irqstatus & IRQTYPE_DSP) { - emu10k1_dsp_irqhandler(card); - irqstatus &= ~IRQTYPE_DSP; - } - - if (irqstatus & IRQTYPE_MPUIN) { - emu10k1_mpuin_irqhandler(card); - irqstatus &= ~IRQTYPE_MPUIN; - } - - if (irqstatus & IRQTYPE_MPUOUT) { - emu10k1_mpuout_irqhandler(card); - irqstatus &= ~IRQTYPE_MPUOUT; - } - - if (irqstatus & IPR_MUTE) { - emu10k1_mute_irqhandler(card); - irqstatus &=~IPR_MUTE; - } - - if (irqstatus & IPR_VOLINCR) { - emu10k1_volincr_irqhandler(card); - irqstatus &=~IPR_VOLINCR; - } - - if (irqstatus & IPR_VOLDECR) { - emu10k1_voldecr_irqhandler(card); - irqstatus &=~IPR_VOLDECR; - } - - if (irqstatus) - emu10k1_irq_disable(card, irqstatus); - } -} diff -Nru a/drivers/sound/emu10k1/irqmgr.h b/drivers/sound/emu10k1/irqmgr.h --- a/drivers/sound/emu10k1/irqmgr.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,52 +0,0 @@ -/* - ********************************************************************** - * irq.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _IRQ_H -#define _IRQ_H - -/* EMU Irq Types */ -#define IRQTYPE_PCIBUSERROR IPR_PCIERROR -#define IRQTYPE_MIXERBUTTON (IPR_VOLINCR | IPR_VOLDECR | IPR_MUTE) -#define IRQTYPE_VOICE (IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK) -#define IRQTYPE_RECORD (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL | IPR_MICBUFFULL | IPR_MICBUFHALFFULL | IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL) -#define IRQTYPE_MPUOUT IPR_MIDITRANSBUFEMPTY -#define IRQTYPE_MPUIN IPR_MIDIRECVBUFEMPTY -#define IRQTYPE_TIMER IPR_INTERVALTIMER -#define IRQTYPE_SPDIF (IPR_GPSPDIFSTATUSCHANGE | IPR_CDROMSTATUSCHANGE) -#define IRQTYPE_DSP IPR_FXDSP - -void emu10k1_timer_irqhandler(struct emu10k1_card *); -void emu10k1_dsp_irqhandler(struct emu10k1_card *); -void emu10k1_mute_irqhandler(struct emu10k1_card *); -void emu10k1_volincr_irqhandler(struct emu10k1_card *); -void emu10k1_voldecr_irqhandler(struct emu10k1_card *); - -#endif /* _IRQ_H */ diff -Nru a/drivers/sound/emu10k1/main.c b/drivers/sound/emu10k1/main.c --- a/drivers/sound/emu10k1/main.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1181 +0,0 @@ -/* - ********************************************************************** - * main.c - Creative EMU10K1 audio driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * November 2, 1999 Alan Cox cleaned up stuff - * - ********************************************************************** - * - * 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. - * - ********************************************************************** - * - * Supported devices: - * /dev/dsp: Standard /dev/dsp device, OSS-compatible - * /dev/dsp1: Routes to rear speakers only - * /dev/mixer: Standard /dev/mixer device, OSS-compatible - * /dev/midi: Raw MIDI UART device, mostly OSS-compatible - * /dev/sequencer: Sequencer Interface (requires sound.o) - * - * Revision history: - * 0.1 beta Initial release - * 0.2 Lowered initial mixer vol. Improved on stuttering wave playback. Added MIDI UART support. - * 0.3 Fixed mixer routing bug, added APS, joystick support. - * 0.4 Added rear-channel, SPDIF support. - * 0.5 Source cleanup, SMP fixes, multiopen support, 64 bit arch fixes, - * moved bh's to tasklets, moved to the new PCI driver initialization style. - * 0.6 Make use of pci_alloc_consistent, improve compatibility layer for 2.2 kernels, - * code reorganization and cleanup. - * 0.7 Support for the Emu-APS. Bug fixes for voice cache setup, mmaped sound + poll(). - * Support for setting external TRAM size. - * 0.8 Make use of the kernel ac97 interface. Support for a dsp patch manager. - * 0.9 Re-enables rear speakers volume controls - * 0.10 Initializes rear speaker volume. - * Dynamic patch storage allocation. - * New private ioctls to change control gpr values. - * Enable volume control interrupts. - * By default enable dsp routes to digital out. - * 0.11 Fixed fx / 4 problem. - * 0.12 Implemented mmaped for recording. - * Fixed bug: not unreserving mmaped buffer pages. - * IRQ handler cleanup. - * 0.13 Fixed problem with dsp1 - * Simplified dsp patch writing (inside the driver) - * Fixed several bugs found by the Stanford tools - * 0.14 New control gpr to oss mixer mapping feature (Chris Purnell) - * Added AC3 Passthrough Support (Juha Yrjola) - * Added Support for 5.1 cards (digital out and the third analog out) - * 0.15 Added Sequencer Support (Daniel Mack) - * Support for multichannel pcm playback (Eduard Hasenleithner) - * 0.16 Mixer improvements, added old treble/bass support (Daniel Bertrand) - * Small code format cleanup. - * Deadlock bug fix for emu10k1_volxxx_irqhandler(). - * - *********************************************************************/ - -/* These are only included once per module */ -#include -#include -#include -#include -#include -#include - -#include "hwaccess.h" -#include "8010.h" -#include "efxmgr.h" -#include "cardwo.h" -#include "cardwi.h" -#include "cardmo.h" -#include "cardmi.h" -#include "recmgr.h" -#include "ecard.h" - - -#ifdef EMU10K1_SEQUENCER -#define MIDI_SYNTH_NAME "EMU10K1 MIDI" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT - -#include "../sound_config.h" -#include "../midi_synth.h" - -/* this should be in dev_table.h */ -#define SNDCARD_EMU10K1 46 -#endif - -#define DRIVER_VERSION "0.16" - -/* FIXME: is this right? */ -/* does the card support 32 bit bus master?*/ -#define EMU10K1_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ - -#ifndef PCI_VENDOR_ID_CREATIVE -#define PCI_VENDOR_ID_CREATIVE 0x1102 -#endif - -#ifndef PCI_DEVICE_ID_CREATIVE_EMU10K1 -#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 -#endif - -#define EMU_APS_SUBID 0x40011102 - -enum { - EMU10K1 = 0, -}; - -static char *card_names[] __devinitdata = { - "EMU10K1", -}; - -static struct pci_device_id emu10k1_pci_tbl[] = { - {PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_EMU10K1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, EMU10K1}, - {0,} -}; - -MODULE_DEVICE_TABLE(pci, emu10k1_pci_tbl); - -/* Global var instantiation */ - -LIST_HEAD(emu10k1_devs); - -extern struct file_operations emu10k1_audio_fops; -extern struct file_operations emu10k1_mixer_fops; -extern struct file_operations emu10k1_midi_fops; - -#ifdef EMU10K1_SEQUENCER -static struct midi_operations emu10k1_midi_operations; -#endif - -extern void emu10k1_interrupt(int, void *, struct pt_regs *s); - -static int __devinit emu10k1_audio_init(struct emu10k1_card *card) -{ - card->audio_dev = register_sound_dsp(&emu10k1_audio_fops, -1); - if (card->audio_dev < 0) { - printk(KERN_ERR "emu10k1: cannot register first audio device!\n"); - goto err_dev; - } - - card->audio_dev1 = register_sound_dsp(&emu10k1_audio_fops, -1); - if (card->audio_dev1 < 0) { - printk(KERN_ERR "emu10k1: cannot register second audio device!\n"); - goto err_dev1; - } - - /* Assign default playback voice parameters */ - card->mchannel_fx = 8; - /* mono voice */ - card->waveout.send_a[0] = 0xff; - card->waveout.send_b[0] = 0xff; - card->waveout.send_c[0] = 0x00; - card->waveout.send_d[0] = 0x00; - card->waveout.send_routing[0] = 0x3210; - - /* stereo voice */ - /* left */ - card->waveout.send_a[1] = 0xff; - card->waveout.send_b[1] = 0x00; - card->waveout.send_c[1] = 0x00; - card->waveout.send_d[1] = 0x00; - card->waveout.send_routing[1] = 0x3210; - - /* right */ - card->waveout.send_a[2] = 0x00; - card->waveout.send_b[2] = 0xff; - card->waveout.send_c[2] = 0x00; - card->waveout.send_d[2] = 0x00; - card->waveout.send_routing[2] = 0x3210; - - /* Assign default recording parameters */ - /* FIXME */ - if(card->isaps) - card->wavein.recsrc = WAVERECORD_FX; - else - card->wavein.recsrc = WAVERECORD_AC97; - - card->wavein.fxwc = 0x0003; - return 0; - -err_dev1: - unregister_sound_dsp(card->audio_dev); -err_dev: - return -ENODEV; -} - -static void __devinit emu10k1_audio_cleanup(struct emu10k1_card *card) -{ - unregister_sound_dsp(card->audio_dev1); - unregister_sound_dsp(card->audio_dev); -} - -static int __devinit emu10k1_mixer_init(struct emu10k1_card *card) -{ - char s[32]; - 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"); - return -EIO; - } - - card->ac97.private_data = card; - - if (!card->isaps) { - 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) { - printk(KERN_ERR "emu10k1: unable to probe AC97 codec\n"); - goto err_out; - } - /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version - does not support this, it shouldn't do any harm */ - sblive_writeptr(card, AC97SLOT, 0, AC97SLOT_CNTR | AC97SLOT_LFE); - - // Force 5bit - //card->ac97.bit_resolution=5; - - if (!proc_mkdir ("driver/emu10k1", 0)) { - printk(KERN_ERR "emu10k1: unable to create proc directory driver/emu10k1\n"); - goto err_out; - } - - sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name); - if (!proc_mkdir (s, 0)) { - printk(KERN_ERR "emu10k1: unable to create proc directory %s\n", s); - goto err_emu10k1_proc; - } - - sprintf(s, "driver/emu10k1/%s/ac97", card->pci_dev->slot_name); - 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; - } - - return 0; - - err_ac97_proc: - sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name); - remove_proc_entry(s, NULL); - - err_emu10k1_proc: - remove_proc_entry("driver/emu10k1", NULL); - err_out: - unregister_sound_mixer (card->ac97.dev_mixer); - return -EIO; -} - -static void __devinit emu10k1_mixer_cleanup(struct emu10k1_card *card) -{ - char s[32]; - - if (!card->isaps) { - sprintf(s, "driver/emu10k1/%s/ac97", card->pci_dev->slot_name); - remove_proc_entry(s, NULL); - - sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name); - remove_proc_entry(s, NULL); - - remove_proc_entry("driver/emu10k1", NULL); - } - - unregister_sound_mixer (card->ac97.dev_mixer); -} - -static int __devinit emu10k1_midi_init(struct emu10k1_card *card) -{ - int ret; - - card->midi_dev = register_sound_midi(&emu10k1_midi_fops, -1); - if (card->midi_dev < 0) { - printk(KERN_ERR "emu10k1: cannot register midi device!\n"); - return -ENODEV; - } - - - card->mpuout = kmalloc(sizeof(struct emu10k1_mpuout), GFP_KERNEL); - if (card->mpuout == NULL) { - printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuout: out of memory\n"); - ret = -ENOMEM; - goto err_out1; - } - - memset(card->mpuout, 0, sizeof(struct emu10k1_mpuout)); - - card->mpuout->intr = 1; - card->mpuout->status = FLAGS_AVAILABLE; - card->mpuout->state = CARDMIDIOUT_STATE_DEFAULT; - - tasklet_init(&card->mpuout->tasklet, emu10k1_mpuout_bh, (unsigned long) card); - - spin_lock_init(&card->mpuout->lock); - - card->mpuin = kmalloc(sizeof(struct emu10k1_mpuin), GFP_KERNEL); - if (card->mpuin == NULL) { - printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuin: out of memory\n"); - ret = -ENOMEM; - goto err_out2; - } - - memset(card->mpuin, 0, sizeof(struct emu10k1_mpuin)); - - card->mpuin->status = FLAGS_AVAILABLE; - - tasklet_init(&card->mpuin->tasklet, emu10k1_mpuin_bh, (unsigned long) card->mpuin); - - spin_lock_init(&card->mpuin->lock); - - /* Reset the MPU port */ - if (emu10k1_mpu_reset(card) < 0) { - ERROR(); - ret = -EIO; - goto err_out3; - } - -#ifdef EMU10K1_SEQUENCER - card->seq_dev = sound_alloc_mididev(); - if (card->seq_dev == -1) - printk(KERN_WARNING "emu10k1: unable to register sequencer device!"); - else { - std_midi_synth.midi_dev = card->seq_dev; - midi_devs[card->seq_dev] = - (struct midi_operations *) - kmalloc(sizeof(struct midi_operations), GFP_KERNEL); - - if (midi_devs[card->seq_dev] == NULL) { - printk(KERN_ERR "emu10k1: unable to allocate memory!"); - sound_unload_mididev(card->seq_dev); - card->seq_dev = -1; - return 0; - } else { - memcpy((char *)midi_devs[card->seq_dev], - (char *)&emu10k1_midi_operations, - sizeof(struct midi_operations)); - midi_devs[card->seq_dev]->devc = card; - sequencer_init(); - } - } - card->seq_mididev = 0; -#endif - return 0; - -err_out3: - kfree(card->mpuin); -err_out2: - kfree(card->mpuout); -err_out1: - unregister_sound_midi(card->midi_dev); - return ret; -} - -static void __devinit emu10k1_midi_cleanup(struct emu10k1_card *card) -{ - tasklet_kill(&card->mpuout->tasklet); - kfree(card->mpuout); - - tasklet_kill(&card->mpuin->tasklet); - kfree(card->mpuin); - -#ifdef EMU10K1_SEQUENCER - if (card->seq_dev > -1) { - kfree(midi_devs[card->seq_dev]); - midi_devs[card->seq_dev] = NULL; - sound_unload_mididev(card->seq_dev); - card->seq_dev = -1; - } -#endif - - unregister_sound_midi(card->midi_dev); -} - -static void __devinit voice_init(struct emu10k1_card *card) -{ - int i; - - for (i = 0; i < NUM_G; i++) - card->voicetable[i] = VOICE_USAGE_FREE; -} - -static void __devinit timer_init(struct emu10k1_card *card) -{ - INIT_LIST_HEAD(&card->timers); - card->timer_delay = TIMER_STOPPED; - card->timer_lock = SPIN_LOCK_UNLOCKED; -} - -static void __devinit addxmgr_init(struct emu10k1_card *card) -{ - u32 count; - - for (count = 0; count < MAXPAGES; count++) - card->emupagetable[count] = 0; - - /* Mark first page as used */ - /* This page is reserved by the driver */ - card->emupagetable[0] = 0x8001; - card->emupagetable[1] = MAXPAGES - 1; -} - -static void __devinit fx_cleanup(struct patch_manager *mgr) -{ - int i; - for(i = 0; i < mgr->current_pages; i++) - free_page((unsigned long) mgr->patch[i]); -} - -static int __devinit fx_init(struct emu10k1_card *card) -{ - struct patch_manager *mgr = &card->mgr; - struct dsp_patch *patch; - struct dsp_rpatch *rpatch; - s32 left, right; - int i; - u32 pc = 0; - u32 patch_n; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - mgr->ctrl_gpr[i][0] = -1; - mgr->ctrl_gpr[i][1] = -1; - } - - for (i = 0; i < 512; i++) - OP(6, 0x40, 0x40, 0x40, 0x40); - - for (i = 0; i < 256; i++) - sblive_writeptr_tag(card, 0, - FXGPREGBASE + i, 0, - TANKMEMADDRREGBASE + i, 0, - TAGLIST_END); - - /* !! The number bellow must equal the number of patches, currently 11 !! */ - mgr->current_pages = (11 + PATCHES_PER_PAGE - 1) / PATCHES_PER_PAGE; - for (i = 0; i < mgr->current_pages; i++) { - mgr->patch[i] = (void *)__get_free_page(GFP_KERNEL); - if (mgr->patch[i] == NULL) { - mgr->current_pages = i; - fx_cleanup(mgr); - return -ENOMEM; - } - memset(mgr->patch[i], 0, PAGE_SIZE); - } - - pc = 0; - patch_n = 0; - //first free GPR = 0x11b - - /* FX volume correction and Volume control*/ - INPUT_PATCH_START(patch, "Pcm L vol", 0x0, 0); - GET_OUTPUT_GPR(patch, 0x100, 0x0); - GET_CONTROL_GPR(patch, 0x106, "Vol", 0, 0x7fffffff); - GET_DYNAMIC_GPR(patch, 0x112); - - OP(4, 0x112, 0x40, PCM_IN_L, 0x44); //*4 - OP(0, 0x100, 0x040, 0x112, 0x106); //*vol - INPUT_PATCH_END(patch); - - - INPUT_PATCH_START(patch, "Pcm R vol", 0x1, 0); - GET_OUTPUT_GPR(patch, 0x101, 0x1); - GET_CONTROL_GPR(patch, 0x107, "Vol", 0, 0x7fffffff); - GET_DYNAMIC_GPR(patch, 0x112); - - OP(4, 0x112, 0x40, PCM_IN_R, 0x44); - OP(0, 0x101, 0x040, 0x112, 0x107); - - INPUT_PATCH_END(patch); - - - // CD-Digital In Volume control - INPUT_PATCH_START(patch, "CD-Digital Vol L", 0x12, 0); - GET_OUTPUT_GPR(patch, 0x10c, 0x12); - GET_CONTROL_GPR(patch, 0x10d, "Vol", 0, 0x7fffffff); - - OP(0, 0x10c, 0x040, SPDIF_CD_L, 0x10d); - INPUT_PATCH_END(patch); - - INPUT_PATCH_START(patch, "CD-Digital Vol R", 0x13, 0); - GET_OUTPUT_GPR(patch, 0x10e, 0x13); - GET_CONTROL_GPR(patch, 0x10f, "Vol", 0, 0x7fffffff); - - OP(0, 0x10e, 0x040, SPDIF_CD_R, 0x10f); - INPUT_PATCH_END(patch); - - //Volume Correction for Multi-channel Inputs - INPUT_PATCH_START(patch, "Multi-Channel Gain", 0x08, 0); - patch->input=patch->output=0x3F00; - - GET_OUTPUT_GPR(patch, 0x113, MULTI_FRONT_L); - GET_OUTPUT_GPR(patch, 0x114, MULTI_FRONT_R); - GET_OUTPUT_GPR(patch, 0x115, MULTI_REAR_L); - GET_OUTPUT_GPR(patch, 0x116, MULTI_REAR_R); - GET_OUTPUT_GPR(patch, 0x117, MULTI_CENTER); - GET_OUTPUT_GPR(patch, 0x118, MULTI_LFE); - - OP(4, 0x113, 0x40, MULTI_FRONT_L, 0x44); - OP(4, 0x114, 0x40, MULTI_FRONT_R, 0x44); - OP(4, 0x115, 0x40, MULTI_REAR_L, 0x44); - OP(4, 0x116, 0x40, MULTI_REAR_R, 0x44); - OP(4, 0x117, 0x40, MULTI_CENTER, 0x44); - OP(4, 0x118, 0x40, MULTI_LFE, 0x44); - - INPUT_PATCH_END(patch); - - - //Routing patch start - ROUTING_PATCH_START(rpatch, "Routing"); - GET_INPUT_GPR(rpatch, 0x100, 0x0); - GET_INPUT_GPR(rpatch, 0x101, 0x1); - GET_INPUT_GPR(rpatch, 0x10c, 0x12); - GET_INPUT_GPR(rpatch, 0x10e, 0x13); - GET_INPUT_GPR(rpatch, 0x113, MULTI_FRONT_L); - GET_INPUT_GPR(rpatch, 0x114, MULTI_FRONT_R); - GET_INPUT_GPR(rpatch, 0x115, MULTI_REAR_L); - GET_INPUT_GPR(rpatch, 0x116, MULTI_REAR_R); - GET_INPUT_GPR(rpatch, 0x117, MULTI_CENTER); - GET_INPUT_GPR(rpatch, 0x118, MULTI_LFE); - - GET_DYNAMIC_GPR(rpatch, 0x102); - GET_DYNAMIC_GPR(rpatch, 0x103); - - GET_OUTPUT_GPR(rpatch, 0x104, 0x8); - GET_OUTPUT_GPR(rpatch, 0x105, 0x9); - GET_OUTPUT_GPR(rpatch, 0x10a, 0x2); - GET_OUTPUT_GPR(rpatch, 0x10b, 0x3); - - - /* input buffer */ - OP(6, 0x102, AC97_IN_L, 0x40, 0x40); - OP(6, 0x103, AC97_IN_R, 0x40, 0x40); - - - /* Digital In + PCM + MULTI_FRONT-> AC97 out (front speakers)*/ - OP(6, AC97_FRONT_L, 0x100, 0x10c, 0x113); - - CONNECT(MULTI_FRONT_L, AC97_FRONT_L); - CONNECT(PCM_IN_L, AC97_FRONT_L); - CONNECT(SPDIF_CD_L, AC97_FRONT_L); - - OP(6, AC97_FRONT_R, 0x101, 0x10e, 0x114); - - CONNECT(MULTI_FRONT_R, AC97_FRONT_R); - CONNECT(PCM_IN_R, AC97_FRONT_R); - CONNECT(SPDIF_CD_R, AC97_FRONT_R); - - /* Digital In + PCM + AC97 In + PCM1 + MULTI_REAR --> Rear Channel */ - OP(6, 0x104, PCM1_IN_L, 0x100, 0x115); - OP(6, 0x104, 0x104, 0x10c, 0x102); - - CONNECT(MULTI_REAR_L, ANALOG_REAR_L); - CONNECT(AC97_IN_L, ANALOG_REAR_L); - CONNECT(PCM_IN_L, ANALOG_REAR_L); - CONNECT(SPDIF_CD_L, ANALOG_REAR_L); - CONNECT(PCM1_IN_L, ANALOG_REAR_L); - - OP(6, 0x105, PCM1_IN_R, 0x101, 0x116); - OP(6, 0x105, 0x105, 0x10e, 0x103); - - CONNECT(MULTI_REAR_R, ANALOG_REAR_R); - CONNECT(AC97_IN_R, ANALOG_REAR_R); - CONNECT(PCM_IN_R, ANALOG_REAR_R); - CONNECT(SPDIF_CD_R, ANALOG_REAR_R); - CONNECT(PCM1_IN_R, ANALOG_REAR_R); - - /* Digital In + PCM + AC97 In + MULTI_FRONT --> Digital out */ - OP(6, 0x10b, 0x100, 0x102, 0x10c); - OP(6, 0x10b, 0x10b, 0x113, 0x40); - - CONNECT(MULTI_FRONT_L, DIGITAL_OUT_L); - CONNECT(PCM_IN_L, DIGITAL_OUT_L); - CONNECT(AC97_IN_L, DIGITAL_OUT_L); - CONNECT(SPDIF_CD_L, DIGITAL_OUT_L); - - OP(6, 0x10a, 0x101, 0x103, 0x10e); - OP(6, 0x10b, 0x10b, 0x114, 0x40); - - CONNECT(MULTI_FRONT_R, DIGITAL_OUT_R); - CONNECT(PCM_IN_R, DIGITAL_OUT_R); - CONNECT(AC97_IN_R, DIGITAL_OUT_R); - CONNECT(SPDIF_CD_R, DIGITAL_OUT_R); - - /* AC97 In --> ADC Recording Buffer */ - OP(6, ADC_REC_L, 0x102, 0x40, 0x40); - - CONNECT(AC97_IN_L, ADC_REC_L); - - OP(6, ADC_REC_R, 0x103, 0x40, 0x40); - - CONNECT(AC97_IN_R, ADC_REC_R); - - - /* fx12:Analog-Center */ - OP(6, ANALOG_CENTER, 0x117, 0x40, 0x40); - CONNECT(MULTI_CENTER, ANALOG_CENTER); - - /* fx11:Analog-LFE */ - OP(6, ANALOG_LFE, 0x118, 0x40, 0x40); - CONNECT(MULTI_LFE, ANALOG_LFE); - - /* fx12:Digital-Center */ - OP(6, DIGITAL_CENTER, 0x117, 0x40, 0x40); - CONNECT(MULTI_CENTER, DIGITAL_CENTER); - - /* fx11:Analog-LFE */ - OP(6, DIGITAL_LFE, 0x118, 0x40, 0x40); - CONNECT(MULTI_LFE, DIGITAL_LFE); - - ROUTING_PATCH_END(rpatch); - - - // Rear volume control - OUTPUT_PATCH_START(patch, "Vol Rear L", 0x8, 0); - GET_INPUT_GPR(patch, 0x104, 0x8); - GET_CONTROL_GPR(patch, 0x119, "Vol", 0, 0x7fffffff); - - OP(0, ANALOG_REAR_L, 0x040, 0x104, 0x119); - OUTPUT_PATCH_END(patch); - - - OUTPUT_PATCH_START(patch, "Vol Rear R", 0x9, 0); - GET_INPUT_GPR(patch, 0x105, 0x9); - GET_CONTROL_GPR(patch, 0x11a, "Vol", 0, 0x7fffffff); - - OP(0, ANALOG_REAR_R, 0x040, 0x105, 0x11a); - OUTPUT_PATCH_END(patch); - - - //Master volume control on front-digital - OUTPUT_PATCH_START(patch, "Vol Master L", 0x2, 1); - GET_INPUT_GPR(patch, 0x10a, 0x2); - GET_CONTROL_GPR(patch, 0x108, "Vol", 0, 0x7fffffff); - - OP(0, DIGITAL_OUT_L, 0x040, 0x10a, 0x108); - OUTPUT_PATCH_END(patch); - - - OUTPUT_PATCH_START(patch, "Vol Master R", 0x3, 1); - GET_INPUT_GPR(patch, 0x10b, 0x3); - GET_CONTROL_GPR(patch, 0x109, "Vol", 0, 0x7fffffff); - - OP(0, DIGITAL_OUT_R, 0x040, 0x10b, 0x109); - OUTPUT_PATCH_END(patch); - - - /* delimiter patch */ - patch = PATCH(mgr, patch_n); - patch->code_size = 0; - - sblive_writeptr(card, DBG, 0, 0); - - mgr->lock = SPIN_LOCK_UNLOCKED; - - - //Master volume - 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; - - 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.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); - - //PCM Volume - 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; - - emu10k1_set_volume_gpr(card, 6, left, VOL_5BIT); - emu10k1_set_volume_gpr(card, 7, right, VOL_5BIT); - - //CD-Digital Volume - mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][0] = 0xd; - mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][1] = 0xf; - - left = right = 67; - card->ac97.mixer_state[SOUND_MIXER_DIGITAL1] = (right << 8) | left; - - 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); - 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; - - return 0; -} - -static int __devinit hw_init(struct emu10k1_card *card) -{ - int nCh; - u32 pagecount; /* tmp */ - int ret; - - /* Disable audio and lock cache */ - emu10k1_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE); - - /* Reset recording buffers */ - sblive_writeptr_tag(card, 0, - MICBS, ADCBS_BUFSIZE_NONE, - MICBA, 0, - FXBS, ADCBS_BUFSIZE_NONE, - FXBA, 0, - ADCBS, ADCBS_BUFSIZE_NONE, - ADCBA, 0, - TAGLIST_END); - - /* Disable channel interrupt */ - emu10k1_writefn0(card, INTE, 0); - sblive_writeptr_tag(card, 0, - CLIEL, 0, - CLIEH, 0, - SOLEL, 0, - SOLEH, 0, - TAGLIST_END); - - /* Init envelope engine */ - for (nCh = 0; nCh < NUM_G; nCh++) { - sblive_writeptr_tag(card, nCh, - DCYSUSV, 0, - IP, 0, - VTFT, 0xffff, - CVCF, 0xffff, - PTRX, 0, - CPF, 0, - CCR, 0, - - PSST, 0, - DSL, 0x10, - CCCA, 0, - Z1, 0, - Z2, 0, - FXRT, 0xd01c0000, - - ATKHLDM, 0, - DCYSUSM, 0, - IFATN, 0xffff, - PEFE, 0, - FMMOD, 0, - TREMFRQ, 24, /* 1 Hz */ - FM2FRQ2, 24, /* 1 Hz */ - TEMPENV, 0, - - /*** These are last so OFF prevents writing ***/ - LFOVAL2, 0, - LFOVAL1, 0, - ATKHLDV, 0, - ENVVOL, 0, - ENVVAL, 0, - TAGLIST_END); - } - - /* - ** Init to 0x02109204 : - ** Clock accuracy = 0 (1000ppm) - ** Sample Rate = 2 (48kHz) - ** Audio Channel = 1 (Left of 2) - ** Source Number = 0 (Unspecified) - ** Generation Status = 1 (Original for Cat Code 12) - ** Cat Code = 12 (Digital Signal Mixer) - ** Mode = 0 (Mode 0) - ** Emphasis = 0 (None) - ** CP = 1 (Copyright unasserted) - ** AN = 0 (Digital audio) - ** P = 0 (Consumer) - */ - - sblive_writeptr_tag(card, 0, - - /* SPDIF0 */ - SPCS0, (SPCS_CLKACCY_1000PPM | 0x002000000 | - SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT), - - /* SPDIF1 */ - SPCS1, (SPCS_CLKACCY_1000PPM | 0x002000000 | - SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT), - - /* SPDIF2 & SPDIF3 */ - SPCS2, (SPCS_CLKACCY_1000PPM | 0x002000000 | - SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT), - - TAGLIST_END); - - ret = fx_init(card); /* initialize effects engine */ - if (ret < 0) - return ret; - - card->tankmem.size = 0; - - card->virtualpagetable.size = MAXPAGES * sizeof(u32); - - card->virtualpagetable.addr = pci_alloc_consistent(card->pci_dev, card->virtualpagetable.size, &card->virtualpagetable.dma_handle); - if (card->virtualpagetable.addr == NULL) { - ERROR(); - ret = -ENOMEM; - goto err0; - } - - card->silentpage.size = EMUPAGESIZE; - - card->silentpage.addr = pci_alloc_consistent(card->pci_dev, card->silentpage.size, &card->silentpage.dma_handle); - if (card->silentpage.addr == NULL) { - ERROR(); - ret = -ENOMEM; - goto err1; - } - - for (pagecount = 0; pagecount < MAXPAGES; pagecount++) - ((u32 *) card->virtualpagetable.addr)[pagecount] = cpu_to_le32((card->silentpage.dma_handle * 2) | pagecount); - - /* Init page table & tank memory base register */ - sblive_writeptr_tag(card, 0, - PTB, card->virtualpagetable.dma_handle, - TCB, 0, - TCBS, 0, - TAGLIST_END); - - for (nCh = 0; nCh < NUM_G; nCh++) { - sblive_writeptr_tag(card, nCh, - MAPA, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), - MAPB, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), - TAGLIST_END); - } - - /* Hokay, now enable the AUD bit */ - /* Enable Audio = 1 */ - /* Mute Disable Audio = 0 */ - /* Lock Tank Memory = 1 */ - /* Lock Sound Memory = 0 */ - /* Auto Mute = 1 */ - - if (card->model == 0x20 || card->model == 0xc400 || - (card->model == 0x21 && card->chiprev < 6)) - emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE); - else - emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE); - - /* Enable Vol_Ctrl irqs */ - emu10k1_irq_enable(card, INTE_VOLINCRENABLE | INTE_VOLDECRENABLE | INTE_MUTEENABLE | INTE_FXDSPENABLE); - - /* FIXME: TOSLink detection */ - card->has_toslink = 0; - - /* Initialize digital passthrough variables */ - card->pt.pos_gpr = card->pt.intr_gpr = card->pt.enable_gpr = -1; - card->pt.selected = 0; - card->pt.state = PT_STATE_INACTIVE; - card->pt.spcs_to_use = 0x01; - card->pt.patch_name = "AC3pass"; - card->pt.intr_gpr_name = "count"; - card->pt.enable_gpr_name = "enable"; - card->pt.pos_gpr_name = "ptr"; - spin_lock_init(&card->pt.lock); - init_waitqueue_head(&card->pt.wait); - -/* tmp = sblive_readfn0(card, HCFG); - if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { - sblive_writefn0(card, HCFG, tmp | 0x800); - - udelay(512); - - if (tmp != (sblive_readfn0(card, HCFG) & ~0x800)) { - card->has_toslink = 1; - sblive_writefn0(card, HCFG, tmp); - } - } -*/ - return 0; - - err1: - pci_free_consistent(card->pci_dev, card->virtualpagetable.size, card->virtualpagetable.addr, card->virtualpagetable.dma_handle); - err0: - fx_cleanup(&card->mgr); - - return ret; -} - -static int __devinit emu10k1_init(struct emu10k1_card *card) -{ - /* Init Card */ - if (hw_init(card) < 0) - return -1; - - voice_init(card); - timer_init(card); - addxmgr_init(card); - - DPD(2, " hw control register -> %#x\n", emu10k1_readfn0(card, HCFG)); - - return 0; -} - -static void __devinit emu10k1_cleanup(struct emu10k1_card *card) -{ - int ch; - - emu10k1_writefn0(card, INTE, 0); - - /** Shutdown the chip **/ - for (ch = 0; ch < NUM_G; ch++) - sblive_writeptr(card, DCYSUSV, ch, 0); - - for (ch = 0; ch < NUM_G; ch++) { - sblive_writeptr_tag(card, ch, - VTFT, 0, - CVCF, 0, - PTRX, 0, - CPF, 0, - TAGLIST_END); - } - - /* Disable audio and lock cache */ - emu10k1_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE); - - sblive_writeptr_tag(card, 0, - PTB, 0, - - /* Reset recording buffers */ - MICBS, ADCBS_BUFSIZE_NONE, - MICBA, 0, - FXBS, ADCBS_BUFSIZE_NONE, - FXBA, 0, - FXWC, 0, - ADCBS, ADCBS_BUFSIZE_NONE, - ADCBA, 0, - TCBS, 0, - TCB, 0, - DBG, 0x8000, - - /* Disable channel interrupt */ - CLIEL, 0, - CLIEH, 0, - SOLEL, 0, - SOLEH, 0, - TAGLIST_END); - - - pci_free_consistent(card->pci_dev, card->virtualpagetable.size, card->virtualpagetable.addr, card->virtualpagetable.dma_handle); - pci_free_consistent(card->pci_dev, card->silentpage.size, card->silentpage.addr, card->silentpage.dma_handle); - - if(card->tankmem.size != 0) - pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle); - - /* release patch storage memory */ - fx_cleanup(&card->mgr); -} - -/* Driver initialization routine */ -static int __devinit emu10k1_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) -{ - struct emu10k1_card *card; - u32 subsysvid; - int ret; - - if (pci_set_dma_mask(pci_dev, EMU10K1_DMA_MASK)) { - printk(KERN_ERR "emu10k1: architecture does not support 32bit PCI busmaster DMA\n"); - return -ENODEV; - } - - if (pci_enable_device(pci_dev)) - return -EIO; - - pci_set_master(pci_dev); - - if ((card = kmalloc(sizeof(struct emu10k1_card), GFP_KERNEL)) == NULL) { - printk(KERN_ERR "emu10k1: out of memory\n"); - return -ENOMEM; - } - memset(card, 0, sizeof(struct emu10k1_card)); - - card->iobase = pci_resource_start(pci_dev, 0); - card->length = pci_resource_len(pci_dev, 0); - - if (request_region(card->iobase, card->length, card_names[pci_id->driver_data]) == NULL) { - printk(KERN_ERR "emu10k1: IO space in use\n"); - ret = -EBUSY; - goto err_region; - } - - pci_set_drvdata(pci_dev, card); - - card->irq = pci_dev->irq; - card->pci_dev = pci_dev; - - /* Reserve IRQ Line */ - if (request_irq(card->irq, emu10k1_interrupt, SA_SHIRQ, card_names[pci_id->driver_data], card)) { - printk(KERN_ERR "emu10k1: IRQ in use\n"); - ret = -EBUSY; - goto err_irq; - } - - pci_read_config_byte(pci_dev, PCI_REVISION_ID, &card->chiprev); - pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &card->model); - - printk(KERN_INFO "emu10k1: %s rev %d model 0x%x found, IO at 0x%04lx-0x%04lx, IRQ %d\n", - card_names[pci_id->driver_data], card->chiprev, card->model, card->iobase, - card->iobase + card->length - 1, card->irq); - - pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &subsysvid); - card->isaps = (subsysvid == EMU_APS_SUBID); - - spin_lock_init(&card->lock); - init_MUTEX(&card->open_sem); - card->open_mode = 0; - init_waitqueue_head(&card->open_wait); - - ret = emu10k1_audio_init(card); - if(ret < 0) { - printk(KERN_ERR "emu10k1: cannot initialize audio devices\n"); - goto err_audio; - } - - ret = emu10k1_mixer_init(card); - if(ret < 0) { - printk(KERN_ERR "emu10k1: cannot initialize AC97 codec\n"); - goto err_mixer; - } - - ret = emu10k1_midi_init(card); - if (ret < 0) { - printk(KERN_ERR "emu10k1: cannot register midi device\n"); - goto err_midi; - } - - ret = emu10k1_init(card); - if (ret < 0) { - printk(KERN_ERR "emu10k1: cannot initialize device\n"); - goto err_emu10k1_init; - } - - if (card->isaps) - emu10k1_ecard_init(card); - - list_add(&card->list, &emu10k1_devs); - - return 0; - -err_emu10k1_init: - emu10k1_midi_cleanup(card); - -err_midi: - emu10k1_mixer_cleanup(card); - -err_mixer: - emu10k1_audio_cleanup(card); - -err_audio: - free_irq(card->irq, card); - -err_irq: - release_region(card->iobase, card->length); - pci_set_drvdata(pci_dev, NULL); - -err_region: - kfree(card); - - return ret; -} - -static void __devexit emu10k1_remove(struct pci_dev *pci_dev) -{ - struct emu10k1_card *card = pci_get_drvdata(pci_dev); - - list_del(&card->list); - - emu10k1_cleanup(card); - emu10k1_midi_cleanup(card); - emu10k1_mixer_cleanup(card); - emu10k1_audio_cleanup(card); - free_irq(card->irq, card); - release_region(card->iobase, card->length); - kfree(card); - pci_set_drvdata(pci_dev, NULL); -} - -MODULE_AUTHOR("Bertrand Lee, Cai Ying. (Email to: emu10k1-devel@opensource.creative.com)"); -MODULE_DESCRIPTION("Creative EMU10K1 PCI Audio Driver v" DRIVER_VERSION "\nCopyright (C) 1999 Creative Technology Ltd."); -MODULE_LICENSE("GPL"); - -static struct pci_driver emu10k1_pci_driver = { - name: "emu10k1", - id_table: emu10k1_pci_tbl, - probe: emu10k1_probe, - remove: emu10k1_remove, -}; - -static int __init emu10k1_init_module(void) -{ - printk(KERN_INFO "Creative EMU10K1 PCI Audio Driver, version " DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); - - return pci_module_init(&emu10k1_pci_driver); -} - -static void __exit emu10k1_cleanup_module(void) -{ - pci_unregister_driver(&emu10k1_pci_driver); - - return; -} - -module_init(emu10k1_init_module); -module_exit(emu10k1_cleanup_module); - -#ifdef EMU10K1_SEQUENCER - -/* in midi.c */ -extern int emu10k1_seq_midi_open(int dev, int mode, - void (*input)(int dev, unsigned char midi_byte), - void (*output)(int dev)); -extern void emu10k1_seq_midi_close(int dev); -extern int emu10k1_seq_midi_out(int dev, unsigned char midi_byte); -extern int emu10k1_seq_midi_start_read(int dev); -extern int emu10k1_seq_midi_end_read(int dev); -extern void emu10k1_seq_midi_kick(int dev); -extern int emu10k1_seq_midi_buffer_status(int dev); - -static struct midi_operations emu10k1_midi_operations = -{ - THIS_MODULE, - {"EMU10K1 MIDI", 0, 0, SNDCARD_EMU10K1}, - &std_midi_synth, - {0}, - emu10k1_seq_midi_open, - emu10k1_seq_midi_close, - NULL, - emu10k1_seq_midi_out, - emu10k1_seq_midi_start_read, - emu10k1_seq_midi_end_read, - emu10k1_seq_midi_kick, - NULL, - emu10k1_seq_midi_buffer_status, - NULL -}; - -#endif diff -Nru a/drivers/sound/emu10k1/midi.c b/drivers/sound/emu10k1/midi.c --- a/drivers/sound/emu10k1/midi.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,597 +0,0 @@ -/* - ********************************************************************** - * midi.c - /dev/midi interface for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#define __NO_VERSION__ -#include -#include -#include -#include -#include -#include - -#include "hwaccess.h" -#include "cardmo.h" -#include "cardmi.h" -#include "midi.h" - -#ifdef EMU10K1_SEQUENCER -#include "../sound_config.h" -#endif - -static spinlock_t midi_spinlock __attribute((unused)) = SPIN_LOCK_UNLOCKED; - -static void init_midi_hdr(struct midi_hdr *midihdr) -{ - midihdr->bufferlength = MIDIIN_BUFLEN; - midihdr->bytesrecorded = 0; - midihdr->flags = 0; -} - -static int midiin_add_buffer(struct emu10k1_mididevice *midi_dev, struct midi_hdr **midihdrptr) -{ - struct midi_hdr *midihdr; - - if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL) { - ERROR(); - return -EINVAL; - } - - init_midi_hdr(midihdr); - - if ((midihdr->data = (u8 *) kmalloc(MIDIIN_BUFLEN, GFP_KERNEL)) == NULL) { - ERROR(); - kfree(midihdr); - return -1; - } - - if (emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr) < 0) { - ERROR(); - kfree(midihdr->data); - kfree(midihdr); - return -1; - } - - *midihdrptr = midihdr; - list_add_tail(&midihdr->list, &midi_dev->mid_hdrs); - - return 0; -} - -static int emu10k1_midi_open(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct emu10k1_card *card = NULL; - struct emu10k1_mididevice *midi_dev; - struct list_head *entry; - - DPF(2, "emu10k1_midi_open()\n"); - - /* Check for correct device to open */ - list_for_each(entry, &emu10k1_devs) { - card = list_entry(entry, struct emu10k1_card, list); - - if (card->midi_dev == minor) - goto match; - } - - return -ENODEV; - -match: -#ifdef EMU10K1_SEQUENCER - if (card->seq_mididev) /* card is opened by sequencer */ - return -EBUSY; -#endif - - /* Wait for device to become free */ - down(&card->open_sem); - while (card->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - if (file->f_flags & O_NONBLOCK) { - up(&card->open_sem); - return -EBUSY; - } - - up(&card->open_sem); - interruptible_sleep_on(&card->open_wait); - - if (signal_pending(current)) { - return -ERESTARTSYS; - } - - down(&card->open_sem); - } - - if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL) - return -EINVAL; - - midi_dev->card = card; - midi_dev->mistate = MIDIIN_STATE_STOPPED; - init_waitqueue_head(&midi_dev->oWait); - init_waitqueue_head(&midi_dev->iWait); - midi_dev->ird = 0; - midi_dev->iwr = 0; - midi_dev->icnt = 0; - INIT_LIST_HEAD(&midi_dev->mid_hdrs); - - if (file->f_mode & FMODE_READ) { - struct midi_openinfo dsCardMidiOpenInfo; - struct midi_hdr *midihdr1; - struct midi_hdr *midihdr2; - - dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; - - if (emu10k1_mpuin_open(card, &dsCardMidiOpenInfo) < 0) { - ERROR(); - kfree(midi_dev); - return -ENODEV; - } - - /* Add two buffers to receive sysex buffer */ - if (midiin_add_buffer(midi_dev, &midihdr1) < 0) { - kfree(midi_dev); - return -ENODEV; - } - - if (midiin_add_buffer(midi_dev, &midihdr2) < 0) { - list_del(&midihdr1->list); - kfree(midihdr1->data); - kfree(midihdr1); - kfree(midi_dev); - return -ENODEV; - } - } - - if (file->f_mode & FMODE_WRITE) { - struct midi_openinfo dsCardMidiOpenInfo; - - dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; - - if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) { - ERROR(); - kfree(midi_dev); - return -ENODEV; - } - } - - file->private_data = (void *) midi_dev; - - card->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); - - up(&card->open_sem); - - return 0; -} - -static int emu10k1_midi_release(struct inode *inode, struct file *file) -{ - struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data; - struct emu10k1_card *card; - - lock_kernel(); - - card = midi_dev->card; - DPF(2, "emu10k1_midi_release()\n"); - - if (file->f_mode & FMODE_WRITE) { - if (!(file->f_flags & O_NONBLOCK)) { - - while (!signal_pending(current) && (card->mpuout->firstmidiq != NULL)) { - DPF(4, "Cannot close - buffers not empty\n"); - - interruptible_sleep_on(&midi_dev->oWait); - - } - } - - emu10k1_mpuout_close(card); - } - - if (file->f_mode & FMODE_READ) { - struct midi_hdr *midihdr; - - if (midi_dev->mistate == MIDIIN_STATE_STARTED) { - emu10k1_mpuin_stop(card); - midi_dev->mistate = MIDIIN_STATE_STOPPED; - } - - emu10k1_mpuin_reset(card); - emu10k1_mpuin_close(card); - - while (!list_empty(&midi_dev->mid_hdrs)) { - midihdr = list_entry(midi_dev->mid_hdrs.next, struct midi_hdr, list); - - list_del(midi_dev->mid_hdrs.next); - kfree(midihdr->data); - kfree(midihdr); - } - } - - kfree(midi_dev); - - down(&card->open_sem); - card->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE)); - up(&card->open_sem); - wake_up_interruptible(&card->open_wait); - - unlock_kernel(); - - return 0; -} - -static ssize_t emu10k1_midi_read(struct file *file, char *buffer, size_t count, loff_t * pos) -{ - struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data; - ssize_t ret = 0; - u16 cnt; - unsigned long flags; - - DPD(4, "emu10k1_midi_read(), count %#x\n", (u32) count); - - if (pos != &file->f_pos) - return -ESPIPE; - - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - - if (midi_dev->mistate == MIDIIN_STATE_STOPPED) { - if (emu10k1_mpuin_start(midi_dev->card) < 0) { - ERROR(); - return -EINVAL; - } - - midi_dev->mistate = MIDIIN_STATE_STARTED; - } - - while (count > 0) { - cnt = MIDIIN_BUFLEN - midi_dev->ird; - - spin_lock_irqsave(&midi_spinlock, flags); - - if (midi_dev->icnt < cnt) - cnt = midi_dev->icnt; - - spin_unlock_irqrestore(&midi_spinlock, flags); - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - DPF(2, " Go to sleep...\n"); - - interruptible_sleep_on(&midi_dev->iWait); - - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - - continue; - } - - if (copy_to_user(buffer, midi_dev->iBuf + midi_dev->ird, cnt)) { - ERROR(); - return ret ? ret : -EFAULT; - } - - midi_dev->ird += cnt; - midi_dev->ird %= MIDIIN_BUFLEN; - - spin_lock_irqsave(&midi_spinlock, flags); - - midi_dev->icnt -= cnt; - - spin_unlock_irqrestore(&midi_spinlock, flags); - - count -= cnt; - buffer += cnt; - ret += cnt; - - if (midi_dev->icnt == 0) - break; - } - - return ret; -} - -static ssize_t emu10k1_midi_write(struct file *file, const char *buffer, size_t count, loff_t * pos) -{ - struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data; - struct midi_hdr *midihdr; - ssize_t ret = 0; - unsigned long flags; - - DPD(4, "emu10k1_midi_write(), count=%#x\n", (u32) count); - - if (pos != &file->f_pos) - return -ESPIPE; - - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - - if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL) - return -EINVAL; - - midihdr->bufferlength = count; - midihdr->bytesrecorded = 0; - midihdr->flags = 0; - - if ((midihdr->data = (u8 *) kmalloc(count, GFP_KERNEL)) == NULL) { - ERROR(); - kfree(midihdr); - return -EINVAL; - } - - if (copy_from_user(midihdr->data, buffer, count)) { - kfree(midihdr->data); - kfree(midihdr); - return ret ? ret : -EFAULT; - } - - spin_lock_irqsave(&midi_spinlock, flags); - - if (emu10k1_mpuout_add_buffer(midi_dev->card, midihdr) < 0) { - ERROR(); - kfree(midihdr->data); - kfree(midihdr); - spin_unlock_irqrestore(&midi_spinlock, flags); - return -EINVAL; - } - - spin_unlock_irqrestore(&midi_spinlock, flags); - - return count; -} - -static unsigned int emu10k1_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - DPF(4, "emu10k1_midi_poll() called\n"); - return 0; -} - -int emu10k1_midi_callback(unsigned long msg, unsigned long refdata, unsigned long *pmsg) -{ - struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) refdata; - struct midi_hdr *midihdr = NULL; - unsigned long flags; - int i; - - DPF(4, "emu10k1_midi_callback()\n"); - - spin_lock_irqsave(&midi_spinlock, flags); - - switch (msg) { - case ICARDMIDI_OUTLONGDATA: - midihdr = (struct midi_hdr *) pmsg[2]; - - kfree(midihdr->data); - kfree(midihdr); - wake_up_interruptible(&midi_dev->oWait); - - break; - - case ICARDMIDI_INLONGDATA: - midihdr = (struct midi_hdr *) pmsg[2]; - - for (i = 0; i < midihdr->bytesrecorded; i++) { - midi_dev->iBuf[midi_dev->iwr++] = midihdr->data[i]; - midi_dev->iwr %= MIDIIN_BUFLEN; - } - - midi_dev->icnt += midihdr->bytesrecorded; - - if (midi_dev->mistate == MIDIIN_STATE_STARTED) { - init_midi_hdr(midihdr); - emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr); - wake_up_interruptible(&midi_dev->iWait); - } - break; - - case ICARDMIDI_INDATA: - { - u8 *pBuf = (u8 *) & pmsg[1]; - u16 bytesvalid = pmsg[2]; - - for (i = 0; i < bytesvalid; i++) { - midi_dev->iBuf[midi_dev->iwr++] = pBuf[i]; - midi_dev->iwr %= MIDIIN_BUFLEN; - } - - midi_dev->icnt += bytesvalid; - } - - wake_up_interruptible(&midi_dev->iWait); - break; - - default: /* Unknown message */ - spin_unlock_irqrestore(&midi_spinlock, flags); - return -1; - } - - spin_unlock_irqrestore(&midi_spinlock, flags); - - return 0; -} - -/* MIDI file operations */ -struct file_operations emu10k1_midi_fops = { - owner: THIS_MODULE, - read: emu10k1_midi_read, - write: emu10k1_midi_write, - poll: emu10k1_midi_poll, - open: emu10k1_midi_open, - release: emu10k1_midi_release, -}; - - -#ifdef EMU10K1_SEQUENCER - -/* functions used for sequencer access */ - -int emu10k1_seq_midi_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev)) -{ - struct emu10k1_card *card; - struct midi_openinfo dsCardMidiOpenInfo; - struct emu10k1_mididevice *midi_dev; - - if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) - return -EINVAL; - - card = midi_devs[dev]->devc; - - if (card->open_mode) /* card is opened native */ - return -EBUSY; - - DPF(2, "emu10k1_seq_midi_open()\n"); - - if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL) - return -EINVAL; - - midi_dev->card = card; - midi_dev->mistate = MIDIIN_STATE_STOPPED; - init_waitqueue_head(&midi_dev->oWait); - init_waitqueue_head(&midi_dev->iWait); - midi_dev->ird = 0; - midi_dev->iwr = 0; - midi_dev->icnt = 0; - INIT_LIST_HEAD(&midi_dev->mid_hdrs); - - dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; - - if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) { - ERROR(); - return -ENODEV; - } - - card->seq_mididev = midi_dev; - - return 0; -} - -void emu10k1_seq_midi_close(int dev) -{ - struct emu10k1_card *card; - - DPF(2, "emu10k1_seq_midi_close()\n"); - if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) - return; - - card = midi_devs[dev]->devc; - emu10k1_mpuout_close(card); - - if (card->seq_mididev) { - kfree(card->seq_mididev); - card->seq_mididev = 0; - } -} - -int emu10k1_seq_midi_out(int dev, unsigned char midi_byte) -{ - struct emu10k1_card *card; - struct midi_hdr *midihdr; - unsigned long flags; - - if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) - return -EINVAL; - - card = midi_devs[dev]->devc; - - if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL) - return -EINVAL; - - midihdr->bufferlength = 1; - midihdr->bytesrecorded = 0; - midihdr->flags = 0; - - if ((midihdr->data = (u8 *) kmalloc(1, GFP_KERNEL)) == NULL) { - ERROR(); - kfree(midihdr); - return -EINVAL; - } - - *(midihdr->data) = midi_byte; - - spin_lock_irqsave(&midi_spinlock, flags); - - if (emu10k1_mpuout_add_buffer(card, midihdr) < 0) { - ERROR(); - kfree(midihdr->data); - kfree(midihdr); - spin_unlock_irqrestore(&midi_spinlock, flags); - return -EINVAL; - } - - spin_unlock_irqrestore(&midi_spinlock, flags); - - return 1; -} - -int emu10k1_seq_midi_start_read(int dev) -{ - return 0; -} - -int emu10k1_seq_midi_end_read(int dev) -{ - return 0; -} - -void emu10k1_seq_midi_kick(int dev) -{ -} - -int emu10k1_seq_midi_buffer_status(int dev) -{ - int count; - struct midi_queue *queue; - struct emu10k1_card *card; - - if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) - return -EINVAL; - - count = 0; - - card = midi_devs[dev]->devc; - queue = card->mpuout->firstmidiq; - - while (queue != NULL) { - count++; - if (queue == card->mpuout->lastmidiq) - break; - - queue = queue->next; - } - - return count; -} - -#endif - diff -Nru a/drivers/sound/emu10k1/midi.h b/drivers/sound/emu10k1/midi.h --- a/drivers/sound/emu10k1/midi.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,55 +0,0 @@ -/* - ********************************************************************** - * midi.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _MIDI_H -#define _MIDI_H - -#define FMODE_MIDI_SHIFT 3 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -#define MIDIIN_STATE_STARTED 0x00000001 -#define MIDIIN_STATE_STOPPED 0x00000002 - -#define MIDIIN_BUFLEN 1024 - -struct emu10k1_mididevice -{ - struct emu10k1_card *card; - u32 mistate; - wait_queue_head_t oWait; - wait_queue_head_t iWait; - s8 iBuf[MIDIIN_BUFLEN]; - u16 ird, iwr, icnt; - struct list_head mid_hdrs; -}; - -#endif /* _MIDI_H */ diff -Nru a/drivers/sound/emu10k1/mixer.c b/drivers/sound/emu10k1/mixer.c --- a/drivers/sound/emu10k1/mixer.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,675 +0,0 @@ -/* - ********************************************************************** - * mixer.c - /dev/mixer interface for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * November 2, 1999 Alan Cox cleaned up stuff - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#define __NO_VERSION__ /* Kernel version only defined once */ -#include -#include -#include -#include - -#include "hwaccess.h" -#include "8010.h" -#include "recmgr.h" - - -static const u32 bass_table[41][5] = { - { 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 }, - { 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d }, - { 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee }, - { 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c }, - { 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b }, - { 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 }, - { 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f }, - { 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 }, - { 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 }, - { 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 }, - { 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 }, - { 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be }, - { 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b }, - { 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c }, - { 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b }, - { 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 }, - { 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a }, - { 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 }, - { 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 }, - { 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e }, - { 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee }, - { 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 }, - { 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 }, - { 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 }, - { 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 }, - { 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e }, - { 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 }, - { 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 }, - { 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 }, - { 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 }, - { 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 }, - { 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca }, - { 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 }, - { 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 }, - { 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 }, - { 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 }, - { 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a }, - { 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f }, - { 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 }, - { 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 }, - { 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 } -}; - -static const u32 treble_table[41][5] = { - { 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 }, - { 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 }, - { 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 }, - { 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca }, - { 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 }, - { 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 }, - { 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 }, - { 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 }, - { 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 }, - { 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df }, - { 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff }, - { 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 }, - { 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c }, - { 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 }, - { 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 }, - { 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 }, - { 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 }, - { 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d }, - { 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 }, - { 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 }, - { 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f }, - { 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb }, - { 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 }, - { 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 }, - { 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd }, - { 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 }, - { 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad }, - { 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 }, - { 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 }, - { 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c }, - { 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 }, - { 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 }, - { 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 }, - { 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 }, - { 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 }, - { 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 }, - { 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d }, - { 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b }, - { 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 }, - { 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd }, - { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 } -}; - - -static void set_bass(struct emu10k1_card *card, int l, int r) -{ - int i; - - l = (l * 40 + 50) / 100; - r = (r * 40 + 50) / 100; - - for (i = 0; i < 5; i++) - sblive_writeptr(card, GPR_BASE + card->mgr.ctrl_gpr[SOUND_MIXER_BASS][0] + i, 0, bass_table[l][i]); -} - -static void set_treble(struct emu10k1_card *card, int l, int r) -{ - int i; - - l = (l * 40 + 50) / 100; - r = (r * 40 + 50) / 100; - - for (i = 0; i < 5; i++) - sblive_writeptr(card, GPR_BASE + card->mgr.ctrl_gpr[SOUND_MIXER_TREBLE][0] + i , 0, treble_table[l][i]); -} - -const char volume_params[SOUND_MIXER_NRDEVICES]= { -/* Used by the ac97 driver */ - [SOUND_MIXER_VOLUME] = VOL_6BIT, - [SOUND_MIXER_BASS] = VOL_4BIT, - [SOUND_MIXER_TREBLE] = VOL_4BIT, - [SOUND_MIXER_PCM] = VOL_5BIT, - [SOUND_MIXER_SPEAKER] = VOL_4BIT, - [SOUND_MIXER_LINE] = VOL_5BIT, - [SOUND_MIXER_MIC] = VOL_5BIT, - [SOUND_MIXER_CD] = VOL_5BIT, - [SOUND_MIXER_ALTPCM] = VOL_6BIT, - [SOUND_MIXER_IGAIN] = VOL_4BIT, - [SOUND_MIXER_LINE1] = VOL_5BIT, - [SOUND_MIXER_PHONEIN] = VOL_5BIT, - [SOUND_MIXER_PHONEOUT] = VOL_6BIT, - [SOUND_MIXER_VIDEO] = VOL_5BIT, -/* Not used by the ac97 driver */ - [SOUND_MIXER_SYNTH] = VOL_5BIT, - [SOUND_MIXER_IMIX] = VOL_5BIT, - [SOUND_MIXER_RECLEV] = VOL_5BIT, - [SOUND_MIXER_OGAIN] = VOL_5BIT, - [SOUND_MIXER_LINE2] = VOL_5BIT, - [SOUND_MIXER_LINE3] = VOL_5BIT, - [SOUND_MIXER_DIGITAL1] = VOL_5BIT, - [SOUND_MIXER_DIGITAL2] = VOL_5BIT, - [SOUND_MIXER_DIGITAL3] = VOL_5BIT, - [SOUND_MIXER_RADIO] = VOL_5BIT, - [SOUND_MIXER_MONITOR] = VOL_5BIT -}; - -/* Mixer file operations */ -static int emu10k1_private_mixer(struct emu10k1_card *card, unsigned int cmd, unsigned long arg) -{ - struct mixer_private_ioctl *ctl; - struct dsp_patch *patch; - u32 size, page; - int addr, size_reg, i, ret; - unsigned int id, ch; - - switch (cmd) { - - case SOUND_MIXER_PRIVATE3: - - ctl = (struct mixer_private_ioctl *) kmalloc(sizeof(struct mixer_private_ioctl), GFP_KERNEL); - if (ctl == NULL) - return -ENOMEM; - - if (copy_from_user(ctl, (void *) arg, sizeof(struct mixer_private_ioctl))) { - kfree(ctl); - return -EFAULT; - } - - ret = 0; - switch (ctl->cmd) { -#ifdef DBGEMU - case CMD_WRITEFN0: - emu10k1_writefn0(card, ctl->val[0], ctl->val[1]); - break; - - case CMD_WRITEPTR: - if (ctl->val[1] >= 0x40 || ctl->val[0] > 0xff) { - ret = -EINVAL; - break; - } - - if ((ctl->val[0] & 0x7ff) > 0x3f) - ctl->val[1] = 0x00; - - sblive_writeptr(card, ctl->val[0], ctl->val[1], ctl->val[2]); - - break; -#endif - case CMD_READFN0: - ctl->val[2] = emu10k1_readfn0(card, ctl->val[0]); - - if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) - ret = -EFAULT; - - break; - - case CMD_READPTR: - if (ctl->val[1] >= 0x40 || (ctl->val[0] & 0x7ff) > 0xff) { - ret = -EINVAL; - break; - } - - if ((ctl->val[0] & 0x7ff) > 0x3f) - ctl->val[1] = 0x00; - - ctl->val[2] = sblive_readptr(card, ctl->val[0], ctl->val[1]); - - if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) - ret = -EFAULT; - - break; - - case CMD_SETRECSRC: - switch (ctl->val[0]) { - case WAVERECORD_AC97: - if (card->isaps) { - ret = -EINVAL; - break; - } - - card->wavein.recsrc = WAVERECORD_AC97; - break; - - case WAVERECORD_MIC: - card->wavein.recsrc = WAVERECORD_MIC; - break; - - case WAVERECORD_FX: - card->wavein.recsrc = WAVERECORD_FX; - card->wavein.fxwc = ctl->val[1] & 0xffff; - - if (!card->wavein.fxwc) - ret = -EINVAL; - - break; - - default: - ret = -EINVAL; - break; - } - break; - - case CMD_GETRECSRC: - ctl->val[0] = card->wavein.recsrc; - ctl->val[1] = card->wavein.fxwc; - if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) - ret = -EFAULT; - - break; - - case CMD_GETVOICEPARAM: - ctl->val[0] = card->waveout.send_routing[0]; - ctl->val[1] = card->waveout.send_a[0] | card->waveout.send_b[0] << 8 | - card->waveout.send_c[0] << 16 | card->waveout.send_d[0] << 24; - - ctl->val[2] = card->waveout.send_routing[1]; - ctl->val[3] = card->waveout.send_a[1] | card->waveout.send_b[1] << 8 | - card->waveout.send_c[1] << 16 | card->waveout.send_d[1] << 24; - - ctl->val[4] = card->waveout.send_routing[2]; - ctl->val[5] = card->waveout.send_a[2] | card->waveout.send_b[2] << 8 | - card->waveout.send_c[2] << 16 | card->waveout.send_d[2] << 24; - - if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) - ret = -EFAULT; - - break; - - case CMD_SETVOICEPARAM: - card->waveout.send_routing[0] = ctl->val[0] & 0xffff; - card->waveout.send_a[0] = ctl->val[1] & 0xff; - card->waveout.send_b[0] = (ctl->val[1] >> 8) & 0xff; - card->waveout.send_c[0] = (ctl->val[1] >> 16) & 0xff; - card->waveout.send_d[0] = (ctl->val[1] >> 24) & 0xff; - - card->waveout.send_routing[1] = ctl->val[2] & 0xffff; - card->waveout.send_a[1] = ctl->val[3] & 0xff; - card->waveout.send_b[1] = (ctl->val[3] >> 8) & 0xff; - card->waveout.send_c[1] = (ctl->val[3] >> 16) & 0xff; - card->waveout.send_d[1] = (ctl->val[3] >> 24) & 0xff; - - card->waveout.send_routing[2] = ctl->val[4] & 0xffff; - card->waveout.send_a[2] = ctl->val[5] & 0xff; - card->waveout.send_b[2] = (ctl->val[5] >> 8) & 0xff; - card->waveout.send_c[2] = (ctl->val[5] >> 16) & 0xff; - card->waveout.send_d[2] = (ctl->val[5] >> 24) & 0xff; - - break; - - case CMD_SETMCH_FX: - card->mchannel_fx = ctl->val[0] & 0x000f; - break; - - case CMD_GETPATCH: - if (ctl->val[0] == 0) { - if (copy_to_user((void *) arg, &card->mgr.rpatch, sizeof(struct dsp_rpatch))) - ret = -EFAULT; - } else { - if ((ctl->val[0] - 1) / PATCHES_PER_PAGE >= card->mgr.current_pages) { - ret = -EINVAL; - break; - } - - if (copy_to_user((void *) arg, PATCH(&card->mgr, ctl->val[0] - 1), sizeof(struct dsp_patch))) - ret = -EFAULT; - } - - break; - - case CMD_GETGPR: - id = ctl->val[0]; - - if (id > NUM_GPRS) { - ret = -EINVAL; - break; - } - - if (copy_to_user((void *) arg, &card->mgr.gpr[id], sizeof(struct dsp_gpr))) - ret = -EFAULT; - - break; - - case CMD_GETCTLGPR: - addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, &((char *) ctl->val)[PATCH_NAME_SIZE]); - ctl->val[0] = sblive_readptr(card, addr, 0); - - if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) - ret = -EFAULT; - - break; - - case CMD_SETPATCH: - if (ctl->val[0] == 0) - memcpy(&card->mgr.rpatch, &ctl->val[1], sizeof(struct dsp_rpatch)); - else { - page = (ctl->val[0] - 1) / PATCHES_PER_PAGE; - if (page > MAX_PATCHES_PAGES) { - ret = -EINVAL; - break; - } - - if (page >= card->mgr.current_pages) { - for (i = card->mgr.current_pages; i < page + 1; i++) { - card->mgr.patch[i] = (void *)__get_free_page(GFP_KERNEL); - if(card->mgr.patch[i] == NULL) { - card->mgr.current_pages = i; - ret = -ENOMEM; - break; - } - memset(card->mgr.patch[i], 0, PAGE_SIZE); - } - card->mgr.current_pages = page + 1; - } - - patch = PATCH(&card->mgr, ctl->val[0] - 1); - - memcpy(patch, &ctl->val[1], sizeof(struct dsp_patch)); - - if (patch->code_size == 0) { - for(i = page + 1; i < card->mgr.current_pages; i++) - free_page((unsigned long) card->mgr.patch[i]); - - card->mgr.current_pages = page + 1; - } - } - break; - - case CMD_SETGPR: - if (ctl->val[0] > NUM_GPRS) { - ret = -EINVAL; - break; - } - - memcpy(&card->mgr.gpr[ctl->val[0]], &ctl->val[1], sizeof(struct dsp_gpr)); - break; - - case CMD_SETCTLGPR: - addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, (char *) ctl->val + PATCH_NAME_SIZE); - emu10k1_set_control_gpr(card, addr, *((s32 *)((char *) ctl->val + 2 * PATCH_NAME_SIZE)), 0); - break; - - case CMD_SETGPOUT: - if (ctl->val[0] > 2 || ctl->val[1] > 1) { - ret= -EINVAL; - break; - } - - emu10k1_writefn0(card, (1 << 24) | (((ctl->val[0]) + 10) << 16) | HCFG, ctl->val[1]); - break; - - case CMD_GETGPR2OSS: - id = ctl->val[0]; - ch = ctl->val[1]; - - if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) { - ret = -EINVAL; - break; - } - - ctl->val[2] = card->mgr.ctrl_gpr[id][ch]; - - if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) - ret = -EFAULT; - - break; - - case CMD_SETGPR2OSS: - id = ctl->val[0]; - ch = ctl->val[1]; - addr = ctl->val[2]; - - if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) { - ret = -EINVAL; - break; - } - - card->mgr.ctrl_gpr[id][ch] = addr; - - if (card->isaps) - break; - - if (addr >= 0) { - unsigned int state = card->ac97.mixer_state[id]; - - if (ch) { - state >>= 8; - card->ac97.stereo_mixers |= (1 << id); - } else { - 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); - } else if (id == SOUND_MIXER_BASS) { - 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 { - if (ch) { - card->ac97.stereo_mixers &= ~(1 << id); - card->ac97.stereo_mixers |= card->ac97_stereo_mixers; - } else { - card->ac97.supported_mixers &= ~(1 << id); - card->ac97.supported_mixers |= card->ac97_supported_mixers; - } - } - break; - - case CMD_SETPASSTHROUGH: - card->pt.selected = ctl->val[0] ? 1 : 0; - if (card->pt.state != PT_STATE_INACTIVE) - break; - - card->pt.spcs_to_use = ctl->val[0] & 0x07; - break; - - case CMD_PRIVATE3_VERSION: - ctl->val[0]=PRIVATE3_VERSION; - if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) - ret = -EFAULT; - break; - - default: - ret = -EINVAL; - break; - } - - kfree(ctl); - return ret; - break; - - case SOUND_MIXER_PRIVATE4: - - if (copy_from_user(&size, (void *) arg, sizeof(size))) - return -EFAULT; - - DPD(2, "External tram size %#x\n", size); - - if (size > 0x1fffff) - return -EINVAL; - - size_reg = 0; - - if (size != 0) { - size = (size - 1) >> 14; - - while (size) { - size >>= 1; - size_reg++; - } - - size = 0x4000 << size_reg; - } - - DPD(2, "External tram size %#x %#x\n", size, size_reg); - - if (size != card->tankmem.size) { - if (card->tankmem.size > 0) { - emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 1); - - sblive_writeptr_tag(card, 0, TCB, 0, TCBS, 0, TAGLIST_END); - - pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle); - - card->tankmem.size = 0; - } - - if (size != 0) { - card->tankmem.addr = pci_alloc_consistent(card->pci_dev, size, &card->tankmem.dma_handle); - if (card->tankmem.addr == NULL) - return -ENOMEM; - - card->tankmem.size = size; - - sblive_writeptr_tag(card, 0, TCB, card->tankmem.dma_handle, TCBS, size_reg, TAGLIST_END); - - emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 0); - } - } - return 0; - break; - - default: - break; - } - - return -EINVAL; -} - -static int emu10k1_dsp_mixer(struct emu10k1_card *card, unsigned int oss_mixer, unsigned long arg) -{ - unsigned int left, right; - int val; - int scale; - - if (get_user(val, (int *)arg)) - return -EFAULT; - - /* cleanse input a little */ - right = ((val >> 8) & 0xff); - left = (val & 0xff); - - if (right > 100) right = 100; - if (left > 100) left = 100; - - card->ac97.mixer_state[oss_mixer] = (right << 8) | left; - if (oss_mixer == SOUND_MIXER_TREBLE) { - set_treble(card, left, right); - return 0; - } if (oss_mixer == SOUND_MIXER_BASS) { - set_bass(card, left, right); - return 0; - } - - if (oss_mixer == SOUND_MIXER_VOLUME) - scale = 1 << card->ac97.bit_resolution; - else - scale = volume_params[oss_mixer]; - - emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, scale); - 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); - - return 0; -} - -static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - int ret; - struct emu10k1_card *card = file->private_data; - unsigned int oss_mixer = _IOC_NR(cmd); - - ret = -EINVAL; - if (!card->isaps) { - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - - 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; - - return 0; - } - - if ((_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) && oss_mixer <= SOUND_MIXER_NRDEVICES) - ret = emu10k1_dsp_mixer(card, oss_mixer, arg); - else - ret = card->ac97.mixer_ioctl(&card->ac97, cmd, arg); - } - - if (ret < 0) - ret = emu10k1_private_mixer(card, cmd, arg); - - return ret; -} - -static int emu10k1_mixer_open(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct emu10k1_card *card = NULL; - struct list_head *entry; - - DPF(4, "emu10k1_mixer_open()\n"); - - list_for_each(entry, &emu10k1_devs) { - card = list_entry(entry, struct emu10k1_card, list); - - if (card->ac97.dev_mixer == minor) - goto match; - } - - return -ENODEV; - - match: - file->private_data = card; - return 0; -} - -static int emu10k1_mixer_release(struct inode *inode, struct file *file) -{ - DPF(4, "emu10k1_mixer_release()\n"); - return 0; -} - -struct file_operations emu10k1_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: emu10k1_mixer_ioctl, - open: emu10k1_mixer_open, - release: emu10k1_mixer_release, -}; diff -Nru a/drivers/sound/emu10k1/passthrough.c b/drivers/sound/emu10k1/passthrough.c --- a/drivers/sound/emu10k1/passthrough.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,239 +0,0 @@ -/* - ********************************************************************** - * passthrough.c -- Emu10k1 digital passthrough - * Copyright (C) 2001 Juha Yrjölä - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * May 15, 2001 Juha Yrjölä base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#define __NO_VERSION__ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hwaccess.h" -#include "cardwo.h" -#include "cardwi.h" -#include "recmgr.h" -#include "irqmgr.h" -#include "audio.h" -#include "8010.h" - -static void pt_putsamples(struct pt_data *pt, u16 *ptr, u16 left, u16 right) -{ - unsigned int idx; - - ptr[pt->copyptr] = left; - idx = pt->copyptr + PT_SAMPLES/2; - idx %= PT_SAMPLES; - ptr[idx] = right; -} - -static inline int pt_can_write(struct pt_data *pt) -{ - return pt->blocks_copied < pt->blocks_played + 8; -} - -static int pt_wait_for_write(struct emu10k1_wavedevice *wavedev, int nonblock) -{ - struct emu10k1_card *card = wavedev->card; - struct pt_data *pt = &card->pt; - - if (nonblock && !pt_can_write(pt)) - return -EAGAIN; - while (!pt_can_write(pt) && pt->state != PT_STATE_INACTIVE) { - interruptible_sleep_on(&pt->wait); - if (signal_pending(current)) - return -ERESTARTSYS; - } - if (pt->state == PT_STATE_INACTIVE) - return -EAGAIN; - - return 0; -} - -static int pt_putblock(struct emu10k1_wavedevice *wave_dev, u16 *block, int nonblock) -{ - struct woinst *woinst = wave_dev->woinst; - struct emu10k1_card *card = wave_dev->card; - struct pt_data *pt = &card->pt; - u16 *ptr = (u16 *) card->tankmem.addr; - int i = 0, r; - unsigned long flags; - - r = pt_wait_for_write(wave_dev, nonblock); - if (r < 0) - return r; - spin_lock_irqsave(&card->pt.lock, flags); - while (i < PT_BLOCKSAMPLES) { - pt_putsamples(pt, ptr, block[2*i], block[2*i+1]); - if (pt->copyptr == 0) - pt->copyptr = PT_SAMPLES; - pt->copyptr--; - i++; - } - woinst->total_copied += PT_BLOCKSIZE; - pt->blocks_copied++; - if (pt->blocks_copied >= 4 && pt->state != PT_STATE_PLAYING) { - DPF(2, "activating digital pass-through playback\n"); - sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 1); - pt->state = PT_STATE_PLAYING; - } - spin_unlock_irqrestore(&card->pt.lock, flags); - return 0; -} - -static int pt_setup(struct emu10k1_wavedevice *wave_dev) -{ - u32 bits; - struct emu10k1_card *card = wave_dev->card; - struct pt_data *pt = &card->pt; - int i; - - for (i = 0; i < 3; i++) { - pt->old_spcs[i] = sblive_readptr(card, SPCS0 + i, 0); - if (pt->spcs_to_use & (1 << i)) { - DPD(2, "using S/PDIF port %d\n", i); - bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | - SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | - 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT; - if (pt->ac3data) - bits |= SPCS_NOTAUDIODATA; - sblive_writeptr(card, SPCS0 + i, 0, bits); - } - } - return 0; -} - -ssize_t emu10k1_pt_write(struct file *file, const char *buffer, size_t count) -{ - struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; - struct emu10k1_card *card = wave_dev->card; - struct pt_data *pt = &card->pt; - int nonblock, i, r, blocks, blocks_copied, bytes_copied = 0; - - DPD(3, "emu10k1_pt_write(): %d bytes\n", count); - - nonblock = file->f_flags & O_NONBLOCK; - - if (card->tankmem.size < PT_SAMPLES*2) - return -EFAULT; - if (pt->state == PT_STATE_INACTIVE) { - DPF(2, "bufptr init\n"); - pt->playptr = PT_SAMPLES-1; - pt->copyptr = PT_INITPTR; - pt->blocks_played = pt->blocks_copied = 0; - memset(card->tankmem.addr, 0, card->tankmem.size); - pt->state = PT_STATE_ACTIVATED; - pt->buf = kmalloc(PT_BLOCKSIZE, GFP_KERNEL); - pt->prepend_size = 0; - if (pt->buf == NULL) - return -ENOMEM; - pt_setup(wave_dev); - } - if (pt->prepend_size) { - int needed = PT_BLOCKSIZE - pt->prepend_size; - - DPD(3, "prepend size %d, prepending %d bytes\n", pt->prepend_size, needed); - if (count < needed) { - copy_from_user(pt->buf + pt->prepend_size, buffer, count); - pt->prepend_size += count; - DPD(3, "prepend size now %d\n", pt->prepend_size); - return count; - } - copy_from_user(pt->buf + pt->prepend_size, buffer, needed); - r = pt_putblock(wave_dev, (u16 *) pt->buf, nonblock); - if (r) - return r; - bytes_copied += needed; - pt->prepend_size = 0; - } - blocks = (count-bytes_copied)/PT_BLOCKSIZE; - blocks_copied = 0; - while (blocks > 0) { - u16 *bufptr = (u16 *) buffer + (bytes_copied/2); - copy_from_user(pt->buf, bufptr, PT_BLOCKSIZE); - bufptr = (u16 *) pt->buf; - r = pt_putblock(wave_dev, bufptr, nonblock); - if (r) { - if (bytes_copied) - return bytes_copied; - else - return r; - } - bytes_copied += PT_BLOCKSIZE; - blocks--; - blocks_copied++; - } - i = count - bytes_copied; - if (i) { - pt->prepend_size = i; - copy_from_user(pt->buf, buffer + bytes_copied, i); - bytes_copied += i; - DPD(3, "filling prepend buffer with %d bytes", i); - } - return bytes_copied; -} - -void emu10k1_pt_stop(struct emu10k1_card *card) -{ - struct pt_data *pt = &card->pt; - int i; - - if (pt->state != PT_STATE_INACTIVE) { - DPF(2, "digital pass-through stopped\n"); - sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 0); - for (i = 0; i < 3; i++) { - if (pt->spcs_to_use & (1 << i)) - sblive_writeptr(card, SPCS0 + i, 0, pt->old_spcs[i]); - } - pt->state = PT_STATE_INACTIVE; - kfree(pt->buf); - } -} - -void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev) -{ - struct woinst *woinst = wave_dev->woinst; - struct pt_data *pt = &wave_dev->card->pt; - u32 pos; - - if (pt->state == PT_STATE_PLAYING && pt->pos_gpr >= 0) { - pos = sblive_readptr(wave_dev->card, GPR_BASE + pt->pos_gpr, 0); - if (pos > PT_BLOCKSAMPLES) - pos = PT_BLOCKSAMPLES; - pos = 4 * (PT_BLOCKSAMPLES - pos); - } else - pos = 0; - woinst->total_played = pt->blocks_played * woinst->buffer.fragment_size + pos; - woinst->buffer.hw_pos = pos; -} diff -Nru a/drivers/sound/emu10k1/passthrough.h b/drivers/sound/emu10k1/passthrough.h --- a/drivers/sound/emu10k1/passthrough.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,70 +0,0 @@ -/* - ********************************************************************** - * passthrough.h -- Emu10k1 digital passthrough header file - * Copyright (C) 2001 Juha Yrjölä - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * May 15, 2001 Juha Yrjölä base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _PASSTHROUGH_H -#define _PASSTHROUGH_H - -#include "audio.h" - -/* number of 16-bit stereo samples in XTRAM buffer */ -#define PT_SAMPLES 0x8000 -#define PT_BLOCKSAMPLES 0x400 -#define PT_BLOCKSIZE (PT_BLOCKSAMPLES*4) -#define PT_BLOCKSIZE_LOG2 12 -#define PT_BLOCKCOUNT (PT_SAMPLES/PT_BLOCKSAMPLES) -#define PT_INITPTR (PT_SAMPLES/2-1) - -#define PT_STATE_INACTIVE 0 -#define PT_STATE_ACTIVATED 1 -#define PT_STATE_PLAYING 2 - -/* passthrough struct */ -struct pt_data -{ - u8 selected, state, spcs_to_use; - int intr_gpr, enable_gpr, pos_gpr; - u32 blocks_played, blocks_copied, old_spcs[3]; - u32 playptr, copyptr; - u32 prepend_size; - u8 *buf; - u8 ac3data; - - char *patch_name, *intr_gpr_name, *enable_gpr_name, *pos_gpr_name; - - wait_queue_head_t wait; - spinlock_t lock; -}; - -ssize_t emu10k1_pt_write(struct file *file, const char *buf, size_t count); -void emu10k1_pt_stop(struct emu10k1_card *card); -void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev); - -#endif /* _PASSTHROUGH_H */ diff -Nru a/drivers/sound/emu10k1/recmgr.c b/drivers/sound/emu10k1/recmgr.c --- a/drivers/sound/emu10k1/recmgr.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,138 +0,0 @@ -/* - ********************************************************************** - * recmgr.c -- Recording manager for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include "8010.h" -#include "recmgr.h" - -void emu10k1_start_record(struct emu10k1_card *card, struct wavein_buffer *buffer) -{ - DPF(2, "emu10k1_start_record()\n"); - - sblive_writeptr(card, buffer->sizereg, 0, buffer->sizeregval); - - if (buffer->adcctl) - sblive_writeptr(card, ADCCR, 0, buffer->adcctl); - - return; -} - -void emu10k1_stop_record(struct emu10k1_card *card, struct wavein_buffer *buffer) -{ - DPF(2, "emu10k1_stop_record()\n"); - - /* Disable record transfer */ - if (buffer->adcctl) - sblive_writeptr(card, ADCCR, 0, 0); - - sblive_writeptr(card, buffer->sizereg, 0, ADCBS_BUFSIZE_NONE); - - return; -} - -void emu10k1_set_record_src(struct emu10k1_card *card, struct wiinst *wiinst) -{ - struct wavein_buffer *buffer = &wiinst->buffer; - - DPF(2, "emu10k1_set_record_src()\n"); - - switch (wiinst->recsrc) { - - case WAVERECORD_AC97: - DPF(2, "recording source: AC97\n"); - buffer->sizereg = ADCBS; - buffer->addrreg = ADCBA; - buffer->idxreg = ADCIDX_IDX; - - switch (wiinst->format.samplingrate) { - case 0xBB80: - buffer->adcctl = ADCCR_SAMPLERATE_48; - break; - case 0xAC44: - buffer->adcctl = ADCCR_SAMPLERATE_44; - break; - case 0x7D00: - buffer->adcctl = ADCCR_SAMPLERATE_32; - break; - case 0x5DC0: - buffer->adcctl = ADCCR_SAMPLERATE_24; - break; - case 0x5622: - buffer->adcctl = ADCCR_SAMPLERATE_22; - break; - case 0x3E80: - buffer->adcctl = ADCCR_SAMPLERATE_16; - break; - case 0x2B11: - buffer->adcctl = ADCCR_SAMPLERATE_11; - break; - case 0x1F40: - buffer->adcctl = ADCCR_SAMPLERATE_8; - break; - default: - BUG(); - break; - } - - buffer->adcctl |= ADCCR_LCHANENABLE; - - if (wiinst->format.channels == 2) - buffer->adcctl |= ADCCR_RCHANENABLE; - - break; - - case WAVERECORD_MIC: - DPF(2, "recording source: MIC\n"); - buffer->sizereg = MICBS; - buffer->addrreg = MICBA; - buffer->idxreg = MICIDX_IDX; - buffer->adcctl = 0; - break; - - case WAVERECORD_FX: - DPF(2, "recording source: FX\n"); - buffer->sizereg = FXBS; - buffer->addrreg = FXBA; - buffer->idxreg = FXIDX_IDX; - buffer->adcctl = 0; - - sblive_writeptr(card, FXWC, 0, wiinst->fxwc); - break; - default: - BUG(); - break; - } - - DPD(2, "bus addx: %#x\n", buffer->dma_handle); - - sblive_writeptr(card, buffer->addrreg, 0, buffer->dma_handle); - - return; -} diff -Nru a/drivers/sound/emu10k1/recmgr.h b/drivers/sound/emu10k1/recmgr.h --- a/drivers/sound/emu10k1/recmgr.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,48 +0,0 @@ -/* - ********************************************************************** - * recmgr.h - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _RECORDMGR_H -#define _RECORDMGR_H - -#include "hwaccess.h" -#include "cardwi.h" - -/* Recording resources */ -#define WAVERECORD_AC97 0x01 -#define WAVERECORD_MIC 0x02 -#define WAVERECORD_FX 0x03 - -void emu10k1_start_record(struct emu10k1_card *, struct wavein_buffer *); -void emu10k1_stop_record(struct emu10k1_card *, struct wavein_buffer *); -void emu10k1_set_record_src(struct emu10k1_card *, struct wiinst *wiinst); - - -#endif /* _RECORDMGR_H */ diff -Nru a/drivers/sound/emu10k1/timer.c b/drivers/sound/emu10k1/timer.c --- a/drivers/sound/emu10k1/timer.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,176 +0,0 @@ - -/* - ********************************************************************** - * timer.c - * Copyright (C) 1999, 2000 Creative Labs, inc. - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -/* 3/6/2000 Improved support for different timer delays Rui Sousa */ - -/* 4/3/2000 Implemented timer list using list.h Rui Sousa */ - -#include "hwaccess.h" -#include "8010.h" -#include "irqmgr.h" -#include "timer.h" - -/* Try to schedule only once per fragment */ - -void emu10k1_timer_irqhandler(struct emu10k1_card *card) -{ - struct emu_timer *t; - struct list_head *entry; - - spin_lock(&card->timer_lock); - - list_for_each(entry, &card->timers) { - t = list_entry(entry, struct emu_timer, list); - - if (t->state & TIMER_STATE_ACTIVE) { - t->count++; - if (t->count == t->count_max) { - t->count = 0; - tasklet_hi_schedule(&t->tasklet); - } - } - } - - spin_unlock(&card->timer_lock); - - return; -} - -void emu10k1_timer_install(struct emu10k1_card *card, struct emu_timer *timer, u32 delay) -{ - struct emu_timer *t; - struct list_head *entry; - unsigned long flags; - - if (delay < 5) - delay = 5; - - timer->delay = delay; - timer->state = TIMER_STATE_INSTALLED; - - spin_lock_irqsave(&card->timer_lock, flags); - - timer->count_max = timer->delay / (card->timer_delay < 1024 ? card->timer_delay : 1024); - timer->count = timer->count_max - 1; - - list_add(&timer->list, &card->timers); - - if (card->timer_delay > delay) { - if (card->timer_delay == TIMER_STOPPED) - emu10k1_irq_enable(card, INTE_INTERVALTIMERENB); - - card->timer_delay = delay; - delay = (delay < 1024 ? delay : 1024); - - emu10k1_writefn0(card, TIMER_RATE, delay); - - list_for_each(entry, &card->timers) { - t = list_entry(entry, struct emu_timer, list); - - t->count_max = t->delay / delay; - /* don't want to think much, just force scheduling - on the next interrupt */ - t->count = t->count_max - 1; - } - - DPD(2, "timer rate --> %u\n", delay); - } - - spin_unlock_irqrestore(&card->timer_lock, flags); - - return; -} - -void emu10k1_timer_uninstall(struct emu10k1_card *card, struct emu_timer *timer) -{ - struct emu_timer *t; - struct list_head *entry; - u32 delay = TIMER_STOPPED; - unsigned long flags; - - if (timer->state == TIMER_STATE_UNINSTALLED) - return; - - spin_lock_irqsave(&card->timer_lock, flags); - - list_del(&timer->list); - - list_for_each(entry, &card->timers) { - t = list_entry(entry, struct emu_timer, list); - - if (t->delay < delay) - delay = t->delay; - } - - if (card->timer_delay != delay) { - card->timer_delay = delay; - - if (delay == TIMER_STOPPED) - emu10k1_irq_disable(card, INTE_INTERVALTIMERENB); - else { - delay = (delay < 1024 ? delay : 1024); - - emu10k1_writefn0(card, TIMER_RATE, delay); - - list_for_each(entry, &card->timers) { - t = list_entry(entry, struct emu_timer, list); - - t->count_max = t->delay / delay; - t->count = t->count_max - 1; - } - } - - DPD(2, "timer rate --> %u\n", delay); - } - - spin_unlock_irqrestore(&card->timer_lock, flags); - - timer->state = TIMER_STATE_UNINSTALLED; - - return; -} - -void emu10k1_timer_enable(struct emu10k1_card *card, struct emu_timer *timer) -{ - unsigned long flags; - - spin_lock_irqsave(&card->timer_lock, flags); - timer->state |= TIMER_STATE_ACTIVE; - spin_unlock_irqrestore(&card->timer_lock, flags); - - return; -} - -void emu10k1_timer_disable(struct emu10k1_card *card, struct emu_timer *timer) -{ - unsigned long flags; - - spin_lock_irqsave(&card->timer_lock, flags); - timer->state &= ~TIMER_STATE_ACTIVE; - spin_unlock_irqrestore(&card->timer_lock, flags); - - return; -} diff -Nru a/drivers/sound/emu10k1/timer.h b/drivers/sound/emu10k1/timer.h --- a/drivers/sound/emu10k1/timer.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,54 +0,0 @@ -/* - ********************************************************************** - * timer.h - * Copyright (C) 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - - -#ifndef _TIMER_H -#define _TIMER_H - -#include -#include -#include "hwaccess.h" - -struct emu_timer -{ - struct list_head list; - struct tasklet_struct tasklet; - u8 state; - u32 count; /* current number of interrupts */ - u32 count_max; /* number of interrupts needed to schedule the bh */ - u32 delay; /* timer delay */ -}; - -void emu10k1_timer_install(struct emu10k1_card *, struct emu_timer *, u32); -void emu10k1_timer_uninstall(struct emu10k1_card *, struct emu_timer *); -void emu10k1_timer_enable(struct emu10k1_card *, struct emu_timer *); -void emu10k1_timer_disable(struct emu10k1_card *, struct emu_timer *); - -#define TIMER_STOPPED 0xffffffff -#define TIMER_STATE_INSTALLED 0x01 -#define TIMER_STATE_ACTIVE 0x02 -#define TIMER_STATE_UNINSTALLED 0x04 - -#endif /* _TIMER_H */ diff -Nru a/drivers/sound/emu10k1/voicemgr.c b/drivers/sound/emu10k1/voicemgr.c --- a/drivers/sound/emu10k1/voicemgr.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,281 +0,0 @@ -/* - ********************************************************************** - * voicemgr.c - Voice manager for emu10k1 driver - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#include "voicemgr.h" -#include "8010.h" - -int emu10k1_voice_alloc(struct emu10k1_card *card, struct emu_voice *voice) -{ - u8 *voicetable = card->voicetable; - int i; - unsigned long flags; - - DPF(2, "emu10k1_voice_alloc()\n"); - - spin_lock_irqsave(&card->lock, flags); - - if (voice->flags & VOICE_FLAGS_STEREO) { - for (i = 0; i < NUM_G; i += 2) - if ((voicetable[i] == VOICE_USAGE_FREE) && (voicetable[i + 1] == VOICE_USAGE_FREE)) { - voicetable[i] = voice->usage; - voicetable[i + 1] = voice->usage; - break; - } - } else { - for (i = 0; i < NUM_G; i++) - if (voicetable[i] == VOICE_USAGE_FREE) { - voicetable[i] = voice->usage; - break; - } - } - - spin_unlock_irqrestore(&card->lock, flags); - - if (i >= NUM_G) - return -1; - - voice->card = card; - voice->num = i; - - for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) { - DPD(2, " voice allocated -> %d\n", voice->num + i); - - sblive_writeptr_tag(card, voice->num + i, IFATN, 0xffff, - DCYSUSV, 0, - VTFT, 0x0000ffff, - PTRX, 0, - TAGLIST_END); - } - - return 0; -} - -void emu10k1_voice_free(struct emu_voice *voice) -{ - struct emu10k1_card *card = voice->card; - int i; - unsigned long flags; - - DPF(2, "emu10k1_voice_free()\n"); - - if (voice->usage == VOICE_USAGE_FREE) - return; - - for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) { - DPD(2, " voice released -> %d\n", voice->num + i); - - sblive_writeptr_tag(card, voice->num + i, DCYSUSV, 0, - VTFT, 0x0000ffff, - PTRX_PITCHTARGET, 0, - CVCF, 0x0000ffff, - CPF, 0, - TAGLIST_END); - } - - voice->usage = VOICE_USAGE_FREE; - - spin_lock_irqsave(&card->lock, flags); - - card->voicetable[voice->num] = VOICE_USAGE_FREE; - - if (voice->flags & VOICE_FLAGS_STEREO) - card->voicetable[voice->num + 1] = VOICE_USAGE_FREE; - - spin_unlock_irqrestore(&card->lock, flags); -} - -void emu10k1_voice_playback_setup(struct emu_voice *voice) -{ - struct emu10k1_card *card = voice->card; - u32 start; - int i; - - DPF(2, "emu10k1_voice_playback_setup()\n"); - - if (voice->flags & VOICE_FLAGS_STEREO) { - /* Set stereo bit */ - start = 28; - sblive_writeptr(card, CPF, voice->num, CPF_STEREO_MASK); - sblive_writeptr(card, CPF, voice->num + 1, CPF_STEREO_MASK); - } else { - start = 30; - sblive_writeptr(card, CPF, voice->num, 0); - } - - if(!(voice->flags & VOICE_FLAGS_16BIT)) - start *= 2; - - voice->start += start; - - for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) { - sblive_writeptr(card, FXRT, voice->num + i, voice->params[i].send_routing << 16); - - /* Stop CA */ - /* Assumption that PT is already 0 so no harm overwriting */ - sblive_writeptr(card, PTRX, voice->num + i, (voice->params[i].send_a << 8) | voice->params[i].send_b); - - sblive_writeptr_tag(card, voice->num + i, - /* CSL, ST, CA */ - DSL, voice->endloop | (voice->params[i].send_d << 24), - PSST, voice->startloop | (voice->params[i].send_c << 24), - CCCA, (voice->start) | CCCA_INTERPROM_0 | ((voice->flags & VOICE_FLAGS_16BIT) ? 0 : CCCA_8BITSELECT), - /* Clear filter delay memory */ - Z1, 0, - Z2, 0, - /* Invalidate maps */ - MAPA, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), - MAPB, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), - /* modulation envelope */ - CVCF, 0x0000ffff, - VTFT, 0x0000ffff, - ATKHLDM, 0, - DCYSUSM, 0x007f, - LFOVAL1, 0x8000, - LFOVAL2, 0x8000, - FMMOD, 0, - TREMFRQ, 0, - FM2FRQ2, 0, - ENVVAL, 0x8000, - /* volume envelope */ - ATKHLDV, 0x7f7f, - ENVVOL, 0x8000, - /* filter envelope */ - PEFE_FILTERAMOUNT, 0x7f, - /* pitch envelope */ - PEFE_PITCHAMOUNT, 0, TAGLIST_END); - - voice->params[i].fc_target = 0xffff; - } -} - -void emu10k1_voices_start(struct emu_voice *first_voice, unsigned int num_voices, int set) -{ - struct emu10k1_card *card = first_voice->card; - struct emu_voice *voice; - unsigned int voicenum; - int j; - - DPF(2, "emu10k1_voices_start()\n"); - - for (voicenum = 0; voicenum < num_voices; voicenum++) - { - voice = first_voice + voicenum; - - if (!set) { - u32 cra, ccis, cs, sample; - if (voice->flags & VOICE_FLAGS_STEREO) { - cra = 64; - ccis = 28; - cs = 4; - } else { - cra = 64; - ccis = 30; - cs = 2; - } - - if(voice->flags & VOICE_FLAGS_16BIT) { - sample = 0x00000000; - } else { - sample = 0x80808080; - ccis *= 2; - } - - for(j = 0; j < cs; j++) - sblive_writeptr(card, CD0 + j, voice->num, sample); - - /* Reset cache */ - sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num, 0); - if (voice->flags & VOICE_FLAGS_STEREO) - sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num + 1, 0); - - sblive_writeptr(card, CCR_READADDRESS, voice->num, cra); - - if (voice->flags & VOICE_FLAGS_STEREO) - sblive_writeptr(card, CCR_READADDRESS, voice->num + 1, cra); - - /* Fill cache */ - sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num, ccis); - } - - for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) { - sblive_writeptr_tag(card, voice->num + j, - IFATN, (voice->params[j].initial_fc << 8) | voice->params[j].initial_attn, - VTFT, (voice->params[j].volume_target << 16) | voice->params[j].fc_target, - CVCF, (voice->params[j].volume_target << 16) | voice->params[j].fc_target, - DCYSUSV, (voice->params[j].byampl_env_sustain << 8) | voice->params[j].byampl_env_decay, - TAGLIST_END); - - emu10k1_clear_stop_on_loop(card, voice->num + j); - } - } - - - for (voicenum = 0; voicenum < num_voices; voicenum++) - { - voice = first_voice + voicenum; - - for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) { - sblive_writeptr(card, PTRX_PITCHTARGET, voice->num + j, voice->pitch_target); - - if (j == 0) - sblive_writeptr(card, CPF_CURRENTPITCH, voice->num, voice->pitch_target); - - sblive_writeptr(card, IP, voice->num + j, voice->initial_pitch); - } - } -} - -void emu10k1_voices_stop(struct emu_voice *first_voice, int num_voices) -{ - struct emu10k1_card *card = first_voice->card; - struct emu_voice *voice; - unsigned int voice_num; - int j; - - DPF(2, "emu10k1_voice_stop()\n"); - - for (voice_num = 0; voice_num < num_voices; voice_num++) - { - voice = first_voice + voice_num; - - for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) { - sblive_writeptr_tag(card, voice->num + j, - PTRX_PITCHTARGET, 0, - CPF_CURRENTPITCH, 0, - IFATN, 0xffff, - VTFT, 0x0000ffff, - CVCF, 0x0000ffff, - IP, 0, - TAGLIST_END); - } - } -} - diff -Nru a/drivers/sound/emu10k1/voicemgr.h b/drivers/sound/emu10k1/voicemgr.h --- a/drivers/sound/emu10k1/voicemgr.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,91 +0,0 @@ -/* - ********************************************************************** - * sblive_voice.h -- EMU Voice Resource Manager header file - * Copyright 1999, 2000 Creative Labs, Inc. - * - ********************************************************************** - * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * - ********************************************************************** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - ********************************************************************** - */ - -#ifndef _VOICEMGR_H -#define _VOICEMGR_H - -#include "hwaccess.h" - -/* struct emu_voice.usage flags */ -#define VOICE_USAGE_FREE 0x01 -#define VOICE_USAGE_MIDI 0x02 -#define VOICE_USAGE_PLAYBACK 0x04 - -/* struct emu_voice.flags flags */ -#define VOICE_FLAGS_STEREO 0x02 -#define VOICE_FLAGS_16BIT 0x04 - -struct voice_param -{ - /* FX bus amount send */ - - u32 send_routing; - - u32 send_a; - u32 send_b; - u32 send_c; - u32 send_d; - - u32 initial_fc; - u32 fc_target; - - u32 initial_attn; - u32 volume_target; - - u32 byampl_env_sustain; - u32 byampl_env_decay; -}; - - -struct emu_voice -{ - struct emu10k1_card *card; - u8 usage; /* Free, MIDI, playback */ - u8 num; /* Voice ID */ - u8 flags; /* Stereo/mono, 8/16 bit */ - - u32 startloop; - u32 endloop; - u32 start; - - u32 initial_pitch; - u32 pitch_target; - - struct voice_param params[2]; -}; - -int emu10k1_voice_alloc(struct emu10k1_card *, struct emu_voice *); -void emu10k1_voice_free(struct emu_voice *); -void emu10k1_voice_playback_setup(struct emu_voice *); -void emu10k1_voices_start(struct emu_voice *, unsigned int, int); -void emu10k1_voices_stop(struct emu_voice *, int); - -#endif /* _VOICEMGR_H */ diff -Nru a/drivers/sound/es1370.c b/drivers/sound/es1370.c --- a/drivers/sound/es1370.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2784 +0,0 @@ -/*****************************************************************************/ - -/* - * es1370.c -- Ensoniq ES1370/Asahi Kasei AK4531 audio driver. - * - * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) - * - * 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. - * - * Special thanks to David C. Niemi - * - * - * Module command line parameters: - * joystick if 1 enables the joystick interface on the card; but it still - * needs a driver for joysticks connected to a standard IBM-PC - * joyport. It is tested with the joy-analog driver. This - * module must be loaded before the joystick driver. Kmod will - * not ensure that. - * lineout if 1 the LINE jack is used as an output instead of an input. - * LINE then contains the unmixed dsp output. This can be used - * to make the card a four channel one: use dsp to output two - * channels to LINE and dac to output the other two channels to - * SPKR. Set the mixer to only output synth to SPKR. - * micbias sets the +5V bias to the mic if using an electretmic. - * - * - * Note: sync mode is not yet supported (i.e. running dsp and dac from the same - * clock source) - * - * Supported devices: - * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible - * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible - * /dev/dsp1 additional DAC, like /dev/dsp, but output only, - * only 5512, 11025, 22050 and 44100 samples/s, - * outputs to mixer "SYNTH" setting - * /dev/midi simple MIDI UART interface, no ioctl - * - * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed - * to be done in software. That is what /dev/dac is for. By now (Q2 1998) - * there are several MIDI to PCM (WAV) packages, one of them is timidity. - * - * Revision history - * 26.03.1998 0.1 Initial release - * 31.03.1998 0.2 Fix bug in GETOSPACE - * 04.04.1998 0.3 Make it work (again) under 2.0.33 - * Fix mixer write operation not returning the actual - * settings - * 05.04.1998 0.4 First attempt at using the new PCI stuff - * 29.04.1998 0.5 Fix hang when ^C is pressed on amp - * 07.05.1998 0.6 Don't double lock around stop_*() in *_release() - * 10.05.1998 0.7 First stab at a simple midi interface (no bells&whistles) - * 14.05.1998 0.8 Don't allow excessive interrupt rates - * 08.06.1998 0.9 First release using Alan Cox' soundcore instead of - * miscdevice - * 05.07.1998 0.10 Fixed the driver to correctly maintin OSS style volume - * settings (not sure if this should be standard) - * Fixed many references: f_flags should be f_mode - * -- Gerald Britton - * 03.08.1998 0.11 Now mixer behaviour can basically be selected between - * "OSS documented" and "OSS actual" behaviour - * Fixed mixer table thanks to Hakan.Lennestal@lu.erisoft.se - * On module startup, set DAC2 to 11kSPS instead of 5.5kSPS, - * as it produces an annoying ssssh in the lower sampling rate - * Do not include modversions.h - * 22.08.1998 0.12 Mixer registers actually have 5 instead of 4 bits - * pointed out by Itai Nahshon - * 31.08.1998 0.13 Fix realplayer problems - dac.count issues - * 08.10.1998 0.14 Joystick support fixed - * -- Oliver Neukum - * 10.12.1998 0.15 Fix drain_dac trying to wait on not yet initialized DMA - * 16.12.1998 0.16 Don't wake up app until there are fragsize bytes to read/write - * 06.01.1999 0.17 remove the silly SA_INTERRUPT flag. - * hopefully killed the egcs section type conflict - * 12.03.1999 0.18 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * 22.03.1999 0.19 return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 07.04.1999 0.20 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * Note: joystick address handling might still be wrong on archs - * other than i386 - * 10.05.1999 0.21 Added support for an electret mic for SB PCI64 - * to the Linux kernel sound driver. This mod also straighten - * out the question marks around the mic impedance setting - * (micz). From Kim.Berts@fisub.mail.abb.com - * 11.05.1999 0.22 Implemented the IMIX call to mute recording monitor. - * Guenter Geiger - * 15.06.1999 0.23 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.1999 0.24 Add pci_set_master - * 02.08.1999 0.25 Added workaround for the "phantom write" bug first - * documented by Dave Sharpless from Anchor Games - * 03.08.1999 0.26 adapt to Linus' new __setup/__initcall - * added kernel command line option "es1370=joystick[,lineout[,micbias]]" - * removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge - * 12.08.1999 0.27 module_init/__setup fixes - * 19.08.1999 0.28 SOUND_MIXER_IMIX fixes, reported by Gianluca - * 31.08.1999 0.29 add spin_lock_init - * replaced current->state = x with set_current_state(x) - * 03.09.1999 0.30 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race - * 28.10.1999 0.31 More waitqueue races fixed - * 08.01.2000 0.32 Prevent some ioctl's from returning bad count values on underrun/overrun; - * Tim Janik's BSE (Bedevilled Sound Engine) found this - * 07.02.2000 0.33 Use pci_alloc_consistent and pci_register_driver - * 21.11.2000 0.34 Initialize dma buffers in poll, otherwise poll may return a bogus mask - * 12.12.2000 0.35 More dma buffer initializations, patch from - * Tjeerd Mulder - * 07.01.2001 0.36 Timeout change in wrcodec as requested by Frank Klemm - * 31.01.2001 0.37 Register/Unregister gameport - * Fix SETTRIGGER non OSS API conformity - * - * some important things missing in Ensoniq documentation: - * - * Experimental PCLKDIV results: play the same waveforms on both DAC1 and DAC2 - * and vary PCLKDIV to obtain zero beat. - * 5512sps: 254 - * 44100sps: 30 - * seems to be fs = 1411200/(PCLKDIV+2) - * - * should find out when curr_sample_ct is cleared and - * where exactly the CCB fetches data - * - * The card uses a 22.5792 MHz crystal. - * The LINEIN jack may be converted to an AOUT jack by - * setting pin 47 (XCTL0) of the ES1370 to high. - * Pin 48 (XCTL1) of the ES1370 sets the +5V bias for an electretmic - * - * - */ - -/*****************************************************************************/ - -#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 DBG(x) {} -/*#define DBG(x) {x}*/ - -/* --------------------------------------------------------------------- */ - -#ifndef PCI_VENDOR_ID_ENSONIQ -#define PCI_VENDOR_ID_ENSONIQ 0x1274 -#endif - -#ifndef PCI_DEVICE_ID_ENSONIQ_ES1370 -#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 -#endif - -#define ES1370_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1370) - -#define ES1370_EXTENT 0x40 -#define JOY_EXTENT 8 - -#define ES1370_REG_CONTROL 0x00 -#define ES1370_REG_STATUS 0x04 -#define ES1370_REG_UART_DATA 0x08 -#define ES1370_REG_UART_STATUS 0x09 -#define ES1370_REG_UART_CONTROL 0x09 -#define ES1370_REG_UART_TEST 0x0a -#define ES1370_REG_MEMPAGE 0x0c -#define ES1370_REG_CODEC 0x10 -#define ES1370_REG_SERIAL_CONTROL 0x20 -#define ES1370_REG_DAC1_SCOUNT 0x24 -#define ES1370_REG_DAC2_SCOUNT 0x28 -#define ES1370_REG_ADC_SCOUNT 0x2c - -#define ES1370_REG_DAC1_FRAMEADR 0xc30 -#define ES1370_REG_DAC1_FRAMECNT 0xc34 -#define ES1370_REG_DAC2_FRAMEADR 0xc38 -#define ES1370_REG_DAC2_FRAMECNT 0xc3c -#define ES1370_REG_ADC_FRAMEADR 0xd30 -#define ES1370_REG_ADC_FRAMECNT 0xd34 -#define ES1370_REG_PHANTOM_FRAMEADR 0xd38 -#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c - -#define ES1370_FMT_U8_MONO 0 -#define ES1370_FMT_U8_STEREO 1 -#define ES1370_FMT_S16_MONO 2 -#define ES1370_FMT_S16_STEREO 3 -#define ES1370_FMT_STEREO 1 -#define ES1370_FMT_S16 2 -#define ES1370_FMT_MASK 3 - -static const unsigned sample_size[] = { 1, 2, 2, 4 }; -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; - -#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2) -#define DAC2_DIVTOSR(x) (1411200/((x)+2)) - -#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ -#define CTRL_XCTL1 0x40000000 /* electret mic bias */ -#define CTRL_OPEN 0x20000000 /* no function, can be read and written */ -#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ -#define CTRL_SH_PCLKDIV 16 -#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ -#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */ -#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */ -#define CTRL_SH_WTSRSEL 12 -#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */ -#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ -#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */ -#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */ -#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ -#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ -#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ -#define CTRL_ADC_EN 0x00000010 /* enable ADC */ -#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ -#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */ -#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */ -#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */ - -#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ -#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */ -#define STAT_CBUSY 0x00000200 /* 1 = codec busy */ -#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */ -#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ -#define STAT_SH_VC 5 -#define STAT_MCCB 0x00000010 /* CCB int pending */ -#define STAT_UART 0x00000008 /* UART int pending */ -#define STAT_DAC1 0x00000004 /* DAC1 int pending */ -#define STAT_DAC2 0x00000002 /* DAC2 int pending */ -#define STAT_ADC 0x00000001 /* ADC int pending */ - -#define USTAT_RXINT 0x80 /* UART rx int pending */ -#define USTAT_TXINT 0x04 /* UART tx int pending */ -#define USTAT_TXRDY 0x02 /* UART tx ready */ -#define USTAT_RXRDY 0x01 /* UART rx ready */ - -#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ -#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ -#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ -#define UCTRL_CNTRL 0x03 /* control field */ -#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ - -#define SCTRL_P2ENDINC 0x00380000 /* */ -#define SCTRL_SH_P2ENDINC 19 -#define SCTRL_P2STINC 0x00070000 /* */ -#define SCTRL_SH_P2STINC 16 -#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ -#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ -#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ -#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ -#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ -#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ -#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ -#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ -#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ -#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ -#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ -#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ -#define SCTRL_R1FMT 0x00000030 /* format mask */ -#define SCTRL_SH_R1FMT 4 -#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ -#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ -#define SCTRL_P2FMT 0x0000000c /* format mask */ -#define SCTRL_SH_P2FMT 2 -#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ -#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ -#define SCTRL_P1FMT 0x00000003 /* format mask */ -#define SCTRL_SH_P1FMT 0 - -/* misc stuff */ - -#define FMODE_DAC 4 /* slight misuse of mode_t */ - -/* MIDI buffer sizes */ - -#define MIDIINBUF 256 -#define MIDIOUTBUF 256 - -#define FMODE_MIDI_SHIFT 3 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -/* --------------------------------------------------------------------- */ - -struct es1370_state { - /* magic */ - unsigned int magic; - - /* list of es1370 devices */ - struct list_head devs; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; - - /* soundcore stuff */ - int dev_audio; - int dev_mixer; - int dev_dac; - int dev_midi; - - /* hardware resources */ - unsigned long io; /* long for SPARC */ - unsigned int irq; - - /* mixer registers; there is no HW readback */ - struct { - unsigned short vol[10]; - unsigned int recsrc; - unsigned int modcnt; - unsigned short micpreamp; - unsigned int imix; - } mix; - - /* wave stuff */ - unsigned ctrl; - unsigned sctrl; - - 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; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - 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 endcleared:1; - unsigned enabled:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac1, dma_dac2, dma_adc; - - /* midi stuff */ - struct { - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t iwait; - wait_queue_head_t owait; - unsigned char ibuf[MIDIINBUF]; - unsigned char obuf[MIDIOUTBUF]; - } midi; - - struct gameport gameport; - struct semaphore sem; -}; - -/* --------------------------------------------------------------------- */ - -static LIST_HEAD(devs); - -/* - * The following buffer is used to point the phantom write channel to, - * so that it cannot wreak havoc. The attribute makes sure it doesn't - * cross a page boundary and ensures dword alignment for the DMA engine - */ -static unsigned char bugbuf[16] __attribute__ ((aligned (16))); - -/* --------------------------------------------------------------------- */ - -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; -} - -/* --------------------------------------------------------------------- */ - -static void wrcodec(struct es1370_state *s, unsigned char idx, unsigned char data) -{ - unsigned long tmo = jiffies + HZ/10, j; - - do { - j = jiffies; - if (!(inl(s->io+ES1370_REG_STATUS) & STAT_CSTAT)) { - outw((((unsigned short)idx)<<8)|data, s->io+ES1370_REG_CODEC); - return; - } - schedule(); - } while ((signed)(tmo-j) > 0); - printk(KERN_ERR "es1370: write to codec register timeout\n"); -} - -/* --------------------------------------------------------------------- */ - -static inline void stop_adc(struct es1370_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ctrl &= ~CTRL_ADC_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_dac1(struct es1370_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ctrl &= ~CTRL_DAC1_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_dac2(struct es1370_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ctrl &= ~CTRL_DAC2_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac1(struct es1370_state *s) -{ - unsigned long flags; - unsigned fragremain, fshift; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) - && s->dma_dac1.ready) { - s->ctrl |= CTRL_DAC1_EN; - s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); - fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (fragremain < 2*fshift) - fragremain = s->dma_dac1.fragsize; - outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac2(struct es1370_state *s) -{ - unsigned long flags; - unsigned fragremain, fshift; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) - && s->dma_dac2.ready) { - s->ctrl |= CTRL_DAC2_EN; - s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | - SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | - (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | - (0 << SCTRL_SH_P2STINC); - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); - fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (fragremain < 2*fshift) - fragremain = s->dma_dac2.fragsize; - outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_adc(struct es1370_state *s) -{ - unsigned long flags; - unsigned fragremain, fshift; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - s->ctrl |= CTRL_ADC_EN; - s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); - fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; - if (fragremain < 2*fshift) - fragremain = s->dma_adc.fragsize; - outl((fragremain >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -static inline void dealloc_dmabuf(struct es1370_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); - pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); - } - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - -static int prog_dmabuf(struct es1370_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) -{ - int order; - unsigned bytepersec; - unsigned bufs; - struct page *page, *pend; - - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - 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); - } - fmt &= ES1370_FMT_MASK; - 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--; - 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 & ES1370_FMT_S16) ? 0 : 0x80, db->dmasize); - outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); - outl(db->dmaaddr, s->io+(reg & 0xff)); - outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); - db->enabled = 1; - db->ready = 1; - return 0; -} - -static inline int prog_dmabuf_adc(struct es1370_state *s) -{ - stop_adc(s); - return prog_dmabuf(s, &s->dma_adc, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), - (s->sctrl >> SCTRL_SH_R1FMT) & ES1370_FMT_MASK, ES1370_REG_ADC_FRAMEADR); -} - -static inline int prog_dmabuf_dac2(struct es1370_state *s) -{ - stop_dac2(s); - return prog_dmabuf(s, &s->dma_dac2, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), - (s->sctrl >> SCTRL_SH_P2FMT) & ES1370_FMT_MASK, ES1370_REG_DAC2_FRAMEADR); -} - -static inline int prog_dmabuf_dac1(struct es1370_state *s) -{ - stop_dac1(s); - return prog_dmabuf(s, &s->dma_dac1, dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], - (s->sctrl >> SCTRL_SH_P1FMT) & ES1370_FMT_MASK, ES1370_REG_DAC1_FRAMEADR); -} - -static inline unsigned get_hwptr(struct es1370_state *s, struct dmabuf *db, unsigned reg) -{ - unsigned hwptr, diff; - - outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); - hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; - diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; - db->hwptr = hwptr; - return diff; -} - -static inline 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; - } - memset(((char *)buf) + bptr, c, len); -} - -/* call with spinlock held! */ -static void es1370_update_ptr(struct es1370_state *s) -{ - int diff; - - /* update ADC pointer */ - if (s->ctrl & CTRL_ADC_EN) { - diff = get_hwptr(s, &s->dma_adc, ES1370_REG_ADC_FRAMECNT); - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - s->ctrl &= ~CTRL_ADC_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - s->dma_adc.error++; - } - } - } - /* update DAC1 pointer */ - if (s->ctrl & CTRL_DAC1_EN) { - diff = get_hwptr(s, &s->dma_dac1, ES1370_REG_DAC1_FRAMECNT); - s->dma_dac1.total_bytes += diff; - if (s->dma_dac1.mapped) { - s->dma_dac1.count += diff; - if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) - wake_up(&s->dma_dac1.wait); - } else { - s->dma_dac1.count -= diff; - if (s->dma_dac1.count <= 0) { - s->ctrl &= ~CTRL_DAC1_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - s->dma_dac1.error++; - } else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { - clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, - s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); - s->dma_dac1.endcleared = 1; - } - if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize) - wake_up(&s->dma_dac1.wait); - } - } - /* update DAC2 pointer */ - if (s->ctrl & CTRL_DAC2_EN) { - diff = get_hwptr(s, &s->dma_dac2, ES1370_REG_DAC2_FRAMECNT); - s->dma_dac2.total_bytes += diff; - if (s->dma_dac2.mapped) { - s->dma_dac2.count += diff; - if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) - wake_up(&s->dma_dac2.wait); - } else { - s->dma_dac2.count -= diff; - if (s->dma_dac2.count <= 0) { - s->ctrl &= ~CTRL_DAC2_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - s->dma_dac2.error++; - } else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { - clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, - s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); - s->dma_dac2.endcleared = 1; - } - if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize) - wake_up(&s->dma_dac2.wait); - } - } -} - -/* hold spinlock for the following! */ -static void es1370_handle_midi(struct es1370_state *s) -{ - unsigned char ch; - int wake; - - if (!(s->ctrl & CTRL_UART_EN)) - return; - wake = 0; - while (inb(s->io+ES1370_REG_UART_STATUS) & USTAT_RXRDY) { - ch = inb(s->io+ES1370_REG_UART_DATA); - if (s->midi.icnt < MIDIINBUF) { - s->midi.ibuf[s->midi.iwr] = ch; - s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; - s->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&s->midi.iwait); - wake = 0; - while ((inb(s->io+ES1370_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { - outb(s->midi.obuf[s->midi.ord], s->io+ES1370_REG_UART_DATA); - s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; - s->midi.ocnt--; - if (s->midi.ocnt < MIDIOUTBUF-16) - wake = 1; - } - if (wake) - wake_up(&s->midi.owait); - outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1370_REG_UART_CONTROL); -} - -static void es1370_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct es1370_state *s = (struct es1370_state *)dev_id; - unsigned int intsrc, sctl; - - /* fastpath out, to ease interrupt sharing */ - intsrc = inl(s->io+ES1370_REG_STATUS); - if (!(intsrc & 0x80000000)) - return; - spin_lock(&s->lock); - /* clear audio interrupts first */ - sctl = s->sctrl; - if (intsrc & STAT_ADC) - sctl &= ~SCTRL_R1INTEN; - if (intsrc & STAT_DAC1) - sctl &= ~SCTRL_P1INTEN; - if (intsrc & STAT_DAC2) - sctl &= ~SCTRL_P2INTEN; - outl(sctl, s->io+ES1370_REG_SERIAL_CONTROL); - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - es1370_update_ptr(s); - es1370_handle_midi(s); - spin_unlock(&s->lock); -} - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT "es1370: invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != ES1370_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - -static const struct { - unsigned volidx:4; - unsigned left:4; - unsigned right:4; - unsigned stereo:1; - unsigned recmask:13; - unsigned avail:1; -} mixtable[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 }, /* master */ - [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 }, /* voice */ - [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 }, /* FM */ - [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 }, /* CD */ - [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 }, /* Line */ - [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 }, /* AUX */ - [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 }, /* Mono1 */ - [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 }, /* Mono2 */ - [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 }, /* Mic */ - [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } /* mono out */ -}; - -static void set_recsrc(struct es1370_state *s, unsigned int val) -{ - unsigned int i, j; - - for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!(val & (1 << i))) - continue; - if (!mixtable[i].recmask) { - val &= ~(1 << i); - continue; - } - j |= mixtable[i].recmask; - } - s->mix.recsrc = val; - wrcodec(s, 0x12, j & 0xd5); - wrcodec(s, 0x13, j & 0xaa); - wrcodec(s, 0x14, (j >> 8) & 0x17); - wrcodec(s, 0x15, (j >> 8) & 0x0f); - i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60; - if (!s->mix.imix) { - i &= 0xff60; /* mute record and line monitor */ - } - wrcodec(s, 0x10, i); - wrcodec(s, 0x11, i >> 8); -} - -static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - int i, val; - unsigned char l, r, rl, rr; - - VALIDATE_STATE(s); - if (cmd == SOUND_MIXER_PRIVATE1) { - /* enable/disable/query mixer preamp */ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != -1) { - s->mix.micpreamp = !!val; - wrcodec(s, 0x19, s->mix.micpreamp); - } - return put_user(s->mix.micpreamp, (int *)arg); - } - if (cmd == SOUND_MIXER_PRIVATE2) { - /* enable/disable/query use of linein as second lineout */ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != -1) { - spin_lock_irqsave(&s->lock, flags); - if (val) - s->ctrl |= CTRL_XCTL0; - else - s->ctrl &= ~CTRL_XCTL0; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user((s->ctrl & CTRL_XCTL0) ? 1 : 0, (int *)arg); - } - if (cmd == SOUND_MIXER_PRIVATE3) { - /* enable/disable/query microphone impedance setting */ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != -1) { - spin_lock_irqsave(&s->lock, flags); - if (val) - s->ctrl |= CTRL_XCTL1; - else - s->ctrl &= ~CTRL_XCTL1; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user((s->ctrl & CTRL_XCTL1) ? 1 : 0, (int *)arg); - } - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - 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; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - 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; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *)arg); - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - if (_SIOC_DIR(cmd) == _SIOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - return put_user(s->mix.recsrc, (int *)arg); - - case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ - val = SOUND_MASK_IMIX; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].avail) - val |= 1 << i; - return put_user(val, (int *)arg); - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].recmask) - val |= 1 << i; - return put_user(val, (int *)arg); - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].stereo) - val |= 1 << i; - return put_user(val, (int *)arg); - - case SOUND_MIXER_CAPS: - return put_user(0, (int *)arg); - - case SOUND_MIXER_IMIX: - return put_user(s->mix.imix, (int *)arg); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) - return -EINVAL; - return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg); - } - } - if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) - return -EINVAL; - s->mix.modcnt++; - switch (_IOC_NR(cmd)) { - - case SOUND_MIXER_IMIX: - if (get_user(s->mix.imix, (int *)arg)) - return -EFAULT; - set_recsrc(s, s->mix.recsrc); - return 0; - - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - if (get_user(val, (int *)arg)) - return -EFAULT; - set_recsrc(s, val); - return 0; - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - if (mixtable[i].stereo) { - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; - if (l < 7) { - rl = 0x80; - l = 0; - } else { - rl = 31 - ((l - 7) / 3); - l = (31 - rl) * 3 + 7; - } - if (r < 7) { - rr = 0x80; - r = 0; - } else { - rr = 31 - ((r - 7) / 3); - r = (31 - rr) * 3 + 7; - } - wrcodec(s, mixtable[i].right, rr); - } else { - if (mixtable[i].left == 15) { - if (l < 2) { - rr = rl = 0x80; - r = l = 0; - } else { - rl = 7 - ((l - 2) / 14); - r = l = (7 - rl) * 14 + 2; - } - } else { - if (l < 7) { - rl = 0x80; - r = l = 0; - } else { - rl = 31 - ((l - 7) / 3); - r = l = (31 - rl) * 3 + 7; - } - } - } - wrcodec(s, mixtable[i].left, rl); -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[mixtable[i].volidx] = ((unsigned int)r << 8) | l; -#else - s->mix.vol[mixtable[i].volidx] = val; -#endif - return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg); - } -} - -/* --------------------------------------------------------------------- */ - -static int es1370_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct list_head *list; - struct es1370_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1370_state, devs); - if (s->dev_mixer == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - return 0; -} - -static int es1370_release_mixdev(struct inode *inode, struct file *file) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - - VALIDATE_STATE(s); - return 0; -} - -static int es1370_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - return mixer_ioctl((struct es1370_state *)file->private_data, cmd, arg); -} - -static /*const*/ struct file_operations es1370_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: es1370_ioctl_mixdev, - open: es1370_open_mixdev, - release: es1370_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac1(struct es1370_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (s->dma_dac1.mapped || !s->dma_dac1.ready) - return 0; - add_wait_queue(&s->dma_dac1.wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac1.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac1.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 - / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; - tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo + 1)) - DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) - } - remove_wait_queue(&s->dma_dac1.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -static int drain_dac2(struct es1370_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (s->dma_dac2.mapped || !s->dma_dac2.ready) - return 0; - add_wait_queue(&s->dma_dac2.wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac2.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac2.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 - / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); - tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo + 1)) - DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) - } - remove_wait_queue(&s->dma_dac2.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (s->dma_adc.mapped) - return -ENXIO; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - down(&s->sem); - if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) - goto out; - - add_wait_queue(&s->dma_adc.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_adc.enabled) - start_adc(s); - 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 out; - } - down(&s->sem); - if (s->dma_adc.mapped) - { - ret = -ENXIO; - goto out; - } - continue; - } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - if (!ret) - ret = -EFAULT; - goto out; - } - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_adc.enabled) - start_adc(s); - } -out: - up(&s->sem); - remove_wait_queue(&s->dma_adc.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (s->dma_dac2.mapped) - return -ENXIO; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - down(&s->sem); - if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) - goto out; - ret = 0; - add_wait_queue(&s->dma_dac2.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac2.count < 0) { - s->dma_dac2.count = 0; - s->dma_dac2.swptr = s->dma_dac2.hwptr; - } - swptr = s->dma_dac2.swptr; - cnt = s->dma_dac2.dmasize-swptr; - if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) - cnt = s->dma_dac2.dmasize - s->dma_dac2.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_dac2.enabled) - start_dac2(s); - 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 out; - } - down(&s->sem); - if (s->dma_dac2.mapped) - { - ret = -ENXIO; - goto out; - } - continue; - } - if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - goto out; - } - swptr = (swptr + cnt) % s->dma_dac2.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac2.swptr = swptr; - s->dma_dac2.count += cnt; - s->dma_dac2.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_dac2.enabled) - start_dac2(s); - } -out: - up(&s->sem); - remove_wait_queue(&s->dma_dac2.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int es1370_poll(struct file *file, struct poll_table_struct *wait) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac2.ready && prog_dmabuf_dac2(s)) - return 0; - poll_wait(file, &s->dma_dac2.wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready && prog_dmabuf_adc(s)) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - 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_dac2.mapped) { - if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int es1370_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - struct dmabuf *db; - int ret = 0; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - down(&s->sem); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf_dac2(s)) != 0) { - goto out; - } - db = &s->dma_dac2; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf_adc(s)) != 0) { - goto out; - } - 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, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) { - ret = -EAGAIN; - goto out; - } - db->mapped = 1; -out: - up(&s->sem); - unlock_kernel(); - return ret; -} - -static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val, mapped, ret; - - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - return drain_dac2(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: - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - synchronize_irq(); - s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val >= 0) { - if (s->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) - return -EINVAL; - if (val < 4000) - val = 4000; - if (val > 50000) - val = 50000; - stop_adc(s); - stop_dac2(s); - s->dma_adc.ready = s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (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.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val) - s->sctrl |= SCTRL_R1SMB; - else - s->sctrl &= ~SCTRL_R1SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val) - s->sctrl |= SCTRL_P2SMB; - else - s->sctrl &= ~SCTRL_P2SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - 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); - s->dma_adc.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val >= 2) - s->sctrl |= SCTRL_R1SMB; - else - s->sctrl &= ~SCTRL_R1SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val >= 2) - s->sctrl |= SCTRL_P2SMB; - else - s->sctrl &= ~SCTRL_P2SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - } - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (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); - s->dma_adc.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val == AFMT_S16_LE) - s->sctrl |= SCTRL_R1SEB; - else - s->sctrl &= ~SCTRL_R1SEB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val == AFMT_S16_LE) - s->sctrl |= SCTRL_P2SEB; - else - s->sctrl &= ~SCTRL_P2SEB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - } - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? - AFMT_S16_LE : AFMT_U8, (int *)arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) - 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; - s->dma_adc.enabled = 1; - start_adc(s); - } else { - s->dma_adc.enabled = 0; - stop_adc(s); - } - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) - return ret; - s->dma_dac2.enabled = 1; - start_dac2(s); - } else { - s->dma_dac2.enabled = 0; - stop_dac2(s); - } - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - abinfo.fragsize = s->dma_dac2.fragsize; - count = s->dma_dac2.count; - if (count < 0) - count = 0; - abinfo.bytes = s->dma_dac2.dmasize - count; - abinfo.fragstotal = s->dma_dac2.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; - 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)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - count = s->dma_adc.count; - if (count < 0) - count = 0; - abinfo.bytes = 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_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - count = s->dma_dac2.count; - 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; - if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - count = s->dma_adc.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_adc.fragshift; - 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_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - cinfo.bytes = s->dma_dac2.total_bytes; - count = s->dma_dac2.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac2.fragshift; - cinfo.ptr = s->dma_dac2.hwptr; - if (s->dma_dac2.mapped) - s->dma_dac2.count &= s->dma_dac2.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_dac2(s))) - return val; - return put_user(s->dma_dac2.fragsize, (int *)arg); - } - if ((val = prog_dmabuf_adc(s))) - return val; - 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) { - 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 (file->f_mode & FMODE_WRITE) { - s->dma_dac2.ossfragshift = val & 0xffff; - s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac2.ossfragshift < 4) - s->dma_dac2.ossfragshift = 4; - if (s->dma_dac2.ossfragshift > 15) - s->dma_dac2.ossfragshift = 15; - if (s->dma_dac2.ossmaxfrags < 4) - s->dma_dac2.ossmaxfrags = 4; - } - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac2.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; - if (file->f_mode & FMODE_WRITE) - s->dma_dac2.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? - 2 : 1, (int *)arg); - - case SOUND_PCM_READ_BITS: - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? - 16 : 8, (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 es1370_open(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct list_head *list; - struct es1370_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1370_state, devs); - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - VALIDATE_STATE(s); - 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); - if (!(s->open_mode & (FMODE_READ|FMODE_WRITE))) - s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV); - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - s->dma_adc.enabled = 1; - s->sctrl &= ~SCTRL_R1FMT; - if ((minor & 0xf) == SND_DEV_DSP16) - s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_R1FMT; - else - s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_R1FMT; - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; - s->dma_dac2.enabled = 1; - s->sctrl &= ~SCTRL_P2FMT; - if ((minor & 0xf) == SND_DEV_DSP16) - s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P2FMT; - else - s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P2FMT; - } - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - up(&s->open_sem); - init_MUTEX(&s->sem); - return 0; -} - -static int es1370_release(struct inode *inode, struct file *file) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - - VALIDATE_STATE(s); - lock_kernel(); - if (file->f_mode & FMODE_WRITE) - drain_dac2(s, file->f_flags & O_NONBLOCK); - down(&s->open_sem); - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - synchronize_irq(); - dealloc_dmabuf(s, &s->dma_dac2); - } - 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); - wake_up(&s->open_wait); - up(&s->open_sem); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations es1370_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: es1370_read, - write: es1370_write, - poll: es1370_poll, - ioctl: es1370_ioctl, - mmap: es1370_mmap, - open: es1370_open, - release: es1370_release, -}; - -/* --------------------------------------------------------------------- */ - -static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (s->dma_dac1.mapped) - return -ENXIO; - if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - add_wait_queue(&s->dma_dac1.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac1.count < 0) { - s->dma_dac1.count = 0; - s->dma_dac1.swptr = s->dma_dac1.hwptr; - } - swptr = s->dma_dac1.swptr; - cnt = s->dma_dac1.dmasize-swptr; - if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) - cnt = s->dma_dac1.dmasize - s->dma_dac1.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_dac1.enabled) - start_dac1(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_dac1.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac1.swptr = swptr; - s->dma_dac1.count += cnt; - s->dma_dac1.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_dac1.enabled) - start_dac1(s); - } - remove_wait_queue(&s->dma_dac1.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int es1370_poll_dac(struct file *file, struct poll_table_struct *wait) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (!s->dma_dac1.ready && prog_dmabuf_dac1(s)) - return 0; - poll_wait(file, &s->dma_dac1.wait, wait); - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - if (s->dma_dac1.mapped) { - if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int es1370_mmap_dac(struct file *file, struct vm_area_struct *vma) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - int ret; - unsigned long size; - - VALIDATE_STATE(s); - if (!(vma->vm_flags & VM_WRITE)) - return -EINVAL; - lock_kernel(); - if ((ret = prog_dmabuf_dac1(s)) != 0) - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << s->dma_dac1.buforder)) - goto out; - ret = -EAGAIN; - if (remap_page_range(vma, vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot)) - goto out; - s->dma_dac1.mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - unsigned ctrl; - int val, ret; - - VALIDATE_STATE(s); - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_SYNC: - return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); - - case SNDCTL_DSP_SETDUPLEX: - return -EINVAL; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); - - case SNDCTL_DSP_RESET: - stop_dac1(s); - synchronize_irq(); - s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val >= 0) { - stop_dac1(s); - s->dma_dac1.ready = 0; - for (ctrl = 0; ctrl <= 2; ctrl++) - if (val < (dac1_samplerate[ctrl] + dac1_samplerate[ctrl+1]) / 2) - break; - spin_lock_irqsave(&s->lock, flags); - s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (ctrl << CTRL_SH_WTSRSEL); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - stop_dac1(s); - s->dma_dac1.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val) - s->sctrl |= SCTRL_P1SMB; - else - s->sctrl &= ~SCTRL_P1SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) { - if (s->dma_dac1.mapped) - return -EINVAL; - stop_dac1(s); - s->dma_dac1.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val >= 2) - s->sctrl |= SCTRL_P1SMB; - else - s->sctrl &= ~SCTRL_P1SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (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) { - stop_dac1(s); - s->dma_dac1.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val == AFMT_S16_LE) - s->sctrl |= SCTRL_P1SEB; - else - s->sctrl &= ~SCTRL_P1SEB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) - return ret; - s->dma_dac1.enabled = 1; - start_dac1(s); - } else { - s->dma_dac1.enabled = 0; - stop_dac1(s); - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - abinfo.fragsize = s->dma_dac1.fragsize; - count = s->dma_dac1.count; - if (count < 0) - count = 0; - abinfo.bytes = s->dma_dac1.dmasize - count; - abinfo.fragstotal = s->dma_dac1.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac1.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 (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - count = s->dma_dac1.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - return put_user(count, (int *)arg); - - case SNDCTL_DSP_GETOPTR: - if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - cinfo.bytes = s->dma_dac1.total_bytes; - count = s->dma_dac1.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac1.fragshift; - cinfo.ptr = s->dma_dac1.hwptr; - if (s->dma_dac1.mapped) - s->dma_dac1.count &= s->dma_dac1.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); - - case SNDCTL_DSP_GETBLKSIZE: - if ((val = prog_dmabuf_dac1(s))) - return val; - return put_user(s->dma_dac1.fragsize, (int *)arg); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) - return -EFAULT; - s->dma_dac1.ossfragshift = val & 0xffff; - s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac1.ossfragshift < 4) - s->dma_dac1.ossfragshift = 4; - if (s->dma_dac1.ossfragshift > 15) - s->dma_dac1.ossfragshift = 15; - if (s->dma_dac1.ossmaxfrags < 4) - s->dma_dac1.ossmaxfrags = 4; - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if (s->dma_dac1.subdivision) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - s->dma_dac1.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); - - case SOUND_PCM_READ_BITS: - return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, (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 es1370_open_dac(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct list_head *list; - struct es1370_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1370_state, devs); - if (!((s->dev_dac ^ minor) & ~0xf)) - break; - } - VALIDATE_STATE(s); - /* we allow opening with O_RDWR, most programs do it although they will only write */ -#if 0 - if (file->f_mode & FMODE_READ) - return -EPERM; -#endif - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & FMODE_DAC) { - 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); - } - s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; - s->dma_dac1.enabled = 1; - spin_lock_irqsave(&s->lock, flags); - s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (1 << CTRL_SH_WTSRSEL); - s->sctrl &= ~SCTRL_P1FMT; - if ((minor & 0xf) == SND_DEV_DSP16) - s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P1FMT; - else - s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P1FMT; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= FMODE_DAC; - up(&s->open_sem); - return 0; -} - -static int es1370_release_dac(struct inode *inode, struct file *file) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - - VALIDATE_STATE(s); - lock_kernel(); - drain_dac1(s, file->f_flags & O_NONBLOCK); - down(&s->open_sem); - stop_dac1(s); - dealloc_dmabuf(s, &s->dma_dac1); - s->open_mode &= ~FMODE_DAC; - wake_up(&s->open_wait); - up(&s->open_sem); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations es1370_dac_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - write: es1370_write_dac, - poll: es1370_poll_dac, - ioctl: es1370_ioctl_dac, - mmap: es1370_mmap_dac, - open: es1370_open_dac, - release: es1370_release_dac, -}; - -/* --------------------------------------------------------------------- */ - -static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.iwait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.ird; - cnt = MIDIINBUF - ptr; - if (s->midi.icnt < cnt) - cnt = s->midi.icnt; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIINBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.ird = ptr; - s->midi.icnt -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - break; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.iwait, &wait); - return ret; -} - -static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.owait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.owr; - cnt = MIDIOUTBUF - ptr; - if (s->midi.ocnt + cnt > MIDIOUTBUF) - cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) { - __set_current_state(TASK_INTERRUPTIBLE); - es1370_handle_midi(s); - } - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIOUTBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.owr = ptr; - s->midi.ocnt += cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&s->lock, flags); - es1370_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.owait, &wait); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int es1370_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &s->midi.owait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &s->midi.iwait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - if (s->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->midi.ocnt < MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int es1370_midi_open(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct list_head *list; - struct es1370_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1370_state, devs); - if (s->dev_midi == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - 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); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - outb(UCTRL_CNTRL_SWR, s->io+ES1370_REG_UART_CONTROL); - outb(0, s->io+ES1370_REG_UART_CONTROL); - outb(0, s->io+ES1370_REG_UART_TEST); - } - if (file->f_mode & FMODE_READ) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - } - s->ctrl |= CTRL_UART_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - es1370_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); - up(&s->open_sem); - return 0; -} - -static int es1370_midi_release(struct inode *inode, struct file *file) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - VALIDATE_STATE(s); - - lock_kernel(); - if (file->f_mode & FMODE_WRITE) { - add_wait_queue(&s->midi.owait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->midi.ocnt; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - 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; - } - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");) - } - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - } - down(&s->open_sem); - s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->ctrl &= ~CTRL_UART_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - } - spin_unlock_irqrestore(&s->lock, flags); - wake_up(&s->open_wait); - up(&s->open_sem); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations es1370_midi_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: es1370_midi_read, - write: es1370_midi_write, - poll: es1370_midi_poll, - open: es1370_midi_open, - release: es1370_midi_release, -}; - -/* --------------------------------------------------------------------- */ - -/* maximum number of devices; only used for command line params */ -#define NR_DEVICE 5 - -static int joystick[NR_DEVICE] = { 0, }; -static int lineout[NR_DEVICE] = { 0, }; -static int micbias[NR_DEVICE] = { 0, }; - -static unsigned int devindex = 0; - -MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)"); -MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); -MODULE_PARM(micbias, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("ES1370 AudioPCI Driver"); -MODULE_LICENSE("GPL"); - - -/* --------------------------------------------------------------------- */ - -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_LINE2, 0x4040 }, - { SOUND_MIXER_WRITE_LINE3, 0x4040 }, - { SOUND_MIXER_WRITE_MIC, 0x4040 }, - { SOUND_MIXER_WRITE_OGAIN, 0x4040 } -}; - -static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) -{ - struct es1370_state *s; - mm_segment_t fs; - int i, val, ret; - - if ((ret=pci_enable_device(pcidev))) - return ret; - - if ( !(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) || - !pci_resource_start(pcidev, 0) - ) - return -ENODEV; - if (pcidev->irq == 0) - return -ENODEV; - i = pci_set_dma_mask(pcidev, 0xffffffff); - if (i) { - printk(KERN_WARNING "es1370: architecture does not support 32bit PCI busmaster DMA\n"); - return i; - } - if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) { - printk(KERN_WARNING "es1370: out of memory\n"); - return -ENOMEM; - } - memset(s, 0, sizeof(struct es1370_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac1.wait); - init_waitqueue_head(&s->dma_dac2.wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->midi.iwait); - init_waitqueue_head(&s->midi.owait); - init_MUTEX(&s->open_sem); - spin_lock_init(&s->lock); - s->magic = ES1370_MAGIC; - s->dev = pcidev; - s->io = pci_resource_start(pcidev, 0); - s->irq = pcidev->irq; - if (!request_region(s->io, ES1370_EXTENT, "es1370")) { - printk(KERN_ERR "es1370: io ports %#lx-%#lx in use\n", s->io, s->io+ES1370_EXTENT-1); - ret = -EBUSY; - goto err_region; - } - if ((ret=request_irq(s->irq, es1370_interrupt, SA_SHIRQ, "es1370",s))) { - printk(KERN_ERR "es1370: irq %u in use\n", s->irq); - goto err_irq; - } - - /* initialize codec registers */ - /* note: setting CTRL_SERR_DIS is reported to break - * mic bias setting (by Kim.Berts@fisub.mail.abb.com) */ - s->ctrl = CTRL_CDC_EN | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL); - s->gameport.io = 0; - if (joystick[devindex]) { - if (!request_region(0x200, JOY_EXTENT, "es1370")) - printk(KERN_ERR "es1370: joystick io port 0x200 in use\n"); - else { - s->ctrl |= CTRL_JYSTK_EN; - s->gameport.io = 0x200; - } - } - if (lineout[devindex]) - s->ctrl |= CTRL_XCTL0; - if (micbias[devindex]) - s->ctrl |= CTRL_XCTL1; - s->sctrl = 0; - printk(KERN_INFO "es1370: found adapter at io %#lx irq %u\n" - KERN_INFO "es1370: features: joystick %s, line %s, mic impedance %s\n", - s->io, s->irq, (s->ctrl & CTRL_JYSTK_EN) ? "on" : "off", - (s->ctrl & CTRL_XCTL0) ? "out" : "in", - (s->ctrl & CTRL_XCTL1) ? "1" : "0"); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1)) < 0) { - ret = s->dev_audio; - goto err_dev1; - } - if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1)) < 0) { - ret = s->dev_mixer; - goto err_dev2; - } - if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1)) < 0) { - ret = s->dev_dac; - goto err_dev3; - } - if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1)) < 0) { - ret = s->dev_midi; - goto err_dev4; - } - /* initialize the chips */ - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - /* point phantom write channel to "bugbuf" */ - outl((ES1370_REG_PHANTOM_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE); - outl(virt_to_bus(bugbuf), s->io+(ES1370_REG_PHANTOM_FRAMEADR & 0xff)); - outl(0, s->io+(ES1370_REG_PHANTOM_FRAMECNT & 0xff)); - pci_set_master(pcidev); /* enable bus mastering */ - wrcodec(s, 0x16, 3); /* no RST, PD */ - wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ - wrcodec(s, 0x18, 0); /* recording source is mixer */ - wrcodec(s, 0x19, s->mix.micpreamp = 1); /* turn on MIC preamp */ - s->mix.imix = 1; - fs = get_fs(); - set_fs(KERNEL_DS); - val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD; - 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); - } - set_fs(fs); - /* register gameport */ - gameport_register_port(&s->gameport); - - /* store it in the driver field */ - pci_set_drvdata(pcidev, s); - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - /* increment devindex */ - if (devindex < NR_DEVICE-1) - devindex++; - return 0; - - err_dev4: - unregister_sound_dsp(s->dev_dac); - err_dev3: - unregister_sound_mixer(s->dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR "es1370: cannot register misc device\n"); - free_irq(s->irq, s); - if (s->gameport.io) - release_region(s->gameport.io, JOY_EXTENT); - err_irq: - release_region(s->io, ES1370_EXTENT); - err_region: - kfree(s); - return ret; -} - -static void __devinit es1370_remove(struct pci_dev *dev) -{ - struct es1370_state *s = pci_get_drvdata(dev); - - if (!s) - return; - list_del(&s->devs); - outl(CTRL_SERR_DIS | (1 << CTRL_SH_WTSRSEL), s->io+ES1370_REG_CONTROL); /* switch everything off */ - outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */ - synchronize_irq(); - free_irq(s->irq, s); - if (s->gameport.io) { - gameport_unregister_port(&s->gameport); - release_region(s->gameport.io, JOY_EXTENT); - } - release_region(s->io, ES1370_EXTENT); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->dev_mixer); - unregister_sound_dsp(s->dev_dac); - unregister_sound_midi(s->dev_midi); - kfree(s); - pci_set_drvdata(dev, NULL); -} - -static struct pci_device_id id_table[] __devinitdata = { - { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver es1370_driver = { - name: "es1370", - id_table: id_table, - probe: es1370_probe, - remove: es1370_remove -}; - -static int __init init_es1370(void) -{ - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - printk(KERN_INFO "es1370: version v0.37 time " __TIME__ " " __DATE__ "\n"); - return pci_module_init(&es1370_driver); -} - -static void __exit cleanup_es1370(void) -{ - printk(KERN_INFO "es1370: unloading\n"); - pci_unregister_driver(&es1370_driver); -} - -module_init(init_es1370); -module_exit(cleanup_es1370); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* format is: es1370=[joystick[,lineout[,micbias]]] */ - -static int __init es1370_setup(char *str) -{ - static unsigned __initdata nr_dev = 0; - - if (nr_dev >= NR_DEVICE) - return 0; - - (void) - ( (get_option(&str,&joystick[nr_dev]) == 2) - && (get_option(&str,&lineout [nr_dev]) == 2) - && get_option(&str,&micbias [nr_dev]) - ); - - nr_dev++; - return 1; -} - -__setup("es1370=", es1370_setup); - -#endif /* MODULE */ diff -Nru a/drivers/sound/es1371.c b/drivers/sound/es1371.c --- a/drivers/sound/es1371.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3084 +0,0 @@ -/*****************************************************************************/ - -/* - * es1371.c -- Creative Ensoniq ES1371. - * - * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) - * - * 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. - * - * Special thanks to Ensoniq - * - * - * Module command line parameters: - * joystick must be set to the base I/O-Port to be used for - * the gameport. Legal values are 0x200, 0x208, 0x210 and 0x218. - * The gameport is mirrored eight times. - * - * Supported devices: - * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible - * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible - * /dev/dsp1 additional DAC, like /dev/dsp, but outputs to mixer "SYNTH" setting - * /dev/midi simple MIDI UART interface, no ioctl - * - * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed - * to be done in software. That is what /dev/dac is for. By now (Q2 1998) - * there are several MIDI to PCM (WAV) packages, one of them is timidity. - * - * Revision history - * 04.06.1998 0.1 Initial release - * Mixer stuff should be overhauled; especially optional AC97 mixer bits - * should be detected. This results in strange behaviour of some mixer - * settings, like master volume and mic. - * 08.06.1998 0.2 First release using Alan Cox' soundcore instead of miscdevice - * 03.08.1998 0.3 Do not include modversions.h - * Now mixer behaviour can basically be selected between - * "OSS documented" and "OSS actual" behaviour - * 31.08.1998 0.4 Fix realplayer problems - dac.count issues - * 27.10.1998 0.5 Fix joystick support - * -- Oliver Neukum (c188@org.chemie.uni-muenchen.de) - * 10.12.1998 0.6 Fix drain_dac trying to wait on not yet initialized DMA - * 23.12.1998 0.7 Fix a few f_file & FMODE_ bugs - * Don't wake up app until there are fragsize bytes to read/write - * 06.01.1999 0.8 remove the silly SA_INTERRUPT flag. - * hopefully killed the egcs section type conflict - * 12.03.1999 0.9 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * 22.03.1999 0.10 return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 07.04.1999 0.11 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * Another Alpha fix (wait_src_ready in init routine) - * reported by "Ivan N. Kokshaysky" - * Note: joystick address handling might still be wrong on archs - * other than i386 - * 15.06.1999 0.12 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.1999 0.13 Add pci_set_master - * 03.08.1999 0.14 adapt to Linus' new __setup/__initcall - * added kernel command line option "es1371=joystickaddr" - * removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge - * 10.08.1999 0.15 (Re)added S/PDIF module option for cards revision >= 4. - * Initial version by Dave Platt . - * module_init/__setup fixes - * 08.16.1999 0.16 Joe Cotellese - * Added detection for ES1371 revision ID so that we can - * detect the ES1373 and later parts. - * added AC97 #defines for readability - * added a /proc file system for dumping hardware state - * updated SRC and CODEC w/r functions to accomodate bugs - * in some versions of the ES137x chips. - * 31.08.1999 0.17 add spin_lock_init - * replaced current->state = x with set_current_state(x) - * 03.09.1999 0.18 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race - * 21.10.1999 0.19 Round sampling rates, requested by - * Kasamatsu Kenichi - * 27.10.1999 0.20 Added SigmaTel 3D enhancement string - * Codec ID printing changes - * 28.10.1999 0.21 More waitqueue races fixed - * Joe Cotellese - * Changed PCI detection routine so we can more easily - * detect ES137x chip and derivatives. - * 05.01.2000 0.22 Should now work with rev7 boards; patch by - * Eric Lemar, elemar@cs.washington.edu - * 08.01.2000 0.23 Prevent some ioctl's from returning bad count values on underrun/overrun; - * Tim Janik's BSE (Bedevilled Sound Engine) found this - * 07.02.2000 0.24 Use pci_alloc_consistent and pci_register_driver - * 07.02.2000 0.25 Use ac97_codec - * 01.03.2000 0.26 SPDIF patch by Mikael Bouillot - * Use pci_module_init - * 21.11.2000 0.27 Initialize dma buffers in poll, otherwise poll may return a bogus mask - * 12.12.2000 0.28 More dma buffer initializations, patch from - * Tjeerd Mulder - * 05.01.2001 0.29 Hopefully updates will not be required anymore when Creative bumps - * the CT5880 revision. - * suggested by Stephan Müller - * 31.01.2001 0.30 Register/Unregister gameport - * Fix SETTRIGGER non OSS API conformity - * 14.07.2001 0.31 Add list of laptops needing amplifier control - */ - -/*****************************************************************************/ - -#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 ES1371_DEBUG -#define DBG(x) {} -/*#define DBG(x) {x}*/ - -/* --------------------------------------------------------------------- */ - -#ifndef PCI_VENDOR_ID_ENSONIQ -#define PCI_VENDOR_ID_ENSONIQ 0x1274 -#endif - -#ifndef PCI_VENDOR_ID_ECTIVA -#define PCI_VENDOR_ID_ECTIVA 0x1102 -#endif - -#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371 -#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 -#endif - -#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880 -#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 -#endif - -#ifndef PCI_DEVICE_ID_ECTIVA_EV1938 -#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938 -#endif - -/* ES1371 chip ID */ -/* This is a little confusing because all ES1371 compatible chips have the - same DEVICE_ID, the only thing differentiating them is the REV_ID field. - This is only significant if you want to enable features on the later parts. - Yes, I know it's stupid and why didn't we use the sub IDs? -*/ -#define ES1371REV_ES1373_A 0x04 -#define ES1371REV_ES1373_B 0x06 -#define ES1371REV_CT5880_A 0x07 -#define CT5880REV_CT5880_C 0x02 -#define CT5880REV_CT5880_D 0x03 -#define ES1371REV_ES1371_B 0x09 -#define EV1938REV_EV1938_A 0x00 -#define ES1371REV_ES1373_8 0x08 - -#define ES1371_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371) - -#define ES1371_EXTENT 0x40 -#define JOY_EXTENT 8 - -#define ES1371_REG_CONTROL 0x00 -#define ES1371_REG_STATUS 0x04 /* on the 5880 it is control/status */ -#define ES1371_REG_UART_DATA 0x08 -#define ES1371_REG_UART_STATUS 0x09 -#define ES1371_REG_UART_CONTROL 0x09 -#define ES1371_REG_UART_TEST 0x0a -#define ES1371_REG_MEMPAGE 0x0c -#define ES1371_REG_SRCONV 0x10 -#define ES1371_REG_CODEC 0x14 -#define ES1371_REG_LEGACY 0x18 -#define ES1371_REG_SERIAL_CONTROL 0x20 -#define ES1371_REG_DAC1_SCOUNT 0x24 -#define ES1371_REG_DAC2_SCOUNT 0x28 -#define ES1371_REG_ADC_SCOUNT 0x2c - -#define ES1371_REG_DAC1_FRAMEADR 0xc30 -#define ES1371_REG_DAC1_FRAMECNT 0xc34 -#define ES1371_REG_DAC2_FRAMEADR 0xc38 -#define ES1371_REG_DAC2_FRAMECNT 0xc3c -#define ES1371_REG_ADC_FRAMEADR 0xd30 -#define ES1371_REG_ADC_FRAMECNT 0xd34 - -#define ES1371_FMT_U8_MONO 0 -#define ES1371_FMT_U8_STEREO 1 -#define ES1371_FMT_S16_MONO 2 -#define ES1371_FMT_S16_STEREO 3 -#define ES1371_FMT_STEREO 1 -#define ES1371_FMT_S16 2 -#define ES1371_FMT_MASK 3 - -static const unsigned sample_size[] = { 1, 2, 2, 4 }; -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -#define CTRL_RECEN_B 0x08000000 /* 1 = don't mix analog in to digital out */ -#define CTRL_SPDIFEN_B 0x04000000 -#define CTRL_JOY_SHIFT 24 -#define CTRL_JOY_MASK 3 -#define CTRL_JOY_200 0x00000000 /* joystick base address */ -#define CTRL_JOY_208 0x01000000 -#define CTRL_JOY_210 0x02000000 -#define CTRL_JOY_218 0x03000000 -#define CTRL_GPIO_IN0 0x00100000 /* general purpose inputs/outputs */ -#define CTRL_GPIO_IN1 0x00200000 -#define CTRL_GPIO_IN2 0x00400000 -#define CTRL_GPIO_IN3 0x00800000 -#define CTRL_GPIO_OUT0 0x00010000 -#define CTRL_GPIO_OUT1 0x00020000 -#define CTRL_GPIO_OUT2 0x00040000 -#define CTRL_GPIO_OUT3 0x00080000 -#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ -#define CTRL_SYNCRES 0x00004000 /* AC97 warm reset */ -#define CTRL_ADCSTOP 0x00002000 /* stop ADC transfers */ -#define CTRL_PWR_INTRM 0x00001000 /* 1 = power level ints enabled */ -#define CTRL_M_CB 0x00000800 /* recording source: 0 = ADC, 1 = MPEG */ -#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ -#define CTRL_PDLEV0 0x00000000 /* power down level */ -#define CTRL_PDLEV1 0x00000100 -#define CTRL_PDLEV2 0x00000200 -#define CTRL_PDLEV3 0x00000300 -#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ -#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ -#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ -#define CTRL_ADC_EN 0x00000010 /* enable ADC */ -#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ -#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port */ -#define CTRL_XTALCLKDIS 0x00000002 /* 1 = disable crystal clock input */ -#define CTRL_PCICLKDIS 0x00000001 /* 1 = disable PCI clock distribution */ - - -#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ -#define CSTAT_5880_AC97_RST 0x20000000 /* CT5880 Reset bit */ -#define STAT_EN_SPDIF 0x00040000 /* enable S/PDIF circuitry */ -#define STAT_TS_SPDIF 0x00020000 /* test S/PDIF circuitry */ -#define STAT_TESTMODE 0x00010000 /* test ASIC */ -#define STAT_SYNC_ERR 0x00000100 /* 1 = codec sync error */ -#define STAT_VC 0x000000c0 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ -#define STAT_SH_VC 6 -#define STAT_MPWR 0x00000020 /* power level interrupt */ -#define STAT_MCCB 0x00000010 /* CCB int pending */ -#define STAT_UART 0x00000008 /* UART int pending */ -#define STAT_DAC1 0x00000004 /* DAC1 int pending */ -#define STAT_DAC2 0x00000002 /* DAC2 int pending */ -#define STAT_ADC 0x00000001 /* ADC int pending */ - -#define USTAT_RXINT 0x80 /* UART rx int pending */ -#define USTAT_TXINT 0x04 /* UART tx int pending */ -#define USTAT_TXRDY 0x02 /* UART tx ready */ -#define USTAT_RXRDY 0x01 /* UART rx ready */ - -#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ -#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ -#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ -#define UCTRL_CNTRL 0x03 /* control field */ -#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ - -/* sample rate converter */ -#define SRC_OKSTATE 1 - -#define SRC_RAMADDR_MASK 0xfe000000 -#define SRC_RAMADDR_SHIFT 25 -#define SRC_DAC1FREEZE (1UL << 21) -#define SRC_DAC2FREEZE (1UL << 20) -#define SRC_ADCFREEZE (1UL << 19) - - -#define SRC_WE 0x01000000 /* read/write control for SRC RAM */ -#define SRC_BUSY 0x00800000 /* SRC busy */ -#define SRC_DIS 0x00400000 /* 1 = disable SRC */ -#define SRC_DDAC1 0x00200000 /* 1 = disable accum update for DAC1 */ -#define SRC_DDAC2 0x00100000 /* 1 = disable accum update for DAC2 */ -#define SRC_DADC 0x00080000 /* 1 = disable accum update for ADC2 */ -#define SRC_CTLMASK 0x00780000 -#define SRC_RAMDATA_MASK 0x0000ffff -#define SRC_RAMDATA_SHIFT 0 - -#define SRCREG_ADC 0x78 -#define SRCREG_DAC1 0x70 -#define SRCREG_DAC2 0x74 -#define SRCREG_VOL_ADC 0x6c -#define SRCREG_VOL_DAC1 0x7c -#define SRCREG_VOL_DAC2 0x7e - -#define SRCREG_TRUNC_N 0x00 -#define SRCREG_INT_REGS 0x01 -#define SRCREG_ACCUM_FRAC 0x02 -#define SRCREG_VFREQ_FRAC 0x03 - -#define CODEC_PIRD 0x00800000 /* 0 = write AC97 register */ -#define CODEC_PIADD_MASK 0x007f0000 -#define CODEC_PIADD_SHIFT 16 -#define CODEC_PIDAT_MASK 0x0000ffff -#define CODEC_PIDAT_SHIFT 0 - -#define CODEC_RDY 0x80000000 /* AC97 read data valid */ -#define CODEC_WIP 0x40000000 /* AC97 write in progress */ -#define CODEC_PORD 0x00800000 /* 0 = write AC97 register */ -#define CODEC_POADD_MASK 0x007f0000 -#define CODEC_POADD_SHIFT 16 -#define CODEC_PODAT_MASK 0x0000ffff -#define CODEC_PODAT_SHIFT 0 - - -#define LEGACY_JFAST 0x80000000 /* fast joystick timing */ -#define LEGACY_FIRQ 0x01000000 /* force IRQ */ - -#define SCTRL_DACTEST 0x00400000 /* 1 = DAC test, test vector generation purposes */ -#define SCTRL_P2ENDINC 0x00380000 /* */ -#define SCTRL_SH_P2ENDINC 19 -#define SCTRL_P2STINC 0x00070000 /* */ -#define SCTRL_SH_P2STINC 16 -#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ -#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ -#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ -#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ -#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ -#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ -#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ -#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ -#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ -#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ -#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ -#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ -#define SCTRL_R1FMT 0x00000030 /* format mask */ -#define SCTRL_SH_R1FMT 4 -#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ -#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ -#define SCTRL_P2FMT 0x0000000c /* format mask */ -#define SCTRL_SH_P2FMT 2 -#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ -#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ -#define SCTRL_P1FMT 0x00000003 /* format mask */ -#define SCTRL_SH_P1FMT 0 - - -/* misc stuff */ -#define POLL_COUNT 0x1000 -#define FMODE_DAC 4 /* slight misuse of mode_t */ - -/* MIDI buffer sizes */ - -#define MIDIINBUF 256 -#define MIDIOUTBUF 256 - -#define FMODE_MIDI_SHIFT 3 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -#define ES1371_MODULE_NAME "es1371" -#define PFX ES1371_MODULE_NAME ": " - -/* --------------------------------------------------------------------- */ - -struct es1371_state { - /* magic */ - unsigned int magic; - - /* list of es1371 devices */ - struct list_head devs; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; - - /* soundcore stuff */ - int dev_audio; - int dev_dac; - int dev_midi; - - /* hardware resources */ - unsigned long io; /* long for SPARC */ - 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 */ - -#ifdef ES1371_DEBUG - /* debug /proc entry */ - struct proc_dir_entry *ps; -#endif /* ES1371_DEBUG */ - - struct ac97_codec codec; - - /* wave stuff */ - unsigned ctrl; - unsigned sctrl; - unsigned dac1rate, dac2rate, 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; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - 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 endcleared:1; - unsigned enabled:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac1, dma_dac2, dma_adc; - - /* midi stuff */ - struct { - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t iwait; - wait_queue_head_t owait; - unsigned char ibuf[MIDIINBUF]; - unsigned char obuf[MIDIOUTBUF]; - } midi; - - struct gameport gameport; - struct semaphore sem; -}; - -/* --------------------------------------------------------------------- */ - -static LIST_HEAD(devs); - -/* --------------------------------------------------------------------- */ - -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; -} - -/* --------------------------------------------------------------------- */ - -static unsigned wait_src_ready(struct es1371_state *s) -{ - unsigned int t, r; - - for (t = 0; t < POLL_COUNT; t++) { - if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY)) - return r; - udelay(1); - } - printk(KERN_DEBUG PFX "sample rate converter timeout r = 0x%08x\n", r); - return r; -} - -static unsigned src_read(struct es1371_state *s, unsigned reg) -{ - unsigned int temp,i,orig; - - /* wait for ready */ - temp = wait_src_ready (s); - - /* we can only access the SRC at certain times, make sure - we're allowed to before we read */ - - orig = temp; - /* expose the SRC state bits */ - outl ( (temp & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT) | 0x10000UL, - s->io + ES1371_REG_SRCONV); - - /* now, wait for busy and the correct time to read */ - temp = wait_src_ready (s); - - if ( (temp & 0x00870000UL ) != ( SRC_OKSTATE << 16 )){ - /* wait for the right state */ - for (i=0; iio + ES1371_REG_SRCONV); - if ( (temp & 0x00870000UL ) == ( SRC_OKSTATE << 16 )) - break; - } - } - - /* hide the state bits */ - outl ((orig & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT), s->io + ES1371_REG_SRCONV); - return temp; - - -} - -static void src_write(struct es1371_state *s, unsigned reg, unsigned data) -{ - - unsigned int r; - - r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); - r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; - r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK; - outl(r | SRC_WE, s->io + ES1371_REG_SRCONV); - -} - -/* --------------------------------------------------------------------- */ - -/* most of the following here is black magic */ -static void set_adc_rate(struct es1371_state *s, unsigned rate) -{ - unsigned long flags; - unsigned int n, truncm, freq; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - n = rate / 3000; - if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) - n--; - truncm = (21 * n - 1) | 1; - freq = ((48000UL << 15) / rate) * n; - s->adcrate = (48000UL << 15) / (freq / n); - spin_lock_irqsave(&s->lock, flags); - if (rate >= 24000) { - if (truncm > 239) - truncm = 239; - src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, - (((239 - truncm) >> 1) << 9) | (n << 4)); - } else { - if (truncm > 119) - truncm = 119; - src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, - 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); - } - src_write(s, SRCREG_ADC+SRCREG_INT_REGS, - (src_read(s, SRCREG_ADC+SRCREG_INT_REGS) & 0x00ff) | - ((freq >> 5) & 0xfc00)); - src_write(s, SRCREG_ADC+SRCREG_VFREQ_FRAC, freq & 0x7fff); - src_write(s, SRCREG_VOL_ADC, n << 8); - src_write(s, SRCREG_VOL_ADC+1, n << 8); - spin_unlock_irqrestore(&s->lock, flags); -} - - -static void set_dac1_rate(struct es1371_state *s, unsigned rate) -{ - unsigned long flags; - unsigned int freq, r; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - freq = ((rate << 15) + 1500) / 3000; - s->dac1rate = (freq * 3000 + 16384) >> 15; - spin_lock_irqsave(&s->lock, flags); - r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1; - outl(r, s->io + ES1371_REG_SRCONV); - src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, - (src_read(s, SRCREG_DAC1+SRCREG_INT_REGS) & 0x00ff) | - ((freq >> 5) & 0xfc00)); - src_write(s, SRCREG_DAC1+SRCREG_VFREQ_FRAC, freq & 0x7fff); - r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)); - outl(r, s->io + ES1371_REG_SRCONV); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void set_dac2_rate(struct es1371_state *s, unsigned rate) -{ - unsigned long flags; - unsigned int freq, r; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - freq = ((rate << 15) + 1500) / 3000; - s->dac2rate = (freq * 3000 + 16384) >> 15; - spin_lock_irqsave(&s->lock, flags); - r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2; - outl(r, s->io + ES1371_REG_SRCONV); - src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, - (src_read(s, SRCREG_DAC2+SRCREG_INT_REGS) & 0x00ff) | - ((freq >> 5) & 0xfc00)); - src_write(s, SRCREG_DAC2+SRCREG_VFREQ_FRAC, freq & 0x7fff); - r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)); - outl(r, s->io + ES1371_REG_SRCONV); - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -static void __init src_init(struct es1371_state *s) -{ - unsigned int i; - - /* before we enable or disable the SRC we need - to wait for it to become ready */ - wait_src_ready(s); - - outl(SRC_DIS, s->io + ES1371_REG_SRCONV); - - for (i = 0; i < 0x80; i++) - src_write(s, i, 0); - - src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4); - src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10); - src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4); - src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10); - src_write(s, SRCREG_VOL_ADC, 1 << 12); - src_write(s, SRCREG_VOL_ADC+1, 1 << 12); - src_write(s, SRCREG_VOL_DAC1, 1 << 12); - src_write(s, SRCREG_VOL_DAC1+1, 1 << 12); - src_write(s, SRCREG_VOL_DAC2, 1 << 12); - src_write(s, SRCREG_VOL_DAC2+1, 1 << 12); - set_adc_rate(s, 22050); - set_dac1_rate(s, 22050); - set_dac2_rate(s, 22050); - - /* WARNING: - * enabling the sample rate converter without properly programming - * its parameters causes the chip to lock up (the SRC busy bit will - * be stuck high, and I've found no way to rectify this other than - * power cycle) - */ - wait_src_ready(s); - outl(0, s->io+ES1371_REG_SRCONV); -} - -/* --------------------------------------------------------------------- */ - -static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) -{ - struct es1371_state *s = (struct es1371_state *)codec->private_data; - unsigned long flags; - unsigned t, x; - - 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); - - /* enable SRC state data in SRC mux */ - outl((x & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, - s->io+ES1371_REG_SRCONV); - - /* wait for not busy (state 0) first to avoid - transition states */ - for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0 ) - break; - udelay(1); - } - - /* wait for a SAFE time to write addr/data and then do it, dammit */ - for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000) - break; - udelay(1); - } - - outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | - ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC); - - /* restore SRC reg */ - wait_src_ready(s); - outl(x, s->io+ES1371_REG_SRCONV); - spin_unlock_irqrestore(&s->lock, flags); -} - -static u16 rdcodec(struct ac97_codec *codec, u8 addr) -{ - struct es1371_state *s = (struct es1371_state *)codec->private_data; - unsigned long flags; - unsigned t, x; - - /* 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)); - - /* enable SRC state data in SRC mux */ - outl( x | 0x00010000, - s->io+ES1371_REG_SRCONV); - - /* wait for not busy (state 0) first to avoid - transition states */ - for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0 ) - break; - udelay(1); - } - - /* wait for a SAFE time to write addr/data and then do it, dammit */ - for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000) - break; - udelay(1); - } - - outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC); - /* 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++) - if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) - break; - - /* now wait for the stinkin' data (RDY) */ - for (t = 0; t < POLL_COUNT; t++) - if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY) - break; - - return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); -} - -/* --------------------------------------------------------------------- */ - -static inline void stop_adc(struct es1371_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ctrl &= ~CTRL_ADC_EN; - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_dac1(struct es1371_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ctrl &= ~CTRL_DAC1_EN; - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_dac2(struct es1371_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ctrl &= ~CTRL_DAC2_EN; - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac1(struct es1371_state *s) -{ - unsigned long flags; - unsigned fragremain, fshift; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) - && s->dma_dac1.ready) { - s->ctrl |= CTRL_DAC1_EN; - s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); - fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (fragremain < 2*fshift) - fragremain = s->dma_dac1.fragsize; - outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT); - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac2(struct es1371_state *s) -{ - unsigned long flags; - unsigned fragremain, fshift; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) - && s->dma_dac2.ready) { - s->ctrl |= CTRL_DAC2_EN; - s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | - SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | - (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | - (0 << SCTRL_SH_P2STINC); - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); - fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (fragremain < 2*fshift) - fragremain = s->dma_dac2.fragsize; - outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT); - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_adc(struct es1371_state *s) -{ - unsigned long flags; - unsigned fragremain, fshift; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - s->ctrl |= CTRL_ADC_EN; - s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); - fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; - if (fragremain < 2*fshift) - fragremain = s->dma_adc.fragsize; - outl((fragremain >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT); - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - - -static inline void dealloc_dmabuf(struct es1371_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); - pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); - } - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - -static int prog_dmabuf(struct es1371_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) -{ - int order; - unsigned bytepersec; - unsigned bufs; - struct page *page, *pend; - - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - 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); - } - fmt &= ES1371_FMT_MASK; - 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--; - 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 & ES1371_FMT_S16) ? 0 : 0x80, db->dmasize); - outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE); - outl(db->dmaaddr, s->io+(reg & 0xff)); - outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); - db->enabled = 1; - db->ready = 1; - return 0; -} - -static inline int prog_dmabuf_adc(struct es1371_state *s) -{ - stop_adc(s); - return prog_dmabuf(s, &s->dma_adc, s->adcrate, (s->sctrl >> SCTRL_SH_R1FMT) & ES1371_FMT_MASK, - ES1371_REG_ADC_FRAMEADR); -} - -static inline int prog_dmabuf_dac2(struct es1371_state *s) -{ - stop_dac2(s); - return prog_dmabuf(s, &s->dma_dac2, s->dac2rate, (s->sctrl >> SCTRL_SH_P2FMT) & ES1371_FMT_MASK, - ES1371_REG_DAC2_FRAMEADR); -} - -static inline int prog_dmabuf_dac1(struct es1371_state *s) -{ - stop_dac1(s); - return prog_dmabuf(s, &s->dma_dac1, s->dac1rate, (s->sctrl >> SCTRL_SH_P1FMT) & ES1371_FMT_MASK, - ES1371_REG_DAC1_FRAMEADR); -} - -static inline unsigned get_hwptr(struct es1371_state *s, struct dmabuf *db, unsigned reg) -{ - unsigned hwptr, diff; - - outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE); - hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; - diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; - db->hwptr = hwptr; - return diff; -} - -static inline 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; - } - memset(((char *)buf) + bptr, c, len); -} - -/* call with spinlock held! */ -static void es1371_update_ptr(struct es1371_state *s) -{ - int diff; - - /* update ADC pointer */ - if (s->ctrl & CTRL_ADC_EN) { - diff = get_hwptr(s, &s->dma_adc, ES1371_REG_ADC_FRAMECNT); - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - s->ctrl &= ~CTRL_ADC_EN; - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - s->dma_adc.error++; - } - } - } - /* update DAC1 pointer */ - if (s->ctrl & CTRL_DAC1_EN) { - diff = get_hwptr(s, &s->dma_dac1, ES1371_REG_DAC1_FRAMECNT); - s->dma_dac1.total_bytes += diff; - if (s->dma_dac1.mapped) { - s->dma_dac1.count += diff; - if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) - wake_up(&s->dma_dac1.wait); - } else { - s->dma_dac1.count -= diff; - if (s->dma_dac1.count <= 0) { - s->ctrl &= ~CTRL_DAC1_EN; - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - s->dma_dac1.error++; - } else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { - clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, - s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); - s->dma_dac1.endcleared = 1; - } - if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize) - wake_up(&s->dma_dac1.wait); - } - } - /* update DAC2 pointer */ - if (s->ctrl & CTRL_DAC2_EN) { - diff = get_hwptr(s, &s->dma_dac2, ES1371_REG_DAC2_FRAMECNT); - s->dma_dac2.total_bytes += diff; - if (s->dma_dac2.mapped) { - s->dma_dac2.count += diff; - if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) - wake_up(&s->dma_dac2.wait); - } else { - s->dma_dac2.count -= diff; - if (s->dma_dac2.count <= 0) { - s->ctrl &= ~CTRL_DAC2_EN; - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - s->dma_dac2.error++; - } else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { - clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, - s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); - s->dma_dac2.endcleared = 1; - } - if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize) - wake_up(&s->dma_dac2.wait); - } - } -} - -/* hold spinlock for the following! */ -static void es1371_handle_midi(struct es1371_state *s) -{ - unsigned char ch; - int wake; - - if (!(s->ctrl & CTRL_UART_EN)) - return; - wake = 0; - while (inb(s->io+ES1371_REG_UART_STATUS) & USTAT_RXRDY) { - ch = inb(s->io+ES1371_REG_UART_DATA); - if (s->midi.icnt < MIDIINBUF) { - s->midi.ibuf[s->midi.iwr] = ch; - s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; - s->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&s->midi.iwait); - wake = 0; - while ((inb(s->io+ES1371_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { - outb(s->midi.obuf[s->midi.ord], s->io+ES1371_REG_UART_DATA); - s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; - s->midi.ocnt--; - if (s->midi.ocnt < MIDIOUTBUF-16) - wake = 1; - } - if (wake) - wake_up(&s->midi.owait); - outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1371_REG_UART_CONTROL); -} - -static void es1371_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct es1371_state *s = (struct es1371_state *)dev_id; - unsigned int intsrc, sctl; - - /* fastpath out, to ease interrupt sharing */ - intsrc = inl(s->io+ES1371_REG_STATUS); - if (!(intsrc & 0x80000000)) - return; - spin_lock(&s->lock); - /* clear audio interrupts first */ - sctl = s->sctrl; - if (intsrc & STAT_ADC) - sctl &= ~SCTRL_R1INTEN; - if (intsrc & STAT_DAC1) - sctl &= ~SCTRL_P1INTEN; - if (intsrc & STAT_DAC2) - sctl &= ~SCTRL_P2INTEN; - outl(sctl, s->io+ES1371_REG_SERIAL_CONTROL); - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - es1371_update_ptr(s); - es1371_handle_midi(s); - spin_unlock(&s->lock); -} - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != ES1371_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - -/* Conversion table for S/PDIF PCM volume emulation through the SRC */ -/* dB-linear table of DAC vol values; -0dB to -46.5dB with mute */ -static const unsigned short DACVolTable[101] = -{ - 0x1000, 0x0f2a, 0x0e60, 0x0da0, 0x0cea, 0x0c3e, 0x0b9a, 0x0aff, - 0x0a6d, 0x09e1, 0x095e, 0x08e1, 0x086a, 0x07fa, 0x078f, 0x072a, - 0x06cb, 0x0670, 0x061a, 0x05c9, 0x057b, 0x0532, 0x04ed, 0x04ab, - 0x046d, 0x0432, 0x03fa, 0x03c5, 0x0392, 0x0363, 0x0335, 0x030b, - 0x02e2, 0x02bc, 0x0297, 0x0275, 0x0254, 0x0235, 0x0217, 0x01fb, - 0x01e1, 0x01c8, 0x01b0, 0x0199, 0x0184, 0x0170, 0x015d, 0x014b, - 0x0139, 0x0129, 0x0119, 0x010b, 0x00fd, 0x00f0, 0x00e3, 0x00d7, - 0x00cc, 0x00c1, 0x00b7, 0x00ae, 0x00a5, 0x009c, 0x0094, 0x008c, - 0x0085, 0x007e, 0x0077, 0x0071, 0x006b, 0x0066, 0x0060, 0x005b, - 0x0057, 0x0052, 0x004e, 0x004a, 0x0046, 0x0042, 0x003f, 0x003c, - 0x0038, 0x0036, 0x0033, 0x0030, 0x002e, 0x002b, 0x0029, 0x0027, - 0x0025, 0x0023, 0x0021, 0x001f, 0x001e, 0x001c, 0x001b, 0x0019, - 0x0018, 0x0017, 0x0016, 0x0014, 0x0000 -}; - -/* - * when we are in S/PDIF mode, we want to disable any analog output so - * we filter the mixer ioctls - */ -static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) -{ - struct es1371_state *s = (struct es1371_state *)codec->private_data; - int val; - unsigned long flags; - unsigned int left, right; - - VALIDATE_STATE(s); - /* filter mixer ioctls to catch PCM and MASTER volume when in S/PDIF mode */ - if (s->spdif_volume == -1) - return codec->mixer_ioctl(codec, cmd, arg); - switch (cmd) { - case SOUND_MIXER_WRITE_VOLUME: - return 0; - - case SOUND_MIXER_WRITE_PCM: /* use SRC for PCM volume */ - 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; - spin_lock_irqsave(&s->lock, flags); - src_write(s, SRCREG_VOL_DAC2, DACVolTable[100 - left]); - src_write(s, SRCREG_VOL_DAC2+1, DACVolTable[100 - right]); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - case SOUND_MIXER_READ_PCM: - return put_user(s->spdif_volume, (int *)arg); - } - return codec->mixer_ioctl(codec, cmd, arg); -} - -/* --------------------------------------------------------------------- */ - -/* - * AC97 Mixer Register to Connections mapping of the Concert 97 board - * - * AC97_MASTER_VOL_STEREO Line Out - * AC97_MASTER_VOL_MONO TAD Output - * AC97_PCBEEP_VOL none - * AC97_PHONE_VOL TAD Input (mono) - * AC97_MIC_VOL MIC Input (mono) - * AC97_LINEIN_VOL Line Input (stereo) - * AC97_CD_VOL CD Input (stereo) - * AC97_VIDEO_VOL none - * AC97_AUX_VOL Aux Input (stereo) - * AC97_PCMOUT_VOL Wave Output (stereo) - */ - -static int es1371_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct list_head *list; - struct es1371_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1371_state, devs); - if (s->codec.dev_mixer == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - return 0; -} - -static int es1371_release_mixdev(struct inode *inode, struct file *file) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - - VALIDATE_STATE(s); - return 0; -} - -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; - - return mixdev_ioctl(codec, cmd, arg); -} - -static /*const*/ struct file_operations es1371_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: es1371_ioctl_mixdev, - open: es1371_open_mixdev, - release: es1371_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac1(struct es1371_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (s->dma_dac1.mapped || !s->dma_dac1.ready) - return 0; - add_wait_queue(&s->dma_dac1.wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac1.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac1.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate; - tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo + 1)) - DBG(printk(KERN_DEBUG PFX "dac1 dma timed out??\n");) - } - remove_wait_queue(&s->dma_dac1.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -static int drain_dac2(struct es1371_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (s->dma_dac2.mapped || !s->dma_dac2.ready) - return 0; - add_wait_queue(&s->dma_dac2.wait, &wait); - for (;;) { - __set_current_state(TASK_UNINTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac2.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac2.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate; - tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo + 1)) - DBG(printk(KERN_DEBUG PFX "dac2 dma timed out??\n");) - } - remove_wait_queue(&s->dma_dac2.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (s->dma_adc.mapped) - return -ENXIO; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - down(&s->sem); - if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) - goto out2; - - add_wait_queue(&s->dma_adc.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_adc.enabled) - start_adc(s); - 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); - if (s->dma_adc.mapped) - { - ret = -ENXIO; - goto out; - } - continue; - } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - if (!ret) - ret = -EFAULT; - goto out; - } - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_adc.enabled) - start_adc(s); - } -out: - up(&s->sem); -out2: - remove_wait_queue(&s->dma_adc.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (s->dma_dac2.mapped) - return -ENXIO; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - down(&s->sem); - if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) - goto out3; - ret = 0; - add_wait_queue(&s->dma_dac2.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac2.count < 0) { - s->dma_dac2.count = 0; - s->dma_dac2.swptr = s->dma_dac2.hwptr; - } - swptr = s->dma_dac2.swptr; - cnt = s->dma_dac2.dmasize-swptr; - if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) - cnt = s->dma_dac2.dmasize - s->dma_dac2.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_dac2.enabled) - start_dac2(s); - 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); - if (s->dma_dac2.mapped) - { - ret = -ENXIO; - goto out; - } - continue; - } - if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - goto out; - } - swptr = (swptr + cnt) % s->dma_dac2.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac2.swptr = swptr; - s->dma_dac2.count += cnt; - s->dma_dac2.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_dac2.enabled) - start_dac2(s); - } -out: - up(&s->sem); -out2: - remove_wait_queue(&s->dma_dac2.wait, &wait); -out3: - set_current_state(TASK_RUNNING); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int es1371_poll(struct file *file, struct poll_table_struct *wait) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac2.ready && prog_dmabuf_dac2(s)) - return 0; - poll_wait(file, &s->dma_dac2.wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready && prog_dmabuf_adc(s)) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); - es1371_update_ptr(s); - 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_dac2.mapped) { - if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int es1371_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - struct dmabuf *db; - int ret = 0; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - down(&s->sem); - - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf_dac2(s)) != 0) { - goto out; - } - db = &s->dma_dac2; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf_adc(s)) != 0) { - goto out; - } - 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, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) { - ret = -EAGAIN; - goto out; - } - db->mapped = 1; -out: - up(&s->sem); - unlock_kernel(); - return ret; -} - -static int es1371_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val, mapped, ret; - - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - return drain_dac2(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: - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - synchronize_irq(); - s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - 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); - s->dma_adc.ready = 0; - set_adc_rate(s, val); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - s->dma_dac2.ready = 0; - set_dac2_rate(s, val); - } - } - return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (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.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val) - s->sctrl |= SCTRL_R1SMB; - else - s->sctrl &= ~SCTRL_R1SMB; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val) - s->sctrl |= SCTRL_P2SMB; - else - s->sctrl &= ~SCTRL_P2SMB; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - 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); - s->dma_adc.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val >= 2) - s->sctrl |= SCTRL_R1SMB; - else - s->sctrl &= ~SCTRL_R1SMB; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val >= 2) - s->sctrl |= SCTRL_P2SMB; - else - s->sctrl &= ~SCTRL_P2SMB; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - } - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (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); - s->dma_adc.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val == AFMT_S16_LE) - s->sctrl |= SCTRL_R1SEB; - else - s->sctrl &= ~SCTRL_R1SEB; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val == AFMT_S16_LE) - s->sctrl |= SCTRL_P2SEB; - else - s->sctrl &= ~SCTRL_P2SEB; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - } - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? - AFMT_S16_LE : AFMT_U8, (int *)arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) - 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; - s->dma_adc.enabled = 1; - start_adc(s); - } else { - s->dma_adc.enabled = 0; - stop_adc(s); - } - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) - return ret; - s->dma_dac2.enabled = 1; - start_dac2(s); - } else { - s->dma_dac2.enabled = 0; - stop_dac2(s); - } - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1371_update_ptr(s); - abinfo.fragsize = s->dma_dac2.fragsize; - count = s->dma_dac2.count; - if (count < 0) - count = 0; - abinfo.bytes = s->dma_dac2.dmasize - count; - abinfo.fragstotal = s->dma_dac2.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; - 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)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1371_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - count = s->dma_adc.count; - if (count < 0) - count = 0; - abinfo.bytes = 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_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1371_update_ptr(s); - count = s->dma_dac2.count; - 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; - if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1371_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - count = s->dma_adc.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_adc.fragshift; - 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_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1371_update_ptr(s); - cinfo.bytes = s->dma_dac2.total_bytes; - count = s->dma_dac2.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac2.fragshift; - cinfo.ptr = s->dma_dac2.hwptr; - if (s->dma_dac2.mapped) - s->dma_dac2.count &= s->dma_dac2.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_dac2(s))) - return val; - return put_user(s->dma_dac2.fragsize, (int *)arg); - } - if ((val = prog_dmabuf_adc(s))) - return val; - 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) { - 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 (file->f_mode & FMODE_WRITE) { - s->dma_dac2.ossfragshift = val & 0xffff; - s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac2.ossfragshift < 4) - s->dma_dac2.ossfragshift = 4; - if (s->dma_dac2.ossfragshift > 15) - s->dma_dac2.ossfragshift = 15; - if (s->dma_dac2.ossmaxfrags < 4) - s->dma_dac2.ossmaxfrags = 4; - } - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac2.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; - if (file->f_mode & FMODE_WRITE) - s->dma_dac2.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg); - - case SOUND_PCM_READ_BITS: - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? 16 : 8, (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 es1371_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 es1371_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1371_state, devs); - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - VALIDATE_STATE(s); - 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); - } - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - s->dma_adc.enabled = 1; - set_adc_rate(s, 8000); - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; - s->dma_dac2.enabled = 1; - set_dac2_rate(s, 8000); - } - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - s->sctrl &= ~SCTRL_R1FMT; - if ((minor & 0xf) == SND_DEV_DSP16) - s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_R1FMT; - else - s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_R1FMT; - } - if (file->f_mode & FMODE_WRITE) { - s->sctrl &= ~SCTRL_P2FMT; - if ((minor & 0xf) == SND_DEV_DSP16) - s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P2FMT; - else - s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P2FMT; - } - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - up(&s->open_sem); - init_MUTEX(&s->sem); - return 0; -} - -static int es1371_release(struct inode *inode, struct file *file) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - - VALIDATE_STATE(s); - lock_kernel(); - if (file->f_mode & FMODE_WRITE) - drain_dac2(s, file->f_flags & O_NONBLOCK); - down(&s->open_sem); - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - dealloc_dmabuf(s, &s->dma_dac2); - } - 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 es1371_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: es1371_read, - write: es1371_write, - poll: es1371_poll, - ioctl: es1371_ioctl, - mmap: es1371_mmap, - open: es1371_open, - release: es1371_release, -}; - -/* --------------------------------------------------------------------- */ - -static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (s->dma_dac1.mapped) - return -ENXIO; - if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - add_wait_queue(&s->dma_dac1.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac1.count < 0) { - s->dma_dac1.count = 0; - s->dma_dac1.swptr = s->dma_dac1.hwptr; - } - swptr = s->dma_dac1.swptr; - cnt = s->dma_dac1.dmasize-swptr; - if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) - cnt = s->dma_dac1.dmasize - s->dma_dac1.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_dac1.enabled) - start_dac1(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_dac1.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac1.swptr = swptr; - s->dma_dac1.count += cnt; - s->dma_dac1.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_dac1.enabled) - start_dac1(s); - } - remove_wait_queue(&s->dma_dac1.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int es1371_poll_dac(struct file *file, struct poll_table_struct *wait) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (!s->dma_dac1.ready && prog_dmabuf_dac1(s)) - return 0; - poll_wait(file, &s->dma_dac1.wait, wait); - spin_lock_irqsave(&s->lock, flags); - es1371_update_ptr(s); - if (s->dma_dac1.mapped) { - if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int es1371_mmap_dac(struct file *file, struct vm_area_struct *vma) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - int ret; - unsigned long size; - - VALIDATE_STATE(s); - if (!(vma->vm_flags & VM_WRITE)) - return -EINVAL; - lock_kernel(); - if ((ret = prog_dmabuf_dac1(s)) != 0) - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << s->dma_dac1.buforder)) - goto out; - ret = -EAGAIN; - if (remap_page_range(vma, vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot)) - goto out; - s->dma_dac1.mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int es1371_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val, ret; - - VALIDATE_STATE(s); - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_SYNC: - return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); - - case SNDCTL_DSP_SETDUPLEX: - return -EINVAL; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); - - case SNDCTL_DSP_RESET: - stop_dac1(s); - synchronize_irq(); - s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val >= 0) { - stop_dac1(s); - s->dma_dac1.ready = 0; - set_dac1_rate(s, val); - } - return put_user(s->dac1rate, (int *)arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - stop_dac1(s); - s->dma_dac1.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val) - s->sctrl |= SCTRL_P1SMB; - else - s->sctrl &= ~SCTRL_P1SMB; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) { - stop_dac1(s); - s->dma_dac1.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val >= 2) - s->sctrl |= SCTRL_P1SMB; - else - s->sctrl &= ~SCTRL_P1SMB; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (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) { - stop_dac1(s); - s->dma_dac1.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val == AFMT_S16_LE) - s->sctrl |= SCTRL_P1SEB; - else - s->sctrl &= ~SCTRL_P1SEB; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) - return ret; - s->dma_dac1.enabled = 1; - start_dac1(s); - } else { - s->dma_dac1.enabled = 0; - stop_dac1(s); - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1371_update_ptr(s); - abinfo.fragsize = s->dma_dac1.fragsize; - count = s->dma_dac1.count; - if (count < 0) - count = 0; - abinfo.bytes = s->dma_dac1.dmasize - count; - abinfo.fragstotal = s->dma_dac1.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac1.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 (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1371_update_ptr(s); - count = s->dma_dac1.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - return put_user(count, (int *)arg); - - case SNDCTL_DSP_GETOPTR: - if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1371_update_ptr(s); - cinfo.bytes = s->dma_dac1.total_bytes; - count = s->dma_dac1.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac1.fragshift; - cinfo.ptr = s->dma_dac1.hwptr; - if (s->dma_dac1.mapped) - s->dma_dac1.count &= s->dma_dac1.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); - - case SNDCTL_DSP_GETBLKSIZE: - if ((val = prog_dmabuf_dac1(s))) - return val; - return put_user(s->dma_dac1.fragsize, (int *)arg); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) - return -EFAULT; - s->dma_dac1.ossfragshift = val & 0xffff; - s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac1.ossfragshift < 4) - s->dma_dac1.ossfragshift = 4; - if (s->dma_dac1.ossfragshift > 15) - s->dma_dac1.ossfragshift = 15; - if (s->dma_dac1.ossmaxfrags < 4) - s->dma_dac1.ossmaxfrags = 4; - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if (s->dma_dac1.subdivision) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - s->dma_dac1.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user(s->dac1rate, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); - - case SOUND_PCM_READ_BITS: - return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, (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 es1371_open_dac(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct list_head *list; - struct es1371_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1371_state, devs); - if (!((s->dev_dac ^ minor) & ~0xf)) - break; - } - VALIDATE_STATE(s); - /* we allow opening with O_RDWR, most programs do it although they will only write */ -#if 0 - if (file->f_mode & FMODE_READ) - return -EPERM; -#endif - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & FMODE_DAC) { - 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); - } - s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; - s->dma_dac1.enabled = 1; - set_dac1_rate(s, 8000); - spin_lock_irqsave(&s->lock, flags); - s->sctrl &= ~SCTRL_P1FMT; - if ((minor & 0xf) == SND_DEV_DSP16) - s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P1FMT; - else - s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P1FMT; - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= FMODE_DAC; - up(&s->open_sem); - return 0; -} - -static int es1371_release_dac(struct inode *inode, struct file *file) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - - VALIDATE_STATE(s); - lock_kernel(); - drain_dac1(s, file->f_flags & O_NONBLOCK); - down(&s->open_sem); - stop_dac1(s); - dealloc_dmabuf(s, &s->dma_dac1); - s->open_mode &= ~FMODE_DAC; - up(&s->open_sem); - wake_up(&s->open_wait); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations es1371_dac_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - write: es1371_write_dac, - poll: es1371_poll_dac, - ioctl: es1371_ioctl_dac, - mmap: es1371_mmap_dac, - open: es1371_open_dac, - release: es1371_release_dac, -}; - -/* --------------------------------------------------------------------- */ - -static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.iwait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.ird; - cnt = MIDIINBUF - ptr; - if (s->midi.icnt < cnt) - cnt = s->midi.icnt; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIINBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.ird = ptr; - s->midi.icnt -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - break; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.iwait, &wait); - return ret; -} - -static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.owait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.owr; - cnt = MIDIOUTBUF - ptr; - if (s->midi.ocnt + cnt > MIDIOUTBUF) - cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) { - __set_current_state(TASK_INTERRUPTIBLE); - es1371_handle_midi(s); - } - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIOUTBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.owr = ptr; - s->midi.ocnt += cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&s->lock, flags); - es1371_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.owait, &wait); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int es1371_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &s->midi.owait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &s->midi.iwait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - if (s->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->midi.ocnt < MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int es1371_midi_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 es1371_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1371_state, devs); - if (s->dev_midi == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - 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); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - outb(UCTRL_CNTRL_SWR, s->io+ES1371_REG_UART_CONTROL); - outb(0, s->io+ES1371_REG_UART_CONTROL); - outb(0, s->io+ES1371_REG_UART_TEST); - } - if (file->f_mode & FMODE_READ) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - } - s->ctrl |= CTRL_UART_EN; - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - es1371_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); - up(&s->open_sem); - return 0; -} - -static int es1371_midi_release(struct inode *inode, struct file *file) -{ - struct es1371_state *s = (struct es1371_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - VALIDATE_STATE(s); - lock_kernel(); - if (file->f_mode & FMODE_WRITE) { - add_wait_queue(&s->midi.owait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->midi.ocnt; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - 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; - } - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG PFX "midi timed out??\n"); - } - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - } - down(&s->open_sem); - s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->ctrl &= ~CTRL_UART_EN; - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - } - spin_unlock_irqrestore(&s->lock, flags); - up(&s->open_sem); - wake_up(&s->open_wait); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations es1371_midi_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: es1371_midi_read, - write: es1371_midi_write, - poll: es1371_midi_poll, - open: es1371_midi_open, - release: es1371_midi_release, -}; - -/* --------------------------------------------------------------------- */ - -/* - * for debugging purposes, we'll create a proc device that dumps the - * CODEC chipstate - */ - -#ifdef ES1371_DEBUG -static int proc_es1371_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data) -{ - struct es1371_state *s; - int cnt, len = 0; - - if (list_empty(&devs)) - return 0; - s = list_entry(devs.next, struct es1371_state, devs); - /* print out header */ - len += sprintf(buf + len, "\t\tCreative ES137x Debug Dump-o-matic\n"); - - /* 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)); - - if (fpos >=len){ - *start = buf; - *eof =1; - return 0; - } - *start = buf + fpos; - if ((len -= fpos) > length) - return length; - *eof =1; - return len; - -} -#endif /* ES1371_DEBUG */ - -/* --------------------------------------------------------------------- */ - -/* maximum number of devices; only used for command line params */ -#define NR_DEVICE 5 - -static int joystick[NR_DEVICE] = { 0, }; -static int spdif[NR_DEVICE] = { 0, }; -static int nomix[NR_DEVICE] = { 0, }; - -static unsigned int devindex = 0; -static int amplifier = 0; - -MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(joystick, "sets address and enables joystick interface (still need separate driver)"); -MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(spdif, "if 1 the output is in S/PDIF digital mode"); -MODULE_PARM(nomix, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(nomix, "if 1 no analog audio is mixed to the digital output"); -MODULE_PARM(amplifier, "i"); -MODULE_PARM_DESC(amplifier, "Set to 1 if the machine needs the amp control enabling (many laptops)"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("ES1371 AudioPCI97 Driver"); -MODULE_LICENSE("GPL"); - - -/* --------------------------------------------------------------------- */ - -static struct initvol { - int mixch; - int vol; -} initvol[] __initdata = { - { SOUND_MIXER_WRITE_LINE, 0x4040 }, - { SOUND_MIXER_WRITE_CD, 0x4040 }, - { MIXER_WRITE(SOUND_MIXER_VIDEO), 0x4040 }, - { SOUND_MIXER_WRITE_LINE1, 0x4040 }, - { SOUND_MIXER_WRITE_PCM, 0x4040 }, - { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, - { MIXER_WRITE(SOUND_MIXER_PHONEOUT), 0x4040 }, - { SOUND_MIXER_WRITE_OGAIN, 0x4040 }, - { MIXER_WRITE(SOUND_MIXER_PHONEIN), 0x4040 }, - { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, - { SOUND_MIXER_WRITE_MIC, 0x4040 }, - { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, - { SOUND_MIXER_WRITE_IGAIN, 0x4040 } -}; - -static struct -{ - short svid, sdid; -} amplifier_needed[] = -{ - { 0x107B, 0x2150 }, /* Gateway Solo 2150 */ - { 0x13BD, 0x100C }, /* Mebius PC-MJ100V */ - { 0x1102, 0x5938 }, /* Targa Xtender 300 */ - { 0x1102, 0x8938 }, /* IPC notebook */ - { PCI_ANY_ID, PCI_ANY_ID } -}; - - -static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) -{ - struct es1371_state *s; - mm_segment_t fs; - int i, val, res = -1; - int idx; - unsigned long tmo; - signed long tmo2; - unsigned int cssr; - - if ((res=pci_enable_device(pcidev))) - return res; - - if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO)) - return -ENODEV; - if (pcidev->irq == 0) - return -ENODEV; - i = pci_set_dma_mask(pcidev, 0xffffffff); - if (i) { - printk(KERN_WARNING "es1371: architecture does not support 32bit PCI busmaster DMA\n"); - return i; - } - if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) { - printk(KERN_WARNING PFX "out of memory\n"); - return -ENOMEM; - } - memset(s, 0, sizeof(struct es1371_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac1.wait); - init_waitqueue_head(&s->dma_dac2.wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->midi.iwait); - init_waitqueue_head(&s->midi.owait); - init_MUTEX(&s->open_sem); - spin_lock_init(&s->lock); - s->magic = ES1371_MAGIC; - 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; - 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")) { - printk(KERN_ERR PFX "io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1); - res = -EBUSY; - goto err_region; - } - if ((res=request_irq(s->irq, es1371_interrupt, SA_SHIRQ, "es1371",s))) { - printk(KERN_ERR PFX "irq %u in use\n", s->irq); - goto err_irq; - } - printk(KERN_INFO PFX "found es1371 rev %d at io %#lx irq %u\n" - KERN_INFO PFX "features: joystick 0x%x\n", s->rev, s->io, s->irq, joystick[devindex]); - /* 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)) - goto err_dev2; - if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1)) < 0)) - goto err_dev3; - if ((res=(s->dev_midi = register_sound_midi(&es1371_midi_fops, -1))<0 )) - goto err_dev4; -#ifdef ES1371_DEBUG - /* intialize the debug proc device */ - s->ps = create_proc_read_entry("es1371",0,NULL,proc_es1371_dump,NULL); -#endif /* ES1371_DEBUG */ - - /* initialize codec registers */ - s->ctrl = 0; - - /* Check amplifier requirements */ - - if(amplifier) - s->ctrl |= CTRL_GPIO_OUT0; - else for(idx = 0; amplifier_needed[idx].svid != PCI_ANY_ID; idx++) - { - if(pcidev->subsystem_vendor == amplifier_needed[idx].svid && - pcidev->subsystem_device == amplifier_needed[idx].sdid) - { - s->ctrl |= CTRL_GPIO_OUT0; /* turn internal amplifier on */ - printk(KERN_INFO PFX "Enabling internal amplifier.\n"); - } - } - s->gameport.io = 0; - if ((joystick[devindex] & ~0x18) == 0x200) { - if (!request_region(joystick[devindex], JOY_EXTENT, "es1371")) - printk(KERN_ERR PFX "joystick address 0x%x already in use\n", joystick[devindex]); - else { - s->ctrl |= CTRL_JYSTK_EN | (((joystick[devindex] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); - s->gameport.io = joystick[devindex]; - } - } else if (joystick[devindex] == 1) { - for (i = 0x218; i >= 0x200; i -= 0x08) { - if (request_region(i, JOY_EXTENT, "es1371")) { - s->ctrl |= CTRL_JYSTK_EN | (((i >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); - s->gameport.io = i; - break; - } - } - if (!s->gameport.io) - printk(KERN_ERR PFX "no free joystick address found\n"); - } - s->sctrl = 0; - cssr = 0; - s->spdif_volume = -1; - /* check to see if s/pdif mode is being requested */ - if (spdif[devindex]) { - if (s->rev >= 4) { - printk(KERN_INFO PFX "enabling S/PDIF output\n"); - s->spdif_volume = 0; - cssr |= STAT_EN_SPDIF; - s->ctrl |= CTRL_SPDIFEN_B; - if (nomix[devindex]) /* don't mix analog inputs to s/pdif output */ - s->ctrl |= CTRL_RECEN_B; - } else { - printk(KERN_ERR PFX "revision %d does not support S/PDIF\n", s->rev); - } - } - /* initialize the chips */ - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); - outl(0, s->io+ES1371_REG_LEGACY); - pci_set_master(pcidev); /* enable bus mastering */ - /* if we are a 5880 turn on the AC97 */ - if (s->vendor == PCI_VENDOR_ID_ENSONIQ && - ((s->device == PCI_DEVICE_ID_ENSONIQ_CT5880 && s->rev >= CT5880REV_CT5880_C) || - (s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_CT5880_A) || - (s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_ES1373_8))) { - cssr |= CSTAT_5880_AC97_RST; - outl(cssr, s->io+ES1371_REG_STATUS); - /* need to delay around 20ms(bleech) to give - some CODECs enough time to wakeup */ - tmo = jiffies + (HZ / 50) + 1; - for (;;) { - tmo2 = tmo - jiffies; - if (tmo2 <= 0) - break; - schedule_timeout(tmo2); - } - } - /* AC97 warm reset to start the bitclk */ - outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL); - udelay(2); - outl(s->ctrl, s->io+ES1371_REG_CONTROL); - /* init the sample rate converter */ - src_init(s); - /* codec init */ - if (!ac97_probe_codec(&s->codec)) { - res = -ENODEV; - goto err_gp; - } - /* set default values */ - - fs = get_fs(); - set_fs(KERNEL_DS); - val = SOUND_MASK_LINE; - 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); - } - /* 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); - } - set_fs(fs); - /* turn on S/PDIF output driver if requested */ - outl(cssr, s->io+ES1371_REG_STATUS); - /* register gameport */ - gameport_register_port(&s->gameport); - /* store it in the driver field */ - pci_set_drvdata(pcidev, s); - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - /* increment devindex */ - if (devindex < NR_DEVICE-1) - devindex++; - return 0; - - err_gp: - if (s->gameport.io) - release_region(s->gameport.io, JOY_EXTENT); - err_dev4: - unregister_sound_dsp(s->dev_dac); - err_dev3: - unregister_sound_mixer(s->codec.dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR PFX "cannot register misc device\n"); - free_irq(s->irq, s); - err_irq: - release_region(s->io, ES1371_EXTENT); - err_region: - kfree(s); - return res; -} - -static void __devinit es1371_remove(struct pci_dev *dev) -{ - struct es1371_state *s = pci_get_drvdata(dev); - - if (!s) - return; - list_del(&s->devs); -#ifdef ES1371_DEBUG - if (s->ps) - remove_proc_entry("es1371", NULL); -#endif /* ES1371_DEBUG */ - outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */ - outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */ - synchronize_irq(); - free_irq(s->irq, s); - if (s->gameport.io) { - gameport_unregister_port(&s->gameport); - release_region(s->gameport.io, JOY_EXTENT); - } - release_region(s->io, ES1371_EXTENT); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->codec.dev_mixer); - unregister_sound_dsp(s->dev_dac); - unregister_sound_midi(s->dev_midi); - kfree(s); - pci_set_drvdata(dev, NULL); -} - -static struct pci_device_id id_table[] __devinitdata = { - { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_CT5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { PCI_VENDOR_ID_ECTIVA, PCI_DEVICE_ID_ECTIVA_EV1938, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver es1371_driver = { - name: "es1371", - id_table: id_table, - probe: es1371_probe, - remove: es1371_remove -}; - -static int __init init_es1371(void) -{ - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - printk(KERN_INFO PFX "version v0.30 time " __TIME__ " " __DATE__ "\n"); - return pci_module_init(&es1371_driver); -} - -static void __exit cleanup_es1371(void) -{ - printk(KERN_INFO PFX "unloading\n"); - pci_unregister_driver(&es1371_driver); -} - -module_init(init_es1371); -module_exit(cleanup_es1371); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* format is: es1371=[joystick] */ - -static int __init es1371_setup(char *str) -{ - static unsigned __initdata nr_dev = 0; - - if (nr_dev >= NR_DEVICE) - return 0; - if (get_option(&str, &joystick[nr_dev]) == 2) - (void)get_option(&str, &spdif[nr_dev]); - nr_dev++; - return 1; -} - -__setup("es1371=", es1371_setup); - -#endif /* MODULE */ diff -Nru a/drivers/sound/esssolo1.c b/drivers/sound/esssolo1.c --- a/drivers/sound/esssolo1.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2484 +0,0 @@ -/****************************************************************************/ - -/* - * esssolo1.c -- ESS Technology Solo1 (ES1946) audio driver. - * - * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) - * - * 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 so far - * - * 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 - * - * Revision history - * 10.11.1998 0.1 Initial release (without any hardware) - * 22.03.1999 0.2 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 07.04.1999 0.3 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * 15.06.1999 0.4 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.1999 0.5 Add pci_set_master - * 12.08.1999 0.6 Fix MIDI UART crashing the driver - * Changed mixer semantics from OSS documented - * behaviour to OSS "code behaviour". - * Recording might actually work now. - * The real DDMA controller address register is at PCI config - * 0x60, while the register at 0x18 is used as a placeholder - * register for BIOS address allocation. This register - * is supposed to be copied into 0x60, according - * to the Solo1 datasheet. When I do that, I can access - * the DDMA registers except the mask bit, which - * is stuck at 1. When I copy the contents of 0x18 +0x10 - * to the DDMA base register, everything seems to work. - * The fun part is that the Windows Solo1 driver doesn't - * seem to do these tricks. - * Bugs remaining: plops and clicks when starting/stopping playback - * 31.08.1999 0.7 add spin_lock_init - * replaced current->state = x with set_current_state(x) - * 03.09.1999 0.8 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race - * 07.10.1999 0.9 Fix initialization; complain if sequencer writes time out - * Revised resource grabbing for the FM synthesizer - * 28.10.1999 0.10 More waitqueue races fixed - * 09.12.1999 0.11 Work around stupid Alpha port issue (virt_to_bus(kmalloc(GFP_DMA)) > 16M) - * Disabling recording on Alpha - * 12.01.2000 0.12 Prevent some ioctl's from returning bad count values on underrun/overrun; - * Tim Janik's BSE (Bedevilled Sound Engine) found this - * Integrated (aka redid 8-)) APM support patch by Zach Brown - * 07.02.2000 0.13 Use pci_alloc_consistent and pci_register_driver - * 19.02.2000 0.14 Use pci_dma_supported to determine if recording should be disabled - * 13.03.2000 0.15 Reintroduce initialization of a couple of PCI config space registers - * 21.11.2000 0.16 Initialize dma buffers in poll, otherwise poll may return a bogus mask - * 12.12.2000 0.17 More dma buffer initializations, patch from - * Tjeerd Mulder - * 31.01.2001 0.18 Register/Unregister gameport, original patch from - * Nathaniel Daw - * Fix SETTRIGGER non OSS API conformity - * 10.03.2001 provide abs function, prevent picking up a bogus kernel macro - * for abs. Bug report by Andrew Morton - * 15.05.2001 pci_enable_device moved, return values in probe cleaned - * up. Marcus Meissner - * 22.05.2001 0.19 more cleanups, changed PM to PCI 2.4 style, got rid - * of global list of devices, using pci device data. - * Marcus Meissner - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dm.h" - -/* --------------------------------------------------------------------- */ - -#undef OSS_DOCUMENTED_MIXER_SEMANTICS - -/* --------------------------------------------------------------------- */ - -#ifndef PCI_VENDOR_ID_ESS -#define PCI_VENDOR_ID_ESS 0x125d -#endif -#ifndef PCI_DEVICE_ID_ESS_SOLO1 -#define PCI_DEVICE_ID_ESS_SOLO1 0x1969 -#endif - -#define SOLO1_MAGIC ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1) - -#define DDMABASE_OFFSET 0 /* chip bug workaround kludge */ -#define DDMABASE_EXTENT 16 - -#define IOBASE_EXTENT 16 -#define SBBASE_EXTENT 16 -#define VCBASE_EXTENT (DDMABASE_EXTENT+DDMABASE_OFFSET) -#define MPUBASE_EXTENT 4 -#define GPBASE_EXTENT 4 -#define GAMEPORT_EXTENT 4 - -#define FMSYNTH_EXTENT 4 - -/* MIDI buffer sizes */ - -#define MIDIINBUF 256 -#define MIDIOUTBUF 256 - -#define FMODE_MIDI_SHIFT 3 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -#define FMODE_DMFM 0x10 - -static struct pci_driver solo1_driver; - -/* --------------------------------------------------------------------- */ - -struct solo1_state { - /* magic */ - unsigned int magic; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; - - /* soundcore stuff */ - int dev_audio; - int dev_mixer; - int dev_midi; - int dev_dmfm; - - /* hardware resources */ - unsigned long iobase, sbbase, vcbase, ddmabase, mpubase; /* long for SPARC */ - unsigned int irq; - - /* mixer registers */ - struct { - unsigned short vol[10]; - unsigned int recsrc; - unsigned int modcnt; - unsigned short micpreamp; - } mix; - - /* wave stuff */ - unsigned fmt; - unsigned channels; - unsigned rate; - unsigned char clkdiv; - unsigned ena; - - 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; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - 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 endcleared:1; - unsigned enabled:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac, dma_adc; - - /* midi stuff */ - struct { - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t iwait; - wait_queue_head_t owait; - struct timer_list timer; - unsigned char ibuf[MIDIINBUF]; - unsigned char obuf[MIDIOUTBUF]; - } midi; - - struct gameport gameport; -}; - -/* --------------------------------------------------------------------- */ - -static inline void write_seq(struct solo1_state *s, unsigned char data) -{ - int i; - unsigned long flags; - - /* the __cli stunt is to send the data within the command window */ - for (i = 0; i < 0xffff; i++) { - __save_flags(flags); - __cli(); - if (!(inb(s->sbbase+0xc) & 0x80)) { - outb(data, s->sbbase+0xc); - __restore_flags(flags); - return; - } - __restore_flags(flags); - } - printk(KERN_ERR "esssolo1: write_seq timeout\n"); - outb(data, s->sbbase+0xc); -} - -static inline int read_seq(struct solo1_state *s, unsigned char *data) -{ - int i; - - if (!data) - return 0; - for (i = 0; i < 0xffff; i++) - if (inb(s->sbbase+0xe) & 0x80) { - *data = inb(s->sbbase+0xa); - return 1; - } - printk(KERN_ERR "esssolo1: read_seq timeout\n"); - return 0; -} - -static int inline reset_ctrl(struct solo1_state *s) -{ - int i; - - outb(3, s->sbbase+6); /* clear sequencer and FIFO */ - udelay(10); - outb(0, s->sbbase+6); - for (i = 0; i < 0xffff; i++) - if (inb(s->sbbase+0xe) & 0x80) - if (inb(s->sbbase+0xa) == 0xaa) { - write_seq(s, 0xc6); /* enter enhanced mode */ - return 1; - } - return 0; -} - -static void write_ctrl(struct solo1_state *s, unsigned char reg, unsigned char data) -{ - write_seq(s, reg); - write_seq(s, data); -} - -#if 0 /* unused */ -static unsigned char read_ctrl(struct solo1_state *s, unsigned char reg) -{ - unsigned char r; - - write_seq(s, 0xc0); - write_seq(s, reg); - read_seq(s, &r); - return r; -} -#endif /* unused */ - -static void write_mixer(struct solo1_state *s, unsigned char reg, unsigned char data) -{ - outb(reg, s->sbbase+4); - outb(data, s->sbbase+5); -} - -static unsigned char read_mixer(struct solo1_state *s, unsigned char reg) -{ - outb(reg, s->sbbase+4); - return inb(s->sbbase+5); -} - -/* --------------------------------------------------------------------- */ - -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; -} - -/* --------------------------------------------------------------------- */ - -static inline void stop_dac(struct solo1_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ena &= ~FMODE_WRITE; - write_mixer(s, 0x78, 0x10); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac(struct solo1_state *s) -{ - unsigned long flags; - - 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; - write_mixer(s, 0x78, 0x12); - udelay(10); - write_mixer(s, 0x78, 0x13); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_adc(struct solo1_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ena &= ~FMODE_READ; - write_ctrl(s, 0xb8, 0xe); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_adc(struct solo1_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ena & FMODE_READ) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - s->ena |= FMODE_READ; - write_ctrl(s, 0xb8, 0xf); -#if 0 - printk(KERN_DEBUG "solo1: DMAbuffer: 0x%08lx\n", (long)s->dma_adc.rawbuf); - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", - inb(s->ddmabase+0xf), inw(s->ddmabase+4), inl(s->ddmabase), inb(s->ddmabase+8)); -#endif - outb(0, s->ddmabase+0xd); /* master reset */ - outb(1, s->ddmabase+0xf); /* mask */ - outb(0x54/*0x14*/, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ - outl(virt_to_bus(s->dma_adc.rawbuf), s->ddmabase); - outw(s->dma_adc.dmasize-1, s->ddmabase+4); - outb(0, s->ddmabase+0xf); - } - spin_unlock_irqrestore(&s->lock, flags); -#if 0 - printk(KERN_DEBUG "solo1: start DMA: reg B8: 0x%02x SBstat: 0x%02x\n" - KERN_DEBUG "solo1: DMA: stat: 0x%02x cnt: 0x%04x mask: 0x%02x\n", - read_ctrl(s, 0xb8), inb(s->sbbase+0xc), - inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->ddmabase+0xf)); - printk(KERN_DEBUG "solo1: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" - KERN_DEBUG "solo1: B1: 0x%02x B2: 0x%02x B4: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n", - read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), - read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb4), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), - read_ctrl(s, 0xb9)); -#endif -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -static inline void dealloc_dmabuf(struct solo1_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); - pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); - } - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - -static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db) -{ - int order; - unsigned bytespersec; - unsigned bufs, sample_shift = 0; - struct page *page, *pend; - - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - 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); - } - if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) - sample_shift++; - if (s->channels > 1) - sample_shift++; - bytespersec = s->rate << sample_shift; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytespersec) - db->fragshift = ld2(bytespersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytespersec/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--; - 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; - db->dmasize = db->numfrag << db->fragshift; - db->enabled = 1; - return 0; -} - -static inline int prog_dmabuf_adc(struct solo1_state *s) -{ - unsigned long va; - int c; - - stop_adc(s); - /* check if PCI implementation supports 24bit busmaster DMA */ - if (s->dev->dma_mask > 0xffffff) - return -EIO; - if ((c = prog_dmabuf(s, &s->dma_adc))) - return c; - va = s->dma_adc.dmaaddr; - if ((va & ~((1<<24)-1))) - panic("solo1: buffer above 16M boundary"); - outb(0, s->ddmabase+0xd); /* clear */ - outb(1, s->ddmabase+0xf); /* mask */ - /*outb(0, s->ddmabase+8);*/ /* enable (enable is active low!) */ - outb(0x54, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ - outl(va, s->ddmabase); - outw(s->dma_adc.dmasize-1, s->ddmabase+4); - c = - s->dma_adc.fragsamples; - write_ctrl(s, 0xa4, c); - write_ctrl(s, 0xa5, c >> 8); - outb(0, s->ddmabase+0xf); - s->dma_adc.ready = 1; - return 0; -} - -static inline int prog_dmabuf_dac(struct solo1_state *s) -{ - unsigned long va; - int c; - - stop_dac(s); - if ((c = prog_dmabuf(s, &s->dma_dac))) - return c; - memset(s->dma_dac.rawbuf, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80, s->dma_dac.dmasize); /* almost correct for U16 */ - va = s->dma_dac.dmaaddr; - if ((va ^ (va + s->dma_dac.dmasize - 1)) & ~((1<<20)-1)) - panic("solo1: buffer crosses 1M boundary"); - outl(va, s->iobase); - /* warning: s->dma_dac.dmasize & 0xffff must not be zero! i.e. this limits us to a 32k buffer */ - outw(s->dma_dac.dmasize, s->iobase+4); - c = - s->dma_dac.fragsamples; - write_mixer(s, 0x74, c); - write_mixer(s, 0x76, c >> 8); - outb(0xa, s->iobase+6); - s->dma_dac.ready = 1; - return 0; -} - -static inline 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; - } - memset(((char *)buf) + bptr, c, len); -} - -/* call with spinlock held! */ - -static void solo1_update_ptr(struct solo1_state *s) -{ - int diff; - unsigned hwptr; - - /* update ADC pointer */ - if (s->ena & FMODE_READ) { - hwptr = (s->dma_adc.dmasize - 1 - inw(s->ddmabase+4)) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; -#if 0 - printk(KERN_DEBUG "solo1: rd: hwptr %u swptr %u dmasize %u count %u\n", - s->dma_adc.hwptr, s->dma_adc.swptr, s->dma_adc.dmasize, s->dma_adc.count); -#endif - if (s->dma_adc.mapped) { - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - } else { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - s->ena &= ~FMODE_READ; - write_ctrl(s, 0xb8, 0xe); - s->dma_adc.error++; - } - if (s->dma_adc.count > 0) - wake_up(&s->dma_adc.wait); - } - } - /* update DAC pointer */ - if (s->ena & FMODE_WRITE) { - hwptr = (s->dma_dac.dmasize - inw(s->iobase+4)) % s->dma_dac.dmasize; - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; -#if 0 - printk(KERN_DEBUG "solo1: wr: hwptr %u swptr %u dmasize %u count %u\n", - s->dma_dac.hwptr, s->dma_dac.swptr, s->dma_dac.dmasize, s->dma_dac.count); -#endif - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - wake_up(&s->dma_dac.wait); - } else { - s->dma_dac.count -= diff; - if (s->dma_dac.count <= 0) { - s->ena &= ~FMODE_WRITE; - write_mixer(s, 0x78, 0x12); - s->dma_dac.error++; - } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { - clear_advance(s->dma_dac.rawbuf, s->dma_dac.dmasize, s->dma_dac.swptr, - s->dma_dac.fragsize, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80); - s->dma_dac.endcleared = 1; - } - if (s->dma_dac.count < (signed)s->dma_dac.dmasize) - wake_up(&s->dma_dac.wait); - } - } -} - -/* --------------------------------------------------------------------- */ - -static void prog_codec(struct solo1_state *s) -{ - unsigned long flags; - int fdiv, filter; - unsigned char c; - - reset_ctrl(s); - write_seq(s, 0xd3); - /* program sampling rates */ - filter = s->rate * 9 / 20; /* Set filter roll-off to 90% of rate/2 */ - fdiv = 256 - 7160000 / (filter * 82); - spin_lock_irqsave(&s->lock, flags); - write_ctrl(s, 0xa1, s->clkdiv); - write_ctrl(s, 0xa2, fdiv); - write_mixer(s, 0x70, s->clkdiv); - write_mixer(s, 0x72, fdiv); - /* program ADC parameters */ - write_ctrl(s, 0xb8, 0xe); - write_ctrl(s, 0xb9, /*0x1*/0); - write_ctrl(s, 0xa8, (s->channels > 1) ? 0x11 : 0x12); - c = 0xd0; - if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) - c |= 0x04; - if (s->fmt & (AFMT_S16_LE | AFMT_S8)) - c |= 0x20; - if (s->channels > 1) - c ^= 0x48; - write_ctrl(s, 0xb7, (c & 0x70) | 1); - write_ctrl(s, 0xb7, c); - write_ctrl(s, 0xb1, 0x50); - write_ctrl(s, 0xb2, 0x50); - /* program DAC parameters */ - c = 0x40; - if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) - c |= 1; - if (s->fmt & (AFMT_S16_LE | AFMT_S8)) - c |= 4; - if (s->channels > 1) - c |= 2; - write_mixer(s, 0x7a, c); - write_mixer(s, 0x78, 0x10); - s->ena = 0; - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT "solo1: invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != SOLO1_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - -static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long arg) -{ - static const unsigned int mixer_src[8] = { - SOUND_MASK_MIC, SOUND_MASK_MIC, SOUND_MASK_CD, SOUND_MASK_VOLUME, - SOUND_MASK_MIC, 0, SOUND_MASK_LINE, 0 - }; - static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_PCM] = 1, /* voice */ - [SOUND_MIXER_SYNTH] = 2, /* FM */ - [SOUND_MIXER_CD] = 3, /* CD */ - [SOUND_MIXER_LINE] = 4, /* Line */ - [SOUND_MIXER_LINE1] = 5, /* AUX */ - [SOUND_MIXER_MIC] = 6, /* Mic */ - [SOUND_MIXER_LINE2] = 7, /* Mono in */ - [SOUND_MIXER_SPEAKER] = 8, /* Speaker */ - [SOUND_MIXER_RECLEV] = 9, /* Recording level */ - [SOUND_MIXER_VOLUME] = 10 /* Master Volume */ - }; - static const unsigned char mixreg[] = { - 0x7c, /* voice */ - 0x36, /* FM */ - 0x38, /* CD */ - 0x3e, /* Line */ - 0x3a, /* AUX */ - 0x1a, /* Mic */ - 0x6d /* Mono in */ - }; - unsigned char l, r, rl, rr, vidx; - int i, val; - - VALIDATE_STATE(s); - - if (cmd == SOUND_MIXER_PRIVATE1) { - /* enable/disable/query mixer preamp */ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != -1) { - val = val ? 0xff : 0xf7; - write_mixer(s, 0x7d, (read_mixer(s, 0x7d) | 0x08) & val); - } - val = (read_mixer(s, 0x7d) & 0x08) ? 1 : 0; - return put_user(val, (int *)arg); - } - if (cmd == SOUND_MIXER_PRIVATE2) { - /* enable/disable/query spatializer */ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != -1) { - val &= 0x3f; - write_mixer(s, 0x52, val); - write_mixer(s, 0x50, val ? 0x08 : 0); - } - return put_user(read_mixer(s, 0x52), (int *)arg); - } - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - 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; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - 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; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *)arg); - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - if (_SIOC_DIR(cmd) == _SIOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - return put_user(mixer_src[read_mixer(s, 0x1c) & 7], (int *)arg); - - case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ - return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | - SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | - SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV | - SOUND_MASK_SPEAKER, (int *)arg); - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME, (int *)arg); - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | - SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | - SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | 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 (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) - return -EINVAL; - s->mix.modcnt++; - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ -#if 0 - { - static const unsigned char regs[] = { - 0x1c, 0x1a, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x60, 0x62, 0x6d, 0x7c - }; - int i; - - for (i = 0; i < sizeof(regs); i++) - printk(KERN_DEBUG "solo1: mixer reg 0x%02x: 0x%02x\n", - regs[i], read_mixer(s, regs[i])); - printk(KERN_DEBUG "solo1: ctrl reg 0x%02x: 0x%02x\n", - 0xb4, read_ctrl(s, 0xb4)); - } -#endif - if (get_user(val, (int *)arg)) - return -EFAULT; - i = hweight32(val); - if (i == 0) - return 0; - else if (i > 1) - val &= ~mixer_src[read_mixer(s, 0x1c) & 7]; - for (i = 0; i < 8; i++) { - if (mixer_src[i] & val) - break; - } - if (i > 7) - return 0; - write_mixer(s, 0x1c, i); - return 0; - - case SOUND_MIXER_VOLUME: - 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; - if (l < 6) { - rl = 0x40; - l = 0; - } else { - rl = (l * 2 - 11) / 3; - l = (rl * 3 + 11) / 2; - } - if (r < 6) { - rr = 0x40; - r = 0; - } else { - rr = (r * 2 - 11) / 3; - r = (rr * 3 + 11) / 2; - } - write_mixer(s, 0x60, rl); - write_mixer(s, 0x62, rr); -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[9] = ((unsigned int)r << 8) | l; -#else - s->mix.vol[9] = val; -#endif - return put_user(s->mix.vol[9], (int *)arg); - - case SOUND_MIXER_SPEAKER: - if (get_user(val, (int *)arg)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - else if (l < 2) - l = 2; - rl = (l - 2) / 14; - l = rl * 14 + 2; - write_mixer(s, 0x3c, rl); -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[7] = l * 0x101; -#else - s->mix.vol[7] = val; -#endif - return put_user(s->mix.vol[7], (int *)arg); - - case SOUND_MIXER_RECLEV: - if (get_user(val, (int *)arg)) - return -EFAULT; - l = (val << 1) & 0x1fe; - if (l > 200) - l = 200; - else if (l < 5) - l = 5; - r = (val >> 7) & 0x1fe; - if (r > 200) - r = 200; - else if (r < 5) - r = 5; - rl = (l - 5) / 13; - rr = (r - 5) / 13; - r = (rl * 13 + 5) / 2; - l = (rr * 13 + 5) / 2; - write_ctrl(s, 0xb4, (rl << 4) | rr); -#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); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - l = (val << 1) & 0x1fe; - if (l > 200) - l = 200; - else if (l < 5) - l = 5; - r = (val >> 7) & 0x1fe; - if (r > 200) - r = 200; - else if (r < 5) - r = 5; - rl = (l - 5) / 13; - rr = (r - 5) / 13; - r = (rl * 13 + 5) / 2; - l = (rr * 13 + 5) / 2; - write_mixer(s, mixreg[vidx-1], (rl << 4) | rr); -#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 int solo1_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct solo1_state *s = NULL; - struct pci_dev *pci_dev; - - pci_for_each_dev(pci_dev) { - struct pci_driver *drvr; - drvr = pci_dev_driver (pci_dev); - if (drvr != &solo1_driver) - continue; - s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - continue; - if (s->dev_mixer == minor) - break; - } - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - return 0; -} - -static int solo1_release_mixdev(struct inode *inode, struct file *file) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - - VALIDATE_STATE(s); - return 0; -} - -static int solo1_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - return mixer_ioctl((struct solo1_state *)file->private_data, cmd, arg); -} - -static /*const*/ struct file_operations solo1_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: solo1_ioctl_mixdev, - open: solo1_open_mixdev, - release: solo1_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct solo1_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count; - unsigned tmo; - - if (s->dma_dac.mapped) - return 0; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - 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) { - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate; - if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) - tmo >>= 1; - if (s->channels > 1) - tmo >>= 1; - if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "solo1: dma timed out??\n"); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - 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; - add_wait_queue(&s->dma_adc.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x cnt: %u\n", - read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc), cnt); -#endif - if (cnt <= 0) { - if (s->dma_adc.enabled) - start_adc(s); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" - KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" - KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" - KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", - read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), - read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), - inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); -#endif - if (inb(s->ddmabase+15) & 1) - printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" - KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" - KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" - KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", - read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), - read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), - inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); -#endif - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_adc.enabled) - start_adc(s); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x\n", - read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc)); -#endif - } - remove_wait_queue(&s->dma_adc.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -static ssize_t solo1_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - 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; -#if 0 - printk(KERN_DEBUG "solo1_write: reg 70: 0x%02x 71: 0x%02x 72: 0x%02x 74: 0x%02x 76: 0x%02x 78: 0x%02x 7A: 0x%02x\n" - KERN_DEBUG "solo1_write: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x SBstat: 0x%02x\n", - read_mixer(s, 0x70), read_mixer(s, 0x71), read_mixer(s, 0x72), read_mixer(s, 0x74), read_mixer(s, 0x76), - read_mixer(s, 0x78), read_mixer(s, 0x7a), inl(s->iobase), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); - printk(KERN_DEBUG "solo1_write: reg 78: 0x%02x reg 7A: 0x%02x DMAcnt: 0x%04x DMAstat: 0x%02x SBstat: 0x%02x\n", - read_mixer(s, 0x78), read_mixer(s, 0x7a), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); -#endif - ret = 0; - add_wait_queue(&s->dma_dac.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - swptr = s->dma_dac.swptr; - cnt = s->dma_dac.dmasize-swptr; - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_dac.enabled) - start_dac(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_dac.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_dac.enabled) - start_dac(s); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int solo1_poll(struct file *file, struct poll_table_struct *wait) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - 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) { - if (!s->dma_adc.ready && prog_dmabuf_adc(s)) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - 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; - } - } - 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) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - - -static int solo1_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - struct dmabuf *db; - int ret = -EINVAL; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf_dac(s)) != 0) - goto out; - db = &s->dma_dac; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf_adc(s)) != 0) - goto out; - db = &s->dma_adc; - } else - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) - goto out; - ret = -EAGAIN; - if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) - goto out; - db->mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret, count; - int div1, div2; - unsigned rate1, rate2; - - 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: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_SYNC: - 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: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(); - s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - prog_codec(s); - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val >= 0) { - stop_adc(s); - stop_dac(s); - s->dma_adc.ready = s->dma_dac.ready = 0; - /* program sampling rates */ - if (val > 48000) - val = 48000; - if (val < 6300) - val = 6300; - div1 = (768000 + val / 2) / val; - rate1 = (768000 + div1 / 2) / div1; - div1 = -div1; - div2 = (793800 + val / 2) / val; - rate2 = (793800 + div2 / 2) / div2; - div2 = (-div2) & 0x7f; - if (abs(val - rate2) < abs(val - rate1)) { - rate1 = rate2; - div1 = div2; - } - s->rate = rate1; - s->clkdiv = div1; - prog_codec(s); - } - return put_user(s->rate, (int *)arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - stop_adc(s); - stop_dac(s); - s->dma_adc.ready = s->dma_dac.ready = 0; - /* program channels */ - s->channels = val ? 2 : 1; - prog_codec(s); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) { - stop_adc(s); - stop_dac(s); - s->dma_adc.ready = s->dma_dac.ready = 0; - /* program channels */ - s->channels = (val >= 2) ? 2 : 1; - prog_codec(s); - } - return put_user(s->channels, (int *)arg); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != AFMT_QUERY) { - stop_adc(s); - stop_dac(s); - s->dma_adc.ready = s->dma_dac.ready = 0; - /* program format */ - if (val != AFMT_S16_LE && val != AFMT_U16_LE && - val != AFMT_S8 && val != AFMT_U8) - val = AFMT_U8; - s->fmt = val; - prog_codec(s); - } - return put_user(s->fmt, (int *)arg); - - case SNDCTL_DSP_POST: - 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; - s->dma_dac.enabled = 1; - start_adc(s); - if (inb(s->ddmabase+15) & 1) - printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); - } else { - s->dma_dac.enabled = 0; - 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; - s->dma_dac.enabled = 1; - start_dac(s); - } else { - s->dma_dac.enabled = 0; - 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)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - count = s->dma_dac.count; - 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; - 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)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - 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 && (val = prog_dmabuf_dac(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - count = s->dma_dac.count; - 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; - if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; - 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 && (val = prog_dmabuf_dac(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - count = s->dma_dac.count; - if (count < 0) - count = 0; - cinfo.blocks = 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); -#if 0 - printk(KERN_DEBUG "esssolo1: GETOPTR: bytes %u blocks %u ptr %u, buforder %u numfrag %u fragshift %u\n" - KERN_DEBUG "esssolo1: swptr %u count %u fragsize %u dmasize %u fragsamples %u\n", - cinfo.bytes, cinfo.blocks, cinfo.ptr, s->dma_dac.buforder, s->dma_dac.numfrag, s->dma_dac.fragshift, - s->dma_dac.swptr, s->dma_dac.count, s->dma_dac.fragsize, s->dma_dac.dmasize, s->dma_dac.fragsamples); -#endif - 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; - 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) { - 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 (file->f_mode & FMODE_WRITE) { - 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; - } - 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) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user(s->rate, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - return put_user(s->channels, (int *)arg); - - case SOUND_PCM_READ_BITS: - return put_user((s->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 solo1_release(struct inode *inode, struct file *file) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - - VALIDATE_STATE(s); - 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); - outb(0, s->iobase+6); /* disable DMA */ - dealloc_dmabuf(s, &s->dma_dac); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - outb(1, s->ddmabase+0xf); /* mask DMA channel */ - outb(0, s->ddmabase+0xd); /* DMA master clear */ - dealloc_dmabuf(s, &s->dma_adc); - } - s->open_mode &= ~(FMODE_READ | FMODE_WRITE); - wake_up(&s->open_wait); - up(&s->open_sem); - unlock_kernel(); - return 0; -} - -static int solo1_open(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - struct solo1_state *s = NULL; - struct pci_dev *pci_dev; - - pci_for_each_dev(pci_dev) { - struct pci_driver *drvr; - - drvr = pci_dev_driver(pci_dev); - if (drvr != &solo1_driver) - continue; - s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - continue; - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & (FMODE_READ | FMODE_WRITE)) { - 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); - } - s->fmt = AFMT_U8; - s->channels = 1; - s->rate = 8000; - s->clkdiv = 96 | 0x80; - s->ena = 0; - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - s->dma_adc.enabled = 1; - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; - s->dma_dac.enabled = 1; - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - up(&s->open_sem); - prog_codec(s); - return 0; -} - -static /*const*/ struct file_operations solo1_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: solo1_read, - write: solo1_write, - poll: solo1_poll, - ioctl: solo1_ioctl, - mmap: solo1_mmap, - open: solo1_open, - release: solo1_release, -}; - -/* --------------------------------------------------------------------- */ - -/* hold spinlock for the following! */ -static void solo1_handle_midi(struct solo1_state *s) -{ - unsigned char ch; - int wake; - - if (!(s->mpubase)) - return; - wake = 0; - while (!(inb(s->mpubase+1) & 0x80)) { - ch = inb(s->mpubase); - if (s->midi.icnt < MIDIINBUF) { - s->midi.ibuf[s->midi.iwr] = ch; - s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; - s->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&s->midi.iwait); - wake = 0; - while (!(inb(s->mpubase+1) & 0x40) && s->midi.ocnt > 0) { - outb(s->midi.obuf[s->midi.ord], s->mpubase); - s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; - s->midi.ocnt--; - if (s->midi.ocnt < MIDIOUTBUF-16) - wake = 1; - } - if (wake) - wake_up(&s->midi.owait); -} - -static void solo1_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct solo1_state *s = (struct solo1_state *)dev_id; - unsigned int intsrc; - - /* fastpath out, to ease interrupt sharing */ - intsrc = inb(s->iobase+7); /* get interrupt source(s) */ - if (!intsrc) - return; - (void)inb(s->sbbase+0xe); /* clear interrupt */ - spin_lock(&s->lock); - /* clear audio interrupts first */ - if (intsrc & 0x20) - write_mixer(s, 0x7a, read_mixer(s, 0x7a) & 0x7f); - solo1_update_ptr(s); - solo1_handle_midi(s); - spin_unlock(&s->lock); -} - -static void solo1_midi_timer(unsigned long data) -{ - struct solo1_state *s = (struct solo1_state *)data; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - solo1_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - s->midi.timer.expires = jiffies+1; - add_timer(&s->midi.timer); -} - -/* --------------------------------------------------------------------- */ - -static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.iwait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.ird; - cnt = MIDIINBUF - ptr; - if (s->midi.icnt < cnt) - cnt = s->midi.icnt; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIINBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.ird = ptr; - s->midi.icnt -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - break; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.iwait, &wait); - return ret; -} - -static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.owait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.owr; - cnt = MIDIOUTBUF - ptr; - if (s->midi.ocnt + cnt > MIDIOUTBUF) - cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) { - __set_current_state(TASK_INTERRUPTIBLE); - solo1_handle_midi(s); - } - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIOUTBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.owr = ptr; - s->midi.ocnt += cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&s->lock, flags); - solo1_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.owait, &wait); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int solo1_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_flags & FMODE_WRITE) - poll_wait(file, &s->midi.owait, wait); - if (file->f_flags & FMODE_READ) - poll_wait(file, &s->midi.iwait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_flags & FMODE_READ) { - if (s->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_flags & FMODE_WRITE) { - if (s->midi.ocnt < MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int solo1_midi_open(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct solo1_state *s = NULL; - struct pci_dev *pci_dev; - - pci_for_each_dev(pci_dev) { - struct pci_driver *drvr; - - drvr = pci_dev_driver(pci_dev); - if (drvr != &solo1_driver) - continue; - s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - continue; - if (s->dev_midi == minor) - break; - } - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - 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); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - outb(0xff, s->mpubase+1); /* reset command */ - outb(0x3f, s->mpubase+1); /* uart command */ - if (!(inb(s->mpubase+1) & 0x80)) - inb(s->mpubase); - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - outb(0xb0, s->iobase + 7); /* enable A1, A2, MPU irq's */ - init_timer(&s->midi.timer); - s->midi.timer.expires = jiffies+1; - s->midi.timer.data = (unsigned long)s; - s->midi.timer.function = solo1_midi_timer; - add_timer(&s->midi.timer); - } - if (file->f_mode & FMODE_READ) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - } - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); - up(&s->open_sem); - return 0; -} - -static int solo1_midi_release(struct inode *inode, struct file *file) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - VALIDATE_STATE(s); - - lock_kernel(); - if (file->f_mode & FMODE_WRITE) { - add_wait_queue(&s->midi.owait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->midi.ocnt; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - 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; - } - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "solo1: midi timed out??\n"); - } - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - } - down(&s->open_sem); - s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - outb(0x30, s->iobase + 7); /* enable A1, A2 irq's */ - del_timer(&s->midi.timer); - } - spin_unlock_irqrestore(&s->lock, flags); - wake_up(&s->open_wait); - up(&s->open_sem); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations solo1_midi_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: solo1_midi_read, - write: solo1_midi_write, - poll: solo1_midi_poll, - open: solo1_midi_open, - release: solo1_midi_release, -}; - -/* --------------------------------------------------------------------- */ - -static int solo1_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - static const unsigned char op_offset[18] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 - }; - struct solo1_state *s = (struct solo1_state *)file->private_data; - struct dm_fm_voice v; - struct dm_fm_note n; - struct dm_fm_params p; - unsigned int io; - unsigned int regb; - - switch (cmd) { - case FM_IOCTL_RESET: - for (regb = 0xb0; regb < 0xb9; regb++) { - outb(regb, s->sbbase); - outb(0, s->sbbase+1); - outb(regb, s->sbbase+2); - outb(0, s->sbbase+3); - } - return 0; - - case FM_IOCTL_PLAY_NOTE: - if (copy_from_user(&n, (void *)arg, sizeof(n))) - return -EFAULT; - if (n.voice >= 18) - return -EINVAL; - if (n.voice >= 9) { - regb = n.voice - 9; - io = s->sbbase+2; - } else { - regb = n.voice; - io = s->sbbase; - } - outb(0xa0 + regb, io); - outb(n.fnum & 0xff, io+1); - outb(0xb0 + regb, io); - outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); - return 0; - - case FM_IOCTL_SET_VOICE: - if (copy_from_user(&v, (void *)arg, sizeof(v))) - return -EFAULT; - if (v.voice >= 18) - return -EINVAL; - regb = op_offset[v.voice]; - io = s->sbbase + ((v.op & 1) << 1); - outb(0x20 + regb, io); - outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | - ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); - outb(0x40 + regb, io); - outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); - outb(0x60 + regb, io); - outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); - outb(0x80 + regb, io); - outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); - outb(0xe0 + regb, io); - outb(v.waveform & 0x7, io+1); - if (n.voice >= 9) { - regb = n.voice - 9; - io = s->sbbase+2; - } else { - regb = n.voice; - io = s->sbbase; - } - outb(0xc0 + regb, io); - outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | - (v.connection & 1), io+1); - return 0; - - case FM_IOCTL_SET_PARAMS: - if (copy_from_user(&p, (void *)arg, sizeof(p))) - return -EFAULT; - outb(0x08, s->sbbase); - outb((p.kbd_split & 1) << 6, s->sbbase+1); - outb(0xbd, s->sbbase); - outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | - ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->sbbase+1); - return 0; - - case FM_IOCTL_SET_OPL: - outb(4, s->sbbase+2); - outb(arg, s->sbbase+3); - return 0; - - case FM_IOCTL_SET_MODE: - outb(5, s->sbbase+2); - outb(arg & 1, s->sbbase+3); - return 0; - - default: - return -EINVAL; - } -} - -static int solo1_dmfm_open(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - struct solo1_state *s = NULL; - struct pci_dev *pci_dev; - - pci_for_each_dev(pci_dev) { - struct pci_driver *drvr; - - drvr = pci_dev_driver(pci_dev); - if (drvr != &solo1_driver) - continue; - s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - continue; - if (s->dev_dmfm == minor) - break; - } - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & FMODE_DMFM) { - 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); - } - if (!request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1")) { - up(&s->open_sem); - printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n"); - return -EBUSY; - } - /* init the stuff */ - outb(1, s->sbbase); - outb(0x20, s->sbbase+1); /* enable waveforms */ - outb(4, s->sbbase+2); - outb(0, s->sbbase+3); /* no 4op enabled */ - outb(5, s->sbbase+2); - outb(1, s->sbbase+3); /* enable OPL3 */ - s->open_mode |= FMODE_DMFM; - up(&s->open_sem); - return 0; -} - -static int solo1_dmfm_release(struct inode *inode, struct file *file) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - unsigned int regb; - - VALIDATE_STATE(s); - lock_kernel(); - down(&s->open_sem); - s->open_mode &= ~FMODE_DMFM; - for (regb = 0xb0; regb < 0xb9; regb++) { - outb(regb, s->sbbase); - outb(0, s->sbbase+1); - outb(regb, s->sbbase+2); - outb(0, s->sbbase+3); - } - release_region(s->sbbase, FMSYNTH_EXTENT); - wake_up(&s->open_wait); - up(&s->open_sem); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations solo1_dmfm_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: solo1_dmfm_ioctl, - open: solo1_dmfm_open, - release: solo1_dmfm_release, -}; - -/* --------------------------------------------------------------------- */ - -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_LINE2, 0x4040 }, - { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, - { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, - { SOUND_MIXER_WRITE_MIC, 0x4040 } -}; - -static int setup_solo1(struct solo1_state *s) -{ - struct pci_dev *pcidev = s->dev; - mm_segment_t fs; - int i, val; - - /* initialize DDMA base address */ - printk(KERN_DEBUG "solo1: ddma base address: 0x%lx\n", s->ddmabase); - pci_write_config_word(pcidev, 0x60, (s->ddmabase & (~0xf)) | 1); - /* set DMA policy to DDMA, IRQ emulation off (CLKRUN disabled for now) */ - pci_write_config_dword(pcidev, 0x50, 0); - /* disable legacy audio address decode */ - pci_write_config_word(pcidev, 0x40, 0x907f); - - /* initialize the chips */ - if (!reset_ctrl(s)) { - printk(KERN_ERR "esssolo1: cannot reset controller\n"); - return -1; - } - outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */ - - /* initialize mixer regs */ - write_mixer(s, 0x7f, 0); /* disable music digital recording */ - write_mixer(s, 0x7d, 0x0c); /* enable mic preamp, MONO_OUT is 2nd DAC right channel */ - write_mixer(s, 0x64, 0x45); /* volume control */ - write_mixer(s, 0x48, 0x10); /* enable music DAC/ES6xx interface */ - write_mixer(s, 0x50, 0); /* disable spatializer */ - write_mixer(s, 0x52, 0); - write_mixer(s, 0x14, 0); /* DAC1 minimum volume */ - write_mixer(s, 0x71, 0x20); /* enable new 0xA1 reg format */ - outb(0, s->ddmabase+0xd); /* DMA master clear */ - outb(1, s->ddmabase+0xf); /* mask channel */ - /*outb(0, s->ddmabase+0x8);*/ /* enable controller (enable is low active!!) */ - - pci_set_master(pcidev); /* enable bus mastering */ - - fs = get_fs(); - set_fs(KERNEL_DS); - 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); - } - val = 1; /* enable mic preamp */ - mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long)&val); - set_fs(fs); - return 0; -} - -static int -solo1_suspend(struct pci_dev *pci_dev, u32 state) { - struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - return 1; - outb(0, s->iobase+6); - /* DMA master clear */ - outb(0, s->ddmabase+0xd); - /* reset sequencer and FIFO */ - outb(3, s->sbbase+6); - /* turn off DDMA controller address space */ - pci_write_config_word(s->dev, 0x60, 0); - return 0; -} - -static int -solo1_resume(struct pci_dev *pci_dev) { - struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - return 1; - setup_solo1(s); - return 0; -} - -static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) -{ - struct solo1_state *s; - int ret; - - if ((ret=pci_enable_device(pcidev))) - return ret; - if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) || - !(pci_resource_flags(pcidev, 1) & IORESOURCE_IO) || - !(pci_resource_flags(pcidev, 2) & IORESOURCE_IO) || - !(pci_resource_flags(pcidev, 3) & IORESOURCE_IO)) - return -ENODEV; - if (pcidev->irq == 0) - return -ENODEV; - - /* Recording requires 24-bit DMA, so attempt to set dma mask - * to 24 bits first, then 32 bits (playback only) if that fails. - */ - if (pci_set_dma_mask(pcidev, 0x00ffffff) && - pci_set_dma_mask(pcidev, 0xffffffff)) { - printk(KERN_WARNING "solo1: architecture does not support 24bit or 32bit PCI busmaster DMA\n"); - return -ENODEV; - } - - if (!(s = kmalloc(sizeof(struct solo1_state), GFP_KERNEL))) { - printk(KERN_WARNING "solo1: out of memory\n"); - return -ENOMEM; - } - memset(s, 0, sizeof(struct solo1_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->midi.iwait); - init_waitqueue_head(&s->midi.owait); - init_MUTEX(&s->open_sem); - spin_lock_init(&s->lock); - s->magic = SOLO1_MAGIC; - s->dev = pcidev; - s->iobase = pci_resource_start(pcidev, 0); - s->sbbase = pci_resource_start(pcidev, 1); - s->vcbase = pci_resource_start(pcidev, 2); - s->ddmabase = s->vcbase + DDMABASE_OFFSET; - s->mpubase = pci_resource_start(pcidev, 3); - s->gameport.io = pci_resource_start(pcidev, 4); - s->irq = pcidev->irq; - ret = -EBUSY; - if (!request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1")) { - printk(KERN_ERR "solo1: io ports in use\n"); - goto err_region1; - } - if (!request_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT, "ESS Solo1")) { - printk(KERN_ERR "solo1: io ports in use\n"); - goto err_region2; - } - if (!request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1")) { - printk(KERN_ERR "solo1: io ports in use\n"); - goto err_region3; - } - if (!request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1")) { - printk(KERN_ERR "solo1: io ports in use\n"); - goto err_region4; - } - if (s->gameport.io && !request_region(s->gameport.io, GAMEPORT_EXTENT, "ESS Solo1")) { - printk(KERN_ERR "solo1: gameport io ports in use\n"); - s->gameport.io = 0; - } - if ((ret=request_irq(s->irq,solo1_interrupt,SA_SHIRQ,"ESS Solo1",s))) { - printk(KERN_ERR "solo1: irq %u in use\n", s->irq); - goto err_irq; - } - printk(KERN_INFO "solo1: joystick port at %#x\n", s->gameport.io+1); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1)) < 0) { - ret = s->dev_audio; - goto err_dev1; - } - if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1)) < 0) { - ret = s->dev_mixer; - goto err_dev2; - } - if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1)) < 0) { - ret = s->dev_midi; - goto err_dev3; - } - if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) { - ret = s->dev_dmfm; - goto err_dev4; - } - if (setup_solo1(s)) { - ret = -EIO; - goto err; - } - /* register gameport */ - gameport_register_port(&s->gameport); - /* store it in the driver field */ - pci_set_drvdata(pcidev, s); - return 0; - - err: - unregister_sound_dsp(s->dev_dmfm); - err_dev4: - unregister_sound_dsp(s->dev_midi); - err_dev3: - unregister_sound_mixer(s->dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR "solo1: initialisation error\n"); - free_irq(s->irq, s); - err_irq: - if (s->gameport.io) - release_region(s->gameport.io, GAMEPORT_EXTENT); - release_region(s->iobase, IOBASE_EXTENT); - err_region4: - release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); - err_region3: - release_region(s->ddmabase, DDMABASE_EXTENT); - err_region2: - release_region(s->mpubase, MPUBASE_EXTENT); - err_region1: - kfree(s); - return ret; -} - -static void __devinit solo1_remove(struct pci_dev *dev) -{ - struct solo1_state *s = pci_get_drvdata(dev); - - if (!s) - return; - /* stop DMA controller */ - outb(0, s->iobase+6); - outb(0, s->ddmabase+0xd); /* DMA master clear */ - outb(3, s->sbbase+6); /* reset sequencer and FIFO */ - synchronize_irq(); - pci_write_config_word(s->dev, 0x60, 0); /* turn off DDMA controller address space */ - free_irq(s->irq, s); - if (s->gameport.io) { - gameport_unregister_port(&s->gameport); - release_region(s->gameport.io, GAMEPORT_EXTENT); - } - release_region(s->iobase, IOBASE_EXTENT); - release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); - release_region(s->ddmabase, DDMABASE_EXTENT); - release_region(s->mpubase, MPUBASE_EXTENT); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->dev_mixer); - unregister_sound_midi(s->dev_midi); - unregister_sound_special(s->dev_dmfm); - kfree(s); - pci_set_drvdata(dev, NULL); -} - -static struct pci_device_id id_table[] __devinitdata = { - { PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver solo1_driver = { - name: "ESS Solo1", - id_table: id_table, - probe: solo1_probe, - remove: solo1_remove, - suspend: solo1_suspend, - resume: solo1_resume -}; - - -static int __init init_solo1(void) -{ - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - printk(KERN_INFO "solo1: version v0.19 time " __TIME__ " " __DATE__ "\n"); - if (!pci_register_driver(&solo1_driver)) { - pci_unregister_driver(&solo1_driver); - return -ENODEV; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("ESS Solo1 Driver"); -MODULE_LICENSE("GPL"); - - -static void __exit cleanup_solo1(void) -{ - printk(KERN_INFO "solo1: unloading\n"); - pci_unregister_driver(&solo1_driver); -} - -/* --------------------------------------------------------------------- */ - -module_init(init_solo1); -module_exit(cleanup_solo1); - diff -Nru a/drivers/sound/gus.h b/drivers/sound/gus.h --- a/drivers/sound/gus.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,30 +0,0 @@ -/* - * gus.h - * - * Copyright: Christoph Hellwig - * - */ - -#include "ad1848.h" - -/* From gus_card.c */ -int gus_set_midi_irq(int num); -void gusintr(int irq, void *dev_id, struct pt_regs * dummy); - -/* From gus_wave.c */ -int gus_wave_detect(int baseaddr); -void gus_wave_init(struct address_info *hw_config); -void gus_wave_unload (struct address_info *hw_config); -void gus_voice_irq(void); -void gus_write8(int reg, unsigned int data); -void guswave_dma_irq(void); -void gus_delay(void); -int gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg); -void gus_timer_command (unsigned int addr, unsigned int val); - -/* From gus_midi.c */ -void gus_midi_init(struct address_info *hw_config); -void gus_midi_interrupt(int dummy); - -/* From ics2101.c */ -int ics2101_mixer_init(void); diff -Nru a/drivers/sound/gus_card.c b/drivers/sound/gus_card.c --- a/drivers/sound/gus_card.c Tue Feb 19 18:08:56 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,298 +0,0 @@ -/* - * sound/gus_card.c - * - * Detection routine for the Gravis Ultrasound. - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * - * Frank van de Pol : Fixed GUS MAX interrupt handling, enabled simultanious - * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. - * Christoph Hellwig: Adapted to module_init/module_exit, simple cleanups. - * - * Status: - * Tested... - */ - - -#include -#include -#include - -#include "sound_config.h" - -#include "gus.h" -#include "gus_hw.h" - -void gusintr(int irq, void *dev_id, struct pt_regs *dummy); - -int gus_base = 0, gus_irq = 0, gus_dma = 0; -int gus_no_wave_dma = 0; -extern int gus_wave_volume; -extern int gus_pcm_volume; -extern int have_gus_max; -int gus_pnp_flag = 0; -#ifdef CONFIG_SOUND_GUS16 -static int db16 = 0; /* Has a Gus16 AD1848 on it */ -#endif - -static void __init attach_gus(struct address_info *hw_config) -{ - gus_wave_init(hw_config); - - request_region(hw_config->io_base, 16, "GUS"); - request_region(hw_config->io_base + 0x100, 12, "GUS"); /* 0x10c-> is MAX */ - - if (sound_alloc_dma(hw_config->dma, "GUS")) - printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma); - if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) - if (sound_alloc_dma(hw_config->dma2, "GUS(2)")) - printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma2); - gus_midi_init(hw_config); - if(request_irq(hw_config->irq, gusintr, 0, "Gravis Ultrasound", hw_config)<0) - printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq); - - return; -} - -static int __init probe_gus(struct address_info *hw_config) -{ - int irq; - int io_addr; - - if (hw_config->card_subtype == 1) - gus_pnp_flag = 1; - - irq = hw_config->irq; - - if (hw_config->card_subtype == 0) /* GUS/MAX/ACE */ - if (irq != 3 && irq != 5 && irq != 7 && irq != 9 && - irq != 11 && irq != 12 && irq != 15) - { - printk(KERN_ERR "GUS: Unsupported IRQ %d\n", irq); - return 0; - } - if (check_region(hw_config->io_base, 16)) - printk(KERN_ERR "GUS: I/O range conflict (1)\n"); - else if (check_region(hw_config->io_base + 0x100, 16)) - printk(KERN_ERR "GUS: I/O range conflict (2)\n"); - else if (gus_wave_detect(hw_config->io_base)) - return 1; - -#ifndef EXCLUDE_GUS_IODETECT - - /* - * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) - */ - - for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) - if (io_addr != hw_config->io_base) /* - * Already tested - */ - if (!check_region(io_addr, 16)) - if (!check_region(io_addr + 0x100, 16)) - if (gus_wave_detect(io_addr)) - { - hw_config->io_base = io_addr; - return 1; - } -#endif - - printk("NO GUS card found !\n"); - return 0; -} - -static void __exit unload_gus(struct address_info *hw_config) -{ - DDB(printk("unload_gus(%x)\n", hw_config->io_base)); - - gus_wave_unload(hw_config); - - release_region(hw_config->io_base, 16); - release_region(hw_config->io_base + 0x100, 12); /* 0x10c-> is MAX */ - free_irq(hw_config->irq, hw_config); - - sound_free_dma(hw_config->dma); - - if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) - sound_free_dma(hw_config->dma2); -} - -void gusintr(int irq, void *dev_id, struct pt_regs *dummy) -{ - unsigned char src; - extern int gus_timer_enabled; - - sti(); - -#ifdef CONFIG_SOUND_GUSMAX - if (have_gus_max) { - struct address_info *hw_config = dev_id; - adintr(irq, (void *)hw_config->slots[1], NULL); - } -#endif -#ifdef CONFIG_SOUND_GUS16 - if (db16) { - struct address_info *hw_config = dev_id; - adintr(irq, (void *)hw_config->slots[3], NULL); - } -#endif - - while (1) - { - if (!(src = inb(u_IrqStatus))) - return; - - if (src & DMA_TC_IRQ) - { - guswave_dma_irq(); - } - if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) - { - gus_midi_interrupt(0); - } - if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) - { - if (gus_timer_enabled) - sound_timer_interrupt(); - gus_write8(0x45, 0); /* Ack IRQ */ - gus_timer_command(4, 0x80); /* Reset IRQ flags */ - } - if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) - gus_voice_irq(); - } -} - -/* - * Some extra code for the 16 bit sampling option - */ - -#ifdef CONFIG_SOUND_GUS16 - -static int __init probe_gus_db16(struct address_info *hw_config) -{ - return ad1848_detect(hw_config->io_base, NULL, hw_config->osp); -} - -static void __init attach_gus_db16(struct address_info *hw_config) -{ - gus_pcm_volume = 100; - gus_wave_volume = 90; - - hw_config->slots[3] = ad1848_init("GUS 16 bit sampling", hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma, 0, - hw_config->osp, - THIS_MODULE); -} - -static void __exit unload_gus_db16(struct address_info *hw_config) -{ - - ad1848_unload(hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma, 0); - sound_unload_audiodev(hw_config->slots[3]); -} -#endif - -#ifdef CONFIG_SOUND_GUS16 -static int gus16 = 0; -#endif -#ifdef CONFIG_SOUND_GUSMAX -static int no_wave_dma = 0;/* Set if no dma is to be used for the - wave table (GF1 chip) */ -#endif - - -/* - * Note DMA2 of -1 has the right meaning in the GUS driver as well - * as here. - */ - -static struct address_info cfg; - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma16 = -1; /* Set this for modules that need it */ -static int __initdata type = 0; /* 1 for PnP */ - -MODULE_PARM(io, "i"); -MODULE_PARM(irq, "i"); -MODULE_PARM(dma, "i"); -MODULE_PARM(dma16, "i"); -MODULE_PARM(type, "i"); -#ifdef CONFIG_SOUND_GUSMAX -MODULE_PARM(no_wave_dma, "i"); -#endif -#ifdef CONFIG_SOUND_GUS16 -MODULE_PARM(db16, "i"); -MODULE_PARM(gus16, "i"); -#endif -MODULE_LICENSE("GPL"); - -static int __init init_gus(void) -{ - printk(KERN_INFO "Gravis Ultrasound audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma16; - cfg.card_subtype = type; -#ifdef CONFIG_SOUND_GUSMAX - gus_no_wave_dma = no_wave_dma; -#endif - - if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { - printk(KERN_ERR "I/O, IRQ, and DMA are mandatory\n"); - return -EINVAL; - } - -#ifdef CONFIG_SOUND_GUS16 - if (probe_gus_db16(&cfg) && gus16) { - /* FIXME: This can't work, can it ? -- Christoph */ - attach_gus_db16(&cfg); - db16 = 1; - } -#endif - if (!probe_gus(&cfg)) - return -ENODEV; - attach_gus(&cfg); - - return 0; -} - -static void __exit cleanup_gus(void) -{ -#ifdef CONFIG_SOUND_GUS16 - if (db16) - unload_gus_db16(&cfg); -#endif - unload_gus(&cfg); -} - -module_init(init_gus); -module_exit(cleanup_gus); - -#ifndef MODULE -static int __init setup_gus(char *str) -{ - /* io, irq, dma, dma2 */ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma16 = ints[4]; - - return 1; -} - -__setup("gus=", setup_gus); -#endif diff -Nru a/drivers/sound/gus_hw.h b/drivers/sound/gus_hw.h --- a/drivers/sound/gus_hw.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,50 +0,0 @@ - -/* - * I/O addresses - */ - -#define u_Base (gus_base + 0x000) -#define u_Mixer u_Base -#define u_Status (gus_base + 0x006) -#define u_TimerControl (gus_base + 0x008) -#define u_TimerData (gus_base + 0x009) -#define u_IRQDMAControl (gus_base + 0x00b) -#define u_MidiControl (gus_base + 0x100) -#define MIDI_RESET 0x03 -#define MIDI_ENABLE_XMIT 0x20 -#define MIDI_ENABLE_RCV 0x80 -#define u_MidiStatus u_MidiControl -#define MIDI_RCV_FULL 0x01 -#define MIDI_XMIT_EMPTY 0x02 -#define MIDI_FRAME_ERR 0x10 -#define MIDI_OVERRUN 0x20 -#define MIDI_IRQ_PEND 0x80 -#define u_MidiData (gus_base + 0x101) -#define u_Voice (gus_base + 0x102) -#define u_Command (gus_base + 0x103) -#define u_DataLo (gus_base + 0x104) -#define u_DataHi (gus_base + 0x105) -#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */ -#define u_MixSelect (gus_base + 0x506) /* registers. */ -#define u_IrqStatus u_Status -# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ -# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ -# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ -# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ -# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ -# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ -# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ - -#define ICS2101 1 -# define ICS_MIXDEVS 6 -# define DEV_MIC 0 -# define DEV_LINE 1 -# define DEV_CD 2 -# define DEV_GF1 3 -# define DEV_UNUSED 4 -# define DEV_VOL 5 - -# define CHN_LEFT 0 -# define CHN_RIGHT 1 -#define CS4231 2 -#define u_DRAMIO (gus_base + 0x107) diff -Nru a/drivers/sound/gus_linearvol.h b/drivers/sound/gus_linearvol.h --- a/drivers/sound/gus_linearvol.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,18 +0,0 @@ -static unsigned short gus_linearvol[128] = { - 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0, - 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0, - 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70, - 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0, - 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38, - 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78, - 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8, - 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8, - 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c, - 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c, - 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c, - 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c, - 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c, - 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc, - 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc, - 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc -}; diff -Nru a/drivers/sound/gus_midi.c b/drivers/sound/gus_midi.c --- a/drivers/sound/gus_midi.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,262 +0,0 @@ -/* - * sound/gus2_midi.c - * - * The low level driver for the GUS Midi Interface. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz - * Added __init to gus_midi_init() - */ - -#include -#include "sound_config.h" - -#include "gus.h" -#include "gus_hw.h" - -static int midi_busy = 0, input_opened = 0; -static int my_dev; -static int output_used = 0; -static volatile unsigned char gus_midi_control; - -static void (*midi_input_intr) (int dev, unsigned char data); - -static unsigned char tmp_queue[256]; -extern int gus_pnp_flag; -static volatile int qlen; -static volatile unsigned char qhead, qtail; -extern int gus_base, gus_irq, gus_dma; -extern int *gus_osp; - -static int GUS_MIDI_STATUS(void) -{ - return inb(u_MidiStatus); -} - -static int gus_midi_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev)) -{ - if (midi_busy) - { -/* printk("GUS: Midi busy\n");*/ - return -EBUSY; - } - outb((MIDI_RESET), u_MidiControl); - gus_delay(); - - gus_midi_control = 0; - input_opened = 0; - - if (mode == OPEN_READ || mode == OPEN_READWRITE) - if (!gus_pnp_flag) - { - gus_midi_control |= MIDI_ENABLE_RCV; - input_opened = 1; - } - outb((gus_midi_control), u_MidiControl); /* Enable */ - - midi_busy = 1; - qlen = qhead = qtail = output_used = 0; - midi_input_intr = input; - - return 0; -} - -static int dump_to_midi(unsigned char midi_byte) -{ - unsigned long flags; - int ok = 0; - - output_used = 1; - - save_flags(flags); - cli(); - - if (GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY) - { - ok = 1; - outb((midi_byte), u_MidiData); - } - else - { - /* - * Enable Midi xmit interrupts (again) - */ - gus_midi_control |= MIDI_ENABLE_XMIT; - outb((gus_midi_control), u_MidiControl); - } - - restore_flags(flags); - return ok; -} - -static void gus_midi_close(int dev) -{ - /* - * Reset FIFO pointers, disable intrs - */ - - outb((MIDI_RESET), u_MidiControl); - midi_busy = 0; -} - -static int gus_midi_out(int dev, unsigned char midi_byte) -{ - unsigned long flags; - - /* - * Drain the local queue first - */ - - save_flags(flags); - cli(); - - while (qlen && dump_to_midi(tmp_queue[qhead])) - { - qlen--; - qhead++; - } - restore_flags(flags); - - /* - * Output the byte if the local queue is empty. - */ - - if (!qlen) - if (dump_to_midi(midi_byte)) - return 1; /* - * OK - */ - - /* - * Put to the local queue - */ - - if (qlen >= 256) - return 0; /* - * Local queue full - */ - save_flags(flags); - cli(); - - tmp_queue[qtail] = midi_byte; - qlen++; - qtail++; - - restore_flags(flags); - return 1; -} - -static int gus_midi_start_read(int dev) -{ - return 0; -} - -static int gus_midi_end_read(int dev) -{ - return 0; -} - -static void gus_midi_kick(int dev) -{ -} - -static int gus_midi_buffer_status(int dev) -{ - unsigned long flags; - - if (!output_used) - return 0; - - save_flags(flags); - cli(); - - if (qlen && dump_to_midi(tmp_queue[qhead])) - { - qlen--; - qhead++; - } - restore_flags(flags); - return (qlen > 0) | !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY); -} - -#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static struct midi_operations gus_midi_operations = -{ - owner: THIS_MODULE, - info: {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS}, - converter: &std_midi_synth, - in_info: {0}, - open: gus_midi_open, - close: gus_midi_close, - outputc: gus_midi_out, - start_read: gus_midi_start_read, - end_read: gus_midi_end_read, - kick: gus_midi_kick, - buffer_status: gus_midi_buffer_status, -}; - -void __init gus_midi_init(struct address_info *hw_config) -{ - int dev = sound_alloc_mididev(); - - if (dev == -1) - { - printk(KERN_INFO "gus_midi: Too many midi devices detected\n"); - return; - } - outb((MIDI_RESET), u_MidiControl); - - std_midi_synth.midi_dev = my_dev = dev; - hw_config->slots[2] = dev; - midi_devs[dev] = &gus_midi_operations; - sequencer_init(); - return; -} - -void gus_midi_interrupt(int dummy) -{ - volatile unsigned char stat, data; - unsigned long flags; - int timeout = 10; - - save_flags(flags); - cli(); - - while (timeout-- > 0 && (stat = GUS_MIDI_STATUS()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY)) - { - if (stat & MIDI_RCV_FULL) - { - data = inb(u_MidiData); - if (input_opened) - midi_input_intr(my_dev, data); - } - if (stat & MIDI_XMIT_EMPTY) - { - while (qlen && dump_to_midi(tmp_queue[qhead])) - { - qlen--; - qhead++; - } - if (!qlen) - { - /* - * Disable Midi output interrupts, since no data in the buffer - */ - gus_midi_control &= ~MIDI_ENABLE_XMIT; - outb((gus_midi_control), u_MidiControl); - outb((gus_midi_control), u_MidiControl); - } - } - } - restore_flags(flags); -} diff -Nru a/drivers/sound/gus_vol.c b/drivers/sound/gus_vol.c --- a/drivers/sound/gus_vol.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,153 +0,0 @@ - -/* - * gus_vol.c - Compute volume for GUS. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -#include "sound_config.h" - -#include "gus.h" -#include "gus_linearvol.h" - -#define GUS_VOLUME gus_wave_volume - - -extern int gus_wave_volume; - -/* - * Calculate gus volume from note velocity, main volume, expression, and - * intrinsic patch volume given in patch library. Expression is multiplied - * in, so it emphasizes differences in note velocity, while main volume is - * added in -- I don't know whether this is right, but it seems reasonable to - * me. (In the previous stage, main volume controller messages were changed - * to expression controller messages, if they were found to be used for - * dynamic volume adjustments, so here, main volume can be assumed to be - * constant throughout a song.) - * - * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so - * we can give a big boost to very weak voices like nylon guitar and the - * basses. The normal value is 64. Strings are assigned lower values. - */ - -unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev) -{ - int i, m, n, x; - - - /* - * A voice volume of 64 is considered neutral, so adjust the main volume if - * something other than this neutral value was assigned in the patch - * library. - */ - x = 256 + 6 * (voicev - 64); - - /* - * Boost expression by voice volume above neutral. - */ - - if (voicev > 65) - xpn += voicev - 64; - xpn += (voicev - 64) / 2; - - /* - * Combine multiplicative and level components. - */ - x = vel * xpn * 6 + (voicev / 4) * x; - -#ifdef GUS_VOLUME - /* - * Further adjustment by installation-specific master volume control - * (default 60). - */ - x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; -#endif - -#ifdef GUS_USE_CHN_MAIN_VOLUME - /* - * Experimental support for the channel main volume - */ - - mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */ - x = (x * mainv * mainv) / 16384; -#endif - - if (x < 2) - return (0); - else if (x >= 65535) - return ((15 << 8) | 255); - - /* - * Convert to GUS's logarithmic form with 4 bit exponent i and 8 bit - * mantissa m. - */ - - n = x; - i = 7; - if (n < 128) - { - while (i > 0 && n < (1 << i)) - i--; - } - else - { - while (n > 255) - { - n >>= 1; - i++; - } - } - /* - * Mantissa is part of linear volume not expressed in exponent. (This is - * not quite like real logs -- I wonder if it's right.) - */ - m = x - (1 << i); - - /* - * Adjust mantissa to 8 bits. - */ - if (m > 0) - { - if (i > 8) - m >>= i - 8; - else if (i < 8) - m <<= 8 - i; - } - return ((i << 8) + m); -} - -/* - * Volume-values are interpreted as linear values. Volume is based on the - * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in) - * and the volume set by the mixer-device (default 60%). - */ - -unsigned short gus_linear_vol(int vol, int mainvol) -{ - int mixer_mainvol; - - if (vol <= 0) - vol = 0; - else if (vol >= 127) - vol = 127; - -#ifdef GUS_VOLUME - mixer_mainvol = GUS_VOLUME; -#else - mixer_mainvol = 100; -#endif - -#ifdef GUS_USE_CHN_MAIN_VOLUME - if (mainvol <= 0) - mainvol = 0; - else if (mainvol >= 127) - mainvol = 127; -#else - mainvol = 127; -#endif - return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100]; -} diff -Nru a/drivers/sound/gus_wave.c b/drivers/sound/gus_wave.c --- a/drivers/sound/gus_wave.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3540 +0,0 @@ -/* - * sound/gus_wave.c - * - * Driver for the Gravis UltraSound wave table synth. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Frank van de Pol : Fixed GUS MAX interrupt handling. Enabled simultanious - * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. - * Bartlomiej Zolnierkiewicz : added some __init/__exit - */ - -#include -#include - -#define GUSPNP_AUTODETECT - -#include "sound_config.h" -#include - -#include "gus.h" -#include "gus_hw.h" - -#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024)) - -#define MAX_SAMPLE 150 -#define MAX_PATCH 256 - -#define NOT_SAMPLE 0xffff - -struct voice_info -{ - unsigned long orig_freq; - unsigned long current_freq; - unsigned long mode; - int fixed_pitch; - int bender; - int bender_range; - int panning; - int midi_volume; - unsigned int initial_volume; - unsigned int current_volume; - int loop_irq_mode, loop_irq_parm; -#define LMODE_FINISH 1 -#define LMODE_PCM 2 -#define LMODE_PCM_STOP 3 - int volume_irq_mode, volume_irq_parm; -#define VMODE_HALT 1 -#define VMODE_ENVELOPE 2 -#define VMODE_START_NOTE 3 - - int env_phase; - unsigned char env_rate[6]; - unsigned char env_offset[6]; - - /* - * Volume computation parameters for gus_adagio_vol() - */ - int main_vol, expression_vol, patch_vol; - - /* Variables for "Ultraclick" removal */ - int dev_pending, note_pending, volume_pending, - sample_pending; - char kill_pending; - long offset_pending; - -}; - -static struct voice_alloc_info *voice_alloc; -static struct address_info *gus_hw_config; -extern int gus_base; -extern int gus_irq, gus_dma; -extern int gus_pnp_flag; -extern int gus_no_wave_dma; -static int gus_dma2 = -1; -static int dual_dma_mode = 0; -static long gus_mem_size = 0; -static long free_mem_ptr = 0; -static int gus_busy = 0; -static int gus_no_dma = 0; -static int nr_voices = 0; -static int gus_devnum = 0; -static int volume_base, volume_scale, volume_method; -static int gus_recmask = SOUND_MASK_MIC; -static int recording_active = 0; -static int only_read_access = 0; -static int only_8_bits = 0; - -int iw_mode = 0; -int gus_wave_volume = 60; -int gus_pcm_volume = 80; -int have_gus_max = 0; -static int gus_line_vol = 100, gus_mic_vol = 0; -static unsigned char mix_image = 0x00; - -int gus_timer_enabled = 0; - -/* - * Current version of this driver doesn't allow synth and PCM functions - * at the same time. The active_device specifies the active driver - */ - -static int active_device = 0; - -#define GUS_DEV_WAVE 1 /* Wave table synth */ -#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */ -#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */ - -static int gus_audio_speed; -static int gus_audio_channels; -static int gus_audio_bits; -static int gus_audio_bsize; -static char bounce_buf[8 * 1024]; /* Must match value set to max_fragment */ - -static DECLARE_WAIT_QUEUE_HEAD(dram_sleeper); - -/* - * Variables and buffers for PCM output - */ - -#define MAX_PCM_BUFFERS (128*MAX_REALTIME_FACTOR) /* Don't change */ - -static int pcm_bsize, pcm_nblk, pcm_banksize; -static int pcm_datasize[MAX_PCM_BUFFERS]; -static volatile int pcm_head, pcm_tail, pcm_qlen; -static volatile int pcm_active; -static volatile int dma_active; -static int pcm_opened = 0; -static int pcm_current_dev; -static int pcm_current_block; -static unsigned long pcm_current_buf; -static int pcm_current_count; -static int pcm_current_intrflag; - -extern int *gus_osp; - -static struct voice_info voices[32]; - -static int freq_div_table[] = -{ - 44100, /* 14 */ - 41160, /* 15 */ - 38587, /* 16 */ - 36317, /* 17 */ - 34300, /* 18 */ - 32494, /* 19 */ - 30870, /* 20 */ - 29400, /* 21 */ - 28063, /* 22 */ - 26843, /* 23 */ - 25725, /* 24 */ - 24696, /* 25 */ - 23746, /* 26 */ - 22866, /* 27 */ - 22050, /* 28 */ - 21289, /* 29 */ - 20580, /* 30 */ - 19916, /* 31 */ - 19293 /* 32 */ -}; - -static struct patch_info *samples = NULL; -static long sample_ptrs[MAX_SAMPLE + 1]; -static int sample_map[32]; -static int free_sample; -static int mixer_type = 0; - - -static int patch_table[MAX_PATCH]; -static int patch_map[32]; - -static struct synth_info gus_info = { - "Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, - 0, 16, 0, MAX_PATCH -}; - -static void gus_poke(long addr, unsigned char data); -static void compute_and_set_volume(int voice, int volume, int ramp_time); -extern unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev); -extern unsigned short gus_linear_vol(int vol, int mainvol); -static void compute_volume(int voice, int volume); -static void do_volume_irq(int voice); -static void set_input_volumes(void); -static void gus_tmr_install(int io_base); - -#define INSTANT_RAMP -1 /* Instant change. No ramping */ -#define FAST_RAMP 0 /* Fastest possible ramp */ - -static void reset_sample_memory(void) -{ - int i; - - for (i = 0; i <= MAX_SAMPLE; i++) - sample_ptrs[i] = -1; - for (i = 0; i < 32; i++) - sample_map[i] = -1; - for (i = 0; i < 32; i++) - patch_map[i] = -1; - - gus_poke(0, 0); /* Put a silent sample to the beginning */ - gus_poke(1, 0); - free_mem_ptr = 2; - - free_sample = 0; - - for (i = 0; i < MAX_PATCH; i++) - patch_table[i] = NOT_SAMPLE; -} - -void gus_delay(void) -{ - int i; - - for (i = 0; i < 7; i++) - inb(u_DRAMIO); -} - -static void gus_poke(long addr, unsigned char data) -{ /* Writes a byte to the DRAM */ - unsigned long flags; - - save_flags(flags); - cli(); - outb((0x43), u_Command); - outb((addr & 0xff), u_DataLo); - outb(((addr >> 8) & 0xff), u_DataHi); - - outb((0x44), u_Command); - outb(((addr >> 16) & 0xff), u_DataHi); - outb((data), u_DRAMIO); - restore_flags(flags); -} - -static unsigned char gus_peek(long addr) -{ /* Reads a byte from the DRAM */ - unsigned long flags; - unsigned char tmp; - - save_flags(flags); - cli(); - outb((0x43), u_Command); - outb((addr & 0xff), u_DataLo); - outb(((addr >> 8) & 0xff), u_DataHi); - - outb((0x44), u_Command); - outb(((addr >> 16) & 0xff), u_DataHi); - tmp = inb(u_DRAMIO); - restore_flags(flags); - - return tmp; -} - -void gus_write8(int reg, unsigned int data) -{ /* Writes to an indirect register (8 bit) */ - unsigned long flags; - - save_flags(flags); - cli(); - - outb((reg), u_Command); - outb(((unsigned char) (data & 0xff)), u_DataHi); - - restore_flags(flags); -} - -static unsigned char gus_read8(int reg) -{ - /* Reads from an indirect register (8 bit). Offset 0x80. */ - unsigned long flags; - unsigned char val; - - save_flags(flags); - cli(); - outb((reg | 0x80), u_Command); - val = inb(u_DataHi); - restore_flags(flags); - - return val; -} - -static unsigned char gus_look8(int reg) -{ - /* Reads from an indirect register (8 bit). No additional offset. */ - unsigned long flags; - unsigned char val; - - save_flags(flags); - cli(); - outb((reg), u_Command); - val = inb(u_DataHi); - restore_flags(flags); - - return val; -} - -static void gus_write16(int reg, unsigned int data) -{ - /* Writes to an indirect register (16 bit) */ - unsigned long flags; - - save_flags(flags); - cli(); - - outb((reg), u_Command); - - outb(((unsigned char) (data & 0xff)), u_DataLo); - outb(((unsigned char) ((data >> 8) & 0xff)), u_DataHi); - - restore_flags(flags); -} - -static unsigned short gus_read16(int reg) -{ - /* Reads from an indirect register (16 bit). Offset 0x80. */ - unsigned long flags; - unsigned char hi, lo; - - save_flags(flags); - cli(); - - outb((reg | 0x80), u_Command); - - lo = inb(u_DataLo); - hi = inb(u_DataHi); - - restore_flags(flags); - - return ((hi << 8) & 0xff00) | lo; -} - -static unsigned short gus_look16(int reg) -{ - /* Reads from an indirect register (16 bit). No additional offset. */ - unsigned long flags; - unsigned char hi, lo; - - save_flags(flags); - cli(); - - outb((reg), u_Command); - - lo = inb(u_DataLo); - hi = inb(u_DataHi); - - restore_flags(flags); - - return ((hi << 8) & 0xff00) | lo; -} - -static void gus_write_addr(int reg, unsigned long address, int frac, int is16bit) -{ - /* Writes an 24 bit memory address */ - unsigned long hold_address; - unsigned long flags; - - save_flags(flags); - cli(); - if (is16bit) - { - if (iw_mode) - { - /* Interwave spesific address translations */ - address >>= 1; - } - else - { - /* - * Special processing required for 16 bit patches - */ - - hold_address = address; - address = address >> 1; - address &= 0x0001ffffL; - address |= (hold_address & 0x000c0000L); - } - } - gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); - gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) - + (frac << 5)); - /* Could writing twice fix problems with GUS_VOICE_POS()? Let's try. */ - gus_delay(); - gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); - gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) - + (frac << 5)); - restore_flags(flags); -} - -static void gus_select_voice(int voice) -{ - if (voice < 0 || voice > 31) - return; - outb((voice), u_Voice); -} - -static void gus_select_max_voices(int nvoices) -{ - if (iw_mode) - nvoices = 32; - if (nvoices < 14) - nvoices = 14; - if (nvoices > 32) - nvoices = 32; - - voice_alloc->max_voice = nr_voices = nvoices; - gus_write8(0x0e, (nvoices - 1) | 0xc0); -} - -static void gus_voice_on(unsigned int mode) -{ - gus_write8(0x00, (unsigned char) (mode & 0xfc)); - gus_delay(); - gus_write8(0x00, (unsigned char) (mode & 0xfc)); -} - -static void gus_voice_off(void) -{ - gus_write8(0x00, gus_read8(0x00) | 0x03); -} - -static void gus_voice_mode(unsigned int m) -{ - unsigned char mode = (unsigned char) (m & 0xff); - - gus_write8(0x00, (gus_read8(0x00) & 0x03) | - (mode & 0xfc)); /* Don't touch last two bits */ - gus_delay(); - gus_write8(0x00, (gus_read8(0x00) & 0x03) | (mode & 0xfc)); -} - -static void gus_voice_freq(unsigned long freq) -{ - unsigned long divisor = freq_div_table[nr_voices - 14]; - unsigned short fc; - - /* Interwave plays at 44100 Hz with any number of voices */ - if (iw_mode) - fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100); - else - fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); - fc = fc << 1; - - gus_write16(0x01, fc); -} - -static void gus_voice_volume(unsigned int vol) -{ - gus_write8(0x0d, 0x03); /* Stop ramp before setting volume */ - gus_write16(0x09, (unsigned short) (vol << 4)); -} - -static void gus_voice_balance(unsigned int balance) -{ - gus_write8(0x0c, (unsigned char) (balance & 0xff)); -} - -static void gus_ramp_range(unsigned int low, unsigned int high) -{ - gus_write8(0x07, (unsigned char) ((low >> 4) & 0xff)); - gus_write8(0x08, (unsigned char) ((high >> 4) & 0xff)); -} - -static void gus_ramp_rate(unsigned int scale, unsigned int rate) -{ - gus_write8(0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); -} - -static void gus_rampon(unsigned int m) -{ - unsigned char mode = (unsigned char) (m & 0xff); - - gus_write8(0x0d, mode & 0xfc); - gus_delay(); - gus_write8(0x0d, mode & 0xfc); -} - -static void gus_ramp_mode(unsigned int m) -{ - unsigned char mode = (unsigned char) (m & 0xff); - - gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | - (mode & 0xfc)); /* Leave the last 2 bits alone */ - gus_delay(); - gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | (mode & 0xfc)); -} - -static void gus_rampoff(void) -{ - gus_write8(0x0d, 0x03); -} - -static void gus_set_voice_pos(int voice, long position) -{ - int sample_no; - - if ((sample_no = sample_map[voice]) != -1) { - if (position < samples[sample_no].len) { - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - voices[voice].offset_pending = position; - else - gus_write_addr(0x0a, sample_ptrs[sample_no] + position, 0, - samples[sample_no].mode & WAVE_16_BITS); - } - } -} - -static void gus_voice_init(int voice) -{ - unsigned long flags; - - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_voice_volume(0); - gus_voice_off(); - gus_write_addr(0x0a, 0, 0, 0); /* Set current position to 0 */ - gus_write8(0x00, 0x03); /* Voice off */ - gus_write8(0x0d, 0x03); /* Ramping off */ - voice_alloc->map[voice] = 0; - voice_alloc->alloc_times[voice] = 0; - restore_flags(flags); - -} - -static void gus_voice_init2(int voice) -{ - voices[voice].panning = 0; - voices[voice].mode = 0; - voices[voice].orig_freq = 20000; - voices[voice].current_freq = 20000; - voices[voice].bender = 0; - voices[voice].bender_range = 200; - voices[voice].initial_volume = 0; - voices[voice].current_volume = 0; - voices[voice].loop_irq_mode = 0; - voices[voice].loop_irq_parm = 0; - voices[voice].volume_irq_mode = 0; - voices[voice].volume_irq_parm = 0; - voices[voice].env_phase = 0; - voices[voice].main_vol = 127; - voices[voice].patch_vol = 127; - voices[voice].expression_vol = 127; - voices[voice].sample_pending = -1; - voices[voice].fixed_pitch = 0; -} - -static void step_envelope(int voice) -{ - unsigned vol, prev_vol, phase; - unsigned char rate; - long int flags; - - if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) - { - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_rampoff(); - restore_flags(flags); - return; - /* - * Sustain phase begins. Continue envelope after receiving note off. - */ - } - if (voices[voice].env_phase >= 5) - { - /* Envelope finished. Shoot the voice down */ - gus_voice_init(voice); - return; - } - prev_vol = voices[voice].current_volume; - phase = ++voices[voice].env_phase; - compute_volume(voice, voices[voice].midi_volume); - vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; - rate = voices[voice].env_rate[phase]; - - save_flags(flags); - cli(); - gus_select_voice(voice); - - gus_voice_volume(prev_vol); - - - gus_write8(0x06, rate); /* Ramping rate */ - - voices[voice].volume_irq_mode = VMODE_ENVELOPE; - - if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ - { - restore_flags(flags); - step_envelope(voice); /* Continue the envelope on the next step */ - return; - } - if (vol > prev_vol) - { - if (vol >= (4096 - 64)) - vol = 4096 - 65; - gus_ramp_range(0, vol); - gus_rampon(0x20); /* Increasing volume, with IRQ */ - } - else - { - if (vol <= 64) - vol = 65; - gus_ramp_range(vol, 4030); - gus_rampon(0x60); /* Decreasing volume, with IRQ */ - } - voices[voice].current_volume = vol; - restore_flags(flags); -} - -static void init_envelope(int voice) -{ - voices[voice].env_phase = -1; - voices[voice].current_volume = 64; - - step_envelope(voice); -} - -static void start_release(int voice, long int flags) -{ - if (gus_read8(0x00) & 0x03) - return; /* Voice already stopped */ - - voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ - - voices[voice].current_volume = voices[voice].initial_volume = - gus_read16(0x09) >> 4; /* Get current volume */ - - voices[voice].mode &= ~WAVE_SUSTAIN_ON; - gus_rampoff(); - restore_flags(flags); - step_envelope(voice); -} - -static void gus_voice_fade(int voice) -{ - int instr_no = sample_map[voice], is16bits; - long int flags; - - save_flags(flags); - cli(); - gus_select_voice(voice); - - if (instr_no < 0 || instr_no > MAX_SAMPLE) - { - gus_write8(0x00, 0x03); /* Hard stop */ - voice_alloc->map[voice] = 0; - restore_flags(flags); - return; - } - is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */ - - if (voices[voice].mode & WAVE_ENVELOPES) - { - start_release(voice, flags); - restore_flags(flags); - return; - } - /* - * Ramp the volume down but not too quickly. - */ - if ((int) (gus_read16(0x09) >> 4) < 100) /* Get current volume */ - { - gus_voice_off(); - gus_rampoff(); - gus_voice_init(voice); - restore_flags(flags); - return; - } - gus_ramp_range(65, 4030); - gus_ramp_rate(2, 4); - gus_rampon(0x40 | 0x20); /* Down, once, with IRQ */ - voices[voice].volume_irq_mode = VMODE_HALT; - restore_flags(flags); -} - -static void gus_reset(void) -{ - int i; - - gus_select_max_voices(24); - volume_base = 3071; - volume_scale = 4; - volume_method = VOL_METHOD_ADAGIO; - - for (i = 0; i < 32; i++) - { - gus_voice_init(i); /* Turn voice off */ - gus_voice_init2(i); - } -} - -static void gus_initialize(void) -{ - unsigned long flags; - unsigned char dma_image, irq_image, tmp; - - static unsigned char gus_irq_map[16] = { - 0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7 - }; - - static unsigned char gus_dma_map[8] = { - 0, 1, 0, 2, 0, 3, 4, 5 - }; - - save_flags(flags); - cli(); - gus_write8(0x4c, 0); /* Reset GF1 */ - gus_delay(); - gus_delay(); - - gus_write8(0x4c, 1); /* Release Reset */ - gus_delay(); - gus_delay(); - - /* - * Clear all interrupts - */ - - gus_write8(0x41, 0); /* DMA control */ - gus_write8(0x45, 0); /* Timer control */ - gus_write8(0x49, 0); /* Sample control */ - - gus_select_max_voices(24); - - inb(u_Status); /* Touch the status register */ - - gus_look8(0x41); /* Clear any pending DMA IRQs */ - gus_look8(0x49); /* Clear any pending sample IRQs */ - gus_read8(0x0f); /* Clear pending IRQs */ - - gus_reset(); /* Resets all voices */ - - gus_look8(0x41); /* Clear any pending DMA IRQs */ - gus_look8(0x49); /* Clear any pending sample IRQs */ - gus_read8(0x0f); /* Clear pending IRQs */ - - gus_write8(0x4c, 7); /* Master reset | DAC enable | IRQ enable */ - - /* - * Set up for Digital ASIC - */ - - outb((0x05), gus_base + 0x0f); - - mix_image |= 0x02; /* Disable line out (for a moment) */ - outb((mix_image), u_Mixer); - - outb((0x00), u_IRQDMAControl); - - outb((0x00), gus_base + 0x0f); - - /* - * Now set up the DMA and IRQ interface - * - * The GUS supports two IRQs and two DMAs. - * - * Just one DMA channel is used. This prevents simultaneous ADC and DAC. - * Adding this support requires significant changes to the dmabuf.c, dsp.c - * and audio.c also. - */ - - irq_image = 0; - tmp = gus_irq_map[gus_irq]; - if (!gus_pnp_flag && !tmp) - printk(KERN_WARNING "Warning! GUS IRQ not selected\n"); - irq_image |= tmp; - irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ - - dual_dma_mode = 1; - if (gus_dma2 == gus_dma || gus_dma2 == -1) - { - dual_dma_mode = 0; - dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */ - - tmp = gus_dma_map[gus_dma]; - if (!tmp) - printk(KERN_WARNING "Warning! GUS DMA not selected\n"); - - dma_image |= tmp; - } - else - { - /* Setup dual DMA channel mode for GUS MAX */ - - dma_image = gus_dma_map[gus_dma]; - if (!dma_image) - printk(KERN_WARNING "Warning! GUS DMA not selected\n"); - - tmp = gus_dma_map[gus_dma2] << 3; - if (!tmp) - { - printk(KERN_WARNING "Warning! Invalid GUS MAX DMA\n"); - tmp = 0x40; /* Combine DMA channels */ - dual_dma_mode = 0; - } - dma_image |= tmp; - } - - /* - * For some reason the IRQ and DMA addresses must be written twice - */ - - /* - * Doing it first time - */ - - outb((mix_image), u_Mixer); /* Select DMA control */ - outb((dma_image | 0x80), u_IRQDMAControl); /* Set DMA address */ - - outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ - outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ - - /* - * Doing it second time - */ - - outb((mix_image), u_Mixer); /* Select DMA control */ - outb((dma_image), u_IRQDMAControl); /* Set DMA address */ - - outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ - outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ - - gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ - - mix_image &= ~0x02; /* Enable line out */ - mix_image |= 0x08; /* Enable IRQ */ - outb((mix_image), u_Mixer); /* - * Turn mixer channels on - * Note! Mic in is left off. - */ - - gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ - - gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ - - inb(u_Status); /* Touch the status register */ - - gus_look8(0x41); /* Clear any pending DMA IRQs */ - gus_look8(0x49); /* Clear any pending sample IRQs */ - - gus_read8(0x0f); /* Clear pending IRQs */ - - if (iw_mode) - gus_write8(0x19, gus_read8(0x19) | 0x01); - restore_flags(flags); -} - - -static void __init pnp_mem_init(void) -{ -#include "iwmem.h" -#define CHUNK_SIZE (256*1024) -#define BANK_SIZE (4*1024*1024) -#define CHUNKS_PER_BANK (BANK_SIZE/CHUNK_SIZE) - - int bank, chunk, addr, total = 0; - int bank_sizes[4]; - int i, j, bits = -1, testbits = -1, nbanks = 0; - - /* - * This routine determines what kind of RAM is installed in each of the four - * SIMM banks and configures the DRAM address decode logic accordingly. - */ - - /* - * Place the chip into enhanced mode - */ - gus_write8(0x19, gus_read8(0x19) | 0x01); - gus_write8(0x53, gus_look8(0x53) & ~0x02); /* Select DRAM I/O access */ - - /* - * Set memory configuration to 4 DRAM banks of 4M in each (16M total). - */ - - gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | 0x000c); - - /* - * Perform the DRAM size detection for each bank individually. - */ - for (bank = 0; bank < 4; bank++) - { - int size = 0; - - addr = bank * BANK_SIZE; - - /* Clean check points of each chunk */ - for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) - { - gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); - gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); - } - - /* Write a value to each chunk point and verify the result */ - for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) - { - gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x55); - gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0xAA); - - if (gus_peek(addr + chunk * CHUNK_SIZE + 0L) == 0x55 && - gus_peek(addr + chunk * CHUNK_SIZE + 1L) == 0xAA) - { - /* OK. There is RAM. Now check for possible shadows */ - int ok = 1, chunk2; - - for (chunk2 = 0; ok && chunk2 < chunk; chunk2++) - if (gus_peek(addr + chunk2 * CHUNK_SIZE + 0L) || - gus_peek(addr + chunk2 * CHUNK_SIZE + 1L)) - ok = 0; /* Addressing wraps */ - - if (ok) - size = (chunk + 1) * CHUNK_SIZE; - } - gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); - gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); - } - bank_sizes[bank] = size; - if (size) - nbanks = bank + 1; - DDB(printk("Interwave: Bank %d, size=%dk\n", bank, size / 1024)); - } - - if (nbanks == 0) /* No RAM - Give up */ - { - printk(KERN_ERR "Sound: An Interwave audio chip detected but no DRAM\n"); - printk(KERN_ERR "Sound: Unable to work with this card.\n"); - gus_write8(0x19, gus_read8(0x19) & ~0x01); - gus_mem_size = 0; - return; - } - - /* - * Now we know how much DRAM there is in each bank. The next step is - * to find a DRAM size encoding (0 to 12) which is best for the combination - * we have. - * - * First try if any of the possible alternatives matches exactly the amount - * of memory we have. - */ - - for (i = 0; bits == -1 && i < 13; i++) - { - bits = i; - - for (j = 0; bits != -1 && j < 4; j++) - if (mem_decode[i][j] != bank_sizes[j]) - bits = -1; /* No hit */ - } - - /* - * If necessary, try to find a combination where other than the last - * bank matches our configuration and the last bank is left oversized. - * In this way we don't leave holes in the middle of memory. - */ - - if (bits == -1) /* No luck yet */ - { - for (i = 0; bits == -1 && i < 13; i++) - { - bits = i; - - for (j = 0; bits != -1 && j < nbanks - 1; j++) - if (mem_decode[i][j] != bank_sizes[j]) - bits = -1; /* No hit */ - if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1]) - bits = -1; /* The last bank is too small */ - } - } - /* - * The last resort is to search for a combination where the banks are - * smaller than the actual SIMMs. This leaves some memory in the banks - * unused but doesn't leave holes in the DRAM address space. - */ - if (bits == -1) /* No luck yet */ - { - for (i = 0; i < 13; i++) - { - testbits = i; - for (j = 0; testbits != -1 && j < nbanks - 1; j++) - if (mem_decode[i][j] > bank_sizes[j]) { - testbits = -1; - } - if(testbits > bits) bits = testbits; - } - if (bits != -1) - { - printk(KERN_INFO "Interwave: Can't use all installed RAM.\n"); - printk(KERN_INFO "Interwave: Try reordering SIMMS.\n"); - } - printk(KERN_INFO "Interwave: Can't find working DRAM encoding.\n"); - printk(KERN_INFO "Interwave: Defaulting to 256k. Try reordering SIMMS.\n"); - bits = 0; - } - DDB(printk("Interwave: Selecting DRAM addressing mode %d\n", bits)); - - for (bank = 0; bank < 4; bank++) - { - DDB(printk(" Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024)); - - if (bank_sizes[bank] > mem_decode[bits][bank]) - total += mem_decode[bits][bank]; - else - total += bank_sizes[bank]; - } - - DDB(printk("Total %dk of DRAM (enhanced mode)\n", total / 1024)); - - /* - * Set the memory addressing mode. - */ - gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | bits); - -/* Leave the chip into enhanced mode. Disable LFO */ - gus_mem_size = total; - iw_mode = 1; - gus_write8(0x19, (gus_read8(0x19) | 0x01) & ~0x02); -} - -int __init gus_wave_detect(int baseaddr) -{ - unsigned long i, max_mem = 1024L; - unsigned long loc; - unsigned char val; - - gus_base = baseaddr; - - gus_write8(0x4c, 0); /* Reset GF1 */ - gus_delay(); - gus_delay(); - - gus_write8(0x4c, 1); /* Release Reset */ - gus_delay(); - gus_delay(); - -#ifdef GUSPNP_AUTODETECT - val = gus_look8(0x5b); /* Version number register */ - gus_write8(0x5b, ~val); /* Invert all bits */ - - if ((gus_look8(0x5b) & 0xf0) == (val & 0xf0)) /* No change */ - { - if ((gus_look8(0x5b) & 0x0f) == ((~val) & 0x0f)) /* Change */ - { - DDB(printk("Interwave chip version %d detected\n", (val & 0xf0) >> 4)); - gus_pnp_flag = 1; - } - else - { - DDB(printk("Not an Interwave chip (%x)\n", gus_look8(0x5b))); - gus_pnp_flag = 0; - } - } - gus_write8(0x5b, val); /* Restore all bits */ -#endif - - if (gus_pnp_flag) - pnp_mem_init(); - if (iw_mode) - return 1; - - /* See if there is first block there.... */ - gus_poke(0L, 0xaa); - if (gus_peek(0L) != 0xaa) - return (0); - - /* Now zero it out so that I can check for mirroring .. */ - gus_poke(0L, 0x00); - for (i = 1L; i < max_mem; i++) - { - int n, failed; - - /* check for mirroring ... */ - if (gus_peek(0L) != 0) - break; - loc = i << 10; - - for (n = loc - 1, failed = 0; n <= loc; n++) - { - gus_poke(loc, 0xaa); - if (gus_peek(loc) != 0xaa) - failed = 1; - gus_poke(loc, 0x55); - if (gus_peek(loc) != 0x55) - failed = 1; - } - if (failed) - break; - } - gus_mem_size = i << 10; - return 1; -} - -static int guswave_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - - switch (cmd) - { - case SNDCTL_SYNTH_INFO: - gus_info.nr_voices = nr_voices; - if (copy_to_user(arg, &gus_info, sizeof(gus_info))) - return -EFAULT; - return 0; - - case SNDCTL_SEQ_RESETSAMPLES: - reset_sample_memory(); - return 0; - - case SNDCTL_SEQ_PERCMODE: - return 0; - - case SNDCTL_SYNTH_MEMAVL: - return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32; - - default: - return -EINVAL; - } -} - -static int guswave_set_instr(int dev, int voice, int instr_no) -{ - int sample_no; - - if (instr_no < 0 || instr_no > MAX_PATCH) - instr_no = 0; /* Default to acoustic piano */ - - if (voice < 0 || voice > 31) - return -EINVAL; - - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - { - voices[voice].sample_pending = instr_no; - return 0; - } - sample_no = patch_table[instr_no]; - patch_map[voice] = -1; - - if (sample_no == NOT_SAMPLE) - { -/* printk("GUS: Undefined patch %d for voice %d\n", instr_no, voice);*/ - return -EINVAL; /* Patch not defined */ - } - if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ - { -/* printk("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);*/ - return -EINVAL; - } - sample_map[voice] = sample_no; - patch_map[voice] = instr_no; - return 0; -} - -static int guswave_kill_note(int dev, int voice, int note, int velocity) -{ - unsigned long flags; - - save_flags(flags); - cli(); - /* voice_alloc->map[voice] = 0xffff; */ - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - { - voices[voice].kill_pending = 1; - restore_flags(flags); - } - else - { - restore_flags(flags); - gus_voice_fade(voice); - } - - return 0; -} - -static void guswave_aftertouch(int dev, int voice, int pressure) -{ -} - -static void guswave_panning(int dev, int voice, int value) -{ - if (voice >= 0 || voice < 32) - voices[voice].panning = value; -} - -static void guswave_volume_method(int dev, int mode) -{ - if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) - volume_method = mode; -} - -static void compute_volume(int voice, int volume) -{ - if (volume < 128) - voices[voice].midi_volume = volume; - - switch (volume_method) - { - case VOL_METHOD_ADAGIO: - voices[voice].initial_volume = - gus_adagio_vol(voices[voice].midi_volume, voices[voice].main_vol, - voices[voice].expression_vol, - voices[voice].patch_vol); - break; - - case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ - voices[voice].initial_volume = gus_linear_vol(volume, voices[voice].main_vol); - break; - - default: - voices[voice].initial_volume = volume_base + - (voices[voice].midi_volume * volume_scale); - } - - if (voices[voice].initial_volume > 4030) - voices[voice].initial_volume = 4030; -} - -static void compute_and_set_volume(int voice, int volume, int ramp_time) -{ - int curr, target, rate; - unsigned long flags; - - compute_volume(voice, volume); - voices[voice].current_volume = voices[voice].initial_volume; - - save_flags(flags); - cli(); - /* - * CAUTION! Interrupts disabled. Enable them before returning - */ - - gus_select_voice(voice); - - curr = gus_read16(0x09) >> 4; - target = voices[voice].initial_volume; - - if (ramp_time == INSTANT_RAMP) - { - gus_rampoff(); - gus_voice_volume(target); - restore_flags(flags); - return; - } - if (ramp_time == FAST_RAMP) - rate = 63; - else - rate = 16; - gus_ramp_rate(0, rate); - - if ((target - curr) / 64 == 0) /* Close enough to target. */ - { - gus_rampoff(); - gus_voice_volume(target); - restore_flags(flags); - return; - } - if (target > curr) - { - if (target > (4095 - 65)) - target = 4095 - 65; - gus_ramp_range(curr, target); - gus_rampon(0x00); /* Ramp up, once, no IRQ */ - } - else - { - if (target < 65) - target = 65; - - gus_ramp_range(target, curr); - gus_rampon(0x40); /* Ramp down, once, no irq */ - } - restore_flags(flags); -} - -static void dynamic_volume_change(int voice) -{ - unsigned char status; - unsigned long flags; - - save_flags(flags); - cli(); - gus_select_voice(voice); - status = gus_read8(0x00); /* Get voice status */ - restore_flags(flags); - - if (status & 0x03) - return; /* Voice was not running */ - - if (!(voices[voice].mode & WAVE_ENVELOPES)) - { - compute_and_set_volume(voice, voices[voice].midi_volume, 1); - return; - } - - /* - * Voice is running and has envelopes. - */ - - save_flags(flags); - cli(); - gus_select_voice(voice); - status = gus_read8(0x0d); /* Ramping status */ - restore_flags(flags); - - if (status & 0x03) /* Sustain phase? */ - { - compute_and_set_volume(voice, voices[voice].midi_volume, 1); - return; - } - if (voices[voice].env_phase < 0) - return; - - compute_volume(voice, voices[voice].midi_volume); - -} - -static void guswave_controller(int dev, int voice, int ctrl_num, int value) -{ - unsigned long flags; - unsigned long freq; - - if (voice < 0 || voice > 31) - return; - - switch (ctrl_num) - { - case CTRL_PITCH_BENDER: - voices[voice].bender = value; - - if (voices[voice].volume_irq_mode != VMODE_START_NOTE) - { - freq = compute_finetune(voices[voice].orig_freq, value, voices[voice].bender_range, 0); - voices[voice].current_freq = freq; - - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_voice_freq(freq); - restore_flags(flags); - } - break; - - case CTRL_PITCH_BENDER_RANGE: - voices[voice].bender_range = value; - break; - case CTL_EXPRESSION: - value /= 128; - case CTRL_EXPRESSION: - if (volume_method == VOL_METHOD_ADAGIO) - { - voices[voice].expression_vol = value; - if (voices[voice].volume_irq_mode != VMODE_START_NOTE) - dynamic_volume_change(voice); - } - break; - - case CTL_PAN: - voices[voice].panning = (value * 2) - 128; - break; - - case CTL_MAIN_VOLUME: - value = (value * 100) / 16383; - - case CTRL_MAIN_VOLUME: - voices[voice].main_vol = value; - if (voices[voice].volume_irq_mode != VMODE_START_NOTE) - dynamic_volume_change(voice); - break; - - default: - break; - } -} - -static int guswave_start_note2(int dev, int voice, int note_num, int volume) -{ - int sample, best_sample, best_delta, delta_freq; - int is16bits, samplep, patch, pan; - unsigned long note_freq, base_note, freq, flags; - unsigned char mode = 0; - - if (voice < 0 || voice > 31) - { -/* printk("GUS: Invalid voice\n");*/ - return -EINVAL; - } - if (note_num == 255) - { - if (voices[voice].mode & WAVE_ENVELOPES) - { - voices[voice].midi_volume = volume; - dynamic_volume_change(voice); - return 0; - } - compute_and_set_volume(voice, volume, 1); - return 0; - } - if ((patch = patch_map[voice]) == -1) - return -EINVAL; - if ((samplep = patch_table[patch]) == NOT_SAMPLE) - { - return -EINVAL; - } - note_freq = note_to_freq(note_num); - - /* - * Find a sample within a patch so that the note_freq is between low_note - * and high_note. - */ - sample = -1; - - best_sample = samplep; - best_delta = 1000000; - while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1) - { - delta_freq = note_freq - samples[samplep].base_note; - if (delta_freq < 0) - delta_freq = -delta_freq; - if (delta_freq < best_delta) - { - best_sample = samplep; - best_delta = delta_freq; - } - if (samples[samplep].low_note <= note_freq && - note_freq <= samples[samplep].high_note) - { - sample = samplep; - } - else - samplep = samples[samplep].key; /* Link to next sample */ - } - if (sample == -1) - sample = best_sample; - - if (sample == -1) - { -/* printk("GUS: Patch %d not defined for note %d\n", patch, note_num);*/ - return 0; /* Should play default patch ??? */ - } - is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; - voices[voice].mode = samples[sample].mode; - voices[voice].patch_vol = samples[sample].volume; - - if (iw_mode) - gus_write8(0x15, 0x00); /* RAM, Reset voice deactivate bit of SMSI */ - - if (voices[voice].mode & WAVE_ENVELOPES) - { - int i; - - for (i = 0; i < 6; i++) - { - voices[voice].env_rate[i] = samples[sample].env_rate[i]; - voices[voice].env_offset[i] = samples[sample].env_offset[i]; - } - } - sample_map[voice] = sample; - - if (voices[voice].fixed_pitch) /* Fixed pitch */ - { - freq = samples[sample].base_freq; - } - else - { - base_note = samples[sample].base_note / 100; - note_freq /= 100; - - freq = samples[sample].base_freq * note_freq / base_note; - } - - voices[voice].orig_freq = freq; - - /* - * Since the pitch bender may have been set before playing the note, we - * have to calculate the bending now. - */ - - freq = compute_finetune(voices[voice].orig_freq, voices[voice].bender, - voices[voice].bender_range, 0); - voices[voice].current_freq = freq; - - pan = (samples[sample].panning + voices[voice].panning) / 32; - pan += 7; - if (pan < 0) - pan = 0; - if (pan > 15) - pan = 15; - - if (samples[sample].mode & WAVE_16_BITS) - { - mode |= 0x04; /* 16 bits */ - if ((sample_ptrs[sample] / GUS_BANK_SIZE) != - ((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE)) - printk(KERN_ERR "GUS: Sample address error\n"); - } - /************************************************************************* - * CAUTION! Interrupts disabled. Don't return before enabling - *************************************************************************/ - - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_voice_off(); - gus_rampoff(); - - restore_flags(flags); - - if (voices[voice].mode & WAVE_ENVELOPES) - { - compute_volume(voice, volume); - init_envelope(voice); - } - else - { - compute_and_set_volume(voice, volume, 0); - } - - save_flags(flags); - cli(); - gus_select_voice(voice); - - if (samples[sample].mode & WAVE_LOOP_BACK) - gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].len - - voices[voice].offset_pending, 0, is16bits); /* start=end */ - else - gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending, 0, is16bits); /* Sample start=begin */ - - if (samples[sample].mode & WAVE_LOOPING) - { - mode |= 0x08; - - if (samples[sample].mode & WAVE_BIDIR_LOOP) - mode |= 0x10; - - if (samples[sample].mode & WAVE_LOOP_BACK) - { - gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].loop_end - - voices[voice].offset_pending, - (samples[sample].fractions >> 4) & 0x0f, is16bits); - mode |= 0x40; - } - gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start, - samples[sample].fractions & 0x0f, is16bits); /* Loop start location */ - gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end, - (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ - } - else - { - mode |= 0x20; /* Loop IRQ at the end */ - voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */ - voices[voice].loop_irq_parm = 1; - gus_write_addr(0x02, sample_ptrs[sample], 0, is16bits); /* Loop start location */ - gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1, - (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ - } - gus_voice_freq(freq); - gus_voice_balance(pan); - gus_voice_on(mode); - restore_flags(flags); - - return 0; -} - -/* - * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking - * when the note playing on the voice is changed. It uses volume - * ramping. - */ - -static int guswave_start_note(int dev, int voice, int note_num, int volume) -{ - long int flags; - int mode; - int ret_val = 0; - - save_flags(flags); - cli(); - if (note_num == 255) - { - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - { - voices[voice].volume_pending = volume; - } - else - { - ret_val = guswave_start_note2(dev, voice, note_num, volume); - } - } - else - { - gus_select_voice(voice); - mode = gus_read8(0x00); - if (mode & 0x20) - gus_write8(0x00, mode & 0xdf); /* No interrupt! */ - - voices[voice].offset_pending = 0; - voices[voice].kill_pending = 0; - voices[voice].volume_irq_mode = 0; - voices[voice].loop_irq_mode = 0; - - if (voices[voice].sample_pending >= 0) - { - restore_flags(flags); /* Run temporarily with interrupts enabled */ - guswave_set_instr(voices[voice].dev_pending, voice, voices[voice].sample_pending); - voices[voice].sample_pending = -1; - save_flags(flags); - cli(); - gus_select_voice(voice); /* Reselect the voice (just to be sure) */ - } - if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < (unsigned) 2065)) - { - ret_val = guswave_start_note2(dev, voice, note_num, volume); - } - else - { - voices[voice].dev_pending = dev; - voices[voice].note_pending = note_num; - voices[voice].volume_pending = volume; - voices[voice].volume_irq_mode = VMODE_START_NOTE; - - gus_rampoff(); - gus_ramp_range(2000, 4065); - gus_ramp_rate(0, 63); /* Fastest possible rate */ - gus_rampon(0x20 | 0x40); /* Ramp down, once, irq */ - } - } - restore_flags(flags); - return ret_val; -} - -static void guswave_reset(int dev) -{ - int i; - - for (i = 0; i < 32; i++) - { - gus_voice_init(i); - gus_voice_init2(i); - } -} - -static int guswave_open(int dev, int mode) -{ - int err; - - if (gus_busy) - return -EBUSY; - - voice_alloc->timestamp = 0; - - if (gus_no_wave_dma) { - gus_no_dma = 1; - } else { - if ((err = DMAbuf_open_dma(gus_devnum)) < 0) - { - /* printk( "GUS: Loading samples without DMA\n"); */ - gus_no_dma = 1; /* Upload samples using PIO */ - } - else - gus_no_dma = 0; - } - - init_waitqueue_head(&dram_sleeper); - gus_busy = 1; - active_device = GUS_DEV_WAVE; - - gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ - gus_initialize(); - gus_reset(); - gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ - - return 0; -} - -static void guswave_close(int dev) -{ - gus_busy = 0; - active_device = 0; - gus_reset(); - - if (!gus_no_dma) - DMAbuf_close_dma(gus_devnum); -} - -static int guswave_load_patch(int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) -{ - struct patch_info patch; - int instr; - long sizeof_patch; - - unsigned long blk_sz, blk_end, left, src_offs, target; - - sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */ - - if (format != GUS_PATCH) - { -/* printk("GUS Error: Invalid patch format (key) 0x%x\n", format);*/ - return -EINVAL; - } - if (count < sizeof_patch) - { -/* printk("GUS Error: Patch header too short\n");*/ - return -EINVAL; - } - count -= sizeof_patch; - - if (free_sample >= MAX_SAMPLE) - { -/* printk("GUS: Sample table full\n");*/ - return -ENOSPC; - } - /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. - */ - - copy_from_user(&((char *) &patch)[offs], &(addr)[offs], sizeof_patch - offs); - - if (patch.mode & WAVE_ROM) - return -EINVAL; - if (gus_mem_size == 0) - return -ENOSPC; - - instr = patch.instr_no; - - if (instr < 0 || instr > MAX_PATCH) - { -/* printk(KERN_ERR "GUS: Invalid patch number %d\n", instr);*/ - return -EINVAL; - } - if (count < patch.len) - { -/* printk(KERN_ERR "GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len);*/ - patch.len = count; - } - if (patch.len <= 0 || patch.len > gus_mem_size) - { -/* printk(KERN_ERR "GUS: Invalid sample length %d\n", (int) patch.len);*/ - return -EINVAL; - } - if (patch.mode & WAVE_LOOPING) - { - if (patch.loop_start < 0 || patch.loop_start >= patch.len) - { -/* printk(KERN_ERR "GUS: Invalid loop start\n");*/ - return -EINVAL; - } - if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) - { -/* printk(KERN_ERR "GUS: Invalid loop end\n");*/ - return -EINVAL; - } - } - free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */ - - if (patch.mode & WAVE_16_BITS) - { - /* - * 16 bit samples must fit one 256k bank. - */ - if (patch.len >= GUS_BANK_SIZE) - { -/* printk("GUS: Sample (16 bit) too long %d\n", (int) patch.len);*/ - return -ENOSPC; - } - if ((free_mem_ptr / GUS_BANK_SIZE) != - ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) - { - unsigned long tmp_mem = - /* Align to 256K */ - ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; - - if ((tmp_mem + patch.len) > gus_mem_size) - return -ENOSPC; - - free_mem_ptr = tmp_mem; /* This leaves unusable memory */ - } - } - if ((free_mem_ptr + patch.len) > gus_mem_size) - return -ENOSPC; - - sample_ptrs[free_sample] = free_mem_ptr; - - /* - * Tremolo is not possible with envelopes - */ - - if (patch.mode & WAVE_ENVELOPES) - patch.mode &= ~WAVE_TREMOLO; - - if (!(patch.mode & WAVE_FRACTIONS)) - { - patch.fractions = 0; - } - memcpy((char *) &samples[free_sample], &patch, sizeof_patch); - - /* - * Link this_one sample to the list of samples for patch 'instr'. - */ - - samples[free_sample].key = patch_table[instr]; - patch_table[instr] = free_sample; - - /* - * Use DMA to transfer the wave data to the DRAM - */ - - left = patch.len; - src_offs = 0; - target = free_mem_ptr; - - while (left) /* Not completely transferred yet */ - { - blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use; - if (blk_sz > left) - blk_sz = left; - - /* - * DMA cannot cross bank (256k) boundaries. Check for that. - */ - - blk_end = target + blk_sz; - - if ((target / GUS_BANK_SIZE) != (blk_end / GUS_BANK_SIZE)) - { - /* Split the block */ - blk_end &= ~(GUS_BANK_SIZE - 1); - blk_sz = blk_end - target; - } - if (gus_no_dma) - { - /* - * For some reason the DMA is not possible. We have to use PIO. - */ - long i; - unsigned char data; - - for (i = 0; i < blk_sz; i++) - { - get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[sizeof_patch + i])); - if (patch.mode & WAVE_UNSIGNED) - if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) - data ^= 0x80; /* Convert to signed */ - gus_poke(target + i, data); - } - } - else - { - unsigned long address, hold_address; - unsigned char dma_command; - unsigned long flags; - - if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL) - { - printk(KERN_ERR "GUS: DMA buffer == NULL\n"); - return -ENOSPC; - } - /* - * OK, move now. First in and then out. - */ - - copy_from_user(audio_devs[gus_devnum]->dmap_out->raw_buf, &(addr)[sizeof_patch + src_offs], blk_sz); - - save_flags(flags); - cli(); - /******** INTERRUPTS DISABLED NOW ********/ - gus_write8(0x41, 0); /* Disable GF1 DMA */ - DMAbuf_start_dma(gus_devnum, audio_devs[gus_devnum]->dmap_out->raw_buf_phys, - blk_sz, DMA_MODE_WRITE); - - /* - * Set the DRAM address for the wave data - */ - - if (iw_mode) - { - /* Different address translation in enhanced mode */ - - unsigned char hi; - - if (gus_dma > 4) - address = target >> 1; /* Convert to 16 bit word address */ - else - address = target; - - hi = (unsigned char) ((address >> 16) & 0xf0); - hi += (unsigned char) (address & 0x0f); - - gus_write16(0x42, (address >> 4) & 0xffff); /* DMA address (low) */ - gus_write8(0x50, hi); - } - else - { - address = target; - if (audio_devs[gus_devnum]->dmap_out->dma > 3) - { - hold_address = address; - address = address >> 1; - address &= 0x0001ffffL; - address |= (hold_address & 0x000c0000L); - } - gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ - } - - /* - * Start the DMA transfer - */ - - dma_command = 0x21; /* IRQ enable, DMA start */ - if (patch.mode & WAVE_UNSIGNED) - dma_command |= 0x80; /* Invert MSB */ - if (patch.mode & WAVE_16_BITS) - dma_command |= 0x40; /* 16 bit _DATA_ */ - if (audio_devs[gus_devnum]->dmap_out->dma > 3) - dma_command |= 0x04; /* 16 bit DMA _channel_ */ - - gus_write8(0x41, dma_command); /* Lets go luteet (=bugs) */ - - /* - * Sleep here until the DRAM DMA done interrupt is served - */ - active_device = GUS_DEV_WAVE; - - if (!interruptible_sleep_on_timeout(&dram_sleeper, HZ)) - printk("GUS: DMA Transfer timed out\n"); - restore_flags(flags); - } - - /* - * Now the next part - */ - - left -= blk_sz; - src_offs += blk_sz; - target += blk_sz; - - gus_write8(0x41, 0); /* Stop DMA */ - } - - free_mem_ptr += patch.len; - free_sample++; - return 0; -} - -static void guswave_hw_control(int dev, unsigned char *event_rec) -{ - int voice, cmd; - unsigned short p1, p2; - unsigned int plong; - unsigned long flags; - - cmd = event_rec[2]; - voice = event_rec[3]; - p1 = *(unsigned short *) &event_rec[4]; - p2 = *(unsigned short *) &event_rec[6]; - plong = *(unsigned int *) &event_rec[4]; - - if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && - (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) - do_volume_irq(voice); - - switch (cmd) - { - case _GUS_NUMVOICES: - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_select_max_voices(p1); - restore_flags(flags); - break; - - case _GUS_VOICESAMPLE: - guswave_set_instr(dev, voice, p1); - break; - - case _GUS_VOICEON: - save_flags(flags); - cli(); - gus_select_voice(voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_voice_on(p1); - restore_flags(flags); - break; - - case _GUS_VOICEOFF: - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_voice_off(); - restore_flags(flags); - break; - - case _GUS_VOICEFADE: - gus_voice_fade(voice); - break; - - case _GUS_VOICEMODE: - save_flags(flags); - cli(); - gus_select_voice(voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_voice_mode(p1); - restore_flags(flags); - break; - - case _GUS_VOICEBALA: - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_voice_balance(p1); - restore_flags(flags); - break; - - case _GUS_VOICEFREQ: - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_voice_freq(plong); - restore_flags(flags); - break; - - case _GUS_VOICEVOL: - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_voice_volume(p1); - restore_flags(flags); - break; - - case _GUS_VOICEVOL2: /* Just update the software voice level */ - voices[voice].initial_volume = voices[voice].current_volume = p1; - break; - - case _GUS_RAMPRANGE: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_ramp_range(p1, p2); - restore_flags(flags); - break; - - case _GUS_RAMPRATE: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NJET-NJET */ - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_ramp_rate(p1, p2); - restore_flags(flags); - break; - - case _GUS_RAMPMODE: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ - save_flags(flags); - cli(); - gus_select_voice(voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_ramp_mode(p1); - restore_flags(flags); - break; - - case _GUS_RAMPON: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* EI-EI */ - save_flags(flags); - cli(); - gus_select_voice(voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_rampon(p1); - restore_flags(flags); - break; - - case _GUS_RAMPOFF: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NEJ-NEJ */ - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_rampoff(); - restore_flags(flags); - break; - - case _GUS_VOLUME_SCALE: - volume_base = p1; - volume_scale = p2; - break; - - case _GUS_VOICE_POS: - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_set_voice_pos(voice, plong); - restore_flags(flags); - break; - - default: - break; - } -} - -static int gus_audio_set_speed(int speed) -{ - if (speed <= 0) - speed = gus_audio_speed; - - if (speed < 4000) - speed = 4000; - - if (speed > 44100) - speed = 44100; - - gus_audio_speed = speed; - - if (only_read_access) - { - /* Compute nearest valid recording speed and return it */ - - /* speed = (9878400 / (gus_audio_speed + 2)) / 16; */ - speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; - speed = (9878400 / (speed * 16)) - 2; - } - return speed; -} - -static int gus_audio_set_channels(int channels) -{ - if (!channels) - return gus_audio_channels; - if (channels > 2) - channels = 2; - if (channels < 1) - channels = 1; - gus_audio_channels = channels; - return channels; -} - -static int gus_audio_set_bits(int bits) -{ - if (!bits) - return gus_audio_bits; - - if (bits != 8 && bits != 16) - bits = 8; - - if (only_8_bits) - bits = 8; - - gus_audio_bits = bits; - return bits; -} - -static int gus_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int val; - - switch (cmd) - { - case SOUND_PCM_WRITE_RATE: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = gus_audio_set_speed(val); - break; - - case SOUND_PCM_READ_RATE: - val = gus_audio_speed; - break; - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = gus_audio_set_channels(val + 1) - 1; - break; - - case SOUND_PCM_WRITE_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = gus_audio_set_channels(val); - break; - - case SOUND_PCM_READ_CHANNELS: - val = gus_audio_channels; - break; - - case SNDCTL_DSP_SETFMT: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = gus_audio_set_bits(val); - break; - - case SOUND_PCM_READ_BITS: - val = gus_audio_bits; - break; - - case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */ - case SOUND_PCM_READ_FILTER: - val = -EINVAL; - break; - default: - return -EINVAL; - } - return put_user(val, (int *)arg); -} - -static void gus_audio_reset(int dev) -{ - if (recording_active) - { - gus_write8(0x49, 0x00); /* Halt recording */ - set_input_volumes(); - } -} - -static int saved_iw_mode; /* A hack hack hack */ - -static int gus_audio_open(int dev, int mode) -{ - if (gus_busy) - return -EBUSY; - - if (gus_pnp_flag && mode & OPEN_READ) - { -/* printk(KERN_ERR "GUS: Audio device #%d is playback only.\n", dev);*/ - return -EIO; - } - gus_initialize(); - - gus_busy = 1; - active_device = 0; - - saved_iw_mode = iw_mode; - if (iw_mode) - { - /* There are some problems with audio in enhanced mode so disable it */ - gus_write8(0x19, gus_read8(0x19) & ~0x01); /* Disable enhanced mode */ - iw_mode = 0; - } - - gus_reset(); - reset_sample_memory(); - gus_select_max_voices(14); - - pcm_active = 0; - dma_active = 0; - pcm_opened = 1; - if (mode & OPEN_READ) - { - recording_active = 1; - set_input_volumes(); - } - only_read_access = !(mode & OPEN_WRITE); - only_8_bits = mode & OPEN_READ; - if (only_8_bits) - audio_devs[dev]->format_mask = AFMT_U8; - else - audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE; - - return 0; -} - -static void gus_audio_close(int dev) -{ - iw_mode = saved_iw_mode; - gus_reset(); - gus_busy = 0; - pcm_opened = 0; - active_device = 0; - - if (recording_active) - { - gus_write8(0x49, 0x00); /* Halt recording */ - set_input_volumes(); - } - recording_active = 0; -} - -static void gus_audio_update_volume(void) -{ - unsigned long flags; - int voice; - - if (pcm_active && pcm_opened) - for (voice = 0; voice < gus_audio_channels; voice++) - { - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_rampoff(); - gus_voice_volume(1530 + (25 * gus_pcm_volume)); - gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); - restore_flags(flags); - } -} - -static void play_next_pcm_block(void) -{ - unsigned long flags; - int speed = gus_audio_speed; - int this_one, is16bits, chn; - unsigned long dram_loc; - unsigned char mode[2], ramp_mode[2]; - - if (!pcm_qlen) - return; - - this_one = pcm_head; - - for (chn = 0; chn < gus_audio_channels; chn++) - { - mode[chn] = 0x00; - ramp_mode[chn] = 0x03; /* Ramping and rollover off */ - - if (chn == 0) - { - mode[chn] |= 0x20; /* Loop IRQ */ - voices[chn].loop_irq_mode = LMODE_PCM; - } - if (gus_audio_bits != 8) - { - is16bits = 1; - mode[chn] |= 0x04; /* 16 bit data */ - } - else - is16bits = 0; - - dram_loc = this_one * pcm_bsize; - dram_loc += chn * pcm_banksize; - - if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */ - { - mode[chn] |= 0x08; /* Enable loop */ - ramp_mode[chn] = 0x03; /* Disable rollover bit */ - } - else - { - if (chn == 0) - ramp_mode[chn] = 0x04; /* Enable rollover bit */ - } - save_flags(flags); - cli(); - gus_select_voice(chn); - gus_voice_freq(speed); - - if (gus_audio_channels == 1) - gus_voice_balance(7); /* mono */ - else if (chn == 0) - gus_voice_balance(0); /* left */ - else - gus_voice_balance(15); /* right */ - - if (!pcm_active) /* Playback not already active */ - { - /* - * The playback was not started yet (or there has been a pause). - * Start the voice (again) and ask for a rollover irq at the end of - * this_one block. If this_one one is last of the buffers, use just - * the normal loop with irq. - */ - - gus_voice_off(); - gus_rampoff(); - gus_voice_volume(1530 + (25 * gus_pcm_volume)); - gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); - - gus_write_addr(0x0a, chn * pcm_banksize, 0, is16bits); /* Starting position */ - gus_write_addr(0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */ - - if (chn != 0) - gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1, - 0, is16bits); /* Loop end location */ - } - if (chn == 0) - gus_write_addr(0x04, dram_loc + pcm_bsize - 1, - 0, is16bits); /* Loop end location */ - else - mode[chn] |= 0x08; /* Enable looping */ - restore_flags(flags); - } - for (chn = 0; chn < gus_audio_channels; chn++) - { - save_flags(flags); - cli(); - gus_select_voice(chn); - gus_write8(0x0d, ramp_mode[chn]); - if (iw_mode) - gus_write8(0x15, 0x00); /* Reset voice deactivate bit of SMSI */ - gus_voice_on(mode[chn]); - restore_flags(flags); - } - pcm_active = 1; -} - -static void gus_transfer_output_block(int dev, unsigned long buf, - int total_count, int intrflag, int chn) -{ - /* - * This routine transfers one block of audio data to the DRAM. In mono mode - * it's called just once. When in stereo mode, this_one routine is called - * once for both channels. - * - * The left/mono channel data is transferred to the beginning of dram and the - * right data to the area pointed by gus_page_size. - */ - - int this_one, count; - unsigned long flags; - unsigned char dma_command; - unsigned long address, hold_address; - - save_flags(flags); - cli(); - - count = total_count / gus_audio_channels; - - if (chn == 0) - { - if (pcm_qlen >= pcm_nblk) - printk(KERN_WARNING "GUS Warning: PCM buffers out of sync\n"); - - this_one = pcm_current_block = pcm_tail; - pcm_qlen++; - pcm_tail = (pcm_tail + 1) % pcm_nblk; - pcm_datasize[this_one] = count; - } - else - this_one = pcm_current_block; - - gus_write8(0x41, 0); /* Disable GF1 DMA */ - DMAbuf_start_dma(dev, buf + (chn * count), count, DMA_MODE_WRITE); - - address = this_one * pcm_bsize; - address += chn * pcm_banksize; - - if (audio_devs[dev]->dmap_out->dma > 3) - { - hold_address = address; - address = address >> 1; - address &= 0x0001ffffL; - address |= (hold_address & 0x000c0000L); - } - gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ - - dma_command = 0x21; /* IRQ enable, DMA start */ - - if (gus_audio_bits != 8) - dma_command |= 0x40; /* 16 bit _DATA_ */ - else - dma_command |= 0x80; /* Invert MSB */ - - if (audio_devs[dev]->dmap_out->dma > 3) - dma_command |= 0x04; /* 16 bit DMA channel */ - - gus_write8(0x41, dma_command); /* Kick start */ - - if (chn == (gus_audio_channels - 1)) /* Last channel */ - { - /* - * Last (right or mono) channel data - */ - dma_active = 1; /* DMA started. There is a unacknowledged buffer */ - active_device = GUS_DEV_PCM_DONE; - if (!pcm_active && (pcm_qlen > 1 || count < pcm_bsize)) - { - play_next_pcm_block(); - } - } - else - { - /* - * Left channel data. The right channel - * is transferred after DMA interrupt - */ - active_device = GUS_DEV_PCM_CONTINUE; - } - - restore_flags(flags); -} - -static void gus_uninterleave8(char *buf, int l) -{ -/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */ - int i, p = 0, halfsize = l / 2; - char *buf2 = buf + halfsize, *src = bounce_buf; - - memcpy(bounce_buf, buf, l); - - for (i = 0; i < halfsize; i++) - { - buf[i] = src[p++]; /* Left channel */ - buf2[i] = src[p++]; /* Right channel */ - } -} - -static void gus_uninterleave16(short *buf, int l) -{ -/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */ - int i, p = 0, halfsize = l / 2; - short *buf2 = buf + halfsize, *src = (short *) bounce_buf; - - memcpy(bounce_buf, (char *) buf, l * 2); - - for (i = 0; i < halfsize; i++) - { - buf[i] = src[p++]; /* Left channel */ - buf2[i] = src[p++]; /* Right channel */ - } -} - -static void gus_audio_output_block(int dev, unsigned long buf, int total_count, - int intrflag) -{ - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - - dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT; - - pcm_current_buf = buf; - pcm_current_count = total_count; - pcm_current_intrflag = intrflag; - pcm_current_dev = dev; - if (gus_audio_channels == 2) - { - char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys); - - if (gus_audio_bits == 8) - gus_uninterleave8(b, total_count); - else - gus_uninterleave16((short *) b, total_count / 2); - } - gus_transfer_output_block(dev, buf, total_count, intrflag, 0); -} - -static void gus_audio_start_input(int dev, unsigned long buf, int count, - int intrflag) -{ - unsigned long flags; - unsigned char mode; - - save_flags(flags); - cli(); - - DMAbuf_start_dma(dev, buf, count, DMA_MODE_READ); - mode = 0xa0; /* DMA IRQ enabled, invert MSB */ - - if (audio_devs[dev]->dmap_in->dma > 3) - mode |= 0x04; /* 16 bit DMA channel */ - if (gus_audio_channels > 1) - mode |= 0x02; /* Stereo */ - mode |= 0x01; /* DMA enable */ - - gus_write8(0x49, mode); - restore_flags(flags); -} - -static int gus_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - unsigned int rate; - - gus_audio_bsize = bsize; - audio_devs[dev]->dmap_in->flags |= DMA_NODMA; - rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; - - gus_write8(0x48, rate & 0xff); /* Set sampling rate */ - - if (gus_audio_bits != 8) - { -/* printk("GUS Error: 16 bit recording not supported\n");*/ - return -EINVAL; - } - return 0; -} - -static int gus_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - int i; - - long mem_ptr, mem_size; - - audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT; - mem_ptr = 0; - mem_size = gus_mem_size / gus_audio_channels; - - if (mem_size > (256 * 1024)) - mem_size = 256 * 1024; - - pcm_bsize = bsize / gus_audio_channels; - pcm_head = pcm_tail = pcm_qlen = 0; - - pcm_nblk = 2; /* MAX_PCM_BUFFERS; */ - if ((pcm_bsize * pcm_nblk) > mem_size) - pcm_nblk = mem_size / pcm_bsize; - - for (i = 0; i < pcm_nblk; i++) - pcm_datasize[i] = 0; - - pcm_banksize = pcm_nblk * pcm_bsize; - - if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024)) - pcm_nblk--; - gus_write8(0x41, 0); /* Disable GF1 DMA */ - return 0; -} - -static int gus_local_qlen(int dev) -{ - return pcm_qlen; -} - - -static struct audio_driver gus_audio_driver = -{ - owner: THIS_MODULE, - open: gus_audio_open, - close: gus_audio_close, - output_block: gus_audio_output_block, - start_input: gus_audio_start_input, - ioctl: gus_audio_ioctl, - prepare_for_input: gus_audio_prepare_for_input, - prepare_for_output: gus_audio_prepare_for_output, - halt_io: gus_audio_reset, - local_qlen: gus_local_qlen, -}; - -static void guswave_setup_voice(int dev, int voice, int chn) -{ - struct channel_info *info = &synth_devs[dev]->chn_info[chn]; - - guswave_set_instr(dev, voice, info->pgm_num); - voices[voice].expression_vol = info->controllers[CTL_EXPRESSION]; /* Just MSB */ - voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128; - voices[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; - voices[voice].bender = 0; - voices[voice].bender_range = info->bender_range; - - if (chn == 9) - voices[voice].fixed_pitch = 1; -} - -static void guswave_bender(int dev, int voice, int value) -{ - int freq; - unsigned long flags; - - voices[voice].bender = value - 8192; - freq = compute_finetune(voices[voice].orig_freq, value - 8192, voices[voice].bender_range, 0); - voices[voice].current_freq = freq; - - save_flags(flags); - cli(); - gus_select_voice(voice); - gus_voice_freq(freq); - restore_flags(flags); -} - -static int guswave_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) -{ - int i, p, best = -1, best_time = 0x7fffffff; - - p = alloc->ptr; - /* - * First look for a completely stopped voice - */ - - for (i = 0; i < alloc->max_voice; i++) - { - if (alloc->map[p] == 0) - { - alloc->ptr = p; - return p; - } - if (alloc->alloc_times[p] < best_time) - { - best = p; - best_time = alloc->alloc_times[p]; - } - p = (p + 1) % alloc->max_voice; - } - - /* - * Then look for a releasing voice - */ - - for (i = 0; i < alloc->max_voice; i++) - { - if (alloc->map[p] == 0xffff) - { - alloc->ptr = p; - return p; - } - p = (p + 1) % alloc->max_voice; - } - if (best >= 0) - p = best; - - alloc->ptr = p; - return p; -} - -static struct synth_operations guswave_operations = -{ - owner: THIS_MODULE, - id: "GUS", - info: &gus_info, - midi_dev: 0, - synth_type: SYNTH_TYPE_SAMPLE, - synth_subtype: SAMPLE_TYPE_GUS, - open: guswave_open, - close: guswave_close, - ioctl: guswave_ioctl, - kill_note: guswave_kill_note, - start_note: guswave_start_note, - set_instr: guswave_set_instr, - reset: guswave_reset, - hw_control: guswave_hw_control, - load_patch: guswave_load_patch, - aftertouch: guswave_aftertouch, - controller: guswave_controller, - panning: guswave_panning, - volume_method: guswave_volume_method, - bender: guswave_bender, - alloc_voice: guswave_alloc, - setup_voice: guswave_setup_voice -}; - -static void set_input_volumes(void) -{ - unsigned long flags; - unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ - - if (have_gus_max) /* Don't disturb GUS MAX */ - return; - - save_flags(flags); - cli(); - - /* - * Enable channels having vol > 10% - * Note! bit 0x01 means the line in DISABLED while 0x04 means - * the mic in ENABLED. - */ - if (gus_line_vol > 10) - mask &= ~0x01; - if (gus_mic_vol > 10) - mask |= 0x04; - - if (recording_active) - { - /* - * Disable channel, if not selected for recording - */ - if (!(gus_recmask & SOUND_MASK_LINE)) - mask |= 0x01; - if (!(gus_recmask & SOUND_MASK_MIC)) - mask &= ~0x04; - } - mix_image &= ~0x07; - mix_image |= mask & 0x07; - outb((mix_image), u_Mixer); - - restore_flags(flags); -} - -#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ - SOUND_MASK_SYNTH|SOUND_MASK_PCM) - -int gus_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int vol, val; - - if (((cmd >> 8) & 0xff) != 'M') - return -EINVAL; - - if (!access_ok(VERIFY_WRITE, (int *)arg, sizeof(int))) - return -EFAULT; - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - { - if (__get_user(val, (int *) arg)) - return -EFAULT; - - switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - gus_recmask = val & MIX_DEVS; - if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) - gus_recmask = SOUND_MASK_MIC; - /* Note! Input volumes are updated during next open for recording */ - val = gus_recmask; - break; - - case SOUND_MIXER_MIC: - vol = val & 0xff; - if (vol < 0) - vol = 0; - if (vol > 100) - vol = 100; - gus_mic_vol = vol; - set_input_volumes(); - val = vol | (vol << 8); - break; - - case SOUND_MIXER_LINE: - vol = val & 0xff; - if (vol < 0) - vol = 0; - if (vol > 100) - vol = 100; - gus_line_vol = vol; - set_input_volumes(); - val = vol | (vol << 8); - break; - - case SOUND_MIXER_PCM: - gus_pcm_volume = val & 0xff; - if (gus_pcm_volume < 0) - gus_pcm_volume = 0; - if (gus_pcm_volume > 100) - gus_pcm_volume = 100; - gus_audio_update_volume(); - val = gus_pcm_volume | (gus_pcm_volume << 8); - break; - - case SOUND_MIXER_SYNTH: - gus_wave_volume = val & 0xff; - if (gus_wave_volume < 0) - gus_wave_volume = 0; - if (gus_wave_volume > 100) - gus_wave_volume = 100; - if (active_device == GUS_DEV_WAVE) - { - int voice; - for (voice = 0; voice < nr_voices; voice++) - dynamic_volume_change(voice); /* Apply the new vol */ - } - val = gus_wave_volume | (gus_wave_volume << 8); - break; - - default: - return -EINVAL; - } - } - else - { - switch (cmd & 0xff) - { - /* - * Return parameters - */ - case SOUND_MIXER_RECSRC: - val = gus_recmask; - break; - - case SOUND_MIXER_DEVMASK: - val = MIX_DEVS; - break; - - case SOUND_MIXER_STEREODEVS: - val = 0; - break; - - case SOUND_MIXER_RECMASK: - val = SOUND_MASK_MIC | SOUND_MASK_LINE; - break; - - case SOUND_MIXER_CAPS: - val = 0; - break; - - case SOUND_MIXER_MIC: - val = gus_mic_vol | (gus_mic_vol << 8); - break; - - case SOUND_MIXER_LINE: - val = gus_line_vol | (gus_line_vol << 8); - break; - - case SOUND_MIXER_PCM: - val = gus_pcm_volume | (gus_pcm_volume << 8); - break; - - case SOUND_MIXER_SYNTH: - val = gus_wave_volume | (gus_wave_volume << 8); - break; - - default: - return -EINVAL; - } - } - return __put_user(val, (int *)arg); -} - -static struct mixer_operations gus_mixer_operations = -{ - owner: THIS_MODULE, - id: "GUS", - name: "Gravis Ultrasound", - ioctl: gus_default_mixer_ioctl -}; - -static int __init gus_default_mixer_init(void) -{ - int n; - - if ((n = sound_alloc_mixerdev()) != -1) - { - /* - * Don't install if there is another - * mixer - */ - mixer_devs[n] = &gus_mixer_operations; - } - if (have_gus_max) - { - /* - * Enable all mixer channels on the GF1 side. Otherwise recording will - * not be possible using GUS MAX. - */ - mix_image &= ~0x07; - mix_image |= 0x04; /* All channels enabled */ - outb((mix_image), u_Mixer); - } - return n; -} - -void __init gus_wave_init(struct address_info *hw_config) -{ - unsigned long flags; - unsigned char val; - char *model_num = "2.4"; - char tmp[64], tmp2[64]; - int gus_type = 0x24; /* 2.4 */ - - int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2; - int sdev; - - hw_config->slots[0] = -1; /* No wave */ - hw_config->slots[1] = -1; /* No ad1848 */ - hw_config->slots[4] = -1; /* No audio */ - hw_config->slots[5] = -1; /* No mixer */ - - if (!gus_pnp_flag) - { - if (irq < 0 || irq > 15) - { - printk(KERN_ERR "ERROR! Invalid IRQ#%d. GUS Disabled", irq); - return; - } - } - - if (dma < 0 || dma > 7 || dma == 4) - { - printk(KERN_ERR "ERROR! Invalid DMA#%d. GUS Disabled", dma); - return; - } - gus_irq = irq; - gus_dma = dma; - gus_dma2 = dma2; - gus_hw_config = hw_config; - - if (gus_dma2 == -1) - gus_dma2 = dma; - - /* - * Try to identify the GUS model. - * - * Versions < 3.6 don't have the digital ASIC. Try to probe it first. - */ - - save_flags(flags); - cli(); - outb((0x20), gus_base + 0x0f); - val = inb(gus_base + 0x0f); - restore_flags(flags); - - if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */ - { - int ad_flags = 0; - - if (gus_pnp_flag) - ad_flags = 0x12345678; /* Interwave "magic" */ - /* - * It has the digital ASIC so the card is at least v3.4. - * Next try to detect the true model. - */ - - if (gus_pnp_flag) /* Hack hack hack */ - val = 10; - else - val = inb(u_MixSelect); - - /* - * Value 255 means pre-3.7 which don't have mixer. - * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. - * 10 and above is GUS MAX which has the CS4231 codec/mixer. - * - */ - - if (val == 255 || val < 5) - { - model_num = "3.4"; - gus_type = 0x34; - } - else if (val < 10) - { - model_num = "3.7"; - gus_type = 0x37; - mixer_type = ICS2101; - request_region(u_MixSelect, 1, "GUS mixer"); - } - else - { - model_num = "MAX"; - gus_type = 0x40; - mixer_type = CS4231; -#ifdef CONFIG_SOUND_GUSMAX - { - unsigned char max_config = 0x40; /* Codec enable */ - - if (gus_dma2 == -1) - gus_dma2 = gus_dma; - - if (gus_dma > 3) - max_config |= 0x10; /* 16 bit capture DMA */ - - if (gus_dma2 > 3) - max_config |= 0x20; /* 16 bit playback DMA */ - - max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ - - outb((max_config), gus_base + 0x106); /* UltraMax control */ - } - - if (ad1848_detect(gus_base + 0x10c, &ad_flags, hw_config->osp)) - { - char *name = "GUS MAX"; - int old_num_mixers = num_mixers; - - if (gus_pnp_flag) - name = "GUS PnP"; - - gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; - gus_wave_volume = 90; - have_gus_max = 1; - if (hw_config->name) - name = hw_config->name; - - hw_config->slots[1] = ad1848_init(name, gus_base + 0x10c, - -irq, gus_dma2, /* Playback DMA */ - gus_dma, /* Capture DMA */ - 1, /* Share DMA channels with GF1 */ - hw_config->osp, - THIS_MODULE); - - if (num_mixers > old_num_mixers) - { - /* GUS has it's own mixer map */ - AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH); - AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); - AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); - } - } - else - printk(KERN_WARNING "GUS: No CS4231 ??"); -#else - printk(KERN_ERR "GUS MAX found, but not compiled in\n"); -#endif - } - } - else - { - /* - * ASIC not detected so the card must be 2.2 or 2.4. - * There could still be the 16-bit/mixer daughter card. - */ - } - - if (hw_config->name) - { - strncpy(tmp, hw_config->name, 45); - tmp[45] = 0; - sprintf(tmp2, "%s (%dk)", tmp, (int) gus_mem_size / 1024); - tmp2[sizeof(tmp2) - 1] = 0; - } - else if (gus_pnp_flag) - { - sprintf(tmp2, "Gravis UltraSound PnP (%dk)", - (int) gus_mem_size / 1024); - } - else - sprintf(tmp2, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); - - - samples = (struct patch_info *)vmalloc((MAX_SAMPLE + 1) * sizeof(*samples)); - if (samples == NULL) - { - printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n"); - return; - } - conf_printf(tmp2, hw_config); - tmp2[sizeof(gus_info.name) - 1] = 0; - strcpy(gus_info.name, tmp2); - - if ((sdev = sound_alloc_synthdev()) == -1) - printk(KERN_WARNING "gus_init: Too many synthesizers\n"); - else - { - voice_alloc = &guswave_operations.alloc; - if (iw_mode) - guswave_operations.id = "IWAVE"; - hw_config->slots[0] = sdev; - synth_devs[sdev] = &guswave_operations; - sequencer_init(); - gus_tmr_install(gus_base + 8); - } - - reset_sample_memory(); - - gus_initialize(); - - if ((gus_mem_size > 0) & !gus_no_wave_dma) - { - hw_config->slots[4] = -1; - if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION, - "Ultrasound", - &gus_audio_driver, - sizeof(struct audio_driver), - NEEDS_RESTART | - ((!iw_mode && dma2 != dma && dma2 != -1) ? - DMA_DUPLEX : 0), - AFMT_U8 | AFMT_S16_LE, - NULL, dma, dma2)) < 0) - { - return; - } - - hw_config->slots[4] = gus_devnum; - audio_devs[gus_devnum]->min_fragment = 9; /* 512k */ - audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */ - audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */ - audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; - } - - /* - * Mixer dependent initialization. - */ - - switch (mixer_type) - { - case ICS2101: - gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; - gus_wave_volume = 90; - request_region(u_MixSelect, 1, "GUS mixer"); - hw_config->slots[5] = ics2101_mixer_init(); - audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ - return; - - case CS4231: - /* Initialized elsewhere (ad1848.c) */ - default: - hw_config->slots[5] = gus_default_mixer_init(); - audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ - return; - } -} - -void __exit gus_wave_unload(struct address_info *hw_config) -{ -#ifdef CONFIG_SOUND_GUSMAX - if (have_gus_max) - { - ad1848_unload(gus_base + 0x10c, - -gus_irq, - gus_dma2, /* Playback DMA */ - gus_dma, /* Capture DMA */ - 1); /* Share DMA channels with GF1 */ - } -#endif - - if (mixer_type == ICS2101) - { - release_region(u_MixSelect, 1); - } - if (hw_config->slots[0] != -1) - sound_unload_synthdev(hw_config->slots[0]); - if (hw_config->slots[1] != -1) - sound_unload_audiodev(hw_config->slots[1]); - if (hw_config->slots[2] != -1) - sound_unload_mididev(hw_config->slots[2]); - if (hw_config->slots[4] != -1) - sound_unload_audiodev(hw_config->slots[4]); - if (hw_config->slots[5] != -1) - sound_unload_mixerdev(hw_config->slots[5]); - - if(samples) - vfree(samples); - samples=NULL; -} - -static void do_loop_irq(int voice) -{ - unsigned char tmp; - int mode, parm; - unsigned long flags; - - save_flags(flags); - cli(); - gus_select_voice(voice); - - tmp = gus_read8(0x00); - tmp &= ~0x20; /* - * Disable wave IRQ for this_one voice - */ - gus_write8(0x00, tmp); - - if (tmp & 0x03) /* Voice stopped */ - voice_alloc->map[voice] = 0; - - mode = voices[voice].loop_irq_mode; - voices[voice].loop_irq_mode = 0; - parm = voices[voice].loop_irq_parm; - - switch (mode) - { - case LMODE_FINISH: /* - * Final loop finished, shoot volume down - */ - - if ((int) (gus_read16(0x09) >> 4) < 100) /* - * Get current volume - */ - { - gus_voice_off(); - gus_rampoff(); - gus_voice_init(voice); - break; - } - gus_ramp_range(65, 4065); - gus_ramp_rate(0, 63); /* - * Fastest possible rate - */ - gus_rampon(0x20 | 0x40); /* - * Ramp down, once, irq - */ - voices[voice].volume_irq_mode = VMODE_HALT; - break; - - case LMODE_PCM_STOP: - pcm_active = 0; /* Signal to the play_next_pcm_block routine */ - case LMODE_PCM: - { - pcm_qlen--; - pcm_head = (pcm_head + 1) % pcm_nblk; - if (pcm_qlen && pcm_active) - { - play_next_pcm_block(); - } - else - { - /* Underrun. Just stop the voice */ - gus_select_voice(0); /* Left channel */ - gus_voice_off(); - gus_rampoff(); - gus_select_voice(1); /* Right channel */ - gus_voice_off(); - gus_rampoff(); - pcm_active = 0; - } - - /* - * If the queue was full before this interrupt, the DMA transfer was - * suspended. Let it continue now. - */ - - if (audio_devs[gus_devnum]->dmap_out->qlen > 0) - DMAbuf_outputintr(gus_devnum, 0); - } - break; - - default: - break; - } - restore_flags(flags); -} - -static void do_volume_irq(int voice) -{ - unsigned char tmp; - int mode, parm; - unsigned long flags; - - save_flags(flags); - cli(); - - gus_select_voice(voice); - tmp = gus_read8(0x0d); - tmp &= ~0x20; /* - * Disable volume ramp IRQ - */ - gus_write8(0x0d, tmp); - - mode = voices[voice].volume_irq_mode; - voices[voice].volume_irq_mode = 0; - parm = voices[voice].volume_irq_parm; - - switch (mode) - { - case VMODE_HALT: /* Decay phase finished */ - if (iw_mode) - gus_write8(0x15, 0x02); /* Set voice deactivate bit of SMSI */ - restore_flags(flags); - gus_voice_init(voice); - break; - - case VMODE_ENVELOPE: - gus_rampoff(); - restore_flags(flags); - step_envelope(voice); - break; - - case VMODE_START_NOTE: - restore_flags(flags); - guswave_start_note2(voices[voice].dev_pending, voice, - voices[voice].note_pending, voices[voice].volume_pending); - if (voices[voice].kill_pending) - guswave_kill_note(voices[voice].dev_pending, voice, - voices[voice].note_pending, 0); - - if (voices[voice].sample_pending >= 0) - { - guswave_set_instr(voices[voice].dev_pending, voice, - voices[voice].sample_pending); - voices[voice].sample_pending = -1; - } - break; - - default: - restore_flags(flags); - } - restore_flags(flags); -} - -void gus_voice_irq(void) -{ - unsigned long wave_ignore = 0, volume_ignore = 0; - unsigned long voice_bit; - - unsigned char src, voice; - - while (1) - { - src = gus_read8(0x0f); /* - * Get source info - */ - voice = src & 0x1f; - src &= 0xc0; - - if (src == (0x80 | 0x40)) - return; /* - * No interrupt - */ - - voice_bit = 1 << voice; - - if (!(src & 0x80)) /* - * Wave IRQ pending - */ - if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /* - * Not done - * yet - */ - { - wave_ignore |= voice_bit; - do_loop_irq(voice); - } - if (!(src & 0x40)) /* - * Volume IRQ pending - */ - if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /* - * Not done - * yet - */ - { - volume_ignore |= voice_bit; - do_volume_irq(voice); - } - } -} - -void guswave_dma_irq(void) -{ - unsigned char status; - - status = gus_look8(0x41); /* Get DMA IRQ Status */ - if (status & 0x40) /* DMA interrupt pending */ - switch (active_device) - { - case GUS_DEV_WAVE: - wake_up(&dram_sleeper); - break; - - case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */ - gus_write8(0x41, 0); /* Disable GF1 DMA */ - gus_transfer_output_block(pcm_current_dev, pcm_current_buf, - pcm_current_count, - pcm_current_intrflag, 1); - break; - - case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */ - gus_write8(0x41, 0); /* Disable GF1 DMA */ - if (pcm_qlen < pcm_nblk) - { - dma_active = 0; - if (gus_busy) - { - if (audio_devs[gus_devnum]->dmap_out->qlen > 0) - DMAbuf_outputintr(gus_devnum, 0); - } - } - break; - - default: - break; - } - status = gus_look8(0x49); /* - * Get Sampling IRQ Status - */ - if (status & 0x40) /* - * Sampling Irq pending - */ - { - DMAbuf_inputintr(gus_devnum); - } -} - -/* - * Timer stuff - */ - -static volatile int select_addr, data_addr; -static volatile int curr_timer = 0; - -void gus_timer_command(unsigned int addr, unsigned int val) -{ - int i; - - outb(((unsigned char) (addr & 0xff)), select_addr); - - for (i = 0; i < 2; i++) - inb(select_addr); - - outb(((unsigned char) (val & 0xff)), data_addr); - - for (i = 0; i < 2; i++) - inb(select_addr); -} - -static void arm_timer(int timer, unsigned int interval) -{ - curr_timer = timer; - - if (timer == 1) - { - gus_write8(0x46, 256 - interval); /* Set counter for timer 1 */ - gus_write8(0x45, 0x04); /* Enable timer 1 IRQ */ - gus_timer_command(0x04, 0x01); /* Start timer 1 */ - } - else - { - gus_write8(0x47, 256 - interval); /* Set counter for timer 2 */ - gus_write8(0x45, 0x08); /* Enable timer 2 IRQ */ - gus_timer_command(0x04, 0x02); /* Start timer 2 */ - } - - gus_timer_enabled = 1; -} - -static unsigned int gus_tmr_start(int dev, unsigned int usecs_per_tick) -{ - int timer_no, resolution; - int divisor; - - if (usecs_per_tick > (256 * 80)) - { - timer_no = 2; - resolution = 320; /* usec */ - } - else - { - timer_no = 1; - resolution = 80; /* usec */ - } - divisor = (usecs_per_tick + (resolution / 2)) / resolution; - arm_timer(timer_no, divisor); - - return divisor * resolution; -} - -static void gus_tmr_disable(int dev) -{ - gus_write8(0x45, 0); /* Disable both timers */ - gus_timer_enabled = 0; -} - -static void gus_tmr_restart(int dev) -{ - if (curr_timer == 1) - gus_write8(0x45, 0x04); /* Start timer 1 again */ - else - gus_write8(0x45, 0x08); /* Start timer 2 again */ - gus_timer_enabled = 1; -} - -static struct sound_lowlev_timer gus_tmr = -{ - 0, - 1, - gus_tmr_start, - gus_tmr_disable, - gus_tmr_restart -}; - -static void gus_tmr_install(int io_base) -{ - struct sound_lowlev_timer *tmr; - - select_addr = io_base; - data_addr = io_base + 1; - - tmr = &gus_tmr; - -#ifdef THIS_GETS_FIXED - sound_timer_init(&gus_tmr, "GUS"); -#endif -} diff -Nru a/drivers/sound/hex2hex.c b/drivers/sound/hex2hex.c --- a/drivers/sound/hex2hex.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,101 +0,0 @@ -/* - * hex2hex reads stdin in Intel HEX format and produces an - * (unsigned char) array which contains the bytes and writes it - * to stdout using C syntax - */ - -#include -#include -#include - -#define ABANDON(why) { fprintf(stderr, "%s\n", why); exit(1); } -#define MAX_SIZE (256*1024) -unsigned char buf[MAX_SIZE]; - -int loadhex(FILE *inf, unsigned char *buf) -{ - int l=0, c, i; - - while ((c=getc(inf))!=EOF) - { - if (c == ':') /* Sync with beginning of line */ - { - int n, check; - unsigned char sum; - int addr; - int linetype; - - if (fscanf(inf, "%02x", &n) != 1) - ABANDON("File format error"); - sum = n; - - if (fscanf(inf, "%04x", &addr) != 1) - ABANDON("File format error"); - sum += addr/256; - sum += addr%256; - - if (fscanf(inf, "%02x", &linetype) != 1) - ABANDON("File format error"); - sum += linetype; - - if (linetype != 0) - continue; - - for (i=0;i= MAX_SIZE) - ABANDON("File too large"); - buf[addr++] = c; - if (addr > l) - l = addr; - sum += c; - } - - if (fscanf(inf, "%02x", &check) != 1) - ABANDON("File format error"); - - sum = ~sum + 1; - if (check != sum) - ABANDON("Line checksum error"); - } - } - - return l; -} - -int main( int argc, const char * argv [] ) -{ - const char * varline; - int i,l; - int id=0; - - if(argv[1] && strcmp(argv[1], "-i")==0) - { - argv++; - argc--; - id=1; - } - if(argv[1]==NULL) - { - fprintf(stderr,"hex2hex: [-i] filename\n"); - exit(1); - } - varline = argv[1]; - l = loadhex(stdin, buf); - - printf("/*\n *\t Computer generated file. Do not edit.\n */\n"); - printf("static int %s_len = %d;\n", varline, l); - printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":""); - - for (i=0;i - * - * Built from: - * Low level code: Zach Brown (original nonworking i810 OSS driver) - * Jaroslav Kysela (working ALSA driver) - * - * Framework: Thomas Sailer - * Extended by: Zach Brown - * and others.. - * - * Hardware Provided By: - * Analog Devices (A major AC97 codec maker) - * Intel Corp (you've probably heard of them already) - * - * AC97 clues and assistance provided by - * Analog Devices - * Zach 'Fufu' Brown - * Jeff Garzik - * - * 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. - * - * - * Intel 810 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. - * - * Fix The Sound On Dell - * - * Not everyone uses 48KHz. We know of no way to detect this reliably - * and certainly not to get the right data. If your i810 audio sounds - * stupid you may need to investigate other speeds. According to Analog - * they tend to use a 14.318MHz clock which gives you a base rate of - * 41194Hz. - * - * This is available via the 'ftsodell=1' option. - * - * If you need to force a specific rate set the clocking= option - * - * This driver is cursed. (Ben LaHaise) - */ - -#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_INTEL_82801 -#define PCI_DEVICE_ID_INTEL_82801 0x2415 -#endif -#ifndef PCI_DEVICE_ID_INTEL_82901 -#define PCI_DEVICE_ID_INTEL_82901 0x2425 -#endif -#ifndef PCI_DEVICE_ID_INTEL_ICH2 -#define PCI_DEVICE_ID_INTEL_ICH2 0x2445 -#endif -#ifndef PCI_DEVICE_ID_INTEL_ICH3 -#define PCI_DEVICE_ID_INTEL_ICH3 0x2485 -#endif -#ifndef PCI_DEVICE_ID_INTEL_440MX -#define PCI_DEVICE_ID_INTEL_440MX 0x7195 -#endif -#ifndef PCI_DEVICE_ID_SI_7012 -#define PCI_DEVICE_ID_SI_7012 0x7012 -#endif -#ifndef PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO -#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1 -#endif - -static int ftsodell=0; -static int strict_clocking=0; -static unsigned int clocking=0; -static int spdif_locked=0; - -//#define DEBUG -//#define DEBUG2 -//#define DEBUG_INTERRUPTS -//#define DEBUG_MMAP - -#define ADC_RUNNING 1 -#define DAC_RUNNING 2 - -#define I810_FMT_16BIT 1 -#define I810_FMT_STEREO 2 -#define I810_FMT_MASK 3 - -#define SPDIF_ON 0x0004 -#define SURR_ON 0x0010 -#define CENTER_LFE_ON 0x0020 -#define VOL_MUTED 0x8000 - -/* 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 i810 channel */ -#define SG_LEN 32 -struct i810_channel -{ - /* these sg guys should probably be allocated - seperately 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 seperate 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##_PIV = 0x##DIG##a, /* Prefetched Index Value */ \ - PRE##_CR = 0x##DIG##b /* Control Register */ \ -} - -ENUM_ENGINE(OFF,0); /* Offsets */ -ENUM_ENGINE(PI,0); /* PCM In */ -ENUM_ENGINE(PO,1); /* PCM Out */ -ENUM_ENGINE(MC,2); /* Mic In */ - -enum { - GLOB_CNT = 0x2c, /* Global Control */ - GLOB_STA = 0x30, /* Global Status */ - CAS = 0x34 /* Codec Write Semaphore Register */ -}; - -/* 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) */ -#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI) - -/* interrupts for the whole chip */ -#define INT_SEC (1<<11) -#define INT_PRI (1<<10) -#define INT_MC (1<<7) -#define INT_PO (1<<6) -#define INT_PI (1<<5) -#define INT_MO (1<<2) -#define INT_NI (1<<1) -#define INT_GPI (1<<0) -#define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI) - - -#define DRIVER_VERSION "0.21" - -/* magic numbers to protect our data structures */ -#define I810_CARD_MAGIC 0x5072696E /* "Prin" */ -#define I810_STATE_MAGIC 0x63657373 /* "cess" */ -#define I810_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ -#define NR_HW_CH 3 - -/* 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 }; - -enum { - ICH82801AA = 0, - ICH82901AB, - INTEL440MX, - INTELICH2, - INTELICH3, - SI7012, - NVIDIA_NFORCE -}; - -static char * card_names[] = { - "Intel ICH 82801AA", - "Intel ICH 82901AB", - "Intel 440MX", - "Intel ICH2", - "Intel ICH3", - "SiS 7012", - "NVIDIA nForce Audio" -}; - -static struct pci_device_id i810_pci_tbl [] __initdata = { - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82801AA}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82901, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82901AB}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_440MX, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTEL440MX}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH2}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH3}, - {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, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, NVIDIA_NFORCE}, - {0,} -}; - -MODULE_DEVICE_TABLE (pci, i810_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 i810_state { - unsigned int magic; - struct i810_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 i810_channel *read_channel; - struct i810_channel *write_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 i810_card { - struct i810_channel channel[3]; - unsigned int magic; - - /* We keep i810 cards in a linked list */ - struct i810_card *next; - - /* The i810 has a certain amount of cross channel interaction - so we use a single per card lock */ - spinlock_t 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 i810_state *states[NR_HW_CH]; - - u16 ac97_features; - u16 ac97_status; - u16 channels; - - /* hardware resources */ - unsigned long iobase; - unsigned long ac97base; - u32 irq; - - /* Function support */ - struct i810_channel *(*alloc_pcm_channel)(struct i810_card *); - struct i810_channel *(*alloc_rec_pcm_channel)(struct i810_card *); - struct i810_channel *(*alloc_rec_mic_channel)(struct i810_card *); - void (*free_pcm_channel)(struct i810_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 i810_card *devs = NULL; - -static int i810_open_mixdev(struct inode *inode, struct file *file); -static int i810_ioctl_mixdev(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg); -static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); - -static struct i810_channel *i810_alloc_pcm_channel(struct i810_card *card) -{ - if(card->channel[1].used==1) - return NULL; - card->channel[1].used=1; - return &card->channel[1]; -} - -static struct i810_channel *i810_alloc_rec_pcm_channel(struct i810_card *card) -{ - if(card->channel[0].used==1) - return NULL; - card->channel[0].used=1; - return &card->channel[0]; -} - -static struct i810_channel *i810_alloc_rec_mic_channel(struct i810_card *card) -{ - if(card->channel[2].used==1) - return NULL; - card->channel[2].used=1; - return &card->channel[2]; -} - -static void i810_free_pcm_channel(struct i810_card *card, int channel) -{ - card->channel[channel].used=0; -} - -static int i810_valid_spdif_rate ( struct ac97_codec *codec, int rate ) -{ - unsigned long id = 0L; - - id = (i810_ac97_get(codec, AC97_VENDOR_ID1) << 16); - id |= i810_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff; -#ifdef DEBUG - printk ( "i810_audio: codec = %s, codec_id = 0x%08lx\n", codec->name, id); -#endif - switch ( id ) { - case 0x41445361: /* AD1886 */ - 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); -} - -/* i810_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 i810_set_spdif_output(struct i810_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)) { -#ifdef DEBUG - printk(KERN_WARNING "i810_audio: S/PDIF transmitter not available.\n"); -#endif - 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)); - - /* 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; - } - - 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 - - /* turn off S/PDIF */ - i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); - 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)); - } -} - -/* i810_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 i810_set_dac_channels(struct i810_state *state, int channel) -{ - int aud_reg; - struct ac97_codec *codec = state->card->ac97_codec[0]; - - aud_reg = i810_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; - } - i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); - -} - - -/* set playback sample rate */ -static unsigned int i810_set_dac_rate(struct i810_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; -#ifdef DEBUG - printk("Asked for %d Hz, but ac97_features says we only do %dHz. Sorry!\n", - rate,clocking); -#endif - 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; - } -#ifdef DEBUG - printk("i810_audio: called i810_set_dac_rate : asked for %d, got %d\n", rate, dmabuf->rate); -#endif - rate = new_rate; - return dmabuf->rate; -} - -/* set recording sample rate */ -static unsigned int i810_set_adc_rate(struct i810_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; - } -#ifdef DEBUG - printk("i810_audio: called i810_set_adc_rate : rate = %d/%d\n", dmabuf->rate, rate); -#endif - return dmabuf->rate; -} - -/* get current playback/recording dma buffer pointer (byte offset from LBA), - called with spinlock held! */ - -static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned int civ, offset, port, port_picb, bytes = 2; - - if (!dmabuf->enable) - return 0; - - if (rec) - port = state->card->iobase + dmabuf->read_channel->port; - else - port = state->card->iobase + dmabuf->write_channel->port; - - if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) { - port_picb = port + OFF_SR; - bytes = 1; - } else - 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 the right place, and we will suddenly think - * we just went forward dmasize - fragsize bytes, causing - * totally stupid *huge* dma overrun messages. We are - * assuming that the 1us delay 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)); - - return (((civ + 1) * dmabuf->fragsize - (bytes * offset)) - % dmabuf->dmasize); -} - -/* Stop recording (lock held) */ -static inline void __stop_adc(struct i810_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct i810_card *card = state->card; - - dmabuf->enable &= ~ADC_RUNNING; - outb(0, card->iobase + PI_CR); - // wait for the card to acknowledge shutdown - while( inb(card->iobase + PI_CR) != 0 ) ; - // now clear any latent interrupt bits (like the halt bit) - if(card->pci_id == PCI_DEVICE_ID_SI_7012) - outb( inb(card->iobase + PI_PICB), card->iobase + PI_PICB ); - else - outb( inb(card->iobase + PI_SR), card->iobase + PI_SR ); - outl( inl(card->iobase + GLOB_STA) & INT_PI, card->iobase + GLOB_STA); -} - -static void stop_adc(struct i810_state *state) -{ - struct i810_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 i810_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) | 1, state->card->iobase + PI_CR); - } -} - -static void start_adc(struct i810_state *state) -{ - struct i810_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 i810_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct i810_card *card = state->card; - - dmabuf->enable &= ~DAC_RUNNING; - outb(0, card->iobase + PO_CR); - // wait for the card to acknowledge shutdown - while( inb(card->iobase + PO_CR) != 0 ) ; - // now clear any latent interrupt bits (like the halt bit) - if(card->pci_id == PCI_DEVICE_ID_SI_7012) - outb( inb(card->iobase + PO_PICB), card->iobase + PO_PICB ); - else - outb( inb(card->iobase + PO_SR), card->iobase + PO_SR ); - outl( inl(card->iobase + GLOB_STA) & INT_PO, card->iobase + GLOB_STA); -} - -static void stop_dac(struct i810_state *state) -{ - struct i810_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 i810_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) | 1, state->card->iobase + PO_CR); - } -} -static void start_dac(struct i810_state *state) -{ - struct i810_card *card = state->card; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - __start_dac(state); - spin_unlock_irqrestore(&card->lock, flags); -} - -#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -/* allocate DMA buffer, playback and recording buffer should be allocated seperately */ -static int alloc_dmabuf(struct i810_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<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< size ) - continue; - if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, - PAGE_SIZE << order, - &dmabuf->dma_handle))) - break; - } - if (!rawbuf) - return -ENOMEM; - - -#ifdef DEBUG - printk("i810_audio: allocated %ld (order = %d) bytes at %p\n", - PAGE_SIZE << order, order, rawbuf); -#endif - - 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++) - mem_map_reserve(page); - - return 0; -} - -/* free DMA buffer */ -static void dealloc_dmabuf(struct i810_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++) - mem_map_unreserve(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 i810_state *state, unsigned rec) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct i810_channel *c; - 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); - 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(dmabuf->read_channel) - c = dmabuf->read_channel; - else - c = dmabuf->write_channel; - while(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;inumfrag;i++) - { - sg->busaddr=virt_to_bus(dmabuf->rawbuf+dmabuf->fragsize*i); - // the card will always be doing 16bit stereo - sg->control=dmabuf->fragsamples; - if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) - sg->control <<= 1; - sg->control|=CON_BUFPAD; - // 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); - - if(c != dmabuf->write_channel) - c = dmabuf->write_channel; - else - c = NULL; - } - - /* set the ready flag for the dma buffer */ - dmabuf->ready = 1; - -#ifdef DEBUG - printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, " - "fragsize = %d dmasize = %d\n", - dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, - dmabuf->fragsize, dmabuf->dmasize); -#endif - - return 0; -} - -static void __i810_update_lvi(struct i810_state *state, int rec) -{ - struct dmabuf *dmabuf = &state->dmabuf; - int x, port; - - port = state->card->iobase; - if(rec) - port += dmabuf->read_channel->port; - else - 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))) ) ; - } 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))) ) ; - } - } - - /* 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 i810_update_lvi(struct i810_state *state, int rec) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - - if(!dmabuf->ready) - return; - spin_lock_irqsave(&state->card->lock, flags); - __i810_update_lvi(state, rec); - spin_unlock_irqrestore(&state->card->lock, flags); -} - -/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ -static void i810_update_ptr(struct i810_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 = i810_get_dma_addr(state, 1); - diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; -#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) - printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); -#endif - 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 "i810_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 = i810_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 "i810_audio: DMA overrun on write\n"); - printk("i810_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); - } -} - -static inline int i810_get_free_write_space(struct i810_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - int free; - - i810_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 i810_get_available_read_data(struct i810_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - int avail; - - i810_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 i810_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); - i810_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; - i810_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 "i810_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 void i810_channel_interrupt(struct i810_card *card) -{ - int i, count; - -#ifdef DEBUG_INTERRUPTS - printk("CHANNEL "); -#endif - for(i=0;istates[i]; - struct i810_channel *c; - struct dmabuf *dmabuf; - unsigned long port = card->iobase; - u16 status; - - if(!state) - continue; - if(!state->dmabuf.ready) - continue; - dmabuf = &state->dmabuf; - if(dmabuf->enable & DAC_RUNNING) { - c=dmabuf->write_channel; - } else if(dmabuf->enable & ADC_RUNNING) { - c=dmabuf->read_channel; - } else /* This can occur going from R/W to close */ - continue; - - port+=c->port; - - if(card->pci_id == PCI_DEVICE_ID_SI_7012) - status = inw(port + OFF_PICB); - else - status = inw(port + OFF_SR); - -#ifdef DEBUG_INTERRUPTS - printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status); -#endif - if(status & DMA_INT_COMPLETE) - { - /* only wake_up() waiters if this interrupt signals - * us being beyond a userfragsize of data open or - * available, and i810_update_ptr() does that for - * us - */ - i810_update_ptr(state); -#ifdef DEBUG_INTERRUPTS - printk("COMP %d ", dmabuf->hwptr / - dmabuf->fragsize); -#endif - } - if(status & (DMA_INT_LVI | DMA_INT_DCH)) - { - /* wake_up() unconditionally on LVI and DCH */ - i810_update_ptr(state); - wake_up(&dmabuf->wait); -#ifdef DEBUG_INTERRUPTS - if(status & DMA_INT_LVI) - printk("LVI "); - if(status & DMA_INT_DCH) - printk("DCH -"); -#endif - if(dmabuf->enable & DAC_RUNNING) - count = dmabuf->count; - else - count = dmabuf->dmasize - dmabuf->count; - if(count > 0) { - outb(inb(port+OFF_CR) | 1, port+OFF_CR); -#ifdef DEBUG_INTERRUPTS - printk(" CONTINUE "); -#endif - } else { - if (dmabuf->enable & DAC_RUNNING) - __stop_dac(state); - if (dmabuf->enable & ADC_RUNNING) - __stop_adc(state); - dmabuf->enable = 0; - wake_up(&dmabuf->wait); -#ifdef DEBUG_INTERRUPTS - printk(" STOP "); -#endif - } - } - if(card->pci_id == PCI_DEVICE_ID_SI_7012) - outw(status & DMA_INT_MASK, port + OFF_PICB); - else - outw(status & DMA_INT_MASK, port + OFF_SR); - } -#ifdef DEBUG_INTERRUPTS - printk(")\n"); -#endif -} - -static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct i810_card *card = (struct i810_card *)dev_id; - u32 status; - - spin_lock(&card->lock); - - status = inl(card->iobase + GLOB_STA); - - if(!(status & INT_MASK)) - { - spin_unlock(&card->lock); - return; /* not for us */ - } - - if(status & (INT_PO|INT_PI|INT_MC)) - i810_channel_interrupt(card); - - /* clear 'em */ - outl(status & INT_MASK, card->iobase + GLOB_STA); - spin_unlock(&card->lock); -} - -/* 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 i810_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct i810_state *state = (struct i810_state *)file->private_data; - struct i810_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("i810_audio: i810_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 = i810_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. - */ - i810_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)) { -#ifdef DEBUG - printk(KERN_ERR "i810_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); -#endif - /* 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: - i810_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 i810_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct i810_state *state = (struct i810_state *)file->private_data; - struct i810_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("i810_audio: i810_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 (!dmabuf->write_channel) { - dmabuf->ready = 0; - dmabuf->write_channel = card->alloc_pcm_channel(card); - if(!dmabuf->write_channel) - return -EBUSY; - } - 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 = i810_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 "i810_audio: i810_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 - */ - dmabuf->trigger = PCM_ENABLE_OUTPUT; - i810_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. */ - if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { -#ifdef DEBUG - printk(KERN_ERR "i810_audio: playback schedule timeout, " - "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, - dmabuf->hwptr, dmabuf->swptr); -#endif - /* a buffer underrun, we delay the recovery until next time the - while loop begin and we REALLY have data to play */ - //return ret; - } - 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: - i810_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 i810_poll(struct file *file, struct poll_table_struct *wait) -{ - struct i810_state *state = (struct i810_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); - if (dmabuf->enable & ADC_RUNNING || - dmabuf->trigger & PCM_ENABLE_INPUT) { - if (i810_get_available_read_data(state) >= - (signed)dmabuf->userfragsize) - mask |= POLLIN | POLLRDNORM; - } - if (dmabuf->enable & DAC_RUNNING || - dmabuf->trigger & PCM_ENABLE_OUTPUT) { - if (i810_get_free_write_space(state) >= - (signed)dmabuf->userfragsize) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&state->card->lock, flags); - return mask; -} - -static int i810_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct i810_state *state = (struct i810_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->vm_start, virt_to_phys(dmabuf->rawbuf), - size, vma->vm_page_prot)) - goto out; - dmabuf->mapped = 1; - dmabuf->trigger = 0; - ret = 0; -#ifdef DEBUG_MMAP - printk("i810_audio: mmap'ed %ld bytes of data space\n", size); -#endif -out: - unlock_kernel(); - return ret; -} - -static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct i810_state *state = (struct i810_state *)file->private_data; - struct i810_channel *c = NULL; - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - unsigned int i_glob_cnt; - int val = 0, ret; - struct ac97_codec *codec = state->card->ac97_codec[0]; - -#ifdef DEBUG - printk("i810_audio: i810_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 (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(); - 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 (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 */ - /* AD1886 only supports 48000, need to check that */ - if ( i810_valid_spdif_rate ( codec, val ) ) { - /* Set DAC rate */ - i810_set_spdif_output ( state, -1, 0 ); - stop_dac(state); - dmabuf->ready = 0; - spin_lock_irqsave(&state->card->lock, flags); - i810_set_dac_rate(state, val); - spin_unlock_irqrestore(&state->card->lock, flags); - /* Set S/PDIF transmitter rate. */ - i810_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); - i810_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); - i810_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); - } - return put_user(1, (int *)arg); - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - 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: -#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 & ADC_RUNNING) { - stop_adc(state); - } - } else { - return put_user(state->card->channels, (int *)arg); - } - - /* ICH and ICH0 only support 2 channels */ - if ( state->card->pci_id == 0x2415 || state->card->pci_id == 0x2425 ) - return put_user(2, (int *)arg); - - /* Multi-channel support was added with ICH2. Bits in */ - /* Global Status and Global Control register are now */ - /* used to indicate this. */ - - i_glob_cnt = inl(state->card->iobase + GLOB_CNT); - - /* Current # of channels enabled */ - if ( i_glob_cnt & 0x0100000 ) - ret = 4; - else if ( i_glob_cnt & 0x0200000 ) - ret = 6; - else - ret = 2; - - switch ( val ) { - case 2: /* 2 channels is always supported */ - outl(state->card->iobase + GLOB_CNT, (i_glob_cnt & 0xcfffff)); - /* Do we need to change mixer settings???? */ - break; - case 4: /* Supported on some chipsets, better check first */ - if ( state->card->channels >= 4 ) { - outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0100000)); - /* Do we need to change mixer settings??? */ - } else { - val = ret; - } - break; - case 6: /* Supported on some chipsets, better check first */ - if ( state->card->channels >= 6 ) { - outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0200000)); - /* Do we need to change mixer settings??? */ - } else { - val = ret; - } - 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(!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 (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - return val; - spin_lock_irqsave(&state->card->lock, flags); - i810_update_ptr(state); - abinfo.fragsize = dmabuf->userfragsize; - abinfo.fragstotal = dmabuf->userfrags; - if (dmabuf->mapped) - abinfo.bytes = dmabuf->dmasize; - else - abinfo.bytes = i810_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 (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - return val; - spin_lock_irqsave(&state->card->lock, flags); - val = i810_get_free_write_space(state); - cinfo.bytes = dmabuf->total_bytes; - cinfo.ptr = dmabuf->hwptr; - cinfo.blocks = val/dmabuf->userfragsize; - if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { - dmabuf->count += val; - dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; - __i810_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 = i810_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 = i810_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; - __i810_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); - } - 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); - i810_update_ptr(state); - dmabuf->count = 0; - dmabuf->swptr = dmabuf->hwptr; - dmabuf->count = i810_get_free_write_space(state); - dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; - __i810_update_lvi(state, 0); - spin_unlock_irqrestore(&state->card->lock, flags); - } else - start_dac(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); - i810_update_ptr(state); - dmabuf->swptr = dmabuf->hwptr; - dmabuf->count = 0; - spin_unlock_irqrestore(&state->card->lock, flags); - } - i810_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); - i810_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 = i810_ac97_get(codec, AC97_SPDIF_CONTROL); - val |= (ret & 0x3000); - - i810_ac97_set(codec, AC97_SPDIF_CONTROL, val); - if(i810_ac97_get(codec, AC97_SPDIF_CONTROL) != val ) { - printk(KERN_ERR "i810_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val); - return -EFAULT; - } - } -#ifdef DEBUG - else - printk(KERN_WARNING "i810_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 "i810_audio: S/PDIF transmitter not avalible.\n"); -#endif - val = 0; - } else { - val = i810_ac97_get(codec, AC97_SPDIF_CONTROL); - } - //return put_user((val & 0xcfff), (int *)arg); - return put_user(val, (int *)arg); - - 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. - */ - - i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, dmabuf->rate ); - 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 ) - i810_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; - } - i810_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: -#ifdef DEBUG - printk("SNDCTL_* -EINVAL\n"); -#endif - return -EINVAL; - } - return -EINVAL; -} - -static int i810_open(struct inode *inode, struct file *file) -{ - int i = 0; - struct i810_card *card = devs; - struct i810_state *state = NULL; - struct dmabuf *dmabuf = NULL; - - /* find an avaiable 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 i810_state *) - kmalloc(sizeof(struct i810_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - memset(state, 0, sizeof(struct i810_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 = I810_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; - i810_set_adc_rate(state, 8000); - } - if(file->f_mode & FMODE_WRITE) { - 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 ( spdif_locked ) { - i810_set_dac_rate(state, spdif_locked); - i810_set_spdif_output(state, AC97_EA_SPSA_3_4, spdif_locked); - } else { - i810_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 |= I810_FMT_16BIT | I810_FMT_STEREO; - dmabuf->ossfragsize = 0; - dmabuf->ossmaxfrags = 0; - dmabuf->subdivision = 0; - - state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - - return 0; -} - -static int i810_release(struct inode *inode, struct file *file) -{ - struct i810_state *state = (struct i810_state *)file->private_data; - struct i810_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 & PCM_ENABLE_INPUT) { - stop_adc(state); - } - spin_lock_irqsave(&card->lock, flags); - dealloc_dmabuf(state); - if (file->f_mode & FMODE_WRITE) { - 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 i810_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: i810_read, - write: i810_write, - poll: i810_poll, - ioctl: i810_ioctl, - mmap: i810_mmap, - open: i810_open, - release: i810_release, -}; - -/* Write AC97 codec registers */ - -static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg) -{ - struct i810_card *card = dev->private_data; - int count = 100; - u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); - - while(count-- && (inb(card->iobase + CAS) & 1)) - udelay(1); - - return inw(card->ac97base + reg_set); -} - -static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) -{ - struct i810_card *card = dev->private_data; - int count = 100; - u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); - - while(count-- && (inb(card->iobase + CAS) & 1)) - udelay(1); - outw(data, card->ac97base + reg_set); -} - - -/* OSS /dev/mixer file operation methods */ - -static int i810_open_mixdev(struct inode *inode, struct file *file) -{ - int i; - unsigned int minor = minor(inode->i_rdev); - struct i810_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 i810_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 i810_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: i810_ioctl_mixdev, - open: i810_open_mixdev, -}; - -/* AC97 codec initialisation. These small functions exist so we don't - duplicate code between module init and apm resume */ - -static inline int i810_ac97_exists(struct i810_card *card,int ac97_number) -{ - u32 reg = inl(card->iobase + GLOB_STA); - return (reg & (0x100 << ac97_number)); -} - -static inline int i810_ac97_enable_variable_rate(struct ac97_codec *codec) -{ - i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9); - i810_ac97_set(codec,AC97_EXTENDED_STATUS, - i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800); - - return (i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1); -} - - -static int i810_ac97_probe_and_powerup(struct i810_card *card,struct ac97_codec *codec) -{ - /* Returns 0 on failure */ - int i; - - if (ac97_probe_codec(codec) == 0) return 0; - - /* power it all up */ - i810_ac97_set(codec, AC97_POWER_CONTROL, - i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); - /* wait for analog ready */ - for (i=10; - i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); - i--) - { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/20); - } - return i; -} - -/* if I knew what this did, I'd give it a better name */ -static int i810_ac97_random_init_stuff(struct i810_card *card) -{ - u32 reg = inl(card->iobase + GLOB_CNT); - int i; - - if((reg&2)==0) /* Cold required */ - reg|=2; - else - reg|=4; /* Warm */ - - reg&=~8; /* ACLink on */ - outl(reg , card->iobase + GLOB_CNT); - - for(i=0;i<10;i++) - { - if((inl(card->iobase+GLOB_CNT)&4)==0) - break; - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/20); - } - if(i==10) - { - printk(KERN_ERR "i810_audio: AC'97 reset failed.\n"); - return 0; - } - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/2); - reg = inl(card->iobase + GLOB_STA); - inw(card->ac97base); - return 1; -} - -static int __init i810_ac97_init(struct i810_card *card) -{ - int num_ac97 = 0; - int total_channels = 0; - struct ac97_codec *codec; - u16 eid; - u32 reg; - - if(!i810_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.... */ - - card->channels = 2; - reg = inl(card->iobase + GLOB_STA); - if ( reg & 0x0200000 ) - card->channels = 6; - else if ( reg & 0x0100000 ) - card->channels = 4; - printk("i810_audio: Audio Controller supports %d channels.\n", card->channels); - - inw(card->ac97base); - - 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 (!i810_ac97_exists(card,num_ac97)) { - if(num_ac97 == 0) - printk(KERN_ERR "i810_audio: Primary codec not ready.\n"); - break; /* I think this works, if not ready stop */ - } - - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) - return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); - - /* 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 = i810_ac97_get; - codec->codec_write = i810_ac97_set; - - if(!i810_ac97_probe_and_powerup(card,codec)) { - printk("i810_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 = i810_ac97_get(codec, AC97_EXTENDED_ID); - - if(eid==0xFFFFFF) - { - printk(KERN_WARNING "i810_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 810 hardware engine */ - - if(!(eid&0x0001)) - printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n"); - else - { - if(!i810_ac97_enable_variable_rate(codec)) { - printk(KERN_WARNING "i810_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(eid & 0x200) { /* GOOD, AMAP support */ - if (eid & 0x0080) /* L/R Surround channels */ - total_channels += 2; - if (eid & 0x0140) /* LFE and Center channels */ - total_channels += 2; - printk("i810_audio: AC'97 codec %d supports AMAP, total channels = %d\n", num_ac97, total_channels); - } else if (eid & 0x0400) { /* this only works on 2.2 compliant codecs */ - eid &= 0xffcf; - if((eid & 0xc000) != 0) { - switch ( total_channels ) { - case 2: - /* Set dsa1, dsa0 to 01 */ - eid |= 0x0010; - break; - case 4: - /* Set dsa1, dsa0 to 10 */ - eid |= 0x0020; - break; - case 6: - /* Set dsa1, dsa0 to 11 */ - eid |= 0x0030; - break; - } - total_channels += 2; - } - i810_ac97_set(codec, AC97_EXTENDED_ID, eid); - eid = i810_ac97_get(codec, AC97_EXTENDED_ID); - printk("i810_audio: AC'97 codec %d, new EID value = 0x%04x\n", num_ac97, eid); - if (eid & 0x0080) /* L/R Surround channels */ - total_channels += 2; - if (eid & 0x0140) /* LFE and Center channels */ - total_channels += 2; - printk("i810_audio: AC'97 codec %d, DAC map configured, total channels = %d\n", num_ac97, total_channels); - } else { - printk("i810_audio: AC'97 codec %d Unable to map surround DAC's (or DAC's not present), total channels = %d\n", num_ac97, total_channels); - } - - if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) { - printk(KERN_ERR "i810_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 i810_configure_clocking (void) -{ - struct i810_card *card; - struct i810_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 i810 in a machine? Besides, clocking is global, so unless - * someone actually thinks more than one i810 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 i810_state *) - kmalloc(sizeof(struct i810_state), GFP_KERNEL); - if (state == NULL) - return; - memset(state, 0, sizeof(struct i810_state)); - dmabuf = &state->dmabuf; - - dmabuf->write_channel = card->alloc_pcm_channel(card); - state->virt = 0; - state->card = card; - state->magic = I810_STATE_MAGIC; - init_waitqueue_head(&dmabuf->wait); - init_MUTEX(&state->open_sem); - dmabuf->fmt = I810_FMT_STEREO | I810_FMT_16BIT; - dmabuf->trigger = PCM_ENABLE_OUTPUT; - i810_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); - save_flags(flags); - cli(); - start_dac(state); - offset = i810_get_dma_addr(state, 0); - mdelay(50); - new_offset = i810_get_dma_addr(state, 0); - stop_dac(state); - outb(2,card->iobase+dmabuf->write_channel->port+OFF_CR); - restore_flags(flags); - i = new_offset - offset; -#ifdef DEBUG - printk("i810_audio: %d bytes in 50 milliseconds\n", i); -#endif - if(i == 0) - goto config_out; - i = i / 4 * 20; - if (i > 48500 || i < 47500) { - clocking = clocking * clocking / i; - printk("i810_audio: setting clocking to %d\n", clocking); - } -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 i810_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) -{ - struct i810_card *card; - - if (pci_enable_device(pci_dev)) - return -EIO; - - if (pci_set_dma_mask(pci_dev, I810_DMA_MASK)) { - printk(KERN_ERR "intel810: architecture does not support" - " 32bit PCI busmaster DMA\n"); - return -ENODEV; - } - - if ((card = kmalloc(sizeof(struct i810_card), GFP_KERNEL)) == NULL) { - printk(KERN_ERR "i810_audio: out of memory\n"); - return -ENOMEM; - } - memset(card, 0, sizeof(*card)); - - card->initializing = 1; - card->iobase = pci_resource_start (pci_dev, 1); - card->ac97base = 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 = I810_CARD_MAGIC; -#ifdef CONFIG_PM - card->pm_suspended=0; -#endif - spin_lock_init(&card->lock); - devs = card; - - pci_set_master(pci_dev); - - printk(KERN_INFO "i810: %s found at IO 0x%04lx and 0x%04lx, IRQ %d\n", - card_names[pci_id->driver_data], card->iobase, card->ac97base, - card->irq); - - card->alloc_pcm_channel = i810_alloc_pcm_channel; - card->alloc_rec_pcm_channel = i810_alloc_rec_pcm_channel; - card->alloc_rec_mic_channel = i810_alloc_rec_mic_channel; - card->free_pcm_channel = i810_free_pcm_channel; - card->channel[0].offset = 0; - card->channel[0].port = 0x00; - card->channel[0].num=0; - card->channel[1].offset = 0; - card->channel[1].port = 0x10; - card->channel[1].num=1; - card->channel[2].offset = 0; - card->channel[2].port = 0x20; - card->channel[2].num=2; - - /* claim our iospace and irq */ - request_region(card->iobase, 64, card_names[pci_id->driver_data]); - request_region(card->ac97base, 256, card_names[pci_id->driver_data]); - - if (request_irq(card->irq, &i810_interrupt, SA_SHIRQ, - card_names[pci_id->driver_data], card)) { - printk(KERN_ERR "i810_audio: unable to allocate irq %d\n", card->irq); - release_region(card->iobase, 64); - release_region(card->ac97base, 256); - kfree(card); - return -ENODEV; - } - - /* initialize AC97 codec and register /dev/mixer */ - if (i810_ac97_init(card) <= 0) { - release_region(card->iobase, 64); - release_region(card->ac97base, 256); - free_irq(card->irq, card); - kfree(card); - return -ENODEV; - } - pci_set_drvdata(pci_dev, card); - - if(clocking == 0) { - clocking = 48000; - i810_configure_clocking(); - } - - /* register /dev/dsp */ - if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) { - int i; - printk(KERN_ERR "i810_audio: couldn't register DSP device!\n"); - release_region(card->iobase, 64); - release_region(card->ac97base, 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 __exit i810_remove(struct pci_dev *pci_dev) -{ - int i; - struct i810_card *card = pci_get_drvdata(pci_dev); - /* free hardware resources */ - free_irq(card->irq, devs); - release_region(card->iobase, 64); - release_region(card->ac97base, 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); - kfree (card->ac97_codec[i]); - card->ac97_codec[i] = NULL; - } - unregister_sound_dsp(card->dev_audio); - kfree(card); -} - -#ifdef CONFIG_PM -static int i810_pm_suspend(struct pci_dev *dev, u32 pm_state) -{ - struct i810_card *card = pci_get_drvdata(dev); - struct i810_state *state; - unsigned long flags; - struct dmabuf *dmabuf; - int i,num_ac97; -#ifdef DEBUG - printk("i810_audio: i810_pm_suspend called\n"); -#endif - if(!card) return 0; - spin_lock_irqsave(&card->lock, flags); - card->pm_suspended=1; - for(i=0;istates[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 i810_pm_resume(struct pci_dev *dev) -{ - int num_ac97,i=0; - struct i810_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 */ - - i810_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 || !i810_ac97_exists(card,num_ac97)) { - if(num_ac97) continue; - else BUG(); - } - if(!i810_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(!i810_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;istates[i]; - if(state) { - if(state->pm_saved_adc_rate) - i810_set_adc_rate(state,state->pm_saved_adc_rate); - if(state->pm_saved_dac_rate) - i810_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;istates[i]; - if(state) wake_up(&state->dmabuf.wait); - } - - return 0; -} -#endif /* CONFIG_PM */ - -MODULE_AUTHOR(""); -MODULE_DESCRIPTION("Intel 810 audio support"); -MODULE_LICENSE("GPL"); -MODULE_PARM(ftsodell, "i"); -MODULE_PARM(clocking, "i"); -MODULE_PARM(strict_clocking, "i"); -MODULE_PARM(spdif_locked, "i"); - -#define I810_MODULE_NAME "intel810_audio" - -static struct pci_driver i810_pci_driver = { - name: I810_MODULE_NAME, - id_table: i810_pci_tbl, - probe: i810_probe, - remove: i810_remove, -#ifdef CONFIG_PM - suspend: i810_pm_suspend, - resume: i810_pm_resume, -#endif /* CONFIG_PM */ -}; - - -static int __init i810_init_module (void) -{ - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - - printk(KERN_INFO "Intel 810 + AC97 Audio, version " - DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); - - if (!pci_register_driver(&i810_pci_driver)) { - pci_unregister_driver(&i810_pci_driver); - return -ENODEV; - } - if(ftsodell != 0) { - printk("i810_audio: ftsodell is now a deprecated option.\n"); - } - if(spdif_locked > 0 ) { - if(spdif_locked == 32000 || spdif_locked == 44100 || spdif_locked == 48000) { - printk("i810_audio: Enabling S/PDIF at sample rate %dHz.\n", spdif_locked); - } else { - printk("i810_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); - spdif_locked = 0; - } - } - - return 0; -} - -static void __exit i810_cleanup_module (void) -{ - pci_unregister_driver(&i810_pci_driver); -} - -module_init(i810_init_module); -module_exit(i810_cleanup_module); - -/* -Local Variables: -c-basic-offset: 8 -End: -*/ diff -Nru a/drivers/sound/ics2101.c b/drivers/sound/ics2101.c --- a/drivers/sound/ics2101.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,246 +0,0 @@ -/* - * sound/ics2101.c - * - * Driver for the ICS2101 mixer of GUS v3.7. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Bartlomiej Zolnierkiewicz : added __init to ics2101_mixer_init() - */ -#include -#include "sound_config.h" - -#include - -#include "gus.h" -#include "gus_hw.h" - -#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ - SOUND_MASK_SYNTH| \ - SOUND_MASK_CD | SOUND_MASK_VOLUME) - -extern int *gus_osp; -extern int gus_base; -static int volumes[ICS_MIXDEVS]; -static int left_fix[ICS_MIXDEVS] = -{1, 1, 1, 2, 1, 2}; -static int right_fix[ICS_MIXDEVS] = -{2, 2, 2, 1, 2, 1}; - -static int scale_vol(int vol) -{ - /* - * Experimental volume scaling by Risto Kankkunen. - * This should give smoother volume response than just - * a plain multiplication. - */ - - int e; - - if (vol < 0) - vol = 0; - if (vol > 100) - vol = 100; - vol = (31 * vol + 50) / 100; - e = 0; - if (vol) - { - while (vol < 16) - { - vol <<= 1; - e--; - } - vol -= 16; - e += 7; - } - return ((e << 4) + vol); -} - -static void write_mix(int dev, int chn, int vol) -{ - int *selector; - unsigned long flags; - int ctrl_addr = dev << 3; - int attn_addr = dev << 3; - - vol = scale_vol(vol); - - if (chn == CHN_LEFT) - { - selector = left_fix; - ctrl_addr |= 0x00; - attn_addr |= 0x02; - } - else - { - selector = right_fix; - ctrl_addr |= 0x01; - attn_addr |= 0x03; - } - - save_flags(flags); - cli(); - outb((ctrl_addr), u_MixSelect); - outb((selector[dev]), u_MixData); - outb((attn_addr), u_MixSelect); - outb(((unsigned char) vol), u_MixData); - restore_flags(flags); -} - -static int set_volumes(int dev, int vol) -{ - int left = vol & 0x00ff; - int right = (vol >> 8) & 0x00ff; - - if (left < 0) - left = 0; - if (left > 100) - left = 100; - if (right < 0) - right = 0; - if (right > 100) - right = 100; - - write_mix(dev, CHN_LEFT, left); - write_mix(dev, CHN_RIGHT, right); - - vol = left + (right << 8); - volumes[dev] = vol; - return vol; -} - -static int ics2101_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int val; - - if (((cmd >> 8) & 0xff) == 'M') { - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - - if (get_user(val, (int *)arg)) - return -EFAULT; - switch (cmd & 0xff) { - case SOUND_MIXER_RECSRC: - return gus_default_mixer_ioctl(dev, cmd, arg); - - case SOUND_MIXER_MIC: - val = set_volumes(DEV_MIC, val); - break; - - case SOUND_MIXER_CD: - val = set_volumes(DEV_CD, val); - break; - - case SOUND_MIXER_LINE: - val = set_volumes(DEV_LINE, val); - break; - - case SOUND_MIXER_SYNTH: - val = set_volumes(DEV_GF1, val); - break; - - case SOUND_MIXER_VOLUME: - val = set_volumes(DEV_VOL, val); - break; - - default: - return -EINVAL; - } - return put_user(val, (int *)arg); - } else { - switch (cmd & 0xff) { - /* - * Return parameters - */ - case SOUND_MIXER_RECSRC: - return gus_default_mixer_ioctl(dev, cmd, arg); - - case SOUND_MIXER_DEVMASK: - val = MIX_DEVS; - break; - - case SOUND_MIXER_STEREODEVS: - val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; - break; - - case SOUND_MIXER_RECMASK: - val = SOUND_MASK_MIC | SOUND_MASK_LINE; - break; - - case SOUND_MIXER_CAPS: - val = 0; - break; - - case SOUND_MIXER_MIC: - val = volumes[DEV_MIC]; - break; - - case SOUND_MIXER_LINE: - val = volumes[DEV_LINE]; - break; - - case SOUND_MIXER_CD: - val = volumes[DEV_CD]; - break; - - case SOUND_MIXER_VOLUME: - val = volumes[DEV_VOL]; - break; - - case SOUND_MIXER_SYNTH: - val = volumes[DEV_GF1]; - break; - - default: - return -EINVAL; - } - return put_user(val, (int *)arg); - } - } - return -EINVAL; -} - -static struct mixer_operations ics2101_mixer_operations = -{ - owner: THIS_MODULE, - id: "ICS2101", - name: "ICS2101 Multimedia Mixer", - ioctl: ics2101_mixer_ioctl -}; - -int __init ics2101_mixer_init(void) -{ - int i; - int n; - - if ((n = sound_alloc_mixerdev()) != -1) - { - mixer_devs[n] = &ics2101_mixer_operations; - - /* - * Some GUS v3.7 cards had some channels flipped. Disable - * the flipping feature if the model id is other than 5. - */ - - if (inb(u_MixSelect) != 5) - { - for (i = 0; i < ICS_MIXDEVS; i++) - left_fix[i] = 1; - for (i = 0; i < ICS_MIXDEVS; i++) - right_fix[i] = 2; - } - set_volumes(DEV_GF1, 0x5a5a); - set_volumes(DEV_CD, 0x5a5a); - set_volumes(DEV_MIC, 0x0000); - set_volumes(DEV_LINE, 0x5a5a); - set_volumes(DEV_VOL, 0x5a5a); - set_volumes(DEV_UNUSED, 0x0000); - } - return n; -} diff -Nru a/drivers/sound/ite8172.c b/drivers/sound/ite8172.c --- a/drivers/sound/ite8172.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1952 +0,0 @@ -/* - * ite8172.c -- ITE IT8172G Sound Driver. - * - * 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. - * 2. The following support is untested: - * * Memory mapping the audio buffers, and the ioctl controls that go - * with it. - * * S/PDIF output. - * 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 - */ -#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 IT8172_DEBUG -#undef IT8172_VERBOSE_DEBUG -#define DBG(x) {} - -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - - -/* - * Audio Controller register bit definitions follow. See - * include/asm/it8172/it8172.h for register offsets. - */ - -/* PCM Out Volume Reg */ -#define PCMOV_PCMOM (1<<15) /* PCM Out Mute default 1: mute */ -#define PCMOV_PCMRCG_BIT 8 /* PCM Right channel Gain */ -#define PCMOV_PCMRCG_MASK (0x1f<= 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; - - if (in_interrupt()) - return; - - 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; - - 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; -} - -static void set_adc_rate(struct it8172_state *s, unsigned rate) -{ - unsigned long flags; - unsigned short sr; - - 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); - - s->adcrate = rate; -} - - -static void set_dac_rate(struct it8172_state *s, unsigned rate) -{ - unsigned long flags; - unsigned short sr; - - 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); - - s->dacrate = rate; -} - - -/* --------------------------------------------------------------------- */ - -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"); - - 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 - - outw(data, s->io+IT_AC_CIRDP); // send data first - outw(circp, s->io+IT_AC_CIRCP); - - spin_unlock_irqrestore(&s->lock, flags); -} - - -static void waitcodec(struct ac97_codec *codec) -{ - unsigned short temp; - - /* 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); - } - - // 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; - } -} - - -/* --------------------------------------------------------------------- */ - -extern inline void stop_adc(struct it8172_state *s) -{ - struct dmabuf* db = &s->dma_adc; - unsigned long flags; - unsigned char imc; - - if (db->stopped) - return; - - 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); - - // disable capture interrupt - imc = inb(s->io+IT_AC_IMC); - outb(imc | IMC_CCIM, s->io+IT_AC_IMC); - - db->stopped = 1; - - spin_unlock_irqrestore(&s->lock, flags); -} - -extern inline void stop_dac(struct it8172_state *s) -{ - struct dmabuf* db = &s->dma_dac; - unsigned long flags; - unsigned char imc; - - if (db->stopped) - return; - - 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); - - // disable playback interrupt - imc = inb(s->io+IT_AC_IMC); - outb(imc | IMC_PCIM, s->io+IT_AC_IMC); - - db->stopped = 1; - - 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; - - if (!db->stopped) - return; - - 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); - - 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; - - 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; - - 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; - - // 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); - - 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 it8172_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); - 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++) - 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; - 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, db->dmasize); - - // set data length register - outw(db->fragsize, s->io+reg+2); - db->ready = 1; - - return 0; -} - -extern 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); -} - -extern 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); -} - - -/* hold spinlock for the following! */ - -static void 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))) - return; - - /* 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; - - 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->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; - - 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->count -= dac->fragsize; - dac->total_bytes += dac->fragsize; - - /* wake up anybody listening */ - if (waitqueue_active(&dac->wait)) - wake_up_interruptible(&dac->wait); - - if (dac->count <= 0) - stop_dac(s); - } - - spin_unlock(&s->lock); -} - -/* --------------------------------------------------------------------- */ - -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; -} - -static int it8172_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 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; - - 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, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct it8172_state *s, int nonblock) -{ - unsigned long flags; - int count, tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - 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->dacrate; - tmo >>= sample_shift[(s->pcc & CC_FMT_MASK) >> CC_FMT_BIT]; - it8172_delay(tmo); - } - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -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; - - 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; - } - - 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 -= bufcnt; - buffer += bufcnt; - ret += bufcnt; - } // while (count > 0) - - 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); - - 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; - } - - 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; - - count -= bufcnt; - buffer += bufcnt; - ret += bufcnt; - } // while (count > 0) - - 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; -} - -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)) { - unlock_kernel(); - return -EAGAIN; - } - db->mapped = 1; - unlock_kernel(); - return 0; -} - - -#ifdef IT8172_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 - -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; - - 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; - - 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 ((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_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_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; - } - 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; - 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; - 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_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_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; - } - - 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; - } - 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); - - 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; -} - -static int it8172_release(struct inode *inode, struct file *file) -{ - 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; -} - -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, -}; - - -/* --------------------------------------------------------------------- */ - - -/* --------------------------------------------------------------------- */ - -/* - * for debugging purposes, we'll create a proc device that dumps the - * CODEC chipstate - */ - -#ifdef IT8172_DEBUG -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)); - - if (fpos >=len){ - *start = buf; - *eof =1; - return 0; - } - *start = buf + fpos; - if ((len -= fpos) > length) - return length; - *eof =1; - return len; - -} -#endif /* IT8172_DEBUG */ - -/* --------------------------------------------------------------------- */ - -/* maximum number of devices; only used for command line params */ -#define NR_DEVICE 5 - -static int spdif[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_AUTHOR("Monta Vista Software, stevel@mvista.com"); -MODULE_DESCRIPTION("IT8172 AudioPCI97 Driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -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; - } - - 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; - -#ifdef IT8172_DEBUG - /* 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; - 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, - (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); -#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; - - err_dev3: - unregister_sound_mixer(s->codec.dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR PFX "cannot register misc device\n"); - free_irq(s->irq, s); - err_irq: - release_region(s->io, pci_resource_len(pcidev,0)); - err_region: - kfree(s); - return -1; -} - -static void __devinit it8172_remove(struct pci_dev *dev) -{ - struct it8172_state *s = pci_get_drvdata(dev); - - if (!s) - return; - list_del(&s->devs); -#ifdef IT8172_DEBUG - if (s->ps) - remove_proc_entry(IT8172_MODULE_NAME, NULL); -#endif /* IT8172_DEBUG */ - 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); - 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, } -}; - -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 -}; - -static int __init init_it8172(void) -{ - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - printk("version v0.26 time " __TIME__ " " __DATE__ "\n"); - return pci_module_init(&it8172_driver); -} - -static void __exit cleanup_it8172(void) -{ - printk(KERN_INFO PFX "unloading\n"); - pci_unregister_driver(&it8172_driver); -} - -module_init(init_it8172); -module_exit(cleanup_it8172); - diff -Nru a/drivers/sound/iwmem.h b/drivers/sound/iwmem.h --- a/drivers/sound/iwmem.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,36 +0,0 @@ -/* - * sound/iwmem.h - * - * DRAM size encoding table for AMD Interwave chip. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * Bartlomiej Zolnierkiewicz : added __initdata to mem_decode - */ - - -#define K 1024 -#define M (1024*K) -static int mem_decode[][4] __initdata = -{ -/* Bank0 Bank1 Bank2 Bank3 Encoding bits */ - {256*K, 0, 0, 0}, /* 0 */ - {256*K, 256*K, 0, 0}, /* 1 */ - {256*K, 256*K, 256*K, 256*K}, /* 2 */ - {256*K, 1*M, 0, 0}, /* 3 */ - {256*K, 1*M, 1*M, 1*M}, /* 4 */ - {256*K, 256*K, 1*M, 0}, /* 5 */ - {256*K, 256*K, 1*M, 1*M}, /* 6 */ - {1*M, 0, 0, 0}, /* 7 */ - {1*M, 1*M, 0, 0}, /* 8 */ - {1*M, 1*M, 1*M, 1*M}, /* 9 */ - {4*M, 0, 0, 0}, /* 10 */ - {4*M, 4*M, 0, 0}, /* 11 */ - {4*M, 4*M, 4*M, 4*M} /* 12 */ -}; diff -Nru a/drivers/sound/mad16.c b/drivers/sound/mad16.c --- a/drivers/sound/mad16.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1074 +0,0 @@ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * mad16.c - * - * Initialization code for OPTi MAD16 compatible audio chips. Including - * - * OPTi 82C928 MAD16 (replaced by C929) - * OAK OTI-601D Mozart - * OAK OTI-605 Mozart (later version with MPU401 Midi) - * OPTi 82C929 MAD16 Pro - * OPTi 82C930 - * OPTi 82C924 - * - * These audio interface chips don't produce sound themselves. They just - * connect some other components (OPL-[234] and a WSS compatible codec) - * to the PC bus and perform I/O, DMA and IRQ address decoding. There is - * also a UART for the MPU-401 mode (not 82C928/Mozart). - * The Mozart chip appears to be compatible with the 82C928, although later - * issues of the card, using the OTI-605 chip, have an MPU-401 compatable Midi - * port. This port is configured differently to that of the OPTi audio chips. - * - * Changes - * - * Alan Cox Clean up, added module selections. - * - * A. Wik Added support for Opti924 PnP. - * Improved debugging support. 16-May-1998 - * Fixed bug. 16-Jun-1998 - * - * Torsten Duwe Made Opti924 PnP support non-destructive - * 23-Dec-1998 - * - * Paul Grayson Added support for Midi on later Mozart cards. - * 25-Nov-1999 - * Christoph Hellwig Adapted to module_init/module_exit. - * Arnaldo C. de Melo got rid of attach_uart401 21-Sep-2000 - * - * Pavel Rabel Clean up Nov-2000 - */ - -#include -#include -#include - -#include "sound_config.h" - -#include "ad1848.h" -#include "sb.h" -#include "mpu401.h" - -static int mad16_conf; -static int mad16_cdsel; - -static int already_initialized = 0; - -#define C928 1 -#define MOZART 2 -#define C929 3 -#define C930 4 -#define C924 5 - -/* - * Registers - * - * The MAD16 occupies I/O ports 0xf8d to 0xf93 (fixed locations). - * All ports are inactive by default. They can be activated by - * writing 0xE2 or 0xE3 to the password register. The password is valid - * only until the next I/O read or write. - * - * 82C930 uses 0xE4 as the password and indirect addressing to access - * the config registers. - */ - -#define MC0_PORT 0xf8c /* Dummy port */ -#define MC1_PORT 0xf8d /* SB address, CD-ROM interface type, joystick */ -#define MC2_PORT 0xf8e /* CD-ROM address, IRQ, DMA, plus OPL4 bit */ -#define MC3_PORT 0xf8f -#define PASSWD_REG 0xf8f -#define MC4_PORT 0xf90 -#define MC5_PORT 0xf91 -#define MC6_PORT 0xf92 -#define MC7_PORT 0xf93 -#define MC8_PORT 0xf94 -#define MC9_PORT 0xf95 -#define MC10_PORT 0xf96 -#define MC11_PORT 0xf97 -#define MC12_PORT 0xf98 - -static int board_type = C928; - -static int *mad16_osp; -static int c931_detected; /* minor differences from C930 */ -static char c924pnp = 0; /* " " " C924 */ -static int debug = 0; /* debugging output */ - -#ifdef DDB -#undef DDB -#endif -#define DDB(x) {if (debug) x;} - -static unsigned char mad_read(int port) -{ - unsigned long flags; - unsigned char tmp; - - save_flags(flags); - cli(); - - switch (board_type) /* Output password */ - { - case C928: - case MOZART: - outb((0xE2), PASSWD_REG); - break; - - case C929: - outb((0xE3), PASSWD_REG); - break; - - case C930: - /* outb(( 0xE4), PASSWD_REG); */ - break; - - case C924: - /* the c924 has its ports relocated by -128 if - PnP is enabled -aw */ - if (!c924pnp) - outb((0xE5), PASSWD_REG); else - outb((0xE5), PASSWD_REG - 0x80); - break; - } - - if (board_type == C930) - { - outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ - tmp = inb(0xe0f); /* Read from data reg */ - } - else - if (!c924pnp) - tmp = inb(port); else - tmp = inb(port-0x80); - restore_flags(flags); - - return tmp; -} - -static void mad_write(int port, int value) -{ - unsigned long flags; - - save_flags(flags); - cli(); - - switch (board_type) /* Output password */ - { - case C928: - case MOZART: - outb((0xE2), PASSWD_REG); - break; - - case C929: - outb((0xE3), PASSWD_REG); - break; - - case C930: - /* outb(( 0xE4), PASSWD_REG); */ - break; - - case C924: - if (!c924pnp) - outb((0xE5), PASSWD_REG); else - outb((0xE5), PASSWD_REG - 0x80); - break; - } - - if (board_type == C930) - { - outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ - outb(((unsigned char) (value & 0xff)), 0xe0f); - } - else - if (!c924pnp) - outb(((unsigned char) (value & 0xff)), port); else - outb(((unsigned char) (value & 0xff)), port-0x80); - restore_flags(flags); -} - -static int __init detect_c930(void) -{ - unsigned char tmp = mad_read(MC1_PORT); - - if ((tmp & 0x06) != 0x06) - { - DDB(printk("Wrong C930 signature (%x)\n", tmp)); - /* return 0; */ - } - mad_write(MC1_PORT, 0); - - if (mad_read(MC1_PORT) != 0x06) - { - DDB(printk("Wrong C930 signature2 (%x)\n", tmp)); - /* return 0; */ - } - mad_write(MC1_PORT, tmp); /* Restore bits */ - - mad_write(MC7_PORT, 0); - if ((tmp = mad_read(MC7_PORT)) != 0) - { - DDB(printk("MC7 not writable (%x)\n", tmp)); - return 0; - } - mad_write(MC7_PORT, 0xcb); - if ((tmp = mad_read(MC7_PORT)) != 0xcb) - { - DDB(printk("MC7 not writable2 (%x)\n", tmp)); - return 0; - } - - tmp = mad_read(MC0_PORT+18); - if (tmp == 0xff || tmp == 0x00) - return 1; - /* We probably have a C931 */ - DDB(printk("Detected C931 config=0x%02x\n", tmp)); - c931_detected = 1; - - /* - * We cannot configure the chip if it is in PnP mode. - * If we have a CSN assigned (bit 8 in MC13) we first try - * a software reset, then a software power off, finally - * Clearing PnP mode. The last option is not - * Bit 8 in MC13 - */ - if ((mad_read(MC0_PORT+13) & 0x80) == 0) - return 1; - - /* Software reset */ - mad_write(MC9_PORT, 0x02); - mad_write(MC9_PORT, 0x00); - - if ((mad_read(MC0_PORT+13) & 0x80) == 0) - return 1; - - /* Power off, and on again */ - mad_write(MC9_PORT, 0xc2); - mad_write(MC9_PORT, 0xc0); - - if ((mad_read(MC0_PORT+13) & 0x80) == 0) - return 1; - -#if 0 - /* Force off PnP mode. This is not recommended because - * the PnP bios will not recognize the chip on the next - * warm boot and may assignd different resources to other - * PnP/PCI cards. - */ - mad_write(MC0_PORT+17, 0x04); -#endif - return 1; -} - -static int __init detect_mad16(void) -{ - unsigned char tmp, tmp2, bit; - int i, port; - - /* - * Check that reading a register doesn't return bus float (0xff) - * when the card is accessed using password. This may fail in case - * the card is in low power mode. Normally at least the power saving - * mode bit should be 0. - */ - - if ((tmp = mad_read(MC1_PORT)) == 0xff) - { - DDB(printk("MC1_PORT returned 0xff\n")); - return 0; - } - for (i = 0xf8d; i <= 0xf98; i++) - if (!c924pnp) - DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i))) else - DDB(printk("Port %0x (init value) = %0x\n", i-0x80, mad_read(i))); - - if (board_type == C930) - return detect_c930(); - - /* - * Now check that the gate is closed on first I/O after writing - * the password. (This is how a MAD16 compatible card works). - */ - - if ((tmp2 = inb(MC1_PORT)) == tmp) /* It didn't close */ - { - DDB(printk("MC1_PORT didn't close after read (0x%02x)\n", tmp2)); - return 0; - } - - bit = (c924pnp) ? 0x20 : 0x80; - port = (c924pnp) ? MC2_PORT : MC1_PORT; - - tmp = mad_read(port); - mad_write(port, tmp ^ bit); /* Toggle a bit */ - if ((tmp2 = mad_read(port)) != (tmp ^ bit)) /* Compare the bit */ - { - mad_write(port, tmp); /* Restore */ - DDB(printk("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2)); - return 0; - } - mad_write(port, tmp); /* Restore */ - return 1; /* Bingo */ -} - -static int __init wss_init(struct address_info *hw_config) -{ - int ad_flags = 0; - - /* - * Verify the WSS parameters - */ - - if (check_region(hw_config->io_base, 8)) - { - printk(KERN_ERR "MSS: I/O port conflict\n"); - return 0; - } - if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) - return 0; - /* - * Check if the IO port returns valid signature. The original MS Sound - * system returns 0x04 while some cards (AudioTrix Pro for example) - * return 0x00. - */ - - if ((inb(hw_config->io_base + 3) & 0x3f) != 0x04 && - (inb(hw_config->io_base + 3) & 0x3f) != 0x00) - { - DDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb(hw_config->io_base + 3))); - return 0; - } - if (hw_config->irq > 11) - { - printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); - return 0; - } - if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) - { - printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma); - return 0; - } - /* - * Check that DMA0 is not in use with a 8 bit board. - */ - - if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) - { - printk("MSS: Can't use DMA0 with a 8 bit card/slot\n"); - return 0; - } - if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) - printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); - return 1; -} - -static int __init init_c930(struct address_info *hw_config) -{ - unsigned char cfg = 0; - - if(c931_detected) - { - /* Bit 0 has reversd meaning. Bits 1 and 2 sese - reversed on write. - Support only IDE cdrom. IDE port programmed - somewhere else. */ - cfg = (cfg & 0x09) ^ 0x07; - } - - switch (hw_config->io_base) - { - case 0x530: - cfg |= 0x00; - break; - case 0xe80: - cfg |= 0x10; - break; - case 0xf40: - cfg |= 0x20; - break; - case 0x604: - cfg |= 0x30; - break; - default: - printk(KERN_ERR "MAD16: Invalid codec port %x\n", hw_config->io_base); - return 0; - } - mad_write(MC1_PORT, cfg); - - /* MC2 is CD configuration. Don't touch it. */ - - mad_write(MC3_PORT, 0); /* Disable SB mode IRQ and DMA */ - - /* bit 2 of MC4 reverses it's meaning between the C930 - and the C931. */ - cfg = c931_detected ? 0x04 : 0x00; - - mad_write(MC4_PORT, 0x52|cfg); - - mad_write(MC5_PORT, 0x3C); /* Init it into mode2 */ - mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ - mad_write(MC7_PORT, 0xCB); - mad_write(MC10_PORT, 0x11); - - return wss_init(hw_config); -} - -static int __init chip_detect(void) -{ - int i; - - /* - * Then try to detect with the old password - */ - board_type = C924; - - DDB(printk("Detect using password = 0xE5\n")); - - if (detect_mad16()) { - return 1; - } - - board_type = C928; - - DDB(printk("Detect using password = 0xE2\n")); - - if (detect_mad16()) - { - unsigned char model; - - if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) { - DDB(printk("mad16.c: Mozart detected\n")); - board_type = MOZART; - } else { - DDB(printk("mad16.c: 82C928 detected???\n")); - board_type = C928; - } - return 1; - } - - board_type = C929; - - DDB(printk("Detect using password = 0xE3\n")); - - if (detect_mad16()) - { - DDB(printk("mad16.c: 82C929 detected\n")); - return 1; - } - - if (inb(PASSWD_REG) != 0xff) - return 0; - - /* - * First relocate MC# registers to 0xe0e/0xe0f, disable password - */ - - outb((0xE4), PASSWD_REG); - outb((0x80), PASSWD_REG); - - board_type = C930; - - DDB(printk("Detect using password = 0xE4\n")); - - for (i = 0xf8d; i <= 0xf93; i++) - DDB(printk("port %03x = %02x\n", i, mad_read(i))); - - if(detect_mad16()) { - DDB(printk("mad16.c: 82C930 detected\n")); - return 1; - } - - /* The C931 has the password reg at F8D */ - outb((0xE4), 0xF8D); - outb((0x80), 0xF8D); - DDB(printk("Detect using password = 0xE4 for C931\n")); - - if (detect_mad16()) { - return 1; - } - - board_type = C924; - c924pnp++; - DDB(printk("Detect using password = 0xE5 (again), port offset -0x80\n")); - if (detect_mad16()) { - DDB(printk("mad16.c: 82C924 PnP detected\n")); - return 1; - } - - c924pnp=0; - - return 0; -} - -static int __init probe_mad16(struct address_info *hw_config) -{ - int i; - static int valid_ports[] = - { - 0x530, 0xe80, 0xf40, 0x604 - }; - unsigned char tmp; - unsigned char cs4231_mode = 0; - - int ad_flags = 0; - - if (already_initialized) - return 0; - - mad16_osp = hw_config->osp; - - /* - * Check that all ports return 0xff (bus float) when no password - * is written to the password register. - */ - - DDB(printk("--- Detecting MAD16 / Mozart ---\n")); - if (!chip_detect()) - return 0; - - if (board_type == C930) - return init_c930(hw_config); - - - for (i = 0xf8d; i <= 0xf93; i++) - if (!c924pnp) - DDB(printk("port %03x = %02x\n", i, mad_read(i))) else - DDB(printk("port %03x = %02x\n", i-0x80, mad_read(i))); - -/* - * Set the WSS address - */ - - tmp = (mad_read(MC1_PORT) & 0x0f) | 0x80; /* Enable WSS, Disable SB */ - - for (i = 0; i < 5; i++) - { - if (i > 3) /* Not a valid port */ - { - printk(KERN_ERR "MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base); - return 0; - } - if (valid_ports[i] == hw_config->io_base) - { - tmp |= i << 4; /* WSS port select bits */ - break; - } - } - - /* - * Set optional CD-ROM and joystick settings. - */ - - tmp &= ~0x0f; - mad_write(MC1_PORT, tmp); - - tmp = mad_read(MC2_PORT); - - mad_write(MC2_PORT, tmp); - mad_write(MC3_PORT, 0xf0); /* Disable SB */ - - if (board_type == C924) /* Specific C924 init values */ - { - mad_write(MC4_PORT, 0xA0); - mad_write(MC5_PORT, 0x05); - mad_write(MC6_PORT, 0x03); - } - if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) - return 0; - - if (ad_flags & (AD_F_CS4231 | AD_F_CS4248)) - cs4231_mode = 0x02; /* CS4248/CS4231 sync delay switch */ - - if (board_type == C929) - { - mad_write(MC4_PORT, 0xa2); - mad_write(MC5_PORT, 0xA5 | cs4231_mode); - mad_write(MC6_PORT, 0x03); /* Disable MPU401 */ - } - else - { - mad_write(MC4_PORT, 0x02); - mad_write(MC5_PORT, 0x30 | cs4231_mode); - } - - for (i = 0xf8d; i <= 0xf93; i++) if (!c924pnp) - DDB(printk("port %03x after init = %02x\n", i, mad_read(i))) else - DDB(printk("port %03x after init = %02x\n", i-0x80, mad_read(i))); - wss_init(hw_config); - - return 1; -} - -static void __init attach_mad16(struct address_info *hw_config) -{ - - static signed char interrupt_bits[12] = { - -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 - }; - signed char bits; - - static char dma_bits[4] = { - 1, 2, 0, 3 - }; - - int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; - int ad_flags = 0, dma = hw_config->dma, dma2 = hw_config->dma2; - unsigned char dma2_bit = 0; - - already_initialized = 1; - - if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) - return; - - /* - * Set the IRQ and DMA addresses. - */ - - if (board_type == C930 || c924pnp) - interrupt_bits[5] = 0x28; /* Also IRQ5 is possible on C930 */ - - bits = interrupt_bits[hw_config->irq]; - if (bits == -1) - return; - - outb((bits | 0x40), config_port); - if ((inb(version_port) & 0x40) == 0) - printk(KERN_ERR "[IRQ Conflict?]\n"); - - /* - * Handle the capture DMA channel - */ - - if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma) - { - if (!((dma == 0 && dma2 == 1) || - (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0))) - { /* Unsupported combination. Try to swap channels */ - int tmp = dma; - - dma = dma2; - dma2 = tmp; - } - if ((dma == 0 && dma2 == 1) || (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0)) - { - dma2_bit = 0x04; /* Enable capture DMA */ - } - else - { - printk("MAD16: Invalid capture DMA\n"); - dma2 = dma; - } - } - else dma2 = dma; - - outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ - - hw_config->slots[0] = ad1848_init("MAD16 WSS", hw_config->io_base + 4, - hw_config->irq, - dma, - dma2, 0, - hw_config->osp, - THIS_MODULE); - request_region(hw_config->io_base, 4, "MAD16 WSS config"); -} - -static int __init probe_mad16_mpu(struct address_info *hw_config) -{ - static int mpu_attached = 0; - unsigned char tmp; - - if (!already_initialized) /* The MSS port must be initialized first */ - return 0; - - if (mpu_attached) /* Don't let them call this twice */ - return 0; - mpu_attached = 1; - - if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ - { - -#ifdef CONFIG_MAD16_OLDCARD - - tmp = mad_read(MC3_PORT); - - /* - * MAD16 SB base is defined by the WSS base. It cannot be changed - * alone. - * Ignore configured I/O base. Use the active setting. - */ - - if (mad_read(MC1_PORT) & 0x20) - hw_config->io_base = 0x240; - else - hw_config->io_base = 0x220; - - switch (hw_config->irq) - { - case 5: - tmp = (tmp & 0x3f) | 0x80; - break; - case 7: - tmp = (tmp & 0x3f); - break; - case 11: - tmp = (tmp & 0x3f) | 0x40; - break; - default: - printk(KERN_ERR "mad16/Mozart: Invalid MIDI IRQ\n"); - return 0; - } - - mad_write(MC3_PORT, tmp | 0x04); - hw_config->driver_use_1 = SB_MIDI_ONLY; - if (!sb_dsp_detect(hw_config, 0, 0, NULL)) - return 0; - - if (mad_read(MC1_PORT) & 0x20) - hw_config->io_base = 0x240; - else - hw_config->io_base = 0x220; - - hw_config->name = "Mad16/Mozart"; - sb_dsp_init(hw_config, THIS_MODULE); - return 1; -#else - /* assuming all later Mozart cards are identified as - * either 82C928 or Mozart. If so, following code attempts - * to set MPU register. TODO - add probing - */ - - tmp = mad_read(MC8_PORT); - - switch (hw_config->irq) - { - case 5: - tmp |= 0x08; - break; - case 7: - tmp |= 0x10; - break; - case 9: - tmp |= 0x18; - break; - case 10: - tmp |= 0x20; - break; - case 11: - tmp |= 0x28; - break; - default: - printk(KERN_ERR "mad16/MOZART: invalid mpu_irq\n"); - return 0; - } - - switch (hw_config->io_base) - { - case 0x300: - tmp |= 0x01; - break; - case 0x310: - tmp |= 0x03; - break; - case 0x320: - tmp |= 0x05; - break; - case 0x330: - tmp |= 0x07; - break; - default: - printk(KERN_ERR "mad16/MOZART: invalid mpu_io\n"); - return 0; - } - - mad_write(MC8_PORT, tmp); /* write MPU port parameters */ - goto probe_401; -#endif - } - tmp = mad_read(MC6_PORT) & 0x83; - tmp |= 0x80; /* MPU-401 enable */ - - /* Set the MPU base bits */ - - switch (hw_config->io_base) - { - case 0x300: - tmp |= 0x60; - break; - case 0x310: - tmp |= 0x40; - break; - case 0x320: - tmp |= 0x20; - break; - case 0x330: - tmp |= 0x00; - break; - default: - printk(KERN_ERR "MAD16: Invalid MIDI port 0x%x\n", hw_config->io_base); - return 0; - } - - /* Set the MPU IRQ bits */ - - switch (hw_config->irq) - { - case 5: - tmp |= 0x10; - break; - case 7: - tmp |= 0x18; - break; - case 9: - tmp |= 0x00; - break; - case 10: - tmp |= 0x08; - break; - default: - printk(KERN_ERR "MAD16: Invalid MIDI IRQ %d\n", hw_config->irq); - break; - } - - mad_write(MC6_PORT, tmp); /* Write MPU401 config */ - -#ifndef CONFIG_MAD16_OLDCARD -probe_401: -#endif - hw_config->driver_use_1 = SB_MIDI_ONLY; - hw_config->name = "Mad16/Mozart"; - return probe_uart401(hw_config, THIS_MODULE); -} - -static void __exit unload_mad16(struct address_info *hw_config) -{ - ad1848_unload(hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - hw_config->dma2, 0); - release_region(hw_config->io_base, 4); - sound_unload_audiodev(hw_config->slots[0]); -} - -static void __exit unload_mad16_mpu(struct address_info *hw_config) -{ -#ifdef CONFIG_MAD16_OLDCARD - if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ - { - sb_dsp_unload(hw_config, 0); - return; - } -#endif - - unload_uart401(hw_config); -} - -static struct address_info cfg; -static struct address_info cfg_mpu; - -static int found_mpu; - -static int __initdata mpu_io = 0; -static int __initdata mpu_irq = 0; -static int __initdata io = -1; -static int __initdata dma = -1; -static int __initdata dma16 = -1; /* Set this for modules that need it */ -static int __initdata irq = -1; -static int __initdata cdtype = 0; -static int __initdata cdirq = 0; -static int __initdata cdport = 0x340; -static int __initdata cddma = -1; -static int __initdata opl4 = 0; -static int __initdata joystick = 0; - -MODULE_PARM(mpu_io, "i"); -MODULE_PARM(mpu_irq, "i"); -MODULE_PARM(io,"i"); -MODULE_PARM(dma,"i"); -MODULE_PARM(dma16,"i"); -MODULE_PARM(irq,"i"); -MODULE_PARM(cdtype,"i"); -MODULE_PARM(cdirq,"i"); -MODULE_PARM(cdport,"i"); -MODULE_PARM(cddma,"i"); -MODULE_PARM(opl4,"i"); -MODULE_PARM(joystick,"i"); -MODULE_PARM(debug,"i"); - -static int __initdata dma_map[2][8] = -{ - {0x03, -1, -1, -1, -1, 0x00, 0x01, 0x02}, - {0x03, -1, 0x01, 0x00, -1, -1, -1, -1} -}; - -static int __initdata irq_map[16] = -{ - 0x00, -1, -1, 0x0A, - -1, 0x04, -1, 0x08, - -1, 0x10, 0x14, 0x18, - -1, -1, -1, -1 -}; - -static int __init init_mad16(void) -{ - int dmatype = 0; - - printk(KERN_INFO "MAD16 audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - printk(KERN_INFO "CDROM "); - switch (cdtype) - { - case 0x00: - printk("Disabled"); - cdirq = 0; - break; - case 0x02: - printk("Sony CDU31A"); - dmatype = 1; - if(cddma == -1) cddma = 3; - break; - case 0x04: - printk("Mitsumi"); - dmatype = 0; - if(cddma == -1) cddma = 5; - break; - case 0x06: - printk("Panasonic Lasermate"); - dmatype = 1; - if(cddma == -1) cddma = 3; - break; - case 0x08: - printk("Secondary IDE"); - dmatype = 0; - if(cddma == -1) cddma = 5; - break; - case 0x0A: - printk("Primary IDE"); - dmatype = 0; - if(cddma == -1) cddma = 5; - break; - default: - printk("\n"); - printk(KERN_ERR "Invalid CDROM type\n"); - return -EINVAL; - } - - /* - * Build the config words - */ - - mad16_conf = (joystick ^ 1) | cdtype; - mad16_cdsel = 0; - if (opl4) - mad16_cdsel |= 0x20; - - if(cdtype){ - if (cddma > 7 || cddma < 0 || dma_map[dmatype][cddma] == -1) - { - printk("\n"); - printk(KERN_ERR "Invalid CDROM DMA\n"); - return -EINVAL; - } - if (cddma) - printk(", DMA %d", cddma); - else - printk(", no DMA"); - - if (!cdirq) - printk(", no IRQ"); - else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1) - { - printk(", invalid IRQ (disabling)"); - cdirq = 0; - } - else printk(", IRQ %d", cdirq); - - mad16_cdsel |= dma_map[dmatype][cddma]; - - if (cdtype < 0x08) - { - switch (cdport) - { - case 0x340: - mad16_cdsel |= 0x00; - break; - case 0x330: - mad16_cdsel |= 0x40; - break; - case 0x360: - mad16_cdsel |= 0x80; - break; - case 0x320: - mad16_cdsel |= 0xC0; - break; - default: - printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport); - return -EINVAL; - } - } - mad16_cdsel |= irq_map[cdirq]; - } - - printk(".\n"); - printk(KERN_INFO "Joystick port "); - if (joystick == 1) - printk("enabled.\n"); - else - { - joystick = 0; - printk("disabled.\n"); - } - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma16; - - if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { - printk(KERN_ERR "I/O, DMA and irq are mandatory\n"); - return -EINVAL; - } - - if (!probe_mad16(&cfg)) - return -ENODEV; - - cfg_mpu.io_base = mpu_io; - cfg_mpu.irq = mpu_irq; - - attach_mad16(&cfg); - - found_mpu = probe_mad16_mpu(&cfg_mpu); - return 0; -} - -static void __exit cleanup_mad16(void) -{ - if (found_mpu) - unload_mad16_mpu(&cfg_mpu); - unload_mad16(&cfg); -} - -module_init(init_mad16); -module_exit(cleanup_mad16); - -#ifndef MODULE -static int __init setup_mad16(char *str) -{ - /* io, irq */ - int ints[7]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma16 = ints[4]; - mpu_io = ints[5]; - mpu_irq = ints[6]; - - return 1; -} - -__setup("mad16=", setup_mad16); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/maestro.c b/drivers/sound/maestro.c --- a/drivers/sound/maestro.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3807 +0,0 @@ -/***************************************************************************** - * - * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x - * - * 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. - * - * (c) Copyright 1999 Alan Cox - * - * Based heavily on SonicVibes.c: - * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Heavily modified by Zach Brown based on lunch - * with ESS engineers. Many thanks to Howard Kim for providing - * contacts and hardware. Honorable mention goes to Eric - * Brombaugh for all sorts of things. Best regards to the - * proprietors of Hack Central for fine lodging. - * - * Supported devices: - * /dev/dsp0-3 standard /dev/dsp device, (mostly) OSS compatible - * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible - * - * Hardware Description - * - * A working Maestro setup contains the Maestro chip wired to a - * codec or 2. In the Maestro we have the APUs, the ASSP, and the - * Wavecache. The APUs can be though of as virtual audio routing - * channels. They can take data from a number of sources and perform - * basic encodings of the data. The wavecache is a storehouse for - * PCM data. Typically it deals with PCI and interracts with the - * APUs. The ASSP is a wacky DSP like device that ESS is loth - * to release docs on. Thankfully it isn't required on the Maestro - * until you start doing insane things like FM emulation and surround - * encoding. The codecs are almost always AC-97 compliant codecs, - * but it appears that early Maestros may have had PT101 (an ESS - * part?) wired to them. The only real difference in the Maestro - * families is external goop like docking capability, memory for - * the ASSP, and initialization differences. - * - * Driver Operation - * - * We only drive the APU/Wavecache as typical DACs and drive the - * mixers in the codecs. There are 64 APUs. We assign 6 to each - * /dev/dsp? device. 2 channels for output, and 4 channels for - * input. - * - * Each APU can do a number of things, but we only really use - * 3 basic functions. For playback we use them to convert PCM - * data fetched over PCI by the wavecahche into analog data that - * is handed to the codec. One APU for mono, and a pair for stereo. - * When in stereo, the combination of smarts in the APU and Wavecache - * decide which wavecache gets the left or right channel. - * - * For record we still use the old overly mono system. For each in - * coming channel the data comes in from the codec, through a 'input' - * APU, through another rate converter APU, and then into memory via - * the wavecache and PCI. If its stereo, we mash it back into LRLR in - * software. The pass between the 2 APUs is supposedly what requires us - * to have a 512 byte buffer sitting around in wavecache/memory. - * - * The wavecache makes our life even more fun. First off, it can - * only address the first 28 bits of PCI address space, making it - * useless on quite a few architectures. Secondly, its insane. - * It claims to fetch from 4 regions of PCI space, each 4 meg in length. - * But that doesn't really work. You can only use 1 region. So all our - * allocations have to be in 4meg of each other. Booo. Hiss. - * So we have a module parameter, dsps_order, that is the order of - * the number of dsps to provide. All their buffer space is allocated - * on open time. The sonicvibes OSS routines we inherited really want - * power of 2 buffers, so we have all those next to each other, then - * 512 byte regions for the recording wavecaches. This ends up - * wasting quite a bit of memory. The only fixes I can see would be - * getting a kernel allocator that could work in zones, or figuring out - * just how to coerce the WP into doing what we want. - * - * The indirection of the various registers means we have to spinlock - * nearly all register accesses. We have the main register indirection - * like the wave cache, maestro registers, etc. Then we have beasts - * like the APU interface that is indirect registers gotten at through - * the main maestro indirection. Ouch. We spinlock around the actual - * ports on a per card basis. This means spinlock activity at each IO - * operation, but the only IO operation clusters are in non critical - * paths and it makes the code far easier to follow. Interrupts are - * blocked while holding the locks because the int handler has to - * get at some of them :(. The mixer interface doesn't, however. - * We also have an OSS state lock that is thrown around in a few - * places. - * - * This driver has brute force APM suspend support. We catch suspend - * notifications and stop all work being done on the chip. Any people - * that try between this shutdown and the real suspend operation will - * be put to sleep. When we resume we restore our software state on - * the chip and wake up the people that were using it. The code thats - * being used now is quite dirty and assumes we're on a uni-processor - * machine. Much of it will need to be cleaned up for SMP ACPI or - * similar. - * - * We also pay attention to PCI power management now. The driver - * will power down units of the chip that it knows aren't needed. - * The WaveProcessor and company are only powered on when people - * have /dev/dsp*s open. On removal the driver will - * power down the maestro entirely. There could still be - * trouble with BIOSen that magically change power states - * themselves, but we'll see. - * - * History - * v0.15 - May 21 2001 - Marcus Meissner - * Ported to Linux 2.4 PCI API. Some clean ups, global devs list - * removed (now using pci device driver data). - * PM needs to be polished still. Bumped version. - * (still kind of v0.14) May 13 2001 - Ben Pfaff - * Add support for 978 docking and basic hardware volume control - * (still kind of v0.14) Nov 23 - Alan Cox - * Add clocking= for people with seriously warped hardware - * (still v0.14) Nov 10 2000 - Bartlomiej Zolnierkiewicz - * add __init to maestro_ac97_init() and maestro_install() - * (still based on v0.14) Mar 29 2000 - Zach Brown - * move to 2.3 power management interface, which - * required hacking some suspend/resume/check paths - * make static compilation work - * v0.14 - Jan 28 2000 - Zach Brown - * add PCI power management through ACPI regs. - * we now shut down on machine reboot/halt - * leave scary PCI config items alone (isa stuff, mostly) - * enable 1921s, it seems only mine was broke. - * fix swapped left/right pcm dac. har har. - * up bob freq, increase buffers, fix pointers at underflow - * silly compilation problems - * v0.13 - Nov 18 1999 - Zach Brown - * fix nec Versas? man would that be cool. - * v0.12 - Nov 12 1999 - Zach Brown - * brown bag volume max fix.. - * v0.11 - Nov 11 1999 - Zach Brown - * use proper stereo apu decoding, mmap/write should work. - * make volume sliders more useful, tweak rate calculation. - * fix lame 8bit format reporting bug. duh. apm apu saving buglet also - * fix maestro 1 clock freq "bug", remove pt101 support - * v0.10 - Oct 28 1999 - Zach Brown - * aha, so, sometimes the WP writes a status word to offset 0 - * from one of the PCMBARs. rearrange allocation accordingly.. - * cheers again to Eric for being a good hacker in investigating this. - * Jeroen Hoogervorst submits 7500 fix out of nowhere. yay. :) - * v0.09 - Oct 23 1999 - Zach Brown - * added APM support. - * re-order something such that some 2Es now work. Magic! - * new codec reset routine. made some codecs come to life. - * fix clear_advance, sync some control with ESS. - * now write to all base regs to be paranoid. - * v0.08 - Oct 20 1999 - Zach Brown - * Fix initial buflen bug. I am so smart. also smp compiling.. - * I owe Eric yet another beer: fixed recmask, igain, - * muting, and adc sync consistency. Go Team. - * v0.07 - Oct 4 1999 - Zach Brown - * tweak adc/dac, formating, and stuff to allow full duplex - * allocate dsps memory at open() so we can fit in the wavecache window - * fix wavecache braindamage. again. no more scribbling? - * fix ess 1921 codec bug on some laptops. - * fix dumb pci scanning bug - * started 2.3 cleanup, redid spinlocks, little cleanups - * v0.06 - Sep 20 1999 - Zach Brown - * fix wavecache thinkos. limit to 1 /dev/dsp. - * eric is wearing his thinking toque this week. - * spotted apu mode bugs and gain ramping problem - * don't touch weird mixer regs, make recmask optional - * fixed igain inversion, defaults for mixers, clean up rec_start - * make mono recording work. - * report subsystem stuff, please send reports. - * littles: parallel out, amp now - * v0.05 - Sep 17 1999 - Zach Brown - * merged and fixed up Eric's initial recording code - * munged format handling to catch misuse, needs rewrite. - * revert ring bus init, fixup shared int, add pci busmaster setting - * fix mixer oss interface, fix mic mute and recmask - * mask off unsupported mixers, reset with all 1s, modularize defaults - * make sure bob is running while we need it - * got rid of device limit, initial minimal apm hooks - * pull out dead code/includes, only allow multimedia/audio maestros - * v0.04 - Sep 01 1999 - Zach Brown - * copied memory leak fix from sonicvibes driver - * different ac97 reset, play with 2.0 ac97, simplify ring bus setup - * bob freq code, region sanity, jitter sync fix; all from Eric - * - * TODO - * fix bob frequency - * endianness - * do smart things with ac97 2.0 bits. - * dual codecs - * leave 54->61 open - * - * it also would be fun to have a mode that would not use pci dma at all - * but would copy into the wavecache on board memory and use that - * on architectures that don't like the maestro's pci dma ickiness. - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -static int maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *d); - -#include "maestro.h" - -static struct pci_driver maestro_pci_driver; - -/* --------------------------------------------------------------------- */ - -#define M_DEBUG 1 - -#ifdef M_DEBUG -static int debug=0; -#define M_printk(args...) {if (debug) printk(args);} -#else -#define M_printk(x) -#endif - -/* we try to setup 2^(dsps_order) /dev/dsp devices */ -static int dsps_order=0; -/* wether or not we mess around with power management */ -static int use_pm=2; /* set to 1 for force */ -/* clocking for broken hardware - a few laptops seem to use a 50Khz clock - ie insmod with clocking=50000 or so */ - -static int clocking=48000; - -MODULE_AUTHOR("Zach Brown , Alan Cox "); -MODULE_DESCRIPTION("ESS Maestro Driver"); -MODULE_LICENSE("GPL"); - -#ifdef M_DEBUG -MODULE_PARM(debug,"i"); -#endif -MODULE_PARM(dsps_order,"i"); -MODULE_PARM(use_pm,"i"); -MODULE_PARM(clocking, "i"); - -/* --------------------------------------------------------------------- */ -#define DRIVER_VERSION "0.15" - -#ifndef PCI_VENDOR_ESS -#define PCI_VENDOR_ESS 0x125D -#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 /* Maestro 2 */ -#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 /* Maestro 2E */ - -#define PCI_VENDOR_ESS_OLD 0x1285 /* Platform Tech, - the people the maestro - was bought from */ -#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 /* maestro 1 */ -#endif /* PCI_VENDOR_ESS */ - -#define ESS_CHAN_HARD 0x100 - -/* NEC Versas ? */ -#define NEC_VERSA_SUBID1 0x80581033 -#define NEC_VERSA_SUBID2 0x803c1033 - - -/* changed so that I could actually find all the - references and fix them up. its a little more readable now. */ -#define ESS_FMT_STEREO 0x01 -#define ESS_FMT_16BIT 0x02 -#define ESS_FMT_MASK 0x03 -#define ESS_DAC_SHIFT 0 -#define ESS_ADC_SHIFT 4 - -#define ESS_STATE_MAGIC 0x125D1968 -#define ESS_CARD_MAGIC 0x19283746 - -#define DAC_RUNNING 1 -#define ADC_RUNNING 2 - -#define MAX_DSP_ORDER 2 -#define MAX_DSPS (1<src buffer page */ - void *mixbuf; - -}; - -struct ess_card { - unsigned int magic; - - /* We keep maestro cards in a linked list */ - struct ess_card *next; - - int dev_mixer; - - int card_type; - - /* as most of this is static, - perhaps it should be a pointer to a global struct */ - struct mixer_goo { - int modcnt; - int supported_mixers; - int stereo_mixers; - int record_sources; - /* the caller must guarantee arg sanity before calling these */ -/* int (*read_mixer)(struct ess_card *card, int index);*/ - void (*write_mixer)(struct ess_card *card,int mixer, unsigned int left,unsigned int right); - int (*recmask_io)(struct ess_card *card,int rw,int mask); - unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; - } mix; - - int power_regs; - - int in_suspend; - wait_queue_head_t suspend_queue; - - struct ess_state channels[MAX_DSPS]; - u16 maestro_map[NR_IDRS]; /* Register map */ - /* we have to store this junk so that we can come back from a - suspend */ - u16 apu_map[NR_APUS][NR_APU_REGS]; /* contents of apu regs */ - - /* this locks around the physical registers on the card */ - spinlock_t lock; - - /* memory for this card.. wavecache limited :(*/ - void *dmapages; - int dmaorder; - - /* hardware resources */ - struct pci_dev *pcidev; - u32 iobase; - u32 irq; - - int bob_freq; - char dsps_open; - - int dock_mute_vol; -}; - -static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ); - -static 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; -} - - -/* --------------------------------------------------------------------- */ - -static void check_suspend(struct ess_card *card); - -/* --------------------------------------------------------------------- */ - - -/* - * ESS Maestro AC97 codec programming interface. - */ - -static void maestro_ac97_set(struct ess_card *card, u8 cmd, u16 val) -{ - int io = card->iobase; - int i; - /* - * Wait for the codec bus to be free - */ - - check_suspend(card); - - for(i=0;i<10000;i++) - { - if(!(inb(io+ESS_AC97_INDEX)&1)) - break; - } - /* - * Write the bus - */ - outw(val, io+ESS_AC97_DATA); - mdelay(1); - outb(cmd, io+ESS_AC97_INDEX); - mdelay(1); -} - -static u16 maestro_ac97_get(struct ess_card *card, u8 cmd) -{ - int io = card->iobase; - int sanity=10000; - u16 data; - int i; - - check_suspend(card); - /* - * Wait for the codec bus to be free - */ - - for(i=0;i<10000;i++) - { - if(!(inb(io+ESS_AC97_INDEX)&1)) - break; - } - - outb(cmd|0x80, io+ESS_AC97_INDEX); - mdelay(1); - - while(inb(io+ESS_AC97_INDEX)&1) - { - sanity--; - if(!sanity) - { - printk(KERN_ERR "maestro: ac97 codec timeout reading 0x%x.\n",cmd); - return 0; - } - } - data=inw(io+ESS_AC97_DATA); - mdelay(1); - return data; -} - -/* OSS interface to the ac97s.. */ - -#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\ - SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\ - SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN) - -#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ - SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\ - SOUND_MASK_SPEAKER) - -#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ - SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ - SOUND_MASK_PHONEIN) - -#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<offset); - - if(AC97_STEREO_MASK & (1<> 8) & 0x7f; - right = val & 0x7f; - - if (mixer == SOUND_MIXER_IGAIN) { - right = (right * 100) / mh->scale; - left = (left * 100) / mh->scale; - else { - right = 100 - ((right * 100) / mh->scale); - left = 100 - ((left * 100) / mh->scale); - } - - ret = left | (right << 8); - } else if (mixer == SOUND_MIXER_SPEAKER) { - ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); - } else if (mixer == SOUND_MIXER_MIC) { - ret = 100 - (((val & 0x1f) * 100) / mh->scale); - /* the low bit is optional in the tone sliders and masking - it lets is avoid the 0xf 'bypass'.. */ - } else if (mixer == SOUND_MIXER_BASS) { - ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); - } else if (mixer == SOUND_MIXER_TREBLE) { - ret = 100 - (((val & 0xe) * 100) / mh->scale); - } - - M_printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret); - - return ret; -} -#endif - -/* write the OSS encoded volume to the given OSS encoded mixer, - again caller's job to make sure all is well in arg land, - call with spinlock held */ - -/* linear scale -> log */ -static unsigned char lin2log[101] = -{ -0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 , -50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 , -63 , 65 , 66 , 67 , 68 , 69 , 69 , 70 , 71 , -72 , 73 , 73 , 74 , 75 , 75 , 76 , 77 , 77 , -78 , 78 , 79 , 80 , 80 , 81 , 81 , 82 , 82 , -83 , 83 , 84 , 84 , 84 , 85 , 85 , 86 , 86 , -87 , 87 , 87 , 88 , 88 , 88 , 89 , 89 , 89 , -90 , 90 , 90 , 91 , 91 , 91 , 92 , 92 , 92 , -93 , 93 , 93 , 94 , 94 , 94 , 94 , 95 , 95 , -95 , 95 , 96 , 96 , 96 , 96 , 97 , 97 , 97 , -97 , 98 , 98 , 98 , 98 , 99 , 99 , 99 , 99 , 99 -}; - -static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, unsigned int right) -{ - u16 val=0; - struct ac97_mixer_hw *mh = &ac97_hw[mixer]; - - M_printk("wrote mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right); - - if(AC97_STEREO_MASK & (1<scale) / 100; - left = (left * mh->scale) / 100; - if ((left == 0) && (right == 0)) - val |= 0x8000; - } else { - /* log conversion for the stereo controls */ - if((left == 0) && (right == 0)) - val = 0x8000; - right = ((100 - lin2log[right]) * mh->scale) / 100; - left = ((100 - lin2log[left]) * mh->scale) / 100; - } - - val |= (left << 8) | right; - - } else if (mixer == SOUND_MIXER_SPEAKER) { - val = (((100 - left) * mh->scale) / 100) << 1; - } else if (mixer == SOUND_MIXER_MIC) { - val = maestro_ac97_get(card, mh->offset) & ~0x801f; - val |= (((100 - left) * mh->scale) / 100); - /* the low bit is optional in the tone sliders and masking - it lets is avoid the 0xf 'bypass'.. */ - } else if (mixer == SOUND_MIXER_BASS) { - val = maestro_ac97_get(card , mh->offset) & ~0x0f00; - val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; - } else if (mixer == SOUND_MIXER_TREBLE) { - val = maestro_ac97_get(card , mh->offset) & ~0x000f; - val |= (((100 - left) * mh->scale) / 100) & 0x000e; - } - - maestro_ac97_set(card , mh->offset, val); - - M_printk(" -> %x\n",val); -} - -/* the following tables allow us to go from - OSS <-> ac97 quickly. */ - -enum ac97_recsettings { - AC97_REC_MIC=0, - AC97_REC_CD, - AC97_REC_VIDEO, - AC97_REC_AUX, - AC97_REC_LINE, - AC97_REC_STEREO, /* combination of all enabled outputs.. */ - AC97_REC_MONO, /*.. or the mono equivalent */ - AC97_REC_PHONE -}; - -static unsigned int ac97_oss_mask[] = { - [AC97_REC_MIC] = SOUND_MASK_MIC, - [AC97_REC_CD] = SOUND_MASK_CD, - [AC97_REC_VIDEO] = SOUND_MASK_VIDEO, - [AC97_REC_AUX] = SOUND_MASK_LINE1, - [AC97_REC_LINE] = SOUND_MASK_LINE, - [AC97_REC_PHONE] = SOUND_MASK_PHONEIN -}; - -/* indexed by bit position */ -static unsigned int ac97_oss_rm[] = { - [SOUND_MIXER_MIC] = AC97_REC_MIC, - [SOUND_MIXER_CD] = AC97_REC_CD, - [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, - [SOUND_MIXER_LINE1] = AC97_REC_AUX, - [SOUND_MIXER_LINE] = AC97_REC_LINE, - [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE -}; - -/* read or write the recmask - the ac97 can really have left and right recording - 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 ess_card *card, int read, int mask) -{ - unsigned int val = ac97_oss_mask[ maestro_ac97_get(card, 0x1a) & 0x7 ]; - - if (read) return val; - - /* oss can have many inputs, maestro cant. try - to pick the 'new' one */ - - if (mask != val) mask &= ~val; - - val = ffs(mask) - 1; - val = ac97_oss_rm[val]; - val |= val << 8; /* set both channels */ - - M_printk("maestro: setting ac97 recmask to 0x%x\n",val); - - maestro_ac97_set(card,0x1a,val); - - return 0; -}; - -/* - * The Maestro can be wired to a standard AC97 compliant codec - * (see www.intel.com for the pdf's on this), or to a PT101 codec - * which appears to be the ES1918 (data sheet on the esstech.com.tw site) - * - * The PT101 setup is untested. - */ - -static u16 __init maestro_ac97_init(struct ess_card *card) -{ - u16 vend1, vend2, caps; - - card->mix.supported_mixers = AC97_SUPPORTED_MASK; - card->mix.stereo_mixers = AC97_STEREO_MASK; - card->mix.record_sources = AC97_RECORD_MASK; -/* card->mix.read_mixer = ac97_read_mixer;*/ - card->mix.write_mixer = ac97_write_mixer; - card->mix.recmask_io = ac97_recmask_io; - - vend1 = maestro_ac97_get(card, 0x7c); - vend2 = maestro_ac97_get(card, 0x7e); - - caps = maestro_ac97_get(card, 0x00); - - printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n", - vend1,vend2,caps,maestro_ac97_get(card,0x26) & 0xf); - - if (! (caps & 0x4) ) { - /* no bass/treble nobs */ - card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); - } - - /* XXX endianness, dork head. */ - /* vendor specifc bits.. */ - switch ((long)(vend1 << 16) | vend2) { - case 0x545200ff: /* TriTech */ - /* no idea what this does */ - maestro_ac97_set(card,0x2a,0x0001); - maestro_ac97_set(card,0x2c,0x0000); - maestro_ac97_set(card,0x2c,0xffff); - break; -#if 0 /* i thought the problems I was seeing were with - the 1921, but apparently they were with the pci board - it was on, so this code is commented out. - lets see if this holds true. */ - case 0x83847609: /* ESS 1921 */ - /* writing to 0xe (mic) or 0x1a (recmask) seems - to hang this codec */ - card->mix.supported_mixers &= ~(SOUND_MASK_MIC); - card->mix.record_sources = 0; - card->mix.recmask_io = NULL; -#if 0 /* don't ask. I have yet to see what these actually do. */ - maestro_ac97_set(card,0x76,0xABBA); /* o/~ Take a chance on me o/~ */ - udelay(20); - maestro_ac97_set(card,0x78,0x3002); - udelay(20); - maestro_ac97_set(card,0x78,0x3802); - udelay(20); -#endif - break; -#endif - default: break; - } - - maestro_ac97_set(card, 0x1E, 0x0404); - /* null misc stuff */ - maestro_ac97_set(card, 0x20, 0x0000); - - return 0; -} - -#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 :) */ -static u16 maestro_pt101_init(struct ess_card *card,int iobase) -{ - printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); - /* who knows.. */ - maestro_ac97_set(iobase, 0x2A, 0x0001); - maestro_ac97_set(iobase, 0x2C, 0x0000); - maestro_ac97_set(iobase, 0x2C, 0xFFFF); - maestro_ac97_set(iobase, 0x10, 0x9F1F); - maestro_ac97_set(iobase, 0x12, 0x0808); - maestro_ac97_set(iobase, 0x14, 0x9F1F); - maestro_ac97_set(iobase, 0x16, 0x9F1F); - maestro_ac97_set(iobase, 0x18, 0x0404); - maestro_ac97_set(iobase, 0x1A, 0x0000); - maestro_ac97_set(iobase, 0x1C, 0x0000); - maestro_ac97_set(iobase, 0x02, 0x0404); - maestro_ac97_set(iobase, 0x04, 0x0808); - maestro_ac97_set(iobase, 0x0C, 0x801F); - maestro_ac97_set(iobase, 0x0E, 0x801F); - return 0; -} -#endif - -/* this is very magic, and very slow.. */ -static void -maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev) -{ - u16 save_68; - u16 w; - u32 vend; - - outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); - outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); - outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); - - /* reset the first codec */ - outw(0x0000, ioaddr+0x36); - save_68 = inw(ioaddr+0x68); - pci_read_config_word(pcidev, 0x58, &w); /* something magical with gpio and bus arb. */ - pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &vend); - if( w & 0x1) - save_68 |= 0x10; - outw(0xfffe, ioaddr + 0x64); /* tickly gpio 0.. */ - outw(0x0001, ioaddr + 0x68); - outw(0x0000, ioaddr + 0x60); - udelay(20); - outw(0x0001, ioaddr + 0x60); - mdelay(20); - - outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ - outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38); - outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a); - outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c); - - /* now the second codec */ - outw(0x0000, ioaddr+0x36); - outw(0xfff7, ioaddr + 0x64); - save_68 = inw(ioaddr+0x68); - outw(0x0009, ioaddr + 0x68); - outw(0x0001, ioaddr + 0x60); - udelay(20); - outw(0x0009, ioaddr + 0x60); - mdelay(500); /* .. ouch.. */ - outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); - outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); - outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); - -#if 0 /* the loop here needs to be much better if we want it.. */ - M_printk("trying software reset\n"); - /* try and do a software reset */ - outb(0x80|0x7c, ioaddr + 0x30); - for (w=0; ; w++) { - if ((inw(ioaddr+ 0x30) & 1) == 0) { - if(inb(ioaddr + 0x32) !=0) break; - - outb(0x80|0x7d, ioaddr + 0x30); - if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; - outb(0x80|0x7f, ioaddr + 0x30); - if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; - } - - if( w > 10000) { - outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ - mdelay(500); /* oh my.. */ - outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); - udelay(1); - outw( 0x80, ioaddr+0x30); - for(w = 0 ; w < 10000; w++) { - if((inw(ioaddr + 0x30) & 1) ==0) break; - } - } - } -#endif - if ( vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { - /* turn on external amp? */ - outw(0xf9ff, ioaddr + 0x64); - outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68); - outw(0x0209, ioaddr + 0x60); - } - - /* Turn on the 978 docking chip. - First frob the "master output enable" bit, - then set most of the playback volume control registers to max. */ - outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); - outb(0xff, ioaddr+0xc3); - outb(0xff, ioaddr+0xc4); - outb(0xff, ioaddr+0xc6); - outb(0xff, ioaddr+0xc8); - outb(0x3f, ioaddr+0xcf); - outb(0x3f, ioaddr+0xd0); -} -/* - * Indirect register access. Not all registers are readable so we - * need to keep register state ourselves - */ - -#define WRITEABLE_MAP 0xEFFFFF -#define READABLE_MAP 0x64003F - -/* - * The Maestro engineers were a little indirection happy. These indirected - * registers themselves include indirect registers at another layer - */ - -static void __maestro_write(struct ess_card *card, u16 reg, u16 data) -{ - long ioaddr = card->iobase; - - outw(reg, ioaddr+0x02); - outw(data, ioaddr+0x00); - if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg); - else card->maestro_map[reg]=data; - -} - -static void maestro_write(struct ess_state *s, u16 reg, u16 data) -{ - unsigned long flags; - - check_suspend(s->card); - spin_lock_irqsave(&s->card->lock,flags); - - __maestro_write(s->card,reg,data); - - spin_unlock_irqrestore(&s->card->lock,flags); -} - -static u16 __maestro_read(struct ess_card *card, u16 reg) -{ - long ioaddr = card->iobase; - - outw(reg, ioaddr+0x02); - return card->maestro_map[reg]=inw(ioaddr+0x00); -} - -static u16 maestro_read(struct ess_state *s, u16 reg) -{ - if(READABLE_MAP & (1<card); - spin_lock_irqsave(&s->card->lock,flags); - - __maestro_read(s->card,reg); - - spin_unlock_irqrestore(&s->card->lock,flags); - } - return s->card->maestro_map[reg]; -} - -/* - * These routines handle accessing the second level indirections to the - * wave ram. - */ - -/* - * The register names are the ones ESS uses (see 104T31.ZIP) - */ - -#define IDR0_DATA_PORT 0x00 -#define IDR1_CRAM_POINTER 0x01 -#define IDR2_CRAM_DATA 0x02 -#define IDR3_WAVE_DATA 0x03 -#define IDR4_WAVE_PTR_LOW 0x04 -#define IDR5_WAVE_PTR_HI 0x05 -#define IDR6_TIMER_CTRL 0x06 -#define IDR7_WAVE_ROMRAM 0x07 - -static void apu_index_set(struct ess_card *card, u16 index) -{ - int i; - __maestro_write(card, IDR1_CRAM_POINTER, index); - for(i=0;i<1000;i++) - if(__maestro_read(card, IDR1_CRAM_POINTER)==index) - return; - printk(KERN_WARNING "maestro: APU register select failed.\n"); -} - -static void apu_data_set(struct ess_card *card, u16 data) -{ - int i; - for(i=0;i<1000;i++) - { - if(__maestro_read(card, IDR0_DATA_PORT)==data) - return; - __maestro_write(card, IDR0_DATA_PORT, data); - } -} - -/* - * This is the public interface for APU manipulation. It handles the - * interlock to avoid two APU writes in parallel etc. Don't diddle - * directly with the stuff above. - */ - -static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data) -{ - unsigned long flags; - - check_suspend(s->card); - - if(channel&ESS_CHAN_HARD) - channel&=~ESS_CHAN_HARD; - else - { - if(channel>5) - printk("BAD CHANNEL %d.\n",channel); - else - channel = s->apu[channel]; - /* store based on real hardware apu/reg */ - s->card->apu_map[channel][reg]=data; - } - reg|=(channel<<4); - - /* hooray for double indirection!! */ - spin_lock_irqsave(&s->card->lock,flags); - - apu_index_set(s->card, reg); - apu_data_set(s->card, data); - - spin_unlock_irqrestore(&s->card->lock,flags); -} - -static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg) -{ - unsigned long flags; - u16 v; - - check_suspend(s->card); - - if(channel&ESS_CHAN_HARD) - channel&=~ESS_CHAN_HARD; - else - channel = s->apu[channel]; - - reg|=(channel<<4); - - spin_lock_irqsave(&s->card->lock,flags); - - apu_index_set(s->card, reg); - v=__maestro_read(s->card, IDR0_DATA_PORT); - - spin_unlock_irqrestore(&s->card->lock,flags); - return v; -} - - -/* - * The wavecache buffers between the APUs and - * pci bus mastering - */ - -static void wave_set_register(struct ess_state *s, u16 reg, u16 value) -{ - long ioaddr = s->card->iobase; - unsigned long flags; - check_suspend(s->card); - - spin_lock_irqsave(&s->card->lock,flags); - - outw(reg, ioaddr+0x10); - outw(value, ioaddr+0x12); - - spin_unlock_irqrestore(&s->card->lock,flags); -} - -static u16 wave_get_register(struct ess_state *s, u16 reg) -{ - long ioaddr = s->card->iobase; - unsigned long flags; - u16 value; - check_suspend(s->card); - - spin_lock_irqsave(&s->card->lock,flags); - outw(reg, ioaddr+0x10); - value=inw(ioaddr+0x12); - spin_unlock_irqrestore(&s->card->lock,flags); - - return value; -} - -static void sound_reset(int ioaddr) -{ - outw(0x2000, 0x18+ioaddr); - udelay(1); - outw(0x0000, 0x18+ioaddr); - udelay(1); -} - -/* sets the play formats of these apus, should be passed the already shifted format */ -static void set_apu_fmt(struct ess_state *s, int apu, int mode) -{ - int apu_fmt = 0x10; - - if(!(mode&ESS_FMT_16BIT)) apu_fmt+=0x20; - if((mode&ESS_FMT_STEREO)) apu_fmt+=0x10; - s->apu_mode[apu] = apu_fmt; - s->apu_mode[apu+1] = apu_fmt; -} - -/* this only fixes the output apu mode to be later set by start_dac and - company. output apu modes are set in ess_rec_setup */ -static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data) -{ - s->fmt = (s->fmt & mask) | data; - set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK); -} - -/* this is off by a little bit.. */ -static u32 compute_rate(struct ess_state *s, u32 freq) -{ - u32 clock = clock_freq[s->card->card_type]; - - freq = (freq * clocking)/48000; - - if (freq == 48000) - return 0x10000; - - return ((freq / clock) <<16 )+ - (((freq % clock) << 16) / clock); -} - -static void set_dac_rate(struct ess_state *s, unsigned int rate) -{ - u32 freq; - int fmt = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - - s->ratedac = rate; - - if(! (fmt & ESS_FMT_16BIT) && !(fmt & ESS_FMT_STEREO)) - rate >>= 1; - -/* M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/ - - freq = compute_rate(s, rate); - - /* Load the frequency, turn on 6dB */ - apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 0, 3, freq>>8); - apu_set_register(s, 1, 2,(apu_get_register(s, 1, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 1, 3, freq>>8); -} - -static void set_adc_rate(struct ess_state *s, unsigned rate) -{ - u32 freq; - - /* Sample Rate conversion APUs don't like 0x10000 for their rate */ - if (rate > 47999) - rate = 47999; - if (rate < 4000) - rate = 4000; - - s->rateadc = rate; - - freq = compute_rate(s, rate); - - /* Load the frequency, turn on 6dB */ - apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 2, 3, freq>>8); - apu_set_register(s, 3, 2,(apu_get_register(s, 3, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 3, 3, freq>>8); - - /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ - freq = 0x10000; - - apu_set_register(s, 4, 2,(apu_get_register(s, 4, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 4, 3, freq>>8); - apu_set_register(s, 5, 2,(apu_get_register(s, 5, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 5, 3, freq>>8); -} - -/* Stop our host of recording apus */ -static inline void stop_adc(struct ess_state *s) -{ - /* XXX lets hope we don't have to lock around this */ - if (! (s->enable & ADC_RUNNING)) return; - - s->enable &= ~ADC_RUNNING; - apu_set_register(s, 2, 0, apu_get_register(s, 2, 0)&0xFF0F); - apu_set_register(s, 3, 0, apu_get_register(s, 3, 0)&0xFF0F); - apu_set_register(s, 4, 0, apu_get_register(s, 2, 0)&0xFF0F); - apu_set_register(s, 5, 0, apu_get_register(s, 3, 0)&0xFF0F); -} - -/* stop output apus */ -static void stop_dac(struct ess_state *s) -{ - /* XXX have to lock around this? */ - if (! (s->enable & DAC_RUNNING)) return; - - s->enable &= ~DAC_RUNNING; - apu_set_register(s, 0, 0, apu_get_register(s, 0, 0)&0xFF0F); - apu_set_register(s, 1, 0, apu_get_register(s, 1, 0)&0xFF0F); -} - -static void start_dac(struct ess_state *s) -{ - /* XXX locks? */ - if ( (s->dma_dac.mapped || s->dma_dac.count > 0) && - s->dma_dac.ready && - (! (s->enable & DAC_RUNNING)) ) { - - s->enable |= DAC_RUNNING; - - apu_set_register(s, 0, 0, - (apu_get_register(s, 0, 0)&0xFF0F)|s->apu_mode[0]); - - if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) - apu_set_register(s, 1, 0, - (apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]); - } -} - -static void start_adc(struct ess_state *s) -{ - /* XXX locks? */ - if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready && (! (s->enable & ADC_RUNNING)) ) { - - s->enable |= ADC_RUNNING; - apu_set_register(s, 2, 0, - (apu_get_register(s, 2, 0)&0xFF0F)|s->apu_mode[2]); - apu_set_register(s, 4, 0, - (apu_get_register(s, 4, 0)&0xFF0F)|s->apu_mode[4]); - - if( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { - apu_set_register(s, 3, 0, - (apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]); - apu_set_register(s, 5, 0, - (apu_get_register(s, 5, 0)&0xFF0F)|s->apu_mode[5]); - } - - } -} - - -/* - * Native play back driver - */ - -/* the mode passed should be already shifted and masked */ -static void -ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) -{ - u32 pa; - u32 tmpval; - int high_apu = 0; - int channel; - - M_printk("mode=%d rate=%d buf=%p len=%d.\n", - mode, rate, buffer, size); - - /* all maestro sizes are in 16bit words */ - size >>=1; - - if(mode&ESS_FMT_STEREO) { - high_apu++; - /* only 16/stereo gets size divided */ - if(mode&ESS_FMT_16BIT) - size>>=1; - } - - for(channel=0; channel <= high_apu; channel++) - { - pa = virt_to_bus(buffer); - - /* set the wavecache control reg */ - tmpval = (pa - 0x10) & 0xFFF8; - if(!(mode & ESS_FMT_16BIT)) tmpval |= 4; - if(mode & ESS_FMT_STEREO) tmpval |= 2; - ess->apu_base[channel]=tmpval; - wave_set_register(ess, ess->apu[channel]<<3, tmpval); - - pa -= virt_to_bus(ess->card->dmapages); - pa>>=1; /* words */ - - /* base offset of dma calcs when reading the pointer - on the left one */ - if(!channel) ess->dma_dac.base = pa&0xFFFF; - - pa|=0x00400000; /* System RAM */ - - /* XXX the 16bit here might not be needed.. */ - if((mode & ESS_FMT_STEREO) && (mode & ESS_FMT_16BIT)) { - if(channel) - pa|=0x00800000; /* Stereo */ - pa>>=1; - } - -/* XXX think about endianess when writing these registers */ - M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa); - /* start of sample */ - apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); - apu_set_register(ess, channel, 5, pa&0xFFFF); - /* sample end */ - apu_set_register(ess, channel, 6, (pa+size)&0xFFFF); - /* setting loop len == sample len */ - apu_set_register(ess, channel, 7, size); - - /* clear effects/env.. */ - apu_set_register(ess, channel, 8, 0x0000); - /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ - apu_set_register(ess, channel, 9, 0xD000); - - /* clear routing stuff */ - apu_set_register(ess, channel, 11, 0x0000); - /* dma on, no envelopes, filter to all 1s) */ - apu_set_register(ess, channel, 0, 0x400F); - - if(mode&ESS_FMT_16BIT) - ess->apu_mode[channel]=0x10; - else - ess->apu_mode[channel]=0x30; - - if(mode&ESS_FMT_STEREO) { - /* set panning: left or right */ - apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0 : 0x10)); - ess->apu_mode[channel] += 0x10; - } else - apu_set_register(ess, channel, 10, 0x8F08); - } - - /* clear WP interrupts */ - outw(1, ess->card->iobase+0x04); - /* enable WP ints */ - outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); - - /* go team! */ - set_dac_rate(ess,rate); - start_dac(ess); -} - -/* - * Native record driver - */ - -/* again, passed mode is alrady shifted/masked */ -static void -ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) -{ - int apu_step = 2; - int channel; - - M_printk("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n", - mode, rate, buffer, size); - - /* all maestro sizes are in 16bit words */ - size >>=1; - - /* we're given the full size of the buffer, but - in stereo each channel will only use its half */ - if(mode&ESS_FMT_STEREO) { - size >>=1; - apu_step = 1; - } - - /* APU assignments: 2 = mono/left SRC - 3 = right SRC - 4 = mono/left Input Mixer - 5 = right Input Mixer */ - for(channel=2;channel<6;channel+=apu_step) - { - int i; - int bsize, route; - u32 pa; - u32 tmpval; - - /* data seems to flow from the codec, through an apu into - the 'mixbuf' bit of page, then through the SRC apu - and out to the real 'buffer'. ok. sure. */ - - if(channel & 0x04) { - /* ok, we're an input mixer going from adc - through the mixbuf to the other apus */ - - if(!(channel & 0x01)) { - pa = virt_to_bus(ess->mixbuf); - } else { - pa = virt_to_bus(ess->mixbuf + (PAGE_SIZE >> 4)); - } - - /* we source from a 'magic' apu */ - bsize = PAGE_SIZE >> 5; /* half of this channels alloc, in words */ - route = 0x14 + (channel - 4); /* parallel in crap, see maestro reg 0xC [8-11] */ - ess->apu_mode[channel] = 0x90; /* Input Mixer */ - - } else { - /* we're a rate converter taking - input from the input apus and outputing it to - system memory */ - if(!(channel & 0x01)) { - pa = virt_to_bus(buffer); - } else { - /* right channel records its split half. - *2 accomodates for rampant shifting earlier */ - pa = virt_to_bus(buffer + size*2); - } - - ess->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ - - bsize = size; - /* get input from inputing apu */ - route = channel + 2; - } - - M_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n",pa,channel); - - /* set the wavecache control reg */ - tmpval = (pa - 0x10) & 0xFFF8; - ess->apu_base[channel]=tmpval; - wave_set_register(ess, ess->apu[channel]<<3, tmpval); - - pa -= virt_to_bus(ess->card->dmapages); - pa>>=1; /* words */ - - /* base offset of dma calcs when reading the pointer - on this left one */ - if(channel==2) ess->dma_adc.base = pa&0xFFFF; - - pa|=0x00400000; /* bit 22 -> System RAM */ - - M_printk("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n", - ess->apu[channel], pa, bsize, route); - - /* Begin loading the APU */ - for(i=0;i<15;i++) /* clear all PBRs */ - apu_set_register(ess, channel, i, 0x0000); - - apu_set_register(ess, channel, 0, 0x400F); - - /* need to enable subgroups.. and we should probably - have different groups for different /dev/dsps.. */ - apu_set_register(ess, channel, 2, 0x8); - - /* Load the buffer into the wave engine */ - apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); - /* XXX reg is little endian.. */ - apu_set_register(ess, channel, 5, pa&0xFFFF); - apu_set_register(ess, channel, 6, (pa+bsize)&0xFFFF); - apu_set_register(ess, channel, 7, bsize); - - /* clear effects/env.. */ - apu_set_register(ess, channel, 8, 0x00F0); - - /* amplitude now? sure. why not. */ - apu_set_register(ess, channel, 9, 0x0000); - - /* set filter tune, radius, polar pan */ - apu_set_register(ess, channel, 10, 0x8F08); - - /* route input */ - apu_set_register(ess, channel, 11, route); - } - - /* clear WP interrupts */ - outw(1, ess->card->iobase+0x04); - /* enable WP ints */ - outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); - - /* let 'er rip */ - set_adc_rate(ess,rate); - start_adc(ess); -} -/* --------------------------------------------------------------------- */ - -static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count) -{ - M_printk("set_dmaa??\n"); -} - -static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count) -{ - M_printk("set_dmac??\n"); -} - -/* Playback pointer */ -static inline unsigned get_dmaa(struct ess_state *s) -{ - int offset; - - offset = apu_get_register(s,0,5); - -/* M_printk("dmaa: offset: %d, base: %d\n",offset,s->dma_dac.base); */ - - offset-=s->dma_dac.base; - - return (offset&0xFFFE)<<1; /* hardware is in words */ -} - -/* Record pointer */ -static inline unsigned get_dmac(struct ess_state *s) -{ - int offset; - - offset = apu_get_register(s,2,5); - -/* M_printk("dmac: offset: %d, base: %d\n",offset,s->dma_adc.base); */ - - /* The offset is an address not a position relative to base */ - offset-=s->dma_adc.base; - - return (offset&0xFFFE)<<1; /* hardware is in words */ -} - -/* - * Meet Bob, the timer... - */ - -static void ess_interrupt(int irq, void *dev_id, struct pt_regs *regs); - -static void stop_bob(struct ess_state *s) -{ - /* Mask IDR 11,17 */ - maestro_write(s, 0x11, maestro_read(s, 0x11)&~1); - maestro_write(s, 0x17, maestro_read(s, 0x17)&~1); -} - -/* eventually we could be clever and limit bob ints - to the frequency at which our smallest duration - chunks may expire */ -#define ESS_SYSCLK 50000000 -static void start_bob(struct ess_state *s) -{ - int prescale; - int divide; - - /* XXX make freq selector much smarter, see calc_bob_rate */ - int freq = 200; - - /* compute ideal interrupt frequency for buffer size & play rate */ - /* first, find best prescaler value to match freq */ - for(prescale=5;prescale<12;prescale++) - if(freq > (ESS_SYSCLK>>(prescale+9))) - break; - - /* next, back off prescaler whilst getting divider into optimum range */ - divide=1; - while((prescale > 5) && (divide<32)) - { - prescale--; - divide <<=1; - } - divide>>=1; - - /* now fine-tune the divider for best match */ - for(;divide<31;divide++) - if(freq >= ((ESS_SYSCLK>>(prescale+9))/(divide+1))) - break; - - /* divide = 0 is illegal, but don't let prescale = 4! */ - if(divide == 0) - { - divide++; - if(prescale>5) - prescale--; - } - - maestro_write(s, 6, 0x9000 | (prescale<<5) | divide); /* set reg */ - - /* Now set IDR 11/17 */ - maestro_write(s, 0x11, maestro_read(s, 0x11)|1); - maestro_write(s, 0x17, maestro_read(s, 0x17)|1); -} -/* --------------------------------------------------------------------- */ - -/* this quickly calculates the frequency needed for bob - and sets it if its different than what bob is - currently running at. its called often so - needs to be fairly quick. */ -#define BOB_MIN 50 -#define BOB_MAX 400 -static void calc_bob_rate(struct ess_state *s) { -#if 0 /* this thing tries to set the frequency of bob such that - there are 2 interrupts / buffer walked by the dac/adc. That - is probably very wrong for people who actually care about - mid buffer positioning. it should be calculated as bytes/interrupt - and that needs to be decided :) so for now just use the static 150 - in start_bob.*/ - - unsigned int dac_rate=2,adc_rate=1,newrate; - static int israte=-1; - - if (s->dma_dac.fragsize == 0) dac_rate = BOB_MIN; - else { - dac_rate = (2 * s->ratedac * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / - (s->dma_dac.fragsize) ; - } - - if (s->dma_adc.fragsize == 0) adc_rate = BOB_MIN; - else { - adc_rate = (2 * s->rateadc * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / - (s->dma_adc.fragsize) ; - } - - if(dac_rate > adc_rate) newrate = adc_rate; - else newrate=dac_rate; - - if(newrate > BOB_MAX) newrate = BOB_MAX; - else { - if(newrate < BOB_MIN) - newrate = BOB_MIN; - } - - if( israte != newrate) { - printk("dac: %d adc: %d rate: %d\n",dac_rate,adc_rate,israte); - israte=newrate; - } -#endif - -} - -static int -prog_dmabuf(struct ess_state *s, unsigned rec) -{ - struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; - unsigned rate = rec ? s->rateadc : s->ratedac; - unsigned bytepersec; - unsigned bufs; - unsigned char fmt; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - fmt = s->fmt; - if (rec) { - stop_adc(s); - fmt >>= ESS_ADC_SHIFT; - } else { - stop_dac(s); - fmt >>= ESS_DAC_SHIFT; - } - spin_unlock_irqrestore(&s->lock, flags); - fmt &= ESS_FMT_MASK; - - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - - /* this algorithm is a little nuts.. where did /1000 come from? */ - 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--; - 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; - - M_printk("maestro: setup oss: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); - - memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); - - spin_lock_irqsave(&s->lock, flags); - if (rec) - ess_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); - else - ess_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); - - spin_unlock_irqrestore(&s->lock, flags); - db->ready = 1; - - return 0; -} - -static __inline__ void -clear_advance(struct ess_state *s) -{ - unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; - - unsigned char *buf = s->dma_dac.rawbuf; - unsigned bsize = s->dma_dac.dmasize; - unsigned bptr = s->dma_dac.swptr; - unsigned len = s->dma_dac.fragsize; - - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(buf + bptr, c, x); - /* account for wrapping? */ - bptr = 0; - len -= x; - } - memset(buf + bptr, c, len); -} - -/* call with spinlock held! */ -static void -ess_update_ptr(struct ess_state *s) -{ - unsigned hwptr; - int diff; - - /* update ADC pointer */ - if (s->dma_adc.ready) { - /* oh boy should this all be re-written. everything in the current code paths think - that the various counters/pointers are expressed in bytes to the user but we have - two apus doing stereo stuff so we fix it up here.. it propogates to all the various - counters from here. */ - if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { - hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize; - } else { - hwptr = get_dmac(s) % s->dma_adc.dmasize; - } - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - /* FILL ME - wrindir(s, SV_CIENABLE, s->enable); */ - stop_adc(s); - /* brute force everyone back in sync, sigh */ - s->dma_adc.count = 0; - s->dma_adc.swptr = 0; - s->dma_adc.hwptr = 0; - s->dma_adc.error++; - } - } - } - /* update DAC pointer */ - if (s->dma_dac.ready) { - hwptr = get_dmaa(s) % s->dma_dac.dmasize; - /* the apu only reports the length it has seen, not the - length of the memory that has been used (the WP - knows that) */ - if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT)) - hwptr<<=1; - - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; -/* M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/ - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { - wake_up(&s->dma_dac.wait); - } - } else { - s->dma_dac.count -= diff; -/* M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */ - if (s->dma_dac.count <= 0) { - M_printk("underflow! diff: %d count: %d hw: %d sw: %d\n", diff, s->dma_dac.count, - hwptr, s->dma_dac.swptr); - /* FILL ME - wrindir(s, SV_CIENABLE, s->enable); */ - /* XXX how on earth can calling this with the lock held work.. */ - stop_dac(s); - /* brute force everyone back in sync, sigh */ - s->dma_dac.count = 0; - s->dma_dac.swptr = hwptr; - s->dma_dac.error++; - } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { - clear_advance(s); - s->dma_dac.endcleared = 1; - } - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { - wake_up(&s->dma_dac.wait); -/* printk("waking up DAC count: %d sw: %d hw: %d\n",s->dma_dac.count, s->dma_dac.swptr, - hwptr);*/ - } - } - } -} - -static void -ess_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct ess_state *s; - struct ess_card *c = (struct ess_card *)dev_id; - int i; - u32 event; - - if ( ! (event = inb(c->iobase+0x1A)) ) return; - - outw(inw(c->iobase+4)&1, c->iobase+4); - -/* M_printk("maestro int: %x\n",event);*/ - if(event&(1<<6)) - { - int x; - enum {UP_EVT, DOWN_EVT, MUTE_EVT} vol_evt; - int volume; - - /* Figure out which volume control button was pushed, - based on differences from the default register - values. */ - x = inb(c->iobase+0x1c); - if (x&1) vol_evt = MUTE_EVT; - else if (((x>>1)&7) > 4) vol_evt = UP_EVT; - else vol_evt = DOWN_EVT; - - /* Reset the volume control registers. */ - outb(0x88, c->iobase+0x1c); - outb(0x88, c->iobase+0x1d); - outb(0x88, c->iobase+0x1e); - outb(0x88, c->iobase+0x1f); - - /* Deal with the button press in a hammer-handed - manner by adjusting the master mixer volume. */ - volume = c->mix.mixer_state[0] & 0xff; - if (vol_evt == UP_EVT) { - volume += 10; - if (volume > 100) - volume = 100; - } - else if (vol_evt == DOWN_EVT) { - volume -= 10; - if (volume < 0) - volume = 0; - } else { - /* vol_evt == MUTE_EVT */ - if (volume == 0) - volume = c->dock_mute_vol; - else { - c->dock_mute_vol = volume; - volume = 0; - } - } - set_mixer (c, 0, (volume << 8) | volume); - } - - /* Ack all the interrupts. */ - outb(0xFF, c->iobase+0x1A); - - /* - * Update the pointers for all APU's we are running. - */ - for(i=0;ichannels[i]; - if(s->dev_audio == -1) - break; - spin_lock(&s->lock); - ess_update_ptr(s); - spin_unlock(&s->lock); - } -} - - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT "maestro: invalid magic value in %s\n"; - -#define VALIDATE_MAGIC(FOO,MAG) \ -({ \ - if (!(FOO) || (FOO)->magic != MAG) { \ - printk(invalid_magic,__FUNCTION__); \ - return -ENXIO; \ - } \ -}) - -#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,ESS_STATE_MAGIC) -#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,ESS_CARD_MAGIC) - -static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ) -{ - unsigned int left,right; - /* cleanse input a little */ - right = ((val >> 8) & 0xff) ; - left = (val & 0xff) ; - - if(right > 100) right = 100; - if(left > 100) left = 100; - - card->mix.mixer_state[mixer]=(right << 8) | left; - card->mix.write_mixer(card,mixer,left,right); -} - -static void -mixer_push_state(struct ess_card *card) -{ - int i; - for(i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) { - if( ! supported_mixer(card,i)) continue; - - set_mixer(card,i,card->mix.mixer_state[i]); - } -} - -static int mixer_ioctl(struct ess_card *card, unsigned int cmd, unsigned long arg) -{ - int i, val=0; - unsigned long flags; - - VALIDATE_CARD(card); - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - 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; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - 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; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *)arg); - - if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - - if (_IOC_DIR(cmd) == _IOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* give them the current record source */ - - if(!card->mix.recmask_io) { - val = 0; - } else { - spin_lock_irqsave(&card->lock, flags); - val = card->mix.recmask_io(card,1,0); - spin_unlock_irqrestore(&card->lock, flags); - } - break; - - case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ - val = card->mix.supported_mixers; - break; - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - val = card->mix.record_sources; - break; - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - val = card->mix.stereo_mixers; - break; - - case SOUND_MIXER_CAPS: - val = SOUND_CAP_EXCL_INPUT; - break; - - default: /* read a specific mixer */ - i = _IOC_NR(cmd); - - if ( ! supported_mixer(card,i)) - return -EINVAL; - - /* do we ever want to touch the hardware? */ -/* spin_lock_irqsave(&card->lock, flags); - val = card->mix.read_mixer(card,i); - spin_unlock_irqrestore(&card->lock, flags);*/ - - val = card->mix.mixer_state[i]; -/* M_printk("returned 0x%x for mixer %d\n",val,i);*/ - - break; - } - return put_user(val,(int *)arg); - } - - if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ)) - return -EINVAL; - - card->mix.modcnt++; - - if (get_user(val, (int *)arg)) - return -EFAULT; - - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - - if (!card->mix.recmask_io) return -EINVAL; - if(!val) return 0; - if(! (val &= card->mix.record_sources)) return -EINVAL; - - spin_lock_irqsave(&card->lock, flags); - card->mix.recmask_io(card,0,val); - spin_unlock_irqrestore(&card->lock, flags); - return 0; - - default: - i = _IOC_NR(cmd); - - if ( ! supported_mixer(card,i)) - return -EINVAL; - - spin_lock_irqsave(&card->lock, flags); - set_mixer(card,i,val); - spin_unlock_irqrestore(&card->lock, flags); - - return 0; - } -} - -/* --------------------------------------------------------------------- */ -static int ess_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct ess_card *card = NULL; - struct pci_dev *pdev; - struct pci_driver *drvr; - - pci_for_each_dev(pdev) { - drvr = pci_dev_driver (pdev); - if (drvr == &maestro_pci_driver) { - card = (struct ess_card*)pci_get_drvdata (pdev); - if (!card) - continue; - if (card->dev_mixer == minor) - break; - } - } - if (!card) - return -ENODEV; - file->private_data = card; - return 0; -} - -static int ess_release_mixdev(struct inode *inode, struct file *file) -{ - struct ess_card *card = (struct ess_card *)file->private_data; - - VALIDATE_CARD(card); - - return 0; -} - -static int ess_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct ess_card *card = (struct ess_card *)file->private_data; - - VALIDATE_CARD(card); - - return mixer_ioctl(card, cmd, arg); -} - -static /*const*/ struct file_operations ess_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: ess_ioctl_mixdev, - open: ess_open_mixdev, - release: ess_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct ess_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait,current); - unsigned long flags; - int count; - signed long tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; - current->state = TASK_INTERRUPTIBLE; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - /* XXX uhm.. questionable locking*/ - 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) { - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - tmo = (count * HZ) / s->ratedac; - tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; - /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. - or something. who cares. - zach */ - if (!schedule_timeout(tmo ? tmo : 1) && tmo) - M_printk(KERN_DEBUG "maestro: dma timed out?? %ld\n",jiffies); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ -/* Zach sez: "god this is gross.." */ -static int -comb_stereo(unsigned char *real_buffer,unsigned char *tmp_buffer, int offset, - int count, int bufsize) -{ - /* No such thing as stereo recording, so we - use dual input mixers. which means we have to - combine mono to stereo buffer. yuck. - - but we don't have to be able to work a byte at a time..*/ - - unsigned char *so,*left,*right; - int i; - - so = tmp_buffer; - left = real_buffer + offset; - right = real_buffer + bufsize/2 + offset; - -/* M_printk("comb_stereo writing %d to %p from %p and %p, offset: %d size: %d\n",count/2, tmp_buffer,left,right,offset,bufsize);*/ - - for(i=count/4; i ; i--) { - (*(so+2)) = *(right++); - (*(so+3)) = *(right++); - (*so) = *(left++); - (*(so+1)) = *(left++); - so+=4; - } - - return 0; -} - -/* in this loop, dma_adc.count signifies the amount of data thats waiting - to be copied to the user's buffer. it is filled by the interrupt - handler and drained by this loop. */ -static ssize_t -ess_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - unsigned char *combbuf = NULL; - - 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(s, 1))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - if(!(combbuf = kmalloc(count,GFP_KERNEL))) - return -ENOMEM; - ret = 0; - - calc_bob_rate(s); - - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - /* remember, all these things are expressed in bytes to be - sent to the user.. hence the evil / 2 down below */ - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - spin_unlock_irqrestore(&s->lock, flags); - - if (cnt > count) - cnt = count; - - if ( cnt > 0 ) cnt &= ~3; - - if (cnt <= 0) { - start_adc(s); - if (file->f_flags & O_NONBLOCK) - { - ret = ret ? ret : -EAGAIN; - goto rec_return_free; - } - if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { - if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, - s->dma_adc.hwptr, s->dma_adc.swptr); - stop_adc(s); - spin_lock_irqsave(&s->lock, flags); - set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); - /* program enhanced mode registers */ - /* FILL ME */ -/* wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); - wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); */ - s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) - { - ret = ret ? ret : -ERESTARTSYS; - goto rec_return_free; - } - continue; - } - - if(s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { - /* swptr/2 so that we know the real offset in each apu's buffer */ - comb_stereo(s->dma_adc.rawbuf,combbuf,swptr/2,cnt,s->dma_adc.dmasize); - if (copy_to_user(buffer, combbuf, cnt)) { - ret = ret ? ret : -EFAULT; - goto rec_return_free; - } - } else { - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - ret = ret ? ret : -EFAULT; - goto rec_return_free; - } - } - - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - start_adc(s); - } - -rec_return_free: - if(combbuf) kfree(combbuf); - return ret; -} - -static ssize_t -ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - 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(s, 0))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - - calc_bob_rate(s); - - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - swptr = s->dma_dac.swptr; - - cnt = s->dma_dac.dmasize-swptr; - - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - - spin_unlock_irqrestore(&s->lock, flags); - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - start_dac(s); - if (file->f_flags & O_NONBLOCK) { - if(!ret) ret = -EAGAIN; - goto return_free; - } - if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { - if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, - s->dma_dac.hwptr, s->dma_dac.swptr); - stop_dac(s); - spin_lock_irqsave(&s->lock, flags); - set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); - /* program enhanced mode registers */ -/* wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); - wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); */ - /* FILL ME */ - s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) { - if (!ret) ret = -ERESTARTSYS; - goto return_free; - } - continue; - } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) ret = -EFAULT; - goto return_free; - } -/* printk("wrote %d bytes at sw: %d cnt: %d while hw: %d\n",cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr);*/ - - swptr = (swptr + cnt) % s->dma_dac.dmasize; - - spin_lock_irqsave(&s->lock, flags); - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - start_dac(s); - } -return_free: - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - -/* In 0.14 prog_dmabuf always returns success anyway ... */ - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac.ready && prog_dmabuf(s, 0)) - return 0; - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready && prog_dmabuf(s, 1)) - return 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); - ess_update_ptr(s); - 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 ess_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - struct dmabuf *db; - int ret = -EINVAL; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 1)) != 0) - goto out; - db = &s->dma_dac; - } else -#if 0 - /* if we can have the wp/wc do the combining - we can turn this back on. */ - if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 0)) != 0) - goto out; - db = &s->dma_adc; - } else -#endif - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) - goto out; - ret = -EAGAIN; - if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) - goto out; - db->mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret; - unsigned char fmtm, fmtd; - -/* printk("maestro: ess_ioctl: cmd %d\n", cmd);*/ - - 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: - 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: - /* XXX fix */ - 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.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - 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); - s->dma_adc.ready = 0; - set_adc_rate(s, val); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - set_dac_rate(s, val); - } - } - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val) - fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val) - fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val >= 2) - fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val >= 2) - fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) - : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != AFMT_QUERY) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - /* fixed at 16bit for now */ - fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; -#if 0 - if (val == AFMT_S16_LE) - fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); -#endif - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (ESS_FMT_16BIT << ESS_ADC_SHIFT) - : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? - AFMT_S16_LE : - AFMT_U8, - (int *)arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) - val |= PCM_ENABLE_INPUT; - if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) - 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(s, 1))) - 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(s, 0))) - 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 && (ret = prog_dmabuf(s, 0))) - return ret; - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - 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 && (ret = prog_dmabuf(s, 1))) - return ret; - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - 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 && (ret = prog_dmabuf(s, 0))) - return ret; - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - 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 && (ret = prog_dmabuf(s, 1))) - return ret; - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; - 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 && (ret = prog_dmabuf(s, 0))) - return ret; - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - 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(s, 0))) - return val; - return put_user(s->dma_dac.fragsize, (int *)arg); - } - if ((val = prog_dmabuf(s, 1))) - return val; - return put_user(s->dma_adc.fragsize, (int *)arg); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) - return -EFAULT; - M_printk("maestro: SETFRAGMENT: %0x\n",val); - if (file->f_mode & FMODE_READ) { - 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 (file->f_mode & FMODE_WRITE) { - 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; - } - 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) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) - : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); - - case SOUND_PCM_READ_BITS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) - : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - - } - return -EINVAL; -} - -static void -set_base_registers(struct ess_state *s,void *vaddr) -{ - unsigned long packed_phys = virt_to_bus(vaddr)>>12; - wave_set_register(s, 0x01FC , packed_phys); - wave_set_register(s, 0x01FD , packed_phys); - wave_set_register(s, 0x01FE , packed_phys); - wave_set_register(s, 0x01FF , packed_phys); -} - -/* - * this guy makes sure we're in the right power - * state for what we want to be doing - */ -static void maestro_power(struct ess_card *card, int tostate) -{ - u16 active_mask = acpi_state_mask[tostate]; - u8 state; - - if(!use_pm) return; - - pci_read_config_byte(card->pcidev, card->power_regs+0x4, &state); - state&=3; - - /* make sure we're in the right state */ - if(state != tostate) { - M_printk(KERN_WARNING "maestro: dev %02x:%02x.%x switching from D%d to D%d\n", - card->pcidev->bus->number, - PCI_SLOT(card->pcidev->devfn), - PCI_FUNC(card->pcidev->devfn), - state,tostate); - pci_write_config_byte(card->pcidev, card->power_regs+0x4, tostate); - } - - /* and make sure the units we care about are on - XXX we might want to do this before state flipping? */ - pci_write_config_word(card->pcidev, 0x54, ~ active_mask); - pci_write_config_word(card->pcidev, 0x56, ~ active_mask); -} - -/* we allocate a large power of two for all our memory. - this is cut up into (not to scale :): - |silly fifo word | 512byte mixbuf per adc | dac/adc * channels | -*/ -static int -allocate_buffers(struct ess_state *s) -{ - void *rawbuf=NULL; - int order,i; - struct page *page, *pend; - - /* alloc as big a chunk as we can */ - for (order = (dsps_order + (16-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--) - if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order))) - break; - - if (!rawbuf) - return 1; - - M_printk("maestro: allocated %ld (%d) bytes at %p\n",PAGE_SIZE<card->dmapages = rawbuf; - s->card->dmaorder = order; - - for(i=0;icard->channels[i]; - - if(ess->dev_audio == -1) - continue; - - ess->dma_dac.ready = s->dma_dac.mapped = 0; - ess->dma_adc.ready = s->dma_adc.mapped = 0; - ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - dsps_order - 1; - - /* offset dac and adc buffers starting half way through and then at each [da][ad]c's - order's intervals.. */ - ess->dma_dac.rawbuf = rawbuf + (PAGE_SIZE<<(order-1)) + (i * ( PAGE_SIZE << (ess->dma_dac.buforder + 1 ))); - ess->dma_adc.rawbuf = ess->dma_dac.rawbuf + ( PAGE_SIZE << ess->dma_dac.buforder); - /* offset mixbuf by a mixbuf so that the lame status fifo can - happily scribble away.. */ - ess->mixbuf = rawbuf + (512 * (i+1)); - - M_printk("maestro: setup apu %d: dac: %p adc: %p mix: %p\n",i,ess->dma_dac.rawbuf, - ess->dma_adc.rawbuf, ess->mixbuf); - - } - - /* 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++) - mem_map_reserve(page); - - return 0; -} -static void -free_buffers(struct ess_state *s) -{ - struct page *page, *pend; - - s->dma_dac.rawbuf = s->dma_adc.rawbuf = NULL; - s->dma_dac.mapped = s->dma_adc.mapped = 0; - s->dma_dac.ready = s->dma_adc.ready = 0; - - M_printk("maestro: freeing %p\n",s->card->dmapages); - /* undo marking the pages as reserved */ - - pend = virt_to_page(s->card->dmapages + (PAGE_SIZE << s->card->dmaorder) - 1); - for (page = virt_to_page(s->card->dmapages); page <= pend; page++) - mem_map_unreserve(page); - - free_pages((unsigned long)s->card->dmapages,s->card->dmaorder); - s->card->dmapages = NULL; -} - -static int -ess_open(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct ess_state *s = NULL; - unsigned char fmtm = ~0, fmts = 0; - struct pci_dev *pdev; - /* - * Scan the cards and find the channel. We only - * do this at open time so it is ok - */ - - pci_for_each_dev(pdev) { - struct ess_card *c; - struct pci_driver *drvr; - - drvr = pci_dev_driver (pdev); - if (drvr == &maestro_pci_driver) { - int i; - struct ess_state *sp; - - c = (struct ess_card*)pci_get_drvdata (pdev); - if (!c) - continue; - for(i=0;ichannels[i]; - if(sp->dev_audio < 0) - continue; - if((sp->dev_audio ^ minor) & ~0xf) - continue; - s=sp; - } - } - } - if (!s) - return -ENODEV; - - VALIDATE_STATE(s); - 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 -EWOULDBLOCK; - } - up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - down(&s->open_sem); - } - - /* under semaphore.. */ - if ((s->card->dmapages==NULL) && allocate_buffers(s)) { - up(&s->open_sem); - return -ENOMEM; - } - - /* we're covered by the open_sem */ - if( ! s->card->dsps_open ) { - maestro_power(s->card,ACPI_D0); - start_bob(s); - } - s->card->dsps_open++; - M_printk("maestro: open, %d bobs now\n",s->card->dsps_open); - - /* ok, lets write WC base regs now that we've - powered up the chip */ - M_printk("maestro: writing 0x%lx (bus 0x%lx) to the wp\n",virt_to_bus(s->card->dmapages), - ((virt_to_bus(s->card->dmapages))&0xFFE00000)>>12); - set_base_registers(s,s->card->dmapages); - - if (file->f_mode & FMODE_READ) { -/* - fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; */ - - fmtm &= ~((ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT); - fmts = (ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT; - - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - set_adc_rate(s, 8000); - } - if (file->f_mode & FMODE_WRITE) { - fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; - - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; - set_dac_rate(s, 8000); - } - set_fmt(s, fmtm, fmts); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - - up(&s->open_sem); - return 0; -} - -static int -ess_release(struct inode *inode, struct file *file) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - - VALIDATE_STATE(s); - 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); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - } - - s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - /* we're covered by the open_sem */ - M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1); - if( --s->card->dsps_open <= 0) { - s->card->dsps_open = 0; - stop_bob(s); - free_buffers(s); - maestro_power(s->card,ACPI_D2); - } - up(&s->open_sem); - wake_up(&s->open_wait); - unlock_kernel(); - return 0; -} - -static struct file_operations ess_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: ess_read, - write: ess_write, - poll: ess_poll, - ioctl: ess_ioctl, - mmap: ess_mmap, - open: ess_open, - release: ess_release, -}; - -static int -maestro_config(struct ess_card *card) -{ - struct pci_dev *pcidev = card->pcidev; - struct ess_state *ess = &card->channels[0]; - int apu,iobase = card->iobase; - u16 w; - u32 n; - - /* We used to muck around with pci config space that - * we had no business messing with. We don't know enough - * about the machine to know which DMA mode is appropriate, - * etc. We were guessing wrong on some machines and making - * them unhappy. We now trust in the BIOS to do things right, - * which almost certainly means a new host of problems will - * arise with broken BIOS implementations. screw 'em. - * We're already intolerant of machines that don't assign - * IRQs. - */ - - /* do config work at full power */ - maestro_power(card,ACPI_D0); - - pci_read_config_word(pcidev, 0x50, &w); - - w&=~(1<<5); /* Don't swap left/right (undoc)*/ - - pci_write_config_word(pcidev, 0x50, w); - - pci_read_config_word(pcidev, 0x52, &w); - w&=~(1<<15); /* Turn off internal clock multiplier */ - /* XXX how do we know which to use? */ - w&=~(1<<14); /* External clock */ - - w|= (1<<7); /* Hardware volume control on */ - w|= (1<<6); /* Debounce off: easier to push the HWV buttons. */ - w&=~(1<<5); /* GPIO 4:5 */ - w|= (1<<4); /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ - w&=~(1<<2); /* MIDI fix off (undoc) */ - w&=~(1<<1); /* reserved, always write 0 */ - pci_write_config_word(pcidev, 0x52, w); - - /* - * Legacy mode - */ - - pci_read_config_word(pcidev, 0x40, &w); - w|=(1<<15); /* legacy decode off */ - w&=~(1<<14); /* Disable SIRQ */ - w&=~(0x1f); /* disable mpu irq/io, game port, fm, SB */ - - pci_write_config_word(pcidev, 0x40, w); - - /* Set up 978 docking control chip. */ - pci_read_config_word(pcidev, 0x58, &w); - w|=1<<2; /* Enable 978. */ - w|=1<<3; /* Turn on 978 hardware volume control. */ - w&=~(1<<11); /* Turn on 978 mixer volume control. */ - pci_write_config_word(pcidev, 0x58, w); - - sound_reset(iobase); - - /* - * Ring Bus Setup - */ - - /* setup usual 0x34 stuff.. 0x36 may be chip specific */ - outw(0xC090, iobase+0x34); /* direct sound, stereo */ - udelay(20); - outw(0x3000, iobase+0x36); /* direct sound, stereo */ - udelay(20); - - - /* - * Reset the CODEC - */ - - maestro_ac97_reset(iobase,pcidev); - - /* - * Ring Bus Setup - */ - - n=inl(iobase+0x34); - n&=~0xF000; - n|=12<<12; /* Direct Sound, Stereo */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n&=~0x0F00; /* Modem off */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n&=~0x00F0; - n|=9<<4; /* DAC, Stereo */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n&=~0x000F; /* ASSP off */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n|=(1<<29); /* Enable ring bus */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n|=(1<<28); /* Enable serial bus */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n&=~0x00F00000; /* MIC off */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n&=~0x000F0000; /* I2S off */ - outl(n, iobase+0x34); - - - w=inw(iobase+0x18); - w&=~(1<<7); /* ClkRun off */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w&=~(1<<6); /* Hardware volume control interrupt off... for now. */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w&=~(1<<4); /* ASSP irq off */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w&=~(1<<3); /* ISDN irq off */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w|=(1<<2); /* Direct Sound IRQ on */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w&=~(1<<1); /* MPU401 IRQ off */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w|=(1<<0); /* SB IRQ on */ - outw(w, iobase+0x18); - - /* Set hardware volume control registers to midpoints. - We can tell which button was pushed based on how they change. */ - outb(0x88, iobase+0x1c); - outb(0x88, iobase+0x1d); - outb(0x88, iobase+0x1e); - outb(0x88, iobase+0x1f); - - /* it appears some maestros (dell 7500) only work if these are set, - regardless of wether we use the assp or not. */ - - outb(0, iobase+0xA4); - outb(3, iobase+0xA2); - outb(0, iobase+0xA6); - - for(apu=0;apu<16;apu++) - { - /* Write 0 into the buffer area 0x1E0->1EF */ - outw(0x01E0+apu, 0x10+iobase); - outw(0x0000, 0x12+iobase); - - /* - * The 1.10 test program seem to write 0 into the buffer area - * 0x1D0-0x1DF too. - */ - outw(0x01D0+apu, 0x10+iobase); - outw(0x0000, 0x12+iobase); - } - -#if 1 - wave_set_register(ess, IDR7_WAVE_ROMRAM, - (wave_get_register(ess, IDR7_WAVE_ROMRAM)&0xFF00)); - wave_set_register(ess, IDR7_WAVE_ROMRAM, - wave_get_register(ess, IDR7_WAVE_ROMRAM)|0x100); - wave_set_register(ess, IDR7_WAVE_ROMRAM, - wave_get_register(ess, IDR7_WAVE_ROMRAM)&~0x200); - wave_set_register(ess, IDR7_WAVE_ROMRAM, - wave_get_register(ess, IDR7_WAVE_ROMRAM)|~0x400); -#else - maestro_write(ess, IDR7_WAVE_ROMRAM, - (maestro_read(ess, IDR7_WAVE_ROMRAM)&0xFF00)); - maestro_write(ess, IDR7_WAVE_ROMRAM, - maestro_read(ess, IDR7_WAVE_ROMRAM)|0x100); - maestro_write(ess, IDR7_WAVE_ROMRAM, - maestro_read(ess, IDR7_WAVE_ROMRAM)&~0x200); - maestro_write(ess, IDR7_WAVE_ROMRAM, - maestro_read(ess, IDR7_WAVE_ROMRAM)|0x400); -#endif - - maestro_write(ess, IDR2_CRAM_DATA, 0x0000); - maestro_write(ess, 0x08, 0xB004); - /* Now back to the DirectSound stuff */ - maestro_write(ess, 0x09, 0x001B); - maestro_write(ess, 0x0A, 0x8000); - maestro_write(ess, 0x0B, 0x3F37); - maestro_write(ess, 0x0C, 0x0098); - - /* parallel out ?? */ - maestro_write(ess, 0x0C, - (maestro_read(ess, 0x0C)&~0xF000)|0x8000); - /* parallel in, has something to do with recording :) */ - maestro_write(ess, 0x0C, - (maestro_read(ess, 0x0C)&~0x0F00)|0x0500); - - maestro_write(ess, 0x0D, 0x7632); - - /* Wave cache control on - test off, sg off, - enable, enable extra chans 1Mb */ - - outw(inw(0x14+iobase)|(1<<8),0x14+iobase); - outw(inw(0x14+iobase)&0xFE03,0x14+iobase); - outw((inw(0x14+iobase)&0xFFFC), 0x14+iobase); - outw(inw(0x14+iobase)|(1<<7),0x14+iobase); - - outw(0xA1A0, 0x14+iobase); /* 0300 ? */ - - /* Now clear the APU control ram */ - for(apu=0;apupower_regs = 0; - - /* check to see if we have a capabilities list in - the config register */ - pci_read_config_word(pcidev, PCI_STATUS, &w); - if(! w & PCI_STATUS_CAP_LIST) return 0; - - /* walk the list, starting at the head. */ - pci_read_config_byte(pcidev,PCI_CAPABILITY_LIST,&next); - - while(next && max--) { - pci_read_config_dword(pcidev, next & ~3, &n); - if((n & 0xff) == PCI_CAP_ID_PM) { - card->power_regs = next; - break; - } - next = ((n>>8) & 0xff); - } - - return card->power_regs ? 1 : 0; -} - -static int __init -maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid) -{ - int card_type = pdid->driver_data; - u32 n; - int iobase; - int i, ret; - struct ess_card *card; - struct ess_state *ess; - struct pm_dev *pmdev; - int num = 0; - -/* when built into the kernel, we only print version if device is found */ -#ifndef MODULE - static int printed_version; - if (!printed_version++) - printk(version); -#endif - - /* don't pick up weird modem maestros */ - if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) - return -ENODEV; - - - if ((ret=pci_enable_device(pcidev))) - return ret; - - iobase = pci_resource_start(pcidev,0); - if (!iobase || !(pci_resource_flags(pcidev, 0 ) & IORESOURCE_IO)) - return -ENODEV; - - if(pcidev->irq == 0) - return -ENODEV; - - /* stake our claim on the iospace */ - if( request_region(iobase, 256, card_names[card_type]) == NULL ) - { - printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase); - return -EBUSY; - } - - /* just to be sure */ - pci_set_master(pcidev); - - card = kmalloc(sizeof(struct ess_card), GFP_KERNEL); - if(card == NULL) - { - printk(KERN_WARNING "maestro: out of memory\n"); - release_region(iobase, 256); - return -ENOMEM; - } - - memset(card, 0, sizeof(*card)); - card->pcidev = pcidev; - - pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), - maestro_pm_callback); - if (pmdev) - pmdev->data = card; - - card->iobase = iobase; - card->card_type = card_type; - card->irq = pcidev->irq; - card->magic = ESS_CARD_MAGIC; - spin_lock_init(&card->lock); - init_waitqueue_head(&card->suspend_queue); - - card->dock_mute_vol = 50; - - /* init our groups of 6 apus */ - for(i=0;ichannels[i]; - - s->index = i; - - s->card = card; - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - spin_lock_init(&s->lock); - init_MUTEX(&s->open_sem); - s->magic = ESS_STATE_MAGIC; - - s->apu[0] = 6*i; - s->apu[1] = (6*i)+1; - s->apu[2] = (6*i)+2; - s->apu[3] = (6*i)+3; - s->apu[4] = (6*i)+4; - s->apu[5] = (6*i)+5; - - if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) - printk("maestro: BOTCH!\n"); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0) - break; - } - - num = i; - - /* clear the rest if we ran out of slots to register */ - for(;ichannels[i]; - s->dev_audio = -1; - } - - ess = &card->channels[0]; - - /* - * Ok card ready. Begin setup proper - */ - - printk(KERN_INFO "maestro: Configuring %s found at IO 0x%04X IRQ %d\n", - card_names[card_type],iobase,card->irq); - pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n); - printk(KERN_INFO "maestro: subvendor id: 0x%08x\n",n); - - /* turn off power management unless: - * - the user explicitly asks for it - * or - * - we're not a 2e, lesser chipps seem to have problems. - * - we're not on our _very_ small whitelist. some implemenetations - * really dont' like the pm code, others require it. - * feel free to expand this as required. - */ -#define SUBSYSTEM_VENDOR(x) (x&0xffff) - if( (use_pm != 1) && - ((card_type != TYPE_MAESTRO2E) || (SUBSYSTEM_VENDOR(n) != 0x1028))) - use_pm = 0; - - if(!use_pm) - printk(KERN_INFO "maestro: not attempting power management.\n"); - else { - if(!parse_power(card,pcidev)) - printk(KERN_INFO "maestro: no PCI power management interface found.\n"); - else { - pci_read_config_dword(pcidev, card->power_regs, &n); - printk(KERN_INFO "maestro: PCI power management capability: 0x%x\n",n>>16); - } - } - - maestro_config(card); - - if(maestro_ac97_get(card, 0x00)==0x0080) { - printk(KERN_ERR "maestro: my goodness! you seem to have a pt101 codec, which is quite rare.\n" - "\tyou should tell someone about this.\n"); - } else { - maestro_ac97_init(card); - } - - if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) { - printk("maestro: couldn't register mixer!\n"); - } else { - memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state)); - mixer_push_state(card); - } - - if((ret=request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card))) - { - printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq); - unregister_sound_mixer(card->dev_mixer); - for(i=0;ichannels[i]; - if(s->dev_audio != -1) - unregister_sound_dsp(s->dev_audio); - } - release_region(card->iobase, 256); - unregister_reboot_notifier(&maestro_nb); - kfree(card); - return ret; - } - - /* Turn on hardware volume control interrupt. - This has to come after we grab the IRQ above, - or a crash will result on installation if a button has been pressed, - because in that case we'll get an immediate interrupt. */ - n = inw(iobase+0x18); - n|=(1<<6); - outw(n, iobase+0x18); - - pci_set_drvdata(pcidev,card); - /* now go to sleep 'till something interesting happens */ - maestro_power(card,ACPI_D2); - - printk(KERN_INFO "maestro: %d channels configured.\n", num); - return 0; -} - -static void maestro_remove(struct pci_dev *pcidev) { - struct ess_card *card = pci_get_drvdata(pcidev); - int i; - - /* XXX maybe should force stop bob, but should be all - stopped by _release by now */ - free_irq(card->irq, card); - unregister_sound_mixer(card->dev_mixer); - for(i=0;ichannels[i]; - if(ess->dev_audio != -1) - unregister_sound_dsp(ess->dev_audio); - } - /* Goodbye, Mr. Bond. */ - maestro_power(card,ACPI_D3); - release_region(card->iobase, 256); - kfree(card); - pci_set_drvdata(pcidev,NULL); -} - -static struct pci_device_id maestro_pci_tbl[] __devinitdata = { - {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2}, - {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2E}, - {PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO}, - {0,} -}; -MODULE_DEVICE_TABLE(pci, maestro_pci_tbl); - -static struct pci_driver maestro_pci_driver = { - name:"maestro", - id_table:maestro_pci_tbl, - probe:maestro_probe, - remove:maestro_remove, -}; - -int __init init_maestro(void) -{ - int rc; - - rc = pci_module_init(&maestro_pci_driver); - if (rc < 0) - return rc; - - if (register_reboot_notifier(&maestro_nb)) - printk(KERN_WARNING "maestro: reboot notifier registration failed; may not reboot properly.\n"); -#ifdef MODULE - printk(version); -#endif - if (dsps_order < 0) { - dsps_order = 1; - printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); - } - else if (dsps_order > MAX_DSP_ORDER) { - dsps_order = MAX_DSP_ORDER; - printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); - } - return 0; -} - -static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf) -{ - /* this notifier is called when the kernel is really shut down. */ - M_printk("maestro: shutting down\n"); - /* this will remove all card instances too */ - pci_unregister_driver(&maestro_pci_driver); - /* XXX dunno about power management */ - return NOTIFY_OK; -} - -/* --------------------------------------------------------------------- */ - - -void cleanup_maestro(void) { - M_printk("maestro: unloading\n"); - pci_unregister_driver(&maestro_pci_driver); - pm_unregister_all(maestro_pm_callback); - unregister_reboot_notifier(&maestro_nb); -} - -/* --------------------------------------------------------------------- */ - -void -check_suspend(struct ess_card *card) -{ - DECLARE_WAITQUEUE(wait, current); - - if(!card->in_suspend) return; - - card->in_suspend++; - add_wait_queue(&(card->suspend_queue), &wait); - current->state = TASK_UNINTERRUPTIBLE; - schedule(); - remove_wait_queue(&(card->suspend_queue), &wait); - current->state = TASK_RUNNING; -} - -static int -maestro_suspend(struct ess_card *card) -{ - unsigned long flags; - int i,j; - - save_flags(flags); - cli(); /* over-kill */ - - M_printk("maestro: apm in dev %p\n",card); - - /* we have to read from the apu regs, need - to power it up */ - maestro_power(card,ACPI_D0); - - for(i=0;ichannels[i]; - - if(s->dev_audio == -1) - continue; - - M_printk("maestro: stopping apus for device %d\n",i); - stop_dac(s); - stop_adc(s); - for(j=0;j<6;j++) - card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5); - - } - - /* get rid of interrupts? */ - if( card->dsps_open > 0) - stop_bob(&card->channels[0]); - - card->in_suspend++; - - restore_flags(flags); - - /* we trust in the bios to power down the chip on suspend. - * XXX I'm also not sure that in_suspend will protect - * against all reg accesses from here on out. - */ - return 0; -} -static int -maestro_resume(struct ess_card *card) -{ - unsigned long flags; - int i; - - save_flags(flags); - cli(); /* over-kill */ - - card->in_suspend = 0; - - M_printk("maestro: resuming card at %p\n",card); - - /* restore all our config */ - maestro_config(card); - /* need to restore the base pointers.. */ - if(card->dmapages) - set_base_registers(&card->channels[0],card->dmapages); - - mixer_push_state(card); - - /* set each channels' apu control registers before - * restoring audio - */ - for(i=0;ichannels[i]; - int chan,reg; - - if(s->dev_audio == -1) - continue; - - for(chan = 0 ; chan < 6 ; chan++) { - wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]); - for(reg = 1 ; reg < NR_APU_REGS ; reg++) - apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]); - } - for(chan = 0 ; chan < 6 ; chan++) - apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F); - } - - /* now we flip on the music */ - - if( card->dsps_open <= 0) { - /* this card's idle */ - maestro_power(card,ACPI_D2); - } else { - /* ok, we're actually playing things on - this card */ - maestro_power(card,ACPI_D0); - start_bob(&card->channels[0]); - for(i=0;ichannels[i]; - - /* these use the apu_mode, and can handle - spurious calls */ - start_dac(s); - start_adc(s); - } - } - - restore_flags(flags); - - /* all right, we think things are ready, - wake up people who were using the device - when we suspended */ - wake_up(&(card->suspend_queue)); - - return 0; -} - -int -maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - struct ess_card *card = (struct ess_card*) dev->data; - - if ( ! card ) goto out; - - M_printk("maestro: pm event 0x%x received for card %p\n", rqst, card); - - switch (rqst) { - case PM_SUSPEND: - maestro_suspend(card); - break; - case PM_RESUME: - maestro_resume(card); - break; - /* - * we'd also like to find out about - * power level changes because some biosen - * do mean things to the maestro when they - * change their power state. - */ - } -out: - return 0; -} - -module_init(init_maestro); -module_exit(cleanup_maestro); diff -Nru a/drivers/sound/maestro.h b/drivers/sound/maestro.h --- a/drivers/sound/maestro.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,60 +0,0 @@ -/* - * Registers for the ESS PCI cards - */ - -/* - * Memory access - */ - -#define ESS_MEM_DATA 0x00 -#define ESS_MEM_INDEX 0x02 - -/* - * AC-97 Codec port. Delay 1uS after each write. This is used to - * talk AC-97 (see intel.com). Write data then register. - */ - -#define ESS_AC97_INDEX 0x30 /* byte wide */ -#define ESS_AC97_DATA 0x32 - -/* - * Reading is a bit different. You write register|0x80 to ubdex - * delay 1uS poll the low bit of index, when it clears read the - * data value. - */ - -/* - * Control port. Not yet fully understood - * The value 0xC090 gets loaded to it then 0x0000 and 0x2800 - * to the data port. Then after 4uS the value 0x300 is written - */ - -#define RING_BUS_CTRL_L 0x34 -#define RING_BUS_CTRL_H 0x36 - -/* - * This is also used during setup. The value 0x17 is written to it - */ - -#define ESS_SETUP_18 0x18 - -/* - * And this one gets 0x000b - */ - -#define ESS_SETUP_A2 0xA2 - -/* - * And this 0x0000 - */ - -#define ESS_SETUP_A4 0xA4 -#define ESS_SETUP_A6 0xA6 - -/* - * Stuff to do with Harpo - the wave stuff - */ - -#define ESS_WAVETABLE_SIZE 0x14 -#define ESS_WAVETABLE_2M 0xA180 - diff -Nru a/drivers/sound/maestro3.c b/drivers/sound/maestro3.c --- a/drivers/sound/maestro3.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2962 +0,0 @@ -/***************************************************************************** - * - * ESS Maestro3/Allegro driver for Linux 2.4.x - * - * 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. - * - * (c) Copyright 2000 Zach Brown - * - * I need to thank many people for helping make this driver happen. - * As always, Eric Brombaugh was a hacking machine and killed many bugs - * that I was too dumb to notice. Howard Kim at ESS provided reference boards - * and as much docs as he could. Todd and Mick at Dell tested snapshots on - * an army of laptops. msw and deviant at Red Hat also humoured me by hanging - * their laptops every few hours in the name of science. - * - * Shouts go out to Mike "DJ XPCom" Ang. - * - * History - * v1.22 - Feb 28 2001 - Zach Brown - * allocate mem at insmod/setup, rather than open - * limit pci dma addresses to 28bit, thanks guys. - * v1.21 - Feb 04 2001 - Zach Brown - * fix up really dumb notifier -> suspend oops - * v1.20 - Jan 30 2001 - Zach Brown - * get rid of pm callback and use pci_dev suspend/resume instead - * m3_probe cleanups, including pm oops think-o - * v1.10 - Jan 6 2001 - Zach Brown - * revert to lame remap_page_range mmap() just to make it work - * record mmap fixed. - * fix up incredibly broken open/release resource management - * duh. fix record format setting. - * add SMP locking and cleanup formatting here and there - * v1.00 - Dec 16 2000 - Zach Brown - * port to sexy 2.4 interfaces - * properly align instance allocations so recording works - * clean up function namespace a little :/ - * update PCI IDs based on mail from ESS - * arbitrarily bump version number to show its 2.4 now, - * 2.2 will stay 0., oss_audio port gets 2. - * v0.03 - Nov 05 2000 - Zach Brown - * disable recording but allow dsp to be opened read - * pull out most silly compat defines - * v0.02 - Nov 04 2000 - Zach Brown - * changed clocking setup for m3, slowdown fixed. - * codec reset is hopefully reliable now - * rudimentary apm/power management makes suspend/resume work - * v0.01 - Oct 31 2000 - Zach Brown - * first release - * v0.00 - Sep 09 2000 - Zach Brown - * first pass derivation from maestro.c - * - * TODO - * in/out allocated contiguously so fullduplex mmap will work? - * no beep on init (mute) - * resetup msrc data memory if freq changes? - * - * -- - * - * Allow me to ramble a bit about the m3 architecture. The core of the - * chip is the 'assp', the custom ESS dsp that runs the show. It has - * a small amount of code and data ram. ESS drops binary dsp code images - * on our heads, but we don't get to see specs on the dsp. - * - * The constant piece of code on the dsp is the 'kernel'. It also has a - * chunk of the dsp memory that is statically set aside for its control - * info. This is the KDATA defines in maestro3.h. Part of its core - * data is a list of code addresses that point to the pieces of DSP code - * that it should walk through in its loop. These other pieces of code - * do the real work. The kernel presumably jumps into each of them in turn. - * These code images tend to have their own data area, and one can have - * multiple data areas representing different states for each of the 'client - * instance' code portions. There is generally a list in the kernel data - * that points to the data instances for a given piece of code. - * - * We've only been given the binary image for the 'minisrc', mini sample - * rate converter. This is rather annoying because it limits the work - * we can do on the dsp, but it also greatly simplifies the job of managing - * dsp data memory for the code and data for our playing streams :). We - * statically allocate the minisrc code into a region we 'know' to be free - * based on the map of the binary kernel image we're loading. We also - * statically allocate the data areas for the maximum number of pcm streams - * we can be dealing with. This max is set by the length of the static list - * in the kernel data that records the number of minisrc data regions we - * can have. Thats right, all software dsp mixing with static code list - * limits. Rock. - * - * How sound goes in and out is still a relative mystery. It appears - * that the dsp has the ability to get input and output through various - * 'connections'. To do IO from or to a connection, you put the address - * of the minisrc client area in the static kernel data lists for that - * input or output. so for pcm -> dsp -> mixer, we put the minisrc data - * instance in the DMA list and also in the list for the mixer. I guess - * it Just Knows which is in/out, and we give some dma control info that - * helps. There are all sorts of cool inputs/outputs that it seems we can't - * use without dsp code images that know how to use them. - * - * So at init time we preload all the memory allocation stuff and set some - * system wide parameters. When we really get a sound to play we build - * up its minisrc header (stream parameters, buffer addresses, input/output - * settings). Then we throw its header on the various lists. We also - * tickle some KDATA settings that ask the assp to raise clock interrupts - * and do some amount of software mixing before handing data to the ac97. - * - * Sorry for the vague details. Feel free to ask Eric or myself if you - * happen to be trying to use this driver elsewhere. Please accept my - * apologies for the quality of the OSS support code, its passed through - * too many hands now and desperately wants to be rethought. - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - /* - * for crizappy mmap() - */ -#include - -#include "maestro3.h" - -#define M_DEBUG 1 - -#define DRIVER_VERSION "1.22" -#define M3_MODULE_NAME "maestro3" -#define PFX M3_MODULE_NAME ": " - -#define M3_STATE_MAGIC 0x734d724d -#define M3_CARD_MAGIC 0x646e6f50 - -#define ESS_FMT_STEREO 0x01 -#define ESS_FMT_16BIT 0x02 -#define ESS_FMT_MASK 0x03 -#define ESS_DAC_SHIFT 0 -#define ESS_ADC_SHIFT 4 - -#define DAC_RUNNING 1 -#define ADC_RUNNING 2 - -#define SND_DEV_DSP16 5 - -#ifdef M_DEBUG -static int debug; -#define DPMOD 1 /* per module load */ -#define DPSTR 2 /* per 'stream' */ -#define DPSYS 3 /* per syscall */ -#define DPCRAP 4 /* stuff the user shouldn't see unless they're really debuggin */ -#define DPINT 5 /* per interrupt, LOTS */ -#define DPRINTK(DP, args...) {if (debug >= (DP)) printk(KERN_DEBUG PFX args);} -#else -#define DPRINTK(x) -#endif - -struct m3_list { - int curlen; - u16 mem_addr; - int max; -}; - -int external_amp = 1; - -struct m3_state { - unsigned int magic; - struct m3_card *card; - unsigned char fmt, enable; - - int index; - - /* this locks around the oss state in the driver */ - spinlock_t lock; - - struct semaphore open_sem; - wait_queue_head_t open_wait; - mode_t open_mode; - - int dev_audio; - - struct assp_instance { - u16 code, data; - } dac_inst, adc_inst; - - /* should be in dmabuf */ - unsigned int rateadc, ratedac; - - struct dmabuf { - void *rawbuf; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - 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 endcleared:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - /* new in m3 */ - int mixer_index, dma_index, msrc_index, adc1_index; - int in_lists; - /* 2.4.. */ - dma_addr_t handle; - - } dma_dac, dma_adc; -}; - -struct m3_card { - unsigned int magic; - - struct m3_card *next; - - struct ac97_codec *ac97; - spinlock_t ac97_lock; - - int card_type; - -#define NR_DSPS 1 -#define MAX_DSPS NR_DSPS - struct m3_state channels[MAX_DSPS]; - - /* this locks around the physical registers on the card */ - spinlock_t lock; - - /* hardware resources */ - struct pci_dev *pcidev; - u32 iobase; - u32 irq; - - int dacs_active; - - int timer_users; - - struct m3_list msrc_list, - mixer_list, - adc1_list, - dma_list; - - /* for storing reset state..*/ - u8 reset_state; - - u16 *suspend_mem; - int in_suspend; - wait_queue_head_t suspend_queue; -}; - -/* - * an arbitrary volume we set the internal - * volume settings to so that the ac97 volume - * range is a little less insane. 0x7fff is - * max. - */ -#define ARB_VOLUME ( 0x6800 ) - -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -enum { - ESS_ALLEGRO, - ESS_MAESTRO3, - /* - * a maestro3 with 'hardware strapping', only - * found inside ESS? - */ - ESS_MAESTRO3HW, -}; - -static char *card_names[] = { - [ESS_ALLEGRO] = "Allegro", - [ESS_MAESTRO3] = "Maestro3(i)", - [ESS_MAESTRO3HW] = "Maestro3(i)hw" -}; - -#ifndef PCI_VENDOR_ESS -#define PCI_VENDOR_ESS 0x125D -#endif - -#define M3_DEVICE(DEV, TYPE) \ -{ \ -vendor: PCI_VENDOR_ESS, \ -device: DEV, \ -subvendor: PCI_ANY_ID, \ -subdevice: PCI_ANY_ID, \ -class: PCI_CLASS_MULTIMEDIA_AUDIO << 8, \ -class_mask: 0xffff << 8, \ -driver_data: TYPE, \ -} - -static struct pci_device_id m3_id_table[] __initdata = { - M3_DEVICE(0x1988, ESS_ALLEGRO), - M3_DEVICE(0x1998, ESS_MAESTRO3), - M3_DEVICE(0x199a, ESS_MAESTRO3HW), - {0,} -}; - -MODULE_DEVICE_TABLE (pci, m3_id_table); - -/* - * reports seem to indicate that the m3 is limited - * to 28bit bus addresses. aaaargggh... - */ -#define M3_PCI_DMA_MASK 0x0fffffff - -static 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; -} - -static struct m3_card *devs; - -/* - * I'm not very good at laying out functions in a file :) - */ -static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf); -static int m3_suspend(struct pci_dev *pci_dev, u32 state); -static void check_suspend(struct m3_card *card); - -struct notifier_block m3_reboot_nb = {m3_notifier, NULL, 0}; - -static void m3_outw(struct m3_card *card, - u16 value, unsigned long reg) -{ - check_suspend(card); - outw(value, card->iobase + reg); -} - -static u16 m3_inw(struct m3_card *card, unsigned long reg) -{ - check_suspend(card); - return inw(card->iobase + reg); -} -static void m3_outb(struct m3_card *card, - u8 value, unsigned long reg) -{ - check_suspend(card); - outb(value, card->iobase + reg); -} -static u8 m3_inb(struct m3_card *card, unsigned long reg) -{ - check_suspend(card); - return inb(card->iobase + reg); -} - -/* - * access 16bit words to the code or data regions of the dsp's memory. - * index addresses 16bit words. - */ -static u16 __m3_assp_read(struct m3_card *card, u16 region, u16 index) -{ - m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); - m3_outw(card, index, DSP_PORT_MEMORY_INDEX); - return m3_inw(card, DSP_PORT_MEMORY_DATA); -} -static u16 m3_assp_read(struct m3_card *card, u16 region, u16 index) -{ - unsigned long flags; - u16 ret; - - spin_lock_irqsave(&(card->lock), flags); - ret = __m3_assp_read(card, region, index); - spin_unlock_irqrestore(&(card->lock), flags); - - return ret; -} - -static void __m3_assp_write(struct m3_card *card, - u16 region, u16 index, u16 data) -{ - m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); - m3_outw(card, index, DSP_PORT_MEMORY_INDEX); - m3_outw(card, data, DSP_PORT_MEMORY_DATA); -} -static void m3_assp_write(struct m3_card *card, - u16 region, u16 index, u16 data) -{ - unsigned long flags; - - spin_lock_irqsave(&(card->lock), flags); - __m3_assp_write(card, region, index, data); - spin_unlock_irqrestore(&(card->lock), flags); -} - -static void m3_assp_halt(struct m3_card *card) -{ - card->reset_state = m3_inb(card, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK; - mdelay(10); - m3_outb(card, card->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); -} - -static void m3_assp_continue(struct m3_card *card) -{ - m3_outb(card, card->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); -} - -/* - * This makes me sad. the maestro3 has lists - * internally that must be packed.. 0 terminates, - * apparently, or maybe all unused entries have - * to be 0, the lists have static lengths set - * by the binary code images. - */ - -static int m3_add_list(struct m3_card *card, - struct m3_list *list, u16 val) -{ - DPRINTK(DPSTR, "adding val 0x%x to list 0x%p at pos %d\n", - val, list, list->curlen); - - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - list->mem_addr + list->curlen, - val); - - return list->curlen++; - -} - -static void m3_remove_list(struct m3_card *card, - struct m3_list *list, int index) -{ - u16 val; - int lastindex = list->curlen - 1; - - DPRINTK(DPSTR, "removing ind %d from list 0x%p\n", - index, list); - - if(index != lastindex) { - val = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, - list->mem_addr + lastindex); - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - list->mem_addr + index, - val); - } - - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - list->mem_addr + lastindex, - 0); - - list->curlen--; -} - -static void set_fmt(struct m3_state *s, unsigned char mask, unsigned char data) -{ - int tmp; - - s->fmt = (s->fmt & mask) | data; - - tmp = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; - - /* write to 'mono' word */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + SRC3_DIRECTION_OFFSET + 1, - (tmp & ESS_FMT_STEREO) ? 0 : 1); - /* write to '8bit' word */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + SRC3_DIRECTION_OFFSET + 2, - (tmp & ESS_FMT_16BIT) ? 0 : 1); - - tmp = (s->fmt >> ESS_ADC_SHIFT) & ESS_FMT_MASK; - - /* write to 'mono' word */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + SRC3_DIRECTION_OFFSET + 1, - (tmp & ESS_FMT_STEREO) ? 0 : 1); - /* write to '8bit' word */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + SRC3_DIRECTION_OFFSET + 2, - (tmp & ESS_FMT_16BIT) ? 0 : 1); -} - -static void set_dac_rate(struct m3_state *s, unsigned int rate) -{ - u32 freq; - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - - s->ratedac = rate; - - freq = ((rate << 15) + 24000 ) / 48000; - if(freq) - freq--; - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_FREQUENCY, - freq); -} - -static void set_adc_rate(struct m3_state *s, unsigned int rate) -{ - u32 freq; - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - - s->rateadc = rate; - - freq = ((rate << 15) + 24000 ) / 48000; - if(freq) - freq--; - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_FREQUENCY, - freq); -} - -static void inc_timer_users(struct m3_card *card) -{ - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - - card->timer_users++; - DPRINTK(DPSYS, "inc timer users now %d\n", - card->timer_users); - if(card->timer_users != 1) - goto out; - - __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_TIMER_COUNT_RELOAD, - 240 ) ; - - __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_TIMER_COUNT_CURRENT, - 240 ) ; - - m3_outw(card, - m3_inw(card, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE, - HOST_INT_CTRL); -out: - spin_unlock_irqrestore(&card->lock, flags); -} - -static void dec_timer_users(struct m3_card *card) -{ - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - - card->timer_users--; - DPRINTK(DPSYS, "dec timer users now %d\n", - card->timer_users); - if(card->timer_users > 0 ) - goto out; - - __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_TIMER_COUNT_RELOAD, - 0 ) ; - - __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_TIMER_COUNT_CURRENT, - 0 ) ; - - m3_outw(card, m3_inw(card, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE, - HOST_INT_CTRL); -out: - spin_unlock_irqrestore(&card->lock, flags); -} - -/* - * {start,stop}_{adc,dac} should be called - * while holding the 'state' lock and they - * will try to grab the 'card' lock.. - */ -static void stop_adc(struct m3_state *s) -{ - if (! (s->enable & ADC_RUNNING)) - return; - - s->enable &= ~ADC_RUNNING; - dec_timer_users(s->card); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_INSTANCE_READY, 0); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - KDATA_ADC1_REQUEST, 0); -} - -static void stop_dac(struct m3_state *s) -{ - if (! (s->enable & DAC_RUNNING)) - return; - - DPRINTK(DPSYS, "stop_dac()\n"); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_INSTANCE_READY, 0); - - s->enable &= ~DAC_RUNNING; - s->card->dacs_active--; - dec_timer_users(s->card); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - KDATA_MIXER_TASK_NUMBER, - s->card->dacs_active ) ; -} - -static void start_dac(struct m3_state *s) -{ - if( (!s->dma_dac.mapped && s->dma_dac.count < 1) || - !s->dma_dac.ready || - (s->enable & DAC_RUNNING)) - return; - - DPRINTK(DPSYS, "start_dac()\n"); - - s->enable |= DAC_RUNNING; - s->card->dacs_active++; - inc_timer_users(s->card); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_INSTANCE_READY, 1); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - KDATA_MIXER_TASK_NUMBER, - s->card->dacs_active ) ; -} - -static void start_adc(struct m3_state *s) -{ - if ((! s->dma_adc.mapped && - s->dma_adc.count >= (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - || !s->dma_adc.ready - || (s->enable & ADC_RUNNING) ) - return; - - DPRINTK(DPSYS, "start_adc()\n"); - - s->enable |= ADC_RUNNING; - inc_timer_users(s->card); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - KDATA_ADC1_REQUEST, 1); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_INSTANCE_READY, 1); -} - -static struct play_vals { - u16 addr, val; -} pv[] = { - {CDATA_LEFT_VOLUME, ARB_VOLUME}, - {CDATA_RIGHT_VOLUME, ARB_VOLUME}, - {SRC3_DIRECTION_OFFSET, 0} , - /* +1, +2 are stereo/16 bit */ - {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ - {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ - {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ - {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ - {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ - {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ - {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ - {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ - {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ - {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ - {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ - {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ - {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */ - {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */ - {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */ - {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ - {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */ -}; - - -/* the mode passed should be already shifted and masked */ -static void m3_play_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size) -{ - int dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2); - int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2); - int dsp_in_buffer = s->dac_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); - int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; - struct dmabuf *db = &s->dma_dac; - int i; - - DPRINTK(DPSTR, "mode=%d rate=%d buf=%p len=%d.\n", - mode, rate, buffer, size); - -#define LO(x) ((x) & 0xffff) -#define HI(x) LO((x) >> 16) - - /* host dma buffer pointers */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_ADDRL, - LO(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_ADDRH, - HI(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1L, - LO(virt_to_bus(buffer) + size)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1H, - HI(virt_to_bus(buffer) + size)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_CURRENTL, - LO(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_CURRENTH, - HI(virt_to_bus(buffer))); -#undef LO -#undef HI - - /* dsp buffers */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_IN_BUF_BEGIN, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_IN_BUF_END_PLUS_1, - dsp_in_buffer + (dsp_in_size / 2)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_IN_BUF_HEAD, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_IN_BUF_TAIL, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_OUT_BUF_BEGIN, - dsp_out_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_OUT_BUF_END_PLUS_1, - dsp_out_buffer + (dsp_out_size / 2)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_OUT_BUF_HEAD, - dsp_out_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_OUT_BUF_TAIL, - dsp_out_buffer); - - /* - * some per client initializers - */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + SRC3_DIRECTION_OFFSET + 12, - s->dac_inst.data + 40 + 8); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + SRC3_DIRECTION_OFFSET + 19, - s->dac_inst.code + MINISRC_COEF_LOC); - - /* enable or disable low pass filter? */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + SRC3_DIRECTION_OFFSET + 22, - s->ratedac > 45000 ? 0xff : 0 ); - - /* tell it which way dma is going? */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_DMA_CONTROL, - DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); - - /* - * set an armload of static initializers - */ - for(i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + pv[i].addr, pv[i].val); - - /* - * put us in the lists if we're not already there - */ - - if(db->in_lists == 0) { - - db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, - s->dac_inst.data >> DP_SHIFT_COUNT); - - db->dma_index = m3_add_list(s->card, &s->card->dma_list, - s->dac_inst.data >> DP_SHIFT_COUNT); - - db->mixer_index = m3_add_list(s->card, &s->card->mixer_list, - s->dac_inst.data >> DP_SHIFT_COUNT); - - db->in_lists = 1; - } - - set_dac_rate(s,rate); - start_dac(s); -} - -/* - * Native record driver - */ -static struct rec_vals { - u16 addr, val; -} rv[] = { - {CDATA_LEFT_VOLUME, ARB_VOLUME}, - {CDATA_RIGHT_VOLUME, ARB_VOLUME}, - {SRC3_DIRECTION_OFFSET, 1} , - /* +1, +2 are stereo/16 bit */ - {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ - {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ - {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ - {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ - {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ - {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ - {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ - {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ - {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ - {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ - {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ - {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ - {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */ - {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */ - {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */ - {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */ - {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ - {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */ - {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */ -}; - -/* again, passed mode is alrady shifted/masked */ -static void m3_rec_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size) -{ - int dsp_in_size = MINISRC_IN_BUFFER_SIZE + (0x10 * 2); - int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2); - int dsp_in_buffer = s->adc_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); - int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; - struct dmabuf *db = &s->dma_adc; - int i; - - DPRINTK(DPSTR, "rec_setup mode=%d rate=%d buf=%p len=%d.\n", - mode, rate, buffer, size); - -#define LO(x) ((x) & 0xffff) -#define HI(x) LO((x) >> 16) - - /* host dma buffer pointers */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_ADDRL, - LO(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_ADDRH, - HI(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1L, - LO(virt_to_bus(buffer) + size)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1H, - HI(virt_to_bus(buffer) + size)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_CURRENTL, - LO(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_CURRENTH, - HI(virt_to_bus(buffer))); -#undef LO -#undef HI - - /* dsp buffers */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_IN_BUF_BEGIN, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_IN_BUF_END_PLUS_1, - dsp_in_buffer + (dsp_in_size / 2)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_IN_BUF_HEAD, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_IN_BUF_TAIL, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_OUT_BUF_BEGIN, - dsp_out_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_OUT_BUF_END_PLUS_1, - dsp_out_buffer + (dsp_out_size / 2)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_OUT_BUF_HEAD, - dsp_out_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_OUT_BUF_TAIL, - dsp_out_buffer); - - /* - * some per client initializers - */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + SRC3_DIRECTION_OFFSET + 12, - s->adc_inst.data + 40 + 8); - - /* tell it which way dma is going? */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_DMA_CONTROL, - DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + - DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); - - /* - * set an armload of static initializers - */ - for(i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + rv[i].addr, rv[i].val); - - /* - * put us in the lists if we're not already there - */ - - if(db->in_lists == 0) { - - db->adc1_index = m3_add_list(s->card, &s->card->adc1_list, - s->adc_inst.data >> DP_SHIFT_COUNT); - - db->dma_index = m3_add_list(s->card, &s->card->dma_list, - s->adc_inst.data >> DP_SHIFT_COUNT); - - db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, - s->adc_inst.data >> DP_SHIFT_COUNT); - - db->in_lists = 1; - } - - set_adc_rate(s,rate); - start_adc(s); -} -/* --------------------------------------------------------------------- */ - -static void set_dmaa(struct m3_state *s, unsigned int addr, unsigned int count) -{ - DPRINTK(DPINT,"set_dmaa??\n"); -} - -static void set_dmac(struct m3_state *s, unsigned int addr, unsigned int count) -{ - DPRINTK(DPINT,"set_dmac??\n"); -} - -u32 get_dma_pos(struct m3_card *card, - int instance_addr) -{ - u16 hi = 0, lo = 0; - int retry = 10; - - /* - * try and get a valid answer - */ - while(retry--) { - hi = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, - instance_addr + CDATA_HOST_SRC_CURRENTH); - - lo = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, - instance_addr + CDATA_HOST_SRC_CURRENTL); - - if(hi == m3_assp_read(card, MEMTYPE_INTERNAL_DATA, - instance_addr + CDATA_HOST_SRC_CURRENTH)) - break; - } - return lo | (hi<<16); -} - -u32 get_dmaa(struct m3_state *s) -{ - u32 offset; - - offset = get_dma_pos(s->card, s->dac_inst.data) - - virt_to_bus(s->dma_dac.rawbuf); - - DPRINTK(DPINT,"get_dmaa: 0x%08x\n",offset); - - return offset; -} - -u32 get_dmac(struct m3_state *s) -{ - u32 offset; - - offset = get_dma_pos(s->card, s->adc_inst.data) - - virt_to_bus(s->dma_adc.rawbuf); - - DPRINTK(DPINT,"get_dmac: 0x%08x\n",offset); - - return offset; - -} - -static void m3_interrupt(int irq, void *dev_id, struct pt_regs *regs); - -static int -prog_dmabuf(struct m3_state *s, unsigned rec) -{ - struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; - unsigned rate = rec ? s->rateadc : s->ratedac; - unsigned bytepersec; - unsigned bufs; - unsigned char fmt; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - - fmt = s->fmt; - if (rec) { - stop_adc(s); - fmt >>= ESS_ADC_SHIFT; - } else { - stop_dac(s); - fmt >>= ESS_DAC_SHIFT; - } - fmt &= ESS_FMT_MASK; - - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - - 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--; - 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; - - DPRINTK(DPSTR,"prog_dmabuf: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); - - memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); - - if (rec) - m3_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); - else - m3_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); - - db->ready = 1; - - spin_unlock_irqrestore(&s->lock, flags); - - return 0; -} - -static void clear_advance(struct m3_state *s) -{ - unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; - - unsigned char *buf = s->dma_dac.rawbuf; - unsigned bsize = s->dma_dac.dmasize; - unsigned bptr = s->dma_dac.swptr; - unsigned len = s->dma_dac.fragsize; - - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(buf + bptr, c, x); - /* account for wrapping? */ - bptr = 0; - len -= x; - } - memset(buf + bptr, c, len); -} - -/* call with spinlock held! */ -static void m3_update_ptr(struct m3_state *s) -{ - unsigned hwptr; - int diff; - - /* update ADC pointer */ - if (s->dma_adc.ready) { - hwptr = get_dmac(s) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - stop_adc(s); - /* brute force everyone back in sync, sigh */ - s->dma_adc.count = 0; - s->dma_adc.swptr = 0; - s->dma_adc.hwptr = 0; - s->dma_adc.error++; - } - } - } - /* update DAC pointer */ - if (s->dma_dac.ready) { - hwptr = get_dmaa(s) % s->dma_dac.dmasize; - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; - - DPRINTK(DPINT,"updating dac: hwptr: %6d diff: %6d count: %6d\n", - hwptr,diff,s->dma_dac.count); - - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - - if (s->dma_dac.mapped) { - - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { - wake_up(&s->dma_dac.wait); - } - } else { - - s->dma_dac.count -= diff; - - if (s->dma_dac.count <= 0) { - DPRINTK(DPCRAP,"underflow! diff: %d (0x%x) count: %d (0x%x) hw: %d (0x%x) sw: %d (0x%x)\n", - diff, diff, - s->dma_dac.count, - s->dma_dac.count, - hwptr, hwptr, - s->dma_dac.swptr, - s->dma_dac.swptr); - stop_dac(s); - /* brute force everyone back in sync, sigh */ - s->dma_dac.count = 0; - s->dma_dac.swptr = hwptr; - s->dma_dac.error++; - } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { - clear_advance(s); - s->dma_dac.endcleared = 1; - } - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { - wake_up(&s->dma_dac.wait); - DPRINTK(DPINT,"waking up DAC count: %d sw: %d hw: %d\n", - s->dma_dac.count, s->dma_dac.swptr, hwptr); - } - } - } -} - -static void m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct m3_card *c = (struct m3_card *)dev_id; - struct m3_state *s = &c->channels[0]; - u8 status; - - status = inb(c->iobase+0x1A); - - if(status == 0xff) return; - - /* presumably acking the ints? */ - outw(status, c->iobase+0x1A); - - if(c->in_suspend) - return; - - /* - * ack an assp int if its running - * and has an int pending - */ - if( status & ASSP_INT_PENDING) { - u8 ctl = inb(c->iobase + ASSP_CONTROL_B); - if( !(ctl & STOP_ASSP_CLOCK)) { - ctl = inb(c->iobase + ASSP_HOST_INT_STATUS ); - if(ctl & DSP2HOST_REQ_TIMER) { - outb( DSP2HOST_REQ_TIMER, c->iobase + ASSP_HOST_INT_STATUS); - /* update adc/dac info if it was a timer int */ - spin_lock(&s->lock); - m3_update_ptr(s); - spin_unlock(&s->lock); - } - } - } - - /* XXX is this needed? */ - if(status & 0x40) - outb(0x40, c->iobase+0x1A); -} - - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value in %s\n"; - -#define VALIDATE_MAGIC(FOO,MAG) \ -({ \ - if (!(FOO) || (FOO)->magic != MAG) { \ - printk(invalid_magic,__FUNCTION__); \ - return -ENXIO; \ - } \ -}) - -#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,M3_STATE_MAGIC) -#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,M3_CARD_MAGIC) - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct m3_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait,current); - unsigned long flags; - int count; - signed long tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&s->dma_dac.wait, &wait); - 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) { - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = (count * HZ) / s->ratedac; - tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; - /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. - or something. who cares. - zach */ - if (!schedule_timeout(tmo ? tmo : 1) && tmo) - DPRINTK(DPCRAP,"dma timed out?? %ld\n",jiffies); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -static ssize_t m3_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - 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(s, 1))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - - spin_lock_irqsave(&s->lock, flags); - - while (count > 0) { - int timed_out; - - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - start_adc(s); - if (file->f_flags & O_NONBLOCK) - { - ret = ret ? ret : -EAGAIN; - goto out; - } - - spin_unlock_irqrestore(&s->lock, flags); - timed_out = interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ) == 0; - spin_lock_irqsave(&s->lock, flags); - - if(timed_out) { - printk("read: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n", - s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, - s->dma_adc.hwptr, s->dma_adc.swptr); - stop_adc(s); - set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); - s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; - } - if (signal_pending(current)) - { - ret = ret ? ret : -ERESTARTSYS; - goto out; - } - continue; - } - - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - ret = ret ? ret : -EFAULT; - return ret; - } - spin_lock_irqsave(&s->lock, flags); - - swptr = (swptr + cnt) % s->dma_adc.dmasize; - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - count -= cnt; - buffer += cnt; - ret += cnt; - start_adc(s); - } - -out: - spin_unlock_irqrestore(&s->lock, flags); - return ret; -} - -static ssize_t m3_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - 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(s, 0))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - - spin_lock_irqsave(&s->lock, flags); - - while (count > 0) { - int timed_out; - - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - swptr = s->dma_dac.swptr; - - cnt = s->dma_dac.dmasize-swptr; - - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - start_dac(s); - if (file->f_flags & O_NONBLOCK) { - if(!ret) ret = -EAGAIN; - goto out; - } - spin_unlock_irqrestore(&s->lock, flags); - timed_out = interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ) == 0; - spin_lock_irqsave(&s->lock, flags); - if(timed_out) { - DPRINTK(DPCRAP,"write: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n", - s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, - s->dma_dac.hwptr, s->dma_dac.swptr); - stop_dac(s); - set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); - s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; - } - if (signal_pending(current)) { - if (!ret) ret = -ERESTARTSYS; - goto out; - } - continue; - } - spin_unlock_irqrestore(&s->lock, flags); - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) ret = -EFAULT; - return ret; - } - spin_lock_irqsave(&s->lock, flags); - - DPRINTK(DPSYS,"wrote %6d bytes at sw: %6d cnt: %6d while hw: %6d\n", - cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr); - - swptr = (swptr + cnt) % s->dma_dac.dmasize; - - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - count -= cnt; - buffer += cnt; - ret += cnt; - start_dac(s); - } -out: - spin_unlock_irqrestore(&s->lock, flags); - return ret; -} - -static unsigned int m3_poll(struct file *file, struct poll_table_struct *wait) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - 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); - m3_update_ptr(s); - - 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 m3_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - unsigned long max_size, size, start, offset; - struct dmabuf *db; - int ret = -EINVAL; - - VALIDATE_STATE(s); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 0)) != 0) - return ret; - db = &s->dma_dac; - } else - if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 1)) != 0) - return ret; - db = &s->dma_adc; - } else - return -EINVAL; - - max_size = db->dmasize; - - start = vma->vm_start; - offset = (vma->vm_pgoff << PAGE_SHIFT); - size = vma->vm_end - vma->vm_start; - - if(size > max_size) - goto out; - if(offset > max_size - size) - goto out; - - /* - * this will be ->nopage() once I can - * ask Jeff what the hell I'm doing wrong. - */ - ret = -EAGAIN; - if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) - goto out; - - db->mapped = 1; - ret = 0; - -out: - return ret; -} - -/* - * this function is a disaster.. - */ -#define get_user_ret(x, ptr, ret) ({ if(get_user(x, ptr)) return ret; }) -static int m3_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret; - unsigned char fmtm, fmtd; - - VALIDATE_STATE(s); - - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - - DPRINTK(DPSYS,"m3_ioctl: cmd %d\n", cmd); - - 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: - /* XXX fix */ - 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: - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(); - s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - case SNDCTL_DSP_SPEED: - get_user_ret(val, (int *)arg, -EFAULT); - spin_lock_irqsave(&s->lock, flags); - if (val >= 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - set_adc_rate(s, val); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - set_dac_rate(s, val); - } - } - spin_unlock_irqrestore(&s->lock, flags); - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - - case SNDCTL_DSP_STEREO: - get_user_ret(val, (int *)arg, -EFAULT); - spin_lock_irqsave(&s->lock, flags); - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val) - fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val) - fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - case SNDCTL_DSP_CHANNELS: - get_user_ret(val, (int *)arg, -EFAULT); - spin_lock_irqsave(&s->lock, flags); - if (val != 0) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val >= 2) - fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val >= 2) - fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - } - spin_unlock_irqrestore(&s->lock, flags); - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) - : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - get_user_ret(val, (int *)arg, -EFAULT); - spin_lock_irqsave(&s->lock, flags); - if (val != AFMT_QUERY) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - } - spin_unlock_irqrestore(&s->lock, flags); - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (ESS_FMT_16BIT << ESS_ADC_SHIFT) - : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? - AFMT_S16_LE : - AFMT_U8, - (int *)arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) - val |= PCM_ENABLE_INPUT; - if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, (int *)arg); - - case SNDCTL_DSP_SETTRIGGER: - get_user_ret(val, (int *)arg, -EFAULT); - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - 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(s, 0))) - 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->enable & DAC_RUNNING) && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - m3_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - 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->enable & ADC_RUNNING) && (val = prog_dmabuf(s, 1)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - m3_update_ptr(s); - 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; - spin_lock_irqsave(&s->lock, flags); - m3_update_ptr(s); - 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; - spin_lock_irqsave(&s->lock, flags); - m3_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; - 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; - spin_lock_irqsave(&s->lock, flags); - m3_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - 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(s, 0))) - return val; - return put_user(s->dma_dac.fragsize, (int *)arg); - } - if ((val = prog_dmabuf(s, 1))) - return val; - return put_user(s->dma_adc.fragsize, (int *)arg); - - case SNDCTL_DSP_SETFRAGMENT: - get_user_ret(val, (int *)arg, -EFAULT); - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - 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 (file->f_mode & FMODE_WRITE) { - 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; - } - spin_unlock_irqrestore(&s->lock, flags); - 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; - get_user_ret(val, (int *)arg, -EFAULT); - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) - : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); - - case SOUND_PCM_READ_BITS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) - : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - - } - return -EINVAL; -} - -static int -allocate_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db) -{ - int order; - - DPRINTK(DPSTR,"allocating for dmabuf %p\n", db); - - /* - * alloc as big a chunk as we can, start with - * 64k 'cause we're insane. based on order cause - * the amazingly complicated prog_dmabuf wants it. - * - * pci_alloc_sonsistent guarantees that it won't cross a natural - * boundry; the m3 hardware can't have dma cross a 64k bus - * address boundry. - */ - for (order = 16-PAGE_SHIFT; order >= 1; order--) { - db->rawbuf = pci_alloc_consistent(pci_dev, PAGE_SIZE << order, - &(db->handle)); - if(db->rawbuf) - break; - } - - if (!db->rawbuf) - return 1; - - DPRINTK(DPSTR,"allocated %ld (%d) bytes at %p\n", - PAGE_SIZE<rawbuf); - - { - struct page *page, *pend; - - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << order) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - mem_map_reserve(page); - } - - - db->buforder = order; - db->ready = 0; - db->mapped = 0; - - return 0; -} - -static void -nuke_lists(struct m3_card *card, struct dmabuf *db) -{ - m3_remove_list(card, &(card->dma_list), db->dma_index); - m3_remove_list(card, &(card->msrc_list), db->msrc_index); - db->in_lists = 0; -} - -static void -free_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db) -{ - if(db->rawbuf == NULL) - return; - - DPRINTK(DPSTR,"freeing %p from dmabuf %p\n",db->rawbuf, db); - - { - struct page *page, *pend; - 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(pci_dev, PAGE_SIZE << db->buforder, - db->rawbuf, db->handle); - - db->rawbuf = NULL; - db->buforder = 0; - db->mapped = 0; - db->ready = 0; -} - -static int m3_open(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct m3_card *c; - struct m3_state *s = NULL; - int i; - unsigned char fmtm = ~0, fmts = 0; - unsigned long flags; - - /* - * Scan the cards and find the channel. We only - * do this at open time so it is ok - */ - for(c = devs ; c != NULL ; c = c->next) { - - for(i=0;ichannels[i].dev_audio < 0) - continue; - if((c->channels[i].dev_audio ^ minor) & ~0xf) - continue; - - s = &c->channels[i]; - break; - } - } - - if (!s) - return -ENODEV; - - VALIDATE_STATE(s); - - 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 -EWOULDBLOCK; - } - up(&s->open_sem); - interruptible_sleep_on(&s->open_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - down(&s->open_sem); - } - - spin_lock_irqsave(&s->lock, flags); - - if (file->f_mode & FMODE_READ) { - fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; - - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - set_adc_rate(s, 8000); - } - if (file->f_mode & FMODE_WRITE) { - fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; - - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; - set_dac_rate(s, 8000); - } - set_fmt(s, fmtm, fmts); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - - up(&s->open_sem); - spin_unlock_irqrestore(&s->lock, flags); - return 0; -} - -static int m3_release(struct inode *inode, struct file *file) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - unsigned long flags; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) - drain_dac(s, file->f_flags & O_NONBLOCK); - - down(&s->open_sem); - spin_lock_irqsave(&s->lock, flags); - - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - if(s->dma_dac.in_lists) { - m3_remove_list(s->card, &(s->card->mixer_list), s->dma_dac.mixer_index); - nuke_lists(s->card, &(s->dma_dac)); - } - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - if(s->dma_adc.in_lists) { - m3_remove_list(s->card, &(s->card->adc1_list), s->dma_adc.adc1_index); - nuke_lists(s->card, &(s->dma_adc)); - } - } - - s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - - spin_unlock_irqrestore(&s->lock, flags); - up(&s->open_sem); - wake_up(&s->open_wait); - - return 0; -} - -/* - * Wait for the ac97 serial bus to be free. - * return nonzero if the bus is still busy. - */ -static int m3_ac97_wait(struct m3_card *card) -{ - int i = 10000; - - while( (m3_inb(card, 0x30) & 1) && i--) ; - - return i == 0; -} - -u16 m3_ac97_read(struct ac97_codec *codec, u8 reg) -{ - u16 ret = 0; - struct m3_card *card = codec->private_data; - - spin_lock(&card->ac97_lock); - - if(m3_ac97_wait(card)) { - printk(KERN_ERR PFX "serial bus busy reading reg 0x%x\n",reg); - goto out; - } - - m3_outb(card, 0x80 | (reg & 0x7f), 0x30); - - if(m3_ac97_wait(card)) { - printk(KERN_ERR PFX "serial bus busy finishing read reg 0x%x\n",reg); - goto out; - } - - ret = m3_inw(card, 0x32); - DPRINTK(DPCRAP,"reading 0x%04x from 0x%02x\n",ret, reg); - -out: - spin_unlock(&card->ac97_lock); - return ret; -} - -void m3_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) -{ - struct m3_card *card = codec->private_data; - - spin_lock(&card->ac97_lock); - - if(m3_ac97_wait(card)) { - printk(KERN_ERR PFX "serial bus busy writing 0x%x to 0x%x\n",val, reg); - goto out; - } - DPRINTK(DPCRAP,"writing 0x%04x to 0x%02x\n", val, reg); - - m3_outw(card, val, 0x32); - m3_outb(card, reg & 0x7f, 0x30); -out: - spin_unlock(&card->ac97_lock); -} -/* OSS /dev/mixer file operation methods */ -static int m3_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = minor(inode->i_rdev); - struct m3_card *card = devs; - - for (card = devs; card != NULL; card = card->next) { - if((card->ac97 != NULL) && (card->ac97->dev_mixer == minor)) - break; - } - - if (!card) { - return -ENODEV; - } - - file->private_data = card->ac97; - - return 0; -} - -static int m3_release_mixdev(struct inode *inode, struct file *file) -{ - return 0; -} - -static int m3_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 struct file_operations m3_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: m3_ioctl_mixdev, - open: m3_open_mixdev, - release: m3_release_mixdev, -}; - -void remote_codec_config(int io, int isremote) -{ - isremote = isremote ? 1 : 0; - - outw( (inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote, - io + RING_BUS_CTRL_B); - outw( (inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote, - io + SDO_OUT_DEST_CTRL); - outw( (inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote, - io + SDO_IN_DEST_CTRL); -} - -/* - * hack, returns non zero on err - */ -static int try_read_vendor(struct m3_card *card) -{ - u16 ret; - - if(m3_ac97_wait(card)) - return 1; - - m3_outb(card, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30); - - if(m3_ac97_wait(card)) - return 1; - - ret = m3_inw(card, 0x32); - - return (ret == 0) || (ret == 0xffff); -} - -static void m3_codec_reset(struct m3_card *card, int busywait) -{ - u16 dir; - int delay1 = 0, delay2 = 0, i; - int io = card->iobase; - - switch (card->card_type) { - /* - * the onboard codec on the allegro seems - * to want to wait a very long time before - * coming back to life - */ - case ESS_ALLEGRO: - delay1 = 50; - delay2 = 800; - break; - case ESS_MAESTRO3: - case ESS_MAESTRO3HW: - delay1 = 20; - delay2 = 500; - break; - } - - for(i = 0; i < 5; i ++) { - dir = inw(io + GPIO_DIRECTION); - dir |= 0x10; /* assuming pci bus master? */ - - remote_codec_config(io, 0); - - outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A); - udelay(20); - - outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION); - outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK); - outw(0, io + GPIO_DATA); - outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION); - - if(busywait) { - mdelay(delay1); - } else { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((delay1 * HZ) / 1000); - } - - outw(GPO_PRIMARY_AC97, io + GPIO_DATA); - udelay(5); - /* ok, bring back the ac-link */ - outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A); - outw(~0, io + GPIO_MASK); - - if(busywait) { - mdelay(delay2); - } else { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((delay2 * HZ) / 1000); - } - if(! try_read_vendor(card)) - break; - - delay1 += 10; - delay2 += 100; - - DPRINTK(DPMOD, "retrying codec reset with delays of %d and %d ms\n", - delay1, delay2); - } - -#if 0 - /* more gung-ho reset that doesn't - * seem to work anywhere :) - */ - tmp = inw(io + RING_BUS_CTRL_A); - outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A); - mdelay(20); - outw(tmp, io + RING_BUS_CTRL_A); - mdelay(50); -#endif -} - -static int __init m3_codec_install(struct m3_card *card) -{ - struct ac97_codec *codec; - - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) - return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); - - codec->private_data = card; - codec->codec_read = m3_ac97_read; - codec->codec_write = m3_ac97_write; - /* someday we should support secondary codecs.. */ - codec->id = 0; - - if (ac97_probe_codec(codec) == 0) { - printk(KERN_ERR PFX "codec probe failed\n"); - kfree(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); - return -1; - } - - card->ac97 = codec; - - return 0; -} - - -#define MINISRC_LPF_LEN 10 -static u16 minisrc_lpf[MINISRC_LPF_LEN] = { - 0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C, - 0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F -}; -static void m3_assp_init(struct m3_card *card) -{ - int i; - - /* zero kernel data */ - for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_BASE_ADDR + i, 0); - - /* zero mixer data? */ - for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_BASE_ADDR2 + i, 0); - - /* init dma pointer */ - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_CURRENT_DMA, - KDATA_DMA_XFER0); - - /* write kernel into code memory.. */ - for(i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) { - m3_assp_write(card, MEMTYPE_INTERNAL_CODE, - REV_B_CODE_MEMORY_BEGIN + i, - assp_kernel_image[i]); - } - - /* - * We only have this one client and we know that 0x400 - * is free in our kernel's mem map, so lets just - * drop it there. It seems that the minisrc doesn't - * need vectors, so we won't bother with them.. - */ - for(i = 0 ; i < sizeof(assp_minisrc_image) / 2; i++) { - m3_assp_write(card, MEMTYPE_INTERNAL_CODE, - 0x400 + i, - assp_minisrc_image[i]); - } - - /* - * write the coefficients for the low pass filter? - */ - for(i = 0; i < MINISRC_LPF_LEN ; i++) { - m3_assp_write(card, MEMTYPE_INTERNAL_CODE, - 0x400 + MINISRC_COEF_LOC + i, - minisrc_lpf[i]); - } - - m3_assp_write(card, MEMTYPE_INTERNAL_CODE, - 0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN, - 0x8000); - - /* - * the minisrc is the only thing on - * our task list.. - */ - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_TASK0, - 0x400); - - /* - * init the mixer number.. - */ - - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_MIXER_TASK_NUMBER,0); - - /* - * EXTREME KERNEL MASTER VOLUME - */ - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_DAC_LEFT_VOLUME, ARB_VOLUME); - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME); - - card->mixer_list.mem_addr = KDATA_MIXER_XFER0; - card->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS; - card->adc1_list.mem_addr = KDATA_ADC1_XFER0; - card->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS; - card->dma_list.mem_addr = KDATA_DMA_XFER0; - card->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS; - card->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC; - card->msrc_list.max = MAX_INSTANCE_MINISRC; -} - -static int setup_msrc(struct m3_card *card, - struct assp_instance *inst, int index) -{ - int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + - MINISRC_IN_BUFFER_SIZE / 2 + - 1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 ); - int address, i; - - /* - * the revb memory map has 0x1100 through 0x1c00 - * free. - */ - - /* - * align instance address to 256 bytes so that it's - * shifted list address is aligned. - * list address = (mem address >> 1) >> 7; - */ - data_bytes = (data_bytes + 255) & ~255; - address = 0x1100 + ((data_bytes/2) * index); - - if((address + (data_bytes/2)) >= 0x1c00) { - printk(KERN_ERR PFX "no memory for %d bytes at ind %d (addr 0x%x)\n", - data_bytes, index, address); - return -1; - } - - for(i = 0; i < data_bytes/2 ; i++) - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - address + i, 0); - - inst->code = 0x400; - inst->data = address; - - return 0; -} - -static int m3_assp_client_init(struct m3_state *s) -{ - setup_msrc(s->card, &(s->dac_inst), s->index * 2); - setup_msrc(s->card, &(s->adc_inst), (s->index * 2) + 1); - - return 0; -} - -static void m3_amp_enable(struct m3_card *card, int enable) -{ - /* - * this works for the reference board, have to find - * out about others - * - * this needs more magic for 4 speaker, but.. - */ - int io = card->iobase; - u16 gpo, polarity_port, polarity; - - if(!external_amp) - return; - - switch (card->card_type) { - case ESS_ALLEGRO: - polarity_port = 0x1800; - break; - default: - /* presumably this is for all 'maestro3's.. */ - polarity_port = 0x1100; - break; - } - - gpo = (polarity_port >> 8) & 0x0F; - polarity = polarity_port >> 12; - if ( enable ) - polarity = !polarity; - polarity = polarity << gpo; - gpo = 1 << gpo; - - outw(~gpo , io + GPIO_MASK); - - outw( inw(io + GPIO_DIRECTION) | gpo , - io + GPIO_DIRECTION); - - outw( (GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity) , - io + GPIO_DATA); - - outw(0xffff , io + GPIO_MASK); -} - -static int -maestro_config(struct m3_card *card) -{ - struct pci_dev *pcidev = card->pcidev; - u32 n; - u8 t; /* makes as much sense as 'n', no? */ - - pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); - n &= REDUCED_DEBOUNCE; - n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; - pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); - - outb(RESET_ASSP, card->iobase + ASSP_CONTROL_B); - pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); - n &= ~INT_CLK_SELECT; - if(card->card_type >= ESS_MAESTRO3) { - n &= ~INT_CLK_MULT_ENABLE; - n |= INT_CLK_SRC_NOT_PCI; - } - n &= ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 ); - pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); - - if(card->card_type <= ESS_ALLEGRO) { - pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n); - n |= IN_CLK_12MHZ_SELECT; - pci_write_config_dword(pcidev, PCI_USER_CONFIG, n); - } - - t = inb(card->iobase + ASSP_CONTROL_A); - t &= ~( DSP_CLK_36MHZ_SELECT | ASSP_CLK_49MHZ_SELECT); - t |= ASSP_CLK_49MHZ_SELECT; - t |= ASSP_0_WS_ENABLE; - outb(t, card->iobase + ASSP_CONTROL_A); - - outb(RUN_ASSP, card->iobase + ASSP_CONTROL_B); - - return 0; -} - -static void m3_enable_ints(struct m3_card *card) -{ - unsigned long io = card->iobase; - - outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL); - outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, - io + ASSP_CONTROL_C); -} - -static struct file_operations m3_audio_fops = { - owner: THIS_MODULE, - llseek: &no_llseek, - read: &m3_read, - write: &m3_write, - poll: &m3_poll, - ioctl: &m3_ioctl, - mmap: &m3_mmap, - open: &m3_open, - release: &m3_release, -}; - -#ifdef CONFIG_PM -int alloc_dsp_suspendmem(struct m3_card *card) -{ - int len = sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH); - - if( (card->suspend_mem = vmalloc(len)) == NULL) - return 1; - - return 0; -} -void free_dsp_suspendmem(struct m3_card *card) -{ - if(card->suspend_mem) - vfree(card->suspend_mem); -} - -#else -#define alloc_dsp_suspendmem(args...) 0 -#define free_dsp_suspendmem(args...) -#endif - -/* - * great day! this function is ugly as hell. - */ -static int __init m3_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) -{ - u32 n; - int i; - struct m3_card *card = NULL; - int ret = 0; - int card_type = pci_id->driver_data; - - DPRINTK(DPMOD, "in maestro_install\n"); - - if (pci_enable_device(pci_dev)) - return -EIO; - - if (pci_set_dma_mask(pci_dev, M3_PCI_DMA_MASK)) { - printk(KERN_ERR PFX "architecture does not support limiting to 28bit PCI bus addresses\n"); - return -ENODEV; - } - - pci_set_master(pci_dev); - - if( (card = kmalloc(sizeof(struct m3_card), GFP_KERNEL)) == NULL) { - printk(KERN_WARNING PFX "out of memory\n"); - return -ENOMEM; - } - memset(card, 0, sizeof(struct m3_card)); - card->pcidev = pci_dev; - init_waitqueue_head(&card->suspend_queue); - - if ( ! request_region(pci_resource_start(pci_dev, 0), - pci_resource_len (pci_dev, 0), M3_MODULE_NAME)) { - - printk(KERN_WARNING PFX "unable to reserve I/O space.\n"); - ret = -EBUSY; - goto out; - } - - card->iobase = pci_resource_start(pci_dev, 0); - - if(alloc_dsp_suspendmem(card)) { - printk(KERN_WARNING PFX "couldn't alloc %d bytes for saving dsp state on suspend\n", - REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH); - ret = -ENOMEM; - goto out; - } - - card->card_type = card_type; - card->irq = pci_dev->irq; - card->next = devs; - card->magic = M3_CARD_MAGIC; - spin_lock_init(&card->lock); - spin_lock_init(&card->ac97_lock); - devs = card; - for(i = 0; ichannels[i]); - s->dev_audio = -1; - } - - printk(KERN_INFO PFX "Configuring ESS %s found at IO 0x%04X IRQ %d\n", - card_names[card->card_type], card->iobase, card->irq); - - pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &n); - printk(KERN_INFO PFX " subvendor id: 0x%08x\n",n); - - maestro_config(card); - m3_assp_halt(card); - - m3_codec_reset(card, 0); - - if(m3_codec_install(card)) { - ret = -EIO; - goto out; - } - - m3_assp_init(card); - m3_amp_enable(card, 1); - - for(i=0;ichannels[i]; - - s->index = i; - - s->card = card; - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - spin_lock_init(&s->lock); - init_MUTEX(&(s->open_sem)); - s->magic = M3_STATE_MAGIC; - - m3_assp_client_init(s); - - if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) - printk(KERN_WARNING PFX "initing a dsp device that is already in use?\n"); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1)) < 0) { - break; - } - - if( allocate_dmabuf(card->pcidev, &(s->dma_adc)) || - allocate_dmabuf(card->pcidev, &(s->dma_dac))) { - ret = -ENOMEM; - goto out; - } - } - - if(request_irq(card->irq, m3_interrupt, SA_SHIRQ, card_names[card->card_type], card)) { - - printk(KERN_ERR PFX "unable to allocate irq %d,\n", card->irq); - - ret = -EIO; - goto out; - } - - pci_set_drvdata(pci_dev, card); - - m3_enable_ints(card); - m3_assp_continue(card); - -out: - if(ret) { - if(card->iobase) - release_region(pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); - free_dsp_suspendmem(card); - if(card->ac97) { - unregister_sound_mixer(card->ac97->dev_mixer); - kfree(card->ac97); - } - for(i=0;ichannels[i]; - if(s->dev_audio != -1) - unregister_sound_dsp(s->dev_audio); - } - kfree(card); - } - - return ret; -} - -static void m3_remove(struct pci_dev *pci_dev) -{ - struct m3_card *card; - - unregister_reboot_notifier(&m3_reboot_nb); - - while ((card = devs)) { - int i; - devs = devs->next; - - free_irq(card->irq, card); - unregister_sound_mixer(card->ac97->dev_mixer); - kfree(card->ac97); - - for(i=0;ichannels[i]; - if(s->dev_audio < 0) - continue; - - unregister_sound_dsp(s->dev_audio); - free_dmabuf(card->pcidev, &s->dma_adc); - free_dmabuf(card->pcidev, &s->dma_dac); - } - - release_region(card->iobase, 256); - free_dsp_suspendmem(card); - kfree(card); - } - devs = NULL; -} - -/* - * some bioses like the sound chip to be powered down - * at shutdown. We're just calling _suspend to - * achieve that.. - */ -static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf) -{ - struct m3_card *card; - - DPRINTK(DPMOD, "notifier suspending all cards\n"); - - for(card = devs; card != NULL; card = card->next) { - if(!card->in_suspend) - m3_suspend(card->pcidev, 3); /* XXX legal? */ - } - return 0; -} - -static int m3_suspend(struct pci_dev *pci_dev, u32 state) -{ - unsigned long flags; - int i; - struct m3_card *card = pci_get_drvdata(pci_dev); - - /* must be a better way.. */ - save_flags(flags); - cli(); - - DPRINTK(DPMOD, "pm in dev %p\n",card); - - for(i=0;ichannels[i]; - - if(s->dev_audio == -1) - continue; - - DPRINTK(DPMOD, "stop_adc/dac() device %d\n",i); - stop_dac(s); - stop_adc(s); - } - - mdelay(10); /* give the assp a chance to idle.. */ - - m3_assp_halt(card); - - if(card->suspend_mem) { - int index = 0; - - DPRINTK(DPMOD, "saving code\n"); - for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++) - card->suspend_mem[index++] = - m3_assp_read(card, MEMTYPE_INTERNAL_CODE, i); - DPRINTK(DPMOD, "saving data\n"); - for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) - card->suspend_mem[index++] = - m3_assp_read(card, MEMTYPE_INTERNAL_DATA, i); - } - - DPRINTK(DPMOD, "powering down apci regs\n"); - m3_outw(card, 0xffff, 0x54); - m3_outw(card, 0xffff, 0x56); - - card->in_suspend = 1; - - restore_flags(flags); - - return 0; -} - -static int m3_resume(struct pci_dev *pci_dev) -{ - unsigned long flags; - int index; - int i; - struct m3_card *card = pci_get_drvdata(pci_dev); - - save_flags(flags); /* paranoia */ - cli(); - card->in_suspend = 0; - - DPRINTK(DPMOD, "resuming\n"); - - /* first lets just bring everything back. .*/ - - DPRINTK(DPMOD, "bringing power back on card 0x%p\n",card); - m3_outw(card, 0, 0x54); - m3_outw(card, 0, 0x56); - - DPRINTK(DPMOD, "restoring pci configs and reseting codec\n"); - maestro_config(card); - m3_assp_halt(card); - m3_codec_reset(card, 1); - - DPRINTK(DPMOD, "restoring dsp code card\n"); - index = 0; - for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++) - m3_assp_write(card, MEMTYPE_INTERNAL_CODE, i, - card->suspend_mem[index++]); - for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, i, - card->suspend_mem[index++]); - - /* tell the dma engine to restart itself */ - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_DMA_ACTIVE, 0); - - DPRINTK(DPMOD, "resuming dsp\n"); - m3_assp_continue(card); - - DPRINTK(DPMOD, "enabling ints\n"); - m3_enable_ints(card); - - /* bring back the old school flavor */ - for(i = 0; i < SOUND_MIXER_NRDEVICES ; i++) { - int state = card->ac97->mixer_state[i]; - if (!supported_mixer(card->ac97, i)) - continue; - - card->ac97->write_mixer(card->ac97, i, - state & 0xff, (state >> 8) & 0xff); - } - - m3_amp_enable(card, 1); - - /* - * now we flip on the music - */ - for(i=0;ichannels[i]; - if(s->dev_audio == -1) - continue; - /* - * db->ready makes it so these guys can be - * called unconditionally.. - */ - DPRINTK(DPMOD, "turning on dacs ind %d\n",i); - start_dac(s); - start_adc(s); - } - - restore_flags(flags); - - /* - * all right, we think things are ready, - * wake up people who were using the device - * when we suspended - */ - wake_up(&card->suspend_queue); - - return 0; -} - -MODULE_AUTHOR("Zach Brown "); -MODULE_DESCRIPTION("ESS Maestro3/Allegro Driver"); -MODULE_LICENSE("GPL"); - -#ifdef M_DEBUG -MODULE_PARM(debug,"i"); -#endif -MODULE_PARM(external_amp,"i"); - -static struct pci_driver m3_pci_driver = { - name: "ess_m3_audio", - id_table: m3_id_table, - probe: m3_probe, - remove: m3_remove, - suspend: m3_suspend, - resume: m3_resume, -}; - -static int __init m3_init_module(void) -{ - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - - printk(KERN_INFO PFX "version " DRIVER_VERSION " built at " __TIME__ " " __DATE__ "\n"); - - if (register_reboot_notifier(&m3_reboot_nb)) { - printk(KERN_WARNING PFX "reboot notifier registration failed\n"); - return -ENODEV; /* ? */ - } - - if (!pci_register_driver(&m3_pci_driver)) { - pci_unregister_driver(&m3_pci_driver); - unregister_reboot_notifier(&m3_reboot_nb); - return -ENODEV; - } - return 0; -} - -static void __exit m3_cleanup_module(void) -{ - pci_unregister_driver(&m3_pci_driver); -} - -module_init(m3_init_module); -module_exit(m3_cleanup_module); - -void check_suspend(struct m3_card *card) -{ - DECLARE_WAITQUEUE(wait, current); - - if(!card->in_suspend) - return; - - card->in_suspend++; - add_wait_queue(&card->suspend_queue, &wait); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule(); - remove_wait_queue(&card->suspend_queue, &wait); - set_current_state(TASK_RUNNING); -} diff -Nru a/drivers/sound/maestro3.h b/drivers/sound/maestro3.h --- a/drivers/sound/maestro3.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,821 +0,0 @@ -/* - * ESS Technology allegro audio driver. - * - * Copyright (C) 1992-2000 Don Kim (don.kim@esstech.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. - * - * Hacked for the maestro3 driver by zab - */ - -// Allegro PCI configuration registers -#define PCI_LEGACY_AUDIO_CTRL 0x40 -#define SOUND_BLASTER_ENABLE 0x00000001 -#define FM_SYNTHESIS_ENABLE 0x00000002 -#define GAME_PORT_ENABLE 0x00000004 -#define MPU401_IO_ENABLE 0x00000008 -#define MPU401_IRQ_ENABLE 0x00000010 -#define ALIAS_10BIT_IO 0x00000020 -#define SB_DMA_MASK 0x000000C0 -#define SB_DMA_0 0x00000040 -#define SB_DMA_1 0x00000040 -#define SB_DMA_R 0x00000080 -#define SB_DMA_3 0x000000C0 -#define SB_IRQ_MASK 0x00000700 -#define SB_IRQ_5 0x00000000 -#define SB_IRQ_7 0x00000100 -#define SB_IRQ_9 0x00000200 -#define SB_IRQ_10 0x00000300 -#define MIDI_IRQ_MASK 0x00003800 -#define SERIAL_IRQ_ENABLE 0x00004000 -#define DISABLE_LEGACY 0x00008000 - -#define PCI_ALLEGRO_CONFIG 0x50 -#define SB_ADDR_240 0x00000004 -#define MPU_ADDR_MASK 0x00000018 -#define MPU_ADDR_330 0x00000000 -#define MPU_ADDR_300 0x00000008 -#define MPU_ADDR_320 0x00000010 -#define MPU_ADDR_340 0x00000018 -#define USE_PCI_TIMING 0x00000040 -#define POSTED_WRITE_ENABLE 0x00000080 -#define DMA_POLICY_MASK 0x00000700 -#define DMA_DDMA 0x00000000 -#define DMA_TDMA 0x00000100 -#define DMA_PCPCI 0x00000200 -#define DMA_WBDMA16 0x00000400 -#define DMA_WBDMA4 0x00000500 -#define DMA_WBDMA2 0x00000600 -#define DMA_WBDMA1 0x00000700 -#define DMA_SAFE_GUARD 0x00000800 -#define HI_PERF_GP_ENABLE 0x00001000 -#define PIC_SNOOP_MODE_0 0x00002000 -#define PIC_SNOOP_MODE_1 0x00004000 -#define SOUNDBLASTER_IRQ_MASK 0x00008000 -#define RING_IN_ENABLE 0x00010000 -#define SPDIF_TEST_MODE 0x00020000 -#define CLK_MULT_MODE_SELECT_2 0x00040000 -#define EEPROM_WRITE_ENABLE 0x00080000 -#define CODEC_DIR_IN 0x00100000 -#define HV_BUTTON_FROM_GD 0x00200000 -#define REDUCED_DEBOUNCE 0x00400000 -#define HV_CTRL_ENABLE 0x00800000 -#define SPDIF_ENABLE 0x01000000 -#define CLK_DIV_SELECT 0x06000000 -#define CLK_DIV_BY_48 0x00000000 -#define CLK_DIV_BY_49 0x02000000 -#define CLK_DIV_BY_50 0x04000000 -#define CLK_DIV_RESERVED 0x06000000 -#define PM_CTRL_ENABLE 0x08000000 -#define CLK_MULT_MODE_SELECT 0x30000000 -#define CLK_MULT_MODE_SHIFT 28 -#define CLK_MULT_MODE_0 0x00000000 -#define CLK_MULT_MODE_1 0x10000000 -#define CLK_MULT_MODE_2 0x20000000 -#define CLK_MULT_MODE_3 0x30000000 -#define INT_CLK_SELECT 0x40000000 -#define INT_CLK_MULT_RESET 0x80000000 - -// M3 -#define INT_CLK_SRC_NOT_PCI 0x00100000 -#define INT_CLK_MULT_ENABLE 0x80000000 - -#define PCI_ACPI_CONTROL 0x54 -#define PCI_ACPI_D0 0x00000000 -#define PCI_ACPI_D1 0xB4F70000 -#define PCI_ACPI_D2 0xB4F7B4F7 - -#define PCI_USER_CONFIG 0x58 -#define EXT_PCI_MASTER_ENABLE 0x00000001 -#define SPDIF_OUT_SELECT 0x00000002 -#define TEST_PIN_DIR_CTRL 0x00000004 -#define AC97_CODEC_TEST 0x00000020 -#define TRI_STATE_BUFFER 0x00000080 -#define IN_CLK_12MHZ_SELECT 0x00000100 -#define MULTI_FUNC_DISABLE 0x00000200 -#define EXT_MASTER_PAIR_SEL 0x00000400 -#define PCI_MASTER_SUPPORT 0x00000800 -#define STOP_CLOCK_ENABLE 0x00001000 -#define EAPD_DRIVE_ENABLE 0x00002000 -#define REQ_TRI_STATE_ENABLE 0x00004000 -#define REQ_LOW_ENABLE 0x00008000 -#define MIDI_1_ENABLE 0x00010000 -#define MIDI_2_ENABLE 0x00020000 -#define SB_AUDIO_SYNC 0x00040000 -#define HV_CTRL_TEST 0x00100000 -#define SOUNDBLASTER_TEST 0x00400000 - -#define PCI_USER_CONFIG_C 0x5C - -#define PCI_DDMA_CTRL 0x60 -#define DDMA_ENABLE 0x00000001 - - -// Allegro registers -#define HOST_INT_CTRL 0x18 -#define SB_INT_ENABLE 0x0001 -#define MPU401_INT_ENABLE 0x0002 -#define ASSP_INT_ENABLE 0x0010 -#define RING_INT_ENABLE 0x0020 -#define HV_INT_ENABLE 0x0040 -#define CLKRUN_GEN_ENABLE 0x0100 -#define HV_CTRL_TO_PME 0x0400 -#define SOFTWARE_RESET_ENABLE 0x8000 - -/* - * should be using the above defines, probably. - */ -#define REGB_ENABLE_RESET 0x01 -#define REGB_STOP_CLOCK 0x10 - -#define HOST_INT_STATUS 0x1A -#define SB_INT_PENDING 0x01 -#define MPU401_INT_PENDING 0x02 -#define ASSP_INT_PENDING 0x10 -#define RING_INT_PENDING 0x20 -#define HV_INT_PENDING 0x40 - -#define HARDWARE_VOL_CTRL 0x1B -#define SHADOW_MIX_REG_VOICE 0x1C -#define HW_VOL_COUNTER_VOICE 0x1D -#define SHADOW_MIX_REG_MASTER 0x1E -#define HW_VOL_COUNTER_MASTER 0x1F - -#define CODEC_COMMAND 0x30 -#define CODEC_READ_B 0x80 - -#define CODEC_STATUS 0x30 -#define CODEC_BUSY_B 0x01 - -#define CODEC_DATA 0x32 - -#define RING_BUS_CTRL_A 0x36 -#define RAC_PME_ENABLE 0x0100 -#define RAC_SDFS_ENABLE 0x0200 -#define LAC_PME_ENABLE 0x0400 -#define LAC_SDFS_ENABLE 0x0800 -#define SERIAL_AC_LINK_ENABLE 0x1000 -#define IO_SRAM_ENABLE 0x2000 -#define IIS_INPUT_ENABLE 0x8000 - -#define RING_BUS_CTRL_B 0x38 -#define SECOND_CODEC_ID_MASK 0x0003 -#define SPDIF_FUNC_ENABLE 0x0010 -#define SECOND_AC_ENABLE 0x0020 -#define SB_MODULE_INTF_ENABLE 0x0040 -#define SSPE_ENABLE 0x0040 -#define M3I_DOCK_ENABLE 0x0080 - -#define SDO_OUT_DEST_CTRL 0x3A -#define COMMAND_ADDR_OUT 0x0003 -#define PCM_LR_OUT_LOCAL 0x0000 -#define PCM_LR_OUT_REMOTE 0x0004 -#define PCM_LR_OUT_MUTE 0x0008 -#define PCM_LR_OUT_BOTH 0x000C -#define LINE1_DAC_OUT_LOCAL 0x0000 -#define LINE1_DAC_OUT_REMOTE 0x0010 -#define LINE1_DAC_OUT_MUTE 0x0020 -#define LINE1_DAC_OUT_BOTH 0x0030 -#define PCM_CLS_OUT_LOCAL 0x0000 -#define PCM_CLS_OUT_REMOTE 0x0040 -#define PCM_CLS_OUT_MUTE 0x0080 -#define PCM_CLS_OUT_BOTH 0x00C0 -#define PCM_RLF_OUT_LOCAL 0x0000 -#define PCM_RLF_OUT_REMOTE 0x0100 -#define PCM_RLF_OUT_MUTE 0x0200 -#define PCM_RLF_OUT_BOTH 0x0300 -#define LINE2_DAC_OUT_LOCAL 0x0000 -#define LINE2_DAC_OUT_REMOTE 0x0400 -#define LINE2_DAC_OUT_MUTE 0x0800 -#define LINE2_DAC_OUT_BOTH 0x0C00 -#define HANDSET_OUT_LOCAL 0x0000 -#define HANDSET_OUT_REMOTE 0x1000 -#define HANDSET_OUT_MUTE 0x2000 -#define HANDSET_OUT_BOTH 0x3000 -#define IO_CTRL_OUT_LOCAL 0x0000 -#define IO_CTRL_OUT_REMOTE 0x4000 -#define IO_CTRL_OUT_MUTE 0x8000 -#define IO_CTRL_OUT_BOTH 0xC000 - -#define SDO_IN_DEST_CTRL 0x3C -#define STATUS_ADDR_IN 0x0003 -#define PCM_LR_IN_LOCAL 0x0000 -#define PCM_LR_IN_REMOTE 0x0004 -#define PCM_LR_RESERVED 0x0008 -#define PCM_LR_IN_BOTH 0x000C -#define LINE1_ADC_IN_LOCAL 0x0000 -#define LINE1_ADC_IN_REMOTE 0x0010 -#define LINE1_ADC_IN_MUTE 0x0020 -#define MIC_ADC_IN_LOCAL 0x0000 -#define MIC_ADC_IN_REMOTE 0x0040 -#define MIC_ADC_IN_MUTE 0x0080 -#define LINE2_DAC_IN_LOCAL 0x0000 -#define LINE2_DAC_IN_REMOTE 0x0400 -#define LINE2_DAC_IN_MUTE 0x0800 -#define HANDSET_IN_LOCAL 0x0000 -#define HANDSET_IN_REMOTE 0x1000 -#define HANDSET_IN_MUTE 0x2000 -#define IO_STATUS_IN_LOCAL 0x0000 -#define IO_STATUS_IN_REMOTE 0x4000 - -#define SPDIF_IN_CTRL 0x3E -#define SPDIF_IN_ENABLE 0x0001 - -#define GPIO_DATA 0x60 -#define GPIO_DATA_MASK 0x0FFF -#define GPIO_HV_STATUS 0x3000 -#define GPIO_PME_STATUS 0x4000 - -#define GPIO_MASK 0x64 -#define GPIO_DIRECTION 0x68 -#define GPO_PRIMARY_AC97 0x0001 -#define GPI_LINEOUT_SENSE 0x0004 -#define GPO_SECONDARY_AC97 0x0008 -#define GPI_VOL_DOWN 0x0010 -#define GPI_VOL_UP 0x0020 -#define GPI_IIS_CLK 0x0040 -#define GPI_IIS_LRCLK 0x0080 -#define GPI_IIS_DATA 0x0100 -#define GPI_DOCKING_STATUS 0x0100 -#define GPI_HEADPHONE_SENSE 0x0200 -#define GPO_EXT_AMP_SHUTDOWN 0x1000 - -// M3 -#define GPO_M3_EXT_AMP_SHUTDN 0x0002 - -#define ASSP_INDEX_PORT 0x80 -#define ASSP_MEMORY_PORT 0x82 -#define ASSP_DATA_PORT 0x84 - -#define MPU401_DATA_PORT 0x98 -#define MPU401_STATUS_PORT 0x99 - -#define CLK_MULT_DATA_PORT 0x9C - -#define ASSP_CONTROL_A 0xA2 -#define ASSP_0_WS_ENABLE 0x01 -#define ASSP_CTRL_A_RESERVED1 0x02 -#define ASSP_CTRL_A_RESERVED2 0x04 -#define ASSP_CLK_49MHZ_SELECT 0x08 -#define FAST_PLU_ENABLE 0x10 -#define ASSP_CTRL_A_RESERVED3 0x20 -#define DSP_CLK_36MHZ_SELECT 0x40 - -#define ASSP_CONTROL_B 0xA4 -#define RESET_ASSP 0x00 -#define RUN_ASSP 0x01 -#define ENABLE_ASSP_CLOCK 0x00 -#define STOP_ASSP_CLOCK 0x10 -#define RESET_TOGGLE 0x40 - -#define ASSP_CONTROL_C 0xA6 -#define ASSP_HOST_INT_ENABLE 0x01 -#define FM_ADDR_REMAP_DISABLE 0x02 -#define HOST_WRITE_PORT_ENABLE 0x08 - -#define ASSP_HOST_INT_STATUS 0xAC -#define DSP2HOST_REQ_PIORECORD 0x01 -#define DSP2HOST_REQ_I2SRATE 0x02 -#define DSP2HOST_REQ_TIMER 0x04 - -// AC97 registers -// XXX fix this crap up -/*#define AC97_RESET 0x00*/ - -#define AC97_VOL_MUTE_B 0x8000 -#define AC97_VOL_M 0x1F -#define AC97_LEFT_VOL_S 8 - -#define AC97_MASTER_VOL 0x02 -#define AC97_LINE_LEVEL_VOL 0x04 -#define AC97_MASTER_MONO_VOL 0x06 -#define AC97_PC_BEEP_VOL 0x0A -#define AC97_PC_BEEP_VOL_M 0x0F -#define AC97_SROUND_MASTER_VOL 0x38 -#define AC97_PC_BEEP_VOL_S 1 - -/*#define AC97_PHONE_VOL 0x0C -#define AC97_MIC_VOL 0x0E*/ -#define AC97_MIC_20DB_ENABLE 0x40 - -/*#define AC97_LINEIN_VOL 0x10 -#define AC97_CD_VOL 0x12 -#define AC97_VIDEO_VOL 0x14 -#define AC97_AUX_VOL 0x16*/ -#define AC97_PCM_OUT_VOL 0x18 -/*#define AC97_RECORD_SELECT 0x1A*/ -#define AC97_RECORD_MIC 0x00 -#define AC97_RECORD_CD 0x01 -#define AC97_RECORD_VIDEO 0x02 -#define AC97_RECORD_AUX 0x03 -#define AC97_RECORD_MONO_MUX 0x02 -#define AC97_RECORD_DIGITAL 0x03 -#define AC97_RECORD_LINE 0x04 -#define AC97_RECORD_STEREO 0x05 -#define AC97_RECORD_MONO 0x06 -#define AC97_RECORD_PHONE 0x07 - -/*#define AC97_RECORD_GAIN 0x1C*/ -#define AC97_RECORD_VOL_M 0x0F - -/*#define AC97_GENERAL_PURPOSE 0x20*/ -#define AC97_POWER_DOWN_CTRL 0x26 -#define AC97_ADC_READY 0x0001 -#define AC97_DAC_READY 0x0002 -#define AC97_ANALOG_READY 0x0004 -#define AC97_VREF_ON 0x0008 -#define AC97_PR0 0x0100 -#define AC97_PR1 0x0200 -#define AC97_PR2 0x0400 -#define AC97_PR3 0x0800 -#define AC97_PR4 0x1000 - -#define AC97_RESERVED1 0x28 - -#define AC97_VENDOR_TEST 0x5A - -#define AC97_CLOCK_DELAY 0x5C -#define AC97_LINEOUT_MUX_SEL 0x0001 -#define AC97_MONO_MUX_SEL 0x0002 -#define AC97_CLOCK_DELAY_SEL 0x1F -#define AC97_DAC_CDS_SHIFT 6 -#define AC97_ADC_CDS_SHIFT 11 - -#define AC97_MULTI_CHANNEL_SEL 0x74 - -/*#define AC97_VENDOR_ID1 0x7C -#define AC97_VENDOR_ID2 0x7E*/ - -/* - * ASSP control regs - */ -#define DSP_PORT_TIMER_COUNT 0x06 - -#define DSP_PORT_MEMORY_INDEX 0x80 - -#define DSP_PORT_MEMORY_TYPE 0x82 -#define MEMTYPE_INTERNAL_CODE 0x0002 -#define MEMTYPE_INTERNAL_DATA 0x0003 -#define MEMTYPE_MASK 0x0003 - -#define DSP_PORT_MEMORY_DATA 0x84 - -#define DSP_PORT_CONTROL_REG_A 0xA2 -#define DSP_PORT_CONTROL_REG_B 0xA4 -#define DSP_PORT_CONTROL_REG_C 0xA6 - -#define REV_A_CODE_MEMORY_BEGIN 0x0000 -#define REV_A_CODE_MEMORY_END 0x0FFF -#define REV_A_CODE_MEMORY_UNIT_LENGTH 0x0040 -#define REV_A_CODE_MEMORY_LENGTH (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1) - -#define REV_B_CODE_MEMORY_BEGIN 0x0000 -#define REV_B_CODE_MEMORY_END 0x0BFF -#define REV_B_CODE_MEMORY_UNIT_LENGTH 0x0040 -#define REV_B_CODE_MEMORY_LENGTH (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1) - -#define REV_A_DATA_MEMORY_BEGIN 0x1000 -#define REV_A_DATA_MEMORY_END 0x2FFF -#define REV_A_DATA_MEMORY_UNIT_LENGTH 0x0080 -#define REV_A_DATA_MEMORY_LENGTH (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1) - -#define REV_B_DATA_MEMORY_BEGIN 0x1000 -#define REV_B_DATA_MEMORY_END 0x2BFF -#define REV_B_DATA_MEMORY_UNIT_LENGTH 0x0080 -#define REV_B_DATA_MEMORY_LENGTH (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1) - - -#define NUM_UNITS_KERNEL_CODE 16 -#define NUM_UNITS_KERNEL_DATA 2 - -#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16 -#define NUM_UNITS_KERNEL_DATA_WITH_HSP 5 - -/* - * Kernel data layout - */ - -#define DP_SHIFT_COUNT 7 - -#define KDATA_BASE_ADDR 0x1000 -#define KDATA_BASE_ADDR2 0x1080 - -#define KDATA_TASK0 (KDATA_BASE_ADDR + 0x0000) -#define KDATA_TASK1 (KDATA_BASE_ADDR + 0x0001) -#define KDATA_TASK2 (KDATA_BASE_ADDR + 0x0002) -#define KDATA_TASK3 (KDATA_BASE_ADDR + 0x0003) -#define KDATA_TASK4 (KDATA_BASE_ADDR + 0x0004) -#define KDATA_TASK5 (KDATA_BASE_ADDR + 0x0005) -#define KDATA_TASK6 (KDATA_BASE_ADDR + 0x0006) -#define KDATA_TASK7 (KDATA_BASE_ADDR + 0x0007) -#define KDATA_TASK_ENDMARK (KDATA_BASE_ADDR + 0x0008) - -#define KDATA_CURRENT_TASK (KDATA_BASE_ADDR + 0x0009) -#define KDATA_TASK_SWITCH (KDATA_BASE_ADDR + 0x000A) - -#define KDATA_INSTANCE0_POS3D (KDATA_BASE_ADDR + 0x000B) -#define KDATA_INSTANCE1_POS3D (KDATA_BASE_ADDR + 0x000C) -#define KDATA_INSTANCE2_POS3D (KDATA_BASE_ADDR + 0x000D) -#define KDATA_INSTANCE3_POS3D (KDATA_BASE_ADDR + 0x000E) -#define KDATA_INSTANCE4_POS3D (KDATA_BASE_ADDR + 0x000F) -#define KDATA_INSTANCE5_POS3D (KDATA_BASE_ADDR + 0x0010) -#define KDATA_INSTANCE6_POS3D (KDATA_BASE_ADDR + 0x0011) -#define KDATA_INSTANCE7_POS3D (KDATA_BASE_ADDR + 0x0012) -#define KDATA_INSTANCE8_POS3D (KDATA_BASE_ADDR + 0x0013) -#define KDATA_INSTANCE_POS3D_ENDMARK (KDATA_BASE_ADDR + 0x0014) - -#define KDATA_INSTANCE0_SPKVIRT (KDATA_BASE_ADDR + 0x0015) -#define KDATA_INSTANCE_SPKVIRT_ENDMARK (KDATA_BASE_ADDR + 0x0016) - -#define KDATA_INSTANCE0_SPDIF (KDATA_BASE_ADDR + 0x0017) -#define KDATA_INSTANCE_SPDIF_ENDMARK (KDATA_BASE_ADDR + 0x0018) - -#define KDATA_INSTANCE0_MODEM (KDATA_BASE_ADDR + 0x0019) -#define KDATA_INSTANCE_MODEM_ENDMARK (KDATA_BASE_ADDR + 0x001A) - -#define KDATA_INSTANCE0_SRC (KDATA_BASE_ADDR + 0x001B) -#define KDATA_INSTANCE1_SRC (KDATA_BASE_ADDR + 0x001C) -#define KDATA_INSTANCE_SRC_ENDMARK (KDATA_BASE_ADDR + 0x001D) - -#define KDATA_INSTANCE0_MINISRC (KDATA_BASE_ADDR + 0x001E) -#define KDATA_INSTANCE1_MINISRC (KDATA_BASE_ADDR + 0x001F) -#define KDATA_INSTANCE2_MINISRC (KDATA_BASE_ADDR + 0x0020) -#define KDATA_INSTANCE3_MINISRC (KDATA_BASE_ADDR + 0x0021) -#define KDATA_INSTANCE_MINISRC_ENDMARK (KDATA_BASE_ADDR + 0x0022) - -#define KDATA_INSTANCE0_CPYTHRU (KDATA_BASE_ADDR + 0x0023) -#define KDATA_INSTANCE1_CPYTHRU (KDATA_BASE_ADDR + 0x0024) -#define KDATA_INSTANCE_CPYTHRU_ENDMARK (KDATA_BASE_ADDR + 0x0025) - -#define KDATA_CURRENT_DMA (KDATA_BASE_ADDR + 0x0026) -#define KDATA_DMA_SWITCH (KDATA_BASE_ADDR + 0x0027) -#define KDATA_DMA_ACTIVE (KDATA_BASE_ADDR + 0x0028) - -#define KDATA_DMA_XFER0 (KDATA_BASE_ADDR + 0x0029) -#define KDATA_DMA_XFER1 (KDATA_BASE_ADDR + 0x002A) -#define KDATA_DMA_XFER2 (KDATA_BASE_ADDR + 0x002B) -#define KDATA_DMA_XFER3 (KDATA_BASE_ADDR + 0x002C) -#define KDATA_DMA_XFER4 (KDATA_BASE_ADDR + 0x002D) -#define KDATA_DMA_XFER5 (KDATA_BASE_ADDR + 0x002E) -#define KDATA_DMA_XFER6 (KDATA_BASE_ADDR + 0x002F) -#define KDATA_DMA_XFER7 (KDATA_BASE_ADDR + 0x0030) -#define KDATA_DMA_XFER8 (KDATA_BASE_ADDR + 0x0031) -#define KDATA_DMA_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0032) - -#define KDATA_I2S_SAMPLE_COUNT (KDATA_BASE_ADDR + 0x0033) -#define KDATA_I2S_INT_METER (KDATA_BASE_ADDR + 0x0034) -#define KDATA_I2S_ACTIVE (KDATA_BASE_ADDR + 0x0035) - -#define KDATA_TIMER_COUNT_RELOAD (KDATA_BASE_ADDR + 0x0036) -#define KDATA_TIMER_COUNT_CURRENT (KDATA_BASE_ADDR + 0x0037) - -#define KDATA_HALT_SYNCH_CLIENT (KDATA_BASE_ADDR + 0x0038) -#define KDATA_HALT_SYNCH_DMA (KDATA_BASE_ADDR + 0x0039) -#define KDATA_HALT_ACKNOWLEDGE (KDATA_BASE_ADDR + 0x003A) - -#define KDATA_ADC1_XFER0 (KDATA_BASE_ADDR + 0x003B) -#define KDATA_ADC1_XFER_ENDMARK (KDATA_BASE_ADDR + 0x003C) -#define KDATA_ADC1_LEFT_VOLUME (KDATA_BASE_ADDR + 0x003D) -#define KDATA_ADC1_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x003E) -#define KDATA_ADC1_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x003F) -#define KDATA_ADC1_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0040) - -#define KDATA_ADC2_XFER0 (KDATA_BASE_ADDR + 0x0041) -#define KDATA_ADC2_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0042) -#define KDATA_ADC2_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0043) -#define KDATA_ADC2_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x0044) -#define KDATA_ADC2_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x0045) -#define KDATA_ADC2_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0046) - -#define KDATA_CD_XFER0 (KDATA_BASE_ADDR + 0x0047) -#define KDATA_CD_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0048) -#define KDATA_CD_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0049) -#define KDATA_CD_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x004A) -#define KDATA_CD_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x004B) -#define KDATA_CD_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x004C) - -#define KDATA_MIC_XFER0 (KDATA_BASE_ADDR + 0x004D) -#define KDATA_MIC_XFER_ENDMARK (KDATA_BASE_ADDR + 0x004E) -#define KDATA_MIC_VOLUME (KDATA_BASE_ADDR + 0x004F) -#define KDATA_MIC_SUR_VOL (KDATA_BASE_ADDR + 0x0050) - -#define KDATA_I2S_XFER0 (KDATA_BASE_ADDR + 0x0051) -#define KDATA_I2S_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0052) - -#define KDATA_CHI_XFER0 (KDATA_BASE_ADDR + 0x0053) -#define KDATA_CHI_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0054) - -#define KDATA_SPDIF_XFER (KDATA_BASE_ADDR + 0x0055) -#define KDATA_SPDIF_CURRENT_FRAME (KDATA_BASE_ADDR + 0x0056) -#define KDATA_SPDIF_FRAME0 (KDATA_BASE_ADDR + 0x0057) -#define KDATA_SPDIF_FRAME1 (KDATA_BASE_ADDR + 0x0058) -#define KDATA_SPDIF_FRAME2 (KDATA_BASE_ADDR + 0x0059) - -#define KDATA_SPDIF_REQUEST (KDATA_BASE_ADDR + 0x005A) -#define KDATA_SPDIF_TEMP (KDATA_BASE_ADDR + 0x005B) - -#define KDATA_SPDIFIN_XFER0 (KDATA_BASE_ADDR + 0x005C) -#define KDATA_SPDIFIN_XFER_ENDMARK (KDATA_BASE_ADDR + 0x005D) -#define KDATA_SPDIFIN_INT_METER (KDATA_BASE_ADDR + 0x005E) - -#define KDATA_DSP_RESET_COUNT (KDATA_BASE_ADDR + 0x005F) -#define KDATA_DEBUG_OUTPUT (KDATA_BASE_ADDR + 0x0060) - -#define KDATA_KERNEL_ISR_LIST (KDATA_BASE_ADDR + 0x0061) - -#define KDATA_KERNEL_ISR_CBSR1 (KDATA_BASE_ADDR + 0x0062) -#define KDATA_KERNEL_ISR_CBER1 (KDATA_BASE_ADDR + 0x0063) -#define KDATA_KERNEL_ISR_CBCR (KDATA_BASE_ADDR + 0x0064) -#define KDATA_KERNEL_ISR_AR0 (KDATA_BASE_ADDR + 0x0065) -#define KDATA_KERNEL_ISR_AR1 (KDATA_BASE_ADDR + 0x0066) -#define KDATA_KERNEL_ISR_AR2 (KDATA_BASE_ADDR + 0x0067) -#define KDATA_KERNEL_ISR_AR3 (KDATA_BASE_ADDR + 0x0068) -#define KDATA_KERNEL_ISR_AR4 (KDATA_BASE_ADDR + 0x0069) -#define KDATA_KERNEL_ISR_AR5 (KDATA_BASE_ADDR + 0x006A) -#define KDATA_KERNEL_ISR_BRCR (KDATA_BASE_ADDR + 0x006B) -#define KDATA_KERNEL_ISR_PASR (KDATA_BASE_ADDR + 0x006C) -#define KDATA_KERNEL_ISR_PAER (KDATA_BASE_ADDR + 0x006D) - -#define KDATA_CLIENT_SCRATCH0 (KDATA_BASE_ADDR + 0x006E) -#define KDATA_CLIENT_SCRATCH1 (KDATA_BASE_ADDR + 0x006F) -#define KDATA_KERNEL_SCRATCH (KDATA_BASE_ADDR + 0x0070) -#define KDATA_KERNEL_ISR_SCRATCH (KDATA_BASE_ADDR + 0x0071) - -#define KDATA_OUEUE_LEFT (KDATA_BASE_ADDR + 0x0072) -#define KDATA_QUEUE_RIGHT (KDATA_BASE_ADDR + 0x0073) - -#define KDATA_ADC1_REQUEST (KDATA_BASE_ADDR + 0x0074) -#define KDATA_ADC2_REQUEST (KDATA_BASE_ADDR + 0x0075) -#define KDATA_CD_REQUEST (KDATA_BASE_ADDR + 0x0076) -#define KDATA_MIC_REQUEST (KDATA_BASE_ADDR + 0x0077) - -#define KDATA_ADC1_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0078) -#define KDATA_ADC2_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0079) -#define KDATA_CD_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007A) -#define KDATA_MIC_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007B) -#define KDATA_MIC_SYNC_COUNTER (KDATA_BASE_ADDR + 0x007C) - -/* - * second 'segment' (?) reserved for mixer - * buffers.. - */ - -#define KDATA_MIXER_WORD0 (KDATA_BASE_ADDR2 + 0x0000) -#define KDATA_MIXER_WORD1 (KDATA_BASE_ADDR2 + 0x0001) -#define KDATA_MIXER_WORD2 (KDATA_BASE_ADDR2 + 0x0002) -#define KDATA_MIXER_WORD3 (KDATA_BASE_ADDR2 + 0x0003) -#define KDATA_MIXER_WORD4 (KDATA_BASE_ADDR2 + 0x0004) -#define KDATA_MIXER_WORD5 (KDATA_BASE_ADDR2 + 0x0005) -#define KDATA_MIXER_WORD6 (KDATA_BASE_ADDR2 + 0x0006) -#define KDATA_MIXER_WORD7 (KDATA_BASE_ADDR2 + 0x0007) -#define KDATA_MIXER_WORD8 (KDATA_BASE_ADDR2 + 0x0008) -#define KDATA_MIXER_WORD9 (KDATA_BASE_ADDR2 + 0x0009) -#define KDATA_MIXER_WORDA (KDATA_BASE_ADDR2 + 0x000A) -#define KDATA_MIXER_WORDB (KDATA_BASE_ADDR2 + 0x000B) -#define KDATA_MIXER_WORDC (KDATA_BASE_ADDR2 + 0x000C) -#define KDATA_MIXER_WORDD (KDATA_BASE_ADDR2 + 0x000D) -#define KDATA_MIXER_WORDE (KDATA_BASE_ADDR2 + 0x000E) -#define KDATA_MIXER_WORDF (KDATA_BASE_ADDR2 + 0x000F) - -#define KDATA_MIXER_XFER0 (KDATA_BASE_ADDR2 + 0x0010) -#define KDATA_MIXER_XFER1 (KDATA_BASE_ADDR2 + 0x0011) -#define KDATA_MIXER_XFER2 (KDATA_BASE_ADDR2 + 0x0012) -#define KDATA_MIXER_XFER3 (KDATA_BASE_ADDR2 + 0x0013) -#define KDATA_MIXER_XFER4 (KDATA_BASE_ADDR2 + 0x0014) -#define KDATA_MIXER_XFER5 (KDATA_BASE_ADDR2 + 0x0015) -#define KDATA_MIXER_XFER6 (KDATA_BASE_ADDR2 + 0x0016) -#define KDATA_MIXER_XFER7 (KDATA_BASE_ADDR2 + 0x0017) -#define KDATA_MIXER_XFER8 (KDATA_BASE_ADDR2 + 0x0018) -#define KDATA_MIXER_XFER9 (KDATA_BASE_ADDR2 + 0x0019) -#define KDATA_MIXER_XFER_ENDMARK (KDATA_BASE_ADDR2 + 0x001A) - -#define KDATA_MIXER_TASK_NUMBER (KDATA_BASE_ADDR2 + 0x001B) -#define KDATA_CURRENT_MIXER (KDATA_BASE_ADDR2 + 0x001C) -#define KDATA_MIXER_ACTIVE (KDATA_BASE_ADDR2 + 0x001D) -#define KDATA_MIXER_BANK_STATUS (KDATA_BASE_ADDR2 + 0x001E) -#define KDATA_DAC_LEFT_VOLUME (KDATA_BASE_ADDR2 + 0x001F) -#define KDATA_DAC_RIGHT_VOLUME (KDATA_BASE_ADDR2 + 0x0020) - -#define MAX_INSTANCE_MINISRC (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC) -#define MAX_VIRTUAL_DMA_CHANNELS (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0) -#define MAX_VIRTUAL_MIXER_CHANNELS (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0) -#define MAX_VIRTUAL_ADC1_CHANNELS (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0) - -/* - * client data area offsets - */ -#define CDATA_INSTANCE_READY 0x00 - -#define CDATA_HOST_SRC_ADDRL 0x01 -#define CDATA_HOST_SRC_ADDRH 0x02 -#define CDATA_HOST_SRC_END_PLUS_1L 0x03 -#define CDATA_HOST_SRC_END_PLUS_1H 0x04 -#define CDATA_HOST_SRC_CURRENTL 0x05 -#define CDATA_HOST_SRC_CURRENTH 0x06 - -#define CDATA_IN_BUF_CONNECT 0x07 -#define CDATA_OUT_BUF_CONNECT 0x08 - -#define CDATA_IN_BUF_BEGIN 0x09 -#define CDATA_IN_BUF_END_PLUS_1 0x0A -#define CDATA_IN_BUF_HEAD 0x0B -#define CDATA_IN_BUF_TAIL 0x0C -#define CDATA_OUT_BUF_BEGIN 0x0D -#define CDATA_OUT_BUF_END_PLUS_1 0x0E -#define CDATA_OUT_BUF_HEAD 0x0F -#define CDATA_OUT_BUF_TAIL 0x10 - -#define CDATA_DMA_CONTROL 0x11 -#define CDATA_RESERVED 0x12 - -#define CDATA_FREQUENCY 0x13 -#define CDATA_LEFT_VOLUME 0x14 -#define CDATA_RIGHT_VOLUME 0x15 -#define CDATA_LEFT_SUR_VOL 0x16 -#define CDATA_RIGHT_SUR_VOL 0x17 - -#define CDATA_HEADER_LEN 0x18 - -#define SRC3_DIRECTION_OFFSET CDATA_HEADER_LEN -#define SRC3_MODE_OFFSET (CDATA_HEADER_LEN + 1) -#define SRC3_WORD_LENGTH_OFFSET (CDATA_HEADER_LEN + 2) -#define SRC3_PARAMETER_OFFSET (CDATA_HEADER_LEN + 3) -#define SRC3_COEFF_ADDR_OFFSET (CDATA_HEADER_LEN + 8) -#define SRC3_FILTAP_ADDR_OFFSET (CDATA_HEADER_LEN + 10) -#define SRC3_TEMP_INBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 16) -#define SRC3_TEMP_OUTBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 17) - -#define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 ) -#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) -#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) -#define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 ) -#define MINISRC_BIQUAD_STAGE 2 -#define MINISRC_COEF_LOC 0X175 - -#define DMACONTROL_BLOCK_MASK 0x000F -#define DMAC_BLOCK0_SELECTOR 0x0000 -#define DMAC_BLOCK1_SELECTOR 0x0001 -#define DMAC_BLOCK2_SELECTOR 0x0002 -#define DMAC_BLOCK3_SELECTOR 0x0003 -#define DMAC_BLOCK4_SELECTOR 0x0004 -#define DMAC_BLOCK5_SELECTOR 0x0005 -#define DMAC_BLOCK6_SELECTOR 0x0006 -#define DMAC_BLOCK7_SELECTOR 0x0007 -#define DMAC_BLOCK8_SELECTOR 0x0008 -#define DMAC_BLOCK9_SELECTOR 0x0009 -#define DMAC_BLOCKA_SELECTOR 0x000A -#define DMAC_BLOCKB_SELECTOR 0x000B -#define DMAC_BLOCKC_SELECTOR 0x000C -#define DMAC_BLOCKD_SELECTOR 0x000D -#define DMAC_BLOCKE_SELECTOR 0x000E -#define DMAC_BLOCKF_SELECTOR 0x000F -#define DMACONTROL_PAGE_MASK 0x00F0 -#define DMAC_PAGE0_SELECTOR 0x0030 -#define DMAC_PAGE1_SELECTOR 0x0020 -#define DMAC_PAGE2_SELECTOR 0x0010 -#define DMAC_PAGE3_SELECTOR 0x0000 -#define DMACONTROL_AUTOREPEAT 0x1000 -#define DMACONTROL_STOPPED 0x2000 -#define DMACONTROL_DIRECTION 0x0100 - - -/* - * DSP Code images - */ - -u16 assp_kernel_image[] = { - 0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, - 0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, - 0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, - 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, - 0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, - 0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, - 0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, - 0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, - 0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, - 0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, - 0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, - 0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, - 0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, - 0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, - 0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, - 0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, - 0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, - 0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, - 0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, - 0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, - 0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, - 0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, - 0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, - 0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, - 0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, - 0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, - 0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, - 0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, - 0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, - 0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, - 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, - 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, - 0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, - 0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, - 0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, - 0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, - 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, - 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, - 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, - 0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, - 0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, - 0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, - 0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, - 0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, - 0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, - 0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, - 0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, - 0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, - 0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, - 0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, - 0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, - 0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, - 0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, - 0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, - 0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, - 0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, - 0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, - 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, - 0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, - 0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, - 0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, - 0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, - 0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, - 0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, - 0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, - 0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, - 0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, - 0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, - 0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, - 0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, - 0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, - 0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, - 0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, - 0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, - 0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, - 0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, - 0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, - 0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, - 0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, - 0xBE3A, -}; - -/* - * Mini sample rate converter code image - * that is to be loaded at 0x400 on the DSP. - */ -u16 assp_minisrc_image[] = { - - 0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, - 0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, - 0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, - 0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, - 0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, - 0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, - 0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, - 0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, - 0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, - 0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, - 0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, - 0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, - 0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, - 0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, - 0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, - 0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, - 0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, - 0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, - 0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, - 0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, - 0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, - 0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, - 0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, - 0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, - 0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, - 0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, - 0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, - 0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, - 0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, - 0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, - 0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, - 0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - diff -Nru a/drivers/sound/maestro_tables.h b/drivers/sound/maestro_tables.h --- a/drivers/sound/maestro_tables.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,333 +0,0 @@ -/* - * Set up data block for the ESS soundblaster emulation. This is like - * the example code from ftp.esstech.com.tw (104T31.ZIP) - */ - -u16 asp_block_0[]= { - 0x7980, 0x003a, 0x7980, 0x007d, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, - 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, - 0x7980, 0x0be6, 0x7980, 0x0069, 0xbe3a, 0x8b00, 0x8048, 0x6945, - 0xb801, 0x9045, 0x6941, 0xe388, 0x0031, 0x6944, 0xba50, 0xe38c, - 0x0031, 0x8b88, 0x6942, 0x304a, 0xe308, 0x0028, 0x6949, 0x9042, - 0x0042, 0xafa0, 0x8000, 0xafa0, 0x8000, 0x8042, 0x6944, 0xb801, - 0x9044, 0x0048, 0xbe3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0xbe41, 0xbf80, 0x0101, 0x8806, 0x8804, 0xb912, - 0x8807, 0xbc26, 0xbe40, 0xb92f, 0x9001, 0x9002, 0x003b, 0x0122, - 0x7a88, 0x0055, 0x003a, 0x013c, 0x7a88, 0x0055, 0x4902, 0xe100, - 0x0800, 0x0c02, 0xa000, 0x7980, 0x004e, 0xb908, 0x8809, 0xbec6, - 0x0067, 0x4a80, 0xe200, 0x0065, 0x4880, 0xe200, 0x0063, 0xb97f, - 0x6e80, 0x7980, 0x0066, 0x1089, 0x9088, 0xb900, 0x90a9, 0x8ba8, - 0xef00, 0x803d, 0x0003, 0x1007, 0x881f, 0x8b88, 0xbb0f, 0xada0, - 0x0810, 0x9003, 0x4903, 0xe200, 0x007b, 0x9002, 0x6a05, 0x6d04, - 0x9803, 0x9804, 0x9005, 0x003d, 0xbe3a, 0xaf06, 0x0000, 0x803d, - 0x8b88, 0x6906, 0x8810, 0xaf06, 0x0001, 0xbfb0, 0x00ff, 0xbaf5, - 0xe3cc, 0x009a, 0x5e06, 0x003f, 0xbf80, 0x0080, 0x4a06, 0xe200, - 0x0095, 0x6e80, 0x6d06, 0x7980, 0x009b, 0x9080, 0x0810, 0xba46, - 0x8810, 0x8b00, 0x1006, 0x9080, 0x003d, 0xbe3a, 0x1024, 0x2009, - 0x8810, 0x8b88, 0x8b00, 0x1089, 0xbfb0, 0x000e, 0xbe0a, 0x880d, - 0x903e, 0x1038, 0x2008, 0x8810, 0x1037, 0x2008, 0x8811, 0x5f3e, - 0x0000, 0x8b00, 0xf500, 0x5e80, 0xfffe, 0x1039, 0x2008, 0x8815, - 0x8b8d, 0x0231, 0x0333, 0x108a, 0x90ad, 0x1025, 0x200a, 0x8815, - 0x0605, 0xb91f, 0x8809, 0x4f16, 0x1080, 0xf600, 0xbfb0, - 0x0003, 0xbfb0, 0x0007, 0x9089, 0xbe47, 0xbe43, 0xbec6, - 0x00f6, 0x4f8b, 0x14a8, 0xe500, 0x6380, 0x8b89, 0x4e8a, - 0xbfe3, 0xe500, 0x2480, 0xbe46, 0x8b9d, 0xbfe7, 0xbfb0, - 0x00ff, 0x288c, 0x2026, 0x8814, 0xbe47, 0x8b00, 0x738f, - 0x54aa, 0xbe03, 0xbe0a, 0x2fa8, 0x988a, 0x8da9, 0xbe03, - 0x4d8e, 0xbfe1, 0xf500, 0x6180, 0x9880, 0x8ba9, 0xbe03, - 0x4c8e, 0xbfe1, 0xf500, 0x6180, 0x9880, 0x8ba9, 0xbe42, - 0x1039, 0x2008, 0x8815, 0x8b8d, 0x8b00, 0x8d8a, 0x7980, - 0x0bcf}; - -u16 asp_block_1[]={ - 0x0005, 0xb900, 0x9008, 0x9009, 0x900a, 0xbb3f, 0x90a0, - 0x001a, 0x011b, 0x021c, 0x0323, 0x1089, 0x9015, 0x108a, - 0x9016, 0x108b, 0x9017, 0x1088, 0x9018, 0x0137, 0x0524, - 0x8b8d, 0x4f16, 0xe200, 0x0827, 0x4f15, 0xe200, 0x0827, - 0x7a80, 0x08e6, 0x102c, 0xb802, 0x8813, 0x8b8b, 0xb900, - 0x90a0, 0x908d, 0x7980, 0x0833, 0x7a80, 0x0913, 0x7803, - 0x7a80, 0x0913, 0x102c, 0xb802, 0x8813, 0x8b8b, 0xb903, - 0x90a0, 0x908d, 0x7c02, 0x4f16, 0xe200, 0x0845, 0x4e15, - 0xe200, 0x0845, 0x7a80, 0x08e6, 0x102c, 0xb806, 0x8813, - 0x8b8b, 0xb901, 0x90a0, 0x908d, 0x7980, 0x0851, 0x7a80, - 0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, 0xb806, 0x8813, - 0x8b8b, 0xb904, 0x90a0, 0x908d, 0x7c02, 0x4f16, 0xe200, - 0x0863, 0x4d15, 0xe200, 0x0863, 0x7a80, 0x08e6, 0x102c, - 0xb80a, 0x8813, 0x8b8b, 0xb902, 0x90a0, 0x908d, 0x7980, - 0x086f, 0x7a80, 0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, - 0xb80a, 0x8813, 0x8b8b, 0xb905, 0x90a0, 0x908d, 0x7801, - 0x7a80, 0x0913, 0x7801, 0x7a80, 0x0913, 0x7801, 0x7a80, - 0x0913, 0x1024, 0xbf90, 0x0100, 0x8815, 0x4f16, 0xe200, - 0x088e, 0x4c15, 0xe200, 0x088e, 0x7a80, 0x08e6, 0x102c, - 0xb814, 0x8813, 0x8b8b, 0xbf80, 0x0100, 0x90a0, 0x908d, - 0x7980, 0x089b, 0x7a80, 0x0913, 0x7803, 0x7a80, 0x0913, - 0x102c, 0xb814, 0x8813, 0x8b8b, 0xbf80, 0x0103, 0x90a0, - 0x908d, 0x7c02, 0x4f16, 0xe200, 0x08ae, 0x4b15, 0xe200, - 0x08ae, 0x7a80, 0x08e6, 0x102c, 0xb818, 0x8813, 0x8b8b, - 0xbf80, 0x0101, 0x90a0, 0x908d, 0x7980, 0x08bb, 0x7a80, - 0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, 0xb818, 0x8813, - 0x8b8b, 0xbf80, 0x0104, 0x90a0, 0x908d, 0x7c02, 0x4f16, - 0xe200, 0x08ce, 0x4a15, 0xe200, 0x08ce, 0x7a80, 0x08e6, - 0x102c, 0xb81c, 0x8813, 0x8b8b, 0xbf80, 0x0102, 0x90a0, - 0x908d, 0x7980, 0x08db, 0x7a80, 0x0913, 0x7803, 0x7a80, - 0x0913, 0x102c, 0xb81c, 0x8813, 0x8b8b, 0xbf80, 0x0105, - 0x90a0, 0x908d, 0x7801, 0x7a80, 0x0913, 0x7801, 0x7a80, - 0x0913, 0x7801, 0x7a80, 0x0913, 0x7980, 0x0920, 0x4f80, - 0x7803, 0xe100, 0x08fe, 0x4f89, 0xe100, 0x08f5, 0xb901, - 0x90a0, 0xb902, 0x90a0, 0x90a0, 0xb906, 0x90ad, 0xef00, - 0xb901, 0x90a0, 0xb906, 0x90a0, 0xb900, 0x90a0, 0xb906, - 0x90ad, 0xef00, 0x4f89, 0xe100, 0x090a, 0xb905, 0x90a0, - 0xb900, 0x90a0, 0xb902, 0x90a0, 0xb906, 0x90ad, 0xef00, - 0xb905, 0x90a0, 0xb900, 0x90a0, 0xb906, 0x90a0, 0xb904, - 0x90ad, 0xef00, 0x4f89, 0xe100, 0x091b, 0xb901, 0x90a0, - 0xb906, 0x90ad, 0xef00, 0xb905, 0x90a0, 0xb904, 0x90ad, - 0xef00, 0xb91f, 0x8809, 0x0034, 0x8b88, 0xbec6, 0x0932, - 0x1313, 0xbe1e, 0x1014, 0xbe1a, 0xbe01, 0xbfe8, 0xbe17, - 0x6a13, 0x6214, 0xbe14, 0x9813, 0x9014, 0x98a0, 0xbe47, - 0x5f0f, 0x002e, 0xe200, 0x093d, 0xbf80, 0xffd2, 0x900f, - 0x7980, 0x0940, 0x100f, 0xb801, 0x900f, 0x400f, 0x8b00, - 0xe500, 0xbe01, 0xbe09, 0x9010, 0xbe46, 0x5f11, 0x003f, - 0xe200, 0x094f, 0xb900, 0x9011, 0x7980, 0x0952, 0x1011, - 0xb801, 0x9011, 0x1001, 0xe388, 0x0bcf, 0x1021, 0x2009, - 0x8813, 0x8b8b, 0x8b00, 0x1080, 0xbfe6, 0x7810, 0x8b00, - 0x8b00, 0x2180, 0xbfb0, 0x0007, 0x4c11, 0x8b00, 0xe100, - 0x096c, 0x4b11, 0x8b00, 0xe600, 0xb900, 0x7980, 0x096d, - 0xbe0a, 0x4a11, 0x8b00, 0xe500, 0xbe01, 0x9012, 0x1037, - 0x2008, 0x8811, 0x102e, 0x2008, 0x8812, 0x4a89, 0xb901, - 0xe500, 0x9019, 0xe100, 0x09c9, 0xb900, 0x9019, 0x4a18, - 0xe200, 0x09c9, 0x5f0a, 0x0010, 0xe200, 0x098d, 0x4b18, - 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, 0x0013, - 0xe200, 0x0997, 0x4b18, 0xb901, 0xe500, 0x9019, 0x7980, - 0x09c9, 0x5f0a, 0x0011, 0xe200, 0x09a4, 0x4f18, 0xb905, - 0xe500, 0x9080, 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, - 0x5f0a, 0x0014, 0xe200, 0x09b1, 0x4c18, 0xb904, 0xe500, - 0x9080, 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, - 0x0012, 0xe200, 0x09be, 0x4d18, 0xb905, 0xe500, 0x9080, - 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, 0x0015, - 0xe200, 0x09c9, 0x4e18, 0xb904, 0xe500, 0x9080, 0xb901, - 0xe500, 0x9019, 0x8b8a, 0x4f19, 0xe200, 0x09d4, 0x4b80, - 0xe200, 0x09d6, 0x5d80, 0x0020, 0x7980, 0x09d9, 0x5d80, - 0x0010, 0x4a80, 0xe200, 0x0bca, 0x1001, 0xba01, 0x9001, - 0x1024, 0x2009, 0x8815, 0x8b89, 0x4d8d, 0xe200, 0x09f8, - 0x4f16, 0xe100, 0x09ed, 0x8b89, 0x1080, 0xbfc0, 0x000c, - 0x908c, 0x7980, 0x09f8, 0x4b89, 0x108d, 0xf600, 0xbfb0, - 0x0003, 0x4a89, 0x8b00, 0xf500, 0xbfc0, 0x0008, 0x908c, - 0x1022, 0x2009, 0x8811, 0x102e, 0x2008, 0x8812, 0x102f, - 0x2008, 0x8813, 0x1020, 0x200a, 0x8814, 0x101d, 0x200a, - 0x8815, 0x8b8a, 0x4f19, 0xe100, 0x0a11, 0x5e80, 0x0020, - 0x5d80, 0x0018, 0x7980, 0x0a3e, 0x4f80, 0xe200, 0x0a1e, - 0x8b8b, 0x108a, 0xbfa0, 0x7fc0, 0x8b00, 0xf704, 0x5c80, - 0x0003, 0x7980, 0x0a3e, 0x4e80, 0xe200, 0x0a36, 0x8b8c, - 0x178b, 0xbe01, 0xbfb7, 0x00f0, 0x308a, 0xe344, 0x0a3e, - 0x8b8d, 0x4a8a, 0x8b00, 0xe200, 0x0a32, 0x5c80, 0x0006, - 0x7980, 0x0a3e, 0x5c80, 0x000a, 0x7980, 0x0a3e, 0x4c80, - 0xe200, 0x0a3e, 0x4b80, 0x8b00, 0xf500, 0x5c80, 0x0019, - 0x4f80, 0xe200, 0x0a4d, 0x101f, 0x200a, 0x8816, 0x8b00, - 0x8b00, 0x8b8e, 0x1780, 0xbfb7, 0x00f0, 0x900b, 0x7980, - 0x0a64, 0x4e80, 0xe200, 0x0a5c, 0x101f, 0x200a, 0x8816, - 0x8b00, 0x8b00, 0x8b8e, 0x1b80, 0xbfbb, 0x000f, 0x900b, - 0x7980, 0x0a64, 0x4c80, 0xe200, 0x0a64, 0x8b8c, 0x1b80, - 0xbfbb, 0x000f, 0x900b, 0x8b89, 0x1880, 0xbfb8, 0x001c, - 0x4917, 0xe100, 0x0a6e, 0x4f80, 0x7980, 0x0a70, 0x4e80, - 0x8b00, 0xf500, 0xbf98, 0x0002, 0x8b8d, 0x4b89, 0x8b00, - 0xe600, 0xbfe1, 0x903e, 0xbe43, 0x6a0b, 0x613e, 0xbfe8, - 0x980c, 0xbe42, 0x7c10, 0x1080, 0xbfe5, 0xbe1e, 0x7810, - 0x1280, 0xbfb2, 0x001f, 0xbe11, 0x2027, 0x8811, 0x101e, - 0x200a, 0x8816, 0x8b00, 0x128e, 0x4980, 0xe100, 0x0a9b, - 0x4880, 0xe100, 0x0a98, 0xb900, 0x7980, 0x0a9f, 0xbe0a, - 0x7980, 0x0a9f, 0x4880, 0xe200, 0x0a9f, 0xbe09, 0xbe1e, - 0x158d, 0xbfb5, 0x003f, 0xbe11, 0x4889, 0xe200, 0x0aae, - 0xbe1e, 0x1010, 0x4818, 0x8b00, 0xe600, 0xbfe1, 0xbe11, - 0xbfe1, 0x8811, 0xbe1e, 0xb9ff, 0x8819, 0x8b00, 0x8b00, - 0xbf46, 0x8b00, 0xe600, 0xbe1f, 0xbe01, 0xbfb0, 0x00ff, - 0x202a, 0x8811, 0x8b00, 0x8b00, 0x108a, 0x900d, 0xb91f, - 0x8809, 0x0732, 0x730d, 0x4f80, 0xe200, 0x0ad8, 0x1028, - 0x200c, 0x8815, 0xbe43, 0x8b8b, 0x6a8d, 0xbec6, 0x0ad4, - 0x618b, 0x9880, 0x548f, 0x8dad, 0xbe42, 0x7980, 0x0b24, - 0x4e80, 0xe200, 0x0af9, 0x8b8c, 0x178b, 0xbe01, 0xbfb7, - 0x00f0, 0x903e, 0x1029, 0x200c, 0x8815, 0x108d, 0xbec6, - 0x0af6, 0x308b, 0xbe1e, 0x303e, 0x903f, 0x403f, 0xbe1f, - 0xe500, 0x103e, 0x9080, 0xbfe6, 0x202a, 0x8811, 0x8b00, - 0x1089, 0x548f, 0x8dad, 0x7980, 0x0b24, 0x4c8b, 0xe200, - 0x0b1b, 0x1029, 0x200c, 0x8815, 0xbf80, 0x007f, 0x903e, - 0x108d, 0xbec6, 0x0b14, 0x308b, 0xbe1e, 0x303e, 0x903f, - 0x403f, 0xbe1f, 0xe500, 0xb900, 0x9080, 0xbfe6, 0x202a, - 0x8811, 0x8b00, 0x1089, 0x548f, 0x8dad, 0x8b8a, 0xf500, - 0x5e80, 0xffdf, 0x7980, 0x0b24, 0x1089, 0xbfe6, 0x202a, - 0x8811, 0x8b00, 0x8b00, 0x548f, 0xbb1f, 0x8da0, 0x8b8f, - 0x0732, 0x1021, 0x2009, 0x8811, 0x8b89, 0x101d, 0x200a, - 0x8812, 0x1080, 0x7810, 0x8b00, 0xbe47, 0x288a, 0xbfb0, - 0x03ff, 0x4989, 0xe200, 0x0b3e, 0xbe1e, 0x1012, 0x4918, - 0x8b00, 0xe600, 0xbe0a, 0xbe11, 0x900e, 0xbe46, 0x108a, - 0xbfb0, 0x001c, 0xbfe1, 0x880d, 0x8b00, 0x6b0e, 0xbe0a, - 0x880c, 0x108c, 0xbfb0, 0x000f, 0x202d, 0x8814, 0x8b00, - 0x8b00, 0x5589, 0xbf03, 0x8c0e, 0xbf00, 0x1030, 0x2008, - 0x8811, 0xb91f, 0x8809, 0x108b, 0x0333, 0xbec6, 0x0b5f, - 0x200e, 0x90a0, 0x8b00, 0x8b89, 0x9080, 0x4a18, 0xe200, - 0x0b7a, 0x5f0a, 0x0011, 0xe200, 0x0b6c, 0x4f18, 0xe900, - 0x0b7c, 0x5f0a, 0x0014, 0xe200, 0x0b73, 0x4c18, 0xe900, - 0x0b9b, 0x5f0a, 0x0015, 0xe200, 0x0b7a, 0x4e18, 0xe900, - 0x0bb2, 0x7980, 0x009e, 0x0034, 0xb91f, 0x8809, 0x0333, - 0x8b8b, 0xbec6, 0x0b99, 0x1280, 0x6c80, 0xbfea, 0xbe1e, - 0x1580, 0x6c88, 0xbfec, 0xbe13, 0x903e, 0x6cab, 0x903f, - 0x4f3e, 0xb900, 0xf500, 0xbf80, 0x8000, 0x4f3f, 0xbf90, - 0x0d00, 0xf500, 0xbf90, 0x2700, 0x90a0, 0xef00, 0x0034, - 0xb91f, 0x8809, 0x0333, 0x8b88, 0xbec6, 0x0bb0, 0x1eab, - 0x6c80, 0xbfed, 0x903e, 0x4180, 0xb900, 0xf500, 0xbf80, - 0x8000, 0x4f3e, 0x8b00, 0xf500, 0xbf90, 0x4000, 0x90a8, - 0xef00, 0x0034, 0xb91f, 0x8809, 0x0333, 0x8b8b, 0xbec6, - 0x0bc8, 0x1280, 0x6c8b, 0xbfea, 0xbe1e, 0x1580, 0x6c80, - 0xbfec, 0xbe13, 0x903e, 0x4f3e, 0xbf80, 0x4000, 0xf500, - 0xbf90, 0x8000, 0x90a0, 0xef00, 0x0231, 0x8b8a, 0xb900, - 0xbb20, 0x90a0, 0x5f08, 0x0023, 0xe100, 0x0043, 0x1008, - 0xb801, 0x9008, 0x102b, 0x2008, 0x8812, 0x8b00, 0x8b00, - 0x1080, 0x900a, 0x102c, 0x2008, 0x8812, 0x8b00, 0x8b00, - 0x1080, 0x9009, 0x7980, 0x0952, 0x8148, 0x6946, 0xb801, - 0x9046, 0xb901, 0x9041, 0x6944, 0xba08, 0xe344, 0x0bfe, - 0x9044, 0x8b89, 0x0143, 0x694b, 0x881f, 0xbb0f, 0xada0, - 0x8143, 0x0811, 0x304a, 0xe308, 0x0bfe, 0x6949, 0x9043, - 0x0148, 0xbe3a -}; - -u16 asp_block_2[]={ - 0x0000, 0x0000, 0x0003, 0x0003, 0x0001, 0x0001, - 0x0004, 0x0004, 0x0002, 0x0002, 0x0005, 0x0005, - 0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, - 0x0100, 0x0100, 0x0103, 0x0103, 0x0101, 0x0101, - 0x0104, 0x0104, 0x0102, 0x0102, 0x0105, 0x0105, - 0x0106, 0x0106, 0x0107, 0x0107, 0x0108, 0x0108 -}; - -u16 asp_block_3[]={ - 0x0000, 0x0000, 0x0000, 0x1200, 0x1200, 0x1280, 0x0000, 0x05d0, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x1104, 0x1105, 0x1008, 0x1020, 0x1040, 0x1060, - 0x1080, 0x10a0, 0x10b0, 0x100d, 0x1010, 0x10e0, 0x2000, 0x2980, - 0x2b00, 0x2b40, 0x2a00, 0x2b90, 0x13dc, 0x2b80, 0x11bc, 0x134c, - 0x1370, 0x12e0, 0x1240, 0x1260, 0x12c0, 0x009e, 0x0045, 0x10bc, - 0x1394, 0x13b8, 0x11f6, 0x10f6, 0x11b0, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0815, 0x0956, 0x09df, 0x0be5, 0x0a19, 0x0a48, 0x0b37, - 0x0b5d, 0x0a8b, 0x0aae, 0x0ad2 }; - - -u16 asp_block_4[] = { - 0x0192, 0x04b6, 0x07d9, 0x0afb, 0x0e1c, 0x113a, 0x1455, 0x176e, - 0x1a83, 0x1d93, 0x209f, 0x23a7, 0x26a8, 0x29a4, 0x2c99, 0x2f87, - 0x326e, 0x354e, 0x3825, 0x3af3, 0x3db8, 0x4074, 0x4326, 0x45cd, - 0x486a, 0x4afb, 0x4d81, 0x4ffb, 0x5269, 0x54ca, 0x571e, 0x5964, - 0x5b9d, 0x5dc8, 0x5fe4, 0x61f1, 0x63ef, 0x65de, 0x67bd, 0x698c, - 0x6b4b, 0x6cf9, 0x6e97, 0x7023, 0x719e, 0x7308, 0x7460, 0x75a6, - 0x76d9, 0x77fb, 0x790a, 0x7a06, 0x7aef, 0x7bc6, 0x7c89, 0x7d3a, - 0x7dd6, 0x7e60, 0x7ed6, 0x7f38, 0x7f87, 0x7fc2, 0x7fea, 0x7ffe, - 0x7ffe, 0x7fea, 0x7fc2, 0x7f87, 0x7f38, 0x7ed6, 0x7e60, 0x7dd6, - 0x7d3a, 0x7c89, 0x7bc6, 0x7aef, 0x7a06, 0x790a, 0x77fb, 0x76d9, - 0x75a6, 0x7460, 0x7308, 0x719e, 0x7023, 0x6e97, 0x6cf9, 0x6b4b, - 0x698c, 0x67bd, 0x65de, 0x63ef, 0x61f1, 0x5fe4, 0x5dc8, 0x5b9d, - 0x5964, 0x571e, 0x54ca, 0x5269, 0x4ffb, 0x4d81, 0x4afb, 0x486a, - 0x45cd, 0x4326, 0x4074, 0x3db8, 0x3af3, 0x3825, 0x354e, 0x326e, - 0x2f87, 0x2c99, 0x29a4, 0x26a8, 0x23a7, 0x209f, 0x1d93, 0x1a83, - 0x176e, 0x1455, 0x113a, 0x0e1c, 0x0afb, 0x07d9, 0x04b6, 0x0192, - 0xfe6f, 0xfb4b, 0xf828, 0xf506, 0xf1e5, 0xeec7, 0xebac, 0xe893, - 0xe57e, 0xe26e, 0xdf62, 0xdc5a, 0xd959, 0xd65d, 0xd368, 0xd07a, - 0xcd93, 0xcab3, 0xc7dc, 0xc50e, 0xc249, 0xbf8d, 0xbcdb, 0xba34, - 0xb797, 0xb506, 0xb280, 0xb006, 0xad98, 0xab37, 0xa8e3, 0xa69d, - 0xa464, 0xa239, 0xa01d, 0x9e10, 0x9c12, 0x9a23, 0x9844, 0x9675, - 0x94b6, 0x9308, 0x916a, 0x8fde, 0x8e63, 0x8cf9, 0x8ba1, 0x8a5b, - 0x8928, 0x8806, 0x86f7, 0x85fb, 0x8512, 0x843b, 0x8378, 0x82c7, - 0x822b, 0x81a1, 0x812b, 0x80c9, 0x807a, 0x803f, 0x8017, 0x8003, - 0x8003, 0x8017, 0x803f, 0x807a, 0x80c9, 0x812b, 0x81a1, 0x822b, - 0x82c7, 0x8378, 0x843b, 0x8512, 0x85fb, 0x86f7, 0x8806, 0x8928, - 0x8a5b, 0x8ba1, 0x8cf9, 0x8e63, 0x8fde, 0x916a, 0x9308, 0x94b6, - 0x9675, 0x9844, 0x9a23, 0x9c12, 0x9e10, 0xa01d, 0xa239, 0xa464, - 0xa69d, 0xa8e3, 0xab37, 0xad98, 0xb006, 0xb280, 0xb506, 0xb797, - 0xba34, 0xbcdb, 0xbf8d, 0xc249, 0xc50e, 0xc7dc, 0xcab3, 0xcd93, - 0xd07a, 0xd368, 0xd65d, 0xd959, 0xdc5a, 0xdf62, 0xe26e, 0xe57e, - 0xe893, 0xebac, 0xeec7, 0xf1e5, 0xf506, 0xf828, 0xfb4b, 0xfe6f, - 0x7cc3, 0x725e, 0x68d5, 0x6017, 0x5813, 0x50b9, 0x49fb, 0x43cd, - 0x3e22, 0x38ef, 0x342b, 0x2fcc, 0x2bc9, 0x281c, 0x24be, 0x21a6, - 0x1ed1, 0x1c37, 0x19d5, 0x17a6, 0x15a5, 0x13ce, 0x121f, 0x1093, - 0x0f28, 0x0ddc, 0x0cab, 0x0b93, 0x0a92, 0x09a7, 0x08cf, 0x080a, - 0x0754, 0x06ae, 0x0615, 0x0589, 0x0509, 0x0494, 0x0428, 0x03c5, - 0x036a, 0x0317, 0x02cb, 0x0285, 0x0245, 0x020a, 0x01d4, 0x01a2, - 0x0175, 0x014b, 0x0125, 0x0102, 0x00e2, 0x00c5, 0x00aa, 0x0091, - 0x007b, 0x0066, 0x0053, 0x0041, 0x0031, 0x0022, 0x0015, 0x0009, - 0xfffe, 0xfff2, 0xffe5, 0xffd7, 0xffc8, 0xffb7, 0xffa5, 0xff91, - 0xff7b, 0xff64, 0xff4a, 0xff2e, 0xff0f, 0xfeee, 0xfec9, 0xfea1, - 0xfe76, 0xfe46, 0xfe13, 0xfdda, 0xfd9d, 0xfd5a, 0xfd11, 0xfcc1, - 0xfc6b, 0xfc0c, 0xfba5, 0xfb34, 0xfab9, 0xfa33, 0xf9a1, 0xf902, - 0xf854, 0xf797, 0xf6c8, 0xf5e7, 0xf4f1, 0xf3e5, 0xf2c1, 0xf183, - 0xf027, 0xeeac, 0xed0f, 0xeb4d, 0xe961, 0xe74a, 0xe501, 0xe284, - 0xdfcd, 0xdcd8, 0xd99d, 0xd618, 0xd242, 0xce12, 0xc982, 0xc487, - 0xbf1a, 0xb92e, 0xb2ba, 0xabaf, 0xa402, 0x9ba3, 0x9282, 0x888d, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e, 0x0010, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x000a, 0x000e, - 0x0010, 0x0014, 0x0016, 0x0018, 0x001a, 0x001c, 0x001e, 0x0020, - 0x0000, 0x0000, 0x0000, 0x000a, 0x0010, 0x0016, 0x001a, 0x001e, - 0x0020, 0x0024, 0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, - 0x0000, 0x0000, 0x0010, 0x001a, 0x0020, 0x0026, 0x002a, 0x002e, - 0x0030, 0x0034, 0x0036, 0x0038, 0x003a, 0x003c, 0x003e, 0x0040, - 0x0000, 0x0010, 0x0020, 0x002a, 0x0030, 0x0036, 0x003a, 0x003e, - 0x0040, 0x0044, 0x0046, 0x0048, 0x004a, 0x004c, 0x004e, 0x0050, - 0x0000, 0x0020, 0x0030, 0x003a, 0x0040, 0x0046, 0x004a, 0x004e, - 0x0050, 0x0054, 0x0056, 0x0058, 0x005a, 0x005c, 0x005e, 0x0060, - 0x0000, 0x0030, 0x0040, 0x004a, 0x0050, 0x0056, 0x005a, 0x005e, - 0x0060, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, 0x0070, - 0x0008, 0x0021, 0x0042, 0x0064, 0x0086, 0x00a8, 0x00cb, 0x00ee, - 0x0111, 0x0135, 0x0158, 0x017d, 0x01a1, 0x01c6, 0x01eb, 0x0211, - 0x0236, 0x025d, 0x0283, 0x02aa, 0x02d1, 0x02f9, 0x0321, 0x0349, - 0x0372, 0x039b, 0x03c4, 0x03ee, 0x0418, 0x0442, 0x046d, 0x0499, - 0x04c4, 0x04f0, 0x051d, 0x054a, 0x0577, 0x05a5, 0x05d3, 0x0601, - 0x0630, 0x0660, 0x0690, 0x06c0, 0x06f1, 0x0722, 0x0753, 0x0785, - 0x07b8, 0x07eb, 0x081e, 0x0852, 0x0886, 0x08bb, 0x08f0, 0x0926, - 0x095c, 0x0993, 0x09ca, 0x0a02, 0x0a3a, 0x0a73, 0x0aac, 0x0ae6, - 0x0b20, 0x0b5b, 0x0b96, 0x0bd2, 0x0c0e, 0x0c4b, 0x0c89, 0x0cc7, - 0x0d05, 0x0d44, 0x0d84, 0x0dc5, 0x0e05, 0x0e47, 0x0e89, 0x0ecc, - 0x0f0f, 0x0f53, 0x0f97, 0x0fdd, 0x1022, 0x1069, 0x10b0, 0x10f7, - 0x1140, 0x1189, 0x11d2, 0x121c, 0x1267, 0x12b3, 0x12ff, 0x134c, - 0x139a, 0x13e8, 0x1438, 0x1487, 0x14d8, 0x1529, 0x157b, 0x15ce, - 0x1621, 0x1676, 0x16cb, 0x1720, 0x1777, 0x17ce, 0x1826, 0x187f, - 0x18d9, 0x1934, 0x198f, 0x19eb, 0x1a48, 0x1aa6, 0x1b05, 0x1b64, - 0x1bc5, 0x1c26, 0x1c88, 0x1ceb, 0x1d4f, 0x1db4, 0x1e1a, 0x1e80, - 0x1ee8, 0x1f51, 0x1fba, 0x2025, 0x2090, 0x20fc, 0x216a, 0x21d8, - 0x2247, 0x22b8, 0x2329, 0x239b, 0x240f, 0x2483, 0x24f9, 0x256f, - 0x25e7, 0x2660, 0x26da, 0x2755, 0x27d1, 0x284e, 0x28cc, 0x294b, - 0x29cc, 0x2a4e, 0x2ad1, 0x2b55, 0x2bda, 0x2c61, 0x2ce8, 0x2d71, - 0x2dfb, 0x2e87, 0x2f13, 0x2fa1, 0x3031, 0x30c1, 0x3153, 0x31e6, - 0x327b, 0x3310, 0x33a8, 0x3440, 0x34da, 0x3575, 0x3612, 0x36b0, - 0x3750, 0x37f1, 0x3893, 0x3937, 0x39dc, 0x3a83, 0x3b2c, 0x3bd6, - 0x3c81, 0x3d2e, 0x3ddd, 0x3e8d, 0x3f3f, 0x3ff2, 0x40a7, 0x415d, - 0x4216, 0x42d0, 0x438b, 0x4448, 0x4507, 0x45c8, 0x468b, 0x474f, - 0x4815, 0x48dd, 0x49a6, 0x4a72, 0x4b3f, 0x4c0e, 0x4cdf, 0x4db2, - 0x4e87, 0x4f5d, 0x5036, 0x5111, 0x51ed, 0x52cc, 0x53ac, 0x548f, - 0x5573, 0x565a, 0x5743, 0x582e, 0x591b, 0x5a0a, 0x5afb, 0x5bef, - 0x5ce4, 0x5ddc, 0x5ed7, 0x5fd3, 0x60d2, 0x61d3, 0x62d6, 0x63dc, - 0x64e4, 0x65ee, 0x66fb, 0x680a, 0x691c, 0x6a30, 0x6b47, 0x6c60, - 0x6d7c, 0x6e9a, 0x6fbb, 0x70de, 0x7204, 0x732d, 0x7459, 0x7587, - 0x76b8, 0x77eb, 0x7922, 0x7a5b, 0x7b97, 0x7cd6, 0x7e18, 0x7f5d, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, - 0x0003, 0x0004, 0x0004, 0x0005, 0x0006, 0x0008, 0x0009, 0x000a, - 0x000c, 0x0010, 0x0012, 0x0015, 0x0019, 0x0022, 0x0024, 0x002a, - 0x0031, 0x003e, 0x0049, 0x0055, 0x0062, 0x007c, 0x0092, 0x00a9, - 0x00c4, 0x00fc, 0x0125, 0x0152, 0x0187, 0x01f2, 0x024a, 0x02a4, - 0x030d, 0x03e3, 0x0492, 0x0547, 0x061b, 0x07c7, 0x0923, 0x0a8d, - 0x0c19, 0x0eb3, 0x1228, 0x14c1, 0x17fb, 0x1d17, 0x22f2, 0x2835, - 0x2dd4, 0x3cd0, 0x4cf5, 0x51cc, 0x7fff, 0x7fff, 0x7fff, 0x7fff, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, - 0x0002, 0x0003, 0x0004, 0x0005, 0x0005, 0x0007, 0x0008, 0x000a, - 0x000b, 0x000e, 0x0011, 0x0014, 0x0017, 0x001c, 0x0022, 0x0028, - 0x002e, 0x0039, 0x0045, 0x0050, 0x005c, 0x0073, 0x008a, 0x00a0, - 0x00b9, 0x00e7, 0x0114, 0x0141, 0x0172, 0x01ce, 0x0228, 0x0283, - 0x02e3, 0x039b, 0x0454, 0x0501, 0x05bf, 0x072a, 0x0899, 0x0a18, - 0x0b7e, 0x0e55, 0x10d3, 0x1404, 0x16c3, 0x16c3, 0x16c3, 0x16c3, - 0x0012, 0x0024, 0x0048, 0x006c, 0x0090, 0x00b4, 0x00d8, 0x00fc, - 0x0120, 0x0144, 0x0168, 0x0168, 0x01b0, 0x01b0, 0x021c, 0x021c, - 0x0000, 0x0003, 0x0008, 0x000b, 0x0001, 0x0004, 0x0009, 0x000c, - 0x0002, 0x0005, 0x000a, 0x000d, 0x0010, 0x0013, 0x0011, 0x0014, - 0x0012, 0x0015, 0x0100, 0x0103, 0x0108, 0x010b, 0x0101, 0x0104, - 0x0109, 0x010c, 0x0102, 0x0105, 0x010a, 0x010d, 0x0110, 0x0113, - 0x0111, 0x0114, 0x0112, 0x0115 }; diff -Nru a/drivers/sound/maui.c b/drivers/sound/maui.c --- a/drivers/sound/maui.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,477 +0,0 @@ -/* - * sound/maui.c - * - * The low level driver for Turtle Beach Maui and Tropez. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * Alan Cox General clean up, use kernel IRQ - * system - * Christoph Hellwig Adapted to module_init/module_exit - * Bartlomiej Zolnierkiewicz - * Added __init to download_code() - * - * Status: - * Andrew J. Kroll Tested 06/01/1999 with: - * * OSWF.MOT File Version: 1.15 - * * OSWF.MOT File Dated: 09/12/94 - * * Older versions will cause problems. - */ - -#include -#include -#include - -#define USE_SEQ_MACROS -#define USE_SIMPLE_MACROS - -#include "sound_config.h" -#include "sound_firmware.h" - -#include "mpu401.h" - -static int maui_base = 0x330; - -static volatile int irq_ok = 0; -static int *maui_osp; - -#define HOST_DATA_PORT (maui_base + 2) -#define HOST_STAT_PORT (maui_base + 3) -#define HOST_CTRL_PORT (maui_base + 3) - -#define STAT_TX_INTR 0x40 -#define STAT_TX_AVAIL 0x20 -#define STAT_TX_IENA 0x10 -#define STAT_RX_INTR 0x04 -#define STAT_RX_AVAIL 0x02 -#define STAT_RX_IENA 0x01 - -static int (*orig_load_patch) (int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) = NULL; - -#include "maui_boot.h" - -static int maui_wait(int mask) -{ - int i; - - /* - * Perform a short initial wait without sleeping - */ - - for (i = 0; i < 100; i++) - if (inb(HOST_STAT_PORT) & mask) - return 1; - - /* - * Wait up to 15 seconds with sleeping - */ - - for (i = 0; i < 150; i++) { - if (inb(HOST_STAT_PORT) & mask) - return 1; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ / 10); - if (signal_pending(current)) - return 0; - } - return 0; -} - -static int maui_read(void) -{ - if (maui_wait(STAT_RX_AVAIL)) - return inb(HOST_DATA_PORT); - return -1; -} - -static int maui_write(unsigned char data) -{ - if (maui_wait(STAT_TX_AVAIL)) { - outb((data), HOST_DATA_PORT); - return 1; - } - printk(KERN_WARNING "Maui: Write timeout\n"); - return 0; -} - -static void mauiintr(int irq, void *dev_id, struct pt_regs *dummy) -{ - irq_ok = 1; -} - -static int __init download_code(void) -{ - int i, lines = 0; - int eol_seen = 0, done = 0; - int skip = 1; - - printk(KERN_INFO "Code download (%d bytes): ", maui_osLen); - - for (i = 0; i < maui_osLen; i++) { - if (maui_os[i] != '\r') { - if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n'))) { - skip = 0; - - if (maui_os[i] == '\n') - eol_seen = skip = 1; - else if (maui_os[i] == 'S') { - if (maui_os[i + 1] == '8') - done = 1; - if (!maui_write(0xF1)) - goto failure; - if (!maui_write('S')) - goto failure; - } else { - if (!maui_write(maui_os[i])) - goto failure; - } - - if (eol_seen) { - int c = 0; - int n; - - eol_seen = 0; - - for (n = 0; n < 2; n++) { - if (maui_wait(STAT_RX_AVAIL)) { - c = inb(HOST_DATA_PORT); - break; - } - } - if (c != 0x80) { - printk("Download not acknowledged\n"); - return 0; - } - else if (!(lines++ % 10)) - printk("."); - - if (done) { - printk("\n"); - printk(KERN_INFO "Download complete\n"); - return 1; - } - } - } - } - } - -failure: - printk("\n"); - printk(KERN_ERR "Download failed!!!\n"); - return 0; -} - -static int __init maui_init(int irq) -{ - unsigned char bits; - - switch (irq) { - case 9: - bits = 0x00; - break; - case 5: - bits = 0x08; - break; - case 12: - bits = 0x10; - break; - case 15: - bits = 0x18; - break; - - default: - printk(KERN_ERR "Maui: Invalid IRQ %d\n", irq); - return 0; - } - outb((0x00), HOST_CTRL_PORT); /* Reset */ - outb((bits), HOST_DATA_PORT); /* Set the IRQ bits */ - outb((bits | 0x80), HOST_DATA_PORT); /* Set the IRQ bits again? */ - outb((0x80), HOST_CTRL_PORT); /* Leave reset */ - outb((0x80), HOST_CTRL_PORT); /* Leave reset */ - outb((0xD0), HOST_CTRL_PORT); /* Cause interrupt */ - -#ifdef CONFIG_SMP - { - int i; - for (i = 0; i < 1000000 && !irq_ok; i++) - ; - if (!irq_ok) - return 0; - } -#endif - outb((0x80), HOST_CTRL_PORT); /* Leave reset */ - - printk(KERN_INFO "Turtle Beach Maui initialization\n"); - - if (!download_code()) - return 0; - - outb((0xE0), HOST_CTRL_PORT); /* Normal operation */ - - /* Select mpu401 mode */ - - maui_write(0xf0); - maui_write(1); - if (maui_read() != 0x80) { - maui_write(0xf0); - maui_write(1); - if (maui_read() != 0x80) - printk(KERN_ERR "Maui didn't acknowledge set HW mode command\n"); - } - printk(KERN_INFO "Maui initialized OK\n"); - return 1; -} - -static int maui_short_wait(int mask) { - int i; - - for (i = 0; i < 1000; i++) { - if (inb(HOST_STAT_PORT) & mask) { - return 1; - } - } - return 0; -} - -static int maui_load_patch(int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) -{ - - struct sysex_info header; - unsigned long left, src_offs; - int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header; - int i; - - if (format == SYSEX_PATCH) /* Handled by midi_synth.c */ - return orig_load_patch(dev, format, addr, offs, count, pmgr_flag); - - if (format != MAUI_PATCH) - { - printk(KERN_WARNING "Maui: Unknown patch format\n"); - } - if (count < hdr_size) { -/* printk("Maui error: Patch header too short\n");*/ - return -EINVAL; - } - count -= hdr_size; - - /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. - */ - - if(copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs)) - return -EFAULT; - - if (count < header.len) { - printk(KERN_ERR "Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len); - header.len = count; - } - left = header.len; - src_offs = 0; - - for (i = 0; i < left; i++) { - unsigned char data; - - if(get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]))) - return -EFAULT; - if (i == 0 && !(data & 0x80)) - return -EINVAL; - - if (maui_write(data) == -1) - return -EIO; - } - - if ((i = maui_read()) != 0x80) { - if (i != -1) - printk("Maui: Error status %02x\n", i); - return -EIO; - } - return 0; -} - -static int __init probe_maui(struct address_info *hw_config) -{ - int i; - int tmp1, tmp2, ret; - - if (check_region(hw_config->io_base, 8)) - return 0; - - maui_base = hw_config->io_base; - maui_osp = hw_config->osp; - - if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0) - return 0; - - /* - * Initialize the processor if necessary - */ - - if (maui_osLen > 0) { - if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) || - !maui_write(0x9F) || /* Report firmware version */ - !maui_short_wait(STAT_RX_AVAIL) || - maui_read() == -1 || maui_read() == -1) - if (!maui_init(hw_config->irq)) { - free_irq(hw_config->irq, NULL); - return 0; - } - } - if (!maui_write(0xCF)) /* Report hardware version */ { - printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); - free_irq(hw_config->irq, NULL); - return 0; - } - if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) { - printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); - free_irq(hw_config->irq, NULL); - return 0; - } - if (tmp1 == 0xff || tmp2 == 0xff) { - free_irq(hw_config->irq, NULL); - return 0; - } - printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2); - - if (!maui_write(0x9F)) /* Report firmware version */ - return 0; - if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) - return 0; - - printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2); - - if (!maui_write(0x85)) /* Report free DRAM */ - return 0; - tmp1 = 0; - for (i = 0; i < 4; i++) { - tmp1 |= maui_read() << (7 * i); - } - printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024); - - for (i = 0; i < 1000; i++) - if (probe_mpu401(hw_config)) - break; - - ret = probe_mpu401(hw_config); - - if (ret) - request_region(hw_config->io_base + 2, 6, "Maui"); - - return ret; -} - -static void __init attach_maui(struct address_info *hw_config) -{ - int this_dev; - - conf_printf("Maui", hw_config); - - hw_config->irq *= -1; - hw_config->name = "Maui"; - attach_mpu401(hw_config, THIS_MODULE); - - if (hw_config->slots[1] != -1) /* The MPU401 driver installed itself */ { - struct synth_operations *synth; - - this_dev = hw_config->slots[1]; - - /* - * Intercept patch loading calls so that they can be handled - * by the Maui driver. - */ - - synth = midi_devs[this_dev]->converter; - synth->id = "MAUI"; - - if (synth != NULL) { - orig_load_patch = synth->load_patch; - synth->load_patch = &maui_load_patch; - } else - printk(KERN_ERR "Maui: Can't install patch loader\n"); - } -} - -static void __exit unload_maui(struct address_info *hw_config) -{ - int irq = hw_config->irq; - release_region(hw_config->io_base + 2, 6); - unload_mpu401(hw_config); - - if (irq < 0) - irq = -irq; - if (irq > 0) - free_irq(irq, NULL); -} - -static int fw_load = 0; - -static struct address_info cfg; - -static int __initdata io = -1; -static int __initdata irq = -1; - -MODULE_PARM(io,"i"); -MODULE_PARM(irq,"i"); - -/* - * Install a Maui card. Needs mpu401 loaded already. - */ - -static int __init init_maui(void) -{ - printk(KERN_INFO "Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n"); - - cfg.io_base = io; - cfg.irq = irq; - - if (cfg.io_base == -1 || cfg.irq == -1) { - printk(KERN_INFO "maui: irq and io must be set.\n"); - return -EINVAL; - } - - if (maui_os == NULL) { - fw_load = 1; - maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os); - } - if (probe_maui(&cfg) == 0) - return -ENODEV; - attach_maui(&cfg); - - return 0; -} - -static void __exit cleanup_maui(void) -{ - if (fw_load && maui_os) - vfree(maui_os); - unload_maui(&cfg); -} - -module_init(init_maui); -module_exit(cleanup_maui); - -#ifndef MODULE -static int __init setup_maui(char *str) -{ - /* io, irq */ - int ints[3]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - - return 1; -} - -__setup("maui=", setup_maui); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/midi_ctrl.h b/drivers/sound/midi_ctrl.h --- a/drivers/sound/midi_ctrl.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,22 +0,0 @@ -static unsigned char ctrl_def_values[128] = -{ - 0x40,0x00,0x40,0x40, 0x40,0x40,0x40,0x7f, /* 0 to 7 */ - 0x40,0x40,0x40,0x7f, 0x40,0x40,0x40,0x40, /* 8 to 15 */ - 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 16 to 23 */ - 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 24 to 31 */ - - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */ - - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */ - - 0x00,0x00,0x7f,0x7f, 0x7f,0x7f,0x00,0x00, /* 96 to 103 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */ -}; diff -Nru a/drivers/sound/midi_syms.c b/drivers/sound/midi_syms.c --- a/drivers/sound/midi_syms.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,31 +0,0 @@ -/* - * Exported symbols for midi driver. - * __NO_VERSION__ because this is still part of sound.o. - */ - -#define __NO_VERSION__ -#include - -char midi_syms_symbol; - -#include "sound_config.h" -#define _MIDI_SYNTH_C_ -#include "midi_synth.h" - -EXPORT_SYMBOL(do_midi_msg); -EXPORT_SYMBOL(midi_synth_open); -EXPORT_SYMBOL(midi_synth_close); -EXPORT_SYMBOL(midi_synth_ioctl); -EXPORT_SYMBOL(midi_synth_kill_note); -EXPORT_SYMBOL(midi_synth_start_note); -EXPORT_SYMBOL(midi_synth_set_instr); -EXPORT_SYMBOL(midi_synth_reset); -EXPORT_SYMBOL(midi_synth_hw_control); -EXPORT_SYMBOL(midi_synth_aftertouch); -EXPORT_SYMBOL(midi_synth_controller); -EXPORT_SYMBOL(midi_synth_panning); -EXPORT_SYMBOL(midi_synth_setup_voice); -EXPORT_SYMBOL(midi_synth_send_sysex); -EXPORT_SYMBOL(midi_synth_bender); -EXPORT_SYMBOL(midi_synth_load_patch); -EXPORT_SYMBOL(MIDIbuf_avail); diff -Nru a/drivers/sound/midi_synth.c b/drivers/sound/midi_synth.c --- a/drivers/sound/midi_synth.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,697 +0,0 @@ -/* - * sound/midi_synth.c - * - * High level midi sequencer manager for dumb MIDI interfaces. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Andrew Veliath : fixed running status in MIDI input state machine - */ -#define USE_SEQ_MACROS -#define USE_SIMPLE_MACROS - -#include "sound_config.h" - -#define _MIDI_SYNTH_C_ - -#include "midi_synth.h" - -static int midi2synth[MAX_MIDI_DEV]; -static int sysex_state[MAX_MIDI_DEV] = -{0}; -static unsigned char prev_out_status[MAX_MIDI_DEV]; - -#define STORE(cmd) \ -{ \ - int len; \ - unsigned char obuf[8]; \ - cmd; \ - seq_input_event(obuf, len); \ -} - -#define _seqbuf obuf -#define _seqbufptr 0 -#define _SEQ_ADVBUF(x) len=x - -void -do_midi_msg(int synthno, unsigned char *msg, int mlen) -{ - switch (msg[0] & 0xf0) - { - case 0x90: - if (msg[2] != 0) - { - STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2])); - break; - } - msg[2] = 64; - - case 0x80: - STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2])); - break; - - case 0xA0: - STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2])); - break; - - case 0xB0: - STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f, - msg[1], msg[2])); - break; - - case 0xC0: - STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1])); - break; - - case 0xD0: - STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1])); - break; - - case 0xE0: - STORE(SEQ_BENDER(synthno, msg[0] & 0x0f, - (msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7))); - break; - - default: - /* printk( "MPU: Unknown midi channel message %02x\n", msg[0]); */ - ; - } -} - -static void -midi_outc(int midi_dev, int data) -{ - int timeout; - - for (timeout = 0; timeout < 3200; timeout++) - if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff))) - { - if (data & 0x80) /* - * Status byte - */ - prev_out_status[midi_dev] = - (unsigned char) (data & 0xff); /* - * Store for running status - */ - return; /* - * Mission complete - */ - } - /* - * Sorry! No space on buffers. - */ - printk("Midi send timed out\n"); -} - -static int -prefix_cmd(int midi_dev, unsigned char status) -{ - if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL) - return 1; - - return midi_devs[midi_dev]->prefix_cmd(midi_dev, status); -} - -static void -midi_synth_input(int orig_dev, unsigned char data) -{ - int dev; - struct midi_input_info *inc; - - static unsigned char len_tab[] = /* # of data bytes following a status - */ - { - 2, /* 8x */ - 2, /* 9x */ - 2, /* Ax */ - 2, /* Bx */ - 1, /* Cx */ - 1, /* Dx */ - 2, /* Ex */ - 0 /* Fx */ - }; - - if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL) - return; - - if (data == 0xfe) /* Ignore active sensing */ - return; - - dev = midi2synth[orig_dev]; - inc = &midi_devs[orig_dev]->in_info; - - switch (inc->m_state) - { - case MST_INIT: - if (data & 0x80) /* MIDI status byte */ - { - if ((data & 0xf0) == 0xf0) /* Common message */ - { - switch (data) - { - case 0xf0: /* Sysex */ - inc->m_state = MST_SYSEX; - break; /* Sysex */ - - case 0xf1: /* MTC quarter frame */ - case 0xf3: /* Song select */ - inc->m_state = MST_DATA; - inc->m_ptr = 1; - inc->m_left = 1; - inc->m_buf[0] = data; - break; - - case 0xf2: /* Song position pointer */ - inc->m_state = MST_DATA; - inc->m_ptr = 1; - inc->m_left = 2; - inc->m_buf[0] = data; - break; - - default: - inc->m_buf[0] = data; - inc->m_ptr = 1; - do_midi_msg(dev, inc->m_buf, inc->m_ptr); - inc->m_ptr = 0; - inc->m_left = 0; - } - } else - { - inc->m_state = MST_DATA; - inc->m_ptr = 1; - inc->m_left = len_tab[(data >> 4) - 8]; - inc->m_buf[0] = inc->m_prev_status = data; - } - } else if (inc->m_prev_status & 0x80) { - /* Data byte (use running status) */ - inc->m_ptr = 2; - inc->m_buf[1] = data; - inc->m_buf[0] = inc->m_prev_status; - inc->m_left = len_tab[(inc->m_buf[0] >> 4) - 8] - 1; - if (inc->m_left > 0) - inc->m_state = MST_DATA; /* Not done yet */ - else { - inc->m_state = MST_INIT; - do_midi_msg(dev, inc->m_buf, inc->m_ptr); - inc->m_ptr = 0; - } - } - break; /* MST_INIT */ - - case MST_DATA: - inc->m_buf[inc->m_ptr++] = data; - if (--inc->m_left <= 0) - { - inc->m_state = MST_INIT; - do_midi_msg(dev, inc->m_buf, inc->m_ptr); - inc->m_ptr = 0; - } - break; /* MST_DATA */ - - case MST_SYSEX: - if (data == 0xf7) /* Sysex end */ - { - inc->m_state = MST_INIT; - inc->m_left = 0; - inc->m_ptr = 0; - } - break; /* MST_SYSEX */ - - default: - printk("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data); - inc->m_state = MST_INIT; - } -} - -static void -leave_sysex(int dev) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int timeout = 0; - - if (!sysex_state[dev]) - return; - - sysex_state[dev] = 0; - - while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) && - timeout < 1000) - timeout++; - - sysex_state[dev] = 0; -} - -static void -midi_synth_output(int dev) -{ - /* - * Currently NOP - */ -} - -int midi_synth_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - /* - * int orig_dev = synth_devs[dev]->midi_dev; - */ - - switch (cmd) { - - case SNDCTL_SYNTH_INFO: - if (__copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info))) - return -EFAULT; - return 0; - - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - - default: - return -EINVAL; - } -} - -int -midi_synth_kill_note(int dev, int channel, int note, int velocity) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int msg, chn; - - if (note < 0 || note > 127) - return 0; - if (channel < 0 || channel > 15) - return 0; - if (velocity < 0) - velocity = 0; - if (velocity > 127) - velocity = 127; - - leave_sysex(dev); - - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; - - if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80)) - { /* - * Use running status - */ - if (!prefix_cmd(orig_dev, note)) - return 0; - - midi_outc(orig_dev, note); - - if (msg == 0x90) /* - * Running status = Note on - */ - midi_outc(orig_dev, 0); /* - * Note on with velocity 0 == note - * off - */ - else - midi_outc(orig_dev, velocity); - } else - { - if (velocity == 64) - { - if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f))) - return 0; - midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /* - * Note on - */ - midi_outc(orig_dev, note); - midi_outc(orig_dev, 0); /* - * Zero G - */ - } else - { - if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f))) - return 0; - midi_outc(orig_dev, 0x80 | (channel & 0x0f)); /* - * Note off - */ - midi_outc(orig_dev, note); - midi_outc(orig_dev, velocity); - } - } - - return 0; -} - -int -midi_synth_set_instr(int dev, int channel, int instr_no) -{ - int orig_dev = synth_devs[dev]->midi_dev; - - if (instr_no < 0 || instr_no > 127) - instr_no = 0; - if (channel < 0 || channel > 15) - return 0; - - leave_sysex(dev); - - if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f))) - return 0; - midi_outc(orig_dev, 0xc0 | (channel & 0x0f)); /* - * Program change - */ - midi_outc(orig_dev, instr_no); - - return 0; -} - -int -midi_synth_start_note(int dev, int channel, int note, int velocity) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int msg, chn; - - if (note < 0 || note > 127) - return 0; - if (channel < 0 || channel > 15) - return 0; - if (velocity < 0) - velocity = 0; - if (velocity > 127) - velocity = 127; - - leave_sysex(dev); - - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; - - if (chn == channel && msg == 0x90) - { /* - * Use running status - */ - if (!prefix_cmd(orig_dev, note)) - return 0; - midi_outc(orig_dev, note); - midi_outc(orig_dev, velocity); - } else - { - if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f))) - return 0; - midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /* - * Note on - */ - midi_outc(orig_dev, note); - midi_outc(orig_dev, velocity); - } - return 0; -} - -void -midi_synth_reset(int dev) -{ - - leave_sysex(dev); -} - -int -midi_synth_open(int dev, int mode) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int err; - unsigned long flags; - struct midi_input_info *inc; - - if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL) - return -ENXIO; - - midi2synth[orig_dev] = dev; - sysex_state[dev] = 0; - prev_out_status[orig_dev] = 0; - - if ((err = midi_devs[orig_dev]->open(orig_dev, mode, - midi_synth_input, midi_synth_output)) < 0) - return err; - inc = &midi_devs[orig_dev]->in_info; - - save_flags(flags); - cli(); - inc->m_busy = 0; - inc->m_state = MST_INIT; - inc->m_ptr = 0; - inc->m_left = 0; - inc->m_prev_status = 0x00; - restore_flags(flags); - - return 1; -} - -void -midi_synth_close(int dev) -{ - int orig_dev = synth_devs[dev]->midi_dev; - - leave_sysex(dev); - - /* - * Shut up the synths by sending just single active sensing message. - */ - midi_devs[orig_dev]->outputc(orig_dev, 0xfe); - - midi_devs[orig_dev]->close(orig_dev); -} - -void -midi_synth_hw_control(int dev, unsigned char *event) -{ -} - -int -midi_synth_load_patch(int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) -{ - int orig_dev = synth_devs[dev]->midi_dev; - - struct sysex_info sysex; - int i; - unsigned long left, src_offs, eox_seen = 0; - int first_byte = 1; - int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex; - - leave_sysex(dev); - - if (!prefix_cmd(orig_dev, 0xf0)) - return 0; - - if (format != SYSEX_PATCH) - { -/* printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);*/ - return -EINVAL; - } - if (count < hdr_size) - { -/* printk("MIDI Error: Patch header too short\n");*/ - return -EINVAL; - } - count -= hdr_size; - - /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. - */ - - if(copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs)) - return -EFAULT; - - if (count < sysex.len) - { -/* printk(KERN_WARNING "MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);*/ - sysex.len = count; - } - left = sysex.len; - src_offs = 0; - - for (i = 0; i < left && !signal_pending(current); i++) - { - unsigned char data; - - get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i])); - - eox_seen = (i > 0 && data & 0x80); /* End of sysex */ - - if (eox_seen && data != 0xf7) - data = 0xf7; - - if (i == 0) - { - if (data != 0xf0) - { - printk(KERN_WARNING "midi_synth: Sysex start missing\n"); - return -EINVAL; - } - } - while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) && - !signal_pending(current)) - schedule(); - - if (!first_byte && data & 0x80) - return 0; - first_byte = 0; - } - - if (!eox_seen) - midi_outc(orig_dev, 0xf7); - return 0; -} - -void midi_synth_panning(int dev, int channel, int pressure) -{ -} - -void midi_synth_aftertouch(int dev, int channel, int pressure) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int msg, chn; - - if (pressure < 0 || pressure > 127) - return; - if (channel < 0 || channel > 15) - return; - - leave_sysex(dev); - - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; - - if (msg != 0xd0 || chn != channel) /* - * Test for running status - */ - { - if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f))) - return; - midi_outc(orig_dev, 0xd0 | (channel & 0x0f)); /* - * Channel pressure - */ - } else if (!prefix_cmd(orig_dev, pressure)) - return; - - midi_outc(orig_dev, pressure); -} - -void -midi_synth_controller(int dev, int channel, int ctrl_num, int value) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int chn, msg; - - if (ctrl_num < 0 || ctrl_num > 127) - return; - if (channel < 0 || channel > 15) - return; - - leave_sysex(dev); - - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; - - if (msg != 0xb0 || chn != channel) - { - if (!prefix_cmd(orig_dev, 0xb0 | (channel & 0x0f))) - return; - midi_outc(orig_dev, 0xb0 | (channel & 0x0f)); - } else if (!prefix_cmd(orig_dev, ctrl_num)) - return; - - midi_outc(orig_dev, ctrl_num); - midi_outc(orig_dev, value & 0x7f); -} - -void -midi_synth_bender(int dev, int channel, int value) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int msg, prev_chn; - - if (channel < 0 || channel > 15) - return; - - if (value < 0 || value > 16383) - return; - - leave_sysex(dev); - - msg = prev_out_status[orig_dev] & 0xf0; - prev_chn = prev_out_status[orig_dev] & 0x0f; - - if (msg != 0xd0 || prev_chn != channel) /* - * Test for running status - */ - { - if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f))) - return; - midi_outc(orig_dev, 0xe0 | (channel & 0x0f)); - } else if (!prefix_cmd(orig_dev, value & 0x7f)) - return; - - midi_outc(orig_dev, value & 0x7f); - midi_outc(orig_dev, (value >> 7) & 0x7f); -} - -void -midi_synth_setup_voice(int dev, int voice, int channel) -{ -} - -int -midi_synth_send_sysex(int dev, unsigned char *bytes, int len) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int i; - - for (i = 0; i < len; i++) - { - switch (bytes[i]) - { - case 0xf0: /* Start sysex */ - if (!prefix_cmd(orig_dev, 0xf0)) - return 0; - sysex_state[dev] = 1; - break; - - case 0xf7: /* End sysex */ - if (!sysex_state[dev]) /* Orphan sysex end */ - return 0; - sysex_state[dev] = 0; - break; - - default: - if (!sysex_state[dev]) - return 0; - - if (bytes[i] & 0x80) /* Error. Another message before sysex end */ - { - bytes[i] = 0xf7; /* Sysex end */ - sysex_state[dev] = 0; - } - } - - if (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i])) - { -/* - * Hardware level buffer is full. Abort the sysex message. - */ - - int timeout = 0; - - bytes[i] = 0xf7; - sysex_state[dev] = 0; - - while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) && - timeout < 1000) - timeout++; - } - if (!sysex_state[dev]) - return 0; - } - - return 0; -} diff -Nru a/drivers/sound/midi_synth.h b/drivers/sound/midi_synth.h --- a/drivers/sound/midi_synth.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,47 +0,0 @@ -int midi_synth_ioctl (int dev, - unsigned int cmd, caddr_t arg); -int midi_synth_kill_note (int dev, int channel, int note, int velocity); -int midi_synth_set_instr (int dev, int channel, int instr_no); -int midi_synth_start_note (int dev, int channel, int note, int volume); -void midi_synth_reset (int dev); -int midi_synth_open (int dev, int mode); -void midi_synth_close (int dev); -void midi_synth_hw_control (int dev, unsigned char *event); -int midi_synth_load_patch (int dev, int format, const char * addr, - int offs, int count, int pmgr_flag); -void midi_synth_panning (int dev, int channel, int pressure); -void midi_synth_aftertouch (int dev, int channel, int pressure); -void midi_synth_controller (int dev, int channel, int ctrl_num, int value); -void midi_synth_bender (int dev, int chn, int value); -void midi_synth_setup_voice (int dev, int voice, int chn); -int midi_synth_send_sysex(int dev, unsigned char *bytes,int len); - -#ifndef _MIDI_SYNTH_C_ -static struct synth_info std_synth_info = -{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS}; - -static struct synth_operations std_midi_synth = -{ - owner: THIS_MODULE, - id: "MIDI", - info: &std_synth_info, - midi_dev: 0, - synth_type: SYNTH_TYPE_MIDI, - synth_subtype: 0, - open: midi_synth_open, - close: midi_synth_close, - ioctl: midi_synth_ioctl, - kill_note: midi_synth_kill_note, - start_note: midi_synth_start_note, - set_instr: midi_synth_set_instr, - reset: midi_synth_reset, - hw_control: midi_synth_hw_control, - load_patch: midi_synth_load_patch, - aftertouch: midi_synth_aftertouch, - controller: midi_synth_controller, - panning: midi_synth_panning, - bender: midi_synth_bender, - setup_voice: midi_synth_setup_voice, - send_sysex: midi_synth_send_sysex -}; -#endif diff -Nru a/drivers/sound/midibuf.c b/drivers/sound/midibuf.c --- a/drivers/sound/midibuf.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,438 +0,0 @@ -/* - * sound/midibuf.c - * - * Device file manager for /dev/midi# - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - */ -#include -#include - -#define MIDIBUF_C - -#include "sound_config.h" - - -/* - * Don't make MAX_QUEUE_SIZE larger than 4000 - */ - -#define MAX_QUEUE_SIZE 4000 - -static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV]; -static wait_queue_head_t input_sleeper[MAX_MIDI_DEV]; - -struct midi_buf -{ - int len, head, tail; - unsigned char queue[MAX_QUEUE_SIZE]; -}; - -struct midi_parms -{ - long prech_timeout; /* - * Timeout before the first ch - */ -}; - -static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL}; -static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL}; -static struct midi_parms parms[MAX_MIDI_DEV]; - -static void midi_poll(unsigned long dummy); - - -static struct timer_list poll_timer = { - function: midi_poll -}; - -static volatile int open_devs = 0; - -#define DATA_AVAIL(q) (q->len) -#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len) - -#define QUEUE_BYTE(q, data) \ - if (SPACE_AVAIL(q)) \ - { \ - unsigned long flags; \ - save_flags( flags);cli(); \ - q->queue[q->tail] = (data); \ - q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \ - restore_flags(flags); \ - } - -#define REMOVE_BYTE(q, data) \ - if (DATA_AVAIL(q)) \ - { \ - unsigned long flags; \ - save_flags( flags);cli(); \ - data = q->queue[q->head]; \ - q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \ - restore_flags(flags); \ - } - -static void drain_midi_queue(int dev) -{ - - /* - * Give the Midi driver time to drain its output queues - */ - - if (midi_devs[dev]->buffer_status != NULL) - while (!signal_pending(current) && midi_devs[dev]->buffer_status(dev)) - interruptible_sleep_on_timeout(&midi_sleeper[dev], - HZ/10); -} - -static void midi_input_intr(int dev, unsigned char data) -{ - if (midi_in_buf[dev] == NULL) - return; - - if (data == 0xfe) /* - * Active sensing - */ - return; /* - * Ignore - */ - - if (SPACE_AVAIL(midi_in_buf[dev])) { - QUEUE_BYTE(midi_in_buf[dev], data); - wake_up(&input_sleeper[dev]); - } -} - -static void midi_output_intr(int dev) -{ - /* - * Currently NOP - */ -} - -static void midi_poll(unsigned long dummy) -{ - unsigned long flags; - int dev; - - save_flags(flags); - cli(); - if (open_devs) - { - for (dev = 0; dev < num_midis; dev++) - if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL) - { - int ok = 1; - - while (DATA_AVAIL(midi_out_buf[dev]) && ok) - { - int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head]; - - restore_flags(flags); /* Give some time to others */ - ok = midi_devs[dev]->outputc(dev, c); - cli(); - midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE; - midi_out_buf[dev]->len--; - } - - if (DATA_AVAIL(midi_out_buf[dev]) < 100) - wake_up(&midi_sleeper[dev]); - } - poll_timer.expires = (1) + jiffies; - add_timer(&poll_timer); - /* - * Come back later - */ - } - restore_flags(flags); -} - -int MIDIbuf_open(int dev, struct file *file) -{ - int mode, err; - - dev = dev >> 4; - mode = translate_mode(file); - - if (num_midis > MAX_MIDI_DEV) - { - printk(KERN_ERR "midi: Too many midi interfaces\n"); - num_midis = MAX_MIDI_DEV; - } - if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) - return -ENXIO; - /* - * Interrupts disabled. Be careful - */ - - if (midi_devs[dev]->owner) - __MOD_INC_USE_COUNT (midi_devs[dev]->owner); - - if ((err = midi_devs[dev]->open(dev, mode, - midi_input_intr, midi_output_intr)) < 0) - return err; - - parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT; - midi_in_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf)); - - if (midi_in_buf[dev] == NULL) - { - printk(KERN_WARNING "midi: Can't allocate buffer\n"); - midi_devs[dev]->close(dev); - return -EIO; - } - midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0; - - midi_out_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf)); - - if (midi_out_buf[dev] == NULL) - { - printk(KERN_WARNING "midi: Can't allocate buffer\n"); - midi_devs[dev]->close(dev); - vfree(midi_in_buf[dev]); - midi_in_buf[dev] = NULL; - return -EIO; - } - midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0; - open_devs++; - - init_waitqueue_head(&midi_sleeper[dev]); - init_waitqueue_head(&input_sleeper[dev]); - - if (open_devs < 2) /* This was first open */ - { - poll_timer.expires = 1 + jiffies; - add_timer(&poll_timer); /* Start polling */ - } - return err; -} - -void MIDIbuf_release(int dev, struct file *file) -{ - int mode; - unsigned long flags; - - dev = dev >> 4; - mode = translate_mode(file); - - if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) - return; - - save_flags(flags); - cli(); - - /* - * Wait until the queue is empty - */ - - if (mode != OPEN_READ) - { - midi_devs[dev]->outputc(dev, 0xfe); /* - * Active sensing to shut the - * devices - */ - - while (!signal_pending(current) && DATA_AVAIL(midi_out_buf[dev])) - interruptible_sleep_on(&midi_sleeper[dev]); - /* - * Sync - */ - - drain_midi_queue(dev); /* - * Ensure the output queues are empty - */ - } - restore_flags(flags); - - midi_devs[dev]->close(dev); - - open_devs--; - if (open_devs == 0) - del_timer_sync(&poll_timer); - vfree(midi_in_buf[dev]); - vfree(midi_out_buf[dev]); - midi_in_buf[dev] = NULL; - midi_out_buf[dev] = NULL; - - if (midi_devs[dev]->owner) - __MOD_DEC_USE_COUNT (midi_devs[dev]->owner); -} - -int MIDIbuf_write(int dev, struct file *file, const char *buf, int count) -{ - unsigned long flags; - int c, n, i; - unsigned char tmp_data; - - dev = dev >> 4; - - if (!count) - return 0; - - save_flags(flags); - cli(); - - c = 0; - - while (c < count) - { - n = SPACE_AVAIL(midi_out_buf[dev]); - - if (n == 0) { /* - * No space just now. - */ - - if (file->f_flags & O_NONBLOCK) { - restore_flags(flags); - return -EAGAIN; - } - - interruptible_sleep_on(&midi_sleeper[dev]); - if (signal_pending(current)) - { - restore_flags(flags); - return -EINTR; - } - n = SPACE_AVAIL(midi_out_buf[dev]); - } - if (n > (count - c)) - n = count - c; - - for (i = 0; i < n; i++) - { - /* BROKE BROKE BROKE - CANT DO THIS WITH CLI !! */ - copy_from_user((char *) &tmp_data, &(buf)[c], 1); - QUEUE_BYTE(midi_out_buf[dev], tmp_data); - c++; - } - } - restore_flags(flags); - return c; -} - - -int MIDIbuf_read(int dev, struct file *file, char *buf, int count) -{ - int n, c = 0; - unsigned long flags; - unsigned char tmp_data; - - dev = dev >> 4; - - save_flags(flags); - cli(); - - if (!DATA_AVAIL(midi_in_buf[dev])) { /* - * No data yet, wait - */ - if (file->f_flags & O_NONBLOCK) { - restore_flags(flags); - return -EAGAIN; - } - interruptible_sleep_on_timeout(&input_sleeper[dev], - parms[dev].prech_timeout); - - if (signal_pending(current)) - c = -EINTR; /* The user is getting restless */ - } - if (c == 0 && DATA_AVAIL(midi_in_buf[dev])) /* - * Got some bytes - */ - { - n = DATA_AVAIL(midi_in_buf[dev]); - if (n > count) - n = count; - c = 0; - - while (c < n) - { - char *fixit; - REMOVE_BYTE(midi_in_buf[dev], tmp_data); - fixit = (char *) &tmp_data; - /* BROKE BROKE BROKE */ - copy_to_user(&(buf)[c], fixit, 1); - c++; - } - } - restore_flags(flags); - return c; -} - -int MIDIbuf_ioctl(int dev, struct file *file, - unsigned int cmd, caddr_t arg) -{ - int val; - - dev = dev >> 4; - - if (((cmd >> 8) & 0xff) == 'C') - { - if (midi_devs[dev]->coproc) /* Coprocessor ioctl */ - return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0); -/* printk("/dev/midi%d: No coprocessor for this device\n", dev);*/ - return -ENXIO; - } - else - { - switch (cmd) - { - case SNDCTL_MIDI_PRETIME: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val < 0) - val = 0; - val = (HZ * val) / 10; - parms[dev].prech_timeout = val; - return put_user(val, (int *)arg); - - default: - if (!midi_devs[dev]->ioctl) - return -EINVAL; - return midi_devs[dev]->ioctl(dev, cmd, arg); - } - } -} - -/* No kernel lock - fine */ -unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait) -{ - unsigned int mask = 0; - - dev = dev >> 4; - - /* input */ - poll_wait(file, &input_sleeper[dev], wait); - if (DATA_AVAIL(midi_in_buf[dev])) - mask |= POLLIN | POLLRDNORM; - - /* output */ - poll_wait(file, &midi_sleeper[dev], wait); - if (!SPACE_AVAIL(midi_out_buf[dev])) - mask |= POLLOUT | POLLWRNORM; - - return mask; -} - - -void MIDIbuf_init(void) -{ - /* drag in midi_syms.o */ - { - extern char midi_syms_symbol; - midi_syms_symbol = 0; - } -} - -int MIDIbuf_avail(int dev) -{ - if (midi_in_buf[dev]) - return DATA_AVAIL (midi_in_buf[dev]); - return 0; -} diff -Nru a/drivers/sound/mpu401.c b/drivers/sound/mpu401.c --- a/drivers/sound/mpu401.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1797 +0,0 @@ -/* - * sound/mpu401.c - * - * The low level driver for Roland MPU-401 compatible Midi cards. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) - * Alan Cox modularisation, use normal request_irq, use dev_id - * Bartlomiej Zolnierkiewicz removed some __init to allow using many drivers - * Chris Rankin Update the module-usage counter for the coprocessor - */ - -#include -#include - -#define USE_SEQ_MACROS -#define USE_SIMPLE_MACROS - -#include "sound_config.h" - -#include "coproc.h" -#include "mpu401.h" - -static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL; - -struct mpu_config -{ - int base; /* - * I/O base - */ - int irq; - int opened; /* - * Open mode - */ - int devno; - int synthno; - int uart_mode; - int initialized; - int mode; -#define MODE_MIDI 1 -#define MODE_SYNTH 2 - unsigned char version, revision; - unsigned int capabilities; -#define MPU_CAP_INTLG 0x10000000 -#define MPU_CAP_SYNC 0x00000010 -#define MPU_CAP_FSK 0x00000020 -#define MPU_CAP_CLS 0x00000040 -#define MPU_CAP_SMPTE 0x00000080 -#define MPU_CAP_2PORT 0x00000001 - int timer_flag; - -#define MBUF_MAX 10 -#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ - {printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} - int m_busy; - unsigned char m_buf[MBUF_MAX]; - int m_ptr; - int m_state; - int m_left; - unsigned char last_status; - void (*inputintr) (int dev, unsigned char data); - int shared_irq; - int *osp; - }; - -#define DATAPORT(base) (base) -#define COMDPORT(base) (base+1) -#define STATPORT(base) (base+1) - - -static void mpu401_close(int dev); - -static int mpu401_status(struct mpu_config *devc) -{ - return inb(STATPORT(devc->base)); -} - -#define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL)) -#define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY)) - -static void write_command(struct mpu_config *devc, unsigned char cmd) -{ - outb(cmd, COMDPORT(devc->base)); -} - -static int read_data(struct mpu_config *devc) -{ - return inb(DATAPORT(devc->base)); -} - -static void write_data(struct mpu_config *devc, unsigned char byte) -{ - outb(byte, DATAPORT(devc->base)); -} - -#define OUTPUT_READY 0x40 -#define INPUT_AVAIL 0x80 -#define MPU_ACK 0xFE -#define MPU_RESET 0xFF -#define UART_MODE_ON 0x3F - -static struct mpu_config dev_conf[MAX_MIDI_DEV] = -{ - {0} -}; - -static int n_mpu_devs = 0; - -static int reset_mpu401(struct mpu_config *devc); -static void set_uart_mode(int dev, struct mpu_config *devc, int arg); - -static int mpu_timer_init(int midi_dev); -static void mpu_timer_interrupt(void); -static void timer_ext_event(struct mpu_config *devc, int event, int parm); - -static struct synth_info mpu_synth_info_proto = { - "MPU-401 MIDI interface", - 0, - SYNTH_TYPE_MIDI, - MIDI_TYPE_MPU401, - 0, 128, - 0, 128, - SYNTH_CAP_INPUT -}; - -static struct synth_info mpu_synth_info[MAX_MIDI_DEV]; - -/* - * States for the input scanner - */ - -#define ST_INIT 0 /* Ready for timing byte or msg */ -#define ST_TIMED 1 /* Leading timing byte rcvd */ -#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */ - -#define ST_SYSMSG 100 /* System message (sysx etc). */ -#define ST_SYSEX 101 /* System exclusive msg */ -#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */ -#define ST_SONGSEL 103 /* Song select */ -#define ST_SONGPOS 104 /* Song position pointer */ - -static unsigned char len_tab[] = /* # of data bytes following a status - */ -{ - 2, /* 8x */ - 2, /* 9x */ - 2, /* Ax */ - 2, /* Bx */ - 1, /* Cx */ - 1, /* Dx */ - 2, /* Ex */ - 0 /* Fx */ -}; - -#define STORE(cmd) \ -{ \ - int len; \ - unsigned char obuf[8]; \ - cmd; \ - seq_input_event(obuf, len); \ -} - -#define _seqbuf obuf -#define _seqbufptr 0 -#define _SEQ_ADVBUF(x) len=x - -static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic) -{ - - switch (devc->m_state) - { - case ST_INIT: - switch (midic) - { - case 0xf8: - /* Timer overflow */ - break; - - case 0xfc: - printk(""); - break; - - case 0xfd: - if (devc->timer_flag) - mpu_timer_interrupt(); - break; - - case 0xfe: - return MPU_ACK; - - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - printk("", midic & 0x0f); - break; - - case 0xf9: - printk(""); - break; - - case 0xff: - devc->m_state = ST_SYSMSG; - break; - - default: - if (midic <= 0xef) - { - /* printk( "mpu time: %d ", midic); */ - devc->m_state = ST_TIMED; - } - else - printk(" ", midic); - } - break; - - case ST_TIMED: - { - int msg = ((int) (midic & 0xf0) >> 4); - - devc->m_state = ST_DATABYTE; - - if (msg < 8) /* Data byte */ - { - /* printk( "midi msg (running status) "); */ - msg = ((int) (devc->last_status & 0xf0) >> 4); - msg -= 8; - devc->m_left = len_tab[msg] - 1; - - devc->m_ptr = 2; - devc->m_buf[0] = devc->last_status; - devc->m_buf[1] = midic; - - if (devc->m_left <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - } - else if (msg == 0xf) /* MPU MARK */ - { - devc->m_state = ST_INIT; - - switch (midic) - { - case 0xf8: - /* printk( "NOP "); */ - break; - - case 0xf9: - /* printk( "meas end "); */ - break; - - case 0xfc: - /* printk( "data end "); */ - break; - - default: - printk("Unknown MPU mark %02x\n", midic); - } - } - else - { - devc->last_status = midic; - /* printk( "midi msg "); */ - msg -= 8; - devc->m_left = len_tab[msg]; - - devc->m_ptr = 1; - devc->m_buf[0] = midic; - - if (devc->m_left <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - } - } - break; - - case ST_SYSMSG: - switch (midic) - { - case 0xf0: - printk(""); - devc->m_state = ST_SYSEX; - break; - - case 0xf1: - devc->m_state = ST_MTC; - break; - - case 0xf2: - devc->m_state = ST_SONGPOS; - devc->m_ptr = 0; - break; - - case 0xf3: - devc->m_state = ST_SONGSEL; - break; - - case 0xf6: - /* printk( "tune_request\n"); */ - devc->m_state = ST_INIT; - - /* - * Real time messages - */ - case 0xf8: - /* midi clock */ - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_CLOCK, 0); - break; - - case 0xfA: - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_START, 0); - break; - - case 0xFB: - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_CONTINUE, 0); - break; - - case 0xFC: - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_STOP, 0); - break; - - case 0xFE: - /* active sensing */ - devc->m_state = ST_INIT; - break; - - case 0xff: - /* printk( "midi hard reset"); */ - devc->m_state = ST_INIT; - break; - - default: - printk("unknown MIDI sysmsg %0x\n", midic); - devc->m_state = ST_INIT; - } - break; - - case ST_MTC: - devc->m_state = ST_INIT; - printk("MTC frame %x02\n", midic); - break; - - case ST_SYSEX: - if (midic == 0xf7) - { - printk(""); - devc->m_state = ST_INIT; - } - else - printk("%02x ", midic); - break; - - case ST_SONGPOS: - BUFTEST(devc); - devc->m_buf[devc->m_ptr++] = midic; - if (devc->m_ptr == 2) - { - devc->m_state = ST_INIT; - devc->m_ptr = 0; - timer_ext_event(devc, TMR_SPP, - ((devc->m_buf[1] & 0x7f) << 7) | - (devc->m_buf[0] & 0x7f)); - } - break; - - case ST_DATABYTE: - BUFTEST(devc); - devc->m_buf[devc->m_ptr++] = midic; - if ((--devc->m_left) <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - break; - - default: - printk("Bad state %d ", devc->m_state); - devc->m_state = ST_INIT; - } - return 1; -} - -static void mpu401_input_loop(struct mpu_config *devc) -{ - unsigned long flags; - int busy; - int n; - - save_flags(flags); - cli(); - busy = devc->m_busy; - devc->m_busy = 1; - restore_flags(flags); - - if (busy) /* Already inside the scanner */ - return; - - n = 50; - - while (input_avail(devc) && n-- > 0) - { - unsigned char c = read_data(devc); - - if (devc->mode == MODE_SYNTH) - { - mpu_input_scanner(devc, c); - } - else if (devc->opened & OPEN_READ && devc->inputintr != NULL) - devc->inputintr(devc->devno, c); - } - devc->m_busy = 0; -} - -int intchk_mpu401(void *dev_id) -{ - struct mpu_config *devc; - int dev = (int) dev_id; - - devc = &dev_conf[dev]; - return input_avail(devc); -} - -void mpuintr(int irq, void *dev_id, struct pt_regs *dummy) -{ - struct mpu_config *devc; - int dev = (int) dev_id; - - sti(); - devc = &dev_conf[dev]; - - if (input_avail(devc)) - { - if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH)) - mpu401_input_loop(devc); - else - { - /* Dummy read (just to acknowledge the interrupt) */ - read_data(devc); - } - } -} - -static int mpu401_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - int err; - struct mpu_config *devc; - struct coproc_operations *coprocessor; - - if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) - return -ENXIO; - - devc = &dev_conf[dev]; - - if (devc->opened) - return -EBUSY; - /* - * Verify that the device is really running. - * Some devices (such as Ensoniq SoundScape don't - * work before the on board processor (OBP) is initialized - * by downloading its microcode. - */ - - if (!devc->initialized) - { - if (mpu401_status(devc) == 0xff) /* Bus float */ - { - printk(KERN_ERR "mpu401: Device not initialized properly\n"); - return -EIO; - } - reset_mpu401(devc); - } - - if ( (coprocessor = midi_devs[dev]->coproc) != NULL ) - { - if (coprocessor->owner) - __MOD_INC_USE_COUNT(coprocessor->owner); - - if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0) - { - printk(KERN_WARNING "MPU-401: Can't access coprocessor device\n"); - mpu401_close(dev); - return err; - } - } - - set_uart_mode(dev, devc, 1); - devc->mode = MODE_MIDI; - devc->synthno = 0; - - mpu401_input_loop(devc); - - devc->inputintr = input; - devc->opened = mode; - - return 0; -} - -static void mpu401_close(int dev) -{ - struct mpu_config *devc; - struct coproc_operations *coprocessor; - - devc = &dev_conf[dev]; - if (devc->uart_mode) - reset_mpu401(devc); /* - * This disables the UART mode - */ - devc->mode = 0; - devc->inputintr = NULL; - - coprocessor = midi_devs[dev]->coproc; - if (coprocessor) { - coprocessor->close(coprocessor->devc, COPR_MIDI); - - if (coprocessor->owner) - __MOD_DEC_USE_COUNT(coprocessor->owner); - } - devc->opened = 0; -} - -static int mpu401_out(int dev, unsigned char midi_byte) -{ - int timeout; - unsigned long flags; - - struct mpu_config *devc; - - devc = &dev_conf[dev]; - - /* - * Sometimes it takes about 30000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - - save_flags(flags); - cli(); - if (!output_ready(devc)) - { - printk(KERN_WARNING "mpu401: Send data timeout\n"); - restore_flags(flags); - return 0; - } - write_data(devc, midi_byte); - restore_flags(flags); - return 1; -} - -static int mpu401_command(int dev, mpu_command_rec * cmd) -{ - int i, timeout, ok; - int ret = 0; - unsigned long flags; - struct mpu_config *devc; - - devc = &dev_conf[dev]; - - if (devc->uart_mode) /* - * Not possible in UART mode - */ - { - printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n"); - return -EINVAL; - } - /* - * Test for input since pending input seems to block the output. - */ - if (input_avail(devc)) - mpu401_input_loop(devc); - - /* - * Sometimes it takes about 50000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - timeout = 50000; -retry: - if (timeout-- <= 0) - { - printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd); - return -EIO; - } - save_flags(flags); - cli(); - - if (!output_ready(devc)) - { - restore_flags(flags); - goto retry; - } - write_command(devc, cmd->cmd); - - ok = 0; - for (timeout = 50000; timeout > 0 && !ok; timeout--) - { - if (input_avail(devc)) - { - if (devc->opened && devc->mode == MODE_SYNTH) - { - if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK) - ok = 1; - } - else - { - /* Device is not currently open. Use simpler method */ - if (read_data(devc) == MPU_ACK) - ok = 1; - } - } - } - if (!ok) - { - restore_flags(flags); - return -EIO; - } - if (cmd->nr_args) - { - for (i = 0; i < cmd->nr_args; i++) - { - for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--); - - if (!mpu401_out(dev, cmd->data[i])) - { - restore_flags(flags); - printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd); - return -EIO; - } - } - } - ret = 0; - cmd->data[0] = 0; - - if (cmd->nr_returns) - { - for (i = 0; i < cmd->nr_returns; i++) - { - ok = 0; - for (timeout = 5000; timeout > 0 && !ok; timeout--) - if (input_avail(devc)) - { - cmd->data[i] = read_data(devc); - ok = 1; - } - if (!ok) - { - restore_flags(flags); - return -EIO; - } - } - } - restore_flags(flags); - return ret; -} - -static int mpu_cmd(int dev, int cmd, int data) -{ - int ret; - - static mpu_command_rec rec; - - rec.cmd = cmd & 0xff; - rec.nr_args = ((cmd & 0xf0) == 0xE0); - rec.nr_returns = ((cmd & 0xf0) == 0xA0); - rec.data[0] = data & 0xff; - - if ((ret = mpu401_command(dev, &rec)) < 0) - return ret; - return (unsigned char) rec.data[0]; -} - -static int mpu401_prefix_cmd(int dev, unsigned char status) -{ - struct mpu_config *devc = &dev_conf[dev]; - - if (devc->uart_mode) - return 1; - - if (status < 0xf0) - { - if (mpu_cmd(dev, 0xD0, 0) < 0) - return 0; - return 1; - } - switch (status) - { - case 0xF0: - if (mpu_cmd(dev, 0xDF, 0) < 0) - return 0; - return 1; - - default: - return 0; - } -} - -static int mpu401_start_read(int dev) -{ - return 0; -} - -static int mpu401_end_read(int dev) -{ - return 0; -} - -static int mpu401_ioctl(int dev, unsigned cmd, caddr_t arg) -{ - struct mpu_config *devc; - mpu_command_rec rec; - int val, ret; - - devc = &dev_conf[dev]; - switch (cmd) - { - case SNDCTL_MIDI_MPUMODE: - if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */ - printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n"); - return -EINVAL; - } - if (get_user(val, (int *)arg)) - return -EFAULT; - set_uart_mode(dev, devc, !val); - return 0; - - case SNDCTL_MIDI_MPUCMD: - if (copy_from_user(&rec, arg, sizeof(rec))) - return -EFAULT; - if ((ret = mpu401_command(dev, &rec)) < 0) - return ret; - if (copy_to_user(arg, &rec, sizeof(rec))) - return -EFAULT; - return 0; - - default: - return -EINVAL; - } -} - -static void mpu401_kick(int dev) -{ -} - -static int mpu401_buffer_status(int dev) -{ - return 0; /* - * No data in buffers - */ -} - -static int mpu_synth_ioctl(int dev, - unsigned int cmd, caddr_t arg) -{ - int midi_dev; - struct mpu_config *devc; - - midi_dev = synth_devs[dev]->midi_dev; - - if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL) - return -ENXIO; - - devc = &dev_conf[midi_dev]; - - switch (cmd) - { - - case SNDCTL_SYNTH_INFO: - memcpy((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof(struct synth_info)); - return 0; - - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - - default: - return -EINVAL; - } -} - -static int mpu_synth_open(int dev, int mode) -{ - int midi_dev, err; - struct mpu_config *devc; - struct coproc_operations *coprocessor; - - midi_dev = synth_devs[dev]->midi_dev; - - if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL) - return -ENXIO; - - devc = &dev_conf[midi_dev]; - - /* - * Verify that the device is really running. - * Some devices (such as Ensoniq SoundScape don't - * work before the on board processor (OBP) is initialized - * by downloading its microcode. - */ - - if (!devc->initialized) - { - if (mpu401_status(devc) == 0xff) /* Bus float */ - { - printk(KERN_ERR "mpu401: Device not initialized properly\n"); - return -EIO; - } - reset_mpu401(devc); - } - if (devc->opened) - return -EBUSY; - devc->mode = MODE_SYNTH; - devc->synthno = dev; - - devc->inputintr = NULL; - - coprocessor = midi_devs[midi_dev]->coproc; - if (coprocessor) { - if (coprocessor->owner) - __MOD_INC_USE_COUNT(coprocessor->owner); - - if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0) - { - printk(KERN_WARNING "mpu401: Can't access coprocessor device\n"); - return err; - } - } - devc->opened = mode; - reset_mpu401(devc); - - if (mode & OPEN_READ) - { - mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */ - mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ - mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */ - } - return 0; -} - -static void mpu_synth_close(int dev) -{ - int midi_dev; - struct mpu_config *devc; - struct coproc_operations *coprocessor; - - midi_dev = synth_devs[dev]->midi_dev; - - devc = &dev_conf[midi_dev]; - mpu_cmd(midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */ - mpu_cmd(midi_dev, 0x8a, 0); /* Disable data in stopped mode */ - - devc->inputintr = NULL; - - coprocessor = midi_devs[midi_dev]->coproc; - if (coprocessor) { - coprocessor->close(coprocessor->devc, COPR_MIDI); - - if (coprocessor->owner) - __MOD_DEC_USE_COUNT(coprocessor->owner); - } - devc->opened = 0; - devc->mode = 0; -} - -#define MIDI_SYNTH_NAME "MPU-401 UART Midi" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static struct synth_operations mpu401_synth_proto = -{ - owner: THIS_MODULE, - id: "MPU401", - info: NULL, - midi_dev: 0, - synth_type: SYNTH_TYPE_MIDI, - synth_subtype: 0, - open: mpu_synth_open, - close: mpu_synth_close, - ioctl: mpu_synth_ioctl, - kill_note: midi_synth_kill_note, - start_note: midi_synth_start_note, - set_instr: midi_synth_set_instr, - reset: midi_synth_reset, - hw_control: midi_synth_hw_control, - load_patch: midi_synth_load_patch, - aftertouch: midi_synth_aftertouch, - controller: midi_synth_controller, - panning: midi_synth_panning, - bender: midi_synth_bender, - setup_voice: midi_synth_setup_voice, - send_sysex: midi_synth_send_sysex -}; - -static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV]; - -static struct midi_operations mpu401_midi_proto = -{ - owner: THIS_MODULE, - info: {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, - in_info: {0}, - open: mpu401_open, - close: mpu401_close, - ioctl: mpu401_ioctl, - outputc: mpu401_out, - start_read: mpu401_start_read, - end_read: mpu401_end_read, - kick: mpu401_kick, - buffer_status: mpu401_buffer_status, - prefix_cmd: mpu401_prefix_cmd -}; - -static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV]; - -static void mpu401_chk_version(int n, struct mpu_config *devc) -{ - int tmp; - unsigned long flags; - - devc->version = devc->revision = 0; - - save_flags(flags); - cli(); - if ((tmp = mpu_cmd(n, 0xAC, 0)) < 0) - { - restore_flags(flags); - return; - } - if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */ - { - restore_flags(flags); - return; - } - devc->version = tmp; - - if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0) - { - devc->version = 0; - restore_flags(flags); - return; - } - devc->revision = tmp; - restore_flags(flags); -} - -void attach_mpu401(struct address_info *hw_config, struct module *owner) -{ - unsigned long flags; - char revision_char; - - int m; - struct mpu_config *devc; - - hw_config->slots[1] = -1; - m = sound_alloc_mididev(); - if (m == -1) - { - printk(KERN_WARNING "MPU-401: Too many midi devices detected\n"); - return; - } - devc = &dev_conf[m]; - devc->base = hw_config->io_base; - devc->osp = hw_config->osp; - devc->irq = hw_config->irq; - devc->opened = 0; - devc->uart_mode = 0; - devc->initialized = 0; - devc->version = 0; - devc->revision = 0; - devc->capabilities = 0; - devc->timer_flag = 0; - devc->m_busy = 0; - devc->m_state = ST_INIT; - devc->shared_irq = hw_config->always_detect; - devc->irq = hw_config->irq; - - if (devc->irq < 0) - { - devc->irq *= -1; - devc->shared_irq = 1; - } - - if (!hw_config->always_detect) - { - /* Verify the hardware again */ - if (!reset_mpu401(devc)) - { - printk(KERN_WARNING "mpu401: Device didn't respond\n"); - sound_unload_mididev(m); - return; - } - if (!devc->shared_irq) - { - if (request_irq(devc->irq, mpuintr, 0, "mpu401", (void *)m) < 0) - { - printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq); - sound_unload_mididev(m); - return; - } - } - save_flags(flags); - cli(); - mpu401_chk_version(m, devc); - if (devc->version == 0) - mpu401_chk_version(m, devc); - restore_flags(flags); - } - request_region(hw_config->io_base, 2, "mpu401"); - - if (devc->version != 0) - if (mpu_cmd(m, 0xC5, 0) >= 0) /* Set timebase OK */ - if (mpu_cmd(m, 0xE0, 120) >= 0) /* Set tempo OK */ - devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */ - - - mpu401_synth_operations[m] = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); - - if (mpu401_synth_operations[m] == NULL) - { - sound_unload_mididev(m); - printk(KERN_ERR "mpu401: Can't allocate memory\n"); - return; - } - if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */ - { - memcpy((char *) mpu401_synth_operations[m], - (char *) &std_midi_synth, - sizeof(struct synth_operations)); - } - else - { - memcpy((char *) mpu401_synth_operations[m], - (char *) &mpu401_synth_proto, - sizeof(struct synth_operations)); - } - if (owner) - mpu401_synth_operations[m]->owner = owner; - - memcpy((char *) &mpu401_midi_operations[m], - (char *) &mpu401_midi_proto, - sizeof(struct midi_operations)); - - mpu401_midi_operations[m].converter = mpu401_synth_operations[m]; - - memcpy((char *) &mpu_synth_info[m], - (char *) &mpu_synth_info_proto, - sizeof(struct synth_info)); - - n_mpu_devs++; - - if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */ - { - int ports = (devc->revision & 0x08) ? 32 : 16; - - devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | - MPU_CAP_CLS | MPU_CAP_2PORT; - - revision_char = (devc->revision == 0x7f) ? 'M' : ' '; - sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d", - ports, - revision_char, - n_mpu_devs); - } - else - { - revision_char = devc->revision ? devc->revision + '@' : ' '; - if ((int) devc->revision > ('Z' - '@')) - revision_char = '+'; - - devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; - - if (hw_config->name) - sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name); - else - sprintf(mpu_synth_info[m].name, - "MPU-401 %d.%d%c Midi interface #%d", - (int) (devc->version & 0xf0) >> 4, - devc->version & 0x0f, - revision_char, - n_mpu_devs); - } - - strcpy(mpu401_midi_operations[m].info.name, - mpu_synth_info[m].name); - - conf_printf(mpu_synth_info[m].name, hw_config); - - mpu401_synth_operations[m]->midi_dev = devc->devno = m; - mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno]; - - if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */ - hw_config->slots[2] = mpu_timer_init(m); - - midi_devs[m] = &mpu401_midi_operations[devc->devno]; - - if (owner) - midi_devs[m]->owner = owner; - - hw_config->slots[1] = m; - sequencer_init(); -} - -static int reset_mpu401(struct mpu_config *devc) -{ - unsigned long flags; - int ok, timeout, n; - int timeout_limit; - - /* - * Send the RESET command. Try again if no success at the first time. - * (If the device is in the UART mode, it will not ack the reset cmd). - */ - - ok = 0; - - timeout_limit = devc->initialized ? 30000 : 100000; - devc->initialized = 1; - - for (n = 0; n < 2 && !ok; n++) - { - for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) - ok = output_ready(devc); - - write_command(devc, MPU_RESET); /* - * Send MPU-401 RESET Command - */ - - /* - * Wait at least 25 msec. This method is not accurate so let's make the - * loop bit longer. Cannot sleep since this is called during boot. - */ - - for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) - { - save_flags(flags); - cli(); - if (input_avail(devc)) - if (read_data(devc) == MPU_ACK) - ok = 1; - restore_flags(flags); - } - - } - - devc->m_state = ST_INIT; - devc->m_ptr = 0; - devc->m_left = 0; - devc->last_status = 0; - devc->uart_mode = 0; - - return ok; -} - -static void set_uart_mode(int dev, struct mpu_config *devc, int arg) -{ - if (!arg && (devc->capabilities & MPU_CAP_INTLG)) - return; - if ((devc->uart_mode == 0) == (arg == 0)) - return; /* Already set */ - reset_mpu401(devc); /* This exits the uart mode */ - - if (arg) - { - if (mpu_cmd(dev, UART_MODE_ON, 0) < 0) - { - printk(KERN_ERR "mpu401: Can't enter UART mode\n"); - devc->uart_mode = 0; - return; - } - } - devc->uart_mode = arg; - -} - -int probe_mpu401(struct address_info *hw_config) -{ - int ok = 0; - struct mpu_config tmp_devc; - - if (check_region(hw_config->io_base, 2)) - { - printk(KERN_ERR "mpu401: I/O port %x already in use\n\n", hw_config->io_base); - return 0; - } - tmp_devc.base = hw_config->io_base; - tmp_devc.irq = hw_config->irq; - tmp_devc.initialized = 0; - tmp_devc.opened = 0; - tmp_devc.osp = hw_config->osp; - - if (hw_config->always_detect) - return 1; - - if (inb(hw_config->io_base + 1) == 0xff) - { - DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base)); - return 0; /* Just bus float? */ - } - ok = reset_mpu401(&tmp_devc); - - if (!ok) - { - DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base)); - } - return ok; -} - -void __exit unload_mpu401(struct address_info *hw_config) -{ - void *p; - int n=hw_config->slots[1]; - - release_region(hw_config->io_base, 2); - if (hw_config->always_detect == 0 && hw_config->irq > 0) - free_irq(hw_config->irq, (void *)n); - p=mpu401_synth_operations[n]; - sound_unload_mididev(n); - sound_unload_timerdev(hw_config->slots[2]); - if(p) - kfree(p); -} - -/***************************************************** - * Timer stuff - ****************************************************/ - -static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0; -static volatile int curr_tempo, curr_timebase, hw_timebase; -static int max_timebase = 8; /* 8*24=192 ppqn */ -static volatile unsigned long next_event_time; -static volatile unsigned long curr_ticks, curr_clocks; -static unsigned long prev_event_time; -static int metronome_mode; - -static unsigned long clocks2ticks(unsigned long clocks) -{ - /* - * The MPU-401 supports just a limited set of possible timebase values. - * Since the applications require more choices, the driver has to - * program the HW to do its best and to convert between the HW and - * actual timebases. - */ - return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase; -} - -static void set_timebase(int midi_dev, int val) -{ - int hw_val; - - if (val < 48) - val = 48; - if (val > 1000) - val = 1000; - - hw_val = val; - hw_val = (hw_val + 12) / 24; - if (hw_val > max_timebase) - hw_val = max_timebase; - - if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0) - { - printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24); - return; - } - hw_timebase = hw_val * 24; - curr_timebase = val; - -} - -static void tmr_reset(void) -{ - unsigned long flags; - - save_flags(flags); - cli(); - next_event_time = (unsigned long) -1; - prev_event_time = 0; - curr_ticks = curr_clocks = 0; - restore_flags(flags); -} - -static void set_timer_mode(int midi_dev) -{ - if (timer_mode & TMR_MODE_CLS) - mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ - else if (timer_mode & TMR_MODE_SMPTE) - mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ - - if (timer_mode & TMR_INTERNAL) - { - mpu_cmd(midi_dev, 0x80, 0); /* Use MIDI sync */ - } - else - { - if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) - { - mpu_cmd(midi_dev, 0x82, 0); /* Use MIDI sync */ - mpu_cmd(midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ - } - else if (timer_mode & TMR_MODE_FSK) - mpu_cmd(midi_dev, 0x81, 0); /* Use FSK sync */ - } -} - -static void stop_metronome(int midi_dev) -{ - mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ -} - -static void setup_metronome(int midi_dev) -{ - int numerator, denominator; - int clks_per_click, num_32nds_per_beat; - int beats_per_measure; - - numerator = ((unsigned) metronome_mode >> 24) & 0xff; - denominator = ((unsigned) metronome_mode >> 16) & 0xff; - clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff; - num_32nds_per_beat = (unsigned) metronome_mode & 0xff; - beats_per_measure = (numerator * 4) >> denominator; - - if (!metronome_mode) - mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ - else - { - mpu_cmd(midi_dev, 0xE4, clks_per_click); - mpu_cmd(midi_dev, 0xE6, beats_per_measure); - mpu_cmd(midi_dev, 0x83, 0); /* Enable metronome without accents */ - } -} - -static int mpu_start_timer(int midi_dev) -{ - tmr_reset(); - set_timer_mode(midi_dev); - - if (tmr_running) - return TIMER_NOT_ARMED; /* Already running */ - - if (timer_mode & TMR_INTERNAL) - { - mpu_cmd(midi_dev, 0x02, 0); /* Send MIDI start */ - tmr_running = 1; - return TIMER_NOT_ARMED; - } - else - { - mpu_cmd(midi_dev, 0x35, 0); /* Enable mode messages to PC */ - mpu_cmd(midi_dev, 0x38, 0); /* Enable sys common messages to PC */ - mpu_cmd(midi_dev, 0x39, 0); /* Enable real time messages to PC */ - mpu_cmd(midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ - } - return TIMER_ARMED; -} - -static int mpu_timer_open(int dev, int mode) -{ - int midi_dev = sound_timer_devs[dev]->devlink; - - if (timer_open) - return -EBUSY; - - tmr_reset(); - curr_tempo = 50; - mpu_cmd(midi_dev, 0xE0, 50); - curr_timebase = hw_timebase = 120; - set_timebase(midi_dev, 120); - timer_open = 1; - metronome_mode = 0; - set_timer_mode(midi_dev); - - mpu_cmd(midi_dev, 0xe7, 0x04); /* Send all clocks to host */ - mpu_cmd(midi_dev, 0x95, 0); /* Enable clock to host */ - - return 0; -} - -static void mpu_timer_close(int dev) -{ - int midi_dev = sound_timer_devs[dev]->devlink; - - timer_open = tmr_running = 0; - mpu_cmd(midi_dev, 0x15, 0); /* Stop all */ - mpu_cmd(midi_dev, 0x94, 0); /* Disable clock to host */ - mpu_cmd(midi_dev, 0x8c, 0); /* Disable measure end messages to host */ - stop_metronome(midi_dev); -} - -static int mpu_timer_event(int dev, unsigned char *event) -{ - unsigned char command = event[1]; - unsigned long parm = *(unsigned int *) &event[4]; - int midi_dev = sound_timer_devs[dev]->devlink; - - switch (command) - { - case TMR_WAIT_REL: - parm += prev_event_time; - case TMR_WAIT_ABS: - if (parm > 0) - { - long time; - - if (parm <= curr_ticks) /* It's the time */ - return TIMER_NOT_ARMED; - time = parm; - next_event_time = prev_event_time = time; - - return TIMER_ARMED; - } - break; - - case TMR_START: - if (tmr_running) - break; - return mpu_start_timer(midi_dev); - - case TMR_STOP: - mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ - stop_metronome(midi_dev); - tmr_running = 0; - break; - - case TMR_CONTINUE: - if (tmr_running) - break; - mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ - setup_metronome(midi_dev); - tmr_running = 1; - break; - - case TMR_TEMPO: - if (parm) - { - if (parm < 8) - parm = 8; - if (parm > 250) - parm = 250; - if (mpu_cmd(midi_dev, 0xE0, parm) < 0) - printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm); - curr_tempo = parm; - } - break; - - case TMR_ECHO: - seq_copy_to_input(event, 8); - break; - - case TMR_TIMESIG: - if (metronome_mode) /* Metronome enabled */ - { - metronome_mode = parm; - setup_metronome(midi_dev); - } - break; - - default:; - } - return TIMER_NOT_ARMED; -} - -static unsigned long mpu_timer_get_time(int dev) -{ - if (!timer_open) - return 0; - - return curr_ticks; -} - -static int mpu_timer_ioctl(int dev, unsigned int command, caddr_t arg) -{ - int midi_dev = sound_timer_devs[dev]->devlink; - - switch (command) - { - case SNDCTL_TMR_SOURCE: - { - int parm; - - parm = *(int *) arg; - parm &= timer_caps; - - if (parm != 0) - { - timer_mode = parm; - - if (timer_mode & TMR_MODE_CLS) - mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ - else if (timer_mode & TMR_MODE_SMPTE) - mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ - } - return (*(int *) arg = timer_mode); - } - break; - - case SNDCTL_TMR_START: - mpu_start_timer(midi_dev); - return 0; - - case SNDCTL_TMR_STOP: - tmr_running = 0; - mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ - stop_metronome(midi_dev); - return 0; - - case SNDCTL_TMR_CONTINUE: - if (tmr_running) - return 0; - tmr_running = 1; - mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ - return 0; - - case SNDCTL_TMR_TIMEBASE: - { - int val; - - val = *(int *) arg; - if (val) - set_timebase(midi_dev, val); - return (*(int *) arg = curr_timebase); - } - break; - - case SNDCTL_TMR_TEMPO: - { - int val; - int ret; - - val = *(int *) arg; - - if (val) - { - if (val < 8) - val = 8; - if (val > 250) - val = 250; - if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0) - { - printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val); - return ret; - } - curr_tempo = val; - } - return (*(int *) arg = curr_tempo); - } - break; - - case SNDCTL_SEQ_CTRLRATE: - { - int val; - - val = *(int *) arg; - if (val != 0) /* Can't change */ - return -EINVAL; - return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60); - } - break; - - case SNDCTL_SEQ_GETTIME: - return (*(int *) arg = curr_ticks); - - case SNDCTL_TMR_METRONOME: - metronome_mode = *(int *) arg; - setup_metronome(midi_dev); - return 0; - - default:; - } - return -EINVAL; -} - -static void mpu_timer_arm(int dev, long time) -{ - if (time < 0) - time = curr_ticks + 1; - else if (time <= curr_ticks) /* It's the time */ - return; - next_event_time = prev_event_time = time; - return; -} - -static struct sound_timer_operations mpu_timer = -{ - owner: THIS_MODULE, - info: {"MPU-401 Timer", 0}, - priority: 10, /* Priority */ - devlink: 0, /* Local device link */ - open: mpu_timer_open, - close: mpu_timer_close, - event: mpu_timer_event, - get_time: mpu_timer_get_time, - ioctl: mpu_timer_ioctl, - arm_timer: mpu_timer_arm -}; - -static void mpu_timer_interrupt(void) -{ - if (!timer_open) - return; - - if (!tmr_running) - return; - - curr_clocks++; - curr_ticks = clocks2ticks(curr_clocks); - - if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer(0); - } -} - -static void timer_ext_event(struct mpu_config *devc, int event, int parm) -{ - int midi_dev = devc->devno; - - if (!devc->timer_flag) - return; - - switch (event) - { - case TMR_CLOCK: - printk(""); - break; - - case TMR_START: - printk("Ext MIDI start\n"); - if (!tmr_running) - { - if (timer_mode & TMR_EXTERNAL) - { - tmr_running = 1; - setup_metronome(midi_dev); - next_event_time = 0; - STORE(SEQ_START_TIMER()); - } - } - break; - - case TMR_STOP: - printk("Ext MIDI stop\n"); - if (timer_mode & TMR_EXTERNAL) - { - tmr_running = 0; - stop_metronome(midi_dev); - STORE(SEQ_STOP_TIMER()); - } - break; - - case TMR_CONTINUE: - printk("Ext MIDI continue\n"); - if (timer_mode & TMR_EXTERNAL) - { - tmr_running = 1; - setup_metronome(midi_dev); - STORE(SEQ_CONTINUE_TIMER()); - } - break; - - case TMR_SPP: - printk("Songpos: %d\n", parm); - if (timer_mode & TMR_EXTERNAL) - { - STORE(SEQ_SONGPOS(parm)); - } - break; - } -} - -static int mpu_timer_init(int midi_dev) -{ - struct mpu_config *devc; - int n; - - devc = &dev_conf[midi_dev]; - - if (timer_initialized) - return -1; /* There is already a similar timer */ - - timer_initialized = 1; - - mpu_timer.devlink = midi_dev; - dev_conf[midi_dev].timer_flag = 1; - - n = sound_alloc_timerdev(); - if (n == -1) - n = 0; - sound_timer_devs[n] = &mpu_timer; - - if (devc->version < 0x20) /* Original MPU-401 */ - timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI; - else - { - /* - * The version number 2.0 is used (at least) by the - * MusicQuest cards and the Roland Super-MPU. - * - * MusicQuest has given a special meaning to the bits of the - * revision number. The Super-MPU returns 0. - */ - - if (devc->revision) - timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI; - - if (devc->revision & 0x02) - timer_caps |= TMR_MODE_CLS; - - - if (devc->revision & 0x40) - max_timebase = 10; /* Has the 216 and 240 ppqn modes */ - } - - timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps; - return n; - -} - -EXPORT_SYMBOL(probe_mpu401); -EXPORT_SYMBOL(attach_mpu401); -EXPORT_SYMBOL(unload_mpu401); -EXPORT_SYMBOL(intchk_mpu401); -EXPORT_SYMBOL(mpuintr); - -static struct address_info cfg; - -static int __initdata io = -1; -static int __initdata irq = -1; - -MODULE_PARM(irq, "i"); -MODULE_PARM(io, "i"); - -int __init init_mpu401(void) -{ - /* Can be loaded either for module use or to provide functions - to others */ - if (io != -1 && irq != -1) { - cfg.irq = irq; - cfg.io_base = io; - if (probe_mpu401(&cfg) == 0) - return -ENODEV; - attach_mpu401(&cfg, THIS_MODULE); - } - - return 0; -} - -void __exit cleanup_mpu401(void) -{ - if (io != -1 && irq != -1) { - /* Check for use by, for example, sscape driver */ - unload_mpu401(&cfg); - } -} - -module_init(init_mpu401); -module_exit(cleanup_mpu401); - -#ifndef MODULE -static int __init setup_mpu401(char *str) -{ - /* io, irq */ - int ints[3]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - - return 1; -} - -__setup("mpu401=", setup_mpu401); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/mpu401.h b/drivers/sound/mpu401.h --- a/drivers/sound/mpu401.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,20 +0,0 @@ -/* - * uart401.h - * - * Copyright: Christoph Hellwig - * - */ - -/* From uart401.c */ -int probe_uart401 (struct address_info *hw_config, struct module *owner); -void unload_uart401 (struct address_info *hw_config); - -void uart401intr (int irq, void *dev_id, struct pt_regs * dummy); - -/* From mpu401.c */ -int probe_mpu401(struct address_info *hw_config); -void attach_mpu401(struct address_info * hw_config, struct module *owner); -void unload_mpu401(struct address_info *hw_info); - -int intchk_mpu401(void *dev_id); -void mpuintr(int irq, void *dev_id, struct pt_regs * dummy); diff -Nru a/drivers/sound/msnd.c b/drivers/sound/msnd.c --- a/drivers/sound/msnd.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,406 +0,0 @@ -/********************************************************************* - * - * msnd.c - Driver Base - * - * Turtle Beach MultiSound Sound Card Driver for Linux - * - * Copyright (C) 1998 Andrew Veliath - * - * 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. - * - * $Id: msnd.c,v 1.17 1999/03/21 16:50:09 andrewtv Exp $ - * - ********************************************************************/ - -#include -#if LINUX_VERSION_CODE < 0x020101 -# define LINUX20 -#endif -#include -#include -#include -#include -#include -#include -#include -#ifdef LINUX20 -# include -# include -# include -# include -# include "sound_config.h" -#else -# include -# include -# include -# include -#endif -#include -#include "msnd.h" - -#define LOGNAME "msnd" - -#define MSND_MAX_DEVS 4 - -static multisound_dev_t *devs[MSND_MAX_DEVS]; -static int num_devs; - -int __init msnd_register(multisound_dev_t *dev) -{ - int i; - - for (i = 0; i < MSND_MAX_DEVS; ++i) - if (devs[i] == NULL) - break; - - if (i == MSND_MAX_DEVS) - return -ENOMEM; - - devs[i] = dev; - ++num_devs; - - MOD_INC_USE_COUNT; - - return 0; -} - -void msnd_unregister(multisound_dev_t *dev) -{ - int i; - - for (i = 0; i < MSND_MAX_DEVS; ++i) - if (devs[i] == dev) - break; - - if (i == MSND_MAX_DEVS) { - printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n"); - return; - } - - devs[i] = NULL; - --num_devs; - - MOD_DEC_USE_COUNT; -} - -int msnd_get_num_devs(void) -{ - return num_devs; -} - -multisound_dev_t *msnd_get_dev(int j) -{ - int i; - - for (i = 0; i < MSND_MAX_DEVS && j; ++i) - if (devs[i] != NULL) - --j; - - if (i == MSND_MAX_DEVS || j != 0) - return NULL; - - return devs[i]; -} - -void msnd_init_queue(unsigned long base, int start, int size) -{ - isa_writew(PCTODSP_BASED(start), base + JQS_wStart); - isa_writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize); - isa_writew(0, base + JQS_wHead); - isa_writew(0, base + JQS_wTail); -} - -void msnd_fifo_init(msnd_fifo *f) -{ - f->data = NULL; -} - -void msnd_fifo_free(msnd_fifo *f) -{ - if (f->data) { - vfree(f->data); - f->data = NULL; - } -} - -int msnd_fifo_alloc(msnd_fifo *f, size_t n) -{ - msnd_fifo_free(f); - f->data = (char *)vmalloc(n); - f->n = n; - f->tail = 0; - f->head = 0; - f->len = 0; - - if (!f->data) - return -ENOMEM; - - return 0; -} - -void msnd_fifo_make_empty(msnd_fifo *f) -{ - f->len = f->tail = f->head = 0; -} - -int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user) -{ - int count = 0; - - if (f->len == f->n) - return 0; - - while ((count < len) && (f->len != f->n)) { - - int nwritten; - - if (f->head <= f->tail) { - nwritten = len - count; - if (nwritten > f->n - f->tail) - nwritten = f->n - f->tail; - } - else { - nwritten = f->head - f->tail; - if (nwritten > len - count) - nwritten = len - count; - } - - if (user) { - if (copy_from_user(f->data + f->tail, buf, nwritten)) - return -EFAULT; - } else - isa_memcpy_fromio(f->data + f->tail, (unsigned long) buf, nwritten); - - count += nwritten; - buf += nwritten; - f->len += nwritten; - f->tail += nwritten; - f->tail %= f->n; - } - - return count; -} - -int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user) -{ - int count = 0; - - if (f->len == 0) - return f->len; - - while ((count < len) && (f->len > 0)) { - - int nread; - - if (f->tail <= f->head) { - nread = len - count; - if (nread > f->n - f->head) - nread = f->n - f->head; - } - else { - nread = f->tail - f->head; - if (nread > len - count) - nread = len - count; - } - - if (user) { - if (copy_to_user(buf, f->data + f->head, nread)) - return -EFAULT; - } else - isa_memcpy_toio((unsigned long) buf, f->data + f->head, nread); - - count += nread; - buf += nread; - f->len -= nread; - f->head += nread; - f->head %= f->n; - } - - return count; -} - -int msnd_wait_TXDE(multisound_dev_t *dev) -{ - register unsigned int io = dev->io; - register int timeout = 1000; - - while(timeout-- > 0) - if (inb(io + HP_ISR) & HPISR_TXDE) - return 0; - - return -EIO; -} - -int msnd_wait_HC0(multisound_dev_t *dev) -{ - register unsigned int io = dev->io; - register int timeout = 1000; - - while(timeout-- > 0) - if (!(inb(io + HP_CVR) & HPCVR_HC)) - return 0; - - return -EIO; -} - -int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - if (msnd_wait_HC0(dev) == 0) { - outb(cmd, dev->io + HP_CVR); - spin_unlock_irqrestore(&dev->lock, flags); - return 0; - } - spin_unlock_irqrestore(&dev->lock, flags); - - printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n"); - - return -EIO; -} - -int msnd_send_word(multisound_dev_t *dev, unsigned char high, - unsigned char mid, unsigned char low) -{ - register unsigned int io = dev->io; - - if (msnd_wait_TXDE(dev) == 0) { - outb(high, io + HP_TXH); - outb(mid, io + HP_TXM); - outb(low, io + HP_TXL); - return 0; - } - - printk(KERN_DEBUG LOGNAME ": Send host word timeout\n"); - - return -EIO; -} - -int msnd_upload_host(multisound_dev_t *dev, char *bin, int len) -{ - int i; - - if (len % 3 != 0) { - printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n"); - return -EINVAL; - } - - for (i = 0; i < len; i += 3) - if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0) - return -EIO; - - inb(dev->io + HP_RXL); - inb(dev->io + HP_CVR); - - return 0; -} - -int msnd_enable_irq(multisound_dev_t *dev) -{ - unsigned long flags; - - if (dev->irq_ref++) - return 0; - - printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n"); - - spin_lock_irqsave(&dev->lock, flags); - if (msnd_wait_TXDE(dev) == 0) { - outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR); - if (dev->type == msndClassic) - outb(dev->irqid, dev->io + HP_IRQM); - outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR); - outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR); - enable_irq(dev->irq); - msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size); - spin_unlock_irqrestore(&dev->lock, flags); - return 0; - } - spin_unlock_irqrestore(&dev->lock, flags); - - printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n"); - - return -EIO; -} - -int msnd_disable_irq(multisound_dev_t *dev) -{ - unsigned long flags; - - if (--dev->irq_ref > 0) - return 0; - - if (dev->irq_ref < 0) - printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref); - - printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n"); - - spin_lock_irqsave(&dev->lock, flags); - if (msnd_wait_TXDE(dev) == 0) { - outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR); - if (dev->type == msndClassic) - outb(HPIRQ_NONE, dev->io + HP_IRQM); - disable_irq(dev->irq); - spin_unlock_irqrestore(&dev->lock, flags); - return 0; - } - spin_unlock_irqrestore(&dev->lock, flags); - - printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n"); - - return -EIO; -} - -#ifndef LINUX20 -EXPORT_SYMBOL(msnd_register); -EXPORT_SYMBOL(msnd_unregister); -EXPORT_SYMBOL(msnd_get_num_devs); -EXPORT_SYMBOL(msnd_get_dev); - -EXPORT_SYMBOL(msnd_init_queue); - -EXPORT_SYMBOL(msnd_fifo_init); -EXPORT_SYMBOL(msnd_fifo_free); -EXPORT_SYMBOL(msnd_fifo_alloc); -EXPORT_SYMBOL(msnd_fifo_make_empty); -EXPORT_SYMBOL(msnd_fifo_write); -EXPORT_SYMBOL(msnd_fifo_read); - -EXPORT_SYMBOL(msnd_wait_TXDE); -EXPORT_SYMBOL(msnd_wait_HC0); -EXPORT_SYMBOL(msnd_send_dsp_cmd); -EXPORT_SYMBOL(msnd_send_word); -EXPORT_SYMBOL(msnd_upload_host); - -EXPORT_SYMBOL(msnd_enable_irq); -EXPORT_SYMBOL(msnd_disable_irq); -#endif - -#ifdef MODULE -MODULE_AUTHOR ("Andrew Veliath "); -MODULE_DESCRIPTION ("Turtle Beach MultiSound Driver Base"); -MODULE_LICENSE("GPL"); - - -int init_module(void) -{ - return 0; -} - -void cleanup_module(void) -{ -} -#endif diff -Nru a/drivers/sound/msnd.h b/drivers/sound/msnd.h --- a/drivers/sound/msnd.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,281 +0,0 @@ -/********************************************************************* - * - * msnd.h - * - * Turtle Beach MultiSound Sound Card Driver for Linux - * - * Some parts of this header file were derived from the Turtle Beach - * MultiSound Driver Development Kit. - * - * Copyright (C) 1998 Andrew Veliath - * Copyright (C) 1993 Turtle Beach Systems, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * $Id: msnd.h,v 1.36 1999/03/21 17:05:42 andrewtv Exp $ - * - ********************************************************************/ -#ifndef __MSND_H -#define __MSND_H - -#define VERSION "0.8.3.1" - -#define DEFSAMPLERATE DSP_DEFAULT_SPEED -#define DEFSAMPLESIZE AFMT_U8 -#define DEFCHANNELS 1 - -#define DEFFIFOSIZE 128 - -#define SNDCARD_MSND 38 - -#define SRAM_BANK_SIZE 0x8000 -#define SRAM_CNTL_START 0x7F00 - -#define DSP_BASE_ADDR 0x4000 -#define DSP_BANK_BASE 0x4000 - -#define HP_ICR 0x00 -#define HP_CVR 0x01 -#define HP_ISR 0x02 -#define HP_IVR 0x03 -#define HP_NU 0x04 -#define HP_INFO 0x04 -#define HP_TXH 0x05 -#define HP_RXH 0x05 -#define HP_TXM 0x06 -#define HP_RXM 0x06 -#define HP_TXL 0x07 -#define HP_RXL 0x07 - -#define HP_ICR_DEF 0x00 -#define HP_CVR_DEF 0x12 -#define HP_ISR_DEF 0x06 -#define HP_IVR_DEF 0x0f -#define HP_NU_DEF 0x00 - -#define HP_IRQM 0x09 - -#define HPR_BLRC 0x08 -#define HPR_SPR1 0x09 -#define HPR_SPR2 0x0A -#define HPR_TCL0 0x0B -#define HPR_TCL1 0x0C -#define HPR_TCL2 0x0D -#define HPR_TCL3 0x0E -#define HPR_TCL4 0x0F - -#define HPICR_INIT 0x80 -#define HPICR_HM1 0x40 -#define HPICR_HM0 0x20 -#define HPICR_HF1 0x10 -#define HPICR_HF0 0x08 -#define HPICR_TREQ 0x02 -#define HPICR_RREQ 0x01 - -#define HPCVR_HC 0x80 - -#define HPISR_HREQ 0x80 -#define HPISR_DMA 0x40 -#define HPISR_HF3 0x10 -#define HPISR_HF2 0x08 -#define HPISR_TRDY 0x04 -#define HPISR_TXDE 0x02 -#define HPISR_RXDF 0x01 - -#define HPIO_290 0 -#define HPIO_260 1 -#define HPIO_250 2 -#define HPIO_240 3 -#define HPIO_230 4 -#define HPIO_220 5 -#define HPIO_210 6 -#define HPIO_3E0 7 - -#define HPMEM_NONE 0 -#define HPMEM_B000 1 -#define HPMEM_C800 2 -#define HPMEM_D000 3 -#define HPMEM_D400 4 -#define HPMEM_D800 5 -#define HPMEM_E000 6 -#define HPMEM_E800 7 - -#define HPIRQ_NONE 0 -#define HPIRQ_5 1 -#define HPIRQ_7 2 -#define HPIRQ_9 3 -#define HPIRQ_10 4 -#define HPIRQ_11 5 -#define HPIRQ_12 6 -#define HPIRQ_15 7 - -#define HIMT_PLAY_DONE 0x00 -#define HIMT_RECORD_DONE 0x01 -#define HIMT_MIDI_EOS 0x02 -#define HIMT_MIDI_OUT 0x03 - -#define HIMT_MIDI_IN_UCHAR 0x0E -#define HIMT_DSP 0x0F - -#define HDEX_BASE 0x92 -#define HDEX_PLAY_START (0 + HDEX_BASE) -#define HDEX_PLAY_STOP (1 + HDEX_BASE) -#define HDEX_PLAY_PAUSE (2 + HDEX_BASE) -#define HDEX_PLAY_RESUME (3 + HDEX_BASE) -#define HDEX_RECORD_START (4 + HDEX_BASE) -#define HDEX_RECORD_STOP (5 + HDEX_BASE) -#define HDEX_MIDI_IN_START (6 + HDEX_BASE) -#define HDEX_MIDI_IN_STOP (7 + HDEX_BASE) -#define HDEX_MIDI_OUT_START (8 + HDEX_BASE) -#define HDEX_MIDI_OUT_STOP (9 + HDEX_BASE) -#define HDEX_AUX_REQ (10 + HDEX_BASE) - -#define HIWORD(l) ((WORD)((((DWORD)(l)) >> 16) & 0xFFFF)) -#define LOWORD(l) ((WORD)(DWORD)(l)) -#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) -#define LOBYTE(w) ((BYTE)(w)) -#define MAKELONG(low,hi) ((long)(((WORD)(low))|(((DWORD)((WORD)(hi)))<<16))) -#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8))) - -#define PCTODSP_OFFSET(w) (USHORT)((w)/2) -#define PCTODSP_BASED(w) (USHORT)(((w)/2) + DSP_BASE_ADDR) -#define DSPTOPC_BASED(w) (((w) - DSP_BASE_ADDR) * 2) - -#ifdef SLOWIO -# undef outb -# undef inb -# define outb outb_p -# define inb inb_p -#endif - -/* JobQueueStruct */ -#define JQS_wStart 0x00 -#define JQS_wSize 0x02 -#define JQS_wHead 0x04 -#define JQS_wTail 0x06 -#define JQS__size 0x08 - -/* DAQueueDataStruct */ -#define DAQDS_wStart 0x00 -#define DAQDS_wSize 0x02 -#define DAQDS_wFormat 0x04 -#define DAQDS_wSampleSize 0x06 -#define DAQDS_wChannels 0x08 -#define DAQDS_wSampleRate 0x0A -#define DAQDS_wIntMsg 0x0C -#define DAQDS_wFlags 0x0E -#define DAQDS__size 0x10 - -typedef u8 BYTE; -typedef u16 USHORT; -typedef u16 WORD; -typedef u32 DWORD; -typedef unsigned long LPDAQD; - -/* Generic FIFO */ -typedef struct { - size_t n, len; - char *data; - int head, tail; -} msnd_fifo; - -typedef struct multisound_dev { - /* Linux device info */ - char *name; - int dsp_minor, mixer_minor; - int ext_midi_dev, hdr_midi_dev; - - /* Hardware resources */ - int io, numio; - int memid, irqid; - int irq, irq_ref; - unsigned char info; - unsigned long base; - - /* Motorola 56k DSP SMA */ - unsigned long SMA; - unsigned long DAPQ, DARQ, MODQ, MIDQ, DSPQ; - unsigned long pwDSPQData, pwMIDQData, pwMODQData; - int dspq_data_buff, dspq_buff_size; - - /* State variables */ - enum { msndClassic, msndPinnacle } type; - mode_t mode; - unsigned long flags; -#define F_RESETTING 0 -#define F_HAVEDIGITAL 1 -#define F_AUDIO_WRITE_INUSE 2 -#define F_WRITING 3 -#define F_WRITEBLOCK 4 -#define F_WRITEFLUSH 5 -#define F_AUDIO_READ_INUSE 6 -#define F_READING 7 -#define F_READBLOCK 8 -#define F_EXT_MIDI_INUSE 9 -#define F_HDR_MIDI_INUSE 10 -#define F_DISABLE_WRITE_NDELAY 11 - wait_queue_head_t writeblock; - wait_queue_head_t readblock; - wait_queue_head_t writeflush; - spinlock_t lock; - int nresets; - unsigned long recsrc; - int left_levels[16]; - int right_levels[16]; - int mixer_mod_count; - int calibrate_signal; - int play_sample_size, play_sample_rate, play_channels; - int play_ndelay; - int rec_sample_size, rec_sample_rate, rec_channels; - int rec_ndelay; - BYTE bCurrentMidiPatch; - - /* Digital audio FIFOs */ - msnd_fifo DAPF, DARF; - int fifosize; - int last_playbank, last_recbank; - - /* MIDI in callback */ - void (*midi_in_interrupt)(struct multisound_dev *); -} multisound_dev_t; - -#ifndef mdelay -# define mdelay(a) udelay((a) * 1000) -#endif - -int msnd_register(multisound_dev_t *dev); -void msnd_unregister(multisound_dev_t *dev); -int msnd_get_num_devs(void); -multisound_dev_t * msnd_get_dev(int i); - -void msnd_init_queue(unsigned long, int start, int size); - -void msnd_fifo_init(msnd_fifo *f); -void msnd_fifo_free(msnd_fifo *f); -int msnd_fifo_alloc(msnd_fifo *f, size_t n); -void msnd_fifo_make_empty(msnd_fifo *f); -int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user); -int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user); - -int msnd_wait_TXDE(multisound_dev_t *dev); -int msnd_wait_HC0(multisound_dev_t *dev); -int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd); -int msnd_send_word(multisound_dev_t *dev, unsigned char high, - unsigned char mid, unsigned char low); -int msnd_upload_host(multisound_dev_t *dev, char *bin, int len); -int msnd_enable_irq(multisound_dev_t *dev); -int msnd_disable_irq(multisound_dev_t *dev); - -#endif /* __MSND_H */ diff -Nru a/drivers/sound/msnd_classic.c b/drivers/sound/msnd_classic.c --- a/drivers/sound/msnd_classic.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3 +0,0 @@ -/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */ -#define MSND_CLASSIC -#include "msnd_pinnacle.c" diff -Nru a/drivers/sound/msnd_classic.h b/drivers/sound/msnd_classic.h --- a/drivers/sound/msnd_classic.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,188 +0,0 @@ -/********************************************************************* - * - * msnd_classic.h - * - * Turtle Beach MultiSound Sound Card Driver for Linux - * - * Some parts of this header file were derived from the Turtle Beach - * MultiSound Driver Development Kit. - * - * Copyright (C) 1998 Andrew Veliath - * Copyright (C) 1993 Turtle Beach Systems, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * $Id: msnd_classic.h,v 1.10 1999/03/21 17:36:09 andrewtv Exp $ - * - ********************************************************************/ -#ifndef __MSND_CLASSIC_H -#define __MSND_CLASSIC_H - -#include - -#define DSP_NUMIO 0x10 - -#define HP_MEMM 0x08 - -#define HP_BITM 0x0E -#define HP_WAIT 0x0D -#define HP_DSPR 0x0A -#define HP_PROR 0x0B -#define HP_BLKS 0x0C - -#define HPPRORESET_OFF 0 -#define HPPRORESET_ON 1 - -#define HPDSPRESET_OFF 0 -#define HPDSPRESET_ON 1 - -#define HPBLKSEL_0 0 -#define HPBLKSEL_1 1 - -#define HPWAITSTATE_0 0 -#define HPWAITSTATE_1 1 - -#define HPBITMODE_16 0 -#define HPBITMODE_8 1 - -#define HIDSP_INT_PLAY_UNDER 0x00 -#define HIDSP_INT_RECORD_OVER 0x01 -#define HIDSP_INPUT_CLIPPING 0x02 -#define HIDSP_MIDI_IN_OVER 0x10 -#define HIDSP_MIDI_OVERRUN_ERR 0x13 - -#define HDEXAR_CLEAR_PEAKS 1 -#define HDEXAR_IN_SET_POTS 2 -#define HDEXAR_AUX_SET_POTS 3 -#define HDEXAR_CAL_A_TO_D 4 -#define HDEXAR_RD_EXT_DSP_BITS 5 - -#define TIME_PRO_RESET_DONE 0x028A -#define TIME_PRO_SYSEX 0x0040 -#define TIME_PRO_RESET 0x0032 - -#define AGND 0x01 -#define SIGNAL 0x02 - -#define EXT_DSP_BIT_DCAL 0x0001 -#define EXT_DSP_BIT_MIDI_CON 0x0002 - -#define BUFFSIZE 0x8000 -#define HOSTQ_SIZE 0x40 - -#define SRAM_CNTL_START 0x7F00 -#define SMA_STRUCT_START 0x7F40 - -#define DAP_BUFF_SIZE 0x2400 -#define DAR_BUFF_SIZE 0x2000 - -#define DAPQ_STRUCT_SIZE 0x10 -#define DARQ_STRUCT_SIZE 0x10 -#define DAPQ_BUFF_SIZE (3 * 0x10) -#define DARQ_BUFF_SIZE (3 * 0x10) -#define MODQ_BUFF_SIZE 0x400 -#define MIDQ_BUFF_SIZE 0x200 -#define DSPQ_BUFF_SIZE 0x40 - -#define DAPQ_DATA_BUFF 0x6C00 -#define DARQ_DATA_BUFF 0x6C30 -#define MODQ_DATA_BUFF 0x6C60 -#define MIDQ_DATA_BUFF 0x7060 -#define DSPQ_DATA_BUFF 0x7260 - -#define DAPQ_OFFSET SRAM_CNTL_START -#define DARQ_OFFSET (SRAM_CNTL_START + 0x08) -#define MODQ_OFFSET (SRAM_CNTL_START + 0x10) -#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18) -#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20) - -#define MOP_SYNTH 0x10 -#define MOP_EXTOUT 0x32 -#define MOP_EXTTHRU 0x02 -#define MOP_OUTMASK 0x01 - -#define MIP_EXTIN 0x01 -#define MIP_SYNTH 0x00 -#define MIP_INMASK 0x32 - -/* Classic SMA Common Data */ -#define SMA_wCurrPlayBytes 0x0000 -#define SMA_wCurrRecordBytes 0x0002 -#define SMA_wCurrPlayVolLeft 0x0004 -#define SMA_wCurrPlayVolRight 0x0006 -#define SMA_wCurrInVolLeft 0x0008 -#define SMA_wCurrInVolRight 0x000a -#define SMA_wUser_3 0x000c -#define SMA_wUser_4 0x000e -#define SMA_dwUser_5 0x0010 -#define SMA_dwUser_6 0x0014 -#define SMA_wUser_7 0x0018 -#define SMA_wReserved_A 0x001a -#define SMA_wReserved_B 0x001c -#define SMA_wReserved_C 0x001e -#define SMA_wReserved_D 0x0020 -#define SMA_wReserved_E 0x0022 -#define SMA_wReserved_F 0x0024 -#define SMA_wReserved_G 0x0026 -#define SMA_wReserved_H 0x0028 -#define SMA_wCurrDSPStatusFlags 0x002a -#define SMA_wCurrHostStatusFlags 0x002c -#define SMA_wCurrInputTagBits 0x002e -#define SMA_wCurrLeftPeak 0x0030 -#define SMA_wCurrRightPeak 0x0032 -#define SMA_wExtDSPbits 0x0034 -#define SMA_bExtHostbits 0x0036 -#define SMA_bBoardLevel 0x0037 -#define SMA_bInPotPosRight 0x0038 -#define SMA_bInPotPosLeft 0x0039 -#define SMA_bAuxPotPosRight 0x003a -#define SMA_bAuxPotPosLeft 0x003b -#define SMA_wCurrMastVolLeft 0x003c -#define SMA_wCurrMastVolRight 0x003e -#define SMA_bUser_12 0x0040 -#define SMA_bUser_13 0x0041 -#define SMA_wUser_14 0x0042 -#define SMA_wUser_15 0x0044 -#define SMA_wCalFreqAtoD 0x0046 -#define SMA_wUser_16 0x0048 -#define SMA_wUser_17 0x004a -#define SMA__size 0x004c - -#ifdef HAVE_DSPCODEH -# include "msndperm.c" -# include "msndinit.c" -# define PERMCODE msndperm -# define INITCODE msndinit -# define PERMCODESIZE sizeof(msndperm) -# define INITCODESIZE sizeof(msndinit) -#else -# ifndef CONFIG_MSNDCLAS_INIT_FILE -# define CONFIG_MSNDCLAS_INIT_FILE \ - "/etc/sound/msndinit.bin" -# endif -# ifndef CONFIG_MSNDCLAS_PERM_FILE -# define CONFIG_MSNDCLAS_PERM_FILE \ - "/etc/sound/msndperm.bin" -# endif -# define PERMCODEFILE CONFIG_MSNDCLAS_PERM_FILE -# define INITCODEFILE CONFIG_MSNDCLAS_INIT_FILE -# define PERMCODE dspini -# define INITCODE permini -# define PERMCODESIZE sizeof_dspini -# define INITCODESIZE sizeof_permini -#endif -#define LONGNAME "MultiSound (Classic/Monterey/Tahiti)" - -#endif /* __MSND_CLASSIC_H */ diff -Nru a/drivers/sound/msnd_pinnacle.c b/drivers/sound/msnd_pinnacle.c --- a/drivers/sound/msnd_pinnacle.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1894 +0,0 @@ -/********************************************************************* - * - * Turtle Beach MultiSound Sound Card Driver for Linux - * Linux 2.0/2.2 Version - * - * msnd_pinnacle.c / msnd_classic.c - * - * -- If MSND_CLASSIC is defined: - * - * -> driver for Turtle Beach Classic/Monterey/Tahiti - * - * -- Else - * - * -> driver for Turtle Beach Pinnacle/Fiji - * - * Copyright (C) 1998 Andrew Veliath - * - * 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. - * - * $Id: msnd_pinnacle.c,v 1.8 2000/12/30 00:33:21 sycamore Exp $ - * - * 12-3-2000 Modified IO port validation Steve Sycamore - * - * - * $$$: msnd_pinnacle.c,v 1.75 1999/03/21 16:50:09 andrewtv $$$ $ - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sound_config.h" -#include "sound_firmware.h" -#ifdef MSND_CLASSIC -# ifndef __alpha__ -# define SLOWIO -# endif -#endif -#include "msnd.h" -#ifdef MSND_CLASSIC -# ifdef CONFIG_MSNDCLAS_HAVE_BOOT -# define HAVE_DSPCODEH -# endif -# include "msnd_classic.h" -# define LOGNAME "msnd_classic" -#else -# ifdef CONFIG_MSNDPIN_HAVE_BOOT -# define HAVE_DSPCODEH -# endif -# include "msnd_pinnacle.h" -# define LOGNAME "msnd_pinnacle" -#endif - -#ifndef CONFIG_MSND_WRITE_NDELAY -# define CONFIG_MSND_WRITE_NDELAY 1 -#endif - -#define get_play_delay_jiffies(size) ((size) * HZ * \ - dev.play_sample_size / 8 / \ - dev.play_sample_rate / \ - dev.play_channels) - -#define get_rec_delay_jiffies(size) ((size) * HZ * \ - dev.rec_sample_size / 8 / \ - dev.rec_sample_rate / \ - dev.rec_channels) - -static multisound_dev_t dev; - -#ifndef HAVE_DSPCODEH -static char *dspini, *permini; -static int sizeof_dspini, sizeof_permini; -#endif - -static int dsp_full_reset(void); -static void dsp_write_flush(void); - -static __inline__ int chk_send_dsp_cmd(multisound_dev_t *dev, register BYTE cmd) -{ - if (msnd_send_dsp_cmd(dev, cmd) == 0) - return 0; - dsp_full_reset(); - return msnd_send_dsp_cmd(dev, cmd); -} - -static void reset_play_queue(void) -{ - int n; - LPDAQD lpDAQ; - - dev.last_playbank = -1; - isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wHead); - isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wTail); - - for (n = 0, lpDAQ = dev.base + DAPQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { - isa_writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart); - isa_writew(0, lpDAQ + DAQDS_wSize); - isa_writew(1, lpDAQ + DAQDS_wFormat); - isa_writew(dev.play_sample_size, lpDAQ + DAQDS_wSampleSize); - isa_writew(dev.play_channels, lpDAQ + DAQDS_wChannels); - isa_writew(dev.play_sample_rate, lpDAQ + DAQDS_wSampleRate); - isa_writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg); - isa_writew(n, lpDAQ + DAQDS_wFlags); - } -} - -static void reset_record_queue(void) -{ - int n; - LPDAQD lpDAQ; - unsigned long flags; - - dev.last_recbank = 2; - isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DARQ + JQS_wHead); - isa_writew(PCTODSP_OFFSET(dev.last_recbank * DAQDS__size), dev.DARQ + JQS_wTail); - - /* Critical section: bank 1 access */ - spin_lock_irqsave(&dev.lock, flags); - outb(HPBLKSEL_1, dev.io + HP_BLKS); - isa_memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); - outb(HPBLKSEL_0, dev.io + HP_BLKS); - spin_unlock_irqrestore(&dev.lock, flags); - - for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { - isa_writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart); - isa_writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize); - isa_writew(1, lpDAQ + DAQDS_wFormat); - isa_writew(dev.rec_sample_size, lpDAQ + DAQDS_wSampleSize); - isa_writew(dev.rec_channels, lpDAQ + DAQDS_wChannels); - isa_writew(dev.rec_sample_rate, lpDAQ + DAQDS_wSampleRate); - isa_writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg); - isa_writew(n, lpDAQ + DAQDS_wFlags); - } -} - -static void reset_queues(void) -{ - if (dev.mode & FMODE_WRITE) { - msnd_fifo_make_empty(&dev.DAPF); - reset_play_queue(); - } - if (dev.mode & FMODE_READ) { - msnd_fifo_make_empty(&dev.DARF); - reset_record_queue(); - } -} - -static int dsp_set_format(struct file *file, int val) -{ - int data, i; - LPDAQD lpDAQ, lpDARQ; - - lpDAQ = dev.base + DAPQ_DATA_BUFF; - lpDARQ = dev.base + DARQ_DATA_BUFF; - - switch (val) { - case AFMT_U8: - case AFMT_S16_LE: - data = val; - break; - default: - data = DEFSAMPLESIZE; - break; - } - - for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { - if (file->f_mode & FMODE_WRITE) - isa_writew(data, lpDAQ + DAQDS_wSampleSize); - if (file->f_mode & FMODE_READ) - isa_writew(data, lpDARQ + DAQDS_wSampleSize); - } - if (file->f_mode & FMODE_WRITE) - dev.play_sample_size = data; - if (file->f_mode & FMODE_READ) - dev.rec_sample_size = data; - - return data; -} - -static int dsp_get_frag_size(void) -{ - int size; - size = dev.fifosize / 4; - if (size > 32 * 1024) - size = 32 * 1024; - return size; -} - -static int dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int val, i, data, tmp; - LPDAQD lpDAQ, lpDARQ; - audio_buf_info abinfo; - unsigned long flags; - - lpDAQ = dev.base + DAPQ_DATA_BUFF; - lpDARQ = dev.base + DARQ_DATA_BUFF; - - switch (cmd) { - case SNDCTL_DSP_SUBDIVIDE: - case SNDCTL_DSP_SETFRAGMENT: - case SNDCTL_DSP_SETDUPLEX: - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETIPTR: - case SNDCTL_DSP_GETOPTR: - case SNDCTL_DSP_MAPINBUF: - case SNDCTL_DSP_MAPOUTBUF: - return -EINVAL; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&dev.lock, flags); - abinfo.fragsize = dsp_get_frag_size(); - abinfo.bytes = dev.DAPF.n - dev.DAPF.len; - abinfo.fragstotal = dev.DAPF.n / abinfo.fragsize; - abinfo.fragments = abinfo.bytes / abinfo.fragsize; - spin_unlock_irqrestore(&dev.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; - spin_lock_irqsave(&dev.lock, flags); - abinfo.fragsize = dsp_get_frag_size(); - abinfo.bytes = dev.DARF.n - dev.DARF.len; - abinfo.fragstotal = dev.DARF.n / abinfo.fragsize; - abinfo.fragments = abinfo.bytes / abinfo.fragsize; - spin_unlock_irqrestore(&dev.lock, flags); - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_RESET: - dev.nresets = 0; - reset_queues(); - return 0; - - case SNDCTL_DSP_SYNC: - dsp_write_flush(); - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - tmp = dsp_get_frag_size(); - if (put_user(tmp, (int *)arg)) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETFMTS: - val = AFMT_S16_LE | AFMT_U8; - if (put_user(val, (int *)arg)) - return -EFAULT; - return 0; - - case SNDCTL_DSP_SETFMT: - if (get_user(val, (int *)arg)) - return -EFAULT; - - if (file->f_mode & FMODE_WRITE) - data = val == AFMT_QUERY - ? dev.play_sample_size - : dsp_set_format(file, val); - else - data = val == AFMT_QUERY - ? dev.rec_sample_size - : dsp_set_format(file, val); - - if (put_user(data, (int *)arg)) - return -EFAULT; - return 0; - - case SNDCTL_DSP_NONBLOCK: - if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags) && - file->f_mode & FMODE_WRITE) - dev.play_ndelay = 1; - if (file->f_mode & FMODE_READ) - dev.rec_ndelay = 1; - return 0; - - case SNDCTL_DSP_GETCAPS: - val = DSP_CAP_DUPLEX | DSP_CAP_BATCH; - if (put_user(val, (int *)arg)) - return -EFAULT; - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) - return -EFAULT; - - if (val < 8000) - val = 8000; - - if (val > 48000) - val = 48000; - - data = val; - - for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { - if (file->f_mode & FMODE_WRITE) - isa_writew(data, lpDAQ + DAQDS_wSampleRate); - if (file->f_mode & FMODE_READ) - isa_writew(data, lpDARQ + DAQDS_wSampleRate); - } - if (file->f_mode & FMODE_WRITE) - dev.play_sample_rate = data; - if (file->f_mode & FMODE_READ) - dev.rec_sample_rate = data; - - if (put_user(data, (int *)arg)) - return -EFAULT; - return 0; - - case SNDCTL_DSP_CHANNELS: - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - - if (cmd == SNDCTL_DSP_CHANNELS) { - switch (val) { - case 1: - case 2: - data = val; - break; - default: - val = data = 2; - break; - } - } else { - switch (val) { - case 0: - data = 1; - break; - default: - val = 1; - case 1: - data = 2; - break; - } - } - - for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { - if (file->f_mode & FMODE_WRITE) - isa_writew(data, lpDAQ + DAQDS_wChannels); - if (file->f_mode & FMODE_READ) - isa_writew(data, lpDARQ + DAQDS_wChannels); - } - if (file->f_mode & FMODE_WRITE) - dev.play_channels = data; - if (file->f_mode & FMODE_READ) - dev.rec_channels = data; - - if (put_user(val, (int *)arg)) - return -EFAULT; - return 0; - } - - return -EINVAL; -} - -static int mixer_get(int d) -{ - if (d > 31) - return -EINVAL; - - switch (d) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_IMIX: - case SOUND_MIXER_LINE1: -#ifndef MSND_CLASSIC - case SOUND_MIXER_MIC: - case SOUND_MIXER_SYNTH: -#endif - return (dev.left_levels[d] >> 8) * 100 / 0xff | - (((dev.right_levels[d] >> 8) * 100 / 0xff) << 8); - default: - return 0; - } -} - -#define update_volm(a,b) \ - isa_writew((dev.left_levels[a] >> 1) * \ - isa_readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \ - dev.SMA + SMA_##b##Left); \ - isa_writew((dev.right_levels[a] >> 1) * \ - isa_readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \ - dev.SMA + SMA_##b##Right); - -#define update_potm(d,s,ar) \ - isa_writeb((dev.left_levels[d] >> 8) * \ - isa_readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \ - dev.SMA + SMA_##s##Left); \ - isa_writeb((dev.right_levels[d] >> 8) * \ - isa_readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \ - dev.SMA + SMA_##s##Right); \ - if (msnd_send_word(&dev, 0, 0, ar) == 0) \ - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - -#define update_pot(d,s,ar) \ - isa_writeb(dev.left_levels[d] >> 8, \ - dev.SMA + SMA_##s##Left); \ - isa_writeb(dev.right_levels[d] >> 8, \ - dev.SMA + SMA_##s##Right); \ - if (msnd_send_word(&dev, 0, 0, ar) == 0) \ - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - -static int mixer_set(int d, int value) -{ - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int bLeft, bRight; - int wLeft, wRight; - int updatemaster = 0; - - if (d > 31) - return -EINVAL; - - bLeft = left * 0xff / 100; - wLeft = left * 0xffff / 100; - - bRight = right * 0xff / 100; - wRight = right * 0xffff / 100; - - dev.left_levels[d] = wLeft; - dev.right_levels[d] = wRight; - - switch (d) { - /* master volume unscaled controls */ - case SOUND_MIXER_LINE: /* line pot control */ - /* scaled by IMIX in digital mix */ - isa_writeb(bLeft, dev.SMA + SMA_bInPotPosLeft); - isa_writeb(bRight, dev.SMA + SMA_bInPotPosRight); - if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - break; -#ifndef MSND_CLASSIC - case SOUND_MIXER_MIC: /* mic pot control */ - /* scaled by IMIX in digital mix */ - isa_writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft); - isa_writeb(bRight, dev.SMA + SMA_bMicPotPosRight); - if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - break; -#endif - case SOUND_MIXER_VOLUME: /* master volume */ - isa_writew(wLeft, dev.SMA + SMA_wCurrMastVolLeft); - isa_writew(wRight, dev.SMA + SMA_wCurrMastVolRight); - /* fall through */ - - case SOUND_MIXER_LINE1: /* aux pot control */ - /* scaled by master volume */ - /* fall through */ - - /* digital controls */ - case SOUND_MIXER_SYNTH: /* synth vol (dsp mix) */ - case SOUND_MIXER_PCM: /* pcm vol (dsp mix) */ - case SOUND_MIXER_IMIX: /* input monitor (dsp mix) */ - /* scaled by master volume */ - updatemaster = 1; - break; - - default: - return 0; - } - - if (updatemaster) { - /* update master volume scaled controls */ - update_volm(SOUND_MIXER_PCM, wCurrPlayVol); - update_volm(SOUND_MIXER_IMIX, wCurrInVol); -#ifndef MSND_CLASSIC - update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol); -#endif - update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS); - } - - return mixer_get(d); -} - -static void mixer_setup(void) -{ - update_pot(SOUND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); - update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS); - update_volm(SOUND_MIXER_PCM, wCurrPlayVol); - update_volm(SOUND_MIXER_IMIX, wCurrInVol); -#ifndef MSND_CLASSIC - update_pot(SOUND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS); - update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol); -#endif -} - -static unsigned long set_recsrc(unsigned long recsrc) -{ - if (dev.recsrc == recsrc) - return dev.recsrc; -#ifdef HAVE_NORECSRC - else if (recsrc == 0) - dev.recsrc = 0; -#endif - else - dev.recsrc ^= recsrc; - -#ifndef MSND_CLASSIC - if (dev.recsrc & SOUND_MASK_IMIX) { - if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - } - else if (dev.recsrc & SOUND_MASK_SYNTH) { - if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - } - else if ((dev.recsrc & SOUND_MASK_DIGITAL1) && test_bit(F_HAVEDIGITAL, &dev.flags)) { - if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - } - else { -#ifdef HAVE_NORECSRC - /* Select no input (?) */ - dev.recsrc = 0; -#else - dev.recsrc = SOUND_MASK_IMIX; - if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); -#endif - } -#endif /* MSND_CLASSIC */ - - return dev.recsrc; -} - -static unsigned long force_recsrc(unsigned long recsrc) -{ - dev.recsrc = 0; - return set_recsrc(recsrc); -} - -#define set_mixer_info() \ - 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) -{ - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - set_mixer_info(); - info.modify_counter = dev.mixer_mod_count; - return copy_to_user((void *)arg, &info, sizeof(info)); - } else if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - set_mixer_info(); - return copy_to_user((void *)arg, &info, sizeof(info)); - } else if (cmd == SOUND_MIXER_PRIVATE1) { - dev.nresets = 0; - dsp_full_reset(); - return 0; - } else if (((cmd >> 8) & 0xff) == 'M') { - int val = 0; - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - switch (cmd & 0xff) { - case SOUND_MIXER_RECSRC: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = set_recsrc(val); - break; - - default: - if (get_user(val, (int *)arg)) - return -EFAULT; - val = mixer_set(cmd & 0xff, val); - break; - } - ++dev.mixer_mod_count; - return put_user(val, (int *)arg); - } else { - switch (cmd & 0xff) { - case SOUND_MIXER_RECSRC: - val = dev.recsrc; - break; - - case SOUND_MIXER_DEVMASK: - case SOUND_MIXER_STEREODEVS: - val = SOUND_MASK_PCM | - SOUND_MASK_LINE | - SOUND_MASK_IMIX | - SOUND_MASK_LINE1 | -#ifndef MSND_CLASSIC - SOUND_MASK_MIC | - SOUND_MASK_SYNTH | -#endif - SOUND_MASK_VOLUME; - break; - - case SOUND_MIXER_RECMASK: -#ifdef MSND_CLASSIC - val = 0; -#else - val = SOUND_MASK_IMIX | - SOUND_MASK_SYNTH; - if (test_bit(F_HAVEDIGITAL, &dev.flags)) - val |= SOUND_MASK_DIGITAL1; -#endif - break; - - case SOUND_MIXER_CAPS: - val = SOUND_CAP_EXCL_INPUT; - break; - - default: - if ((val = mixer_get(cmd & 0xff)) < 0) - return -EINVAL; - break; - } - } - - return put_user(val, (int *)arg); - } - - return -EINVAL; -} - -static int dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - int minor = MINOR(inode->i_rdev); - - if (cmd == OSS_GETVERSION) { - int sound_version = SOUND_VERSION; - return put_user(sound_version, (int *)arg); - } - - if (minor == dev.dsp_minor) - return dsp_ioctl(file, cmd, arg); - else if (minor == dev.mixer_minor) - return mixer_ioctl(cmd, arg); - - return -EINVAL; -} - -static void dsp_write_flush(void) -{ - if (!(dev.mode & FMODE_WRITE) || !test_bit(F_WRITING, &dev.flags)) - return; - set_bit(F_WRITEFLUSH, &dev.flags); - interruptible_sleep_on_timeout( - &dev.writeflush, - get_play_delay_jiffies(dev.DAPF.len)); - clear_bit(F_WRITEFLUSH, &dev.flags); - if (!signal_pending(current)) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(get_play_delay_jiffies(DAP_BUFF_SIZE)); - } - clear_bit(F_WRITING, &dev.flags); -} - -static void dsp_halt(struct file *file) -{ - if ((file ? file->f_mode : dev.mode) & FMODE_READ) { - clear_bit(F_READING, &dev.flags); - chk_send_dsp_cmd(&dev, HDEX_RECORD_STOP); - msnd_disable_irq(&dev); - if (file) { - printk(KERN_DEBUG LOGNAME ": Stopping read for %p\n", file); - dev.mode &= ~FMODE_READ; - } - clear_bit(F_AUDIO_READ_INUSE, &dev.flags); - } - if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) { - if (test_bit(F_WRITING, &dev.flags)) { - dsp_write_flush(); - chk_send_dsp_cmd(&dev, HDEX_PLAY_STOP); - } - msnd_disable_irq(&dev); - if (file) { - printk(KERN_DEBUG LOGNAME ": Stopping write for %p\n", file); - dev.mode &= ~FMODE_WRITE; - } - clear_bit(F_AUDIO_WRITE_INUSE, &dev.flags); - } -} - -static int dsp_release(struct file *file) -{ - dsp_halt(file); - return 0; -} - -static int dsp_open(struct file *file) -{ - if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) { - set_bit(F_AUDIO_WRITE_INUSE, &dev.flags); - clear_bit(F_WRITING, &dev.flags); - msnd_fifo_make_empty(&dev.DAPF); - reset_play_queue(); - if (file) { - printk(KERN_DEBUG LOGNAME ": Starting write for %p\n", file); - dev.mode |= FMODE_WRITE; - } - msnd_enable_irq(&dev); - } - if ((file ? file->f_mode : dev.mode) & FMODE_READ) { - set_bit(F_AUDIO_READ_INUSE, &dev.flags); - clear_bit(F_READING, &dev.flags); - msnd_fifo_make_empty(&dev.DARF); - reset_record_queue(); - if (file) { - printk(KERN_DEBUG LOGNAME ": Starting read for %p\n", file); - dev.mode |= FMODE_READ; - } - msnd_enable_irq(&dev); - } - return 0; -} - -static void set_default_play_audio_parameters(void) -{ - dev.play_sample_size = DEFSAMPLESIZE; - dev.play_sample_rate = DEFSAMPLERATE; - dev.play_channels = DEFCHANNELS; -} - -static void set_default_rec_audio_parameters(void) -{ - dev.rec_sample_size = DEFSAMPLESIZE; - dev.rec_sample_rate = DEFSAMPLERATE; - dev.rec_channels = DEFCHANNELS; -} - -static void set_default_audio_parameters(void) -{ - set_default_play_audio_parameters(); - set_default_rec_audio_parameters(); -} - -static int dev_open(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - int err = 0; - - if (minor == dev.dsp_minor) { - if ((file->f_mode & FMODE_WRITE && - test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) || - (file->f_mode & FMODE_READ && - test_bit(F_AUDIO_READ_INUSE, &dev.flags))) - return -EBUSY; - - if ((err = dsp_open(file)) >= 0) { - dev.nresets = 0; - if (file->f_mode & FMODE_WRITE) { - set_default_play_audio_parameters(); - if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags)) - dev.play_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0; - else - dev.play_ndelay = 0; - } - if (file->f_mode & FMODE_READ) { - set_default_rec_audio_parameters(); - dev.rec_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0; - } - } - } - else if (minor == dev.mixer_minor) { - /* nothing */ - } else - err = -EINVAL; - - return err; -} - -static int dev_release(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - int err = 0; - - lock_kernel(); - if (minor == dev.dsp_minor) - err = dsp_release(file); - else if (minor == dev.mixer_minor) { - /* nothing */ - } else - err = -EINVAL; - unlock_kernel(); - return err; -} - -static __inline__ int pack_DARQ_to_DARF(register int bank) -{ - register int size, n, timeout = 3; - register WORD wTmp; - LPDAQD DAQD; - - /* Increment the tail and check for queue wrap */ - wTmp = isa_readw(dev.DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size); - if (wTmp > isa_readw(dev.DARQ + JQS_wSize)) - wTmp = 0; - while (wTmp == isa_readw(dev.DARQ + JQS_wHead) && timeout--) - udelay(1); - isa_writew(wTmp, dev.DARQ + JQS_wTail); - - /* Get our digital audio queue struct */ - DAQD = bank * DAQDS__size + dev.base + DARQ_DATA_BUFF; - - /* Get length of data */ - size = isa_readw(DAQD + DAQDS_wSize); - - /* Read data from the head (unprotected bank 1 access okay - since this is only called inside an interrupt) */ - outb(HPBLKSEL_1, dev.io + HP_BLKS); - if ((n = msnd_fifo_write( - &dev.DARF, - (char *)(dev.base + bank * DAR_BUFF_SIZE), - size, 0)) <= 0) { - outb(HPBLKSEL_0, dev.io + HP_BLKS); - return n; - } - outb(HPBLKSEL_0, dev.io + HP_BLKS); - - return 1; -} - -static __inline__ int pack_DAPF_to_DAPQ(register int start) -{ - register WORD DAPQ_tail; - register int protect = start, nbanks = 0; - LPDAQD DAQD; - - DAPQ_tail = isa_readw(dev.DAPQ + JQS_wTail); - while (DAPQ_tail != isa_readw(dev.DAPQ + JQS_wHead) || start) { - register int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size); - register int n; - unsigned long flags; - - /* Write the data to the new tail */ - if (protect) { - /* Critical section: protect fifo in non-interrupt */ - spin_lock_irqsave(&dev.lock, flags); - if ((n = msnd_fifo_read( - &dev.DAPF, - (char *)(dev.base + bank_num * DAP_BUFF_SIZE), - DAP_BUFF_SIZE, 0)) < 0) { - spin_unlock_irqrestore(&dev.lock, flags); - return n; - } - spin_unlock_irqrestore(&dev.lock, flags); - } else { - if ((n = msnd_fifo_read( - &dev.DAPF, - (char *)(dev.base + bank_num * DAP_BUFF_SIZE), - DAP_BUFF_SIZE, 0)) < 0) { - return n; - } - } - if (!n) - break; - - if (start) - start = 0; - - /* Get our digital audio queue struct */ - DAQD = bank_num * DAQDS__size + dev.base + DAPQ_DATA_BUFF; - - /* Write size of this bank */ - isa_writew(n, DAQD + DAQDS_wSize); - ++nbanks; - - /* Then advance the tail */ - DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size); - isa_writew(DAPQ_tail, dev.DAPQ + JQS_wTail); - /* Tell the DSP to play the bank */ - msnd_send_dsp_cmd(&dev, HDEX_PLAY_START); - } - return nbanks; -} - -static int dsp_read(char *buf, size_t len) -{ - int count = len; - - while (count > 0) { - int n; - unsigned long flags; - - /* Critical section: protect fifo in non-interrupt */ - spin_lock_irqsave(&dev.lock, flags); - if ((n = msnd_fifo_read(&dev.DARF, buf, count, 1)) < 0) { - printk(KERN_WARNING LOGNAME ": FIFO read error\n"); - spin_unlock_irqrestore(&dev.lock, flags); - return n; - } - spin_unlock_irqrestore(&dev.lock, flags); - buf += n; - count -= n; - - if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) { - dev.last_recbank = -1; - if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0) - set_bit(F_READING, &dev.flags); - } - - if (dev.rec_ndelay) - return count == len ? -EAGAIN : len - count; - - if (count > 0) { - set_bit(F_READBLOCK, &dev.flags); - if (!interruptible_sleep_on_timeout( - &dev.readblock, - get_rec_delay_jiffies(DAR_BUFF_SIZE))) - clear_bit(F_READING, &dev.flags); - clear_bit(F_READBLOCK, &dev.flags); - if (signal_pending(current)) - return -EINTR; - } - } - - return len - count; -} - -static int dsp_write(const char *buf, size_t len) -{ - int count = len; - - while (count > 0) { - int n; - unsigned long flags; - - /* Critical section: protect fifo in non-interrupt */ - spin_lock_irqsave(&dev.lock, flags); - if ((n = msnd_fifo_write(&dev.DAPF, buf, count, 1)) < 0) { - printk(KERN_WARNING LOGNAME ": FIFO write error\n"); - spin_unlock_irqrestore(&dev.lock, flags); - return n; - } - spin_unlock_irqrestore(&dev.lock, flags); - buf += n; - count -= n; - - if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) { - dev.last_playbank = -1; - if (pack_DAPF_to_DAPQ(1) > 0) - set_bit(F_WRITING, &dev.flags); - } - - if (dev.play_ndelay) - return count == len ? -EAGAIN : len - count; - - if (count > 0) { - set_bit(F_WRITEBLOCK, &dev.flags); - interruptible_sleep_on_timeout( - &dev.writeblock, - get_play_delay_jiffies(DAP_BUFF_SIZE)); - clear_bit(F_WRITEBLOCK, &dev.flags); - if (signal_pending(current)) - return -EINTR; - } - } - - return len - count; -} - -static ssize_t dev_read(struct file *file, char *buf, size_t count, loff_t *off) -{ - int minor = MINOR(file->f_dentry->d_inode->i_rdev); - if (minor == dev.dsp_minor) - return dsp_read(buf, count); - else - return -EINVAL; -} - -static ssize_t dev_write(struct file *file, const char *buf, size_t count, loff_t *off) -{ - int minor = MINOR(file->f_dentry->d_inode->i_rdev); - if (minor == dev.dsp_minor) - return dsp_write(buf, count); - else - return -EINVAL; -} - -static __inline__ void eval_dsp_msg(register WORD wMessage) -{ - switch (HIBYTE(wMessage)) { - case HIMT_PLAY_DONE: - if (dev.last_playbank == LOBYTE(wMessage) || !test_bit(F_WRITING, &dev.flags)) - break; - dev.last_playbank = LOBYTE(wMessage); - - if (pack_DAPF_to_DAPQ(0) <= 0) { - if (!test_bit(F_WRITEBLOCK, &dev.flags)) { - if (test_and_clear_bit(F_WRITEFLUSH, &dev.flags)) - wake_up_interruptible(&dev.writeflush); - } - clear_bit(F_WRITING, &dev.flags); - } - - if (test_bit(F_WRITEBLOCK, &dev.flags)) - wake_up_interruptible(&dev.writeblock); - break; - - case HIMT_RECORD_DONE: - if (dev.last_recbank == LOBYTE(wMessage)) - break; - dev.last_recbank = LOBYTE(wMessage); - - pack_DARQ_to_DARF(dev.last_recbank); - - if (test_bit(F_READBLOCK, &dev.flags)) - wake_up_interruptible(&dev.readblock); - break; - - case HIMT_DSP: - switch (LOBYTE(wMessage)) { -#ifndef MSND_CLASSIC - case HIDSP_PLAY_UNDER: -#endif - case HIDSP_INT_PLAY_UNDER: -/* printk(KERN_DEBUG LOGNAME ": Play underflow\n"); */ - clear_bit(F_WRITING, &dev.flags); - break; - - case HIDSP_INT_RECORD_OVER: -/* printk(KERN_DEBUG LOGNAME ": Record overflow\n"); */ - clear_bit(F_READING, &dev.flags); - break; - - default: -/* printk(KERN_DEBUG LOGNAME ": DSP message %d 0x%02x\n", - LOBYTE(wMessage), LOBYTE(wMessage)); */ - break; - } - break; - - case HIMT_MIDI_IN_UCHAR: - if (dev.midi_in_interrupt) - (*dev.midi_in_interrupt)(&dev); - break; - - default: -/* printk(KERN_DEBUG LOGNAME ": HIMT message %d 0x%02x\n", HIBYTE(wMessage), HIBYTE(wMessage)); */ - break; - } -} - -static void intr(int irq, void *dev_id, struct pt_regs *regs) -{ - /* Send ack to DSP */ - inb(dev.io + HP_RXL); - - /* Evaluate queued DSP messages */ - while (isa_readw(dev.DSPQ + JQS_wTail) != isa_readw(dev.DSPQ + JQS_wHead)) { - register WORD wTmp; - - eval_dsp_msg(isa_readw(dev.pwDSPQData + 2*isa_readw(dev.DSPQ + JQS_wHead))); - - if ((wTmp = isa_readw(dev.DSPQ + JQS_wHead) + 1) > isa_readw(dev.DSPQ + JQS_wSize)) - isa_writew(0, dev.DSPQ + JQS_wHead); - else - isa_writew(wTmp, dev.DSPQ + JQS_wHead); - } -} - -static struct file_operations dev_fileops = { - owner: THIS_MODULE, - read: dev_read, - write: dev_write, - ioctl: dev_ioctl, - open: dev_open, - release: dev_release, -}; - -static int reset_dsp(void) -{ - int timeout = 100; - - outb(HPDSPRESET_ON, dev.io + HP_DSPR); - mdelay(1); -#ifndef MSND_CLASSIC - dev.info = inb(dev.io + HP_INFO); -#endif - outb(HPDSPRESET_OFF, dev.io + HP_DSPR); - mdelay(1); - while (timeout-- > 0) { - if (inb(dev.io + HP_CVR) == HP_CVR_DEF) - return 0; - mdelay(1); - } - printk(KERN_ERR LOGNAME ": Cannot reset DSP\n"); - - return -EIO; -} - -static int __init probe_multisound(void) -{ -#ifndef MSND_CLASSIC - char *xv, *rev = NULL; - char *pin = "Pinnacle", *fiji = "Fiji"; - char *pinfiji = "Pinnacle/Fiji"; -#endif - - if (check_region(dev.io, dev.numio)) { - printk(KERN_ERR LOGNAME ": I/O port conflict\n"); - return -ENODEV; - } - request_region(dev.io, dev.numio, "probing"); - - if (reset_dsp() < 0) { - release_region(dev.io, dev.numio); - return -ENODEV; - } - -#ifdef MSND_CLASSIC - dev.name = "Classic/Tahiti/Monterey"; - printk(KERN_INFO LOGNAME ": %s, " -#else - switch (dev.info >> 4) { - case 0xf: xv = "<= 1.15"; break; - case 0x1: xv = "1.18/1.2"; break; - case 0x2: xv = "1.3"; break; - case 0x3: xv = "1.4"; break; - default: xv = "unknown"; break; - } - - switch (dev.info & 0x7) { - case 0x0: rev = "I"; dev.name = pin; break; - case 0x1: rev = "F"; dev.name = pin; break; - case 0x2: rev = "G"; dev.name = pin; break; - case 0x3: rev = "H"; dev.name = pin; break; - case 0x4: rev = "E"; dev.name = fiji; break; - case 0x5: rev = "C"; dev.name = fiji; break; - case 0x6: rev = "D"; dev.name = fiji; break; - case 0x7: - rev = "A-B (Fiji) or A-E (Pinnacle)"; - dev.name = pinfiji; - break; - } - printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, " -#endif /* MSND_CLASSIC */ - "I/O 0x%x-0x%x, IRQ %d, memory mapped to 0x%lX-0x%lX\n", - dev.name, -#ifndef MSND_CLASSIC - rev, xv, -#endif - dev.io, dev.io + dev.numio - 1, - dev.irq, - dev.base, dev.base + 0x7fff); - - release_region(dev.io, dev.numio); - return 0; -} - -static int init_sma(void) -{ - static int initted; - WORD mastVolLeft, mastVolRight; - unsigned long flags; - -#ifdef MSND_CLASSIC - outb(dev.memid, dev.io + HP_MEMM); -#endif - outb(HPBLKSEL_0, dev.io + HP_BLKS); - if (initted) { - mastVolLeft = isa_readw(dev.SMA + SMA_wCurrMastVolLeft); - mastVolRight = isa_readw(dev.SMA + SMA_wCurrMastVolRight); - } else - mastVolLeft = mastVolRight = 0; - isa_memset_io(dev.base, 0, 0x8000); - - /* Critical section: bank 1 access */ - spin_lock_irqsave(&dev.lock, flags); - outb(HPBLKSEL_1, dev.io + HP_BLKS); - isa_memset_io(dev.base, 0, 0x8000); - outb(HPBLKSEL_0, dev.io + HP_BLKS); - spin_unlock_irqrestore(&dev.lock, flags); - - dev.pwDSPQData = (dev.base + DSPQ_DATA_BUFF); - dev.pwMODQData = (dev.base + MODQ_DATA_BUFF); - dev.pwMIDQData = (dev.base + MIDQ_DATA_BUFF); - - /* Motorola 56k shared memory base */ - dev.SMA = dev.base + SMA_STRUCT_START; - - /* Digital audio play queue */ - dev.DAPQ = dev.base + DAPQ_OFFSET; - msnd_init_queue(dev.DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE); - - /* Digital audio record queue */ - dev.DARQ = dev.base + DARQ_OFFSET; - msnd_init_queue(dev.DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); - - /* MIDI out queue */ - dev.MODQ = dev.base + MODQ_OFFSET; - msnd_init_queue(dev.MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE); - - /* MIDI in queue */ - dev.MIDQ = dev.base + MIDQ_OFFSET; - msnd_init_queue(dev.MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE); - - /* DSP -> host message queue */ - dev.DSPQ = dev.base + DSPQ_OFFSET; - msnd_init_queue(dev.DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE); - - /* Setup some DSP values */ -#ifndef MSND_CLASSIC - isa_writew(1, dev.SMA + SMA_wCurrPlayFormat); - isa_writew(dev.play_sample_size, dev.SMA + SMA_wCurrPlaySampleSize); - isa_writew(dev.play_channels, dev.SMA + SMA_wCurrPlayChannels); - isa_writew(dev.play_sample_rate, dev.SMA + SMA_wCurrPlaySampleRate); -#endif - isa_writew(dev.play_sample_rate, dev.SMA + SMA_wCalFreqAtoD); - isa_writew(mastVolLeft, dev.SMA + SMA_wCurrMastVolLeft); - isa_writew(mastVolRight, dev.SMA + SMA_wCurrMastVolRight); -#ifndef MSND_CLASSIC - isa_writel(0x00010000, dev.SMA + SMA_dwCurrPlayPitch); - isa_writel(0x00000001, dev.SMA + SMA_dwCurrPlayRate); -#endif - isa_writew(0x303, dev.SMA + SMA_wCurrInputTagBits); - - initted = 1; - - return 0; -} - -static int __init calibrate_adc(WORD srate) -{ - isa_writew(srate, dev.SMA + SMA_wCalFreqAtoD); - if (dev.calibrate_signal == 0) - isa_writew(isa_readw(dev.SMA + SMA_wCurrHostStatusFlags) - | 0x0001, dev.SMA + SMA_wCurrHostStatusFlags); - else - isa_writew(isa_readw(dev.SMA + SMA_wCurrHostStatusFlags) - & ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags); - if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 && - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ / 3); - return 0; - } - printk(KERN_WARNING LOGNAME ": ADC calibration failed\n"); - - return -EIO; -} - -static int upload_dsp_code(void) -{ - outb(HPBLKSEL_0, dev.io + HP_BLKS); -#ifndef HAVE_DSPCODEH - INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE); - if (!INITCODE) { - printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE); - return -EBUSY; - } - - PERMCODESIZE = mod_firmware_load(PERMCODEFILE, &PERMCODE); - if (!PERMCODE) { - printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE); - vfree(INITCODE); - return -EBUSY; - } -#endif - isa_memcpy_toio(dev.base, PERMCODE, PERMCODESIZE); - if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) { - printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n"); - return -ENODEV; - } -#ifdef HAVE_DSPCODEH - printk(KERN_INFO LOGNAME ": DSP firmware uploaded (resident)\n"); -#else - printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n"); -#endif - -#ifndef HAVE_DSPCODEH - vfree(INITCODE); - vfree(PERMCODE); -#endif - - return 0; -} - -#ifdef MSND_CLASSIC -static void reset_proteus(void) -{ - outb(HPPRORESET_ON, dev.io + HP_PROR); - mdelay(TIME_PRO_RESET); - outb(HPPRORESET_OFF, dev.io + HP_PROR); - mdelay(TIME_PRO_RESET_DONE); -} -#endif - -static int initialize(void) -{ - int err, timeout; - -#ifdef MSND_CLASSIC - outb(HPWAITSTATE_0, dev.io + HP_WAIT); - outb(HPBITMODE_16, dev.io + HP_BITM); - - reset_proteus(); -#endif - if ((err = init_sma()) < 0) { - printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n"); - return err; - } - - if ((err = reset_dsp()) < 0) - return err; - - if ((err = upload_dsp_code()) < 0) { - printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n"); - return err; - } - - timeout = 200; - while (isa_readw(dev.base)) { - mdelay(1); - if (!timeout--) { - printk(KERN_DEBUG LOGNAME ": DSP reset timeout\n"); - return -EIO; - } - } - - mixer_setup(); - - return 0; -} - -static int dsp_full_reset(void) -{ - int rv; - - if (test_bit(F_RESETTING, &dev.flags) || ++dev.nresets > 10) - return 0; - - set_bit(F_RESETTING, &dev.flags); - printk(KERN_INFO LOGNAME ": DSP reset\n"); - dsp_halt(NULL); /* Unconditionally halt */ - if ((rv = initialize())) - printk(KERN_WARNING LOGNAME ": DSP reset failed\n"); - force_recsrc(dev.recsrc); - dsp_open(NULL); - clear_bit(F_RESETTING, &dev.flags); - - return rv; -} - -static int __init attach_multisound(void) -{ - int err; - - if ((err = request_irq(dev.irq, intr, 0, dev.name, &dev)) < 0) { - printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq); - return err; - } - request_region(dev.io, dev.numio, dev.name); - - if ((err = dsp_full_reset()) < 0) { - release_region(dev.io, dev.numio); - free_irq(dev.irq, &dev); - return err; - } - - if ((err = msnd_register(&dev)) < 0) { - printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n"); - release_region(dev.io, dev.numio); - free_irq(dev.irq, &dev); - return err; - } - - if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) { - printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n"); - msnd_unregister(&dev); - release_region(dev.io, dev.numio); - free_irq(dev.irq, &dev); - return dev.dsp_minor; - } - - if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) { - printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n"); - unregister_sound_mixer(dev.mixer_minor); - msnd_unregister(&dev); - release_region(dev.io, dev.numio); - free_irq(dev.irq, &dev); - return dev.mixer_minor; - } - - dev.ext_midi_dev = dev.hdr_midi_dev = -1; - - disable_irq(dev.irq); - calibrate_adc(dev.play_sample_rate); -#ifndef MSND_CLASSIC - force_recsrc(SOUND_MASK_IMIX); -#endif - - return 0; -} - -static void __exit unload_multisound(void) -{ - release_region(dev.io, dev.numio); - free_irq(dev.irq, &dev); - unregister_sound_mixer(dev.mixer_minor); - unregister_sound_dsp(dev.dsp_minor); - msnd_unregister(&dev); -} - -#ifndef MSND_CLASSIC - -/* Pinnacle/Fiji Logical Device Configuration */ - -static int __init msnd_write_cfg(int cfg, int reg, int value) -{ - outb(reg, cfg); - outb(value, cfg + 1); - if (value != inb(cfg + 1)) { - printk(KERN_ERR LOGNAME ": msnd_write_cfg: I/O error\n"); - return -EIO; - } - return 0; -} - -static int __init msnd_write_cfg_io0(int cfg, int num, WORD io) -{ - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io))) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io))) - return -EIO; - return 0; -} - -static int __init msnd_write_cfg_io1(int cfg, int num, WORD io) -{ - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io))) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io))) - return -EIO; - return 0; -} - -static int __init msnd_write_cfg_irq(int cfg, int num, WORD irq) -{ - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq))) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE)) - return -EIO; - return 0; -} - -static int __init msnd_write_cfg_mem(int cfg, int num, int mem) -{ - WORD wmem; - - mem >>= 8; - mem &= 0xfff; - wmem = (WORD)mem; - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem))) - return -EIO; - if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem))) - return -EIO; - if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT))) - return -EIO; - return 0; -} - -static int __init msnd_activate_logical(int cfg, int num) -{ - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE)) - return -EIO; - return 0; -} - -static int __init msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem) -{ - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg_io0(cfg, num, io0)) - return -EIO; - if (msnd_write_cfg_io1(cfg, num, io1)) - return -EIO; - if (msnd_write_cfg_irq(cfg, num, irq)) - return -EIO; - if (msnd_write_cfg_mem(cfg, num, mem)) - return -EIO; - if (msnd_activate_logical(cfg, num)) - return -EIO; - return 0; -} - -typedef struct msnd_pinnacle_cfg_device { - WORD io0, io1, irq; - int mem; -} msnd_pinnacle_cfg_t[4]; - -static int __init msnd_pinnacle_cfg_devices(int cfg, int reset, msnd_pinnacle_cfg_t device) -{ - int i; - - /* Reset devices if told to */ - if (reset) { - printk(KERN_INFO LOGNAME ": Resetting all devices\n"); - for (i = 0; i < 4; ++i) - if (msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0)) - return -EIO; - } - - /* Configure specified devices */ - for (i = 0; i < 4; ++i) { - - switch (i) { - case 0: /* DSP */ - if (!(device[i].io0 && device[i].irq && device[i].mem)) - continue; - break; - case 1: /* MPU */ - if (!(device[i].io0 && device[i].irq)) - continue; - printk(KERN_INFO LOGNAME - ": Configuring MPU to I/O 0x%x IRQ %d\n", - device[i].io0, device[i].irq); - break; - case 2: /* IDE */ - if (!(device[i].io0 && device[i].io1 && device[i].irq)) - continue; - printk(KERN_INFO LOGNAME - ": Configuring IDE to I/O 0x%x, 0x%x IRQ %d\n", - device[i].io0, device[i].io1, device[i].irq); - break; - case 3: /* Joystick */ - if (!(device[i].io0)) - continue; - printk(KERN_INFO LOGNAME - ": Configuring joystick to I/O 0x%x\n", - device[i].io0); - break; - } - - /* Configure the device */ - if (msnd_write_cfg_logical(cfg, i, device[i].io0, device[i].io1, device[i].irq, device[i].mem)) - return -EIO; - } - - return 0; -} -#endif - -#ifdef MODULE -MODULE_AUTHOR ("Andrew Veliath "); -MODULE_DESCRIPTION ("Turtle Beach " LONGNAME " Linux Driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM (io, "i"); -MODULE_PARM (irq, "i"); -MODULE_PARM (mem, "i"); -MODULE_PARM (write_ndelay, "i"); -MODULE_PARM (fifosize, "i"); -MODULE_PARM (calibrate_signal, "i"); -#ifndef MSND_CLASSIC -MODULE_PARM (digital, "i"); -MODULE_PARM (cfg, "i"); -MODULE_PARM (reset, "i"); -MODULE_PARM (mpu_io, "i"); -MODULE_PARM (mpu_irq, "i"); -MODULE_PARM (ide_io0, "i"); -MODULE_PARM (ide_io1, "i"); -MODULE_PARM (ide_irq, "i"); -MODULE_PARM (joystick_io, "i"); -#endif - -static int io __initdata = -1; -static int irq __initdata = -1; -static int mem __initdata = -1; -static int write_ndelay __initdata = -1; - -#ifndef MSND_CLASSIC -/* Pinnacle/Fiji non-PnP Config Port */ -static int cfg __initdata = -1; - -/* Extra Peripheral Configuration */ -static int reset __initdata = 0; -static int mpu_io __initdata = 0; -static int mpu_irq __initdata = 0; -static int ide_io0 __initdata = 0; -static int ide_io1 __initdata = 0; -static int ide_irq __initdata = 0; -static int joystick_io __initdata = 0; - -/* If we have the digital daugherboard... */ -static int digital __initdata = 0; -#endif - -static int fifosize __initdata = DEFFIFOSIZE; -static int calibrate_signal __initdata = 0; - -#else /* not a module */ - -static int write_ndelay __initdata = -1; - -#ifdef MSND_CLASSIC -static int io __initdata = CONFIG_MSNDCLAS_IO; -static int irq __initdata = CONFIG_MSNDCLAS_IRQ; -static int mem __initdata = CONFIG_MSNDCLAS_MEM; -#else /* Pinnacle/Fiji */ - -static int io __initdata = CONFIG_MSNDPIN_IO; -static int irq __initdata = CONFIG_MSNDPIN_IRQ; -static int mem __initdata = CONFIG_MSNDPIN_MEM; - -/* Pinnacle/Fiji non-PnP Config Port */ -#ifdef CONFIG_MSNDPIN_NONPNP -# ifndef CONFIG_MSNDPIN_CFG -# define CONFIG_MSNDPIN_CFG 0x250 -# endif -#else -# ifdef CONFIG_MSNDPIN_CFG -# undef CONFIG_MSNDPIN_CFG -# endif -# define CONFIG_MSNDPIN_CFG -1 -#endif -static int cfg __initdata = CONFIG_MSNDPIN_CFG; -/* If not a module, we don't need to bother with reset=1 */ -static int reset; - -/* Extra Peripheral Configuration (Default: Disable) */ -#ifndef CONFIG_MSNDPIN_MPU_IO -# define CONFIG_MSNDPIN_MPU_IO 0 -#endif -static int mpu_io __initdata = CONFIG_MSNDPIN_MPU_IO; - -#ifndef CONFIG_MSNDPIN_MPU_IRQ -# define CONFIG_MSNDPIN_MPU_IRQ 0 -#endif -static int mpu_irq __initdata = CONFIG_MSNDPIN_MPU_IRQ; - -#ifndef CONFIG_MSNDPIN_IDE_IO0 -# define CONFIG_MSNDPIN_IDE_IO0 0 -#endif -static int ide_io0 __initdata = CONFIG_MSNDPIN_IDE_IO0; - -#ifndef CONFIG_MSNDPIN_IDE_IO1 -# define CONFIG_MSNDPIN_IDE_IO1 0 -#endif -static int ide_io1 __initdata = CONFIG_MSNDPIN_IDE_IO1; - -#ifndef CONFIG_MSNDPIN_IDE_IRQ -# define CONFIG_MSNDPIN_IDE_IRQ 0 -#endif -static int ide_irq __initdata = CONFIG_MSNDPIN_IDE_IRQ; - -#ifndef CONFIG_MSNDPIN_JOYSTICK_IO -# define CONFIG_MSNDPIN_JOYSTICK_IO 0 -#endif -static int joystick_io __initdata = CONFIG_MSNDPIN_JOYSTICK_IO; - -/* Have SPDIF (Digital) Daughterboard */ -#ifndef CONFIG_MSNDPIN_DIGITAL -# define CONFIG_MSNDPIN_DIGITAL 0 -#endif -static int digital __initdata = CONFIG_MSNDPIN_DIGITAL; - -#endif /* MSND_CLASSIC */ - -#ifndef CONFIG_MSND_FIFOSIZE -# define CONFIG_MSND_FIFOSIZE DEFFIFOSIZE -#endif -static int fifosize __initdata = CONFIG_MSND_FIFOSIZE; - -#ifndef CONFIG_MSND_CALSIGNAL -# define CONFIG_MSND_CALSIGNAL 0 -#endif -static int -calibrate_signal __initdata = CONFIG_MSND_CALSIGNAL; -#endif /* MODULE */ - - -static int __init msnd_init(void) -{ - int err; -#ifndef MSND_CLASSIC - static msnd_pinnacle_cfg_t pinnacle_devs; -#endif /* MSND_CLASSIC */ - - printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version " - VERSION ", Copyright (C) 1998 Andrew Veliath\n"); - - if (io == -1 || irq == -1 || mem == -1) - printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n"); - -#ifdef MSND_CLASSIC - if (io == -1 || - !(io == 0x290 || - io == 0x260 || - io == 0x250 || - io == 0x240 || - io == 0x230 || - io == 0x220 || - io == 0x210 || - io == 0x3e0)) { - printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, or 0x3E0\n"); - return -EINVAL; - } -#else - if (io == -1 || - io < 0x100 || - io > 0x3e0 || - (io % 0x10) != 0) { - printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must within the range 0x100 to 0x3E0 and must be evenly divisible by 0x10\n"); - return -EINVAL; - } -#endif /* MSND_CLASSIC */ - - if (irq == -1 || - !(irq == 5 || - irq == 7 || - irq == 9 || - irq == 10 || - irq == 11 || - irq == 12)) { - printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n"); - return -EINVAL; - } - - if (mem == -1 || - !(mem == 0xb0000 || - mem == 0xc8000 || - mem == 0xd0000 || - mem == 0xd8000 || - mem == 0xe0000 || - mem == 0xe8000)) { - printk(KERN_ERR LOGNAME ": \"mem\" - must be set to " - "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); - return -EINVAL; - } - -#ifdef MSND_CLASSIC - switch (irq) { - case 5: dev.irqid = HPIRQ_5; break; - case 7: dev.irqid = HPIRQ_7; break; - case 9: dev.irqid = HPIRQ_9; break; - case 10: dev.irqid = HPIRQ_10; break; - case 11: dev.irqid = HPIRQ_11; break; - case 12: dev.irqid = HPIRQ_12; break; - } - - switch (mem) { - case 0xb0000: dev.memid = HPMEM_B000; break; - case 0xc8000: dev.memid = HPMEM_C800; break; - case 0xd0000: dev.memid = HPMEM_D000; break; - case 0xd8000: dev.memid = HPMEM_D800; break; - case 0xe0000: dev.memid = HPMEM_E000; break; - case 0xe8000: dev.memid = HPMEM_E800; break; - } -#else - if (cfg == -1) { - printk(KERN_INFO LOGNAME ": Assuming PnP mode\n"); - } else if (cfg != 0x250 && cfg != 0x260 && cfg != 0x270) { - printk(KERN_INFO LOGNAME ": Config port must be 0x250, 0x260 or 0x270 (or unspecified for PnP mode)\n"); - return -EINVAL; - } else { - printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%x\n", cfg); - - /* DSP */ - pinnacle_devs[0].io0 = io; - pinnacle_devs[0].irq = irq; - pinnacle_devs[0].mem = mem; - - /* The following are Pinnacle specific */ - - /* MPU */ - pinnacle_devs[1].io0 = mpu_io; - pinnacle_devs[1].irq = mpu_irq; - - /* IDE */ - pinnacle_devs[2].io0 = ide_io0; - pinnacle_devs[2].io1 = ide_io1; - pinnacle_devs[2].irq = ide_irq; - - /* Joystick */ - pinnacle_devs[3].io0 = joystick_io; - - if (check_region(cfg, 2)) { - printk(KERN_ERR LOGNAME ": Config port 0x%x conflict\n", cfg); - return -EIO; - } - - request_region(cfg, 2, "Pinnacle/Fiji Config"); - if (msnd_pinnacle_cfg_devices(cfg, reset, pinnacle_devs)) { - printk(KERN_ERR LOGNAME ": Device configuration error\n"); - release_region(cfg, 2); - return -EIO; - } - release_region(cfg, 2); - } -#endif /* MSND_CLASSIC */ - - if (fifosize < 16) - fifosize = 16; - - if (fifosize > 1024) - fifosize = 1024; - - set_default_audio_parameters(); -#ifdef MSND_CLASSIC - dev.type = msndClassic; -#else - dev.type = msndPinnacle; -#endif - dev.io = io; - dev.numio = DSP_NUMIO; - dev.irq = irq; - dev.base = mem; - dev.fifosize = fifosize * 1024; - dev.calibrate_signal = calibrate_signal ? 1 : 0; - dev.recsrc = 0; - dev.dspq_data_buff = DSPQ_DATA_BUFF; - dev.dspq_buff_size = DSPQ_BUFF_SIZE; - if (write_ndelay == -1) - write_ndelay = CONFIG_MSND_WRITE_NDELAY; - if (write_ndelay) - clear_bit(F_DISABLE_WRITE_NDELAY, &dev.flags); - else - set_bit(F_DISABLE_WRITE_NDELAY, &dev.flags); -#ifndef MSND_CLASSIC - if (digital) - set_bit(F_HAVEDIGITAL, &dev.flags); -#endif - init_waitqueue_head(&dev.writeblock); - init_waitqueue_head(&dev.readblock); - init_waitqueue_head(&dev.writeflush); - msnd_fifo_init(&dev.DAPF); - msnd_fifo_init(&dev.DARF); - spin_lock_init(&dev.lock); - printk(KERN_INFO LOGNAME ": %u byte audio FIFOs (x2)\n", dev.fifosize); - if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) { - printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n"); - return err; - } - - if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) { - printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n"); - msnd_fifo_free(&dev.DAPF); - return err; - } - - if ((err = probe_multisound()) < 0) { - printk(KERN_ERR LOGNAME ": Probe failed\n"); - msnd_fifo_free(&dev.DAPF); - msnd_fifo_free(&dev.DARF); - return err; - } - - if ((err = attach_multisound()) < 0) { - printk(KERN_ERR LOGNAME ": Attach failed\n"); - msnd_fifo_free(&dev.DAPF); - msnd_fifo_free(&dev.DARF); - return err; - } - - return 0; -} - -static void __exit msdn_cleanup(void) -{ - unload_multisound(); - msnd_fifo_free(&dev.DAPF); - msnd_fifo_free(&dev.DARF); -} - -module_init(msnd_init); -module_exit(msdn_cleanup); diff -Nru a/drivers/sound/msnd_pinnacle.h b/drivers/sound/msnd_pinnacle.h --- a/drivers/sound/msnd_pinnacle.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,249 +0,0 @@ -/********************************************************************* - * - * msnd_pinnacle.h - * - * Turtle Beach MultiSound Sound Card Driver for Linux - * - * Some parts of this header file were derived from the Turtle Beach - * MultiSound Driver Development Kit. - * - * Copyright (C) 1998 Andrew Veliath - * Copyright (C) 1993 Turtle Beach Systems, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * $Id: msnd_pinnacle.h,v 1.11 1999/03/21 17:36:09 andrewtv Exp $ - * - ********************************************************************/ -#ifndef __MSND_PINNACLE_H -#define __MSND_PINNACLE_H - -#include - -#define DSP_NUMIO 0x08 - -#define IREG_LOGDEVICE 0x07 -#define IREG_ACTIVATE 0x30 -#define LD_ACTIVATE 0x01 -#define LD_DISACTIVATE 0x00 -#define IREG_EECONTROL 0x3F -#define IREG_MEMBASEHI 0x40 -#define IREG_MEMBASELO 0x41 -#define IREG_MEMCONTROL 0x42 -#define IREG_MEMRANGEHI 0x43 -#define IREG_MEMRANGELO 0x44 -#define MEMTYPE_8BIT 0x00 -#define MEMTYPE_16BIT 0x02 -#define MEMTYPE_RANGE 0x00 -#define MEMTYPE_HIADDR 0x01 -#define IREG_IO0_BASEHI 0x60 -#define IREG_IO0_BASELO 0x61 -#define IREG_IO1_BASEHI 0x62 -#define IREG_IO1_BASELO 0x63 -#define IREG_IRQ_NUMBER 0x70 -#define IREG_IRQ_TYPE 0x71 -#define IRQTYPE_HIGH 0x02 -#define IRQTYPE_LOW 0x00 -#define IRQTYPE_LEVEL 0x01 -#define IRQTYPE_EDGE 0x00 - -#define HP_DSPR 0x04 -#define HP_BLKS 0x04 - -#define HPDSPRESET_OFF 2 -#define HPDSPRESET_ON 0 - -#define HPBLKSEL_0 2 -#define HPBLKSEL_1 3 - -#define HIMT_DAT_OFF 0x03 - -#define HIDSP_PLAY_UNDER 0x00 -#define HIDSP_INT_PLAY_UNDER 0x01 -#define HIDSP_SSI_TX_UNDER 0x02 -#define HIDSP_RECQ_OVERFLOW 0x08 -#define HIDSP_INT_RECORD_OVER 0x09 -#define HIDSP_SSI_RX_OVERFLOW 0x0a - -#define HIDSP_MIDI_IN_OVER 0x10 - -#define HIDSP_MIDI_FRAME_ERR 0x11 -#define HIDSP_MIDI_PARITY_ERR 0x12 -#define HIDSP_MIDI_OVERRUN_ERR 0x13 - -#define HIDSP_INPUT_CLIPPING 0x20 -#define HIDSP_MIX_CLIPPING 0x30 -#define HIDSP_DAT_IN_OFF 0x21 - -#define HDEXAR_SET_ANA_IN 0 -#define HDEXAR_CLEAR_PEAKS 1 -#define HDEXAR_IN_SET_POTS 2 -#define HDEXAR_AUX_SET_POTS 3 -#define HDEXAR_CAL_A_TO_D 4 -#define HDEXAR_RD_EXT_DSP_BITS 5 - -#define HDEXAR_SET_SYNTH_IN 4 -#define HDEXAR_READ_DAT_IN 5 -#define HDEXAR_MIC_SET_POTS 6 -#define HDEXAR_SET_DAT_IN 7 - -#define HDEXAR_SET_SYNTH_48 8 -#define HDEXAR_SET_SYNTH_44 9 - -#define TIME_PRO_RESET_DONE 0x028A -#define TIME_PRO_SYSEX 0x001E -#define TIME_PRO_RESET 0x0032 - -#define AGND 0x01 -#define SIGNAL 0x02 - -#define EXT_DSP_BIT_DCAL 0x0001 -#define EXT_DSP_BIT_MIDI_CON 0x0002 - -#define BUFFSIZE 0x8000 -#define HOSTQ_SIZE 0x40 - -#define SRAM_CNTL_START 0x7F00 -#define SMA_STRUCT_START 0x7F40 - -#define DAP_BUFF_SIZE 0x2400 -#define DAR_BUFF_SIZE 0x2000 - -#define DAPQ_STRUCT_SIZE 0x10 -#define DARQ_STRUCT_SIZE 0x10 -#define DAPQ_BUFF_SIZE (3 * 0x10) -#define DARQ_BUFF_SIZE (3 * 0x10) -#define MODQ_BUFF_SIZE 0x400 -#define MIDQ_BUFF_SIZE 0x800 -#define DSPQ_BUFF_SIZE 0x5A0 - -#define DAPQ_DATA_BUFF 0x6C00 -#define DARQ_DATA_BUFF 0x6C30 -#define MODQ_DATA_BUFF 0x6C60 -#define MIDQ_DATA_BUFF 0x7060 -#define DSPQ_DATA_BUFF 0x7860 - -#define DAPQ_OFFSET SRAM_CNTL_START -#define DARQ_OFFSET (SRAM_CNTL_START + 0x08) -#define MODQ_OFFSET (SRAM_CNTL_START + 0x10) -#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18) -#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20) - -#define MOP_WAVEHDR 0 -#define MOP_EXTOUT 1 -#define MOP_HWINIT 0xfe -#define MOP_NONE 0xff -#define MOP_MAX 1 - -#define MIP_EXTIN 0 -#define MIP_WAVEHDR 1 -#define MIP_HWINIT 0xfe -#define MIP_MAX 1 - -/* Pinnacle/Fiji SMA Common Data */ -#define SMA_wCurrPlayBytes 0x0000 -#define SMA_wCurrRecordBytes 0x0002 -#define SMA_wCurrPlayVolLeft 0x0004 -#define SMA_wCurrPlayVolRight 0x0006 -#define SMA_wCurrInVolLeft 0x0008 -#define SMA_wCurrInVolRight 0x000a -#define SMA_wCurrMHdrVolLeft 0x000c -#define SMA_wCurrMHdrVolRight 0x000e -#define SMA_dwCurrPlayPitch 0x0010 -#define SMA_dwCurrPlayRate 0x0014 -#define SMA_wCurrMIDIIOPatch 0x0018 -#define SMA_wCurrPlayFormat 0x001a -#define SMA_wCurrPlaySampleSize 0x001c -#define SMA_wCurrPlayChannels 0x001e -#define SMA_wCurrPlaySampleRate 0x0020 -#define SMA_wCurrRecordFormat 0x0022 -#define SMA_wCurrRecordSampleSize 0x0024 -#define SMA_wCurrRecordChannels 0x0026 -#define SMA_wCurrRecordSampleRate 0x0028 -#define SMA_wCurrDSPStatusFlags 0x002a -#define SMA_wCurrHostStatusFlags 0x002c -#define SMA_wCurrInputTagBits 0x002e -#define SMA_wCurrLeftPeak 0x0030 -#define SMA_wCurrRightPeak 0x0032 -#define SMA_bMicPotPosLeft 0x0034 -#define SMA_bMicPotPosRight 0x0035 -#define SMA_bMicPotMaxLeft 0x0036 -#define SMA_bMicPotMaxRight 0x0037 -#define SMA_bInPotPosLeft 0x0038 -#define SMA_bInPotPosRight 0x0039 -#define SMA_bAuxPotPosLeft 0x003a -#define SMA_bAuxPotPosRight 0x003b -#define SMA_bInPotMaxLeft 0x003c -#define SMA_bInPotMaxRight 0x003d -#define SMA_bAuxPotMaxLeft 0x003e -#define SMA_bAuxPotMaxRight 0x003f -#define SMA_bInPotMaxMethod 0x0040 -#define SMA_bAuxPotMaxMethod 0x0041 -#define SMA_wCurrMastVolLeft 0x0042 -#define SMA_wCurrMastVolRight 0x0044 -#define SMA_wCalFreqAtoD 0x0046 -#define SMA_wCurrAuxVolLeft 0x0048 -#define SMA_wCurrAuxVolRight 0x004a -#define SMA_wCurrPlay1VolLeft 0x004c -#define SMA_wCurrPlay1VolRight 0x004e -#define SMA_wCurrPlay2VolLeft 0x0050 -#define SMA_wCurrPlay2VolRight 0x0052 -#define SMA_wCurrPlay3VolLeft 0x0054 -#define SMA_wCurrPlay3VolRight 0x0056 -#define SMA_wCurrPlay4VolLeft 0x0058 -#define SMA_wCurrPlay4VolRight 0x005a -#define SMA_wCurrPlay1PeakLeft 0x005c -#define SMA_wCurrPlay1PeakRight 0x005e -#define SMA_wCurrPlay2PeakLeft 0x0060 -#define SMA_wCurrPlay2PeakRight 0x0062 -#define SMA_wCurrPlay3PeakLeft 0x0064 -#define SMA_wCurrPlay3PeakRight 0x0066 -#define SMA_wCurrPlay4PeakLeft 0x0068 -#define SMA_wCurrPlay4PeakRight 0x006a -#define SMA_wCurrPlayPeakLeft 0x006c -#define SMA_wCurrPlayPeakRight 0x006e -#define SMA_wCurrDATSR 0x0070 -#define SMA_wCurrDATRXCHNL 0x0072 -#define SMA_wCurrDATTXCHNL 0x0074 -#define SMA_wCurrDATRXRate 0x0076 -#define SMA_dwDSPPlayCount 0x0078 -#define SMA__size 0x007c - -#ifdef HAVE_DSPCODEH -# include "pndsperm.c" -# include "pndspini.c" -# define PERMCODE pndsperm -# define INITCODE pndspini -# define PERMCODESIZE sizeof(pndsperm) -# define INITCODESIZE sizeof(pndspini) -#else -# ifndef CONFIG_MSNDPIN_INIT_FILE -# define CONFIG_MSNDPIN_INIT_FILE \ - "/etc/sound/pndspini.bin" -# endif -# ifndef CONFIG_MSNDPIN_PERM_FILE -# define CONFIG_MSNDPIN_PERM_FILE \ - "/etc/sound/pndsperm.bin" -# endif -# define PERMCODEFILE CONFIG_MSNDPIN_PERM_FILE -# define INITCODEFILE CONFIG_MSNDPIN_INIT_FILE -# define PERMCODE dspini -# define INITCODE permini -# define PERMCODESIZE sizeof_dspini -# define INITCODESIZE sizeof_permini -#endif -#define LONGNAME "MultiSound (Pinnacle/Fiji)" - -#endif /* __MSND_PINNACLE_H */ diff -Nru a/drivers/sound/nec_vrc5477.c b/drivers/sound/nec_vrc5477.c --- a/drivers/sound/nec_vrc5477.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2039 +0,0 @@ -/*********************************************************************** - * Copyright 2001 MontaVista Software Inc. - * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net - * - * drivers/sound/nec_vrc5477.c - * AC97 sound dirver for NEC Vrc5477 chip (an integrated, - * multi-function controller chip for MIPS CPUs) - * - * 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 code is derived from ite8172.c, which is written by Steve Longerbeam. - * - * Features: - * Currently we only support the following capabilities: - * . mono output to PCM L/R (line out). - * . stereo output to PCM L/R (line out). - * . mono input from PCM L (line in). - * . stereo output from PCM (line in). - * . sampling rate at 48k or variable sampling rate - * . support /dev/dsp, /dev/mixer devices, standard OSS devices. - * . only support 16-bit PCM format (hardware limit, no software - * translation) - * . support duplex, but no trigger or realtime. - * - * Specifically the following are not supported: - * . app-set frag size. - * . mmap'ed buffer access - */ - -/* - * Original comments from ite8172.c file. - */ - -/* - * - * 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. - * 2. The following support is untested: - * * Memory mapping the audio buffers, and the ioctl controls that go - * with it. - * * S/PDIF output. - * 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 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#undef VRC5477_AC97_VERBOSE_DEBUG - -/* one must turn on CONFIG_LL_DEBUG before VERBOSE_DEBUG is turned */ -#if defined(VRC5477_AC97_VERBOSE_DEBUG) -#if !defined(CONFIG_LL_DEBUG) -#error "You must turn CONFIG_LL_DEBUG" -#endif -#endif - -#if defined(VRC5477_AC97_VERBOSE_DEBUG) -static u16 inTicket=0; /* check sync between intr & write */ -static u16 outTicket=0; -#endif - -/* --------------------------------------------------------------------- */ - -#undef OSS_DOCUMENTED_MIXER_SEMANTICS - -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -#define VRC5477_INT_CLR 0x0 -#define VRC5477_INT_STATUS 0x0 -#define VRC5477_CODEC_WR 0x4 -#define VRC5477_CODEC_RD 0x8 -#define VRC5477_CTRL 0x18 -#define VRC5477_ACLINK_CTRL 0x1c -#define VRC5477_INT_MASK 0x24 - -#define VRC5477_DAC1_CTRL 0x30 -#define VRC5477_DAC1L 0x34 -#define VRC5477_DAC1_BADDR 0x38 -#define VRC5477_DAC2_CTRL 0x3c -#define VRC5477_DAC2L 0x40 -#define VRC5477_DAC2_BADDR 0x44 -#define VRC5477_DAC3_CTRL 0x48 -#define VRC5477_DAC3L 0x4c -#define VRC5477_DAC3_BADDR 0x50 - -#define VRC5477_ADC1_CTRL 0x54 -#define VRC5477_ADC1L 0x58 -#define VRC5477_ADC1_BADDR 0x5c -#define VRC5477_ADC2_CTRL 0x60 -#define VRC5477_ADC2L 0x64 -#define VRC5477_ADC2_BADDR 0x68 -#define VRC5477_ADC3_CTRL 0x6c -#define VRC5477_ADC3L 0x70 -#define VRC5477_ADC3_BADDR 0x74 - -#define VRC5477_CODEC_WR_RWC (1 << 23) - -#define VRC5477_CODEC_RD_RRDYA (1 << 31) -#define VRC5477_CODEC_RD_RRDYD (1 << 30) - -#define VRC5477_ACLINK_CTRL_RST_ON (1 << 15) -#define VRC5477_ACLINK_CTRL_RST_TIME 0x7f -#define VRC5477_ACLINK_CTRL_SYNC_ON (1 << 30) -#define VRC5477_ACLINK_CTRL_CK_STOP_ON (1 << 31) - -#define VRC5477_CTRL_DAC2ENB (1 << 15) -#define VRC5477_CTRL_ADC2ENB (1 << 14) -#define VRC5477_CTRL_DAC1ENB (1 << 13) -#define VRC5477_CTRL_ADC1ENB (1 << 12) - -#define VRC5477_INT_MASK_NMASK (1 << 31) -#define VRC5477_INT_MASK_DAC1END (1 << 5) -#define VRC5477_INT_MASK_DAC2END (1 << 4) -#define VRC5477_INT_MASK_DAC3END (1 << 3) -#define VRC5477_INT_MASK_ADC1END (1 << 2) -#define VRC5477_INT_MASK_ADC2END (1 << 1) -#define VRC5477_INT_MASK_ADC3END (1 << 0) - -#define VRC5477_DMA_ACTIVATION (1 << 31) -#define VRC5477_DMA_WIP (1 << 30) - - -#define VRC5477_AC97_MODULE_NAME "NEC_Vrc5477_audio" -#define PFX VRC5477_AC97_MODULE_NAME ": " - -/* --------------------------------------------------------------------- */ - -struct vrc5477_ac97_state { - /* list of vrc5477_ac97 devices */ - struct list_head devs; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; - - /* soundcore stuff */ - int dev_audio; - - /* hardware resources */ - unsigned long io; - unsigned int irq; - -#ifdef CONFIG_LL_DEBUG - /* debug /proc entry */ - struct proc_dir_entry *ps; - struct proc_dir_entry *ac97_ps; -#endif /* CONFIG_LL_DEBUG */ - - struct ac97_codec codec; - - unsigned dacChannels, adcChannels; - unsigned short dacRate, adcRate; - - spinlock_t lock; - struct semaphore open_sem; - mode_t open_mode; - wait_queue_head_t open_wait; - - struct dmabuf { - void *lbuf, *rbuf; - dma_addr_t lbufDma, rbufDma; - unsigned bufOrder; - unsigned numFrag; - unsigned fragShift; - unsigned fragSize; /* redundant */ - unsigned fragTotalSize; /* = numFrag * fragSize(real) */ - unsigned nextIn; - unsigned nextOut; - int count; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; - /* OSS stuff */ - unsigned stopped:1; - unsigned ready:1; - } dma_dac, dma_adc; - - #define WORK_BUF_SIZE 2048 - struct { - u16 lchannel; - u16 rchannel; - } workBuf[WORK_BUF_SIZE/4]; -}; - -/* --------------------------------------------------------------------- */ - -static LIST_HEAD(devs); - -/* --------------------------------------------------------------------- */ - -extern 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; -} - -/* --------------------------------------------------------------------- */ - -static u16 rdcodec(struct ac97_codec *codec, u8 addr) -{ - struct vrc5477_ac97_state *s = - (struct vrc5477_ac97_state *)codec->private_data; - unsigned long flags; - u32 result; - - spin_lock_irqsave(&s->lock, flags); - - /* wait until we can access codec registers */ - while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); - - /* write the address and "read" command to codec */ - addr = addr & 0x7f; - outl((addr << 16) | VRC5477_CODEC_WR_RWC, s->io + VRC5477_CODEC_WR); - - /* get the return result */ - udelay(100); /* workaround hardware bug */ - while ( (result = inl(s->io + VRC5477_CODEC_RD)) & - (VRC5477_CODEC_RD_RRDYA | VRC5477_CODEC_RD_RRDYD) ) { - /* we get either addr or data, or both */ - if (result & VRC5477_CODEC_RD_RRDYA) { - MIPS_ASSERT(addr == ((result >> 16) & 0x7f) ); - } - if (result & VRC5477_CODEC_RD_RRDYD) { - break; - } - } - - spin_unlock_irqrestore(&s->lock, flags); - - return result & 0xffff;; -} - - -static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) -{ - struct vrc5477_ac97_state *s = - (struct vrc5477_ac97_state *)codec->private_data; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - - /* wait until we can access codec registers */ - while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); - - /* write the address and value to codec */ - outl((addr << 16) | data, s->io + VRC5477_CODEC_WR); - - spin_unlock_irqrestore(&s->lock, flags); -} - - -static void waitcodec(struct ac97_codec *codec) -{ - struct vrc5477_ac97_state *s = - (struct vrc5477_ac97_state *)codec->private_data; - - /* wait until we can access codec registers */ - while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); -} - - -/* --------------------------------------------------------------------- */ - -static void vrc5477_ac97_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 void set_adc_rate(struct vrc5477_ac97_state *s, unsigned rate) -{ - wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, rate); - s->adcRate = rate; -} - - -static void set_dac_rate(struct vrc5477_ac97_state *s, unsigned rate) -{ - wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, rate); - s->dacRate = rate; -} - - -/* --------------------------------------------------------------------- */ - -extern inline void -stop_dac(struct vrc5477_ac97_state *s) -{ - struct dmabuf* db = &s->dma_dac; - unsigned long flags; - u32 temp; - - spin_lock_irqsave(&s->lock, flags); - - if (db->stopped) { - spin_unlock_irqrestore(&s->lock, flags); - return; - } - - /* deactivate the dma */ - outl(0, s->io + VRC5477_DAC1_CTRL); - outl(0, s->io + VRC5477_DAC2_CTRL); - - /* wait for DAM completely stop */ - while (inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); - while (inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); - - /* disable dac slots in aclink */ - temp = inl(s->io + VRC5477_CTRL); - temp &= ~ (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB); - outl (temp, s->io + VRC5477_CTRL); - - /* disable interrupts */ - temp = inl(s->io + VRC5477_INT_MASK); - temp &= ~ (VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END); - outl (temp, s->io + VRC5477_INT_MASK); - - /* clear pending ones */ - outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, - s->io + VRC5477_INT_CLR); - - db->stopped = 1; - - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac(struct vrc5477_ac97_state *s) -{ - struct dmabuf* db = &s->dma_dac; - unsigned long flags; - u32 dmaLength; - u32 temp; - - spin_lock_irqsave(&s->lock, flags); - - if (!db->stopped) { - spin_unlock_irqrestore(&s->lock, flags); - return; - } - - /* we should have some data to do the DMA trasnfer */ - MIPS_ASSERT(db->count >= db->fragSize); - - /* clear pending fales interrupts */ - outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, - s->io + VRC5477_INT_CLR); - - /* enable interrupts */ - temp = inl(s->io + VRC5477_INT_MASK); - temp |= VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END; - outl(temp, s->io + VRC5477_INT_MASK); - - /* setup dma base addr */ - outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC1_BADDR); - if (s->dacChannels == 1) { - outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR); - } else { - outl(db->rbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR); - } - - /* set dma length, in the unit of 0x10 bytes */ - dmaLength = db->fragSize >> 4; - outl(dmaLength, s->io + VRC5477_DAC1L); - outl(dmaLength, s->io + VRC5477_DAC2L); - - /* activate dma */ - outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC1_CTRL); - outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC2_CTRL); - - /* enable dac slots - we should hear the music now! */ - temp = inl(s->io + VRC5477_CTRL); - temp |= (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB); - outl (temp, s->io + VRC5477_CTRL); - - /* it is time to setup next dma transfer */ - MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); - MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); - - temp = db->nextOut + db->fragSize; - if (temp >= db->fragTotalSize) { - MIPS_ASSERT(temp == db->fragTotalSize); - temp = 0; - } - - outl(db->lbufDma + temp, s->io + VRC5477_DAC1_BADDR); - if (s->dacChannels == 1) { - outl(db->lbufDma + temp, s->io + VRC5477_DAC2_BADDR); - } else { - outl(db->rbufDma + temp, s->io + VRC5477_DAC2_BADDR); - } - - db->stopped = 0; - -#if defined(VRC5477_AC97_VERBOSE_DEBUG) - outTicket = *(u16*)(db->lbuf+db->nextOut); - if (db->count > db->fragSize) { - MIPS_ASSERT((u16)(outTicket+1) == *(u16*)(db->lbuf+temp)); - } -#endif - - spin_unlock_irqrestore(&s->lock, flags); -} - -extern inline void stop_adc(struct vrc5477_ac97_state *s) -{ - struct dmabuf* db = &s->dma_adc; - unsigned long flags; - u32 temp; - - spin_lock_irqsave(&s->lock, flags); - - if (db->stopped) { - spin_unlock_irqrestore(&s->lock, flags); - return; - } - - /* deactivate the dma */ - outl(0, s->io + VRC5477_ADC1_CTRL); - outl(0, s->io + VRC5477_ADC2_CTRL); - - /* disable adc slots in aclink */ - temp = inl(s->io + VRC5477_CTRL); - temp &= ~ (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB); - outl (temp, s->io + VRC5477_CTRL); - - /* disable interrupts */ - temp = inl(s->io + VRC5477_INT_MASK); - temp &= ~ (VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END); - outl (temp, s->io + VRC5477_INT_MASK); - - /* clear pending ones */ - outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, - s->io + VRC5477_INT_CLR); - - db->stopped = 1; - - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_adc(struct vrc5477_ac97_state *s) -{ - struct dmabuf* db = &s->dma_adc; - unsigned long flags; - u32 dmaLength; - u32 temp; - - spin_lock_irqsave(&s->lock, flags); - - if (!db->stopped) { - spin_unlock_irqrestore(&s->lock, flags); - return; - } - - /* we should at least have some free space in the buffer */ - MIPS_ASSERT(db->count < db->fragTotalSize - db->fragSize * 2); - - /* clear pending ones */ - outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, - s->io + VRC5477_INT_CLR); - - /* enable interrupts */ - temp = inl(s->io + VRC5477_INT_MASK); - temp |= VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END; - outl(temp, s->io + VRC5477_INT_MASK); - - /* setup dma base addr */ - outl(db->lbufDma + db->nextIn, s->io + VRC5477_ADC1_BADDR); - outl(db->rbufDma + db->nextIn, s->io + VRC5477_ADC2_BADDR); - - /* setup dma length */ - dmaLength = db->fragSize >> 4; - outl(dmaLength, s->io + VRC5477_ADC1L); - outl(dmaLength, s->io + VRC5477_ADC2L); - - /* activate dma */ - outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_ADC1_CTRL); - outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_ADC2_CTRL); - - /* enable adc slots */ - temp = inl(s->io + VRC5477_CTRL); - temp |= (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB); - outl (temp, s->io + VRC5477_CTRL); - - /* it is time to setup next dma transfer */ - temp = db->nextIn + db->fragSize; - if (temp >= db->fragTotalSize) { - MIPS_ASSERT(temp == db->fragTotalSize); - temp = 0; - } - outl(db->lbufDma + temp, s->io + VRC5477_ADC1_BADDR); - outl(db->rbufDma + temp, s->io + VRC5477_ADC2_BADDR); - - db->stopped = 0; - - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -extern inline void dealloc_dmabuf(struct vrc5477_ac97_state *s, - struct dmabuf *db) -{ - if (db->lbuf) { - MIPS_ASSERT(db->rbuf); - pci_free_consistent(s->dev, PAGE_SIZE << db->bufOrder, - db->lbuf, db->lbufDma); - pci_free_consistent(s->dev, PAGE_SIZE << db->bufOrder, - db->rbuf, db->rbufDma); - db->lbuf = db->rbuf = NULL; - } - db->nextIn = db->nextOut = 0; - db->ready = 0; -} - -static int prog_dmabuf(struct vrc5477_ac97_state *s, - struct dmabuf *db, - unsigned rate) -{ - int order; - unsigned bufsize; - - if (!db->lbuf) { - MIPS_ASSERT(!db->rbuf); - - db->ready = 0; - for (order = DMABUF_DEFAULTORDER; - order >= DMABUF_MINORDER; - order--) { - db->lbuf = pci_alloc_consistent(s->dev, - PAGE_SIZE << order, - &db->lbufDma); - db->rbuf = pci_alloc_consistent(s->dev, - PAGE_SIZE << order, - &db->rbufDma); - if (db->lbuf && db->rbuf) break; - if (db->lbuf) { - MIPS_ASSERT(!db->rbuf); - pci_free_consistent(s->dev, - PAGE_SIZE << order, - db->lbuf, - db->lbufDma); - } - } - if (!db->lbuf) { - MIPS_ASSERT(!db->rbuf); - return -ENOMEM; - } - - db->bufOrder = order; - } - - db->count = 0; - db->nextIn = db->nextOut = 0; - - bufsize = PAGE_SIZE << db->bufOrder; - db->fragShift = ld2(rate * 2 / 100); - if (db->fragShift < 4) db->fragShift = 4; - - db->numFrag = bufsize >> db->fragShift; - while (db->numFrag < 4 && db->fragShift > 4) { - db->fragShift--; - db->numFrag = bufsize >> db->fragShift; - } - db->fragSize = 1 << db->fragShift; - db->fragTotalSize = db->numFrag << db->fragShift; - memset(db->lbuf, 0, db->fragTotalSize); - memset(db->rbuf, 0, db->fragTotalSize); - - db->ready = 1; - - return 0; -} - -extern inline int prog_dmabuf_adc(struct vrc5477_ac97_state *s) -{ - stop_adc(s); - return prog_dmabuf(s, &s->dma_adc, s->adcRate); -} - -extern inline int prog_dmabuf_dac(struct vrc5477_ac97_state *s) -{ - stop_dac(s); - return prog_dmabuf(s, &s->dma_dac, s->dacRate); -} - - -/* --------------------------------------------------------------------- */ -/* hold spinlock for the following! */ - -static inline void vrc5477_ac97_adc_interrupt(struct vrc5477_ac97_state *s) -{ - struct dmabuf* adc = &s->dma_adc; - unsigned temp; - - /* we need two frags avaiable because one is already being used - * and the other will be used when next interrupt happens. - */ - if (adc->count >= adc->fragTotalSize - adc->fragSize) { - stop_adc(s); - adc->error++; - printk(KERN_INFO PFX "adc overrun\n"); - return; - } - - /* set the base addr for next DMA transfer */ - temp = adc->nextIn + 2*adc->fragSize; - if (temp >= adc->fragTotalSize) { - MIPS_ASSERT( (temp == adc->fragTotalSize) || - (temp == adc->fragTotalSize + adc->fragSize) ); - temp -= adc->fragTotalSize; - } - outl(adc->lbufDma + temp, s->io + VRC5477_ADC1_BADDR); - outl(adc->rbufDma + temp, s->io + VRC5477_ADC2_BADDR); - - /* adjust nextIn */ - adc->nextIn += adc->fragSize; - if (adc->nextIn >= adc->fragTotalSize) { - MIPS_ASSERT(adc->nextIn == adc->fragTotalSize); - adc->nextIn = 0; - } - - /* adjust count */ - adc->count += adc->fragSize; - - /* wake up anybody listening */ - if (waitqueue_active(&adc->wait)) { - wake_up_interruptible(&adc->wait); - } -} - -static inline void vrc5477_ac97_dac_interrupt(struct vrc5477_ac97_state *s) -{ - struct dmabuf* dac = &s->dma_dac; - unsigned temp; - - /* next DMA transfer should already started */ - MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); - MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); - - /* let us set for next next DMA transfer */ - temp = dac->nextOut + dac->fragSize*2; - if (temp >= dac->fragTotalSize) { - MIPS_ASSERT( (temp == dac->fragTotalSize) || - (temp == dac->fragTotalSize + dac->fragSize) ); - temp -= dac->fragTotalSize; - } - outl(dac->lbufDma + temp, s->io + VRC5477_DAC1_BADDR); - if (s->dacChannels == 1) { - outl(dac->lbufDma + temp, s->io + VRC5477_DAC2_BADDR); - } else { - outl(dac->rbufDma + temp, s->io + VRC5477_DAC2_BADDR); - } - -#if defined(VRC5477_AC97_VERBOSE_DEBUG) - if (*(u16*)(dac->lbuf + dac->nextOut) != outTicket) { - printk("assert fail: - %d vs %d\n", - *(u16*)(dac->lbuf + dac->nextOut), - outTicket); - MIPS_ASSERT(1 == 0); - } -#endif - - /* adjust nextOut pointer */ - dac->nextOut += dac->fragSize; - if (dac->nextOut >= dac->fragTotalSize) { - MIPS_ASSERT(dac->nextOut == dac->fragTotalSize); - dac->nextOut = 0; - } - - /* adjust count */ - dac->count -= dac->fragSize; - if (dac->count <=0 ) { - MIPS_ASSERT(dac->count == 0); - MIPS_ASSERT(dac->nextIn == dac->nextOut); - /* buffer under run */ - stop_dac(s); - } - -#if defined(VRC5477_AC97_VERBOSE_DEBUG) - if (dac->count) { - outTicket ++; - MIPS_ASSERT(*(u16*)(dac->lbuf + dac->nextOut) == outTicket); - } -#endif - - /* we cannot have both under run and someone is waiting on us */ - MIPS_ASSERT(! (waitqueue_active(&dac->wait) && (dac->count <= 0)) ); - - /* wake up anybody listening */ - if (waitqueue_active(&dac->wait)) - wake_up_interruptible(&dac->wait); -} - -static void vrc5477_ac97_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)dev_id; - u32 irqStatus; - u32 adcInterrupts, dacInterrupts; - - spin_lock(&s->lock); - - /* get irqStatus and clear the detected ones */ - irqStatus = inl(s->io + VRC5477_INT_STATUS); - outl(irqStatus, s->io + VRC5477_INT_CLR); - - /* let us see what we get */ - dacInterrupts = VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END; - adcInterrupts = VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END; - if (irqStatus & dacInterrupts) { - /* we should get both interrupts, but just in case ... */ - if (irqStatus & VRC5477_INT_MASK_DAC1END) { - vrc5477_ac97_dac_interrupt(s); - } - if ( (irqStatus & dacInterrupts) != dacInterrupts ) { - printk(KERN_WARNING "vrc5477_ac97 : dac interrupts not in sync!!!\n"); - stop_dac(s); - start_dac(s); - } - } else if (irqStatus & adcInterrupts) { - /* we should get both interrupts, but just in case ... */ - if(irqStatus & VRC5477_INT_MASK_ADC1END) { - vrc5477_ac97_adc_interrupt(s); - } - if ( (irqStatus & adcInterrupts) != adcInterrupts ) { - printk(KERN_WARNING "vrc5477_ac97 : adc interrupts not in sync!!!\n"); - stop_adc(s); - start_adc(s); - } - } - - spin_unlock(&s->lock); -} - -/* --------------------------------------------------------------------- */ - -static int vrc5477_ac97_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - struct list_head *list; - struct vrc5477_ac97_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct vrc5477_ac97_state, devs); - if (s->codec.dev_mixer == minor) - break; - } - file->private_data = s; - return 0; -} - -static int vrc5477_ac97_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 vrc5477_ac97_ioctl_mixdev(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct vrc5477_ac97_state *s = - (struct vrc5477_ac97_state *)file->private_data; - struct ac97_codec *codec = &s->codec; - - return mixdev_ioctl(codec, cmd, arg); -} - -static /*const*/ struct file_operations vrc5477_ac97_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: vrc5477_ac97_ioctl_mixdev, - open: vrc5477_ac97_open_mixdev, - release: vrc5477_ac97_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct vrc5477_ac97_state *s, int nonblock) -{ - unsigned long flags; - int count, tmo; - - if (!s->dma_dac.ready) - 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->dacRate / 2; - vrc5477_ac97_delay(tmo); - } - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int inline -copy_two_channel_adc_to_user(struct vrc5477_ac97_state *s, - char *buffer, - int copyCount) -{ - struct dmabuf *db = &s->dma_adc; - int bufStart = db->nextOut; - for (; copyCount > 0; ) { - int i; - int count = copyCount; - if (count > WORK_BUF_SIZE/2) count = WORK_BUF_SIZE/2; - for (i=0; i< count/2; i++) { - s->workBuf[i].lchannel = - *(u16*)(db->lbuf + bufStart + i*2); - s->workBuf[i].rchannel = - *(u16*)(db->rbuf + bufStart + i*2); - } - if (copy_to_user(buffer, s->workBuf, count*2)) { - return -1; - } - - copyCount -= count; - bufStart += count; - MIPS_ASSERT(bufStart <= db->fragTotalSize); - buffer += count *2; - } - return 0; -} - -/* return the total bytes that is copied */ -static int inline -copy_adc_to_user(struct vrc5477_ac97_state *s, - char * buffer, - size_t count, - int avail) -{ - struct dmabuf *db = &s->dma_adc; - int copyCount=0; - int copyFragCount=0; - int totalCopyCount = 0; - int totalCopyFragCount = 0; - unsigned long flags; - - /* adjust count to signel channel byte count */ - count >>= s->adcChannels - 1; - - /* we may have to "copy" twice as ring buffer wraps around */ - for (; (avail > 0) && (count > 0); ) { - /* determine max possible copy count for single channel */ - copyCount = count; - if (copyCount > avail) { - copyCount = avail; - } - if (copyCount + db->nextOut > db->fragTotalSize) { - copyCount = db->fragTotalSize - db->nextOut; - MIPS_ASSERT((copyCount % db->fragSize) == 0); - } - - copyFragCount = (copyCount-1) >> db->fragShift; - copyFragCount = (copyFragCount+1) << db->fragShift; - MIPS_ASSERT(copyFragCount >= copyCount); - - /* we copy differently based on adc channels */ - if (s->adcChannels == 1) { - if (copy_to_user(buffer, - db->lbuf + db->nextOut, - copyCount)) - return -1; - } else { - /* *sigh* we have to mix two streams into one */ - if (copy_two_channel_adc_to_user(s, buffer, copyCount)) - return -1; - } - - count -= copyCount; - totalCopyCount += copyCount; - avail -= copyFragCount; - totalCopyFragCount += copyFragCount; - - buffer += copyCount << (s->adcChannels-1); - - db->nextOut += copyFragCount; - if (db->nextOut >= db->fragTotalSize) { - MIPS_ASSERT(db->nextOut == db->fragTotalSize); - db->nextOut = 0; - } - - MIPS_ASSERT((copyFragCount % db->fragSize) == 0); - MIPS_ASSERT( (count == 0) || (copyCount == copyFragCount)); - } - - spin_lock_irqsave(&s->lock, flags); - db->count -= totalCopyFragCount; - spin_unlock_irqrestore(&s->lock, flags); - - return totalCopyCount << (s->adcChannels-1); -} - -static ssize_t -vrc5477_ac97_read(struct file *file, - char *buffer, - size_t count, - loff_t *ppos) -{ - struct vrc5477_ac97_state *s = - (struct vrc5477_ac97_state *)file->private_data; - struct dmabuf *db = &s->dma_adc; - ssize_t ret = 0; - unsigned long flags; - int copyCount; - size_t avail; - - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - - MIPS_ASSERT(db->ready); - - 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); - - MIPS_ASSERT( (avail % db->fragSize) == 0); - copyCount = copy_adc_to_user(s, buffer, count, avail); - if (copyCount <=0 ) { - if (!ret) ret = -EFAULT; - return ret; - } - - count -= copyCount; - buffer += copyCount; - ret += copyCount; - } // while (count > 0) - - return ret; -} - -static int inline -copy_two_channel_dac_from_user(struct vrc5477_ac97_state *s, - const char *buffer, - int copyCount) -{ - struct dmabuf *db = &s->dma_dac; - int bufStart = db->nextIn; - - MIPS_ASSERT(db->ready); - - for (; copyCount > 0; ) { - int i; - int count = copyCount; - if (count > WORK_BUF_SIZE/2) count = WORK_BUF_SIZE/2; - if (copy_from_user(s->workBuf, buffer, count*2)) { - return -1; - } - for (i=0; i< count/2; i++) { - *(u16*)(db->lbuf + bufStart + i*2) = - s->workBuf[i].lchannel; - *(u16*)(db->rbuf + bufStart + i*2) = - s->workBuf[i].rchannel; - } - - copyCount -= count; - bufStart += count; - MIPS_ASSERT(bufStart <= db->fragTotalSize); - buffer += count *2; - } - return 0; - -} - -/* return the total bytes that is copied */ -static int inline -copy_dac_from_user(struct vrc5477_ac97_state *s, - const char *buffer, - size_t count, - int avail) -{ - struct dmabuf *db = &s->dma_dac; - int copyCount=0; - int copyFragCount=0; - int totalCopyCount = 0; - int totalCopyFragCount = 0; - unsigned long flags; -#if defined(VRC5477_AC97_VERBOSE_DEBUG) - int i; -#endif - - /* adjust count to signel channel byte count */ - count >>= s->dacChannels - 1; - - /* we may have to "copy" twice as ring buffer wraps around */ - for (; (avail > 0) && (count > 0); ) { - /* determine max possible copy count for single channel */ - copyCount = count; - if (copyCount > avail) { - copyCount = avail; - } - if (copyCount + db->nextIn > db->fragTotalSize) { - copyCount = db->fragTotalSize - db->nextIn; - MIPS_ASSERT((copyCount % db->fragSize) == 0); - MIPS_ASSERT(copyCount > 0); - } - - copyFragCount = (copyCount-1) >> db->fragShift; - copyFragCount = (copyFragCount+1) << db->fragShift; - MIPS_ASSERT(copyFragCount >= copyCount); - - /* we copy differently based on the number channels */ - if (s->dacChannels == 1) { - if (copy_from_user(db->lbuf + db->nextIn, - buffer, - copyCount)) - return -1; - /* fill gaps with 0 */ - memset(db->lbuf + db->nextIn + copyCount, - 0, - copyFragCount - copyCount); - } else { - /* we have demux the stream into two separate ones */ - if (copy_two_channel_dac_from_user(s, buffer, copyCount)) - return -1; - /* fill gaps with 0 */ - memset(db->lbuf + db->nextIn + copyCount, - 0, - copyFragCount - copyCount); - memset(db->rbuf + db->nextIn + copyCount, - 0, - copyFragCount - copyCount); - } - -#if defined(VRC5477_AC97_VERBOSE_DEBUG) - for (i=0; i< copyFragCount; i+= db->fragSize) { - *(u16*)(db->lbuf + db->nextIn + i) = inTicket ++; - } -#endif - - count -= copyCount; - totalCopyCount =+ copyCount; - avail -= copyFragCount; - totalCopyFragCount += copyFragCount; - - buffer += copyCount << (s->dacChannels - 1); - - db->nextIn += copyFragCount; - if (db->nextIn >= db->fragTotalSize) { - MIPS_ASSERT(db->nextIn == db->fragTotalSize); - db->nextIn = 0; - } - - MIPS_ASSERT((copyFragCount % db->fragSize) == 0); - MIPS_ASSERT( (count == 0) || (copyCount == copyFragCount)); - } - - spin_lock_irqsave(&s->lock, flags); - db->count += totalCopyFragCount; - if (db->stopped) { - start_dac(s); - } - - /* nextIn should not be equal to nextOut unless we are full */ - MIPS_ASSERT( ( (db->count == db->fragTotalSize) && - (db->nextIn == db->nextOut) ) || - ( (db->count < db->fragTotalSize) && - (db->nextIn != db->nextOut) ) ); - - spin_unlock_irqrestore(&s->lock, flags); - - return totalCopyCount << (s->dacChannels-1); - -} - -static ssize_t vrc5477_ac97_write(struct file *file, const char *buffer, - size_t count, loff_t *ppos) -{ - struct vrc5477_ac97_state *s = - (struct vrc5477_ac97_state *)file->private_data; - struct dmabuf *db = &s->dma_dac; - ssize_t ret; - unsigned long flags; - int copyCount, avail; - - if (ppos != &file->f_pos) - return -ESPIPE; - 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->fragTotalSize - 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); - - MIPS_ASSERT( (avail % db->fragSize) == 0); - copyCount = copy_dac_from_user(s, buffer, count, avail); - if (copyCount < 0) { - if (!ret) ret = -EFAULT; - return ret; - } - - count -= copyCount; - buffer += copyCount; - ret += copyCount; - } // while (count > 0) - - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int vrc5477_ac97_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct vrc5477_ac97_state *s = (struct vrc5477_ac97_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 ((signed)s->dma_dac.fragTotalSize >= - s->dma_dac.count + (signed)s->dma_dac.fragSize) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -#ifdef CONFIG_LL_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_SETFMT, "SNDCTL_DSP_SETFMT"}, - {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_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 vrc5477_ac97_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - int count; - int val, ret; - -#ifdef CONFIG_LL_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, (int *)arg); - - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(); - s->dma_dac.count = 0; - s->dma_dac.nextIn = s->dma_dac.nextOut = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(); - s->dma_adc.count = 0; - s->dma_adc.nextIn = s->dma_adc.nextOut = 0; - } - 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->adcChannels = 2; - else - s->adcChannels = 1; - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - if (val) - s->dacChannels = 2; - else - s->dacChannels = 1; - 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 ( (val != 1) && (val != 2)) val = 2; - - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dacChannels = val; - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dacChannels = 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, (int *)arg); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != AFMT_QUERY) { - if (val != AFMT_S16_LE) return -EINVAL; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - } else { - val = AFMT_S16_LE; - } - return put_user(val, (int *)arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - case SNDCTL_DSP_SETTRIGGER: - /* NO trigger */ - return -EINVAL; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - abinfo.fragsize = s->dma_dac.fragSize << (s->dacChannels-1); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - abinfo.bytes = (s->dma_dac.fragTotalSize - count) << - (s->dacChannels-1); - abinfo.fragstotal = s->dma_dac.numFrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragShift >> - (s->dacChannels-1); - 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 << (s->adcChannels-1); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_adc.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - abinfo.bytes = count << (s->adcChannels-1); - abinfo.fragstotal = s->dma_adc.numFrag; - abinfo.fragments = (abinfo.bytes >> s->dma_adc.fragShift) >> - (s->adcChannels-1); - 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; - spin_unlock_irqrestore(&s->lock, flags); - return put_user(count, (int *)arg); - - case SNDCTL_DSP_GETIPTR: - case SNDCTL_DSP_GETOPTR: - /* we cannot get DMA ptr */ - return -EINVAL; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) - return put_user(s->dma_dac.fragSize << (s->dacChannels-1), (int *)arg); - else - return put_user(s->dma_adc.fragSize << (s->adcChannels-1), (int *)arg); - - case SNDCTL_DSP_SETFRAGMENT: - /* we ignore fragment size request */ - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - /* what is this for? [jsun] */ - 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->adcChannels, (int *)arg); - else - return put_user(s->dacChannels ? 2 : 1, (int *)arg); - - case SOUND_PCM_READ_BITS: - return put_user(16, (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 vrc5477_ac97_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 vrc5477_ac97_state *s; - int ret=0; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct vrc5477_ac97_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; - } - 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); - - if (file->f_mode & FMODE_READ) { - /* set default settings */ - set_adc_rate(s, 48000); - s->adcChannels = 2; - - ret = prog_dmabuf_adc(s); - if (ret) goto bailout; - } - if (file->f_mode & FMODE_WRITE) { - /* set default settings */ - set_dac_rate(s, 48000); - s->dacChannels = 2; - - ret = prog_dmabuf_dac(s); - if (ret) goto bailout; - } - - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - - bailout: - spin_unlock_irqrestore(&s->lock, flags); - - up(&s->open_sem); - return ret; -} - -static int vrc5477_ac97_release(struct inode *inode, struct file *file) -{ - struct vrc5477_ac97_state *s = - (struct vrc5477_ac97_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; -} - -static /*const*/ struct file_operations vrc5477_ac97_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: vrc5477_ac97_read, - write: vrc5477_ac97_write, - poll: vrc5477_ac97_poll, - ioctl: vrc5477_ac97_ioctl, - // mmap: vrc5477_ac97_mmap, - open: vrc5477_ac97_open, - release: vrc5477_ac97_release, -}; - - -/* --------------------------------------------------------------------- */ - - -/* --------------------------------------------------------------------- */ - -/* - * for debugging purposes, we'll create a proc device that dumps the - * CODEC chipstate - */ - -#ifdef CONFIG_LL_DEBUG - -struct { - const char *regname; - unsigned regaddr; -} vrc5477_ac97_regs[] = { - {"VRC5477_INT_STATUS", VRC5477_INT_STATUS}, - {"VRC5477_CODEC_WR", VRC5477_CODEC_WR}, - {"VRC5477_CODEC_RD", VRC5477_CODEC_RD}, - {"VRC5477_CTRL", VRC5477_CTRL}, - {"VRC5477_ACLINK_CTRL", VRC5477_ACLINK_CTRL}, - {"VRC5477_INT_MASK", VRC5477_INT_MASK}, - {"VRC5477_DAC1_CTRL", VRC5477_DAC1_CTRL}, - {"VRC5477_DAC1L", VRC5477_DAC1L}, - {"VRC5477_DAC1_BADDR", VRC5477_DAC1_BADDR}, - {"VRC5477_DAC2_CTRL", VRC5477_DAC2_CTRL}, - {"VRC5477_DAC2L", VRC5477_DAC2L}, - {"VRC5477_DAC2_BADDR", VRC5477_DAC2_BADDR}, - {"VRC5477_DAC3_CTRL", VRC5477_DAC3_CTRL}, - {"VRC5477_DAC3L", VRC5477_DAC3L}, - {"VRC5477_DAC3_BADDR", VRC5477_DAC3_BADDR}, - {"VRC5477_ADC1_CTRL", VRC5477_ADC1_CTRL}, - {"VRC5477_ADC1L", VRC5477_ADC1L}, - {"VRC5477_ADC1_BADDR", VRC5477_ADC1_BADDR}, - {"VRC5477_ADC2_CTRL", VRC5477_ADC2_CTRL}, - {"VRC5477_ADC2L", VRC5477_ADC2L}, - {"VRC5477_ADC2_BADDR", VRC5477_ADC2_BADDR}, - {"VRC5477_ADC3_CTRL", VRC5477_ADC3_CTRL}, - {"VRC5477_ADC3L", VRC5477_ADC3L}, - {"VRC5477_ADC3_BADDR", VRC5477_ADC3_BADDR}, - {NULL, 0x0} -}; - -static int proc_vrc5477_ac97_dump (char *buf, char **start, off_t fpos, - int length, int *eof, void *data) -{ - struct vrc5477_ac97_state *s; - int cnt, len = 0; - - if (list_empty(&devs)) - return 0; - s = list_entry(devs.next, struct vrc5477_ac97_state, devs); - - /* print out header */ - len += sprintf(buf + len, "\n\t\tVrc5477 Audio Debug\n\n"); - - // print out digital controller state - len += sprintf (buf + len, "NEC Vrc5477 Audio Controller registers\n"); - len += sprintf (buf + len, "---------------------------------\n"); - for (cnt=0; vrc5477_ac97_regs[cnt].regname != NULL; cnt++) { - len+= sprintf (buf + len, "%-20s = %08x\n", - vrc5477_ac97_regs[cnt].regname, - inl(s->io + vrc5477_ac97_regs[cnt].regaddr)); - } - - /* print out driver state */ - len += sprintf (buf + len, "NEC Vrc5477 Audio driver states\n"); - len += sprintf (buf + len, "---------------------------------\n"); - len += sprintf (buf + len, "dacChannels = %d\n", s->dacChannels); - len += sprintf (buf + len, "adcChannels = %d\n", s->adcChannels); - len += sprintf (buf + len, "dacRate = %d\n", s->dacRate); - len += sprintf (buf + len, "adcRate = %d\n", s->adcRate); - - len += sprintf (buf + len, "dma_dac is %s ready\n", - s->dma_dac.ready? "" : "not"); - if (s->dma_dac.ready) { - len += sprintf (buf + len, "dma_dac is %s stopped.\n", - s->dma_dac.stopped? "" : "not"); - len += sprintf (buf + len, "dma_dac.fragSize = %x\n", - s->dma_dac.fragSize); - len += sprintf (buf + len, "dma_dac.fragShift = %x\n", - s->dma_dac.fragShift); - len += sprintf (buf + len, "dma_dac.numFrag = %x\n", - s->dma_dac.numFrag); - len += sprintf (buf + len, "dma_dac.fragTotalSize = %x\n", - s->dma_dac.fragTotalSize); - len += sprintf (buf + len, "dma_dac.nextIn = %x\n", - s->dma_dac.nextIn); - len += sprintf (buf + len, "dma_dac.nextOut = %x\n", - s->dma_dac.nextOut); - len += sprintf (buf + len, "dma_dac.count = %x\n", - s->dma_dac.count); - } - - len += sprintf (buf + len, "dma_adc is %s ready\n", - s->dma_adc.ready? "" : "not"); - if (s->dma_adc.ready) { - len += sprintf (buf + len, "dma_adc is %s stopped.\n", - s->dma_adc.stopped? "" : "not"); - len += sprintf (buf + len, "dma_adc.fragSize = %x\n", - s->dma_adc.fragSize); - len += sprintf (buf + len, "dma_adc.fragShift = %x\n", - s->dma_adc.fragShift); - len += sprintf (buf + len, "dma_adc.numFrag = %x\n", - s->dma_adc.numFrag); - len += sprintf (buf + len, "dma_adc.fragTotalSize = %x\n", - s->dma_adc.fragTotalSize); - len += sprintf (buf + len, "dma_adc.nextIn = %x\n", - s->dma_adc.nextIn); - len += sprintf (buf + len, "dma_adc.nextOut = %x\n", - s->dma_adc.nextOut); - len += sprintf (buf + len, "dma_adc.count = %x\n", - s->dma_adc.count); - } - - /* 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 len; - -} -#endif /* CONFIG_LL_DEBUG */ - -/* --------------------------------------------------------------------- */ - -/* maximum number of devices; only used for command line params */ -#define NR_DEVICE 5 - -static unsigned int devindex = 0; - -MODULE_AUTHOR("Monta Vista Software, jsun@mvista.com or jsun@junsun.net"); -MODULE_DESCRIPTION("NEC Vrc5477 audio (AC97) Driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ -extern void jsun_scan_pci_bus(void); -extern void vrc5477_show_pci_regs(void); -extern void vrc5477_show_pdar_regs(void); - -/* -------------------------------------------------------- */ -#define AC97_BASE 0xbb000000 -#define myinl(x) *(volatile u32*)(AC97_BASE + (x)) -#define myoutl(x,y) *(volatile u32*)(AC97_BASE + (y)) = (x) - -u16 myrdcodec(u8 addr) -{ - u32 result; - - /* wait until we can access codec registers */ - // while (inl(VRC5477_CODEC_WR) & 0x80000000); - - /* write the address and "read" command to codec */ - addr = addr & 0x7f; - myoutl((addr << 16) | VRC5477_CODEC_WR_RWC, VRC5477_CODEC_WR); - - /* get the return result */ - udelay(100); /* workaround hardware bug */ - // dump_memory(0xbb000000, 48); - while ( ((result=myinl(VRC5477_CODEC_RD)) & 0xc0000000) != 0xc0000000); - MIPS_ASSERT(addr == ((result >> 16) & 0x7f) ); - return result & 0xffff; -} - -void mywrcodec(u8 addr, u16 data) -{ - /* wait until we can access codec registers */ - while (myinl(VRC5477_CODEC_WR) & 0x80000000); - - /* write the address and value to codec */ - myoutl((addr << 16) | data, VRC5477_CODEC_WR); - -} - - -void jsun_ac97_test(struct vrc5477_ac97_state *s) -{ - int i; - - /* reset codec */ - /* - wrcodec(&s->codec, 0, 0); - while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); - */ - mywrcodec(0, 0); - while (myinl(VRC5477_CODEC_WR) & 0x80000000); - - for (i=0; i< 0x40; i+=4) { - MIPS_ASSERT(inl(s->io+i) == myinl(i)); - } - - printk("codec registers : "); - for (i=0; i<= 0x3a; i+=2) { - if ( (i%0x10) == 0) { - printk("\n%02x\t", i); - } - // printk("%04x\t", rdcodec(&s->codec, i)); - printk("%04x\t", myrdcodec(i)); - } - printk("\n\n"); - printk("codec registers : "); - for (i=0; i<= 0x3a; i+=2) { - if ( (i%0x10) == 0) { - printk("\n%02x\t", i); - } - printk("%04x\t", rdcodec(&s->codec, i)); - } - printk("\n\n"); -} - -static int __devinit vrc5477_ac97_probe(struct pci_dev *pcidev, - const struct pci_device_id *pciid) -{ - struct vrc5477_ac97_state *s; - char proc_str[80]; - - MIPS_DEBUG(printk("vrc5477_ac97_probe() invoked\n")); - - if (pcidev->irq == 0) - return -1; - - if (!(s = kmalloc(sizeof(struct vrc5477_ac97_state), GFP_KERNEL))) { - printk(KERN_ERR PFX "alloc of device struct failed\n"); - return -1; - } - memset(s, 0, sizeof(struct vrc5477_ac97_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->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 - * no persistent state across file opens. - */ - - if (!request_region(s->io, pci_resource_len(pcidev,0), - VRC5477_AC97_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, vrc5477_ac97_interrupt, SA_INTERRUPT, - VRC5477_AC97_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(&vrc5477_ac97_audio_fops, -1)) < 0) - goto err_dev1; - if ((s->codec.dev_mixer = - register_sound_mixer(&vrc5477_ac97_mixer_fops, -1)) < 0) - goto err_dev2; - -#ifdef CONFIG_LL_DEBUG - /* intialize the debug proc device */ - s->ps = create_proc_read_entry(VRC5477_AC97_MODULE_NAME, 0, NULL, - proc_vrc5477_ac97_dump, NULL); -#endif /* CONFIG_LL_DEBUG */ - - /* enable pci io and bus mastering */ - if (pci_enable_device(pcidev)) - goto err_dev3; - pci_set_master(pcidev); - -/* -jsun_scan_pci_bus(); -vrc5477_show_pci_regs(); -vrc5477_show_pdar_regs(); -*/ - - /* cold reset the AC97 */ - outl(VRC5477_ACLINK_CTRL_RST_ON | VRC5477_ACLINK_CTRL_RST_TIME, - s->io + VRC5477_ACLINK_CTRL); - while (inl(s->io + VRC5477_ACLINK_CTRL) & VRC5477_ACLINK_CTRL_RST_ON); - -/* -jsun_ac97_test(s); -*/ - - /* codec init */ - if (!ac97_probe_codec(&s->codec)) - goto err_dev3; - -#ifdef CONFIG_LL_DEBUG - sprintf(proc_str, "driver/%s/%d/ac97", - VRC5477_AC97_MODULE_NAME, s->codec.id); - s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, - ac97_read_proc, &s->codec); - /* TODO : why this proc file does not show up? */ -#endif - - /* let us get the default volumne louder */ - wrcodec(&s->codec, 0x2, 0); - wrcodec(&s->codec, 0x18, 0x0707); - /* mute line in loopback to line out */ - wrcodec(&s->codec, 0x10, 0x8000); - - /* by default we select line in the input */ - wrcodec(&s->codec, 0x1a, 0x0404); - /* pick middle value for record gain */ - // wrcodec(&s->codec, 0x1c, 0x0707); - 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); - - /* 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); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR PFX "cannot register misc device\n"); - free_irq(s->irq, s); - err_irq: - release_region(s->io, pci_resource_len(pcidev,0)); - err_region: - kfree(s); - return -1; -} - -static void __devinit vrc5477_ac97_remove(struct pci_dev *dev) -{ - struct vrc5477_ac97_state *s = pci_get_drvdata(dev); - - if (!s) - return; - list_del(&s->devs); -#ifdef CONFIG_LL_DEBUG - if (s->ps) - remove_proc_entry(VRC5477_AC97_MODULE_NAME, NULL); -#endif /* CONFIG_LL_DEBUG */ - 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); - kfree(s); - pci_set_drvdata(dev, NULL); -} - - -#define PCI_VENDOR_ID_NEC 0x1033 -#define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00A6 -static struct pci_device_id id_table[] __devinitdata = { - { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_VRC5477_AC97, - PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver vrc5477_ac97_driver = { - name: VRC5477_AC97_MODULE_NAME, - id_table: id_table, - probe: vrc5477_ac97_probe, - remove: vrc5477_ac97_remove -}; - -static int __init init_vrc5477_ac97(void) -{ - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - printk("Vrc5477 AC97 driver: version v0.1 time " __TIME__ " " __DATE__ " by Jun Sun\n"); - return pci_module_init(&vrc5477_ac97_driver); -} - -static void __exit cleanup_vrc5477_ac97(void) -{ - printk(KERN_INFO PFX "unloading\n"); - pci_unregister_driver(&vrc5477_ac97_driver); -} - -module_init(init_vrc5477_ac97); -module_exit(cleanup_vrc5477_ac97); - diff -Nru a/drivers/sound/nm256.h b/drivers/sound/nm256.h --- a/drivers/sound/nm256.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,290 +0,0 @@ -#ifndef _NM256_H_ -#define _NM256_H_ - -#include "ac97.h" - -/* The revisions that we currently handle. */ -enum nm256rev { - REV_NM256AV, REV_NM256ZX -}; - -/* Per-card structure. */ -struct nm256_info -{ - /* Magic number used to verify that this struct is valid. */ -#define NM_MAGIC_SIG 0x55aa00ff - int magsig; - - /* Revision number */ - enum nm256rev rev; - - struct ac97_hwint mdev; - - /* Our audio device numbers. */ - int dev[2]; - - /* The # of times each device has been opened. (Should only be - 0 or 1). */ - int opencnt[2]; - - /* We use two devices, because we can do simultaneous play and record. - This keeps track of which device is being used for what purpose; - these are the actual device numbers. */ - int dev_for_play; - int dev_for_record; - - /* The mixer device. */ - int mixer_oss_dev; - - /* - * Can only be opened once for each operation. These aren't set - * until an actual I/O operation is performed; this allows one - * device to be open for read/write without inhibiting I/O to - * the other device. - */ - int is_open_play; - int is_open_record; - - /* Non-zero if we're currently playing a sample. */ - int playing; - /* Ditto for recording a sample. */ - int recording; - - /* The two memory ports. */ - struct nm256_ports { - /* Physical address of the port. */ - u32 physaddr; - /* Our mapped-in pointer. */ - char *ptr; - /* PTR's offset within the physical port. */ - u32 start_offset; - /* And the offset of the end of the buffer. */ - u32 end_offset; - } port[2]; - - /* The following are offsets within memory port 1. */ - u32 coeffBuf; - u32 allCoeffBuf; - - /* Record and playback buffers. */ - u32 abuf1, abuf2; - - /* Offset of the AC97 mixer in memory port 2. */ - u32 mixer; - - /* Offset of the mixer status register in memory port 2. */ - u32 mixer_status_offset; - - /* Non-zero if we have written initial values to the mixer. */ - u8 mixer_values_init; - - /* - * Status mask bit; (*mixer_status_loc & mixer_status_mask) == 0 means - * it's ready. - */ - u16 mixer_status_mask; - - /* The sizes of the playback and record ring buffers. */ - u32 playbackBufferSize; - u32 recordBufferSize; - - /* Are the coefficient values in the memory cache current? */ - u8 coeffsCurrent; - - /* For writes, the amount we last wrote. */ - u32 requested_amt; - /* The start of the block currently playing. */ - u32 curPlayPos; - - /* The amount of data we were requested to record. */ - u32 requestedRecAmt; - /* The offset of the currently-recording block. */ - u32 curRecPos; - /* The destination buffer. */ - char *recBuf; - - /* Our IRQ number. */ - int irq; - - /* A flag indicating how many times we've grabbed the IRQ. */ - int has_irq; - - /* The card interrupt service routine. */ - void (*introutine) (int, void *, struct pt_regs *); - - /* Current audio config, cached. */ - struct sinfo { - u32 samplerate; - u8 bits; - u8 stereo; - } sinfo[2]; /* goes with each device */ - - /* The cards are stored in a chain; this is the next card. */ - struct nm256_info *next_card; -}; - -/* Debug flag--bigger numbers mean more output. */ -extern int nm256_debug; - -/* The BIOS signature. */ -#define NM_SIGNATURE 0x4e4d0000 -/* Signature mask. */ -#define NM_SIG_MASK 0xffff0000 - -/* Size of the second memory area. */ -#define NM_PORT2_SIZE 4096 - -/* The base offset of the mixer in the second memory area. */ -#define NM_MIXER_OFFSET 0x600 - -/* The maximum size of a coefficient entry. */ -#define NM_MAX_COEFFICIENT 0x5000 - -/* The interrupt register. */ -#define NM_INT_REG 0xa04 -/* And its bits. */ -#define NM_PLAYBACK_INT 0x40 -#define NM_RECORD_INT 0x100 -#define NM_MISC_INT_1 0x4000 -#define NM_MISC_INT_2 0x1 -#define NM_ACK_INT(CARD, X) nm256_writePort16((CARD), 2, NM_INT_REG, (X) << 1) - -/* The AV's "mixer ready" status bit and location. */ -#define NM_MIXER_STATUS_OFFSET 0xa04 -#define NM_MIXER_READY_MASK 0x0800 -#define NM_MIXER_PRESENCE 0xa06 -#define NM_PRESENCE_MASK 0x0050 -#define NM_PRESENCE_VALUE 0x0040 - -/* - * For the ZX. It uses the same interrupt register, but it holds 32 - * bits instead of 16. - */ -#define NM2_PLAYBACK_INT 0x10000 -#define NM2_RECORD_INT 0x80000 -#define NM2_MISC_INT_1 0x8 -#define NM2_MISC_INT_2 0x2 -#define NM2_ACK_INT(CARD, X) nm256_writePort32((CARD), 2, NM_INT_REG, (X)) - -/* The ZX's "mixer ready" status bit and location. */ -#define NM2_MIXER_STATUS_OFFSET 0xa06 -#define NM2_MIXER_READY_MASK 0x0800 - -/* The playback registers start from here. */ -#define NM_PLAYBACK_REG_OFFSET 0x0 -/* The record registers start from here. */ -#define NM_RECORD_REG_OFFSET 0x200 - -/* The rate register is located 2 bytes from the start of the register area. */ -#define NM_RATE_REG_OFFSET 2 - -/* Mono/stereo flag, number of bits on playback, and rate mask. */ -#define NM_RATE_STEREO 1 -#define NM_RATE_BITS_16 2 -#define NM_RATE_MASK 0xf0 - -/* Playback enable register. */ -#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1) -#define NM_PLAYBACK_ENABLE_FLAG 1 -#define NM_PLAYBACK_ONESHOT 2 -#define NM_PLAYBACK_FREERUN 4 - -/* Mutes the audio output. */ -#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18) -#define NM_AUDIO_MUTE_LEFT 0x8000 -#define NM_AUDIO_MUTE_RIGHT 0x0080 - -/* Recording enable register. */ -#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0) -#define NM_RECORD_ENABLE_FLAG 1 -#define NM_RECORD_FREERUN 2 - -#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4) -#define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10) -#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc) -#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8) - -#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4) -#define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14) -#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc) -#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8) - -/* A few trivial routines to make it easier to work with the registers - on the chip. */ - -/* This is a common code portion used to fix up the port offsets. */ -#define NM_FIX_PORT \ - if (port < 1 || port > 2 || card == NULL) \ - return -1; \ -\ - if (offset < card->port[port - 1].start_offset \ - || offset >= card->port[port - 1].end_offset) { \ - printk (KERN_ERR "Bad access: port %d, offset 0x%x\n", port, offset); \ - return -1; \ - } \ - offset -= card->port[port - 1].start_offset; - -#define DEFwritePortX(X, func) \ -static inline int nm256_writePort##X (struct nm256_info *card,\ - int port, int offset, int value)\ -{\ - u##X *addr;\ -\ - if (nm256_debug > 1)\ - printk (KERN_DEBUG "Writing 0x%x to %d:0x%x\n", value, port, offset);\ -\ - NM_FIX_PORT;\ -\ - addr = (u##X *)(card->port[port - 1].ptr + offset);\ - func (value, addr);\ - return 0;\ -} - -DEFwritePortX (8, writeb) -DEFwritePortX (16, writew) -DEFwritePortX (32, writel) - -#define DEFreadPortX(X, func) \ -static inline u##X nm256_readPort##X (struct nm256_info *card,\ - int port, int offset)\ -{\ - u##X *addr;\ -\ - NM_FIX_PORT\ -\ - addr = (u##X *)(card->port[port - 1].ptr + offset);\ - return func(addr);\ -} - -DEFreadPortX (8, readb) -DEFreadPortX (16, readw) -DEFreadPortX (32, readl) - -static inline int -nm256_writeBuffer8 (struct nm256_info *card, u8 *src, int port, int offset, - int amt) -{ - NM_FIX_PORT; - memcpy_toio (card->port[port - 1].ptr + offset, src, amt); - return 0; -} - -static inline int -nm256_readBuffer8 (struct nm256_info *card, u8 *dst, int port, int offset, - int amt) -{ - NM_FIX_PORT; - memcpy_fromio (dst, card->port[port - 1].ptr + offset, amt); - return 0; -} - -/* Returns a non-zero value if we should use the coefficient cache. */ -extern int nm256_cachedCoefficients (struct nm256_info *card); - -#endif - -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ diff -Nru a/drivers/sound/nm256_audio.c b/drivers/sound/nm256_audio.c --- a/drivers/sound/nm256_audio.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1690 +0,0 @@ -/* - * Audio driver for the NeoMagic 256AV and 256ZX chipsets in native - * mode, with AC97 mixer support. - * - * Overall design and parts of this code stolen from vidc_*.c and - * skeleton.c. - * - * Yeah, there are a lot of magic constants in here. You tell ME what - * they are. I just get this stuff psychically, remember? - * - * This driver was written by someone who wishes to remain anonymous. - * It is in the public domain, so share and enjoy. Try to make a profit - * off of it; go on, I dare you. - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz - * Added some __init - * 19-04-2001 Marcus Meissner - * Ported to 2.4 PCI API. - */ - -#define __NO_VERSION__ -#include -#include -#include -#include -#include -#include "sound_config.h" -#include "nm256.h" -#include "nm256_coeff.h" - -int nm256_debug; -static int force_load; - -/* - * The size of the playback reserve. When the playback buffer has less - * than NM256_PLAY_WMARK_SIZE bytes to output, we request a new - * buffer. - */ -#define NM256_PLAY_WMARK_SIZE 512 - -static struct audio_driver nm256_audio_driver; - -static int nm256_grabInterrupt (struct nm256_info *card); -static int nm256_releaseInterrupt (struct nm256_info *card); -static void nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy); -static void nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy); -static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data); - -/* These belong in linux/pci.h. */ -#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 -#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 - -/* List of cards. */ -static struct nm256_info *nmcard_list; - -/* Release the mapped-in memory for CARD. */ -static void -nm256_release_ports (struct nm256_info *card) -{ - int x; - - for (x = 0; x < 2; x++) { - if (card->port[x].ptr != NULL) { - iounmap (card->port[x].ptr); - card->port[x].ptr = NULL; - } - } -} - -/* - * Map in the memory ports for CARD, if they aren't already mapped in - * and have been configured. If successful, a zero value is returned; - * otherwise any previously mapped-in areas are released and a non-zero - * value is returned. - * - * This is invoked twice, once for each port. Ideally it would only be - * called once, but we now need to map in the second port in order to - * check how much memory the card has on the 256ZX. - */ -static int -nm256_remap_ports (struct nm256_info *card) -{ - int x; - - for (x = 0; x < 2; x++) { - if (card->port[x].ptr == NULL && card->port[x].end_offset > 0) { - u32 physaddr - = card->port[x].physaddr + card->port[x].start_offset; - u32 size - = card->port[x].end_offset - card->port[x].start_offset; - - card->port[x].ptr = ioremap_nocache (physaddr, size); - - if (card->port[x].ptr == NULL) { - printk (KERN_ERR "NM256: Unable to remap port %d\n", x + 1); - nm256_release_ports (card); - return -1; - } - } - } - return 0; -} - -/* Locate the card in our list. */ -static struct nm256_info * -nm256_find_card (int dev) -{ - struct nm256_info *card; - - for (card = nmcard_list; card != NULL; card = card->next_card) - if (card->dev[0] == dev || card->dev[1] == dev) - return card; - - return NULL; -} - -/* - * Ditto, but find the card struct corresponding to the mixer device DEV - * instead. - */ -static struct nm256_info * -nm256_find_card_for_mixer (int dev) -{ - struct nm256_info *card; - - for (card = nmcard_list; card != NULL; card = card->next_card) - if (card->mixer_oss_dev == dev) - return card; - - return NULL; -} - -static int usecache; -static int buffertop; - -/* Check to see if we're using the bank of cached coefficients. */ -int -nm256_cachedCoefficients (struct nm256_info *card) -{ - return usecache; -} - -/* The actual rates supported by the card. */ -static int samplerates[9] = { - 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 -}; - -/* - * Set the card samplerate, word size and stereo mode to correspond to - * the settings in the CARD struct for the specified device in DEV. - * We keep two separate sets of information, one for each device; the - * hardware is not actually configured until a read or write is - * attempted. - */ - -int -nm256_setInfo (int dev, struct nm256_info *card) -{ - int x; - int w; - int targetrate; - - if (card->dev[0] == dev) - w = 0; - else if (card->dev[1] == dev) - w = 1; - else - return -ENODEV; - - targetrate = card->sinfo[w].samplerate; - - if ((card->sinfo[w].bits != 8 && card->sinfo[w].bits != 16) - || targetrate < samplerates[0] - || targetrate > samplerates[7]) - return -EINVAL; - - for (x = 0; x < 8; x++) - if (targetrate < ((samplerates[x] + samplerates[x + 1]) / 2)) - break; - - if (x < 8) { - u8 ratebits = ((x << 4) & NM_RATE_MASK); - if (card->sinfo[w].bits == 16) - ratebits |= NM_RATE_BITS_16; - if (card->sinfo[w].stereo) - ratebits |= NM_RATE_STEREO; - - card->sinfo[w].samplerate = samplerates[x]; - - - if (card->dev_for_play == dev && card->playing) { - if (nm256_debug) - printk (KERN_DEBUG "Setting play ratebits to 0x%x\n", - ratebits); - nm256_loadCoefficient (card, 0, x); - nm256_writePort8 (card, 2, - NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, - ratebits); - } - - if (card->dev_for_record == dev && card->recording) { - if (nm256_debug) - printk (KERN_DEBUG "Setting record ratebits to 0x%x\n", - ratebits); - nm256_loadCoefficient (card, 1, x); - nm256_writePort8 (card, 2, - NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, - ratebits); - } - return 0; - } - else - return -EINVAL; -} - -/* Start the play process going. */ -static void -startPlay (struct nm256_info *card) -{ - if (! card->playing) { - card->playing = 1; - if (nm256_grabInterrupt (card) == 0) { - nm256_setInfo (card->dev_for_play, card); - - /* Enable playback engine and interrupts. */ - nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG, - NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); - - /* Enable both channels. */ - nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG, 0x0); - } - } -} - -/* - * Request one chunk of AMT bytes from the recording device. When the - * operation is complete, the data will be copied into BUFFER and the - * function DMAbuf_inputintr will be invoked. - */ - -static void -nm256_startRecording (struct nm256_info *card, char *buffer, u32 amt) -{ - u32 endpos; - int enableEngine = 0; - u32 ringsize = card->recordBufferSize; - unsigned long flags; - - if (amt > (ringsize / 2)) { - /* - * Of course this won't actually work right, because the - * caller is going to assume we will give what we got asked - * for. - */ - printk (KERN_ERR "NM256: Read request too large: %d\n", amt); - amt = ringsize / 2; - } - - if (amt < 8) { - printk (KERN_ERR "NM256: Read request too small; %d\n", amt); - return; - } - - save_flags (flags); - cli (); - /* - * If we're not currently recording, set up the start and end registers - * for the recording engine. - */ - if (! card->recording) { - card->recording = 1; - if (nm256_grabInterrupt (card) == 0) { - card->curRecPos = 0; - nm256_setInfo (card->dev_for_record, card); - nm256_writePort32 (card, 2, NM_RBUFFER_START, card->abuf2); - nm256_writePort32 (card, 2, NM_RBUFFER_END, - card->abuf2 + ringsize); - - nm256_writePort32 (card, 2, NM_RBUFFER_CURRP, - card->abuf2 + card->curRecPos); - enableEngine = 1; - } - else { - /* Not sure what else to do here. */ - restore_flags (flags); - return; - } - } - - /* - * If we happen to go past the end of the buffer a bit (due to a - * delayed interrupt) it's OK. So might as well set the watermark - * right at the end of the data we want. - */ - endpos = card->abuf2 + ((card->curRecPos + amt) % ringsize); - - card->recBuf = buffer; - card->requestedRecAmt = amt; - nm256_writePort32 (card, 2, NM_RBUFFER_WMARK, endpos); - /* Enable recording engine and interrupts. */ - if (enableEngine) - nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG, - NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN); - - restore_flags (flags); -} - -/* Stop the play engine. */ -static void -stopPlay (struct nm256_info *card) -{ - /* Shut off sound from both channels. */ - nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG, - NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); - /* Disable play engine. */ - nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG, 0); - if (card->playing) { - nm256_releaseInterrupt (card); - - /* Reset the relevant state bits. */ - card->playing = 0; - card->curPlayPos = 0; - } -} - -/* Stop recording. */ -static void -stopRecord (struct nm256_info *card) -{ - /* Disable recording engine. */ - nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG, 0); - - if (card->recording) { - nm256_releaseInterrupt (card); - - card->recording = 0; - card->curRecPos = 0; - } -} - -/* - * Ring buffers, man. That's where the hip-hop, wild-n-wooly action's at. - * 1972? (Well, I suppose it was cheep-n-easy to implement.) - * - * Write AMT bytes of BUFFER to the playback ring buffer, and start the - * playback engine running. It will only accept up to 1/2 of the total - * size of the ring buffer. No check is made that we're about to overwrite - * the currently-playing sample. - */ - -static void -nm256_write_block (struct nm256_info *card, char *buffer, u32 amt) -{ - u32 ringsize = card->playbackBufferSize; - u32 endstop; - unsigned long flags; - - if (amt > (ringsize / 2)) { - printk (KERN_ERR "NM256: Write request too large: %d\n", amt); - amt = (ringsize / 2); - } - - if (amt < NM256_PLAY_WMARK_SIZE) { - printk (KERN_ERR "NM256: Write request too small: %d\n", amt); - return; - } - - card->curPlayPos %= ringsize; - - card->requested_amt = amt; - - save_flags (flags); - cli (); - - if ((card->curPlayPos + amt) >= ringsize) { - u32 rem = ringsize - card->curPlayPos; - - nm256_writeBuffer8 (card, buffer, 1, - card->abuf1 + card->curPlayPos, - rem); - if (amt > rem) - nm256_writeBuffer8 (card, buffer + rem, 1, card->abuf1, - amt - rem); - } - else - nm256_writeBuffer8 (card, buffer, 1, - card->abuf1 + card->curPlayPos, - amt); - - /* - * Setup the start-n-stop-n-limit registers, and start that engine - * goin'. - * - * Normally we just let it wrap around to avoid the click-click - * action scene. - */ - if (! card->playing) { - /* The PBUFFER_END register in this case points to one sample - before the end of the buffer. */ - int w = (card->dev_for_play == card->dev[0] ? 0 : 1); - int sampsize = (card->sinfo[w].bits == 16 ? 2 : 1); - - if (card->sinfo[w].stereo) - sampsize *= 2; - - /* Need to set the not-normally-changing-registers up. */ - nm256_writePort32 (card, 2, NM_PBUFFER_START, - card->abuf1 + card->curPlayPos); - nm256_writePort32 (card, 2, NM_PBUFFER_END, - card->abuf1 + ringsize - sampsize); - nm256_writePort32 (card, 2, NM_PBUFFER_CURRP, - card->abuf1 + card->curPlayPos); - } - endstop = (card->curPlayPos + amt - NM256_PLAY_WMARK_SIZE) % ringsize; - nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop); - - if (! card->playing) - startPlay (card); - - restore_flags (flags); -} - -/* We just got a card playback interrupt; process it. */ -static void -nm256_get_new_block (struct nm256_info *card) -{ - /* Check to see how much got played so far. */ - u32 amt = nm256_readPort32 (card, 2, NM_PBUFFER_CURRP) - card->abuf1; - - if (amt >= card->playbackBufferSize) { - printk (KERN_ERR "NM256: Sound playback pointer invalid!\n"); - amt = 0; - } - - if (amt < card->curPlayPos) - amt = (card->playbackBufferSize - card->curPlayPos) + amt; - else - amt -= card->curPlayPos; - - if (card->requested_amt > (amt + NM256_PLAY_WMARK_SIZE)) { - u32 endstop = - card->curPlayPos + card->requested_amt - NM256_PLAY_WMARK_SIZE; - nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop); - } - else { - card->curPlayPos += card->requested_amt; - /* Get a new block to write. This will eventually invoke - nm256_write_block () or stopPlay (). */ - DMAbuf_outputintr (card->dev_for_play, 1); - } -} - -/* Ultra cheez-whiz. But I'm too lazy to grep headers. */ -#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) - -/* - * Read the last-recorded block from the ring buffer, copy it into the - * saved buffer pointer, and invoke DMAuf_inputintr() with the recording - * device. - */ - -static void -nm256_read_block (struct nm256_info *card) -{ - /* Grab the current position of the recording pointer. */ - u32 currptr = nm256_readPort32 (card, 2, NM_RBUFFER_CURRP) - card->abuf2; - u32 amtToRead = card->requestedRecAmt; - u32 ringsize = card->recordBufferSize; - - if (currptr >= card->recordBufferSize) { - printk (KERN_ERR "NM256: Sound buffer record pointer invalid!\n"); - currptr = 0; - } - - /* - * This test is probably redundant; we shouldn't be here unless - * it's true. - */ - if (card->recording) { - /* If we wrapped around, copy everything from the start of our - recording buffer to the end of the buffer. */ - if (currptr < card->curRecPos) { - u32 amt = MIN (ringsize - card->curRecPos, amtToRead); - - nm256_readBuffer8 (card, card->recBuf, 1, - card->abuf2 + card->curRecPos, - amt); - amtToRead -= amt; - card->curRecPos += amt; - card->recBuf += amt; - if (card->curRecPos == ringsize) - card->curRecPos = 0; - } - - if ((card->curRecPos < currptr) && (amtToRead > 0)) { - u32 amt = MIN (currptr - card->curRecPos, amtToRead); - nm256_readBuffer8 (card, card->recBuf, 1, - card->abuf2 + card->curRecPos, amt); - card->curRecPos = ((card->curRecPos + amt) % ringsize); - } - card->recBuf = NULL; - card->requestedRecAmt = 0; - DMAbuf_inputintr (card->dev_for_record); - } -} -#undef MIN - -/* - * Initialize the hardware. - */ -static void -nm256_initHw (struct nm256_info *card) -{ - /* Reset everything. */ - nm256_writePort8 (card, 2, 0x0, 0x11); - nm256_writePort16 (card, 2, 0x214, 0); - - stopRecord (card); - stopPlay (card); -} - -/* - * Handle a potential interrupt for the device referred to by DEV_ID. - * - * I don't like the cut-n-paste job here either between the two routines, - * but there are sufficient differences between the two interrupt handlers - * that parameterizing it isn't all that great either. (Could use a macro, - * I suppose...yucky bleah.) - */ - -static void -nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy) -{ - struct nm256_info *card = (struct nm256_info *)dev_id; - u16 status; - static int badintrcount = 0; - - if ((card == NULL) || (card->magsig != NM_MAGIC_SIG)) { - printk (KERN_ERR "NM256: Bad card pointer\n"); - return; - } - - status = nm256_readPort16 (card, 2, NM_INT_REG); - - /* Not ours. */ - if (status == 0) { - if (badintrcount++ > 1000) { - /* - * I'm not sure if the best thing is to stop the card from - * playing or just release the interrupt (after all, we're in - * a bad situation, so doing fancy stuff may not be such a good - * idea). - * - * I worry about the card engine continuing to play noise - * over and over, however--that could become a very - * obnoxious problem. And we know that when this usually - * happens things are fairly safe, it just means the user's - * inserted a PCMCIA card and someone's spamming us with IRQ 9s. - */ - - if (card->playing) - stopPlay (card); - if (card->recording) - stopRecord (card); - badintrcount = 0; - } - return; - } - - badintrcount = 0; - - /* Rather boring; check for individual interrupts and process them. */ - - if (status & NM_PLAYBACK_INT) { - status &= ~NM_PLAYBACK_INT; - NM_ACK_INT (card, NM_PLAYBACK_INT); - - if (card->playing) - nm256_get_new_block (card); - } - - if (status & NM_RECORD_INT) { - status &= ~NM_RECORD_INT; - NM_ACK_INT (card, NM_RECORD_INT); - - if (card->recording) - nm256_read_block (card); - } - - if (status & NM_MISC_INT_1) { - u8 cbyte; - - status &= ~NM_MISC_INT_1; - printk (KERN_ERR "NM256: Got misc interrupt #1\n"); - NM_ACK_INT (card, NM_MISC_INT_1); - nm256_writePort16 (card, 2, NM_INT_REG, 0x8000); - cbyte = nm256_readPort8 (card, 2, 0x400); - nm256_writePort8 (card, 2, 0x400, cbyte | 2); - } - - if (status & NM_MISC_INT_2) { - u8 cbyte; - - status &= ~NM_MISC_INT_2; - printk (KERN_ERR "NM256: Got misc interrupt #2\n"); - NM_ACK_INT (card, NM_MISC_INT_2); - cbyte = nm256_readPort8 (card, 2, 0x400); - nm256_writePort8 (card, 2, 0x400, cbyte & ~2); - } - - /* Unknown interrupt. */ - if (status) { - printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n", - status); - /* Pray. */ - NM_ACK_INT (card, status); - } -} - -/* - * Handle a potential interrupt for the device referred to by DEV_ID. - * This handler is for the 256ZX, and is very similar to the non-ZX - * routine. - */ - -static void -nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy) -{ - struct nm256_info *card = (struct nm256_info *)dev_id; - u32 status; - static int badintrcount = 0; - - if ((card == NULL) || (card->magsig != NM_MAGIC_SIG)) { - printk (KERN_ERR "NM256: Bad card pointer\n"); - return; - } - - status = nm256_readPort32 (card, 2, NM_INT_REG); - - /* Not ours. */ - if (status == 0) { - if (badintrcount++ > 1000) { - printk (KERN_ERR "NM256: Releasing interrupt, over 1000 invalid interrupts\n"); - /* - * I'm not sure if the best thing is to stop the card from - * playing or just release the interrupt (after all, we're in - * a bad situation, so doing fancy stuff may not be such a good - * idea). - * - * I worry about the card engine continuing to play noise - * over and over, however--that could become a very - * obnoxious problem. And we know that when this usually - * happens things are fairly safe, it just means the user's - * inserted a PCMCIA card and someone's spamming us with - * IRQ 9s. - */ - - if (card->playing) - stopPlay (card); - if (card->recording) - stopRecord (card); - badintrcount = 0; - } - return; - } - - badintrcount = 0; - - /* Rather boring; check for individual interrupts and process them. */ - - if (status & NM2_PLAYBACK_INT) { - status &= ~NM2_PLAYBACK_INT; - NM2_ACK_INT (card, NM2_PLAYBACK_INT); - - if (card->playing) - nm256_get_new_block (card); - } - - if (status & NM2_RECORD_INT) { - status &= ~NM2_RECORD_INT; - NM2_ACK_INT (card, NM2_RECORD_INT); - - if (card->recording) - nm256_read_block (card); - } - - if (status & NM2_MISC_INT_1) { - u8 cbyte; - - status &= ~NM2_MISC_INT_1; - printk (KERN_ERR "NM256: Got misc interrupt #1\n"); - NM2_ACK_INT (card, NM2_MISC_INT_1); - cbyte = nm256_readPort8 (card, 2, 0x400); - nm256_writePort8 (card, 2, 0x400, cbyte | 2); - } - - if (status & NM2_MISC_INT_2) { - u8 cbyte; - - status &= ~NM2_MISC_INT_2; - printk (KERN_ERR "NM256: Got misc interrupt #2\n"); - NM2_ACK_INT (card, NM2_MISC_INT_2); - cbyte = nm256_readPort8 (card, 2, 0x400); - nm256_writePort8 (card, 2, 0x400, cbyte & ~2); - } - - /* Unknown interrupt. */ - if (status) { - printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n", - status); - /* Pray. */ - NM2_ACK_INT (card, status); - } -} - -/* - * Request our interrupt. - */ -static int -nm256_grabInterrupt (struct nm256_info *card) -{ - if (card->has_irq++ == 0) { - if (request_irq (card->irq, card->introutine, SA_SHIRQ, - "NM256_audio", card) < 0) { - printk (KERN_ERR "NM256: can't obtain IRQ %d\n", card->irq); - return -1; - } - } - return 0; -} - -/* - * Release our interrupt. - */ -static int -nm256_releaseInterrupt (struct nm256_info *card) -{ - if (card->has_irq <= 0) { - printk (KERN_ERR "nm256: too many calls to releaseInterrupt\n"); - return -1; - } - card->has_irq--; - if (card->has_irq == 0) { - free_irq (card->irq, card); - } - return 0; -} - -/* - * Waits for the mixer to become ready to be written; returns a zero value - * if it timed out. - */ - -static int -nm256_isReady (struct ac97_hwint *dev) -{ - struct nm256_info *card = (struct nm256_info *)dev->driver_private; - int t2 = 10; - u32 testaddr; - u16 testb; - int done = 0; - - if (card->magsig != NM_MAGIC_SIG) { - printk (KERN_ERR "NM256: Bad magic signature in isReady!\n"); - return 0; - } - - testaddr = card->mixer_status_offset; - testb = card->mixer_status_mask; - - /* - * Loop around waiting for the mixer to become ready. - */ - while (! done && t2-- > 0) { - if ((nm256_readPort16 (card, 2, testaddr) & testb) == 0) - done = 1; - else - udelay (100); - } - return done; -} - -/* - * Return the contents of the AC97 mixer register REG. Returns a positive - * value if successful, or a negative error code. - */ -static int -nm256_readAC97Reg (struct ac97_hwint *dev, u8 reg) -{ - struct nm256_info *card = (struct nm256_info *)dev->driver_private; - - if (card->magsig != NM_MAGIC_SIG) { - printk (KERN_ERR "NM256: Bad magic signature in readAC97Reg!\n"); - return -EINVAL; - } - - if (reg < 128) { - int res; - - nm256_isReady (dev); - res = nm256_readPort16 (card, 2, card->mixer + reg); - /* Magic delay. Bleah yucky. */ - udelay (1000); - return res; - } - else - return -EINVAL; -} - -/* - * Writes VALUE to AC97 mixer register REG. Returns 0 if successful, or - * a negative error code. - */ -static int -nm256_writeAC97Reg (struct ac97_hwint *dev, u8 reg, u16 value) -{ - unsigned long flags; - int tries = 2; - int done = 0; - u32 base; - - struct nm256_info *card = (struct nm256_info *)dev->driver_private; - - if (card->magsig != NM_MAGIC_SIG) { - printk (KERN_ERR "NM256: Bad magic signature in writeAC97Reg!\n"); - return -EINVAL; - } - - base = card->mixer; - - save_flags (flags); - cli (); - - nm256_isReady (dev); - - /* Wait for the write to take, too. */ - while ((tries-- > 0) && !done) { - nm256_writePort16 (card, 2, base + reg, value); - if (nm256_isReady (dev)) { - done = 1; - break; - } - - } - - restore_flags (flags); - udelay (1000); - - return ! done; -} - -/* - * Initial register values to be written to the AC97 mixer. - * While most of these are identical to the reset values, we do this - * so that we have most of the register contents cached--this avoids - * reading from the mixer directly (which seems to be problematic, - * probably due to ignorance). - */ -struct initialValues -{ - unsigned short port; - unsigned short value; -}; - -static struct initialValues nm256_ac97_initial_values[] = -{ - { AC97_MASTER_VOL_STEREO, 0x8000 }, - { AC97_HEADPHONE_VOL, 0x8000 }, - { AC97_MASTER_VOL_MONO, 0x0000 }, - { AC97_PCBEEP_VOL, 0x0000 }, - { AC97_PHONE_VOL, 0x0008 }, - { AC97_MIC_VOL, 0x8000 }, - { AC97_LINEIN_VOL, 0x8808 }, - { AC97_CD_VOL, 0x8808 }, - { AC97_VIDEO_VOL, 0x8808 }, - { AC97_AUX_VOL, 0x8808 }, - { AC97_PCMOUT_VOL, 0x0808 }, - { AC97_RECORD_SELECT, 0x0000 }, - { AC97_RECORD_GAIN, 0x0B0B }, - { AC97_GENERAL_PURPOSE, 0x0000 }, - { 0xffff, 0xffff } -}; - -/* Initialize the AC97 into a known state. */ -static int -nm256_resetAC97 (struct ac97_hwint *dev) -{ - struct nm256_info *card = (struct nm256_info *)dev->driver_private; - int x; - - if (card->magsig != NM_MAGIC_SIG) { - printk (KERN_ERR "NM256: Bad magic signature in resetAC97!\n"); - return -EINVAL; - } - - /* Reset the mixer. 'Tis magic! */ - nm256_writePort8 (card, 2, 0x6c0, 1); - nm256_writePort8 (card, 2, 0x6cc, 0x87); - nm256_writePort8 (card, 2, 0x6cc, 0x80); - nm256_writePort8 (card, 2, 0x6cc, 0x0); - - if (! card->mixer_values_init) { - for (x = 0; nm256_ac97_initial_values[x].port != 0xffff; x++) { - ac97_put_register (dev, - nm256_ac97_initial_values[x].port, - nm256_ac97_initial_values[x].value); - card->mixer_values_init = 1; - } - } - - return 0; -} - -/* - * We don't do anything particularly special here; it just passes the - * mixer ioctl to the AC97 driver. - */ -static int -nm256_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) -{ - struct nm256_info *card = nm256_find_card_for_mixer (dev); - if (card != NULL) - return ac97_mixer_ioctl (&(card->mdev), cmd, arg); - else - return -ENODEV; -} - -static struct mixer_operations nm256_mixer_operations = { - owner: THIS_MODULE, - id: "NeoMagic", - name: "NM256AC97Mixer", - ioctl: nm256_default_mixer_ioctl -}; - -/* - * Default settings for the OSS mixer. These are set last, after the - * mixer is initialized. - * - * I "love" C sometimes. Got braces? - */ -static struct ac97_mixer_value_list mixer_defaults[] = { - { SOUND_MIXER_VOLUME, { { 85, 85 } } }, - { SOUND_MIXER_SPEAKER, { { 100 } } }, - { SOUND_MIXER_PCM, { { 65, 65 } } }, - { SOUND_MIXER_CD, { { 65, 65 } } }, - { -1, { { 0, 0 } } } -}; - - -/* Installs the AC97 mixer into CARD. */ -static int __init -nm256_install_mixer (struct nm256_info *card) -{ - int mixer; - - card->mdev.reset_device = nm256_resetAC97; - card->mdev.read_reg = nm256_readAC97Reg; - card->mdev.write_reg = nm256_writeAC97Reg; - card->mdev.driver_private = (void *)card; - - if (ac97_init (&(card->mdev))) - return -1; - - mixer = sound_alloc_mixerdev(); - if (num_mixers >= MAX_MIXER_DEV) { - printk ("NM256 mixer: Unable to alloc mixerdev\n"); - return -1; - } - - mixer_devs[mixer] = &nm256_mixer_operations; - card->mixer_oss_dev = mixer; - - /* Some reasonable default values. */ - ac97_set_values (&(card->mdev), mixer_defaults); - - printk(KERN_INFO "Initialized AC97 mixer\n"); - return 0; -} - -/* Perform a full reset on the hardware; this is invoked when an APM - resume event occurs. */ -static void -nm256_full_reset (struct nm256_info *card) -{ - nm256_initHw (card); - ac97_reset (&(card->mdev)); -} - -/* - * See if the signature left by the NM256 BIOS is intact; if so, we use - * the associated address as the end of our audio buffer in the video - * RAM. - */ - -static void __init -nm256_peek_for_sig (struct nm256_info *card) -{ - u32 port1offset - = card->port[0].physaddr + card->port[0].end_offset - 0x0400; - /* The signature is located 1K below the end of video RAM. */ - char *temp = ioremap_nocache (port1offset, 16); - /* Default buffer end is 5120 bytes below the top of RAM. */ - u32 default_value = card->port[0].end_offset - 0x1400; - u32 sig; - - /* Install the default value first, so we don't have to repeatedly - do it if there is a problem. */ - card->port[0].end_offset = default_value; - - if (temp == NULL) { - printk (KERN_ERR "NM256: Unable to scan for card signature in video RAM\n"); - return; - } - sig = readl (temp); - if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { - u32 pointer = readl (temp + 4); - - /* - * If it's obviously invalid, don't use it (the port already has a - * suitable default value set). - */ - if (pointer != 0xffffffff) - card->port[0].end_offset = pointer; - - printk (KERN_INFO "NM256: Found card signature in video RAM: 0x%x\n", - pointer); - } - - iounmap (temp); -} - -/* - * Install a driver for the PCI device referenced by PCIDEV. - * VERSTR is a human-readable version string. - */ - -static int __init -nm256_install(struct pci_dev *pcidev, enum nm256rev rev, char *verstr) -{ - struct nm256_info *card; - struct pm_dev *pmdev; - int x; - - if (pci_enable_device(pcidev)) - return 0; - - card = kmalloc (sizeof (struct nm256_info), GFP_KERNEL); - if (card == NULL) { - printk (KERN_ERR "NM256: out of memory!\n"); - return 0; - } - - card->magsig = NM_MAGIC_SIG; - card->playing = 0; - card->recording = 0; - card->rev = rev; - - /* Init the memory port info. */ - for (x = 0; x < 2; x++) { - card->port[x].physaddr = pci_resource_start (pcidev, x); - card->port[x].ptr = NULL; - card->port[x].start_offset = 0; - card->port[x].end_offset = 0; - } - - /* Port 2 is easy. */ - card->port[1].start_offset = 0; - card->port[1].end_offset = NM_PORT2_SIZE; - - /* Yuck. But we have to map in port 2 so we can check how much RAM the - card has. */ - if (nm256_remap_ports (card)) { - kfree (card); - return 0; - } - - /* - * The NM256 has two memory ports. The first port is nothing - * more than a chunk of video RAM, which is used as the I/O ring - * buffer. The second port has the actual juicy stuff (like the - * mixer and the playback engine control registers). - */ - - if (card->rev == REV_NM256AV) { - /* Ok, try to see if this is a non-AC97 version of the hardware. */ - int pval = nm256_readPort16 (card, 2, NM_MIXER_PRESENCE); - if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { - if (! force_load) { - 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 "\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"); - printk (KERN_ERR " IRQ and/or DMA for the sound card, this is *not* the correct\n"); - printk (KERN_ERR " driver to use.)\n"); - nm256_release_ports (card); - kfree (card); - return 0; - } - else { - printk (KERN_INFO "NM256: Forcing driver load as per user request.\n"); - } - } - else { - /* printk (KERN_INFO "NM256: Congratulations. You're not running Eunice.\n")*/; - } - card->port[0].end_offset = 2560 * 1024; - card->introutine = nm256_interrupt; - card->mixer_status_offset = NM_MIXER_STATUS_OFFSET; - card->mixer_status_mask = NM_MIXER_READY_MASK; - } - else { - /* Not sure if there is any relevant detect for the ZX or not. */ - if (nm256_readPort8 (card, 2, 0xa0b) != 0) - card->port[0].end_offset = 6144 * 1024; - else - card->port[0].end_offset = 4096 * 1024; - - card->introutine = nm256_interrupt_zx; - card->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; - card->mixer_status_mask = NM2_MIXER_READY_MASK; - } - - if (buffertop >= 98304 && buffertop < card->port[0].end_offset) - card->port[0].end_offset = buffertop; - else - nm256_peek_for_sig (card); - - card->port[0].start_offset = card->port[0].end_offset - 98304; - - printk (KERN_INFO "NM256: Mapping port 1 from 0x%x - 0x%x\n", - card->port[0].start_offset, card->port[0].end_offset); - - if (nm256_remap_ports (card)) { - kfree (card); - return 0; - } - - /* See if we can get the interrupt. */ - - card->irq = pcidev->irq; - card->has_irq = 0; - - if (nm256_grabInterrupt (card) != 0) { - nm256_release_ports (card); - kfree (card); - return 0; - } - - nm256_releaseInterrupt (card); - - /* - * Init the board. - */ - - card->playbackBufferSize = 16384; - card->recordBufferSize = 16384; - - card->coeffBuf = card->port[0].end_offset - NM_MAX_COEFFICIENT; - card->abuf2 = card->coeffBuf - card->recordBufferSize; - card->abuf1 = card->abuf2 - card->playbackBufferSize; - card->allCoeffBuf = card->abuf2 - (NM_TOTAL_COEFF_COUNT * 4); - - /* Fixed setting. */ - card->mixer = NM_MIXER_OFFSET; - card->mixer_values_init = 0; - - card->is_open_play = 0; - card->is_open_record = 0; - - card->coeffsCurrent = 0; - - card->opencnt[0] = 0; card->opencnt[1] = 0; - - /* Reasonable default settings, but largely unnecessary. */ - for (x = 0; x < 2; x++) { - card->sinfo[x].bits = 8; - card->sinfo[x].stereo = 0; - card->sinfo[x].samplerate = 8000; - } - - nm256_initHw (card); - - for (x = 0; x < 2; x++) { - if ((card->dev[x] = - sound_install_audiodrv(AUDIO_DRIVER_VERSION, - "NM256", &nm256_audio_driver, - sizeof(struct audio_driver), - DMA_NODMA, AFMT_U8 | AFMT_S16_LE, - NULL, -1, -1)) >= 0) { - /* 1K minimum buffer size. */ - audio_devs[card->dev[x]]->min_fragment = 10; - /* Maximum of 8K buffer size. */ - audio_devs[card->dev[x]]->max_fragment = 13; - } - else { - printk(KERN_ERR "NM256: Too many PCM devices available\n"); - nm256_release_ports (card); - kfree (card); - return 0; - } - } - - pci_set_drvdata(pcidev,card); - - /* Insert the card in the list. */ - card->next_card = nmcard_list; - nmcard_list = card; - - printk(KERN_INFO "Initialized NeoMagic %s audio in PCI native mode\n", - verstr); - - /* - * And our mixer. (We should allow support for other mixers, maybe.) - */ - - nm256_install_mixer (card); - - pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), handle_pm_event); - if (pmdev) - pmdev->data = card; - - return 1; -} - - -/* - * PM event handler, so the card is properly reinitialized after a power - * event. - */ -static int -handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) -{ - struct nm256_info *crd = (struct nm256_info*) dev->data; - if (crd) { - switch (rqst) { - case PM_SUSPEND: - break; - case PM_RESUME: - { - int playing = crd->playing; - nm256_full_reset (crd); - /* - * A little ugly, but that's ok; pretend the - * block we were playing is done. - */ - if (playing) - DMAbuf_outputintr (crd->dev_for_play, 1); - } - break; - } - } - return 0; -} - -static int __devinit -nm256_probe(struct pci_dev *pcidev,const struct pci_device_id *pciid) -{ - if (pcidev->device == PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO) - return nm256_install(pcidev, REV_NM256AV, "256AV"); - if (pcidev->device == PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO) - return nm256_install(pcidev, REV_NM256ZX, "256ZX"); - return -1; /* should not come here ... */ -} - -static void __devinit -nm256_remove(struct pci_dev *pcidev) { - struct nm256_info *xcard = pci_get_drvdata(pcidev); - struct nm256_info *card,*next_card = NULL; - - for (card = nmcard_list; card != NULL; card = next_card) { - next_card = card->next_card; - if (card == xcard) { - stopPlay (card); - stopRecord (card); - if (card->has_irq) - free_irq (card->irq, card); - nm256_release_ports (card); - sound_unload_mixerdev (card->mixer_oss_dev); - sound_unload_audiodev (card->dev[0]); - sound_unload_audiodev (card->dev[1]); - kfree (card); - break; - } - } - if (nmcard_list == card) - nmcard_list = next_card; -} - -/* - * Open the device - * - * DEV - device - * MODE - mode to open device (logical OR of OPEN_READ and OPEN_WRITE) - * - * Called when opening the DMAbuf (dmabuf.c:259) - */ -static int -nm256_audio_open(int dev, int mode) -{ - struct nm256_info *card = nm256_find_card (dev); - int w; - - if (card == NULL) - return -ENODEV; - - if (card->dev[0] == dev) - w = 0; - else if (card->dev[1] == dev) - w = 1; - else - return -ENODEV; - - if (card->opencnt[w] > 0) - return -EBUSY; - - /* No bits set? Huh? */ - if (! ((mode & OPEN_READ) || (mode & OPEN_WRITE))) - return -EIO; - - /* - * If it's open for both read and write, and the card's currently - * being read or written to, then do the opposite of what has - * already been done. Otherwise, don't specify any mode until the - * user actually tries to do I/O. (Some programs open the device - * for both read and write, but only actually do reading or writing.) - */ - - if ((mode & OPEN_WRITE) && (mode & OPEN_READ)) { - if (card->is_open_play) - mode = OPEN_WRITE; - else if (card->is_open_record) - mode = OPEN_READ; - else mode = 0; - } - - if (mode & OPEN_WRITE) { - if (card->is_open_play == 0) { - card->dev_for_play = dev; - card->is_open_play = 1; - } - else - return -EBUSY; - } - - if (mode & OPEN_READ) { - if (card->is_open_record == 0) { - card->dev_for_record = dev; - card->is_open_record = 1; - } - else - return -EBUSY; - } - - card->opencnt[w]++; - return 0; -} - -/* - * Close the device - * - * DEV - device - * - * Called when closing the DMAbuf (dmabuf.c:477) - * after halt_xfer - */ -static void -nm256_audio_close(int dev) -{ - struct nm256_info *card = nm256_find_card (dev); - - if (card != NULL) { - int w; - - if (card->dev[0] == dev) - w = 0; - else if (card->dev[1] == dev) - w = 1; - else - return; - - card->opencnt[w]--; - if (card->opencnt[w] <= 0) { - card->opencnt[w] = 0; - - if (card->dev_for_play == dev) { - stopPlay (card); - card->is_open_play = 0; - card->dev_for_play = -1; - } - - if (card->dev_for_record == dev) { - stopRecord (card); - card->is_open_record = 0; - card->dev_for_record = -1; - } - } - } -} - -/* Standard ioctl handler. */ -static int -nm256_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int ret; - u32 oldinfo; - int w; - - struct nm256_info *card = nm256_find_card (dev); - - if (card == NULL) - return -ENODEV; - - if (dev == card->dev[0]) - w = 0; - else - w = 1; - - /* - * The code here is messy. There are probably better ways to do - * it. (It should be possible to handle it the same way the AC97 mixer - * is done.) - */ - switch (cmd) - { - case SOUND_PCM_WRITE_RATE: - if (get_user(ret, (int *) arg)) - return -EFAULT; - - if (ret != 0) { - oldinfo = card->sinfo[w].samplerate; - card->sinfo[w].samplerate = ret; - ret = nm256_setInfo(dev, card); - if (ret != 0) - card->sinfo[w].samplerate = oldinfo; - } - if (ret == 0) - ret = card->sinfo[w].samplerate; - break; - - case SOUND_PCM_READ_RATE: - ret = card->sinfo[w].samplerate; - break; - - case SNDCTL_DSP_STEREO: - if (get_user(ret, (int *) arg)) - return -EFAULT; - - card->sinfo[w].stereo = ret ? 1 : 0; - ret = nm256_setInfo (dev, card); - if (ret == 0) - ret = card->sinfo[w].stereo; - - break; - - case SOUND_PCM_WRITE_CHANNELS: - if (get_user(ret, (int *) arg)) - return -EFAULT; - - if (ret < 1 || ret > 3) - ret = card->sinfo[w].stereo + 1; - else { - card->sinfo[w].stereo = ret - 1; - ret = nm256_setInfo (dev, card); - if (ret == 0) - ret = card->sinfo[w].stereo + 1; - } - break; - - case SOUND_PCM_READ_CHANNELS: - ret = card->sinfo[w].stereo + 1; - break; - - case SNDCTL_DSP_SETFMT: - if (get_user(ret, (int *) arg)) - return -EFAULT; - - if (ret != 0) { - oldinfo = card->sinfo[w].bits; - card->sinfo[w].bits = ret; - ret = nm256_setInfo (dev, card); - if (ret != 0) - card->sinfo[w].bits = oldinfo; - } - if (ret == 0) - ret = card->sinfo[w].bits; - break; - - case SOUND_PCM_READ_BITS: - ret = card->sinfo[w].bits; - break; - - default: - return -EINVAL; - } - return put_user(ret, (int *) arg); -} - -/* - * Given the sound device DEV and an associated physical buffer PHYSBUF, - * return a pointer to the actual buffer in kernel space. - * - * This routine should exist as part of the soundcore routines. - */ - -static char * -nm256_getDMAbuffer (int dev, unsigned long physbuf) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_out; - char *dma_start = - (char *)(physbuf - (unsigned long)dmap->raw_buf_phys - + (unsigned long)dmap->raw_buf); - - return dma_start; -} - - -/* - * Output a block to sound device - * - * dev - device number - * buf - physical address of buffer - * total_count - total byte count in buffer - * intrflag - set if this has been called from an interrupt - * (via DMAbuf_outputintr) - * restart_dma - set if engine needs to be re-initialised - * - * Called when: - * 1. Starting output (dmabuf.c:1327) - * 2. (dmabuf.c:1504) - * 3. A new buffer needs to be sent to the device (dmabuf.c:1579) - */ -static void -nm256_audio_output_block(int dev, unsigned long physbuf, - int total_count, int intrflag) -{ - struct nm256_info *card = nm256_find_card (dev); - - if (card != NULL) { - char *dma_buf = nm256_getDMAbuffer (dev, physbuf); - card->is_open_play = 1; - card->dev_for_play = dev; - nm256_write_block (card, dma_buf, total_count); - } -} - -/* Ditto, but do recording instead. */ -static void -nm256_audio_start_input(int dev, unsigned long physbuf, int count, - int intrflag) -{ - struct nm256_info *card = nm256_find_card (dev); - - if (card != NULL) { - char *dma_buf = nm256_getDMAbuffer (dev, physbuf); - card->is_open_record = 1; - card->dev_for_record = dev; - nm256_startRecording (card, dma_buf, count); - } -} - -/* - * Prepare for inputting samples to DEV. - * Each requested buffer will be BSIZE byes long, with a total of - * BCOUNT buffers. - */ - -static int -nm256_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - struct nm256_info *card = nm256_find_card (dev); - - if (card == NULL) - return -ENODEV; - - if (card->is_open_record && card->dev_for_record != dev) - return -EBUSY; - - audio_devs[dev]->dmap_in->flags |= DMA_NODMA; - return 0; -} - -/* - * Prepare for outputting samples to `dev' - * - * Each buffer that will be passed will be `bsize' bytes long, - * with a total of `bcount' buffers. - * - * Called when: - * 1. A trigger enables audio output (dmabuf.c:978) - * 2. We get a write buffer without dma_mode setup (dmabuf.c:1152) - * 3. We restart a transfer (dmabuf.c:1324) - */ - -static int -nm256_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - struct nm256_info *card = nm256_find_card (dev); - - if (card == NULL) - return -ENODEV; - - if (card->is_open_play && card->dev_for_play != dev) - return -EBUSY; - - audio_devs[dev]->dmap_out->flags |= DMA_NODMA; - return 0; -} - -/* Stop the current operations associated with DEV. */ -static void -nm256_audio_reset(int dev) -{ - struct nm256_info *card = nm256_find_card (dev); - - if (card != NULL) { - if (card->dev_for_play == dev) - stopPlay (card); - if (card->dev_for_record == dev) - stopRecord (card); - } -} - -static int -nm256_audio_local_qlen(int dev) -{ - return 0; -} - -static struct audio_driver nm256_audio_driver = -{ - owner: THIS_MODULE, - open: nm256_audio_open, - close: nm256_audio_close, - output_block: nm256_audio_output_block, - start_input: nm256_audio_start_input, - ioctl: nm256_audio_ioctl, - prepare_for_input: nm256_audio_prepare_for_input, - prepare_for_output:nm256_audio_prepare_for_output, - halt_io: nm256_audio_reset, - local_qlen: nm256_audio_local_qlen, -}; - -static struct pci_device_id nm256_pci_tbl[] __devinitdata = { - {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0}, - {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0}, - {0,} -}; -MODULE_DEVICE_TABLE(pci, nm256_pci_tbl); -MODULE_LICENSE("GPL"); - - -struct pci_driver nm256_pci_driver = { - name:"nm256_audio", - id_table:nm256_pci_tbl, - probe:nm256_probe, - remove:nm256_remove, -}; - -MODULE_PARM (usecache, "i"); -MODULE_PARM (buffertop, "i"); -MODULE_PARM (nm256_debug, "i"); -MODULE_PARM (force_load, "i"); - -static int __init do_init_nm256(void) -{ - printk (KERN_INFO "NeoMagic 256AV/256ZX audio driver, version 1.1p\n"); - return pci_module_init(&nm256_pci_driver); -} - -static void __exit cleanup_nm256 (void) -{ - pci_unregister_driver(&nm256_pci_driver); - pm_unregister_all (&handle_pm_event); -} - -module_init(do_init_nm256); -module_exit(cleanup_nm256); - -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ diff -Nru a/drivers/sound/nm256_coeff.h b/drivers/sound/nm256_coeff.h --- a/drivers/sound/nm256_coeff.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,4697 +0,0 @@ -#ifndef NM256_COEFF_H -#define NM256_COEFF_H - -#define NM_TOTAL_COEFF_COUNT 0x3158 - -static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { - 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21, - 0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01, - 0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD, - 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, - 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, - 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFD, 0xFF, - 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, - 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, - 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x02, 0x00, 0x05, - 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, - 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, - 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, - 0x60, 0x00, 0xA4, 0xFF, 0xDE, 0xFF, 0xB5, 0x01, 0x5E, 0xF9, 0xE9, - 0x45, 0x62, 0x11, 0x87, 0xF7, 0x21, 0x05, 0xEF, 0xFC, 0xA5, 0x01, - 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, - 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, - 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, - 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, - 0xCA, 0x01, 0x95, 0xFC, 0xEA, 0x05, 0xBB, 0xF5, 0x25, 0x17, 0x3C, - 0x43, 0x8D, 0xF6, 0x43, 0x03, 0xF5, 0xFE, 0x26, 0x00, 0x20, 0x00, - 0xE2, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, - 0x01, 0x4C, 0xFC, 0x26, 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, - 0x8F, 0xF1, 0xCA, 0x06, 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, - 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD5, 0xFF, 0xBC, 0x00, - 0xF0, 0xFD, 0xEC, 0x04, 0xD9, 0xF3, 0xB1, 0x3E, 0xCD, 0x1E, 0xC1, - 0xF3, 0xAF, 0x06, 0x49, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, - 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, - 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, - 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, - 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0x98, 0x01, 0x0D, 0xFD, - 0xE0, 0x04, 0x14, 0xF8, 0xC3, 0x0F, 0x89, 0x46, 0x4C, 0xFA, 0x38, - 0x01, 0x25, 0x00, 0x7D, 0xFF, 0x73, 0x00, 0xC2, 0xFF, 0x0F, 0x00, - 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, - 0x07, 0x84, 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, - 0x41, 0xFD, 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0xFF, - 0xFF, 0x15, 0x00, 0x97, 0xFF, 0x37, 0x01, 0x22, 0xFD, 0x23, 0x06, - 0x2F, 0xF2, 0x11, 0x39, 0x7B, 0x26, 0x50, 0xF2, 0x1B, 0x07, 0x32, - 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, - 0xC8, 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, - 0xF9, 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, - 0xA2, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, - 0x00, 0x6A, 0xFF, 0x53, 0x01, 0xA6, 0xFD, 0xA6, 0x03, 0xA1, 0xFA, - 0xDE, 0x08, 0x76, 0x48, 0x0C, 0xFF, 0xDE, 0xFE, 0x73, 0x01, 0xC9, - 0xFE, 0xCA, 0x00, 0xA0, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, - 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, - 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, - 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x68, - 0xFF, 0x93, 0x01, 0x92, 0xFC, 0xE2, 0x06, 0x83, 0xF1, 0x8C, 0x32, - 0xED, 0x2D, 0x90, 0xF1, 0x1E, 0x07, 0x57, 0xFC, 0xBD, 0x01, 0x51, - 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, - 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, - 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, - 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, 0x03, 0x01, 0x53, - 0xFE, 0x53, 0x02, 0x39, 0xFD, 0xA9, 0x02, 0xF2, 0x48, 0xB9, 0x04, - 0x54, 0xFC, 0xCA, 0x02, 0x16, 0xFE, 0x20, 0x01, 0x7F, 0xFF, 0x20, - 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC3, 0x01, - 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, 0x43, 0x20, - 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, 0xDD, 0xFF, - 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCD, 0x01, 0x43, - 0xFC, 0x2A, 0x07, 0xBC, 0xF1, 0x64, 0x2B, 0xE3, 0x34, 0xA3, 0xF1, - 0xAE, 0x06, 0xBD, 0xFC, 0x77, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, - 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, 0xFD, - 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, 0xC8, - 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, - 0x14, 0x00, 0xAC, 0xFF, 0xAC, 0x00, 0x08, 0xFF, 0xFD, 0x00, 0xB5, - 0xFF, 0x4B, 0xFD, 0xF4, 0x47, 0x30, 0x0B, 0xBC, 0xF9, 0x17, 0x04, - 0x6E, 0xFD, 0x6D, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, - 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, 0x26, 0xFD, 0xAD, 0x04, - 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, 0xFB, 0xD4, 0x00, 0x5D, - 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, 0x10, 0x00, 0xFD, 0xFF, - 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0x01, 0x07, 0xBE, - 0xF2, 0xD6, 0x23, 0x1F, 0x3B, 0xA5, 0xF2, 0xC5, 0x05, 0x62, 0xFD, - 0x10, 0x01, 0xAB, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, - 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, 0x4D, 0x06, 0x00, 0xF2, - 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, 0x07, 0x34, 0xFC, 0xDD, - 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, - 0x56, 0x00, 0xB9, 0xFF, 0xB8, 0xFF, 0xF7, 0x01, 0xE2, 0xF8, 0x8D, - 0x45, 0x46, 0x12, 0x3C, 0xF7, 0x43, 0x05, 0xDF, 0xFC, 0xAC, 0x01, - 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, - 0xFF, 0x46, 0x01, 0xC3, 0xFD, 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, - 0xA6, 0x48, 0xF8, 0xFF, 0x70, 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, - 0x00, 0x9A, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, - 0xDE, 0x01, 0x5D, 0xFC, 0x74, 0x06, 0x63, 0xF4, 0x23, 0x1C, 0x66, - 0x40, 0xAA, 0xF4, 0x65, 0x04, 0x44, 0xFE, 0x8B, 0x00, 0xEE, 0xFF, - 0xF5, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, - 0x01, 0x80, 0xFC, 0xF7, 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, - 0x83, 0xF1, 0x13, 0x07, 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, - 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xED, 0xFF, 0x05, 0x00, 0x5D, 0x00, - 0x95, 0xFE, 0xE2, 0x03, 0x7F, 0xF5, 0xCC, 0x41, 0xC7, 0x19, 0xFF, - 0xF4, 0x37, 0x06, 0x75, 0xFC, 0xD6, 0x01, 0x39, 0xFF, 0x35, 0x00, - 0xFE, 0xFF, 0x1B, 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, - 0x02, 0xAA, 0xFD, 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, - 0x05, 0x03, 0xF7, 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, - 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBB, 0x01, 0xBA, 0xFC, - 0x95, 0x05, 0x83, 0xF6, 0x8C, 0x14, 0x87, 0x44, 0xBB, 0xF7, 0x98, - 0x02, 0x5A, 0xFF, 0xEE, 0xFF, 0x3C, 0x00, 0xD8, 0xFF, 0x0A, 0x00, - 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, - 0x07, 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, - 0xD5, 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, - 0x00, 0x07, 0x00, 0xBE, 0xFF, 0xEA, 0x00, 0xA2, 0xFD, 0x65, 0x05, - 0x28, 0xF3, 0xDB, 0x3C, 0x78, 0x21, 0x30, 0xF3, 0xDF, 0x06, 0x3A, - 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, - 0xB2, 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, - 0xFC, 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, - 0x79, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, - 0x00, 0x58, 0xFF, 0x82, 0x01, 0x3F, 0xFD, 0x78, 0x04, 0xF2, 0xF8, - 0x50, 0x0D, 0x5E, 0x47, 0xD5, 0xFB, 0x6F, 0x00, 0x96, 0x00, 0x40, - 0xFF, 0x91, 0x00, 0xB7, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, - 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, - 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, - 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x85, - 0xFF, 0x5B, 0x01, 0xE9, 0xFC, 0x73, 0x06, 0xD8, 0xF1, 0xE5, 0x36, - 0x19, 0x29, 0xF8, 0xF1, 0x29, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x42, - 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, 0xFF, 0x47, 0x00, - 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, 0x8D, - 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, 0xFF, - 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x75, 0xFF, 0x39, - 0x01, 0xE0, 0xFD, 0x33, 0x03, 0x87, 0xFB, 0xA2, 0x06, 0xCB, 0x48, - 0xEA, 0x00, 0x01, 0xFE, 0xE9, 0x01, 0x8A, 0xFE, 0xE8, 0x00, 0x95, - 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, 0xDA, 0x01, - 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, 0x41, 0x1F, - 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, 0xF0, 0xFF, - 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5B, 0xFF, 0xAB, 0x01, 0x6F, - 0xFC, 0x08, 0x07, 0x7E, 0xF1, 0x21, 0x30, 0x67, 0x30, 0x7D, 0xF1, - 0x05, 0x07, 0x73, 0xFC, 0xA8, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, - 0xFF, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0x67, 0xFE, - 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, 0xA6, 0xF4, 0x5A, - 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, - 0x1A, 0x00, 0x96, 0xFF, 0xE5, 0x00, 0x91, 0xFE, 0xDC, 0x01, 0x1A, - 0xFE, 0xB3, 0x00, 0xC3, 0x48, 0xE1, 0x06, 0x6E, 0xFB, 0x40, 0x03, - 0xDA, 0xFD, 0x3C, 0x01, 0x74, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, - 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, - 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, - 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, - 0x33, 0x00, 0x41, 0xFF, 0xD9, 0x01, 0x36, 0xFC, 0x28, 0x07, 0x01, - 0xF2, 0xCE, 0x28, 0x23, 0x37, 0xE0, 0xF1, 0x6B, 0x06, 0xEF, 0xFC, - 0x57, 0x01, 0x87, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, - 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, 0xFD, 0x9C, 0x05, 0xDC, 0xF2, - 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, 0xF3, 0x06, 0x35, 0xFC, 0xE6, - 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xB8, 0xFF, - 0x8E, 0x00, 0x46, 0xFF, 0x8A, 0x00, 0x86, 0x00, 0xA7, 0xFB, 0x48, - 0x47, 0x95, 0x0D, 0xD9, 0xF8, 0x84, 0x04, 0x39, 0xFD, 0x85, 0x01, - 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, - 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, - 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, - 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, - 0xE6, 0x01, 0x3B, 0xFC, 0xDA, 0x06, 0x3F, 0xF3, 0x2C, 0x21, 0x11, - 0x3D, 0x3A, 0xF3, 0x58, 0x05, 0xAA, 0xFD, 0xE5, 0x00, 0xC1, 0xFF, - 0x06, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, - 0x01, 0xCF, 0xFC, 0x96, 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, - 0xD4, 0xF1, 0x2B, 0x07, 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, - 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD9, 0xFF, 0x39, 0x00, 0xF4, 0xFF, - 0x4E, 0xFF, 0xAC, 0x02, 0x98, 0xF7, 0x65, 0x44, 0xD6, 0x14, 0x6C, - 0xF6, 0x9F, 0x05, 0xB6, 0xFC, 0xBD, 0x01, 0x42, 0xFF, 0x32, 0x00, - 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, - 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, - 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, - 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD5, 0x01, 0x78, 0xFC, - 0x2F, 0x06, 0x13, 0xF5, 0x7C, 0x19, 0xF7, 0x41, 0x9B, 0xF5, 0xD1, - 0x03, 0x9F, 0xFE, 0x57, 0x00, 0x08, 0x00, 0xEC, 0xFF, 0x06, 0x00, - 0xFD, 0xFF, 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, - 0x07, 0x85, 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, - 0x84, 0xFC, 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, - 0x00, 0xF6, 0xFF, 0xEB, 0xFF, 0x91, 0x00, 0x3B, 0xFE, 0x75, 0x04, - 0x92, 0xF4, 0x36, 0x40, 0x6E, 0x1C, 0x50, 0xF4, 0x7B, 0x06, 0x5B, - 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, - 0x9C, 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, - 0xFF, 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, - 0x49, 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, - 0x00, 0x49, 0xFF, 0xAA, 0x01, 0xE4, 0xFC, 0x38, 0x05, 0x54, 0xF7, - 0xFE, 0x11, 0xAA, 0x45, 0x09, 0xF9, 0xE2, 0x01, 0xC4, 0xFF, 0xB3, - 0xFF, 0x59, 0x00, 0xCD, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, - 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, - 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, - 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA9, - 0xFF, 0x15, 0x01, 0x5B, 0xFD, 0xD0, 0x05, 0x97, 0xF2, 0xE6, 0x3A, - 0x21, 0x24, 0xB1, 0xF2, 0x04, 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x39, - 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, - 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, - 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, - 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x6A, - 0x01, 0x74, 0xFD, 0x0A, 0x04, 0xD5, 0xF9, 0xED, 0x0A, 0x03, 0x48, - 0x7C, 0xFD, 0x9E, 0xFF, 0x0A, 0x01, 0x01, 0xFF, 0xAF, 0x00, 0xAB, - 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, - 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, 0x3D, 0x91, - 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, 0x02, 0x00, - 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x75, 0xFF, 0x7A, 0x01, 0xB8, - 0xFC, 0xB4, 0x06, 0x9E, 0xF1, 0xA2, 0x34, 0xAD, 0x2B, 0xB6, 0xF1, - 0x29, 0x07, 0x45, 0xFC, 0xCB, 0x01, 0x49, 0xFF, 0x31, 0x00, 0xFD, - 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, 0xFF, - 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, 0xCA, - 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, - 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0x1C, 0x01, 0x1C, 0xFE, 0xBD, - 0x02, 0x6E, 0xFC, 0x7D, 0x04, 0xF3, 0x48, 0xE2, 0x02, 0x1F, 0xFD, - 0x60, 0x02, 0x4C, 0xFE, 0x06, 0x01, 0x89, 0xFF, 0x1D, 0x00, 0xFE, - 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, 0x88, 0xFC, 0x09, 0x06, - 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, 0xF6, 0x83, 0x03, 0xCF, - 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, - 0x2E, 0x00, 0x50, 0xFF, 0xBF, 0x01, 0x54, 0xFC, 0x20, 0x07, 0x94, - 0xF1, 0xA6, 0x2D, 0xD0, 0x32, 0x85, 0xF1, 0xDD, 0x06, 0x96, 0xFC, - 0x90, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, - 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, 0xB9, 0x04, 0x27, 0xF4, - 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, 0x06, 0x50, 0xFC, 0xE2, - 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA2, 0xFF, - 0xC7, 0x00, 0xD0, 0xFE, 0x65, 0x01, 0xF6, 0xFE, 0xD9, 0xFE, 0x6A, - 0x48, 0x1F, 0x09, 0x87, 0xFA, 0xB3, 0x03, 0xA0, 0xFD, 0x56, 0x01, - 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, - 0xFF, 0xA0, 0x01, 0xFB, 0xFC, 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, - 0x2B, 0x46, 0xBB, 0xF9, 0x83, 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, - 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, - 0xE1, 0x01, 0x31, 0xFC, 0x19, 0x07, 0x5B, 0xF2, 0x30, 0x26, 0x4B, - 0x39, 0x3B, 0xF2, 0x1A, 0x06, 0x29, 0xFD, 0x33, 0x01, 0x99, 0xFF, - 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, - 0x01, 0x3A, 0xFD, 0x00, 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, - 0x79, 0xF2, 0x12, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, - 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC4, 0xFF, 0x70, 0x00, 0x84, 0xFF, - 0x19, 0x00, 0x4D, 0x01, 0x22, 0xFA, 0x70, 0x46, 0x0A, 0x10, 0xFC, - 0xF7, 0xEB, 0x04, 0x08, 0xFD, 0x9A, 0x01, 0x4F, 0xFF, 0x2E, 0x00, - 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, - 0xFD, 0xD2, 0x03, 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, - 0x33, 0xFF, 0x45, 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, - 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4B, 0xFC, - 0xA9, 0x06, 0xD2, 0xF3, 0x81, 0x1E, 0xE4, 0x3E, 0xEF, 0xF3, 0xDE, - 0x04, 0xF9, 0xFD, 0xB7, 0x00, 0xD8, 0xFF, 0xFD, 0xFF, 0x03, 0x00, - 0xFD, 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, - 0x06, 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, - 0x4E, 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, - 0x00, 0xE4, 0xFF, 0x1D, 0x00, 0x2D, 0x00, 0xEA, 0xFE, 0x56, 0x03, - 0x6D, 0xF6, 0x17, 0x43, 0x70, 0x17, 0xA6, 0xF5, 0xF3, 0x05, 0x91, - 0xFC, 0xCC, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, - 0x86, 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, - 0x03, 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, - 0x14, 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, - 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x99, 0xFC, 0xE1, 0x05, 0xD1, 0xF5, - 0xDC, 0x16, 0x65, 0x43, 0xAD, 0xF6, 0x31, 0x03, 0x00, 0xFF, 0x20, - 0x00, 0x23, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, - 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, - 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, - 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xD3, - 0xFF, 0xC1, 0x00, 0xE7, 0xFD, 0xFA, 0x04, 0xC4, 0xF3, 0x7E, 0x3E, - 0x19, 0x1F, 0xB0, 0xF3, 0xB5, 0x06, 0x47, 0xFC, 0xE4, 0x01, 0x36, - 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, 0x00, - 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, 0x47, - 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, 0xFF, - 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x96, - 0x01, 0x13, 0xFD, 0xD5, 0x04, 0x2C, 0xF8, 0x7D, 0x0F, 0xA3, 0x46, - 0x76, 0xFA, 0x22, 0x01, 0x32, 0x00, 0x76, 0xFF, 0x76, 0x00, 0xC1, - 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, 0xE4, 0x01, - 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, 0x3A, 0x74, - 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, 0x11, 0x00, - 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x95, 0xFF, 0x3B, 0x01, 0x1B, - 0xFD, 0x2D, 0x06, 0x24, 0xF2, 0xD3, 0x38, 0xC6, 0x26, 0x45, 0xF2, - 0x1D, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, - 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, 0xFF, 0xE2, 0xFF, - 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, 0x8F, 0xF7, 0x1D, - 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, - 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x51, 0x01, 0xAC, 0xFD, 0x9A, - 0x03, 0xBA, 0xFA, 0x9E, 0x08, 0x81, 0x48, 0x40, 0xFF, 0xC6, 0xFE, - 0x80, 0x01, 0xC2, 0xFE, 0xCE, 0x00, 0x9F, 0xFF, 0x17, 0x00, 0xFE, - 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, - 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, - 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFD, 0xFF, - 0x27, 0x00, 0x66, 0xFF, 0x96, 0x01, 0x8E, 0xFC, 0xE7, 0x06, 0x81, - 0xF1, 0x48, 0x32, 0x34, 0x2E, 0x8D, 0xF1, 0x1C, 0x07, 0x5A, 0xFC, - 0xBB, 0x01, 0x53, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, - 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, 0xFE, 0xA6, 0x03, 0xE4, 0xF5, - 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, 0x1A, 0x06, 0x81, 0xFC, 0xD2, - 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8B, 0xFF, - 0xFF, 0x00, 0x5A, 0xFE, 0x46, 0x02, 0x52, 0xFD, 0x70, 0x02, 0xED, - 0x48, 0xF5, 0x04, 0x3B, 0xFC, 0xD7, 0x02, 0x0F, 0xFE, 0x23, 0x01, - 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, - 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, - 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, - 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, - 0xCE, 0x01, 0x41, 0xFC, 0x2A, 0x07, 0xC2, 0xF1, 0x1B, 0x2B, 0x25, - 0x35, 0xA8, 0xF1, 0xA7, 0x06, 0xC2, 0xFC, 0x74, 0x01, 0x78, 0xFF, - 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, - 0x00, 0xBF, 0xFD, 0x38, 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, - 0x66, 0xF3, 0xCE, 0x06, 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, - 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAE, 0xFF, 0xA9, 0x00, 0x0F, 0xFF, - 0xF0, 0x00, 0xCD, 0xFF, 0x1B, 0xFD, 0xE4, 0x47, 0x73, 0x0B, 0xA2, - 0xF9, 0x23, 0x04, 0x68, 0xFD, 0x70, 0x01, 0x5F, 0xFF, 0x29, 0x00, - 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, - 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, - 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, - 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, - 0xFD, 0x06, 0xCB, 0xF2, 0x8A, 0x23, 0x58, 0x3B, 0xB4, 0xF2, 0xBA, - 0x05, 0x6A, 0xFD, 0x0B, 0x01, 0xAE, 0xFF, 0x0D, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, - 0x06, 0xF7, 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, - 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, - 0x00, 0xCF, 0xFF, 0x52, 0x00, 0xC0, 0xFF, 0xAC, 0xFF, 0x0C, 0x02, - 0xBC, 0xF8, 0x6D, 0x45, 0x8E, 0x12, 0x24, 0xF7, 0x4D, 0x05, 0xDB, - 0xFC, 0xAE, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, - 0x24, 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, - 0xFB, 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, - 0xA3, 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, - 0x00, 0x37, 0xFF, 0xDD, 0x01, 0x60, 0xFC, 0x6D, 0x06, 0x76, 0xF4, - 0xD8, 0x1B, 0x95, 0x40, 0xC3, 0xF4, 0x56, 0x04, 0x4E, 0xFE, 0x85, - 0x00, 0xF1, 0xFF, 0xF4, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, - 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, - 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, - 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x02, - 0x00, 0x63, 0x00, 0x8A, 0xFE, 0xF3, 0x03, 0x63, 0xF5, 0xA1, 0x41, - 0x12, 0x1A, 0xEB, 0xF4, 0x3F, 0x06, 0x72, 0xFC, 0xD7, 0x01, 0x39, - 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, - 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, - 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, - 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBA, - 0x01, 0xBF, 0xFC, 0x8B, 0x05, 0x99, 0xF6, 0x43, 0x14, 0xA9, 0x44, - 0xDE, 0xF7, 0x85, 0x02, 0x65, 0xFF, 0xE7, 0xFF, 0x3F, 0x00, 0xD6, - 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD5, 0x01, - 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, 0x36, 0xC5, - 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, 0x1E, 0x00, - 0xFE, 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBC, 0xFF, 0xEF, 0x00, 0x9A, - 0xFD, 0x72, 0x05, 0x16, 0xF3, 0xA5, 0x3C, 0xC4, 0x21, 0x21, 0xF3, - 0xE4, 0x06, 0x39, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, - 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, 0x00, - 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, 0x5A, - 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x80, 0x01, 0x45, 0xFD, 0x6C, - 0x04, 0x0B, 0xF9, 0x0B, 0x0D, 0x73, 0x47, 0x02, 0xFC, 0x58, 0x00, - 0xA3, 0x00, 0x39, 0xFF, 0x94, 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, - 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, 0xFC, 0xEB, 0x06, - 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, 0xF2, 0x84, 0x05, 0x8D, - 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, - 0x1D, 0x00, 0x83, 0xFF, 0x5E, 0x01, 0xE3, 0xFC, 0x7B, 0x06, 0xD0, - 0xF1, 0xA5, 0x36, 0x62, 0x29, 0xEF, 0xF1, 0x29, 0x07, 0x39, 0xFC, - 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, - 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, 0x67, 0x02, 0x14, 0xF8, - 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, 0x05, 0xC5, 0xFC, 0xB7, - 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, - 0x76, 0xFF, 0x35, 0x01, 0xE7, 0xFD, 0x26, 0x03, 0xA1, 0xFB, 0x64, - 0x06, 0xD2, 0x48, 0x21, 0x01, 0xE8, 0xFD, 0xF7, 0x01, 0x83, 0xFE, - 0xEC, 0x00, 0x93, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, - 0xFF, 0xD9, 0x01, 0x6D, 0xFC, 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, - 0x5F, 0x41, 0x3A, 0xF5, 0x0C, 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, - 0xFF, 0xEF, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, - 0xAD, 0x01, 0x6C, 0xFC, 0x0C, 0x07, 0x7F, 0xF1, 0xDC, 0x2F, 0xAD, - 0x30, 0x7D, 0xF1, 0x01, 0x07, 0x76, 0xFC, 0xA6, 0x01, 0x5E, 0xFF, - 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, - 0x00, 0x5D, 0xFE, 0x3E, 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, - 0x93, 0xF4, 0x62, 0x06, 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, - 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x97, 0xFF, 0xE2, 0x00, 0x98, 0xFE, - 0xCF, 0x01, 0x33, 0xFE, 0x7D, 0x00, 0xBB, 0x48, 0x1F, 0x07, 0x54, - 0xFB, 0x4C, 0x03, 0xD3, 0xFD, 0x3F, 0x01, 0x73, 0xFF, 0x23, 0x00, - 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, - 0xFC, 0x5D, 0x05, 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, - 0x2A, 0x02, 0x9A, 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, - 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDA, 0x01, 0x35, 0xFC, - 0x27, 0x07, 0x09, 0xF2, 0x85, 0x28, 0x63, 0x37, 0xE9, 0xF1, 0x63, - 0x06, 0xF5, 0xFC, 0x53, 0x01, 0x89, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, - 0x00, 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, - 0x05, 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, - 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, - 0x00, 0xB9, 0xFF, 0x8A, 0x00, 0x4D, 0xFF, 0x7D, 0x00, 0x9C, 0x00, - 0x7B, 0xFB, 0x31, 0x47, 0xD9, 0x0D, 0xC0, 0xF8, 0x8F, 0x04, 0x34, - 0xFD, 0x87, 0x01, 0x56, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x29, 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, - 0xF9, 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, - 0x19, 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, - 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD5, 0x06, 0x4F, 0xF3, - 0xE0, 0x20, 0x45, 0x3D, 0x4D, 0xF3, 0x4B, 0x05, 0xB3, 0xFD, 0xE0, - 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, - 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, - 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, - 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xDA, 0xFF, 0x36, - 0x00, 0xFA, 0xFF, 0x43, 0xFF, 0xBF, 0x02, 0x75, 0xF7, 0x42, 0x44, - 0x20, 0x15, 0x55, 0xF6, 0xA9, 0x05, 0xB2, 0xFC, 0xBF, 0x01, 0x41, - 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, - 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, 0xEA, - 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, 0x00, - 0x8D, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD4, - 0x01, 0x7C, 0xFC, 0x27, 0x06, 0x28, 0xF5, 0x31, 0x19, 0x21, 0x42, - 0xB8, 0xF5, 0xC0, 0x03, 0xAA, 0xFE, 0x51, 0x00, 0x0B, 0x00, 0xEA, - 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, 0xB7, 0x01, - 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, 0x31, 0x7E, - 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, 0x28, 0x00, - 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xE8, 0xFF, 0x96, 0x00, 0x31, - 0xFE, 0x84, 0x04, 0x79, 0xF4, 0x07, 0x40, 0xBA, 0x1C, 0x3E, 0xF4, - 0x82, 0x06, 0x58, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, - 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, 0xFE, 0x93, 0x01, - 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, 0xE1, 0xFA, 0x86, - 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA8, 0x01, 0xE9, 0xFC, 0x2D, - 0x05, 0x6B, 0xF7, 0xB6, 0x11, 0xC8, 0x45, 0x30, 0xF9, 0xCD, 0x01, - 0xD0, 0xFF, 0xAC, 0xFF, 0x5C, 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, - 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, - 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, - 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, - 0x10, 0x00, 0xA7, 0xFF, 0x19, 0x01, 0x53, 0xFD, 0xDB, 0x05, 0x88, - 0xF2, 0xAD, 0x3A, 0x6D, 0x24, 0xA4, 0xF2, 0x08, 0x07, 0x32, 0xFC, - 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBF, - 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, 0x00, 0x01, 0x01, 0xB6, 0xFA, - 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, 0xC4, 0x04, 0x1B, 0xFD, 0x92, - 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, - 0x63, 0xFF, 0x67, 0x01, 0x7A, 0xFD, 0xFE, 0x03, 0xEE, 0xF9, 0xAA, - 0x0A, 0x16, 0x48, 0xAC, 0xFD, 0x86, 0xFF, 0x17, 0x01, 0xFA, 0xFE, - 0xB3, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, - 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, - 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, - 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x73, 0xFF, - 0x7D, 0x01, 0xB3, 0xFC, 0xBB, 0x06, 0x9A, 0xF1, 0x60, 0x34, 0xF5, - 0x2B, 0xB0, 0xF1, 0x28, 0x07, 0x47, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, - 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, - 0x00, 0x10, 0xFF, 0x15, 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, - 0xF1, 0xF5, 0xD3, 0x05, 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, - 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x19, 0x01, - 0x23, 0xFE, 0xB0, 0x02, 0x87, 0xFC, 0x41, 0x04, 0xF4, 0x48, 0x1C, - 0x03, 0x06, 0xFD, 0x6E, 0x02, 0x45, 0xFE, 0x09, 0x01, 0x88, 0xFF, - 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, - 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, - 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, - 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4F, 0xFF, 0xC1, 0x01, 0x52, 0xFC, - 0x22, 0x07, 0x98, 0xF1, 0x5E, 0x2D, 0x13, 0x33, 0x87, 0xF1, 0xD8, - 0x06, 0x9B, 0xFC, 0x8D, 0x01, 0x6B, 0xFF, 0x25, 0x00, 0xFD, 0xFF, - 0x03, 0x00, 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, - 0x04, 0x10, 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, - 0x4E, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, - 0x00, 0xA3, 0xFF, 0xC3, 0x00, 0xD7, 0xFE, 0x58, 0x01, 0x0F, 0xFF, - 0xA6, 0xFE, 0x5D, 0x48, 0x61, 0x09, 0x6E, 0xFA, 0xC0, 0x03, 0x99, - 0xFD, 0x59, 0x01, 0x68, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0x2E, 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, - 0xF7, 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, - 0x8E, 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, - 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x16, 0x07, 0x67, 0xF2, - 0xE5, 0x25, 0x87, 0x39, 0x47, 0xF2, 0x10, 0x06, 0x30, 0xFD, 0x2F, - 0x01, 0x9C, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x00, - 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, - 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, - 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC5, 0xFF, 0x6D, - 0x00, 0x8B, 0xFF, 0x0D, 0x00, 0x63, 0x01, 0xF9, 0xF9, 0x55, 0x46, - 0x51, 0x10, 0xE3, 0xF7, 0xF7, 0x04, 0x03, 0xFD, 0x9D, 0x01, 0x4E, - 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, - 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, - 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, - 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, - 0x01, 0x4D, 0xFC, 0xA3, 0x06, 0xE4, 0xF3, 0x36, 0x1E, 0x16, 0x3F, - 0x05, 0xF4, 0xCF, 0x04, 0x02, 0xFE, 0xB2, 0x00, 0xDB, 0xFF, 0xFC, - 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, 0x8B, 0x01, - 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, 0x2D, 0x9A, - 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, 0x2F, 0x00, - 0xFD, 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x1A, 0x00, 0x33, 0x00, 0xDF, - 0xFE, 0x68, 0x03, 0x4E, 0xF6, 0xEE, 0x42, 0xBB, 0x17, 0x90, 0xF5, - 0xFC, 0x05, 0x8E, 0xFC, 0xCD, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, - 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, 0x02, - 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, 0xA9, - 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC7, 0x01, 0x9D, 0xFC, 0xD8, - 0x05, 0xE7, 0xF5, 0x91, 0x16, 0x89, 0x43, 0xCD, 0xF6, 0x1E, 0x03, - 0x0B, 0xFF, 0x1A, 0x00, 0x26, 0x00, 0xE0, 0xFF, 0x08, 0x00, 0xFD, - 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, 0x48, 0xFC, 0x28, 0x07, - 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, 0xF1, 0xBE, 0x06, 0xB0, - 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, - 0x00, 0x00, 0xD0, 0xFF, 0xC7, 0x00, 0xDE, 0xFD, 0x08, 0x05, 0xB0, - 0xF3, 0x4A, 0x3E, 0x64, 0x1F, 0xA0, 0xF3, 0xBB, 0x06, 0x45, 0xFC, - 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA9, - 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, 0x7A, 0xFF, 0xC5, 0xFD, - 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, 0x03, 0x7D, 0xFD, 0x66, - 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, - 0x52, 0xFF, 0x93, 0x01, 0x18, 0xFD, 0xC9, 0x04, 0x45, 0xF8, 0x36, - 0x0F, 0xBB, 0x46, 0xA1, 0xFA, 0x0C, 0x01, 0x3E, 0x00, 0x70, 0xFF, - 0x7A, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, - 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, - 0x8F, 0x3A, 0x82, 0xF2, 0xE1, 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, - 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x17, 0x00, 0x93, 0xFF, - 0x3F, 0x01, 0x15, 0xFD, 0x36, 0x06, 0x19, 0xF2, 0x97, 0x38, 0x11, - 0x27, 0x3B, 0xF2, 0x1F, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, - 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, - 0xFF, 0xD6, 0xFF, 0xC3, 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, - 0x77, 0xF7, 0x28, 0x05, 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, - 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6D, 0xFF, 0x4E, 0x01, - 0xB3, 0xFD, 0x8D, 0x03, 0xD4, 0xFA, 0x5D, 0x08, 0x8D, 0x48, 0x74, - 0xFF, 0xAE, 0xFE, 0x8D, 0x01, 0xBB, 0xFE, 0xD1, 0x00, 0x9E, 0xFF, - 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, - 0xFC, 0x85, 0x06, 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, - 0x8C, 0x04, 0x2C, 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, - 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x65, 0xFF, 0x98, 0x01, 0x8A, 0xFC, - 0xEC, 0x06, 0x7F, 0xF1, 0x04, 0x32, 0x7B, 0x2E, 0x8A, 0xF1, 0x1A, - 0x07, 0x5D, 0xFC, 0xB8, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, - 0x06, 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, - 0x03, 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, - 0x7D, 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, - 0x00, 0x8D, 0xFF, 0xFC, 0x00, 0x61, 0xFE, 0x39, 0x02, 0x6B, 0xFD, - 0x37, 0x02, 0xEB, 0x48, 0x31, 0x05, 0x21, 0xFC, 0xE4, 0x02, 0x08, - 0xFE, 0x26, 0x01, 0x7C, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0x32, 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, - 0xF6, 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, - 0xFE, 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x32, - 0x00, 0x47, 0xFF, 0xD0, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xCA, 0xF1, - 0xD1, 0x2A, 0x65, 0x35, 0xAE, 0xF1, 0xA0, 0x06, 0xC7, 0xFC, 0x70, - 0x01, 0x7A, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, - 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, - 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, - 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA5, - 0x00, 0x16, 0xFF, 0xE3, 0x00, 0xE4, 0xFF, 0xEB, 0xFC, 0xD2, 0x47, - 0xB6, 0x0B, 0x89, 0xF9, 0x2F, 0x04, 0x62, 0xFD, 0x72, 0x01, 0x5E, - 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x56, 0xFF, - 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, 0x26, - 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, 0x00, - 0xBA, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, - 0x01, 0x34, 0xFC, 0xF9, 0x06, 0xD9, 0xF2, 0x3F, 0x23, 0x90, 0x3B, - 0xC4, 0xF2, 0xAE, 0x05, 0x72, 0xFD, 0x07, 0x01, 0xB0, 0xFF, 0x0C, - 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, 0x51, 0x01, - 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, 0x28, 0x0E, - 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, 0x34, 0x00, - 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x4F, 0x00, 0xC7, 0xFF, 0xA0, - 0xFF, 0x20, 0x02, 0x96, 0xF8, 0x4E, 0x45, 0xD7, 0x12, 0x0D, 0xF7, - 0x58, 0x05, 0xD6, 0xFC, 0xB0, 0x01, 0x47, 0xFF, 0x30, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, 0x01, 0xD0, 0xFD, - 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, 0x62, 0x00, 0x3F, - 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, 0xFF, 0x19, 0x00, - 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x63, 0xFC, 0x66, - 0x06, 0x89, 0xF4, 0x8C, 0x1B, 0xC3, 0x40, 0xDD, 0xF4, 0x46, 0x04, - 0x58, 0xFE, 0x80, 0x00, 0xF4, 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, - 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, - 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, - 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, - 0xEF, 0xFF, 0xFF, 0xFF, 0x69, 0x00, 0x80, 0xFE, 0x04, 0x04, 0x48, - 0xF5, 0x74, 0x41, 0x5D, 0x1A, 0xD7, 0xF4, 0x47, 0x06, 0x6F, 0xFC, - 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x93, - 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, 0x01, 0xDC, 0xFD, 0x3C, 0x01, - 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, 0x1F, 0x03, 0xEA, 0xFD, 0x34, - 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, - 0x44, 0xFF, 0xB8, 0x01, 0xC3, 0xFC, 0x81, 0x05, 0xB0, 0xF6, 0xFA, - 0x13, 0xCC, 0x44, 0x02, 0xF8, 0x71, 0x02, 0x71, 0xFF, 0xE1, 0xFF, - 0x42, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, - 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, - 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, - 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x09, 0x00, 0xBA, 0xFF, - 0xF4, 0x00, 0x91, 0xFD, 0x7E, 0x05, 0x05, 0xF3, 0x6E, 0x3C, 0x10, - 0x22, 0x12, 0xF3, 0xE9, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, - 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, - 0xFF, 0xA9, 0x00, 0x4D, 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, - 0x18, 0xF9, 0x66, 0x04, 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5A, 0xFF, 0x7D, 0x01, - 0x4B, 0xFD, 0x60, 0x04, 0x24, 0xF9, 0xC6, 0x0C, 0x86, 0x47, 0x30, - 0xFC, 0x41, 0x00, 0xB0, 0x00, 0x32, 0xFF, 0x98, 0x00, 0xB4, 0xFF, - 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, - 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, - 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, - 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x81, 0xFF, 0x62, 0x01, 0xDD, 0xFC, - 0x83, 0x06, 0xC9, 0xF1, 0x66, 0x36, 0xAC, 0x29, 0xE7, 0xF1, 0x2A, - 0x07, 0x3A, 0xFC, 0xD5, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, - 0x0B, 0x00, 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, - 0x02, 0xF0, 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, - 0xC1, 0xFC, 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, - 0x00, 0x22, 0x00, 0x77, 0xFF, 0x32, 0x01, 0xED, 0xFD, 0x19, 0x03, - 0xBB, 0xFB, 0x26, 0x06, 0xD7, 0x48, 0x58, 0x01, 0xCF, 0xFD, 0x04, - 0x02, 0x7D, 0xFE, 0xEF, 0x00, 0x92, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, - 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, - 0xF4, 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, - 0x66, 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2B, - 0x00, 0x59, 0xFF, 0xB0, 0x01, 0x69, 0xFC, 0x0F, 0x07, 0x80, 0xF1, - 0x96, 0x2F, 0xF2, 0x30, 0x7C, 0xF1, 0xFD, 0x06, 0x7A, 0xFC, 0xA3, - 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF4, 0xFF, - 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, - 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, - 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x98, 0xFF, 0xDE, - 0x00, 0x9F, 0xFE, 0xC2, 0x01, 0x4B, 0xFE, 0x48, 0x00, 0xB3, 0x48, - 0x5E, 0x07, 0x3B, 0xFB, 0x59, 0x03, 0xCD, 0xFD, 0x42, 0x01, 0x71, - 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, - 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, - 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, - 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDB, - 0x01, 0x35, 0xFC, 0x25, 0x07, 0x13, 0xF2, 0x3A, 0x28, 0xA0, 0x37, - 0xF2, 0xF1, 0x5A, 0x06, 0xFB, 0xFC, 0x4F, 0x01, 0x8B, 0xFF, 0x1A, - 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, 0x09, 0x01, - 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, 0x23, 0xD2, - 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, - 0xFD, 0xFF, 0x11, 0x00, 0xBB, 0xFF, 0x87, 0x00, 0x54, 0xFF, 0x70, - 0x00, 0xB3, 0x00, 0x4E, 0xFB, 0x1A, 0x47, 0x1F, 0x0E, 0xA8, 0xF8, - 0x9B, 0x04, 0x2E, 0xFD, 0x8A, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, 0xFD, - 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, 0xD9, - 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, 0x00, - 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD0, - 0x06, 0x5E, 0xF3, 0x94, 0x20, 0x7B, 0x3D, 0x60, 0xF3, 0x3E, 0x05, - 0xBB, 0xFD, 0xDB, 0x00, 0xC6, 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, - 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, 0xC4, 0xFC, 0xA4, 0x06, - 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, 0xF1, 0x2A, 0x07, 0x40, - 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, - 0xDB, 0xFF, 0x33, 0x00, 0x01, 0x00, 0x38, 0xFF, 0xD3, 0x02, 0x53, - 0xF7, 0x1F, 0x44, 0x69, 0x15, 0x3F, 0xF6, 0xB2, 0x05, 0xAD, 0xFC, - 0xC1, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, - 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, 0xDE, 0x02, 0x2E, 0xFC, - 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, 0xFD, 0x3F, 0x02, 0x5D, - 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, - 0x3B, 0xFF, 0xD3, 0x01, 0x7F, 0xFC, 0x1F, 0x06, 0x3C, 0xF5, 0xE6, - 0x18, 0x4D, 0x42, 0xD5, 0xF5, 0xAF, 0x03, 0xB4, 0xFE, 0x4B, 0x00, - 0x0E, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, - 0xFF, 0xBA, 0x01, 0x5B, 0xFC, 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, - 0x26, 0x32, 0x80, 0xF1, 0xEA, 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, - 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, 0xFF, 0xE6, 0xFF, - 0x9C, 0x00, 0x27, 0xFE, 0x94, 0x04, 0x61, 0xF4, 0xD7, 0x3F, 0x06, - 0x1D, 0x2B, 0xF4, 0x89, 0x06, 0x56, 0xFC, 0xE0, 0x01, 0x37, 0xFF, - 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, - 0xFE, 0x86, 0x01, 0xBA, 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, - 0xC7, 0xFA, 0x93, 0x03, 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, - 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA6, 0x01, - 0xEE, 0xFC, 0x23, 0x05, 0x83, 0xF7, 0x6E, 0x11, 0xE5, 0x45, 0x57, - 0xF9, 0xB8, 0x01, 0xDC, 0xFF, 0xA5, 0xFF, 0x5F, 0x00, 0xCA, 0xFF, - 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, - 0xFC, 0x1E, 0x07, 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, - 0x32, 0x06, 0x18, 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA4, 0xFF, 0x1D, 0x01, 0x4C, 0xFD, - 0xE6, 0x05, 0x7B, 0xF2, 0x71, 0x3A, 0xB8, 0x24, 0x97, 0xF2, 0x0B, - 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, - 0x0F, 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, - 0x01, 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, - 0x15, 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, - 0x00, 0x28, 0x00, 0x64, 0xFF, 0x65, 0x01, 0x81, 0xFD, 0xF2, 0x03, - 0x08, 0xFA, 0x68, 0x0A, 0x25, 0x48, 0xDE, 0xFD, 0x6E, 0xFF, 0x24, - 0x01, 0xF3, 0xFE, 0xB6, 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, - 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, - 0xF3, 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, - 0xC4, 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x23, - 0x00, 0x71, 0xFF, 0x81, 0x01, 0xAE, 0xFC, 0xC1, 0x06, 0x95, 0xF1, - 0x1E, 0x34, 0x3E, 0x2C, 0xAB, 0xF1, 0x27, 0x07, 0x49, 0xFC, 0xC8, - 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, - 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, - 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, - 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, - 0xFF, 0x16, 0x01, 0x2A, 0xFE, 0xA3, 0x02, 0xA1, 0xFC, 0x06, 0x04, - 0xF5, 0x48, 0x56, 0x03, 0xED, 0xFC, 0x7B, 0x02, 0x3E, 0xFE, 0x0C, - 0x01, 0x86, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, - 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, 0x02, - 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, 0x00, - 0xE4, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, - 0x01, 0x4F, 0xFC, 0x24, 0x07, 0x9C, 0xF1, 0x17, 0x2D, 0x57, 0x33, - 0x8A, 0xF1, 0xD3, 0x06, 0x9F, 0xFC, 0x8A, 0x01, 0x6D, 0xFF, 0x25, - 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, 0xB4, 0x00, - 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, - 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, - 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC0, 0x00, 0xDE, 0xFE, 0x4B, - 0x01, 0x27, 0xFF, 0x73, 0xFE, 0x4F, 0x48, 0xA2, 0x09, 0x54, 0xFA, - 0xCC, 0x03, 0x93, 0xFD, 0x5C, 0x01, 0x67, 0xFF, 0x27, 0x00, 0x00, - 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, 0x01, 0x05, 0xFD, - 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, 0x0D, 0xFA, 0x58, - 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, 0xFF, 0x0E, 0x00, - 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x14, - 0x07, 0x73, 0xF2, 0x99, 0x25, 0xC2, 0x39, 0x54, 0xF2, 0x05, 0x06, - 0x37, 0xFD, 0x2B, 0x01, 0x9E, 0xFF, 0x13, 0x00, 0xFF, 0xFF, 0xFF, - 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, - 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, - 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, - 0xC6, 0xFF, 0x69, 0x00, 0x91, 0xFF, 0x00, 0x00, 0x78, 0x01, 0xD0, - 0xF9, 0x39, 0x46, 0x98, 0x10, 0xCB, 0xF7, 0x02, 0x05, 0xFE, 0xFC, - 0x9F, 0x01, 0x4D, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, - 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, 0xFD, 0xB9, 0x03, 0x7B, 0xFA, - 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, 0x03, 0xFF, 0x5F, 0x01, 0xD4, - 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, - 0x36, 0xFF, 0xE2, 0x01, 0x4F, 0xFC, 0x9C, 0x06, 0xF5, 0xF3, 0xEA, - 0x1D, 0x47, 0x3F, 0x1B, 0xF4, 0xC1, 0x04, 0x0B, 0xFE, 0xAC, 0x00, - 0xDE, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, - 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, - 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, - 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x17, 0x00, - 0x39, 0x00, 0xD4, 0xFE, 0x7A, 0x03, 0x2F, 0xF6, 0xC7, 0x42, 0x06, - 0x18, 0x7B, 0xF5, 0x05, 0x06, 0x8A, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, - 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, - 0xFE, 0x67, 0x02, 0x13, 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, - 0x7A, 0xFC, 0xB6, 0x02, 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, - 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, - 0xA1, 0xFC, 0xCF, 0x05, 0xFC, 0xF5, 0x47, 0x16, 0xB0, 0x43, 0xEE, - 0xF6, 0x0C, 0x03, 0x16, 0xFF, 0x14, 0x00, 0x29, 0x00, 0xDF, 0xFF, - 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, - 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, - 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, - 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCE, 0xFF, 0xCC, 0x00, 0xD5, 0xFD, - 0x16, 0x05, 0x9B, 0xF3, 0x18, 0x3E, 0xB1, 0x1F, 0x8F, 0xF3, 0xC0, - 0x06, 0x43, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, - 0x15, 0x00, 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, - 0xFF, 0x94, 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, - 0x77, 0xFD, 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, - 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x91, 0x01, 0x1E, 0xFD, 0xBE, 0x04, - 0x5E, 0xF8, 0xF0, 0x0E, 0xD3, 0x46, 0xCB, 0xFA, 0xF6, 0x00, 0x4B, - 0x00, 0x69, 0xFF, 0x7D, 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, - 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, - 0xF2, 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, - 0x17, 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, - 0x00, 0x91, 0xFF, 0x43, 0x01, 0x0E, 0xFD, 0x40, 0x06, 0x0F, 0xF2, - 0x5B, 0x38, 0x5C, 0x27, 0x30, 0xF2, 0x21, 0x07, 0x33, 0xFC, 0xDE, - 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCC, 0xFF, - 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, - 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, - 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6E, - 0xFF, 0x4B, 0x01, 0xB9, 0xFD, 0x80, 0x03, 0xEE, 0xFA, 0x1D, 0x08, - 0x98, 0x48, 0xA8, 0xFF, 0x95, 0xFE, 0x9A, 0x01, 0xB4, 0xFE, 0xD4, - 0x00, 0x9C, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, - 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, - 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, - 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9B, - 0x01, 0x86, 0xFC, 0xF1, 0x06, 0x7E, 0xF1, 0xC0, 0x31, 0xC2, 0x2E, - 0x87, 0xF1, 0x17, 0x07, 0x5F, 0xFC, 0xB6, 0x01, 0x55, 0xFF, 0x2D, - 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, 0x54, 0x00, - 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, 0x19, 0x1E, - 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, 0x35, 0x00, - 0xFE, 0xFF, 0x1C, 0x00, 0x8E, 0xFF, 0xF9, 0x00, 0x68, 0xFE, 0x2C, - 0x02, 0x84, 0xFD, 0xFF, 0x01, 0xE6, 0x48, 0x6E, 0x05, 0x07, 0xFC, - 0xF1, 0x02, 0x01, 0xFE, 0x29, 0x01, 0x7B, 0xFF, 0x21, 0x00, 0x00, - 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, 0xFC, - 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, 0xB6, - 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, 0x00, - 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3E, 0xFC, 0x2B, - 0x07, 0xD0, 0xF1, 0x89, 0x2A, 0xA6, 0x35, 0xB4, 0xF1, 0x99, 0x06, - 0xCD, 0xFC, 0x6D, 0x01, 0x7C, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, - 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, 0xAE, 0xFD, 0x52, 0x05, - 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, 0xF3, 0xD8, 0x06, 0x3C, - 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, - 0xB0, 0xFF, 0xA2, 0x00, 0x1D, 0xFF, 0xD6, 0x00, 0xFC, 0xFF, 0xBC, - 0xFC, 0xC0, 0x47, 0xFA, 0x0B, 0x70, 0xF9, 0x3C, 0x04, 0x5C, 0xFD, - 0x75, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, - 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, 0x89, 0x04, 0xCD, 0xF8, - 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, 0x00, 0x83, 0x00, 0x4A, - 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, - 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF5, 0x06, 0xE7, 0xF2, 0xF2, - 0x22, 0xC7, 0x3B, 0xD4, 0xF2, 0xA2, 0x05, 0x7A, 0xFD, 0x02, 0x01, - 0xB2, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, - 0xFF, 0x55, 0x01, 0xF2, 0xFC, 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, - 0xAA, 0x28, 0x05, 0xF2, 0x27, 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, - 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD2, 0xFF, 0x4C, 0x00, - 0xCD, 0xFF, 0x94, 0xFF, 0x34, 0x02, 0x70, 0xF8, 0x2E, 0x45, 0x20, - 0x13, 0xF6, 0xF6, 0x62, 0x05, 0xD1, 0xFC, 0xB2, 0x01, 0x46, 0xFF, - 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, - 0x01, 0xD6, 0xFD, 0x46, 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, - 0x98, 0x00, 0x26, 0xFE, 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, - 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, - 0x66, 0xFC, 0x5E, 0x06, 0x9C, 0xF4, 0x40, 0x1B, 0xEF, 0x40, 0xF7, - 0xF4, 0x35, 0x04, 0x62, 0xFE, 0x7A, 0x00, 0xF7, 0xFF, 0xF2, 0xFF, - 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, - 0xFC, 0x03, 0x07, 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, - 0x0A, 0x07, 0x6E, 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, - 0xFF, 0x05, 0x00, 0xF0, 0xFF, 0xFC, 0xFF, 0x6E, 0x00, 0x76, 0xFE, - 0x15, 0x04, 0x2C, 0xF5, 0x49, 0x41, 0xA9, 0x1A, 0xC3, 0xF4, 0x4F, - 0x06, 0x6C, 0xFC, 0xD9, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, - 0x1A, 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, - 0xFD, 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, - 0xE4, 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, - 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB6, 0x01, 0xC8, 0xFC, 0x77, 0x05, - 0xC7, 0xF6, 0xB1, 0x13, 0xED, 0x44, 0x26, 0xF8, 0x5D, 0x02, 0x7D, - 0xFF, 0xDA, 0xFF, 0x46, 0x00, 0xD4, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, - 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, - 0xF1, 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, - 0x5C, 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, - 0x00, 0xB7, 0xFF, 0xF9, 0x00, 0x89, 0xFD, 0x8A, 0x05, 0xF4, 0xF2, - 0x37, 0x3C, 0x5B, 0x22, 0x03, 0xF3, 0xED, 0x06, 0x37, 0xFC, 0xE6, - 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB6, 0xFF, - 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, - 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, - 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, - 0xFF, 0x7A, 0x01, 0x50, 0xFD, 0x54, 0x04, 0x3D, 0xF9, 0x82, 0x0C, - 0x9A, 0x47, 0x5E, 0xFC, 0x2A, 0x00, 0xBD, 0x00, 0x2B, 0xFF, 0x9B, - 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, - 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, 0xC0, - 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, 0xFF, - 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x80, 0xFF, 0x66, - 0x01, 0xD8, 0xFC, 0x8B, 0x06, 0xC1, 0xF1, 0x27, 0x36, 0xF6, 0x29, - 0xDF, 0xF1, 0x2A, 0x07, 0x3B, 0xFC, 0xD4, 0x01, 0x44, 0xFF, 0x32, - 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, 0xEA, 0xFF, - 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, 0x14, 0x8E, - 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, 0x32, 0x00, - 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x79, 0xFF, 0x2F, 0x01, 0xF4, - 0xFD, 0x0C, 0x03, 0xD4, 0xFB, 0xE9, 0x05, 0xDE, 0x48, 0x8F, 0x01, - 0xB6, 0xFD, 0x11, 0x02, 0x76, 0xFE, 0xF2, 0x00, 0x91, 0xFF, 0x1B, - 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, 0x01, 0x73, 0xFC, - 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, 0x71, 0xF5, 0xEB, - 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, 0xFF, 0x06, 0x00, - 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB2, 0x01, 0x65, 0xFC, 0x12, - 0x07, 0x82, 0xF1, 0x50, 0x2F, 0x38, 0x31, 0x7C, 0xF1, 0xF9, 0x06, - 0x7E, 0xFC, 0xA1, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, - 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, - 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, - 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, - 0x9A, 0xFF, 0xDB, 0x00, 0xA6, 0xFE, 0xB4, 0x01, 0x64, 0xFE, 0x12, - 0x00, 0xAA, 0x48, 0x9E, 0x07, 0x21, 0xFB, 0x66, 0x03, 0xC6, 0xFD, - 0x45, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, - 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, 0xFC, 0x48, 0x05, 0x30, 0xF7, - 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, 0x01, 0x02, 0xB2, 0xFF, 0xBD, - 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, - 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x24, 0x07, 0x1C, 0xF2, 0xF0, - 0x27, 0xDF, 0x37, 0xFB, 0xF1, 0x51, 0x06, 0x01, 0xFD, 0x4B, 0x01, - 0x8D, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, - 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, - 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, - 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBC, 0xFF, 0x84, 0x00, - 0x5B, 0xFF, 0x64, 0x00, 0xC9, 0x00, 0x22, 0xFB, 0x02, 0x47, 0x64, - 0x0E, 0x8F, 0xF8, 0xA7, 0x04, 0x29, 0xFD, 0x8C, 0x01, 0x54, 0xFF, - 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, - 0x01, 0x6B, 0xFD, 0x1D, 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, - 0x33, 0xFD, 0xC1, 0xFF, 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, - 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, - 0x40, 0xFC, 0xCB, 0x06, 0x6E, 0xF3, 0x49, 0x20, 0xB0, 0x3D, 0x73, - 0xF3, 0x31, 0x05, 0xC4, 0xFD, 0xD6, 0x00, 0xC8, 0xFF, 0x03, 0x00, - 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, - 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, - 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, - 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x2F, 0x00, 0x07, 0x00, 0x2C, 0xFF, - 0xE6, 0x02, 0x31, 0xF7, 0xFA, 0x43, 0xB3, 0x15, 0x29, 0xF6, 0xBC, - 0x05, 0xA9, 0xFC, 0xC2, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, - 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, - 0x02, 0x47, 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, - 0x4D, 0x02, 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFE, - 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, 0x83, 0xFC, 0x16, 0x06, - 0x51, 0xF5, 0x9B, 0x18, 0x75, 0x42, 0xF3, 0xF5, 0x9D, 0x03, 0xBF, - 0xFE, 0x45, 0x00, 0x11, 0x00, 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, - 0x2E, 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, - 0xF1, 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, - 0x94, 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF9, - 0xFF, 0xE3, 0xFF, 0xA1, 0x00, 0x1E, 0xFE, 0xA3, 0x04, 0x49, 0xF4, - 0xA8, 0x3F, 0x52, 0x1D, 0x19, 0xF4, 0x90, 0x06, 0x53, 0xFC, 0xE1, - 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, - 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, - 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, - 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, - 0xFF, 0xA3, 0x01, 0xF3, 0xFC, 0x18, 0x05, 0x9B, 0xF7, 0x27, 0x11, - 0x02, 0x46, 0x7F, 0xF9, 0xA3, 0x01, 0xE8, 0xFF, 0x9F, 0xFF, 0x63, - 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, - 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, - 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, - 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA2, 0xFF, 0x22, - 0x01, 0x45, 0xFD, 0xF1, 0x05, 0x6D, 0xF2, 0x38, 0x3A, 0x03, 0x25, - 0x8B, 0xF2, 0x0E, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x3A, 0xFF, 0x36, - 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, 0x7A, 0xFF, - 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, 0x0F, 0x20, - 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, 0x2E, 0x00, - 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x62, 0x01, 0x87, - 0xFD, 0xE5, 0x03, 0x21, 0xFA, 0x25, 0x0A, 0x33, 0x48, 0x0F, 0xFE, - 0x57, 0xFF, 0x31, 0x01, 0xEC, 0xFE, 0xB9, 0x00, 0xA7, 0xFF, 0x15, - 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, 0xFC, - 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, 0xF3, - 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, 0x00, - 0xFE, 0xFF, 0x23, 0x00, 0x70, 0xFF, 0x84, 0x01, 0xA9, 0xFC, 0xC7, - 0x06, 0x91, 0xF1, 0xDC, 0x33, 0x87, 0x2C, 0xA5, 0xF1, 0x26, 0x07, - 0x4B, 0xFC, 0xC6, 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, - 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, 0xFA, 0xFE, 0x3A, 0x03, - 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, 0xF5, 0xE6, 0x05, 0x97, - 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, - 0x1E, 0x00, 0x84, 0xFF, 0x13, 0x01, 0x31, 0xFE, 0x95, 0x02, 0xBA, - 0xFC, 0xCB, 0x03, 0xF7, 0x48, 0x91, 0x03, 0xD3, 0xFC, 0x88, 0x02, - 0x38, 0xFE, 0x10, 0x01, 0x85, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, - 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, 0xEF, 0x05, 0xB0, 0xF5, - 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, 0x03, 0xEF, 0xFE, 0x2A, - 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, - 0x4D, 0xFF, 0xC4, 0x01, 0x4D, 0xFC, 0x25, 0x07, 0xA1, 0xF1, 0xCE, - 0x2C, 0x99, 0x33, 0x8E, 0xF1, 0xCD, 0x06, 0xA4, 0xFC, 0x87, 0x01, - 0x6E, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD7, - 0xFF, 0xBA, 0x00, 0xF4, 0xFD, 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, - 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, - 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBD, 0x00, - 0xE5, 0xFE, 0x3E, 0x01, 0x3F, 0xFF, 0x41, 0xFE, 0x41, 0x48, 0xE4, - 0x09, 0x3B, 0xFA, 0xD9, 0x03, 0x8D, 0xFD, 0x5F, 0x01, 0x66, 0xFF, - 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, - 0x01, 0x0B, 0xFD, 0xE6, 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, - 0x37, 0xFA, 0x42, 0x01, 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, - 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, - 0x31, 0xFC, 0x11, 0x07, 0x7F, 0xF2, 0x4E, 0x25, 0xFD, 0x39, 0x60, - 0xF2, 0xFB, 0x05, 0x3E, 0xFD, 0x26, 0x01, 0xA0, 0xFF, 0x12, 0x00, - 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, - 0xFD, 0x1E, 0x06, 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, - 0x1A, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, - 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x66, 0x00, 0x98, 0xFF, 0xF4, 0xFF, - 0x8E, 0x01, 0xA7, 0xF9, 0x1D, 0x46, 0xDF, 0x10, 0xB3, 0xF7, 0x0D, - 0x05, 0xF8, 0xFC, 0xA1, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, - 0x00, 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, - 0x03, 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, - 0x6C, 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFE, - 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x51, 0xFC, 0x96, 0x06, - 0x07, 0xF4, 0x9E, 0x1D, 0x77, 0x3F, 0x32, 0xF4, 0xB2, 0x04, 0x15, - 0xFE, 0xA7, 0x00, 0xE0, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, - 0x26, 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, - 0xF1, 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, - 0xBE, 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE7, - 0xFF, 0x14, 0x00, 0x3F, 0x00, 0xC9, 0xFE, 0x8C, 0x03, 0x11, 0xF6, - 0x9E, 0x42, 0x50, 0x18, 0x66, 0xF5, 0x0D, 0x06, 0x86, 0xFC, 0xD0, - 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, - 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, - 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, - 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, - 0xFF, 0xC4, 0x01, 0xA5, 0xFC, 0xC5, 0x05, 0x13, 0xF6, 0xFD, 0x15, - 0xD4, 0x43, 0x0F, 0xF7, 0xF9, 0x02, 0x21, 0xFF, 0x0D, 0x00, 0x2C, - 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, 0xFF, - 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, 0xC3, - 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, 0xFF, - 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, 0xCB, 0xFF, 0xD1, - 0x00, 0xCC, 0xFD, 0x24, 0x05, 0x87, 0xF3, 0xE4, 0x3D, 0xFD, 0x1F, - 0x7F, 0xF3, 0xC6, 0x06, 0x41, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, - 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, 0x05, 0xFF, - 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, 0x0B, 0xC8, - 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, 0x28, 0x00, - 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x8F, 0x01, 0x23, - 0xFD, 0xB2, 0x04, 0x76, 0xF8, 0xAA, 0x0E, 0xED, 0x46, 0xF7, 0xFA, - 0xDF, 0x00, 0x57, 0x00, 0x62, 0xFF, 0x80, 0x00, 0xBD, 0xFF, 0x10, - 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x33, 0xFC, - 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, 0x9E, 0xF2, 0xCB, - 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x18, 0x00, 0x8F, 0xFF, 0x47, 0x01, 0x08, 0xFD, 0x49, - 0x06, 0x05, 0xF2, 0x1D, 0x38, 0xA6, 0x27, 0x26, 0xF2, 0x23, 0x07, - 0x33, 0xFC, 0xDD, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, - 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, - 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, - 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, - 0x24, 0x00, 0x6F, 0xFF, 0x48, 0x01, 0xC0, 0xFD, 0x73, 0x03, 0x07, - 0xFB, 0xDD, 0x07, 0xA1, 0x48, 0xDD, 0xFF, 0x7D, 0xFE, 0xA7, 0x01, - 0xAD, 0xFE, 0xD8, 0x00, 0x9B, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, - 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, 0xFC, 0x78, 0x06, 0x5A, 0xF4, - 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, 0x6D, 0x04, 0x3F, 0xFE, 0x8E, - 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, - 0x62, 0xFF, 0x9E, 0x01, 0x82, 0xFC, 0xF5, 0x06, 0x7D, 0xF1, 0x7B, - 0x31, 0x09, 0x2F, 0x84, 0xF1, 0x15, 0x07, 0x62, 0xFC, 0xB4, 0x01, - 0x56, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, - 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, - 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, - 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x8F, 0xFF, 0xF5, 0x00, - 0x6F, 0xFE, 0x1E, 0x02, 0x9D, 0xFD, 0xC7, 0x01, 0xE1, 0x48, 0xAB, - 0x05, 0xEE, 0xFB, 0xFE, 0x02, 0xFB, 0xFD, 0x2C, 0x01, 0x7A, 0xFF, - 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, - 0x01, 0xB8, 0xFC, 0x9A, 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, - 0xA9, 0xF7, 0xA2, 0x02, 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, - 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD3, 0x01, - 0x3C, 0xFC, 0x2A, 0x07, 0xD8, 0xF1, 0x3F, 0x2A, 0xE6, 0x35, 0xBB, - 0xF1, 0x92, 0x06, 0xD2, 0xFC, 0x69, 0x01, 0x7E, 0xFF, 0x1F, 0x00, - 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, - 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, - 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, - 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0x9F, 0x00, 0x24, 0xFF, 0xC9, 0x00, - 0x13, 0x00, 0x8D, 0xFC, 0xAE, 0x47, 0x3E, 0x0C, 0x56, 0xF9, 0x48, - 0x04, 0x56, 0xFD, 0x78, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, - 0x04, 0xE6, 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, - 0x90, 0x00, 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFD, - 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xF1, 0x06, - 0xF5, 0xF2, 0xA7, 0x22, 0xFF, 0x3B, 0xE4, 0xF2, 0x96, 0x05, 0x81, - 0xFD, 0xFD, 0x00, 0xB5, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, - 0x1C, 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, - 0xF1, 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, - 0xD8, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, - 0xFF, 0x49, 0x00, 0xD4, 0xFF, 0x88, 0xFF, 0x49, 0x02, 0x4B, 0xF8, - 0x0D, 0x45, 0x68, 0x13, 0xDF, 0xF6, 0x6C, 0x05, 0xCC, 0xFC, 0xB4, - 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, - 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, - 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, - 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, - 0xFF, 0xDA, 0x01, 0x69, 0xFC, 0x57, 0x06, 0xAF, 0xF4, 0xF5, 0x1A, - 0x1D, 0x41, 0x11, 0xF5, 0x25, 0x04, 0x6C, 0xFE, 0x74, 0x00, 0xF9, - 0xFF, 0xF1, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, - 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, - 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, - 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF1, 0xFF, 0xF9, 0xFF, 0x74, - 0x00, 0x6C, 0xFE, 0x25, 0x04, 0x11, 0xF5, 0x1D, 0x41, 0xF5, 0x1A, - 0xAF, 0xF4, 0x57, 0x06, 0x69, 0xFC, 0xDA, 0x01, 0x38, 0xFF, 0x36, - 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, 0x8E, 0xFE, - 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, 0x06, 0x7B, - 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, 0x23, 0x00, - 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB4, 0x01, 0xCC, - 0xFC, 0x6C, 0x05, 0xDF, 0xF6, 0x68, 0x13, 0x0D, 0x45, 0x4B, 0xF8, - 0x49, 0x02, 0x88, 0xFF, 0xD4, 0xFF, 0x49, 0x00, 0xD3, 0xFF, 0x0B, - 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, 0xFC, - 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, 0x6F, - 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, - 0x01, 0x00, 0x0B, 0x00, 0xB5, 0xFF, 0xFD, 0x00, 0x81, 0xFD, 0x96, - 0x05, 0xE4, 0xF2, 0xFF, 0x3B, 0xA7, 0x22, 0xF5, 0xF2, 0xF1, 0x06, - 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, - 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, 0x90, 0x00, 0x7A, 0x00, - 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, 0xF8, 0x7E, 0x04, 0x3C, - 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x5C, 0xFF, 0x78, 0x01, 0x56, 0xFD, 0x48, 0x04, 0x56, - 0xF9, 0x3E, 0x0C, 0xAE, 0x47, 0x8D, 0xFC, 0x13, 0x00, 0xC9, 0x00, - 0x24, 0xFF, 0x9F, 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, - 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, 0xDD, 0x06, 0x37, 0xF3, - 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, 0x05, 0xA6, 0xFD, 0xE8, - 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, - 0x7E, 0xFF, 0x69, 0x01, 0xD2, 0xFC, 0x92, 0x06, 0xBB, 0xF1, 0xE6, - 0x35, 0x3F, 0x2A, 0xD8, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, - 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, - 0x00, 0xF1, 0xFF, 0x54, 0xFF, 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, - 0xB1, 0x14, 0x77, 0xF6, 0x9A, 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, - 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, - 0x2C, 0x01, 0xFB, 0xFD, 0xFE, 0x02, 0xEE, 0xFB, 0xAB, 0x05, 0xE1, - 0x48, 0xC7, 0x01, 0x9D, 0xFD, 0x1E, 0x02, 0x6F, 0xFE, 0xF5, 0x00, - 0x8F, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, - 0x01, 0x77, 0xFC, 0x33, 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, - 0x8D, 0xF5, 0xDA, 0x03, 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, - 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x56, 0xFF, 0xB4, 0x01, - 0x62, 0xFC, 0x15, 0x07, 0x84, 0xF1, 0x09, 0x2F, 0x7B, 0x31, 0x7D, - 0xF1, 0xF5, 0x06, 0x82, 0xFC, 0x9E, 0x01, 0x62, 0xFF, 0x28, 0x00, - 0xFD, 0xFF, 0x04, 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, - 0xFE, 0x6D, 0x04, 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, - 0x78, 0x06, 0x5C, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, - 0xFF, 0x18, 0x00, 0x9B, 0xFF, 0xD8, 0x00, 0xAD, 0xFE, 0xA7, 0x01, - 0x7D, 0xFE, 0xDD, 0xFF, 0xA1, 0x48, 0xDD, 0x07, 0x07, 0xFB, 0x73, - 0x03, 0xC0, 0xFD, 0x48, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, - 0x05, 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, - 0xBE, 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0xFD, - 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDD, 0x01, 0x33, 0xFC, 0x23, 0x07, - 0x26, 0xF2, 0xA6, 0x27, 0x1D, 0x38, 0x05, 0xF2, 0x49, 0x06, 0x08, - 0xFD, 0x47, 0x01, 0x8F, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, - 0x0E, 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, - 0xF2, 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, - 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBD, - 0xFF, 0x80, 0x00, 0x62, 0xFF, 0x57, 0x00, 0xDF, 0x00, 0xF7, 0xFA, - 0xED, 0x46, 0xAA, 0x0E, 0x76, 0xF8, 0xB2, 0x04, 0x23, 0xFD, 0x8F, - 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, - 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, - 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, - 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, - 0xFF, 0xE5, 0x01, 0x41, 0xFC, 0xC6, 0x06, 0x7F, 0xF3, 0xFD, 0x1F, - 0xE4, 0x3D, 0x87, 0xF3, 0x24, 0x05, 0xCC, 0xFD, 0xD1, 0x00, 0xCB, - 0xFF, 0x02, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, 0xFF, - 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, 0x89, - 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, 0xFF, - 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2C, 0x00, 0x0D, - 0x00, 0x21, 0xFF, 0xF9, 0x02, 0x0F, 0xF7, 0xD4, 0x43, 0xFD, 0x15, - 0x13, 0xF6, 0xC5, 0x05, 0xA5, 0xFC, 0xC4, 0x01, 0x40, 0xFF, 0x33, - 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0x1E, 0x01, - 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, 0x48, 0xC6, - 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, 0x8A, 0xFF, - 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD0, 0x01, 0x86, - 0xFC, 0x0D, 0x06, 0x66, 0xF5, 0x50, 0x18, 0x9E, 0x42, 0x11, 0xF6, - 0x8C, 0x03, 0xC9, 0xFE, 0x3F, 0x00, 0x14, 0x00, 0xE7, 0xFF, 0x07, - 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, 0x01, 0x56, 0xFC, - 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, 0x84, 0xF1, 0xE0, - 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, - 0x03, 0x00, 0xFA, 0xFF, 0xE0, 0xFF, 0xA7, 0x00, 0x15, 0xFE, 0xB2, - 0x04, 0x32, 0xF4, 0x77, 0x3F, 0x9E, 0x1D, 0x07, 0xF4, 0x96, 0x06, - 0x51, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, - 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, - 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, - 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0x2F, 0x00, 0x4C, 0xFF, 0xA1, 0x01, 0xF8, 0xFC, 0x0D, 0x05, 0xB3, - 0xF7, 0xDF, 0x10, 0x1D, 0x46, 0xA7, 0xF9, 0x8E, 0x01, 0xF4, 0xFF, - 0x98, 0xFF, 0x66, 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, - 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, 0xFC, 0x1A, 0x07, 0x56, 0xF2, - 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, 0x1E, 0x06, 0x25, 0xFD, 0x35, - 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x12, 0x00, - 0xA0, 0xFF, 0x26, 0x01, 0x3E, 0xFD, 0xFB, 0x05, 0x60, 0xF2, 0xFD, - 0x39, 0x4E, 0x25, 0x7F, 0xF2, 0x11, 0x07, 0x31, 0xFC, 0xE3, 0x01, - 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, - 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, - 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, - 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, - 0x5F, 0x01, 0x8D, 0xFD, 0xD9, 0x03, 0x3B, 0xFA, 0xE4, 0x09, 0x41, - 0x48, 0x41, 0xFE, 0x3F, 0xFF, 0x3E, 0x01, 0xE5, 0xFE, 0xBD, 0x00, - 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, - 0x01, 0x4A, 0xFC, 0xAC, 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, - 0xE4, 0xF3, 0xE5, 0x04, 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, - 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6E, 0xFF, 0x87, 0x01, - 0xA4, 0xFC, 0xCD, 0x06, 0x8E, 0xF1, 0x99, 0x33, 0xCE, 0x2C, 0xA1, - 0xF1, 0x25, 0x07, 0x4D, 0xFC, 0xC4, 0x01, 0x4D, 0xFF, 0x2F, 0x00, - 0xFD, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, - 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, - 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, - 0xFF, 0x1E, 0x00, 0x85, 0xFF, 0x10, 0x01, 0x38, 0xFE, 0x88, 0x02, - 0xD3, 0xFC, 0x91, 0x03, 0xF7, 0x48, 0xCB, 0x03, 0xBA, 0xFC, 0x95, - 0x02, 0x31, 0xFE, 0x13, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, 0x00, - 0xFE, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, - 0x05, 0xC6, 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, - 0xFA, 0xFE, 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0xFD, - 0xFF, 0x30, 0x00, 0x4C, 0xFF, 0xC6, 0x01, 0x4B, 0xFC, 0x26, 0x07, - 0xA5, 0xF1, 0x87, 0x2C, 0xDC, 0x33, 0x91, 0xF1, 0xC7, 0x06, 0xA9, - 0xFC, 0x84, 0x01, 0x70, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x03, 0x00, - 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, - 0xF3, 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, - 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA7, - 0xFF, 0xB9, 0x00, 0xEC, 0xFE, 0x31, 0x01, 0x57, 0xFF, 0x0F, 0xFE, - 0x33, 0x48, 0x25, 0x0A, 0x21, 0xFA, 0xE5, 0x03, 0x87, 0xFD, 0x62, - 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, - 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, - 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, - 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, - 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0E, 0x07, 0x8B, 0xF2, 0x03, 0x25, - 0x38, 0x3A, 0x6D, 0xF2, 0xF1, 0x05, 0x45, 0xFD, 0x22, 0x01, 0xA2, - 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, - 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, - 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, - 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x63, 0x00, 0x9F, - 0xFF, 0xE8, 0xFF, 0xA3, 0x01, 0x7F, 0xF9, 0x02, 0x46, 0x27, 0x11, - 0x9B, 0xF7, 0x18, 0x05, 0xF3, 0xFC, 0xA3, 0x01, 0x4C, 0xFF, 0x2F, - 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, 0x52, 0x01, - 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, 0x48, 0x26, - 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, 0xA0, 0xFF, - 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE1, 0x01, 0x53, - 0xFC, 0x90, 0x06, 0x19, 0xF4, 0x52, 0x1D, 0xA8, 0x3F, 0x49, 0xF4, - 0xA3, 0x04, 0x1E, 0xFE, 0xA1, 0x00, 0xE3, 0xFF, 0xF9, 0xFF, 0x04, - 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, 0xFC, - 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, 0x1D, - 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, - 0x07, 0x00, 0xE8, 0xFF, 0x11, 0x00, 0x45, 0x00, 0xBF, 0xFE, 0x9D, - 0x03, 0xF3, 0xF5, 0x75, 0x42, 0x9B, 0x18, 0x51, 0xF5, 0x16, 0x06, - 0x83, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, - 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, 0x4D, 0x02, 0x45, 0xFD, - 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, 0xFC, 0xD1, 0x02, 0x12, - 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0x33, 0x00, 0x40, 0xFF, 0xC2, 0x01, 0xA9, 0xFC, 0xBC, 0x05, 0x29, - 0xF6, 0xB3, 0x15, 0xFA, 0x43, 0x31, 0xF7, 0xE6, 0x02, 0x2C, 0xFF, - 0x07, 0x00, 0x2F, 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, - 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, 0x2A, 0x07, 0xBF, 0xF1, - 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, 0x06, 0xBF, 0xFC, 0x75, - 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, - 0xC8, 0xFF, 0xD6, 0x00, 0xC4, 0xFD, 0x31, 0x05, 0x73, 0xF3, 0xB0, - 0x3D, 0x49, 0x20, 0x6E, 0xF3, 0xCB, 0x06, 0x40, 0xFC, 0xE6, 0x01, - 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, - 0x00, 0x0C, 0xFF, 0xF7, 0x00, 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, - 0x51, 0x0B, 0xAF, 0xF9, 0x1D, 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, - 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, - 0x8C, 0x01, 0x29, 0xFD, 0xA7, 0x04, 0x8F, 0xF8, 0x64, 0x0E, 0x02, - 0x47, 0x22, 0xFB, 0xC9, 0x00, 0x64, 0x00, 0x5B, 0xFF, 0x84, 0x00, - 0xBC, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, - 0x01, 0x33, 0xFC, 0xFF, 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, - 0xAD, 0xF2, 0xBF, 0x05, 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, - 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8D, 0xFF, 0x4B, 0x01, - 0x01, 0xFD, 0x51, 0x06, 0xFB, 0xF1, 0xDF, 0x37, 0xF0, 0x27, 0x1C, - 0xF2, 0x24, 0x07, 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, - 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, - 0xFF, 0x01, 0x02, 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, - 0x48, 0x05, 0xDD, 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x45, 0x01, 0xC6, 0xFD, - 0x66, 0x03, 0x21, 0xFB, 0x9E, 0x07, 0xAA, 0x48, 0x12, 0x00, 0x64, - 0xFE, 0xB4, 0x01, 0xA6, 0xFE, 0xDB, 0x00, 0x9A, 0xFF, 0x19, 0x00, - 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, - 0x06, 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, - 0x49, 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFD, - 0xFF, 0x29, 0x00, 0x61, 0xFF, 0xA1, 0x01, 0x7E, 0xFC, 0xF9, 0x06, - 0x7C, 0xF1, 0x38, 0x31, 0x50, 0x2F, 0x82, 0xF1, 0x12, 0x07, 0x65, - 0xFC, 0xB2, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, - 0xED, 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, - 0xF5, 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, - 0xD7, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, - 0xFF, 0xF2, 0x00, 0x76, 0xFE, 0x11, 0x02, 0xB6, 0xFD, 0x8F, 0x01, - 0xDE, 0x48, 0xE9, 0x05, 0xD4, 0xFB, 0x0C, 0x03, 0xF4, 0xFD, 0x2F, - 0x01, 0x79, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, - 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, - 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, - 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, - 0xFF, 0xD4, 0x01, 0x3B, 0xFC, 0x2A, 0x07, 0xDF, 0xF1, 0xF6, 0x29, - 0x27, 0x36, 0xC1, 0xF1, 0x8B, 0x06, 0xD8, 0xFC, 0x66, 0x01, 0x80, - 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xBD, 0xFF, - 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, 0x9E, - 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, 0xFF, - 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x9B, 0x00, 0x2B, - 0xFF, 0xBD, 0x00, 0x2A, 0x00, 0x5E, 0xFC, 0x9A, 0x47, 0x82, 0x0C, - 0x3D, 0xF9, 0x54, 0x04, 0x50, 0xFD, 0x7A, 0x01, 0x5B, 0xFF, 0x2A, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x81, 0x01, - 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, 0x47, 0xEB, - 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, 0xB6, 0xFF, - 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, - 0xFC, 0xED, 0x06, 0x03, 0xF3, 0x5B, 0x22, 0x37, 0x3C, 0xF4, 0xF2, - 0x8A, 0x05, 0x89, 0xFD, 0xF9, 0x00, 0xB7, 0xFF, 0x0A, 0x00, 0x01, - 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, 0x01, 0xE6, 0xFC, - 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, 0xF3, 0xF1, 0x29, - 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, - 0x0B, 0x00, 0xD4, 0xFF, 0x46, 0x00, 0xDA, 0xFF, 0x7D, 0xFF, 0x5D, - 0x02, 0x26, 0xF8, 0xED, 0x44, 0xB1, 0x13, 0xC7, 0xF6, 0x77, 0x05, - 0xC8, 0xFC, 0xB6, 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, - 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, - 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, - 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, - 0x35, 0x00, 0x38, 0xFF, 0xD9, 0x01, 0x6C, 0xFC, 0x4F, 0x06, 0xC3, - 0xF4, 0xA9, 0x1A, 0x49, 0x41, 0x2C, 0xF5, 0x15, 0x04, 0x76, 0xFE, - 0x6E, 0x00, 0xFC, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, - 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, 0xFC, 0x0A, 0x07, 0x7E, 0xF1, - 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, 0x03, 0x07, 0x75, 0xFC, 0xA7, - 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF2, 0xFF, - 0xF7, 0xFF, 0x7A, 0x00, 0x62, 0xFE, 0x35, 0x04, 0xF7, 0xF4, 0xEF, - 0x40, 0x40, 0x1B, 0x9C, 0xF4, 0x5E, 0x06, 0x66, 0xFC, 0xDB, 0x01, - 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, - 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, - 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, - 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, - 0xB2, 0x01, 0xD1, 0xFC, 0x62, 0x05, 0xF6, 0xF6, 0x20, 0x13, 0x2E, - 0x45, 0x70, 0xF8, 0x34, 0x02, 0x94, 0xFF, 0xCD, 0xFF, 0x4C, 0x00, - 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, - 0x01, 0x36, 0xFC, 0x27, 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, - 0xE4, 0xF1, 0x67, 0x06, 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, - 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, 0x00, 0xB2, 0xFF, 0x02, 0x01, - 0x7A, 0xFD, 0xA2, 0x05, 0xD4, 0xF2, 0xC7, 0x3B, 0xF2, 0x22, 0xE7, - 0xF2, 0xF5, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, - 0xFD, 0xFF, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, - 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, - 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x75, 0x01, 0x5C, 0xFD, - 0x3C, 0x04, 0x70, 0xF9, 0xFA, 0x0B, 0xC0, 0x47, 0xBC, 0xFC, 0xFC, - 0xFF, 0xD6, 0x00, 0x1D, 0xFF, 0xA2, 0x00, 0xB0, 0xFF, 0x13, 0x00, - 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, - 0x06, 0x47, 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, - 0xAE, 0xFD, 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0xFE, - 0xFF, 0x1F, 0x00, 0x7C, 0xFF, 0x6D, 0x01, 0xCD, 0xFC, 0x99, 0x06, - 0xB4, 0xF1, 0xA6, 0x35, 0x89, 0x2A, 0xD0, 0xF1, 0x2B, 0x07, 0x3E, - 0xFC, 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, - 0xD9, 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, - 0xF7, 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, - 0xBE, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, - 0x00, 0x7B, 0xFF, 0x29, 0x01, 0x01, 0xFE, 0xF1, 0x02, 0x07, 0xFC, - 0x6E, 0x05, 0xE6, 0x48, 0xFF, 0x01, 0x84, 0xFD, 0x2C, 0x02, 0x68, - 0xFE, 0xF9, 0x00, 0x8E, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, - 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, - 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, - 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x55, - 0xFF, 0xB6, 0x01, 0x5F, 0xFC, 0x17, 0x07, 0x87, 0xF1, 0xC2, 0x2E, - 0xC0, 0x31, 0x7E, 0xF1, 0xF1, 0x06, 0x86, 0xFC, 0x9B, 0x01, 0x63, - 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, - 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, - 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, - 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9C, 0xFF, 0xD4, 0x00, 0xB4, - 0xFE, 0x9A, 0x01, 0x95, 0xFE, 0xA8, 0xFF, 0x98, 0x48, 0x1D, 0x08, - 0xEE, 0xFA, 0x80, 0x03, 0xB9, 0xFD, 0x4B, 0x01, 0x6E, 0xFF, 0x25, - 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA9, 0x01, - 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, 0x45, 0x1C, - 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, 0xCC, 0xFF, - 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDE, 0x01, 0x33, - 0xFC, 0x21, 0x07, 0x30, 0xF2, 0x5C, 0x27, 0x5B, 0x38, 0x0F, 0xF2, - 0x40, 0x06, 0x0E, 0xFD, 0x43, 0x01, 0x91, 0xFF, 0x18, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, 0xFD, - 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, 0x06, - 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, - 0x10, 0x00, 0xBE, 0xFF, 0x7D, 0x00, 0x69, 0xFF, 0x4B, 0x00, 0xF6, - 0x00, 0xCB, 0xFA, 0xD3, 0x46, 0xF0, 0x0E, 0x5E, 0xF8, 0xBE, 0x04, - 0x1E, 0xFD, 0x91, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, - 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, 0x77, 0xFD, 0x04, 0x04, - 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, 0xFD, 0x92, 0xFF, 0x10, - 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, - 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x43, 0xFC, 0xC0, 0x06, 0x8F, - 0xF3, 0xB1, 0x1F, 0x18, 0x3E, 0x9B, 0xF3, 0x16, 0x05, 0xD5, 0xFD, - 0xCC, 0x00, 0xCE, 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, - 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, 0xB8, 0x06, 0x9C, 0xF1, - 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, 0x07, 0x46, 0xFC, 0xCA, - 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, - 0x29, 0x00, 0x14, 0x00, 0x16, 0xFF, 0x0C, 0x03, 0xEE, 0xF6, 0xB0, - 0x43, 0x47, 0x16, 0xFC, 0xF5, 0xCF, 0x05, 0xA1, 0xFC, 0xC6, 0x01, - 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, - 0xFF, 0x1B, 0x01, 0x20, 0xFE, 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, - 0xF4, 0x48, 0xFF, 0x02, 0x13, 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, - 0x01, 0x88, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, - 0xCF, 0x01, 0x8A, 0xFC, 0x05, 0x06, 0x7B, 0xF5, 0x06, 0x18, 0xC7, - 0x42, 0x2F, 0xF6, 0x7A, 0x03, 0xD4, 0xFE, 0x39, 0x00, 0x17, 0x00, - 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, - 0x01, 0x53, 0xFC, 0x21, 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, - 0x86, 0xF1, 0xDB, 0x06, 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, - 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDE, 0xFF, 0xAC, 0x00, - 0x0B, 0xFE, 0xC1, 0x04, 0x1B, 0xF4, 0x47, 0x3F, 0xEA, 0x1D, 0xF5, - 0xF3, 0x9C, 0x06, 0x4F, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, - 0xFE, 0xFF, 0x16, 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, - 0x01, 0x03, 0xFF, 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, - 0xB9, 0x03, 0x9D, 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, - 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4D, 0xFF, 0x9F, 0x01, 0xFE, 0xFC, - 0x02, 0x05, 0xCB, 0xF7, 0x98, 0x10, 0x39, 0x46, 0xD0, 0xF9, 0x78, - 0x01, 0x00, 0x00, 0x91, 0xFF, 0x69, 0x00, 0xC6, 0xFF, 0x0E, 0x00, - 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, - 0x07, 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, - 0x2C, 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, - 0xFF, 0x13, 0x00, 0x9E, 0xFF, 0x2B, 0x01, 0x37, 0xFD, 0x05, 0x06, - 0x54, 0xF2, 0xC2, 0x39, 0x99, 0x25, 0x73, 0xF2, 0x14, 0x07, 0x31, - 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, - 0xC4, 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, - 0xFA, 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, - 0x9C, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, - 0x00, 0x67, 0xFF, 0x5C, 0x01, 0x93, 0xFD, 0xCC, 0x03, 0x54, 0xFA, - 0xA2, 0x09, 0x4F, 0x48, 0x73, 0xFE, 0x27, 0xFF, 0x4B, 0x01, 0xDE, - 0xFE, 0xC0, 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, - 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, - 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, - 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6D, - 0xFF, 0x8A, 0x01, 0x9F, 0xFC, 0xD3, 0x06, 0x8A, 0xF1, 0x57, 0x33, - 0x17, 0x2D, 0x9C, 0xF1, 0x24, 0x07, 0x4F, 0xFC, 0xC3, 0x01, 0x4E, - 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE4, 0xFF, 0x1B, 0x00, - 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, 0x96, - 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, 0xFF, - 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x86, 0xFF, 0x0C, 0x01, 0x3E, - 0xFE, 0x7B, 0x02, 0xED, 0xFC, 0x56, 0x03, 0xF5, 0x48, 0x06, 0x04, - 0xA1, 0xFC, 0xA3, 0x02, 0x2A, 0xFE, 0x16, 0x01, 0x83, 0xFF, 0x1F, - 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC8, 0x01, - 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, 0x43, 0xBD, - 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, 0xE1, 0xFF, - 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC8, 0x01, 0x49, - 0xFC, 0x27, 0x07, 0xAB, 0xF1, 0x3E, 0x2C, 0x1E, 0x34, 0x95, 0xF1, - 0xC1, 0x06, 0xAE, 0xFC, 0x81, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, - 0xFF, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, 0x00, 0xE2, 0xFD, - 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, 0xA8, 0xF3, 0xB8, - 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, - 0x15, 0x00, 0xA8, 0xFF, 0xB6, 0x00, 0xF3, 0xFE, 0x24, 0x01, 0x6E, - 0xFF, 0xDE, 0xFD, 0x25, 0x48, 0x68, 0x0A, 0x08, 0xFA, 0xF2, 0x03, - 0x81, 0xFD, 0x65, 0x01, 0x64, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, - 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, - 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, - 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, - 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0B, 0x07, 0x97, - 0xF2, 0xB8, 0x24, 0x71, 0x3A, 0x7B, 0xF2, 0xE6, 0x05, 0x4C, 0xFD, - 0x1D, 0x01, 0xA4, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, - 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, 0xFD, 0x32, 0x06, 0x1F, 0xF2, - 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, 0x1E, 0x07, 0x32, 0xFC, 0xDF, - 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, - 0x5F, 0x00, 0xA5, 0xFF, 0xDC, 0xFF, 0xB8, 0x01, 0x57, 0xF9, 0xE5, - 0x45, 0x6E, 0x11, 0x83, 0xF7, 0x23, 0x05, 0xEE, 0xFC, 0xA6, 0x01, - 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, - 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, - 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, - 0x00, 0x9E, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, - 0xE0, 0x01, 0x56, 0xFC, 0x89, 0x06, 0x2B, 0xF4, 0x06, 0x1D, 0xD7, - 0x3F, 0x61, 0xF4, 0x94, 0x04, 0x27, 0xFE, 0x9C, 0x00, 0xE6, 0xFF, - 0xF8, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, - 0x01, 0x8C, 0xFC, 0xEA, 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, - 0x8B, 0xF1, 0x1B, 0x07, 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, - 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, 0xFF, 0x0E, 0x00, 0x4B, 0x00, - 0xB4, 0xFE, 0xAF, 0x03, 0xD5, 0xF5, 0x4D, 0x42, 0xE6, 0x18, 0x3C, - 0xF5, 0x1F, 0x06, 0x7F, 0xFC, 0xD3, 0x01, 0x3B, 0xFF, 0x35, 0x00, - 0xFE, 0xFF, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, - 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, - 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, - 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xC1, 0x01, 0xAD, 0xFC, - 0xB2, 0x05, 0x3F, 0xF6, 0x69, 0x15, 0x1F, 0x44, 0x53, 0xF7, 0xD3, - 0x02, 0x38, 0xFF, 0x01, 0x00, 0x33, 0x00, 0xDB, 0xFF, 0x09, 0x00, - 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, - 0x07, 0xC6, 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, - 0xC4, 0xFC, 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, - 0x00, 0x04, 0x00, 0xC6, 0xFF, 0xDB, 0x00, 0xBB, 0xFD, 0x3E, 0x05, - 0x60, 0xF3, 0x7B, 0x3D, 0x94, 0x20, 0x5E, 0xF3, 0xD0, 0x06, 0x3E, - 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, - 0xAE, 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, - 0xFD, 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, - 0x71, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, - 0x00, 0x55, 0xFF, 0x8A, 0x01, 0x2E, 0xFD, 0x9B, 0x04, 0xA8, 0xF8, - 0x1F, 0x0E, 0x1A, 0x47, 0x4E, 0xFB, 0xB3, 0x00, 0x70, 0x00, 0x54, - 0xFF, 0x87, 0x00, 0xBB, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, - 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, - 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, - 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8B, - 0xFF, 0x4F, 0x01, 0xFB, 0xFC, 0x5A, 0x06, 0xF2, 0xF1, 0xA0, 0x37, - 0x3A, 0x28, 0x13, 0xF2, 0x25, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, - 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, - 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, - 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, - 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x42, - 0x01, 0xCD, 0xFD, 0x59, 0x03, 0x3B, 0xFB, 0x5E, 0x07, 0xB3, 0x48, - 0x48, 0x00, 0x4B, 0xFE, 0xC2, 0x01, 0x9F, 0xFE, 0xDE, 0x00, 0x98, - 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDD, 0x01, - 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, 0x40, 0xD0, - 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, 0xF4, 0xFF, - 0x05, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA3, 0x01, 0x7A, - 0xFC, 0xFD, 0x06, 0x7C, 0xF1, 0xF2, 0x30, 0x96, 0x2F, 0x80, 0xF1, - 0x0F, 0x07, 0x69, 0xFC, 0xB0, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, - 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, 0xFE, - 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, 0x43, - 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, - 0x1B, 0x00, 0x92, 0xFF, 0xEF, 0x00, 0x7D, 0xFE, 0x04, 0x02, 0xCF, - 0xFD, 0x58, 0x01, 0xD7, 0x48, 0x26, 0x06, 0xBB, 0xFB, 0x19, 0x03, - 0xED, 0xFD, 0x32, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, - 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, 0xC1, 0xFC, 0x86, 0x05, - 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, 0xF7, 0x7B, 0x02, 0x6B, - 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, - 0x33, 0x00, 0x43, 0xFF, 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE7, - 0xF1, 0xAC, 0x29, 0x66, 0x36, 0xC9, 0xF1, 0x83, 0x06, 0xDD, 0xFC, - 0x62, 0x01, 0x81, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x08, - 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, 0x78, 0x05, 0x0E, 0xF3, - 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, 0x06, 0x38, 0xFC, 0xE6, - 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB4, 0xFF, - 0x98, 0x00, 0x32, 0xFF, 0xB0, 0x00, 0x41, 0x00, 0x30, 0xFC, 0x86, - 0x47, 0xC6, 0x0C, 0x24, 0xF9, 0x60, 0x04, 0x4B, 0xFD, 0x7D, 0x01, - 0x5A, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x5A, - 0xFF, 0x7E, 0x01, 0x48, 0xFD, 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, - 0x7C, 0x47, 0x19, 0xFC, 0x4D, 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, - 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, - 0xE6, 0x01, 0x38, 0xFC, 0xE9, 0x06, 0x12, 0xF3, 0x10, 0x22, 0x6E, - 0x3C, 0x05, 0xF3, 0x7E, 0x05, 0x91, 0xFD, 0xF4, 0x00, 0xBA, 0xFF, - 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, - 0x01, 0xE0, 0xFC, 0x7F, 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, - 0xEB, 0xF1, 0x2A, 0x07, 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, - 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, 0xFF, 0x42, 0x00, 0xE1, 0xFF, - 0x71, 0xFF, 0x71, 0x02, 0x02, 0xF8, 0xCC, 0x44, 0xFA, 0x13, 0xB0, - 0xF6, 0x81, 0x05, 0xC3, 0xFC, 0xB8, 0x01, 0x44, 0xFF, 0x31, 0x00, - 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, - 0xFD, 0x1F, 0x03, 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, - 0xDC, 0xFD, 0xFD, 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, - 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x6F, 0xFC, - 0x47, 0x06, 0xD7, 0xF4, 0x5D, 0x1A, 0x74, 0x41, 0x48, 0xF5, 0x04, - 0x04, 0x80, 0xFE, 0x69, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0x05, 0x00, - 0xFD, 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, - 0x07, 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, - 0x78, 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, - 0x00, 0xF3, 0xFF, 0xF4, 0xFF, 0x80, 0x00, 0x58, 0xFE, 0x46, 0x04, - 0xDD, 0xF4, 0xC3, 0x40, 0x8C, 0x1B, 0x89, 0xF4, 0x66, 0x06, 0x63, - 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, - 0x98, 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, - 0x00, 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, - 0x40, 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, - 0x00, 0x47, 0xFF, 0xB0, 0x01, 0xD6, 0xFC, 0x58, 0x05, 0x0D, 0xF7, - 0xD7, 0x12, 0x4E, 0x45, 0x96, 0xF8, 0x20, 0x02, 0xA0, 0xFF, 0xC7, - 0xFF, 0x4F, 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, - 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, - 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, - 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0xB0, - 0xFF, 0x07, 0x01, 0x72, 0xFD, 0xAE, 0x05, 0xC4, 0xF2, 0x90, 0x3B, - 0x3F, 0x23, 0xD9, 0xF2, 0xF9, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, - 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, 0x00, - 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, 0xFC, - 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, 0xFF, - 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5E, 0xFF, 0x72, - 0x01, 0x62, 0xFD, 0x2F, 0x04, 0x89, 0xF9, 0xB6, 0x0B, 0xD2, 0x47, - 0xEB, 0xFC, 0xE4, 0xFF, 0xE3, 0x00, 0x16, 0xFF, 0xA5, 0x00, 0xAF, - 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, - 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, 0x3D, 0x56, - 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, 0x05, 0x00, - 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x7A, 0xFF, 0x70, 0x01, 0xC7, - 0xFC, 0xA0, 0x06, 0xAE, 0xF1, 0x65, 0x35, 0xD1, 0x2A, 0xCA, 0xF1, - 0x2A, 0x07, 0x40, 0xFC, 0xD0, 0x01, 0x47, 0xFF, 0x32, 0x00, 0xFD, - 0xFF, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x3D, 0xFF, - 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, 0x4A, 0xF6, 0xAD, - 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, - 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, 0x26, 0x01, 0x08, 0xFE, 0xE4, - 0x02, 0x21, 0xFC, 0x31, 0x05, 0xEB, 0x48, 0x37, 0x02, 0x6B, 0xFD, - 0x39, 0x02, 0x61, 0xFE, 0xFC, 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFE, - 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, - 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, - 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFD, 0xFF, - 0x2D, 0x00, 0x54, 0xFF, 0xB8, 0x01, 0x5D, 0xFC, 0x1A, 0x07, 0x8A, - 0xF1, 0x7B, 0x2E, 0x04, 0x32, 0x7F, 0xF1, 0xEC, 0x06, 0x8A, 0xFC, - 0x98, 0x01, 0x65, 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, - 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, 0xFE, 0x8C, 0x04, 0x6D, 0xF4, - 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, 0x85, 0x06, 0x57, 0xFC, 0xE0, - 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9E, 0xFF, - 0xD1, 0x00, 0xBB, 0xFE, 0x8D, 0x01, 0xAE, 0xFE, 0x74, 0xFF, 0x8D, - 0x48, 0x5D, 0x08, 0xD4, 0xFA, 0x8D, 0x03, 0xB3, 0xFD, 0x4E, 0x01, - 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, - 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, - 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, - 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3D, 0xFF, - 0xDF, 0x01, 0x32, 0xFC, 0x1F, 0x07, 0x3B, 0xF2, 0x11, 0x27, 0x97, - 0x38, 0x19, 0xF2, 0x36, 0x06, 0x15, 0xFD, 0x3F, 0x01, 0x93, 0xFF, - 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, - 0x01, 0x50, 0xFD, 0xE1, 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, - 0x9D, 0xF2, 0x09, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, - 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0x7A, 0x00, 0x70, 0xFF, - 0x3E, 0x00, 0x0C, 0x01, 0xA1, 0xFA, 0xBB, 0x46, 0x36, 0x0F, 0x45, - 0xF8, 0xC9, 0x04, 0x18, 0xFD, 0x93, 0x01, 0x52, 0xFF, 0x2D, 0x00, - 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, - 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, - 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, - 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x45, 0xFC, - 0xBB, 0x06, 0xA0, 0xF3, 0x64, 0x1F, 0x4A, 0x3E, 0xB0, 0xF3, 0x08, - 0x05, 0xDE, 0xFD, 0xC7, 0x00, 0xD0, 0xFF, 0x00, 0x00, 0x02, 0x00, - 0xFE, 0xFF, 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, - 0x06, 0x97, 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, - 0x48, 0xFC, 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, - 0x00, 0xE0, 0xFF, 0x26, 0x00, 0x1A, 0x00, 0x0B, 0xFF, 0x1E, 0x03, - 0xCD, 0xF6, 0x89, 0x43, 0x91, 0x16, 0xE7, 0xF5, 0xD8, 0x05, 0x9D, - 0xFC, 0xC7, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, - 0x1F, 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, - 0xFC, 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, - 0x42, 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, - 0x00, 0x3C, 0xFF, 0xCD, 0x01, 0x8E, 0xFC, 0xFC, 0x05, 0x90, 0xF5, - 0xBB, 0x17, 0xEE, 0x42, 0x4E, 0xF6, 0x68, 0x03, 0xDF, 0xFE, 0x33, - 0x00, 0x1A, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2F, 0x00, - 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, - 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, - 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0xDB, - 0xFF, 0xB2, 0x00, 0x02, 0xFE, 0xCF, 0x04, 0x05, 0xF4, 0x16, 0x3F, - 0x36, 0x1E, 0xE4, 0xF3, 0xA3, 0x06, 0x4D, 0xFC, 0xE3, 0x01, 0x36, - 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, - 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, - 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, - 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9D, - 0x01, 0x03, 0xFD, 0xF7, 0x04, 0xE3, 0xF7, 0x51, 0x10, 0x55, 0x46, - 0xF9, 0xF9, 0x63, 0x01, 0x0D, 0x00, 0x8B, 0xFF, 0x6D, 0x00, 0xC5, - 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, - 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, 0x39, 0x4D, - 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, 0x13, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x9C, 0xFF, 0x2F, 0x01, 0x30, - 0xFD, 0x10, 0x06, 0x47, 0xF2, 0x87, 0x39, 0xE5, 0x25, 0x67, 0xF2, - 0x16, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, - 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, 0x00, - 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, 0xFC, - 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, - 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x59, 0x01, 0x99, 0xFD, 0xC0, - 0x03, 0x6E, 0xFA, 0x61, 0x09, 0x5D, 0x48, 0xA6, 0xFE, 0x0F, 0xFF, - 0x58, 0x01, 0xD7, 0xFE, 0xC3, 0x00, 0xA3, 0xFF, 0x16, 0x00, 0xFE, - 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4E, 0xFC, 0xA0, 0x06, - 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, 0xF4, 0xC8, 0x04, 0x07, - 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, 0x03, 0x00, 0xFD, 0xFF, - 0x25, 0x00, 0x6B, 0xFF, 0x8D, 0x01, 0x9B, 0xFC, 0xD8, 0x06, 0x87, - 0xF1, 0x13, 0x33, 0x5E, 0x2D, 0x98, 0xF1, 0x22, 0x07, 0x52, 0xFC, - 0xC1, 0x01, 0x4F, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE5, - 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, 0x71, 0x03, 0x3F, 0xF6, - 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, 0x06, 0x8C, 0xFC, 0xCE, - 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, - 0x09, 0x01, 0x45, 0xFE, 0x6E, 0x02, 0x06, 0xFD, 0x1C, 0x03, 0xF4, - 0x48, 0x41, 0x04, 0x87, 0xFC, 0xB0, 0x02, 0x23, 0xFE, 0x19, 0x01, - 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, - 0xFF, 0xC6, 0x01, 0x9F, 0xFC, 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, - 0x9E, 0x43, 0xDD, 0xF6, 0x15, 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, - 0x00, 0xDF, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, - 0xCA, 0x01, 0x47, 0xFC, 0x28, 0x07, 0xB0, 0xF1, 0xF5, 0x2B, 0x60, - 0x34, 0x9A, 0xF1, 0xBB, 0x06, 0xB3, 0xFC, 0x7D, 0x01, 0x73, 0xFF, - 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, - 0x00, 0xDA, 0xFD, 0x0F, 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, - 0x97, 0xF3, 0xBD, 0x06, 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, - 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xAA, 0xFF, 0xB3, 0x00, 0xFA, 0xFE, - 0x17, 0x01, 0x86, 0xFF, 0xAC, 0xFD, 0x16, 0x48, 0xAA, 0x0A, 0xEE, - 0xF9, 0xFE, 0x03, 0x7A, 0xFD, 0x67, 0x01, 0x63, 0xFF, 0x28, 0x00, - 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, - 0xFD, 0xC4, 0x04, 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, - 0x01, 0x01, 0x44, 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, - 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, - 0x08, 0x07, 0xA4, 0xF2, 0x6D, 0x24, 0xAD, 0x3A, 0x88, 0xF2, 0xDB, - 0x05, 0x53, 0xFD, 0x19, 0x01, 0xA7, 0xFF, 0x10, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, - 0x06, 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, - 0x33, 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, - 0x00, 0xCB, 0xFF, 0x5C, 0x00, 0xAC, 0xFF, 0xD0, 0xFF, 0xCD, 0x01, - 0x30, 0xF9, 0xC8, 0x45, 0xB6, 0x11, 0x6B, 0xF7, 0x2D, 0x05, 0xE9, - 0xFC, 0xA8, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, - 0x25, 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, - 0xFA, 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, - 0xB8, 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, - 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x58, 0xFC, 0x82, 0x06, 0x3E, 0xF4, - 0xBA, 0x1C, 0x07, 0x40, 0x79, 0xF4, 0x84, 0x04, 0x31, 0xFE, 0x96, - 0x00, 0xE8, 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, - 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, - 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, - 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEA, 0xFF, 0x0B, - 0x00, 0x51, 0x00, 0xAA, 0xFE, 0xC0, 0x03, 0xB8, 0xF5, 0x21, 0x42, - 0x31, 0x19, 0x28, 0xF5, 0x27, 0x06, 0x7C, 0xFC, 0xD4, 0x01, 0x3A, - 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, 0x00, - 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, 0x50, - 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, 0xFF, - 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xBF, - 0x01, 0xB2, 0xFC, 0xA9, 0x05, 0x55, 0xF6, 0x20, 0x15, 0x42, 0x44, - 0x75, 0xF7, 0xBF, 0x02, 0x43, 0xFF, 0xFA, 0xFF, 0x36, 0x00, 0xDA, - 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, - 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, 0x35, 0xB1, - 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, 0x20, 0x00, - 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE0, 0x00, 0xB3, - 0xFD, 0x4B, 0x05, 0x4D, 0xF3, 0x45, 0x3D, 0xE0, 0x20, 0x4F, 0xF3, - 0xD5, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, - 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, 0xFF, 0xDD, 0x00, - 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, 0x7C, 0xF9, 0x35, - 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x2C, 0x00, 0x56, 0xFF, 0x87, 0x01, 0x34, 0xFD, 0x8F, - 0x04, 0xC0, 0xF8, 0xD9, 0x0D, 0x31, 0x47, 0x7B, 0xFB, 0x9C, 0x00, - 0x7D, 0x00, 0x4D, 0xFF, 0x8A, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, - 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, - 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, - 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, - 0x1A, 0x00, 0x89, 0xFF, 0x53, 0x01, 0xF5, 0xFC, 0x63, 0x06, 0xE9, - 0xF1, 0x63, 0x37, 0x85, 0x28, 0x09, 0xF2, 0x27, 0x07, 0x35, 0xFC, - 0xDA, 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD1, - 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, 0xFF, 0x2A, 0x02, 0x83, 0xF8, - 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, 0x5D, 0x05, 0xD3, 0xFC, 0xB1, - 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, - 0x73, 0xFF, 0x3F, 0x01, 0xD3, 0xFD, 0x4C, 0x03, 0x54, 0xFB, 0x1F, - 0x07, 0xBB, 0x48, 0x7D, 0x00, 0x33, 0xFE, 0xCF, 0x01, 0x98, 0xFE, - 0xE2, 0x00, 0x97, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, - 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, - 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, - 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5E, 0xFF, - 0xA6, 0x01, 0x76, 0xFC, 0x01, 0x07, 0x7D, 0xF1, 0xAD, 0x30, 0xDC, - 0x2F, 0x7F, 0xF1, 0x0C, 0x07, 0x6C, 0xFC, 0xAD, 0x01, 0x5A, 0xFF, - 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, - 0x00, 0x7B, 0xFE, 0x0C, 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, - 0xCD, 0xF4, 0x4B, 0x06, 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, - 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x93, 0xFF, 0xEC, 0x00, 0x83, 0xFE, - 0xF7, 0x01, 0xE8, 0xFD, 0x21, 0x01, 0xD2, 0x48, 0x64, 0x06, 0xA1, - 0xFB, 0x26, 0x03, 0xE7, 0xFD, 0x35, 0x01, 0x76, 0xFF, 0x22, 0x00, - 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, - 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, - 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, - 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x39, 0xFC, - 0x29, 0x07, 0xEF, 0xF1, 0x62, 0x29, 0xA5, 0x36, 0xD0, 0xF1, 0x7B, - 0x06, 0xE3, 0xFC, 0x5E, 0x01, 0x83, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, - 0x01, 0x00, 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, - 0x05, 0xFD, 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, - 0x37, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, - 0x00, 0xB5, 0xFF, 0x94, 0x00, 0x39, 0xFF, 0xA3, 0x00, 0x58, 0x00, - 0x02, 0xFC, 0x73, 0x47, 0x0B, 0x0D, 0x0B, 0xF9, 0x6C, 0x04, 0x45, - 0xFD, 0x80, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, - 0xF9, 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, - 0x2E, 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, - 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x39, 0xFC, 0xE4, 0x06, 0x21, 0xF3, - 0xC4, 0x21, 0xA5, 0x3C, 0x16, 0xF3, 0x72, 0x05, 0x9A, 0xFD, 0xEF, - 0x00, 0xBC, 0xFF, 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, - 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, - 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, - 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD6, 0xFF, 0x3F, - 0x00, 0xE7, 0xFF, 0x65, 0xFF, 0x85, 0x02, 0xDE, 0xF7, 0xA9, 0x44, - 0x43, 0x14, 0x99, 0xF6, 0x8B, 0x05, 0xBF, 0xFC, 0xBA, 0x01, 0x43, - 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, - 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, - 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, - 0x91, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, - 0x01, 0x72, 0xFC, 0x3F, 0x06, 0xEB, 0xF4, 0x12, 0x1A, 0xA1, 0x41, - 0x63, 0xF5, 0xF3, 0x03, 0x8A, 0xFE, 0x63, 0x00, 0x02, 0x00, 0xEE, - 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, 0xB1, 0x01, - 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, 0x31, 0x7C, - 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, 0x29, 0x00, - 0xFD, 0xFF, 0x04, 0x00, 0xF4, 0xFF, 0xF1, 0xFF, 0x85, 0x00, 0x4E, - 0xFE, 0x56, 0x04, 0xC3, 0xF4, 0x95, 0x40, 0xD8, 0x1B, 0x76, 0xF4, - 0x6D, 0x06, 0x60, 0xFC, 0xDD, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, - 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, 0x01, - 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, 0x60, - 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAE, 0x01, 0xDB, 0xFC, 0x4D, - 0x05, 0x24, 0xF7, 0x8E, 0x12, 0x6D, 0x45, 0xBC, 0xF8, 0x0C, 0x02, - 0xAC, 0xFF, 0xC0, 0xFF, 0x52, 0x00, 0xCF, 0xFF, 0x0C, 0x00, 0xFD, - 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x25, 0x07, - 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, 0xF1, 0x56, 0x06, 0xFE, - 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, - 0x0D, 0x00, 0xAE, 0xFF, 0x0B, 0x01, 0x6A, 0xFD, 0xBA, 0x05, 0xB4, - 0xF2, 0x58, 0x3B, 0x8A, 0x23, 0xCB, 0xF2, 0xFD, 0x06, 0x34, 0xFC, - 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBB, - 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, 0xBE, 0x00, 0x38, 0xFB, - 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, 0x04, 0x2B, 0xFD, 0x8B, - 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, - 0x5F, 0xFF, 0x70, 0x01, 0x68, 0xFD, 0x23, 0x04, 0xA2, 0xF9, 0x73, - 0x0B, 0xE4, 0x47, 0x1B, 0xFD, 0xCD, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, - 0xA9, 0x00, 0xAE, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, - 0xFF, 0xE6, 0x01, 0x3F, 0xFC, 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, - 0x96, 0x3D, 0x69, 0xF3, 0x38, 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, - 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x78, 0xFF, - 0x74, 0x01, 0xC2, 0xFC, 0xA7, 0x06, 0xA8, 0xF1, 0x25, 0x35, 0x1B, - 0x2B, 0xC2, 0xF1, 0x2A, 0x07, 0x41, 0xFC, 0xCE, 0x01, 0x47, 0xFF, - 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, - 0x00, 0x32, 0xFF, 0xDC, 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, - 0x34, 0xF6, 0xB7, 0x05, 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, - 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x23, 0x01, - 0x0F, 0xFE, 0xD7, 0x02, 0x3B, 0xFC, 0xF5, 0x04, 0xED, 0x48, 0x70, - 0x02, 0x52, 0xFD, 0x46, 0x02, 0x5A, 0xFE, 0xFF, 0x00, 0x8B, 0xFF, - 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, - 0xFC, 0x1A, 0x06, 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, - 0xA6, 0x03, 0xB9, 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, - 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x53, 0xFF, 0xBB, 0x01, 0x5A, 0xFC, - 0x1C, 0x07, 0x8D, 0xF1, 0x34, 0x2E, 0x48, 0x32, 0x81, 0xF1, 0xE7, - 0x06, 0x8E, 0xFC, 0x96, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, - 0x04, 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, - 0x04, 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, - 0x55, 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, - 0x00, 0x9F, 0xFF, 0xCE, 0x00, 0xC2, 0xFE, 0x80, 0x01, 0xC6, 0xFE, - 0x40, 0xFF, 0x81, 0x48, 0x9E, 0x08, 0xBA, 0xFA, 0x9A, 0x03, 0xAC, - 0xFD, 0x51, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0x2F, 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, - 0xF7, 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, - 0xA2, 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, - 0x00, 0x3D, 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1D, 0x07, 0x45, 0xF2, - 0xC6, 0x26, 0xD3, 0x38, 0x24, 0xF2, 0x2D, 0x06, 0x1B, 0xFD, 0x3B, - 0x01, 0x95, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, - 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, - 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, - 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC1, 0xFF, 0x76, - 0x00, 0x76, 0xFF, 0x32, 0x00, 0x22, 0x01, 0x76, 0xFA, 0xA3, 0x46, - 0x7D, 0x0F, 0x2C, 0xF8, 0xD5, 0x04, 0x13, 0xFD, 0x96, 0x01, 0x51, - 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, 0xFF, - 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, 0x2C, - 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, 0x00, - 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, - 0x01, 0x47, 0xFC, 0xB5, 0x06, 0xB0, 0xF3, 0x19, 0x1F, 0x7E, 0x3E, - 0xC4, 0xF3, 0xFA, 0x04, 0xE7, 0xFD, 0xC1, 0x00, 0xD3, 0xFF, 0xFF, - 0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, 0x82, 0x01, - 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, 0x2C, 0xA8, - 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, 0x30, 0x00, - 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, 0x23, 0x00, 0x20, 0x00, 0x00, - 0xFF, 0x31, 0x03, 0xAD, 0xF6, 0x65, 0x43, 0xDC, 0x16, 0xD1, 0xF5, - 0xE1, 0x05, 0x99, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, 0x01, 0x2D, 0xFE, - 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, 0x73, 0x03, 0xE0, - 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, 0xFF, 0x1E, 0x00, - 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCC, 0x01, 0x91, 0xFC, 0xF3, - 0x05, 0xA6, 0xF5, 0x70, 0x17, 0x17, 0x43, 0x6D, 0xF6, 0x56, 0x03, - 0xEA, 0xFE, 0x2D, 0x00, 0x1D, 0x00, 0xE4, 0xFF, 0x08, 0x00, 0xFD, - 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, - 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, - 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, - 0xFD, 0xFF, 0xD8, 0xFF, 0xB7, 0x00, 0xF9, 0xFD, 0xDE, 0x04, 0xEF, - 0xF3, 0xE4, 0x3E, 0x81, 0x1E, 0xD2, 0xF3, 0xA9, 0x06, 0x4B, 0xFC, - 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA5, - 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, 0x01, 0x33, 0xFF, 0x5A, 0xFE, - 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, 0xD2, 0x03, 0x90, 0xFD, 0x5E, - 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, - 0x4F, 0xFF, 0x9A, 0x01, 0x08, 0xFD, 0xEB, 0x04, 0xFC, 0xF7, 0x0A, - 0x10, 0x70, 0x46, 0x22, 0xFA, 0x4D, 0x01, 0x19, 0x00, 0x84, 0xFF, - 0x70, 0x00, 0xC4, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, - 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, - 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, - 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x99, 0xFF, - 0x33, 0x01, 0x29, 0xFD, 0x1A, 0x06, 0x3B, 0xF2, 0x4B, 0x39, 0x30, - 0x26, 0x5B, 0xF2, 0x19, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, - 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, - 0xFF, 0xFA, 0xFF, 0x83, 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, - 0xBF, 0xF7, 0x07, 0x05, 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, - 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x56, 0x01, - 0xA0, 0xFD, 0xB3, 0x03, 0x87, 0xFA, 0x1F, 0x09, 0x6A, 0x48, 0xD9, - 0xFE, 0xF6, 0xFE, 0x65, 0x01, 0xD0, 0xFE, 0xC7, 0x00, 0xA2, 0xFF, - 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, - 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, - 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, - 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x69, 0xFF, 0x90, 0x01, 0x96, 0xFC, - 0xDD, 0x06, 0x85, 0xF1, 0xD0, 0x32, 0xA6, 0x2D, 0x94, 0xF1, 0x20, - 0x07, 0x54, 0xFC, 0xBF, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, - 0x07, 0x00, 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, - 0x03, 0x20, 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, - 0x88, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, - 0x00, 0x89, 0xFF, 0x06, 0x01, 0x4C, 0xFE, 0x60, 0x02, 0x1F, 0xFD, - 0xE2, 0x02, 0xF3, 0x48, 0x7D, 0x04, 0x6E, 0xFC, 0xBD, 0x02, 0x1C, - 0xFE, 0x1C, 0x01, 0x80, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0x33, 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, - 0xF6, 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, - 0x11, 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, - 0x00, 0x49, 0xFF, 0xCB, 0x01, 0x45, 0xFC, 0x29, 0x07, 0xB6, 0xF1, - 0xAD, 0x2B, 0xA2, 0x34, 0x9E, 0xF1, 0xB4, 0x06, 0xB8, 0xFC, 0x7A, - 0x01, 0x75, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, - 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, - 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, - 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAB, 0xFF, 0xAF, - 0x00, 0x01, 0xFF, 0x0A, 0x01, 0x9E, 0xFF, 0x7C, 0xFD, 0x03, 0x48, - 0xED, 0x0A, 0xD5, 0xF9, 0x0A, 0x04, 0x74, 0xFD, 0x6A, 0x01, 0x62, - 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, - 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, - 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, - 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, - 0x01, 0x33, 0xFC, 0x04, 0x07, 0xB1, 0xF2, 0x21, 0x24, 0xE6, 0x3A, - 0x97, 0xF2, 0xD0, 0x05, 0x5B, 0xFD, 0x15, 0x01, 0xA9, 0xFF, 0x0F, - 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, 0x45, 0x01, - 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, 0x27, 0x2B, - 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, 0x34, 0x00, - 0xFD, 0xFF, 0x0D, 0x00, 0xCD, 0xFF, 0x59, 0x00, 0xB3, 0xFF, 0xC4, - 0xFF, 0xE2, 0x01, 0x09, 0xF9, 0xAA, 0x45, 0xFE, 0x11, 0x54, 0xF7, - 0x38, 0x05, 0xE4, 0xFC, 0xAA, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, 0xFD, - 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, 0x89, - 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, 0x00, - 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5B, 0xFC, 0x7B, - 0x06, 0x50, 0xF4, 0x6E, 0x1C, 0x36, 0x40, 0x92, 0xF4, 0x75, 0x04, - 0x3B, 0xFE, 0x91, 0x00, 0xEB, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, - 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, 0x84, 0xFC, 0xF3, 0x06, - 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, 0xF1, 0x16, 0x07, 0x61, - 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, - 0xEC, 0xFF, 0x08, 0x00, 0x57, 0x00, 0x9F, 0xFE, 0xD1, 0x03, 0x9B, - 0xF5, 0xF7, 0x41, 0x7C, 0x19, 0x13, 0xF5, 0x2F, 0x06, 0x78, 0xFC, - 0xD5, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8F, - 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, 0x91, 0xFD, 0xE3, 0x01, - 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, 0x02, 0xFE, 0xFD, 0x2B, - 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, - 0x42, 0xFF, 0xBD, 0x01, 0xB6, 0xFC, 0x9F, 0x05, 0x6C, 0xF6, 0xD6, - 0x14, 0x65, 0x44, 0x98, 0xF7, 0xAC, 0x02, 0x4E, 0xFF, 0xF4, 0xFF, - 0x39, 0x00, 0xD9, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, - 0xFF, 0xD2, 0x01, 0x3D, 0xFC, 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, - 0xC6, 0x35, 0xB7, 0xF1, 0x96, 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, - 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x06, 0x00, 0xC1, 0xFF, - 0xE5, 0x00, 0xAA, 0xFD, 0x58, 0x05, 0x3A, 0xF3, 0x11, 0x3D, 0x2C, - 0x21, 0x3F, 0xF3, 0xDA, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, - 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, - 0xFF, 0xD0, 0x00, 0x07, 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, - 0x63, 0xF9, 0x42, 0x04, 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x85, 0x01, - 0x39, 0xFD, 0x84, 0x04, 0xD9, 0xF8, 0x95, 0x0D, 0x48, 0x47, 0xA7, - 0xFB, 0x86, 0x00, 0x8A, 0x00, 0x46, 0xFF, 0x8E, 0x00, 0xB8, 0xFF, - 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, - 0xFC, 0xF3, 0x06, 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, - 0x9C, 0x05, 0x7E, 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, - 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x87, 0xFF, 0x57, 0x01, 0xEF, 0xFC, - 0x6B, 0x06, 0xE0, 0xF1, 0x23, 0x37, 0xCE, 0x28, 0x01, 0xF2, 0x28, - 0x07, 0x36, 0xFC, 0xD9, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, - 0x0B, 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, - 0x02, 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, - 0xCF, 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, - 0x00, 0x23, 0x00, 0x74, 0xFF, 0x3C, 0x01, 0xDA, 0xFD, 0x40, 0x03, - 0x6E, 0xFB, 0xE1, 0x06, 0xC3, 0x48, 0xB3, 0x00, 0x1A, 0xFE, 0xDC, - 0x01, 0x91, 0xFE, 0xE5, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, - 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, - 0xF4, 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, - 0x77, 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, - 0x00, 0x5C, 0xFF, 0xA8, 0x01, 0x73, 0xFC, 0x05, 0x07, 0x7D, 0xF1, - 0x67, 0x30, 0x21, 0x30, 0x7E, 0xF1, 0x08, 0x07, 0x6F, 0xFC, 0xAB, - 0x01, 0x5B, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF0, 0xFF, - 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, - 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, - 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE8, - 0x00, 0x8A, 0xFE, 0xE9, 0x01, 0x01, 0xFE, 0xEA, 0x00, 0xCB, 0x48, - 0xA2, 0x06, 0x87, 0xFB, 0x33, 0x03, 0xE0, 0xFD, 0x39, 0x01, 0x75, - 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, - 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, 0xFD, - 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, 0x00, - 0xD3, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD8, - 0x01, 0x37, 0xFC, 0x29, 0x07, 0xF8, 0xF1, 0x19, 0x29, 0xE5, 0x36, - 0xD8, 0xF1, 0x73, 0x06, 0xE9, 0xFC, 0x5B, 0x01, 0x85, 0xFF, 0x1C, - 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, 0xFB, 0x00, - 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, 0x22, 0xFC, - 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, - 0xFD, 0xFF, 0x12, 0x00, 0xB7, 0xFF, 0x91, 0x00, 0x40, 0xFF, 0x96, - 0x00, 0x6F, 0x00, 0xD5, 0xFB, 0x5E, 0x47, 0x50, 0x0D, 0xF2, 0xF8, - 0x78, 0x04, 0x3F, 0xFD, 0x82, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, 0x01, 0x53, 0xFD, - 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, 0x76, 0xFC, 0x1F, - 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, 0xFF, 0x13, 0x00, - 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xDF, - 0x06, 0x30, 0xF3, 0x78, 0x21, 0xDB, 0x3C, 0x28, 0xF3, 0x65, 0x05, - 0xA2, 0xFD, 0xEA, 0x00, 0xBE, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, - 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, - 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, - 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, - 0xD8, 0xFF, 0x3C, 0x00, 0xEE, 0xFF, 0x5A, 0xFF, 0x98, 0x02, 0xBB, - 0xF7, 0x87, 0x44, 0x8C, 0x14, 0x83, 0xF6, 0x95, 0x05, 0xBA, 0xFC, - 0xBB, 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, - 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, 0xFD, 0x05, 0x03, 0xE1, 0xFB, - 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, 0xAA, 0xFD, 0x18, 0x02, 0x72, - 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, - 0x39, 0xFF, 0xD6, 0x01, 0x75, 0xFC, 0x37, 0x06, 0xFF, 0xF4, 0xC7, - 0x19, 0xCC, 0x41, 0x7F, 0xF5, 0xE2, 0x03, 0x95, 0xFE, 0x5D, 0x00, - 0x05, 0x00, 0xED, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, - 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, - 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, - 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF5, 0xFF, 0xEE, 0xFF, - 0x8B, 0x00, 0x44, 0xFE, 0x65, 0x04, 0xAA, 0xF4, 0x66, 0x40, 0x23, - 0x1C, 0x63, 0xF4, 0x74, 0x06, 0x5D, 0xFC, 0xDE, 0x01, 0x37, 0xFF, - 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, - 0xFE, 0xAE, 0x01, 0x70, 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, - 0x14, 0xFB, 0x6D, 0x03, 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, - 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAC, 0x01, - 0xDF, 0xFC, 0x43, 0x05, 0x3C, 0xF7, 0x46, 0x12, 0x8D, 0x45, 0xE2, - 0xF8, 0xF7, 0x01, 0xB8, 0xFF, 0xB9, 0xFF, 0x56, 0x00, 0xCE, 0xFF, - 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, - 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, - 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAB, 0xFF, 0x10, 0x01, 0x62, 0xFD, - 0xC5, 0x05, 0xA5, 0xF2, 0x1F, 0x3B, 0xD6, 0x23, 0xBE, 0xF2, 0x01, - 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, - 0x10, 0x00, 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, - 0x00, 0x0C, 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, - 0x26, 0xFD, 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, - 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6D, 0x01, 0x6E, 0xFD, 0x17, 0x04, - 0xBC, 0xF9, 0x30, 0x0B, 0xF4, 0x47, 0x4B, 0xFD, 0xB5, 0xFF, 0xFD, - 0x00, 0x08, 0xFF, 0xAC, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, - 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, - 0xF3, 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, - 0xD4, 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, - 0x00, 0x77, 0xFF, 0x77, 0x01, 0xBD, 0xFC, 0xAE, 0x06, 0xA3, 0xF1, - 0xE3, 0x34, 0x64, 0x2B, 0xBC, 0xF1, 0x2A, 0x07, 0x43, 0xFC, 0xCD, - 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDD, 0xFF, - 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, - 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, - 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, - 0xFF, 0x20, 0x01, 0x16, 0xFE, 0xCA, 0x02, 0x54, 0xFC, 0xB9, 0x04, - 0xF2, 0x48, 0xA9, 0x02, 0x39, 0xFD, 0x53, 0x02, 0x53, 0xFE, 0x03, - 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, - 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, - 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, - 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBD, - 0x01, 0x57, 0xFC, 0x1E, 0x07, 0x90, 0xF1, 0xED, 0x2D, 0x8C, 0x32, - 0x83, 0xF1, 0xE2, 0x06, 0x92, 0xFC, 0x93, 0x01, 0x68, 0xFF, 0x26, - 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, 0xA4, 0x00, - 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, 0x1D, 0x10, - 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, 0x36, 0x00, - 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, 0xCA, 0x00, 0xC9, 0xFE, 0x73, - 0x01, 0xDE, 0xFE, 0x0C, 0xFF, 0x76, 0x48, 0xDE, 0x08, 0xA1, 0xFA, - 0xA6, 0x03, 0xA6, 0xFD, 0x53, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, - 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, 0xFC, - 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, 0x98, - 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, 0x00, - 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x32, 0xFC, 0x1B, - 0x07, 0x50, 0xF2, 0x7B, 0x26, 0x11, 0x39, 0x2F, 0xF2, 0x23, 0x06, - 0x22, 0xFD, 0x37, 0x01, 0x97, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, - 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, 0x41, 0xFD, 0xF6, 0x05, - 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, 0xF2, 0x0F, 0x07, 0x31, - 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, - 0xC2, 0xFF, 0x73, 0x00, 0x7D, 0xFF, 0x25, 0x00, 0x38, 0x01, 0x4C, - 0xFA, 0x89, 0x46, 0xC3, 0x0F, 0x14, 0xF8, 0xE0, 0x04, 0x0D, 0xFD, - 0x98, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, - 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, 0xDF, 0x03, 0x2E, 0xFA, - 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, 0xFF, 0x38, 0x01, 0xE9, - 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, - 0x36, 0xFF, 0xE4, 0x01, 0x49, 0xFC, 0xAF, 0x06, 0xC1, 0xF3, 0xCD, - 0x1E, 0xB1, 0x3E, 0xD9, 0xF3, 0xEC, 0x04, 0xF0, 0xFD, 0xBC, 0x00, - 0xD5, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, - 0xFF, 0x85, 0x01, 0xA6, 0xFC, 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, - 0xAB, 0x2C, 0xA3, 0xF1, 0x26, 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, - 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE2, 0xFF, 0x20, 0x00, - 0x26, 0x00, 0xF5, 0xFE, 0x43, 0x03, 0x8D, 0xF6, 0x3C, 0x43, 0x25, - 0x17, 0xBB, 0xF5, 0xEA, 0x05, 0x95, 0xFC, 0xCA, 0x01, 0x3D, 0xFF, - 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, - 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, - 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, - 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, - 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, - 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, - 0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, - 0xFD, 0x4E, 0x05, 0x4A, 0xF3, 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, - 0xD6, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, - 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, - 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, - 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, - 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, 0x01, 0x4C, 0xFC, 0x26, - 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, 0x8F, 0xF1, 0xCA, 0x06, - 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x16, - 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, - 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, - 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, - 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, 0x07, 0x84, - 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, 0x41, 0xFD, - 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, - 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, - 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, - 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, - 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, - 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, - 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0x07, 0x00, 0xE8, 0xFF, 0x12, - 0x00, 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, - 0x76, 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, - 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, - 0xC3, 0x01, 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, - 0x43, 0x20, 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, - 0xDD, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, - 0x00, 0xC8, 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, - 0x76, 0xF3, 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, - 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, - 0x26, 0xFD, 0xAD, 0x04, 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, - 0xFB, 0xD4, 0x00, 0x5D, 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, - 0x10, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, - 0xFD, 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, - 0x23, 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, - 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x46, 0x01, 0xC3, 0xFD, - 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, 0xA6, 0x48, 0xF8, 0xFF, 0x70, - 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, 0x00, 0x9A, 0xFF, 0x19, 0x00, - 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, - 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, - 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, - 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, 0x02, 0xAA, 0xFD, - 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, 0x05, 0x03, 0xF7, - 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, - 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, 0xDC, - 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, 0xFC, - 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xB2, - 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, 0xFC, - 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, 0x79, - 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, - 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, - 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, - 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xD3, 0xFF, 0x47, - 0x00, 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, - 0x8D, 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, - 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, - 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, - 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, - 0xF0, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, - 0x00, 0x67, 0xFE, 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, - 0xA6, 0xF4, 0x5A, 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, - 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, - 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, - 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, - 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, - 0xFD, 0x9C, 0x05, 0xDC, 0xF2, 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, - 0xF3, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, - 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x76, 0x01, 0x59, 0xFD, - 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, 0xB6, 0x47, 0xA4, 0xFC, 0x07, - 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, 0x00, 0xB1, 0xFF, 0x13, 0x00, - 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, 0x01, 0xCF, 0xFC, 0x96, - 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, 0xD4, 0xF1, 0x2B, 0x07, - 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, - 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, - 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, - 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, - 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, 0x07, 0x85, - 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, 0x84, 0xFC, - 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9C, - 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, - 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, - 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, - 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, - 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, - 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, - 0x00, 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, - 0xCD, 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, - 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, - 0xE5, 0x01, 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, - 0x3D, 0x91, 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, - 0x02, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, - 0x00, 0x1B, 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, - 0x07, 0xF6, 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, - 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, - 0x88, 0xFC, 0x09, 0x06, 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, - 0xF6, 0x83, 0x03, 0xCF, 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, - 0x07, 0x00, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, - 0xFE, 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, - 0x99, 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, - 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, 0xFF, 0xA0, 0x01, 0xFB, 0xFC, - 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, 0x2B, 0x46, 0xBB, 0xF9, 0x83, - 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, 0x00, 0xC7, 0xFF, 0x0E, 0x00, - 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, - 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, - 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, - 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, 0xFD, 0xD2, 0x03, - 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, 0x33, 0xFF, 0x45, - 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, 0x00, 0xFD, 0xFF, - 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, 0x8C, - 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, 0xFC, - 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x86, - 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, 0x03, - 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, 0x14, - 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, - 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, - 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, - 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, - 0x00, 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, - 0x47, 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, - 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, - 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, - 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, - 0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, - 0xFF, 0xE2, 0xFF, 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, - 0x8F, 0xF7, 0x1D, 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, - 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, - 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, - 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, - 0x04, 0x00, 0x07, 0x00, 0xE9, 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, - 0xFE, 0xA6, 0x03, 0xE4, 0xF5, 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, - 0x1A, 0x06, 0x81, 0xFC, 0xD2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, - 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC1, 0x01, 0xAB, 0xFC, - 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, 0x0B, 0x44, 0x42, 0xF7, 0xDC, - 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, 0x00, 0xDC, 0xFF, 0x09, 0x00, - 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, 0x00, 0xBF, 0xFD, 0x38, - 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, 0x66, 0xF3, 0xCE, 0x06, - 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, - 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, - 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, - 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, 0x00, 0xFF, 0xFF, - 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, 0x06, 0xF7, - 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, 0x34, 0xFC, - 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, - 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, - 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, - 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, - 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, - 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, - 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, - 0x00, 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, - 0x07, 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, - 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, - 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, - 0x36, 0xC5, 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, - 0x1E, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, - 0xFF, 0xB6, 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, - 0x31, 0xF9, 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, - 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, - 0x37, 0xFC, 0xEB, 0x06, 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, - 0xF2, 0x84, 0x05, 0x8D, 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, - 0x01, 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, - 0xFF, 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, - 0x7C, 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, - 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD9, 0x01, 0x6D, 0xFC, - 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, 0x5F, 0x41, 0x3A, 0xF5, 0x0C, - 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, 0xFF, 0xEF, 0xFF, 0x05, 0x00, - 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, - 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, - 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, - 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, 0xFC, 0x5D, 0x05, - 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, 0x2A, 0x02, 0x9A, - 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, 0xCC, - 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, 0xFC, - 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, - 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, 0xF9, - 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, 0x19, - 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x20, 0x00, - 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, - 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, - 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, - 0xFF, 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, - 0xEA, 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, - 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, - 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, - 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, - 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, - 0xFE, 0x93, 0x01, 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, - 0xE1, 0xFA, 0x86, 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, - 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, - 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, - 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, - 0xFF, 0xFF, 0x10, 0x00, 0xBF, 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, - 0x00, 0x01, 0x01, 0xB6, 0xFA, 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, - 0xC4, 0x04, 0x1B, 0xFD, 0x92, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, - 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x44, 0xFC, - 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, 0x31, 0x3E, 0xA5, 0xF3, 0x0F, - 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, 0xFF, 0x01, 0x00, 0x02, 0x00, - 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, 0x00, 0x10, 0xFF, 0x15, - 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, 0xF1, 0xF5, 0xD3, 0x05, - 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, - 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, - 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, - 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0x03, 0x00, - 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, 0x04, 0x10, - 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, 0x4E, 0xFC, - 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, - 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, - 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, - 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x13, 0x00, - 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, - 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, - 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, - 0xFF, 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, - 0x57, 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, - 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, - 0x8B, 0x01, 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, - 0x2D, 0x9A, 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, - 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, - 0xFE, 0x74, 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, - 0x94, 0xFC, 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, - 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, - 0x48, 0xFC, 0x28, 0x07, 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, - 0xF1, 0xBE, 0x06, 0xB0, 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, - 0xFE, 0xFF, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, - 0x01, 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, - 0xF8, 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, - 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, - 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, 0x8F, 0x3A, 0x82, 0xF2, 0xE1, - 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, 0xFF, 0x10, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, - 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, - 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, - 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, 0xFC, 0x85, 0x06, - 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, 0x8C, 0x04, 0x2C, - 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, 0x00, 0x06, 0x00, - 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, 0xC7, - 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, 0xFC, - 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, - 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, 0xF6, - 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, 0xFE, - 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x05, 0x00, - 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, - 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, - 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x56, - 0xFF, 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, - 0x26, 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, - 0x00, 0xBA, 0xFF, 0x11, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, - 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, - 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, - 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, - 0x01, 0xD0, 0xFD, 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, - 0x62, 0x00, 0x3F, 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, - 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, - 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, - 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, - 0xFD, 0xFF, 0x1B, 0x00, 0x93, 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, - 0x01, 0xDC, 0xFD, 0x3C, 0x01, 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, - 0x1F, 0x03, 0xEA, 0xFD, 0x34, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, - 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, 0xFF, 0xD6, 0x01, 0x39, 0xFC, - 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, 0x85, 0x36, 0xCC, 0xF1, 0x7F, - 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, - 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, 0xFF, 0xA9, 0x00, 0x4D, - 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, 0x18, 0xF9, 0x66, 0x04, - 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, - 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, - 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, - 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, 0x00, 0x0B, 0x00, - 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, 0x02, 0xF0, - 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, 0xC1, 0xFC, - 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, - 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, - 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, - 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0x05, 0x00, 0xF4, 0xFF, - 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, - 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, - 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x47, - 0xFF, 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, - 0x5C, 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, - 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, - 0x09, 0x01, 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, - 0x23, 0xD2, 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, - 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, - 0x01, 0x65, 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, - 0x03, 0xFD, 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, - 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, - 0xC4, 0xFC, 0xA4, 0x06, 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, - 0xF1, 0x2A, 0x07, 0x40, 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, - 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, - 0xFE, 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, - 0x5E, 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, - 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0xBA, 0x01, 0x5B, 0xFC, - 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, 0x26, 0x32, 0x80, 0xF1, 0xEA, - 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, - 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, - 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, - 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, - 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, 0xFC, 0x1E, 0x07, - 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, 0x32, 0x06, 0x18, - 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, - 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, 0x8B, - 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, 0xFD, - 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, - 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, 0xF3, - 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, 0xC4, - 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xE1, 0xFF, - 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, - 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, - 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, - 0xFF, 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, - 0x02, 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, - 0x00, 0xE4, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, - 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, - 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, - 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, - 0x01, 0x05, 0xFD, 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, - 0x0D, 0xFA, 0x58, 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, - 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, - 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, - 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, - 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, - 0xFD, 0xB9, 0x03, 0x7B, 0xFA, 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, - 0x03, 0xFF, 0x5F, 0x01, 0xD4, 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, - 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, 0xFF, 0x8E, 0x01, 0x99, 0xFC, - 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, 0x82, 0x2D, 0x96, 0xF1, 0x21, - 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, - 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, 0xFE, 0x67, 0x02, 0x13, - 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, 0x7A, 0xFC, 0xB6, 0x02, - 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, - 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, - 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, - 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x15, 0x00, - 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, 0xFF, 0x94, - 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, 0x77, 0xFD, - 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, - 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, - 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, - 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xCC, 0xFF, - 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, - 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, - 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, - 0xFF, 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, - 0x1F, 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, - 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, - 0x54, 0x00, 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, - 0x19, 0x1E, 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, - 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, - 0x01, 0xB4, 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, - 0x86, 0xF7, 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, - 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, - 0xAE, 0xFD, 0x52, 0x05, 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, - 0xF3, 0xD8, 0x06, 0x3C, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, - 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, - 0xFD, 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, - 0x91, 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, - 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, 0xFF, 0x55, 0x01, 0xF2, 0xFC, - 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, 0xAA, 0x28, 0x05, 0xF2, 0x27, - 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, - 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, - 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, - 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFD, - 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, 0xFC, 0x03, 0x07, - 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, 0x0A, 0x07, 0x6E, - 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x1A, 0x00, - 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, 0x05, - 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, 0xFD, - 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, - 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, 0xF1, - 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, 0x5C, - 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB6, 0xFF, - 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, - 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, - 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, - 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, - 0xC0, 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, - 0xFF, 0x07, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, - 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, - 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, - 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, - 0x01, 0x73, 0xFC, 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, - 0x71, 0xF5, 0xEB, 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, - 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, - 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, - 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, - 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, - 0xFC, 0x48, 0x05, 0x30, 0xF7, 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, - 0x01, 0x02, 0xB2, 0xFF, 0xBD, 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, - 0x00, 0x00, 0x00, 0x0E, 0x00, 0xAC, 0xFF, 0x0E, 0x01, 0x66, 0xFD, - 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, 0xB0, 0x23, 0xC4, 0xF2, 0xFF, - 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, - 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, 0x01, 0x6B, 0xFD, 0x1D, - 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, 0x33, 0xFD, 0xC1, 0xFF, - 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, 0xFF, 0x14, 0x00, 0xFE, - 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, - 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, - 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, - 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, 0x02, 0x47, - 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, 0x4D, 0x02, - 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, - 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, - 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, - 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA0, 0xFF, - 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, - 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, - 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, - 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, - 0xF2, 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, - 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, - 0x7A, 0xFF, 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, - 0x0F, 0x20, 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, - 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, - 0x01, 0x48, 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, - 0xCF, 0xF3, 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, - 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, - 0xFA, 0xFE, 0x3A, 0x03, 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, - 0xF5, 0xE6, 0x05, 0x97, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, - 0xFE, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, - 0xFC, 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, - 0x4D, 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, - 0x00, 0x03, 0x00, 0xFE, 0xFF, 0xD7, 0xFF, 0xBA, 0x00, 0xF4, 0xFD, - 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, - 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, - 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, - 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, - 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0xFF, - 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, 0xFD, 0x1E, 0x06, - 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, 0x1A, 0x07, 0x31, - 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, - 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, 0x94, - 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, 0x01, - 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, - 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, 0xF1, - 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, 0xBE, - 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, - 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, - 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, - 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, - 0xFF, 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, - 0xC3, 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, - 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, - 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, - 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, - 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, - 0x01, 0x33, 0xFC, 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, - 0x9E, 0xF2, 0xCB, 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, - 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, - 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, - 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, - 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, - 0xFC, 0x78, 0x06, 0x5A, 0xF4, 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, - 0x6D, 0x04, 0x3F, 0xFE, 0x8E, 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, - 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x5A, 0x00, 0x9A, 0xFE, - 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, 0xA1, 0x19, 0x09, 0xF5, 0x33, - 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, - 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, 0x01, 0xB8, 0xFC, 0x9A, - 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, 0xA9, 0xF7, 0xA2, 0x02, - 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, 0xFF, 0x0A, 0x00, 0x01, - 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, - 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, - 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, - 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, 0x04, 0xE6, - 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, 0x90, 0x00, - 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFE, 0xFF, 0x1C, - 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, - 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, - 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, - 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, - 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, - 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, - 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, - 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, - 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, - 0x8E, 0xFE, 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, - 0x06, 0x7B, 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, - 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, - 0x01, 0x37, 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, - 0xDC, 0xF1, 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, - 0x00, 0xFE, 0xFF, 0x11, 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, - 0x90, 0x00, 0x7A, 0x00, 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, - 0xF8, 0x7E, 0x04, 0x3C, 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, - 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, - 0xFC, 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, - 0x5F, 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, - 0x00, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, 0x00, 0xF1, 0xFF, 0x54, 0xFF, - 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, 0xB1, 0x14, 0x77, 0xF6, 0x9A, - 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, - 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, - 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, - 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x04, - 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, 0xFE, 0x6D, 0x04, - 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, 0x78, 0x06, 0x5C, - 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, - 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, 0x48, - 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, 0xFF, - 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0E, - 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, 0xF2, - 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, 0xE5, - 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, - 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, - 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, - 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, - 0xFF, 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, - 0x89, 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, - 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, - 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, - 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, - 0x8A, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, - 0x01, 0x56, 0xFC, 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, - 0x84, 0xF1, 0xE0, 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, - 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, - 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, - 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, - 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, - 0xFC, 0x1A, 0x07, 0x56, 0xF2, 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, - 0x1E, 0x06, 0x25, 0xFD, 0x35, 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, - 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, 0x00, 0x81, 0xFF, 0x1F, 0x00, - 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, 0xE7, 0x0F, 0x08, 0xF8, 0xE6, - 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, - 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x4A, 0xFC, 0xAC, - 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, 0xE4, 0xF3, 0xE5, 0x04, - 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0x08, - 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, - 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, - 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFE, 0xFF, - 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, 0x05, 0xC6, - 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, 0xFA, 0xFE, - 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFF, - 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, - 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, - 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, - 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, - 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, - 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, - 0xFF, 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, - 0xA0, 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, - 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, - 0x52, 0x01, 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, - 0x48, 0x26, 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, - 0xA0, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, - 0x01, 0x90, 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, - 0x8E, 0xF1, 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, - 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, - 0x4D, 0x02, 0x45, 0xFD, 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, - 0xFC, 0xD1, 0x02, 0x12, 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, - 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, - 0xFC, 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, - 0xAB, 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, - 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, 0x00, 0x0C, 0xFF, 0xF7, 0x00, - 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, 0x51, 0x0B, 0xAF, 0xF9, 0x1D, - 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, - 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, - 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, - 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x0C, - 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, 0xFF, 0x01, 0x02, - 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, 0x48, 0x05, 0xDD, - 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, - 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, 0x6C, - 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, 0xFE, - 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xED, - 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, 0xF5, - 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, 0xD7, - 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, - 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, - 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, - 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x07, 0x00, 0xBD, - 0xFF, 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, - 0x9E, 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, - 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, - 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, - 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, - 0xB6, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, - 0x01, 0xE6, 0xFC, 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, - 0xF3, 0xF1, 0x29, 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, - 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, - 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, - 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, - 0x1A, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, - 0xFC, 0x0A, 0x07, 0x7E, 0xF1, 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, - 0x03, 0x07, 0x75, 0xFC, 0xA7, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, - 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, 0x00, 0x95, 0xFE, 0xD5, 0x01, - 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, 0x00, 0x07, 0x61, 0xFB, 0x46, - 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, 0xFF, 0x23, 0x00, 0x00, 0x00, - 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, 0x01, 0x36, 0xFC, 0x27, - 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, 0xE4, 0xF1, 0x67, 0x06, - 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x11, - 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, - 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, - 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, - 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, 0x06, 0x47, - 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, 0xAE, 0xFD, - 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD9, - 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, - 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, - 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, - 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, - 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, - 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF7, 0xFF, 0xEA, - 0xFF, 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, - 0x94, 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, - 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, - 0xA9, 0x01, 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, - 0x45, 0x1C, 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, - 0xCC, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, - 0x01, 0x57, 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, - 0xAA, 0xF2, 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, - 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, - 0x77, 0xFD, 0x04, 0x04, 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, - 0xFD, 0x92, 0xFF, 0x10, 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, - 0x15, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, - 0xFC, 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, - 0x29, 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, - 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x1B, 0x01, 0x20, 0xFE, - 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, 0xF4, 0x48, 0xFF, 0x02, 0x13, - 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, 0x01, 0x88, 0xFF, 0x1D, 0x00, - 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, - 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, - 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, - 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, 0x01, 0x03, 0xFF, - 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, 0xB9, 0x03, 0x9D, - 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, - 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, 0x61, - 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, 0xFD, - 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC4, - 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, 0xFA, - 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, 0x9C, - 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, - 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, - 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, - 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE4, 0xFF, 0x1B, - 0x00, 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, - 0x96, 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, - 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, - 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, - 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, - 0xE1, 0xFF, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, - 0x00, 0xE2, 0xFD, 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, - 0xA8, 0xF3, 0xB8, 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, - 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, - 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, - 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, - 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, - 0xFD, 0x32, 0x06, 0x1F, 0xF2, 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, - 0x1E, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, - 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x4F, 0x01, 0xB0, 0xFD, - 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, 0x86, 0x48, 0x5A, 0xFF, 0xBA, - 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, 0x00, 0x9E, 0xFF, 0x17, 0x00, - 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, 0x01, 0x8C, 0xFC, 0xEA, - 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, 0x8B, 0xF1, 0x1B, 0x07, - 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, - 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, - 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, - 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, - 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xC6, - 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, 0xC4, 0xFC, - 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAE, - 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, - 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, - 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, - 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, - 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, - 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, - 0x00, 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, - 0xB2, 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, - 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, - 0xDD, 0x01, 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, - 0x40, 0xD0, 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, - 0xF4, 0xFF, 0x05, 0x00, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, - 0x00, 0x85, 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, - 0xE1, 0xF4, 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, - 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, - 0xC1, 0xFC, 0x86, 0x05, 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, - 0xF7, 0x7B, 0x02, 0x6B, 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, - 0x0B, 0x00, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, - 0xFD, 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, - 0xE6, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, - 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x5A, 0xFF, 0x7E, 0x01, 0x48, 0xFD, - 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, 0x7C, 0x47, 0x19, 0xFC, 0x4D, - 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, 0x00, 0xB5, 0xFF, 0x12, 0x00, - 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, - 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, - 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, - 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, 0xFD, 0x1F, 0x03, - 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, 0xDC, 0xFD, 0xFD, - 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, - 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, 0x80, - 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, 0xFC, - 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x98, - 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, 0x00, - 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, 0x40, - 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, - 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, - 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, - 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, - 0x00, 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, - 0xFC, 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, - 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, - 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, - 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, - 0x05, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, - 0xFF, 0x3D, 0xFF, 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, - 0x4A, 0xF6, 0xAD, 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, - 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, - 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, - 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, - 0x06, 0x00, 0x04, 0x00, 0xF8, 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, - 0xFE, 0x8C, 0x04, 0x6D, 0xF4, 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, - 0x85, 0x06, 0x57, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, - 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, 0xFF, 0xA7, 0x01, 0xEC, 0xFC, - 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, 0xD7, 0x45, 0x43, 0xF9, 0xC3, - 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, 0x00, 0xCB, 0xFF, 0x0D, 0x00, - 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, 0x01, 0x50, 0xFD, 0xE1, - 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, 0x9D, 0xF2, 0x09, 0x07, - 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, - 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, - 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, - 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, 0x00, 0xFE, 0xFF, - 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, 0x06, 0x97, - 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, 0x48, 0xFC, - 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, - 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, - 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, - 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, - 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, - 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, - 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, - 0x00, 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, - 0x81, 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, - 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, - 0xE2, 0x01, 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, - 0x39, 0x4D, 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, - 0x13, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, - 0xFF, 0x06, 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, - 0xD7, 0xF7, 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, - 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, - 0x4E, 0xFC, 0xA0, 0x06, 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, - 0xF4, 0xC8, 0x04, 0x07, 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, - 0x03, 0x00, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, - 0xFE, 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, - 0x00, 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, - 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, 0x9F, 0xFC, - 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, 0x9E, 0x43, 0xDD, 0xF6, 0x15, - 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, 0x00, 0xDF, 0xFF, 0x09, 0x00, - 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, - 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, - 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, - 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, 0xFD, 0xC4, 0x04, - 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, 0x01, 0x01, 0x44, - 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, 0x00, 0xFF, 0xFF, - 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, 0x14, - 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, 0xFC, - 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, - 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, 0xFA, - 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, 0xB8, - 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, - 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, - 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, - 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, - 0x00, 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, - 0x50, 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, - 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, - 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, - 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, - 0x20, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, - 0xFF, 0xDD, 0x00, 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, - 0x7C, 0xF9, 0x35, 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, - 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, - 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, - 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, - 0x00, 0x00, 0x0C, 0x00, 0xD1, 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, - 0xFF, 0x2A, 0x02, 0x83, 0xF8, 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, - 0x5D, 0x05, 0xD3, 0xFC, 0xB1, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, - 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x64, 0xFC, - 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, 0xD9, 0x40, 0xEA, 0xF4, 0x3E, - 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, 0xFF, 0xF3, 0xFF, 0x05, 0x00, - 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x7B, 0xFE, 0x0C, - 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, 0xCD, 0xF4, 0x4B, 0x06, - 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, - 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, - 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, - 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0x01, 0x00, - 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, 0x05, 0xFD, - 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, 0x37, 0xFC, - 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, - 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, - 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, - 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1E, 0x00, - 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, - 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, - 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, - 0xFF, 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, - 0xDB, 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, - 0x00, 0x91, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, - 0xB1, 0x01, 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, - 0x31, 0x7C, 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, - 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, - 0xFE, 0xBB, 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, - 0x2E, 0xFB, 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, - 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, - 0x34, 0xFC, 0x25, 0x07, 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, - 0xF1, 0x56, 0x06, 0xFE, 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, - 0xFF, 0xFF, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, - 0x00, 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, - 0xA1, 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, - 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3F, 0xFC, - 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, 0x96, 0x3D, 0x69, 0xF3, 0x38, - 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, 0xFF, 0x04, 0x00, 0x02, 0x00, - 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, - 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, - 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, - 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, 0xFC, 0x1A, 0x06, - 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, 0xA6, 0x03, 0xB9, - 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0x04, 0x00, - 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, 0x55, - 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, 0xFC, - 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, - 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, 0xF7, - 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, 0xA2, - 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00, - 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, - 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, - 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, - 0xFF, 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, - 0x2C, 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, - 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, - 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, - 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, - 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, - 0x01, 0x2D, 0xFE, 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, - 0x73, 0x03, 0xE0, 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, - 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, - 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, - 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, - 0xFD, 0xFF, 0x16, 0x00, 0xA5, 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, - 0x01, 0x33, 0xFF, 0x5A, 0xFE, 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, - 0xD2, 0x03, 0x90, 0xFD, 0x5E, 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, - 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, - 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, 0xDF, 0x39, 0x5A, 0xF2, 0x00, - 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, 0xFF, 0x13, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, 0xFF, 0xFA, 0xFF, 0x83, - 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, 0xBF, 0xF7, 0x07, 0x05, - 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, - 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, - 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, - 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0x07, 0x00, - 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, 0x03, 0x20, - 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, 0x88, 0xFC, - 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, - 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, - 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, - 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, - 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, - 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, - 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x53, - 0xFF, 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, - 0xE1, 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, - 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, - 0x45, 0x01, 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, - 0x27, 0x2B, 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, - 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, - 0x01, 0xBC, 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, - 0xC3, 0xFF, 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, - 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, - 0x84, 0xFC, 0xF3, 0x06, 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, - 0xF1, 0x16, 0x07, 0x61, 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, - 0xFD, 0xFF, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, - 0x02, 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, - 0xF8, 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, - 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD2, 0x01, 0x3D, 0xFC, - 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, 0xC6, 0x35, 0xB7, 0xF1, 0x96, - 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, - 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, - 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, - 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, - 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF3, 0x06, - 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, 0x9C, 0x05, 0x7E, - 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, - 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, 0x5E, - 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, 0xFC, - 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, - 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, 0xF4, - 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, 0x77, - 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF0, 0xFF, - 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, - 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, - 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x45, - 0xFF, 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, - 0xFD, 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, - 0x00, 0xD3, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, - 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, - 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, - 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, - 0x01, 0x53, 0xFD, 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, - 0x76, 0xFC, 0x1F, 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, - 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, - 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, - 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, - 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, - 0xFD, 0x05, 0x03, 0xE1, 0xFB, 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, - 0xAA, 0xFD, 0x18, 0x02, 0x72, 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, - 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB3, 0x01, 0x64, 0xFC, - 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, 0x5A, 0x31, 0x7D, 0xF1, 0xF7, - 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, - 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, 0xFE, 0xAE, 0x01, 0x70, - 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, 0x14, 0xFB, 0x6D, 0x03, - 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, - 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, - 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, - 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x10, 0x00, - 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, 0x00, 0x0C, - 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, 0x26, 0xFD, - 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, - 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, - 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, - 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDD, 0xFF, - 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, - 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, - 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3B, - 0xFF, 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, - 0x89, 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, - 0x00, 0xE8, 0xFF, 0x07, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, - 0xA4, 0x00, 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, - 0x1D, 0x10, 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, - 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, - 0x01, 0xF6, 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, - 0x93, 0xF9, 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, - 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, - 0x41, 0xFD, 0xF6, 0x05, 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, - 0xF2, 0x0F, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, - 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, - 0xFD, 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, - 0x4B, 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, - 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, 0xFF, 0x85, 0x01, 0xA6, 0xFC, - 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, 0xAB, 0x2C, 0xA3, 0xF1, 0x26, - 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, 0xFF, 0x30, 0x00, 0xFD, 0xFF, - 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, - 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, - 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFD, - 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, - 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, - 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x00, 0x00, - 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, - 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, - 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x02, 0x00, 0x05, - 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, - 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, - 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x36, 0x00, - 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, - 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, - 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x84, - 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, - 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, - 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, - 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, - 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, - 0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, 0xFF, 0x64, 0x00, 0x9B, - 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, 0x10, 0x46, 0x03, 0x11, - 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, 0x01, 0x4C, 0xFF, 0x2F, - 0x00, 0xFF, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, 0x42, 0x00, - 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, 0x18, 0x5C, - 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, - 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, - 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, - 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, - 0xFF, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, - 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, - 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, - 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, - 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, - 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0xFD, - 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, - 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, - 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, - 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, - 0xF2, 0x81, 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, - 0xFB, 0x00, 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, - 0x00, 0x38, 0xFF, 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, - 0xCE, 0x1A, 0x32, 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, - 0x00, 0xFB, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, - 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, - 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, - 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, - 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, - 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, - 0x00, 0xB1, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, - 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, - 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, - 0x8F, 0xFF, 0x1C, 0x00, 0x18, 0x00, 0x9C, 0xFF, 0xD6, 0x00, 0xB1, - 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, 0x9C, 0x48, 0xFD, 0x07, - 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, 0x01, 0x6E, 0xFF, 0x24, - 0x00, 0x00, 0x00, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, 0x65, 0xFF, - 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, 0x0E, 0x6A, - 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, 0x2D, 0x00, - 0xFF, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, - 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, - 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, - 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, - 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, - 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, - 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, - 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, - 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, - 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, - 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, - 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, - 0x30, 0x00, 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, - 0xF1, 0x62, 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, - 0x82, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, - 0x00, 0x3A, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, - 0xDD, 0x24, 0x54, 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, - 0x01, 0xA3, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, - 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, - 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, - 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, - 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, - 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, - 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, - 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, - 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, - 0xBB, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x43, - 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, 0x7E, 0x07, 0xAF, 0x48, - 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, 0xFE, 0xDD, 0x00, 0x99, - 0xFF, 0x19, 0x00, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, 0x79, 0xFE, - 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, 0x06, 0xC7, - 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, 0x22, 0x00, - 0x00, 0x00, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, - 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, - 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, - 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, - 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, - 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, - 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, - 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, - 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, - 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, - 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, - 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, - 0x20, 0x00, 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, - 0xF1, 0x86, 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, - 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2D, - 0x00, 0x54, 0xFF, 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, - 0x9F, 0x2E, 0xE3, 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, - 0x01, 0x64, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, - 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, - 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, - 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, - 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, - 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, - 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, - 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, - 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, - 0xE5, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9E, - 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, 0x75, 0x10, 0x48, 0x46, - 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, 0xFF, 0x6B, 0x00, 0xC6, - 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x5B, 0x01, - 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, 0x48, 0x8D, - 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, 0xA4, 0xFF, - 0x16, 0x00, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, - 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, - 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, - 0x00, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, - 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, - 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, - 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, - 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x06, - 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, - 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, - 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x02, 0x00, - 0x05, 0x00, 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, - 0xF3, 0x61, 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, - 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x1A, - 0x00, 0x8A, 0xFF, 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, - 0x82, 0x37, 0x60, 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, - 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x29, 0x00, - 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, - 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, - 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x43, - 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, - 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, - 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, - 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, - 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, - 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, - 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, 0x38, 0x1A, 0x8C, 0x41, - 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, 0x00, 0x01, 0x00, 0xEE, - 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, 0xAF, 0x01, - 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, 0x45, 0xA9, - 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, 0xD0, 0xFF, - 0x0C, 0x00, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, - 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, - 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, - 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, - 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, - 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, - 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, - 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x0F, - 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, - 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, - 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x08, 0x00, - 0xE1, 0xFF, 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, - 0xF6, 0x77, 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, - 0xC8, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xFD, - 0xFF, 0xD9, 0xFF, 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, - 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, - 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x14, 0x00, - 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, - 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, - 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x25, 0x00, 0x6A, - 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, - 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, - 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, - 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, - 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, - 0x22, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, - 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, 0x46, 0x24, 0xC8, 0x3A, - 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, 0x01, 0xA8, 0xFF, 0x0F, - 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, - 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, 0x40, 0x85, - 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, 0xF7, 0xFF, - 0x04, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, - 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, - 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, - 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, - 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, - 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, - 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, - 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, - 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0x1A, - 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, - 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, - 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0x12, 0x00, - 0xB6, 0xFF, 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, - 0xFB, 0x69, 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, - 0x81, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD7, - 0xFF, 0x3E, 0x00, 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, - 0x99, 0x44, 0x68, 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, - 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF5, 0xFF, - 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, - 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, - 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, - 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, - 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, - 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, - 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, - 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, - 0x31, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2E, 0x00, 0x52, 0xFF, 0xBC, - 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, 0x11, 0x2E, 0x6B, 0x32, - 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, 0x01, 0x67, 0xFF, 0x26, - 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE0, 0x01, - 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, 0x38, 0x2A, - 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, 0x16, 0x00, - 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, - 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, - 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, - 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, - 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, - 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, - 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, - 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, - 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0x00, - 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, - 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, - 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0x1D, 0x00, - 0x8A, 0xFF, 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, - 0x02, 0xF2, 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, - 0x1E, 0x01, 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAC, - 0xFF, 0xAE, 0x00, 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, - 0xFD, 0x47, 0x0E, 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, - 0x01, 0x61, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, - 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, - 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, - 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, - 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, - 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, - 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, - 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, - 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, - 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1C, 0x00, 0x86, 0xFF, 0x59, - 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, 0x04, 0x37, 0xF3, 0x28, - 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x41, 0xFF, 0x33, - 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, - 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, - 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, - 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, - 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, - 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, - 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, - 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, - 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, - 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, - 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, - 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0xFF, - 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, - 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, - 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, - 0xF9, 0x0E, 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, - 0x05, 0xFF, 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x7F, 0xFF, 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, - 0x9B, 0x04, 0xF2, 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, - 0xFE, 0x04, 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0x17, 0x00, 0xA1, 0xFF, - 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, - 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, - 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC3, 0xFF, 0x71, - 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, - 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, - 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, - 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, - 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, - 0x34, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, - 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, 0x98, 0x3E, 0xF3, 0x1E, - 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, - 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, 0x39, 0x01, - 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, 0x26, 0x4B, - 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, 0x35, 0x00, - 0xFD, 0xFF, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, - 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, - 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, - 0xFF, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, - 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, - 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, - 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, - 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, - 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFE, - 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, - 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, - 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFF, 0xFF, - 0x32, 0x00, 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, - 0xF6, 0x68, 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, - 0xEA, 0xFF, 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x2B, - 0x00, 0x59, 0xFF, 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, - 0x2D, 0x0D, 0x69, 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, - 0xFF, 0x93, 0x00, 0xB6, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, - 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, - 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, - 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0x1A, 0x00, 0x96, 0xFF, 0xE3, - 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, - 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, - 0xFF, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, - 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, - 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, - 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD9, 0xFF, 0x37, 0x00, 0xF7, - 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, 0x53, 0x44, 0xFB, 0x14, - 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, 0x01, 0x42, 0xFF, 0x32, - 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, 0x00, - 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, 0x1C, 0x47, - 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, - 0xFE, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, - 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, - 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, - 0xFF, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, - 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, - 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, - 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, - 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, - 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0xFD, - 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, - 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, - 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, - 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, - 0xF3, 0x5B, 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, - 0xB4, 0x00, 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, - 0x00, 0x3E, 0xFF, 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, - 0xB6, 0x16, 0x77, 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, - 0x00, 0x25, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFF, 0xFF, 0x2D, 0x00, - 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, - 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, - 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x6C, - 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, - 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, - 0x00, 0x9E, 0xFF, 0x17, 0x00, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, - 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, - 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, - 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAE, 0xFF, 0xA7, 0x00, 0x12, - 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, 0xDC, 0x47, 0x95, 0x0B, - 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, 0x01, 0x5F, 0xFF, 0x29, - 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, 0xC3, 0xFF, - 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, 0x12, 0x19, - 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, 0x30, 0x00, - 0xFF, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, - 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, - 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, - 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, - 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, - 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, - 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, - 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, - 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0xFD, - 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, - 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, - 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, - 0x34, 0x00, 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, - 0xF2, 0x60, 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, - 0x51, 0x01, 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, - 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, - 0xBA, 0x20, 0x61, 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, - 0x00, 0xC5, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x35, 0x00, - 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, - 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, - 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, - 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, - 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, - 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, - 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, - 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, - 0xA9, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x82, 0xFF, 0x18, - 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, 0x24, 0x04, 0xF5, 0x48, - 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, 0xFE, 0x0B, 0x01, 0x87, - 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, 0xDB, 0xFE, - 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, 0x09, 0x61, - 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, 0x26, 0x00, - 0x00, 0x00, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, - 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, - 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, - 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, - 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, - 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, - 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, - 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, - 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, - 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, - 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, - 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, - 0x28, 0x00, 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, - 0xF1, 0xE3, 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, - 0xB7, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x32, - 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, - 0xAE, 0x2A, 0x86, 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, - 0x01, 0x7B, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, - 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, - 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, - 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, - 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, - 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, - 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, - 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, - 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, - 0xD5, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, 0xFF, 0x7C, - 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, 0xA4, 0x0C, 0x90, 0x47, - 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, 0xFF, 0x99, 0x00, 0xB3, - 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, 0x31, 0x01, - 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, 0x48, 0x73, - 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, 0x91, 0xFF, - 0x1B, 0x00, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, - 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, - 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, - 0x00, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, - 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, - 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, - 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, - 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, - 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x04, - 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, - 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, - 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, - 0x11, 0x00, 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, - 0xF2, 0x54, 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, - 0xE4, 0x01, 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x23, - 0x00, 0x71, 0xFF, 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, - 0xFD, 0x33, 0x62, 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, - 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2F, 0x00, - 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, - 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, - 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3B, - 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, - 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, - 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, - 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, - 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, - 0xFB, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC5, - 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, 0x22, 0x16, 0xC3, 0x43, - 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, 0x00, 0x2B, 0x00, 0xDE, - 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x90, 0x01, - 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, 0x46, 0xE1, - 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, 0xBE, 0xFF, - 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, - 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, - 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, - 0x00, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, - 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, - 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, - 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, - 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, - 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x0B, - 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, - 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, - 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x05, 0x00, - 0xF0, 0xFF, 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, - 0xF5, 0x32, 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, - 0xDA, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, - 0x00, 0xB6, 0xFF, 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, - 0x1C, 0x3C, 0x81, 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, - 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1E, 0x00, - 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, - 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, - 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2C, 0x00, 0x57, - 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, - 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, - 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, - 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, - 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, - 0x19, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, - 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, 0x22, 0x20, 0xCA, 0x3D, - 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, 0x00, 0xCA, 0xFF, 0x03, - 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, - 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, 0x42, 0x02, - 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, 0xE8, 0xFF, - 0x07, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, - 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, - 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, - 0x00, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, - 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, - 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, - 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, - 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, - 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, - 0x00, 0xF4, 0xFF, 0x1A, 0x00, 0xFF, 0x00, 0x07, 0x03, 0x16, 0x06, - 0x7C, 0x09, 0x2A, 0x0C, 0x2E, 0x0D, 0x2A, 0x0C, 0x7C, 0x09, 0x16, - 0x06, 0x07, 0x03, 0xFF, 0x00, 0x1A, 0x00, 0xF4, 0xFF, 0xF2, 0xFF, - 0xA0, 0xFF, 0x71, 0xFF, 0x71, 0x00, 0x86, 0x03, 0x73, 0x08, 0x88, - 0x0D, 0x78, 0x10, 0xC9, 0x0F, 0xD5, 0x0B, 0x8B, 0x06, 0x28, 0x02, - 0xDF, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDC, - 0xFF, 0x80, 0xFF, 0x9A, 0xFF, 0x46, 0x01, 0x1E, 0x05, 0x5A, 0x0A, - 0xED, 0x0E, 0xAA, 0x10, 0xAF, 0x0E, 0xFD, 0x09, 0xCB, 0x04, 0x18, - 0x01, 0x8E, 0xFF, 0x85, 0xFF, 0xE1, 0xFF, 0xFC, 0xFF, 0xBD, 0xFF, - 0x6D, 0xFF, 0xF6, 0xFF, 0x65, 0x02, 0xE5, 0x06, 0x2B, 0x0C, 0xF3, - 0x0F, 0x60, 0x10, 0x3B, 0x0D, 0x16, 0x08, 0x3F, 0x03, 0x50, 0x00, - 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, 0xFF, 0xEF, 0xFF, 0x9A, 0xFF, 0x75, - 0xFF, 0x91, 0x00, 0xC9, 0x03, 0xC8, 0x08, 0xCC, 0x0D, 0x89, 0x10, - 0x9F, 0x0F, 0x85, 0x0B, 0x3B, 0x06, 0xF4, 0x01, 0xCD, 0xFF, 0x72, - 0xFF, 0xC9, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0x7B, 0xFF, - 0xA5, 0xFF, 0x73, 0x01, 0x6A, 0x05, 0xAD, 0x0A, 0x21, 0x0F, 0xA6, - 0x10, 0x74, 0x0E, 0xA9, 0x09, 0x83, 0x04, 0xF0, 0x00, 0x85, 0xFF, - 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, 0xFF, 0xB7, 0xFF, 0x6C, 0xFF, 0x0C, - 0x00, 0x9D, 0x02, 0x37, 0x07, 0x78, 0x0C, 0x15, 0x10, 0x47, 0x10, - 0xF3, 0x0C, 0xC2, 0x07, 0x01, 0x03, 0x35, 0x00, 0x6D, 0xFF, 0xAD, - 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, 0x94, 0xFF, 0x7A, 0xFF, 0xB3, 0x00, - 0x0D, 0x04, 0x1C, 0x09, 0x0D, 0x0E, 0x97, 0x10, 0x73, 0x0F, 0x35, - 0x0B, 0xEB, 0x05, 0xC1, 0x01, 0xBD, 0xFF, 0x75, 0xFF, 0xCE, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x77, 0xFF, 0xB3, 0xFF, 0xA1, - 0x01, 0xB7, 0x05, 0xFF, 0x0A, 0x53, 0x0F, 0x9E, 0x10, 0x37, 0x0E, - 0x55, 0x09, 0x3B, 0x04, 0xCB, 0x00, 0x7E, 0xFF, 0x90, 0xFF, 0xE9, - 0xFF, 0xF8, 0xFF, 0xB1, 0xFF, 0x6C, 0xFF, 0x24, 0x00, 0xD8, 0x02, - 0x8A, 0x07, 0xC2, 0x0C, 0x34, 0x10, 0x2A, 0x10, 0xAA, 0x0C, 0x6F, - 0x07, 0xC4, 0x02, 0x1C, 0x00, 0x6C, 0xFF, 0xB3, 0xFF, 0xF9, 0xFF, - 0xE8, 0xFF, 0x8E, 0xFF, 0x80, 0xFF, 0xD7, 0x00, 0x53, 0x04, 0x71, - 0x09, 0x4C, 0x0E, 0xA1, 0x10, 0x43, 0x0F, 0xE3, 0x0A, 0x9D, 0x05, - 0x91, 0x01, 0xAE, 0xFF, 0x79, 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, - 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC2, 0xFF, 0xD2, 0x01, 0x06, 0x06, - 0x50, 0x0B, 0x82, 0x0F, 0x93, 0x10, 0xF8, 0x0D, 0x00, 0x09, 0xF6, - 0x03, 0xA7, 0x00, 0x78, 0xFF, 0x96, 0xFF, 0xEC, 0xFF, 0xF6, 0xFF, - 0xAB, 0xFF, 0x6D, 0xFF, 0x3E, 0x00, 0x15, 0x03, 0xDE, 0x07, 0x0B, - 0x0D, 0x50, 0x10, 0x0A, 0x10, 0x5E, 0x0C, 0x1C, 0x07, 0x8A, 0x02, - 0x04, 0x00, 0x6C, 0xFF, 0xB9, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, 0x89, - 0xFF, 0x88, 0xFF, 0xFD, 0x00, 0x9B, 0x04, 0xC5, 0x09, 0x88, 0x0E, - 0xA8, 0x10, 0x10, 0x0F, 0x91, 0x0A, 0x50, 0x05, 0x64, 0x01, 0xA1, - 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, - 0x71, 0xFF, 0xD3, 0xFF, 0x05, 0x02, 0x55, 0x06, 0xA0, 0x0B, 0xAD, - 0x0F, 0x84, 0x10, 0xB6, 0x0D, 0xAC, 0x08, 0xB3, 0x03, 0x86, 0x00, - 0x74, 0xFF, 0x9C, 0xFF, 0xF0, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, 0x6F, - 0xFF, 0x5A, 0x00, 0x54, 0x03, 0x32, 0x08, 0x52, 0x0D, 0x68, 0x10, - 0xE6, 0x0F, 0x11, 0x0C, 0xCA, 0x06, 0x52, 0x02, 0xEF, 0xFF, 0x6E, - 0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xDF, 0xFF, 0x84, 0xFF, 0x91, 0xFF, - 0x25, 0x01, 0xE4, 0x04, 0x19, 0x0A, 0xC2, 0x0E, 0xAA, 0x10, 0xDA, - 0x0E, 0x3E, 0x0A, 0x05, 0x05, 0x38, 0x01, 0x96, 0xFF, 0x81, 0xFF, - 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE6, - 0xFF, 0x3A, 0x02, 0xA6, 0x06, 0xEF, 0x0B, 0xD6, 0x0F, 0x71, 0x10, - 0x71, 0x0D, 0x57, 0x08, 0x71, 0x03, 0x67, 0x00, 0x70, 0xFF, 0xA2, - 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x78, 0x00, - 0x95, 0x03, 0x86, 0x08, 0x98, 0x0D, 0x7C, 0x10, 0xC0, 0x0F, 0xC3, - 0x0B, 0x79, 0x06, 0x1C, 0x02, 0xDB, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, - 0xFE, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9C, 0xFF, 0x50, - 0x01, 0x2F, 0x05, 0x6C, 0x0A, 0xF9, 0x0E, 0xA9, 0x10, 0xA2, 0x0E, - 0xEA, 0x09, 0xBB, 0x04, 0x0F, 0x01, 0x8C, 0xFF, 0x87, 0xFF, 0xE2, - 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xFA, 0xFF, 0x71, 0x02, - 0xF7, 0x06, 0x3C, 0x0C, 0xFB, 0x0F, 0x5B, 0x10, 0x2B, 0x0D, 0x03, - 0x08, 0x31, 0x03, 0x4A, 0x00, 0x6E, 0xFF, 0xA8, 0xFF, 0xF5, 0xFF, - 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x98, 0x00, 0xD8, 0x03, 0xDB, - 0x08, 0xDB, 0x0D, 0x8D, 0x10, 0x96, 0x0F, 0x73, 0x0B, 0x29, 0x06, - 0xE8, 0x01, 0xC9, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, 0x00, - 0x00, 0xD6, 0xFF, 0x7A, 0xFF, 0xA8, 0xFF, 0x7D, 0x01, 0x7B, 0x05, - 0xBF, 0x0A, 0x2D, 0x0F, 0xA5, 0x10, 0x67, 0x0E, 0x96, 0x09, 0x73, - 0x04, 0xE7, 0x00, 0x84, 0xFF, 0x8C, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, - 0xB6, 0xFF, 0x6C, 0xFF, 0x11, 0x00, 0xAA, 0x02, 0x4A, 0x07, 0x88, - 0x0C, 0x1C, 0x10, 0x41, 0x10, 0xE3, 0x0C, 0xAF, 0x07, 0xF3, 0x02, - 0x2F, 0x00, 0x6C, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, - 0xFF, 0x7B, 0xFF, 0xBB, 0x00, 0x1C, 0x04, 0x2F, 0x09, 0x1B, 0x0E, - 0x9A, 0x10, 0x68, 0x0F, 0x23, 0x0B, 0xDA, 0x05, 0xB7, 0x01, 0xB9, - 0xFF, 0x76, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0xFF, - 0x76, 0xFF, 0xB6, 0xFF, 0xAC, 0x01, 0xC8, 0x05, 0x11, 0x0B, 0x5E, - 0x0F, 0x9C, 0x10, 0x29, 0x0E, 0x42, 0x09, 0x2C, 0x04, 0xC2, 0x00, - 0x7D, 0xFF, 0x92, 0xFF, 0xEA, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, 0x6C, - 0xFF, 0x29, 0x00, 0xE6, 0x02, 0x9D, 0x07, 0xD3, 0x0C, 0x3B, 0x10, - 0x23, 0x10, 0x99, 0x0C, 0x5C, 0x07, 0xB7, 0x02, 0x16, 0x00, 0x6C, - 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8D, 0xFF, 0x82, 0xFF, - 0xDF, 0x00, 0x63, 0x04, 0x84, 0x09, 0x59, 0x0E, 0xA3, 0x10, 0x38, - 0x0F, 0xD1, 0x0A, 0x8C, 0x05, 0x87, 0x01, 0xAB, 0xFF, 0x79, 0xFF, - 0xD5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC6, - 0xFF, 0xDD, 0x01, 0x17, 0x06, 0x62, 0x0B, 0x8C, 0x0F, 0x90, 0x10, - 0xE9, 0x0D, 0xED, 0x08, 0xE7, 0x03, 0xA0, 0x00, 0x77, 0xFF, 0x97, - 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xA9, 0xFF, 0x6D, 0xFF, 0x44, 0x00, - 0x23, 0x03, 0xF1, 0x07, 0x1B, 0x0D, 0x55, 0x10, 0x02, 0x10, 0x4D, - 0x0C, 0x0A, 0x07, 0x7E, 0x02, 0xFF, 0xFF, 0x6D, 0xFF, 0xBA, 0xFF, - 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x8A, 0xFF, 0x06, 0x01, 0xAB, - 0x04, 0xD8, 0x09, 0x95, 0x0E, 0xA9, 0x10, 0x05, 0x0F, 0x7F, 0x0A, - 0x40, 0x05, 0x5A, 0x01, 0x9F, 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, - 0x00, 0xFE, 0xFF, 0xC6, 0xFF, 0x70, 0xFF, 0xD7, 0xFF, 0x10, 0x02, - 0x67, 0x06, 0xB1, 0x0B, 0xB7, 0x0F, 0x80, 0x10, 0xA7, 0x0D, 0x99, - 0x08, 0xA4, 0x03, 0x7F, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, 0xFF, - 0xF3, 0xFF, 0xA3, 0xFF, 0x70, 0xFF, 0x60, 0x00, 0x62, 0x03, 0x45, - 0x08, 0x62, 0x0D, 0x6C, 0x10, 0xDE, 0x0F, 0x00, 0x0C, 0xB8, 0x06, - 0x46, 0x02, 0xEA, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFD, 0xFF, 0x00, - 0x00, 0xDE, 0xFF, 0x83, 0xFF, 0x94, 0xFF, 0x2F, 0x01, 0xF4, 0x04, - 0x2B, 0x0A, 0xCE, 0x0E, 0xAA, 0x10, 0xCE, 0x0E, 0x2B, 0x0A, 0xF4, - 0x04, 0x2F, 0x01, 0x94, 0xFF, 0x83, 0xFF, 0xDE, 0xFF, 0xFD, 0xFF, - 0xC0, 0xFF, 0x6E, 0xFF, 0xEA, 0xFF, 0x46, 0x02, 0xB8, 0x06, 0x00, - 0x0C, 0xDE, 0x0F, 0x6C, 0x10, 0x62, 0x0D, 0x45, 0x08, 0x62, 0x03, - 0x60, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, - 0xFF, 0x73, 0xFF, 0x7F, 0x00, 0xA4, 0x03, 0x99, 0x08, 0xA7, 0x0D, - 0x80, 0x10, 0xB7, 0x0F, 0xB1, 0x0B, 0x67, 0x06, 0x10, 0x02, 0xD7, - 0xFF, 0x70, 0xFF, 0xC6, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, 0xFF, - 0x7E, 0xFF, 0x9F, 0xFF, 0x5A, 0x01, 0x40, 0x05, 0x7F, 0x0A, 0x05, - 0x0F, 0xA9, 0x10, 0x95, 0x0E, 0xD8, 0x09, 0xAB, 0x04, 0x06, 0x01, - 0x8A, 0xFF, 0x88, 0xFF, 0xE3, 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, - 0xFF, 0xFF, 0xFF, 0x7E, 0x02, 0x0A, 0x07, 0x4D, 0x0C, 0x02, 0x10, - 0x55, 0x10, 0x1B, 0x0D, 0xF1, 0x07, 0x23, 0x03, 0x44, 0x00, 0x6D, - 0xFF, 0xA9, 0xFF, 0xF6, 0xFF, 0xED, 0xFF, 0x97, 0xFF, 0x77, 0xFF, - 0xA0, 0x00, 0xE7, 0x03, 0xED, 0x08, 0xE9, 0x0D, 0x90, 0x10, 0x8C, - 0x0F, 0x62, 0x0B, 0x17, 0x06, 0xDD, 0x01, 0xC6, 0xFF, 0x73, 0xFF, - 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x79, 0xFF, 0xAB, - 0xFF, 0x87, 0x01, 0x8C, 0x05, 0xD1, 0x0A, 0x38, 0x0F, 0xA3, 0x10, - 0x59, 0x0E, 0x84, 0x09, 0x63, 0x04, 0xDF, 0x00, 0x82, 0xFF, 0x8D, - 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, 0xB4, 0xFF, 0x6C, 0xFF, 0x16, 0x00, - 0xB7, 0x02, 0x5C, 0x07, 0x99, 0x0C, 0x23, 0x10, 0x3B, 0x10, 0xD3, - 0x0C, 0x9D, 0x07, 0xE6, 0x02, 0x29, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, - 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7D, 0xFF, 0xC2, 0x00, 0x2C, - 0x04, 0x42, 0x09, 0x29, 0x0E, 0x9C, 0x10, 0x5E, 0x0F, 0x11, 0x0B, - 0xC8, 0x05, 0xAC, 0x01, 0xB6, 0xFF, 0x76, 0xFF, 0xD1, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB9, 0xFF, 0xB7, 0x01, - 0xDA, 0x05, 0x23, 0x0B, 0x68, 0x0F, 0x9A, 0x10, 0x1B, 0x0E, 0x2F, - 0x09, 0x1C, 0x04, 0xBB, 0x00, 0x7B, 0xFF, 0x93, 0xFF, 0xEA, 0xFF, - 0xF7, 0xFF, 0xAE, 0xFF, 0x6C, 0xFF, 0x2F, 0x00, 0xF3, 0x02, 0xAF, - 0x07, 0xE3, 0x0C, 0x41, 0x10, 0x1C, 0x10, 0x88, 0x0C, 0x4A, 0x07, - 0xAA, 0x02, 0x11, 0x00, 0x6C, 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE6, - 0xFF, 0x8C, 0xFF, 0x84, 0xFF, 0xE7, 0x00, 0x73, 0x04, 0x96, 0x09, - 0x67, 0x0E, 0xA5, 0x10, 0x2D, 0x0F, 0xBF, 0x0A, 0x7B, 0x05, 0x7D, - 0x01, 0xA8, 0xFF, 0x7A, 0xFF, 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, - 0xCA, 0xFF, 0x72, 0xFF, 0xC9, 0xFF, 0xE8, 0x01, 0x29, 0x06, 0x73, - 0x0B, 0x96, 0x0F, 0x8D, 0x10, 0xDB, 0x0D, 0xDB, 0x08, 0xD8, 0x03, - 0x98, 0x00, 0x76, 0xFF, 0x99, 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA8, - 0xFF, 0x6E, 0xFF, 0x4A, 0x00, 0x31, 0x03, 0x03, 0x08, 0x2B, 0x0D, - 0x5B, 0x10, 0xFB, 0x0F, 0x3C, 0x0C, 0xF7, 0x06, 0x71, 0x02, 0xFA, - 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, 0xFC, 0xFF, 0xE2, 0xFF, 0x87, 0xFF, - 0x8C, 0xFF, 0x0F, 0x01, 0xBB, 0x04, 0xEA, 0x09, 0xA2, 0x0E, 0xA9, - 0x10, 0xF9, 0x0E, 0x6C, 0x0A, 0x2F, 0x05, 0x50, 0x01, 0x9C, 0xFF, - 0x7F, 0xFF, 0xDB, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, 0x70, - 0xFF, 0xDB, 0xFF, 0x1C, 0x02, 0x79, 0x06, 0xC3, 0x0B, 0xC0, 0x0F, - 0x7C, 0x10, 0x98, 0x0D, 0x86, 0x08, 0x95, 0x03, 0x78, 0x00, 0x72, - 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA2, 0xFF, 0x70, 0xFF, - 0x67, 0x00, 0x71, 0x03, 0x57, 0x08, 0x71, 0x0D, 0x71, 0x10, 0xD6, - 0x0F, 0xEF, 0x0B, 0xA6, 0x06, 0x3A, 0x02, 0xE6, 0xFF, 0x6E, 0xFF, - 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x96, - 0xFF, 0x38, 0x01, 0x05, 0x05, 0x3E, 0x0A, 0xDA, 0x0E, 0xAA, 0x10, - 0xC2, 0x0E, 0x19, 0x0A, 0xE4, 0x04, 0x25, 0x01, 0x91, 0xFF, 0x84, - 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xBF, 0xFF, 0x6E, 0xFF, 0xEF, 0xFF, - 0x52, 0x02, 0xCA, 0x06, 0x11, 0x0C, 0xE6, 0x0F, 0x68, 0x10, 0x52, - 0x0D, 0x32, 0x08, 0x54, 0x03, 0x5A, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, - 0xF4, 0xFF, 0xF0, 0xFF, 0x9C, 0xFF, 0x74, 0xFF, 0x86, 0x00, 0xB3, - 0x03, 0xAC, 0x08, 0xB6, 0x0D, 0x84, 0x10, 0xAD, 0x0F, 0xA0, 0x0B, - 0x55, 0x06, 0x05, 0x02, 0xD3, 0xFF, 0x71, 0xFF, 0xC7, 0xFF, 0xFE, - 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA1, 0xFF, 0x64, 0x01, - 0x50, 0x05, 0x91, 0x0A, 0x10, 0x0F, 0xA8, 0x10, 0x88, 0x0E, 0xC5, - 0x09, 0x9B, 0x04, 0xFD, 0x00, 0x88, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, - 0xFB, 0xFF, 0xB9, 0xFF, 0x6C, 0xFF, 0x04, 0x00, 0x8A, 0x02, 0x1C, - 0x07, 0x5E, 0x0C, 0x0A, 0x10, 0x50, 0x10, 0x0B, 0x0D, 0xDE, 0x07, - 0x15, 0x03, 0x3E, 0x00, 0x6D, 0xFF, 0xAB, 0xFF, 0xF6, 0xFF, 0xEC, - 0xFF, 0x96, 0xFF, 0x78, 0xFF, 0xA7, 0x00, 0xF6, 0x03, 0x00, 0x09, - 0xF8, 0x0D, 0x93, 0x10, 0x82, 0x0F, 0x50, 0x0B, 0x06, 0x06, 0xD2, - 0x01, 0xC2, 0xFF, 0x74, 0xFF, 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0xD4, 0xFF, 0x79, 0xFF, 0xAE, 0xFF, 0x91, 0x01, 0x9D, 0x05, 0xE3, - 0x0A, 0x43, 0x0F, 0xA1, 0x10, 0x4C, 0x0E, 0x71, 0x09, 0x53, 0x04, - 0xD7, 0x00, 0x80, 0xFF, 0x8E, 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB3, - 0xFF, 0x6C, 0xFF, 0x1C, 0x00, 0xC4, 0x02, 0x6F, 0x07, 0xAA, 0x0C, - 0x2A, 0x10, 0x34, 0x10, 0xC2, 0x0C, 0x8A, 0x07, 0xD8, 0x02, 0x24, - 0x00, 0x6C, 0xFF, 0xB1, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x90, 0xFF, - 0x7E, 0xFF, 0xCB, 0x00, 0x3B, 0x04, 0x55, 0x09, 0x37, 0x0E, 0x9E, - 0x10, 0x53, 0x0F, 0xFF, 0x0A, 0xB7, 0x05, 0xA1, 0x01, 0xB3, 0xFF, - 0x77, 0xFF, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x75, - 0xFF, 0xBD, 0xFF, 0xC1, 0x01, 0xEB, 0x05, 0x35, 0x0B, 0x73, 0x0F, - 0x97, 0x10, 0x0D, 0x0E, 0x1C, 0x09, 0x0D, 0x04, 0xB3, 0x00, 0x7A, - 0xFF, 0x94, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAD, 0xFF, 0x6D, 0xFF, - 0x35, 0x00, 0x01, 0x03, 0xC2, 0x07, 0xF3, 0x0C, 0x47, 0x10, 0x15, - 0x10, 0x78, 0x0C, 0x37, 0x07, 0x9D, 0x02, 0x0C, 0x00, 0x6C, 0xFF, - 0xB7, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, 0xF0, - 0x00, 0x83, 0x04, 0xA9, 0x09, 0x74, 0x0E, 0xA6, 0x10, 0x21, 0x0F, - 0xAD, 0x0A, 0x6A, 0x05, 0x73, 0x01, 0xA5, 0xFF, 0x7B, 0xFF, 0xD7, - 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC9, 0xFF, 0x72, 0xFF, 0xCD, 0xFF, - 0xF4, 0x01, 0x3B, 0x06, 0x85, 0x0B, 0x9F, 0x0F, 0x89, 0x10, 0xCC, - 0x0D, 0xC8, 0x08, 0xC9, 0x03, 0x91, 0x00, 0x75, 0xFF, 0x9A, 0xFF, - 0xEF, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x50, 0x00, 0x3F, - 0x03, 0x16, 0x08, 0x3B, 0x0D, 0x60, 0x10, 0xF3, 0x0F, 0x2B, 0x0C, - 0xE5, 0x06, 0x65, 0x02, 0xF6, 0xFF, 0x6D, 0xFF, 0xBD, 0xFF, 0xFC, - 0xFF, 0xE1, 0xFF, 0x85, 0xFF, 0x8E, 0xFF, 0x18, 0x01, 0xCB, 0x04, - 0xFD, 0x09, 0xAF, 0x0E, 0xAA, 0x10, 0xED, 0x0E, 0x5A, 0x0A, 0x1E, - 0x05, 0x46, 0x01, 0x9A, 0xFF, 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, - 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, 0xFF, 0xDF, 0xFF, 0x28, 0x02, 0x8B, - 0x06, 0xD5, 0x0B, 0xC9, 0x0F, 0x78, 0x10, 0x88, 0x0D, 0x73, 0x08, - 0x86, 0x03, 0x71, 0x00, 0x71, 0xFF, 0xA0, 0xFF, 0xF2, 0xFF, 0xF2, - 0xFF, 0xA1, 0xFF, 0x71, 0xFF, 0x6E, 0x00, 0x7F, 0x03, 0x6A, 0x08, - 0x81, 0x0D, 0x76, 0x10, 0xCD, 0x0F, 0xDD, 0x0B, 0x94, 0x06, 0x2E, - 0x02, 0xE1, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, - 0xDC, 0xFF, 0x80, 0xFF, 0x98, 0xFF, 0x42, 0x01, 0x16, 0x05, 0x50, - 0x0A, 0xE7, 0x0E, 0xAA, 0x10, 0xB5, 0x0E, 0x06, 0x0A, 0xD3, 0x04, - 0x1C, 0x01, 0x8F, 0xFF, 0x85, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, 0xBE, - 0xFF, 0x6D, 0xFF, 0xF3, 0xFF, 0x5E, 0x02, 0xDC, 0x06, 0x23, 0x0C, - 0xEF, 0x0F, 0x63, 0x10, 0x43, 0x0D, 0x1F, 0x08, 0x46, 0x03, 0x53, - 0x00, 0x6E, 0xFF, 0xA6, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, 0xFF, - 0x75, 0xFF, 0x8D, 0x00, 0xC1, 0x03, 0xBE, 0x08, 0xC4, 0x0D, 0x88, - 0x10, 0xA4, 0x0F, 0x8E, 0x0B, 0x43, 0x06, 0xF9, 0x01, 0xCF, 0xFF, - 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, 0x7C, - 0xFF, 0xA4, 0xFF, 0x6E, 0x01, 0x61, 0x05, 0xA3, 0x0A, 0x1C, 0x0F, - 0xA7, 0x10, 0x7B, 0x0E, 0xB2, 0x09, 0x8B, 0x04, 0xF4, 0x00, 0x86, - 0xFF, 0x8A, 0xFF, 0xE4, 0xFF, 0xFA, 0xFF, 0xB8, 0xFF, 0x6C, 0xFF, - 0x09, 0x00, 0x97, 0x02, 0x2E, 0x07, 0x6F, 0x0C, 0x11, 0x10, 0x4A, - 0x10, 0xFB, 0x0C, 0xCB, 0x07, 0x07, 0x03, 0x38, 0x00, 0x6D, 0xFF, - 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, 0xAF, - 0x00, 0x05, 0x04, 0x13, 0x09, 0x06, 0x0E, 0x96, 0x10, 0x78, 0x0F, - 0x3E, 0x0B, 0xF4, 0x05, 0xC7, 0x01, 0xBF, 0xFF, 0x74, 0xFF, 0xCE, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x78, 0xFF, 0xB1, 0xFF, - 0x9C, 0x01, 0xAE, 0x05, 0xF6, 0x0A, 0x4E, 0x0F, 0x9F, 0x10, 0x3E, - 0x0E, 0x5E, 0x09, 0x43, 0x04, 0xCF, 0x00, 0x7F, 0xFF, 0x90, 0xFF, - 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x21, 0x00, 0xD2, - 0x02, 0x81, 0x07, 0xBA, 0x0C, 0x31, 0x10, 0x2E, 0x10, 0xB2, 0x0C, - 0x78, 0x07, 0xCB, 0x02, 0x1E, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, 0xF9, - 0xFF, 0xE8, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xD3, 0x00, 0x4B, 0x04, - 0x67, 0x09, 0x45, 0x0E, 0xA0, 0x10, 0x48, 0x0F, 0xEC, 0x0A, 0xA6, - 0x05, 0x97, 0x01, 0xB0, 0xFF, 0x78, 0xFF, 0xD3, 0xFF, 0x00, 0x00, - 0xFF, 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC0, 0xFF, 0xCC, 0x01, 0xFD, - 0x05, 0x47, 0x0B, 0x7D, 0x0F, 0x94, 0x10, 0xFF, 0x0D, 0x0A, 0x09, - 0xFE, 0x03, 0xAB, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, 0xF7, - 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x3B, 0x00, 0x0E, 0x03, 0xD5, 0x07, - 0x03, 0x0D, 0x4D, 0x10, 0x0E, 0x10, 0x67, 0x0C, 0x25, 0x07, 0x91, - 0x02, 0x07, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, - 0x89, 0xFF, 0x87, 0xFF, 0xF9, 0x00, 0x93, 0x04, 0xBC, 0x09, 0x82, - 0x0E, 0xA7, 0x10, 0x16, 0x0F, 0x9A, 0x0A, 0x59, 0x05, 0x69, 0x01, - 0xA3, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC8, - 0xFF, 0x71, 0xFF, 0xD1, 0xFF, 0xFF, 0x01, 0x4C, 0x06, 0x97, 0x0B, - 0xA9, 0x0F, 0x86, 0x10, 0xBD, 0x0D, 0xB5, 0x08, 0xBA, 0x03, 0x8A, - 0x00, 0x74, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, - 0x6F, 0xFF, 0x57, 0x00, 0x4D, 0x03, 0x29, 0x08, 0x4B, 0x0D, 0x65, - 0x10, 0xEB, 0x0F, 0x1A, 0x0C, 0xD3, 0x06, 0x58, 0x02, 0xF1, 0xFF, - 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x84, 0xFF, 0x90, - 0xFF, 0x21, 0x01, 0xDC, 0x04, 0x10, 0x0A, 0xBB, 0x0E, 0xAA, 0x10, - 0xE1, 0x0E, 0x47, 0x0A, 0x0D, 0x05, 0x3D, 0x01, 0x97, 0xFF, 0x81, - 0xFF, 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC2, 0xFF, 0x6F, 0xFF, - 0xE4, 0xFF, 0x34, 0x02, 0x9D, 0x06, 0xE6, 0x0B, 0xD1, 0x0F, 0x73, - 0x10, 0x79, 0x0D, 0x61, 0x08, 0x78, 0x03, 0x6A, 0x00, 0x70, 0xFF, - 0xA1, 0xFF, 0xF2, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x74, - 0x00, 0x8E, 0x03, 0x7D, 0x08, 0x90, 0x0D, 0x7A, 0x10, 0xC4, 0x0F, - 0xCC, 0x0B, 0x82, 0x06, 0x22, 0x02, 0xDD, 0xFF, 0x6F, 0xFF, 0xC4, - 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9B, 0xFF, - 0x4B, 0x01, 0x26, 0x05, 0x63, 0x0A, 0xF3, 0x0E, 0xAA, 0x10, 0xA8, - 0x0E, 0xF4, 0x09, 0xC3, 0x04, 0x13, 0x01, 0x8D, 0xFF, 0x86, 0xFF, - 0xE1, 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xF8, 0xFF, 0x6B, - 0x02, 0xEE, 0x06, 0x34, 0x0C, 0xF7, 0x0F, 0x5D, 0x10, 0x33, 0x0D, - 0x0D, 0x08, 0x38, 0x03, 0x4D, 0x00, 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, - 0xFF, 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x94, 0x00, 0xD0, 0x03, - 0xD1, 0x08, 0xD3, 0x0D, 0x8B, 0x10, 0x9A, 0x0F, 0x7C, 0x0B, 0x32, - 0x06, 0xEE, 0x01, 0xCB, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, - 0x00, 0x00, 0xD6, 0xFF, 0x7B, 0xFF, 0xA7, 0xFF, 0x78, 0x01, 0x72, - 0x05, 0xB6, 0x0A, 0x27, 0x0F, 0xA5, 0x10, 0x6E, 0x0E, 0xA0, 0x09, - 0x7B, 0x04, 0xEC, 0x00, 0x85, 0xFF, 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, - 0xFF, 0xB6, 0xFF, 0x6C, 0xFF, 0x0E, 0x00, 0xA4, 0x02, 0x41, 0x07, - 0x80, 0x0C, 0x19, 0x10, 0x44, 0x10, 0xEB, 0x0C, 0xB9, 0x07, 0xFA, - 0x02, 0x32, 0x00, 0x6D, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, - 0x93, 0xFF, 0x7B, 0xFF, 0xB7, 0x00, 0x15, 0x04, 0x26, 0x09, 0x14, - 0x0E, 0x98, 0x10, 0x6D, 0x0F, 0x2C, 0x0B, 0xE3, 0x05, 0xBC, 0x01, - 0xBB, 0xFF, 0x75, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, - 0xFF, 0x77, 0xFF, 0xB5, 0xFF, 0xA6, 0x01, 0xC0, 0x05, 0x08, 0x0B, - 0x58, 0x0F, 0x9D, 0x10, 0x30, 0x0E, 0x4B, 0x09, 0x34, 0x04, 0xC6, - 0x00, 0x7D, 0xFF, 0x91, 0xFF, 0xE9, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, - 0x6C, 0xFF, 0x27, 0x00, 0xDF, 0x02, 0x94, 0x07, 0xCA, 0x0C, 0x37, - 0x10, 0x27, 0x10, 0xA1, 0x0C, 0x65, 0x07, 0xBE, 0x02, 0x19, 0x00, - 0x6C, 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8E, 0xFF, 0x81, - 0xFF, 0xDB, 0x00, 0x5B, 0x04, 0x7A, 0x09, 0x53, 0x0E, 0xA2, 0x10, - 0x3D, 0x0F, 0xDA, 0x0A, 0x95, 0x05, 0x8C, 0x01, 0xAD, 0xFF, 0x79, - 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xFF, 0x73, 0xFF, - 0xC4, 0xFF, 0xD7, 0x01, 0x0E, 0x06, 0x59, 0x0B, 0x87, 0x0F, 0x91, - 0x10, 0xF0, 0x0D, 0xF7, 0x08, 0xEF, 0x03, 0xA3, 0x00, 0x78, 0xFF, - 0x97, 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xAA, 0xFF, 0x6D, 0xFF, 0x41, - 0x00, 0x1C, 0x03, 0xE7, 0x07, 0x13, 0x0D, 0x52, 0x10, 0x06, 0x10, - 0x56, 0x0C, 0x13, 0x07, 0x84, 0x02, 0x02, 0x00, 0x6D, 0xFF, 0xBA, - 0xFF, 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x01, 0x01, - 0xA3, 0x04, 0xCE, 0x09, 0x8F, 0x0E, 0xA8, 0x10, 0x0A, 0x0F, 0x88, - 0x0A, 0x48, 0x05, 0x5F, 0x01, 0xA0, 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, - 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, 0x70, 0xFF, 0xD5, 0xFF, 0x0B, - 0x02, 0x5E, 0x06, 0xA9, 0x0B, 0xB2, 0x0F, 0x82, 0x10, 0xAE, 0x0D, - 0xA2, 0x08, 0xAB, 0x03, 0x82, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, - 0xFF, 0xF3, 0xFF, 0xA4, 0xFF, 0x6F, 0xFF, 0x5D, 0x00, 0x5B, 0x03, - 0x3B, 0x08, 0x5A, 0x0D, 0x6A, 0x10, 0xE2, 0x0F, 0x09, 0x0C, 0xC1, - 0x06, 0x4C, 0x02, 0xEC, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFC, 0xFF, - 0xDF, 0xFF, 0x83, 0xFF, 0x93, 0xFF, 0x2A, 0x01, 0xEC, 0x04, 0x22, - 0x0A, 0xC8, 0x0E, 0xAB, 0x10, 0xD4, 0x0E, 0x35, 0x0A, 0xFD, 0x04, - 0x33, 0x01, 0x95, 0xFF, 0x82, 0xFF, 0xDE, 0xFF, 0x00, 0x00, 0xFD, - 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE8, 0xFF, 0x40, 0x02, 0xAF, 0x06, - 0xF7, 0x0B, 0xDA, 0x0F, 0x6F, 0x10, 0x6A, 0x0D, 0x4E, 0x08, 0x6A, - 0x03, 0x64, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, - 0x9E, 0xFF, 0x72, 0xFF, 0x7B, 0x00, 0x9C, 0x03, 0x90, 0x08, 0x9F, - 0x0D, 0x7E, 0x10, 0xBB, 0x0F, 0xBA, 0x0B, 0x70, 0x06, 0x16, 0x02, - 0xD9, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, - 0xFF, 0x7E, 0xFF, 0x9D, 0xFF, 0x55, 0x01, 0x37, 0x05, 0x75, 0x0A, - 0xFF, 0x0E, 0xA9, 0x10, 0x9C, 0x0E, 0xE1, 0x09, 0xB3, 0x04, 0x0A, - 0x01, 0x8B, 0xFF, 0x87, 0xFF, 0xE2, 0xFF, 0xFB, 0xFF, 0xBB, 0xFF, - 0x6D, 0xFF, 0xFD, 0xFF, 0x77, 0x02, 0x01, 0x07, 0x45, 0x0C, 0xFF, - 0x0F, 0x58, 0x10, 0x23, 0x0D, 0xFA, 0x07, 0x2A, 0x03, 0x47, 0x00, - 0x6E, 0xFF, 0xA9, 0xFF, 0xF5, 0xFF, 0xED, 0xFF, 0x98, 0xFF, 0x77, - 0xFF, 0x9C, 0x00, 0xDF, 0x03, 0xE4, 0x08, 0xE2, 0x0D, 0x8E, 0x10, - 0x91, 0x0F, 0x6B, 0x0B, 0x20, 0x06, 0xE3, 0x01, 0xC8, 0xFF, 0x73, - 0xFF, 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x7A, 0xFF, - 0xAA, 0xFF, 0x82, 0x01, 0x83, 0x05, 0xC8, 0x0A, 0x32, 0x0F, 0xA4, - 0x10, 0x60, 0x0E, 0x8D, 0x09, 0x6B, 0x04, 0xE3, 0x00, 0x83, 0xFF, - 0x8D, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, 0xB5, 0xFF, 0x6C, 0xFF, 0x14, - 0x00, 0xB1, 0x02, 0x53, 0x07, 0x91, 0x0C, 0x20, 0x10, 0x3E, 0x10, - 0xDB, 0x0C, 0xA6, 0x07, 0xEC, 0x02, 0x2C, 0x00, 0x6C, 0xFF, 0xAF, - 0xFF, 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7C, 0xFF, 0xBE, 0x00, - 0x24, 0x04, 0x38, 0x09, 0x22, 0x0E, 0x9B, 0x10, 0x63, 0x0F, 0x1A, - 0x0B, 0xD1, 0x05, 0xB1, 0x01, 0xB8, 0xFF, 0x76, 0xFF, 0xD0, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB8, 0xFF, 0xB1, - 0x01, 0xD1, 0x05, 0x1A, 0x0B, 0x63, 0x0F, 0x9B, 0x10, 0x22, 0x0E, - 0x38, 0x09, 0x24, 0x04, 0xBE, 0x00, 0x7C, 0xFF, 0x92, 0xFF, 0xEA, - 0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0x6C, 0xFF, 0x2C, 0x00, 0xEC, 0x02, - 0xA6, 0x07, 0xDB, 0x0C, 0x3E, 0x10, 0x20, 0x10, 0x91, 0x0C, 0x53, - 0x07, 0xB1, 0x02, 0x14, 0x00, 0x6C, 0xFF, 0xB5, 0xFF, 0xFA, 0xFF, - 0xE6, 0xFF, 0x8D, 0xFF, 0x83, 0xFF, 0xE3, 0x00, 0x6B, 0x04, 0x8D, - 0x09, 0x60, 0x0E, 0xA4, 0x10, 0x32, 0x0F, 0xC8, 0x0A, 0x83, 0x05, - 0x82, 0x01, 0xAA, 0xFF, 0x7A, 0xFF, 0xD5, 0xFF, 0x00, 0x00, 0xFF, - 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC8, 0xFF, 0xE3, 0x01, 0x20, 0x06, - 0x6B, 0x0B, 0x91, 0x0F, 0x8E, 0x10, 0xE2, 0x0D, 0xE4, 0x08, 0xDF, - 0x03, 0x9C, 0x00, 0x77, 0xFF, 0x98, 0xFF, 0xED, 0xFF, 0xF5, 0xFF, - 0xA9, 0xFF, 0x6E, 0xFF, 0x47, 0x00, 0x2A, 0x03, 0xFA, 0x07, 0x23, - 0x0D, 0x58, 0x10, 0xFF, 0x0F, 0x45, 0x0C, 0x01, 0x07, 0x77, 0x02, - 0xFD, 0xFF, 0x6D, 0xFF, 0xBB, 0xFF, 0xFB, 0xFF, 0xE2, 0xFF, 0x87, - 0xFF, 0x8B, 0xFF, 0x0A, 0x01, 0xB3, 0x04, 0xE1, 0x09, 0x9C, 0x0E, - 0xA9, 0x10, 0xFF, 0x0E, 0x75, 0x0A, 0x37, 0x05, 0x55, 0x01, 0x9D, - 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, - 0x70, 0xFF, 0xD9, 0xFF, 0x16, 0x02, 0x70, 0x06, 0xBA, 0x0B, 0xBB, - 0x0F, 0x7E, 0x10, 0x9F, 0x0D, 0x90, 0x08, 0x9C, 0x03, 0x7B, 0x00, - 0x72, 0xFF, 0x9E, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA3, 0xFF, 0x70, - 0xFF, 0x64, 0x00, 0x6A, 0x03, 0x4E, 0x08, 0x6A, 0x0D, 0x6F, 0x10, - 0xDA, 0x0F, 0xF7, 0x0B, 0xAF, 0x06, 0x40, 0x02, 0xE8, 0xFF, 0x6E, - 0xFF, 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDE, 0xFF, 0x82, 0xFF, - 0x95, 0xFF, 0x33, 0x01, 0xFD, 0x04, 0x35, 0x0A, 0xD4, 0x0E, 0xAB, - 0x10, 0xC8, 0x0E, 0x22, 0x0A, 0xEC, 0x04, 0x2A, 0x01, 0x93, 0xFF, - 0x83, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xC0, 0xFF, 0x6E, 0xFF, 0xEC, - 0xFF, 0x4C, 0x02, 0xC1, 0x06, 0x09, 0x0C, 0xE2, 0x0F, 0x6A, 0x10, - 0x5A, 0x0D, 0x3B, 0x08, 0x5B, 0x03, 0x5D, 0x00, 0x6F, 0xFF, 0xA4, - 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, 0xFF, 0x73, 0xFF, 0x82, 0x00, - 0xAB, 0x03, 0xA2, 0x08, 0xAE, 0x0D, 0x82, 0x10, 0xB2, 0x0F, 0xA9, - 0x0B, 0x5E, 0x06, 0x0B, 0x02, 0xD5, 0xFF, 0x70, 0xFF, 0xC7, 0xFF, - 0xFE, 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA0, 0xFF, 0x5F, - 0x01, 0x48, 0x05, 0x88, 0x0A, 0x0A, 0x0F, 0xA8, 0x10, 0x8F, 0x0E, - 0xCE, 0x09, 0xA3, 0x04, 0x01, 0x01, 0x89, 0xFF, 0x88, 0xFF, 0xE3, - 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, 0xFF, 0x02, 0x00, 0x84, 0x02, - 0x13, 0x07, 0x56, 0x0C, 0x06, 0x10, 0x52, 0x10, 0x13, 0x0D, 0xE7, - 0x07, 0x1C, 0x03, 0x41, 0x00, 0x6D, 0xFF, 0xAA, 0xFF, 0xF6, 0xFF, - 0xED, 0xFF, 0x97, 0xFF, 0x78, 0xFF, 0xA3, 0x00, 0xEF, 0x03, 0xF7, - 0x08, 0xF0, 0x0D, 0x91, 0x10, 0x87, 0x0F, 0x59, 0x0B, 0x0E, 0x06, - 0xD7, 0x01, 0xC4, 0xFF, 0x73, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0x00, - 0x00, 0xD4, 0xFF, 0x79, 0xFF, 0xAD, 0xFF, 0x8C, 0x01, 0x95, 0x05, - 0xDA, 0x0A, 0x3D, 0x0F, 0xA2, 0x10, 0x53, 0x0E, 0x7A, 0x09, 0x5B, - 0x04, 0xDB, 0x00, 0x81, 0xFF, 0x8E, 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, - 0xB4, 0xFF, 0x6C, 0xFF, 0x19, 0x00, 0xBE, 0x02, 0x65, 0x07, 0xA1, - 0x0C, 0x27, 0x10, 0x37, 0x10, 0xCA, 0x0C, 0x94, 0x07, 0xDF, 0x02, - 0x27, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x91, - 0xFF, 0x7D, 0xFF, 0xC6, 0x00, 0x34, 0x04, 0x4B, 0x09, 0x30, 0x0E, - 0x9D, 0x10, 0x58, 0x0F, 0x08, 0x0B, 0xC0, 0x05, 0xA6, 0x01, 0xB5, - 0xFF, 0x77, 0xFF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, - 0x75, 0xFF, 0xBB, 0xFF, 0xBC, 0x01, 0xE3, 0x05, 0x2C, 0x0B, 0x6D, - 0x0F, 0x98, 0x10, 0x14, 0x0E, 0x26, 0x09, 0x15, 0x04, 0xB7, 0x00, - 0x7B, 0xFF, 0x93, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAE, 0xFF, 0x6D, - 0xFF, 0x32, 0x00, 0xFA, 0x02, 0xB9, 0x07, 0xEB, 0x0C, 0x44, 0x10, - 0x19, 0x10, 0x80, 0x0C, 0x41, 0x07, 0xA4, 0x02, 0x0E, 0x00, 0x6C, - 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, - 0xEC, 0x00, 0x7B, 0x04, 0xA0, 0x09, 0x6E, 0x0E, 0xA5, 0x10, 0x27, - 0x0F, 0xB6, 0x0A, 0x72, 0x05, 0x78, 0x01, 0xA7, 0xFF, 0x7B, 0xFF, - 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xCA, 0xFF, 0x72, 0xFF, 0xCB, - 0xFF, 0xEE, 0x01, 0x32, 0x06, 0x7C, 0x0B, 0x9A, 0x0F, 0x8B, 0x10, - 0xD3, 0x0D, 0xD1, 0x08, 0xD0, 0x03, 0x94, 0x00, 0x76, 0xFF, 0x99, - 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x4D, 0x00, - 0x38, 0x03, 0x0D, 0x08, 0x33, 0x0D, 0x5D, 0x10, 0xF7, 0x0F, 0x34, - 0x0C, 0xEE, 0x06, 0x6B, 0x02, 0xF8, 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, - 0xFC, 0xFF, 0xE1, 0xFF, 0x86, 0xFF, 0x8D, 0xFF, 0x13, 0x01, 0xC3, - 0x04, 0xF4, 0x09, 0xA8, 0x0E, 0xAA, 0x10, 0xF3, 0x0E, 0x63, 0x0A, - 0x26, 0x05, 0x4B, 0x01, 0x9B, 0xFF, 0x7F, 0xFF, 0xDB, 0xFF, 0x00, - 0x00, 0xFD, 0xFF, 0xC4, 0xFF, 0x6F, 0xFF, 0xDD, 0xFF, 0x22, 0x02, - 0x82, 0x06, 0xCC, 0x0B, 0xC4, 0x0F, 0x7A, 0x10, 0x90, 0x0D, 0x7D, - 0x08, 0x8E, 0x03, 0x74, 0x00, 0x72, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, - 0xF2, 0xFF, 0xA1, 0xFF, 0x70, 0xFF, 0x6A, 0x00, 0x78, 0x03, 0x61, - 0x08, 0x79, 0x0D, 0x73, 0x10, 0xD1, 0x0F, 0xE6, 0x0B, 0x9D, 0x06, - 0x34, 0x02, 0xE4, 0xFF, 0x6F, 0xFF, 0xC2, 0xFF, 0xFD, 0xFF, 0x00, - 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x97, 0xFF, 0x3D, 0x01, 0x0D, 0x05, - 0x47, 0x0A, 0xE1, 0x0E, 0xAA, 0x10, 0xBB, 0x0E, 0x10, 0x0A, 0xDC, - 0x04, 0x21, 0x01, 0x90, 0xFF, 0x84, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, - 0xBE, 0xFF, 0x6D, 0xFF, 0xF1, 0xFF, 0x58, 0x02, 0xD3, 0x06, 0x1A, - 0x0C, 0xEB, 0x0F, 0x65, 0x10, 0x4B, 0x0D, 0x29, 0x08, 0x4D, 0x03, - 0x57, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, - 0xFF, 0x74, 0xFF, 0x8A, 0x00, 0xBA, 0x03, 0xB5, 0x08, 0xBD, 0x0D, - 0x86, 0x10, 0xA9, 0x0F, 0x97, 0x0B, 0x4C, 0x06, 0xFF, 0x01, 0xD1, - 0xFF, 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, - 0x7C, 0xFF, 0xA3, 0xFF, 0x69, 0x01, 0x59, 0x05, 0x9A, 0x0A, 0x16, - 0x0F, 0xA7, 0x10, 0x82, 0x0E, 0xBC, 0x09, 0x93, 0x04, 0xF9, 0x00, - 0x87, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, 0xFB, 0xFF, 0xB8, 0xFF, 0x6C, - 0xFF, 0x07, 0x00, 0x91, 0x02, 0x25, 0x07, 0x67, 0x0C, 0x0E, 0x10, - 0x4D, 0x10, 0x03, 0x0D, 0xD5, 0x07, 0x0E, 0x03, 0x3B, 0x00, 0x6D, - 0xFF, 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, - 0xAB, 0x00, 0xFE, 0x03, 0x0A, 0x09, 0xFF, 0x0D, 0x94, 0x10, 0x7D, - 0x0F, 0x47, 0x0B, 0xFD, 0x05, 0xCC, 0x01, 0xC0, 0xFF, 0x74, 0xFF, - 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD3, 0xFF, 0x78, 0xFF, 0xB0, - 0xFF, 0x97, 0x01, 0xA6, 0x05, 0xEC, 0x0A, 0x48, 0x0F, 0xA0, 0x10, - 0x45, 0x0E, 0x67, 0x09, 0x4B, 0x04, 0xD3, 0x00, 0x80, 0xFF, 0x8F, - 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x1E, 0x00, - 0xCB, 0x02, 0x78, 0x07, 0xB2, 0x0C, 0x2E, 0x10, 0x31, 0x10, 0xBA, - 0x0C, 0x81, 0x07, 0xD2, 0x02, 0x21, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, - 0xF9, 0xFF, 0xE8, 0xFF, 0x90, 0xFF, 0x7F, 0xFF, 0xCF, 0x00, 0x43, - 0x04, 0x5E, 0x09, 0x3E, 0x0E, 0x9F, 0x10, 0x4E, 0x0F, 0xF6, 0x0A, - 0xAE, 0x05, 0x9C, 0x01, 0xB1, 0xFF, 0x78, 0xFF, 0xD2, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x74, 0xFF, 0xBF, 0xFF, 0xC7, 0x01, - 0xF4, 0x05, 0x3E, 0x0B, 0x78, 0x0F, 0x96, 0x10, 0x06, 0x0E, 0x13, - 0x09, 0x05, 0x04, 0xAF, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, - 0xF7, 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x38, 0x00, 0x07, 0x03, 0xCB, - 0x07, 0xFB, 0x0C, 0x4A, 0x10, 0x11, 0x10, 0x6F, 0x0C, 0x2E, 0x07, - 0x97, 0x02, 0x09, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFA, 0xFF, 0xE4, - 0xFF, 0x8A, 0xFF, 0x86, 0xFF, 0xF4, 0x00, 0x8B, 0x04, 0xB2, 0x09, - 0x7B, 0x0E, 0xA7, 0x10, 0x1C, 0x0F, 0xA3, 0x0A, 0x61, 0x05, 0x6E, - 0x01, 0xA4, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, - 0xC8, 0xFF, 0x71, 0xFF, 0xCF, 0xFF, 0xF9, 0x01, 0x43, 0x06, 0x8E, - 0x0B, 0xA4, 0x0F, 0x88, 0x10, 0xC4, 0x0D, 0xBE, 0x08, 0xC1, 0x03, - 0x8D, 0x00, 0x75, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA6, - 0xFF, 0x6E, 0xFF, 0x53, 0x00, 0x46, 0x03, 0x1F, 0x08, 0x43, 0x0D, - 0x63, 0x10, 0xEF, 0x0F, 0x23, 0x0C, 0xDC, 0x06, 0x5E, 0x02, 0xF3, - 0xFF, 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x85, 0xFF, - 0x8F, 0xFF, 0x1C, 0x01, 0xD3, 0x04, 0x06, 0x0A, 0xB5, 0x0E, 0xAA, - 0x10, 0xE7, 0x0E, 0x50, 0x0A, 0x16, 0x05, 0x42, 0x01, 0x98, 0xFF, - 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, - 0xFF, 0xE1, 0xFF, 0x2E, 0x02, 0x94, 0x06, 0xDD, 0x0B, 0xCD, 0x0F, - 0x76, 0x10, 0x81, 0x0D, 0x6A, 0x08, 0x7F, 0x03, 0x6E, 0x00, 0x71, - 0xFF, 0xA1, 0xFF, 0xF2, 0xFF, 0x00, 0x00, 0x15, 0x00, 0xD1, 0xFF, - 0x8B, 0xFE, 0xBC, 0xFD, 0xE1, 0x00, 0x84, 0x09, 0xB0, 0x13, 0x47, - 0x18, 0xB0, 0x13, 0x84, 0x09, 0xE1, 0x00, 0xBC, 0xFD, 0x8B, 0xFE, - 0xD1, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xDA, 0x00, 0x30, - 0x00, 0x5D, 0xFC, 0xB3, 0xFC, 0x35, 0x0A, 0xC2, 0x1C, 0x24, 0x20, - 0x48, 0x10, 0x5D, 0xFF, 0x74, 0xFB, 0x3A, 0xFF, 0xFB, 0x00, 0x42, - 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2C, 0x00, 0xF3, 0x00, 0xAD, 0xFF, - 0xC5, 0xFB, 0x11, 0xFE, 0xAF, 0x0D, 0xEF, 0x1E, 0x68, 0x1E, 0xBC, - 0x0C, 0xA7, 0xFD, 0xEA, 0xFB, 0xD3, 0xFF, 0xEE, 0x00, 0x24, 0x00, - 0xFA, 0xFF, 0xF7, 0xFF, 0x4C, 0x00, 0xFB, 0x00, 0x0C, 0xFF, 0x5F, - 0xFB, 0xE8, 0xFF, 0x3D, 0x11, 0x7E, 0x20, 0x13, 0x1C, 0x4C, 0x09, - 0x6A, 0xFC, 0x8C, 0xFC, 0x4E, 0x00, 0xD1, 0x00, 0x0E, 0x00, 0xFD, - 0xFF, 0xF7, 0xFF, 0x72, 0x00, 0xEC, 0x00, 0x55, 0xFE, 0x3D, 0xFB, - 0x37, 0x02, 0xBE, 0x14, 0x5D, 0x21, 0x40, 0x19, 0x18, 0x06, 0xA2, - 0xFB, 0x47, 0xFD, 0xA7, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xC0, 0x00, 0x92, 0xFD, 0x73, - 0xFB, 0xF2, 0x04, 0x0E, 0x18, 0x81, 0x21, 0x0C, 0x16, 0x37, 0x03, - 0x47, 0xFB, 0x0B, 0xFE, 0xDF, 0x00, 0x82, 0x00, 0xF9, 0xFF, 0xFE, - 0xFF, 0x08, 0x00, 0xC3, 0x00, 0x74, 0x00, 0xD2, 0xFC, 0x10, 0xFC, - 0x08, 0x08, 0x0A, 0x1B, 0xE9, 0x20, 0x9A, 0x12, 0xBE, 0x00, 0x49, - 0xFB, 0xC8, 0xFE, 0xF9, 0x00, 0x5A, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, - 0x1B, 0x00, 0xE4, 0x00, 0x06, 0x00, 0x24, 0xFC, 0x1E, 0xFD, 0x65, - 0x0B, 0x94, 0x1D, 0x9D, 0x1F, 0x0D, 0x0F, 0xB8, 0xFE, 0x96, 0xFB, - 0x72, 0xFF, 0xF9, 0x00, 0x37, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x36, - 0x00, 0xF8, 0x00, 0x78, 0xFF, 0x9B, 0xFB, 0xA6, 0xFE, 0xE9, 0x0E, - 0x8D, 0x1F, 0xAA, 0x1D, 0x87, 0x0B, 0x2B, 0xFD, 0x1E, 0xFC, 0x02, - 0x00, 0xE5, 0x00, 0x1C, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x58, 0x00, - 0xF9, 0x00, 0xCF, 0xFE, 0x4A, 0xFB, 0xA7, 0x00, 0x77, 0x12, 0xE0, - 0x20, 0x26, 0x1B, 0x28, 0x08, 0x18, 0xFC, 0xCB, 0xFC, 0x71, 0x00, - 0xC5, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x80, 0x00, 0xE1, - 0x00, 0x13, 0xFE, 0x45, 0xFB, 0x1D, 0x03, 0xEB, 0x15, 0x7F, 0x21, - 0x2D, 0x18, 0x0E, 0x05, 0x77, 0xFB, 0x8B, 0xFD, 0xBE, 0x00, 0x9D, - 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA9, 0x00, - 0xAA, 0x00, 0x4F, 0xFD, 0x9D, 0xFB, 0xFA, 0x05, 0x22, 0x19, 0x62, - 0x21, 0xE0, 0x14, 0x50, 0x02, 0x3E, 0xFB, 0x4E, 0xFE, 0xEB, 0x00, - 0x73, 0x00, 0xF7, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xD0, 0x00, 0x52, - 0x00, 0x93, 0xFC, 0x60, 0xFC, 0x2C, 0x09, 0xFA, 0x1B, 0x8A, 0x20, - 0x60, 0x11, 0xFD, 0xFF, 0x5C, 0xFB, 0x06, 0xFF, 0xFB, 0x00, 0x4D, - 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x23, 0x00, 0xED, 0x00, 0xD9, 0xFF, - 0xEF, 0xFB, 0x98, 0xFD, 0x99, 0x0C, 0x54, 0x1E, 0x02, 0x1F, 0xD2, - 0x0D, 0x20, 0xFE, 0xC0, 0xFB, 0xA7, 0xFF, 0xF4, 0x00, 0x2D, 0x00, - 0xF9, 0xFF, 0xF8, 0xFF, 0x41, 0x00, 0xFB, 0x00, 0x41, 0xFF, 0x78, - 0xFB, 0x4A, 0xFF, 0x25, 0x10, 0x16, 0x20, 0xDA, 0x1C, 0x56, 0x0A, - 0xBE, 0xFC, 0x56, 0xFC, 0x2C, 0x00, 0xDB, 0x00, 0x14, 0x00, 0xFD, - 0xFF, 0xF7, 0xFF, 0x66, 0x00, 0xF4, 0x00, 0x8F, 0xFE, 0x3F, 0xFB, - 0x75, 0x01, 0xAE, 0x13, 0x2C, 0x21, 0x2A, 0x1A, 0x0D, 0x07, 0xD4, - 0xFB, 0x0C, 0xFD, 0x8F, 0x00, 0xB7, 0x00, 0x03, 0x00, 0xFF, 0xFF, - 0x00, 0x00, 0xFA, 0xFF, 0x8E, 0x00, 0xD1, 0x00, 0xCF, 0xFD, 0x58, - 0xFB, 0x10, 0x04, 0x10, 0x17, 0x8A, 0x21, 0x10, 0x17, 0x10, 0x04, - 0x58, 0xFB, 0xCF, 0xFD, 0xD1, 0x00, 0x8E, 0x00, 0xFA, 0xFF, 0xFF, - 0xFF, 0x03, 0x00, 0xB7, 0x00, 0x8F, 0x00, 0x0C, 0xFD, 0xD4, 0xFB, - 0x0D, 0x07, 0x2A, 0x1A, 0x2C, 0x21, 0xAE, 0x13, 0x75, 0x01, 0x3F, - 0xFB, 0x8F, 0xFE, 0xF4, 0x00, 0x66, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, - 0x14, 0x00, 0xDB, 0x00, 0x2C, 0x00, 0x56, 0xFC, 0xBE, 0xFC, 0x56, - 0x0A, 0xDA, 0x1C, 0x16, 0x20, 0x25, 0x10, 0x4A, 0xFF, 0x78, 0xFB, - 0x41, 0xFF, 0xFB, 0x00, 0x41, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2D, - 0x00, 0xF4, 0x00, 0xA7, 0xFF, 0xC0, 0xFB, 0x20, 0xFE, 0xD2, 0x0D, - 0x02, 0x1F, 0x54, 0x1E, 0x99, 0x0C, 0x98, 0xFD, 0xEF, 0xFB, 0xD9, - 0xFF, 0xED, 0x00, 0x23, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4D, 0x00, - 0xFB, 0x00, 0x06, 0xFF, 0x5C, 0xFB, 0xFD, 0xFF, 0x60, 0x11, 0x8A, - 0x20, 0xFA, 0x1B, 0x2C, 0x09, 0x60, 0xFC, 0x93, 0xFC, 0x52, 0x00, - 0xD0, 0x00, 0x0D, 0x00, 0xFE, 0xFF, 0xF7, 0xFF, 0x73, 0x00, 0xEB, - 0x00, 0x4E, 0xFE, 0x3E, 0xFB, 0x50, 0x02, 0xE0, 0x14, 0x62, 0x21, - 0x22, 0x19, 0xFA, 0x05, 0x9D, 0xFB, 0x4F, 0xFD, 0xAA, 0x00, 0xA9, - 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x9D, 0x00, - 0xBE, 0x00, 0x8B, 0xFD, 0x77, 0xFB, 0x0E, 0x05, 0x2D, 0x18, 0x7F, - 0x21, 0xEB, 0x15, 0x1D, 0x03, 0x45, 0xFB, 0x13, 0xFE, 0xE1, 0x00, - 0x80, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x08, 0x00, 0xC5, 0x00, 0x71, - 0x00, 0xCB, 0xFC, 0x18, 0xFC, 0x28, 0x08, 0x26, 0x1B, 0xE0, 0x20, - 0x77, 0x12, 0xA7, 0x00, 0x4A, 0xFB, 0xCF, 0xFE, 0xF9, 0x00, 0x58, - 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1C, 0x00, 0xE5, 0x00, 0x02, 0x00, - 0x1E, 0xFC, 0x2B, 0xFD, 0x87, 0x0B, 0xAA, 0x1D, 0x8D, 0x1F, 0xE9, - 0x0E, 0xA6, 0xFE, 0x9B, 0xFB, 0x78, 0xFF, 0xF8, 0x00, 0x36, 0x00, - 0xF9, 0xFF, 0xF8, 0xFF, 0x37, 0x00, 0xF9, 0x00, 0x72, 0xFF, 0x96, - 0xFB, 0xB8, 0xFE, 0x0D, 0x0F, 0x9D, 0x1F, 0x94, 0x1D, 0x65, 0x0B, - 0x1E, 0xFD, 0x24, 0xFC, 0x06, 0x00, 0xE4, 0x00, 0x1B, 0x00, 0xFC, - 0xFF, 0xF7, 0xFF, 0x5A, 0x00, 0xF9, 0x00, 0xC8, 0xFE, 0x49, 0xFB, - 0xBE, 0x00, 0x9A, 0x12, 0xE9, 0x20, 0x0A, 0x1B, 0x08, 0x08, 0x10, - 0xFC, 0xD2, 0xFC, 0x74, 0x00, 0xC3, 0x00, 0x08, 0x00, 0xFE, 0xFF, - 0xF9, 0xFF, 0x82, 0x00, 0xDF, 0x00, 0x0B, 0xFE, 0x47, 0xFB, 0x37, - 0x03, 0x0C, 0x16, 0x81, 0x21, 0x0E, 0x18, 0xF2, 0x04, 0x73, 0xFB, - 0x92, 0xFD, 0xC0, 0x00, 0x9B, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xAB, 0x00, 0xA7, 0x00, 0x47, 0xFD, 0xA2, 0xFB, - 0x18, 0x06, 0x40, 0x19, 0x5D, 0x21, 0xBE, 0x14, 0x37, 0x02, 0x3D, - 0xFB, 0x55, 0xFE, 0xEC, 0x00, 0x72, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, - 0x0E, 0x00, 0xD1, 0x00, 0x4E, 0x00, 0x8C, 0xFC, 0x6A, 0xFC, 0x4C, - 0x09, 0x13, 0x1C, 0x7E, 0x20, 0x3D, 0x11, 0xE8, 0xFF, 0x5F, 0xFB, - 0x0C, 0xFF, 0xFB, 0x00, 0x4C, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x24, - 0x00, 0xEE, 0x00, 0xD3, 0xFF, 0xEA, 0xFB, 0xA7, 0xFD, 0xBC, 0x0C, - 0x68, 0x1E, 0xEF, 0x1E, 0xAF, 0x0D, 0x11, 0xFE, 0xC5, 0xFB, 0xAD, - 0xFF, 0xF3, 0x00, 0x2C, 0x00, 0xFA, 0xFF, 0xF8, 0xFF, 0x42, 0x00, - 0xFB, 0x00, 0x3A, 0xFF, 0x74, 0xFB, 0x5D, 0xFF, 0x48, 0x10, 0x24, - 0x20, 0xC2, 0x1C, 0x35, 0x0A, 0xB3, 0xFC, 0x5D, 0xFC, 0x30, 0x00, - 0xDA, 0x00, 0x13, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x67, 0x00, 0xF3, - 0x00, 0x88, 0xFE, 0x3E, 0xFB, 0x8C, 0x01, 0xD0, 0x13, 0x33, 0x21, - 0x0D, 0x1A, 0xEE, 0x06, 0xCD, 0xFB, 0x13, 0xFD, 0x92, 0x00, 0xB6, - 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFA, 0xFF, 0x90, 0x00, - 0xCF, 0x00, 0xC7, 0xFD, 0x5B, 0xFB, 0x2B, 0x04, 0x31, 0x17, 0x8A, - 0x21, 0xF0, 0x16, 0xF4, 0x03, 0x56, 0xFB, 0xD6, 0xFD, 0xD3, 0x00, - 0x8D, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0xB9, 0x00, 0x8C, - 0x00, 0x05, 0xFD, 0xDB, 0xFB, 0x2C, 0x07, 0x47, 0x1A, 0x25, 0x21, - 0x8B, 0x13, 0x5D, 0x01, 0x40, 0xFB, 0x97, 0xFE, 0xF5, 0x00, 0x64, - 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x15, 0x00, 0xDC, 0x00, 0x27, 0x00, - 0x50, 0xFC, 0xCA, 0xFC, 0x78, 0x0A, 0xF2, 0x1C, 0x07, 0x20, 0x02, - 0x10, 0x37, 0xFF, 0x7B, 0xFB, 0x47, 0xFF, 0xFB, 0x00, 0x40, 0x00, - 0xF8, 0xFF, 0xF9, 0xFF, 0x2E, 0x00, 0xF5, 0x00, 0xA2, 0xFF, 0xBB, - 0xFB, 0x31, 0xFE, 0xF5, 0x0D, 0x14, 0x1F, 0x3F, 0x1E, 0x77, 0x0C, - 0x8A, 0xFD, 0xF5, 0xFB, 0xDE, 0xFF, 0xEC, 0x00, 0x22, 0x00, 0xFB, - 0xFF, 0xF7, 0xFF, 0x4E, 0x00, 0xFB, 0x00, 0xFF, 0xFE, 0x59, 0xFB, - 0x11, 0x00, 0x83, 0x11, 0x96, 0x20, 0xE0, 0x1B, 0x0B, 0x09, 0x56, - 0xFC, 0x99, 0xFC, 0x56, 0x00, 0xCE, 0x00, 0x0D, 0x00, 0xFE, 0xFF, - 0xF8, 0xFF, 0x75, 0x00, 0xEA, 0x00, 0x47, 0xFE, 0x3E, 0xFB, 0x69, - 0x02, 0x02, 0x15, 0x66, 0x21, 0x04, 0x19, 0xDC, 0x05, 0x98, 0xFB, - 0x56, 0xFD, 0xAD, 0x00, 0xA8, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, - 0x00, 0xFD, 0xFF, 0x9E, 0x00, 0xBC, 0x00, 0x83, 0xFD, 0x7B, 0xFB, - 0x2B, 0x05, 0x4C, 0x18, 0x7C, 0x21, 0xCA, 0x15, 0x03, 0x03, 0x44, - 0xFB, 0x1A, 0xFE, 0xE2, 0x00, 0x7E, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, - 0x09, 0x00, 0xC6, 0x00, 0x6D, 0x00, 0xC3, 0xFC, 0x20, 0xFC, 0x49, - 0x08, 0x41, 0x1B, 0xD6, 0x20, 0x54, 0x12, 0x92, 0x00, 0x4C, 0xFB, - 0xD6, 0xFE, 0xFA, 0x00, 0x57, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, - 0x00, 0xE6, 0x00, 0xFD, 0xFF, 0x18, 0xFC, 0x38, 0xFD, 0xA9, 0x0B, - 0xC0, 0x1D, 0x7C, 0x1F, 0xC6, 0x0E, 0x95, 0xFE, 0x9F, 0xFB, 0x7E, - 0xFF, 0xF8, 0x00, 0x35, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x38, 0x00, - 0xF9, 0x00, 0x6C, 0xFF, 0x92, 0xFB, 0xC9, 0xFE, 0x2F, 0x0F, 0xAD, - 0x1F, 0x7D, 0x1D, 0x42, 0x0B, 0x12, 0xFD, 0x2A, 0xFC, 0x0B, 0x00, - 0xE3, 0x00, 0x1A, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5B, 0x00, 0xF8, - 0x00, 0xC1, 0xFE, 0x47, 0xFB, 0xD4, 0x00, 0xBC, 0x12, 0xF3, 0x20, - 0xEF, 0x1A, 0xE9, 0x07, 0x08, 0xFC, 0xD9, 0xFC, 0x78, 0x00, 0xC2, - 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0x00, 0xDD, 0x00, - 0x04, 0xFE, 0x49, 0xFB, 0x52, 0x03, 0x2D, 0x16, 0x83, 0x21, 0xEF, - 0x17, 0xD5, 0x04, 0x6F, 0xFB, 0x9A, 0xFD, 0xC3, 0x00, 0x9A, 0x00, - 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xAD, 0x00, 0xA4, - 0x00, 0x40, 0xFD, 0xA8, 0xFB, 0x36, 0x06, 0x5E, 0x19, 0x58, 0x21, - 0x9C, 0x14, 0x1E, 0x02, 0x3D, 0xFB, 0x5D, 0xFE, 0xED, 0x00, 0x70, - 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, 0x00, 0xD2, 0x00, 0x4A, 0x00, - 0x85, 0xFC, 0x74, 0xFC, 0x6D, 0x09, 0x2D, 0x1C, 0x72, 0x20, 0x1A, - 0x11, 0xD4, 0xFF, 0x61, 0xFB, 0x13, 0xFF, 0xFC, 0x00, 0x4A, 0x00, - 0xF7, 0xFF, 0xFA, 0xFF, 0x25, 0x00, 0xEF, 0x00, 0xCE, 0xFF, 0xE4, - 0xFB, 0xB5, 0xFD, 0xDE, 0x0C, 0x7C, 0x1E, 0xDD, 0x1E, 0x8C, 0x0D, - 0x01, 0xFE, 0xCA, 0xFB, 0xB3, 0xFF, 0xF3, 0x00, 0x2B, 0x00, 0xFA, - 0xFF, 0xF8, 0xFF, 0x44, 0x00, 0xFB, 0x00, 0x34, 0xFF, 0x71, 0xFB, - 0x71, 0xFF, 0x6B, 0x10, 0x32, 0x20, 0xA9, 0x1C, 0x13, 0x0A, 0xA8, - 0xFC, 0x63, 0xFC, 0x35, 0x00, 0xD9, 0x00, 0x12, 0x00, 0xFD, 0xFF, - 0xF7, 0xFF, 0x69, 0x00, 0xF2, 0x00, 0x81, 0xFE, 0x3E, 0xFB, 0xA4, - 0x01, 0xF2, 0x13, 0x3A, 0x21, 0xF0, 0x19, 0xCF, 0x06, 0xC7, 0xFB, - 0x1B, 0xFD, 0x96, 0x00, 0xB4, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, - 0x00, 0xFB, 0xFF, 0x92, 0x00, 0xCD, 0x00, 0xC0, 0xFD, 0x5E, 0xFB, - 0x47, 0x04, 0x51, 0x17, 0x8A, 0x21, 0xD0, 0x16, 0xD9, 0x03, 0x53, - 0xFB, 0xDE, 0xFD, 0xD5, 0x00, 0x8B, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, - 0x04, 0x00, 0xBA, 0x00, 0x89, 0x00, 0xFD, 0xFC, 0xE2, 0xFB, 0x4B, - 0x07, 0x63, 0x1A, 0x1D, 0x21, 0x69, 0x13, 0x46, 0x01, 0x41, 0xFB, - 0x9E, 0xFE, 0xF5, 0x00, 0x63, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x16, - 0x00, 0xDD, 0x00, 0x23, 0x00, 0x49, 0xFC, 0xD5, 0xFC, 0x99, 0x0A, - 0x09, 0x1D, 0xF9, 0x1F, 0xDF, 0x0F, 0x24, 0xFF, 0x7F, 0xFB, 0x4D, - 0xFF, 0xFB, 0x00, 0x3F, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2F, 0x00, - 0xF5, 0x00, 0x9C, 0xFF, 0xB6, 0xFB, 0x41, 0xFE, 0x17, 0x0E, 0x26, - 0x1F, 0x2B, 0x1E, 0x54, 0x0C, 0x7C, 0xFD, 0xFA, 0xFB, 0xE3, 0xFF, - 0xEB, 0x00, 0x21, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x50, 0x00, 0xFB, - 0x00, 0xF8, 0xFE, 0x57, 0xFB, 0x26, 0x00, 0xA6, 0x11, 0xA1, 0x20, - 0xC6, 0x1B, 0xEA, 0x08, 0x4D, 0xFC, 0xA0, 0xFC, 0x5A, 0x00, 0xCD, - 0x00, 0x0C, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0xE9, 0x00, - 0x3F, 0xFE, 0x3F, 0xFB, 0x82, 0x02, 0x23, 0x15, 0x6B, 0x21, 0xE5, - 0x18, 0xBE, 0x05, 0x93, 0xFB, 0x5E, 0xFD, 0xAF, 0x00, 0xA6, 0x00, - 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xA0, 0x00, 0xB9, - 0x00, 0x7C, 0xFD, 0x80, 0xFB, 0x48, 0x05, 0x6B, 0x18, 0x79, 0x21, - 0xA9, 0x15, 0xE9, 0x02, 0x43, 0xFB, 0x21, 0xFE, 0xE3, 0x00, 0x7D, - 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x09, 0x00, 0xC7, 0x00, 0x69, 0x00, - 0xBC, 0xFC, 0x29, 0xFC, 0x69, 0x08, 0x5C, 0x1B, 0xCC, 0x20, 0x32, - 0x12, 0x7C, 0x00, 0x4E, 0xFB, 0xDD, 0xFE, 0xFA, 0x00, 0x56, 0x00, - 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, 0x00, 0xE7, 0x00, 0xF8, 0xFF, 0x12, - 0xFC, 0x45, 0xFD, 0xCB, 0x0B, 0xD6, 0x1D, 0x6C, 0x1F, 0xA3, 0x0E, - 0x84, 0xFE, 0xA4, 0xFB, 0x84, 0xFF, 0xF7, 0x00, 0x34, 0x00, 0xF9, - 0xFF, 0xF8, 0xFF, 0x3A, 0x00, 0xFA, 0x00, 0x66, 0xFF, 0x8E, 0xFB, - 0xDB, 0xFE, 0x53, 0x0F, 0xBD, 0x1F, 0x66, 0x1D, 0x21, 0x0B, 0x05, - 0xFD, 0x30, 0xFC, 0x10, 0x00, 0xE2, 0x00, 0x19, 0x00, 0xFC, 0xFF, - 0xF7, 0xFF, 0x5D, 0x00, 0xF8, 0x00, 0xBA, 0xFE, 0x46, 0xFB, 0xEA, - 0x00, 0xDF, 0x12, 0xFC, 0x20, 0xD3, 0x1A, 0xC9, 0x07, 0x00, 0xFC, - 0xE0, 0xFC, 0x7B, 0x00, 0xC0, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, - 0xFF, 0x85, 0x00, 0xDC, 0x00, 0xFC, 0xFD, 0x4A, 0xFB, 0x6C, 0x03, - 0x4E, 0x16, 0x85, 0x21, 0xCF, 0x17, 0xB8, 0x04, 0x6C, 0xFB, 0xA2, - 0xFD, 0xC5, 0x00, 0x98, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, - 0x01, 0x00, 0xAE, 0x00, 0xA1, 0x00, 0x38, 0xFD, 0xAE, 0xFB, 0x54, - 0x06, 0x7C, 0x19, 0x53, 0x21, 0x7B, 0x14, 0x05, 0x02, 0x3D, 0xFB, - 0x64, 0xFE, 0xEE, 0x00, 0x6F, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, - 0x00, 0xD4, 0x00, 0x46, 0x00, 0x7E, 0xFC, 0x7E, 0xFC, 0x8E, 0x09, - 0x46, 0x1C, 0x66, 0x20, 0xF7, 0x10, 0xC0, 0xFF, 0x64, 0xFB, 0x1A, - 0xFF, 0xFC, 0x00, 0x49, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x26, 0x00, - 0xF0, 0x00, 0xC9, 0xFF, 0xDF, 0xFB, 0xC4, 0xFD, 0x01, 0x0D, 0x90, - 0x1E, 0xCA, 0x1E, 0x69, 0x0D, 0xF1, 0xFD, 0xCF, 0xFB, 0xB8, 0xFF, - 0xF2, 0x00, 0x29, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x45, 0x00, 0xFC, - 0x00, 0x2D, 0xFF, 0x6D, 0xFB, 0x84, 0xFF, 0x8E, 0x10, 0x3F, 0x20, - 0x91, 0x1C, 0xF2, 0x09, 0x9D, 0xFC, 0x6A, 0xFC, 0x39, 0x00, 0xD7, - 0x00, 0x12, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6A, 0x00, 0xF1, 0x00, - 0x7A, 0xFE, 0x3D, 0xFB, 0xBC, 0x01, 0x14, 0x14, 0x41, 0x21, 0xD4, - 0x19, 0xB0, 0x06, 0xC0, 0xFB, 0x22, 0xFD, 0x99, 0x00, 0xB3, 0x00, - 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x93, 0x00, 0xCB, - 0x00, 0xB8, 0xFD, 0x61, 0xFB, 0x63, 0x04, 0x71, 0x17, 0x89, 0x21, - 0xB0, 0x16, 0xBD, 0x03, 0x51, 0xFB, 0xE6, 0xFD, 0xD7, 0x00, 0x8A, - 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0xBC, 0x00, 0x86, 0x00, - 0xF6, 0xFC, 0xE9, 0xFB, 0x6A, 0x07, 0x80, 0x1A, 0x15, 0x21, 0x47, - 0x13, 0x2F, 0x01, 0x42, 0xFB, 0xA5, 0xFE, 0xF6, 0x00, 0x61, 0x00, - 0xF7, 0xFF, 0xFC, 0xFF, 0x16, 0x00, 0xDF, 0x00, 0x1E, 0x00, 0x43, - 0xFC, 0xE1, 0xFC, 0xBB, 0x0A, 0x21, 0x1D, 0xEA, 0x1F, 0xBC, 0x0F, - 0x12, 0xFF, 0x82, 0xFB, 0x54, 0xFF, 0xFA, 0x00, 0x3D, 0x00, 0xF8, - 0xFF, 0xF9, 0xFF, 0x30, 0x00, 0xF6, 0x00, 0x96, 0xFF, 0xB1, 0xFB, - 0x51, 0xFE, 0x3A, 0x0E, 0x38, 0x1F, 0x16, 0x1E, 0x32, 0x0C, 0x6E, - 0xFD, 0x00, 0xFC, 0xE8, 0xFF, 0xEA, 0x00, 0x20, 0x00, 0xFB, 0xFF, - 0xF7, 0xFF, 0x51, 0x00, 0xFB, 0x00, 0xF1, 0xFE, 0x54, 0xFB, 0x3B, - 0x00, 0xC9, 0x11, 0xAD, 0x20, 0xAC, 0x1B, 0xCA, 0x08, 0x44, 0xFC, - 0xA7, 0xFC, 0x5E, 0x00, 0xCC, 0x00, 0x0B, 0x00, 0xFE, 0xFF, 0xF8, - 0xFF, 0x78, 0x00, 0xE7, 0x00, 0x38, 0xFE, 0x40, 0xFB, 0x9B, 0x02, - 0x45, 0x15, 0x6F, 0x21, 0xC7, 0x18, 0xA1, 0x05, 0x8E, 0xFB, 0x65, - 0xFD, 0xB2, 0x00, 0xA5, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xFE, 0xFF, 0xA2, 0x00, 0xB7, 0x00, 0x74, 0xFD, 0x84, 0xFB, 0x66, - 0x05, 0x8A, 0x18, 0x76, 0x21, 0x87, 0x15, 0xCF, 0x02, 0x41, 0xFB, - 0x29, 0xFE, 0xE5, 0x00, 0x7B, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0A, - 0x00, 0xC9, 0x00, 0x66, 0x00, 0xB5, 0xFC, 0x32, 0xFC, 0x89, 0x08, - 0x77, 0x1B, 0xC2, 0x20, 0x0F, 0x12, 0x66, 0x00, 0x50, 0xFB, 0xE4, - 0xFE, 0xFA, 0x00, 0x54, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1E, 0x00, - 0xE8, 0x00, 0xF3, 0xFF, 0x0C, 0xFC, 0x53, 0xFD, 0xED, 0x0B, 0xEB, - 0x1D, 0x5A, 0x1F, 0x80, 0x0E, 0x73, 0xFE, 0xA8, 0xFB, 0x8A, 0xFF, - 0xF7, 0x00, 0x32, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3B, 0x00, 0xFA, - 0x00, 0x60, 0xFF, 0x8A, 0xFB, 0xED, 0xFE, 0x76, 0x0F, 0xCC, 0x1F, - 0x4F, 0x1D, 0xFF, 0x0A, 0xF9, 0xFC, 0x36, 0xFC, 0x15, 0x00, 0xE1, - 0x00, 0x18, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5E, 0x00, 0xF7, 0x00, - 0xB3, 0xFE, 0x44, 0xFB, 0x01, 0x01, 0x02, 0x13, 0x04, 0x21, 0xB8, - 0x1A, 0xA9, 0x07, 0xF8, 0xFB, 0xE7, 0xFC, 0x7F, 0x00, 0xBF, 0x00, - 0x06, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x86, 0x00, 0xDA, 0x00, 0xF5, - 0xFD, 0x4C, 0xFB, 0x87, 0x03, 0x6E, 0x16, 0x86, 0x21, 0xB0, 0x17, - 0x9C, 0x04, 0x68, 0xFB, 0xA9, 0xFD, 0xC7, 0x00, 0x96, 0x00, 0xFB, - 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xB0, 0x00, 0x9F, 0x00, - 0x31, 0xFD, 0xB4, 0xFB, 0x73, 0x06, 0x99, 0x19, 0x4D, 0x21, 0x59, - 0x14, 0xED, 0x01, 0x3D, 0xFB, 0x6B, 0xFE, 0xEF, 0x00, 0x6D, 0x00, - 0xF7, 0xFF, 0xFD, 0xFF, 0x10, 0x00, 0xD5, 0x00, 0x42, 0x00, 0x77, - 0xFC, 0x88, 0xFC, 0xAF, 0x09, 0x5F, 0x1C, 0x59, 0x20, 0xD4, 0x10, - 0xAC, 0xFF, 0x67, 0xFB, 0x20, 0xFF, 0xFC, 0x00, 0x48, 0x00, 0xF7, - 0xFF, 0xFA, 0xFF, 0x27, 0x00, 0xF0, 0x00, 0xC3, 0xFF, 0xD9, 0xFB, - 0xD3, 0xFD, 0x24, 0x0D, 0xA3, 0x1E, 0xB7, 0x1E, 0x46, 0x0D, 0xE2, - 0xFD, 0xD4, 0xFB, 0xBE, 0xFF, 0xF1, 0x00, 0x28, 0x00, 0xFA, 0xFF, - 0xF7, 0xFF, 0x46, 0x00, 0xFC, 0x00, 0x27, 0xFF, 0x6A, 0xFB, 0x98, - 0xFF, 0xB1, 0x10, 0x4C, 0x20, 0x78, 0x1C, 0xD1, 0x09, 0x93, 0xFC, - 0x71, 0xFC, 0x3D, 0x00, 0xD6, 0x00, 0x11, 0x00, 0xFD, 0xFF, 0xF7, - 0xFF, 0x6C, 0x00, 0xF0, 0x00, 0x72, 0xFE, 0x3D, 0xFB, 0xD4, 0x01, - 0x36, 0x14, 0x47, 0x21, 0xB6, 0x19, 0x91, 0x06, 0xBA, 0xFB, 0x29, - 0xFD, 0x9C, 0x00, 0xB1, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, - 0xFB, 0xFF, 0x95, 0x00, 0xC9, 0x00, 0xB1, 0xFD, 0x65, 0xFB, 0x80, - 0x04, 0x90, 0x17, 0x88, 0x21, 0x8F, 0x16, 0xA2, 0x03, 0x4E, 0xFB, - 0xED, 0xFD, 0xD9, 0x00, 0x88, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x05, - 0x00, 0xBD, 0x00, 0x82, 0x00, 0xEF, 0xFC, 0xF0, 0xFB, 0x8A, 0x07, - 0x9C, 0x1A, 0x0D, 0x21, 0x24, 0x13, 0x18, 0x01, 0x43, 0xFB, 0xAC, - 0xFE, 0xF7, 0x00, 0x60, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x17, 0x00, - 0xE0, 0x00, 0x1A, 0x00, 0x3D, 0xFC, 0xED, 0xFC, 0xDD, 0x0A, 0x38, - 0x1D, 0xDB, 0x1F, 0x99, 0x0F, 0xFF, 0xFE, 0x86, 0xFB, 0x5A, 0xFF, - 0xFA, 0x00, 0x3C, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x31, 0x00, 0xF6, - 0x00, 0x90, 0xFF, 0xAD, 0xFB, 0x62, 0xFE, 0x5D, 0x0E, 0x49, 0x1F, - 0x01, 0x1E, 0x10, 0x0C, 0x60, 0xFD, 0x06, 0xFC, 0xEE, 0xFF, 0xE9, - 0x00, 0x1F, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x53, 0x00, 0xFB, 0x00, - 0xEB, 0xFE, 0x52, 0xFB, 0x51, 0x00, 0xEC, 0x11, 0xB7, 0x20, 0x91, - 0x1B, 0xA9, 0x08, 0x3B, 0xFC, 0xAE, 0xFC, 0x62, 0x00, 0xCA, 0x00, - 0x0B, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7A, 0x00, 0xE6, 0x00, 0x30, - 0xFE, 0x40, 0xFB, 0xB5, 0x02, 0x66, 0x15, 0x73, 0x21, 0xA9, 0x18, - 0x83, 0x05, 0x89, 0xFB, 0x6D, 0xFD, 0xB4, 0x00, 0xA3, 0x00, 0xFE, - 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xA3, 0x00, 0xB4, 0x00, - 0x6D, 0xFD, 0x89, 0xFB, 0x83, 0x05, 0xA9, 0x18, 0x73, 0x21, 0x66, - 0x15, 0xB5, 0x02, 0x40, 0xFB, 0x30, 0xFE, 0xE6, 0x00, 0x7A, 0x00, - 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, 0xCA, 0x00, 0x62, 0x00, 0xAE, - 0xFC, 0x3B, 0xFC, 0xA9, 0x08, 0x91, 0x1B, 0xB7, 0x20, 0xEC, 0x11, - 0x51, 0x00, 0x52, 0xFB, 0xEB, 0xFE, 0xFB, 0x00, 0x53, 0x00, 0xF7, - 0xFF, 0xFB, 0xFF, 0x1F, 0x00, 0xE9, 0x00, 0xEE, 0xFF, 0x06, 0xFC, - 0x60, 0xFD, 0x10, 0x0C, 0x01, 0x1E, 0x49, 0x1F, 0x5D, 0x0E, 0x62, - 0xFE, 0xAD, 0xFB, 0x90, 0xFF, 0xF6, 0x00, 0x31, 0x00, 0xF9, 0xFF, - 0xF8, 0xFF, 0x3C, 0x00, 0xFA, 0x00, 0x5A, 0xFF, 0x86, 0xFB, 0xFF, - 0xFE, 0x99, 0x0F, 0xDB, 0x1F, 0x38, 0x1D, 0xDD, 0x0A, 0xED, 0xFC, - 0x3D, 0xFC, 0x1A, 0x00, 0xE0, 0x00, 0x17, 0x00, 0xFC, 0xFF, 0xF7, - 0xFF, 0x60, 0x00, 0xF7, 0x00, 0xAC, 0xFE, 0x43, 0xFB, 0x18, 0x01, - 0x24, 0x13, 0x0D, 0x21, 0x9C, 0x1A, 0x8A, 0x07, 0xF0, 0xFB, 0xEF, - 0xFC, 0x82, 0x00, 0xBD, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, - 0x88, 0x00, 0xD9, 0x00, 0xED, 0xFD, 0x4E, 0xFB, 0xA2, 0x03, 0x8F, - 0x16, 0x88, 0x21, 0x90, 0x17, 0x80, 0x04, 0x65, 0xFB, 0xB1, 0xFD, - 0xC9, 0x00, 0x95, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, - 0x00, 0xB1, 0x00, 0x9C, 0x00, 0x29, 0xFD, 0xBA, 0xFB, 0x91, 0x06, - 0xB6, 0x19, 0x47, 0x21, 0x36, 0x14, 0xD4, 0x01, 0x3D, 0xFB, 0x72, - 0xFE, 0xF0, 0x00, 0x6C, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x11, 0x00, - 0xD6, 0x00, 0x3D, 0x00, 0x71, 0xFC, 0x93, 0xFC, 0xD1, 0x09, 0x78, - 0x1C, 0x4C, 0x20, 0xB1, 0x10, 0x98, 0xFF, 0x6A, 0xFB, 0x27, 0xFF, - 0xFC, 0x00, 0x46, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x28, 0x00, 0xF1, - 0x00, 0xBE, 0xFF, 0xD4, 0xFB, 0xE2, 0xFD, 0x46, 0x0D, 0xB7, 0x1E, - 0xA3, 0x1E, 0x24, 0x0D, 0xD3, 0xFD, 0xD9, 0xFB, 0xC3, 0xFF, 0xF0, - 0x00, 0x27, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x48, 0x00, 0xFC, 0x00, - 0x20, 0xFF, 0x67, 0xFB, 0xAC, 0xFF, 0xD4, 0x10, 0x59, 0x20, 0x5F, - 0x1C, 0xAF, 0x09, 0x88, 0xFC, 0x77, 0xFC, 0x42, 0x00, 0xD5, 0x00, - 0x10, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6D, 0x00, 0xEF, 0x00, 0x6B, - 0xFE, 0x3D, 0xFB, 0xED, 0x01, 0x59, 0x14, 0x4D, 0x21, 0x99, 0x19, - 0x73, 0x06, 0xB4, 0xFB, 0x31, 0xFD, 0x9F, 0x00, 0xB0, 0x00, 0x01, - 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x96, 0x00, 0xC7, 0x00, - 0xA9, 0xFD, 0x68, 0xFB, 0x9C, 0x04, 0xB0, 0x17, 0x86, 0x21, 0x6E, - 0x16, 0x87, 0x03, 0x4C, 0xFB, 0xF5, 0xFD, 0xDA, 0x00, 0x86, 0x00, - 0xF9, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0xBF, 0x00, 0x7F, 0x00, 0xE7, - 0xFC, 0xF8, 0xFB, 0xA9, 0x07, 0xB8, 0x1A, 0x04, 0x21, 0x02, 0x13, - 0x01, 0x01, 0x44, 0xFB, 0xB3, 0xFE, 0xF7, 0x00, 0x5E, 0x00, 0xF7, - 0xFF, 0xFC, 0xFF, 0x18, 0x00, 0xE1, 0x00, 0x15, 0x00, 0x36, 0xFC, - 0xF9, 0xFC, 0xFF, 0x0A, 0x4F, 0x1D, 0xCC, 0x1F, 0x76, 0x0F, 0xED, - 0xFE, 0x8A, 0xFB, 0x60, 0xFF, 0xFA, 0x00, 0x3B, 0x00, 0xF8, 0xFF, - 0xF9, 0xFF, 0x32, 0x00, 0xF7, 0x00, 0x8A, 0xFF, 0xA8, 0xFB, 0x73, - 0xFE, 0x80, 0x0E, 0x5A, 0x1F, 0xEB, 0x1D, 0xED, 0x0B, 0x53, 0xFD, - 0x0C, 0xFC, 0xF3, 0xFF, 0xE8, 0x00, 0x1E, 0x00, 0xFB, 0xFF, 0xF7, - 0xFF, 0x54, 0x00, 0xFA, 0x00, 0xE4, 0xFE, 0x50, 0xFB, 0x66, 0x00, - 0x0F, 0x12, 0xC2, 0x20, 0x77, 0x1B, 0x89, 0x08, 0x32, 0xFC, 0xB5, - 0xFC, 0x66, 0x00, 0xC9, 0x00, 0x0A, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, - 0x7B, 0x00, 0xE5, 0x00, 0x29, 0xFE, 0x41, 0xFB, 0xCF, 0x02, 0x87, - 0x15, 0x76, 0x21, 0x8A, 0x18, 0x66, 0x05, 0x84, 0xFB, 0x74, 0xFD, - 0xB7, 0x00, 0xA2, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, - 0xFF, 0xA5, 0x00, 0xB2, 0x00, 0x65, 0xFD, 0x8E, 0xFB, 0xA1, 0x05, - 0xC7, 0x18, 0x6F, 0x21, 0x45, 0x15, 0x9B, 0x02, 0x40, 0xFB, 0x38, - 0xFE, 0xE7, 0x00, 0x78, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, - 0xCC, 0x00, 0x5E, 0x00, 0xA7, 0xFC, 0x44, 0xFC, 0xCA, 0x08, 0xAC, - 0x1B, 0xAD, 0x20, 0xC9, 0x11, 0x3B, 0x00, 0x54, 0xFB, 0xF1, 0xFE, - 0xFB, 0x00, 0x51, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x20, 0x00, 0xEA, - 0x00, 0xE8, 0xFF, 0x00, 0xFC, 0x6E, 0xFD, 0x32, 0x0C, 0x16, 0x1E, - 0x38, 0x1F, 0x3A, 0x0E, 0x51, 0xFE, 0xB1, 0xFB, 0x96, 0xFF, 0xF6, - 0x00, 0x30, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3D, 0x00, 0xFA, 0x00, - 0x54, 0xFF, 0x82, 0xFB, 0x12, 0xFF, 0xBC, 0x0F, 0xEA, 0x1F, 0x21, - 0x1D, 0xBB, 0x0A, 0xE1, 0xFC, 0x43, 0xFC, 0x1E, 0x00, 0xDF, 0x00, - 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x61, 0x00, 0xF6, 0x00, 0xA5, - 0xFE, 0x42, 0xFB, 0x2F, 0x01, 0x47, 0x13, 0x15, 0x21, 0x80, 0x1A, - 0x6A, 0x07, 0xE9, 0xFB, 0xF6, 0xFC, 0x86, 0x00, 0xBC, 0x00, 0x05, - 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8A, 0x00, 0xD7, 0x00, 0xE6, 0xFD, - 0x51, 0xFB, 0xBD, 0x03, 0xB0, 0x16, 0x89, 0x21, 0x71, 0x17, 0x63, - 0x04, 0x61, 0xFB, 0xB8, 0xFD, 0xCB, 0x00, 0x93, 0x00, 0xFB, 0xFF, - 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xB3, 0x00, 0x99, 0x00, 0x22, - 0xFD, 0xC0, 0xFB, 0xB0, 0x06, 0xD4, 0x19, 0x41, 0x21, 0x14, 0x14, - 0xBC, 0x01, 0x3D, 0xFB, 0x7A, 0xFE, 0xF1, 0x00, 0x6A, 0x00, 0xF7, - 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD7, 0x00, 0x39, 0x00, 0x6A, 0xFC, - 0x9D, 0xFC, 0xF2, 0x09, 0x91, 0x1C, 0x3F, 0x20, 0x8E, 0x10, 0x84, - 0xFF, 0x6D, 0xFB, 0x2D, 0xFF, 0xFC, 0x00, 0x45, 0x00, 0xF7, 0xFF, - 0xFA, 0xFF, 0x29, 0x00, 0xF2, 0x00, 0xB8, 0xFF, 0xCF, 0xFB, 0xF1, - 0xFD, 0x69, 0x0D, 0xCA, 0x1E, 0x90, 0x1E, 0x01, 0x0D, 0xC4, 0xFD, - 0xDF, 0xFB, 0xC9, 0xFF, 0xF0, 0x00, 0x26, 0x00, 0xFA, 0xFF, 0xF7, - 0xFF, 0x49, 0x00, 0xFC, 0x00, 0x1A, 0xFF, 0x64, 0xFB, 0xC0, 0xFF, - 0xF7, 0x10, 0x66, 0x20, 0x46, 0x1C, 0x8E, 0x09, 0x7E, 0xFC, 0x7E, - 0xFC, 0x46, 0x00, 0xD4, 0x00, 0x0F, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, - 0x6F, 0x00, 0xEE, 0x00, 0x64, 0xFE, 0x3D, 0xFB, 0x05, 0x02, 0x7B, - 0x14, 0x53, 0x21, 0x7C, 0x19, 0x54, 0x06, 0xAE, 0xFB, 0x38, 0xFD, - 0xA1, 0x00, 0xAE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC, - 0xFF, 0x98, 0x00, 0xC5, 0x00, 0xA2, 0xFD, 0x6C, 0xFB, 0xB8, 0x04, - 0xCF, 0x17, 0x85, 0x21, 0x4E, 0x16, 0x6C, 0x03, 0x4A, 0xFB, 0xFC, - 0xFD, 0xDC, 0x00, 0x85, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x07, 0x00, - 0xC0, 0x00, 0x7B, 0x00, 0xE0, 0xFC, 0x00, 0xFC, 0xC9, 0x07, 0xD3, - 0x1A, 0xFC, 0x20, 0xDF, 0x12, 0xEA, 0x00, 0x46, 0xFB, 0xBA, 0xFE, - 0xF8, 0x00, 0x5D, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x19, 0x00, 0xE2, - 0x00, 0x10, 0x00, 0x30, 0xFC, 0x05, 0xFD, 0x21, 0x0B, 0x66, 0x1D, - 0xBD, 0x1F, 0x53, 0x0F, 0xDB, 0xFE, 0x8E, 0xFB, 0x66, 0xFF, 0xFA, - 0x00, 0x3A, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x34, 0x00, 0xF7, 0x00, - 0x84, 0xFF, 0xA4, 0xFB, 0x84, 0xFE, 0xA3, 0x0E, 0x6C, 0x1F, 0xD6, - 0x1D, 0xCB, 0x0B, 0x45, 0xFD, 0x12, 0xFC, 0xF8, 0xFF, 0xE7, 0x00, - 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x56, 0x00, 0xFA, 0x00, 0xDD, - 0xFE, 0x4E, 0xFB, 0x7C, 0x00, 0x32, 0x12, 0xCC, 0x20, 0x5C, 0x1B, - 0x69, 0x08, 0x29, 0xFC, 0xBC, 0xFC, 0x69, 0x00, 0xC7, 0x00, 0x09, - 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7D, 0x00, 0xE3, 0x00, 0x21, 0xFE, - 0x43, 0xFB, 0xE9, 0x02, 0xA9, 0x15, 0x79, 0x21, 0x6B, 0x18, 0x48, - 0x05, 0x80, 0xFB, 0x7C, 0xFD, 0xB9, 0x00, 0xA0, 0x00, 0xFD, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA6, 0x00, 0xAF, 0x00, 0x5E, - 0xFD, 0x93, 0xFB, 0xBE, 0x05, 0xE5, 0x18, 0x6B, 0x21, 0x23, 0x15, - 0x82, 0x02, 0x3F, 0xFB, 0x3F, 0xFE, 0xE9, 0x00, 0x77, 0x00, 0xF8, - 0xFF, 0xFE, 0xFF, 0x0C, 0x00, 0xCD, 0x00, 0x5A, 0x00, 0xA0, 0xFC, - 0x4D, 0xFC, 0xEA, 0x08, 0xC6, 0x1B, 0xA1, 0x20, 0xA6, 0x11, 0x26, - 0x00, 0x57, 0xFB, 0xF8, 0xFE, 0xFB, 0x00, 0x50, 0x00, 0xF7, 0xFF, - 0xFB, 0xFF, 0x21, 0x00, 0xEB, 0x00, 0xE3, 0xFF, 0xFA, 0xFB, 0x7C, - 0xFD, 0x54, 0x0C, 0x2B, 0x1E, 0x26, 0x1F, 0x17, 0x0E, 0x41, 0xFE, - 0xB6, 0xFB, 0x9C, 0xFF, 0xF5, 0x00, 0x2F, 0x00, 0xF9, 0xFF, 0xF8, - 0xFF, 0x3F, 0x00, 0xFB, 0x00, 0x4D, 0xFF, 0x7F, 0xFB, 0x24, 0xFF, - 0xDF, 0x0F, 0xF9, 0x1F, 0x09, 0x1D, 0x99, 0x0A, 0xD5, 0xFC, 0x49, - 0xFC, 0x23, 0x00, 0xDD, 0x00, 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, - 0x63, 0x00, 0xF5, 0x00, 0x9E, 0xFE, 0x41, 0xFB, 0x46, 0x01, 0x69, - 0x13, 0x1D, 0x21, 0x63, 0x1A, 0x4B, 0x07, 0xE2, 0xFB, 0xFD, 0xFC, - 0x89, 0x00, 0xBA, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8B, - 0x00, 0xD5, 0x00, 0xDE, 0xFD, 0x53, 0xFB, 0xD9, 0x03, 0xD0, 0x16, - 0x8A, 0x21, 0x51, 0x17, 0x47, 0x04, 0x5E, 0xFB, 0xC0, 0xFD, 0xCD, - 0x00, 0x92, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, - 0xB4, 0x00, 0x96, 0x00, 0x1B, 0xFD, 0xC7, 0xFB, 0xCF, 0x06, 0xF0, - 0x19, 0x3A, 0x21, 0xF2, 0x13, 0xA4, 0x01, 0x3E, 0xFB, 0x81, 0xFE, - 0xF2, 0x00, 0x69, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD9, - 0x00, 0x35, 0x00, 0x63, 0xFC, 0xA8, 0xFC, 0x13, 0x0A, 0xA9, 0x1C, - 0x32, 0x20, 0x6B, 0x10, 0x71, 0xFF, 0x71, 0xFB, 0x34, 0xFF, 0xFB, - 0x00, 0x44, 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2B, 0x00, 0xF3, 0x00, - 0xB3, 0xFF, 0xCA, 0xFB, 0x01, 0xFE, 0x8C, 0x0D, 0xDD, 0x1E, 0x7C, - 0x1E, 0xDE, 0x0C, 0xB5, 0xFD, 0xE4, 0xFB, 0xCE, 0xFF, 0xEF, 0x00, - 0x25, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4A, 0x00, 0xFC, 0x00, 0x13, - 0xFF, 0x61, 0xFB, 0xD4, 0xFF, 0x1A, 0x11, 0x72, 0x20, 0x2D, 0x1C, - 0x6D, 0x09, 0x74, 0xFC, 0x85, 0xFC, 0x4A, 0x00, 0xD2, 0x00, 0x0F, - 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x70, 0x00, 0xED, 0x00, 0x5D, 0xFE, - 0x3D, 0xFB, 0x1E, 0x02, 0x9C, 0x14, 0x58, 0x21, 0x5E, 0x19, 0x36, - 0x06, 0xA8, 0xFB, 0x40, 0xFD, 0xA4, 0x00, 0xAD, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x9A, 0x00, 0xC3, 0x00, 0x9A, - 0xFD, 0x6F, 0xFB, 0xD5, 0x04, 0xEF, 0x17, 0x83, 0x21, 0x2D, 0x16, - 0x52, 0x03, 0x49, 0xFB, 0x04, 0xFE, 0xDD, 0x00, 0x83, 0x00, 0xF9, - 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xC2, 0x00, 0x78, 0x00, 0xD9, 0xFC, - 0x08, 0xFC, 0xE9, 0x07, 0xEF, 0x1A, 0xF3, 0x20, 0xBC, 0x12, 0xD4, - 0x00, 0x47, 0xFB, 0xC1, 0xFE, 0xF8, 0x00, 0x5B, 0x00, 0xF7, 0xFF, - 0xFC, 0xFF, 0x1A, 0x00, 0xE3, 0x00, 0x0B, 0x00, 0x2A, 0xFC, 0x12, - 0xFD, 0x42, 0x0B, 0x7D, 0x1D, 0xAD, 0x1F, 0x2F, 0x0F, 0xC9, 0xFE, - 0x92, 0xFB, 0x6C, 0xFF, 0xF9, 0x00, 0x38, 0x00, 0xF8, 0xFF, 0xF9, - 0xFF, 0x35, 0x00, 0xF8, 0x00, 0x7E, 0xFF, 0x9F, 0xFB, 0x95, 0xFE, - 0xC6, 0x0E, 0x7C, 0x1F, 0xC0, 0x1D, 0xA9, 0x0B, 0x38, 0xFD, 0x18, - 0xFC, 0xFD, 0xFF, 0xE6, 0x00, 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, - 0x57, 0x00, 0xFA, 0x00, 0xD6, 0xFE, 0x4C, 0xFB, 0x92, 0x00, 0x54, - 0x12, 0xD6, 0x20, 0x41, 0x1B, 0x49, 0x08, 0x20, 0xFC, 0xC3, 0xFC, - 0x6D, 0x00, 0xC6, 0x00, 0x09, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7E, - 0x00, 0xE2, 0x00, 0x1A, 0xFE, 0x44, 0xFB, 0x03, 0x03, 0xCA, 0x15, - 0x7C, 0x21, 0x4C, 0x18, 0x2B, 0x05, 0x7B, 0xFB, 0x83, 0xFD, 0xBC, - 0x00, 0x9E, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0xA8, 0x00, 0xAD, 0x00, 0x56, 0xFD, 0x98, 0xFB, 0xDC, 0x05, 0x04, - 0x19, 0x66, 0x21, 0x02, 0x15, 0x69, 0x02, 0x3E, 0xFB, 0x47, 0xFE, - 0xEA, 0x00, 0x75, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xCE, - 0x00, 0x56, 0x00, 0x99, 0xFC, 0x56, 0xFC, 0x0B, 0x09, 0xE0, 0x1B, - 0x96, 0x20, 0x83, 0x11, 0x11, 0x00, 0x59, 0xFB, 0xFF, 0xFE, 0xFB, - 0x00, 0x4E, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x22, 0x00, 0xEC, 0x00, - 0xDE, 0xFF, 0xF5, 0xFB, 0x8A, 0xFD, 0x77, 0x0C, 0x3F, 0x1E, 0x14, - 0x1F, 0xF5, 0x0D, 0x31, 0xFE, 0xBB, 0xFB, 0xA2, 0xFF, 0xF5, 0x00, - 0x2E, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x40, 0x00, 0xFB, 0x00, 0x47, - 0xFF, 0x7B, 0xFB, 0x37, 0xFF, 0x02, 0x10, 0x07, 0x20, 0xF2, 0x1C, - 0x78, 0x0A, 0xCA, 0xFC, 0x50, 0xFC, 0x27, 0x00, 0xDC, 0x00, 0x15, - 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x64, 0x00, 0xF5, 0x00, 0x97, 0xFE, - 0x40, 0xFB, 0x5D, 0x01, 0x8B, 0x13, 0x25, 0x21, 0x47, 0x1A, 0x2C, - 0x07, 0xDB, 0xFB, 0x05, 0xFD, 0x8C, 0x00, 0xB9, 0x00, 0x04, 0x00, - 0xFF, 0xFF, 0xFA, 0xFF, 0x8D, 0x00, 0xD3, 0x00, 0xD6, 0xFD, 0x56, - 0xFB, 0xF4, 0x03, 0xF0, 0x16, 0x8A, 0x21, 0x31, 0x17, 0x2B, 0x04, - 0x5B, 0xFB, 0xC7, 0xFD, 0xCF, 0x00, 0x90, 0x00, 0xFA, 0xFF, 0x00, - 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xB6, 0x00, 0x92, 0x00, 0x13, 0xFD, - 0xCD, 0xFB, 0xEE, 0x06, 0x0D, 0x1A, 0x33, 0x21, 0xD0, 0x13, 0x8C, - 0x01, 0x3E, 0xFB, 0x88, 0xFE, 0xF3, 0x00, 0x67, 0x00, 0xF7, 0xFF, - 0x06, 0x00, 0x1D, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0xA1, 0x02, 0xA6, - 0xF8, 0x56, 0x02, 0xA5, 0x28, 0xA5, 0x28, 0x56, 0x02, 0xA6, 0xF8, - 0xA1, 0x02, 0xFE, 0x00, 0x03, 0xFF, 0x1D, 0x00, 0x06, 0x00, 0x00, - 0x00, 0x21, 0x00, 0xA6, 0xFF, 0x3F, 0xFF, 0x0B, 0x03, 0x42, 0xFE, - 0x3E, 0xF8, 0x7F, 0x15, 0xAC, 0x30, 0x7F, 0x15, 0x3E, 0xF8, 0x42, - 0xFE, 0x0B, 0x03, 0x3F, 0xFF, 0xA6, 0xFF, 0x21, 0x00, 0x00, 0x00, - 0xFA, 0xFF, 0xCE, 0xFF, 0x14, 0x01, 0x00, 0xFD, 0x35, 0x06, 0xD5, - 0xF4, 0xDA, 0x15, 0x92, 0x40, 0xAE, 0xFE, 0xF3, 0xFC, 0x68, 0x03, - 0x86, 0xFD, 0x51, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEC, - 0xFF, 0xF9, 0xFF, 0xC6, 0x00, 0x55, 0xFD, 0x35, 0x06, 0x90, 0xF3, - 0xE5, 0x1C, 0x6B, 0x3D, 0x71, 0xFA, 0x34, 0xFF, 0x46, 0x02, 0xFF, - 0xFD, 0x2D, 0x01, 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDB, 0xFF, - 0x2D, 0x00, 0x60, 0x00, 0xE1, 0xFD, 0xCE, 0x05, 0xED, 0xF2, 0xF3, - 0x23, 0x20, 0x39, 0x22, 0xF7, 0x44, 0x01, 0x1F, 0x01, 0x89, 0xFE, - 0xFB, 0x00, 0x9C, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC9, 0xFF, 0x68, - 0x00, 0xE5, 0xFF, 0xA0, 0xFE, 0xFB, 0x04, 0x0C, 0xF3, 0xC5, 0x2A, - 0xD8, 0x33, 0xC9, 0xF4, 0x0B, 0x03, 0x05, 0x00, 0x1A, 0xFF, 0xC1, - 0x00, 0xAD, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB5, 0xFF, 0xA5, 0x00, - 0x5C, 0xFF, 0x8C, 0xFF, 0xBF, 0x03, 0x06, 0xF4, 0x22, 0x31, 0xC8, - 0x2D, 0x63, 0xF3, 0x76, 0x04, 0x08, 0xFF, 0xA7, 0xFF, 0x84, 0x00, - 0xC0, 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA4, 0xFF, 0xE1, 0x00, 0xCB, - 0xFE, 0x9B, 0x00, 0x21, 0x02, 0xEE, 0xF5, 0xCD, 0x36, 0x24, 0x27, - 0xE1, 0xF2, 0x7A, 0x05, 0x33, 0xFE, 0x2A, 0x00, 0x47, 0x00, 0xD3, - 0xFF, 0x04, 0x00, 0x0F, 0x00, 0x95, 0xFF, 0x17, 0x01, 0x3D, 0xFE, - 0xBD, 0x01, 0x30, 0x00, 0xCC, 0xF8, 0x92, 0x3B, 0x2A, 0x20, 0x2E, - 0xF3, 0x12, 0x06, 0x8F, 0xFD, 0x9A, 0x00, 0x10, 0x00, 0xE5, 0xFF, - 0x02, 0x00, 0x10, 0x00, 0x8C, 0xFF, 0x42, 0x01, 0xBB, 0xFD, 0xE4, - 0x02, 0x01, 0xFE, 0x9C, 0xFC, 0x45, 0x3F, 0x16, 0x19, 0x2D, 0xF4, - 0x41, 0x06, 0x21, 0xFD, 0xF3, 0x00, 0xE0, 0xFF, 0xF4, 0xFF, 0x01, - 0x00, 0x10, 0x00, 0x8B, 0xFF, 0x5D, 0x01, 0x4F, 0xFD, 0xFB, 0x03, - 0xB2, 0xFB, 0x53, 0x01, 0xC2, 0x41, 0x24, 0x12, 0xBA, 0xF5, 0x0F, - 0x06, 0xE9, 0xFC, 0x33, 0x01, 0xBB, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x93, 0xFF, 0x63, 0x01, 0x04, 0xFD, 0xEF, 0x04, 0x62, - 0xF9, 0xD7, 0x06, 0xF2, 0x42, 0x8D, 0x0B, 0xB0, 0xF7, 0x87, 0x05, - 0xE6, 0xFC, 0x58, 0x01, 0xA0, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x07, 0x00, 0xA5, 0xFF, 0x52, 0x01, 0xE2, 0xFC, 0xAD, 0x05, - 0x35, 0xF7, 0x08, 0x0D, 0xCB, 0x42, 0x81, 0x05, 0xE8, 0xF9, 0xBB, - 0x04, 0x12, 0xFD, 0x64, 0x01, 0x90, 0xFF, 0x0E, 0x00, 0x00, 0x00, - 0xFE, 0xFF, 0xC2, 0xFF, 0x27, 0x01, 0xF1, 0xFC, 0x22, 0x06, 0x54, - 0xF5, 0xB8, 0x13, 0x4A, 0x41, 0x29, 0x00, 0x3C, 0xFC, 0xBD, 0x03, - 0x66, 0xFD, 0x58, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF1, - 0xFF, 0xEB, 0xFF, 0xE1, 0x00, 0x35, 0xFD, 0x40, 0x06, 0xE4, 0xF3, - 0xB7, 0x1A, 0x85, 0x3E, 0xA6, 0xFB, 0x86, 0xFE, 0xA0, 0x02, 0xD7, - 0xFD, 0x39, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xE1, 0xFF, - 0x1C, 0x00, 0x82, 0x00, 0xB0, 0xFD, 0xF9, 0x05, 0x0C, 0xF3, 0xCB, - 0x21, 0x8F, 0x3A, 0x0D, 0xF8, 0xA9, 0x00, 0x79, 0x01, 0x5D, 0xFE, - 0x0B, 0x01, 0x98, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCE, 0xFF, 0x55, - 0x00, 0x0D, 0x00, 0x60, 0xFE, 0x48, 0x05, 0xEC, 0xF2, 0xB6, 0x28, - 0x91, 0x35, 0x68, 0xF5, 0x88, 0x02, 0x5A, 0x00, 0xED, 0xFE, 0xD4, - 0x00, 0xA8, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0x92, 0x00, - 0x87, 0xFF, 0x3F, 0xFF, 0x2B, 0x04, 0xA1, 0xF3, 0x3D, 0x2F, 0xB8, - 0x2F, 0xB8, 0xF3, 0x11, 0x04, 0x52, 0xFF, 0x7C, 0xFF, 0x97, 0x00, - 0xBA, 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA9, 0xFF, 0xCF, 0x00, 0xF8, - 0xFE, 0x44, 0x00, 0xAA, 0x02, 0x3E, 0xF5, 0x24, 0x35, 0x3B, 0x29, - 0xF2, 0xF2, 0x35, 0x05, 0x70, 0xFE, 0x03, 0x00, 0x5A, 0x00, 0xCD, - 0xFF, 0x05, 0x00, 0x0E, 0x00, 0x99, 0xFF, 0x07, 0x01, 0x68, 0xFE, - 0x63, 0x01, 0xD0, 0x00, 0xD0, 0xF7, 0x35, 0x3A, 0x55, 0x22, 0x02, - 0xF3, 0xEF, 0x05, 0xBC, 0xFD, 0x7A, 0x00, 0x20, 0x00, 0xDF, 0xFF, - 0x03, 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x36, 0x01, 0xE1, 0xFD, 0x8A, - 0x02, 0xB2, 0xFE, 0x56, 0xFB, 0x40, 0x3E, 0x42, 0x1B, 0xCE, 0xF3, - 0x3E, 0x06, 0x3D, 0xFD, 0xDB, 0x00, 0xEE, 0xFF, 0xF0, 0xFF, 0x01, - 0x00, 0x11, 0x00, 0x8A, 0xFF, 0x57, 0x01, 0x6D, 0xFD, 0xA8, 0x03, - 0x69, 0xFC, 0xC8, 0xFF, 0x20, 0x41, 0x40, 0x14, 0x33, 0xF5, 0x28, - 0x06, 0xF5, 0xFC, 0x22, 0x01, 0xC5, 0xFF, 0xFD, 0xFF, 0x00, 0x00, - 0x0F, 0x00, 0x8F, 0xFF, 0x64, 0x01, 0x17, 0xFD, 0xA9, 0x04, 0x16, - 0xFA, 0x10, 0x05, 0xB8, 0x42, 0x87, 0x0D, 0x0D, 0xF7, 0xB9, 0x05, - 0xE2, 0xFC, 0x50, 0x01, 0xA7, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0A, 0x00, 0x9E, 0xFF, 0x5A, 0x01, 0xE8, 0xFC, 0x7A, 0x05, - 0xDA, 0xF7, 0x10, 0x0B, 0xFB, 0x42, 0x4B, 0x07, 0x35, 0xF9, 0x00, - 0x05, 0x00, 0xFD, 0x63, 0x01, 0x94, 0xFF, 0x0D, 0x00, 0x00, 0x00, - 0x01, 0x00, 0xB8, 0xFF, 0x37, 0x01, 0xE7, 0xFC, 0x07, 0x06, 0xDE, - 0xF5, 0x9F, 0x11, 0xE4, 0x41, 0xB8, 0x01, 0x84, 0xFB, 0x0F, 0x04, - 0x48, 0xFD, 0x5E, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF5, - 0xFF, 0xDD, 0xFF, 0xF9, 0x00, 0x1B, 0xFD, 0x41, 0x06, 0x47, 0xF4, - 0x8B, 0x18, 0x81, 0x3F, 0xF1, 0xFC, 0xD5, 0xFD, 0xFA, 0x02, 0xB2, - 0xFD, 0x45, 0x01, 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE6, 0xFF, - 0x0C, 0x00, 0xA2, 0x00, 0x85, 0xFD, 0x1A, 0x06, 0x3C, 0xF3, 0x9F, - 0x1F, 0xE6, 0x3B, 0x0E, 0xF9, 0x07, 0x00, 0xD4, 0x01, 0x33, 0xFE, - 0x1B, 0x01, 0x94, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD4, 0xFF, 0x43, - 0x00, 0x33, 0x00, 0x25, 0xFE, 0x89, 0x05, 0xE0, 0xF2, 0x9C, 0x26, - 0x33, 0x37, 0x1E, 0xF6, 0xFD, 0x01, 0xB0, 0x00, 0xC0, 0xFE, 0xE6, - 0x00, 0xA2, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC1, 0xFF, 0x7F, 0x00, - 0xB2, 0xFF, 0xF6, 0xFE, 0x8E, 0x04, 0x51, 0xF3, 0x49, 0x2D, 0x98, - 0x31, 0x23, 0xF4, 0xA2, 0x03, 0xA0, 0xFF, 0x51, 0xFF, 0xAA, 0x00, - 0xB4, 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAE, 0xFF, 0xBD, 0x00, 0x25, - 0xFF, 0xF1, 0xFF, 0x2B, 0x03, 0xA5, 0xF4, 0x68, 0x33, 0x48, 0x2B, - 0x17, 0xF3, 0xE7, 0x04, 0xB1, 0xFE, 0xDB, 0xFF, 0x6C, 0x00, 0xC7, - 0xFF, 0x06, 0x00, 0x0D, 0x00, 0x9E, 0xFF, 0xF7, 0x00, 0x94, 0xFE, - 0x09, 0x01, 0x6A, 0x01, 0xEB, 0xF6, 0xC1, 0x38, 0x7D, 0x24, 0xE8, - 0xF2, 0xC1, 0x05, 0xEE, 0xFD, 0x57, 0x00, 0x31, 0x00, 0xDA, 0xFF, - 0x03, 0x00, 0x10, 0x00, 0x91, 0xFF, 0x29, 0x01, 0x09, 0xFE, 0x2F, - 0x02, 0x5F, 0xFF, 0x27, 0xFA, 0x20, 0x3D, 0x70, 0x1D, 0x7D, 0xF3, - 0x31, 0x06, 0x5E, 0xFD, 0xBF, 0x00, 0xFD, 0xFF, 0xEB, 0xFF, 0x02, - 0x00, 0x11, 0x00, 0x8B, 0xFF, 0x4E, 0x01, 0x8E, 0xFD, 0x52, 0x03, - 0x20, 0xFD, 0x52, 0xFE, 0x60, 0x40, 0x63, 0x16, 0xB7, 0xF4, 0x39, - 0x06, 0x05, 0xFD, 0x0F, 0x01, 0xD1, 0xFF, 0xF9, 0xFF, 0x00, 0x00, - 0x10, 0x00, 0x8D, 0xFF, 0x62, 0x01, 0x2E, 0xFD, 0x5E, 0x04, 0xCC, - 0xFA, 0x5B, 0x03, 0x5E, 0x42, 0x8E, 0x0F, 0x71, 0xF6, 0xE4, 0x05, - 0xE2, 0xFC, 0x45, 0x01, 0xAF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0B, 0x00, 0x99, 0xFF, 0x60, 0x01, 0xF2, 0xFC, 0x40, 0x05, - 0x85, 0xF8, 0x26, 0x09, 0x0C, 0x43, 0x26, 0x09, 0x85, 0xF8, 0x40, - 0x05, 0xF2, 0xFC, 0x60, 0x01, 0x99, 0xFF, 0x0B, 0x00, 0x00, 0x00, - 0x04, 0x00, 0xAF, 0xFF, 0x45, 0x01, 0xE2, 0xFC, 0xE4, 0x05, 0x71, - 0xF6, 0x8E, 0x0F, 0x5E, 0x42, 0x5B, 0x03, 0xCC, 0xFA, 0x5E, 0x04, - 0x2E, 0xFD, 0x62, 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xF9, - 0xFF, 0xD1, 0xFF, 0x0F, 0x01, 0x05, 0xFD, 0x39, 0x06, 0xB7, 0xF4, - 0x63, 0x16, 0x60, 0x40, 0x52, 0xFE, 0x20, 0xFD, 0x52, 0x03, 0x8E, - 0xFD, 0x4E, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEB, 0xFF, - 0xFD, 0xFF, 0xBF, 0x00, 0x5E, 0xFD, 0x31, 0x06, 0x7D, 0xF3, 0x70, - 0x1D, 0x20, 0x3D, 0x27, 0xFA, 0x5F, 0xFF, 0x2F, 0x02, 0x09, 0xFE, - 0x29, 0x01, 0x91, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDA, 0xFF, 0x31, - 0x00, 0x57, 0x00, 0xEE, 0xFD, 0xC1, 0x05, 0xE8, 0xF2, 0x7D, 0x24, - 0xC1, 0x38, 0xEB, 0xF6, 0x6A, 0x01, 0x09, 0x01, 0x94, 0xFE, 0xF7, - 0x00, 0x9E, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC7, 0xFF, 0x6C, 0x00, - 0xDB, 0xFF, 0xB1, 0xFE, 0xE7, 0x04, 0x17, 0xF3, 0x48, 0x2B, 0x68, - 0x33, 0xA5, 0xF4, 0x2B, 0x03, 0xF1, 0xFF, 0x25, 0xFF, 0xBD, 0x00, - 0xAE, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB4, 0xFF, 0xAA, 0x00, 0x51, - 0xFF, 0xA0, 0xFF, 0xA2, 0x03, 0x23, 0xF4, 0x98, 0x31, 0x49, 0x2D, - 0x51, 0xF3, 0x8E, 0x04, 0xF6, 0xFE, 0xB2, 0xFF, 0x7F, 0x00, 0xC1, - 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA2, 0xFF, 0xE6, 0x00, 0xC0, 0xFE, - 0xB0, 0x00, 0xFD, 0x01, 0x1E, 0xF6, 0x33, 0x37, 0x9C, 0x26, 0xE0, - 0xF2, 0x89, 0x05, 0x25, 0xFE, 0x33, 0x00, 0x43, 0x00, 0xD4, 0xFF, - 0x04, 0x00, 0x0F, 0x00, 0x94, 0xFF, 0x1B, 0x01, 0x33, 0xFE, 0xD4, - 0x01, 0x07, 0x00, 0x0E, 0xF9, 0xE6, 0x3B, 0x9F, 0x1F, 0x3C, 0xF3, - 0x1A, 0x06, 0x85, 0xFD, 0xA2, 0x00, 0x0C, 0x00, 0xE6, 0xFF, 0x02, - 0x00, 0x11, 0x00, 0x8C, 0xFF, 0x45, 0x01, 0xB2, 0xFD, 0xFA, 0x02, - 0xD5, 0xFD, 0xF1, 0xFC, 0x81, 0x3F, 0x8B, 0x18, 0x47, 0xF4, 0x41, - 0x06, 0x1B, 0xFD, 0xF9, 0x00, 0xDD, 0xFF, 0xF5, 0xFF, 0x01, 0x00, - 0x10, 0x00, 0x8B, 0xFF, 0x5E, 0x01, 0x48, 0xFD, 0x0F, 0x04, 0x84, - 0xFB, 0xB8, 0x01, 0xE4, 0x41, 0x9F, 0x11, 0xDE, 0xF5, 0x07, 0x06, - 0xE7, 0xFC, 0x37, 0x01, 0xB8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0D, - 0x00, 0x94, 0xFF, 0x63, 0x01, 0x00, 0xFD, 0x00, 0x05, 0x35, 0xF9, - 0x4B, 0x07, 0xFB, 0x42, 0x10, 0x0B, 0xDA, 0xF7, 0x7A, 0x05, 0xE8, - 0xFC, 0x5A, 0x01, 0x9E, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x00, 0xA7, 0xFF, 0x50, 0x01, 0xE2, 0xFC, 0xB9, 0x05, 0x0D, - 0xF7, 0x87, 0x0D, 0xB8, 0x42, 0x10, 0x05, 0x16, 0xFA, 0xA9, 0x04, - 0x17, 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFD, - 0xFF, 0xC5, 0xFF, 0x22, 0x01, 0xF5, 0xFC, 0x28, 0x06, 0x33, 0xF5, - 0x40, 0x14, 0x20, 0x41, 0xC8, 0xFF, 0x69, 0xFC, 0xA8, 0x03, 0x6D, - 0xFD, 0x57, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF0, 0xFF, - 0xEE, 0xFF, 0xDB, 0x00, 0x3D, 0xFD, 0x3E, 0x06, 0xCE, 0xF3, 0x42, - 0x1B, 0x40, 0x3E, 0x56, 0xFB, 0xB2, 0xFE, 0x8A, 0x02, 0xE1, 0xFD, - 0x36, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDF, 0xFF, 0x20, - 0x00, 0x7A, 0x00, 0xBC, 0xFD, 0xEF, 0x05, 0x02, 0xF3, 0x55, 0x22, - 0x35, 0x3A, 0xD0, 0xF7, 0xD0, 0x00, 0x63, 0x01, 0x68, 0xFE, 0x07, - 0x01, 0x99, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCD, 0xFF, 0x5A, 0x00, - 0x03, 0x00, 0x70, 0xFE, 0x35, 0x05, 0xF2, 0xF2, 0x3B, 0x29, 0x24, - 0x35, 0x3E, 0xF5, 0xAA, 0x02, 0x44, 0x00, 0xF8, 0xFE, 0xCF, 0x00, - 0xA9, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBA, 0xFF, 0x97, 0x00, 0x7C, - 0xFF, 0x52, 0xFF, 0x11, 0x04, 0xB8, 0xF3, 0xB8, 0x2F, 0x3D, 0x2F, - 0xA1, 0xF3, 0x2B, 0x04, 0x3F, 0xFF, 0x87, 0xFF, 0x92, 0x00, 0xBB, - 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA8, 0xFF, 0xD4, 0x00, 0xED, 0xFE, - 0x5A, 0x00, 0x88, 0x02, 0x68, 0xF5, 0x91, 0x35, 0xB6, 0x28, 0xEC, - 0xF2, 0x48, 0x05, 0x60, 0xFE, 0x0D, 0x00, 0x55, 0x00, 0xCE, 0xFF, - 0x05, 0x00, 0x0E, 0x00, 0x98, 0xFF, 0x0B, 0x01, 0x5D, 0xFE, 0x79, - 0x01, 0xA9, 0x00, 0x0D, 0xF8, 0x8F, 0x3A, 0xCB, 0x21, 0x0C, 0xF3, - 0xF9, 0x05, 0xB0, 0xFD, 0x82, 0x00, 0x1C, 0x00, 0xE1, 0xFF, 0x03, - 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x39, 0x01, 0xD7, 0xFD, 0xA0, 0x02, - 0x86, 0xFE, 0xA6, 0xFB, 0x85, 0x3E, 0xB7, 0x1A, 0xE4, 0xF3, 0x40, - 0x06, 0x35, 0xFD, 0xE1, 0x00, 0xEB, 0xFF, 0xF1, 0xFF, 0x01, 0x00, - 0x11, 0x00, 0x8A, 0xFF, 0x58, 0x01, 0x66, 0xFD, 0xBD, 0x03, 0x3C, - 0xFC, 0x29, 0x00, 0x4A, 0x41, 0xB8, 0x13, 0x54, 0xF5, 0x22, 0x06, - 0xF1, 0xFC, 0x27, 0x01, 0xC2, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x0E, - 0x00, 0x90, 0xFF, 0x64, 0x01, 0x12, 0xFD, 0xBB, 0x04, 0xE8, 0xF9, - 0x81, 0x05, 0xCB, 0x42, 0x08, 0x0D, 0x35, 0xF7, 0xAD, 0x05, 0xE2, - 0xFC, 0x52, 0x01, 0xA5, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x00, 0xA0, 0xFF, 0x58, 0x01, 0xE6, 0xFC, 0x87, 0x05, 0xB0, - 0xF7, 0x8D, 0x0B, 0xF2, 0x42, 0xD7, 0x06, 0x62, 0xF9, 0xEF, 0x04, - 0x04, 0xFD, 0x63, 0x01, 0x93, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xBB, 0xFF, 0x33, 0x01, 0xE9, 0xFC, 0x0F, 0x06, 0xBA, 0xF5, - 0x24, 0x12, 0xC2, 0x41, 0x53, 0x01, 0xB2, 0xFB, 0xFB, 0x03, 0x4F, - 0xFD, 0x5D, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF4, 0xFF, - 0xE0, 0xFF, 0xF3, 0x00, 0x21, 0xFD, 0x41, 0x06, 0x2D, 0xF4, 0x16, - 0x19, 0x45, 0x3F, 0x9C, 0xFC, 0x01, 0xFE, 0xE4, 0x02, 0xBB, 0xFD, - 0x42, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE5, 0xFF, 0x10, - 0x00, 0x9A, 0x00, 0x8F, 0xFD, 0x12, 0x06, 0x2E, 0xF3, 0x2A, 0x20, - 0x92, 0x3B, 0xCC, 0xF8, 0x30, 0x00, 0xBD, 0x01, 0x3D, 0xFE, 0x17, - 0x01, 0x95, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD3, 0xFF, 0x47, 0x00, - 0x2A, 0x00, 0x33, 0xFE, 0x7A, 0x05, 0xE1, 0xF2, 0x24, 0x27, 0xCD, - 0x36, 0xEE, 0xF5, 0x21, 0x02, 0x9B, 0x00, 0xCB, 0xFE, 0xE1, 0x00, - 0xA4, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0x84, 0x00, 0xA7, - 0xFF, 0x08, 0xFF, 0x76, 0x04, 0x63, 0xF3, 0xC8, 0x2D, 0x22, 0x31, - 0x06, 0xF4, 0xBF, 0x03, 0x8C, 0xFF, 0x5C, 0xFF, 0xA5, 0x00, 0xB5, - 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAD, 0xFF, 0xC1, 0x00, 0x1A, 0xFF, - 0x05, 0x00, 0x0B, 0x03, 0xC9, 0xF4, 0xD8, 0x33, 0xC5, 0x2A, 0x0C, - 0xF3, 0xFB, 0x04, 0xA0, 0xFE, 0xE5, 0xFF, 0x68, 0x00, 0xC9, 0xFF, - 0x06, 0x00, 0x0D, 0x00, 0x9C, 0xFF, 0xFB, 0x00, 0x89, 0xFE, 0x1F, - 0x01, 0x44, 0x01, 0x22, 0xF7, 0x20, 0x39, 0xF3, 0x23, 0xED, 0xF2, - 0xCE, 0x05, 0xE1, 0xFD, 0x60, 0x00, 0x2D, 0x00, 0xDB, 0xFF, 0x03, - 0x00, 0x10, 0x00, 0x90, 0xFF, 0x2D, 0x01, 0xFF, 0xFD, 0x46, 0x02, - 0x34, 0xFF, 0x71, 0xFA, 0x6B, 0x3D, 0xE5, 0x1C, 0x90, 0xF3, 0x35, - 0x06, 0x55, 0xFD, 0xC6, 0x00, 0xF9, 0xFF, 0xEC, 0xFF, 0x01, 0x00, - 0x11, 0x00, 0x8B, 0xFF, 0x51, 0x01, 0x86, 0xFD, 0x68, 0x03, 0xF3, - 0xFC, 0xAE, 0xFE, 0x92, 0x40, 0xDA, 0x15, 0xD5, 0xF4, 0x35, 0x06, - 0x00, 0xFD, 0x14, 0x01, 0xCE, 0xFF, 0xFA, 0xFF, 0x00, 0x00, 0x0F, - 0x00, 0x8D, 0xFF, 0x63, 0x01, 0x28, 0xFD, 0x71, 0x04, 0x9E, 0xFA, - 0xC7, 0x03, 0x79, 0x42, 0x0B, 0x0F, 0x97, 0xF6, 0xDA, 0x05, 0xE2, - 0xFC, 0x48, 0x01, 0xAD, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x9A, 0xFF, 0x5F, 0x01, 0xEF, 0xFC, 0x4F, 0x05, 0x5A, - 0xF8, 0x9F, 0x09, 0x0A, 0x43, 0xAE, 0x08, 0xB1, 0xF8, 0x30, 0x05, - 0xF5, 0xFC, 0x61, 0x01, 0x97, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, - 0x00, 0xB1, 0xFF, 0x41, 0x01, 0xE3, 0xFC, 0xED, 0x05, 0x4C, 0xF6, - 0x11, 0x10, 0x42, 0x42, 0xF1, 0x02, 0xFA, 0xFA, 0x4B, 0x04, 0x34, - 0xFD, 0x61, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF8, 0xFF, - 0xD4, 0xFF, 0x0A, 0x01, 0x0A, 0xFD, 0x3C, 0x06, 0x9A, 0xF4, 0xED, - 0x16, 0x2A, 0x40, 0xF8, 0xFD, 0x4D, 0xFD, 0x3C, 0x03, 0x97, 0xFD, - 0x4C, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEA, 0xFF, 0x00, - 0x00, 0xB8, 0x00, 0x67, 0xFD, 0x2C, 0x06, 0x6B, 0xF3, 0xFC, 0x1D, - 0xD3, 0x3C, 0xDF, 0xF9, 0x89, 0xFF, 0x18, 0x02, 0x13, 0xFE, 0x26, - 0x01, 0x92, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD9, 0xFF, 0x36, 0x00, - 0x4E, 0x00, 0xFB, 0xFD, 0xB4, 0x05, 0xE4, 0xF2, 0x04, 0x25, 0x5F, - 0x38, 0xB6, 0xF6, 0x90, 0x01, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, - 0x9F, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC6, 0xFF, 0x71, 0x00, 0xD1, - 0xFF, 0xC2, 0xFE, 0xD1, 0x04, 0x23, 0xF3, 0xC9, 0x2B, 0xF5, 0x32, - 0x83, 0xF4, 0x49, 0x03, 0xDC, 0xFF, 0x30, 0xFF, 0xB8, 0x00, 0xB0, - 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB3, 0xFF, 0xAE, 0x00, 0x46, 0xFF, - 0xB4, 0xFF, 0x85, 0x03, 0x42, 0xF4, 0x0E, 0x32, 0xCA, 0x2C, 0x41, - 0xF3, 0xA5, 0x04, 0xE4, 0xFE, 0xBC, 0xFF, 0x7A, 0x00, 0xC3, 0xFF, - 0x07, 0x00, 0x0D, 0x00, 0xA1, 0xFF, 0xEA, 0x00, 0xB5, 0xFE, 0xC6, - 0x00, 0xD9, 0x01, 0x4F, 0xF6, 0x99, 0x37, 0x16, 0x26, 0xE0, 0xF2, - 0x98, 0x05, 0x16, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0xD6, 0xFF, 0x04, - 0x00, 0x0F, 0x00, 0x93, 0xFF, 0x1F, 0x01, 0x28, 0xFE, 0xEB, 0x01, - 0xDD, 0xFF, 0x52, 0xF9, 0x36, 0x3C, 0x13, 0x1F, 0x4B, 0xF3, 0x20, - 0x06, 0x7B, 0xFD, 0xA9, 0x00, 0x08, 0x00, 0xE7, 0xFF, 0x02, 0x00, - 0x11, 0x00, 0x8C, 0xFF, 0x47, 0x01, 0xA9, 0xFD, 0x10, 0x03, 0xA8, - 0xFD, 0x47, 0xFD, 0xBB, 0x3F, 0x01, 0x18, 0x62, 0xF4, 0x40, 0x06, - 0x15, 0xFD, 0xFF, 0x00, 0xDA, 0xFF, 0xF6, 0xFF, 0x01, 0x00, 0x10, - 0x00, 0x8B, 0xFF, 0x5F, 0x01, 0x41, 0xFD, 0x23, 0x04, 0x56, 0xFB, - 0x1F, 0x02, 0x06, 0x42, 0x19, 0x11, 0x02, 0xF6, 0xFF, 0x05, 0xE5, - 0xFC, 0x3B, 0x01, 0xB6, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, - 0x95, 0xFF, 0x62, 0x01, 0xFC, 0xFC, 0x10, 0x05, 0x09, 0xF9, 0xC1, - 0x07, 0x03, 0x43, 0x94, 0x0A, 0x05, 0xF8, 0x6C, 0x05, 0xEA, 0xFC, - 0x5C, 0x01, 0x9D, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0xA9, 0xFF, 0x4D, 0x01, 0xE1, 0xFC, 0xC4, 0x05, 0xE6, 0xF6, - 0x08, 0x0E, 0xA5, 0x42, 0xA1, 0x04, 0x43, 0xFA, 0x97, 0x04, 0x1D, - 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF, - 0xC8, 0xFF, 0x1E, 0x01, 0xF8, 0xFC, 0x2D, 0x06, 0x13, 0xF5, 0xC8, - 0x14, 0xF2, 0x40, 0x69, 0xFF, 0x97, 0xFC, 0x92, 0x03, 0x75, 0xFD, - 0x55, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEF, 0xFF, 0xF2, - 0xFF, 0xD4, 0x00, 0x45, 0xFD, 0x3B, 0x06, 0xB8, 0xF3, 0xCE, 0x1B, - 0xFB, 0x3D, 0x08, 0xFB, 0xDE, 0xFE, 0x73, 0x02, 0xEB, 0xFD, 0x33, - 0x01, 0x8F, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDE, 0xFF, 0x25, 0x00, - 0x71, 0x00, 0xC8, 0xFD, 0xE5, 0x05, 0xFA, 0xF2, 0xDF, 0x22, 0xDB, - 0x39, 0x94, 0xF7, 0xF7, 0x00, 0x4C, 0x01, 0x73, 0xFE, 0x03, 0x01, - 0x9A, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCC, 0xFF, 0x5E, 0x00, 0xF9, - 0xFF, 0x80, 0xFE, 0x23, 0x05, 0xF9, 0xF2, 0xC0, 0x29, 0xB8, 0x34, - 0x16, 0xF5, 0xCB, 0x02, 0x2F, 0x00, 0x03, 0xFF, 0xCA, 0x00, 0xAA, - 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xB8, 0xFF, 0x9B, 0x00, 0x72, 0xFF, - 0x65, 0xFF, 0xF6, 0x03, 0xD1, 0xF3, 0x31, 0x30, 0xC1, 0x2E, 0x8B, - 0xF3, 0x45, 0x04, 0x2D, 0xFF, 0x92, 0xFF, 0x8D, 0x00, 0xBD, 0xFF, - 0x08, 0x00, 0x0C, 0x00, 0xA6, 0xFF, 0xD8, 0x00, 0xE2, 0xFE, 0x6F, - 0x00, 0x66, 0x02, 0x93, 0xF5, 0xFB, 0x35, 0x31, 0x28, 0xE7, 0xF2, - 0x59, 0x05, 0x51, 0xFE, 0x17, 0x00, 0x50, 0x00, 0xD0, 0xFF, 0x05, - 0x00, 0x0E, 0x00, 0x97, 0xFF, 0x0F, 0x01, 0x53, 0xFE, 0x90, 0x01, - 0x81, 0x00, 0x4B, 0xF8, 0xE6, 0x3A, 0x3F, 0x21, 0x16, 0xF3, 0x02, - 0x06, 0xA5, 0xFD, 0x8A, 0x00, 0x18, 0x00, 0xE2, 0xFF, 0x02, 0x00, - 0x10, 0x00, 0x8D, 0xFF, 0x3C, 0x01, 0xCE, 0xFD, 0xB7, 0x02, 0x5A, - 0xFE, 0xF7, 0xFB, 0xC6, 0x3E, 0x2C, 0x1A, 0xFC, 0xF3, 0x41, 0x06, - 0x2E, 0xFD, 0xE7, 0x00, 0xE7, 0xFF, 0xF2, 0xFF, 0x01, 0x00, 0x10, - 0x00, 0x8B, 0xFF, 0x5A, 0x01, 0x5E, 0xFD, 0xD2, 0x03, 0x0E, 0xFC, - 0x8B, 0x00, 0x75, 0x41, 0x32, 0x13, 0x75, 0xF5, 0x1C, 0x06, 0xEE, - 0xFC, 0x2B, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, - 0x91, 0xFF, 0x64, 0x01, 0x0D, 0xFD, 0xCD, 0x04, 0xBB, 0xF9, 0xF2, - 0x05, 0xD9, 0x42, 0x88, 0x0C, 0x5E, 0xF7, 0xA1, 0x05, 0xE3, 0xFC, - 0x54, 0x01, 0xA3, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, - 0x00, 0xA2, 0xFF, 0x56, 0x01, 0xE5, 0xFC, 0x94, 0x05, 0x87, 0xF7, - 0x0A, 0x0C, 0xE6, 0x42, 0x64, 0x06, 0x8E, 0xF9, 0xDE, 0x04, 0x09, - 0xFD, 0x64, 0x01, 0x92, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xBD, 0xFF, 0x2F, 0x01, 0xEC, 0xFC, 0x16, 0x06, 0x98, 0xF5, 0xAB, - 0x12, 0x9C, 0x41, 0xEE, 0x00, 0xE0, 0xFB, 0xE6, 0x03, 0x57, 0xFD, - 0x5B, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF3, 0xFF, 0xE4, - 0xFF, 0xED, 0x00, 0x27, 0xFD, 0x41, 0x06, 0x14, 0xF4, 0xA1, 0x19, - 0x06, 0x3F, 0x49, 0xFC, 0x2E, 0xFE, 0xCD, 0x02, 0xC4, 0xFD, 0x3F, - 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE3, 0xFF, 0x14, 0x00, - 0x92, 0x00, 0x9A, 0xFD, 0x0A, 0x06, 0x22, 0xF3, 0xB4, 0x20, 0x3C, - 0x3B, 0x8B, 0xF8, 0x58, 0x00, 0xA7, 0x01, 0x48, 0xFE, 0x13, 0x01, - 0x96, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD1, 0xFF, 0x4C, 0x00, 0x20, - 0x00, 0x42, 0xFE, 0x6A, 0x05, 0xE3, 0xF2, 0xAB, 0x27, 0x66, 0x36, - 0xC0, 0xF5, 0x44, 0x02, 0x85, 0x00, 0xD7, 0xFE, 0xDD, 0x00, 0xA5, - 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xBE, 0xFF, 0x89, 0x00, 0x9D, 0xFF, - 0x1A, 0xFF, 0x5E, 0x04, 0x76, 0xF3, 0x45, 0x2E, 0xAA, 0x30, 0xEB, - 0xF3, 0xDB, 0x03, 0x79, 0xFF, 0x67, 0xFF, 0xA0, 0x00, 0xB7, 0xFF, - 0x09, 0x00, 0x0B, 0x00, 0xAC, 0xFF, 0xC6, 0x00, 0x0E, 0xFF, 0x1A, - 0x00, 0xEB, 0x02, 0xEF, 0xF4, 0x49, 0x34, 0x43, 0x2A, 0x02, 0xF3, - 0x0F, 0x05, 0x90, 0xFE, 0xEF, 0xFF, 0x63, 0x00, 0xCA, 0xFF, 0x06, - 0x00, 0x0E, 0x00, 0x9B, 0xFF, 0xFF, 0x00, 0x7E, 0xFE, 0x36, 0x01, - 0x1E, 0x01, 0x5B, 0xF7, 0x7E, 0x39, 0x69, 0x23, 0xF3, 0xF2, 0xD9, - 0x05, 0xD4, 0xFD, 0x69, 0x00, 0x29, 0x00, 0xDD, 0xFF, 0x03, 0x00, - 0x10, 0x00, 0x90, 0xFF, 0x30, 0x01, 0xF5, 0xFD, 0x5C, 0x02, 0x09, - 0xFF, 0xBC, 0xFA, 0xB5, 0x3D, 0x5A, 0x1C, 0xA3, 0xF3, 0x38, 0x06, - 0x4D, 0xFD, 0xCD, 0x00, 0xF5, 0xFF, 0xED, 0xFF, 0x01, 0x00, 0x11, - 0x00, 0x8B, 0xFF, 0x53, 0x01, 0x7E, 0xFD, 0x7D, 0x03, 0xC5, 0xFC, - 0x0B, 0xFF, 0xC3, 0x40, 0x51, 0x15, 0xF4, 0xF4, 0x31, 0x06, 0xFC, - 0xFC, 0x19, 0x01, 0xCB, 0xFF, 0xFB, 0xFF, 0x00, 0x00, 0x0F, 0x00, - 0x8E, 0xFF, 0x63, 0x01, 0x22, 0xFD, 0x84, 0x04, 0x71, 0xFA, 0x34, - 0x04, 0x90, 0x42, 0x89, 0x0E, 0xBE, 0xF6, 0xCF, 0x05, 0xE1, 0xFC, - 0x4A, 0x01, 0xAB, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, - 0x00, 0x9B, 0xFF, 0x5D, 0x01, 0xEC, 0xFC, 0x5D, 0x05, 0x2F, 0xF8, - 0x19, 0x0A, 0x07, 0x43, 0x37, 0x08, 0xDD, 0xF8, 0x21, 0x05, 0xF8, - 0xFC, 0x62, 0x01, 0x96, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, - 0xB4, 0xFF, 0x3E, 0x01, 0xE4, 0xFC, 0xF6, 0x05, 0x26, 0xF6, 0x95, - 0x10, 0x26, 0x42, 0x87, 0x02, 0x28, 0xFB, 0x37, 0x04, 0x3B, 0xFD, - 0x60, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF7, 0xFF, 0xD7, - 0xFF, 0x04, 0x01, 0x0F, 0xFD, 0x3E, 0x06, 0x7D, 0xF4, 0x76, 0x17, - 0xF4, 0x3F, 0x9F, 0xFD, 0x7B, 0xFD, 0x26, 0x03, 0xA0, 0xFD, 0x4A, - 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE9, 0xFF, 0x04, 0x00, - 0xB1, 0x00, 0x71, 0xFD, 0x26, 0x06, 0x5A, 0xF3, 0x88, 0x1E, 0x87, - 0x3C, 0x98, 0xF9, 0xB3, 0xFF, 0x02, 0x02, 0x1E, 0xFE, 0x22, 0x01, - 0x93, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD7, 0xFF, 0x3A, 0x00, 0x45, - 0x00, 0x09, 0xFE, 0xA7, 0x05, 0xE1, 0xF2, 0x8D, 0x25, 0xFD, 0x37, - 0x82, 0xF6, 0xB5, 0x01, 0xDC, 0x00, 0xAA, 0xFE, 0xEE, 0x00, 0xA0, - 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC4, 0xFF, 0x76, 0x00, 0xC7, 0xFF, - 0xD3, 0xFE, 0xBC, 0x04, 0x31, 0xF3, 0x4A, 0x2C, 0x83, 0x32, 0x61, - 0xF4, 0x68, 0x03, 0xC8, 0xFF, 0x3B, 0xFF, 0xB3, 0x00, 0xB1, 0xFF, - 0x0A, 0x00, 0x0A, 0x00, 0xB1, 0xFF, 0xB3, 0x00, 0x3B, 0xFF, 0xC8, - 0xFF, 0x68, 0x03, 0x61, 0xF4, 0x83, 0x32, 0x4A, 0x2C, 0x31, 0xF3, - 0xBC, 0x04, 0xD3, 0xFE, 0xC7, 0xFF, 0x76, 0x00, 0xC4, 0xFF, 0x06, - 0x00, 0x0D, 0x00, 0xA0, 0xFF, 0xEE, 0x00, 0xAA, 0xFE, 0xDC, 0x00, - 0xB5, 0x01, 0x82, 0xF6, 0xFD, 0x37, 0x8D, 0x25, 0xE1, 0xF2, 0xA7, - 0x05, 0x09, 0xFE, 0x45, 0x00, 0x3A, 0x00, 0xD7, 0xFF, 0x04, 0x00, - 0x0F, 0x00, 0x93, 0xFF, 0x22, 0x01, 0x1E, 0xFE, 0x02, 0x02, 0xB3, - 0xFF, 0x98, 0xF9, 0x87, 0x3C, 0x88, 0x1E, 0x5A, 0xF3, 0x26, 0x06, - 0x71, 0xFD, 0xB1, 0x00, 0x04, 0x00, 0xE9, 0xFF, 0x02, 0x00, 0x11, - 0x00, 0x8B, 0xFF, 0x4A, 0x01, 0xA0, 0xFD, 0x26, 0x03, 0x7B, 0xFD, - 0x9F, 0xFD, 0xF4, 0x3F, 0x76, 0x17, 0x7D, 0xF4, 0x3E, 0x06, 0x0F, - 0xFD, 0x04, 0x01, 0xD7, 0xFF, 0xF7, 0xFF, 0x01, 0x00, 0x10, 0x00, - 0x8C, 0xFF, 0x60, 0x01, 0x3B, 0xFD, 0x37, 0x04, 0x28, 0xFB, 0x87, - 0x02, 0x26, 0x42, 0x95, 0x10, 0x26, 0xF6, 0xF6, 0x05, 0xE4, 0xFC, - 0x3E, 0x01, 0xB4, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x96, - 0xFF, 0x62, 0x01, 0xF8, 0xFC, 0x21, 0x05, 0xDD, 0xF8, 0x37, 0x08, - 0x07, 0x43, 0x19, 0x0A, 0x2F, 0xF8, 0x5D, 0x05, 0xEC, 0xFC, 0x5D, - 0x01, 0x9B, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, - 0xAB, 0xFF, 0x4A, 0x01, 0xE1, 0xFC, 0xCF, 0x05, 0xBE, 0xF6, 0x89, - 0x0E, 0x90, 0x42, 0x34, 0x04, 0x71, 0xFA, 0x84, 0x04, 0x22, 0xFD, - 0x63, 0x01, 0x8E, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0xFF, 0xCB, - 0xFF, 0x19, 0x01, 0xFC, 0xFC, 0x31, 0x06, 0xF4, 0xF4, 0x51, 0x15, - 0xC3, 0x40, 0x0B, 0xFF, 0xC5, 0xFC, 0x7D, 0x03, 0x7E, 0xFD, 0x53, - 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xED, 0xFF, 0xF5, 0xFF, - 0xCD, 0x00, 0x4D, 0xFD, 0x38, 0x06, 0xA3, 0xF3, 0x5A, 0x1C, 0xB5, - 0x3D, 0xBC, 0xFA, 0x09, 0xFF, 0x5C, 0x02, 0xF5, 0xFD, 0x30, 0x01, - 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDD, 0xFF, 0x29, 0x00, 0x69, - 0x00, 0xD4, 0xFD, 0xD9, 0x05, 0xF3, 0xF2, 0x69, 0x23, 0x7E, 0x39, - 0x5B, 0xF7, 0x1E, 0x01, 0x36, 0x01, 0x7E, 0xFE, 0xFF, 0x00, 0x9B, - 0xFF, 0x0E, 0x00, 0x06, 0x00, 0xCA, 0xFF, 0x63, 0x00, 0xEF, 0xFF, - 0x90, 0xFE, 0x0F, 0x05, 0x02, 0xF3, 0x43, 0x2A, 0x49, 0x34, 0xEF, - 0xF4, 0xEB, 0x02, 0x1A, 0x00, 0x0E, 0xFF, 0xC6, 0x00, 0xAC, 0xFF, - 0x0B, 0x00, 0x09, 0x00, 0xB7, 0xFF, 0xA0, 0x00, 0x67, 0xFF, 0x79, - 0xFF, 0xDB, 0x03, 0xEB, 0xF3, 0xAA, 0x30, 0x45, 0x2E, 0x76, 0xF3, - 0x5E, 0x04, 0x1A, 0xFF, 0x9D, 0xFF, 0x89, 0x00, 0xBE, 0xFF, 0x07, - 0x00, 0x0C, 0x00, 0xA5, 0xFF, 0xDD, 0x00, 0xD7, 0xFE, 0x85, 0x00, - 0x44, 0x02, 0xC0, 0xF5, 0x66, 0x36, 0xAB, 0x27, 0xE3, 0xF2, 0x6A, - 0x05, 0x42, 0xFE, 0x20, 0x00, 0x4C, 0x00, 0xD1, 0xFF, 0x04, 0x00, - 0x0F, 0x00, 0x96, 0xFF, 0x13, 0x01, 0x48, 0xFE, 0xA7, 0x01, 0x58, - 0x00, 0x8B, 0xF8, 0x3C, 0x3B, 0xB4, 0x20, 0x22, 0xF3, 0x0A, 0x06, - 0x9A, 0xFD, 0x92, 0x00, 0x14, 0x00, 0xE3, 0xFF, 0x02, 0x00, 0x10, - 0x00, 0x8D, 0xFF, 0x3F, 0x01, 0xC4, 0xFD, 0xCD, 0x02, 0x2E, 0xFE, - 0x49, 0xFC, 0x06, 0x3F, 0xA1, 0x19, 0x14, 0xF4, 0x41, 0x06, 0x27, - 0xFD, 0xED, 0x00, 0xE4, 0xFF, 0xF3, 0xFF, 0x01, 0x00, 0x10, 0x00, - 0x8B, 0xFF, 0x5B, 0x01, 0x57, 0xFD, 0xE6, 0x03, 0xE0, 0xFB, 0xEE, - 0x00, 0x9C, 0x41, 0xAB, 0x12, 0x98, 0xF5, 0x16, 0x06, 0xEC, 0xFC, - 0x2F, 0x01, 0xBD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x92, - 0xFF, 0x64, 0x01, 0x09, 0xFD, 0xDE, 0x04, 0x8E, 0xF9, 0x64, 0x06, - 0xE6, 0x42, 0x0A, 0x0C, 0x87, 0xF7, 0x94, 0x05, 0xE5, 0xFC, 0x56, - 0x01, 0xA2, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, - 0xA3, 0xFF, 0x54, 0x01, 0xE3, 0xFC, 0xA1, 0x05, 0x5E, 0xF7, 0x88, - 0x0C, 0xD9, 0x42, 0xF2, 0x05, 0xBB, 0xF9, 0xCD, 0x04, 0x0D, 0xFD, - 0x64, 0x01, 0x91, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, - 0xFF, 0x2B, 0x01, 0xEE, 0xFC, 0x1C, 0x06, 0x75, 0xF5, 0x32, 0x13, - 0x75, 0x41, 0x8B, 0x00, 0x0E, 0xFC, 0xD2, 0x03, 0x5E, 0xFD, 0x5A, - 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF2, 0xFF, 0xE7, 0xFF, - 0xE7, 0x00, 0x2E, 0xFD, 0x41, 0x06, 0xFC, 0xF3, 0x2C, 0x1A, 0xC6, - 0x3E, 0xF7, 0xFB, 0x5A, 0xFE, 0xB7, 0x02, 0xCE, 0xFD, 0x3C, 0x01, - 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x18, 0x00, 0x8A, - 0x00, 0xA5, 0xFD, 0x02, 0x06, 0x16, 0xF3, 0x3F, 0x21, 0xE6, 0x3A, - 0x4B, 0xF8, 0x81, 0x00, 0x90, 0x01, 0x53, 0xFE, 0x0F, 0x01, 0x97, - 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xD0, 0xFF, 0x50, 0x00, 0x17, 0x00, - 0x51, 0xFE, 0x59, 0x05, 0xE7, 0xF2, 0x31, 0x28, 0xFB, 0x35, 0x93, - 0xF5, 0x66, 0x02, 0x6F, 0x00, 0xE2, 0xFE, 0xD8, 0x00, 0xA6, 0xFF, - 0x0C, 0x00, 0x08, 0x00, 0xBD, 0xFF, 0x8D, 0x00, 0x92, 0xFF, 0x2D, - 0xFF, 0x45, 0x04, 0x8B, 0xF3, 0xC1, 0x2E, 0x31, 0x30, 0xD1, 0xF3, - 0xF6, 0x03, 0x65, 0xFF, 0x72, 0xFF, 0x9B, 0x00, 0xB8, 0xFF, 0x08, - 0x00, 0x0B, 0x00, 0xAA, 0xFF, 0xCA, 0x00, 0x03, 0xFF, 0x2F, 0x00, - 0xCB, 0x02, 0x16, 0xF5, 0xB8, 0x34, 0xC0, 0x29, 0xF9, 0xF2, 0x23, - 0x05, 0x80, 0xFE, 0xF9, 0xFF, 0x5E, 0x00, 0xCC, 0xFF, 0x05, 0x00, - 0x0E, 0x00, 0x9A, 0xFF, 0x03, 0x01, 0x73, 0xFE, 0x4C, 0x01, 0xF7, - 0x00, 0x94, 0xF7, 0xDB, 0x39, 0xDF, 0x22, 0xFA, 0xF2, 0xE5, 0x05, - 0xC8, 0xFD, 0x71, 0x00, 0x25, 0x00, 0xDE, 0xFF, 0x03, 0x00, 0x10, - 0x00, 0x8F, 0xFF, 0x33, 0x01, 0xEB, 0xFD, 0x73, 0x02, 0xDE, 0xFE, - 0x08, 0xFB, 0xFB, 0x3D, 0xCE, 0x1B, 0xB8, 0xF3, 0x3B, 0x06, 0x45, - 0xFD, 0xD4, 0x00, 0xF2, 0xFF, 0xEF, 0xFF, 0x01, 0x00, 0x11, 0x00, - 0x8A, 0xFF, 0x55, 0x01, 0x75, 0xFD, 0x92, 0x03, 0x97, 0xFC, 0x69, - 0xFF, 0xF2, 0x40, 0xC8, 0x14, 0x13, 0xF5, 0x2D, 0x06, 0xF8, 0xFC, - 0x1E, 0x01, 0xC8, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x8F, - 0xFF, 0x64, 0x01, 0x1D, 0xFD, 0x97, 0x04, 0x43, 0xFA, 0xA1, 0x04, - 0xA5, 0x42, 0x08, 0x0E, 0xE6, 0xF6, 0xC4, 0x05, 0xE1, 0xFC, 0x4D, - 0x01, 0xA9, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, - 0x9D, 0xFF, 0x5C, 0x01, 0xEA, 0xFC, 0x6C, 0x05, 0x05, 0xF8, 0x94, - 0x0A, 0x03, 0x43, 0xC1, 0x07, 0x09, 0xF9, 0x10, 0x05, 0xFC, 0xFC, - 0x62, 0x01, 0x95, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB6, - 0xFF, 0x3B, 0x01, 0xE5, 0xFC, 0xFF, 0x05, 0x02, 0xF6, 0x19, 0x11, - 0x06, 0x42, 0x1F, 0x02, 0x56, 0xFB, 0x23, 0x04, 0x41, 0xFD, 0x5F, - 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF6, 0xFF, 0xDA, 0xFF, - 0xFF, 0x00, 0x15, 0xFD, 0x40, 0x06, 0x62, 0xF4, 0x01, 0x18, 0xBB, - 0x3F, 0x47, 0xFD, 0xA8, 0xFD, 0x10, 0x03, 0xA9, 0xFD, 0x47, 0x01, - 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE7, 0xFF, 0x08, 0x00, 0xA9, - 0x00, 0x7B, 0xFD, 0x20, 0x06, 0x4B, 0xF3, 0x13, 0x1F, 0x36, 0x3C, - 0x52, 0xF9, 0xDD, 0xFF, 0xEB, 0x01, 0x28, 0xFE, 0x1F, 0x01, 0x93, - 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD6, 0xFF, 0x3F, 0x00, 0x3C, 0x00, - 0x16, 0xFE, 0x98, 0x05, 0xE0, 0xF2, 0x16, 0x26, 0x99, 0x37, 0x4F, - 0xF6, 0xD9, 0x01, 0xC6, 0x00, 0xB5, 0xFE, 0xEA, 0x00, 0xA1, 0xFF, - 0x0D, 0x00, 0x07, 0x00, 0xC3, 0xFF, 0x7A, 0x00, 0xBC, 0xFF, 0xE4, - 0xFE, 0xA5, 0x04, 0x41, 0xF3, 0xCA, 0x2C, 0x0E, 0x32, 0x42, 0xF4, - 0x85, 0x03, 0xB4, 0xFF, 0x46, 0xFF, 0xAE, 0x00, 0xB3, 0xFF, 0x09, - 0x00, 0x0A, 0x00, 0xB0, 0xFF, 0xB8, 0x00, 0x30, 0xFF, 0xDC, 0xFF, - 0x49, 0x03, 0x83, 0xF4, 0xF5, 0x32, 0xC9, 0x2B, 0x23, 0xF3, 0xD1, - 0x04, 0xC2, 0xFE, 0xD1, 0xFF, 0x71, 0x00, 0xC6, 0xFF, 0x06, 0x00, - 0x0D, 0x00, 0x9F, 0xFF, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, 0x90, - 0x01, 0xB6, 0xF6, 0x5F, 0x38, 0x04, 0x25, 0xE4, 0xF2, 0xB4, 0x05, - 0xFB, 0xFD, 0x4E, 0x00, 0x36, 0x00, 0xD9, 0xFF, 0x04, 0x00, 0x0F, - 0x00, 0x92, 0xFF, 0x26, 0x01, 0x13, 0xFE, 0x18, 0x02, 0x89, 0xFF, - 0xDF, 0xF9, 0xD3, 0x3C, 0xFC, 0x1D, 0x6B, 0xF3, 0x2C, 0x06, 0x67, - 0xFD, 0xB8, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0x02, 0x00, 0x11, 0x00, - 0x8B, 0xFF, 0x4C, 0x01, 0x97, 0xFD, 0x3C, 0x03, 0x4D, 0xFD, 0xF8, - 0xFD, 0x2A, 0x40, 0xED, 0x16, 0x9A, 0xF4, 0x3C, 0x06, 0x0A, 0xFD, - 0x0A, 0x01, 0xD4, 0xFF, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x8C, - 0xFF, 0x61, 0x01, 0x34, 0xFD, 0x4B, 0x04, 0xFA, 0xFA, 0xF1, 0x02, - 0x42, 0x42, 0x11, 0x10, 0x4C, 0xF6, 0xED, 0x05, 0xE3, 0xFC, 0x41, - 0x01, 0xB1, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x97, 0xFF, - 0x61, 0x01, 0xF5, 0xFC, 0x30, 0x05, 0xB1, 0xF8, 0xAE, 0x08, 0x0A, - 0x43, 0x9F, 0x09, 0x5A, 0xF8, 0x4F, 0x05, 0xEF, 0xFC, 0x5F, 0x01, - 0x9A, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xAD, - 0xFF, 0x48, 0x01, 0xE2, 0xFC, 0xDA, 0x05, 0x97, 0xF6, 0x0B, 0x0F, - 0x79, 0x42, 0xC7, 0x03, 0x9E, 0xFA, 0x71, 0x04, 0x28, 0xFD, 0x63, - 0x01, 0x8D, 0xFF, 0x0F, 0x00 -}; - -static u16 -CoefficientSizes[] = { - /* Playback */ - 0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000, - /* Record */ - 0x0020, 0x1260, 0x0020, 0x1260, 0x0000, 0x0040, 0x1260, 0x0000, -}; - -#ifndef JUST_DATA - -static u16 -nm256_getStartOffset (u8 which) -{ - u16 offset = 0; - - while (which-- > 0) - offset += CoefficientSizes[which]; - - return offset; -} - -static void -nm256_loadOneCoefficient (struct nm256_info *card, int devnum, u32 port, - u16 which) -{ - u32 coeffBuf = (which < 8) ? card->coeffBuf : card->allCoeffBuf; - u16 offset = nm256_getStartOffset (which); - u16 size = CoefficientSizes[which]; - - card->coeffsCurrent = 0; - - if (nm256_debug) - printk (KERN_INFO "NM256: Loading coefficient buffer 0x%x-0x%x with coefficient %d, size %d, port 0x%x\n", - coeffBuf, coeffBuf + size - 1, which, size, port); - nm256_writeBuffer8 (card, coefficients + offset, 1, coeffBuf, size); - nm256_writePort32 (card, 2, port + 0, coeffBuf); - /* ??? Record seems to behave differently than playback. */ - if (devnum == 0) - size--; - nm256_writePort32 (card, 2, port + 4, coeffBuf + size); -} - -static void -nm256_loadAllCoefficients (struct nm256_info *card) -{ - nm256_writeBuffer8 (card, coefficients, 1, card->allCoeffBuf, - NM_TOTAL_COEFF_COUNT * 4); - card->coeffsCurrent = 1; -} - -void -nm256_loadCoefficient (struct nm256_info *card, int which, int number) -{ - static u16 addrs[3] = { 0x1c, 0x21c, 0x408 }; - /* The enable register for the specified engine. */ - u32 poffset = (which == 1 ? 0x200 : 1); - - if (nm256_readPort8 (card, 2, poffset) & 1) { - printk (KERN_ERR "NM256: Engine was enabled while loading coefficients!\n"); - return; - } - - /* The recording engine uses coefficient values 8-15. */ - if (which == 1) - number += 8; - - if (! nm256_cachedCoefficients (card)) - nm256_loadOneCoefficient (card, which, addrs[which], number); - else { - u32 base = card->allCoeffBuf; - u32 offset = nm256_getStartOffset (number); - u32 endOffset = offset + CoefficientSizes[number]; - - if (nm256_debug) - printk (KERN_DEBUG "loading coefficient %d at port 0x%x, offset %d (0x%x-0x%x)\n", - number, addrs[which], offset, base + offset, - base + endOffset - 1); - - if (! card->coeffsCurrent) - nm256_loadAllCoefficients (card); - - nm256_writePort32 (card, 2, addrs[which], base + offset); - nm256_writePort32 (card, 2, addrs[which] + 4, base + endOffset - 1); - } -} - -#endif /* JUST_DATA */ - -#endif - -/* - * Local variables: - * c-basic-offset: 4 - * End: - */ diff -Nru a/drivers/sound/opl3.c b/drivers/sound/opl3.c --- a/drivers/sound/opl3.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1253 +0,0 @@ -/* - * sound/opl3.c - * - * A low level driver for Yamaha YM3812 and OPL-3 -chips - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Changes - * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) - * Alan Cox modularisation, fixed sound_mem allocs. - * Christoph Hellwig Adapted to module_init/module_exit - * Arnaldo C. de Melo get rid of check_region, use request_region for - * OPL4, release it on exit, some cleanups. - * - * Status - * Believed to work. Badly needs rewriting a bit to support multiple - * OPL3 devices. - */ - -#include -#include -#include - -/* - * Major improvements to the FM handling 30AUG92 by Rob Hooft, - * hooft@chem.ruu.nl - */ - -#include "sound_config.h" - -#include "opl3.h" -#include "opl3_hw.h" - -#define MAX_VOICE 18 -#define OFFS_4OP 11 - -struct voice_info -{ - unsigned char keyon_byte; - long bender; - long bender_range; - unsigned long orig_freq; - unsigned long current_freq; - int volume; - int mode; - int panning; /* 0xffff means not set */ -}; - -typedef struct opl_devinfo -{ - int base; - int left_io, right_io; - int nr_voice; - int lv_map[MAX_VOICE]; - - struct voice_info voc[MAX_VOICE]; - struct voice_alloc_info *v_alloc; - struct channel_info *chn_info; - - struct sbi_instrument i_map[SBFM_MAXINSTR]; - struct sbi_instrument *act_i[MAX_VOICE]; - - struct synth_info fm_info; - - int busy; - int model; - unsigned char cmask; - - int is_opl4; - int *osp; -} opl_devinfo; - -static struct opl_devinfo *devc = NULL; - -static int detected_model; - -static int store_instr(int instr_no, struct sbi_instrument *instr); -static void freq_to_fnum(int freq, int *block, int *fnum); -static void opl3_command(int io_addr, unsigned int addr, unsigned int val); -static int opl3_kill_note(int dev, int voice, int note, int velocity); - -static void enter_4op_mode(void) -{ - int i; - static int v4op[MAX_VOICE] = { - 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17 - }; - - devc->cmask = 0x3f; /* Connect all possible 4 OP voice operators */ - opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f); - - for (i = 0; i < 3; i++) - pv_map[i].voice_mode = 4; - for (i = 3; i < 6; i++) - pv_map[i].voice_mode = 0; - - for (i = 9; i < 12; i++) - pv_map[i].voice_mode = 4; - for (i = 12; i < 15; i++) - pv_map[i].voice_mode = 0; - - for (i = 0; i < 12; i++) - devc->lv_map[i] = v4op[i]; - devc->v_alloc->max_voice = devc->nr_voice = 12; -} - -static int opl3_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - struct sbi_instrument ins; - - switch (cmd) { - case SNDCTL_FM_LOAD_INSTR: - printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); - if (copy_from_user(&ins, arg, sizeof(ins))) - return -EFAULT; - if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { - printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); - return -EINVAL; - } - return store_instr(ins.channel, &ins); - - case SNDCTL_SYNTH_INFO: - devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice; - if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info))) - return -EFAULT; - return 0; - - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - - case SNDCTL_FM_4OP_ENABLE: - if (devc->model == 2) - enter_4op_mode(); - return 0; - - default: - return -EINVAL; - } -} - -int opl3_detect(int ioaddr, int *osp) -{ - /* - * This function returns 1 if the FM chip is present at the given I/O port - * The detection algorithm plays with the timer built in the FM chip and - * looks for a change in the status register. - * - * Note! The timers of the FM chip are not connected to AdLib (and compatible) - * boards. - * - * Note2! The chip is initialized if detected. - */ - - unsigned char stat1, signature; - int i; - - if (devc != NULL) - { - printk(KERN_ERR "opl3: Only one OPL3 supported.\n"); - return 0; - } - - devc = (struct opl_devinfo *)kmalloc(sizeof(*devc), GFP_KERNEL); - - if (devc == NULL) - { - printk(KERN_ERR "opl3: Can't allocate memory for the device control " - "structure \n "); - return 0; - } - - memset(devc, 0, sizeof(*devc)); - strcpy(devc->fm_info.name, "OPL2"); - - if (!request_region(ioaddr, 4, devc->fm_info.name)) { - printk(KERN_WARNING "opl3: I/O port 0x%x already in use\n", ioaddr); - goto cleanup_devc; - } - - devc->osp = osp; - devc->base = ioaddr; - - /* Reset timers 1 and 2 */ - opl3_command(ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); - - /* Reset the IRQ of the FM chip */ - opl3_command(ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); - - signature = stat1 = inb(ioaddr); /* Status register */ - - if (signature != 0x00 && signature != 0x06 && signature != 0x02 && - signature != 0x0f) - { - MDB(printk(KERN_INFO "OPL3 not detected %x\n", signature)); - goto cleanup_region; - } - - if (signature == 0x06) /* OPL2 */ - { - detected_model = 2; - } - else if (signature == 0x00 || signature == 0x0f) /* OPL3 or OPL4 */ - { - unsigned char tmp; - - detected_model = 3; - - /* - * Detect availability of OPL4 (_experimental_). Works probably - * only after a cold boot. In addition the OPL4 port - * of the chip may not be connected to the PC bus at all. - */ - - opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0x00); - opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE); - - if ((tmp = inb(ioaddr)) == 0x02) /* Have a OPL4 */ - { - detected_model = 4; - } - - if (request_region(ioaddr - 8, 2, "OPL4")) /* OPL4 port was free */ - { - int tmp; - - outb((0x02), ioaddr - 8); /* Select OPL4 ID register */ - udelay(10); - tmp = inb(ioaddr - 7); /* Read it */ - udelay(10); - - if (tmp == 0x20) /* OPL4 should return 0x20 here */ - { - detected_model = 4; - outb((0xF8), ioaddr - 8); /* Select OPL4 FM mixer control */ - udelay(10); - outb((0x1B), ioaddr - 7); /* Write value */ - udelay(10); - } - else - { /* release OPL4 port */ - release_region(ioaddr - 8, 2); - detected_model = 3; - } - } - opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0); - } - for (i = 0; i < 9; i++) - opl3_command(ioaddr, KEYON_BLOCK + i, 0); /* - * Note off - */ - - opl3_command(ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT); - opl3_command(ioaddr, PERCOSSION_REGISTER, 0x00); /* - * Melodic mode. - */ - return 1; -cleanup_region: - release_region(ioaddr, 4); -cleanup_devc: - kfree(devc); - devc = NULL; - return 0; -} - -static int opl3_kill_note (int devno, int voice, int note, int velocity) -{ - struct physical_voice_info *map; - - if (voice < 0 || voice >= devc->nr_voice) - return 0; - - devc->v_alloc->map[voice] = 0; - - map = &pv_map[devc->lv_map[voice]]; - DEB(printk("Kill note %d\n", voice)); - - if (map->voice_mode == 0) - return 0; - - opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20); - devc->voc[voice].keyon_byte = 0; - devc->voc[voice].bender = 0; - devc->voc[voice].volume = 64; - devc->voc[voice].panning = 0xffff; /* Not set */ - devc->voc[voice].bender_range = 200; - devc->voc[voice].orig_freq = 0; - devc->voc[voice].current_freq = 0; - devc->voc[voice].mode = 0; - return 0; -} - -#define HIHAT 0 -#define CYMBAL 1 -#define TOMTOM 2 -#define SNARE 3 -#define BDRUM 4 -#define UNDEFINED TOMTOM -#define DEFAULT TOMTOM - -static int store_instr(int instr_no, struct sbi_instrument *instr) -{ - if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2)) - printk(KERN_WARNING "FM warning: Invalid patch format field (key) 0x%x\n", instr->key); - memcpy((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof(*instr)); - return 0; -} - -static int opl3_set_instr (int dev, int voice, int instr_no) -{ - if (voice < 0 || voice >= devc->nr_voice) - return 0; - if (instr_no < 0 || instr_no >= SBFM_MAXINSTR) - instr_no = 0; /* Acoustic piano (usually) */ - - devc->act_i[voice] = &devc->i_map[instr_no]; - return 0; -} - -/* - * The next table looks magical, but it certainly is not. Its values have - * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception - * for i=0. This log-table converts a linear volume-scaling (0..127) to a - * logarithmic scaling as present in the FM-synthesizer chips. so : Volume - * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative - * volume -8 it was implemented as a table because it is only 128 bytes and - * it saves a lot of log() calculations. (RH) - */ - -static char fm_volume_table[128] = -{ - -64, -48, -40, -35, -32, -29, -27, -26, - -24, -23, -21, -20, -19, -18, -18, -17, - -16, -15, -15, -14, -13, -13, -12, -12, - -11, -11, -10, -10, -10, -9, -9, -8, - -8, -8, -7, -7, -7, -6, -6, -6, - -5, -5, -5, -5, -4, -4, -4, -4, - -3, -3, -3, -3, -2, -2, -2, -2, - -2, -1, -1, -1, -1, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 4, - 4, 4, 4, 4, 4, 4, 4, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 8, 8, 8, 8, 8 -}; - -static void calc_vol(unsigned char *regbyte, int volume, int main_vol) -{ - int level = (~*regbyte & 0x3f); - - if (main_vol > 127) - main_vol = 127; - volume = (volume * main_vol) / 127; - - if (level) - level += fm_volume_table[volume]; - - if (level > 0x3f) - level = 0x3f; - if (level < 0) - level = 0; - - *regbyte = (*regbyte & 0xc0) | (~level & 0x3f); -} - -static void set_voice_volume(int voice, int volume, int main_vol) -{ - unsigned char vol1, vol2, vol3, vol4; - struct sbi_instrument *instr; - struct physical_voice_info *map; - - if (voice < 0 || voice >= devc->nr_voice) - return; - - map = &pv_map[devc->lv_map[voice]]; - instr = devc->act_i[voice]; - - if (!instr) - instr = &devc->i_map[0]; - - if (instr->channel < 0) - return; - - if (devc->voc[voice].mode == 0) - return; - - if (devc->voc[voice].mode == 2) - { - vol1 = instr->operators[2]; - vol2 = instr->operators[3]; - if ((instr->operators[10] & 0x01)) - { - calc_vol(&vol1, volume, main_vol); - calc_vol(&vol2, volume, main_vol); - } - else - { - calc_vol(&vol2, volume, main_vol); - } - opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1); - opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2); - } - else - { /* - * 4 OP voice - */ - int connection; - - vol1 = instr->operators[2]; - vol2 = instr->operators[3]; - vol3 = instr->operators[OFFS_4OP + 2]; - vol4 = instr->operators[OFFS_4OP + 3]; - - /* - * The connection method for 4 OP devc->voc is defined by the rightmost - * bits at the offsets 10 and 10+OFFS_4OP - */ - - connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); - - switch (connection) - { - case 0: - calc_vol(&vol4, volume, main_vol); - break; - - case 1: - calc_vol(&vol2, volume, main_vol); - calc_vol(&vol4, volume, main_vol); - break; - - case 2: - calc_vol(&vol1, volume, main_vol); - calc_vol(&vol4, volume, main_vol); - break; - - case 3: - calc_vol(&vol1, volume, main_vol); - calc_vol(&vol3, volume, main_vol); - calc_vol(&vol4, volume, main_vol); - break; - - default: - ; - } - opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1); - opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2); - opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], vol3); - opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], vol4); - } -} - -static int opl3_start_note (int dev, int voice, int note, int volume) -{ - unsigned char data, fpc; - int block, fnum, freq, voice_mode, pan; - struct sbi_instrument *instr; - struct physical_voice_info *map; - - if (voice < 0 || voice >= devc->nr_voice) - return 0; - - map = &pv_map[devc->lv_map[voice]]; - pan = devc->voc[voice].panning; - - if (map->voice_mode == 0) - return 0; - - if (note == 255) /* - * Just change the volume - */ - { - set_voice_volume(voice, volume, devc->voc[voice].volume); - return 0; - } - - /* - * Kill previous note before playing - */ - - opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* - * Carrier - * volume to - * min - */ - opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* - * Modulator - * volume to - */ - - if (map->voice_mode == 4) - { - opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff); - opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff); - } - - opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* - * Note - * off - */ - - instr = devc->act_i[voice]; - - if (!instr) - instr = &devc->i_map[0]; - - if (instr->channel < 0) - { - printk(KERN_WARNING "opl3: Initializing voice %d with undefined instrument\n", voice); - return 0; - } - - if (map->voice_mode == 2 && instr->key == OPL3_PATCH) - return 0; /* - * Cannot play - */ - - voice_mode = map->voice_mode; - - if (voice_mode == 4) - { - int voice_shift; - - voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3; - voice_shift += map->voice_num; - - if (instr->key != OPL3_PATCH) /* - * Just 2 OP patch - */ - { - voice_mode = 2; - devc->cmask &= ~(1 << voice_shift); - } - else - { - devc->cmask |= (1 << voice_shift); - } - - opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); - } - - /* - * Set Sound Characteristics - */ - - opl3_command(map->ioaddr, AM_VIB + map->op[0], instr->operators[0]); - opl3_command(map->ioaddr, AM_VIB + map->op[1], instr->operators[1]); - - /* - * Set Attack/Decay - */ - - opl3_command(map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]); - opl3_command(map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]); - - /* - * Set Sustain/Release - */ - - opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); - opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]); - - /* - * Set Wave Select - */ - - opl3_command(map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]); - opl3_command(map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]); - - /* - * Set Feedback/Connection - */ - - fpc = instr->operators[10]; - - if (pan != 0xffff) - { - fpc &= ~STEREO_BITS; - if (pan < -64) - fpc |= VOICE_TO_LEFT; - else - if (pan > 64) - fpc |= VOICE_TO_RIGHT; - else - fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT); - } - - if (!(fpc & 0x30)) - fpc |= 0x30; /* - * Ensure that at least one chn is enabled - */ - opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc); - - /* - * If the voice is a 4 OP one, initialize the operators 3 and 4 also - */ - - if (voice_mode == 4) - { - /* - * Set Sound Characteristics - */ - - opl3_command(map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); - opl3_command(map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]); - - /* - * Set Attack/Decay - */ - - opl3_command(map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); - opl3_command(map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]); - - /* - * Set Sustain/Release - */ - - opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); - opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]); - - /* - * Set Wave Select - */ - - opl3_command(map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); - opl3_command(map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]); - - /* - * Set Feedback/Connection - */ - - fpc = instr->operators[OFFS_4OP + 10]; - if (!(fpc & 0x30)) - fpc |= 0x30; /* - * Ensure that at least one chn is enabled - */ - opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc); - } - - devc->voc[voice].mode = voice_mode; - set_voice_volume(voice, volume, devc->voc[voice].volume); - - freq = devc->voc[voice].orig_freq = note_to_freq(note) / 1000; - - /* - * Since the pitch bender may have been set before playing the note, we - * have to calculate the bending now. - */ - - freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0); - devc->voc[voice].current_freq = freq; - - freq_to_fnum(freq, &block, &fnum); - - /* - * Play note - */ - - data = fnum & 0xff; /* - * Least significant bits of fnumber - */ - opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); - - data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); - devc->voc[voice].keyon_byte = data; - opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); - if (voice_mode == 4) - opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); - - return 0; -} - -static void freq_to_fnum (int freq, int *block, int *fnum) -{ - int f, octave; - - /* - * Converts the note frequency to block and fnum values for the FM chip - */ - /* - * First try to compute the block -value (octave) where the note belongs - */ - - f = freq; - - octave = 5; - - if (f == 0) - octave = 0; - else if (f < 261) - { - while (f < 261) - { - octave--; - f <<= 1; - } - } - else if (f > 493) - { - while (f > 493) - { - octave++; - f >>= 1; - } - } - - if (octave > 7) - octave = 7; - - *fnum = freq * (1 << (20 - octave)) / 49716; - *block = octave; -} - -static void opl3_command (int io_addr, unsigned int addr, unsigned int val) -{ - int i; - - /* - * The original 2-OP synth requires a quite long delay after writing to a - * register. The OPL-3 survives with just two INBs - */ - - outb(((unsigned char) (addr & 0xff)), io_addr); - - if (devc->model != 2) - udelay(10); - else - for (i = 0; i < 2; i++) - inb(io_addr); - - outb(((unsigned char) (val & 0xff)), io_addr + 1); - - if (devc->model != 2) - udelay(30); - else - for (i = 0; i < 2; i++) - inb(io_addr); -} - -static void opl3_reset(int devno) -{ - int i; - - for (i = 0; i < 18; i++) - devc->lv_map[i] = i; - - for (i = 0; i < devc->nr_voice; i++) - { - opl3_command(pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff); - - opl3_command(pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff); - - if (pv_map[devc->lv_map[i]].voice_mode == 4) - { - opl3_command(pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff); - - opl3_command(pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff); - } - - opl3_kill_note(devno, i, 0, 64); - } - - if (devc->model == 2) - { - devc->v_alloc->max_voice = devc->nr_voice = 18; - - for (i = 0; i < 18; i++) - pv_map[i].voice_mode = 2; - - } -} - -static int opl3_open(int dev, int mode) -{ - int i; - - if (devc->busy) - return -EBUSY; - devc->busy = 1; - - devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; - devc->v_alloc->timestamp = 0; - - for (i = 0; i < 18; i++) - { - devc->v_alloc->map[i] = 0; - devc->v_alloc->alloc_times[i] = 0; - } - - devc->cmask = 0x00; /* - * Just 2 OP mode - */ - if (devc->model == 2) - opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); - return 0; -} - -static void opl3_close(int dev) -{ - devc->busy = 0; - devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; - - devc->fm_info.nr_drums = 0; - devc->fm_info.perc_mode = 0; - - opl3_reset(dev); -} - -static void opl3_hw_control(int dev, unsigned char *event) -{ -} - -static int opl3_load_patch(int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) -{ - struct sbi_instrument ins; - - if (count = SBFM_MAXINSTR) - { - printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); - return -EINVAL; - } - ins.key = format; - - return store_instr(ins.channel, &ins); -} - -static void opl3_panning(int dev, int voice, int value) -{ - devc->voc[voice].panning = value; -} - -static void opl3_volume_method(int dev, int mode) -{ -} - -#define SET_VIBRATO(cell) { \ - tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ - if (pressure > 110) \ - tmp |= 0x40; /* Vibrato on */ \ - opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);} - -static void opl3_aftertouch(int dev, int voice, int pressure) -{ - int tmp; - struct sbi_instrument *instr; - struct physical_voice_info *map; - - if (voice < 0 || voice >= devc->nr_voice) - return; - - map = &pv_map[devc->lv_map[voice]]; - - DEB(printk("Aftertouch %d\n", voice)); - - if (map->voice_mode == 0) - return; - - /* - * Adjust the amount of vibrato depending the pressure - */ - - instr = devc->act_i[voice]; - - if (!instr) - instr = &devc->i_map[0]; - - if (devc->voc[voice].mode == 4) - { - int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); - - switch (connection) - { - case 0: - SET_VIBRATO(4); - break; - - case 1: - SET_VIBRATO(2); - SET_VIBRATO(4); - break; - - case 2: - SET_VIBRATO(1); - SET_VIBRATO(4); - break; - - case 3: - SET_VIBRATO(1); - SET_VIBRATO(3); - SET_VIBRATO(4); - break; - - } - /* - * Not implemented yet - */ - } - else - { - SET_VIBRATO(1); - - if ((instr->operators[10] & 0x01)) /* - * Additive synthesis - */ - SET_VIBRATO(2); - } -} - -#undef SET_VIBRATO - -static void bend_pitch(int dev, int voice, int value) -{ - unsigned char data; - int block, fnum, freq; - struct physical_voice_info *map; - - map = &pv_map[devc->lv_map[voice]]; - - if (map->voice_mode == 0) - return; - - devc->voc[voice].bender = value; - if (!value) - return; - if (!(devc->voc[voice].keyon_byte & 0x20)) - return; /* - * Not keyed on - */ - - freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0); - devc->voc[voice].current_freq = freq; - - freq_to_fnum(freq, &block, &fnum); - - data = fnum & 0xff; /* - * Least significant bits of fnumber - */ - opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); - - data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); - devc->voc[voice].keyon_byte = data; - opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); -} - -static void opl3_controller (int dev, int voice, int ctrl_num, int value) -{ - if (voice < 0 || voice >= devc->nr_voice) - return; - - switch (ctrl_num) - { - case CTRL_PITCH_BENDER: - bend_pitch(dev, voice, value); - break; - - case CTRL_PITCH_BENDER_RANGE: - devc->voc[voice].bender_range = value; - break; - - case CTL_MAIN_VOLUME: - devc->voc[voice].volume = value / 128; - break; - - case CTL_PAN: - devc->voc[voice].panning = (value * 2) - 128; - break; - } -} - -static void opl3_bender(int dev, int voice, int value) -{ - if (voice < 0 || voice >= devc->nr_voice) - return; - - bend_pitch(dev, voice, value - 8192); -} - -static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc) -{ - int i, p, best, first, avail, best_time = 0x7fffffff; - struct sbi_instrument *instr; - int is4op; - int instr_no; - - if (chn < 0 || chn > 15) - instr_no = 0; - else - instr_no = devc->chn_info[chn].pgm_num; - - instr = &devc->i_map[instr_no]; - if (instr->channel < 0 || /* Instrument not loaded */ - devc->nr_voice != 12) /* Not in 4 OP mode */ - is4op = 0; - else if (devc->nr_voice == 12) /* 4 OP mode */ - is4op = (instr->key == OPL3_PATCH); - else - is4op = 0; - - if (is4op) - { - first = p = 0; - avail = 6; - } - else - { - if (devc->nr_voice == 12) /* 4 OP mode. Use the '2 OP only' operators first */ - first = p = 6; - else - first = p = 0; - avail = devc->nr_voice; - } - - /* - * Now try to find a free voice - */ - best = first; - - for (i = 0; i < avail; i++) - { - if (alloc->map[p] == 0) - { - return p; - } - if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */ - { - best_time = alloc->alloc_times[p]; - best = p; - } - p = (p + 1) % avail; - } - - /* - * Insert some kind of priority mechanism here. - */ - - if (best < 0) - best = 0; - if (best > devc->nr_voice) - best -= devc->nr_voice; - - return best; /* All devc->voc in use. Select the first one. */ -} - -static void opl3_setup_voice(int dev, int voice, int chn) -{ - struct channel_info *info = - &synth_devs[dev]->chn_info[chn]; - - opl3_set_instr(dev, voice, info->pgm_num); - - devc->voc[voice].bender = 0; - devc->voc[voice].bender_range = info->bender_range; - devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME]; - devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; -} - -static struct synth_operations opl3_operations = -{ - owner: THIS_MODULE, - id: "OPL", - info: NULL, - midi_dev: 0, - synth_type: SYNTH_TYPE_FM, - synth_subtype: FM_TYPE_ADLIB, - open: opl3_open, - close: opl3_close, - ioctl: opl3_ioctl, - kill_note: opl3_kill_note, - start_note: opl3_start_note, - set_instr: opl3_set_instr, - reset: opl3_reset, - hw_control: opl3_hw_control, - load_patch: opl3_load_patch, - aftertouch: opl3_aftertouch, - controller: opl3_controller, - panning: opl3_panning, - volume_method: opl3_volume_method, - bender: opl3_bender, - alloc_voice: opl3_alloc_voice, - setup_voice: opl3_setup_voice -}; - -int opl3_init(int ioaddr, int *osp, struct module *owner) -{ - int i; - int me; - - if (devc == NULL) - { - printk(KERN_ERR "opl3: Device control structure not initialized.\n"); - return -1; - } - - if ((me = sound_alloc_synthdev()) == -1) - { - printk(KERN_WARNING "opl3: Too many synthesizers\n"); - return -1; - } - - devc->nr_voice = 9; - - devc->fm_info.device = 0; - devc->fm_info.synth_type = SYNTH_TYPE_FM; - devc->fm_info.synth_subtype = FM_TYPE_ADLIB; - devc->fm_info.perc_mode = 0; - devc->fm_info.nr_voices = 9; - devc->fm_info.nr_drums = 0; - devc->fm_info.instr_bank_size = SBFM_MAXINSTR; - devc->fm_info.capabilities = 0; - devc->left_io = ioaddr; - devc->right_io = ioaddr + 2; - - if (detected_model <= 2) - devc->model = 1; - else - { - devc->model = 2; - if (detected_model == 4) - devc->is_opl4 = 1; - } - - opl3_operations.info = &devc->fm_info; - - synth_devs[me] = &opl3_operations; - - if (owner) - synth_devs[me]->owner = owner; - - sequencer_init(); - devc->v_alloc = &opl3_operations.alloc; - devc->chn_info = &opl3_operations.chn_info[0]; - - if (devc->model == 2) - { - if (devc->is_opl4) - strcpy(devc->fm_info.name, "Yamaha OPL4/OPL3 FM"); - else - strcpy(devc->fm_info.name, "Yamaha OPL3"); - - devc->v_alloc->max_voice = devc->nr_voice = 18; - devc->fm_info.nr_drums = 0; - devc->fm_info.synth_subtype = FM_TYPE_OPL3; - devc->fm_info.capabilities |= SYNTH_CAP_OPL3; - - for (i = 0; i < 18; i++) - { - if (pv_map[i].ioaddr == USE_LEFT) - pv_map[i].ioaddr = devc->left_io; - else - pv_map[i].ioaddr = devc->right_io; - } - opl3_command(devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE); - opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x00); - } - else - { - strcpy(devc->fm_info.name, "Yamaha OPL2"); - devc->v_alloc->max_voice = devc->nr_voice = 9; - devc->fm_info.nr_drums = 0; - - for (i = 0; i < 18; i++) - pv_map[i].ioaddr = devc->left_io; - }; - conf_printf2(devc->fm_info.name, ioaddr, 0, -1, -1); - - for (i = 0; i < SBFM_MAXINSTR; i++) - devc->i_map[i].channel = -1; - - return me; -} - -EXPORT_SYMBOL(opl3_init); -EXPORT_SYMBOL(opl3_detect); - -static int me; - -static int io = -1; - -MODULE_PARM(io, "i"); - -static int __init init_opl3 (void) -{ - printk(KERN_INFO "YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996\n"); - - if (io != -1) /* User loading pure OPL3 module */ - { - if (!opl3_detect(io, NULL)) - { - return -ENODEV; - } - - me = opl3_init(io, NULL, THIS_MODULE); - } - - return 0; -} - -static void __exit cleanup_opl3(void) -{ - if (devc && io != -1) - { - if (devc->base) { - release_region(devc->base,4); - if (devc->is_opl4) - release_region(devc->base - 8, 2); - } - kfree(devc); - devc = NULL; - sound_unload_synthdev(me); - } -} - -module_init(init_opl3); -module_exit(cleanup_opl3); - -#ifndef MODULE -static int __init setup_opl3(char *str) -{ - /* io */ - int ints[2]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - - return 1; -} - -__setup("opl3=", setup_opl3); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/opl3.h b/drivers/sound/opl3.h --- a/drivers/sound/opl3.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -/* - * opl3.h - * - * Copyright: Christoph Hellwig - * - */ - -int opl3_detect (int ioaddr, int *osp); -int opl3_init(int ioaddr, int *osp, struct module *owner); - -void enable_opl3_mode(int left, int right, int both); diff -Nru a/drivers/sound/opl3_hw.h b/drivers/sound/opl3_hw.h --- a/drivers/sound/opl3_hw.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,246 +0,0 @@ -/* - * opl3_hw.h - Definitions of the OPL-3 registers - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * The OPL-3 mode is switched on by writing 0x01, to the offset 5 - * of the right side. - * - * Another special register at the right side is at offset 4. It contains - * a bit mask defining which voices are used as 4 OP voices. - * - * The percussive mode is implemented in the left side only. - * - * With the above exceptions the both sides can be operated independently. - * - * A 4 OP voice can be created by setting the corresponding - * bit at offset 4 of the right side. - * - * For example setting the rightmost bit (0x01) changes the - * first voice on the right side to the 4 OP mode. The fourth - * voice is made inaccessible. - * - * If a voice is set to the 2 OP mode, it works like 2 OP modes - * of the original YM3812 (AdLib). In addition the voice can - * be connected the left, right or both stereo channels. It can - * even be left unconnected. This works with 4 OP voices also. - * - * The stereo connection bits are located in the FEEDBACK_CONNECTION - * register of the voice (0xC0-0xC8). In 4 OP voices these bits are - * in the second half of the voice. - */ - -/* - * Register numbers for the global registers - */ - -#define TEST_REGISTER 0x01 -#define ENABLE_WAVE_SELECT 0x20 - -#define TIMER1_REGISTER 0x02 -#define TIMER2_REGISTER 0x03 -#define TIMER_CONTROL_REGISTER 0x04 /* Left side */ -#define IRQ_RESET 0x80 -#define TIMER1_MASK 0x40 -#define TIMER2_MASK 0x20 -#define TIMER1_START 0x01 -#define TIMER2_START 0x02 - -#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */ -#define RIGHT_4OP_0 0x01 -#define RIGHT_4OP_1 0x02 -#define RIGHT_4OP_2 0x04 -#define LEFT_4OP_0 0x08 -#define LEFT_4OP_1 0x10 -#define LEFT_4OP_2 0x20 - -#define OPL3_MODE_REGISTER 0x05 /* Right side */ -#define OPL3_ENABLE 0x01 -#define OPL4_ENABLE 0x02 - -#define KBD_SPLIT_REGISTER 0x08 /* Left side */ -#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ -#define KEYBOARD_SPLIT 0x40 - -#define PERCOSSION_REGISTER 0xbd /* Left side only */ -#define TREMOLO_DEPTH 0x80 -#define VIBRATO_DEPTH 0x40 -#define PERCOSSION_ENABLE 0x20 -#define BASSDRUM_ON 0x10 -#define SNAREDRUM_ON 0x08 -#define TOMTOM_ON 0x04 -#define CYMBAL_ON 0x02 -#define HIHAT_ON 0x01 - -/* - * Offsets to the register banks for operators. To get the - * register number just add the operator offset to the bank offset - * - * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) - */ -#define AM_VIB 0x20 -#define TREMOLO_ON 0x80 -#define VIBRATO_ON 0x40 -#define SUSTAIN_ON 0x20 -#define KSR 0x10 /* Key scaling rate */ -#define MULTIPLE_MASK 0x0f /* Frequency multiplier */ - - /* - * KSL/Total level (0x40 to 0x55) - */ -#define KSL_LEVEL 0x40 -#define KSL_MASK 0xc0 /* Envelope scaling bits */ -#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ - -/* - * Attack / Decay rate (0x60 to 0x75) - */ -#define ATTACK_DECAY 0x60 -#define ATTACK_MASK 0xf0 -#define DECAY_MASK 0x0f - -/* - * Sustain level / Release rate (0x80 to 0x95) - */ -#define SUSTAIN_RELEASE 0x80 -#define SUSTAIN_MASK 0xf0 -#define RELEASE_MASK 0x0f - -/* - * Wave select (0xE0 to 0xF5) - */ -#define WAVE_SELECT 0xe0 - -/* - * Offsets to the register banks for voices. Just add to the - * voice number to get the register number. - * - * F-Number low bits (0xA0 to 0xA8). - */ -#define FNUM_LOW 0xa0 - -/* - * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) - */ -#define KEYON_BLOCK 0xb0 -#define KEYON_BIT 0x20 -#define BLOCKNUM_MASK 0x1c -#define FNUM_HIGH_MASK 0x03 - -/* - * Feedback / Connection (0xc0 to 0xc8) - * - * These registers have two new bits when the OPL-3 mode - * is selected. These bits controls connecting the voice - * to the stereo channels. For 4 OP voices this bit is - * defined in the second half of the voice (add 3 to the - * register offset). - * - * For 4 OP voices the connection bit is used in the - * both halves (gives 4 ways to connect the operators). - */ -#define FEEDBACK_CONNECTION 0xc0 -#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ -#define CONNECTION_BIT 0x01 -/* - * In the 4 OP mode there is four possible configurations how the - * operators can be connected together (in 2 OP modes there is just - * AM or FM). The 4 OP connection mode is defined by the rightmost - * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves. - * - * First half Second half Mode - * - * +---+ - * v | - * 0 0 >+-1-+--2--3--4--> - * - * - * - * +---+ - * | | - * 0 1 >+-1-+--2-+ - * |-> - * >--3----4-+ - * - * +---+ - * | | - * 1 0 >+-1-+-----+ - * |-> - * >--2--3--4-+ - * - * +---+ - * | | - * 1 1 >+-1-+--+ - * | - * >--2--3-+-> - * | - * >--4----+ - */ -#define STEREO_BITS 0x30 /* OPL-3 only */ -#define VOICE_TO_LEFT 0x10 -#define VOICE_TO_RIGHT 0x20 - -/* - * Definition table for the physical voices - */ - -struct physical_voice_info { - unsigned char voice_num; - unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */ - unsigned short ioaddr; /* I/O port (left or right side) */ - unsigned char op[4]; /* Operator offsets */ - }; - -/* - * There is 18 possible 2 OP voices - * (9 in the left and 9 in the right). - * The first OP is the modulator and 2nd is the carrier. - * - * The first three voices in the both sides may be connected - * with another voice to a 4 OP voice. For example voice 0 - * can be connected with voice 3. The operators of voice 3 are - * used as operators 3 and 4 of the new 4 OP voice. - * In this case the 2 OP voice number 0 is the 'first half' and - * voice 3 is the second. - */ - -#define USE_LEFT 0 -#define USE_RIGHT 1 - -static struct physical_voice_info pv_map[18] = -{ -/* No Mode Side OP1 OP2 OP3 OP4 */ -/* --------------------------------------------------- */ - { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}}, - { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}}, - { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}}, - - { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}}, - { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}}, - { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}}, - - { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */ - { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */ - { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */ - - { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}}, - { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}}, - { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}}, - - { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}}, - { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}}, - { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}}, - - { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}}, - { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}}, - { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}} -}; -/* - * DMA buffer calls - */ diff -Nru a/drivers/sound/opl3sa.c b/drivers/sound/opl3sa.c --- a/drivers/sound/opl3sa.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,337 +0,0 @@ -/* - * sound/opl3sa.c - * - * Low level driver for Yamaha YMF701B aka OPL3-SA chip - * - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * Alan Cox Modularisation - * Christoph Hellwig Adapted to module_init/module_exit - * Arnaldo C. de Melo got rid of attach_uart401 - * - * FIXME: - * Check for install of mpu etc is wrong, should check result of the mss stuff - */ - -#include -#include - -#undef SB_OK - -#include "sound_config.h" - -#include "ad1848.h" -#include "mpu401.h" - -#ifdef SB_OK -#include "sb.h" -static int sb_initialized = 0; -#endif - -static int kilroy_was_here = 0; /* Don't detect twice */ -static int mpu_initialized = 0; - -static int *opl3sa_osp = NULL; - -static unsigned char opl3sa_read(int addr) -{ - unsigned long flags; - unsigned char tmp; - - save_flags(flags); - cli(); - outb((0x1d), 0xf86); /* password */ - outb(((unsigned char) addr), 0xf86); /* address */ - tmp = inb(0xf87); /* data */ - restore_flags(flags); - - return tmp; -} - -static void opl3sa_write(int addr, int data) -{ - unsigned long flags; - - save_flags(flags); - cli(); - outb((0x1d), 0xf86); /* password */ - outb(((unsigned char) addr), 0xf86); /* address */ - outb(((unsigned char) data), 0xf87); /* data */ - restore_flags(flags); -} - -static int __init opl3sa_detect(void) -{ - int tmp; - - if (((tmp = opl3sa_read(0x01)) & 0xc4) != 0x04) - { - DDB(printk("OPL3-SA detect error 1 (%x)\n", opl3sa_read(0x01))); - /* return 0; */ - } - - /* - * Check that the password feature has any effect - */ - - if (inb(0xf87) == tmp) - { - DDB(printk("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb(0xf87))); - return 0; - } - tmp = (opl3sa_read(0x04) & 0xe0) >> 5; - - if (tmp != 0 && tmp != 1) - { - DDB(printk("OPL3-SA detect failed 3 (%d)\n", tmp)); - return 0; - } - DDB(printk("OPL3-SA mode %x detected\n", tmp)); - - opl3sa_write(0x01, 0x00); /* Disable MSS */ - opl3sa_write(0x02, 0x00); /* Disable SB */ - opl3sa_write(0x03, 0x00); /* Disable MPU */ - - return 1; -} - -/* - * Probe and attach routines for the Windows Sound System mode of - * OPL3-SA - */ - -static int __init probe_opl3sa_wss(struct address_info *hw_config) -{ - int ret; - unsigned char tmp = 0x24; /* WSS enable */ - - if (check_region(0xf86, 2)) /* Control port is busy */ - return 0; - /* - * Check if the IO port returns valid signature. The original MS Sound - * system returns 0x04 while some cards (OPL3-SA for example) - * return 0x00. - */ - - if (check_region(hw_config->io_base, 8)) - { - printk(KERN_ERR "OPL3-SA: MSS I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - opl3sa_osp = hw_config->osp; - - if (!opl3sa_detect()) - { - printk(KERN_ERR "OSS: OPL3-SA chip not found\n"); - return 0; - } - - switch (hw_config->io_base) - { - case 0x530: - tmp |= 0x00; - break; - case 0xe80: - tmp |= 0x08; - break; - case 0xf40: - tmp |= 0x10; - break; - case 0x604: - tmp |= 0x18; - break; - default: - printk(KERN_ERR "OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base); - return 0; - } - - opl3sa_write(0x01, tmp); /* WSS setup register */ - kilroy_was_here = 1; - - ret = probe_ms_sound(hw_config); - if (ret) - request_region(0xf86, 2, "OPL3-SA"); - - return ret; -} - -static void __init attach_opl3sa_wss(struct address_info *hw_config) -{ - int nm = num_mixers; - - /* FIXME */ - attach_ms_sound(hw_config, THIS_MODULE); - if (num_mixers > nm) /* A mixer was installed */ - { - AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); - AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); - AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); - } -} - - -static int __init probe_opl3sa_mpu(struct address_info *hw_config) -{ - unsigned char conf; - static signed char irq_bits[] = { - -1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4 - }; - - if (!kilroy_was_here) - return 0; /* OPL3-SA has not been detected earlier */ - - if (mpu_initialized) - { - DDB(printk("OPL3-SA: MPU mode already initialized\n")); - return 0; - } - if (hw_config->irq > 10) - { - printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); - return 0; - } - if (irq_bits[hw_config->irq] == -1) - { - printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); - return 0; - } - switch (hw_config->io_base) - { - case 0x330: - conf = 0x00; - break; - case 0x332: - conf = 0x20; - break; - case 0x334: - conf = 0x40; - break; - case 0x300: - conf = 0x60; - break; - default: - return 0; /* Invalid port */ - } - - conf |= 0x83; /* MPU & OPL3 (synth) & game port enable */ - conf |= irq_bits[hw_config->irq] << 2; - - opl3sa_write(0x03, conf); - - mpu_initialized = 1; - hw_config->name = "OPL3-SA (MPU401)"; - - return probe_uart401(hw_config, THIS_MODULE); -} - -static void __exit unload_opl3sa_wss(struct address_info *hw_config) -{ - int dma2 = hw_config->dma2; - - if (dma2 == -1) - dma2 = hw_config->dma; - - release_region(0xf86, 2); - release_region(hw_config->io_base, 4); - - ad1848_unload(hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - dma2, - 0); - sound_unload_audiodev(hw_config->slots[0]); -} - -static inline void __exit unload_opl3sa_mpu(struct address_info *hw_config) -{ - unload_uart401(hw_config); -} - -#ifdef SB_OK -static inline void __exit unload_opl3sa_sb(struct address_info *hw_config) -{ - sb_dsp_unload(hw_config); -} -#endif - -static int found_mpu; - -static struct address_info cfg; -static struct address_info cfg_mpu; - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; -static int __initdata mpu_io = -1; -static int __initdata mpu_irq = -1; - -MODULE_PARM(io,"i"); -MODULE_PARM(irq,"i"); -MODULE_PARM(dma,"i"); -MODULE_PARM(dma2,"i"); -MODULE_PARM(mpu_io,"i"); -MODULE_PARM(mpu_irq,"i"); - -static int __init init_opl3sa(void) -{ - if (io == -1 || irq == -1 || dma == -1) { - printk(KERN_ERR "opl3sa: dma, irq and io must be set.\n"); - return -EINVAL; - } - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - - cfg_mpu.io_base = mpu_io; - cfg_mpu.irq = mpu_irq; - - if (probe_opl3sa_wss(&cfg) == 0) - return -ENODEV; - - found_mpu=probe_opl3sa_mpu(&cfg_mpu); - - attach_opl3sa_wss(&cfg); - return 0; -} - -static void __exit cleanup_opl3sa(void) -{ - if(found_mpu) - unload_opl3sa_mpu(&cfg_mpu); - unload_opl3sa_wss(&cfg); -} - -module_init(init_opl3sa); -module_exit(cleanup_opl3sa); - -#ifndef MODULE -static int __init setup_opl3sa(char *str) -{ - /* io, irq, dma, dma2, mpu_io, mpu_irq */ - int ints[7]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - mpu_io = ints[5]; - mpu_irq = ints[6]; - - return 1; -} - -__setup("opl3sa=", setup_opl3sa); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/opl3sa2.c b/drivers/sound/opl3sa2.c --- a/drivers/sound/opl3sa2.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1190 +0,0 @@ -/* - * sound/opl3sa2.c - * - * A low level driver for Yamaha OPL3-SA2 and SA3 cards. - * NOTE: All traces of the name OPL3-SAx have now (December 2000) been - * removed from the driver code, as an email exchange with Yamaha - * provided the information that the YMF-719 is indeed just a - * re-badged 715. - * - * Copyright 1998-2001 Scott Murray - * - * Originally based on the CS4232 driver (in cs4232.c) by Hannu Savolainen - * and others. Now incorporates code/ideas from pss.c, also by Hannu - * Savolainen. Both of those files are distributed with the following - * license: - * - * "Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info." - * - * As such, in accordance with the above license, this file, opl3sa2.c, is - * distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2 (June 1991). - * See the "COPYING" file distributed with this software for more information. - * - * Change History - * -------------- - * Scott Murray Original driver (Jun 14, 1998) - * Paul J.Y. Lahaie Changed probing / attach code order - * Scott Murray Added mixer support (Dec 03, 1998) - * Scott Murray Changed detection code to be more forgiving, - * added force option as last resort, - * fixed ioctl return values. (Dec 30, 1998) - * Scott Murray Simpler detection code should work all the time now - * (with thanks to Ben Hutchings for the heuristic), - * removed now unnecessary force option. (Jan 5, 1999) - * Christoph Hellwig Adapted to module_init/module_exit (Mar 4, 2000) - * Scott Murray Reworked SA2 versus SA3 mixer code, updated chipset - * version detection code (again!). (Dec 5, 2000) - * Scott Murray Adjusted master volume mixer scaling. (Dec 6, 2000) - * Scott Murray Based on a patch by Joel Yliluoma (aka Bisqwit), - * integrated wide mixer and adjusted mic, bass, treble - * scaling. (Dec 6, 2000) - * Scott Murray Based on a patch by Peter Englmaier, integrated - * ymode and loopback options. (Dec 6, 2000) - * Scott Murray Inspired by a patch by Peter Englmaier, and based on - * what ALSA does, added initialization code for the - * default DMA and IRQ settings. (Dec 6, 2000) - * Scott Murray Added some more checks to the card detection code, - * based on what ALSA does. (Dec 12, 2000) - * Scott Murray Inspired by similar patches from John Fremlin, - * Jim Radford, Mike Rolig, and Ingmar Steen, added 2.4 - * ISA PnP API support, mainly based on bits from - * sb_card.c and awe_wave.c. (Dec 12, 2000) - * Scott Murray Some small cleanups to the init code output. - * (Jan 7, 2001) - * Zwane Mwaikambo Added PM support. (Dec 4 2001) - * - */ - -#include -#include -#include -#include -#include -#include -#include "sound_config.h" - -#include "ad1848.h" -#include "mpu401.h" - -/* Useful control port indexes: */ -#define OPL3SA2_PM 0x01 -#define OPL3SA2_SYS_CTRL 0x02 -#define OPL3SA2_IRQ_CONFIG 0x03 -#define OPL3SA2_DMA_CONFIG 0x06 -#define OPL3SA2_MASTER_LEFT 0x07 -#define OPL3SA2_MASTER_RIGHT 0x08 -#define OPL3SA2_MIC 0x09 -#define OPL3SA2_MISC 0x0A - -#define OPL3SA3_WIDE 0x14 -#define OPL3SA3_BASS 0x15 -#define OPL3SA3_TREBLE 0x16 - -/* Useful constants: */ -#define DEFAULT_VOLUME 50 -#define DEFAULT_MIC 50 -#define DEFAULT_TIMBRE 0 - -/* Power saving modes */ -#define OPL3SA2_PM_MODE1 0x05 -#define OPL3SA2_PM_MODE2 0x04 -#define OPL3SA2_PM_MODE3 0x03 - -/* For checking against what the card returns: */ -#define VERSION_UNKNOWN 0 -#define VERSION_YMF711 1 -#define VERSION_YMF715 2 -#define VERSION_YMF715B 3 -#define VERSION_YMF715E 4 -/* also assuming that anything > 4 but <= 7 is a 715E */ - -/* Chipset type constants for use below */ -#define CHIPSET_UNKNOWN -1 -#define CHIPSET_OPL3SA2 0 -#define CHIPSET_OPL3SA3 1 - -#ifdef __ISAPNP__ -#define OPL3SA2_CARDS_MAX 4 -#else -#define OPL3SA2_CARDS_MAX 1 -#endif - -/* This should be pretty obvious */ -static int opl3sa2_cards_num; /* = 0 */ - -/* What's my version(s)? */ -static int chipset[OPL3SA2_CARDS_MAX] = { CHIPSET_UNKNOWN }; - -/* Oh well, let's just cache the name(s) */ -static char chipset_name[OPL3SA2_CARDS_MAX][12]; - -/* Where's my mixer(s)? */ -static int opl3sa2_mixer[OPL3SA2_CARDS_MAX] = { -1 }; - -/* Bag o' mixer data */ -typedef struct opl3sa2_mixerdata_tag { - unsigned short cfg_port; - unsigned short padding; - unsigned char reg; - unsigned int in_suspend; - struct pm_dev *pmdev; - unsigned int card; - unsigned int volume_l; - unsigned int volume_r; - unsigned int mic; - unsigned int bass_l; - unsigned int bass_r; - unsigned int treble_l; - unsigned int treble_r; - unsigned int wide_l; - unsigned int wide_r; -} opl3sa2_mixerdata; -static opl3sa2_mixerdata opl3sa2_data[OPL3SA2_CARDS_MAX]; - -static struct address_info cfg[OPL3SA2_CARDS_MAX]; -static struct address_info cfg_mss[OPL3SA2_CARDS_MAX]; -static struct address_info cfg_mpu[OPL3SA2_CARDS_MAX]; - -/* Our parameters */ -static int __initdata io = -1; -static int __initdata mss_io = -1; -static int __initdata mpu_io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; -static int __initdata ymode = -1; -static int __initdata loopback = -1; - -#ifdef __ISAPNP__ -/* PnP specific parameters */ -static int __initdata isapnp = 1; -static int __initdata multiple = 1; - -/* PnP devices */ -struct pci_dev* opl3sa2_dev[OPL3SA2_CARDS_MAX]; - -/* Whether said devices have been activated */ -static int opl3sa2_activated[OPL3SA2_CARDS_MAX]; -#else -static int __initdata isapnp; /* = 0 */ -static int __initdata multiple; /* = 0 */ -#endif - -MODULE_DESCRIPTION("Module for OPL3-SA2 and SA3 sound cards (uses AD1848 MSS driver)."); -MODULE_AUTHOR("Scott Murray "); -MODULE_LICENSE("GPL"); - - -MODULE_PARM(io, "i"); -MODULE_PARM_DESC(io, "Set I/O base of OPL3-SA2 or SA3 card (usually 0x370. Address must be even and must be from 0x100 to 0xFFE)"); - -MODULE_PARM(mss_io, "i"); -MODULE_PARM_DESC(mss_io, "Set MSS (audio) I/O base (0x530, 0xE80, or other. Address must end in 0 or 4 and must be from 0x530 to 0xF48)"); - -MODULE_PARM(mpu_io, "i"); -MODULE_PARM_DESC(mpu_io, "Set MIDI I/O base (0x330 or other. Address must be even and must be from 0x300 to 0x334)"); - -MODULE_PARM(irq, "i"); -MODULE_PARM_DESC(mss_irq, "Set MSS (audio) IRQ (5, 7, 9, 10, 11, 12)"); - -MODULE_PARM(dma, "i"); -MODULE_PARM_DESC(dma, "Set MSS (audio) first DMA channel (0, 1, 3)"); - -MODULE_PARM(dma2, "i"); -MODULE_PARM_DESC(dma2, "Set MSS (audio) second DMA channel (0, 1, 3)"); - -MODULE_PARM(ymode, "i"); -MODULE_PARM_DESC(ymode, "Set Yamaha 3D enhancement mode (0 = Desktop/Normal, 1 = Notebook PC (1), 2 = Notebook PC (2), 3 = Hi-Fi)"); - -MODULE_PARM(loopback, "i"); -MODULE_PARM_DESC(loopback, "Set A/D input source. Useful for echo cancellation (0 = Mic Rch (default), 1 = Mono output loopback)"); - -#ifdef __ISAPNP__ -MODULE_PARM(isapnp, "i"); -MODULE_PARM_DESC(isapnp, "When set to 0, ISA PnP support will be disabled"); - -MODULE_PARM(multiple, "i"); -MODULE_PARM_DESC(multiple, "When set to 0, will not search for multiple cards"); -#endif - - -/* - * Standard read and write functions -*/ - -static inline void opl3sa2_write(unsigned short port, - unsigned char index, - unsigned char data) -{ - outb_p(index, port); - outb(data, port + 1); -} - - -static inline void opl3sa2_read(unsigned short port, - unsigned char index, - unsigned char* data) -{ - outb_p(index, port); - *data = inb(port + 1); -} - - -/* - * All of the mixer functions... - */ - -static void opl3sa2_set_volume(opl3sa2_mixerdata* devc, int left, int right) -{ - static unsigned char scale[101] = { - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00 - }; - unsigned char vol; - - vol = scale[left]; - - /* If level is zero, turn on mute */ - if(!left) - vol |= 0x80; - - opl3sa2_write(devc->cfg_port, OPL3SA2_MASTER_LEFT, vol); - - vol = scale[right]; - - /* If level is zero, turn on mute */ - if(!right) - vol |= 0x80; - - opl3sa2_write(devc->cfg_port, OPL3SA2_MASTER_RIGHT, vol); -} - - -static void opl3sa2_set_mic(opl3sa2_mixerdata* devc, int level) -{ - unsigned char vol = 0x1F; - - if((level >= 0) && (level <= 100)) - vol = 0x1F - (unsigned char) (32 * level / 101); - - /* If level is zero, turn on mute */ - if(!level) - vol |= 0x80; - - opl3sa2_write(devc->cfg_port, OPL3SA2_MIC, vol); -} - - -static void opl3sa3_set_bass(opl3sa2_mixerdata* devc, int left, int right) -{ - unsigned char bass; - - bass = left ? ((unsigned char) (8 * left / 101)) : 0; - bass |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4; - - opl3sa2_write(devc->cfg_port, OPL3SA3_BASS, bass); -} - - -static void opl3sa3_set_treble(opl3sa2_mixerdata* devc, int left, int right) -{ - unsigned char treble; - - treble = left ? ((unsigned char) (8 * left / 101)) : 0; - treble |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4; - - opl3sa2_write(devc->cfg_port, OPL3SA3_TREBLE, treble); -} - - -static void opl3sa3_set_wide(opl3sa2_mixerdata* devc, int left, int right) -{ - unsigned char wide; - - wide = left ? ((unsigned char) (8 * left / 101)) : 0; - wide |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4; - - opl3sa2_write(devc->cfg_port, OPL3SA3_WIDE, wide); -} - - -static void opl3sa2_mixer_reset(opl3sa2_mixerdata* devc, int card) -{ - if(devc) { - opl3sa2_set_volume(devc, DEFAULT_VOLUME, DEFAULT_VOLUME); - devc->volume_l = devc->volume_r = DEFAULT_VOLUME; - - opl3sa2_set_mic(devc, DEFAULT_MIC); - devc->mic = DEFAULT_MIC; - - if(chipset[card] == CHIPSET_OPL3SA3) { - opl3sa3_set_bass(devc, DEFAULT_TIMBRE, DEFAULT_TIMBRE); - devc->bass_l = devc->bass_r = DEFAULT_TIMBRE; - opl3sa3_set_treble(devc, DEFAULT_TIMBRE, DEFAULT_TIMBRE); - devc->treble_l = devc->treble_r = DEFAULT_TIMBRE; - } - } -} - - -static void opl3sa2_mixer_restore(opl3sa2_mixerdata* devc, int card) -{ - if (devc) { - opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r); - opl3sa2_set_mic(devc, devc->mic); - - if (chipset[card] == CHIPSET_OPL3SA3) { - opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r); - opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r); - } - } -} - - -static inline void arg_to_vol_mono(unsigned int vol, int* value) -{ - int left; - - left = vol & 0x00ff; - if (left > 100) - left = 100; - *value = left; -} - - -static inline void arg_to_vol_stereo(unsigned int vol, int* aleft, int* aright) -{ - arg_to_vol_mono(vol, aleft); - arg_to_vol_mono(vol >> 8, aright); -} - - -static inline int ret_vol_mono(int vol) -{ - return ((vol << 8) | vol); -} - - -static inline int ret_vol_stereo(int left, int right) -{ - return ((right << 8) | left); -} - - -static int opl3sa2_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int cmdf = cmd & 0xff; - - opl3sa2_mixerdata* devc = (opl3sa2_mixerdata*) mixer_devs[dev]->devc; - - switch(cmdf) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_MIC: - case SOUND_MIXER_DEVMASK: - case SOUND_MIXER_STEREODEVS: - case SOUND_MIXER_RECMASK: - case SOUND_MIXER_RECSRC: - case SOUND_MIXER_CAPS: - break; - - default: - return -EINVAL; - } - - if(((cmd >> 8) & 0xff) != 'M') - return -EINVAL; - - if(_SIOC_DIR (cmd) & _SIOC_WRITE) { - switch (cmdf) { - case SOUND_MIXER_VOLUME: - arg_to_vol_stereo(*(unsigned int*)arg, - &devc->volume_l, &devc->volume_r); - opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r); - *(int*)arg = ret_vol_stereo(devc->volume_l, devc->volume_r); - return 0; - - case SOUND_MIXER_MIC: - arg_to_vol_mono(*(unsigned int*)arg, &devc->mic); - opl3sa2_set_mic(devc, devc->mic); - *(int*)arg = ret_vol_mono(devc->mic); - return 0; - - default: - return -EINVAL; - } - } - else { - /* - * Return parameters - */ - switch (cmdf) { - case SOUND_MIXER_DEVMASK: - *(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_MIC); - return 0; - - case SOUND_MIXER_STEREODEVS: - *(int*)arg = SOUND_MASK_VOLUME; - return 0; - - case SOUND_MIXER_RECMASK: - /* No recording devices */ - return (*(int*)arg = 0); - - case SOUND_MIXER_CAPS: - *(int*)arg = SOUND_CAP_EXCL_INPUT; - return 0; - - case SOUND_MIXER_RECSRC: - /* No recording source */ - return (*(int*)arg = 0); - - case SOUND_MIXER_VOLUME: - *(int*)arg = ret_vol_stereo(devc->volume_l, devc->volume_r); - return 0; - - case SOUND_MIXER_MIC: - *(int*)arg = ret_vol_mono(devc->mic); - return 0; - - default: - return -EINVAL; - } - } -} -/* opl3sa2_mixer_ioctl end */ - - -static int opl3sa3_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int cmdf = cmd & 0xff; - - opl3sa2_mixerdata* devc = (opl3sa2_mixerdata*) mixer_devs[dev]->devc; - - switch(cmdf) { - case SOUND_MIXER_BASS: - case SOUND_MIXER_TREBLE: - case SOUND_MIXER_DIGITAL1: - case SOUND_MIXER_DEVMASK: - case SOUND_MIXER_STEREODEVS: - break; - - default: - return opl3sa2_mixer_ioctl(dev, cmd, arg); - } - - if(((cmd >> 8) & 0xff) != 'M') - return -EINVAL; - - if(_SIOC_DIR (cmd) & _SIOC_WRITE) { - switch (cmdf) { - case SOUND_MIXER_BASS: - arg_to_vol_stereo(*(unsigned int*)arg, - &devc->bass_l, &devc->bass_r); - opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r); - *(int*)arg = ret_vol_stereo(devc->bass_l, devc->bass_r); - return 0; - - case SOUND_MIXER_TREBLE: - arg_to_vol_stereo(*(unsigned int*)arg, - &devc->treble_l, &devc->treble_r); - opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r); - *(int*)arg = ret_vol_stereo(devc->treble_l, devc->treble_r); - return 0; - - case SOUND_MIXER_DIGITAL1: - arg_to_vol_stereo(*(unsigned int*)arg, - &devc->wide_l, &devc->wide_r); - opl3sa3_set_wide(devc, devc->wide_l, devc->wide_r); - *(int*)arg = ret_vol_stereo(devc->wide_l, devc->wide_r); - return 0; - - default: - return -EINVAL; - } - } - else - { - /* - * Return parameters - */ - switch (cmdf) { - case SOUND_MIXER_DEVMASK: - *(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_MIC | - SOUND_MASK_BASS | SOUND_MASK_TREBLE | - SOUND_MASK_DIGITAL1); - return 0; - - case SOUND_MIXER_STEREODEVS: - *(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_BASS | - SOUND_MASK_TREBLE | SOUND_MASK_DIGITAL1); - return 0; - - case SOUND_MIXER_BASS: - *(int*)arg = ret_vol_stereo(devc->bass_l, devc->bass_r); - return 0; - - case SOUND_MIXER_TREBLE: - *(int*)arg = ret_vol_stereo(devc->treble_l, devc->treble_r); - return 0; - - case SOUND_MIXER_DIGITAL1: - *(int*)arg = ret_vol_stereo(devc->wide_l, devc->wide_r); - return 0; - - default: - return -EINVAL; - } - } -} -/* opl3sa3_mixer_ioctl end */ - - -static struct mixer_operations opl3sa2_mixer_operations = -{ - owner: THIS_MODULE, - id: "OPL3-SA2", - name: "Yamaha OPL3-SA2", - ioctl: opl3sa2_mixer_ioctl -}; - -static struct mixer_operations opl3sa3_mixer_operations = -{ - owner: THIS_MODULE, - id: "OPL3-SA3", - name: "Yamaha OPL3-SA3", - ioctl: opl3sa3_mixer_ioctl -}; - -/* End of mixer-related stuff */ - - -/* - * Component probe, attach, unload functions - */ - -static inline int __init probe_opl3sa2_mpu(struct address_info* hw_config) -{ - return probe_mpu401(hw_config); -} - - -static inline void __init attach_opl3sa2_mpu(struct address_info* hw_config) -{ - attach_mpu401(hw_config, THIS_MODULE); -} - - -static inline void __exit unload_opl3sa2_mpu(struct address_info *hw_config) -{ - unload_mpu401(hw_config); -} - - -static inline int __init probe_opl3sa2_mss(struct address_info* hw_config) -{ - return probe_ms_sound(hw_config); -} - - -static void __init attach_opl3sa2_mss(struct address_info* hw_config) -{ - int initial_mixers; - - initial_mixers = num_mixers; - attach_ms_sound(hw_config, THIS_MODULE); /* Slot 0 */ - if(hw_config->slots[0] != -1) { - /* Did the MSS driver install? */ - if(num_mixers == (initial_mixers + 1)) { - /* The MSS mixer is installed, reroute mixers appropiately */ - AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); - AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); - AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); - } - else { - printk(KERN_ERR "opl3sa2: MSS mixer not installed?\n"); - } - } -} - - -static inline void __exit unload_opl3sa2_mss(struct address_info* hw_config) -{ - unload_ms_sound(hw_config); -} - - -static int __init probe_opl3sa2(struct address_info* hw_config, int card) -{ - unsigned char misc; - unsigned char tmp; - unsigned char version; - char tag; - - /* - * Verify that the I/O port range is free. - */ - if(check_region(hw_config->io_base, 2)) { - printk(KERN_ERR "opl3sa2: Control I/O port %#x not free\n", - hw_config->io_base); - return 0; - } - - /* - * Check if writing to the read-only version bits of the miscellaneous - * register succeeds or not (it should not). - */ - opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &misc); - opl3sa2_write(hw_config->io_base, OPL3SA2_MISC, misc ^ 0x07); - opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &tmp); - if(tmp != misc) { - printk(KERN_ERR "opl3sa2: Control I/O port %#x is not a YMF7xx chipset!\n", - hw_config->io_base); - return 0; - } - - /* - * Check if the MIC register is accessible. - */ - opl3sa2_read(hw_config->io_base, OPL3SA2_MIC, &tmp); - opl3sa2_write(hw_config->io_base, OPL3SA2_MIC, 0x8a); - opl3sa2_read(hw_config->io_base, OPL3SA2_MIC, &tmp); - if((tmp & 0x9f) != 0x8a) { - printk(KERN_ERR - "opl3sa2: Control I/O port %#x is not a YMF7xx chipset!\n", - hw_config->io_base); - return 0; - } - opl3sa2_write(hw_config->io_base, OPL3SA2_MIC, tmp); - - /* - * Determine chipset type (SA2 or SA3) - * - * This is done by looking at the chipset version in the lower 3 bits - * of the miscellaneous register. - */ - version = misc & 0x07; - printk(KERN_DEBUG "opl3sa2: chipset version = %#x\n", version); - switch(version) { - case 0: - chipset[card] = CHIPSET_UNKNOWN; - tag = '?'; /* silence compiler warning */ - printk(KERN_ERR - "opl3sa2: Unknown Yamaha audio controller version\n"); - break; - - case VERSION_YMF711: - chipset[card] = CHIPSET_OPL3SA2; - tag = '2'; - printk(KERN_INFO "opl3sa2: Found OPL3-SA2 (YMF711)\n"); - break; - - case VERSION_YMF715: - chipset[card] = CHIPSET_OPL3SA3; - tag = '3'; - printk(KERN_INFO - "opl3sa2: Found OPL3-SA3 (YMF715 or YMF719)\n"); - break; - - case VERSION_YMF715B: - chipset[card] = CHIPSET_OPL3SA3; - tag = '3'; - printk(KERN_INFO - "opl3sa2: Found OPL3-SA3 (YMF715B or YMF719B)\n"); - break; - - case VERSION_YMF715E: - default: - chipset[card] = CHIPSET_OPL3SA3; - tag = '3'; - printk(KERN_INFO - "opl3sa2: Found OPL3-SA3 (YMF715E or YMF719E)\n"); - break; - } - - if(chipset[card] != CHIPSET_UNKNOWN) { - /* Generate a pretty name */ - sprintf(chipset_name[card], "OPL3-SA%c", tag); - return 1; - } - return 0; -} - - -static void __init attach_opl3sa2(struct address_info* hw_config, int card) -{ - request_region(hw_config->io_base, 2, chipset_name[card]); - - /* Initialize IRQ configuration to IRQ-B: -, IRQ-A: WSS+MPU+OPL3 */ - opl3sa2_write(hw_config->io_base, OPL3SA2_IRQ_CONFIG, 0x0d); - - /* Initialize DMA configuration */ - if(hw_config->dma2 == hw_config->dma) { - /* Want DMA configuration DMA-B: -, DMA-A: WSS-P+WSS-R */ - opl3sa2_write(hw_config->io_base, OPL3SA2_DMA_CONFIG, 0x03); - } - else { - /* Want DMA configuration DMA-B: WSS-R, DMA-A: WSS-P */ - opl3sa2_write(hw_config->io_base, OPL3SA2_DMA_CONFIG, 0x21); - } -} - - -static void __init attach_opl3sa2_mixer(struct address_info *hw_config, int card) -{ - struct mixer_operations* mixer_operations; - opl3sa2_mixerdata* devc; - - /* Install master mixer */ - if(chipset[card] == CHIPSET_OPL3SA3) { - mixer_operations = &opl3sa3_mixer_operations; - } - else { - mixer_operations = &opl3sa2_mixer_operations; - } - - if((devc = &opl3sa2_data[card])) { - devc->cfg_port = hw_config->io_base; - - opl3sa2_mixer[card] = sound_install_mixer(MIXER_DRIVER_VERSION, - mixer_operations->name, - mixer_operations, - sizeof(struct mixer_operations), - devc); - if(opl3sa2_mixer[card] < 0) { - printk(KERN_ERR "opl3sa2: Could not install %s master mixer\n", - mixer_operations->name); - } - else - opl3sa2_mixer_reset(devc, card); - } -} - - -static void __init opl3sa2_clear_slots(struct address_info* hw_config) -{ - int i; - - for(i = 0; i < 6; i++) { - hw_config->slots[i] = -1; - } -} - - -static void __init opl3sa2_set_ymode(struct address_info* hw_config, int ymode) -{ - /* - * Set the Yamaha 3D enhancement mode (aka Ymersion) if asked to and - * it's supported. - * - * 0: Desktop (aka normal) 5-12 cm speakers - * 1: Notebook PC mode 1 3 cm speakers - * 2: Notebook PC mode 2 1.5 cm speakers - * 3: Hi-fi 16-38 cm speakers - */ - if(ymode >= 0 && ymode <= 3) { - unsigned char sys_ctrl; - - opl3sa2_read(hw_config->io_base, OPL3SA2_SYS_CTRL, &sys_ctrl); - sys_ctrl = (sys_ctrl & 0xcf) | ((ymode & 3) << 4); - opl3sa2_write(hw_config->io_base, OPL3SA2_SYS_CTRL, sys_ctrl); - } - else { - printk(KERN_ERR "opl3sa2: not setting ymode, it must be one of 0,1,2,3\n"); - } -} - - -static void __init opl3sa2_set_loopback(struct address_info* hw_config, int loopback) -{ - if(loopback >= 0 && loopback <= 1) { - unsigned char misc; - - opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &misc); - misc = (misc & 0xef) | ((loopback & 1) << 4); - opl3sa2_write(hw_config->io_base, OPL3SA2_MISC, misc); - } - else { - printk(KERN_ERR "opl3sa2: not setting loopback, it must be either 0 or 1\n"); - } -} - - -static void __exit unload_opl3sa2(struct address_info* hw_config, int card) -{ - /* Release control ports */ - release_region(hw_config->io_base, 2); - - /* Unload mixer */ - if(opl3sa2_mixer[card] >= 0) - sound_unload_mixerdev(opl3sa2_mixer[card]); -} - - -#ifdef __ISAPNP__ - -struct isapnp_device_id isapnp_opl3sa2_list[] __initdata = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), - 0 }, - {0} -}; - -MODULE_DEVICE_TABLE(isapnp, isapnp_opl3sa2_list); - -static int __init opl3sa2_isapnp_probe(struct address_info* hw_cfg, - struct address_info* mss_cfg, - struct address_info* mpu_cfg, - int card) -{ - static struct pci_dev* dev; - int ret; - - /* Find and configure device */ - dev = isapnp_find_dev(NULL, - ISAPNP_VENDOR('Y','M','H'), - ISAPNP_FUNCTION(0x0021), - dev); - if(dev == NULL) { - return -ENODEV; - } - - /* - * If device is active, assume configured with /proc/isapnp - * and use anyway. Any other way to check this? - */ - ret = dev->prepare(dev); - if(ret && ret != -EBUSY) { - printk(KERN_ERR "opl3sa2: ISA PnP found device that could not be autoconfigured.\n"); - return -ENODEV; - } - if(ret == -EBUSY) { - opl3sa2_activated[card] = 1; - } - else { - if(dev->activate(dev) < 0) { - printk(KERN_WARNING "opl3sa2: ISA PnP activate failed\n"); - opl3sa2_activated[card] = 0; - return -ENODEV; - } - - printk(KERN_DEBUG - "opl3sa2: Activated ISA PnP card %d (active=%d)\n", - card, dev->active); - - } - - /* Our own config: */ - hw_cfg->io_base = dev->resource[4].start; - hw_cfg->irq = dev->irq_resource[0].start; - hw_cfg->dma = dev->dma_resource[0].start; - hw_cfg->dma2 = dev->dma_resource[1].start; - - /* The MSS config: */ - mss_cfg->io_base = dev->resource[1].start; - mss_cfg->irq = dev->irq_resource[0].start; - mss_cfg->dma = dev->dma_resource[0].start; - mss_cfg->dma2 = dev->dma_resource[1].start; - mss_cfg->card_subtype = 1; /* No IRQ or DMA setup */ - - mpu_cfg->io_base = dev->resource[3].start; - mpu_cfg->irq = dev->irq_resource[0].start; - mpu_cfg->dma = -1; - mpu_cfg->dma2 = -1; - mpu_cfg->always_detect = 1; /* It's there, so use shared IRQs */ - - /* Call me paranoid: */ - opl3sa2_clear_slots(hw_cfg); - opl3sa2_clear_slots(mss_cfg); - opl3sa2_clear_slots(mpu_cfg); - - opl3sa2_dev[card] = dev; - - return 0; -} -#endif /* __ISAPNP__ */ - -/* End of component functions */ - -/* Power Management support functions */ -static int opl3sa2_suspend(struct pm_dev *pdev, unsigned char pm_mode) -{ - unsigned long flags; - opl3sa2_mixerdata *p; - - if (!pdev) - return -EINVAL; - - save_flags(flags); - cli(); - - p = (opl3sa2_mixerdata *) pdev->data; - p->in_suspend = 1; - switch (pm_mode) { - case 1: - pm_mode = OPL3SA2_PM_MODE1; - break; - case 2: - pm_mode = OPL3SA2_PM_MODE2; - break; - case 3: - pm_mode = OPL3SA2_PM_MODE3; - break; - default: - pm_mode = OPL3SA2_PM_MODE3; - break; - } - - /* its supposed to automute before suspending, so we wont bother */ - opl3sa2_read(p->cfg_port, OPL3SA2_PM, &p->reg); - opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->reg | pm_mode); - - restore_flags(flags); - return 0; -} - -static int opl3sa2_resume(struct pm_dev *pdev) -{ - unsigned long flags; - opl3sa2_mixerdata *p; - - if (!pdev) - return -EINVAL; - - p = (opl3sa2_mixerdata *) pdev->data; - save_flags(flags); - cli(); - - /* I don't think this is necessary */ - opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->reg); - opl3sa2_mixer_restore(p, p->card); - p->in_suspend = 0; - - restore_flags(flags); - return 0; -} - -static int opl3sa2_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data) -{ - unsigned char mode = (unsigned char)data; - - switch (rqst) { - case PM_SUSPEND: - return opl3sa2_suspend(pdev, mode); - - case PM_RESUME: - return opl3sa2_resume(pdev); - } - return 0; -} - -/* - * Install OPL3-SA2 based card(s). - * - * Need to have ad1848 and mpu401 loaded ready. - */ -static int __init init_opl3sa2(void) -{ - int card; - int max; - - /* Sanitize isapnp and multiple settings */ - isapnp = isapnp != 0 ? 1 : 0; - multiple = multiple != 0 ? 1 : 0; - - max = (multiple && isapnp) ? OPL3SA2_CARDS_MAX : 1; - for(card = 0; card < max; card++, opl3sa2_cards_num++) { -#ifdef __ISAPNP__ - /* - * Please remember that even with __ISAPNP__ defined one - * should still be able to disable PNP support for this - * single driver! - */ - if(isapnp && opl3sa2_isapnp_probe(&cfg[card], - &cfg_mss[card], - &cfg_mpu[card], - card) < 0) { - if(!opl3sa2_cards_num) - printk(KERN_INFO "opl3sa2: No PnP cards found\n"); - if(io == -1) - break; - isapnp=0; - printk(KERN_INFO "opl3sa2: Search for a card at 0x%d.\n", io); - /* Fall through */ - } -#endif - /* If a user wants an I/O then assume they meant it */ - - if(!isapnp) { - if(io == -1 || irq == -1 || dma == -1 || - dma2 == -1 || mss_io == -1) { - printk(KERN_ERR - "opl3sa2: io, mss_io, irq, dma, and dma2 must be set\n"); - return -EINVAL; - } - - /* - * Our own config: - * (NOTE: IRQ and DMA aren't used, so they're set to - * give pretty output from conf_printf. :) - */ - cfg[card].io_base = io; - cfg[card].irq = irq; - cfg[card].dma = dma; - cfg[card].dma2 = dma2; - - /* The MSS config: */ - cfg_mss[card].io_base = mss_io; - cfg_mss[card].irq = irq; - cfg_mss[card].dma = dma; - cfg_mss[card].dma2 = dma2; - cfg_mss[card].card_subtype = 1; /* No IRQ or DMA setup */ - - cfg_mpu[card].io_base = mpu_io; - cfg_mpu[card].irq = irq; - cfg_mpu[card].dma = -1; - cfg_mpu[card].always_detect = 1; /* Use shared IRQs */ - - /* Call me paranoid: */ - opl3sa2_clear_slots(&cfg[card]); - opl3sa2_clear_slots(&cfg_mss[card]); - opl3sa2_clear_slots(&cfg_mpu[card]); - } - - if(!probe_opl3sa2(&cfg[card], card) || - !probe_opl3sa2_mss(&cfg_mss[card])) { - /* - * If one or more cards are already registered, don't - * return an error but print a warning. Note, this - * should never really happen unless the hardware or - * ISA PnP screwed up. - */ - if(opl3sa2_cards_num) { - printk(KERN_WARNING - "opl3sa2: There was a problem probing one " - " of the ISA PNP cards, continuing\n"); - opl3sa2_cards_num--; - continue; - } else - return -ENODEV; - } - - attach_opl3sa2(&cfg[card], card); - conf_printf(chipset_name[card], &cfg[card]); - attach_opl3sa2_mss(&cfg_mss[card]); - attach_opl3sa2_mixer(&cfg[card], card); - - opl3sa2_data[card].card = card; - /* register our power management capabilities */ - opl3sa2_data[card].pmdev = pm_register(PM_ISA_DEV, card, opl3sa2_pm_callback); - if (opl3sa2_data[card].pmdev) - opl3sa2_data[card].pmdev->data = &opl3sa2_data[card]; - - /* - * Set the Yamaha 3D enhancement mode (aka Ymersion) if asked to and - * it's supported. - */ - if(ymode != -1) { - if(chipset[card] == CHIPSET_OPL3SA2) { - printk(KERN_ERR - "opl3sa2: ymode not supported on OPL3-SA2\n"); - } - else { - opl3sa2_set_ymode(&cfg[card], ymode); - } - } - - - /* Set A/D input to Mono loopback if asked to. */ - if(loopback != -1) { - opl3sa2_set_loopback(&cfg[card], loopback); - } - - /* Attach MPU if we've been asked to do so */ - if(cfg_mpu[card].io_base != -1) { - if(probe_opl3sa2_mpu(&cfg_mpu[card])) { - attach_opl3sa2_mpu(&cfg_mpu[card]); - } - } - } - - if(isapnp) { - printk(KERN_NOTICE "opl3sa2: %d PnP card(s) found.\n", opl3sa2_cards_num); - } - - return 0; -} - - -/* - * Uninstall OPL3-SA2 based card(s). - */ -static void __exit cleanup_opl3sa2(void) -{ - int card; - - for(card = 0; card < opl3sa2_cards_num; card++) { - if (opl3sa2_data[card].pmdev) - pm_unregister(opl3sa2_data[card].pmdev); - - if(cfg_mpu[card].slots[1] != -1) { - unload_opl3sa2_mpu(&cfg_mpu[card]); - } - unload_opl3sa2_mss(&cfg_mss[card]); - unload_opl3sa2(&cfg[card], card); - -#ifdef __ISAPNP__ - if(opl3sa2_activated[card] && opl3sa2_dev[card]) { - opl3sa2_dev[card]->deactivate(opl3sa2_dev[card]); - - printk(KERN_DEBUG - "opl3sa2: Deactivated ISA PnP card %d (active=%d)\n", - card, opl3sa2_dev[card]->active); - } -#endif - } -} - -module_init(init_opl3sa2); -module_exit(cleanup_opl3sa2); - -#ifndef MODULE -static int __init setup_opl3sa2(char *str) -{ - /* io, irq, dma, dma2,... */ -#ifdef __ISAPNP__ - int ints[11]; -#else - int ints[9]; -#endif - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - mss_io = ints[5]; - mpu_io = ints[6]; - ymode = ints[7]; - loopback = ints[8]; -#ifdef __ISAPNP__ - isapnp = ints[9]; - multiple = ints[10]; -#endif - return 1; -} - -__setup("opl3sa2=", setup_opl3sa2); -#endif diff -Nru a/drivers/sound/os.h b/drivers/sound/os.h --- a/drivers/sound/os.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,57 +0,0 @@ -#define ALLOW_SELECT -#undef NO_INLINE_ASM -#define SHORT_BANNERS -#define MANUAL_PNP -#undef DO_TIMINGS - -#include -#include - -#if LINUX_VERSION_CODE > 131328 -#define LINUX21X -#endif - -#ifdef __KERNEL__ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __alpha__ -#include -#endif -#include -#include -#include -#include -#endif - -#include -#include - -#define FALSE 0 -#define TRUE 1 - -extern int sound_alloc_dma(int chn, char *deviceID); -extern int sound_open_dma(int chn, char *deviceID); -extern void sound_free_dma(int chn); -extern void sound_close_dma(int chn); - -extern void reprogram_timer(void); - -#define USE_AUTOINIT_DMA - -extern caddr_t sound_mem_blocks[1024]; -extern int sound_nblocks; - -#undef PSEUDO_DMA_AUTOINIT -#define ALLOW_BUFFER_MAPPING - -extern struct file_operations oss_sound_fops; diff -Nru a/drivers/sound/pas2.h b/drivers/sound/pas2.h --- a/drivers/sound/pas2.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,23 +0,0 @@ -/* - * pas2.h - * - * Copyright: Christoph Hellwig - * - */ - -/* From pas_card.c */ -int pas_set_intr(int mask); -int pas_remove_intr(int mask); -unsigned char pas_read(int ioaddr); -void pas_write(unsigned char data, int ioaddr); - -/* From pas_audio.c */ -void pas_pcm_interrupt(unsigned char status, int cause); -void pas_pcm_init(struct address_info *hw_config); - -/* From pas_mixer.c */ -int pas_init_mixer(void); - -/* From pas_midi.c */ -void pas_midi_init(void); -void pas_midi_interrupt(void); diff -Nru a/drivers/sound/pas2_card.c b/drivers/sound/pas2_card.c --- a/drivers/sound/pas2_card.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,464 +0,0 @@ -/* - * sound/pas2_card.c - * - * Detection routine for the Pro Audio Spectrum cards. - */ - -#include -#include -#include -#include "sound_config.h" - -#include "pas2.h" -#include "sb.h" - -static unsigned char dma_bits[] = { - 4, 1, 2, 3, 0, 5, 6, 7 -}; - -static unsigned char irq_bits[] = { - 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 -}; - -static unsigned char sb_irq_bits[] = { - 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, - 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 -}; - -static unsigned char sb_dma_bits[] = { - 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 -}; - -/* - * The Address Translation code is used to convert I/O register addresses to - * be relative to the given base -register - */ - -int translate_code = 0; -static int pas_intr_mask = 0; -static int pas_irq = 0; -static int pas_sb_base = 0; -#ifndef CONFIG_PAS_JOYSTICK -static int joystick = 0; -#else -static int joystick = 1; -#endif -#ifdef SYMPHONY_PAS -static int symphony = 1; -#else -static int symphony = 0; -#endif -#ifdef BROKEN_BUS_CLOCK -static int broken_bus_clock = 1; -#else -static int broken_bus_clock = 0; -#endif - -static struct address_info cfg; -static struct address_info cfg2; - -char pas_model = 0; -static char *pas_model_names[] = { - "", - "Pro AudioSpectrum+", - "CDPC", - "Pro AudioSpectrum 16", - "Pro AudioSpectrum 16D" -}; - -/* - * pas_read() and pas_write() are equivalents of inb and outb - * These routines perform the I/O address translation required - * to support other than the default base address - */ - -extern void mix_write(unsigned char data, int ioaddr); - -unsigned char pas_read(int ioaddr) -{ - return inb(ioaddr + translate_code); -} - -void pas_write(unsigned char data, int ioaddr) -{ - outb((data), ioaddr + translate_code); -} - -/******************* Begin of the Interrupt Handler ********************/ - -void pasintr(int irq, void *dev_id, struct pt_regs *dummy) -{ - int status; - - status = pas_read(0x0B89); - pas_write(status, 0x0B89); /* Clear interrupt */ - - if (status & 0x08) - { - pas_pcm_interrupt(status, 1); - status &= ~0x08; - } - if (status & 0x10) - { - pas_midi_interrupt(); - status &= ~0x10; - } -} - -int pas_set_intr(int mask) -{ - if (!mask) - return 0; - - pas_intr_mask |= mask; - - pas_write(pas_intr_mask, 0x0B8B); - return 0; -} - -int pas_remove_intr(int mask) -{ - if (!mask) - return 0; - - pas_intr_mask &= ~mask; - pas_write(pas_intr_mask, 0x0B8B); - - return 0; -} - -/******************* End of the Interrupt handler **********************/ - -/******************* Begin of the Initialization Code ******************/ - -static int __init config_pas_hw(struct address_info *hw_config) -{ - char ok = 1; - unsigned int_ptrs; /* scsi/sound interrupt pointers */ - - pas_irq = hw_config->irq; - - pas_write(0x00, 0x0B8B); - pas_write(0x36, 0x138B); - pas_write(0x36, 0x1388); - pas_write(0, 0x1388); - pas_write(0x74, 0x138B); - pas_write(0x74, 0x1389); - pas_write(0, 0x1389); - - pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A); - pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A); - pas_write(0x01 | 0x02 | 0x04 | 0x10 /* - * | - * 0x80 - */ , 0xB88); - - pas_write(0x80 - | joystick?0x40:0 - ,0xF388); - - if (pas_irq < 0 || pas_irq > 15) - { - printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); - hw_config->irq=-1; - ok = 0; - } - else - { - int_ptrs = pas_read(0xF38A); - int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq]; - pas_write(int_ptrs, 0xF38A); - if (!irq_bits[pas_irq]) - { - printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); - hw_config->irq=-1; - ok = 0; - } - else - { - if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) { - printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq); - hw_config->irq=-1; - ok = 0; - } - } - } - - if (hw_config->dma < 0 || hw_config->dma > 7) - { - printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); - hw_config->dma=-1; - ok = 0; - } - else - { - pas_write(dma_bits[hw_config->dma], 0xF389); - if (!dma_bits[hw_config->dma]) - { - printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); - hw_config->dma=-1; - ok = 0; - } - else - { - if (sound_alloc_dma(hw_config->dma, "PAS16")) - { - printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n"); - hw_config->dma=-1; - ok = 0; - } - } - } - - /* - * This fixes the timing problems of the PAS due to the Symphony chipset - * as per Media Vision. Only define this if your PAS doesn't work correctly. - */ - - if(symphony) - { - outb((0x05), 0xa8); - outb((0x60), 0xa9); - } - - if(broken_bus_clock) - pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388); - else - /* - * pas_write(0x01, 0x8388); - */ - pas_write(0x01 | 0x10 | 0x20, 0x8388); - - pas_write(0x18, 0x838A); /* ??? */ - pas_write(0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */ - pas_write(8, 0xBF8A); - - mix_write(0x80 | 5, 0x078B); - mix_write(5, 0x078B); - -#if !defined(DISABLE_SB_EMULATION) - - { - struct address_info *sb_config; - - sb_config = &cfg2; - if (sb_config->io_base) - { - unsigned char irq_dma; - - /* - * Turn on Sound Blaster compatibility - * bit 1 = SB emulation - * bit 0 = MPU401 emulation (CDPC only :-( ) - */ - - pas_write(0x02, 0xF788); - - /* - * "Emulation address" - */ - - pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789); - pas_sb_base = sb_config->io_base; - - if (!sb_dma_bits[sb_config->dma]) - printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma); - - if (!sb_irq_bits[sb_config->irq]) - printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq); - - irq_dma = sb_dma_bits[sb_config->dma] | - sb_irq_bits[sb_config->irq]; - - pas_write(irq_dma, 0xFB8A); - } - else - pas_write(0x00, 0xF788); - } -#else - pas_write(0x00, 0xF788); -#endif - - if (!ok) - printk(KERN_WARNING "PAS16: Driver not enabled\n"); - - return ok; -} - -static int __init detect_pas_hw(struct address_info *hw_config) -{ - unsigned char board_id, foo; - - /* - * WARNING: Setting an option like W:1 or so that disables warm boot reset - * of the card will screw up this detect code something fierce. Adding code - * to handle this means possibly interfering with other cards on the bus if - * you have something on base port 0x388. SO be forewarned. - */ - - outb((0xBC), 0x9A01); /* Activate first board */ - outb((hw_config->io_base >> 2), 0x9A01); /* Set base address */ - translate_code = hw_config->io_base - 0x388; - pas_write(1, 0xBF88); /* Select one wait states */ - - board_id = pas_read(0x0B8B); - - if (board_id == 0xff) - return 0; - - /* - * We probably have a PAS-series board, now check for a PAS16-series board - * by trying to change the board revision bits. PAS16-series hardware won't - * let you do this - the bits are read-only. - */ - - foo = board_id ^ 0xe0; - - pas_write(foo, 0x0B8B); - foo = pas_read(0x0B8B); - pas_write(board_id, 0x0B8B); - - if (board_id != foo) - return 0; - - pas_model = pas_read(0xFF88); - - return pas_model; -} - -static void __init attach_pas_card(struct address_info *hw_config) -{ - pas_irq = hw_config->irq; - - if (detect_pas_hw(hw_config)) - { - - if ((pas_model = pas_read(0xFF88))) - { - char temp[100]; - - sprintf(temp, - "%s rev %d", pas_model_names[(int) pas_model], - pas_read(0x2789)); - conf_printf(temp, hw_config); - } - if (config_pas_hw(hw_config)) - { - pas_pcm_init(hw_config); - -#if !defined(MODULE) && !defined(DISABLE_SB_EMULATION) - sb_dsp_disable_midi(pas_sb_base); /* No MIDI capability */ -#endif - - pas_midi_init(); - pas_init_mixer(); - } - } -} - -static inline int __init probe_pas(struct address_info *hw_config) -{ - return detect_pas_hw(hw_config); -} - -static void __exit unload_pas(struct address_info *hw_config) -{ - extern int pas_audiodev; - extern int pas2_mididev; - - if (hw_config->dma>0) - sound_free_dma(hw_config->dma); - if (hw_config->irq>0) - free_irq(hw_config->irq, hw_config); - - if(pas_audiodev!=-1) - sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev); - if(pas2_mididev!=-1) - sound_unload_mididev(pas2_mididev); - if(pas_audiodev!=-1) - sound_unload_audiodev(pas_audiodev); -} - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma16 = -1; /* Set this for modules that need it */ - -static int __initdata sb_io = 0; -static int __initdata sb_irq = -1; -static int __initdata sb_dma = -1; -static int __initdata sb_dma16 = -1; - -MODULE_PARM(io,"i"); -MODULE_PARM(irq,"i"); -MODULE_PARM(dma,"i"); -MODULE_PARM(dma16,"i"); - -MODULE_PARM(sb_io,"i"); -MODULE_PARM(sb_irq,"i"); -MODULE_PARM(sb_dma,"i"); -MODULE_PARM(sb_dma16,"i"); - -MODULE_PARM(joystick,"i"); -MODULE_PARM(symphony,"i"); -MODULE_PARM(broken_bus_clock,"i"); - -MODULE_LICENSE("GPL"); - -static int __init init_pas2(void) -{ - printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma16; - - cfg2.io_base = sb_io; - cfg2.irq = sb_irq; - cfg2.dma = sb_dma; - cfg2.dma2 = sb_dma16; - - if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { - printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); - return -EINVAL; - } - - if (!probe_pas(&cfg)) - return -ENODEV; - attach_pas_card(&cfg); - - return 0; -} - -static void __exit cleanup_pas2(void) -{ - unload_pas(&cfg); -} - -module_init(init_pas2); -module_exit(cleanup_pas2); - -#ifndef MODULE -static int __init setup_pas2(char *str) -{ - /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, sb_dma2 */ - int ints[9]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma16 = ints[4]; - - sb_io = ints[5]; - sb_irq = ints[6]; - sb_dma = ints[7]; - sb_dma16 = ints[8]; - - return 1; -} - -__setup("pas2=", setup_pas2); -#endif diff -Nru a/drivers/sound/pas2_midi.c b/drivers/sound/pas2_midi.c --- a/drivers/sound/pas2_midi.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,264 +0,0 @@ -/* - * sound/pas2_midi.c - * - * The low level driver for the PAS Midi Interface. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Bartlomiej Zolnierkiewicz : Added __init to pas_init_mixer() - */ - -#include -#include "sound_config.h" - -#include "pas2.h" - -static int midi_busy = 0, input_opened = 0; -static int my_dev; - -int pas2_mididev=-1; - -static unsigned char tmp_queue[256]; -static volatile int qlen; -static volatile unsigned char qhead, qtail; - -static void (*midi_input_intr) (int dev, unsigned char data); - -static int pas_midi_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - int err; - unsigned long flags; - unsigned char ctrl; - - - if (midi_busy) - return -EBUSY; - - /* - * Reset input and output FIFO pointers - */ - pas_write(0x20 | 0x40, - 0x178b); - - save_flags(flags); - cli(); - - if ((err = pas_set_intr(0x10)) < 0) - { - restore_flags(flags); - return err; - } - /* - * Enable input available and output FIFO empty interrupts - */ - - ctrl = 0; - input_opened = 0; - midi_input_intr = input; - - if (mode == OPEN_READ || mode == OPEN_READWRITE) - { - ctrl |= 0x04; /* Enable input */ - input_opened = 1; - } - if (mode == OPEN_WRITE || mode == OPEN_READWRITE) - { - ctrl |= 0x08 | 0x10; /* Enable output */ - } - pas_write(ctrl, 0x178b); - - /* - * Acknowledge any pending interrupts - */ - - pas_write(0xff, 0x1B88); - - restore_flags(flags); - - midi_busy = 1; - qlen = qhead = qtail = 0; - return 0; -} - -static void pas_midi_close(int dev) -{ - - /* - * Reset FIFO pointers, disable intrs - */ - pas_write(0x20 | 0x40, 0x178b); - - pas_remove_intr(0x10); - midi_busy = 0; -} - -static int dump_to_midi(unsigned char midi_byte) -{ - int fifo_space, x; - - fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f; - - /* - * The MIDI FIFO space register and it's documentation is nonunderstandable. - * There seem to be no way to differentiate between buffer full and buffer - * empty situations. For this reason we don't never write the buffer - * completely full. In this way we can assume that 0 (or is it 15) - * means that the buffer is empty. - */ - - if (fifo_space < 2 && fifo_space != 0) /* Full (almost) */ - return 0; /* Ask upper layers to retry after some time */ - - pas_write(midi_byte, 0x178A); - - return 1; -} - -static int pas_midi_out(int dev, unsigned char midi_byte) -{ - - unsigned long flags; - - /* - * Drain the local queue first - */ - - save_flags(flags); - cli(); - - while (qlen && dump_to_midi(tmp_queue[qhead])) - { - qlen--; - qhead++; - } - - restore_flags(flags); - - /* - * Output the byte if the local queue is empty. - */ - - if (!qlen) - if (dump_to_midi(midi_byte)) - return 1; - - /* - * Put to the local queue - */ - - if (qlen >= 256) - return 0; /* Local queue full */ - - save_flags(flags); - cli(); - - tmp_queue[qtail] = midi_byte; - qlen++; - qtail++; - - restore_flags(flags); - - return 1; -} - -static int pas_midi_start_read(int dev) -{ - return 0; -} - -static int pas_midi_end_read(int dev) -{ - return 0; -} - -static void pas_midi_kick(int dev) -{ -} - -static int pas_buffer_status(int dev) -{ - return qlen; -} - -#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static struct midi_operations pas_midi_operations = -{ - owner: THIS_MODULE, - info: {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS}, - converter: &std_midi_synth, - in_info: {0}, - open: pas_midi_open, - close: pas_midi_close, - outputc: pas_midi_out, - start_read: pas_midi_start_read, - end_read: pas_midi_end_read, - kick: pas_midi_kick, - buffer_status: pas_buffer_status, -}; - -void __init pas_midi_init(void) -{ - int dev = sound_alloc_mididev(); - - if (dev == -1) - { - printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n"); - return; - } - std_midi_synth.midi_dev = my_dev = dev; - midi_devs[dev] = &pas_midi_operations; - pas2_mididev = dev; - sequencer_init(); -} - -void pas_midi_interrupt(void) -{ - unsigned char stat; - int i, incount; - unsigned long flags; - - stat = pas_read(0x1B88); - - if (stat & 0x04) /* Input data available */ - { - incount = pas_read(0x1B89) & 0x0f; /* Input FIFO size */ - if (!incount) - incount = 16; - - for (i = 0; i < incount; i++) - if (input_opened) - { - midi_input_intr(my_dev, pas_read(0x178A)); - } else - pas_read(0x178A); /* Flush */ - } - if (stat & (0x08 | 0x10)) - { - save_flags(flags); - cli(); - - while (qlen && dump_to_midi(tmp_queue[qhead])) - { - qlen--; - qhead++; - } - - restore_flags(flags); - } - if (stat & 0x40) - { - printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat); - } - pas_write(stat, 0x1B88); /* Acknowledge interrupts */ -} diff -Nru a/drivers/sound/pas2_mixer.c b/drivers/sound/pas2_mixer.c --- a/drivers/sound/pas2_mixer.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,335 +0,0 @@ - -/* - * sound/pas2_mixer.c - * - * Mixer routines for the Pro Audio Spectrum cards. - */ - -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Bartlomiej Zolnierkiewicz : added __init to pas_init_mixer() - */ -#include -#include "sound_config.h" - -#include "pas2.h" - -#ifndef DEB -#define DEB(what) /* (what) */ -#endif - -extern int translate_code; -extern char pas_model; -extern int *pas_osp; -extern int pas_audiodev; - -static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */ -static int mode_control = 0; - -#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_ALTPCM) - -#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \ - SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV) - -static int *levels; - -static int default_levels[32] = -{ - 0x3232, /* Master Volume */ - 0x3232, /* Bass */ - 0x3232, /* Treble */ - 0x5050, /* FM */ - 0x4b4b, /* PCM */ - 0x3232, /* PC Speaker */ - 0x4b4b, /* Ext Line */ - 0x4b4b, /* Mic */ - 0x4b4b, /* CD */ - 0x6464, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x6464 /* Recording level */ -}; - -void -mix_write(unsigned char data, int ioaddr) -{ - /* - * The Revision D cards have a problem with their MVA508 interface. The - * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and - * MSBs out of the output byte and to do a 16-bit out to the mixer port - - * 1. We need to do this because it isn't timing problem but chip access - * sequence problem. - */ - - if (pas_model == 4) - { - outw(data | (data << 8), (ioaddr + translate_code) - 1); - outb((0x80), 0); - } else - pas_write(data, ioaddr); -} - -static int -mixer_output(int right_vol, int left_vol, int div, int bits, - int mixer) /* Input or output mixer */ -{ - int left = left_vol * div / 100; - int right = right_vol * div / 100; - - - if (bits & 0x10) - { - left |= mixer; - right |= mixer; - } - if (bits == 0x03 || bits == 0x04) - { - mix_write(0x80 | bits, 0x078B); - mix_write(left, 0x078B); - right_vol = left_vol; - } else - { - mix_write(0x80 | 0x20 | bits, 0x078B); - mix_write(left, 0x078B); - mix_write(0x80 | 0x40 | bits, 0x078B); - mix_write(right, 0x078B); - } - - return (left_vol | (right_vol << 8)); -} - -static void -set_mode(int new_mode) -{ - mix_write(0x80 | 0x05, 0x078B); - mix_write(new_mode, 0x078B); - - mode_control = new_mode; -} - -static int -pas_mixer_set(int whichDev, unsigned int level) -{ - int left, right, devmask, changed, i, mixer = 0; - - DEB(printk("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level)); - - left = level & 0x7f; - right = (level & 0x7f00) >> 8; - - if (whichDev < SOUND_MIXER_NRDEVICES) { - if ((1 << whichDev) & rec_devices) - mixer = 0x20; - else - mixer = 0x00; - } - - switch (whichDev) - { - case SOUND_MIXER_VOLUME: /* Master volume (0-63) */ - levels[whichDev] = mixer_output(right, left, 63, 0x01, 0); - break; - - /* - * Note! Bass and Treble are mono devices. Will use just the left - * channel. - */ - case SOUND_MIXER_BASS: /* Bass (0-12) */ - levels[whichDev] = mixer_output(right, left, 12, 0x03, 0); - break; - case SOUND_MIXER_TREBLE: /* Treble (0-12) */ - levels[whichDev] = mixer_output(right, left, 12, 0x04, 0); - break; - - case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x00, mixer); - break; - case SOUND_MIXER_PCM: /* PAS PCM (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x05, mixer); - break; - case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x07, mixer); - break; - case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x06, mixer); - break; - case SOUND_MIXER_LINE: /* External line (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x02, mixer); - break; - case SOUND_MIXER_CD: /* CD (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x03, mixer); - break; - case SOUND_MIXER_MIC: /* External microphone (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x04, mixer); - break; - case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x01, - 0x00); - break; - case SOUND_MIXER_RECLEV: /* Recording level (0-15) */ - levels[whichDev] = mixer_output(right, left, 15, 0x02, 0); - break; - - - case SOUND_MIXER_RECSRC: - devmask = level & POSSIBLE_RECORDING_DEVICES; - - changed = devmask ^ rec_devices; - rec_devices = devmask; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (changed & (1 << i)) - { - pas_mixer_set(i, levels[i]); - } - return rec_devices; - break; - - default: - return -EINVAL; - } - - return (levels[whichDev]); -} - -/*****/ - -static void -pas_mixer_reset(void) -{ - int foo; - - DEB(printk("pas2_mixer.c: void pas_mixer_reset(void)\n")); - - for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) - pas_mixer_set(foo, levels[foo]); - - set_mode(0x04 | 0x01); -} - -static int pas_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int level,v ; - - DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); - if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */ - if (get_user(level, (int *)arg)) - return -EFAULT; - if (level == -1) /* Return current settings */ - level = (mode_control & 0x04); - else { - mode_control &= ~0x04; - if (level) - mode_control |= 0x04; - set_mode(mode_control); - } - level = !!level; - return put_user(level, (int *)arg); - } - if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */ - if (get_user(level, (int *)arg)) - return -EFAULT; - if (level == -1) { /* Return current settings */ - if (!(mode_control & 0x03)) - level = 0; - else - level = ((mode_control & 0x03) + 1) * 20; - } else { - int i = 0; - - level &= 0x7f; - if (level) - i = (level / 20) - 1; - mode_control &= ~0x03; - mode_control |= i & 0x03; - set_mode(mode_control); - if (i) - i = (i + 1) * 20; - level = i; - } - return put_user(level, (int *)arg); - } - if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */ - if (get_user(level, (int *)arg)) - return -EFAULT; - if (level == -1) /* Return current settings */ - level = !(pas_read(0x0B8A) & 0x20); - else { - if (level) - pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A); - else - pas_write(pas_read(0x0B8A) | 0x20, 0x0B8A); - - level = !(pas_read(0x0B8A) & 0x20); - } - return put_user(level, (int *)arg); - } - if (((cmd >> 8) & 0xff) == 'M') { - if (get_user(v, (int *)arg)) - return -EFAULT; - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - v = pas_mixer_set(cmd & 0xff, v); - } else { - switch (cmd & 0xff) { - case SOUND_MIXER_RECSRC: - v = rec_devices; - break; - - case SOUND_MIXER_STEREODEVS: - v = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE); - break; - - case SOUND_MIXER_DEVMASK: - v = SUPPORTED_MIXER_DEVICES; - break; - - case SOUND_MIXER_RECMASK: - v = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES; - break; - - case SOUND_MIXER_CAPS: - v = 0; /* No special capabilities */ - break; - - default: - v = levels[cmd & 0xff]; - break; - } - } - return put_user(v, (int *)arg); - } - return -EINVAL; -} - -static struct mixer_operations pas_mixer_operations = -{ - owner: THIS_MODULE, - id: "PAS16", - name: "Pro Audio Spectrum 16", - ioctl: pas_mixer_ioctl -}; - -int __init -pas_init_mixer(void) -{ - int d; - - levels = load_mixer_volumes("PAS16_1", default_levels, 1); - - pas_mixer_reset(); - - if ((d = sound_alloc_mixerdev()) != -1) - { - audio_devs[pas_audiodev]->mixer_dev = d; - mixer_devs[d] = &pas_mixer_operations; - } - return 1; -} diff -Nru a/drivers/sound/pas2_pcm.c b/drivers/sound/pas2_pcm.c --- a/drivers/sound/pas2_pcm.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,438 +0,0 @@ -/* - * pas2_pcm.c Audio routines for PAS16 - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Alan Cox : Swatted a double allocation of device bug. Made a few - * more things module options. - * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init() - */ - -#include -#include "sound_config.h" - -#include "pas2.h" - -#ifndef DEB -#define DEB(WHAT) -#endif - -#define PAS_PCM_INTRBITS (0x08) -/* - * Sample buffer timer interrupt enable - */ - -#define PCM_NON 0 -#define PCM_DAC 1 -#define PCM_ADC 2 - -static unsigned long pcm_speed = 0; /* sampling rate */ -static unsigned char pcm_channels = 1; /* channels (1 or 2) */ -static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */ -static unsigned char pcm_filter = 0; /* filter FLAG */ -static unsigned char pcm_mode = PCM_NON; -static unsigned long pcm_count = 0; -static unsigned short pcm_bitsok = 8; /* mask of OK bits */ -static int pcm_busy = 0; -int pas_audiodev = -1; -static int open_mode = 0; - -static int pcm_set_speed(int arg) -{ - int foo, tmp; - unsigned long flags; - - if (arg == 0) - return pcm_speed; - - if (arg > 44100) - arg = 44100; - if (arg < 5000) - arg = 5000; - - if (pcm_channels & 2) - { - foo = (596590 + (arg / 2)) / arg; - arg = (596590 + (foo / 2)) / foo; - } - else - { - foo = (1193180 + (arg / 2)) / arg; - arg = (1193180 + (foo / 2)) / foo; - } - - pcm_speed = arg; - - tmp = pas_read(0x0B8A); - - /* - * Set anti-aliasing filters according to sample rate. You really *NEED* - * to enable this feature for all normal recording unless you want to - * experiment with aliasing effects. - * These filters apply to the selected "recording" source. - * I (pfw) don't know the encoding of these 5 bits. The values shown - * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/. - * - * I cleared bit 5 of these values, since that bit controls the master - * mute flag. (Olav Wölfelschneider) - * - */ -#if !defined NO_AUTO_FILTER_SET - tmp &= 0xe0; - if (pcm_speed >= 2 * 17897) - tmp |= 0x01; - else if (pcm_speed >= 2 * 15909) - tmp |= 0x02; - else if (pcm_speed >= 2 * 11931) - tmp |= 0x09; - else if (pcm_speed >= 2 * 8948) - tmp |= 0x11; - else if (pcm_speed >= 2 * 5965) - tmp |= 0x19; - else if (pcm_speed >= 2 * 2982) - tmp |= 0x04; - pcm_filter = tmp; -#endif - - save_flags(flags); - cli(); - - pas_write(tmp & ~(0x40 | 0x80), 0x0B8A); - pas_write(0x00 | 0x30 | 0x04, 0x138B); - pas_write(foo & 0xff, 0x1388); - pas_write((foo >> 8) & 0xff, 0x1388); - pas_write(tmp, 0x0B8A); - - restore_flags(flags); - - return pcm_speed; -} - -static int pcm_set_channels(int arg) -{ - - if ((arg != 1) && (arg != 2)) - return pcm_channels; - - if (arg != pcm_channels) - { - pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A); - - pcm_channels = arg; - pcm_set_speed(pcm_speed); /* The speed must be reinitialized */ - } - return pcm_channels; -} - -static int pcm_set_bits(int arg) -{ - if (arg == 0) - return pcm_bits; - - if ((arg & pcm_bitsok) != arg) - return pcm_bits; - - if (arg != pcm_bits) - { - pas_write(pas_read(0x8389) ^ 0x04, 0x8389); - - pcm_bits = arg; - } - return pcm_bits; -} - -static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int val, ret; - - DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); - - switch (cmd) - { - case SOUND_PCM_WRITE_RATE: - if (get_user(val, (int *)arg)) - return -EFAULT; - ret = pcm_set_speed(val); - break; - - case SOUND_PCM_READ_RATE: - ret = pcm_speed; - break; - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - ret = pcm_set_channels(val + 1) - 1; - break; - - case SOUND_PCM_WRITE_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - ret = pcm_set_channels(val); - break; - - case SOUND_PCM_READ_CHANNELS: - ret = pcm_channels; - break; - - case SNDCTL_DSP_SETFMT: - if (get_user(val, (int *)arg)) - return -EFAULT; - ret = pcm_set_bits(val); - break; - - case SOUND_PCM_READ_BITS: - ret = pcm_bits; - break; - - default: - return -EINVAL; - } - return put_user(ret, (int *)arg); -} - -static void pas_audio_reset(int dev) -{ - DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n")); - - pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); /* Disable PCM */ -} - -static int pas_audio_open(int dev, int mode) -{ - int err; - unsigned long flags; - - DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode)); - - save_flags(flags); - cli(); - if (pcm_busy) - { - restore_flags(flags); - return -EBUSY; - } - pcm_busy = 1; - restore_flags(flags); - - if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0) - return err; - - - pcm_count = 0; - open_mode = mode; - - return 0; -} - -static void pas_audio_close(int dev) -{ - unsigned long flags; - - DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n")); - - save_flags(flags); - cli(); - - pas_audio_reset(dev); - pas_remove_intr(PAS_PCM_INTRBITS); - pcm_mode = PCM_NON; - - pcm_busy = 0; - restore_flags(flags); -} - -static void pas_audio_output_block(int dev, unsigned long buf, int count, - int intrflag) -{ - unsigned long flags, cnt; - - DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count)); - - cnt = count; - if (audio_devs[dev]->dmap_out->dma > 3) - cnt >>= 1; - - if (audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - cnt == pcm_count) - return; - - save_flags(flags); - cli(); - - pas_write(pas_read(0xF8A) & ~0x40, - 0xF8A); - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - - if (count != pcm_count) - { - pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A); - pas_write(0x40 | 0x30 | 0x04, 0x138B); - pas_write(count & 0xff, 0x1389); - pas_write((count >> 8) & 0xff, 0x1389); - pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A); - - pcm_count = count; - } - pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); -#ifdef NO_TRIGGER - pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); -#endif - - pcm_mode = PCM_DAC; - - restore_flags(flags); -} - -static void pas_audio_start_input(int dev, unsigned long buf, int count, - int intrflag) -{ - unsigned long flags; - int cnt; - - DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count)); - - cnt = count; - if (audio_devs[dev]->dmap_out->dma > 3) - cnt >>= 1; - - if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE && - intrflag && - cnt == pcm_count) - return; - - save_flags(flags); - cli(); - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - - if (count != pcm_count) - { - pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A); - pas_write(0x40 | 0x30 | 0x04, 0x138B); - pas_write(count & 0xff, 0x1389); - pas_write((count >> 8) & 0xff, 0x1389); - pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A); - - pcm_count = count; - } - pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); -#ifdef NO_TRIGGER - pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); -#endif - - pcm_mode = PCM_ADC; - - restore_flags(flags); -} - -#ifndef NO_TRIGGER -static void pas_audio_trigger(int dev, int state) -{ - unsigned long flags; - - save_flags(flags); - cli(); - state &= open_mode; - - if (state & PCM_ENABLE_OUTPUT) - pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); - else if (state & PCM_ENABLE_INPUT) - pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); - else - pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); - - restore_flags(flags); -} -#endif - -static int pas_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - pas_audio_reset(dev); - return 0; -} - -static int pas_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - pas_audio_reset(dev); - return 0; -} - -static struct audio_driver pas_audio_driver = -{ - owner: THIS_MODULE, - open: pas_audio_open, - close: pas_audio_close, - output_block: pas_audio_output_block, - start_input: pas_audio_start_input, - ioctl: pas_audio_ioctl, - prepare_for_input: pas_audio_prepare_for_input, - prepare_for_output: pas_audio_prepare_for_output, - halt_io: pas_audio_reset, - trigger: pas_audio_trigger -}; - -void __init pas_pcm_init(struct address_info *hw_config) -{ - DEB(printk("pas2_pcm.c: long pas_pcm_init()\n")); - - pcm_bitsok = 8; - if (pas_read(0xEF8B) & 0x08) - pcm_bitsok |= 16; - - pcm_set_speed(DSP_DEFAULT_SPEED); - - if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, - "Pro Audio Spectrum", - &pas_audio_driver, - sizeof(struct audio_driver), - DMA_AUTOMODE, - AFMT_U8 | AFMT_S16_LE, - NULL, - hw_config->dma, - hw_config->dma)) < 0) - printk(KERN_WARNING "PAS16: Too many PCM devices available\n"); -} - -void pas_pcm_interrupt(unsigned char status, int cause) -{ - if (cause == 1) - { - /* - * Halt the PCM first. Otherwise we don't have time to start a new - * block before the PCM chip proceeds to the next sample - */ - - if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE)) - pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); - - switch (pcm_mode) - { - case PCM_DAC: - DMAbuf_outputintr(pas_audiodev, 1); - break; - - case PCM_ADC: - DMAbuf_inputintr(pas_audiodev); - break; - - default: - printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n"); - } - } -} diff -Nru a/drivers/sound/pss.c b/drivers/sound/pss.c --- a/drivers/sound/pss.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1238 +0,0 @@ -/* - * sound/pss.c - * - * The low level driver for the Personal Sound System (ECHO ESC614). - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) - * Alan Cox modularisation, clean up. - * - * 98-02-21: Vladimir Michl - * Added mixer device for Beethoven ADSP-16 (master volume, - * bass, treble, synth), only for speakers. - * Fixed bug in pss_write (exchange parameters) - * Fixed config port of SB - * Requested two regions for PSS (PSS mixer, PSS config) - * Modified pss_download_boot - * To probe_pss_mss added test for initialize AD1848 - * 98-05-28: Vladimir Michl - * Fixed computation of mixer volumes - * 04-05-1999: Anthony Barbachan - * Added code that allows the user to enable his cdrom and/or - * joystick through the module parameters pss_cdrom_port and - * pss_enable_joystick. pss_cdrom_port takes a port address as its - * argument. pss_enable_joystick takes either a 0 or a non-0 as its - * argument. - * 04-06-1999: Anthony Barbachan - * Separated some code into new functions for easier reuse. - * Cleaned up and streamlined new code. Added code to allow a user - * to only use this driver for enabling non-sound components - * through the new module parameter pss_no_sound (flag). Added - * code that would allow a user to decide whether the driver should - * reset the configured hardware settings for the PSS board through - * the module parameter pss_keep_settings (flag). This flag will - * allow a user to free up resources in use by this card if needbe, - * furthermore it allows him to use this driver to just enable the - * emulations and then be unloaded as it is no longer needed. Both - * new settings are only available to this driver if compiled as a - * module. The default settings of all new parameters are set to - * load the driver as it did in previous versions. - * 04-07-1999: Anthony Barbachan - * Added module parameter pss_firmware to allow the user to tell - * the driver where the fireware file is located. The default - * setting is the previous hardcoded setting "/etc/sound/pss_synth". - * 00-03-03: Christoph Hellwig - * Adapted to module_init/module_exit - * 11-10-2000: Bartlomiej Zolnierkiewicz - * Added __init to probe_pss(), attach_pss() and probe_pss_mpu() - * 02-Jan-2001: Chris Rankin - * Specify that this module owns the coprocessor - */ - - -#include -#include -#include - -#include "sound_config.h" -#include "sound_firmware.h" - -#include "ad1848.h" -#include "mpu401.h" - -/* - * PSS registers. - */ -#define REG(x) (devc->base+x) -#define PSS_DATA 0 -#define PSS_STATUS 2 -#define PSS_CONTROL 2 -#define PSS_ID 4 -#define PSS_IRQACK 4 -#define PSS_PIO 0x1a - -/* - * Config registers - */ -#define CONF_PSS 0x10 -#define CONF_WSS 0x12 -#define CONF_SB 0x14 -#define CONF_CDROM 0x16 -#define CONF_MIDI 0x18 - -/* - * Status bits. - */ -#define PSS_FLAG3 0x0800 -#define PSS_FLAG2 0x0400 -#define PSS_FLAG1 0x1000 -#define PSS_FLAG0 0x0800 -#define PSS_WRITE_EMPTY 0x8000 -#define PSS_READ_FULL 0x4000 - -/* - * WSS registers - */ -#define WSS_INDEX 4 -#define WSS_DATA 5 - -/* - * WSS status bits - */ -#define WSS_INITIALIZING 0x80 -#define WSS_AUTOCALIBRATION 0x20 - -#define NO_WSS_MIXER -1 - -#include "coproc.h" - -#include "pss_boot.h" - -/* If compiled into kernel, it enable or disable pss mixer */ -#ifdef CONFIG_PSS_MIXER -static unsigned char pss_mixer = 1; -#else -static unsigned char pss_mixer = 0; -#endif - - -typedef struct pss_mixerdata { - unsigned int volume_l; - unsigned int volume_r; - unsigned int bass; - unsigned int treble; - unsigned int synth; -} pss_mixerdata; - -typedef struct pss_confdata { - int base; - int irq; - int dma; - int *osp; - pss_mixerdata mixer; - int ad_mixer_dev; -} pss_confdata; - -static pss_confdata pss_data; -static pss_confdata *devc = &pss_data; - -static int pss_initialized = 0; -static int nonstandard_microcode = 0; -static int pss_cdrom_port = -1; /* Parameter for the PSS cdrom port */ -static int pss_enable_joystick = 0;/* Parameter for enabling the joystick */ - -static void pss_write(pss_confdata *devc, int data) -{ - int i, limit; - - limit = jiffies + HZ/10; /* The timeout is 0.1 seconds */ - /* - * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes - * called while interrupts are disabled. This means that the timer is - * disabled also. However the timeout situation is a abnormal condition. - * Normally the DSP should be ready to accept commands after just couple of - * loops. - */ - - for (i = 0; i < 5000000 && time_before(jiffies, limit); i++) - { - if (inw(REG(PSS_STATUS)) & PSS_WRITE_EMPTY) - { - outw(data, REG(PSS_DATA)); - return; - } - } - printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data); -} - -int __init probe_pss(struct address_info *hw_config) -{ - unsigned short id; - int irq, dma; - - devc->base = hw_config->io_base; - irq = devc->irq = hw_config->irq; - dma = devc->dma = hw_config->dma; - devc->osp = hw_config->osp; - - if (devc->base != 0x220 && devc->base != 0x240) - if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */ - return 0; - - if (check_region(devc->base, 0x19 /*16*/)) { - printk(KERN_ERR "PSS: I/O port conflict\n"); - return 0; - } - id = inw(REG(PSS_ID)); - if ((id >> 8) != 'E') { - printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); - return 0; - } - return 1; -} - -static int set_irq(pss_confdata * devc, int dev, int irq) -{ - static unsigned short irq_bits[16] = - { - 0x0000, 0x0000, 0x0000, 0x0008, - 0x0000, 0x0010, 0x0000, 0x0018, - 0x0000, 0x0020, 0x0028, 0x0030, - 0x0038, 0x0000, 0x0000, 0x0000 - }; - - unsigned short tmp, bits; - - if (irq < 0 || irq > 15) - return 0; - - tmp = inw(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */ - - if ((bits = irq_bits[irq]) == 0 && irq != 0) - { - printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq); - return 0; - } - outw(tmp | bits, REG(dev)); - return 1; -} - -static int set_io_base(pss_confdata * devc, int dev, int base) -{ - unsigned short tmp = inw(REG(dev)) & 0x003f; - unsigned short bits = (base & 0x0ffc) << 4; - - outw(bits | tmp, REG(dev)); - - return 1; -} - -static int set_dma(pss_confdata * devc, int dev, int dma) -{ - static unsigned short dma_bits[8] = - { - 0x0001, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0006, 0x0007 - }; - - unsigned short tmp, bits; - - if (dma < 0 || dma > 7) - return 0; - - tmp = inw(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */ - - if ((bits = dma_bits[dma]) == 0 && dma != 4) - { - printk(KERN_ERR "PSS: Invalid DMA %d\n", dma); - return 0; - } - outw(tmp | bits, REG(dev)); - return 1; -} - -static int pss_reset_dsp(pss_confdata * devc) -{ - unsigned long i, limit = jiffies + HZ/10; - - outw(0x2000, REG(PSS_CONTROL)); - for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) - inw(REG(PSS_CONTROL)); - outw(0x0000, REG(PSS_CONTROL)); - return 1; -} - -static int pss_put_dspword(pss_confdata * devc, unsigned short word) -{ - int i, val; - - for (i = 0; i < 327680; i++) - { - val = inw(REG(PSS_STATUS)); - if (val & PSS_WRITE_EMPTY) - { - outw(word, REG(PSS_DATA)); - return 1; - } - } - return 0; -} - -static int pss_get_dspword(pss_confdata * devc, unsigned short *word) -{ - int i, val; - - for (i = 0; i < 327680; i++) - { - val = inw(REG(PSS_STATUS)); - if (val & PSS_READ_FULL) - { - *word = inw(REG(PSS_DATA)); - return 1; - } - } - return 0; -} - -static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags) -{ - int i, limit, val, count; - - if (flags & CPF_FIRST) - { -/*_____ Warn DSP software that a boot is coming */ - outw(0x00fe, REG(PSS_DATA)); - - limit = jiffies + HZ/10; - for (i = 0; i < 32768 && time_before(jiffies, limit); i++) - if (inw(REG(PSS_DATA)) == 0x5500) - break; - - outw(*block++, REG(PSS_DATA)); - pss_reset_dsp(devc); - } - count = 1; - while ((flags&CPF_LAST) || count= size && flags & CPF_LAST) - break; - else - { - printk("\n"); - printk(KERN_ERR "PSS: Download timeout problems, byte %d=%d\n", count, size); - return 0; - } - } -/*_____ Send the next byte */ - if (count >= size) - { - /* If not data in block send 0xffff */ - outw (0xffff, REG (PSS_DATA)); - } - else - { - /*_____ Send the next byte */ - outw (*block++, REG (PSS_DATA)); - }; - count++; - } - - if (flags & CPF_LAST) - { -/*_____ Why */ - outw(0, REG(PSS_DATA)); - - limit = jiffies + HZ/10; - for (i = 0; i < 32768 && (limit - jiffies >= 0); i++) - val = inw(REG(PSS_STATUS)); - - limit = jiffies + HZ/10; - for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) - { - val = inw(REG(PSS_STATUS)); - if (val & 0x4000) - break; - } - - /* now read the version */ - for (i = 0; i < 32000; i++) - { - val = inw(REG(PSS_STATUS)); - if (val & PSS_READ_FULL) - break; - } - if (i == 32000) - return 0; - - val = inw(REG(PSS_DATA)); - /* printk( "", val/16, val % 16); */ - } - return 1; -} - -/* Mixer */ -static void set_master_volume(pss_confdata *devc, int left, int right) -{ - static unsigned char log_scale[101] = { - 0xdb, 0xe0, 0xe3, 0xe5, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, - 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, - 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, - 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, - 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff - }; - pss_write(devc, 0x0010); - pss_write(devc, log_scale[left] | 0x0000); - pss_write(devc, 0x0010); - pss_write(devc, log_scale[right] | 0x0100); -} - -static void set_synth_volume(pss_confdata *devc, int volume) -{ - int vol = ((0x8000*volume)/100L); - pss_write(devc, 0x0080); - pss_write(devc, vol); - pss_write(devc, 0x0081); - pss_write(devc, vol); -} - -static void set_bass(pss_confdata *devc, int level) -{ - int vol = (int)(((0xfd - 0xf0) * level)/100L) + 0xf0; - pss_write(devc, 0x0010); - pss_write(devc, vol | 0x0200); -}; - -static void set_treble(pss_confdata *devc, int level) -{ - int vol = (((0xfd - 0xf0) * level)/100L) + 0xf0; - pss_write(devc, 0x0010); - pss_write(devc, vol | 0x0300); -}; - -static void pss_mixer_reset(pss_confdata *devc) -{ - set_master_volume(devc, 33, 33); - set_bass(devc, 50); - set_treble(devc, 50); - set_synth_volume(devc, 30); - pss_write (devc, 0x0010); - pss_write (devc, 0x0800 | 0xce); /* Stereo */ - - if(pss_mixer) - { - devc->mixer.volume_l = devc->mixer.volume_r = 33; - devc->mixer.bass = 50; - devc->mixer.treble = 50; - devc->mixer.synth = 30; - } -} - -static void arg_to_volume_mono(unsigned int volume, int *aleft) -{ - int left; - - left = volume & 0x00ff; - if (left > 100) - left = 100; - *aleft = left; -} - -static void arg_to_volume_stereo(unsigned int volume, int *aleft, int *aright) -{ - arg_to_volume_mono(volume, aleft); - arg_to_volume_mono(volume >> 8, aright); -} - -static int ret_vol_mono(int left) -{ - return ((left << 8) | left); -} - -static int ret_vol_stereo(int left, int right) -{ - return ((right << 8) | left); -} - -static int call_ad_mixer(pss_confdata *devc,unsigned int cmd, caddr_t arg) -{ - if (devc->ad_mixer_dev != NO_WSS_MIXER) - return mixer_devs[devc->ad_mixer_dev]->ioctl(devc->ad_mixer_dev, cmd, arg); - else - return -EINVAL; -} - -static int pss_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) -{ - pss_confdata *devc = mixer_devs[dev]->devc; - int cmdf = cmd & 0xff; - - if ((cmdf != SOUND_MIXER_VOLUME) && (cmdf != SOUND_MIXER_BASS) && - (cmdf != SOUND_MIXER_TREBLE) && (cmdf != SOUND_MIXER_SYNTH) && - (cmdf != SOUND_MIXER_DEVMASK) && (cmdf != SOUND_MIXER_STEREODEVS) && - (cmdf != SOUND_MIXER_RECMASK) && (cmdf != SOUND_MIXER_CAPS) && - (cmdf != SOUND_MIXER_RECSRC)) - { - return call_ad_mixer(devc, cmd, arg); - } - - if (((cmd >> 8) & 0xff) != 'M') - return -EINVAL; - - if (_SIOC_DIR (cmd) & _SIOC_WRITE) - { - switch (cmdf) - { - case SOUND_MIXER_RECSRC: - if (devc->ad_mixer_dev != NO_WSS_MIXER) - return call_ad_mixer(devc, cmd, arg); - else - { - if (*(int *)arg != 0) - return -EINVAL; - return 0; - } - case SOUND_MIXER_VOLUME: - arg_to_volume_stereo(*(unsigned int *)arg, &devc->mixer.volume_l, - &devc->mixer.volume_r); - set_master_volume(devc, devc->mixer.volume_l, - devc->mixer.volume_r); - return ret_vol_stereo(devc->mixer.volume_l, - devc->mixer.volume_r); - - case SOUND_MIXER_BASS: - arg_to_volume_mono(*(unsigned int *)arg, - &devc->mixer.bass); - set_bass(devc, devc->mixer.bass); - return ret_vol_mono(devc->mixer.bass); - - case SOUND_MIXER_TREBLE: - arg_to_volume_mono(*(unsigned int *)arg, - &devc->mixer.treble); - set_treble(devc, devc->mixer.treble); - return ret_vol_mono(devc->mixer.treble); - - case SOUND_MIXER_SYNTH: - arg_to_volume_mono(*(unsigned int *)arg, - &devc->mixer.synth); - set_synth_volume(devc, devc->mixer.synth); - return ret_vol_mono(devc->mixer.synth); - - default: - return -EINVAL; - } - } - else - { - /* - * Return parameters - */ - switch (cmdf) - { - - case SOUND_MIXER_DEVMASK: - if (call_ad_mixer(devc, cmd, arg) == -EINVAL) - *(int *)arg = 0; /* no mixer devices */ - return (*(int *)arg |= SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH); - - case SOUND_MIXER_STEREODEVS: - if (call_ad_mixer(devc, cmd, arg) == -EINVAL) - *(int *)arg = 0; /* no stereo devices */ - return (*(int *)arg |= SOUND_MASK_VOLUME); - - case SOUND_MIXER_RECMASK: - if (devc->ad_mixer_dev != NO_WSS_MIXER) - return call_ad_mixer(devc, cmd, arg); - else - return (*(int *)arg = 0); /* no record devices */ - - case SOUND_MIXER_CAPS: - if (devc->ad_mixer_dev != NO_WSS_MIXER) - return call_ad_mixer(devc, cmd, arg); - else - return (*(int *)arg = SOUND_CAP_EXCL_INPUT); - - case SOUND_MIXER_RECSRC: - if (devc->ad_mixer_dev != NO_WSS_MIXER) - return call_ad_mixer(devc, cmd, arg); - else - return (*(int *)arg = 0); /* no record source */ - - case SOUND_MIXER_VOLUME: - return (*(int *)arg = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r)); - - case SOUND_MIXER_BASS: - return (*(int *)arg = ret_vol_mono(devc->mixer.bass)); - - case SOUND_MIXER_TREBLE: - return (*(int *)arg = ret_vol_mono(devc->mixer.treble)); - - case SOUND_MIXER_SYNTH: - return (*(int *)arg = ret_vol_mono(devc->mixer.synth)); - default: - return -EINVAL; - } - } -} - -static struct mixer_operations pss_mixer_operations = -{ - owner: THIS_MODULE, - id: "SOUNDPORT", - name: "PSS-AD1848", - ioctl: pss_mixer_ioctl -}; - -void disable_all_emulations(void) -{ - outw(0x0000, REG(CONF_PSS)); /* 0x0400 enables joystick */ - outw(0x0000, REG(CONF_WSS)); - outw(0x0000, REG(CONF_SB)); - outw(0x0000, REG(CONF_MIDI)); - outw(0x0000, REG(CONF_CDROM)); -} - -void configure_nonsound_components(void) -{ - /* Configure Joystick port */ - - if(pss_enable_joystick) - { - outw(0x0400, REG(CONF_PSS)); /* 0x0400 enables joystick */ - printk(KERN_INFO "PSS: joystick enabled.\n"); - } - else - { - printk(KERN_INFO "PSS: joystick port not enabled.\n"); - } - - /* Configure CDROM port */ - - if(pss_cdrom_port == -1) /* If cdrom port enablation wasn't requested */ - { - printk(KERN_INFO "PSS: CDROM port not enabled.\n"); - } - else if(check_region(pss_cdrom_port, 2)) - { - printk(KERN_ERR "PSS: CDROM I/O port conflict.\n"); - } - else if(!set_io_base(devc, CONF_CDROM, pss_cdrom_port)) - { - printk(KERN_ERR "PSS: CDROM I/O port could not be set.\n"); - } - else /* CDROM port successfully configured */ - { - printk(KERN_INFO "PSS: CDROM I/O port set to 0x%x.\n", pss_cdrom_port); - } -} - -void __init attach_pss(struct address_info *hw_config) -{ - unsigned short id; - char tmp[100]; - - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->dma = hw_config->dma; - devc->osp = hw_config->osp; - devc->ad_mixer_dev = NO_WSS_MIXER; - - if (!probe_pss(hw_config)) - return; - - request_region(hw_config->io_base, 0x10, "PSS mixer, SB emulation"); - request_region(hw_config->io_base + 0x10, 0x9, "PSS config"); - - id = inw(REG(PSS_ID)) & 0x00ff; - - /* - * Disable all emulations. Will be enabled later (if required). - */ - - disable_all_emulations(); - -#if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES - if (sound_alloc_dma(hw_config->dma, "PSS")) - { - printk("pss.c: Can't allocate DMA channel.\n"); - return; - } - if (!set_irq(devc, CONF_PSS, devc->irq)) - { - printk("PSS: IRQ allocation error.\n"); - return; - } - if (!set_dma(devc, CONF_PSS, devc->dma)) - { - printk(KERN_ERR "PSS: DMA allocation error\n"); - return; - } -#endif - - configure_nonsound_components(); - pss_initialized = 1; - sprintf(tmp, "ECHO-PSS Rev. %d", id); - conf_printf(tmp, hw_config); -} - -int __init probe_pss_mpu(struct address_info *hw_config) -{ - int timeout; - - if (!pss_initialized) - return 0; - - if (check_region(hw_config->io_base, 2)) - { - printk(KERN_ERR "PSS: MPU I/O port conflict\n"); - return 0; - } - if (!set_io_base(devc, CONF_MIDI, hw_config->io_base)) - { - printk(KERN_ERR "PSS: MIDI base could not be set.\n"); - return 0; - } - if (!set_irq(devc, CONF_MIDI, hw_config->irq)) - { - printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n"); - return 0; - } - if (!pss_synthLen) - { - printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n"); - return 0; - } - if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); - return 0; - } - - /* - * Finally wait until the DSP algorithm has initialized itself and - * deactivates receive interrupt. - */ - - for (timeout = 900000; timeout > 0; timeout--) - { - if ((inb(hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */ - inb(hw_config->io_base); /* Discard it */ - else - break; /* No more input */ - } - - return probe_mpu401(hw_config); -} - -static int pss_coproc_open(void *dev_info, int sub_device) -{ - switch (sub_device) - { - case COPR_MIDI: - if (pss_synthLen == 0) - { - printk(KERN_ERR "PSS: MIDI synth microcode not available.\n"); - return -EIO; - } - if (nonstandard_microcode) - if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); - return -EIO; - } - nonstandard_microcode = 0; - break; - - default: - break; - } - return 0; -} - -static void pss_coproc_close(void *dev_info, int sub_device) -{ - return; -} - -static void pss_coproc_reset(void *dev_info) -{ - if (pss_synthLen) - if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); - } - nonstandard_microcode = 0; -} - -static int download_boot_block(void *dev_info, copr_buffer * buf) -{ - if (buf->len <= 0 || buf->len > sizeof(buf->data)) - return -EINVAL; - - if (!pss_download_boot(devc, buf->data, buf->len, buf->flags)) - { - printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n"); - return -EIO; - } - nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */ - return 0; -} - -static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local) -{ - copr_buffer *buf; - copr_msg *mbuf; - copr_debug_buf dbuf; - unsigned short tmp; - unsigned long flags; - unsigned short *data; - int i, err; - /* printk( "PSS coproc ioctl %x %x %d\n", cmd, arg, local); */ - - switch (cmd) - { - case SNDCTL_COPR_RESET: - pss_coproc_reset(dev_info); - return 0; - - case SNDCTL_COPR_LOAD: - buf = (copr_buffer *) vmalloc(sizeof(copr_buffer)); - if (buf == NULL) - return -ENOSPC; - if (copy_from_user(buf, arg, sizeof(copr_buffer))) { - vfree(buf); - return -EFAULT; - } - err = download_boot_block(dev_info, buf); - vfree(buf); - return err; - - case SNDCTL_COPR_SENDMSG: - mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); - if (mbuf == NULL) - return -ENOSPC; - if (copy_from_user(mbuf, arg, sizeof(copr_msg))) { - vfree(mbuf); - return -EFAULT; - } - data = (unsigned short *)(mbuf->data); - save_flags(flags); - cli(); - for (i = 0; i < mbuf->len; i++) { - if (!pss_put_dspword(devc, *data++)) { - restore_flags(flags); - mbuf->len = i; /* feed back number of WORDs sent */ - err = copy_to_user(arg, mbuf, sizeof(copr_msg)); - vfree(mbuf); - return err ? -EFAULT : -EIO; - } - } - restore_flags(flags); - vfree(mbuf); - return 0; - - case SNDCTL_COPR_RCVMSG: - err = 0; - mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); - if (mbuf == NULL) - return -ENOSPC; - data = (unsigned short *)mbuf->data; - save_flags(flags); - cli(); - for (i = 0; i < sizeof(mbuf->data)/sizeof(unsigned short); i++) { - mbuf->len = i; /* feed back number of WORDs read */ - if (!pss_get_dspword(devc, data++)) { - if (i == 0) - err = -EIO; - break; - } - } - restore_flags(flags); - if (copy_to_user(arg, mbuf, sizeof(copr_msg))) - err = -EFAULT; - vfree(mbuf); - return err; - - case SNDCTL_COPR_RDATA: - if (copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - save_flags(flags); - cli(); - if (!pss_put_dspword(devc, 0x00d0)) { - restore_flags(flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { - restore_flags(flags); - return -EIO; - } - if (!pss_get_dspword(devc, &tmp)) { - restore_flags(flags); - return -EIO; - } - dbuf.parm1 = tmp; - restore_flags(flags); - if (copy_to_user(arg, &dbuf, sizeof(dbuf))) - return -EFAULT; - return 0; - - case SNDCTL_COPR_WDATA: - if (copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - save_flags(flags); - cli(); - if (!pss_put_dspword(devc, 0x00d1)) { - restore_flags(flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) { - restore_flags(flags); - return -EIO; - } - tmp = (unsigned int)dbuf.parm2 & 0xffff; - if (!pss_put_dspword(devc, tmp)) { - restore_flags(flags); - return -EIO; - } - restore_flags(flags); - return 0; - - case SNDCTL_COPR_WCODE: - if (copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - save_flags(flags); - cli(); - if (!pss_put_dspword(devc, 0x00d3)) { - restore_flags(flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { - restore_flags(flags); - return -EIO; - } - tmp = (unsigned int)dbuf.parm2 & 0x00ff; - if (!pss_put_dspword(devc, tmp)) { - restore_flags(flags); - return -EIO; - } - tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff; - if (!pss_put_dspword(devc, tmp)) { - restore_flags(flags); - return -EIO; - } - restore_flags(flags); - return 0; - - case SNDCTL_COPR_RCODE: - if (copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - save_flags(flags); - cli(); - if (!pss_put_dspword(devc, 0x00d2)) { - restore_flags(flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { - restore_flags(flags); - return -EIO; - } - if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */ - restore_flags(flags); - return -EIO; - } - dbuf.parm1 = tmp << 8; - if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */ - restore_flags(flags); - return -EIO; - } - dbuf.parm1 |= tmp & 0x00ff; - restore_flags(flags); - if (copy_to_user(arg, &dbuf, sizeof(dbuf))) - return -EFAULT; - return 0; - - default: - return -EINVAL; - } - return -EINVAL; -} - -static coproc_operations pss_coproc_operations = -{ - "ADSP-2115", - THIS_MODULE, - pss_coproc_open, - pss_coproc_close, - pss_coproc_ioctl, - pss_coproc_reset, - &pss_data -}; - -static void __init attach_pss_mpu(struct address_info *hw_config) -{ - attach_mpu401(hw_config, THIS_MODULE); /* Slot 1 */ - if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ - midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations; -} - -static int __init probe_pss_mss(struct address_info *hw_config) -{ - volatile int timeout; - - if (!pss_initialized) - return 0; - - if (check_region(hw_config->io_base, 8)) - { - printk(KERN_ERR "PSS: WSS I/O port conflicts.\n"); - return 0; - } - if (!set_io_base(devc, CONF_WSS, hw_config->io_base)) - { - printk("PSS: WSS base not settable.\n"); - return 0; - } - if (!set_irq(devc, CONF_WSS, hw_config->irq)) - { - printk("PSS: WSS IRQ allocation error.\n"); - return 0; - } - if (!set_dma(devc, CONF_WSS, hw_config->dma)) - { - printk(KERN_ERR "PSS: WSS DMA allocation error\n"); - return 0; - } - /* - * For some reason the card returns 0xff in the WSS status register - * immediately after boot. Probably MIDI+SB emulation algorithm - * downloaded to the ADSP2115 spends some time initializing the card. - * Let's try to wait until it finishes this task. - */ - for (timeout = 0; timeout < 100000 && (inb(hw_config->io_base + WSS_INDEX) & - WSS_INITIALIZING); timeout++) - ; - - outb((0x0b), hw_config->io_base + WSS_INDEX); /* Required by some cards */ - - for (timeout = 0; (inb(hw_config->io_base + WSS_DATA) & WSS_AUTOCALIBRATION) && - (timeout < 100000); timeout++) - ; - - return probe_ms_sound(hw_config); -} - -static void __init attach_pss_mss(struct address_info *hw_config) -{ - int my_mix = -999; /* gcc shut up */ - - devc->ad_mixer_dev = NO_WSS_MIXER; - if (pss_mixer) - { - if ((my_mix = sound_install_mixer (MIXER_DRIVER_VERSION, - "PSS-SPEAKERS and AD1848 (through MSS audio codec)", - &pss_mixer_operations, - sizeof (struct mixer_operations), - devc)) < 0) - { - printk(KERN_ERR "Could not install PSS mixer\n"); - return; - } - } - pss_mixer_reset(devc); - attach_ms_sound(hw_config, THIS_MODULE); /* Slot 0 */ - - if (hw_config->slots[0] != -1) - { - /* The MSS driver installed itself */ - audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations; - if (pss_mixer && (num_mixers == (my_mix + 2))) - { - /* The MSS mixer installed */ - devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev; - } - } -} - -static inline void __exit unload_pss(struct address_info *hw_config) -{ - release_region(hw_config->io_base, 0x10); - release_region(hw_config->io_base+0x10, 0x9); -} - -static inline void __exit unload_pss_mpu(struct address_info *hw_config) -{ - unload_mpu401(hw_config); -} - -static inline void __exit unload_pss_mss(struct address_info *hw_config) -{ - unload_ms_sound(hw_config); -} - - -static struct address_info cfg; -static struct address_info cfg2; -static struct address_info cfg_mpu; - -static int pss_io __initdata = -1; -static int mss_io __initdata = -1; -static int mss_irq __initdata = -1; -static int mss_dma __initdata = -1; -static int mpu_io __initdata = -1; -static int mpu_irq __initdata = -1; -static int pss_no_sound __initdata = 0; /* Just configure non-sound components */ -static int pss_keep_settings = 1; /* Keep hardware settings at module exit */ -static char *pss_firmware = "/etc/sound/pss_synth"; - -MODULE_PARM(pss_io, "i"); -MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)"); -MODULE_PARM(mss_io, "i"); -MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)"); -MODULE_PARM(mss_irq, "i"); -MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)"); -MODULE_PARM(mss_dma, "i"); -MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)"); -MODULE_PARM(mpu_io, "i"); -MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)"); -MODULE_PARM(mpu_irq, "i"); -MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)"); -MODULE_PARM(pss_cdrom_port, "i"); -MODULE_PARM_DESC(pss_cdrom_port, "Set the PSS CDROM port i/o base (0x340 or other)"); -MODULE_PARM(pss_enable_joystick, "i"); -MODULE_PARM_DESC(pss_enable_joystick, "Enables the PSS joystick port (1 to enable, 0 to disable)"); -MODULE_PARM(pss_no_sound, "i"); -MODULE_PARM_DESC(pss_no_sound, "Configure sound compoents (0 - no, 1 - yes)"); -MODULE_PARM(pss_keep_settings, "i"); -MODULE_PARM_DESC(pss_keep_settings, "Keep hardware setting at driver unloading (0 - no, 1 - yes)"); -MODULE_PARM(pss_firmware, "s"); -MODULE_PARM_DESC(pss_firmware, "Location of the firmware file (default - /etc/sound/pss_synth)"); -MODULE_PARM(pss_mixer, "b"); -MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards."); -MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl"); -MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards).\n"); -MODULE_LICENSE("GPL"); - - -static int fw_load = 0; -static int pssmpu = 0, pssmss = 0; - -/* - * Load a PSS sound card module - */ - -static int __init init_pss(void) -{ - - if(pss_no_sound) /* If configuring only nonsound components */ - { - cfg.io_base = pss_io; - if(!probe_pss(&cfg)) - return -ENODEV; - printk(KERN_INFO "ECHO-PSS Rev. %d\n", inw(REG(PSS_ID)) & 0x00ff); - printk(KERN_INFO "PSS: loading in no sound mode.\n"); - disable_all_emulations(); - configure_nonsound_components(); - return 0; - } - - cfg.io_base = pss_io; - - cfg2.io_base = mss_io; - cfg2.irq = mss_irq; - cfg2.dma = mss_dma; - - cfg_mpu.io_base = mpu_io; - cfg_mpu.irq = mpu_irq; - - if (cfg.io_base == -1 || cfg2.io_base == -1 || cfg2.irq == -1 || cfg.dma == -1) { - printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n"); - return -EINVAL; - } - - if (!pss_synth) { - fw_load = 1; - pss_synthLen = mod_firmware_load(pss_firmware, (void *) &pss_synth); - } - if (!probe_pss(&cfg)) - return -ENODEV; - attach_pss(&cfg); - /* - * Attach stuff - */ - if (probe_pss_mpu(&cfg_mpu)) { - pssmpu = 1; - attach_pss_mpu(&cfg_mpu); - } - if (probe_pss_mss(&cfg2)) { - pssmss = 1; - attach_pss_mss(&cfg2); - } - - return 0; -} - -static void __exit cleanup_pss(void) -{ - if(!pss_no_sound) - { - if(fw_load && pss_synth) - vfree(pss_synth); - if(pssmss) - unload_pss_mss(&cfg2); - if(pssmpu) - unload_pss_mpu(&cfg_mpu); - unload_pss(&cfg); - } - - if(!pss_keep_settings) /* Keep hardware settings if asked */ - { - disable_all_emulations(); - printk(KERN_INFO "Resetting PSS sound card configurations.\n"); - } -} - -module_init(init_pss); -module_exit(cleanup_pss); - -#ifndef MODULE -static int __init setup_pss(char *str) -{ - /* io, mss_io, mss_irq, mss_dma, mpu_io, mpu_irq */ - int ints[7]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - pss_io = ints[1]; - mss_io = ints[2]; - mss_irq = ints[3]; - mss_dma = ints[4]; - mpu_io = ints[5]; - mpu_irq = ints[6]; - - return 1; -} - -__setup("pss=", setup_pss); -#endif diff -Nru a/drivers/sound/rme96xx.c b/drivers/sound/rme96xx.c --- a/drivers/sound/rme96xx.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1565 +0,0 @@ -/* (C) 2000 Guenter Geiger - with copy/pastes from the driver of Winfried Ritsch - based on es1370.c - - - - * 10 Jan 2001: 0.1 initial version - * 19 Jan 2001: 0.2 fixed bug in select() - * 27 Apr 2001: 0.3 more than one card usable - * 11 May 2001: 0.4 fixed for SMP, included into kernel source tree - * 17 May 2001: 0.5 draining code didn't work on new cards - * 18 May 2001: 0.6 remove synchronize_irq() call - -TODO: - - test more than one card --- done - - check for pci IOREGION (see es1370) in rme96xx_probe ?? - - error detection - - mmap interface - - mixer mmap interface - - mixer ioctl - - get rid of noise upon first open (why ??) - - allow multiple open(at least for read) - - allow multiple open for non overlapping regions - - recheck the multiple devices part (offsets of different devices, etc) - - do decent draining in _release --- done - - SMP support -*/ - -#ifndef RMEVERSION -#define RMEVERSION "0.6" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "rme96xx.h" - -#define NR_DEVICE 2 - -static int devices = 1; -MODULE_PARM(devices, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(devices, "number of dsp devices allocated by the driver"); - - -MODULE_AUTHOR("Guenter Geiger, geiger@debian.org"); -MODULE_DESCRIPTION("RME9652/36 \"Hammerfall\" Driver"); -MODULE_LICENSE("GPL"); - - -#ifdef DEBUG -#define DBG(x) printk("RME_DEBUG:");x -#define COMM(x) printk("RME_COMM: " x "\n"); -#else -#define DBG(x) while (0) {} -#define COMM(x) -#endif - -/*-------------------------------------------------------------------------- - Preporcessor Macros and Definitions - --------------------------------------------------------------------------*/ - -#define RME96xx_MAGIC 0x6473 - -/* Registers-Space in offsets from base address with 16MByte size */ - -#define RME96xx_IO_EXTENT 16l*1024l*1024l -#define RME96xx_CHANNELS_PER_CARD 26 - -/* Write - Register */ - -/* 0,4,8,12,16,20,24,28 ... hardware init (erasing fifo-pointer intern) */ -#define RME96xx_num_of_init_regs 8 - -#define RME96xx_init_buffer (0/4) -#define RME96xx_play_buffer (32/4) /* pointer to 26x64kBit RAM from mainboard */ -#define RME96xx_rec_buffer (36/4) /* pointer to 26x64kBit RAM from mainboard */ -#define RME96xx_control_register (64/4) /* exact meaning see below */ -#define RME96xx_irq_clear (96/4) /* irq acknowledge */ -#define RME96xx_time_code (100/4) /* if used with alesis adat */ -#define RME96xx_thru_base (128/4) /* 132...228 Thru for 26 channels */ -#define RME96xx_thru_channels RME96xx_CHANNELS_PER_CARD - -/* Read Register */ - -#define RME96xx_status_register 0 /* meaning see below */ - - - -/* Status Register: */ -/* ------------------------------------------------------------------------ */ -#define RME96xx_IRQ 0x0000001 /* IRQ is High if not reset by RMExx_irq_clear */ -#define RME96xx_lock_2 0x0000002 /* ADAT 3-PLL: 1=locked, 0=unlocked */ -#define RME96xx_lock_1 0x0000004 /* ADAT 2-PLL: 1=locked, 0=unlocked */ -#define RME96xx_lock_0 0x0000008 /* ADAT 1-PLL: 1=locked, 0=unlocked */ - -#define RME96xx_fs48 0x0000010 /* sample rate 0 ...44.1/88.2, 1 ... 48/96 Khz */ -#define RME96xx_wsel_rd 0x0000020 /* if Word-Clock is used and valid then 1 */ -#define RME96xx_buf_pos1 0x0000040 /* Bit 6..15 : Position of buffer-pointer in 64Bytes-blocks */ -#define RME96xx_buf_pos2 0x0000080 /* resolution +/- 1 64Byte/block (since 64Bytes bursts) */ - -#define RME96xx_buf_pos3 0x0000100 /* 10 bits = 1024 values */ -#define RME96xx_buf_pos4 0x0000200 /* if we mask off the first 6 bits, we can take the status */ -#define RME96xx_buf_pos5 0x0000400 /* register as sample counter in the hardware buffer */ -#define RME96xx_buf_pos6 0x0000800 - -#define RME96xx_buf_pos7 0x0001000 -#define RME96xx_buf_pos8 0x0002000 -#define RME96xx_buf_pos9 0x0004000 -#define RME96xx_buf_pos10 0x0008000 - -#define RME96xx_sync_2 0x0010000 /* if ADAT-IN3 synced to system clock */ -#define RME96xx_sync_1 0x0020000 /* if ADAT-IN2 synced to system clock */ -#define RME96xx_sync_0 0x0040000 /* if ADAT-IN1 synced to system clock */ -#define RME96xx_DS_rd 0x0080000 /* 1=Double Speed, 0=Normal Speed */ - -#define RME96xx_tc_busy 0x0100000 /* 1=time-code copy in progress (960ms) */ -#define RME96xx_tc_out 0x0200000 /* time-code out bit */ -#define RME96xx_F_0 0x0400000 /* 000=64kHz, 100=88.2kHz, 011=96kHz */ -#define RME96xx_F_1 0x0800000 /* 111=32kHz, 110=44.1kHz, 101=48kHz, */ - -#define RME96xx_F_2 0x1000000 /* od external Crystal Chip if ERF=1*/ -#define RME96xx_ERF 0x2000000 /* Error-Flag of SDPIF Receiver (1=No Lock)*/ -#define RME96xx_buffer_id 0x4000000 /* toggles by each interrupt on rec/play */ -#define RME96xx_tc_valid 0x8000000 /* 1 = a signal is detected on time-code input */ - -/* Status Register Fields */ - -#define RME96xx_lock (RME96xx_lock_0|RME96xx_lock_1|RME96xx_lock_2) -#define RME96xx_buf_pos 0x000FFC0 -#define RME96xx_sync (RME96xx_sync_0|RME96xx_sync_1|RME96xx_sync_2) -#define RME96xx_F (RME96xx_F_0|RME96xx_F_1|RME96xx_F_2) - - - -/* Control-Register: */ -/*--------------------------------------------------------------------------------*/ - -#define RME96xx_start_bit 0x0001 /* start record/play */ -#define RME96xx_latency0 0x0002 /* Bit 0 - Buffer size or latency */ -#define RME96xx_latency1 0x0004 /* Bit 1 - Buffer size or latency */ -#define RME96xx_latency2 0x0008 /* Bit 2 - Buffer size or latency */ - -#define RME96xx_Master 0x0010 /* Clock Mode Master=1,Slave/Auto=0 */ -#define RME96xx_IE 0x0020 /* Interupt Enable */ -#define RME96xx_freq 0x0040 /* samplerate 0=44.1/88.2, 1=48/96 kHz*/ - - -#define RME96xx_DS 0x0100 /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */ -#define RME96xx_PRO 0x0200 /* spdif 0=consumer, 1=professional Mode*/ -#define RME96xx_EMP 0x0400 /* spdif Emphasis 0=None, 1=ON */ -#define RME96xx_Dolby 0x0800 /* spdif Non-audio bit 1=set, 0=unset */ - -#define RME96xx_opt_out 0x1000 /* Use 1st optical OUT as SPDIF: 1=yes,0=no */ -#define RME96xx_wsel 0x2000 /* use Wordclock as sync (overwrites master)*/ -#define RME96xx_inp_0 0x4000 /* SPDIF-IN: 00=optical (ADAT1), */ -#define RME96xx_inp_1 0x8000 /* 01=koaxial (Cinch), 10=Internal CDROM*/ - -#define RME96xx_SyncRef0 0x10000 /* preferred sync-source in autosync */ -#define RME96xx_SyncRef1 0x20000 /* 00=ADAT1,01=ADAT2,10=ADAT3,11=SPDIF */ - - -#define RME96xx_ctrl_init (RME96xx_latency0 |\ - RME96xx_Master |\ - RME96xx_inp_1) - - - -/* Control register fields and shortcuts */ - -#define RME96xx_latency (RME96xx_latency0|RME96xx_latency1|RME96xx_latency2) -#define RME96xx_inp (RME96xx_inp_0|RME96xx_inp_1) -#define RME96xx_SyncRef (RME96xx_SyncRef0|RME96xx_SyncRef1) -/* latency = 512Bytes * 2^n, where n is made from Bit3 ... Bit0 */ - -#define RME96xx_SET_LATENCY(x) (((x)&0x7)<<1) -#define RME96xx_GET_LATENCY(x) (((x)>>1)&0x7) -#define RME96xx_SET_inp(x) (((x)&0x3)<<14) -#define RME96xx_GET_inp(x) (((x)>>14)&0x3) -#define RME96xx_SET_SyncRef(x) (((x)&0x3)<<17) -#define RME96xx_GET_SyncRef(x) (((x)>>17)&0x3) - - -/* buffer sizes */ -#define RME96xx_BYTES_PER_SAMPLE 4 /* sizeof(u32) */ -#define RME_16K 16*1024 - -#define RME96xx_DMA_MAX_SAMPLES (RME_16K) -#define RME96xx_DMA_MAX_SIZE (RME_16K * RME96xx_BYTES_PER_SAMPLE) -#define RME96xx_DMA_MAX_SIZE_ALL (RME96xx_DMA_MAX_SIZE * RME96xx_CHANNELS_PER_CARD) - -#define RME96xx_NUM_OF_FRAGMENTS 2 -#define RME96xx_FRAGMENT_MAX_SIZE (RME96xx_DMA_MAX_SIZE/2) -#define RME96xx_FRAGMENT_MAX_SAMPLES (RME96xx_DMA_MAX_SAMPLES/2) -#define RME96xx_MAX_LATENCY 7 /* 16k samples */ - - -#define RME96xx_MAX_DEVS 4 /* we provide some OSS stereodevs */ - -#define RME_MESS "rme96xx:" -/*------------------------------------------------------------------------ - Types, struct and function declarations - ------------------------------------------------------------------------*/ - - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT RME_MESS" invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != RME96xx_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - - -static struct file_operations rme96xx_audio_fops; -static struct file_operations rme96xx_mixer_fops; -static int numcards; - -typedef int32_t raw_sample_t; - -typedef struct _rme96xx_info { - - /* hardware settings */ - int magic; - struct pci_dev * pcidev; /* pci_dev structure */ - unsigned long *iobase; - unsigned int irq; - - /* list of rme96xx devices */ - struct list_head devs; - - spinlock_t lock; - - u32 *recbuf; /* memory for rec buffer */ - u32 *playbuf; /* memory for play buffer */ - - u32 control_register; - - u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */ - - int open_count; - - - int rate; - int latency; - unsigned int fragsize; - int started; - - int hwptr; /* can be negativ because of pci burst offset */ - unsigned int hwbufid; /* set by interrupt, buffer which is written/read now */ - - struct dmabuf { - - unsigned int format; - int formatshift; - int inchannels; /* number of channels for device */ - int outchannels; /* number of channels for device */ - int mono; /* if true, we play mono on 2 channels */ - int inoffset; /* which channel is considered the first one */ - int outoffset; - - /* state */ - int opened; /* open() made */ - int started; /* first write/read */ - int mmapped; /* mmap */ - int open_mode; - - struct _rme96xx_info *s; - - /* pointer to read/write position in buffer */ - unsigned readptr; - unsigned writeptr; - - unsigned error; /* over/underruns cleared on sync again */ - - /* waiting and locking */ - wait_queue_head_t wait; - struct semaphore open_sem; - wait_queue_head_t open_wait; - - } dma[RME96xx_MAX_DEVS]; - - int dspnum[RME96xx_MAX_DEVS]; /* register with sound subsystem */ - int mixer; /* register with sound subsystem */ -} rme96xx_info; - - -/* fiddling with the card (first level hardware control) */ - -inline void rme96xx_set_ctrl(rme96xx_info* s,int mask) -{ - - s->control_register|=mask; - writel(s->control_register,s->iobase + RME96xx_control_register); - -} - -inline void rme96xx_unset_ctrl(rme96xx_info* s,int mask) -{ - - s->control_register&=(~mask); - writel(s->control_register,s->iobase + RME96xx_control_register); - -} - - - -/* the hwbuf in the status register seems to have some jitter, to get rid of - it, we first only let the numbers grow, to be on the secure side we - subtract a certain amount RME96xx_BURSTBYTES from the resulting number */ - -/* the function returns the hardware pointer in bytes */ -#define RME96xx_BURSTBYTES -64 /* bytes by which hwptr could be off */ - -inline int rme96xx_gethwptr(rme96xx_info* s,int exact) -{ - long flags; - if (exact) { - unsigned int hwp; -/* the hwptr seems to be rather unreliable :(, so we don't use it */ - spin_lock_irqsave(&s->lock,flags); - - hwp = readl(s->iobase + RME96xx_status_register) & 0xffc0; - s->hwptr = (hwp < s->hwptr) ? s->hwptr : hwp; -// s->hwptr = hwp; - - spin_unlock_irqrestore(&s->lock,flags); - return (s->hwptr+RME96xx_BURSTBYTES) & ((s->fragsize<<1)-1); - } - return (s->hwbufid ? s->fragsize : 0); -} - -inline void rme96xx_setlatency(rme96xx_info* s,int l) -{ - s->latency = l; - s->fragsize = 1<<(8+l); - rme96xx_unset_ctrl(s,RME96xx_latency); - rme96xx_set_ctrl(s,RME96xx_SET_LATENCY(l)); -} - - -static void rme96xx_clearbufs(struct dmabuf* dma) -{ - int i,j; - unsigned long flags; - - /* clear dmabufs */ - for(i=0;ioutchannels + dma->mono;j++) - memset(&dma->s->playbuf[(dma->outoffset + j)*RME96xx_DMA_MAX_SAMPLES], - 0, RME96xx_DMA_MAX_SIZE); - } - spin_lock_irqsave(&dma->s->lock,flags); - dma->writeptr = 0; - dma->readptr = 0; - spin_unlock_irqrestore(&dma->s->lock,flags); -} - -static int rme96xx_startcard(rme96xx_info *s,int stop) -{ - int i; - long flags; - - COMM ("startcard"); - if(s->control_register & RME96xx_IE){ - /* disable interrupt first */ - - rme96xx_unset_ctrl( s,RME96xx_start_bit ); - udelay(10); - rme96xx_unset_ctrl( s,RME96xx_IE); - spin_lock_irqsave(&s->lock,flags); /* timing is critical */ - s->started = 0; - spin_unlock_irqrestore(&s->lock,flags); - if (stop) { - COMM("Sound card stopped"); - return 1; - } - } - COMM ("interupt disabled"); - /* first initialize all pointers on card */ - for(i=0;iiobase + i); - udelay(10); /* ?? */ - } - COMM ("regs cleaned"); - - spin_lock_irqsave(&s->lock,flags); /* timing is critical */ - udelay(10); - s->started = 1; - s->hwptr = 0; - spin_unlock_irqrestore(&s->lock,flags); - - rme96xx_set_ctrl( s, RME96xx_IE | RME96xx_start_bit); - - - COMM("Sound card started"); - - return 1; -} - - -inline int rme96xx_getospace(struct dmabuf * dma, unsigned int hwp) -{ - int cnt; - int swptr; - unsigned long flags; - - spin_lock_irqsave(&dma->s->lock,flags); - swptr = dma->writeptr; - cnt = (hwp - swptr); - - if (cnt < 0) { - cnt = ((dma->s->fragsize<<1) - swptr); - } - spin_unlock_irqrestore(&dma->s->lock,flags); - return cnt; -} - -inline int rme96xx_getispace(struct dmabuf * dma, unsigned int hwp) -{ - int cnt; - int swptr; - unsigned long flags; - - spin_lock_irqsave(&dma->s->lock,flags); - swptr = dma->readptr; - cnt = (hwp - swptr); - - if (cnt < 0) { - cnt = ((dma->s->fragsize<<1) - swptr); - } - spin_unlock_irqrestore(&dma->s->lock,flags); - return cnt; -} - - -inline int rme96xx_copyfromuser(struct dmabuf* dma,const char* buffer,int count,int hop) -{ - int swptr = dma->writeptr; - switch (dma->format) { - case AFMT_S32_BLOCKED: - { - char* buf = (char*)buffer; - int cnt = count/dma->outchannels; - int i; - for (i=0;i < dma->outchannels;i++) { - char* hwbuf =(char*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES]; - hwbuf+=swptr; - - if (copy_from_user(hwbuf,buf, cnt)) - return -1; - buf+=hop; - } - swptr+=cnt; - break; - } - case AFMT_S16_LE: - { - int i,j; - int cnt = count/dma->outchannels; - for (i=0;i < dma->outchannels + dma->mono;i++) { - short* sbuf = (short*)buffer + i*(!dma->mono); - short* hwbuf =(short*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES]; - hwbuf+=(swptr>>1); - for (j=0;j<(cnt>>1);j++) { - hwbuf++; /* skip the low 16 bits */ - __get_user(*hwbuf++,sbuf++); - sbuf+=(dma->outchannels-1); - } - } - swptr += (cnt<<1); - break; - } - default: - printk(RME_MESS" unsupported format\n"); - return -1; - } /* switch */ - - swptr&=((dma->s->fragsize<<1) -1); - dma->writeptr = swptr; - - return 0; -} - -/* The count argument is the number of bytes */ -inline int rme96xx_copytouser(struct dmabuf* dma,const char* buffer,int count,int hop) -{ - int swptr = dma->readptr; - switch (dma->format) { - case AFMT_S32_BLOCKED: - { - char* buf = (char*)buffer; - int cnt = count/dma->inchannels; - int i; - - for (i=0;i < dma->inchannels;i++) { - char* hwbuf =(char*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; - hwbuf+=swptr; - - if (copy_to_user(buf,hwbuf,cnt)) - return -1; - buf+=hop; - } - swptr+=cnt; - break; - } - case AFMT_S16_LE: - { - int i,j; - int cnt = count/dma->inchannels; - for (i=0;i < dma->inchannels;i++) { - short* sbuf = (short*)buffer + i; - short* hwbuf =(short*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; - hwbuf+=(swptr>>1); - for (j=0;j<(cnt>>1);j++) { - hwbuf++; - __put_user(*hwbuf++,sbuf++); - sbuf+=(dma->inchannels-1); - } - } - swptr += (cnt<<1); - break; - } - default: - printk(RME_MESS" unsupported format\n"); - return -1; - } /* switch */ - - swptr&=((dma->s->fragsize<<1) -1); - dma->readptr = swptr; - return 0; -} - - -static void rme96xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - int i; - rme96xx_info *s = (rme96xx_info *)dev_id; - struct dmabuf *db; - u32 status; - unsigned long flags; - - status = readl(s->iobase + RME96xx_status_register); - if (!(status & RME96xx_IRQ)) { - return; - } - - spin_lock_irqsave(&s->lock,flags); - writel(0,s->iobase + RME96xx_irq_clear); - - s->hwbufid = (status & RME96xx_buffer_id)>>26; - if ((status & 0xffc0) <= 256) s->hwptr = 0; - for(i=0;idma[i]); - if(db->started > 0) - wake_up(&(db->wait)); - } - spin_unlock_irqrestore(&s->lock,flags); -} - - - -/*---------------------------------------------------------------------------- - PCI detection and module initialization stuff - ----------------------------------------------------------------------------*/ - -void* busmaster_malloc(int size) { - int pg; /* 2 s exponent of memory size */ - char *buf; - - DBG(printk("kernel malloc pages ..\n")); - - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - - buf = (char *) __get_free_pages(GFP_KERNEL | GFP_DMA, pg); - - if (buf) { - struct page* page, *last_page; - - page = virt_to_page(buf); - last_page = virt_to_page(buf + (1 << pg)); - DBG(printk("setting reserved bit\n")); - while (page < last_page) { - SetPageReserved(page); - page++; - } - return buf; - } - DBG(printk("allocated %ld",(long)buf)); - return NULL; -} - -void busmaster_free(void* ptr,int size) { - int pg; - struct page* page, *last_page; - - if (ptr == NULL) - return; - - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - - page = virt_to_page(ptr); - last_page = page + (1 << pg); - while (page < last_page) { - ClearPageReserved(page); - page++; - } - DBG(printk("freeing pages\n")); - free_pages((unsigned long) ptr, pg); - DBG(printk("done\n")); -} - -/* initialize those parts of the info structure which are not pci detectable resources */ - -static int rme96xx_dmabuf_init(rme96xx_info * s,struct dmabuf* dma,int ioffset,int ooffset) { - - init_MUTEX(&dma->open_sem); - init_waitqueue_head(&dma->open_wait); - init_waitqueue_head(&dma->wait); - dma->s = s; - dma->error = 0; - - dma->format = AFMT_S32_BLOCKED; - dma->formatshift = 0; - dma->inchannels = dma->outchannels = 1; - dma->inoffset = ioffset; - dma->outoffset = ooffset; - - dma->opened=0; - dma->started=0; - dma->mmapped=0; - dma->open_mode=0; - dma->mono=0; - - rme96xx_clearbufs(dma); - return 0; -} - - -int rme96xx_init(rme96xx_info* s) -{ - int i; - DBG(printk(__FUNCTION__"\n")); - numcards++; - - s->magic = RME96xx_MAGIC; - - spin_lock_init(&s->lock); - - COMM ("setup busmaster memory") - s->recbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); - s->playbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); - - if (!s->recbuf || !s->playbuf) { - printk(KERN_ERR RME_MESS" Unable to allocate busmaster memory\n"); - return -ENODEV; - } - - COMM ("setting rec and playbuffers") - - writel((u32) virt_to_bus(s->recbuf),s->iobase + RME96xx_rec_buffer); - writel((u32) virt_to_bus(s->playbuf),s->iobase + RME96xx_play_buffer); - - COMM ("initializing control register") - rme96xx_unset_ctrl(s,0xffffffff); - rme96xx_set_ctrl(s,RME96xx_ctrl_init); - - - COMM ("setup devices") - for (i=0;i < devices;i++) { - struct dmabuf * dma = &s->dma[i]; - rme96xx_dmabuf_init(s,dma,2*i,2*i); - } - - s->started = 0; - rme96xx_setlatency(s,7); - - printk(KERN_INFO RME_MESS" card %d initialized\n",numcards); - return 0; -} - - -/* open uses this to figure out which device was opened .. this seems to be - unnecessary complex */ - -static LIST_HEAD(devs); - -static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) -{ - int i; - rme96xx_info *s; - - DBG(printk(__FUNCTION__"\n")); - - if (pcidev->irq == 0) - return -1; - if (!pci_dma_supported(pcidev, 0xffffffff)) { - printk(KERN_WARNING RME_MESS" architecture does not support 32bit PCI busmaster DMA\n"); - return -1; - } - if (!(s = kmalloc(sizeof(rme96xx_info), GFP_KERNEL))) { - printk(KERN_WARNING RME_MESS" out of memory\n"); - return -1; - } - memset(s, 0, sizeof(rme96xx_info)); - - s->pcidev = pcidev; - s->iobase = ioremap(pci_resource_start(pcidev, 0),RME96xx_IO_EXTENT); - s->irq = pcidev->irq; - - DBG(printk("remapped iobase: %lx irq %d\n",(long)s->iobase,s->irq)); - - if (pci_enable_device(pcidev)) - goto err_irq; - if (request_irq(s->irq, rme96xx_interrupt, SA_SHIRQ, "es1370", s)) { - printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq); - goto err_irq; - } - - /* initialize the card */ - - i = 0; - if (rme96xx_init(s) < 0) { - printk(KERN_ERR RME_MESS" initialization failed\n"); - goto err_devices; - } - for (i=0;idspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1)) < 0) - goto err_devices; - } - - if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1)) < 0) - goto err_devices; - - pci_set_drvdata(pcidev, s); - pcidev->dma_mask = 0xffffffff; /* ????? */ - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - - DBG(printk("initialization successful\n")); - return 0; - - /* error handler */ - err_devices: - while (i--) - unregister_sound_dsp(s->dspnum[i]); - free_irq(s->irq,s); - err_irq: - kfree(s); - return -1; -} - - -static void __devinit rme96xx_remove(struct pci_dev *dev) -{ - int i; - rme96xx_info *s = pci_get_drvdata(dev); - - if (!s) { - printk(KERN_ERR"device structure not valid\n"); - return ; - } - - if (s->started) rme96xx_startcard(s,0); - - i = devices; - while (i) { - i--; - unregister_sound_dsp(s->dspnum[i]); - } - - unregister_sound_mixer(s->mixer); -/* synchronize_irq(); This call got lost somehow ? */ - free_irq(s->irq,s); - busmaster_free(s->recbuf,RME96xx_DMA_MAX_SIZE_ALL); - busmaster_free(s->playbuf,RME96xx_DMA_MAX_SIZE_ALL); - kfree(s); - pci_set_drvdata(dev, NULL); -} - - -#ifndef PCI_VENDOR_ID_RME -#define PCI_VENDOR_ID_RME 0x10ee -#endif -#ifndef PCI_DEVICE_ID_RME9652 -#define PCI_DEVICE_ID_RME9652 0x3fc4 -#endif -#ifndef PCI_ANY_ID -#define PCI_ANY_ID 0 -#endif - -static struct pci_device_id id_table[] __devinitdata = { - { PCI_VENDOR_ID_RME, PCI_DEVICE_ID_RME9652, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver rme96xx_driver = { - name: "rme96xx", - id_table: id_table, - probe: rme96xx_probe, - remove: rme96xx_remove -}; - -static int __init init_rme96xx(void) -{ - - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - printk(KERN_INFO RME_MESS" version "RMEVERSION" time " __TIME__ " " __DATE__ "\n"); - printk(KERN_INFO RME_MESS" reserving %d dsp device(s)\n",devices); - numcards = 0; - return pci_module_init(&rme96xx_driver); -} - -static void __exit cleanup_rme96xx(void) -{ - printk(KERN_INFO RME_MESS" unloading\n"); - pci_unregister_driver(&rme96xx_driver); -} - -module_init(init_rme96xx); -module_exit(cleanup_rme96xx); - - - - - -/*-------------------------------------------------------------------------- - Implementation of file operations ----------------------------------------------------------------------------*/ - -#define RME96xx_FMT (AFMT_S16_LE|AFMT_U8|AFMT_S32_BLOCKED) - - -static int rme96xx_ioctl(struct inode *in, struct file *file, - unsigned int cmd, unsigned long arg) -{ - - - struct dmabuf * dma = (struct dmabuf *)file->private_data; - rme96xx_info *s = dma->s; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val = 0; - - VALIDATE_STATE(s); - - DBG(printk("ioctl %ud\n",cmd)); - - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_SYNC: -#if 0 - if (file->f_mode & FMODE_WRITE) - return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); -#endif - 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: -// rme96xx_clearbufs(dma); - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val >= 0) { -/* generally it's not a problem if we change the speed - if (dma->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) - return -EINVAL; -*/ - spin_lock_irqsave(&s->lock, flags); - - switch (val) { - case 44100: - case 88200: - rme96xx_unset_ctrl(s,RME96xx_freq); - break; - case 48000: - case 96000: - rme96xx_set_ctrl(s,RME96xx_freq); - break; - default: - rme96xx_unset_ctrl(s,RME96xx_freq); - val = 44100; - } - if (val > 50000) - rme96xx_set_ctrl(s,RME96xx_DS); - else - rme96xx_unset_ctrl(s,RME96xx_DS); - s->rate = val; - spin_unlock_irqrestore(&s->lock, flags); - } - DBG(printk("speed set to %d\n",val)); - return put_user(val, (int *)arg); - - case SNDCTL_DSP_STEREO: /* this plays a mono file on two channels */ - if (get_user(val, (int *)arg)) - return -EFAULT; - - if (!val) { - DBG(printk("setting to mono\n")); - dma->mono=1; - dma->inchannels = 1; - dma->outchannels = 1; - } - else { - DBG(printk("setting to stereo\n")); - dma->mono = 0; - dma->inchannels = 2; - dma->outchannels = 2; - } - return 0; - case SNDCTL_DSP_CHANNELS: - /* remember to check for resonable offset/channel pairs here */ - if (get_user(val, (int *)arg)) - return -EFAULT; - - if (file->f_mode & FMODE_WRITE) { - if (val > 0 && (dma->outoffset + val) <= RME96xx_CHANNELS_PER_CARD) - dma->outchannels = val; - else - dma->outchannels = val = 2; - DBG(printk("setting to outchannels %d\n",val)); - } - if (file->f_mode & FMODE_READ) { - if (val > 0 && (dma->inoffset + val) <= RME96xx_CHANNELS_PER_CARD) - dma->inchannels = val; - else - dma->inchannels = val = 2; - DBG(printk("setting to inchannels %d\n",val)); - } - - dma->mono=0; - - return put_user(val, (int *)arg); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(RME96xx_FMT, (int *)arg); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - DBG(printk("setting to format %x\n",val)); - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != AFMT_QUERY) { - if (val & RME96xx_FMT) - dma->format = val; - switch (dma->format) { - case AFMT_S16_LE: - dma->formatshift=1; - break; - case AFMT_S32_BLOCKED: - dma->formatshift=0; - break; - } - } - return put_user(dma->format, (int *)arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; -#if 0 - if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) - val |= PCM_ENABLE_OUTPUT; -#endif - return put_user(val, (int *)arg); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, (int *)arg)) - return -EFAULT; -#if 0 - 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_dac2.ready && (ret = prog_dmabuf_dac2(s))) - return ret; - start_dac2(s); - } else - stop_dac2(s); - } -#endif - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - - val = rme96xx_gethwptr(dma->s,0); - - - count = rme96xx_getospace(dma,val); - if (!s->started) count = s->fragsize*2; - abinfo.fragsize =(s->fragsize*dma->outchannels)>>dma->formatshift; - abinfo.bytes = (count*dma->outchannels)>>dma->formatshift; - abinfo.fragstotal = 2; - abinfo.fragments = (count > s->fragsize); - - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - - val = rme96xx_gethwptr(dma->s,0); - - count = rme96xx_getispace(dma,val); - - abinfo.fragsize = (s->fragsize*dma->inchannels)>>dma->formatshift; - abinfo.bytes = (count*dma->inchannels)>>dma->formatshift;; - abinfo.fragstotal = 2; - abinfo.fragments = count > s->fragsize; - 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: /* What shold this exactly do ? , - ATM it is just abinfo.bytes */ - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - - val = rme96xx_gethwptr(dma->s,0); - count = val - dma->readptr; - if (count < 0) - count += s->fragsize<<1; - - return put_user(count, (int *)arg); - - -/* check out how to use mmaped mode (can only be blocked !!!) */ - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - val = rme96xx_gethwptr(dma->s,0); - spin_lock_irqsave(&s->lock,flags); - cinfo.bytes = s->fragsize<<1;; - count = val - dma->readptr; - if (count < 0) - count += s->fragsize<<1; - - cinfo.blocks = (count > s->fragsize); - cinfo.ptr = val; - if (dma->mmapped) - dma->readptr &= s->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_READ)) - return -EINVAL; - val = rme96xx_gethwptr(dma->s,0); - spin_lock_irqsave(&s->lock,flags); - cinfo.bytes = s->fragsize<<1;; - count = val - dma->writeptr; - if (count < 0) - count += s->fragsize<<1; - - cinfo.blocks = (count > s->fragsize); - cinfo.ptr = val; - if (dma->mmapped) - dma->writeptr &= s->fragsize<<1; - spin_unlock_irqrestore(&s->lock,flags); - return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); - case SNDCTL_DSP_GETBLKSIZE: - return put_user(s->fragsize, (int *)arg); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) - return -EFAULT; - val&=0xffff; - val -= 7; - if (val < 0) val = 0; - if (val > 7) val = 7; - rme96xx_setlatency(s,val); - return 0; - - case SNDCTL_DSP_SUBDIVIDE: -#if 0 - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac2.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; - if (file->f_mode & FMODE_WRITE) - s->dma_dac2.subdivision = val; -#endif - return 0; - - case SOUND_PCM_READ_RATE: - return put_user(s->rate, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - return put_user(dma->outchannels, (int *)arg); - - case SOUND_PCM_READ_BITS: - switch (dma->format) { - case AFMT_S32_BLOCKED: - val = 32; - break; - case AFMT_S16_LE: - val = 16; - break; - } - return put_user(val, (int *)arg); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - - } - - - return -ENODEV; -} - - - -static int rme96xx_open(struct inode *in, struct file *f) -{ - int minor = MINOR(in->i_rdev); - struct list_head *list; - int devnum = ((minor-3)/16) % devices; /* default = 0 */ - rme96xx_info *s; - struct dmabuf* dma; - DECLARE_WAITQUEUE(wait, current); - - DBG(printk("device num %d open\n",devnum)); - -/* ??? */ - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, rme96xx_info, devs); - if (!((s->dspnum[devnum] ^ minor) & ~0xf)) - break; - } - VALIDATE_STATE(s); -/* ??? */ - - dma = &s->dma[devnum]; - f->private_data = dma; - /* wait for device to become free */ - down(&s->dma[devnum].open_sem); - while (dma->open_mode & f->f_mode) { - if (f->f_flags & O_NONBLOCK) { - up(&dma->open_sem); - return -EBUSY; - } - add_wait_queue(&dma->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - up(&dma->open_sem); - schedule(); - remove_wait_queue(&dma->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - down(&dma->open_sem); - } - - COMM ("hardware open") - - if (!s->dma[devnum].opened) rme96xx_dmabuf_init(dma->s,dma,dma->inoffset,dma->outoffset); - - s->dma[devnum].open_mode |= (f->f_mode & (FMODE_READ | FMODE_WRITE)); - s->dma[devnum].opened = 1; - up(&s->dma[devnum].open_sem); - - DBG(printk("device num %d open finished\n",devnum)); - return 0; -} - -static int rme96xx_release(struct inode *in, struct file *file) -{ - struct dmabuf * dma = (struct dmabuf*) file->private_data; - int hwp; - DBG(printk(__FUNCTION__"\n")); - - COMM ("draining") - if (dma->open_mode & FMODE_WRITE) { -#if 0 /* Why doesn't this work with some cards ?? */ - hwp = rme96xx_gethwptr(dma->s,0); - while (rme96xx_getospace(dma,hwp)) { - interruptible_sleep_on(&(dma->wait)); - hwp = rme96xx_gethwptr(dma->s,0); - } -#endif - rme96xx_clearbufs(dma); - } - - dma->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - - if (!(dma->open_mode & (FMODE_READ|FMODE_WRITE))) { - dma->opened = 0; - if (dma->s->started) rme96xx_startcard(dma->s,1); - } - - wake_up(&dma->open_wait); - up(&dma->open_sem); - - return 0; -} - - -static ssize_t rme96xx_write(struct file *file, const char *buffer, - size_t count, loff_t *ppos) -{ - struct dmabuf *dma = (struct dmabuf *)file->private_data; - ssize_t ret = 0; - int cnt; /* number of bytes from "buffer" that will/can be used */ - int hop = count/dma->outchannels; - int hwp; - int exact = (file->f_flags & O_NONBLOCK); - - - if(dma == NULL || (dma->s) == NULL) - return -ENXIO; - - if (ppos != &file->f_pos) - return -ESPIPE; - - if (dma->mmapped || !dma->opened) - return -ENXIO; - - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - - if (! (dma->open_mode & FMODE_WRITE)) - return -ENXIO; - - if (!dma->s->started) rme96xx_startcard(dma->s,exact); - hwp = rme96xx_gethwptr(dma->s,0); - - if(!(dma->started)){ - COMM ("first write") - - dma->readptr = hwp; - dma->writeptr = hwp; - dma->started = 1; - COMM ("first write done") - } - - while (count > 0) { - cnt = rme96xx_getospace(dma,hwp); - cnt>>=dma->formatshift; - cnt*=dma->outchannels; - if (cnt > count) - cnt = count; - - if (cnt != 0) { - if (rme96xx_copyfromuser(dma,buffer,cnt,hop)) - return ret ? ret : -EFAULT; - count -= cnt; - buffer += cnt; - ret += cnt; - if (count == 0) return ret; - } - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - - if ((hwp - dma->writeptr) <= 0) { - interruptible_sleep_on(&(dma->wait)); - - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - } - - hwp = rme96xx_gethwptr(dma->s,exact); - - }; /* count > 0 */ - - return ret; -} - -static ssize_t rme96xx_read(struct file *file, char *buffer,size_t count, loff_t *ppos) -{ - struct dmabuf *dma = (struct dmabuf *)file->private_data; - ssize_t ret = 0; - int cnt; - int hop = count/dma->inchannels; - int hwp; - int exact = (file->f_flags & O_NONBLOCK); - - - if(dma == NULL || (dma->s) == NULL) - return -ENXIO; - - if (ppos != &file->f_pos) - return -ESPIPE; - - if (dma->mmapped || !dma->opened) - return -ENXIO; - - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - - if (! (dma->open_mode & FMODE_READ)) - return -ENXIO; - - if (count > ((dma->s->fragsize*dma->inchannels)>>dma->formatshift)) - return -EFAULT; - - if (!dma->s->started) rme96xx_startcard(dma->s,exact); - hwp = rme96xx_gethwptr(dma->s,0); - - if(!(dma->started)){ - COMM ("first read") - - dma->writeptr = hwp; - dma->readptr = hwp; - dma->started = 1; - } - - while (count > 0) { - cnt = rme96xx_getispace(dma,hwp); - cnt>>=dma->formatshift; - cnt*=dma->inchannels; - - if (cnt > count) - cnt = count; - - if (cnt != 0) { - - if (rme96xx_copytouser(dma,buffer,cnt,hop)) - return ret ? ret : -EFAULT; - - count -= cnt; - buffer += cnt; - ret += cnt; - if (count == 0) return ret; - } - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - - if ((hwp - dma->readptr) <= 0) { - interruptible_sleep_on(&(dma->wait)); - - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - } - hwp = rme96xx_gethwptr(dma->s,exact); - - }; /* count > 0 */ - - return ret; -} - -static int rm96xx_mmap(struct file *file, struct vm_area_struct *vma) { - struct dmabuf *dma = (struct dmabuf *)file->private_data; - rme96xx_info* s = dma->s; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - - if (vma->vm_pgoff != 0) { - unlock_kernel(); - return -EINVAL; - } - size = vma->vm_end - vma->vm_start; - if (size > RME96xx_DMA_MAX_SIZE) { - unlock_kernel(); - return -EINVAL; - } - - - if (vma->vm_flags & VM_WRITE) { - if (!s->started) rme96xx_startcard(s,1); - - if (remap_page_range(vma, vma->vm_start, virt_to_phys(s->playbuf + dma->outoffset*RME96xx_DMA_MAX_SIZE), size, vma->vm_page_prot)) { - unlock_kernel(); - return -EAGAIN; - } - } - else if (vma->vm_flags & VM_READ) { - if (!s->started) rme96xx_startcard(s,1); - if (remap_page_range(vma, vma->vm_start, virt_to_phys(s->playbuf + dma->inoffset*RME96xx_DMA_MAX_SIZE), size, vma->vm_page_prot)) { - unlock_kernel(); - return -EAGAIN; - } - } else { - unlock_kernel(); - return -EINVAL; - } - - -/* this is the mapping */ - - dma->mmapped = 1; - unlock_kernel(); - return 0; -} - -static unsigned int rme96xx_poll(struct file *file, struct poll_table_struct *wait) -{ - struct dmabuf *dma = (struct dmabuf *)file->private_data; - rme96xx_info* s = dma->s; - unsigned int mask = 0; - unsigned int hwp,cnt; - - DBG(printk("rme96xx poll_wait ...\n")); - VALIDATE_STATE(s); - - if (!s->started) { - mask |= POLLOUT | POLLWRNORM; - } - poll_wait(file, &dma->wait, wait); - - hwp = rme96xx_gethwptr(dma->s,0); - - DBG(printk("rme96xx poll: ..cnt %d > %d\n",cnt,s->fragsize)); - - cnt = rme96xx_getispace(dma,hwp); - - if (file->f_mode & FMODE_READ) - if (cnt > 0) - mask |= POLLIN | POLLRDNORM; - - - - cnt = rme96xx_getospace(dma,hwp); - - if (file->f_mode & FMODE_WRITE) - if (cnt > 0) - mask |= POLLOUT | POLLWRNORM; - - -// printk("rme96xx poll_wait ...%d > %d\n",rme96xx_getospace(dma,hwp),rme96xx_getispace(dma,hwp)); - - return mask; -} - - -static struct file_operations rme96xx_audio_fops = { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - owner: THIS_MODULE, -#endif - read: rme96xx_read, - write: rme96xx_write, - poll: rme96xx_poll, - ioctl: rme96xx_ioctl, - mmap: rm96xx_mmap, - open: rme96xx_open, - release: rme96xx_release -}; - -static int rme96xx_mixer_open(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - struct list_head *list; - rme96xx_info *s; - - COMM ("mixer open"); - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, rme96xx_info, devs); - if (s->mixer== minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - - COMM ("mixer opened") - return 0; -} - -static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - rme96xx_info *s = (rme96xx_info *)file->private_data; - u32 status; - - status = readl(s->iobase + RME96xx_status_register); - - VALIDATE_STATE(s); - if (cmd == SOUND_MIXER_PRIVATE1) { - rme_mixer mixer; - copy_from_user(&mixer,(void*)arg,sizeof(mixer)); - - if (file->f_mode & FMODE_WRITE) { - s->dma[mixer.devnr].outoffset = mixer.o_offset; - s->dma[mixer.devnr].inoffset = mixer.i_offset; - } - - mixer.o_offset = s->dma[mixer.devnr].outoffset; - mixer.i_offset = s->dma[mixer.devnr].inoffset; - - return copy_to_user((void *)arg, &mixer, sizeof(mixer)) ? -EFAULT : 0; - } - if (cmd == SOUND_MIXER_PRIVATE2) { - return put_user(status, (int *)arg); - } - if (cmd == SOUND_MIXER_PRIVATE3) { - u32 control; - copy_from_user(&control,(void*)arg,sizeof(control)); - if (file->f_mode & FMODE_WRITE) { - s->control_register = control; - writel(control,s->iobase + RME96xx_control_register); - } - - return put_user(s->control_register, (int *)arg); - } - return -1; -} - - - -static int rme96xx_mixer_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static /*const*/ struct file_operations rme96xx_mixer_fops = { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - owner: THIS_MODULE, -#endif - ioctl: rme96xx_mixer_ioctl, - open: rme96xx_mixer_open, - release: rme96xx_mixer_release, -}; diff -Nru a/drivers/sound/rme96xx.h b/drivers/sound/rme96xx.h --- a/drivers/sound/rme96xx.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,61 +0,0 @@ -/* (C) 2000 Guenter Geiger - with copy/pastes from the driver of Winfried Ritsch -*/ - - -#ifndef AFMT_S32_BLOCKED -#define AFMT_S32_BLOCKED 0x0000400 -#endif - -#ifndef AFMT_S16_BLOCKED -#define AFMT_S16_BLOCKED 0x0000800 -#endif - - -typedef struct rme_status { - unsigned int irq:1; /* high or low */ - unsigned int lockmask:3; /* ADAT1, ADAT2, ADAT3 */ - unsigned int sr48:1; /* current sample rate */ - unsigned int wclock:1; /* wordclock used ? */ - unsigned int bufpoint:10; - - unsigned int syncmask:3; /* ADAT1, ADAT2, ADAT3 */ - unsigned int doublespeed:1; - unsigned int tc_busy:1; - unsigned int tc_out:1; - unsigned int crystalrate:3; - unsigned int spdif_error:1; - unsigned int bufid:1; - unsigned int tc_valid:1; -} rme_status_t; - - -typedef struct rme_control { - unsigned int start:1; - unsigned int latency:3; - - unsigned int master:1; - unsigned int ie:1; - unsigned int sr48:1; - unsigned int spare:1; - - unsigned int doublespeed:1; - unsigned int pro:1; - unsigned int emphasis:1; - unsigned int dolby:1; - - unsigned int opt_out:1; - unsigned int wordclock:1; - unsigned int spdif_in:2; - - unsigned int sync_ref:2; -} rme_ctrl_t; - - -typedef struct _rme_mixer { - int i_offset; - int o_offset; - int devnr; - int spare[8]; -} rme_mixer; - diff -Nru a/drivers/sound/sb.h b/drivers/sound/sb.h --- a/drivers/sound/sb.h Tue Feb 19 18:08:56 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,187 +0,0 @@ -#define DSP_RESET (devc->base + 0x6) -#define DSP_READ (devc->base + 0xA) -#define DSP_WRITE (devc->base + 0xC) -#define DSP_COMMAND (devc->base + 0xC) -#define DSP_STATUS (devc->base + 0xC) -#define DSP_DATA_AVAIL (devc->base + 0xE) -#define DSP_DATA_AVL16 (devc->base + 0xF) -#define MIXER_ADDR (devc->base + 0x4) -#define MIXER_DATA (devc->base + 0x5) -#define OPL3_LEFT (devc->base + 0x0) -#define OPL3_RIGHT (devc->base + 0x2) -#define OPL3_BOTH (devc->base + 0x8) -/* DSP Commands */ - -#define DSP_CMD_SPKON 0xD1 -#define DSP_CMD_SPKOFF 0xD3 -#define DSP_CMD_DMAON 0xD0 -#define DSP_CMD_DMAOFF 0xD4 - -#define IMODE_NONE 0 -#define IMODE_OUTPUT PCM_ENABLE_OUTPUT -#define IMODE_INPUT PCM_ENABLE_INPUT -#define IMODE_INIT 3 -#define IMODE_MIDI 4 - -#define NORMAL_MIDI 0 -#define UART_MIDI 1 - - -/* - * Device models - */ -#define MDL_NONE 0 -#define MDL_SB1 1 /* SB1.0 or 1.5 */ -#define MDL_SB2 2 /* SB2.0 */ -#define MDL_SB201 3 /* SB2.01 */ -#define MDL_SBPRO 4 /* SB Pro */ -#define MDL_SB16 5 /* SB16/32/AWE */ -#define MDL_SBPNP 6 /* SB16/32/AWE PnP */ -#define MDL_JAZZ 10 /* Media Vision Jazz16 */ -#define MDL_SMW 11 /* Logitech SoundMan Wave (Jazz16) */ -#define MDL_ESS 12 /* ESS ES688 and ES1688 */ -#define MDL_AZTECH 13 /* Aztech Sound Galaxy family */ -#define MDL_ES1868MIDI 14 /* MIDI port of ESS1868 */ -#define MDL_AEDSP 15 /* Audio Excel DSP 16 */ -#define MDL_ESSPCI 16 /* ESS PCI card */ -#define MDL_YMPCI 17 /* Yamaha PCI sb in emulation */ - -#define SUBMDL_ALS007 42 /* ALS-007 differs from SB16 only in mixer */ - /* register assignment */ -#define SUBMDL_ALS100 43 /* ALS-100 allows sampling rates of up */ - /* to 48kHz */ - -/* - * Config flags - */ -#define SB_NO_MIDI 0x00000001 -#define SB_NO_MIXER 0x00000002 -#define SB_NO_AUDIO 0x00000004 -#define SB_NO_RECORDING 0x00000008 /* No audio recording */ -#define SB_MIDI_ONLY (SB_NO_AUDIO|SB_NO_MIXER) -#define SB_PCI_IRQ 0x00000010 /* PCI shared IRQ */ - -struct mixer_def { - unsigned int regno: 8; - unsigned int bitoffs:4; - unsigned int nbits:4; -}; - -typedef struct mixer_def mixer_tab[32][2]; -typedef struct mixer_def mixer_ent; - -struct sb_module_options -{ - int esstype; /* ESS chip type */ - int acer; /* Do acer notebook init? */ - int sm_games; /* Logitech soundman games? */ -}; - -typedef struct sb_devc { - int dev; - - /* Hardware parameters */ - int *osp; - int minor, major; - int type; - int model, submodel; - int caps; -# define SBCAP_STEREO 0x00000001 -# define SBCAP_16BITS 0x00000002 - - /* Hardware resources */ - int base; - int irq; - int dma8, dma16; - - int pcibase; /* For ESS Maestro etc */ - - /* State variables */ - int opened; - /* new audio fields for full duplex support */ - int fullduplex; - int duplex; - int speed, bits, channels; - volatile int irq_ok; - volatile int intr_active, irq_mode; - /* duplicate audio fields for full duplex support */ - volatile int intr_active_16, irq_mode_16; - - /* Mixer fields */ - int *levels; - mixer_tab *iomap; - int mixer_caps, recmask, outmask, supported_devices; - int supported_rec_devices, supported_out_devices; - int my_mixerdev; - int sbmixnum; - - /* Audio fields */ - unsigned long trg_buf; - int trigger_bits; - int trg_bytes; - int trg_intrflag; - int trg_restart; - /* duplicate audio fields for full duplex support */ - unsigned long trg_buf_16; - int trigger_bits_16; - int trg_bytes_16; - int trg_intrflag_16; - int trg_restart_16; - - unsigned char tconst; - - /* MIDI fields */ - int my_mididev; - int input_opened; - int midi_broken; - void (*midi_input_intr) (int dev, unsigned char data); - void *midi_irq_cookie; /* IRQ cookie for the midi */ - - spinlock_t lock; - - struct sb_module_options sbmo; /* Module options */ - - } sb_devc; - -/* - * PCI card types - */ - -#define SB_PCI_ESSMAESTRO 1 /* ESS Maestro Legacy */ -#define SB_PCI_YAMAHA 2 /* Yamaha Legacy */ - -/* - * Functions - */ - -int sb_dsp_command (sb_devc *devc, unsigned char val); -int sb_dsp_get_byte(sb_devc * devc); -int sb_dsp_reset (sb_devc *devc); -void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value); -unsigned int sb_getmixer (sb_devc *devc, unsigned int port); -int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo); -int sb_dsp_init (struct address_info *hw_config, struct module *owner); -void sb_dsp_unload(struct address_info *hw_config, int sbmpu); -int sb_mixer_init(sb_devc *devc, struct module *owner); -void sb_mixer_unload(sb_devc *devc); -void sb_mixer_set_stereo (sb_devc *devc, int mode); -void smw_mixer_init(sb_devc *devc); -void sb_dsp_midi_init (sb_devc *devc, struct module *owner); -void sb_audio_init (sb_devc *devc, char *name, struct module *owner); -void sb_midi_interrupt (sb_devc *devc); -void sb_chgmixer (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val); -int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right); - -int sb_audio_open(int dev, int mode); -void sb_audio_close(int dev); - -extern sb_devc *last_sb; - -/* From sb_common.c */ -void sb_dsp_disable_midi(int port); -void sb_dsp_disable_recording(int port); -int probe_sbmpu (struct address_info *hw_config, struct module *owner); -void unload_sbmpu (struct address_info *hw_config); - -void unload_sb16(struct address_info *hw_info); -void unload_sb16midi(struct address_info *hw_info); diff -Nru a/drivers/sound/sb_audio.c b/drivers/sound/sb_audio.c --- a/drivers/sound/sb_audio.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1093 +0,0 @@ -/* - * sound/sb_audio.c - * - * Audio routines for Sound Blaster compatible cards. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes - * Alan Cox : Formatting and clean ups - * - * Status - * Mostly working. Weird uart bug causing irq storms - * - * Daniel J. Rodriksson: Changes to make sb16 work full duplex. - * Maybe other 16 bit cards in this code could behave - * the same. - * Chris Rankin: Use spinlocks instead of CLI/STI - */ - -#include - -#include "sound_config.h" - -#include "sb_mixer.h" -#include "sb.h" - -#include "sb_ess.h" - -int sb_audio_open(int dev, int mode) -{ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - { - printk(KERN_ERR "Sound Blaster: incomplete initialization.\n"); - return -ENXIO; - } - if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ) - { - if (mode == OPEN_READ) - return -EPERM; - } - spin_lock_irqsave(&devc->lock, flags); - if (devc->opened) - { - spin_unlock_irqrestore(&devc->lock, flags); - return -EBUSY; - } - if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex) - { - if (sound_open_dma(devc->dma16, "Sound Blaster 16 bit")) - { - spin_unlock_irqrestore(&devc->lock, flags); - return -EBUSY; - } - } - devc->opened = mode; - spin_unlock_irqrestore(&devc->lock, flags); - - devc->irq_mode = IMODE_NONE; - devc->irq_mode_16 = IMODE_NONE; - devc->fullduplex = devc->duplex && - ((mode & OPEN_READ) && (mode & OPEN_WRITE)); - sb_dsp_reset(devc); - - /* At first glance this check isn't enough, some ESS chips might not - * have a RECLEV. However if they don't common_mixer_set will refuse - * cause devc->iomap has no register mapping for RECLEV - */ - if (devc->model == MDL_ESS) ess_mixer_reload (devc, SOUND_MIXER_RECLEV); - - /* The ALS007 seems to require that the DSP be removed from the output */ - /* in order for recording to be activated properly. This is done by */ - /* setting the appropriate bits of the output control register 4ch to */ - /* zero. This code assumes that the output control registers are not */ - /* used anywhere else and therefore the DSP bits are *always* ON for */ - /* output and OFF for sampling. */ - - if (devc->submodel == SUBMDL_ALS007) - { - if (mode & OPEN_READ) - sb_setmixer(devc,ALS007_OUTPUT_CTRL2, - sb_getmixer(devc,ALS007_OUTPUT_CTRL2) & 0xf9); - else - sb_setmixer(devc,ALS007_OUTPUT_CTRL2, - sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06); - } - return 0; -} - -void sb_audio_close(int dev) -{ - sb_devc *devc = audio_devs[dev]->devc; - - /* fix things if mmap turned off fullduplex */ - if(devc->duplex - && !devc->fullduplex - && (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE)) - { - struct dma_buffparms *dmap_temp; - dmap_temp = audio_devs[dev]->dmap_out; - audio_devs[dev]->dmap_out = audio_devs[dev]->dmap_in; - audio_devs[dev]->dmap_in = dmap_temp; - } - audio_devs[dev]->dmap_out->dma = devc->dma8; - audio_devs[dev]->dmap_in->dma = ( devc->duplex ) ? - devc->dma16 : devc->dma8; - - if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex) - sound_close_dma(devc->dma16); - - /* For ALS007, turn DSP output back on if closing the device for read */ - - if ((devc->submodel == SUBMDL_ALS007) && (devc->opened & OPEN_READ)) - { - sb_setmixer(devc,ALS007_OUTPUT_CTRL2, - sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06); - } - devc->opened = 0; -} - -static void sb_set_output_parms(int dev, unsigned long buf, int nr_bytes, - int intrflag) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (!devc->fullduplex || devc->bits == AFMT_S16_LE) - { - devc->trg_buf = buf; - devc->trg_bytes = nr_bytes; - devc->trg_intrflag = intrflag; - devc->irq_mode = IMODE_OUTPUT; - } - else - { - devc->trg_buf_16 = buf; - devc->trg_bytes_16 = nr_bytes; - devc->trg_intrflag_16 = intrflag; - devc->irq_mode_16 = IMODE_OUTPUT; - } -} - -static void sb_set_input_parms(int dev, unsigned long buf, int count, int intrflag) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (!devc->fullduplex || devc->bits != AFMT_S16_LE) - { - devc->trg_buf = buf; - devc->trg_bytes = count; - devc->trg_intrflag = intrflag; - devc->irq_mode = IMODE_INPUT; - } - else - { - devc->trg_buf_16 = buf; - devc->trg_bytes_16 = count; - devc->trg_intrflag_16 = intrflag; - devc->irq_mode_16 = IMODE_INPUT; - } -} - -/* - * SB1.x compatible routines - */ - -static void sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_OUTPUT; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x14)) /* 8 bit DAC using DMA */ - { - sb_dsp_command(devc, (unsigned char) (count & 0xff)); - sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); - } - else - printk(KERN_WARNING "Sound Blaster: unable to start DAC.\n"); - spin_unlock_irqrestore(&devc->lock, flags); - devc->intr_active = 1; -} - -static void sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - - /* - * Start a DMA input to the buffer pointed by dmaqtail - */ - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_INPUT; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x24)) /* 8 bit ADC using DMA */ - { - sb_dsp_command(devc, (unsigned char) (count & 0xff)); - sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); - } - else - printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); - spin_unlock_irqrestore(&devc->lock, flags); - - devc->intr_active = 1; -} - -static void sb1_audio_trigger(int dev, int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - - bits &= devc->irq_mode; - - if (!bits) - sb_dsp_command(devc, 0xd0); /* Halt DMA */ - else - { - switch (devc->irq_mode) - { - case IMODE_INPUT: - sb1_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - - case IMODE_OUTPUT: - sb1_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - } - } - devc->trigger_bits = bits; -} - -static int sb1_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x40)) - sb_dsp_command(devc, devc->tconst); - sb_dsp_command(devc, DSP_CMD_SPKOFF); - spin_unlock_irqrestore(&devc->lock, flags); - - devc->trigger_bits = 0; - return 0; -} - -static int sb1_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x40)) - sb_dsp_command(devc, devc->tconst); - sb_dsp_command(devc, DSP_CMD_SPKON); - spin_unlock_irqrestore(&devc->lock, flags); - devc->trigger_bits = 0; - return 0; -} - -static int sb1_audio_set_speed(int dev, int speed) -{ - int max_speed = 23000; - sb_devc *devc = audio_devs[dev]->devc; - int tmp; - - if (devc->opened & OPEN_READ) - max_speed = 13000; - - if (speed > 0) - { - if (speed < 4000) - speed = 4000; - - if (speed > max_speed) - speed = max_speed; - - devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; - tmp = 256 - devc->tconst; - speed = (1000000 + tmp / 2) / tmp; - - devc->speed = speed; - } - return devc->speed; -} - -static short sb1_audio_set_channels(int dev, short channels) -{ - sb_devc *devc = audio_devs[dev]->devc; - return devc->channels = 1; -} - -static unsigned int sb1_audio_set_bits(int dev, unsigned int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - return devc->bits = 8; -} - -static void sb1_audio_halt_xfer(int dev) -{ - unsigned long flags; - sb_devc *devc = audio_devs[dev]->devc; - - spin_lock_irqsave(&devc->lock, flags); - sb_dsp_reset(devc); - spin_unlock_irqrestore(&devc->lock, flags); -} - -/* - * SB 2.0 and SB 2.01 compatible routines - */ - -static void sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes, - int intrflag) -{ - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - unsigned char cmd; - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_OUTPUT; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ - { - sb_dsp_command(devc, (unsigned char) (count & 0xff)); - sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); - - if (devc->speed * devc->channels <= 23000) - cmd = 0x1c; /* 8 bit PCM output */ - else - cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */ - - if (!sb_dsp_command(devc, cmd)) - printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); - } - else - printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); - spin_unlock_irqrestore(&devc->lock, flags); - devc->intr_active = 1; -} - -static void sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - unsigned char cmd; - - /* - * Start a DMA input to the buffer pointed by dmaqtail - */ - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_INPUT; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ - { - sb_dsp_command(devc, (unsigned char) (count & 0xff)); - sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); - - if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000)) - cmd = 0x2c; /* 8 bit PCM input */ - else - cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */ - - if (!sb_dsp_command(devc, cmd)) - printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); - } - else - printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); - spin_unlock_irqrestore(&devc->lock, flags); - devc->intr_active = 1; -} - -static void sb20_audio_trigger(int dev, int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - bits &= devc->irq_mode; - - if (!bits) - sb_dsp_command(devc, 0xd0); /* Halt DMA */ - else - { - switch (devc->irq_mode) - { - case IMODE_INPUT: - sb20_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - - case IMODE_OUTPUT: - sb20_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - } - } - devc->trigger_bits = bits; -} - -/* - * SB2.01 specific speed setup - */ - -static int sb201_audio_set_speed(int dev, int speed) -{ - sb_devc *devc = audio_devs[dev]->devc; - int tmp; - int s = speed * devc->channels; - - if (speed > 0) - { - if (speed < 4000) - speed = 4000; - if (speed > 44100) - speed = 44100; - if (devc->opened & OPEN_READ && speed > 15000) - speed = 15000; - devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff; - tmp = 256 - devc->tconst; - speed = ((1000000 + tmp / 2) / tmp) / devc->channels; - - devc->speed = speed; - } - return devc->speed; -} - -/* - * SB Pro specific routines - */ - -static int sbpro_audio_prepare_for_input(int dev, int bsize, int bcount) -{ /* For SB Pro and Jazz16 */ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - unsigned char bits = 0; - - if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) - audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = - devc->bits == 16 ? devc->dma16 : devc->dma8; - - if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) - if (devc->bits == AFMT_S16_LE) - bits = 0x04; /* 16 bit mode */ - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x40)) - sb_dsp_command(devc, devc->tconst); - sb_dsp_command(devc, DSP_CMD_SPKOFF); - if (devc->channels == 1) - sb_dsp_command(devc, 0xa0 | bits); /* Mono input */ - else - sb_dsp_command(devc, 0xa8 | bits); /* Stereo input */ - spin_unlock_irqrestore(&devc->lock, flags); - - devc->trigger_bits = 0; - return 0; -} - -static int sbpro_audio_prepare_for_output(int dev, int bsize, int bcount) -{ /* For SB Pro and Jazz16 */ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - unsigned char tmp; - unsigned char bits = 0; - - if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) - audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = devc->bits == 16 ? devc->dma16 : devc->dma8; - if (devc->model == MDL_SBPRO) - sb_mixer_set_stereo(devc, devc->channels == 2); - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x40)) - sb_dsp_command(devc, devc->tconst); - sb_dsp_command(devc, DSP_CMD_SPKON); - - if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) - { - if (devc->bits == AFMT_S16_LE) - bits = 0x04; /* 16 bit mode */ - - if (devc->channels == 1) - sb_dsp_command(devc, 0xa0 | bits); /* Mono output */ - else - sb_dsp_command(devc, 0xa8 | bits); /* Stereo output */ - } - else - { - tmp = sb_getmixer(devc, 0x0e); - if (devc->channels == 1) - tmp &= ~0x02; - else - tmp |= 0x02; - sb_setmixer(devc, 0x0e, tmp); - } - spin_unlock_irqrestore(&devc->lock, flags); - devc->trigger_bits = 0; - return 0; -} - -static int sbpro_audio_set_speed(int dev, int speed) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (speed > 0) - { - if (speed < 4000) - speed = 4000; - if (speed > 44100) - speed = 44100; - if (devc->channels > 1 && speed > 22050) - speed = 22050; - sb201_audio_set_speed(dev, speed); - } - return devc->speed; -} - -static short sbpro_audio_set_channels(int dev, short channels) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (channels == 1 || channels == 2) - { - if (channels != devc->channels) - { - devc->channels = channels; - if (devc->model == MDL_SBPRO && devc->channels == 2) - sbpro_audio_set_speed(dev, devc->speed); - } - } - return devc->channels; -} - -static int jazz16_audio_set_speed(int dev, int speed) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (speed > 0) - { - int tmp; - int s = speed * devc->channels; - - if (speed < 5000) - speed = 5000; - if (speed > 44100) - speed = 44100; - - devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff; - - tmp = 256 - devc->tconst; - speed = ((1000000 + tmp / 2) / tmp) / devc->channels; - - devc->speed = speed; - } - return devc->speed; -} - -/* - * SB16 specific routines - */ - -static int sb16_audio_set_speed(int dev, int speed) -{ - sb_devc *devc = audio_devs[dev]->devc; - int max_speed = devc->submodel == SUBMDL_ALS100 ? 48000 : 44100; - - if (speed > 0) - { - if (speed < 5000) /* which of these */ - speed = 4000; /* is correct ??? */ - - if (speed > max_speed) - speed = max_speed; - - devc->speed = speed; - } - return devc->speed; -} - -static unsigned int sb16_audio_set_bits(int dev, unsigned int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (bits != 0) - { - if (bits == AFMT_U8 || bits == AFMT_S16_LE) - devc->bits = bits; - else - devc->bits = AFMT_U8; - } - - return devc->bits; -} - -static int sb16_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (!devc->fullduplex) - { - audio_devs[dev]->dmap_out->dma = - audio_devs[dev]->dmap_in->dma = - devc->bits == AFMT_S16_LE ? - devc->dma16 : devc->dma8; - } - else if (devc->bits == AFMT_S16_LE) - { - audio_devs[dev]->dmap_out->dma = devc->dma8; - audio_devs[dev]->dmap_in->dma = devc->dma16; - } - else - { - audio_devs[dev]->dmap_out->dma = devc->dma16; - audio_devs[dev]->dmap_in->dma = devc->dma8; - } - - devc->trigger_bits = 0; - return 0; -} - -static int sb16_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (!devc->fullduplex) - { - audio_devs[dev]->dmap_out->dma = - audio_devs[dev]->dmap_in->dma = - devc->bits == AFMT_S16_LE ? - devc->dma16 : devc->dma8; - } - else if (devc->bits == AFMT_S16_LE) - { - audio_devs[dev]->dmap_out->dma = devc->dma8; - audio_devs[dev]->dmap_in->dma = devc->dma16; - } - else - { - audio_devs[dev]->dmap_out->dma = devc->dma16; - audio_devs[dev]->dmap_in->dma = devc->dma8; - } - - devc->trigger_bits = 0; - return 0; -} - -static void sb16_audio_output_block(int dev, unsigned long buf, int count, - int intrflag) -{ - unsigned long flags, cnt; - sb_devc *devc = audio_devs[dev]->devc; - unsigned long bits; - - if (!devc->fullduplex || devc->bits == AFMT_S16_LE) - { - devc->irq_mode = IMODE_OUTPUT; - devc->intr_active = 1; - } - else - { - devc->irq_mode_16 = IMODE_OUTPUT; - devc->intr_active_16 = 1; - } - - /* save value */ - spin_lock_irqsave(&devc->lock, flags); - bits = devc->bits; - if (devc->fullduplex) - devc->bits = (devc->bits == AFMT_S16_LE) ? - AFMT_U8 : AFMT_S16_LE; - spin_unlock_irqrestore(&devc->lock, flags); - - cnt = count; - if (devc->bits == AFMT_S16_LE) - cnt >>= 1; - cnt--; - - spin_lock_irqsave(&devc->lock, flags); - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - - sb_dsp_command(devc, 0x41); - sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff)); - sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff)); - - sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6)); - sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + - (devc->bits == AFMT_S16_LE ? 0x10 : 0))); - sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); - sb_dsp_command(devc, (unsigned char) (cnt >> 8)); - - /* restore real value after all programming */ - devc->bits = bits; - spin_unlock_irqrestore(&devc->lock, flags); -} - - -/* - * This fails on the Cyrix MediaGX. If you don't have the DMA enabled - * before the first sample arrives it locks up. However even if you - * do enable the DMA in time you just get DMA timeouts and missing - * interrupts and stuff, so for now I've not bothered fixing this either. - */ - -static void sb16_audio_start_input(int dev, unsigned long buf, int count, int intrflag) -{ - unsigned long flags, cnt; - sb_devc *devc = audio_devs[dev]->devc; - - if (!devc->fullduplex || devc->bits != AFMT_S16_LE) - { - devc->irq_mode = IMODE_INPUT; - devc->intr_active = 1; - } - else - { - devc->irq_mode_16 = IMODE_INPUT; - devc->intr_active_16 = 1; - } - - cnt = count; - if (devc->bits == AFMT_S16_LE) - cnt >>= 1; - cnt--; - - spin_lock_irqsave(&devc->lock, flags); - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - - sb_dsp_command(devc, 0x42); - sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff)); - sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff)); - - sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce)); - sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + - (devc->bits == AFMT_S16_LE ? 0x10 : 0))); - sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); - sb_dsp_command(devc, (unsigned char) (cnt >> 8)); - - spin_unlock_irqrestore(&devc->lock, flags); -} - -static void sb16_audio_trigger(int dev, int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - - int bits_16 = bits & devc->irq_mode_16; - bits &= devc->irq_mode; - - if (!bits && !bits_16) - sb_dsp_command(devc, 0xd0); /* Halt DMA */ - else - { - if (bits) - { - switch (devc->irq_mode) - { - case IMODE_INPUT: - sb16_audio_start_input(dev, - devc->trg_buf, - devc->trg_bytes, - devc->trg_intrflag); - break; - - case IMODE_OUTPUT: - sb16_audio_output_block(dev, - devc->trg_buf, - devc->trg_bytes, - devc->trg_intrflag); - break; - } - } - if (bits_16) - { - switch (devc->irq_mode_16) - { - case IMODE_INPUT: - sb16_audio_start_input(dev, - devc->trg_buf_16, - devc->trg_bytes_16, - devc->trg_intrflag_16); - break; - - case IMODE_OUTPUT: - sb16_audio_output_block(dev, - devc->trg_buf_16, - devc->trg_bytes_16, - devc->trg_intrflag_16); - break; - } - } - } - - devc->trigger_bits = bits | bits_16; -} - -static unsigned char lbuf8[2048]; -static signed short *lbuf16 = (signed short *)lbuf8; -#define LBUFCOPYSIZE 1024 -static void -sb16_copy_from_user(int dev, - char *localbuf, int localoffs, - const char *userbuf, int useroffs, - int max_in, int max_out, - int *used, int *returned, - int len) -{ - sb_devc *devc = audio_devs[dev]->devc; - int i, c, p, locallen; - unsigned char *buf8; - signed short *buf16; - - /* if not duplex no conversion */ - if (!devc->fullduplex) - { - copy_from_user (localbuf + localoffs, userbuf + useroffs, len); - *used = len; - *returned = len; - } - else if (devc->bits == AFMT_S16_LE) - { - /* 16 -> 8 */ - /* max_in >> 1, max number of samples in ( 16 bits ) */ - /* max_out, max number of samples out ( 8 bits ) */ - /* len, number of samples that will be taken ( 16 bits )*/ - /* c, count of samples remaining in buffer ( 16 bits )*/ - /* p, count of samples already processed ( 16 bits )*/ - len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1); - c = len; - p = 0; - buf8 = (unsigned char *)(localbuf + localoffs); - while (c) - { - locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c); - /* << 1 in order to get 16 bit samples */ - copy_from_user (lbuf16, - userbuf+useroffs + (p << 1), - locallen << 1); - for (i = 0; i < locallen; i++) - { - buf8[p+i] = ~((lbuf16[i] >> 8) & 0xff) ^ 0x80; - } - c -= locallen; p += locallen; - } - /* used = ( samples * 16 bits size ) */ - *used = len << 1; - /* returned = ( samples * 8 bits size ) */ - *returned = len; - } - else - { - /* 8 -> 16 */ - /* max_in, max number of samples in ( 8 bits ) */ - /* max_out >> 1, max number of samples out ( 16 bits ) */ - /* len, number of samples that will be taken ( 8 bits )*/ - /* c, count of samples remaining in buffer ( 8 bits )*/ - /* p, count of samples already processed ( 8 bits )*/ - len = max_in > (max_out >> 1) ? (max_out >> 1) : max_in; - c = len; - p = 0; - buf16 = (signed short *)(localbuf + localoffs); - while (c) - { - locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c); - copy_from_user (lbuf8, - userbuf+useroffs + p, - locallen); - for (i = 0; i < locallen; i++) - { - buf16[p+i] = (~lbuf8[i] ^ 0x80) << 8; - } - c -= locallen; p += locallen; - } - /* used = ( samples * 8 bits size ) */ - *used = len; - /* returned = ( samples * 16 bits size ) */ - *returned = len << 1; - } -} - -static void -sb16_audio_mmap(int dev) -{ - sb_devc *devc = audio_devs[dev]->devc; - devc->fullduplex = 0; -} - -static struct audio_driver sb1_audio_driver = /* SB1.x */ -{ - owner: THIS_MODULE, - open: sb_audio_open, - close: sb_audio_close, - output_block: sb_set_output_parms, - start_input: sb_set_input_parms, - prepare_for_input: sb1_audio_prepare_for_input, - prepare_for_output: sb1_audio_prepare_for_output, - halt_io: sb1_audio_halt_xfer, - trigger: sb1_audio_trigger, - set_speed: sb1_audio_set_speed, - set_bits: sb1_audio_set_bits, - set_channels: sb1_audio_set_channels -}; - -static struct audio_driver sb20_audio_driver = /* SB2.0 */ -{ - owner: THIS_MODULE, - open: sb_audio_open, - close: sb_audio_close, - output_block: sb_set_output_parms, - start_input: sb_set_input_parms, - prepare_for_input: sb1_audio_prepare_for_input, - prepare_for_output: sb1_audio_prepare_for_output, - halt_io: sb1_audio_halt_xfer, - trigger: sb20_audio_trigger, - set_speed: sb1_audio_set_speed, - set_bits: sb1_audio_set_bits, - set_channels: sb1_audio_set_channels -}; - -static struct audio_driver sb201_audio_driver = /* SB2.01 */ -{ - owner: THIS_MODULE, - open: sb_audio_open, - close: sb_audio_close, - output_block: sb_set_output_parms, - start_input: sb_set_input_parms, - prepare_for_input: sb1_audio_prepare_for_input, - prepare_for_output: sb1_audio_prepare_for_output, - halt_io: sb1_audio_halt_xfer, - trigger: sb20_audio_trigger, - set_speed: sb201_audio_set_speed, - set_bits: sb1_audio_set_bits, - set_channels: sb1_audio_set_channels -}; - -static struct audio_driver sbpro_audio_driver = /* SB Pro */ -{ - owner: THIS_MODULE, - open: sb_audio_open, - close: sb_audio_close, - output_block: sb_set_output_parms, - start_input: sb_set_input_parms, - prepare_for_input: sbpro_audio_prepare_for_input, - prepare_for_output: sbpro_audio_prepare_for_output, - halt_io: sb1_audio_halt_xfer, - trigger: sb20_audio_trigger, - set_speed: sbpro_audio_set_speed, - set_bits: sb1_audio_set_bits, - set_channels: sbpro_audio_set_channels -}; - -static struct audio_driver jazz16_audio_driver = /* Jazz16 and SM Wave */ -{ - owner: THIS_MODULE, - open: sb_audio_open, - close: sb_audio_close, - output_block: sb_set_output_parms, - start_input: sb_set_input_parms, - prepare_for_input: sbpro_audio_prepare_for_input, - prepare_for_output: sbpro_audio_prepare_for_output, - halt_io: sb1_audio_halt_xfer, - trigger: sb20_audio_trigger, - set_speed: jazz16_audio_set_speed, - set_bits: sb16_audio_set_bits, - set_channels: sbpro_audio_set_channels -}; - -static struct audio_driver sb16_audio_driver = /* SB16 */ -{ - owner: THIS_MODULE, - open: sb_audio_open, - close: sb_audio_close, - output_block: sb_set_output_parms, - start_input: sb_set_input_parms, - prepare_for_input: sb16_audio_prepare_for_input, - prepare_for_output: sb16_audio_prepare_for_output, - halt_io: sb1_audio_halt_xfer, - copy_user: sb16_copy_from_user, - trigger: sb16_audio_trigger, - set_speed: sb16_audio_set_speed, - set_bits: sb16_audio_set_bits, - set_channels: sbpro_audio_set_channels, - mmap: sb16_audio_mmap -}; - -void sb_audio_init(sb_devc * devc, char *name, struct module *owner) -{ - int audio_flags = 0; - int format_mask = AFMT_U8; - - struct audio_driver *driver = &sb1_audio_driver; - - switch (devc->model) - { - case MDL_SB1: /* SB1.0 or SB 1.5 */ - DDB(printk("Will use standard SB1.x driver\n")); - audio_flags = DMA_HARDSTOP; - break; - - case MDL_SB2: - DDB(printk("Will use SB2.0 driver\n")); - audio_flags = DMA_AUTOMODE; - driver = &sb20_audio_driver; - break; - - case MDL_SB201: - DDB(printk("Will use SB2.01 (high speed) driver\n")); - audio_flags = DMA_AUTOMODE; - driver = &sb201_audio_driver; - break; - - case MDL_JAZZ: - case MDL_SMW: - DDB(printk("Will use Jazz16 driver\n")); - audio_flags = DMA_AUTOMODE; - format_mask |= AFMT_S16_LE; - driver = &jazz16_audio_driver; - break; - - case MDL_ESS: - DDB(printk("Will use ESS ES688/1688 driver\n")); - driver = ess_audio_init (devc, &audio_flags, &format_mask); - break; - - case MDL_SB16: - DDB(printk("Will use SB16 driver\n")); - audio_flags = DMA_AUTOMODE; - format_mask |= AFMT_S16_LE; - if (devc->dma8 != devc->dma16 && devc->dma16 != -1) - { - audio_flags |= DMA_DUPLEX; - devc->duplex = 1; - } - driver = &sb16_audio_driver; - break; - - default: - DDB(printk("Will use SB Pro driver\n")); - audio_flags = DMA_AUTOMODE; - driver = &sbpro_audio_driver; - } - - if (owner) - driver->owner = owner; - - if ((devc->dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, - name,driver, sizeof(struct audio_driver), - audio_flags, format_mask, devc, - devc->dma8, - devc->duplex ? devc->dma16 : devc->dma8)) < 0) - { - printk(KERN_ERR "Sound Blaster: unable to install audio.\n"); - return; - } - audio_devs[devc->dev]->mixer_dev = devc->my_mixerdev; - audio_devs[devc->dev]->min_fragment = 5; -} diff -Nru a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c --- a/drivers/sound/sb_card.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1035 +0,0 @@ -/* - * sound/sb_card.c - * - * Detection routine for the Sound Blaster cards. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * 26-11-1999 Patched to compile without ISA PnP support in the - * kernel - Daniel Stone (tamriel@ductape.net) - * - * 06-01-2000 Refined and bugfixed ISA PnP support, added - * CMI 8330 support - Alessandro Zummo - * - * 18-01-2000 Separated sb_card and sb_common - * Jeff Garzik - * - * 04-02-2000 Added Soundblaster AWE 64 PnP support, isapnpjump - * Alessandro Zummo - * - * 11-02-2000 Added Soundblaster AWE 32 PnP support, refined PnP code - * Alessandro Zummo - * - * 13-02-2000 Hopefully fixed awe/sb16 related bugs, code cleanup - * Alessandro Zummo - * - * 13-03-2000 Added some more cards, thanks to Torsten Werner. - * Removed joystick and wavetable code, there are better places for them. - * Code cleanup plus some fixes. - * Alessandro Zummo - * - * 26-03-2000 Fixed acer, esstype and sm_games module options. - * Alessandro Zummo - * - * 12-04-2000 ISAPnP cleanup, reorg, fixes, and multiple card support. - * Thanks to Gaël Quéri and Alessandro Zummo for testing and fixes. - * Paul E. Laufer - * - * 06-05-2000 added another card. Daniel M. Newman - * - * 25-05-2000 Added Creative SB AWE64 Gold (CTL00B2). - * Pål-Kristian Engstad - * - * 12-08-2000 Added Creative SB32 PnP (CTL009F). - * Kasatenko Ivan Alex. - * - * 21-09-2000 Got rid of attach_sbmpu - * Arnaldo Carvalho de Melo - * - * 28-10-2000 Added pnplegacy support - * Daniel Church - * - * 01-10-2001 Added a new flavor of Creative SB AWE64 PnP (CTL00E9). - * Jerome Cornet - */ - -#include -#include -#include -#include -#include - -#include "sound_config.h" - -#include "sb_mixer.h" -#include "sb.h" - -#ifdef __ISAPNP__ -#define SB_CARDS_MAX 5 -#else -#define SB_CARDS_MAX 1 -#endif - -static int sbmpu[SB_CARDS_MAX] = {0}; -static int sb_cards_num = 0; - -extern void *smw_free; - -/* - * Note DMA2 of -1 has the right meaning in the SB16 driver as well - * as here. It will cause either an error if it is needed or a fallback - * to the 8bit channel. - */ - -static int __initdata mpu_io = 0; -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma16 = -1; /* Set this for modules that need it */ -static int __initdata type = 0; /* Can set this to a specific card type */ -static int __initdata esstype = 0; /* ESS chip type */ -static int __initdata acer = 0; /* Do acer notebook init? */ -static int __initdata sm_games = 0; /* Logitech soundman games? */ - -static void __init attach_sb_card(struct address_info *hw_config) -{ - if(!sb_dsp_init(hw_config, THIS_MODULE)) - hw_config->slots[0] = -1; -} - -static int __init probe_sb(struct address_info *hw_config) -{ - struct sb_module_options sbmo; - - if (hw_config->io_base == -1 || hw_config->dma == -1 || hw_config->irq == -1) - { - printk(KERN_ERR "sb: I/O, IRQ, and DMA are mandatory\n"); - return -EINVAL; - } - -#ifdef CONFIG_MCA - /* MCA code added by ZP Gu (zpg@castle.net) */ - if (MCA_bus) { /* no multiple REPLY card probing */ - int slot; - u8 pos2, pos3, pos4; - - slot = mca_find_adapter( 0x5138, 0 ); - if( slot == MCA_NOTFOUND ) - { - slot = mca_find_adapter( 0x5137, 0 ); - - if (slot != MCA_NOTFOUND) - mca_set_adapter_name( slot, "REPLY SB16 & SCSI Adapter" ); - } - else - { - mca_set_adapter_name( slot, "REPLY SB16 Adapter" ); - } - - if (slot != MCA_NOTFOUND) - { - mca_mark_as_used(slot); - pos2 = mca_read_stored_pos( slot, 2 ); - pos3 = mca_read_stored_pos( slot, 3 ); - pos4 = mca_read_stored_pos( slot, 4 ); - - if (pos2 & 0x4) - { - /* enabled? */ - static unsigned short irq[] = { 0, 5, 7, 10 }; - /* - static unsigned short midiaddr[] = {0, 0x330, 0, 0x300 }; - */ - - hw_config->io_base = 0x220 + 0x20 * (pos2 >> 6); - hw_config->irq = irq[(pos4 >> 5) & 0x3]; - hw_config->dma = pos3 & 0xf; - /* Reply ADF wrong on High DMA, pos[1] should start w/ 00 */ - hw_config->dma2 = (pos3 >> 4) & 0x3; - if (hw_config->dma2 == 0) - hw_config->dma2 = hw_config->dma; - else - hw_config->dma2 += 4; - /* - hw_config->driver_use_2 = midiaddr[(pos2 >> 3) & 0x3]; - */ - - printk(KERN_INFO "sb: Reply MCA SB at slot=%d \ -iobase=0x%x irq=%d lo_dma=%d hi_dma=%d\n", - slot+1, - hw_config->io_base, hw_config->irq, - hw_config->dma, hw_config->dma2); - } - else - { - printk (KERN_INFO "sb: Reply SB Base I/O address disabled\n"); - } - } - } -#endif - - /* Setup extra module options */ - - sbmo.acer = acer; - sbmo.sm_games = sm_games; - sbmo.esstype = esstype; - - return sb_dsp_detect(hw_config, 0, 0, &sbmo); -} - -static void __exit unload_sb(struct address_info *hw_config, int card) -{ - if(hw_config->slots[0]!=-1) - sb_dsp_unload(hw_config, sbmpu[card]); -} - -static struct address_info cfg[SB_CARDS_MAX]; -static struct address_info cfg_mpu[SB_CARDS_MAX]; - -struct pci_dev *sb_dev[SB_CARDS_MAX] = {NULL}, - *mpu_dev[SB_CARDS_MAX] = {NULL}, - *opl_dev[SB_CARDS_MAX] = {NULL}; - - -#ifdef __ISAPNP__ -static int isapnp = 1; -static int isapnpjump = 0; -static int multiple = 1; -static int pnplegacy = 0; -static int reverse = 0; -static int uart401 = 0; - -static int audio_activated[SB_CARDS_MAX] = {0}; -static int mpu_activated[SB_CARDS_MAX] = {0}; -static int opl_activated[SB_CARDS_MAX] = {0}; -#else -static int isapnp = 0; -static int multiple = 0; -static int pnplegacy = 0; -#endif - -MODULE_DESCRIPTION("Soundblaster driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM(io, "i"); -MODULE_PARM(irq, "i"); -MODULE_PARM(dma, "i"); -MODULE_PARM(dma16, "i"); -MODULE_PARM(mpu_io, "i"); -MODULE_PARM(type, "i"); -MODULE_PARM(sm_games, "i"); -MODULE_PARM(esstype, "i"); -MODULE_PARM(acer, "i"); - -#ifdef __ISAPNP__ -MODULE_PARM(isapnp, "i"); -MODULE_PARM(isapnpjump, "i"); -MODULE_PARM(multiple, "i"); -MODULE_PARM(pnplegacy, "i"); -MODULE_PARM(reverse, "i"); -MODULE_PARM(uart401, "i"); -MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled"); -MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke."); -MODULE_PARM_DESC(multiple, "When set to 0, will not search for multiple cards"); -MODULE_PARM_DESC(pnplegacy, "When set to 1, will search for a legacy SB card along with any PnP cards."); -MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order"); -MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable the mpu on some clones"); -#endif - -MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); -MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)"); -MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)"); -MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)"); -MODULE_PARM_DESC(mpu_io, "Mpu base address"); -MODULE_PARM_DESC(type, "You can set this to specific card type"); -MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games"); -MODULE_PARM_DESC(esstype, "ESS chip type"); -MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks"); - -#ifdef __ISAPNP__ - -/* Please add new entries at the end of the table */ -static struct { - char *name; - unsigned short card_vendor, card_device, - audio_vendor, audio_function, - mpu_vendor, mpu_function, - opl_vendor, opl_function; - short dma, dma2, mpu_io, mpu_irq; /* see sb_init() */ -} sb_isapnp_list[] __initdata = { - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00ed), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster 16", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster Vibra16S", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster Vibra16C", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster Vibra16CL", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster Vibra16X", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 32", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Creative SB32 PnP", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009F), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64 Gold", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64 Gold", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), - 0,0,0,0, - 0,1,1,-1}, - {"Sound Blaster AWE 64", - ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), - 0,0,0,0, - 0,1,1,-1}, - {"ESS 1688", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1868", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1868", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1869 PnP AudioDrive", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1869", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1878", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), - 0,0,0,0, - 0,1,2,-1}, - {"ESS 1879", - ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), - 0,0,0,0, - 0,1,2,-1}, - {"CMI 8330 SoundPRO", - ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), - 0,1,0,-1}, - {"Diamond DT0197H", - ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 0,-1,0,0}, - {"ALS007", - ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 0,-1,0,0}, - {"ALS100", - ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 1,0,0,0}, - {"ALS110", - ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 1,0,0,0}, - {"ALS120", - ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 1,0,0,0}, - {"ALS200", - ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 1,0,0,0}, - {"RTL3000", - ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), - 1,0,0,0}, - {0} -}; - -static struct isapnp_device_id id_table[] __devinitdata = { - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00ed), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009F), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, - - { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), - ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), 0 }, - - { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), - ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), 0 }, - - { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020), 0 }, - - { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - - { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), 0 }, - - { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), 0 }, - - { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, - {0} -}; - -MODULE_DEVICE_TABLE(isapnp, id_table); - -static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) -{ - int err; - - /* Device already active? Let's use it */ - if(dev->active) - return(dev); - - if((err = dev->activate(dev)) < 0) { - printk(KERN_ERR "sb: %s %s config failed (out of resources?)[%d]\n", devname, resname, err); - - dev->deactivate(dev); - - return(NULL); - } - return(dev); -} - -static struct pci_dev *sb_init(struct pci_bus *bus, struct address_info *hw_config, struct address_info *mpu_config, int slot, int card) -{ - - /* Configure Audio device */ - if((sb_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].audio_vendor, sb_isapnp_list[slot].audio_function, NULL))) - { - int ret; - ret = sb_dev[card]->prepare(sb_dev[card]); - /* If device is active, assume configured with /proc/isapnp - * and use anyway. Some other way to check this? */ - if(ret && ret != -EBUSY) { - printk(KERN_ERR "sb: ISAPnP found device that could not be autoconfigured.\n"); - return(NULL); - } - if(ret == -EBUSY) - audio_activated[card] = 1; - - if((sb_dev[card] = activate_dev(sb_isapnp_list[slot].name, "sb", sb_dev[card]))) - { - hw_config->io_base = sb_dev[card]->resource[0].start; - hw_config->irq = sb_dev[card]->irq_resource[0].start; - hw_config->dma = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma].start; - if(sb_isapnp_list[slot].dma2 != -1) - hw_config->dma2 = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma2].start; - else - hw_config->dma2 = -1; - } else - return(NULL); - } else - return(NULL); - - /* Cards with separate OPL3 device (ALS, CMI, etc.) - * This is just to activate the device so the OPL module can use it */ - if(sb_isapnp_list[slot].opl_vendor || sb_isapnp_list[slot].opl_function) { - if((opl_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].opl_vendor, sb_isapnp_list[slot].opl_function, NULL))) { - int ret = opl_dev[card]->prepare(opl_dev[card]); - /* If device is active, assume configured with - * /proc/isapnp and use anyway */ - if(ret && ret != -EBUSY) { - printk(KERN_ERR "sb: OPL device could not be autoconfigured.\n"); - return(sb_dev[card]); - } - if(ret == -EBUSY) - opl_activated[card] = 1; - - /* Some have irq and dma for opl. the opl3 driver wont - * use 'em so don't configure 'em and hope it works -PEL */ - opl_dev[card]->irq_resource[0].flags = 0; - opl_dev[card]->dma_resource[0].flags = 0; - - opl_dev[card] = activate_dev(sb_isapnp_list[slot].name, "opl3", opl_dev[card]); - } else - printk(KERN_ERR "sb: %s isapnp panic: opl3 device not found\n", sb_isapnp_list[slot].name); - } - - /* Cards with MPU as part of Audio device (CTL and ESS) */ - if(!sb_isapnp_list[slot].mpu_vendor) { - mpu_config->io_base = sb_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start; - return(sb_dev[card]); - } - - /* Cards with separate MPU device (ALS, CMI, etc.) */ - if(!uart401) - return(sb_dev[card]); - if((mpu_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].mpu_vendor, sb_isapnp_list[slot].mpu_function, NULL))) - { - int ret = mpu_dev[card]->prepare(mpu_dev[card]); - /* If device is active, assume configured with /proc/isapnp - * and use anyway */ - if(ret && ret != -EBUSY) { - printk(KERN_ERR "sb: MPU device could not be autoconfigured.\n"); - return(sb_dev[card]); - } - if(ret == -EBUSY) - mpu_activated[card] = 1; - - /* Some cards ask for irq but don't need them - azummo */ - if(sb_isapnp_list[slot].mpu_irq == -1) - mpu_dev[card]->irq_resource[0].flags = 0; - - if((mpu_dev[card] = activate_dev(sb_isapnp_list[slot].name, "mpu", mpu_dev[card]))) { - mpu_config->io_base = mpu_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start; - if(sb_isapnp_list[slot].mpu_irq != -1) - mpu_config->irq = mpu_dev[card]->irq_resource[sb_isapnp_list[slot].mpu_irq].start; - } - } - else - printk(KERN_ERR "sb: %s isapnp panic: mpu not found\n", sb_isapnp_list[slot].name); - - return(sb_dev[card]); -} - -static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, int slot, int card) -{ - char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name; - - printk(KERN_INFO "sb: %s detected\n", busname); - - /* Initialize this baby. */ - - if(sb_init(bus, hw_config, mpu_config, slot, card)) { - /* We got it. */ - - printk(KERN_NOTICE "sb: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", - busname, - hw_config->io_base, hw_config->irq, hw_config->dma, - hw_config->dma2); - return 1; - } - else - printk(KERN_INFO "sb: Failed to initialize %s\n", busname); - - return 0; -} - -static int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config, int card) -{ - static int first = 1; - int i; - - /* Count entries in sb_isapnp_list */ - for (i = 0; sb_isapnp_list[i].card_vendor != 0; i++); - i--; - - /* Check and adjust isapnpjump */ - if( isapnpjump < 0 || isapnpjump > i) { - isapnpjump = reverse ? i : 0; - printk(KERN_ERR "sb: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump); - } - - if(!first || !reverse) - i = isapnpjump; - first = 0; - while(sb_isapnp_list[i].card_vendor != 0) { - static struct pci_bus *bus = NULL; - - while ((bus = isapnp_find_card( - sb_isapnp_list[i].card_vendor, - sb_isapnp_list[i].card_device, - bus))) { - - if(sb_isapnp_init(hw_config, mpu_config, bus, i, card)) { - isapnpjump = i; /* start next search from here */ - return 0; - } - } - i += reverse ? -1 : 1; - } - - return -ENODEV; -} -#endif - -static int __init init_sb(void) -{ - int card, max = (multiple && isapnp) ? SB_CARDS_MAX : 1; - - printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - for(card = 0; card < max; card++, sb_cards_num++) { -#ifdef __ISAPNP__ - /* Please remember that even with __ISAPNP__ defined one - * should still be able to disable PNP support for this - * single driver! */ - if((!pnplegacy||card>0) && isapnp && (sb_isapnp_probe(&cfg[card], &cfg_mpu[card], card) < 0) ) { - if(!sb_cards_num) { - /* Found no ISAPnP cards, so check for a non-pnp - * card and set the detection loop for 1 cycle - */ - printk(KERN_NOTICE "sb: No ISAPnP cards found, trying standard ones...\n"); - isapnp = 0; - max = 1; - } else - /* found all the ISAPnP cards so exit the - * detection loop. */ - break; - } -#endif - - if(!isapnp || (pnplegacy&&card==0)) { - cfg[card].io_base = io; - cfg[card].irq = irq; - cfg[card].dma = dma; - cfg[card].dma2 = dma16; - } - - cfg[card].card_subtype = type; - - if (!probe_sb(&cfg[card])) { - /* if one or more cards already registered, don't - * return an error but print a warning. Note, this - * should never really happen unless the hardware - * or ISAPnP screwed up. */ - if (sb_cards_num) { - printk(KERN_WARNING "sb.c: There was a " \ - "problem probing one of your SoundBlaster " \ - "ISAPnP soundcards. Continuing.\n"); - card--; - sb_cards_num--; - continue; - } else if(pnplegacy && isapnp) { - printk(KERN_NOTICE "sb: No legacy SoundBlaster cards " \ - "found. Continuing with PnP detection.\n"); - pnplegacy=0; - card--; - continue; - } else - return -ENODEV; - } - attach_sb_card(&cfg[card]); - - if(cfg[card].slots[0]==-1) { - if(card==0 && pnplegacy && isapnp) { - printk(KERN_NOTICE "sb: No legacy SoundBlaster cards " \ - "found. Continuing with PnP detection.\n"); - pnplegacy=0; - card--; - continue; - } else - return -ENODEV; - } - - if (!isapnp||(pnplegacy&&card==0)) - cfg_mpu[card].io_base = mpu_io; - if (probe_sbmpu(&cfg_mpu[card], THIS_MODULE)) - sbmpu[card] = 1; - } - - if(isapnp) - printk(KERN_NOTICE "sb: %d Soundblaster PnP card(s) found.\n", sb_cards_num); - - return 0; -} - -static void __exit cleanup_sb(void) -{ - int i; - - if (smw_free) { - vfree(smw_free); - smw_free = NULL; - } - - for(i = 0; i < sb_cards_num; i++) { - unload_sb(&cfg[i], i); - if (sbmpu[i]) - unload_sbmpu(&cfg_mpu[i]); - -#ifdef __ISAPNP__ - if(!audio_activated[i] && sb_dev[i]) - sb_dev[i]->deactivate(sb_dev[i]); - if(!mpu_activated[i] && mpu_dev[i]) - mpu_dev[i]->deactivate(mpu_dev[i]); - if(!opl_activated[i] && opl_dev[i]) - opl_dev[i]->deactivate(opl_dev[i]); -#endif - } -} - -module_init(init_sb); -module_exit(cleanup_sb); - -#ifndef MODULE -static int __init setup_sb(char *str) -{ - /* io, irq, dma, dma2 - just the basics */ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma16 = ints[4]; - - return 1; -} -__setup("sb=", setup_sb); -#endif diff -Nru a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c --- a/drivers/sound/sb_common.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1295 +0,0 @@ -/* - * sound/sb_common.c - * - * Common routines for Sound Blaster compatible cards. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Daniel J. Rodriksson: Modified sbintr to handle 8 and 16 bit interrupts - * for full duplex support ( only sb16 by now ) - * Rolf Fokkens: Added (BETA?) support for ES1887 chips. - * (fokkensr@vertis.nl) Which means: You can adjust the recording levels. - * - * 2000/01/18 - separated sb_card and sb_common - - * Jeff Garzik - * - * 2000/09/18 - got rid of attach_uart401 - * Arnaldo Carvalho de Melo - * - * 2001/01/26 - replaced CLI/STI with spinlocks - * Chris Rankin - */ - -#include -#include -#include -#include -#include - -#include "sound_config.h" -#include "sound_firmware.h" - -#include "mpu401.h" - -#include "sb_mixer.h" -#include "sb.h" -#include "sb_ess.h" - -/* - * global module flag - */ - -int sb_be_quiet; - -static sb_devc *detected_devc; /* For communication from probe to init */ -static sb_devc *last_devc; /* For MPU401 initialization */ - -static unsigned char jazz_irq_bits[] = { - 0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6 -}; - -static unsigned char jazz_dma_bits[] = { - 0, 1, 0, 2, 0, 3, 0, 4 -}; - -void *smw_free; - -/* - * Jazz16 chipset specific control variables - */ - -static int jazz16_base; /* Not detected */ -static unsigned char jazz16_bits; /* I/O relocation bits */ -static spinlock_t jazz16_lock = SPIN_LOCK_UNLOCKED; - -/* - * Logitech Soundman Wave specific initialization code - */ - -#ifdef SMW_MIDI0001_INCLUDED -#include "smw-midi0001.h" -#else -static unsigned char *smw_ucode; -static int smw_ucodeLen; - -#endif - -sb_devc *last_sb; /* Last sb loaded */ - -int sb_dsp_command(sb_devc * devc, unsigned char val) -{ - int i; - unsigned long limit; - - limit = jiffies + HZ / 10; /* Timeout */ - - /* - * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes - * called while interrupts are disabled. This means that the timer is - * disabled also. However the timeout situation is a abnormal condition. - * Normally the DSP should be ready to accept commands after just couple of - * loops. - */ - - for (i = 0; i < 500000 && (limit-jiffies)>0; i++) - { - if ((inb(DSP_STATUS) & 0x80) == 0) - { - outb((val), DSP_COMMAND); - return 1; - } - } - printk(KERN_WARNING "Sound Blaster: DSP command(%x) timeout.\n", val); - return 0; -} - -int sb_dsp_get_byte(sb_devc * devc) -{ - int i; - - for (i = 1000; i; i--) - { - if (inb(DSP_DATA_AVAIL) & 0x80) - return inb(DSP_READ); - } - return 0xffff; -} - -static void sb_intr (sb_devc *devc) -{ - int status; - unsigned char src = 0xff; - - if (devc->model == MDL_SB16) - { - src = sb_getmixer(devc, IRQ_STAT); /* Interrupt source register */ - - if (src & 4) /* MPU401 interrupt */ - if(devc->midi_irq_cookie) - uart401intr(devc->irq, devc->midi_irq_cookie, NULL); - - if (!(src & 3)) - return; /* Not a DSP interrupt */ - } - if (devc->intr_active && (!devc->fullduplex || (src & 0x01))) - { - switch (devc->irq_mode) - { - case IMODE_OUTPUT: - DMAbuf_outputintr(devc->dev, 1); - break; - - case IMODE_INPUT: - DMAbuf_inputintr(devc->dev); - break; - - case IMODE_INIT: - break; - - case IMODE_MIDI: - sb_midi_interrupt(devc); - break; - - default: - /* printk(KERN_WARN "Sound Blaster: Unexpected interrupt\n"); */ - ; - } - } - else if (devc->intr_active_16 && (src & 0x02)) - { - switch (devc->irq_mode_16) - { - case IMODE_OUTPUT: - DMAbuf_outputintr(devc->dev, 1); - break; - - case IMODE_INPUT: - DMAbuf_inputintr(devc->dev); - break; - - case IMODE_INIT: - break; - - default: - /* printk(KERN_WARN "Sound Blaster: Unexpected interrupt\n"); */ - ; - } - } - /* - * Acknowledge interrupts - */ - - if (src & 0x01) - status = inb(DSP_DATA_AVAIL); - - if (devc->model == MDL_SB16 && src & 0x02) - status = inb(DSP_DATA_AVL16); -} - -static void pci_intr(sb_devc *devc) -{ - int src = inb(devc->pcibase+0x1A); - src&=3; - if(src) - sb_intr(devc); -} - -static void sbintr(int irq, void *dev_id, struct pt_regs *dummy) -{ - sb_devc *devc = dev_id; - - devc->irq_ok = 1; - - switch (devc->model) { - case MDL_ESSPCI: - pci_intr (devc); - break; - - case MDL_ESS: - ess_intr (devc); - break; - default: - sb_intr (devc); - break; - } -} - -int sb_dsp_reset(sb_devc * devc) -{ - int loopc; - - DEB(printk("Entered sb_dsp_reset()\n")); - - if (devc->model == MDL_ESS) return ess_dsp_reset (devc); - - /* This is only for non-ESS chips */ - - outb(1, DSP_RESET); - - udelay(10); - outb(0, DSP_RESET); - udelay(30); - - for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++); - - if (inb(DSP_READ) != 0xAA) - { - DDB(printk("sb: No response to RESET\n")); - return 0; /* Sorry */ - } - - DEB(printk("sb_dsp_reset() OK\n")); - - return 1; -} - -static void dsp_get_vers(sb_devc * devc) -{ - int i; - - unsigned long flags; - - DDB(printk("Entered dsp_get_vers()\n")); - spin_lock_irqsave(&devc->lock, flags); - devc->major = devc->minor = 0; - sb_dsp_command(devc, 0xe1); /* Get version */ - - for (i = 100000; i; i--) - { - if (inb(DSP_DATA_AVAIL) & 0x80) - { - if (devc->major == 0) - devc->major = inb(DSP_READ); - else - { - devc->minor = inb(DSP_READ); - break; - } - } - } - spin_unlock_irqrestore(&devc->lock, flags); - DDB(printk("DSP version %d.%02d\n", devc->major, devc->minor)); -} - -static int sb16_set_dma_hw(sb_devc * devc) -{ - int bits; - - if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3) - { - printk(KERN_ERR "SB16: Invalid 8 bit DMA (%d)\n", devc->dma8); - return 0; - } - bits = (1 << devc->dma8); - - if (devc->dma16 >= 5 && devc->dma16 <= 7) - bits |= (1 << devc->dma16); - - sb_setmixer(devc, DMA_NR, bits); - return 1; -} - -static void sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config) -{ - /* - * This routine initializes new MIDI port setup register of SB Vibra (CT2502). - */ - unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06; - - switch (hw_config->io_base) - { - case 0x300: - sb_setmixer(devc, 0x84, bits | 0x04); - break; - - case 0x330: - sb_setmixer(devc, 0x84, bits | 0x00); - break; - - default: - sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */ - printk(KERN_ERR "SB16: Invalid MIDI I/O port %x\n", hw_config->io_base); - } -} - -static int sb16_set_irq_hw(sb_devc * devc, int level) -{ - int ival; - - switch (level) - { - case 5: - ival = 2; - break; - case 7: - ival = 4; - break; - case 9: - ival = 1; - break; - case 10: - ival = 8; - break; - default: - printk(KERN_ERR "SB16: Invalid IRQ%d\n", level); - return 0; - } - sb_setmixer(devc, IRQ_NR, ival); - return 1; -} - -static void relocate_Jazz16(sb_devc * devc, struct address_info *hw_config) -{ - unsigned char bits = 0; - unsigned long flags; - - if (jazz16_base != 0 && jazz16_base != hw_config->io_base) - return; - - switch (hw_config->io_base) - { - case 0x220: - bits = 1; - break; - case 0x240: - bits = 2; - break; - case 0x260: - bits = 3; - break; - default: - return; - } - bits = jazz16_bits = bits << 5; - jazz16_base = hw_config->io_base; - - /* - * Magic wake up sequence by writing to 0x201 (aka Joystick port) - */ - spin_lock_irqsave(&jazz16_lock, flags); - outb((0xAF), 0x201); - outb((0x50), 0x201); - outb((bits), 0x201); - spin_unlock_irqrestore(&jazz16_lock, flags); -} - -static int init_Jazz16(sb_devc * devc, struct address_info *hw_config) -{ - char name[100]; - /* - * First try to check that the card has Jazz16 chip. It identifies itself - * by returning 0x12 as response to DSP command 0xfa. - */ - - if (!sb_dsp_command(devc, 0xfa)) - return 0; - - if (sb_dsp_get_byte(devc) != 0x12) - return 0; - - /* - * OK so far. Now configure the IRQ and DMA channel used by the card. - */ - if (hw_config->irq < 1 || hw_config->irq > 15 || jazz_irq_bits[hw_config->irq] == 0) - { - printk(KERN_ERR "Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq); - return 0; - } - if (hw_config->dma < 0 || hw_config->dma > 3 || jazz_dma_bits[hw_config->dma] == 0) - { - printk(KERN_ERR "Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma); - return 0; - } - if (hw_config->dma2 < 0) - { - printk(KERN_ERR "Jazz16: No 16 bit DMA channel defined\n"); - return 0; - } - if (hw_config->dma2 < 5 || hw_config->dma2 > 7 || jazz_dma_bits[hw_config->dma2] == 0) - { - printk(KERN_ERR "Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2); - return 0; - } - devc->dma16 = hw_config->dma2; - - if (!sb_dsp_command(devc, 0xfb)) - return 0; - - if (!sb_dsp_command(devc, jazz_dma_bits[hw_config->dma] | - (jazz_dma_bits[hw_config->dma2] << 4))) - return 0; - - if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq])) - return 0; - - /* - * Now we have configured a standard Jazz16 device. - */ - devc->model = MDL_JAZZ; - strcpy(name, "Jazz16"); - - hw_config->name = "Jazz16"; - devc->caps |= SB_NO_MIDI; - return 1; -} - -static void relocate_ess1688(sb_devc * devc) -{ - unsigned char bits; - - switch (devc->base) - { - case 0x220: - bits = 0x04; - break; - case 0x230: - bits = 0x05; - break; - case 0x240: - bits = 0x06; - break; - case 0x250: - bits = 0x07; - break; - default: - return; /* Wrong port */ - } - - DDB(printk("Doing ESS1688 address selection\n")); - - /* - * ES1688 supports two alternative ways for software address config. - * First try the so called Read-Sequence-Key method. - */ - - /* Reset the sequence logic */ - inb(0x229); - inb(0x229); - inb(0x229); - - /* Perform the read sequence */ - inb(0x22b); - inb(0x229); - inb(0x22b); - inb(0x229); - inb(0x229); - inb(0x22b); - inb(0x229); - - /* Select the base address by reading from it. Then probe using the port. */ - inb(devc->base); - if (sb_dsp_reset(devc)) /* Bingo */ - return; - -#if 0 /* This causes system lockups (Nokia 386/25 at least) */ - /* - * The last resort is the system control register method. - */ - - outb((0x00), 0xfb); /* 0xFB is the unlock register */ - outb((0x00), 0xe0); /* Select index 0 */ - outb((bits), 0xe1); /* Write the config bits */ - outb((0x00), 0xf9); /* 0xFB is the lock register */ -#endif -} - -int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo) -{ - sb_devc sb_info; - sb_devc *devc = &sb_info; - - memset((char *) &sb_info, 0, sizeof(sb_info)); /* Zero everything */ - - /* Copy module options in place */ - if(sbmo) memcpy(&devc->sbmo, sbmo, sizeof(struct sb_module_options)); - - sb_info.my_mididev = -1; - sb_info.my_mixerdev = -1; - sb_info.dev = -1; - - /* - * Initialize variables - */ - - DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base)); - if (check_region(hw_config->io_base, 16)) - { -#ifdef MODULE - printk(KERN_INFO "sb: I/O region in use.\n"); -#endif - return 0; - } - - devc->lock = SPIN_LOCK_UNLOCKED; - devc->type = hw_config->card_subtype; - - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->dma8 = hw_config->dma; - - devc->dma16 = -1; - devc->pcibase = pciio; - - if(pci == SB_PCI_ESSMAESTRO) - { - devc->model = MDL_ESSPCI; - devc->caps |= SB_PCI_IRQ; - hw_config->driver_use_1 |= SB_PCI_IRQ; - hw_config->card_subtype = MDL_ESSPCI; - } - - if(pci == SB_PCI_YAMAHA) - { - devc->model = MDL_YMPCI; - devc->caps |= SB_PCI_IRQ; - hw_config->driver_use_1 |= SB_PCI_IRQ; - hw_config->card_subtype = MDL_YMPCI; - - printk("Yamaha PCI mode.\n"); - } - - if (devc->sbmo.acer) - { - unsigned long flags; - - spin_lock_irqsave(&devc->lock, flags); - inb(devc->base + 0x09); - inb(devc->base + 0x09); - inb(devc->base + 0x09); - inb(devc->base + 0x0b); - inb(devc->base + 0x09); - inb(devc->base + 0x0b); - inb(devc->base + 0x09); - inb(devc->base + 0x09); - inb(devc->base + 0x0b); - inb(devc->base + 0x09); - inb(devc->base + 0x00); - spin_unlock_irqrestore(&devc->lock, flags); - } - /* - * Detect the device - */ - - if (sb_dsp_reset(devc)) - dsp_get_vers(devc); - else - devc->major = 0; - - if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW) - if (devc->major == 0 || (devc->major == 3 && devc->minor == 1)) - relocate_Jazz16(devc, hw_config); - - if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0)) - relocate_ess1688(devc); - - if (!sb_dsp_reset(devc)) - { - DDB(printk("SB reset failed\n")); -#ifdef MODULE - printk(KERN_INFO "sb: dsp reset failed.\n"); -#endif - return 0; - } - if (devc->major == 0) - dsp_get_vers(devc); - - if (devc->major == 3 && devc->minor == 1) - { - if (devc->type == MDL_AZTECH) /* SG Washington? */ - { - if (sb_dsp_command(devc, 0x09)) - if (sb_dsp_command(devc, 0x00)) /* Enter WSS mode */ - { - int i; - - /* Have some delay */ - for (i = 0; i < 10000; i++) - inb(DSP_DATA_AVAIL); - devc->caps = SB_NO_AUDIO | SB_NO_MIDI; /* Mixer only */ - devc->model = MDL_AZTECH; - } - } - } - - if(devc->type == MDL_ESSPCI) - devc->model = MDL_ESSPCI; - - if(devc->type == MDL_YMPCI) - { - printk("YMPCI selected\n"); - devc->model = MDL_YMPCI; - } - - /* - * Save device information for sb_dsp_init() - */ - - - detected_devc = (sb_devc *)kmalloc(sizeof(sb_devc), GFP_KERNEL); - if (detected_devc == NULL) - { - printk(KERN_ERR "sb: Can't allocate memory for device information\n"); - return 0; - } - memcpy(detected_devc, devc, sizeof(sb_devc)); - MDB(printk(KERN_INFO "SB %d.%02d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base)); - return 1; -} - -int sb_dsp_init(struct address_info *hw_config, struct module *owner) -{ - sb_devc *devc; - char name[100]; - extern int sb_be_quiet; - int mixer22, mixer30; - -/* - * Check if we had detected a SB device earlier - */ - DDB(printk("sb_dsp_init(%x) entered\n", hw_config->io_base)); - name[0] = 0; - - if (detected_devc == NULL) - { - MDB(printk("No detected device\n")); - return 0; - } - devc = detected_devc; - detected_devc = NULL; - - if (devc->base != hw_config->io_base) - { - DDB(printk("I/O port mismatch\n")); - return 0; - } - /* - * Now continue initialization of the device - */ - - devc->caps = hw_config->driver_use_1; - - if (!((devc->caps & SB_NO_AUDIO) && (devc->caps & SB_NO_MIDI)) && hw_config->irq > 0) - { /* IRQ setup */ - - /* - * ESS PCI cards do shared PCI IRQ stuff. Since they - * will get shared PCI irq lines we must cope. - */ - - int i=(devc->caps&SB_PCI_IRQ)?SA_SHIRQ:0; - - if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0) - { - printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq); - return 0; - } - devc->irq_ok = 0; - - if (devc->major == 4) - if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */ - { - free_irq(devc->irq, devc); - return 0; - } - if ((devc->type == 0 || devc->type == MDL_ESS) && - devc->major == 3 && devc->minor == 1) - { /* Handle various chipsets which claim they are SB Pro compatible */ - if ((devc->type != 0 && devc->type != MDL_ESS) || - !ess_init(devc, hw_config)) - { - if ((devc->type != 0 && devc->type != MDL_JAZZ && - devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config)) - { - DDB(printk("This is a genuine SB Pro\n")); - } - } - } - if (devc->major == 4 && devc->minor <= 11 ) /* Won't work */ - devc->irq_ok = 1; - else - { - int n; - - for (n = 0; n < 3 && devc->irq_ok == 0; n++) - { - if (sb_dsp_command(devc, 0xf2)) /* Cause interrupt immediately */ - { - int i; - - for (i = 0; !devc->irq_ok && i < 10000; i++); - } - } - if (!devc->irq_ok) - printk(KERN_WARNING "sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq); - else - { - DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq)); - } - } - } /* IRQ setup */ - request_region(hw_config->io_base, 16, "soundblaster"); - - last_sb = devc; - - switch (devc->major) - { - case 1: /* SB 1.0 or 1.5 */ - devc->model = hw_config->card_subtype = MDL_SB1; - break; - - case 2: /* SB 2.x */ - if (devc->minor == 0) - devc->model = hw_config->card_subtype = MDL_SB2; - else - devc->model = hw_config->card_subtype = MDL_SB201; - break; - - case 3: /* SB Pro and most clones */ - switch (devc->model) { - case 0: - devc->model = hw_config->card_subtype = MDL_SBPRO; - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster Pro (8 BIT ONLY)"; - break; - case MDL_ESS: - ess_dsp_init(devc, hw_config); - break; - } - break; - - case 4: - devc->model = hw_config->card_subtype = MDL_SB16; - /* - * ALS007 and ALS100 return DSP version 4.2 and have 2 post-reset !=0 - * registers at 0x3c and 0x4c (output ctrl registers on ALS007) whereas - * a "standard" SB16 doesn't have a register at 0x4c. ALS100 actively - * updates register 0x22 whenever 0x30 changes, as per the SB16 spec. - * Since ALS007 doesn't, this can be used to differentiate the 2 cards. - */ - if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c)) - { - mixer30 = sb_getmixer(devc,0x30); - sb_setmixer(devc,0x22,(mixer22=sb_getmixer(devc,0x22)) & 0x0f); - sb_setmixer(devc,0x30,0xff); - /* ALS100 will force 0x30 to 0xf8 like SB16; ALS007 will allow 0xff. */ - /* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10. */ - if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10)) - { - devc->submodel = SUBMDL_ALS100; - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster 16 (ALS-100)"; - } - else - { - sb_setmixer(devc,0x3c,0x1f); /* Enable all inputs */ - sb_setmixer(devc,0x4c,0x1f); - sb_setmixer(devc,0x22,mixer22); /* Restore 0x22 to original value */ - devc->submodel = SUBMDL_ALS007; - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster 16 (ALS-007)"; - } - sb_setmixer(devc,0x30,mixer30); - } - else if (hw_config->name == NULL) - hw_config->name = "Sound Blaster 16"; - - if (hw_config->dma2 == -1) - devc->dma16 = devc->dma8; - else if (hw_config->dma2 < 5 || hw_config->dma2 > 7) - { - printk(KERN_WARNING "SB16: Bad or missing 16 bit DMA channel\n"); - devc->dma16 = devc->dma8; - } - else - devc->dma16 = hw_config->dma2; - - if(!sb16_set_dma_hw(devc)) { - free_irq(devc->irq, devc); - release_region(hw_config->io_base, 16); - return 0; - } - - devc->caps |= SB_NO_MIDI; - } - - if (!(devc->caps & SB_NO_MIXER)) - if (devc->major == 3 || devc->major == 4) - sb_mixer_init(devc, owner); - - if (!(devc->caps & SB_NO_MIDI)) - sb_dsp_midi_init(devc, owner); - - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)"; - - sprintf(name, "%s (%d.%02d)", hw_config->name, devc->major, devc->minor); - conf_printf(name, hw_config); - - /* - * Assuming that a sound card is Sound Blaster (compatible) is the most common - * configuration error and the mother of all problems. Usually sound cards - * emulate SB Pro but in addition they have a 16 bit native mode which should be - * used in Unix. See Readme.cards for more information about configuring OSS/Free - * properly. - */ - if (devc->model <= MDL_SBPRO) - { - if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1 (rare ones may have 3.2). */ - { - printk(KERN_INFO "This sound card may not be fully Sound Blaster Pro compatible.\n"); - printk(KERN_INFO "In many cases there is another way to configure OSS so that\n"); - printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n"); - printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n"); - } - else if (!sb_be_quiet && devc->model == MDL_SBPRO) - { - printk(KERN_INFO "SB DSP version is just %d.%02d which means that your card is\n", devc->major, devc->minor); - printk(KERN_INFO "several years old (8 bit only device) or alternatively the sound driver\n"); - printk(KERN_INFO "is incorrectly configured.\n"); - } - } - hw_config->card_subtype = devc->model; - hw_config->slots[0]=devc->dev; - last_devc = devc; /* For SB MPU detection */ - - if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0) - { - if (sound_alloc_dma(devc->dma8, "SoundBlaster8")) - { - printk(KERN_WARNING "Sound Blaster: Can't allocate 8 bit DMA channel %d\n", devc->dma8); - } - if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) - { - if (sound_alloc_dma(devc->dma16, "SoundBlaster16")) - printk(KERN_WARNING "Sound Blaster: can't allocate 16 bit DMA channel %d.\n", devc->dma16); - } - sb_audio_init(devc, name, owner); - hw_config->slots[0]=devc->dev; - } - else - { - MDB(printk("Sound Blaster: no audio devices found.\n")); - } - return 1; -} - -void sb_dsp_disable_midi(int io_base) -{ -} - -void sb_dsp_disable_recording(int io_base) -{ -} - -/* if (sbmpu) below we allow mpu401 to manage the midi devs - otherwise we have to unload them. (Andrzej Krzysztofowicz) */ - -void sb_dsp_unload(struct address_info *hw_config, int sbmpu) -{ - sb_devc *devc; - - devc = audio_devs[hw_config->slots[0]]->devc; - - if (devc && devc->base == hw_config->io_base) - { - if ((devc->model & MDL_ESS) && devc->pcibase) - release_region(devc->pcibase, 8); - - release_region(devc->base, 16); - - if (!(devc->caps & SB_NO_AUDIO)) - { - sound_free_dma(devc->dma8); - if (devc->dma16 >= 0) - sound_free_dma(devc->dma16); - } - if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI)) - { - if (devc->irq > 0) - free_irq(devc->irq, devc); - - sb_mixer_unload(devc); - /* We don't have to do this bit any more the UART401 is its own - master -- Krzysztof Halasa */ - /* But we have to do it, if UART401 is not detected */ - if (!sbmpu) - sound_unload_mididev(devc->my_mididev); - sound_unload_audiodev(devc->dev); - } - kfree(devc); - } - else - release_region(hw_config->io_base, 16); - if(detected_devc) - kfree(detected_devc); -} - -/* - * Mixer access routines - * - * ES1887 modifications: some mixer registers reside in the - * range above 0xa0. These must be accessed in another way. - */ - -void sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value) -{ - unsigned long flags; - - if (devc->model == MDL_ESS) return ess_setmixer (devc, port, value); - - spin_lock_irqsave(&devc->lock, flags); - - outb(((unsigned char) (port & 0xff)), MIXER_ADDR); - udelay(20); - outb(((unsigned char) (value & 0xff)), MIXER_DATA); - udelay(20); - - spin_unlock_irqrestore(&devc->lock, flags); -} - -unsigned int sb_getmixer(sb_devc * devc, unsigned int port) -{ - unsigned int val; - unsigned long flags; - - if (devc->model == MDL_ESS) return ess_getmixer (devc, port); - - spin_lock_irqsave(&devc->lock, flags); - - outb(((unsigned char) (port & 0xff)), MIXER_ADDR); - udelay(20); - val = inb(MIXER_DATA); - udelay(20); - - spin_unlock_irqrestore(&devc->lock, flags); - - return val; -} - -void sb_chgmixer - (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val) -{ - int value; - - value = sb_getmixer(devc, reg); - value = (value & ~mask) | (val & mask); - sb_setmixer(devc, reg, value); -} - -/* - * MPU401 MIDI initialization. - */ - -static void smw_putmem(sb_devc * devc, int base, int addr, unsigned char val) -{ - unsigned long flags; - - spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */ - - outb((addr & 0xff), base + 1); /* Low address bits */ - outb((addr >> 8), base + 2); /* High address bits */ - outb((val), base); /* Data */ - - spin_unlock_irqrestore(&jazz16_lock, flags); -} - -static unsigned char smw_getmem(sb_devc * devc, int base, int addr) -{ - unsigned long flags; - unsigned char val; - - spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */ - - outb((addr & 0xff), base + 1); /* Low address bits */ - outb((addr >> 8), base + 2); /* High address bits */ - val = inb(base); /* Data */ - - spin_unlock_irqrestore(&jazz16_lock, flags); - return val; -} - -static int smw_midi_init(sb_devc * devc, struct address_info *hw_config) -{ - int mpu_base = hw_config->io_base; - int mp_base = mpu_base + 4; /* Microcontroller base */ - int i; - unsigned char control; - - - /* - * Reset the microcontroller so that the RAM can be accessed - */ - - control = inb(mpu_base + 7); - outb((control | 3), mpu_base + 7); /* Set last two bits to 1 (?) */ - outb(((control & 0xfe) | 2), mpu_base + 7); /* xxxxxxx0 resets the mc */ - - mdelay(3); /* Wait at least 1ms */ - - outb((control & 0xfc), mpu_base + 7); /* xxxxxx00 enables RAM */ - - /* - * Detect microcontroller by probing the 8k RAM area - */ - smw_putmem(devc, mp_base, 0, 0x00); - smw_putmem(devc, mp_base, 1, 0xff); - udelay(10); - - if (smw_getmem(devc, mp_base, 0) != 0x00 || smw_getmem(devc, mp_base, 1) != 0xff) - { - DDB(printk("SM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1))); - return 0; /* No RAM */ - } - /* - * There is RAM so assume it's really a SM Wave - */ - - devc->model = MDL_SMW; - smw_mixer_init(devc); - -#ifdef MODULE - if (!smw_ucode) - { - smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode); - smw_free = smw_ucode; - } -#endif - if (smw_ucodeLen > 0) - { - if (smw_ucodeLen != 8192) - { - printk(KERN_ERR "SM Wave: Invalid microcode (MIDI0001.BIN) length\n"); - return 1; - } - /* - * Download microcode - */ - - for (i = 0; i < 8192; i++) - smw_putmem(devc, mp_base, i, smw_ucode[i]); - - /* - * Verify microcode - */ - - for (i = 0; i < 8192; i++) - if (smw_getmem(devc, mp_base, i) != smw_ucode[i]) - { - printk(KERN_ERR "SM Wave: Microcode verification failed\n"); - return 0; - } - } - control = 0; -#ifdef SMW_SCSI_IRQ - /* - * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt - * is disabled by default. - * - * FIXME - make this a module option - * - * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10. - */ - { - static unsigned char scsi_irq_bits[] = { - 0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0 - }; - control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6; - } -#endif - -#ifdef SMW_OPL4_ENABLE - /* - * Make the OPL4 chip visible on the PC bus at 0x380. - * - * There is no need to enable this feature since this driver - * doesn't support OPL4 yet. Also there is no RAM in SM Wave so - * enabling OPL4 is pretty useless. - */ - control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */ - /* control |= 0x20; Uncomment this if you want to use IRQ7 */ -#endif - outb((control | 0x03), mpu_base + 7); /* xxxxxx11 restarts */ - hw_config->name = "SoundMan Wave"; - return 1; -} - -static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config) -{ - int mpu_base = hw_config->io_base; - int sb_base = devc->base; - int irq = hw_config->irq; - - unsigned char bits = 0; - unsigned long flags; - - if (irq < 0) - irq *= -1; - - if (irq < 1 || irq > 15 || - jazz_irq_bits[irq] == 0) - { - printk(KERN_ERR "Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq); - return 0; - } - switch (sb_base) - { - case 0x220: - bits = 1; - break; - case 0x240: - bits = 2; - break; - case 0x260: - bits = 3; - break; - default: - return 0; - } - bits = jazz16_bits = bits << 5; - switch (mpu_base) - { - case 0x310: - bits |= 1; - break; - case 0x320: - bits |= 2; - break; - case 0x330: - bits |= 3; - break; - default: - printk(KERN_ERR "Jazz16: Invalid MIDI I/O port %x\n", mpu_base); - return 0; - } - /* - * Magic wake up sequence by writing to 0x201 (aka Joystick port) - */ - spin_lock_irqsave(&jazz16_lock, flags); - outb(0xAF, 0x201); - outb(0x50, 0x201); - outb(bits, 0x201); - spin_unlock_irqrestore(&jazz16_lock, flags); - - hw_config->name = "Jazz16"; - smw_midi_init(devc, hw_config); - - if (!sb_dsp_command(devc, 0xfb)) - return 0; - - if (!sb_dsp_command(devc, jazz_dma_bits[devc->dma8] | - (jazz_dma_bits[devc->dma16] << 4))) - return 0; - - if (!sb_dsp_command(devc, jazz_irq_bits[devc->irq] | - (jazz_irq_bits[irq] << 4))) - return 0; - - return 1; -} - -int probe_sbmpu(struct address_info *hw_config, struct module *owner) -{ - sb_devc *devc = last_devc; - int ret; - - if (last_devc == NULL) - return 0; - - last_devc = 0; - - if (hw_config->io_base <= 0) - { - /* The real vibra16 is fine about this, but we have to go - wipe up after Cyrix again */ - - if(devc->model == MDL_SB16 && devc->minor >= 12) - { - unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06; - sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */ - } - return 0; - } - -#if defined(CONFIG_SOUND_MPU401) - if (devc->model == MDL_ESS) - { - if (check_region(hw_config->io_base, 2)) - { - printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - if (!ess_midi_init(devc, hw_config)) - return 0; - hw_config->name = "ESS1xxx MPU"; - devc->midi_irq_cookie = NULL; - if (!probe_mpu401(hw_config)) - return 0; - attach_mpu401(hw_config, owner); - if (last_sb->irq == -hw_config->irq) - last_sb->midi_irq_cookie=(void *)hw_config->slots[1]; - return 1; - } -#endif - - switch (devc->model) - { - case MDL_SB16: - if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330) - { - printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base); - return 0; - } - hw_config->name = "Sound Blaster 16"; - if (hw_config->irq < 3 || hw_config->irq == devc->irq) - hw_config->irq = -devc->irq; - if (devc->minor > 12) /* What is Vibra's version??? */ - sb16_set_mpu_port(devc, hw_config); - break; - - case MDL_JAZZ: - if (hw_config->irq < 3 || hw_config->irq == devc->irq) - hw_config->irq = -devc->irq; - if (!init_Jazz16_midi(devc, hw_config)) - return 0; - break; - - case MDL_YMPCI: - hw_config->name = "Yamaha PCI Legacy"; - printk("Yamaha PCI legacy UART401 check.\n"); - break; - default: - return 0; - } - - ret = probe_uart401(hw_config, owner); - if (ret) - last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc; - return ret; -} - -void unload_sbmpu(struct address_info *hw_config) -{ -#if defined(CONFIG_SOUND_MPU401) - if (!strcmp (hw_config->name, "ESS1xxx MPU")) { - unload_mpu401(hw_config); - return; - } -#endif - unload_uart401(hw_config); -} - -EXPORT_SYMBOL(sb_dsp_init); -EXPORT_SYMBOL(sb_dsp_detect); -EXPORT_SYMBOL(sb_dsp_unload); -EXPORT_SYMBOL(sb_dsp_disable_midi); -EXPORT_SYMBOL(sb_be_quiet); -EXPORT_SYMBOL(probe_sbmpu); -EXPORT_SYMBOL(unload_sbmpu); -EXPORT_SYMBOL(smw_free); -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/sb_ess.c b/drivers/sound/sb_ess.c --- a/drivers/sound/sb_ess.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1826 +0,0 @@ -#undef FKS_LOGGING -#undef FKS_TEST - -/* - * tabs should be 4 spaces, in vi(m): set tabstop=4 - * - * TODO: consistency speed calculations!! - * cleanup! - * ????: Did I break MIDI support? - * - * History: - * - * Rolf Fokkens (Dec 20 1998): ES188x recording level support on a per - * fokkensr@vertis.nl input basis. - * (Dec 24 1998): Recognition of ES1788, ES1887, ES1888, - * ES1868, ES1869 and ES1878. Could be used for - * specific handling in the future. All except - * ES1887 and ES1888 and ES688 are handled like - * ES1688. - * (Dec 27 1998): RECLEV for all (?) ES1688+ chips. ES188x now - * have the "Dec 20" support + RECLEV - * (Jan 2 1999): Preparation for Full Duplex. This means - * Audio 2 is now used for playback when dma16 - * is specified. The next step would be to use - * Audio 1 and Audio 2 at the same time. - * (Jan 9 1999): Put all ESS stuff into sb_ess.[ch], this - * includes both the ESS stuff that has been in - * sb_*[ch] before I touched it and the ESS support - * I added later - * (Jan 23 1999): Full Duplex seems to work. I wrote a small - * test proggy which works OK. Haven't found - * any applications to test it though. So why did - * I bother to create it anyway?? :) Just for - * fun. - * (May 2 1999): I tried to be too smart by "introducing" - * ess_calc_best_speed (). The idea was that two - * dividers could be used to setup a samplerate, - * ess_calc_best_speed () would choose the best. - * This works for playback, but results in - * recording problems for high samplerates. I - * fixed this by removing ess_calc_best_speed () - * and just doing what the documentation says. - * Andy Sloane (Jun 4 1999): Stole some code from ALSA to fix the playback - * andy@guildsoftware.com speed on ES1869, ES1879, ES1887, and ES1888. - * 1879's were previously ignored by this driver; - * added (untested) support for those. - * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for - * zezo@inet.bg _ALL_ ESS models, not only ES1887 - * - * This files contains ESS chip specifics. It's based on the existing ESS - * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This - * file adds features like: - * - Chip Identification (as shown in /proc/sound) - * - RECLEV support for ES1688 and later - * - 6 bits playback level support chips later than ES1688 - * - Recording level support on a per-device basis for ES1887 - * - Full-Duplex for ES1887 - * - * Full duplex is enabled by specifying dma16. While the normal dma must - * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit - * DMA channel, while the others are 8 bit.. - * - * ESS detection isn't full proof (yet). If it fails an additional module - * parameter esstype can be specified to be one of the following: - * -1, 0, 688, 1688, 1868, 1869, 1788, 1887, 1888 - * -1 means: mimic 2.0 behaviour, - * 0 means: auto detect. - * others: explicitly specify chip - * -1 is default, cause auto detect still doesn't work. - */ - -/* - * About the documentation - * - * I don't know if the chips all are OK, but the documentation is buggy. 'cause - * I don't have all the cips myself, there's a lot I cannot verify. I'll try to - * keep track of my latest insights about his here. If you have additional info, - * please enlighten me (fokkensr@vertis.nl)! - * - * I had the impression that ES1688 also has 6 bit master volume control. The - * documentation about ES1888 (rev C, october '95) claims that ES1888 has - * the following features ES1688 doesn't have: - * - 6 bit master volume - * - Full Duplex - * So ES1688 apparently doesn't have 6 bit master volume control, but the - * ES1688 does have RECLEV control. Makes me wonder: does ES688 have it too? - * Without RECLEV ES688 won't be much fun I guess. - * - * From the ES1888 (rev C, october '95) documentation I got the impression - * that registers 0x68 to 0x6e don't exist which means: no recording volume - * controls. To my surprise the ES888 documentation (1/14/96) claims that - * ES888 does have these record mixer registers, but that ES1888 doesn't have - * 0x69 and 0x6b. So the rest should be there. - * - * I'm trying to get ES1887 Full Duplex. Audio 2 is playback only, while Audio 2 - * is both record and playback. I think I should use Audio 2 for all playback. - * - * The documentation is an adventure: it's close but not fully accurate. I - * found out that after a reset some registers are *NOT* reset, though the - * docs say the would be. Interresting ones are 0x7f, 0x7d and 0x7a. They are - * related to the Audio 2 channel. I also was suprised about the consequenses - * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves - * into ES1888 mode. This means that it claims IRQ 11, which happens to be my - * ISDN adapter. Needless to say it no longer worked. I now understand why - * after rebooting 0x7f already was 0x05, the value of my choice: the BIOS - * did it. - * - * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is decribed - * as if it's exactly the same as register 0xa1. This is *NOT* true. The - * description of 0x70 in ES1869 docs is accurate however. - * Well, the assumption about ES1869 was wrong: register 0x70 is very much - * like register 0xa1, except that bit 7 is allways 1, whatever you want - * it to be. - * - * When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2 - * has effect. - * - * Software reset not being able to reset all registers is great! Especially - * the fact that register 0x78 isn't reset is great when you wanna change back - * to single dma operation (simplex): audio 2 is still operation, and uses the - * same dma as audio 1: your ess changes into a funny echo machine. - * - * Received the new that ES1688 is detected as a ES1788. Did some thinking: - * the ES1887 detection scheme suggests in step 2 to try if bit 3 of register - * 0x64 can be changed. This is inaccurate, first I inverted the * check: "If - * can be modified, it's a 1688", which lead to a correct detection - * of my ES1887. It resulted however in bad detection of 1688 (reported by mail) - * and 1868 (if no PnP detection first): they result in a 1788 being detected. - * I don't have docs on 1688, but I do have docs on 1868: The documentation is - * probably inaccurate in the fact that I should check bit 2, not bit 3. This - * is what I do now. - */ - -/* - * About recognition of ESS chips - * - * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in - * a (preliminary ??) datasheet on ES1887. It's aim is to identify ES1887, but - * during detection the text claims that "this chip may be ..." when a step - * fails. This scheme is used to distinct between the above chips. - * It appears however that some PnP chips like ES1868 are recognized as ES1788 - * by the ES1887 detection scheme. These PnP chips can be detected in another - * way however: ES1868, ES1869 and ES1878 can be recognized (full proof I think) - * by repeatedly reading mixer register 0x40. This is done by ess_identify in - * sb_common.c. - * This results in the following detection steps: - * - distinct between ES688 and ES1688+ (as always done in this driver) - * if ES688 we're ready - * - try to detect ES1868, ES1869 or ES1878 - * if successful we're ready - * - try to detect ES1888, ES1887 or ES1788 - * if successful we're ready - * - Dunno. Must be 1688. Will do in general - * - * About RECLEV support: - * - * The existing ES1688 support didn't take care of the ES1688+ recording - * levels very well. Whenever a device was selected (recmask) for recording - * it's recording level was loud, and it couldn't be changed. The fact that - * internal register 0xb4 could take care of RECLEV, didn't work meaning until - * it's value was restored every time the chip was reset; this reset the - * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with. - * - * About ES1887 support: - * - * The ES1887 has separate registers to control the recording levels, for all - * inputs. The ES1887 specific software makes these levels the same as their - * corresponding playback levels, unless recmask says they aren't recorded. In - * the latter case the recording volumes are 0. - * Now recording levels of inputs can be controlled, by changing the playback - * levels. Futhermore several devices can be recorded together (which is not - * possible with the ES1688. - * Besides the separate recording level control for each input, the common - * recordig level can also be controlled by RECLEV as described above. - * - * Not only ES1887 have this recording mixer. I know the following from the - * documentation: - * ES688 no - * ES1688 no - * ES1868 no - * ES1869 yes - * ES1878 no - * ES1879 yes - * ES1888 no/yes Contradicting documentation; most recent: yes - * ES1946 yes This is a PCI chip; not handled by this driver - */ - -#include -#include - -#include "sound_config.h" -#include "sb_mixer.h" -#include "sb.h" - -#include "sb_ess.h" - -#define ESSTYPE_LIKE20 -1 /* Mimic 2.0 behaviour */ -#define ESSTYPE_DETECT 0 /* Mimic 2.0 behaviour */ - -#define SUBMDL_ES1788 0x10 /* Subtype ES1788 for specific handling */ -#define SUBMDL_ES1868 0x11 /* Subtype ES1868 for specific handling */ -#define SUBMDL_ES1869 0x12 /* Subtype ES1869 for specific handling */ -#define SUBMDL_ES1878 0x13 /* Subtype ES1878 for specific handling */ -#define SUBMDL_ES1879 0x16 /* ES1879 was initially forgotten */ -#define SUBMDL_ES1887 0x14 /* Subtype ES1887 for specific handling */ -#define SUBMDL_ES1888 0x15 /* Subtype ES1888 for specific handling */ - -#define SB_CAP_ES18XX_RATE 0x100 - -#define ES1688_CLOCK1 795444 /* 128 - div */ -#define ES1688_CLOCK2 397722 /* 256 - div */ -#define ES18XX_CLOCK1 793800 /* 128 - div */ -#define ES18XX_CLOCK2 768000 /* 256 - div */ - -#ifdef FKS_LOGGING -static void ess_show_mixerregs (sb_devc *devc); -#endif -static int ess_read (sb_devc * devc, unsigned char reg); -static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data); -static void ess_chgmixer - (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val); - -/**************************************************************************** - * * - * ESS audio * - * * - ****************************************************************************/ - -struct ess_command {short cmd; short data;}; - -/* - * Commands for initializing Audio 1 for input (record) - */ -static struct ess_command ess_i08m[] = /* input 8 bit mono */ - { {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} }; -static struct ess_command ess_i16m[] = /* input 16 bit mono */ - { {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} }; -static struct ess_command ess_i08s[] = /* input 8 bit stereo */ - { {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} }; -static struct ess_command ess_i16s[] = /* input 16 bit stereo */ - { {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} }; - -static struct ess_command *ess_inp_cmds[] = - { ess_i08m, ess_i16m, ess_i08s, ess_i16s }; - - -/* - * Commands for initializing Audio 1 for output (playback) - */ -static struct ess_command ess_o08m[] = /* output 8 bit mono */ - { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} }; -static struct ess_command ess_o16m[] = /* output 16 bit mono */ - { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} }; -static struct ess_command ess_o08s[] = /* output 8 bit stereo */ - { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} }; -static struct ess_command ess_o16s[] = /* output 16 bit stereo */ - { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} }; - -static struct ess_command *ess_out_cmds[] = - { ess_o08m, ess_o16m, ess_o08s, ess_o16s }; - -static void ess_exec_commands - (sb_devc *devc, struct ess_command *cmdtab[]) -{ - struct ess_command *cmd; - - cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ]; - - while (cmd->cmd != -1) { - ess_write (devc, cmd->cmd, cmd->data); - cmd++; - } -} - -static void ess_change - (sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val) -{ - int value; - - value = ess_read (devc, reg); - value = (value & ~mask) | (val & mask); - ess_write (devc, reg, value); -} - -static void ess_set_output_parms - (int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (devc->duplex) { - devc->trg_buf_16 = buf; - devc->trg_bytes_16 = nr_bytes; - devc->trg_intrflag_16 = intrflag; - devc->irq_mode_16 = IMODE_OUTPUT; - } else { - devc->trg_buf = buf; - devc->trg_bytes = nr_bytes; - devc->trg_intrflag = intrflag; - devc->irq_mode = IMODE_OUTPUT; - } -} - -static void ess_set_input_parms - (int dev, unsigned long buf, int count, int intrflag) -{ - sb_devc *devc = audio_devs[dev]->devc; - - devc->trg_buf = buf; - devc->trg_bytes = count; - devc->trg_intrflag = intrflag; - devc->irq_mode = IMODE_INPUT; -} - -static int ess_calc_div (int clock, int revert, int *speedp, int *diffp) -{ - int divider; - int speed, diff; - int retval; - - speed = *speedp; - divider = (clock + speed / 2) / speed; - retval = revert - divider; - if (retval > revert - 1) { - retval = revert - 1; - divider = revert - retval; - } - /* This line is suggested. Must be wrong I think - *speedp = (clock + divider / 2) / divider; - So I chose the next one */ - - *speedp = clock / divider; - diff = speed - *speedp; - if (diff < 0) diff =-diff; - *diffp = diff; - - return retval; -} - -static int ess_calc_best_speed - (int clock1, int rev1, int clock2, int rev2, int *divp, int *speedp) -{ - int speed1 = *speedp, speed2 = *speedp; - int div1, div2; - int diff1, diff2; - int retval; - - div1 = ess_calc_div (clock1, rev1, &speed1, &diff1); - div2 = ess_calc_div (clock2, rev2, &speed2, &diff2); - - if (diff1 < diff2) { - *divp = div1; - *speedp = speed1; - retval = 1; - } else { - /* *divp = div2; */ - *divp = 0x80 | div2; - *speedp = speed2; - retval = 2; - } - - return retval; -} - -/* - * Depending on the audiochannel ESS devices can - * have different clock settings. These are made consistent for duplex - * however. - * callers of ess_speed only do an audionum suggestion, which means - * input suggests 1, output suggests 2. This suggestion is only true - * however when doing duplex. - */ -static void ess_common_speed (sb_devc *devc, int *speedp, int *divp) -{ - int diff = 0, div; - - if (devc->duplex) { - /* - * The 0x80 is important for the first audio channel - */ - if (devc->submodel == SUBMDL_ES1888) { - div = 0x80 | ess_calc_div (795500, 256, speedp, &diff); - } else { - div = 0x80 | ess_calc_div (795500, 128, speedp, &diff); - } - } else if(devc->caps & SB_CAP_ES18XX_RATE) { - if (devc->submodel == SUBMDL_ES1888) { - ess_calc_best_speed(397700, 128, 795500, 256, - &div, speedp); - } else { - ess_calc_best_speed(ES18XX_CLOCK1, 128, ES18XX_CLOCK2, 256, - &div, speedp); - } - } else { - if (*speedp > 22000) { - div = 0x80 | ess_calc_div (ES1688_CLOCK1, 256, speedp, &diff); - } else { - div = 0x00 | ess_calc_div (ES1688_CLOCK2, 128, speedp, &diff); - } - } - *divp = div; -} - -static void ess_speed (sb_devc *devc, int audionum) -{ - int speed; - int div, div2; - - ess_common_speed (devc, &(devc->speed), &div); - -#ifdef FKS_REG_LOGGING -printk (KERN_INFO "FKS: ess_speed (%d) b speed = %d, div=%x\n", audionum, devc->speed, div); -#endif - - /* Set filter roll-off to 90% of speed/2 */ - speed = (devc->speed * 9) / 20; - - div2 = 256 - 7160000 / (speed * 82); - - if (!devc->duplex) audionum = 1; - - if (audionum == 1) { - /* Change behaviour of register A1 * - sb_chg_mixer(devc, 0x71, 0x20, 0x20) - * For ES1869 only??? */ - ess_write (devc, 0xa1, div); - ess_write (devc, 0xa2, div2); - } else { - ess_setmixer (devc, 0x70, div); - /* - * FKS: fascinating: 0x72 doesn't seem to work. - */ - ess_write (devc, 0xa2, div2); - ess_setmixer (devc, 0x72, div2); - } -} - -static int ess_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - - ess_speed(devc, 1); - - sb_dsp_command(devc, DSP_CMD_SPKOFF); - - ess_write (devc, 0xb8, 0x0e); /* Auto init DMA mode */ - ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */ - ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */ - - ess_exec_commands (devc, ess_inp_cmds); - - ess_change (devc, 0xb1, 0xf0, 0x50); - ess_change (devc, 0xb2, 0xf0, 0x50); - - devc->trigger_bits = 0; - return 0; -} - -static int ess_audio_prepare_for_output_audio1 (int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - - sb_dsp_reset(devc); - ess_speed(devc, 1); - ess_write (devc, 0xb8, 4); /* Auto init DMA mode */ - ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */ - ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/request) */ - - ess_exec_commands (devc, ess_out_cmds); - - ess_change (devc, 0xb1, 0xf0, 0x50); /* Enable DMA */ - ess_change (devc, 0xb2, 0xf0, 0x50); /* Enable IRQ */ - - sb_dsp_command(devc, DSP_CMD_SPKON); /* There be sound! */ - - devc->trigger_bits = 0; - return 0; -} - -static int ess_audio_prepare_for_output_audio2 (int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - unsigned char bits; - -/* FKS: qqq - sb_dsp_reset(devc); -*/ - - /* - * Auto-Initialize: - * DMA mode + demand mode (8 bytes/request, yes I want it all!) - * But leave 16-bit DMA bit untouched! - */ - ess_chgmixer (devc, 0x78, 0xd0, 0xd0); - - ess_speed(devc, 2); - - /* bits 4:3 on ES1887 represent recording source. Keep them! */ - bits = ess_getmixer (devc, 0x7a) & 0x18; - - /* Set stereo/mono */ - if (devc->channels != 1) bits |= 0x02; - - /* Init DACs; UNSIGNED mode for 8 bit; SIGNED mode for 16 bit */ - if (devc->bits != AFMT_U8) bits |= 0x05; /* 16 bit */ - - /* Enable DMA, IRQ will be shared (hopefully)*/ - bits |= 0x60; - - ess_setmixer (devc, 0x7a, bits); - - ess_mixer_reload (devc, SOUND_MIXER_PCM); /* There be sound! */ - - devc->trigger_bits = 0; - return 0; -} - -static int ess_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - -#ifdef FKS_REG_LOGGING -printk(KERN_INFO "ess_audio_prepare_for_output: dma_out=%d,dma_in=%d\n" -, audio_devs[dev]->dmap_out->dma, audio_devs[dev]->dmap_in->dma); -#endif - - if (devc->duplex) { - return ess_audio_prepare_for_output_audio2 (dev, bsize, bcount); - } else { - return ess_audio_prepare_for_output_audio1 (dev, bsize, bcount); - } -} - -static void ess_audio_halt_xfer(int dev) -{ - unsigned long flags; - sb_devc *devc = audio_devs[dev]->devc; - - spin_lock_irqsave(&devc->lock, flags); - sb_dsp_reset(devc); - spin_unlock_irqrestore(&devc->lock, flags); - - /* - * Audio 2 may still be operational! Creates awful sounds! - */ - if (devc->duplex) ess_chgmixer(devc, 0x78, 0x03, 0x00); -} - -static void ess_audio_start_input - (int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - short c = -nr_bytes; - - /* - * Start a DMA input to the buffer pointed by dmaqtail - */ - - if (audio_devs[dev]->dmap_in->dma > 3) count >>= 1; - count--; - - devc->irq_mode = IMODE_INPUT; - - ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); - ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); - - ess_change (devc, 0xb8, 0x0f, 0x0f); /* Go */ - devc->intr_active = 1; -} - -static void ess_audio_output_block_audio1 - (int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - short c = -nr_bytes; - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_OUTPUT; - - ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); - ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); - - ess_change (devc, 0xb8, 0x05, 0x05); /* Go */ - devc->intr_active = 1; -} - -static void ess_audio_output_block_audio2 - (int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - short c = -nr_bytes; - - if (audio_devs[dev]->dmap_out->dma > 3) count >>= 1; - count--; - - ess_setmixer (devc, 0x74, (unsigned char) ((unsigned short) c & 0xff)); - ess_setmixer (devc, 0x76, (unsigned char) (((unsigned short) c >> 8) & 0xff)); - ess_chgmixer (devc, 0x78, 0x03, 0x03); /* Go */ - - devc->irq_mode_16 = IMODE_OUTPUT; - devc->intr_active_16 = 1; -} - -static void ess_audio_output_block - (int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (devc->duplex) { - ess_audio_output_block_audio2 (dev, buf, nr_bytes, intrflag); - } else { - ess_audio_output_block_audio1 (dev, buf, nr_bytes, intrflag); - } -} - -/* - * FKS: the if-statements for both bits and bits_16 are quite alike. - * Combine this... - */ -static void ess_audio_trigger(int dev, int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - - int bits_16 = bits & devc->irq_mode_16; - bits &= devc->irq_mode; - - if (!bits && !bits_16) { - /* FKS oh oh.... wrong?? for dma 16? */ - sb_dsp_command(devc, 0xd0); /* Halt DMA */ - } - - if (bits) { - switch (devc->irq_mode) - { - case IMODE_INPUT: - ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - - case IMODE_OUTPUT: - ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - } - } - - if (bits_16) { - switch (devc->irq_mode_16) { - case IMODE_INPUT: - ess_audio_start_input(dev, devc->trg_buf_16, devc->trg_bytes_16, - devc->trg_intrflag_16); - break; - - case IMODE_OUTPUT: - ess_audio_output_block(dev, devc->trg_buf_16, devc->trg_bytes_16, - devc->trg_intrflag_16); - break; - } - } - - devc->trigger_bits = bits | bits_16; -} - -static int ess_audio_set_speed(int dev, int speed) -{ - sb_devc *devc = audio_devs[dev]->devc; - int minspeed, maxspeed, dummydiv; - - if (speed > 0) { - minspeed = (devc->duplex ? 6215 : 5000 ); - maxspeed = (devc->duplex ? 44100 : 48000); - if (speed < minspeed) speed = minspeed; - if (speed > maxspeed) speed = maxspeed; - - ess_common_speed (devc, &speed, &dummydiv); - - devc->speed = speed; - } - return devc->speed; -} - -/* - * FKS: This is a one-on-one copy of sb1_audio_set_bits - */ -static unsigned int ess_audio_set_bits(int dev, unsigned int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (bits != 0) { - if (bits == AFMT_U8 || bits == AFMT_S16_LE) { - devc->bits = bits; - } else { - devc->bits = AFMT_U8; - } - } - - return devc->bits; -} - -/* - * FKS: This is a one-on-one copy of sbpro_audio_set_channels - * (*) Modified it!! - */ -static short ess_audio_set_channels(int dev, short channels) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (channels == 1 || channels == 2) devc->channels = channels; - - return devc->channels; -} - -static struct audio_driver ess_audio_driver = /* ESS ES688/1688 */ -{ - owner: THIS_MODULE, - open: sb_audio_open, - close: sb_audio_close, - output_block: ess_set_output_parms, - start_input: ess_set_input_parms, - prepare_for_input: ess_audio_prepare_for_input, - prepare_for_output: ess_audio_prepare_for_output, - halt_io: ess_audio_halt_xfer, - trigger: ess_audio_trigger, - set_speed: ess_audio_set_speed, - set_bits: ess_audio_set_bits, - set_channels: ess_audio_set_channels -}; - -/* - * ess_audio_init must be called from sb_audio_init - */ -struct audio_driver *ess_audio_init - (sb_devc *devc, int *audio_flags, int *format_mask) -{ - *audio_flags = DMA_AUTOMODE; - *format_mask |= AFMT_S16_LE; - - if (devc->duplex) { - int tmp_dma; - /* - * sb_audio_init thinks dma8 is for playback and - * dma16 is for record. Not now! So swap them. - */ - tmp_dma = devc->dma16; - devc->dma16 = devc->dma8; - devc->dma8 = tmp_dma; - - *audio_flags |= DMA_DUPLEX; - } - - return &ess_audio_driver; -} - -/**************************************************************************** - * * - * ESS common * - * * - ****************************************************************************/ -static void ess_handle_channel - (char *channel, int dev, int intr_active, unsigned char flag, int irq_mode) -{ - if (!intr_active || !flag) return; -#ifdef FKS_REG_LOGGING -printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode); -#endif - switch (irq_mode) { - case IMODE_OUTPUT: - DMAbuf_outputintr (dev, 1); - break; - - case IMODE_INPUT: - DMAbuf_inputintr (dev); - break; - - case IMODE_INIT: - break; - - default:; - /* printk(KERN_WARN "ESS: Unexpected interrupt\n"); */ - } -} - -/* - * FKS: TODO!!! Finish this! - * - * I think midi stuff uses uart401, without interrupts. - * So IMODE_MIDI isn't a value for devc->irq_mode. - */ -void ess_intr (sb_devc *devc) -{ - int status; - unsigned char src; - - if (devc->submodel == SUBMDL_ES1887) { - src = ess_getmixer (devc, 0x7f) >> 4; - } else { - src = 0xff; - } - -#ifdef FKS_REG_LOGGING -printk(KERN_INFO "FKS: sbintr src=%x\n",(int)src); -#endif - ess_handle_channel - ( "Audio 1" - , devc->dev, devc->intr_active , src & 0x01, devc->irq_mode ); - ess_handle_channel - ( "Audio 2" - , devc->dev, devc->intr_active_16, src & 0x02, devc->irq_mode_16); - /* - * Acknowledge interrupts - */ - if (devc->submodel == SUBMDL_ES1887 && (src & 0x02)) { - ess_chgmixer (devc, 0x7a, 0x80, 0x00); - } - - if (src & 0x01) { - status = inb(DSP_DATA_AVAIL); - } -} - -static void ess_extended (sb_devc * devc) -{ - /* Enable extended mode */ - - sb_dsp_command(devc, 0xc6); -} - -static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data) -{ -#ifdef FKS_REG_LOGGING -printk(KERN_INFO "FKS: write reg %x: %x\n", reg, data); -#endif - /* Write a byte to an extended mode register of ES1688 */ - - if (!sb_dsp_command(devc, reg)) - return 0; - - return sb_dsp_command(devc, data); -} - -static int ess_read (sb_devc * devc, unsigned char reg) -{ - /* Read a byte from an extended mode register of ES1688 */ - - /* Read register command */ - if (!sb_dsp_command(devc, 0xc0)) return -1; - - if (!sb_dsp_command(devc, reg )) return -1; - - return sb_dsp_get_byte(devc); -} - -int ess_dsp_reset(sb_devc * devc) -{ - int loopc; - -#ifdef FKS_REG_LOGGING -printk(KERN_INFO "FKS: ess_dsp_reset 1\n"); -ess_show_mixerregs (devc); -#endif - - DEB(printk("Entered ess_dsp_reset()\n")); - - outb(3, DSP_RESET); /* Reset FIFO too */ - - udelay(10); - outb(0, DSP_RESET); - udelay(30); - - for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++); - - if (inb(DSP_READ) != 0xAA) { - DDB(printk("sb: No response to RESET\n")); - return 0; /* Sorry */ - } - ess_extended (devc); - - DEB(printk("sb_dsp_reset() OK\n")); - -#ifdef FKS_LOGGING -printk(KERN_INFO "FKS: dsp_reset 2\n"); -ess_show_mixerregs (devc); -#endif - - return 1; -} - -static int ess_irq_bits (int irq) -{ - switch (irq) { - case 2: - case 9: - return 0; - - case 5: - return 1; - - case 7: - return 2; - - case 10: - return 3; - - default: - printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", irq); - return -1; - } -} - -/* - * Set IRQ configuration register for all ESS models - */ -static int ess_common_set_irq_hw (sb_devc * devc) -{ - int irq_bits; - - if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return 0; - - if (!ess_write (devc, 0xb1, 0x50 | (irq_bits << 2))) { - printk(KERN_ERR "ES1688: Failed to write to IRQ config register\n"); - return 0; - } - return 1; -} - -/* - * I wanna use modern ES1887 mixer irq handling. Funny is the - * fact that my BIOS wants the same. But suppose someone's BIOS - * doesn't do this! - * This is independent of duplex. If there's a 1887 this will - * prevent it from going into 1888 mode. - */ -static void ess_es1887_set_irq_hw (sb_devc * devc) -{ - int irq_bits; - - if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return; - - ess_chgmixer (devc, 0x7f, 0x0f, 0x01 | ((irq_bits + 1) << 1)); -} - -static int ess_set_irq_hw (sb_devc * devc) -{ - if (devc->submodel == SUBMDL_ES1887) ess_es1887_set_irq_hw (devc); - - return ess_common_set_irq_hw (devc); -} - -#ifdef FKS_TEST - -/* - * FKS_test: - * for ES1887: 00, 18, non wr bits: 0001 1000 - * for ES1868: 00, b8, non wr bits: 1011 1000 - * for ES1888: 00, f8, non wr bits: 1111 1000 - * for ES1688: 00, f8, non wr bits: 1111 1000 - * + ES968 - */ - -static void FKS_test (sb_devc * devc) -{ - int val1, val2; - val1 = ess_getmixer (devc, 0x64); - ess_setmixer (devc, 0x64, ~val1); - val2 = ess_getmixer (devc, 0x64) ^ ~val1; - ess_setmixer (devc, 0x64, val1); - val1 ^= ess_getmixer (devc, 0x64); -printk (KERN_INFO "FKS: FKS_test %02x, %02x\n", (val1 & 0x0ff), (val2 & 0x0ff)); -}; -#endif - -static unsigned int ess_identify (sb_devc * devc) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&devc->lock, flags); - outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR); - - udelay(20); - val = inb(MIXER_DATA) << 8; - udelay(20); - val |= inb(MIXER_DATA); - udelay(20); - spin_unlock_irqrestore(&devc->lock, flags); - - return val; -} - -/* - * ESS technology describes a detection scheme in their docs. It involves - * fiddling with the bits in certain mixer registers. ess_probe is supposed - * to help. - * - * FKS: tracing shows ess_probe writes wrong value to 0x64. Bit 3 reads 1, but - * should be written 0 only. Check this. - */ -static int ess_probe (sb_devc * devc, int reg, int xorval) -{ - int val1, val2, val3; - - val1 = ess_getmixer (devc, reg); - val2 = val1 ^ xorval; - ess_setmixer (devc, reg, val2); - val3 = ess_getmixer (devc, reg); - ess_setmixer (devc, reg, val1); - - return (val2 == val3); -} - -int ess_init(sb_devc * devc, struct address_info *hw_config) -{ - unsigned char cfg; - int ess_major = 0, ess_minor = 0; - int i; - static char name[100], modelname[10]; - - /* - * Try to detect ESS chips. - */ - - sb_dsp_command(devc, 0xe7); /* Return identification */ - - for (i = 1000; i; i--) { - if (inb(DSP_DATA_AVAIL) & 0x80) { - if (ess_major == 0) { - ess_major = inb(DSP_READ); - } else { - ess_minor = inb(DSP_READ); - break; - } - } - } - - if (ess_major == 0) return 0; - - if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) { - sprintf(name, "ESS ES488 AudioDrive (rev %d)", - ess_minor & 0x0f); - hw_config->name = name; - devc->model = MDL_SBPRO; - return 1; - } - - /* - * This the detection heuristic of ESS technology, though somewhat - * changed to actually make it work. - * This results in the following detection steps: - * - distinct between ES688 and ES1688+ (as always done in this driver) - * if ES688 we're ready - * - try to detect ES1868, ES1869 or ES1878 (ess_identify) - * if successful we're ready - * - try to detect ES1888, ES1887 or ES1788 (aim: detect ES1887) - * if successful we're ready - * - Dunno. Must be 1688. Will do in general - * - * This is the most BETA part of the software: Will the detection - * always work? - */ - devc->model = MDL_ESS; - devc->submodel = ess_minor & 0x0f; - - if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) { - char *chip = NULL; - int submodel = -1; - - switch (devc->sbmo.esstype) { - case ESSTYPE_DETECT: - case ESSTYPE_LIKE20: - break; - case 688: - submodel = 0x00; - break; - case 1688: - submodel = 0x08; - break; - case 1868: - submodel = SUBMDL_ES1868; - break; - case 1869: - submodel = SUBMDL_ES1869; - break; - case 1788: - submodel = SUBMDL_ES1788; - break; - case 1878: - submodel = SUBMDL_ES1878; - break; - case 1879: - submodel = SUBMDL_ES1879; - break; - case 1887: - submodel = SUBMDL_ES1887; - break; - case 1888: - submodel = SUBMDL_ES1888; - break; - default: - printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype); - return 0; - }; - if (submodel != -1) { - devc->submodel = submodel; - sprintf (modelname, "ES%d", devc->sbmo.esstype); - chip = modelname; - }; - if (chip == NULL && (ess_minor & 0x0f) < 8) { - chip = "ES688"; - }; -#ifdef FKS_TEST -FKS_test (devc); -#endif - /* - * If Nothing detected yet, and we want 2.0 behaviour... - * Then let's assume it's ES1688. - */ - if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) { - chip = "ES1688"; - }; - - if (chip == NULL) { - int type; - - type = ess_identify (devc); - - switch (type) { - case 0x1868: - chip = "ES1868"; - devc->submodel = SUBMDL_ES1868; - break; - case 0x1869: - chip = "ES1869"; - devc->submodel = SUBMDL_ES1869; - break; - case 0x1878: - chip = "ES1878"; - devc->submodel = SUBMDL_ES1878; - break; - case 0x1879: - chip = "ES1879"; - devc->submodel = SUBMDL_ES1879; - break; - default: - if ((type & 0x00ff) != ((type >> 8) & 0x00ff)) { - printk ("ess_init: Unrecognized %04x\n", type); - } - }; - }; -#if 0 - /* - * this one failed: - * the probing of bit 4 is another thought: from ES1788 and up, all - * chips seem to have hardware volume control. Bit 4 is readonly to - * check if a hardware volume interrupt has fired. - * Cause ES688/ES1688 don't have this feature, bit 4 might be writeable - * for these chips. - */ - if (chip == NULL && !ess_probe(devc, 0x64, (1 << 4))) { -#endif - /* - * the probing of bit 2 is my idea. The ES1887 docs want me to probe - * bit 3. This results in ES1688 being detected as ES1788. - * Bit 2 is for "Enable HWV IRQE", but as ES(1)688 chips don't have - * HardWare Volume, I think they don't have this IRQE. - */ - if (chip == NULL && ess_probe(devc, 0x64, (1 << 2))) { - if (ess_probe (devc, 0x70, 0x7f)) { - if (ess_probe (devc, 0x64, (1 << 5))) { - chip = "ES1887"; - devc->submodel = SUBMDL_ES1887; - } else { - chip = "ES1888"; - devc->submodel = SUBMDL_ES1888; - } - } else { - chip = "ES1788"; - devc->submodel = SUBMDL_ES1788; - } - }; - if (chip == NULL) { - chip = "ES1688"; - }; - - printk ( KERN_INFO "ESS chip %s %s%s\n" - , chip - , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20 - ? "detected" - : "specified" - ) - , ( devc->sbmo.esstype == ESSTYPE_LIKE20 - ? " (kernel 2.0 compatible)" - : "" - ) - ); - - sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f); - } else { - strcpy(name, "Jazz16"); - } - - /* AAS: info stolen from ALSA: these boards have different clocks */ - switch(devc->submodel) { -/* APPARENTLY NOT 1869 AND 1887 - case SUBMDL_ES1869: - case SUBMDL_ES1887: -*/ - case SUBMDL_ES1888: - devc->caps |= SB_CAP_ES18XX_RATE; - break; - } - - hw_config->name = name; - /* FKS: sb_dsp_reset to enable extended mode???? */ - sb_dsp_reset(devc); /* Turn on extended mode */ - - /* - * Enable joystick and OPL3 - */ - cfg = ess_getmixer (devc, 0x40); - ess_setmixer (devc, 0x40, cfg | 0x03); - if (devc->submodel >= 8) { /* ES1688 */ - devc->caps |= SB_NO_MIDI; /* ES1688 uses MPU401 MIDI mode */ - } - sb_dsp_reset (devc); - - /* - * This is important! If it's not done, the IRQ probe in sb_dsp_init - * may fail. - */ - return ess_set_irq_hw (devc); -} - -static int ess_set_dma_hw(sb_devc * devc) -{ - unsigned char cfg, dma_bits = 0, dma16_bits; - int dma; - -#ifdef FKS_LOGGING -printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n" -, devc->dma8, devc->dma16, devc->duplex); -#endif - - /* - * FKS: It seems as if this duplex flag isn't set yet. Check it. - */ - dma = devc->dma8; - - if (dma > 3 || dma < 0 || dma == 2) { - dma_bits = 0; - printk(KERN_ERR "ESS1688: Invalid DMA8 %d\n", dma); - return 0; - } else { - /* Extended mode DMA enable */ - cfg = 0x50; - - if (dma == 3) { - dma_bits = 3; - } else { - dma_bits = dma + 1; - } - } - - if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) { - printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n"); - return 0; - } - - if (devc->duplex) { - dma = devc->dma16; - dma16_bits = 0; - - if (dma >= 0) { - switch (dma) { - case 0: - dma_bits = 0x04; - break; - case 1: - dma_bits = 0x05; - break; - case 3: - dma_bits = 0x06; - break; - case 5: - dma_bits = 0x07; - dma16_bits = 0x20; - break; - default: - printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma); - return 0; - }; - ess_chgmixer (devc, 0x78, 0x20, dma16_bits); - ess_chgmixer (devc, 0x7d, 0x07, dma_bits); - } - } - return 1; -} - -/* - * This one is called from sb_dsp_init. - * - * Return values: - * 0: Failed - * 1: Succeeded or doesn't apply (not SUBMDL_ES1887) - */ -int ess_dsp_init (sb_devc *devc, struct address_info *hw_config) -{ - /* - * Caller also checks this, but anyway - */ - if (devc->model != MDL_ESS) { - printk (KERN_INFO "ess_dsp_init for non ESS chip\n"); - return 1; - } - /* - * This for ES1887 to run Full Duplex. Actually ES1888 - * is allowed to do so too. I have no idea yet if this - * will work for ES1888 however. - * - * For SB16 having both dma8 and dma16 means enable - * Full Duplex. Let's try this for ES1887 too - * - */ - if (devc->submodel == SUBMDL_ES1887) { - if (hw_config->dma2 != -1) { - devc->dma16 = hw_config->dma2; - } - /* - * devc->duplex initialization is put here, cause - * ess_set_dma_hw needs it. - */ - if (devc->dma8 != devc->dma16 && devc->dma16 != -1) { - devc->duplex = 1; - } - } - if (!ess_set_dma_hw (devc)) { - free_irq(devc->irq, devc); - return 0; - } - return 1; -} - -/**************************************************************************** - * * - * ESS mixer * - * * - ****************************************************************************/ - -#define ES688_RECORDING_DEVICES \ - ( SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD ) -#define ES688_MIXER_DEVICES \ - ( SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE \ - | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME \ - | SOUND_MASK_LINE2 | SOUND_MASK_SPEAKER ) - -#define ES1688_RECORDING_DEVICES \ - ( ES688_RECORDING_DEVICES ) -#define ES1688_MIXER_DEVICES \ - ( ES688_MIXER_DEVICES | SOUND_MASK_RECLEV ) - -#define ES1887_RECORDING_DEVICES \ - ( ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 | SOUND_MASK_SYNTH) -#define ES1887_MIXER_DEVICES \ - ( ES1688_MIXER_DEVICES ) - -/* - * Mixer registers of ES1887 - * - * These registers specifically take care of recording levels. To make the - * mapping from playback devices to recording devices every recording - * devices = playback device + ES_REC_MIXER_RECDIFF - */ -#define ES_REC_MIXER_RECBASE (SOUND_MIXER_LINE3 + 1) -#define ES_REC_MIXER_RECDIFF (ES_REC_MIXER_RECBASE - SOUND_MIXER_SYNTH) - -#define ES_REC_MIXER_RECSYNTH (SOUND_MIXER_SYNTH + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECPCM (SOUND_MIXER_PCM + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECSPEAKER (SOUND_MIXER_SPEAKER + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECLINE (SOUND_MIXER_LINE + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECMIC (SOUND_MIXER_MIC + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECCD (SOUND_MIXER_CD + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECIMIX (SOUND_MIXER_IMIX + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECALTPCM (SOUND_MIXER_ALTPCM + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECRECLEV (SOUND_MIXER_RECLEV + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECIGAIN (SOUND_MIXER_IGAIN + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECOGAIN (SOUND_MIXER_OGAIN + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECLINE1 (SOUND_MIXER_LINE1 + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECLINE2 (SOUND_MIXER_LINE2 + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECLINE3 (SOUND_MIXER_LINE3 + ES_REC_MIXER_RECDIFF) - -static mixer_tab es688_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), -MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), -MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) -}; - -/* - * The ES1688 specifics... hopefully correct... - * - 6 bit master volume - * I was wrong, ES1888 docs say ES1688 didn't have it. - * - RECLEV control - * These may apply to ES688 too. I have no idea. - */ -static mixer_tab es1688_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), -MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), -MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) -}; - -static mixer_tab es1688later_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), -MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), -MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) -}; - -/* - * This one is for all ESS chips with a record mixer. - * It's not used (yet) however - */ -static mixer_tab es_rec_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), -MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), -MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4), -MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4), -MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4), -MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4), -MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4), -MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0) -}; - -/* - * This one is for ES1887. It's little different from es_rec_mix: it - * has 0x7c for PCM playback level. This is because ES1887 uses - * Audio 2 for playback. - */ -static mixer_tab es1887_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x7c, 7, 4, 0x7c, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), -MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), -MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4), -MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4), -MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4), -MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4), -MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4), -MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0) -}; - -static int ess_has_rec_mixer (int submodel) -{ - switch (submodel) { - case SUBMDL_ES1887: - return 1; - default: - return 0; - }; -}; - -#ifdef FKS_LOGGING -static int ess_mixer_mon_regs[] - = { 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7d, 0x7f - , 0xa1, 0xa2, 0xa4, 0xa5, 0xa8, 0xa9 - , 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9 - , 0x00}; - -static void ess_show_mixerregs (sb_devc *devc) -{ - int *mp = ess_mixer_mon_regs; - -return; - - while (*mp != 0) { - printk (KERN_INFO "res (%x)=%x\n", *mp, (int)(ess_getmixer (devc, *mp))); - mp++; - } -} -#endif - -void ess_setmixer (sb_devc * devc, unsigned int port, unsigned int value) -{ - unsigned long flags; - -#ifdef FKS_LOGGING -printk(KERN_INFO "FKS: write mixer %x: %x\n", port, value); -#endif - - spin_lock_irqsave(&devc->lock, flags); - if (port >= 0xa0) { - ess_write (devc, port, value); - } else { - outb(((unsigned char) (port & 0xff)), MIXER_ADDR); - - udelay(20); - outb(((unsigned char) (value & 0xff)), MIXER_DATA); - udelay(20); - }; - spin_unlock_irqrestore(&devc->lock, flags); -} - -unsigned int ess_getmixer (sb_devc * devc, unsigned int port) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&devc->lock, flags); - - if (port >= 0xa0) { - val = ess_read (devc, port); - } else { - outb(((unsigned char) (port & 0xff)), MIXER_ADDR); - - udelay(20); - val = inb(MIXER_DATA); - udelay(20); - } - spin_unlock_irqrestore(&devc->lock, flags); - - return val; -} - -static void ess_chgmixer - (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val) -{ - int value; - - value = ess_getmixer (devc, reg); - value = (value & ~mask) | (val & mask); - ess_setmixer (devc, reg, value); -} - -/* - * ess_mixer_init must be called from sb_mixer_init - */ -void ess_mixer_init (sb_devc * devc) -{ - devc->mixer_caps = SOUND_CAP_EXCL_INPUT; - - /* - * Take care of ES1887 specifics... - */ - switch (devc->submodel) { - case SUBMDL_ES1887: - devc->supported_devices = ES1887_MIXER_DEVICES; - devc->supported_rec_devices = ES1887_RECORDING_DEVICES; -#ifdef FKS_LOGGING -printk (KERN_INFO "FKS: ess_mixer_init dup = %d\n", devc->duplex); -#endif - if (devc->duplex) { - devc->iomap = &es1887_mix; - } else { - devc->iomap = &es_rec_mix; - } - break; - default: - if (devc->submodel < 8) { - devc->supported_devices = ES688_MIXER_DEVICES; - devc->supported_rec_devices = ES688_RECORDING_DEVICES; - devc->iomap = &es688_mix; - } else { - /* - * es1688 has 4 bits master vol. - * later chips have 6 bits (?) - */ - devc->supported_devices = ES1688_MIXER_DEVICES; - devc->supported_rec_devices = ES1688_RECORDING_DEVICES; - if (devc->submodel < 0x10) { - devc->iomap = &es1688_mix; - } else { - devc->iomap = &es1688later_mix; - } - } - } -} - -/* - * Changing playback levels at an ESS chip with record mixer means having to - * take care of recording levels of recorded inputs (devc->recmask) too! - */ -int ess_mixer_set(sb_devc *devc, int dev, int left, int right) -{ - if (ess_has_rec_mixer (devc->submodel) && (devc->recmask & (1 << dev))) { - sb_common_mixer_set (devc, dev + ES_REC_MIXER_RECDIFF, left, right); - } - return sb_common_mixer_set (devc, dev, left, right); -} - -/* - * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After - * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload - * helps. - */ -void ess_mixer_reload (sb_devc *devc, int dev) -{ - int left, right, value; - - value = devc->levels[dev]; - left = value & 0x000000ff; - right = (value & 0x0000ff00) >> 8; - - sb_common_mixer_set(devc, dev, left, right); -} - -int es_rec_set_recmask(sb_devc * devc, int mask) -{ - int i, i_mask, cur_mask, diff_mask; - int value, left, right; - -#ifdef FKS_LOGGING -printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask); -#endif - /* - * Changing the recmask on an ESS chip with recording mixer means: - * (1) Find the differences - * (2) For "turned-on" inputs: make the recording level the playback level - * (3) For "turned-off" inputs: make the recording level zero - */ - cur_mask = devc->recmask; - diff_mask = (cur_mask ^ mask); - - for (i = 0; i < 32; i++) { - i_mask = (1 << i); - if (diff_mask & i_mask) { /* Difference? (1) */ - if (mask & i_mask) { /* Turn it on (2) */ - value = devc->levels[i]; - left = value & 0x000000ff; - right = (value & 0x0000ff00) >> 8; - } else { /* Turn it off (3) */ - left = 0; - left = 0; - right = 0; - } - sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right); - } - } - return mask; -} - -int ess_set_recmask(sb_devc * devc, int *mask) -{ - /* This applies to ESS chips with record mixers only! */ - - if (ess_has_rec_mixer (devc->submodel)) { - *mask = es_rec_set_recmask (devc, *mask); - return 1; /* Applied */ - } else { - return 0; /* Not applied */ - } -} - -/* - * ess_mixer_reset must be called from sb_mixer_reset - */ -int ess_mixer_reset (sb_devc * devc) -{ - /* - * Separate actions for ESS chips with a record mixer: - */ - if (ess_has_rec_mixer (devc->submodel)) { - switch (devc->submodel) { - case SUBMDL_ES1887: - /* - * Separate actions for ES1887: - * Change registers 7a and 1c to make the record mixer the - * actual recording source. - */ - ess_chgmixer(devc, 0x7a, 0x18, 0x08); - ess_chgmixer(devc, 0x1c, 0x07, 0x07); - break; - }; - /* - * Call set_recmask for proper initialization - */ - devc->recmask = devc->supported_rec_devices; - es_rec_set_recmask(devc, 0); - devc->recmask = 0; - - return 1; /* We took care of recmask. */ - } else { - return 0; /* We didn't take care; caller do it */ - } -} - -/**************************************************************************** - * * - * ESS midi * - * * - ****************************************************************************/ - -/* - * FKS: IRQ may be shared. Hm. And if so? Then What? - */ -int ess_midi_init(sb_devc * devc, struct address_info *hw_config) -{ - unsigned char cfg, tmp; - - cfg = ess_getmixer (devc, 0x40) & 0x03; - - if (devc->submodel < 8) { - ess_setmixer (devc, 0x40, cfg | 0x03); /* Enable OPL3 & joystick */ - return 0; /* ES688 doesn't support MPU401 mode */ - } - tmp = (hw_config->io_base & 0x0f0) >> 4; - - if (tmp > 3) { - ess_setmixer (devc, 0x40, cfg); - return 0; - } - cfg |= tmp << 3; - - tmp = 1; /* MPU enabled without interrupts */ - - /* May be shared: if so the value is -ve */ - - switch (abs(hw_config->irq)) { - case 9: - tmp = 0x4; - break; - case 5: - tmp = 0x5; - break; - case 7: - tmp = 0x6; - break; - case 10: - tmp = 0x7; - break; - default: - return 0; - } - - cfg |= tmp << 5; - ess_setmixer (devc, 0x40, cfg | 0x03); - - return 1; -} - diff -Nru a/drivers/sound/sb_ess.h b/drivers/sound/sb_ess.h --- a/drivers/sound/sb_ess.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,34 +0,0 @@ -/* - * Created: 9-Jan-1999 Rolf Fokkens - */ - -extern void ess_intr - (sb_devc *devc); -extern int ess_dsp_init - (sb_devc *devc, struct address_info *hw_config); - -extern struct audio_driver *ess_audio_init - (sb_devc *devc, int *audio_flags, int *format_mask); -extern int ess_midi_init - (sb_devc *devc, struct address_info *hw_config); -extern void ess_mixer_init - (sb_devc *devc); - -extern int ess_init - (sb_devc *devc, struct address_info *hw_config); -extern int ess_dsp_reset - (sb_devc *devc); - -extern void ess_setmixer - (sb_devc *devc, unsigned int port, unsigned int value); -extern unsigned int ess_getmixer - (sb_devc *devc, unsigned int port); -extern int ess_mixer_set - (sb_devc *devc, int dev, int left, int right); -extern int ess_mixer_reset - (sb_devc *devc); -extern void ess_mixer_reload - (sb_devc * devc, int dev); -extern int ess_set_recmask - (sb_devc *devc, int *mask); - diff -Nru a/drivers/sound/sb_midi.c b/drivers/sound/sb_midi.c --- a/drivers/sound/sb_midi.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,205 +0,0 @@ -/* - * sound/sb_dsp.c - * - * The low level driver for the Sound Blaster DS chips. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - -#include - -#include "sound_config.h" - -#include "sb.h" -#undef SB_TEST_IRQ - -/* - * The DSP channel can be used either for input or output. Variable - * 'sb_irq_mode' will be set when the program calls read or write first time - * after open. Current version doesn't support mode changes without closing - * and reopening the device. Support for this feature may be implemented in a - * future version of this driver. - */ - - -static int sb_midi_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - sb_devc *devc = midi_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - return -ENXIO; - - spin_lock_irqsave(&devc->lock, flags); - if (devc->opened) - { - spin_unlock_irqrestore(&devc->lock, flags); - return -EBUSY; - } - devc->opened = 1; - spin_unlock_irqrestore(&devc->lock, flags); - - devc->irq_mode = IMODE_MIDI; - devc->midi_broken = 0; - - sb_dsp_reset(devc); - - if (!sb_dsp_command(devc, 0x35)) /* Start MIDI UART mode */ - { - devc->opened = 0; - return -EIO; - } - devc->intr_active = 1; - - if (mode & OPEN_READ) - { - devc->input_opened = 1; - devc->midi_input_intr = input; - } - return 0; -} - -static void sb_midi_close(int dev) -{ - sb_devc *devc = midi_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - return; - - spin_lock_irqsave(&devc->lock, flags); - sb_dsp_reset(devc); - devc->intr_active = 0; - devc->input_opened = 0; - devc->opened = 0; - spin_unlock_irqrestore(&devc->lock, flags); -} - -static int sb_midi_out(int dev, unsigned char midi_byte) -{ - sb_devc *devc = midi_devs[dev]->devc; - - if (devc == NULL) - return 1; - - if (devc->midi_broken) - return 1; - - if (!sb_dsp_command(devc, midi_byte)) - { - devc->midi_broken = 1; - return 1; - } - return 1; -} - -static int sb_midi_start_read(int dev) -{ - return 0; -} - -static int sb_midi_end_read(int dev) -{ - sb_devc *devc = midi_devs[dev]->devc; - - if (devc == NULL) - return -ENXIO; - - sb_dsp_reset(devc); - devc->intr_active = 0; - return 0; -} - -static int sb_midi_ioctl(int dev, unsigned cmd, caddr_t arg) -{ - return -EINVAL; -} - -void sb_midi_interrupt(sb_devc * devc) -{ - unsigned long flags; - unsigned char data; - - if (devc == NULL) - return; - - spin_lock_irqsave(&devc->lock, flags); - - data = inb(DSP_READ); - if (devc->input_opened) - devc->midi_input_intr(devc->my_mididev, data); - - spin_unlock_irqrestore(&devc->lock, flags); -} - -#define MIDI_SYNTH_NAME "Sound Blaster Midi" -#define MIDI_SYNTH_CAPS 0 -#include "midi_synth.h" - -static struct midi_operations sb_midi_operations = -{ - owner: THIS_MODULE, - info: {"Sound Blaster", 0, 0, SNDCARD_SB}, - converter: &std_midi_synth, - in_info: {0}, - open: sb_midi_open, - close: sb_midi_close, - ioctl: sb_midi_ioctl, - outputc: sb_midi_out, - start_read: sb_midi_start_read, - end_read: sb_midi_end_read, -}; - -void sb_dsp_midi_init(sb_devc * devc, struct module *owner) -{ - int dev; - - if (devc->model < 2) /* No MIDI support for SB 1.x */ - return; - - dev = sound_alloc_mididev(); - - if (dev == -1) - { - printk(KERN_ERR "sb_midi: too many MIDI devices detected\n"); - return; - } - std_midi_synth.midi_dev = devc->my_mididev = dev; - midi_devs[dev] = (struct midi_operations *)kmalloc(sizeof(struct midi_operations), GFP_KERNEL); - if (midi_devs[dev] == NULL) - { - printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); - sound_unload_mididev(dev); - return; - } - memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations, - sizeof(struct midi_operations)); - - if (owner) - midi_devs[dev]->owner = owner; - - midi_devs[dev]->devc = devc; - - - midi_devs[dev]->converter = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); - if (midi_devs[dev]->converter == NULL) - { - printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); - kfree(midi_devs[dev]); - sound_unload_mididev(dev); - return; - } - memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth, - sizeof(struct synth_operations)); - - midi_devs[dev]->converter->id = "SBMIDI"; - sequencer_init(); -} diff -Nru a/drivers/sound/sb_mixer.c b/drivers/sound/sb_mixer.c --- a/drivers/sound/sb_mixer.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,757 +0,0 @@ -/* - * sound/sb_mixer.c - * - * The low level mixer driver for the Sound Blaster compatible cards. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Rolf Fokkens (Dec 20 1998) : Moved ESS stuff into sb_ess.[ch] - * Stanislav Voronyi : Support for AWE 3DSE device (Jun 7 1999) - */ - -#include "sound_config.h" - -#define __SB_MIXER_C__ - -#include "sb.h" -#include "sb_mixer.h" - -#include "sb_ess.h" - -#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) - -/* Same as SB Pro, unless I find otherwise */ -#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES - -#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_VOLUME) - -/* SG NX Pro has treble and bass settings on the mixer. The 'speaker' - * channel is the COVOX/DisneySoundSource emulation volume control - * on the mixer. It does NOT control speaker volume. Should have own - * mask eventually? - */ -#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \ - SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER ) - -#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD) - -#define SB16_OUTFILTER_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD) - -#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | \ - SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \ - SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \ - SOUND_MASK_IMIX) - -/* These are the only devices that are working at the moment. Others could - * be added once they are identified and a method is found to control them. - */ -#define ALS007_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \ - SOUND_MASK_PCM | SOUND_MASK_MIC | \ - SOUND_MASK_CD | \ - SOUND_MASK_VOLUME) - -static mixer_tab sbpro_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0) -}; - -static mixer_tab sb16_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5), -MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4), -MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4), -MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5), -MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5), -MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5), -MIX_ENT(SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */ -MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2), -MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2) -}; - -static mixer_tab als007_mix = -{ -MIX_ENT(SOUND_MIXER_VOLUME, 0x62, 7, 4, 0x62, 3, 4), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x66, 7, 4, 0x66, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x64, 7, 4, 0x64, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x6e, 7, 4, 0x6e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x6a, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_CD, 0x68, 7, 4, 0x68, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */ -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0) -}; - - -/* SM_GAMES Master volume is lower and PCM & FM volumes - higher than with SB Pro. This improves the - sound quality */ - -static int smg_default_levels[32] = -{ - 0x2020, /* Master Volume */ - 0x4b4b, /* Bass */ - 0x4b4b, /* Treble */ - 0x6464, /* FM */ - 0x6464, /* PCM */ - 0x4b4b, /* PC Speaker */ - 0x4b4b, /* Ext Line */ - 0x0000, /* Mic */ - 0x4b4b, /* CD */ - 0x4b4b, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x4b4b, /* Recording level */ - 0x4b4b, /* Input gain */ - 0x4b4b, /* Output gain */ - 0x4040, /* Line1 */ - 0x4040, /* Line2 */ - 0x1515 /* Line3 */ -}; - -static int sb_default_levels[32] = -{ - 0x5a5a, /* Master Volume */ - 0x4b4b, /* Bass */ - 0x4b4b, /* Treble */ - 0x4b4b, /* FM */ - 0x4b4b, /* PCM */ - 0x4b4b, /* PC Speaker */ - 0x4b4b, /* Ext Line */ - 0x1010, /* Mic */ - 0x4b4b, /* CD */ - 0x0000, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x4b4b, /* Recording level */ - 0x4b4b, /* Input gain */ - 0x4b4b, /* Output gain */ - 0x4040, /* Line1 */ - 0x4040, /* Line2 */ - 0x1515 /* Line3 */ -}; - -static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = -{ - 0x00, /* SOUND_MIXER_VOLUME */ - 0x00, /* SOUND_MIXER_BASS */ - 0x00, /* SOUND_MIXER_TREBLE */ - 0x40, /* SOUND_MIXER_SYNTH */ - 0x00, /* SOUND_MIXER_PCM */ - 0x00, /* SOUND_MIXER_SPEAKER */ - 0x10, /* SOUND_MIXER_LINE */ - 0x01, /* SOUND_MIXER_MIC */ - 0x04, /* SOUND_MIXER_CD */ - 0x00, /* SOUND_MIXER_IMIX */ - 0x00, /* SOUND_MIXER_ALTPCM */ - 0x00, /* SOUND_MIXER_RECLEV */ - 0x00, /* SOUND_MIXER_IGAIN */ - 0x00 /* SOUND_MIXER_OGAIN */ -}; - -static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] = -{ - 0x00, /* SOUND_MIXER_VOLUME */ - 0x00, /* SOUND_MIXER_BASS */ - 0x00, /* SOUND_MIXER_TREBLE */ - 0x20, /* SOUND_MIXER_SYNTH */ - 0x00, /* SOUND_MIXER_PCM */ - 0x00, /* SOUND_MIXER_SPEAKER */ - 0x08, /* SOUND_MIXER_LINE */ - 0x01, /* SOUND_MIXER_MIC */ - 0x02, /* SOUND_MIXER_CD */ - 0x00, /* SOUND_MIXER_IMIX */ - 0x00, /* SOUND_MIXER_ALTPCM */ - 0x00, /* SOUND_MIXER_RECLEV */ - 0x00, /* SOUND_MIXER_IGAIN */ - 0x00 /* SOUND_MIXER_OGAIN */ -}; - -static char smw_mix_regs[] = /* Left mixer registers */ -{ - 0x0b, /* SOUND_MIXER_VOLUME */ - 0x0d, /* SOUND_MIXER_BASS */ - 0x0d, /* SOUND_MIXER_TREBLE */ - 0x05, /* SOUND_MIXER_SYNTH */ - 0x09, /* SOUND_MIXER_PCM */ - 0x00, /* SOUND_MIXER_SPEAKER */ - 0x03, /* SOUND_MIXER_LINE */ - 0x01, /* SOUND_MIXER_MIC */ - 0x07, /* SOUND_MIXER_CD */ - 0x00, /* SOUND_MIXER_IMIX */ - 0x00, /* SOUND_MIXER_ALTPCM */ - 0x00, /* SOUND_MIXER_RECLEV */ - 0x00, /* SOUND_MIXER_IGAIN */ - 0x00, /* SOUND_MIXER_OGAIN */ - 0x00, /* SOUND_MIXER_LINE1 */ - 0x00, /* SOUND_MIXER_LINE2 */ - 0x00 /* SOUND_MIXER_LINE3 */ -}; - -static int sbmixnum = 1; - -static void sb_mixer_reset(sb_devc * devc); - -void sb_mixer_set_stereo(sb_devc * devc, int mode) -{ - sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC)); -} - -static int detect_mixer(sb_devc * devc) -{ - /* Just trust the mixer is there */ - return 1; -} - -static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval) -{ - unsigned char mask; - int shift; - - mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1; - newval = (int) ((newval * mask) + 50) / 100; /* Scale */ - - shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1; - - *regval &= ~(mask << shift); /* Mask out previous value */ - *regval |= (newval & mask) << shift; /* Set the new value */ -} - -static int sb_mixer_get(sb_devc * devc, int dev) -{ - if (!((1 << dev) & devc->supported_devices)) - return -EINVAL; - return devc->levels[dev]; -} - -void smw_mixer_init(sb_devc * devc) -{ - int i; - - sb_setmixer(devc, 0x00, 0x18); /* Mute unused (Telephone) line */ - sb_setmixer(devc, 0x10, 0x38); /* Config register 2 */ - - devc->supported_devices = 0; - for (i = 0; i < sizeof(smw_mix_regs); i++) - if (smw_mix_regs[i] != 0) - devc->supported_devices |= (1 << i); - - devc->supported_rec_devices = devc->supported_devices & - ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME); - sb_mixer_reset(devc); -} - -int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right) -{ - int regoffs; - unsigned char val; - - regoffs = (*devc->iomap)[dev][LEFT_CHN].regno; - - if (regoffs == 0) - return -EINVAL; - - val = sb_getmixer(devc, regoffs); - change_bits(devc, &val, dev, LEFT_CHN, left); - - if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /* - * Change register - */ - { - sb_setmixer(devc, regoffs, val); /* - * Save the old one - */ - regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno; - - if (regoffs == 0) - return left | (left << 8); /* - * Just left channel present - */ - - val = sb_getmixer(devc, regoffs); /* - * Read the new one - */ - } - change_bits(devc, &val, dev, RIGHT_CHN, right); - - sb_setmixer(devc, regoffs, val); - - return left | (right << 8); -} - -static int smw_mixer_set(sb_devc * devc, int dev, int left, int right) -{ - int reg, val; - - switch (dev) - { - case SOUND_MIXER_VOLUME: - sb_setmixer(devc, 0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */ - sb_setmixer(devc, 0x0c, 96 - (96 * right / 100)); - break; - - case SOUND_MIXER_BASS: - case SOUND_MIXER_TREBLE: - devc->levels[dev] = left | (right << 8); - /* Set left bass and treble values */ - val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4; - val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f; - sb_setmixer(devc, 0x0d, val); - - /* Set right bass and treble values */ - val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4; - val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f; - sb_setmixer(devc, 0x0e, val); - - break; - - default: - reg = smw_mix_regs[dev]; - if (reg == 0) - return -EINVAL; - sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */ - sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40); - } - - devc->levels[dev] = left | (right << 8); - return left | (right << 8); -} - -static int sb_mixer_set(sb_devc * devc, int dev, int value) -{ - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int retval; - - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - if (dev > 31) - return -EINVAL; - - if (!(devc->supported_devices & (1 << dev))) /* - * Not supported - */ - return -EINVAL; - - /* Differentiate depending on the chipsets */ - switch (devc->model) { - case MDL_SMW: - retval = smw_mixer_set(devc, dev, left, right); - break; - case MDL_ESS: - retval = ess_mixer_set(devc, dev, left, right); - break; - default: - retval = sb_common_mixer_set(devc, dev, left, right); - } - if (retval >= 0) devc->levels[dev] = retval; - - return retval; -} - -/* - * set_recsrc doesn't apply to ES188x - */ -static void set_recsrc(sb_devc * devc, int src) -{ - sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7)); -} - -static int set_recmask(sb_devc * devc, int mask) -{ - int devmask, i; - unsigned char regimageL, regimageR; - - devmask = mask & devc->supported_rec_devices; - - switch (devc->model) - { - case MDL_SBPRO: - case MDL_ESS: - case MDL_JAZZ: - case MDL_SMW: - if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) { - break; - }; - if (devmask != SOUND_MASK_MIC && - devmask != SOUND_MASK_LINE && - devmask != SOUND_MASK_CD) - { - /* - * More than one device selected. Drop the - * previous selection - */ - devmask &= ~devc->recmask; - } - if (devmask != SOUND_MASK_MIC && - devmask != SOUND_MASK_LINE && - devmask != SOUND_MASK_CD) - { - /* - * More than one device selected. Default to - * mic - */ - devmask = SOUND_MASK_MIC; - } - if (devmask ^ devc->recmask) /* - * Input source changed - */ - { - switch (devmask) - { - case SOUND_MASK_MIC: - set_recsrc(devc, SRC__MIC); - break; - - case SOUND_MASK_LINE: - set_recsrc(devc, SRC__LINE); - break; - - case SOUND_MASK_CD: - set_recsrc(devc, SRC__CD); - break; - - default: - set_recsrc(devc, SRC__MIC); - } - } - break; - - case MDL_SB16: - if (!devmask) - devmask = SOUND_MASK_MIC; - - if (devc->submodel == SUBMDL_ALS007) - { - switch (devmask) - { - case SOUND_MASK_LINE: - sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE); - break; - case SOUND_MASK_CD: - sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD); - break; - case SOUND_MASK_SYNTH: - sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH); - break; - default: /* Also takes care of SOUND_MASK_MIC case */ - sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC); - break; - } - } - else - { - regimageL = regimageR = 0; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - { - if ((1 << i) & devmask) - { - regimageL |= sb16_recmasks_L[i]; - regimageR |= sb16_recmasks_R[i]; - } - sb_setmixer (devc, SB16_IMASK_L, regimageL); - sb_setmixer (devc, SB16_IMASK_R, regimageR); - } - } - break; - } - devc->recmask = devmask; - return devc->recmask; -} - -static int set_outmask(sb_devc * devc, int mask) -{ - int devmask, i; - unsigned char regimage; - - devmask = mask & devc->supported_out_devices; - - switch (devc->model) - { - case MDL_SB16: - if (devc->submodel == SUBMDL_ALS007) - break; - else - { - regimage = 0; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - { - if ((1 << i) & devmask) - { - regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]); - } - sb_setmixer (devc, SB16_OMASK, regimage); - } - } - break; - default: - break; - } - - devc->outmask = devmask; - return devc->outmask; -} - -static int sb_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - sb_devc *devc = mixer_devs[dev]->devc; - int val, ret; - - /* - * Use ioctl(fd, SOUND_MIXER_AGC, &mode) to turn AGC off (0) or on (1). - * Use ioctl(fd, SOUND_MIXER_3DSE, &mode) to turn 3DSE off (0) or on (1) - * or mode==2 put 3DSE state to mode. - */ - if (devc->model == MDL_SB16) { - if (cmd == SOUND_MIXER_AGC) - { - if (get_user(val, (int *)arg)) - return -EFAULT; - sb_setmixer(devc, 0x43, (~val) & 0x01); - return 0; - } - if (cmd == SOUND_MIXER_3DSE) - { - /* I put here 15, but I don't know the exact version. - At least my 4.13 havn't 3DSE, 4.16 has it. */ - if (devc->minor < 15) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val == 0 || val == 1) - sb_chgmixer(devc, AWE_3DSE, 0x01, val); - else if (val == 2) - { - ret = sb_getmixer(devc, AWE_3DSE)&0x01; - return put_user(ret, (int *)arg); - } - else - return -EINVAL; - return 0; - } - } - if (((cmd >> 8) & 0xff) == 'M') - { - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - { - if (get_user(val, (int *)arg)) - return -EFAULT; - switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - ret = set_recmask(devc, val); - break; - - case SOUND_MIXER_OUTSRC: - ret = set_outmask(devc, val); - break; - - default: - ret = sb_mixer_set(devc, cmd & 0xff, val); - } - } - else switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - ret = devc->recmask; - break; - - case SOUND_MIXER_OUTSRC: - ret = devc->outmask; - break; - - case SOUND_MIXER_DEVMASK: - ret = devc->supported_devices; - break; - - case SOUND_MIXER_STEREODEVS: - ret = devc->supported_devices; - /* The ESS seems to have stereo mic controls */ - if (devc->model == MDL_ESS) - ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX); - else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW) - ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); - break; - - case SOUND_MIXER_RECMASK: - ret = devc->supported_rec_devices; - break; - - case SOUND_MIXER_OUTMASK: - ret = devc->supported_out_devices; - break; - - case SOUND_MIXER_CAPS: - ret = devc->mixer_caps; - break; - - default: - ret = sb_mixer_get(devc, cmd & 0xff); - break; - } - return put_user(ret, (int *)arg); - } else - return -EINVAL; -} - -static struct mixer_operations sb_mixer_operations = -{ - owner: THIS_MODULE, - id: "SB", - name: "Sound Blaster", - ioctl: sb_mixer_ioctl -}; - -static struct mixer_operations als007_mixer_operations = -{ - owner: THIS_MODULE, - id: "ALS007", - name: "Avance ALS-007", - ioctl: sb_mixer_ioctl -}; - -static void sb_mixer_reset(sb_devc * devc) -{ - char name[32]; - int i; - - sprintf(name, "SB_%d", devc->sbmixnum); - - if (devc->sbmo.sm_games) - devc->levels = load_mixer_volumes(name, smg_default_levels, 1); - else - devc->levels = load_mixer_volumes(name, sb_default_levels, 1); - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - sb_mixer_set(devc, i, devc->levels[i]); - - if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) { - set_recmask(devc, SOUND_MASK_MIC); - }; -} - -int sb_mixer_init(sb_devc * devc, struct module *owner) -{ - int mixer_type = 0; - int m; - - devc->sbmixnum = sbmixnum++; - devc->levels = NULL; - - sb_setmixer(devc, 0x00, 0); /* Reset mixer */ - - if (!(mixer_type = detect_mixer(devc))) - return 0; /* No mixer. Why? */ - - switch (devc->model) - { - case MDL_ESSPCI: - case MDL_YMPCI: - case MDL_SBPRO: - case MDL_AZTECH: - case MDL_JAZZ: - devc->mixer_caps = SOUND_CAP_EXCL_INPUT; - devc->supported_devices = SBPRO_MIXER_DEVICES; - devc->supported_rec_devices = SBPRO_RECORDING_DEVICES; - devc->iomap = &sbpro_mix; - break; - - case MDL_ESS: - ess_mixer_init (devc); - break; - - case MDL_SMW: - devc->mixer_caps = SOUND_CAP_EXCL_INPUT; - devc->supported_devices = 0; - devc->supported_rec_devices = 0; - devc->iomap = &sbpro_mix; - smw_mixer_init(devc); - break; - - case MDL_SB16: - devc->mixer_caps = 0; - devc->supported_rec_devices = SB16_RECORDING_DEVICES; - devc->supported_out_devices = SB16_OUTFILTER_DEVICES; - if (devc->submodel != SUBMDL_ALS007) - { - devc->supported_devices = SB16_MIXER_DEVICES; - devc->iomap = &sb16_mix; - } - else - { - devc->supported_devices = ALS007_MIXER_DEVICES; - devc->iomap = &als007_mix; - } - break; - - default: - printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model); - return 0; - } - - m = sound_alloc_mixerdev(); - if (m == -1) - return 0; - - mixer_devs[m] = (struct mixer_operations *)kmalloc(sizeof(struct mixer_operations), GFP_KERNEL); - if (mixer_devs[m] == NULL) - { - printk(KERN_ERR "sb_mixer: Can't allocate memory\n"); - sound_unload_mixerdev(m); - return 0; - } - - if (devc->submodel != SUBMDL_ALS007) - memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations)); - else - memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations)); - - mixer_devs[m]->devc = devc; - - if (owner) - mixer_devs[m]->owner = owner; - - devc->my_mixerdev = m; - sb_mixer_reset(devc); - return 1; -} - -void sb_mixer_unload(sb_devc *devc) -{ - if (devc->my_mixerdev == -1) - return; - - kfree(mixer_devs[devc->my_mixerdev]); - sound_unload_mixerdev(devc->my_mixerdev); - sbmixnum--; -} diff -Nru a/drivers/sound/sb_mixer.h b/drivers/sound/sb_mixer.h --- a/drivers/sound/sb_mixer.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,105 +0,0 @@ -/* - * sound/sb_mixer.h - * - * Definitions for the SB Pro and SB16 mixers - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - -/* - * Modified: - * Hunyue Yau Jan 6 1994 - * Added defines for the Sound Galaxy NX Pro mixer. - * - * Rolf Fokkens Dec 20 1998 - * Added defines for some ES188x chips. - * - * Rolf Fokkens Dec 27 1998 - * Moved static stuff to sb_mixer.c - * - */ -/* - * Mixer registers - * - * NOTE! RECORD_SRC == IN_FILTER - */ - -/* - * Mixer registers of SB Pro - */ -#define VOC_VOL 0x04 -#define MIC_VOL 0x0A -#define MIC_MIX 0x0A -#define RECORD_SRC 0x0C -#define IN_FILTER 0x0C -#define OUT_FILTER 0x0E -#define MASTER_VOL 0x22 -#define FM_VOL 0x26 -#define CD_VOL 0x28 -#define LINE_VOL 0x2E -#define IRQ_NR 0x80 -#define DMA_NR 0x81 -#define IRQ_STAT 0x82 -#define OPSW 0x3c - -/* - * Additional registers on the SG NX Pro - */ -#define COVOX_VOL 0x42 -#define TREBLE_LVL 0x44 -#define BASS_LVL 0x46 - -#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */ -#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */ -#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */ -#define FILT_OFF (1 << 5) - -#define MONO_DAC 0x00 -#define STEREO_DAC 0x02 - -/* - * Mixer registers of SB16 - */ -#define SB16_OMASK 0x3c -#define SB16_IMASK_L 0x3d -#define SB16_IMASK_R 0x3e - -#define LEFT_CHN 0 -#define RIGHT_CHN 1 - -/* - * 3DSE register of AWE32/64 - */ -#define AWE_3DSE 0x90 - -/* - * Mixer registers of ALS007 - */ -#define ALS007_RECORD_SRC 0x6c -#define ALS007_OUTPUT_CTRL1 0x3c -#define ALS007_OUTPUT_CTRL2 0x4c - -#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \ - {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}} - -/* - * Recording sources (SB Pro) - */ - -#define SRC__MIC 1 /* Select Microphone recording source */ -#define SRC__CD 3 /* Select CD recording source */ -#define SRC__LINE 7 /* Use Line-in for recording source */ - -/* - * Recording sources for ALS-007 - */ - -#define ALS007_MIC 4 -#define ALS007_LINE 6 -#define ALS007_CD 2 -#define ALS007_SYNTH 7 diff -Nru a/drivers/sound/sequencer.c b/drivers/sound/sequencer.c --- a/drivers/sound/sequencer.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1699 +0,0 @@ -/* - * sound/sequencer.c - * - * The sequencer personality manager. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Alan Cox : reformatted and fixed a pair of null pointer bugs - */ -#include - -#define SEQUENCER_C -#include "sound_config.h" - -#include "midi_ctrl.h" - -static int sequencer_ok = 0; -static struct sound_timer_operations *tmr; -static int tmr_no = -1; /* Currently selected timer */ -static int pending_timer = -1; /* For timer change operation */ -extern unsigned long seq_time; - -static int obsolete_api_used = 0; - -/* - * Local counts for number of synth and MIDI devices. These are initialized - * by the sequencer_open. - */ -static int max_mididev = 0; -static int max_synthdev = 0; - -/* - * The seq_mode gives the operating mode of the sequencer: - * 1 = level1 (the default) - * 2 = level2 (extended capabilities) - */ - -#define SEQ_1 1 -#define SEQ_2 2 -static int seq_mode = SEQ_1; - -static DECLARE_WAIT_QUEUE_HEAD(seq_sleeper); -static DECLARE_WAIT_QUEUE_HEAD(midi_sleeper); - -static int midi_opened[MAX_MIDI_DEV] = { - 0 -}; - -static int midi_written[MAX_MIDI_DEV] = { - 0 -}; - -static unsigned long prev_input_time = 0; -static int prev_event_time; - -#include "tuning.h" - -#define EV_SZ 8 -#define IEV_SZ 8 - -static unsigned char *queue = NULL; -static unsigned char *iqueue = NULL; - -static volatile int qhead = 0, qtail = 0, qlen = 0; -static volatile int iqhead = 0, iqtail = 0, iqlen = 0; -static volatile int seq_playing = 0; -static volatile int sequencer_busy = 0; -static int output_threshold; -static long pre_event_timeout; -static unsigned synth_open_mask; - -static int seq_queue(unsigned char *note, char nonblock); -static void seq_startplay(void); -static int seq_sync(void); -static void seq_reset(void); - -#if MAX_SYNTH_DEV > 15 -#error Too many synthesizer devices enabled. -#endif - -int sequencer_read(int dev, struct file *file, char *buf, int count) -{ - int c = count, p = 0; - int ev_len; - unsigned long flags; - - dev = dev >> 4; - - ev_len = seq_mode == SEQ_1 ? 4 : 8; - - save_flags(flags); - cli(); - - if (!iqlen) - { - if (file->f_flags & O_NONBLOCK) { - restore_flags(flags); - return -EAGAIN; - } - - interruptible_sleep_on_timeout(&midi_sleeper, - pre_event_timeout); - if (!iqlen) - { - restore_flags(flags); - return 0; - } - } - while (iqlen && c >= ev_len) - { - char *fixit = (char *) &iqueue[iqhead * IEV_SZ]; - copy_to_user(&(buf)[p], fixit, ev_len); - p += ev_len; - c -= ev_len; - - iqhead = (iqhead + 1) % SEQ_MAX_QUEUE; - iqlen--; - } - restore_flags(flags); - return count - c; -} - -static void sequencer_midi_output(int dev) -{ - /* - * Currently NOP - */ -} - -void seq_copy_to_input(unsigned char *event_rec, int len) -{ - unsigned long flags; - - /* - * Verify that the len is valid for the current mode. - */ - - if (len != 4 && len != 8) - return; - if ((seq_mode == SEQ_1) != (len == 4)) - return; - - if (iqlen >= (SEQ_MAX_QUEUE - 1)) - return; /* Overflow */ - - save_flags(flags); - cli(); - memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len); - iqlen++; - iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; - wake_up(&midi_sleeper); - restore_flags(flags); -} - -static void sequencer_midi_input(int dev, unsigned char data) -{ - unsigned int tstamp; - unsigned char event_rec[4]; - - if (data == 0xfe) /* Ignore active sensing */ - return; - - tstamp = jiffies - seq_time; - - if (tstamp != prev_input_time) - { - tstamp = (tstamp << 8) | SEQ_WAIT; - seq_copy_to_input((unsigned char *) &tstamp, 4); - prev_input_time = tstamp; - } - event_rec[0] = SEQ_MIDIPUTC; - event_rec[1] = data; - event_rec[2] = dev; - event_rec[3] = 0; - - seq_copy_to_input(event_rec, 4); -} - -void seq_input_event(unsigned char *event_rec, int len) -{ - unsigned long this_time; - - if (seq_mode == SEQ_2) - this_time = tmr->get_time(tmr_no); - else - this_time = jiffies - seq_time; - - if (this_time != prev_input_time) - { - unsigned char tmp_event[8]; - - tmp_event[0] = EV_TIMING; - tmp_event[1] = TMR_WAIT_ABS; - tmp_event[2] = 0; - tmp_event[3] = 0; - *(unsigned int *) &tmp_event[4] = this_time; - - seq_copy_to_input(tmp_event, 8); - prev_input_time = this_time; - } - seq_copy_to_input(event_rec, len); -} - -int sequencer_write(int dev, struct file *file, const char *buf, int count) -{ - unsigned char event_rec[EV_SZ], ev_code; - int p = 0, c, ev_size; - int err; - int mode = translate_mode(file); - - dev = dev >> 4; - - DEB(printk("sequencer_write(dev=%d, count=%d)\n", dev, count)); - - if (mode == OPEN_READ) - return -EIO; - - c = count; - - while (c >= 4) - { - copy_from_user((char *) event_rec, &(buf)[p], 4); - ev_code = event_rec[0]; - - if (ev_code == SEQ_FULLSIZE) - { - int err, fmt; - - dev = *(unsigned short *) &event_rec[2]; - if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL) - return -ENXIO; - - if (!(synth_open_mask & (1 << dev))) - return -ENXIO; - - fmt = (*(short *) &event_rec[0]) & 0xffff; - err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0); - if (err < 0) - return err; - - return err; - } - if (ev_code >= 128) - { - if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) - { - printk(KERN_WARNING "Sequencer: Invalid level 2 event %x\n", ev_code); - return -EINVAL; - } - ev_size = 8; - - if (c < ev_size) - { - if (!seq_playing) - seq_startplay(); - return count - c; - } - copy_from_user((char *) &event_rec[4], &(buf)[p + 4], 4); - - } - else - { - if (seq_mode == SEQ_2) - { - printk(KERN_WARNING "Sequencer: 4 byte event in level 2 mode\n"); - return -EINVAL; - } - ev_size = 4; - - if (event_rec[0] != SEQ_MIDIPUTC) - obsolete_api_used = 1; - } - - if (event_rec[0] == SEQ_MIDIPUTC) - { - if (!midi_opened[event_rec[2]]) - { - int mode; - int dev = event_rec[2]; - - if (dev >= max_mididev || midi_devs[dev]==NULL) - { - /*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/ - return -ENXIO; - } - mode = translate_mode(file); - - if ((err = midi_devs[dev]->open(dev, mode, - sequencer_midi_input, sequencer_midi_output)) < 0) - { - seq_reset(); - printk(KERN_WARNING "Sequencer Error: Unable to open Midi #%d\n", dev); - return err; - } - midi_opened[dev] = 1; - } - } - if (!seq_queue(event_rec, (file->f_flags & (O_NONBLOCK) ? 1 : 0))) - { - int processed = count - c; - - if (!seq_playing) - seq_startplay(); - - if (!processed && (file->f_flags & O_NONBLOCK)) - return -EAGAIN; - else - return processed; - } - p += ev_size; - c -= ev_size; - } - - if (!seq_playing) - seq_startplay(); - - return count; -} - -static int seq_queue(unsigned char *note, char nonblock) -{ - - /* - * Test if there is space in the queue - */ - - if (qlen >= SEQ_MAX_QUEUE) - if (!seq_playing) - seq_startplay(); /* - * Give chance to drain the queue - */ - - if (!nonblock && qlen >= SEQ_MAX_QUEUE && !waitqueue_active(&seq_sleeper)) { - /* - * Sleep until there is enough space on the queue - */ - interruptible_sleep_on(&seq_sleeper); - } - if (qlen >= SEQ_MAX_QUEUE) - { - return 0; /* - * To be sure - */ - } - memcpy(&queue[qtail * EV_SZ], note, EV_SZ); - - qtail = (qtail + 1) % SEQ_MAX_QUEUE; - qlen++; - - return 1; -} - -static int extended_event(unsigned char *q) -{ - int dev = q[2]; - - if (dev < 0 || dev >= max_synthdev) - return -ENXIO; - - if (!(synth_open_mask & (1 << dev))) - return -ENXIO; - - switch (q[1]) - { - case SEQ_NOTEOFF: - synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]); - break; - - case SEQ_NOTEON: - if (q[4] > 127 && q[4] != 255) - return 0; - - if (q[5] == 0) - { - synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]); - break; - } - synth_devs[dev]->start_note(dev, q[3], q[4], q[5]); - break; - - case SEQ_PGMCHANGE: - synth_devs[dev]->set_instr(dev, q[3], q[4]); - break; - - case SEQ_AFTERTOUCH: - synth_devs[dev]->aftertouch(dev, q[3], q[4]); - break; - - case SEQ_BALANCE: - synth_devs[dev]->panning(dev, q[3], (char) q[4]); - break; - - case SEQ_CONTROLLER: - synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8))); - break; - - case SEQ_VOLMODE: - if (synth_devs[dev]->volume_method != NULL) - synth_devs[dev]->volume_method(dev, q[3]); - break; - - default: - return -EINVAL; - } - return 0; -} - -static int find_voice(int dev, int chn, int note) -{ - unsigned short key; - int i; - - key = (chn << 8) | (note + 1); - for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) - if (synth_devs[dev]->alloc.map[i] == key) - return i; - return -1; -} - -static int alloc_voice(int dev, int chn, int note) -{ - unsigned short key; - int voice; - - key = (chn << 8) | (note + 1); - - voice = synth_devs[dev]->alloc_voice(dev, chn, note, - &synth_devs[dev]->alloc); - synth_devs[dev]->alloc.map[voice] = key; - synth_devs[dev]->alloc.alloc_times[voice] = - synth_devs[dev]->alloc.timestamp++; - return voice; -} - -static void seq_chn_voice_event(unsigned char *event_rec) -{ -#define dev event_rec[1] -#define cmd event_rec[2] -#define chn event_rec[3] -#define note event_rec[4] -#define parm event_rec[5] - - int voice = -1; - - if ((int) dev > max_synthdev || synth_devs[dev] == NULL) - return; - if (!(synth_open_mask & (1 << dev))) - return; - if (!synth_devs[dev]) - return; - - if (seq_mode == SEQ_2) - { - if (synth_devs[dev]->alloc_voice) - voice = find_voice(dev, chn, note); - - if (cmd == MIDI_NOTEON && parm == 0) - { - cmd = MIDI_NOTEOFF; - parm = 64; - } - } - - switch (cmd) - { - case MIDI_NOTEON: - if (note > 127 && note != 255) /* Not a seq2 feature */ - return; - - if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice) - { - /* Internal synthesizer (FM, GUS, etc) */ - voice = alloc_voice(dev, chn, note); - } - if (voice == -1) - voice = chn; - - if (seq_mode == SEQ_2 && (int) dev < num_synths) - { - /* - * The MIDI channel 10 is a percussive channel. Use the note - * number to select the proper patch (128 to 255) to play. - */ - - if (chn == 9) - { - synth_devs[dev]->set_instr(dev, voice, 128 + note); - synth_devs[dev]->chn_info[chn].pgm_num = 128 + note; - } - synth_devs[dev]->setup_voice(dev, voice, chn); - } - synth_devs[dev]->start_note(dev, voice, note, parm); - break; - - case MIDI_NOTEOFF: - if (voice == -1) - voice = chn; - synth_devs[dev]->kill_note(dev, voice, note, parm); - break; - - case MIDI_KEY_PRESSURE: - if (voice == -1) - voice = chn; - synth_devs[dev]->aftertouch(dev, voice, parm); - break; - - default:; - } -#undef dev -#undef cmd -#undef chn -#undef note -#undef parm -} - - -static void seq_chn_common_event(unsigned char *event_rec) -{ - unsigned char dev = event_rec[1]; - unsigned char cmd = event_rec[2]; - unsigned char chn = event_rec[3]; - unsigned char p1 = event_rec[4]; - - /* unsigned char p2 = event_rec[5]; */ - unsigned short w14 = *(short *) &event_rec[6]; - - if ((int) dev > max_synthdev || synth_devs[dev] == NULL) - return; - if (!(synth_open_mask & (1 << dev))) - return; - if (!synth_devs[dev]) - return; - - switch (cmd) - { - case MIDI_PGM_CHANGE: - if (seq_mode == SEQ_2) - { - synth_devs[dev]->chn_info[chn].pgm_num = p1; - if ((int) dev >= num_synths) - synth_devs[dev]->set_instr(dev, chn, p1); - } - else - synth_devs[dev]->set_instr(dev, chn, p1); - - break; - - case MIDI_CTL_CHANGE: - if (seq_mode == SEQ_2) - { - if (chn > 15 || p1 > 127) - break; - - synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f; - - if (p1 < 32) /* Setting MSB should clear LSB to 0 */ - synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0; - - if ((int) dev < num_synths) - { - int val = w14 & 0x7f; - int i, key; - - if (p1 < 64) /* Combine MSB and LSB */ - { - val = ((synth_devs[dev]-> - chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7) - | (synth_devs[dev]-> - chn_info[chn].controllers[p1 | 32] & 0x7f); - p1 &= ~32; - } - /* Handle all playing notes on this channel */ - - key = ((int) chn << 8); - - for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) - if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) - synth_devs[dev]->controller(dev, i, p1, val); - } - else - synth_devs[dev]->controller(dev, chn, p1, w14); - } - else /* Mode 1 */ - synth_devs[dev]->controller(dev, chn, p1, w14); - break; - - case MIDI_PITCH_BEND: - if (seq_mode == SEQ_2) - { - synth_devs[dev]->chn_info[chn].bender_value = w14; - - if ((int) dev < num_synths) - { - /* Handle all playing notes on this channel */ - int i, key; - - key = (chn << 8); - - for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) - if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) - synth_devs[dev]->bender(dev, i, w14); - } - else - synth_devs[dev]->bender(dev, chn, w14); - } - else /* MODE 1 */ - synth_devs[dev]->bender(dev, chn, w14); - break; - - default:; - } -} - -static int seq_timing_event(unsigned char *event_rec) -{ - unsigned char cmd = event_rec[1]; - unsigned int parm = *(int *) &event_rec[4]; - - if (seq_mode == SEQ_2) - { - int ret; - - if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED) - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - wake_up(&seq_sleeper); - return ret; - } - switch (cmd) - { - case TMR_WAIT_REL: - parm += prev_event_time; - - /* - * NOTE! No break here. Execution of TMR_WAIT_REL continues in the - * next case (TMR_WAIT_ABS) - */ - - case TMR_WAIT_ABS: - if (parm > 0) - { - long time; - - time = parm; - prev_event_time = time; - - seq_playing = 1; - request_sound_timer(time); - - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - wake_up(&seq_sleeper); - return TIMER_ARMED; - } - break; - - case TMR_START: - seq_time = jiffies; - prev_input_time = 0; - prev_event_time = 0; - break; - - case TMR_STOP: - break; - - case TMR_CONTINUE: - break; - - case TMR_TEMPO: - break; - - case TMR_ECHO: - if (seq_mode == SEQ_2) - seq_copy_to_input(event_rec, 8); - else - { - parm = (parm << 8 | SEQ_ECHO); - seq_copy_to_input((unsigned char *) &parm, 4); - } - break; - - default:; - } - - return TIMER_NOT_ARMED; -} - -static void seq_local_event(unsigned char *event_rec) -{ - unsigned char cmd = event_rec[1]; - unsigned int parm = *((unsigned int *) &event_rec[4]); - - switch (cmd) - { - case LOCL_STARTAUDIO: - DMAbuf_start_devices(parm); - break; - - default:; - } -} - -static void seq_sysex_message(unsigned char *event_rec) -{ - int dev = event_rec[1]; - int i, l = 0; - unsigned char *buf = &event_rec[2]; - - if ((int) dev > max_synthdev) - return; - if (!(synth_open_mask & (1 << dev))) - return; - if (!synth_devs[dev]) - return; - - l = 0; - for (i = 0; i < 6 && buf[i] != 0xff; i++) - l = i + 1; - - if (!synth_devs[dev]->send_sysex) - return; - if (l > 0) - synth_devs[dev]->send_sysex(dev, buf, l); -} - -static int play_event(unsigned char *q) -{ - /* - * NOTE! This routine returns - * 0 = normal event played. - * 1 = Timer armed. Suspend playback until timer callback. - * 2 = MIDI output buffer full. Restore queue and suspend until timer - */ - unsigned int *delay; - - switch (q[0]) - { - case SEQ_NOTEOFF: - if (synth_open_mask & (1 << 0)) - if (synth_devs[0]) - synth_devs[0]->kill_note(0, q[1], 255, q[3]); - break; - - case SEQ_NOTEON: - if (q[4] < 128 || q[4] == 255) - if (synth_open_mask & (1 << 0)) - if (synth_devs[0]) - synth_devs[0]->start_note(0, q[1], q[2], q[3]); - break; - - case SEQ_WAIT: - delay = (unsigned int *) q; /* - * Bytes 1 to 3 are containing the * - * delay in 'ticks' - */ - *delay = (*delay >> 8) & 0xffffff; - - if (*delay > 0) - { - long time; - - seq_playing = 1; - time = *delay; - prev_event_time = time; - - request_sound_timer(time); - - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - wake_up(&seq_sleeper); - /* - * The timer is now active and will reinvoke this function - * after the timer expires. Return to the caller now. - */ - return 1; - } - break; - - case SEQ_PGMCHANGE: - if (synth_open_mask & (1 << 0)) - if (synth_devs[0]) - synth_devs[0]->set_instr(0, q[1], q[2]); - break; - - case SEQ_SYNCTIMER: /* - * Reset timer - */ - seq_time = jiffies; - prev_input_time = 0; - prev_event_time = 0; - break; - - case SEQ_MIDIPUTC: /* - * Put a midi character - */ - if (midi_opened[q[2]]) - { - int dev; - - dev = q[2]; - - if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) - break; - - if (!midi_devs[dev]->outputc(dev, q[1])) - { - /* - * Output FIFO is full. Wait one timer cycle and try again. - */ - - seq_playing = 1; - request_sound_timer(-1); - return 2; - } - else - midi_written[dev] = 1; - } - break; - - case SEQ_ECHO: - seq_copy_to_input(q, 4); /* - * Echo back to the process - */ - break; - - case SEQ_PRIVATE: - if ((int) q[1] < max_synthdev) - synth_devs[q[1]]->hw_control(q[1], q); - break; - - case SEQ_EXTENDED: - extended_event(q); - break; - - case EV_CHN_VOICE: - seq_chn_voice_event(q); - break; - - case EV_CHN_COMMON: - seq_chn_common_event(q); - break; - - case EV_TIMING: - if (seq_timing_event(q) == TIMER_ARMED) - { - return 1; - } - break; - - case EV_SEQ_LOCAL: - seq_local_event(q); - break; - - case EV_SYSEX: - seq_sysex_message(q); - break; - - default:; - } - return 0; -} - -static void seq_startplay(void) -{ - unsigned long flags; - int this_one, action; - - while (qlen > 0) - { - - save_flags(flags); - cli(); - qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; - qlen--; - restore_flags(flags); - - seq_playing = 1; - - if ((action = play_event(&queue[this_one * EV_SZ]))) - { /* Suspend playback. Next timer routine invokes this routine again */ - if (action == 2) - { - qlen++; - qhead = this_one; - } - return; - } - } - - seq_playing = 0; - - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - wake_up(&seq_sleeper); -} - -static void reset_controllers(int dev, unsigned char *controller, int update_dev) -{ - int i; - for (i = 0; i < 128; i++) - controller[i] = ctrl_def_values[i]; -} - -static void setup_mode2(void) -{ - int dev; - - max_synthdev = num_synths; - - for (dev = 0; dev < num_midis; dev++) - { - if (midi_devs[dev] && midi_devs[dev]->converter != NULL) - { - synth_devs[max_synthdev++] = midi_devs[dev]->converter; - } - } - - for (dev = 0; dev < max_synthdev; dev++) - { - int chn; - - synth_devs[dev]->sysex_ptr = 0; - synth_devs[dev]->emulation = 0; - - for (chn = 0; chn < 16; chn++) - { - synth_devs[dev]->chn_info[chn].pgm_num = 0; - reset_controllers(dev, - synth_devs[dev]->chn_info[chn].controllers,0); - synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */ - synth_devs[dev]->chn_info[chn].bender_range = 200; - } - } - max_mididev = 0; - seq_mode = SEQ_2; -} - -int sequencer_open(int dev, struct file *file) -{ - int retval, mode, i; - int level, tmp; - unsigned long flags; - - if (!sequencer_ok) - sequencer_init(); - - level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1; - - dev = dev >> 4; - mode = translate_mode(file); - - DEB(printk("sequencer_open(dev=%d)\n", dev)); - - if (!sequencer_ok) - { -/* printk("Sound card: sequencer not initialized\n");*/ - return -ENXIO; - } - if (dev) /* Patch manager device (obsolete) */ - return -ENXIO; - - if(synth_devs[dev] == NULL) - request_module("synth0"); - - if (mode == OPEN_READ) - { - if (!num_midis) - { - /*printk("Sequencer: No MIDI devices. Input not possible\n");*/ - sequencer_busy = 0; - return -ENXIO; - } - } - save_flags(flags); - cli(); - if (sequencer_busy) - { - restore_flags(flags); - return -EBUSY; - } - sequencer_busy = 1; - obsolete_api_used = 0; - restore_flags(flags); - - max_mididev = num_midis; - max_synthdev = num_synths; - pre_event_timeout = MAX_SCHEDULE_TIMEOUT; - seq_mode = SEQ_1; - - if (pending_timer != -1) - { - tmr_no = pending_timer; - pending_timer = -1; - } - if (tmr_no == -1) /* Not selected yet */ - { - int i, best; - - best = -1; - for (i = 0; i < num_sound_timers; i++) - if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best) - { - tmr_no = i; - best = sound_timer_devs[i]->priority; - } - if (tmr_no == -1) /* Should not be */ - tmr_no = 0; - } - tmr = sound_timer_devs[tmr_no]; - - if (level == 2) - { - if (tmr == NULL) - { - /*printk("sequencer: No timer for level 2\n");*/ - sequencer_busy = 0; - return -ENXIO; - } - setup_mode2(); - } - if (!max_synthdev && !max_mididev) - { - sequencer_busy=0; - return -ENXIO; - } - - synth_open_mask = 0; - - for (i = 0; i < max_mididev; i++) - { - midi_opened[i] = 0; - midi_written[i] = 0; - } - - for (i = 0; i < max_synthdev; i++) - { - if (synth_devs[i]==NULL) - continue; - - if (synth_devs[i]->owner) - __MOD_INC_USE_COUNT (synth_devs[i]->owner); - - if ((tmp = synth_devs[i]->open(i, mode)) < 0) - { - printk(KERN_WARNING "Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp); - if (synth_devs[i]->midi_dev) - printk(KERN_WARNING "(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev); - } - else - { - synth_open_mask |= (1 << i); - if (synth_devs[i]->midi_dev) - midi_opened[synth_devs[i]->midi_dev] = 1; - } - } - - seq_time = jiffies; - - prev_input_time = 0; - prev_event_time = 0; - - if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) - { - /* - * Initialize midi input devices - */ - - for (i = 0; i < max_mididev; i++) - if (!midi_opened[i] && midi_devs[i]) - { - if (midi_devs[i]->owner) - __MOD_INC_USE_COUNT (midi_devs[i]->owner); - - if ((retval = midi_devs[i]->open(i, mode, - sequencer_midi_input, sequencer_midi_output)) >= 0) - { - midi_opened[i] = 1; - } - } - } - - if (seq_mode == SEQ_2) { - if (tmr->owner) - __MOD_INC_USE_COUNT (tmr->owner); - tmr->open(tmr_no, seq_mode); - } - - init_waitqueue_head(&seq_sleeper); - init_waitqueue_head(&midi_sleeper); - output_threshold = SEQ_MAX_QUEUE / 2; - - return 0; -} - -void seq_drain_midi_queues(void) -{ - int i, n; - - /* - * Give the Midi drivers time to drain their output queues - */ - - n = 1; - - while (!signal_pending(current) && n) - { - n = 0; - - for (i = 0; i < max_mididev; i++) - if (midi_opened[i] && midi_written[i]) - if (midi_devs[i]->buffer_status != NULL) - if (midi_devs[i]->buffer_status(i)) - n++; - - /* - * Let's have a delay - */ - - if (n) - interruptible_sleep_on_timeout(&seq_sleeper, - HZ/10); - } -} - -void sequencer_release(int dev, struct file *file) -{ - int i; - int mode = translate_mode(file); - - dev = dev >> 4; - - DEB(printk("sequencer_release(dev=%d)\n", dev)); - - /* - * Wait until the queue is empty (if we don't have nonblock) - */ - - if (mode != OPEN_READ && !(file->f_flags & O_NONBLOCK)) - { - while (!signal_pending(current) && qlen > 0) - { - seq_sync(); - interruptible_sleep_on_timeout(&seq_sleeper, - 3*HZ); - /* Extra delay */ - } - } - - if (mode != OPEN_READ) - seq_drain_midi_queues(); /* - * Ensure the output queues are empty - */ - seq_reset(); - if (mode != OPEN_READ) - seq_drain_midi_queues(); /* - * Flush the all notes off messages - */ - - for (i = 0; i < max_synthdev; i++) - { - if (synth_open_mask & (1 << i)) /* - * Actually opened - */ - if (synth_devs[i]) - { - synth_devs[i]->close(i); - - if (synth_devs[i]->owner) - __MOD_DEC_USE_COUNT (synth_devs[i]->owner); - - if (synth_devs[i]->midi_dev) - midi_opened[synth_devs[i]->midi_dev] = 0; - } - } - - for (i = 0; i < max_mididev; i++) - { - if (midi_opened[i]) { - midi_devs[i]->close(i); - if (midi_devs[i]->owner) - __MOD_DEC_USE_COUNT (midi_devs[i]->owner); - } - } - - if (seq_mode == SEQ_2) { - tmr->close(tmr_no); - if (tmr->owner) - __MOD_DEC_USE_COUNT (tmr->owner); - } - - if (obsolete_api_used) - printk(KERN_WARNING "/dev/music: Obsolete (4 byte) API was used by %s\n", current->comm); - sequencer_busy = 0; -} - -static int seq_sync(void) -{ - unsigned long flags; - - if (qlen && !seq_playing && !signal_pending(current)) - seq_startplay(); - - save_flags(flags); - cli(); - if (qlen > 0) - interruptible_sleep_on_timeout(&seq_sleeper, HZ); - restore_flags(flags); - return qlen; -} - -static void midi_outc(int dev, unsigned char data) -{ - /* - * NOTE! Calls sleep(). Don't call this from interrupt. - */ - - int n; - unsigned long flags; - - /* - * This routine sends one byte to the Midi channel. - * If the output FIFO is full, it waits until there - * is space in the queue - */ - - n = 3 * HZ; /* Timeout */ - - save_flags(flags); - cli(); - while (n && !midi_devs[dev]->outputc(dev, data)) { - interruptible_sleep_on_timeout(&seq_sleeper, HZ/25); - n--; - } - restore_flags(flags); -} - -static void seq_reset(void) -{ - /* - * NOTE! Calls sleep(). Don't call this from interrupt. - */ - - int i; - int chn; - unsigned long flags; - - sound_stop_timer(); - - seq_time = jiffies; - prev_input_time = 0; - prev_event_time = 0; - - qlen = qhead = qtail = 0; - iqlen = iqhead = iqtail = 0; - - for (i = 0; i < max_synthdev; i++) - if (synth_open_mask & (1 << i)) - if (synth_devs[i]) - synth_devs[i]->reset(i); - - if (seq_mode == SEQ_2) - { - for (chn = 0; chn < 16; chn++) - for (i = 0; i < max_synthdev; i++) - if (synth_open_mask & (1 << i)) - if (synth_devs[i]) - { - synth_devs[i]->controller(i, chn, 123, 0); /* All notes off */ - synth_devs[i]->controller(i, chn, 121, 0); /* Reset all ctl */ - synth_devs[i]->bender(i, chn, 1 << 13); /* Bender off */ - } - } - else /* seq_mode == SEQ_1 */ - { - for (i = 0; i < max_mididev; i++) - if (midi_written[i]) /* - * Midi used. Some notes may still be playing - */ - { - /* - * Sending just a ACTIVE SENSING message should be enough to stop all - * playing notes. Since there are devices not recognizing the - * active sensing, we have to send some all notes off messages also. - */ - midi_outc(i, 0xfe); - - for (chn = 0; chn < 16; chn++) - { - midi_outc(i, (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */ - midi_outc(i, 0x7b); /* All notes off */ - midi_outc(i, 0); /* Dummy parameter */ - } - - midi_devs[i]->close(i); - - midi_written[i] = 0; - midi_opened[i] = 0; - } - } - - seq_playing = 0; - - save_flags(flags); - cli(); - - if (waitqueue_active(&seq_sleeper)) { - /* printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */ - wake_up(&seq_sleeper); - } - restore_flags(flags); -} - -static void seq_panic(void) -{ - /* - * This routine is called by the application in case the user - * wants to reset the system to the default state. - */ - - seq_reset(); - - /* - * Since some of the devices don't recognize the active sensing and - * all notes off messages, we have to shut all notes manually. - * - * TO BE IMPLEMENTED LATER - */ - - /* - * Also return the controllers to their default states - */ -} - -int sequencer_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg) -{ - int midi_dev, orig_dev, val, err; - int mode = translate_mode(file); - struct synth_info inf; - struct seq_event_rec event_rec; - unsigned long flags; - - orig_dev = dev = dev >> 4; - - switch (cmd) - { - case SNDCTL_TMR_TIMEBASE: - case SNDCTL_TMR_TEMPO: - case SNDCTL_TMR_START: - case SNDCTL_TMR_STOP: - case SNDCTL_TMR_CONTINUE: - case SNDCTL_TMR_METRONOME: - case SNDCTL_TMR_SOURCE: - if (seq_mode != SEQ_2) - return -EINVAL; - return tmr->ioctl(tmr_no, cmd, arg); - - case SNDCTL_TMR_SELECT: - if (seq_mode != SEQ_2) - return -EINVAL; - if (get_user(pending_timer, (int *)arg)) - return -EFAULT; - if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL) - { - pending_timer = -1; - return -EINVAL; - } - val = pending_timer; - break; - - case SNDCTL_SEQ_PANIC: - seq_panic(); - return -EINVAL; - - case SNDCTL_SEQ_SYNC: - if (mode == OPEN_READ) - return 0; - while (qlen > 0 && !signal_pending(current)) - seq_sync(); - return qlen ? -EINTR : 0; - - case SNDCTL_SEQ_RESET: - seq_reset(); - return 0; - - case SNDCTL_SEQ_TESTMIDI: - if (__get_user(midi_dev, (int *)arg)) - return -EFAULT; - if (midi_dev < 0 || midi_dev >= max_mididev || !midi_devs[midi_dev]) - return -ENXIO; - - if (!midi_opened[midi_dev] && - (err = midi_devs[midi_dev]->open(midi_dev, mode, sequencer_midi_input, - sequencer_midi_output)) < 0) - return err; - midi_opened[midi_dev] = 1; - return 0; - - case SNDCTL_SEQ_GETINCOUNT: - if (mode == OPEN_WRITE) - return 0; - val = iqlen; - break; - - case SNDCTL_SEQ_GETOUTCOUNT: - if (mode == OPEN_READ) - return 0; - val = SEQ_MAX_QUEUE - qlen; - break; - - case SNDCTL_SEQ_GETTIME: - if (seq_mode == SEQ_2) - return tmr->ioctl(tmr_no, cmd, arg); - val = jiffies - seq_time; - break; - - case SNDCTL_SEQ_CTRLRATE: - /* - * If *arg == 0, just return the current rate - */ - if (seq_mode == SEQ_2) - return tmr->ioctl(tmr_no, cmd, arg); - - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) - return -EINVAL; - val = HZ; - break; - - case SNDCTL_SEQ_RESETSAMPLES: - case SNDCTL_SYNTH_REMOVESAMPLE: - case SNDCTL_SYNTH_CONTROL: - if (get_user(dev, (int *)arg)) - return -EFAULT; - if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) - return -ENXIO; - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - return -EBUSY; - return synth_devs[dev]->ioctl(dev, cmd, arg); - - case SNDCTL_SEQ_NRSYNTHS: - val = max_synthdev; - break; - - case SNDCTL_SEQ_NRMIDIS: - val = max_mididev; - break; - - case SNDCTL_SYNTH_MEMAVL: - if (get_user(dev, (int *)arg)) - return -EFAULT; - if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) - return -ENXIO; - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - return -EBUSY; - val = synth_devs[dev]->ioctl(dev, cmd, arg); - break; - - case SNDCTL_FM_4OP_ENABLE: - if (get_user(dev, (int *)arg)) - return -EFAULT; - if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) - return -ENXIO; - if (!(synth_open_mask & (1 << dev))) - return -ENXIO; - synth_devs[dev]->ioctl(dev, cmd, arg); - return 0; - - case SNDCTL_SYNTH_INFO: - if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device)))) - return -EFAULT; - if (dev < 0 || dev >= max_synthdev) - return -ENXIO; - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - return -EBUSY; - return synth_devs[dev]->ioctl(dev, cmd, arg); - - /* Like SYNTH_INFO but returns ID in the name field */ - case SNDCTL_SYNTH_ID: - if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device)))) - return -EFAULT; - if (dev < 0 || dev >= max_synthdev) - return -ENXIO; - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - return -EBUSY; - memcpy(&inf, synth_devs[dev]->info, sizeof(inf)); - strncpy(inf.name, synth_devs[dev]->id, sizeof(inf.name)); - inf.device = dev; - return copy_to_user(arg, &inf, sizeof(inf))?-EFAULT:0; - - case SNDCTL_SEQ_OUTOFBAND: - if (copy_from_user(&event_rec, arg, sizeof(event_rec))) - return -EFAULT; - save_flags(flags); - cli(); - play_event(event_rec.arr); - restore_flags(flags); - return 0; - - case SNDCTL_MIDI_INFO: - if (get_user(dev, (int *)(&(((struct midi_info *)arg)->device)))) - return -EFAULT; - if (dev < 0 || dev >= max_mididev || !midi_devs[dev]) - return -ENXIO; - midi_devs[dev]->info.device = dev; - return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct midi_info))?-EFAULT:0; - - case SNDCTL_SEQ_THRESHOLD: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val < 1) - val = 1; - if (val >= SEQ_MAX_QUEUE) - val = SEQ_MAX_QUEUE - 1; - output_threshold = val; - return 0; - - case SNDCTL_MIDI_PRETIME: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val < 0) - val = 0; - val = (HZ * val) / 10; - pre_event_timeout = val; - break; - - default: - if (mode == OPEN_READ) - return -EIO; - if (!synth_devs[0]) - return -ENXIO; - if (!(synth_open_mask & (1 << 0))) - return -ENXIO; - if (!synth_devs[0]->ioctl) - return -EINVAL; - return synth_devs[0]->ioctl(0, cmd, arg); - } - return put_user(val, (int *)arg); -} - -/* No kernel lock - we're using the global irq lock here */ -unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait) -{ - unsigned long flags; - unsigned int mask = 0; - - dev = dev >> 4; - - save_flags(flags); - cli(); - /* input */ - poll_wait(file, &midi_sleeper, wait); - if (iqlen) - mask |= POLLIN | POLLRDNORM; - - /* output */ - poll_wait(file, &seq_sleeper, wait); - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - mask |= POLLOUT | POLLWRNORM; - restore_flags(flags); - return mask; -} - - -void sequencer_timer(unsigned long dummy) -{ - seq_startplay(); -} - -int note_to_freq(int note_num) -{ - - /* - * This routine converts a midi note to a frequency (multiplied by 1000) - */ - - int note, octave, note_freq; - static int notes[] = - { - 261632, 277189, 293671, 311132, 329632, 349232, - 369998, 391998, 415306, 440000, 466162, 493880 - }; - -#define BASE_OCTAVE 5 - - octave = note_num / 12; - note = note_num % 12; - - note_freq = notes[note]; - - if (octave < BASE_OCTAVE) - note_freq >>= (BASE_OCTAVE - octave); - else if (octave > BASE_OCTAVE) - note_freq <<= (octave - BASE_OCTAVE); - - /* - * note_freq >>= 1; - */ - - return note_freq; -} - -unsigned long compute_finetune(unsigned long base_freq, int bend, int range, - int vibrato_cents) -{ - unsigned long amount; - int negative, semitones, cents, multiplier = 1; - - if (!bend) - return base_freq; - if (!range) - return base_freq; - - if (!base_freq) - return base_freq; - - if (range >= 8192) - range = 8192; - - bend = bend * range / 8192; /* Convert to cents */ - bend += vibrato_cents; - - if (!bend) - return base_freq; - - negative = bend < 0 ? 1 : 0; - - if (bend < 0) - bend *= -1; - if (bend > range) - bend = range; - - /* - if (bend > 2399) - bend = 2399; - */ - while (bend > 2399) - { - multiplier *= 4; - bend -= 2400; - } - - semitones = bend / 100; - if (semitones > 99) - semitones = 99; - cents = bend % 100; - - amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000; - - if (negative) - return (base_freq * 10000) / amount; /* Bend down */ - else - return (base_freq * amount) / 10000; /* Bend up */ -} - - -void sequencer_init(void) -{ - /* drag in sequencer_syms.o */ - { - extern char sequencer_syms_symbol; - sequencer_syms_symbol = 0; - } - - if (sequencer_ok) - return; - MIDIbuf_init(); - queue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * EV_SZ); - if (queue == NULL) - { - printk(KERN_ERR "sequencer: Can't allocate memory for sequencer output queue\n"); - return; - } - iqueue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * IEV_SZ); - if (iqueue == NULL) - { - printk(KERN_ERR "sequencer: Can't allocate memory for sequencer input queue\n"); - vfree(queue); - return; - } - sequencer_ok = 1; -} - -void sequencer_unload(void) -{ - if(queue) - { - vfree(queue); - queue=NULL; - } - if(iqueue) - { - vfree(iqueue); - iqueue=NULL; - } -} diff -Nru a/drivers/sound/sequencer_syms.c b/drivers/sound/sequencer_syms.c --- a/drivers/sound/sequencer_syms.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,32 +0,0 @@ -/* - * Exported symbols for sequencer driver. - * __NO_VERSION__ because this is still part of sound.o. - */ - -#define __NO_VERSION__ -#include - -char sequencer_syms_symbol; - -#include "sound_config.h" -#include "sound_calls.h" - -EXPORT_SYMBOL(note_to_freq); -EXPORT_SYMBOL(compute_finetune); -EXPORT_SYMBOL(seq_copy_to_input); -EXPORT_SYMBOL(seq_input_event); -EXPORT_SYMBOL(sequencer_init); -EXPORT_SYMBOL(sequencer_timer); - -EXPORT_SYMBOL(sound_timer_init); -EXPORT_SYMBOL(sound_timer_interrupt); -EXPORT_SYMBOL(sound_timer_syncinterval); -EXPORT_SYMBOL(reprogram_timer); - -/* Tuning */ - -#define _SEQUENCER_C_ -#include "tuning.h" - -EXPORT_SYMBOL(cent_tuning); -EXPORT_SYMBOL(semitone_tuning); diff -Nru a/drivers/sound/sgalaxy.c b/drivers/sound/sgalaxy.c --- a/drivers/sound/sgalaxy.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,197 +0,0 @@ -/* - * sound/sgalaxy.c - * - * Low level driver for Aztech Sound Galaxy cards. - * Copyright 1998 Artur Skawina - * - * Supported cards: - * Aztech Sound Galaxy Waverider Pro 32 - 3D - * Aztech Sound Galaxy Washington 16 - * - * Based on cs4232.c by Hannu Savolainen and Alan Cox. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz - * Added __init to sb_rst() and sb_cmd() - */ - -#include -#include - -#include "sound_config.h" -#include "ad1848.h" - -static void sleep( unsigned howlong ) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(howlong); -} - -#define DPORT 0x80 - -/* Sound Blaster regs */ - -#define SBDSP_RESET 0x6 -#define SBDSP_READ 0xA -#define SBDSP_COMMAND 0xC -#define SBDSP_STATUS SBDSP_COMMAND -#define SBDSP_DATA_AVAIL 0xE - -static int __init sb_rst(int base) -{ - int i; - - outb( 1, base+SBDSP_RESET ); /* reset the DSP */ - outb( 0, base+SBDSP_RESET ); - - for ( i=0; i<500; i++ ) /* delay */ - inb(DPORT); - - for ( i=0; i<100000; i++ ) - { - if ( inb( base+SBDSP_DATA_AVAIL )&0x80 ) - break; - } - - if ( inb( base+SBDSP_READ )!=0xAA ) - return 0; - - return 1; -} - -static int __init sb_cmd( int base, unsigned char val ) -{ - int i; - - for ( i=100000; i; i-- ) - { - if ( (inb( base+SBDSP_STATUS )&0x80)==0 ) - { - outb( val, base+SBDSP_COMMAND ); - break; - } - } - return i; /* i>0 == success */ -} - - -#define ai_sgbase driver_use_1 - -static int __init probe_sgalaxy( struct address_info *ai ) -{ - if ( check_region( ai->io_base, 8 ) ) { - printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); - return 0; - } - - if ( ad1848_detect( ai->io_base+4, NULL, ai->osp ) ) - return probe_ms_sound(ai); /* The card is already active, check irq etc... */ - - if ( check_region( ai->ai_sgbase, 0x10 ) ) { - printk(KERN_ERR "sgalaxy: SB IO port 0x%03x not available\n", ai->ai_sgbase); - return 0; - } - - /* switch to MSS/WSS mode */ - - sb_rst( ai->ai_sgbase ); - - sb_cmd( ai->ai_sgbase, 9 ); - sb_cmd( ai->ai_sgbase, 0 ); - - sleep( HZ/10 ); - - return probe_ms_sound(ai); -} - -static void __init attach_sgalaxy( struct address_info *ai ) -{ - int n; - - request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB" ); - - attach_ms_sound(ai, THIS_MODULE); - n=ai->slots[0]; - - if (n!=-1 && audio_devs[n]->mixer_dev != -1 ) { - AD1848_REROUTE( SOUND_MIXER_LINE1, SOUND_MIXER_LINE ); /* Line-in */ - AD1848_REROUTE( SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH ); /* FM+Wavetable*/ - AD1848_REROUTE( SOUND_MIXER_LINE3, SOUND_MIXER_CD ); /* CD */ - } -} - -static void __exit unload_sgalaxy( struct address_info *ai ) -{ - unload_ms_sound( ai ); - release_region( ai->ai_sgbase, 0x10 ); -} - -static struct address_info cfg; - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; -static int __initdata sgbase = -1; - -MODULE_PARM(io,"i"); -MODULE_PARM(irq,"i"); -MODULE_PARM(dma,"i"); -MODULE_PARM(dma2,"i"); -MODULE_PARM(sgbase,"i"); - -static int __init init_sgalaxy(void) -{ - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - cfg.ai_sgbase = sgbase; - - if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.ai_sgbase == -1 ) { - printk(KERN_ERR "sgalaxy: io, irq, dma and sgbase must be set.\n"); - return -EINVAL; - } - - if ( probe_sgalaxy(&cfg) == 0 ) - return -ENODEV; - - attach_sgalaxy(&cfg); - - return 0; -} - -static void __exit cleanup_sgalaxy(void) -{ - unload_sgalaxy(&cfg); -} - -module_init(init_sgalaxy); -module_exit(cleanup_sgalaxy); - -#ifndef MODULE -static int __init setup_sgalaxy(char *str) -{ - /* io, irq, dma, dma2, sgbase */ - int ints[6]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - sgbase = ints[5]; - - return 1; -} - -__setup("sgalaxy=", setup_sgalaxy); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/skeleton.c b/drivers/sound/skeleton.c --- a/drivers/sound/skeleton.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,223 +0,0 @@ -/* - * PCI sound skeleton example - * - * (c) 1998 Red Hat Software - * - * This software may be used and distributed according to the - * terms of the GNU General Public License, incorporated herein by - * reference. - * - * This example is designed to be built in the linux/drivers/sound - * directory as part of a kernel build. The example is modular only - * drop me a note once you have a working modular driver and want - * to integrate it with the main code. - * -- Alan - * - * This is a first draft. Please report any errors, corrections or - * improvements to me. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include "sound_config.h" - -/* - * Define our PCI vendor ID here - */ - -#ifndef PCI_VENDOR_MYIDENT -#define PCI_VENDOR_MYIDENT 0x125D - -/* - * PCI identity for the card. - */ - -#define PCI_DEVICE_ID_MYIDENT_MYCARD1 0x1969 -#endif - -#define CARD_NAME "ExampleWave 3D Pro Ultra ThingyWotsit" - -#define MAX_CARDS 8 - -/* - * Each address_info object holds the information about one of - * our card resources. In this case the MSS emulation of our - * ficticious card. Its used to manage and attach things. - */ - -static struct address_info mss_data[MAX_CARDS]; -static int cards = 0; - -/* - * Install the actual card. This is an example - */ - -static int mycard_install(struct pci_dev *pcidev) -{ - int iobase; - int mssbase; - int mpubase; - u8 x; - u16 w; - u32 v; - int i; - int dma; - - /* - * Our imaginary code has its I/O on PCI address 0, a - * MSS on PCI address 1 and an MPU on address 2 - * - * For the example we will only initialise the MSS - */ - - iobase = pci_resource_start(pcidev, 0); - mssbase = pci_resource_start(pcidev, 1); - mpubase = pci_resource_start(pcidev, 2); - - /* - * Reset the board - */ - - /* - * Wait for completion. udelay() waits in microseconds - */ - - udelay(100); - - /* - * Ok card ready. Begin setup proper. You might for example - * load the firmware here - */ - - dma = card_specific_magic(ioaddr); - - /* - * Turn on legacy mode (example), There are also byte and - * dword (32bit) PCI configuration function calls - */ - - pci_read_config_word(pcidev, 0x40, &w); - w&=~(1<<15); /* legacy decode on */ - w|=(1<<14); /* Reserved write as 1 in this case */ - w|=(1<<3)|(1<<1)|(1<<0); /* SB on , FM on, MPU on */ - pci_write_config_word(pcidev, 0x40, w); - - /* - * Let the user know we found his toy. - */ - - printk(KERN_INFO "Programmed "CARD_NAME" at 0x%X to legacy mode.\n", - iobase); - - /* - * Now set it up the description of the card - */ - - mss_data[cards].io_base = mssbase; - mss_data[cards].irq = pcidev->irq; - mss_data[cards].dma = dma; - - /* - * Check there is an MSS present - */ - - if(ad1848_detect(mssbase, NULL, mss_data[cards].osp)==0) - return 0; - - /* - * Initialize it - */ - - mss_data[cards].slots[3] = ad1848_init("MyCard MSS 16bit", - mssbase, - mss_data[cards].irq, - mss_data[cards].dma, - mss_data[cards].dma, - 0, - 0, - THIS_MODULE); - - cards++; - return 1; -} - - -/* - * This loop walks the PCI configuration database and finds where - * the sound cards are. - */ - -int init_mycard(void) -{ - struct pci_dev *pcidev=NULL; - int count=0; - - if(!pci_present()) - return -ENODEV; - - - while((pcidev = pci_find_device(PCI_VENDOR_MYIDENT, PCI_DEVICE_ID_MYIDENT_MYCARD1, pcidev))!=NULL) - { - if (pci_enable_device(pcidev)) - continue; - count+=mycard_install(pcidev); - if(count) - return 0; - if(count==MAX_CARDS) - break; - } - - if(count==0) - return -ENODEV; - return 0; -} - -/* - * This function is called when the user or kernel loads the - * module into memory. - */ - - -int init_module(void) -{ - if(init_mycard()<0) - { - printk(KERN_ERR "No "CARD_NAME" cards found.\n"); - return -ENODEV; - } - - return 0; -} - -/* - * This is called when it is removed. It will only be removed - * when its use count is 0. - */ - -void cleanup_module(void) -{ - for(i=0;i< cards; i++) - { - /* - * Free attached resources - */ - - ad1848_unload(mss_data[i].io_base, - mss_data[i].irq, - mss_data[i].dma, - mss_data[i].dma, - 0); - /* - * And disconnect the device from the kernel - */ - sound_unload_audiodevice(mss_data[i].slots[3]); - } -} - diff -Nru a/drivers/sound/sonicvibes.c b/drivers/sound/sonicvibes.c --- a/drivers/sound/sonicvibes.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2767 +0,0 @@ -/*****************************************************************************/ - -/* - * sonicvibes.c -- S3 Sonic Vibes audio driver. - * - * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) - * - * 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. - * - * Special thanks to David C. Niemi - * - * - * Module command line parameters: - * none so far - * - * - * 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 - * - * The card has both an FM and a Wavetable synth, but I have to figure - * out first how to drive them... - * - * Revision history - * 06.05.1998 0.1 Initial release - * 10.05.1998 0.2 Fixed many bugs, esp. ADC rate calculation - * First stab at a simple midi interface (no bells&whistles) - * 13.05.1998 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of - * set_dac_rate in the FMODE_WRITE case in sv_open - * Fix hwptr out of bounds (now mpg123 works) - * 14.05.1998 0.4 Don't allow excessive interrupt rates - * 08.06.1998 0.5 First release using Alan Cox' soundcore instead of miscdevice - * 03.08.1998 0.6 Do not include modversions.h - * Now mixer behaviour can basically be selected between - * "OSS documented" and "OSS actual" behaviour - * 31.08.1998 0.7 Fix realplayer problems - dac.count issues - * 10.12.1998 0.8 Fix drain_dac trying to wait on not yet initialized DMA - * 16.12.1998 0.9 Fix a few f_file & FMODE_ bugs - * 06.01.1999 0.10 remove the silly SA_INTERRUPT flag. - * hopefully killed the egcs section type conflict - * 12.03.1999 0.11 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * 22.03.1999 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 05.04.1999 0.13 added code to sv_read and sv_write which should detect - * lockups of the sound chip and revive it. This is basically - * an ugly hack, but at least applications using this driver - * won't hang forever. I don't know why these lockups happen, - * it might well be the motherboard chipset (an early 486 PCI - * board with ALI chipset), since every busmastering 100MB - * ethernet card I've tried (Realtek 8139 and Macronix tulip clone) - * exhibit similar behaviour (they work for a couple of packets - * and then lock up and can be revived by ifconfig down/up). - * 07.04.1999 0.14 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * Note: dmaio hack might still be wrong on archs other than i386 - * 15.06.1999 0.15 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.1999 0.16 Add pci_set_master - * 03.08.1999 0.17 adapt to Linus' new __setup/__initcall - * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" - * 12.08.1999 0.18 module_init/__setup fixes - * 24.08.1999 0.19 get rid of the dmaio kludge, replace with allocate_resource - * 31.08.1999 0.20 add spin_lock_init - * use new resource allocation to allocate DDMA IO space - * replaced current->state = x with set_current_state(x) - * 03.09.1999 0.21 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race - * 28.10.1999 0.22 More waitqueue races fixed - * 01.12.1999 0.23 New argument to allocate_resource - * 07.12.1999 0.24 More allocate_resource semantics change - * 08.01.2000 0.25 Prevent some ioctl's from returning bad count values on underrun/overrun; - * Tim Janik's BSE (Bedevilled Sound Engine) found this - * use Martin Mares' pci_assign_resource - * 07.02.2000 0.26 Use pci_alloc_consistent and pci_register_driver - * 21.11.2000 0.27 Initialize dma buffers in poll, otherwise poll may return a bogus mask - * 12.12.2000 0.28 More dma buffer initializations, patch from - * Tjeerd Mulder - * 31.01.2001 0.29 Register/Unregister gameport - * Fix SETTRIGGER non OSS API conformity - * 18.05.2001 0.30 PCI probing and error values cleaned up by Marcus - * Meissner - * - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dm.h" - - -/* --------------------------------------------------------------------- */ - -#undef OSS_DOCUMENTED_MIXER_SEMANTICS - -/* --------------------------------------------------------------------- */ - -#ifndef PCI_VENDOR_ID_S3 -#define PCI_VENDOR_ID_S3 0x5333 -#endif -#ifndef PCI_DEVICE_ID_S3_SONICVIBES -#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 -#endif - -#define SV_MAGIC ((PCI_VENDOR_ID_S3<<16)|PCI_DEVICE_ID_S3_SONICVIBES) - -#define SV_EXTENT_SB 0x10 -#define SV_EXTENT_ENH 0x10 -#define SV_EXTENT_SYNTH 0x4 -#define SV_EXTENT_MIDI 0x4 -#define SV_EXTENT_GAME 0x8 -#define SV_EXTENT_DMA 0x10 - -/* - * we are not a bridge and thus use a resource for DDMA that is used for bridges but - * left empty for normal devices - */ -#define RESOURCE_SB 0 -#define RESOURCE_ENH 1 -#define RESOURCE_SYNTH 2 -#define RESOURCE_MIDI 3 -#define RESOURCE_GAME 4 -#define RESOURCE_DDMA 7 - -#define SV_MIDI_DATA 0 -#define SV_MIDI_COMMAND 1 -#define SV_MIDI_STATUS 1 - -#define SV_DMA_ADDR0 0 -#define SV_DMA_ADDR1 1 -#define SV_DMA_ADDR2 2 -#define SV_DMA_ADDR3 3 -#define SV_DMA_COUNT0 4 -#define SV_DMA_COUNT1 5 -#define SV_DMA_COUNT2 6 -#define SV_DMA_MODE 0xb -#define SV_DMA_RESET 0xd -#define SV_DMA_MASK 0xf - -/* - * DONT reset the DMA controllers unless you understand - * the reset semantics. Assuming reset semantics as in - * the 8237 does not work. - */ - -#define DMA_MODE_AUTOINIT 0x10 -#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ -#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ - -#define SV_CODEC_CONTROL 0 -#define SV_CODEC_INTMASK 1 -#define SV_CODEC_STATUS 2 -#define SV_CODEC_IADDR 4 -#define SV_CODEC_IDATA 5 - -#define SV_CCTRL_RESET 0x80 -#define SV_CCTRL_INTADRIVE 0x20 -#define SV_CCTRL_WAVETABLE 0x08 -#define SV_CCTRL_REVERB 0x04 -#define SV_CCTRL_ENHANCED 0x01 - -#define SV_CINTMASK_DMAA 0x01 -#define SV_CINTMASK_DMAC 0x04 -#define SV_CINTMASK_SPECIAL 0x08 -#define SV_CINTMASK_UPDOWN 0x40 -#define SV_CINTMASK_MIDI 0x80 - -#define SV_CSTAT_DMAA 0x01 -#define SV_CSTAT_DMAC 0x04 -#define SV_CSTAT_SPECIAL 0x08 -#define SV_CSTAT_UPDOWN 0x40 -#define SV_CSTAT_MIDI 0x80 - -#define SV_CIADDR_TRD 0x80 -#define SV_CIADDR_MCE 0x40 - -/* codec indirect registers */ -#define SV_CIMIX_ADCINL 0x00 -#define SV_CIMIX_ADCINR 0x01 -#define SV_CIMIX_AUX1INL 0x02 -#define SV_CIMIX_AUX1INR 0x03 -#define SV_CIMIX_CDINL 0x04 -#define SV_CIMIX_CDINR 0x05 -#define SV_CIMIX_LINEINL 0x06 -#define SV_CIMIX_LINEINR 0x07 -#define SV_CIMIX_MICIN 0x08 -#define SV_CIMIX_SYNTHINL 0x0A -#define SV_CIMIX_SYNTHINR 0x0B -#define SV_CIMIX_AUX2INL 0x0C -#define SV_CIMIX_AUX2INR 0x0D -#define SV_CIMIX_ANALOGINL 0x0E -#define SV_CIMIX_ANALOGINR 0x0F -#define SV_CIMIX_PCMINL 0x10 -#define SV_CIMIX_PCMINR 0x11 - -#define SV_CIGAMECONTROL 0x09 -#define SV_CIDATAFMT 0x12 -#define SV_CIENABLE 0x13 -#define SV_CIUPDOWN 0x14 -#define SV_CIREVISION 0x15 -#define SV_CIADCOUTPUT 0x16 -#define SV_CIDMAABASECOUNT1 0x18 -#define SV_CIDMAABASECOUNT0 0x19 -#define SV_CIDMACBASECOUNT1 0x1c -#define SV_CIDMACBASECOUNT0 0x1d -#define SV_CIPCMSR0 0x1e -#define SV_CIPCMSR1 0x1f -#define SV_CISYNTHSR0 0x20 -#define SV_CISYNTHSR1 0x21 -#define SV_CIADCCLKSOURCE 0x22 -#define SV_CIADCALTSR 0x23 -#define SV_CIADCPLLM 0x24 -#define SV_CIADCPLLN 0x25 -#define SV_CISYNTHPLLM 0x26 -#define SV_CISYNTHPLLN 0x27 -#define SV_CIUARTCONTROL 0x2a -#define SV_CIDRIVECONTROL 0x2b -#define SV_CISRSSPACE 0x2c -#define SV_CISRSCENTER 0x2d -#define SV_CIWAVETABLESRC 0x2e -#define SV_CIANALOGPWRDOWN 0x30 -#define SV_CIDIGITALPWRDOWN 0x31 - - -#define SV_CIMIX_ADCSRC_CD 0x20 -#define SV_CIMIX_ADCSRC_DAC 0x40 -#define SV_CIMIX_ADCSRC_AUX2 0x60 -#define SV_CIMIX_ADCSRC_LINE 0x80 -#define SV_CIMIX_ADCSRC_AUX1 0xa0 -#define SV_CIMIX_ADCSRC_MIC 0xc0 -#define SV_CIMIX_ADCSRC_MIXOUT 0xe0 -#define SV_CIMIX_ADCSRC_MASK 0xe0 - -#define SV_CFMT_STEREO 0x01 -#define SV_CFMT_16BIT 0x02 -#define SV_CFMT_MASK 0x03 -#define SV_CFMT_ASHIFT 0 -#define SV_CFMT_CSHIFT 4 - -static const unsigned sample_size[] = { 1, 2, 2, 4 }; -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -#define SV_CENABLE_PPE 0x4 -#define SV_CENABLE_RE 0x2 -#define SV_CENABLE_PE 0x1 - - -/* MIDI buffer sizes */ - -#define MIDIINBUF 256 -#define MIDIOUTBUF 256 - -#define FMODE_MIDI_SHIFT 2 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -#define FMODE_DMFM 0x10 - -/* --------------------------------------------------------------------- */ - -struct sv_state { - /* magic */ - unsigned int magic; - - /* list of sonicvibes devices */ - struct list_head devs; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; - - /* soundcore stuff */ - int dev_audio; - int dev_mixer; - int dev_midi; - int dev_dmfm; - - /* hardware resources */ - unsigned long iosb, ioenh, iosynth, iomidi; /* long for SPARC */ - unsigned int iodmaa, iodmac, irq; - - /* mixer stuff */ - struct { - unsigned int modcnt; -#ifndef OSS_DOCUMENTED_MIXER_SEMANTICS - unsigned short vol[13]; -#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - } mix; - - /* wave stuff */ - unsigned int rateadc, ratedac; - unsigned char fmt, enable; - - 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; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - 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 endcleared:1; - unsigned enabled:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac, dma_adc; - - /* midi stuff */ - struct { - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t iwait; - wait_queue_head_t owait; - struct timer_list timer; - unsigned char ibuf[MIDIINBUF]; - unsigned char obuf[MIDIOUTBUF]; - } midi; - - struct gameport gameport; -}; - -/* --------------------------------------------------------------------- */ - -static LIST_HEAD(devs); -static unsigned long wavetable_mem = 0; - -/* --------------------------------------------------------------------- */ - -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; -} - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#ifdef hweight32 -#undef hweight32 -#endif - -static inline unsigned int hweight32(unsigned int w) -{ - unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); - res = (res & 0x33333333) + ((res >> 2) & 0x33333333); - res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); - res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); - return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); -} - -/* --------------------------------------------------------------------- */ - -/* - * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver. - */ - -#undef DMABYTEIO - -static void set_dmaa(struct sv_state *s, unsigned int addr, unsigned int count) -{ -#ifdef DMABYTEIO - unsigned io = s->iodmaa, u; - - count--; - for (u = 4; u > 0; u--, addr >>= 8, io++) - outb(addr & 0xff, io); - for (u = 3; u > 0; u--, count >>= 8, io++) - outb(count & 0xff, io); -#else /* DMABYTEIO */ - count--; - outl(addr, s->iodmaa + SV_DMA_ADDR0); - outl(count, s->iodmaa + SV_DMA_COUNT0); -#endif /* DMABYTEIO */ - outb(0x18, s->iodmaa + SV_DMA_MODE); -} - -static void set_dmac(struct sv_state *s, unsigned int addr, unsigned int count) -{ -#ifdef DMABYTEIO - unsigned io = s->iodmac, u; - - count >>= 1; - count--; - for (u = 4; u > 0; u--, addr >>= 8, io++) - outb(addr & 0xff, io); - for (u = 3; u > 0; u--, count >>= 8, io++) - outb(count & 0xff, io); -#else /* DMABYTEIO */ - count >>= 1; - count--; - outl(addr, s->iodmac + SV_DMA_ADDR0); - outl(count, s->iodmac + SV_DMA_COUNT0); -#endif /* DMABYTEIO */ - outb(0x14, s->iodmac + SV_DMA_MODE); -} - -static inline unsigned get_dmaa(struct sv_state *s) -{ -#ifdef DMABYTEIO - unsigned io = s->iodmaa+6, v = 0, u; - - for (u = 3; u > 0; u--, io--) { - v <<= 8; - v |= inb(io); - } - return v + 1; -#else /* DMABYTEIO */ - return (inl(s->iodmaa + SV_DMA_COUNT0) & 0xffffff) + 1; -#endif /* DMABYTEIO */ -} - -static inline unsigned get_dmac(struct sv_state *s) -{ -#ifdef DMABYTEIO - unsigned io = s->iodmac+6, v = 0, u; - - for (u = 3; u > 0; u--, io--) { - v <<= 8; - v |= inb(io); - } - return (v + 1) << 1; -#else /* DMABYTEIO */ - return ((inl(s->iodmac + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; -#endif /* DMABYTEIO */ -} - -static void wrindir(struct sv_state *s, unsigned char idx, unsigned char data) -{ - outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); - udelay(10); - outb(data, s->ioenh + SV_CODEC_IDATA); - udelay(10); -} - -static unsigned char rdindir(struct sv_state *s, unsigned char idx) -{ - unsigned char v; - - outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); - udelay(10); - v = inb(s->ioenh + SV_CODEC_IDATA); - udelay(10); - return v; -} - -static void set_fmt(struct sv_state *s, unsigned char mask, unsigned char data) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - outb(SV_CIDATAFMT | SV_CIADDR_MCE, s->ioenh + SV_CODEC_IADDR); - if (mask) { - s->fmt = inb(s->ioenh + SV_CODEC_IDATA); - udelay(10); - } - s->fmt = (s->fmt & mask) | data; - outb(s->fmt, s->ioenh + SV_CODEC_IDATA); - udelay(10); - outb(0, s->ioenh + SV_CODEC_IADDR); - spin_unlock_irqrestore(&s->lock, flags); - udelay(10); -} - -static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, unsigned char data) -{ - outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); - udelay(10); - outb((inb(s->ioenh + SV_CODEC_IDATA) & mask) ^ data, s->ioenh + SV_CODEC_IDATA); - udelay(10); -} - -#define REFFREQUENCY 24576000 -#define ADCMULT 512 -#define FULLRATE 48000 - -static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate) -{ - unsigned long flags; - unsigned char r, m=0, n=0; - unsigned xm, xn, xr, xd, metric = ~0U; - /* the warnings about m and n used uninitialized are bogus and may safely be ignored */ - - if (rate < 625000/ADCMULT) - rate = 625000/ADCMULT; - if (rate > 150000000/ADCMULT) - rate = 150000000/ADCMULT; - /* slight violation of specs, needed for continuous sampling rates */ - for (r = 0; rate < 75000000/ADCMULT; r += 0x20, rate <<= 1); - for (xn = 3; xn < 35; xn++) - for (xm = 3; xm < 130; xm++) { - xr = REFFREQUENCY/ADCMULT * xm / xn; - xd = abs((signed)(xr - rate)); - if (xd < metric) { - metric = xd; - m = xm - 2; - n = xn - 2; - } - } - reg &= 0x3f; - spin_lock_irqsave(&s->lock, flags); - outb(reg, s->ioenh + SV_CODEC_IADDR); - udelay(10); - outb(m, s->ioenh + SV_CODEC_IDATA); - udelay(10); - outb(reg+1, s->ioenh + SV_CODEC_IADDR); - udelay(10); - outb(r | n, s->ioenh + SV_CODEC_IDATA); - spin_unlock_irqrestore(&s->lock, flags); - udelay(10); - return (REFFREQUENCY/ADCMULT * (m + 2) / (n + 2)) >> ((r >> 5) & 7); -} - -#if 0 - -static unsigned getpll(struct sv_state *s, unsigned char reg) -{ - unsigned long flags; - unsigned char m, n; - - reg &= 0x3f; - spin_lock_irqsave(&s->lock, flags); - outb(reg, s->ioenh + SV_CODEC_IADDR); - udelay(10); - m = inb(s->ioenh + SV_CODEC_IDATA); - udelay(10); - outb(reg+1, s->ioenh + SV_CODEC_IADDR); - udelay(10); - n = inb(s->ioenh + SV_CODEC_IDATA); - spin_unlock_irqrestore(&s->lock, flags); - udelay(10); - return (REFFREQUENCY/ADCMULT * (m + 2) / ((n & 0x1f) + 2)) >> ((n >> 5) & 7); -} - -#endif - -static void set_dac_rate(struct sv_state *s, unsigned rate) -{ - unsigned div; - unsigned long flags; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - div = (rate * 65536 + FULLRATE/2) / FULLRATE; - if (div > 65535) - div = 65535; - spin_lock_irqsave(&s->lock, flags); - wrindir(s, SV_CIPCMSR1, div >> 8); - wrindir(s, SV_CIPCMSR0, div); - spin_unlock_irqrestore(&s->lock, flags); - s->ratedac = (div * FULLRATE + 32768) / 65536; -} - -static void set_adc_rate(struct sv_state *s, unsigned rate) -{ - unsigned long flags; - unsigned rate1, rate2, div; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - rate1 = setpll(s, SV_CIADCPLLM, rate); - div = (48000 + rate/2) / rate; - if (div > 8) - div = 8; - rate2 = (48000 + div/2) / div; - spin_lock_irqsave(&s->lock, flags); - wrindir(s, SV_CIADCALTSR, (div-1) << 4); - if (abs((signed)(rate-rate2)) <= abs((signed)(rate-rate1))) { - wrindir(s, SV_CIADCCLKSOURCE, 0x10); - s->rateadc = rate2; - } else { - wrindir(s, SV_CIADCCLKSOURCE, 0x00); - s->rateadc = rate1; - } - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -static inline void stop_adc(struct sv_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->enable &= ~SV_CENABLE_RE; - wrindir(s, SV_CIENABLE, s->enable); - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_dac(struct sv_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->enable &= ~(SV_CENABLE_PPE | SV_CENABLE_PE); - wrindir(s, SV_CIENABLE, s->enable); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac(struct sv_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { - s->enable = (s->enable & ~SV_CENABLE_PPE) | SV_CENABLE_PE; - wrindir(s, SV_CIENABLE, s->enable); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_adc(struct sv_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - s->enable |= SV_CENABLE_RE; - wrindir(s, SV_CIENABLE, s->enable); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -static void dealloc_dmabuf(struct sv_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); - pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); - } - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - - -/* DMAA is used for playback, DMAC is used for recording */ - -static int prog_dmabuf(struct sv_state *s, unsigned rec) -{ - struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; - unsigned rate = rec ? s->rateadc : s->ratedac; - int order; - unsigned bytepersec; - unsigned bufs; - struct page *page, *pend; - unsigned char fmt; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - fmt = s->fmt; - if (rec) { - s->enable &= ~SV_CENABLE_RE; - fmt >>= SV_CFMT_CSHIFT; - } else { - s->enable &= ~SV_CENABLE_PE; - fmt >>= SV_CFMT_ASHIFT; - } - wrindir(s, SV_CIENABLE, s->enable); - spin_unlock_irqrestore(&s->lock, flags); - fmt &= SV_CFMT_MASK; - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - 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; - if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) - printk(KERN_DEBUG "sv: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", - virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); - if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) - printk(KERN_DEBUG "sv: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", - virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); - /* 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); - } - 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--; - 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 & SV_CFMT_16BIT) ? 0 : 0x80, db->dmasize); - spin_lock_irqsave(&s->lock, flags); - if (rec) { - set_dmac(s, db->dmaaddr, db->numfrag << db->fragshift); - /* program enhanced mode registers */ - wrindir(s, SV_CIDMACBASECOUNT1, (db->fragsamples-1) >> 8); - wrindir(s, SV_CIDMACBASECOUNT0, db->fragsamples-1); - } else { - set_dmaa(s, db->dmaaddr, db->numfrag << db->fragshift); - /* program enhanced mode registers */ - wrindir(s, SV_CIDMAABASECOUNT1, (db->fragsamples-1) >> 8); - wrindir(s, SV_CIDMAABASECOUNT0, db->fragsamples-1); - } - spin_unlock_irqrestore(&s->lock, flags); - db->enabled = 1; - db->ready = 1; - return 0; -} - -static inline void clear_advance(struct sv_state *s) -{ - unsigned char c = (s->fmt & (SV_CFMT_16BIT << SV_CFMT_ASHIFT)) ? 0 : 0x80; - unsigned char *buf = s->dma_dac.rawbuf; - unsigned bsize = s->dma_dac.dmasize; - unsigned bptr = s->dma_dac.swptr; - unsigned len = s->dma_dac.fragsize; - - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(buf + bptr, c, x); - bptr = 0; - len -= x; - } - memset(buf + bptr, c, len); -} - -/* call with spinlock held! */ -static void sv_update_ptr(struct sv_state *s) -{ - unsigned hwptr; - int diff; - - /* update ADC pointer */ - if (s->dma_adc.ready) { - hwptr = (s->dma_adc.dmasize - get_dmac(s)) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - s->enable &= ~SV_CENABLE_RE; - wrindir(s, SV_CIENABLE, s->enable); - s->dma_adc.error++; - } - } - } - /* update DAC pointer */ - if (s->dma_dac.ready) { - hwptr = (s->dma_dac.dmasize - get_dmaa(s)) % s->dma_dac.dmasize; - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - wake_up(&s->dma_dac.wait); - } else { - s->dma_dac.count -= diff; - if (s->dma_dac.count <= 0) { - s->enable &= ~SV_CENABLE_PE; - wrindir(s, SV_CIENABLE, s->enable); - s->dma_dac.error++; - } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { - clear_advance(s); - s->dma_dac.endcleared = 1; - } - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) - wake_up(&s->dma_dac.wait); - } - } -} - -/* hold spinlock for the following! */ -static void sv_handle_midi(struct sv_state *s) -{ - unsigned char ch; - int wake; - - wake = 0; - while (!(inb(s->iomidi+1) & 0x80)) { - ch = inb(s->iomidi); - if (s->midi.icnt < MIDIINBUF) { - s->midi.ibuf[s->midi.iwr] = ch; - s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; - s->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&s->midi.iwait); - wake = 0; - while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { - outb(s->midi.obuf[s->midi.ord], s->iomidi); - s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; - s->midi.ocnt--; - if (s->midi.ocnt < MIDIOUTBUF-16) - wake = 1; - } - if (wake) - wake_up(&s->midi.owait); -} - -static void sv_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct sv_state *s = (struct sv_state *)dev_id; - unsigned int intsrc; - - /* fastpath out, to ease interrupt sharing */ - intsrc = inb(s->ioenh + SV_CODEC_STATUS); - if (!(intsrc & (SV_CSTAT_DMAA | SV_CSTAT_DMAC | SV_CSTAT_MIDI))) - return; - spin_lock(&s->lock); - sv_update_ptr(s); - sv_handle_midi(s); - spin_unlock(&s->lock); -} - -static void sv_midi_timer(unsigned long data) -{ - struct sv_state *s = (struct sv_state *)data; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - sv_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - s->midi.timer.expires = jiffies+1; - add_timer(&s->midi.timer); -} - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT "sv: invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != SV_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - -#define MT_4 1 -#define MT_5MUTE 2 -#define MT_4MUTEMONO 3 -#define MT_6MUTE 4 - -static const struct { - unsigned left:5; - unsigned right:5; - unsigned type:3; - unsigned rec:3; -} mixtable[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_RECLEV] = { SV_CIMIX_ADCINL, SV_CIMIX_ADCINR, MT_4, 0 }, - [SOUND_MIXER_LINE1] = { SV_CIMIX_AUX1INL, SV_CIMIX_AUX1INR, MT_5MUTE, 5 }, - [SOUND_MIXER_CD] = { SV_CIMIX_CDINL, SV_CIMIX_CDINR, MT_5MUTE, 1 }, - [SOUND_MIXER_LINE] = { SV_CIMIX_LINEINL, SV_CIMIX_LINEINR, MT_5MUTE, 4 }, - [SOUND_MIXER_MIC] = { SV_CIMIX_MICIN, SV_CIMIX_ADCINL, MT_4MUTEMONO, 6 }, - [SOUND_MIXER_SYNTH] = { SV_CIMIX_SYNTHINL, SV_CIMIX_SYNTHINR, MT_5MUTE, 2 }, - [SOUND_MIXER_LINE2] = { SV_CIMIX_AUX2INL, SV_CIMIX_AUX2INR, MT_5MUTE, 3 }, - [SOUND_MIXER_VOLUME] = { SV_CIMIX_ANALOGINL, SV_CIMIX_ANALOGINR, MT_5MUTE, 7 }, - [SOUND_MIXER_PCM] = { SV_CIMIX_PCMINL, SV_CIMIX_PCMINR, MT_6MUTE, 0 } -}; - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - -static int return_mixval(struct sv_state *s, unsigned i, int *arg) -{ - unsigned long flags; - unsigned char l, r, rl, rr; - - spin_lock_irqsave(&s->lock, flags); - l = rdindir(s, mixtable[i].left); - r = rdindir(s, mixtable[i].right); - spin_unlock_irqrestore(&s->lock, flags); - switch (mixtable[i].type) { - case MT_4: - r &= 0xf; - l &= 0xf; - rl = 10 + 6 * (l & 15); - rr = 10 + 6 * (r & 15); - break; - - case MT_4MUTEMONO: - rl = 55 - 3 * (l & 15); - if (r & 0x10) - rl += 45; - rr = rl; - r = l; - break; - - case MT_5MUTE: - default: - rl = 100 - 3 * (l & 31); - rr = 100 - 3 * (r & 31); - break; - - case MT_6MUTE: - rl = 100 - 3 * (l & 63) / 2; - rr = 100 - 3 * (r & 63) / 2; - break; - } - if (l & 0x80) - rl = 0; - if (r & 0x80) - rr = 0; - return put_user((rr << 8) | rl, arg); -} - -#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - -static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = -{ - [SOUND_MIXER_RECLEV] = 1, - [SOUND_MIXER_LINE1] = 2, - [SOUND_MIXER_CD] = 3, - [SOUND_MIXER_LINE] = 4, - [SOUND_MIXER_MIC] = 5, - [SOUND_MIXER_SYNTH] = 6, - [SOUND_MIXER_LINE2] = 7, - [SOUND_MIXER_VOLUME] = 8, - [SOUND_MIXER_PCM] = 9 -}; - -#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - -static unsigned mixer_recmask(struct sv_state *s) -{ - unsigned long flags; - int i, j; - - spin_lock_irqsave(&s->lock, flags); - j = rdindir(s, SV_CIMIX_ADCINL) >> 5; - spin_unlock_irqrestore(&s->lock, flags); - j &= 7; - for (i = 0; i < SOUND_MIXER_NRDEVICES && mixtable[i].rec != j; i++); - return 1 << i; -} - -static int mixer_ioctl(struct sv_state *s, unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - int i, val; - unsigned char l, r, rl, rr; - - VALIDATE_STATE(s); - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - 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; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - 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; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *)arg); - if (cmd == SOUND_MIXER_PRIVATE1) { /* SRS settings */ - if (get_user(val, (int *)arg)) - return -EFAULT; - spin_lock_irqsave(&s->lock, flags); - if (val & 1) { - if (val & 2) { - l = 4 - ((val >> 2) & 7); - if (l & ~3) - l = 4; - r = 4 - ((val >> 5) & 7); - if (r & ~3) - r = 4; - wrindir(s, SV_CISRSSPACE, l); - wrindir(s, SV_CISRSCENTER, r); - } else - wrindir(s, SV_CISRSSPACE, 0x80); - } - l = rdindir(s, SV_CISRSSPACE); - r = rdindir(s, SV_CISRSCENTER); - spin_unlock_irqrestore(&s->lock, flags); - if (l & 0x80) - return put_user(0, (int *)arg); - return put_user(((4 - (l & 7)) << 2) | ((4 - (r & 7)) << 5) | 2, (int *)arg); - } - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - if (_SIOC_DIR(cmd) == _SIOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - return put_user(mixer_recmask(s), (int *)arg); - - case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].type) - val |= 1 << i; - return put_user(val, (int *)arg); - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].rec) - val |= 1 << i; - return put_user(val, (int *)arg); - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) - val |= 1 << i; - return put_user(val, (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 || !mixtable[i].type) - return -EINVAL; -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - return return_mixval(s, i, (int *)arg); -#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - if (!volidx[i]) - return -EINVAL; - return put_user(s->mix.vol[volidx[i]-1], (int *)arg); -#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - } - } - if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) - return -EINVAL; - s->mix.modcnt++; - 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); - if (i == 0) - return 0; /*val = mixer_recmask(s);*/ - else if (i > 1) - val &= ~mixer_recmask(s); - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!(val & (1 << i))) - continue; - if (mixtable[i].rec) - break; - } - if (!mixtable[i].rec) - return 0; - spin_lock_irqsave(&s->lock, flags); - frobindir(s, SV_CIMIX_ADCINL, 0x1f, mixtable[i].rec << 5); - frobindir(s, SV_CIMIX_ADCINR, 0x1f, mixtable[i].rec << 5); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) - return -EINVAL; - if (get_user(val, (int *)arg)) - return -EFAULT; - l = val & 0xff; - r = (val >> 8) & 0xff; - if (mixtable[i].type == MT_4MUTEMONO) - l = (r + l) / 2; - if (l > 100) - l = 100; - if (r > 100) - r = 100; - spin_lock_irqsave(&s->lock, flags); - switch (mixtable[i].type) { - case MT_4: - if (l >= 10) - l -= 10; - if (r >= 10) - r -= 10; - frobindir(s, mixtable[i].left, 0xf0, l / 6); - frobindir(s, mixtable[i].right, 0xf0, l / 6); - break; - - case MT_4MUTEMONO: - rr = 0; - if (l < 10) - rl = 0x80; - else { - if (l >= 55) { - rr = 0x10; - l -= 45; - } - rl = (55 - l) / 3; - } - wrindir(s, mixtable[i].left, rl); - frobindir(s, mixtable[i].right, ~0x10, rr); - break; - - case MT_5MUTE: - if (l < 7) - rl = 0x80; - else - rl = (100 - l) / 3; - if (r < 7) - rr = 0x80; - else - rr = (100 - r) / 3; - wrindir(s, mixtable[i].left, rl); - wrindir(s, mixtable[i].right, rr); - break; - - case MT_6MUTE: - if (l < 6) - rl = 0x80; - else - rl = (100 - l) * 2 / 3; - if (r < 6) - rr = 0x80; - else - rr = (100 - r) * 2 / 3; - wrindir(s, mixtable[i].left, rl); - wrindir(s, mixtable[i].right, rr); - break; - } - spin_unlock_irqrestore(&s->lock, flags); -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - return return_mixval(s, i, (int *)arg); -#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - if (!volidx[i]) - return -EINVAL; - s->mix.vol[volidx[i]-1] = val; - return put_user(s->mix.vol[volidx[i]-1], (int *)arg); -#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - } -} - -/* --------------------------------------------------------------------- */ - -static int sv_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - struct list_head *list; - struct sv_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct sv_state, devs); - if (s->dev_mixer == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - return 0; -} - -static int sv_release_mixdev(struct inode *inode, struct file *file) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - - VALIDATE_STATE(s); - return 0; -} - -static int sv_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - return mixer_ioctl((struct sv_state *)file->private_data, cmd, arg); -} - -static /*const*/ struct file_operations sv_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: sv_ioctl_mixdev, - open: sv_open_mixdev, - release: sv_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct sv_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - 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) { - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; - tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; - if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "sv: dma timed out??\n"); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - 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(s, 1))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; -#if 0 - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - spin_unlock_irqrestore(&s->lock, flags); -#endif - add_wait_queue(&s->dma_adc.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_adc.enabled) - start_adc(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - if (!schedule_timeout(HZ)) { - printk(KERN_DEBUG "sv: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, - s->dma_adc.hwptr, s->dma_adc.swptr); - stop_adc(s); - spin_lock_irqsave(&s->lock, flags); - set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); - /* program enhanced mode registers */ - wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); - wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); - s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_adc.enabled) - start_adc(s); - } - remove_wait_queue(&s->dma_adc.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -static ssize_t sv_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - 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(s, 0))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; -#if 0 - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - spin_unlock_irqrestore(&s->lock, flags); -#endif - add_wait_queue(&s->dma_dac.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - swptr = s->dma_dac.swptr; - cnt = s->dma_dac.dmasize-swptr; - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_dac.enabled) - start_dac(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - if (!schedule_timeout(HZ)) { - printk(KERN_DEBUG "sv: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, - s->dma_dac.hwptr, s->dma_dac.swptr); - stop_dac(s); - spin_lock_irqsave(&s->lock, flags); - set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); - /* program enhanced mode registers */ - wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); - wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); - s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_dac.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_dac.enabled) - start_dac(s); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int sv_poll(struct file *file, struct poll_table_struct *wait) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac.ready && prog_dmabuf(s, 1)) - return 0; - poll_wait(file, &s->dma_dac.wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready && prog_dmabuf(s, 0)) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - 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 sv_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - struct dmabuf *db; - int ret = -EINVAL; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 1)) != 0) - goto out; - db = &s->dma_dac; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 0)) != 0) - goto out; - db = &s->dma_adc; - } else - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) - goto out; - ret = -EAGAIN; - if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) - goto out; - db->mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int sv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val, mapped, ret; - unsigned char fmtm, fmtd; - - 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: - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_SYNC: - 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: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(); - s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - 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); - s->dma_adc.ready = 0; - set_adc_rate(s, val); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - set_dac_rate(s, val); - } - } - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val) - fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; - else - fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val) - fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; - else - fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); - } - set_fmt(s, fmtm, fmtd); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val >= 2) - fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; - else - fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val >= 2) - fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; - else - fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); - } - set_fmt(s, fmtm, fmtd); - } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) - : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (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) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; - else - fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_CSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; - else - fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_ASHIFT); - } - set_fmt(s, fmtm, fmtd); - } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) - : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (file->f_mode & FMODE_READ && s->enable & SV_CENABLE_RE) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->enable & SV_CENABLE_PE) - 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(s, 1))) - return ret; - s->dma_adc.enabled = 1; - start_adc(s); - } else { - s->dma_adc.enabled = 0; - stop_adc(s); - } - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - s->dma_dac.enabled = 1; - start_dac(s); - } else { - s->dma_dac.enabled = 0; - 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(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - count = s->dma_dac.count; - 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; - 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(s, 1)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - count = s->dma_adc.count; - if (count < 0) - count = 0; - abinfo.bytes = 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 && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - count = s->dma_dac.count; - 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; - if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - count = s->dma_adc.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_adc.fragshift; - 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 && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - count = s->dma_dac.count; - if (count < 0) - count = 0; - cinfo.blocks = 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(s, 0))) - return val; - return put_user(s->dma_dac.fragsize, (int *)arg); - } - if ((val = prog_dmabuf(s, 1))) - return val; - 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) { - 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 (file->f_mode & FMODE_WRITE) { - 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; - } - 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) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) - : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (int *)arg); - - case SOUND_PCM_READ_BITS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) - : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? 16 : 8, (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 sv_open(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - unsigned char fmtm = ~0, fmts = 0; - struct list_head *list; - struct sv_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct sv_state, devs); - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - VALIDATE_STATE(s); - 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); - } - if (file->f_mode & FMODE_READ) { - fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_CSHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - s->dma_adc.enabled = 1; - set_adc_rate(s, 8000); - } - if (file->f_mode & FMODE_WRITE) { - fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_ASHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; - s->dma_dac.enabled = 1; - set_dac_rate(s, 8000); - } - set_fmt(s, fmtm, fmts); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - up(&s->open_sem); - return 0; -} - -static int sv_release(struct inode *inode, struct file *file) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - - VALIDATE_STATE(s); - 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); - wake_up(&s->open_wait); - up(&s->open_sem); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations sv_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: sv_read, - write: sv_write, - poll: sv_poll, - ioctl: sv_ioctl, - mmap: sv_mmap, - open: sv_open, - release: sv_release, -}; - -/* --------------------------------------------------------------------- */ - -static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.iwait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.ird; - cnt = MIDIINBUF - ptr; - if (s->midi.icnt < cnt) - cnt = s->midi.icnt; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIINBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.ird = ptr; - s->midi.icnt -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - break; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.iwait, &wait); - return ret; -} - -static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (ppos != &file->f_pos) - return -ESPIPE; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.owait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.owr; - cnt = MIDIOUTBUF - ptr; - if (s->midi.ocnt + cnt > MIDIOUTBUF) - cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) { - __set_current_state(TASK_INTERRUPTIBLE); - sv_handle_midi(s); - } - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIOUTBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.owr = ptr; - s->midi.ocnt += cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&s->lock, flags); - sv_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.owait, &wait); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int sv_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &s->midi.owait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &s->midi.iwait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - if (s->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->midi.ocnt < MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int sv_midi_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 sv_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct sv_state, devs); - if (s->dev_midi == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - 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); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - //outb(inb(s->ioenh + SV_CODEC_CONTROL) | SV_CCTRL_WAVETABLE, s->ioenh + SV_CODEC_CONTROL); - outb(inb(s->ioenh + SV_CODEC_INTMASK) | SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); - wrindir(s, SV_CIUARTCONTROL, 5); /* output MIDI data to external and internal synth */ - wrindir(s, SV_CIWAVETABLESRC, 1); /* Wavetable in PC RAM */ - outb(0xff, s->iomidi+1); /* reset command */ - outb(0x3f, s->iomidi+1); /* uart command */ - if (!(inb(s->iomidi+1) & 0x80)) - inb(s->iomidi); - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - init_timer(&s->midi.timer); - s->midi.timer.expires = jiffies+1; - s->midi.timer.data = (unsigned long)s; - s->midi.timer.function = sv_midi_timer; - add_timer(&s->midi.timer); - } - if (file->f_mode & FMODE_READ) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - } - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); - up(&s->open_sem); - return 0; -} - -static int sv_midi_release(struct inode *inode, struct file *file) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - VALIDATE_STATE(s); - - lock_kernel(); - if (file->f_mode & FMODE_WRITE) { - add_wait_queue(&s->midi.owait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->midi.ocnt; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - 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; - } - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "sv: midi timed out??\n"); - } - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - } - down(&s->open_sem); - s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - outb(inb(s->ioenh + SV_CODEC_INTMASK) & ~SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); - del_timer(&s->midi.timer); - } - spin_unlock_irqrestore(&s->lock, flags); - wake_up(&s->open_wait); - up(&s->open_sem); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations sv_midi_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: sv_midi_read, - write: sv_midi_write, - poll: sv_midi_poll, - open: sv_midi_open, - release: sv_midi_release, -}; - -/* --------------------------------------------------------------------- */ - -static int sv_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - static const unsigned char op_offset[18] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 - }; - struct sv_state *s = (struct sv_state *)file->private_data; - struct dm_fm_voice v; - struct dm_fm_note n; - struct dm_fm_params p; - unsigned int io; - unsigned int regb; - - switch (cmd) { - case FM_IOCTL_RESET: - for (regb = 0xb0; regb < 0xb9; regb++) { - outb(regb, s->iosynth); - outb(0, s->iosynth+1); - outb(regb, s->iosynth+2); - outb(0, s->iosynth+3); - } - return 0; - - case FM_IOCTL_PLAY_NOTE: - if (copy_from_user(&n, (void *)arg, sizeof(n))) - return -EFAULT; - if (n.voice >= 18) - return -EINVAL; - if (n.voice >= 9) { - regb = n.voice - 9; - io = s->iosynth+2; - } else { - regb = n.voice; - io = s->iosynth; - } - outb(0xa0 + regb, io); - outb(n.fnum & 0xff, io+1); - outb(0xb0 + regb, io); - outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); - return 0; - - case FM_IOCTL_SET_VOICE: - if (copy_from_user(&v, (void *)arg, sizeof(v))) - return -EFAULT; - if (v.voice >= 18) - return -EINVAL; - regb = op_offset[v.voice]; - io = s->iosynth + ((v.op & 1) << 1); - outb(0x20 + regb, io); - outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | - ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); - outb(0x40 + regb, io); - outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); - outb(0x60 + regb, io); - outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); - outb(0x80 + regb, io); - outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); - outb(0xe0 + regb, io); - outb(v.waveform & 0x7, io+1); - if (n.voice >= 9) { - regb = n.voice - 9; - io = s->iosynth+2; - } else { - regb = n.voice; - io = s->iosynth; - } - outb(0xc0 + regb, io); - outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | - (v.connection & 1), io+1); - return 0; - - case FM_IOCTL_SET_PARAMS: - if (copy_from_user(&p, (void *)arg, sizeof(p))) - return -EFAULT; - outb(0x08, s->iosynth); - outb((p.kbd_split & 1) << 6, s->iosynth+1); - outb(0xbd, s->iosynth); - outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | - ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1); - return 0; - - case FM_IOCTL_SET_OPL: - outb(4, s->iosynth+2); - outb(arg, s->iosynth+3); - return 0; - - case FM_IOCTL_SET_MODE: - outb(5, s->iosynth+2); - outb(arg & 1, s->iosynth+3); - return 0; - - default: - return -EINVAL; - } -} - -static int sv_dmfm_open(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - struct list_head *list; - struct sv_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct sv_state, devs); - if (s->dev_dmfm == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - down(&s->open_sem); - while (s->open_mode & FMODE_DMFM) { - 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); - } - /* init the stuff */ - outb(1, s->iosynth); - outb(0x20, s->iosynth+1); /* enable waveforms */ - outb(4, s->iosynth+2); - outb(0, s->iosynth+3); /* no 4op enabled */ - outb(5, s->iosynth+2); - outb(1, s->iosynth+3); /* enable OPL3 */ - s->open_mode |= FMODE_DMFM; - up(&s->open_sem); - return 0; -} - -static int sv_dmfm_release(struct inode *inode, struct file *file) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - unsigned int regb; - - VALIDATE_STATE(s); - lock_kernel(); - down(&s->open_sem); - s->open_mode &= ~FMODE_DMFM; - for (regb = 0xb0; regb < 0xb9; regb++) { - outb(regb, s->iosynth); - outb(0, s->iosynth+1); - outb(regb, s->iosynth+2); - outb(0, s->iosynth+3); - } - wake_up(&s->open_wait); - up(&s->open_sem); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations sv_dmfm_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: sv_dmfm_ioctl, - open: sv_dmfm_open, - release: sv_dmfm_release, -}; - -/* --------------------------------------------------------------------- */ - -/* maximum number of devices; only used for command line params */ -#define NR_DEVICE 5 - -static int reverb[NR_DEVICE] = { 0, }; - -#if 0 -static int wavetable[NR_DEVICE] = { 0, }; -#endif - -static unsigned int devindex = 0; - -MODULE_PARM(reverb, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(reverb, "if 1 enables the reverb circuitry. NOTE: your card must have the reverb RAM"); -#if 0 -MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled"); -#endif - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("S3 SonicVibes Driver"); -MODULE_LICENSE("GPL"); - - -/* --------------------------------------------------------------------- */ - -static struct initvol { - int mixch; - int vol; -} initvol[] __initdata = { - { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, - { SOUND_MIXER_WRITE_LINE1, 0x4040 }, - { SOUND_MIXER_WRITE_CD, 0x4040 }, - { SOUND_MIXER_WRITE_LINE, 0x4040 }, - { SOUND_MIXER_WRITE_MIC, 0x4040 }, - { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, - { SOUND_MIXER_WRITE_LINE2, 0x4040 }, - { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, - { SOUND_MIXER_WRITE_PCM, 0x4040 } -}; - -#define RSRCISIOREGION(dev,num) (pci_resource_start((dev), (num)) != 0 && \ - (pci_resource_flags((dev), (num)) & IORESOURCE_IO)) - -static int __devinit sv_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) -{ - static char __initdata sv_ddma_name[] = "S3 Inc. SonicVibes DDMA Controller"; - struct sv_state *s; - mm_segment_t fs; - int i, val, ret; - char *ddmaname; - unsigned ddmanamelen; - - if ((ret=pci_enable_device(pcidev))) - return ret; - - if (!RSRCISIOREGION(pcidev, RESOURCE_SB) || - !RSRCISIOREGION(pcidev, RESOURCE_ENH) || - !RSRCISIOREGION(pcidev, RESOURCE_SYNTH) || - !RSRCISIOREGION(pcidev, RESOURCE_MIDI) || - !RSRCISIOREGION(pcidev, RESOURCE_GAME)) - return -ENODEV; - if (pcidev->irq == 0) - return -ENODEV; - if (pci_set_dma_mask(pcidev, 0x00ffffff)) { - printk(KERN_WARNING "sonicvibes: architecture does not support 24bit PCI busmaster DMA\n"); - return -ENODEV; - } - /* try to allocate a DDMA resource if not already available */ - if (!RSRCISIOREGION(pcidev, RESOURCE_DDMA)) { - pcidev->resource[RESOURCE_DDMA].start = 0; - pcidev->resource[RESOURCE_DDMA].end = 2*SV_EXTENT_DMA-1; - pcidev->resource[RESOURCE_DDMA].flags = PCI_BASE_ADDRESS_SPACE_IO | IORESOURCE_IO; - ddmanamelen = strlen(sv_ddma_name)+1; - if (!(ddmaname = kmalloc(ddmanamelen, GFP_KERNEL))) - return -1; - memcpy(ddmaname, sv_ddma_name, ddmanamelen); - pcidev->resource[RESOURCE_DDMA].name = ddmaname; - if (pci_assign_resource(pcidev, RESOURCE_DDMA)) { - pcidev->resource[RESOURCE_DDMA].name = NULL; - kfree(ddmaname); - printk(KERN_ERR "sv: cannot allocate DDMA controller io ports\n"); - return -EBUSY; - } - } - if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) { - printk(KERN_WARNING "sv: out of memory\n"); - return -ENOMEM; - } - memset(s, 0, sizeof(struct sv_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->midi.iwait); - init_waitqueue_head(&s->midi.owait); - init_MUTEX(&s->open_sem); - spin_lock_init(&s->lock); - s->magic = SV_MAGIC; - s->dev = pcidev; - s->iosb = pci_resource_start(pcidev, RESOURCE_SB); - s->ioenh = pci_resource_start(pcidev, RESOURCE_ENH); - s->iosynth = pci_resource_start(pcidev, RESOURCE_SYNTH); - s->iomidi = pci_resource_start(pcidev, RESOURCE_MIDI); - s->iodmaa = pci_resource_start(pcidev, RESOURCE_DDMA); - s->iodmac = pci_resource_start(pcidev, RESOURCE_DDMA) + SV_EXTENT_DMA; - s->gameport.io = pci_resource_start(pcidev, RESOURCE_GAME); - pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9); /* enable and use extended mode */ - pci_write_config_dword(pcidev, 0x48, s->iodmac | 9); /* enable */ - printk(KERN_DEBUG "sv: io ports: %#lx %#lx %#lx %#lx %#x %#x %#x\n", - s->iosb, s->ioenh, s->iosynth, s->iomidi, s->gameport.io, s->iodmaa, s->iodmac); - s->irq = pcidev->irq; - - /* hack */ - pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12); /* wavetable base address */ - - ret = -EBUSY; - if (!request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM")) { - printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1); - goto err_region5; - } - if (!request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA")) { - printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1); - goto err_region4; - } - if (!request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC")) { - printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1); - goto err_region3; - } - if (!request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi")) { - printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1); - goto err_region2; - } - if (!request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth")) { - printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1); - goto err_region1; - } - if (s->gameport.io && !request_region(s->gameport.io, SV_EXTENT_GAME, "ESS Solo1")) { - printk(KERN_ERR "sv: gameport io ports in use\n"); - s->gameport.io = 0; - } - /* initialize codec registers */ - outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */ - udelay(50); - outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */ - udelay(50); - outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE */ - | (reverb[devindex] ? SV_CCTRL_REVERB : 0), s->ioenh + SV_CODEC_CONTROL); - inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */ - wrindir(s, SV_CIDRIVECONTROL, 0); /* drive current 16mA */ - wrindir(s, SV_CIENABLE, s->enable = 0); /* disable DMAA and DMAC */ - outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK); - /* outb(0xff, s->iodmaa + SV_DMA_RESET); */ - /* outb(0xff, s->iodmac + SV_DMA_RESET); */ - inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ - wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */ - wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */ - wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */ - setpll(s, SV_CIADCPLLM, 8000); - wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */ - wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff); - wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff); - wrindir(s, SV_CIADCOUTPUT, 0); - /* request irq */ - if ((ret=request_irq(s->irq,sv_interrupt,SA_SHIRQ,"S3 SonicVibes",s))) { - printk(KERN_ERR "sv: irq %u in use\n", s->irq); - goto err_irq; - } - printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n", - s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION)); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0) { - ret = s->dev_audio; - goto err_dev1; - } - if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0) { - ret = s->dev_mixer; - goto err_dev2; - } - if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0) { - ret = s->dev_midi; - goto err_dev3; - } - if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) { - ret = s->dev_dmfm; - goto err_dev4; - } - pci_set_master(pcidev); /* enable bus mastering */ - /* initialize the chips */ - fs = get_fs(); - set_fs(KERNEL_DS); - val = SOUND_MASK_LINE|SOUND_MASK_SYNTH; - 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); - } - set_fs(fs); - /* register gameport */ - gameport_register_port(&s->gameport); - /* store it in the driver field */ - pci_set_drvdata(pcidev, s); - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - /* increment devindex */ - if (devindex < NR_DEVICE-1) - devindex++; - return 0; - - err_dev4: - unregister_sound_midi(s->dev_midi); - err_dev3: - unregister_sound_mixer(s->dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR "sv: cannot register misc device\n"); - free_irq(s->irq, s); - err_irq: - if (s->gameport.io) - release_region(s->gameport.io, SV_EXTENT_GAME); - release_region(s->iosynth, SV_EXTENT_SYNTH); - err_region1: - release_region(s->iomidi, SV_EXTENT_MIDI); - err_region2: - release_region(s->iodmac, SV_EXTENT_DMA); - err_region3: - release_region(s->iodmaa, SV_EXTENT_DMA); - err_region4: - release_region(s->ioenh, SV_EXTENT_ENH); - err_region5: - kfree(s); - return ret; -} - -static void __devinit sv_remove(struct pci_dev *dev) -{ - struct sv_state *s = pci_get_drvdata(dev); - - if (!s) - return; - list_del(&s->devs); - outb(~0, s->ioenh + SV_CODEC_INTMASK); /* disable ints */ - synchronize_irq(); - inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ - wrindir(s, SV_CIENABLE, 0); /* disable DMAA and DMAC */ - /*outb(0, s->iodmaa + SV_DMA_RESET);*/ - /*outb(0, s->iodmac + SV_DMA_RESET);*/ - free_irq(s->irq, s); - if (s->gameport.io) { - gameport_unregister_port(&s->gameport); - release_region(s->gameport.io, SV_EXTENT_GAME); - } - release_region(s->iodmac, SV_EXTENT_DMA); - release_region(s->iodmaa, SV_EXTENT_DMA); - release_region(s->ioenh, SV_EXTENT_ENH); - release_region(s->iomidi, SV_EXTENT_MIDI); - release_region(s->iosynth, SV_EXTENT_SYNTH); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->dev_mixer); - unregister_sound_midi(s->dev_midi); - unregister_sound_special(s->dev_dmfm); - kfree(s); - pci_set_drvdata(dev, NULL); -} - -static struct pci_device_id id_table[] __devinitdata = { - { PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver sv_driver = { - name: "sonicvibes", - id_table: id_table, - probe: sv_probe, - remove: sv_remove -}; - -static int __init init_sonicvibes(void) -{ - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - printk(KERN_INFO "sv: version v0.30 time " __TIME__ " " __DATE__ "\n"); -#if 0 - if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) - printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); -#endif - return pci_module_init(&sv_driver); -} - -static void __exit cleanup_sonicvibes(void) -{ - printk(KERN_INFO "sv: unloading\n"); - pci_unregister_driver(&sv_driver); - if (wavetable_mem) - free_pages(wavetable_mem, 20-PAGE_SHIFT); -} - -module_init(init_sonicvibes); -module_exit(cleanup_sonicvibes); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* format is: sonicvibes=[reverb] sonicvibesdmaio=dmaioaddr */ - -static int __init sonicvibes_setup(char *str) -{ - static unsigned __initdata nr_dev = 0; - - if (nr_dev >= NR_DEVICE) - return 0; -#if 0 - if (get_option(&str, &reverb[nr_dev]) == 2) - (void)get_option(&str, &wavetable[nr_dev]); -#else - (void)get_option(&str, &reverb[nr_dev]); -#endif - - nr_dev++; - return 1; -} - -__setup("sonicvibes=", sonicvibes_setup); - -#endif /* MODULE */ diff -Nru a/drivers/sound/sound_calls.h b/drivers/sound/sound_calls.h --- a/drivers/sound/sound_calls.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,91 +0,0 @@ -/* - * DMA buffer calls - */ - -int DMAbuf_open(int dev, int mode); -int DMAbuf_release(int dev, int mode); -int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock); -int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock); -int DMAbuf_rmchars(int dev, int buff_no, int c); -int DMAbuf_start_output(int dev, int buff_no, int l); -int DMAbuf_move_wrpointer(int dev, int l); -/* int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local); */ -void DMAbuf_init(int dev, int dma1, int dma2); -void DMAbuf_deinit(int dev); -int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); -int DMAbuf_open_dma (int dev); -void DMAbuf_close_dma (int dev); -void DMAbuf_inputintr(int dev); -void DMAbuf_outputintr(int dev, int underflow_flag); -struct dma_buffparms; -int DMAbuf_space_in_queue (int dev); -int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap); -int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction); -void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap); -unsigned int DMAbuf_poll(struct file *file, int dev, poll_table *wait); -void DMAbuf_start_devices(unsigned int devmask); -void DMAbuf_reset (int dev); -int DMAbuf_sync (int dev); - -/* - * System calls for /dev/dsp and /dev/audio (audio.c) - */ - -int audio_read (int dev, struct file *file, char *buf, int count); -int audio_write (int dev, struct file *file, const char *buf, int count); -int audio_open (int dev, struct file *file); -void audio_release (int dev, struct file *file); -int audio_ioctl (int dev, struct file *file, - unsigned int cmd, caddr_t arg); -void audio_init_devices (void); -void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording); -int dma_ioctl (int dev, unsigned int cmd, caddr_t arg); - -/* - * System calls for the /dev/sequencer - */ - -int sequencer_read (int dev, struct file *file, char *buf, int count); -int sequencer_write (int dev, struct file *file, const char *buf, int count); -int sequencer_open (int dev, struct file *file); -void sequencer_release (int dev, struct file *file); -int sequencer_ioctl (int dev, struct file *file, unsigned int cmd, caddr_t arg); -unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait); - -void sequencer_init (void); -void sequencer_unload (void); -void sequencer_timer(unsigned long dummy); -int note_to_freq(int note_num); -unsigned long compute_finetune(unsigned long base_freq, int bend, int range, - int vibrato_bend); -void seq_input_event(unsigned char *event, int len); -void seq_copy_to_input (unsigned char *event, int len); - -/* - * System calls for the /dev/midi - */ - -int MIDIbuf_read (int dev, struct file *file, char *buf, int count); -int MIDIbuf_write (int dev, struct file *file, const char *buf, int count); -int MIDIbuf_open (int dev, struct file *file); -void MIDIbuf_release (int dev, struct file *file); -int MIDIbuf_ioctl (int dev, struct file *file, unsigned int cmd, caddr_t arg); -unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait); -int MIDIbuf_avail(int dev); - -void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count); -void MIDIbuf_init(void); - - -/* From soundcard.c */ -void request_sound_timer (int count); -void sound_stop_timer(void); -void conf_printf(char *name, struct address_info *hw_config); -void conf_printf2(char *name, int base, int irq, int dma, int dma2); - -/* From sound_timer.c */ -void sound_timer_interrupt(void); -void sound_timer_syncinterval(unsigned int new_usecs); - -/* From midi_synth.c */ -void do_midi_msg (int synthno, unsigned char *msg, int mlen); diff -Nru a/drivers/sound/sound_config.h b/drivers/sound/sound_config.h --- a/drivers/sound/sound_config.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,154 +0,0 @@ -/* sound_config.h - * - * A driver for sound cards, misc. configuration parameters. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - - -#ifndef _SOUND_CONFIG_H_ -#define _SOUND_CONFIG_H_ - -#include -#include -#include - -#include "os.h" -#include "soundvers.h" - - -#ifndef SND_DEFAULT_ENABLE -#define SND_DEFAULT_ENABLE 1 -#endif - -#ifndef MAX_REALTIME_FACTOR -#define MAX_REALTIME_FACTOR 4 -#endif - -/* - * Use always 64k buffer size. There is no reason to use shorter. - */ -#undef DSP_BUFFSIZE -#define DSP_BUFFSIZE (64*1024) - -#ifndef DSP_BUFFCOUNT -#define DSP_BUFFCOUNT 1 /* 1 is recommended. */ -#endif - -#define FM_MONO 0x388 /* This is the I/O address used by AdLib */ - -#ifndef CONFIG_PAS_BASE -#define CONFIG_PAS_BASE 0x388 -#endif - -/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the - driver. (There is no need to alter this) */ -#define SEQ_MAX_QUEUE 1024 - -#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */ -/* 128 instruments for general MIDI setup and 16 unassigned */ - -#define SND_NDEVS 256 /* Number of supported devices */ - -#define DSP_DEFAULT_SPEED 8000 - -#define MAX_AUDIO_DEV 5 -#define MAX_MIXER_DEV 5 -#define MAX_SYNTH_DEV 5 -#define MAX_MIDI_DEV 6 -#define MAX_TIMER_DEV 4 - -struct address_info { - int io_base; - int irq; - int dma; - int dma2; - int always_detect; /* 1=Trust me, it's there */ - char *name; - int driver_use_1; /* Driver defined field 1 */ - int driver_use_2; /* Driver defined field 2 */ - int *osp; /* OS specific info */ - int card_subtype; /* Driver specific. Usually 0 */ - void *memptr; /* Module memory chainer */ - int slots[6]; /* To remember driver slot ids */ -}; - -#define SYNTH_MAX_VOICES 32 - -struct voice_alloc_info { - int max_voice; - int used_voices; - int ptr; /* For device specific use */ - unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */ - int timestamp; - int alloc_times[SYNTH_MAX_VOICES]; - }; - -struct channel_info { - int pgm_num; - int bender_value; - int bender_range; - unsigned char controllers[128]; - }; - -/* - * Process wakeup reasons - */ -#define WK_NONE 0x00 -#define WK_WAKEUP 0x01 -#define WK_TIMEOUT 0x02 -#define WK_SIGNAL 0x04 -#define WK_SLEEP 0x08 -#define WK_SELECT 0x10 -#define WK_ABORT 0x20 - -#define OPEN_READ PCM_ENABLE_INPUT -#define OPEN_WRITE PCM_ENABLE_OUTPUT -#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE) - -#if OPEN_READ == FMODE_READ && OPEN_WRITE == FMODE_WRITE - -extern __inline__ int translate_mode(struct file *file) -{ - return file->f_mode; -} - -#else - -extern __inline__ int translate_mode(struct file *file) -{ - return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) | - ((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0); -} - -#endif - - -#include "sound_calls.h" -#include "dev_table.h" - -#ifndef DEB -#define DEB(x) -#endif - -#ifndef DDB -#define DDB(x) {} -#endif - -#ifndef MDB -#ifdef MODULE -#define MDB(x) x -#else -#define MDB(x) -#endif -#endif - -#define TIMER_ARMED 121234 -#define TIMER_NOT_ARMED 1 - -#endif diff -Nru a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c --- a/drivers/sound/sound_core.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,571 +0,0 @@ -/* - * Sound core handling. Breaks out sound functions to submodules - * - * Author: Alan Cox - * - * Fixes: - * - * - * 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. - * - * -------------------- - * - * Top level handler for the sound subsystem. Various devices can - * plug into this. The fact they dont all go via OSS doesn't mean - * they don't have to implement the OSS API. There is a lot of logic - * to keeping much of the OSS weight out of the code in a compatibility - * module, but its up to the driver to rember to load it... - * - * The code provides a set of functions for registration of devices - * by type. This is done rather than providing a single call so that - * we can hide any future changes in the internals (eg when we go to - * 32bit dev_t) from the modules and their interface. - * - * Secondly we need to allocate the dsp, dsp16 and audio devices as - * one. Thus we misuse the chains a bit to simplify this. - * - * Thirdly to make it more fun and for 2.3.x and above we do all - * of this using fine grained locking. - * - * FIXME: we have to resolve modules and fine grained load/unload - * locking at some point in 2.3.x. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SOUND_STEP 16 - - -struct sound_unit -{ - int unit_minor; - struct file_operations *unit_fops; - struct sound_unit *next; - devfs_handle_t de; -}; - -#ifdef CONFIG_SOUND_MSNDCLAS -extern int msnd_classic_init(void); -#endif -#ifdef CONFIG_SOUND_MSNDPIN -extern int msnd_pinnacle_init(void); -#endif - -/* - * Low level list operator. Scan the ordered list, find a hole and - * join into it. Called with the lock asserted - */ - -static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, struct file_operations *fops, int index, int low, int top) -{ - int n=low; - - if (index < 0) { /* first free */ - - while (*list && (*list)->unit_minornext); - - while(nunit_minor>n) - break; - list=&((*list)->next); - n+=SOUND_STEP; - } - - if(n>=top) - return -ENOENT; - } else { - n = low+(index*16); - while (*list) { - if ((*list)->unit_minor==n) - return -EBUSY; - if ((*list)->unit_minor>n) - break; - list=&((*list)->next); - } - } - - /* - * Fill it in - */ - - s->unit_minor=n; - s->unit_fops=fops; - - /* - * Link it - */ - - s->next=*list; - *list=s; - - - return n; -} - -/* - * Remove a node from the chain. Called with the lock asserted - */ - -static void __sound_remove_unit(struct sound_unit **list, int unit) -{ - while(*list) - { - struct sound_unit *p=*list; - if(p->unit_minor==unit) - { - *list=p->next; - devfs_unregister (p->de); - kfree(p); - return; - } - list=&(p->next); - } - printk(KERN_ERR "Sound device %d went missing!\n", unit); -} - -/* - * This lock guards the sound loader list. - */ - -static spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED; - -/* - * Allocate the controlling structure and add it to the sound driver - * list. Acquires locks as needed - */ - -static devfs_handle_t devfs_handle; - -static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode) -{ - int r; - struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL); - char name_buf[16]; - - if(s==NULL) - return -ENOMEM; - - spin_lock(&sound_loader_lock); - r=__sound_insert_unit(s,list,fops,index,low,top); - spin_unlock(&sound_loader_lock); - - if(r<0) - { - kfree(s); - return r; - } - - if (r == low) - sprintf (name_buf, "%s", name); - else - sprintf (name_buf, "%s%d", name, (r - low) / SOUND_STEP); - s->de = devfs_register (devfs_handle, name_buf, - DEVFS_FL_NONE, SOUND_MAJOR, s->unit_minor, - S_IFCHR | mode, fops, NULL); - return r; -} - -/* - * Remove a unit. Acquires locks as needed. The drivers MUST have - * completed the removal before their file operations become - * invalid. - */ - -static void sound_remove_unit(struct sound_unit **list, int unit) -{ - spin_lock(&sound_loader_lock); - __sound_remove_unit(list, unit); - spin_unlock(&sound_loader_lock); -} - -/* - * Allocations - * - * 0 *16 Mixers - * 1 *8 Sequencers - * 2 *16 Midi - * 3 *16 DSP - * 4 *16 SunDSP - * 5 *16 DSP16 - * 6 -- sndstat (obsolete) - * 7 *16 unused - * 8 -- alternate sequencer (see above) - * 9 *16 raw synthesizer access - * 10 *16 unused - * 11 *16 unused - * 12 *16 unused - * 13 *16 unused - * 14 *16 unused - * 15 *16 unused - */ - -static struct sound_unit *chains[16]; - -/** - * register_sound_special - register a special sound node - * @fops: File operations for the driver - * @unit: Unit number to allocate - * - * Allocate a special sound device by minor number from the sound - * subsystem. The allocated number is returned on succes. On failure - * a negative error code is returned. - */ - -int register_sound_special(struct file_operations *fops, int unit) -{ - char *name; - - switch (unit) { - case 0: - name = "mixer"; - break; - case 1: - name = "sequencer"; - break; - case 2: - name = "midi00"; - break; - case 3: - name = "dsp"; - break; - case 4: - name = "audio"; - break; - case 5: - name = "unknown5"; - break; - case 6: /* Was once sndstat */ - name = "unknown6"; - break; - case 7: - name = "unknown7"; - break; - case 8: - name = "sequencer2"; - break; - case 9: - name = "dmmidi"; - break; - case 10: - name = "dmfm"; - break; - case 11: - name = "unknown11"; - break; - case 12: - name = "adsp"; - break; - case 13: - name = "amidi"; - break; - case 14: - name = "admmidi"; - break; - default: - name = "unknown"; - break; - } - return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1, - name, S_IRUSR | S_IWUSR); -} - -EXPORT_SYMBOL(register_sound_special); - -/** - * register_sound_mixer - register a mixer device - * @fops: File operations for the driver - * @dev: Unit number to allocate - * - * Allocate a mixer device. Unit is the number of the mixer requested. - * Pass -1 to request the next free mixer unit. On success the allocated - * number is returned, on failure a negative error code is returned. - */ - -int register_sound_mixer(struct file_operations *fops, int dev) -{ - return sound_insert_unit(&chains[0], fops, dev, 0, 128, - "mixer", S_IRUSR | S_IWUSR); -} - -EXPORT_SYMBOL(register_sound_mixer); - -/** - * register_sound_midi - register a midi device - * @fops: File operations for the driver - * @dev: Unit number to allocate - * - * Allocate a midi device. Unit is the number of the midi device requested. - * Pass -1 to request the next free midi unit. On success the allocated - * number is returned, on failure a negative error code is returned. - */ - -int register_sound_midi(struct file_operations *fops, int dev) -{ - return sound_insert_unit(&chains[2], fops, dev, 2, 130, - "midi", S_IRUSR | S_IWUSR); -} - -EXPORT_SYMBOL(register_sound_midi); - -/* - * DSP's are registered as a triple. Register only one and cheat - * in open - see below. - */ - -/** - * register_sound_dsp - register a DSP device - * @fops: File operations for the driver - * @dev: Unit number to allocate - * - * Allocate a DSP device. Unit is the number of the DSP requested. - * Pass -1 to request the next free DSP unit. On success the allocated - * number is returned, on failure a negative error code is returned. - * - * This function allocates both the audio and dsp device entries together - * and will always allocate them as a matching pair - eg dsp3/audio3 - */ - -int register_sound_dsp(struct file_operations *fops, int dev) -{ - return sound_insert_unit(&chains[3], fops, dev, 3, 131, - "dsp", S_IWUSR | S_IRUSR); -} - -EXPORT_SYMBOL(register_sound_dsp); - -/** - * register_sound_synth - register a synth device - * @fops: File operations for the driver - * @dev: Unit number to allocate - * - * Allocate a synth device. Unit is the number of the synth device requested. - * Pass -1 to request the next free synth unit. On success the allocated - * number is returned, on failure a negative error code is returned. - */ - - -int register_sound_synth(struct file_operations *fops, int dev) -{ - return sound_insert_unit(&chains[9], fops, dev, 9, 137, - "synth", S_IRUSR | S_IWUSR); -} - -EXPORT_SYMBOL(register_sound_synth); - -/** - * unregister_sound_special - unregister a special sound device - * @unit: unit number to allocate - * - * Release a sound device that was allocated with - * register_sound_special(). The unit passed is the return value from - * the register function. - */ - - -void unregister_sound_special(int unit) -{ - sound_remove_unit(&chains[unit&15], unit); -} - -EXPORT_SYMBOL(unregister_sound_special); - -/** - * unregister_sound_mixer - unregister a mixer - * @unit: unit number to allocate - * - * Release a sound device that was allocated with register_sound_mixer(). - * The unit passed is the return value from the register function. - */ - -void unregister_sound_mixer(int unit) -{ - sound_remove_unit(&chains[0], unit); -} - -EXPORT_SYMBOL(unregister_sound_mixer); - -/** - * unregister_sound_midi - unregister a midi device - * @unit: unit number to allocate - * - * Release a sound device that was allocated with register_sound_midi(). - * The unit passed is the return value from the register function. - */ - -void unregister_sound_midi(int unit) -{ - return sound_remove_unit(&chains[2], unit); -} - -EXPORT_SYMBOL(unregister_sound_midi); - -/** - * unregister_sound_dsp - unregister a DSP device - * @unit: unit number to allocate - * - * Release a sound device that was allocated with register_sound_dsp(). - * The unit passed is the return value from the register function. - * - * Both of the allocated units are released together automatically. - */ - -void unregister_sound_dsp(int unit) -{ - return sound_remove_unit(&chains[3], unit); -} - - -EXPORT_SYMBOL(unregister_sound_dsp); - -/** - * unregister_sound_synth - unregister a synth device - * @unit: unit number to allocate - * - * Release a sound device that was allocated with register_sound_synth(). - * The unit passed is the return value from the register function. - */ - -void unregister_sound_synth(int unit) -{ - return sound_remove_unit(&chains[9], unit); -} - -EXPORT_SYMBOL(unregister_sound_synth); - -/* - * Now our file operations - */ - -static int soundcore_open(struct inode *, struct file *); - -static struct file_operations soundcore_fops= -{ - /* We must have an owner or the module locking fails */ - owner: THIS_MODULE, - open: soundcore_open, -}; - -static struct sound_unit *__look_for_unit(int chain, int unit) -{ - struct sound_unit *s; - - s=chains[chain]; - while(s && s->unit_minor <= unit) - { - if(s->unit_minor==unit) - return s; - s=s->next; - } - return NULL; -} - -int soundcore_open(struct inode *inode, struct file *file) -{ - int chain; - int unit = minor(inode->i_rdev); - struct sound_unit *s; - struct file_operations *new_fops = NULL; - - chain=unit&0x0F; - if(chain==4 || chain==5) /* dsp/audio/dsp16 */ - { - unit&=0xF0; - unit|=3; - chain=3; - } - - spin_lock(&sound_loader_lock); - s = __look_for_unit(chain, unit); - if (s) - new_fops = fops_get(s->unit_fops); - if (!new_fops) { - char mod[32]; - - spin_unlock(&sound_loader_lock); - /* - * Please, don't change this order or code. - * For ALSA slot means soundcard and OSS emulation code - * comes as add-on modules which aren't depend on - * ALSA toplevel modules for soundcards, thus we need - * load them at first. [Jaroslav Kysela ] - */ - sprintf(mod, "sound-slot-%i", unit>>4); - request_module(mod); - sprintf(mod, "sound-service-%i-%i", unit>>4, chain); - request_module(mod); - spin_lock(&sound_loader_lock); - s = __look_for_unit(chain, unit); - if (s) - new_fops = fops_get(s->unit_fops); - } - if (new_fops) { - /* - * We rely upon the fact that we can't be unloaded while the - * subdriver is there, so if ->open() is successful we can - * safely drop the reference counter and if it is not we can - * revert to old ->f_op. Ugly, indeed, but that's the cost of - * switching ->f_op in the first place. - */ - int err = 0; - struct file_operations *old_fops = file->f_op; - file->f_op = new_fops; - spin_unlock(&sound_loader_lock); - if(file->f_op->open) - err = file->f_op->open(inode,file); - if (err) { - fops_put(file->f_op); - file->f_op = fops_get(old_fops); - } - fops_put(old_fops); - return err; - } - spin_unlock(&sound_loader_lock); - return -ENODEV; -} - -extern int mod_firmware_load(const char *, char **); -EXPORT_SYMBOL(mod_firmware_load); - - -MODULE_DESCRIPTION("Core sound module"); -MODULE_AUTHOR("Alan Cox"); -MODULE_LICENSE("GPL"); - -static void __exit cleanup_soundcore(void) -{ - /* We have nothing to really do here - we know the lists must be - empty */ - devfs_unregister_chrdev(SOUND_MAJOR, "sound"); - devfs_unregister (devfs_handle); -} - -static int __init init_soundcore(void) -{ - if(devfs_register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) - { - printk(KERN_ERR "soundcore: sound device already in use.\n"); - return -EBUSY; - } - devfs_handle = devfs_mk_dir (NULL, "sound", NULL); - - return 0; -} - -module_init(init_soundcore); -module_exit(cleanup_soundcore); diff -Nru a/drivers/sound/sound_firmware.c b/drivers/sound/sound_firmware.c --- a/drivers/sound/sound_firmware.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,78 +0,0 @@ -#include "os.h" -#define __KERNEL_SYSCALLS__ -#include -#include -#include -#include -#include -#include - -static int errno; -static int do_mod_firmware_load(const char *fn, char **fp) -{ - int fd; - long l; - char *dp; - - fd = open(fn, 0, 0); - if (fd == -1) - { - printk(KERN_INFO "Unable to load '%s'.\n", fn); - return 0; - } - l = lseek(fd, 0L, 2); - if (l <= 0 || l > 131072) - { - printk(KERN_INFO "Invalid firmware '%s'\n", fn); - sys_close(fd); - return 0; - } - lseek(fd, 0L, 0); - dp = vmalloc(l); - if (dp == NULL) - { - printk(KERN_INFO "Out of memory loading '%s'.\n", fn); - sys_close(fd); - return 0; - } - if (read(fd, dp, l) != l) - { - printk(KERN_INFO "Failed to read '%s'.\n", fn); - vfree(dp); - sys_close(fd); - return 0; - } - close(fd); - *fp = dp; - return (int) l; -} - -/** - * mod_firmware_load - load sound driver firmware - * @fn: filename - * @fp: return for the buffer. - * - * Load the firmware for a sound module (up to 128K) into a buffer. - * The buffer is returned in *fp. It is allocated with vmalloc so is - * virtually linear and not DMAable. The caller should free it with - * vfree when finished. - * - * The length of the buffer is returned on a successful load, the - * value zero on a failure. - * - * Caution: This API is not recommended. Firmware should be loaded via - * an ioctl call and a setup application. This function may disappear - * in future. - */ - -int mod_firmware_load(const char *fn, char **fp) -{ - int r; - mm_segment_t fs = get_fs(); - - set_fs(get_ds()); - r = do_mod_firmware_load(fn, fp); - set_fs(fs); - return r; -} - diff -Nru a/drivers/sound/sound_firmware.h b/drivers/sound/sound_firmware.h --- a/drivers/sound/sound_firmware.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2 +0,0 @@ -extern int mod_firmware_load(const char *fn, char **fp); - diff -Nru a/drivers/sound/sound_syms.c b/drivers/sound/sound_syms.c --- a/drivers/sound/sound_syms.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,53 +0,0 @@ -/* - * The sound core exports the following symbols to the rest of - * modulespace. - * - * (C) Copyright 1997 Alan Cox, Licensed under the GNU GPL - * - * Thu May 27 1999 Andrew J. Kroll - * left out exported symbol... fixed - */ - -#include -#include "sound_config.h" -#include "sound_calls.h" - -char sound_syms_symbol; - -EXPORT_SYMBOL(mixer_devs); -EXPORT_SYMBOL(audio_devs); -EXPORT_SYMBOL(num_mixers); -EXPORT_SYMBOL(num_audiodevs); - -EXPORT_SYMBOL(midi_devs); -EXPORT_SYMBOL(num_midis); -EXPORT_SYMBOL(synth_devs); -EXPORT_SYMBOL(num_synths); - -EXPORT_SYMBOL(sound_timer_devs); -EXPORT_SYMBOL(num_sound_timers); - -EXPORT_SYMBOL(sound_install_audiodrv); -EXPORT_SYMBOL(sound_install_mixer); -EXPORT_SYMBOL(sound_alloc_dma); -EXPORT_SYMBOL(sound_free_dma); -EXPORT_SYMBOL(sound_open_dma); -EXPORT_SYMBOL(sound_close_dma); -EXPORT_SYMBOL(sound_alloc_audiodev); -EXPORT_SYMBOL(sound_alloc_mididev); -EXPORT_SYMBOL(sound_alloc_mixerdev); -EXPORT_SYMBOL(sound_alloc_timerdev); -EXPORT_SYMBOL(sound_alloc_synthdev); -EXPORT_SYMBOL(sound_unload_audiodev); -EXPORT_SYMBOL(sound_unload_mididev); -EXPORT_SYMBOL(sound_unload_mixerdev); -EXPORT_SYMBOL(sound_unload_timerdev); -EXPORT_SYMBOL(sound_unload_synthdev); - -EXPORT_SYMBOL(load_mixer_volumes); - -EXPORT_SYMBOL(conf_printf); -EXPORT_SYMBOL(conf_printf2); - -MODULE_DESCRIPTION("OSS Sound subsystem"); -MODULE_AUTHOR("Hannu Savolainen, et al."); diff -Nru a/drivers/sound/sound_timer.c b/drivers/sound/sound_timer.c --- a/drivers/sound/sound_timer.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,318 +0,0 @@ -/* - * sound/sound_timer.c - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - */ -#include - - -#include "sound_config.h" - -static volatile int initialized, opened, tmr_running; -static volatile time_t tmr_offs, tmr_ctr; -static volatile unsigned long ticks_offs; -static volatile int curr_tempo, curr_timebase; -static volatile unsigned long curr_ticks; -static volatile unsigned long next_event_time; -static unsigned long prev_event_time; -static volatile unsigned long usecs_per_tmr; /* Length of the current interval */ - -static struct sound_lowlev_timer *tmr; - -static unsigned long tmr2ticks(int tmr_value) -{ - /* - * Convert timer ticks to MIDI ticks - */ - - unsigned long tmp; - unsigned long scale; - - tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */ - scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ - return (tmp + (scale / 2)) / scale; -} - -void reprogram_timer(void) -{ - unsigned long usecs_per_tick; - - /* - * The user is changing the timer rate before setting a timer - * slap, bad bad not allowed. - */ - - if(!tmr) - return; - - usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase); - - /* - * Don't kill the system by setting too high timer rate - */ - if (usecs_per_tick < 2000) - usecs_per_tick = 2000; - - usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick); -} - -void sound_timer_syncinterval(unsigned int new_usecs) -{ - /* - * This routine is called by the hardware level if - * the clock frequency has changed for some reason. - */ - tmr_offs = tmr_ctr; - ticks_offs += tmr2ticks(tmr_ctr); - tmr_ctr = 0; - usecs_per_tmr = new_usecs; -} - -static void tmr_reset(void) -{ - unsigned long flags; - - save_flags(flags); - cli(); - tmr_offs = 0; - ticks_offs = 0; - tmr_ctr = 0; - next_event_time = (unsigned long) -1; - prev_event_time = 0; - curr_ticks = 0; - restore_flags(flags); -} - -static int timer_open(int dev, int mode) -{ - if (opened) - return -EBUSY; - tmr_reset(); - curr_tempo = 60; - curr_timebase = 100; - opened = 1; - reprogram_timer(); - return 0; -} - -static void timer_close(int dev) -{ - opened = tmr_running = 0; - tmr->tmr_disable(tmr->dev); -} - -static int timer_event(int dev, unsigned char *event) -{ - unsigned char cmd = event[1]; - unsigned long parm = *(int *) &event[4]; - - switch (cmd) - { - case TMR_WAIT_REL: - parm += prev_event_time; - case TMR_WAIT_ABS: - if (parm > 0) - { - long time; - - if (parm <= curr_ticks) /* It's the time */ - return TIMER_NOT_ARMED; - time = parm; - next_event_time = prev_event_time = time; - return TIMER_ARMED; - } - break; - - case TMR_START: - tmr_reset(); - tmr_running = 1; - reprogram_timer(); - break; - - case TMR_STOP: - tmr_running = 0; - break; - - case TMR_CONTINUE: - tmr_running = 1; - reprogram_timer(); - break; - - case TMR_TEMPO: - if (parm) - { - if (parm < 8) - parm = 8; - if (parm > 250) - parm = 250; - tmr_offs = tmr_ctr; - ticks_offs += tmr2ticks(tmr_ctr); - tmr_ctr = 0; - curr_tempo = parm; - reprogram_timer(); - } - break; - - case TMR_ECHO: - seq_copy_to_input(event, 8); - break; - - default:; - } - return TIMER_NOT_ARMED; -} - -static unsigned long timer_get_time(int dev) -{ - if (!opened) - return 0; - return curr_ticks; -} - -static int timer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int val; - - switch (cmd) - { - case SNDCTL_TMR_SOURCE: - val = TMR_INTERNAL; - break; - - case SNDCTL_TMR_START: - tmr_reset(); - tmr_running = 1; - return 0; - - case SNDCTL_TMR_STOP: - tmr_running = 0; - return 0; - - case SNDCTL_TMR_CONTINUE: - tmr_running = 1; - return 0; - - case SNDCTL_TMR_TIMEBASE: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val) - { - if (val < 1) - val = 1; - if (val > 1000) - val = 1000; - curr_timebase = val; - } - val = curr_timebase; - break; - - case SNDCTL_TMR_TEMPO: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val) - { - if (val < 8) - val = 8; - if (val > 250) - val = 250; - tmr_offs = tmr_ctr; - ticks_offs += tmr2ticks(tmr_ctr); - tmr_ctr = 0; - curr_tempo = val; - reprogram_timer(); - } - val = curr_tempo; - break; - - case SNDCTL_SEQ_CTRLRATE: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) /* Can't change */ - return -EINVAL; - val = ((curr_tempo * curr_timebase) + 30) / 60; - break; - - case SNDCTL_SEQ_GETTIME: - val = curr_ticks; - break; - - case SNDCTL_TMR_METRONOME: - default: - return -EINVAL; - } - return put_user(val, (int *)arg); -} - -static void timer_arm(int dev, long time) -{ - if (time < 0) - time = curr_ticks + 1; - else if (time <= curr_ticks) /* It's the time */ - return; - - next_event_time = prev_event_time = time; - return; -} - -static struct sound_timer_operations sound_timer = -{ - owner: THIS_MODULE, - info: {"Sound Timer", 0}, - priority: 1, /* Priority */ - devlink: 0, /* Local device link */ - open: timer_open, - close: timer_close, - event: timer_event, - get_time: timer_get_time, - ioctl: timer_ioctl, - arm_timer: timer_arm -}; - -void sound_timer_interrupt(void) -{ - if (!opened) - return; - - tmr->tmr_restart(tmr->dev); - - if (!tmr_running) - return; - - tmr_ctr++; - curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); - - if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer(0); - } -} - -void sound_timer_init(struct sound_lowlev_timer *t, char *name) -{ - int n; - - if (initialized) - { - if (t->priority <= tmr->priority) - return; /* There is already a similar or better timer */ - tmr = t; - return; - } - initialized = 1; - tmr = t; - - n = sound_alloc_timerdev(); - if (n == -1) - n = 0; /* Overwrite the system timer */ - strcpy(sound_timer.info.name, name); - sound_timer_devs[n] = &sound_timer; -} diff -Nru a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c --- a/drivers/sound/soundcard.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,782 +0,0 @@ -/* - * linux/drivers/sound/soundcard.c - * - * Sound card driver for Linux - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * integrated sound_switch.c - * Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat, - * which should disappear in the near future) - * Eric Dumas : devfs support (22-Jan-98) with - * fixups by C. Scott Ananian - * Richard Gooch : moved common (non OSS-specific) devices to sound_core.c - * Rob Riggs : Added persistent DMA buffers support (1998/10/17) - * Christoph Hellwig : Some cleanup work (2000/03/01) - */ - -#include - -#include "sound_config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This ought to be moved into include/asm/dma.h - */ -#ifndef valid_dma -#define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4) -#endif - -/* - * Table for permanently allocated memory (used when unloading the module) - */ -caddr_t sound_mem_blocks[1024]; -int sound_nblocks = 0; - -/* Persistent DMA buffers */ -#ifdef CONFIG_SOUND_DMAP -int sound_dmap_flag = 1; -#else -int sound_dmap_flag = 0; -#endif - -static char dma_alloc_map[MAX_DMA_CHANNELS] = {0}; - -#define DMA_MAP_UNAVAIL 0 -#define DMA_MAP_FREE 1 -#define DMA_MAP_BUSY 2 - - -unsigned long seq_time = 0; /* Time for /dev/sequencer */ - -/* - * Table for configurable mixer volume handling - */ -static mixer_vol_table mixer_vols[MAX_MIXER_DEV]; -static int num_mixer_volumes = 0; - -int *load_mixer_volumes(char *name, int *levels, int present) -{ - int i, n; - - for (i = 0; i < num_mixer_volumes; i++) { - if (strcmp(name, mixer_vols[i].name) == 0) { - if (present) - mixer_vols[i].num = i; - return mixer_vols[i].levels; - } - } - if (num_mixer_volumes >= MAX_MIXER_DEV) { - printk(KERN_ERR "Sound: Too many mixers (%s)\n", name); - return levels; - } - n = num_mixer_volumes++; - - strcpy(mixer_vols[n].name, name); - - if (present) - mixer_vols[n].num = n; - else - mixer_vols[n].num = -1; - - for (i = 0; i < 32; i++) - mixer_vols[n].levels[i] = levels[i]; - return mixer_vols[n].levels; -} - -static int set_mixer_levels(caddr_t arg) -{ - /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */ - mixer_vol_table buf; - - if (__copy_from_user(&buf, arg, sizeof(buf))) - return -EFAULT; - load_mixer_volumes(buf.name, buf.levels, 0); - if (__copy_to_user(arg, &buf, sizeof(buf))) - return -EFAULT; - return 0; -} - -static int get_mixer_levels(caddr_t arg) -{ - int n; - - if (__get_user(n, (int *)(&(((mixer_vol_table *)arg)->num)))) - return -EFAULT; - if (n < 0 || n >= num_mixer_volumes) - return -EINVAL; - if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table))) - return -EFAULT; - return 0; -} - -#ifndef MIN -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -/* 4K page size but our output routines use some slack for overruns */ -#define PROC_BLOCK_SIZE (3*1024) - -static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - int dev = minor(file->f_dentry->d_inode->i_rdev); - int ret = -EINVAL; - - /* - * The OSS drivers aren't remotely happy without this locking, - * and unless someone fixes them when they are about to bite the - * big one anyway, we might as well bandage here.. - */ - - lock_kernel(); - - DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count)); - switch (dev & 0x0f) { - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - ret = audio_read(dev, file, buf, count); - break; - - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - ret = sequencer_read(dev, file, buf, count); - break; - - case SND_DEV_MIDIN: - ret = MIDIbuf_read(dev, file, buf, count); - } - unlock_kernel(); - return ret; -} - -static ssize_t sound_write(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - int dev = minor(file->f_dentry->d_inode->i_rdev); - int ret = -EINVAL; - - lock_kernel(); - DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count)); - switch (dev & 0x0f) { - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - ret = sequencer_write(dev, file, buf, count); - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - ret = audio_write(dev, file, buf, count); - break; - - case SND_DEV_MIDIN: - ret = MIDIbuf_write(dev, file, buf, count); - break; - } - unlock_kernel(); - return ret; -} - -static int sound_open(struct inode *inode, struct file *file) -{ - int dev = minor(inode->i_rdev); - int retval; - - DEB(printk("sound_open(dev=%d)\n", dev)); - if ((dev >= SND_NDEVS) || (dev < 0)) { - printk(KERN_ERR "Invalid minor device %d\n", dev); - return -ENXIO; - } - switch (dev & 0x0f) { - case SND_DEV_CTL: - dev >>= 4; - if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { - char modname[20]; - sprintf(modname, "mixer%d", dev); - request_module(modname); - } - if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL)) - return -ENXIO; - - if (mixer_devs[dev]->owner) - __MOD_INC_USE_COUNT (mixer_devs[dev]->owner); - break; - - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - if ((retval = sequencer_open(dev, file)) < 0) - return retval; - break; - - case SND_DEV_MIDIN: - if ((retval = MIDIbuf_open(dev, file)) < 0) - return retval; - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - if ((retval = audio_open(dev, file)) < 0) - return retval; - break; - - default: - printk(KERN_ERR "Invalid minor device %d\n", dev); - return -ENXIO; - } - - return 0; -} - -static int sound_release(struct inode *inode, struct file *file) -{ - int dev = minor(inode->i_rdev); - - lock_kernel(); - DEB(printk("sound_release(dev=%d)\n", dev)); - switch (dev & 0x0f) { - case SND_DEV_CTL: - dev >>= 4; - if (mixer_devs[dev]->owner) - __MOD_DEC_USE_COUNT (mixer_devs[dev]->owner); - break; - - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - sequencer_release(dev, file); - break; - - case SND_DEV_MIDIN: - MIDIbuf_release(dev, file); - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - audio_release(dev, file); - break; - - default: - printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev); - } - unlock_kernel(); - - return 0; -} - -static int get_mixer_info(int dev, caddr_t arg) -{ - mixer_info info; - - strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); - strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); - info.name[sizeof(info.name)-1] = 0; - info.modify_counter = mixer_devs[dev]->modify_counter; - if (__copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - return 0; -} - -static int get_old_mixer_info(int dev, caddr_t arg) -{ - _old_mixer_info info; - - strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); - strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); - info.name[sizeof(info.name)-1] = 0; - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - return 0; -} - -static int sound_mixer_ioctl(int mixdev, unsigned int cmd, caddr_t arg) -{ - if (mixdev < 0 || mixdev >= MAX_MIXER_DEV) - return -ENXIO; - /* Try to load the mixer... */ - if (mixer_devs[mixdev] == NULL) { - char modname[20]; - sprintf(modname, "mixer%d", mixdev); - request_module(modname); - } - if (mixdev >= num_mixers || !mixer_devs[mixdev]) - return -ENXIO; - if (cmd == SOUND_MIXER_INFO) - return get_mixer_info(mixdev, arg); - if (cmd == SOUND_OLD_MIXER_INFO) - return get_old_mixer_info(mixdev, arg); - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - mixer_devs[mixdev]->modify_counter++; - if (!mixer_devs[mixdev]->ioctl) - return -EINVAL; - return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg); -} - -static int sound_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int err, len = 0, dtype; - int dev = minor(inode->i_rdev); - - if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) { - /* - * Have to validate the address given by the process. - */ - len = _SIOC_SIZE(cmd); - if (len < 1 || len > 65536 || arg == 0) - return -EFAULT; - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - if ((err = verify_area(VERIFY_READ, (void *)arg, len)) < 0) - return err; - if (_SIOC_DIR(cmd) & _SIOC_READ) - if ((err = verify_area(VERIFY_WRITE, (void *)arg, len)) < 0) - return err; - } - DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); - if (cmd == OSS_GETVERSION) - return __put_user(SOUND_VERSION, (int *)arg); - - if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */ - (dev & 0x0f) != SND_DEV_CTL) { - dtype = dev & 0x0f; - switch (dtype) { - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - return sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev, - cmd, (caddr_t)arg); - - default: - return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg); - } - } - switch (dev & 0x0f) { - case SND_DEV_CTL: - if (cmd == SOUND_MIXER_GETLEVELS) - return get_mixer_levels((caddr_t)arg); - if (cmd == SOUND_MIXER_SETLEVELS) - return set_mixer_levels((caddr_t)arg); - return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg); - - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - return sequencer_ioctl(dev, file, cmd, (caddr_t)arg); - - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - return audio_ioctl(dev, file, cmd, (caddr_t)arg); - break; - - case SND_DEV_MIDIN: - return MIDIbuf_ioctl(dev, file, cmd, (caddr_t)arg); - break; - - } - return -EINVAL; -} - -static unsigned int sound_poll(struct file *file, poll_table * wait) -{ - struct inode *inode = file->f_dentry->d_inode; - int dev = minor(inode->i_rdev); - - DEB(printk("sound_poll(dev=%d)\n", dev)); - switch (dev & 0x0f) { - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - return sequencer_poll(dev, file, wait); - - case SND_DEV_MIDIN: - return MIDIbuf_poll(dev, file, wait); - - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - return DMAbuf_poll(file, dev >> 4, wait); - } - return 0; -} - -static int sound_mmap(struct file *file, struct vm_area_struct *vma) -{ - int dev_class; - unsigned long size; - struct dma_buffparms *dmap = NULL; - int dev = minor(file->f_dentry->d_inode->i_rdev); - - dev_class = dev & 0x0f; - dev >>= 4; - - if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) { - printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n"); - return -EINVAL; - } - lock_kernel(); - if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */ - dmap = audio_devs[dev]->dmap_out; - else if (vma->vm_flags & VM_READ) - dmap = audio_devs[dev]->dmap_in; - else { - printk(KERN_ERR "Sound: Undefined mmap() access\n"); - unlock_kernel(); - return -EINVAL; - } - - if (dmap == NULL) { - printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n"); - unlock_kernel(); - return -EIO; - } - if (dmap->raw_buf == NULL) { - printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n"); - unlock_kernel(); - return -EIO; - } - if (dmap->mapping_flags) { - printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n"); - unlock_kernel(); - return -EIO; - } - if (vma->vm_pgoff != 0) { - printk(KERN_ERR "Sound: mmap() offset must be 0.\n"); - unlock_kernel(); - return -EINVAL; - } - size = vma->vm_end - vma->vm_start; - - if (size != dmap->bytes_in_use) { - printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use); - } - if (remap_page_range(vma, vma->vm_start, virt_to_phys(dmap->raw_buf), - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) { - unlock_kernel(); - return -EAGAIN; - } - - dmap->mapping_flags |= DMA_MAP_MAPPED; - - if( audio_devs[dev]->d->mmap) - audio_devs[dev]->d->mmap(dev); - - memset(dmap->raw_buf, - dmap->neutral_byte, - dmap->bytes_in_use); - unlock_kernel(); - return 0; -} - -struct file_operations oss_sound_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: sound_read, - write: sound_write, - poll: sound_poll, - ioctl: sound_ioctl, - mmap: sound_mmap, - open: sound_open, - release: sound_release, -}; - -/* - * Create the required special subdevices - */ - -static int create_special_devices(void) -{ - int seq1,seq2; - seq1=register_sound_special(&oss_sound_fops, 1); - if(seq1==-1) - goto bad; - seq2=register_sound_special(&oss_sound_fops, 8); - if(seq2!=-1) - return 0; - unregister_sound_special(1); -bad: - return -1; -} - - -/* These device names follow the official Linux device list, - * Documentation/devices.txt. Let us know if there are other - * common names we should support for compatibility. - * Only those devices not created by the generic code in sound_core.c are - * registered here. - */ -static const struct { - unsigned short minor; - char *name; - umode_t mode; - int *num; -} dev_list[] = { /* list of minor devices */ -/* seems to be some confusion here -- this device is not in the device list */ - {SND_DEV_DSP16, "dspW", S_IWUGO | S_IRUSR | S_IRGRP, - &num_audiodevs}, - {SND_DEV_AUDIO, "audio", S_IWUGO | S_IRUSR | S_IRGRP, - &num_audiodevs}, -}; - -static char * -soundcard_make_name(char *buf, char *name, int idx) { - if (idx==0) - sprintf(buf, "sound/%s", name); - else - sprintf(buf, "sound/%s%d", name, idx); - return buf; -} - -/* Register/unregister audio entries */ -static void soundcard_register_devfs (int do_register) -{ - char name_buf[32]; - int i, j, num; - - for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) { - num = (dev_list[i].num == NULL) ? 0 : *dev_list[i].num; - for (j = 0; j < num || j == 0; j++) { - soundcard_make_name (name_buf, dev_list[i].name, j); - if (do_register) - devfs_register (NULL, name_buf, DEVFS_FL_NONE, - SOUND_MAJOR, dev_list[i].minor+ (j* 0x10), - S_IFCHR | dev_list[i].mode, - &oss_sound_fops, NULL); - else { - devfs_handle_t de; - - de = devfs_find_handle (NULL, name_buf, 0, 0, - DEVFS_SPECIAL_CHR, 0); - devfs_unregister (de); - } - } - } -} - - -static int dmabuf = 0; -static int dmabug = 0; - -MODULE_PARM(dmabuf, "i"); -MODULE_PARM(dmabug, "i"); - -static int __init oss_init(void) -{ - int err; - - /* drag in sound_syms.o */ - { - extern char sound_syms_symbol; - sound_syms_symbol = 0; - } - -#ifdef CONFIG_PCI - if(dmabug) - isa_dma_bridge_buggy = dmabug; -#endif - - err = create_special_devices(); - if (err) { - printk(KERN_ERR "sound: driver already loaded/included in kernel\n"); - return err; - } - - /* Protecting the innocent */ - sound_dmap_flag = (dmabuf > 0 ? 1 : 0); - - soundcard_register_devfs(1); - - if (sound_nblocks >= 1024) - printk(KERN_ERR "Sound warning: Deallocation table was too small.\n"); - - return 0; -} - -static void __exit oss_cleanup(void) -{ - int i; - - if (MOD_IN_USE) - return; - - soundcard_register_devfs (0); - - unregister_sound_special(1); - unregister_sound_special(8); - - sound_stop_timer(); - - sequencer_unload(); - - for (i = 0; i < MAX_DMA_CHANNELS; i++) - if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) { - printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i); - sound_free_dma(i); - } - - for (i = 0; i < sound_nblocks; i++) - vfree(sound_mem_blocks[i]); - -} - -module_init(oss_init); -module_exit(oss_cleanup); -MODULE_LICENSE("GPL"); - - -int sound_alloc_dma(int chn, char *deviceID) -{ - int err; - - if ((err = request_dma(chn, deviceID)) != 0) - return err; - - dma_alloc_map[chn] = DMA_MAP_FREE; - - return 0; -} - -int sound_open_dma(int chn, char *deviceID) -{ - unsigned long flags; - - if (!valid_dma(chn)) { - printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn); - return 1; - } - save_flags(flags); - cli(); - - if (dma_alloc_map[chn] != DMA_MAP_FREE) { - printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]); - restore_flags(flags); - return 1; - } - dma_alloc_map[chn] = DMA_MAP_BUSY; - restore_flags(flags); - return 0; -} - -void sound_free_dma(int chn) -{ - if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) { - /* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */ - return; - } - free_dma(chn); - dma_alloc_map[chn] = DMA_MAP_UNAVAIL; -} - -void sound_close_dma(int chn) -{ - unsigned long flags; - - save_flags(flags); - cli(); - - if (dma_alloc_map[chn] != DMA_MAP_BUSY) { - printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn); - restore_flags(flags); - return; - } - dma_alloc_map[chn] = DMA_MAP_FREE; - restore_flags(flags); -} - -static void do_sequencer_timer(unsigned long dummy) -{ - sequencer_timer(0); -} - - -static struct timer_list seq_timer = -{function: do_sequencer_timer}; - -void request_sound_timer(int count) -{ - extern unsigned long seq_time; - - if (count < 0) { - seq_timer.expires = (-count) + jiffies; - add_timer(&seq_timer); - return; - } - count += seq_time; - - count -= jiffies; - - if (count < 1) - count = 1; - - seq_timer.expires = (count) + jiffies; - add_timer(&seq_timer); -} - -void sound_stop_timer(void) -{ - del_timer(&seq_timer);; -} - -void conf_printf(char *name, struct address_info *hw_config) -{ -#ifndef CONFIG_SOUND_TRACEINIT - return; -#else - printk("<%s> at 0x%03x", name, hw_config->io_base); - - if (hw_config->irq) - printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq); - - if (hw_config->dma != -1 || hw_config->dma2 != -1) - { - printk(" dma %d", hw_config->dma); - if (hw_config->dma2 != -1) - printk(",%d", hw_config->dma2); - } - printk("\n"); -#endif -} - -void conf_printf2(char *name, int base, int irq, int dma, int dma2) -{ -#ifndef CONFIG_SOUND_TRACEINIT - return; -#else - printk("<%s> at 0x%03x", name, base); - - if (irq) - printk(" irq %d", (irq > 0) ? irq : -irq); - - if (dma != -1 || dma2 != -1) - { - printk(" dma %d", dma); - if (dma2 != -1) - printk(",%d", dma2); - } - printk("\n"); -#endif -} diff -Nru a/drivers/sound/soundvers.h b/drivers/sound/soundvers.h --- a/drivers/sound/soundvers.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2 +0,0 @@ -#define SOUND_VERSION_STRING "3.8s2++-971130" -#define SOUND_INTERNAL_VERSION 0x030804 diff -Nru a/drivers/sound/sscape.c b/drivers/sound/sscape.c --- a/drivers/sound/sscape.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1530 +0,0 @@ -/* - * sound/sscape.c - * - * Low level driver for Ensoniq SoundScape - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Sergey Smitienko : ensoniq p'n'p support - * Christoph Hellwig : adapted to module_init/module_exit - * Bartlomiej Zolnierkiewicz : added __init to attach_sscape() - * Chris Rankin : Specify that this module owns the coprocessor - * Arnaldo C. de Melo : added missing restore_flags in sscape_pnp_upload_file - */ - -#include -#include - -#include "sound_config.h" -#include "sound_firmware.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "coproc.h" - -#include "ad1848.h" -#include "mpu401.h" - -/* - * I/O ports - */ -#define MIDI_DATA 0 -#define MIDI_CTRL 1 -#define HOST_CTRL 2 -#define TX_READY 0x02 -#define RX_READY 0x01 -#define HOST_DATA 3 -#define ODIE_ADDR 4 -#define ODIE_DATA 5 - -/* - * Indirect registers - */ - -#define GA_INTSTAT_REG 0 -#define GA_INTENA_REG 1 -#define GA_DMAA_REG 2 -#define GA_DMAB_REG 3 -#define GA_INTCFG_REG 4 -#define GA_DMACFG_REG 5 -#define GA_CDCFG_REG 6 -#define GA_SMCFGA_REG 7 -#define GA_SMCFGB_REG 8 -#define GA_HMCTL_REG 9 - -/* - * DMA channel identifiers (A and B) - */ - -#define SSCAPE_DMA_A 0 -#define SSCAPE_DMA_B 1 - -#define PORT(name) (devc->base+name) - -/* - * Host commands recognized by the OBP microcode - */ - -#define CMD_GEN_HOST_ACK 0x80 -#define CMD_GEN_MPU_ACK 0x81 -#define CMD_GET_BOARD_TYPE 0x82 -#define CMD_SET_CONTROL 0x88 /* Old firmware only */ -#define CMD_GET_CONTROL 0x89 /* Old firmware only */ -#define CTL_MASTER_VOL 0 -#define CTL_MIC_MODE 2 -#define CTL_SYNTH_VOL 4 -#define CTL_WAVE_VOL 7 -#define CMD_SET_EXTMIDI 0x8a -#define CMD_GET_EXTMIDI 0x8b -#define CMD_SET_MT32 0x8c -#define CMD_GET_MT32 0x8d - -#define CMD_ACK 0x80 - -#define IC_ODIE 1 -#define IC_OPUS 2 - -typedef struct sscape_info -{ - int base, irq, dma; - - int codec, codec_irq; /* required to setup pnp cards*/ - int codec_type; - int ic_type; - char* raw_buf; - unsigned long raw_buf_phys; - int buffsize; /* -------------------------- */ - - int ok; /* Properly detected */ - int failed; - int dma_allocated; - int codec_audiodev; - int opened; - int *osp; - int my_audiodev; -} sscape_info; - -static struct sscape_info adev_info = { - 0 -}; - -static struct sscape_info *devc = &adev_info; -static int sscape_mididev = -1; - -/* Some older cards have assigned interrupt bits differently than new ones */ -static char valid_interrupts_old[] = { - 9, 7, 5, 15 -}; - -static char valid_interrupts_new[] = { - 9, 5, 7, 10 -}; - -static char *valid_interrupts = valid_interrupts_new; - -/* - * See the bottom of the driver. This can be set by spea =0/1. - */ - -#ifdef REVEAL_SPEA -static char old_hardware = 1; -#else -static char old_hardware = 0; -#endif - -static void sleep(unsigned howlong) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(howlong); -} - -static unsigned char sscape_read(struct sscape_info *devc, int reg) -{ - unsigned long flags; - unsigned char val; - - save_flags(flags); - cli(); - outb(reg, PORT(ODIE_ADDR)); - val = inb(PORT(ODIE_DATA)); - restore_flags(flags); - return val; -} - -static void sscape_write(struct sscape_info *devc, int reg, int data) -{ - unsigned long flags; - - save_flags(flags); - cli(); - outb(reg, PORT(ODIE_ADDR)); - outb(data, PORT(ODIE_DATA)); - restore_flags(flags); -} - -static unsigned char sscape_pnp_read_codec(sscape_info* devc, unsigned char reg) -{ - unsigned char res; - unsigned long flags; - - save_flags(flags); - cli(); - outb( reg, devc -> codec); - res = inb (devc -> codec + 1); - restore_flags(flags); - return res; - -} - -static void sscape_pnp_write_codec(sscape_info* devc, unsigned char reg, unsigned char data) -{ - unsigned long flags; - - save_flags(flags); - cli(); - outb( reg, devc -> codec); - outb( data, devc -> codec + 1); - restore_flags(flags); -} - -static void host_open(struct sscape_info *devc) -{ - outb((0x00), PORT(HOST_CTRL)); /* Put the board to the host mode */ -} - -static void host_close(struct sscape_info *devc) -{ - outb((0x03), PORT(HOST_CTRL)); /* Put the board to the MIDI mode */ -} - -static int host_write(struct sscape_info *devc, unsigned char *data, int count) -{ - unsigned long flags; - int i, timeout_val; - - save_flags(flags); - cli(); - - /* - * Send the command and data bytes - */ - - for (i = 0; i < count; i++) - { - for (timeout_val = 10000; timeout_val > 0; timeout_val--) - if (inb(PORT(HOST_CTRL)) & TX_READY) - break; - - if (timeout_val <= 0) - { - restore_flags(flags); - return 0; - } - outb(data[i], PORT(HOST_DATA)); - } - restore_flags(flags); - return 1; -} - -static int host_read(struct sscape_info *devc) -{ - unsigned long flags; - int timeout_val; - unsigned char data; - - save_flags(flags); - cli(); - - /* - * Read a byte - */ - - for (timeout_val = 10000; timeout_val > 0; timeout_val--) - if (inb(PORT(HOST_CTRL)) & RX_READY) - break; - - if (timeout_val <= 0) - { - restore_flags(flags); - return -1; - } - data = inb(PORT(HOST_DATA)); - restore_flags(flags); - return data; -} - -#if 0 /* unused */ -static int host_command1(struct sscape_info *devc, int cmd) -{ - unsigned char buf[10]; - buf[0] = (unsigned char) (cmd & 0xff); - return host_write(devc, buf, 1); -} -#endif /* unused */ - - -static int host_command2(struct sscape_info *devc, int cmd, int parm1) -{ - unsigned char buf[10]; - - buf[0] = (unsigned char) (cmd & 0xff); - buf[1] = (unsigned char) (parm1 & 0xff); - - return host_write(devc, buf, 2); -} - -static int host_command3(struct sscape_info *devc, int cmd, int parm1, int parm2) -{ - unsigned char buf[10]; - - buf[0] = (unsigned char) (cmd & 0xff); - buf[1] = (unsigned char) (parm1 & 0xff); - buf[2] = (unsigned char) (parm2 & 0xff); - return host_write(devc, buf, 3); -} - -static void set_mt32(struct sscape_info *devc, int value) -{ - host_open(devc); - host_command2(devc, CMD_SET_MT32, value ? 1 : 0); - if (host_read(devc) != CMD_ACK) - { - /* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */ - } - host_close(devc); -} - -static void set_control(struct sscape_info *devc, int ctrl, int value) -{ - host_open(devc); - host_command3(devc, CMD_SET_CONTROL, ctrl, value); - if (host_read(devc) != CMD_ACK) - { - /* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */ - } - host_close(devc); -} - -static void do_dma(struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode) -{ - unsigned char temp; - - if (dma_chan != SSCAPE_DMA_A) - { - printk(KERN_WARNING "soundscape: Tried to use DMA channel != A. Why?\n"); - return; - } - audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE; - DMAbuf_start_dma(devc->codec_audiodev, buf, blk_size, mode); - audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE; - - temp = devc->dma << 4; /* Setup DMA channel select bits */ - if (devc->dma <= 3) - temp |= 0x80; /* 8 bit DMA channel */ - - temp |= 1; /* Trigger DMA */ - sscape_write(devc, GA_DMAA_REG, temp); - temp &= 0xfe; /* Clear DMA trigger */ - sscape_write(devc, GA_DMAA_REG, temp); -} - -static int verify_mpu(struct sscape_info *devc) -{ - /* - * The SoundScape board could be in three modes (MPU, 8250 and host). - * If the card is not in the MPU mode, enabling the MPU driver will - * cause infinite loop (the driver believes that there is always some - * received data in the buffer. - * - * Detect this by looking if there are more than 10 received MIDI bytes - * (0x00) in the buffer. - */ - - int i; - - for (i = 0; i < 10; i++) - { - if (inb(devc->base + HOST_CTRL) & 0x80) - return 1; - - if (inb(devc->base) != 0x00) - return 1; - } - printk(KERN_WARNING "SoundScape: The device is not in the MPU-401 mode\n"); - return 0; -} - -static int sscape_coproc_open(void *dev_info, int sub_device) -{ - if (sub_device == COPR_MIDI) - { - set_mt32(devc, 0); - if (!verify_mpu(devc)) - return -EIO; - } - return 0; -} - -static void sscape_coproc_close(void *dev_info, int sub_device) -{ - struct sscape_info *devc = dev_info; - unsigned long flags; - - save_flags(flags); - cli(); - if (devc->dma_allocated) - { - sscape_write(devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */ - devc->dma_allocated = 0; - } - restore_flags(flags); - return; -} - -static void sscape_coproc_reset(void *dev_info) -{ -} - -static int sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag) -{ - unsigned long flags; - unsigned char temp; - volatile int done, timeout_val; - static unsigned char codec_dma_bits = 0; - - if (flag & CPF_FIRST) - { - /* - * First block. Have to allocate DMA and to reset the board - * before continuing. - */ - - save_flags(flags); - cli(); - codec_dma_bits = sscape_read(devc, GA_CDCFG_REG); - - if (devc->dma_allocated == 0) - devc->dma_allocated = 1; - - restore_flags(flags); - - sscape_write(devc, GA_HMCTL_REG, - (temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f); /*Reset */ - - for (timeout_val = 10000; timeout_val > 0; timeout_val--) - sscape_read(devc, GA_HMCTL_REG); /* Delay */ - - /* Take board out of reset */ - sscape_write(devc, GA_HMCTL_REG, - (temp = sscape_read(devc, GA_HMCTL_REG)) | 0x80); - } - /* - * Transfer one code block using DMA - */ - if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL) - { - printk(KERN_WARNING "soundscape: DMA buffer not available\n"); - return 0; - } - memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size); - - save_flags(flags); - cli(); - - /******** INTERRUPTS DISABLED NOW ********/ - - do_dma(devc, SSCAPE_DMA_A, - audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys, - size, DMA_MODE_WRITE); - - /* - * Wait until transfer completes. - */ - - done = 0; - timeout_val = 30; - while (!done && timeout_val-- > 0) - { - int resid; - - if (HZ / 50) - sleep(HZ / 50); - clear_dma_ff(devc->dma); - if ((resid = get_dma_residue(devc->dma)) == 0) - done = 1; - } - - restore_flags(flags); - if (!done) - return 0; - - if (flag & CPF_LAST) - { - /* - * Take the board out of reset - */ - outb((0x00), PORT(HOST_CTRL)); - outb((0x00), PORT(MIDI_CTRL)); - - temp = sscape_read(devc, GA_HMCTL_REG); - temp |= 0x40; - sscape_write(devc, GA_HMCTL_REG, temp); /* Kickstart the board */ - - /* - * Wait until the ODB wakes up - */ - - save_flags(flags); - cli(); - done = 0; - timeout_val = 5 * HZ; - while (!done && timeout_val-- > 0) - { - unsigned char x; - - sleep(1); - x = inb(PORT(HOST_DATA)); - if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */ - { - DDB(printk("Soundscape: Acknowledge = %x\n", x)); - done = 1; - } - } - sscape_write(devc, GA_CDCFG_REG, codec_dma_bits); - - restore_flags(flags); - if (!done) - { - printk(KERN_ERR "soundscape: The OBP didn't respond after code download\n"); - return 0; - } - save_flags(flags); - cli(); - done = 0; - timeout_val = 5 * HZ; - while (!done && timeout_val-- > 0) - { - sleep(1); - if (inb(PORT(HOST_DATA)) == 0xfe) /* Host startup acknowledge */ - done = 1; - } - restore_flags(flags); - if (!done) - { - printk(KERN_ERR "soundscape: OBP Initialization failed.\n"); - return 0; - } - printk(KERN_INFO "SoundScape board initialized OK\n"); - set_control(devc, CTL_MASTER_VOL, 100); - set_control(devc, CTL_SYNTH_VOL, 100); - -#ifdef SSCAPE_DEBUG3 - /* - * Temporary debugging aid. Print contents of the registers after - * downloading the code. - */ - { - int i; - - for (i = 0; i < 13; i++) - printk("I%d = %02x (new value)\n", i, sscape_read(devc, i)); - } -#endif - - } - return 1; -} - -static int download_boot_block(void *dev_info, copr_buffer * buf) -{ - if (buf->len <= 0 || buf->len > sizeof(buf->data)) - return -EINVAL; - - if (!sscape_download_boot(devc, buf->data, buf->len, buf->flags)) - { - printk(KERN_ERR "soundscape: Unable to load microcode block to the OBP.\n"); - return -EIO; - } - return 0; -} - -static int sscape_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local) -{ - copr_buffer *buf; - int err; - - switch (cmd) - { - case SNDCTL_COPR_RESET: - sscape_coproc_reset(dev_info); - return 0; - - case SNDCTL_COPR_LOAD: - buf = (copr_buffer *) vmalloc(sizeof(copr_buffer)); - if (buf == NULL) - return -ENOSPC; - if (copy_from_user(buf, arg, sizeof(copr_buffer))) - { - vfree(buf); - return -EFAULT; - } - err = download_boot_block(dev_info, buf); - vfree(buf); - return err; - - default: - return -EINVAL; - } -} - -static coproc_operations sscape_coproc_operations = -{ - "SoundScape M68K", - THIS_MODULE, - sscape_coproc_open, - sscape_coproc_close, - sscape_coproc_ioctl, - sscape_coproc_reset, - &adev_info -}; - -static int sscape_detected = 0; -static int sscape_is_pnp = 0; - -void __init attach_sscape(struct address_info *hw_config) -{ -#ifndef SSCAPE_REGS - /* - * Config register values for Spea/V7 Media FX and Ensoniq S-2000. - * These values are card - * dependent. If you have another SoundScape based card, you have to - * find the correct values. Do the following: - * - Compile this driver with SSCAPE_DEBUG1 defined. - * - Shut down and power off your machine. - * - Boot with DOS so that the SSINIT.EXE program is run. - * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed - * when detecting the SoundScape. - * - Modify the following list to use the values printed during boot. - * Undefine the SSCAPE_DEBUG1 - */ -#define SSCAPE_REGS { \ -/* I0 */ 0x00, \ -/* I1 */ 0xf0, /* Note! Ignored. Set always to 0xf0 */ \ -/* I2 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \ -/* I3 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \ -/* I4 */ 0xf5, /* Ignored */ \ -/* I5 */ 0x10, \ -/* I6 */ 0x00, \ -/* I7 */ 0x2e, /* I7 MEM config A. Likely to vary between models */ \ -/* I8 */ 0x00, /* I8 MEM config B. Likely to vary between models */ \ -/* I9 */ 0x40 /* Ignored */ \ - } -#endif - - unsigned long flags; - static unsigned char regs[10] = SSCAPE_REGS; - - int i, irq_bits = 0xff; - - if (sscape_detected != hw_config->io_base) - return; - - request_region(devc->base + 2, 6, "SoundScape"); - if (old_hardware) - { - valid_interrupts = valid_interrupts_old; - conf_printf("Ensoniq SoundScape (old)", hw_config); - } - else - conf_printf("Ensoniq SoundScape", hw_config); - - for (i = 0; i < sizeof(valid_interrupts); i++) - { - if (hw_config->irq == valid_interrupts[i]) - { - irq_bits = i; - break; - } - } - if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff)) - { - printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq); - return; - } - - if (!sscape_is_pnp) { - - save_flags(flags); - cli(); - for (i = 1; i < 10; i++) - { - switch (i) - { - case 1: /* Host interrupt enable */ - sscape_write(devc, i, 0xf0); /* All interrupts enabled */ - break; - - case 2: /* DMA A status/trigger register */ - case 3: /* DMA B status/trigger register */ - sscape_write(devc, i, 0x20); /* DMA channel disabled */ - break; - - case 4: /* Host interrupt config reg */ - sscape_write(devc, i, 0xf0 | (irq_bits << 2) | irq_bits); - break; - - case 5: /* Don't destroy CD-ROM DMA config bits (0xc0) */ - sscape_write(devc, i, (regs[i] & 0x3f) | (sscape_read(devc, i) & 0xc0)); - break; - - case 6: /* CD-ROM config (WSS codec actually) */ - sscape_write(devc, i, regs[i]); - break; - - case 9: /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */ - sscape_write(devc, i, (sscape_read(devc, i) & 0xf0) | 0x08); - break; - - default: - sscape_write(devc, i, regs[i]); - } - } - restore_flags(flags); - } -#ifdef SSCAPE_DEBUG2 - /* - * Temporary debugging aid. Print contents of the registers after - * changing them. - */ - { - int i; - - for (i = 0; i < 13; i++) - printk("I%d = %02x (new value)\n", i, sscape_read(devc, i)); - } -#endif - - if (probe_mpu401(hw_config)) - hw_config->always_detect = 1; - hw_config->name = "SoundScape"; - - hw_config->irq *= -1; /* Negative value signals IRQ sharing */ - attach_mpu401(hw_config, THIS_MODULE); - hw_config->irq *= -1; /* Restore it */ - - if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ - { - sscape_mididev = hw_config->slots[1]; - midi_devs[hw_config->slots[1]]->coproc = &sscape_coproc_operations; - } - sscape_write(devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */ - devc->ok = 1; - devc->failed = 0; -} - -static int detect_ga(sscape_info * devc) -{ - unsigned char save; - - DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base)); - - if (check_region(devc->base, 8)) - return 0; - - /* - * First check that the address register of "ODIE" is - * there and that it has exactly 4 writable bits. - * First 4 bits - */ - - if ((save = inb(PORT(ODIE_ADDR))) & 0xf0) - { - DDB(printk("soundscape: Detect error A\n")); - return 0; - } - outb((0x00), PORT(ODIE_ADDR)); - if (inb(PORT(ODIE_ADDR)) != 0x00) - { - DDB(printk("soundscape: Detect error B\n")); - return 0; - } - outb((0xff), PORT(ODIE_ADDR)); - if (inb(PORT(ODIE_ADDR)) != 0x0f) - { - DDB(printk("soundscape: Detect error C\n")); - return 0; - } - outb((save), PORT(ODIE_ADDR)); - - /* - * Now verify that some indirect registers return zero on some bits. - * This may break the driver with some future revisions of "ODIE" but... - */ - - if (sscape_read(devc, 0) & 0x0c) - { - DDB(printk("soundscape: Detect error D (%x)\n", sscape_read(devc, 0))); - return 0; - } - if (sscape_read(devc, 1) & 0x0f) - { - DDB(printk("soundscape: Detect error E\n")); - return 0; - } - if (sscape_read(devc, 5) & 0x0f) - { - DDB(printk("soundscape: Detect error F\n")); - return 0; - } - return 1; -} - -static int sscape_read_host_ctrl(sscape_info* devc) -{ - return host_read(devc); -} - -static void sscape_write_host_ctrl2(sscape_info *devc, int a, int b) -{ - host_command2(devc, a, b); -} - -static int sscape_alloc_dma(sscape_info *devc) -{ - char *start_addr, *end_addr; - int dma_pagesize; - int sz, size; - struct page *page; - - if (devc->raw_buf != NULL) return 0; /* Already done */ - dma_pagesize = (devc->dma < 4) ? (64 * 1024) : (128 * 1024); - devc->raw_buf = NULL; - devc->buffsize = 8192*4; - if (devc->buffsize > dma_pagesize) devc->buffsize = dma_pagesize; - start_addr = NULL; - /* - * Now loop until we get a free buffer. Try to get smaller buffer if - * it fails. Don't accept smaller than 8k buffer for performance - * reasons. - */ - while (start_addr == NULL && devc->buffsize > PAGE_SIZE) { - for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1); - devc->buffsize = PAGE_SIZE * (1 << sz); - start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz); - if (start_addr == NULL) devc->buffsize /= 2; - } - - if (start_addr == NULL) { - printk(KERN_ERR "sscape pnp init error: Couldn't allocate DMA buffer\n"); - return 0; - } else { - /* make some checks */ - end_addr = start_addr + devc->buffsize - 1; - /* now check if it fits into the same dma-pagesize */ - - if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1)) - || end_addr >= (char *) (MAX_DMA_ADDRESS)) { - printk(KERN_ERR "sscape pnp: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, devc->buffsize); - return 0; - } - } - devc->raw_buf = start_addr; - devc->raw_buf_phys = virt_to_bus(start_addr); - - for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) - mem_map_reserve(page); - return 1; -} - -static void sscape_free_dma(sscape_info *devc) -{ - int sz, size; - unsigned long start_addr, end_addr; - struct page *page; - - if (devc->raw_buf == NULL) return; - for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1); - start_addr = (unsigned long) devc->raw_buf; - end_addr = start_addr + devc->buffsize; - - for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) - mem_map_unreserve(page); - - free_pages((unsigned long) devc->raw_buf, sz); - devc->raw_buf = NULL; -} - -/* Intel version !!!!!!!!! */ - -static int sscape_start_dma(int chan, unsigned long physaddr, int count, int dma_mode) -{ - unsigned long flags; - - flags = claim_dma_lock(); - disable_dma(chan); - clear_dma_ff(chan); - set_dma_mode(chan, dma_mode); - set_dma_addr(chan, physaddr); - set_dma_count(chan, count); - enable_dma(chan); - release_dma_lock(flags); - return 0; -} - -static void sscape_pnp_start_dma(sscape_info* devc, int arg ) -{ - int reg; - if (arg == 0) reg = 2; - else reg = 3; - - sscape_write(devc, reg, sscape_read( devc, reg) | 0x01); - sscape_write(devc, reg, sscape_read( devc, reg) & 0xFE); -} - -static int sscape_pnp_wait_dma (sscape_info* devc, int arg ) -{ - int reg; - unsigned long i; - unsigned char d; - - if (arg == 0) reg = 2; - else reg = 3; - - sleep ( 1 ); - i = 0; - do { - d = sscape_read(devc, reg) & 1; - if ( d == 1) break; - i++; - } while (i < 500000); - d = sscape_read(devc, reg) & 1; - return d; -} - -static int sscape_pnp_alloc_dma(sscape_info* devc) -{ - /* printk(KERN_INFO "sscape: requesting dma\n"); */ - if (request_dma(devc -> dma, "sscape")) return 0; - /* printk(KERN_INFO "sscape: dma channel allocated\n"); */ - if (!sscape_alloc_dma(devc)) { - free_dma(devc -> dma); - return 0; - }; - return 1; -} - -static void sscape_pnp_free_dma(sscape_info* devc) -{ - sscape_free_dma( devc); - free_dma(devc -> dma ); - /* printk(KERN_INFO "sscape: dma released\n"); */ -} - -static int sscape_pnp_upload_file(sscape_info* devc, char* fn) -{ - int done = 0; - int timeout_val; - char* data,*dt; - int len,l; - unsigned long flags; - - sscape_write( devc, 9, sscape_read(devc, 9 ) & 0x3F ); - sscape_write( devc, 2, (devc -> dma << 4) | 0x80 ); - sscape_write( devc, 3, 0x20 ); - sscape_write( devc, 9, sscape_read( devc, 9 ) | 0x80 ); - - len = mod_firmware_load(fn, &data); - if (len == 0) { - printk(KERN_ERR "sscape: file not found: %s\n", fn); - return 0; - } - dt = data; - save_flags(flags); - cli(); - while ( len > 0 ) { - if (len > devc -> buffsize) l = devc->buffsize; - else l = len; - len -= l; - memcpy(devc->raw_buf, dt, l); dt += l; - sscape_start_dma(devc->dma, devc->raw_buf_phys, l, 0x48); - sscape_pnp_start_dma ( devc, 0 ); - if (sscape_pnp_wait_dma ( devc, 0 ) == 0) { - restore_flags(flags); - return 0; - } - } - - restore_flags(flags); - vfree(data); - - outb(0, devc -> base + 2); - outb(0, devc -> base); - - sscape_write ( devc, 9, sscape_read( devc, 9 ) | 0x40); - - timeout_val = 5 * HZ; - while (!done && timeout_val-- > 0) - { - unsigned char x; - sleep(1); - x = inb( devc -> base + 3); - if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */ - { - //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x); - done = 1; - } - } - timeout_val = 5 * HZ; - done = 0; - while (!done && timeout_val-- > 0) - { - unsigned char x; - sleep(1); - x = inb( devc -> base + 3); - if (x == 0xfe) /* OBP startup acknowledge */ - { - //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x); - done = 1; - } - } - - if ( !done ) printk(KERN_ERR "soundscape: OBP Initialization failed.\n"); - - sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40); - sscape_write( devc, 3, (devc -> dma << 4) + 0x80); - return 1; -} - -static void __init sscape_pnp_init_hw(sscape_info* devc) -{ - unsigned char midi_irq = 0, sb_irq = 0; - unsigned i; - static char code_file_name[23] = "/sndscape/sndscape.cox"; - - int sscape_sb_enable = 0; - int sscape_joystic_enable = 0x7f; - int sscape_mic_enable = 0; - int sscape_ext_midi = 0; - - if ( !sscape_pnp_alloc_dma(devc) ) { - printk(KERN_ERR "sscape: faild to allocate dma\n"); - return; - } - - for (i = 0; i < 4; i++) { - if ( devc -> irq == valid_interrupts[i] ) - midi_irq = i; - if ( devc -> codec_irq == valid_interrupts[i] ) - sb_irq = i; - } - - sscape_write( devc, 5, 0x50); - sscape_write( devc, 7, 0x2e); - sscape_write( devc, 8, 0x00); - - sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40); - sscape_write( devc, 3, ( devc -> dma << 4) | 0x80); - - if ( sscape_sb_enable ) - sscape_write (devc, 4, 0xF0 | (sb_irq << 2) | midi_irq); - else - sscape_write (devc, 4, 0xF0 | (midi_irq<<2) | midi_irq); - - i = 0x10; //sscape_read(devc, 9) & (devc->ic_type == IC_ODIE ? 0xf0 : 0xc0); - if ( sscape_sb_enable ) - i |= devc->ic_type == IC_ODIE ? 0x05 : 0x07; - if (sscape_joystic_enable) i |= 8; - - sscape_write (devc, 9, i); - sscape_write (devc, 6, 0x80); - sscape_write (devc, 1, 0x80); - - if (devc -> codec_type == 2) { - sscape_pnp_write_codec( devc, 0x0C, 0x50); - sscape_pnp_write_codec( devc, 0x10, sscape_pnp_read_codec( devc, 0x10) & 0x3F); - sscape_pnp_write_codec( devc, 0x11, sscape_pnp_read_codec( devc, 0x11) | 0xC0); - sscape_pnp_write_codec( devc, 29, 0x20); - } - - if (sscape_pnp_upload_file(devc, "/sndscape/scope.cod") == 0 ) { - printk(KERN_ERR "sscape: faild to upload file /sndscape/scope.cod\n"); - sscape_pnp_free_dma(devc); - return; - } - - i = sscape_read_host_ctrl( devc ); - - if ( (i & 0x0F) > 7 ) { - printk(KERN_ERR "sscape: scope.cod faild\n"); - sscape_pnp_free_dma(devc); - return; - } - if ( i & 0x10 ) sscape_write( devc, 7, 0x2F); - code_file_name[21] = (char) ( i & 0x0F) + 0x30; - if (sscape_pnp_upload_file( devc, code_file_name) == 0) { - printk(KERN_ERR "sscape: faild to upload file %s\n", code_file_name); - sscape_pnp_free_dma(devc); - return; - } - - if (devc->ic_type != IC_ODIE) { - sscape_pnp_write_codec( devc, 10, (sscape_pnp_read_codec(devc, 10) & 0x7f) | - ( sscape_mic_enable == 0 ? 0x00 : 0x80) ); - } - sscape_write_host_ctrl2( devc, 0x84, 0x64 ); /* MIDI volume */ - sscape_write_host_ctrl2( devc, 0x86, 0x64 ); /* MIDI volume?? */ - sscape_write_host_ctrl2( devc, 0x8A, sscape_ext_midi); - - sscape_pnp_write_codec ( devc, 6, 0x3f ); //WAV_VOL - sscape_pnp_write_codec ( devc, 7, 0x3f ); //WAV_VOL - sscape_pnp_write_codec ( devc, 2, 0x1F ); //WD_CDXVOLL - sscape_pnp_write_codec ( devc, 3, 0x1F ); //WD_CDXVOLR - - if (devc -> codec_type == 1) { - sscape_pnp_write_codec ( devc, 4, 0x1F ); - sscape_pnp_write_codec ( devc, 5, 0x1F ); - sscape_write_host_ctrl2( devc, 0x88, sscape_mic_enable); - } else { - int t; - sscape_pnp_write_codec ( devc, 0x10, 0x1F << 1); - sscape_pnp_write_codec ( devc, 0x11, 0xC0 | (0x1F << 1)); - - t = sscape_pnp_read_codec( devc, 0x00) & 0xDF; - if ( (sscape_mic_enable == 0)) t |= 0; - else t |= 0x20; - sscape_pnp_write_codec ( devc, 0x00, t); - t = sscape_pnp_read_codec( devc, 0x01) & 0xDF; - if ( (sscape_mic_enable == 0) ) t |= 0; - else t |= 0x20; - sscape_pnp_write_codec ( devc, 0x01, t); - sscape_pnp_write_codec ( devc, 0x40 | 29 , 0x20); - outb(0, devc -> codec); - } - if (devc -> ic_type == IC_OPUS ) { - int i = sscape_read( devc, 9 ); - sscape_write( devc, 9, i | 3 ); - sscape_write( devc, 3, 0x40); - - if (check_region(0x228, 1)) { - outb(0, 0x228); - release_region(0x228,1); - } - sscape_write( devc, 3, (devc -> dma << 4) | 0x80); - sscape_write( devc, 9, i ); - } - - host_close ( devc ); - sscape_pnp_free_dma(devc); -} - -static int __init detect_sscape_pnp(sscape_info* devc) -{ - long i, irq_bits = 0xff; - unsigned int d; - - DDB(printk("Entered detect_sscape_pnp(%x)\n", devc->base)); - - if (check_region(devc->base, 8)) { - printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->base); - return 0; - } - - if (check_region(devc->codec, 2)) { - printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->codec); - return 0; - } - - if ( (inb( devc -> base + 2) & 0x78) != 0) return 0; - - d = inb ( devc -> base + 4) & 0xF0; - if ( (d & 0x80) != 0) return 0; - - if (d == 0) { - devc->codec_type = 1; - devc->ic_type = IC_ODIE; - } - else if ( (d & 0x60) != 0) { - devc->codec_type = 2; - devc->ic_type = IC_OPUS; - } - else if ( (d & 0x40) != 0) { - devc->codec_type = 2; - devc->ic_type = IC_ODIE; - } - else return 0; - - sscape_is_pnp = 1; - - outb(0xFA, devc -> base+4); - if ((inb( devc -> base+4) & 0x9F) != 0x0A) - return 0; - outb(0xFE, devc -> base+4); - if ( (inb(devc -> base+4) & 0x9F) != 0x0E) - return 0; - if ( (inb(devc -> base+5) & 0x9F) != 0x0E) - return 0; - - if (devc->codec_type == 2) { - if (devc -> codec != devc -> base + 8) - printk("soundscape warning: incorrect codec port specified\n"); - devc -> codec = devc -> base + 8; - d = 0x10 | (sscape_read(devc, 9) & 0xCF); - sscape_write(devc, 9, d); - sscape_write(devc, 6, 0x80); - } else { - //todo: check codec is not base + 8 - } - - d = (sscape_read(devc, 9) & 0x3F) | 0xC0; - sscape_write(devc, 9, d); - - for (i = 0; i < 550000; i++) - if ( !(inb(devc -> codec) & 0x80) ) break; - - d = inb(devc -> codec); - if (d & 0x80) - return 0; - if ( inb(devc -> codec + 2) == 0xFF) - return 0; - - sscape_write(devc, 9, sscape_read(devc, 9) & 0x3F ); - - d = inb(devc -> codec) & 0x80; - if ( d == 0) { - printk(KERN_INFO "soundscape: hardware detected\n"); - valid_interrupts = valid_interrupts_new; - } else { - printk(KERN_INFO "soundscape: board looks like media fx\n"); - valid_interrupts = valid_interrupts_old; - old_hardware = 1; - } - - sscape_write( devc, 9, 0xC0 | (sscape_read(devc, 9) & 0x3F) ); - - for (i = 0; i < 550000; i++) - if ( !(inb(devc -> codec) & 0x80)) - break; - - sscape_pnp_init_hw(devc); - - for (i = 0; i < sizeof(valid_interrupts); i++) - { - if (devc->codec_irq == valid_interrupts[i]) { - irq_bits = i; - break; - } - } - sscape_write(devc, GA_INTENA_REG, 0x00); - sscape_write(devc, GA_DMACFG_REG, 0x50); - sscape_write(devc, GA_DMAA_REG, 0x70); - sscape_write(devc, GA_DMAB_REG, 0x20); - sscape_write(devc, GA_INTCFG_REG, 0xf0); - sscape_write(devc, GA_CDCFG_REG, 0x89 | (devc->dma << 4) | (irq_bits << 1)); - - sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 0) | 0x20); - sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 1) | 0x20); - - return 1; -} - -static int __init probe_sscape(struct address_info *hw_config) -{ - - if (sscape_detected != 0 && sscape_detected != hw_config->io_base) - return 0; - - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->dma = hw_config->dma; - devc->osp = hw_config->osp; - -#ifdef SSCAPE_DEBUG1 - /* - * Temporary debugging aid. Print contents of the registers before - * changing them. - */ - { - int i; - - for (i = 0; i < 13; i++) - printk("I%d = %02x (old value)\n", i, sscape_read(devc, i)); - } -#endif - devc->failed = 1; - - if (!detect_ga(devc)) { - if (detect_sscape_pnp(devc)) { - sscape_detected = hw_config->io_base; - return 1; - } - else return 0; - } - - if (old_hardware) /* Check that it's really an old Spea/Reveal card. */ - { - unsigned char tmp; - int cc; - - if (!((tmp = sscape_read(devc, GA_HMCTL_REG)) & 0xc0)) - { - sscape_write(devc, GA_HMCTL_REG, tmp | 0x80); - for (cc = 0; cc < 200000; ++cc) - inb(devc->base + ODIE_ADDR); - } - } - sscape_detected = hw_config->io_base; - return 1; -} - -static int __init probe_ss_ms_sound(struct address_info *hw_config) -{ - int i, irq_bits = 0xff; - int ad_flags = 0; - - if (devc->failed) - { - printk(KERN_ERR "soundscape: Card not detected\n"); - return 0; - } - if (devc->ok == 0) - { - printk(KERN_ERR "soundscape: Invalid initialization order.\n"); - return 0; - } - for (i = 0; i < sizeof(valid_interrupts); i++) - { - if (hw_config->irq == valid_interrupts[i]) - { - irq_bits = i; - break; - } - } - if (hw_config->irq > 15 || irq_bits == 0xff) - { - printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq); - return 0; - } - - if (!sscape_is_pnp) { - if (old_hardware) - ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ - return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); - } - else { - if (old_hardware) - ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ - else - ad_flags = 0x87654321; /* Tell that we have a soundscape pnp with 1845 chip */ - return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); - } -} - -static void __init attach_ss_ms_sound(struct address_info *hw_config) -{ - /* - * This routine configures the SoundScape card for use with the - * Win Sound System driver. The AD1848 codec interface uses the CD-ROM - * config registers of the "ODIE". - */ - - int i, irq_bits = 0xff; - - - if (!sscape_is_pnp) /*pnp is already setup*/ - { - /* - * Setup the DMA polarity. - */ - sscape_write(devc, GA_DMACFG_REG, 0x50); - - /* - * Take the gate-array off of the DMA channel. - */ - sscape_write(devc, GA_DMAB_REG, 0x20); - - /* - * Init the AD1848 (CD-ROM) config reg. - */ - for (i = 0; i < sizeof(valid_interrupts); i++) - { - if (hw_config->irq == valid_interrupts[i]) - { - irq_bits = i; - break; - } - } - sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1)); - } - - if (hw_config->irq == devc->irq) - printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n"); - - hw_config->slots[0] = ad1848_init( - sscape_is_pnp ? "SoundScape" : "SoundScape PNP", - hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma, - 0, - devc->osp, - THIS_MODULE); - - - if (hw_config->slots[0] != -1) /* The AD1848 driver installed itself */ - { - audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations; - devc->codec_audiodev = hw_config->slots[0]; - devc->my_audiodev = hw_config->slots[0]; - - /* Set proper routings here (what are they) */ - AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); - } - -#ifdef SSCAPE_DEBUG5 - /* - * Temporary debugging aid. Print contents of the registers - * after the AD1848 device has been initialized. - */ - { - int i; - - for (i = 0; i < 13; i++) - printk("I%d = %02x\n", i, sscape_read(devc, i)); - } -#endif - -} - -static void __exit unload_sscape(struct address_info *hw_config) -{ - release_region(devc->base + 2, 6); - unload_mpu401(hw_config); -} - -static void __exit unload_ss_ms_sound(struct address_info *hw_config) -{ - ad1848_unload(hw_config->io_base, - hw_config->irq, - devc->dma, - devc->dma, - 0); - sound_unload_audiodev(hw_config->slots[0]); -} - -static struct address_info cfg; -static struct address_info cfg_mpu; - -static int __initdata spea = -1; -static int __initdata mss = 0; -static int __initdata dma = -1; -static int __initdata irq = -1; -static int __initdata io = -1; -static int __initdata mpu_irq = -1; -static int __initdata mpu_io = -1; - -MODULE_PARM(dma, "i"); -MODULE_PARM(irq, "i"); -MODULE_PARM(io, "i"); -MODULE_PARM(spea, "i"); /* spea=0/1 set the old_hardware */ -MODULE_PARM(mpu_irq, "i"); -MODULE_PARM(mpu_io, "i"); -MODULE_PARM(mss, "i"); - -static int __init init_sscape(void) -{ - printk(KERN_INFO "Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - cfg.irq = irq; - cfg.dma = dma; - cfg.io_base = io; - - cfg_mpu.irq = mpu_irq; - cfg_mpu.io_base = mpu_io; - /* WEH - Try to get right dma channel */ - cfg_mpu.dma = dma; - - devc->codec = cfg.io_base; - devc->codec_irq = cfg.irq; - devc->codec_type = 0; - devc->ic_type = 0; - devc->raw_buf = NULL; - - if (cfg.dma == -1 || cfg.irq == -1 || cfg.io_base == -1) { - printk(KERN_ERR "DMA, IRQ, and IO port must be specified.\n"); - return -EINVAL; - } - - if (cfg_mpu.irq == -1 && cfg_mpu.io_base != -1) { - printk(KERN_ERR "MPU_IRQ must be specified if MPU_IO is set.\n"); - return -EINVAL; - } - - if(spea != -1) { - old_hardware = spea; - printk(KERN_INFO "Forcing %s hardware support.\n", - spea?"new":"old"); - } - if (probe_sscape(&cfg_mpu) == 0) - return -ENODEV; - - attach_sscape(&cfg_mpu); - - mss = probe_ss_ms_sound(&cfg); - - if (mss) - attach_ss_ms_sound(&cfg); - - return 0; -} - -static void __exit cleanup_sscape(void) -{ - if (mss) - unload_ss_ms_sound(&cfg); - unload_sscape(&cfg_mpu); -} - -module_init(init_sscape); -module_exit(cleanup_sscape); - -#ifndef MODULE -static int __init setup_sscape(char *str) -{ - /* io, irq, dma, mpu_io, mpu_irq */ - int ints[6]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - mpu_io = ints[4]; - mpu_irq = ints[5]; - - return 1; -} - -__setup("sscape=", setup_sscape); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/sys_timer.c b/drivers/sound/sys_timer.c --- a/drivers/sound/sys_timer.c Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,287 +0,0 @@ -/* - * sound/sys_timer.c - * - * The default timer for the Level 2 sequencer interface - * Uses the (1/HZ sec) timer of kernel. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Andrew Veliath : adapted tmr2ticks from level 1 sequencer (avoid overflow) - */ -#include "sound_config.h" - -static volatile int opened = 0, tmr_running = 0; -static volatile time_t tmr_offs, tmr_ctr; -static volatile unsigned long ticks_offs; -static volatile int curr_tempo, curr_timebase; -static volatile unsigned long curr_ticks; -static volatile unsigned long next_event_time; -static unsigned long prev_event_time; - -static void poll_def_tmr(unsigned long dummy); - - -static struct timer_list def_tmr = -{function: poll_def_tmr}; - -static unsigned long -tmr2ticks(int tmr_value) -{ - /* - * Convert timer ticks to MIDI ticks - */ - - unsigned long tmp; - unsigned long scale; - - /* tmr_value (ticks per sec) * - 1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */ - tmp = tmr_value * (1000000 / HZ); - scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ - return (tmp + scale / 2) / scale; -} - -static void -poll_def_tmr(unsigned long dummy) -{ - - if (opened) - { - - { - def_tmr.expires = (1) + jiffies; - add_timer(&def_tmr); - }; - - if (tmr_running) - { - tmr_ctr++; - curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); - - if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer(0); - } - } - } -} - -static void -tmr_reset(void) -{ - unsigned long flags; - - save_flags(flags); - cli(); - tmr_offs = 0; - ticks_offs = 0; - tmr_ctr = 0; - next_event_time = (unsigned long) -1; - prev_event_time = 0; - curr_ticks = 0; - restore_flags(flags); -} - -static int -def_tmr_open(int dev, int mode) -{ - if (opened) - return -EBUSY; - - tmr_reset(); - curr_tempo = 60; - curr_timebase = 100; - opened = 1; - - ; - - { - def_tmr.expires = (1) + jiffies; - add_timer(&def_tmr); - }; - - return 0; -} - -static void -def_tmr_close(int dev) -{ - opened = tmr_running = 0; - del_timer(&def_tmr);; -} - -static int -def_tmr_event(int dev, unsigned char *event) -{ - unsigned char cmd = event[1]; - unsigned long parm = *(int *) &event[4]; - - switch (cmd) - { - case TMR_WAIT_REL: - parm += prev_event_time; - case TMR_WAIT_ABS: - if (parm > 0) - { - long time; - - if (parm <= curr_ticks) /* It's the time */ - return TIMER_NOT_ARMED; - - time = parm; - next_event_time = prev_event_time = time; - - return TIMER_ARMED; - } - break; - - case TMR_START: - tmr_reset(); - tmr_running = 1; - break; - - case TMR_STOP: - tmr_running = 0; - break; - - case TMR_CONTINUE: - tmr_running = 1; - break; - - case TMR_TEMPO: - if (parm) - { - if (parm < 8) - parm = 8; - if (parm > 360) - parm = 360; - tmr_offs = tmr_ctr; - ticks_offs += tmr2ticks(tmr_ctr); - tmr_ctr = 0; - curr_tempo = parm; - } - break; - - case TMR_ECHO: - seq_copy_to_input(event, 8); - break; - - default:; - } - - return TIMER_NOT_ARMED; -} - -static unsigned long -def_tmr_get_time(int dev) -{ - if (!opened) - return 0; - - return curr_ticks; -} - -/* same as sound_timer.c:timer_ioctl!? */ -static int def_tmr_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - int val; - - switch (cmd) { - case SNDCTL_TMR_SOURCE: - return __put_user(TMR_INTERNAL, (int *)arg); - - case SNDCTL_TMR_START: - tmr_reset(); - tmr_running = 1; - return 0; - - case SNDCTL_TMR_STOP: - tmr_running = 0; - return 0; - - case SNDCTL_TMR_CONTINUE: - tmr_running = 1; - return 0; - - case SNDCTL_TMR_TIMEBASE: - if (__get_user(val, (int *)arg)) - return -EFAULT; - if (val) { - if (val < 1) - val = 1; - if (val > 1000) - val = 1000; - curr_timebase = val; - } - return __put_user(curr_timebase, (int *)arg); - - case SNDCTL_TMR_TEMPO: - if (__get_user(val, (int *)arg)) - return -EFAULT; - if (val) { - if (val < 8) - val = 8; - if (val > 250) - val = 250; - tmr_offs = tmr_ctr; - ticks_offs += tmr2ticks(tmr_ctr); - tmr_ctr = 0; - curr_tempo = val; - reprogram_timer(); - } - return __put_user(curr_tempo, (int *)arg); - - case SNDCTL_SEQ_CTRLRATE: - if (__get_user(val, (int *)arg)) - return -EFAULT; - if (val != 0) /* Can't change */ - return -EINVAL; - val = ((curr_tempo * curr_timebase) + 30) / 60; - return __put_user(val, (int *)arg); - - case SNDCTL_SEQ_GETTIME: - return __put_user(curr_ticks, (int *)arg); - - case SNDCTL_TMR_METRONOME: - /* NOP */ - break; - - default:; - } - return -EINVAL; -} - -static void -def_tmr_arm(int dev, long time) -{ - if (time < 0) - time = curr_ticks + 1; - else if (time <= curr_ticks) /* It's the time */ - return; - - next_event_time = prev_event_time = time; - - return; -} - -struct sound_timer_operations default_sound_timer = -{ - owner: THIS_MODULE, - info: {"System clock", 0}, - priority: 0, /* Priority */ - devlink: 0, /* Local device link */ - open: def_tmr_open, - close: def_tmr_close, - event: def_tmr_event, - get_time: def_tmr_get_time, - ioctl: def_tmr_ioctl, - arm_timer: def_tmr_arm -}; diff -Nru a/drivers/sound/trident.c b/drivers/sound/trident.c --- a/drivers/sound/trident.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,4231 +0,0 @@ -/* - * OSS driver for Linux 2.4.x for - * - * Trident 4D-Wave - * SiS 7018 - * ALi 5451 - * Tvia/IGST CyberPro 5050 - * - * Driver: Alan Cox - * - * Built from: - * Low level code: from ALSA - * Framework: Thomas Sailer - * Extended by: Zach Brown - * - * Hacked up by: - * Aaron Holtzman - * Ollie Lho SiS 7018 Audio Core Support - * Ching-Ling Lee ALi 5451 Audio Core Support - * Matt Wu ALi 5451 Audio Core Support - * Peter Wächtler CyberPro5050 support - * - * - * 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. - * - * History - * v0.14.9d - * October 8 2001 Arnaldo Carvalho de Melo - * use set_current_state, properly release resources on failure in - * trident_probe, get rid of check_region - * v0.14.9c - * August 10 2001 Peter Wächtler - * added support for Tvia (formerly Integraphics/IGST) CyberPro5050 - * this chip is often found in settop boxes (combined video+audio) - * v0.14.9b - * Switch to static inline not extern inline (gcc 3) - * v0.14.9a - * Aug 6 2001 Alan Cox - * 0.14.9 crashed on rmmod due to a timer/bh left running. Simplified - * the existing logic (the BH doesnt help as ac97 is lock_irqsave) - * and used del_timer_sync to clean up - * Fixed a problem where the ALi change broke my generic card - * v0.14.9 - * Jul 10 2001 Matt Wu - * Add H/W Volume Control - * v0.14.8a - * July 7 2001 Alan Cox - * Moved Matt Wu's ac97 register cache into the card structure - * v0.14.8 - * Apr 30 2001 Matt Wu - * Set EBUF1 and EBUF2 to still mode - * Add dc97/ac97 reset function - * Fix power management: ali_restore_regs - * unreleased - * Mar 09 2001 Matt Wu - * Add cache for ac97 access - * v0.14.7 - * Feb 06 2001 Matt Wu - * Fix ac97 initialization - * Fix bug: an extra tail will be played when playing - * Jan 05 2001 Matt Wu - * Implement multi-channels and S/PDIF in support for ALi 1535+ - * v0.14.6 - * Nov 1 2000 Ching-Ling Lee - * Fix the bug of memory leak when switching 5.1-channels to 2 channels. - * Add lock protection into dynamic changing format of data. - * Oct 18 2000 Ching-Ling Lee - * 5.1-channels support for ALi - * June 28 2000 Ching-Ling Lee - * S/PDIF out/in(playback/record) support for ALi 1535+, using /proc to be selected by user - * Simple Power Management support for ALi - * v0.14.5 May 23 2000 Ollie Lho - * Misc bug fix from the Net - * v0.14.4 May 20 2000 Aaron Holtzman - * Fix kfree'd memory access in release - * Fix race in open while looking for a free virtual channel slot - * remove open_wait wq (which appears to be unused) - * v0.14.3 May 10 2000 Ollie Lho - * fixed a small bug in trident_update_ptr, xmms 1.0.1 no longer uses 100% CPU - * v0.14.2 Mar 29 2000 Ching-Ling Lee - * Add clear to silence advance in trident_update_ptr - * fix invalid data of the end of the sound - * v0.14.1 Mar 24 2000 Ching-Ling Lee - * ALi 5451 support added, playback and recording O.K. - * ALi 5451 originally developed and structured based on sonicvibes, and - * suggested to merge into this file by Alan Cox. - * v0.14 Mar 15 2000 Ollie Lho - * 5.1 channel output support with channel binding. What's the Matrix ? - * v0.13.1 Mar 10 2000 Ollie Lho - * few minor bugs on dual codec support, needs more testing - * v0.13 Mar 03 2000 Ollie Lho - * new pci_* for 2.4 kernel, back ported to 2.2 - * v0.12 Feb 23 2000 Ollie Lho - * Preliminary Recording support - * v0.11.2 Feb 19 2000 Ollie Lho - * removed incomplete full-dulplex support - * v0.11.1 Jan 28 2000 Ollie Lho - * small bug in setting sample rate for 4d-nx (reported by Aaron) - * v0.11 Jan 27 2000 Ollie Lho - * DMA bug, scheduler latency, second try - * v0.10 Jan 24 2000 Ollie Lho - * DMA bug fixed, found kernel scheduling problem - * v0.09 Jan 20 2000 Ollie Lho - * Clean up of channel register access routine (prepare for channel binding) - * v0.08 Jan 14 2000 Ollie Lho - * Isolation of AC97 codec code - * v0.07 Jan 13 2000 Ollie Lho - * Get rid of ugly old low level access routines (e.g. CHRegs.lp****) - * v0.06 Jan 11 2000 Ollie Lho - * Preliminary support for dual (more ?) AC97 codecs - * v0.05 Jan 08 2000 Luca Montecchiani - * adapt to 2.3.x new __setup/__init call - * v0.04 Dec 31 1999 Ollie Lho - * Multiple Open, using Middle Loop Interrupt to smooth playback - * v0.03 Dec 24 1999 Ollie Lho - * mem leak in prog_dmabuf and dealloc_dmabuf removed - * v0.02 Dec 15 1999 Ollie Lho - * SiS 7018 support added, playback O.K. - * v0.01 Alan Cox et. al. - * Initial Release in kernel 2.3.30, does not work - * - * ToDo - * Clean up of low level channel register access code. (done) - * Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done) - * Dual AC97 codecs support (done) - * Recording support (done) - * Mmap support - * "Channel Binding" ioctl extension (done) - * new pci device driver interface for 2.4 kernel (done) - * - * Lock order (high->low) - * lock - hardware lock - * open_sem - guard opens - * sem - guard dmabuf, write re-entry etc - */ - -#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 - -#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC -#include -#endif - -#include "trident.h" - -#include - -#define DRIVER_VERSION "0.14.9d" - -/* magic numbers to protect our data structures */ -#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */ -#define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */ - -#define TRIDENT_DMA_MASK 0x3fffffff /* DMA buffer mask for pci_alloc_consist */ -#define ALI_DMA_MASK 0xffffffff /* ALI Tridents lack the 30-bit limitation */ - -#define NR_HW_CH 32 - -/* maxinum nuber of AC97 codecs connected, AC97 2.0 defined 4, but 7018 and 4D-NX only - have 2 SDATA_IN lines (currently) */ -#define NR_AC97 2 - -/* minor number of /dev/swmodem (temporary, experimental) */ -#define SND_DEV_SWMODEM 7 - -static const unsigned ali_multi_channels_5_1[] = { /*ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL,*/ ALI_CENTER_CHANNEL, ALI_LEF_CHANNEL, ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL}; - -static const unsigned sample_size[] = { 1, 2, 2, 4 }; -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n"; - -enum { - TRIDENT_4D_DX = 0, - TRIDENT_4D_NX, - SIS_7018, - ALI_5451, - CYBER5050 -}; - -static char * card_names[] = { - "Trident 4DWave DX", - "Trident 4DWave NX", - "SiS 7018 PCI Audio", - "ALi Audio Accelerator", - "Tvia/IGST CyberPro 5050" -}; - -static struct pci_device_id trident_pci_tbl [] __devinitdata = { - {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_DX}, - {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_NX}, - {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018}, - {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI_5451}, - { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5050, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, CYBER5050}, - {0,} -}; - -MODULE_DEVICE_TABLE (pci, trident_pci_tbl); - -/* "software" or virtual channel, an instance of opened /dev/dsp */ -struct trident_state { - unsigned int magic; - struct trident_card *card; /* Card info */ - - /* file mode */ - mode_t open_mode; - - /* virtual channel number */ - int virt; - - struct dmabuf { - /* wave sample stuff */ - unsigned int rate; - unsigned char fmt, enable; - - /* hardware channel */ - struct trident_channel *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 comsumed 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 */ - unsigned fragsize; - unsigned dmasize; - unsigned fragsamples; - - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned endcleared:1; - unsigned update_flag; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - - } dmabuf; - - /* 5.1channels */ - struct trident_state *other_states[4]; - int multi_channels_adjust_count; - unsigned chans_num; - unsigned fmt_flag:1; - /* Guard against mmap/write/read races */ - struct semaphore sem; - -}; - -/* hardware channels */ -struct trident_channel { - int num; /* channel number */ - u32 lba; /* Loop Begine Address, where dma buffer starts */ - u32 eso; /* End Sample Offset, wehre dma buffer ends (in the unit of samples) */ - u32 delta; /* delta value, sample rate / 48k for playback, 48k/sample rate for recording */ - u16 attribute; /* control where PCM data go and come */ - u16 fm_vol; - u32 control; /* signed/unsigned, 8/16 bits, mono/stereo */ -}; - -struct trident_pcm_bank_address { - u32 start; - u32 stop; - u32 aint; - u32 aint_en; -}; -static struct trident_pcm_bank_address bank_a_addrs = -{ - T4D_START_A, - T4D_STOP_A, - T4D_AINT_A, - T4D_AINTEN_A -}; -static struct trident_pcm_bank_address bank_b_addrs = -{ - T4D_START_B, - T4D_STOP_B, - T4D_AINT_B, - T4D_AINTEN_B -}; -struct trident_pcm_bank { - /* register addresses to control bank operations */ - struct trident_pcm_bank_address *addresses; - /* each bank has 32 channels */ - u32 bitmap; /* channel allocation bitmap */ - struct trident_channel channels[32]; -}; - -struct trident_card { - unsigned int magic; - - /* We keep trident cards in a linked list */ - struct trident_card *next; - - /* single open lock mechanism, only used for recording */ - struct semaphore open_sem; - - /* The trident has a certain amount of cross channel interaction - so we use a single per card lock */ - spinlock_t lock; - - /* PCI device stuff */ - struct pci_dev * pci_dev; - u16 pci_id; - u8 revision; - - /* soundcore stuff */ - int dev_audio; - - /* structures for abstraction of hardware facilities, codecs, banks and channels*/ - struct ac97_codec *ac97_codec[NR_AC97]; - struct trident_pcm_bank banks[NR_BANKS]; - struct trident_state *states[NR_HW_CH]; - - /* hardware resources */ - unsigned long iobase; - u32 irq; - - /* Function support */ - struct trident_channel *(*alloc_pcm_channel)(struct trident_card *); - struct trident_channel *(*alloc_rec_pcm_channel)(struct trident_card *); - void (*free_pcm_channel)(struct trident_card *, unsigned int chan); - void (*address_interrupt)(struct trident_card *); - - /* Added by Matt Wu 01-05-2001 for spdif in */ - int multi_channel_use_count; - int rec_channel_use_count; - u16 mixer_regs[64][NR_AC97]; /* Made card local by Alan */ - int mixer_regs_ready; - - /* Added for hardware volume control */ - int hwvolctl; - struct timer_list timer; -}; - -/* table to map from CHANNELMASK to channel attribute for SiS 7018 */ -static u16 mask2attr [] = -{ - PCM_LR, PCM_LR, SURR_LR, CENTER_LFE, - HSET, MIC, MODEM_LINE1, MODEM_LINE2, - I2S_LR, SPDIF_LR -}; -/* table to map from channel attribute to CHANNELMASK for SiS 7018 */ -static int attr2mask [] = { - DSP_BIND_MODEM1, DSP_BIND_MODEM2, DSP_BIND_FRONT, DSP_BIND_HANDSET, - DSP_BIND_I2S, DSP_BIND_CENTER_LFE, DSP_BIND_SURR, DSP_BIND_SPDIF -}; - -/* Added by Matt Wu 01-05-2001 for spdif in */ -static int ali_close_multi_channels(void); -static void ali_delay(struct trident_card *card,int interval); -static void ali_detect_spdif_rate(struct trident_card *card); - -static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val); -static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg); - -static struct trident_card *devs; - -static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val); -static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg); - -static int trident_open_mixdev(struct inode *inode, struct file *file); -static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg); - -static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val); -static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg); -static void ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate); -static void ali_enable_special_channel(struct trident_state *stat); -static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card); -static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card); -static void ali_restore_regs(struct trident_card *card); -static void ali_save_regs(struct trident_card *card); -static int trident_suspend(struct pci_dev *dev, u32 unused); -static int trident_resume(struct pci_dev *dev); -static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel); -static int ali_setup_multi_channels(struct trident_card *card, int chan_nums); -static unsigned int ali_get_spdif_in_rate(struct trident_card *card); -static void ali_setup_spdif_in(struct trident_card *card); -static void ali_disable_spdif_in(struct trident_card *card); -static void ali_disable_special_channel(struct trident_card *card, int ch); -static void ali_setup_spdif_out(struct trident_card *card, int flag); -static int ali_write_5_1(struct trident_state *state, const char *buffer,int cnt_for_multi_channel, unsigned int *copy_count, unsigned int *state_cnt); -static int ali_allocate_other_states_resources(struct trident_state *state, int chan_nums); -static void ali_free_other_states_resources(struct trident_state *state); - - -/* save registers for ALi Power Management */ -static struct ali_saved_registers { - unsigned long global_regs[ALI_GLOBAL_REGS]; - unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS]; - unsigned mixer_regs[ALI_MIXER_REGS]; -} ali_registers; - -#define seek_offset(dma_ptr, buffer, cnt, offset, copy_count) (dma_ptr) += (offset); \ - (buffer) += (offset); \ - (cnt) -= (offset); \ - (copy_count) += (offset); - -#define lock_set_fmt(state) {spin_lock_irqsave(&state->card->lock, flags); \ - if (state->fmt_flag) { \ - spin_unlock_irqrestore(&state->card->lock, flags); \ - return -EFAULT; \ - } \ - state->fmt_flag = 1; \ - spin_unlock_irqrestore(&state->card->lock, flags);} - -#define unlock_set_fmt(state) {spin_lock_irqsave(&state->card->lock, flags); \ - state->fmt_flag = 0; \ - spin_unlock_irqrestore(&state->card->lock, flags);} - -static int trident_enable_loop_interrupts(struct trident_card * card) -{ - u32 global_control; - - global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR)); - - switch (card->pci_id) - { - case PCI_DEVICE_ID_SI_7018: - global_control |= (ENDLP_IE | MIDLP_IE| BANK_B_EN); - break; - case PCI_DEVICE_ID_ALI_5451: - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - case PCI_DEVICE_ID_INTERG_5050: - global_control |= (ENDLP_IE | MIDLP_IE); - break; - default: - return FALSE; - } - - outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); - -#ifdef DEBUG - printk("trident: Enable Loop Interrupts, globctl = 0x%08X\n", - inl(TRID_REG(card, T4D_LFO_GC_CIR))); -#endif - return (TRUE); -} - -static int trident_disable_loop_interrupts(struct trident_card * card) -{ - u32 global_control; - - global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR)); - global_control &= ~(ENDLP_IE | MIDLP_IE); - outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); - -#ifdef DEBUG - printk("trident: Disabled Loop Interrupts, globctl = 0x%08X\n", - global_control); -#endif - return (TRUE); -} - -static void trident_enable_voice_irq(struct trident_card * card, unsigned int channel) -{ - unsigned int mask = 1 << (channel & 0x1f); - struct trident_pcm_bank *bank = &card->banks[channel >> 5]; - u32 reg, addr = bank->addresses->aint_en; - - reg = inl(TRID_REG(card, addr)); - reg |= mask; - outl(reg, TRID_REG(card, addr)); - -#ifdef DEBUG - reg = inl(TRID_REG(card, addr)); - printk("trident: enabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); -#endif -} - -static void trident_disable_voice_irq(struct trident_card * card, unsigned int channel) -{ - unsigned int mask = 1 << (channel & 0x1f); - struct trident_pcm_bank *bank = &card->banks[channel >> 5]; - u32 reg, addr = bank->addresses->aint_en; - - reg = inl(TRID_REG(card, addr)); - reg &= ~mask; - outl(reg, TRID_REG(card, addr)); - - /* Ack the channel in case the interrupt was set before we disable it. */ - outl(mask, TRID_REG(card, bank->addresses->aint)); - -#ifdef DEBUG - reg = inl(TRID_REG(card, addr)); - printk("trident: disabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); -#endif -} - -static void trident_start_voice(struct trident_card * card, unsigned int channel) -{ - unsigned int mask = 1 << (channel & 0x1f); - struct trident_pcm_bank *bank = &card->banks[channel >> 5]; - u32 addr = bank->addresses->start; - -#ifdef DEBUG - u32 reg; -#endif - - outl(mask, TRID_REG(card, addr)); - -#ifdef DEBUG - reg = inl(TRID_REG(card, addr)); - printk("trident: start voice on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_START_B? "START_B":"START_A",reg,addr); -#endif -} - -static void trident_stop_voice(struct trident_card * card, unsigned int channel) -{ - unsigned int mask = 1 << (channel & 0x1f); - struct trident_pcm_bank *bank = &card->banks[channel >> 5]; - u32 addr = bank->addresses->stop; - -#ifdef DEBUG - u32 reg; -#endif - - outl(mask, TRID_REG(card, addr)); - -#ifdef DEBUG - reg = inl(TRID_REG(card, addr)); - printk("trident: stop voice on channel %d, %s = 0x%08x(addr:%X)\n", - channel, addr==T4D_STOP_B? "STOP_B":"STOP_A",reg,addr); -#endif -} - -static u32 trident_get_interrupt_mask (struct trident_card * card, unsigned int channel) -{ - struct trident_pcm_bank *bank = &card->banks[channel]; - u32 addr = bank->addresses->aint; - return inl(TRID_REG(card, addr)); -} - -static int trident_check_channel_interrupt(struct trident_card * card, unsigned int channel) -{ - unsigned int mask = 1 << (channel & 0x1f); - u32 reg = trident_get_interrupt_mask (card, channel >> 5); - -#ifdef DEBUG - if (reg & mask) - printk("trident: channel %d has interrupt, %s = 0x%08x\n", - channel,reg==T4D_AINT_B? "AINT_B":"AINT_A", reg); -#endif - return (reg & mask) ? TRUE : FALSE; -} - -static void trident_ack_channel_interrupt(struct trident_card * card, unsigned int channel) -{ - unsigned int mask = 1 << (channel & 0x1f); - struct trident_pcm_bank *bank = &card->banks[channel >> 5]; - u32 reg, addr = bank->addresses->aint; - - reg = inl(TRID_REG(card, addr)); - reg &= mask; - outl(reg, TRID_REG(card, addr)); - -#ifdef DEBUG - reg = inl(TRID_REG(card, T4D_AINT_B)); - printk("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n", - channel, reg); -#endif -} - -static struct trident_channel * trident_alloc_pcm_channel(struct trident_card *card) -{ - struct trident_pcm_bank *bank; - int idx; - - bank = &card->banks[BANK_B]; - - for (idx = 31; idx >= 0; idx--) { - if (!(bank->bitmap & (1 << idx))) { - struct trident_channel *channel = &bank->channels[idx]; - bank->bitmap |= 1 << idx; - channel->num = idx + 32; - return channel; - } - } - - /* no more free channels available */ - printk(KERN_ERR "trident: no more channels available on Bank B.\n"); - return NULL; -} - -static void trident_free_pcm_channel(struct trident_card *card, unsigned int channel) -{ - int bank; - unsigned char b; - - if (channel < 31 || channel > 63) - return; - - if (card->pci_id == PCI_DEVICE_ID_TRIDENT_4DWAVE_DX || - card->pci_id == PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) { - b = inb (TRID_REG(card, T4D_REC_CH)); - if ((b & ~0x80) == channel) - outb(0x0, TRID_REG(card, T4D_REC_CH)); - } - - bank = channel >> 5; - channel = channel & 0x1f; - - card->banks[bank].bitmap &= ~(1 << (channel)); -} - -static struct trident_channel * cyber_alloc_pcm_channel(struct trident_card *card) -{ - struct trident_pcm_bank *bank; - int idx; - - /* The cyberpro 5050 has only 32 voices and one bank */ - /* .. at least they are not documented (if you want to call that - * crap documentation), perhaps broken ? */ - - bank = &card->banks[BANK_A]; - - for (idx = 31; idx >= 0; idx--) { - if (!(bank->bitmap & (1 << idx))) { - struct trident_channel *channel = &bank->channels[idx]; - bank->bitmap |= 1 << idx; - channel->num = idx; - return channel; - } - } - - /* no more free channels available */ - printk(KERN_ERR "cyberpro5050: no more channels available on Bank A.\n"); - return NULL; -} - -static void cyber_free_pcm_channel(struct trident_card *card, unsigned int channel) -{ - if (channel > 31) - return; - card->banks[BANK_A].bitmap &= ~(1 << (channel)); -} - -static inline void cyber_outidx(int port,int idx,int data) -{ - outb(idx,port); - outb(data,port+1); -} - -static inline int cyber_inidx(int port,int idx) -{ - outb(idx,port); - return inb(port+1); -} - -static int cyber_init_ritual(struct trident_card *card) -{ - /* some black magic, taken from SDK samples */ - /* remove this and nothing will work */ - int portDat; - int ret = 0; - unsigned long flags; - - /* - * Keep interrupts off for the configure - we don't want to - * clash with another cyberpro config event - */ - - save_flags(flags); - cli(); - portDat = cyber_inidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE); - /* enable, if it was disabled */ - if( (portDat & CYBER_BMSK_AUENZ) != CYBER_BMSK_AUENZ_ENABLE ) { - printk(KERN_INFO "cyberpro5050: enabling audio controller\n" ); - cyber_outidx( CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE, - portDat | CYBER_BMSK_AUENZ_ENABLE ); - /* check again if hardware is enabled now */ - portDat = cyber_inidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE); - } - if( (portDat & CYBER_BMSK_AUENZ) != CYBER_BMSK_AUENZ_ENABLE ) - { - printk(KERN_ERR "cyberpro5050: initAudioAccess: no success\n" ); - ret = -1; - } - else - { - cyber_outidx( CYBER_PORT_AUDIO, CYBER_IDX_IRQ_ENABLE, CYBER_BMSK_AUDIO_INT_ENABLE ); - cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x01 ); - cyber_outidx( CYBER_PORT_AUDIO, 0xba, 0x20 ); - cyber_outidx( CYBER_PORT_AUDIO, 0xbb, 0x08 ); - cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x02 ); - cyber_outidx( CYBER_PORT_AUDIO, 0xb3, 0x06 ); - cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x00 ); - } - restore_flags(flags); - return ret; -} - -/* called with spin lock held */ - -static int trident_load_channel_registers(struct trident_card *card, u32 *data, unsigned int channel) -{ - int i; - - if (channel > 63) - return FALSE; - - /* select hardware channel to write */ - outb(channel, TRID_REG(card, T4D_LFO_GC_CIR)); - - /* Output the channel registers, but don't write register - three to an ALI chip. */ - for (i = 0; i < CHANNEL_REGS; i++) { - if (i == 3 && card->pci_id == PCI_DEVICE_ID_ALI_5451) - continue; - outl(data[i], TRID_REG(card, CHANNEL_START + 4*i)); - } - if (card->pci_id == PCI_DEVICE_ID_ALI_5451 || - card->pci_id == PCI_DEVICE_ID_INTERG_5050) { - outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF1)); - outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF2)); - } - return TRUE; -} - -/* called with spin lock held */ -static int trident_write_voice_regs(struct trident_state *state) -{ - unsigned int data[CHANNEL_REGS + 1]; - struct trident_channel *channel; - - channel = state->dmabuf.channel; - - data[1] = channel->lba; - data[4] = channel->control; - - switch (state->card->pci_id) - { - case PCI_DEVICE_ID_ALI_5451: - data[0] = 0; /* Current Sample Offset */ - data[2] = (channel->eso << 16) | (channel->delta & 0xffff); - data[3] = 0; - break; - case PCI_DEVICE_ID_SI_7018: - case PCI_DEVICE_ID_INTERG_5050: - data[0] = 0; /* Current Sample Offset */ - data[2] = (channel->eso << 16) | (channel->delta & 0xffff); - data[3] = (channel->attribute << 16) | (channel->fm_vol & 0xffff); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - data[0] = 0; /* Current Sample Offset */ - data[2] = (channel->eso << 16) | (channel->delta & 0xffff); - data[3] = channel->fm_vol & 0xffff; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - data[0] = (channel->delta << 24); - data[2] = ((channel->delta << 16) & 0xff000000) | (channel->eso & 0x00ffffff); - data[3] = channel->fm_vol & 0xffff; - break; - default: - return FALSE; - } - - return trident_load_channel_registers(state->card, data, channel->num); -} - -static int compute_rate_play(u32 rate) -{ - int delta; - /* We special case 44100 and 8000 since rounding with the equation - does not give us an accurate enough value. For 11025 and 22050 - the equation gives us the best answer. All other frequencies will - also use the equation. JDW */ - if (rate == 44100) - delta = 0xeb3; - else if (rate == 8000) - delta = 0x2ab; - else if (rate == 48000) - delta = 0x1000; - else - delta = (((rate << 12) + rate) / 48000) & 0x0000ffff; - return delta; -} - -static int compute_rate_rec(u32 rate) -{ - int delta; - - if (rate == 44100) - delta = 0x116a; - else if (rate == 8000) - delta = 0x6000; - else if (rate == 48000) - delta = 0x1000; - else - delta = ((48000 << 12) / rate) & 0x0000ffff; - - return delta; -} -/* set playback sample rate */ -static unsigned int trident_set_dac_rate(struct trident_state * state, unsigned int rate) -{ - struct dmabuf *dmabuf = &state->dmabuf; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - - dmabuf->rate = rate; - dmabuf->channel->delta = compute_rate_play(rate); - - trident_write_voice_regs(state); - -#ifdef DEBUG - printk("trident: called trident_set_dac_rate : rate = %d\n", rate); -#endif - - return rate; -} - -/* set recording sample rate */ -static unsigned int trident_set_adc_rate(struct trident_state * state, unsigned int rate) -{ - struct dmabuf *dmabuf = &state->dmabuf; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - - dmabuf->rate = rate; - dmabuf->channel->delta = compute_rate_rec(rate); - - trident_write_voice_regs(state); - -#ifdef DEBUG - printk("trident: called trident_set_adc_rate : rate = %d\n", rate); -#endif - return rate; -} - -/* prepare channel attributes for playback */ -static void trident_play_setup(struct trident_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct trident_channel *channel = dmabuf->channel; - - channel->lba = dmabuf->dma_handle; - channel->delta = compute_rate_play(dmabuf->rate); - - channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; - channel->eso -= 1; - - if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) { - channel->attribute = 0; - if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451) { - if ((channel->num == ALI_SPDIF_IN_CHANNEL) || (channel->num == ALI_PCM_IN_CHANNEL)) - ali_disable_special_channel(state->card, channel->num); - else if ((inl(TRID_REG(state->card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE) - && (channel->num == ALI_SPDIF_OUT_CHANNEL)) - { - ali_set_spdif_out_rate(state->card, state->dmabuf.rate); - state->dmabuf.channel->delta = 0x1000; - } - } - } - - channel->fm_vol = 0x0; - - channel->control = CHANNEL_LOOP; - if (dmabuf->fmt & TRIDENT_FMT_16BIT) { - /* 16-bits */ - channel->control |= CHANNEL_16BITS; - /* signed */ - channel->control |= CHANNEL_SIGNED; - } - if (dmabuf->fmt & TRIDENT_FMT_STEREO) - /* stereo */ - channel->control |= CHANNEL_STEREO; -#ifdef DEBUG - printk("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); -#endif - trident_write_voice_regs(state); -} - -/* prepare channel attributes for recording */ -static void trident_rec_setup(struct trident_state *state) -{ - u16 w; - u8 bval; - - struct trident_card *card = state->card; - struct dmabuf *dmabuf = &state->dmabuf; - struct trident_channel *channel = dmabuf->channel; - unsigned int rate; - - /* Enable AC-97 ADC (capture) */ - switch (card->pci_id) - { - case PCI_DEVICE_ID_ALI_5451: - ali_enable_special_channel(state); - break; - case PCI_DEVICE_ID_SI_7018: - /* for 7018, the ac97 is always in playback/record (duplex) mode */ - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT)); - outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); - /* enable and set record channel */ - outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - w = inw(TRID_REG(card, T4D_MISCINT)); - outw(w | 0x1000, TRID_REG(card, T4D_MISCINT)); - /* enable and set record channel */ - outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH)); - break; - case PCI_DEVICE_ID_INTERG_5050: - /* don't know yet, using special channel 22 in GC1(0xd4)? */ - break; - default: - return; - } - - channel->lba = dmabuf->dma_handle; - channel->delta = compute_rate_rec(dmabuf->rate); - if ((card->pci_id == PCI_DEVICE_ID_ALI_5451) && (channel->num == ALI_SPDIF_IN_CHANNEL)) { - rate = ali_get_spdif_in_rate(card); - if (rate == 0) - { - printk(KERN_WARNING "trident: ALi 5451 S/PDIF input setup error!\n"); - rate = 48000; - } - bval = inb(TRID_REG(card,ALI_SPDIF_CTRL)); - if (bval & 0x10) - { - outb(bval,TRID_REG(card,ALI_SPDIF_CTRL)); - printk(KERN_WARNING "trident: cleared ALi 5451 S/PDIF parity error flag.\n"); - } - - if (rate != 48000) - channel->delta = ((rate << 12) / dmabuf->rate) & 0x0000ffff; - } - - channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; - channel->eso -= 1; - - if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) { - channel->attribute = 0; - } - - channel->fm_vol = 0x0; - - channel->control = CHANNEL_LOOP; - if (dmabuf->fmt & TRIDENT_FMT_16BIT) { - /* 16-bits */ - channel->control |= CHANNEL_16BITS; - /* signed */ - channel->control |= CHANNEL_SIGNED; - } - if (dmabuf->fmt & TRIDENT_FMT_STEREO) - /* stereo */ - channel->control |= CHANNEL_STEREO; -#ifdef DEBUG - printk("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); -#endif - trident_write_voice_regs(state); -} - -/* get current playback/recording dma buffer pointer (byte offset from LBA), - called with spinlock held! */ -static inline unsigned trident_get_dma_addr(struct trident_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - u32 cso; - - if (!dmabuf->enable) - return 0; - - outb(dmabuf->channel->num, TRID_REG(state->card, T4D_LFO_GC_CIR)); - - switch (state->card->pci_id) - { - case PCI_DEVICE_ID_ALI_5451: - case PCI_DEVICE_ID_SI_7018: - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - case PCI_DEVICE_ID_INTERG_5050: - /* 16 bits ESO, CSO for 7018 and DX */ - cso = inw(TRID_REG(state->card, CH_DX_CSO_ALPHA_FMS + 2)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - /* 24 bits ESO, CSO for NX */ - cso = inl(TRID_REG(state->card, CH_NX_DELTA_CSO)) & 0x00ffffff; - break; - default: - return 0; - } - -#ifdef DEBUG - printk("trident: trident_get_dma_addr: chip reported channel: %d, " - "cso = 0x%04x\n", - dmabuf->channel->num, cso); -#endif - /* ESO and CSO are in units of Samples, convert to byte offset */ - cso <<= sample_shift[dmabuf->fmt]; - - return (cso % dmabuf->dmasize); -} - -/* Stop recording (lock held) */ -static inline void __stop_adc(struct trident_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned int chan_num = dmabuf->channel->num; - struct trident_card *card = state->card; - - dmabuf->enable &= ~ADC_RUNNING; - trident_stop_voice(card, chan_num); - trident_disable_voice_irq(card, chan_num); -} - -static void stop_adc(struct trident_state *state) -{ - struct trident_card *card = state->card; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - __stop_adc(state); - spin_unlock_irqrestore(&card->lock, flags); -} - -static void start_adc(struct trident_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned int chan_num = dmabuf->channel->num; - struct trident_card *card = state->card; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - if ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) && dmabuf->ready) { - dmabuf->enable |= ADC_RUNNING; - trident_enable_voice_irq(card, chan_num); - trident_start_voice(card, chan_num); - } - spin_unlock_irqrestore(&card->lock, flags); -} - -/* stop playback (lock held) */ -static inline void __stop_dac(struct trident_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned int chan_num = dmabuf->channel->num; - struct trident_card *card = state->card; - - dmabuf->enable &= ~DAC_RUNNING; - trident_stop_voice(card, chan_num); - if (state->chans_num == 6) { - trident_stop_voice(card, state->other_states[0]->dmabuf.channel->num); - trident_stop_voice(card, state->other_states[1]->dmabuf.channel->num); - trident_stop_voice(card, state->other_states[2]->dmabuf.channel->num); - trident_stop_voice(card, state->other_states[3]->dmabuf.channel->num); - } - trident_disable_voice_irq(card, chan_num); -} - -static void stop_dac(struct trident_state *state) -{ - struct trident_card *card = state->card; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - __stop_dac(state); - spin_unlock_irqrestore(&card->lock, flags); -} - -static void start_dac(struct trident_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned int chan_num = dmabuf->channel->num; - struct trident_card *card = state->card; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) { - dmabuf->enable |= DAC_RUNNING; - trident_enable_voice_irq(card, chan_num); - trident_start_voice(card, chan_num); - if (state->chans_num == 6) { - trident_start_voice(card, state->other_states[0]->dmabuf.channel->num); - trident_start_voice(card, state->other_states[1]->dmabuf.channel->num); - trident_start_voice(card, state->other_states[2]->dmabuf.channel->num); - trident_start_voice(card, state->other_states[3]->dmabuf.channel->num); - } - } - spin_unlock_irqrestore(&card->lock, flags); -} - -#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -/* allocate DMA buffer, playback and recording buffer should be allocated seperately */ -static int alloc_dmabuf(struct trident_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - void *rawbuf = NULL; - int order; - struct page *page, *pend; - - /* alloc as big a chunk as we can, FIXME: is this necessary ?? */ - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) - if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, - PAGE_SIZE << order, - &dmabuf->dma_handle))) - break; - if (!rawbuf) - return -ENOMEM; - -#ifdef DEBUG - printk("trident: allocated %ld (order = %d) bytes at %p\n", - PAGE_SIZE << order, order, rawbuf); -#endif - - 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++) - mem_map_reserve(page); - - return 0; -} - -/* free DMA buffer */ -static void dealloc_dmabuf(struct trident_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++) - mem_map_unreserve(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 trident_state *state, unsigned rec) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned bytepersec; - struct trident_state *s = state; - unsigned bufsize, dma_nums; - unsigned long flags; - int ret, i, order; - struct page *page, *pend; - - lock_set_fmt(state); - if (state->chans_num == 6) - dma_nums = 5; - else dma_nums = 1; - - for (i = 0; i < dma_nums; i++) { - if (i > 0) { - s = state->other_states[i - 1]; - dmabuf = &s->dmabuf; - dmabuf->fmt = state->dmabuf.fmt; - dmabuf->rate = state->dmabuf.rate; - } - - spin_lock_irqsave(&s->card->lock, flags); - dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0; - dmabuf->count = dmabuf->error = 0; - spin_unlock_irqrestore(&s->card->lock, flags); - - /* allocate DMA buffer if not allocated yet */ - if (!dmabuf->rawbuf) { - if (i == 0) { - if ((ret = alloc_dmabuf(state))) { - unlock_set_fmt(state); - return ret; - } - } - else { - if ((order = state->dmabuf.buforder - 1) >= DMABUF_MINORDER) { - dmabuf->rawbuf = pci_alloc_consistent(state->card->pci_dev, - PAGE_SIZE << order, - &dmabuf->dma_handle); - } - if (!dmabuf->rawbuf) { - free_pages((unsigned long)state->dmabuf.rawbuf, state->dmabuf.buforder); - state->dmabuf.rawbuf = NULL; - i-=2; - for (; i >= 0; i--) { - pci_free_consistent(state->card->pci_dev, - PAGE_SIZE << state->other_states[i]->dmabuf.buforder, - state->other_states[i]->dmabuf.rawbuf, - state->other_states[i]->dmabuf.dma_handle); - } - unlock_set_fmt(state); - return -ENOMEM; - } - dmabuf->ready = dmabuf->mapped = 0; - dmabuf->buforder = order; - pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << order) - 1); - for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) - mem_map_reserve(page); - } - } - /* FIXME: figure out all this OSS fragment stuff */ - bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt]; - bufsize = PAGE_SIZE << dmabuf->buforder; - if (dmabuf->ossfragshift) { - if ((1000 << dmabuf->ossfragshift) < bytepersec) - dmabuf->fragshift = ld2(bytepersec/1000); - else - dmabuf->fragshift = dmabuf->ossfragshift; - } else { - /* lets hand out reasonable big ass buffers by default */ - dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2); - } - dmabuf->numfrag = bufsize >> dmabuf->fragshift; - while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) { - dmabuf->fragshift--; - dmabuf->numfrag = bufsize >> dmabuf->fragshift; - } - dmabuf->fragsize = 1 << dmabuf->fragshift; - if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag) - dmabuf->numfrag = dmabuf->ossmaxfrags; - dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt]; - dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; - - memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80, - dmabuf->dmasize); - - spin_lock_irqsave(&s->card->lock, flags); - if (rec) { - trident_rec_setup(s); - } else { - trident_play_setup(s); - } - spin_unlock_irqrestore(&s->card->lock, flags); - - /* set the ready flag for the dma buffer */ - dmabuf->ready = 1; - -#ifdef DEBUG - printk("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); -#endif - } - unlock_set_fmt(state); - return 0; -} - -/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e. - |------------|------------| or |xxxxxxxxxxxx|------------| or |xxxxxxxxxxxx|xxxxxxxxxxxx| - but we almost always get this - |xxxxxx------|------------| or |xxxxxxxxxxxx|xxxxx-------| - so we have to clear the tail space to "silence" - |xxxxxx000000|------------| or |xxxxxxxxxxxx|xxxxxx000000| -*/ -static void trident_clear_tail(struct trident_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned swptr; - unsigned char silence = (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80; - unsigned int len; - unsigned long flags; - - spin_lock_irqsave(&state->card->lock, flags); - swptr = dmabuf->swptr; - spin_unlock_irqrestore(&state->card->lock, flags); - - if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize) - return; - - if (swptr < dmabuf->dmasize/2) - len = dmabuf->dmasize/2 - swptr; - else - len = dmabuf->dmasize - swptr; - - memset(dmabuf->rawbuf + swptr, silence, len); - if(state->card->pci_id != PCI_DEVICE_ID_ALI_5451) - { - spin_lock_irqsave(&state->card->lock, flags); - dmabuf->swptr += len; - dmabuf->count += len; - spin_unlock_irqrestore(&state->card->lock, flags); - } - - /* restart the dma machine in case it is halted */ - start_dac(state); -} - -static int drain_dac(struct trident_state *state, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - unsigned long tmo; - int count; - unsigned long diff = 0; - - if (dmabuf->mapped || !dmabuf->ready) - return 0; - - add_wait_queue(&dmabuf->wait, &wait); - for (;;) { - /* It seems that we have to set the current state to TASK_INTERRUPTIBLE - every time to make the process really go to sleep */ - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&state->card->lock, flags); - count = dmabuf->count; - spin_unlock_irqrestore(&state->card->lock, flags); - - if (count <= 0) - break; - - if (signal_pending(current)) - break; - - if (nonblock) { - remove_wait_queue(&dmabuf->wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - - /* No matter how much data is left in the buffer, we have to wait until - CSO == ESO/2 or CSO == ESO when address engine interrupts */ - if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451 || - state->card->pci_id == PCI_DEVICE_ID_INTERG_5050) - { - diff = dmabuf->swptr - trident_get_dma_addr(state) + dmabuf->dmasize ; - diff = diff % (dmabuf->dmasize); - tmo = (diff * HZ) / dmabuf->rate; - } - else - { - tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; - } - tmo >>= sample_shift[dmabuf->fmt]; - if (!schedule_timeout(tmo ? tmo : 1) && tmo){ - break; - } - } - remove_wait_queue(&dmabuf->wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - - return 0; -} - -/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ -static void trident_update_ptr(struct trident_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned hwptr, swptr; - int clear_cnt = 0; - int diff; - unsigned char silence; - unsigned half_dmasize; - - /* update hardware pointer */ - hwptr = trident_get_dma_addr(state); - diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; - dmabuf->hwptr = hwptr; - dmabuf->total_bytes += diff; - - /* error handling and process wake up for ADC */ - if (dmabuf->enable == ADC_RUNNING) { - if (dmabuf->mapped) { - dmabuf->count -= diff; - if (dmabuf->count >= (signed)dmabuf->fragsize) - wake_up(&dmabuf->wait); - } else { - dmabuf->count += diff; - - if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { - /* buffer underrun or buffer overrun, we have no way to recover - it here, just stop the machine and let the process force hwptr - and swptr to sync */ - __stop_adc(state); - dmabuf->error++; - } - if (dmabuf->count < (signed)dmabuf->dmasize/2) - wake_up(&dmabuf->wait); - } - } - - /* error handling and process wake up for DAC */ - if (dmabuf->enable == DAC_RUNNING) { - if (dmabuf->mapped) { - dmabuf->count += diff; - if (dmabuf->count >= (signed)dmabuf->fragsize) - wake_up(&dmabuf->wait); - } else { - dmabuf->count -= diff; - - if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { - /* buffer underrun or buffer overrun, we have no way to recover - it here, just stop the machine and let the process force hwptr - and swptr to sync */ - __stop_dac(state); - dmabuf->error++; - } - else if (!dmabuf->endcleared) { - swptr = dmabuf->swptr; - silence = (dmabuf->fmt & TRIDENT_FMT_16BIT ? 0 : 0x80); - if (dmabuf->update_flag & ALI_ADDRESS_INT_UPDATE) { - /* We must clear end data of 1/2 dmabuf if needed. - According to 1/2 algorithm of Address Engine Interrupt, - check the validation of the data of half dmasize. */ - half_dmasize = dmabuf->dmasize / 2; - if ((diff = hwptr - half_dmasize) < 0 ) - diff = hwptr; - if ((dmabuf->count + diff) < half_dmasize) { - //there is invalid data in the end of half buffer - if ((clear_cnt = half_dmasize - swptr) < 0) - clear_cnt += half_dmasize; - //clear the invalid data - memset (dmabuf->rawbuf + swptr, - silence, clear_cnt); - if (state->chans_num == 6) { - clear_cnt = clear_cnt / 2; - swptr = swptr / 2; - memset (state->other_states[0]->dmabuf.rawbuf + swptr, - silence, clear_cnt); - memset (state->other_states[1]->dmabuf.rawbuf + swptr, - silence, clear_cnt); - memset (state->other_states[2]->dmabuf.rawbuf + swptr, - silence, clear_cnt); - memset (state->other_states[3]->dmabuf.rawbuf + swptr, - silence, clear_cnt); - } - dmabuf->endcleared = 1; - } - } else if (dmabuf->count < (signed) dmabuf->fragsize) { - clear_cnt = dmabuf->fragsize; - if ((swptr + clear_cnt) > dmabuf->dmasize) - clear_cnt = dmabuf->dmasize - swptr; - memset (dmabuf->rawbuf + swptr, silence, clear_cnt); - if (state->chans_num == 6) { - clear_cnt = clear_cnt / 2; - swptr = swptr / 2; - memset (state->other_states[0]->dmabuf.rawbuf + swptr, - silence, clear_cnt); - memset (state->other_states[1]->dmabuf.rawbuf + swptr, - silence, clear_cnt); - memset (state->other_states[2]->dmabuf.rawbuf + swptr, - silence, clear_cnt); - memset (state->other_states[3]->dmabuf.rawbuf + swptr, - silence, clear_cnt); - } - dmabuf->endcleared = 1; - } - } - /* trident_update_ptr is called by interrupt handler or by process via - ioctl/poll, we only wake up the waiting process when we have more - than 1/2 buffer free (always true for interrupt handler) */ - if (dmabuf->count < (signed)dmabuf->dmasize/2) - wake_up(&dmabuf->wait); - } - } - dmabuf->update_flag &= ~ALI_ADDRESS_INT_UPDATE; -} - -static void trident_address_interrupt(struct trident_card *card) -{ - int i; - struct trident_state *state; - - /* Update the pointers for all channels we are running. */ - /* FIXME: should read interrupt status only once */ - for (i = 0; i < NR_HW_CH; i++) { - if (trident_check_channel_interrupt(card, 63 - i)) { - trident_ack_channel_interrupt(card, 63 - i); - if ((state = card->states[i]) != NULL) { - trident_update_ptr(state); - } else { - printk("trident: spurious channel irq %d.\n", - 63 - i); - trident_stop_voice(card, 63 - i); - trident_disable_voice_irq(card, 63 - i); - } - } - } -} - -static void ali_hwvol_control(struct trident_card *card, int opt) -{ - u16 dwTemp, volume[2], mute, diff, *pVol[2]; - - dwTemp = ali_ac97_read(card->ac97_codec[0], 0x02); - mute = dwTemp & 0x8000; - volume[0] = dwTemp & 0x001f; - volume[1] = (dwTemp & 0x1f00) >> 8; - if (volume[0] < volume [1]) { - pVol[0] = &volume[0]; - pVol[1] = &volume[1]; - } else { - pVol[1] = &volume[0]; - pVol[0] = &volume[1]; - } - diff = *(pVol[1]) - *(pVol[0]); - - if (opt == 1) { // MUTE - dwTemp ^= 0x8000; - ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp); - } else if (opt == 2) { // Down - if (mute) - return; - if (*(pVol[1]) < 0x001f) { - (*pVol[1])++; - *(pVol[0]) = *(pVol[1]) - diff; - } - dwTemp &= 0xe0e0; - dwTemp |= (volume[0]) | (volume[1] << 8); - ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp); - card->ac97_codec[0]->mixer_state[0] = ((32-volume[0])*25/8) | (((32-volume[1])*25/8) << 8); - } else if (opt == 4) { // Up - if (mute) - return; - if (*(pVol[0]) >0) { - (*pVol[0])--; - *(pVol[1]) = *(pVol[0]) + diff; - } - dwTemp &= 0xe0e0; - dwTemp |= (volume[0]) | (volume[1] << 8); - ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp); - card->ac97_codec[0]->mixer_state[0] = ((32-volume[0])*25/8) | (((32-volume[1])*25/8) << 8); - } - else - { - /* Nothing needs doing */ - } -} - -/* - * Re-enable reporting of vol change after 0.1 seconds - */ - -static void ali_timeout(unsigned long ptr) -{ - struct trident_card *card = (struct trident_card *)ptr; - u16 temp = 0; - - /* Enable GPIO IRQ (MISCINT bit 18h)*/ - temp = inw(TRID_REG(card, T4D_MISCINT + 2)); - temp |= 0x0004; - outw(temp, TRID_REG(card, T4D_MISCINT + 2)); -} - -/* - * Set up the timer to clear the vol change notification - */ - -static void ali_set_timer(struct trident_card *card) -{ - /* Add Timer Routine to Enable GPIO IRQ */ - del_timer(&card->timer); /* Never queue twice */ - card->timer.function = ali_timeout; - card->timer.data = (unsigned long) card; - card->timer.expires = jiffies + HZ/10; - add_timer(&card->timer); -} - -/* - * Process a GPIO event - */ - -static void ali_queue_task(struct trident_card *card, int opt) -{ - u16 temp; - - /* Disable GPIO IRQ (MISCINT bit 18h)*/ - temp = inw(TRID_REG(card, T4D_MISCINT + 2)); - temp &= (u16)(~0x0004); - outw(temp, TRID_REG(card, T4D_MISCINT + 2)); - - /* Adjust the volume */ - ali_hwvol_control(card, opt); - - /* Set the timer for 1/10th sec */ - ali_set_timer(card); -} - -static void cyber_address_interrupt(struct trident_card *card) -{ - int i,irq_status; - struct trident_state *state; - - /* Update the pointers for all channels we are running. */ - /* FIXED: read interrupt status only once */ - irq_status=inl(TRID_REG(card, T4D_AINT_A) ); -#ifdef DEBUG - printk("cyber_address_interrupt: irq_status 0x%X\n",irq_status); -#endif - for (i = 0; i < NR_HW_CH; i++) { - if (irq_status & ( 1 << (31 - i)) ) { - - /* clear bit by writing a 1, zeroes are ignored */ - outl( (1 <<(31-i)), TRID_REG(card, T4D_AINT_A)); - -#ifdef DEBUG - printk("cyber_interrupt: channel %d\n", 31-i); -#endif - if ((state = card->states[i]) != NULL) { - trident_update_ptr(state); - } else { - printk("cyber5050: spurious channel irq %d.\n", - 31 - i); - trident_stop_voice(card, 31 - i); - trident_disable_voice_irq(card, 31 - i); - } - } - } -} - -static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct trident_card *card = (struct trident_card *)dev_id; - u32 event; - u32 gpio; - - spin_lock(&card->lock); - event = inl(TRID_REG(card, T4D_MISCINT)); - -#ifdef DEBUG - printk("trident: trident_interrupt called, MISCINT = 0x%08x\n", event); -#endif - - if (event & ADDRESS_IRQ) { - card->address_interrupt(card); - } - - if(card->pci_id == PCI_DEVICE_ID_ALI_5451) - { - /* GPIO IRQ (H/W Volume Control) */ - event = inl(TRID_REG(card, T4D_MISCINT)); - if (event & (1<<25)) { - gpio = inl(TRID_REG(card, ALI_GPIO)); - if (!timer_pending(&card->timer)) - ali_queue_task(card, gpio&0x07); - } - event = inl(TRID_REG(card, T4D_MISCINT)); - outl(event | (ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(card, T4D_MISCINT)); - spin_unlock(&card->lock); - return; - } - - /* manually clear interrupt status, bad hardware design, blame T^2 */ - outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), - TRID_REG(card, T4D_MISCINT)); - spin_unlock(&card->lock); -} - -/* 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 trident_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct trident_state *state = (struct trident_state *)file->private_data; - struct dmabuf *dmabuf = &state->dmabuf; - ssize_t ret = 0; - unsigned long flags; - unsigned swptr; - int cnt; - -#ifdef DEBUG - printk("trident: trident_read called, count = %d\n", count); -#endif - - VALIDATE_STATE(state); - if (ppos != &file->f_pos) - return -ESPIPE; - - if (dmabuf->mapped) - return -ENXIO; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - - down(&state->sem); - if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) - goto out; - - while (count > 0) { - spin_lock_irqsave(&state->card->lock, flags); - if (dmabuf->count > (signed) dmabuf->dmasize) { - /* buffer overrun, we are recovering from sleep_on_timeout, - resync hwptr and swptr, make process flush the buffer */ - dmabuf->count = dmabuf->dmasize; - dmabuf->swptr = dmabuf->hwptr; - } - swptr = dmabuf->swptr; - cnt = dmabuf->dmasize - swptr; - if (dmabuf->count < cnt) - cnt = dmabuf->count; - spin_unlock_irqrestore(&state->card->lock, flags); - - if (cnt > count) - cnt = count; - if (cnt <= 0) { - unsigned long tmo; - /* buffer is empty, start the dma machine and wait for data to be - recorded */ - start_adc(state); - if (file->f_flags & O_NONBLOCK) { - if (!ret) ret = -EAGAIN; - goto out; - } - - up(&state->sem); - /* No matter how much space left in the buffer, we have to wait until - CSO == ESO/2 or CSO == ESO when address engine interrupts */ - tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); - tmo >>= sample_shift[dmabuf->fmt]; - /* 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 (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { -#ifdef DEBUG - printk(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); -#endif - /* 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)) { - if(!ret) ret = -ERESTARTSYS; - goto out; - } - down(&state->sem); - if(dmabuf->mapped) - { - if(!ret) - ret = -ENXIO; - goto out; - } - continue; - } - - if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { - if (!ret) ret = -EFAULT; - goto out; - } - - swptr = (swptr + cnt) % dmabuf->dmasize; - - spin_lock_irqsave(&state->card->lock, flags); - dmabuf->swptr = swptr; - dmabuf->count -= cnt; - spin_unlock_irqrestore(&state->card->lock, flags); - - count -= cnt; - buffer += cnt; - ret += cnt; - start_adc(state); - } -out: - up(&state->sem); - 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 trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct trident_state *state = (struct trident_state *)file->private_data; - struct dmabuf *dmabuf = &state->dmabuf; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - unsigned int state_cnt; - unsigned int copy_count; - -#ifdef DEBUG - printk("trident: trident_write called, count = %d\n", count); -#endif - VALIDATE_STATE(state); - if (ppos != &file->f_pos) - return -ESPIPE; - - /* - * Guard against an mmap or ioctl while writing - */ - - down(&state->sem); - - if (dmabuf->mapped) - { - ret = -ENXIO; - goto out; - } - if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) - goto out; - - if (!access_ok(VERIFY_READ, buffer, count)) - { - ret= -EFAULT; - goto out; - } - - ret = 0; - - while (count > 0) { - spin_lock_irqsave(&state->card->lock, flags); - if (dmabuf->count < 0) { - /* buffer underrun, we are recovering from sleep_on_timeout, - resync hwptr and swptr */ - dmabuf->count = 0; - dmabuf->swptr = dmabuf->hwptr; - } - swptr = dmabuf->swptr; - cnt = dmabuf->dmasize - swptr; - if (dmabuf->count + cnt > dmabuf->dmasize) - cnt = dmabuf->dmasize - dmabuf->count; - spin_unlock_irqrestore(&state->card->lock, flags); - - if (cnt > count) - cnt = count; - if (cnt <= 0) { - unsigned long tmo; - /* buffer is full, start the dma machine and wait for data to be - played */ - start_dac(state); - if (file->f_flags & O_NONBLOCK) { - if (!ret) ret = -EAGAIN; - goto out; - } - /* No matter how much data left in the buffer, we have to wait until - CSO == ESO/2 or CSO == ESO when address engine interrupts */ - lock_set_fmt(state); - tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); - tmo >>= sample_shift[dmabuf->fmt]; - unlock_set_fmt(state); - up(&state->sem); - - /* 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. */ - if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { -#ifdef DEBUG - printk(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); -#endif - /* a buffer underrun, we delay the recovery until next time the - while loop begin and we REALLY have data to play */ - } - if (signal_pending(current)) { - if (!ret) ret = -ERESTARTSYS; - goto out; - } - down(&state->sem); - if(dmabuf->mapped) - { - if(!ret) - ret = -ENXIO; - goto out; - } - continue; - } - lock_set_fmt(state); - if (state->chans_num == 6) { - copy_count = 0; - state_cnt = 0; - if (ali_write_5_1(state, buffer, cnt, ©_count, &state_cnt) == -EFAULT) { - if (state_cnt){ - swptr = (swptr + state_cnt) % dmabuf->dmasize; - spin_lock_irqsave(&state->card->lock, flags); - dmabuf->swptr = swptr; - dmabuf->count += state_cnt; - dmabuf->endcleared = 0; - spin_unlock_irqrestore(&state->card->lock, flags); - } - ret += copy_count; - if (!ret) ret = -EFAULT; - unlock_set_fmt(state); - goto out; - } - } - else { - if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { - if (!ret) ret = -EFAULT; - unlock_set_fmt(state); - goto out; - } - state_cnt = cnt; - } - unlock_set_fmt(state); - - swptr = (swptr + state_cnt) % dmabuf->dmasize; - - spin_lock_irqsave(&state->card->lock, flags); - dmabuf->swptr = swptr; - dmabuf->count += state_cnt; - dmabuf->endcleared = 0; - spin_unlock_irqrestore(&state->card->lock, flags); - - count -= cnt; - buffer += cnt; - ret += cnt; - start_dac(state); - } -out: - up(&state->sem); - return ret; -} - - -/* No kernel lock - we have our own spinlock */ -static unsigned int trident_poll(struct file *file, struct poll_table_struct *wait) -{ - struct trident_state *state = (struct trident_state *)file->private_data; - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(state); - - /* - * Guard against a parallel poll and write causing multiple - * prog_dmabuf events - */ - - down(&state->sem); - - if (file->f_mode & FMODE_WRITE) { - if (!dmabuf->ready && prog_dmabuf(state, 0)) - { - up(&state->sem); - return 0; - } - poll_wait(file, &dmabuf->wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!dmabuf->ready && prog_dmabuf(state, 1)) - { - up(&state->sem); - return 0; - } - poll_wait(file, &dmabuf->wait, wait); - } - - up(&state->sem); - - spin_lock_irqsave(&state->card->lock, flags); - trident_update_ptr(state); - if (file->f_mode & FMODE_READ) { - if (dmabuf->count >= (signed)dmabuf->fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (dmabuf->mapped) { - if (dmabuf->count >= (signed)dmabuf->fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&state->card->lock, flags); - - return mask; -} - -static int trident_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct trident_state *state = (struct trident_state *)file->private_data; - struct dmabuf *dmabuf = &state->dmabuf; - int ret = -EINVAL; - unsigned long size; - - VALIDATE_STATE(state); - lock_kernel(); - - /* - * Lock against poll read write or mmap creating buffers. Also lock - * a read or write against an mmap. - */ - - down(&state->sem); - - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(state, 0)) != 0) - goto out; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(state, 1)) != 0) - goto out; - } else - 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; - ret = 0; -out: - up(&state->sem); - unlock_kernel(); - return ret; -} - -static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct trident_state *state = (struct trident_state *)file->private_data; - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret = 0; - - struct trident_card *card = state->card; - - VALIDATE_STATE(state); - mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) || - ((file->f_mode & FMODE_READ) && dmabuf->mapped); -#ifdef DEBUG - printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n", - _IOC_NR(cmd), arg ? *(int *)arg : 0); -#endif - - switch (cmd) - { - case OSS_GETVERSION: - ret = put_user(SOUND_VERSION, (int *)arg); - break; - - case SNDCTL_DSP_RESET: - /* FIXME: spin_lock ? */ - if (file->f_mode & FMODE_WRITE) { - stop_dac(state); - synchronize_irq(); - dmabuf->ready = 0; - dmabuf->swptr = dmabuf->hwptr = 0; - dmabuf->count = dmabuf->total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(state); - synchronize_irq(); - dmabuf->ready = 0; - dmabuf->swptr = dmabuf->hwptr = 0; - dmabuf->count = dmabuf->total_bytes = 0; - } - break; - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - ret = drain_dac(state, file->f_flags & O_NONBLOCK); - break; - - case SNDCTL_DSP_SPEED: /* set smaple rate */ - if (get_user(val, (int *)arg)) - { - ret = -EFAULT; - break; - } - if (val >= 0) { - if (file->f_mode & FMODE_WRITE) { - stop_dac(state); - dmabuf->ready = 0; - spin_lock_irqsave(&state->card->lock, flags); - trident_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); - trident_set_adc_rate(state, val); - spin_unlock_irqrestore(&state->card->lock, flags); - } - } - ret = put_user(dmabuf->rate, (int *)arg); - break; - - case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ - if (get_user(val, (int *)arg)) - { - ret = -EFAULT; - break; - } - lock_set_fmt(state); - if (file->f_mode & FMODE_WRITE) { - stop_dac(state); - dmabuf->ready = 0; - if (val) - dmabuf->fmt |= TRIDENT_FMT_STEREO; - else - dmabuf->fmt &= ~TRIDENT_FMT_STEREO; - } - if (file->f_mode & FMODE_READ) { - stop_adc(state); - dmabuf->ready = 0; - if (val) - dmabuf->fmt |= TRIDENT_FMT_STEREO; - else - dmabuf->fmt &= ~TRIDENT_FMT_STEREO; - } - unlock_set_fmt(state); - break; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf(state, 0))) - ret = val; - else - ret = put_user(dmabuf->fragsize, (int *)arg); - break; - } - if (file->f_mode & FMODE_READ) { - if ((val = prog_dmabuf(state, 1))) - ret = val; - else - ret = put_user(dmabuf->fragsize, (int *)arg); - break; - } - - case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ - ret = put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); - break; - - case SNDCTL_DSP_SETFMT: /* Select sample format */ - if (get_user(val, (int *)arg)) - { - ret = -EFAULT; - break; - } - lock_set_fmt(state); - if (val != AFMT_QUERY) { - if (file->f_mode & FMODE_WRITE) { - stop_dac(state); - dmabuf->ready = 0; - if (val == AFMT_S16_LE) - dmabuf->fmt |= TRIDENT_FMT_16BIT; - else - dmabuf->fmt &= ~TRIDENT_FMT_16BIT; - } - if (file->f_mode & FMODE_READ) { - stop_adc(state); - dmabuf->ready = 0; - if (val == AFMT_S16_LE) - dmabuf->fmt |= TRIDENT_FMT_16BIT; - else - dmabuf->fmt &= ~TRIDENT_FMT_16BIT; - } - } - unlock_set_fmt(state); - ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? - AFMT_S16_LE : AFMT_U8, (int *)arg); - break; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - { - ret = -EFAULT; - break; - } - if (val != 0) { - lock_set_fmt(state); - if (file->f_mode & FMODE_WRITE) { - stop_dac(state); - dmabuf->ready = 0; - - //prevent from memory leak - if ((state->chans_num > 2) && (state->chans_num != val)) { - ali_free_other_states_resources(state); - state->chans_num = 1; - } - - if (val >= 2) - { - - dmabuf->fmt |= TRIDENT_FMT_STEREO; - if ((val == 6) && (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)) { - - if( card->rec_channel_use_count > 0 ) - { - printk(KERN_ERR "trident: Record is working on the card!\n"); - ret = -EBUSY; - break; - } - - ret = ali_setup_multi_channels(state->card, 6); - if (ret < 0) { - unlock_set_fmt(state); - break; - } - down(&state->card->open_sem); - ret = ali_allocate_other_states_resources(state, 6); - if (ret < 0) { - up(&state->card->open_sem); - unlock_set_fmt(state); - break; - } - state->card->multi_channel_use_count ++; - up(&state->card->open_sem); - } - else val = 2; /*yield to 2-channels*/ - } - else - dmabuf->fmt &= ~TRIDENT_FMT_STEREO; - state->chans_num = val; - } - if (file->f_mode & FMODE_READ) { - stop_adc(state); - dmabuf->ready = 0; - if (val >= 2) { - if (!((file->f_mode & FMODE_WRITE) && (val == 6))) - val = 2; - dmabuf->fmt |= TRIDENT_FMT_STEREO; - } - else - dmabuf->fmt &= ~TRIDENT_FMT_STEREO; - state->chans_num = val; - } - unlock_set_fmt(state); - } - ret = put_user(val, (int *)arg); - break; - - case SNDCTL_DSP_POST: - /* Cause the working fragment to be output */ - break; - - case SNDCTL_DSP_SUBDIVIDE: - if (dmabuf->subdivision) - { - ret = -EINVAL; - break; - } - if (get_user(val, (int *)arg)) - { - ret = -EFAULT; - break; - } - if (val != 1 && val != 2 && val != 4) - { - ret = -EINVAL; - break; - } - dmabuf->subdivision = val; - break; - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) - { - ret = -EFAULT; - break; - } - - dmabuf->ossfragshift = val & 0xffff; - dmabuf->ossmaxfrags = (val >> 16) & 0xffff; - if (dmabuf->ossfragshift < 4) - dmabuf->ossfragshift = 4; - if (dmabuf->ossfragshift > 15) - dmabuf->ossfragshift = 15; - if (dmabuf->ossmaxfrags < 4) - dmabuf->ossmaxfrags = 4; - - break; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - { - ret = -EINVAL; - break; - } - if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - { - ret = val; - break; - } - spin_lock_irqsave(&state->card->lock, flags); - trident_update_ptr(state); - abinfo.fragsize = dmabuf->fragsize; - abinfo.bytes = dmabuf->dmasize - dmabuf->count; - abinfo.fragstotal = dmabuf->numfrag; - abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; - spin_unlock_irqrestore(&state->card->lock, flags); - ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - break; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - { - ret = -EINVAL; - break; - } - if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) - { - ret = val; - break; - } - spin_lock_irqsave(&state->card->lock, flags); - trident_update_ptr(state); - abinfo.fragsize = dmabuf->fragsize; - abinfo.bytes = dmabuf->count; - abinfo.fragstotal = dmabuf->numfrag; - abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; - spin_unlock_irqrestore(&state->card->lock, flags); - ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - break; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - break; - - case SNDCTL_DSP_GETCAPS: - ret = put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND, - (int *)arg); - break; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if ((file->f_mode & FMODE_READ) && dmabuf->enable) - val |= PCM_ENABLE_INPUT; - if ((file->f_mode & FMODE_WRITE) && dmabuf->enable) - val |= PCM_ENABLE_OUTPUT; - ret = put_user(val, (int *)arg); - break; - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, (int *)arg)) - { - ret = -EFAULT; - break; - } - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) - break; - start_adc(state); - } else - stop_adc(state); - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) - break; - start_dac(state); - } else - stop_dac(state); - } - break; - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - { - ret = -EINVAL; - break; - } - if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) - { - ret = val; - break; - } - spin_lock_irqsave(&state->card->lock, flags); - trident_update_ptr(state); - cinfo.bytes = dmabuf->total_bytes; - cinfo.blocks = dmabuf->count >> dmabuf->fragshift; - cinfo.ptr = dmabuf->hwptr; - if (dmabuf->mapped) - dmabuf->count &= dmabuf->fragsize-1; - spin_unlock_irqrestore(&state->card->lock, flags); - ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); - break; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - { - ret = -EINVAL; - break; - } - if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - { - ret = val; - break; - } - - spin_lock_irqsave(&state->card->lock, flags); - trident_update_ptr(state); - cinfo.bytes = dmabuf->total_bytes; - cinfo.blocks = dmabuf->count >> dmabuf->fragshift; - cinfo.ptr = dmabuf->hwptr; - if (dmabuf->mapped) - dmabuf->count &= dmabuf->fragsize-1; - spin_unlock_irqrestore(&state->card->lock, flags); - ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo))?-EFAULT:0; - break; - - case SNDCTL_DSP_SETDUPLEX: - ret = -EINVAL; - break; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - { - ret = -EINVAL; - break; - } - if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - { - ret = val; - break; - } - spin_lock_irqsave(&state->card->lock, flags); - trident_update_ptr(state); - val = dmabuf->count; - spin_unlock_irqrestore(&state->card->lock, flags); - ret = put_user(val, (int *)arg); - break; - - case SOUND_PCM_READ_RATE: - ret = put_user(dmabuf->rate, (int *)arg); - break; - - case SOUND_PCM_READ_CHANNELS: - ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1, - (int *)arg); - break; - - case SOUND_PCM_READ_BITS: - ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? - AFMT_S16_LE : AFMT_U8, (int *)arg); - break; - - case SNDCTL_DSP_GETCHANNELMASK: - ret = put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE, - (int *)arg); - break; - - case SNDCTL_DSP_BIND_CHANNEL: - if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) - { - ret = -EINVAL; - break; - } - - if (get_user(val, (int *)arg)) - { - ret = -EFAULT; - break; - } - if (val == DSP_BIND_QUERY) { - val = dmabuf->channel->attribute | 0x3c00; - val = attr2mask[val >> 8]; - } else { - dmabuf->ready = 0; - if (file->f_mode & FMODE_READ) - dmabuf->channel->attribute = (CHANNEL_REC|SRC_ENABLE); - if (file->f_mode & FMODE_WRITE) - dmabuf->channel->attribute = (CHANNEL_SPC_PB|SRC_ENABLE); - dmabuf->channel->attribute |= mask2attr[ffs(val)]; - } - ret = put_user(val, (int *)arg); - break; - - case SNDCTL_DSP_MAPINBUF: - case SNDCTL_DSP_MAPOUTBUF: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_WRITE_FILTER: - case SOUND_PCM_READ_FILTER: - default: - ret = -EINVAL; - break; - - } - return ret; -} - -static int trident_open(struct inode *inode, struct file *file) -{ - int i = 0; - int minor = minor(inode->i_rdev); - struct trident_card *card = devs; - struct trident_state *state = NULL; - struct dmabuf *dmabuf = NULL; - - /* Added by Matt Wu 01-05-2001 */ - if(file->f_mode & FMODE_READ) - { - if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { - if (card->multi_channel_use_count > 0) - return -EBUSY; - } - } - - /* find an available virtual channel (instance of /dev/dsp) */ - while (card != NULL) { - down(&card->open_sem); - if(file->f_mode & FMODE_READ) - { - /* Skip opens on cards that are in 6 channel mode */ - if (card->multi_channel_use_count > 0) - { - up(&card->open_sem); - card = card->next; - continue; - } - } - for (i = 0; i < NR_HW_CH; i++) { - if (card->states[i] == NULL) { - state = card->states[i] = (struct trident_state *) - kmalloc(sizeof(struct trident_state), GFP_KERNEL); - if (state == NULL) { - return -ENOMEM; - } - memset(state, 0, sizeof(struct trident_state)); - init_MUTEX(&state->sem); - dmabuf = &state->dmabuf; - goto found_virt; - } - } - up(&card->open_sem); - card = card->next; - } - /* no more virtual channel avaiable */ - if (!state) { - return -ENODEV; - } - found_virt: - /* found a free virtual channel, allocate hardware channels */ - if(file->f_mode & FMODE_READ) - dmabuf->channel = card->alloc_rec_pcm_channel(card); - else - dmabuf->channel = card->alloc_pcm_channel(card); - - if (dmabuf->channel == NULL) { - kfree (card->states[i]); - card->states[i] = NULL; - return -ENODEV; - } - - /* initialize the virtual channel */ - state->virt = i; - state->card = card; - state->magic = TRIDENT_STATE_MAGIC; - init_waitqueue_head(&dmabuf->wait); - file->private_data = state; - - /* 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 */ - if (file->f_mode & FMODE_WRITE) { - dmabuf->fmt &= ~TRIDENT_FMT_MASK; - if ((minor & 0x0f) == SND_DEV_DSP16) - dmabuf->fmt |= TRIDENT_FMT_16BIT; - dmabuf->ossfragshift = 0; - dmabuf->ossmaxfrags = 0; - dmabuf->subdivision = 0; - if (card->pci_id == PCI_DEVICE_ID_SI_7018) { - /* set default channel attribute to normal playback */ - dmabuf->channel->attribute = CHANNEL_PB; - } - trident_set_dac_rate(state, 8000); - } - - if (file->f_mode & FMODE_READ) { - /* FIXME: Trident 4d can only record in signed 16-bits stereo, 48kHz sample, - to be dealed with in trident_set_adc_rate() ?? */ - dmabuf->fmt &= ~TRIDENT_FMT_MASK; - if ((minor & 0x0f) == SND_DEV_DSP16) - dmabuf->fmt |= TRIDENT_FMT_16BIT; - dmabuf->ossfragshift = 0; - dmabuf->ossmaxfrags = 0; - dmabuf->subdivision = 0; - if (card->pci_id == PCI_DEVICE_ID_SI_7018) { - /* set default channel attribute to 0x8a80, record from - PCM L/R FIFO and mono = (left + right + 1)/2*/ - dmabuf->channel->attribute = - (CHANNEL_REC|PCM_LR|MONO_MIX); - } - trident_set_adc_rate(state, 8000); - - /* Added by Matt Wu 01-05-2001 */ - if(card->pci_id == PCI_DEVICE_ID_ALI_5451) - card->rec_channel_use_count ++; - } - - state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - up(&card->open_sem); - -#ifdef DEBUG - printk(KERN_ERR "trident: open virtual channel %d, hard channel %d\n", - state->virt, dmabuf->channel->num); -#endif - - return 0; -} - -static int trident_release(struct inode *inode, struct file *file) -{ - struct trident_state *state = (struct trident_state *)file->private_data; - struct trident_card *card; - struct dmabuf *dmabuf; - unsigned long flags; - - lock_kernel(); - card = state->card; - dmabuf = &state->dmabuf; - VALIDATE_STATE(state); - - if (file->f_mode & FMODE_WRITE) { - trident_clear_tail(state); - drain_dac(state, file->f_flags & O_NONBLOCK); - } - -#ifdef DEBUG - printk(KERN_ERR "trident: closing virtual channel %d, hard channel %d\n", - state->virt, dmabuf->channel->num); -#endif - - /* stop DMA state machine and free DMA buffers/channels */ - down(&card->open_sem); - - if (file->f_mode & FMODE_WRITE) { - stop_dac(state); - lock_set_fmt(state); - - unlock_set_fmt(state); - dealloc_dmabuf(state); - state->card->free_pcm_channel(state->card, dmabuf->channel->num); - - /* Added by Matt Wu */ - if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { - if (state->chans_num > 2) { - if (card->multi_channel_use_count-- < 0) - card->multi_channel_use_count = 0; - if (card->multi_channel_use_count == 0) - ali_close_multi_channels(); - ali_free_other_states_resources(state); - } - } - } - if (file->f_mode & FMODE_READ) { - stop_adc(state); - dealloc_dmabuf(state); - state->card->free_pcm_channel(state->card, dmabuf->channel->num); - - /* Added by Matt Wu */ - if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { - if( card->rec_channel_use_count-- < 0 ) - card->rec_channel_use_count = 0; - } - } - - card->states[state->virt] = NULL; - kfree(state); - - /* we're covered by the open_sem */ - up(&card->open_sem); - unlock_kernel(); - - return 0; -} - -static /*const*/ struct file_operations trident_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: trident_read, - write: trident_write, - poll: trident_poll, - ioctl: trident_ioctl, - mmap: trident_mmap, - open: trident_open, - release: trident_release, -}; - -/* trident specific AC97 functions */ -/* Write AC97 codec registers */ -static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val) -{ - struct trident_card *card = (struct trident_card *)codec->private_data; - unsigned int address, mask, busy; - unsigned short count = 0xffff; - unsigned long flags; - u32 data; - - data = ((u32) val) << 16; - - switch (card->pci_id) - { - default: - case PCI_DEVICE_ID_SI_7018: - address = SI_AC97_WRITE; - mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY; - if (codec->id) - mask |= SI_AC97_SECONDARY; - busy = SI_AC97_BUSY_WRITE; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - address = DX_ACR0_AC97_W; - mask = busy = DX_AC97_BUSY_WRITE; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - address = NX_ACR1_AC97_W; - mask = NX_AC97_BUSY_WRITE; - if (codec->id) - mask |= NX_AC97_WRITE_SECONDARY; - busy = NX_AC97_BUSY_WRITE; - break; - case PCI_DEVICE_ID_INTERG_5050: - address = SI_AC97_WRITE; - mask = busy = SI_AC97_BUSY_WRITE; - if (codec->id) - mask |= SI_AC97_SECONDARY; - break; - } - - spin_lock_irqsave(&card->lock, flags); - do { - if ((inw(TRID_REG(card, address)) & busy) == 0) - break; - } while (count--); - - - data |= (mask | (reg & AC97_REG_ADDR)); - - if (count == 0) { - printk(KERN_ERR "trident: AC97 CODEC write timed out.\n"); - spin_unlock_irqrestore(&card->lock, flags); - return; - } - - outl(data, TRID_REG(card, address)); - spin_unlock_irqrestore(&card->lock, flags); -} - -/* Read AC97 codec registers */ -static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg) -{ - struct trident_card *card = (struct trident_card *)codec->private_data; - unsigned int address, mask, busy; - unsigned short count = 0xffff; - unsigned long flags; - u32 data; - - switch (card->pci_id) - { - default: - case PCI_DEVICE_ID_SI_7018: - address = SI_AC97_READ; - mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY; - if (codec->id) - mask |= SI_AC97_SECONDARY; - busy = SI_AC97_BUSY_READ; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - address = DX_ACR1_AC97_R; - mask = busy = DX_AC97_BUSY_READ; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - if (codec->id) - address = NX_ACR3_AC97_R_SECONDARY; - else - address = NX_ACR2_AC97_R_PRIMARY; - mask = NX_AC97_BUSY_READ; - busy = NX_AC97_BUSY_READ | NX_AC97_BUSY_DATA; - break; - case PCI_DEVICE_ID_INTERG_5050: - address = SI_AC97_READ; - mask = busy = SI_AC97_BUSY_READ; - if (codec->id) - mask |= SI_AC97_SECONDARY; - break; - } - - data = (mask | (reg & AC97_REG_ADDR)); - - spin_lock_irqsave(&card->lock, flags); - outl(data, TRID_REG(card, address)); - do { - data = inl(TRID_REG(card, address)); - if ((data & busy) == 0) - break; - } while (count--); - spin_unlock_irqrestore(&card->lock, flags); - - if (count == 0) { - printk(KERN_ERR "trident: AC97 CODEC read timed out.\n"); - data = 0; - } - return ((u16) (data >> 16)); -} - -/* Write AC97 codec registers for ALi*/ -static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val) -{ - unsigned int address, mask; - unsigned int wCount1 = 0xffff; - unsigned int wCount2= 0xffff; - unsigned long chk1, chk2; - unsigned long flags; - u32 data; - - data = ((u32) val) << 16; - - if(!card) - BUG(); - - address = ALI_AC97_WRITE; - mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY; - if (secondary) - mask |= ALI_AC97_SECONDARY; - if (card->revision == ALI_5451_V02) - mask |= ALI_AC97_WRITE_MIXER_REGISTER; - - spin_lock_irqsave(&card->lock, flags); - while (wCount1--) { - if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_WRITE) == 0) { - data |= (mask | (reg & AC97_REG_ADDR)); - - chk1 = inl(TRID_REG(card, ALI_STIMER)); - chk2 = inl(TRID_REG(card, ALI_STIMER)); - while (wCount2-- && (chk1 == chk2)) - chk2 = inl(TRID_REG(card, ALI_STIMER)); - if (wCount2 == 0) { - spin_unlock_irqrestore(&card->lock, flags); - return; - } - outl(data, TRID_REG(card, address)); //write! - spin_unlock_irqrestore(&card->lock, flags); - return; //success - } - inw(TRID_REG(card, address)); //wait for a read cycle - } - - printk(KERN_ERR "ali: AC97 CODEC write timed out.\n"); - spin_unlock_irqrestore(&card->lock, flags); - return; -} - -/* Read AC97 codec registers for ALi*/ -static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg) -{ - unsigned int address, mask; - unsigned int wCount1 = 0xffff; - unsigned int wCount2= 0xffff; - unsigned long chk1, chk2; - unsigned long flags; - u32 data; - - if(!card) - BUG(); - - address = ALI_AC97_READ; - if (card->revision == ALI_5451_V02) { - address = ALI_AC97_WRITE; - } - mask = ALI_AC97_READ_ACTION | ALI_AC97_AUDIO_BUSY; - if (secondary) - mask |= ALI_AC97_SECONDARY; - - spin_lock_irqsave(&card->lock, flags); - data = (mask | (reg & AC97_REG_ADDR)); - while (wCount1--) { - if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_READ) == 0) { - chk1 = inl(TRID_REG(card, ALI_STIMER)); - chk2 = inl(TRID_REG(card, ALI_STIMER)); - while (wCount2-- && (chk1 == chk2)) - chk2 = inl(TRID_REG(card, ALI_STIMER)); - if (wCount2 == 0) { - printk(KERN_ERR "ali: AC97 CODEC read timed out.\n"); - spin_unlock_irqrestore(&card->lock, flags); - return 0; - } - outl(data, TRID_REG(card, address)); //read! - wCount2 = 0xffff; - while (wCount2--) { - if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_READ) == 0) { - data = inl(TRID_REG(card, address)); - spin_unlock_irqrestore(&card->lock, flags); - return ((u16) (data >> 16)); - } - } - } - inw(TRID_REG(card, address)); //wait a read cycle - } - spin_unlock_irqrestore(&card->lock, flags); - printk(KERN_ERR "ali: AC97 CODEC read timed out.\n"); - return 0; -} - -static void ali_enable_special_channel(struct trident_state *stat) -{ - struct trident_card *card = stat->card; - unsigned long s_channels; - - s_channels = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); - s_channels |= (1<dmabuf.channel->num); - outl(s_channels, TRID_REG(card, ALI_GLOBAL_CONTROL)); -} - -static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg) -{ - int id; - u16 data; - struct trident_card *card = NULL; - - /* Added by Matt Wu */ - if (!codec) - BUG(); - - card = (struct trident_card *)codec->private_data; - - if(!card->mixer_regs_ready) - return ali_ac97_get(card, codec->id, reg); - - if(codec->id) - id = 1; - else - id = 0; - - data = card->mixer_regs[reg/2][id]; - return data; -} - -static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) -{ - int id; - struct trident_card *card; - - /* Added by Matt Wu */ - if (!codec) - BUG(); - - card = (struct trident_card *)codec->private_data; - - if (!card->mixer_regs_ready) - { - ali_ac97_set(card, codec->id, reg, val); - return; - } - - if(codec->id) - id = 1; - else - id = 0; - - card->mixer_regs[reg/2][id] = val; - ali_ac97_set(card, codec->id, reg, val); -} - -/* -flag: ALI_SPDIF_OUT_TO_SPDIF_OUT - ALI_PCM_TO_SPDIF_OUT -*/ - -static void ali_setup_spdif_out(struct trident_card *card, int flag) -{ - unsigned long spdif; - unsigned char ch; - - char temp; - struct pci_dev *pci_dev = NULL; - - pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev); - if (pci_dev == NULL) - return; - pci_read_config_byte(pci_dev, 0x61, &temp); - temp |= 0x40; - pci_write_config_byte(pci_dev, 0x61, temp); - pci_read_config_byte(pci_dev, 0x7d, &temp); - temp |= 0x01; - pci_write_config_byte(pci_dev, 0x7d, temp); - pci_read_config_byte(pci_dev, 0x7e, &temp); - temp &= (~0x20); - temp |= 0x10; - pci_write_config_byte(pci_dev, 0x7e, temp); - - ch = inb(TRID_REG(card, ALI_SCTRL)); - outb(ch | ALI_SPDIF_OUT_ENABLE, TRID_REG(card, ALI_SCTRL)); - ch = inb(TRID_REG(card, ALI_SPDIF_CTRL)); - outb(ch & ALI_SPDIF_OUT_CH_STATUS, TRID_REG(card, ALI_SPDIF_CTRL)); - - if (flag & ALI_SPDIF_OUT_TO_SPDIF_OUT) { - spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL)); - spdif |= ALI_SPDIF_OUT_CH_ENABLE; - spdif &= ALI_SPDIF_OUT_SEL_SPDIF; - outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); - spdif = inw(TRID_REG(card, ALI_SPDIF_CS)); - if (flag & ALI_SPDIF_OUT_NON_PCM) - spdif |= 0x0002; - else spdif &= (~0x0002); - outw(spdif, TRID_REG(card, ALI_SPDIF_CS)); - } - else { - spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL)); - spdif |= ALI_SPDIF_OUT_SEL_PCM; - outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); - } -} - -static void ali_disable_special_channel(struct trident_card *card, int ch) -{ - unsigned long sc; - - sc = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); - sc &= ~(1 << ch); - outl(sc, TRID_REG(card, ALI_GLOBAL_CONTROL)); -} - -static void ali_disable_spdif_in(struct trident_card *card) -{ - unsigned long spdif; - - spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); - spdif &= (~ALI_SPDIF_IN_SUPPORT); - outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); - - ali_disable_special_channel(card, ALI_SPDIF_IN_CHANNEL); -} - -static void ali_setup_spdif_in(struct trident_card *card) -{ - unsigned long spdif; - - //Set SPDIF IN Supported - spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); - spdif |= ALI_SPDIF_IN_SUPPORT; - outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); - - //Set SPDIF IN Rec - spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); - spdif |= ALI_SPDIF_IN_CH_ENABLE; - outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); - - spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL)); - spdif |= ALI_SPDIF_IN_CH_STATUS; - outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL)); -/* - spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL)); - spdif |= ALI_SPDIF_IN_FUNC_ENABLE; - outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL)); -*/ -} - -static void ali_delay(struct trident_card *card,int interval) -{ - unsigned long begintimer,currenttimer; - - begintimer = inl(TRID_REG(card, ALI_STIMER)); - currenttimer = inl(TRID_REG(card, ALI_STIMER)); - - while (currenttimer < begintimer + interval) - currenttimer = inl(TRID_REG(card, ALI_STIMER)); -} - -static void ali_detect_spdif_rate(struct trident_card *card) -{ - u16 wval = 0; - u16 count = 0; - u8 bval = 0, R1 = 0, R2 = 0; - - bval = inb(TRID_REG(card,ALI_SPDIF_CTRL)); - bval |= 0x02; - outb(bval,TRID_REG(card,ALI_SPDIF_CTRL)); - - bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1)); - bval |= 0x1F; - outb(bval,TRID_REG(card,ALI_SPDIF_CTRL + 1)); - - while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000) - { - count ++; - - ali_delay(card, 6); - - bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1)); - R1 = bval & 0x1F; - } - - if (count > 50000) - { - printk(KERN_WARNING "trident: Error in ali_detect_spdif_rate!\n"); - return; - } - - count = 0; - - while (count <= 50000) - { - count ++; - - ali_delay(card, 6); - - bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1)); - R2 = bval & 0x1F; - - if(R2 != R1) - R1 = R2; - else - break; - } - - if (count > 50000) - { - printk(KERN_WARNING "trident: Error in ali_detect_spdif_rate!\n"); - return; - } - - switch (R2) - { - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - wval = inw(TRID_REG(card,ALI_SPDIF_CTRL + 2)); - wval &= 0xE0F0; - wval |= (u16)0x09 << 8 | (u16)0x05; - outw(wval,TRID_REG(card,ALI_SPDIF_CTRL + 2)); - - bval = inb(TRID_REG(card,ALI_SPDIF_CS +3)) & 0xF0; - outb(bval|0x02,TRID_REG(card,ALI_SPDIF_CS + 3)); - break; - - case 0x12: - wval = inw(TRID_REG(card,ALI_SPDIF_CTRL + 2)); - wval &= 0xE0F0; - wval |= (u16)0x0E << 8 | (u16)0x08; - outw(wval,TRID_REG(card,ALI_SPDIF_CTRL + 2)); - - bval = inb(TRID_REG(card,ALI_SPDIF_CS +3)) & 0xF0; - outb(bval|0x03,TRID_REG(card,ALI_SPDIF_CS + 3)); - break; - - default: - break; - } - -} - -static unsigned int ali_get_spdif_in_rate(struct trident_card *card) -{ - u32 dwRate = 0; - u8 bval = 0; - - ali_detect_spdif_rate(card); - - bval = inb(TRID_REG(card,ALI_SPDIF_CTRL)); - bval &= 0x7F; - bval |= 0x40; - outb(bval,TRID_REG(card,ALI_SPDIF_CTRL)); - - bval = inb(TRID_REG(card,ALI_SPDIF_CS + 3)); - bval &= 0x0F; - - switch (bval) - { - case 0: - dwRate = 44100; - break; - case 1: - dwRate = 48000; - break; - case 2: - dwRate = 32000; - break; - default: - // Error occurs - break; - } - - return dwRate; - -} - -static int ali_close_multi_channels(void) -{ - char temp = 0; - struct pci_dev *pci_dev = NULL; - - 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_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); - - return 0; -} - -static int ali_setup_multi_channels(struct trident_card *card, int chan_nums) -{ - unsigned long dwValue; - char temp = 0; - struct pci_dev *pci_dev = NULL; - - 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_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, (int)0xB8,(u8) temp); - if (chan_nums == 6) { - dwValue = inl(TRID_REG(card, ALI_SCTRL)) | 0x000f0000; - outl(dwValue, TRID_REG(card, ALI_SCTRL)); - mdelay(4); - dwValue = inl(TRID_REG(card, ALI_SCTRL)); - if (dwValue & 0x2000000) { - ali_ac97_write(card->ac97_codec[0], 0x02, 8080); - ali_ac97_write(card->ac97_codec[0], 0x36, 0); - ali_ac97_write(card->ac97_codec[0], 0x38, 0); - ali_ac97_write(card->ac97_codec[1], 0x36, 0); - ali_ac97_write(card->ac97_codec[1], 0x38, 0); - ali_ac97_write(card->ac97_codec[1], 0x02, 0x0606); - ali_ac97_write(card->ac97_codec[1], 0x18, 0x0303); - ali_ac97_write(card->ac97_codec[1], 0x74, 0x3); - return 1; - } - } - return -EINVAL; -} - -static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel) -{ - int bank; - - if (channel > 31) - return; - - bank = channel >> 5; - channel = channel & 0x1f; - - card->banks[bank].bitmap &= ~(1 << (channel)); -} - -static int ali_allocate_other_states_resources(struct trident_state *state, int chan_nums) -{ - struct trident_card *card = state->card; - struct trident_state *s; - int i, state_count = 0; - struct trident_pcm_bank *bank; - struct trident_channel *channel; - - bank = &card->banks[BANK_A]; - - if (chan_nums == 6) { - for(i = 0;(i < ALI_CHANNELS) && (state_count != 4); i++) { - if (!card->states[i]) { - if (!(bank->bitmap & (1 << ali_multi_channels_5_1[state_count]))) { - bank->bitmap |= (1 << ali_multi_channels_5_1[state_count]); - channel = &bank->channels[ali_multi_channels_5_1[state_count]]; - channel->num = ali_multi_channels_5_1[state_count]; - } - else { - state_count--; - for (; state_count >= 0; state_count--) { - kfree(state->other_states[state_count]); - ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); - } - return -EBUSY; - } - s = card->states[i] = (struct trident_state *) - kmalloc(sizeof(struct trident_state), GFP_KERNEL); - if (!s) { - ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); - state_count--; - for (; state_count >= 0; state_count--) { - ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); - kfree(state->other_states[state_count]); - } - return -ENOMEM; - } - memset(s, 0, sizeof(struct trident_state)); - - s->dmabuf.channel = channel; - s->dmabuf.ossfragshift = s->dmabuf.ossmaxfrags = s->dmabuf.subdivision = 0; - init_waitqueue_head(&s->dmabuf.wait); - s->magic = card->magic; - s->card = card; - s->virt = i; - ali_enable_special_channel(s); - state->other_states[state_count++] = s; - } - } - - if (state_count != 4) { - state_count--; - for (; state_count >= 0; state_count--) { - kfree(state->other_states[state_count]); - ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); - } - return -EBUSY; - } - } - return 0; -} - -static void ali_save_regs(struct trident_card *card) -{ - unsigned long flags; - int i, j; - - save_flags(flags); - cli(); - - ali_registers.global_regs[0x2c] = inl(TRID_REG(card,T4D_MISCINT)); - //ali_registers.global_regs[0x20] = inl(TRID_REG(card,T4D_START_A)); - ali_registers.global_regs[0x21] = inl(TRID_REG(card,T4D_STOP_A)); - - //disable all IRQ bits - outl(ALI_DISABLE_ALL_IRQ, TRID_REG(card, T4D_MISCINT)); - - for (i = 1; i < ALI_MIXER_REGS; i++) - ali_registers.mixer_regs[i] = ali_ac97_read (card->ac97_codec[0], i*2); - - for (i = 0; i < ALI_GLOBAL_REGS; i++) - { - if ((i*4 == T4D_MISCINT) || (i*4 == T4D_STOP_A)) - continue; - ali_registers.global_regs[i] = inl(TRID_REG(card, i*4)); - } - - for (i = 0; i < ALI_CHANNELS; i++) - { - outb(i,TRID_REG(card, T4D_LFO_GC_CIR)); - for (j = 0; j < ALI_CHANNEL_REGS; j++) - ali_registers.channel_regs[i][j] = inl(TRID_REG(card, j*4 + 0xe0)); - } - - //Stop all HW channel - outl(ALI_STOP_ALL_CHANNELS, TRID_REG(card, T4D_STOP_A)); - - restore_flags(flags); -} - -static void ali_restore_regs(struct trident_card *card) -{ - unsigned long flags; - int i, j; - - save_flags(flags); - cli(); - - for (i = 1; i < ALI_MIXER_REGS; i++) - ali_ac97_write(card->ac97_codec[0], i*2, ali_registers.mixer_regs[i]); - - for (i = 0; i < ALI_CHANNELS; i++) - { - outb(i,TRID_REG(card, T4D_LFO_GC_CIR)); - for (j = 0; j < ALI_CHANNEL_REGS; j++) - outl(ali_registers.channel_regs[i][j], TRID_REG(card, j*4 + 0xe0)); - } - - for (i = 0; i < ALI_GLOBAL_REGS; i++) - { - if ((i*4 == T4D_MISCINT) || (i*4 == T4D_STOP_A) || (i*4 == T4D_START_A)) - continue; - outl(ali_registers.global_regs[i], TRID_REG(card, i*4)); - } - - //start HW channel - outl(ali_registers.global_regs[0x20], TRID_REG(card,T4D_START_A)); - //restore IRQ enable bits - outl(ali_registers.global_regs[0x2c], TRID_REG(card,T4D_MISCINT)); - - restore_flags(flags); -} - -static int trident_suspend(struct pci_dev *dev, u32 unused) -{ - struct trident_card *card = (struct trident_card *) dev; - - if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { - ali_save_regs(card); - } - return 0; -} - -static int trident_resume(struct pci_dev *dev) -{ - struct trident_card *card = (struct trident_card *) dev; - - if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { - ali_restore_regs(card); - } - return 0; -} - -static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card) -{ - struct trident_pcm_bank *bank; - int idx; - - bank = &card->banks[BANK_A]; - - if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & (ALI_SPDIF_OUT_CH_ENABLE)) { - idx = ALI_SPDIF_OUT_CHANNEL; - if (!(bank->bitmap & (1 << idx))) { - struct trident_channel *channel = &bank->channels[idx]; - bank->bitmap |= 1 << idx; - channel->num = idx; - return channel; - } - } - - for (idx = ALI_PCM_OUT_CHANNEL_FIRST; idx <= ALI_PCM_OUT_CHANNEL_LAST ; idx++) { - if (!(bank->bitmap & (1 << idx))) { - struct trident_channel *channel = &bank->channels[idx]; - bank->bitmap |= 1 << idx; - channel->num = idx; - return channel; - } - } - - /* no more free channels avaliable */ -// printk(KERN_ERR "ali: no more channels available on Bank A.\n"); - return NULL; -} - -static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card) -{ - struct trident_pcm_bank *bank; - int idx; - - if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_IN_SUPPORT) - idx = ALI_SPDIF_IN_CHANNEL; - else idx = ALI_PCM_IN_CHANNEL; - - bank = &card->banks[BANK_A]; - - if (!(bank->bitmap & (1 << idx))) { - struct trident_channel *channel = &bank->channels[idx]; - bank->bitmap |= 1 << idx; - channel->num = idx; - return channel; - } - - /* no free recordable channels avaliable */ -// printk(KERN_ERR "ali: no recordable channels available on Bank A.\n"); - return NULL; -} - -static void ali_set_spdif_out_rate(struct trident_card *card, 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(TRID_REG(card, ALI_SPDIF_CTRL)) & ALI_SPDIF_OUT_CH_STATUS; //select spdif_out - - ch_st_sel |= 0x80; //select right - outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL)); - outb(status_rate | 0x20, TRID_REG(card, ALI_SPDIF_CS + 2)); - - ch_st_sel &= (~0x80); //select left - outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL)); - outw(status_rate | 0x10, TRID_REG(card, ALI_SPDIF_CS + 2)); -} - -static void ali_address_interrupt(struct trident_card *card) -{ - int i, channel; - struct trident_state *state; - u32 mask, channel_mask; - - mask = trident_get_interrupt_mask (card, 0); - for (i = 0; i < NR_HW_CH; i++) { - if ((state = card->states[i]) == NULL) - continue; - channel = state->dmabuf.channel->num; - if ((channel_mask = 1 << channel) & mask) { - mask &= ~channel_mask; - trident_ack_channel_interrupt(card, channel); - udelay(100); - state->dmabuf.update_flag |= ALI_ADDRESS_INT_UPDATE; - trident_update_ptr(state); - } - } - if (mask) { - for (i = 0; i < NR_HW_CH; i++) { - if (mask & (1 << i)) { - printk("ali: spurious channel irq %d.\n", i); - trident_ack_channel_interrupt(card, i); - trident_stop_voice(card, i); - trident_disable_voice_irq(card, i); - } - } - } -} - -/* Updating the values of counters of other_states' DMAs without lock -protection is no harm because all DMAs of multi-channels and interrupt -depend on a master state's DMA, and changing the counters of the master -state DMA is protected by a spinlock. -*/ -static int ali_write_5_1(struct trident_state *state, const char *buf, int cnt_for_multi_channel, unsigned int *copy_count, unsigned int *state_cnt) -{ - - struct dmabuf *dmabuf = &state->dmabuf; - struct dmabuf *dmabuf_temp; - const char *buffer = buf; - unsigned swptr, other_dma_nums, sample_s; - unsigned int i, loop; - - other_dma_nums = 4; - sample_s = sample_size[dmabuf->fmt] >> 1; - swptr = dmabuf->swptr; - - if ((i = state->multi_channels_adjust_count) > 0) { - if (i == 1) { - if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s)) - return -EFAULT; - seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); - i--; - (*state_cnt) += sample_s; - state->multi_channels_adjust_count++; - } - else i = i - (state->chans_num - other_dma_nums); - for (; (i < other_dma_nums) && (cnt_for_multi_channel > 0); i++) { - dmabuf_temp = &state->other_states[i]->dmabuf; - if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) - return -EFAULT; - seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); - } - if (cnt_for_multi_channel == 0) - state->multi_channels_adjust_count += i; - } - if (cnt_for_multi_channel > 0) { - loop = cnt_for_multi_channel / (state->chans_num * sample_s); - for (i = 0; i < loop; i++) { - if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s * 2)) - return -EFAULT; - seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s * 2, *copy_count); - (*state_cnt) += (sample_s * 2); - - dmabuf_temp = &state->other_states[0]->dmabuf; - if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) - return -EFAULT; - seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); - - dmabuf_temp = &state->other_states[1]->dmabuf; - if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) - return -EFAULT; - seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); - - dmabuf_temp = &state->other_states[2]->dmabuf; - if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) - return -EFAULT; - seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); - - dmabuf_temp = &state->other_states[3]->dmabuf; - if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) - return -EFAULT; - seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); - } - - if (cnt_for_multi_channel > 0) { - state->multi_channels_adjust_count = cnt_for_multi_channel / sample_s; - - if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s)) - return -EFAULT; - seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); - (*state_cnt) += sample_s; - - if (cnt_for_multi_channel > 0) { - if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s)) - return -EFAULT; - seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); - (*state_cnt) += sample_s; - - if (cnt_for_multi_channel > 0) { - loop = state->multi_channels_adjust_count - (state->chans_num - other_dma_nums); - for (i = 0; i < loop; i++) { - dmabuf_temp = &state->other_states[i]->dmabuf; - if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) - return -EFAULT; - seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); - } - } - } - } - else - state->multi_channels_adjust_count = 0; - } - for (i = 0; i < other_dma_nums; i++) { - dmabuf_temp = &state->other_states[i]->dmabuf; - dmabuf_temp->swptr = dmabuf_temp->swptr % dmabuf_temp->dmasize; - } - return *state_cnt; -} - -static void ali_free_other_states_resources(struct trident_state *state) -{ - int i; - struct trident_card *card = state->card; - struct trident_state *s; - unsigned other_states_count; - - other_states_count = state->chans_num - 2; /* except PCM L/R channels*/ - for ( i = 0; i < other_states_count; i++) { - s = state->other_states[i]; - dealloc_dmabuf(s); - ali_disable_special_channel(s->card, s->dmabuf.channel->num); - state->card->free_pcm_channel(s->card, s->dmabuf.channel->num); - card->states[s->virt] = NULL; - kfree(s); - } -} - -#ifdef CONFIG_PROC_FS -struct proc_dir_entry *res; -static int ali_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) -{ - struct trident_card *card = (struct trident_card *)data; - unsigned long flags; - char c; - - if (count<0) - return -EINVAL; - if (count == 0) - return 0; - if (get_user(c, buffer)) - return -EFAULT; - - spin_lock_irqsave(&card->lock, flags); - switch (c) { - case '0': - ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT); - ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL); - break; - case '1': - ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT|ALI_SPDIF_OUT_PCM); - break; - case '2': - ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT|ALI_SPDIF_OUT_NON_PCM); - break; - case '3': - ali_disable_spdif_in(card); //default - break; - case '4': - ali_setup_spdif_in(card); - break; - } - spin_unlock_irqrestore(&card->lock, flags); - - return count; -} -#endif - -/* OSS /dev/mixer file operation methods */ -static int trident_open_mixdev(struct inode *inode, struct file *file) -{ - int i = 0; - int minor = minor(inode->i_rdev); - struct trident_card *card = devs; - - for (card = devs; card != NULL; card = card->next) - for (i = 0; i < NR_AC97; i++) - if (card->ac97_codec[i] != NULL && - card->ac97_codec[i]->dev_mixer == minor) - goto match; - - if (!card) { - return -ENODEV; - } - match: - file->private_data = card->ac97_codec[i]; - - - return 0; -} - -static int trident_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 trident_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: trident_ioctl_mixdev, - open: trident_open_mixdev, -}; - -static int ali_reset_5451(struct trident_card *card) -{ - struct pci_dev *pci_dev = NULL; - unsigned int dwVal; - unsigned short wCount, wReg; - - pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev); - if (pci_dev == NULL) - return -1; - - pci_read_config_dword(pci_dev, 0x7c, &dwVal); - pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); - udelay(5000); - pci_read_config_dword(pci_dev, 0x7c, &dwVal); - pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); - udelay(5000); - - pci_dev = card->pci_dev; - if (pci_dev == NULL) - return -1; - - pci_read_config_dword(pci_dev, 0x44, &dwVal); - pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000); - udelay(500); - pci_read_config_dword(pci_dev, 0x44, &dwVal); - pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff); - udelay(5000); - - wCount = 200; - while(wCount--) { - wReg = ali_ac97_get(card, 0, AC97_POWER_CONTROL); - if((wReg & 0x000f) == 0x000f) - return 0; - udelay(500); - } - return 0; -} - -/* AC97 codec initialisation. */ -static int __init trident_ac97_init(struct trident_card *card) -{ - int num_ac97 = 0; - unsigned long ready_2nd = 0; - struct ac97_codec *codec; - int i = 0; - - - /* initialize controller side of AC link, and find out if secondary codes - really exist */ - switch (card->pci_id) - { - case PCI_DEVICE_ID_ALI_5451: - if (ali_reset_5451(card)) - { - printk(KERN_ERR "trident_ac97_init: error resetting 5451.\n"); - return -1; - } - outl(0x80000001,TRID_REG(card, ALI_GLOBAL_CONTROL)); - outl(0x00000000,TRID_REG(card, T4D_AINTEN_A)); - outl(0xffffffff,TRID_REG(card, T4D_AINT_A)); - outl(0x00000000,TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); - outb(0x10, TRID_REG(card, ALI_MPUR2)); - ready_2nd = inl(TRID_REG(card, ALI_SCTRL)); - ready_2nd &= 0x3fff; - outl(ready_2nd | PCMOUT | 0x8000, TRID_REG(card, ALI_SCTRL)); - ready_2nd = inl(TRID_REG(card, ALI_SCTRL)); - ready_2nd &= SI_AC97_SECONDARY_READY; - if (card->revision < ALI_5451_V02) - ready_2nd = 0; - break; - case PCI_DEVICE_ID_SI_7018: - /* disable AC97 GPIO interrupt */ - outl(0x00, TRID_REG(card, SI_AC97_GPIO)); - /* when power up the AC link is in cold reset mode so stop it */ - outl(PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID, - TRID_REG(card, SI_SERIAL_INTF_CTRL)); - /* it take a long time to recover from a cold reset (especially when you have - more than one codec) */ - udelay(2000); - ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL)); - ready_2nd &= SI_AC97_SECONDARY_READY; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - /* playback on */ - outl(DX_AC97_PLAYBACK, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - /* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */ - outl(NX_AC97_PCM_OUTPUT, TRID_REG(card, NX_ACR0_AC97_COM_STAT)); - ready_2nd = inl(TRID_REG(card, NX_ACR0_AC97_COM_STAT)); - ready_2nd &= NX_AC97_SECONDARY_READY; - break; - case PCI_DEVICE_ID_INTERG_5050: - /* disable AC97 GPIO interrupt */ - outl(0x00, TRID_REG(card, SI_AC97_GPIO)); - /* when power up, the AC link is in cold reset mode, so stop it */ - outl(PCMOUT|SURROUT|CENTEROUT|LFEOUT, - TRID_REG(card, SI_SERIAL_INTF_CTRL)); - /* it take a long time to recover from a cold reset (especially when you have - more than one codec) */ - udelay(2000); - ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL)); - ready_2nd &= SI_AC97_SECONDARY_READY; - break; - } - - for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) - return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); - - /* initialize some basic codec information, other fields will be filled - in ac97_probe_codec */ - codec->private_data = card; - codec->id = num_ac97; - - if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { - codec->codec_read = ali_ac97_read; - codec->codec_write = ali_ac97_write; - } - else { - codec->codec_read = trident_ac97_get; - codec->codec_write = trident_ac97_set; - } - - if (ac97_probe_codec(codec) == 0) - break; - - if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) { - printk(KERN_ERR "trident: couldn't register mixer!\n"); - kfree(codec); - break; - } - - card->ac97_codec[num_ac97] = codec; - - /* if there is no secondary codec at all, don't probe any more */ - if (!ready_2nd) - break; - } - - if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { - for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { - if (card->ac97_codec[num_ac97] == NULL) - break; - for (i=0; i<64;i++) - card->mixer_regs[i][num_ac97] = ali_ac97_get(card, num_ac97,i*2); - } - } - return num_ac97+1; -} - -/* 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 trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) -{ - unsigned long iobase; - struct trident_card *card; - u8 bits; - u8 revision; - int i = 0; - u16 temp; - struct pci_dev *pci_dev_m1533 = NULL; - int rc = -ENODEV; - u64 dma_mask; - - if (pci_enable_device(pci_dev)) - goto out; - - if (pci_dev->device == PCI_DEVICE_ID_ALI_5451) - dma_mask = ALI_DMA_MASK; - else - dma_mask = TRIDENT_DMA_MASK; - if (pci_set_dma_mask(pci_dev, dma_mask)) { - printk(KERN_ERR "trident: architecture does not support" - " %s PCI busmaster DMA\n", - pci_dev->device == PCI_DEVICE_ID_ALI_5451 ? - "32-bit" : "30-bit"); - goto out; - } - pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); - - if (pci_id->device == PCI_DEVICE_ID_INTERG_5050) - iobase = pci_resource_start(pci_dev, 1); - else - iobase = pci_resource_start(pci_dev, 0); - - if (!request_region(iobase, 256, card_names[pci_id->driver_data])) { - printk(KERN_ERR "trident: can't allocate I/O space at 0x%4.4lx\n", - iobase); - goto out; - } - - rc = -ENOMEM; - if ((card = kmalloc(sizeof(struct trident_card), GFP_KERNEL)) == NULL) { - printk(KERN_ERR "trident: out of memory\n"); - goto out_release_region; - } - memset(card, 0, sizeof(*card)); - - card->iobase = iobase; - card->pci_dev = pci_dev; - card->pci_id = pci_id->device; - card->revision = revision; - card->irq = pci_dev->irq; - card->next = devs; - card->magic = TRIDENT_CARD_MAGIC; - card->banks[BANK_A].addresses = &bank_a_addrs; - card->banks[BANK_A].bitmap = 0UL; - card->banks[BANK_B].addresses = &bank_b_addrs; - card->banks[BANK_B].bitmap = 0UL; - - init_MUTEX(&card->open_sem); - spin_lock_init(&card->lock); - init_timer(&card->timer); - - devs = card; - - pci_set_master(pci_dev); - - printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n", - card_names[pci_id->driver_data], card->iobase, card->irq); - - if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { - /* ALi channel Management */ - card->alloc_pcm_channel = ali_alloc_pcm_channel; - card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel; - card->free_pcm_channel = ali_free_pcm_channel; - - card->address_interrupt = ali_address_interrupt; - - /* Added by Matt Wu 01-05-2001 for spdif in */ - card->multi_channel_use_count = 0; - card->rec_channel_use_count = 0; - - /* ALi SPDIF OUT function */ - if(card->revision == ALI_5451_V02) { - ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT); -#ifdef CONFIG_PROC_FS - res = create_proc_entry("ALi5451", 0, NULL); - if (res) { - res->write_proc = ali_write_proc; - res->data = card; - } -#endif - } - - /* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */ - card->hwvolctl = 0; - pci_dev_m1533 = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev_m1533); - rc = -ENODEV; - if (pci_dev_m1533 == NULL) - goto out_proc_fs; - pci_read_config_byte(pci_dev_m1533, 0x63, &bits); - if (bits & (1<<5)) - card->hwvolctl = 1; - if (card->hwvolctl) - { - /* Clear m1533 pci cfg 78h bit 30 to zero, which makes - GPIO11/12/13 work as ACGP_UP/DOWN/MUTE. */ - pci_read_config_byte(pci_dev_m1533, 0x7b, &bits); - bits &= 0xbf; /*clear bit 6 */ - pci_write_config_byte(pci_dev_m1533, 0x7b, bits); - } - } - else if(card->pci_id == PCI_DEVICE_ID_INTERG_5050) - { - card->alloc_pcm_channel = cyber_alloc_pcm_channel; - card->alloc_rec_pcm_channel = cyber_alloc_pcm_channel; - card->free_pcm_channel = cyber_free_pcm_channel; - card->address_interrupt = cyber_address_interrupt; - cyber_init_ritual(card); - } - else - { - card->alloc_pcm_channel = trident_alloc_pcm_channel; - card->alloc_rec_pcm_channel = trident_alloc_pcm_channel; - card->free_pcm_channel = trident_free_pcm_channel; - card->address_interrupt = trident_address_interrupt; - } - - /* claim our irq */ - rc = -ENODEV; - if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ, - card_names[pci_id->driver_data], card)) { - printk(KERN_ERR "trident: unable to allocate irq %d\n", card->irq); - goto out_proc_fs; - } - /* register /dev/dsp */ - if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) { - printk(KERN_ERR "trident: couldn't register DSP device!\n"); - goto out_free_irq; - } - card->mixer_regs_ready = 0; - /* initialize AC97 codec and register /dev/mixer */ - if (trident_ac97_init(card) <= 0) { - /* 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); - kfree (card->ac97_codec[i]); - } - } - goto out_unregister_sound_dsp; - } - card->mixer_regs_ready = 1; - outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); - - if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { - /* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */ - if(card->hwvolctl) - { - /* Enable GPIO IRQ (MISCINT bit 18h)*/ - temp = inw(TRID_REG(card, T4D_MISCINT + 2)); - temp |= 0x0004; - outw(temp, TRID_REG(card, T4D_MISCINT + 2)); - - /* Enable H/W Volume Control GLOVAL CONTROL bit 0*/ - temp = inw(TRID_REG(card, ALI_GLOBAL_CONTROL)); - temp |= 0x0001; - outw(temp, TRID_REG(card, ALI_GLOBAL_CONTROL)); - - } - if(card->revision == ALI_5451_V02) - ali_close_multi_channels(); - /* edited by HMSEO for GT sound */ -#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC - { - u16 ac97_data; - extern struct hwrpb_struct *hwrpb; - - if ((hwrpb->sys_type) == 201) { - printk(KERN_INFO "trident: Running on Alpha system type Nautilus\n"); - ac97_data = ali_ac97_get(card, 0, AC97_POWER_CONTROL); - ali_ac97_set(card, 0, AC97_POWER_CONTROL, ac97_data | ALI_EAPD_POWER_DOWN); - } - } -#endif - /* edited by HMSEO for GT sound*/ - } - rc = 0; - pci_set_drvdata(pci_dev, card); - - /* Enable Address Engine Interrupts */ - trident_enable_loop_interrupts(card); -out: return rc; -out_unregister_sound_dsp: - unregister_sound_dsp(card->dev_audio); -out_free_irq: - free_irq(card->irq, card); -out_proc_fs: -#ifdef CONFIG_PROC_FS - if (res) { - remove_proc_entry("ALi5451", NULL); - res = NULL; - } -#endif - kfree(card); - devs = NULL; -out_release_region: - release_region(iobase, 256); - goto out; -} - -static void __exit trident_remove(struct pci_dev *pci_dev) -{ - int i; - struct trident_card *card = pci_get_drvdata(pci_dev); - - /* - * Kill running timers before unload. We can't have them - * going off after rmmod! - */ - if(card->hwvolctl) - del_timer_sync(&card->timer); - - /* ALi S/PDIF and Power Management */ - if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { - ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT); - ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL); - ali_disable_spdif_in(card); -#ifdef CONFIG_PROC_FS - remove_proc_entry("ALi5451", NULL); -#endif - } - - /* Kill interrupts, and SP/DIF */ - trident_disable_loop_interrupts(card); - - /* free hardware resources */ - free_irq(card->irq, card); - 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); - kfree (card->ac97_codec[i]); - } - unregister_sound_dsp(card->dev_audio); - - kfree(card); - - pci_set_drvdata(pci_dev, NULL); -} - -MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho, Ching Ling Lee"); -MODULE_DESCRIPTION("Trident 4DWave/SiS 7018/ALi 5451 and Tvia/IGST CyberPro5050 PCI Audio Driver"); -MODULE_LICENSE("GPL"); - - -#define TRIDENT_MODULE_NAME "trident" - -static struct pci_driver trident_pci_driver = { - name: TRIDENT_MODULE_NAME, - id_table: trident_pci_tbl, - probe: trident_probe, - remove: trident_remove, - suspend: trident_suspend, - resume: trident_resume -}; - -static int __init trident_init_module (void) -{ - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - - printk(KERN_INFO "Trident 4DWave/SiS 7018/ALi 5451,Tvia CyberPro 5050 PCI Audio, version " - DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); - - if (!pci_register_driver(&trident_pci_driver)) { - pci_unregister_driver(&trident_pci_driver); - return -ENODEV; - } - return 0; -} - -static void __exit trident_cleanup_module (void) -{ - pci_unregister_driver(&trident_pci_driver); -} - -module_init(trident_init_module); -module_exit(trident_cleanup_module); diff -Nru a/drivers/sound/trident.h b/drivers/sound/trident.h --- a/drivers/sound/trident.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,362 +0,0 @@ -#ifndef __TRID4DWAVE_H -#define __TRID4DWAVE_H - -/* - * audio@tridentmicro.com - * Fri Feb 19 15:55:28 MST 1999 - * Definitions for Trident 4DWave DX/NX chips - * - * - * 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. - * - */ - -/* PCI vendor and device ID */ -#ifndef PCI_VENDOR_ID_TRIDENT -#define PCI_VENDOR_ID_TRIDENT 0x1023 -#endif - -#ifndef PCI_VENDOR_ID_SI -#define PCI_VENDOR_ID_SI 0x1039 -#endif - -#ifndef PCI_VENDOR_ID_ALI -#define PCI_VENDOR_ID_ALI 0x10b9 -#endif - -#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX -#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 -#endif - -#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX -#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 -#endif - -#ifndef PCI_DEVICE_ID_SI_7018 -#define PCI_DEVICE_ID_SI_7018 0x7018 -#endif - -#ifndef PCI_DEVICE_ID_ALI_5451 -#define PCI_DEVICE_ID_ALI_5451 0x5451 -#endif - -#ifndef PCI_DEVICE_ID_ALI_1533 -#define PCI_DEVICE_ID_ALI_1533 0x1533 -#endif - -#ifndef FALSE -#define FALSE 0 -#define TRUE 1 -#endif - -#define CHANNEL_REGS 5 -#define CHANNEL_START 0xe0 // The first bytes of the contiguous register space. - -#define BANK_A 0 -#define BANK_B 1 -#define NR_BANKS 2 - -#define TRIDENT_FMT_STEREO 0x01 -#define TRIDENT_FMT_16BIT 0x02 -#define TRIDENT_FMT_MASK 0x03 - -#define DAC_RUNNING 0x01 -#define ADC_RUNNING 0x02 - -/* Register Addresses */ - -/* operational registers common to DX, NX, 7018 */ -enum trident_op_registers { - T4D_REC_CH = 0x70, - T4D_START_A = 0x80, T4D_STOP_A = 0x84, - T4D_DLY_A = 0x88, T4D_SIGN_CSO_A = 0x8c, - T4D_CSPF_A = 0x90, T4D_CEBC_A = 0x94, - T4D_AINT_A = 0x98, T4D_EINT_A = 0x9c, - T4D_LFO_GC_CIR = 0xa0, T4D_AINTEN_A = 0xa4, - T4D_MUSICVOL_WAVEVOL = 0xa8, T4D_SBDELTA_DELTA_R = 0xac, - T4D_MISCINT = 0xb0, T4D_START_B = 0xb4, - T4D_STOP_B = 0xb8, T4D_CSPF_B = 0xbc, - T4D_SBBL_SBCL = 0xc0, T4D_SBCTRL_SBE2R_SBDD = 0xc4, - T4D_STIMER = 0xc8, T4D_LFO_B_I2S_DELTA = 0xcc, - T4D_AINT_B = 0xd8, T4D_AINTEN_B = 0xdc, - ALI_MPUR2 = 0x22, ALI_GPIO = 0x7c, - ALI_EBUF1 = 0xf4, - ALI_EBUF2 = 0xf8 -}; - -enum ali_op_registers { - ALI_SCTRL = 0x48, - ALI_GLOBAL_CONTROL = 0xd4, - ALI_STIMER = 0xc8, - ALI_SPDIF_CS = 0x70, - ALI_SPDIF_CTRL = 0x74 -}; - -enum ali_registers_number { - ALI_GLOBAL_REGS = 56, - ALI_CHANNEL_REGS = 8, - ALI_MIXER_REGS = 20 -}; - -enum ali_sctrl_control_bit { - ALI_SPDIF_OUT_ENABLE = 0x20 -}; - -enum ali_global_control_bit { - ALI_SPDIF_OUT_SEL_PCM = 0x00000400, - ALI_SPDIF_IN_SUPPORT = 0x00000800, - ALI_SPDIF_OUT_CH_ENABLE = 0x00008000, - ALI_SPDIF_IN_CH_ENABLE = 0x00080000, - ALI_PCM_IN_DISABLE = 0x7fffffff, - ALI_PCM_IN_ENABLE = 0x80000000, - ALI_SPDIF_IN_CH_DISABLE = 0xfff7ffff, - ALI_SPDIF_OUT_CH_DISABLE = 0xffff7fff, - ALI_SPDIF_OUT_SEL_SPDIF = 0xfffffbff - -}; - -enum ali_spdif_control_bit { - ALI_SPDIF_IN_FUNC_ENABLE = 0x02, - ALI_SPDIF_IN_CH_STATUS = 0x40, - ALI_SPDIF_OUT_CH_STATUS = 0xbf - -}; - -enum ali_control_all { - ALI_DISABLE_ALL_IRQ = 0, - ALI_CHANNELS = 32, - ALI_STOP_ALL_CHANNELS = 0xffffffff, - ALI_MULTI_CHANNELS_START_STOP = 0x07800000 -}; - -enum ali_EMOD_control_bit { - ALI_EMOD_DEC = 0x00000000, - ALI_EMOD_INC = 0x10000000, - ALI_EMOD_Delay = 0x20000000, - ALI_EMOD_Still = 0x30000000 -}; - -enum ali_pcm_in_channel_num { - ALI_NORMAL_CHANNEL = 0, - ALI_SPDIF_OUT_CHANNEL = 15, - ALI_SPDIF_IN_CHANNEL = 19, - ALI_LEF_CHANNEL = 23, - ALI_CENTER_CHANNEL = 24, - ALI_SURR_RIGHT_CHANNEL = 25, - ALI_SURR_LEFT_CHANNEL = 26, - ALI_PCM_IN_CHANNEL = 31 -}; - -enum ali_pcm_out_channel_num { - ALI_PCM_OUT_CHANNEL_FIRST = 0, - ALI_PCM_OUT_CHANNEL_LAST = 31 -}; - -enum ali_ac97_power_control_bit { - ALI_EAPD_POWER_DOWN = 0x8000 -}; - -enum ali_update_ptr_flags { - ALI_ADDRESS_INT_UPDATE = 0x01 -}; - -enum ali_revision { - ALI_5451_V02 = 0x02 -}; - -enum ali_spdif_out_control { - ALI_PCM_TO_SPDIF_OUT = 0, - ALI_SPDIF_OUT_TO_SPDIF_OUT = 1, - ALI_SPDIF_OUT_PCM = 0, - ALI_SPDIF_OUT_NON_PCM = 2 -}; - -/* S/PDIF Operational Registers for 4D-NX */ -enum nx_spdif_registers { - NX_SPCTRL_SPCSO = 0x24, NX_SPLBA = 0x28, - NX_SPESO = 0x2c, NX_SPCSTATUS = 0x64 -}; - -/* OP registers to access each hardware channel */ -enum channel_registers { - CH_DX_CSO_ALPHA_FMS = 0xe0, CH_DX_ESO_DELTA = 0xe8, - CH_DX_FMC_RVOL_CVOL = 0xec, - CH_NX_DELTA_CSO = 0xe0, CH_NX_DELTA_ESO = 0xe8, - CH_NX_ALPHA_FMS_FMC_RVOL_CVOL = 0xec, - CH_LBA = 0xe4, - CH_GVSEL_PAN_VOL_CTRL_EC = 0xf0 -}; - -/* registers to read/write/control AC97 codec */ -enum dx_ac97_registers { - DX_ACR0_AC97_W = 0x40, DX_ACR1_AC97_R = 0x44, - DX_ACR2_AC97_COM_STAT = 0x48 -}; - -enum nx_ac97_registers { - NX_ACR0_AC97_COM_STAT = 0x40, NX_ACR1_AC97_W = 0x44, - NX_ACR2_AC97_R_PRIMARY = 0x48, NX_ACR3_AC97_R_SECONDARY = 0x4c -}; - -enum si_ac97_registers { - SI_AC97_WRITE = 0x40, SI_AC97_READ = 0x44, - SI_SERIAL_INTF_CTRL = 0x48, SI_AC97_GPIO = 0x4c -}; - -enum ali_ac97_registers { - ALI_AC97_WRITE = 0x40, ALI_AC97_READ = 0x44 -}; - -/* Bit mask for operational registers */ -#define AC97_REG_ADDR 0x000000ff - -enum ali_ac97_bits { - ALI_AC97_BUSY_WRITE = 0x8000, ALI_AC97_BUSY_READ = 0x8000, - ALI_AC97_WRITE_ACTION = 0x8000, ALI_AC97_READ_ACTION = 0x8000, - ALI_AC97_AUDIO_BUSY = 0x4000, ALI_AC97_SECONDARY = 0x0080, - ALI_AC97_READ_MIXER_REGISTER = 0xfeff, - ALI_AC97_WRITE_MIXER_REGISTER = 0x0100 -}; - -enum sis7018_ac97_bits { - SI_AC97_BUSY_WRITE = 0x8000, SI_AC97_BUSY_READ = 0x8000, - SI_AC97_AUDIO_BUSY = 0x4000, SI_AC97_MODEM_BUSY = 0x2000, - SI_AC97_SECONDARY = 0x0080 -}; - -enum trident_dx_ac97_bits { - DX_AC97_BUSY_WRITE = 0x8000, DX_AC97_BUSY_READ = 0x8000, - DX_AC97_READY = 0x0010, DX_AC97_RECORD = 0x0008, - DX_AC97_PLAYBACK = 0x0002 -}; - -enum trident_nx_ac97_bits { - /* ACR1-3 */ - NX_AC97_BUSY_WRITE = 0x0800, NX_AC97_BUSY_READ = 0x0800, - NX_AC97_BUSY_DATA = 0x0400, NX_AC97_WRITE_SECONDARY = 0x0100, - /* ACR0 */ - NX_AC97_SECONDARY_READY = 0x0040, NX_AC97_SECONDARY_RECORD = 0x0020, - NX_AC97_SURROUND_OUTPUT = 0x0010, - NX_AC97_PRIMARY_READY = 0x0008, NX_AC97_PRIMARY_RECORD = 0x0004, - NX_AC97_PCM_OUTPUT = 0x0002, - NX_AC97_WARM_RESET = 0x0001 -}; - -enum serial_intf_ctrl_bits { - WARM_REST = 0x00000001, COLD_RESET = 0x00000002, - I2S_CLOCK = 0x00000004, PCM_SEC_AC97= 0x00000008, - AC97_DBL_RATE = 0x00000010, SPDIF_EN = 0x00000020, - I2S_OUTPUT_EN = 0x00000040, I2S_INPUT_EN = 0x00000080, - PCMIN = 0x00000100, LINE1IN = 0x00000200, - MICIN = 0x00000400, LINE2IN = 0x00000800, - HEAD_SET_IN = 0x00001000, GPIOIN = 0x00002000, - /* 7018 spec says id = 01 but the demo board routed to 10 - SECONDARY_ID= 0x00004000, */ - SECONDARY_ID= 0x00004000, - PCMOUT = 0x00010000, SURROUT = 0x00020000, - CENTEROUT = 0x00040000, LFEOUT = 0x00080000, - LINE1OUT = 0x00100000, LINE2OUT = 0x00200000, - GPIOOUT = 0x00400000, - SI_AC97_PRIMARY_READY = 0x01000000, - SI_AC97_SECONDARY_READY = 0x02000000, -}; - -enum global_control_bits { - CHANNLE_IDX = 0x0000003f, PB_RESET = 0x00000100, - PAUSE_ENG = 0x00000200, - OVERRUN_IE = 0x00000400, UNDERRUN_IE = 0x00000800, - ENDLP_IE = 0x00001000, MIDLP_IE = 0x00002000, - ETOG_IE = 0x00004000, - EDROP_IE = 0x00008000, BANK_B_EN = 0x00010000 -}; - -enum channel_control_bits { - CHANNEL_LOOP = 0x00001000, CHANNEL_SIGNED = 0x00002000, - CHANNEL_STEREO = 0x00004000, CHANNEL_16BITS = 0x00008000, -}; - -enum channel_attribute { - /* playback/record select */ - CHANNEL_PB = 0x0000, CHANNEL_SPC_PB = 0x4000, - CHANNEL_REC = 0x8000, CHANNEL_REC_PB = 0xc000, - /* playback destination/record source select */ - MODEM_LINE1 = 0x0000, MODEM_LINE2 = 0x0400, - PCM_LR = 0x0800, HSET = 0x0c00, - I2S_LR = 0x1000, CENTER_LFE = 0x1400, - SURR_LR = 0x1800, SPDIF_LR = 0x1c00, - MIC = 0x1400, - /* mist stuff */ - MONO_LEFT = 0x0000, MONO_RIGHT = 0x0100, - MONO_MIX = 0x0200, SRC_ENABLE = 0x0080, -}; - -enum miscint_bits { - PB_UNDERRUN_IRO = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, - SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, - OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020, - ENVELOPE_IRQ = 0x00000040, ST_IRQ = 0x00000080, - PB_UNDERRUN = 0x00000100, REC_OVERRUN = 0x00000200, - MIXER_UNDERFLOW = 0x00000400, MIXER_OVERFLOW = 0x00000800, - ST_TARGET_REACHED = 0x00008000, PB_24K_MODE = 0x00010000, - ST_IRQ_EN = 0x00800000, ACGPIO_IRQ = 0x01000000 -}; - -#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) ) - -#define CYBER_PORT_AUDIO 0x3CE -#define CYBER_IDX_AUDIO_ENABLE 0x7B -#define CYBER_BMSK_AUDIO_INT_ENABLE 0x09 -#define CYBER_BMSK_AUENZ 0x01 -#define CYBER_BMSK_AUENZ_ENABLE 0x00 -#define CYBER_IDX_IRQ_ENABLE 0x12 - -#define VALIDATE_MAGIC(FOO,MAG) \ -({ \ - if (!(FOO) || (FOO)->magic != MAG) { \ - printk(invalid_magic,__FUNCTION__); \ - return -ENXIO; \ - } \ -}) - -#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,TRIDENT_STATE_MAGIC) -#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,TRIDENT_CARD_MAGIC) - -extern __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; -} - -#endif /* __TRID4DWAVE_H */ - diff -Nru a/drivers/sound/trix.c b/drivers/sound/trix.c --- a/drivers/sound/trix.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,546 +0,0 @@ -/* - * sound/trix.c - * - * Low level driver for the MediaTrix AudioTrix Pro - * (MT-0002-PC Control Chip) - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes - * Alan Cox Modularisation, cleanup. - * Christoph Hellwig Adapted to module_init/module_exit - * Arnaldo C. de Melo Got rid of attach_uart401 - */ - -#include -#include - -#include "sound_config.h" -#include "sb.h" -#include "sound_firmware.h" - -#include "ad1848.h" -#include "mpu401.h" - -#include "trix_boot.h" - -static int kilroy_was_here = 0; /* Don't detect twice */ -static int sb_initialized = 0; -static int mpu_initialized = 0; - -static int *trix_osp = NULL; - -static int mpu = 0; - -static int joystick=0; - -static unsigned char trix_read(int addr) -{ - outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */ - return inb(0x391); /* MT-0002-PC ASIC data */ -} - -static void trix_write(int addr, int data) -{ - outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */ - outb(((unsigned char) data), 0x391); /* MT-0002-PC ASIC data */ -} - -static void download_boot(int base) -{ - int i = 0, n = trix_boot_len; - - if (trix_boot_len == 0) - return; - - trix_write(0xf8, 0x00); /* ??????? */ - outb((0x01), base + 6); /* Clear the internal data pointer */ - outb((0x00), base + 6); /* Restart */ - - /* - * Write the boot code to the RAM upload/download register. - * Each write increments the internal data pointer. - */ - outb((0x01), base + 6); /* Clear the internal data pointer */ - outb((0x1A), 0x390); /* Select RAM download/upload port */ - - for (i = 0; i < n; i++) - outb((trix_boot[i]), 0x391); - for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */ - outb((0x00), 0x391); - outb((0x00), base + 6); /* Reset */ - outb((0x50), 0x390); /* ?????? */ - -} - -static int trix_set_wss_port(struct address_info *hw_config) -{ - unsigned char addr_bits; - - if (check_region(0x390, 2)) - { - printk(KERN_ERR "AudioTrix: Config port I/O conflict\n"); - return 0; - } - if (kilroy_was_here) /* Already initialized */ - return 0; - - if (trix_read(0x15) != 0x71) /* No ASIC signature */ - { - MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n")); - return 0; - } - kilroy_was_here = 1; - - /* - * Reset some registers. - */ - - trix_write(0x13, 0); - trix_write(0x14, 0); - - /* - * Configure the ASIC to place the codec to the proper I/O location - */ - - switch (hw_config->io_base) - { - case 0x530: - addr_bits = 0; - break; - case 0x604: - addr_bits = 1; - break; - case 0xE80: - addr_bits = 2; - break; - case 0xF40: - addr_bits = 3; - break; - default: - return 0; - } - - trix_write(0x19, (trix_read(0x19) & 0x03) | addr_bits); - return 1; -} - -/* - * Probe and attach routines for the Windows Sound System mode of - * AudioTrix Pro - */ - -static int __init probe_trix_wss(struct address_info *hw_config) -{ - int ret; - - /* - * Check if the IO port returns valid signature. The original MS Sound - * system returns 0x04 while some cards (AudioTrix Pro for example) - * return 0x00. - */ - if (check_region(hw_config->io_base, 8)) - { - printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - trix_osp = hw_config->osp; - - if (!trix_set_wss_port(hw_config)) - return 0; - - if ((inb(hw_config->io_base + 3) & 0x3f) != 0x00) - { - MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base)); - return 0; - } - if (hw_config->irq > 11) - { - printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq); - return 0; - } - if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) - { - printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", hw_config->dma); - return 0; - } - if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) - if (hw_config->dma2 != 0 && hw_config->dma2 != 1 && hw_config->dma2 != 3) - { - printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", hw_config->dma2); - return 0; - } - /* - * Check that DMA0 is not in use with a 8 bit board. - */ - - if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) - { - printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n"); - return 0; - } - if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) - { - printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq); - return 0; - } - ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); - - if (ret) - { - if(joystick==1) - trix_write(0x15, 0x80); - request_region(0x390, 2, "AudioTrix"); - } - return ret; -} - -static void __init attach_trix_wss(struct address_info *hw_config) -{ - static unsigned char interrupt_bits[12] = { - 0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x10, 0x18, 0x20 - }; - char bits; - - static unsigned char dma_bits[4] = { - 1, 2, 0, 3 - }; - - int config_port = hw_config->io_base + 0; - int dma1 = hw_config->dma, dma2 = hw_config->dma2; - int old_num_mixers = num_mixers; - - trix_osp = hw_config->osp; - - if (!kilroy_was_here) - { - DDB(printk("AudioTrix: Attach called but not probed yet???\n")); - return; - } - - /* - * Set the IRQ and DMA addresses. - */ - - bits = interrupt_bits[hw_config->irq]; - if (bits == 0) - { - printk("AudioTrix: Bad IRQ (%d)\n", hw_config->irq); - return; - } - outb((bits | 0x40), config_port); - - if (hw_config->dma2 == -1 || hw_config->dma2 == hw_config->dma) - { - bits |= dma_bits[dma1]; - dma2 = dma1; - } - else - { - unsigned char tmp; - - tmp = trix_read(0x13) & ~30; - trix_write(0x13, tmp | 0x80 | (dma1 << 4)); - - tmp = trix_read(0x14) & ~30; - trix_write(0x14, tmp | 0x80 | (dma2 << 4)); - } - - outb((bits), config_port); /* Write IRQ+DMA setup */ - - hw_config->slots[0] = ad1848_init("AudioTrix Pro", hw_config->io_base + 4, - hw_config->irq, - dma1, - dma2, - 0, - hw_config->osp, - THIS_MODULE); - request_region(hw_config->io_base, 4, "MSS config"); - - if (num_mixers > old_num_mixers) /* Mixer got installed */ - { - AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); /* Line in */ - AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); - AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* OPL4 */ - AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM); /* SB */ - } -} - -static int __init probe_trix_sb(struct address_info *hw_config) -{ - - int tmp; - unsigned char conf; - static signed char irq_translate[] = { - -1, -1, -1, 0, 1, 2, -1, 3 - }; - - if (trix_boot_len == 0) - return 0; /* No boot code -> no fun */ - - if (!kilroy_was_here) - return 0; /* AudioTrix Pro has not been detected earlier */ - - if (sb_initialized) - return 0; - - if (check_region(hw_config->io_base, 16)) - { - printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - if ((hw_config->io_base & 0xffffff8f) != 0x200) - return 0; - - tmp = hw_config->irq; - if (tmp > 7) - return 0; - if (irq_translate[tmp] == -1) - return 0; - - tmp = hw_config->dma; - if (tmp != 1 && tmp != 3) - return 0; - - conf = 0x84; /* DMA and IRQ enable */ - conf |= hw_config->io_base & 0x70; /* I/O address bits */ - conf |= irq_translate[hw_config->irq]; - if (hw_config->dma == 3) - conf |= 0x08; - trix_write(0x1b, conf); - - download_boot(hw_config->io_base); - sb_initialized = 1; - - hw_config->name = "AudioTrix SB"; - return sb_dsp_detect(hw_config, 0, 0, NULL); -} - -static void __init attach_trix_sb(struct address_info *hw_config) -{ - extern int sb_be_quiet; - int old_quiet; - - hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING; - - /* Prevent false alarms */ - old_quiet = sb_be_quiet; - sb_be_quiet = 1; - - sb_dsp_init(hw_config, THIS_MODULE); - - sb_be_quiet = old_quiet; -} - -static int __init probe_trix_mpu(struct address_info *hw_config) -{ - unsigned char conf; - static int irq_bits[] = { - -1, -1, -1, 1, 2, 3, -1, 4, -1, 5 - }; - - if (!kilroy_was_here) - { - DDB(printk("Trix: WSS and SB modes must be initialized before MPU\n")); - return 0; /* AudioTrix Pro has not been detected earlier */ - } - if (!sb_initialized) - { - DDB(printk("Trix: SB mode must be initialized before MPU\n")); - return 0; - } - if (mpu_initialized) - { - DDB(printk("Trix: MPU mode already initialized\n")); - return 0; - } - if (hw_config->irq > 9) - { - printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq); - return 0; - } - if (irq_bits[hw_config->irq] == -1) - { - printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq); - return 0; - } - switch (hw_config->io_base) - { - case 0x330: - conf = 0x00; - break; - case 0x370: - conf = 0x04; - break; - case 0x3b0: - conf = 0x08; - break; - case 0x3f0: - conf = 0x0c; - break; - default: - return 0; /* Invalid port */ - } - - conf |= irq_bits[hw_config->irq] << 4; - trix_write(0x19, (trix_read(0x19) & 0x83) | conf); - mpu_initialized = 1; - hw_config->name = "AudioTrix Pro"; - return probe_uart401(hw_config, THIS_MODULE); -} - -static void __exit unload_trix_wss(struct address_info *hw_config) -{ - int dma2 = hw_config->dma2; - - if (dma2 == -1) - dma2 = hw_config->dma; - - release_region(0x390, 2); - release_region(hw_config->io_base, 4); - - ad1848_unload(hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - dma2, - 0); - sound_unload_audiodev(hw_config->slots[0]); -} - -static inline void __exit unload_trix_mpu(struct address_info *hw_config) -{ - unload_uart401(hw_config); -} - -static inline void __exit unload_trix_sb(struct address_info *hw_config) -{ - sb_dsp_unload(hw_config, mpu); -} - -static struct address_info cfg; -static struct address_info cfg2; -static struct address_info cfg_mpu; - -static int sb = 0; -static int fw_load; - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; /* Set this for modules that need it */ -static int __initdata sb_io = -1; -static int __initdata sb_dma = -1; -static int __initdata sb_irq = -1; -static int __initdata mpu_io = -1; -static int __initdata mpu_irq = -1; - -MODULE_PARM(io,"i"); -MODULE_PARM(irq,"i"); -MODULE_PARM(dma,"i"); -MODULE_PARM(dma2,"i"); -MODULE_PARM(sb_io,"i"); -MODULE_PARM(sb_dma,"i"); -MODULE_PARM(sb_irq,"i"); -MODULE_PARM(mpu_io,"i"); -MODULE_PARM(mpu_irq,"i"); -MODULE_PARM(joystick, "i"); - -static int __init init_trix(void) -{ - printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - - cfg2.io_base = sb_io; - cfg2.irq = sb_irq; - cfg2.dma = sb_dma; - - cfg_mpu.io_base = mpu_io; - cfg_mpu.irq = mpu_irq; - - if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { - printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); - return -EINVAL; - } - - if (cfg2.io_base != -1 && (cfg2.irq == -1 || cfg2.dma == -1)) { - printk(KERN_INFO "CONFIG_SB_IRQ and CONFIG_SB_DMA must be specified if SB_IO is set.\n"); - return -EINVAL; - } - if (cfg_mpu.io_base != -1 && cfg_mpu.irq == -1) { - printk(KERN_INFO "CONFIG_MPU_IRQ must be specified if MPU_IO is set.\n"); - return -EINVAL; - } - if (!trix_boot) - { - fw_load = 1; - trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin", - (char **) &trix_boot); - } - if (!probe_trix_wss(&cfg)) - return -ENODEV; - attach_trix_wss(&cfg); - - /* - * We must attach in the right order to get the firmware - * loaded up in time. - */ - - if (cfg2.io_base != -1) { - sb = probe_trix_sb(&cfg2); - if (sb) - attach_trix_sb(&cfg2); - } - - if (cfg_mpu.io_base != -1) - mpu = probe_trix_mpu(&cfg_mpu); - - return 0; -} - -static void __exit cleanup_trix(void) -{ - if (fw_load && trix_boot) - vfree(trix_boot); - if (sb) - unload_trix_sb(&cfg2); - if (mpu) - unload_trix_mpu(&cfg_mpu); - unload_trix_wss(&cfg); -} - -module_init(init_trix); -module_exit(cleanup_trix); - -#ifndef MODULE -static int __init setup_trix (char *str) -{ - /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, mpu_io, mpu_irq */ - int ints[9]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - sb_io = ints[5]; - sb_irq = ints[6]; - sb_dma = ints[6]; - mpu_io = ints[7]; - mpu_irq = ints[8]; - - return 1; -} - -__setup("trix=", setup_trix); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/tuning.h b/drivers/sound/tuning.h --- a/drivers/sound/tuning.h Tue Feb 19 18:08:56 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,29 +0,0 @@ -#ifdef SEQUENCER_C - -unsigned short semitone_tuning[24] = -{ -/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, -/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, -/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 -}; - -unsigned short cent_tuning[100] = -{ -/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, -/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, -/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, -/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, -/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, -/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, -/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, -/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, -/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, -/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, -/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, -/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, -/* 96 */ 10570, 10576, 10582, 10589 -}; -#else -extern unsigned short semitone_tuning[24]; -extern unsigned short cent_tuning[100]; -#endif diff -Nru a/drivers/sound/uart401.c b/drivers/sound/uart401.c --- a/drivers/sound/uart401.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,481 +0,0 @@ -/* - * sound/uart401.c - * - * MPU-401 UART driver (formerly uart401_midi.c) - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * Alan Cox Reformatted, removed sound_mem usage, use normal Linux - * interrupt allocation. Protect against bogus unload - * Fixed to allow IRQ > 15 - * Christoph Hellwig Adapted to module_init/module_exit - * Arnaldo C. de Melo got rid of check_region - * - * Status: - * Untested - */ - -#include -#include - -#include "sound_config.h" - -#include "mpu401.h" - -typedef struct uart401_devc -{ - int base; - int irq; - int *osp; - void (*midi_input_intr) (int dev, unsigned char data); - int opened, disabled; - volatile unsigned char input_byte; - int my_dev; - int share_irq; -} -uart401_devc; - -#define DATAPORT (devc->base) -#define COMDPORT (devc->base+1) -#define STATPORT (devc->base+1) - -static int uart401_status(uart401_devc * devc) -{ - return inb(STATPORT); -} - -#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL)) -#define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY)) - -static void uart401_cmd(uart401_devc * devc, unsigned char cmd) -{ - outb((cmd), COMDPORT); -} - -static int uart401_read(uart401_devc * devc) -{ - return inb(DATAPORT); -} - -static void uart401_write(uart401_devc * devc, unsigned char byte) -{ - outb((byte), DATAPORT); -} - -#define OUTPUT_READY 0x40 -#define INPUT_AVAIL 0x80 -#define MPU_ACK 0xFE -#define MPU_RESET 0xFF -#define UART_MODE_ON 0x3F - -static int reset_uart401(uart401_devc * devc); -static void enter_uart_mode(uart401_devc * devc); - -static void uart401_input_loop(uart401_devc * devc) -{ - int work_limit=30000; - - while (input_avail(devc) && --work_limit) - { - unsigned char c = uart401_read(devc); - - if (c == MPU_ACK) - devc->input_byte = c; - else if (devc->opened & OPEN_READ && devc->midi_input_intr) - devc->midi_input_intr(devc->my_dev, c); - } - if(work_limit==0) - printk(KERN_WARNING "Too much work in interrupt on uart401 (0x%X). UART jabbering ??\n", devc->base); -} - -void uart401intr(int irq, void *dev_id, struct pt_regs *dummy) -{ - uart401_devc *devc = dev_id; - - if (devc == NULL) - { - printk(KERN_ERR "uart401: bad devc\n"); - return; - } - - if (input_avail(devc)) - uart401_input_loop(devc); -} - -static int -uart401_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; - - if (devc->opened) - return -EBUSY; - - /* Flush the UART */ - - while (input_avail(devc)) - uart401_read(devc); - - devc->midi_input_intr = input; - devc->opened = mode; - enter_uart_mode(devc); - devc->disabled = 0; - - return 0; -} - -static void uart401_close(int dev) -{ - uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; - - reset_uart401(devc); - devc->opened = 0; -} - -static int uart401_out(int dev, unsigned char midi_byte) -{ - int timeout; - unsigned long flags; - uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; - - if (devc->disabled) - return 1; - /* - * Test for input since pending input seems to block the output. - */ - - save_flags(flags); - cli(); - - if (input_avail(devc)) - uart401_input_loop(devc); - - restore_flags(flags); - - /* - * Sometimes it takes about 13000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - - if (!output_ready(devc)) - { - printk(KERN_WARNING "uart401: Timeout - Device not responding\n"); - devc->disabled = 1; - reset_uart401(devc); - enter_uart_mode(devc); - return 1; - } - uart401_write(devc, midi_byte); - return 1; -} - -static inline int uart401_start_read(int dev) -{ - return 0; -} - -static inline int uart401_end_read(int dev) -{ - return 0; -} - -static inline void uart401_kick(int dev) -{ -} - -static inline int uart401_buffer_status(int dev) -{ - return 0; -} - -#define MIDI_SYNTH_NAME "MPU-401 UART" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static const struct midi_operations uart401_operations = -{ - owner: THIS_MODULE, - info: {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401}, - converter: &std_midi_synth, - in_info: {0}, - open: uart401_open, - close: uart401_close, - outputc: uart401_out, - start_read: uart401_start_read, - end_read: uart401_end_read, - kick: uart401_kick, - buffer_status: uart401_buffer_status, -}; - -static void enter_uart_mode(uart401_devc * devc) -{ - int ok, timeout; - unsigned long flags; - - save_flags(flags); - cli(); - for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - - devc->input_byte = 0; - uart401_cmd(devc, UART_MODE_ON); - - ok = 0; - for (timeout = 50000; timeout > 0 && !ok; timeout--) - if (devc->input_byte == MPU_ACK) - ok = 1; - else if (input_avail(devc)) - if (uart401_read(devc) == MPU_ACK) - ok = 1; - - restore_flags(flags); -} - -static int reset_uart401(uart401_devc * devc) -{ - int ok, timeout, n; - - /* - * Send the RESET command. Try again if no success at the first time. - */ - - ok = 0; - - for (n = 0; n < 2 && !ok; n++) - { - for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - devc->input_byte = 0; - uart401_cmd(devc, MPU_RESET); - - /* - * Wait at least 25 msec. This method is not accurate so let's make the - * loop bit longer. Cannot sleep since this is called during boot. - */ - - for (timeout = 50000; timeout > 0 && !ok; timeout--) - { - if (devc->input_byte == MPU_ACK) /* Interrupt */ - ok = 1; - else if (input_avail(devc)) - { - if (uart401_read(devc) == MPU_ACK) - ok = 1; - } - } - } - - - if (ok) - { - DEB(printk("Reset UART401 OK\n")); - } - else - DDB(printk("Reset UART401 failed - No hardware detected.\n")); - - if (ok) - uart401_input_loop(devc); /* - * Flush input before enabling interrupts - */ - - return ok; -} - -int probe_uart401(struct address_info *hw_config, struct module *owner) -{ - uart401_devc *devc; - char *name = "MPU-401 (UART) MIDI"; - int ok = 0; - unsigned long flags; - - DDB(printk("Entered probe_uart401()\n")); - - /* Default to "not found" */ - hw_config->slots[4] = -1; - - if (!request_region(hw_config->io_base, 4, "MPU-401 UART")) { - printk(KERN_INFO "uart401: could not request_region(%d, 4)\n", hw_config->io_base); - return 0; - } - - devc = kmalloc(sizeof(uart401_devc), GFP_KERNEL); - if (!devc) { - printk(KERN_WARNING "uart401: Can't allocate memory\n"); - goto cleanup_region; - } - - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->osp = hw_config->osp; - devc->midi_input_intr = NULL; - devc->opened = 0; - devc->input_byte = 0; - devc->my_dev = 0; - devc->share_irq = 0; - - save_flags(flags); - cli(); - ok = reset_uart401(devc); - restore_flags(flags); - - if (!ok) - goto cleanup_devc; - - if (hw_config->name) - name = hw_config->name; - - if (devc->irq < 0) { - devc->share_irq = 1; - devc->irq *= -1; - } else - devc->share_irq = 0; - - if (!devc->share_irq) - if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0) { - printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq); - devc->share_irq = 1; - } - devc->my_dev = sound_alloc_mididev(); - enter_uart_mode(devc); - - if (devc->my_dev == -1) { - printk(KERN_INFO "uart401: Too many midi devices detected\n"); - goto cleanup_irq; - } - conf_printf(name, hw_config); - midi_devs[devc->my_dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL); - if (!midi_devs[devc->my_dev]) { - printk(KERN_ERR "uart401: Failed to allocate memory\n"); - goto cleanup_unload_mididev; - } - memcpy(midi_devs[devc->my_dev], &uart401_operations, sizeof(struct midi_operations)); - - if (owner) - midi_devs[devc->my_dev]->owner = owner; - - midi_devs[devc->my_dev]->devc = devc; - midi_devs[devc->my_dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL); - if (!midi_devs[devc->my_dev]->converter) { - printk(KERN_WARNING "uart401: Failed to allocate memory\n"); - goto cleanup_midi_devs; - } - memcpy(midi_devs[devc->my_dev]->converter, &std_midi_synth, sizeof(struct synth_operations)); - strcpy(midi_devs[devc->my_dev]->info.name, name); - midi_devs[devc->my_dev]->converter->id = "UART401"; - midi_devs[devc->my_dev]->converter->midi_dev = devc->my_dev; - - if (owner) - midi_devs[devc->my_dev]->converter->owner = owner; - - hw_config->slots[4] = devc->my_dev; - sequencer_init(); - devc->opened = 0; - return 1; -cleanup_midi_devs: - kfree(midi_devs[devc->my_dev]); -cleanup_unload_mididev: - sound_unload_mididev(devc->my_dev); -cleanup_irq: - if (!devc->share_irq) - free_irq(devc->irq, devc); -cleanup_devc: - kfree(devc); -cleanup_region: - release_region(hw_config->io_base, 4); - return 0; -} - -void unload_uart401(struct address_info *hw_config) -{ - uart401_devc *devc; - int n=hw_config->slots[4]; - - /* Not set up */ - if(n==-1 || midi_devs[n]==NULL) - return; - - /* Not allocated (erm ??) */ - - devc = midi_devs[hw_config->slots[4]]->devc; - if (devc == NULL) - return; - - reset_uart401(devc); - release_region(hw_config->io_base, 4); - - if (!devc->share_irq) - free_irq(devc->irq, devc); - if (devc) - { - kfree(midi_devs[devc->my_dev]->converter); - kfree(midi_devs[devc->my_dev]); - kfree(devc); - devc = NULL; - } - /* This kills midi_devs[x] */ - sound_unload_mididev(hw_config->slots[4]); -} - -EXPORT_SYMBOL(probe_uart401); -EXPORT_SYMBOL(unload_uart401); -EXPORT_SYMBOL(uart401intr); - -static struct address_info cfg_mpu; - -static int __initdata io = -1; -static int __initdata irq = -1; - -MODULE_PARM(io, "i"); -MODULE_PARM(irq, "i"); - - -static int __init init_uart401(void) -{ - cfg_mpu.irq = irq; - cfg_mpu.io_base = io; - - /* Can be loaded either for module use or to provide functions - to others */ - if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) { - printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997"); - if (!probe_uart401(&cfg_mpu, THIS_MODULE)) - return -ENODEV; - } - - return 0; -} - -static void __exit cleanup_uart401(void) -{ - if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) - unload_uart401(&cfg_mpu); -} - -module_init(init_uart401); -module_exit(cleanup_uart401); - -#ifndef MODULE -static int __init setup_uart401(char *str) -{ - /* io, irq */ - int ints[3]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - - return 1; -} - -__setup("uart401=", setup_uart401); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/uart6850.c b/drivers/sound/uart6850.c --- a/drivers/sound/uart6850.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,364 +0,0 @@ -/* - * sound/uart6850.c - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver. - * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2. - * - * Alan Cox: Updated for new modular code. Removed snd_* irq handling. Now - * uses native linux resources - * Christoph Hellwig: Adapted to module_init/module_exit - * Jeff Garzik: Made it work again, in theory - * FIXME: If the request_irq() succeeds, the probe succeeds. Ug. - * - * Status: Testing required (no shit -jgarzik) - * - * - */ - -#include -#include - -/* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl: - * added 6850 support, used with COVOX SoundMaster II and custom cards. - */ - -#include "sound_config.h" - -static int uart6850_base = 0x330; - -static int *uart6850_osp; - -#define DATAPORT (uart6850_base) -#define COMDPORT (uart6850_base+1) -#define STATPORT (uart6850_base+1) - -static int uart6850_status(void) -{ - return inb(STATPORT); -} - -#define input_avail() (uart6850_status()&INPUT_AVAIL) -#define output_ready() (uart6850_status()&OUTPUT_READY) - -static void uart6850_cmd(unsigned char cmd) -{ - outb(cmd, COMDPORT); -} - -static int uart6850_read(void) -{ - return inb(DATAPORT); -} - -static void uart6850_write(unsigned char byte) -{ - outb(byte, DATAPORT); -} - -#define OUTPUT_READY 0x02 /* Mask for data ready Bit */ -#define INPUT_AVAIL 0x01 /* Mask for Data Send Ready Bit */ - -#define UART_RESET 0x95 -#define UART_MODE_ON 0x03 - -static int uart6850_opened; -static int uart6850_irq; -static int uart6850_detected; -static int my_dev; - -static void (*midi_input_intr) (int dev, unsigned char data); -static void poll_uart6850(unsigned long dummy); - - -static struct timer_list uart6850_timer = { - function: poll_uart6850 -}; - -static void uart6850_input_loop(void) -{ - int count = 10; - - while (count) - { - /* - * Not timed out - */ - if (input_avail()) - { - unsigned char c = uart6850_read(); - count = 100; - if (uart6850_opened & OPEN_READ) - midi_input_intr(my_dev, c); - } - else - { - while (!input_avail() && count) - count--; - } - } -} - -void m6850intr(int irq, void *dev_id, struct pt_regs *dummy) -{ - if (input_avail()) - uart6850_input_loop(); -} - -/* - * It looks like there is no input interrupts in the UART mode. Let's try - * polling. - */ - -static void poll_uart6850(unsigned long dummy) -{ - unsigned long flags; - - if (!(uart6850_opened & OPEN_READ)) - return; /* Device has been closed */ - - save_flags(flags); - cli(); - - if (input_avail()) - uart6850_input_loop(); - - uart6850_timer.expires = 1 + jiffies; - add_timer(&uart6850_timer); - - /* - * Come back later - */ - - restore_flags(flags); -} - -static int uart6850_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - if (uart6850_opened) - { -/* printk("Midi6850: Midi busy\n");*/ - return -EBUSY; - }; - - uart6850_cmd(UART_RESET); - uart6850_input_loop(); - midi_input_intr = input; - uart6850_opened = mode; - poll_uart6850(0); /* - * Enable input polling - */ - - return 0; -} - -static void uart6850_close(int dev) -{ - uart6850_cmd(UART_MODE_ON); - del_timer(&uart6850_timer); - uart6850_opened = 0; -} - -static int uart6850_out(int dev, unsigned char midi_byte) -{ - int timeout; - unsigned long flags; - - /* - * Test for input since pending input seems to block the output. - */ - - save_flags(flags); - cli(); - - if (input_avail()) - uart6850_input_loop(); - - restore_flags(flags); - - /* - * Sometimes it takes about 13000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /* - * Wait - */ - if (!output_ready()) - { - printk(KERN_WARNING "Midi6850: Timeout\n"); - return 0; - } - uart6850_write(midi_byte); - return 1; -} - -static inline int uart6850_command(int dev, unsigned char *midi_byte) -{ - return 1; -} - -static inline int uart6850_start_read(int dev) -{ - return 0; -} - -static inline int uart6850_end_read(int dev) -{ - return 0; -} - -static inline void uart6850_kick(int dev) -{ -} - -static inline int uart6850_buffer_status(int dev) -{ - return 0; /* - * No data in buffers - */ -} - -#define MIDI_SYNTH_NAME "6850 UART Midi" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static struct midi_operations uart6850_operations = -{ - owner: THIS_MODULE, - info: {"6850 UART", 0, 0, SNDCARD_UART6850}, - converter: &std_midi_synth, - in_info: {0}, - open: uart6850_open, - close: uart6850_close, - outputc: uart6850_out, - start_read: uart6850_start_read, - end_read: uart6850_end_read, - kick: uart6850_kick, - command: uart6850_command, - buffer_status: uart6850_buffer_status -}; - - -static void __init attach_uart6850(struct address_info *hw_config) -{ - int ok, timeout; - unsigned long flags; - - if (!uart6850_detected) - return; - - if ((my_dev = sound_alloc_mididev()) == -1) - { - printk(KERN_INFO "uart6850: Too many midi devices detected\n"); - return; - } - uart6850_base = hw_config->io_base; - uart6850_osp = hw_config->osp; - uart6850_irq = hw_config->irq; - - save_flags(flags); - cli(); - - for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /* - * Wait - */ - uart6850_cmd(UART_MODE_ON); - ok = 1; - restore_flags(flags); - - conf_printf("6850 Midi Interface", hw_config); - - std_midi_synth.midi_dev = my_dev; - hw_config->slots[4] = my_dev; - midi_devs[my_dev] = &uart6850_operations; - sequencer_init(); -} - -static inline int reset_uart6850(void) -{ - uart6850_read(); - return 1; /* - * OK - */ -} - -static int __init probe_uart6850(struct address_info *hw_config) -{ - int ok; - - uart6850_osp = hw_config->osp; - uart6850_base = hw_config->io_base; - uart6850_irq = hw_config->irq; - - if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0) - return 0; - - ok = reset_uart6850(); - uart6850_detected = ok; - return ok; -} - -static void __exit unload_uart6850(struct address_info *hw_config) -{ - free_irq(hw_config->irq, NULL); - sound_unload_mididev(hw_config->slots[4]); -} - -static struct address_info cfg_mpu; - -static int __initdata io = -1; -static int __initdata irq = -1; - -MODULE_PARM(io,"i"); -MODULE_PARM(irq,"i"); - -static int __init init_uart6850(void) -{ - cfg_mpu.io_base = io; - cfg_mpu.irq = irq; - - if (cfg_mpu.io_base == -1 || cfg_mpu.irq == -1) { - printk(KERN_INFO "uart6850: irq and io must be set.\n"); - return -EINVAL; - } - - if (probe_uart6850(&cfg_mpu)) - return -ENODEV; - attach_uart6850(&cfg_mpu); - - return 0; -} - -static void __exit cleanup_uart6850(void) -{ - unload_uart6850(&cfg_mpu); -} - -module_init(init_uart6850); -module_exit(cleanup_uart6850); - -#ifndef MODULE -static int __init setup_uart6850(char *str) -{ - /* io, irq */ - int ints[3]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - - return 1; -} -__setup("uart6850=", setup_uart6850); -#endif -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/ulaw.h b/drivers/sound/ulaw.h --- a/drivers/sound/ulaw.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,69 +0,0 @@ -static unsigned char ulaw_dsp[] = { - 3, 7, 11, 15, 19, 23, 27, 31, - 35, 39, 43, 47, 51, 55, 59, 63, - 66, 68, 70, 72, 74, 76, 78, 80, - 82, 84, 86, 88, 90, 92, 94, 96, - 98, 99, 100, 101, 102, 103, 104, 105, - 106, 107, 108, 109, 110, 111, 112, 113, - 113, 114, 114, 115, 115, 116, 116, 117, - 117, 118, 118, 119, 119, 120, 120, 121, - 121, 121, 122, 122, 122, 122, 123, 123, - 123, 123, 124, 124, 124, 124, 125, 125, - 125, 125, 125, 125, 126, 126, 126, 126, - 126, 126, 126, 126, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 253, 249, 245, 241, 237, 233, 229, 225, - 221, 217, 213, 209, 205, 201, 197, 193, - 190, 188, 186, 184, 182, 180, 178, 176, - 174, 172, 170, 168, 166, 164, 162, 160, - 158, 157, 156, 155, 154, 153, 152, 151, - 150, 149, 148, 147, 146, 145, 144, 143, - 143, 142, 142, 141, 141, 140, 140, 139, - 139, 138, 138, 137, 137, 136, 136, 135, - 135, 135, 134, 134, 134, 134, 133, 133, - 133, 133, 132, 132, 132, 132, 131, 131, - 131, 131, 131, 131, 130, 130, 130, 130, - 130, 130, 130, 130, 129, 129, 129, 129, - 129, 129, 129, 129, 129, 129, 129, 129, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, -}; - -static unsigned char dsp_ulaw[] = { - 0, 0, 0, 0, 0, 1, 1, 1, - 1, 2, 2, 2, 2, 3, 3, 3, - 3, 4, 4, 4, 4, 5, 5, 5, - 5, 6, 6, 6, 6, 7, 7, 7, - 7, 8, 8, 8, 8, 9, 9, 9, - 9, 10, 10, 10, 10, 11, 11, 11, - 11, 12, 12, 12, 12, 13, 13, 13, - 13, 14, 14, 14, 14, 15, 15, 15, - 15, 16, 16, 17, 17, 18, 18, 19, - 19, 20, 20, 21, 21, 22, 22, 23, - 23, 24, 24, 25, 25, 26, 26, 27, - 27, 28, 28, 29, 29, 30, 30, 31, - 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, - 47, 49, 51, 53, 55, 57, 59, 61, - 63, 66, 70, 74, 78, 84, 92, 104, - 254, 231, 219, 211, 205, 201, 197, 193, - 190, 188, 186, 184, 182, 180, 178, 176, - 175, 174, 173, 172, 171, 170, 169, 168, - 167, 166, 165, 164, 163, 162, 161, 160, - 159, 159, 158, 158, 157, 157, 156, 156, - 155, 155, 154, 154, 153, 153, 152, 152, - 151, 151, 150, 150, 149, 149, 148, 148, - 147, 147, 146, 146, 145, 145, 144, 144, - 143, 143, 143, 143, 142, 142, 142, 142, - 141, 141, 141, 141, 140, 140, 140, 140, - 139, 139, 139, 139, 138, 138, 138, 138, - 137, 137, 137, 137, 136, 136, 136, 136, - 135, 135, 135, 135, 134, 134, 134, 134, - 133, 133, 133, 133, 132, 132, 132, 132, - 131, 131, 131, 131, 130, 130, 130, 130, - 129, 129, 129, 129, 128, 128, 128, 128, -}; diff -Nru a/drivers/sound/v_midi.c b/drivers/sound/v_midi.c --- a/drivers/sound/v_midi.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,290 +0,0 @@ -/* - * sound/v_midi.c - * - * The low level driver for the Sound Blaster DS chips. - * - * - * Copyright (C) by Hannu Savolainen 1993-1996 - * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * ?? - * - * Changes - * Alan Cox Modularisation, changed memory allocations - * Christoph Hellwig Adapted to module_init/module_exit - * - * Status - * Untested - */ - -#include -#include - -#include "sound_config.h" - -#include "v_midi.h" - -static vmidi_devc *v_devc[2] = { NULL, NULL}; -static int midi1,midi2; -static void *midi_mem = NULL; - -/* - * The DSP channel can be used either for input or output. Variable - * 'sb_irq_mode' will be set when the program calls read or write first time - * after open. Current version doesn't support mode changes without closing - * and reopening the device. Support for this feature may be implemented in a - * future version of this driver. - */ - - -void (*midi_input_intr) (int dev, unsigned char data); - -static int v_midi_open (int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - vmidi_devc *devc = midi_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - return -(ENXIO); - - save_flags (flags); - cli(); - if (devc->opened) - { - restore_flags (flags); - return -(EBUSY); - } - devc->opened = 1; - restore_flags (flags); - - devc->intr_active = 1; - - if (mode & OPEN_READ) - { - devc->input_opened = 1; - devc->midi_input_intr = input; - } - - return 0; -} - -static void v_midi_close (int dev) -{ - vmidi_devc *devc = midi_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - return; - - save_flags (flags); - cli (); - devc->intr_active = 0; - devc->input_opened = 0; - devc->opened = 0; - restore_flags (flags); -} - -static int v_midi_out (int dev, unsigned char midi_byte) -{ - vmidi_devc *devc = midi_devs[dev]->devc; - vmidi_devc *pdevc = midi_devs[devc->pair_mididev]->devc; - - if (devc == NULL) - return -(ENXIO); - - if (pdevc->input_opened > 0){ - if (MIDIbuf_avail(pdevc->my_mididev) > 500) - return 0; - pdevc->midi_input_intr (pdevc->my_mididev, midi_byte); - } - return 1; -} - -static inline int v_midi_start_read (int dev) -{ - return 0; -} - -static int v_midi_end_read (int dev) -{ - vmidi_devc *devc = midi_devs[dev]->devc; - if (devc == NULL) - return -ENXIO; - - devc->intr_active = 0; - return 0; -} - -/* why -EPERM and not -EINVAL?? */ - -static inline int v_midi_ioctl (int dev, unsigned cmd, caddr_t arg) -{ - return -EPERM; -} - - -#define MIDI_SYNTH_NAME "Loopback MIDI" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT - -#include "midi_synth.h" - -static struct midi_operations v_midi_operations = -{ - owner: THIS_MODULE, - info: {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI}, - converter: &std_midi_synth, - in_info: {0}, - open: v_midi_open, - close: v_midi_close, - ioctl: v_midi_ioctl, - outputc: v_midi_out, - start_read: v_midi_start_read, - end_read: v_midi_end_read, -}; - -static struct midi_operations v_midi_operations2 = -{ - owner: THIS_MODULE, - info: {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI}, - converter: &std_midi_synth, - in_info: {0}, - open: v_midi_open, - close: v_midi_close, - ioctl: v_midi_ioctl, - outputc: v_midi_out, - start_read: v_midi_start_read, - end_read: v_midi_end_read, -}; - -/* - * We kmalloc just one of these - it makes life simpler and the code - * cleaner and the memory handling far more efficient - */ - -struct vmidi_memory -{ - /* Must be first */ - struct midi_operations m_ops[2]; - struct synth_operations s_ops[2]; - struct vmidi_devc v_ops[2]; -}; - -static void __init attach_v_midi (struct address_info *hw_config) -{ - struct vmidi_memory *m; - /* printk("Attaching v_midi device.....\n"); */ - - midi1 = sound_alloc_mididev(); - if (midi1 == -1) - { - printk(KERN_ERR "v_midi: Too many midi devices detected\n"); - return; - } - - m=(struct vmidi_memory *)kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL); - if (m == NULL) - { - printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n"); - sound_unload_mididev(midi1); - return; - } - - midi_mem = m; - - midi_devs[midi1] = &m->m_ops[0]; - - - midi2 = sound_alloc_mididev(); - if (midi2 == -1) - { - printk (KERN_ERR "v_midi: Too many midi devices detected\n"); - kfree(m); - sound_unload_mididev(midi1); - return; - } - - midi_devs[midi2] = &m->m_ops[1]; - - /* printk("VMIDI1: %d VMIDI2: %d\n",midi1,midi2); */ - - /* for MIDI-1 */ - v_devc[0] = &m->v_ops[0]; - memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations, - sizeof (struct midi_operations)); - - v_devc[0]->my_mididev = midi1; - v_devc[0]->pair_mididev = midi2; - v_devc[0]->opened = v_devc[0]->input_opened = 0; - v_devc[0]->intr_active = 0; - v_devc[0]->midi_input_intr = NULL; - - midi_devs[midi1]->devc = v_devc[0]; - - midi_devs[midi1]->converter = &m->s_ops[0]; - std_midi_synth.midi_dev = midi1; - memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth, - sizeof (struct synth_operations)); - midi_devs[midi1]->converter->id = "V_MIDI 1"; - - /* for MIDI-2 */ - v_devc[1] = &m->v_ops[1]; - - memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2, - sizeof (struct midi_operations)); - - v_devc[1]->my_mididev = midi2; - v_devc[1]->pair_mididev = midi1; - v_devc[1]->opened = v_devc[1]->input_opened = 0; - v_devc[1]->intr_active = 0; - v_devc[1]->midi_input_intr = NULL; - - midi_devs[midi2]->devc = v_devc[1]; - midi_devs[midi2]->converter = &m->s_ops[1]; - - std_midi_synth.midi_dev = midi2; - memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth, - sizeof (struct synth_operations)); - midi_devs[midi2]->converter->id = "V_MIDI 2"; - - sequencer_init(); - /* printk("Attached v_midi device\n"); */ -} - -static inline int __init probe_v_midi(struct address_info *hw_config) -{ - return(1); /* always OK */ -} - - -static void __exit unload_v_midi(struct address_info *hw_config) -{ - sound_unload_mididev(midi1); - sound_unload_mididev(midi2); - kfree(midi_mem); -} - -static struct address_info cfg; /* dummy */ - -static int __init init_vmidi(void) -{ - printk("MIDI Loopback device driver\n"); - if (!probe_v_midi(&cfg)) - return -ENODEV; - attach_v_midi(&cfg); - - return 0; -} - -static void __exit cleanup_vmidi(void) -{ - unload_v_midi(&cfg); -} - -module_init(init_vmidi); -module_exit(cleanup_vmidi); -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/v_midi.h b/drivers/sound/v_midi.h --- a/drivers/sound/v_midi.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,15 +0,0 @@ -typedef struct vmidi_devc { - int dev; - - /* State variables */ - int opened; - - - /* MIDI fields */ - int my_mididev; - int pair_mididev; - int input_opened; - int intr_active; - void (*midi_input_intr) (int dev, unsigned char data); - } vmidi_devc; - diff -Nru a/drivers/sound/via82cxxx_audio.c b/drivers/sound/via82cxxx_audio.c --- a/drivers/sound/via82cxxx_audio.c Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3541 +0,0 @@ -/* - * Support for VIA 82Cxxx Audio Codecs - * Copyright 1999,2000 Jeff Garzik - * - * Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2. - * See the "COPYING" file distributed with this software for more info. - * - * 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" - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sound_config.h" -#include "dev_table.h" -#include "mpu401.h" - - -#undef VIA_DEBUG /* define to enable debugging output and checks */ -#ifdef VIA_DEBUG -/* note: prints function name for you */ -#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -#define DPRINTK(fmt, args...) -#endif - -#undef VIA_NDEBUG /* define to disable lightweight runtime checks */ -#ifdef VIA_NDEBUG -#define assert(expr) -#else -#define assert(expr) \ - if(!(expr)) { \ - printk( "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ - } -#endif - -#if defined(CONFIG_PROC_FS) && \ - defined(CONFIG_SOUND_VIA82CXXX_PROCFS) -#define VIA_PROC_FS 1 -#endif - -#define VIA_SUPPORT_MMAP 1 /* buggy, for now... */ - -#define MAX_CARDS 1 - -#define VIA_CARD_NAME "VIA 82Cxxx Audio driver " VIA_VERSION -#define VIA_MODULE_NAME "via82cxxx" -#define PFX VIA_MODULE_NAME ": " - -#define VIA_COUNTER_LIMIT 100000 - -/* size of DMA buffers */ -#define VIA_MAX_BUFFER_DMA_PAGES 32 - -/* buffering default values in ms */ -#define VIA_DEFAULT_FRAG_TIME 20 -#define VIA_DEFAULT_BUFFER_TIME 500 - -#define VIA_MAX_FRAG_SIZE PAGE_SIZE -#define VIA_MIN_FRAG_SIZE 64 - -#define VIA_MIN_FRAG_NUMBER 2 - -/* 82C686 function 5 (audio codec) PCI configuration registers */ -#define VIA_ACLINK_STATUS 0x40 -#define VIA_ACLINK_CTRL 0x41 -#define VIA_FUNC_ENABLE 0x42 -#define VIA_PNP_CONTROL 0x43 -#define VIA_FM_NMI_CTRL 0x48 - -/* - * controller base 0 (scatter-gather) registers - * - * NOTE: Via datasheet lists first channel as "read" - * channel and second channel as "write" channel. - * I changed the naming of the constants to be more - * clear than I felt the datasheet to be. - */ - -#define VIA_BASE0_PCM_OUT_CHAN 0x00 /* output PCM to user */ -#define VIA_BASE0_PCM_OUT_CHAN_STATUS 0x00 -#define VIA_BASE0_PCM_OUT_CHAN_CTRL 0x01 -#define VIA_BASE0_PCM_OUT_CHAN_TYPE 0x02 - -#define VIA_BASE0_PCM_IN_CHAN 0x10 /* input PCM from user */ -#define VIA_BASE0_PCM_IN_CHAN_STATUS 0x10 -#define VIA_BASE0_PCM_IN_CHAN_CTRL 0x11 -#define VIA_BASE0_PCM_IN_CHAN_TYPE 0x12 - -/* offsets from base */ -#define VIA_PCM_STATUS 0x00 -#define VIA_PCM_CONTROL 0x01 -#define VIA_PCM_TYPE 0x02 -#define VIA_PCM_TABLE_ADDR 0x04 -#define VIA_PCM_BLOCK_COUNT 0x0C - -/* XXX unused DMA channel for FM PCM data */ -#define VIA_BASE0_FM_OUT_CHAN 0x20 -#define VIA_BASE0_FM_OUT_CHAN_STATUS 0x20 -#define VIA_BASE0_FM_OUT_CHAN_CTRL 0x21 -#define VIA_BASE0_FM_OUT_CHAN_TYPE 0x22 - -#define VIA_BASE0_AC97_CTRL 0x80 -#define VIA_BASE0_SGD_STATUS_SHADOW 0x84 -#define VIA_BASE0_GPI_INT_ENABLE 0x8C -#define VIA_INTR_OUT ((1<<0) | (1<<4) | (1<<8)) -#define VIA_INTR_IN ((1<<1) | (1<<5) | (1<<9)) -#define VIA_INTR_FM ((1<<2) | (1<<6) | (1<<10)) -#define VIA_INTR_MASK (VIA_INTR_OUT | VIA_INTR_IN | VIA_INTR_FM) - -/* 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 */ -#define VIA_INT_SEL_PCI_LAST_LINE_READ (0) /* int at PCI read of last line */ -#define VIA_INT_SEL_LAST_SAMPLE_SENT (1<<2) /* int at last sample sent */ -#define VIA_INT_SEL_ONE_LINE_LEFT (1<<3) /* int at less than one line to send */ -#define VIA_PCM_FMT_STEREO (1<<4) /* PCM stereo format (bit clear == mono) */ -#define VIA_PCM_FMT_16BIT (1<<5) /* PCM 16-bit format (bit clear == 8-bit) */ -#define VIA_PCM_REC_FIFO (1<<6) /* PCM Recording FIFO */ -#define VIA_RESTART_SGD_ON_EOL (1<<7) /* restart scatter-gather at EOL */ -#define VIA_PCM_FMT_MASK (VIA_PCM_FMT_STEREO|VIA_PCM_FMT_16BIT) -#define VIA_CHAN_TYPE_MASK (VIA_RESTART_SGD_ON_EOL | \ - VIA_IRQ_ON_FLAG | \ - VIA_IRQ_ON_EOL) -#define VIA_CHAN_TYPE_INT_SELECT (VIA_INT_SEL_LAST_SAMPLE_SENT) - -/* PCI configuration register bits and masks */ -#define VIA_CR40_AC97_READY 0x01 -#define VIA_CR40_AC97_LOW_POWER 0x02 -#define VIA_CR40_SECONDARY_READY 0x04 - -#define VIA_CR41_AC97_ENABLE 0x80 /* enable AC97 codec */ -#define VIA_CR41_AC97_RESET 0x40 /* clear bit to reset AC97 */ -#define VIA_CR41_AC97_WAKEUP 0x20 /* wake up from power-down mode */ -#define VIA_CR41_AC97_SDO 0x10 /* force Serial Data Out (SDO) high */ -#define VIA_CR41_VRA 0x08 /* enable variable sample rate */ -#define VIA_CR41_PCM_ENABLE 0x04 /* AC Link SGD Read Channel PCM Data Output */ -#define VIA_CR41_FM_PCM_ENABLE 0x02 /* AC Link FM Channel PCM Data Out */ -#define VIA_CR41_SB_PCM_ENABLE 0x01 /* AC Link SB PCM Data Output */ -#define VIA_CR41_BOOT_MASK (VIA_CR41_AC97_ENABLE | \ - VIA_CR41_AC97_WAKEUP | \ - VIA_CR41_AC97_SDO) -#define VIA_CR41_RUN_MASK (VIA_CR41_AC97_ENABLE | \ - VIA_CR41_AC97_RESET | \ - VIA_CR41_VRA | \ - VIA_CR41_PCM_ENABLE) - -#define VIA_CR42_SB_ENABLE 0x01 -#define VIA_CR42_MIDI_ENABLE 0x02 -#define VIA_CR42_FM_ENABLE 0x04 -#define VIA_CR42_GAME_ENABLE 0x08 -#define VIA_CR42_MIDI_IRQMASK 0x40 -#define VIA_CR42_MIDI_PNP 0x80 - -#define VIA_CR44_SECOND_CODEC_SUPPORT (1 << 6) -#define VIA_CR44_AC_LINK_ACCESS (1 << 7) - -#define VIA_CR48_FM_TRAP_TO_NMI (1 << 2) - -/* controller base 0 register bitmasks */ -#define VIA_INT_DISABLE_MASK (~(0x01|0x02)) -#define VIA_SGD_STOPPED (1 << 2) -#define VIA_SGD_PAUSED (1 << 6) -#define VIA_SGD_ACTIVE (1 << 7) -#define VIA_SGD_TERMINATE (1 << 6) -#define VIA_SGD_FLAG (1 << 0) -#define VIA_SGD_EOL (1 << 1) -#define VIA_SGD_START (1 << 7) - -#define VIA_CR80_FIRST_CODEC 0 -#define VIA_CR80_SECOND_CODEC (1 << 30) -#define VIA_CR80_FIRST_CODEC_VALID (1 << 25) -#define VIA_CR80_VALID (1 << 25) -#define VIA_CR80_SECOND_CODEC_VALID (1 << 27) -#define VIA_CR80_BUSY (1 << 24) -#define VIA_CR83_BUSY (1) -#define VIA_CR83_FIRST_CODEC_VALID (1 << 1) -#define VIA_CR80_READ (1 << 23) -#define VIA_CR80_WRITE_MODE 0 -#define VIA_CR80_REG_IDX(idx) ((((idx) & 0xFF) >> 1) << 16) - -/* capabilities we announce */ -#ifdef VIA_SUPPORT_MMAP -#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | DSP_CAP_MMAP | \ - DSP_CAP_TRIGGER | DSP_CAP_REALTIME) -#else -#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | \ - DSP_CAP_TRIGGER | DSP_CAP_REALTIME) -#endif - -/* scatter-gather DMA table entry, exactly as passed to hardware */ -struct via_sgd_table { - u32 addr; - u32 count; /* includes additional VIA_xxx bits also */ -}; - -#define VIA_EOL (1 << 31) -#define VIA_FLAG (1 << 30) -#define VIA_STOP (1 << 29) - - -enum via_channel_states { - sgd_stopped = 0, - sgd_in_progress = 1, -}; - - -struct via_buffer_pgtbl { - dma_addr_t handle; - void *cpuaddr; -}; - - -struct via_channel { - atomic_t n_frags; - atomic_t hw_ptr; - wait_queue_head_t wait; - - unsigned int sw_ptr; - unsigned int slop_len; - unsigned int n_irqs; - int bytes; - - unsigned is_active : 1; - unsigned is_record : 1; - unsigned is_mapped : 1; - unsigned is_enabled : 1; - u8 pcm_fmt; /* VIA_PCM_FMT_xxx */ - - unsigned rate; /* sample rate */ - unsigned int frag_size; - unsigned int frag_number; - - volatile struct via_sgd_table *sgtable; - dma_addr_t sgt_handle; - - unsigned int page_number; - struct via_buffer_pgtbl pgtbl[VIA_MAX_BUFFER_DMA_PAGES]; - - long iobase; - - const char *name; -}; - - -/* data stored for each chip */ -struct via_info { - struct pci_dev *pdev; - long baseaddr; - - struct ac97_codec ac97; - 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; - - int locked_rate : 1; - - struct semaphore syscall_sem; - struct semaphore open_sem; - - struct via_channel ch_in; - struct via_channel ch_out; - struct via_channel ch_fm; - -#ifdef CONFIG_MIDI_VIA82CXXX - void *midi_devc; - struct address_info midi_info; -#endif -}; - - -/* number of cards, used for assigning unique numbers to cards */ -static unsigned via_num_cards = 0; - - - -/**************************************************************** - * - * prototypes - * - * - */ - -static int via_init_one (struct pci_dev *dev, const struct pci_device_id *id); -static void via_remove_one (struct pci_dev *pdev); - -static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos); -static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos); -static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait); -static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); -static int via_dsp_open (struct inode *inode, struct file *file); -static int via_dsp_release(struct inode *inode, struct file *file); -static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma); - -static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg); -static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value); -static u8 via_ac97_wait_idle (struct via_info *card); - -static void via_chan_free (struct via_info *card, struct via_channel *chan); -static void via_chan_clear (struct via_info *card, struct via_channel *chan); -static void via_chan_pcm_fmt (struct via_channel *chan, int reset); -static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan); - -#ifdef VIA_PROC_FS -static int via_init_proc (void); -static void via_cleanup_proc (void); -static int via_card_init_proc (struct via_info *card); -static void via_card_cleanup_proc (struct via_info *card); -#else -static inline int via_init_proc (void) { return 0; } -static inline void via_cleanup_proc (void) {} -static inline int via_card_init_proc (struct via_info *card) { return 0; } -static inline void via_card_cleanup_proc (struct via_info *card) {} -#endif - - -/**************************************************************** - * - * Various data the driver needs - * - * - */ - - -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_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, - PCI_ANY_ID, PCI_ANY_ID, }, - { 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: via_remove_one, -}; - - -/**************************************************************** - * - * Low-level base 0 register read/write helpers - * - * - */ - -/** - * via_chan_stop - Terminate DMA on specified PCM channel - * @iobase: PCI base address for SGD channel registers - * - * Terminate scatter-gather DMA operation for given - * channel (derived from @iobase), if DMA is active. - * - * Note that @iobase is not the PCI base address, - * but the PCI base address plus an offset to - * one of three PCM channels supported by the chip. - * - */ - -static inline void via_chan_stop (long iobase) -{ - if (inb (iobase + VIA_PCM_STATUS) & VIA_SGD_ACTIVE) - outb (VIA_SGD_TERMINATE, iobase + VIA_PCM_CONTROL); -} - - -/** - * via_chan_status_clear - Clear status flags on specified DMA channel - * @iobase: PCI base address for SGD channel registers - * - * Clear any pending status flags for the given - * DMA channel (derived from @iobase), if any - * flags are asserted. - * - * Note that @iobase is not the PCI base address, - * but the PCI base address plus an offset to - * one of three PCM channels supported by the chip. - * - */ - -static inline void via_chan_status_clear (long iobase) -{ - u8 tmp = inb (iobase + VIA_PCM_STATUS); - - if (tmp != 0) - outb (tmp, iobase + VIA_PCM_STATUS); -} - - -/** - * sg_begin - Begin recording or playback on a PCM channel - * @chan: Channel for which DMA operation shall begin - * - * Start scatter-gather DMA for the given channel. - * - */ - -static inline void sg_begin (struct via_channel *chan) -{ - outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL); -} - - -static int sg_active (long iobase) -{ - u8 tmp = inb (iobase + VIA_PCM_STATUS); - if ((tmp & VIA_SGD_STOPPED) || (tmp & VIA_SGD_PAUSED)) { - printk(KERN_WARNING "via82cxxx warning: SG stopped or paused\n"); - return 0; - } - if (tmp & VIA_SGD_ACTIVE) - return 1; - return 0; -} - - -/**************************************************************** - * - * Miscellaneous debris - * - * - */ - - -/** - * via_syscall_down - down the card-specific syscell semaphore - * @card: Private info for specified board - * @nonblock: boolean, non-zero if O_NONBLOCK is set - * - * Encapsulates standard method of acquiring the syscall sem. - * - * Returns negative errno on error, or zero for success. - */ - -static inline int via_syscall_down (struct via_info *card, int nonblock) -{ - /* Thomas Sailer: - * EAGAIN is supposed to be used if IO is pending, - * not if there is contention on some internal - * synchronization primitive which should be - * held only for a short time anyway - */ - nonblock = 0; - - if (nonblock) { - if (down_trylock (&card->syscall_sem)) - return -EAGAIN; - } else { - if (down_interruptible (&card->syscall_sem)) - return -ERESTARTSYS; - } - - return 0; -} - - -/** - * via_stop_everything - Stop all audio operations - * @card: Private info for specified board - * - * Stops all DMA operations and interrupts, and clear - * any pending status bits resulting from those operations. - */ - -static void via_stop_everything (struct via_info *card) -{ - u8 tmp, new_tmp; - - DPRINTK ("ENTER\n"); - - assert (card != NULL); - - /* - * terminate any existing operations on audio read/write channels - */ - 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); - - /* - * clear any existing stops / flags (sanity check mainly) - */ - 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); - - /* - * clear any enabled interrupt bits - */ - tmp = inb (card->baseaddr + VIA_BASE0_PCM_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_PCM_OUT_CHAN_TYPE); - - tmp = inb (card->baseaddr + VIA_BASE0_PCM_IN_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_PCM_IN_CHAN_TYPE); - - tmp = inb (card->baseaddr + VIA_BASE0_FM_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_FM_OUT_CHAN_TYPE); - - udelay(10); - - /* - * clear any existing flags - */ - 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); - - DPRINTK ("EXIT\n"); -} - - -/** - * via_set_rate - Set PCM rate for given channel - * @ac97: Pointer to generic codec info struct - * @chan: Private info for specified channel - * @rate: Desired PCM sample rate, in Khz - * - * Sets the PCM sample rate for a channel. - * - * Values for @rate are clamped to a range of 4000 Khz through 48000 Khz, - * due to hardware constraints. - */ - -static int via_set_rate (struct ac97_codec *ac97, - struct via_channel *chan, unsigned rate) -{ - struct via_info *card = ac97->private_data; - int rate_reg; - - DPRINTK ("ENTER, rate = %d\n", rate); - - if (chan->rate == rate) - goto out; - if (card->locked_rate) { - chan->rate = 48000; - goto out; - } - - if (rate > 48000) rate = 48000; - if (rate < 4000) rate = 4000; - - 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); - - 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); - - /* the hardware might return a value different than what we - * passed to it, so read the rate value back from hardware - * to see what we came up with - */ - chan->rate = via_ac97_read_reg (ac97, rate_reg); - - if (chan->rate == 0) { - card->locked_rate = 1; - chan->rate = 48000; - printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); - } - -out: - DPRINTK ("EXIT, returning rate %d Hz\n", chan->rate); - return chan->rate; -} - - -/**************************************************************** - * - * Channel-specific operations - * - * - */ - - -/** - * via_chan_init_defaults - Initialize a struct via_channel - * @card: Private audio chip info - * @chan: Channel to be initialized - * - * Zero @chan, and then set all static defaults for the structure. - */ - -static void via_chan_init_defaults (struct via_info *card, struct via_channel *chan) -{ - memset (chan, 0, sizeof (*chan)); - - if (chan == &card->ch_out) { - chan->name = "PCM-OUT"; - 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; - chan->is_record = 1; - } else if (chan == &card->ch_fm) { - chan->name = "PCM-OUT-FM"; - chan->iobase = card->baseaddr + VIA_BASE0_FM_OUT_CHAN; - } else { - BUG(); - } - - init_waitqueue_head (&chan->wait); - - chan->pcm_fmt = VIA_PCM_FMT_MASK; - chan->is_enabled = 1; - - chan->frag_number = 0; - chan->frag_size = 0; - atomic_set(&chan->n_frags, 0); - atomic_set (&chan->hw_ptr, 0); -} - -/** - * via_chan_init - Initialize PCM channel - * @card: Private audio chip info - * @chan: Channel to be initialized - * - * 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. - */ - - -static void via_chan_init (struct via_info *card, struct via_channel *chan) -{ - - DPRINTK ("ENTER\n"); - - /* bzero channel structure, and init members to defaults */ - via_chan_init_defaults (card, chan); - - /* stop any existing channel output */ - via_chan_clear (card, chan); - via_chan_status_clear (chan->iobase); - via_chan_pcm_fmt (chan, 1); - - DPRINTK ("EXIT\n"); -} - -/** - * via_chan_buffer_init - Initialize PCM channel buffer - * @card: Private audio chip info - * @chan: Channel to be initialized - * - * Performs some of the preparations necessary to begin - * using a PCM channel. - * - * Currently the preparations include allocating the - * scatter-gather DMA table and buffers, - * and passing the - * address of the DMA table to the hardware. - * - * Note that special care is taken when passing the - * DMA table address to hardware, because it was found - * during driver development that the hardware did not - * always "take" the address. - */ - -static int via_chan_buffer_init (struct via_info *card, struct via_channel *chan) -{ - int page, offset; - int i; - - DPRINTK ("ENTER\n"); - - if (chan->sgtable != NULL) { - DPRINTK ("EXIT\n"); - return 0; - } - - /* alloc DMA-able memory for scatter-gather table */ - chan->sgtable = pci_alloc_consistent (card->pdev, - (sizeof (struct via_sgd_table) * chan->frag_number), - &chan->sgt_handle); - if (!chan->sgtable) { - printk (KERN_ERR PFX "DMA table alloc fail, aborting\n"); - DPRINTK ("EXIT\n"); - return -ENOMEM; - } - - memset ((void*)chan->sgtable, 0, - (sizeof (struct via_sgd_table) * chan->frag_number)); - - /* alloc DMA-able memory for scatter-gather buffers */ - - chan->page_number = (chan->frag_number * chan->frag_size) / PAGE_SIZE + - (((chan->frag_number * chan->frag_size) % PAGE_SIZE) ? 1 : 0); - - for (i = 0; i < chan->page_number; i++) { - chan->pgtbl[i].cpuaddr = pci_alloc_consistent (card->pdev, PAGE_SIZE, - &chan->pgtbl[i].handle); - - if (!chan->pgtbl[i].cpuaddr) { - chan->page_number = i; - goto err_out_nomem; - } - -#ifndef VIA_NDEBUG - memset (chan->pgtbl[i].cpuaddr, 0xBC, chan->frag_size); -#endif - -#if 1 - DPRINTK ("dmabuf_pg #%d (h=%lx, v2p=%lx, a=%p)\n", - i, (long)chan->pgtbl[i].handle, - virt_to_phys(chan->pgtbl[i].cpuaddr), - chan->pgtbl[i].cpuaddr); -#endif - } - - for (i = 0; i < chan->frag_number; i++) { - - page = i / (PAGE_SIZE / chan->frag_size); - offset = (i % (PAGE_SIZE / chan->frag_size)) * chan->frag_size; - - chan->sgtable[i].count = cpu_to_le32 (chan->frag_size | VIA_FLAG); - chan->sgtable[i].addr = cpu_to_le32 (chan->pgtbl[page].handle + offset); - -#if 1 - DPRINTK ("dmabuf #%d (32(h)=%lx)\n", - i, - (long)chan->sgtable[i].addr); -#endif - } - - /* overwrite the last buffer information */ - chan->sgtable[chan->frag_number - 1].count = cpu_to_le32 (chan->frag_size | VIA_EOL); - - /* set location of DMA-able scatter-gather info table */ - DPRINTK ("outl (0x%X, 0x%04lX)\n", - chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR); - - via_ac97_wait_idle (card); - outl (chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR); - udelay (20); - via_ac97_wait_idle (card); - - DPRINTK ("inl (0x%lX) = %x\n", - chan->iobase + VIA_PCM_TABLE_ADDR, - inl(chan->iobase + VIA_PCM_TABLE_ADDR)); - - DPRINTK ("EXIT\n"); - return 0; - -err_out_nomem: - printk (KERN_ERR PFX "DMA buffer alloc fail, aborting\n"); - via_chan_buffer_free (card, chan); - DPRINTK ("EXIT\n"); - return -ENOMEM; -} - - -/** - * via_chan_free - Release a PCM channel - * @card: Private audio chip info - * @chan: Channel to be released - * - * Performs all the functions necessary to clean up - * an initialized channel. - * - * Currently these functions include disabled any - * active DMA operations, setting the PCM channel - * back to a known state, and releasing any allocated - * sound buffers. - */ - -static void via_chan_free (struct via_info *card, struct via_channel *chan) -{ - DPRINTK ("ENTER\n"); - - spin_lock_irq (&card->lock); - - /* stop any existing channel output */ - via_chan_status_clear (chan->iobase); - via_chan_stop (chan->iobase); - via_chan_status_clear (chan->iobase); - - spin_unlock_irq (&card->lock); - - synchronize_irq(); - - DPRINTK ("EXIT\n"); -} - -static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan) -{ - int i; - - DPRINTK ("ENTER\n"); - - /* zero location of DMA-able scatter-gather info table */ - via_ac97_wait_idle(card); - outl (0, chan->iobase + VIA_PCM_TABLE_ADDR); - - for (i = 0; i < chan->page_number; i++) - if (chan->pgtbl[i].cpuaddr) { - pci_free_consistent (card->pdev, PAGE_SIZE, - chan->pgtbl[i].cpuaddr, - chan->pgtbl[i].handle); - chan->pgtbl[i].cpuaddr = NULL; - chan->pgtbl[i].handle = 0; - } - - chan->page_number = 0; - - if (chan->sgtable) { - pci_free_consistent (card->pdev, - (sizeof (struct via_sgd_table) * chan->frag_number), - (void*)chan->sgtable, chan->sgt_handle); - chan->sgtable = NULL; - } - - DPRINTK ("EXIT\n"); -} - - -/** - * via_chan_pcm_fmt - Update PCM channel settings - * @chan: Channel to be updated - * @reset: Boolean. If non-zero, channel will be reset - * to 8-bit mono mode. - * - * Stores the settings of the current PCM format, - * 8-bit or 16-bit, and mono/stereo, into the - * hardware settings for the specified channel. - * If @reset is non-zero, the channel is reset - * to 8-bit mono mode. Otherwise, the channel - * is set to the values stored in the channel - * information struct @chan. - */ - -static void via_chan_pcm_fmt (struct via_channel *chan, int reset) -{ - DPRINTK ("ENTER, pcm_fmt=0x%02X, reset=%s\n", - chan->pcm_fmt, reset ? "yes" : "no"); - - assert (chan != NULL); - - if (reset) - /* reset to 8-bit mono mode */ - chan->pcm_fmt = 0; - - /* enable interrupts on FLAG and EOL */ - chan->pcm_fmt |= VIA_CHAN_TYPE_MASK; - - /* if we are recording, enable recording fifo bit */ - if (chan->is_record) - chan->pcm_fmt |= VIA_PCM_REC_FIFO; - /* set interrupt select bits where applicable (PCM in & out channels) */ - if (!chan->is_record) - chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT; - - outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE); - - DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n", - chan->pcm_fmt, - inb (chan->iobase + VIA_PCM_TYPE)); -} - - -/** - * via_chan_clear - Stop DMA channel operation, and reset pointers - * @card: the chip to accessed - * @chan: Channel to be cleared - * - * Call via_chan_stop to halt DMA operations, and then resets - * all software pointers which track DMA operation. - */ - -static void via_chan_clear (struct via_info *card, struct via_channel *chan) -{ - DPRINTK ("ENTER\n"); - via_chan_stop (chan->iobase); - via_chan_buffer_free(card, chan); - chan->is_active = 0; - chan->is_mapped = 0; - chan->is_enabled = 1; - chan->slop_len = 0; - chan->sw_ptr = 0; - chan->n_irqs = 0; - atomic_set (&chan->hw_ptr, 0); - DPRINTK ("EXIT\n"); -} - - -/** - * via_chan_set_speed - Set PCM sample rate for given channel - * @card: Private info for specified board - * @chan: Channel whose sample rate will be adjusted - * @val: New sample rate, in Khz - * - * Helper function for the %SNDCTL_DSP_SPEED ioctl. OSS semantics - * demand that all audio operations halt (if they are not already - * halted) when the %SNDCTL_DSP_SPEED is given. - * - * This function halts all audio operations for the given channel - * @chan, and then calls via_set_rate to set the audio hardware - * to the new rate. - */ - -static int via_chan_set_speed (struct via_info *card, - struct via_channel *chan, int val) -{ - DPRINTK ("ENTER, requested rate = %d\n", val); - - via_chan_clear (card, chan); - - val = via_set_rate (&card->ac97, chan, val); - - DPRINTK ("EXIT, returning %d\n", val); - return val; -} - - -/** - * via_chan_set_fmt - Set PCM sample size for given channel - * @card: Private info for specified board - * @chan: Channel whose sample size will be adjusted - * @val: New sample size, use the %AFMT_xxx constants - * - * Helper function for the %SNDCTL_DSP_SETFMT ioctl. OSS semantics - * demand that all audio operations halt (if they are not already - * halted) when the %SNDCTL_DSP_SETFMT is given. - * - * This function halts all audio operations for the given channel - * @chan, and then calls via_chan_pcm_fmt to set the audio hardware - * to the new sample size, either 8-bit or 16-bit. - */ - -static int via_chan_set_fmt (struct via_info *card, - struct via_channel *chan, int val) -{ - DPRINTK ("ENTER, val=%s\n", - val == AFMT_U8 ? "AFMT_U8" : - val == AFMT_S16_LE ? "AFMT_S16_LE" : - "unknown"); - - via_chan_clear (card, chan); - - assert (val != AFMT_QUERY); /* this case is handled elsewhere */ - - switch (val) { - case AFMT_S16_LE: - if ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) == 0) { - chan->pcm_fmt |= VIA_PCM_FMT_16BIT; - via_chan_pcm_fmt (chan, 0); - } - break; - - case AFMT_U8: - if (chan->pcm_fmt & VIA_PCM_FMT_16BIT) { - chan->pcm_fmt &= ~VIA_PCM_FMT_16BIT; - via_chan_pcm_fmt (chan, 0); - } - break; - - default: - DPRINTK ("unknown AFMT: 0x%X\n", val); - val = AFMT_S16_LE; - } - - DPRINTK ("EXIT\n"); - return val; -} - - -/** - * via_chan_set_stereo - Enable or disable stereo for a DMA channel - * @card: Private info for specified board - * @chan: Channel whose stereo setting will be adjusted - * @val: New sample size, use the %AFMT_xxx constants - * - * Helper function for the %SNDCTL_DSP_CHANNELS and %SNDCTL_DSP_STEREO ioctls. OSS semantics - * demand that all audio operations halt (if they are not already - * halted) when %SNDCTL_DSP_CHANNELS or SNDCTL_DSP_STEREO is given. - * - * This function halts all audio operations for the given channel - * @chan, and then calls via_chan_pcm_fmt to set the audio hardware - * to enable or disable stereo. - */ - -static int via_chan_set_stereo (struct via_info *card, - struct via_channel *chan, int val) -{ - DPRINTK ("ENTER, channels = %d\n", val); - - via_chan_clear (card, chan); - - switch (val) { - - /* mono */ - case 1: - chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO; - via_chan_pcm_fmt (chan, 0); - break; - - /* stereo */ - case 2: - chan->pcm_fmt |= VIA_PCM_FMT_STEREO; - via_chan_pcm_fmt (chan, 0); - break; - - /* unknown */ - default: - printk (KERN_WARNING PFX "unknown number of channels\n"); - val = -EINVAL; - break; - } - - DPRINTK ("EXIT, returning %d\n", val); - return val; -} - -static int via_chan_set_buffering (struct via_info *card, - struct via_channel *chan, int val) -{ - int shift; - - DPRINTK ("ENTER\n"); - - /* in both cases the buffer cannot be changed */ - if (chan->is_active || chan->is_mapped) { - DPRINTK ("EXIT\n"); - return -EINVAL; - } - - /* called outside SETFRAGMENT */ - /* set defaults or do nothing */ - if (val < 0) { - - if (chan->frag_size && chan->frag_number) - goto out; - - 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; - - shift = 0; - while (chan->frag_size) { - chan->frag_size >>= 1; - shift++; - } - chan->frag_size = 1 << shift; - - chan->frag_number = (VIA_DEFAULT_BUFFER_TIME / VIA_DEFAULT_FRAG_TIME); - - DPRINTK ("setting default values %d %d\n", chan->frag_size, chan->frag_number); - } else { - chan->frag_size = 1 << (val & 0xFFFF); - chan->frag_number = (val >> 16) & 0xFFFF; - - DPRINTK ("using user values %d %d\n", chan->frag_size, chan->frag_number); - } - - /* quake3 wants frag_number to be a power of two */ - shift = 0; - while (chan->frag_number) { - chan->frag_number >>= 1; - shift++; - } - chan->frag_number = 1 << shift; - - if (chan->frag_size > VIA_MAX_FRAG_SIZE) - chan->frag_size = VIA_MAX_FRAG_SIZE; - else if (chan->frag_size < VIA_MIN_FRAG_SIZE) - chan->frag_size = VIA_MIN_FRAG_SIZE; - - if (chan->frag_number < VIA_MIN_FRAG_NUMBER) - chan->frag_number = VIA_MIN_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; - -out: - if (chan->is_record) - atomic_set (&chan->n_frags, 0); - else - atomic_set (&chan->n_frags, chan->frag_number); - - DPRINTK ("EXIT\n"); - - return 0; -} - -#ifdef VIA_CHAN_DUMP_BUFS -/** - * via_chan_dump_bufs - Display DMA table contents - * @chan: Channel whose DMA table will be displayed - * - * Debugging function which displays the contents of the - * scatter-gather DMA table for the given channel @chan. - */ - -static void via_chan_dump_bufs (struct via_channel *chan) -{ - int i; - - for (i = 0; i < chan->frag_number; i++) { - DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n", - i, chan->sgtable[i].addr, - chan->sgtable[i].count & 0x00FFFFFF, - chan->sgtable[i].count & VIA_FLAG ? 1 : 0, - chan->sgtable[i].count & VIA_EOL ? 1 : 0); - } - DPRINTK ("buf_in_use = %d, nextbuf = %d\n", - atomic_read (&chan->buf_in_use), - atomic_read (&chan->sw_ptr)); -} -#endif /* VIA_CHAN_DUMP_BUFS */ - - -/** - * via_chan_flush_frag - Flush partially-full playback buffer to hardware - * @chan: Channel whose DMA table will be displayed - * - * Flushes partially-full playback buffer to hardware. - */ - -static void via_chan_flush_frag (struct via_channel *chan) -{ - DPRINTK ("ENTER\n"); - - assert (chan->slop_len > 0); - - if (chan->sw_ptr == (chan->frag_number - 1)) - chan->sw_ptr = 0; - else - chan->sw_ptr++; - - chan->slop_len = 0; - - assert (atomic_read (&chan->n_frags) > 0); - atomic_dec (&chan->n_frags); - - DPRINTK ("EXIT\n"); -} - - - -/** - * via_chan_maybe_start - Initiate audio hardware DMA operation - * @chan: Channel whose DMA is to be started - * - * Initiate DMA operation, if the DMA engine for the given - * channel @chan is not already active. - */ - -static inline void via_chan_maybe_start (struct via_channel *chan) -{ - assert (chan->is_active == sg_active(chan->iobase)); - - if (!chan->is_active && chan->is_enabled) { - chan->is_active = 1; - sg_begin (chan); - DPRINTK ("starting channel %s\n", chan->name); - } -} - - -/**************************************************************** - * - * Interface to ac97-codec module - * - * - */ - -/** - * via_ac97_wait_idle - Wait until AC97 codec is not busy - * @card: Private info for specified board - * - * Sleep until the AC97 codec is no longer busy. - * Returns the final value read from the SGD - * register being polled. - */ - -static u8 via_ac97_wait_idle (struct via_info *card) -{ - u8 tmp8; - int counter = VIA_COUNTER_LIMIT; - - DPRINTK ("ENTER/EXIT\n"); - - assert (card != NULL); - assert (card->pdev != NULL); - - do { - udelay (15); - - tmp8 = inb (card->baseaddr + 0x83); - } while ((tmp8 & VIA_CR83_BUSY) && (counter-- > 0)); - - if (tmp8 & VIA_CR83_BUSY) - printk (KERN_WARNING PFX "timeout waiting on AC97 codec\n"); - return tmp8; -} - - -/** - * via_ac97_read_reg - Read AC97 standard register - * @codec: Pointer to generic AC97 codec info - * @reg: Index of AC97 register to be read - * - * Read the value of a single AC97 codec register, - * as defined by the Intel AC97 specification. - * - * Defines the standard AC97 read-register operation - * required by the kernel's ac97_codec interface. - * - * Returns the 16-bit value stored in the specified - * register. - */ - -static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg) -{ - unsigned long data; - struct via_info *card; - int counter; - - DPRINTK ("ENTER\n"); - - assert (codec != NULL); - assert (codec->private_data != NULL); - - card = codec->private_data; - - /* 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 - the same time as the command */ - data = (reg << 16) | VIA_CR80_READ | VIA_CR80_VALID; - - outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); - udelay (20); - - for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) { - udelay (1); - if ((((data = inl(card->baseaddr + VIA_BASE0_AC97_CTRL)) & - (VIA_CR80_VALID|VIA_CR80_BUSY)) == VIA_CR80_VALID)) - goto out; - } - - printk (KERN_WARNING PFX "timeout while reading AC97 codec (0x%lX)\n", data); - goto err_out; - -out: - /* Once the valid bit has become set, we must wait a complete AC97 - frame before the data has settled. */ - udelay(25); - data = (unsigned long) inl (card->baseaddr + VIA_BASE0_AC97_CTRL); - - outb (0x02, card->baseaddr + 0x83); - - if (((data & 0x007F0000) >> 16) == reg) { - DPRINTK ("EXIT, success, data=0x%lx, retval=0x%lx\n", - data, data & 0x0000FFFF); - return data & 0x0000FFFF; - } - - printk (KERN_WARNING "via82cxxx_audio: not our index: reg=0x%x, newreg=0x%lx\n", - reg, ((data & 0x007F0000) >> 16)); - -err_out: - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -/** - * via_ac97_write_reg - Write AC97 standard register - * @codec: Pointer to generic AC97 codec info - * @reg: Index of AC97 register to be written - * @value: Value to be written to AC97 register - * - * Write the value of a single AC97 codec register, - * as defined by the Intel AC97 specification. - * - * Defines the standard AC97 write-register operation - * required by the kernel's ac97_codec interface. - */ - -static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value) -{ - u32 data; - struct via_info *card; - int counter; - - DPRINTK ("ENTER\n"); - - assert (codec != NULL); - assert (codec->private_data != NULL); - - card = codec->private_data; - - data = (reg << 16) + value; - outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); - udelay (10); - - for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) { - if ((inb (card->baseaddr + 0x83) & VIA_CR83_BUSY) == 0) - goto out; - - udelay (15); - } - - printk (KERN_WARNING PFX "timeout after AC97 codec write (0x%X, 0x%X)\n", reg, value); - -out: - DPRINTK ("EXIT\n"); -} - - -static int via_mixer_open (struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct via_info *card; - struct pci_dev *pdev; - struct pci_driver *drvr; - - DPRINTK ("ENTER\n"); - - pci_for_each_dev(pdev) { - drvr = pci_dev_driver (pdev); - if (drvr == &via_driver) { - assert (pci_get_drvdata (pdev) != NULL); - - card = pci_get_drvdata (pdev); - if (card->ac97.dev_mixer == minor) - goto match; - } - } - - DPRINTK ("EXIT, returning -ENODEV\n"); - return -ENODEV; - -match: - file->private_data = &card->ac97; - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - -static int via_mixer_ioctl (struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct ac97_codec *codec = file->private_data; - struct via_info *card; - int nonblock = (file->f_flags & O_NONBLOCK); - int rc; - - DPRINTK ("ENTER\n"); - - assert (codec != NULL); - card = codec->private_data; - assert (card != NULL); - - rc = via_syscall_down (card, nonblock); - if (rc) goto out; - - rc = codec->mixer_ioctl(codec, cmd, arg); - - up (&card->syscall_sem); - -out: - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - - -static struct file_operations via_mixer_fops = { - owner: THIS_MODULE, - open: via_mixer_open, - llseek: no_llseek, - ioctl: via_mixer_ioctl, -}; - - -static int __init via_ac97_reset (struct via_info *card) -{ - struct pci_dev *pdev = card->pdev; - u8 tmp8; - u16 tmp16; - - DPRINTK ("ENTER\n"); - - assert (pdev != NULL); - -#ifndef NDEBUG - { - u8 r40,r41,r42,r43,r44,r48; - pci_read_config_byte (card->pdev, 0x40, &r40); - pci_read_config_byte (card->pdev, 0x41, &r41); - pci_read_config_byte (card->pdev, 0x42, &r42); - pci_read_config_byte (card->pdev, 0x43, &r43); - pci_read_config_byte (card->pdev, 0x44, &r44); - pci_read_config_byte (card->pdev, 0x48, &r48); - DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", - r40,r41,r42,r43,r44,r48); - - spin_lock_irq (&card->lock); - DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", - inb (card->baseaddr + 0x00), - inb (card->baseaddr + 0x01), - inb (card->baseaddr + 0x02), - inl (card->baseaddr + 0x04), - inl (card->baseaddr + 0x0C), - inl (card->baseaddr + 0x80), - inl (card->baseaddr + 0x84)); - spin_unlock_irq (&card->lock); - - } -#endif - - /* - * Reset AC97 controller: enable, disable, enable, - * pausing after each command for good luck. Only - * do this if the codec is not ready, because it causes - * loud pops and such due to such a hard codec reset. - */ - pci_read_config_byte (pdev, VIA_ACLINK_STATUS, &tmp8); - if ((tmp8 & VIA_CR40_AC97_READY) == 0) { - pci_write_config_byte (pdev, VIA_ACLINK_CTRL, - VIA_CR41_AC97_ENABLE | - VIA_CR41_AC97_RESET | - VIA_CR41_AC97_WAKEUP); - udelay (100); - - pci_write_config_byte (pdev, VIA_ACLINK_CTRL, 0); - udelay (100); - - pci_write_config_byte (pdev, VIA_ACLINK_CTRL, - VIA_CR41_AC97_ENABLE | - VIA_CR41_PCM_ENABLE | - VIA_CR41_VRA | VIA_CR41_AC97_RESET); - udelay (100); - } - - /* Make sure VRA is enabled, in case we didn't do a - * complete codec reset, above - */ - pci_read_config_byte (pdev, VIA_ACLINK_CTRL, &tmp8); - if (((tmp8 & VIA_CR41_VRA) == 0) || - ((tmp8 & VIA_CR41_AC97_ENABLE) == 0) || - ((tmp8 & VIA_CR41_PCM_ENABLE) == 0) || - ((tmp8 & VIA_CR41_AC97_RESET) == 0)) { - pci_write_config_byte (pdev, VIA_ACLINK_CTRL, - VIA_CR41_AC97_ENABLE | - VIA_CR41_PCM_ENABLE | - VIA_CR41_VRA | VIA_CR41_AC97_RESET); - udelay (100); - } - -#if 0 /* this breaks on K7M */ - /* 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); - - /* 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); - if ((tmp16 & 1) == 0) - via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -static void via_ac97_codec_wait (struct ac97_codec *codec) -{ - assert (codec->private_data != NULL); - via_ac97_wait_idle (codec->private_data); -} - - -static int __init via_ac97_init (struct via_info *card) -{ - int rc; - u16 tmp16; - - DPRINTK ("ENTER\n"); - - 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.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"); - return -EIO; - } - - rc = via_ac97_reset (card); - if (rc) { - printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n"); - goto err_out; - } - - 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); - - /* - * 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); - 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); - if ((tmp16 & 1) == 0) { - card->locked_rate = 1; - printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); - } - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out: - unregister_sound_mixer (card->ac97.dev_mixer); - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - - -static void via_ac97_cleanup (struct via_info *card) -{ - DPRINTK ("ENTER\n"); - - assert (card != NULL); - assert (card->ac97.dev_mixer >= 0); - - unregister_sound_mixer (card->ac97.dev_mixer); - - DPRINTK ("EXIT\n"); -} - - - -/**************************************************************** - * - * Interrupt-related code - * - */ - -/** - * via_intr_channel - handle an interrupt for a single channel - * @chan: handle interrupt for this channel - * - * This is the "meat" of the interrupt handler, - * containing the actions taken each time an interrupt - * occurs. All communication and coordination with - * userspace takes place here. - * - * Locking: inside card->lock - */ - -static void via_intr_channel (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) - return; - - /* acknowledge any flagged bits ASAP */ - outb (status, chan->iobase); - - if (!chan->sgtable) /* XXX: temporary solution */ - return; - - /* grab current h/w ptr value */ - n = atomic_read (&chan->hw_ptr); - - /* sanity check: make sure our h/w ptr doesn't have a weird value */ - 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 - */ - if (n == (chan->frag_number - 1)) { - chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_EOL); - atomic_set (&chan->hw_ptr, 0); - } else { - chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_FLAG); - atomic_inc (&chan->hw_ptr); - } - - /* accounting crap for SNDCTL_DSP_GETxPTR */ - chan->n_irqs++; - chan->bytes += chan->frag_size; - if (chan->bytes < 0) /* handle overflow of 31-bit value */ - chan->bytes = chan->frag_size; - - /* wake up anyone listening to see when interrupts occur */ - if (waitqueue_active (&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)); -} - - -static void via_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_INTR_MASK)) - { -#ifdef CONFIG_MIDI_VIA82CXXX - if (card->midi_devc) - uart401intr(irq, card->midi_devc, regs); -#endif - return; - } - DPRINTK ("intr, status32 == 0x%08X\n", status32); - - /* synchronize interrupt handling under SMP. this spinlock - * goes away completely on UP - */ - spin_lock (&card->lock); - - if (status32 & VIA_INTR_OUT) - via_intr_channel (&card->ch_out); - if (status32 & VIA_INTR_IN) - via_intr_channel (&card->ch_in); - if (status32 & VIA_INTR_FM) - via_intr_channel (&card->ch_fm); - - spin_unlock (&card->lock); -} - - -/** - * via_interrupt_init - Initialize interrupt handling - * @card: Private info for specified board - * - * Obtain and reserve IRQ for using in handling audio events. - * Also, disable any IRQ-generating resources, to make sure - * we don't get interrupts before we want them. - */ - -static int via_interrupt_init (struct via_info *card) -{ - u8 tmp8; - - DPRINTK ("ENTER\n"); - - assert (card != NULL); - assert (card->pdev != NULL); - - /* check for sane IRQ number. can this ever happen? */ - if (card->pdev->irq < 2) { - printk (KERN_ERR PFX "insane IRQ %d, aborting\n", - card->pdev->irq); - DPRINTK ("EXIT, returning -EIO\n"); - 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); - } - - 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; - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -/**************************************************************** - * - * OSS DSP device - * - */ - -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, -}; - - -static int __init via_dsp_init (struct via_info *card) -{ - u8 tmp8; - - DPRINTK ("ENTER\n"); - - 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); - } - - via_stop_everything (card); - - card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1); - if (card->dev_dsp < 0) { - DPRINTK ("EXIT, returning -ENODEV\n"); - return -ENODEV; - } - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -static void via_dsp_cleanup (struct via_info *card) -{ - DPRINTK ("ENTER\n"); - - assert (card != NULL); - assert (card->dev_dsp >= 0); - - via_stop_everything (card); - - unregister_sound_dsp (card->dev_dsp); - - DPRINTK ("EXIT\n"); -} - - -static struct page * via_mm_nopage (struct vm_area_struct * vma, - unsigned long address, int write_access) -{ - struct via_info *card = vma->vm_private_data; - struct via_channel *chan = &card->ch_out; - struct page *dmapage; - unsigned long pgoff; - int rd, wr; - - DPRINTK ("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh, wr %d\n", - vma->vm_start, - address - vma->vm_start, - (address - vma->vm_start) >> PAGE_SHIFT, - address, - write_access); - - if (address > vma->vm_end) { - DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n"); - return NOPAGE_SIGBUS; /* Disallow mremap */ - } - if (!card) { - DPRINTK ("EXIT, returning NOPAGE_OOM\n"); - return NOPAGE_OOM; /* Nothing allocated */ - } - - pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT); - rd = card->ch_in.is_mapped; - wr = card->ch_out.is_mapped; - -#ifndef VIA_NDEBUG - { - unsigned long max_bufs = chan->frag_number; - if (rd && wr) max_bufs *= 2; - /* via_dsp_mmap() should ensure this */ - assert (pgoff < max_bufs); - } -#endif - - /* if full-duplex (read+write) and we have two sets of bufs, - * then the playback buffers come first, sez soundcard.c */ - if (pgoff >= chan->page_number) { - pgoff -= chan->page_number; - chan = &card->ch_in; - } else if (!wr) - chan = &card->ch_in; - - assert ((((unsigned long)chan->pgtbl[pgoff].cpuaddr) % PAGE_SIZE) == 0); - - dmapage = virt_to_page (chan->pgtbl[pgoff].cpuaddr); - DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n", - dmapage, (unsigned long) chan->pgtbl[pgoff].cpuaddr); - get_page (dmapage); - return dmapage; -} - - -#ifndef VM_RESERVED -static int via_mm_swapout (struct page *page, struct file *filp) -{ - return 0; -} -#endif /* VM_RESERVED */ - - -struct vm_operations_struct via_mm_ops = { - nopage: via_mm_nopage, - -#ifndef VM_RESERVED - swapout: via_mm_swapout, -#endif -}; - - -static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct via_info *card; - int nonblock = (file->f_flags & O_NONBLOCK); - int rc = -EINVAL, rd=0, wr=0; - unsigned long max_size, size, start, offset; - - assert (file != NULL); - assert (vma != NULL); - card = file->private_data; - assert (card != NULL); - - DPRINTK ("ENTER, start %lXh, size %ld, pgoff %ld\n", - vma->vm_start, - vma->vm_end - vma->vm_start, - vma->vm_pgoff); - - max_size = 0; - if (vma->vm_flags & VM_READ) { - rd = 1; - via_chan_set_buffering(card, &card->ch_in, -1); - via_chan_buffer_init (card, &card->ch_in); - max_size += card->ch_in.page_number << PAGE_SHIFT; - } - if (vma->vm_flags & VM_WRITE) { - wr = 1; - via_chan_set_buffering(card, &card->ch_out, -1); - via_chan_buffer_init (card, &card->ch_out); - max_size += card->ch_out.page_number << PAGE_SHIFT; - } - - start = vma->vm_start; - offset = (vma->vm_pgoff << PAGE_SHIFT); - size = vma->vm_end - vma->vm_start; - - /* some basic size/offset sanity checks */ - if (size > max_size) - goto out; - if (offset > max_size - size) - goto out; - - rc = via_syscall_down (card, nonblock); - if (rc) goto out; - - vma->vm_ops = &via_mm_ops; - vma->vm_private_data = card; - -#ifdef VM_RESERVED - vma->vm_flags |= VM_RESERVED; -#endif - - if (rd) - card->ch_in.is_mapped = 1; - if (wr) - card->ch_out.is_mapped = 1; - - up (&card->syscall_sem); - rc = 0; - -out: - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - - -static ssize_t via_dsp_do_read (struct via_info *card, - char *userbuf, size_t count, - int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - const char *orig_userbuf = userbuf; - struct via_channel *chan = &card->ch_in; - size_t size; - int n, tmp; - ssize_t ret = 0; - - /* if SGD has not yet been started, start it */ - via_chan_maybe_start (chan); - -handle_one_block: - /* just to be a nice neighbor */ - /* Thomas Sailer: - * But also to ourselves, release semaphore if we do so */ - if (need_resched()) { - up(&card->syscall_sem); - schedule (); - ret = via_syscall_down (card, nonblock); - if (ret) - goto out; - } - - /* grab current channel software pointer. In the case of - * recording, this is pointing to the next buffer that - * will receive data from the audio hardware. - */ - n = chan->sw_ptr; - - /* n_frags represents the number of fragments waiting - * to be copied to userland. sleep until at least - * one buffer has been read from the audio hardware. - */ - add_wait_queue(&chan->wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - tmp = atomic_read (&chan->n_frags); - assert (tmp >= 0); - assert (tmp <= chan->frag_number); - if (tmp) - break; - if (nonblock || !chan->is_active) { - ret = -EAGAIN; - break; - } - - up(&card->syscall_sem); - - DPRINTK ("Sleeping on block %d\n", n); - schedule(); - - ret = via_syscall_down (card, nonblock); - if (ret) - break; - - if (signal_pending (current)) { - ret = -ERESTARTSYS; - break; - } - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&chan->wait, &wait); - if (ret) - goto out; - - /* Now that we have a buffer we can read from, send - * as much as sample data possible to userspace. - */ - while ((count > 0) && (chan->slop_len < chan->frag_size)) { - size_t slop_left = chan->frag_size - chan->slop_len; - void *base = chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr; - unsigned ofs = n % (PAGE_SIZE / chan->frag_size); - - size = (count < slop_left) ? count : slop_left; - if (copy_to_user (userbuf, - base + ofs + chan->slop_len, - size)) { - ret = -EFAULT; - goto out; - } - - count -= size; - chan->slop_len += size; - userbuf += size; - } - - /* If we didn't copy the buffer completely to userspace, - * stop now. - */ - if (chan->slop_len < chan->frag_size) - goto out; - - /* - * If we get to this point, we copied one buffer completely - * to userspace, give the buffer back to the hardware. - */ - - /* advance channel software pointer to point to - * the next buffer from which we will copy - */ - if (chan->sw_ptr == (chan->frag_number - 1)) - chan->sw_ptr = 0; - else - chan->sw_ptr++; - - /* mark one less buffer waiting to be processed */ - assert (atomic_read (&chan->n_frags) > 0); - atomic_dec (&chan->n_frags); - - /* we are at a block boundary, there is no fragment data */ - chan->slop_len = 0; - - 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", - inb (card->baseaddr + 0x00), - inb (card->baseaddr + 0x01), - inb (card->baseaddr + 0x02), - inl (card->baseaddr + 0x04), - inl (card->baseaddr + 0x0C), - inl (card->baseaddr + 0x80), - inl (card->baseaddr + 0x84)); - - if (count > 0) - goto handle_one_block; - -out: - return (userbuf != orig_userbuf) ? (userbuf - orig_userbuf) : ret; -} - - -static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct via_info *card; - int nonblock = (file->f_flags & O_NONBLOCK); - int rc; - - DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n", - file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); - - assert (file != NULL); - assert (buffer != NULL); - card = file->private_data; - assert (card != NULL); - - if (ppos != &file->f_pos) { - DPRINTK ("EXIT, returning -ESPIPE\n"); - return -ESPIPE; - } - - rc = via_syscall_down (card, nonblock); - if (rc) goto out; - - if (card->ch_in.is_mapped) { - rc = -ENXIO; - goto out_up; - } - - via_chan_set_buffering(card, &card->ch_in, -1); - rc = via_chan_buffer_init (card, &card->ch_in); - - if (rc) - goto out_up; - - rc = via_dsp_do_read (card, buffer, count, nonblock); - -out_up: - up (&card->syscall_sem); -out: - DPRINTK ("EXIT, returning %ld\n",(long) rc); - return rc; -} - - -static ssize_t via_dsp_do_write (struct via_info *card, - const char *userbuf, size_t count, - int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - const char *orig_userbuf = userbuf; - struct via_channel *chan = &card->ch_out; - volatile struct via_sgd_table *sgtable = chan->sgtable; - size_t size; - int n, tmp; - ssize_t ret = 0; - -handle_one_block: - /* just to be a nice neighbor */ - /* Thomas Sailer: - * But also to ourselves, release semaphore if we do so */ - if (need_resched()) { - up(&card->syscall_sem); - schedule (); - ret = via_syscall_down (card, nonblock); - if (ret) - goto out; - } - - /* grab current channel fragment pointer. In the case of - * playback, this is pointing to the next fragment that - * should receive data from userland. - */ - n = chan->sw_ptr; - - /* n_frags represents the number of fragments remaining - * to be filled by userspace. Sleep until - * at least one fragment is available for our use. - */ - add_wait_queue(&chan->wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - tmp = atomic_read (&chan->n_frags); - assert (tmp >= 0); - assert (tmp <= chan->frag_number); - if (tmp) - break; - if (nonblock || !chan->is_active) { - ret = -EAGAIN; - break; - } - - up(&card->syscall_sem); - - DPRINTK ("Sleeping on page %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record); - schedule(); - - ret = via_syscall_down (card, nonblock); - if (ret) - break; - - if (signal_pending (current)) { - ret = -ERESTARTSYS; - break; - } - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&chan->wait, &wait); - if (ret) - goto out; - - /* Now that we have at least one fragment we can write to, fill the buffer - * as much as possible with data from userspace. - */ - while ((count > 0) && (chan->slop_len < chan->frag_size)) { - size_t slop_left = chan->frag_size - chan->slop_len; - - size = (count < slop_left) ? count : slop_left; - if (copy_from_user (chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + (n % (PAGE_SIZE / chan->frag_size)) * chan->frag_size + chan->slop_len, - userbuf, size)) { - ret = -EFAULT; - goto out; - } - - count -= size; - chan->slop_len += size; - userbuf += size; - } - - /* If we didn't fill up the buffer with data, stop now. - * Put a 'stop' marker in the DMA table too, to tell the - * audio hardware to stop if it gets here. - */ - if (chan->slop_len < chan->frag_size) { - sgtable[n].count = cpu_to_le32 (chan->slop_len | VIA_EOL | VIA_STOP); - goto out; - } - - /* - * If we get to this point, we have filled a buffer with - * audio data, flush the buffer to audio hardware. - */ - - /* Record the true size for the audio hardware to notice */ - if (n == (chan->frag_number - 1)) - sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_EOL); - else - sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_FLAG); - - /* advance channel software pointer to point to - * the next buffer we will fill with data - */ - if (chan->sw_ptr == (chan->frag_number - 1)) - chan->sw_ptr = 0; - else - chan->sw_ptr++; - - /* mark one less buffer as being available for userspace consumption */ - assert (atomic_read (&chan->n_frags) > 0); - atomic_dec (&chan->n_frags); - - /* we are at a block boundary, there is no fragment data */ - chan->slop_len = 0; - - /* if SGD has not yet been started, start it */ - via_chan_maybe_start (chan); - - 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", - inb (card->baseaddr + 0x00), - inb (card->baseaddr + 0x01), - inb (card->baseaddr + 0x02), - inl (card->baseaddr + 0x04), - inl (card->baseaddr + 0x0C), - inl (card->baseaddr + 0x80), - inl (card->baseaddr + 0x84)); - - if (count > 0) - goto handle_one_block; - -out: - return userbuf - orig_userbuf; -} - - -static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct via_info *card; - ssize_t rc; - int nonblock = (file->f_flags & O_NONBLOCK); - - DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n", - file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); - - assert (file != NULL); - assert (buffer != NULL); - card = file->private_data; - assert (card != NULL); - - if (ppos != &file->f_pos) { - DPRINTK ("EXIT, returning -ESPIPE\n"); - return -ESPIPE; - } - - rc = via_syscall_down (card, nonblock); - if (rc) goto out; - - if (card->ch_out.is_mapped) { - rc = -ENXIO; - goto out_up; - } - - via_chan_set_buffering(card, &card->ch_out, -1); - rc = via_chan_buffer_init (card, &card->ch_out); - - if (rc) - goto out_up; - - rc = via_dsp_do_write (card, buffer, count, nonblock); - -out_up: - up (&card->syscall_sem); -out: - DPRINTK ("EXIT, returning %ld\n",(long) rc); - return rc; -} - - -static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait) -{ - struct via_info *card; - struct via_channel *chan; - unsigned int mask = 0; - - DPRINTK ("ENTER\n"); - - assert (file != NULL); - card = file->private_data; - assert (card != NULL); - - if (file->f_mode & FMODE_READ) { - chan = &card->ch_in; - if (sg_active (chan->iobase)) - poll_wait(file, &chan->wait, wait); - if (atomic_read (&chan->n_frags) > 0) - mask |= POLLIN | POLLRDNORM; - } - - if (file->f_mode & FMODE_WRITE) { - chan = &card->ch_out; - if (sg_active (chan->iobase)) - poll_wait(file, &chan->wait, wait); - if (atomic_read (&chan->n_frags) > 0) - mask |= POLLOUT | POLLWRNORM; - } - - DPRINTK ("EXIT, returning %u\n", mask); - return mask; -} - - -/** - * via_dsp_drain_playback - sleep until all playback samples are flushed - * @card: Private info for specified board - * @chan: Channel to drain - * @nonblock: boolean, non-zero if O_NONBLOCK is set - * - * Sleeps until all playback has been flushed to the audio - * hardware. - * - * Locking: inside card->syscall_sem - */ - -static int via_dsp_drain_playback (struct via_info *card, - struct via_channel *chan, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - int ret = 0; - - DPRINTK ("ENTER, nonblock = %d\n", nonblock); - - if (chan->slop_len > 0) - via_chan_flush_frag (chan); - - if (atomic_read (&chan->n_frags) == chan->frag_number) - goto out; - - via_chan_maybe_start (chan); - - add_wait_queue(&chan->wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - if (atomic_read (&chan->n_frags) >= chan->frag_number) - break; - - if (nonblock) { - DPRINTK ("EXIT, returning -EAGAIN\n"); - ret = -EAGAIN; - break; - } - -#ifdef VIA_DEBUG - { - u8 r40,r41,r42,r43,r44,r48; - pci_read_config_byte (card->pdev, 0x40, &r40); - pci_read_config_byte (card->pdev, 0x41, &r41); - pci_read_config_byte (card->pdev, 0x42, &r42); - pci_read_config_byte (card->pdev, 0x43, &r43); - pci_read_config_byte (card->pdev, 0x44, &r44); - pci_read_config_byte (card->pdev, 0x48, &r48); - DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", - r40,r41,r42,r43,r44,r48); - - DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", - inb (card->baseaddr + 0x00), - inb (card->baseaddr + 0x01), - inb (card->baseaddr + 0x02), - inl (card->baseaddr + 0x04), - inl (card->baseaddr + 0x0C), - inl (card->baseaddr + 0x80), - inl (card->baseaddr + 0x84)); - } - - if (!chan->is_active) - printk (KERN_ERR "sleeping but not active\n"); -#endif - - up(&card->syscall_sem); - - DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_frags)); - schedule(); - - if ((ret = via_syscall_down (card, nonblock))) - break; - - if (signal_pending (current)) { - DPRINTK ("EXIT, returning -ERESTARTSYS\n"); - ret = -ERESTARTSYS; - break; - } - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&chan->wait, &wait); - -#ifdef VIA_DEBUG - { - u8 r40,r41,r42,r43,r44,r48; - pci_read_config_byte (card->pdev, 0x40, &r40); - pci_read_config_byte (card->pdev, 0x41, &r41); - pci_read_config_byte (card->pdev, 0x42, &r42); - pci_read_config_byte (card->pdev, 0x43, &r43); - pci_read_config_byte (card->pdev, 0x44, &r44); - pci_read_config_byte (card->pdev, 0x48, &r48); - DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", - r40,r41,r42,r43,r44,r48); - - DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", - inb (card->baseaddr + 0x00), - inb (card->baseaddr + 0x01), - inb (card->baseaddr + 0x02), - inl (card->baseaddr + 0x04), - inl (card->baseaddr + 0x0C), - inl (card->baseaddr + 0x80), - inl (card->baseaddr + 0x84)); - - DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_frags)); - } -#endif - -out: - DPRINTK ("EXIT, returning %d\n", ret); - return ret; -} - - -/** - * via_dsp_ioctl_space - get information about channel buffering - * @card: Private info for specified board - * @chan: pointer to channel-specific info - * @arg: user buffer for returned information - * - * Handles SNDCTL_DSP_GETISPACE and SNDCTL_DSP_GETOSPACE. - * - * Locking: inside card->syscall_sem - */ - -static int via_dsp_ioctl_space (struct via_info *card, - struct via_channel *chan, - void *arg) -{ - audio_buf_info info; - - via_chan_set_buffering(card, chan, -1); - - info.fragstotal = chan->frag_number; - info.fragsize = chan->frag_size; - - /* number of full fragments we can read/write without blocking */ - info.fragments = atomic_read (&chan->n_frags); - - if ((chan->slop_len % chan->frag_size > 0) && (info.fragments > 0)) - info.fragments--; - - /* number of bytes that can be read or written immediately - * without blocking. - */ - info.bytes = (info.fragments * chan->frag_size); - if (chan->slop_len % chan->frag_size > 0) - info.bytes += chan->frag_size - (chan->slop_len % chan->frag_size); - - DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n", - info.fragstotal, - info.fragsize, - info.fragments, - info.bytes); - - return copy_to_user (arg, &info, sizeof (info)); -} - - -/** - * via_dsp_ioctl_ptr - get information about hardware buffer ptr - * @card: Private info for specified board - * @chan: pointer to channel-specific info - * @arg: user buffer for returned information - * - * Handles SNDCTL_DSP_GETIPTR and SNDCTL_DSP_GETOPTR. - * - * Locking: inside card->syscall_sem - */ - -static int via_dsp_ioctl_ptr (struct via_info *card, - struct via_channel *chan, - void *arg) -{ - count_info info; - - spin_lock_irq (&card->lock); - - info.bytes = chan->bytes; - info.blocks = chan->n_irqs; - chan->n_irqs = 0; - - spin_unlock_irq (&card->lock); - - 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); - info.ptr += extra; - info.bytes += extra; - } else { - info.ptr = 0; - } - - DPRINTK ("EXIT, returning bytes=%d, blocks=%d, ptr=%d\n", - info.bytes, - info.blocks, - info.ptr); - - return copy_to_user (arg, &info, sizeof (info)); -} - - -static int via_dsp_ioctl_trigger (struct via_channel *chan, int val) -{ - int enable, do_something; - - if (chan->is_record) - enable = (val & PCM_ENABLE_INPUT); - else - enable = (val & PCM_ENABLE_OUTPUT); - - if (!chan->is_enabled && enable) { - do_something = 1; - } else if (chan->is_enabled && !enable) { - do_something = -1; - } else { - do_something = 0; - } - - DPRINTK ("enable=%d, do_something=%d\n", - enable, do_something); - - if (chan->is_active && do_something) - return -EINVAL; - - if (do_something == 1) { - chan->is_enabled = 1; - via_chan_maybe_start (chan); - DPRINTK ("Triggering input\n"); - } - - else if (do_something == -1) { - chan->is_enabled = 0; - DPRINTK ("Setup input trigger\n"); - } - - return 0; -} - - -static int via_dsp_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int rc, rd=0, wr=0, val=0; - struct via_info *card; - struct via_channel *chan; - int nonblock = (file->f_flags & O_NONBLOCK); - - assert (file != NULL); - card = file->private_data; - assert (card != NULL); - - if (file->f_mode & FMODE_WRITE) - wr = 1; - if (file->f_mode & FMODE_READ) - rd = 1; - - rc = via_syscall_down (card, nonblock); - if (rc) - return rc; - rc = -EINVAL; - - switch (cmd) { - - /* OSS API version. XXX unverified */ - case OSS_GETVERSION: - DPRINTK ("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n"); - rc = put_user (SOUND_VERSION, (int *)arg); - break; - - /* list of supported PCM data formats */ - case SNDCTL_DSP_GETFMTS: - DPRINTK ("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n"); - rc = put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg); - break; - - /* query or set current channel's PCM data format */ - case SNDCTL_DSP_SETFMT: - if (get_user(val, (int *)arg)) { - rc = -EFAULT; - break; - } - DPRINTK ("DSP_SETFMT, val==%d\n", val); - if (val != AFMT_QUERY) { - rc = 0; - - if (rd) - rc = via_chan_set_fmt (card, &card->ch_in, val); - - if (rc >= 0 && wr) - rc = via_chan_set_fmt (card, &card->ch_out, val); - - if (rc < 0) - break; - - val = rc; - } else { - if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_16BIT)) || - (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_16BIT))) - val = AFMT_S16_LE; - else - val = AFMT_U8; - } - DPRINTK ("SETFMT EXIT, returning %d\n", val); - rc = put_user (val, (int *)arg); - break; - - /* query or set number of channels (1=mono, 2=stereo) */ - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) { - rc = -EFAULT; - break; - } - DPRINTK ("DSP_CHANNELS, val==%d\n", val); - if (val != 0) { - rc = 0; - - if (rd) - rc = via_chan_set_stereo (card, &card->ch_in, val); - - if (rc >= 0 && wr) - rc = via_chan_set_stereo (card, &card->ch_out, val); - - if (rc < 0) - break; - - 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; - else - val = 1; - } - DPRINTK ("CHANNELS EXIT, returning %d\n", val); - rc = put_user (val, (int *)arg); - break; - - /* enable (val is not zero) or disable (val == 0) stereo */ - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) { - rc = -EFAULT; - break; - } - DPRINTK ("DSP_STEREO, val==%d\n", val); - rc = 0; - - if (rd) - rc = via_chan_set_stereo (card, &card->ch_in, val ? 2 : 1); - if (rc >= 0 && wr) - rc = via_chan_set_stereo (card, &card->ch_out, val ? 2 : 1); - - if (rc < 0) - break; - - val = rc - 1; - - DPRINTK ("STEREO EXIT, returning %d\n", val); - rc = put_user(val, (int *) arg); - break; - - /* query or set sampling rate */ - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) { - rc = -EFAULT; - break; - } - DPRINTK ("DSP_SPEED, val==%d\n", val); - if (val < 0) { - rc = -EINVAL; - break; - } - if (val > 0) { - rc = 0; - - if (rd) - rc = via_chan_set_speed (card, &card->ch_in, val); - if (rc >= 0 && wr) - rc = via_chan_set_speed (card, &card->ch_out, val); - - if (rc < 0) - break; - - val = rc; - } else { - if (rd) - val = card->ch_in.rate; - else if (wr) - val = card->ch_out.rate; - else - val = 0; - } - DPRINTK ("SPEED EXIT, returning %d\n", val); - rc = put_user (val, (int *)arg); - break; - - /* wait until all buffers have been played, and then stop device */ - case SNDCTL_DSP_SYNC: - DPRINTK ("DSP_SYNC\n"); - rc = 0; - if (wr) { - DPRINTK ("SYNC EXIT (after calling via_dsp_drain_playback)\n"); - rc = via_dsp_drain_playback (card, &card->ch_out, nonblock); - } - break; - - /* stop recording/playback immediately */ - case SNDCTL_DSP_RESET: - DPRINTK ("DSP_RESET\n"); - if (rd) { - via_chan_clear (card, &card->ch_in); - card->ch_in.frag_number = 0; - card->ch_in.frag_size = 0; - atomic_set(&card->ch_in.n_frags, 0); - } - - if (wr) { - via_chan_clear (card, &card->ch_out); - card->ch_out.frag_number = 0; - card->ch_out.frag_size = 0; - atomic_set(&card->ch_out.n_frags, 0); - } - - rc = 0; - break; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - rc = 0; - break; - - /* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */ - case SNDCTL_DSP_GETCAPS: - DPRINTK ("DSP_GETCAPS\n"); - rc = put_user(VIA_DSP_CAP, (int *)arg); - break; - - /* obtain buffer fragment size */ - case SNDCTL_DSP_GETBLKSIZE: - DPRINTK ("DSP_GETBLKSIZE\n"); - - if (rd) { - via_chan_set_buffering(card, &card->ch_in, -1); - rc = put_user(card->ch_in.frag_size, (int *)arg); - } else if (wr) { - via_chan_set_buffering(card, &card->ch_out, -1); - rc = put_user(card->ch_out.frag_size, (int *)arg); - } - break; - - /* obtain information about input buffering */ - case SNDCTL_DSP_GETISPACE: - DPRINTK ("DSP_GETISPACE\n"); - if (rd) - rc = via_dsp_ioctl_space (card, &card->ch_in, (void*) arg); - break; - - /* obtain information about output buffering */ - case SNDCTL_DSP_GETOSPACE: - DPRINTK ("DSP_GETOSPACE\n"); - if (wr) - rc = via_dsp_ioctl_space (card, &card->ch_out, (void*) arg); - break; - - /* obtain information about input hardware pointer */ - case SNDCTL_DSP_GETIPTR: - DPRINTK ("DSP_GETIPTR\n"); - if (rd) - rc = via_dsp_ioctl_ptr (card, &card->ch_in, (void*) arg); - break; - - /* obtain information about output hardware pointer */ - case SNDCTL_DSP_GETOPTR: - DPRINTK ("DSP_GETOPTR\n"); - if (wr) - rc = via_dsp_ioctl_ptr (card, &card->ch_out, (void*) arg); - break; - - /* return number of bytes remaining to be played by DMA engine */ - case SNDCTL_DSP_GETODELAY: - { - DPRINTK ("DSP_GETODELAY\n"); - - chan = &card->ch_out; - - if (!wr) - break; - - if (chan->is_active) { - - val = chan->frag_number - atomic_read (&chan->n_frags); - - if (val > 0) { - val *= chan->frag_size; - val -= chan->frag_size - - inl (chan->iobase + VIA_PCM_BLOCK_COUNT); - } - val += chan->slop_len % chan->frag_size; - } else - val = 0; - - assert (val <= (chan->frag_size * chan->frag_number)); - - DPRINTK ("GETODELAY EXIT, val = %d bytes\n", val); - rc = put_user (val, (int *)arg); - break; - } - - /* handle the quick-start of a channel, - * or the notification that a quick-start will - * occur in the future - */ - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, (int *)arg)) { - rc = -EFAULT; - break; - } - DPRINTK ("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n", - rd, wr, card->ch_in.is_active, card->ch_out.is_active, - card->ch_in.is_enabled, card->ch_out.is_enabled); - - rc = 0; - - if (rd) - rc = via_dsp_ioctl_trigger (&card->ch_in, val); - - if (!rc && wr) - rc = via_dsp_ioctl_trigger (&card->ch_out, val); - - break; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if ((file->f_mode & FMODE_READ) && card->ch_in.is_enabled) - val |= PCM_ENABLE_INPUT; - if ((file->f_mode & FMODE_WRITE) && card->ch_out.is_enabled) - val |= PCM_ENABLE_OUTPUT; - rc = put_user(val, (int *)arg); - break; - - /* Enable full duplex. Since we do this as soon as we are opened - * with O_RDWR, this is mainly a no-op that always returns success. - */ - case SNDCTL_DSP_SETDUPLEX: - DPRINTK ("DSP_SETDUPLEX\n"); - if (!rd || !wr) - break; - rc = 0; - break; - - /* set fragment size. implemented as a successful no-op for now */ - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) { - rc = -EFAULT; - break; - } - DPRINTK ("DSP_SETFRAGMENT, val==%d\n", val); - - if (rd) - rc = via_chan_set_buffering(card, &card->ch_in, val); - - if (wr) - rc = via_chan_set_buffering(card, &card->ch_out, val); - - DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n", - val & 0xFFFF, - val & 0xFFFF, - (val >> 16) & 0xFFFF, - (val >> 16) & 0xFFFF); - - rc = 0; - break; - - /* inform device of an upcoming pause in input (or output). */ - case SNDCTL_DSP_POST: - DPRINTK ("DSP_POST\n"); - if (wr) { - if (card->ch_out.slop_len > 0) - via_chan_flush_frag (&card->ch_out); - via_chan_maybe_start (&card->ch_out); - } - - rc = 0; - break; - - /* not implemented */ - default: - DPRINTK ("unhandled ioctl, cmd==%u, arg==%p\n", - cmd, (void*) arg); - break; - } - - up (&card->syscall_sem); - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - - -static int via_dsp_open (struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct via_info *card; - struct pci_dev *pdev; - struct via_channel *chan; - struct pci_driver *drvr; - int nonblock = (file->f_flags & O_NONBLOCK); - - DPRINTK ("ENTER, minor=%d, file->f_mode=0x%x\n", minor, file->f_mode); - - if (!(file->f_mode & (FMODE_READ | FMODE_WRITE))) { - DPRINTK ("EXIT, returning -EINVAL\n"); - return -EINVAL; - } - - card = NULL; - pci_for_each_dev(pdev) { - drvr = pci_dev_driver (pdev); - if (drvr == &via_driver) { - assert (pci_get_drvdata (pdev) != NULL); - - card = pci_get_drvdata (pdev); - DPRINTK ("dev_dsp = %d, minor = %d, assn = %d\n", - card->dev_dsp, minor, - (card->dev_dsp ^ minor) & ~0xf); - - if (((card->dev_dsp ^ minor) & ~0xf) == 0) - goto match; - } - } - - DPRINTK ("no matching %s found\n", card ? "minor" : "driver"); - return -ENODEV; - -match: - if (nonblock) { - if (down_trylock (&card->open_sem)) { - DPRINTK ("EXIT, returning -EAGAIN\n"); - return -EAGAIN; - } - } else { - if (down_interruptible (&card->open_sem)) { - DPRINTK ("EXIT, returning -ERESTARTSYS\n"); - return -ERESTARTSYS; - } - } - - file->private_data = card; - DPRINTK ("file->f_mode == 0x%x\n", file->f_mode); - - /* handle input from analog source */ - if (file->f_mode & FMODE_READ) { - chan = &card->ch_in; - - via_chan_init (card, chan); - - /* why is this forced to 16-bit stereo in all drivers? */ - chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO; - - via_chan_pcm_fmt (chan, 0); - via_set_rate (&card->ac97, chan, 44100); - } - - /* handle output to analog source */ - if (file->f_mode & FMODE_WRITE) { - chan = &card->ch_out; - - via_chan_init (card, chan); - - if (file->f_mode & FMODE_READ) { - /* 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; - via_chan_pcm_fmt (chan, 0); - 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); - } else { - via_chan_pcm_fmt (chan, 1); - via_set_rate (&card->ac97, chan, 8000); - } - } - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -static int via_dsp_release(struct inode *inode, struct file *file) -{ - struct via_info *card; - int nonblock = (file->f_flags & O_NONBLOCK); - int rc; - - DPRINTK ("ENTER\n"); - - assert (file != NULL); - card = file->private_data; - assert (card != NULL); - - rc = via_syscall_down (card, nonblock); - if (rc) { - DPRINTK ("EXIT (syscall_down error), rc=%d\n", rc); - return rc; - } - - if (file->f_mode & FMODE_WRITE) { - rc = via_dsp_drain_playback (card, &card->ch_out, nonblock); - if (rc) - printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc); - - via_chan_free (card, &card->ch_out); - via_chan_buffer_free(card, &card->ch_out); - } - - if (file->f_mode & FMODE_READ) { - via_chan_free (card, &card->ch_in); - via_chan_buffer_free (card, &card->ch_in); - } - - up (&card->syscall_sem); - up (&card->open_sem); - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -/**************************************************************** - * - * Chip setup and kernel registration - * - * - */ - -static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id *id) -{ -#ifdef CONFIG_MIDI_VIA82CXXX - u8 r42; -#endif - int rc; - struct via_info *card; - static int printed_version = 0; - - DPRINTK ("ENTER\n"); - - if (printed_version++ == 0) - printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n"); - - rc = pci_enable_device (pdev); - if (rc) - goto err_out; - - rc = pci_request_regions (pdev, "via82cxxx_audio"); - if (rc) - goto err_out_disable; - - card = kmalloc (sizeof (*card), GFP_KERNEL); - if (!card) { - printk (KERN_ERR PFX "out of memory, aborting\n"); - rc = -ENOMEM; - goto err_out_res; - } - - pci_set_drvdata (pdev, card); - - memset (card, 0, sizeof (*card)); - card->pdev = pdev; - card->baseaddr = pci_resource_start (pdev, 0); - card->card_num = via_num_cards++; - spin_lock_init (&card->lock); - init_MUTEX (&card->syscall_sem); - init_MUTEX (&card->open_sem); - - /* we must init these now, in case the intr handler needs them */ - via_chan_init_defaults (card, &card->ch_out); - via_chan_init_defaults (card, &card->ch_in); - via_chan_init_defaults (card, &card->ch_fm); - - /* if BAR 2 is present, chip is Rev H or later, - * which means it has a few extra features */ - if (pci_resource_start (pdev, 2) > 0) - card->rev_h = 1; - - if (pdev->irq < 1) { - printk (KERN_ERR PFX "invalid PCI IRQ %d, aborting\n", pdev->irq); - rc = -ENODEV; - goto err_out_kfree; - } - - if (!(pci_resource_flags (pdev, 0) & IORESOURCE_IO)) { - printk (KERN_ERR PFX "unable to locate I/O resources, aborting\n"); - rc = -ENODEV; - goto err_out_kfree; - } - - /* - * init AC97 mixer and codec - */ - rc = via_ac97_init (card); - if (rc) { - printk (KERN_ERR PFX "AC97 init failed, aborting\n"); - goto err_out_kfree; - } - - /* - * init DSP device - */ - rc = via_dsp_init (card); - if (rc) { - printk (KERN_ERR PFX "DSP device init failed, aborting\n"); - goto err_out_have_mixer; - } - - /* - * per-card /proc info - */ - rc = via_card_init_proc (card); - if (rc) { - printk (KERN_ERR PFX "card-specific /proc init failed, aborting\n"); - goto err_out_have_dsp; - } - - /* - * init and turn on interrupts, as the last thing we do - */ - rc = via_interrupt_init (card); - if (rc) { - printk (KERN_ERR PFX "interrupt init failed, aborting\n"); - goto err_out_have_proc; - } - - printk (KERN_INFO PFX "board #%d at 0x%04lX, IRQ %d\n", - card->card_num + 1, card->baseaddr, pdev->irq); - -#ifdef CONFIG_MIDI_VIA82CXXX - /* 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 (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"); - } - } -#endif - - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_have_proc: - via_card_cleanup_proc (card); - -err_out_have_dsp: - via_dsp_cleanup (card); - -err_out_have_mixer: - via_ac97_cleanup (card); - -err_out_kfree: -#ifndef VIA_NDEBUG - memset (card, 0xAB, sizeof (*card)); /* poison memory */ -#endif - kfree (card); - -err_out_res: - pci_release_regions (pdev); - -err_out_disable: - pci_disable_device (pdev); - -err_out: - pci_set_drvdata (pdev, NULL); - DPRINTK ("EXIT - returning %d\n", rc); - return rc; -} - - -static void __exit via_remove_one (struct pci_dev *pdev) -{ - struct via_info *card; - - DPRINTK ("ENTER\n"); - - assert (pdev != NULL); - card = pci_get_drvdata (pdev); - assert (card != NULL); - -#ifdef CONFIG_MIDI_VIA82CXXX - if (card->midi_info.io_base) - unload_uart401(&card->midi_info); -#endif - - free_irq (card->pdev->irq, card); - via_card_cleanup_proc (card); - via_dsp_cleanup (card); - via_ac97_cleanup (card); - -#ifndef VIA_NDEBUG - memset (card, 0xAB, sizeof (*card)); /* poison memory */ -#endif - kfree (card); - - pci_set_drvdata (pdev, NULL); - - pci_release_regions (pdev); - pci_disable_device (pdev); - pci_set_power_state (pdev, 3); /* ...zzzzzz */ - - DPRINTK ("EXIT\n"); - return; -} - - -/**************************************************************** - * - * Driver initialization and cleanup - * - * - */ - -static int __init init_via82cxxx_audio(void) -{ - int rc; - - DPRINTK ("ENTER\n"); - - rc = via_init_proc (); - if (rc) { - DPRINTK ("EXIT, returning %d\n", rc); - return rc; - } - - rc = pci_register_driver (&via_driver); - if (rc < 1) { - if (rc == 0) - pci_unregister_driver (&via_driver); - via_cleanup_proc (); - DPRINTK ("EXIT, returning -ENODEV\n"); - return -ENODEV; - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -static void __exit cleanup_via82cxxx_audio(void) -{ - DPRINTK ("ENTER\n"); - - pci_unregister_driver (&via_driver); - via_cleanup_proc (); - - DPRINTK ("EXIT\n"); -} - - -module_init(init_via82cxxx_audio); -module_exit(cleanup_via82cxxx_audio); - -MODULE_AUTHOR("Jeff Garzik"); -MODULE_DESCRIPTION("DSP audio and mixer driver for Via 82Cxxx audio devices"); -MODULE_LICENSE("GPL"); - -EXPORT_NO_SYMBOLS; - - - -#ifdef VIA_PROC_FS - -/**************************************************************** - * - * /proc/driver/via/info - * - * - */ - -static int via_info_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ -#define YN(val,bit) (((val) & (bit)) ? "yes" : "no") -#define ED(val,bit) (((val) & (bit)) ? "enable" : "disable") - - int len = 0; - u8 r40, r41, r42, r44; - struct via_info *card = data; - - DPRINTK ("ENTER\n"); - - assert (card != NULL); - - len += sprintf (page+len, VIA_CARD_NAME "\n\n"); - - pci_read_config_byte (card->pdev, 0x40, &r40); - pci_read_config_byte (card->pdev, 0x41, &r41); - pci_read_config_byte (card->pdev, 0x42, &r42); - pci_read_config_byte (card->pdev, 0x44, &r44); - - len += sprintf (page+len, - "Via 82Cxxx PCI registers:\n" - "\n" - "40 Codec Ready: %s\n" - " Codec Low-power: %s\n" - " Secondary Codec Ready: %s\n" - "\n" - "41 Interface Enable: %s\n" - " De-Assert Reset: %s\n" - " Force SYNC high: %s\n" - " Force SDO high: %s\n" - " Variable Sample Rate On-Demand Mode: %s\n" - " SGD Read Channel PCM Data Out: %s\n" - " FM Channel PCM Data Out: %s\n" - " SB PCM Data Out: %s\n" - "\n" - "42 Game port enabled: %s\n" - " SoundBlaster enabled: %s\n" - " FM enabled: %s\n" - " MIDI enabled: %s\n" - "\n" - "44 AC-Link Interface Access: %s\n" - " Secondary Codec Support: %s\n" - - "\n", - - YN (r40, VIA_CR40_AC97_READY), - YN (r40, VIA_CR40_AC97_LOW_POWER), - YN (r40, VIA_CR40_SECONDARY_READY), - - ED (r41, VIA_CR41_AC97_ENABLE), - YN (r41, (1 << 6)), - YN (r41, (1 << 5)), - YN (r41, (1 << 4)), - ED (r41, (1 << 3)), - ED (r41, (1 << 2)), - ED (r41, (1 << 1)), - ED (r41, (1 << 0)), - - YN (r42, VIA_CR42_GAME_ENABLE), - YN (r42, VIA_CR42_SB_ENABLE), - YN (r42, VIA_CR42_FM_ENABLE), - YN (r42, VIA_CR42_MIDI_ENABLE), - - YN (r44, VIA_CR44_AC_LINK_ACCESS), - YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT) - - ); - - DPRINTK ("EXIT, returning %d\n", len); - return len; - -#undef YN -#undef ED -} - - -/**************************************************************** - * - * /proc/driver/via/... setup and cleanup - * - * - */ - -static int __init via_init_proc (void) -{ - DPRINTK ("ENTER\n"); - - if (!proc_mkdir ("driver/via", 0)) - return -EIO; - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -static void via_cleanup_proc (void) -{ - DPRINTK ("ENTER\n"); - - remove_proc_entry ("driver/via", NULL); - - DPRINTK ("EXIT\n"); -} - - -static int __init via_card_init_proc (struct via_info *card) -{ - char s[32]; - int rc; - - DPRINTK ("ENTER\n"); - - sprintf (s, "driver/via/%d", card->card_num); - if (!proc_mkdir (s, 0)) { - rc = -EIO; - goto err_out_none; - } - - sprintf (s, "driver/via/%d/info", card->card_num); - if (!create_proc_read_entry (s, 0, 0, via_info_read_proc, card)) { - rc = -EIO; - goto err_out_dir; - } - - sprintf (s, "driver/via/%d/ac97", card->card_num); - if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) { - rc = -EIO; - goto err_out_info; - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_info: - sprintf (s, "driver/via/%d/info", card->card_num); - remove_proc_entry (s, NULL); - -err_out_dir: - sprintf (s, "driver/via/%d", card->card_num); - remove_proc_entry (s, NULL); - -err_out_none: - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - - -static void via_card_cleanup_proc (struct via_info *card) -{ - char s[32]; - - DPRINTK ("ENTER\n"); - - sprintf (s, "driver/via/%d/ac97", card->card_num); - remove_proc_entry (s, NULL); - - sprintf (s, "driver/via/%d/info", card->card_num); - remove_proc_entry (s, NULL); - - sprintf (s, "driver/via/%d", card->card_num); - remove_proc_entry (s, NULL); - - DPRINTK ("EXIT\n"); -} - -#endif /* VIA_PROC_FS */ diff -Nru a/drivers/sound/vidc.c b/drivers/sound/vidc.c --- a/drivers/sound/vidc.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,549 +0,0 @@ -/* - * linux/drivers/sound/vidc.c - * - * Copyright (C) 1997-2000 by Russell King - * - * 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. - * - * VIDC20 audio driver. - * - * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA - * engine. The DMA transfers fixed-format (16-bit little-endian linear) - * samples to the VIDC20, which then transfers this data serially to the - * DACs. The samplerate is controlled by the VIDC. - * - * We currently support a mixer device, but it is currently non-functional. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "sound_config.h" -#include "vidc.h" - -#ifndef _SIOC_TYPE -#define _SIOC_TYPE(x) _IOC_TYPE(x) -#endif -#ifndef _SIOC_NR -#define _SIOC_NR(x) _IOC_NR(x) -#endif - -#define VIDC_SOUND_CLOCK (250000) - -/* - * When using SERIAL SOUND mode (external DAC), the number of physical - * channels is fixed at 2. - */ -static int vidc_busy; -static int vidc_adev; -static int vidc_audio_rate; -static char vidc_audio_format; -static char vidc_audio_channels; - -static unsigned char vidc_level_l[SOUND_MIXER_NRDEVICES] = { - 85, /* master */ - 50, /* bass */ - 50, /* treble */ - 0, /* synth */ - 75, /* pcm */ - 0, /* speaker */ - 100, /* ext line */ - 0, /* mic */ - 100, /* CD */ - 0, -}; - -static unsigned char vidc_level_r[SOUND_MIXER_NRDEVICES] = { - 85, /* master */ - 50, /* bass */ - 50, /* treble */ - 0, /* synth */ - 75, /* pcm */ - 0, /* speaker */ - 100, /* ext line */ - 0, /* mic */ - 100, /* CD */ - 0, -}; - -static unsigned int vidc_audio_volume_l; /* left PCM vol, 0 - 65536 */ -static unsigned int vidc_audio_volume_r; /* right PCM vol, 0 - 65536 */ - -static void (*old_mksound)(unsigned int hz, unsigned int ticks); -extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); -extern void vidc_update_filler(int bits, int channels); -extern int softoss_dev; - -static void -vidc_mksound(unsigned int hz, unsigned int ticks) -{ -// printk("BEEP - %d %d!\n", hz, ticks); -} - -static void -vidc_mixer_set(int mdev, unsigned int level) -{ - unsigned int lev_l = level & 0x007f; - unsigned int lev_r = (level & 0x7f00) >> 8; - unsigned int mlev_l, mlev_r; - - if (lev_l > 100) - lev_l = 100; - if (lev_r > 100) - lev_r = 100; - -#define SCALE(lev,master) ((lev) * (master) * 65536 / 10000) - - mlev_l = vidc_level_l[SOUND_MIXER_VOLUME]; - mlev_r = vidc_level_r[SOUND_MIXER_VOLUME]; - - switch (mdev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_PCM: - vidc_level_l[mdev] = lev_l; - vidc_level_r[mdev] = lev_r; - - vidc_audio_volume_l = SCALE(lev_l, mlev_l); - vidc_audio_volume_r = SCALE(lev_r, mlev_r); -/*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/ - break; - } -#undef SCALE -} - -static int vidc_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - unsigned int val; - unsigned int mdev; - - if (_SIOC_TYPE(cmd) != 'M') - return -EINVAL; - - mdev = _SIOC_NR(cmd); - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - if (get_user(val, (unsigned int *)arg)) - return -EFAULT; - - if (mdev < SOUND_MIXER_NRDEVICES) - vidc_mixer_set(mdev, val); - else - return -EINVAL; - } - - /* - * Return parameters - */ - switch (mdev) { - case SOUND_MIXER_RECSRC: - val = 0; - break; - - case SOUND_MIXER_DEVMASK: - val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH; - break; - - case SOUND_MIXER_STEREODEVS: - val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH; - break; - - case SOUND_MIXER_RECMASK: - val = 0; - break; - - case SOUND_MIXER_CAPS: - val = 0; - break; - - default: - if (mdev < SOUND_MIXER_NRDEVICES) - val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8; - else - return -EINVAL; - } - - return put_user(val, (unsigned int *)arg) ? -EFAULT : 0; -} - -static unsigned int vidc_audio_set_format(int dev, unsigned int fmt) -{ - switch (fmt) { - default: - fmt = AFMT_S16_LE; - case AFMT_U8: - case AFMT_S8: - case AFMT_S16_LE: - vidc_audio_format = fmt; - vidc_update_filler(vidc_audio_format, vidc_audio_channels); - case AFMT_QUERY: - break; - } - return vidc_audio_format; -} - -static int vidc_audio_set_speed(int dev, int rate) -{ - if (rate) { - unsigned int hwctrl, hwrate; - unsigned int newsize, new2size; - - /* - * If we have selected 44.1kHz, use the DAC clock. - */ - if (0 && rate == 44100) { - hwctrl = 0x00000002; - hwrate = 3; - } else { - hwctrl = 0x00000003; - - hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1; - if (hwrate < 3) - hwrate = 3; - if (hwrate > 255) - hwrate = 255; - - rate = VIDC_SOUND_CLOCK / hwrate; - } - - vidc_writel(0xb0000000 | (hwrate - 2)); - vidc_writel(0xb1000000 | hwctrl); - - newsize = (10000 / hwrate) & ~3; - if (newsize < 208) - newsize = 208; - if (newsize > 4096) - newsize = 4096; - for (new2size = 128; new2size < newsize; new2size <<= 1); - if (new2size - newsize > newsize - (new2size >> 1)) - new2size >>= 1; - if (new2size > 4096) { - printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n", - newsize, new2size); - new2size = 4096; - } - dma_bufsize = new2size; - vidc_audio_rate = rate; - } - return vidc_audio_rate; -} - -static short vidc_audio_set_channels(int dev, short channels) -{ - switch (channels) { - default: - channels = 2; - case 1: - case 2: - vidc_audio_channels = channels; - vidc_update_filler(vidc_audio_format, vidc_audio_channels); - case 0: - break; - } - return vidc_audio_channels; -} - -/* - * Open the device - */ -static int vidc_audio_open(int dev, int mode) -{ - /* This audio device does not have recording capability */ - if (mode == OPEN_READ) - return -EPERM; - - if (vidc_busy) - return -EBUSY; - - vidc_busy = 1; - return 0; -} - -/* - * Close the device - */ -static void vidc_audio_close(int dev) -{ - vidc_busy = 0; -} - -/* - * Output a block via DMA to sound device. - * - * We just set the DMA start and count; the DMA interrupt routine - * will take care of formatting the samples (via the appropriate - * vidc_filler routine), and flag via vidc_audio_dma_interrupt when - * more data is required. - */ -static void -vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one) -{ - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - unsigned long flags; - - local_irq_save(flags); - dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf; - dma_count = total_count; - local_irq_restore(flags); -} - -static void -vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag) -{ -} - -static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - return -EINVAL; -} - -static void vidc_audio_dma_interrupt(void) -{ - DMAbuf_outputintr(vidc_adev, 1); -} - -/* - * Prepare for outputting samples. - * - * Each buffer that will be passed will be `bsize' bytes long, - * with a total of `bcount' buffers. - */ -static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - struct audio_operations *adev = audio_devs[dev]; - - dma_interrupt = NULL; - adev->dmap_out->flags |= DMA_NODMA; - - return 0; -} - -/* - * Stop our current operation. - */ -static void vidc_audio_reset(int dev) -{ - dma_interrupt = NULL; -} - -static int vidc_audio_local_qlen(int dev) -{ - return /*dma_count !=*/ 0; -} - -static void vidc_audio_trigger(int dev, int enable_bits) -{ - struct audio_operations *adev = audio_devs[dev]; - - if (enable_bits & PCM_ENABLE_OUTPUT) { - if (!(adev->flags & DMA_ACTIVE)) { - unsigned long flags; - - local_irq_save(flags); - - /* prevent recusion */ - adev->flags |= DMA_ACTIVE; - - dma_interrupt = vidc_audio_dma_interrupt; - vidc_sound_dma_irq(0, NULL, NULL); - iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR); - - local_irq_restore(flags); - } - } -} - -static struct audio_driver vidc_audio_driver = -{ - owner: THIS_MODULE, - open: vidc_audio_open, - close: vidc_audio_close, - output_block: vidc_audio_output_block, - start_input: vidc_audio_start_input, - prepare_for_input: vidc_audio_prepare_for_input, - prepare_for_output: vidc_audio_prepare_for_output, - halt_io: vidc_audio_reset, - local_qlen: vidc_audio_local_qlen, - trigger: vidc_audio_trigger, - set_speed: vidc_audio_set_speed, - set_bits: vidc_audio_set_format, - set_channels: vidc_audio_set_channels -}; - -static struct mixer_operations vidc_mixer_operations = { - owner: THIS_MODULE, - id: "VIDC", - name: "VIDCsound", - ioctl: vidc_mixer_ioctl -}; - -void vidc_update_filler(int format, int channels) -{ -#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) - - switch (TYPE(format, channels)) { - default: - case TYPE(AFMT_U8, 1): - vidc_filler = vidc_fill_1x8_u; - break; - - case TYPE(AFMT_U8, 2): - vidc_filler = vidc_fill_2x8_u; - break; - - case TYPE(AFMT_S8, 1): - vidc_filler = vidc_fill_1x8_s; - break; - - case TYPE(AFMT_S8, 2): - vidc_filler = vidc_fill_2x8_s; - break; - - case TYPE(AFMT_S16_LE, 1): - vidc_filler = vidc_fill_1x16_s; - break; - - case TYPE(AFMT_S16_LE, 2): - vidc_filler = vidc_fill_2x16_s; - break; - } -} - -static void __init attach_vidc(struct address_info *hw_config) -{ - char name[32]; - int i, adev; - - sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype); - conf_printf(name, hw_config); - memset(dma_buf, 0, sizeof(dma_buf)); - - adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name, - &vidc_audio_driver, sizeof(vidc_audio_driver), - DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE, - NULL, hw_config->dma, hw_config->dma2); - - if (adev < 0) - goto audio_failed; - - /* - * 1024 bytes => 64 buffers - */ - audio_devs[adev]->min_fragment = 10; - audio_devs[adev]->mixer_dev = num_mixers; - - audio_devs[adev]->mixer_dev = - sound_install_mixer(MIXER_DRIVER_VERSION, - name, &vidc_mixer_operations, - sizeof(vidc_mixer_operations), NULL); - - if (audio_devs[adev]->mixer_dev < 0) - goto mixer_failed; - - for (i = 0; i < 2; i++) { - dma_buf[i] = get_free_page(GFP_KERNEL); - if (!dma_buf[i]) { - printk(KERN_ERR "%s: can't allocate required buffers\n", - name); - goto mem_failed; - } - dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]); - } - - if (sound_alloc_dma(hw_config->dma, hw_config->name)) { - printk(KERN_ERR "%s: DMA %d is in use\n", name, hw_config->dma); - goto dma_failed; - } - - if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0, - hw_config->name, &dma_start)) { - printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq); - goto irq_failed; - } - old_mksound = kd_mksound; - kd_mksound = vidc_mksound; - vidc_adev = adev; - vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8)); - -#if defined(CONFIG_SOUND_SOFTOSS) || defined(CONFIG_SOUND_SOFTOSS_MODULE) - softoss_dev = adev; -#endif - return; - -irq_failed: - sound_free_dma(hw_config->dma); -dma_failed: -mem_failed: - for (i = 0; i < 2; i++) - free_page(dma_buf[i]); - sound_unload_mixerdev(audio_devs[adev]->mixer_dev); -mixer_failed: - sound_unload_audiodev(adev); -audio_failed: - return; -} - -static int __init probe_vidc(struct address_info *hw_config) -{ - hw_config->irq = IRQ_DMAS0; - hw_config->dma = DMA_VIRTUAL_SOUND; - hw_config->dma2 = -1; - hw_config->card_subtype = 16; - hw_config->name = "VIDC20"; - return 1; -} - -static void __exit unload_vidc(struct address_info *hw_config) -{ - int i, adev = vidc_adev; - - vidc_adev = -1; - - if (old_mksound) - kd_mksound = old_mksound; - - free_irq(hw_config->irq, &dma_start); - sound_free_dma(hw_config->dma); - - if (adev >= 0) { - sound_unload_mixerdev(audio_devs[adev]->mixer_dev); - sound_unload_audiodev(adev); - for (i = 0; i < 2; i++) - free_page(dma_buf[i]); - } -} - -static struct address_info cfg; - -static int __init init_vidc(void) -{ - if (probe_vidc(&cfg) == 0) - return -ENODEV; - - attach_vidc(&cfg); - - return 0; -} - -static void __exit cleanup_vidc(void) -{ - unload_vidc(&cfg); -} - -module_init(init_vidc); -module_exit(cleanup_vidc); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("VIDC20 audio driver"); -MODULE_LICENSE("GPL"); -EXPORT_NO_SYMBOLS; diff -Nru a/drivers/sound/vidc.h b/drivers/sound/vidc.h --- a/drivers/sound/vidc.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,67 +0,0 @@ -/* - * linux/drivers/sound/vidc.h - * - * Copyright (C) 1997 Russell King - * - * 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. - * - * VIDC sound function prototypes - */ - -/* vidc.c */ - -extern int vidc_busy; - -/* vidc_fill.S */ - -/* - * Filler routines for different channels and sample sizes - */ - -extern unsigned long vidc_fill_1x8_u(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); -extern unsigned long vidc_fill_2x8_u(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); -extern unsigned long vidc_fill_1x8_s(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); -extern unsigned long vidc_fill_2x8_s(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); -extern unsigned long vidc_fill_1x16_s(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); -extern unsigned long vidc_fill_2x16_s(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); - -/* - * DMA Interrupt handler - */ - -extern void vidc_sound_dma_irq(int irqnr, void *ref, struct pt_regs *regs); - -/* - * Filler routine pointer - */ - -extern unsigned long (*vidc_filler) (unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); - -/* - * Virtual DMA buffer exhausted - */ - -extern void (*dma_interrupt) (void); - -/* - * Virtual DMA buffer addresses - */ - -extern unsigned long dma_start, dma_count, dma_bufsize; -extern unsigned long dma_buf[2], dma_pbuf[2]; - -/* vidc_synth.c */ - -extern void vidc_synth_init(struct address_info *hw_config); -extern void vidc_synth_exit(struct address_info *hw_config); -extern int vidc_synth_get_volume(void); -extern int vidc_synth_set_volume(int vol); diff -Nru a/drivers/sound/vidc_fill.S b/drivers/sound/vidc_fill.S --- a/drivers/sound/vidc_fill.S Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,218 +0,0 @@ -/* - * linux/drivers/sound/vidc_fill.S - * - * Copyright (C) 1997 Russell King - * - * 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. - * - * Filler routines for DMA buffers - */ -#define __ASSEMBLY__ -#include -#include -#include -#include - - .text - -ENTRY(vidc_fill_1x8_u) - mov ip, #0xff00 -1: cmp r0, r1 - bge SYMBOL_NAME(vidc_clear) - ldrb r4, [r0], #1 - eor r4, r4, #0x80 - and r4, ip, r4, lsl #8 - orr r4, r4, r4, lsl #16 - str r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_2x8_u) - mov ip, #0xff00 -1: cmp r0, r1 - bge SYMBOL_NAME(vidc_clear) - ldr r4, [r0], #2 - and r5, r4, ip - and r4, ip, r4, lsl #8 - orr r4, r4, r5, lsl #16 - orr r4, r4, r4, lsr #8 - str r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_1x8_s) - mov ip, #0xff00 -1: cmp r0, r1 - bge SYMBOL_NAME(vidc_clear) - ldrb r4, [r0], #1 - and r4, ip, r4, lsl #8 - orr r4, r4, r4, lsl #16 - str r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_2x8_s) - mov ip, #0xff00 -1: cmp r0, r1 - bge SYMBOL_NAME(vidc_clear) - ldr r4, [r0], #2 - and r5, r4, ip - and r4, ip, r4, lsl #8 - orr r4, r4, r5, lsl #16 - orr r4, r4, r4, lsr #8 - str r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_1x16_s) - mov ip, #0xff00 - orr ip, ip, ip, lsr #8 -1: cmp r0, r1 - bge SYMBOL_NAME(vidc_clear) - ldr r5, [r0], #2 - and r4, r5, ip - orr r4, r4, r4, lsl #16 - str r4, [r2], #4 - cmp r0, r1 - addlt r0, r0, #2 - andlt r4, r5, ip, lsl #16 - orrlt r4, r4, r4, lsr #16 - strlt r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_2x16_s) - mov ip, #0xff00 - orr ip, ip, ip, lsr #8 -1: cmp r0, r1 - bge SYMBOL_NAME(vidc_clear) - ldr r4, [r0], #4 - str r4, [r2], #4 - cmp r0, r1 - ldrlt r4, [r0], #4 - strlt r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_noaudio) - mov r0, #0 - mov r1, #0 -2: mov r4, #0 - mov r5, #0 -1: cmp r2, r3 - stmltia r2!, {r0, r1, r4, r5} - blt 1b - mov pc, lr - -ENTRY(vidc_clear) - mov r0, #0 - mov r1, #0 - tst r2, #4 - str r0, [r2], #4 - tst r2, #8 - stmia r2!, {r0, r1} - b 2b - -/* - * Call filler routines with: - * r0 = phys address - * r1 = phys end - * r2 = buffer - * Returns: - * r0 = new buffer address - * r2 = new buffer finish - * r4 = corrupted - * r5 = corrupted - * ip = corrupted - */ - -ENTRY(vidc_sound_dma_irq) - stmfd sp!, {r4 - r8, lr} - ldr r8, =SYMBOL_NAME(dma_start) - ldmia r8, {r0, r1, r2, r3, r4, r5} - teq r1, #0 - adreq r4, SYMBOL_NAME(vidc_fill_noaudio) - moveq r7, #1 << 31 - movne r7, #0 - mov ip, #IOMD_BASE & 0xff000000 - orr ip, ip, #IOMD_BASE & 0x00ff0000 - ldrb r6, [ip, #IOMD_SD0ST] - tst r6, #DMA_ST_OFL @ Check for overrun - eorne r6, r6, #DMA_ST_AB - tst r6, #DMA_ST_AB - moveq r2, r3 @ DMAing A, update B - add r3, r2, r5 @ End of DMA buffer - add r1, r1, r0 @ End of virtual DMA buffer - mov lr, pc - mov pc, r4 @ Call fill routine (uses r4, ip) - sub r1, r1, r0 @ Remaining length - stmia r8, {r0, r1} - mov r0, #0 - tst r2, #4 @ Round buffer up to 4 words - strne r0, [r2], #4 - tst r2, #8 - strne r0, [r2], #4 - strne r0, [r2], #4 - sub r2, r2, #16 - mov r2, r2, lsl #20 - movs r2, r2, lsr #20 - orreq r2, r2, #1 << 30 @ Set L bit - orr r2, r2, r7 - ldmdb r8, {r3, r4, r5} - tst r6, #DMA_ST_AB - mov ip, #IOMD_BASE & 0xff000000 - orr ip, ip, #IOMD_BASE & 0x00ff0000 - streq r4, [ip, #IOMD_SD0CURB] - strne r5, [ip, #IOMD_SD0CURA] - streq r2, [ip, #IOMD_SD0ENDB] - strne r2, [ip, #IOMD_SD0ENDA] - ldr lr, [ip, #IOMD_SD0ST] - tst lr, #DMA_ST_OFL - bne 1f - tst r6, #DMA_ST_AB - strne r4, [ip, #IOMD_SD0CURB] - streq r5, [ip, #IOMD_SD0CURA] - strne r2, [ip, #IOMD_SD0ENDB] - streq r2, [ip, #IOMD_SD0ENDA] -1: teq r7, #0 - mov r0, #0x10 - strneb r0, [ip, #IOMD_SD0CR] - ldmfd sp!, {r4 - r8, lr} - teq r1, #0 @ If we have no more - movne pc, lr - teq r3, #0 - movne pc, r3 @ Call interrupt routine - mov pc, lr - - .data - .globl SYMBOL_NAME(dma_interrupt) -SYMBOL_NAME(dma_interrupt): - .long 0 @ r3 - .globl SYMBOL_NAME(dma_pbuf) -SYMBOL_NAME(dma_pbuf): - .long 0 @ r4 - .long 0 @ r5 - .globl SYMBOL_NAME(dma_start) -SYMBOL_NAME(dma_start): - .long 0 @ r0 - .globl SYMBOL_NAME(dma_count) -SYMBOL_NAME(dma_count): - .long 0 @ r1 - .globl SYMBOL_NAME(dma_buf) -SYMBOL_NAME(dma_buf): - .long 0 @ r2 - .long 0 @ r3 - .globl SYMBOL_NAME(vidc_filler) -SYMBOL_NAME(vidc_filler): - .long SYMBOL_NAME(vidc_fill_noaudio) @ r4 - .globl SYMBOL_NAME(dma_bufsize) -SYMBOL_NAME(dma_bufsize): - .long 0x1000 @ r5 diff -Nru a/drivers/sound/vwsnd.c b/drivers/sound/vwsnd.c --- a/drivers/sound/vwsnd.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3477 +0,0 @@ -/* - * Sound driver for Silicon Graphics 320 and 540 Visual Workstations' - * onboard audio. See notes in ../../Documentation/sound/vwsnd . - * - * Copyright 1999 Silicon Graphics, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; 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. - */ - -#undef VWSND_DEBUG /* define for debugging */ - -/* - * XXX to do - - * - * External sync. - * Rename swbuf, hwbuf, u&i, hwptr&swptr to something rational. - * Bug - if select() called before read(), pcm_setup() not called. - * Bug - output doesn't stop soon enough if process killed. - */ - -/* - * Things to test - - * - * Will readv/writev work? Write a test. - * - * insmod/rmmod 100 million times. - * - * Run I/O until int ptrs wrap around (roughly 6.2 hours @ DAT - * rate). - * - * Concurrent threads banging on mixer simultaneously, both UP - * and SMP kernels. Especially, watch for thread A changing - * OUTSRC while thread B changes gain -- both write to the same - * ad1843 register. - * - * What happens if a client opens /dev/audio then forks? - * Do two procs have /dev/audio open? Test. - * - * Pump audio through the CD, MIC and line inputs and verify that - * they mix/mute into the output. - * - * Apps: - * amp - * mpg123 - * x11amp - * mxv - * kmedia - * esound - * need more input apps - * - * Run tests while bombarding with signals. setitimer(2) will do it... */ - -/* - * This driver is organized in nine sections. - * The nine sections are: - * - * debug stuff - * low level lithium access - * high level lithium access - * AD1843 access - * PCM I/O - * audio driver - * mixer driver - * probe/attach/unload - * initialization and loadable kernel module interface - * - * That is roughly the order of increasing abstraction, so forward - * dependencies are minimal. - */ - -/* - * Locking Notes - * - * INC_USE_COUNT and DEC_USE_COUNT keep track of the number of - * open descriptors to this driver. They store it in vwsnd_use_count. - * The global device list, vwsnd_dev_list, is immutable when the IN_USE - * is true. - * - * devc->open_lock is a semaphore that is used to enforce the - * single reader/single writer rule for /dev/audio. The rule is - * that each device may have at most one reader and one writer. - * Open will block until the previous client has closed the - * device, unless O_NONBLOCK is specified. - * - * The semaphore devc->io_sema serializes PCM I/O syscalls. This - * is unnecessary in Linux 2.2, because the kernel lock - * serializes read, write, and ioctl globally, but it's there, - * ready for the brave, new post-kernel-lock world. - * - * Locking between interrupt and baselevel is handled by the - * "lock" spinlock in vwsnd_port (one lock each for read and - * write). Each half holds the lock just long enough to see what - * area it owns and update its pointers. See pcm_output() and - * pcm_input() for most of the gory stuff. - * - * devc->mix_sema serializes all mixer ioctls. This is also - * redundant because of the kernel lock. - * - * The lowest level lock is lith->lithium_lock. It is a - * spinlock which is held during the two-register tango of - * reading/writing an AD1843 register. See - * li_{read,write}_ad1843_reg(). - */ - -/* - * Sample Format Notes - * - * Lithium's DMA engine has two formats: 16-bit 2's complement - * and 8-bit unsigned . 16-bit transfers the data unmodified, 2 - * bytes per sample. 8-bit unsigned transfers 1 byte per sample - * and XORs each byte with 0x80. Lithium can input or output - * either mono or stereo in either format. - * - * The AD1843 has four formats: 16-bit 2's complement, 8-bit - * unsigned, 8-bit mu-Law and 8-bit A-Law. - * - * This driver supports five formats: AFMT_S8, AFMT_U8, - * AFMT_MU_LAW, AFMT_A_LAW, and AFMT_S16_LE. - * - * For AFMT_U8 output, we keep the AD1843 in 16-bit mode, and - * rely on Lithium's XOR to translate between U8 and S8. - * - * For AFMT_S8, AFMT_MU_LAW and AFMT_A_LAW output, we have to XOR - * the 0x80 bit in software to compensate for Lithium's XOR. - * This happens in pcm_copy_{in,out}(). - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz - * Added some __init/__exit - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sound_config.h" - -/*****************************************************************************/ -/* debug stuff */ - -#ifdef VWSND_DEBUG - -#include /* for in_interrupt() */ - -static int shut_up = 1; - -/* - * dbgassert - called when an assertion fails. - */ - -static void dbgassert(const char *fcn, int line, const char *expr) -{ - if (in_interrupt()) - panic("ASSERTION FAILED IN INTERRUPT, %s:%s:%d %s\n", - __FILE__, fcn, line, expr); - else { - int x; - printk(KERN_ERR "ASSERTION FAILED, %s:%s:%d %s\n", - __FILE__, fcn, line, expr); - x = * (volatile int *) 0; /* force proc to exit */ - } -} - -/* - * Bunch of useful debug macros: - * - * ASSERT - print unless e nonzero (panic if in interrupt) - * DBGDO - include arbitrary code if debugging - * DBGX - debug print raw (w/o function name) - * DBGP - debug print w/ function name - * DBGE - debug print function entry - * DBGC - debug print function call - * DBGR - debug print function return - * DBGXV - debug print raw when verbose - * DBGPV - debug print when verbose - * DBGEV - debug print function entry when verbose - * DBGRV - debug print function return when verbose - */ - -#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__FUNCTION__, __LINE__, #e)) -#define DBGDO(x) x -#define DBGX(fmt, args...) (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args)) -#define DBGP(fmt, args...) (DBGX(__FUNCTION__ ": " fmt, ##args)) -#define DBGE(fmt, args...) (DBGX(__FUNCTION__ fmt, ##args)) -#define DBGC(rtn) (DBGP("calling %s\n", rtn)) -#define DBGR() (DBGP("returning\n")) -#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args)) -#define DBGPV(fmt, args...) (shut_up ? 0 : DBGP(fmt, ##args)) -#define DBGEV(fmt, args...) (shut_up ? 0 : DBGE(fmt, ##args)) -#define DBGCV(rtn) (shut_up ? 0 : DBGC(rtn)) -#define DBGRV() (shut_up ? 0 : DBGR()) - -#else /* !VWSND_DEBUG */ - -#define ASSERT(e) ((void) 0) -#define DBGDO(x) /* don't */ -#define DBGX(fmt, args...) ((void) 0) -#define DBGP(fmt, args...) ((void) 0) -#define DBGE(fmt, args...) ((void) 0) -#define DBGC(rtn) ((void) 0) -#define DBGR() ((void) 0) -#define DBGPV(fmt, args...) ((void) 0) -#define DBGXV(fmt, args...) ((void) 0) -#define DBGEV(fmt, args...) ((void) 0) -#define DBGCV(rtn) ((void) 0) -#define DBGRV() ((void) 0) - -#endif /* !VWSND_DEBUG */ - -/*****************************************************************************/ -/* low level lithium access */ - -/* - * We need to talk to Lithium registers on three pages. Here are - * the pages' offsets from the base address (0xFF001000). - */ - -enum { - LI_PAGE0_OFFSET = 0x01000 - 0x1000, /* FF001000 */ - LI_PAGE1_OFFSET = 0x0F000 - 0x1000, /* FF00F000 */ - LI_PAGE2_OFFSET = 0x10000 - 0x1000, /* FF010000 */ -}; - -/* low-level lithium data */ - -typedef struct lithium { - caddr_t page0; /* virtual addresses */ - caddr_t page1; - caddr_t page2; - spinlock_t lock; /* protects codec and UST/MSC access */ -} lithium_t; - -/* - * li_create initializes the lithium_t structure and sets up vm mappings - * to access the registers. - * Returns 0 on success, -errno on failure. - */ - -static int li_create(lithium_t *lith, unsigned long baseaddr) -{ - static void li_destroy(lithium_t *); - - lith->lock = SPIN_LOCK_UNLOCKED; - lith->page0 = ioremap_nocache(baseaddr + LI_PAGE0_OFFSET, PAGE_SIZE); - lith->page1 = ioremap_nocache(baseaddr + LI_PAGE1_OFFSET, PAGE_SIZE); - lith->page2 = ioremap_nocache(baseaddr + LI_PAGE2_OFFSET, PAGE_SIZE); - if (!lith->page0 || !lith->page1 || !lith->page2) { - li_destroy(lith); - return -ENOMEM; - } - return 0; -} - -/* - * li_destroy destroys the lithium_t structure and vm mappings. - */ - -static void li_destroy(lithium_t *lith) -{ - if (lith->page0) { - iounmap(lith->page0); - lith->page0 = NULL; - } - if (lith->page1) { - iounmap(lith->page1); - lith->page1 = NULL; - } - if (lith->page2) { - iounmap(lith->page2); - lith->page2 = NULL; - } -} - -/* - * basic register accessors - read/write long/byte - */ - -static __inline__ unsigned long li_readl(lithium_t *lith, int off) -{ - return * (volatile unsigned long *) (lith->page0 + off); -} - -static __inline__ unsigned char li_readb(lithium_t *lith, int off) -{ - return * (volatile unsigned char *) (lith->page0 + off); -} - -static __inline__ void li_writel(lithium_t *lith, int off, unsigned long val) -{ - * (volatile unsigned long *) (lith->page0 + off) = val; -} - -static __inline__ void li_writeb(lithium_t *lith, int off, unsigned char val) -{ - * (volatile unsigned char *) (lith->page0 + off) = val; -} - -/*****************************************************************************/ -/* High Level Lithium Access */ - -/* - * Lithium DMA Notes - * - * Lithium has two dedicated DMA channels for audio. They are known - * as comm1 and comm2 (communication areas 1 and 2). Comm1 is for - * input, and comm2 is for output. Each is controlled by three - * registers: BASE (base address), CFG (config) and CCTL - * (config/control). - * - * Each DMA channel points to a physically contiguous ring buffer in - * main memory of up to 8 Kbytes. (This driver always uses 8 Kb.) - * There are three pointers into the ring buffer: read, write, and - * trigger. The pointers are 8 bits each. Each pointer points to - * 32-byte "chunks" of data. The DMA engine moves 32 bytes at a time, - * so there is no finer-granularity control. - * - * In comm1, the hardware updates the write ptr, and software updates - * the read ptr. In comm2, it's the opposite: hardware updates the - * read ptr, and software updates the write ptr. I designate the - * hardware-updated ptr as the hwptr, and the software-updated ptr as - * the swptr. - * - * The trigger ptr and trigger mask are used to trigger interrupts. - * From the Lithium spec, section 5.6.8, revision of 12/15/1998: - * - * Trigger Mask Value - * - * A three bit wide field that represents a power of two mask - * that is used whenever the trigger pointer is compared to its - * respective read or write pointer. A value of zero here - * implies a mask of 0xFF and a value of seven implies a mask - * 0x01. This value can be used to sub-divide the ring buffer - * into pie sections so that interrupts monitor the progress of - * hardware from section to section. - * - * My interpretation of that is, whenever the hw ptr is updated, it is - * compared with the trigger ptr, and the result is masked by the - * trigger mask. (Actually, by the complement of the trigger mask.) - * If the result is zero, an interrupt is triggered. I.e., interrupt - * if ((hwptr & ~mask) == (trptr & ~mask)). The mask is formed from - * the trigger register value as mask = (1 << (8 - tmreg)) - 1. - * - * In yet different words, setting tmreg to 0 causes an interrupt after - * every 256 DMA chunks (8192 bytes) or once per traversal of the - * ring buffer. Setting it to 7 caues an interrupt every 2 DMA chunks - * (64 bytes) or 128 times per traversal of the ring buffer. - */ - -/* Lithium register offsets and bit definitions */ - -#define LI_HOST_CONTROLLER 0x000 -# define LI_HC_RESET 0x00008000 -# define LI_HC_LINK_ENABLE 0x00004000 -# define LI_HC_LINK_FAILURE 0x00000004 -# define LI_HC_LINK_CODEC 0x00000002 -# define LI_HC_LINK_READY 0x00000001 - -#define LI_INTR_STATUS 0x010 -#define LI_INTR_MASK 0x014 -# define LI_INTR_LINK_ERR 0x00008000 -# define LI_INTR_COMM2_TRIG 0x00000008 -# define LI_INTR_COMM2_UNDERFLOW 0x00000004 -# define LI_INTR_COMM1_TRIG 0x00000002 -# define LI_INTR_COMM1_OVERFLOW 0x00000001 - -#define LI_CODEC_COMMAND 0x018 -# define LI_CC_BUSY 0x00008000 -# define LI_CC_DIR 0x00000080 -# define LI_CC_DIR_RD LI_CC_DIR -# define LI_CC_DIR_WR (!LI_CC_DIR) -# define LI_CC_ADDR_MASK 0x0000007F - -#define LI_CODEC_DATA 0x01C - -#define LI_COMM1_BASE 0x100 -#define LI_COMM1_CTL 0x104 -# define LI_CCTL_RESET 0x80000000 -# define LI_CCTL_SIZE 0x70000000 -# define LI_CCTL_DMA_ENABLE 0x08000000 -# define LI_CCTL_TMASK 0x07000000 /* trigger mask */ -# define LI_CCTL_TPTR 0x00FF0000 /* trigger pointer */ -# define LI_CCTL_RPTR 0x0000FF00 -# define LI_CCTL_WPTR 0x000000FF -#define LI_COMM1_CFG 0x108 -# define LI_CCFG_LOCK 0x00008000 -# define LI_CCFG_SLOT 0x00000070 -# define LI_CCFG_DIRECTION 0x00000008 -# define LI_CCFG_DIR_IN (!LI_CCFG_DIRECTION) -# define LI_CCFG_DIR_OUT LI_CCFG_DIRECTION -# define LI_CCFG_MODE 0x00000004 -# define LI_CCFG_MODE_MONO (!LI_CCFG_MODE) -# define LI_CCFG_MODE_STEREO LI_CCFG_MODE -# define LI_CCFG_FORMAT 0x00000003 -# define LI_CCFG_FMT_8BIT 0x00000000 -# define LI_CCFG_FMT_16BIT 0x00000001 -#define LI_COMM2_BASE 0x10C -#define LI_COMM2_CTL 0x110 - /* bit definitions are the same as LI_COMM1_CTL */ -#define LI_COMM2_CFG 0x114 - /* bit definitions are the same as LI_COMM1_CFG */ - -#define LI_UST_LOW 0x200 /* 64-bit Unadjusted System Time is */ -#define LI_UST_HIGH 0x204 /* microseconds since boot */ - -#define LI_AUDIO1_UST 0x300 /* UST-MSC pairs */ -#define LI_AUDIO1_MSC 0x304 /* MSC (Media Stream Counter) */ -#define LI_AUDIO2_UST 0x308 /* counts samples actually */ -#define LI_AUDIO2_MSC 0x30C /* processed as of time UST */ - -/* - * Lithium's DMA engine operates on chunks of 32 bytes. We call that - * a DMACHUNK. - */ - -#define DMACHUNK_SHIFT 5 -#define DMACHUNK_SIZE (1 << DMACHUNK_SHIFT) -#define BYTES_TO_CHUNKS(bytes) ((bytes) >> DMACHUNK_SHIFT) -#define CHUNKS_TO_BYTES(chunks) ((chunks) << DMACHUNK_SHIFT) - -/* - * Two convenient macros to shift bitfields into/out of position. - * - * Observe that (mask & -mask) is (1 << low_set_bit_of(mask)). - * As long as mask is constant, we trust the compiler will change the - * multipy and divide into shifts. - */ - -#define SHIFT_FIELD(val, mask) (((val) * ((mask) & -(mask))) & (mask)) -#define UNSHIFT_FIELD(val, mask) (((val) & (mask)) / ((mask) & -(mask))) - -/* - * dma_chan_desc is invariant information about a Lithium - * DMA channel. There are two instances, li_comm1 and li_comm2. - * - * Note that the CCTL register fields are write ptr and read ptr, but what - * we care about are which pointer is updated by software and which by - * hardware. - */ - -typedef struct dma_chan_desc { - int basereg; - int cfgreg; - int ctlreg; - int hwptrreg; - int swptrreg; - int ustreg; - int mscreg; - unsigned long swptrmask; - int ad1843_slot; - int direction; /* LI_CCTL_DIR_IN/OUT */ -} dma_chan_desc_t; - -static const dma_chan_desc_t li_comm1 = { - LI_COMM1_BASE, /* base register offset */ - LI_COMM1_CFG, /* config register offset */ - LI_COMM1_CTL, /* control register offset */ - LI_COMM1_CTL + 0, /* hw ptr reg offset (write ptr) */ - LI_COMM1_CTL + 1, /* sw ptr reg offset (read ptr) */ - LI_AUDIO1_UST, /* ust reg offset */ - LI_AUDIO1_MSC, /* msc reg offset */ - LI_CCTL_RPTR, /* sw ptr bitmask in ctlval */ - 2, /* ad1843 serial slot */ - LI_CCFG_DIR_IN /* direction */ -}; - -static const dma_chan_desc_t li_comm2 = { - LI_COMM2_BASE, /* base register offset */ - LI_COMM2_CFG, /* config register offset */ - LI_COMM2_CTL, /* control register offset */ - LI_COMM2_CTL + 1, /* hw ptr reg offset (read ptr) */ - LI_COMM2_CTL + 0, /* sw ptr reg offset (writr ptr) */ - LI_AUDIO2_UST, /* ust reg offset */ - LI_AUDIO2_MSC, /* msc reg offset */ - LI_CCTL_WPTR, /* sw ptr bitmask in ctlval */ - 2, /* ad1843 serial slot */ - LI_CCFG_DIR_OUT /* direction */ -}; - -/* - * dma_chan is variable information about a Lithium DMA channel. - * - * The desc field points to invariant information. - * The lith field points to a lithium_t which is passed - * to li_read* and li_write* to access the registers. - * The *val fields shadow the lithium registers' contents. - */ - -typedef struct dma_chan { - const dma_chan_desc_t *desc; - lithium_t *lith; - unsigned long baseval; - unsigned long cfgval; - unsigned long ctlval; -} dma_chan_t; - -/* - * ustmsc is a UST/MSC pair (Unadjusted System Time/Media Stream Counter). - * UST is time in microseconds since the system booted, and MSC is a - * counter that increments with every audio sample. - */ - -typedef struct ustmsc { - unsigned long long ust; - unsigned long msc; -} ustmsc_t; - -/* - * li_ad1843_wait waits until lithium says the AD1843 register - * exchange is not busy. Returns 0 on success, -EBUSY on timeout. - * - * Locking: must be called with lithium_lock held. - */ - -static int li_ad1843_wait(lithium_t *lith) -{ - unsigned long later = jiffies + 2; - while (li_readl(lith, LI_CODEC_COMMAND) & LI_CC_BUSY) - if (jiffies >= later) - return -EBUSY; - return 0; -} - -/* - * li_read_ad1843_reg returns the current contents of a 16 bit AD1843 register. - * - * Returns unsigned register value on success, -errno on failure. - */ - -static int li_read_ad1843_reg(lithium_t *lith, int reg) -{ - int val; - - ASSERT(!in_interrupt()); - spin_lock(&lith->lock); - { - val = li_ad1843_wait(lith); - if (val == 0) { - li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_RD | reg); - val = li_ad1843_wait(lith); - } - if (val == 0) - val = li_readl(lith, LI_CODEC_DATA); - } - spin_unlock(&lith->lock); - - DBGXV("li_read_ad1843_reg(lith=0x%p, reg=%d) returns 0x%04x\n", - lith, reg, val); - - return val; -} - -/* - * li_write_ad1843_reg writes the specified value to a 16 bit AD1843 register. - */ - -static void li_write_ad1843_reg(lithium_t *lith, int reg, int newval) -{ - spin_lock(&lith->lock); - { - if (li_ad1843_wait(lith) == 0) { - li_writel(lith, LI_CODEC_DATA, newval); - li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_WR | reg); - } - } - spin_unlock(&lith->lock); -} - -/* - * li_setup_dma calculates all the register settings for DMA in a particular - * mode. It takes too many arguments. - */ - -static void li_setup_dma(dma_chan_t *chan, - const dma_chan_desc_t *desc, - lithium_t *lith, - unsigned long buffer_paddr, - int bufshift, - int fragshift, - int channels, - int sampsize) -{ - unsigned long mode, format; - unsigned long size, tmask; - - DBGEV("(chan=0x%p, desc=0x%p, lith=0x%p, buffer_paddr=0x%lx, " - "bufshift=%d, fragshift=%d, channels=%d, sampsize=%d)\n", - chan, desc, lith, buffer_paddr, - bufshift, fragshift, channels, sampsize); - - /* Reset the channel first. */ - - li_writel(lith, desc->ctlreg, LI_CCTL_RESET); - - ASSERT(channels == 1 || channels == 2); - if (channels == 2) - mode = LI_CCFG_MODE_STEREO; - else - mode = LI_CCFG_MODE_MONO; - ASSERT(sampsize == 1 || sampsize == 2); - if (sampsize == 2) - format = LI_CCFG_FMT_16BIT; - else - format = LI_CCFG_FMT_8BIT; - chan->desc = desc; - chan->lith = lith; - - /* - * Lithium DMA address register takes a 40-bit physical - * address, right-shifted by 8 so it fits in 32 bits. Bit 37 - * must be set -- it enables cache coherence. - */ - - ASSERT(!(buffer_paddr & 0xFF)); - chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8); - - chan->cfgval = (!LI_CCFG_LOCK | - SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) | - desc->direction | - mode | - format); - - size = bufshift - 6; - tmask = 13 - fragshift; /* See Lithium DMA Notes above. */ - ASSERT(size >= 2 && size <= 7); - ASSERT(tmask >= 1 && tmask <= 7); - chan->ctlval = (!LI_CCTL_RESET | - SHIFT_FIELD(size, LI_CCTL_SIZE) | - !LI_CCTL_DMA_ENABLE | - SHIFT_FIELD(tmask, LI_CCTL_TMASK) | - SHIFT_FIELD(0, LI_CCTL_TPTR)); - - DBGPV("basereg 0x%x = 0x%lx\n", desc->basereg, chan->baseval); - DBGPV("cfgreg 0x%x = 0x%lx\n", desc->cfgreg, chan->cfgval); - DBGPV("ctlreg 0x%x = 0x%lx\n", desc->ctlreg, chan->ctlval); - - li_writel(lith, desc->basereg, chan->baseval); - li_writel(lith, desc->cfgreg, chan->cfgval); - li_writel(lith, desc->ctlreg, chan->ctlval); - - DBGRV(); -} - -static void li_shutdown_dma(dma_chan_t *chan) -{ - lithium_t *lith = chan->lith; - caddr_t lith1 = lith->page1; - - DBGEV("(chan=0x%p)\n", chan); - - chan->ctlval &= ~LI_CCTL_DMA_ENABLE; - DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval); - li_writel(lith, chan->desc->ctlreg, chan->ctlval); - - /* - * Offset 0x500 on Lithium page 1 is an undocumented, - * unsupported register that holds the zero sample value. - * Lithium is supposed to output zero samples when DMA is - * inactive, and repeat the last sample when DMA underflows. - * But it has a bug, where, after underflow occurs, the zero - * sample is not reset. - * - * I expect this to break in a future rev of Lithium. - */ - - if (lith1 && chan->desc->direction == LI_CCFG_DIR_OUT) - * (volatile unsigned long *) (lith1 + 0x500) = 0; -} - -/* - * li_activate_dma always starts dma at the beginning of the buffer. - * - * N.B., these may be called from interrupt. - */ - -static __inline__ void li_activate_dma(dma_chan_t *chan) -{ - chan->ctlval |= LI_CCTL_DMA_ENABLE; - DBGPV("ctlval = 0x%lx\n", chan->ctlval); - li_writel(chan->lith, chan->desc->ctlreg, chan->ctlval); -} - -static void li_deactivate_dma(dma_chan_t *chan) -{ - lithium_t *lith = chan->lith; - caddr_t lith2 = lith->page2; - - chan->ctlval &= ~(LI_CCTL_DMA_ENABLE | LI_CCTL_RPTR | LI_CCTL_WPTR); - DBGPV("ctlval = 0x%lx\n", chan->ctlval); - DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval); - li_writel(lith, chan->desc->ctlreg, chan->ctlval); - - /* - * Offsets 0x98 and 0x9C on Lithium page 2 are undocumented, - * unsupported registers that are internal copies of the DMA - * read and write pointers. Because of a Lithium bug, these - * registers aren't zeroed correctly when DMA is shut off. So - * we whack them directly. - * - * I expect this to break in a future rev of Lithium. - */ - - if (lith2 && chan->desc->direction == LI_CCFG_DIR_OUT) { - * (volatile unsigned long *) (lith2 + 0x98) = 0; - * (volatile unsigned long *) (lith2 + 0x9C) = 0; - } -} - -/* - * read/write the ring buffer pointers. These routines' arguments and results - * are byte offsets from the beginning of the ring buffer. - */ - -static __inline__ int li_read_swptr(dma_chan_t *chan) -{ - const unsigned long mask = chan->desc->swptrmask; - - return CHUNKS_TO_BYTES(UNSHIFT_FIELD(chan->ctlval, mask)); -} - -static __inline__ int li_read_hwptr(dma_chan_t *chan) -{ - return CHUNKS_TO_BYTES(li_readb(chan->lith, chan->desc->hwptrreg)); -} - -static __inline__ void li_write_swptr(dma_chan_t *chan, int val) -{ - const unsigned long mask = chan->desc->swptrmask; - - ASSERT(!(val & ~CHUNKS_TO_BYTES(0xFF))); - val = BYTES_TO_CHUNKS(val); - chan->ctlval = (chan->ctlval & ~mask) | SHIFT_FIELD(val, mask); - li_writeb(chan->lith, chan->desc->swptrreg, val); -} - -/* li_read_USTMSC() returns a UST/MSC pair for the given channel. */ - -static void li_read_USTMSC(dma_chan_t *chan, ustmsc_t *ustmsc) -{ - lithium_t *lith = chan->lith; - const dma_chan_desc_t *desc = chan->desc; - unsigned long now_low, now_high0, now_high1, chan_ust; - - spin_lock(&lith->lock); - { - /* - * retry until we do all five reads without the - * high word changing. (High word increments - * every 2^32 microseconds, i.e., not often) - */ - do { - now_high0 = li_readl(lith, LI_UST_HIGH); - now_low = li_readl(lith, LI_UST_LOW); - - /* - * Lithium guarantees these two reads will be - * atomic -- ust will not increment after msc - * is read. - */ - - ustmsc->msc = li_readl(lith, desc->mscreg); - chan_ust = li_readl(lith, desc->ustreg); - - now_high1 = li_readl(lith, LI_UST_HIGH); - } while (now_high0 != now_high1); - } - spin_unlock(&lith->lock); - ustmsc->ust = ((unsigned long long) now_high0 << 32 | chan_ust); -} - -static void li_enable_interrupts(lithium_t *lith, unsigned int mask) -{ - DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask); - - /* clear any already-pending interrupts. */ - - li_writel(lith, LI_INTR_STATUS, mask); - - /* enable the interrupts. */ - - mask |= li_readl(lith, LI_INTR_MASK); - li_writel(lith, LI_INTR_MASK, mask); -} - -static void li_disable_interrupts(lithium_t *lith, unsigned int mask) -{ - unsigned int keepmask; - - DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask); - - /* disable the interrupts */ - - keepmask = li_readl(lith, LI_INTR_MASK) & ~mask; - li_writel(lith, LI_INTR_MASK, keepmask); - - /* clear any pending interrupts. */ - - li_writel(lith, LI_INTR_STATUS, mask); -} - -/* Get the interrupt status and clear all pending interrupts. */ - -static unsigned int li_get_clear_intr_status(lithium_t *lith) -{ - unsigned int status; - - status = li_readl(lith, LI_INTR_STATUS); - li_writel(lith, LI_INTR_STATUS, ~0); - return status & li_readl(lith, LI_INTR_MASK); -} - -static int li_init(lithium_t *lith) -{ - /* 1. System power supplies stabilize. */ - - /* 2. Assert the ~RESET signal. */ - - li_writel(lith, LI_HOST_CONTROLLER, LI_HC_RESET); - udelay(1); - - /* 3. Deassert the ~RESET signal and enter a wait period to allow - the AD1843 internal clocks and the external crystal oscillator - to stabilize. */ - - li_writel(lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE); - udelay(1); - - return 0; -} - -/*****************************************************************************/ -/* AD1843 access */ - -/* - * AD1843 bitfield definitions. All are named as in the AD1843 data - * sheet, with ad1843_ prepended and individual bit numbers removed. - * - * E.g., bits LSS0 through LSS2 become ad1843_LSS. - * - * Only the bitfields we need are defined. - */ - -typedef struct ad1843_bitfield { - char reg; - char lo_bit; - char nbits; -} ad1843_bitfield_t; - -static const ad1843_bitfield_t - ad1843_PDNO = { 0, 14, 1 }, /* Converter Power-Down Flag */ - ad1843_INIT = { 0, 15, 1 }, /* Clock Initialization Flag */ - ad1843_RIG = { 2, 0, 4 }, /* Right ADC Input Gain */ - ad1843_RMGE = { 2, 4, 1 }, /* Right ADC Mic Gain Enable */ - ad1843_RSS = { 2, 5, 3 }, /* Right ADC Source Select */ - ad1843_LIG = { 2, 8, 4 }, /* Left ADC Input Gain */ - ad1843_LMGE = { 2, 12, 1 }, /* Left ADC Mic Gain Enable */ - ad1843_LSS = { 2, 13, 3 }, /* Left ADC Source Select */ - ad1843_RX1M = { 4, 0, 5 }, /* Right Aux 1 Mix Gain/Atten */ - ad1843_RX1MM = { 4, 7, 1 }, /* Right Aux 1 Mix Mute */ - ad1843_LX1M = { 4, 8, 5 }, /* Left Aux 1 Mix Gain/Atten */ - ad1843_LX1MM = { 4, 15, 1 }, /* Left Aux 1 Mix Mute */ - ad1843_RX2M = { 5, 0, 5 }, /* Right Aux 2 Mix Gain/Atten */ - ad1843_RX2MM = { 5, 7, 1 }, /* Right Aux 2 Mix Mute */ - ad1843_LX2M = { 5, 8, 5 }, /* Left Aux 2 Mix Gain/Atten */ - ad1843_LX2MM = { 5, 15, 1 }, /* Left Aux 2 Mix Mute */ - ad1843_RMCM = { 7, 0, 5 }, /* Right Mic Mix Gain/Atten */ - ad1843_RMCMM = { 7, 7, 1 }, /* Right Mic Mix Mute */ - ad1843_LMCM = { 7, 8, 5 }, /* Left Mic Mix Gain/Atten */ - ad1843_LMCMM = { 7, 15, 1 }, /* Left Mic Mix Mute */ - ad1843_HPOS = { 8, 4, 1 }, /* Headphone Output Voltage Swing */ - ad1843_HPOM = { 8, 5, 1 }, /* Headphone Output Mute */ - ad1843_RDA1G = { 9, 0, 6 }, /* Right DAC1 Analog/Digital Gain */ - ad1843_RDA1GM = { 9, 7, 1 }, /* Right DAC1 Analog Mute */ - ad1843_LDA1G = { 9, 8, 6 }, /* Left DAC1 Analog/Digital Gain */ - ad1843_LDA1GM = { 9, 15, 1 }, /* Left DAC1 Analog Mute */ - ad1843_RDA1AM = { 11, 7, 1 }, /* Right DAC1 Digital Mute */ - ad1843_LDA1AM = { 11, 15, 1 }, /* Left DAC1 Digital Mute */ - ad1843_ADLC = { 15, 0, 2 }, /* ADC Left Sample Rate Source */ - ad1843_ADRC = { 15, 2, 2 }, /* ADC Right Sample Rate Source */ - ad1843_DA1C = { 15, 8, 2 }, /* DAC1 Sample Rate Source */ - ad1843_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */ - ad1843_C2C = { 20, 0, 16 }, /* Clock 1 Sample Rate Select */ - ad1843_DAADL = { 25, 4, 2 }, /* Digital ADC Left Source Select */ - ad1843_DAADR = { 25, 6, 2 }, /* Digital ADC Right Source Select */ - ad1843_DRSFLT = { 25, 15, 1 }, /* Digital Reampler Filter Mode */ - ad1843_ADLF = { 26, 0, 2 }, /* ADC Left Channel Data Format */ - ad1843_ADRF = { 26, 2, 2 }, /* ADC Right Channel Data Format */ - ad1843_ADTLK = { 26, 4, 1 }, /* ADC Transmit Lock Mode Select */ - ad1843_SCF = { 26, 7, 1 }, /* SCLK Frequency Select */ - ad1843_DA1F = { 26, 8, 2 }, /* DAC1 Data Format Select */ - ad1843_DA1SM = { 26, 14, 1 }, /* DAC1 Stereo/Mono Mode Select */ - ad1843_ADLEN = { 27, 0, 1 }, /* ADC Left Channel Enable */ - ad1843_ADREN = { 27, 1, 1 }, /* ADC Right Channel Enable */ - ad1843_AAMEN = { 27, 4, 1 }, /* Analog to Analog Mix Enable */ - ad1843_ANAEN = { 27, 7, 1 }, /* Analog Channel Enable */ - ad1843_DA1EN = { 27, 8, 1 }, /* DAC1 Enable */ - ad1843_DA2EN = { 27, 9, 1 }, /* DAC2 Enable */ - ad1843_C1EN = { 28, 11, 1 }, /* Clock Generator 1 Enable */ - ad1843_C2EN = { 28, 12, 1 }, /* Clock Generator 2 Enable */ - ad1843_PDNI = { 28, 15, 1 }; /* Converter Power Down */ - -/* - * The various registers of the AD1843 use three different formats for - * specifying gain. The ad1843_gain structure parameterizes the - * formats. - */ - -typedef struct ad1843_gain { - - int negative; /* nonzero if gain is negative. */ - const ad1843_bitfield_t *lfield; - const ad1843_bitfield_t *rfield; - -} ad1843_gain_t; - -static const ad1843_gain_t ad1843_gain_RECLEV - = { 0, &ad1843_LIG, &ad1843_RIG }; -static const ad1843_gain_t ad1843_gain_LINE - = { 1, &ad1843_LX1M, &ad1843_RX1M }; -static const ad1843_gain_t ad1843_gain_CD - = { 1, &ad1843_LX2M, &ad1843_RX2M }; -static const ad1843_gain_t ad1843_gain_MIC - = { 1, &ad1843_LMCM, &ad1843_RMCM }; -static const ad1843_gain_t ad1843_gain_PCM - = { 1, &ad1843_LDA1G, &ad1843_RDA1G }; - -/* read the current value of an AD1843 bitfield. */ - -static int ad1843_read_bits(lithium_t *lith, const ad1843_bitfield_t *field) -{ - int w = li_read_ad1843_reg(lith, field->reg); - int val = w >> field->lo_bit & ((1 << field->nbits) - 1); - - DBGXV("ad1843_read_bits(lith=0x%p, field->{%d %d %d}) returns 0x%x\n", - lith, field->reg, field->lo_bit, field->nbits, val); - - return val; -} - -/* - * write a new value to an AD1843 bitfield and return the old value. - */ - -static int ad1843_write_bits(lithium_t *lith, - const ad1843_bitfield_t *field, - int newval) -{ - int w = li_read_ad1843_reg(lith, field->reg); - int mask = ((1 << field->nbits) - 1) << field->lo_bit; - int oldval = (w & mask) >> field->lo_bit; - int newbits = (newval << field->lo_bit) & mask; - w = (w & ~mask) | newbits; - (void) li_write_ad1843_reg(lith, field->reg, w); - - DBGXV("ad1843_write_bits(lith=0x%p, field->{%d %d %d}, val=0x%x) " - "returns 0x%x\n", - lith, field->reg, field->lo_bit, field->nbits, newval, - oldval); - - return oldval; -} - -/* - * ad1843_read_multi reads multiple bitfields from the same AD1843 - * register. It uses a single read cycle to do it. (Reading the - * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20 - * microseconds.) - * - * Called ike this. - * - * ad1843_read_multi(lith, nfields, - * &ad1843_FIELD1, &val1, - * &ad1843_FIELD2, &val2, ...); - */ - -static void ad1843_read_multi(lithium_t *lith, int argcount, ...) -{ - va_list ap; - const ad1843_bitfield_t *fp; - int w = 0, mask, *value, reg = -1; - - va_start(ap, argcount); - while (--argcount >= 0) { - fp = va_arg(ap, const ad1843_bitfield_t *); - value = va_arg(ap, int *); - if (reg == -1) { - reg = fp->reg; - w = li_read_ad1843_reg(lith, reg); - } - ASSERT(reg == fp->reg); - mask = (1 << fp->nbits) - 1; - *value = w >> fp->lo_bit & mask; - } - va_end(ap); -} - -/* - * ad1843_write_multi stores multiple bitfields into the same AD1843 - * register. It uses one read and one write cycle to do it. - * - * Called like this. - * - * ad1843_write_multi(lith, nfields, - * &ad1843_FIELD1, val1, - * &ad1843_FIELF2, val2, ...); - */ - -static void ad1843_write_multi(lithium_t *lith, int argcount, ...) -{ - va_list ap; - int reg; - const ad1843_bitfield_t *fp; - int value; - int w, m, mask, bits; - - mask = 0; - bits = 0; - reg = -1; - - va_start(ap, argcount); - while (--argcount >= 0) { - fp = va_arg(ap, const ad1843_bitfield_t *); - value = va_arg(ap, int); - if (reg == -1) - reg = fp->reg; - ASSERT(fp->reg == reg); - m = ((1 << fp->nbits) - 1) << fp->lo_bit; - mask |= m; - bits |= (value << fp->lo_bit) & m; - } - va_end(ap); - ASSERT(!(bits & ~mask)); - if (~mask & 0xFFFF) - w = li_read_ad1843_reg(lith, reg); - else - w = 0; - w = (w & ~mask) | bits; - (void) li_write_ad1843_reg(lith, reg, w); -} - -/* - * ad1843_get_gain reads the specified register and extracts the gain value - * using the supplied gain type. It returns the gain in OSS format. - */ - -static int ad1843_get_gain(lithium_t *lith, const ad1843_gain_t *gp) -{ - int lg, rg; - unsigned short mask = (1 << gp->lfield->nbits) - 1; - - ad1843_read_multi(lith, 2, gp->lfield, &lg, gp->rfield, &rg); - if (gp->negative) { - lg = mask - lg; - rg = mask - rg; - } - lg = (lg * 100 + (mask >> 1)) / mask; - rg = (rg * 100 + (mask >> 1)) / mask; - return lg << 0 | rg << 8; -} - -/* - * Set an audio channel's gain. Converts from OSS format to AD1843's - * format. - * - * Returns the new gain, which may be lower than the old gain. - */ - -static int ad1843_set_gain(lithium_t *lith, - const ad1843_gain_t *gp, - int newval) -{ - unsigned short mask = (1 << gp->lfield->nbits) - 1; - - int lg = newval >> 0 & 0xFF; - int rg = newval >> 8; - if (lg < 0 || lg > 100 || rg < 0 || rg > 100) - return -EINVAL; - lg = (lg * mask + (mask >> 1)) / 100; - rg = (rg * mask + (mask >> 1)) / 100; - if (gp->negative) { - lg = mask - lg; - rg = mask - rg; - } - ad1843_write_multi(lith, 2, gp->lfield, lg, gp->rfield, rg); - return ad1843_get_gain(lith, gp); -} - -/* Returns the current recording source, in OSS format. */ - -static int ad1843_get_recsrc(lithium_t *lith) -{ - int ls = ad1843_read_bits(lith, &ad1843_LSS); - - switch (ls) { - case 1: - return SOUND_MASK_MIC; - case 2: - return SOUND_MASK_LINE; - case 3: - return SOUND_MASK_CD; - case 6: - return SOUND_MASK_PCM; - default: - ASSERT(0); - return -1; - } -} - -/* - * Enable/disable digital resample mode in the AD1843. - * - * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down - * while switching modes. So we save DA1's state (DA2's state is not - * interesting), power them down, switch into/out of resample mode, - * power them up, and restore state. - * - * This will cause audible glitches if D/A or A/D is going on, so the - * driver disallows that (in mixer_write_ioctl()). - * - * The open question is, is this worth doing? I'm leaving it in, - * because it's written, but... - */ - -static void ad1843_set_resample_mode(lithium_t *lith, int onoff) -{ - /* Save DA1 mute and gain (addr 9 is DA1 analog gain/attenuation) */ - int save_da1 = li_read_ad1843_reg(lith, 9); - - /* Power down A/D and D/A. */ - ad1843_write_multi(lith, 4, - &ad1843_DA1EN, 0, - &ad1843_DA2EN, 0, - &ad1843_ADLEN, 0, - &ad1843_ADREN, 0); - - /* Switch mode */ - ASSERT(onoff == 0 || onoff == 1); - ad1843_write_bits(lith, &ad1843_DRSFLT, onoff); - - /* Power up A/D and D/A. */ - ad1843_write_multi(lith, 3, - &ad1843_DA1EN, 1, - &ad1843_ADLEN, 1, - &ad1843_ADREN, 1); - - /* Restore DA1 mute and gain. */ - li_write_ad1843_reg(lith, 9, save_da1); -} - -/* - * Set recording source. Arg newsrc specifies an OSS channel mask. - * - * The complication is that when we switch into/out of loopback mode - * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of - * digital resampling mode. - * - * Returns newsrc on success, -errno on failure. - */ - -static int ad1843_set_recsrc(lithium_t *lith, int newsrc) -{ - int bits; - int oldbits; - - switch (newsrc) { - case SOUND_MASK_PCM: - bits = 6; - break; - - case SOUND_MASK_MIC: - bits = 1; - break; - - case SOUND_MASK_LINE: - bits = 2; - break; - - case SOUND_MASK_CD: - bits = 3; - break; - - default: - return -EINVAL; - } - oldbits = ad1843_read_bits(lith, &ad1843_LSS); - if (newsrc == SOUND_MASK_PCM && oldbits != 6) { - DBGP("enabling digital resample mode\n"); - ad1843_set_resample_mode(lith, 1); - ad1843_write_multi(lith, 2, - &ad1843_DAADL, 2, - &ad1843_DAADR, 2); - } else if (newsrc != SOUND_MASK_PCM && oldbits == 6) { - DBGP("disabling digital resample mode\n"); - ad1843_set_resample_mode(lith, 0); - ad1843_write_multi(lith, 2, - &ad1843_DAADL, 0, - &ad1843_DAADR, 0); - } - ad1843_write_multi(lith, 2, &ad1843_LSS, bits, &ad1843_RSS, bits); - return newsrc; -} - -/* - * Return current output sources, in OSS format. - */ - -static int ad1843_get_outsrc(lithium_t *lith) -{ - int pcm, line, mic, cd; - - pcm = ad1843_read_bits(lith, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM; - line = ad1843_read_bits(lith, &ad1843_LX1MM) ? 0 : SOUND_MASK_LINE; - cd = ad1843_read_bits(lith, &ad1843_LX2MM) ? 0 : SOUND_MASK_CD; - mic = ad1843_read_bits(lith, &ad1843_LMCMM) ? 0 : SOUND_MASK_MIC; - - return pcm | line | cd | mic; -} - -/* - * Set output sources. Arg is a mask of active sources in OSS format. - * - * Returns source mask on success, -errno on failure. - */ - -static int ad1843_set_outsrc(lithium_t *lith, int mask) -{ - int pcm, line, mic, cd; - - if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_CD | SOUND_MASK_MIC)) - return -EINVAL; - pcm = (mask & SOUND_MASK_PCM) ? 0 : 1; - line = (mask & SOUND_MASK_LINE) ? 0 : 1; - mic = (mask & SOUND_MASK_MIC) ? 0 : 1; - cd = (mask & SOUND_MASK_CD) ? 0 : 1; - - ad1843_write_multi(lith, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm); - ad1843_write_multi(lith, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line); - ad1843_write_multi(lith, 2, &ad1843_LX2MM, cd, &ad1843_RX2MM, cd); - ad1843_write_multi(lith, 2, &ad1843_LMCMM, mic, &ad1843_RMCMM, mic); - - return mask; -} - -/* Setup ad1843 for D/A conversion. */ - -static void ad1843_setup_dac(lithium_t *lith, - int framerate, - int fmt, - int channels) -{ - int ad_fmt = 0, ad_mode = 0; - - DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n", - lith, framerate, fmt, channels); - - switch (fmt) { - case AFMT_S8: ad_fmt = 1; break; - case AFMT_U8: ad_fmt = 1; break; - case AFMT_S16_LE: ad_fmt = 1; break; - case AFMT_MU_LAW: ad_fmt = 2; break; - case AFMT_A_LAW: ad_fmt = 3; break; - default: ASSERT(0); - } - - switch (channels) { - case 2: ad_mode = 0; break; - case 1: ad_mode = 1; break; - default: ASSERT(0); - } - - DBGPV("ad_mode = %d, ad_fmt = %d\n", ad_mode, ad_fmt); - ASSERT(framerate >= 4000 && framerate <= 49000); - ad1843_write_bits(lith, &ad1843_C1C, framerate); - ad1843_write_multi(lith, 2, - &ad1843_DA1SM, ad_mode, &ad1843_DA1F, ad_fmt); -} - -static void ad1843_shutdown_dac(lithium_t *lith) -{ - ad1843_write_bits(lith, &ad1843_DA1F, 1); -} - -static void ad1843_setup_adc(lithium_t *lith, int framerate, int fmt, int channels) -{ - int da_fmt = 0; - - DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n", - lith, framerate, fmt, channels); - - switch (fmt) { - case AFMT_S8: da_fmt = 1; break; - case AFMT_U8: da_fmt = 1; break; - case AFMT_S16_LE: da_fmt = 1; break; - case AFMT_MU_LAW: da_fmt = 2; break; - case AFMT_A_LAW: da_fmt = 3; break; - default: ASSERT(0); - } - - DBGPV("da_fmt = %d\n", da_fmt); - ASSERT(framerate >= 4000 && framerate <= 49000); - ad1843_write_bits(lith, &ad1843_C2C, framerate); - ad1843_write_multi(lith, 2, - &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt); -} - -static void ad1843_shutdown_adc(lithium_t *lith) -{ - /* nothing to do */ -} - -/* - * Fully initialize the ad1843. As described in the AD1843 data - * sheet, section "START-UP SEQUENCE". The numbered comments are - * subsection headings from the data sheet. See the data sheet, pages - * 52-54, for more info. - * - * return 0 on success, -errno on failure. */ - -static int __init ad1843_init(lithium_t *lith) -{ - unsigned long later; - int err; - - err = li_init(lith); - if (err) - return err; - - if (ad1843_read_bits(lith, &ad1843_INIT) != 0) { - printk(KERN_ERR "vwsnd sound: AD1843 won't initialize\n"); - return -EIO; - } - - ad1843_write_bits(lith, &ad1843_SCF, 1); - - /* 4. Put the conversion resources into standby. */ - - ad1843_write_bits(lith, &ad1843_PDNI, 0); - later = jiffies + HZ / 2; /* roughly half a second */ - DBGDO(shut_up++); - while (ad1843_read_bits(lith, &ad1843_PDNO)) { - if (jiffies > later) { - printk(KERN_ERR - "vwsnd audio: AD1843 won't power up\n"); - return -EIO; - } - schedule(); - } - DBGDO(shut_up--); - - /* 5. Power up the clock generators and enable clock output pins. */ - - ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1); - - /* 6. Configure conversion resources while they are in standby. */ - - /* DAC1 uses clock 1 as source, ADC uses clock 2. Always. */ - - ad1843_write_multi(lith, 3, - &ad1843_DA1C, 1, - &ad1843_ADLC, 2, - &ad1843_ADRC, 2); - - /* 7. Enable conversion resources. */ - - ad1843_write_bits(lith, &ad1843_ADTLK, 1); - ad1843_write_multi(lith, 5, - &ad1843_ANAEN, 1, - &ad1843_AAMEN, 1, - &ad1843_DA1EN, 1, - &ad1843_ADLEN, 1, - &ad1843_ADREN, 1); - - /* 8. Configure conversion resources while they are enabled. */ - - ad1843_write_bits(lith, &ad1843_DA1C, 1); - - /* Unmute all channels. */ - - ad1843_set_outsrc(lith, - (SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_MIC | SOUND_MASK_CD)); - ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0); - - /* Set default recording source to Line In and set - * mic gain to +20 dB. - */ - - ad1843_set_recsrc(lith, SOUND_MASK_LINE); - ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1); - - /* Set Speaker Out level to +/- 4V and unmute it. */ - - ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0); - - return 0; -} - -/*****************************************************************************/ -/* PCM I/O */ - -#define READ_INTR_MASK (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW) -#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW) - -typedef enum vwsnd_port_swstate { /* software state */ - SW_OFF, - SW_INITIAL, - SW_RUN, - SW_DRAIN, -} vwsnd_port_swstate_t; - -typedef enum vwsnd_port_hwstate { /* hardware state */ - HW_STOPPED, - HW_RUNNING, -} vwsnd_port_hwstate_t; - -/* - * These flags are read by ISR, but only written at baseline. - */ - -typedef enum vwsnd_port_flags { - DISABLED = 1 << 0, - ERFLOWN = 1 << 1, /* overflown or underflown */ - HW_BUSY = 1 << 2, -} vwsnd_port_flags_t; - -/* - * vwsnd_port is the per-port data structure. Each device has two - * ports, one for input and one for output. - * - * Locking: - * - * port->lock protects: hwstate, flags, swb_[iu]_avail. - * - * devc->io_sema protects: swstate, sw_*, swb_[iu]_idx. - * - * everything else is only written by open/release or - * pcm_{setup,shutdown}(), which are serialized by a - * combination of devc->open_sema and devc->io_sema. - */ - -typedef struct vwsnd_port { - - spinlock_t lock; - wait_queue_head_t queue; - vwsnd_port_swstate_t swstate; - vwsnd_port_hwstate_t hwstate; - vwsnd_port_flags_t flags; - - int sw_channels; - int sw_samplefmt; - int sw_framerate; - int sample_size; - int frame_size; - unsigned int zero_word; /* zero for the sample format */ - - int sw_fragshift; - int sw_fragcount; - int sw_subdivshift; - - unsigned int hw_fragshift; - unsigned int hw_fragsize; - unsigned int hw_fragcount; - - int hwbuf_size; - unsigned long hwbuf_paddr; - unsigned long hwbuf_vaddr; - caddr_t hwbuf; /* hwbuf == hwbuf_vaddr */ - int hwbuf_max; /* max bytes to preload */ - - caddr_t swbuf; - unsigned int swbuf_size; /* size in bytes */ - unsigned int swb_u_idx; /* index of next user byte */ - unsigned int swb_i_idx; /* index of next intr byte */ - unsigned int swb_u_avail; /* # bytes avail to user */ - unsigned int swb_i_avail; /* # bytes avail to intr */ - - dma_chan_t chan; - - /* Accounting */ - - int byte_count; - int frag_count; - int MSC_offset; - -} vwsnd_port_t; - -/* vwsnd_dev is the per-device data structure. */ - -typedef struct vwsnd_dev { - struct vwsnd_dev *next_dev; - int audio_minor; /* minor number of audio device */ - int mixer_minor; /* minor number of mixer device */ - - struct semaphore open_sema; - struct semaphore io_sema; - struct semaphore mix_sema; - mode_t open_mode; - wait_queue_head_t open_wait; - - lithium_t lith; - - vwsnd_port_t rport; - vwsnd_port_t wport; -} vwsnd_dev_t; - -static vwsnd_dev_t *vwsnd_dev_list; /* linked list of all devices */ - -static atomic_t vwsnd_use_count = ATOMIC_INIT(0); - -# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count)) -# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count)) -# define IN_USE (atomic_read(&vwsnd_use_count) != 0) - -/* - * Lithium can only DMA multiples of 32 bytes. Its DMA buffer may - * be up to 8 Kb. This driver always uses 8 Kb. - * - * Memory bug workaround -- I'm not sure what's going on here, but - * somehow pcm_copy_out() was triggering segv's going on to the next - * page of the hw buffer. So, I make the hw buffer one size bigger - * than we actually use. That way, the following page is allocated - * and mapped, and no error. I suspect that something is broken - * in Cobalt, but haven't really investigated. HBO is the actual - * size of the buffer, and HWBUF_ORDER is what we allocate. - */ - -#define HWBUF_SHIFT 13 -#define HWBUF_SIZE (1 << HWBUF_SHIFT) -# define HBO (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0) -# define HWBUF_ORDER (HBO + 1) /* next size bigger */ -#define MIN_SPEED 4000 -#define MAX_SPEED 49000 - -#define MIN_FRAGSHIFT (DMACHUNK_SHIFT + 1) -#define MAX_FRAGSHIFT (PAGE_SHIFT) -#define MIN_FRAGSIZE (1 << MIN_FRAGSHIFT) -#define MAX_FRAGSIZE (1 << MAX_FRAGSHIFT) -#define MIN_FRAGCOUNT(fragsize) 3 -#define MAX_FRAGCOUNT(fragsize) (32 * PAGE_SIZE / (fragsize)) -#define DEFAULT_FRAGSHIFT 12 -#define DEFAULT_FRAGCOUNT 16 -#define DEFAULT_SUBDIVSHIFT 0 - -/* - * The software buffer (swbuf) is a ring buffer shared between user - * level and interrupt level. Each level owns some of the bytes in - * the buffer, and may give bytes away by calling swb_inc_{u,i}(). - * User level calls _u for user, and interrupt level calls _i for - * interrupt. - * - * port->swb_{u,i}_avail is the number of bytes available to that level. - * - * port->swb_{u,i}_idx is the index of the first available byte in the - * buffer. - * - * Each level calls swb_inc_{u,i}() to atomically increment its index, - * recalculate the number of bytes available for both sides, and - * return the number of bytes available. Since each side can only - * give away bytes, the other side can only increase the number of - * bytes available to this side. Each side updates its own index - * variable, swb_{u,i}_idx, so no lock is needed to read it. - * - * To query the number of bytes available, call swb_inc_{u,i} with an - * increment of zero. - */ - -static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc) -{ - if (inc) { - port->swb_u_idx += inc; - port->swb_u_idx %= port->swbuf_size; - port->swb_u_avail -= inc; - port->swb_i_avail += inc; - } - return port->swb_u_avail; -} - -static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc) -{ - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&port->lock, flags); - { - ret = __swb_inc_u(port, inc); - } - spin_unlock_irqrestore(&port->lock, flags); - return ret; -} - -static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc) -{ - if (inc) { - port->swb_i_idx += inc; - port->swb_i_idx %= port->swbuf_size; - port->swb_i_avail -= inc; - port->swb_u_avail += inc; - } - return port->swb_i_avail; -} - -static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc) -{ - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&port->lock, flags); - { - ret = __swb_inc_i(port, inc); - } - spin_unlock_irqrestore(&port->lock, flags); - return ret; -} - -/* - * pcm_setup - this routine initializes all port state after - * mode-setting ioctls have been done, but before the first I/O is - * done. - * - * Locking: called with devc->io_sema held. - * - * Returns 0 on success, -errno on failure. - */ - -static int pcm_setup(vwsnd_dev_t *devc, - vwsnd_port_t *rport, - vwsnd_port_t *wport) -{ - vwsnd_port_t *aport = rport ? rport : wport; - int sample_size; - unsigned int zero_word; - - DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport); - - ASSERT(aport != NULL); - if (aport->swbuf != NULL) - return 0; - switch (aport->sw_samplefmt) { - case AFMT_MU_LAW: - sample_size = 1; - zero_word = 0xFFFFFFFF ^ 0x80808080; - break; - - case AFMT_A_LAW: - sample_size = 1; - zero_word = 0xD5D5D5D5 ^ 0x80808080; - break; - - case AFMT_U8: - sample_size = 1; - zero_word = 0x80808080; - break; - - case AFMT_S8: - sample_size = 1; - zero_word = 0x00000000; - break; - - case AFMT_S16_LE: - sample_size = 2; - zero_word = 0x00000000; - break; - - default: - sample_size = 0; /* prevent compiler warning */ - zero_word = 0; - ASSERT(0); - } - aport->sample_size = sample_size; - aport->zero_word = zero_word; - aport->frame_size = aport->sw_channels * aport->sample_size; - aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift; - aport->hw_fragsize = 1 << aport->hw_fragshift; - aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift; - ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE); - ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE); - ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize)); - ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize)); - if (rport) { - int hwfrags, swfrags; - rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE; - hwfrags = rport->hwbuf_max >> aport->hw_fragshift; - swfrags = aport->hw_fragcount - hwfrags; - if (swfrags < 2) - swfrags = 2; - rport->swbuf_size = swfrags * aport->hw_fragsize; - DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags); - DBGPV("read hwbuf_max = %d, swbuf_size = %d\n", - rport->hwbuf_max, rport->swbuf_size); - } - if (wport) { - int hwfrags, swfrags; - int total_bytes = aport->hw_fragcount * aport->hw_fragsize; - wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE; - if (wport->hwbuf_max > total_bytes) - wport->hwbuf_max = total_bytes; - hwfrags = wport->hwbuf_max >> aport->hw_fragshift; - DBGPV("hwfrags = %d\n", hwfrags); - swfrags = aport->hw_fragcount - hwfrags; - if (swfrags < 2) - swfrags = 2; - wport->swbuf_size = swfrags * aport->hw_fragsize; - DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags); - DBGPV("write hwbuf_max = %d, swbuf_size = %d\n", - wport->hwbuf_max, wport->swbuf_size); - } - - aport->swb_u_idx = 0; - aport->swb_i_idx = 0; - aport->byte_count = 0; - - /* - * Is this a Cobalt bug? We need to make this buffer extend - * one page further than we actually use -- somehow memcpy - * causes an exceptoin otherwise. I suspect there's a bug in - * Cobalt (or somewhere) where it's generating a fault on a - * speculative load or something. Obviously, I haven't taken - * the time to track it down. - */ - - aport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE); - if (!aport->swbuf) - return -ENOMEM; - if (rport && wport) { - ASSERT(aport == rport); - ASSERT(wport->swbuf == NULL); - /* One extra page - see comment above. */ - wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE); - if (!wport->swbuf) { - vfree(aport->swbuf); - aport->swbuf = NULL; - return -ENOMEM; - } - wport->sample_size = rport->sample_size; - wport->zero_word = rport->zero_word; - wport->frame_size = rport->frame_size; - wport->hw_fragshift = rport->hw_fragshift; - wport->hw_fragsize = rport->hw_fragsize; - wport->hw_fragcount = rport->hw_fragcount; - wport->swbuf_size = rport->swbuf_size; - wport->hwbuf_max = rport->hwbuf_max; - wport->swb_u_idx = rport->swb_u_idx; - wport->swb_i_idx = rport->swb_i_idx; - wport->byte_count = rport->byte_count; - } - if (rport) { - rport->swb_u_avail = 0; - rport->swb_i_avail = rport->swbuf_size; - rport->swstate = SW_RUN; - li_setup_dma(&rport->chan, - &li_comm1, - &devc->lith, - rport->hwbuf_paddr, - HWBUF_SHIFT, - rport->hw_fragshift, - rport->sw_channels, - rport->sample_size); - ad1843_setup_adc(&devc->lith, - rport->sw_framerate, - rport->sw_samplefmt, - rport->sw_channels); - li_enable_interrupts(&devc->lith, READ_INTR_MASK); - if (!(rport->flags & DISABLED)) { - ustmsc_t ustmsc; - rport->hwstate = HW_RUNNING; - li_activate_dma(&rport->chan); - li_read_USTMSC(&rport->chan, &ustmsc); - rport->MSC_offset = ustmsc.msc; - } - } - if (wport) { - if (wport->hwbuf_max > wport->swbuf_size) - wport->hwbuf_max = wport->swbuf_size; - wport->flags &= ~ERFLOWN; - wport->swb_u_avail = wport->swbuf_size; - wport->swb_i_avail = 0; - wport->swstate = SW_RUN; - li_setup_dma(&wport->chan, - &li_comm2, - &devc->lith, - wport->hwbuf_paddr, - HWBUF_SHIFT, - wport->hw_fragshift, - wport->sw_channels, - wport->sample_size); - ad1843_setup_dac(&devc->lith, - wport->sw_framerate, - wport->sw_samplefmt, - wport->sw_channels); - li_enable_interrupts(&devc->lith, WRITE_INTR_MASK); - } - DBGRV(); - return 0; -} - -/* - * pcm_shutdown_port - shut down one port (direction) for PCM I/O. - * Only called from pcm_shutdown. - */ - -static void pcm_shutdown_port(vwsnd_dev_t *devc, - vwsnd_port_t *aport, - unsigned int mask) -{ - unsigned long flags; - vwsnd_port_hwstate_t hwstate; - DECLARE_WAITQUEUE(wait, current); - - aport->swstate = SW_INITIAL; - add_wait_queue(&aport->queue, &wait); - while (1) { - set_current_state(TASK_UNINTERRUPTIBLE); - spin_lock_irqsave(&aport->lock, flags); - { - hwstate = aport->hwstate; - } - spin_unlock_irqrestore(&aport->lock, flags); - if (hwstate == HW_STOPPED) - break; - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&aport->queue, &wait); - li_disable_interrupts(&devc->lith, mask); - if (aport == &devc->rport) - ad1843_shutdown_adc(&devc->lith); - else /* aport == &devc->wport) */ - ad1843_shutdown_dac(&devc->lith); - li_shutdown_dma(&aport->chan); - vfree(aport->swbuf); - aport->swbuf = NULL; - aport->byte_count = 0; -} - -/* - * pcm_shutdown undoes what pcm_setup did. - * Also sets the ports' swstate to newstate. - */ - -static void pcm_shutdown(vwsnd_dev_t *devc, - vwsnd_port_t *rport, - vwsnd_port_t *wport) -{ - DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport); - - if (rport && rport->swbuf) { - DBGPV("shutting down rport\n"); - pcm_shutdown_port(devc, rport, READ_INTR_MASK); - } - if (wport && wport->swbuf) { - DBGPV("shutting down wport\n"); - pcm_shutdown_port(devc, wport, WRITE_INTR_MASK); - } - DBGRV(); -} - -static void pcm_copy_in(vwsnd_port_t *rport, int swidx, int hwidx, int nb) -{ - char *src = rport->hwbuf + hwidx; - char *dst = rport->swbuf + swidx; - int fmt = rport->sw_samplefmt; - - DBGPV("swidx = %d, hwidx = %d\n", swidx, hwidx); - ASSERT(rport->hwbuf != NULL); - ASSERT(rport->swbuf != NULL); - ASSERT(nb > 0 && (nb % 32) == 0); - ASSERT(swidx % 32 == 0 && hwidx % 32 == 0); - ASSERT(swidx >= 0 && swidx + nb <= rport->swbuf_size); - ASSERT(hwidx >= 0 && hwidx + nb <= rport->hwbuf_size); - - if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) { - - /* See Sample Format Notes above. */ - - char *end = src + nb; - while (src < end) - *dst++ = *src++ ^ 0x80; - } else - memcpy(dst, src, nb); -} - -static void pcm_copy_out(vwsnd_port_t *wport, int swidx, int hwidx, int nb) -{ - char *src = wport->swbuf + swidx; - char *dst = wport->hwbuf + hwidx; - int fmt = wport->sw_samplefmt; - - ASSERT(nb > 0 && (nb % 32) == 0); - ASSERT(wport->hwbuf != NULL); - ASSERT(wport->swbuf != NULL); - ASSERT(swidx % 32 == 0 && hwidx % 32 == 0); - ASSERT(swidx >= 0 && swidx + nb <= wport->swbuf_size); - ASSERT(hwidx >= 0 && hwidx + nb <= wport->hwbuf_size); - if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) { - - /* See Sample Format Notes above. */ - - char *end = src + nb; - while (src < end) - *dst++ = *src++ ^ 0x80; - } else - memcpy(dst, src, nb); -} - -/* - * pcm_output() is called both from baselevel and from interrupt level. - * This is where audio frames are copied into the hardware-accessible - * ring buffer. - * - * Locking note: The part of this routine that figures out what to do - * holds wport->lock. The longer part releases wport->lock, but sets - * wport->flags & HW_BUSY. Afterward, it reacquires wport->lock, and - * checks for more work to do. - * - * If another thread calls pcm_output() while HW_BUSY is set, it - * returns immediately, knowing that the thread that set HW_BUSY will - * look for more work to do before returning. - * - * This has the advantage that port->lock is held for several short - * periods instead of one long period. Also, when pcm_output is - * called from base level, it reenables interrupts. - */ - -static void pcm_output(vwsnd_dev_t *devc, int erflown, int nb) -{ - vwsnd_port_t *wport = &devc->wport; - const int hwmax = wport->hwbuf_max; - const int hwsize = wport->hwbuf_size; - const int swsize = wport->swbuf_size; - const int fragsize = wport->hw_fragsize; - unsigned long iflags; - - DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb); - spin_lock_irqsave(&wport->lock, iflags); - if (erflown) - wport->flags |= ERFLOWN; - (void) __swb_inc_u(wport, nb); - if (wport->flags & HW_BUSY) { - spin_unlock_irqrestore(&wport->lock, iflags); - DBGPV("returning: HW BUSY\n"); - return; - } - if (wport->flags & DISABLED) { - spin_unlock_irqrestore(&wport->lock, iflags); - DBGPV("returning: DISABLED\n"); - return; - } - wport->flags |= HW_BUSY; - while (1) { - int swptr, hwptr, hw_avail, sw_avail, swidx; - vwsnd_port_hwstate_t hwstate = wport->hwstate; - vwsnd_port_swstate_t swstate = wport->swstate; - int hw_unavail; - ustmsc_t ustmsc; - - hwptr = li_read_hwptr(&wport->chan); - swptr = li_read_swptr(&wport->chan); - hw_unavail = (swptr - hwptr + hwsize) % hwsize; - hw_avail = (hwmax - hw_unavail) & -fragsize; - sw_avail = wport->swb_i_avail & -fragsize; - if (sw_avail && swstate == SW_RUN) { - if (wport->flags & ERFLOWN) { - wport->flags &= ~ERFLOWN; - } - } else if (swstate == SW_INITIAL || - swstate == SW_OFF || - (swstate == SW_DRAIN && - !sw_avail && - (wport->flags & ERFLOWN))) { - DBGP("stopping. hwstate = %d\n", hwstate); - if (hwstate != HW_STOPPED) { - li_deactivate_dma(&wport->chan); - wport->hwstate = HW_STOPPED; - } - wake_up(&wport->queue); - break; - } - if (!sw_avail || !hw_avail) - break; - spin_unlock_irqrestore(&wport->lock, iflags); - - /* - * We gave up the port lock, but we have the HW_BUSY flag. - * Proceed without accessing any nonlocal state. - * Do not exit the loop -- must check for more work. - */ - - swidx = wport->swb_i_idx; - nb = hw_avail; - if (nb > sw_avail) - nb = sw_avail; - if (nb > hwsize - swptr) - nb = hwsize - swptr; /* don't overflow hwbuf */ - if (nb > swsize - swidx) - nb = swsize - swidx; /* don't overflow swbuf */ - ASSERT(nb > 0); - if (nb % fragsize) { - DBGP("nb = %d, fragsize = %d\n", nb, fragsize); - DBGP("hw_avail = %d\n", hw_avail); - DBGP("sw_avail = %d\n", sw_avail); - DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr); - DBGP("swsize = %d, swidx = %d\n", swsize, swidx); - } - ASSERT(!(nb % fragsize)); - DBGPV("copying swb[%d..%d] to hwb[%d..%d]\n", - swidx, swidx + nb, swptr, swptr + nb); - pcm_copy_out(wport, swidx, swptr, nb); - li_write_swptr(&wport->chan, (swptr + nb) % hwsize); - spin_lock_irqsave(&wport->lock, iflags); - if (hwstate == HW_STOPPED) { - DBGPV("starting\n"); - li_activate_dma(&wport->chan); - wport->hwstate = HW_RUNNING; - li_read_USTMSC(&wport->chan, &ustmsc); - ASSERT(wport->byte_count % wport->frame_size == 0); - wport->MSC_offset = ustmsc.msc - wport->byte_count / wport->frame_size; - } - __swb_inc_i(wport, nb); - wport->byte_count += nb; - wport->frag_count += nb / fragsize; - ASSERT(nb % fragsize == 0); - wake_up(&wport->queue); - } - wport->flags &= ~HW_BUSY; - spin_unlock_irqrestore(&wport->lock, iflags); - DBGRV(); -} - -/* - * pcm_input() is called both from baselevel and from interrupt level. - * This is where audio frames are copied out of the hardware-accessible - * ring buffer. - * - * Locking note: The part of this routine that figures out what to do - * holds rport->lock. The longer part releases rport->lock, but sets - * rport->flags & HW_BUSY. Afterward, it reacquires rport->lock, and - * checks for more work to do. - * - * If another thread calls pcm_input() while HW_BUSY is set, it - * returns immediately, knowing that the thread that set HW_BUSY will - * look for more work to do before returning. - * - * This has the advantage that port->lock is held for several short - * periods instead of one long period. Also, when pcm_input is - * called from base level, it reenables interrupts. - */ - -static void pcm_input(vwsnd_dev_t *devc, int erflown, int nb) -{ - vwsnd_port_t *rport = &devc->rport; - const int hwmax = rport->hwbuf_max; - const int hwsize = rport->hwbuf_size; - const int swsize = rport->swbuf_size; - const int fragsize = rport->hw_fragsize; - unsigned long iflags; - - DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb); - - spin_lock_irqsave(&rport->lock, iflags); - if (erflown) - rport->flags |= ERFLOWN; - (void) __swb_inc_u(rport, nb); - if (rport->flags & HW_BUSY || !rport->swbuf) { - spin_unlock_irqrestore(&rport->lock, iflags); - DBGPV("returning: HW BUSY or !swbuf\n"); - return; - } - if (rport->flags & DISABLED) { - spin_unlock_irqrestore(&rport->lock, iflags); - DBGPV("returning: DISABLED\n"); - return; - } - rport->flags |= HW_BUSY; - while (1) { - int swptr, hwptr, hw_avail, sw_avail, swidx; - vwsnd_port_hwstate_t hwstate = rport->hwstate; - vwsnd_port_swstate_t swstate = rport->swstate; - - hwptr = li_read_hwptr(&rport->chan); - swptr = li_read_swptr(&rport->chan); - hw_avail = (hwptr - swptr + hwsize) % hwsize & -fragsize; - if (hw_avail > hwmax) - hw_avail = hwmax; - sw_avail = rport->swb_i_avail & -fragsize; - if (swstate != SW_RUN) { - DBGP("stopping. hwstate = %d\n", hwstate); - if (hwstate != HW_STOPPED) { - li_deactivate_dma(&rport->chan); - rport->hwstate = HW_STOPPED; - } - wake_up(&rport->queue); - break; - } - if (!sw_avail || !hw_avail) - break; - spin_unlock_irqrestore(&rport->lock, iflags); - - /* - * We gave up the port lock, but we have the HW_BUSY flag. - * Proceed without accessing any nonlocal state. - * Do not exit the loop -- must check for more work. - */ - - swidx = rport->swb_i_idx; - nb = hw_avail; - if (nb > sw_avail) - nb = sw_avail; - if (nb > hwsize - swptr) - nb = hwsize - swptr; /* don't overflow hwbuf */ - if (nb > swsize - swidx) - nb = swsize - swidx; /* don't overflow swbuf */ - ASSERT(nb > 0); - if (nb % fragsize) { - DBGP("nb = %d, fragsize = %d\n", nb, fragsize); - DBGP("hw_avail = %d\n", hw_avail); - DBGP("sw_avail = %d\n", sw_avail); - DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr); - DBGP("swsize = %d, swidx = %d\n", swsize, swidx); - } - ASSERT(!(nb % fragsize)); - DBGPV("copying hwb[%d..%d] to swb[%d..%d]\n", - swptr, swptr + nb, swidx, swidx + nb); - pcm_copy_in(rport, swidx, swptr, nb); - li_write_swptr(&rport->chan, (swptr + nb) % hwsize); - spin_lock_irqsave(&rport->lock, iflags); - __swb_inc_i(rport, nb); - rport->byte_count += nb; - rport->frag_count += nb / fragsize; - ASSERT(nb % fragsize == 0); - wake_up(&rport->queue); - } - rport->flags &= ~HW_BUSY; - spin_unlock_irqrestore(&rport->lock, iflags); - DBGRV(); -} - -/* - * pcm_flush_frag() writes zero samples to fill the current fragment, - * then flushes it to the hardware. - * - * It is only meaningful to flush output, not input. - */ - -static void pcm_flush_frag(vwsnd_dev_t *devc) -{ - vwsnd_port_t *wport = &devc->wport; - - DBGPV("swstate = %d\n", wport->swstate); - if (wport->swstate == SW_RUN) { - int idx = wport->swb_u_idx; - int end = (idx + wport->hw_fragsize - 1) - >> wport->hw_fragshift - << wport->hw_fragshift; - int nb = end - idx; - DBGPV("clearing %d bytes\n", nb); - if (nb) - memset(wport->swbuf + idx, - (char) wport->zero_word, - nb); - wport->swstate = SW_DRAIN; - pcm_output(devc, 0, nb); - } - DBGRV(); -} - -/* - * Wait for output to drain. This sleeps uninterruptibly because - * there is nothing intelligent we can do if interrupted. This - * means the process will be delayed in responding to the signal. - */ - -static void pcm_write_sync(vwsnd_dev_t *devc) -{ - vwsnd_port_t *wport = &devc->wport; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - vwsnd_port_hwstate_t hwstate; - - DBGEV("(devc=0x%p)\n", devc); - add_wait_queue(&wport->queue, &wait); - while (1) { - set_current_state(TASK_UNINTERRUPTIBLE); - spin_lock_irqsave(&wport->lock, flags); - { - hwstate = wport->hwstate; - } - spin_unlock_irqrestore(&wport->lock, flags); - if (hwstate == HW_STOPPED) - break; - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&wport->queue, &wait); - DBGPV("swstate = %d, hwstate = %d\n", wport->swstate, wport->hwstate); - DBGRV(); -} - -/*****************************************************************************/ -/* audio driver */ - -/* - * seek on an audio device always fails. - */ - -static void vwsnd_audio_read_intr(vwsnd_dev_t *devc, unsigned int status) -{ - int overflown = status & LI_INTR_COMM1_OVERFLOW; - - if (status & READ_INTR_MASK) - pcm_input(devc, overflown, 0); -} - -static void vwsnd_audio_write_intr(vwsnd_dev_t *devc, unsigned int status) -{ - int underflown = status & LI_INTR_COMM2_UNDERFLOW; - - if (status & WRITE_INTR_MASK) - pcm_output(devc, underflown, 0); -} - -static void vwsnd_audio_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - vwsnd_dev_t *devc = (vwsnd_dev_t *) dev_id; - unsigned int status; - - DBGEV("(irq=%d, dev_id=0x%p, regs=0x%p)\n", irq, dev_id, regs); - - status = li_get_clear_intr_status(&devc->lith); - vwsnd_audio_read_intr(devc, status); - vwsnd_audio_write_intr(devc, status); -} - -static ssize_t vwsnd_audio_do_read(struct file *file, - char *buffer, - size_t count, - loff_t *ppos) -{ - vwsnd_dev_t *devc = file->private_data; - vwsnd_port_t *rport = ((file->f_mode & FMODE_READ) ? - &devc->rport : NULL); - int ret, nb; - - DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n", - file, buffer, count, ppos); - - if (!rport) - return -EINVAL; - - if (rport->swbuf == NULL) { - vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? - &devc->wport : NULL; - ret = pcm_setup(devc, rport, wport); - if (ret < 0) - return ret; - } - - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - while (count) { - DECLARE_WAITQUEUE(wait, current); - add_wait_queue(&rport->queue, &wait); - while ((nb = swb_inc_u(rport, 0)) == 0) { - DBGPV("blocking\n"); - set_current_state(TASK_INTERRUPTIBLE); - if (rport->flags & DISABLED || - file->f_flags & O_NONBLOCK) { - current->state = TASK_RUNNING; - remove_wait_queue(&rport->queue, &wait); - return ret ? ret : -EAGAIN; - } - schedule(); - if (signal_pending(current)) { - current->state = TASK_RUNNING; - remove_wait_queue(&rport->queue, &wait); - return ret ? ret : -ERESTARTSYS; - } - } - current->state = TASK_RUNNING; - remove_wait_queue(&rport->queue, &wait); - pcm_input(devc, 0, 0); - /* nb bytes are available in userbuf. */ - if (nb > count) - nb = count; - DBGPV("nb = %d\n", nb); - copy_to_user(buffer, rport->swbuf + rport->swb_u_idx, nb); - (void) swb_inc_u(rport, nb); - buffer += nb; - count -= nb; - ret += nb; - } - DBGPV("returning %d\n", ret); - return ret; -} - -static ssize_t vwsnd_audio_read(struct file *file, - char *buffer, - size_t count, - loff_t *ppos) -{ - vwsnd_dev_t *devc = file->private_data; - ssize_t ret; - - down(&devc->io_sema); - ret = vwsnd_audio_do_read(file, buffer, count, ppos); - up(&devc->io_sema); - return ret; -} - -static ssize_t vwsnd_audio_do_write(struct file *file, - const char *buffer, - size_t count, - loff_t *ppos) -{ - vwsnd_dev_t *devc = file->private_data; - vwsnd_port_t *wport = ((file->f_mode & FMODE_WRITE) ? - &devc->wport : NULL); - int ret, nb; - - DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n", - file, buffer, count, ppos); - - if (!wport) - return -EINVAL; - - if (wport->swbuf == NULL) { - vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? - &devc->rport : NULL; - ret = pcm_setup(devc, rport, wport); - if (ret < 0) - return ret; - } - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - while (count) { - DECLARE_WAITQUEUE(wait, current); - add_wait_queue(&wport->queue, &wait); - while ((nb = swb_inc_u(wport, 0)) == 0) { - set_current_state(TASK_INTERRUPTIBLE); - if (wport->flags & DISABLED || - file->f_flags & O_NONBLOCK) { - current->state = TASK_RUNNING; - remove_wait_queue(&wport->queue, &wait); - return ret ? ret : -EAGAIN; - } - schedule(); - if (signal_pending(current)) { - current->state = TASK_RUNNING; - remove_wait_queue(&wport->queue, &wait); - return ret ? ret : -ERESTARTSYS; - } - } - current->state = TASK_RUNNING; - remove_wait_queue(&wport->queue, &wait); - /* nb bytes are available in userbuf. */ - if (nb > count) - nb = count; - DBGPV("nb = %d\n", nb); - copy_from_user(wport->swbuf + wport->swb_u_idx, buffer, nb); - pcm_output(devc, 0, nb); - buffer += nb; - count -= nb; - ret += nb; - } - DBGPV("returning %d\n", ret); - return ret; -} - -static ssize_t vwsnd_audio_write(struct file *file, - const char *buffer, - size_t count, - loff_t *ppos) -{ - vwsnd_dev_t *devc = file->private_data; - ssize_t ret; - - down(&devc->io_sema); - ret = vwsnd_audio_do_write(file, buffer, count, ppos); - up(&devc->io_sema); - return ret; -} - -/* No kernel lock - fine */ -static unsigned int vwsnd_audio_poll(struct file *file, - struct poll_table_struct *wait) -{ - vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; - vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? - &devc->rport : NULL; - vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? - &devc->wport : NULL; - unsigned int mask = 0; - - DBGEV("(file=0x%p, wait=0x%p)\n", file, wait); - - ASSERT(rport || wport); - if (rport) { - poll_wait(file, &rport->queue, wait); - if (swb_inc_u(rport, 0)) - mask |= (POLLIN | POLLRDNORM); - } - if (wport) { - poll_wait(file, &wport->queue, wait); - if (wport->swbuf == NULL || swb_inc_u(wport, 0)) - mask |= (POLLOUT | POLLWRNORM); - } - - DBGPV("returning 0x%x\n", mask); - return mask; -} - -static int vwsnd_audio_do_ioctl(struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) -{ - vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; - vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? - &devc->rport : NULL; - vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? - &devc->wport : NULL; - vwsnd_port_t *aport = rport ? rport : wport; - struct audio_buf_info buf_info; - struct count_info info; - unsigned long flags; - int ival; - - - DBGEV("(inode=0x%p, file=0x%p, cmd=0x%x, arg=0x%lx)\n", - inode, file, cmd, arg); - switch (cmd) { - case OSS_GETVERSION: /* _SIOR ('M', 118, int) */ - DBGX("OSS_GETVERSION\n"); - ival = SOUND_VERSION; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_GETCAPS: /* _SIOR ('P',15, int) */ - DBGX("SNDCTL_DSP_GETCAPS\n"); - ival = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_GETFMTS: /* _SIOR ('P',11, int) */ - DBGX("SNDCTL_DSP_GETFMTS\n"); - ival = (AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | - AFMT_U8 | AFMT_S8); - return put_user(ival, (int *) arg); - break; - - case SOUND_PCM_READ_RATE: /* _SIOR ('P', 2, int) */ - DBGX("SOUND_PCM_READ_RATE\n"); - ival = aport->sw_framerate; - return put_user(ival, (int *) arg); - - case SOUND_PCM_READ_CHANNELS: /* _SIOR ('P', 6, int) */ - DBGX("SOUND_PCM_READ_CHANNELS\n"); - ival = aport->sw_channels; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_SPEED: /* _SIOWR('P', 2, int) */ - if (get_user(ival, (int *) arg)) - return -EFAULT; - DBGX("SNDCTL_DSP_SPEED %d\n", ival); - if (ival) { - if (aport->swstate != SW_INITIAL) { - DBGX("SNDCTL_DSP_SPEED failed: swstate = %d\n", - aport->swstate); - return -EINVAL; - } - if (ival < MIN_SPEED) - ival = MIN_SPEED; - if (ival > MAX_SPEED) - ival = MAX_SPEED; - if (rport) - rport->sw_framerate = ival; - if (wport) - wport->sw_framerate = ival; - } else - ival = aport->sw_framerate; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_STEREO: /* _SIOWR('P', 3, int) */ - if (get_user(ival, (int *) arg)) - return -EFAULT; - DBGX("SNDCTL_DSP_STEREO %d\n", ival); - if (ival != 0 && ival != 1) - return -EINVAL; - if (aport->swstate != SW_INITIAL) - return -EINVAL; - if (rport) - rport->sw_channels = ival + 1; - if (wport) - wport->sw_channels = ival + 1; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_CHANNELS: /* _SIOWR('P', 6, int) */ - if (get_user(ival, (int *) arg)) - return -EFAULT; - DBGX("SNDCTL_DSP_CHANNELS %d\n", ival); - if (ival != 1 && ival != 2) - return -EINVAL; - if (aport->swstate != SW_INITIAL) - return -EINVAL; - if (rport) - rport->sw_channels = ival; - if (wport) - wport->sw_channels = ival; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_GETBLKSIZE: /* _SIOWR('P', 4, int) */ - ival = pcm_setup(devc, rport, wport); - if (ival < 0) { - DBGX("SNDCTL_DSP_GETBLKSIZE failed, errno %d\n", ival); - return ival; - } - ival = 1 << aport->sw_fragshift; - DBGX("SNDCTL_DSP_GETBLKSIZE returning %d\n", ival); - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_SETFRAGMENT: /* _SIOWR('P',10, int) */ - if (get_user(ival, (int *) arg)) - return -EFAULT; - DBGX("SNDCTL_DSP_SETFRAGMENT %d:%d\n", - ival >> 16, ival & 0xFFFF); - if (aport->swstate != SW_INITIAL) - return -EINVAL; - { - int sw_fragshift = ival & 0xFFFF; - int sw_subdivshift = aport->sw_subdivshift; - int hw_fragshift = sw_fragshift - sw_subdivshift; - int sw_fragcount = (ival >> 16) & 0xFFFF; - int hw_fragsize; - if (hw_fragshift < MIN_FRAGSHIFT) - hw_fragshift = MIN_FRAGSHIFT; - if (hw_fragshift > MAX_FRAGSHIFT) - hw_fragshift = MAX_FRAGSHIFT; - sw_fragshift = hw_fragshift + aport->sw_subdivshift; - hw_fragsize = 1 << hw_fragshift; - if (sw_fragcount < MIN_FRAGCOUNT(hw_fragsize)) - sw_fragcount = MIN_FRAGCOUNT(hw_fragsize); - if (sw_fragcount > MAX_FRAGCOUNT(hw_fragsize)) - sw_fragcount = MAX_FRAGCOUNT(hw_fragsize); - DBGPV("sw_fragshift = %d\n", sw_fragshift); - DBGPV("rport = 0x%p, wport = 0x%p\n", rport, wport); - if (rport) { - rport->sw_fragshift = sw_fragshift; - rport->sw_fragcount = sw_fragcount; - } - if (wport) { - wport->sw_fragshift = sw_fragshift; - wport->sw_fragcount = sw_fragcount; - } - ival = sw_fragcount << 16 | sw_fragshift; - } - DBGX("SNDCTL_DSP_SETFRAGMENT returns %d:%d\n", - ival >> 16, ival & 0xFFFF); - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_SUBDIVIDE: /* _SIOWR('P', 9, int) */ - if (get_user(ival, (int *) arg)) - return -EFAULT; - DBGX("SNDCTL_DSP_SUBDIVIDE %d\n", ival); - if (aport->swstate != SW_INITIAL) - return -EINVAL; - { - int subdivshift; - int hw_fragshift, hw_fragsize, hw_fragcount; - switch (ival) { - case 1: subdivshift = 0; break; - case 2: subdivshift = 1; break; - case 4: subdivshift = 2; break; - default: return -EINVAL; - } - hw_fragshift = aport->sw_fragshift - subdivshift; - if (hw_fragshift < MIN_FRAGSHIFT || - hw_fragshift > MAX_FRAGSHIFT) - return -EINVAL; - hw_fragsize = 1 << hw_fragshift; - hw_fragcount = aport->sw_fragcount >> subdivshift; - if (hw_fragcount < MIN_FRAGCOUNT(hw_fragsize) || - hw_fragcount > MAX_FRAGCOUNT(hw_fragsize)) - return -EINVAL; - if (rport) - rport->sw_subdivshift = subdivshift; - if (wport) - wport->sw_subdivshift = subdivshift; - } - return 0; - - case SNDCTL_DSP_SETFMT: /* _SIOWR('P',5, int) */ - if (get_user(ival, (int *) arg)) - return -EFAULT; - DBGX("SNDCTL_DSP_SETFMT %d\n", ival); - if (ival != AFMT_QUERY) { - if (aport->swstate != SW_INITIAL) { - DBGP("SETFMT failed, swstate = %d\n", - aport->swstate); - return -EINVAL; - } - switch (ival) { - case AFMT_MU_LAW: - case AFMT_A_LAW: - case AFMT_U8: - case AFMT_S8: - case AFMT_S16_LE: - if (rport) - rport->sw_samplefmt = ival; - if (wport) - wport->sw_samplefmt = ival; - break; - default: - return -EINVAL; - } - } - ival = aport->sw_samplefmt; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_GETOSPACE: /* _SIOR ('P',12, audio_buf_info) */ - DBGXV("SNDCTL_DSP_GETOSPACE\n"); - if (!wport) - return -EINVAL; - ival = pcm_setup(devc, rport, wport); - if (ival < 0) - return ival; - ival = swb_inc_u(wport, 0); - buf_info.fragments = ival >> wport->sw_fragshift; - buf_info.fragstotal = wport->sw_fragcount; - buf_info.fragsize = 1 << wport->sw_fragshift; - buf_info.bytes = ival; - DBGXV("SNDCTL_DSP_GETOSPACE returns { %d %d %d %d }\n", - buf_info.fragments, buf_info.fragstotal, - buf_info.fragsize, buf_info.bytes); - return copy_to_user((void *) arg, &buf_info, sizeof buf_info); - - case SNDCTL_DSP_GETISPACE: /* _SIOR ('P',13, audio_buf_info) */ - DBGX("SNDCTL_DSP_GETISPACE\n"); - if (!rport) - return -EINVAL; - ival = pcm_setup(devc, rport, wport); - if (ival < 0) - return ival; - ival = swb_inc_u(rport, 0); - buf_info.fragments = ival >> rport->sw_fragshift; - buf_info.fragstotal = rport->sw_fragcount; - buf_info.fragsize = 1 << rport->sw_fragshift; - buf_info.bytes = ival; - DBGX("SNDCTL_DSP_GETISPACE returns { %d %d %d %d }\n", - buf_info.fragments, buf_info.fragstotal, - buf_info.fragsize, buf_info.bytes); - return copy_to_user((void *) arg, &buf_info, sizeof buf_info); - - case SNDCTL_DSP_NONBLOCK: /* _SIO ('P',14) */ - DBGX("SNDCTL_DSP_NONBLOCK\n"); - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_RESET: /* _SIO ('P', 0) */ - DBGX("SNDCTL_DSP_RESET\n"); - /* - * Nothing special needs to be done for input. Input - * samples sit in swbuf, but it will be reinitialized - * to empty when pcm_setup() is called. - */ - if (wport && wport->swbuf) { - wport->swstate = SW_INITIAL; - pcm_output(devc, 0, 0); - pcm_write_sync(devc); - } - pcm_shutdown(devc, rport, wport); - return 0; - - case SNDCTL_DSP_SYNC: /* _SIO ('P', 1) */ - DBGX("SNDCTL_DSP_SYNC\n"); - if (wport) { - pcm_flush_frag(devc); - pcm_write_sync(devc); - } - pcm_shutdown(devc, rport, wport); - return 0; - - case SNDCTL_DSP_POST: /* _SIO ('P', 8) */ - DBGX("SNDCTL_DSP_POST\n"); - if (!wport) - return -EINVAL; - pcm_flush_frag(devc); - return 0; - - case SNDCTL_DSP_GETIPTR: /* _SIOR ('P', 17, count_info) */ - DBGX("SNDCTL_DSP_GETIPTR\n"); - if (!rport) - return -EINVAL; - spin_lock_irqsave(&rport->lock, flags); - { - ustmsc_t ustmsc; - if (rport->hwstate == HW_RUNNING) { - ASSERT(rport->swstate == SW_RUN); - li_read_USTMSC(&rport->chan, &ustmsc); - info.bytes = ustmsc.msc - rport->MSC_offset; - info.bytes *= rport->frame_size; - } else { - info.bytes = rport->byte_count; - } - info.blocks = rport->frag_count; - info.ptr = 0; /* not implemented */ - rport->frag_count = 0; - } - spin_unlock_irqrestore(&rport->lock, flags); - return copy_to_user((void *) arg, &info, sizeof info); - - case SNDCTL_DSP_GETOPTR: /* _SIOR ('P',18, count_info) */ - DBGX("SNDCTL_DSP_GETOPTR\n"); - if (!wport) - return -EINVAL; - spin_lock_irqsave(&wport->lock, flags); - { - ustmsc_t ustmsc; - if (wport->hwstate == HW_RUNNING) { - ASSERT(wport->swstate == SW_RUN); - li_read_USTMSC(&wport->chan, &ustmsc); - info.bytes = ustmsc.msc - wport->MSC_offset; - info.bytes *= wport->frame_size; - } else { - info.bytes = wport->byte_count; - } - info.blocks = wport->frag_count; - info.ptr = 0; /* not implemented */ - wport->frag_count = 0; - } - spin_unlock_irqrestore(&wport->lock, flags); - return copy_to_user((void *) arg, &info, sizeof info); - - case SNDCTL_DSP_GETODELAY: /* _SIOR ('P', 23, int) */ - DBGX("SNDCTL_DSP_GETODELAY\n"); - if (!wport) - return -EINVAL; - spin_lock_irqsave(&wport->lock, flags); - { - int fsize = wport->frame_size; - ival = wport->swb_i_avail / fsize; - if (wport->hwstate == HW_RUNNING) { - int swptr, hwptr, hwframes, hwbytes, hwsize; - int totalhwbytes; - ustmsc_t ustmsc; - - hwsize = wport->hwbuf_size; - swptr = li_read_swptr(&wport->chan); - li_read_USTMSC(&wport->chan, &ustmsc); - hwframes = ustmsc.msc - wport->MSC_offset; - totalhwbytes = hwframes * fsize; - hwptr = totalhwbytes % hwsize; - hwbytes = (swptr - hwptr + hwsize) % hwsize; - ival += hwbytes / fsize; - } - } - spin_unlock_irqrestore(&wport->lock, flags); - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_PROFILE: /* _SIOW ('P', 23, int) */ - DBGX("SNDCTL_DSP_PROFILE\n"); - - /* - * Thomas Sailer explains SNDCTL_DSP_PROFILE - * (private email, March 24, 1999): - * - * This gives the sound driver a hint on what it - * should do with partial fragments - * (i.e. fragments partially filled with write). - * This can direct the driver to zero them or - * leave them alone. But don't ask me what this - * is good for, my driver just zeroes the last - * fragment before the receiver stops, no idea - * what good for any other behaviour could - * be. Implementing it as NOP seems safe. - */ - - break; - - case SNDCTL_DSP_GETTRIGGER: /* _SIOR ('P',16, int) */ - DBGX("SNDCTL_DSP_GETTRIGGER\n"); - ival = 0; - if (rport) { - spin_lock_irqsave(&rport->lock, flags); - { - if (!(rport->flags & DISABLED)) - ival |= PCM_ENABLE_INPUT; - } - spin_unlock_irqrestore(&rport->lock, flags); - } - if (wport) { - spin_lock_irqsave(&wport->lock, flags); - { - if (!(wport->flags & DISABLED)) - ival |= PCM_ENABLE_OUTPUT; - } - spin_unlock_irqrestore(&wport->lock, flags); - } - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_SETTRIGGER: /* _SIOW ('P',16, int) */ - if (get_user(ival, (int *) arg)) - return -EFAULT; - DBGX("SNDCTL_DSP_SETTRIGGER %d\n", ival); - - /* - * If user is disabling I/O and port is not in initial - * state, fail with EINVAL. - */ - - if (((rport && !(ival & PCM_ENABLE_INPUT)) || - (wport && !(ival & PCM_ENABLE_OUTPUT))) && - aport->swstate != SW_INITIAL) - return -EINVAL; - - if (rport) { - vwsnd_port_hwstate_t hwstate; - spin_lock_irqsave(&rport->lock, flags); - { - hwstate = rport->hwstate; - if (ival & PCM_ENABLE_INPUT) - rport->flags &= ~DISABLED; - else - rport->flags |= DISABLED; - } - spin_unlock_irqrestore(&rport->lock, flags); - if (hwstate != HW_RUNNING && ival & PCM_ENABLE_INPUT) { - - if (rport->swstate == SW_INITIAL) - pcm_setup(devc, rport, wport); - else - li_activate_dma(&rport->chan); - } - } - if (wport) { - vwsnd_port_flags_t pflags; - spin_lock_irqsave(&wport->lock, flags); - { - pflags = wport->flags; - if (ival & PCM_ENABLE_OUTPUT) - wport->flags &= ~DISABLED; - else - wport->flags |= DISABLED; - } - spin_unlock_irqrestore(&wport->lock, flags); - if (pflags & DISABLED && ival & PCM_ENABLE_OUTPUT) { - if (wport->swstate == SW_RUN) - pcm_output(devc, 0, 0); - } - } - return 0; - - default: - DBGP("unknown ioctl 0x%x\n", cmd); - return -EINVAL; - } - DBGP("unimplemented ioctl 0x%x\n", cmd); - return -EINVAL; -} - -static int vwsnd_audio_ioctl(struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) -{ - vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; - int ret; - - down(&devc->io_sema); - ret = vwsnd_audio_do_ioctl(inode, file, cmd, arg); - up(&devc->io_sema); - return ret; -} - -/* No mmap. */ - -static int vwsnd_audio_mmap(struct file *file, struct vm_area_struct *vma) -{ - DBGE("(file=0x%p, vma=0x%p)\n", file, vma); - return -ENODEV; -} - -/* - * Open the audio device for read and/or write. - * - * Returns 0 on success, -errno on failure. - */ - -static int vwsnd_audio_open(struct inode *inode, struct file *file) -{ - vwsnd_dev_t *devc; - dev_t minor = MINOR(inode->i_rdev); - int sw_samplefmt; - - DBGE("(inode=0x%p, file=0x%p)\n", inode, file); - - INC_USE_COUNT; - for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) - if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F)) - break; - - if (devc == NULL) { - DEC_USE_COUNT; - return -ENODEV; - } - - down(&devc->open_sema); - while (devc->open_mode & file->f_mode) { - up(&devc->open_sema); - if (file->f_flags & O_NONBLOCK) { - DEC_USE_COUNT; - return -EBUSY; - } - interruptible_sleep_on(&devc->open_wait); - if (signal_pending(current)) { - DEC_USE_COUNT; - return -ERESTARTSYS; - } - down(&devc->open_sema); - } - devc->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - up(&devc->open_sema); - - /* get default sample format from minor number. */ - - sw_samplefmt = 0; - if ((minor & 0xF) == SND_DEV_DSP) - sw_samplefmt = AFMT_U8; - else if ((minor & 0xF) == SND_DEV_AUDIO) - sw_samplefmt = AFMT_MU_LAW; - else if ((minor & 0xF) == SND_DEV_DSP16) - sw_samplefmt = AFMT_S16_LE; - else - ASSERT(0); - - /* Initialize vwsnd_ports. */ - - down(&devc->io_sema); - { - if (file->f_mode & FMODE_READ) { - devc->rport.swstate = SW_INITIAL; - devc->rport.flags = 0; - devc->rport.sw_channels = 1; - devc->rport.sw_samplefmt = sw_samplefmt; - devc->rport.sw_framerate = 8000; - devc->rport.sw_fragshift = DEFAULT_FRAGSHIFT; - devc->rport.sw_fragcount = DEFAULT_FRAGCOUNT; - devc->rport.sw_subdivshift = DEFAULT_SUBDIVSHIFT; - devc->rport.byte_count = 0; - devc->rport.frag_count = 0; - } - if (file->f_mode & FMODE_WRITE) { - devc->wport.swstate = SW_INITIAL; - devc->wport.flags = 0; - devc->wport.sw_channels = 1; - devc->wport.sw_samplefmt = sw_samplefmt; - devc->wport.sw_framerate = 8000; - devc->wport.sw_fragshift = DEFAULT_FRAGSHIFT; - devc->wport.sw_fragcount = DEFAULT_FRAGCOUNT; - devc->wport.sw_subdivshift = DEFAULT_SUBDIVSHIFT; - devc->wport.byte_count = 0; - devc->wport.frag_count = 0; - } - } - up(&devc->io_sema); - - file->private_data = devc; - DBGRV(); - return 0; -} - -/* - * Release (close) the audio device. - */ - -static int vwsnd_audio_release(struct inode *inode, struct file *file) -{ - vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; - vwsnd_port_t *wport = NULL, *rport = NULL; - int err = 0; - - lock_kernel(); - down(&devc->io_sema); - { - DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); - - if (file->f_mode & FMODE_READ) - rport = &devc->rport; - if (file->f_mode & FMODE_WRITE) { - wport = &devc->wport; - pcm_flush_frag(devc); - pcm_write_sync(devc); - } - pcm_shutdown(devc, rport, wport); - if (rport) - rport->swstate = SW_OFF; - if (wport) - wport->swstate = SW_OFF; - } - up(&devc->io_sema); - - down(&devc->open_sema); - { - devc->open_mode &= ~file->f_mode; - } - up(&devc->open_sema); - wake_up(&devc->open_wait); - DEC_USE_COUNT; - DBGR(); - unlock_kernel(); - return err; -} - -static struct file_operations vwsnd_audio_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: vwsnd_audio_read, - write: vwsnd_audio_write, - poll: vwsnd_audio_poll, - ioctl: vwsnd_audio_ioctl, - mmap: vwsnd_audio_mmap, - open: vwsnd_audio_open, - release: vwsnd_audio_release, -}; - -/*****************************************************************************/ -/* mixer driver */ - -/* open the mixer device. */ - -static int vwsnd_mixer_open(struct inode *inode, struct file *file) -{ - vwsnd_dev_t *devc; - - DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); - - INC_USE_COUNT; - for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) - if (devc->mixer_minor == MINOR(inode->i_rdev)) - break; - - if (devc == NULL) { - DEC_USE_COUNT; - return -ENODEV; - } - file->private_data = devc; - return 0; -} - -/* release (close) the mixer device. */ - -static int vwsnd_mixer_release(struct inode *inode, struct file *file) -{ - DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); - DEC_USE_COUNT; - return 0; -} - -/* mixer_read_ioctl handles all read ioctls on the mixer device. */ - -static int mixer_read_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg) -{ - int val = -1; - - DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg); - - switch (nr) { - case SOUND_MIXER_CAPS: - val = SOUND_CAP_EXCL_INPUT; - break; - - case SOUND_MIXER_DEVMASK: - val = (SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV); - break; - - case SOUND_MIXER_STEREODEVS: - val = (SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV); - break; - - case SOUND_MIXER_OUTMASK: - val = (SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_MIC | SOUND_MASK_CD); - break; - - case SOUND_MIXER_RECMASK: - val = (SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_MIC | SOUND_MASK_CD); - break; - - case SOUND_MIXER_PCM: - val = ad1843_get_gain(&devc->lith, &ad1843_gain_PCM); - break; - - case SOUND_MIXER_LINE: - val = ad1843_get_gain(&devc->lith, &ad1843_gain_LINE); - break; - - case SOUND_MIXER_MIC: - val = ad1843_get_gain(&devc->lith, &ad1843_gain_MIC); - break; - - case SOUND_MIXER_CD: - val = ad1843_get_gain(&devc->lith, &ad1843_gain_CD); - break; - - case SOUND_MIXER_RECLEV: - val = ad1843_get_gain(&devc->lith, &ad1843_gain_RECLEV); - break; - - case SOUND_MIXER_RECSRC: - val = ad1843_get_recsrc(&devc->lith); - break; - - case SOUND_MIXER_OUTSRC: - val = ad1843_get_outsrc(&devc->lith); - break; - - default: - return -EINVAL; - } - return put_user(val, (int *) arg); -} - -/* mixer_write_ioctl handles all write ioctls on the mixer device. */ - -static int mixer_write_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg) -{ - int val; - int err; - - DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg); - - err = get_user(val, (int *) arg); - if (err) - return -EFAULT; - switch (nr) { - case SOUND_MIXER_PCM: - val = ad1843_set_gain(&devc->lith, &ad1843_gain_PCM, val); - break; - - case SOUND_MIXER_LINE: - val = ad1843_set_gain(&devc->lith, &ad1843_gain_LINE, val); - break; - - case SOUND_MIXER_MIC: - val = ad1843_set_gain(&devc->lith, &ad1843_gain_MIC, val); - break; - - case SOUND_MIXER_CD: - val = ad1843_set_gain(&devc->lith, &ad1843_gain_CD, val); - break; - - case SOUND_MIXER_RECLEV: - val = ad1843_set_gain(&devc->lith, &ad1843_gain_RECLEV, val); - break; - - case SOUND_MIXER_RECSRC: - if (devc->rport.swbuf || devc->wport.swbuf) - return -EBUSY; /* can't change recsrc while running */ - val = ad1843_set_recsrc(&devc->lith, val); - break; - - case SOUND_MIXER_OUTSRC: - val = ad1843_set_outsrc(&devc->lith, val); - break; - - default: - return -EINVAL; - } - if (val < 0) - return val; - return put_user(val, (int *) arg); -} - -/* This is the ioctl entry to the mixer driver. */ - -static int vwsnd_mixer_ioctl(struct inode *ioctl, - struct file *file, - unsigned int cmd, - unsigned long arg) -{ - vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; - const unsigned int nrmask = _IOC_NRMASK << _IOC_NRSHIFT; - const unsigned int nr = (cmd & nrmask) >> _IOC_NRSHIFT; - int retval; - - DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg); - - down(&devc->mix_sema); - { - if ((cmd & ~nrmask) == MIXER_READ(0)) - retval = mixer_read_ioctl(devc, nr, (caddr_t) arg); - else if ((cmd & ~nrmask) == MIXER_WRITE(0)) - retval = mixer_write_ioctl(devc, nr, (caddr_t) arg); - else - retval = -EINVAL; - } - up(&devc->mix_sema); - return retval; -} - -static struct file_operations vwsnd_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: vwsnd_mixer_ioctl, - open: vwsnd_mixer_open, - release: vwsnd_mixer_release, -}; - -/*****************************************************************************/ -/* probe/attach/unload */ - -/* driver probe routine. Return nonzero if hardware is found. */ - -static int __init probe_vwsnd(struct address_info *hw_config) -{ - lithium_t lith; - int w; - unsigned long later; - - DBGEV("(hw_config=0x%p)\n", hw_config); - - /* XXX verify lithium present (to prevent crash on non-vw) */ - - if (li_create(&lith, hw_config->io_base) != 0) { - printk(KERN_WARNING "probe_vwsnd: can't map lithium\n"); - return 0; - } - later = jiffies + 2; - li_writel(&lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE); - do { - w = li_readl(&lith, LI_HOST_CONTROLLER); - } while (w == LI_HC_LINK_ENABLE && jiffies < later); - - li_destroy(&lith); - - DBGPV("HC = 0x%04x\n", w); - - if ((w == LI_HC_LINK_ENABLE) || (w & LI_HC_LINK_CODEC)) { - - /* This may indicate a beta machine with no audio, - * or a future machine with different audio. - * On beta-release 320 w/ no audio, HC == 0x4000 */ - - printk(KERN_WARNING "probe_vwsnd: audio codec not found\n"); - return 0; - } - - if (w & LI_HC_LINK_FAILURE) { - printk(KERN_WARNING "probe_vwsnd: can't init audio codec\n"); - return 0; - } - - printk(KERN_INFO "probe_vwsnd: lithium audio found\n"); - - return 1; -} - -/* - * driver attach routine. Initialize driver data structures and - * initialize hardware. A new vwsnd_dev_t is allocated and put - * onto the global list, vwsnd_dev_list. - * - * Return +minor_dev on success, -errno on failure. - */ - -static int __init attach_vwsnd(struct address_info *hw_config) -{ - vwsnd_dev_t *devc = NULL; - int err = -ENOMEM; - - DBGEV("(hw_config=0x%p)\n", hw_config); - - devc = kmalloc(sizeof (vwsnd_dev_t), GFP_KERNEL); - if (devc == NULL) - goto fail0; - - err = li_create(&devc->lith, hw_config->io_base); - if (err) - goto fail1; - - init_waitqueue(&devc->open_wait); - - devc->rport.hwbuf_size = HWBUF_SIZE; - devc->rport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER); - if (!devc->rport.hwbuf_vaddr) - goto fail2; - devc->rport.hwbuf = (caddr_t) devc->rport.hwbuf_vaddr; - devc->rport.hwbuf_paddr = virt_to_phys(devc->rport.hwbuf); - - /* - * Quote from the NT driver: - * - * // WARNING!!! HACK to setup output dma!!! - * // This is required because even on output there is some data - * // trickling into the input DMA channel. This is a bug in the - * // Lithium microcode. - * // --sde - * - * We set the input side's DMA base address here. It will remain - * valid until the driver is unloaded. - */ - - li_writel(&devc->lith, LI_COMM1_BASE, - devc->rport.hwbuf_paddr >> 8 | 1 << (37 - 8)); - - devc->wport.hwbuf_size = HWBUF_SIZE; - devc->wport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER); - if (!devc->wport.hwbuf_vaddr) - goto fail3; - devc->wport.hwbuf = (caddr_t) devc->wport.hwbuf_vaddr; - devc->wport.hwbuf_paddr = virt_to_phys(devc->wport.hwbuf); - DBGP("wport hwbuf = 0x%p\n", devc->wport.hwbuf); - - DBGDO(shut_up++); - err = ad1843_init(&devc->lith); - DBGDO(shut_up--); - if (err) - goto fail4; - - /* install interrupt handler */ - - err = request_irq(hw_config->irq, vwsnd_audio_intr, 0, "vwsnd", devc); - if (err) - goto fail5; - - /* register this device's drivers. */ - - devc->audio_minor = register_sound_dsp(&vwsnd_audio_fops, -1); - if ((err = devc->audio_minor) < 0) { - DBGDO(printk(KERN_WARNING - "attach_vwsnd: register_sound_dsp error %d\n", - err)); - goto fail6; - } - devc->mixer_minor = register_sound_mixer(&vwsnd_mixer_fops, - devc->audio_minor >> 4); - if ((err = devc->mixer_minor) < 0) { - DBGDO(printk(KERN_WARNING - "attach_vwsnd: register_sound_mixer error %d\n", - err)); - goto fail7; - } - - /* Squirrel away device indices for unload routine. */ - - hw_config->slots[0] = devc->audio_minor; - - /* Initialize as much of *devc as possible */ - - devc->open_sema = MUTEX; - devc->io_sema = MUTEX; - devc->mix_sema = MUTEX; - devc->open_mode = 0; - devc->rport.lock = SPIN_LOCK_UNLOCKED; - init_waitqueue(&devc->rport.queue); - devc->rport.swstate = SW_OFF; - devc->rport.hwstate = HW_STOPPED; - devc->rport.flags = 0; - devc->rport.swbuf = NULL; - devc->wport.lock = SPIN_LOCK_UNLOCKED; - init_waitqueue(&devc->wport.queue); - devc->wport.swstate = SW_OFF; - devc->wport.hwstate = HW_STOPPED; - devc->wport.flags = 0; - devc->wport.swbuf = NULL; - - /* Success. Link us onto the local device list. */ - - devc->next_dev = vwsnd_dev_list; - vwsnd_dev_list = devc; - return devc->audio_minor; - - /* So many ways to fail. Undo what we did. */ - - fail7: - unregister_sound_dsp(devc->audio_minor); - fail6: - free_irq(hw_config->irq, devc); - fail5: - fail4: - free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER); - fail3: - free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER); - fail2: - li_destroy(&devc->lith); - fail1: - kfree(devc); - fail0: - return err; -} - -static int __exit unload_vwsnd(struct address_info *hw_config) -{ - vwsnd_dev_t *devc, **devcp; - - DBGE("()\n"); - - devcp = &vwsnd_dev_list; - while ((devc = *devcp)) { - if (devc->audio_minor == hw_config->slots[0]) { - *devcp = devc->next_dev; - break; - } - devcp = &devc->next_dev; - } - - if (!devc) - return -ENODEV; - - unregister_sound_mixer(devc->mixer_minor); - unregister_sound_dsp(devc->audio_minor); - free_irq(hw_config->irq, devc); - free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER); - free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER); - li_destroy(&devc->lith); - kfree(devc); - - return 0; -} - -/*****************************************************************************/ -/* initialization and loadable kernel module interface */ - -static struct address_info the_hw_config = { - 0xFF001000, /* lithium phys addr */ - CO_IRQ(CO_APIC_LI_AUDIO) /* irq */ -}; - -MODULE_DESCRIPTION("SGI Visual Workstation sound module"); -MODULE_AUTHOR("Bob Miller "); -MODULE_LICENSE("GPL"); - -static int __init init_vwsnd(void) -{ - int err; - - DBGXV("\n"); - DBGXV("sound::vwsnd::init_module()\n"); - - if(!probe_vwsnd(&the_hw_config)) - return -ENODEV; - err = attach_vwsnd(&the_hw_config); - if (err < 0) - return err; - return 0; -} - -static void __exit cleanup_vwsnd(void) -{ - DBGX("sound::vwsnd::cleanup_module()\n"); - - unload_vwsnd(&the_hw_config); -} - -module_init(init_vwsnd); -module_exit(cleanup_vwsnd); diff -Nru a/drivers/sound/waveartist.c b/drivers/sound/waveartist.c --- a/drivers/sound/waveartist.c Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2033 +0,0 @@ -/* - * linux/drivers/sound/waveartist.c - * - * The low level driver for the RWA010 Rockwell Wave Artist - * codec chip used in the Rebel.com NetWinder. - * - * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk) - * and Pat Beirne (patb@corel.ca) - * - * - * Copyright (C) by Rebel.com 1998-1999 - * - * RWA010 specs received under NDA from Rockwell - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz - * Added __init to waveartist_init() - */ - -/* Debugging */ -#define DEBUG_CMD 1 -#define DEBUG_OUT 2 -#define DEBUG_IN 4 -#define DEBUG_INTR 8 -#define DEBUG_MIXER 16 -#define DEBUG_TRIGGER 32 - -#define debug_flg (0) - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "sound_config.h" -#include "waveartist.h" - -#ifdef CONFIG_ARM -#include -#include -#endif - -#ifndef NO_DMA -#define NO_DMA 255 -#endif - -#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\ - SOUND_MASK_PCM |\ - SOUND_MASK_LINE |\ - SOUND_MASK_MIC |\ - SOUND_MASK_LINE1 |\ - SOUND_MASK_RECLEV |\ - SOUND_MASK_VOLUME |\ - SOUND_MASK_IMIX) - -static unsigned short levels[SOUND_MIXER_NRDEVICES] = { - 0x5555, /* Master Volume */ - 0x0000, /* Bass */ - 0x0000, /* Treble */ - 0x2323, /* Synth (FM) */ - 0x4b4b, /* PCM */ - 0x6464, /* PC Speaker */ - 0x0000, /* Ext Line */ - 0x0000, /* Mic */ - 0x0000, /* CD */ - 0x6464, /* Recording monitor */ - 0x0000, /* SB PCM (ALT PCM) */ - 0x0000, /* Recording level */ - 0x6464, /* Input gain */ - 0x6464, /* Output gain */ - 0x0000, /* Line1 (Aux1) */ - 0x0000, /* Line2 (Aux2) */ - 0x0000, /* Line3 (Aux3) */ - 0x0000, /* Digital1 */ - 0x0000, /* Digital2 */ - 0x0000, /* Digital3 */ - 0x0000, /* Phone In */ - 0x6464, /* Phone Out */ - 0x0000, /* Video */ - 0x0000, /* Radio */ - 0x0000 /* Monitor */ -}; - -typedef struct { - struct address_info hw; /* hardware */ - char *chip_name; - - int xfer_count; - int audio_mode; - int open_mode; - int audio_flags; - int record_dev; - int playback_dev; - int dev_no; - - /* Mixer parameters */ - const struct waveartist_mixer_info *mix; - - unsigned short *levels; /* cache of volume settings */ - int recmask; /* currently enabled recording device! */ - -#ifdef CONFIG_ARCH_NETWINDER - signed int slider_vol; /* hardware slider volume */ - unsigned int handset_detect :1; - unsigned int telephone_detect:1; - unsigned int no_autoselect :1;/* handset/telephone autoselects a path */ - unsigned int spkr_mute_state :1;/* set by ioctl or autoselect */ - unsigned int line_mute_state :1;/* set by ioctl or autoselect */ - unsigned int use_slider :1;/* use slider setting for o/p vol */ -#endif -} wavnc_info; - -/* - * This is the implementation specific mixer information. - */ -struct waveartist_mixer_info { - unsigned int supported_devs; /* Supported devices */ - unsigned int recording_devs; /* Recordable devies */ - unsigned int stereo_devs; /* Stereo devices */ - - unsigned int (*select_input)(wavnc_info *, unsigned int, - unsigned char *, unsigned char *); - int (*decode_mixer)(wavnc_info *, int, - unsigned char, unsigned char); - int (*get_mixer)(wavnc_info *, int); -}; - -typedef struct wavnc_port_info { - int open_mode; - int speed; - int channels; - int audio_format; -} wavnc_port_info; - -static int nr_waveartist_devs; -static wavnc_info adev_info[MAX_AUDIO_DEV]; -static spinlock_t waveartist_lock = SPIN_LOCK_UNLOCKED; - -#ifndef CONFIG_ARCH_NETWINDER -#define machine_is_netwinder() 0 -#else -static struct timer_list vnc_timer; -static void vnc_configure_mixer(wavnc_info *devc, unsigned int input_mask); -static int vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg); -static void vnc_slider_tick(unsigned long data); -#endif - -static inline void -waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set) -{ - unsigned int ctlr_port = hw->io_base + CTLR; - - clear = ~clear & inb(ctlr_port); - - outb(clear | set, ctlr_port); -} - -/* Toggle IRQ acknowledge line - */ -static inline void -waveartist_iack(wavnc_info *devc) -{ - unsigned int ctlr_port = devc->hw.io_base + CTLR; - int old_ctlr; - - old_ctlr = inb(ctlr_port) & ~IRQ_ACK; - - outb(old_ctlr | IRQ_ACK, ctlr_port); - outb(old_ctlr, ctlr_port); -} - -static inline int -waveartist_sleep(int timeout_ms) -{ - unsigned int timeout = timeout_ms * 10 * HZ / 100; - - do { - set_current_state(TASK_INTERRUPTIBLE); - timeout = schedule_timeout(timeout); - } while (timeout); - - return 0; -} - -static int -waveartist_reset(wavnc_info *devc) -{ - struct address_info *hw = &devc->hw; - unsigned int timeout, res = -1; - - waveartist_set_ctlr(hw, -1, RESET); - waveartist_sleep(2); - waveartist_set_ctlr(hw, RESET, 0); - - timeout = 500; - do { - mdelay(2); - - if (inb(hw->io_base + STATR) & CMD_RF) { - res = inw(hw->io_base + CMDR); - if (res == 0x55aa) - break; - } - } while (--timeout); - - if (timeout == 0) { - printk(KERN_WARNING "WaveArtist: reset timeout "); - if (res != (unsigned int)-1) - printk("(res=%04X)", res); - printk("\n"); - return 1; - } - return 0; -} - -/* Helper function to send and receive words - * from WaveArtist. It handles all the handshaking - * and can send or receive multiple words. - */ -static int -waveartist_cmd(wavnc_info *devc, - int nr_cmd, unsigned int *cmd, - int nr_resp, unsigned int *resp) -{ - unsigned int io_base = devc->hw.io_base; - unsigned int timed_out = 0; - unsigned int i; - - if (debug_flg & DEBUG_CMD) { - printk("waveartist_cmd: cmd="); - - for (i = 0; i < nr_cmd; i++) - printk("%04X ", cmd[i]); - - printk("\n"); - } - - if (inb(io_base + STATR) & CMD_RF) { - int old_data; - - /* flush the port - */ - - old_data = inw(io_base + CMDR); - - if (debug_flg & DEBUG_CMD) - printk("flushed %04X...", old_data); - - udelay(10); - } - - for (i = 0; !timed_out && i < nr_cmd; i++) { - int count; - - for (count = 5000; count; count--) - if (inb(io_base + STATR) & CMD_WE) - break; - - if (!count) - timed_out = 1; - else - outw(cmd[i], io_base + CMDR); - } - - for (i = 0; !timed_out && i < nr_resp; i++) { - int count; - - for (count = 5000; count; count--) - if (inb(io_base + STATR) & CMD_RF) - break; - - if (!count) - timed_out = 1; - else - resp[i] = inw(io_base + CMDR); - } - - if (debug_flg & DEBUG_CMD) { - if (!timed_out) { - printk("waveartist_cmd: resp="); - - for (i = 0; i < nr_resp; i++) - printk("%04X ", resp[i]); - - printk("\n"); - } else - printk("waveartist_cmd: timed out\n"); - } - - return timed_out ? 1 : 0; -} - -/* - * Send one command word - */ -static inline int -waveartist_cmd1(wavnc_info *devc, unsigned int cmd) -{ - return waveartist_cmd(devc, 1, &cmd, 0, NULL); -} - -/* - * Send one command, receive one word - */ -static inline unsigned int -waveartist_cmd1_r(wavnc_info *devc, unsigned int cmd) -{ - unsigned int ret; - - waveartist_cmd(devc, 1, &cmd, 1, &ret); - - return ret; -} - -/* - * Send a double command, receive one - * word (and throw it away) - */ -static inline int -waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg) -{ - unsigned int vals[2]; - - vals[0] = cmd; - vals[1] = arg; - - return waveartist_cmd(devc, 2, vals, 1, vals); -} - -/* - * Send a triple command - */ -static inline int -waveartist_cmd3(wavnc_info *devc, unsigned int cmd, - unsigned int arg1, unsigned int arg2) -{ - unsigned int vals[3]; - - vals[0] = cmd; - vals[1] = arg1; - vals[2] = arg2; - - return waveartist_cmd(devc, 3, vals, 0, NULL); -} - -static int -waveartist_getrev(wavnc_info *devc, char *rev) -{ - unsigned int temp[2]; - unsigned int cmd = WACMD_GETREV; - - waveartist_cmd(devc, 1, &cmd, 2, temp); - - rev[0] = temp[0] >> 8; - rev[1] = temp[0] & 255; - rev[2] = '\0'; - - return temp[0]; -} - -static void waveartist_halt_output(int dev); -static void waveartist_halt_input(int dev); -static void waveartist_halt(int dev); -static void waveartist_trigger(int dev, int state); - -static int -waveartist_open(int dev, int mode) -{ - wavnc_info *devc; - wavnc_port_info *portc; - unsigned long flags; - - if (dev < 0 || dev >= num_audiodevs) - return -ENXIO; - - devc = (wavnc_info *) audio_devs[dev]->devc; - portc = (wavnc_port_info *) audio_devs[dev]->portc; - - spin_lock_irqsave(&waveartist_lock, flags); - if (portc->open_mode || (devc->open_mode & mode)) { - spin_unlock_irqrestore(&waveartist_lock, flags); - return -EBUSY; - } - - devc->audio_mode = 0; - devc->open_mode |= mode; - portc->open_mode = mode; - waveartist_trigger(dev, 0); - - if (mode & OPEN_READ) - devc->record_dev = dev; - if (mode & OPEN_WRITE) - devc->playback_dev = dev; - spin_unlock_irqrestore(&waveartist_lock, flags); - - return 0; -} - -static void -waveartist_close(int dev) -{ - wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; - wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; - unsigned long flags; - - spin_lock_irqsave(&waveartist_lock, flags); - - waveartist_halt(dev); - - devc->audio_mode = 0; - devc->open_mode &= ~portc->open_mode; - portc->open_mode = 0; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag) -{ - wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; - wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; - unsigned long flags; - unsigned int count = __count; - - if (debug_flg & DEBUG_OUT) - printk("waveartist: output block, buf=0x%lx, count=0x%x...\n", - buf, count); - /* - * 16 bit data - */ - if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) - count >>= 1; - - if (portc->channels > 1) - count >>= 1; - - count -= 1; - - if (devc->audio_mode & PCM_ENABLE_OUTPUT && - audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - count == devc->xfer_count) { - devc->audio_mode |= PCM_ENABLE_OUTPUT; - return; /* - * Auto DMA mode on. No need to react - */ - } - - spin_lock_irqsave(&waveartist_lock, flags); - - /* - * set sample count - */ - waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count); - - devc->xfer_count = count; - devc->audio_mode |= PCM_ENABLE_OUTPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag) -{ - wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; - wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; - unsigned long flags; - unsigned int count = __count; - - if (debug_flg & DEBUG_IN) - printk("waveartist: start input, buf=0x%lx, count=0x%x...\n", - buf, count); - - if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ - count >>= 1; - - if (portc->channels > 1) - count >>= 1; - - count -= 1; - - if (devc->audio_mode & PCM_ENABLE_INPUT && - audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - count == devc->xfer_count) { - devc->audio_mode |= PCM_ENABLE_INPUT; - return; /* - * Auto DMA mode on. No need to react - */ - } - - spin_lock_irqsave(&waveartist_lock, flags); - - /* - * set sample count - */ - waveartist_cmd2(devc, WACMD_INPUTSIZE, count); - - devc->xfer_count = count; - devc->audio_mode |= PCM_ENABLE_INPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static int -waveartist_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - return -EINVAL; -} - -static unsigned int -waveartist_get_speed(wavnc_port_info *portc) -{ - unsigned int speed; - - /* - * program the speed, channels, bits - */ - if (portc->speed == 8000) - speed = 0x2E71; - else if (portc->speed == 11025) - speed = 0x4000; - else if (portc->speed == 22050) - speed = 0x8000; - else if (portc->speed == 44100) - speed = 0x0; - else { - /* - * non-standard - just calculate - */ - speed = portc->speed << 16; - - speed = (speed / 44100) & 65535; - } - - return speed; -} - -static unsigned int -waveartist_get_bits(wavnc_port_info *portc) -{ - unsigned int bits; - - if (portc->audio_format == AFMT_S16_LE) - bits = 1; - else if (portc->audio_format == AFMT_S8) - bits = 0; - else - bits = 2; //default AFMT_U8 - - return bits; -} - -static int -waveartist_prepare_for_input(int dev, int bsize, int bcount) -{ - unsigned long flags; - wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; - wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; - unsigned int speed, bits; - - if (devc->audio_mode) - return 0; - - speed = waveartist_get_speed(portc); - bits = waveartist_get_bits(portc); - - spin_lock_irqsave(&waveartist_lock, flags); - - if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) - printk(KERN_WARNING "waveartist: error setting the " - "record format to %d\n", portc->audio_format); - - if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels)) - printk(KERN_WARNING "waveartist: error setting record " - "to %d channels\n", portc->channels); - - /* - * write cmd SetSampleSpeedTimeConstant - */ - if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed)) - printk(KERN_WARNING "waveartist: error setting the record " - "speed to %dHz.\n", portc->speed); - - if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1)) - printk(KERN_WARNING "waveartist: error setting the record " - "data path to 0x%X\n", 1); - - if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) - printk(KERN_WARNING "waveartist: error setting the record " - "format to %d\n", portc->audio_format); - - devc->xfer_count = 0; - spin_unlock_irqrestore(&waveartist_lock, flags); - waveartist_halt_input(dev); - - if (debug_flg & DEBUG_INTR) { - printk("WA CTLR reg: 0x%02X.\n", - inb(devc->hw.io_base + CTLR)); - printk("WA STAT reg: 0x%02X.\n", - inb(devc->hw.io_base + STATR)); - printk("WA IRQS reg: 0x%02X.\n", - inb(devc->hw.io_base + IRQSTAT)); - } - - return 0; -} - -static int -waveartist_prepare_for_output(int dev, int bsize, int bcount) -{ - unsigned long flags; - wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; - wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; - unsigned int speed, bits; - - /* - * program the speed, channels, bits - */ - speed = waveartist_get_speed(portc); - bits = waveartist_get_bits(portc); - - spin_lock_irqsave(&waveartist_lock, flags); - - if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) && - waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed)) - printk(KERN_WARNING "waveartist: error setting the playback " - "speed to %dHz.\n", portc->speed); - - if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels)) - printk(KERN_WARNING "waveartist: error setting the playback " - "to %d channels\n", portc->channels); - - if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0)) - printk(KERN_WARNING "waveartist: error setting the playback " - "data path to 0x%X\n", 0); - - if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits)) - printk(KERN_WARNING "waveartist: error setting the playback " - "format to %d\n", portc->audio_format); - - devc->xfer_count = 0; - spin_unlock_irqrestore(&waveartist_lock, flags); - waveartist_halt_output(dev); - - if (debug_flg & DEBUG_INTR) { - printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR)); - printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR)); - printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT)); - } - - return 0; -} - -static void -waveartist_halt(int dev) -{ - wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; - wavnc_info *devc; - - if (portc->open_mode & OPEN_WRITE) - waveartist_halt_output(dev); - - if (portc->open_mode & OPEN_READ) - waveartist_halt_input(dev); - - devc = (wavnc_info *) audio_devs[dev]->devc; - devc->audio_mode = 0; -} - -static void -waveartist_halt_input(int dev) -{ - wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; - unsigned long flags; - - spin_lock_irqsave(&waveartist_lock, flags); - - /* - * Stop capture - */ - waveartist_cmd1(devc, WACMD_INPUTSTOP); - - devc->audio_mode &= ~PCM_ENABLE_INPUT; - - /* - * Clear interrupt by toggling - * the IRQ_ACK bit in CTRL - */ - if (inb(devc->hw.io_base + STATR) & IRQ_REQ) - waveartist_iack(devc); - -// devc->audio_mode &= ~PCM_ENABLE_INPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_halt_output(int dev) -{ - wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; - unsigned long flags; - - spin_lock_irqsave(&waveartist_lock, flags); - - waveartist_cmd1(devc, WACMD_OUTPUTSTOP); - - devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - - /* - * Clear interrupt by toggling - * the IRQ_ACK bit in CTRL - */ - if (inb(devc->hw.io_base + STATR) & IRQ_REQ) - waveartist_iack(devc); - -// devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_trigger(int dev, int state) -{ - wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; - wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; - unsigned long flags; - - if (debug_flg & DEBUG_TRIGGER) { - printk("wavnc: audio trigger "); - if (state & PCM_ENABLE_INPUT) - printk("in "); - if (state & PCM_ENABLE_OUTPUT) - printk("out"); - printk("\n"); - } - - spin_lock_irqsave(&waveartist_lock, flags); - - state &= devc->audio_mode; - - if (portc->open_mode & OPEN_READ && - state & PCM_ENABLE_INPUT) - /* - * enable ADC Data Transfer to PC - */ - waveartist_cmd1(devc, WACMD_INPUTSTART); - - if (portc->open_mode & OPEN_WRITE && - state & PCM_ENABLE_OUTPUT) - /* - * enable DAC data transfer from PC - */ - waveartist_cmd1(devc, WACMD_OUTPUTSTART); - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static int -waveartist_set_speed(int dev, int arg) -{ - wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; - - if (arg <= 0) - return portc->speed; - - if (arg < 5000) - arg = 5000; - if (arg > 44100) - arg = 44100; - - portc->speed = arg; - return portc->speed; - -} - -static short -waveartist_set_channels(int dev, short arg) -{ - wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; - - if (arg != 1 && arg != 2) - return portc->channels; - - portc->channels = arg; - return arg; -} - -static unsigned int -waveartist_set_bits(int dev, unsigned int arg) -{ - wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; - - if (arg == 0) - return portc->audio_format; - - if ((arg != AFMT_U8) && (arg != AFMT_S16_LE) && (arg != AFMT_S8)) - arg = AFMT_U8; - - portc->audio_format = arg; - - return arg; -} - -static struct audio_driver waveartist_audio_driver = { - owner: THIS_MODULE, - open: waveartist_open, - close: waveartist_close, - output_block: waveartist_output_block, - start_input: waveartist_start_input, - ioctl: waveartist_ioctl, - prepare_for_input: waveartist_prepare_for_input, - prepare_for_output: waveartist_prepare_for_output, - halt_io: waveartist_halt, - halt_input: waveartist_halt_input, - halt_output: waveartist_halt_output, - trigger: waveartist_trigger, - set_speed: waveartist_set_speed, - set_bits: waveartist_set_bits, - set_channels: waveartist_set_channels -}; - - -static void -waveartist_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - wavnc_info *devc = (wavnc_info *)dev_id; - int irqstatus, status; - - irqstatus = inb(devc->hw.io_base + IRQSTAT); - status = inb(devc->hw.io_base + STATR); - - if (debug_flg & DEBUG_INTR) - printk("waveartist_intr: stat=%02x, irqstat=%02x\n", - status, irqstatus); - - if (status & IRQ_REQ) /* Clear interrupt */ - waveartist_iack(devc); - else - printk(KERN_WARNING "waveartist: unexpected interrupt\n"); - - if (irqstatus & 0x01) { - int temp = 1; - - /* PCM buffer done - */ - if ((status & DMA0) && (devc->audio_mode & PCM_ENABLE_OUTPUT)) { - DMAbuf_outputintr(devc->playback_dev, 1); - temp = 0; - } - if ((status & DMA1) && (devc->audio_mode & PCM_ENABLE_INPUT)) { - DMAbuf_inputintr(devc->record_dev); - temp = 0; - } - if (temp) //default: - printk(KERN_WARNING "waveartist: Unknown interrupt\n"); - } - if (irqstatus & 0x2) - // We do not use SB mode natively... - printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n"); -} - -/* ------------------------------------------------------------------------- - * Mixer stuff - */ -struct mix_ent { - unsigned char reg_l; - unsigned char reg_r; - unsigned char shift; - unsigned char max; -}; - -static const struct mix_ent mix_devs[SOUND_MIXER_NRDEVICES] = { - { 2, 6, 1, 7 }, /* SOUND_MIXER_VOLUME */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_BASS */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_TREBLE */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_SYNTH */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_PCM */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_SPEAKER */ - { 0, 4, 6, 31 }, /* SOUND_MIXER_LINE */ - { 2, 6, 4, 3 }, /* SOUND_MIXER_MIC */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_CD */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_IMIX */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_ALTPCM */ -#if 0 - { 3, 7, 0, 10 }, /* SOUND_MIXER_RECLEV */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_IGAIN */ -#else - { 0, 0, 0, 0 }, /* SOUND_MIXER_RECLEV */ - { 3, 7, 0, 7 }, /* SOUND_MIXER_IGAIN */ -#endif - { 0, 0, 0, 0 }, /* SOUND_MIXER_OGAIN */ - { 0, 4, 1, 31 }, /* SOUND_MIXER_LINE1 */ - { 1, 5, 6, 31 }, /* SOUND_MIXER_LINE2 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_LINE3 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL1 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL2 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL3 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEIN */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEOUT */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_VIDEO */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_RADIO */ - { 0, 0, 0, 0 } /* SOUND_MIXER_MONITOR */ -}; - -static void -waveartist_mixer_update(wavnc_info *devc, int whichDev) -{ - unsigned int lev_left, lev_right; - - lev_left = devc->levels[whichDev] & 0xff; - lev_right = devc->levels[whichDev] >> 8; - - if (lev_left > 100) - lev_left = 100; - if (lev_right > 100) - lev_right = 100; - -#define SCALE(lev,max) ((lev) * (max) / 100) - - if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT) - whichDev = SOUND_MIXER_VOLUME; - - if (mix_devs[whichDev].reg_l || mix_devs[whichDev].reg_r) { - const struct mix_ent *mix = mix_devs + whichDev; - unsigned int mask, left, right; - - mask = mix->max << mix->shift; - lev_left = SCALE(lev_left, mix->max) << mix->shift; - lev_right = SCALE(lev_right, mix->max) << mix->shift; - - /* read left setting */ - left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | - mix->reg_l << 8); - - /* read right setting */ - right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | - mix->reg_r << 8); - - left = (left & ~mask) | (lev_left & mask); - right = (right & ~mask) | (lev_right & mask); - - /* write left,right back */ - waveartist_cmd3(devc, WACMD_SET_MIXER, left, right); - } else { - switch(whichDev) { - case SOUND_MIXER_PCM: - waveartist_cmd3(devc, WACMD_SET_LEVEL, - SCALE(lev_left, 32767), - SCALE(lev_right, 32767)); - break; - - case SOUND_MIXER_SYNTH: - waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL, - SCALE(lev_left, 32767), - SCALE(lev_right, 32767)); - break; - } - } -} - -/* - * Set the ADC MUX to the specified values. We do NOT do any - * checking of the values passed, since we assume that the - * relevant *_select_input function has done that for us. - */ -static void -waveartist_set_adc_mux(wavnc_info *devc, char left_dev, char right_dev) -{ - unsigned int reg_08, reg_09; - - reg_08 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0800); - reg_09 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0900); - - reg_08 = (reg_08 & ~0x3f) | right_dev << 3 | left_dev; - - waveartist_cmd3(devc, WACMD_SET_MIXER, reg_08, reg_09); -} - -/* - * Decode a recording mask into a mixer selection as follows: - * - * OSS Source WA Source Actual source - * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848) - * SOUND_MASK_LINE Line Line in - * SOUND_MASK_LINE1 Aux 1 Aux 1 in - * SOUND_MASK_LINE2 Aux 2 Aux 2 in - * SOUND_MASK_MIC Mic Microphone - */ -static unsigned int -waveartist_select_input(wavnc_info *devc, unsigned int recmask, - unsigned char *dev_l, unsigned char *dev_r) -{ - unsigned int recdev = ADC_MUX_NONE; - - if (recmask & SOUND_MASK_IMIX) { - recmask = SOUND_MASK_IMIX; - recdev = ADC_MUX_MIXER; - } else if (recmask & SOUND_MASK_LINE2) { - recmask = SOUND_MASK_LINE2; - recdev = ADC_MUX_AUX2; - } else if (recmask & SOUND_MASK_LINE1) { - recmask = SOUND_MASK_LINE1; - recdev = ADC_MUX_AUX1; - } else if (recmask & SOUND_MASK_LINE) { - recmask = SOUND_MASK_LINE; - recdev = ADC_MUX_LINE; - } else if (recmask & SOUND_MASK_MIC) { - recmask = SOUND_MASK_MIC; - recdev = ADC_MUX_MIC; - } - - *dev_l = *dev_r = recdev; - - return recmask; -} - -static int -waveartist_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l, - unsigned char lev_r) -{ - switch (dev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_SYNTH: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_MIC: - case SOUND_MIXER_IGAIN: - case SOUND_MIXER_LINE1: - case SOUND_MIXER_LINE2: - devc->levels[dev] = lev_l | lev_r << 8; - break; - - case SOUND_MIXER_IMIX: - break; - - default: - dev = -EINVAL; - break; - } - - return dev; -} - -static int waveartist_get_mixer(wavnc_info *devc, int dev) -{ - return devc->levels[dev]; -} - -static const struct waveartist_mixer_info waveartist_mixer = { - supported_devs: SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN, - recording_devs: SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | - SOUND_MASK_IMIX, - stereo_devs: (SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN) & ~ - (SOUND_MASK_SPEAKER | SOUND_MASK_IMIX), - select_input: waveartist_select_input, - decode_mixer: waveartist_decode_mixer, - get_mixer: waveartist_get_mixer, -}; - -static void -waveartist_set_recmask(wavnc_info *devc, unsigned int recmask) -{ - unsigned char dev_l, dev_r; - - recmask &= devc->mix->recording_devs; - - /* - * If more than one recording device selected, - * disable the device that is currently in use. - */ - if (hweight32(recmask) > 1) - recmask &= ~devc->recmask; - - /* - * Translate the recording device mask into - * the ADC multiplexer settings. - */ - devc->recmask = devc->mix->select_input(devc, recmask, - &dev_l, &dev_r); - - waveartist_set_adc_mux(devc, dev_l, dev_r); -} - -static int -waveartist_set_mixer(wavnc_info *devc, int dev, unsigned int level) -{ - unsigned int lev_left = level & 0x00ff; - unsigned int lev_right = (level & 0xff00) >> 8; - - if (lev_left > 100) - lev_left = 100; - if (lev_right > 100) - lev_right = 100; - - /* - * Mono devices have their right volume forced to their - * left volume. (from ALSA driver OSS emulation). - */ - if (!(devc->mix->stereo_devs & (1 << dev))) - lev_right = lev_left; - - dev = devc->mix->decode_mixer(devc, dev, lev_left, lev_right); - - if (dev >= 0) - waveartist_mixer_update(devc, dev); - - return dev < 0 ? dev : 0; -} - -static int -waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; - int ret = 0, val, nr; - - /* - * All SOUND_MIXER_* ioctls use type 'M' - */ - if (((cmd >> 8) & 255) != 'M') - return -ENOIOCTLCMD; - -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) { - ret = vnc_private_ioctl(dev, cmd, arg); - if (ret != -ENOIOCTLCMD) - return ret; - else - ret = 0; - } -#endif - - nr = cmd & 0xff; - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - if (get_user(val, (int *)arg)) - return -EFAULT; - - switch (nr) { - case SOUND_MIXER_RECSRC: - waveartist_set_recmask(devc, val); - break; - - default: - ret = -EINVAL; - if (nr < SOUND_MIXER_NRDEVICES && - devc->mix->supported_devs & (1 << nr)) - ret = waveartist_set_mixer(devc, nr, val); - } - } - - if (ret == 0 && _SIOC_DIR(cmd) & _SIOC_READ) { - ret = -EINVAL; - - switch (nr) { - case SOUND_MIXER_RECSRC: - ret = devc->recmask; - break; - - case SOUND_MIXER_DEVMASK: - ret = devc->mix->supported_devs; - break; - - case SOUND_MIXER_STEREODEVS: - ret = devc->mix->stereo_devs; - break; - - case SOUND_MIXER_RECMASK: - ret = devc->mix->recording_devs; - break; - - case SOUND_MIXER_CAPS: - ret = SOUND_CAP_EXCL_INPUT; - break; - - default: - if (nr < SOUND_MIXER_NRDEVICES) - ret = devc->mix->get_mixer(devc, nr); - break; - } - - if (ret >= 0) - ret = put_user(ret, (int *)arg) ? -EFAULT : 0; - } - - return ret; -} - -static struct mixer_operations waveartist_mixer_operations = -{ - owner: THIS_MODULE, - id: "WaveArtist", - name: "WaveArtist", - ioctl: waveartist_mixer_ioctl -}; - -static void -waveartist_mixer_reset(wavnc_info *devc) -{ - int i; - - if (debug_flg & DEBUG_MIXER) - printk("%s: mixer_reset\n", devc->hw.name); - - /* - * reset mixer cmd - */ - waveartist_cmd1(devc, WACMD_RST_MIXER); - - /* - * set input for ADC to come from 'quiet' - * turn on default modes - */ - waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836); - - /* - * set mixer input select to none, RX filter gains 0 dB - */ - waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00); - - /* - * set bit 0 reg 2 to 1 - unmute MonoOut - */ - waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800); - - /* set default input device = internal mic - * current recording device = none - */ - waveartist_set_recmask(devc, 0); - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - waveartist_mixer_update(devc, i); -} - -static int __init waveartist_init(wavnc_info *devc) -{ - wavnc_port_info *portc; - char rev[3], dev_name[64]; - int my_dev; - - if (waveartist_reset(devc)) - return -ENODEV; - - sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name); - - if (waveartist_getrev(devc, rev)) { - strcat(dev_name, " rev. "); - strcat(dev_name, rev); - } - strcat(dev_name, ")"); - - conf_printf2(dev_name, devc->hw.io_base, devc->hw.irq, - devc->hw.dma, devc->hw.dma2); - - portc = (wavnc_port_info *)kmalloc(sizeof(wavnc_port_info), GFP_KERNEL); - if (portc == NULL) - goto nomem; - - memset(portc, 0, sizeof(wavnc_port_info)); - - my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name, - &waveartist_audio_driver, sizeof(struct audio_driver), - devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8, - devc, devc->hw.dma, devc->hw.dma2); - - if (my_dev < 0) - goto free; - - audio_devs[my_dev]->portc = portc; - - waveartist_mixer_reset(devc); - - /* - * clear any pending interrupt - */ - waveartist_iack(devc); - - if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) { - printk(KERN_ERR "%s: IRQ %d in use\n", - devc->hw.name, devc->hw.irq); - goto uninstall; - } - - if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) { - printk(KERN_ERR "%s: Can't allocate DMA%d\n", - devc->hw.name, devc->hw.dma); - goto uninstall_irq; - } - - if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA) - if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) { - printk(KERN_ERR "%s: can't allocate DMA%d\n", - devc->hw.name, devc->hw.dma2); - goto uninstall_dma; - } - - waveartist_set_ctlr(&devc->hw, 0, DMA1_IE | DMA0_IE); - - audio_devs[my_dev]->mixer_dev = - sound_install_mixer(MIXER_DRIVER_VERSION, - dev_name, - &waveartist_mixer_operations, - sizeof(struct mixer_operations), - devc); - - return my_dev; - -uninstall_dma: - sound_free_dma(devc->hw.dma); - -uninstall_irq: - free_irq(devc->hw.irq, devc); - -uninstall: - sound_unload_audiodev(my_dev); - -free: - kfree(portc); - -nomem: - return -1; -} - -static int __init probe_waveartist(struct address_info *hw_config) -{ - wavnc_info *devc = &adev_info[nr_waveartist_devs]; - - if (nr_waveartist_devs >= MAX_AUDIO_DEV) { - printk(KERN_WARNING "waveartist: too many audio devices\n"); - return 0; - } - - if (check_region(hw_config->io_base, 15)) { - printk(KERN_WARNING "WaveArtist: I/O port conflict\n"); - return 0; - } - - if (hw_config->irq > 15 || hw_config->irq < 0) { - printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n", - hw_config->irq); - return 0; - } - - if (hw_config->dma != 3) { - printk(KERN_WARNING "WaveArtist: Bad DMA %d\n", - hw_config->dma); - return 0; - } - - hw_config->name = "WaveArtist"; - devc->hw = *hw_config; - devc->open_mode = 0; - devc->chip_name = "RWA-010"; - - return 1; -} - -static void __init -attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *mix) -{ - wavnc_info *devc = &adev_info[nr_waveartist_devs]; - - /* - * NOTE! If irq < 0, there is another driver which has allocated the - * IRQ so that this driver doesn't need to allocate/deallocate it. - * The actually used IRQ is ABS(irq). - */ - devc->hw = *hw; - devc->hw.irq = (hw->irq > 0) ? hw->irq : 0; - devc->open_mode = 0; - devc->playback_dev = 0; - devc->record_dev = 0; - devc->audio_flags = DMA_AUTOMODE; - devc->levels = levels; - - if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA) - devc->audio_flags |= DMA_DUPLEX; - - request_region(hw->io_base, 15, devc->hw.name); - - devc->mix = mix; - devc->dev_no = waveartist_init(devc); - - if (devc->dev_no < 0) - release_region(hw->io_base, 15); - else { -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) { - init_timer(&vnc_timer); - vnc_timer.function = vnc_slider_tick; - vnc_timer.expires = jiffies; - vnc_timer.data = nr_waveartist_devs; - add_timer(&vnc_timer); - - vnc_configure_mixer(devc, 0); - - devc->no_autoselect = 1; - } -#endif - nr_waveartist_devs += 1; - } -} - -static void __exit unload_waveartist(struct address_info *hw) -{ - wavnc_info *devc = NULL; - int i; - - for (i = 0; i < nr_waveartist_devs; i++) - if (hw->io_base == adev_info[i].hw.io_base) { - devc = adev_info + i; - break; - } - - if (devc != NULL) { - int mixer; - -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) - del_timer(&vnc_timer); -#endif - - release_region(devc->hw.io_base, 15); - - waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0); - - if (devc->hw.irq >= 0) - free_irq(devc->hw.irq, devc); - - sound_free_dma(devc->hw.dma); - - if (devc->hw.dma != devc->hw.dma2 && - devc->hw.dma2 != NO_DMA) - sound_free_dma(devc->hw.dma2); - - mixer = audio_devs[devc->dev_no]->mixer_dev; - - if (mixer >= 0) - sound_unload_mixerdev(mixer); - - if (devc->dev_no >= 0) - sound_unload_audiodev(devc->dev_no); - - nr_waveartist_devs -= 1; - - for (; i < nr_waveartist_devs; i++) - adev_info[i] = adev_info[i + 1]; - } else - printk(KERN_WARNING "waveartist: can't find device " - "to unload\n"); -} - -#ifdef CONFIG_ARCH_NETWINDER - -/* - * Rebel.com Netwinder specifics... - */ - -#include - -#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec - -#define MIXER_PRIVATE3_RESET 0x53570000 -#define MIXER_PRIVATE3_READ 0x53570001 -#define MIXER_PRIVATE3_WRITE 0x53570002 - -#define VNC_MUTE_INTERNAL_SPKR 0x01 //the sw mute on/off control bit -#define VNC_MUTE_LINE_OUT 0x10 -#define VNC_PHONE_DETECT 0x20 -#define VNC_HANDSET_DETECT 0x40 -#define VNC_DISABLE_AUTOSWITCH 0x80 - -extern spinlock_t gpio_lock; - -static inline void -vnc_mute_spkr(wavnc_info *devc) -{ - unsigned long flags; - - spin_lock_irqsave(&gpio_lock, flags); - cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE); - spin_unlock_irqrestore(&gpio_lock, flags); -} - -static void -vnc_mute_lout(wavnc_info *devc) -{ - unsigned int left, right; - - left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL); - right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x400); - - if (devc->line_mute_state) { - left &= ~1; - right &= ~1; - } else { - left |= 1; - right |= 1; - } - waveartist_cmd3(devc, WACMD_SET_MIXER, left, right); - -} - -static int -vnc_volume_slider(wavnc_info *devc) -{ - static signed int old_slider_volume; - unsigned long flags; - signed int volume = 255; - - *CSR_TIMER1_LOAD = 0x00ffffff; - - save_flags(flags); - cli(); - - outb(0xFF, 0x201); - *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; - - while (volume && (inb(0x201) & 0x01)) - volume--; - - *CSR_TIMER1_CNTL = 0; - - restore_flags(flags); - - volume = 0x00ffffff - *CSR_TIMER1_VALUE; - - -#ifndef REVERSE - volume = 150 - (volume >> 5); -#else - volume = (volume >> 6) - 25; -#endif - - if (volume < 0) - volume = 0; - - if (volume > 100) - volume = 100; - - /* - * slider quite often reads +-8, so debounce this random noise - */ - if (abs(volume - old_slider_volume) > 7) { - old_slider_volume = volume; - - if (debug_flg & DEBUG_MIXER) - printk(KERN_DEBUG "Slider volume: %d.\n", volume); - } - - return old_slider_volume; -} - -/* - * Decode a recording mask into a mixer selection on the NetWinder - * as follows: - * - * OSS Source WA Source Actual source - * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848) - * SOUND_MASK_LINE Line Line in - * SOUND_MASK_LINE1 Left Mic Handset - * SOUND_MASK_PHONEIN Left Aux Telephone microphone - * SOUND_MASK_MIC Right Mic Builtin microphone - */ -static unsigned int -netwinder_select_input(wavnc_info *devc, unsigned int recmask, - unsigned char *dev_l, unsigned char *dev_r) -{ - unsigned int recdev_l = ADC_MUX_NONE, recdev_r = ADC_MUX_NONE; - - if (recmask & SOUND_MASK_IMIX) { - recmask = SOUND_MASK_IMIX; - recdev_l = ADC_MUX_MIXER; - recdev_r = ADC_MUX_MIXER; - } else if (recmask & SOUND_MASK_LINE) { - recmask = SOUND_MASK_LINE; - recdev_l = ADC_MUX_LINE; - recdev_r = ADC_MUX_LINE; - } else if (recmask & SOUND_MASK_LINE1) { - recmask = SOUND_MASK_LINE1; - waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ - recdev_l = ADC_MUX_MIC; - recdev_r = ADC_MUX_NONE; - } else if (recmask & SOUND_MASK_PHONEIN) { - recmask = SOUND_MASK_PHONEIN; - waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ - recdev_l = ADC_MUX_AUX1; - recdev_r = ADC_MUX_NONE; - } else if (recmask & SOUND_MASK_MIC) { - recmask = SOUND_MASK_MIC; - waveartist_cmd1(devc, WACMD_SET_MONO | 0x100); /* right */ - recdev_l = ADC_MUX_NONE; - recdev_r = ADC_MUX_MIC; - } - - *dev_l = recdev_l; - *dev_r = recdev_r; - - return recmask; -} - -static int -netwinder_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l, - unsigned char lev_r) -{ - switch (dev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_SYNTH: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_IGAIN: - devc->levels[dev] = lev_l | lev_r << 8; - break; - - case SOUND_MIXER_MIC: /* right mic only */ - devc->levels[SOUND_MIXER_MIC] &= 0xff; - devc->levels[SOUND_MIXER_MIC] |= lev_l << 8; - break; - - case SOUND_MIXER_LINE1: /* left mic only */ - devc->levels[SOUND_MIXER_MIC] &= 0xff00; - devc->levels[SOUND_MIXER_MIC] |= lev_l; - dev = SOUND_MIXER_MIC; - break; - - case SOUND_MIXER_PHONEIN: /* left aux only */ - devc->levels[SOUND_MIXER_LINE1] = lev_l; - dev = SOUND_MIXER_LINE1; - break; - - case SOUND_MIXER_IMIX: - case SOUND_MIXER_PHONEOUT: - break; - - default: - dev = -EINVAL; - break; - } - return dev; -} - -static int netwinder_get_mixer(wavnc_info *devc, int dev) -{ - int levels; - - switch (dev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_SYNTH: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_IGAIN: - levels = devc->levels[dev]; - break; - - case SOUND_MIXER_MIC: /* builtin mic: right mic only */ - levels = devc->levels[SOUND_MIXER_MIC] >> 8; - levels |= levels << 8; - break; - - case SOUND_MIXER_LINE1: /* handset mic: left mic only */ - levels = devc->levels[SOUND_MIXER_MIC] & 0xff; - levels |= levels << 8; - break; - - case SOUND_MIXER_PHONEIN: /* phone mic: left aux1 only */ - levels = devc->levels[SOUND_MIXER_LINE1] & 0xff; - levels |= levels << 8; - break; - - default: - levels = 0; - } - - return levels; -} - -/* - * Waveartist specific mixer information. - */ -static const struct waveartist_mixer_info netwinder_mixer = { - supported_devs: SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | - SOUND_MASK_PCM | SOUND_MASK_SPEAKER | - SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_IMIX | SOUND_MASK_LINE1 | - SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT| - SOUND_MASK_IGAIN, - - recording_devs: SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_IMIX | SOUND_MASK_LINE1 | - SOUND_MASK_PHONEIN, - - stereo_devs: SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | - SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_IMIX | SOUND_MASK_IGAIN, - - select_input: netwinder_select_input, - decode_mixer: netwinder_decode_mixer, - get_mixer: netwinder_get_mixer, -}; - -static void -vnc_configure_mixer(wavnc_info *devc, unsigned int recmask) -{ - if (!devc->no_autoselect) { - if (devc->handset_detect) { - recmask = SOUND_MASK_LINE1; - devc->spkr_mute_state = devc->line_mute_state = 1; - } else if (devc->telephone_detect) { - recmask = SOUND_MASK_PHONEIN; - devc->spkr_mute_state = devc->line_mute_state = 1; - } else { - /* unless someone has asked for LINE-IN, - * we default to MIC - */ - if ((devc->recmask & SOUND_MASK_LINE) == 0) - devc->recmask = SOUND_MASK_MIC; - devc->spkr_mute_state = devc->line_mute_state = 0; - } - vnc_mute_spkr(devc); - vnc_mute_lout(devc); - - if (recmask != devc->recmask) - waveartist_set_recmask(devc, recmask); - } -} - -static int -vnc_slider(wavnc_info *devc) -{ - signed int slider_volume; - unsigned int temp, old_hs, old_td; - - /* - * read the "buttons" state. - * Bit 4 = 0 means handset present - * Bit 5 = 1 means phone offhook - */ - temp = inb(0x201); - - old_hs = devc->handset_detect; - old_td = devc->telephone_detect; - - devc->handset_detect = !(temp & 0x10); - devc->telephone_detect = !!(temp & 0x20); - - if (!devc->no_autoselect && - (old_hs != devc->handset_detect || - old_td != devc->telephone_detect)) - vnc_configure_mixer(devc, devc->recmask); - - slider_volume = vnc_volume_slider(devc); - - /* - * If we're using software controlled volume, and - * the slider moves by more than 20%, then we - * switch back to slider controlled volume. - */ - if (abs(devc->slider_vol - slider_volume) > 20) - devc->use_slider = 1; - - /* - * use only left channel - */ - temp = levels[SOUND_MIXER_VOLUME] & 0xFF; - - if (slider_volume != temp && devc->use_slider) { - devc->slider_vol = slider_volume; - - waveartist_set_mixer(devc, SOUND_MIXER_VOLUME, - slider_volume | slider_volume << 8); - - return 1; - } - - return 0; -} - -static void -vnc_slider_tick(unsigned long data) -{ - int next_timeout; - - if (vnc_slider(adev_info + data)) - next_timeout = 5; // mixer reported change - else - next_timeout = VNC_TIMER_PERIOD; - - mod_timer(&vnc_timer, jiffies + next_timeout); -} - -static int -vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; - int val; - - switch (cmd) { - case SOUND_MIXER_PRIVATE1: - { - u_int prev_spkr_mute, prev_line_mute, prev_auto_state; - int val; - - if (get_user(val, (int *)arg)) - return -EFAULT; - - /* check if parameter is logical */ - if (val & ~(VNC_MUTE_INTERNAL_SPKR | - VNC_MUTE_LINE_OUT | - VNC_DISABLE_AUTOSWITCH)) - return -EINVAL; - - prev_auto_state = devc->no_autoselect; - prev_spkr_mute = devc->spkr_mute_state; - prev_line_mute = devc->line_mute_state; - - devc->no_autoselect = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0; - devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0; - devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0; - - if (prev_spkr_mute != devc->spkr_mute_state) - vnc_mute_spkr(devc); - - if (prev_line_mute != devc->line_mute_state) - vnc_mute_lout(devc); - - if (prev_auto_state != devc->no_autoselect) - vnc_configure_mixer(devc, devc->recmask); - - return 0; - } - - case SOUND_MIXER_PRIVATE2: - if (get_user(val, (int *)arg)) - return -EFAULT; - - switch (val) { -#define VNC_SOUND_PAUSE 0x53 //to pause the DSP -#define VNC_SOUND_RESUME 0x57 //to unpause the DSP - case VNC_SOUND_PAUSE: - waveartist_cmd1(devc, 0x16); - break; - - case VNC_SOUND_RESUME: - waveartist_cmd1(devc, 0x18); - break; - - default: - return -EINVAL; - } - return 0; - - /* private ioctl to allow bulk access to waveartist */ - case SOUND_MIXER_PRIVATE3: - { - unsigned long flags; - int mixer_reg[15], i, val; - - if (get_user(val, (int *)arg)) - return -EFAULT; - if (copy_from_user(mixer_reg, (void *)val, sizeof(mixer_reg))) - return -EFAULT; - - switch (mixer_reg[14]) { - case MIXER_PRIVATE3_RESET: - waveartist_mixer_reset(devc); - break; - - case MIXER_PRIVATE3_WRITE: - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]); - - waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]); - waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]); - break; - - case MIXER_PRIVATE3_READ: - spin_lock_irqsave(&waveartist_lock, flags); - - for (i = 0x30; i < 14 << 8; i += 1 << 8) - waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8)); - - spin_unlock_irqrestore(&waveartist_lock, flags); - - if (copy_to_user((void *)val, mixer_reg, sizeof(mixer_reg))) - return -EFAULT; - break; - - default: - return -EINVAL; - } - return 0; - } - - /* read back the state from PRIVATE1 */ - case SOUND_MIXER_PRIVATE4: - val = (devc->spkr_mute_state ? VNC_MUTE_INTERNAL_SPKR : 0) | - (devc->line_mute_state ? VNC_MUTE_LINE_OUT : 0) | - (devc->handset_detect ? VNC_HANDSET_DETECT : 0) | - (devc->telephone_detect ? VNC_PHONE_DETECT : 0) | - (devc->no_autoselect ? VNC_DISABLE_AUTOSWITCH : 0); - - return put_user(val, (int *)arg) ? -EFAULT : 0; - } - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - /* - * special case for master volume: if we - * received this call - switch from hw - * volume control to a software volume - * control, till the hw volume is modified - * to signal that user wants to be back in - * hardware... - */ - if ((cmd & 0xff) == SOUND_MIXER_VOLUME) - devc->use_slider = 0; - - /* speaker output */ - if ((cmd & 0xff) == SOUND_MIXER_SPEAKER) { - unsigned int val, l, r; - - if (get_user(val, (int *)arg)) - return -EFAULT; - - l = val & 0x7f; - r = (val & 0x7f00) >> 8; - val = (l + r) / 2; - devc->levels[SOUND_MIXER_SPEAKER] = val | (val << 8); - devc->spkr_mute_state = (val <= 50); - vnc_mute_spkr(devc); - return 0; - } - } - - return -ENOIOCTLCMD; -} - -#endif - -static struct address_info cfg; - -static int attached; - -static int __initdata io = 0; -static int __initdata irq = 0; -static int __initdata dma = 0; -static int __initdata dma2 = 0; - - -static int __init init_waveartist(void) -{ - const struct waveartist_mixer_info *mix; - - if (!io && machine_is_netwinder()) { - /* - * The NetWinder WaveArtist is at a fixed address. - * If the user does not supply an address, use the - * well-known parameters. - */ - io = 0x250; - irq = 12; - dma = 3; - dma2 = 7; - } - - mix = &waveartist_mixer; -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) - mix = &netwinder_mixer; -#endif - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - - if (!probe_waveartist(&cfg)) - return -ENODEV; - - attach_waveartist(&cfg, mix); - attached = 1; - - return 0; -} - -static void __exit cleanup_waveartist(void) -{ - if (attached) - unload_waveartist(&cfg); -} - -module_init(init_waveartist); -module_exit(cleanup_waveartist); - -#ifndef MODULE -static int __init setup_waveartist(char *str) -{ - /* io, irq, dma, dma2 */ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - - return 1; -} -__setup("waveartist=", setup_waveartist); -#endif - -MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver"); -MODULE_PARM(io, "i"); /* IO base */ -MODULE_PARM(irq, "i"); /* IRQ */ -MODULE_PARM(dma, "i"); /* DMA */ -MODULE_PARM(dma2, "i"); /* DMA2 */ -MODULE_LICENSE("GPL"); diff -Nru a/drivers/sound/waveartist.h b/drivers/sound/waveartist.h --- a/drivers/sound/waveartist.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,92 +0,0 @@ -/* - * linux/drivers/sound/waveartist.h - * - * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder - */ - -//registers -#define CMDR 0 -#define DATR 2 -#define CTLR 4 -#define STATR 5 -#define IRQSTAT 12 - -//bit defs -//reg STATR -#define CMD_WE 0x80 -#define CMD_RF 0x40 -#define DAT_WE 0x20 -#define DAT_RF 0x10 - -#define IRQ_REQ 0x08 -#define DMA1 0x04 -#define DMA0 0x02 - -//bit defs -//reg CTLR -#define CMD_WEIE 0x80 -#define CMD_RFIE 0x40 -#define DAT_WEIE 0x20 -#define DAT_RFIE 0x10 - -#define RESET 0x08 -#define DMA1_IE 0x04 -#define DMA0_IE 0x02 -#define IRQ_ACK 0x01 - -//commands - -#define WACMD_SYSTEMID 0x00 -#define WACMD_GETREV 0x00 -#define WACMD_INPUTFORMAT 0x10 //0-8S, 1-16S, 2-8U -#define WACMD_INPUTCHANNELS 0x11 //1-Mono, 2-Stereo -#define WACMD_INPUTSPEED 0x12 //sampling rate -#define WACMD_INPUTDMA 0x13 //0-8bit, 1-16bit, 2-PIO -#define WACMD_INPUTSIZE 0x14 //samples to interrupt -#define WACMD_INPUTSTART 0x15 //start ADC -#define WACMD_INPUTPAUSE 0x16 //pause ADC -#define WACMD_INPUTSTOP 0x17 //stop ADC -#define WACMD_INPUTRESUME 0x18 //resume ADC -#define WACMD_INPUTPIO 0x19 //PIO ADC - -#define WACMD_OUTPUTFORMAT 0x20 //0-8S, 1-16S, 2-8U -#define WACMD_OUTPUTCHANNELS 0x21 //1-Mono, 2-Stereo -#define WACMD_OUTPUTSPEED 0x22 //sampling rate -#define WACMD_OUTPUTDMA 0x23 //0-8bit, 1-16bit, 2-PIO -#define WACMD_OUTPUTSIZE 0x24 //samples to interrupt -#define WACMD_OUTPUTSTART 0x25 //start ADC -#define WACMD_OUTPUTPAUSE 0x26 //pause ADC -#define WACMD_OUTPUTSTOP 0x27 //stop ADC -#define WACMD_OUTPUTRESUME 0x28 //resume ADC -#define WACMD_OUTPUTPIO 0x29 //PIO ADC - -#define WACMD_GET_LEVEL 0x30 -#define WACMD_SET_LEVEL 0x31 -#define WACMD_SET_MIXER 0x32 -#define WACMD_RST_MIXER 0x33 -#define WACMD_SET_MONO 0x34 - -/* - * Definitions for left/right recording input mux - */ -#define ADC_MUX_NONE 0 -#define ADC_MUX_MIXER 1 -#define ADC_MUX_LINE 2 -#define ADC_MUX_AUX2 3 -#define ADC_MUX_AUX1 4 -#define ADC_MUX_MIC 5 - -/* - * Definitions for mixer gain settings - */ -#define MIX_GAIN_LINE 0 /* line in */ -#define MIX_GAIN_AUX1 1 /* aux1 */ -#define MIX_GAIN_AUX2 2 /* aux2 */ -#define MIX_GAIN_XMIC 3 /* crossover mic */ -#define MIX_GAIN_MIC 4 /* normal mic */ -#define MIX_GAIN_PREMIC 5 /* preamp mic */ -#define MIX_GAIN_OUT 6 /* output */ -#define MIX_GAIN_MONO 7 /* mono in */ - -int wa_sendcmd(unsigned int cmd); -int wa_writecmd(unsigned int cmd, unsigned int arg); diff -Nru a/drivers/sound/wavfront.c b/drivers/sound/wavfront.c --- a/drivers/sound/wavfront.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3529 +0,0 @@ -/* -*- linux-c -*- - * - * sound/wavfront.c - * - * A Linux driver for Turtle Beach WaveFront Series (Maui, Tropez, Tropez Plus) - * - * This driver supports the onboard wavetable synthesizer (an ICS2115), - * including patch, sample and program loading and unloading, conversion - * of GUS patches during loading, and full user-level access to all - * WaveFront commands. It tries to provide semi-intelligent patch and - * sample management as well. - * - * It also provides support for the ICS emulation of an MPU-401. Full - * support for the ICS emulation's "virtual MIDI mode" is provided in - * wf_midi.c. - * - * Support is also provided for the Tropez Plus' onboard FX processor, - * a Yamaha YSS225. Currently, code exists to configure the YSS225, - * and there is an interface allowing tweaking of any of its memory - * addresses. However, I have been unable to decipher the logical - * positioning of the configuration info for various effects, so for - * now, you just get the YSS225 in the same state as Turtle Beach's - * "SETUPSND.EXE" utility leaves it. - * - * The boards' DAC/ADC (a Crystal CS4232) is supported by cs4232.[co], - * This chip also controls the configuration of the card: the wavefront - * synth is logical unit 4. - * - * - * Supported devices: - * - * /dev/dsp - using cs4232+ad1848 modules, OSS compatible - * /dev/midiNN and /dev/midiNN+1 - using wf_midi code, OSS compatible - * /dev/synth00 - raw synth interface - * - ********************************************************************** - * - * Copyright (C) by Paul Barton-Davis 1998 - * - * Some portions of this file are taken from work that is - * copyright (C) by Hannu Savolainen 1993-1996 - * - * Although the relevant code here is all new, the handling of - * sample/alias/multi- samples is entirely based on a driver by Matt - * Martin and Rutger Nijlunsing which demonstrated how to get things - * to work correctly. The GUS patch loading code has been almost - * unaltered by me, except to fit formatting and function names in the - * rest of the file. Many thanks to them. - * - * Appreciation and thanks to Hannu Savolainen for his early work on the Maui - * driver, and answering a few questions while this one was developed. - * - * Absolutely NO thanks to Turtle Beach/Voyetra and Yamaha for their - * complete lack of help in developing this driver, and in particular - * for their utter silence in response to questions about undocumented - * aspects of configuring a WaveFront soundcard, particularly the - * effects processor. - * - * $Id: wavfront.c,v 0.7 1998/09/09 15:47:36 pbd Exp $ - * - * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz - * Added some __init and __initdata to entries in yss225.c - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "sound_config.h" - -#include - -#define _MIDI_SYNTH_C_ -#define MIDI_SYNTH_NAME "WaveFront MIDI" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -/* Compile-time control of the extent to which OSS is supported. - - I consider /dev/sequencer to be an anachronism, but given its - widespread usage by various Linux MIDI software, it seems worth - offering support to it if its not too painful. Instead of using - /dev/sequencer, I recommend: - - for synth programming and patch loading: /dev/synthNN - for kernel-synchronized MIDI sequencing: the ALSA sequencer - for direct MIDI control: /dev/midiNN - - I have never tried static compilation into the kernel. The #if's - for this are really just notes to myself about what the code is - for. -*/ - -#define OSS_SUPPORT_SEQ 0x1 /* use of /dev/sequencer */ -#define OSS_SUPPORT_STATIC_INSTALL 0x2 /* static compilation into kernel */ - -#define OSS_SUPPORT_LEVEL 0x1 /* just /dev/sequencer for now */ - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ -static int (*midi_load_patch) (int devno, int format, const char *addr, - int offs, int count, int pmgr_flag) = NULL; -#endif /* OSS_SUPPORT_SEQ */ - -/* if WF_DEBUG not defined, no run-time debugging messages will - be available via the debug flag setting. Given the current - beta state of the driver, this will remain set until a future - version. -*/ - -#define WF_DEBUG 1 - -#ifdef WF_DEBUG - -/* Thank goodness for gcc's preprocessor ... */ - -#define DPRINT(cond, format, args...) \ - if ((dev.debug & (cond)) == (cond)) { \ - printk (KERN_DEBUG LOGNAME format, ## args); \ - } -#else -#define DPRINT(cond, format, args...) -#endif - -#define LOGNAME "WaveFront: " - -/* bitmasks for WaveFront status port value */ - -#define STAT_RINTR_ENABLED 0x01 -#define STAT_CAN_READ 0x02 -#define STAT_INTR_READ 0x04 -#define STAT_WINTR_ENABLED 0x10 -#define STAT_CAN_WRITE 0x20 -#define STAT_INTR_WRITE 0x40 - -/*** Module-accessible parameters ***************************************/ - -int wf_raw; /* we normally check for "raw state" to firmware - loading. if set, then during driver loading, the - state of the board is ignored, and we reset the - board and load the firmware anyway. - */ - -int fx_raw = 1; /* if this is zero, we'll leave the FX processor in - whatever state it is when the driver is loaded. - The default is to download the microprogram and - associated coefficients to set it up for "default" - operation, whatever that means. - */ - -int debug_default; /* you can set this to control debugging - during driver loading. it takes any combination - of the WF_DEBUG_* flags defined in - wavefront.h - */ - -/* XXX this needs to be made firmware and hardware version dependent */ - -char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed - version of the WaveFront OS - */ - -int wait_polls = 2000; /* This is a number of tries we poll the status register - before resorting to sleeping. WaveFront being an ISA - card each poll takes about 1.2us. So before going to - sleep we wait up to 2.4ms in a loop. - */ - -int sleep_length = HZ/100; /* This says how long we're going to sleep between polls. - 10ms sounds reasonable for fast response. - */ - -int sleep_tries = 50; /* Wait for status 0.5 seconds total. */ - -int reset_time = 2; /* hundreths of a second we wait after a HW reset for - the expected interrupt. - */ - -int ramcheck_time = 20; /* time in seconds to wait while ROM code - checks on-board RAM. - */ - -int osrun_time = 10; /* time in seconds we wait for the OS to - start running. - */ - -MODULE_PARM(wf_raw,"i"); -MODULE_PARM(fx_raw,"i"); -MODULE_PARM(debug_default,"i"); -MODULE_PARM(wait_polls,"i"); -MODULE_PARM(sleep_length,"i"); -MODULE_PARM(sleep_tries,"i"); -MODULE_PARM(ospath,"s"); -MODULE_PARM(reset_time,"i"); -MODULE_PARM(ramcheck_time,"i"); -MODULE_PARM(osrun_time,"i"); - -/***************************************************************************/ - -/* Note: because this module doesn't export any symbols, this really isn't - a global variable, even if it looks like one. I was quite confused by - this when I started writing this as a (newer) module -- pbd. -*/ - -struct wf_config { - int devno; /* device number from kernel */ - int irq; /* "you were one, one of the few ..." */ - int base; /* low i/o port address */ - -#define mpu_data_port base -#define mpu_command_port base + 1 /* write semantics */ -#define mpu_status_port base + 1 /* read semantics */ -#define data_port base + 2 -#define status_port base + 3 /* read semantics */ -#define control_port base + 3 /* write semantics */ -#define block_port base + 4 /* 16 bit, writeonly */ -#define last_block_port base + 6 /* 16 bit, writeonly */ - - /* FX ports. These are mapped through the ICS2115 to the YS225. - The ICS2115 takes care of flipping the relevant pins on the - YS225 so that access to each of these ports does the right - thing. Note: these are NOT documented by Turtle Beach. - */ - -#define fx_status base + 8 -#define fx_op base + 8 -#define fx_lcr base + 9 -#define fx_dsp_addr base + 0xa -#define fx_dsp_page base + 0xb -#define fx_dsp_lsb base + 0xc -#define fx_dsp_msb base + 0xd -#define fx_mod_addr base + 0xe -#define fx_mod_data base + 0xf - - volatile int irq_ok; /* set by interrupt handler */ - volatile int irq_cnt; /* ditto */ - int opened; /* flag, holds open(2) mode */ - char debug; /* debugging flags */ - int freemem; /* installed RAM, in bytes */ - - int synth_dev; /* devno for "raw" synth */ - int mididev; /* devno for internal MIDI */ - int ext_mididev; /* devno for external MIDI */ - int fx_mididev; /* devno for FX MIDI interface */ -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - int oss_dev; /* devno for OSS sequencer synth */ -#endif /* OSS_SUPPORT_SEQ */ - - char fw_version[2]; /* major = [0], minor = [1] */ - char hw_version[2]; /* major = [0], minor = [1] */ - char israw; /* needs Motorola microcode */ - char has_fx; /* has FX processor (Tropez+) */ - char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ - char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ - char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ - int samples_used; /* how many */ - char interrupts_on; /* h/w MPU interrupts enabled ? */ - char rom_samples_rdonly; /* can we write on ROM samples */ - wait_queue_head_t interrupt_sleeper; -} dev; - -static int detect_wffx(void); -static int wffx_ioctl (wavefront_fx_info *); -static int wffx_init (void); - -static int wavefront_delete_sample (int sampnum); -static int wavefront_find_free_sample (void); - -/* From wf_midi.c */ - -extern int virtual_midi_enable (void); -extern int virtual_midi_disable (void); -extern int detect_wf_mpu (int, int); -extern int install_wf_mpu (void); -extern int uninstall_wf_mpu (void); - -typedef struct { - int cmd; - char *action; - unsigned int read_cnt; - unsigned int write_cnt; - int need_ack; -} wavefront_command; - -static struct { - int errno; - const char *errstr; -} wavefront_errors[] = { - { 0x01, "Bad sample number" }, - { 0x02, "Out of sample memory" }, - { 0x03, "Bad patch number" }, - { 0x04, "Error in number of voices" }, - { 0x06, "Sample load already in progress" }, - { 0x0B, "No sample load request pending" }, - { 0x0E, "Bad MIDI channel number" }, - { 0x10, "Download Record Error" }, - { 0x80, "Success" }, - { 0x0, 0x0 } -}; - -#define NEEDS_ACK 1 - -static wavefront_command wavefront_commands[] = { - { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, - { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, - { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, - { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, - { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, - { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, - { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, - { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, - { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, - { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, - { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, - { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, - { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, - { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, - { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, - { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, - { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, - { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, - { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, - { WFC_DOWNLOAD_SAMPLE, "download sample", - 0, WF_SAMPLE_BYTES, NEEDS_ACK }, - { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, - { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", - 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, - { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, - - /* This command requires a variable number of bytes to be written. - There is a hack in wavefront_cmd() to support this. The actual - count is passed in as the read buffer ptr, cast appropriately. - Ugh. - */ - - { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, - - /* This one is a hack as well. We just read the first byte of the - response, don't fetch an ACK, and leave the rest to the - calling function. Ugly, ugly, ugly. - */ - - { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, - { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", - 0, WF_ALIAS_BYTES, NEEDS_ACK }, - { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, - { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, - { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, - { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, - { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, - { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, - { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, - { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, - { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, - { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, - NEEDS_ACK}, - { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, - { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", - 0, 1, NEEDS_ACK }, - { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, - { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", - 32, 0, 0 }, - { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, - { 0x00 } -}; - -static const char * -wavefront_errorstr (int errnum) - -{ - int i; - - for (i = 0; wavefront_errors[i].errstr; i++) { - if (wavefront_errors[i].errno == errnum) { - return wavefront_errors[i].errstr; - } - } - - return "Unknown WaveFront error"; -} - -static wavefront_command * -wavefront_get_command (int cmd) - -{ - int i; - - for (i = 0; wavefront_commands[i].cmd != 0; i++) { - if (cmd == wavefront_commands[i].cmd) { - return &wavefront_commands[i]; - } - } - - return (wavefront_command *) 0; -} - -static inline int -wavefront_status (void) - -{ - return inb (dev.status_port); -} - -static int -wavefront_wait (int mask) - -{ - int i; - - for (i = 0; i < wait_polls; i++) - if (wavefront_status() & mask) - return 1; - - for (i = 0; i < sleep_tries; i++) { - - if (wavefront_status() & mask) { - set_current_state(TASK_RUNNING); - return 1; - } - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(sleep_length); - if (signal_pending(current)) - break; - } - - set_current_state(TASK_RUNNING); - return 0; -} - -static int -wavefront_read (void) - -{ - if (wavefront_wait (STAT_CAN_READ)) - return inb (dev.data_port); - - DPRINT (WF_DEBUG_DATA, "read timeout.\n"); - - return -1; -} - -static int -wavefront_write (unsigned char data) - -{ - if (wavefront_wait (STAT_CAN_WRITE)) { - outb (data, dev.data_port); - return 0; - } - - DPRINT (WF_DEBUG_DATA, "write timeout.\n"); - - return -1; -} - -static int -wavefront_cmd (int cmd, unsigned char *rbuf, unsigned char *wbuf) - -{ - int ack; - int i; - int c; - wavefront_command *wfcmd; - - if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { - printk (KERN_WARNING LOGNAME "command 0x%x not supported.\n", - cmd); - return 1; - } - - /* Hack to handle the one variable-size write command. See - wavefront_send_multisample() for the other half of this - gross and ugly strategy. - */ - - if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { - wfcmd->write_cnt = (unsigned int) rbuf; - rbuf = 0; - } - - DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n", - cmd, wfcmd->action, wfcmd->read_cnt, - wfcmd->write_cnt, wfcmd->need_ack); - - if (wavefront_write (cmd)) { - DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request " - "0x%x [%s].\n", - cmd, wfcmd->action); - return 1; - } - - if (wfcmd->write_cnt > 0) { - DPRINT (WF_DEBUG_DATA, "writing %d bytes " - "for 0x%x\n", - wfcmd->write_cnt, cmd); - - for (i = 0; i < wfcmd->write_cnt; i++) { - if (wavefront_write (wbuf[i])) { - DPRINT (WF_DEBUG_IO, "bad write for byte " - "%d of 0x%x [%s].\n", - i, cmd, wfcmd->action); - return 1; - } - - DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n", - i, wbuf[i]); - } - } - - if (wfcmd->read_cnt > 0) { - DPRINT (WF_DEBUG_DATA, "reading %d ints " - "for 0x%x\n", - wfcmd->read_cnt, cmd); - - for (i = 0; i < wfcmd->read_cnt; i++) { - - if ((c = wavefront_read()) == -1) { - DPRINT (WF_DEBUG_IO, "bad read for byte " - "%d of 0x%x [%s].\n", - i, cmd, wfcmd->action); - return 1; - } - - /* Now handle errors. Lots of special cases here */ - - if (c == 0xff) { - if ((c = wavefront_read ()) == -1) { - DPRINT (WF_DEBUG_IO, "bad read for " - "error byte at " - "read byte %d " - "of 0x%x [%s].\n", - i, cmd, - wfcmd->action); - return 1; - } - - /* Can you believe this madness ? */ - - if (c == 1 && - wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { - rbuf[0] = WF_ST_EMPTY; - return (0); - - } else if (c == 3 && - wfcmd->cmd == WFC_UPLOAD_PATCH) { - - return 3; - - } else if (c == 1 && - wfcmd->cmd == WFC_UPLOAD_PROGRAM) { - - return 1; - - } else { - - DPRINT (WF_DEBUG_IO, "error %d (%s) " - "during " - "read for byte " - "%d of 0x%x " - "[%s].\n", - c, - wavefront_errorstr (c), - i, cmd, - wfcmd->action); - return 1; - - } - - } else { - rbuf[i] = c; - } - - DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]); - } - } - - if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { - - DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd); - - /* Some commands need an ACK, but return zero instead - of the standard value. - */ - - if ((ack = wavefront_read()) == 0) { - ack = WF_ACK; - } - - if (ack != WF_ACK) { - if (ack == -1) { - DPRINT (WF_DEBUG_IO, "cannot read ack for " - "0x%x [%s].\n", - cmd, wfcmd->action); - return 1; - - } else { - int err = -1; /* something unknown */ - - if (ack == 0xff) { /* explicit error */ - - if ((err = wavefront_read ()) == -1) { - DPRINT (WF_DEBUG_DATA, - "cannot read err " - "for 0x%x [%s].\n", - cmd, wfcmd->action); - } - } - - DPRINT (WF_DEBUG_IO, "0x%x [%s] " - "failed (0x%x, 0x%x, %s)\n", - cmd, wfcmd->action, ack, err, - wavefront_errorstr (err)); - - return -err; - } - } - - DPRINT (WF_DEBUG_DATA, "ack received " - "for 0x%x [%s]\n", - cmd, wfcmd->action); - } else { - - DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need " - "ACK (%d,%d,%d)\n", - cmd, wfcmd->action, wfcmd->read_cnt, - wfcmd->write_cnt, wfcmd->need_ack); - } - - return 0; - -} - -/*********************************************************************** -WaveFront: data munging - -Things here are weird. All data written to the board cannot -have its most significant bit set. Any data item with values -potentially > 0x7F (127) must be split across multiple bytes. - -Sometimes, we need to munge numeric values that are represented on -the x86 side as 8-32 bit values. Sometimes, we need to munge data -that is represented on the x86 side as an array of bytes. The most -efficient approach to handling both cases seems to be to use 2 -different functions for munging and 2 for de-munging. This avoids -weird casting and worrying about bit-level offsets. - -**********************************************************************/ - -static -unsigned char * -munge_int32 (unsigned int src, - unsigned char *dst, - unsigned int dst_size) -{ - int i; - - for (i = 0;i < dst_size; i++) { - *dst = src & 0x7F; /* Mask high bit of LSB */ - src = src >> 7; /* Rotate Right 7 bits */ - /* Note: we leave the upper bits in place */ - - dst++; - }; - return dst; -}; - -static int -demunge_int32 (unsigned char* src, int src_size) - -{ - int i; - int outval = 0; - - for (i = src_size - 1; i >= 0; i--) { - outval=(outval<<7)+src[i]; - } - - return outval; -}; - -static -unsigned char * -munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) - -{ - int i; - unsigned int last = dst_size / 2; - - for (i = 0; i < last; i++) { - *dst++ = src[i] & 0x7f; - *dst++ = src[i] >> 7; - } - return dst; -} - -static -unsigned char * -demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) - -{ - int i; - unsigned char *end = src + src_bytes; - - end = src + src_bytes; - - /* NOTE: src and dst *CAN* point to the same address */ - - for (i = 0; src != end; i++) { - dst[i] = *src++; - dst[i] |= (*src++)<<7; - } - - return dst; -} - -/*********************************************************************** -WaveFront: sample, patch and program management. -***********************************************************************/ - -static int -wavefront_delete_sample (int sample_num) - -{ - unsigned char wbuf[2]; - int x; - - wbuf[0] = sample_num & 0x7f; - wbuf[1] = sample_num >> 7; - - if ((x = wavefront_cmd (WFC_DELETE_SAMPLE, 0, wbuf)) == 0) { - dev.sample_status[sample_num] = WF_ST_EMPTY; - } - - return x; -} - -static int -wavefront_get_sample_status (int assume_rom) - -{ - int i; - unsigned char rbuf[32], wbuf[32]; - unsigned int sc_real, sc_alias, sc_multi; - - /* check sample status */ - - if (wavefront_cmd (WFC_GET_NSAMPLES, rbuf, wbuf)) { - printk (KERN_WARNING LOGNAME "cannot request sample count.\n"); - return -1; - } - - sc_real = sc_alias = sc_multi = dev.samples_used = 0; - - for (i = 0; i < WF_MAX_SAMPLE; i++) { - - wbuf[0] = i & 0x7f; - wbuf[1] = i >> 7; - - if (wavefront_cmd (WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { - printk (KERN_WARNING LOGNAME - "cannot identify sample " - "type of slot %d\n", i); - dev.sample_status[i] = WF_ST_EMPTY; - continue; - } - - dev.sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); - - if (assume_rom) { - dev.sample_status[i] |= WF_SLOT_ROM; - } - - switch (rbuf[0] & WF_ST_MASK) { - case WF_ST_SAMPLE: - sc_real++; - break; - case WF_ST_MULTISAMPLE: - sc_multi++; - break; - case WF_ST_ALIAS: - sc_alias++; - break; - case WF_ST_EMPTY: - break; - - default: - printk (KERN_WARNING LOGNAME "unknown sample type for " - "slot %d (0x%x)\n", - i, rbuf[0]); - } - - if (rbuf[0] != WF_ST_EMPTY) { - dev.samples_used++; - } - } - - printk (KERN_INFO LOGNAME - "%d samples used (%d real, %d aliases, %d multi), " - "%d empty\n", dev.samples_used, sc_real, sc_alias, sc_multi, - WF_MAX_SAMPLE - dev.samples_used); - - - return (0); - -} - -static int -wavefront_get_patch_status (void) - -{ - unsigned char patchbuf[WF_PATCH_BYTES]; - unsigned char patchnum[2]; - wavefront_patch *p; - int i, x, cnt, cnt2; - - for (i = 0; i < WF_MAX_PATCH; i++) { - patchnum[0] = i & 0x7f; - patchnum[1] = i >> 7; - - if ((x = wavefront_cmd (WFC_UPLOAD_PATCH, patchbuf, - patchnum)) == 0) { - - dev.patch_status[i] |= WF_SLOT_FILLED; - p = (wavefront_patch *) patchbuf; - dev.sample_status - [p->sample_number|(p->sample_msb<<7)] |= - WF_SLOT_USED; - - } else if (x == 3) { /* Bad patch number */ - dev.patch_status[i] = 0; - } else { - printk (KERN_ERR LOGNAME "upload patch " - "error 0x%x\n", x); - dev.patch_status[i] = 0; - return 1; - } - } - - /* program status has already filled in slot_used bits */ - - for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { - if (dev.patch_status[i] & WF_SLOT_FILLED) { - cnt++; - } - if (dev.patch_status[i] & WF_SLOT_USED) { - cnt2++; - } - - } - printk (KERN_INFO LOGNAME - "%d patch slots filled, %d in use\n", cnt, cnt2); - - return (0); -} - -static int -wavefront_get_program_status (void) - -{ - unsigned char progbuf[WF_PROGRAM_BYTES]; - wavefront_program prog; - unsigned char prognum; - int i, x, l, cnt; - - for (i = 0; i < WF_MAX_PROGRAM; i++) { - prognum = i; - - if ((x = wavefront_cmd (WFC_UPLOAD_PROGRAM, progbuf, - &prognum)) == 0) { - - dev.prog_status[i] |= WF_SLOT_USED; - - demunge_buf (progbuf, (unsigned char *) &prog, - WF_PROGRAM_BYTES); - - for (l = 0; l < WF_NUM_LAYERS; l++) { - if (prog.layer[l].mute) { - dev.patch_status - [prog.layer[l].patch_number] |= - WF_SLOT_USED; - } - } - } else if (x == 1) { /* Bad program number */ - dev.prog_status[i] = 0; - } else { - printk (KERN_ERR LOGNAME "upload program " - "error 0x%x\n", x); - dev.prog_status[i] = 0; - } - } - - for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { - if (dev.prog_status[i]) { - cnt++; - } - } - - printk (KERN_INFO LOGNAME "%d programs slots in use\n", cnt); - - return (0); -} - -static int -wavefront_send_patch (wavefront_patch_info *header) - -{ - unsigned char buf[WF_PATCH_BYTES+2]; - unsigned char *bptr; - - DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", - header->number); - - dev.patch_status[header->number] |= WF_SLOT_FILLED; - - bptr = buf; - bptr = munge_int32 (header->number, buf, 2); - munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); - - if (wavefront_cmd (WFC_DOWNLOAD_PATCH, 0, buf)) { - printk (KERN_ERR LOGNAME "download patch failed\n"); - return -(EIO); - } - - return (0); -} - -static int -wavefront_send_program (wavefront_patch_info *header) - -{ - unsigned char buf[WF_PROGRAM_BYTES+1]; - int i; - - DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", - header->number); - - dev.prog_status[header->number] = WF_SLOT_USED; - - /* XXX need to zero existing SLOT_USED bit for program_status[i] - where `i' is the program that's being (potentially) overwritten. - */ - - for (i = 0; i < WF_NUM_LAYERS; i++) { - if (header->hdr.pr.layer[i].mute) { - dev.patch_status[header->hdr.pr.layer[i].patch_number] |= - WF_SLOT_USED; - - /* XXX need to mark SLOT_USED for sample used by - patch_number, but this means we have to load it. Ick. - */ - } - } - - buf[0] = header->number; - munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); - - if (wavefront_cmd (WFC_DOWNLOAD_PROGRAM, 0, buf)) { - printk (KERN_WARNING LOGNAME "download patch failed\n"); - return -(EIO); - } - - return (0); -} - -static int -wavefront_freemem (void) - -{ - char rbuf[8]; - - if (wavefront_cmd (WFC_REPORT_FREE_MEMORY, rbuf, 0)) { - printk (KERN_WARNING LOGNAME "can't get memory stats.\n"); - return -1; - } else { - return demunge_int32 (rbuf, 4); - } -} - -static int -wavefront_send_sample (wavefront_patch_info *header, - UINT16 *dataptr, - int data_is_unsigned) - -{ - /* samples are downloaded via a 16-bit wide i/o port - (you could think of it as 2 adjacent 8-bit wide ports - but its less efficient that way). therefore, all - the blocksizes and so forth listed in the documentation, - and used conventionally to refer to sample sizes, - which are given in 8-bit units (bytes), need to be - divided by 2. - */ - - UINT16 sample_short; - UINT32 length; - UINT16 *data_end = 0; - unsigned int i; - const int max_blksize = 4096/2; - unsigned int written; - unsigned int blocksize; - int dma_ack; - int blocknum; - unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; - unsigned char *shptr; - int skip = 0; - int initial_skip = 0; - - DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, " - "type %d, %d bytes from 0x%x\n", - header->size ? "" : "header ", - header->number, header->subkey, - header->size, - (int) header->dataptr); - - if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) { - int x; - - if ((x = wavefront_find_free_sample ()) < 0) { - return -ENOMEM; - } - printk (KERN_DEBUG LOGNAME "unspecified sample => %d\n", x); - header->number = x; - } - - if (header->size) { - - /* XXX its a debatable point whether or not RDONLY semantics - on the ROM samples should cover just the sample data or - the sample header. For now, it only covers the sample data, - so anyone is free at all times to rewrite sample headers. - - My reason for this is that we have the sample headers - available in the WFB file for General MIDI, and so these - can always be reset if needed. The sample data, however, - cannot be recovered without a complete reset and firmware - reload of the ICS2115, which is a very expensive operation. - - So, doing things this way allows us to honor the notion of - "RESETSAMPLES" reasonably cheaply. Note however, that this - is done purely at user level: there is no WFB parser in - this driver, and so a complete reset (back to General MIDI, - or theoretically some other configuration) is the - responsibility of the user level library. - - To try to do this in the kernel would be a little - crazy: we'd need 158K of kernel space just to hold - a copy of the patch/program/sample header data. - */ - - if (dev.rom_samples_rdonly) { - if (dev.sample_status[header->number] & WF_SLOT_ROM) { - printk (KERN_ERR LOGNAME "sample slot %d " - "write protected\n", - header->number); - return -EACCES; - } - } - - wavefront_delete_sample (header->number); - } - - if (header->size) { - dev.freemem = wavefront_freemem (); - - if (dev.freemem < header->size) { - printk (KERN_ERR LOGNAME - "insufficient memory to " - "load %d byte sample.\n", - header->size); - return -ENOMEM; - } - - } - - skip = WF_GET_CHANNEL(&header->hdr.s); - - if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { - printk (KERN_ERR LOGNAME "channel selection only " - "possible on 16-bit samples"); - return -(EINVAL); - } - - switch (skip) { - case 0: - initial_skip = 0; - skip = 1; - break; - case 1: - initial_skip = 0; - skip = 2; - break; - case 2: - initial_skip = 1; - skip = 2; - break; - case 3: - initial_skip = 2; - skip = 3; - break; - case 4: - initial_skip = 3; - skip = 4; - break; - case 5: - initial_skip = 4; - skip = 5; - break; - case 6: - initial_skip = 5; - skip = 6; - break; - } - - DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => " - "initial skip = %d, skip = %d\n", - WF_GET_CHANNEL (&header->hdr.s), - initial_skip, skip); - - /* Be safe, and zero the "Unused" bits ... */ - - WF_SET_CHANNEL(&header->hdr.s, 0); - - /* adjust size for 16 bit samples by dividing by two. We always - send 16 bits per write, even for 8 bit samples, so the length - is always half the size of the sample data in bytes. - */ - - length = header->size / 2; - - /* the data we're sent has not been munged, and in fact, the - header we have to send isn't just a munged copy either. - so, build the sample header right here. - */ - - shptr = &sample_hdr[0]; - - shptr = munge_int32 (header->number, shptr, 2); - - if (header->size) { - shptr = munge_int32 (length, shptr, 4); - } - - /* Yes, a 4 byte result doesn't contain all of the offset bits, - but the offset only uses 24 bits. - */ - - shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleStartOffset), - shptr, 4); - shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopStartOffset), - shptr, 4); - shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopEndOffset), - shptr, 4); - shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset), - shptr, 4); - - /* This one is truly weird. What kind of weirdo decided that in - a system dominated by 16 and 32 bit integers, they would use - a just 12 bits ? - */ - - shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); - - /* Why is this nybblified, when the MSB is *always* zero ? - Anyway, we can't take address of bitfield, so make a - good-faith guess at where it starts. - */ - - shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), - shptr, 2); - - if (wavefront_cmd (header->size ? - WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, - 0, sample_hdr)) { - printk (KERN_WARNING LOGNAME "sample %sdownload refused.\n", - header->size ? "" : "header "); - return -(EIO); - } - - if (header->size == 0) { - goto sent; /* Sorry. Just had to have one somewhere */ - } - - data_end = dataptr + length; - - /* Do any initial skip over an unused channel's data */ - - dataptr += initial_skip; - - for (written = 0, blocknum = 0; - written < length; written += max_blksize, blocknum++) { - - if ((length - written) > max_blksize) { - blocksize = max_blksize; - } else { - /* round to nearest 16-byte value */ - blocksize = ((length-written+7)&~0x7); - } - - if (wavefront_cmd (WFC_DOWNLOAD_BLOCK, 0, 0)) { - printk (KERN_WARNING LOGNAME "download block " - "request refused.\n"); - return -(EIO); - } - - for (i = 0; i < blocksize; i++) { - - if (dataptr < data_end) { - - __get_user (sample_short, dataptr); - dataptr += skip; - - if (data_is_unsigned) { /* GUS ? */ - - if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { - - /* 8 bit sample - resolution, sign - extend both bytes. - */ - - ((unsigned char*) - &sample_short)[0] += 0x7f; - ((unsigned char*) - &sample_short)[1] += 0x7f; - - } else { - - /* 16 bit sample - resolution, sign - extend the MSB. - */ - - sample_short += 0x7fff; - } - } - - } else { - - /* In padding section of final block: - - Don't fetch unsupplied data from - user space, just continue with - whatever the final value was. - */ - } - - if (i < blocksize - 1) { - outw (sample_short, dev.block_port); - } else { - outw (sample_short, dev.last_block_port); - } - } - - /* Get "DMA page acknowledge", even though its really - nothing to do with DMA at all. - */ - - if ((dma_ack = wavefront_read ()) != WF_DMA_ACK) { - if (dma_ack == -1) { - printk (KERN_ERR LOGNAME "upload sample " - "DMA ack timeout\n"); - return -(EIO); - } else { - printk (KERN_ERR LOGNAME "upload sample " - "DMA ack error 0x%x\n", - dma_ack); - return -(EIO); - } - } - } - - dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); - - /* Note, label is here because sending the sample header shouldn't - alter the sample_status info at all. - */ - - sent: - return (0); -} - -static int -wavefront_send_alias (wavefront_patch_info *header) - -{ - unsigned char alias_hdr[WF_ALIAS_BYTES]; - - DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is " - "alias for %d\n", - header->number, - header->hdr.a.OriginalSample); - - munge_int32 (header->number, &alias_hdr[0], 2); - munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); - munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), - &alias_hdr[4], 4); - munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), - &alias_hdr[8], 4); - munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), - &alias_hdr[12], 4); - munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), - &alias_hdr[16], 4); - munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); - munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); - - if (wavefront_cmd (WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) { - printk (KERN_ERR LOGNAME "download alias failed.\n"); - return -(EIO); - } - - dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); - - return (0); -} - -static int -wavefront_send_multisample (wavefront_patch_info *header) -{ - int i; - int num_samples; - unsigned char msample_hdr[WF_MSAMPLE_BYTES]; - - munge_int32 (header->number, &msample_hdr[0], 2); - - /* You'll recall at this point that the "number of samples" value - in a wavefront_multisample struct is actually the log2 of the - real number of samples. - */ - - num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); - msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; - - DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n", - header->number, - header->hdr.ms.NumberOfSamples, - num_samples); - - for (i = 0; i < num_samples; i++) { - DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n", - i, header->hdr.ms.SampleNumber[i]); - munge_int32 (header->hdr.ms.SampleNumber[i], - &msample_hdr[3+(i*2)], 2); - } - - /* Need a hack here to pass in the number of bytes - to be written to the synth. This is ugly, and perhaps - one day, I'll fix it. - */ - - if (wavefront_cmd (WFC_DOWNLOAD_MULTISAMPLE, - (unsigned char *) ((num_samples*2)+3), - msample_hdr)) { - printk (KERN_ERR LOGNAME "download of multisample failed.\n"); - return -(EIO); - } - - dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); - - return (0); -} - -static int -wavefront_fetch_multisample (wavefront_patch_info *header) -{ - int i; - unsigned char log_ns[1]; - unsigned char number[2]; - int num_samples; - - munge_int32 (header->number, number, 2); - - if (wavefront_cmd (WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { - printk (KERN_ERR LOGNAME "upload multisample failed.\n"); - return -(EIO); - } - - DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n", - header->number, log_ns[0]); - - header->hdr.ms.NumberOfSamples = log_ns[0]; - - /* get the number of samples ... */ - - num_samples = (1 << log_ns[0]); - - for (i = 0; i < num_samples; i++) { - char d[2]; - - if ((d[0] = wavefront_read ()) == -1) { - printk (KERN_ERR LOGNAME "upload multisample failed " - "during sample loop.\n"); - return -(EIO); - } - - if ((d[1] = wavefront_read ()) == -1) { - printk (KERN_ERR LOGNAME "upload multisample failed " - "during sample loop.\n"); - return -(EIO); - } - - header->hdr.ms.SampleNumber[i] = - demunge_int32 ((unsigned char *) d, 2); - - DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n", - i, header->hdr.ms.SampleNumber[i]); - } - - return (0); -} - - -static int -wavefront_send_drum (wavefront_patch_info *header) - -{ - unsigned char drumbuf[WF_DRUM_BYTES]; - wavefront_drum *drum = &header->hdr.d; - int i; - - DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI " - "note %d, patch = %d\n", - header->number, drum->PatchNumber); - - drumbuf[0] = header->number & 0x7f; - - for (i = 0; i < 4; i++) { - munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); - } - - if (wavefront_cmd (WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) { - printk (KERN_ERR LOGNAME "download drum failed.\n"); - return -(EIO); - } - - return (0); -} - -static int -wavefront_find_free_sample (void) - -{ - int i; - - for (i = 0; i < WF_MAX_SAMPLE; i++) { - if (!(dev.sample_status[i] & WF_SLOT_FILLED)) { - return i; - } - } - printk (KERN_WARNING LOGNAME "no free sample slots!\n"); - return -1; -} - -static int -wavefront_find_free_patch (void) - -{ - int i; - - for (i = 0; i < WF_MAX_PATCH; i++) { - if (!(dev.patch_status[i] & WF_SLOT_FILLED)) { - return i; - } - } - printk (KERN_WARNING LOGNAME "no free patch slots!\n"); - return -1; -} - -static int -log2_2048(int n) - -{ - int tbl[]={0, 0, 2048, 3246, 4096, 4755, 5294, 5749, 6143, - 6492, 6803, 7084, 7342, 7578, 7797, 8001, 8192, - 8371, 8540, 8699, 8851, 8995, 9132, 9264, 9390, - 9510, 9626, 9738, 9845, 9949, 10049, 10146}; - int i; - - /* Returns 2048*log2(n) */ - - /* FIXME: this is like doing integer math - on quantum particles (RuN) */ - - i=0; - while(n>=32*256) { - n>>=8; - i+=2048*8; - } - while(n>=32) { - n>>=1; - i+=2048; - } - i+=tbl[n]; - return(i); -} - -static int -wavefront_load_gus_patch (int devno, int format, const char *addr, - int offs, int count, int pmgr_flag) -{ - struct patch_info guspatch; - wavefront_patch_info samp, pat, prog; - wavefront_patch *patp; - wavefront_sample *sampp; - wavefront_program *progp; - - int i,base_note; - long sizeof_patch; - - /* Copy in the header of the GUS patch */ - - sizeof_patch = (long) &guspatch.data[0] - (long) &guspatch; - copy_from_user (&((char *) &guspatch)[offs], - &(addr)[offs], sizeof_patch - offs); - - if ((i = wavefront_find_free_patch ()) == -1) { - return -EBUSY; - } - pat.number = i; - pat.subkey = WF_ST_PATCH; - patp = &pat.hdr.p; - - if ((i = wavefront_find_free_sample ()) == -1) { - return -EBUSY; - } - samp.number = i; - samp.subkey = WF_ST_SAMPLE; - samp.size = guspatch.len; - sampp = &samp.hdr.s; - - prog.number = guspatch.instr_no; - progp = &prog.hdr.pr; - - /* Setup the patch structure */ - - patp->amplitude_bias=guspatch.volume; - patp->portamento=0; - patp->sample_number= samp.number & 0xff; - patp->sample_msb= samp.number>>8; - patp->pitch_bend= /*12*/ 0; - patp->mono=1; - patp->retrigger=1; - patp->nohold=(guspatch.mode & WAVE_SUSTAIN_ON) ? 0:1; - patp->frequency_bias=0; - patp->restart=0; - patp->reuse=0; - patp->reset_lfo=1; - patp->fm_src2=0; - patp->fm_src1=WF_MOD_MOD_WHEEL; - patp->am_src=WF_MOD_PRESSURE; - patp->am_amount=127; - patp->fc1_mod_amount=0; - patp->fc2_mod_amount=0; - patp->fm_amount1=0; - patp->fm_amount2=0; - patp->envelope1.attack_level=127; - patp->envelope1.decay1_level=127; - patp->envelope1.decay2_level=127; - patp->envelope1.sustain_level=127; - patp->envelope1.release_level=0; - patp->envelope2.attack_velocity=127; - patp->envelope2.attack_level=127; - patp->envelope2.decay1_level=127; - patp->envelope2.decay2_level=127; - patp->envelope2.sustain_level=127; - patp->envelope2.release_level=0; - patp->envelope2.attack_velocity=127; - patp->randomizer=0; - - /* Program for this patch */ - - progp->layer[0].patch_number= pat.number; /* XXX is this right ? */ - progp->layer[0].mute=1; - progp->layer[0].pan_or_mod=1; - progp->layer[0].pan=7; - progp->layer[0].mix_level=127 /* guspatch.volume */; - progp->layer[0].split_type=0; - progp->layer[0].split_point=0; - progp->layer[0].play_below=0; - - for (i = 1; i < 4; i++) { - progp->layer[i].mute=0; - } - - /* Sample data */ - - sampp->SampleResolution=((~guspatch.mode & WAVE_16_BITS)<<1); - - for (base_note=0; - note_to_freq (base_note) < guspatch.base_note; - base_note++); - - if ((guspatch.base_note-note_to_freq(base_note)) - >(note_to_freq(base_note)-guspatch.base_note)) - base_note++; - - printk(KERN_DEBUG "ref freq=%d,base note=%d\n", - guspatch.base_freq, - base_note); - - sampp->FrequencyBias = (29550 - log2_2048(guspatch.base_freq) - + base_note*171); - printk(KERN_DEBUG "Freq Bias is %d\n", sampp->FrequencyBias); - sampp->Loop=(guspatch.mode & WAVE_LOOPING) ? 1:0; - sampp->sampleStartOffset.Fraction=0; - sampp->sampleStartOffset.Integer=0; - sampp->loopStartOffset.Fraction=0; - sampp->loopStartOffset.Integer=guspatch.loop_start - >>((guspatch.mode&WAVE_16_BITS) ? 1:0); - sampp->loopEndOffset.Fraction=0; - sampp->loopEndOffset.Integer=guspatch.loop_end - >>((guspatch.mode&WAVE_16_BITS) ? 1:0); - sampp->sampleEndOffset.Fraction=0; - sampp->sampleEndOffset.Integer=guspatch.len >> (guspatch.mode&1); - sampp->Bidirectional=(guspatch.mode&WAVE_BIDIR_LOOP) ? 1:0; - sampp->Reverse=(guspatch.mode&WAVE_LOOP_BACK) ? 1:0; - - /* Now ship it down */ - - wavefront_send_sample (&samp, - (unsigned short *) &(addr)[sizeof_patch], - (guspatch.mode & WAVE_UNSIGNED) ? 1:0); - wavefront_send_patch (&pat); - wavefront_send_program (&prog); - - /* Now pan as best we can ... use the slave/internal MIDI device - number if it exists (since it talks to the WaveFront), or the - master otherwise. - */ - - if (dev.mididev > 0) { - midi_synth_controller (dev.mididev, guspatch.instr_no, 10, - ((guspatch.panning << 4) > 127) ? - 127 : (guspatch.panning << 4)); - } - - return(0); -} - -static int -wavefront_load_patch (const char *addr) - - -{ - wavefront_patch_info header; - - if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) - - sizeof(wavefront_any))) { - printk (KERN_WARNING LOGNAME "bad address for load patch.\n"); - return -(EINVAL); - } - - DPRINT (WF_DEBUG_LOAD_PATCH, "download " - "Sample type: %d " - "Sample number: %d " - "Sample size: %d\n", - header.subkey, - header.number, - header.size); - - switch (header.subkey) { - case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ - - copy_from_user ((unsigned char *) &header.hdr.s, - (unsigned char *) header.hdrptr, - sizeof (wavefront_sample)); - - return wavefront_send_sample (&header, header.dataptr, 0); - - case WF_ST_MULTISAMPLE: - - copy_from_user ((unsigned char *) &header.hdr.s, - (unsigned char *) header.hdrptr, - sizeof (wavefront_multisample)); - - return wavefront_send_multisample (&header); - - - case WF_ST_ALIAS: - - copy_from_user ((unsigned char *) &header.hdr.a, - (unsigned char *) header.hdrptr, - sizeof (wavefront_alias)); - - return wavefront_send_alias (&header); - - case WF_ST_DRUM: - copy_from_user ((unsigned char *) &header.hdr.d, - (unsigned char *) header.hdrptr, - sizeof (wavefront_drum)); - - return wavefront_send_drum (&header); - - case WF_ST_PATCH: - copy_from_user ((unsigned char *) &header.hdr.p, - (unsigned char *) header.hdrptr, - sizeof (wavefront_patch)); - - return wavefront_send_patch (&header); - - case WF_ST_PROGRAM: - copy_from_user ((unsigned char *) &header.hdr.pr, - (unsigned char *) header.hdrptr, - sizeof (wavefront_program)); - - return wavefront_send_program (&header); - - default: - printk (KERN_ERR LOGNAME "unknown patch type %d.\n", - header.subkey); - return -(EINVAL); - } - - return 0; -} - -/*********************************************************************** -WaveFront: /dev/sequencer{,2} and other hardware-dependent interfaces -***********************************************************************/ - -static void -process_sample_hdr (UCHAR8 *buf) - -{ - wavefront_sample s; - UCHAR8 *ptr; - - ptr = buf; - - /* The board doesn't send us an exact copy of a "wavefront_sample" - in response to an Upload Sample Header command. Instead, we - have to convert the data format back into our data structure, - just as in the Download Sample command, where we have to do - something very similar in the reverse direction. - */ - - *((UINT32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; - *((UINT32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; - *((UINT32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; - *((UINT32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; - *((UINT32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; - - s.SampleResolution = *ptr & 0x3; - s.Loop = *ptr & 0x8; - s.Bidirectional = *ptr & 0x10; - s.Reverse = *ptr & 0x40; - - /* Now copy it back to where it came from */ - - memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); -} - -static int -wavefront_synth_control (int cmd, wavefront_control *wc) - -{ - unsigned char patchnumbuf[2]; - int i; - - DPRINT (WF_DEBUG_CMD, "synth control with " - "cmd 0x%x\n", wc->cmd); - - /* Pre-handling of or for various commands */ - - switch (wc->cmd) { - case WFC_DISABLE_INTERRUPTS: - printk (KERN_INFO LOGNAME "interrupts disabled.\n"); - outb (0x80|0x20, dev.control_port); - dev.interrupts_on = 0; - return 0; - - case WFC_ENABLE_INTERRUPTS: - printk (KERN_INFO LOGNAME "interrupts enabled.\n"); - outb (0x80|0x40|0x20, dev.control_port); - dev.interrupts_on = 1; - return 0; - - case WFC_INTERRUPT_STATUS: - wc->rbuf[0] = dev.interrupts_on; - return 0; - - case WFC_ROMSAMPLES_RDONLY: - dev.rom_samples_rdonly = wc->wbuf[0]; - wc->status = 0; - return 0; - - case WFC_IDENTIFY_SLOT_TYPE: - i = wc->wbuf[0] | (wc->wbuf[1] << 7); - if (i <0 || i >= WF_MAX_SAMPLE) { - printk (KERN_WARNING LOGNAME "invalid slot ID %d\n", - i); - wc->status = EINVAL; - return 0; - } - wc->rbuf[0] = dev.sample_status[i]; - wc->status = 0; - return 0; - - case WFC_DEBUG_DRIVER: - dev.debug = wc->wbuf[0]; - printk (KERN_INFO LOGNAME "debug = 0x%x\n", dev.debug); - return 0; - - case WFC_FX_IOCTL: - wffx_ioctl ((wavefront_fx_info *) &wc->wbuf[0]); - return 0; - - case WFC_UPLOAD_PATCH: - munge_int32 (*((UINT32 *) wc->wbuf), patchnumbuf, 2); - memcpy (wc->wbuf, patchnumbuf, 2); - break; - - case WFC_UPLOAD_MULTISAMPLE: - /* multisamples have to be handled differently, and - cannot be dealt with properly by wavefront_cmd() alone. - */ - wc->status = wavefront_fetch_multisample - ((wavefront_patch_info *) wc->rbuf); - return 0; - - case WFC_UPLOAD_SAMPLE_ALIAS: - printk (KERN_INFO LOGNAME "support for sample alias upload " - "being considered.\n"); - wc->status = EINVAL; - return -EINVAL; - } - - wc->status = wavefront_cmd (wc->cmd, wc->rbuf, wc->wbuf); - - /* Post-handling of certain commands. - - In particular, if the command was an upload, demunge the data - so that the user-level doesn't have to think about it. - */ - - if (wc->status == 0) { - switch (wc->cmd) { - /* intercept any freemem requests so that we know - we are always current with the user-level view - of things. - */ - - case WFC_REPORT_FREE_MEMORY: - dev.freemem = demunge_int32 (wc->rbuf, 4); - break; - - case WFC_UPLOAD_PATCH: - demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES); - break; - - case WFC_UPLOAD_PROGRAM: - demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES); - break; - - case WFC_UPLOAD_EDRUM_PROGRAM: - demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1); - break; - - case WFC_UPLOAD_SAMPLE_HEADER: - process_sample_hdr (wc->rbuf); - break; - - case WFC_UPLOAD_SAMPLE_ALIAS: - printk (KERN_INFO LOGNAME "support for " - "sample aliases still " - "being considered.\n"); - break; - - case WFC_VMIDI_OFF: - if (virtual_midi_disable () < 0) { - return -(EIO); - } - break; - - case WFC_VMIDI_ON: - if (virtual_midi_enable () < 0) { - return -(EIO); - } - break; - } - } - - return 0; -} - - -/***********************************************************************/ -/* WaveFront: Linux file system interface (for access via raw synth) */ -/***********************************************************************/ - -static int -wavefront_open (struct inode *inode, struct file *file) -{ - /* XXX fix me */ - dev.opened = file->f_flags; - return 0; -} - -static int -wavefront_release(struct inode *inode, struct file *file) -{ - lock_kernel(); - dev.opened = 0; - dev.debug = 0; - unlock_kernel(); - return 0; -} - -static int -wavefront_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - wavefront_control wc; - int err; - - switch (cmd) { - - case WFCTL_WFCMD: - copy_from_user (&wc, (void *) arg, sizeof (wc)); - - if ((err = wavefront_synth_control (cmd, &wc)) == 0) { - copy_to_user ((void *) arg, &wc, sizeof (wc)); - } - - return err; - - case WFCTL_LOAD_SPP: - return wavefront_load_patch ((const char *) arg); - - default: - printk (KERN_WARNING LOGNAME "invalid ioctl %#x\n", cmd); - return -(EINVAL); - - } - return 0; -} - -static /*const*/ struct file_operations wavefront_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: wavefront_ioctl, - open: wavefront_open, - release: wavefront_release, -}; - - -/***********************************************************************/ -/* WaveFront: OSS installation and support interface */ -/***********************************************************************/ - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - -static struct synth_info wavefront_info = -{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT, - 0, 32, 0, 0, SYNTH_CAP_INPUT}; - -static int -wavefront_oss_open (int devno, int mode) - -{ - dev.opened = mode; - return 0; -} - -static void -wavefront_oss_close (int devno) - -{ - dev.opened = 0; - dev.debug = 0; - return; -} - -static int -wavefront_oss_ioctl (int devno, unsigned int cmd, caddr_t arg) - -{ - wavefront_control wc; - int err; - - switch (cmd) { - case SNDCTL_SYNTH_INFO: - if(copy_to_user(&((char *) arg)[0], &wavefront_info, - sizeof (wavefront_info))) - return -EFAULT; - return 0; - - case SNDCTL_SEQ_RESETSAMPLES: -// printk (KERN_WARNING LOGNAME "driver cannot reset samples.\n"); - return 0; /* don't force an error */ - - case SNDCTL_SEQ_PERCMODE: - return 0; /* don't force an error */ - - case SNDCTL_SYNTH_MEMAVL: - if ((dev.freemem = wavefront_freemem ()) < 0) { - printk (KERN_ERR LOGNAME "cannot get memory size\n"); - return -EIO; - } else { - return dev.freemem; - } - break; - - case SNDCTL_SYNTH_CONTROL: - if(copy_from_user (&wc, arg, sizeof (wc))) - err = -EFAULT; - else if ((err = wavefront_synth_control (cmd, &wc)) == 0) { - if(copy_to_user (arg, &wc, sizeof (wc))) - err = -EFAULT; - } - - return err; - - default: - return -(EINVAL); - } -} - -int -wavefront_oss_load_patch (int devno, int format, const char *addr, - int offs, int count, int pmgr_flag) -{ - - if (format == SYSEX_PATCH) { /* Handled by midi_synth.c */ - if (midi_load_patch == NULL) { - printk (KERN_ERR LOGNAME - "SYSEX not loadable: " - "no midi patch loader!\n"); - return -(EINVAL); - } - - return midi_load_patch (devno, format, addr, - offs, count, pmgr_flag); - - } else if (format == GUS_PATCH) { - return wavefront_load_gus_patch (devno, format, - addr, offs, count, pmgr_flag); - - } else if (format != WAVEFRONT_PATCH) { - printk (KERN_ERR LOGNAME "unknown patch format %d\n", format); - return -(EINVAL); - } - - if (count < sizeof (wavefront_patch_info)) { - printk (KERN_ERR LOGNAME "sample header too short\n"); - return -(EINVAL); - } - - /* "addr" points to a user-space wavefront_patch_info */ - - return wavefront_load_patch (addr); -} - -static struct synth_operations wavefront_operations = -{ - owner: THIS_MODULE, - id: "WaveFront", - info: &wavefront_info, - midi_dev: 0, - synth_type: SYNTH_TYPE_SAMPLE, - synth_subtype: SAMPLE_TYPE_WAVEFRONT, - open: wavefront_oss_open, - close: wavefront_oss_close, - ioctl: wavefront_oss_ioctl, - kill_note: midi_synth_kill_note, - start_note: midi_synth_start_note, - set_instr: midi_synth_set_instr, - reset: midi_synth_reset, - load_patch: midi_synth_load_patch, - aftertouch: midi_synth_aftertouch, - controller: midi_synth_controller, - panning: midi_synth_panning, - bender: midi_synth_bender, - setup_voice: midi_synth_setup_voice -}; -#endif /* OSS_SUPPORT_SEQ */ - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_STATIC_INSTALL - -static void __init attach_wavefront (struct address_info *hw_config) -{ - (void) install_wavefront (); -} - -static int __init probe_wavefront (struct address_info *hw_config) -{ - return !detect_wavefront (hw_config->irq, hw_config->io_base); -} - -static void __exit unload_wavefront (struct address_info *hw_config) -{ - (void) uninstall_wavefront (); -} - -#endif /* OSS_SUPPORT_STATIC_INSTALL */ - -/***********************************************************************/ -/* WaveFront: Linux modular sound kernel installation interface */ -/***********************************************************************/ - -void -wavefrontintr (int irq, void *dev_id, struct pt_regs *dummy) -{ - struct wf_config *hw = dev_id; - - /* - Some comments on interrupts. I attempted a version of this - driver that used interrupts throughout the code instead of - doing busy and/or sleep-waiting. Alas, it appears that once - the Motorola firmware is downloaded, the card *never* - generates an RX interrupt. These are successfully generated - during firmware loading, and after that wavefront_status() - reports that an interrupt is pending on the card from time - to time, but it never seems to be delivered to this - driver. Note also that wavefront_status() continues to - report that RX interrupts are enabled, suggesting that I - didn't goof up and disable them by mistake. - - Thus, I stepped back to a prior version of - wavefront_wait(), the only place where this really - matters. Its sad, but I've looked through the code to check - on things, and I really feel certain that the Motorola - firmware prevents RX-ready interrupts. - */ - - if ((wavefront_status() & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) { - return; - } - - hw->irq_ok = 1; - hw->irq_cnt++; - wake_up_interruptible (&hw->interrupt_sleeper); -} - -/* STATUS REGISTER - -0 Host Rx Interrupt Enable (1=Enabled) -1 Host Rx Register Full (1=Full) -2 Host Rx Interrupt Pending (1=Interrupt) -3 Unused -4 Host Tx Interrupt (1=Enabled) -5 Host Tx Register empty (1=Empty) -6 Host Tx Interrupt Pending (1=Interrupt) -7 Unused -*/ - -int -wavefront_interrupt_bits (int irq) - -{ - int bits; - - switch (irq) { - case 9: - bits = 0x00; - break; - case 5: - bits = 0x08; - break; - case 12: - bits = 0x10; - break; - case 15: - bits = 0x18; - break; - - default: - printk (KERN_WARNING LOGNAME "invalid IRQ %d\n", irq); - bits = -1; - } - - return bits; -} - -void -wavefront_should_cause_interrupt (int val, int port, int timeout) - -{ - unsigned long flags; - - save_flags (flags); - cli(); - dev.irq_ok = 0; - outb (val,port); - interruptible_sleep_on_timeout (&dev.interrupt_sleeper, timeout); - restore_flags (flags); -} - -static int __init wavefront_hw_reset (void) -{ - int bits; - int hwv[2]; - unsigned long irq_mask; - short reported_irq; - - /* IRQ already checked in init_module() */ - - bits = wavefront_interrupt_bits (dev.irq); - - printk (KERN_DEBUG LOGNAME "autodetecting WaveFront IRQ\n"); - - sti (); - - irq_mask = probe_irq_on (); - - outb (0x0, dev.control_port); - outb (0x80 | 0x40 | bits, dev.data_port); - wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1, - dev.control_port, - (reset_time*HZ)/100); - - reported_irq = probe_irq_off (irq_mask); - - if (reported_irq != dev.irq) { - if (reported_irq == 0) { - printk (KERN_ERR LOGNAME - "No unassigned interrupts detected " - "after h/w reset\n"); - } else if (reported_irq < 0) { - printk (KERN_ERR LOGNAME - "Multiple unassigned interrupts detected " - "after h/w reset\n"); - } else { - printk (KERN_ERR LOGNAME "autodetected IRQ %d not the " - "value provided (%d)\n", reported_irq, - dev.irq); - } - dev.irq = -1; - return 1; - } else { - printk (KERN_INFO LOGNAME "autodetected IRQ at %d\n", - reported_irq); - } - - if (request_irq (dev.irq, wavefrontintr, - SA_INTERRUPT|SA_SHIRQ, - "wavefront synth", &dev) < 0) { - printk (KERN_WARNING LOGNAME "IRQ %d not available!\n", - dev.irq); - return 1; - } - - /* try reset of port */ - - outb (0x0, dev.control_port); - - /* At this point, the board is in reset, and the H/W initialization - register is accessed at the same address as the data port. - - Bit 7 - Enable IRQ Driver - 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs - 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. - - Bit 6 - MIDI Interface Select - - 0 - Use the MIDI Input from the 26-pin WaveBlaster - compatible header as the serial MIDI source - 1 - Use the MIDI Input from the 9-pin D connector as the - serial MIDI source. - - Bits 5:3 - IRQ Selection - 0 0 0 - IRQ 2/9 - 0 0 1 - IRQ 5 - 0 1 0 - IRQ 12 - 0 1 1 - IRQ 15 - 1 0 0 - Reserved - 1 0 1 - Reserved - 1 1 0 - Reserved - 1 1 1 - Reserved - - Bits 2:1 - Reserved - Bit 0 - Disable Boot ROM - 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM - 1 - memory accesses to 03FC30-03FFFFH are directed to external - storage. - - */ - - /* configure hardware: IRQ, enable interrupts, - plus external 9-pin MIDI interface selected - */ - - outb (0x80 | 0x40 | bits, dev.data_port); - - /* CONTROL REGISTER - - 0 Host Rx Interrupt Enable (1=Enabled) 0x1 - 1 Unused 0x2 - 2 Unused 0x4 - 3 Unused 0x8 - 4 Host Tx Interrupt Enable 0x10 - 5 Mute (0=Mute; 1=Play) 0x20 - 6 Master Interrupt Enable (1=Enabled) 0x40 - 7 Master Reset (0=Reset; 1=Run) 0x80 - - Take us out of reset, mute output, master + TX + RX interrupts on. - - We'll get an interrupt presumably to tell us that the TX - register is clear. - */ - - wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1, - dev.control_port, - (reset_time*HZ)/100); - - /* Note: data port is now the data port, not the h/w initialization - port. - */ - - if (!dev.irq_ok) { - printk (KERN_WARNING LOGNAME - "intr not received after h/w un-reset.\n"); - goto gone_bad; - } - - dev.interrupts_on = 1; - - /* Note: data port is now the data port, not the h/w initialization - port. - - At this point, only "HW VERSION" or "DOWNLOAD OS" commands - will work. So, issue one of them, and wait for TX - interrupt. This can take a *long* time after a cold boot, - while the ISC ROM does its RAM test. The SDK says up to 4 - seconds - with 12MB of RAM on a Tropez+, it takes a lot - longer than that (~16secs). Note that the card understands - the difference between a warm and a cold boot, so - subsequent ISC2115 reboots (say, caused by module - reloading) will get through this much faster. - - XXX Interesting question: why is no RX interrupt received first ? - */ - - wavefront_should_cause_interrupt(WFC_HARDWARE_VERSION, - dev.data_port, ramcheck_time*HZ); - - if (!dev.irq_ok) { - printk (KERN_WARNING LOGNAME - "post-RAM-check interrupt not received.\n"); - goto gone_bad; - } - - if (!wavefront_wait (STAT_CAN_READ)) { - printk (KERN_WARNING LOGNAME - "no response to HW version cmd.\n"); - goto gone_bad; - } - - if ((hwv[0] = wavefront_read ()) == -1) { - printk (KERN_WARNING LOGNAME - "board not responding correctly.\n"); - goto gone_bad; - } - - if (hwv[0] == 0xFF) { /* NAK */ - - /* Board's RAM test failed. Try to read error code, - and tell us about it either way. - */ - - if ((hwv[0] = wavefront_read ()) == -1) { - printk (KERN_WARNING LOGNAME "on-board RAM test failed " - "(bad error code).\n"); - } else { - printk (KERN_WARNING LOGNAME "on-board RAM test failed " - "(error code: 0x%x).\n", - hwv[0]); - } - goto gone_bad; - } - - /* We're OK, just get the next byte of the HW version response */ - - if ((hwv[1] = wavefront_read ()) == -1) { - printk (KERN_WARNING LOGNAME "incorrect h/w response.\n"); - goto gone_bad; - } - - printk (KERN_INFO LOGNAME "hardware version %d.%d\n", - hwv[0], hwv[1]); - - return 0; - - - gone_bad: - if (dev.irq >= 0) { - free_irq (dev.irq, &dev); - dev.irq = -1; - } - return (1); -} - -static int __init detect_wavefront (int irq, int io_base) -{ - unsigned char rbuf[4], wbuf[4]; - - /* TB docs say the device takes up 8 ports, but we know that - if there is an FX device present (i.e. a Tropez+) it really - consumes 16. - */ - - if (check_region (io_base, 16)) { - printk (KERN_ERR LOGNAME "IO address range 0x%x - 0x%x " - "already in use - ignored\n", dev.base, - dev.base+15); - return -1; - } - - dev.irq = irq; - dev.base = io_base; - dev.israw = 0; - dev.debug = debug_default; - dev.interrupts_on = 0; - dev.irq_cnt = 0; - dev.rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */ - - if (wavefront_cmd (WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { - - dev.fw_version[0] = rbuf[0]; - dev.fw_version[1] = rbuf[1]; - printk (KERN_INFO LOGNAME - "firmware %d.%d already loaded.\n", - rbuf[0], rbuf[1]); - - /* check that a command actually works */ - - if (wavefront_cmd (WFC_HARDWARE_VERSION, - rbuf, wbuf) == 0) { - dev.hw_version[0] = rbuf[0]; - dev.hw_version[1] = rbuf[1]; - } else { - printk (KERN_WARNING LOGNAME "not raw, but no " - "hardware version!\n"); - return 0; - } - - if (!wf_raw) { - return 1; - } else { - printk (KERN_INFO LOGNAME - "reloading firmware anyway.\n"); - dev.israw = 1; - } - - } else { - - dev.israw = 1; - printk (KERN_INFO LOGNAME - "no response to firmware probe, assume raw.\n"); - - } - - init_waitqueue_head (&dev.interrupt_sleeper); - - if (wavefront_hw_reset ()) { - printk (KERN_WARNING LOGNAME "hardware reset failed\n"); - return 0; - } - - /* Check for FX device, present only on Tropez+ */ - - dev.has_fx = (detect_wffx () == 0); - - return 1; -} - -#include "os.h" -#define __KERNEL_SYSCALLS__ -#include -#include -#include -#include -#include - -static int errno; - -static int -wavefront_download_firmware (char *path) - -{ - unsigned char section[WF_SECTION_MAX]; - char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ - int section_cnt_downloaded = 0; - int fd; - int c; - int i; - mm_segment_t fs; - - /* This tries to be a bit cleverer than the stuff Alan Cox did for - the generic sound firmware, in that it actually knows - something about the structure of the Motorola firmware. In - particular, it uses a version that has been stripped of the - 20K of useless header information, and had section lengths - added, making it possible to load the entire OS without any - [kv]malloc() activity, since the longest entity we ever read is - 42 bytes (well, WF_SECTION_MAX) long. - */ - - fs = get_fs(); - set_fs (get_ds()); - - if ((fd = open (path, 0, 0)) < 0) { - printk (KERN_WARNING LOGNAME "Unable to load \"%s\".\n", - path); - return 1; - } - - while (1) { - int x; - - if ((x = read (fd, §ion_length, sizeof (section_length))) != - sizeof (section_length)) { - printk (KERN_ERR LOGNAME "firmware read error.\n"); - goto failure; - } - - if (section_length == 0) { - break; - } - - if (read (fd, section, section_length) != section_length) { - printk (KERN_ERR LOGNAME "firmware section " - "read error.\n"); - goto failure; - } - - /* Send command */ - - if (wavefront_write (WFC_DOWNLOAD_OS)) { - goto failure; - } - - for (i = 0; i < section_length; i++) { - if (wavefront_write (section[i])) { - goto failure; - } - } - - /* get ACK */ - - if (wavefront_wait (STAT_CAN_READ)) { - - if ((c = inb (dev.data_port)) != WF_ACK) { - - printk (KERN_ERR LOGNAME "download " - "of section #%d not " - "acknowledged, ack = 0x%x\n", - section_cnt_downloaded + 1, c); - goto failure; - - } - - } else { - printk (KERN_ERR LOGNAME "time out for firmware ACK.\n"); - goto failure; - } - - } - - close (fd); - set_fs (fs); - return 0; - - failure: - close (fd); - set_fs (fs); - printk (KERN_ERR "\nWaveFront: firmware download failed!!!\n"); - return 1; -} - -static int __init wavefront_config_midi (void) -{ - unsigned char rbuf[4], wbuf[4]; - - if (detect_wf_mpu (dev.irq, dev.base) < 0) { - printk (KERN_WARNING LOGNAME - "could not find working MIDI device\n"); - return -1; - } - - if ((dev.mididev = install_wf_mpu ()) < 0) { - printk (KERN_WARNING LOGNAME - "MIDI interfaces not configured\n"); - return -1; - } - - /* Route external MIDI to WaveFront synth (by default) */ - - if (wavefront_cmd (WFC_MISYNTH_ON, rbuf, wbuf)) { - printk (KERN_WARNING LOGNAME - "cannot enable MIDI-IN to synth routing.\n"); - /* XXX error ? */ - } - - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - /* Get the regular MIDI patch loading function, so we can - use it if we ever get handed a SYSEX patch. This is - unlikely, because its so damn slow, but we may as well - leave this functionality from maui.c behind, since it - could be useful for sequencer applications that can - only use MIDI to do patch loading. - */ - - if (midi_devs[dev.mididev]->converter != NULL) { - midi_load_patch = midi_devs[dev.mididev]->converter->load_patch; - midi_devs[dev.mididev]->converter->load_patch = - &wavefront_oss_load_patch; - } - -#endif /* OSS_SUPPORT_SEQ */ - - /* Turn on Virtual MIDI, but first *always* turn it off, - since otherwise consectutive reloads of the driver will - never cause the hardware to generate the initial "internal" or - "external" source bytes in the MIDI data stream. This - is pretty important, since the internal hardware generally will - be used to generate none or very little MIDI output, and - thus the only source of MIDI data is actually external. Without - the switch bytes, the driver will think it all comes from - the internal interface. Duh. - */ - - if (wavefront_cmd (WFC_VMIDI_OFF, rbuf, wbuf)) { - printk (KERN_WARNING LOGNAME - "virtual MIDI mode not disabled\n"); - return 0; /* We're OK, but missing the external MIDI dev */ - } - - if ((dev.ext_mididev = virtual_midi_enable ()) < 0) { - printk (KERN_WARNING LOGNAME "no virtual MIDI access.\n"); - } else { - if (wavefront_cmd (WFC_VMIDI_ON, rbuf, wbuf)) { - printk (KERN_WARNING LOGNAME - "cannot enable virtual MIDI mode.\n"); - virtual_midi_disable (); - } - } - - return 0; -} - -static int __init wavefront_do_reset (int atboot) -{ - char voices[1]; - - if (!atboot && wavefront_hw_reset ()) { - printk (KERN_WARNING LOGNAME "hw reset failed.\n"); - goto gone_bad; - } - - if (dev.israw) { - if (wavefront_download_firmware (ospath)) { - goto gone_bad; - } - - dev.israw = 0; - - /* Wait for the OS to get running. The protocol for - this is non-obvious, and was determined by - using port-IO tracing in DOSemu and some - experimentation here. - - Rather than using timed waits, use interrupts creatively. - */ - - wavefront_should_cause_interrupt (WFC_NOOP, - dev.data_port, - (osrun_time*HZ)); - - if (!dev.irq_ok) { - printk (KERN_WARNING LOGNAME - "no post-OS interrupt.\n"); - goto gone_bad; - } - - /* Now, do it again ! */ - - wavefront_should_cause_interrupt (WFC_NOOP, - dev.data_port, (10*HZ)); - - if (!dev.irq_ok) { - printk (KERN_WARNING LOGNAME - "no post-OS interrupt(2).\n"); - goto gone_bad; - } - - /* OK, no (RX/TX) interrupts any more, but leave mute - in effect. - */ - - outb (0x80|0x40, dev.control_port); - - /* No need for the IRQ anymore */ - - free_irq (dev.irq, &dev); - - } - - if (dev.has_fx && fx_raw) { - wffx_init (); - } - - /* SETUPSND.EXE asks for sample memory config here, but since i - have no idea how to interpret the result, we'll forget - about it. - */ - - if ((dev.freemem = wavefront_freemem ()) < 0) { - goto gone_bad; - } - - printk (KERN_INFO LOGNAME "available DRAM %dk\n", dev.freemem / 1024); - - if (wavefront_write (0xf0) || - wavefront_write (1) || - (wavefront_read () < 0)) { - dev.debug = 0; - printk (KERN_WARNING LOGNAME "MPU emulation mode not set.\n"); - goto gone_bad; - } - - voices[0] = 32; - - if (wavefront_cmd (WFC_SET_NVOICES, 0, voices)) { - printk (KERN_WARNING LOGNAME - "cannot set number of voices to 32.\n"); - goto gone_bad; - } - - - return 0; - - gone_bad: - /* reset that sucker so that it doesn't bother us. */ - - outb (0x0, dev.control_port); - dev.interrupts_on = 0; - if (dev.irq >= 0) { - free_irq (dev.irq, &dev); - } - return 1; -} - -static int __init wavefront_init (int atboot) -{ - int samples_are_from_rom; - - if (dev.israw) { - samples_are_from_rom = 1; - } else { - /* XXX is this always true ? */ - samples_are_from_rom = 0; - } - - if (dev.israw || fx_raw) { - if (wavefront_do_reset (atboot)) { - return -1; - } - } - - wavefront_get_sample_status (samples_are_from_rom); - wavefront_get_program_status (); - wavefront_get_patch_status (); - - /* Start normal operation: unreset, master interrupt enabled, no mute - */ - - outb (0x80|0x40|0x20, dev.control_port); - - return (0); -} - -static int __init install_wavefront (void) - -{ - if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1)) < 0) { - printk (KERN_ERR LOGNAME "cannot register raw synth\n"); - return -1; - } - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - if ((dev.oss_dev = sound_alloc_synthdev()) == -1) { - printk (KERN_ERR LOGNAME "Too many sequencers\n"); - return -1; - } else { - synth_devs[dev.oss_dev] = &wavefront_operations; - } -#endif /* OSS_SUPPORT_SEQ */ - - if (wavefront_init (1) < 0) { - printk (KERN_WARNING LOGNAME "initialization failed.\n"); - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - sound_unload_synthdev (dev.oss_dev); -#endif /* OSS_SUPPORT_SEQ */ - - return -1; - } - - request_region (dev.base+2, 6, "wavefront synth"); - - if (dev.has_fx) { - request_region (dev.base+8, 8, "wavefront fx"); - } - - if (wavefront_config_midi ()) { - printk (KERN_WARNING LOGNAME "could not initialize MIDI.\n"); - } - - return dev.oss_dev; -} - -static void __exit uninstall_wavefront (void) -{ - /* the first two i/o addresses are freed by the wf_mpu code */ - release_region (dev.base+2, 6); - - if (dev.has_fx) { - release_region (dev.base+8, 8); - } - - unregister_sound_synth (dev.synth_dev); - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - sound_unload_synthdev (dev.oss_dev); -#endif /* OSS_SUPPORT_SEQ */ - uninstall_wf_mpu (); -} - -/***********************************************************************/ -/* WaveFront FX control */ -/***********************************************************************/ - -#include "yss225.h" - -/* Control bits for the Load Control Register - */ - -#define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ -#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ -#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ - -static int -wffx_idle (void) - -{ - int i; - unsigned int x = 0x80; - - for (i = 0; i < 1000; i++) { - x = inb (dev.fx_status); - if ((x & 0x80) == 0) { - break; - } - } - - if (x & 0x80) { - printk (KERN_ERR LOGNAME "FX device never idle.\n"); - return 0; - } - - return (1); -} - -int __init detect_wffx (void) -{ - /* This is a crude check, but its the best one I have for now. - Certainly on the Maui and the Tropez, wffx_idle() will - report "never idle", which suggests that this test should - work OK. - */ - - if (inb (dev.fx_status) & 0x80) { - printk (KERN_INFO LOGNAME "Hmm, probably a Maui or Tropez.\n"); - return -1; - } - - return 0; -} - -int __init attach_wffx (void) -{ - if ((dev.fx_mididev = sound_alloc_mididev ()) < 0) { - printk (KERN_WARNING LOGNAME "cannot install FX Midi driver\n"); - return -1; - } - - return 0; -} - -void -wffx_mute (int onoff) - -{ - if (!wffx_idle()) { - return; - } - - outb (onoff ? 0x02 : 0x00, dev.fx_op); -} - -static int -wffx_memset (int page, - int addr, int cnt, unsigned short *data) -{ - if (page < 0 || page > 7) { - printk (KERN_ERR LOGNAME "FX memset: " - "page must be >= 0 and <= 7\n"); - return -(EINVAL); - } - - if (addr < 0 || addr > 0x7f) { - printk (KERN_ERR LOGNAME "FX memset: " - "addr must be >= 0 and <= 7f\n"); - return -(EINVAL); - } - - if (cnt == 1) { - - outb (FX_LSB_TRANSFER, dev.fx_lcr); - outb (page, dev.fx_dsp_page); - outb (addr, dev.fx_dsp_addr); - outb ((data[0] >> 8), dev.fx_dsp_msb); - outb ((data[0] & 0xff), dev.fx_dsp_lsb); - - printk (KERN_INFO LOGNAME "FX: addr %d:%x set to 0x%x\n", - page, addr, data[0]); - - } else { - int i; - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (page, dev.fx_dsp_page); - outb (addr, dev.fx_dsp_addr); - - for (i = 0; i < cnt; i++) { - outb ((data[i] >> 8), dev.fx_dsp_msb); - outb ((data[i] & 0xff), dev.fx_dsp_lsb); - if (!wffx_idle ()) { - break; - } - } - - if (i != cnt) { - printk (KERN_WARNING LOGNAME - "FX memset " - "(0x%x, 0x%x, 0x%x, %d) incomplete\n", - page, addr, (int) data, cnt); - return -(EIO); - } - } - - return 0; -} - -static int -wffx_ioctl (wavefront_fx_info *r) - -{ - unsigned short page_data[256]; - unsigned short *pd; - - switch (r->request) { - case WFFX_MUTE: - wffx_mute (r->data[0]); - return 0; - - case WFFX_MEMSET: - - if (r->data[2] <= 0) { - printk (KERN_ERR LOGNAME "cannot write " - "<= 0 bytes to FX\n"); - return -(EINVAL); - } else if (r->data[2] == 1) { - pd = (unsigned short *) &r->data[3]; - } else { - if (r->data[2] > sizeof (page_data)) { - printk (KERN_ERR LOGNAME "cannot write " - "> 255 bytes to FX\n"); - return -(EINVAL); - } - copy_from_user (page_data, (unsigned char *) r->data[3], - r->data[2]); - pd = page_data; - } - - return wffx_memset (r->data[0], /* page */ - r->data[1], /* addr */ - r->data[2], /* cnt */ - pd); - - default: - printk (KERN_WARNING LOGNAME - "FX: ioctl %d not yet supported\n", - r->request); - return -(EINVAL); - } -} - -/* YSS225 initialization. - - This code was developed using DOSEMU. The Turtle Beach SETUPSND - utility was run with I/O tracing in DOSEMU enabled, and a reconstruction - of the port I/O done, using the Yamaha faxback document as a guide - to add more logic to the code. Its really pretty weird. - - There was an alternative approach of just dumping the whole I/O - sequence as a series of port/value pairs and a simple loop - that output it. However, I hope that eventually I'll get more - control over what this code does, and so I tried to stick with - a somewhat "algorithmic" approach. -*/ - -static int __init wffx_init (void) -{ - int i; - int j; - - /* Set all bits for all channels on the MOD unit to zero */ - /* XXX But why do this twice ? */ - - for (j = 0; j < 2; j++) { - for (i = 0x10; i <= 0xff; i++) { - - if (!wffx_idle ()) { - return (-1); - } - - outb (i, dev.fx_mod_addr); - outb (0x0, dev.fx_mod_data); - } - } - - if (!wffx_idle()) return (-1); - outb (0x02, dev.fx_op); /* mute on */ - - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x44, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x42, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x43, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x7c, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x7e, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x46, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x49, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x47, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x4a, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - - /* either because of stupidity by TB's programmers, or because it - actually does something, rezero the MOD page. - */ - for (i = 0x10; i <= 0xff; i++) { - - if (!wffx_idle ()) { - return (-1); - } - - outb (i, dev.fx_mod_addr); - outb (0x0, dev.fx_mod_data); - } - /* load page zero */ - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x00, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_zero); i += 2) { - outb (page_zero[i], dev.fx_dsp_msb); - outb (page_zero[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - /* Now load page one */ - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x01, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_one); i += 2) { - outb (page_one[i], dev.fx_dsp_msb); - outb (page_one[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x02, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_two); i++) { - outb (page_two[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x03, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_three); i++) { - outb (page_three[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x04, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_four); i++) { - outb (page_four[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - /* Load memory area (page six) */ - - outb (FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x06, dev.fx_dsp_page); - - for (i = 0; i < sizeof (page_six); i += 3) { - outb (page_six[i], dev.fx_dsp_addr); - outb (page_six[i+1], dev.fx_dsp_msb); - outb (page_six[i+2], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x07, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_seven); i += 2) { - outb (page_seven[i], dev.fx_dsp_msb); - outb (page_seven[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - /* Now setup the MOD area. We do this algorithmically in order to - save a little data space. It could be done in the same fashion - as the "pages". - */ - - for (i = 0x00; i <= 0x0f; i++) { - outb (0x01, dev.fx_mod_addr); - outb (i, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - outb (0x02, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0xb0; i <= 0xbf; i++) { - outb (i, dev.fx_mod_addr); - outb (0x20, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0xf0; i <= 0xff; i++) { - outb (i, dev.fx_mod_addr); - outb (0x20, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0x10; i <= 0x1d; i++) { - outb (i, dev.fx_mod_addr); - outb (0xff, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x1e, dev.fx_mod_addr); - outb (0x40, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - for (i = 0x1f; i <= 0x2d; i++) { - outb (i, dev.fx_mod_addr); - outb (0xff, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x2e, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - for (i = 0x2f; i <= 0x3e; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x3f, dev.fx_mod_addr); - outb (0x20, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - for (i = 0x40; i <= 0x4d; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x4e, dev.fx_mod_addr); - outb (0x0e, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - outb (0x4f, dev.fx_mod_addr); - outb (0x0e, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - - for (i = 0x50; i <= 0x6b; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x6c, dev.fx_mod_addr); - outb (0x40, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - outb (0x6d, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - outb (0x6e, dev.fx_mod_addr); - outb (0x40, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - outb (0x6f, dev.fx_mod_addr); - outb (0x40, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - for (i = 0x70; i <= 0x7f; i++) { - outb (i, dev.fx_mod_addr); - outb (0xc0, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0x80; i <= 0xaf; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0xc0; i <= 0xdd; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0xde, dev.fx_mod_addr); - outb (0x10, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - outb (0xdf, dev.fx_mod_addr); - outb (0x10, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - for (i = 0xe0; i <= 0xef; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0x00; i <= 0x0f; i++) { - outb (0x01, dev.fx_mod_addr); - outb (i, dev.fx_mod_data); - outb (0x02, dev.fx_mod_addr); - outb (0x01, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x02, dev.fx_op); /* mute on */ - - /* Now set the coefficients and so forth for the programs above */ - - for (i = 0; i < sizeof (coefficients); i += 4) { - outb (coefficients[i], dev.fx_dsp_page); - outb (coefficients[i+1], dev.fx_dsp_addr); - outb (coefficients[i+2], dev.fx_dsp_msb); - outb (coefficients[i+3], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - /* Some settings (?) that are too small to bundle into loops */ - - if (!wffx_idle()) return (-1); - outb (0x1e, dev.fx_mod_addr); - outb (0x14, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - outb (0xde, dev.fx_mod_addr); - outb (0x20, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - outb (0xdf, dev.fx_mod_addr); - outb (0x20, dev.fx_mod_data); - - /* some more coefficients */ - - if (!wffx_idle()) return (-1); - outb (0x06, dev.fx_dsp_page); - outb (0x78, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x40, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x03, dev.fx_dsp_addr); - outb (0x0f, dev.fx_dsp_msb); - outb (0xff, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x0b, dev.fx_dsp_addr); - outb (0x0f, dev.fx_dsp_msb); - outb (0xff, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x02, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x0a, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x46, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x49, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - - /* Now, for some strange reason, lets reload every page - and all the coefficients over again. I have *NO* idea - why this is done. I do know that no sound is produced - is this phase is omitted. - */ - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x00, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_zero_v2); i += 2) { - outb (page_zero_v2[i], dev.fx_dsp_msb); - outb (page_zero_v2[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x01, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_one_v2); i += 2) { - outb (page_one_v2[i], dev.fx_dsp_msb); - outb (page_one_v2[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - if (!wffx_idle()) return (-1); - if (!wffx_idle()) return (-1); - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x02, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_two_v2); i++) { - outb (page_two_v2[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x03, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_three_v2); i++) { - outb (page_three_v2[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x04, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_four_v2); i++) { - outb (page_four_v2[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x06, dev.fx_dsp_page); - - /* Page six v.2 is algorithmic */ - - for (i = 0x10; i <= 0x3e; i += 2) { - outb (i, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x07, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_seven_v2); i += 2) { - outb (page_seven_v2[i], dev.fx_dsp_msb); - outb (page_seven_v2[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - for (i = 0x00; i < sizeof(mod_v2); i += 2) { - outb (mod_v2[i], dev.fx_mod_addr); - outb (mod_v2[i+1], dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0; i < sizeof (coefficients2); i += 4) { - outb (coefficients2[i], dev.fx_dsp_page); - outb (coefficients2[i+1], dev.fx_dsp_addr); - outb (coefficients2[i+2], dev.fx_dsp_msb); - outb (coefficients2[i+3], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - for (i = 0; i < sizeof (coefficients3); i += 2) { - int x; - - outb (0x07, dev.fx_dsp_page); - x = (i % 4) ? 0x4e : 0x4c; - outb (x, dev.fx_dsp_addr); - outb (coefficients3[i], dev.fx_dsp_msb); - outb (coefficients3[i+1], dev.fx_dsp_lsb); - } - - outb (0x00, dev.fx_op); /* mute off */ - if (!wffx_idle()) return (-1); - - return (0); -} - -static int io = -1; -static int irq = -1; - -MODULE_AUTHOR ("Paul Barton-Davis "); -MODULE_DESCRIPTION ("Turtle Beach WaveFront Linux Driver"); -MODULE_LICENSE("GPL"); -MODULE_PARM (io,"i"); -MODULE_PARM (irq,"i"); - -static int __init init_wavfront (void) -{ - printk ("Turtle Beach WaveFront Driver\n" - "Copyright (C) by Hannu Solvainen, " - "Paul Barton-Davis 1993-1998.\n"); - - /* XXX t'would be lovely to ask the CS4232 for these values, eh ? */ - - if (io == -1 || irq == -1) { - printk (KERN_INFO LOGNAME "irq and io options must be set.\n"); - return -EINVAL; - } - - if (wavefront_interrupt_bits (irq) < 0) { - printk (KERN_INFO LOGNAME - "IRQ must be 9, 5, 12 or 15 (not %d)\n", irq); - return -ENODEV; - } - - if (detect_wavefront (irq, io) < 0) { - return -ENODEV; - } - - if (install_wavefront () < 0) { - return -EIO; - } - - return 0; -} - -static void __exit cleanup_wavfront (void) -{ - uninstall_wavefront (); -} - -module_init(init_wavfront); -module_exit(cleanup_wavfront); diff -Nru a/drivers/sound/wf_midi.c b/drivers/sound/wf_midi.c --- a/drivers/sound/wf_midi.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,880 +0,0 @@ -/* - * sound/wf_midi.c - * - * The low level driver for the WaveFront ICS2115 MIDI interface(s) - * Note that there is also an MPU-401 emulation (actually, a UART-401 - * emulation) on the CS4232 on the Tropez Plus. This code has nothing - * to do with that interface at all. - * - * The interface is essentially just a UART-401, but is has the - * interesting property of supporting what Turtle Beach called - * "Virtual MIDI" mode. In this mode, there are effectively *two* - * MIDI buses accessible via the interface, one that is routed - * solely to/from the external WaveFront synthesizer and the other - * corresponding to the pin/socket connector used to link external - * MIDI devices to the board. - * - * This driver fully supports this mode, allowing two distinct - * midi devices (/dev/midiNN and /dev/midiNN+1) to be used - * completely independently, giving 32 channels of MIDI routing, - * 16 to the WaveFront synth and 16 to the external MIDI bus. - * - * Switching between the two is accomplished externally by the driver - * using the two otherwise unused MIDI bytes. See the code for more details. - * - * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see wavefront.c) - * - * The main reason to turn off Virtual MIDI mode is when you want to - * tightly couple the WaveFront synth with an external MIDI - * device. You won't be able to distinguish the source of any MIDI - * data except via SysEx ID, but thats probably OK, since for the most - * part, the WaveFront won't be sending any MIDI data at all. - * - * The main reason to turn on Virtual MIDI Mode is to provide two - * completely independent 16-channel MIDI buses, one to the - * WaveFront and one to any external MIDI devices. Given the 32 - * voice nature of the WaveFront, its pretty easy to find a use - * for all 16 channels driving just that synth. - * - */ - -/* - * Copyright (C) by Paul Barton-Davis 1998 - * Some portions of this file are derived from work that is: - * - * CopyriGht (C) by Hannu Savolainen 1993-1996 - * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - -#include -#include "sound_config.h" - -#include - -#ifdef MODULE - -struct wf_mpu_config { - int base; -#define DATAPORT(d) (d)->base -#define COMDPORT(d) (d)->base+1 -#define STATPORT(d) (d)->base+1 - - int irq; - int opened; - int devno; - int synthno; - int mode; -#define MODE_MIDI 1 -#define MODE_SYNTH 2 - - void (*inputintr) (int dev, unsigned char data); - char isvirtual; /* do virtual I/O stuff */ -}; - -static struct wf_mpu_config devs[2]; -static struct wf_mpu_config *phys_dev = &devs[0]; -static struct wf_mpu_config *virt_dev = &devs[1]; - -static void start_uart_mode (void); - -#define OUTPUT_READY 0x40 -#define INPUT_AVAIL 0x80 -#define MPU_ACK 0xFE -#define UART_MODE_ON 0x3F - -static inline int wf_mpu_status (void) -{ - return inb (STATPORT (phys_dev)); -} - -static inline int input_avail (void) -{ - return !(wf_mpu_status() & INPUT_AVAIL); -} - -static inline int output_ready (void) -{ - return !(wf_mpu_status() & OUTPUT_READY); -} - -static inline int read_data (void) -{ - return inb (DATAPORT (phys_dev)); -} - -static inline void write_data (unsigned char byte) -{ - outb (byte, DATAPORT (phys_dev)); -} - -/* - * States for the input scanner (should be in dev_table.h) - */ - -#define MST_SYSMSG 100 /* System message (sysx etc). */ -#define MST_MTC 102 /* Midi Time Code (MTC) qframe msg */ -#define MST_SONGSEL 103 /* Song select */ -#define MST_SONGPOS 104 /* Song position pointer */ -#define MST_TIMED 105 /* Leading timing byte rcvd */ - -/* buffer space check for input scanner */ - -#define BUFTEST(mi) if (mi->m_ptr >= MI_MAX || mi->m_ptr < 0) \ -{printk(KERN_ERR "WF-MPU: Invalid buffer pointer %d/%d, s=%d\n", \ - mi->m_ptr, mi->m_left, mi->m_state);mi->m_ptr--;} - -static unsigned char len_tab[] = /* # of data bytes following a status - */ -{ - 2, /* 8x */ - 2, /* 9x */ - 2, /* Ax */ - 2, /* Bx */ - 1, /* Cx */ - 1, /* Dx */ - 2, /* Ex */ - 0 /* Fx */ -}; - -static int -wf_mpu_input_scanner (int devno, int synthdev, unsigned char midic) - -{ - struct midi_input_info *mi = &midi_devs[devno]->in_info; - - switch (mi->m_state) { - case MST_INIT: - switch (midic) { - case 0xf8: - /* Timer overflow */ - break; - - case 0xfc: - break; - - case 0xfd: - /* XXX do something useful with this. If there is - an external MIDI timer (e.g. a hardware sequencer, - a useful timer can be derived ... - - For now, no timer support. - */ - break; - - case 0xfe: - return MPU_ACK; - break; - - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - break; - - case 0xf9: - break; - - case 0xff: - mi->m_state = MST_SYSMSG; - break; - - default: - if (midic <= 0xef) { - mi->m_state = MST_TIMED; - } - else - printk (KERN_ERR " ", - midic); - } - break; - - case MST_TIMED: - { - int msg = ((int) (midic & 0xf0) >> 4); - - mi->m_state = MST_DATA; - - if (msg < 8) { /* Data byte */ - - msg = ((int) (mi->m_prev_status & 0xf0) >> 4); - msg -= 8; - mi->m_left = len_tab[msg] - 1; - - mi->m_ptr = 2; - mi->m_buf[0] = mi->m_prev_status; - mi->m_buf[1] = midic; - - if (mi->m_left <= 0) { - mi->m_state = MST_INIT; - do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); - mi->m_ptr = 0; - } - } else if (msg == 0xf) { /* MPU MARK */ - - mi->m_state = MST_INIT; - - switch (midic) { - case 0xf8: - break; - - case 0xf9: - break; - - case 0xfc: - break; - - default: - break; - } - } else { - mi->m_prev_status = midic; - msg -= 8; - mi->m_left = len_tab[msg]; - - mi->m_ptr = 1; - mi->m_buf[0] = midic; - - if (mi->m_left <= 0) { - mi->m_state = MST_INIT; - do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); - mi->m_ptr = 0; - } - } - } - break; - - case MST_SYSMSG: - switch (midic) { - case 0xf0: - mi->m_state = MST_SYSEX; - break; - - case 0xf1: - mi->m_state = MST_MTC; - break; - - case 0xf2: - mi->m_state = MST_SONGPOS; - mi->m_ptr = 0; - break; - - case 0xf3: - mi->m_state = MST_SONGSEL; - break; - - case 0xf6: - mi->m_state = MST_INIT; - - /* - * Real time messages - */ - case 0xf8: - /* midi clock */ - mi->m_state = MST_INIT; - /* XXX need ext MIDI timer support */ - break; - - case 0xfA: - mi->m_state = MST_INIT; - /* XXX need ext MIDI timer support */ - break; - - case 0xFB: - mi->m_state = MST_INIT; - /* XXX need ext MIDI timer support */ - break; - - case 0xFC: - mi->m_state = MST_INIT; - /* XXX need ext MIDI timer support */ - break; - - case 0xFE: - /* active sensing */ - mi->m_state = MST_INIT; - break; - - case 0xff: - mi->m_state = MST_INIT; - break; - - default: - printk (KERN_ERR "unknown MIDI sysmsg %0x\n", midic); - mi->m_state = MST_INIT; - } - break; - - case MST_MTC: - mi->m_state = MST_INIT; - break; - - case MST_SYSEX: - if (midic == 0xf7) { - mi->m_state = MST_INIT; - } else { - /* XXX fix me */ - } - break; - - case MST_SONGPOS: - BUFTEST (mi); - mi->m_buf[mi->m_ptr++] = midic; - if (mi->m_ptr == 2) { - mi->m_state = MST_INIT; - mi->m_ptr = 0; - /* XXX need ext MIDI timer support */ - } - break; - - case MST_DATA: - BUFTEST (mi); - mi->m_buf[mi->m_ptr++] = midic; - if ((--mi->m_left) <= 0) { - mi->m_state = MST_INIT; - do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); - mi->m_ptr = 0; - } - break; - - default: - printk (KERN_ERR "Bad state %d ", mi->m_state); - mi->m_state = MST_INIT; - } - - return 1; -} - -void -wf_mpuintr (int irq, void *dev_id, struct pt_regs *dummy) - -{ - struct wf_mpu_config *physical_dev = dev_id; - static struct wf_mpu_config *input_dev = 0; - struct midi_input_info *mi = &midi_devs[physical_dev->devno]->in_info; - int n; - - if (!input_avail()) { /* not for us */ - return; - } - - if (mi->m_busy) return; - mi->m_busy = 1; - sti (); - - if (!input_dev) { - input_dev = physical_dev; - } - - n = 50; /* XXX why ? */ - - do { - unsigned char c = read_data (); - - if (phys_dev->isvirtual) { - - if (c == WF_EXTERNAL_SWITCH) { - input_dev = virt_dev; - continue; - } else if (c == WF_INTERNAL_SWITCH) { - input_dev = phys_dev; - continue; - } /* else just leave it as it is */ - - } else { - input_dev = phys_dev; - } - - if (input_dev->mode == MODE_SYNTH) { - - wf_mpu_input_scanner (input_dev->devno, - input_dev->synthno, c); - - } else if (input_dev->opened & OPEN_READ) { - - if (input_dev->inputintr) { - input_dev->inputintr (input_dev->devno, c); - } - } - - } while (input_avail() && n-- > 0); - - mi->m_busy = 0; -} - -static int -wf_mpu_open (int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) - ) -{ - struct wf_mpu_config *devc; - - if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) - return -(ENXIO); - - if (phys_dev->devno == dev) { - devc = phys_dev; - } else if (phys_dev->isvirtual && virt_dev->devno == dev) { - devc = virt_dev; - } else { - printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); - return -(EINVAL); - } - - if (devc->opened) { - return -(EBUSY); - } - - devc->mode = MODE_MIDI; - devc->opened = mode; - devc->synthno = 0; - - devc->inputintr = input; - return 0; -} - -static void -wf_mpu_close (int dev) -{ - struct wf_mpu_config *devc; - - if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) - return; - - if (phys_dev->devno == dev) { - devc = phys_dev; - } else if (phys_dev->isvirtual && virt_dev->devno == dev) { - devc = virt_dev; - } else { - printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); - return; - } - - devc->mode = 0; - devc->inputintr = NULL; - devc->opened = 0; -} - -static int -wf_mpu_out (int dev, unsigned char midi_byte) -{ - int timeout; - unsigned long flags; - static int lastoutdev = -1; - unsigned char switchch; - - if (phys_dev->isvirtual && lastoutdev != dev) { - - if (dev == phys_dev->devno) { - switchch = WF_INTERNAL_SWITCH; - } else if (dev == virt_dev->devno) { - switchch = WF_EXTERNAL_SWITCH; - } else { - printk (KERN_ERR "WF-MPU: bad device number %d", dev); - return (0); - } - - /* XXX fix me */ - - for (timeout = 30000; timeout > 0 && !output_ready (); - timeout--); - - save_flags (flags); - cli (); - - if (!output_ready ()) { - printk (KERN_WARNING "WF-MPU: Send switch " - "byte timeout\n"); - restore_flags (flags); - return 0; - } - - write_data (switchch); - restore_flags (flags); - } - - lastoutdev = dev; - - /* - * Sometimes it takes about 30000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - /* XXX fix me */ - - for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); - - save_flags (flags); - cli (); - if (!output_ready ()) { - printk (KERN_WARNING "WF-MPU: Send data timeout\n"); - restore_flags (flags); - return 0; - } - - write_data (midi_byte); - restore_flags (flags); - - return 1; -} - -static inline int wf_mpu_start_read (int dev) { - return 0; -} - -static inline int wf_mpu_end_read (int dev) { - return 0; -} - -static int wf_mpu_ioctl (int dev, unsigned cmd, caddr_t arg) -{ - printk (KERN_WARNING - "WF-MPU: Intelligent mode not supported by hardware.\n"); - return -(EINVAL); -} - -static int wf_mpu_buffer_status (int dev) -{ - return 0; -} - -static struct synth_operations wf_mpu_synth_operations[2]; -static struct midi_operations wf_mpu_midi_operations[2]; - -static struct midi_operations wf_mpu_midi_proto = -{ - owner: THIS_MODULE, - info: {"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, - in_info: {0}, /* in_info */ - open: wf_mpu_open, - close: wf_mpu_close, - ioctl: wf_mpu_ioctl, - outputc: wf_mpu_out, - start_read: wf_mpu_start_read, - end_read: wf_mpu_end_read, - buffer_status: wf_mpu_buffer_status, -}; - -static struct synth_info wf_mpu_synth_info_proto = -{"WaveFront MPU-401 interface", 0, - SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT}; - -static struct synth_info wf_mpu_synth_info[2]; - -static int -wf_mpu_synth_ioctl (int dev, - unsigned int cmd, caddr_t arg) -{ - int midi_dev; - int index; - - midi_dev = synth_devs[dev]->midi_dev; - - if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) - return -(ENXIO); - - if (midi_dev == phys_dev->devno) { - index = 0; - } else if (phys_dev->isvirtual && midi_dev == virt_dev->devno) { - index = 1; - } else { - return -(EINVAL); - } - - switch (cmd) { - - case SNDCTL_SYNTH_INFO: - if(copy_to_user (&((char *) arg)[0], - &wf_mpu_synth_info[index], - sizeof (struct synth_info))) - return -EFAULT; - return 0; - - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - - default: - return -EINVAL; - } -} - -static int -wf_mpu_synth_open (int dev, int mode) -{ - int midi_dev; - struct wf_mpu_config *devc; - - midi_dev = synth_devs[dev]->midi_dev; - - if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) { - return -(ENXIO); - } - - if (phys_dev->devno == midi_dev) { - devc = phys_dev; - } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { - devc = virt_dev; - } else { - printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); - return -(EINVAL); - } - - if (devc->opened) { - return -(EBUSY); - } - - devc->mode = MODE_SYNTH; - devc->synthno = dev; - devc->opened = mode; - devc->inputintr = NULL; - return 0; -} - -static void -wf_mpu_synth_close (int dev) -{ - int midi_dev; - struct wf_mpu_config *devc; - - midi_dev = synth_devs[dev]->midi_dev; - - if (phys_dev->devno == midi_dev) { - devc = phys_dev; - } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { - devc = virt_dev; - } else { - printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); - return; - } - - devc->inputintr = NULL; - devc->opened = 0; - devc->mode = 0; -} - -#define _MIDI_SYNTH_C_ -#define MIDI_SYNTH_NAME "WaveFront (MIDI)" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static struct synth_operations wf_mpu_synth_proto = -{ - owner: THIS_MODULE, - id: "WaveFront (ICS2115)", - info: NULL, /* info field, filled in during configuration */ - midi_dev: 0, /* MIDI dev XXX should this be -1 ? */ - synth_type: SYNTH_TYPE_MIDI, - synth_subtype: SAMPLE_TYPE_WAVEFRONT, - open: wf_mpu_synth_open, - close: wf_mpu_synth_close, - ioctl: wf_mpu_synth_ioctl, - kill_note: midi_synth_kill_note, - start_note: midi_synth_start_note, - set_instr: midi_synth_set_instr, - reset: midi_synth_reset, - hw_control: midi_synth_hw_control, - load_patch: midi_synth_load_patch, - aftertouch: midi_synth_aftertouch, - controller: midi_synth_controller, - panning: midi_synth_panning, - bender: midi_synth_bender, - setup_voice: midi_synth_setup_voice, - send_sysex: midi_synth_send_sysex -}; - -static int -config_wf_mpu (struct wf_mpu_config *dev) - -{ - int is_external; - char *name; - int index; - - if (dev == phys_dev) { - name = "WaveFront internal MIDI"; - is_external = 0; - index = 0; - memcpy ((char *) &wf_mpu_synth_operations[index], - (char *) &wf_mpu_synth_proto, - sizeof (struct synth_operations)); - } else { - name = "WaveFront external MIDI"; - is_external = 1; - index = 1; - /* no synth operations for an external MIDI interface */ - } - - memcpy ((char *) &wf_mpu_synth_info[dev->devno], - (char *) &wf_mpu_synth_info_proto, - sizeof (struct synth_info)); - - strcpy (wf_mpu_synth_info[index].name, name); - - wf_mpu_synth_operations[index].midi_dev = dev->devno; - wf_mpu_synth_operations[index].info = &wf_mpu_synth_info[index]; - - memcpy ((char *) &wf_mpu_midi_operations[index], - (char *) &wf_mpu_midi_proto, - sizeof (struct midi_operations)); - - if (is_external) { - wf_mpu_midi_operations[index].converter = NULL; - } else { - wf_mpu_midi_operations[index].converter = - &wf_mpu_synth_operations[index]; - } - - strcpy (wf_mpu_midi_operations[index].info.name, name); - - midi_devs[dev->devno] = &wf_mpu_midi_operations[index]; - midi_devs[dev->devno]->in_info.m_busy = 0; - midi_devs[dev->devno]->in_info.m_state = MST_INIT; - midi_devs[dev->devno]->in_info.m_ptr = 0; - midi_devs[dev->devno]->in_info.m_left = 0; - midi_devs[dev->devno]->in_info.m_prev_status = 0; - - devs[index].opened = 0; - devs[index].mode = 0; - - return (0); -} - -int virtual_midi_enable (void) - -{ - if ((virt_dev->devno < 0) && - (virt_dev->devno = sound_alloc_mididev()) == -1) { - printk (KERN_ERR - "WF-MPU: too many midi devices detected\n"); - return -1; - } - - config_wf_mpu (virt_dev); - - phys_dev->isvirtual = 1; - return virt_dev->devno; -} - -int -virtual_midi_disable (void) - -{ - unsigned long flags; - - save_flags (flags); - cli(); - - wf_mpu_close (virt_dev->devno); - /* no synth on virt_dev, so no need to call wf_mpu_synth_close() */ - phys_dev->isvirtual = 0; - - restore_flags (flags); - - return 0; -} - -int __init detect_wf_mpu (int irq, int io_base) -{ - if (check_region (io_base, 2)) { - printk (KERN_WARNING "WF-MPU: I/O port %x already in use.\n", - io_base); - return -1; - } - - phys_dev->base = io_base; - phys_dev->irq = irq; - phys_dev->devno = -1; - virt_dev->devno = -1; - - return 0; -} - -int __init install_wf_mpu (void) -{ - if ((phys_dev->devno = sound_alloc_mididev()) < 0){ - - printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n"); - return -1; - - } - - request_region (phys_dev->base, 2, "wavefront midi"); - phys_dev->isvirtual = 0; - - if (config_wf_mpu (phys_dev)) { - - printk (KERN_WARNING - "WF-MPU: configuration for MIDI device %d failed\n", - phys_dev->devno); - sound_unload_mididev (phys_dev->devno); - - } - - /* OK, now we're configured to handle an interrupt ... */ - - if (request_irq (phys_dev->irq, wf_mpuintr, SA_INTERRUPT|SA_SHIRQ, - "wavefront midi", phys_dev) < 0) { - - printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n", - phys_dev->irq); - return -1; - - } - - /* This being a WaveFront (ICS-2115) emulated MPU-401, we have - to switch it into UART (dumb) mode, because otherwise, it - won't do anything at all. - */ - - start_uart_mode (); - - return phys_dev->devno; -} - -void -uninstall_wf_mpu (void) - -{ - release_region (phys_dev->base, 2); - free_irq (phys_dev->irq, phys_dev); - sound_unload_mididev (phys_dev->devno); - - if (virt_dev->devno >= 0) { - sound_unload_mididev (virt_dev->devno); - } -} - -static void -start_uart_mode (void) - -{ - int ok, i; - unsigned long flags; - - save_flags (flags); - cli (); - - /* XXX fix me */ - - for (i = 0; i < 30000 && !output_ready (); i++); - - outb (UART_MODE_ON, COMDPORT(phys_dev)); - - for (ok = 0, i = 50000; i > 0 && !ok; i--) { - if (input_avail ()) { - if (read_data () == MPU_ACK) { - ok = 1; - } - } - } - - restore_flags (flags); -} -#endif diff -Nru a/drivers/sound/ymfpci.c b/drivers/sound/ymfpci.c --- a/drivers/sound/ymfpci.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2671 +0,0 @@ -/* - * Copyright 1999 Jaroslav Kysela - * Copyright 2000 Alan Cox - * Copyright 2001 Kai Germaschewski - * Copyright 2002 Pete Zaitcev - * - * Yamaha YMF7xx driver. - * - * This code is a result of high-speed collision - * between ymfpci.c of ALSA and cs46xx.c of Linux. - * -- Pete Zaitcev ; 2000/09/18 - * - * 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. - * - * TODO: - * - Use P44Slot for 44.1 playback (beware of idle buzzing in P44Slot). - * - 96KHz playback for DVD - use pitch of 2.0. - * - Retain DMA buffer on close, do not wait the end of frame. - * - Resolve XXX tagged questions. - * - Cannot play 5133Hz. - * - 2001/01/07 Consider if we can remove voice_lock, like so: - * : Allocate/deallocate voices in open/close under semafore. - * : We access voices in interrupt, that only for pcms that open. - * voice_lock around playback_prepare closes interrupts for insane duration. - * - Revisit the way voice_alloc is done - too confusing, overcomplicated. - * Should support various channel types, however. - * - Remove prog_dmabuf from read/write, leave it in open. - * - 2001/01/07 Replace the OPL3 part of CONFIG_SOUND_YMFPCI_LEGACY code with - * native synthesizer through a playback slot. - * - 2001/11/29 ac97_save_state - * Talk to Kai to remove ac97_save_state before it's too late! - * - Second AC97 - * - Restore S/PDIF - Toshibas have it. - * - * Kai used pci_alloc_consistent for DMA buffer, which sounds a little - * unconventional. However, given how small our fragments can be, - * a little uncached access is perhaps better than endless flushing. - * On i386 and other I/O-coherent architectures pci_alloc_consistent - * is entirely harmless. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY -# include "sound_config.h" -# include "mpu401.h" -#endif -#include "ymfpci.h" - -/* - * I do not believe in debug levels as I never can guess what - * part of the code is going to be problematic in the future. - * Don't forget to run your klogd with -c 8. - * - * Example (do not remove): - * #define YMFDBG(fmt, arg...) do{ printk(KERN_DEBUG fmt, ##arg); }while(0) - */ -#define YMFDBGW(fmt, arg...) /* */ /* write counts */ -#define YMFDBGI(fmt, arg...) /* */ /* interrupts */ -#define YMFDBGX(fmt, arg...) /* */ /* ioctl */ - -static int ymf_playback_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); -static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); -static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice); -static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank); -static int ymf_playback_prepare(struct ymf_state *state); -static int ymf_capture_prepare(struct ymf_state *state); -static struct ymf_state *ymf_state_alloc(ymfpci_t *unit); - -static void ymfpci_aclink_reset(struct pci_dev * pci); -static void ymfpci_disable_dsp(ymfpci_t *unit); -static void ymfpci_download_image(ymfpci_t *codec); -static void ymf_memload(ymfpci_t *unit); - -static LIST_HEAD(ymf_devs); - -/* - * constants - */ - -static struct pci_device_id ymf_id_tbl[] __devinitdata = { -#define DEV(v, d, data) \ - { PCI_VENDOR_ID_##v, PCI_DEVICE_ID_##v##_##d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)data } - DEV (YAMAHA, 724, "YMF724"), - DEV (YAMAHA, 724F, "YMF724F"), - DEV (YAMAHA, 740, "YMF740"), - DEV (YAMAHA, 740C, "YMF740C"), - DEV (YAMAHA, 744, "YMF744"), - DEV (YAMAHA, 754, "YMF754"), -#undef DEV - { } -}; -MODULE_DEVICE_TABLE(pci, ymf_id_tbl); - -/* - * common I/O routines - */ - -static inline u8 ymfpci_readb(ymfpci_t *codec, u32 offset) -{ - return readb(codec->reg_area_virt + offset); -} - -static inline void ymfpci_writeb(ymfpci_t *codec, u32 offset, u8 val) -{ - writeb(val, codec->reg_area_virt + offset); -} - -static inline u16 ymfpci_readw(ymfpci_t *codec, u32 offset) -{ - return readw(codec->reg_area_virt + offset); -} - -static inline void ymfpci_writew(ymfpci_t *codec, u32 offset, u16 val) -{ - writew(val, codec->reg_area_virt + offset); -} - -static inline u32 ymfpci_readl(ymfpci_t *codec, u32 offset) -{ - return readl(codec->reg_area_virt + offset); -} - -static inline void ymfpci_writel(ymfpci_t *codec, u32 offset, u32 val) -{ - writel(val, codec->reg_area_virt + offset); -} - -static int ymfpci_codec_ready(ymfpci_t *codec, int secondary, int sched) -{ - signed long end_time; - u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; - - end_time = jiffies + 3 * (HZ / 4); - do { - if ((ymfpci_readw(codec, reg) & 0x8000) == 0) - return 0; - if (sched) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } - } while (end_time - (signed long)jiffies >= 0); - printk(KERN_ERR "ymfpci_codec_ready: codec %i is not ready [0x%x]\n", - secondary, ymfpci_readw(codec, reg)); - return -EBUSY; -} - -static void ymfpci_codec_write(struct ac97_codec *dev, u8 reg, u16 val) -{ - ymfpci_t *codec = dev->private_data; - u32 cmd; - - /* 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); -} - -static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg) -{ - ymfpci_t *unit = dev->private_data; - int i; - - if (ymfpci_codec_ready(unit, 0, 0)) - return ~0; - ymfpci_writew(unit, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); - if (ymfpci_codec_ready(unit, 0, 0)) - return ~0; - if (unit->pci->device == PCI_DEVICE_ID_YAMAHA_744 && unit->rev < 2) { - for (i = 0; i < 600; i++) - ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); - } - return ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); -} - -/* - * Misc routines - */ - -/* - * Calculate the actual sampling rate relatetively to the base clock (48kHz). - */ -static u32 ymfpci_calc_delta(u32 rate) -{ - switch (rate) { - case 8000: return 0x02aaab00; - case 11025: return 0x03accd00; - case 16000: return 0x05555500; - case 22050: return 0x07599a00; - case 32000: return 0x0aaaab00; - case 44100: return 0x0eb33300; - default: return ((rate << 16) / 48000) << 12; - } -} - -static u32 def_rate[8] = { - 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 -}; - -static u32 ymfpci_calc_lpfK(u32 rate) -{ - u32 i; - static u32 val[8] = { - 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000, - 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000 - }; - - if (rate == 44100) - return 0x40000000; /* FIXME: What's the right value? */ - for (i = 0; i < 8; i++) - if (rate <= def_rate[i]) - return val[i]; - return val[0]; -} - -static u32 ymfpci_calc_lpfQ(u32 rate) -{ - u32 i; - static u32 val[8] = { - 0x35280000, 0x34A70000, 0x32020000, 0x31770000, - 0x31390000, 0x31C90000, 0x33D00000, 0x40000000 - }; - - if (rate == 44100) - return 0x370A0000; - for (i = 0; i < 8; i++) - if (rate <= def_rate[i]) - return val[i]; - return val[0]; -} - -static u32 ymf_calc_lend(u32 rate) -{ - return (rate * YMF_SAMPF) / 48000; -} - -/* - * We ever allow only a few formats, but let's be generic, for smaller surprise. - */ -static int ymf_pcm_format_width(int format) -{ - static int mask16 = AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE; - - if ((format & (format-1)) != 0) { - printk(KERN_ERR "ymfpci: format 0x%x is not a power of 2\n", format); - return 8; - } - - if (format == AFMT_IMA_ADPCM) return 4; - if ((format & mask16) != 0) return 16; - return 8; -} - -static void ymf_pcm_update_shift(struct ymf_pcm_format *f) -{ - f->shift = 0; - if (f->voices == 2) - f->shift++; - if (ymf_pcm_format_width(f->format) == 16) - f->shift++; -} - -/* Are you sure 32K is not too much? See if mpg123 skips on loaded systems. */ -#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -/* - * Allocate DMA buffer - */ -static int alloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) -{ - void *rawbuf = NULL; - dma_addr_t dma_addr; - int order; - struct page *map, *mapend; - - /* alloc as big a chunk as we can */ - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { - rawbuf = pci_alloc_consistent(unit->pci, PAGE_SIZE << order, &dma_addr); - if (rawbuf) - break; - } - if (!rawbuf) - return -ENOMEM; - -#if 0 - printk(KERN_DEBUG "ymfpci: allocated %ld (order = %d) bytes at %p\n", - PAGE_SIZE << order, order, rawbuf); -#endif - - dmabuf->ready = dmabuf->mapped = 0; - dmabuf->rawbuf = rawbuf; - dmabuf->dma_addr = dma_addr; - dmabuf->buforder = order; - - /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ - mapend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); - for (map = virt_to_page(rawbuf); map <= mapend; map++) - set_bit(PG_reserved, &map->flags); - - return 0; -} - -/* - * Free DMA buffer - */ -static void dealloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) -{ - struct page *map, *mapend; - - if (dmabuf->rawbuf) { - /* undo marking the pages as reserved */ - mapend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); - for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) - clear_bit(PG_reserved, &map->flags); - - pci_free_consistent(unit->pci, PAGE_SIZE << dmabuf->buforder, - dmabuf->rawbuf, dmabuf->dma_addr); - } - dmabuf->rawbuf = NULL; - dmabuf->mapped = dmabuf->ready = 0; -} - -static int prog_dmabuf(struct ymf_state *state, int rec) -{ - struct ymf_dmabuf *dmabuf; - int w_16; - unsigned bufsize; - unsigned long flags; - int redzone, redfrags; - int ret; - - w_16 = ymf_pcm_format_width(state->format.format) == 16; - dmabuf = rec ? &state->rpcm.dmabuf : &state->wpcm.dmabuf; - - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->hwptr = dmabuf->swptr = 0; - dmabuf->total_bytes = 0; - dmabuf->count = 0; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - - /* allocate DMA buffer if not allocated yet */ - if (!dmabuf->rawbuf) - if ((ret = alloc_dmabuf(state->unit, dmabuf))) - return ret; - - /* - * Create fake fragment sizes and numbers for OSS ioctls. - * Import what Doom might have set with SNDCTL_DSP_SETFRAGMENT. - */ - bufsize = PAGE_SIZE << dmabuf->buforder; - /* By default we give 4 big buffers. */ - dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT - 2); - if (dmabuf->ossfragshift > 3 && - dmabuf->ossfragshift < dmabuf->fragshift) { - /* If OSS set smaller fragments, give more smaller buffers. */ - dmabuf->fragshift = dmabuf->ossfragshift; - } - dmabuf->fragsize = 1 << dmabuf->fragshift; - - dmabuf->numfrag = bufsize >> dmabuf->fragshift; - dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; - - if (dmabuf->ossmaxfrags >= 2) { - redzone = ymf_calc_lend(state->format.rate); - redzone <<= state->format.shift; - redzone *= 3; - redfrags = (redzone + dmabuf->fragsize-1) >> dmabuf->fragshift; - - if (dmabuf->ossmaxfrags + redfrags < dmabuf->numfrag) { - dmabuf->numfrag = dmabuf->ossmaxfrags + redfrags; - dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; - } - } - - memset(dmabuf->rawbuf, w_16 ? 0 : 0x80, dmabuf->dmasize); - - /* - * Now set up the ring - */ - - /* XXX ret = rec? cap_pre(): pbk_pre(); */ - spin_lock_irqsave(&state->unit->voice_lock, flags); - if (rec) { - if ((ret = ymf_capture_prepare(state)) != 0) { - spin_unlock_irqrestore(&state->unit->voice_lock, flags); - return ret; - } - } else { - if ((ret = ymf_playback_prepare(state)) != 0) { - spin_unlock_irqrestore(&state->unit->voice_lock, flags); - return ret; - } - } - spin_unlock_irqrestore(&state->unit->voice_lock, flags); - - /* set the ready flag for the dma buffer (this comment is not stupid) */ - dmabuf->ready = 1; - -#if 0 - printk(KERN_DEBUG "prog_dmabuf: rate %d format 0x%x," - " numfrag %d fragsize %d dmasize %d\n", - state->format.rate, state->format.format, dmabuf->numfrag, - dmabuf->fragsize, dmabuf->dmasize); -#endif - - return 0; -} - -static void ymf_start_dac(struct ymf_state *state) -{ - ymf_playback_trigger(state->unit, &state->wpcm, 1); -} - -// static void ymf_start_adc(struct ymf_state *state) -// { -// ymf_capture_trigger(state->unit, &state->rpcm, 1); -// } - -/* - * Wait until output is drained. - * This does not kill the hardware for the sake of ioctls. - */ -static void ymf_wait_dac(struct ymf_state *state) -{ - struct ymf_unit *unit = state->unit; - struct ymf_pcm *ypcm = &state->wpcm; - DECLARE_WAITQUEUE(waita, current); - unsigned long flags; - - add_wait_queue(&ypcm->dmabuf.wait, &waita); - - spin_lock_irqsave(&unit->reg_lock, flags); - if (ypcm->dmabuf.count != 0 && !ypcm->running) { - ymf_playback_trigger(unit, ypcm, 1); - } - -#if 0 - if (file->f_flags & O_NONBLOCK) { - /* - * XXX Our mistake is to attach DMA buffer to state - * rather than to some per-device structure. - * Cannot skip waiting, can only make it shorter. - */ - } -#endif - - set_current_state(TASK_UNINTERRUPTIBLE); - while (ypcm->running) { - spin_unlock_irqrestore(&unit->reg_lock, flags); - schedule(); - spin_lock_irqsave(&unit->reg_lock, flags); - set_current_state(TASK_UNINTERRUPTIBLE); - } - spin_unlock_irqrestore(&unit->reg_lock, flags); - - set_current_state(TASK_RUNNING); - remove_wait_queue(&ypcm->dmabuf.wait, &waita); - - /* - * This function may take up to 4 seconds to reach this point - * (32K circular buffer, 8000 Hz). User notices. - */ -} - -/* Can just stop, without wait. Or can we? */ -static void ymf_stop_adc(struct ymf_state *state) -{ - struct ymf_unit *unit = state->unit; - unsigned long flags; - - spin_lock_irqsave(&unit->reg_lock, flags); - ymf_capture_trigger(unit, &state->rpcm, 0); - spin_unlock_irqrestore(&unit->reg_lock, flags); -} - -/* - * Hardware start management - */ - -static void ymfpci_hw_start(ymfpci_t *unit) -{ - unsigned long flags; - - spin_lock_irqsave(&unit->reg_lock, flags); - if (unit->start_count++ == 0) { - ymfpci_writel(unit, YDSXGR_MODE, - ymfpci_readl(unit, YDSXGR_MODE) | 3); - unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; - } - spin_unlock_irqrestore(&unit->reg_lock, flags); -} - -static void ymfpci_hw_stop(ymfpci_t *unit) -{ - unsigned long flags; - long timeout = 1000; - - spin_lock_irqsave(&unit->reg_lock, flags); - if (--unit->start_count == 0) { - ymfpci_writel(unit, YDSXGR_MODE, - ymfpci_readl(unit, YDSXGR_MODE) & ~3); - while (timeout-- > 0) { - if ((ymfpci_readl(unit, YDSXGR_STATUS) & 2) == 0) - break; - } - } - spin_unlock_irqrestore(&unit->reg_lock, flags); -} - -/* - * Playback voice management - */ - -static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t *rvoice[]) -{ - ymfpci_voice_t *voice, *voice2; - int idx; - - for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) { - voice = &codec->voices[idx]; - voice2 = pair ? &codec->voices[idx+1] : NULL; - if (voice->use || (voice2 && voice2->use)) - continue; - voice->use = 1; - if (voice2) - voice2->use = 1; - switch (type) { - case YMFPCI_PCM: - voice->pcm = 1; - if (voice2) - voice2->pcm = 1; - break; - case YMFPCI_SYNTH: - voice->synth = 1; - break; - case YMFPCI_MIDI: - voice->midi = 1; - break; - } - ymfpci_hw_start(codec); - rvoice[0] = voice; - if (voice2) { - ymfpci_hw_start(codec); - rvoice[1] = voice2; - } - return 0; - } - return -EBUSY; /* Your audio channel is open by someone else. */ -} - -static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice) -{ - ymfpci_hw_stop(unit); - pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; - pvoice->ypcm = NULL; -} - -/* - */ - -static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) -{ - struct ymf_pcm *ypcm; - int redzone; - int pos, delta, swptr; - int played, distance; - struct ymf_state *state; - struct ymf_dmabuf *dmabuf; - char silence; - - if ((ypcm = voice->ypcm) == NULL) { - return; - } - if ((state = ypcm->state) == NULL) { - ypcm->running = 0; // lock it - return; - } - dmabuf = &ypcm->dmabuf; - spin_lock(&codec->reg_lock); - if (ypcm->running) { - YMFDBGI("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n", - voice->number, codec->active_bank, dmabuf->count, - le32_to_cpu(voice->bank[0].start), - le32_to_cpu(voice->bank[1].start)); - silence = (ymf_pcm_format_width(state->format.format) == 16) ? - 0 : 0x80; - /* We need actual left-hand-side redzone size here. */ - redzone = ymf_calc_lend(state->format.rate); - redzone <<= (state->format.shift + 1); - swptr = dmabuf->swptr; - - pos = le32_to_cpu(voice->bank[codec->active_bank].start); - pos <<= state->format.shift; - if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ - printk(KERN_ERR "ymfpci%d: runaway voice %d: hwptr %d=>%d dmasize %d\n", - codec->dev_audio, voice->number, - dmabuf->hwptr, pos, dmabuf->dmasize); - pos = 0; - } - if (pos < dmabuf->hwptr) { - delta = dmabuf->dmasize - dmabuf->hwptr; - memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); - delta += pos; - memset(dmabuf->rawbuf, silence, pos); - } else { - delta = pos - dmabuf->hwptr; - memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); - } - dmabuf->hwptr = pos; - - if (dmabuf->count == 0) { - printk(KERN_ERR "ymfpci%d: %d: strain: hwptr %d\n", - codec->dev_audio, voice->number, dmabuf->hwptr); - ymf_playback_trigger(codec, ypcm, 0); - } - - if (swptr <= pos) { - distance = pos - swptr; - } else { - distance = dmabuf->dmasize - (swptr - pos); - } - if (distance < redzone) { - /* - * hwptr inside redzone => DMA ran out of samples. - */ - if (delta < dmabuf->count) { - /* - * Lost interrupt or other screwage. - */ - printk(KERN_ERR "ymfpci%d: %d: lost: delta %d" - " hwptr %d swptr %d distance %d count %d\n", - codec->dev_audio, voice->number, delta, - dmabuf->hwptr, swptr, distance, dmabuf->count); - } else { - /* - * Normal end of DMA. - */ - YMFDBGI("ymfpci%d: %d: done: delta %d" - " hwptr %d swptr %d distance %d count %d\n", - codec->dev_audio, voice->number, delta, - dmabuf->hwptr, swptr, distance, dmabuf->count); - } - played = dmabuf->count; - if (ypcm->running) { - ymf_playback_trigger(codec, ypcm, 0); - } - } else { - /* - * hwptr is chipping away towards a remote swptr. - * Calculate other distance and apply it to count. - */ - if (swptr >= pos) { - distance = swptr - pos; - } else { - distance = dmabuf->dmasize - (pos - swptr); - } - if (distance < dmabuf->count) { - played = dmabuf->count - distance; - } else { - played = 0; - } - } - - dmabuf->total_bytes += played; - dmabuf->count -= played; - if (dmabuf->count < dmabuf->dmasize / 2) { - wake_up(&dmabuf->wait); - } - } - spin_unlock(&codec->reg_lock); -} - -static void ymf_cap_interrupt(ymfpci_t *unit, struct ymf_capture *cap) -{ - struct ymf_pcm *ypcm; - int redzone; - struct ymf_state *state; - struct ymf_dmabuf *dmabuf; - int pos, delta; - int cnt; - - if ((ypcm = cap->ypcm) == NULL) { - return; - } - if ((state = ypcm->state) == NULL) { - ypcm->running = 0; // lock it - return; - } - dmabuf = &ypcm->dmabuf; - spin_lock(&unit->reg_lock); - if (ypcm->running) { - redzone = ymf_calc_lend(state->format.rate); - redzone <<= (state->format.shift + 1); - - pos = le32_to_cpu(cap->bank[unit->active_bank].start); - // pos <<= state->format.shift; - if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ - printk(KERN_ERR "ymfpci%d: runaway capture %d: hwptr %d=>%d dmasize %d\n", - unit->dev_audio, ypcm->capture_bank_number, - dmabuf->hwptr, pos, dmabuf->dmasize); - pos = 0; - } - if (pos < dmabuf->hwptr) { - delta = dmabuf->dmasize - dmabuf->hwptr; - delta += pos; - } else { - delta = pos - dmabuf->hwptr; - } - dmabuf->hwptr = pos; - - cnt = dmabuf->count; - cnt += delta; - if (cnt + redzone > dmabuf->dmasize) { - /* Overflow - bump swptr */ - dmabuf->count = dmabuf->dmasize - redzone; - dmabuf->swptr = dmabuf->hwptr + redzone; - if (dmabuf->swptr >= dmabuf->dmasize) { - dmabuf->swptr -= dmabuf->dmasize; - } - } else { - dmabuf->count = cnt; - } - - dmabuf->total_bytes += delta; - if (dmabuf->count) { /* && is_sleeping XXX */ - wake_up(&dmabuf->wait); - } - } - spin_unlock(&unit->reg_lock); -} - -static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) -{ - - if (ypcm->voices[0] == NULL) { - return -EINVAL; - } - if (cmd != 0) { - codec->ctrl_playback[ypcm->voices[0]->number + 1] = - cpu_to_le32(ypcm->voices[0]->bank_ba); - if (ypcm->voices[1] != NULL) - codec->ctrl_playback[ypcm->voices[1]->number + 1] = - cpu_to_le32(ypcm->voices[1]->bank_ba); - ypcm->running = 1; - } else { - codec->ctrl_playback[ypcm->voices[0]->number + 1] = 0; - if (ypcm->voices[1] != NULL) - codec->ctrl_playback[ypcm->voices[1]->number + 1] = 0; - ypcm->running = 0; - } - return 0; -} - -static void ymf_capture_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) -{ - u32 tmp; - - if (cmd != 0) { - tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); - ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); - ypcm->running = 1; - } else { - tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); - ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); - ypcm->running = 0; - } -} - -static int ymfpci_pcm_voice_alloc(struct ymf_pcm *ypcm, int voices) -{ - struct ymf_unit *unit; - int err; - - unit = ypcm->state->unit; - if (ypcm->voices[1] != NULL && voices < 2) { - ymfpci_voice_free(unit, ypcm->voices[1]); - ypcm->voices[1] = NULL; - } - if (voices == 1 && ypcm->voices[0] != NULL) - return 0; /* already allocated */ - if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) - return 0; /* already allocated */ - if (voices > 1) { - if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { - ymfpci_voice_free(unit, ypcm->voices[0]); - ypcm->voices[0] = NULL; - } - if ((err = voice_alloc(unit, YMFPCI_PCM, 1, ypcm->voices)) < 0) - return err; - ypcm->voices[0]->ypcm = ypcm; - ypcm->voices[1]->ypcm = ypcm; - } else { - if ((err = voice_alloc(unit, YMFPCI_PCM, 0, ypcm->voices)) < 0) - return err; - ypcm->voices[0]->ypcm = ypcm; - } - return 0; -} - -static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo, - int rate, int w_16, unsigned long addr, unsigned int end, int spdif) -{ - u32 format; - u32 delta = ymfpci_calc_delta(rate); - u32 lpfQ = ymfpci_calc_lpfQ(rate); - u32 lpfK = ymfpci_calc_lpfK(rate); - ymfpci_playback_bank_t *bank; - int nbank; - - /* - * The gain is a floating point number. According to the manual, - * bit 31 indicates a sign bit, bit 30 indicates an integer part, - * and bits [29:15] indicate a decimal fraction part. Thus, - * for a gain of 1.0 the constant of 0x40000000 is loaded. - */ - unsigned default_gain = cpu_to_le32(0x40000000); - - format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); - if (stereo) - end >>= 1; - if (w_16) - end >>= 1; - for (nbank = 0; nbank < 2; nbank++) { - bank = &voice->bank[nbank]; - bank->format = cpu_to_le32(format); - bank->loop_default = 0; /* 0-loops forever, otherwise count */ - bank->base = cpu_to_le32(addr); - bank->loop_start = 0; - bank->loop_end = cpu_to_le32(end); - bank->loop_frac = 0; - bank->eg_gain_end = default_gain; - bank->lpfQ = cpu_to_le32(lpfQ); - bank->status = 0; - bank->num_of_frames = 0; - bank->loop_count = 0; - bank->start = 0; - bank->start_frac = 0; - bank->delta = - bank->delta_end = cpu_to_le32(delta); - bank->lpfK = - bank->lpfK_end = cpu_to_le32(lpfK); - bank->eg_gain = default_gain; - bank->lpfD1 = - bank->lpfD2 = 0; - - bank->left_gain = - bank->right_gain = - bank->left_gain_end = - bank->right_gain_end = - bank->eff1_gain = - bank->eff2_gain = - bank->eff3_gain = - bank->eff1_gain_end = - bank->eff2_gain_end = - bank->eff3_gain_end = 0; - - if (!stereo) { - if (!spdif) { - bank->left_gain = - bank->right_gain = - bank->left_gain_end = - bank->right_gain_end = default_gain; - } else { - bank->eff2_gain = - bank->eff2_gain_end = - bank->eff3_gain = - bank->eff3_gain_end = default_gain; - } - } else { - if (!spdif) { - if ((voice->number & 1) == 0) { - bank->left_gain = - bank->left_gain_end = default_gain; - } else { - bank->format |= cpu_to_le32(1); - bank->right_gain = - bank->right_gain_end = default_gain; - } - } else { - if ((voice->number & 1) == 0) { - bank->eff2_gain = - bank->eff2_gain_end = default_gain; - } else { - bank->format |= cpu_to_le32(1); - bank->eff3_gain = - bank->eff3_gain_end = default_gain; - } - } - } - } -} - -/* - * XXX Capture channel allocation is entirely fake at the moment. - * We use only one channel and mark it busy as required. - */ -static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank) -{ - struct ymf_capture *cap; - int cbank; - - cbank = 1; /* Only ADC slot is used for now. */ - cap = &unit->capture[cbank]; - if (cap->use) - return -EBUSY; - cap->use = 1; - *pbank = cbank; - return 0; -} - -static int ymf_playback_prepare(struct ymf_state *state) -{ - struct ymf_pcm *ypcm = &state->wpcm; - int err, nvoice; - - if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) { - /* Somebody started 32 mpg123's in parallel? */ - printk(KERN_INFO "ymfpci%d: cannot allocate voice\n", - state->unit->dev_audio); - return err; - } - - for (nvoice = 0; nvoice < state->format.voices; nvoice++) { - ymf_pcm_init_voice(ypcm->voices[nvoice], - state->format.voices == 2, state->format.rate, - ymf_pcm_format_width(state->format.format) == 16, - ypcm->dmabuf.dma_addr, ypcm->dmabuf.dmasize, - ypcm->spdif); - } - return 0; -} - -static int ymf_capture_prepare(struct ymf_state *state) -{ - ymfpci_t *unit = state->unit; - struct ymf_pcm *ypcm = &state->rpcm; - ymfpci_capture_bank_t * bank; - /* XXX This is confusing, gotta rename one of them banks... */ - int nbank; /* flip-flop bank */ - int cbank; /* input [super-]bank */ - struct ymf_capture *cap; - u32 rate, format; - - if (ypcm->capture_bank_number == -1) { - if (ymf_capture_alloc(unit, &cbank) != 0) - return -EBUSY; - - ypcm->capture_bank_number = cbank; - - cap = &unit->capture[cbank]; - cap->bank = unit->bank_capture[cbank][0]; - cap->ypcm = ypcm; - ymfpci_hw_start(unit); - } - - // ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream); - // frag_size is replaced with nonfragged byte-aligned rolling buffer - rate = ((48000 * 4096) / state->format.rate) - 1; - format = 0; - if (state->format.voices == 2) - format |= 2; - if (ymf_pcm_format_width(state->format.format) == 8) - format |= 1; - switch (ypcm->capture_bank_number) { - case 0: - ymfpci_writel(unit, YDSXGR_RECFORMAT, format); - ymfpci_writel(unit, YDSXGR_RECSLOTSR, rate); - break; - case 1: - ymfpci_writel(unit, YDSXGR_ADCFORMAT, format); - ymfpci_writel(unit, YDSXGR_ADCSLOTSR, rate); - break; - } - for (nbank = 0; nbank < 2; nbank++) { - bank = unit->bank_capture[ypcm->capture_bank_number][nbank]; - bank->base = cpu_to_le32(ypcm->dmabuf.dma_addr); - // bank->loop_end = ypcm->dmabuf.dmasize >> state->format.shift; - bank->loop_end = cpu_to_le32(ypcm->dmabuf.dmasize); - bank->start = 0; - bank->num_of_loops = 0; - } -#if 0 /* s/pdif */ - if (state->digital.dig_valid) - /*state->digital.type == SND_PCM_DIG_AES_IEC958*/ - ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS, - state->digital.dig_status[0] | (state->digital.dig_status[1] << 8)); -#endif - return 0; -} - -void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - ymfpci_t *codec = dev_id; - u32 status, nvoice, mode; - struct ymf_voice *voice; - struct ymf_capture *cap; - - status = ymfpci_readl(codec, YDSXGR_STATUS); - if (status & 0x80000000) { - codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1; - spin_lock(&codec->voice_lock); - for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) { - voice = &codec->voices[nvoice]; - if (voice->use) - ymf_pcm_interrupt(codec, voice); - } - for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) { - cap = &codec->capture[nvoice]; - if (cap->use) - ymf_cap_interrupt(codec, cap); - } - spin_unlock(&codec->voice_lock); - spin_lock(&codec->reg_lock); - ymfpci_writel(codec, YDSXGR_STATUS, 0x80000000); - mode = ymfpci_readl(codec, YDSXGR_MODE) | 2; - ymfpci_writel(codec, YDSXGR_MODE, mode); - spin_unlock(&codec->reg_lock); - } - - status = ymfpci_readl(codec, YDSXGR_INTFLAG); - if (status & 1) { - /* timer handler */ - ymfpci_writel(codec, YDSXGR_INTFLAG, ~0); - } -} - -static void ymf_pcm_free_substream(struct ymf_pcm *ypcm) -{ - unsigned long flags; - struct ymf_unit *unit; - - unit = ypcm->state->unit; - - if (ypcm->type == PLAYBACK_VOICE) { - spin_lock_irqsave(&unit->voice_lock, flags); - if (ypcm->voices[1]) - ymfpci_voice_free(unit, ypcm->voices[1]); - if (ypcm->voices[0]) - ymfpci_voice_free(unit, ypcm->voices[0]); - spin_unlock_irqrestore(&unit->voice_lock, flags); - } else { - if (ypcm->capture_bank_number != -1) { - unit->capture[ypcm->capture_bank_number].use = 0; - ypcm->capture_bank_number = -1; - ymfpci_hw_stop(unit); - } - } -} - -static struct ymf_state *ymf_state_alloc(ymfpci_t *unit) -{ - struct ymf_pcm *ypcm; - struct ymf_state *state; - - if ((state = kmalloc(sizeof(struct ymf_state), GFP_KERNEL)) == NULL) { - goto out0; - } - memset(state, 0, sizeof(struct ymf_state)); - - ypcm = &state->wpcm; - ypcm->state = state; - ypcm->type = PLAYBACK_VOICE; - ypcm->capture_bank_number = -1; - init_waitqueue_head(&ypcm->dmabuf.wait); - - ypcm = &state->rpcm; - ypcm->state = state; - ypcm->type = CAPTURE_AC97; - ypcm->capture_bank_number = -1; - init_waitqueue_head(&ypcm->dmabuf.wait); - - state->unit = unit; - - state->format.format = AFMT_U8; - state->format.rate = 8000; - state->format.voices = 1; - ymf_pcm_update_shift(&state->format); - - return state; - -out0: - return NULL; -} - -/* AES/IEC958 channel status bits */ -#define SND_PCM_AES0_PROFESSIONAL (1<<0) /* 0 = consumer, 1 = professional */ -#define SND_PCM_AES0_NONAUDIO (1<<1) /* 0 = audio, 1 = non-audio */ -#define SND_PCM_AES0_PRO_EMPHASIS (7<<2) /* mask - emphasis */ -#define SND_PCM_AES0_PRO_EMPHASIS_NOTID (0<<2) /* emphasis not indicated */ -#define SND_PCM_AES0_PRO_EMPHASIS_NONE (1<<2) /* none emphasis */ -#define SND_PCM_AES0_PRO_EMPHASIS_5015 (3<<2) /* 50/15us emphasis */ -#define SND_PCM_AES0_PRO_EMPHASIS_CCITT (7<<2) /* CCITT J.17 emphasis */ -#define SND_PCM_AES0_PRO_FREQ_UNLOCKED (1<<5) /* source sample frequency: 0 = locked, 1 = unlocked */ -#define SND_PCM_AES0_PRO_FS (3<<6) /* mask - sample frequency */ -#define SND_PCM_AES0_PRO_FS_NOTID (0<<6) /* fs not indicated */ -#define SND_PCM_AES0_PRO_FS_44100 (1<<6) /* 44.1kHz */ -#define SND_PCM_AES0_PRO_FS_48000 (2<<6) /* 48kHz */ -#define SND_PCM_AES0_PRO_FS_32000 (3<<6) /* 32kHz */ -#define SND_PCM_AES0_CON_NOT_COPYRIGHT (1<<2) /* 0 = copyright, 1 = not copyright */ -#define SND_PCM_AES0_CON_EMPHASIS (7<<3) /* mask - emphasis */ -#define SND_PCM_AES0_CON_EMPHASIS_NONE (0<<3) /* none emphasis */ -#define SND_PCM_AES0_CON_EMPHASIS_5015 (1<<3) /* 50/15us emphasis */ -#define SND_PCM_AES0_CON_MODE (3<<6) /* mask - mode */ -#define SND_PCM_AES1_PRO_MODE (15<<0) /* mask - channel mode */ -#define SND_PCM_AES1_PRO_MODE_NOTID (0<<0) /* not indicated */ -#define SND_PCM_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */ -#define SND_PCM_AES1_PRO_MODE_SINGLE (4<<0) /* single channel */ -#define SND_PCM_AES1_PRO_MODE_TWO (8<<0) /* two channels */ -#define SND_PCM_AES1_PRO_MODE_PRIMARY (12<<0) /* primary/secondary */ -#define SND_PCM_AES1_PRO_MODE_BYTE3 (15<<0) /* vector to byte 3 */ -#define SND_PCM_AES1_PRO_USERBITS (15<<4) /* mask - user bits */ -#define SND_PCM_AES1_PRO_USERBITS_NOTID (0<<4) /* not indicated */ -#define SND_PCM_AES1_PRO_USERBITS_192 (8<<4) /* 192-bit structure */ -#define SND_PCM_AES1_PRO_USERBITS_UDEF (12<<4) /* user defined application */ -#define SND_PCM_AES1_CON_CATEGORY 0x7f -#define SND_PCM_AES1_CON_GENERAL 0x00 -#define SND_PCM_AES1_CON_EXPERIMENTAL 0x40 -#define SND_PCM_AES1_CON_SOLIDMEM_MASK 0x0f -#define SND_PCM_AES1_CON_SOLIDMEM_ID 0x08 -#define SND_PCM_AES1_CON_BROADCAST1_MASK 0x07 -#define SND_PCM_AES1_CON_BROADCAST1_ID 0x04 -#define SND_PCM_AES1_CON_DIGDIGCONV_MASK 0x07 -#define SND_PCM_AES1_CON_DIGDIGCONV_ID 0x02 -#define SND_PCM_AES1_CON_ADC_COPYRIGHT_MASK 0x1f -#define SND_PCM_AES1_CON_ADC_COPYRIGHT_ID 0x06 -#define SND_PCM_AES1_CON_ADC_MASK 0x1f -#define SND_PCM_AES1_CON_ADC_ID 0x16 -#define SND_PCM_AES1_CON_BROADCAST2_MASK 0x0f -#define SND_PCM_AES1_CON_BROADCAST2_ID 0x0e -#define SND_PCM_AES1_CON_LASEROPT_MASK 0x07 -#define SND_PCM_AES1_CON_LASEROPT_ID 0x01 -#define SND_PCM_AES1_CON_MUSICAL_MASK 0x07 -#define SND_PCM_AES1_CON_MUSICAL_ID 0x05 -#define SND_PCM_AES1_CON_MAGNETIC_MASK 0x07 -#define SND_PCM_AES1_CON_MAGNETIC_ID 0x03 -#define SND_PCM_AES1_CON_IEC908_CD (SND_PCM_AES1_CON_LASEROPT_ID|0x00) -#define SND_PCM_AES1_CON_NON_IEC908_CD (SND_PCM_AES1_CON_LASEROPT_ID|0x08) -#define SND_PCM_AES1_CON_PCM_CODER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x00) -#define SND_PCM_AES1_CON_SAMPLER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x20) -#define SND_PCM_AES1_CON_MIXER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x10) -#define SND_PCM_AES1_CON_RATE_CONVERTER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x18) -#define SND_PCM_AES1_CON_SYNTHESIZER (SND_PCM_AES1_CON_MUSICAL_ID|0x00) -#define SND_PCM_AES1_CON_MICROPHONE (SND_PCM_AES1_CON_MUSICAL_ID|0x08) -#define SND_PCM_AES1_CON_DAT (SND_PCM_AES1_CON_MAGNETIC_ID|0x00) -#define SND_PCM_AES1_CON_VCR (SND_PCM_AES1_CON_MAGNETIC_ID|0x08) -#define SND_PCM_AES1_CON_ORIGINAL (1<<7) /* this bits depends on the category code */ -#define SND_PCM_AES2_PRO_SBITS (7<<0) /* mask - sample bits */ -#define SND_PCM_AES2_PRO_SBITS_20 (2<<0) /* 20-bit - coordination */ -#define SND_PCM_AES2_PRO_SBITS_24 (4<<0) /* 24-bit - main audio */ -#define SND_PCM_AES2_PRO_SBITS_UDEF (6<<0) /* user defined application */ -#define SND_PCM_AES2_PRO_WORDLEN (7<<3) /* mask - source word length */ -#define SND_PCM_AES2_PRO_WORDLEN_NOTID (0<<3) /* not indicated */ -#define SND_PCM_AES2_PRO_WORDLEN_22_18 (2<<3) /* 22-bit or 18-bit */ -#define SND_PCM_AES2_PRO_WORDLEN_23_19 (4<<3) /* 23-bit or 19-bit */ -#define SND_PCM_AES2_PRO_WORDLEN_24_20 (5<<3) /* 24-bit or 20-bit */ -#define SND_PCM_AES2_PRO_WORDLEN_20_16 (6<<3) /* 20-bit or 16-bit */ -#define SND_PCM_AES2_CON_SOURCE (15<<0) /* mask - source number */ -#define SND_PCM_AES2_CON_SOURCE_UNSPEC (0<<0) /* unspecified */ -#define SND_PCM_AES2_CON_CHANNEL (15<<4) /* mask - channel number */ -#define SND_PCM_AES2_CON_CHANNEL_UNSPEC (0<<4) /* unspecified */ -#define SND_PCM_AES3_CON_FS (15<<0) /* mask - sample frequency */ -#define SND_PCM_AES3_CON_FS_44100 (0<<0) /* 44.1kHz */ -#define SND_PCM_AES3_CON_FS_48000 (2<<0) /* 48kHz */ -#define SND_PCM_AES3_CON_FS_32000 (3<<0) /* 32kHz */ -#define SND_PCM_AES3_CON_CLOCK (3<<4) /* mask - clock accuracy */ -#define SND_PCM_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */ -#define SND_PCM_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */ -#define SND_PCM_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */ - -/* - * User interface - */ - -/* - * 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 -ymf_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - struct ymf_dmabuf *dmabuf = &state->rpcm.dmabuf; - struct ymf_unit *unit = state->unit; - DECLARE_WAITQUEUE(waita, current); - ssize_t ret; - unsigned long flags; - unsigned int swptr; - int cnt; /* This many to go in this revolution */ - - if (ppos != &file->f_pos) - return -ESPIPE; - if (dmabuf->mapped) - return -ENXIO; - if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) - return ret; - ret = 0; - - add_wait_queue(&dmabuf->wait, &waita); - set_current_state(TASK_INTERRUPTIBLE); - while (count > 0) { - spin_lock_irqsave(&unit->reg_lock, flags); - if (unit->suspended) { - spin_unlock_irqrestore(&unit->reg_lock, flags); - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) { - if (!ret) ret = -EAGAIN; - break; - } - continue; - } - swptr = dmabuf->swptr; - cnt = dmabuf->dmasize - swptr; - if (dmabuf->count < cnt) - cnt = dmabuf->count; - spin_unlock_irqrestore(&unit->reg_lock, flags); - - if (cnt > count) - cnt = count; - if (cnt <= 0) { - unsigned long tmo; - /* buffer is empty, start the dma machine and wait for data to be - recorded */ - spin_lock_irqsave(&state->unit->reg_lock, flags); - if (!state->rpcm.running) { - ymf_capture_trigger(state->unit, &state->rpcm, 1); - } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - if (file->f_flags & O_NONBLOCK) { - if (!ret) ret = -EAGAIN; - break; - } - /* This isnt strictly right for the 810 but it'll do */ - tmo = (dmabuf->dmasize * HZ) / (state->format.rate * 2); - tmo >>= state->format.shift; - /* 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. */ - tmo = schedule_timeout(tmo); - spin_lock_irqsave(&state->unit->reg_lock, flags); - set_current_state(TASK_INTERRUPTIBLE); - if (tmo == 0 && dmabuf->count == 0) { - printk(KERN_ERR "ymfpci%d: recording schedule timeout, " - "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - state->unit->dev_audio, - dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, - dmabuf->hwptr, dmabuf->swptr); - } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - if (signal_pending(current)) { - if (!ret) ret = -ERESTARTSYS; - break; - } - continue; - } - - if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { - if (!ret) ret = -EFAULT; - break; - } - - swptr = (swptr + cnt) % dmabuf->dmasize; - - spin_lock_irqsave(&unit->reg_lock, flags); - if (unit->suspended) { - spin_unlock_irqrestore(&unit->reg_lock, flags); - continue; - } - - dmabuf->swptr = swptr; - dmabuf->count -= cnt; - // spin_unlock_irqrestore(&unit->reg_lock, flags); - - count -= cnt; - buffer += cnt; - ret += cnt; - // spin_lock_irqsave(&unit->reg_lock, flags); - if (!state->rpcm.running) { - ymf_capture_trigger(unit, &state->rpcm, 1); - } - spin_unlock_irqrestore(&unit->reg_lock, flags); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&dmabuf->wait, &waita); - - return ret; -} - -static ssize_t -ymf_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; - struct ymf_unit *unit = state->unit; - DECLARE_WAITQUEUE(waita, current); - ssize_t ret; - unsigned long flags; - unsigned int swptr; - int cnt; /* This many to go in this revolution */ - int redzone; - int delay; - - YMFDBGW("ymf_write: count %d\n", count); - - if (ppos != &file->f_pos) - return -ESPIPE; - if (dmabuf->mapped) - return -ENXIO; - if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) - return ret; - ret = 0; - - /* - * Alan's cs46xx works without a red zone - marvel of ingenuity. - * We are not so brilliant... Red zone does two things: - * 1. allows for safe start after a pause as we have no way - * to know what the actual, relentlessly advancing, hwptr is. - * 2. makes computations in ymf_pcm_interrupt simpler. - */ - redzone = ymf_calc_lend(state->format.rate) << state->format.shift; - redzone *= 3; /* 2 redzone + 1 possible uncertainty reserve. */ - - add_wait_queue(&dmabuf->wait, &waita); - set_current_state(TASK_INTERRUPTIBLE); - while (count > 0) { - spin_lock_irqsave(&unit->reg_lock, flags); - if (unit->suspended) { - spin_unlock_irqrestore(&unit->reg_lock, flags); - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) { - if (!ret) ret = -EAGAIN; - break; - } - continue; - } - if (dmabuf->count < 0) { - printk(KERN_ERR - "ymf_write: count %d, was legal in cs46xx\n", - dmabuf->count); - dmabuf->count = 0; - } - if (dmabuf->count == 0) { - swptr = dmabuf->hwptr; - if (state->wpcm.running) { - /* - * Add uncertainty reserve. - */ - cnt = ymf_calc_lend(state->format.rate); - cnt <<= state->format.shift; - if ((swptr += cnt) >= dmabuf->dmasize) { - swptr -= dmabuf->dmasize; - } - } - dmabuf->swptr = swptr; - } else { - /* - * XXX This is not right if dmabuf->count is small - - * about 2*x frame size or less. We cannot count on - * on appending and not causing an artefact. - * Should use a variation of the count==0 case above. - */ - swptr = dmabuf->swptr; - } - cnt = dmabuf->dmasize - swptr; - if (dmabuf->count + cnt > dmabuf->dmasize - redzone) - cnt = (dmabuf->dmasize - redzone) - dmabuf->count; - spin_unlock_irqrestore(&unit->reg_lock, flags); - - if (cnt > count) - cnt = count; - if (cnt <= 0) { - YMFDBGW("ymf_write: full, count %d swptr %d\n", - dmabuf->count, dmabuf->swptr); - /* - * buffer is full, start the dma machine and - * wait for data to be played - */ - spin_lock_irqsave(&unit->reg_lock, flags); - if (!state->wpcm.running) { - ymf_playback_trigger(unit, &state->wpcm, 1); - } - spin_unlock_irqrestore(&unit->reg_lock, flags); - if (file->f_flags & O_NONBLOCK) { - if (!ret) ret = -EAGAIN; - break; - } - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) { - if (!ret) ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { - if (!ret) ret = -EFAULT; - break; - } - - if ((swptr += cnt) >= dmabuf->dmasize) { - swptr -= dmabuf->dmasize; - } - - spin_lock_irqsave(&unit->reg_lock, flags); - if (unit->suspended) { - spin_unlock_irqrestore(&unit->reg_lock, flags); - continue; - } - dmabuf->swptr = swptr; - dmabuf->count += cnt; - - /* - * Start here is a bad idea - may cause startup click - * in /bin/play when dmabuf is not full yet. - * However, some broken applications do not make - * any use of SNDCTL_DSP_SYNC (Doom is the worst). - * One frame is about 5.3ms, Doom write size is 46ms. - */ - delay = state->format.rate / 20; /* 50ms */ - delay <<= state->format.shift; - if (dmabuf->count >= delay && !state->wpcm.running) { - ymf_playback_trigger(unit, &state->wpcm, 1); - } - - spin_unlock_irqrestore(&unit->reg_lock, flags); - - count -= cnt; - buffer += cnt; - ret += cnt; - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&dmabuf->wait, &waita); - - YMFDBGW("ymf_write: ret %d dmabuf.count %d\n", ret, dmabuf->count); - return ret; -} - -static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - struct ymf_dmabuf *dmabuf; - int redzone; - unsigned long flags; - unsigned int mask = 0; - - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &state->wpcm.dmabuf.wait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &state->rpcm.dmabuf.wait, wait); - - spin_lock_irqsave(&state->unit->reg_lock, flags); - if (file->f_mode & FMODE_READ) { - dmabuf = &state->rpcm.dmabuf; - if (dmabuf->count >= (signed)dmabuf->fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - redzone = ymf_calc_lend(state->format.rate); - redzone <<= state->format.shift; - redzone *= 3; - - dmabuf = &state->wpcm.dmabuf; - if (dmabuf->mapped) { - if (dmabuf->count >= (signed)dmabuf->fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - /* - * Don't select unless a full fragment is available. - * Otherwise artsd does GETOSPACE, sees 0, and loops. - */ - if (dmabuf->count + redzone + dmabuf->fragsize - <= dmabuf->dmasize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - - return mask; -} - -static int ymf_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; - int ret; - unsigned long size; - - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(state, 0)) != 0) - return ret; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(state, 1)) != 0) - return ret; - } else - return -EINVAL; - - if (vma->vm_pgoff != 0) - return -EINVAL; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << dmabuf->buforder)) - return -EINVAL; - if (remap_page_range(vma, vma->vm_start, virt_to_phys(dmabuf->rawbuf), - size, vma->vm_page_prot)) - return -EAGAIN; - dmabuf->mapped = 1; - -/* P3 */ printk(KERN_INFO "ymfpci: using memory mapped sound, untested!\n"); - return 0; -} - -static int ymf_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - struct ymf_dmabuf *dmabuf; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int redzone; - int val; - - switch (cmd) { - case OSS_GETVERSION: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETVER) arg 0x%lx\n", cmd, arg); - return put_user(SOUND_VERSION, (int *)arg); - - case SNDCTL_DSP_RESET: - YMFDBGX("ymf_ioctl: cmd 0x%x(RESET)\n", cmd); - if (file->f_mode & FMODE_WRITE) { - ymf_wait_dac(state); - dmabuf = &state->wpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - dmabuf->swptr = dmabuf->hwptr; - dmabuf->count = dmabuf->total_bytes = 0; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - if (file->f_mode & FMODE_READ) { - ymf_stop_adc(state); - dmabuf = &state->rpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - dmabuf->swptr = dmabuf->hwptr; - dmabuf->count = dmabuf->total_bytes = 0; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - return 0; - - case SNDCTL_DSP_SYNC: - YMFDBGX("ymf_ioctl: cmd 0x%x(SYNC)\n", cmd); - if (file->f_mode & FMODE_WRITE) { - dmabuf = &state->wpcm.dmabuf; - if (file->f_flags & O_NONBLOCK) { - spin_lock_irqsave(&state->unit->reg_lock, flags); - if (dmabuf->count != 0 && !state->wpcm.running) { - ymf_start_dac(state); - } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } else { - ymf_wait_dac(state); - } - } - /* XXX What does this do for reading? dmabuf->count=0; ? */ - return 0; - - case SNDCTL_DSP_SPEED: /* set smaple rate */ - if (get_user(val, (int *)arg)) - return -EFAULT; - YMFDBGX("ymf_ioctl: cmd 0x%x(SPEED) sp %d\n", cmd, val); - if (val >= 8000 && val <= 48000) { - if (file->f_mode & FMODE_WRITE) { - ymf_wait_dac(state); - dmabuf = &state->wpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.rate = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - if (file->f_mode & FMODE_READ) { - ymf_stop_adc(state); - dmabuf = &state->rpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.rate = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - } - return put_user(state->format.rate, (int *)arg); - - /* - * OSS manual does not mention SNDCTL_DSP_STEREO at all. - * All channels are mono and if you want stereo, you - * play into two channels with SNDCTL_DSP_CHANNELS. - * However, mpg123 calls it. I wonder, why Michael Hipp used it. - */ - case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ - if (get_user(val, (int *)arg)) - return -EFAULT; - YMFDBGX("ymf_ioctl: cmd 0x%x(STEREO) st %d\n", cmd, val); - if (file->f_mode & FMODE_WRITE) { - ymf_wait_dac(state); - dmabuf = &state->wpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.voices = val ? 2 : 1; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - if (file->f_mode & FMODE_READ) { - ymf_stop_adc(state); - dmabuf = &state->rpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.voices = val ? 2 : 1; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETBLK)\n", cmd); - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf(state, 0))) - return val; - val = state->wpcm.dmabuf.fragsize; - YMFDBGX("ymf_ioctl: GETBLK w %d\n", val); - return put_user(val, (int *)arg); - } - if (file->f_mode & FMODE_READ) { - if ((val = prog_dmabuf(state, 1))) - return val; - val = state->rpcm.dmabuf.fragsize; - YMFDBGX("ymf_ioctl: GETBLK r %d\n", val); - return put_user(val, (int *)arg); - } - return -EINVAL; - - case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ - YMFDBGX("ymf_ioctl: cmd 0x%x(GETFMTS)\n", cmd); - return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); - - case SNDCTL_DSP_SETFMT: /* Select sample format */ - if (get_user(val, (int *)arg)) - return -EFAULT; - YMFDBGX("ymf_ioctl: cmd 0x%x(SETFMT) fmt %d\n", cmd, val); - if (val == AFMT_S16_LE || val == AFMT_U8) { - if (file->f_mode & FMODE_WRITE) { - ymf_wait_dac(state); - dmabuf = &state->wpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.format = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - if (file->f_mode & FMODE_READ) { - ymf_stop_adc(state); - dmabuf = &state->rpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.format = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - } - return put_user(state->format.format, (int *)arg); - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; - YMFDBGX("ymf_ioctl: cmd 0x%x(CHAN) ch %d\n", cmd, val); - if (val != 0) { - if (file->f_mode & FMODE_WRITE) { - ymf_wait_dac(state); - if (val == 1 || val == 2) { - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf = &state->wpcm.dmabuf; - dmabuf->ready = 0; - state->format.voices = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - } - if (file->f_mode & FMODE_READ) { - ymf_stop_adc(state); - if (val == 1 || val == 2) { - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf = &state->rpcm.dmabuf; - dmabuf->ready = 0; - state->format.voices = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - } - } - return put_user(state->format.voices, (int *)arg); - - case SNDCTL_DSP_POST: - YMFDBGX("ymf_ioctl: cmd 0x%x(POST)\n", cmd); - /* - * Quoting OSS PG: - * The ioctl SNDCTL_DSP_POST is a lightweight version of - * SNDCTL_DSP_SYNC. It just tells to the driver that there - * is likely to be a pause in the output. This makes it - * possible for the device to handle the pause more - * intelligently. This ioctl doesn't block the application. - * - * The paragraph above is a clumsy way to say "flush ioctl". - * This ioctl is used by mpg123. - */ - spin_lock_irqsave(&state->unit->reg_lock, flags); - if (state->wpcm.dmabuf.count != 0 && !state->wpcm.running) { - ymf_start_dac(state); - } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - return 0; - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) - return -EFAULT; - YMFDBGX("ymf_ioctl: cmd 0x%x(SETFRAG) fr 0x%04x:%04x(%d:%d)\n", - cmd, - (val >> 16) & 0xFFFF, val & 0xFFFF, - (val >> 16) & 0xFFFF, val & 0xFFFF); - dmabuf = &state->wpcm.dmabuf; - dmabuf->ossfragshift = val & 0xffff; - dmabuf->ossmaxfrags = (val >> 16) & 0xffff; - if (dmabuf->ossfragshift < 4) - dmabuf->ossfragshift = 4; - if (dmabuf->ossfragshift > 15) - dmabuf->ossfragshift = 15; - return 0; - - case SNDCTL_DSP_GETOSPACE: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETOSPACE)\n", cmd); - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - dmabuf = &state->wpcm.dmabuf; - if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - return val; - redzone = ymf_calc_lend(state->format.rate); - redzone <<= state->format.shift; - redzone *= 3; - spin_lock_irqsave(&state->unit->reg_lock, flags); - abinfo.fragsize = dmabuf->fragsize; - abinfo.bytes = dmabuf->dmasize - dmabuf->count - redzone; - abinfo.fragstotal = dmabuf->numfrag; - abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETISPACE)\n", cmd); - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - dmabuf = &state->rpcm.dmabuf; - if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) - return val; - spin_lock_irqsave(&state->unit->reg_lock, flags); - abinfo.fragsize = dmabuf->fragsize; - abinfo.bytes = dmabuf->count; - abinfo.fragstotal = dmabuf->numfrag; - abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - YMFDBGX("ymf_ioctl: cmd 0x%x(NONBLOCK)\n", cmd); - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETCAPS: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETCAPS)\n", cmd); - /* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, - (int *)arg); */ - return put_user(0, (int *)arg); - - case SNDCTL_DSP_GETIPTR: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETIPTR)\n", cmd); - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - dmabuf = &state->rpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - cinfo.bytes = dmabuf->total_bytes; - cinfo.blocks = dmabuf->count >> dmabuf->fragshift; - cinfo.ptr = dmabuf->hwptr; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - YMFDBGX("ymf_ioctl: GETIPTR ptr %d bytes %d\n", - cinfo.ptr, cinfo.bytes); - return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETOPTR: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETOPTR)\n", cmd); - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - dmabuf = &state->wpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - cinfo.bytes = dmabuf->total_bytes; - cinfo.blocks = dmabuf->count >> dmabuf->fragshift; - cinfo.ptr = dmabuf->hwptr; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - YMFDBGX("ymf_ioctl: GETOPTR ptr %d bytes %d\n", - cinfo.ptr, cinfo.bytes); - return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_SETDUPLEX: - YMFDBGX("ymf_ioctl: cmd 0x%x(SETDUPLEX)\n", cmd); - return 0; /* Always duplex */ - - case SOUND_PCM_READ_RATE: - YMFDBGX("ymf_ioctl: cmd 0x%x(READ_RATE)\n", cmd); - return put_user(state->format.rate, (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - YMFDBGX("ymf_ioctl: cmd 0x%x(READ_CH)\n", cmd); - return put_user(state->format.voices, (int *)arg); - - case SOUND_PCM_READ_BITS: - YMFDBGX("ymf_ioctl: cmd 0x%x(READ_BITS)\n", cmd); - return put_user(AFMT_S16_LE, (int *)arg); - - case SNDCTL_DSP_MAPINBUF: - case SNDCTL_DSP_MAPOUTBUF: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_WRITE_FILTER: - case SOUND_PCM_READ_FILTER: - YMFDBGX("ymf_ioctl: cmd 0x%x unsupported\n", cmd); - return -ENOTTY; - - default: - /* - * Some programs mix up audio devices and ioctls - * or perhaps they expect "universal" ioctls, - * for instance we get SNDCTL_TMR_CONTINUE here. - * (mpg123 -g 100 ends here too - to be fixed.) - */ - YMFDBGX("ymf_ioctl: cmd 0x%x unknown\n", cmd); - break; - } - return -ENOTTY; -} - -/* - * open(2) - * We use upper part of the minor to distinguish between soundcards. - * Channels are opened with a clone open. - */ -static int ymf_open(struct inode *inode, struct file *file) -{ - struct list_head *list; - ymfpci_t *unit = NULL; - int minor; - struct ymf_state *state; - int err; - - minor = minor(inode->i_rdev); - if ((minor & 0x0F) == 3) { /* /dev/dspN */ - ; - } else { - return -ENXIO; - } - - unit = NULL; /* gcc warns */ - list_for_each(list, &ymf_devs) { - unit = list_entry(list, ymfpci_t, ymf_devs); - if (((unit->dev_audio ^ minor) & ~0x0F) == 0) - break; - } - if (list == &ymf_devs) - return -ENODEV; - - down(&unit->open_sem); - - if ((state = ymf_state_alloc(unit)) == NULL) { - up(&unit->open_sem); - return -ENOMEM; - } - list_add_tail(&state->chain, &unit->states); - - file->private_data = state; - - /* - * ymf_read and ymf_write that we borrowed from cs46xx - * allocate buffers with prog_dmabuf(). We call prog_dmabuf - * here so that in case of DMA memory exhaustion open - * fails rather than write. - * - * XXX prog_dmabuf allocates voice. Should allocate explicitly, above. - */ - if (file->f_mode & FMODE_WRITE) { - if (!state->wpcm.dmabuf.ready) { - if ((err = prog_dmabuf(state, 0)) != 0) { - goto out_nodma; - } - } - } - if (file->f_mode & FMODE_READ) { - if (!state->rpcm.dmabuf.ready) { - if ((err = prog_dmabuf(state, 1)) != 0) { - goto out_nodma; - } - } - } - -#if 0 /* test if interrupts work */ - ymfpci_writew(unit, YDSXGR_TIMERCOUNT, 0xfffe); /* ~ 680ms */ - ymfpci_writeb(unit, YDSXGR_TIMERCTRL, - (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN)); -#endif - up(&unit->open_sem); - - return 0; - -out_nodma: - /* - * XXX Broken custom: "goto out_xxx" in other place is - * a nestable exception, but here it is not nestable due to semaphore. - * XXX Doubtful technique of self-describing objects.... - */ - dealloc_dmabuf(unit, &state->wpcm.dmabuf); - dealloc_dmabuf(unit, &state->rpcm.dmabuf); - ymf_pcm_free_substream(&state->wpcm); - ymf_pcm_free_substream(&state->rpcm); - - list_del(&state->chain); - kfree(state); - - up(&unit->open_sem); - return err; -} - -static int ymf_release(struct inode *inode, struct file *file) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - ymfpci_t *unit = state->unit; - -#if 0 /* test if interrupts work */ - ymfpci_writeb(unit, YDSXGR_TIMERCTRL, 0); -#endif - - down(&unit->open_sem); - - /* - * XXX Solve the case of O_NONBLOCK close - don't deallocate here. - * Deallocate when unloading the driver and we can wait. - */ - ymf_wait_dac(state); - ymf_stop_adc(state); /* fortunately, it's immediate */ - dealloc_dmabuf(unit, &state->wpcm.dmabuf); - dealloc_dmabuf(unit, &state->rpcm.dmabuf); - ymf_pcm_free_substream(&state->wpcm); - ymf_pcm_free_substream(&state->rpcm); - - list_del(&state->chain); - file->private_data = NULL; /* Can you tell I programmed Solaris */ - kfree(state); - - up(&unit->open_sem); - - return 0; -} - -/* - * Mixer operations are based on cs46xx. - */ -static int ymf_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = minor(inode->i_rdev); - struct list_head *list; - ymfpci_t *unit; - int i; - - list_for_each(list, &ymf_devs) { - unit = list_entry(list, ymfpci_t, ymf_devs); - for (i = 0; i < NR_AC97; i++) { - if (unit->ac97_codec[i] != NULL && - unit->ac97_codec[i]->dev_mixer == minor) { - goto match; - } - } - } - return -ENODEV; - - match: - file->private_data = unit->ac97_codec[i]; - - return 0; -} - -static int ymf_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 int ymf_release_mixdev(struct inode *inode, struct file *file) -{ - return 0; -} - -static /*const*/ struct file_operations ymf_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: ymf_read, - write: ymf_write, - poll: ymf_poll, - ioctl: ymf_ioctl, - mmap: ymf_mmap, - open: ymf_open, - release: ymf_release, -}; - -static /*const*/ struct file_operations ymf_mixer_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - ioctl: ymf_ioctl_mixdev, - open: ymf_open_mixdev, - release: ymf_release_mixdev, -}; - -/* - */ - -static int ymf_suspend(struct pci_dev *pcidev, u32 unused) -{ - struct ymf_unit *unit = pci_get_drvdata(pcidev); - unsigned long flags; - struct ymf_dmabuf *dmabuf; - struct list_head *p; - struct ymf_state *state; - struct ac97_codec *codec; - int i; - - spin_lock_irqsave(&unit->reg_lock, flags); - - unit->suspended = 1; - - for (i = 0; i < NR_AC97; i++) { - if ((codec = unit->ac97_codec[i]) != NULL) - ac97_save_state(codec); - } - - list_for_each(p, &unit->states) { - state = list_entry(p, struct ymf_state, chain); - - dmabuf = &state->wpcm.dmabuf; - dmabuf->hwptr = dmabuf->swptr = 0; - dmabuf->total_bytes = 0; - dmabuf->count = 0; - - dmabuf = &state->rpcm.dmabuf; - dmabuf->hwptr = dmabuf->swptr = 0; - dmabuf->total_bytes = 0; - dmabuf->count = 0; - } - - ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0); - ymfpci_disable_dsp(unit); - - spin_unlock_irqrestore(&unit->reg_lock, flags); - - return 0; -} - -static int ymf_resume(struct pci_dev *pcidev) -{ - struct ymf_unit *unit = pci_get_drvdata(pcidev); - unsigned long flags; - struct list_head *p; - struct ymf_state *state; - struct ac97_codec *codec; - int i; - - ymfpci_aclink_reset(unit->pci); - ymfpci_codec_ready(unit, 0, 1); /* prints diag if not ready. */ - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - /* XXX At this time the legacy registers are probably deprogrammed. */ -#endif - - ymfpci_download_image(unit); - - ymf_memload(unit); - - spin_lock_irqsave(&unit->reg_lock, flags); - - if (unit->start_count) { - ymfpci_writel(unit, YDSXGR_MODE, 3); - unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; - } - - for (i = 0; i < NR_AC97; i++) { - if ((codec = unit->ac97_codec[i]) != NULL) - ac97_restore_state(codec); - } - - unit->suspended = 0; - list_for_each(p, &unit->states) { - state = list_entry(p, struct ymf_state, chain); - wake_up(&state->wpcm.dmabuf.wait); - wake_up(&state->rpcm.dmabuf.wait); - } - - spin_unlock_irqrestore(&unit->reg_lock, flags); - return 0; -} - -/* - * initialization routines - */ - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - -static int ymfpci_setup_legacy(ymfpci_t *unit, struct pci_dev *pcidev) -{ - int v; - int mpuio = -1, oplio = -1; - - switch (unit->iomidi) { - case 0x330: - mpuio = 0; - break; - case 0x300: - mpuio = 1; - break; - case 0x332: - mpuio = 2; - break; - case 0x334: - mpuio = 3; - break; - default: ; - } - - switch (unit->iosynth) { - case 0x388: - oplio = 0; - break; - case 0x398: - oplio = 1; - break; - case 0x3a0: - oplio = 2; - break; - case 0x3a8: - oplio = 3; - break; - default: ; - } - - if (mpuio >= 0 || oplio >= 0) { - /* 0x0020: 1 - 10 bits of I/O address decoded, 0 - 16 bits. */ - v = 0x001e; - pci_write_config_word(pcidev, PCIR_LEGCTRL, v); - - switch (pcidev->device) { - case PCI_DEVICE_ID_YAMAHA_724: - case PCI_DEVICE_ID_YAMAHA_740: - case PCI_DEVICE_ID_YAMAHA_724F: - case PCI_DEVICE_ID_YAMAHA_740C: - v = 0x8800; - if (mpuio >= 0) { v |= mpuio<<4; } - if (oplio >= 0) { v |= oplio; } - pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); - break; - - case PCI_DEVICE_ID_YAMAHA_744: - case PCI_DEVICE_ID_YAMAHA_754: - v = 0x8800; - pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); - if (oplio >= 0) { - pci_write_config_word(pcidev, PCIR_OPLADR, unit->iosynth); - } - if (mpuio >= 0) { - pci_write_config_word(pcidev, PCIR_MPUADR, unit->iomidi); - } - break; - - default: - printk(KERN_ERR "ymfpci: Unknown device ID: 0x%x\n", - pcidev->device); - return -EINVAL; - } - } - - return 0; -} -#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ - -static void ymfpci_aclink_reset(struct pci_dev * pci) -{ - u8 cmd; - - /* - * In the 744, 754 only 0x01 exists, 0x02 is undefined. - * It does not seem to hurt to trip both regardless of revision. - */ - pci_read_config_byte(pci, PCIR_DSXGCTRL, &cmd); - pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); - pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd | 0x03); - pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); - - pci_write_config_word(pci, PCIR_DSXPWRCTRL1, 0); - pci_write_config_word(pci, PCIR_DSXPWRCTRL2, 0); -} - -static void ymfpci_enable_dsp(ymfpci_t *codec) -{ - ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000001); -} - -static void ymfpci_disable_dsp(ymfpci_t *codec) -{ - u32 val; - int timeout = 1000; - - val = ymfpci_readl(codec, YDSXGR_CONFIG); - if (val) - ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000000); - while (timeout-- > 0) { - val = ymfpci_readl(codec, YDSXGR_STATUS); - if ((val & 0x00000002) == 0) - break; - } -} - -#include "ymfpci_image.h" - -static void ymfpci_download_image(ymfpci_t *codec) -{ - int i, ver_1e; - u16 ctrl; - - ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x00000000); - ymfpci_disable_dsp(codec); - ymfpci_writel(codec, YDSXGR_MODE, 0x00010000); - ymfpci_writel(codec, YDSXGR_MODE, 0x00000000); - ymfpci_writel(codec, YDSXGR_MAPOFREC, 0x00000000); - ymfpci_writel(codec, YDSXGR_MAPOFEFFECT, 0x00000000); - ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0x00000000); - ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0x00000000); - ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0x00000000); - ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); - ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); - - /* setup DSP instruction code */ - for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) - ymfpci_writel(codec, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]); - - switch (codec->pci->device) { - case PCI_DEVICE_ID_YAMAHA_724F: - case PCI_DEVICE_ID_YAMAHA_740C: - case PCI_DEVICE_ID_YAMAHA_744: - case PCI_DEVICE_ID_YAMAHA_754: - ver_1e = 1; - break; - default: - ver_1e = 0; - } - - if (ver_1e) { - /* setup control instruction code */ - for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) - ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst1E[i]); - } else { - for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) - ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst[i]); - } - - ymfpci_enable_dsp(codec); - - /* 0.02s sounds not too bad, we may do schedule_timeout() later. */ - mdelay(20); /* seems we need some delay after downloading image.. */ -} - -static int ymfpci_memalloc(ymfpci_t *codec) -{ - unsigned int playback_ctrl_size; - unsigned int bank_size_playback; - unsigned int bank_size_capture; - unsigned int bank_size_effect; - unsigned int size; - unsigned int off; - char *ptr; - dma_addr_t pba; - int voice, bank; - - playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES; - bank_size_playback = ymfpci_readl(codec, YDSXGR_PLAYCTRLSIZE) << 2; - bank_size_capture = ymfpci_readl(codec, YDSXGR_RECCTRLSIZE) << 2; - bank_size_effect = ymfpci_readl(codec, YDSXGR_EFFCTRLSIZE) << 2; - codec->work_size = YDSXG_DEFAULT_WORK_SIZE; - - size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) + - ((bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0xff) & ~0xff) + - ((bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0xff) & ~0xff) + - ((bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0xff) & ~0xff) + - codec->work_size; - - ptr = pci_alloc_consistent(codec->pci, size + 0xff, &pba); - if (ptr == NULL) - return -ENOMEM; - codec->dma_area_va = ptr; - codec->dma_area_ba = pba; - codec->dma_area_size = size + 0xff; - - if ((off = ((uint) ptr) & 0xff) != 0) { - ptr += 0x100 - off; - pba += 0x100 - off; - } - - /* - * Hardware requires only ptr[playback_ctrl_size] zeroed, - * but in our judgement it is a wrong kind of savings, so clear it all. - */ - memset(ptr, 0, size); - - codec->ctrl_playback = (u32 *)ptr; - codec->ctrl_playback_ba = pba; - codec->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES); - ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff; - pba += (playback_ctrl_size + 0x00ff) & ~0x00ff; - - off = 0; - for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) { - codec->voices[voice].number = voice; - codec->voices[voice].bank = - (ymfpci_playback_bank_t *) (ptr + off); - codec->voices[voice].bank_ba = pba + off; - off += 2 * bank_size_playback; /* 2 banks */ - } - off = (off + 0xff) & ~0xff; - ptr += off; - pba += off; - - off = 0; - codec->bank_base_capture = pba; - for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++) - for (bank = 0; bank < 2; bank++) { - codec->bank_capture[voice][bank] = - (ymfpci_capture_bank_t *) (ptr + off); - off += bank_size_capture; - } - off = (off + 0xff) & ~0xff; - ptr += off; - pba += off; - - off = 0; - codec->bank_base_effect = pba; - for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++) - for (bank = 0; bank < 2; bank++) { - codec->bank_effect[voice][bank] = - (ymfpci_effect_bank_t *) (ptr + off); - off += bank_size_effect; - } - off = (off + 0xff) & ~0xff; - ptr += off; - pba += off; - - codec->work_base = pba; - - return 0; -} - -static void ymfpci_memfree(ymfpci_t *codec) -{ - ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0); - ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0); - ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0); - ymfpci_writel(codec, YDSXGR_WORKBASE, 0); - ymfpci_writel(codec, YDSXGR_WORKSIZE, 0); - pci_free_consistent(codec->pci, - codec->dma_area_size, codec->dma_area_va, codec->dma_area_ba); -} - -static void ymf_memload(ymfpci_t *unit) -{ - - ymfpci_writel(unit, YDSXGR_PLAYCTRLBASE, unit->ctrl_playback_ba); - ymfpci_writel(unit, YDSXGR_RECCTRLBASE, unit->bank_base_capture); - ymfpci_writel(unit, YDSXGR_EFFCTRLBASE, unit->bank_base_effect); - ymfpci_writel(unit, YDSXGR_WORKBASE, unit->work_base); - ymfpci_writel(unit, YDSXGR_WORKSIZE, unit->work_size >> 2); - - /* S/PDIF output initialization */ - ymfpci_writew(unit, YDSXGR_SPDIFOUTCTRL, 0); - ymfpci_writew(unit, YDSXGR_SPDIFOUTSTATUS, - SND_PCM_AES0_CON_EMPHASIS_NONE | - (SND_PCM_AES1_CON_ORIGINAL << 8) | - (SND_PCM_AES1_CON_PCM_CODER << 8)); - - /* S/PDIF input initialization */ - ymfpci_writew(unit, YDSXGR_SPDIFINCTRL, 0); - - /* move this volume setup to mixer */ - ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); - ymfpci_writel(unit, YDSXGR_BUF441OUTVOL, 0); - ymfpci_writel(unit, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); - ymfpci_writel(unit, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); -} - -static int ymf_ac97_init(ymfpci_t *unit, int num_ac97) -{ - struct ac97_codec *codec; - u16 eid; - - if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) - return -ENOMEM; - memset(codec, 0, sizeof(struct ac97_codec)); - - /* initialize some basic codec information, other fields will be filled - in ac97_probe_codec */ - codec->private_data = unit; - codec->id = num_ac97; - - codec->codec_read = ymfpci_codec_read; - codec->codec_write = ymfpci_codec_write; - - if (ac97_probe_codec(codec) == 0) { - printk(KERN_ERR "ymfpci: ac97_probe_codec failed\n"); - goto out_kfree; - } - - eid = ymfpci_codec_read(codec, AC97_EXTENDED_ID); - if (eid==0xFFFFFF) { - printk(KERN_WARNING "ymfpci: no codec attached ?\n"); - goto out_kfree; - } - - unit->ac97_features = eid; - - if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) { - printk(KERN_ERR "ymfpci: couldn't register mixer!\n"); - goto out_kfree; - } - - unit->ac97_codec[num_ac97] = codec; - - return 0; - out_kfree: - kfree(codec); - return -ENODEV; -} - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY -# ifdef MODULE -static int mpu_io = 0; -static int synth_io = 0; -MODULE_PARM(mpu_io, "i"); -MODULE_PARM(synth_io, "i"); -# else -static int mpu_io = 0x330; -static int synth_io = 0x388; -# endif -static int assigned; -#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ - -static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent) -{ - u16 ctrl; - unsigned long base; - ymfpci_t *codec; - - int err; - - if ((err = pci_enable_device(pcidev)) != 0) { - printk(KERN_ERR "ymfpci: pci_enable_device failed\n"); - return err; - } - base = pci_resource_start(pcidev, 0); - - if ((codec = kmalloc(sizeof(ymfpci_t), GFP_KERNEL)) == NULL) { - printk(KERN_ERR "ymfpci: no core\n"); - return -ENOMEM; - } - memset(codec, 0, sizeof(*codec)); - - spin_lock_init(&codec->reg_lock); - spin_lock_init(&codec->voice_lock); - init_MUTEX(&codec->open_sem); - INIT_LIST_HEAD(&codec->states); - codec->pci = pcidev; - - pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev); - - if (request_mem_region(base, 0x8000, "ymfpci") == NULL) { - printk(KERN_ERR "ymfpci: unable to request mem region\n"); - goto out_free; - } - - if ((codec->reg_area_virt = ioremap(base, 0x8000)) == NULL) { - printk(KERN_ERR "ymfpci: unable to map registers\n"); - goto out_release_region; - } - - pci_set_master(pcidev); - - printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n", - (char *)ent->driver_data, base, pcidev->irq); - - ymfpci_aclink_reset(pcidev); - if (ymfpci_codec_ready(codec, 0, 1) < 0) - goto out_unmap; - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - if (assigned == 0) { - codec->iomidi = mpu_io; - codec->iosynth = synth_io; - if (ymfpci_setup_legacy(codec, pcidev) < 0) - goto out_unmap; - assigned = 1; - } -#endif - - ymfpci_download_image(codec); - - if (ymfpci_memalloc(codec) < 0) - goto out_disable_dsp; - ymf_memload(codec); - - if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) { - printk(KERN_ERR "ymfpci: unable to request IRQ %d\n", - pcidev->irq); - goto out_memfree; - } - - /* register /dev/dsp */ - if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) { - printk(KERN_ERR "ymfpci: unable to register dsp\n"); - goto out_free_irq; - } - - /* - * Poke just the primary for the moment. - */ - if ((err = ymf_ac97_init(codec, 0)) != 0) - goto out_unregister_sound_dsp; - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - codec->opl3_data.name = "ymfpci"; - codec->mpu_data.name = "ymfpci"; - - codec->opl3_data.io_base = codec->iosynth; - codec->opl3_data.irq = -1; - - codec->mpu_data.io_base = codec->iomidi; - codec->mpu_data.irq = -1; /* May be different from our PCI IRQ. */ - - if (codec->iomidi) { - if (!probe_uart401(&codec->mpu_data, THIS_MODULE)) { - codec->iomidi = 0; /* XXX kludge */ - } - } -#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ - - /* put it into driver list */ - list_add_tail(&codec->ymf_devs, &ymf_devs); - pci_set_drvdata(pcidev, codec); - - return 0; - - out_unregister_sound_dsp: - unregister_sound_dsp(codec->dev_audio); - out_free_irq: - free_irq(pcidev->irq, codec); - out_memfree: - ymfpci_memfree(codec); - out_disable_dsp: - ymfpci_disable_dsp(codec); - ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); - ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); - ymfpci_writel(codec, YDSXGR_STATUS, ~0); - out_unmap: - iounmap(codec->reg_area_virt); - out_release_region: - release_mem_region(pci_resource_start(pcidev, 0), 0x8000); - out_free: - kfree(codec); - return -ENODEV; -} - -static void __devexit ymf_remove_one(struct pci_dev *pcidev) -{ - __u16 ctrl; - ymfpci_t *codec = pci_get_drvdata(pcidev); - - /* remove from list of devices */ - list_del(&codec->ymf_devs); - - unregister_sound_mixer(codec->ac97_codec[0]->dev_mixer); - kfree(codec->ac97_codec[0]); - unregister_sound_dsp(codec->dev_audio); - free_irq(pcidev->irq, codec); - ymfpci_memfree(codec); - ymfpci_writel(codec, YDSXGR_STATUS, ~0); - ymfpci_disable_dsp(codec); - ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); - ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); - iounmap(codec->reg_area_virt); - release_mem_region(pci_resource_start(pcidev, 0), 0x8000); -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - if (codec->iomidi) { - unload_uart401(&codec->mpu_data); - } -#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ - kfree(codec); -} - -MODULE_AUTHOR("Jaroslav Kysela"); -MODULE_DESCRIPTION("Yamaha YMF7xx PCI Audio"); -MODULE_LICENSE("GPL"); - -static struct pci_driver ymfpci_driver = { - name: "ymfpci", - id_table: ymf_id_tbl, - probe: ymf_probe_one, - remove: ymf_remove_one, - suspend: ymf_suspend, - resume: ymf_resume -}; - -static int __init ymf_init_module(void) -{ - return pci_module_init(&ymfpci_driver); -} - -static void __exit ymf_cleanup_module (void) -{ - pci_unregister_driver(&ymfpci_driver); -} - -module_init(ymf_init_module); -module_exit(ymf_cleanup_module); diff -Nru a/drivers/sound/ymfpci.h b/drivers/sound/ymfpci.h --- a/drivers/sound/ymfpci.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,359 +0,0 @@ -#ifndef __YMFPCI_H -#define __YMFPCI_H - -/* - * Copyright (c) by Jaroslav Kysela - * Definitions for Yahama YMF724/740/744/754 chips - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ -#include - -/* - * Direct registers - */ - -/* #define YMFREG(codec, reg) (codec->port + YDSXGR_##reg) */ - -#define YDSXGR_INTFLAG 0x0004 -#define YDSXGR_ACTIVITY 0x0006 -#define YDSXGR_GLOBALCTRL 0x0008 -#define YDSXGR_ZVCTRL 0x000A -#define YDSXGR_TIMERCTRL 0x0010 -#define YDSXGR_TIMERCTRL_TEN 0x0001 -#define YDSXGR_TIMERCTRL_TIEN 0x0002 -#define YDSXGR_TIMERCOUNT 0x0012 -#define YDSXGR_SPDIFOUTCTRL 0x0018 -#define YDSXGR_SPDIFOUTSTATUS 0x001C -#define YDSXGR_EEPROMCTRL 0x0020 -#define YDSXGR_SPDIFINCTRL 0x0034 -#define YDSXGR_SPDIFINSTATUS 0x0038 -#define YDSXGR_DSPPROGRAMDL 0x0048 -#define YDSXGR_DLCNTRL 0x004C -#define YDSXGR_GPIOININTFLAG 0x0050 -#define YDSXGR_GPIOININTENABLE 0x0052 -#define YDSXGR_GPIOINSTATUS 0x0054 -#define YDSXGR_GPIOOUTCTRL 0x0056 -#define YDSXGR_GPIOFUNCENABLE 0x0058 -#define YDSXGR_GPIOTYPECONFIG 0x005A -#define YDSXGR_AC97CMDDATA 0x0060 -#define YDSXGR_AC97CMDADR 0x0062 -#define YDSXGR_PRISTATUSDATA 0x0064 -#define YDSXGR_PRISTATUSADR 0x0066 -#define YDSXGR_SECSTATUSDATA 0x0068 -#define YDSXGR_SECSTATUSADR 0x006A -#define YDSXGR_SECCONFIG 0x0070 -#define YDSXGR_LEGACYOUTVOL 0x0080 -#define YDSXGR_LEGACYOUTVOLL 0x0080 -#define YDSXGR_LEGACYOUTVOLR 0x0082 -#define YDSXGR_NATIVEDACOUTVOL 0x0084 -#define YDSXGR_NATIVEDACOUTVOLL 0x0084 -#define YDSXGR_NATIVEDACOUTVOLR 0x0086 -#define YDSXGR_SPDIFOUTVOL 0x0088 -#define YDSXGR_SPDIFOUTVOLL 0x0088 -#define YDSXGR_SPDIFOUTVOLR 0x008A -#define YDSXGR_AC3OUTVOL 0x008C -#define YDSXGR_AC3OUTVOLL 0x008C -#define YDSXGR_AC3OUTVOLR 0x008E -#define YDSXGR_PRIADCOUTVOL 0x0090 -#define YDSXGR_PRIADCOUTVOLL 0x0090 -#define YDSXGR_PRIADCOUTVOLR 0x0092 -#define YDSXGR_LEGACYLOOPVOL 0x0094 -#define YDSXGR_LEGACYLOOPVOLL 0x0094 -#define YDSXGR_LEGACYLOOPVOLR 0x0096 -#define YDSXGR_NATIVEDACLOOPVOL 0x0098 -#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 -#define YDSXGR_NATIVEDACLOOPVOLR 0x009A -#define YDSXGR_SPDIFLOOPVOL 0x009C -#define YDSXGR_SPDIFLOOPVOLL 0x009E -#define YDSXGR_SPDIFLOOPVOLR 0x009E -#define YDSXGR_AC3LOOPVOL 0x00A0 -#define YDSXGR_AC3LOOPVOLL 0x00A0 -#define YDSXGR_AC3LOOPVOLR 0x00A2 -#define YDSXGR_PRIADCLOOPVOL 0x00A4 -#define YDSXGR_PRIADCLOOPVOLL 0x00A4 -#define YDSXGR_PRIADCLOOPVOLR 0x00A6 -#define YDSXGR_NATIVEADCINVOL 0x00A8 -#define YDSXGR_NATIVEADCINVOLL 0x00A8 -#define YDSXGR_NATIVEADCINVOLR 0x00AA -#define YDSXGR_NATIVEDACINVOL 0x00AC -#define YDSXGR_NATIVEDACINVOLL 0x00AC -#define YDSXGR_NATIVEDACINVOLR 0x00AE -#define YDSXGR_BUF441OUTVOL 0x00B0 -#define YDSXGR_BUF441OUTVOLL 0x00B0 -#define YDSXGR_BUF441OUTVOLR 0x00B2 -#define YDSXGR_BUF441LOOPVOL 0x00B4 -#define YDSXGR_BUF441LOOPVOLL 0x00B4 -#define YDSXGR_BUF441LOOPVOLR 0x00B6 -#define YDSXGR_SPDIFOUTVOL2 0x00B8 -#define YDSXGR_SPDIFOUTVOL2L 0x00B8 -#define YDSXGR_SPDIFOUTVOL2R 0x00BA -#define YDSXGR_SPDIFLOOPVOL2 0x00BC -#define YDSXGR_SPDIFLOOPVOL2L 0x00BC -#define YDSXGR_SPDIFLOOPVOL2R 0x00BE -#define YDSXGR_ADCSLOTSR 0x00C0 -#define YDSXGR_RECSLOTSR 0x00C4 -#define YDSXGR_ADCFORMAT 0x00C8 -#define YDSXGR_RECFORMAT 0x00CC -#define YDSXGR_P44SLOTSR 0x00D0 -#define YDSXGR_STATUS 0x0100 -#define YDSXGR_CTRLSELECT 0x0104 -#define YDSXGR_MODE 0x0108 -#define YDSXGR_SAMPLECOUNT 0x010C -#define YDSXGR_NUMOFSAMPLES 0x0110 -#define YDSXGR_CONFIG 0x0114 -#define YDSXGR_PLAYCTRLSIZE 0x0140 -#define YDSXGR_RECCTRLSIZE 0x0144 -#define YDSXGR_EFFCTRLSIZE 0x0148 -#define YDSXGR_WORKSIZE 0x014C -#define YDSXGR_MAPOFREC 0x0150 -#define YDSXGR_MAPOFEFFECT 0x0154 -#define YDSXGR_PLAYCTRLBASE 0x0158 -#define YDSXGR_RECCTRLBASE 0x015C -#define YDSXGR_EFFCTRLBASE 0x0160 -#define YDSXGR_WORKBASE 0x0164 -#define YDSXGR_DSPINSTRAM 0x1000 -#define YDSXGR_CTRLINSTRAM 0x4000 - -#define YDSXG_AC97READCMD 0x8000 -#define YDSXG_AC97WRITECMD 0x0000 - -#define PCIR_LEGCTRL 0x40 -#define PCIR_ELEGCTRL 0x42 -#define PCIR_DSXGCTRL 0x48 -#define PCIR_DSXPWRCTRL1 0x4a -#define PCIR_DSXPWRCTRL2 0x4e -#define PCIR_OPLADR 0x60 -#define PCIR_SBADR 0x62 -#define PCIR_MPUADR 0x64 - -#define YDSXG_DSPLENGTH 0x0080 -#define YDSXG_CTRLLENGTH 0x3000 - -#define YDSXG_DEFAULT_WORK_SIZE 0x0400 - -#define YDSXG_PLAYBACK_VOICES 64 -#define YDSXG_CAPTURE_VOICES 2 -#define YDSXG_EFFECT_VOICES 5 - -/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ -#define NR_AC97 2 - -#define YMF_SAMPF 256 /* Samples per frame @48000 */ - -/* - * The slot/voice control bank (2 of these per voice) - */ - -typedef struct stru_ymfpci_playback_bank { - u32 format; - u32 loop_default; - u32 base; /* 32-bit address */ - u32 loop_start; /* 32-bit offset */ - u32 loop_end; /* 32-bit offset */ - u32 loop_frac; /* 8-bit fraction - loop_start */ - u32 delta_end; /* pitch delta end */ - u32 lpfK_end; - u32 eg_gain_end; - u32 left_gain_end; - u32 right_gain_end; - u32 eff1_gain_end; - u32 eff2_gain_end; - u32 eff3_gain_end; - u32 lpfQ; - u32 status; /* P3: Always 0 for some reason. */ - u32 num_of_frames; - u32 loop_count; - u32 start; /* P3: J. reads this to know where chip is. */ - u32 start_frac; - u32 delta; - u32 lpfK; - u32 eg_gain; - u32 left_gain; - u32 right_gain; - u32 eff1_gain; - u32 eff2_gain; - u32 eff3_gain; - u32 lpfD1; - u32 lpfD2; -} ymfpci_playback_bank_t; - -typedef struct stru_ymfpci_capture_bank { - u32 base; /* 32-bit address (aligned at 4) */ - u32 loop_end; /* size in BYTES (aligned at 4) */ - u32 start; /* 32-bit offset */ - u32 num_of_loops; /* counter */ -} ymfpci_capture_bank_t; - -typedef struct stru_ymfpci_effect_bank { - u32 base; /* 32-bit address */ - u32 loop_end; /* 32-bit offset */ - u32 start; /* 32-bit offset */ - u32 temp; -} ymfpci_effect_bank_t; - -typedef struct ymf_voice ymfpci_voice_t; -/* - * Throughout the code Yaroslav names YMF unit pointer "codec" - * even though it does not correspond to any codec. Must be historic. - * We replace it with "unit" over time. - * AC97 parts use "codec" to denote a codec, naturally. - */ -typedef struct ymf_unit ymfpci_t; - -typedef enum { - YMFPCI_PCM, - YMFPCI_SYNTH, - YMFPCI_MIDI -} ymfpci_voice_type_t; - -struct ymf_voice { - // ymfpci_t *codec; - int number; - char use, pcm, synth, midi; // bool - ymfpci_playback_bank_t *bank; - struct ymf_pcm *ypcm; - dma_addr_t bank_ba; -}; - -struct ymf_capture { - // struct ymf_unit *unit; - int use; - ymfpci_capture_bank_t *bank; - struct ymf_pcm *ypcm; -}; - -struct ymf_unit { - u8 rev; /* PCI revision */ - void *reg_area_virt; - void *dma_area_va; - dma_addr_t dma_area_ba; - unsigned int dma_area_size; - - dma_addr_t bank_base_capture; - dma_addr_t bank_base_effect; - dma_addr_t work_base; - unsigned int work_size; - - u32 *ctrl_playback; - dma_addr_t ctrl_playback_ba; - ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2]; - ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2]; - ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2]; - - int start_count; - int suspended; - - u32 active_bank; - struct ymf_voice voices[YDSXG_PLAYBACK_VOICES]; - struct ymf_capture capture[YDSXG_CAPTURE_VOICES]; - - struct ac97_codec *ac97_codec[NR_AC97]; - u16 ac97_features; - - struct pci_dev *pci; - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - /* legacy hardware resources */ - unsigned int iosynth, iomidi; - struct address_info opl3_data, mpu_data; -#endif - - spinlock_t reg_lock; - spinlock_t voice_lock; - - /* soundcore stuff */ - int dev_audio; - struct semaphore open_sem; - - struct list_head ymf_devs; - struct list_head states; /* List of states for this unit */ -}; - -struct ymf_dmabuf { - dma_addr_t dma_addr; - void *rawbuf; - unsigned buforder; - - /* OSS buffer management stuff */ - unsigned numfrag; - unsigned fragshift; - - /* our buffer acts like a circular ring */ - unsigned hwptr; /* where dma last started */ - unsigned swptr; /* where driver last clear/filled */ - int count; /* fill count */ - unsigned total_bytes; /* total bytes dmaed by hardware */ - - wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ - - /* redundant, but makes calculations easier */ - unsigned fragsize; - unsigned dmasize; /* Total rawbuf[] size */ - - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; -}; - -struct ymf_pcm_format { - int format; /* OSS format */ - int rate; /* rate in Hz */ - int voices; /* number of voices */ - int shift; /* redundant, computed from the above */ -}; - -typedef enum { - PLAYBACK_VOICE, - CAPTURE_REC, - CAPTURE_AC97, - EFFECT_DRY_LEFT, - EFFECT_DRY_RIGHT, - EFFECT_EFF1, - EFFECT_EFF2, - EFFECT_EFF3 -} ymfpci_pcm_type_t; - -/* This is variant record, but we hate unions. Little waste on pointers []. */ -struct ymf_pcm { - ymfpci_pcm_type_t type; - struct ymf_state *state; - - ymfpci_voice_t *voices[2]; - int capture_bank_number; - - struct ymf_dmabuf dmabuf; - int running; - int spdif; -}; - -/* - * "Software" or virtual channel, an instance of opened /dev/dsp. - * It may have two physical channels (pcms) for duplex operations. - */ - -struct ymf_state { - struct list_head chain; - struct ymf_unit *unit; /* backpointer */ - struct ymf_pcm rpcm, wpcm; - struct ymf_pcm_format format; -}; - -#endif /* __YMFPCI_H */ diff -Nru a/drivers/sound/ymfpci_image.h b/drivers/sound/ymfpci_image.h --- a/drivers/sound/ymfpci_image.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1565 +0,0 @@ -#ifndef _HWMCODE_ -#define _HWMCODE_ - -static u32 DspInst[YDSXG_DSPLENGTH / 4] = { - 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, - 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, - 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, - 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000 -}; - -static u32 CntrlInst[YDSXG_CTRLLENGTH / 4] = { - 0x000007, 0x240007, 0x0C0007, 0x1C0007, - 0x060007, 0x700002, 0x000020, 0x030040, - 0x007104, 0x004286, 0x030040, 0x000F0D, - 0x000810, 0x20043A, 0x000282, 0x00020D, - 0x000810, 0x20043A, 0x001282, 0x200E82, - 0x001A82, 0x032D0D, 0x000810, 0x10043A, - 0x02D38D, 0x000810, 0x18043A, 0x00010D, - 0x020015, 0x0000FD, 0x000020, 0x038860, - 0x039060, 0x038060, 0x038040, 0x038040, - 0x038040, 0x018040, 0x000A7D, 0x038040, - 0x038040, 0x018040, 0x200402, 0x000882, - 0x08001A, 0x000904, 0x015986, 0x000007, - 0x260007, 0x000007, 0x000007, 0x018A06, - 0x000007, 0x030C8D, 0x000810, 0x18043A, - 0x260007, 0x00087D, 0x018042, 0x00160A, - 0x04A206, 0x000007, 0x00218D, 0x000810, - 0x08043A, 0x21C206, 0x000007, 0x0007FD, - 0x018042, 0x08000A, 0x000904, 0x029386, - 0x000195, 0x090D04, 0x000007, 0x000820, - 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, - 0x032206, 0x018040, 0x000A7D, 0x038042, - 0x13804A, 0x18000A, 0x001820, 0x059060, - 0x058860, 0x018040, 0x0000FD, 0x018042, - 0x70000A, 0x000115, 0x071144, 0x032386, - 0x030000, 0x007020, 0x034A06, 0x018040, - 0x00348D, 0x000810, 0x08043A, 0x21EA06, - 0x000007, 0x02D38D, 0x000810, 0x18043A, - 0x018206, 0x000007, 0x240007, 0x000F8D, - 0x000810, 0x00163A, 0x002402, 0x005C02, - 0x0028FD, 0x000020, 0x018040, 0x08000D, - 0x000815, 0x510984, 0x000007, 0x00004D, - 0x000E5D, 0x000E02, 0x00418D, 0x000810, - 0x08043A, 0x2C8A06, 0x000007, 0x00008D, - 0x000924, 0x000F02, 0x00458D, 0x000810, - 0x08043A, 0x2C8A06, 0x000007, 0x00387D, - 0x018042, 0x08000A, 0x001015, 0x010984, - 0x018386, 0x000007, 0x01AA06, 0x000007, - 0x0008FD, 0x018042, 0x18000A, 0x001904, - 0x218086, 0x280007, 0x001810, 0x28043A, - 0x280C02, 0x00000D, 0x000810, 0x28143A, - 0x08808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00020D, 0x189904, 0x000007, - 0x00402D, 0x0000BD, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x055A86, 0x000007, - 0x000100, 0x000A20, 0x00047D, 0x018040, - 0x018042, 0x20000A, 0x003015, 0x012144, - 0x034986, 0x000007, 0x002104, 0x034986, - 0x000007, 0x000F8D, 0x000810, 0x280C3A, - 0x023944, 0x06C986, 0x000007, 0x001810, - 0x28043A, 0x08810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x002810, 0x78003A, - 0x00688D, 0x000810, 0x08043A, 0x288A06, - 0x000007, 0x00400D, 0x001015, 0x189904, - 0x292904, 0x393904, 0x000007, 0x060206, - 0x000007, 0x0004F5, 0x00007D, 0x000020, - 0x00008D, 0x010860, 0x018040, 0x00047D, - 0x038042, 0x21804A, 0x18000A, 0x021944, - 0x215886, 0x000007, 0x004075, 0x71F104, - 0x000007, 0x010042, 0x28000A, 0x002904, - 0x212086, 0x000007, 0x003C0D, 0x30A904, - 0x000007, 0x00077D, 0x018042, 0x08000A, - 0x000904, 0x07DA86, 0x00057D, 0x002820, - 0x03B060, 0x07F206, 0x018040, 0x003020, - 0x03A860, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x07FA86, 0x000007, - 0x00057D, 0x018042, 0x28040A, 0x000E8D, - 0x000810, 0x280C3A, 0x00000D, 0x000810, - 0x28143A, 0x09000D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x003DFD, 0x000020, - 0x018040, 0x00107D, 0x008D8D, 0x000810, - 0x08043A, 0x288A06, 0x000007, 0x000815, - 0x08001A, 0x010984, 0x095186, 0x00137D, - 0x200500, 0x280F20, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x038A60, 0x018040, 0x007FBD, 0x383DC4, - 0x000007, 0x001A7D, 0x001375, 0x018042, - 0x09004A, 0x10000A, 0x0B8D04, 0x139504, - 0x000007, 0x000820, 0x019060, 0x001104, - 0x212086, 0x010040, 0x0017FD, 0x018042, - 0x08000A, 0x000904, 0x212286, 0x000007, - 0x00197D, 0x038042, 0x09804A, 0x10000A, - 0x000924, 0x001664, 0x0011FD, 0x038042, - 0x2B804A, 0x19804A, 0x00008D, 0x218944, - 0x000007, 0x002244, 0x0AE186, 0x000007, - 0x001A64, 0x002A24, 0x00197D, 0x080102, - 0x100122, 0x000820, 0x039060, 0x018040, - 0x003DFD, 0x00008D, 0x000820, 0x018040, - 0x001375, 0x001A7D, 0x010042, 0x09804A, - 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, - 0x309144, 0x000007, 0x00060D, 0x000A15, - 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, - 0x000464, 0x01B3E4, 0x0232E4, 0x000464, - 0x000464, 0x000464, 0x000464, 0x00040D, - 0x08B1C4, 0x000007, 0x000820, 0x000BF5, - 0x030040, 0x00197D, 0x038042, 0x09804A, - 0x000A24, 0x08000A, 0x080E64, 0x000007, - 0x100122, 0x000820, 0x031060, 0x010040, - 0x0064AC, 0x00027D, 0x000020, 0x018040, - 0x00107D, 0x018042, 0x0011FD, 0x3B804A, - 0x09804A, 0x20000A, 0x000095, 0x1A1144, - 0x00A144, 0x0D2086, 0x00040D, 0x00B984, - 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, - 0x09804A, 0x28000A, 0x000095, 0x010924, - 0x002A64, 0x0D1186, 0x000007, 0x002904, - 0x0D2286, 0x000007, 0x0D2A06, 0x080002, - 0x00008D, 0x00387D, 0x000820, 0x018040, - 0x00127D, 0x018042, 0x10000A, 0x003904, - 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, - 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, - 0x000015, 0x00082D, 0x02C78D, 0x000820, - 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, - 0x0E7186, 0x400025, 0x00008D, 0x110944, - 0x000007, 0x00018D, 0x109504, 0x000007, - 0x009164, 0x000424, 0x000424, 0x000424, - 0x100102, 0x280002, 0x02C68D, 0x000820, - 0x0EC206, 0x00018D, 0x00042D, 0x00008D, - 0x109504, 0x000007, 0x00020D, 0x109184, - 0x000007, 0x02C70D, 0x000820, 0x00008D, - 0x0038FD, 0x018040, 0x003BFD, 0x001020, - 0x03A860, 0x000815, 0x313184, 0x212184, - 0x000007, 0x03B060, 0x03A060, 0x018040, - 0x0022FD, 0x000095, 0x010924, 0x000424, - 0x000424, 0x001264, 0x100102, 0x000820, - 0x039060, 0x018040, 0x001924, 0x00FB8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x000424, - 0x000424, 0x00117D, 0x018042, 0x08000A, - 0x000A24, 0x280502, 0x280C02, 0x09800D, - 0x000820, 0x0002FD, 0x018040, 0x200007, - 0x0022FD, 0x018042, 0x08000A, 0x000095, - 0x280DC4, 0x011924, 0x00197D, 0x018042, - 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, - 0x113144, 0x0A8D04, 0x000007, 0x080A44, - 0x129504, 0x000007, 0x0023FD, 0x001020, - 0x038040, 0x101244, 0x000007, 0x000820, - 0x039060, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x10FA86, 0x000007, - 0x003BFD, 0x000100, 0x000A10, 0x0B807A, - 0x13804A, 0x090984, 0x000007, 0x000095, - 0x013D04, 0x118086, 0x10000A, 0x100002, - 0x090984, 0x000007, 0x038042, 0x11804A, - 0x090D04, 0x000007, 0x10000A, 0x090D84, - 0x000007, 0x00257D, 0x000820, 0x018040, - 0x00010D, 0x000810, 0x28143A, 0x00127D, - 0x018042, 0x20000A, 0x00197D, 0x018042, - 0x00117D, 0x31804A, 0x10000A, 0x003124, - 0x01280D, 0x00397D, 0x000820, 0x058040, - 0x038042, 0x09844A, 0x000606, 0x08040A, - 0x300102, 0x003124, 0x000424, 0x000424, - 0x001224, 0x280502, 0x001A4C, 0x130186, - 0x700002, 0x00002D, 0x030000, 0x00387D, - 0x018042, 0x10000A, 0x132A06, 0x002124, - 0x0000AD, 0x100002, 0x00010D, 0x000924, - 0x006B24, 0x01368D, 0x00397D, 0x000820, - 0x058040, 0x038042, 0x09844A, 0x000606, - 0x08040A, 0x003264, 0x00008D, 0x000A24, - 0x001020, 0x00227D, 0x018040, 0x013C0D, - 0x000810, 0x08043A, 0x29D206, 0x000007, - 0x002820, 0x00207D, 0x018040, 0x00117D, - 0x038042, 0x13804A, 0x33800A, 0x00387D, - 0x018042, 0x08000A, 0x000904, 0x163A86, - 0x000007, 0x00008D, 0x030964, 0x01478D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x380102, - 0x000424, 0x000424, 0x001224, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x14A286, - 0x000007, 0x280502, 0x001A4C, 0x163986, - 0x000007, 0x032164, 0x00632C, 0x003DFD, - 0x018042, 0x08000A, 0x000095, 0x090904, - 0x000007, 0x000820, 0x001A4C, 0x156186, - 0x018040, 0x030000, 0x157A06, 0x002124, - 0x00010D, 0x000924, 0x006B24, 0x015B8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x003A64, - 0x000095, 0x001224, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x15DA86, 0x000007, - 0x01628D, 0x000810, 0x08043A, 0x29D206, - 0x000007, 0x14D206, 0x000007, 0x007020, - 0x08010A, 0x10012A, 0x0020FD, 0x038860, - 0x039060, 0x018040, 0x00227D, 0x018042, - 0x003DFD, 0x08000A, 0x31844A, 0x000904, - 0x16D886, 0x18008B, 0x00008D, 0x189904, - 0x00312C, 0x17AA06, 0x000007, 0x00324C, - 0x173386, 0x000007, 0x001904, 0x173086, - 0x000007, 0x000095, 0x199144, 0x00222C, - 0x003124, 0x00636C, 0x000E3D, 0x001375, - 0x000BFD, 0x010042, 0x09804A, 0x10000A, - 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, - 0x000007, 0x00008D, 0x189904, 0x00226C, - 0x00322C, 0x30050A, 0x301DAB, 0x002083, - 0x0018FD, 0x018042, 0x08000A, 0x018924, - 0x300502, 0x001083, 0x001875, 0x010042, - 0x10000A, 0x00008D, 0x010924, 0x001375, - 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, - 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, - 0x305C8B, 0x006083, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x187A86, 0x000007, - 0x001E2D, 0x0005FD, 0x018042, 0x08000A, - 0x028924, 0x280502, 0x00060D, 0x000810, - 0x280C3A, 0x00008D, 0x000810, 0x28143A, - 0x0A808D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x001275, 0x030042, 0x21004A, - 0x00008D, 0x1A0944, 0x000007, 0x01980D, - 0x000810, 0x08043A, 0x2B2206, 0x000007, - 0x0001F5, 0x030042, 0x0D004A, 0x10000A, - 0x089144, 0x000007, 0x000820, 0x010040, - 0x0025F5, 0x0A3144, 0x000007, 0x000820, - 0x032860, 0x030040, 0x00217D, 0x038042, - 0x0B804A, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x00008D, 0x000124, 0x00012C, - 0x000E64, 0x001A64, 0x00636C, 0x08010A, - 0x10012A, 0x000820, 0x031060, 0x030040, - 0x0020FD, 0x018042, 0x08000A, 0x00227D, - 0x018042, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x00197D, 0x018042, 0x08000A, - 0x0022FD, 0x038042, 0x10000A, 0x000820, - 0x031060, 0x030040, 0x090D04, 0x000007, - 0x000820, 0x030040, 0x038042, 0x0B804A, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x038042, 0x13804A, 0x19804A, 0x110D04, - 0x198D04, 0x000007, 0x08000A, 0x001020, - 0x031860, 0x030860, 0x030040, 0x00008D, - 0x0B0944, 0x000007, 0x000820, 0x010040, - 0x0005F5, 0x030042, 0x08000A, 0x000820, - 0x010040, 0x0000F5, 0x010042, 0x08000A, - 0x000904, 0x1C6086, 0x001E75, 0x030042, - 0x01044A, 0x000C0A, 0x1C7206, 0x000007, - 0x000402, 0x000C02, 0x00177D, 0x001AF5, - 0x018042, 0x03144A, 0x031C4A, 0x03244A, - 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, - 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, - 0x030042, 0x0B004A, 0x1B804A, 0x13804A, - 0x20000A, 0x089144, 0x19A144, 0x0389E4, - 0x0399EC, 0x005502, 0x005D0A, 0x030042, - 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, - 0x089144, 0x19A144, 0x0389E4, 0x0399EC, - 0x006502, 0x006D0A, 0x030042, 0x0B004A, - 0x19004A, 0x2B804A, 0x13804A, 0x21804A, - 0x30000A, 0x089144, 0x19A144, 0x2AB144, - 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, - 0x03A9E4, 0x000702, 0x00107D, 0x000415, - 0x018042, 0x08000A, 0x0109E4, 0x000F02, - 0x002AF5, 0x0019FD, 0x010042, 0x09804A, - 0x10000A, 0x000934, 0x001674, 0x0029F5, - 0x010042, 0x10000A, 0x00917C, 0x002075, - 0x010042, 0x08000A, 0x000904, 0x1ED286, - 0x0026F5, 0x0027F5, 0x030042, 0x09004A, - 0x10000A, 0x000A3C, 0x00167C, 0x001A75, - 0x000BFD, 0x010042, 0x51804A, 0x48000A, - 0x160007, 0x001075, 0x010042, 0x282C0A, - 0x281D12, 0x282512, 0x001F32, 0x1E0007, - 0x0E0007, 0x001975, 0x010042, 0x002DF5, - 0x0D004A, 0x10000A, 0x009144, 0x1FB286, - 0x010042, 0x28340A, 0x000E5D, 0x00008D, - 0x000375, 0x000820, 0x010040, 0x05D2F4, - 0x54D104, 0x00735C, 0x205386, 0x000007, - 0x0C0007, 0x080007, 0x0A0007, 0x02040D, - 0x000810, 0x08043A, 0x332206, 0x000007, - 0x205A06, 0x000007, 0x080007, 0x002275, - 0x010042, 0x20000A, 0x002104, 0x212086, - 0x001E2D, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x209286, 0x000007, 0x002010, - 0x30043A, 0x00057D, 0x0180C3, 0x08000A, - 0x028924, 0x280502, 0x280C02, 0x0A810D, - 0x000820, 0x0002F5, 0x010040, 0x220007, - 0x0004FD, 0x018042, 0x70000A, 0x030000, - 0x007020, 0x06FA06, 0x018040, 0x02180D, - 0x000810, 0x08043A, 0x2B2206, 0x000007, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x218A86, 0x000007, 0x01F206, 0x000007, - 0x000875, 0x0009FD, 0x00010D, 0x220A06, - 0x000295, 0x000B75, 0x00097D, 0x00000D, - 0x000515, 0x010042, 0x18000A, 0x001904, - 0x287886, 0x0006F5, 0x001020, 0x010040, - 0x0004F5, 0x000820, 0x010040, 0x000775, - 0x010042, 0x09804A, 0x10000A, 0x001124, - 0x000904, 0x22BA86, 0x000815, 0x080102, - 0x101204, 0x22DA06, 0x000575, 0x081204, - 0x000007, 0x100102, 0x000575, 0x000425, - 0x021124, 0x100102, 0x000820, 0x031060, - 0x010040, 0x001924, 0x287886, 0x00008D, - 0x000464, 0x009D04, 0x278886, 0x180102, - 0x000575, 0x010042, 0x28040A, 0x00018D, - 0x000924, 0x280D02, 0x00000D, 0x000924, - 0x281502, 0x10000D, 0x000820, 0x0002F5, - 0x010040, 0x200007, 0x001175, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x23C286, - 0x000007, 0x000100, 0x080B20, 0x130B60, - 0x1B0B60, 0x030A60, 0x010040, 0x050042, - 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, - 0x0006F5, 0x010042, 0x28140A, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x24CA86, 0x004015, 0x000095, 0x010D04, - 0x24B886, 0x100022, 0x10002A, 0x24E206, - 0x000007, 0x333104, 0x2AA904, 0x000007, - 0x032124, 0x280502, 0x001124, 0x000424, - 0x000424, 0x003224, 0x00292C, 0x00636C, - 0x25F386, 0x000007, 0x02B164, 0x000464, - 0x000464, 0x00008D, 0x000A64, 0x280D02, - 0x10008D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x00008D, 0x38B904, 0x000007, - 0x03296C, 0x30010A, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x25BA86, 0x000007, - 0x02312C, 0x28050A, 0x00008D, 0x01096C, - 0x280D0A, 0x10010D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x001124, 0x000424, - 0x000424, 0x003224, 0x300102, 0x032944, - 0x267A86, 0x000007, 0x300002, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x26C086, 0x003124, 0x000464, 0x300102, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x26CA86, 0x000007, 0x003124, 0x300502, - 0x003924, 0x300583, 0x000883, 0x0005F5, - 0x010042, 0x28040A, 0x00008D, 0x008124, - 0x280D02, 0x00008D, 0x008124, 0x281502, - 0x10018D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x001025, 0x000575, 0x030042, - 0x09004A, 0x10000A, 0x0A0904, 0x121104, - 0x000007, 0x001020, 0x050860, 0x050040, - 0x0006FD, 0x018042, 0x09004A, 0x10000A, - 0x0000A5, 0x0A0904, 0x121104, 0x000007, - 0x000820, 0x019060, 0x010040, 0x0002F5, - 0x010042, 0x08000A, 0x000904, 0x284286, - 0x000007, 0x230A06, 0x000007, 0x000606, - 0x000007, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x289286, 0x000007, 0x000100, - 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, - 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, - 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, - 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, - 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, - 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, - 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, - 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, - 0x000606, 0x018040, 0x00008D, 0x000A64, - 0x280D02, 0x000A24, 0x00027D, 0x018042, - 0x10000A, 0x001224, 0x0003FD, 0x018042, - 0x08000A, 0x000904, 0x2A8286, 0x000007, - 0x00018D, 0x000A24, 0x000464, 0x000464, - 0x080102, 0x000924, 0x000424, 0x000424, - 0x100102, 0x02000D, 0x009144, 0x2AD986, - 0x000007, 0x0001FD, 0x018042, 0x08000A, - 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, - 0x000820, 0x0002FD, 0x018040, 0x200007, - 0x00027D, 0x001020, 0x000606, 0x018040, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x2B2A86, 0x000007, 0x00037D, 0x018042, - 0x08000A, 0x000904, 0x2B5A86, 0x000007, - 0x000075, 0x002E7D, 0x010042, 0x0B804A, - 0x000020, 0x000904, 0x000686, 0x010040, - 0x31844A, 0x30048B, 0x000883, 0x00008D, - 0x000810, 0x28143A, 0x00008D, 0x000810, - 0x280C3A, 0x000675, 0x010042, 0x08000A, - 0x003815, 0x010924, 0x280502, 0x0B000D, - 0x000820, 0x0002F5, 0x010040, 0x000606, - 0x220007, 0x000464, 0x000464, 0x000606, - 0x000007, 0x000134, 0x007F8D, 0x00093C, - 0x281D12, 0x282512, 0x001F32, 0x0E0007, - 0x00010D, 0x00037D, 0x000820, 0x018040, - 0x05D2F4, 0x000007, 0x080007, 0x00037D, - 0x018042, 0x08000A, 0x000904, 0x2D0286, - 0x000007, 0x000606, 0x000007, 0x000007, - 0x000012, 0x100007, 0x320007, 0x600007, - 0x100080, 0x48001A, 0x004904, 0x2D6186, - 0x000007, 0x001210, 0x58003A, 0x000145, - 0x5C5D04, 0x000007, 0x000080, 0x48001A, - 0x004904, 0x2DB186, 0x000007, 0x001210, - 0x50003A, 0x005904, 0x2E0886, 0x000045, - 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, - 0x004224, 0x500102, 0x200502, 0x000082, - 0x40001A, 0x004104, 0x2E3986, 0x000007, - 0x003865, 0x40001A, 0x004020, 0x00104D, - 0x04C184, 0x301B86, 0x000040, 0x040007, - 0x000165, 0x000145, 0x004020, 0x000040, - 0x000765, 0x080080, 0x40001A, 0x004104, - 0x2EC986, 0x000007, 0x001210, 0x40003A, - 0x004104, 0x2F2286, 0x00004D, 0x0000CD, - 0x004810, 0x20043A, 0x000882, 0x40001A, - 0x004104, 0x2F3186, 0x000007, 0x004820, - 0x005904, 0x300886, 0x000040, 0x0007E5, - 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, - 0x4216E0, 0x021260, 0x000040, 0x000032, - 0x400075, 0x00007D, 0x07D574, 0x200512, - 0x000082, 0x40001A, 0x004104, 0x2FE186, - 0x000007, 0x037206, 0x640007, 0x060007, - 0x0000E5, 0x000020, 0x000040, 0x000A65, - 0x000020, 0x020040, 0x020040, 0x000040, - 0x000165, 0x000042, 0x70000A, 0x007104, - 0x30A286, 0x000007, 0x018206, 0x640007, - 0x050000, 0x007020, 0x000040, 0x037206, - 0x640007, 0x000007, 0x00306D, 0x028860, - 0x029060, 0x08000A, 0x028860, 0x008040, - 0x100012, 0x00100D, 0x009184, 0x314186, - 0x000E0D, 0x009184, 0x325186, 0x000007, - 0x300007, 0x001020, 0x003B6D, 0x008040, - 0x000080, 0x08001A, 0x000904, 0x316186, - 0x000007, 0x001220, 0x000DED, 0x008040, - 0x008042, 0x10000A, 0x40000D, 0x109544, - 0x000007, 0x001020, 0x000DED, 0x008040, - 0x008042, 0x20040A, 0x000082, 0x08001A, - 0x000904, 0x31F186, 0x000007, 0x003B6D, - 0x008042, 0x08000A, 0x000E15, 0x010984, - 0x329B86, 0x600007, 0x08001A, 0x000C15, - 0x010984, 0x328386, 0x000020, 0x1A0007, - 0x0002ED, 0x008040, 0x620007, 0x00306D, - 0x028042, 0x0A804A, 0x000820, 0x0A804A, - 0x000606, 0x10804A, 0x000007, 0x282512, - 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, - 0x000786, 0x000007, 0x0C0007, 0x0A0007, - 0x1C0007, 0x003465, 0x020040, 0x004820, - 0x025060, 0x40000A, 0x024060, 0x000040, - 0x454944, 0x000007, 0x004020, 0x003AE5, - 0x000040, 0x0028E5, 0x000042, 0x48000A, - 0x004904, 0x386886, 0x002C65, 0x000042, - 0x40000A, 0x0000D5, 0x454104, 0x000007, - 0x000655, 0x054504, 0x34F286, 0x0001D5, - 0x054504, 0x34F086, 0x002B65, 0x000042, - 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, - 0x000007, 0x454504, 0x000007, 0x0000CD, - 0x444944, 0x000007, 0x454504, 0x000007, - 0x00014D, 0x554944, 0x000007, 0x045144, - 0x34E986, 0x002C65, 0x000042, 0x48000A, - 0x4CD104, 0x000007, 0x04C144, 0x34F386, - 0x000007, 0x160007, 0x002CE5, 0x040042, - 0x40000A, 0x004020, 0x000040, 0x002965, - 0x000042, 0x40000A, 0x004104, 0x356086, - 0x000007, 0x002402, 0x36A206, 0x005C02, - 0x0025E5, 0x000042, 0x40000A, 0x004274, - 0x002AE5, 0x000042, 0x40000A, 0x004274, - 0x500112, 0x0029E5, 0x000042, 0x40000A, - 0x004234, 0x454104, 0x000007, 0x004020, - 0x000040, 0x003EE5, 0x000020, 0x000040, - 0x002DE5, 0x400152, 0x50000A, 0x045144, - 0x364A86, 0x0000C5, 0x003EE5, 0x004020, - 0x000040, 0x002BE5, 0x000042, 0x40000A, - 0x404254, 0x000007, 0x002AE5, 0x004020, - 0x000040, 0x500132, 0x040134, 0x005674, - 0x0029E5, 0x020042, 0x42000A, 0x000042, - 0x50000A, 0x05417C, 0x0028E5, 0x000042, - 0x48000A, 0x0000C5, 0x4CC144, 0x371086, - 0x0026E5, 0x0027E5, 0x020042, 0x40004A, - 0x50000A, 0x00423C, 0x00567C, 0x0028E5, - 0x004820, 0x000040, 0x281D12, 0x282512, - 0x001F72, 0x002965, 0x000042, 0x40000A, - 0x004104, 0x37AA86, 0x0E0007, 0x160007, - 0x1E0007, 0x003EE5, 0x000042, 0x40000A, - 0x004104, 0x37E886, 0x002D65, 0x000042, - 0x28340A, 0x003465, 0x020042, 0x42004A, - 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, - 0x54D104, 0x00735C, 0x385186, 0x000007, - 0x000606, 0x080007, 0x0C0007, 0x080007, - 0x0A0007, 0x0001E5, 0x020045, 0x004020, - 0x000060, 0x000365, 0x000040, 0x002E65, - 0x001A20, 0x0A1A60, 0x000040, 0x003465, - 0x020042, 0x42004A, 0x004020, 0x4A004A, - 0x000606, 0x50004A, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000 -}; - -// -------------------------------------------- -// DS-1E Controller InstructionRAM Code -// 1999/06/21 -// Buf441 slot is Enabled. -// -------------------------------------------- -// 04/09 creat -// 04/12 stop nise fix -// 06/21 WorkingOff timming -static u32 CntrlInst1E[YDSXG_CTRLLENGTH / 4] = { - 0x000007, 0x240007, 0x0C0007, 0x1C0007, - 0x060007, 0x700002, 0x000020, 0x030040, - 0x007104, 0x004286, 0x030040, 0x000F0D, - 0x000810, 0x20043A, 0x000282, 0x00020D, - 0x000810, 0x20043A, 0x001282, 0x200E82, - 0x00800D, 0x000810, 0x20043A, 0x001A82, - 0x03460D, 0x000810, 0x10043A, 0x02EC0D, - 0x000810, 0x18043A, 0x00010D, 0x020015, - 0x0000FD, 0x000020, 0x038860, 0x039060, - 0x038060, 0x038040, 0x038040, 0x038040, - 0x018040, 0x000A7D, 0x038040, 0x038040, - 0x018040, 0x200402, 0x000882, 0x08001A, - 0x000904, 0x017186, 0x000007, 0x260007, - 0x400007, 0x000007, 0x03258D, 0x000810, - 0x18043A, 0x260007, 0x284402, 0x00087D, - 0x018042, 0x00160A, 0x05A206, 0x000007, - 0x440007, 0x00230D, 0x000810, 0x08043A, - 0x22FA06, 0x000007, 0x0007FD, 0x018042, - 0x08000A, 0x000904, 0x02AB86, 0x000195, - 0x090D04, 0x000007, 0x000820, 0x0000F5, - 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, - 0x018040, 0x000A7D, 0x038042, 0x13804A, - 0x18000A, 0x001820, 0x059060, 0x058860, - 0x018040, 0x0000FD, 0x018042, 0x70000A, - 0x000115, 0x071144, 0x033B86, 0x030000, - 0x007020, 0x036206, 0x018040, 0x00360D, - 0x000810, 0x08043A, 0x232206, 0x000007, - 0x02EC0D, 0x000810, 0x18043A, 0x019A06, - 0x000007, 0x240007, 0x000F8D, 0x000810, - 0x00163A, 0x002402, 0x005C02, 0x0028FD, - 0x000020, 0x018040, 0x08000D, 0x000815, - 0x510984, 0x000007, 0x00004D, 0x000E5D, - 0x000E02, 0x00430D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x00008D, 0x000924, - 0x000F02, 0x00470D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x480480, 0x001210, - 0x28043A, 0x00778D, 0x000810, 0x280C3A, - 0x00068D, 0x000810, 0x28143A, 0x284402, - 0x03258D, 0x000810, 0x18043A, 0x07FF8D, - 0x000820, 0x0002FD, 0x018040, 0x260007, - 0x200007, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x051286, 0x000007, 0x240007, - 0x02EC0D, 0x000810, 0x18043A, 0x00387D, - 0x018042, 0x08000A, 0x001015, 0x010984, - 0x019B86, 0x000007, 0x01B206, 0x000007, - 0x0008FD, 0x018042, 0x18000A, 0x001904, - 0x22B886, 0x280007, 0x001810, 0x28043A, - 0x280C02, 0x00000D, 0x000810, 0x28143A, - 0x08808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00020D, 0x189904, 0x000007, - 0x00402D, 0x0000BD, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x065A86, 0x000007, - 0x000100, 0x000A20, 0x00047D, 0x018040, - 0x018042, 0x20000A, 0x003015, 0x012144, - 0x036186, 0x000007, 0x002104, 0x036186, - 0x000007, 0x000F8D, 0x000810, 0x280C3A, - 0x023944, 0x07C986, 0x000007, 0x001810, - 0x28043A, 0x08810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x002810, 0x78003A, - 0x00788D, 0x000810, 0x08043A, 0x2A1206, - 0x000007, 0x00400D, 0x001015, 0x189904, - 0x292904, 0x393904, 0x000007, 0x070206, - 0x000007, 0x0004F5, 0x00007D, 0x000020, - 0x00008D, 0x010860, 0x018040, 0x00047D, - 0x038042, 0x21804A, 0x18000A, 0x021944, - 0x229086, 0x000007, 0x004075, 0x71F104, - 0x000007, 0x010042, 0x28000A, 0x002904, - 0x225886, 0x000007, 0x003C0D, 0x30A904, - 0x000007, 0x00077D, 0x018042, 0x08000A, - 0x000904, 0x08DA86, 0x00057D, 0x002820, - 0x03B060, 0x08F206, 0x018040, 0x003020, - 0x03A860, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x08FA86, 0x000007, - 0x00057D, 0x018042, 0x28040A, 0x000E8D, - 0x000810, 0x280C3A, 0x00000D, 0x000810, - 0x28143A, 0x09000D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x003DFD, 0x000020, - 0x018040, 0x00107D, 0x009D8D, 0x000810, - 0x08043A, 0x2A1206, 0x000007, 0x000815, - 0x08001A, 0x010984, 0x0A5186, 0x00137D, - 0x200500, 0x280F20, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x038A60, 0x018040, 0x00107D, 0x018042, - 0x08000A, 0x000215, 0x010984, 0x3A8186, - 0x000007, 0x007FBD, 0x383DC4, 0x000007, - 0x001A7D, 0x001375, 0x018042, 0x09004A, - 0x10000A, 0x0B8D04, 0x139504, 0x000007, - 0x000820, 0x019060, 0x001104, 0x225886, - 0x010040, 0x0017FD, 0x018042, 0x08000A, - 0x000904, 0x225A86, 0x000007, 0x00197D, - 0x038042, 0x09804A, 0x10000A, 0x000924, - 0x001664, 0x0011FD, 0x038042, 0x2B804A, - 0x19804A, 0x00008D, 0x218944, 0x000007, - 0x002244, 0x0C1986, 0x000007, 0x001A64, - 0x002A24, 0x00197D, 0x080102, 0x100122, - 0x000820, 0x039060, 0x018040, 0x003DFD, - 0x00008D, 0x000820, 0x018040, 0x001375, - 0x001A7D, 0x010042, 0x09804A, 0x10000A, - 0x00021D, 0x0189E4, 0x2992E4, 0x309144, - 0x000007, 0x00060D, 0x000A15, 0x000C1D, - 0x001025, 0x00A9E4, 0x012BE4, 0x000464, - 0x01B3E4, 0x0232E4, 0x000464, 0x000464, - 0x000464, 0x000464, 0x00040D, 0x08B1C4, - 0x000007, 0x000820, 0x000BF5, 0x030040, - 0x00197D, 0x038042, 0x09804A, 0x000A24, - 0x08000A, 0x080E64, 0x000007, 0x100122, - 0x000820, 0x031060, 0x010040, 0x0064AC, - 0x00027D, 0x000020, 0x018040, 0x00107D, - 0x018042, 0x0011FD, 0x3B804A, 0x09804A, - 0x20000A, 0x000095, 0x1A1144, 0x00A144, - 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, - 0x0018FD, 0x018042, 0x0010FD, 0x09804A, - 0x28000A, 0x000095, 0x010924, 0x002A64, - 0x0E4986, 0x000007, 0x002904, 0x0E5A86, - 0x000007, 0x0E6206, 0x080002, 0x00008D, - 0x00387D, 0x000820, 0x018040, 0x00127D, - 0x018042, 0x10000A, 0x003904, 0x0F0986, - 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, - 0x000025, 0x0FB206, 0x00002D, 0x000015, - 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, - 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, - 0x400025, 0x00008D, 0x110944, 0x000007, - 0x00018D, 0x109504, 0x000007, 0x009164, - 0x000424, 0x000424, 0x000424, 0x100102, - 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, - 0x00018D, 0x00042D, 0x00008D, 0x109504, - 0x000007, 0x00020D, 0x109184, 0x000007, - 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, - 0x018040, 0x003BFD, 0x001020, 0x03A860, - 0x000815, 0x313184, 0x212184, 0x000007, - 0x03B060, 0x03A060, 0x018040, 0x0022FD, - 0x000095, 0x010924, 0x000424, 0x000424, - 0x001264, 0x100102, 0x000820, 0x039060, - 0x018040, 0x001924, 0x010F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x000424, 0x000424, - 0x00117D, 0x018042, 0x08000A, 0x000A24, - 0x280502, 0x280C02, 0x09800D, 0x000820, - 0x0002FD, 0x018040, 0x200007, 0x0022FD, - 0x018042, 0x08000A, 0x000095, 0x280DC4, - 0x011924, 0x00197D, 0x018042, 0x0011FD, - 0x09804A, 0x10000A, 0x0000B5, 0x113144, - 0x0A8D04, 0x000007, 0x080A44, 0x129504, - 0x000007, 0x0023FD, 0x001020, 0x038040, - 0x101244, 0x000007, 0x000820, 0x039060, - 0x018040, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x123286, 0x000007, 0x003BFD, - 0x000100, 0x000A10, 0x0B807A, 0x13804A, - 0x090984, 0x000007, 0x000095, 0x013D04, - 0x12B886, 0x10000A, 0x100002, 0x090984, - 0x000007, 0x038042, 0x11804A, 0x090D04, - 0x000007, 0x10000A, 0x090D84, 0x000007, - 0x00257D, 0x000820, 0x018040, 0x00010D, - 0x000810, 0x28143A, 0x00127D, 0x018042, - 0x20000A, 0x00197D, 0x018042, 0x00117D, - 0x31804A, 0x10000A, 0x003124, 0x013B8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x300102, - 0x003124, 0x000424, 0x000424, 0x001224, - 0x280502, 0x001A4C, 0x143986, 0x700002, - 0x00002D, 0x030000, 0x00387D, 0x018042, - 0x10000A, 0x146206, 0x002124, 0x0000AD, - 0x100002, 0x00010D, 0x000924, 0x006B24, - 0x014A0D, 0x00397D, 0x000820, 0x058040, - 0x038042, 0x09844A, 0x000606, 0x08040A, - 0x003264, 0x00008D, 0x000A24, 0x001020, - 0x00227D, 0x018040, 0x014F8D, 0x000810, - 0x08043A, 0x2B5A06, 0x000007, 0x002820, - 0x00207D, 0x018040, 0x00117D, 0x038042, - 0x13804A, 0x33800A, 0x00387D, 0x018042, - 0x08000A, 0x000904, 0x177286, 0x000007, - 0x00008D, 0x030964, 0x015B0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x380102, 0x000424, - 0x000424, 0x001224, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x15DA86, 0x000007, - 0x280502, 0x001A4C, 0x177186, 0x000007, - 0x032164, 0x00632C, 0x003DFD, 0x018042, - 0x08000A, 0x000095, 0x090904, 0x000007, - 0x000820, 0x001A4C, 0x169986, 0x018040, - 0x030000, 0x16B206, 0x002124, 0x00010D, - 0x000924, 0x006B24, 0x016F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x003A64, 0x000095, - 0x001224, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x171286, 0x000007, 0x01760D, - 0x000810, 0x08043A, 0x2B5A06, 0x000007, - 0x160A06, 0x000007, 0x007020, 0x08010A, - 0x10012A, 0x0020FD, 0x038860, 0x039060, - 0x018040, 0x00227D, 0x018042, 0x003DFD, - 0x08000A, 0x31844A, 0x000904, 0x181086, - 0x18008B, 0x00008D, 0x189904, 0x00312C, - 0x18E206, 0x000007, 0x00324C, 0x186B86, - 0x000007, 0x001904, 0x186886, 0x000007, - 0x000095, 0x199144, 0x00222C, 0x003124, - 0x00636C, 0x000E3D, 0x001375, 0x000BFD, - 0x010042, 0x09804A, 0x10000A, 0x038AEC, - 0x0393EC, 0x00224C, 0x18E186, 0x000007, - 0x00008D, 0x189904, 0x00226C, 0x00322C, - 0x30050A, 0x301DAB, 0x002083, 0x0018FD, - 0x018042, 0x08000A, 0x018924, 0x300502, - 0x001083, 0x001875, 0x010042, 0x10000A, - 0x00008D, 0x010924, 0x001375, 0x330542, - 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, - 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, - 0x006083, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x19B286, 0x000007, 0x001E2D, - 0x0005FD, 0x018042, 0x08000A, 0x028924, - 0x280502, 0x00060D, 0x000810, 0x280C3A, - 0x00008D, 0x000810, 0x28143A, 0x0A808D, - 0x000820, 0x0002F5, 0x010040, 0x220007, - 0x001275, 0x030042, 0x21004A, 0x00008D, - 0x1A0944, 0x000007, 0x01AB8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, - 0x030042, 0x0D004A, 0x10000A, 0x089144, - 0x000007, 0x000820, 0x010040, 0x0025F5, - 0x0A3144, 0x000007, 0x000820, 0x032860, - 0x030040, 0x00217D, 0x038042, 0x0B804A, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00008D, 0x000124, 0x00012C, 0x000E64, - 0x001A64, 0x00636C, 0x08010A, 0x10012A, - 0x000820, 0x031060, 0x030040, 0x0020FD, - 0x018042, 0x08000A, 0x00227D, 0x018042, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00197D, 0x018042, 0x08000A, 0x0022FD, - 0x038042, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x090D04, 0x000007, 0x000820, - 0x030040, 0x038042, 0x0B804A, 0x10000A, - 0x000820, 0x031060, 0x030040, 0x038042, - 0x13804A, 0x19804A, 0x110D04, 0x198D04, - 0x000007, 0x08000A, 0x001020, 0x031860, - 0x030860, 0x030040, 0x00008D, 0x0B0944, - 0x000007, 0x000820, 0x010040, 0x0005F5, - 0x030042, 0x08000A, 0x000820, 0x010040, - 0x0000F5, 0x010042, 0x08000A, 0x000904, - 0x1D9886, 0x001E75, 0x030042, 0x01044A, - 0x000C0A, 0x1DAA06, 0x000007, 0x000402, - 0x000C02, 0x00177D, 0x001AF5, 0x018042, - 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, - 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, - 0x00043D, 0x0013F5, 0x001AFD, 0x030042, - 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, - 0x089144, 0x19A144, 0x0389E4, 0x0399EC, - 0x005502, 0x005D0A, 0x030042, 0x0B004A, - 0x1B804A, 0x13804A, 0x20000A, 0x089144, - 0x19A144, 0x0389E4, 0x0399EC, 0x006502, - 0x006D0A, 0x030042, 0x0B004A, 0x19004A, - 0x2B804A, 0x13804A, 0x21804A, 0x30000A, - 0x089144, 0x19A144, 0x2AB144, 0x0389E4, - 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, - 0x000702, 0x00107D, 0x000415, 0x018042, - 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, - 0x0019FD, 0x010042, 0x09804A, 0x10000A, - 0x000934, 0x001674, 0x0029F5, 0x010042, - 0x10000A, 0x00917C, 0x002075, 0x010042, - 0x08000A, 0x000904, 0x200A86, 0x0026F5, - 0x0027F5, 0x030042, 0x09004A, 0x10000A, - 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, - 0x010042, 0x51804A, 0x48000A, 0x160007, - 0x001075, 0x010042, 0x282C0A, 0x281D12, - 0x282512, 0x001F32, 0x1E0007, 0x0E0007, - 0x001975, 0x010042, 0x002DF5, 0x0D004A, - 0x10000A, 0x009144, 0x20EA86, 0x010042, - 0x28340A, 0x000E5D, 0x00008D, 0x000375, - 0x000820, 0x010040, 0x05D2F4, 0x54D104, - 0x00735C, 0x218B86, 0x000007, 0x0C0007, - 0x080007, 0x0A0007, 0x02178D, 0x000810, - 0x08043A, 0x34B206, 0x000007, 0x219206, - 0x000007, 0x080007, 0x002275, 0x010042, - 0x20000A, 0x002104, 0x225886, 0x001E2D, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x21CA86, 0x000007, 0x002010, 0x30043A, - 0x00057D, 0x0180C3, 0x08000A, 0x028924, - 0x280502, 0x280C02, 0x0A810D, 0x000820, - 0x0002F5, 0x010040, 0x220007, 0x0004FD, - 0x018042, 0x70000A, 0x030000, 0x007020, - 0x07FA06, 0x018040, 0x022B8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x22C286, - 0x000007, 0x020206, 0x000007, 0x000875, - 0x0009FD, 0x00010D, 0x234206, 0x000295, - 0x000B75, 0x00097D, 0x00000D, 0x000515, - 0x010042, 0x18000A, 0x001904, 0x2A0086, - 0x0006F5, 0x001020, 0x010040, 0x0004F5, - 0x000820, 0x010040, 0x000775, 0x010042, - 0x09804A, 0x10000A, 0x001124, 0x000904, - 0x23F286, 0x000815, 0x080102, 0x101204, - 0x241206, 0x000575, 0x081204, 0x000007, - 0x100102, 0x000575, 0x000425, 0x021124, - 0x100102, 0x000820, 0x031060, 0x010040, - 0x001924, 0x2A0086, 0x00008D, 0x000464, - 0x009D04, 0x291086, 0x180102, 0x000575, - 0x010042, 0x28040A, 0x00018D, 0x000924, - 0x280D02, 0x00000D, 0x000924, 0x281502, - 0x10000D, 0x000820, 0x0002F5, 0x010040, - 0x200007, 0x001175, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x24FA86, 0x000007, - 0x000100, 0x080B20, 0x130B60, 0x1B0B60, - 0x030A60, 0x010040, 0x050042, 0x3D004A, - 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, - 0x010042, 0x28140A, 0x0004F5, 0x010042, - 0x08000A, 0x000315, 0x010D04, 0x260286, - 0x004015, 0x000095, 0x010D04, 0x25F086, - 0x100022, 0x10002A, 0x261A06, 0x000007, - 0x333104, 0x2AA904, 0x000007, 0x032124, - 0x280502, 0x284402, 0x001124, 0x400102, - 0x000424, 0x000424, 0x003224, 0x00292C, - 0x00636C, 0x277386, 0x000007, 0x02B164, - 0x000464, 0x000464, 0x00008D, 0x000A64, - 0x280D02, 0x10008D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x00008D, 0x38B904, - 0x000007, 0x03296C, 0x30010A, 0x0002F5, - 0x010042, 0x08000A, 0x000904, 0x270286, - 0x000007, 0x00212C, 0x28050A, 0x00316C, - 0x00046C, 0x00046C, 0x28450A, 0x001124, - 0x006B64, 0x100102, 0x00008D, 0x01096C, - 0x280D0A, 0x10010D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x004124, 0x000424, - 0x000424, 0x003224, 0x300102, 0x032944, - 0x27FA86, 0x000007, 0x300002, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x284086, 0x003124, 0x000464, 0x300102, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x284A86, 0x000007, 0x284402, 0x003124, - 0x300502, 0x003924, 0x300583, 0x000883, - 0x0005F5, 0x010042, 0x28040A, 0x00008D, - 0x008124, 0x280D02, 0x00008D, 0x008124, - 0x281502, 0x10018D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x001025, 0x000575, - 0x030042, 0x09004A, 0x10000A, 0x0A0904, - 0x121104, 0x000007, 0x001020, 0x050860, - 0x050040, 0x0006FD, 0x018042, 0x09004A, - 0x10000A, 0x0000A5, 0x0A0904, 0x121104, - 0x000007, 0x000820, 0x019060, 0x010040, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x29CA86, 0x000007, 0x244206, 0x000007, - 0x000606, 0x000007, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x2A1A86, 0x000007, - 0x000100, 0x080B20, 0x138B60, 0x1B8B60, - 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, - 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, - 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, - 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, - 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, - 0x038A60, 0x000606, 0x018040, 0x00008D, - 0x000A64, 0x280D02, 0x000A24, 0x00027D, - 0x018042, 0x10000A, 0x001224, 0x0003FD, - 0x018042, 0x08000A, 0x000904, 0x2C0A86, - 0x000007, 0x00018D, 0x000A24, 0x000464, - 0x000464, 0x080102, 0x000924, 0x000424, - 0x000424, 0x100102, 0x02000D, 0x009144, - 0x2C6186, 0x000007, 0x0001FD, 0x018042, - 0x08000A, 0x000A44, 0x2C4386, 0x018042, - 0x0A000D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00027D, 0x001020, 0x000606, - 0x018040, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x2CB286, 0x000007, 0x00037D, - 0x018042, 0x08000A, 0x000904, 0x2CE286, - 0x000007, 0x000075, 0x002E7D, 0x010042, - 0x0B804A, 0x000020, 0x000904, 0x000686, - 0x010040, 0x31844A, 0x30048B, 0x000883, - 0x00008D, 0x000810, 0x28143A, 0x00008D, - 0x000810, 0x280C3A, 0x000675, 0x010042, - 0x08000A, 0x003815, 0x010924, 0x280502, - 0x0B000D, 0x000820, 0x0002F5, 0x010040, - 0x000606, 0x220007, 0x000464, 0x000464, - 0x000606, 0x000007, 0x000134, 0x007F8D, - 0x00093C, 0x281D12, 0x282512, 0x001F32, - 0x0E0007, 0x00010D, 0x00037D, 0x000820, - 0x018040, 0x05D2F4, 0x000007, 0x080007, - 0x00037D, 0x018042, 0x08000A, 0x000904, - 0x2E8A86, 0x000007, 0x000606, 0x000007, - 0x000007, 0x000012, 0x100007, 0x320007, - 0x600007, 0x460007, 0x100080, 0x48001A, - 0x004904, 0x2EF186, 0x000007, 0x001210, - 0x58003A, 0x000145, 0x5C5D04, 0x000007, - 0x000080, 0x48001A, 0x004904, 0x2F4186, - 0x000007, 0x001210, 0x50003A, 0x005904, - 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, - 0x7FFF7D, 0x07D524, 0x004224, 0x500102, - 0x200502, 0x000082, 0x40001A, 0x004104, - 0x2FC986, 0x000007, 0x003865, 0x40001A, - 0x004020, 0x00104D, 0x04C184, 0x31AB86, - 0x000040, 0x040007, 0x000165, 0x000145, - 0x004020, 0x000040, 0x000765, 0x080080, - 0x40001A, 0x004104, 0x305986, 0x000007, - 0x001210, 0x40003A, 0x004104, 0x30B286, - 0x00004D, 0x0000CD, 0x004810, 0x20043A, - 0x000882, 0x40001A, 0x004104, 0x30C186, - 0x000007, 0x004820, 0x005904, 0x319886, - 0x000040, 0x0007E5, 0x200480, 0x2816A0, - 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, - 0x000040, 0x000032, 0x400075, 0x00007D, - 0x07D574, 0x200512, 0x000082, 0x40001A, - 0x004104, 0x317186, 0x000007, 0x038A06, - 0x640007, 0x0000E5, 0x000020, 0x000040, - 0x000A65, 0x000020, 0x020040, 0x020040, - 0x000040, 0x000165, 0x000042, 0x70000A, - 0x007104, 0x323286, 0x000007, 0x060007, - 0x019A06, 0x640007, 0x050000, 0x007020, - 0x000040, 0x038A06, 0x640007, 0x000007, - 0x00306D, 0x028860, 0x029060, 0x08000A, - 0x028860, 0x008040, 0x100012, 0x00100D, - 0x009184, 0x32D186, 0x000E0D, 0x009184, - 0x33E186, 0x000007, 0x300007, 0x001020, - 0x003B6D, 0x008040, 0x000080, 0x08001A, - 0x000904, 0x32F186, 0x000007, 0x001220, - 0x000DED, 0x008040, 0x008042, 0x10000A, - 0x40000D, 0x109544, 0x000007, 0x001020, - 0x000DED, 0x008040, 0x008042, 0x20040A, - 0x000082, 0x08001A, 0x000904, 0x338186, - 0x000007, 0x003B6D, 0x008042, 0x08000A, - 0x000E15, 0x010984, 0x342B86, 0x600007, - 0x08001A, 0x000C15, 0x010984, 0x341386, - 0x000020, 0x1A0007, 0x0002ED, 0x008040, - 0x620007, 0x00306D, 0x028042, 0x0A804A, - 0x000820, 0x0A804A, 0x000606, 0x10804A, - 0x000007, 0x282512, 0x001F32, 0x05D2F4, - 0x54D104, 0x00735C, 0x000786, 0x000007, - 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, - 0x020040, 0x004820, 0x025060, 0x40000A, - 0x024060, 0x000040, 0x454944, 0x000007, - 0x004020, 0x003AE5, 0x000040, 0x0028E5, - 0x000042, 0x48000A, 0x004904, 0x39F886, - 0x002C65, 0x000042, 0x40000A, 0x0000D5, - 0x454104, 0x000007, 0x000655, 0x054504, - 0x368286, 0x0001D5, 0x054504, 0x368086, - 0x002B65, 0x000042, 0x003AE5, 0x50004A, - 0x40000A, 0x45C3D4, 0x000007, 0x454504, - 0x000007, 0x0000CD, 0x444944, 0x000007, - 0x454504, 0x000007, 0x00014D, 0x554944, - 0x000007, 0x045144, 0x367986, 0x002C65, - 0x000042, 0x48000A, 0x4CD104, 0x000007, - 0x04C144, 0x368386, 0x000007, 0x160007, - 0x002CE5, 0x040042, 0x40000A, 0x004020, - 0x000040, 0x002965, 0x000042, 0x40000A, - 0x004104, 0x36F086, 0x000007, 0x002402, - 0x383206, 0x005C02, 0x0025E5, 0x000042, - 0x40000A, 0x004274, 0x002AE5, 0x000042, - 0x40000A, 0x004274, 0x500112, 0x0029E5, - 0x000042, 0x40000A, 0x004234, 0x454104, - 0x000007, 0x004020, 0x000040, 0x003EE5, - 0x000020, 0x000040, 0x002DE5, 0x400152, - 0x50000A, 0x045144, 0x37DA86, 0x0000C5, - 0x003EE5, 0x004020, 0x000040, 0x002BE5, - 0x000042, 0x40000A, 0x404254, 0x000007, - 0x002AE5, 0x004020, 0x000040, 0x500132, - 0x040134, 0x005674, 0x0029E5, 0x020042, - 0x42000A, 0x000042, 0x50000A, 0x05417C, - 0x0028E5, 0x000042, 0x48000A, 0x0000C5, - 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, - 0x020042, 0x40004A, 0x50000A, 0x00423C, - 0x00567C, 0x0028E5, 0x004820, 0x000040, - 0x281D12, 0x282512, 0x001F72, 0x002965, - 0x000042, 0x40000A, 0x004104, 0x393A86, - 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, - 0x000042, 0x40000A, 0x004104, 0x397886, - 0x002D65, 0x000042, 0x28340A, 0x003465, - 0x020042, 0x42004A, 0x004020, 0x4A004A, - 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, - 0x39E186, 0x000007, 0x000606, 0x080007, - 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, - 0x020045, 0x004020, 0x000060, 0x000365, - 0x000040, 0x002E65, 0x001A20, 0x0A1A60, - 0x000040, 0x003465, 0x020042, 0x42004A, - 0x004020, 0x4A004A, 0x000606, 0x50004A, - 0x0017FD, 0x018042, 0x08000A, 0x000904, - 0x225A86, 0x000007, 0x00107D, 0x018042, - 0x0011FD, 0x33804A, 0x19804A, 0x20000A, - 0x000095, 0x2A1144, 0x01A144, 0x3B9086, - 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, - 0x018042, 0x0010FD, 0x09804A, 0x38000A, - 0x000095, 0x010924, 0x003A64, 0x3B8186, - 0x000007, 0x003904, 0x3B9286, 0x000007, - 0x3B9A06, 0x00000D, 0x00008D, 0x000820, - 0x00387D, 0x018040, 0x700002, 0x00117D, - 0x018042, 0x00197D, 0x29804A, 0x30000A, - 0x380002, 0x003124, 0x000424, 0x000424, - 0x002A24, 0x280502, 0x00068D, 0x000810, - 0x28143A, 0x00750D, 0x00B124, 0x002264, - 0x3D0386, 0x284402, 0x000810, 0x280C3A, - 0x0B800D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00758D, 0x00B124, 0x100102, - 0x012144, 0x3E4986, 0x001810, 0x10003A, - 0x00387D, 0x018042, 0x08000A, 0x000904, - 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, - 0x00008D, 0x023164, 0x000A64, 0x280D02, - 0x0B808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00387D, 0x018042, 0x08000A, - 0x000904, 0x3E3286, 0x030000, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x3D8286, - 0x000007, 0x002810, 0x28043A, 0x00750D, - 0x030924, 0x002264, 0x280D02, 0x02316C, - 0x28450A, 0x0B810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x00008D, 0x000A24, - 0x3E4A06, 0x100102, 0x001810, 0x10003A, - 0x0000BD, 0x003810, 0x30043A, 0x00187D, - 0x018042, 0x0018FD, 0x09804A, 0x20000A, - 0x0000AD, 0x028924, 0x07212C, 0x001010, - 0x300583, 0x300D8B, 0x3014BB, 0x301C83, - 0x002083, 0x00137D, 0x038042, 0x33844A, - 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, - 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, - 0x001E0D, 0x0005FD, 0x018042, 0x20000A, - 0x020924, 0x00068D, 0x00A96C, 0x00009D, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x3F6A86, 0x000007, 0x280502, 0x280D0A, - 0x284402, 0x001810, 0x28143A, 0x0C008D, - 0x000820, 0x0002FD, 0x018040, 0x220007, - 0x003904, 0x225886, 0x001E0D, 0x00057D, - 0x018042, 0x20000A, 0x020924, 0x0000A5, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x402A86, 0x000007, 0x280502, 0x280C02, - 0x002010, 0x28143A, 0x0C010D, 0x000820, - 0x0002FD, 0x018040, 0x225A06, 0x220007, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000 -}; - -#endif //_HWMCODE_ diff -Nru a/drivers/sound/yss225.c b/drivers/sound/yss225.c --- a/drivers/sound/yss225.c Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,319 +0,0 @@ -#include - -unsigned char page_zero[] __initdata = { -0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, -0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, -0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, -0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, -0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, -0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, -0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, -0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, -0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, -0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, -0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, -0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, -0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, -0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, -0x1d, 0x02, 0xdf -}; - -unsigned char page_one[] __initdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, -0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, -0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, -0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, -0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, -0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, -0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, -0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, -0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, -0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, -0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, -0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, -0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, -0x60, 0x00, 0x1b -}; - -unsigned char page_two[] __initdata = { -0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, -0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, -0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, -0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, -0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, -0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, -0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, -0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 -}; - -unsigned char page_three[] __initdata = { -0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, -0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, -0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, -0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, -0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, -0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, -0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, -0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 -}; - -unsigned char page_four[] __initdata = { -0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, -0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, -0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, -0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, -0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, -0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 -}; - -unsigned char page_six[] __initdata = { -0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, -0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, -0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, -0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, -0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, -0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, -0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, -0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, -0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, -0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, -0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, -0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, -0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, -0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, -0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, -0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, -0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, -0x80, 0x00, 0x7e, 0x80, 0x80 -}; - -unsigned char page_seven[] __initdata = { -0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, -0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, -0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, -0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, -0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, -0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, -0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, -0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, -0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, -0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, -0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, -0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00 -}; - -unsigned char page_zero_v2[] __initdata = { -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -unsigned char page_one_v2[] __initdata = { -0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -unsigned char page_two_v2[] __initdata = { -0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 -}; -unsigned char page_three_v2[] __initdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 -}; -unsigned char page_four_v2[] __initdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 -}; - -unsigned char page_seven_v2[] __initdata = { -0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -unsigned char mod_v2[] __initdata = { -0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, -0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, -0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, -0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, -0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, -0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, -0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, -0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, -0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, -0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, -0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, -0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, -0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, -0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, -0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, -0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, -0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, -0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, -0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, -0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, -0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, -0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, -0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, -0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, -0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, -0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, -0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, -0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 -}; -unsigned char coefficients[] __initdata = { -0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, -0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, -0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, -0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, -0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, -0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, -0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, -0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, -0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, -0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, -0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, -0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, -0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, -0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, -0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, -0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, -0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, -0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, -0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, -0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, -0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, -0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, -0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, -0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, -0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, -0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, -0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, -0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, -0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, -0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, -0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, -0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, -0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, -0xba -}; -unsigned char coefficients2[] __initdata = { -0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, -0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, -0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, -0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, -0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 -}; -unsigned char coefficients3[] __initdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, -0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, -0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, -0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, -0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, -0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, -0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, -0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, -0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, -0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, -0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, -0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, -0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, -0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, -0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, -0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, -0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, -0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, -0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, -0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, -0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, -0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, -0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, -0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, -0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, -0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, -0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, -0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, -0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, -0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, -0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, -0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, -0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, -0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, -0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, -0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, -0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff -}; - diff -Nru a/drivers/sound/yss225.h b/drivers/sound/yss225.h --- a/drivers/sound/yss225.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,24 +0,0 @@ -#ifndef __yss255_h__ -#define __yss255_h__ - -extern unsigned char page_zero[256]; -extern unsigned char page_one[256]; -extern unsigned char page_two[128]; -extern unsigned char page_three[128]; -extern unsigned char page_four[128]; -extern unsigned char page_six[192]; -extern unsigned char page_seven[256]; -extern unsigned char page_zero_v2[96]; -extern unsigned char page_one_v2[96]; -extern unsigned char page_two_v2[48]; -extern unsigned char page_three_v2[48]; -extern unsigned char page_four_v2[48]; -extern unsigned char page_seven_v2[96]; -extern unsigned char mod_v2[304]; -extern unsigned char coefficients[364]; -extern unsigned char coefficients2[56]; -extern unsigned char coefficients3[404]; - - -#endif /* __ys225_h__ */ - diff -Nru a/drivers/usb/CDCEther.c b/drivers/usb/CDCEther.c --- a/drivers/usb/CDCEther.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/CDCEther.c Tue Feb 19 18:08:57 2002 @@ -1144,18 +1144,18 @@ // Zero everything out. memset(ether_dev, 0, sizeof(ether_dev_t)); - ether_dev->rx_urb = usb_alloc_urb(0); + ether_dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!ether_dev->rx_urb) { kfree(ether_dev); return NULL; } - ether_dev->tx_urb = usb_alloc_urb(0); + ether_dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!ether_dev->tx_urb) { usb_free_urb(ether_dev->rx_urb); kfree(ether_dev); return NULL; } - ether_dev->intr_urb = usb_alloc_urb(0); + ether_dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL); if (!ether_dev->intr_urb) { usb_free_urb(ether_dev->tx_urb); usb_free_urb(ether_dev->rx_urb); diff -Nru a/drivers/usb/Config.help b/drivers/usb/Config.help --- a/drivers/usb/Config.help Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/Config.help Tue Feb 19 18:08:59 2002 @@ -505,6 +505,21 @@ The module will be called dabusb.o. If you want to compile it as a module, say M here and read . +CONFIG_USB_KONICAWC + Say Y here if you want support for webcams based on a Konica + chipset. This is known to work with the Intel YC76 webcam. + + This driver uses the Video For Linux API. You must enable + (Y or M in config) Video For Linux (under Character Devices) + to use this driver. Information on this API and pointers to + "v4l" programs may be found on the WWW at + . + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called konicawc.o. If you want to compile it as + a module, say M here and read . + CONFIG_USB_USBNET This driver supports network links over USB with USB "Network" or "data transfer" cables, often used to network laptops to PCs. diff -Nru a/drivers/usb/Config.in b/drivers/usb/Config.in --- a/drivers/usb/Config.in Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/Config.in Tue Feb 19 18:08:59 2002 @@ -80,6 +80,7 @@ dep_tristate ' USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB + dep_tristate ' USB Konica Webcam support' CONFIG_USB_KONICAWC $CONFIG_USB $CONFIG_VIDEO_DEV fi comment 'USB Network adaptors' diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/Makefile Tue Feb 19 18:08:59 2002 @@ -64,6 +64,7 @@ obj-$(CONFIG_USB_PRINTER) += printer.o obj-$(CONFIG_USB_AUDIO) += audio.o obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o +obj-$(CONFIG_USB_KONICAWC) += konicawc.o usbvideo.o obj-$(CONFIG_USB_PWC) += pwc.o obj-$(CONFIG_USB_DC2XX) += dc2xx.o obj-$(CONFIG_USB_MDC800) += mdc800.o diff -Nru a/drivers/usb/audio.c b/drivers/usb/audio.c --- a/drivers/usb/audio.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/audio.c Tue Feb 19 18:08:59 2002 @@ -2829,14 +2829,14 @@ init_waitqueue_head(&as->usbin.dma.wait); init_waitqueue_head(&as->usbout.dma.wait); spin_lock_init(&as->lock); - as->usbin.durb[0].urb = usb_alloc_urb(0); - as->usbin.durb[1].urb = usb_alloc_urb(0); - as->usbin.surb[0].urb = usb_alloc_urb(0); - as->usbin.surb[1].urb = usb_alloc_urb(0); - as->usbout.durb[0].urb = usb_alloc_urb(0); - as->usbout.durb[1].urb = usb_alloc_urb(0); - as->usbout.surb[0].urb = usb_alloc_urb(0); - as->usbout.surb[1].urb = usb_alloc_urb(0); + as->usbin.durb[0].urb = usb_alloc_urb(0, GFP_KERNEL); + as->usbin.durb[1].urb = usb_alloc_urb(0, GFP_KERNEL); + as->usbin.surb[0].urb = usb_alloc_urb(0, GFP_KERNEL); + as->usbin.surb[1].urb = usb_alloc_urb(0, GFP_KERNEL); + as->usbout.durb[0].urb = usb_alloc_urb(0, GFP_KERNEL); + as->usbout.durb[1].urb = usb_alloc_urb(0, GFP_KERNEL); + as->usbout.surb[0].urb = usb_alloc_urb(0, GFP_KERNEL); + as->usbout.surb[1].urb = usb_alloc_urb(0, GFP_KERNEL); if ((!as->usbin.durb[0].urb) || (!as->usbin.durb[1].urb) || (!as->usbin.surb[0].urb) || diff -Nru a/drivers/usb/auerswald.c b/drivers/usb/auerswald.c --- a/drivers/usb/auerswald.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/auerswald.c Tue Feb 19 18:08:59 2002 @@ -699,7 +699,7 @@ dr = kmalloc (sizeof (struct usb_ctrlrequest), GFP_KERNEL); if (!dr) return -ENOMEM; - urb = usb_alloc_urb (0); + urb = usb_alloc_urb (0, GFP_KERNEL); if (!urb) { kfree (dr); return -ENOMEM; @@ -802,7 +802,7 @@ if (!bep->bufp) goto bl_fail; bep->dr = (struct usb_ctrlrequest *) kmalloc (sizeof (struct usb_ctrlrequest), GFP_KERNEL); if (!bep->dr) goto bl_fail; - bep->urbp = usb_alloc_urb (0); + bep->urbp = usb_alloc_urb (0, GFP_KERNEL); if (!bep->urbp) goto bl_fail; list_add_tail (&bep->buff_list, &bcp->free_buff_list); } @@ -1130,7 +1130,7 @@ /* allocate the urb and data buffer */ if (!cp->inturbp) { - cp->inturbp = usb_alloc_urb (0); + cp->inturbp = usb_alloc_urb (0, GFP_KERNEL); if (!cp->inturbp) { ret = -ENOMEM; goto intoend; diff -Nru a/drivers/usb/bluetooth.c b/drivers/usb/bluetooth.c --- a/drivers/usb/bluetooth.c Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/bluetooth.c Tue Feb 19 18:08:58 2002 @@ -1123,7 +1123,7 @@ /* create our control out urb pool */ for (i = 0; i < NUM_CONTROL_URBS; ++i) { - struct urb *urb = usb_alloc_urb(0); + struct urb *urb = usb_alloc_urb(0, GFP_KERNEL); if (urb == NULL) { err("No free urbs available"); goto probe_error; @@ -1134,7 +1134,7 @@ /* set up the endpoint information */ endpoint = bulk_in_endpoint[0]; - bluetooth->read_urb = usb_alloc_urb (0); + bluetooth->read_urb = usb_alloc_urb (0, GFP_KERNEL); if (!bluetooth->read_urb) { err("No free urbs available"); goto probe_error; @@ -1154,7 +1154,7 @@ /* create our write urb pool */ for (i = 0; i < NUM_BULK_URBS; ++i) { - struct urb *urb = usb_alloc_urb(0); + struct urb *urb = usb_alloc_urb(0, GFP_KERNEL); if (urb == NULL) { err("No free urbs available"); goto probe_error; @@ -1170,7 +1170,7 @@ bluetooth->bulk_out_buffer_size = endpoint->wMaxPacketSize * 2; endpoint = interrupt_in_endpoint[0]; - bluetooth->interrupt_in_urb = usb_alloc_urb(0); + bluetooth->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!bluetooth->interrupt_in_urb) { err("No free urbs available"); goto probe_error; diff -Nru a/drivers/usb/catc.c b/drivers/usb/catc.c --- a/drivers/usb/catc.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/catc.c Tue Feb 19 18:08:59 2002 @@ -694,10 +694,10 @@ catc->timer.data = (long) catc; catc->timer.function = catc_stats_timer; - catc->ctrl_urb = usb_alloc_urb(0); - catc->tx_urb = usb_alloc_urb(0); - catc->rx_urb = usb_alloc_urb(0); - catc->irq_urb = usb_alloc_urb(0); + catc->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); + catc->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + catc->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + catc->irq_urb = usb_alloc_urb(0, GFP_KERNEL); if ((!catc->ctrl_urb) || (!catc->tx_urb) || (!catc->rx_urb) || (!catc->irq_urb)) { err("No free urbs available."); diff -Nru a/drivers/usb/dabusb.c b/drivers/usb/dabusb.c --- a/drivers/usb/dabusb.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/dabusb.c Tue Feb 19 18:08:57 2002 @@ -226,7 +226,7 @@ } memset (b, 0, sizeof (buff_t)); b->s = s; - b->purb = usb_alloc_urb(packets); + b->purb = usb_alloc_urb(packets, GFP_KERNEL); if (!b->purb) { err("usb_alloc_urb == NULL"); kfree (b); diff -Nru a/drivers/usb/devio.c b/drivers/usb/devio.c --- a/drivers/usb/devio.c Tue Feb 19 18:09:00 2002 +++ b/drivers/usb/devio.c Tue Feb 19 18:09:00 2002 @@ -181,7 +181,7 @@ if (!as) return NULL; memset(as, 0, assize); - as->urb = usb_alloc_urb(numisoframes); + as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL); if (!as->urb) { kfree(as); return NULL; diff -Nru a/drivers/usb/hcd/ohci-hcd.c b/drivers/usb/hcd/ohci-hcd.c --- a/drivers/usb/hcd/ohci-hcd.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/hcd/ohci-hcd.c Tue Feb 19 18:08:57 2002 @@ -662,7 +662,7 @@ #ifdef CONFIG_PMAC_PBOOK if (_machine == _MACH_Pmac) - disable_irq (ohci->irq); + disable_irq (hcd->pdev->irq); /* else, 2.4 assumes shared irqs -- don't disable */ #endif @@ -836,7 +836,7 @@ #ifdef CONFIG_PMAC_PBOOK if (_machine == _MACH_Pmac) - enable_irq (ohci->irq); + enable_irq (hcd->pdev->irq); #endif if (ohci->hcca->done_head) dl_done_list (ohci, dl_reverse_done_list (ohci)); diff -Nru a/drivers/usb/hid-core.c b/drivers/usb/hid-core.c --- a/drivers/usb/hid-core.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/hid-core.c Tue Feb 19 18:08:57 2002 @@ -1,12 +1,10 @@ /* - * $Id: hid-core.c,v 1.8 2001/05/23 12:02:18 vojtech Exp $ + * $Id: hid-core.c,v 1.42 2002/01/27 00:22:46 vojtech Exp $ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2001 Vojtech Pavlik * * USB HID support for Linux - * - * Sponsored by SuSE */ /* @@ -25,8 +23,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -56,9 +54,10 @@ * Version Information */ -#define DRIVER_VERSION "v1.8" -#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik " -#define DRIVER_DESC "USB HID support drivers" +#define DRIVER_VERSION "v1.31" +#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik " +#define DRIVER_DESC "USB HID core driver" +#define DRIVER_LICENSE "GPL" static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; @@ -205,16 +204,11 @@ return -1; } - if (HID_MAIN_ITEM_VARIABLE & ~flags) { /* ARRAY */ - if (parser->global.logical_maximum <= parser->global.logical_minimum) { - dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum); - return -1; - } - usages = parser->local.usage_index; - /* Hint: we can assume usages < MAX_USAGE here */ - } else { /* VARIABLE */ - usages = parser->global.report_count; + if (parser->global.logical_maximum <= parser->global.logical_minimum) { + dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum); + return -1; } + usages = parser->local.usage_index; offset = report->size; report->size += parser->global.report_size * parser->global.report_count; @@ -311,7 +305,10 @@ return 0; case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: - parser->global.logical_maximum = item_sdata(item); + if (parser->global.logical_minimum < 0) + parser->global.logical_maximum = item_sdata(item); + else + parser->global.logical_maximum = item_udata(item); return 0; case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: @@ -319,11 +316,14 @@ return 0; case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: - parser->global.physical_maximum = item_sdata(item); + if (parser->global.physical_minimum < 0) + parser->global.physical_maximum = item_sdata(item); + else + parser->global.physical_maximum = item_udata(item); return 0; case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: - parser->global.unit_exponent = item_udata(item); + parser->global.unit_exponent = item_sdata(item); return 0; case HID_GLOBAL_ITEM_TAG_UNIT: @@ -508,8 +508,6 @@ for (n = 0; n < report->maxfield; n++) kfree(report->field[n]); - if (report->data) - kfree(report->data); kfree(report); } @@ -538,60 +536,64 @@ * items, though they are not used yet. */ -static __u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) +static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) { - if ((end - start) > 0) { + u8 b; - __u8 b = *start++; - item->type = (b >> 2) & 3; - item->tag = (b >> 4) & 15; + if ((end - start) <= 0) + return NULL; - if (item->tag == HID_ITEM_TAG_LONG) { + b = *start++; - item->format = HID_ITEM_FORMAT_LONG; + item->type = (b >> 2) & 3; + item->tag = (b >> 4) & 15; - if ((end - start) >= 2) { + if (item->tag == HID_ITEM_TAG_LONG) { - item->size = *start++; - item->tag = *start++; + item->format = HID_ITEM_FORMAT_LONG; - if ((end - start) >= item->size) { - item->data.longdata = start; - start += item->size; - return start; - } - } - } else { + if ((end - start) < 2) + return NULL; - item->format = HID_ITEM_FORMAT_SHORT; - item->size = b & 3; - switch (item->size) { - - case 0: - return start; - - case 1: - if ((end - start) >= 1) { - item->data.u8 = *start++; - return start; - } - break; - - case 2: - if ((end - start) >= 2) { - item->data.u16 = le16_to_cpu( get_unaligned(((__u16*)start)++)); - return start; - } - - case 3: - item->size++; - if ((end - start) >= 4) { - item->data.u32 = le32_to_cpu( get_unaligned(((__u32*)start)++)); - return start; - } - } - } + item->size = *start++; + item->tag = *start++; + + if ((end - start) < item->size) + return NULL; + + item->data.longdata = start; + start += item->size; + return start; + } + + item->format = HID_ITEM_FORMAT_SHORT; + item->size = b & 3; + + switch (item->size) { + + case 0: + return start; + + case 1: + if ((end - start) < 1) + return NULL; + item->data.u8 = *start++; + return start; + + case 2: + if ((end - start) < 2) + return NULL; + item->data.u16 = le16_to_cpu(get_unaligned(((__u16*)start)++)); + return start; + + case 3: + item->size++; + if ((end - start) < 4) + return NULL; + item->data.u32 = le32_to_cpu(get_unaligned(((__u32*)start)++)); + return start; } + return NULL; } @@ -638,12 +640,14 @@ end = start + size; while ((start = fetch_item(start, end, &item)) != 0) { + if (item.format != HID_ITEM_FORMAT_SHORT) { dbg("unexpected long global item"); hid_free_device(device); kfree(parser); return NULL; } + if (dispatch_type[item.type](parser, &item)) { dbg("item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); @@ -742,7 +746,6 @@ #endif } - /* * Analyse a received field, and fetch the data from it. The field * content is stored for next report processing (we do differential @@ -797,9 +800,12 @@ memcpy(field->value, value, count * sizeof(__s32)); } -static int hid_input_report(int type, u8 *data, int len, struct hid_device *hid) +static int hid_input_report(int type, struct urb *urb) { + struct hid_device *hid = urb->context; struct hid_report_enum *report_enum = hid->report_enum + type; + u8 *data = urb->transfer_buffer; + int len = urb->actual_length; struct hid_report *report; int n, size; @@ -818,92 +824,46 @@ len--; } - if (!(report = report_enum->report_id_hash[n])) { - dbg("undefined report_id %d received", n); -#ifdef DEBUG - printk(KERN_DEBUG __FILE__ ": report (size %u) = ", len); - for (n = 0; n < len; n++) - printk(" %02x", data[n]); - printk("\n"); +#ifdef DEBUG_DATA + { + int i; + printk(KERN_DEBUG __FILE__ ": report %d (size %u) = ", n, len); + for (i = 0; i < n; i++) + printk(" %02x", data[i]); + printk("\n"); + } #endif + if (!(report = report_enum->report_id_hash[n])) { + dbg("undefined report_id %d received", n); return -1; } size = ((report->size - 1) >> 3) + 1; if (len < size) { - - if (size <= 8) { - dbg("report %d is too short, (%d < %d)", report->id, len, size); - return -1; - } - - /* - * Some low-speed devices have large reports and maxpacketsize 8. - * We buffer the data in that case and parse it when we got it all. - * Works only for unnumbered reports. Doesn't make sense for numbered - * reports anyway - then they don't need to be large. - */ - - if (!report->data) - if (!(report->data = kmalloc(size, GFP_ATOMIC))) { - dbg("couldn't allocate report buffer"); - return -1; - } - - if (report->idx + len > size) { - dbg("report data buffer overflow"); - report->idx = 0; - return -1; - } - - memcpy(report->data + report->idx, data, len); - report->idx += len; - - if (report->idx < size) - return 0; - - data = report->data; + dbg("report %d is too short, (%d < %d)", report->id, len, size); + return -1; } for (n = 0; n < report->maxfield; n++) hid_input_field(hid, report->field[n], data); - report->idx = 0; return 0; } /* - * Interrupt input handler. + * Input interrupt completion handler. */ -static void hid_irq(struct urb *urb) +static void hid_irq_in(struct urb *urb) { if (urb->status) { - dbg("nonzero status in irq %d", urb->status); - return; - } - - hid_input_report(HID_INPUT_REPORT, urb->transfer_buffer, urb->actual_length, urb->context); -} - -/* - * hid_read_report() reads in report values without waiting for an irq urb. - */ - -void hid_read_report(struct hid_device *hid, struct hid_report *report) -{ - int len = ((report->size - 1) >> 3) + 1 + hid->report_enum[report->type].numbered; - u8 data[len]; - int read; - - if ((read = hid_get_report(hid->dev, hid->ifnum, report->type + 1, report->id, data, len)) != len) { - dbg("reading report type %d id %d failed len %d read %d", report->type + 1, report->id, len, read); + dbg("nonzero status in input irq %d", urb->status); return; } - hid_input_report(report->type, data, len, hid); + hid_input_report(HID_INPUT_REPORT, urb); } /* @@ -949,7 +909,8 @@ hid_dump_input(field->usage + offset, value); if (offset >= field->report_count) { - dbg("offset exceeds report_count"); + dbg("offset (%d) exceeds report_count (%d)", offset, field->report_count); + hid_dump_field(field, 8); return -1; } if (field->logical_minimum < 0) { @@ -958,11 +919,6 @@ return -1; } } - if ( (value > field->logical_maximum) - || (value < field->logical_minimum)) { - dbg("value %d is invalid", value); - return -1; - } field->value[offset] = value; return 0; } @@ -986,14 +942,56 @@ return -1; } +/* + * Find a report with a specified HID usage. + */ + +int hid_find_report_by_usage(struct hid_device *hid, __u32 wanted_usage, struct hid_report **report, int type) +{ + struct hid_report_enum *report_enum = hid->report_enum + type; + struct list_head *list = report_enum->report_list.next; + int i, j; + + while (list != &report_enum->report_list) { + *report = (struct hid_report *) list; + list = list->next; + for (i = 0; i < (*report)->maxfield; i++) { + struct hid_field *field = (*report)->field[i]; + for (j = 0; j < field->maxusage; j++) + if (field->logical == wanted_usage) + return j; + } + } + return -1; +} + +int hid_find_field_in_report(struct hid_report *report, __u32 wanted_usage, struct hid_field **field) +{ + int i, j; + + for (i = 0; i < report->maxfield; i++) { + *field = report->field[i]; + for (j = 0; j < (*field)->maxusage; j++) + if ((*field)->usage[j].hid == wanted_usage) + return j; + } + + return -1; +} + static int hid_submit_out(struct hid_device *hid) { - hid->urbout.transfer_buffer_length = le16_to_cpup(&hid->out[hid->outtail].dr.wLength); - hid->urbout.transfer_buffer = hid->out[hid->outtail].buffer; - hid->urbout.setup_packet = (void *) &(hid->out[hid->outtail].dr); - hid->urbout.dev = hid->dev; + struct hid_report *report; + + report = hid->out[hid->outtail]; + + hid_output_report(report, hid->outbuf); + hid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1; + hid->urbout->dev = hid->dev; + + dbg("submitting out urb"); - if (usb_submit_urb(&hid->urbout, GFP_KERNEL)) { + if (usb_submit_urb(hid->urbout, GFP_ATOMIC)) { err("usb_submit_urb(out) failed"); return -1; } @@ -1001,33 +999,168 @@ return 0; } +static int hid_submit_ctrl(struct hid_device *hid) +{ + struct hid_report *report; + unsigned char dir; + + report = hid->ctrl[hid->ctrltail].report; + dir = hid->ctrl[hid->ctrltail].dir; + + if (dir == USB_DIR_OUT) + hid_output_report(report, hid->ctrlbuf); + + hid->urbctrl->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + ((report->id > 0) && (dir != USB_DIR_OUT)); + hid->urbctrl->pipe = (dir == USB_DIR_OUT) ? usb_sndctrlpipe(hid->dev, 0) : usb_rcvctrlpipe(hid->dev, 0); + hid->urbctrl->dev = hid->dev; + + hid->cr.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir; + hid->cr.bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT; + hid->cr.wValue = ((report->type + 1) << 8) | report->id; + hid->cr.wIndex = cpu_to_le16(hid->ifnum); + hid->cr.wLength = cpu_to_le16(hid->urbctrl->transfer_buffer_length); + + dbg("submitting ctrl urb"); + + if (usb_submit_urb(hid->urbctrl, GFP_ATOMIC)) { + err("usb_submit_urb(ctrl) failed"); + return -1; + } + + return 0; +} + +/* + * Output interrupt completion handler. + */ + +static void hid_irq_out(struct urb *urb) +{ + struct hid_device *hid = urb->context; + unsigned long flags; + + if (urb->status) + warn("output irq status %d received", urb->status); + + spin_lock_irqsave(&hid->outlock, flags); + + hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1); + + if (hid->outhead != hid->outtail) { + hid_submit_out(hid); + return; + } + + clear_bit(HID_OUT_RUNNING, &hid->iofl); + + spin_unlock_irqrestore(&hid->outlock, flags); + + wake_up(&hid->wait); +} + +/* + * Control pipe completion handler. + */ + static void hid_ctrl(struct urb *urb) { struct hid_device *hid = urb->context; + unsigned long flags; if (urb->status) warn("ctrl urb status %d received", urb->status); - hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1); + spin_lock_irqsave(&hid->ctrllock, flags); - if (hid->outhead != hid->outtail) - hid_submit_out(hid); + if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN) + hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb); + + hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); + + if (hid->ctrlhead != hid->ctrltail) { + hid_submit_ctrl(hid); + return; + } + + clear_bit(HID_CTRL_RUNNING, &hid->iofl); + + spin_unlock_irqrestore(&hid->ctrllock, flags); + + wake_up(&hid->wait); } -void hid_write_report(struct hid_device *hid, struct hid_report *report) +void hid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) { - hid_output_report(report, hid->out[hid->outhead].buffer); + int head; + unsigned long flags; + + if (hid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { - hid->out[hid->outhead].dr.wValue = cpu_to_le16(0x200 | report->id); - hid->out[hid->outhead].dr.wLength = cpu_to_le16((report->size + 7) >> 3); + spin_lock_irqsave(&hid->outlock, flags); - hid->outhead = (hid->outhead + 1) & (HID_CONTROL_FIFO_SIZE - 1); + if ((head = (hid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == hid->outtail) { + spin_unlock_irqrestore(&hid->outlock, flags); + warn("output queue full"); + return; + } - if (hid->outhead == hid->outtail) - hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1); + hid->out[hid->outhead] = report; + hid->outhead = head; - if (hid->urbout.status != -EINPROGRESS) - hid_submit_out(hid); + if (!test_and_set_bit(HID_OUT_RUNNING, &hid->iofl)) + hid_submit_out(hid); + + spin_unlock_irqrestore(&hid->outlock, flags); + return; + } + + spin_lock_irqsave(&hid->ctrllock, flags); + + if ((head = (hid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == hid->ctrltail) { + spin_unlock_irqrestore(&hid->ctrllock, flags); + warn("control queue full"); + return; + } + + hid->ctrl[hid->ctrlhead].report = report; + hid->ctrl[hid->ctrlhead].dir = dir; + hid->ctrlhead = head; + + if (!test_and_set_bit(HID_CTRL_RUNNING, &hid->iofl)) + hid_submit_ctrl(hid); + + spin_unlock_irqrestore(&hid->ctrllock, flags); +} + +int hid_wait_io(struct hid_device *hid) +{ + DECLARE_WAITQUEUE(wait, current); + int timeout = 10*HZ; + + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&hid->wait, &wait); + + while (timeout && test_bit(HID_CTRL_RUNNING, &hid->iofl) && + test_bit(HID_OUT_RUNNING, &hid->iofl)) + timeout = schedule_timeout(timeout); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&hid->wait, &wait); + + if (!timeout) { + dbg("timeout waiting for ctrl or out queue to clear"); + return -1; + } + + return 0; +} + +static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, + unsigned char type, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, + (type << 8), ifnum, buf, size, HZ * USB_CTRL_GET_TIMEOUT); } int hid_open(struct hid_device *hid) @@ -1035,9 +1168,9 @@ if (hid->open++) return 0; - hid->urb.dev = hid->dev; + hid->urbin->dev = hid->dev; - if (usb_submit_urb(&hid->urb, GFP_KERNEL)) + if (usb_submit_urb(hid->urbin, GFP_KERNEL)) return -EIO; return 0; @@ -1046,30 +1179,52 @@ void hid_close(struct hid_device *hid) { if (!--hid->open) - usb_unlink_urb(&hid->urb); + usb_unlink_urb(hid->urbin); } /* - * Initialize all readable reports + * Initialize all reports */ + void hid_init_reports(struct hid_device *hid) { - int i; - struct hid_report *report; struct hid_report_enum *report_enum; + struct hid_report *report; struct list_head *list; + int len; - for (i = 0; i < HID_REPORT_TYPES; i++) { - if (i == HID_FEATURE_REPORT || i == HID_INPUT_REPORT) { - report_enum = hid->report_enum + i; - list = report_enum->report_list.next; - while (list != &report_enum->report_list) { - report = (struct hid_report *) list; - hid_set_idle(hid->dev, hid->ifnum, 0, report->id); - hid_read_report(hid, report); - list = list->next; - } - } + report_enum = hid->report_enum + HID_INPUT_REPORT; + list = report_enum->report_list.next; + while (list != &report_enum->report_list) { + report = (struct hid_report *) list; + hid_submit_report(hid, report, USB_DIR_IN); + list = list->next; + } + + report_enum = hid->report_enum + HID_FEATURE_REPORT; + list = report_enum->report_list.next; + while (list != &report_enum->report_list) { + report = (struct hid_report *) list; + hid_submit_report(hid, report, USB_DIR_IN); + list = list->next; + } + + if (hid_wait_io(hid)) { + warn("timeout initializing reports\n"); + return; + } + + report_enum = hid->report_enum + HID_INPUT_REPORT; + list = report_enum->report_list.next; + while (list != &report_enum->report_list) { + report = (struct hid_report *) list; + len = ((report->size - 1) >> 3) + 1 + report_enum->numbered; + if (len > hid->urbin->transfer_buffer_length) + hid->urbin->transfer_buffer_length = len < HID_BUFFER_SIZE ? len : HID_BUFFER_SIZE; + usb_control_msg(hid->dev, usb_sndctrlpipe(hid->dev, 0), + 0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE, report->id, + hid->ifnum, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); + list = list->next; } } @@ -1077,6 +1232,10 @@ #define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010 #define USB_DEVICE_ID_WACOM_INTUOS 0x0020 +#define USB_VENDOR_ID_GRIFFIN 0x077d +#define USB_DEVICE_ID_POWERMATE 0x0410 +#define USB_DEVICE_ID_SOUNDKNOB 0x04AA + struct hid_blacklist { __u16 idVendor; __u16 idProduct; @@ -1087,19 +1246,11 @@ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 2}, { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 3}, { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 4}, + { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE }, + { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB }, { 0, 0 } }; -static int get_class_descriptor(struct usb_device *dev, int ifnum, - unsigned char type, void *buf, int size) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, - (type << 8), ifnum, buf, size, - HZ * USB_CTRL_GET_TIMEOUT); -} - - static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) { struct usb_interface_descriptor *interface = dev->actconfig->interface[ifnum].altsetting + 0; @@ -1131,7 +1282,7 @@ { __u8 rdesc[rsize]; - if ((n = get_class_descriptor(dev, interface->bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { + if ((n = hid_get_class_descriptor(dev, interface->bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { dbg("reading report descriptor failed"); return NULL; } @@ -1152,73 +1303,83 @@ for (n = 0; n < interface->bNumEndpoints; n++) { struct usb_endpoint_descriptor *endpoint = &interface->endpoint[n]; - int pipe, maxp; + int pipe; if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */ continue; - if (!(endpoint->bEndpointAddress & 0x80)) /* Not an input endpoint */ - continue; - - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - FILL_INT_URB(&hid->urb, dev, pipe, hid->buffer, maxp > 32 ? 32 : maxp, hid_irq, hid, endpoint->bInterval); - - break; + if (endpoint->bEndpointAddress & USB_DIR_IN) { + if (hid->urbin) + continue; + if (!(hid->urbin = usb_alloc_urb(0, GFP_KERNEL))) + goto fail; + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + FILL_INT_URB(hid->urbin, dev, pipe, hid->inbuf, 0, hid_irq_in, hid, endpoint->bInterval); + } else { + if (hid->urbout) + continue; + if (!(hid->urbout = usb_alloc_urb(0, GFP_KERNEL))) + goto fail; + pipe = usb_sndbulkpipe(dev, endpoint->bEndpointAddress); + FILL_BULK_URB(hid->urbout, dev, pipe, hid->outbuf, 0, hid_irq_out, hid); + } } - if (n == interface->bNumEndpoints) { - dbg("couldn't find an input interrupt endpoint"); - hid_free_device(hid); - return NULL; + if (!hid->urbin) { + err("couldn't find an input interrupt endpoint"); + goto fail; } + init_waitqueue_head(&hid->wait); + + hid->outlock = SPIN_LOCK_UNLOCKED; + hid->ctrllock = SPIN_LOCK_UNLOCKED; + hid->version = hdesc->bcdHID; hid->country = hdesc->bCountryCode; hid->dev = dev; hid->ifnum = interface->bInterfaceNumber; - for (n = 0; n < HID_CONTROL_FIFO_SIZE; n++) { - hid->out[n].dr.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE; - hid->out[n].dr.bRequest = HID_REQ_SET_REPORT; - hid->out[n].dr.wIndex = cpu_to_le16(hid->ifnum); - } - hid->name[0] = 0; - if (!(buf = kmalloc(63, GFP_KERNEL))) - return NULL; + if (!(buf = kmalloc(64, GFP_KERNEL))) + goto fail; - if (usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0) { + if (usb_string(dev, dev->descriptor.iManufacturer, buf, 64) > 0) { strcat(hid->name, buf); - if (usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0) + if (usb_string(dev, dev->descriptor.iProduct, buf, 64) > 0) sprintf(hid->name, "%s %s", hid->name, buf); } else sprintf(hid->name, "%04x:%04x", dev->descriptor.idVendor, dev->descriptor.idProduct); - kfree(buf); + usb_make_path(dev, buf, 63); + sprintf(hid->phys, "%s/input%d", buf, ifnum); - FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0), - (void*) &hid->out[0].dr, hid->out[0].buffer, 1, hid_ctrl, hid); + if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0) + hid->uniq[0] = 0; -/* - * Some devices don't like this and crash. I don't know of any devices - * needing this, so it is disabled for now. - */ + kfree(buf); -#if 0 - if (interface->bInterfaceSubClass == 1) - hid_set_protocol(dev, hid->ifnum, 1); -#endif + hid->urbctrl = usb_alloc_urb(0, GFP_KERNEL); + FILL_CONTROL_URB(hid->urbctrl, dev, 0, (void*) &hid->cr, hid->ctrlbuf, 1, hid_ctrl, hid); return hid; + +fail: + + hid_free_device(hid); + if (hid->urbin) usb_free_urb(hid->urbin); + if (hid->urbout) usb_free_urb(hid->urbout); + if (hid->urbctrl) usb_free_urb(hid->urbctrl); + + return NULL; } static void* hid_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) { struct hid_device *hid; + char path[64]; int i; char *c; @@ -1236,10 +1397,16 @@ if (!hiddev_connect(hid)) hid->claimed |= HID_CLAIMED_HIDDEV; #endif + + if (!hid->claimed) { + hid_free_device(hid); + return NULL; + } + printk(KERN_INFO); if (hid->claimed & HID_CLAIMED_INPUT) - printk("input%d", hid->input.number); + printk("input"); if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV)) printk(","); if (hid->claimed & HID_CLAIMED_HIDDEV) @@ -1252,9 +1419,10 @@ break; } - printk(": USB HID v%x.%02x %s [%s] on usb%d:%d.%d\n", - hid->version >> 8, hid->version & 0xff, c, hid->name, - dev->bus->busnum, dev->devnum, ifnum); + usb_make_path(dev, path, 63); + + printk(": USB HID v%x.%02x %s [%s] on %s\n", + hid->version >> 8, hid->version & 0xff, c, hid->name, path); return hid; } @@ -1263,8 +1431,10 @@ { struct hid_device *hid = ptr; - dbg("cleanup called"); - usb_unlink_urb(&hid->urb); + usb_unlink_urb(hid->urbin); + usb_unlink_urb(hid->urbout); + usb_unlink_urb(hid->urbctrl); + if (hid->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(hid); @@ -1272,12 +1442,17 @@ if (hid->claimed & HID_CLAIMED_HIDDEV) hiddev_disconnect(hid); #endif + + usb_free_urb(hid->urbin); + usb_free_urb(hid->urbctrl); + if (hid->urbout) + usb_free_urb(hid->urbout); + hid_free_device(hid); } static struct usb_device_id hid_usb_ids [] = { - { match_flags: USB_DEVICE_ID_MATCH_INT_CLASS, - bInterfaceClass: USB_INTERFACE_CLASS_HID }, + { bInterfaceClass: USB_INTERFACE_CLASS_HID }, { } /* Terminating entry */ }; @@ -1296,8 +1471,7 @@ hiddev_init(); #endif usb_register(&hid_driver); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } @@ -1313,6 +1487,6 @@ module_init(hid_init); module_exit(hid_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); diff -Nru a/drivers/usb/hid-debug.h b/drivers/usb/hid-debug.h --- a/drivers/usb/hid-debug.h Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/hid-debug.h Tue Feb 19 18:08:57 2002 @@ -1,12 +1,10 @@ /* - * $Id: hid-debug.h,v 1.3 2001/05/10 15:56:07 vojtech Exp $ + * $Id: hid-debug.h,v 1.8 2001/09/25 09:37:57 vojtech Exp $ * * (c) 1999 Andreas Gal - * (c) 2000-2001 Vojtech Pavlik + * (c) 2000-2001 Vojtech Pavlik * * Some debug stuff for the HID parser. - * - * Sponsored by SuSE */ /* @@ -25,8 +23,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ struct hid_usage_entry { @@ -36,6 +34,7 @@ }; static struct hid_usage_entry hid_usage_table[] = { + { 0, 0, "Undefined" }, { 1, 0, "GenericDesktop" }, {0, 0x01, "Pointer"}, {0, 0x02, "Mouse"}, @@ -87,6 +86,7 @@ { 7, 0, "Keyboard" }, { 8, 0, "LED" }, { 9, 0, "Button" }, + { 10, 0, "Ordinal" }, { 12, 0, "Hotkey" }, { 13, 0, "Digitizers" }, {0, 0x01, "Digitizer"}, @@ -112,6 +112,112 @@ {0, 0x45, "Eraser"}, {0, 0x46, "TabletPick"}, { 15, 0, "PhysicalInterfaceDevice" }, + {0, 0x00, "Undefined"}, + {0, 0x01, "Physical_Interface_Device"}, + {0, 0x20, "Normal"}, + {0, 0x21, "Set_Effect_Report"}, + {0, 0x22, "Effect_Block_Index"}, + {0, 0x23, "Parameter_Block_Offset"}, + {0, 0x24, "ROM_Flag"}, + {0, 0x25, "Effect_Type"}, + {0, 0x26, "ET_Constant_Force"}, + {0, 0x27, "ET_Ramp"}, + {0, 0x28, "ET_Custom_Force_Data"}, + {0, 0x30, "ET_Square"}, + {0, 0x31, "ET_Sine"}, + {0, 0x32, "ET_Triangle"}, + {0, 0x33, "ET_Sawtooth_Up"}, + {0, 0x34, "ET_Sawtooth_Down"}, + {0, 0x40, "ET_Spring"}, + {0, 0x41, "ET_Damper"}, + {0, 0x42, "ET_Inertia"}, + {0, 0x43, "ET_Friction"}, + {0, 0x50, "Duration"}, + {0, 0x51, "Sample_Period"}, + {0, 0x52, "Gain"}, + {0, 0x53, "Trigger_Button"}, + {0, 0x54, "Trigger_Repeat_Interval"}, + {0, 0x55, "Axes_Enable"}, + {0, 0x56, "Direction_Enable"}, + {0, 0x57, "Direction"}, + {0, 0x58, "Type_Specific_Block_Offset"}, + {0, 0x59, "Block_Type"}, + {0, 0x5A, "Set_Envelope_Report"}, + {0, 0x5B, "Attack_Level"}, + {0, 0x5C, "Attack_Time"}, + {0, 0x5D, "Fade_Level"}, + {0, 0x5E, "Fade_Time"}, + {0, 0x5F, "Set_Condition_Report"}, + {0, 0x60, "CP_Offset"}, + {0, 0x61, "Positive_Coefficient"}, + {0, 0x62, "Negative_Coefficient"}, + {0, 0x63, "Positive_Saturation"}, + {0, 0x64, "Negative_Saturation"}, + {0, 0x65, "Dead_Band"}, + {0, 0x66, "Download_Force_Sample"}, + {0, 0x67, "Isoch_Custom_Force_Enable"}, + {0, 0x68, "Custom_Force_Data_Report"}, + {0, 0x69, "Custom_Force_Data"}, + {0, 0x6A, "Custom_Force_Vendor_Defined_Data"}, + {0, 0x6B, "Set_Custom_Force_Report"}, + {0, 0x6C, "Custom_Force_Data_Offset"}, + {0, 0x6D, "Sample_Count"}, + {0, 0x6E, "Set_Periodic_Report"}, + {0, 0x6F, "Offset"}, + {0, 0x70, "Magnitude"}, + {0, 0x71, "Phase"}, + {0, 0x72, "Period"}, + {0, 0x73, "Set_Constant_Force_Report"}, + {0, 0x74, "Set_Ramp_Force_Report"}, + {0, 0x75, "Ramp_Start"}, + {0, 0x76, "Ramp_End"}, + {0, 0x77, "Effect_Operation_Report"}, + {0, 0x78, "Effect_Operation"}, + {0, 0x79, "Op_Effect_Start"}, + {0, 0x7A, "Op_Effect_Start_Solo"}, + {0, 0x7B, "Op_Effect_Stop"}, + {0, 0x7C, "Loop_Count"}, + {0, 0x7D, "Device_Gain_Report"}, + {0, 0x7E, "Device_Gain"}, + {0, 0x7F, "PID_Pool_Report"}, + {0, 0x80, "RAM_Pool_Size"}, + {0, 0x81, "ROM_Pool_Size"}, + {0, 0x82, "ROM_Effect_Block_Count"}, + {0, 0x83, "Simultaneous_Effects_Max"}, + {0, 0x84, "Pool_Alignment"}, + {0, 0x85, "PID_Pool_Move_Report"}, + {0, 0x86, "Move_Source"}, + {0, 0x87, "Move_Destination"}, + {0, 0x88, "Move_Length"}, + {0, 0x89, "PID_Block_Load_Report"}, + {0, 0x8B, "Block_Load_Status"}, + {0, 0x8C, "Block_Load_Success"}, + {0, 0x8D, "Block_Load_Full"}, + {0, 0x8E, "Block_Load_Error"}, + {0, 0x8F, "Block_Handle"}, + {0, 0x90, "PID_Block_Free_Report"}, + {0, 0x91, "Type_Specific_Block_Handle"}, + {0, 0x92, "PID_State_Report"}, + {0, 0x94, "Effect_Playing"}, + {0, 0x95, "PID_Device_Control_Report"}, + {0, 0x96, "PID_Device_Control"}, + {0, 0x97, "DC_Enable_Actuators"}, + {0, 0x98, "DC_Disable_Actuators"}, + {0, 0x99, "DC_Stop_All_Effects"}, + {0, 0x9A, "DC_Device_Reset"}, + {0, 0x9B, "DC_Device_Pause"}, + {0, 0x9C, "DC_Device_Continue"}, + {0, 0x9F, "Device_Paused"}, + {0, 0xA0, "Actuators_Enabled"}, + {0, 0xA4, "Safety_Switch"}, + {0, 0xA5, "Actuator_Override_Switch"}, + {0, 0xA6, "Actuator_Power"}, + {0, 0xA7, "Start_Delay"}, + {0, 0xA8, "Parameter_Block_Size"}, + {0, 0xA9, "Device_Managed_Pool"}, + {0, 0xAA, "Shared_Parameter_Blocks"}, + {0, 0xAB, "Create_New_Effect_Report"}, + {0, 0xAC, "RAM_Pool_Available"}, { 0, 0, NULL } }; @@ -176,7 +282,50 @@ tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent); } if (field->unit) { - tab(n); printk("Unit(%u)\n", field->unit); + char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; + char *units[5][8] = { + { "None", "None", "None", "None", "None", "None", "None", "None" }, + { "None", "Centimeter", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" }, + { "None", "Radians", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" }, + { "None", "Inch", "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" }, + { "None", "Degrees", "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" } + }; + + int i; + int sys; + __u32 data = field->unit; + + /* First nibble tells us which system we're in. */ + sys = data & 0xf; + data >>= 4; + + if(sys > 4) { + tab(n); printk("Unit(Invalid)\n"); + } + else { + int earlier_unit = 0; + + tab(n); printk("Unit(%s : ", systems[sys]); + + for (i=1 ; i>= 4; + if (nibble != 0) { + if(earlier_unit++ > 0) + printk("*"); + printk("%s", units[sys][i]); + if(nibble != 1) { + /* This is a _signed_ nibble(!) */ + + int val = nibble & 0x7; + if(nibble & 0x08) + val = -((0x7 & ~val) +1); + printk("^%d", val); + } + } + } + printk(")\n"); + } } tab(n); printk("Report Size(%u)\n", field->report_size); tab(n); printk("Report Count(%u)\n", field->report_count); diff -Nru a/drivers/usb/hid-input.c b/drivers/usb/hid-input.c --- a/drivers/usb/hid-input.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/hid-input.c Tue Feb 19 18:08:59 2002 @@ -1,11 +1,9 @@ /* - * $Id: hid-input.c,v 1.5 2001/05/23 09:25:02 vojtech Exp $ + * $Id: hid-input.c,v 1.18 2001/11/07 09:01:18 vojtech Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik * - * USB HID to Linux Input mapping module - * - * Sponsored by SuSE + * USB HID to Linux Input mapping */ /* @@ -24,13 +22,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include #include -#include #include #include #include @@ -61,12 +58,13 @@ static struct { __s32 x; __s32 y; -} hid_hat_to_axis[] = {{0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; +} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; static void hidinput_configure_usage(struct hid_device *device, struct hid_field *field, struct hid_usage *usage) { struct input_dev *input = &device->input; int max; + int is_abs = 0; unsigned long *bit; switch (usage->hid & HID_USAGE_PAGE) { @@ -198,6 +196,7 @@ case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */ + set_bit(EV_REP, input->evbit); switch (usage->hid & HID_USAGE) { case 0x000: usage->code = 0; break; case 0x034: usage->code = KEY_SLEEP; break; @@ -205,14 +204,21 @@ case 0x08a: usage->code = KEY_WWW; break; case 0x095: usage->code = KEY_HELP; break; + case 0x0b0: usage->code = KEY_PLAY; break; + case 0x0b1: usage->code = KEY_PAUSE; break; + case 0x0b2: usage->code = KEY_RECORD; break; + case 0x0b3: usage->code = KEY_FASTFORWARD; break; case 0x0b4: usage->code = KEY_REWIND; break; case 0x0b5: usage->code = KEY_NEXTSONG; break; case 0x0b6: usage->code = KEY_PREVIOUSSONG; break; case 0x0b7: usage->code = KEY_STOPCD; break; case 0x0b8: usage->code = KEY_EJECTCD; break; case 0x0cd: usage->code = KEY_PLAYPAUSE; break; - + case 0x0e0: is_abs = 1; + usage->code = ABS_VOLUME; + break; case 0x0e2: usage->code = KEY_MUTE; break; + case 0x0e5: usage->code = KEY_BASSBOOST; break; case 0x0e9: usage->code = KEY_VOLUMEUP; break; case 0x0ea: usage->code = KEY_VOLUMEDOWN; break; @@ -220,7 +226,6 @@ case 0x18a: usage->code = KEY_MAIL; break; case 0x192: usage->code = KEY_CALC; break; case 0x194: usage->code = KEY_FILE; break; - case 0x21a: usage->code = KEY_UNDO; break; case 0x21b: usage->code = KEY_COPY; break; case 0x21c: usage->code = KEY_CUT; break; @@ -235,6 +240,34 @@ case 0x22a: usage->code = KEY_BOOKMARKS; break; default: usage->code = KEY_UNKNOWN; break; + } + + if (is_abs) { + usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; + } else { + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + } + break; + + case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ + + set_bit(EV_REP, input->evbit); + switch (usage->hid & HID_USAGE) { + case 0x021: usage->code = KEY_PRINT; break; + case 0x070: usage->code = KEY_HP; break; + case 0x071: usage->code = KEY_CAMERA; break; + case 0x072: usage->code = KEY_SOUND; break; + case 0x073: usage->code = KEY_QUESTION; break; + + case 0x080: usage->code = KEY_EMAIL; break; + case 0x081: usage->code = KEY_CHAT; break; + case 0x082: usage->code = KEY_SEARCH; break; + case 0x083: usage->code = KEY_CONNECT; break; + case 0x084: usage->code = KEY_FINANCE; break; + case 0x085: usage->code = KEY_SPORT; break; + case 0x086: usage->code = KEY_SHOP; break; + + default: usage->code = KEY_UNKNOWN; break; } @@ -353,7 +386,7 @@ } hid_set_field(field, offset, value); - hid_write_report(hid, field->report); + hid_submit_report(hid, field->report, USB_DIR_OUT); return 0; } @@ -397,6 +430,8 @@ hid->input.close = hidinput_close; hid->input.name = hid->name; + hid->input.phys = hid->phys; + hid->input.uniq = hid->uniq; hid->input.idbus = BUS_USB; hid->input.idvendor = dev->descriptor.idVendor; hid->input.idproduct = dev->descriptor.idProduct; diff -Nru a/drivers/usb/hid.h b/drivers/usb/hid.h --- a/drivers/usb/hid.h Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/hid.h Tue Feb 19 18:08:57 2002 @@ -2,12 +2,10 @@ #define __HID_H /* - * $Id: hid.h,v 1.10 2001/05/10 15:56:07 vojtech Exp $ + * $Id: hid.h,v 1.24 2001/12/27 10:37:41 vojtech Exp $ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2001 Vojtech Pavlik - * - * Sponsored by SuSE */ /* @@ -26,13 +24,24 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ +#include +#include +#include + +/* + * USB HID (Human Interface Device) interface class code + */ + +#define USB_INTERFACE_CLASS_HID 3 + /* * HID class requests */ + #define HID_REQ_GET_REPORT 0x01 #define HID_REQ_GET_IDLE 0x02 #define HID_REQ_GET_PROTOCOL 0x03 @@ -43,86 +52,12 @@ /* * HID class descriptor types */ + #define HID_DT_HID (USB_TYPE_CLASS | 0x01) #define HID_DT_REPORT (USB_TYPE_CLASS | 0x02) #define HID_DT_PHYSICAL (USB_TYPE_CLASS | 0x03) /* - * Utilities for class control messaging - */ -static inline int -hid_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - (duration << 8) | report_id, ifnum, NULL, 0, - HZ * USB_CTRL_SET_TIMEOUT); -} - -static inline int -hid_get_protocol(struct usb_device *dev, int ifnum) -{ - unsigned char type; - int ret; - - if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - HID_REQ_GET_PROTOCOL, - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, ifnum, &type, 1, - HZ * USB_CTRL_GET_TIMEOUT)) < 0) - return ret; - - return type; -} - -static inline int -hid_set_protocol(struct usb_device *dev, int ifnum, int protocol) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - protocol, ifnum, NULL, 0, - HZ * USB_CTRL_SET_TIMEOUT); -} - -static inline int -hid_get_report(struct usb_device *dev, int ifnum, unsigned char type, - unsigned char id, void *buf, int size) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - HID_REQ_GET_REPORT, - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - (type << 8) + id, ifnum, buf, size, - HZ * USB_CTRL_GET_TIMEOUT); -} - -static inline int -hid_set_report(struct usb_device *dev, int ifnum, unsigned char type, - unsigned char id, void *buf, int size) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - (type << 8) + id, ifnum, buf, size, HZ); - // FIXME USB_CTRL_SET_TIMEOUT -} - - -/* - * "Boot Protocol" keyboard/mouse drivers use don't use all of HID; - * they're a lot smaller but can't support all the device features. - */ -#ifndef _HID_BOOT_PROTOCOL - -#include -#include -#include - -/* - * USB HID (Human Interface Device) interface class code - */ - -#define USB_INTERFACE_CLASS_HID 3 - -/* * We parse each description item into this structure. Short items data * values are expanded to 32-bit signed int, long items contain a pointer * into the data area. @@ -240,9 +175,11 @@ #define HID_UP_KEYBOARD 0x00070000 #define HID_UP_LED 0x00080000 #define HID_UP_BUTTON 0x00090000 +#define HID_UP_ORDINAL 0x000a0000 #define HID_UP_CONSUMER 0x000c0000 #define HID_UP_DIGITIZER 0x000d0000 #define HID_UP_PID 0x000f0000 +#define HID_UP_HPVENDOR 0xff7f0000 #define HID_USAGE 0x0000ffff @@ -279,7 +216,7 @@ __s32 logical_maximum; __s32 physical_minimum; __s32 physical_maximum; - unsigned unit_exponent; + __s32 unit_exponent; unsigned unit; unsigned report_id; unsigned report_size; @@ -336,7 +273,7 @@ __s32 logical_maximum; __s32 physical_minimum; __s32 physical_maximum; - unsigned unit_exponent; + __s32 unit_exponent; unsigned unit; struct hid_report *report; /* associated report */ }; @@ -350,8 +287,6 @@ struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */ unsigned maxfield; /* maximum valid field index */ unsigned size; /* size of the report (bits) */ - unsigned idx; /* where we're in data */ - unsigned char *data; /* data for multi-packet reports */ struct hid_device *device; /* associated device */ }; @@ -364,16 +299,20 @@ #define HID_REPORT_TYPES 3 #define HID_BUFFER_SIZE 32 -#define HID_CONTROL_FIFO_SIZE 8 +#define HID_CONTROL_FIFO_SIZE 64 +#define HID_OUTPUT_FIFO_SIZE 64 struct hid_control_fifo { - struct usb_ctrlrequest dr; - char buffer[HID_BUFFER_SIZE]; + unsigned char dir; + struct hid_report *report; }; #define HID_CLAIMED_INPUT 1 #define HID_CLAIMED_HIDDEV 2 +#define HID_CTRL_RUNNING 1 +#define HID_OUT_RUNNING 2 + struct hid_device { /* device report descriptor */ __u8 *rdesc; unsigned rsize; @@ -386,12 +325,23 @@ struct usb_device *dev; /* USB device */ int ifnum; /* USB interface number */ - struct urb urb; /* USB URB structure */ - char buffer[HID_BUFFER_SIZE]; /* Rx buffer */ + unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ - struct urb urbout; /* Output URB */ - struct hid_control_fifo out[HID_CONTROL_FIFO_SIZE]; /* Transmit buffer */ - unsigned char outhead, outtail; /* Tx buffer head & tail */ + struct urb *urbin; /* Input URB */ + char inbuf[HID_BUFFER_SIZE]; /* Input buffer */ + + struct urb *urbctrl; /* Control URB */ + struct usb_ctrlrequest cr; /* Control request struct */ + struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */ + unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */ + char ctrlbuf[HID_BUFFER_SIZE]; /* Control buffer */ + spinlock_t ctrllock; /* Control fifo spinlock */ + + struct urb *urbout; /* Output URB */ + struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ + unsigned char outhead, outtail; /* Output pipe fifo head & tail */ + char outbuf[HID_BUFFER_SIZE]; /* Output buffer */ + spinlock_t outlock; /* Output fifo spinlock */ unsigned claimed; /* Claimed by hidinput, hiddev? */ unsigned quirks; /* Various quirks the device can pull on us */ @@ -400,8 +350,12 @@ void *hiddev; /* The hiddev structure */ int minor; /* Hiddev minor number */ + wait_queue_head_t wait; /* For sleeping */ + int open; /* is the device open by anyone? */ char name[128]; /* Device name */ + char phys[64]; /* Device physical location */ + char uniq[64]; /* Device unique identifier (serial #) */ }; #define HID_GLOBAL_STACK_SIZE 4 @@ -441,19 +395,18 @@ #else #define hid_dump_input(a,b) do { } while (0) #define hid_dump_device(c) do { } while (0) -#endif /* DEBUG */ +#define hid_dump_field(a,b) do { } while (0) +#endif -#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || ( a == 0x000c0001)) +#endif + +/* Applications from HID Usage Tables 4/8/99 Version 1.1 */ +/* We ignore a few input applications that are not widely used */ +#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || ( a == 0x00010080) || ( a == 0x000c0001)) int hid_open(struct hid_device *); void hid_close(struct hid_device *); int hid_find_field(struct hid_device *, unsigned int, unsigned int, struct hid_field **); int hid_set_field(struct hid_field *, unsigned, __s32); -void hid_write_report(struct hid_device *, struct hid_report *); -void hid_read_report(struct hid_device *, struct hid_report *); +void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir); void hid_init_reports(struct hid_device *hid); - -#endif /* !_HID_BOOT_PROTOCOL */ - -#endif /* !__HID_H */ - diff -Nru a/drivers/usb/hiddev.c b/drivers/usb/hiddev.c --- a/drivers/usb/hiddev.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/hiddev.c Tue Feb 19 18:08:59 2002 @@ -400,7 +400,7 @@ if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) return -EINVAL; - hid_read_report(hid, report); + hid_submit_report(hid, report, USB_DIR_IN); return 0; @@ -414,7 +414,7 @@ if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) return -EINVAL; - hid_write_report(hid, report); + hid_submit_report(hid, report, USB_DIR_OUT); return 0; diff -Nru a/drivers/usb/hpusbscsi.c b/drivers/usb/hpusbscsi.c --- a/drivers/usb/hpusbscsi.c Tue Feb 19 18:09:00 2002 +++ b/drivers/usb/hpusbscsi.c Tue Feb 19 18:09:00 2002 @@ -56,12 +56,12 @@ return NULL; DEBUG ("Allocated memory\n"); memset (new, 0, sizeof (struct hpusbscsi)); - new->dataurb = usb_alloc_urb(0); + new->dataurb = usb_alloc_urb(0, GFP_KERNEL); if (!new->dataurb) { kfree (new); return NULL; } - new->controlurb = usb_alloc_urb(0); + new->controlurb = usb_alloc_urb(0, GFP_KERNEL); if (!new->controlurb) { usb_free_urb (new->dataurb); kfree (new); @@ -283,7 +283,12 @@ /* Now we need to decide which callback to give to the urb we send the command with */ if (!srb->bufflen) { - usb_callback = simple_command_callback; + if (srb->cmnd[0] == REQUEST_SENSE){ + hpusbscsi->current_data_pipe = usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in); + usb_callback = request_sense_callback; + } else { + usb_callback = simple_command_callback; + } } else { if (likely(srb->use_sg)) { usb_callback = scatter_gather_callback; @@ -341,8 +346,8 @@ struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]); printk(KERN_DEBUG"SCSI reset requested.\n"); - usb_reset_device(hpusbscsi->dev); - printk(KERN_DEBUG"SCSI reset completed.\n"); + //usb_reset_device(hpusbscsi->dev); + //printk(KERN_DEBUG"SCSI reset completed.\n"); hpusbscsi->state = HP_STATE_FREE; return 0; @@ -382,7 +387,7 @@ return; } hpusbscsi->srb->result &= SCSI_ERR_MASK; - hpusbscsi->srb->result |= hpusbscsi->scsi_state_byte<<1; + hpusbscsi->srb->result |= hpusbscsi->scsi_state_byte; if (hpusbscsi->scallback != NULL && hpusbscsi->state == HP_STATE_WAIT) /* we do a callback to the scsi layer if and only if all data has been transfered */ @@ -425,6 +430,32 @@ hpusbscsi->state = HP_STATE_FREE; TRACE_STATE; } +} + +static void request_sense_callback (struct urb *u) +{ + struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context; + + if (unlikely(u->status<0)) { + handle_usb_error(hpusbscsi); + return; + } + + FILL_BULK_URB( + u, + hpusbscsi->dev, + hpusbscsi->current_data_pipe, + hpusbscsi->srb->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + simple_done, + hpusbscsi + ); + + if (unlikely(0 > usb_submit_urb(u, GFP_ATOMIC))) { + handle_usb_error(hpusbscsi); + return; + } + hpusbscsi->state = HP_STATE_WORKING; } static void scatter_gather_callback(struct urb *u) diff -Nru a/drivers/usb/hpusbscsi.h b/drivers/usb/hpusbscsi.h --- a/drivers/usb/hpusbscsi.h Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/hpusbscsi.h Tue Feb 19 18:08:57 2002 @@ -51,7 +51,8 @@ static void simple_command_callback(struct urb *u); static void scatter_gather_callback(struct urb *u); static void simple_payload_callback (struct urb *u); -static void control_interrupt_callback (struct urb *u); +static void request_sense_callback (struct urb *u); +static void control_interrupt_callback (struct urb *u); static void simple_done (struct urb *u); static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback); static int hpusbscsi_scsi_host_reset (Scsi_Cmnd *srb); diff -Nru a/drivers/usb/hub.c b/drivers/usb/hub.c --- a/drivers/usb/hub.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/hub.c Tue Feb 19 18:08:59 2002 @@ -303,7 +303,7 @@ if (maxp > sizeof(hub->buffer)) maxp = sizeof(hub->buffer); - hub->urb = usb_alloc_urb(0); + hub->urb = usb_alloc_urb(0, GFP_KERNEL); if (!hub->urb) { err("couldn't allocate interrupt urb"); kfree(hub->descriptor); @@ -524,6 +524,29 @@ err("cannot disconnect hub %s", dev->devpath); } +static int usb_hub_port_status(struct usb_device *hub, int port, + u16 *status, u16 *change) +{ + struct usb_port_status *portsts; + int ret = -ENOMEM; + + portsts = kmalloc(sizeof(*portsts), GFP_KERNEL); + if (portsts) { + ret = usb_get_port_status(hub, port + 1, portsts); + if (ret < 0) + err("%s(%s) failed (err = %d)", __FUNCTION__, hub->devpath, ret); + else { + *status = le16_to_cpu(portsts->wPortStatus); + *change = le16_to_cpu(portsts->wPortChange); + dbg("port %d, portstatus %x, change %x, %s", port + 1, + *status, *change, portspeed(*status)); + ret = 0; + } + kfree(portsts); + } + return ret; +} + #define HUB_RESET_TRIES 5 #define HUB_PROBE_TRIES 2 #define HUB_SHORT_RESET_TIME 10 @@ -535,8 +558,8 @@ struct usb_device *dev, unsigned int delay) { int delay_time, ret; - struct usb_port_status portsts; - unsigned short portchange, portstatus; + u16 portstatus; + u16 portchange; for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; @@ -545,18 +568,11 @@ wait_ms(delay); /* read and decode port status */ - ret = usb_get_port_status(hub, port + 1, &portsts); + ret = usb_hub_port_status(hub, port, &portstatus, &portchange); if (ret < 0) { - err("get_port_status(%d) failed (err = %d)", - port + 1, ret); return -1; } - portstatus = le16_to_cpu(portsts.wPortStatus); - portchange = le16_to_cpu(portsts.wPortChange); - dbg("port %d, portstatus %x, change %x, %s", port + 1, - portstatus, portchange, portspeed (portstatus)); - /* Device went away? */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) return 1; @@ -629,16 +645,13 @@ } static void usb_hub_port_connect_change(struct usb_hub *hubstate, int port, - struct usb_port_status *portsts) + u16 portstatus, u16 portchange) { struct usb_device *hub = hubstate->dev; struct usb_device *dev; - unsigned short portstatus, portchange; unsigned int delay = HUB_SHORT_RESET_TIME; int i; - portstatus = le16_to_cpu(portsts->wPortStatus); - portchange = le16_to_cpu(portsts->wPortChange); dbg("hub %s port %d, portstatus %x, change %x, %s", hub->devpath, port + 1, portstatus, portchange, portspeed (portstatus)); @@ -759,7 +772,10 @@ struct usb_device *dev; struct usb_hub *hub; struct usb_hub_status hubsts; - unsigned short hubstatus, hubchange; + u16 hubstatus; + u16 hubchange; + u16 portstatus; + u16 portchange; int i, ret; /* @@ -803,23 +819,15 @@ } for (i = 0; i < hub->descriptor->bNbrPorts; i++) { - struct usb_port_status portsts; - unsigned short portstatus, portchange; - - ret = usb_get_port_status(dev, i + 1, &portsts); + ret = usb_hub_port_status(dev, i, &portstatus, &portchange); if (ret < 0) { - err("hub %s get_port_status failed (err = %d)", - dev->devpath, ret); continue; } - portstatus = le16_to_cpu(portsts.wPortStatus); - portchange = le16_to_cpu(portsts.wPortChange); - if (portchange & USB_PORT_STAT_C_CONNECTION) { dbg("hub %s port %d connection change", dev->devpath, i + 1); - usb_hub_port_connect_change(hub, i, &portsts); + usb_hub_port_connect_change(hub, i, portstatus, portchange); } else if (portchange & USB_PORT_STAT_C_ENABLE) { dbg("hub %s port %d enable change, status %x", dev->devpath, i + 1, portstatus); @@ -840,7 +848,7 @@ "re-enabling...", dev->devpath, i + 1); usb_hub_port_connect_change(hub, - i, &portsts); + i, portstatus, portchange); } } diff -Nru a/drivers/usb/inode.c b/drivers/usb/inode.c --- a/drivers/usb/inode.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/inode.c Tue Feb 19 18:08:57 2002 @@ -40,7 +40,6 @@ #include static struct super_operations usbfs_ops; -static struct address_space_operations usbfs_aops; static struct file_operations usbfs_dir_operations; static struct file_operations default_file_operations; static struct inode_operations usbfs_dir_inode_operations; @@ -142,6 +141,7 @@ /* --------------------------------------------------------------------- */ +/* SMP-safe */ static struct dentry *usbfs_lookup (struct inode *dir, struct dentry *dentry) { d_add(dentry, NULL); @@ -167,7 +167,6 @@ inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_rdev = NODEV; - inode->i_mapping->a_ops = &usbfs_aops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { default: @@ -185,6 +184,7 @@ return inode; } +/* SMP-safe */ static int usbfs_mknod (struct inode *dir, struct dentry *dentry, int mode, int dev) { @@ -209,21 +209,6 @@ return usbfs_mknod (dir, dentry, mode | S_IFREG, 0); } -static int usbfs_link (struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry) -{ - struct inode *inode = old_dentry->d_inode; - - if(S_ISDIR(inode->i_mode)) - return -EPERM; - - inode->i_nlink++; - atomic_inc(&inode->i_count); - dget(dentry); - d_instantiate(dentry, inode); - return 0; -} - static inline int usbfs_positive (struct dentry *dentry) { return dentry->d_inode && !d_unhashed(dentry); @@ -254,29 +239,15 @@ if (usbfs_empty(dentry)) { struct inode *inode = dentry->d_inode; + lock_kernel(); inode->i_nlink--; + unlock_kernel(); dput(dentry); error = 0; } return error; } -static int usbfs_rename (struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - int error = -ENOTEMPTY; - - if (usbfs_empty(new_dentry)) { - struct inode *inode = new_dentry->d_inode; - if (inode) { - inode->i_nlink--; - dput(new_dentry); - } - error = 0; - } - return error; -} - #define usbfs_rmdir usbfs_unlink /* default file operations */ @@ -325,19 +296,9 @@ return 0; } -static int default_sync_file (struct file *file, struct dentry *dentry, - int datasync) -{ - return 0; -} - -static struct address_space_operations usbfs_aops = { -}; - static struct file_operations usbfs_dir_operations = { read: generic_read_dir, readdir: dcache_readdir, - fsync: default_sync_file, }; static struct file_operations default_file_operations = { @@ -345,19 +306,14 @@ write: default_write_file, open: default_open, llseek: default_file_lseek, - fsync: default_sync_file, - mmap: generic_file_mmap, }; static struct inode_operations usbfs_dir_inode_operations = { create: usbfs_create, lookup: usbfs_lookup, - link: usbfs_link, unlink: usbfs_unlink, mkdir: usbfs_mkdir, rmdir: usbfs_rmdir, - mknod: usbfs_mknod, - rename: usbfs_rename, }; static struct super_operations usbfs_ops = { diff -Nru a/drivers/usb/kaweth.c b/drivers/usb/kaweth.c --- a/drivers/usb/kaweth.c Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/kaweth.c Tue Feb 19 18:08:58 2002 @@ -873,8 +873,8 @@ kaweth_dbg("Initializing net device."); - kaweth->tx_urb = usb_alloc_urb(0); - kaweth->rx_urb = usb_alloc_urb(0); + kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + kaweth->rx_urb = usb_alloc_urb(0, GFP_KERNEL); kaweth->net = init_etherdev(0, 0); if (!kaweth->net) { @@ -977,7 +977,7 @@ set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&awd.wqh, &wait); urb->context = &awd; - status = usb_submit_urb(urb, GFP_KERNEL); + status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { // something went wrong usb_free_urb(urb); @@ -1020,7 +1020,7 @@ int retv; int length; - urb = usb_alloc_urb(0); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; diff -Nru a/drivers/usb/konicawc.c b/drivers/usb/konicawc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/konicawc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,737 @@ +/* + * $Id: konicawc.c,v 1.12 2002/02/07 23:18:53 spse Exp $ + * + * konicawc.c - konica webcam driver + * + * Author: Simon Evans + * + * Copyright (C) 2002 Simon Evans + * + * Licence: GPL + * + * Driver for USB webcams based on Konica chipset. This + * chipset is used in Intel YC76 camera. + * + */ + +#include +#include +#include + +#define DEBUG + +#include "usbvideo.h" + +#define MAX_BRIGHTNESS 108 +#define MAX_CONTRAST 108 +#define MAX_SATURATION 108 +#define MAX_SHARPNESS 108 +#define MAX_WHITEBAL 363 + +#define MAX_CAMERAS 1 + +enum ctrl_req { + SetWhitebal = 0x01, + SetBrightness = 0x02, + SetSharpness = 0x03, + SetContrast = 0x04, + SetSaturation = 0x05, +}; + + +enum frame_sizes { + SIZE_160X130 = 0, + SIZE_176X144 = 1, + SIZE_320X240 = 2, +}; + + +static usbvideo_t *cams; + +/* Some default values for inital camera settings, + can be set by modprobe */ + +static int debug; +static enum frame_sizes size; +static int brightness = MAX_BRIGHTNESS/2; +static int contrast = MAX_CONTRAST/2; +static int saturation = MAX_SATURATION/2; +static int sharpness = MAX_SHARPNESS/2; +static int whitebal = 3*(MAX_WHITEBAL/4); + + +struct konicawc { + u8 brightness; /* camera uses 0 - 9, x11 for real value */ + u8 contrast; /* as above */ + u8 saturation; /* as above */ + u8 sharpness; /* as above */ + u8 white_bal; /* 0 - 33, x11 for real value */ + u8 fps; /* Stored as fps * 3 */ + u8 size; /* Frame Size */ + int height; + int width; + struct urb *sts_urb[USBVIDEO_NUMFRAMES]; + u8 sts_buf[USBVIDEO_NUMFRAMES][FRAMES_PER_DESC]; + struct urb *last_data_urb; + int lastframe; +}; + + +#define konicawc_set_misc(uvd, req, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, req, value, index, NULL, 0) +#define konicawc_get_misc(uvd, req, value, index, buf, sz) konicawc_ctrl_msg(uvd, USB_DIR_IN, req, value, index, buf, sz) +#define konicawc_set_value(uvd, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, 2, value, index, NULL, 0) + + +static int konicawc_ctrl_msg(uvd_t *uvd, u8 dir, u8 request, u16 value, u16 index, void *buf, int len) +{ + int retval = usb_control_msg(uvd->dev, + dir ? usb_rcvctrlpipe(uvd->dev, 0) : usb_sndctrlpipe(uvd->dev, 0), + request, 0x40 | dir, value, index, buf, len, HZ); + return retval < 0 ? retval : 0; +} + + +static int konicawc_setup_on_open(uvd_t *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + konicawc_set_misc(uvd, 0x2, 0, 0x0b); + dbg("setting brightness to %d (%d)", cam->brightness, + cam->brightness*11); + konicawc_set_value(uvd, cam->brightness, SetBrightness); + dbg("setting white balance to %d (%d)", cam->white_bal, + cam->white_bal*11); + konicawc_set_value(uvd, cam->white_bal, SetWhitebal); + dbg("setting contrast to %d (%d)", cam->contrast, + cam->contrast*11); + konicawc_set_value(uvd, cam->brightness, SetBrightness); + dbg("setting saturation to %d (%d)", cam->saturation, + cam->saturation*11); + konicawc_set_value(uvd, cam->saturation, SetSaturation); + dbg("setting sharpness to %d (%d)", cam->sharpness, + cam->sharpness*11); + konicawc_set_value(uvd, cam->sharpness, SetSharpness); + dbg("setting size %d", cam->size); + switch(cam->size) { + case 0: + konicawc_set_misc(uvd, 0x2, 0xa, 0x08); + break; + + case 1: + konicawc_set_misc(uvd, 0x2, 4, 0x08); + break; + + case 2: + konicawc_set_misc(uvd, 0x2, 5, 0x08); + break; + } + konicawc_set_misc(uvd, 0x2, 1, 0x0b); + cam->lastframe = -1; + return 0; +} + + +static int konicawc_compress_iso(uvd_t *uvd, struct urb *dataurb, struct urb *stsurb) +{ + char *cdata; + int i, totlen = 0; + unsigned char *status = stsurb->transfer_buffer; + int keep = 0, discard = 0, bad = 0; + static int buttonsts = 0; + + for (i = 0; i < dataurb->number_of_packets; i++) { + int button = buttonsts; + unsigned char sts; + int n = dataurb->iso_frame_desc[i].actual_length; + int st = dataurb->iso_frame_desc[i].status; + cdata = dataurb->transfer_buffer + + dataurb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (st < 0) { + if (debug >= 1) + err("Data error: packet=%d. len=%d. status=%d.", + i, n, st); + uvd->stats.iso_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (n <= 0) { + uvd->stats.iso_skip_count++; + continue; + } + + /* See what the status data said about the packet */ + sts = *(status+stsurb->iso_frame_desc[i].offset); + + /* sts: 0x80-0xff: frame start with frame number (ie 0-7f) + * otherwise: + * bit 0 0:drop packet (padding data) + * 1 keep packet + * + * bit 4 0 button not clicked + * 1 button clicked + * button is used to `take a picture' (in software) + */ + + if(sts < 0x80) { + button = sts & 0x40; + sts &= ~0x40; + } + + /* work out the button status, but dont do + anything with it for now */ + + if(button != buttonsts) { + dbg("button: %sclicked", button ? "" : "un"); + buttonsts = button; + } + + if(sts == 0x01) { /* drop frame */ + discard++; + continue; + } + + if((sts > 0x01) && (sts < 0x80)) { + info("unknown status %2.2x", sts); + bad++; + continue; + } + + keep++; + if(*(status+i) & 0x80) { /* frame start */ + unsigned char marker[] = { 0, 0xff, 0, 0x00 }; + if(debug > 1) + dbg("Adding Marker packet = %d, frame = %2.2x", + i, *(status+i)); + marker[3] = *(status+i) - 0x80; + RingQueue_Enqueue(&uvd->dp, marker, 4); + totlen += 4; + } + totlen += n; /* Little local accounting */ + if(debug > 5) + dbg("Adding packet %d, bytes = %d", i, n); + RingQueue_Enqueue(&uvd->dp, cdata, n); + + } + if(debug > 8) { + dbg("finished: keep = %d discard = %d bad = %d added %d bytes", + keep, discard, bad, totlen); + } + return totlen; +} + + +static void konicawc_isoc_irq(struct urb *urb) +{ + int i, len = 0; + uvd_t *uvd = urb->context; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + /* We don't want to do anything if we are about to be removed! */ + if (!CAMERA_IS_OPERATIONAL(uvd)) + return; + + if (urb->actual_length > 32) { + cam->last_data_urb = urb; + return; + + } + + if (!uvd->streaming) { + if (debug >= 1) + info("Not streaming, but interrupt!"); + return; + } + + uvd->stats.urb_count++; + if (urb->actual_length <= 0) + goto urb_done_with; + + /* Copy the data received into ring queue */ + if(cam->last_data_urb) { + len = konicawc_compress_iso(uvd, cam->last_data_urb, urb); + for (i = 0; i < FRAMES_PER_DESC; i++) { + cam->last_data_urb->iso_frame_desc[i].status = 0; + cam->last_data_urb->iso_frame_desc[i].actual_length = 0; + } + cam->last_data_urb = NULL; + } + uvd->stats.urb_length = len; + if (len <= 0) { + goto urb_done_with; + } + + /* Here we got some data */ + uvd->stats.data_count += len; + RingQueue_WakeUpInterruptible(&uvd->dp); + +urb_done_with: + + for (i = 0; i < FRAMES_PER_DESC; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + return; +} + + +static int konicawc_start_data(uvd_t *uvd) +{ + struct usb_device *dev = uvd->dev; + int i, errFlag; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + if (!CAMERA_IS_OPERATIONAL(uvd)) { + err("Camera is not operational"); + return -EFAULT; + } + uvd->curframe = -1; + + /* Alternate interface 1 is is the biggest frame size */ + i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); + if (i < 0) { + err("usb_set_interface error"); + uvd->last_error = i; + return -EBUSY; + } + + /* We double buffer the Iso lists */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + int j, k; + struct urb *urb = uvd->sbuf[i].urb; + urb->dev = dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = uvd->sbuf[i].data; + urb->complete = konicawc_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = uvd->iso_packet_len; + } + + urb = cam->sts_urb[i]; + urb->dev = dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = cam->sts_buf[i]; + urb->complete = konicawc_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAMES_PER_DESC; + for (j=0; j < FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j; + urb->iso_frame_desc[j].length = 1; + } + } + + + /* Link URBs into a ring so that they invoke each other infinitely */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + if ((i+1) < USBVIDEO_NUMSBUF) { + cam->sts_urb[i]->next = uvd->sbuf[i].urb; + uvd->sbuf[i].urb->next = cam->sts_urb[i+1]; + } else { + cam->sts_urb[i]->next = uvd->sbuf[i].urb; + uvd->sbuf[i].urb->next = cam->sts_urb[0]; + } + } + + /* Submit all URBs */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); + if (errFlag) + err ("usb_submit_isoc(%d) ret %d", i, errFlag); + + errFlag = usb_submit_urb(cam->sts_urb[i], GFP_KERNEL); + if (errFlag) + err("usb_submit_isoc(%d) ret %d", i, errFlag); + } + + uvd->streaming = 1; + if (debug > 1) + dbg("streaming=1 video_endp=$%02x", uvd->video_endp); + return 0; +} + + +static void konicawc_stop_data(uvd_t *uvd) +{ + int i, j; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) + return; + + /* Unschedule all of the iso td's */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + j = usb_unlink_urb(uvd->sbuf[i].urb); + if (j < 0) + err("usb_unlink_urb() error %d.", j); + + j = usb_unlink_urb(cam->sts_urb[i]); + if (j < 0) + err("usb_unlink_urb() error %d.", j); + } + + uvd->streaming = 0; + + if (!uvd->remove_pending) { + /* Set packet size to 0 */ + j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); + if (j < 0) { + err("usb_set_interface() error %d.", j); + uvd->last_error = j; + } + } +} + + +static void konicawc_process_isoc(uvd_t *uvd, usbvideo_frame_t *frame) +{ + int n; + int maxline, yplanesz; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + assert(uvd != NULL); + assert(frame != NULL); + + maxline = (cam->height * cam->width * 3) / (2 * 384); + yplanesz = cam->height * cam->width; + if(debug > 5) + dbg("maxline = %d yplanesz = %d", maxline, yplanesz); + + if(debug > 3) + dbg("Frame state = %d", frame->scanstate); + + if(frame->scanstate == ScanState_Scanning) { + int drop = 0; + int curframe; + int fdrops = 0; + if(debug > 3) + dbg("Searching for marker, queue len = %d", RingQueue_GetLength(&uvd->dp)); + while(RingQueue_GetLength(&uvd->dp) >= 4) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) && + (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 3) < 0x80)) { + curframe = RING_QUEUE_PEEK(&uvd->dp, 3); + if(cam->lastframe != -1) { + if(curframe < cam->lastframe) { + fdrops = (curframe + 0x80) - cam->lastframe; + } else { + fdrops = curframe - cam->lastframe; + } + fdrops--; + if(fdrops) + info("Dropped %d frames (%d -> %d)", fdrops, + cam->lastframe, curframe); + } + cam->lastframe = curframe; + frame->curline = 0; + frame->scanstate = ScanState_Lines; + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4); + break; + } + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); + drop++; + } + } + + if(frame->scanstate == ScanState_Scanning) + return; + + /* Try to move data from queue into frame buffer + * We get data in blocks of 384 bytes made up of: + * 256 Y, 64 U, 64 V. + * This needs to be written out as a Y plane, a U plane and a V plane. + */ + + while ( frame->curline < maxline && (n = RingQueue_GetLength(&uvd->dp)) >= 384) { + /* Y */ + RingQueue_Dequeue(&uvd->dp, frame->data + (frame->curline * 256), 256); + /* U */ + RingQueue_Dequeue(&uvd->dp, frame->data + yplanesz + (frame->curline * 64), 64); + /* V */ + RingQueue_Dequeue(&uvd->dp, frame->data + (5 * yplanesz)/4 + (frame->curline * 64), 64); + frame->seqRead_Length += 384; + frame->curline++; + } + /* See if we filled the frame */ + if (frame->curline == maxline) { + if(debug > 5) + dbg("got whole frame"); + + frame->frameState = FrameState_Done_Hold; + frame->curline = 0; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + + +static int konicawc_calculate_fps(uvd_t *uvd) +{ + struct konicawc *t = uvd->user_data; + dbg(""); + + return (t->fps)/3; +} + + +static void konicawc_configure_video(uvd_t *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + u8 buf[2]; + + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); + + RESTRICT_TO_RANGE(brightness, 0, MAX_BRIGHTNESS); + RESTRICT_TO_RANGE(contrast, 0, MAX_CONTRAST); + RESTRICT_TO_RANGE(saturation, 0, MAX_SATURATION); + RESTRICT_TO_RANGE(sharpness, 0, MAX_SHARPNESS); + RESTRICT_TO_RANGE(whitebal, 0, MAX_WHITEBAL); + + cam->brightness = brightness / 11; + cam->contrast = contrast / 11; + cam->saturation = saturation / 11; + cam->sharpness = sharpness / 11; + cam->white_bal = whitebal / 11; + + uvd->vpic.colour = 108; + uvd->vpic.hue = 108; + uvd->vpic.brightness = brightness; + uvd->vpic.contrast = contrast; + uvd->vpic.whiteness = whitebal; + uvd->vpic.depth = 6; + uvd->vpic.palette = VIDEO_PALETTE_YUV420P; + + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); + strcpy(uvd->vcap.name, "Konica Webcam"); + uvd->vcap.type = VID_TYPE_CAPTURE; + uvd->vcap.channels = 1; + uvd->vcap.audios = 0; + uvd->vcap.maxwidth = cam->width; + uvd->vcap.maxheight = cam->height; + uvd->vcap.minwidth = cam->width; + uvd->vcap.minheight = cam->height; + + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); + uvd->vchan.flags = 0 ; + uvd->vchan.tuners = 0; + uvd->vchan.channel = 0; + uvd->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(uvd->vchan.name, "Camera"); + + /* Talk to device */ + dbg("device init"); + if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2)) + dbg("3,10 -> %2.2x %2.2x", buf[0], buf[1]); + if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2)) + dbg("3,10 -> %2.2x %2.2x", buf[0], buf[1]); + if(konicawc_set_misc(uvd, 0x2, 0, 0xd)) + dbg("2,0,d failed"); + dbg("setting initial values"); + +} + + +static void *konicawc_probe(struct usb_device *dev, unsigned int ifnum ,const struct usb_device_id *devid) +{ + uvd_t *uvd = NULL; + int i, nas; + int actInterface=-1, inactInterface=-1, maxPS=0; + unsigned char video_ep = 0; + + if (debug >= 1) + dbg("konicawc_probe(%p,%u.)", dev, ifnum); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return NULL; + + info("Konica Webcam (rev. 0x%04x)", dev->descriptor.bcdDevice); + + /* Validate found interface: must have one ISO endpoint */ + nas = dev->actconfig->interface[ifnum].num_altsetting; + if (debug > 0) + info("Number of alternate settings=%d.", nas); + if (nas < 8) { + err("Too few alternate settings for this camera!"); + return NULL; + } + /* Validate all alternate settings */ + for (i=0; i < nas; i++) { + const struct usb_interface_descriptor *interface; + const struct usb_endpoint_descriptor *endpoint; + + interface = &dev->actconfig->interface[ifnum].altsetting[i]; + if (interface->bNumEndpoints != 2) { + err("Interface %d. has %u. endpoints!", + ifnum, (unsigned)(interface->bNumEndpoints)); + return NULL; + } + endpoint = &interface->endpoint[1]; + dbg("found endpoint: addr: 0x%2.2x maxps = 0x%4.4x", + endpoint->bEndpointAddress, endpoint->wMaxPacketSize); + if (video_ep == 0) + video_ep = endpoint->bEndpointAddress; + else if (video_ep != endpoint->bEndpointAddress) { + err("Alternate settings have different endpoint addresses!"); + return NULL; + } + if ((endpoint->bmAttributes & 0x03) != 0x01) { + err("Interface %d. has non-ISO endpoint!", ifnum); + return NULL; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + err("Interface %d. has ISO OUT endpoint!", ifnum); + return NULL; + } + if (endpoint->wMaxPacketSize == 0) { + if (inactInterface < 0) + inactInterface = i; + else { + err("More than one inactive alt. setting!"); + return NULL; + } + } else { + if (actInterface < 0) { + actInterface = i; + maxPS = endpoint->wMaxPacketSize; + if (debug > 0) + info("Active setting=%d. maxPS=%d.", + i, maxPS); + } else { + /* Got another active alt. setting */ + if (maxPS < endpoint->wMaxPacketSize) { + /* This one is better! */ + actInterface = i; + maxPS = endpoint->wMaxPacketSize; + if (debug > 0) { + info("Even better active setting=%d. maxPS=%d.", + i, maxPS); + } + } + } + } + } + + /* Code below may sleep, need to lock module while we are here */ + MOD_INC_USE_COUNT; + uvd = usbvideo_AllocateDevice(cams); + if (uvd != NULL) { + struct konicawc *konicawc_data = (struct konicawc *)(uvd->user_data); + /* Here uvd is a fully allocated uvd_t object */ + + for(i = 0; i < USBVIDEO_NUMSBUF; i++) { + konicawc_data->sts_urb[i] = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + } + + switch(size) { + case SIZE_160X130: + default: + konicawc_data->height = 136; + konicawc_data->width = 160; + konicawc_data->size = SIZE_160X130; + break; + + case SIZE_176X144: + konicawc_data->height = 144; + konicawc_data->width = 176; + konicawc_data->size = SIZE_176X144; + break; + + case SIZE_320X240: + konicawc_data->height = 240; + konicawc_data->width = 320; + konicawc_data->size = SIZE_320X240; + break; + } + + uvd->flags = 0; + uvd->debug = debug; + uvd->dev = dev; + uvd->iface = ifnum; + uvd->ifaceAltInactive = inactInterface; + uvd->ifaceAltActive = actInterface; + uvd->video_endp = video_ep; + uvd->iso_packet_len = maxPS; + uvd->paletteBits = 1L << VIDEO_PALETTE_YUV420P; + uvd->defaultPalette = VIDEO_PALETTE_YUV420P; + uvd->canvas = VIDEOSIZE(konicawc_data->width, konicawc_data->height); + uvd->videosize = uvd->canvas; + + /* Initialize konicawc specific data */ + konicawc_configure_video(uvd); + + i = usbvideo_RegisterVideoDevice(uvd); + uvd->max_frame_size = (konicawc_data->width * konicawc_data->height * 3)/2; + if (i != 0) { + err("usbvideo_RegisterVideoDevice() failed."); + uvd = NULL; + } + } + MOD_DEC_USE_COUNT; + return uvd; +} + + +static int __init konicawc_init(void) +{ + usbvideo_cb_t cbTbl; + memset(&cbTbl, 0, sizeof(cbTbl)); + cbTbl.probe = konicawc_probe; + cbTbl.setupOnOpen = konicawc_setup_on_open; + cbTbl.processData = konicawc_process_isoc; + cbTbl.getFPS = konicawc_calculate_fps; + cbTbl.startDataPump = konicawc_start_data; + cbTbl.stopDataPump = konicawc_stop_data; + return usbvideo_register( + &cams, + MAX_CAMERAS, + sizeof(struct konicawc), + "konicawc", + &cbTbl, + THIS_MODULE); +} + + +static void __exit konicawc_cleanup(void) +{ + usbvideo_Deregister(&cams); +} + +#if defined(usb_device_id_ver) + +static __devinitdata struct usb_device_id id_table[] = { + { USB_DEVICE(0x04c8, 0x0720) }, /* Intel YC 76 */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, id_table); +#endif /* defined(usb_device_id_ver) */ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Evans "); +MODULE_DESCRIPTION("Konica Webcam driver"); +MODULE_PARM(size, "i"); +MODULE_PARM_DESC(size, "Frame Size 0: 160x136 1: 176x144 2: 320x240"); +MODULE_PARM(brightness, "i"); +MODULE_PARM_DESC(brightness, "Initial brightness 0 - 108"); +MODULE_PARM(contrast, "i"); +MODULE_PARM_DESC(contrast, "Initial contrast 0 - 108"); +MODULE_PARM(saturation, "i"); +MODULE_PARM_DESC(saturation, "Initial saturation 0 - 108"); +MODULE_PARM(sharpness, "i"); +MODULE_PARM_DESC(sharpness, "Initial brightness 0 - 108"); +MODULE_PARM(whitebal, "i"); +MODULE_PARM_DESC(whitebal, "Initial white balance 0 - 363"); +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); +module_init(konicawc_init); +module_exit(konicawc_cleanup); diff -Nru a/drivers/usb/mdc800.c b/drivers/usb/mdc800.c --- a/drivers/usb/mdc800.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/mdc800.c Tue Feb 19 18:08:57 2002 @@ -969,9 +969,9 @@ try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL)); try (mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL)); - try (mdc800->irq_urb=usb_alloc_urb (0)); - try (mdc800->download_urb=usb_alloc_urb (0)); - try (mdc800->write_urb=usb_alloc_urb (0)); + try (mdc800->irq_urb=usb_alloc_urb (0, GFP_KERNEL)); + try (mdc800->download_urb=usb_alloc_urb (0, GFP_KERNEL)); + try (mdc800->write_urb=usb_alloc_urb (0, GFP_KERNEL)); /* Register the driver */ if (usb_register (&mdc800_usb_driver) < 0) diff -Nru a/drivers/usb/microtek.c b/drivers/usb/microtek.c --- a/drivers/usb/microtek.c Tue Feb 19 18:09:00 2002 +++ b/drivers/usb/microtek.c Tue Feb 19 18:09:00 2002 @@ -936,7 +936,7 @@ } memset( new_desc, 0, sizeof(*new_desc) ); - new_desc->urb = usb_alloc_urb(0); + new_desc->urb = usb_alloc_urb(0, GFP_KERNEL); if (!new_desc->urb) { kfree(new_desc); return NULL; diff -Nru a/drivers/usb/ov511.c b/drivers/usb/ov511.c --- a/drivers/usb/ov511.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/ov511.c Tue Feb 19 18:08:59 2002 @@ -47,6 +47,7 @@ #include #include #include +#include #if defined (__i386__) #include @@ -372,48 +373,6 @@ static unsigned char yQuanTable518[] = OV518_YQUANTABLE; static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE; -/********************************************************************** - * - * Memory management - * - * This is a shameless copy from the USB-cpia driver (linux kernel - * version 2.3.29 or so, I have no idea what this code actually does ;). - * Actually it seems to be a copy of a shameless copy of the bttv-driver. - * Or that is a copy of a shameless copy of ... (To the powers: is there - * no generic kernel-function to do this sort of stuff?) - * - * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says - * there will be one, but apparentely not yet -jerdfelt - * - * So I copied it again for the OV511 driver -claudio - **********************************************************************/ - -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long -uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if (pte_present(pte)) { - ret = (unsigned long) - page_address(pte_page(pte)); - ret |= (adr & (PAGE_SIZE - 1)); - } - } - } - - return ret; -} - /* Here we want the physical address of the memory. * This is used when initializing the contents of the * area and marking the pages as reserved. @@ -424,7 +383,7 @@ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); + kva = page_address(vmalloc_to_page(va)); ret = __pa(kva); return ret; } @@ -4426,7 +4385,7 @@ } for (n = 0; n < OV511_NUMSBUF; n++) { - urb = usb_alloc_urb(FRAMES_PER_DESC); + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (!urb) { err("init isoc: usb_alloc_urb ret. NULL"); diff -Nru a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c --- a/drivers/usb/pegasus.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/pegasus.c Tue Feb 19 18:08:59 2002 @@ -858,25 +858,25 @@ pegasus->dev_index = dev_index; init_waitqueue_head( &pegasus->ctrl_wait ); - pegasus->ctrl_urb = usb_alloc_urb(0); + pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->ctrl_urb) { kfree (pegasus); return NULL; } - pegasus->rx_urb = usb_alloc_urb(0); + pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->rx_urb) { usb_free_urb (pegasus->ctrl_urb); kfree (pegasus); return NULL; } - pegasus->tx_urb = usb_alloc_urb(0); + pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->tx_urb) { usb_free_urb (pegasus->rx_urb); usb_free_urb (pegasus->ctrl_urb); kfree (pegasus); return NULL; } - pegasus->intr_urb = usb_alloc_urb(0); + pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->intr_urb) { usb_free_urb (pegasus->tx_urb); usb_free_urb (pegasus->rx_urb); diff -Nru a/drivers/usb/pegasus.h b/drivers/usb/pegasus.h --- a/drivers/usb/pegasus.h Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/pegasus.h Tue Feb 19 18:08:59 2002 @@ -134,9 +134,11 @@ #define VENDOR_ALLIEDTEL 0x07c9 #define VENDOR_BELKIN 0x050d #define VENDOR_BILLIONTON 0x08dd +#define VENDOR_COMPAQ 0x049f #define VENDOR_COREGA 0x07aa #define VENDOR_DLINK 0x2001 #define VENDOR_ELSA 0x05cc +#define VENDOR_HAWKING 0x0e66 #define VENDOR_IODATA 0x04bb #define VENDOR_KINGSTON 0x0951 #define VENDOR_LANEED 0x056e @@ -190,6 +192,8 @@ DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Billionton USBLP-100", VENDOR_BILLIONTON, 0x0987, DEFAULT_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "iPAQ Networking 10/100 USB", VENDOR_COMPAQ, 0x8511, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Billionton USBEL-100", VENDOR_BILLIONTON, 0x0988, DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Billionton USBE-100", VENDOR_BILLIONTON, 0x8511, @@ -212,8 +216,12 @@ DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000, DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "Hawking UF100 10/100 Ethernet", VENDOR_HAWKING, 0x400c, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "IO DATA USB ET/TX", VENDOR_IODATA, 0x0904, DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "IO DATA USB ET/TX-S", VENDOR_IODATA, 0x0913, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Kingston KNU101TX Ethernet", VENDOR_KINGSTON, 0x000a, DEFAULT_GPIO_RESET) PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002, diff -Nru a/drivers/usb/pwc-if.c b/drivers/usb/pwc-if.c --- a/drivers/usb/pwc-if.c Tue Feb 19 18:09:00 2002 +++ b/drivers/usb/pwc-if.c Tue Feb 19 18:09:00 2002 @@ -52,6 +52,7 @@ #include #include #include +#include #include #include "pwc.h" @@ -177,37 +178,6 @@ /***************************************************************************/ /* Private functions */ -/* Memory management functions, nicked from cpia.c, which nicked them from - bttv.c. So far, I've counted duplication of this code 6 times - (bttv, cpia, ibmcam, ov511, pwc, ieee1394). - */ - -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if(pte_present(pte)) { - ret = (unsigned long) page_address(pte_page(pte)); - ret |= (adr & (PAGE_SIZE - 1)); - - } - } - } - return ret; -} - - - /* Here we want the physical address of the memory. * This is used when initializing the contents of the * area and marking the pages as reserved. @@ -217,7 +187,7 @@ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); + kva = page_address(vmalloc_to_page(va)); ret = __pa(kva); return ret; } @@ -827,7 +797,7 @@ ret = 0; for (i = 0; i < MAX_ISO_BUFS; i++) { - urb = usb_alloc_urb(ISO_FRAMES_PER_DESC); + urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) { Err("Failed to allocate urb %d\n", i); ret = -ENOMEM; diff -Nru a/drivers/usb/scanner.c b/drivers/usb/scanner.c --- a/drivers/usb/scanner.c Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/scanner.c Tue Feb 19 18:08:58 2002 @@ -974,7 +974,7 @@ } memset (scn, 0, sizeof(struct scn_usb_data)); - scn->scn_irq = usb_alloc_urb(0); + scn->scn_irq = usb_alloc_urb(0, GFP_KERNEL); if (!scn->scn_irq) { kfree(scn); up(&scn_mutex); diff -Nru a/drivers/usb/se401.c b/drivers/usb/se401.c --- a/drivers/usb/se401.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/se401.c Tue Feb 19 18:08:59 2002 @@ -40,6 +40,7 @@ #include #include #include +#include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) #define virt_to_page(arg) MAP_NR(arg) @@ -80,44 +81,8 @@ * * Memory management * - * This is a shameless copy from the USB-cpia driver (linux kernel - * version 2.3.29 or so, I have no idea what this code actually does ;). - * Actually it seems to be a copy of a shameless copy of the bttv-driver. - * Or that is a copy of a shameless copy of ... (To the powers: is there - * no generic kernel-function to do this sort of stuff?) - * - * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says - * there will be one, but apparentely not yet -jerdfelt - * - * So I copied it again for the ov511 driver -claudio - * - * Same for the se401 driver -Jeroen **********************************************************************/ -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if (pte_present(pte)) { - ret = (unsigned long) page_address(pte_page(pte)); - ret |= (adr & (PAGE_SIZE - 1)); - } - } - } - - return ret; -} - /* Here we want the physical address of the memory. * This is used when initializing the contents of the * area and marking the pages as reserved. @@ -127,7 +92,7 @@ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); + kva = page_address(vmalloc_to_page(va)); ret = __pa(kva); return ret; } @@ -644,7 +609,7 @@ } for (i=0; ireadcount=0; /* Start interrupt transfers for snapshot button */ - se401->inturb=usb_alloc_urb(0); + se401->inturb=usb_alloc_urb(0, GFP_KERNEL); if (!se401->inturb) { info("Allocation of inturb failed"); return 1; diff -Nru a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c --- a/drivers/usb/serial/digi_acceleport.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/serial/digi_acceleport.c Tue Feb 19 18:08:59 2002 @@ -415,14 +415,14 @@ /* Structures */ -typedef struct digi_serial { +struct digi_serial { spinlock_t ds_serial_lock; struct usb_serial_port *ds_oob_port; /* out-of-band port */ int ds_oob_port_num; /* index of out-of-band port */ int ds_device_started; -} digi_serial_t; +}; -typedef struct digi_port { +struct digi_port { spinlock_t dp_port_lock; int dp_port_num; int dp_out_buf_len; @@ -441,7 +441,7 @@ int dp_in_close; /* close in progress */ wait_queue_head_t dp_close_wait; /* wait queue for close */ struct tq_struct dp_wakeup_task; -} digi_port_t; +}; /* Local Function Declarations */ @@ -599,7 +599,7 @@ { unsigned long flags; - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); spin_lock_irqsave( &priv->dp_port_lock, flags ); @@ -645,8 +645,8 @@ int ret = 0; int len; - struct usb_serial_port *oob_port = (struct usb_serial_port *)((digi_serial_t *)port->serial->private)->ds_oob_port; - digi_port_t *oob_priv = (digi_port_t *)oob_port->private; + struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)port->serial->private)->ds_oob_port; + struct digi_port *oob_priv = (struct digi_port *)oob_port->private; unsigned long flags = 0; @@ -714,7 +714,7 @@ int ret = 0; int len; - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); unsigned char *data = port->write_urb->transfer_buffer; unsigned long flags = 0; @@ -800,9 +800,9 @@ { int ret; - digi_port_t *port_priv = (digi_port_t *)port->private; - struct usb_serial_port *oob_port = (struct usb_serial_port *)((digi_serial_t *)port->serial->private)->ds_oob_port; - digi_port_t *oob_priv = (digi_port_t *)oob_port->private; + struct digi_port *port_priv = (struct digi_port *)port->private; + struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)port->serial->private)->ds_oob_port; + struct digi_port *oob_priv = (struct digi_port *)oob_port->private; unsigned char *data = oob_port->write_urb->transfer_buffer; unsigned long flags = 0; @@ -879,7 +879,7 @@ int ret; unsigned char buf[2]; - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); unsigned long flags = 0; @@ -919,7 +919,7 @@ { unsigned long flags; - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); dbg( "digi_rx_throttle: TOP: port=%d", priv->dp_port_num ); @@ -940,7 +940,7 @@ int ret = 0; int len; unsigned long flags; - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); struct tty_struct *tty = port->tty; @@ -984,7 +984,7 @@ struct termios *old_termios ) { - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); unsigned int iflag = port->tty->termios->c_iflag; unsigned int cflag = port->tty->termios->c_cflag; unsigned int old_iflag = old_termios->c_iflag; @@ -1208,7 +1208,7 @@ unsigned int cmd, unsigned long arg ) { - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); unsigned int val; unsigned long flags = 0; @@ -1260,7 +1260,7 @@ { int ret,data_len,new_len; - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); unsigned char *data = port->write_urb->transfer_buffer; unsigned char user_buf[64]; /* 64 bytes is max USB bulk packet */ unsigned long flags = 0; @@ -1347,14 +1347,14 @@ struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial; - digi_port_t *priv; + struct digi_port *priv; int ret = 0; dbg( "digi_write_bulk_callback: TOP, urb->status=%d", urb->status ); /* port and serial sanity check */ - if( port == NULL || (priv=(digi_port_t *)(port->private)) == NULL ) { + if( port == NULL || (priv=(struct digi_port *)(port->private)) == NULL ) { err( __FUNCTION__ ": port or port->private is NULL, status=%d", urb->status ); return; @@ -1367,7 +1367,7 @@ /* handle oob callback */ if( priv->dp_port_num - == ((digi_serial_t *)(serial->private))->ds_oob_port_num ) { + == ((struct digi_serial *)(serial->private))->ds_oob_port_num ) { dbg( "digi_write_bulk_callback: oob callback" ); spin_lock( &priv->dp_port_lock ); priv->dp_write_urb_in_use = 0; @@ -1427,7 +1427,7 @@ { int room; - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); unsigned long flags = 0; @@ -1450,7 +1450,7 @@ static int digi_chars_in_buffer( struct usb_serial_port *port ) { - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); if( port->write_urb->status == -EINPROGRESS @@ -1471,7 +1471,7 @@ int ret; unsigned char buf[32]; - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); struct termios not_termios; unsigned long flags = 0; @@ -1548,7 +1548,7 @@ int ret; unsigned char buf[32]; struct tty_struct *tty = port->tty; - digi_port_t *priv = (digi_port_t *)port->private; + struct digi_port *priv = (struct digi_port *)port->private; unsigned long flags = 0; @@ -1656,7 +1656,7 @@ { int i,ret = 0; - digi_serial_t *serial_priv = (digi_serial_t *)serial->private; + struct digi_serial *serial_priv = (struct digi_serial *)serial->private; struct usb_serial_port *port; @@ -1695,8 +1695,8 @@ { int i; - digi_port_t *priv; - digi_serial_t *serial_priv; + struct digi_port *priv; + struct digi_serial *serial_priv; dbg( "digi_startup: TOP" ); @@ -1707,9 +1707,9 @@ /* allocate port private structure */ priv = serial->port[i].private = - (digi_port_t *)kmalloc( sizeof(digi_port_t), + (struct digi_port *)kmalloc( sizeof(struct digi_port), GFP_KERNEL ); - if( priv == (digi_port_t *)0 ) { + if( priv == (struct digi_port *)0 ) { while( --i >= 0 ) kfree( serial->port[i].private ); return( 1 ); /* error */ @@ -1742,9 +1742,9 @@ /* allocate serial private structure */ serial_priv = serial->private = - (digi_serial_t *)kmalloc( sizeof(digi_serial_t), + (struct digi_serial *)kmalloc( sizeof(struct digi_serial), GFP_KERNEL ); - if( serial_priv == (digi_serial_t *)0 ) { + if( serial_priv == (struct digi_serial *)0 ) { for( i=0; itype->num_ports+1; i++ ) kfree( serial->port[i].private ); return( 1 ); /* error */ @@ -1765,7 +1765,7 @@ { int i; - digi_port_t *priv; + struct digi_port *priv; unsigned long flags; @@ -1800,14 +1800,14 @@ { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - digi_port_t *priv; + struct digi_port *priv; int ret; dbg( "digi_read_bulk_callback: TOP" ); /* port sanity check, do not resubmit if port is not valid */ - if( port == NULL || (priv=(digi_port_t *)(port->private)) == NULL ) { + if( port == NULL || (priv=(struct digi_port *)(port->private)) == NULL ) { err( __FUNCTION__ ": port or port->private is NULL, status=%d", urb->status ); return; @@ -1827,7 +1827,7 @@ /* handle oob or inb callback, do not resubmit if error */ if( priv->dp_port_num - == ((digi_serial_t *)(port->serial->private))->ds_oob_port_num ) { + == ((struct digi_serial *)(port->serial->private))->ds_oob_port_num ) { if( digi_read_oob_callback( urb ) != 0 ) return; } else { @@ -1860,7 +1860,7 @@ struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct tty_struct *tty = port->tty; - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); int opcode = ((unsigned char *)urb->transfer_buffer)[0]; int len = ((unsigned char *)urb->transfer_buffer)[1]; int status = ((unsigned char *)urb->transfer_buffer)[2]; @@ -1972,7 +1972,7 @@ struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; - digi_port_t *priv = (digi_port_t *)(port->private); + struct digi_port *priv = (struct digi_port *)(port->private); int opcode, line, status, val; int i; diff -Nru a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c --- a/drivers/usb/serial/empeg.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/serial/empeg.c Tue Feb 19 18:08:59 2002 @@ -607,7 +607,7 @@ /* create our write urb pool and transfer buffers */ spin_lock_init (&write_urb_pool_lock); for (i = 0; i < NUM_URBS; ++i) { - urb = usb_alloc_urb(0); + urb = usb_alloc_urb(0, GFP_KERNEL); write_urb_pool[i] = urb; if (urb == NULL) { err("No more urbs???"); diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c --- a/drivers/usb/serial/ftdi_sio.c Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/serial/ftdi_sio.c Tue Feb 19 18:08:58 2002 @@ -154,7 +154,7 @@ struct ftdi_private { - ftdi_type_t ftdi_type; + enum ftdi_type ftdi_type; __u16 last_set_data_urb_value ; /* the last data state set - needed for doing a break */ int write_offset; }; @@ -652,7 +652,7 @@ } /* ftdi_sio_serial_read_bulk_callback */ -static __u16 translate_baudrate_to_ftdi(unsigned int cflag, ftdi_type_t ftdi_type) +static __u16 translate_baudrate_to_ftdi(unsigned int cflag, enum ftdi_type ftdi_type) { /* translate_baudrate_to_ftdi */ __u16 urb_value = ftdi_sio_b9600; 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 19 18:08:59 2002 +++ b/drivers/usb/serial/ftdi_sio.h Tue Feb 19 18:08:59 2002 @@ -87,13 +87,13 @@ * Data: None */ -typedef enum { +enum ftdi_type { sio = 1, F8U232AM = 2, -} ftdi_type_t; +}; -typedef enum { +enum { ftdi_sio_b300 = 0, ftdi_sio_b600 = 1, ftdi_sio_b1200 = 2, @@ -104,10 +104,10 @@ ftdi_sio_b38400 = 7, ftdi_sio_b57600 = 8, ftdi_sio_b115200 = 9 -} FTDI_SIO_baudrate_t ; +}; -typedef enum { +enum { ftdi_8U232AM_12MHz_b300 = 0x09c4, ftdi_8U232AM_12MHz_b600 = 0x04E2, ftdi_8U232AM_12MHz_b1200 = 0x0271, @@ -119,9 +119,9 @@ ftdi_8U232AM_12MHz_b57600 = 0x000d, ftdi_8U232AM_12MHz_b115200 = 0x4006, ftdi_8U232AM_12MHz_b230400 = 0x8003, -} FTDI_8U232AM_12MHz_baudrate_t; +}; /* Apparently all devices are 48MHz */ -typedef enum { +enum { ftdi_8U232AM_48MHz_b300 = 0x2710, ftdi_8U232AM_48MHz_b600 = 0x1388, ftdi_8U232AM_48MHz_b1200 = 0x09c4, @@ -136,7 +136,7 @@ ftdi_8U232AM_48MHz_b460800 = 0x4006, ftdi_8U232AM_48MHz_b921600 = 0x8003, -} FTDI_8U232AM_48MHz_baudrate_t; +}; #define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA #define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 diff -Nru a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c --- a/drivers/usb/serial/io_edgeport.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/serial/io_edgeport.c Tue Feb 19 18:08:57 2002 @@ -369,8 +369,8 @@ struct edgeport_serial { char name[MAX_NAME_LEN+1]; /* string name of this device */ - EDGE_MANUF_DESCRIPTOR manuf_descriptor; /* the manufacturer descriptor */ - EDGE_BOOT_DESCRIPTOR boot_descriptor; /* the boot firmware descriptor */ + struct edge_manuf_descriptor manuf_descriptor; /* the manufacturer descriptor */ + struct edge_boot_descriptor boot_descriptor; /* the boot firmware descriptor */ struct edgeport_product_info product_info; /* Product Info */ __u8 interrupt_in_endpoint; /* the interrupt endpoint handle */ @@ -397,17 +397,17 @@ }; /* baud rate information */ -typedef struct _DIVISOR_TABLE_ENTRY { +struct divisor_table_entry { __u32 BaudRate; __u16 Divisor; -} DIVISOR_TABLE_ENTRY, *PDIVISOR_TABLE_ENTRY; +}; // // Define table of divisors for Rev A EdgePort/4 hardware // These assume a 3.6864MHz crystal, the standard /16, and // MCR.7 = 0. // -static DIVISOR_TABLE_ENTRY DivisorTable[] = { +static struct divisor_table_entry divisor_table[] = { { 75, 3072}, { 110, 2095}, /* 2094.545455 => 230450 => .0217 % over */ { 134, 1713}, /* 1713.011152 => 230398.5 => .00065% under */ @@ -507,7 +507,7 @@ __u16 BootBuildNumber; __u8 *BootImage; __u32 BootSize; - PEDGE_FIRMWARE_IMAGE_RECORD record; + struct edge_firmware_image_record *record; unsigned char *firmware; int response; @@ -563,13 +563,13 @@ firmware = BootImage; for (;;) { - record = (PEDGE_FIRMWARE_IMAGE_RECORD)firmware; + record = (struct edge_firmware_image_record *)firmware; response = rom_write (edge_serial->serial, record->ExtAddr, record->Addr, record->Len, &record->Data[0]); if (response < 0) { err("sram_write failed (%x, %x, %d)", record->ExtAddr, record->Addr, record->Len); break; } - firmware += sizeof (EDGE_FIRMWARE_IMAGE_RECORD) + record->Len; + firmware += sizeof (struct edge_firmware_image_record) + record->Len; if (firmware >= &BootImage[BootSize]) { break; } @@ -1091,7 +1091,7 @@ } /* Allocate a URB for the write */ - edge_port->write_urb = usb_alloc_urb (0); + edge_port->write_urb = usb_alloc_urb (0, GFP_KERNEL); if (!edge_port->write_urb) { dbg(__FUNCTION__" - no memory"); @@ -2462,7 +2462,7 @@ usb_serial_debug_data (__FILE__, __FUNCTION__, length, buffer); /* Allocate our next urb */ - urb = usb_alloc_urb (0); + urb = usb_alloc_urb (0, GFP_KERNEL); if (!urb) return -ENOMEM; @@ -2569,9 +2569,9 @@ dbg(__FUNCTION__" - %d", baudrate); - for (i = 0; i < NUM_ENTRIES(DivisorTable); i++) { - if ( DivisorTable[i].BaudRate == baudrate ) { - *divisor = DivisorTable[i].Divisor; + for (i = 0; i < NUM_ENTRIES(divisor_table); i++) { + if ( divisor_table[i].BaudRate == baudrate ) { + *divisor = divisor_table[i].Divisor; return 0; } } @@ -2863,7 +2863,7 @@ ****************************************************************************/ static void load_application_firmware (struct edgeport_serial *edge_serial) { - PEDGE_FIRMWARE_IMAGE_RECORD record; + struct edge_firmware_image_record *record; unsigned char *firmware; unsigned char *FirmwareImage; int ImageSize; @@ -2901,13 +2901,13 @@ for (;;) { - record = (PEDGE_FIRMWARE_IMAGE_RECORD)firmware; + record = (struct edge_firmware_image_record *)firmware; response = sram_write (edge_serial->serial, record->ExtAddr, record->Addr, record->Len, &record->Data[0]); if (response < 0) { err("sram_write failed (%x, %x, %d)", record->ExtAddr, record->Addr, record->Len); break; } - firmware += sizeof (EDGE_FIRMWARE_IMAGE_RECORD) + record->Len; + firmware += sizeof (struct edge_firmware_image_record) + record->Len; if (firmware >= &FirmwareImage[ImageSize]) { break; } diff -Nru a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h --- a/drivers/usb/serial/io_edgeport.h Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/serial/io_edgeport.h Tue Feb 19 18:08:57 2002 @@ -136,7 +136,6 @@ __u16 Strings[1]; /* Start of string block */ }; -typedef struct string_block STRING_BLOCK, *PSTRING_BLOCK; #endif diff -Nru a/drivers/usb/serial/io_fw_boot.h b/drivers/usb/serial/io_fw_boot.h --- a/drivers/usb/serial/io_fw_boot.h Tue Feb 19 18:09:00 2002 +++ b/drivers/usb/serial/io_fw_boot.h Tue Feb 19 18:09:00 2002 @@ -12,20 +12,18 @@ //Image structure definition #if !defined(DEFINED_EDGE_FIRMWARE_IMAGE_RECORD) #define DEFINED_EDGE_FIRMWARE_IMAGE_RECORD - typedef struct _EDGE_FIRMWARE_IMAGE_RECORD - { + struct edge_firmware_image_record { unsigned short ExtAddr; unsigned short Addr; unsigned short Len; unsigned char Data[0]; - } EDGE_FIRMWARE_IMAGE_RECORD, *PEDGE_FIRMWARE_IMAGE_RECORD; + }; - typedef struct _EDGE_FIRMWARE_VERSION_INFO - { + struct edge_firmware_version_info { unsigned char MajorVersion; unsigned char MinorVersion; unsigned short BuildNumber; - } EDGE_FIRMWARE_VERSION_INFO, *PEDGE_FIRMWARE_VERSION_INFO; + }; #endif @@ -549,7 +547,7 @@ 0x7e, 0x74, 0x00, 0x01, 0x02, 0x08, 0xd6, }; -static EDGE_FIRMWARE_VERSION_INFO IMAGE_VERSION_NAME = { +static struct edge_firmware_version_info IMAGE_VERSION_NAME = { 1, 12, 2 }; // Major, Minor, Build #undef IMAGE_VERSION_NAME diff -Nru a/drivers/usb/serial/io_fw_boot2.h b/drivers/usb/serial/io_fw_boot2.h --- a/drivers/usb/serial/io_fw_boot2.h Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/serial/io_fw_boot2.h Tue Feb 19 18:08:58 2002 @@ -12,20 +12,18 @@ //Image structure definition #if !defined(DEFINED_EDGE_FIRMWARE_IMAGE_RECORD) #define DEFINED_EDGE_FIRMWARE_IMAGE_RECORD - typedef struct _EDGE_FIRMWARE_IMAGE_RECORD - { + struct edge_firmware_image_record { unsigned short ExtAddr; unsigned short Addr; unsigned short Len; unsigned char Data[0]; - } EDGE_FIRMWARE_IMAGE_RECORD, *PEDGE_FIRMWARE_IMAGE_RECORD; + }; - typedef struct _EDGE_FIRMWARE_VERSION_INFO - { + struct edge_firmware_version_info { unsigned char MajorVersion; unsigned char MinorVersion; unsigned short BuildNumber; - } EDGE_FIRMWARE_VERSION_INFO, *PEDGE_FIRMWARE_VERSION_INFO; + }; #endif @@ -539,7 +537,7 @@ }; -static EDGE_FIRMWARE_VERSION_INFO IMAGE_VERSION_NAME = { +static struct edge_firmware_version_info IMAGE_VERSION_NAME = { 2, 0, 3 }; // Major, Minor, Build #undef IMAGE_VERSION_NAME diff -Nru a/drivers/usb/serial/io_fw_down.h b/drivers/usb/serial/io_fw_down.h --- a/drivers/usb/serial/io_fw_down.h Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/serial/io_fw_down.h Tue Feb 19 18:08:59 2002 @@ -12,20 +12,18 @@ //Image structure definition #if !defined(DEFINED_EDGE_FIRMWARE_IMAGE_RECORD) #define DEFINED_EDGE_FIRMWARE_IMAGE_RECORD - typedef struct _EDGE_FIRMWARE_IMAGE_RECORD - { + struct edge_firmware_image_record { unsigned short ExtAddr; unsigned short Addr; unsigned short Len; unsigned char Data[0]; - } EDGE_FIRMWARE_IMAGE_RECORD, *PEDGE_FIRMWARE_IMAGE_RECORD; + }; - typedef struct _EDGE_FIRMWARE_VERSION_INFO - { + struct edge_firmware_version_info { unsigned char MajorVersion; unsigned char MinorVersion; unsigned short BuildNumber; - } EDGE_FIRMWARE_VERSION_INFO, *PEDGE_FIRMWARE_VERSION_INFO; + }; #endif @@ -1114,7 +1112,7 @@ 0x08, 0xa5, 0xb8, 0x02, 0x03, 0x4e, 0xa0, 0x08, 0x22, 0x80, 0xfe, }; -static EDGE_FIRMWARE_VERSION_INFO IMAGE_VERSION_NAME = { +static struct edge_firmware_version_info IMAGE_VERSION_NAME = { 1, 12, 3 }; // Major, Minor, Build #undef IMAGE_VERSION_NAME diff -Nru a/drivers/usb/serial/io_fw_down2.h b/drivers/usb/serial/io_fw_down2.h --- a/drivers/usb/serial/io_fw_down2.h Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/serial/io_fw_down2.h Tue Feb 19 18:08:58 2002 @@ -12,20 +12,18 @@ //Image structure definition #if !defined(DEFINED_EDGE_FIRMWARE_IMAGE_RECORD) #define DEFINED_EDGE_FIRMWARE_IMAGE_RECORD - typedef struct _EDGE_FIRMWARE_IMAGE_RECORD - { + struct edge_firmware_image_record { unsigned short ExtAddr; unsigned short Addr; unsigned short Len; unsigned char Data[0]; - } EDGE_FIRMWARE_IMAGE_RECORD, *PEDGE_FIRMWARE_IMAGE_RECORD; + }; - typedef struct _EDGE_FIRMWARE_VERSION_INFO - { - unsigned char MajorVersion; + struct edge_firmware_version_info { + unsigned char MajorVersion; unsigned char MinorVersion; unsigned short BuildNumber; - } EDGE_FIRMWARE_VERSION_INFO, *PEDGE_FIRMWARE_VERSION_INFO; + }; #endif @@ -1126,7 +1124,7 @@ 0x02, 0x03, 0x4e, 0xa0, 0x08, 0x22, 0x80, 0xfe, }; -static EDGE_FIRMWARE_VERSION_INFO IMAGE_VERSION_NAME = { +static struct edge_firmware_version_info IMAGE_VERSION_NAME = { 2, 0, 3 }; // Major, Minor, Build #undef IMAGE_VERSION_NAME diff -Nru a/drivers/usb/serial/io_ionsp.h b/drivers/usb/serial/io_ionsp.h --- a/drivers/usb/serial/io_ionsp.h Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/serial/io_ionsp.h Tue Feb 19 18:08:58 2002 @@ -88,12 +88,12 @@ // Interrupt pipe // -typedef struct _INT_STATUS_PKT { +struct int_status_pkt { __u16 RxBytesAvail; // Additional bytes available to // be read from Bulk IN pipe __u16 TxCredits[ MAX_RS232_PORTS ]; // Additional space available in // given port's TxBuffer -} INT_STATUS_PKT, *PINT_STATUS_PKT; +}; #define GET_INT_STATUS_SIZE(NumPorts) (sizeof(__u16) + (sizeof(__u16) * (NumPorts))) diff -Nru a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h --- a/drivers/usb/serial/io_usbvend.h Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/serial/io_usbvend.h Tue Feb 19 18:08:58 2002 @@ -182,11 +182,11 @@ // Version 2 format of DeviceParams. This format is longer (3C0h) // and starts lower in memory, at the uppermost 1K in ROM. #define EDGE_MANUF_DESC_ADDR 0x00FF7C00 -#define EDGE_MANUF_DESC_LEN sizeof(EDGE_MANUF_DESCRIPTOR) +#define EDGE_MANUF_DESC_LEN sizeof(struct edge_manuf_descriptor) // Boot params descriptor #define EDGE_BOOT_DESC_ADDR 0x00FF7FC0 -#define EDGE_BOOT_DESC_LEN sizeof(EDGE_BOOT_DESCRIPTOR) +#define EDGE_BOOT_DESC_LEN sizeof(struct edge_boot_descriptor) // Define the max block size that may be read or written // in a read/write RAM/ROM command. @@ -227,7 +227,7 @@ #define MAX_SERIALNUMBER_LEN 12 #define MAX_ASSEMBLYNUMBER_LEN 14 -typedef struct _EDGE_MANUF_DESCRIPTOR { +struct edge_manuf_descriptor { __u16 RootDescTable[0x10]; // C00 Root of descriptor tables (just a placeholder) __u8 DescriptorArea[0x2E0]; // C20 Descriptors go here, up to 2E0h (just a placeholder) @@ -272,7 +272,7 @@ __u8 IonConfig; // FBF Config byte for ION manufacturing use // FBF end of structure, total len = 3C0h -} EDGE_MANUF_DESCRIPTOR, *PEDGE_MANUF_DESCRIPTOR; +}; #define MANUF_DESC_VER_1 1 // Original definition of MANUF_DESC @@ -317,10 +317,10 @@ -#define MANUF_SERNUM_LENGTH sizeof(((PEDGE_MANUF_DESCRIPTOR)0)->SerialNumber) -#define MANUF_ASSYNUM_LENGTH sizeof(((PEDGE_MANUF_DESCRIPTOR)0)->AssemblyNumber) -#define MANUF_OEMASSYNUM_LENGTH sizeof(((PEDGE_MANUF_DESCRIPTOR)0)->OemAssyNumber) -#define MANUF_MANUFDATE_LENGTH sizeof(((PEDGE_MANUF_DESCRIPTOR)0)->ManufDate) +#define MANUF_SERNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->SerialNumber) +#define MANUF_ASSYNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->AssemblyNumber) +#define MANUF_OEMASSYNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->OemAssyNumber) +#define MANUF_MANUFDATE_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->ManufDate) #define MANUF_ION_CONFIG_MASTER 0x80 // 1=Master mode, 0=Normal #define MANUF_ION_CONFIG_DIAG 0x40 // 1=Run h/w diags, 0=norm @@ -335,7 +335,7 @@ // - FF:xFFF. Note that the 930-mandated UCONFIG bytes are // included in this structure. // -typedef struct _EDGE_BOOT_DESCRIPTOR { +struct edge_boot_descriptor { __u8 Length; // C0 Desc length, per USB (= 40h) __u8 DescType; // C1 Desc type, per USB (= DEVICE type) __u8 DescVer; // C2 Desc version/format @@ -359,8 +359,7 @@ __u8 UConfig1; // F9 930-defined CPU configuration byte 1 __u8 Reserved3[6]; // FA -- unused, set to 0 -- // FF end of structure, total len = 80 - -} EDGE_BOOT_DESCRIPTOR, *PEDGE_BOOT_DESCRIPTOR; +}; #define BOOT_DESC_VER_1 1 // Original definition of BOOT_PARAMS diff -Nru a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c --- a/drivers/usb/serial/keyspan.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/serial/keyspan.c Tue Feb 19 18:08:57 2002 @@ -107,7 +107,7 @@ /* number of active ports */ atomic_t active_count; - const keyspan_device_details *device_details; + const struct keyspan_device_details *device_details; struct urb *instat_urb; char instat_buf[INSTAT_BUFLEN]; @@ -125,7 +125,7 @@ /* Keep duplicate of device details in each port structure as well - simplifies some of the callback functions etc. */ - const keyspan_device_details *device_details; + const struct keyspan_device_details *device_details; /* Input endpoints and buffer for this port */ struct urb *in_urbs[2]; @@ -243,7 +243,7 @@ { int baud_rate; struct keyspan_port_private *p_priv; - const keyspan_device_details *d_details; + const struct keyspan_device_details *d_details; unsigned int cflag; dbg(__FUNCTION__ ".\n"); @@ -321,7 +321,7 @@ const unsigned char *buf, int count) { struct keyspan_port_private *p_priv; - const keyspan_device_details *d_details; + const struct keyspan_device_details *d_details; int flip; int left, todo; struct urb *this_urb; @@ -481,7 +481,7 @@ static void usa26_instat_callback(struct urb *urb) { unsigned char *data = urb->transfer_buffer; - keyspan_usa26_portStatusMessage *msg; + struct keyspan_usa26_portStatusMessage *msg; struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; @@ -498,7 +498,7 @@ goto exit; } - msg = (keyspan_usa26_portStatusMessage *)data; + msg = (struct keyspan_usa26_portStatusMessage *)data; #if 0 dbg(__FUNCTION__ " port status: port %d cts %d dcd %d dsr %d ri %d toff %d txoff %d rxen %d cr %d\n", @@ -619,7 +619,7 @@ { int err; unsigned char *data = urb->transfer_buffer; - keyspan_usa28_portStatusMessage *msg; + struct keyspan_usa28_portStatusMessage *msg; struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; @@ -642,7 +642,7 @@ data[6], data[7], data[8], data[9], data[10], data[11]);*/ /* Now do something useful with the data */ - msg = (keyspan_usa28_portStatusMessage *)data; + msg = (struct keyspan_usa28_portStatusMessage *)data; /* Check port number from message and retrieve private data */ @@ -710,7 +710,7 @@ { int err; unsigned char *data = urb->transfer_buffer; - keyspan_usa49_portStatusMessage *msg; + struct keyspan_usa49_portStatusMessage *msg; struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; @@ -735,7 +735,7 @@ data[6], data[7], data[8], data[9], data[10]);*/ /* Now do something useful with the data */ - msg = (keyspan_usa49_portStatusMessage *)data; + msg = (struct keyspan_usa49_portStatusMessage *)data; /* Check port number from message and retrieve private data */ if (msg->portNumber >= serial->num_ports) { @@ -851,7 +851,7 @@ struct keyspan_port_private *p_priv; struct keyspan_serial_private *s_priv; struct usb_serial *serial = port->serial; - const keyspan_device_details *d_details; + const struct keyspan_device_details *d_details; int i, already_active, err; struct urb *urb; @@ -1051,7 +1051,7 @@ return NULL; /* endpoint not needed */ dbg (__FUNCTION__ " alloc for endpoint %d.\n", endpoint); - urb = usb_alloc_urb(0); /* No ISO */ + urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ if (urb == NULL) { dbg (__FUNCTION__ " alloc for endpoint %d failed.\n", endpoint); return NULL; @@ -1106,7 +1106,7 @@ { int i, j; struct keyspan_serial_private *s_priv; - const keyspan_device_details *d_details; + const struct keyspan_device_details *d_details; struct usb_serial_port *port; struct keyspan_port_private *p_priv; struct callbacks *cback; @@ -1293,7 +1293,7 @@ struct keyspan_usa26_portControlMessage msg; struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; - const keyspan_device_details *d_details; + const struct keyspan_device_details *d_details; int outcont_urb; struct urb *this_urb; int err; @@ -1429,7 +1429,7 @@ struct keyspan_usa28_portControlMessage msg; struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; - const keyspan_device_details *d_details; + const struct keyspan_device_details *d_details; struct urb *this_urb; int err; @@ -1514,7 +1514,7 @@ struct keyspan_usa49_portControlMessage msg; struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; - const keyspan_device_details *d_details; + const struct keyspan_device_details *d_details; int glocont_urb; struct urb *this_urb; int err; @@ -1640,7 +1640,7 @@ { struct usb_serial *serial = port->serial; struct keyspan_serial_private *s_priv; - const keyspan_device_details *d_details; + const struct keyspan_device_details *d_details; s_priv = (struct keyspan_serial_private *)(serial->private); d_details = s_priv->device_details; @@ -1666,7 +1666,7 @@ struct usb_serial_port *port; struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; - const keyspan_device_details *d_details; + const struct keyspan_device_details *d_details; dbg("keyspan_startup called.\n"); diff -Nru a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h --- a/drivers/usb/serial/keyspan.h Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/serial/keyspan.h Tue Feb 19 18:08:58 2002 @@ -194,7 +194,7 @@ #define keyspan_usa49w_product_id 0x010a -typedef struct { +struct keyspan_device_details { /* product ID value */ int product_id; @@ -232,13 +232,13 @@ u8 *rate_hi, u8 *rate_low, u8 *prescaler); u32 baudclk; -} keyspan_device_details; +}; /* Now for each device type we setup the device detail structure with the appropriate information (provided in Keyspan's documentation) */ -static const keyspan_device_details usa18x_device_details = { +static const struct keyspan_device_details usa18x_device_details = { keyspan_usa18x_product_id, /* product ID */ msg_usa26, /* msg type*/ 1, /* num ports */ @@ -254,7 +254,7 @@ KEYSPAN_USA18X_BAUDCLK /* base baud clock */ }; -static const keyspan_device_details usa19_device_details = { +static const struct keyspan_device_details usa19_device_details = { keyspan_usa19_product_id, /* product ID */ msg_usa28, /* msg type*/ 1, /* num ports */ @@ -270,7 +270,7 @@ KEYSPAN_USA19_BAUDCLK /* base baud clock */ }; -static const keyspan_device_details usa19w_device_details = { +static const struct keyspan_device_details usa19w_device_details = { keyspan_usa19w_product_id, /* product ID */ msg_usa26, /* msg type*/ 1, /* num ports */ @@ -286,7 +286,7 @@ KEYSPAN_USA19W_BAUDCLK /* base baud clock */ }; -static const keyspan_device_details usa28x_device_details = { +static const struct keyspan_device_details usa28x_device_details = { keyspan_usa28x_product_id, /* product ID */ msg_usa26, /* msg type*/ 2, /* num ports */ @@ -302,7 +302,7 @@ KEYSPAN_USA28X_BAUDCLK }; -static const keyspan_device_details usa28xa_device_details = { +static const struct keyspan_device_details usa28xa_device_details = { keyspan_usa28xa_product_id, /* product ID */ msg_usa26, /* msg type*/ 2, /* num ports */ @@ -320,7 +320,7 @@ /* We don't need a separate entry for the usa28xb as it appears as a 28x anyway */ -static const keyspan_device_details usa49w_device_details = { +static const struct keyspan_device_details usa49w_device_details = { keyspan_usa49w_product_id, /* product ID */ msg_usa49, /* msg type*/ 4, /* num ports */ @@ -336,7 +336,7 @@ KEYSPAN_USA49W_BAUDCLK }; -static const keyspan_device_details *keyspan_devices[] = { +static const struct keyspan_device_details *keyspan_devices[] = { &usa18x_device_details, &usa19_device_details, &usa19w_device_details, diff -Nru a/drivers/usb/serial/keyspan_usa26msg.h b/drivers/usb/serial/keyspan_usa26msg.h --- a/drivers/usb/serial/keyspan_usa26msg.h Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/serial/keyspan_usa26msg.h Tue Feb 19 18:08:57 2002 @@ -93,7 +93,7 @@ #define __USA26MSG__ -typedef struct keyspan_usa26_portControlMessage +struct keyspan_usa26_portControlMessage { /* there are three types of "commands" sent in the control message: @@ -164,7 +164,7 @@ returnStatus, // BOTH: return current status (even if it hasn't changed) resetDataToggle;// BOTH: reset data toggle state to DATA0 -} keyspan_usa26_portControlMessage; +}; // defines for bits in lcr #define USA_DATABITS_5 0x00 @@ -182,7 +182,7 @@ // all things called "StatusMessage" are sent on the status endpoint -typedef struct keyspan_usa26_portStatusMessage // one for each port +struct keyspan_usa26_portStatusMessage // one for each port { u8 port, // BOTH: 0=first, 1=second, other=see below hskia_cts, // USA26: reports HSKIA pin @@ -195,7 +195,7 @@ _txXoff, // port is in XOFF state (either host or RX XOFF) rxEnabled, // as configured by rxOn/rxOff 1=on, 0=off controlResponse;// 1=a control message has been processed -} keyspan_usa26_portStatusMessage; +}; // bits in RX data message when STAT byte is included #define RXERROR_OVERRUN 0x02 @@ -203,28 +203,28 @@ #define RXERROR_FRAMING 0x08 #define RXERROR_BREAK 0x10 -typedef struct keyspan_usa26_globalControlMessage +struct keyspan_usa26_globalControlMessage { u8 sendGlobalStatus, // 2=request for two status responses resetStatusToggle, // 1=reset global status toggle resetStatusCount; // a cycling value -} keyspan_usa26_globalControlMessage; +}; -typedef struct keyspan_usa26_globalStatusMessage +struct keyspan_usa26_globalStatusMessage { u8 port, // 3 sendGlobalStatus, // from request, decremented resetStatusCount; // as in request -} keyspan_usa26_globalStatusMessage; +}; -typedef struct keyspan_usa26_globalDebugMessage +struct keyspan_usa26_globalDebugMessage { u8 port, // 2 a, b, c, d; -} keyspan_usa26_globalDebugMessage; +}; // ie: the maximum length of an EZUSB endpoint buffer #define MAX_DATA_LEN 64 diff -Nru a/drivers/usb/serial/keyspan_usa28msg.h b/drivers/usb/serial/keyspan_usa28msg.h --- a/drivers/usb/serial/keyspan_usa28msg.h Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/serial/keyspan_usa28msg.h Tue Feb 19 18:08:57 2002 @@ -95,7 +95,7 @@ #define __USA28MSG__ -typedef struct keyspan_usa28_portControlMessage +struct keyspan_usa28_portControlMessage { /* there are four types of "commands" sent in the control message: @@ -146,9 +146,9 @@ returnStatus, // return current status n times (1 or 2) resetDataToggle;// reset data toggle state to DATA0 -} keyspan_usa28_portControlMessage; +}; -typedef struct keyspan_usa28_portStatusMessage +struct keyspan_usa28_portStatusMessage { u8 port, // 0=first, 1=second, 2=global (see below) cts, @@ -164,32 +164,32 @@ rxBreak, // 1=we're in break state rs232invalid, // 1=no valid signals on rs-232 inputs controlResponse;// 1=a control messages has been processed -} keyspan_usa28_portStatusMessage; +}; // bit defines in txState #define TX_OFF 0x01 // requested by host txOff command #define TX_XOFF 0x02 // either real, or simulated by host -typedef struct keyspan_usa28_globalControlMessage +struct keyspan_usa28_globalControlMessage { u8 sendGlobalStatus, // 2=request for two status responses resetStatusToggle, // 1=reset global status toggle resetStatusCount; // a cycling value -} keyspan_usa28_globalControlMessage; +}; -typedef struct keyspan_usa28_globalStatusMessage +struct keyspan_usa28_globalStatusMessage { u8 port, // 3 sendGlobalStatus, // from request, decremented resetStatusCount; // as in request -} keyspan_usa28_globalStatusMessage; +}; -typedef struct keyspan_usa28_globalDebugMessage +struct keyspan_usa28_globalDebugMessage { u8 port, // 2 n, // typically a count/status byte b; // typically a data byte -} keyspan_usa28_globalDebugMessage; +}; // ie: the maximum length of an EZUSB endpoint buffer #define MAX_DATA_LEN 64 diff -Nru a/drivers/usb/serial/keyspan_usa49msg.h b/drivers/usb/serial/keyspan_usa49msg.h --- a/drivers/usb/serial/keyspan_usa49msg.h Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/serial/keyspan_usa49msg.h Tue Feb 19 18:08:58 2002 @@ -107,7 +107,7 @@ 0x80 globalControlMessage */ -typedef struct keyspan_usa49_portControlMessage +struct keyspan_usa49_portControlMessage { /* 0. 0/1/2/3 port control message follows @@ -173,7 +173,7 @@ enablePort, // start servicing port (move data, check status) disablePort; // stop servicing port (does implicit tx/rx flush/off) -} keyspan_usa49_portControlMessage; +}; // defines for bits in lcr #define USA_DATABITS_5 0x00 @@ -201,7 +201,7 @@ sends any control message (either global or port-specific). */ -typedef struct keyspan_usa49_globalControlMessage +struct keyspan_usa49_globalControlMessage { u8 portNumber, // 0x80 sendGlobalStatus, // 1/2=number of status responses requested @@ -209,7 +209,7 @@ resetStatusCount, // a cycling value remoteWakeupEnable, // 0x10=P1, 0x20=P2, 0x40=P3, 0x80=P4 disableStatusMessages; // 1=send no status until host talks -} keyspan_usa49_globalControlMessage; +}; /* Device->host messages send on the global status endpoint @@ -221,7 +221,7 @@ 0x81 globalDebugMessage */ -typedef struct keyspan_usa49_portStatusMessage // one for each port +struct keyspan_usa49_portStatusMessage // one for each port { u8 portNumber, // 0,1,2,3 cts, // reports CTS pin @@ -234,7 +234,7 @@ controlResponse,// 1=a control message has been processed txAck, // ACK (data TX complete) rs232valid; // RS-232 signal valid -} keyspan_usa49_portStatusMessage; +}; // bits in RX data message when STAT byte is included #define RXERROR_OVERRUN 0x02 @@ -242,19 +242,19 @@ #define RXERROR_FRAMING 0x08 #define RXERROR_BREAK 0x10 -typedef struct keyspan_usa49_globalStatusMessage +struct keyspan_usa49_globalStatusMessage { u8 portNumber, // 0x80=globalStatusMessage sendGlobalStatus, // from request, decremented resetStatusCount; // as in request -} keyspan_usa49_globalStatusMessage; +}; -typedef struct keyspan_usa49_globalDebugMessage +struct keyspan_usa49_globalDebugMessage { u8 portNumber, // 0x81=globalDebugMessage n, // typically a count/status byte b; // typically a data byte -} keyspan_usa49_globalDebugMessage; +}; // ie: the maximum length of an EZUSB endpoint buffer #define MAX_DATA_LEN 64 diff -Nru a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c --- a/drivers/usb/serial/kl5kusb105.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/serial/kl5kusb105.c Tue Feb 19 18:08:57 2002 @@ -280,7 +280,7 @@ spin_lock_init (&priv->write_urb_pool_lock); for (i=0; iwrite_urb_pool[i] = urb; if (urb == NULL) { diff -Nru a/drivers/usb/serial/kl5kusb105.h b/drivers/usb/serial/kl5kusb105.h --- a/drivers/usb/serial/kl5kusb105.h Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/serial/kl5kusb105.h Tue Feb 19 18:08:58 2002 @@ -16,7 +16,7 @@ /* baud rates */ -typedef enum { +enum { kl5kusb105a_sio_b115200 = 0, kl5kusb105a_sio_b57600 = 1, kl5kusb105a_sio_b38400 = 2, @@ -27,7 +27,7 @@ kl5kusb105a_sio_b2400 = 9, /* unchecked */ kl5kusb105a_sio_b1200 = 0xa, /* unchecked */ kl5kusb105a_sio_b600 = 0xb /* unchecked */ -} KL5KUSB105A_SIO_baudrate_t; +}; /* data bits */ #define kl5kusb105a_dtb_7 7 diff -Nru a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c --- a/drivers/usb/serial/usbserial.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/serial/usbserial.c Tue Feb 19 18:08:59 2002 @@ -1203,7 +1203,7 @@ for (i = 0; i < num_bulk_in; ++i) { endpoint = bulk_in_endpoint[i]; port = &serial->port[i]; - port->read_urb = usb_alloc_urb (0); + port->read_urb = usb_alloc_urb (0, GFP_KERNEL); if (!port->read_urb) { err("No free urbs available"); goto probe_error; @@ -1228,7 +1228,7 @@ for (i = 0; i < num_bulk_out; ++i) { endpoint = bulk_out_endpoint[i]; port = &serial->port[i]; - port->write_urb = usb_alloc_urb(0); + port->write_urb = usb_alloc_urb(0, GFP_KERNEL); if (!port->write_urb) { err("No free urbs available"); goto probe_error; @@ -1254,7 +1254,7 @@ for (i = 0; i < num_interrupt_in; ++i) { endpoint = interrupt_in_endpoint[i]; port = &serial->port[i]; - port->interrupt_in_urb = usb_alloc_urb(0); + port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!port->interrupt_in_urb) { err("No free urbs available"); goto probe_error; diff -Nru a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c --- a/drivers/usb/serial/visor.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/serial/visor.c Tue Feb 19 18:08:59 2002 @@ -2,7 +2,7 @@ * USB HandSpring Visor, Palm m50x, and Sony Clie driver * (supports all of the Palm OS USB devices) * - * Copyright (C) 1999 - 2001 + * Copyright (C) 1999 - 2002 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify @@ -12,6 +12,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (02/14/2002) gkh + * Added support for the Clie S-360 device. + * * (12/18/2001) gkh * Added better Clie support for 3.5 devices. Thanks to Geoffrey Levand * for the patch. @@ -131,7 +134,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.8" +#define DRIVER_VERSION "v1.9" #define DRIVER_AUTHOR "Greg Kroah-Hartman " #define DRIVER_DESC "USB HandSpring Visor, Palm m50x, Sony Clié driver" @@ -158,6 +161,7 @@ { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) }, { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) }, + { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) }, { } /* Terminating entry */ }; @@ -173,6 +177,7 @@ { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) }, + { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) }, { } /* Terminating entry */ }; @@ -787,7 +792,7 @@ /* create our write urb pool and transfer buffers */ spin_lock_init (&write_urb_pool_lock); for (i = 0; i < NUM_URBS; ++i) { - urb = usb_alloc_urb(0); + urb = usb_alloc_urb(0, GFP_KERNEL); write_urb_pool[i] = urb; if (urb == NULL) { err("No more urbs???"); diff -Nru a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h --- a/drivers/usb/serial/visor.h Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/serial/visor.h Tue Feb 19 18:08:58 2002 @@ -1,7 +1,7 @@ /* * USB HandSpring Visor driver * - * Copyright (C) 1999 - 2001 + * Copyright (C) 1999 - 2002 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify @@ -28,6 +28,7 @@ #define SONY_VENDOR_ID 0x054C #define SONY_CLIE_3_5_ID 0x0038 #define SONY_CLIE_4_0_ID 0x0066 +#define SONY_CLIE_S360_ID 0x0095 /**************************************************************************** * Handspring Visor Vendor specific request codes (bRequest values) diff -Nru a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c --- a/drivers/usb/storage/debug.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/storage/debug.c Tue Feb 19 18:08:57 2002 @@ -186,25 +186,27 @@ US_DEBUGP("Buffer has %d scatterlists.\n", cmd->use_sg ); for ( i=0; iuse_sg; i++ ) { + char *adr = page_address(sg[i].page) + sg[i].offset; + US_DEBUGP("Length of scatterlist %d is %d.\n",i,sg[i].length); US_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x\n" "%02x %02x %02x %02x %02x %02x %02x %02x\n", - sg[i].address[0], - sg[i].address[1], - sg[i].address[2], - sg[i].address[3], - sg[i].address[4], - sg[i].address[5], - sg[i].address[6], - sg[i].address[7], - sg[i].address[8], - sg[i].address[9], - sg[i].address[10], - sg[i].address[11], - sg[i].address[12], - sg[i].address[13], - sg[i].address[14], - sg[i].address[15]); + adr[0], + adr[1], + adr[2], + adr[3], + adr[4], + adr[5], + adr[6], + adr[7], + adr[8], + adr[9], + adr[10], + adr[11], + adr[12], + adr[13], + adr[14], + adr[15]); } } diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c --- a/drivers/usb/storage/usb.c Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/storage/usb.c Tue Feb 19 18:08:58 2002 @@ -320,7 +320,7 @@ spin_lock_irq(¤t->sigmask_lock); flush_signals(current); sigfillset(¤t->blocked); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); /* set our name for identification purposes */ @@ -515,7 +515,7 @@ down(&(ss->irq_urb_sem)); /* allocate the URB */ - ss->irq_urb = usb_alloc_urb(0); + ss->irq_urb = usb_alloc_urb(0, GFP_KERNEL); if (!ss->irq_urb) { up(&(ss->irq_urb_sem)); US_DEBUGP("couldn't allocate interrupt URB"); @@ -730,7 +730,7 @@ } /* allocate the URB we're going to use */ - ss->current_urb = usb_alloc_urb(0); + ss->current_urb = usb_alloc_urb(0, GFP_KERNEL); if (!ss->current_urb) { usb_dec_dev_use(dev); return NULL; @@ -756,7 +756,7 @@ memset(ss, 0, sizeof(struct us_data)); /* allocate the URB we're going to use */ - ss->current_urb = usb_alloc_urb(0); + ss->current_urb = usb_alloc_urb(0, GFP_KERNEL); if (!ss->current_urb) { kfree(ss); usb_dec_dev_use(dev); diff -Nru a/drivers/usb/stv680.c b/drivers/usb/stv680.c --- a/drivers/usb/stv680.c Tue Feb 19 18:08:58 2002 +++ b/drivers/usb/stv680.c Tue Feb 19 18:08:58 2002 @@ -139,8 +139,11 @@ if (!pgd_none (*pgd)) { pmd = pmd_offset (pgd, adr); if (!pmd_none (*pmd)) { - ptep = pte_offset (pmd, adr); + preempt_disable(); + ptep = pte_offset_map (pmd, adr); pte = *ptep; + pte_unmap(pte); + preempt_enable(); if (pte_present (pte)) { ret = (unsigned long) page_address (pte_page (pte)); ret |= (adr & (PAGE_SIZE - 1)); @@ -804,7 +807,7 @@ } for (i = 0; i < STV680_NUMSBUF; i++) { - urb = usb_alloc_urb (0); + urb = usb_alloc_urb (0, GFP_KERNEL); if (!urb) return ENOMEM; diff -Nru a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c --- a/drivers/usb/usb-skeleton.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/usb-skeleton.c Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * USB Skeleton driver - 0.6 + * USB Skeleton driver - 0.7 * * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) * @@ -22,6 +22,9 @@ * * History: * + * 2002_02_12 - 0.7 - zero out dev in probe function for devices that do + * not have both a bulk in and bulk out endpoint. + * Thanks to Holger Waechtler for the fix. * 2001_11_05 - 0.6 - fix minor locking problem in skel_disconnect. * Thanks to Pete Zaitcev for the fix. * 2001_09_04 - 0.5 - fix devfs bug in skel_disconnect. Thanks to wim delvaux @@ -542,6 +545,7 @@ err ("Out of memory"); goto exit; } + memset (dev, 0x00, sizeof (*dev)); minor_table[minor] = dev; interface = &udev->actconfig->interface[ifnum]; @@ -573,7 +577,8 @@ if (((endpoint->bEndpointAddress & 0x80) == 0x00) && ((endpoint->bmAttributes & 3) == 0x02)) { /* we found a bulk out endpoint */ - dev->write_urb = usb_alloc_urb(0); + /* a probe() may sleep and has no restrictions on memory allocations */ + dev->write_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->write_urb) { err("No free urbs available"); goto error; diff -Nru a/drivers/usb/usb.c b/drivers/usb/usb.c --- a/drivers/usb/usb.c Tue Feb 19 18:09:00 2002 +++ b/drivers/usb/usb.c Tue Feb 19 18:09:00 2002 @@ -1085,6 +1085,7 @@ /** * usb_alloc_urb - creates a new urb for a USB driver to use * @iso_packets: number of iso packets for this urb + * @mem_flags: the type of memory to allocate, see kmalloc() for a list of valid options for this. * * Creates an urb for the USB driver to use, initializes a few internal * structures, incrementes the usage counter, and returns a pointer to it. @@ -1096,13 +1097,13 @@ * * The driver must call usb_free_urb() when it is finished with the urb. */ -struct urb *usb_alloc_urb(int iso_packets) +struct urb *usb_alloc_urb(int iso_packets, int mem_flags) { struct urb *urb; urb = (struct urb *)kmalloc(sizeof(struct urb) + iso_packets * sizeof(struct usb_iso_packet_descriptor), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + mem_flags); if (!urb) { err("alloc_urb: kmalloc failed"); return NULL; @@ -1368,7 +1369,7 @@ int retv; int length; - urb = usb_alloc_urb(0); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; @@ -1456,7 +1457,7 @@ if (len < 0) return -EINVAL; - urb=usb_alloc_urb(0); + urb=usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; @@ -1978,11 +1979,11 @@ if (driver->owner) __MOD_DEC_USE_COUNT(driver->owner); /* if driver->disconnect didn't release the interface */ - if (interface->driver) { - put_device (&interface->dev); + if (interface->driver) usb_driver_release_interface(driver, interface); - } } + /* remove our device node for this interface */ + put_device(&interface->dev); } } @@ -2322,7 +2323,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) { struct usb_interface *iface; - int ret; + struct usb_interface_descriptor *iface_as; + int i, ret; iface = usb_ifnum_to_if(dev, interface); if (!iface) { @@ -2344,8 +2346,30 @@ return ret; iface->act_altsetting = alternate; - dev->toggle[0] = 0; /* 9.1.1.5 says to do this */ - dev->toggle[1] = 0; + + /* 9.1.1.5: reset toggles for all endpoints affected by this iface-as + * + * Note: + * Despite EP0 is always present in all interfaces/AS, the list of + * endpoints from the descriptor does not contain EP0. Due to its + * omnipresence one might expect EP0 being considered "affected" by + * any SetInterface request and hence assume toggles need to be reset. + * However, EP0 toggles are re-synced for every individual transfer + * during the SETUP stage - hence EP0 toggles are "don't care" here. + */ + + iface_as = &iface->altsetting[alternate]; + for (i = 0; i < iface_as->bNumEndpoints; i++) { + u8 ep = iface_as->endpoint[i].bEndpointAddress; + + usb_settoggle(dev, ep&USB_ENDPOINT_NUMBER_MASK, usb_endpoint_out(ep), 0); + } + + /* usb_set_maxpacket() sets the maxpacket size for all EP in all + * interfaces but it shouldn't do any harm here: we have changed + * the AS for the requested interface only, hence for unaffected + * interfaces it's just re-application of still-valid values. + */ usb_set_maxpacket(dev); return 0; } diff -Nru a/drivers/usb/usbkbd.c b/drivers/usb/usbkbd.c --- a/drivers/usb/usbkbd.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/usbkbd.c Tue Feb 19 18:08:59 2002 @@ -1,11 +1,9 @@ /* - * $Id: usbkbd.c,v 1.20 2001/04/26 08:34:49 vojtech Exp $ + * $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * * USB HIDBP Keyboard support - * - * Sponsored by SuSE */ /* @@ -24,8 +22,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -35,19 +33,17 @@ #include #include -#define _HID_BOOT_PROTOCOL -#include "hid.h" - /* * Version Information */ #define DRIVER_VERSION "" -#define DRIVER_AUTHOR "Vojtech Pavlik " +#define DRIVER_AUTHOR "Vojtech Pavlik " #define DRIVER_DESC "USB HID Boot Protocol keyboard driver" +#define DRIVER_LICENSE "GPL" -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); static unsigned char usb_kbd_keycode[256] = { 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, @@ -74,9 +70,10 @@ unsigned char new[8]; unsigned char old[8]; struct urb *irq, *led; - struct usb_ctrlrequest dr; + struct usb_ctrlrequest cr; unsigned char leds, newleds; char name[128]; + char phys[64]; int open; }; @@ -129,7 +126,7 @@ kbd->leds = kbd->newleds; kbd->led->dev = kbd->usbdev; - if (usb_submit_urb(kbd->led, GFP_KERNEL)) + if (usb_submit_urb(kbd->led, GFP_ATOMIC)) err("usb_submit_urb(leds) failed"); return 0; @@ -147,7 +144,7 @@ kbd->leds = kbd->newleds; kbd->led->dev = kbd->usbdev; - if (usb_submit_urb(kbd->led, GFP_KERNEL)) + if (usb_submit_urb(kbd->led, GFP_ATOMIC)) err("usb_submit_urb(leds) failed"); } @@ -181,6 +178,7 @@ struct usb_endpoint_descriptor *endpoint; struct usb_kbd *kbd; int i, pipe, maxp; + char path[64]; char *buf; iface = &dev->actconfig->interface[ifnum]; @@ -195,18 +193,15 @@ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - hid_set_protocol(dev, interface->bInterfaceNumber, 0); - hid_set_idle(dev, interface->bInterfaceNumber, 0, 0); - if (!(kbd = kmalloc(sizeof(struct usb_kbd), GFP_KERNEL))) return NULL; memset(kbd, 0, sizeof(struct usb_kbd)); - kbd->irq = usb_alloc_urb(0); + kbd->irq = usb_alloc_urb(0, GFP_KERNEL); if (!kbd->irq) { kfree(kbd); return NULL; } - kbd->led = usb_alloc_urb(0); + kbd->led = usb_alloc_urb(0, GFP_KERNEL); if (!kbd->led) { usb_free_urb(kbd->irq); kfree(kbd); @@ -230,13 +225,17 @@ FILL_INT_URB(kbd->irq, dev, pipe, kbd->new, maxp > 8 ? 8 : maxp, usb_kbd_irq, kbd, endpoint->bInterval); - kbd->dr.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE; - kbd->dr.bRequest = HID_REQ_SET_REPORT; - kbd->dr.wValue = 0x200; - kbd->dr.wIndex = interface->bInterfaceNumber; - kbd->dr.wLength = 1; + kbd->cr.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + kbd->cr.bRequest = 0x09; + kbd->cr.wValue = 0x200; + kbd->cr.wIndex = interface->bInterfaceNumber; + kbd->cr.wLength = 1; + + usb_make_path(dev, path, 64); + sprintf(kbd->phys, "%s/input0", path); kbd->dev.name = kbd->name; + kbd->dev.phys = kbd->phys; kbd->dev.idbus = BUS_USB; kbd->dev.idvendor = dev->descriptor.idVendor; kbd->dev.idproduct = dev->descriptor.idProduct; @@ -261,12 +260,11 @@ kfree(buf); FILL_CONTROL_URB(kbd->led, dev, usb_sndctrlpipe(dev, 0), - (void*) &kbd->dr, &kbd->leds, 1, usb_kbd_led, kbd); + (void*) &kbd->cr, &kbd->leds, 1, usb_kbd_led, kbd); input_register_device(&kbd->dev); - printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", - kbd->dev.number, kbd->name, dev->bus->busnum, dev->devnum, ifnum); + printk(KERN_INFO "input: %s on %s\n", kbd->name, path); return kbd; } diff -Nru a/drivers/usb/usbmouse.c b/drivers/usb/usbmouse.c --- a/drivers/usb/usbmouse.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/usbmouse.c Tue Feb 19 18:08:59 2002 @@ -1,11 +1,9 @@ /* - * $Id: usbmouse.c,v 1.6 2000/08/14 21:05:26 vojtech Exp $ + * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $ * - * Copyright (c) 1999-2000 Vojtech Pavlik + * Copyright (c) 1999-2001 Vojtech Pavlik * * USB HIDBP Mouse support - * - * Sponsored by SuSE */ /* @@ -24,8 +22,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -35,23 +33,22 @@ #include #include -#define _HID_BOOT_PROTOCOL -#include "hid.h" - /* * Version Information */ #define DRIVER_VERSION "v1.6" -#define DRIVER_AUTHOR "Vojtech Pavlik " +#define DRIVER_AUTHOR "Vojtech Pavlik " #define DRIVER_DESC "USB HID Boot Protocol mouse driver" +#define DRIVER_LICENSE "GPL" -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); struct usb_mouse { signed char data[8]; char name[128]; + char phys[64]; struct usb_device *usbdev; struct input_dev dev; struct urb *irq; @@ -107,6 +104,7 @@ struct usb_endpoint_descriptor *endpoint; struct usb_mouse *mouse; int pipe, maxp; + char path[64]; char *buf; iface = &dev->actconfig->interface[ifnum]; @@ -121,12 +119,10 @@ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - hid_set_idle(dev, interface->bInterfaceNumber, 0, 0); - if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) return NULL; memset(mouse, 0, sizeof(struct usb_mouse)); - mouse->irq = usb_alloc_urb(0); + mouse->irq = usb_alloc_urb(0, GFP_KERNEL); if (!mouse->irq) { kfree(mouse); return NULL; @@ -144,7 +140,11 @@ mouse->dev.open = usb_mouse_open; mouse->dev.close = usb_mouse_close; + usb_make_path(dev, path, 64); + sprintf(mouse->phys, "%s/input0", path); + mouse->dev.name = mouse->name; + mouse->dev.phys = mouse->phys; mouse->dev.idbus = BUS_USB; mouse->dev.idvendor = dev->descriptor.idVendor; mouse->dev.idproduct = dev->descriptor.idProduct; @@ -173,8 +173,7 @@ input_register_device(&mouse->dev); - printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", - mouse->dev.number, mouse->name, dev->bus->busnum, dev->devnum, ifnum); + printk(KERN_INFO "input: %s on %s\n", mouse->name, path); return mouse; } diff -Nru a/drivers/usb/usbnet.c b/drivers/usb/usbnet.c --- a/drivers/usb/usbnet.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/usbnet.c Tue Feb 19 18:08:57 2002 @@ -420,7 +420,7 @@ } // allocate irq urb - if ((priv->irq_urb = usb_alloc_urb (0)) == 0) { + if ((priv->irq_urb = usb_alloc_urb (0, GFP_KERNEL)) == 0) { dbg ("%s: cannot allocate private irq urb per device", dev->net.name); kfree (priv); @@ -1589,7 +1589,7 @@ } } - if (!(urb = usb_alloc_urb (0))) { + if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) { dbg ("no urb"); goto drop; } @@ -1723,7 +1723,7 @@ struct urb *urb; int i; for (i = 0; i < 3 && dev->rxq.qlen < RX_QLEN; i++) { - if ((urb = usb_alloc_urb (0)) != 0) + if ((urb = usb_alloc_urb (0, GFP_ATOMIC)) != 0) rx_submit (dev, urb, GFP_ATOMIC); } if (temp != dev->rxq.qlen) diff -Nru a/drivers/usb/usbvideo.c b/drivers/usb/usbvideo.c --- a/drivers/usb/usbvideo.c Tue Feb 19 18:09:00 2002 +++ b/drivers/usb/usbvideo.c Tue Feb 19 18:09:00 2002 @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -60,30 +61,6 @@ #define MDEBUG(x) do { } while(0) /* Debug memory management */ -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -unsigned long usbvideo_uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if (pte_present(pte)) { - ret = (unsigned long) page_address(pte_page(pte)); - ret |= (adr & (PAGE_SIZE-1)); - } - } - } - MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); - return ret; -} - /* * Here we want the physical address of the memory. * This is used when initializing the contents of the @@ -94,7 +71,7 @@ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); - kva = usbvideo_uvirt_to_kva(pgd_offset_k(va), va); + kva = page_address(vmalloc_to_page(va)); ret = __pa(kva); MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); return ret; @@ -791,6 +768,10 @@ cams->cb.getFrame = usbvideo_GetFrame; if (cams->cb.disconnect == NULL) cams->cb.disconnect = usbvideo_Disconnect; + if (cams->cb.startDataPump == NULL) + cams->cb.startDataPump = usbvideo_StartDataPump; + if (cams->cb.stopDataPump == NULL) + cams->cb.stopDataPump = usbvideo_StopDataPump; #if USES_PROC_FS /* * If both /proc fs callbacks are NULL then we assume that the driver @@ -963,7 +944,7 @@ uvd->remove_pending = 1; /* Now all ISO data will be ignored */ /* At this time we ask to cancel outstanding URBs */ - usbvideo_StopDataPump(uvd); + GET_CALLBACK(uvd, stopDataPump)(uvd); for (i=0; i < USBVIDEO_NUMSBUF; i++) usb_free_urb(uvd->sbuf[i].urb); @@ -1073,7 +1054,7 @@ down(&uvd->lock); for (i=0; i < USBVIDEO_NUMSBUF; i++) { - uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC); + uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (uvd->sbuf[i].urb == NULL) { err("usb_alloc_urb(%d.) failed.", FRAMES_PER_DESC); uvd->uvd_used = 0; @@ -1299,7 +1280,7 @@ if (errCode == 0) { /* Start data pump if we have valid endpoint */ if (uvd->video_endp != 0) - errCode = usbvideo_StartDataPump(uvd); + errCode = GET_CALLBACK(uvd, startDataPump)(uvd); if (errCode == 0) { if (VALID_CALLBACK(uvd, setupOnOpen)) { if (uvd->debug > 1) @@ -1349,8 +1330,8 @@ if (uvd->debug > 1) info("%s($%p)", proc, dev); - down(&uvd->lock); - usbvideo_StopDataPump(uvd); + down(&uvd->lock); + GET_CALLBACK(uvd, stopDataPump)(uvd); usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); uvd->fbuf = NULL; RingQueue_Free(&uvd->dp); diff -Nru a/drivers/usb/usbvideo.h b/drivers/usb/usbvideo.h --- a/drivers/usb/usbvideo.h Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/usbvideo.h Tue Feb 19 18:08:59 2002 @@ -269,6 +269,8 @@ int (*getFrame)(uvd_t *, int); int (*procfs_read)(char *page,char **start,off_t off,int count,int *eof,void *data); int (*procfs_write)(struct file *file,const char *buffer,unsigned long count,void *data); + int (*startDataPump)(uvd_t *uvd); + void (*stopDataPump)(uvd_t *uvd); } usbvideo_cb_t; struct s_usbvideo_t { @@ -328,7 +330,6 @@ void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs); /* Memory allocation routines */ -unsigned long usbvideo_uvirt_to_kva(pgd_t *pgd, unsigned long adr); unsigned long usbvideo_kvirt_to_pa(unsigned long adr); void *usbvideo_rvmalloc(unsigned long size); void usbvideo_rvfree(void *mem, unsigned long size); diff -Nru a/drivers/usb/vicam.c b/drivers/usb/vicam.c --- a/drivers/usb/vicam.c Tue Feb 19 18:08:59 2002 +++ b/drivers/usb/vicam.c Tue Feb 19 18:08:59 2002 @@ -115,8 +115,11 @@ if (!pgd_none(*pgd)) { pmd = pmd_offset(pgd, adr); if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); + preempt_disable(); + ptep = pte_offset_map(pmd, adr); pte = *ptep; + pte_unmap(pte); + preempt_enable(); if(pte_present(pte)) { ret = (unsigned long) page_address(pte_page(pte)); ret |= (adr & (PAGE_SIZE - 1)); @@ -884,7 +887,7 @@ } memset(vicam, 0, sizeof(*vicam)); - vicam->readurb = usb_alloc_urb(0); + vicam->readurb = usb_alloc_urb(0, GFP_KERNEL); if (!vicam->readurb) { kfree(vicam); return NULL; @@ -896,13 +899,18 @@ vicam->win.contrast = 10; /* FIXME */ - if (vicam_init(vicam)) + if (vicam_init(vicam)) { + usb_free_urb(vicam->readurb); + kfree(vicam); return NULL; + } memcpy(&vicam->vdev, &vicam_template, sizeof(vicam_template)); memcpy(vicam->vdev.name, vicam->camera_name, strlen(vicam->camera_name)); if (video_register_device(&vicam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { err("video_register_device"); + usb_free_urb(vicam->readurb); + kfree(vicam); return NULL; } diff -Nru a/drivers/usb/wacom.c b/drivers/usb/wacom.c --- a/drivers/usb/wacom.c Tue Feb 19 18:08:57 2002 +++ b/drivers/usb/wacom.c Tue Feb 19 18:08:57 2002 @@ -1,7 +1,7 @@ /* - * $Id: wacom.c,v 1.22 2001/04/26 11:26:09 vojtech Exp $ + * $Id: wacom.c,v 1.28 2001/09/25 10:12:07 vojtech Exp $ * - * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2000 Andreas Bach Aaen * Copyright (c) 2000 Clifford Wolf * Copyright (c) 2000 Sam Mosel @@ -11,8 +11,6 @@ * * USB Wacom Graphire and Wacom Intuos tablet support * - * Sponsored by SuSE - * * ChangeLog: * v0.1 (vp) - Initial release * v0.2 (aba) - Support for all buttons / combinations @@ -57,8 +55,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include @@ -72,12 +70,13 @@ * Version Information */ #define DRIVER_VERSION "v1.21" -#define DRIVER_AUTHOR "Vojtech Pavlik " +#define DRIVER_AUTHOR "Vojtech Pavlik " #define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver" +#define DRIVER_LICENSE "GPL" -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); #define USB_VENDOR_ID_WACOM 0x056a @@ -106,6 +105,7 @@ int open; int x, y; __u32 serial[2]; + char phys[32]; }; static void wacom_pl_irq(struct urb *urb) @@ -354,11 +354,12 @@ { struct usb_endpoint_descriptor *endpoint; struct wacom *wacom; + char path[64]; if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL))) return NULL; memset(wacom, 0, sizeof(struct wacom)); - wacom->irq = usb_alloc_urb(0); + wacom->irq = usb_alloc_urb(0, GFP_KERNEL); if (!wacom->irq) { kfree(wacom); return NULL; @@ -394,7 +395,11 @@ wacom->dev.open = wacom_open; wacom->dev.close = wacom_close; + usb_make_path(dev, path, 64); + sprintf(wacom->phys, "%s/input0", path); + wacom->dev.name = wacom->features->name; + wacom->dev.phys = wacom->phys; wacom->dev.idbus = BUS_USB; wacom->dev.idvendor = dev->descriptor.idVendor; wacom->dev.idproduct = dev->descriptor.idProduct; @@ -408,8 +413,7 @@ input_register_device(&wacom->dev); - printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", - wacom->dev.number, wacom->features->name, dev->bus->busnum, dev->devnum, ifnum); + printk(KERN_INFO "input: %s on %s\n", wacom->features->name, path); return wacom; } diff -Nru a/drivers/video/Config.help b/drivers/video/Config.help --- a/drivers/video/Config.help Tue Feb 19 18:08:58 2002 +++ b/drivers/video/Config.help Tue Feb 19 18:08:58 2002 @@ -381,8 +381,8 @@ Say Y here if you have a Matrox Millennium, Matrox Millennium II, Matrox Mystique, Matrox Mystique 220, Matrox Productiva G100, Matrox Mystique G200, Matrox Millennium G200, Matrox Marvel G200 video, - Matrox G400 or G450 card in your box. At this time, support for the - G100 is untested and support for G450 is highly experimental. + Matrox G400, G450 or G550 card in your box. At this time, support for + the G-series digital output is almost non-existant. This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). @@ -408,7 +408,7 @@ different from 8. CONFIG_FB_MATROX_G100 - Say Y here if you have a Matrox G100, G200, G400 or G450 based + Say Y here if you have a Matrox G100, G200, G400, G450 or G550 based video card. If you select "Advanced lowlevel driver options", you should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp packed pixel and 32 bpp packed pixel. You can also use font widths @@ -418,6 +418,10 @@ "I2C support" and "I2C bit-banging support" in the character devices section, and then to "Matrox I2C support" and "G400 second head support" here in the framebuffer section. + + If you have G550, you must also compile support for G450/G550 secondary + head into kernel, otherwise picture will be shown only on output you + are probably not using... CONFIG_FB_MATROX_I2C This drivers creates I2C buses which are needed for accessing the @@ -462,7 +466,8 @@ CONFIG_FB_MATROX_G450 Say Y or M here if you want to use a secondary head (meaning two - monitors in parallel) on G450. + monitors in parallel) on G450, or if you are using analog output + of G550. If you compile it as module, two modules are created, matroxfb_crtc2.o and matroxfb_g450.o. Both modules are needed if you diff -Nru a/drivers/video/Config.in b/drivers/video/Config.in --- a/drivers/video/Config.in Tue Feb 19 18:09:00 2002 +++ b/drivers/video/Config.in Tue Feb 19 18:09:00 2002 @@ -128,7 +128,7 @@ dep_tristate ' G400 second head support' CONFIG_FB_MATROX_MAVEN $CONFIG_FB_MATROX_I2C fi fi - dep_tristate ' G450/G550 second head support' CONFIG_FB_MATROX_G450 $CONFIG_FB_MATROX_G100 + dep_tristate ' G450/G550 second head support (mandatory for G550)' CONFIG_FB_MATROX_G450 $CONFIG_FB_MATROX_G100 bool ' Multihead support' CONFIG_FB_MATROX_MULTIHEAD fi tristate ' ATI Mach64 display support (EXPERIMENTAL)' CONFIG_FB_ATY diff -Nru a/drivers/video/fbmem.c b/drivers/video/fbmem.c --- a/drivers/video/fbmem.c Tue Feb 19 18:08:56 2002 +++ b/drivers/video/fbmem.c Tue Feb 19 18:08:56 2002 @@ -616,6 +616,8 @@ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); #elif defined(__sh__) pgprot_val(vma->vm_page_prot) &= ~_PAGE_CACHABLE; +#elif defined(__ia64__) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); #else #warning What do we have to do here?? #endif diff -Nru a/drivers/video/matrox/Makefile b/drivers/video/matrox/Makefile --- a/drivers/video/matrox/Makefile Tue Feb 19 18:08:58 2002 +++ b/drivers/video/matrox/Makefile Tue Feb 19 18:08:58 2002 @@ -7,11 +7,13 @@ # All of the (potential) objects that export symbols. # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. -export-objs := matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o +export-objs := matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o g450_pll.o # Each configuration option enables a list of files. -obj-$(CONFIG_FB_MATROX) += matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o +my-obj-$(CONFIG_FB_MATROX_G100) := g450_pll.o + +obj-$(CONFIG_FB_MATROX) += matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o $(my-obj-y) obj-$(CONFIG_FB_MATROX_I2C) += i2c-matroxfb.o obj-$(CONFIG_FB_MATROX_MAVEN) += matroxfb_maven.o matroxfb_crtc2.o obj-$(CONFIG_FB_MATROX_G450) += matroxfb_g450.o matroxfb_crtc2.o diff -Nru a/drivers/video/matrox/g450_pll.c b/drivers/video/matrox/g450_pll.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/video/matrox/g450_pll.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,472 @@ +/* + * + * Hardware accelerated Matrox PCI cards - G450/G550 PLL control. + * + * (c) 2001 Petr Vandrovec + * + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 + * + * 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 archive for + * more details. + * + */ + +#include "g450_pll.h" +#include "matroxfb_DAC1064.h" + +static inline unsigned int g450_vco2f(unsigned char p, unsigned int fvco) { + return (p & 0x40) ? fvco : fvco >> ((p & 3) + 1); +} + +static inline unsigned int g450_f2vco(unsigned char p, unsigned int fin) { + return (p & 0x40) ? fin : fin << ((p & 3) + 1); +} + +static unsigned int g450_mnp2vco(CPMINFO unsigned int mnp) { + unsigned int m, n; + + m = ((mnp >> 16) & 0x0FF) + 1; + n = ((mnp >> 7) & 0x1FE) + 4; + return (ACCESS_FBINFO(features).pll.ref_freq * n + (m >> 1)) / m; +} + +static inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) { + if (f2 < f1) { + f2 = f1 - f2; + } else { + f2 = f2 - f1; + } + return f2; +} + +#define NO_MORE_MNP 0x01FFFFFF +#define G450_MNP_FREQBITS (0xFFFFFF43) /* do not mask high byte so we'll catch NO_MORE_MNP */ + +static unsigned int g450_nextpll(CPMINFO const struct matrox_pll_limits* pi, unsigned int* fvco, unsigned int mnp) { + unsigned int m, n, p; + unsigned int tvco = *fvco; + + m = (mnp >> 16) & 0xFF; + p = mnp & 0xFF; + + if (m == 0 || m == 0xFF) { + if (m == 0) { + if (p & 0x40) { + return NO_MORE_MNP; + } + if (p & 3) { + p--; + } else { + p = 0x40; + } + tvco >>= 1; + if (tvco < pi->vcomin) { + return NO_MORE_MNP; + } + *fvco = tvco; + } + + p &= 0x43; + if (tvco < 550000) { +/* p |= 0x00; */ + } else if (tvco < 700000) { + p |= 0x08; + } else if (tvco < 1000000) { + p |= 0x10; + } else if (tvco < 1150000) { + p |= 0x18; + } else { + p |= 0x20; + } + m = 9; + } else { + m--; + } + n = ((tvco * (m+1) + ACCESS_FBINFO(features).pll.ref_freq) / (ACCESS_FBINFO(features).pll.ref_freq * 2)) - 2; + return (m << 16) | (n << 8) | p; +} + +static unsigned int g450_firstpll(CPMINFO const struct matrox_pll_limits* pi, unsigned int* vco, unsigned int fout) { + unsigned int p; + unsigned int vcomax; + + vcomax = pi->vcomax; + if (fout > (vcomax / 2)) { + if (fout > vcomax) { + *vco = vcomax; + } else { + *vco = fout; + } + p = 0x40; + } else { + unsigned int tvco; + + p = 3; + tvco = g450_f2vco(p, fout); + while (p && (tvco > vcomax)) { + p--; + tvco >>= 1; + } + if (tvco < pi->vcomin) { + tvco = pi->vcomin; + } + *vco = tvco; + } + return g450_nextpll(PMINFO pi, vco, 0xFF0000 | p); +} + +static inline unsigned int g450_setpll(CPMINFO unsigned int mnp, unsigned int pll) { + switch (pll) { + case M_PIXEL_PLL_A: + matroxfb_DAC_out(PMINFO M1064_XPIXPLLAM, mnp >> 16); + matroxfb_DAC_out(PMINFO M1064_XPIXPLLAN, mnp >> 8); + matroxfb_DAC_out(PMINFO M1064_XPIXPLLAP, mnp); + return M1064_XPIXPLLSTAT; + + case M_PIXEL_PLL_B: + matroxfb_DAC_out(PMINFO M1064_XPIXPLLBM, mnp >> 16); + matroxfb_DAC_out(PMINFO M1064_XPIXPLLBN, mnp >> 8); + matroxfb_DAC_out(PMINFO M1064_XPIXPLLBP, mnp); + return M1064_XPIXPLLSTAT; + + case M_PIXEL_PLL_C: + matroxfb_DAC_out(PMINFO M1064_XPIXPLLCM, mnp >> 16); + matroxfb_DAC_out(PMINFO M1064_XPIXPLLCN, mnp >> 8); + matroxfb_DAC_out(PMINFO M1064_XPIXPLLCP, mnp); + return M1064_XPIXPLLSTAT; + + case M_SYSTEM_PLL: + matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLM, mnp >> 16); + matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLN, mnp >> 8); + matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLP, mnp); + return DAC1064_XSYSPLLSTAT; + + case M_VIDEO_PLL: + matroxfb_DAC_out(PMINFO M1064_XVIDPLLM, mnp >> 16); + matroxfb_DAC_out(PMINFO M1064_XVIDPLLN, mnp >> 8); + matroxfb_DAC_out(PMINFO M1064_XVIDPLLP, mnp); + return M1064_XVIDPLLSTAT; + } + return 0; +} + +static inline unsigned int g450_cmppll(CPMINFO unsigned int mnp, unsigned int pll) { + unsigned char m = mnp >> 16; + unsigned char n = mnp >> 8; + unsigned char p = mnp; + + switch (pll) { + case M_PIXEL_PLL_A: + return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLAM) != m || + matroxfb_DAC_in(PMINFO M1064_XPIXPLLAN) != n || + matroxfb_DAC_in(PMINFO M1064_XPIXPLLAP) != p); + + case M_PIXEL_PLL_B: + return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLBM) != m || + matroxfb_DAC_in(PMINFO M1064_XPIXPLLBN) != n || + matroxfb_DAC_in(PMINFO M1064_XPIXPLLBP) != p); + + case M_PIXEL_PLL_C: + return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLCM) != m || + matroxfb_DAC_in(PMINFO M1064_XPIXPLLCN) != n || + matroxfb_DAC_in(PMINFO M1064_XPIXPLLCP) != p); + + case M_SYSTEM_PLL: + return (matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLM) != m || + matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLN) != n || + matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLP) != p); + + case M_VIDEO_PLL: + return (matroxfb_DAC_in(PMINFO M1064_XVIDPLLM) != m || + matroxfb_DAC_in(PMINFO M1064_XVIDPLLN) != n || + matroxfb_DAC_in(PMINFO M1064_XVIDPLLP) != p); + } + return 1; +} + +static inline int g450_isplllocked(CPMINFO unsigned int regidx) { + unsigned int j; + + for (j = 0; j < 1000; j++) { + if (matroxfb_DAC_in(PMINFO regidx) & 0x40) { + unsigned int r = 0; + int i; + + for (i = 0; i < 100; i++) { + r += matroxfb_DAC_in(PMINFO regidx) & 0x40; + } + return r >= (90 * 0x40); + } + /* udelay(1)... but DAC_in is much slower... */ + } + return 0; +} + +static int g450_testpll(CPMINFO unsigned int mnp, unsigned int pll) { + return g450_isplllocked(PMINFO g450_setpll(PMINFO mnp, pll)); +} + +static void updatehwstate_clk(struct matrox_hw_state* hw, unsigned int mnp, unsigned int pll) { + switch (pll) { + case M_SYSTEM_PLL: + hw->DACclk[3] = mnp >> 16; + hw->DACclk[4] = mnp >> 8; + hw->DACclk[5] = mnp; + break; + } +} + +static inline void g450_setpll_cond(WPMINFO unsigned int mnp, unsigned int pll) { + if (g450_cmppll(PMINFO mnp, pll)) { + g450_setpll(PMINFO mnp, pll); + } +} + +static inline unsigned int g450_findworkingpll(WPMINFO unsigned int pll, unsigned int* mnparray, unsigned int mnpcount) { + unsigned int found = 0; + unsigned int idx; + unsigned int mnpfound = mnparray[0]; + + for (idx = 0; idx < mnpcount; idx++) { + unsigned int sarray[3]; + unsigned int *sptr; + { + unsigned int mnp; + + sptr = sarray; + mnp = mnparray[idx]; + if (mnp & 0x38) { + *sptr++ = mnp - 8; + } + if ((mnp & 0x38) != 0x38) { + *sptr++ = mnp + 8; + } + *sptr = mnp; + } + while (sptr >= sarray) { + unsigned int mnp = *sptr--; + + if (g450_testpll(PMINFO mnp - 0x0300, pll) && + g450_testpll(PMINFO mnp + 0x0300, pll) && + g450_testpll(PMINFO mnp - 0x0200, pll) && + g450_testpll(PMINFO mnp + 0x0200, pll) && + g450_testpll(PMINFO mnp - 0x0100, pll) && + g450_testpll(PMINFO mnp + 0x0100, pll)) { + if (g450_testpll(PMINFO mnp, pll)) { + return mnp; + } + } else if (!found && g450_testpll(PMINFO mnp, pll)) { + mnpfound = mnp; + found = 1; + } + } + } + g450_setpll(PMINFO mnpfound, pll); + return mnpfound; +} + +static void g450_addcache(struct matrox_pll_cache* ci, unsigned int mnp_key, unsigned int mnp_value) { + if (++ci->valid > ARRAY_SIZE(ci->data)) { + ci->valid = ARRAY_SIZE(ci->data); + } + memmove(ci->data + 1, ci->data, (ci->valid - 1) * sizeof(*ci->data)); + ci->data[0].mnp_key = mnp_key & G450_MNP_FREQBITS; + ci->data[0].mnp_value = mnp_value; +} + +static int g450_checkcache(WPMINFO struct matrox_pll_cache* ci, unsigned int mnp_key) { + unsigned int i; + + mnp_key &= G450_MNP_FREQBITS; + for (i = 0; i < ci->valid; i++) { + if (ci->data[i].mnp_key == mnp_key) { + unsigned int mnp; + + mnp = ci->data[i].mnp_value; + if (i) { + memmove(ci->data + 1, ci->data, i * sizeof(*ci->data)); + ci->data[0].mnp_key = mnp_key; + ci->data[0].mnp_value = mnp; + } + return mnp; + } + } + return NO_MORE_MNP; +} + +static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, + unsigned int* mnparray, unsigned int* deltaarray) { + unsigned int mnpcount; + unsigned int pixel_vco; + const struct matrox_pll_limits* pi; + struct matrox_pll_cache* ci; + + pixel_vco = 0; + switch (pll) { + case M_PIXEL_PLL_A: + case M_PIXEL_PLL_B: + case M_PIXEL_PLL_C: + { + u_int8_t tmp; + unsigned long flags; + + matroxfb_DAC_lock_irqsave(flags); + tmp = matroxfb_DAC_in(PMINFO M1064_XPIXCLKCTRL); + if (!(tmp & M1064_XPIXCLKCTRL_PLL_UP)) { + matroxfb_DAC_out(PMINFO M1064_XPIXCLKCTRL, tmp | M1064_XPIXCLKCTRL_PLL_UP); + } + matroxfb_DAC_unlock_irqrestore(flags); + } + { + u_int8_t misc; + + misc = mga_inb(M_MISC_REG_READ) & ~0x0C; + switch (pll) { + case M_PIXEL_PLL_A: + break; + case M_PIXEL_PLL_B: + misc |= 0x04; + break; + default: + misc |= 0x0C; + break; + } + mga_outb(M_MISC_REG, misc); + } + pi = &ACCESS_FBINFO(limits.pixel); + ci = &ACCESS_FBINFO(cache.pixel); + break; + case M_SYSTEM_PLL: + { + u_int32_t opt; + + pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, &opt); + if (!(opt & 0x20)) { + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, opt | 0x20); + } + } + pi = &ACCESS_FBINFO(limits.system); + ci = &ACCESS_FBINFO(cache.system); + break; + case M_VIDEO_PLL: + { + u_int8_t tmp; + unsigned int mnp; + unsigned long flags; + + matroxfb_DAC_lock_irqsave(flags); + tmp = matroxfb_DAC_in(PMINFO M1064_XPWRCTRL); + if (!(tmp & 2)) { + matroxfb_DAC_out(PMINFO M1064_XPWRCTRL, tmp | 2); + } + + mnp = matroxfb_DAC_in(PMINFO M1064_XPIXPLLCM) << 16; + mnp |= matroxfb_DAC_in(PMINFO M1064_XPIXPLLCN) << 8; + pixel_vco = g450_mnp2vco(PMINFO mnp); + matroxfb_DAC_unlock_irqrestore(flags); + } + pi = &ACCESS_FBINFO(limits.video); + ci = &ACCESS_FBINFO(cache.video); + break; + default: + return -EINVAL; + } + + mnpcount = 0; + { + unsigned int mnp; + unsigned int xvco; + + for(mnp = g450_firstpll(PMINFO pi, &xvco, fout); mnp != NO_MORE_MNP; mnp = g450_nextpll(PMINFO pi, &xvco, mnp)) { + unsigned int idx; + unsigned int vco; + unsigned int delta; + + if ((mnp & 0xFF00) < 0x0300 || (mnp & 0xFF00) > 0x7A00) { + continue; + } + vco = g450_mnp2vco(PMINFO mnp); + if (pll == M_VIDEO_PLL) { + unsigned int big, small; + + if (vco < pixel_vco) { + small = vco; + big = pixel_vco; + } else { + small = pixel_vco; + big = vco; + } + while (big > small) { + big >>= 1; + } + if (big == small) { + continue; + } + } + delta = pll_freq_delta(fout, g450_vco2f(mnp, vco)); + for (idx = mnpcount; idx > 0; idx--) { + /* == is important; due to nextpll algorithm we get + sorted equally good frequencies from lower VCO + frequency to higher - with <= lowest wins, while + with < highest one wins */ + if (delta <= deltaarray[idx-1]) { + mnparray[idx] = mnparray[idx-1]; + deltaarray[idx] = deltaarray[idx-1]; + } else { + break; + } + } + mnparray[idx] = mnp; + deltaarray[idx] = delta; + mnpcount++; + } + } + /* VideoPLL and PixelPLL matched: do nothing... In all other cases we should get at least one frequency */ + if (!mnpcount) { + return 1; + } + { + unsigned long flags; + unsigned int mnp; + + matroxfb_DAC_lock_irqsave(flags); + mnp = g450_checkcache(PMINFO ci, mnparray[0]); + if (mnp != NO_MORE_MNP) { + g450_setpll_cond(PMINFO mnp, pll); + } else { + mnp = g450_findworkingpll(PMINFO pll, mnparray, mnpcount); + g450_addcache(ci, mnparray[0], mnp); + } + updatehwstate_clk(&ACCESS_FBINFO(hw), mnp, pll); + matroxfb_DAC_unlock_irqrestore(flags); + } + return 0; +} + +/* It must be greater than number of possible PLL values. + * Currently there is 5(p) * 10(m) = 50 possible values. */ +#define MNP_TABLE_SIZE 64 + +int matroxfb_g450_setclk(WPMINFO unsigned int fout, unsigned int pll) { + unsigned int* arr; + + arr = kmalloc(sizeof(*arr) * MNP_TABLE_SIZE * 2, GFP_KERNEL); + if (arr) { + int r; + + r = __g450_setclk(PMINFO fout, pll, arr, arr + MNP_TABLE_SIZE); + kfree(arr); + return r; + } + return -ENOMEM; +} + +EXPORT_SYMBOL(matroxfb_g450_setclk); + +MODULE_AUTHOR("(c) 2001 Petr Vandrovec "); +MODULE_DESCRIPTION("Matrox G450/G550 PLL driver"); + +MODULE_LICENSE("GPL"); diff -Nru a/drivers/video/matrox/g450_pll.h b/drivers/video/matrox/g450_pll.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/video/matrox/g450_pll.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,8 @@ +#ifndef __G450_PLL_H__ +#define __G450_PLL_H__ + +#include "matroxfb_base.h" + +int matroxfb_g450_setclk(WPMINFO unsigned int fout, unsigned int pll); + +#endif /* __G450_PLL_H__ */ diff -Nru a/drivers/video/matrox/matroxfb_DAC1064.c b/drivers/video/matrox/matroxfb_DAC1064.c --- a/drivers/video/matrox/matroxfb_DAC1064.c Tue Feb 19 18:08:59 2002 +++ b/drivers/video/matrox/matroxfb_DAC1064.c Tue Feb 19 18:08:59 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.54 2001/09/09 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * See matroxfb_base.c for contributors. * @@ -16,6 +18,7 @@ #include "matroxfb_DAC1064.h" #include "matroxfb_misc.h" #include "matroxfb_accel.h" +#include "g450_pll.h" #include #ifdef NEED_DAC1064 @@ -217,19 +220,20 @@ 0x00, 0x00, 0x00, 0xFF, 0xFF}; -static void DAC1064_setpclk(CPMINFO struct matrox_hw_state* hw, unsigned long fout) { +static void DAC1064_setpclk(WPMINFO unsigned long fout) { unsigned int m, n, p; DBG("DAC1064_setpclk") DAC1064_calcclock(PMINFO fout, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p); - hw->DACclk[0] = m; - hw->DACclk[1] = n; - hw->DACclk[2] = p; + ACCESS_FBINFO(hw).DACclk[0] = m; + ACCESS_FBINFO(hw).DACclk[1] = n; + ACCESS_FBINFO(hw).DACclk[2] = p; } -static void DAC1064_setmclk(CPMINFO struct matrox_hw_state* hw, int oscinfo, unsigned long fmem){ +static void DAC1064_setmclk(WPMINFO int oscinfo, unsigned long fmem) { u_int32_t mx; + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); DBG("DAC1064_setmclk") @@ -289,7 +293,9 @@ hw->MXoptionReg = mx; } -void DAC1064_global_init(CPMINFO struct matrox_hw_state* hw) { +void DAC1064_global_init(WPMINFO2) { + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + hw->DACreg[POS1064_XMISCCTRL] &= M1064_XMISCCTRL_DAC_WIDTHMASK; hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_LUT_EN; hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL; @@ -314,7 +320,9 @@ hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN; } -void DAC1064_global_restore(CPMINFO const struct matrox_hw_state* hw) { +void DAC1064_global_restore(WPMINFO2) { + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + outDAC1064(PMINFO M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]); outDAC1064(PMINFO M1064_XMISCCTRL, hw->DACreg[POS1064_XMISCCTRL]); if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) { @@ -328,7 +336,9 @@ } } -static int DAC1064_init_1(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display *p) { +static int DAC1064_init_1(WPMINFO struct my_timming* m, struct display *p) { + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + DBG("DAC1064_init_1") memcpy(hw->DACreg, MGA1064_DAC, sizeof(MGA1064_DAC_regs)); @@ -359,7 +369,7 @@ } } - DAC1064_global_init(PMINFO hw); + DAC1064_global_init(PMINFO2); hw->DACreg[POS1064_XVREFCTRL] = ACCESS_FBINFO(features.DAC1064.xvrefctrl); hw->DACreg[POS1064_XGENCTRL] &= ~M1064_XGENCTRL_SYNC_ON_GREEN_MASK; hw->DACreg[POS1064_XGENCTRL] |= (m->sync & FB_SYNC_ON_GREEN)?M1064_XGENCTRL_SYNC_ON_GREEN:M1064_XGENCTRL_NO_SYNC_ON_GREEN; @@ -368,7 +378,8 @@ return 0; } -static int DAC1064_init_2(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p) { +static int DAC1064_init_2(WPMINFO struct my_timming* m, struct display* p) { + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); DBG("DAC1064_init_2") @@ -409,21 +420,22 @@ return 0; } -static void DAC1064_restore_1(WPMINFO const struct matrox_hw_state* hw, const struct matrox_hw_state* oldhw) { +static void DAC1064_restore_1(WPMINFO2) { + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + CRITFLAGS DBG("DAC1064_restore_1") CRITBEGIN - outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3]); - outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4]); - outDAC1064(PMINFO DAC1064_XSYSPLLP, hw->DACclk[5]); - /* - * We must ALWAYS reprogram hardware due to broken XF4 matrox drivers... - * - * if (!oldhw || memcmp(hw->DACreg, oldhw->DACreg, sizeof(MGA1064_DAC_regs))) - */ + if ((inDAC1064(PMINFO DAC1064_XSYSPLLM) != hw->DACclk[3]) || + (inDAC1064(PMINFO DAC1064_XSYSPLLN) != hw->DACclk[4]) || + (inDAC1064(PMINFO DAC1064_XSYSPLLP) != hw->DACclk[5])) { + outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3]); + outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4]); + outDAC1064(PMINFO DAC1064_XSYSPLLP, hw->DACclk[5]); + } { unsigned int i; @@ -433,12 +445,12 @@ } } - DAC1064_global_restore(PMINFO hw); + DAC1064_global_restore(PMINFO2); CRITEND }; -static void DAC1064_restore_2(WPMINFO const struct matrox_hw_state* hw, const struct matrox_hw_state* oldhw, struct display* p) { +static void DAC1064_restore_2(WPMINFO struct display* p) { #ifdef DEBUG unsigned int i; #endif @@ -449,48 +461,51 @@ #ifdef DEBUG dprintk(KERN_DEBUG "DAC1064regs "); for (i = 0; i < sizeof(MGA1064_DAC_regs); i++) { - dprintk("R%02X=%02X ", MGA1064_DAC_regs[i], hw->DACreg[i]); + dprintk("R%02X=%02X ", MGA1064_DAC_regs[i], ACCESS_FBINFO(hw).DACreg[i]); if ((i & 0x7) == 0x7) dprintk("\n" KERN_DEBUG "continuing... "); } dprintk("\n" KERN_DEBUG "DAC1064clk "); for (i = 0; i < 6; i++) - dprintk("C%02X=%02X ", i, hw->DACclk[i]); + dprintk("C%02X=%02X ", i, ACCESS_FBINFO(hw).DACclk[i]); dprintk("\n"); #endif } -static int m1064_compute(void* outdev, struct my_timming* m, struct matrox_hw_state* hw) { -#define minfo ((struct matrox_fb_info*)outdev) - DAC1064_setpclk(PMINFO hw, m->pixclock); -#undef minfo - return 0; -} - -static int m1064_program(void* outdev, const struct matrox_hw_state* hw) { +static int m1064_compute(void* outdev, struct my_timming* m) { #define minfo ((struct matrox_fb_info*)outdev) - int i; - int tmout; - CRITFLAGS + if (ACCESS_FBINFO(devflags.g450dac)) { + matroxfb_g450_setclk(PMINFO m->pixclock, M_PIXEL_PLL_C); + } else { + int i; + int tmout; + CRITFLAGS - CRITBEGIN + DAC1064_setpclk(PMINFO m->pixclock); - for (i = 0; i < 3; i++) - outDAC1064(PMINFO M1064_XPIXPLLCM + i, hw->DACclk[i]); - for (tmout = 500000; tmout; tmout--) { - if (inDAC1064(PMINFO M1064_XPIXPLLSTAT) & 0x40) - break; - udelay(10); - }; + CRITBEGIN - CRITEND + for (i = 0; i < 3; i++) + outDAC1064(PMINFO M1064_XPIXPLLCM + i, ACCESS_FBINFO(hw).DACclk[i]); + for (tmout = 500000; tmout; tmout--) { + if (inDAC1064(PMINFO M1064_XPIXPLLSTAT) & 0x40) + break; + udelay(10); + }; - if (!tmout) - printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n"); + CRITEND + if (!tmout) + printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n"); + } #undef minfo return 0; } +static int m1064_program(void* outdev) { + /* nothing, hardware is set in m1064_compute */ + return 0; +} + static int m1064_start(void* outdev) { /* nothing */ return 0; @@ -522,12 +537,13 @@ #endif /* NEED_DAC1064 */ #ifdef CONFIG_FB_MATROX_MYSTIQUE -static int MGA1064_init(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p) { +static int MGA1064_init(WPMINFO struct my_timming* m, struct display* p) { + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); DBG("MGA1064_init") - if (DAC1064_init_1(PMINFO hw, m, p)) return 1; - if (matroxfb_vgaHWinit(PMINFO hw, m, p)) return 1; + if (DAC1064_init_1(PMINFO m, p)) return 1; + if (matroxfb_vgaHWinit(PMINFO m, p)) return 1; hw->MiscOutReg = 0xCB; if (m->sync & FB_SYNC_HOR_HIGH_ACT) @@ -537,19 +553,20 @@ if (m->sync & FB_SYNC_COMP_HIGH_ACT) /* should be only FB_SYNC_COMP */ hw->CRTCEXT[3] |= 0x40; - if (DAC1064_init_2(PMINFO hw, m, p)) return 1; + if (DAC1064_init_2(PMINFO m, p)) return 1; return 0; } #endif #ifdef CONFIG_FB_MATROX_G100 -static int MGAG100_init(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p) { +static int MGAG100_init(WPMINFO struct my_timming* m, struct display* p) { + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); DBG("MGAG100_init") - if (DAC1064_init_1(PMINFO hw, m, p)) return 1; + if (DAC1064_init_1(PMINFO m, p)) return 1; hw->MXoptionReg &= ~0x2000; - if (matroxfb_vgaHWinit(PMINFO hw, m, p)) return 1; + if (matroxfb_vgaHWinit(PMINFO m, p)) return 1; hw->MiscOutReg = 0xEF; if (m->sync & FB_SYNC_HOR_HIGH_ACT) @@ -559,13 +576,13 @@ if (m->sync & FB_SYNC_COMP_HIGH_ACT) /* should be only FB_SYNC_COMP */ hw->CRTCEXT[3] |= 0x40; - if (DAC1064_init_2(PMINFO hw, m, p)) return 1; + if (DAC1064_init_2(PMINFO m, p)) return 1; return 0; } #endif /* G100 */ #ifdef CONFIG_FB_MATROX_MYSTIQUE -static void MGA1064_ramdac_init(WPMINFO struct matrox_hw_state* hw){ +static void MGA1064_ramdac_init(WPMINFO2) { DBG("MGA1064_ramdac_init"); @@ -579,7 +596,7 @@ ACCESS_FBINFO(features.pll.post_shift_max) = 3; ACCESS_FBINFO(features.DAC1064.xvrefctrl) = DAC1064_XVREFCTRL_EXTERNAL; /* maybe cmdline MCLK= ?, doc says gclk=44MHz, mclk=66MHz... it was 55/83 with old values */ - DAC1064_setmclk(PMINFO hw, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333); + DAC1064_setmclk(PMINFO DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333); } #endif @@ -591,7 +608,7 @@ static int def50 = 0; /* reg50, & 0x0F, & 0x3000 (only 0x0000, 0x1000, 0x2000 (0x3000 disallowed and treated as 0) */ #endif -static void MGAG100_progPixClock(CPMINFO int flags, int m, int n, int p){ +static void MGAG100_progPixClock(CPMINFO int flags, int m, int n, int p) { int reg; int selClk; int clk; @@ -635,7 +652,7 @@ outDAC1064(PMINFO M1064_XPIXCLKCTRL, inDAC1064(PMINFO M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_DIS); } -static void MGAG100_setPixClock(CPMINFO int flags, int freq){ +static void MGAG100_setPixClock(CPMINFO int flags, int freq) { unsigned int m, n, p; DBG("MGAG100_setPixClock") @@ -646,10 +663,12 @@ #endif #ifdef CONFIG_FB_MATROX_MYSTIQUE -static int MGA1064_preinit(WPMINFO struct matrox_hw_state* hw){ +static int MGA1064_preinit(WPMINFO2) { static const int vxres_mystique[] = { 512, 640, 768, 800, 832, 960, 1024, 1152, 1280, 1600, 1664, 1920, 2048, 0}; + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + DBG("MGA1064_preinit") /* ACCESS_FBINFO(capable.cfb4) = 0; ... preinitialized by 0 */ @@ -680,7 +699,7 @@ return 0; } -static void MGA1064_reset(WPMINFO struct matrox_hw_state* hw){ +static void MGA1064_reset(WPMINFO2) { DBG("MGA1064_reset"); @@ -688,15 +707,130 @@ if (ACCESS_FBINFO(devflags.hwcursor)) ACCESS_FBINFO(video.len_usable) -= 1024; matroxfb_fastfont_init(MINFO); - MGA1064_ramdac_init(PMINFO hw); + MGA1064_ramdac_init(PMINFO2); } #endif #ifdef CONFIG_FB_MATROX_G100 -static int MGAG100_preinit(WPMINFO struct matrox_hw_state* hw){ +static void g450_mclk_init(WPMINFO2) { + /* switch all clocks to PCI source */ + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg | 4); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION3_REG, ACCESS_FBINFO(values).reg.opt3 & ~0x00300C03); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + + if (((ACCESS_FBINFO(values).reg.opt3 & 0x000003) == 0x000003) || + ((ACCESS_FBINFO(values).reg.opt3 & 0x000C00) == 0x000C00) || + ((ACCESS_FBINFO(values).reg.opt3 & 0x300000) == 0x300000)) { + matroxfb_g450_setclk(PMINFO ACCESS_FBINFO(values.pll.video), M_VIDEO_PLL); + } else { + /* slow down video clocks... */ + matroxfb_g450_setclk(PMINFO 0, M_VIDEO_PLL); + } + matroxfb_g450_setclk(PMINFO ACCESS_FBINFO(values.pll.system), M_SYSTEM_PLL); + + /* switch clocks to their real PLL source(s) */ + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg | 4); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION3_REG, ACCESS_FBINFO(values).reg.opt3); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + +} + +static void g450_memory_init(WPMINFO2) { + /* disable memory refresh */ + ACCESS_FBINFO(hw).MXoptionReg &= ~0x001F8000; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + + /* set memory interface parameters */ + ACCESS_FBINFO(hw).MXoptionReg &= ~0x00207E00; + ACCESS_FBINFO(hw).MXoptionReg |= 0x00207E00 & ACCESS_FBINFO(values).reg.opt; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ACCESS_FBINFO(values).reg.opt2); + + mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst); + + /* first set up memory interface with disabled memory interface clocks */ + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_MEMMISC_REG, ACCESS_FBINFO(values).reg.memmisc & ~0x80000000U); + mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); + mga_outl(M_MACCESS, ACCESS_FBINFO(values).reg.maccess); + /* start memory clocks */ + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_MEMMISC_REG, ACCESS_FBINFO(values).reg.memmisc | 0x80000000U); + + udelay(200); + + if (ACCESS_FBINFO(values).memory.ddr && (!ACCESS_FBINFO(values).memory.emrswen || !ACCESS_FBINFO(values).memory.dll)) { + mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk & ~0x1000); + } + mga_outl(M_MACCESS, ACCESS_FBINFO(values).reg.maccess | 0x8000); + + udelay(200); + + ACCESS_FBINFO(hw).MXoptionReg |= 0x001F8000 & ACCESS_FBINFO(values).reg.opt; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + + /* value is written to memory chips only if old != new */ + mga_outl(M_PLNWT, 0); + mga_outl(M_PLNWT, ~0); + + if (ACCESS_FBINFO(values).reg.mctlwtst != ACCESS_FBINFO(values).reg.mctlwtst_core) { + mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst_core); + } + +} + +static void g450_preinit(WPMINFO2) { + u_int32_t c2ctl; + u_int8_t curctl; + u_int8_t c1ctl; + + /* ACCESS_FBINFO(hw).MXoptionReg = minfo->values.reg.opt; */ + ACCESS_FBINFO(hw).MXoptionReg &= 0xC0000100; + ACCESS_FBINFO(hw).MXoptionReg |= 0x00000020; + if (ACCESS_FBINFO(devflags.novga)) + ACCESS_FBINFO(hw).MXoptionReg &= ~0x00000100; + if (ACCESS_FBINFO(devflags.nobios)) + ACCESS_FBINFO(hw).MXoptionReg &= ~0x40000000; + if (ACCESS_FBINFO(devflags.nopciretry)) + ACCESS_FBINFO(hw).MXoptionReg |= 0x20000000; + ACCESS_FBINFO(hw).MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x03400040; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + + /* Init system clocks */ + + /* stop crtc2 */ + c2ctl = mga_inl(M_C2CTL); + mga_outl(M_C2CTL, c2ctl & ~1); + /* stop cursor */ + curctl = inDAC1064(PMINFO M1064_XCURCTRL); + outDAC1064(PMINFO M1064_XCURCTRL, 0); + /* stop crtc1 */ + c1ctl = mga_readr(M_SEQ_INDEX, 1); + mga_setr(M_SEQ_INDEX, 1, c1ctl | 0x20); + + g450_mclk_init(PMINFO2); + g450_memory_init(PMINFO2); + + /* set legacy VGA clock sources for DOSEmu or VMware... */ + matroxfb_g450_setclk(PMINFO 25175, M_PIXEL_PLL_A); + matroxfb_g450_setclk(PMINFO 28322, M_PIXEL_PLL_B); + + /* restore crtc1 */ + mga_setr(M_SEQ_INDEX, 1, c1ctl); + + /* restore cursor */ + outDAC1064(PMINFO M1064_XCURCTRL, curctl); + + /* restore crtc2 */ + mga_outl(M_C2CTL, c2ctl); + + return; +} + +static int MGAG100_preinit(WPMINFO2) { static const int vxres_g100[] = { 512, 640, 768, 800, 832, 960, 1024, 1152, 1280, 1600, 1664, 1920, 2048, 0}; + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + u_int32_t reg50; #if 0 u_int32_t q; @@ -710,7 +844,9 @@ } else { ACCESS_FBINFO(features.pll.vco_freq_min) = 62000; } - ACCESS_FBINFO(features.pll.ref_freq) = 27000; + if (!ACCESS_FBINFO(features.pll.ref_freq)) { + ACCESS_FBINFO(features.pll.ref_freq) = 27000; + } ACCESS_FBINFO(features.pll.feed_div_min) = 7; ACCESS_FBINFO(features.pll.feed_div_max) = 127; ACCESS_FBINFO(features.pll.in_div_min) = 1; @@ -734,6 +870,10 @@ } if (ACCESS_FBINFO(devflags.noinit)) return 0; + if (ACCESS_FBINFO(devflags.g450dac)) { + g450_preinit(PMINFO2); + return 0; + } hw->MXoptionReg &= 0xC0000100; hw->MXoptionReg |= 0x00000020; if (ACCESS_FBINFO(devflags.novga)) @@ -743,12 +883,12 @@ if (ACCESS_FBINFO(devflags.nopciretry)) hw->MXoptionReg |= 0x20000000; pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); - DAC1064_setmclk(PMINFO hw, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PCI, 133333); + DAC1064_setmclk(PMINFO DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PCI, 133333); if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG100) { - pci_read_config_dword(ACCESS_FBINFO(pcidev), 0x50, ®50); + pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ®50); reg50 &= ~0x3000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), 0x50, reg50); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); hw->MXoptionReg |= 0x1080; pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); @@ -763,7 +903,7 @@ udelay(100); reg50 &= ~0xFF; reg50 |= 0x07; - pci_write_config_dword(ACCESS_FBINFO(pcidev), 0x50, reg50); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); /* it should help with G100 */ mga_outb(M_GRAPHICS_INDEX, 6); mga_outb(M_GRAPHICS_DATA, (mga_inb(M_GRAPHICS_DATA) & 3) | 4); @@ -778,29 +918,30 @@ } #endif hw->MXoptionReg |= 0x00078020; - } else if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG200) { - pci_read_config_dword(ACCESS_FBINFO(pcidev), 0x50, ®50); + } else if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG200) { + pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ®50); reg50 &= ~0x3000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), 0x50, reg50); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); if (ACCESS_FBINFO(devflags.memtype) == -1) - ACCESS_FBINFO(devflags.memtype) = 3; - hw->MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10; + hw->MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x1C00; + else + hw->MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10; if (ACCESS_FBINFO(devflags.sgram)) hw->MXoptionReg |= 0x4000; - mga_outl(M_CTLWTST, 0x042450A1); - mga_outl(M_MEMRDBK, 0x00000108); + mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst); + mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); udelay(200); mga_outl(M_MACCESS, 0x00000000); mga_outl(M_MACCESS, 0x00008000); udelay(100); - mga_outw(M_MEMRDBK, 0x00000108); + mga_outw(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); hw->MXoptionReg |= 0x00078020; } else { - pci_read_config_dword(ACCESS_FBINFO(pcidev), 0x50, ®50); + pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ®50); reg50 &= ~0x00000100; reg50 |= 0x00000000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), 0x50, reg50); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); if (ACCESS_FBINFO(devflags.memtype) == -1) ACCESS_FBINFO(devflags.memtype) = 0; @@ -820,8 +961,9 @@ return 0; } -static void MGAG100_reset(WPMINFO struct matrox_hw_state* hw){ +static void MGAG100_reset(WPMINFO2) { u_int8_t b; + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); DBG("MGAG100_reset") @@ -845,13 +987,20 @@ #endif if (!ACCESS_FBINFO(devflags.noinit)) { if (x7AF4 & 8) { - hw->MXoptionReg |= 0x40; + hw->MXoptionReg |= 0x40; /* FIXME... */ pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); } mga_setr(M_EXTVGA_INDEX, 0x06, 0x50); } } - DAC1064_setmclk(PMINFO hw, DAC1064_OPT_RESERVED | DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV1 | DAC1064_OPT_SCLK_PLL, 133333); + if (ACCESS_FBINFO(devflags.g450dac)) { + /* either leave MCLK as is... or they were set in preinit */ + hw->DACclk[3] = inDAC1064(PMINFO DAC1064_XSYSPLLM); + hw->DACclk[4] = inDAC1064(PMINFO DAC1064_XSYSPLLN); + hw->DACclk[5] = inDAC1064(PMINFO DAC1064_XSYSPLLP); + } else { + DAC1064_setmclk(PMINFO DAC1064_OPT_RESERVED | DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV1 | DAC1064_OPT_SCLK_PLL, 133333); + } if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) { if (ACCESS_FBINFO(devflags.dfp_type) == -1) { ACCESS_FBINFO(devflags.dfp_type) = inDAC1064(PMINFO 0x1F); @@ -859,20 +1008,25 @@ } if (ACCESS_FBINFO(devflags.noinit)) return; - MGAG100_setPixClock(PMINFO 4, 25175); - MGAG100_setPixClock(PMINFO 5, 28322); - if (x7AF4 & 0x10) { - b = inDAC1064(PMINFO M1064_XGENIODATA) & ~1; - outDAC1064(PMINFO M1064_XGENIODATA, b); - b = inDAC1064(PMINFO M1064_XGENIOCTRL) | 1; - outDAC1064(PMINFO M1064_XGENIOCTRL, b); + if (ACCESS_FBINFO(devflags.g450dac)) { + } else { + MGAG100_setPixClock(PMINFO 4, 25175); + MGAG100_setPixClock(PMINFO 5, 28322); + if (x7AF4 & 0x10) { + b = inDAC1064(PMINFO M1064_XGENIODATA) & ~1; + outDAC1064(PMINFO M1064_XGENIODATA, b); + b = inDAC1064(PMINFO M1064_XGENIOCTRL) | 1; + outDAC1064(PMINFO M1064_XGENIOCTRL, b); + } } } #endif #ifdef CONFIG_FB_MATROX_MYSTIQUE -static void MGA1064_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_state* oldhw, struct display* p) { +static void MGA1064_restore(WPMINFO struct display* p) { int i; + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + CRITFLAGS DBG("MGA1064_restore") @@ -885,17 +1039,19 @@ CRITEND - DAC1064_restore_1(PMINFO hw, oldhw); - matroxfb_vgaHWrestore(PMINFO hw, oldhw); + DAC1064_restore_1(PMINFO2); + matroxfb_vgaHWrestore(PMINFO2); for (i = 0; i < 6; i++) mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); - DAC1064_restore_2(PMINFO hw, oldhw, p); + DAC1064_restore_2(PMINFO p); } #endif #ifdef CONFIG_FB_MATROX_G100 -static void MGAG100_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_state* oldhw, struct display* p) { +static void MGAG100_restore(WPMINFO struct display* p) { int i; + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + CRITFLAGS DBG("MGAG100_restore") @@ -905,15 +1061,15 @@ pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); CRITEND - DAC1064_restore_1(PMINFO hw, oldhw); - matroxfb_vgaHWrestore(PMINFO hw, oldhw); + DAC1064_restore_1(PMINFO2); + matroxfb_vgaHWrestore(PMINFO2); #ifdef CONFIG_FB_MATROX_32MB if (ACCESS_FBINFO(devflags.support32MB)) mga_setr(M_EXTVGA_INDEX, 8, hw->CRTCEXT[8]); #endif for (i = 0; i < 6; i++) mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); - DAC1064_restore_2(PMINFO hw, oldhw, p); + DAC1064_restore_2(PMINFO p); } #endif diff -Nru a/drivers/video/matrox/matroxfb_DAC1064.h b/drivers/video/matrox/matroxfb_DAC1064.h --- a/drivers/video/matrox/matroxfb_DAC1064.h Tue Feb 19 18:08:59 2002 +++ b/drivers/video/matrox/matroxfb_DAC1064.h Tue Feb 19 18:08:59 2002 @@ -13,8 +13,8 @@ extern struct matrox_switch matrox_G100; #endif #ifdef NEED_DAC1064 -void DAC1064_global_init(CPMINFO struct matrox_hw_state*); -void DAC1064_global_restore(CPMINFO const struct matrox_hw_state*); +void DAC1064_global_init(WPMINFO2); +void DAC1064_global_restore(WPMINFO2); #endif #define M1064_INDEX 0x00 @@ -139,10 +139,10 @@ #define M1064_XOUTPUTCONN 0x8A #define M1064_XSYNCCTRL 0x8B -#define M1064_XPIXPLL2STAT 0x8C -#define M1064_XPIXPLL2P 0x8D -#define M1064_XPIXPLL2N 0x8E -#define M1064_XPIXPLL2M 0x8F +#define M1064_XVIDPLLSTAT 0x8C +#define M1064_XVIDPLLP 0x8D +#define M1064_XVIDPLLM 0x8E +#define M1064_XVIDPLLN 0x8F #define M1064_XPWRCTRL 0xA0 diff -Nru a/drivers/video/matrox/matroxfb_Ti3026.c b/drivers/video/matrox/matroxfb_Ti3026.c --- a/drivers/video/matrox/matroxfb_Ti3026.c Tue Feb 19 18:08:57 2002 +++ b/drivers/video/matrox/matroxfb_Ti3026.c Tue Feb 19 18:08:57 2002 @@ -4,7 +4,9 @@ * * (c) 1998,1999,2000 Petr Vandrovec * - * Version: 1.50 2000/08/10 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2000/11/29 * * MTRR stuff: 1998 Tom Rini * @@ -357,7 +359,7 @@ del_timer_sync(&ACCESS_FBINFO(cursor.timer)); matroxfb_DAC_lock_irqsave(flags); ACCESS_FBINFO(cursor.state) = CM_ERASE; - outTi3026(PMINFO TVP3026_XCURCTRL, ACCESS_FBINFO(currenthw->DACreg[POS3026_XCURCTRL])); + outTi3026(PMINFO TVP3026_XCURCTRL, ACCESS_FBINFO(hw.DACreg[POS3026_XCURCTRL])); matroxfb_DAC_unlock_irqrestore(flags); } return; @@ -377,7 +379,7 @@ ACCESS_FBINFO(cursor.y) = y; x += 64; y += 64; - outTi3026(PMINFO TVP3026_XCURCTRL, ACCESS_FBINFO(currenthw->DACreg[POS3026_XCURCTRL])); + outTi3026(PMINFO TVP3026_XCURCTRL, ACCESS_FBINFO(hw.DACreg[POS3026_XCURCTRL])); mga_outb(M_RAMDAC_BASE+TVP3026_CURPOSXL, x); mga_outb(M_RAMDAC_BASE+TVP3026_CURPOSXH, x >> 8); mga_outb(M_RAMDAC_BASE+TVP3026_CURPOSYL, y); @@ -386,7 +388,7 @@ ACCESS_FBINFO(cursor.state) = CM_DRAW; if (ACCESS_FBINFO(devflags.blink)) mod_timer(&ACCESS_FBINFO(cursor.timer), jiffies + HZ/2); - outTi3026(PMINFO TVP3026_XCURCTRL, ACCESS_FBINFO(currenthw->DACreg[POS3026_XCURCTRL]) | TVP3026_XCURCTRL_XGA); + outTi3026(PMINFO TVP3026_XCURCTRL, ACCESS_FBINFO(hw.DACreg[POS3026_XCURCTRL]) | TVP3026_XCURCTRL_XGA); matroxfb_DAC_unlock_irqrestore(flags); } @@ -418,9 +420,10 @@ return fvco; } -static int Ti3026_setpclk(CPMINFO struct matrox_hw_state* hw, int clk, struct display* p) { +static int Ti3026_setpclk(WPMINFO int clk, struct display* p) { unsigned int f_pll; unsigned int pixfeed, pixin, pixpost; + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); DBG("Ti3026_setpclk") @@ -491,8 +494,9 @@ return 0; } -static int Ti3026_init(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p) { +static int Ti3026_init(WPMINFO struct my_timming* m, struct display* p) { u_int8_t muxctrl = isInterleave(MINFO) ? TVP3026_XMUXCTRL_MEMORY_64BIT : TVP3026_XMUXCTRL_MEMORY_32BIT; + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); DBG("Ti3026_init") @@ -538,7 +542,7 @@ return 1; /* TODO: failed */ } } - if (matroxfb_vgaHWinit(PMINFO hw, m, p)) return 1; + if (matroxfb_vgaHWinit(PMINFO m, p)) return 1; /* set SYNC */ hw->MiscOutReg = 0xCB; @@ -567,11 +571,11 @@ if ((p->type != FB_TYPE_TEXT) && isInterleave(MINFO)) hw->MXoptionReg |= 0x00001000; /* set DAC */ - Ti3026_setpclk(PMINFO hw, m->pixclock, p); + Ti3026_setpclk(PMINFO m->pixclock, p); return 0; } -static void ti3026_setMCLK(CPMINFO struct matrox_hw_state* hw, int fout){ +static void ti3026_setMCLK(WPMINFO int fout){ unsigned int f_pll; unsigned int pclk_m, pclk_n, pclk_p; unsigned int mclk_m, mclk_n, mclk_p; @@ -643,8 +647,8 @@ if (rfhcnt > 15) rfhcnt = 0; } - hw->MXoptionReg = (hw->MXoptionReg & ~0x000F0000) | (rfhcnt << 16); - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + ACCESS_FBINFO(hw).MXoptionReg = (ACCESS_FBINFO(hw).MXoptionReg & ~0x000F0000) | (rfhcnt << 16); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); /* output MCLK to MCLK pin */ outTi3026(PMINFO TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); @@ -670,7 +674,7 @@ printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n"); } -static void ti3026_ramdac_init(WPMINFO struct matrox_hw_state* hw){ +static void ti3026_ramdac_init(WPMINFO2) { DBG("ti3026_ramdac_init") @@ -683,11 +687,13 @@ ACCESS_FBINFO(features.pll.post_shift_max) = 3; if (ACCESS_FBINFO(devflags.noinit)) return; - ti3026_setMCLK(PMINFO hw, 60000); + ti3026_setMCLK(PMINFO 60000); } -static void Ti3026_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_state* oldhw, struct display* p) { +static void Ti3026_restore(WPMINFO struct display* p) { int i; + unsigned char progdac[6]; + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); CRITFLAGS DBG("Ti3026_restore") @@ -705,7 +711,7 @@ CRITEND - matroxfb_vgaHWrestore(PMINFO hw, oldhw); + matroxfb_vgaHWrestore(PMINFO2); CRITBEGIN @@ -715,19 +721,19 @@ for (i = 0; i < 21; i++) { outTi3026(PMINFO DACseq[i], hw->DACreg[i]); } - if (oldhw) { - outTi3026(PMINFO TVP3026_XPLLADDR, 0x00); - oldhw->DACclk[0] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - oldhw->DACclk[3] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x15); - oldhw->DACclk[1] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - oldhw->DACclk[4] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A); - oldhw->DACclk[2] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - oldhw->DACclk[5] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); - } + + outTi3026(PMINFO TVP3026_XPLLADDR, 0x00); + progdac[0] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); + progdac[3] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); + outTi3026(PMINFO TVP3026_XPLLADDR, 0x15); + progdac[1] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); + progdac[4] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); + outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A); + progdac[2] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); + progdac[5] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); + CRITEND - if (!oldhw || memcmp(hw->DACclk, oldhw->DACclk, 6)) { + if (memcmp(hw->DACclk, progdac, 6)) { /* agrhh... setting up PLL is very slow on Millennium... */ /* Mystique PLL is locked in few ms, but Millennium PLL lock takes about 0.15 s... */ /* Maybe even we should call schedule() ? */ @@ -796,22 +802,23 @@ #endif } -static void Ti3026_reset(WPMINFO struct matrox_hw_state* hw){ +static void Ti3026_reset(WPMINFO2) { DBG("Ti3026_reset") matroxfb_fastfont_init(MINFO); - ti3026_ramdac_init(PMINFO hw); + ti3026_ramdac_init(PMINFO2); } -static int Ti3026_preinit(WPMINFO struct matrox_hw_state* hw){ +static int Ti3026_preinit(WPMINFO2) { static const int vxres_mill2[] = { 512, 640, 768, 800, 832, 960, 1024, 1152, 1280, 1600, 1664, 1920, 2048, 0}; static const int vxres_mill1[] = { 640, 768, 800, 960, 1024, 1152, 1280, 1600, 1920, 2048, 0}; + struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); DBG("Ti3026_preinit") diff -Nru a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c --- a/drivers/video/matrox/matroxfb_base.c Tue Feb 19 18:08:58 2002 +++ b/drivers/video/matrox/matroxfb_base.c Tue Feb 19 18:08:58 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.54 2001/09/09 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * MTRR stuff: 1998 Tom Rini * @@ -161,11 +163,11 @@ pos = (var->yoffset * var->xres_virtual + var->xoffset) * ACCESS_FBINFO(curr.final_bppShift) / 32; pos += ACCESS_FBINFO(curr.ydstorg.chunks); } - p0 = ACCESS_FBINFO(currenthw)->CRTC[0x0D] = pos & 0xFF; - p1 = ACCESS_FBINFO(currenthw)->CRTC[0x0C] = (pos & 0xFF00) >> 8; - p2 = ACCESS_FBINFO(currenthw)->CRTCEXT[0] = (ACCESS_FBINFO(currenthw)->CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); + p0 = ACCESS_FBINFO(hw).CRTC[0x0D] = pos & 0xFF; + p1 = ACCESS_FBINFO(hw).CRTC[0x0C] = (pos & 0xFF00) >> 8; + p2 = ACCESS_FBINFO(hw).CRTCEXT[0] = (ACCESS_FBINFO(hw).CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); #ifdef CONFIG_FB_MATROX_32MB - p3 = ACCESS_FBINFO(currenthw)->CRTCEXT[8] = pos >> 21; + p3 = ACCESS_FBINFO(hw).CRTCEXT[8] = pos >> 21; #endif CRITBEGIN @@ -814,7 +816,6 @@ { struct my_timming mt; struct matrox_hw_state* hw; - struct matrox_hw_state* ohw; matroxfb_var2my(var, &mt); /* CRTC1 delays */ @@ -826,16 +827,12 @@ default: mt.delay = 31 + 8; break; } - hw = ACCESS_FBINFO(newhw); - ohw = ACCESS_FBINFO(currenthw); - - /* copy last setting... */ - memcpy(hw, ohw, sizeof(*hw)); + hw = &ACCESS_FBINFO(hw); del_timer_sync(&ACCESS_FBINFO(cursor.timer)); ACCESS_FBINFO(cursor.state) = CM_ERASE; - ACCESS_FBINFO(hw_switch->init(PMINFO hw, &mt, display)); + ACCESS_FBINFO(hw_switch->init(PMINFO &mt, display)); if (display->type == FB_TYPE_TEXT) { if (fontheight(display)) pos = var->yoffset / fontheight(display) * display->next_line / ACCESS_FBINFO(devflags.textstep) + var->xoffset / (fontwidth(display)?fontwidth(display):8); @@ -852,28 +849,26 @@ hw->CRTCEXT[8] = pos >> 21; if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) { if (ACCESS_FBINFO(primout)) - ACCESS_FBINFO(primout)->compute(MINFO, &mt, hw); + ACCESS_FBINFO(primout)->compute(MINFO, &mt); } if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) { down_read(&ACCESS_FBINFO(altout.lock)); if (ACCESS_FBINFO(altout.output)) - ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt, hw); + ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt); up_read(&ACCESS_FBINFO(altout.lock)); } - ACCESS_FBINFO(hw_switch->restore(PMINFO hw, ohw, display)); + ACCESS_FBINFO(hw_switch->restore(PMINFO display)); if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) { if (ACCESS_FBINFO(primout)) - ACCESS_FBINFO(primout)->program(MINFO, hw); + ACCESS_FBINFO(primout)->program(MINFO); } if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) { down_read(&ACCESS_FBINFO(altout.lock)); if (ACCESS_FBINFO(altout.output)) - ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device), hw); + ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device)); up_read(&ACCESS_FBINFO(altout.lock)); } ACCESS_FBINFO(cursor.redraw) = 1; - ACCESS_FBINFO(currenthw) = hw; - ACCESS_FBINFO(newhw) = ohw; if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) { if (ACCESS_FBINFO(primout)) ACCESS_FBINFO(primout)->start(MINFO); @@ -1004,7 +999,7 @@ vblank->flags |= FB_VBLANK_HBLANKING; if (sts1 & 8) vblank->flags |= FB_VBLANK_VSYNCING; - if (vblank->count >= ACCESS_FBINFO(currcon_display)->var.yres) + if (vblank->vcount >= ACCESS_FBINFO(currcon_display)->var.yres) vblank->flags |= FB_VBLANK_VBLANKING; vblank->hcount = 0; vblank->count = 0; @@ -1416,7 +1411,7 @@ #define DEVF_CROSS4MB 0x0010 #define DEVF_TEXT4B 0x0020 #define DEVF_DDC_8_2 0x0040 -#define DEVF_G550DAC 0x0080 +/* #define DEVF_recycled 0x0080 */ #define DEVF_SUPPORT32MB 0x0100 #define DEVF_ANY_VXRES 0x0200 #define DEVF_TEXT16B 0x0400 @@ -1432,12 +1427,13 @@ #define DEVF_G400 (DEVF_G2CORE | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2) /* if you'll find how to drive DFP... */ #define DEVF_G450 (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2 | DEVF_G450DAC | DEVF_SRCORG) -#define DEVF_G550 (DEVF_G450 | DEVF_G550DAC | DEVF_BOTHDACS) +#define DEVF_G550 (DEVF_G450 | DEVF_BOTHDACS) static struct board { unsigned short vendor, device, rev, svid, sid; unsigned int flags; unsigned int maxclk; + enum mga_chip chip; struct video_board* base; const char* name; } dev_list[] = { @@ -1446,18 +1442,21 @@ 0, 0, DEVF_TEXT4B, 230000, + MGA_2064, &vbMillennium, "Millennium (PCI)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2, 0xFF, 0, 0, DEVF_SWAPS, 220000, + MGA_2164, &vbMillennium2, "Millennium II (PCI)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP, 0xFF, 0, 0, DEVF_SWAPS, 250000, + MGA_2164, &vbMillennium2A, "Millennium II (AGP)"}, #endif @@ -1466,122 +1465,107 @@ 0, 0, DEVF_VIDEO64BIT | DEVF_CROSS4MB, 180000, + MGA_1064, &vbMystique, "Mystique (PCI)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS, 0xFF, 0, 0, DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB, 220000, + MGA_1164, &vbMystique, "Mystique 220 (PCI)"}, #endif #ifdef CONFIG_FB_MATROX_G100 {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM, 0xFF, - PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MGA_G100_PCI, - DEVF_G100, - 230000, - &vbG100, - "MGA-G100 (PCI)"}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM, 0xFF, 0, 0, DEVF_G100, 230000, + MGA_G100, &vbG100, - "unknown G100 (PCI)"}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, - PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_GENERIC, - DEVF_G100, - 230000, - &vbG100, - "MGA-G100 (AGP)"}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, - PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MGA_G100_AGP, - DEVF_G100, - 230000, - &vbG100, - "MGA-G100 (AGP)"}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, - PCI_SS_VENDOR_ID_SIEMENS_NIXDORF, PCI_SS_ID_SIEMENS_MGA_G100_AGP, - DEVF_G100, - 230000, - &vbG100, - "MGA-G100 (AGP)"}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, - PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_PRODUCTIVA_G100_AGP, - DEVF_G100, - 230000, - &vbG100, - "Productiva G100 (AGP)"}, + "MGA-G100 (PCI)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, 0, 0, DEVF_G100, 230000, + MGA_G100, &vbG100, - "unknown G100 (AGP)"}, + "MGA-G100 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_PCI, 0xFF, 0, 0, DEVF_G200, 250000, + MGA_G200, &vbG200, - "unknown G200 (PCI)"}, + "MGA-G200 (PCI)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_GENERIC, DEVF_G200, 220000, + MGA_G200, &vbG200, "MGA-G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MYSTIQUE_G200_AGP, DEVF_G200, 230000, + MGA_G200, &vbG200, "Mystique G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MILLENIUM_G200_AGP, DEVF_G200, 250000, + MGA_G200, &vbG200, "Millennium G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MARVEL_G200_AGP, DEVF_G200, 230000, + MGA_G200, &vbG200, "Marvel G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, PCI_SS_VENDOR_ID_SIEMENS_NIXDORF, PCI_SS_ID_SIEMENS_MGA_G200_AGP, DEVF_G200, 230000, + MGA_G200, &vbG200, "MGA-G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, 0, 0, DEVF_G200, 230000, + MGA_G200, &vbG200, "G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0x80, PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MILLENNIUM_G400_MAX_AGP, DEVF_G400, 360000, + MGA_G400, &vbG400, "Millennium G400 MAX (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0x80, 0, 0, DEVF_G400, 300000, + MGA_G400, &vbG400, "G400 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0xFF, 0, 0, DEVF_G450, - 500000, /* ??? vco goes up to 900MHz... */ + 360000, + MGA_G450, &vbG400, "G450"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G550, 0xFF, 0, 0, DEVF_G550, - 500000, + 360000, + MGA_G550, &vbG400, "G550"}, #endif @@ -1589,6 +1573,7 @@ 0, 0, 0, 0, + 0, NULL, NULL}}; @@ -1606,7 +1591,6 @@ unsigned long ctrlptr_phys = 0; unsigned long video_base_phys = 0; unsigned int memsize; - struct matrox_hw_state* hw = ACCESS_FBINFO(currenthw); int err; DBG("initMatrox2") @@ -1620,6 +1604,7 @@ printk(KERN_INFO "matroxfb: Matrox %s detected\n", b->name); ACCESS_FBINFO(capable.plnwt) = 1; + ACCESS_FBINFO(chip) = b->chip; ACCESS_FBINFO(capable.srcorg) = b->flags & DEVF_SRCORG; ACCESS_FBINFO(devflags.video64bits) = b->flags & DEVF_VIDEO64BIT; if (b->flags & DEVF_TEXT4B) { @@ -1657,7 +1642,6 @@ } ACCESS_FBINFO(devflags.dfp_type) = dfp_type; ACCESS_FBINFO(devflags.g450dac) = b->flags & DEVF_G450DAC; - ACCESS_FBINFO(devflags.g550dac) = b->flags & DEVF_G550DAC; ACCESS_FBINFO(devflags.textstep) = ACCESS_FBINFO(devflags.vgastep) * ACCESS_FBINFO(devflags.textmode); ACCESS_FBINFO(devflags.textvram) = 65536 / ACCESS_FBINFO(devflags.textmode); @@ -1666,9 +1650,11 @@ if (b->flags & DEVF_SWAPS) { ctrlptr_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 1); video_base_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 0); + ACCESS_FBINFO(devflags.fbResource) = PCI_BASE_ADDRESS_0; } else { ctrlptr_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 0); video_base_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 1); + ACCESS_FBINFO(devflags.fbResource) = PCI_BASE_ADDRESS_1; } err = -EINVAL; if (!ctrlptr_phys) { @@ -1725,7 +1711,7 @@ } pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_COMMAND, cmd); pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mga_option); - hw->MXoptionReg = mga_option; + ACCESS_FBINFO(hw).MXoptionReg = mga_option; /* select non-DMA memory for PCI_MGA_DATA, otherwise dump of PCI cfg space can lock PCI bus */ /* maybe preinit() candidate, but it is same... for all devices... at this time... */ @@ -1733,7 +1719,8 @@ } err = -ENXIO; - if (ACCESS_FBINFO(hw_switch)->preinit(PMINFO hw)) { + matroxfb_read_pins(PMINFO2); + if (ACCESS_FBINFO(hw_switch)->preinit(PMINFO2)) { goto failVideoIO; } @@ -1746,13 +1733,7 @@ ACCESS_FBINFO(currcon) = -1; ACCESS_FBINFO(currcon_display) = d; - mga_iounmap(ACCESS_FBINFO(video.vbase)); ACCESS_FBINFO(video.base) = video_base_phys; - if (mga_ioremap(video_base_phys, ACCESS_FBINFO(video.len), MGA_IOREMAP_FB, &ACCESS_FBINFO(video.vbase))) { - printk(KERN_ERR "matroxfb: cannot ioremap(%lX, %d), matroxfb disabled\n", - video_base_phys, ACCESS_FBINFO(video.len)); - goto failCtrlIO; - } ACCESS_FBINFO(video.len_usable) = ACCESS_FBINFO(video.len); if (ACCESS_FBINFO(video.len_usable) > b->base->maxdisplayable) ACCESS_FBINFO(video.len_usable) = b->base->maxdisplayable; @@ -1766,7 +1747,7 @@ if (!ACCESS_FBINFO(devflags.novga)) request_region(0x3C0, 32, "matrox"); - ACCESS_FBINFO(hw_switch->reset(PMINFO hw)); + ACCESS_FBINFO(hw_switch->reset(PMINFO2)); ACCESS_FBINFO(fbcon.monspecs.hfmin) = 0; ACCESS_FBINFO(fbcon.monspecs.hfmax) = fh; @@ -1789,7 +1770,7 @@ strcpy(ACCESS_FBINFO(fbcon.modename), "MATROX VGA"); ACCESS_FBINFO(fbcon.changevar) = NULL; - ACCESS_FBINFO(fbcon.node) = -1; + ACCESS_FBINFO(fbcon.node) = NODEV; ACCESS_FBINFO(fbcon.fbops) = &matroxfb_ops; ACCESS_FBINFO(fbcon.disp) = d; ACCESS_FBINFO(fbcon.switch_con) = &matroxfb_switch; @@ -1848,9 +1829,9 @@ + vesafb_defined.right_margin + vesafb_defined.hsync_len); if ((tmp < maxclk) || (maxclk == 0)) maxclk = tmp; } - maxclk = (maxclk + 499) / 500; - if (maxclk) { - tmp = (2000000000 + maxclk) / maxclk; + tmp = (maxclk + 499) / 500; + if (tmp) { + tmp = (2000000000 + tmp) / tmp; if (tmp > pixclock) pixclock = tmp; } } @@ -2070,8 +2051,6 @@ memset(MINFO, 0, sizeof(*MINFO)); memset(d, 0, sizeof(*d)); - ACCESS_FBINFO(currenthw) = &ACCESS_FBINFO(hw1); - ACCESS_FBINFO(newhw) = &ACCESS_FBINFO(hw2); ACCESS_FBINFO(pcidev) = pdev; ACCESS_FBINFO(dead) = 0; ACCESS_FBINFO(usecount) = 0; diff -Nru a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h --- a/drivers/video/matrox/matroxfb_base.h Tue Feb 19 18:09:00 2002 +++ b/drivers/video/matrox/matroxfb_base.h Tue Feb 19 18:09:00 2002 @@ -162,7 +162,7 @@ #define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) -/* G100, G200 and Mystique have (almost) same DAC */ +/* G-series and Mystique have (almost) same DAC */ #undef NEED_DAC1064 #if defined(CONFIG_FB_MATROX_MYSTIQUE) || defined(CONFIG_FB_MATROX_G100) #define NEED_DAC1064 1 @@ -305,6 +305,21 @@ unsigned int delay; /* CRTC delay */ }; +enum { M_SYSTEM_PLL, M_PIXEL_PLL_A, M_PIXEL_PLL_B, M_PIXEL_PLL_C, M_VIDEO_PLL }; + +struct matrox_pll_cache { + unsigned int valid; + struct { + unsigned int mnp_key; + unsigned int mnp_value; + } data[4]; +}; + +struct matrox_pll_limits { + unsigned int vcomin; + unsigned int vcomax; +}; + struct matrox_pll_features { unsigned int vco_freq_min; unsigned int ref_freq; @@ -371,6 +386,8 @@ /* CRTC2 only */ /* u_int32_t TBD */ + + unsigned int vidclk; }; struct matrox_accel_data { @@ -382,8 +399,8 @@ }; struct matrox_altout { - int (*compute)(void* altout_dev, struct my_timming* input, struct matrox_hw_state* state); - int (*program)(void* altout_dev, const struct matrox_hw_state* state); + int (*compute)(void* altout_dev, struct my_timming* input); + int (*program)(void* altout_dev); int (*start)(void* altout_dev); void (*incuse)(void* altout_dev); void (*decuse)(void* altout_dev); @@ -391,6 +408,20 @@ int (*getmode)(void* altout_dev, u_int32_t* mode); }; +enum mga_chip { MGA_2064, MGA_2164, MGA_1064, MGA_1164, MGA_G100, MGA_G200, MGA_G400, MGA_G450, MGA_G550 }; + +struct matrox_bios { + unsigned int bios_valid : 1; + unsigned int pins_len; + unsigned char pins[128]; + struct { + unsigned char vMaj, vMin, vRev; + } version; + struct { + unsigned char state, tvout; + } output; +}; + struct matrox_switch; struct matroxfb_driver; @@ -404,10 +435,7 @@ unsigned int usecount; struct matroxfb_par curr; - struct matrox_hw_state hw1; - struct matrox_hw_state hw2; - struct matrox_hw_state* newhw; - struct matrox_hw_state* currenthw; + struct matrox_hw_state hw; struct matrox_accel_data accel; @@ -464,6 +492,8 @@ spinlock_t accel; } lock; + enum mga_chip chip; + int interleave; int millenium; int milleniumII; @@ -514,8 +544,8 @@ /* 0 except for 6MB Millenium */ int memtype; int g450dac; - int g550dac; int dfp_type; + unsigned int fbResource; } devflags; struct display_switch dispsw; struct { @@ -529,6 +559,38 @@ int redraw; struct timer_list timer; } cursor; + struct matrox_bios bios; + struct { + struct matrox_pll_limits pixel; + struct matrox_pll_limits system; + struct matrox_pll_limits video; + } limits; + struct { + struct matrox_pll_cache pixel; + struct matrox_pll_cache system; + struct matrox_pll_cache video; + } cache; + struct { + struct { + unsigned int video; + unsigned int system; + } pll; + struct { + u_int32_t opt; + u_int32_t opt2; + u_int32_t opt3; + u_int32_t mctlwtst; + u_int32_t mctlwtst_core; + u_int32_t memmisc; + u_int32_t memrdbk; + u_int32_t maccess; + } reg; + struct { + unsigned int ddr:1, + emrswen:1, + dll:1; + } memory; + } values; struct { unsigned red, green, blue, transp; } palette[256]; #if defined(CONFIG_FB_COMPAT_XPMAC) char matrox_name[32]; @@ -599,10 +661,10 @@ #endif struct matrox_switch { - int (*preinit)(WPMINFO struct matrox_hw_state*); - void (*reset)(WPMINFO struct matrox_hw_state*); - int (*init)(CPMINFO struct matrox_hw_state*, struct my_timming*, struct display*); - void (*restore)(WPMINFO struct matrox_hw_state*, struct matrox_hw_state*, struct display*); + int (*preinit)(WPMINFO2); + void (*reset)(WPMINFO2); + int (*init)(WPMINFO struct my_timming*, struct display*); + void (*restore)(WPMINFO struct display*); int (*selhwcursor)(WPMINFO struct display*); }; @@ -617,8 +679,13 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv); #define PCI_OPTION_REG 0x40 +#define PCI_OPTION_ENABLE_ROM 0x40000000 + #define PCI_MGA_INDEX 0x44 #define PCI_MGA_DATA 0x48 +#define PCI_OPTION2_REG 0x50 +#define PCI_OPTION3_REG 0x54 +#define PCI_MEMMISC_REG 0x58 #define M_DWGCTL 0x1C00 #define M_MACCESS 0x1C04 @@ -737,6 +804,8 @@ #define DAC_XGENIOCTRL 0x2A #define DAC_XGENIODATA 0x2B +#define M_C2CTL 0x3E10 + #ifdef __LITTLE_ENDIAN #define MX_OPTION_BSWAP 0x00000000 @@ -794,6 +863,7 @@ extern int matroxfb_DAC_in(CPMINFO int reg); extern struct list_head matroxfb_list; extern void matroxfb_var2my(struct fb_var_screeninfo* fvsi, struct my_timming* mt); +extern int matroxfb_switch(int con, struct fb_info *); #ifdef MATROXFB_USE_SPINLOCKS #define CRITBEGIN spin_lock_irqsave(&ACCESS_FBINFO(lock.accel), critflags); diff -Nru a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c --- a/drivers/video/matrox/matroxfb_crtc2.c Tue Feb 19 18:08:58 2002 +++ b/drivers/video/matrox/matroxfb_crtc2.c Tue Feb 19 18:08:58 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.52 2001/05/25 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * */ @@ -402,43 +404,35 @@ info->changevar(con); if (con == m2info->currcon) { struct my_timming mt; - struct matrox_hw_state* hw; - struct matrox_hw_state* ohw; unsigned int pos; matroxfb_var2my(var, &mt); /* CRTC2 delay */ mt.delay = 34; - hw = ACCESS_FBINFO(newhw); - ohw = ACCESS_FBINFO(currenthw); - - /* copy last setting... */ - memcpy(hw, ohw, sizeof(*hw)); - pos = (var->yoffset * var->xres_virtual + var->xoffset) * var->bits_per_pixel >> 3; pos += m2info->video.offbase; - DAC1064_global_init(PMINFO hw); + DAC1064_global_init(PMINFO2); if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) { if (ACCESS_FBINFO(primout)) - ACCESS_FBINFO(primout)->compute(MINFO, &mt, hw); + ACCESS_FBINFO(primout)->compute(MINFO, &mt); } if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) { down_read(&ACCESS_FBINFO(altout.lock)); if (ACCESS_FBINFO(altout.output)) - ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt, hw); + ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt); up_read(&ACCESS_FBINFO(altout.lock)); } matroxfb_dh_restore(m2info, &mt, p, mode, pos); - DAC1064_global_restore(PMINFO hw); + DAC1064_global_restore(PMINFO2); if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) { if (ACCESS_FBINFO(primout)) - ACCESS_FBINFO(primout)->program(MINFO, hw); + ACCESS_FBINFO(primout)->program(MINFO); } if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) { down_read(&ACCESS_FBINFO(altout.lock)); if (ACCESS_FBINFO(altout.output)) - ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device), hw); + ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device)); up_read(&ACCESS_FBINFO(altout.lock)); } if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) { @@ -693,7 +687,7 @@ strcpy(m2info->fbcon.modename, "MATROX CRTC2"); m2info->fbcon.changevar = NULL; - m2info->fbcon.node = -1; + m2info->fbcon.node = NODEV; m2info->fbcon.fbops = &matroxfb_dh_ops; m2info->fbcon.disp = d; m2info->fbcon.switch_con = &matroxfb_dh_switch; diff -Nru a/drivers/video/matrox/matroxfb_g450.c b/drivers/video/matrox/matroxfb_g450.c --- a/drivers/video/matrox/matroxfb_g450.c Tue Feb 19 18:08:58 2002 +++ b/drivers/video/matrox/matroxfb_g450.c Tue Feb 19 18:08:58 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.51 2001/01/19 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * See matroxfb_base.c for contributors. * @@ -13,119 +15,28 @@ #include "matroxfb_g450.h" #include "matroxfb_misc.h" #include "matroxfb_DAC1064.h" +#include "g450_pll.h" #include #include -static int matroxfb_g450_get_reg(WPMINFO int reg) { - int val; - unsigned long flags; - - matroxfb_DAC_lock_irqsave(flags); - val = matroxfb_DAC_in(PMINFO reg); - matroxfb_DAC_unlock_irqrestore(flags); - return val; -} - -static int matroxfb_g450_set_reg(WPMINFO int reg, int val) { - unsigned long flags; - - matroxfb_DAC_lock_irqsave(flags); - matroxfb_DAC_out(PMINFO reg, val); - matroxfb_DAC_unlock_irqrestore(flags); +static int matroxfb_g450_compute(void* md, struct my_timming* mt) { +#define m2info ((struct matroxfb_g450_info*)md) +#define minfo (m2info->primary_dev) + ACCESS_FBINFO(hw).vidclk = mt->pixclock; +#undef minfo +#undef m2info return 0; } -static const struct matrox_pll_features maven_pll = { - 110000, - 27000, - 4, 127, - 2, 31, - 3 -}; - -static const struct matrox_pll_features g550_pll = { - 135000, - 27000, - 4, 127, - 0, 9, - 3 -}; - -static void DAC1064_calcclock(unsigned int freq, unsigned int fmax, - unsigned int* in, unsigned int* feed, unsigned int* post, - unsigned int timmings) { - unsigned int fvco; - unsigned int p; - - switch (timmings) { - default: - fvco = matroxfb_PLL_calcclock(&maven_pll, freq, fmax, in, feed, &p); - /* 0 => 100 ... 275 MHz - 1 => 243 ... 367 MHz - 2 => 320 ... 475 MHz - 3 => 453 ... 556 MHz - 4 => 540 ... 594 MHz - 5 => 588 ... 621 MHz - 6 => 626 ... 637 MHz - 7 => 631 ... 642 MHz - - As you can see, never choose frequency > 621 MHz, there is unavailable gap... - Just to be sure, currently driver uses 110 ... 500 MHz range. - */ - if (fvco <= 260000) - ; - else if (fvco <= 350000) - p |= 0x08; - else if (fvco <= 460000) - p |= 0x10; - else if (fvco <= 550000) - p |= 0x18; - else if (fvco <= 590000) - p |= 0x20; - else - p |= 0x28; - break; - case 1: - fvco = matroxfb_PLL_calcclock(&g550_pll, freq, fmax, in, feed, &p); - /* p |= 0x00; */ - break; - } - *post = p; - return; -} - -static inline int matroxfb_g450_compute_timming(struct matroxfb_g450_info* m2info, - struct my_timming* mt, - struct mavenregs* m) { - unsigned int a, b, c; - - DAC1064_calcclock(mt->pixclock, 300000, &a, &b, &c, m2info->timmings); - m->regs[0x80] = a; - m->regs[0x81] = b; - m->regs[0x82] = c; - printk(KERN_DEBUG "PLL: %02X %02X %02X\n", a, b, c); +static int matroxfb_g450_program(void* md) { +#define m2info ((struct matroxfb_g450_info*)md) +#define minfo (m2info->primary_dev) + matroxfb_g450_setclk(PMINFO ACCESS_FBINFO(hw).vidclk, M_VIDEO_PLL); +#undef minfo +#undef m2info return 0; } -static inline int matroxfb_g450_program_timming(struct matroxfb_g450_info* m2info, const struct mavenregs* m) { - MINFO_FROM(m2info->primary_dev); - - matroxfb_g450_set_reg(PMINFO M1064_XPIXPLL2M, m->regs[0x81]); - matroxfb_g450_set_reg(PMINFO M1064_XPIXPLL2N, m->regs[0x80]); - matroxfb_g450_set_reg(PMINFO M1064_XPIXPLL2P, m->regs[0x82]); - return 0; -} - -/******************************************************/ - -static int matroxfb_g450_compute(void* md, struct my_timming* mt, struct matrox_hw_state* mr) { - return matroxfb_g450_compute_timming(md, mt, &mr->maven); -} - -static int matroxfb_g450_program(void* md, const struct matrox_hw_state* mr) { - return matroxfb_g450_program_timming(md, &mr->maven); -} - static int matroxfb_g450_start(void* md) { return 0; } @@ -191,7 +102,7 @@ static void* matroxfb_g450_probe(struct matrox_fb_info* minfo) { struct matroxfb_g450_info* m2info; - /* hardware is not G450 incapable... */ + /* hardware is not G450... */ if (!ACCESS_FBINFO(devflags.g450dac)) return NULL; m2info = (struct matroxfb_g450_info*)kmalloc(sizeof(*m2info), GFP_KERNEL); @@ -201,11 +112,6 @@ } memset(m2info, 0, sizeof(*m2info)); m2info->primary_dev = MINFO; - if (ACCESS_FBINFO(devflags.g550dac)) { - m2info->timmings = 1; - } else { - m2info->timmings = 0; - } if (matroxfb_g450_connect(m2info)) { kfree(m2info); printk(KERN_ERR "matroxfb_g450: G450 DAC failed to initialize\n"); diff -Nru a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c --- a/drivers/video/matrox/matroxfb_maven.c Tue Feb 19 18:08:57 2002 +++ b/drivers/video/matrox/matroxfb_maven.c Tue Feb 19 18:08:57 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.51 2001/01/19 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * See matroxfb_base.c for contributors. * @@ -25,10 +27,14 @@ #define MODE_TV(x) (((x) == MODE_PAL) || ((x) == MODE_NTSC)) #define MODE_MONITOR MATROXFB_OUTPUT_MODE_MONITOR +#define MGATVO_B 1 +#define MGATVO_C 2 + struct maven_data { struct matrox_fb_info* primary_head; struct i2c_client* client; int mode; + int version; }; static int maven_get_reg(struct i2c_client* c, char reg) { @@ -623,8 +629,13 @@ m->regs[0xA2] = mt->VTotal - mt->VSyncStart - 1; /* stop vblanking */ m->regs[0xA3] = (mt->VTotal - mt->VSyncStart - 1) >> 8; /* something end... [A6]+1..[A8] */ - m->regs[0xA4] = 0x01; - m->regs[0xA5] = 0x00; + if (md->version == MGATVO_B) { + m->regs[0xA4] = 0x04; + m->regs[0xA5] = 0x00; + } else { + m->regs[0xA4] = 0x01; + m->regs[0xA5] = 0x00; + } /* something start... 0..[A4]-1 */ m->regs[0xA6] = 0x00; m->regs[0xA7] = 0x00; @@ -862,12 +873,20 @@ /******************************************************/ -static int maven_out_compute(void* md, struct my_timming* mt, struct matrox_hw_state* mr) { - return maven_compute_timming(md, mt, &mr->maven); +static int maven_out_compute(void* md, struct my_timming* mt) { +#define mdinfo ((struct maven_data*)md) +#define minfo (mdinfo->primary_head) + return maven_compute_timming(md, mt, &ACCESS_FBINFO(hw).maven); +#undef minfo +#undef mdinfo } -static int maven_out_program(void* md, const struct matrox_hw_state* mr) { - return maven_program_timming(md, &mr->maven); +static int maven_out_program(void* md) { +#define mdinfo ((struct maven_data*)md) +#define minfo (mdinfo->primary_head) + return maven_program_timming(md, &ACCESS_FBINFO(hw).maven); +#undef minfo +#undef mdinfo } static int maven_out_start(void* md) { @@ -916,6 +935,11 @@ ACCESS_FBINFO(altout.output) = &maven_altout; up_write(&ACCESS_FBINFO(altout.lock)); ACCESS_FBINFO(output.all) |= MATROXFB_OUTPUT_CONN_SECONDARY; + if (maven_get_reg(clnt, 0xB2) < 0x14) { + md->version = MGATVO_B; + } else { + md->version = MGATVO_C; + } return 0; } diff -Nru a/drivers/video/matrox/matroxfb_misc.c b/drivers/video/matrox/matroxfb_misc.c --- a/drivers/video/matrox/matroxfb_misc.c Tue Feb 19 18:08:57 2002 +++ b/drivers/video/matrox/matroxfb_misc.c Tue Feb 19 18:08:57 2002 @@ -2,9 +2,11 @@ * * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 * - * (c) 1998,1999,2000 Petr Vandrovec + * (c) 1998,1999,2000,2001 Petr Vandrovec * - * Version: 1.54 2001/09/09 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * MTRR stuff: 1998 Tom Rini * @@ -218,7 +220,7 @@ return bestvco; } -int matroxfb_vgaHWinit(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p) { +int matroxfb_vgaHWinit(WPMINFO struct my_timming* m, struct display* p) { unsigned int hd, hs, he, hbe, ht; unsigned int vd, vs, ve, vt; unsigned int wd; @@ -226,6 +228,7 @@ int i; int text = p->type == FB_TYPE_TEXT; int fwidth; + struct matrox_hw_state * const hw = &ACCESS_FBINFO(hw); if (text) { fwidth = fontwidth(p); @@ -416,8 +419,9 @@ return 0; }; -void matroxfb_vgaHWrestore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_state* oldhw) { +void matroxfb_vgaHWrestore(WPMINFO2) { int i; + struct matrox_hw_state * const hw = &ACCESS_FBINFO(hw); CRITFLAGS DBG("vgaHWrestore") @@ -643,6 +647,354 @@ return 1; } +static void get_pins(unsigned char* pins, struct matrox_bios* bd) { + unsigned int b0 = readb(pins); + + if (b0 == 0x2E && readb(pins+1) == 0x41) { + unsigned int pins_len = readb(pins+2); + unsigned int i; + unsigned char cksum; + unsigned char* dst = bd->pins; + + if (pins_len < 3 || pins_len > 128) { + return; + } + *dst++ = 0x2E; + *dst++ = 0x41; + *dst++ = pins_len; + cksum = 0x2E + 0x41 + pins_len; + for (i = 3; i < pins_len; i++) { + cksum += *dst++ = readb(pins+i); + } + if (cksum) { + return; + } + bd->pins_len = pins_len; + } else if (b0 == 0x40 && readb(pins+1) == 0x00) { + unsigned int i; + unsigned char* dst = bd->pins; + + *dst++ = 0x40; + *dst++ = 0; + for (i = 2; i < 0x40; i++) { + *dst++ = readb(pins+i); + } + bd->pins_len = 0x40; + } +} + +static void get_bios_version(unsigned char* vbios, struct matrox_bios* bd) { + unsigned int pcir_offset; + + pcir_offset = readb(vbios + 24) | (readb(vbios + 25) << 8); + if (pcir_offset >= 26 && pcir_offset < 0xFFE0 && + readb(vbios + pcir_offset ) == 'P' && + readb(vbios + pcir_offset + 1) == 'C' && + readb(vbios + pcir_offset + 2) == 'I' && + readb(vbios + pcir_offset + 3) == 'R') { + unsigned char h; + + h = readb(vbios + pcir_offset + 0x12); + bd->version.vMaj = (h >> 4) & 0xF; + bd->version.vMin = h & 0xF; + bd->version.vRev = readb(vbios + pcir_offset + 0x13); + } else { + unsigned char h; + + h = readb(vbios + 5); + bd->version.vMaj = (h >> 4) & 0xF; + bd->version.vMin = h & 0xF; + bd->version.vRev = 0; + } +} + +static void get_bios_output(unsigned char* vbios, struct matrox_bios* bd) { + unsigned char b; + + b = readb(vbios + 0x7FF1); + if (b == 0xFF) { + b = 0; + } + bd->output.state = b; +} + +static void get_bios_tvout(unsigned char* vbios, struct matrox_bios* bd) { + unsigned int i; + + /* Check for 'IBM .*(V....TVO' string - it means TVO BIOS */ + bd->output.tvout = 0; + if (readb(vbios + 0x1D) != 'I' || + readb(vbios + 0x1E) != 'B' || + readb(vbios + 0x1F) != 'M' || + readb(vbios + 0x20) != ' ') { + return; + } + for (i = 0x2D; i < 0x2D + 128; i++) { + unsigned char b = readb(vbios + i); + + if (b == '(' && readb(vbios + i + 1) == 'V') { + if (readb(vbios + i + 6) == 'T' && + readb(vbios + i + 7) == 'V' && + readb(vbios + i + 8) == 'O') { + bd->output.tvout = 1; + } + return; + } + if (b == 0) + break; + } +} + +static void parse_bios(unsigned char* vbios, struct matrox_bios* bd) { + unsigned int pins_offset; + + if (readb(vbios) != 0x55 || readb(vbios + 1) != 0xAA) { + return; + } + bd->bios_valid = 1; + get_bios_version(vbios, bd); + get_bios_output(vbios, bd); + get_bios_tvout(vbios, bd); + pins_offset = readb(vbios + 0x7FFC) | (readb(vbios + 0x7FFD) << 8); + if (pins_offset <= 0xFF80) { + get_pins(vbios + pins_offset, bd); + } +} + +#define get_u16(x) (le16_to_cpu(get_unaligned((__u16*)(x)))) +#define get_u32(x) (le32_to_cpu(get_unaligned((__u32*)(x)))) +static int parse_pins1(WPMINFO const struct matrox_bios* bd) { + unsigned int maxdac; + + switch (bd->pins[22]) { + case 0: maxdac = 175000; break; + case 1: maxdac = 220000; break; + default: maxdac = 240000; break; + } + if (get_u16(bd->pins + 24)) { + maxdac = get_u16(bd->pins + 24) * 10; + } + MINFO->limits.pixel.vcomax = maxdac; + MINFO->values.pll.system = get_u16(bd->pins + 28) ? get_u16(bd->pins + 28) * 10 : 50000; + /* ignore 4MB, 8MB, module clocks */ + MINFO->features.pll.ref_freq = 14318; + MINFO->values.reg.mctlwtst = 0x00030101; + return 0; +} + +static void default_pins1(WPMINFO2) { + /* Millennium */ + MINFO->limits.pixel.vcomax = 220000; + MINFO->values.pll.system = 50000; + MINFO->features.pll.ref_freq = 14318; + MINFO->values.reg.mctlwtst = 0x00030101; +} + +static int parse_pins2(WPMINFO const struct matrox_bios* bd) { + MINFO->limits.pixel.vcomax = + MINFO->limits.system.vcomax = (bd->pins[41] == 0xFF) ? 230000 : ((bd->pins[41] + 100) * 1000); + MINFO->values.reg.mctlwtst = ((bd->pins[51] & 0x01) ? 0x00000001 : 0) | + ((bd->pins[51] & 0x02) ? 0x00000100 : 0) | + ((bd->pins[51] & 0x04) ? 0x00010000 : 0) | + ((bd->pins[51] & 0x08) ? 0x00020000 : 0); + MINFO->values.pll.system = (bd->pins[43] == 0xFF) ? 50000 : ((bd->pins[43] + 100) * 1000); + MINFO->features.pll.ref_freq = 14318; + return 0; +} + +static void default_pins2(WPMINFO2) { + /* Millennium II, Mystique */ + MINFO->limits.pixel.vcomax = + MINFO->limits.system.vcomax = 230000; + MINFO->values.reg.mctlwtst = 0x00030101; + MINFO->values.pll.system = 50000; + MINFO->features.pll.ref_freq = 14318; +} + +static int parse_pins3(WPMINFO const struct matrox_bios* bd) { + MINFO->limits.pixel.vcomax = + MINFO->limits.system.vcomax = (bd->pins[36] == 0xFF) ? 230000 : ((bd->pins[36] + 100) * 1000); + MINFO->values.reg.mctlwtst = get_u32(bd->pins + 48) == 0xFFFFFFFF ? 0x01250A21 : get_u32(bd->pins + 48); + /* memory config */ + MINFO->values.reg.memrdbk = ((bd->pins[57] << 21) & 0x1E000000) | + ((bd->pins[57] << 22) & 0x00C00000) | + ((bd->pins[56] << 1) & 0x000001E0) | + ( bd->pins[56] & 0x0000000F); + MINFO->values.reg.opt = (bd->pins[54] & 7) << 10; + MINFO->values.reg.opt2 = bd->pins[58] << 12; + MINFO->features.pll.ref_freq = (bd->pins[52] & 0x20) ? 14318 : 27000; + return 0; +} + +static void default_pins3(WPMINFO2) { + /* G100, G200 */ + MINFO->limits.pixel.vcomax = + MINFO->limits.system.vcomax = 230000; + MINFO->values.reg.mctlwtst = 0x01250A21; + MINFO->values.reg.memrdbk = 0x00000000; + MINFO->values.reg.opt = 0x00000C00; + MINFO->values.reg.opt2 = 0x00000000; + MINFO->features.pll.ref_freq = 27000; +} + +static int parse_pins4(WPMINFO const struct matrox_bios* bd) { + MINFO->limits.pixel.vcomax = (bd->pins[ 39] == 0xFF) ? 230000 : bd->pins[ 39] * 4000; + MINFO->limits.system.vcomax = (bd->pins[ 38] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 38] * 4000; + MINFO->values.reg.mctlwtst = get_u32(bd->pins + 71); + MINFO->values.reg.memrdbk = ((bd->pins[87] << 21) & 0x1E000000) | + ((bd->pins[87] << 22) & 0x00C00000) | + ((bd->pins[86] << 1) & 0x000001E0) | + ( bd->pins[86] & 0x0000000F); + MINFO->values.reg.opt = ((bd->pins[53] << 15) & 0x00400000) | + ((bd->pins[53] << 22) & 0x10000000) | + ((bd->pins[53] << 10) & 0x00001C00); + MINFO->values.reg.opt3 = get_u32(bd->pins + 67); + MINFO->values.pll.system = (bd->pins[ 65] == 0xFF) ? 200000 : bd->pins[ 65] * 4000; + MINFO->features.pll.ref_freq = (bd->pins[ 92] & 0x01) ? 14318 : 27000; + return 0; +} + +static void default_pins4(WPMINFO2) { + /* G400 */ + MINFO->limits.pixel.vcomax = + MINFO->limits.system.vcomax = 252000; + MINFO->values.reg.mctlwtst = 0x04A450A1; + MINFO->values.reg.memrdbk = 0x000000E7; + MINFO->values.reg.opt = 0x10000400; + MINFO->values.reg.opt3 = 0x0190A419; + MINFO->values.pll.system = 200000; + MINFO->features.pll.ref_freq = 27000; +} + +static int parse_pins5(WPMINFO const struct matrox_bios* bd) { + unsigned int mult; + + mult = bd->pins[4]?8000:6000; + + MINFO->limits.pixel.vcomax = (bd->pins[ 38] == 0xFF) ? 600000 : bd->pins[ 38] * mult; + MINFO->limits.system.vcomax = (bd->pins[ 36] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 39] * mult; + MINFO->limits.video.vcomax = (bd->pins[ 37] == 0xFF) ? MINFO->limits.system.vcomax : bd->pins[ 37] * mult; + MINFO->limits.pixel.vcomin = (bd->pins[123] == 0xFF) ? 256000 : bd->pins[123] * mult; + MINFO->limits.system.vcomin = (bd->pins[121] == 0xFF) ? MINFO->limits.pixel.vcomin : bd->pins[121] * mult; + MINFO->limits.video.vcomin = (bd->pins[122] == 0xFF) ? MINFO->limits.system.vcomin : bd->pins[122] * mult; + MINFO->values.pll.system = + MINFO->values.pll.video = (bd->pins[ 92] == 0xFF) ? 284000 : bd->pins[ 92] * 4000; + MINFO->values.reg.opt = get_u32(bd->pins+ 48); + MINFO->values.reg.opt2 = get_u32(bd->pins+ 52); + MINFO->values.reg.opt3 = get_u32(bd->pins+ 94); + MINFO->values.reg.mctlwtst = get_u32(bd->pins+ 98); + MINFO->values.reg.memmisc = get_u32(bd->pins+102); + MINFO->values.reg.memrdbk = get_u32(bd->pins+106); + MINFO->features.pll.ref_freq = (bd->pins[110] & 0x01) ? 14318 : 27000; + MINFO->values.memory.ddr = (bd->pins[114] & 0x60) == 0x20; + MINFO->values.memory.dll = (bd->pins[115] & 0x02) != 0; + MINFO->values.memory.emrswen = (bd->pins[115] & 0x01) != 0; + MINFO->values.reg.maccess = MINFO->values.memory.emrswen ? 0x00004000 : 0x00000000; + if (bd->pins[115] & 4) { + MINFO->values.reg.mctlwtst_core = MINFO->values.reg.mctlwtst; + } else { + u_int32_t wtst_xlat[] = { 0, 1, 5, 6, 7, 5, 2, 3 }; + MINFO->values.reg.mctlwtst_core = (MINFO->values.reg.mctlwtst & ~7) | + wtst_xlat[MINFO->values.reg.mctlwtst & 7]; + } + return 0; +} + +static void default_pins5(WPMINFO2) { + /* Mine 16MB G450 with SDRAM DDR */ + MINFO->limits.pixel.vcomax = + MINFO->limits.system.vcomax = + MINFO->limits.video.vcomax = 600000; + MINFO->limits.pixel.vcomin = + MINFO->limits.system.vcomin = + MINFO->limits.video.vcomin = 256000; + MINFO->values.pll.system = + MINFO->values.pll.video = 284000; + MINFO->values.reg.opt = 0x404A1160; + MINFO->values.reg.opt2 = 0x0000AC00; + MINFO->values.reg.opt3 = 0x0090A409; + MINFO->values.reg.mctlwtst_core = + MINFO->values.reg.mctlwtst = 0x0C81462B; + MINFO->values.reg.memmisc = 0x80000004; + MINFO->values.reg.memrdbk = 0x01001103; + MINFO->features.pll.ref_freq = 27000; + MINFO->values.memory.ddr = 1; + MINFO->values.memory.dll = 1; + MINFO->values.memory.emrswen = 1; + MINFO->values.reg.maccess = 0x00004000; +} + +static int matroxfb_set_limits(WPMINFO const struct matrox_bios* bd) { + unsigned int pins_version; + static const unsigned int pinslen[] = { 64, 64, 64, 128, 128 }; + + switch (ACCESS_FBINFO(chip)) { + case MGA_2064: default_pins1(PMINFO2); break; + case MGA_2164: + case MGA_1064: + case MGA_1164: default_pins2(PMINFO2); break; + case MGA_G100: + case MGA_G200: default_pins3(PMINFO2); break; + case MGA_G400: default_pins4(PMINFO2); break; + case MGA_G450: + case MGA_G550: default_pins5(PMINFO2); break; + } + if (!bd->bios_valid) { + printk(KERN_INFO "matroxfb: Your Matrox device does not have BIOS\n"); + return -1; + } + if (bd->pins_len < 64) { + printk(KERN_INFO "matroxfb: BIOS on your Matrox device does not contain powerup info\n"); + return -1; + } + if (bd->pins[0] == 0x2E && bd->pins[1] == 0x41) { + pins_version = bd->pins[5]; + if (pins_version < 2 || pins_version > 5) { + printk(KERN_INFO "matroxfb: Unknown version (%u) of powerup info\n", pins_version); + return -1; + } + } else { + pins_version = 1; + } + if (bd->pins_len != pinslen[pins_version - 1]) { + printk(KERN_INFO "matroxfb: Invalid powerup info\n"); + return -1; + } + switch (pins_version) { + case 1: + return parse_pins1(PMINFO bd); + case 2: + return parse_pins2(PMINFO bd); + case 3: + return parse_pins3(PMINFO bd); + case 4: + return parse_pins4(PMINFO bd); + case 5: + return parse_pins5(PMINFO bd); + default: + printk(KERN_DEBUG "matroxfb: Powerup info version %u is not yet supported\n", pins_version); + return -1; + } +} + +void matroxfb_read_pins(WPMINFO2) { + u32 opt; + u32 biosbase; + u32 fbbase; + struct pci_dev* pdev = ACCESS_FBINFO(pcidev); + + memset(&ACCESS_FBINFO(bios), 0, sizeof(ACCESS_FBINFO(bios))); + pci_read_config_dword(pdev, PCI_OPTION_REG, &opt); + pci_write_config_dword(pdev, PCI_OPTION_REG, opt | PCI_OPTION_ENABLE_ROM); + pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &biosbase); + pci_read_config_dword(pdev, ACCESS_FBINFO(devflags.fbResource), &fbbase); + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, (fbbase & PCI_ROM_ADDRESS_MASK) | PCI_ROM_ADDRESS_ENABLE); + parse_bios(vaddr_va(ACCESS_FBINFO(video).vbase), &ACCESS_FBINFO(bios)); + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, biosbase); + pci_write_config_dword(pdev, PCI_OPTION_REG, opt); + matroxfb_set_limits(PMINFO &ACCESS_FBINFO(bios)); +} + EXPORT_SYMBOL(matroxfb_DAC_in); EXPORT_SYMBOL(matroxfb_DAC_out); EXPORT_SYMBOL(matroxfb_var2my); @@ -657,6 +1009,7 @@ EXPORT_SYMBOL(matroxfb_fastfont_init); /* DAC1064, Ti3026 */ EXPORT_SYMBOL(matroxfb_vgaHWinit); /* DAC1064, Ti3026 */ EXPORT_SYMBOL(matroxfb_vgaHWrestore); /* DAC1064, Ti3026 */ +EXPORT_SYMBOL(matroxfb_read_pins); MODULE_AUTHOR("(c) 1999-2001 Petr Vandrovec "); MODULE_DESCRIPTION("Miscellaneous support for Matrox video cards"); diff -Nru a/drivers/video/matrox/matroxfb_misc.h b/drivers/video/matrox/matroxfb_misc.h --- a/drivers/video/matrox/matroxfb_misc.h Tue Feb 19 18:08:57 2002 +++ b/drivers/video/matrox/matroxfb_misc.h Tue Feb 19 18:08:57 2002 @@ -12,10 +12,11 @@ } void matroxfb_createcursorshape(WPMINFO struct display* p, int vmode); -int matroxfb_vgaHWinit(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p); -void matroxfb_vgaHWrestore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_state* oldhw); +int matroxfb_vgaHWinit(WPMINFO struct my_timming* m, struct display* p); +void matroxfb_vgaHWrestore(WPMINFO2); void matroxfb_fastfont_init(struct matrox_fb_info* minfo); int matrox_text_loadfont(WPMINFO struct display* p); int matroxfb_fastfont_tryset(WPMINFO struct display* p); +void matroxfb_read_pins(WPMINFO2); #endif /* __MATROXFB_MISC_H__ */ diff -Nru a/drivers/video/neofb.c b/drivers/video/neofb.c --- a/drivers/video/neofb.c Tue Feb 19 18:08:57 2002 +++ b/drivers/video/neofb.c Tue Feb 19 18:08:57 2002 @@ -12,6 +12,9 @@ * archive for more details. * * + * 0.3.2 + * - got rid of all floating point (dok) + * * 0.3.1 * - added module license (dok) * @@ -71,7 +74,7 @@ #include "neofb.h" -#define NEOFB_VERSION "0.3.1" +#define NEOFB_VERSION "0.3.2" /* --------------------------------------------------------------------- */ @@ -872,7 +875,7 @@ * * Determine the closest clock frequency to the one requested. */ -#define REF_FREQ 14.31818 +#define REF_FREQ 0xe517 /* 14.31818 in 20.12 fixed point */ #define MAX_N 127 #define MAX_D 31 #define MAX_F 1 @@ -880,17 +883,18 @@ static void neoCalcVCLK (const struct neofb_info *info, struct neofb_par *par, long freq) { int n, d, f; - double f_out; - double f_diff; int n_best = 0, d_best = 0, f_best = 0; - double f_best_diff = 999999.0; - double f_target = freq/1000.0; + long f_best_diff = (0x7ffff << 12); /* 20.12 */ + long f_target = (freq << 12) / 1000; /* 20.12 */ for (f = 0; f <= MAX_F; f++) for (n = 0; n <= MAX_N; n++) for (d = 0; d <= MAX_D; d++) { - f_out = (n+1.0)/((d+1.0)*(1<> 12) * REF_FREQ; f_diff = abs(f_out-f_target); if (f_diff < f_best_diff) { @@ -917,12 +921,12 @@ par->VCLK3Denominator = d_best; #ifdef NEOFB_DEBUG - printk ("neoVCLK: f:%f NumLow=%d NumHi=%d Den=%d Df=%f\n", - f_target, + printk ("neoVCLK: f:%d NumLow=%d NumHi=%d Den=%d Df=%d\n", + f_target >> 12, par->VCLK3NumeratorLow, par->VCLK3NumeratorHigh, par->VCLK3Denominator, - f_best_diff); + f_best_diff >> 12); #endif } diff -Nru a/drivers/video/vgacon.c b/drivers/video/vgacon.c --- a/drivers/video/vgacon.c Tue Feb 19 18:08:59 2002 +++ b/drivers/video/vgacon.c Tue Feb 19 18:08:59 2002 @@ -109,14 +109,7 @@ static unsigned int vga_default_font_height; /* Height of default screen font */ static unsigned char vga_video_type; /* Card type */ static unsigned char vga_hardscroll_enabled; -#ifdef CONFIG_IA64_SOFTSDV_HACKS -/* - * SoftSDV doesn't have hardware assist VGA scrolling - */ -static unsigned char vga_hardscroll_user_enable = 0; -#else static unsigned char vga_hardscroll_user_enable = 1; -#endif static unsigned char vga_font_is_default = 1; static int vga_vesa_blanked; static int vga_palette_blanked; diff -Nru a/fs/adfs/dir.c b/fs/adfs/dir.c --- a/fs/adfs/dir.c Tue Feb 19 18:08:57 2002 +++ b/fs/adfs/dir.c Tue Feb 19 18:08:57 2002 @@ -17,6 +17,7 @@ #include #include #include +#include #include "adfs.h" @@ -271,6 +272,7 @@ int error; dentry->d_op = &adfs_dentry_operations; + lock_kernel(); error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); if (error == 0) { error = -EACCES; @@ -282,6 +284,7 @@ if (inode) error = 0; } + unlock_kernel(); d_add(dentry, inode); return ERR_PTR(error); } diff -Nru a/fs/affs/dir.c b/fs/affs/dir.c --- a/fs/affs/dir.c Tue Feb 19 18:08:58 2002 +++ b/fs/affs/dir.c Tue Feb 19 18:08:58 2002 @@ -79,7 +79,7 @@ stored++; } if (f_pos == 1) { - if (filldir(dirent, "..", 2, f_pos, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) + if (filldir(dirent, "..", 2, f_pos, parent_ino(filp->f_dentry), DT_DIR) < 0) return stored; filp->f_pos = f_pos = 2; stored++; diff -Nru a/fs/affs/file.c b/fs/affs/file.c --- a/fs/affs/file.c Tue Feb 19 18:08:59 2002 +++ b/fs/affs/file.c Tue Feb 19 18:08:59 2002 @@ -807,6 +807,7 @@ pr_debug("AFFS: truncate(inode=%d, size=%u)\n", (u32)inode->i_ino, (u32)inode->i_size); + lock_kernel(); last_blk = 0; ext = 0; if (inode->i_size) { @@ -821,8 +822,10 @@ int res; page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT); - if (!page) + if (!page) { + unlock_kernel(); return; + } size = (size & (PAGE_CACHE_SIZE - 1)) + 1; res = mapping->a_ops->prepare_write(NULL, page, size, size); if (!res) @@ -830,9 +833,12 @@ UnlockPage(page); page_cache_release(page); mark_inode_dirty(inode); + unlock_kernel(); return; - } else if (inode->i_size == AFFS_I(inode)->mmu_private) + } else if (inode->i_size == AFFS_I(inode)->mmu_private) { + unlock_kernel(); return; + } // lock cache ext_bh = affs_get_extblock(inode, ext); @@ -888,4 +894,5 @@ ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension); affs_brelse(ext_bh); } + unlock_kernel(); } diff -Nru a/fs/affs/inode.c b/fs/affs/inode.c --- a/fs/affs/inode.c Tue Feb 19 18:08:59 2002 +++ b/fs/affs/inode.c Tue Feb 19 18:08:59 2002 @@ -275,11 +275,11 @@ affs_delete_inode(struct inode *inode) { pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); - lock_kernel(); inode->i_size = 0; if (S_ISREG(inode->i_mode)) affs_truncate(inode); clear_inode(inode); + lock_kernel(); affs_free_block(inode->i_sb, inode->i_ino); unlock_kernel(); } diff -Nru a/fs/affs/namei.c b/fs/affs/namei.c --- a/fs/affs/namei.c Tue Feb 19 18:08:59 2002 +++ b/fs/affs/namei.c Tue Feb 19 18:08:59 2002 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -217,11 +218,14 @@ pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name); + lock_kernel(); affs_lock_dir(dir); bh = affs_find_entry(dir, dentry); affs_unlock_dir(dir); - if (IS_ERR(bh)) + if (IS_ERR(bh)) { + unlock_kernel(); return ERR_PTR(PTR_ERR(bh)); + } if (bh) { u32 ino = bh->b_blocknr; @@ -235,10 +239,13 @@ } affs_brelse(bh); inode = iget(sb, ino); - if (!inode) + if (!inode) { + unlock_kernel(); return ERR_PTR(-EACCES); + } } dentry->d_op = AFFS_SB->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations; + unlock_kernel(); d_add(dentry, inode); return NULL; } @@ -246,13 +253,17 @@ int affs_unlink(struct inode *dir, struct dentry *dentry) { + int res; pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino, (int)dentry->d_name.len, dentry->d_name.name); if (!dentry->d_inode) return -ENOENT; - return affs_remove_header(dentry); + lock_kernel(); + res = affs_remove_header(dentry); + unlock_kernel(); + return res; } int @@ -265,9 +276,12 @@ pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len, dentry->d_name.name,mode); + lock_kernel(); inode = affs_new_inode(dir); - if (!inode) + if (!inode) { + unlock_kernel(); return -ENOSPC; + } inode->i_mode = mode; mode_to_prot(inode); @@ -280,8 +294,10 @@ if (error) { inode->i_nlink = 0; iput(inode); + unlock_kernel(); return error; } + unlock_kernel(); return 0; } @@ -294,9 +310,12 @@ pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino, (int)dentry->d_name.len,dentry->d_name.name,mode); + lock_kernel(); inode = affs_new_inode(dir); - if (!inode) + if (!inode) { + unlock_kernel(); return -ENOSPC; + } inode->i_mode = S_IFDIR | mode; mode_to_prot(inode); @@ -309,21 +328,29 @@ inode->i_nlink = 0; mark_inode_dirty(inode); iput(inode); + unlock_kernel(); return error; } + unlock_kernel(); return 0; } int affs_rmdir(struct inode *dir, struct dentry *dentry) { + int res; pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino, (int)dentry->d_name.len, dentry->d_name.name); + lock_kernel(); + + /* WTF??? */ if (!dentry->d_inode) return -ENOENT; - return affs_remove_header(dentry); + res = affs_remove_header(dentry); + unlock_kernel(); + return res; } int @@ -339,11 +366,14 @@ pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino, (int)dentry->d_name.len,dentry->d_name.name,symname); + lock_kernel(); maxlen = AFFS_SB->s_hashsize * sizeof(u32) - 1; error = -ENOSPC; inode = affs_new_inode(dir); - if (!inode) + if (!inode) { + unlock_kernel(); return -ENOSPC; + } inode->i_op = &affs_symlink_inode_operations; inode->i_data.a_ops = &affs_symlink_aops; @@ -389,6 +419,7 @@ error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK); if (error) goto err; + unlock_kernel(); return 0; @@ -396,6 +427,7 @@ inode->i_nlink = 0; mark_inode_dirty(inode); iput(inode); + unlock_kernel(); return error; } @@ -408,16 +440,17 @@ pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino, (int)dentry->d_name.len,dentry->d_name.name); - if (S_ISDIR(inode->i_mode)) - return -EPERM; - - error = affs_add_entry(dir, inode, dentry, S_ISDIR(inode->i_mode) ? ST_LINKDIR : ST_LINKFILE); + lock_kernel(); + error = affs_add_entry(dir, inode, dentry, ST_LINKFILE); if (error) { + /* WTF??? */ inode->i_nlink = 0; mark_inode_dirty(inode); iput(inode); + unlock_kernel(); return error; } + unlock_kernel(); return 0; } @@ -429,6 +462,7 @@ struct buffer_head *bh = NULL; int retval; + lock_kernel(); pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n", (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name, (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name); @@ -439,8 +473,10 @@ /* Unlink destination if it already exists */ if (new_dentry->d_inode) { retval = affs_remove_header(new_dentry); - if (retval) + if (retval) { + unlock_kernel(); return retval; + } } retval = -EIO; @@ -466,5 +502,6 @@ done: mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir); affs_brelse(bh); + unlock_kernel(); return retval; } diff -Nru a/fs/attr.c b/fs/attr.c --- a/fs/attr.c Tue Feb 19 18:08:58 2002 +++ b/fs/attr.c Tue Feb 19 18:08:58 2002 @@ -144,7 +144,7 @@ if (!error) { unsigned long dn_mask = setattr_mask(ia_valid); if (dn_mask) - inode_dir_notify(dentry->d_parent->d_inode, dn_mask); + dnotify_parent(dentry, dn_mask); } return error; } diff -Nru a/fs/autofs/dir.c b/fs/autofs/dir.c --- a/fs/autofs/dir.c Tue Feb 19 18:09:00 2002 +++ b/fs/autofs/dir.c Tue Feb 19 18:09:00 2002 @@ -17,6 +17,7 @@ * layer. So all children are negative and dcache-based versions of operations * are OK. */ +/* SMP-safe */ static struct dentry *autofs_dir_lookup(struct inode *dir,struct dentry *dentry) { d_add(dentry, NULL); diff -Nru a/fs/autofs/root.c b/fs/autofs/root.c --- a/fs/autofs/root.c Tue Feb 19 18:09:00 2002 +++ b/fs/autofs/root.c Tue Feb 19 18:09:00 2002 @@ -197,10 +197,13 @@ int oz_mode; DPRINTK(("autofs_root_lookup: name = ")); + lock_kernel(); autofs_say(dentry->d_name.name,dentry->d_name.len); - if (dentry->d_name.len > NAME_MAX) + if (dentry->d_name.len > NAME_MAX) { + unlock_kernel(); return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ + } sbi = autofs_sbi(dir->i_sb); @@ -231,9 +234,12 @@ * a signal. If so we can force a restart.. */ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { - if (signal_pending(current)) + if (signal_pending(current)) { + unlock_kernel(); return ERR_PTR(-ERESTARTNOINTR); + } } + unlock_kernel(); /* * If this dentry is unhashed, then we shouldn't honour this @@ -259,15 +265,22 @@ DPRINTK(("autofs_root_symlink: %s <- ", symname)); autofs_say(dentry->d_name.name,dentry->d_name.len); - if ( !autofs_oz_mode(sbi) ) + lock_kernel(); + if ( !autofs_oz_mode(sbi) ) { + unlock_kernel(); return -EACCES; + } - if ( autofs_hash_lookup(dh, &dentry->d_name) ) + if ( autofs_hash_lookup(dh, &dentry->d_name) ) { + unlock_kernel(); return -EEXIST; + } n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS); - if ( n >= AUTOFS_MAX_SYMLINKS ) + if ( n >= AUTOFS_MAX_SYMLINKS ) { + unlock_kernel(); return -ENOSPC; + } set_bit(n,sbi->symlink_bitmap); sl = &sbi->symlink[n]; @@ -275,6 +288,7 @@ sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL); if ( !sl->data ) { clear_bit(n,sbi->symlink_bitmap); + unlock_kernel(); return -ENOSPC; } @@ -282,6 +296,7 @@ if ( !ent ) { kfree(sl->data); clear_bit(n,sbi->symlink_bitmap); + unlock_kernel(); return -ENOSPC; } @@ -290,6 +305,7 @@ kfree(sl->data); kfree(ent); clear_bit(n,sbi->symlink_bitmap); + unlock_kernel(); return -ENOSPC; } @@ -303,7 +319,7 @@ autofs_hash_insert(dh,ent); d_instantiate(dentry, iget(dir->i_sb,ent->ino)); - + unlock_kernel(); return 0; } @@ -326,18 +342,27 @@ unsigned int n; /* This allows root to remove symlinks */ - if ( !autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) + lock_kernel(); + if ( !autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) { + unlock_kernel(); return -EACCES; + } ent = autofs_hash_lookup(dh, &dentry->d_name); - if ( !ent ) + if ( !ent ) { + unlock_kernel(); return -ENOENT; + } n = ent->ino - AUTOFS_FIRST_SYMLINK; - if ( n >= AUTOFS_MAX_SYMLINKS ) + if ( n >= AUTOFS_MAX_SYMLINKS ) { + unlock_kernel(); return -EISDIR; /* It's a directory, dummy */ - if ( !test_bit(n,sbi->symlink_bitmap) ) + } + if ( !test_bit(n,sbi->symlink_bitmap) ) { + unlock_kernel(); return -EINVAL; /* Nonexistent symlink? Shouldn't happen */ + } dentry->d_time = (unsigned long)(struct autofs_dirhash *)NULL; autofs_hash_delete(ent); @@ -345,6 +370,7 @@ kfree(sbi->symlink[n].data); d_drop(dentry); + unlock_kernel(); return 0; } @@ -354,15 +380,22 @@ struct autofs_dirhash *dh = &sbi->dirhash; struct autofs_dir_ent *ent; - if ( !autofs_oz_mode(sbi) ) + lock_kernel(); + if ( !autofs_oz_mode(sbi) ) { + unlock_kernel(); return -EACCES; + } ent = autofs_hash_lookup(dh, &dentry->d_name); - if ( !ent ) + if ( !ent ) { + unlock_kernel(); return -ENOENT; + } - if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) + if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) { + unlock_kernel(); return -ENOTDIR; /* Not a directory */ + } if ( ent->dentry != dentry ) { printk("autofs_rmdir: odentry != dentry for entry %s\n", dentry->d_name.name); @@ -372,6 +405,7 @@ autofs_hash_delete(ent); dir->i_nlink--; d_drop(dentry); + unlock_kernel(); return 0; } @@ -383,26 +417,35 @@ struct autofs_dir_ent *ent; ino_t ino; - if ( !autofs_oz_mode(sbi) ) + lock_kernel(); + if ( !autofs_oz_mode(sbi) ) { + unlock_kernel(); return -EACCES; + } ent = autofs_hash_lookup(dh, &dentry->d_name); - if ( ent ) + if ( ent ) { + unlock_kernel(); return -EEXIST; + } if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) { printk("autofs: Out of inode numbers -- what the heck did you do??\n"); + unlock_kernel(); return -ENOSPC; } ino = sbi->next_dir_ino++; ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); - if ( !ent ) + if ( !ent ) { + unlock_kernel(); return -ENOSPC; + } ent->name = kmalloc(dentry->d_name.len+1, GFP_KERNEL); if ( !ent->name ) { kfree(ent); + unlock_kernel(); return -ENOSPC; } @@ -414,6 +457,7 @@ dir->i_nlink++; d_instantiate(dentry, iget(dir->i_sb,ino)); + unlock_kernel(); return 0; } diff -Nru a/fs/autofs/waitq.c b/fs/autofs/waitq.c --- a/fs/autofs/waitq.c Tue Feb 19 18:08:58 2002 +++ b/fs/autofs/waitq.c Tue Feb 19 18:08:58 2002 @@ -72,7 +72,7 @@ if (wr == -EPIPE && !sigpipe) { spin_lock_irqsave(¤t->sigmask_lock, flags); sigdelset(¤t->pending.signal, SIGPIPE); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } @@ -164,14 +164,14 @@ spin_lock_irqsave(¤t->sigmask_lock, irqflags); oldset = current->blocked; siginitsetinv(¤t->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, irqflags); interruptible_sleep_on(&wq->queue); spin_lock_irqsave(¤t->sigmask_lock, irqflags); current->blocked = oldset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, irqflags); } else { DPRINTK(("autofs_wait: skipped sleeping\n")); diff -Nru a/fs/autofs4/root.c b/fs/autofs4/root.c --- a/fs/autofs4/root.c Tue Feb 19 18:08:57 2002 +++ b/fs/autofs4/root.c Tue Feb 19 18:08:57 2002 @@ -229,6 +229,7 @@ /* Lookups in non-root dirs never find anything - if it's there, it's already in the dcache */ +/* SMP-safe */ static struct dentry *autofs4_dir_lookup(struct inode *dir, struct dentry *dentry) { #if 0 @@ -256,6 +257,7 @@ sbi = autofs4_sbi(dir->i_sb); + lock_kernel(); oz_mode = autofs4_oz_mode(sbi); DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode)); @@ -288,9 +290,12 @@ * a signal. If so we can force a restart.. */ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { - if (signal_pending(current)) + if (signal_pending(current)) { + unlock_kernel(); return ERR_PTR(-ERESTARTNOINTR); + } } + unlock_kernel(); /* * If this dentry is unhashed, then we shouldn't honour this @@ -316,18 +321,24 @@ DPRINTK(("autofs_dir_symlink: %s <- %.*s\n", symname, dentry->d_name.len, dentry->d_name.name)); - if (!autofs4_oz_mode(sbi)) + lock_kernel(); + if (!autofs4_oz_mode(sbi)) { + unlock_kernel(); return -EACCES; + } ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); - if (ino == NULL) + if (ino == NULL) { + unlock_kernel(); return -ENOSPC; + } ino->size = strlen(symname); ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); if (cp == NULL) { kfree(ino); + unlock_kernel(); return -ENOSPC; } @@ -347,6 +358,7 @@ dir->i_mtime = CURRENT_TIME; + unlock_kernel(); return 0; } @@ -371,8 +383,11 @@ struct autofs_info *ino = autofs4_dentry_ino(dentry); /* This allows root to remove symlinks */ - if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) + lock_kernel(); + if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) { + unlock_kernel(); return -EACCES; + } dput(ino->dentry); @@ -382,6 +397,8 @@ dir->i_mtime = CURRENT_TIME; d_drop(dentry); + + unlock_kernel(); return 0; } @@ -391,12 +408,16 @@ struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); - if (!autofs4_oz_mode(sbi)) + lock_kernel(); + if (!autofs4_oz_mode(sbi)) { + unlock_kernel(); return -EACCES; + } spin_lock(&dcache_lock); if (!list_empty(&dentry->d_subdirs)) { spin_unlock(&dcache_lock); + unlock_kernel(); return -ENOTEMPTY; } list_del_init(&dentry->d_hash); @@ -410,6 +431,7 @@ if (dir->i_nlink) dir->i_nlink--; + unlock_kernel(); return 0; } @@ -421,15 +443,20 @@ struct autofs_info *ino = autofs4_dentry_ino(dentry); struct inode *inode; - if ( !autofs4_oz_mode(sbi) ) + lock_kernel(); + if ( !autofs4_oz_mode(sbi) ) { + unlock_kernel(); return -EACCES; + } DPRINTK(("autofs_dir_mkdir: dentry %p, creating %.*s\n", dentry, dentry->d_name.len, dentry->d_name.name)); ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); - if (ino == NULL) + if (ino == NULL) { + unlock_kernel(); return -ENOSPC; + } inode = autofs4_get_inode(dir->i_sb, ino); d_instantiate(dentry, inode); @@ -445,6 +472,7 @@ dir->i_nlink++; dir->i_mtime = CURRENT_TIME; + unlock_kernel(); return 0; } diff -Nru a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c --- a/fs/autofs4/waitq.c Tue Feb 19 18:08:59 2002 +++ b/fs/autofs4/waitq.c Tue Feb 19 18:08:59 2002 @@ -76,7 +76,7 @@ if (wr == -EPIPE && !sigpipe) { spin_lock_irqsave(¤t->sigmask_lock, flags); sigdelset(¤t->pending.signal, SIGPIPE); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } @@ -201,14 +201,14 @@ spin_lock_irqsave(¤t->sigmask_lock, irqflags); oldset = current->blocked; siginitsetinv(¤t->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, irqflags); interruptible_sleep_on(&wq->queue); spin_lock_irqsave(¤t->sigmask_lock, irqflags); current->blocked = oldset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, irqflags); } else { DPRINTK(("autofs_wait: skipped sleeping\n")); diff -Nru a/fs/bfs/dir.c b/fs/bfs/dir.c --- a/fs/bfs/dir.c Tue Feb 19 18:09:00 2002 +++ b/fs/bfs/dir.c Tue Feb 19 18:09:00 2002 @@ -8,6 +8,7 @@ #include #include #include +#include #include "bfs_defs.h" @@ -82,8 +83,10 @@ inode = new_inode(s); if (!inode) return -ENOSPC; + lock_kernel(); ino = find_first_zero_bit(s->su_imap, s->su_lasti); if (ino > s->su_lasti) { + unlock_kernel(); iput(inode); return -ENOSPC; } @@ -110,8 +113,10 @@ inode->i_nlink--; mark_inode_dirty(inode); iput(inode); + unlock_kernel(); return err; } + unlock_kernel(); d_instantiate(dentry, inode); return 0; } @@ -125,14 +130,18 @@ if (dentry->d_name.len > BFS_NAMELEN) return ERR_PTR(-ENAMETOOLONG); + lock_kernel(); bh = bfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); if (bh) { unsigned long ino = le32_to_cpu(de->ino); brelse(bh); inode = iget(dir->i_sb, ino); - if (!inode) + if (!inode) { + unlock_kernel(); return ERR_PTR(-EACCES); + } } + unlock_kernel(); d_add(dentry, inode); return NULL; } @@ -142,17 +151,18 @@ struct inode * inode = old->d_inode; int err; - if (S_ISDIR(inode->i_mode)) - return -EPERM; - + lock_kernel(); err = bfs_add_entry(dir, new->d_name.name, new->d_name.len, inode->i_ino); - if (err) + if (err) { + unlock_kernel(); return err; + } inode->i_nlink++; inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); atomic_inc(&inode->i_count); d_instantiate(new, inode); + unlock_kernel(); return 0; } @@ -165,6 +175,7 @@ struct bfs_dirent * de; inode = dentry->d_inode; + lock_kernel(); bh = bfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); if (!bh || de->ino != inode->i_ino) goto out_brelse; @@ -185,6 +196,7 @@ out_brelse: brelse(bh); + unlock_kernel(); return error; } @@ -201,6 +213,7 @@ if (S_ISDIR(old_inode->i_mode)) return -EINVAL; + lock_kernel(); old_bh = bfs_find_entry(old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); @@ -237,6 +250,7 @@ error = 0; end_rename: + unlock_kernel(); brelse(old_bh); brelse(new_bh); return error; diff -Nru a/fs/binfmt_elf.c b/fs/binfmt_elf.c --- a/fs/binfmt_elf.c Tue Feb 19 18:08:59 2002 +++ b/fs/binfmt_elf.c Tue Feb 19 18:08:59 2002 @@ -1025,6 +1025,23 @@ } + memset(&prstatus, 0, sizeof(prstatus)); + /* + * This transfers the registers from regs into the standard + * coredump arrangement, whatever that is. + */ +#ifdef ELF_CORE_COPY_REGS + ELF_CORE_COPY_REGS(prstatus.pr_reg, regs) +#else + if (sizeof(elf_gregset_t) != sizeof(struct pt_regs)) + { + printk("sizeof(elf_gregset_t) (%ld) != sizeof(struct pt_regs) (%ld)\n", + (long)sizeof(elf_gregset_t), (long)sizeof(struct pt_regs)); + } + else + *(struct pt_regs *)&prstatus.pr_reg = *regs; +#endif + /* now stop all vm operations */ down_write(¤t->mm->mmap_sem); segs = current->mm->map_count; @@ -1068,7 +1085,6 @@ * Set up the notes in similar form to SVR4 core dumps made * with info from their /proc. */ - memset(&prstatus, 0, sizeof(prstatus)); notes[0].name = "CORE"; notes[0].type = NT_PRSTATUS; @@ -1090,22 +1106,6 @@ prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->times.tms_cstime); prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->times.tms_cstime); - /* - * This transfers the registers from regs into the standard - * coredump arrangement, whatever that is. - */ -#ifdef ELF_CORE_COPY_REGS - ELF_CORE_COPY_REGS(prstatus.pr_reg, regs) -#else - if (sizeof(elf_gregset_t) != sizeof(struct pt_regs)) - { - printk("sizeof(elf_gregset_t) (%ld) != sizeof(struct pt_regs) (%ld)\n", - (long)sizeof(elf_gregset_t), (long)sizeof(struct pt_regs)); - } - else - *(struct pt_regs *)&prstatus.pr_reg = *regs; -#endif - #ifdef DEBUG dump_regs("Passed in regs", (elf_greg_t *)regs); dump_regs("prstatus regs", (elf_greg_t *)&prstatus.pr_reg); @@ -1119,7 +1119,7 @@ psinfo.pr_state = i; psinfo.pr_sname = (i < 0 || i > 5) ? '.' : "RSDZTD"[i]; psinfo.pr_zomb = psinfo.pr_sname == 'Z'; - psinfo.pr_nice = current->__nice; + psinfo.pr_nice = task_nice(current); psinfo.pr_flag = current->flags; psinfo.pr_uid = NEW_TO_OLD_UID(current->uid); psinfo.pr_gid = NEW_TO_OLD_GID(current->gid); @@ -1201,9 +1201,11 @@ if (!maydump(vma)) continue; + #ifdef DEBUG - printk("elf_core_dump: writing %08lx %lx\n", addr, len); + printk("elf_core_dump: writing %08lx-%08lx\n", vma->vm_start, vma->vm_end); #endif + for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) { diff -Nru a/fs/binfmt_misc.c b/fs/binfmt_misc.c --- a/fs/binfmt_misc.c Tue Feb 19 18:08:59 2002 +++ b/fs/binfmt_misc.c Tue Feb 19 18:08:59 2002 @@ -472,11 +472,9 @@ break; case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root); down(&root->d_inode->i_sem); - down(&root->d_inode->i_zombie); kill_node(e); - up(&root->d_inode->i_zombie); up(&root->d_inode->i_sem); dput(root); break; @@ -516,8 +514,6 @@ if (IS_ERR(dentry)) goto out; - down(&root->d_inode->i_zombie); - err = -EEXIST; if (dentry->d_inode) goto out2; @@ -556,7 +552,6 @@ mntput(mnt); err = 0; out2: - up(&root->d_inode->i_zombie); dput(dentry); out: up(&root->d_inode->i_sem); @@ -605,12 +600,10 @@ case 2: enabled = 1; break; case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root); down(&root->d_inode->i_sem); - down(&root->d_inode->i_zombie); while (!list_empty(&entries)) kill_node(list_entry(entries.next, Node, list)); - up(&root->d_inode->i_zombie); up(&root->d_inode->i_sem); dput(root); default: return res; @@ -625,6 +618,7 @@ /* / */ +/* SMP-safe */ static struct dentry * bm_lookup(struct inode *dir, struct dentry *dentry) { d_add(dentry, NULL); diff -Nru a/fs/bio.c b/fs/bio.c --- a/fs/bio.c Tue Feb 19 18:08:57 2002 +++ b/fs/bio.c Tue Feb 19 18:08:57 2002 @@ -316,13 +316,12 @@ return NULL; } -static int bio_end_io_kio(struct bio *bio, int nr_sectors) +static void bio_end_io_kio(struct bio *bio) { struct kiobuf *kio = (struct kiobuf *) bio->bi_private; end_kio_request(kio, test_bit(BIO_UPTODATE, &bio->bi_flags)); bio_put(bio); - return 0; } /** @@ -441,7 +440,7 @@ end_kio_request(kio, !err); } -int bio_endio(struct bio *bio, int uptodate, int nr_sectors) +void bio_endio(struct bio *bio, int uptodate) { if (uptodate) set_bit(BIO_UPTODATE, &bio->bi_flags); @@ -449,9 +448,7 @@ clear_bit(BIO_UPTODATE, &bio->bi_flags); if (bio->bi_end_io) - return bio->bi_end_io(bio, nr_sectors); - - return 0; + bio->bi_end_io(bio); } static void __init biovec_init_pool(void) diff -Nru a/fs/block_dev.c b/fs/block_dev.c --- a/fs/block_dev.c Tue Feb 19 18:09:00 2002 +++ b/fs/block_dev.c Tue Feb 19 18:09:00 2002 @@ -195,11 +195,15 @@ static int __block_fsync(struct inode * inode) { - int ret; + int ret, err; - filemap_fdatasync(inode->i_mapping); - ret = sync_buffers(inode->i_rdev, 1); - filemap_fdatawait(inode->i_mapping); + ret = filemap_fdatasync(inode->i_mapping); + err = sync_buffers(inode->i_rdev, 1); + if (err && !ret) + ret = err; + err = filemap_fdatawait(inode->i_mapping); + if (err && !ret) + ret = err; return ret; } @@ -647,10 +651,14 @@ down(&bdev->bd_sem); lock_kernel(); - if (kind == BDEV_FILE) + switch (kind) { + case BDEV_FILE: __block_fsync(bd_inode); - else if (kind == BDEV_FS) + break; + case BDEV_FS: fsync_no_super(bdev); + break; + } if (!--bdev->bd_openers) kill_bdev(bdev); if (bdev->bd_op->release) diff -Nru a/fs/buffer.c b/fs/buffer.c --- a/fs/buffer.c Tue Feb 19 18:08:57 2002 +++ b/fs/buffer.c Tue Feb 19 18:08:57 2002 @@ -410,9 +410,9 @@ struct file * file; struct dentry * dentry; struct inode * inode; - int err; + int ret, err; - err = -EBADF; + ret = -EBADF; file = fget(fd); if (!file) goto out; @@ -420,21 +420,27 @@ dentry = file->f_dentry; inode = dentry->d_inode; - err = -EINVAL; - if (!file->f_op || !file->f_op->fsync) + ret = -EINVAL; + if (!file->f_op || !file->f_op->fsync) { + /* Why? We can still call filemap_fdatasync */ goto out_putf; + } /* We need to protect against concurrent writers.. */ down(&inode->i_sem); - filemap_fdatasync(inode->i_mapping); + ret = filemap_fdatasync(inode->i_mapping); err = file->f_op->fsync(file, dentry, 0); - filemap_fdatawait(inode->i_mapping); + if (err && !ret) + ret = err; + err = filemap_fdatawait(inode->i_mapping); + if (err && !ret) + ret = err; up(&inode->i_sem); out_putf: fput(file); out: - return err; + return ret; } asmlinkage long sys_fdatasync(unsigned int fd) @@ -442,9 +448,9 @@ struct file * file; struct dentry * dentry; struct inode * inode; - int err; + int ret, err; - err = -EBADF; + ret = -EBADF; file = fget(fd); if (!file) goto out; @@ -452,20 +458,24 @@ dentry = file->f_dentry; inode = dentry->d_inode; - err = -EINVAL; + ret = -EINVAL; if (!file->f_op || !file->f_op->fsync) goto out_putf; down(&inode->i_sem); - filemap_fdatasync(inode->i_mapping); + ret = filemap_fdatasync(inode->i_mapping); err = file->f_op->fsync(file, dentry, 1); - filemap_fdatawait(inode->i_mapping); + if (err && !ret) + ret = err; + err = filemap_fdatawait(inode->i_mapping); + if (err && !ret) + ret = err; up(&inode->i_sem); out_putf: fput(file); out: - return err; + return ret; } /* After several hours of tedious analysis, the following hash @@ -1431,6 +1441,7 @@ int err, i; unsigned long block; struct buffer_head *bh, *head; + int need_unlock; if (!PageLocked(page)) BUG(); @@ -1486,8 +1497,34 @@ return 0; out: + /* + * ENOSPC, or some other error. We may already have added some + * blocks to the file, so we need to write these out to avoid + * exposing stale data. + */ ClearPageUptodate(page); - UnlockPage(page); + bh = head; + need_unlock = 1; + /* Recovery: lock and submit the mapped buffers */ + do { + if (buffer_mapped(bh)) { + lock_buffer(bh); + set_buffer_async_io(bh); + need_unlock = 0; + } + bh = bh->b_this_page; + } while (bh != head); + do { + struct buffer_head *next = bh->b_this_page; + if (buffer_mapped(bh)) { + set_bit(BH_Uptodate, &bh->b_state); + clear_bit(BH_Dirty, &bh->b_state); + submit_bh(WRITE, bh); + } + bh = next; + } while (bh != head); + if (need_unlock) + UnlockPage(page); return err; } @@ -1518,6 +1555,7 @@ continue; if (block_start >= to) break; + clear_bit(BH_New, &bh->b_state); if (!buffer_mapped(bh)) { err = get_block(inode, block, bh, 1); if (err) @@ -1552,12 +1590,35 @@ */ while(wait_bh > wait) { wait_on_buffer(*--wait_bh); - err = -EIO; if (!buffer_uptodate(*wait_bh)) - goto out; + return -EIO; } return 0; out: + /* + * Zero out any newly allocated blocks to avoid exposing stale + * data. If BH_New is set, we know that the block was newly + * allocated in the above loop. + */ + bh = head; + block_start = 0; + do { + block_end = block_start+blocksize; + if (block_end <= from) + goto next_bh; + if (block_start >= to) + break; + if (buffer_new(bh)) { + if (buffer_uptodate(bh)) + printk(KERN_ERR "%s: zeroing uptodate buffer!\n", __FUNCTION__); + memset(kaddr+block_start, 0, bh->b_size); + set_bit(BH_Uptodate, &bh->b_state); + mark_buffer_dirty(bh); + } +next_bh: + block_start = block_end; + bh = bh->b_this_page; + } while (bh != head); return err; } @@ -1954,6 +2015,48 @@ goto done; } +/* + * Commence writeout of all the buffers against a page. The + * page must be locked. Returns zero on success or a negative + * errno. + */ +int writeout_one_page(struct page *page) +{ + struct buffer_head *bh, *head = page->buffers; + + if (!PageLocked(page)) + BUG(); + bh = head; + do { + if (buffer_locked(bh) || !buffer_dirty(bh) || !buffer_uptodate(bh)) + continue; + + bh->b_flushtime = jiffies; + ll_rw_block(WRITE, 1, &bh); + } while ((bh = bh->b_this_page) != head); + return 0; +} +EXPORT_SYMBOL(writeout_one_page); + +/* + * Wait for completion of I/O of all buffers against a page. The page + * must be locked. Returns zero on success or a negative errno. + */ +int waitfor_one_page(struct page *page) +{ + int error = 0; + struct buffer_head *bh, *head = page->buffers; + + bh = head; + do { + wait_on_buffer(bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) + error = -EIO; + } while ((bh = bh->b_this_page) != head); + return error; +} +EXPORT_SYMBOL(waitfor_one_page); + sector_t generic_block_bmap(struct address_space *mapping, sector_t block, get_block_t *get_block) { @@ -2012,8 +2115,7 @@ * of kiobuf structs (much like a user-space iovec list). * * The kiobuf must already be locked for IO. IO is submitted - * asynchronously: you need to check page->locked, page->uptodate, and - * maybe wait on page->wait. + * asynchronously: you need to check page->locked and page->uptodate. * * It is up to the caller to make sure that there are enough blocks * passed in to completely map the iobufs to disk. @@ -2070,8 +2172,8 @@ /* * Start I/O on a page. * This function expects the page to be locked and may return - * before I/O is complete. You then have to check page->locked, - * page->uptodate, and maybe wait on page->wait. + * before I/O is complete. You then have to check page->locked + * and page->uptodate. * * brw_page() is SMP-safe, although it's being called with the * kernel lock held - but the code is ready. @@ -2614,7 +2716,7 @@ spin_lock_irq(&tsk->sigmask_lock); flush_signals(tsk); sigfillset(&tsk->blocked); - recalc_sigpending(tsk); + recalc_sigpending(); spin_unlock_irq(&tsk->sigmask_lock); complete((struct completion *)startup); @@ -2649,7 +2751,7 @@ spin_lock_irq(&tsk->sigmask_lock); sigfillset(&tsk->blocked); siginitsetinv(¤t->blocked, sigmask(SIGCONT) | sigmask(SIGSTOP)); - recalc_sigpending(tsk); + recalc_sigpending(); spin_unlock_irq(&tsk->sigmask_lock); complete((struct completion *)startup); @@ -2675,7 +2777,7 @@ sigdelset(&tsk->pending.signal, SIGSTOP); stopped = 1; } - recalc_sigpending(tsk); + recalc_sigpending(); spin_unlock_irq(&tsk->sigmask_lock); if (stopped) goto stop_kupdate; diff -Nru a/fs/coda/dir.c b/fs/coda/dir.c --- a/fs/coda/dir.c Tue Feb 19 18:08:58 2002 +++ b/fs/coda/dir.c Tue Feb 19 18:08:58 2002 @@ -112,6 +112,7 @@ CDEBUG(D_INODE, "name %s, len %ld in ino %ld, fid %s\n", name, (long)length, dir->i_ino, coda_i2s(dir)); + lock_kernel(); /* control object, create inode on the fly */ if (coda_isroot(dir) && coda_iscontrol(name, length)) { error = coda_cnode_makectl(&res_inode, dir->i_sb); @@ -135,10 +136,14 @@ } error = coda_cnode_make(&res_inode, &resfid, dir->i_sb); - if (error) return ERR_PTR(error); + if (error) { + unlock_kernel(); + return ERR_PTR(error); + } } else if (error != -ENOENT) { CDEBUG(D_INODE, "error for %s(%*s)%d\n", coda_i2s(dir), (int)length, name, error); + unlock_kernel(); return ERR_PTR(error); } CDEBUG(D_INODE, "lookup: %s is (%s), type %d result %d, dropme %d\n", @@ -152,6 +157,7 @@ d_drop(entry); coda_flag_inode(res_inode, C_VATTR); } + unlock_kernel(); return NULL; } @@ -213,12 +219,15 @@ struct ViceFid newfid; struct coda_vattr attrs; + lock_kernel(); coda_vfs_stat.create++; CDEBUG(D_INODE, "name: %s, length %d, mode %o\n", name, length, mode); - if (coda_isroot(dir) && coda_iscontrol(name, length)) + if (coda_isroot(dir) && coda_iscontrol(name, length)) { + unlock_kernel(); return -EPERM; + } error = venus_create(dir->i_sb, coda_i2f(dir), name, length, 0, mode, 0, &newfid, &attrs); @@ -226,12 +235,14 @@ if ( error ) { CDEBUG(D_INODE, "create: %s, result %d\n", coda_f2s(&newfid), error); + unlock_kernel(); d_drop(de); return error; } error = coda_cnode_make(&result, &newfid, dir->i_sb); if ( error ) { + unlock_kernel(); d_drop(de); result = NULL; return error; @@ -239,6 +250,7 @@ /* invalidate the directory cnode's attributes */ coda_dir_changed(dir, 0); + unlock_kernel(); d_instantiate(de, result); return 0; } @@ -255,13 +267,16 @@ if ( coda_hasmknod == 0 ) return -EIO; + lock_kernel(); coda_vfs_stat.create++; CDEBUG(D_INODE, "name: %s, length %d, mode %o, rdev %x\n", name, length, mode, rdev); - if (coda_isroot(dir) && coda_iscontrol(name, length)) + if (coda_isroot(dir) && coda_iscontrol(name, length)) { + unlock_kernel(); return -EPERM; + } error = venus_create(dir->i_sb, coda_i2f(dir), name, length, 0, mode, rdev, &newfid, &attrs); @@ -270,6 +285,7 @@ CDEBUG(D_INODE, "mknod: %s, result %d\n", coda_f2s(&newfid), error); d_drop(de); + unlock_kernel(); return error; } @@ -277,11 +293,13 @@ if ( error ) { d_drop(de); result = NULL; + unlock_kernel(); return error; } /* invalidate the directory cnode's attributes */ coda_dir_changed(dir, 0); + unlock_kernel(); d_instantiate(de, result); return 0; } @@ -295,10 +313,13 @@ int error; struct ViceFid newfid; + lock_kernel(); coda_vfs_stat.mkdir++; - if (coda_isroot(dir) && coda_iscontrol(name, len)) + if (coda_isroot(dir) && coda_iscontrol(name, len)) { + unlock_kernel(); return -EPERM; + } CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n", name, len, coda_i2s(dir), mode); @@ -311,6 +332,7 @@ CDEBUG(D_INODE, "mkdir error: %s result %d\n", coda_f2s(&newfid), error); d_drop(de); + unlock_kernel(); return error; } @@ -320,11 +342,13 @@ error = coda_cnode_make(&inode, &newfid, dir->i_sb); if ( error ) { d_drop(de); + unlock_kernel(); return error; } /* invalidate the directory cnode's attributes */ coda_dir_changed(dir, 1); + unlock_kernel(); d_instantiate(de, inode); return 0; } @@ -338,10 +362,13 @@ int len = de->d_name.len; int error; + lock_kernel(); coda_vfs_stat.link++; - if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) + if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) { + unlock_kernel(); return -EPERM; + } CDEBUG(D_INODE, "old: fid: %s\n", coda_i2s(inode)); CDEBUG(D_INODE, "directory: %s\n", coda_i2s(dir_inode)); @@ -361,6 +388,7 @@ out: CDEBUG(D_INODE, "link result %d\n",error); + unlock_kernel(); return(error); } @@ -373,14 +401,19 @@ int symlen; int error=0; + lock_kernel(); coda_vfs_stat.symlink++; - if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) + if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) { + unlock_kernel(); return -EPERM; + } symlen = strlen(symname); - if ( symlen > CODA_MAXPATHLEN ) - return -ENAMETOOLONG; + if ( symlen > CODA_MAXPATHLEN ) { + unlock_kernel(); + return -ENAMETOOLONG; + } CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen); @@ -397,6 +430,7 @@ coda_dir_changed(dir_inode, 0); CDEBUG(D_INODE, "in symlink result %d\n",error); + unlock_kernel(); return error; } @@ -407,6 +441,7 @@ const char *name = de->d_name.name; int len = de->d_name.len; + lock_kernel(); coda_vfs_stat.unlink++; CDEBUG(D_INODE, " %s in %s, dirino %ld\n", name , @@ -415,11 +450,13 @@ error = venus_remove(dir->i_sb, coda_i2f(dir), name, len); if ( error ) { CDEBUG(D_INODE, "upc returned error %d\n", error); + unlock_kernel(); return error; } coda_dir_changed(dir, 0); de->d_inode->i_nlink--; + unlock_kernel(); return 0; } @@ -430,20 +467,25 @@ int len = de->d_name.len; int error; + lock_kernel(); coda_vfs_stat.rmdir++; - if (!d_unhashed(de)) + if (!d_unhashed(de)) { + unlock_kernel(); return -EBUSY; + } error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len); if ( error ) { CDEBUG(D_INODE, "upc returned error %d\n", error); + unlock_kernel(); return error; } coda_dir_changed(dir, -1); de->d_inode->i_nlink--; d_delete(de); + unlock_kernel(); return 0; } @@ -459,6 +501,7 @@ int link_adjust = 0; int error; + lock_kernel(); coda_vfs_stat.rename++; CDEBUG(D_INODE, "old: %s, (%d length), new: %s" @@ -485,6 +528,7 @@ } CDEBUG(D_INODE, "result %d\n", error); + unlock_kernel(); return error; } diff -Nru a/fs/cramfs/inode.c b/fs/cramfs/inode.c --- a/fs/cramfs/inode.c Tue Feb 19 18:08:57 2002 +++ b/fs/cramfs/inode.c Tue Feb 19 18:08:57 2002 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -330,8 +331,10 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry) { unsigned int offset = 0; - int sorted = dir->i_sb->CRAMFS_SB_FLAGS & CRAMFS_FLAG_SORTED_DIRS; + int sorted; + lock_kernel(); + sorted = dir->i_sb->CRAMFS_SB_FLAGS & CRAMFS_FLAG_SORTED_DIRS; while (offset < dir->i_size) { struct cramfs_inode *de; char *name; @@ -354,8 +357,10 @@ continue; for (;;) { - if (!namelen) + if (!namelen) { + unlock_kernel(); return ERR_PTR(-EIO); + } if (name[namelen-1]) break; namelen--; @@ -367,12 +372,14 @@ continue; if (!retval) { d_add(dentry, get_cramfs_inode(dir->i_sb, de)); + unlock_kernel(); return NULL; } /* else (retval < 0) */ if (sorted) break; } + unlock_kernel(); d_add(dentry, NULL); return NULL; } diff -Nru a/fs/devfs/base.c b/fs/devfs/base.c --- a/fs/devfs/base.c Tue Feb 19 18:08:58 2002 +++ b/fs/devfs/base.c Tue Feb 19 18:08:58 2002 @@ -2678,7 +2678,7 @@ case 0: scan_dir_for_removable (parent); err = (*filldir) (dirent, "..", 2, file->f_pos, - file->f_dentry->d_parent->d_inode->i_ino, DT_DIR); + parent_ino(file->f_dentry), DT_DIR); if (err == -EINVAL) break; if (err < 0) return err; file->f_pos++; @@ -2944,10 +2944,14 @@ up on any error */ dentry->d_op = &devfs_dops; /* First try to get the devfs entry for this directory */ + lock_kernel(); parent = get_devfs_entry_from_vfs_inode (dir); DPRINTK (DEBUG_I_LOOKUP, "(%s): dentry: %p parent: %p by: \"%s\"\n", dentry->d_name.name, dentry, parent, current->comm); - if (parent == NULL) return ERR_PTR (-ENOENT); + if (parent == NULL) { + unlock_kernel(); + return ERR_PTR (-ENOENT); + } 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); @@ -2972,6 +2976,7 @@ if (try_modload (parent, fs_info, dentry->d_name.name, dentry->d_name.len, &tmp) < 0) { /* Lookup event was not queued to devfsd */ + unlock_kernel(); d_add (dentry, NULL); return NULL; } @@ -3014,6 +3019,7 @@ wake_up (&lookup_info.wait_queue); write_unlock (&parent->u.dir.lock); devfs_put (de); + unlock_kernel(); return retval; } /* End Function devfs_lookup */ @@ -3024,19 +3030,30 @@ struct inode *inode = dentry->d_inode; struct fs_info *fs_info = dir->i_sb->u.generic_sbp; + lock_kernel(); de = get_devfs_entry_from_vfs_inode (inode); DPRINTK (DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de); - if (de == NULL) return -ENOENT; - if (!de->vfs_deletable) return -EPERM; + if (de == NULL) { + unlock_kernel(); + return -ENOENT; + } + if (!de->vfs_deletable) { + unlock_kernel(); + return -EPERM; + } write_lock (&de->parent->u.dir.lock); unhooked = _devfs_unhook (de); write_unlock (&de->parent->u.dir.lock); - if (!unhooked) return -ENOENT; + if (!unhooked) { + unlock_kernel(); + return -ENOENT; + } if ( !is_devfsd_or_child (fs_info) ) devfsd_notify_de (de, DEVFSD_NOTIFY_DELETE, inode->i_mode, inode->i_uid, inode->i_gid, fs_info, 0); free_dentry (de); devfs_put (de); + unlock_kernel(); return 0; } /* End Function devfs_unlink */ @@ -3049,27 +3066,37 @@ struct inode *inode; /* First try to get the devfs entry for this directory */ + lock_kernel(); parent = get_devfs_entry_from_vfs_inode (dir); - if (parent == NULL) return -ENOENT; + if (parent == NULL) { + unlock_kernel(); + return -ENOENT; + } err = devfs_do_symlink (parent, dentry->d_name.name, DEVFS_FL_NONE, symname, &de, NULL); DPRINTK (DEBUG_DISABLED, "(%s): errcode from : %d\n", dentry->d_name.name, err); - if (err < 0) return err; + if (err < 0) { + unlock_kernel(); + return err; + } de->vfs_deletable = TRUE; de->inode.uid = current->euid; de->inode.gid = current->egid; de->inode.atime = CURRENT_TIME; de->inode.mtime = CURRENT_TIME; de->inode.ctime = CURRENT_TIME; - if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) + if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) { + unlock_kernel(); return -ENOMEM; + } DPRINTK (DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n", dentry->d_name.name, de->inode.ino, inode, dentry); d_instantiate (dentry, inode); if ( !is_devfsd_or_child (fs_info) ) devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode, inode->i_uid, inode->i_gid, fs_info, 0); + unlock_kernel(); return 0; } /* End Function devfs_symlink */ @@ -3081,26 +3108,38 @@ struct inode *inode; mode = (mode & ~S_IFMT) | S_IFDIR; /* VFS doesn't pass S_IFMT part */ + lock_kernel(); parent = get_devfs_entry_from_vfs_inode (dir); - if (parent == NULL) return -ENOENT; + if (parent == NULL) { + unlock_kernel(); + return -ENOENT; + } de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode); - if (!de) return -ENOMEM; + if (!de) { + unlock_kernel(); + return -ENOMEM; + } de->vfs_deletable = TRUE; - if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 ) + if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 ) { + unlock_kernel(); return err; + } de->inode.uid = current->euid; de->inode.gid = current->egid; de->inode.atime = CURRENT_TIME; de->inode.mtime = CURRENT_TIME; de->inode.ctime = CURRENT_TIME; - if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) + if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) { + unlock_kernel(); return -ENOMEM; + } DPRINTK (DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n", dentry->d_name.name, de->inode.ino, inode, dentry); d_instantiate (dentry, inode); if ( !is_devfsd_or_child (fs_info) ) devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode, inode->i_uid, inode->i_gid, fs_info, 0); + unlock_kernel(); return 0; } /* End Function devfs_mkdir */ @@ -3111,27 +3150,45 @@ struct fs_info *fs_info = dir->i_sb->u.generic_sbp; struct inode *inode = dentry->d_inode; + /* WTF??? */ if (dir->i_sb->u.generic_sbp != inode->i_sb->u.generic_sbp) return -EINVAL; + lock_kernel(); de = get_devfs_entry_from_vfs_inode (inode); - if (de == NULL) return -ENOENT; - if ( !S_ISDIR (de->mode) ) return -ENOTDIR; - if (!de->vfs_deletable) return -EPERM; + if (de == NULL) { + unlock_kernel(); + return -ENOENT; + } + if ( !S_ISDIR (de->mode) ) { + unlock_kernel(); + return -ENOTDIR; + } + if (!de->vfs_deletable) { + unlock_kernel(); + return -EPERM; + } /* First ensure the directory is empty and will stay thay way */ write_lock (&de->u.dir.lock); de->u.dir.no_more_additions = TRUE; if (de->u.dir.first) err = -ENOTEMPTY; write_unlock (&de->u.dir.lock); - if (err) return err; + if (err) { + unlock_kernel(); + return err; + } /* Now unhook the directory from it's parent */ write_lock (&de->parent->u.dir.lock); if ( !_devfs_unhook (de) ) err = -ENOENT; write_unlock (&de->parent->u.dir.lock); - if (err) return err; + if (err) { + unlock_kernel(); + return err; + } if ( !is_devfsd_or_child (fs_info) ) devfsd_notify_de (de, DEVFSD_NOTIFY_DELETE, inode->i_mode, inode->i_uid, inode->i_gid, fs_info, 0); free_dentry (de); devfs_put (de); + unlock_kernel(); return 0; } /* End Function devfs_rmdir */ @@ -3145,31 +3202,43 @@ DPRINTK (DEBUG_I_MKNOD, "(%s): mode: 0%o dev: %d\n", dentry->d_name.name, mode, rdev); + lock_kernel(); parent = get_devfs_entry_from_vfs_inode (dir); - if (parent == NULL) return -ENOENT; + if (parent == NULL) { + unlock_kernel(); + return -ENOENT; + } de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode); - if (!de) return -ENOMEM; + if (!de) { + unlock_kernel(); + return -ENOMEM; + } de->vfs_deletable = TRUE; if ( S_ISBLK (mode) || S_ISCHR (mode) ) { de->u.fcb.u.device.major = MAJOR (rdev); de->u.fcb.u.device.minor = MINOR (rdev); } - if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 ) + if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 ) { + unlock_kernel(); return err; + } de->inode.uid = current->euid; de->inode.gid = current->egid; de->inode.atime = CURRENT_TIME; de->inode.mtime = CURRENT_TIME; de->inode.ctime = CURRENT_TIME; - if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) + if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) { + unlock_kernel(); return -ENOMEM; + } DPRINTK (DEBUG_I_MKNOD, ": new VFS inode(%u): %p dentry: %p\n", de->inode.ino, inode, dentry); d_instantiate (dentry, inode); if ( !is_devfsd_or_child (fs_info) ) devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode, inode->i_uid, inode->i_gid, fs_info, 0); + unlock_kernel(); return 0; } /* End Function devfs_mknod */ diff -Nru a/fs/devpts/root.c b/fs/devpts/root.c --- a/fs/devpts/root.c Tue Feb 19 18:08:57 2002 +++ b/fs/devpts/root.c Tue Feb 19 18:08:57 2002 @@ -14,6 +14,7 @@ #include #include #include +#include #include "devpts_i.h" static int devpts_root_readdir(struct file *,void *,filldir_t); @@ -126,10 +127,12 @@ if ( entry >= sbi->max_ptys ) return NULL; + lock_kernel(); if ( sbi->inodes[entry] ) atomic_inc(&sbi->inodes[entry]->i_count); d_add(dentry, sbi->inodes[entry]); + unlock_kernel(); return NULL; } diff -Nru a/fs/dquot.c b/fs/dquot.c --- a/fs/dquot.c Tue Feb 19 18:08:58 2002 +++ b/fs/dquot.c Tue Feb 19 18:08:58 2002 @@ -1039,6 +1039,7 @@ struct dquot *dquot[MAXQUOTAS]; char warntype[MAXQUOTAS]; + lock_kernel(); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot[cnt] = NODQUOT; warntype[cnt] = NOWARN; @@ -1064,6 +1065,7 @@ for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (dquot[cnt] != NODQUOT) dqput(dquot[cnt]); + unlock_kernel(); return ret; } @@ -1081,6 +1083,7 @@ warntype[cnt] = NOWARN; } /* NOBLOCK Start */ + lock_kernel(); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot[cnt] = dqduplicate(inode -> i_dquot[cnt]); if (dquot[cnt] == NODQUOT) @@ -1101,6 +1104,7 @@ for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (dquot[cnt] != NODQUOT) dqput(dquot[cnt]); + unlock_kernel(); return ret; } @@ -1113,6 +1117,7 @@ struct dquot *dquot; /* NOBLOCK Start */ + lock_kernel(); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot = dqduplicate(inode->i_dquot[cnt]); if (dquot == NODQUOT) @@ -1121,6 +1126,7 @@ dqput(dquot); } inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9); + unlock_kernel(); /* NOBLOCK End */ } @@ -1133,6 +1139,7 @@ struct dquot *dquot; /* NOBLOCK Start */ + lock_kernel(); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot = dqduplicate(inode->i_dquot[cnt]); if (dquot == NODQUOT) @@ -1140,6 +1147,7 @@ dquot_decr_inodes(dquot, number); dqput(dquot); } + unlock_kernel(); /* NOBLOCK End */ } diff -Nru a/fs/driverfs/inode.c b/fs/driverfs/inode.c --- a/fs/driverfs/inode.c Tue Feb 19 18:08:58 2002 +++ b/fs/driverfs/inode.c Tue Feb 19 18:08:58 2002 @@ -48,14 +48,12 @@ #define DRIVERFS_MAGIC 0x42454552 static struct super_operations driverfs_ops; -static struct address_space_operations driverfs_aops; static struct file_operations driverfs_dir_operations; static struct file_operations driverfs_file_operations; static struct inode_operations driverfs_dir_inode_operations; static struct dentry_operations driverfs_dentry_dir_ops; static struct dentry_operations driverfs_dentry_file_ops; - static struct vfsmount *driverfs_mount; static spinlock_t mount_lock = SPIN_LOCK_UNLOCKED; static int mount_count = 0; @@ -68,6 +66,7 @@ return 0; } +/* SMP-safe */ static struct dentry *driverfs_lookup(struct inode *dir, struct dentry *dentry) { d_add(dentry, NULL); @@ -85,7 +84,6 @@ inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_rdev = NODEV; - inode->i_mapping->a_ops = &driverfs_aops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { default: @@ -120,29 +118,22 @@ static int driverfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { + int res; + lock_kernel(); dentry->d_op = &driverfs_dentry_dir_ops; - return driverfs_mknod(dir, dentry, mode | S_IFDIR, 0); + res = driverfs_mknod(dir, dentry, mode | S_IFDIR, 0); + unlock_kernel(); + return res; } static int driverfs_create(struct inode *dir, struct dentry *dentry, int mode) { + int res; + lock_kernel(); dentry->d_op = &driverfs_dentry_file_ops; - return driverfs_mknod(dir, dentry, mode | S_IFREG, 0); -} - -static int -driverfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = old_dentry->d_inode; - - if(S_ISDIR(inode->i_mode)) - return -EPERM; - - inode->i_nlink++; - atomic_inc(&inode->i_count); - dget(dentry); - d_instantiate(dentry, inode); - return 0; + res = driverfs_mknod(dir, dentry, mode | S_IFREG, 0); + unlock_kernel(); + return res; } static inline int driverfs_positive(struct dentry *dentry) @@ -175,43 +166,15 @@ if (driverfs_empty(dentry)) { struct inode *inode = dentry->d_inode; + lock_kernel(); inode->i_nlink--; + unlock_kernel(); dput(dentry); error = 0; } return error; } -static int -driverfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - int error = -ENOTEMPTY; - - if (driverfs_empty(new_dentry)) { - struct inode *inode = new_dentry->d_inode; - if (inode) { - inode->i_nlink--; - dput(new_dentry); - } - error = 0; - } - return error; -} - -static int driverfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -{ - int error; - - error = driverfs_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0); - if (!error) { - int l = strlen(symname) + 1; - struct inode *inode = dentry->d_inode; - error = block_symlink(inode,symname,l); - } - return error; -} - #define driverfs_rmdir driverfs_unlink /** @@ -394,14 +357,6 @@ return 0; } -static int driverfs_sync_file (struct file *file, struct dentry *dentry, int datasync) -{ - return 0; -} - -/* When the dentry goes away (the refcount hits 0), free the - * driver_file_entry for it. - */ static int driverfs_d_delete_file (struct dentry * dentry) { struct driver_file_entry * entry; @@ -412,36 +367,25 @@ return 0; } -static struct address_space_operations driverfs_aops = { - -}; - static struct file_operations driverfs_file_operations = { read: driverfs_read_file, write: driverfs_write_file, llseek: driverfs_file_lseek, - mmap: generic_file_mmap, open: driverfs_open_file, release: driverfs_release, - fsync: driverfs_sync_file, }; static struct file_operations driverfs_dir_operations = { read: generic_read_dir, readdir: dcache_readdir, - fsync: driverfs_sync_file, }; static struct inode_operations driverfs_dir_inode_operations = { create: driverfs_create, lookup: driverfs_lookup, - link: driverfs_link, unlink: driverfs_unlink, - symlink: driverfs_symlink, mkdir: driverfs_mkdir, rmdir: driverfs_rmdir, - mknod: driverfs_mknod, - rename: driverfs_rename, }; static struct dentry_operations driverfs_dentry_file_ops = { @@ -545,35 +489,9 @@ int __init init_driverfs_fs(void) { - int error; - - DBG("%s: registering filesystem.\n",__FUNCTION__); - error = register_filesystem(&driverfs_fs_type); - - if (error) - DBG(KERN_ERR "%s: register_filesystem failed with %d\n", - __FUNCTION__,error); - - return error; + return register_filesystem(&driverfs_fs_type); } -static void __exit exit_driverfs_fs(void) -{ - unregister_filesystem(&driverfs_fs_type); -} -#if 0 -module_init(init_driverfs_fs); -module_exit(exit_driverfs_fs); -#endif - -EXPORT_SYMBOL(driverfs_create_file); -EXPORT_SYMBOL(driverfs_create_dir); -EXPORT_SYMBOL(driverfs_remove_file); -EXPORT_SYMBOL(driverfs_remove_dir); - -MODULE_DESCRIPTION("The device driver filesystem"); -MODULE_LICENSE("GPL"); - /** * driverfs_create_dir - create a directory in the filesystem * @entry: directory entry @@ -604,24 +522,18 @@ return -EFAULT; } - dget(parent_dentry); down(&parent_dentry->d_inode->i_sem); - qstr.name = entry->name; qstr.len = strlen(entry->name); qstr.hash = full_name_hash(entry->name,qstr.len); - dentry = lookup_hash(&qstr,parent_dentry); - if (IS_ERR(dentry)) { - error = PTR_ERR(dentry); - up(&parent_dentry->d_inode->i_sem); - dput(parent_dentry); - } else { + if (!IS_ERR(dentry)) { dentry->d_fsdata = (void *) entry; entry->dentry = dentry; error = vfs_mkdir(parent_dentry->d_inode,dentry,entry->mode); - up(&parent_dentry->d_inode->i_sem); - } + } else + error = PTR_ERR(dentry); + up(&parent_dentry->d_inode->i_sem); if (error) put_mount(); @@ -638,7 +550,6 @@ struct driver_dir_entry * parent) { struct dentry * dentry; - struct dentry * parent_dentry; struct qstr qstr; int error = 0; @@ -653,64 +564,31 @@ return -EINVAL; } - /* make sure daddy doesn't run out on us again... */ - parent_dentry = dget(parent->dentry); - down(&parent_dentry->d_inode->i_sem); - + down(&parent->dentry->d_inode->i_sem); qstr.name = entry->name; qstr.len = strlen(entry->name); qstr.hash = full_name_hash(entry->name,qstr.len); - - dentry = lookup_hash(&qstr,parent_dentry); - if (IS_ERR(dentry)) - error = PTR_ERR(dentry); - else { + dentry = lookup_hash(&qstr,parent->dentry); + if (!IS_ERR(dentry)) { dentry->d_fsdata = (void *)entry; - error = vfs_create(parent_dentry->d_inode,dentry,entry->mode); - } - - /* Still good? Ok, then fill in the blanks: */ - if (!error) { - dentry->d_inode->u.generic_ip = (void *)entry; - - entry->dentry = dentry; - entry->parent = parent; - - list_add_tail(&entry->node,&parent->files); - } - up(&parent_dentry->d_inode->i_sem); + error = vfs_create(parent->dentry->d_inode,dentry,entry->mode); - if (error) { - dput(parent_dentry); + /* Still good? Ok, then fill in the blanks: */ + if (!error) { + dentry->d_inode->u.generic_ip = (void *)entry; + entry->dentry = dentry; + entry->parent = parent; + list_add_tail(&entry->node,&parent->files); + } + } else + error = PTR_ERR(dentry); + up(&parent->dentry->d_inode->i_sem); + if (error) put_mount(); - } return error; } /** - * __remove_file - remove a regular file in the filesystem - * @dentry: dentry of file to remove - * - * Call unlink to remove the file, and dput on the dentry to drop - * the refcount. - */ -static void __remove_file(struct dentry * dentry) -{ - dget(dentry); - down(&dentry->d_inode->i_sem); - - vfs_unlink(dentry->d_parent->d_inode,dentry); - - up(&dentry->d_inode->i_sem); - dput(dentry); - - /* remove reference count from when file was created */ - dput(dentry); - - put_mount(); -} - -/** * driverfs_remove_file - exported file removal * @dir: directory the file supposedly resides in * @name: name of the file @@ -720,14 +598,12 @@ */ void driverfs_remove_file(struct driver_dir_entry * dir, const char * name) { - struct dentry * dentry; struct list_head * node; if (!dir->dentry) return; - dentry = dget(dir->dentry); - down(&dentry->d_inode->i_sem); + down(&dir->dentry->d_inode->i_sem); node = dir->files.next; while (node != &dir->files) { @@ -736,14 +612,13 @@ entry = list_entry(node,struct driver_file_entry,node); if (!strcmp(entry->name,name)) { list_del_init(node); - - __remove_file(entry->dentry); + vfs_unlink(entry->dentry->d_parent->d_inode,entry->dentry); + put_mount(); break; } node = node->next; } - up(&dentry->d_inode->i_sem); - dput(dentry); + up(&dir->dentry->d_inode->i_sem); } /** @@ -757,13 +632,12 @@ void driverfs_remove_dir(struct driver_dir_entry * dir) { struct list_head * node; - struct dentry * dentry; + struct dentry * dentry = dir->dentry; - if (!dir->dentry) + if (!dentry) goto done; - dentry = dget(dir->dentry); - dget(dentry->d_parent); + dget(dentry); down(&dentry->d_parent->d_inode->i_sem); down(&dentry->d_inode->i_sem); @@ -773,18 +647,20 @@ entry = list_entry(node,struct driver_file_entry,node); list_del_init(node); - - __remove_file(entry->dentry); - + vfs_unlink(dentry->d_inode,entry->dentry); + put_mount(); node = dir->files.next; } + up(&dentry->d_inode->i_sem); vfs_rmdir(dentry->d_parent->d_inode,dentry); up(&dentry->d_parent->d_inode->i_sem); - up(&dentry->d_inode->i_sem); - - /* remove reference count from when directory was created */ dput(dentry); done: put_mount(); } + +EXPORT_SYMBOL(driverfs_create_file); +EXPORT_SYMBOL(driverfs_create_dir); +EXPORT_SYMBOL(driverfs_remove_file); +EXPORT_SYMBOL(driverfs_remove_dir); diff -Nru a/fs/efs/namei.c b/fs/efs/namei.c --- a/fs/efs/namei.c Tue Feb 19 18:08:57 2002 +++ b/fs/efs/namei.c Tue Feb 19 18:08:57 2002 @@ -8,6 +8,7 @@ #include #include +#include static efs_ino_t efs_find_entry(struct inode *inode, const char *name, int len) { struct buffer_head *bh; @@ -57,18 +58,17 @@ struct dentry *efs_lookup(struct inode *dir, struct dentry *dentry) { efs_ino_t inodenum; - struct inode * inode; - - if (!dir || !S_ISDIR(dir->i_mode)) - return ERR_PTR(-ENOENT); - - inode = NULL; + struct inode * inode = NULL; + lock_kernel(); inodenum = efs_find_entry(dir, dentry->d_name.name, dentry->d_name.len); if (inodenum) { - if (!(inode = iget(dir->i_sb, inodenum))) + if (!(inode = iget(dir->i_sb, inodenum))) { + unlock_kernel(); return ERR_PTR(-EACCES); + } } + unlock_kernel(); d_add(dentry, inode); return NULL; diff -Nru a/fs/exec.c b/fs/exec.c --- a/fs/exec.c Tue Feb 19 18:08:59 2002 +++ b/fs/exec.c Tue Feb 19 18:08:59 2002 @@ -271,15 +271,18 @@ pmd = pmd_alloc(tsk->mm, pgd, address); if (!pmd) goto out; - pte = pte_alloc(tsk->mm, pmd, address); + pte = pte_alloc_map(tsk->mm, pmd, address); if (!pte) goto out; - if (!pte_none(*pte)) + if (!pte_none(*pte)) { + pte_unmap(pte); goto out; + } lru_cache_add(page); flush_dcache_page(page); flush_page_to_ram(page); set_pte(pte, pte_mkdirty(pte_mkwrite(mk_pte(page, PAGE_COPY)))); + pte_unmap(pte); tsk->mm->rss++; spin_unlock(&tsk->mm->page_table_lock); diff -Nru a/fs/ext2/balloc.c b/fs/ext2/balloc.c --- a/fs/ext2/balloc.c Tue Feb 19 18:08:59 2002 +++ b/fs/ext2/balloc.c Tue Feb 19 18:08:59 2002 @@ -504,27 +504,21 @@ /* * Do block preallocation now if required. */ - /* Writer: ->i_prealloc* */ + write_lock(&EXT2_I(inode)->i_meta_lock); if (group_alloc && !*prealloc_count) { - unsigned long next_block = block + 1; + unsigned n; - *prealloc_block = next_block; - /* Writer: end */ - while (group_alloc && ++j < group_size) { - /* Writer: ->i_prealloc* */ - if (*prealloc_block + *prealloc_count != next_block || - ext2_set_bit (j, bh->b_data)) { - /* Writer: end */ + for (n = 0; n < group_alloc && ++j < group_size; n++) { + if (ext2_set_bit (j, bh->b_data)) break; - } - (*prealloc_count)++; - /* Writer: end */ - next_block++; - es_alloc--; - dq_alloc--; - group_alloc--; - } + } + *prealloc_block = block + 1; + *prealloc_count = n; + es_alloc -= n; + dq_alloc -= n; + group_alloc -= n; } + write_unlock(&EXT2_I(inode)->i_meta_lock); mark_buffer_dirty(bh); if (sb->s_flags & MS_SYNCHRONOUS) { diff -Nru a/fs/ext2/dir.c b/fs/ext2/dir.c --- a/fs/ext2/dir.c Tue Feb 19 18:08:57 2002 +++ b/fs/ext2/dir.c Tue Feb 19 18:08:57 2002 @@ -52,8 +52,13 @@ int err = 0; dir->i_version = ++event; page->mapping->a_ops->commit_write(NULL, page, from, to); - if (IS_SYNC(dir)) - err = waitfor_one_page(page); + if (IS_SYNC(dir)) { + int err2; + err = writeout_one_page(page); + err2 = waitfor_one_page(page); + if (err == 0) + err = err2; + } return err; } diff -Nru a/fs/ext2/ext2.h b/fs/ext2/ext2.h --- a/fs/ext2/ext2.h Tue Feb 19 18:08:59 2002 +++ b/fs/ext2/ext2.h Tue Feb 19 18:08:59 2002 @@ -20,6 +20,7 @@ __u32 i_prealloc_block; __u32 i_prealloc_count; __u32 i_dir_start_lookup; + rwlock_t i_meta_lock; struct inode vfs_inode; }; diff -Nru a/fs/ext2/inode.c b/fs/ext2/inode.c --- a/fs/ext2/inode.c Tue Feb 19 18:08:57 2002 +++ b/fs/ext2/inode.c Tue Feb 19 18:08:57 2002 @@ -34,7 +34,6 @@ MODULE_DESCRIPTION("Second Extended Filesystem"); MODULE_LICENSE("GPL"); - static int ext2_update_inode(struct inode * inode, int do_sync); /* @@ -50,8 +49,6 @@ */ void ext2_delete_inode (struct inode * inode) { - lock_kernel(); - if (is_bad_inode(inode) || inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) @@ -59,15 +56,14 @@ EXT2_I(inode)->i_dtime = CURRENT_TIME; mark_inode_dirty(inode); ext2_update_inode(inode, IS_SYNC(inode)); + inode->i_size = 0; if (inode->i_blocks) ext2_truncate (inode); ext2_free_inode (inode); - unlock_kernel(); return; no_delete: - unlock_kernel(); clear_inode(inode); /* We must guarantee clearing of inode... */ } @@ -75,17 +71,17 @@ { #ifdef EXT2_PREALLOCATE struct ext2_inode_info *ei = EXT2_I(inode); - lock_kernel(); - /* Writer: ->i_prealloc* */ + write_lock(&ei->i_meta_lock); if (ei->i_prealloc_count) { unsigned short total = ei->i_prealloc_count; unsigned long block = ei->i_prealloc_block; ei->i_prealloc_count = 0; ei->i_prealloc_block = 0; - /* Writer: end */ + write_unlock(&ei->i_meta_lock); ext2_free_blocks (inode, block, total); - } - unlock_kernel(); + return; + } else + write_unlock(&ei->i_meta_lock); #endif } @@ -99,17 +95,17 @@ #ifdef EXT2_PREALLOCATE struct ext2_inode_info *ei = EXT2_I(inode); - /* Writer: ->i_prealloc* */ + write_lock(&ei->i_meta_lock); if (ei->i_prealloc_count && - (goal == ei->i_prealloc_block || - goal + 1 == ei->i_prealloc_block)) - { + (goal == ei->i_prealloc_block || goal + 1 == ei->i_prealloc_block)) + { result = ei->i_prealloc_block++; ei->i_prealloc_count--; - /* Writer: end */ + write_unlock(&ei->i_meta_lock); ext2_debug ("preallocation hit (%lu/%lu).\n", ++alloc_hits, ++alloc_attempts); } else { + write_unlock(&ei->i_meta_lock); ext2_discard_prealloc (inode); ext2_debug ("preallocation miss (%lu/%lu).\n", alloc_hits, ++alloc_attempts); @@ -253,17 +249,18 @@ bh = sb_bread(sb, le32_to_cpu(p->key)); if (!bh) goto failure; - /* Reader: pointers */ + read_lock(&EXT2_I(inode)->i_meta_lock); if (!verify_chain(chain, p)) goto changed; add_chain(++p, bh, (u32*)bh->b_data + *++offsets); - /* Reader: end */ + read_unlock(&EXT2_I(inode)->i_meta_lock); if (!p->key) goto no_block; } return NULL; changed: + read_unlock(&EXT2_I(inode)->i_meta_lock); *err = -EAGAIN; goto no_block; failure: @@ -329,13 +326,11 @@ unsigned long *goal) { struct ext2_inode_info *ei = EXT2_I(inode); - /* Writer: ->i_next_alloc* */ + write_lock(&ei->i_meta_lock); if (block == ei->i_next_alloc_block + 1) { ei->i_next_alloc_block++; ei->i_next_alloc_goal++; } - /* Writer: end */ - /* Reader: pointers, ->i_next_alloc* */ if (verify_chain(chain, partial)) { /* * try the heuristic for sequential allocation, @@ -345,9 +340,10 @@ *goal = ei->i_next_alloc_goal; if (!*goal) *goal = ext2_find_near(inode, partial); + write_unlock(&ei->i_meta_lock); return 0; } - /* Reader: end */ + write_unlock(&ei->i_meta_lock); return -EAGAIN; } @@ -454,9 +450,8 @@ /* Verify that place we are splicing to is still there and vacant */ - /* Writer: pointers, ->i_next_alloc* */ + write_lock(&ei->i_meta_lock); if (!verify_chain(chain, where-1) || *where->p) - /* Writer: end */ goto changed; /* That's it */ @@ -465,7 +460,7 @@ ei->i_next_alloc_block = block; ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key); - /* Writer: end */ + write_unlock(&ei->i_meta_lock); /* We are done with atomic stuff, now do the rest of housekeeping */ @@ -487,6 +482,7 @@ return 0; changed: + write_unlock(&ei->i_meta_lock); for (i = 1; i < num; i++) bforget(where[i].bh); for (i = 0; i < num; i++) @@ -520,7 +516,6 @@ if (depth == 0) goto out; - lock_kernel(); reread: partial = ext2_get_branch(inode, depth, offsets, chain, &err); @@ -540,7 +535,6 @@ brelse(partial->bh); partial--; } - unlock_kernel(); out: return err; } @@ -666,16 +660,17 @@ for (k = depth; k > 1 && !offsets[k-1]; k--) ; partial = ext2_get_branch(inode, k, offsets, chain, &err); - /* Writer: pointers */ if (!partial) partial = chain + k-1; /* * If the branch acquired continuation since we've looked at it - * fine, it should all survive and (new) top doesn't belong to us. */ - if (!partial->key && *partial->p) - /* Writer: end */ + write_lock(&EXT2_I(inode)->i_meta_lock); + if (!partial->key && *partial->p) { + write_unlock(&EXT2_I(inode)->i_meta_lock); goto no_top; + } for (p=partial; p>chain && all_zeroes((u32*)p->bh->b_data,p->p); p--) ; /* @@ -690,7 +685,7 @@ *top = *p->p; *p->p = 0; } - /* Writer: end */ + write_unlock(&EXT2_I(inode)->i_meta_lock); while(partial > p) { @@ -879,62 +874,64 @@ mark_inode_dirty(inode); } -void ext2_read_inode (struct inode * inode) +static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino, + struct buffer_head **p) { struct buffer_head * bh; - struct ext2_inode * raw_inode; unsigned long block_group; - unsigned long group_desc; - unsigned long desc; unsigned long block; unsigned long offset; struct ext2_group_desc * gdp; - struct ext2_inode_info *ei = EXT2_I(inode); - - if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO && - inode->i_ino != EXT2_ACL_DATA_INO && - inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) || - inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) { - ext2_error (inode->i_sb, "ext2_read_inode", - "bad inode number: %lu", inode->i_ino); - goto bad_inode; - } - block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb); - if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count) { - ext2_error (inode->i_sb, "ext2_read_inode", - "group >= groups count"); - goto bad_inode; - } - group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb); - desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1); - bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc]; - if (!bh) { - ext2_error (inode->i_sb, "ext2_read_inode", - "Descriptor not loaded"); - goto bad_inode; - } - gdp = (struct ext2_group_desc *) bh->b_data; + *p = NULL; + if ((ino != EXT2_ROOT_INO && ino != EXT2_ACL_IDX_INO && + ino != EXT2_ACL_DATA_INO && ino < EXT2_FIRST_INO(sb)) || + ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) + goto Einval; + + block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb); + gdp = ext2_get_group_desc(sb, block_group, &bh); + if (!gdp) + goto Egdp; /* * Figure out the offset within the block group inode table */ - offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) * - EXT2_INODE_SIZE(inode->i_sb); - block = le32_to_cpu(gdp[desc].bg_inode_table) + - (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb)); - if (!(bh = sb_bread(inode->i_sb, block))) { - ext2_error (inode->i_sb, "ext2_read_inode", - "unable to read inode block - " - "inode=%lu, block=%lu", inode->i_ino, block); - goto bad_inode; - } - offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1); - raw_inode = (struct ext2_inode *) (bh->b_data + offset); + offset = ((ino - 1) % EXT2_INODES_PER_GROUP(sb)) * EXT2_INODE_SIZE(sb); + block = le32_to_cpu(gdp->bg_inode_table) + + (offset >> EXT2_BLOCK_SIZE_BITS(sb)); + if (!(bh = sb_bread(sb, block))) + goto Eio; + + *p = bh; + offset &= (EXT2_BLOCK_SIZE(sb) - 1); + return (struct ext2_inode *) (bh->b_data + offset); + +Einval: + ext2_error(sb, "ext2_get_inode", "bad inode number: %lu", ino); + return ERR_PTR(-EINVAL); +Eio: + ext2_error(sb, "ext2_get_inode", + "unable to read inode block - inode=%lu, block=%lu", + ino, block); +Egdp: + return ERR_PTR(-EIO); +} + +void ext2_read_inode (struct inode * inode) +{ + struct ext2_inode_info *ei = EXT2_I(inode); + ino_t ino = inode->i_ino; + struct buffer_head * bh; + struct ext2_inode * raw_inode = ext2_get_inode(inode->i_sb, ino, &bh); + int n; + + if (IS_ERR(raw_inode)) + goto bad_inode; inode->i_mode = le16_to_cpu(raw_inode->i_mode); inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low); - if(!(test_opt (inode->i_sb, NO_UID32))) { + if (!(test_opt (inode->i_sb, NO_UID32))) { inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16; inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16; } @@ -973,18 +970,17 @@ ei->i_next_alloc_block = 0; ei->i_next_alloc_goal = 0; ei->i_prealloc_count = 0; - ei->i_block_group = block_group; + ei->i_block_group = (ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb); ei->i_dir_start_lookup = 0; /* * NOTE! The in-memory inode i_data array is in little-endian order * even on big-endian machines: we do NOT byteswap the block numbers! */ - for (block = 0; block < EXT2_N_BLOCKS; block++) - ei->i_data[block] = raw_inode->i_block[block]; + for (n = 0; n < EXT2_N_BLOCKS; n++) + ei->i_data[n] = raw_inode->i_block[n]; - if (inode->i_ino == EXT2_ACL_IDX_INO || - inode->i_ino == EXT2_ACL_DATA_INO) + if (ino == EXT2_ACL_IDX_INO || ino == EXT2_ACL_DATA_INO) /* Nothing to do */ ; else if (S_ISREG(inode->i_mode)) { inode->i_op = &ext2_file_inode_operations; @@ -1031,73 +1027,42 @@ static int ext2_update_inode(struct inode * inode, int do_sync) { + struct ext2_inode_info *ei = EXT2_I(inode); + struct super_block *sb = inode->i_sb; + ino_t ino = inode->i_ino; + uid_t uid = inode->i_uid; + gid_t gid = inode->i_gid; struct buffer_head * bh; - struct ext2_inode * raw_inode; - unsigned long block_group; - unsigned long group_desc; - unsigned long desc; - unsigned long block; - unsigned long offset; + struct ext2_inode * raw_inode = ext2_get_inode(sb, ino, &bh); + int n; int err = 0; - struct ext2_group_desc * gdp; - struct ext2_inode_info *ei = EXT2_I(inode); - if ((inode->i_ino != EXT2_ROOT_INO && - inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) || - inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) { - ext2_error (inode->i_sb, "ext2_write_inode", - "bad inode number: %lu", inode->i_ino); - return -EIO; - } - block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb); - if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count) { - ext2_error (inode->i_sb, "ext2_write_inode", - "group >= groups count"); - return -EIO; - } - group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb); - desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1); - bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc]; - if (!bh) { - ext2_error (inode->i_sb, "ext2_write_inode", - "Descriptor not loaded"); - return -EIO; - } - gdp = (struct ext2_group_desc *) bh->b_data; - /* - * Figure out the offset within the block group inode table - */ - offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) * - EXT2_INODE_SIZE(inode->i_sb); - block = le32_to_cpu(gdp[desc].bg_inode_table) + - (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb)); - if (!(bh = sb_bread(inode->i_sb, block))) { - ext2_error (inode->i_sb, "ext2_write_inode", - "unable to read inode block - " - "inode=%lu, block=%lu", inode->i_ino, block); + if (IS_ERR(raw_inode)) + return -EIO; + + if (ino == EXT2_ACL_IDX_INO || ino == EXT2_ACL_DATA_INO) { + ext2_error (sb, "ext2_write_inode", "bad inode number: %lu", ino); + brelse(bh); return -EIO; } - offset &= EXT2_BLOCK_SIZE(inode->i_sb) - 1; - raw_inode = (struct ext2_inode *) (bh->b_data + offset); - raw_inode->i_mode = cpu_to_le16(inode->i_mode); - if(!(test_opt(inode->i_sb, NO_UID32))) { - raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid)); - raw_inode->i_gid_low = cpu_to_le16(low_16_bits(inode->i_gid)); + if (!(test_opt(sb, NO_UID32))) { + raw_inode->i_uid_low = cpu_to_le16(low_16_bits(uid)); + raw_inode->i_gid_low = cpu_to_le16(low_16_bits(gid)); /* * Fix up interoperability with old kernels. Otherwise, old inodes get * re-used with the upper 16 bits of the uid/gid intact */ - if(!ei->i_dtime) { - raw_inode->i_uid_high = cpu_to_le16(high_16_bits(inode->i_uid)); - raw_inode->i_gid_high = cpu_to_le16(high_16_bits(inode->i_gid)); + if (!ei->i_dtime) { + raw_inode->i_uid_high = cpu_to_le16(high_16_bits(uid)); + raw_inode->i_gid_high = cpu_to_le16(high_16_bits(gid)); } else { raw_inode->i_uid_high = 0; raw_inode->i_gid_high = 0; } } else { - raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(inode->i_uid)); - raw_inode->i_gid_low = cpu_to_le16(fs_high2lowgid(inode->i_gid)); + raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(uid)); + raw_inode->i_gid_low = cpu_to_le16(fs_high2lowgid(gid)); raw_inode->i_uid_high = 0; raw_inode->i_gid_high = 0; } @@ -1118,7 +1083,6 @@ else { raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32); if (inode->i_size > 0x7fffffffULL) { - struct super_block *sb = inode->i_sb; if (!EXT2_HAS_RO_COMPAT_FEATURE(sb, EXT2_FEATURE_RO_COMPAT_LARGE_FILE) || EXT2_SB(sb)->s_es->s_rev_level == @@ -1139,15 +1103,15 @@ raw_inode->i_generation = cpu_to_le32(inode->i_generation); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev)); - else for (block = 0; block < EXT2_N_BLOCKS; block++) - raw_inode->i_block[block] = ei->i_data[block]; + else for (n = 0; n < EXT2_N_BLOCKS; n++) + raw_inode->i_block[n] = ei->i_data[n]; mark_buffer_dirty(bh); if (do_sync) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { printk ("IO error syncing ext2 inode [%s:%08lx]\n", - inode->i_sb->s_id, inode->i_ino); + sb->s_id, ino); err = -EIO; } } @@ -1157,9 +1121,7 @@ void ext2_write_inode (struct inode * inode, int wait) { - lock_kernel(); ext2_update_inode (inode, wait); - unlock_kernel(); } int ext2_sync_inode (struct inode *inode) diff -Nru a/fs/ext2/namei.c b/fs/ext2/namei.c --- a/fs/ext2/namei.c Tue Feb 19 18:09:00 2002 +++ b/fs/ext2/namei.c Tue Feb 19 18:09:00 2002 @@ -76,7 +76,7 @@ inode = NULL; if (ino) { inode = iget(dir->i_sb, ino); - if (!inode) + if (!inode) return ERR_PTR(-EACCES); } d_add(dentry, inode); @@ -162,9 +162,6 @@ struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; - - if (S_ISDIR(inode->i_mode)) - return -EPERM; if (inode->i_nlink >= EXT2_LINK_MAX) return -EMLINK; diff -Nru a/fs/ext2/super.c b/fs/ext2/super.c --- a/fs/ext2/super.c Tue Feb 19 18:08:57 2002 +++ b/fs/ext2/super.c Tue Feb 19 18:08:57 2002 @@ -168,8 +168,10 @@ struct ext2_inode_info *ei = (struct ext2_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) + SLAB_CTOR_CONSTRUCTOR) { + rwlock_init(&ei->i_meta_lock); inode_init_once(&ei->vfs_inode); + } } static int init_inodecache(void) diff -Nru a/fs/ext3/inode.c b/fs/ext3/inode.c --- a/fs/ext3/inode.c Tue Feb 19 18:08:57 2002 +++ b/fs/ext3/inode.c Tue Feb 19 18:08:57 2002 @@ -1848,11 +1848,14 @@ if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; + lock_kernel(); ext3_discard_prealloc(inode); handle = start_transaction(inode); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + unlock_kernel(); return; /* AKPM: return what? */ + } blocksize = inode->i_sb->s_blocksize; last_block = (inode->i_size + blocksize-1) @@ -1974,6 +1977,7 @@ ext3_orphan_del(handle, inode); ext3_journal_stop(handle, inode); + unlock_kernel(); } /* diff -Nru a/fs/ext3/namei.c b/fs/ext3/namei.c --- a/fs/ext3/namei.c Tue Feb 19 18:08:57 2002 +++ b/fs/ext3/namei.c Tue Feb 19 18:08:57 2002 @@ -28,6 +28,7 @@ #include #include #include +#include /* @@ -205,6 +206,7 @@ if (dentry->d_name.len > EXT3_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); + lock_kernel(); bh = ext3_find_entry(dentry, &de); inode = NULL; if (bh) { @@ -212,9 +214,12 @@ brelse (bh); inode = iget(dir->i_sb, ino); - if (!inode) + if (!inode) { + unlock_kernel(); return ERR_PTR(-EACCES); + } } + unlock_kernel(); d_add(dentry, inode); return NULL; } @@ -451,9 +456,12 @@ struct inode * inode; int err; + lock_kernel(); handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + unlock_kernel(); return PTR_ERR(handle); + } if (IS_SYNC(dir)) handle->h_sync = 1; @@ -468,6 +476,7 @@ err = ext3_add_nondir(handle, dentry, inode); } ext3_journal_stop(handle, dir); + unlock_kernel(); return err; } @@ -478,9 +487,12 @@ struct inode *inode; int err; + lock_kernel(); handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + unlock_kernel(); return PTR_ERR(handle); + } if (IS_SYNC(dir)) handle->h_sync = 1; @@ -493,6 +505,7 @@ err = ext3_add_nondir(handle, dentry, inode); } ext3_journal_stop(handle, dir); + unlock_kernel(); return err; } @@ -507,9 +520,12 @@ if (dir->i_nlink >= EXT3_LINK_MAX) return -EMLINK; + lock_kernel(); handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + unlock_kernel(); return PTR_ERR(handle); + } if (IS_SYNC(dir)) handle->h_sync = 1; @@ -562,6 +578,7 @@ d_instantiate(dentry, inode); out_stop: ext3_journal_stop(handle, dir); + unlock_kernel(); return err; out_no_entry: @@ -792,9 +809,12 @@ struct ext3_dir_entry_2 * de; handle_t *handle; + lock_kernel(); handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + unlock_kernel(); return PTR_ERR(handle); + } retval = -ENOENT; bh = ext3_find_entry (dentry, &de); @@ -838,6 +858,7 @@ end_rmdir: ext3_journal_stop(handle, dir); brelse (bh); + unlock_kernel(); return retval; } @@ -849,9 +870,12 @@ struct ext3_dir_entry_2 * de; handle_t *handle; + lock_kernel(); handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + unlock_kernel(); return PTR_ERR(handle); + } if (IS_SYNC(dir)) handle->h_sync = 1; @@ -889,6 +913,7 @@ end_unlink: ext3_journal_stop(handle, dir); + unlock_kernel(); brelse (bh); return retval; } @@ -904,9 +929,12 @@ if (l > dir->i_sb->s_blocksize) return -ENAMETOOLONG; + lock_kernel(); handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 5); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + unlock_kernel(); return PTR_ERR(handle); + } if (IS_SYNC(dir)) handle->h_sync = 1; @@ -937,6 +965,7 @@ err = ext3_add_nondir(handle, dentry, inode); out_stop: ext3_journal_stop(handle, dir); + unlock_kernel(); return err; out_no_entry: @@ -953,15 +982,17 @@ struct inode *inode = old_dentry->d_inode; int err; - if (S_ISDIR(inode->i_mode)) - return -EPERM; - - if (inode->i_nlink >= EXT3_LINK_MAX) + lock_kernel(); + if (inode->i_nlink >= EXT3_LINK_MAX) { + unlock_kernel(); return -EMLINK; + } handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + unlock_kernel(); return PTR_ERR(handle); + } if (IS_SYNC(dir)) handle->h_sync = 1; @@ -973,6 +1004,7 @@ ext3_mark_inode_dirty(handle, inode); err = ext3_add_nondir(handle, dentry, inode); ext3_journal_stop(handle, dir); + unlock_kernel(); return err; } @@ -995,9 +1027,12 @@ old_bh = new_bh = dir_bh = NULL; + lock_kernel(); handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS + 2); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + unlock_kernel(); return PTR_ERR(handle); + } if (IS_SYNC(old_dir) || IS_SYNC(new_dir)) handle->h_sync = 1; @@ -1106,6 +1141,7 @@ brelse (old_bh); brelse (new_bh); ext3_journal_stop(handle, old_dir); + unlock_kernel(); return retval; } @@ -1113,13 +1149,13 @@ * directories can handle most operations... */ struct inode_operations ext3_dir_inode_operations = { - create: ext3_create, /* BKL held */ - lookup: ext3_lookup, /* BKL held */ - link: ext3_link, /* BKL held */ - unlink: ext3_unlink, /* BKL held */ - symlink: ext3_symlink, /* BKL held */ - mkdir: ext3_mkdir, /* BKL held */ - rmdir: ext3_rmdir, /* BKL held */ - mknod: ext3_mknod, /* BKL held */ - rename: ext3_rename, /* BKL held */ + create: ext3_create, + lookup: ext3_lookup, + link: ext3_link, + unlink: ext3_unlink, + symlink: ext3_symlink, + mkdir: ext3_mkdir, + rmdir: ext3_rmdir, + mknod: ext3_mknod, + rename: ext3_rename, }; diff -Nru a/fs/fat/dir.c b/fs/fat/dir.c --- a/fs/fat/dir.c Tue Feb 19 18:08:57 2002 +++ b/fs/fat/dir.c Tue Feb 19 18:08:57 2002 @@ -531,7 +531,7 @@ if (!memcmp(de->name,MSDOS_DOT,11)) inum = inode->i_ino; else if (!memcmp(de->name,MSDOS_DOTDOT,11)) { - inum = filp->f_dentry->d_parent->d_inode->i_ino; + inum = parent_ino(filp->f_dentry); } else { struct inode *tmp = fat_iget(sb, ino); if (tmp) { diff -Nru a/fs/fat/file.c b/fs/fat/file.c --- a/fs/fat/file.c Tue Feb 19 18:08:58 2002 +++ b/fs/fat/file.c Tue Feb 19 18:08:58 2002 @@ -10,6 +10,7 @@ #include #include #include +#include #define PRINTK(x) #define Printk(x) printk x @@ -119,8 +120,10 @@ if (MSDOS_I(inode)->mmu_private > inode->i_size) MSDOS_I(inode)->mmu_private = inode->i_size; + lock_kernel(); fat_free(inode, (inode->i_size + (cluster - 1)) >> sbi->cluster_bits); MSDOS_I(inode)->i_attrs |= ATTR_ARCH; + unlock_kernel(); inode->i_ctime = inode->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); } diff -Nru a/fs/fat/inode.c b/fs/fat/inode.c --- a/fs/fat/inode.c Tue Feb 19 18:08:59 2002 +++ b/fs/fat/inode.c Tue Feb 19 18:08:59 2002 @@ -147,10 +147,8 @@ void fat_delete_inode(struct inode *inode) { if (!is_bad_inode(inode)) { - lock_kernel(); inode->i_size = 0; fat_truncate(inode); - unlock_kernel(); } clear_inode(inode); } @@ -432,12 +430,13 @@ struct dentry *result; if (fhtype != 3) - return NULL; + return ERR_PTR(-ESTALE); if (len < 5) - return NULL; + return ERR_PTR(-ESTALE); + /* We cannot find the parent, + It better just *be* there */ if (parent) - return NULL; /* We cannot find the parent, - It better just *be* there */ + return ERR_PTR(-ESTALE); inode = iget(sb, fh[0]); if (!inode || is_bad_inode(inode) || diff -Nru a/fs/file.c b/fs/file.c --- a/fs/file.c Tue Feb 19 18:08:57 2002 +++ b/fs/file.c Tue Feb 19 18:08:57 2002 @@ -37,7 +37,7 @@ int size = num * sizeof(struct file *); if (!array) { - printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num); + printk (KERN_ERR "free_fd_array: array = 0 (num = %d)\n", num); return; } @@ -145,11 +145,6 @@ { int size = num / 8; - if (!array) { - printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num); - return; - } - if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */ return; else if (size <= PAGE_SIZE) diff -Nru a/fs/filesystems.c b/fs/filesystems.c --- a/fs/filesystems.c Tue Feb 19 18:08:58 2002 +++ b/fs/filesystems.c Tue Feb 19 18:08:58 2002 @@ -3,7 +3,7 @@ * * Copyright (C) 1991, 1992 Linus Torvalds * - * table of configured filesystems + * nfsservctl system-call when nfsd is not compiled in. */ #include @@ -14,28 +14,28 @@ #include #include -#if defined(CONFIG_NFSD_MODULE) -struct nfsd_linkage *nfsd_linkage = NULL; +#if ! defined(CONFIG_NFSD) +struct nfsd_linkage *nfsd_linkage; long asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp) { int ret = -ENOSYS; +#if defined(CONFIG_MODULES) lock_kernel(); if (nfsd_linkage || - (request_module ("nfsd") == 0 && nfsd_linkage)) + (request_module ("nfsd") == 0 && nfsd_linkage)) { + __MOD_INC_USE_COUNT(nfsd_linkage->owner); + unlock_kernel(); ret = nfsd_linkage->do_nfsservctl(cmd, argp, resp); - - unlock_kernel(); + __MOD_DEC_USE_COUNT(nfsd_linkage->owner); + } else + unlock_kernel(); +#endif return ret; } EXPORT_SYMBOL(nfsd_linkage); -#elif ! defined (CONFIG_NFSD) -asmlinkage int sys_nfsservctl(int cmd, void *argp, void *resp) -{ - return -ENOSYS; -} #endif /* CONFIG_NFSD */ diff -Nru a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c --- a/fs/freevxfs/vxfs_lookup.c Tue Feb 19 18:09:00 2002 +++ b/fs/freevxfs/vxfs_lookup.c Tue Feb 19 18:09:00 2002 @@ -38,6 +38,7 @@ #include #include #include +#include #include "vxfs.h" #include "vxfs_dir.h" @@ -210,13 +211,16 @@ if (dp->d_name.len > VXFS_NAMELEN) return ERR_PTR(-ENAMETOOLONG); + lock_kernel(); ino = vxfs_inode_by_name(dip, dp); - if (ino == 0) - return NULL; - - ip = iget(dip->i_sb, ino); - if (!ip) - return ERR_PTR(-EACCES); + if (ino) { + ip = iget(dip->i_sb, ino); + if (!ip) { + unlock_kernel(); + return ERR_PTR(-EACCES); + } + } + unlock_kernel(); d_add(dp, ip); return NULL; } diff -Nru a/fs/hfs/dir.c b/fs/hfs/dir.c --- a/fs/hfs/dir.c Tue Feb 19 18:08:58 2002 +++ b/fs/hfs/dir.c Tue Feb 19 18:08:58 2002 @@ -20,6 +20,7 @@ #include #include #include +#include /*================ File-local functions ================*/ @@ -170,15 +171,20 @@ struct inode *inode; int error; + lock_kernel(); /* build the key, checking against reserved names */ - if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len)) + if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len)) { + unlock_kernel(); return -EEXIST; + } if ((error = hfs_cat_create(entry, &key, (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK, HFS_SB(dir->i_sb)->s_type, - HFS_SB(dir->i_sb)->s_creator, &new))) + HFS_SB(dir->i_sb)->s_creator, &new))) { + unlock_kernel(); return error; + } /* create an inode for the new file. back out if we run * into trouble. */ @@ -186,6 +192,7 @@ if (!(inode = hfs_iget(new, HFS_I(dir)->file_type, dentry))) { hfs_cat_delete(entry, new, 1); hfs_cat_put(new); + unlock_kernel(); return -EIO; } @@ -195,6 +202,7 @@ if (HFS_I(dir)->d_drop_op) HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type); mark_inode_dirty(inode); + unlock_kernel(); d_instantiate(dentry, inode); return 0; } @@ -209,26 +217,33 @@ */ int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode) { - struct hfs_cat_entry *entry = HFS_I(parent)->entry; + struct hfs_cat_entry *entry; struct hfs_cat_entry *new; struct hfs_cat_key key; struct inode *inode; int error; + lock_kernel(); + entry = HFS_I(parent)->entry; /* build the key, checking against reserved names */ if (build_key(&key, parent, dentry->d_name.name, - dentry->d_name.len)) + dentry->d_name.len)) { + unlock_kernel(); return -EEXIST; + } /* try to create the directory */ - if ((error = hfs_cat_mkdir(entry, &key, &new))) + if ((error = hfs_cat_mkdir(entry, &key, &new))) { + unlock_kernel(); return error; + } /* back out if we run into trouble */ new->count++; /* hfs_iget eats one */ if (!(inode = hfs_iget(new, HFS_I(parent)->file_type, dentry))) { hfs_cat_delete(entry, new, 1); hfs_cat_put(new); + unlock_kernel(); return -EIO; } @@ -236,6 +251,7 @@ update_dirs_plus(entry, 1); mark_inode_dirty(inode); d_instantiate(dentry, inode); + unlock_kernel(); return 0; } @@ -254,12 +270,18 @@ struct hfs_cat_key key; int error; + lock_kernel(); + entry = HFS_I(dir)->entry; if (build_key(&key, dir, dentry->d_name.name, - dentry->d_name.len)) + dentry->d_name.len)) { + unlock_kernel(); return -EPERM; + } - if (!(victim = hfs_cat_get(entry->mdb, &key))) + if (!(victim = hfs_cat_get(entry->mdb, &key))) { + unlock_kernel(); return -ENOENT; + } error = -EPERM; if (victim->type != HFS_CDR_FIL) @@ -277,6 +299,7 @@ hfs_unlink_put: hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */ + unlock_kernel(); return error; } @@ -290,18 +313,24 @@ */ int hfs_rmdir(struct inode * parent, struct dentry *dentry) { - struct hfs_cat_entry *entry = HFS_I(parent)->entry; + struct hfs_cat_entry *entry; struct hfs_cat_entry *victim = NULL; struct inode *inode = dentry->d_inode; struct hfs_cat_key key; int error; + lock_kernel(); + entry = HFS_I(parent)->entry; if (build_key(&key, parent, dentry->d_name.name, - dentry->d_name.len)) + dentry->d_name.len)) { + unlock_kernel(); return -EPERM; + } - if (!(victim = hfs_cat_get(entry->mdb, &key))) + if (!(victim = hfs_cat_get(entry->mdb, &key))) { + unlock_kernel(); return -ENOENT; + } error = -ENOTDIR; if (victim->type != HFS_CDR_DIR) @@ -329,6 +358,7 @@ hfs_rmdir_put: hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */ + unlock_kernel(); return error; } @@ -346,20 +376,27 @@ int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { - struct hfs_cat_entry *old_parent = HFS_I(old_dir)->entry; - struct hfs_cat_entry *new_parent = HFS_I(new_dir)->entry; + struct hfs_cat_entry *old_parent; + struct hfs_cat_entry *new_parent; struct hfs_cat_entry *victim = NULL; struct hfs_cat_entry *deleted; struct hfs_cat_key key; int error; + lock_kernel(); + old_parent = HFS_I(old_dir)->entry; + new_parent = HFS_I(new_dir)->entry; if (build_key(&key, old_dir, old_dentry->d_name.name, old_dentry->d_name.len) || - (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino))) + (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino))) { + unlock_kernel(); return -EPERM; + } - if (!(victim = hfs_cat_get(old_parent->mdb, &key))) + if (!(victim = hfs_cat_get(old_parent->mdb, &key))) { + unlock_kernel(); return -ENOENT; + } error = -EPERM; if (build_key(&key, new_dir, new_dentry->d_name.name, @@ -388,5 +425,6 @@ hfs_rename_put: hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */ + unlock_kernel(); return error; } diff -Nru a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c --- a/fs/hfs/dir_cap.c Tue Feb 19 18:08:58 2002 +++ b/fs/hfs/dir_cap.c Tue Feb 19 18:08:58 2002 @@ -24,6 +24,7 @@ #include #include #include +#include /*================ Forward declarations ================*/ @@ -102,6 +103,8 @@ struct hfs_cat_key key; struct inode *inode = NULL; + lock_kernel(); + dentry->d_op = &hfs_dentry_operations; entry = HFS_I(dir)->entry; dtype = HFS_ITYPE(dir->i_ino); @@ -112,7 +115,7 @@ /* no need to check for "." or ".." */ - /* Check for special directories if in a normal directory. + /* Check for epecial directories if in a normal directory. Note that cap_dupdir() does an iput(dir). */ if (dtype==HFS_CAP_NDIR) { /* Check for ".resource", ".finderinfo" and ".rootinfo" */ @@ -149,6 +152,7 @@ } done: + unlock_kernel(); d_add(dentry, inode); return NULL; } diff -Nru a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c --- a/fs/hfs/dir_dbl.c Tue Feb 19 18:08:59 2002 +++ b/fs/hfs/dir_dbl.c Tue Feb 19 18:08:59 2002 @@ -20,6 +20,7 @@ #include #include #include +#include /*================ Forward declarations ================*/ @@ -114,6 +115,7 @@ struct hfs_cat_key key; struct inode *inode = NULL; + lock_kernel(); dentry->d_op = &hfs_dentry_operations; entry = HFS_I(dir)->entry; @@ -145,6 +147,7 @@ } done: + unlock_kernel(); d_add(dentry, inode); return NULL; } @@ -269,11 +272,13 @@ { int error; + lock_kernel(); if (is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) { error = -EEXIST; } else { error = hfs_create(dir, dentry, mode); } + unlock_kernel(); return error; } @@ -290,11 +295,13 @@ { int error; + lock_kernel(); if (is_hdr(parent, dentry->d_name.name, dentry->d_name.len)) { error = -EEXIST; } else { error = hfs_mkdir(parent, dentry, mode); } + unlock_kernel(); return error; } @@ -310,11 +317,13 @@ { int error; + lock_kernel(); error = hfs_unlink(dir, dentry); if ((error == -ENOENT) && is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) { error = -EPERM; } + unlock_kernel(); return error; } @@ -330,11 +339,13 @@ { int error; + lock_kernel(); error = hfs_rmdir(parent, dentry); if ((error == -ENOENT) && is_hdr(parent, dentry->d_name.name, dentry->d_name.len)) { error = -ENOTDIR; } + unlock_kernel(); return error; } @@ -355,6 +366,7 @@ { int error; + lock_kernel(); if (is_hdr(new_dir, new_dentry->d_name.name, new_dentry->d_name.len)) { error = -EPERM; @@ -367,6 +379,7 @@ error = -EPERM; } } + unlock_kernel(); return error; } diff -Nru a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c --- a/fs/hfs/dir_nat.c Tue Feb 19 18:08:59 2002 +++ b/fs/hfs/dir_nat.c Tue Feb 19 18:08:59 2002 @@ -26,6 +26,7 @@ #include #include #include +#include /*================ Forward declarations ================*/ @@ -104,6 +105,7 @@ struct hfs_cat_key key; struct inode *inode = NULL; + lock_kernel(); dentry->d_op = &hfs_dentry_operations; entry = HFS_I(dir)->entry; dtype = HFS_ITYPE(dir->i_ino); @@ -154,6 +156,7 @@ } done: + unlock_kernel(); d_add(dentry, inode); return NULL; } @@ -330,6 +333,7 @@ struct hfs_name cname; int error; + lock_kernel(); hfs_nameout(parent, &cname, dentry->d_name.name, dentry->d_name.len); if (hfs_streq(cname.Name, cname.Len, DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) { @@ -346,6 +350,7 @@ } else { error = hfs_rmdir(parent, dentry); } + unlock_kernel(); return error; } @@ -366,9 +371,11 @@ */ static int nat_hdr_unlink(struct inode *dir, struct dentry *dentry) { - struct hfs_cat_entry *entry = HFS_I(dir)->entry; + struct hfs_cat_entry *entry; int error = 0; + lock_kernel(); + entry = HFS_I(dir)->entry; if (!HFS_SB(dir->i_sb)->s_afpd) { /* Not in AFPD compatibility mode */ error = -EPERM; @@ -393,6 +400,7 @@ } } } + unlock_kernel(); return error; } @@ -417,9 +425,11 @@ static int nat_hdr_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { - struct hfs_cat_entry *entry = HFS_I(old_dir)->entry; + struct hfs_cat_entry *entry; int error = 0; + lock_kernel(); + entry = HFS_I(old_dir)->entry; if (!HFS_SB(old_dir->i_sb)->s_afpd) { /* Not in AFPD compatibility mode */ error = -EPERM; @@ -448,5 +458,6 @@ error = -EPERM; } } + unlock_kernel(); return error; } diff -Nru a/fs/hfs/file.c b/fs/hfs/file.c --- a/fs/hfs/file.c Tue Feb 19 18:08:59 2002 +++ b/fs/hfs/file.c Tue Feb 19 18:08:59 2002 @@ -20,6 +20,7 @@ #include #include #include +#include /*================ Forward declarations ================*/ @@ -163,8 +164,7 @@ if (left <= 0) { return 0; } - if ((read = hfs_do_read(inode, HFS_I(inode)->fork, pos, - buf, left, filp->f_reada != 0)) > 0) { + if ((read = hfs_do_read(inode, HFS_I(inode)->fork, pos, buf, left)) > 0) { *ppos += read; filp->f_reada = 1; } @@ -223,8 +223,10 @@ */ static void hfs_file_truncate(struct inode * inode) { - struct hfs_fork *fork = HFS_I(inode)->fork; + struct hfs_fork *fork; + lock_kernel(); + fork = HFS_I(inode)->fork; fork->lsize = inode->i_size; hfs_extent_adj(fork); hfs_cat_mark_dirty(HFS_I(inode)->entry); @@ -232,6 +234,7 @@ inode->i_size = fork->lsize; inode->i_blocks = fork->psize; mark_inode_dirty(inode); + unlock_kernel(); } /* @@ -288,9 +291,8 @@ * It has been changed to take into account that HFS files have no holes. */ hfs_s32 hfs_do_read(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos, - char * buf, hfs_u32 count, int reada) + char * buf, hfs_u32 count) { - kdev_t dev = inode->i_dev; hfs_s32 size, chars, offset, block, blocks, read = 0; int bhrequest, uptodate; int convert = HFS_I(inode)->convert; @@ -309,14 +311,6 @@ blocks = (count+offset+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS; bhb = bhe = buflist; - if (reada) { - if (blocks < read_ahead[major(dev)] / (HFS_SECTOR_SIZE>>9)) { - blocks = read_ahead[major(dev)] / (HFS_SECTOR_SIZE>>9); - } - if (block + blocks > size) { - blocks = size - block; - } - } /* We do this in a two stage process. We first try and request as many blocks as we can, then we wait for the diff -Nru a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c --- a/fs/hfs/file_hdr.c Tue Feb 19 18:08:57 2002 +++ b/fs/hfs/file_hdr.c Tue Feb 19 18:08:57 2002 @@ -592,8 +592,7 @@ if (p) { left -= copy_to_user(buf, p + offset, left); } else if (fork) { - left = hfs_do_read(inode, fork, offset, buf, left, - filp->f_reada != 0); + left = hfs_do_read(inode, fork, offset, buf, left); if (left > 0) { filp->f_reada = 1; } else if (!read) { diff -Nru a/fs/hpfs/dir.c b/fs/hpfs/dir.c --- a/fs/hpfs/dir.c Tue Feb 19 18:08:58 2002 +++ b/fs/hpfs/dir.c Tue Feb 19 18:08:58 2002 @@ -189,6 +189,7 @@ struct inode *result = NULL; struct hpfs_inode_info *hpfs_result; + lock_kernel(); if ((err = hpfs_chk_name((char *)name, &len))) { if (err == -ENAMETOOLONG) return ERR_PTR(-ENAMETOOLONG); goto end_add; @@ -273,6 +274,7 @@ hpfs_unlock_inode(dir); end_add: hpfs_set_dentry_operations(dentry); + unlock_kernel(); d_add(dentry, result); return NULL; @@ -286,5 +288,6 @@ /*bail:*/ hpfs_unlock_inode(dir); + unlock_kernel(); return ERR_PTR(-ENOENT); } diff -Nru a/fs/hpfs/file.c b/fs/hpfs/file.c --- a/fs/hpfs/file.c Tue Feb 19 18:08:59 2002 +++ b/fs/hpfs/file.c Tue Feb 19 18:08:59 2002 @@ -62,11 +62,14 @@ void hpfs_truncate(struct inode *i) { if (IS_IMMUTABLE(i)) return /*-EPERM*/; + lock_kernel(); hpfs_i(i)->i_n_secs = 0; i->i_blocks = 1 + ((i->i_size + 511) >> 9); hpfs_i(i)->mmu_private = i->i_size; hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9)); hpfs_write_inode(i); + hpfs_i(i)->i_n_secs = 0; + unlock_kernel(); } int hpfs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) diff -Nru a/fs/hpfs/namei.c b/fs/hpfs/namei.c --- a/fs/hpfs/namei.c Tue Feb 19 18:08:59 2002 +++ b/fs/hpfs/namei.c Tue Feb 19 18:08:59 2002 @@ -25,6 +25,7 @@ struct hpfs_dirent dee; int err; if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; + lock_kernel(); if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; if (!(dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1))) goto bail1; memset(&dee, 0, sizeof dee); @@ -43,6 +44,7 @@ hpfs_free_sectors(dir->i_sb, fno, 1); hpfs_free_dnode(dir->i_sb, dno); hpfs_unlock_inode(dir); + unlock_kernel(); return -EEXIST; } fnode->len = len; @@ -85,15 +87,17 @@ } hpfs_unlock_iget(dir->i_sb); hpfs_unlock_inode(dir); + unlock_kernel(); return 0; - bail2: +bail2: hpfs_brelse4(&qbh0); hpfs_free_dnode(dir->i_sb, dno); hpfs_unlock_inode(dir); - bail1: +bail1: brelse(bh); hpfs_free_sectors(dir->i_sb, fno, 1); - bail: +bail: + unlock_kernel(); return -ENOSPC; } @@ -108,7 +112,9 @@ int r; struct hpfs_dirent dee; int err; - if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; + if ((err = hpfs_chk_name((char *)name, &len))) + return err==-ENOENT ? -EINVAL : err; + lock_kernel(); if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; memset(&dee, 0, sizeof dee); if (!(mode & 0222)) dee.read_only = 1; @@ -123,6 +129,7 @@ brelse(bh); hpfs_free_sectors(dir->i_sb, fno, 1); hpfs_unlock_inode(dir); + unlock_kernel(); return -EEXIST; } fnode->len = len; @@ -155,12 +162,14 @@ } hpfs_unlock_iget(dir->i_sb); hpfs_unlock_inode(dir); + unlock_kernel(); return 0; - bail1: +bail1: brelse(bh); hpfs_free_sectors(dir->i_sb, fno, 1); hpfs_unlock_inode(dir); - bail: +bail: + unlock_kernel(); return -ENOSPC; } @@ -177,6 +186,7 @@ int err; if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; if (dir->i_sb->s_hpfs_eas < 2) return -EPERM; + lock_kernel(); if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; memset(&dee, 0, sizeof dee); if (!(mode & 0222)) dee.read_only = 1; @@ -191,6 +201,7 @@ brelse(bh); hpfs_free_sectors(dir->i_sb, fno, 1); hpfs_unlock_inode(dir); + unlock_kernel(); return -EEXIST; } fnode->len = len; @@ -216,12 +227,14 @@ hpfs_unlock_iget(dir->i_sb); hpfs_unlock_inode(dir); brelse(bh); + unlock_kernel(); return 0; - bail1: +bail1: brelse(bh); hpfs_free_sectors(dir->i_sb, fno, 1); hpfs_unlock_inode(dir); - bail: +bail: + unlock_kernel(); return -ENOSPC; } @@ -239,7 +252,11 @@ struct inode *result; int err; if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; - if (dir->i_sb->s_hpfs_eas < 2) return -EPERM; + lock_kernel(); + if (dir->i_sb->s_hpfs_eas < 2) { + unlock_kernel(); + return -EPERM; + } if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; memset(&dee, 0, sizeof dee); dee.archive = 1; @@ -253,6 +270,7 @@ brelse(bh); hpfs_free_sectors(dir->i_sb, fno, 1); hpfs_unlock_inode(dir); + unlock_kernel(); return -EEXIST; } fnode->len = len; @@ -284,12 +302,14 @@ } hpfs_unlock_iget(dir->i_sb); hpfs_unlock_inode(dir); + unlock_kernel(); return 0; - bail1: +bail1: brelse(bh); hpfs_free_sectors(dir->i_sb, fno, 1); hpfs_unlock_inode(dir); - bail: +bail: + unlock_kernel(); return -ENOSPC; } @@ -304,21 +324,26 @@ fnode_secno fno; int r; int rep = 0; + + lock_kernel(); hpfs_adjust_length((char *)name, &len); - again: +again: hpfs_lock_2inodes(dir, inode); if (!(de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh))) { hpfs_unlock_2inodes(dir, inode); + unlock_kernel(); return -ENOENT; } if (de->first) { hpfs_brelse4(&qbh); hpfs_unlock_2inodes(dir, inode); + unlock_kernel(); return -EPERM; } if (de->directory) { hpfs_brelse4(&qbh); hpfs_unlock_2inodes(dir, inode); + unlock_kernel(); return -EISDIR; } fno = de->fnode; @@ -351,7 +376,8 @@ rep = 1; goto again; } - ret: +ret: + unlock_kernel(); return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0; } @@ -367,25 +393,30 @@ int n_items = 0; int r; hpfs_adjust_length((char *)name, &len); + lock_kernel(); hpfs_lock_2inodes(dir, inode); if (!(de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh))) { hpfs_unlock_2inodes(dir, inode); + unlock_kernel(); return -ENOENT; } if (de->first) { hpfs_brelse4(&qbh); hpfs_unlock_2inodes(dir, inode); + unlock_kernel(); return -EPERM; } if (!de->directory) { hpfs_brelse4(&qbh); hpfs_unlock_2inodes(dir, inode); + unlock_kernel(); return -ENOTDIR; } hpfs_count_dnodes(dir->i_sb, hpfs_i(inode)->i_dno, NULL, NULL, &n_items); if (n_items) { hpfs_brelse4(&qbh); hpfs_unlock_2inodes(dir, inode); + unlock_kernel(); return -ENOTEMPTY; } fno = de->fnode; @@ -396,6 +427,7 @@ inode->i_nlink = 0; hpfs_unlock_2inodes(dir, inode); } else hpfs_unlock_2inodes(dir, inode); + unlock_kernel(); return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0; } @@ -450,6 +482,7 @@ err = 0; hpfs_adjust_length((char *)old_name, &old_len); + lock_kernel(); hpfs_lock_3inodes(old_dir, new_dir, i); /* Erm? Moving over the empty non-busy directory is perfectly legal */ @@ -530,5 +563,6 @@ hpfs_decide_conv(i, (char *)new_name, new_len); end1: hpfs_unlock_3inodes(old_dir, new_dir, i); + unlock_kernel(); return err; } diff -Nru a/fs/inode.c b/fs/inode.c --- a/fs/inode.c Tue Feb 19 18:08:59 2002 +++ b/fs/inode.c Tue Feb 19 18:08:59 2002 @@ -143,7 +143,6 @@ INIT_LIST_HEAD(&inode->i_dirty_data_buffers); INIT_LIST_HEAD(&inode->i_devices); sema_init(&inode->i_sem, 1); - sema_init(&inode->i_zombie, 1); spin_lock_init(&inode->i_data.i_shared_lock); } diff -Nru a/fs/isofs/compress.c b/fs/isofs/compress.c --- a/fs/isofs/compress.c Tue Feb 19 18:08:57 2002 +++ b/fs/isofs/compress.c Tue Feb 19 18:08:57 2002 @@ -348,7 +348,7 @@ return 0; } -void __exit zisofs_cleanup(void) +void zisofs_cleanup(void) { if ( !initialized ) { printk("zisofs_cleanup: called without initialization\n"); diff -Nru a/fs/isofs/dir.c b/fs/isofs/dir.c --- a/fs/isofs/dir.c Tue Feb 19 18:08:58 2002 +++ b/fs/isofs/dir.c Tue Feb 19 18:08:58 2002 @@ -185,7 +185,7 @@ /* Handle the case of the '..' directory */ if (de->name_len[0] == 1 && de->name[0] == 1) { - inode_number = filp->f_dentry->d_parent->d_inode->i_ino; + inode_number = parent_ino(filp->f_dentry); if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0) break; filp->f_pos += de_len; diff -Nru a/fs/isofs/namei.c b/fs/isofs/namei.c --- a/fs/isofs/namei.c Tue Feb 19 18:08:58 2002 +++ b/fs/isofs/namei.c Tue Feb 19 18:08:58 2002 @@ -15,6 +15,7 @@ #include #include #include /* Joliet? */ +#include #include @@ -167,6 +168,7 @@ if (!page) return ERR_PTR(-ENOMEM); + lock_kernel(); ino = isofs_find_entry(dir, dentry, page_address(page), 1024 + page_address(page)); __free_page(page); @@ -174,9 +176,12 @@ inode = NULL; if (ino) { inode = iget(dir->i_sb, ino); - if (!inode) + if (!inode) { + unlock_kernel(); return ERR_PTR(-EACCES); + } } + unlock_kernel(); d_add(dentry, inode); return NULL; } diff -Nru a/fs/isofs/zisofs.h b/fs/isofs/zisofs.h --- a/fs/isofs/zisofs.h Tue Feb 19 18:08:59 2002 +++ b/fs/isofs/zisofs.h Tue Feb 19 18:08:59 2002 @@ -17,5 +17,5 @@ #ifdef CONFIG_ZISOFS extern struct address_space_operations zisofs_aops; extern int __init zisofs_init(void); -extern void __exit zisofs_cleanup(void); +extern void zisofs_cleanup(void); #endif diff -Nru a/fs/jbd/journal.c b/fs/jbd/journal.c --- a/fs/jbd/journal.c Tue Feb 19 18:08:58 2002 +++ b/fs/jbd/journal.c Tue Feb 19 18:08:58 2002 @@ -205,7 +205,7 @@ daemonize(); spin_lock_irq(¤t->sigmask_lock); sigfillset(¤t->blocked); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); sprintf(current->comm, "kjournald"); diff -Nru a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c --- a/fs/jffs/inode-v23.c Tue Feb 19 18:08:57 2002 +++ b/fs/jffs/inode-v23.c Tue Feb 19 18:08:57 2002 @@ -422,10 +422,12 @@ old_dir, old_dentry->d_name.name, new_dir, new_dentry->d_name.name)); + lock_kernel(); c = (struct jffs_control *)old_dir->i_sb->u.generic_sbp; ASSERT(if (!c) { printk(KERN_ERR "jffs_rename(): The old_dir inode " "didn't have a reference to a jffs_file struct\n"); + unlock_kernel(); return -EIO; }); @@ -544,6 +546,7 @@ jffs_rename_end: D3(printk (KERN_NOTICE "rename(): up biglock\n")); up(&c->fmc->biglock); + unlock_kernel(); return result; } /* jffs_rename() */ @@ -631,6 +634,8 @@ len = dentry->d_name.len; name = dentry->d_name.name; + lock_kernel(); + D3({ char *s = (char *)kmalloc(len + 1, GFP_KERNEL); memcpy(s, name, len); @@ -698,6 +703,7 @@ d_add(dentry, inode); D3(printk (KERN_NOTICE "lookup(): up biglock\n")); up(&c->fmc->biglock); + unlock_kernel(); return NULL; jffs_lookup_end: @@ -705,6 +711,7 @@ up(&c->fmc->biglock); jffs_lookup_end_no_biglock: + unlock_kernel(); return ERR_PTR(r); } /* jffs_lookup() */ @@ -803,11 +810,13 @@ kfree(_name); }); + lock_kernel(); dir_f = (struct jffs_file *)dir->u.generic_ip; ASSERT(if (!dir_f) { printk(KERN_ERR "jffs_mkdir(): No reference to a " "jffs_file struct in inode.\n"); + unlock_kernel(); return -EIO; }); @@ -881,6 +890,7 @@ jffs_mkdir_end: D3(printk (KERN_NOTICE "mkdir(): up biglock\n")); up(&c->fmc->biglock); + unlock_kernel(); return result; } /* jffs_mkdir() */ @@ -893,10 +903,12 @@ int ret; D3(printk("***jffs_rmdir()\n")); D3(printk (KERN_NOTICE "rmdir(): down biglock\n")); + lock_kernel(); down(&c->fmc->biglock); ret = jffs_remove(dir, dentry, S_IFDIR); D3(printk (KERN_NOTICE "rmdir(): up biglock\n")); up(&c->fmc->biglock); + unlock_kernel(); return ret; } @@ -908,12 +920,14 @@ struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp; int ret; + lock_kernel(); D3(printk("***jffs_unlink()\n")); D3(printk (KERN_NOTICE "unlink(): down biglock\n")); down(&c->fmc->biglock); ret = jffs_remove(dir, dentry, 0); D3(printk (KERN_NOTICE "unlink(): up biglock\n")); up(&c->fmc->biglock); + unlock_kernel(); return ret; } @@ -1048,6 +1062,7 @@ D1(printk("***jffs_mknod()\n")); + lock_kernel(); dir_f = (struct jffs_file *)dir->u.generic_ip; c = dir_f->c; @@ -1119,6 +1134,7 @@ jffs_mknod_end: D3(printk (KERN_NOTICE "mknod(): up biglock\n")); up(&c->fmc->biglock); + unlock_kernel(); return result; } /* jffs_mknod() */ @@ -1135,6 +1151,7 @@ int symname_len = strlen(symname); int err; + lock_kernel(); D1({ int len = dentry->d_name.len; char *_name = (char *)kmalloc(len + 1, GFP_KERNEL); @@ -1154,6 +1171,7 @@ ASSERT(if (!dir_f) { printk(KERN_ERR "jffs_symlink(): No reference to a " "jffs_file struct in inode.\n"); + unlock_kernel(); return -EIO; }); @@ -1162,6 +1180,7 @@ /* Create a node and initialize it as much as needed. */ if (!(node = jffs_alloc_node())) { D(printk("jffs_symlink(): Allocation failed: node = NULL\n")); + unlock_kernel(); return -ENOMEM; } D3(printk (KERN_NOTICE "symlink(): down biglock\n")); @@ -1216,6 +1235,7 @@ jffs_symlink_end: D3(printk (KERN_NOTICE "symlink(): up biglock\n")); up(&c->fmc->biglock); + unlock_kernel(); return err; } /* jffs_symlink() */ @@ -1239,6 +1259,7 @@ struct inode *inode; int err; + lock_kernel(); D1({ int len = dentry->d_name.len; char *s = (char *)kmalloc(len + 1, GFP_KERNEL); @@ -1252,6 +1273,7 @@ ASSERT(if (!dir_f) { printk(KERN_ERR "jffs_create(): No reference to a " "jffs_file struct in inode.\n"); + unlock_kernel(); return -EIO; }); @@ -1260,6 +1282,7 @@ /* Create a node and initialize as much as needed. */ if (!(node = jffs_alloc_node())) { D(printk("jffs_create(): Allocation failed: node == 0\n")); + unlock_kernel(); return -ENOMEM; } D3(printk (KERN_NOTICE "create(): down biglock\n")); @@ -1317,6 +1340,7 @@ jffs_create_end: D3(printk (KERN_NOTICE "create(): up biglock\n")); up(&c->fmc->biglock); + unlock_kernel(); return err; } /* jffs_create() */ diff -Nru a/fs/jffs/intrep.c b/fs/jffs/intrep.c --- a/fs/jffs/intrep.c Tue Feb 19 18:08:57 2002 +++ b/fs/jffs/intrep.c Tue Feb 19 18:08:57 2002 @@ -3352,7 +3352,7 @@ init_completion(&c->gc_thread_comp); /* barrier */ spin_lock_irq(¤t->sigmask_lock); siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); strcpy(current->comm, "jffs_gcd"); diff -Nru a/fs/jffs2/background.c b/fs/jffs2/background.c --- a/fs/jffs2/background.c Tue Feb 19 18:08:57 2002 +++ b/fs/jffs2/background.c Tue Feb 19 18:08:57 2002 @@ -114,7 +114,7 @@ for (;;) { spin_lock_irq(¤t->sigmask_lock); siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (!thread_should_wake(c)) { @@ -164,7 +164,7 @@ /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ spin_lock_irq(¤t->sigmask_lock); siginitsetinv (¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); diff -Nru a/fs/jffs2/dir.c b/fs/jffs2/dir.c --- a/fs/jffs2/dir.c Tue Feb 19 18:08:58 2002 +++ b/fs/jffs2/dir.c Tue Feb 19 18:08:58 2002 @@ -43,6 +43,7 @@ #include #include #include +#include #include "nodelist.h" static int jffs2_readdir (struct file *, void *, filldir_t); @@ -98,6 +99,7 @@ D1(printk(KERN_DEBUG "jffs2_lookup()\n")); + lock_kernel(); dir_f = JFFS2_INODE_INFO(dir_i); c = JFFS2_SB_INFO(dir_i->i_sb); @@ -118,10 +120,12 @@ if (ino) { inode = iget(dir_i->i_sb, ino); if (!inode) { + unlock_kernel(); printk(KERN_WARNING "iget() failed for ino #%u\n", ino); return (ERR_PTR(-EIO)); } } + unlock_kernel(); d_add(target, inode); @@ -153,8 +157,8 @@ offset++; } if (offset == 1) { - D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", filp->f_dentry->d_parent->d_inode->i_ino)); - if (filldir(dirent, "..", 2, 1, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) + D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", parent_ino(filp->f_dentry))); + if (filldir(dirent, "..", 2, 1, parent_ino(filp->f_dentry), DT_DIR) < 0) goto out; offset++; } @@ -202,9 +206,12 @@ __u32 writtenlen; int ret; + lock_kernel(); ri = jffs2_alloc_raw_inode(); - if (!ri) + if (!ri) { + unlock_kernel(); return -ENOMEM; + } c = JFFS2_SB_INFO(dir_i->i_sb); @@ -218,6 +225,7 @@ D1(printk(KERN_DEBUG "jffs2_create(): reserved 0x%x bytes\n", alloclen)); if (ret) { jffs2_free_raw_inode(ri); + unlock_kernel(); return ret; } @@ -227,6 +235,7 @@ D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n")); jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); + unlock_kernel(); return PTR_ERR(inode); } @@ -250,6 +259,7 @@ up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); + unlock_kernel(); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be @@ -272,6 +282,7 @@ /* Eep. */ D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); jffs2_clear_inode(inode); + unlock_kernel(); return ret; } } @@ -281,6 +292,7 @@ /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); jffs2_clear_inode(inode); + unlock_kernel(); return -ENOMEM; } @@ -311,6 +323,7 @@ as if it were the final unlink() */ up(&dir_f->sem); jffs2_clear_inode(inode); + unlock_kernel(); return PTR_ERR(fd); } @@ -323,6 +336,7 @@ D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n", inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); + unlock_kernel(); return 0; } @@ -411,7 +425,11 @@ static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry) { - return jffs2_do_unlink(dir_i, dentry, 0); + int res; + lock_kernel(); + res = jffs2_do_unlink(dir_i, dentry, 0); + unlock_kernel(); + return res; } /***********************************************************************/ @@ -483,11 +501,15 @@ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry) { - int ret = jffs2_do_link(old_dentry, dir_i, dentry, 0); + int ret; + + lock_kernel(); + ret = jffs2_do_link(old_dentry, dir_i, dentry, 0); if (!ret) { d_instantiate(dentry, old_dentry->d_inode); atomic_inc(&old_dentry->d_inode->i_count); } + unlock_kernel(); return ret; } @@ -512,6 +534,7 @@ if (strlen(target) > 254) return -EINVAL; + lock_kernel(); ri = jffs2_alloc_raw_inode(); if (!ri) @@ -527,6 +550,7 @@ if (ret) { jffs2_free_raw_inode(ri); + unlock_kernel(); return ret; } @@ -535,6 +559,7 @@ if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); + unlock_kernel(); return PTR_ERR(inode); } @@ -559,6 +584,7 @@ up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); + unlock_kernel(); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be @@ -579,6 +605,7 @@ if (ret) { /* Eep. */ jffs2_clear_inode(inode); + unlock_kernel(); return ret; } } @@ -588,6 +615,7 @@ /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); jffs2_clear_inode(inode); + unlock_kernel(); return -ENOMEM; } @@ -618,6 +646,7 @@ as if it were the final unlink() */ up(&dir_f->sem); jffs2_clear_inode(inode); + unlock_kernel(); return PTR_ERR(fd); } @@ -627,6 +656,7 @@ up(&dir_f->sem); d_instantiate(dentry, inode); + unlock_kernel(); return 0; } @@ -647,9 +677,12 @@ mode |= S_IFDIR; + lock_kernel(); ri = jffs2_alloc_raw_inode(); - if (!ri) + if (!ri) { + unlock_kernel(); return -ENOMEM; + } c = JFFS2_SB_INFO(dir_i->i_sb); @@ -661,6 +694,7 @@ if (ret) { jffs2_free_raw_inode(ri); + unlock_kernel(); return ret; } @@ -669,6 +703,7 @@ if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); + unlock_kernel(); return PTR_ERR(inode); } @@ -689,6 +724,7 @@ up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); + unlock_kernel(); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be @@ -709,6 +745,7 @@ if (ret) { /* Eep. */ jffs2_clear_inode(inode); + unlock_kernel(); return ret; } } @@ -718,6 +755,7 @@ /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); jffs2_clear_inode(inode); + unlock_kernel(); return -ENOMEM; } @@ -748,6 +786,7 @@ as if it were the final unlink() */ up(&dir_f->sem); jffs2_clear_inode(inode); + unlock_kernel(); return PTR_ERR(fd); } @@ -757,6 +796,7 @@ up(&dir_f->sem); d_instantiate(dentry, inode); + unlock_kernel(); return 0; } @@ -765,10 +805,14 @@ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); struct jffs2_full_dirent *fd; + lock_kernel(); for (fd = f->dents ; fd; fd = fd->next) { - if (fd->ino) + if (fd->ino) { + unlock_kernel(); return -ENOTEMPTY; + } } + unlock_kernel(); return jffs2_unlink(dir_i, dentry); } @@ -788,9 +832,12 @@ __u32 writtenlen; int ret; + lock_kernel(); ri = jffs2_alloc_raw_inode(); - if (!ri) + if (!ri) { + unlock_kernel(); return -ENOMEM; + } c = JFFS2_SB_INFO(dir_i->i_sb); @@ -808,6 +855,7 @@ if (ret) { jffs2_free_raw_inode(ri); + unlock_kernel(); return ret; } @@ -816,6 +864,7 @@ if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); + unlock_kernel(); return PTR_ERR(inode); } inode->i_op = &jffs2_file_inode_operations; @@ -840,6 +889,7 @@ up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); + unlock_kernel(); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be @@ -860,6 +910,7 @@ if (ret) { /* Eep. */ jffs2_clear_inode(inode); + unlock_kernel(); return ret; } } @@ -869,6 +920,7 @@ /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); jffs2_clear_inode(inode); + unlock_kernel(); return -ENOMEM; } @@ -902,6 +954,7 @@ as if it were the final unlink() */ up(&dir_f->sem); jffs2_clear_inode(inode); + unlock_kernel(); return PTR_ERR(fd); } @@ -909,6 +962,7 @@ one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); + unlock_kernel(); d_instantiate(dentry, inode); return 0; @@ -924,9 +978,12 @@ */ /* Make a hard link */ + lock_kernel(); ret = jffs2_do_link(old_dentry, new_dir_i, new_dentry, 1); - if (ret) + if (ret) { + unlock_kernel(); return ret; + } /* Unlink the original */ ret = jffs2_do_unlink(old_dir_i, old_dentry, 1); @@ -947,6 +1004,7 @@ } } + unlock_kernel(); return ret; } diff -Nru a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c --- a/fs/lockd/clntproc.c Tue Feb 19 18:08:57 2002 +++ b/fs/lockd/clntproc.c Tue Feb 19 18:08:57 2002 @@ -148,7 +148,7 @@ && fl->fl_type == F_UNLCK && (current->flags & PF_EXITING)) { sigfillset(¤t->blocked); /* Mask all signals */ - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); call = nlmclnt_alloc_call(); @@ -185,7 +185,7 @@ out_restore: spin_lock_irqsave(¤t->sigmask_lock, flags); current->blocked = oldset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); done: @@ -595,7 +595,7 @@ spin_lock_irqsave(¤t->sigmask_lock, flags); oldset = current->blocked; sigfillset(¤t->blocked); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); req = nlmclnt_alloc_call(); @@ -613,7 +613,7 @@ spin_lock_irqsave(¤t->sigmask_lock, flags); current->blocked = oldset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); return status; diff -Nru a/fs/lockd/svc.c b/fs/lockd/svc.c --- a/fs/lockd/svc.c Tue Feb 19 18:08:57 2002 +++ b/fs/lockd/svc.c Tue Feb 19 18:08:57 2002 @@ -104,7 +104,7 @@ /* Process request with signals blocked. */ spin_lock_irq(¤t->sigmask_lock); siginitsetinv(¤t->blocked, sigmask(SIGKILL)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); /* kick rpciod */ @@ -312,7 +312,7 @@ nlmsvc_pid = 0; } spin_lock_irq(¤t->sigmask_lock); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); out: up(&nlmsvc_sema); diff -Nru a/fs/minix/dir.c b/fs/minix/dir.c --- a/fs/minix/dir.c Tue Feb 19 18:08:58 2002 +++ b/fs/minix/dir.c Tue Feb 19 18:08:58 2002 @@ -36,8 +36,13 @@ struct inode *dir = (struct inode *)page->mapping->host; int err = 0; page->mapping->a_ops->commit_write(NULL, page, from, to); - if (IS_SYNC(dir)) - err = waitfor_one_page(page); + if (IS_SYNC(dir)) { + int err2; + err = writeout_one_page(page); + err2 = waitfor_one_page(page); + if (err == 0) + err = err2; + } return err; } @@ -236,10 +241,10 @@ lock_page(page); err = mapping->a_ops->prepare_write(NULL, page, from, to); - if (err) - BUG(); - de->inode = 0; - err = dir_commit_chunk(page, from, to); + if (err == 0) { + de->inode = 0; + err = dir_commit_chunk(page, from, to); + } UnlockPage(page); dir_put_page(page); inode->i_ctime = inode->i_mtime = CURRENT_TIME; @@ -336,10 +341,10 @@ lock_page(page); err = page->mapping->a_ops->prepare_write(NULL, page, from, to); - if (err) - BUG(); - de->inode = inode->i_ino; - err = dir_commit_chunk(page, from, to); + if (err == 0) { + de->inode = inode->i_ino; + err = dir_commit_chunk(page, from, to); + } UnlockPage(page); dir_put_page(page); dir->i_mtime = dir->i_ctime = CURRENT_TIME; diff -Nru a/fs/minix/inode.c b/fs/minix/inode.c --- a/fs/minix/inode.c Tue Feb 19 18:08:56 2002 +++ b/fs/minix/inode.c Tue Feb 19 18:08:56 2002 @@ -27,12 +27,10 @@ static void minix_delete_inode(struct inode *inode) { - lock_kernel(); - inode->i_size = 0; minix_truncate(inode); + lock_kernel(); minix_free_inode(inode); - unlock_kernel(); } @@ -551,10 +549,12 @@ */ void minix_truncate(struct inode * inode) { + lock_kernel(); if (INODE_VERSION(inode) == MINIX_V1) V1_minix_truncate(inode); else V2_minix_truncate(inode); + unlock_kernel(); } static struct super_block *minix_get_sb(struct file_system_type *fs_type, diff -Nru a/fs/minix/namei.c b/fs/minix/namei.c --- a/fs/minix/namei.c Tue Feb 19 18:08:59 2002 +++ b/fs/minix/namei.c Tue Feb 19 18:08:59 2002 @@ -131,9 +131,6 @@ { struct inode *inode = old_dentry->d_inode; - if (S_ISDIR(inode->i_mode)) - return -EPERM; - if (inode->i_nlink >= inode->i_sb->u.minix_sb.s_link_max) return -EMLINK; diff -Nru a/fs/msdos/namei.c b/fs/msdos/namei.c --- a/fs/msdos/namei.c Tue Feb 19 18:09:00 2002 +++ b/fs/msdos/namei.c Tue Feb 19 18:09:00 2002 @@ -12,6 +12,7 @@ #include #include +#include #define MSDOS_DEBUG 0 #define PRINTK(x) @@ -215,6 +216,7 @@ dentry->d_op = &msdos_dentry_operations; + lock_kernel(); res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de, &ino); @@ -231,6 +233,7 @@ out: if (bh) fat_brelse(sb, bh); + unlock_kernel(); return ERR_PTR(res); } @@ -277,27 +280,36 @@ int ino,res,is_hid; char msdos_name[MSDOS_NAME]; + lock_kernel(); res = msdos_format_name(dentry->d_name.name,dentry->d_name.len, msdos_name, &MSDOS_SB(sb)->options); - if (res < 0) + if (res < 0) { + unlock_kernel(); return res; + } is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); /* Have to do it due to foo vs. .foo conflicts */ if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0) { fat_brelse(sb, bh); + unlock_kernel(); return -EINVAL; } inode = NULL; res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 0, is_hid); - if (res) + if (res) { + unlock_kernel(); return res; + } inode = fat_build_inode(dir->i_sb, de, ino, &res); fat_brelse(sb, bh); - if (!inode) + if (!inode) { + unlock_kernel(); return res; + } inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); d_instantiate(dentry, inode); + unlock_kernel(); return 0; } @@ -311,6 +323,7 @@ struct msdos_dir_entry *de; bh = NULL; + lock_kernel(); res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de, &ino); if (res < 0) @@ -335,6 +348,7 @@ rmdir_done: fat_brelse(sb, bh); + unlock_kernel(); return res; } @@ -349,10 +363,13 @@ char msdos_name[MSDOS_NAME]; int ino; + lock_kernel(); res = msdos_format_name(dentry->d_name.name,dentry->d_name.len, msdos_name, &MSDOS_SB(sb)->options); - if (res < 0) + if (res < 0) { + unlock_kernel(); return res; + } is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); /* foo vs .foo situation */ if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0) @@ -380,6 +397,7 @@ res = 0; out_unlock: + unlock_kernel(); return res; mkdir_error: @@ -412,6 +430,7 @@ struct msdos_dir_entry *de; bh = NULL; + lock_kernel(); res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de, &ino); if (res < 0) @@ -427,6 +446,7 @@ mark_inode_dirty(dir); res = 0; unlink_done: + unlock_kernel(); return res; } @@ -541,6 +561,7 @@ int is_hid,old_hid; /* if new file and old file are hidden */ char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME]; + lock_kernel(); error = msdos_format_name(old_dentry->d_name.name, old_dentry->d_name.len,old_msdos_name, &MSDOS_SB(old_dir->i_sb)->options); @@ -564,6 +585,7 @@ fat_brelse(sb, old_bh); rename_done: + unlock_kernel(); return error; } diff -Nru a/fs/namei.c b/fs/namei.c --- a/fs/namei.c Tue Feb 19 18:08:59 2002 +++ b/fs/namei.c Tue Feb 19 18:08:59 2002 @@ -93,6 +93,11 @@ * hopefully we will be able to get rid of that wart in 2.5. So far only * XEmacs seems to be relying on it... */ +/* + * [Sep 2001 AV] Single-semaphore locking scheme (kudos to David Holland) + * implemented. Let's see if raised priority of ->s_vfs_rename_sem gives + * any extra contention... + */ /* In order to reduce some races, while at the same time doing additional * checking and hopefully speeding things up, we copy filenames to the @@ -299,9 +304,7 @@ struct dentry * dentry = d_alloc(parent, name); result = ERR_PTR(-ENOMEM); if (dentry) { - lock_kernel(); result = dir->i_op->lookup(dir, dentry); - unlock_kernel(); if (result) dput(dentry); else @@ -773,9 +776,7 @@ dentry = ERR_PTR(-ENOMEM); if (!new) goto out; - lock_kernel(); dentry = inode->i_op->lookup(inode, new); - unlock_kernel(); if (!dentry) dentry = new; else @@ -931,28 +932,65 @@ return retval; } -int vfs_create(struct inode *dir, struct dentry *dentry, int mode) +/* + * p1 and p2 should be directories on the same fs. + */ +struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) { - int error; + struct dentry *p; - mode &= S_IALLUGO; - mode |= S_IFREG; + if (p1 == p2) { + down(&p1->d_inode->i_sem); + return NULL; + } + + down(&p1->d_inode->i_sb->s_vfs_rename_sem); + + for (p = p1; p->d_parent != p; p = p->d_parent) { + if (p->d_parent == p2) { + down(&p2->d_inode->i_sem); + down(&p1->d_inode->i_sem); + return p; + } + } + + for (p = p2; p->d_parent != p; p = p->d_parent) { + if (p->d_parent == p1) { + down(&p1->d_inode->i_sem); + down(&p2->d_inode->i_sem); + return p; + } + } + + down(&p1->d_inode->i_sem); + down(&p2->d_inode->i_sem); + return NULL; +} + +void unlock_rename(struct dentry *p1, struct dentry *p2) +{ + up(&p1->d_inode->i_sem); + if (p1 != p2) { + up(&p2->d_inode->i_sem); + up(&p1->d_inode->i_sb->s_vfs_rename_sem); + } +} + +int vfs_create(struct inode *dir, struct dentry *dentry, int mode) +{ + int error = may_create(dir, dentry); - down(&dir->i_zombie); - error = may_create(dir, dentry); if (error) - goto exit_lock; + return error; - error = -EACCES; /* shouldn't it be ENOSYS? */ if (!dir->i_op || !dir->i_op->create) - goto exit_lock; + return -EACCES; /* shouldn't it be ENOSYS? */ DQUOT_INIT(dir); - lock_kernel(); + + mode &= S_IALLUGO; + mode |= S_IFREG; error = dir->i_op->create(dir, dentry, mode); - unlock_kernel(); -exit_lock: - up(&dir->i_zombie); if (!error) inode_dir_notify(dir, DN_CREATE); return error; @@ -1212,26 +1250,19 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { - int error = -EPERM; + int error = may_create(dir, dentry); - down(&dir->i_zombie); - if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD)) - goto exit_lock; - - error = may_create(dir, dentry); if (error) - goto exit_lock; + return error; + + if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD)) + return -EPERM; - error = -EPERM; if (!dir->i_op || !dir->i_op->mknod) - goto exit_lock; + return -EPERM; DQUOT_INIT(dir); - lock_kernel(); error = dir->i_op->mknod(dir, dentry, mode, dev); - unlock_kernel(); -exit_lock: - up(&dir->i_zombie); if (!error) inode_dir_notify(dir, DN_CREATE); return error; @@ -1284,25 +1315,17 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { - int error; + int error = may_create(dir, dentry); - down(&dir->i_zombie); - error = may_create(dir, dentry); if (error) - goto exit_lock; + return error; - error = -EPERM; if (!dir->i_op || !dir->i_op->mkdir) - goto exit_lock; + return -EPERM; DQUOT_INIT(dir); mode &= (S_IRWXUGO|S_ISVTX); - lock_kernel(); error = dir->i_op->mkdir(dir, dentry, mode); - unlock_kernel(); - -exit_lock: - up(&dir->i_zombie); if (!error) inode_dir_notify(dir, DN_CREATE); return error; @@ -1369,9 +1392,8 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) { - int error; + int error = may_delete(dir, dentry, 1); - error = may_delete(dir, dentry, 1); if (error) return error; @@ -1380,20 +1402,18 @@ DQUOT_INIT(dir); - double_down(&dir->i_zombie, &dentry->d_inode->i_zombie); + down(&dentry->d_inode->i_sem); d_unhash(dentry); if (IS_DEADDIR(dir)) error = -ENOENT; else if (d_mountpoint(dentry)) error = -EBUSY; else { - lock_kernel(); error = dir->i_op->rmdir(dir, dentry); - unlock_kernel(); if (!error) dentry->d_inode->i_flags |= S_DEAD; } - double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); + up(&dentry->d_inode->i_sem); if (!error) { inode_dir_notify(dir, DN_DELETE); d_delete(dentry); @@ -1447,28 +1467,31 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) { - int error; + int error = may_delete(dir, dentry, 0); - down(&dir->i_zombie); - error = may_delete(dir, dentry, 0); - if (!error) { - error = -EPERM; - if (dir->i_op && dir->i_op->unlink) { - DQUOT_INIT(dir); - if (d_mountpoint(dentry)) - error = -EBUSY; - else { - lock_kernel(); - error = dir->i_op->unlink(dir, dentry); - unlock_kernel(); - if (!error) - d_delete(dentry); - } - } + if (error) + return error; + + if (!dir->i_op || !dir->i_op->unlink) + return -EPERM; + + DQUOT_INIT(dir); + + dget(dentry); + down(&dentry->d_inode->i_sem); + if (d_mountpoint(dentry)) + error = -EBUSY; + else { + error = dir->i_op->unlink(dir, dentry); + if (!error) + d_delete(dentry); } - up(&dir->i_zombie); + up(&dentry->d_inode->i_sem); + dput(dentry); + if (!error) inode_dir_notify(dir, DN_DELETE); + return error; } @@ -1517,24 +1540,16 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) { - int error; + int error = may_create(dir, dentry); - down(&dir->i_zombie); - error = may_create(dir, dentry); if (error) - goto exit_lock; + return error; - error = -EPERM; if (!dir->i_op || !dir->i_op->symlink) - goto exit_lock; + return -EPERM; DQUOT_INIT(dir); - lock_kernel(); error = dir->i_op->symlink(dir, dentry, oldname); - unlock_kernel(); - -exit_lock: - up(&dir->i_zombie); if (!error) inode_dir_notify(dir, DN_CREATE); return error; @@ -1576,39 +1591,33 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { - struct inode *inode; + struct inode *inode = old_dentry->d_inode; int error; - down(&dir->i_zombie); - error = -ENOENT; - inode = old_dentry->d_inode; if (!inode) - goto exit_lock; + return -ENOENT; error = may_create(dir, new_dentry); if (error) - goto exit_lock; + return error; - error = -EXDEV; if (dir->i_sb != inode->i_sb) - goto exit_lock; + return -EXDEV; /* * A link to an append-only or immutable file cannot be created. */ - error = -EPERM; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - goto exit_lock; + return -EPERM; if (!dir->i_op || !dir->i_op->link) - goto exit_lock; + return -EPERM; + if (S_ISDIR(old_dentry->d_inode->i_mode)) + return -EPERM; + down(&old_dentry->d_inode->i_sem); DQUOT_INIT(dir); - lock_kernel(); error = dir->i_op->link(old_dentry, dir, new_dentry); - unlock_kernel(); - -exit_lock: - up(&dir->i_zombie); + up(&old_dentry->d_inode->i_sem); if (!error) inode_dir_notify(dir, DN_CREATE); return error; @@ -1680,17 +1689,23 @@ * story. * c) we have to lock _three_ objects - parents and victim (if it exists). * And that - after we got ->i_sem on parents (until then we don't know - * whether the target exists at all, let alone whether it is a directory - * or not). Solution: ->i_zombie. Taken only after ->i_sem. Always taken - * on link creation/removal of any kind. And taken (without ->i_sem) on - * directory that will be removed (both in rmdir() and here). + * whether the target exists). Solution: try to be smart with locking + * order for inodes. We rely on the fact that tree topology may change + * only under ->s_vfs_rename_sem _and_ that parent of the object we + * move will be locked. Thus we can rank directories by the tree + * (ancestors first) and rank all non-directories after them. + * That works since everybody except rename does "lock parent, lookup, + * lock child" and rename is under ->s_vfs_rename_sem. + * HOWEVER, it relies on the assumption that any object with ->lookup() + * has no more than 1 dentry. If "hybrid" objects will ever appear, + * we'd better make sure that there's no link(2) for them. * d) some filesystems don't support opened-but-unlinked directories, * either because of layout or because they are not ready to deal with * all cases correctly. The latter will be fixed (taking this sort of * stuff into VFS), but the former is not going away. Solution: the same * trick as in rmdir(). * e) conversion from fhandle to dentry may come in the wrong moment - when - * we are removing the target. Solution: we will have to grab ->i_zombie + * we are removing the target. Solution: we will have to grab ->i_sem * in the fhandle_to_dentry code. [FIXME - current nfsfh.c relies on * ->i_sem on parents, which works but leads to some truely excessive * locking]. @@ -1698,131 +1713,101 @@ int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { - int error; + int error = 0; struct inode *target; - if (old_dentry->d_inode == new_dentry->d_inode) - return 0; - - error = may_delete(old_dir, old_dentry, 1); - if (error) - return error; - - if (new_dir->i_sb != old_dir->i_sb) - return -EXDEV; - - if (!new_dentry->d_inode) - error = may_create(new_dir, new_dentry); - else - error = may_delete(new_dir, new_dentry, 1); - if (error) - return error; - - if (!old_dir->i_op || !old_dir->i_op->rename) - return -EPERM; - /* * If we are going to change the parent - check write permissions, * we'll need to flip '..'. */ - if (new_dir != old_dir) { + if (new_dir != old_dir) error = permission(old_dentry->d_inode, MAY_WRITE); - } + if (error) return error; - DQUOT_INIT(old_dir); - DQUOT_INIT(new_dir); - down(&old_dir->i_sb->s_vfs_rename_sem); - error = -EINVAL; - if (is_subdir(new_dentry, old_dentry)) - goto out_unlock; - /* Don't eat your daddy, dear... */ - /* This also avoids locking issues */ - if (old_dentry->d_parent == new_dentry) - goto out_unlock; target = new_dentry->d_inode; - if (target) { /* Hastur! Hastur! Hastur! */ - triple_down(&old_dir->i_zombie, - &new_dir->i_zombie, - &target->i_zombie); + if (target) { + down(&target->i_sem); d_unhash(new_dentry); - } else - double_down(&old_dir->i_zombie, - &new_dir->i_zombie); - if (IS_DEADDIR(old_dir)||IS_DEADDIR(new_dir)) - error = -ENOENT; - else if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) + } + if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) error = -EBUSY; else error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); if (target) { if (!error) target->i_flags |= S_DEAD; - triple_up(&old_dir->i_zombie, - &new_dir->i_zombie, - &target->i_zombie); + up(&target->i_sem); if (d_unhashed(new_dentry)) d_rehash(new_dentry); dput(new_dentry); - } else - double_up(&old_dir->i_zombie, - &new_dir->i_zombie); - - if (!error) + } + if (!error) { + lock_kernel(); d_move(old_dentry,new_dentry); -out_unlock: - up(&old_dir->i_sb->s_vfs_rename_sem); + unlock_kernel(); + } return error; } int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { + struct inode *target; int error; - if (old_dentry->d_inode == new_dentry->d_inode) - return 0; + dget(new_dentry); + target = new_dentry->d_inode; + if (target) + down(&target->i_sem); + if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) + error = -EBUSY; + else + error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); + if (!error) { + /* The following d_move() should become unconditional */ + if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME)) { + lock_kernel(); + d_move(old_dentry, new_dentry); + unlock_kernel(); + } + } + if (target) + up(&target->i_sem); + dput(new_dentry); + return error; +} + +int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int error; + int is_dir = S_ISDIR(old_dentry->d_inode->i_mode); - error = may_delete(old_dir, old_dentry, 0); + if (old_dentry->d_inode == new_dentry->d_inode) + return 0; + + error = may_delete(old_dir, old_dentry, is_dir); if (error) return error; - if (new_dir->i_sb != old_dir->i_sb) - return -EXDEV; - if (!new_dentry->d_inode) error = may_create(new_dir, new_dentry); else - error = may_delete(new_dir, new_dentry, 0); + error = may_delete(new_dir, new_dentry, is_dir); if (error) return error; if (!old_dir->i_op || !old_dir->i_op->rename) return -EPERM; + if (IS_DEADDIR(old_dir)||IS_DEADDIR(new_dir)) + return -ENOENT; DQUOT_INIT(old_dir); DQUOT_INIT(new_dir); - double_down(&old_dir->i_zombie, &new_dir->i_zombie); - if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) - error = -EBUSY; - else - error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); - double_up(&old_dir->i_zombie, &new_dir->i_zombie); - if (error) - return error; - /* The following d_move() should become unconditional */ - if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME)) { - d_move(old_dentry, new_dentry); - } - return 0; -} -int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - int error; - if (S_ISDIR(old_dentry->d_inode->i_mode)) + if (is_dir) error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); else error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); @@ -1842,6 +1827,7 @@ int error = 0; struct dentry * old_dir, * new_dir; struct dentry * old_dentry, *new_dentry; + struct dentry * trap; struct nameidata oldnd, newnd; if (path_init(oldname, LOOKUP_PARENT, &oldnd)) @@ -1868,7 +1854,7 @@ if (newnd.last_type != LAST_NORM) goto exit2; - double_lock(new_dir, old_dir); + trap = lock_rename(new_dir, old_dir); old_dentry = lookup_hash(&oldnd.last, old_dir); error = PTR_ERR(old_dentry); @@ -1886,21 +1872,27 @@ if (newnd.last.name[newnd.last.len]) goto exit4; } + /* source should not be ancestor of target */ + error = -EINVAL; + if (old_dentry == trap) + goto exit4; new_dentry = lookup_hash(&newnd.last, new_dir); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) goto exit4; + /* target should not be an ancestor of source */ + error = -ENOTEMPTY; + if (new_dentry == trap) + goto exit5; - lock_kernel(); error = vfs_rename(old_dir->d_inode, old_dentry, new_dir->d_inode, new_dentry); - unlock_kernel(); - +exit5: dput(new_dentry); exit4: dput(old_dentry); exit3: - double_up(&new_dir->d_inode->i_sem, &old_dir->d_inode->i_sem); + unlock_rename(new_dir, old_dir); exit2: path_release(&newnd); exit1: diff -Nru a/fs/namespace.c b/fs/namespace.c --- a/fs/namespace.c Tue Feb 19 18:08:57 2002 +++ b/fs/namespace.c Tue Feb 19 18:08:57 2002 @@ -466,7 +466,7 @@ return -ENOTDIR; err = -ENOENT; - down(&nd->dentry->d_inode->i_zombie); + down(&nd->dentry->d_inode->i_sem); if (IS_DEADDIR(nd->dentry->d_inode)) goto out_unlock; @@ -481,7 +481,7 @@ } spin_unlock(&dcache_lock); out_unlock: - up(&nd->dentry->d_inode->i_zombie); + up(&nd->dentry->d_inode->i_sem); return err; } @@ -577,7 +577,7 @@ goto out; err = -ENOENT; - down(&nd->dentry->d_inode->i_zombie); + down(&nd->dentry->d_inode->i_sem); if (IS_DEADDIR(nd->dentry->d_inode)) goto out1; @@ -607,7 +607,7 @@ out2: spin_unlock(&dcache_lock); out1: - up(&nd->dentry->d_inode->i_zombie); + up(&nd->dentry->d_inode->i_sem); out: up_write(¤t->namespace->sem); if (!err) @@ -949,7 +949,7 @@ user_nd.dentry = dget(current->fs->root); read_unlock(¤t->fs->lock); down_write(¤t->namespace->sem); - down(&old_nd.dentry->d_inode->i_zombie); + down(&old_nd.dentry->d_inode->i_sem); error = -EINVAL; if (!check_mnt(user_nd.mnt)) goto out2; @@ -992,7 +992,7 @@ path_release(&root_parent); path_release(&parent_nd); out2: - up(&old_nd.dentry->d_inode->i_zombie); + up(&old_nd.dentry->d_inode->i_sem); up_write(¤t->namespace->sem); path_release(&user_nd); path_release(&old_nd); diff -Nru a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c --- a/fs/ncpfs/dir.c Tue Feb 19 18:08:57 2002 +++ b/fs/ncpfs/dir.c Tue Feb 19 18:08:57 2002 @@ -411,8 +411,7 @@ filp->f_pos = 1; } if (filp->f_pos == 1) { - if (filldir(dirent, "..", 2, 1, - dentry->d_parent->d_inode->i_ino, DT_DIR)) + if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR)) goto out; filp->f_pos = 2; } @@ -741,6 +740,7 @@ int error, res, len = dentry->d_name.len + 1; __u8 __name[len]; + lock_kernel(); error = -EIO; if (!ncp_conn_valid(server)) goto finished; @@ -785,6 +785,7 @@ finished: PPRINTK("ncp_lookup: result=%d\n", error); + unlock_kernel(); return ERR_PTR(error); } @@ -825,6 +826,7 @@ PPRINTK("ncp_create_new: creating %s/%s, mode=%x\n", dentry->d_parent->d_name.name, dentry->d_name.name, mode); error = -EIO; + lock_kernel(); if (!ncp_conn_valid(server)) goto out; @@ -861,6 +863,7 @@ finfo.access = opmode; error = ncp_instantiate(dir, dentry, &finfo); out: + unlock_kernel(); return error; } @@ -879,6 +882,7 @@ DPRINTK("ncp_mkdir: making %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); error = -EIO; + lock_kernel(); if (!ncp_conn_valid(server)) goto out; @@ -896,6 +900,7 @@ error = ncp_instantiate(dir, dentry, &finfo); } out: + unlock_kernel(); return error; } @@ -909,6 +914,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); error = -EIO; + lock_kernel(); if (!ncp_conn_valid(server)) goto out; @@ -948,15 +954,18 @@ break; } out: + unlock_kernel(); return error; } static int ncp_unlink(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; - struct ncp_server *server = NCP_SERVER(dir); + struct ncp_server *server; int error; + lock_kernel(); + server = NCP_SERVER(dir); DPRINTK("ncp_unlink: unlinking %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -1007,6 +1016,7 @@ } out: + unlock_kernel(); return error; } @@ -1024,6 +1034,7 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name); error = -EIO; + lock_kernel(); if (!ncp_conn_valid(server)) goto out; @@ -1067,6 +1078,7 @@ break; } out: + unlock_kernel(); return error; } diff -Nru a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c --- a/fs/ncpfs/inode.c Tue Feb 19 18:09:00 2002 +++ b/fs/ncpfs/inode.c Tue Feb 19 18:09:00 2002 @@ -315,8 +315,9 @@ #endif struct ncp_entry_info finfo; + error = -EFAULT; if (raw_data == NULL) - goto out_no_data; + goto out; switch (*(int*)raw_data) { case NCP_MOUNT_VERSION: { @@ -356,23 +357,27 @@ } break; default: - goto out_bad_mount; + error = -ECHRNG; + goto out; } + error = -EBADF; ncp_filp = fget(data.ncp_fd); if (!ncp_filp) - goto out_bad_file; + goto out; + error = -ENOTSOCK; sock_inode = ncp_filp->f_dentry->d_inode; if (!S_ISSOCK(sock_inode->i_mode)) - goto out_bad_file2; + goto out_fput; sock = SOCKET_I(sock_inode); if (!sock) - goto out_bad_file2; + goto out_fput; if (sock->type == SOCK_STREAM) - default_bufsize = 61440; + default_bufsize = 0xF000; else default_bufsize = 1024; + sb->s_maxbytes = 0xFFFFFFFFU; sb->s_blocksize = 1024; /* Eh... Is this correct? */ sb->s_blocksize_bits = 10; sb->s_magic = NCP_SUPER_MAGIC; @@ -408,10 +413,8 @@ printk(KERN_INFO "You need to recompile your ncpfs utils..\n"); } server->m.time_out = server->m.time_out * HZ / 100; - server->m.file_mode = (server->m.file_mode & - (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG; - server->m.dir_mode = (server->m.dir_mode & - (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR; + server->m.file_mode = (server->m.file_mode & S_IRWXUGO) | S_IFREG; + server->m.dir_mode = (server->m.dir_mode & S_IRWXUGO) | S_IFDIR; #ifdef CONFIG_NCPFS_NLS /* load the default NLS charsets */ @@ -422,19 +425,21 @@ server->dentry_ttl = 0; /* no caching */ #undef NCP_PACKET_SIZE -#define NCP_PACKET_SIZE 65536 +#define NCP_PACKET_SIZE 131072 + error = -ENOMEM; server->packet_size = NCP_PACKET_SIZE; server->packet = vmalloc(NCP_PACKET_SIZE); if (server->packet == NULL) - goto out_no_packet; + goto out_nls; ncp_lock_server(server); error = ncp_connect(server); ncp_unlock_server(server); if (error < 0) - goto out_no_connect; + goto out_packet; DPRINTK("ncp_fill_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb)); + error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */ #ifdef CONFIG_NCPFS_PACKET_SIGNING if (ncp_negotiate_size_and_options(server, default_bufsize, NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0) @@ -447,7 +452,7 @@ &(server->buffer_size), &options) != 0) { - goto out_no_bufsize; + goto out_disconnect; } } if (options & 2) @@ -457,7 +462,7 @@ #endif /* CONFIG_NCPFS_PACKET_SIGNING */ if (ncp_negotiate_buffersize(server, default_bufsize, &(server->buffer_size)) != 0) - goto out_no_bufsize; + goto out_disconnect; DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size); memset(&finfo, 0, sizeof(finfo)); @@ -482,9 +487,11 @@ finfo.ino = 2; /* tradition */ server->name_space[finfo.i.volNumber] = NW_NS_DOS; + + error = -ENOMEM; root_inode = ncp_iget(sb, &finfo); if (!root_inode) - goto out_no_root; + goto out_disconnect; DPRINTK("ncp_fill_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber); sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) @@ -493,49 +500,27 @@ return 0; out_no_root: - printk(KERN_ERR "ncp_fill_super: get root inode failed\n"); iput(root_inode); - goto out_disconnect; -out_no_bufsize: - printk(KERN_ERR "ncp_fill_super: could not get bufsize\n"); out_disconnect: ncp_lock_server(server); ncp_disconnect(server); ncp_unlock_server(server); - goto out_free_packet; -out_no_connect: - printk(KERN_ERR "ncp_fill_super: Failed connection, error=%d\n", error); -out_free_packet: +out_packet: vfree(server->packet); - goto out_free_server; -out_no_packet: - printk(KERN_ERR "ncp_fill_super: could not alloc packet\n"); -out_free_server: +out_nls: #ifdef CONFIG_NCPFS_NLS unload_nls(server->nls_io); unload_nls(server->nls_vol); #endif +out_fput: /* 23/12/1998 Marcin Dalecki : * * The previously used put_filp(ncp_filp); was bogous, since * it doesn't proper unlocking. */ fput(ncp_filp); - goto out; - -out_bad_file2: - fput(ncp_filp); -out_bad_file: - printk(KERN_ERR "ncp_fill_super: invalid ncp socket\n"); - goto out; -out_bad_mount: - printk(KERN_INFO "ncp_fill_super: kernel requires mount version %d\n", - NCP_MOUNT_VERSION); - goto out; -out_no_data: - printk(KERN_ERR "ncp_fill_super: missing data argument\n"); out: - return -EINVAL; + return error; } static void ncp_put_super(struct super_block *sb) diff -Nru a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c --- a/fs/ncpfs/sock.c Tue Feb 19 18:08:59 2002 +++ b/fs/ncpfs/sock.c Tue Feb 19 18:08:59 2002 @@ -132,7 +132,7 @@ result = _send(sock, (void *) start, size); if (result < 0) { printk(KERN_ERR "ncp_rpc_call: send error = %d\n", result); - break; + return result; } re_select: poll_initwait(&wait_table); @@ -472,7 +472,7 @@ mask |= sigmask(SIGQUIT); } siginitsetinv(¤t->blocked, mask); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); fs = get_fs(); @@ -487,7 +487,7 @@ spin_lock_irqsave(¤t->sigmask_lock, flags); current->blocked = old_set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } diff -Nru a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c --- a/fs/ncpfs/symlink.c Tue Feb 19 18:08:59 2002 +++ b/fs/ncpfs/symlink.c Tue Feb 19 18:08:59 2002 @@ -28,6 +28,7 @@ #include #include #include +#include #include "ncplib_kernel.h" @@ -121,6 +122,7 @@ return -ENOMEM; err = -EIO; + lock_kernel(); if (ncp_create_new(dir,dentry,0,aSHARED|aHIDDEN)) goto failfree; @@ -147,6 +149,7 @@ ncp_inode_close(inode); ncp_make_closed(inode); + unlock_kernel(); kfree(link); return 0; @@ -154,6 +157,7 @@ ncp_inode_close(inode); ncp_make_closed(inode); failfree: + unlock_kernel(); kfree(link); return err; } diff -Nru a/fs/nfs/dir.c b/fs/nfs/dir.c --- a/fs/nfs/dir.c Tue Feb 19 18:09:00 2002 +++ b/fs/nfs/dir.c Tue Feb 19 18:09:00 2002 @@ -579,6 +579,7 @@ error = -ENOMEM; dentry->d_op = &nfs_dentry_operations; + lock_kernel(); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); inode = NULL; if (error == -ENOENT) @@ -593,6 +594,7 @@ } nfs_renew_times(dentry); } + unlock_kernel(); out: return ERR_PTR(error); } @@ -640,6 +642,7 @@ * select the appropriate create strategy. Currently open_namei * does not pass the create flags. */ + lock_kernel(); nfs_zap_caches(dir); error = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, 0, &fhandle, &fattr); @@ -647,6 +650,7 @@ error = nfs_instantiate(dentry, &fhandle, &fattr); if (error || fhandle.size == 0) d_drop(dentry); + unlock_kernel(); return error; } @@ -666,6 +670,7 @@ attr.ia_mode = mode; attr.ia_valid = ATTR_MODE; + lock_kernel(); nfs_zap_caches(dir); error = NFS_PROTO(dir)->mknod(dir, &dentry->d_name, &attr, rdev, &fhandle, &fattr); @@ -673,6 +678,7 @@ error = nfs_instantiate(dentry, &fhandle, &fattr); if (error || fhandle.size == 0) d_drop(dentry); + unlock_kernel(); return error; } @@ -692,6 +698,7 @@ attr.ia_valid = ATTR_MODE; attr.ia_mode = mode | S_IFDIR; + lock_kernel(); #if 0 /* * Always drop the dentry, we can't always depend on @@ -708,6 +715,7 @@ error = nfs_instantiate(dentry, &fhandle, &fattr); if (error || fhandle.size == 0) d_drop(dentry); + unlock_kernel(); return error; } @@ -718,10 +726,12 @@ dfprintk(VFS, "NFS: rmdir(%s/%ld, %s\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); + lock_kernel(); nfs_zap_caches(dir); error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); if (!error) dentry->d_inode->i_nlink = 0; + unlock_kernel(); return error; } @@ -865,6 +875,7 @@ dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); + lock_kernel(); error = nfs_sillyrename(dir, dentry); if (error && error != -EBUSY) { error = nfs_safe_remove(dentry); @@ -872,6 +883,7 @@ nfs_renew_times(dentry); } } + unlock_kernel(); return error; } @@ -908,6 +920,7 @@ qsymname.name = symname; qsymname.len = strlen(symname); + lock_kernel(); nfs_zap_caches(dir); error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname, &attr, &sym_fh, &sym_attr); @@ -919,6 +932,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); d_drop(dentry); } + unlock_kernel(); out: return error; @@ -939,10 +953,12 @@ * Since nfs_proc_link doesn't return a file handle, * we can't use the existing dentry. */ + lock_kernel(); d_drop(dentry); nfs_zap_caches(dir); NFS_CACHEINV(inode); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); + unlock_kernel(); return error; } @@ -982,6 +998,7 @@ * To prevent any new references to the target during the rename, * we unhash the dentry and free the inode in advance. */ + lock_kernel(); if (!d_unhashed(new_dentry)) { d_drop(new_dentry); rehash = new_dentry; @@ -1058,6 +1075,7 @@ /* new dentry created? */ if (dentry) dput(dentry); + unlock_kernel(); return error; } diff -Nru a/fs/nfs/file.c b/fs/nfs/file.c --- a/fs/nfs/file.c Tue Feb 19 18:08:57 2002 +++ b/fs/nfs/file.c Tue Feb 19 18:08:57 2002 @@ -244,6 +244,7 @@ { struct inode * inode = filp->f_dentry->d_inode; int status = 0; + int status2; dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n", inode->i_sb->s_id, inode->i_ino, @@ -278,11 +279,15 @@ * Flush all pending writes before doing anything * with locks.. */ - filemap_fdatasync(inode->i_mapping); + status = filemap_fdatasync(inode->i_mapping); down(&inode->i_sem); - status = nfs_wb_all(inode); + status2 = nfs_wb_all(inode); + if (status2 && !status) + status = status2; up(&inode->i_sem); - filemap_fdatawait(inode->i_mapping); + status2 = filemap_fdatawait(inode->i_mapping); + if (status2 && !status) + status = status2; if (status < 0) return status; diff -Nru a/fs/nfsd/export.c b/fs/nfsd/export.c --- a/fs/nfsd/export.c Tue Feb 19 18:08:57 2002 +++ b/fs/nfsd/export.c Tue Feb 19 18:08:57 2002 @@ -59,7 +59,6 @@ }; static struct svc_clnthash * clnt_hash[CLIENT_HASHMAX]; static svc_client * clients; -static int initialized; static int hash_lock; static int want_lock; @@ -73,18 +72,17 @@ exp_get(svc_client *clp, kdev_t dev, ino_t ino) { struct list_head *head, *p; - svc_export *exp = NULL; - + if (!clp) return NULL; head = &clp->cl_export[EXPORT_HASH(dev)]; list_for_each(p, head) { - exp = list_entry(p, svc_export, ex_hash); + svc_export *exp = list_entry(p, svc_export, ex_hash); if (exp->ex_ino == ino && kdev_same(exp->ex_dev, dev)) - break; + return exp; } - return exp; + return NULL; } svc_export * @@ -92,18 +90,17 @@ { struct list_head *head, *p; int hash = EXPORT_HASH(mnt->mnt_sb->s_dev); - svc_export *exp = NULL; if (!clp) return NULL; head = &clp->cl_export[hash]; list_for_each(p, head) { - exp = list_entry(p, svc_export, ex_hash); + svc_export *exp = list_entry(p, svc_export, ex_hash); if (exp->ex_dentry == dentry && exp->ex_mnt == mnt) break; } - return exp; + return NULL; } /* @@ -114,14 +111,13 @@ { struct list_head *head = &clp->cl_export[EXPORT_HASH(sb->s_dev)]; struct list_head *p; - svc_export *exp = NULL; list_for_each(p, head) { - exp = list_entry(p, svc_export, ex_hash); + svc_export *exp = list_entry(p, svc_export, ex_hash); if (is_subdir(dentry, exp->ex_dentry)) - break; + return exp; } - return exp; + return NULL; } /* @@ -134,16 +130,15 @@ { struct list_head *head = &clp->cl_export[EXPORT_HASH(sb->s_dev)]; struct list_head *p; - svc_export *exp = NULL; struct dentry *ndentry; list_for_each(p, head) { - exp = list_entry(p, svc_export, ex_hash); + svc_export *exp = list_entry(p, svc_export, ex_hash); ndentry = exp->ex_dentry; if (ndentry && is_subdir(ndentry->d_parent, dentry)) - break; + return exp; } - return exp; + return NULL; } /* Update parent pointers of all exports */ @@ -445,7 +440,7 @@ /* restore the task's signals */ spin_lock_irq(¤t->sigmask_lock); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (!hash_count && !hash_lock) @@ -477,9 +472,6 @@ struct svc_clnthash **hp, **head, *tmp; unsigned long addr = sin->sin_addr.s_addr; - if (!initialized) - return NULL; - head = &clnt_hash[CLIENT_HASH(addr)]; for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) { @@ -552,9 +544,10 @@ if (p == (void *)1) clp = clients; - else if (exp->ex_list.next == &exp->ex_client->cl_list) + else if (exp->ex_list.next == &exp->ex_client->cl_list) { clp = exp->ex_client->cl_next; - else { + *pos += 1LL<<32; + } else { ++*pos; return list_entry(exp->ex_list.next, svc_export, ex_list); } @@ -875,13 +868,11 @@ int i; dprintk("nfsd: initializing export module.\n"); - if (initialized) - return; + for (i = 0; i < CLIENT_HASHMAX; i++) clnt_hash[i] = NULL; clients = NULL; - initialized = 1; } /* @@ -893,8 +884,7 @@ int i; dprintk("nfsd: shutting down export module.\n"); - if (!initialized) - return; + if (exp_writelock() < 0) { printk(KERN_WARNING "Weird: hashtable locked in exp_shutdown"); return; diff -Nru a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c --- a/fs/nfsd/nfs3proc.c Tue Feb 19 18:08:59 2002 +++ b/fs/nfsd/nfs3proc.c Tue Feb 19 18:08:59 2002 @@ -339,7 +339,7 @@ || (argp->ftype == NF3BLK && argp->major >= MAX_BLKDEV) || argp->minor > 0xFF) RETURN_STATUS(nfserr_inval); - rdev = ((argp->major) << 8) | (argp->minor); + rdev = MKDEV(argp->major, argp->minor); } else if (argp->ftype != NF3SOCK && argp->ftype != NF3FIFO) RETURN_STATUS(nfserr_inval); diff -Nru a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c --- a/fs/nfsd/nfsctl.c Tue Feb 19 18:08:58 2002 +++ b/fs/nfsd/nfsctl.c Tue Feb 19 18:08:58 2002 @@ -44,8 +44,6 @@ static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data); #endif -static int initialized; - extern struct seq_operations nfs_exports_op; static int exports_open(struct inode *inode, struct file *file) { @@ -68,20 +66,6 @@ entry->proc_fops = &exports_operations; } -/* - * Initialize nfsd - */ -static void -nfsd_init(void) -{ - nfsd_stat_init(); /* Statistics */ - nfsd_cache_init(); /* RPC reply cache */ - nfsd_export_init(); /* Exports table */ - nfsd_lockd_init(); /* lockd->nfsd callbacks */ - proc_export_init(); - initialized = 1; -} - static inline int nfsctl_svc(struct nfsctl_svc *data) { @@ -203,10 +187,8 @@ int err; int argsize, respsize; - MOD_INC_USE_COUNT; lock_kernel (); - if (!initialized) - nfsd_init(); + err = -EPERM; if (!capable(CAP_SYS_ADMIN)) { goto done; @@ -276,38 +258,47 @@ kfree(res); unlock_kernel (); - MOD_DEC_USE_COUNT; return err; } -#ifdef MODULE -/* New-style module support since 2.1.18 */ EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Olaf Kirch "); MODULE_LICENSE("GPL"); +#ifdef MODULE struct nfsd_linkage nfsd_linkage_s = { do_nfsservctl: handle_sys_nfsservctl, + owner: THIS_MODULE, }; +#endif /* * Initialize the module */ -int -init_module(void) +static int __init +nfsd_init(void) { printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); +#ifdef MODULE nfsd_linkage = &nfsd_linkage_s; +#endif + nfsd_stat_init(); /* Statistics */ + nfsd_cache_init(); /* RPC reply cache */ + nfsd_export_init(); /* Exports table */ + nfsd_lockd_init(); /* lockd->nfsd callbacks */ + proc_export_init(); return 0; } /* * Clean up the mess before unloading the module */ -void -cleanup_module(void) +static void __exit +nfsd_exit(void) { +#ifdef MODULE nfsd_linkage = NULL; +#endif nfsd_export_shutdown(); nfsd_cache_shutdown(); remove_proc_entry("fs/nfs/exports", NULL); @@ -315,4 +306,6 @@ nfsd_stat_shutdown(); nfsd_lockd_shutdown(); } -#endif + +module_init(nfsd_init); +module_exit(nfsd_exit); diff -Nru a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c --- a/fs/nfsd/nfsfh.c Tue Feb 19 18:08:59 2002 +++ b/fs/nfsd/nfsfh.c Tue Feb 19 18:08:59 2002 @@ -754,7 +754,7 @@ *maxsize = 2; return 1; } - *datap++ = ino_t_to_u32(dentry->d_parent->d_inode->i_ino); + *datap++ = ino_t_to_u32(parent_ino(dentry)); *maxsize = 3; return 2; } @@ -814,7 +814,7 @@ fhp->fh_handle.ofh_dev = htonl((major(exp->ex_dev)<<16)| minor(exp->ex_dev)); fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev; fhp->fh_handle.ofh_xino = ino_t_to_u32(exp->ex_ino); - fhp->fh_handle.ofh_dirino = ino_t_to_u32(dentry->d_parent->d_inode->i_ino); + fhp->fh_handle.ofh_dirino = ino_t_to_u32(parent_ino(dentry)); if (inode) _fh_update_old(dentry, exp, &fhp->fh_handle); } else { diff -Nru a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c --- a/fs/nfsd/nfssvc.c Tue Feb 19 18:08:57 2002 +++ b/fs/nfsd/nfssvc.c Tue Feb 19 18:08:57 2002 @@ -180,7 +180,7 @@ /* Block all but the shutdown signals */ spin_lock_irq(¤t->sigmask_lock); siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); /* @@ -205,7 +205,7 @@ /* Process request with signals blocked. */ spin_lock_irq(¤t->sigmask_lock); siginitsetinv(¤t->blocked, ALLOWED_SIGS); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); svc_process(serv, rqstp); diff -Nru a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c --- a/fs/nfsd/vfs.c Tue Feb 19 18:08:59 2002 +++ b/fs/nfsd/vfs.c Tue Feb 19 18:08:59 2002 @@ -209,36 +209,37 @@ dentry = fhp->fh_dentry; inode = dentry->d_inode; - err = inode_change_ok(inode, iap); - /* could be a "touch" (utimes) request where the user is not the owner but does - * have write permission. In this case the user should be allowed to set - * both times to the current time. We could just assume any such SETATTR - * is intended to set the times to "now", but we do a couple of simple tests - * to increase our confidence. + /* NFSv2 does not differentiate between "set-[ac]time-to-now" + * which only requires access, and "set-[ac]time-to-X" which + * requires ownership. + * So if it looks like it might be "set both to the same time which + * is close to now", and if inode_change_ok fails, then we + * convert to "set to now" instead of "set to explicit time" + * + * We only call inode_change_ok as the last test as technically + * it is not an interface that we should be using. It is only + * valid if the filesystem does not define it's own i_op->setattr. */ #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) #define MAX_TOUCH_TIME_ERROR (30*60) - if (err - && (iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET + if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET && iap->ia_mtime == iap->ia_atime ) { - /* looks good. now just make sure time is in the right ballpark. - * solaris, at least, doesn't seem to care what the time request is + /* Looks probable. Now just make sure time is in the right ballpark. + * Solaris, at least, doesn't seem to care what the time request is. + * We require it be within 30 minutes of now. */ time_t delta = iap->ia_atime - CURRENT_TIME; if (delta<0) delta = -delta; - if (delta < MAX_TOUCH_TIME_ERROR) { + if (delta < MAX_TOUCH_TIME_ERROR && + inode_change_ok(inode, iap) != 0) { /* turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME * this will cause notify_change to set these times to "now" */ iap->ia_valid &= ~BOTH_TIME_SET; - err = inode_change_ok(inode, iap); } } - if (err) - goto out_nfserr; - /* The size case is special. It changes the file as well as the attributes. */ if (iap->ia_valid & ATTR_SIZE) { if (iap->ia_size < inode->i_size) { @@ -511,24 +512,33 @@ * As this calls fsync (not fdatasync) there is no need for a write_inode * after it. */ +inline void nfsd_dosync(struct file *filp, struct dentry *dp, + struct file_operations *fop) +{ + struct inode *inode = dp->d_inode; + int (*fsync) (struct file *, struct dentry *, int); + + filemap_fdatasync(inode->i_mapping); + if (fop && (fsync = fop->fsync)) + fsync(filp, dp, 0); + filemap_fdatawait(inode->i_mapping); +} + + void nfsd_sync(struct file *filp) { + struct inode *inode = filp->f_dentry->d_inode; dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name); - down(&filp->f_dentry->d_inode->i_sem); - filp->f_op->fsync(filp, filp->f_dentry, 0); - up(&filp->f_dentry->d_inode->i_sem); + down(&inode->i_sem); + nfsd_dosync(filp, filp->f_dentry, filp->f_op); + up(&inode->i_sem); } void nfsd_sync_dir(struct dentry *dp) { - struct inode *inode = dp->d_inode; - int (*fsync) (struct file *, struct dentry *, int); - - if (inode->i_fop && (fsync = inode->i_fop->fsync)) { - fsync(NULL, dp, 0); - } + nfsd_dosync(NULL, dp, dp->d_inode->i_fop); } /* @@ -1226,7 +1236,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, struct svc_fh *tfhp, char *tname, int tlen) { - struct dentry *fdentry, *tdentry, *odentry, *ndentry; + struct dentry *fdentry, *tdentry, *odentry, *ndentry, *trap; struct inode *fdir, *tdir; int err; @@ -1253,7 +1263,7 @@ /* cannot use fh_lock as we need deadlock protective ordering * so do it by hand */ - double_down(&tdir->i_sem, &fdir->i_sem); + trap = lock_rename(tdentry, fdentry); ffhp->fh_locked = tfhp->fh_locked = 1; fill_pre_wcc(ffhp); fill_pre_wcc(tfhp); @@ -1266,12 +1276,17 @@ err = -ENOENT; if (!odentry->d_inode) goto out_dput_old; + err = -EINVAL; + if (odentry == trap) + goto out_dput_old; ndentry = lookup_one_len(tname, tdentry, tlen); err = PTR_ERR(ndentry); if (IS_ERR(ndentry)) goto out_dput_old; - + err = -ENOTEMPTY; + if (ndentry == trap) + goto out_dput_new; #ifdef MSNFS if ((ffhp->fh_export->ex_flags & NFSEXP_MSNFS) && @@ -1287,6 +1302,8 @@ } dput(ndentry); + out_dput_new: + dput(ndentry); out_dput_old: dput(odentry); out_nfserr: @@ -1299,9 +1316,9 @@ */ fill_post_wcc(ffhp); fill_post_wcc(tfhp); - double_up(&tdir->i_sem, &fdir->i_sem); + unlock_rename(tdentry, fdentry); ffhp->fh_locked = tfhp->fh_locked = 0; - + out: return err; } @@ -1375,7 +1392,6 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, encode_dent_fn func, u32 *buffer, int *countp, u32 *verf) { - struct inode *inode; u32 *p; int oldlen, eof, err; struct file file; @@ -1387,9 +1403,6 @@ if (offset > ~(u32) 0) goto out_close; - err = nfserr_notdir; - if (!file.f_op->readdir) - goto out_close; file.f_pos = offset; /* Set up the readdir context */ @@ -1404,25 +1417,16 @@ * readdir() is not guaranteed to fill up the entire buffer, but * may choose to do less. */ - inode = file.f_dentry->d_inode; - down(&inode->i_sem); - while (1) { + + do { oldlen = cd.buflen; - /* - dprintk("nfsd: f_op->readdir(%s/%ld @ %d) buflen = %d (%d)\n", - file.f_inode->i_sb->s_id, file.f_inode->i_ino, - (int) file.f_pos, (int) oldlen, (int) cd.buflen); - */ - err = file.f_op->readdir(&file, &cd, (filldir_t) func); + err = vfs_readdir(&file, (filldir_t) func, &cd); + if (err < 0) goto out_nfserr; - if (oldlen == cd.buflen) - break; - if (cd.eob) - break; - } - up(&inode->i_sem); + + } while (oldlen != cd.buflen && !cd.eob); /* If we didn't fill the buffer completely, we're at EOF */ eof = !cd.eob; @@ -1449,7 +1453,6 @@ return err; out_nfserr: - up(&inode->i_sem); err = nfserrno(err); goto out_close; } diff -Nru a/fs/ntfs/fs.c b/fs/ntfs/fs.c --- a/fs/ntfs/fs.c Tue Feb 19 18:08:58 2002 +++ b/fs/ntfs/fs.c Tue Feb 19 18:08:58 2002 @@ -276,10 +276,9 @@ ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling " "filldir for .. with len 2, f_pos 0x%Lx, " "inode %lu, DT_DIR.\n", filp->f_pos, - filp->f_dentry->d_parent->d_inode->i_ino); + parent_ino(filp->f_dentry)); cb.ret_code = filldir(dirent, "..", 2, filp->f_pos, - filp->f_dentry->d_parent->d_inode->i_ino, - DT_DIR); + parent_ino(filp->f_dentry), DT_DIR); if (cb.ret_code) goto done; cb.pl++; @@ -529,6 +528,7 @@ walk.name = NULL; walk.namelen = 0; /* Convert to wide string. */ + lock_kernel(); err = ntfs_decodeuni(NTFS_INO2VOL(dir), (char*)d->d_name.name, d->d_name.len, &walk.name, &walk.namelen); if (err) @@ -548,10 +548,12 @@ d_add(d, res); ntfs_free(item); ntfs_free(walk.name); + unlock_kernel(); /* Always return success, the dcache will handle negative entries. */ return NULL; err_ret: ntfs_free(walk.name); + unlock_kernel(); return ERR_PTR(err); } @@ -575,6 +577,7 @@ int error = 0; ntfs_attribute *si; + lock_kernel(); r = new_inode(dir->i_sb); if (!r) { error = -ENOMEM; @@ -619,10 +622,12 @@ r->i_mode |= S_IWUGO; #endif r->i_mode &= ~vol->umask; + unlock_kernel(); insert_inode_hash(r); d_instantiate(d, r); return 0; fail: + unlock_kernel(); if (r) iput(r); return error; @@ -636,6 +641,7 @@ ntfs_inode *ino; ntfs_attribute *si; + lock_kernel(); ntfs_debug (DEBUG_DIR1, "mkdir %s in %x\n", d->d_name.name, dir->i_ino); error = -ENAMETOOLONG; if (d->d_name.len > /* FIXME: */ 255) @@ -676,6 +682,7 @@ error = 0; out: ntfs_debug (DEBUG_DIR1, "mkdir returns %d\n", error); + unlock_kernel(); return error; } #endif diff -Nru a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c --- a/fs/openpromfs/inode.c Tue Feb 19 18:08:59 2002 +++ b/fs/openpromfs/inode.c Tue Feb 19 18:08:59 2002 @@ -633,6 +633,7 @@ inode = NULL; name = dentry->d_name.name; len = dentry->d_name.len; + lock_kernel(); if (name [0] == '.' && len == 5 && !strncmp (name + 1, "node", 4)) { ino = NODEP2INO(NODE(dir->i_ino).first_prop); type = OPFSL_NODENUM; @@ -692,10 +693,13 @@ ino = lookup_children (NODE(dir->i_ino).child, name, len); if (ino) type = OPFSL_DIR; - else + else { + unlock_kernel(); return ERR_PTR(-ENOENT); + } } inode = iget (dir->i_sb, ino); + unlock_kernel(); if (!inode) return ERR_PTR(-EINVAL); switch (type) { @@ -829,24 +833,31 @@ return -ENOENT; if (dentry->d_name.len > 256) return -EINVAL; - if (aliases_nodes == ALIASES_NNODES) - return -EIO; p = kmalloc (dentry->d_name.len + 1, GFP_KERNEL); if (!p) return -ENOMEM; strncpy (p, dentry->d_name.name, dentry->d_name.len); p [dentry->d_name.len] = 0; + lock_kernel(); + if (aliases_nodes == ALIASES_NNODES) { + kfree(p); + unlock_kernel(); + return -EIO; + } alias_names [aliases_nodes++] = p; inode = iget (dir->i_sb, NODEP2INO(NODE(dir->i_ino).first_prop) + aliases_nodes); - if (!inode) + if (!inode) { + unlock_kernel(); return -EINVAL; + } inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR; inode->i_fop = &openpromfs_prop_ops; inode->i_nlink = 1; if (inode->i_size < 0) inode->i_size = 0; inode->u.generic_ip = (void *)(long)(((u16)aliases) | (((u16)(aliases_nodes - 1)) << 16)); + unlock_kernel(); d_instantiate(dentry, inode); return 0; } @@ -860,6 +871,7 @@ name = dentry->d_name.name; len = dentry->d_name.len; + lock_kernel(); for (i = 0; i < aliases_nodes; i++) if ((strlen (alias_names [i]) == len) && !strncmp (name, alias_names[i], len)) { @@ -873,6 +885,7 @@ buffer [10 + len] = 0; prom_feval (buffer); } + unlock_kernel(); return 0; } diff -Nru a/fs/proc/array.c b/fs/proc/array.c --- a/fs/proc/array.c Tue Feb 19 18:08:57 2002 +++ b/fs/proc/array.c Tue Feb 19 18:08:57 2002 @@ -336,12 +336,8 @@ /* scale priority and nice values from timeslices to -20..20 */ /* to make it look like a "normal" Unix priority/nice value */ - priority = task->prio; - if (priority >= MAX_RT_PRIO) - priority -= MAX_RT_PRIO; - else - priority = priority-100; - nice = task->__nice; + priority = task_prio(task); + nice = task_nice(task); read_lock(&tasklist_lock); ppid = task->pid ? task->p_opptr->pid : 0; @@ -397,11 +393,10 @@ return res; } -static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, - int * pages, int * shared, int * dirty, int * total) +static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, int * pages, int * shared, int * dirty, int * total) { - pte_t * pte; - unsigned long end; + unsigned long end, pmd_end; + pte_t *pte; if (pmd_none(*pmd)) return; @@ -410,11 +405,12 @@ pmd_clear(pmd); return; } - pte = pte_offset(pmd, address); - address &= ~PMD_MASK; + preempt_disable(); + pte = pte_offset_map(pmd, address); end = address + size; - if (end > PMD_SIZE) - end = PMD_SIZE; + pmd_end = (address + PMD_SIZE) & PMD_MASK; + if (end > pmd_end) + end = pmd_end; do { pte_t page = *pte; struct page *ptpage; @@ -435,6 +431,8 @@ if (page_count(pte_page(page)) > 1) ++*shared; } while (address < end); + pte_unmap(pte - 1); + preempt_enable(); } static inline void statm_pmd_range(pgd_t * pgd, unsigned long address, unsigned long size, diff -Nru a/fs/proc/base.c b/fs/proc/base.c --- a/fs/proc/base.c Tue Feb 19 18:08:57 2002 +++ b/fs/proc/base.c Tue Feb 19 18:08:57 2002 @@ -623,8 +623,10 @@ } while (i); ino = fake_ino(pid, PROC_PID_FD_DIR + fd); - if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino, DT_LNK) < 0) + if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino, DT_LNK) < 0) { + read_lock(&files->file_lock); break; + } read_lock(&files->file_lock); } read_unlock(&files->file_lock); @@ -777,6 +779,7 @@ /* Lookups */ #define MAX_MULBY10 ((~0U-9)/10) +/* SMP-safe */ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) { unsigned int fd, c; @@ -855,6 +858,7 @@ permission: proc_permission, }; +/* SMP-safe */ static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry) { struct inode *inode; @@ -984,6 +988,7 @@ follow_link: proc_self_follow_link, }; +/* SMP-safe */ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry) { unsigned int pid, c; diff -Nru a/fs/proc/generic.c b/fs/proc/generic.c --- a/fs/proc/generic.c Tue Feb 19 18:09:00 2002 +++ b/fs/proc/generic.c Tue Feb 19 18:09:00 2002 @@ -258,12 +258,11 @@ */ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry) { - struct inode *inode; + struct inode *inode = NULL; struct proc_dir_entry * de; - int error; + int error = -ENOENT; - error = -ENOENT; - inode = NULL; + lock_kernel(); de = PDE(dir); if (de) { for (de = de->subdir; de ; de = de->next) { @@ -279,6 +278,7 @@ } } } + unlock_kernel(); if (inode) { dentry->d_op = &proc_dentry_operations; @@ -319,7 +319,7 @@ /* fall through */ case 1: if (filldir(dirent, "..", 2, i, - filp->f_dentry->d_parent->d_inode->i_ino, + parent_ino(filp->f_dentry), DT_DIR) < 0) return 0; i++; diff -Nru a/fs/proc/inode.c b/fs/proc/inode.c --- a/fs/proc/inode.c Tue Feb 19 18:08:59 2002 +++ b/fs/proc/inode.c Tue Feb 19 18:08:59 2002 @@ -13,6 +13,7 @@ #include #include #include +#include #define __NO_VERSION__ #include #include diff -Nru a/fs/proc/root.c b/fs/proc/root.c --- a/fs/proc/root.c Tue Feb 19 18:08:58 2002 +++ b/fs/proc/root.c Tue Feb 19 18:08:58 2002 @@ -16,6 +16,7 @@ #include #include #include +#include struct proc_dir_entry *proc_net, *proc_bus, *proc_root_fs, *proc_root_driver; @@ -80,15 +81,14 @@ static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry) { if (dir->i_ino == PROC_ROOT_INO) { /* check for safety... */ - int nlink = proc_root.nlink; - - nlink += nr_threads; - - dir->i_nlink = nlink; + lock_kernel(); + dir->i_nlink = proc_root.nlink + nr_threads; + unlock_kernel(); } - if (!proc_lookup(dir, dentry)) + if (!proc_lookup(dir, dentry)) { return NULL; + } return proc_pid_lookup(dir, dentry); } diff -Nru a/fs/qnx4/inode.c b/fs/qnx4/inode.c --- a/fs/qnx4/inode.c Tue Feb 19 18:08:59 2002 +++ b/fs/qnx4/inode.c Tue Feb 19 18:08:59 2002 @@ -63,9 +63,9 @@ static void qnx4_delete_inode(struct inode *inode) { QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino)); - lock_kernel(); inode->i_size = 0; qnx4_truncate(inode); + lock_kernel(); qnx4_free_inode(inode); unlock_kernel(); } diff -Nru a/fs/qnx4/namei.c b/fs/qnx4/namei.c --- a/fs/qnx4/namei.c Tue Feb 19 18:08:57 2002 +++ b/fs/qnx4/namei.c Tue Feb 19 18:08:57 2002 @@ -21,6 +21,7 @@ #include #include #include +#include /* @@ -115,6 +116,7 @@ int len = dentry->d_name.len; struct inode *foundinode = NULL; + lock_kernel(); if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) goto out; /* The entry is linked, let's get the real info */ @@ -127,10 +129,12 @@ brelse(bh); if ((foundinode = iget(dir->i_sb, ino)) == NULL) { + unlock_kernel(); QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); return ERR_PTR(-EACCES); } out: + unlock_kernel(); d_add(dentry, foundinode); return NULL; @@ -155,9 +159,11 @@ int ino; QNX4DEBUG(("qnx4: qnx4_rmdir [%s]\n", dentry->d_name.name)); + lock_kernel(); bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, &de, &ino); if (bh == NULL) { + unlock_kernel(); return -ENOENT; } inode = dentry->d_inode; @@ -189,6 +195,7 @@ end_rmdir: brelse(bh); + unlock_kernel(); return retval; } @@ -201,9 +208,11 @@ int ino; QNX4DEBUG(("qnx4: qnx4_unlink [%s]\n", dentry->d_name.name)); + lock_kernel(); bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, &de, &ino); if (bh == NULL) { + unlock_kernel(); return -ENOENT; } inode = dentry->d_inode; @@ -229,7 +238,8 @@ mark_inode_dirty(inode); retval = 0; - end_unlink: +end_unlink: + unlock_kernel(); brelse(bh); return retval; diff -Nru a/fs/qnx4/truncate.c b/fs/qnx4/truncate.c --- a/fs/qnx4/truncate.c Tue Feb 19 18:08:59 2002 +++ b/fs/qnx4/truncate.c Tue Feb 19 18:08:59 2002 @@ -16,6 +16,7 @@ #include #include #include +#include #include #ifdef CONFIG_QNX4FS_RW @@ -26,12 +27,14 @@ S_ISLNK(inode->i_mode))) { return; } + lock_kernel(); if (!(S_ISDIR(inode->i_mode))) { /* TODO */ } QNX4DEBUG(("qnx4: qnx4_truncate called\n")); inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); + unlock_kernel(); } #endif diff -Nru a/fs/ramfs/inode.c b/fs/ramfs/inode.c --- a/fs/ramfs/inode.c Tue Feb 19 18:08:59 2002 +++ b/fs/ramfs/inode.c Tue Feb 19 18:08:59 2002 @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -53,6 +54,7 @@ * Lookup the data. This is trivial - if the dentry didn't already * exist, we know it is negative. */ +/* SMP-safe */ static struct dentry * ramfs_lookup(struct inode *dir, struct dentry *dentry) { d_add(dentry, NULL); @@ -133,6 +135,7 @@ /* * File creation. Allocate an inode, and we're done.. */ +/* SMP-safe */ static int ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) { struct inode * inode = ramfs_get_inode(dir->i_sb, mode, dev); @@ -163,9 +166,6 @@ { struct inode *inode = old_dentry->d_inode; - if (S_ISDIR(inode->i_mode)) - return -EPERM; - inode->i_nlink++; atomic_inc(&inode->i_count); /* New dentry reference */ dget(dentry); /* Extra pinning count for the created dentry */ @@ -249,13 +249,18 @@ static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname) { - int error; + struct inode *inode; + int error = -ENOSPC; - error = ramfs_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0); - if (!error) { + inode = ramfs_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0); + if (inode) { int l = strlen(symname)+1; - struct inode *inode = dentry->d_inode; error = block_symlink(inode, symname, l); + if (!error) { + d_instantiate(dentry, inode); + dget(dentry); + } else + iput(inode); } return error; } diff -Nru a/fs/read_write.c b/fs/read_write.c --- a/fs/read_write.c Tue Feb 19 18:08:57 2002 +++ b/fs/read_write.c Tue Feb 19 18:08:57 2002 @@ -189,8 +189,7 @@ } } if (ret > 0) - inode_dir_notify(file->f_dentry->d_parent->d_inode, - DN_ACCESS); + dnotify_parent(file->f_dentry, DN_ACCESS); fput(file); } return ret; @@ -216,8 +215,7 @@ } } if (ret > 0) - inode_dir_notify(file->f_dentry->d_parent->d_inode, - DN_MODIFY); + dnotify_parent(file->f_dentry, DN_MODIFY); fput(file); } return ret; @@ -321,7 +319,7 @@ out_nofree: /* VERIFY_WRITE actually means a read, as we write to user space */ if ((ret + (type == VERIFY_WRITE)) > 0) - inode_dir_notify(file->f_dentry->d_parent->d_inode, + dnotify_parent(file->f_dentry, (type == VERIFY_WRITE) ? DN_MODIFY : DN_ACCESS); return ret; } @@ -394,7 +392,7 @@ goto out; ret = read(file, buf, count, &pos); if (ret > 0) - inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_ACCESS); + dnotify_parent(file->f_dentry, DN_ACCESS); out: fput(file); bad_file: @@ -426,7 +424,7 @@ ret = write(file, buf, count, &pos); if (ret > 0) - inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_MODIFY); + dnotify_parent(file->f_dentry, DN_MODIFY); out: fput(file); bad_file: diff -Nru a/fs/readdir.c b/fs/readdir.c --- a/fs/readdir.c Tue Feb 19 18:09:00 2002 +++ b/fs/readdir.c Tue Feb 19 18:09:00 2002 @@ -21,14 +21,12 @@ if (!file->f_op || !file->f_op->readdir) goto out; down(&inode->i_sem); - down(&inode->i_zombie); res = -ENOENT; if (!IS_DEADDIR(inode)) { lock_kernel(); res = file->f_op->readdir(file, buf, filler); unlock_kernel(); } - up(&inode->i_zombie); up(&inode->i_sem); out: return res; @@ -54,7 +52,7 @@ filp->f_pos++; /* fallthrough */ case 1: - if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) + if (filldir(dirent, "..", 2, i, parent_ino(dentry), DT_DIR) < 0) break; i++; filp->f_pos++; diff -Nru a/fs/reiserfs/dir.c b/fs/reiserfs/dir.c --- a/fs/reiserfs/dir.c Tue Feb 19 18:08:57 2002 +++ b/fs/reiserfs/dir.c Tue Feb 19 18:08:57 2002 @@ -115,13 +115,13 @@ if (d_reclen <= 32) { local_buf = small_buf ; } else { - local_buf = kmalloc(d_reclen, GFP_NOFS) ; + local_buf = reiserfs_kmalloc(d_reclen, GFP_NOFS, inode->i_sb) ; if (!local_buf) { pathrelse (&path_to_entry); return -ENOMEM ; } if (item_moved (&tmp_ih, &path_to_entry)) { - kfree(local_buf) ; + reiserfs_kfree(local_buf, d_reclen, inode->i_sb) ; goto research; } } @@ -133,12 +133,12 @@ if (filldir (dirent, local_buf, d_reclen, d_off, d_ino, DT_UNKNOWN) < 0) { if (local_buf != small_buf) { - kfree(local_buf) ; + reiserfs_kfree(local_buf, d_reclen, inode->i_sb) ; } goto end; } if (local_buf != small_buf) { - kfree(local_buf) ; + reiserfs_kfree(local_buf, d_reclen, inode->i_sb) ; } // next entry should be looked for with such offset diff -Nru a/fs/reiserfs/file.c b/fs/reiserfs/file.c --- a/fs/reiserfs/file.c Tue Feb 19 18:08:57 2002 +++ b/fs/reiserfs/file.c Tue Feb 19 18:08:57 2002 @@ -66,7 +66,9 @@ } static void reiserfs_vfs_truncate_file(struct inode *inode) { + lock_kernel(); reiserfs_truncate_file(inode, 1) ; + unlock_kernel(); } /* Sync a reiserfs file. */ diff -Nru a/fs/reiserfs/fix_node.c b/fs/reiserfs/fix_node.c --- a/fs/reiserfs/fix_node.c Tue Feb 19 18:08:59 2002 +++ b/fs/reiserfs/fix_node.c Tue Feb 19 18:08:59 2002 @@ -1979,7 +1979,7 @@ return CARRY_ON; } - +#ifdef CONFIG_REISERFS_CHECK void * reiserfs_kmalloc (size_t size, int flags, struct super_block * s) { void * vp; @@ -2007,6 +2007,7 @@ reiserfs_warning ("vs-8302: reiserfs_kfree: allocated memory %d\n", s->u.reiserfs_sb.s_kmallocs); } +#endif static int get_virtual_node_size (struct super_block * sb, struct buffer_head * bh) diff -Nru a/fs/reiserfs/item_ops.c b/fs/reiserfs/item_ops.c --- a/fs/reiserfs/item_ops.c Tue Feb 19 18:08:58 2002 +++ b/fs/reiserfs/item_ops.c Tue Feb 19 18:08:58 2002 @@ -685,17 +685,110 @@ ////////////////////////////////////////////////////////////////////////////// +// Error catching functions to catch errors caused by incorrect item types. +// +static int errcatch_bytes_number (struct item_head * ih, int block_size) +{ + reiserfs_warning ("green-16001: Invalid item type observed, run fsck ASAP\n"); + return 0; +} + +static void errcatch_decrement_key (struct cpu_key * key) +{ + reiserfs_warning ("green-16002: Invalid item type observed, run fsck ASAP\n"); +} + + +static int errcatch_is_left_mergeable (struct key * key, unsigned long bsize) +{ + reiserfs_warning ("green-16003: Invalid item type observed, run fsck ASAP\n"); + return 0; +} + + +static void errcatch_print_item (struct item_head * ih, char * item) +{ + reiserfs_warning ("green-16004: Invalid item type observed, run fsck ASAP\n"); +} + + +static void errcatch_check_item (struct item_head * ih, char * item) +{ + reiserfs_warning ("green-16005: Invalid item type observed, run fsck ASAP\n"); +} + +static int errcatch_create_vi (struct virtual_node * vn, + struct virtual_item * vi, + int is_affected, + int insert_size) +{ + reiserfs_warning ("green-16006: Invalid item type observed, run fsck ASAP\n"); + return 0; // We might return -1 here as well, but it won't help as create_virtual_node() from where + // this operation is called from is of return type void. +} + +static int errcatch_check_left (struct virtual_item * vi, int free, + int start_skip, int end_skip) +{ + reiserfs_warning ("green-16007: Invalid item type observed, run fsck ASAP\n"); + return -1; +} + + +static int errcatch_check_right (struct virtual_item * vi, int free) +{ + reiserfs_warning ("green-16008: Invalid item type observed, run fsck ASAP\n"); + return -1; +} + +static int errcatch_part_size (struct virtual_item * vi, int first, int count) +{ + reiserfs_warning ("green-16009: Invalid item type observed, run fsck ASAP\n"); + return 0; +} + +static int errcatch_unit_num (struct virtual_item * vi) +{ + reiserfs_warning ("green-16010: Invalid item type observed, run fsck ASAP\n"); + return 0; +} + +static void errcatch_print_vi (struct virtual_item * vi) +{ + reiserfs_warning ("green-16011: Invalid item type observed, run fsck ASAP\n"); +} + +struct item_operations errcatch_ops = { + errcatch_bytes_number, + errcatch_decrement_key, + errcatch_is_left_mergeable, + errcatch_print_item, + errcatch_check_item, + + errcatch_create_vi, + errcatch_check_left, + errcatch_check_right, + errcatch_part_size, + errcatch_unit_num, + errcatch_print_vi +}; + + + +////////////////////////////////////////////////////////////////////////////// // // #if ! (TYPE_STAT_DATA == 0 && TYPE_INDIRECT == 1 && TYPE_DIRECT == 2 && TYPE_DIRENTRY == 3) do not compile #endif -struct item_operations * item_ops [4] = { +struct item_operations * item_ops [TYPE_ANY + 1] = { &stat_data_ops, &indirect_ops, &direct_ops, - &direntry_ops + &direntry_ops, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &errcatch_ops /* This is to catch errors with invalid type (15th entry for TYPE_ANY) */ }; diff -Nru a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c --- a/fs/reiserfs/journal.c Tue Feb 19 18:08:57 2002 +++ b/fs/reiserfs/journal.c Tue Feb 19 18:08:57 2002 @@ -71,7 +71,9 @@ static DECLARE_WAIT_QUEUE_HEAD(reiserfs_commit_thread_done) ; DECLARE_TASK_QUEUE(reiserfs_commit_thread_tq) ; -#define JOURNAL_TRANS_HALF 1018 /* must be correct to keep the desc and commit structs at 4k */ +#define JOURNAL_TRANS_HALF 1018 /* must be correct to keep the desc and commit + structs at 4k */ +#define BUFNR 64 /*read ahead */ /* cnode stat bits. Move these into reiserfs_fs.h */ @@ -118,13 +120,13 @@ struct reiserfs_bitmap_node *bn ; static int id = 0 ; - bn = kmalloc(sizeof(struct reiserfs_bitmap_node), GFP_NOFS) ; + bn = reiserfs_kmalloc(sizeof(struct reiserfs_bitmap_node), GFP_NOFS, p_s_sb) ; if (!bn) { return NULL ; } - bn->data = kmalloc(p_s_sb->s_blocksize, GFP_NOFS) ; + bn->data = reiserfs_kmalloc(p_s_sb->s_blocksize, GFP_NOFS, p_s_sb) ; if (!bn->data) { - kfree(bn) ; + reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb) ; return NULL ; } bn->id = id++ ; @@ -159,8 +161,8 @@ struct reiserfs_bitmap_node *bn) { SB_JOURNAL(p_s_sb)->j_used_bitmap_nodes-- ; if (SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes > REISERFS_MAX_BITMAP_NODES) { - kfree(bn->data) ; - kfree(bn) ; + reiserfs_kfree(bn->data, p_s_sb->s_blocksize, p_s_sb) ; + reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb) ; } else { list_add(&bn->list, &SB_JOURNAL(p_s_sb)->j_bitmap_nodes) ; SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes++ ; @@ -228,8 +230,8 @@ while(next != &SB_JOURNAL(p_s_sb)->j_bitmap_nodes) { bn = list_entry(next, struct reiserfs_bitmap_node, list) ; list_del(next) ; - kfree(bn->data) ; - kfree(bn) ; + reiserfs_kfree(bn->data, p_s_sb->s_blocksize, p_s_sb) ; + reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb) ; next = SB_JOURNAL(p_s_sb)->j_bitmap_nodes.next ; SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes-- ; } @@ -1507,13 +1509,13 @@ } trans_id = le32_to_cpu(desc->j_trans_id) ; /* now we know we've got a good transaction, and it was inside the valid time ranges */ - log_blocks = kmalloc(le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), GFP_NOFS) ; - real_blocks = kmalloc(le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), GFP_NOFS) ; + log_blocks = reiserfs_kmalloc(le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), GFP_NOFS, p_s_sb) ; + real_blocks = reiserfs_kmalloc(le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), GFP_NOFS, p_s_sb) ; if (!log_blocks || !real_blocks) { brelse(c_bh) ; brelse(d_bh) ; - kfree(log_blocks) ; - kfree(real_blocks) ; + reiserfs_kfree(log_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ; + reiserfs_kfree(real_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ; reiserfs_warning("journal-1169: kmalloc failed, unable to mount FS\n") ; return -1 ; } @@ -1532,8 +1534,8 @@ brelse_array(real_blocks, i) ; brelse(c_bh) ; brelse(d_bh) ; - kfree(log_blocks) ; - kfree(real_blocks) ; + reiserfs_kfree(log_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ; + reiserfs_kfree(real_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ; return -1 ; } } @@ -1547,8 +1549,8 @@ brelse_array(real_blocks, le32_to_cpu(desc->j_len)) ; brelse(c_bh) ; brelse(d_bh) ; - kfree(log_blocks) ; - kfree(real_blocks) ; + reiserfs_kfree(log_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ; + reiserfs_kfree(real_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ; return -1 ; } memcpy(real_blocks[i]->b_data, log_blocks[i]->b_data, real_blocks[i]->b_size) ; @@ -1567,8 +1569,8 @@ brelse_array(real_blocks + i, le32_to_cpu(desc->j_len) - i) ; brelse(c_bh) ; brelse(d_bh) ; - kfree(log_blocks) ; - kfree(real_blocks) ; + reiserfs_kfree(log_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ; + reiserfs_kfree(real_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ; return -1 ; } brelse(real_blocks[i]) ; @@ -1584,8 +1586,8 @@ SB_JOURNAL(p_s_sb)->j_trans_id = trans_id + 1; brelse(c_bh) ; brelse(d_bh) ; - kfree(log_blocks) ; - kfree(real_blocks) ; + reiserfs_kfree(log_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ; + reiserfs_kfree(real_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ; return 0 ; } @@ -1598,6 +1600,41 @@ ** ** On exit, it sets things up so the first transaction will work correctly. */ +struct buffer_head * reiserfs_breada (kdev_t dev, int block, int bufsize, + unsigned int max_block) +{ + struct buffer_head * bhlist[BUFNR]; + unsigned int blocks = BUFNR; + struct buffer_head * bh; + int i, j; + + bh = getblk (dev, block, bufsize); + if (buffer_uptodate (bh)) + return (bh); + + if (block + BUFNR > max_block) { + blocks = max_block - block; + } + bhlist[0] = bh; + j = 1; + for (i = 1; i < blocks; i++) { + bh = getblk (dev, block + i, bufsize); + if (buffer_uptodate (bh)) { + brelse (bh); + break; + } + else bhlist[j++] = bh; + } + ll_rw_block (READ, j, bhlist); + for(i = 1; i < j; i++) + brelse (bhlist[i]); + bh = bhlist[0]; + wait_on_buffer (bh); + if (buffer_uptodate (bh)) + return bh; + brelse (bh); + return NULL; +} static int journal_read(struct super_block *p_s_sb) { struct reiserfs_journal_desc *desc ; unsigned long last_flush_trans_id = 0 ; @@ -1668,7 +1705,8 @@ ** all the valid transactions, and pick out the oldest. */ while(continue_replay && cur_dblock < (SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_ONDISK_JOURNAL_SIZE(p_s_sb))) { - d_bh = bread(SB_JOURNAL_DEV(p_s_sb), cur_dblock, p_s_sb->s_blocksize) ; + d_bh = reiserfs_breada(p_s_sb->s_dev, cur_dblock, p_s_sb->s_blocksize, + SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_ONDISK_JOURNAL_SIZE(p_s_sb)) ; ret = journal_transaction_is_valid(p_s_sb, d_bh, &oldest_invalid_trans_id, &newest_mount_id) ; if (ret == 1) { desc = (struct reiserfs_journal_desc *)d_bh->b_data ; @@ -1785,7 +1823,7 @@ atomic_read(&(jl->j_commit_left)) == 0) { kupdate_one_transaction(ct->p_s_sb, jl) ; } - kfree(ct->self) ; + reiserfs_kfree(ct->self, sizeof(struct reiserfs_journal_commit_task), ct->p_s_sb) ; } static void setup_commit_task_arg(struct reiserfs_journal_commit_task *ct, @@ -1809,7 +1847,7 @@ /* using GFP_NOFS, GFP_KERNEL could try to flush inodes, which will try ** to start/join a transaction, which will deadlock */ - ct = kmalloc(sizeof(struct reiserfs_journal_commit_task), GFP_NOFS) ; + ct = reiserfs_kmalloc(sizeof(struct reiserfs_journal_commit_task), GFP_NOFS, p_s_sb) ; if (ct) { setup_commit_task_arg(ct, p_s_sb, jindex) ; queue_task(&(ct->task), &reiserfs_commit_thread_tq); @@ -1839,7 +1877,7 @@ spin_lock_irq(¤t->sigmask_lock); sigfillset(¤t->blocked); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); sprintf(current->comm, "kreiserfsd") ; diff -Nru a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c --- a/fs/reiserfs/namei.c Tue Feb 19 18:08:57 2002 +++ b/fs/reiserfs/namei.c Tue Feb 19 18:08:57 2002 @@ -340,7 +340,7 @@ static struct dentry * reiserfs_lookup (struct inode * dir, struct dentry * dentry) { int retval; - struct inode * inode = 0; + struct inode * inode = NULL; struct reiserfs_dir_entry de; INITIALIZE_PATH (path_to_entry); @@ -349,15 +349,21 @@ if (dentry->d_name.len > REISERFS_MAX_NAME_LEN (dir->i_sb->s_blocksize)) return ERR_PTR(-ENAMETOOLONG); + lock_kernel(); de.de_gen_number_bit_string = 0; retval = reiserfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &path_to_entry, &de); pathrelse (&path_to_entry); if (retval == NAME_FOUND) { inode = reiserfs_iget (dir->i_sb, (struct cpu_key *)&(de.de_dir_id)); if (!inode || IS_ERR(inode)) { + unlock_kernel(); return ERR_PTR(-EACCES); } } + unlock_kernel(); + if ( retval == IO_ERROR ) { + return ERR_PTR(-EIO); + } d_add(dentry, inode); return NULL; @@ -443,6 +449,10 @@ reiserfs_kfree (buffer, buflen, dir->i_sb); pathrelse (&path); + if ( retval == IO_ERROR ) { + return -EIO; + } + if (retval != NAME_FOUND) { reiserfs_warning ("zam-7002:" __FUNCTION__ ": \"reiserfs_find_entry\" has returned" " unexpected value (%d)\n", retval); @@ -515,11 +525,11 @@ int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 ; struct reiserfs_transaction_handle th ; - inode = new_inode(dir->i_sb) ; if (!inode) { return -ENOMEM ; } + lock_kernel(); journal_begin(&th, dir->i_sb, jbegin_count) ; th.t_caller = "create" ; windex = push_journal_writer("reiserfs_create") ; @@ -527,6 +537,7 @@ if (!inode) { pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return retval; } @@ -544,6 +555,7 @@ // in the same transactioin journal_end(&th, dir->i_sb, jbegin_count) ; iput (inode); + unlock_kernel(); return retval; } reiserfs_update_inode_transaction(inode) ; @@ -552,6 +564,7 @@ d_instantiate(dentry, inode); pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return 0; } @@ -575,6 +588,7 @@ if (!inode) { return -ENOMEM ; } + lock_kernel(); journal_begin(&th, dir->i_sb, jbegin_count) ; windex = push_journal_writer("reiserfs_mknod") ; @@ -582,6 +596,7 @@ if (!inode) { pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return retval; } @@ -601,12 +616,14 @@ pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; iput (inode); + unlock_kernel(); return retval; } d_instantiate(dentry, inode); pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return 0; } @@ -630,6 +647,7 @@ if (!inode) { return -ENOMEM ; } + lock_kernel(); journal_begin(&th, dir->i_sb, jbegin_count) ; windex = push_journal_writer("reiserfs_mkdir") ; @@ -646,6 +664,7 @@ pop_journal_writer(windex) ; dir->i_nlink-- ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return retval; } reiserfs_update_inode_transaction(inode) ; @@ -664,6 +683,7 @@ pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; iput (inode); + unlock_kernel(); return retval; } @@ -673,6 +693,7 @@ d_instantiate(dentry, inode); pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return 0; } @@ -711,14 +732,19 @@ /* we will be doing 2 balancings and update 2 stat data */ jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2; + lock_kernel(); journal_begin(&th, dir->i_sb, jbegin_count) ; windex = push_journal_writer("reiserfs_rmdir") ; de.de_gen_number_bit_string = 0; - if (reiserfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &path, &de) == NAME_NOT_FOUND) { + if ( (retval = reiserfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &path, &de)) == NAME_NOT_FOUND) { retval = -ENOENT; goto end_rmdir; + } else if ( retval == IO_ERROR) { + retval = -EIO; + goto end_rmdir; } + inode = dentry->d_inode; reiserfs_update_inode_transaction(inode) ; @@ -760,6 +786,7 @@ pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; reiserfs_check_path(&path) ; + unlock_kernel(); return 0; end_rmdir: @@ -769,6 +796,7 @@ pathrelse (&path); pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return retval; } @@ -796,13 +824,17 @@ two stat datas */ jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2; + lock_kernel(); journal_begin(&th, dir->i_sb, jbegin_count) ; windex = push_journal_writer("reiserfs_unlink") ; de.de_gen_number_bit_string = 0; - if (reiserfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &path, &de) == NAME_NOT_FOUND) { + if ( (retval = reiserfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &path, &de)) == NAME_NOT_FOUND) { retval = -ENOENT; goto end_unlink; + } else if (retval == IO_ERROR) { + retval = -EIO; + goto end_unlink; } reiserfs_update_inode_transaction(inode) ; @@ -841,6 +873,7 @@ pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; reiserfs_check_path(&path) ; + unlock_kernel(); return 0; end_unlink: @@ -848,6 +881,7 @@ pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; reiserfs_check_path(&path) ; + unlock_kernel(); return retval; } @@ -881,9 +915,11 @@ return -ENAMETOOLONG; } - name = kmalloc (item_len, GFP_NOFS); + lock_kernel(); + name = reiserfs_kmalloc (item_len, GFP_NOFS, dir->i_sb); if (!name) { iput(inode) ; + unlock_kernel(); return -ENOMEM; } memcpy (name, symname, strlen (symname)); @@ -894,10 +930,11 @@ inode = reiserfs_new_inode (&th, dir, S_IFLNK | S_IRWXUGO, name, strlen (symname), dentry, inode, &retval); - kfree (name); + reiserfs_kfree (name, item_len, dir->i_sb); if (inode == 0) { /* reiserfs_new_inode iputs for us */ pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return retval; } @@ -919,12 +956,14 @@ pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; iput (inode); + unlock_kernel(); return retval; } d_instantiate(dentry, inode); pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return 0; } @@ -944,12 +983,10 @@ struct reiserfs_transaction_handle th ; int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; - - if (S_ISDIR(inode->i_mode)) - return -EPERM; - + lock_kernel(); if (inode->i_nlink >= REISERFS_LINK_MAX) { //FIXME: sd_nlink is 32 bit for new files + unlock_kernel(); return -EMLINK; } @@ -966,6 +1003,7 @@ if (retval) { pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return retval; } @@ -977,6 +1015,7 @@ d_instantiate(dentry, inode); pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; + unlock_kernel(); return 0; } @@ -1062,11 +1101,17 @@ // make sure, that oldname still exists and points to an object we // are going to rename old_de.de_gen_number_bit_string = 0; + lock_kernel(); retval = reiserfs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_entry_path, &old_de); pathrelse (&old_entry_path); + if (retval == IO_ERROR) { + unlock_kernel(); + return -EIO; + } + if (retval != NAME_FOUND || old_de.de_objectid != old_inode->i_ino) { - // FIXME: IO error is possible here + unlock_kernel(); return -ENOENT; } @@ -1077,6 +1122,7 @@ if (new_inode) { if (!reiserfs_empty_dir(new_inode)) { + unlock_kernel(); return -ENOTEMPTY; } } @@ -1087,12 +1133,16 @@ dot_dot_de.de_gen_number_bit_string = 0; retval = reiserfs_find_entry (old_inode, "..", 2, &dot_dot_entry_path, &dot_dot_de); pathrelse (&dot_dot_entry_path); - if (retval != NAME_FOUND) + if (retval != NAME_FOUND) { + unlock_kernel(); return -EIO; + } /* inode number of .. must equal old_dir->i_ino */ - if (dot_dot_de.de_objectid != old_dir->i_ino) + if (dot_dot_de.de_objectid != old_dir->i_ino) { + unlock_kernel(); return -EIO; + } } journal_begin(&th, old_dir->i_sb, jbegin_count) ; @@ -1111,6 +1161,7 @@ } else if (retval) { pop_journal_writer(windex) ; journal_end(&th, old_dir->i_sb, jbegin_count) ; + unlock_kernel(); return retval; } @@ -1138,6 +1189,8 @@ new_de.de_gen_number_bit_string = 0; retval = reiserfs_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_entry_path, &new_de); + // reiserfs_add_entry should not return IO_ERROR, because it is called with essentially same parameters from + // reiserfs_add_entry above, and we'll catch any i/o errors before we get here. if (retval != NAME_FOUND_INVISIBLE && retval != NAME_FOUND) BUG (); @@ -1255,6 +1308,7 @@ pop_journal_writer(windex) ; journal_end(&th, old_dir->i_sb, jbegin_count) ; + unlock_kernel(); return 0; } diff -Nru a/fs/reiserfs/prints.c b/fs/reiserfs/prints.c --- a/fs/reiserfs/prints.c Tue Feb 19 18:08:57 2002 +++ b/fs/reiserfs/prints.c Tue Feb 19 18:08:57 2002 @@ -477,6 +477,17 @@ return 0; } +char * reiserfs_hashname(int code) +{ + if ( code == YURA_HASH) + return "rupasov"; + if ( code == TEA_HASH) + return "tea"; + if ( code == R5_HASH) + return "r5"; + + return "unknown"; +} /* return 1 if this is not super block */ static int print_super_block (struct buffer_head * bh) { @@ -519,8 +530,7 @@ printk ("Journal orig size %d\n", sb_jp_journal_size(rs)); printk ("FS state %d\n", sb_fs_state(rs)); printk ("Hash function \"%s\"\n", - sb_hash_function_code(rs) == TEA_HASH ? "tea" : - ( sb_hash_function_code(rs) == YURA_HASH ? "rupasov" : (sb_hash_function_code(rs) == R5_HASH ? "r5" : "unknown"))); + reiserfs_hashname(sb_hash_function_code(rs))); printk ("Tree height %d\n", sb_tree_height(rs)); return 0; diff -Nru a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c --- a/fs/reiserfs/stree.c Tue Feb 19 18:08:58 2002 +++ b/fs/reiserfs/stree.c Tue Feb 19 18:08:58 2002 @@ -524,6 +524,10 @@ ih = (struct item_head *)(buf + BLKH_SIZE); prev_location = blocksize; for (i = 0; i < nr; i ++, ih ++) { + if ( le_ih_k_type(ih) == TYPE_ANY) { + reiserfs_warning ("is_leaf: wrong item type for item %h\n",ih); + return 0; + } if (ih_location (ih) >= blocksize || ih_location (ih) < IH_SIZE * nr) { reiserfs_warning ("is_leaf: item location seems wrong: %h\n", ih); return 0; @@ -1701,8 +1705,7 @@ } if ( n_file_size == 0 || n_file_size < n_new_file_size ) { - pathrelse(&s_search_path); - return; + goto update_and_out ; } /* Update key to search for the last file item. */ @@ -1755,6 +1758,7 @@ "PAP-5680: truncate did not finish: new_file_size %Ld, current %Ld, oid %d\n", n_new_file_size, n_file_size, s_item_key.on_disk_key.k_objectid); +update_and_out: if (update_timestamps) { // this is truncate, not file closing p_s_inode->i_mtime = p_s_inode->i_ctime = CURRENT_TIME; diff -Nru a/fs/reiserfs/super.c b/fs/reiserfs/super.c --- a/fs/reiserfs/super.c Tue Feb 19 18:08:59 2002 +++ b/fs/reiserfs/super.c Tue Feb 19 18:08:59 2002 @@ -115,7 +115,7 @@ protecting unlink is bigger that a key lf "save link" which protects truncate), so there left no items to make truncate completion on */ -static void remove_save_link_only (struct super_block * s, struct key * key) +static void remove_save_link_only (struct super_block * s, struct key * key, int oid_free) { struct reiserfs_transaction_handle th; @@ -123,7 +123,7 @@ journal_begin (&th, s, JOURNAL_PER_BALANCE_CNT); reiserfs_delete_solid_item (&th, key); - if (is_direct_le_key (KEY_FORMAT_3_5, key)) + if (oid_free) /* removals are protected by direct items */ reiserfs_release_objectid (&th, le32_to_cpu (key->k_objectid)); @@ -196,7 +196,7 @@ "save" link and release objectid */ reiserfs_warning ("vs-2180: finish_unfinished: iget failed for %K\n", &obj_key); - remove_save_link_only (s, &save_link_key); + remove_save_link_only (s, &save_link_key, 1); continue; } @@ -204,9 +204,21 @@ /* file is not unlinked */ reiserfs_warning ("vs-2185: finish_unfinished: file %K is not unlinked\n", &obj_key); - remove_save_link_only (s, &save_link_key); + remove_save_link_only (s, &save_link_key, 0); continue; } + + if (truncate && S_ISDIR (inode->i_mode) ) { + /* We got a truncate request for a dir which is impossible. + The only imaginable way is to execute unfinished truncate request + then boot into old kernel, remove the file and create dir with + the same key. */ + reiserfs_warning("green-2101: impossible truncate on a directory %k. Please report\n", INODE_PKEY (inode)); + remove_save_link_only (s, &save_link_key, 0); + truncate = 0; + iput (inode); + continue; + } if (truncate) { REISERFS_I(inode) -> i_flags |= i_link_saved_truncate_mask; @@ -272,6 +284,8 @@ 4/*length*/, 0xffff/*free space*/); } else { /* truncate */ + if (S_ISDIR (inode->i_mode)) + reiserfs_warning("green-2102: Adding a truncate savelink for a directory %k! Please report\n", INODE_PKEY(inode)); set_cpu_key_k_offset (&key, 1); set_cpu_key_k_type (&key, TYPE_INDIRECT); @@ -296,10 +310,11 @@ /* put "save" link inot tree */ retval = reiserfs_insert_item (th, &path, &key, &ih, (char *)&link); - if (retval) - reiserfs_warning ("vs-2120: add_save_link: insert_item returned %d\n", + if (retval) { + if (retval != -ENOSPC) + reiserfs_warning ("vs-2120: add_save_link: insert_item returned %d\n", retval); - else { + } else { if( truncate ) REISERFS_I(inode) -> i_flags |= i_link_saved_truncate_mask; else @@ -640,28 +655,30 @@ static int read_bitmaps (struct super_block * s) { - int i, bmp, dl ; - struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK(s); + int i, bmp; - SB_AP_BITMAP (s) = reiserfs_kmalloc (sizeof (struct buffer_head *) * sb_bmap_nr(rs), GFP_NOFS, s); + SB_AP_BITMAP (s) = reiserfs_kmalloc (sizeof (struct buffer_head *) * SB_BMAP_NR(s), GFP_NOFS, s); if (SB_AP_BITMAP (s) == 0) return 1; - memset (SB_AP_BITMAP (s), 0, sizeof (struct buffer_head *) * sb_bmap_nr(rs)); - - /* reiserfs leaves the first 64k unused so that any partition - labeling scheme currently used will have enough space. Then we - need one block for the super. -Hans */ - bmp = (REISERFS_DISK_OFFSET_IN_BYTES / s->s_blocksize) + 1; /* first of bitmap blocks */ - SB_AP_BITMAP (s)[0] = reiserfs_bread (s, bmp); - if(!SB_AP_BITMAP(s)[0]) - return 1; - for (i = 1, bmp = dl = s->s_blocksize * 8; i < sb_bmap_nr(rs); i ++) { - SB_AP_BITMAP (s)[i] = reiserfs_bread (s, bmp); - if (!SB_AP_BITMAP (s)[i]) + for (i = 0, bmp = REISERFS_DISK_OFFSET_IN_BYTES / s->s_blocksize + 1; + i < SB_BMAP_NR(s); i++, bmp = s->s_blocksize * 8 * i) { + SB_AP_BITMAP (s)[i] = getblk (s->s_dev, bmp, s->s_blocksize); + if (!buffer_uptodate(SB_AP_BITMAP(s)[i])) + ll_rw_block(READ, 1, SB_AP_BITMAP(s) + i); + } + for (i = 0; i < SB_BMAP_NR(s); i++) { + wait_on_buffer(SB_AP_BITMAP (s)[i]); + if (!buffer_uptodate(SB_AP_BITMAP(s)[i])) { + reiserfs_warning("sh-2029: reiserfs read_bitmaps: " + "bitmap block (#%lu) reading failed\n", + SB_AP_BITMAP(s)[i]->b_blocknr); + for (i = 0; i < SB_BMAP_NR(s); i++) + brelse(SB_AP_BITMAP(s)[i]); + reiserfs_kfree(SB_AP_BITMAP(s), sizeof(struct buffer_head *) * SB_BMAP_NR(s), s); + SB_AP_BITMAP(s) = NULL; return 1; - bmp += dl; + } } - return 0; } @@ -835,7 +852,9 @@ inode = s->s_root->d_inode; - while (1) { + do { // Some serious "goto"-hater was there ;) + u32 teahash, r5hash, yurahash; + make_cpu_key (&key, inode, ~0, TYPE_DIRENTRY, 3); retval = search_by_entry_key (s, &key, &path, &de); if (retval == IO_ERROR) { @@ -854,20 +873,30 @@ "is using the default hash\n"); break; } - if (GET_HASH_VALUE(yura_hash (de.de_name, de.de_namelen)) == - GET_HASH_VALUE(keyed_hash (de.de_name, de.de_namelen))) { - reiserfs_warning ("reiserfs: Could not detect hash function " - "please mount with -o hash={tea,rupasov,r5}\n") ; - hash = UNSET_HASH ; + r5hash=GET_HASH_VALUE (r5_hash (de.de_name, de.de_namelen)); + teahash=GET_HASH_VALUE (keyed_hash (de.de_name, de.de_namelen)); + yurahash=GET_HASH_VALUE (yura_hash (de.de_name, de.de_namelen)); + if ( ( (teahash == r5hash) && (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num]))) == r5hash) ) || + ( (teahash == yurahash) && (yurahash == GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])))) ) || + ( (r5hash == yurahash) && (yurahash == GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])))) ) ) { + reiserfs_warning("reiserfs: Unable to automatically detect hash" + "function for device %s\n" + "please mount with -o hash={tea,rupasov,r5}\n", kdevname (s->s_dev)); + hash = UNSET_HASH; break; } - if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == - GET_HASH_VALUE (yura_hash (de.de_name, de.de_namelen))) + if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == yurahash) hash = YURA_HASH; - else + else if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == teahash) hash = TEA_HASH; - break; - } + else if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == r5hash) + hash = R5_HASH; + else { + reiserfs_warning("reiserfs: Unrecognised hash function for " + "device %s\n", kdevname (s->s_dev)); + hash = UNSET_HASH; + } + } while (0); pathrelse (&path); return hash; @@ -892,16 +921,16 @@ ** mount options */ if (reiserfs_rupasov_hash(s) && code != YURA_HASH) { - printk("REISERFS: Error, tea hash detected, " - "unable to force rupasov hash\n") ; + printk("REISERFS: Error, %s hash detected, " + "unable to force rupasov hash\n", reiserfs_hashname(code)) ; code = UNSET_HASH ; } else if (reiserfs_tea_hash(s) && code != TEA_HASH) { - printk("REISERFS: Error, rupasov hash detected, " - "unable to force tea hash\n") ; + printk("REISERFS: Error, %s hash detected, " + "unable to force tea hash\n", reiserfs_hashname(code)) ; code = UNSET_HASH ; } else if (reiserfs_r5_hash(s) && code != R5_HASH) { - printk("REISERFS: Error, r5 hash detected, " - "unable to force r5 hash\n") ; + printk("REISERFS: Error, %s hash detected, " + "unable to force r5 hash\n", reiserfs_hashname(code)) ; code = UNSET_HASH ; } } else { diff -Nru a/fs/romfs/inode.c b/fs/romfs/inode.c --- a/fs/romfs/inode.c Tue Feb 19 18:08:57 2002 +++ b/fs/romfs/inode.c Tue Feb 19 18:08:57 2002 @@ -318,6 +318,7 @@ res = -EACCES; /* placeholder for "no data here" */ offset = dir->i_ino & ROMFH_MASK; + lock_kernel(); if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) goto out; @@ -378,7 +379,8 @@ outi: res = 0; d_add (dentry, inode); -out: return ERR_PTR(res); +out: unlock_kernel(); + return ERR_PTR(res); } /* diff -Nru a/fs/smbfs/dir.c b/fs/smbfs/dir.c --- a/fs/smbfs/dir.c Tue Feb 19 18:08:59 2002 +++ b/fs/smbfs/dir.c Tue Feb 19 18:08:59 2002 @@ -82,8 +82,7 @@ filp->f_pos = 1; /* fallthrough */ case 1: - if (filldir(dirent, "..", 2, 1, - dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) + if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR) < 0) goto out; filp->f_pos = 2; } @@ -399,6 +398,7 @@ if (dentry->d_name.len > SMB_MAXNAMELEN) goto out; + lock_kernel(); error = smb_proc_getattr(dentry, &finfo); #ifdef SMBFS_PARANOIA if (error && error != -ENOENT) @@ -426,6 +426,7 @@ error = 0; } } + unlock_kernel(); out: return ERR_PTR(error); } @@ -483,6 +484,7 @@ VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode); + lock_kernel(); smb_invalid_dir_cache(dir); error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid); if (!error) { @@ -491,6 +493,7 @@ PARANOIA("%s/%s failed, error=%d\n", DENTRY_PATH(dentry), error); } + unlock_kernel(); return error; } @@ -500,11 +503,13 @@ { int error; + lock_kernel(); smb_invalid_dir_cache(dir); error = smb_proc_mkdir(dentry); if (!error) { error = smb_instantiate(dentry, 0, 0); } + unlock_kernel(); return error; } @@ -517,6 +522,7 @@ /* * Close the directory if it's open. */ + lock_kernel(); smb_close(inode); /* @@ -530,6 +536,7 @@ error = smb_proc_rmdir(dentry); out: + unlock_kernel(); return error; } @@ -541,12 +548,14 @@ /* * Close the file if it's open. */ + lock_kernel(); smb_close(dentry->d_inode); smb_invalid_dir_cache(dir); error = smb_proc_unlink(dentry); if (!error) smb_renew_times(dentry); + unlock_kernel(); return error; } @@ -560,6 +569,7 @@ * Close any open files, and check whether to delete the * target before attempting the rename. */ + lock_kernel(); if (old_dentry->d_inode) smb_close(old_dentry->d_inode); if (new_dentry->d_inode) { @@ -582,5 +592,6 @@ smb_renew_times(new_dentry); } out: + unlock_kernel(); return error; } diff -Nru a/fs/smbfs/proc.c b/fs/smbfs/proc.c --- a/fs/smbfs/proc.c Tue Feb 19 18:08:58 2002 +++ b/fs/smbfs/proc.c Tue Feb 19 18:08:58 2002 @@ -103,6 +103,8 @@ struct nls_table *nls_from, struct nls_table *nls_to) { + if (olen < ilen) + return -ENAMETOOLONG; memcpy(output, input, ilen); return ilen; } @@ -126,20 +128,21 @@ /* convert by changing to unicode and back to the new cp */ n = nls_from->char2uni((unsigned char *)input, ilen, &ch); if (n < 0) - goto out; + goto fail; input += n; ilen -= n; n = nls_to->uni2char(ch, output, olen); if (n < 0) - goto out; + goto fail; output += n; olen -= n; len += n; } -out: return len; +fail: + return n; } static int setcodepage(struct nls_table **p, char *name) @@ -214,71 +217,79 @@ * smb_build_path: build the path to entry and name storing it in buf. * The path returned will have the trailing '\0'. */ -static int smb_build_path(struct smb_sb_info *server, char * buf, +static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen, struct dentry * entry, struct qstr * name) { char *path = buf; int len; + if (maxlen < 2) + return -ENAMETOOLONG; + + if (maxlen > SMB_MAXNAMELEN + 1) + maxlen = SMB_MAXNAMELEN + 1; + if (entry == NULL) goto test_name_and_out; /* * If IS_ROOT, we have to do no walking at all. */ - if (IS_ROOT(entry)) { - *(path++) = '\\'; - if (name != NULL) - goto name_and_out; - goto out; + if (IS_ROOT(entry) && !name) { + *path++ = '\\'; + *path++ = '\0'; + return 2; } /* * Build the path string walking the tree backward from end to ROOT * and store it in reversed order [see reverse_string()] */ - for (;;) { - if (entry->d_name.len > SMB_MAXNAMELEN) - return -ENAMETOOLONG; - if (path - buf + entry->d_name.len > SMB_MAXPATHLEN) + while (!IS_ROOT(entry)) { + if (maxlen < 3) return -ENAMETOOLONG; - len = server->convert(path, SMB_MAXNAMELEN, + len = server->convert(path, maxlen-2, entry->d_name.name, entry->d_name.len, server->local_nls, server->remote_nls); + if (len < 0) + return len; reverse_string(path, len); path += len; - - *(path++) = '\\'; + *path++ = '\\'; + maxlen -= len+1; entry = entry->d_parent; - if (IS_ROOT(entry)) break; } - reverse_string(buf, path-buf); + /* maxlen is at least 1 */ test_name_and_out: - if (name != NULL) { - *(path++) = '\\'; -name_and_out: - len = server->convert(path, SMB_MAXNAMELEN, + if (name) { + if (maxlen < 3) + return -ENAMETOOLONG; + *path++ = '\\'; + len = server->convert(path, maxlen-2, name->name, name->len, server->local_nls, server->remote_nls); + if (len < 0) + return len; path += len; + maxlen -= len+1; } -out: - *(path++) = '\0'; - return (path-buf); + /* maxlen is at least 1 */ + *path++ = '\0'; + return path-buf; } -static int smb_encode_path(struct smb_sb_info *server, char *buf, +static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen, struct dentry *dir, struct qstr *name) { int result; - result = smb_build_path(server, buf, dir, name); + result = smb_build_path(server, buf, maxlen, dir, name); if (result < 0) goto out; if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) @@ -287,6 +298,23 @@ return result; } +static int smb_simple_encode_path(struct smb_sb_info *server, char **p, + struct dentry * entry, struct qstr * name) +{ + char *s = *p; + int res; + int maxlen = ((char *)server->packet + server->packet_size) - s; + + if (!maxlen) + return -ENAMETOOLONG; + *s++ = 4; + res = smb_encode_path(server, s, maxlen-1, entry, name); + if (res < 0) + return res; + *p = s + res; + return 0; +} + /* The following are taken directly from msdos-fs */ /* Linear day numbers of the respective 1sts in non-leap years. */ @@ -966,12 +994,9 @@ p = smb_setup_header(server, SMBopen, 2, 0); WSET(server->packet, smb_vwv0, mode); WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); - *p++ = 4; - res = smb_encode_path(server, p, dentry, NULL); + res = smb_simple_encode_path(server, &p, dentry, NULL); if (res < 0) goto out; - p += res; - smb_setup_bcc(server, p); res = smb_request_ok(server, SMBopen, 7, 0); @@ -1243,11 +1268,9 @@ p = smb_setup_header(server, SMBcreate, 3, 0); WSET(server->packet, smb_vwv0, attr); DSET(server->packet, smb_vwv1, utc2local(server, ctime)); - *p++ = 4; - result = smb_encode_path(server, p, dentry, NULL); + result = smb_simple_encode_path(server, &p, dentry, NULL); if (result < 0) goto out; - p += result; smb_setup_bcc(server, p); result = smb_request_ok(server, SMBcreate, 1, 0); @@ -1276,19 +1299,12 @@ retry: p = smb_setup_header(server, SMBmv, 1, 0); WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN | aDIR); - - *p++ = 4; - result = smb_encode_path(server, p, old_dentry, NULL); + result = smb_simple_encode_path(server, &p, old_dentry, NULL); if (result < 0) goto out; - p += result; - - *p++ = 4; - result = smb_encode_path(server, p, new_dentry, NULL); + result = smb_simple_encode_path(server, &p, new_dentry, NULL); if (result < 0) goto out; - p += result; - smb_setup_bcc(server, p); if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) { @@ -1316,11 +1332,9 @@ retry: p = smb_setup_header(server, command, 0, 0); - *p++ = 4; - result = smb_encode_path(server, p, dentry, NULL); + result = smb_simple_encode_path(server, &p, dentry, NULL); if (result < 0) goto out; - p += result; smb_setup_bcc(server, p); result = smb_request_ok(server, command, 0, 0); @@ -1386,11 +1400,9 @@ retry: p = smb_setup_header(server, SMBunlink, 1, 0); WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN); - *p++ = 4; - result = smb_encode_path(server, p, dentry, NULL); + result = smb_simple_encode_path(server, &p, dentry, NULL); if (result < 0) goto out; - p += result; smb_setup_bcc(server, p); if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) { @@ -1590,8 +1602,7 @@ struct smb_sb_info *server = server_from_dentry(dir); struct qstr qname; struct smb_fattr fattr; - - unsigned char *p; + char *p; int result; int i, first, entries_seen, entries; int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE; @@ -1613,17 +1624,26 @@ p = smb_setup_header(server, SMBsearch, 2, 0); WSET(server->packet, smb_vwv0, entries_asked); WSET(server->packet, smb_vwv1, aDIR); - *p++ = 4; if (first == 1) { - result = smb_encode_path(server, p, dir, &mask); + result = smb_simple_encode_path(server, &p, dir, &mask); if (result < 0) goto unlock_return; - p += result; + if (p + 3 > (char*)server->packet+server->packet_size) { + result = -ENAMETOOLONG; + goto unlock_return; + } *p++ = 5; WSET(p, 0, 0); p += 2; first = 0; } else { + if (p + 5 + SMB_STATUS_SIZE > + (char*)server->packet + server->packet_size) { + result = -ENAMETOOLONG; + goto unlock_return; + } + + *p++ = 4; *p++ = 0; *p++ = 5; WSET(p, 0, SMB_STATUS_SIZE); @@ -1862,7 +1882,7 @@ */ mask = param + 12; - mask_len = smb_encode_path(server, mask, dir, &star); + mask_len = smb_encode_path(server, mask, SMB_MAXNAMELEN+1, dir, &star); if (mask_len < 0) { result = mask_len; goto unlock_return; @@ -2069,7 +2089,7 @@ int mask_len, result; retry: - mask_len = smb_encode_path(server, mask, dentry, NULL); + mask_len = smb_encode_path(server, mask, SMB_MAXNAMELEN+1, dentry, NULL); if (mask_len < 0) { result = mask_len; goto out; @@ -2147,11 +2167,9 @@ retry: p = smb_setup_header(server, SMBgetatr, 0, 0); - *p++ = 4; - result = smb_encode_path(server, p, dir, NULL); + result = smb_simple_encode_path(server, &p, dir, NULL); if (result < 0) goto out; - p += result; smb_setup_bcc(server, p); if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0) @@ -2197,7 +2215,7 @@ retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); - result = smb_encode_path(server, param + 6, dir, NULL); + result = smb_encode_path(server, param+6, SMB_MAXNAMELEN+1, dir, NULL); if (result < 0) goto out; p = param + 6 + result; @@ -2344,11 +2362,13 @@ WSET(server->packet, smb_vwv5, 0); WSET(server->packet, smb_vwv6, 0); WSET(server->packet, smb_vwv7, 0); - *p++ = 4; - result = smb_encode_path(server, p, dentry, NULL); + result = smb_simple_encode_path(server, &p, dentry, NULL); if (result < 0) goto out; - p += result; + if (p + 2 > (char *)server->packet + server->packet_size) { + result = -ENAMETOOLONG; + goto out; + } *p++ = 4; *p++ = 0; smb_setup_bcc(server, p); @@ -2445,7 +2465,7 @@ retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); - result = smb_encode_path(server, param + 6, dir, NULL); + result = smb_encode_path(server, param+6, SMB_MAXNAMELEN+1, dir, NULL); if (result < 0) goto out; p = param + 6 + result; diff -Nru a/fs/smbfs/sock.c b/fs/smbfs/sock.c --- a/fs/smbfs/sock.c Tue Feb 19 18:08:59 2002 +++ b/fs/smbfs/sock.c Tue Feb 19 18:08:59 2002 @@ -638,7 +638,7 @@ sigpipe = sigismember(¤t->pending.signal, SIGPIPE); old_set = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); fs = get_fs(); @@ -655,7 +655,7 @@ if (result == -EPIPE && !sigpipe) sigdelset(¤t->pending.signal, SIGPIPE); current->blocked = old_set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); set_fs(fs); @@ -829,7 +829,7 @@ sigpipe = sigismember(¤t->pending.signal, SIGPIPE); old_set = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); fs = get_fs(); @@ -848,7 +848,7 @@ if (result == -EPIPE && !sigpipe) sigdelset(¤t->pending.signal, SIGPIPE); current->blocked = old_set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); set_fs(fs); diff -Nru a/fs/sysv/ChangeLog b/fs/sysv/ChangeLog --- a/fs/sysv/ChangeLog Tue Feb 19 18:08:57 2002 +++ b/fs/sysv/ChangeLog Tue Feb 19 18:08:57 2002 @@ -1,3 +1,9 @@ +Thu Feb 14 2002 Andrew Morton + + * dir_commit_chunk(): call writeout_one_page() as well as + waitfor_one_page() for IS_SYNC directories, so that we + actually do sync the directory. (forward-port from 2.4). + Thu Feb 7 2002 Alexander Viro * super.c: switched to ->get_sb() @@ -96,4 +102,5 @@ include/linux/sysv_fs_sb.h: Remove symlink faking. Noone really wants to use these as linux filesystems and native OSes don't support it anyway. + diff -Nru a/fs/sysv/dir.c b/fs/sysv/dir.c --- a/fs/sysv/dir.c Tue Feb 19 18:08:58 2002 +++ b/fs/sysv/dir.c Tue Feb 19 18:08:58 2002 @@ -42,8 +42,13 @@ int err = 0; page->mapping->a_ops->commit_write(NULL, page, from, to); - if (IS_SYNC(dir)) - err = waitfor_one_page(page); + if (IS_SYNC(dir)) { + int err2; + err = writeout_one_page(page); + err2 = waitfor_one_page(page); + if (err == 0) + err = err2; + } return err; } diff -Nru a/fs/sysv/inode.c b/fs/sysv/inode.c --- a/fs/sysv/inode.c Tue Feb 19 18:08:57 2002 +++ b/fs/sysv/inode.c Tue Feb 19 18:08:57 2002 @@ -260,9 +260,9 @@ static void sysv_delete_inode(struct inode *inode) { - lock_kernel(); inode->i_size = 0; sysv_truncate(inode); + lock_kernel(); sysv_free_inode(inode); unlock_kernel(); } diff -Nru a/fs/sysv/itree.c b/fs/sysv/itree.c --- a/fs/sysv/itree.c Tue Feb 19 18:08:58 2002 +++ b/fs/sysv/itree.c Tue Feb 19 18:08:58 2002 @@ -371,6 +371,8 @@ if (n == 0) return; + lock_kernel(); + if (n == 1) { free_data(inode, i_data+offsets[0], i_data + DIRECT); goto do_indirects; @@ -409,6 +411,7 @@ sysv_sync_inode (inode); else mark_inode_dirty(inode); + unlock_kernel(); } static int sysv_writepage(struct page *page) diff -Nru a/fs/sysv/namei.c b/fs/sysv/namei.c --- a/fs/sysv/namei.c Tue Feb 19 18:08:57 2002 +++ b/fs/sysv/namei.c Tue Feb 19 18:08:57 2002 @@ -15,6 +15,7 @@ #include #include #include +#include static inline void inc_count(struct inode *inode) { @@ -76,7 +77,7 @@ if (ino) { inode = iget(dir->i_sb, ino); - if (!inode) + if (!inode) return ERR_PTR(-EACCES); } d_add(dentry, inode); @@ -136,9 +137,6 @@ struct dentry * dentry) { struct inode *inode = old_dentry->d_inode; - - if (S_ISDIR(inode->i_mode)) - return -EPERM; if (inode->i_nlink >= inode->i_sb->sv_link_max) return -EMLINK; diff -Nru a/fs/udf/inode.c b/fs/udf/inode.c --- a/fs/udf/inode.c Tue Feb 19 18:09:00 2002 +++ b/fs/udf/inode.c Tue Feb 19 18:09:00 2002 @@ -108,20 +108,19 @@ */ void udf_delete_inode(struct inode * inode) { - lock_kernel(); - if (is_bad_inode(inode)) goto no_delete; inode->i_size = 0; udf_truncate(inode); + lock_kernel(); + udf_update_inode(inode, IS_SYNC(inode)); udf_free_inode(inode); unlock_kernel(); return; no_delete: - unlock_kernel(); clear_inode(inode); } @@ -858,6 +857,7 @@ if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; + lock_kernel(); if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) { if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + @@ -867,6 +867,7 @@ if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) { inode->i_size = UDF_I_LENALLOC(inode); + unlock_kernel(); return; } else @@ -899,6 +900,7 @@ udf_sync_inode (inode); else mark_inode_dirty(inode); + unlock_kernel(); } /* diff -Nru a/fs/udf/namei.c b/fs/udf/namei.c --- a/fs/udf/namei.c Tue Feb 19 18:08:59 2002 +++ b/fs/udf/namei.c Tue Feb 19 18:08:59 2002 @@ -298,14 +298,17 @@ if (dentry->d_name.len > UDF_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); + lock_kernel(); #ifdef UDF_RECOVERY /* temporary shorthand for specifying files by inode number */ if (!strncmp(dentry->d_name.name, ".B=", 3) ) { lb_addr lb = { 0, simple_strtoul(dentry->d_name.name+3, NULL, 0) }; inode = udf_iget(dir->i_sb, lb); - if (!inode) + if (!inode) { + unlock_kernel(); return ERR_PTR(-EACCES); + } } else #endif /* UDF_RECOVERY */ @@ -317,9 +320,12 @@ udf_release_data(fibh.sbh); inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation)); - if ( !inode ) + if ( !inode ) { + unlock_kernel(); return ERR_PTR(-EACCES); + } } + unlock_kernel(); d_add(dentry, inode); return NULL; } @@ -625,9 +631,12 @@ struct FileIdentDesc cfi, *fi; int err; + lock_kernel(); inode = udf_new_inode(dir, mode, &err); - if (!inode) + if (!inode) { + unlock_kernel(); return err; + } if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) inode->i_data.a_ops = &udf_adinicb_aops; @@ -643,6 +652,7 @@ inode->i_nlink --; mark_inode_dirty(inode); iput(inode); + unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); @@ -657,6 +667,7 @@ if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); + unlock_kernel(); d_instantiate(dentry, inode); return 0; } @@ -668,6 +679,7 @@ int err; struct FileIdentDesc cfi, *fi; + lock_kernel(); err = -EIO; inode = udf_new_inode(dir, mode, &err); if (!inode) @@ -680,6 +692,7 @@ inode->i_nlink --; mark_inode_dirty(inode); iput(inode); + unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); @@ -699,6 +712,7 @@ d_instantiate(dentry, inode); err = 0; out: + unlock_kernel(); return err; } @@ -709,6 +723,7 @@ int err; struct FileIdentDesc cfi, *fi; + lock_kernel(); err = -EMLINK; if (dir->i_nlink >= (256<i_nlink))-1) goto out; @@ -761,6 +776,7 @@ udf_release_data(fibh.sbh); err = 0; out: + unlock_kernel(); return err; } @@ -836,6 +852,7 @@ struct FileIdentDesc *fi, cfi; retval = -ENOENT; + lock_kernel(); fi = udf_find_entry(dir, dentry, &fibh, &cfi); if (!fi) goto out; @@ -866,6 +883,7 @@ udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); out: + unlock_kernel(); return retval; } @@ -878,6 +896,7 @@ struct FileIdentDesc cfi; retval = -ENOENT; + lock_kernel(); fi = udf_find_entry(dir, dentry, &fibh, &cfi); if (!fi) goto out; @@ -912,6 +931,7 @@ udf_release_data(fibh.ebh); udf_release_data(fibh.sbh); out: + unlock_kernel(); return retval; } @@ -929,6 +949,7 @@ int err; int block; + lock_kernel(); if (!(inode = udf_new_inode(dir, S_IFLNK, &err))) goto out; @@ -1072,6 +1093,7 @@ err = 0; out: + unlock_kernel(); return err; out_no_entry: @@ -1089,14 +1111,16 @@ int err; struct FileIdentDesc cfi, *fi; - if (S_ISDIR(inode->i_mode)) - return -EPERM; - - if (inode->i_nlink >= (256<i_nlink))-1) + lock_kernel(); + if (inode->i_nlink >= (256<i_nlink))-1) { + unlock_kernel(); return -EMLINK; + } - if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { + unlock_kernel(); return err; + } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); if (UDF_SB_LVIDBH(inode->i_sb)) @@ -1126,6 +1150,7 @@ mark_inode_dirty(inode); atomic_inc(&inode->i_count); d_instantiate(dentry, inode); + unlock_kernel(); return 0; } @@ -1142,6 +1167,7 @@ struct buffer_head *dir_bh = NULL; int retval = -ENOENT; + lock_kernel(); if ((ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi))) { if (ofibh.sbh != ofibh.ebh) @@ -1271,6 +1297,7 @@ udf_release_data(nfibh.ebh); udf_release_data(nfibh.sbh); } + unlock_kernel(); return retval; } diff -Nru a/fs/ufs/namei.c b/fs/ufs/namei.c --- a/fs/ufs/namei.c Tue Feb 19 18:09:00 2002 +++ b/fs/ufs/namei.c Tue Feb 19 18:09:00 2002 @@ -27,6 +27,7 @@ #include #include #include +#include #undef UFS_NAMEI_DEBUG @@ -68,12 +69,16 @@ if (dentry->d_name.len > UFS_MAXNAMLEN) return ERR_PTR(-ENAMETOOLONG); + lock_kernel(); ino = ufs_inode_by_name(dir, dentry); if (ino) { inode = iget(dir->i_sb, ino); - if (!inode) + if (!inode) { + unlock_kernel(); return ERR_PTR(-EACCES); + } } + unlock_kernel(); d_add(dentry, inode); return NULL; } @@ -95,7 +100,9 @@ inode->i_fop = &ufs_file_operations; inode->i_mapping->a_ops = &ufs_aops; mark_inode_dirty(inode); + lock_kernel(); err = ufs_add_nondir(dentry, inode); + unlock_kernel(); } return err; } @@ -107,7 +114,9 @@ if (!IS_ERR(inode)) { init_special_inode(inode, mode, rdev); mark_inode_dirty(inode); + lock_kernel(); err = ufs_add_nondir(dentry, inode); + unlock_kernel(); } return err; } @@ -123,6 +132,7 @@ if (l > sb->s_blocksize) goto out; + lock_kernel(); inode = ufs_new_inode(dir, S_IFLNK | S_IRWXUGO); err = PTR_ERR(inode); if (IS_ERR(inode)) @@ -145,6 +155,7 @@ err = ufs_add_nondir(dentry, inode); out: + unlock_kernel(); return err; out_fail: @@ -157,18 +168,21 @@ struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; + int error; - if (S_ISDIR(inode->i_mode)) - return -EPERM; - - if (inode->i_nlink >= UFS_LINK_MAX) + lock_kernel(); + if (inode->i_nlink >= UFS_LINK_MAX) { + unlock_kernel(); return -EMLINK; + } inode->i_ctime = CURRENT_TIME; ufs_inc_count(inode); atomic_inc(&inode->i_count); - return ufs_add_nondir(dentry, inode); + error = ufs_add_nondir(dentry, inode); + unlock_kernel(); + return error; } static int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) @@ -179,6 +193,7 @@ if (dir->i_nlink >= UFS_LINK_MAX) goto out; + lock_kernel(); ufs_inc_count(dir); inode = ufs_new_inode(dir, S_IFDIR|mode); @@ -198,6 +213,7 @@ err = ufs_add_link(dentry, inode); if (err) goto out_fail; + unlock_kernel(); d_instantiate(dentry, inode); out: @@ -209,6 +225,7 @@ iput (inode); out_dir: ufs_dec_count(dir); + unlock_kernel(); goto out; } @@ -219,6 +236,7 @@ struct ufs_dir_entry * de; int err = -ENOENT; + lock_kernel(); de = ufs_find_entry (dentry, &bh); if (!de) goto out; @@ -231,6 +249,7 @@ ufs_dec_count(inode); err = 0; out: + unlock_kernel(); return err; } @@ -239,6 +258,7 @@ struct inode * inode = dentry->d_inode; int err= -ENOTEMPTY; + lock_kernel(); if (ufs_empty_dir (inode)) { err = ufs_unlink(dir, dentry); if (!err) { @@ -247,6 +267,7 @@ ufs_dec_count(dir); } } + unlock_kernel(); return err; } @@ -261,6 +282,7 @@ struct ufs_dir_entry *old_de; int err = -ENOENT; + lock_kernel(); old_de = ufs_find_entry (old_dentry, &old_bh); if (!old_de) goto out; @@ -313,6 +335,7 @@ ufs_set_link(old_inode, dir_de, dir_bh, new_dir); ufs_dec_count(old_dir); } + unlock_kernel(); return 0; out_dir: @@ -321,6 +344,7 @@ out_old: brelse (old_bh); out: + unlock_kernel(); return err; } diff -Nru a/fs/ufs/truncate.c b/fs/ufs/truncate.c --- a/fs/ufs/truncate.c Tue Feb 19 18:09:00 2002 +++ b/fs/ufs/truncate.c Tue Feb 19 18:09:00 2002 @@ -37,6 +37,7 @@ #include #include #include +#include #include "swab.h" #include "util.h" @@ -439,6 +440,7 @@ return; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; + lock_kernel(); while (1) { retry = ufs_trunc_direct(inode); retry |= ufs_trunc_indirect (inode, UFS_IND_BLOCK, @@ -464,6 +466,7 @@ } inode->i_mtime = inode->i_ctime = CURRENT_TIME; ufsi->i_lastfrag = DIRECT_FRAGMENT; + unlock_kernel(); mark_inode_dirty(inode); UFSD(("EXIT\n")) } diff -Nru a/fs/vfat/namei.c b/fs/vfat/namei.c --- a/fs/vfat/namei.c Tue Feb 19 18:08:57 2002 +++ b/fs/vfat/namei.c Tue Feb 19 18:08:57 2002 @@ -21,6 +21,7 @@ #include #include #include +#include #define DEBUG_LEVEL 0 #if (DEBUG_LEVEL >= 1) @@ -986,6 +987,7 @@ PRINTK2(("vfat_lookup: name=%s, len=%d\n", dentry->d_name.name, dentry->d_name.len)); + lock_kernel(); table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0; dentry->d_op = &vfat_dentry_ops[table]; @@ -997,19 +999,23 @@ } inode = fat_build_inode(dir->i_sb, de, sinfo.ino, &res); fat_brelse(dir->i_sb, bh); - if (res) + if (res) { + unlock_kernel(); return ERR_PTR(res); + } alias = d_find_alias(inode); if (alias) { if (d_invalidate(alias)==0) dput(alias); else { iput(inode); + unlock_kernel(); return alias; } } error: + unlock_kernel(); dentry->d_op = &vfat_dentry_ops[table]; dentry->d_time = dentry->d_parent->d_inode->i_version; d_add(dentry,inode); @@ -1025,19 +1031,25 @@ struct vfat_slot_info sinfo; int res; + lock_kernel(); res = vfat_add_entry(dir, &dentry->d_name, 0, &sinfo, &bh, &de); - if (res < 0) + if (res < 0) { + unlock_kernel(); return res; + } inode = fat_build_inode(sb, de, sinfo.ino, &res); fat_brelse(sb, bh); - if (!inode) + if (!inode) { + unlock_kernel(); return res; + } inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); inode->i_version++; dir->i_version++; dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry,inode); + unlock_kernel(); return 0; } @@ -1074,13 +1086,18 @@ struct buffer_head *bh = NULL; struct msdos_dir_entry *de; + lock_kernel(); res = fat_dir_empty(dentry->d_inode); - if (res) + if (res) { + unlock_kernel(); return res; + } res = vfat_find(dir,&dentry->d_name,&sinfo, &bh, &de); - if (res<0) + if (res<0) { + unlock_kernel(); return res; + } dentry->d_inode->i_nlink = 0; dentry->d_inode->i_mtime = CURRENT_TIME; dentry->d_inode->i_atime = CURRENT_TIME; @@ -1089,6 +1106,7 @@ /* releases bh */ vfat_remove_entry(dir,&sinfo,bh,de); dir->i_nlink--; + unlock_kernel(); return 0; } @@ -1100,9 +1118,12 @@ struct msdos_dir_entry *de; PRINTK1(("vfat_unlink: %s\n", dentry->d_name.name)); + lock_kernel(); res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de); - if (res < 0) + if (res < 0) { + unlock_kernel(); return res; + } dentry->d_inode->i_nlink = 0; dentry->d_inode->i_mtime = CURRENT_TIME; dentry->d_inode->i_atime = CURRENT_TIME; @@ -1110,6 +1131,7 @@ mark_inode_dirty(dentry->d_inode); /* releases bh */ vfat_remove_entry(dir,&sinfo,bh,de); + unlock_kernel(); return res; } @@ -1124,9 +1146,12 @@ struct msdos_dir_entry *de; int res; + lock_kernel(); res = vfat_add_entry(dir, &dentry->d_name, 1, &sinfo, &bh, &de); - if (res < 0) + if (res < 0) { + unlock_kernel(); return res; + } inode = fat_build_inode(sb, de, sinfo.ino, &res); if (!inode) goto out; @@ -1143,6 +1168,7 @@ d_instantiate(dentry,inode); out: fat_brelse(sb, bh); + unlock_kernel(); return res; mkdir_failed: @@ -1155,6 +1181,7 @@ vfat_remove_entry(dir,&sinfo,bh,de); iput(inode); dir->i_nlink--; + unlock_kernel(); return res; } @@ -1172,6 +1199,7 @@ old_bh = new_bh = dotdot_bh = NULL; old_inode = old_dentry->d_inode; new_inode = new_dentry->d_inode; + lock_kernel(); res = vfat_find(old_dir,&old_dentry->d_name,&old_sinfo,&old_bh,&old_de); PRINTK3(("vfat_rename 2\n")); if (res < 0) goto rename_done; @@ -1238,6 +1266,7 @@ fat_brelse(sb, dotdot_bh); fat_brelse(sb, old_bh); fat_brelse(sb, new_bh); + unlock_kernel(); return res; } diff -Nru a/fs/xattr.c b/fs/xattr.c --- a/fs/xattr.c Tue Feb 19 18:08:59 2002 +++ b/fs/xattr.c Tue Feb 19 18:08:59 2002 @@ -67,9 +67,11 @@ if (flags & ~(XATTR_CREATE|XATTR_REPLACE)) return -EINVAL; - if (copy_from_user(kname, name, XATTR_NAME_MAX)) - return -EFAULT; - kname[XATTR_NAME_MAX] = '\0'; + error = strncpy_from_user(kname, name, sizeof(kname)); + if (error == 0 || error == sizeof(kname)) + error = -ERANGE; + if (error < 0) + return error; kvalue = xattr_alloc(size, XATTR_SIZE_MAX); if (IS_ERR(kvalue)) @@ -136,16 +138,18 @@ /* * Extended attribute GET operations */ -static long +static ssize_t getxattr(struct dentry *d, char *name, void *value, size_t size) { - int error; + ssize_t error; void *kvalue; char kname[XATTR_NAME_MAX + 1]; - if (copy_from_user(kname, name, XATTR_NAME_MAX)) - return -EFAULT; - kname[XATTR_NAME_MAX] = '\0'; + error = strncpy_from_user(kname, name, sizeof(kname)); + if (error == 0 || error == sizeof(kname)) + error = -ERANGE; + if (error < 0) + return error; kvalue = xattr_alloc(size, XATTR_SIZE_MAX); if (IS_ERR(kvalue)) @@ -159,17 +163,17 @@ } if (kvalue && error > 0) - if (copy_to_user(value, kvalue, size)) + if (copy_to_user(value, kvalue, error)) error = -EFAULT; xattr_free(kvalue, size); return error; } -asmlinkage long +asmlinkage ssize_t sys_getxattr(char *path, char *name, void *value, size_t size) { struct nameidata nd; - int error; + ssize_t error; error = user_path_walk(path, &nd); if (error) @@ -179,11 +183,11 @@ return error; } -asmlinkage long +asmlinkage ssize_t sys_lgetxattr(char *path, char *name, void *value, size_t size) { struct nameidata nd; - int error; + ssize_t error; error = user_path_walk_link(path, &nd); if (error) @@ -193,11 +197,11 @@ return error; } -asmlinkage long +asmlinkage ssize_t sys_fgetxattr(int fd, char *name, void *value, size_t size) { struct file *f; - int error = -EBADF; + ssize_t error = -EBADF; f = fget(fd); if (!f) @@ -210,10 +214,10 @@ /* * Extended attribute LIST operations */ -static long +static ssize_t listxattr(struct dentry *d, char *list, size_t size) { - int error; + ssize_t error; char *klist; klist = (char *)xattr_alloc(size, XATTR_LIST_MAX); @@ -228,17 +232,17 @@ } if (klist && error > 0) - if (copy_to_user(list, klist, size)) + if (copy_to_user(list, klist, error)) error = -EFAULT; xattr_free(klist, size); return error; } -asmlinkage long +asmlinkage ssize_t sys_listxattr(char *path, char *list, size_t size) { struct nameidata nd; - int error; + ssize_t error; error = user_path_walk(path, &nd); if (error) @@ -248,11 +252,11 @@ return error; } -asmlinkage long +asmlinkage ssize_t sys_llistxattr(char *path, char *list, size_t size) { struct nameidata nd; - int error; + ssize_t error; error = user_path_walk_link(path, &nd); if (error) @@ -262,11 +266,11 @@ return error; } -asmlinkage long +asmlinkage ssize_t sys_flistxattr(int fd, char *list, size_t size) { struct file *f; - int error = -EBADF; + ssize_t error = -EBADF; f = fget(fd); if (!f) @@ -285,9 +289,11 @@ int error; char kname[XATTR_NAME_MAX + 1]; - if (copy_from_user(kname, name, XATTR_NAME_MAX)) - return -EFAULT; - kname[XATTR_NAME_MAX] = '\0'; + error = strncpy_from_user(kname, name, sizeof(kname)); + if (error == 0 || error == sizeof(kname)) + error = -ERANGE; + if (error < 0) + return error; error = -EOPNOTSUPP; if (d->d_inode->i_op && d->d_inode->i_op->removexattr) { diff -Nru a/include/asm-alpha/asm_offsets.h b/include/asm-alpha/asm_offsets.h --- a/include/asm-alpha/asm_offsets.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -#ifndef __ASM_OFFSETS_H__ -#define __ASM_OFFSETS_H__ -#define TASK_STATE 0 -#define TASK_FLAGS 8 -#define TASK_SIGPENDING 16 -#define TASK_ADDR_LIMIT 24 -#define TASK_EXEC_DOMAIN 32 -#define TASK_NEED_RESCHED 40 -#define TASK_SIZE 1096 -#define STACK_SIZE 16384 -#define HAE_CACHE 0 -#define HAE_REG 8 -#endif /* __ASM_OFFSETS_H__ */ diff -Nru a/include/asm-alpha/bitops.h b/include/asm-alpha/bitops.h --- a/include/asm-alpha/bitops.h Tue Feb 19 18:09:00 2002 +++ b/include/asm-alpha/bitops.h Tue Feb 19 18:09:00 2002 @@ -3,6 +3,7 @@ #include #include +#include /* * Copyright 1994, Linus Torvalds. @@ -60,25 +61,25 @@ __asm__ __volatile__( "1: ldl_l %0,%3\n" - " and %0,%2,%0\n" + " bic %0,%2,%0\n" " stl_c %0,%1\n" " beq %0,2f\n" ".subsection 2\n" "2: br 1b\n" ".previous" :"=&r" (temp), "=m" (*m) - :"Ir" (~(1UL << (nr & 31))), "m" (*m)); + :"Ir" (1UL << (nr & 31)), "m" (*m)); } /* * WARNING: non atomic version. */ static __inline__ void -__change_bit(unsigned long nr, volatile void * addr) +__clear_bit(unsigned long nr, volatile void * addr) { int *m = ((int *) addr) + (nr >> 5); - *m ^= 1 << (nr & 31); + *m &= ~(1 << (nr & 31)); } static inline void @@ -99,6 +100,17 @@ :"Ir" (1UL << (nr & 31)), "m" (*m)); } +/* + * WARNING: non atomic version. + */ +static __inline__ void +__change_bit(unsigned long nr, volatile void * addr) +{ + int *m = ((int *) addr) + (nr >> 5); + + *m ^= 1 << (nr & 31); +} + static inline int test_and_set_bit(unsigned long nr, volatile void *addr) { @@ -181,20 +193,6 @@ return (old & mask) != 0; } -/* - * WARNING: non atomic version. - */ -static __inline__ int -__test_and_change_bit(unsigned long nr, volatile void * addr) -{ - unsigned long mask = 1 << (nr & 0x1f); - int *m = ((int *) addr) + (nr >> 5); - int old = *m; - - *m = old ^ mask; - return (old & mask) != 0; -} - static inline int test_and_change_bit(unsigned long nr, volatile void * addr) { @@ -220,6 +218,20 @@ return oldbit != 0; } +/* + * WARNING: non atomic version. + */ +static __inline__ int +__test_and_change_bit(unsigned long nr, volatile void * addr) +{ + unsigned long mask = 1 << (nr & 0x1f); + int *m = ((int *) addr) + (nr >> 5); + int old = *m; + + *m = old ^ mask; + return (old & mask) != 0; +} + static inline int test_bit(int nr, volatile void * addr) { @@ -235,12 +247,15 @@ */ static inline unsigned long ffz_b(unsigned long x) { - unsigned long sum = 0; + unsigned long sum, x1, x2, x4; x = ~x & -~x; /* set first 0 bit, clear others */ - if (x & 0xF0) sum += 4; - if (x & 0xCC) sum += 2; - if (x & 0xAA) sum += 1; + x1 = x & 0xAA; + x2 = x & 0xCC; + x4 = x & 0xF0; + sum = x2 ? 2 : 0; + sum += (x4 != 0) * 4; + sum += (x1 != 0); return sum; } @@ -257,24 +272,46 @@ __asm__("cmpbge %1,%2,%0" : "=r"(bits) : "r"(word), "r"(~0UL)); qofs = ffz_b(bits); - __asm__("extbl %1,%2,%0" : "=r"(bits) : "r"(word), "r"(qofs)); + bits = __kernel_extbl(word, qofs); bofs = ffz_b(bits); return qofs*8 + bofs; #endif } +/* + * __ffs = Find First set bit in word. Undefined if no set bit exists. + */ +static inline unsigned long __ffs(unsigned long word) +{ +#if defined(__alpha_cix__) && defined(__alpha_fix__) + /* Whee. EV67 can calculate it directly. */ + unsigned long result; + __asm__("cttz %1,%0" : "=r"(result) : "r"(word)); + return result; +#else + unsigned long bits, qofs, bofs; + + __asm__("cmpbge $31,%1,%0" : "=r"(bits) : "r"(word)); + qofs = ffz_b(bits); + bits = __kernel_extbl(word, qofs); + bofs = ffz_b(~bits); + + return qofs*8 + bofs; +#endif +} + #ifdef __KERNEL__ /* * ffs: find first bit set. This is defined the same way as * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). + * differs in spirit from the above __ffs. */ static inline int ffs(int word) { - int result = ffz(~word); + int result = __ffs(word); return word ? result+1 : 0; } @@ -316,6 +353,14 @@ #define hweight16(x) hweight64((x) & 0xfffful) #define hweight8(x) hweight64((x) & 0xfful) #else +static inline unsigned long hweight64(unsigned long w) +{ + unsigned long result; + for (result = 0; w ; w >>= 1) + result += (w & 1); + return result; +} + #define hweight32(x) generic_hweight32(x) #define hweight16(x) generic_hweight16(x) #define hweight8(x) generic_hweight8(x) @@ -365,10 +410,53 @@ } /* - * The optimizer actually does good code for this case.. + * Find next one bit in a bitmap reasonably efficiently. + */ +static inline unsigned long +find_next_bit(void * addr, unsigned long size, unsigned long offset) +{ + unsigned long * p = ((unsigned long *) addr) + (offset >> 6); + unsigned long result = offset & ~63UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 63UL; + if (offset) { + tmp = *(p++); + tmp &= ~0UL << offset; + if (size < 64) + goto found_first; + if (tmp) + goto found_middle; + size -= 64; + result += 64; + } + while (size & ~63UL) { + if ((tmp = *(p++))) + goto found_middle; + result += 64; + size -= 64; + } + if (!size) + return result; + tmp = *p; +found_first: + tmp &= ~0UL >> (64 - size); + if (!tmp) + return result + size; +found_middle: + return result + __ffs(tmp); +} + +/* + * The optimizer actually does good code for this case. */ #define find_first_zero_bit(addr, size) \ find_next_zero_bit((addr), (size), 0) +#define find_first_bit(addr, size) \ + find_next_bit((addr), (size), 0) #ifdef __KERNEL__ diff -Nru a/include/asm-alpha/current.h b/include/asm-alpha/current.h --- a/include/asm-alpha/current.h Tue Feb 19 18:09:00 2002 +++ b/include/asm-alpha/current.h Tue Feb 19 18:09:00 2002 @@ -1,6 +1,9 @@ #ifndef _ALPHA_CURRENT_H #define _ALPHA_CURRENT_H -register struct task_struct *current __asm__("$8"); +#include -#endif /* !(_ALPHA_CURRENT_H) */ +#define get_current() (current_thread_info()->task + 0) +#define current get_current() + +#endif /* _ALPHA_CURRENT_H */ diff -Nru a/include/asm-alpha/fpu.h b/include/asm-alpha/fpu.h --- a/include/asm-alpha/fpu.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-alpha/fpu.h Tue Feb 19 18:08:57 2002 @@ -31,7 +31,7 @@ /* * IEEE trap enables are implemented in software. These per-thread - * bits are stored in the "flags" field of "struct thread_struct". + * bits are stored in the "ieee_state" field of "struct thread_info". * Thus, the bits are defined so as not to conflict with the * floating-point enable bit (which is architected). On top of that, * we want to make these bits compatible with OSF/1 so diff -Nru a/include/asm-alpha/io.h b/include/asm-alpha/io.h --- a/include/asm-alpha/io.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-alpha/io.h Tue Feb 19 18:08:59 2002 @@ -18,6 +18,7 @@ #include #include #include +#include #include /* @@ -60,7 +61,10 @@ return (void *) (address + IDENT_ADDR); } -#define page_to_phys(page) (((page) - (page)->zone->zone_mem_map) << PAGE_SHIFT) +#define page_to_phys(page) PAGE_TO_PA(page) + +/* This depends on working iommu. */ +#define BIO_VMERGE_BOUNDARY (alpha_mv.mv_pci_tbi ? PAGE_SIZE : 0) /* * Change addresses as seen by the kernel (virtual) to addresses as diff -Nru a/include/asm-alpha/mmu_context.h b/include/asm-alpha/mmu_context.h --- a/include/asm-alpha/mmu_context.h Tue Feb 19 18:09:00 2002 +++ b/include/asm-alpha/mmu_context.h Tue Feb 19 18:09:00 2002 @@ -21,8 +21,34 @@ #include #endif +/* ??? This does not belong here. */ +/* + * Every architecture must define this function. It's the fastest + * way of searching a 168-bit bitmap where the first 128 bits are + * unlikely to be set. It's guaranteed that at least one of the 168 + * bits is set. + */ +#if MAX_RT_PRIO != 128 || MAX_PRIO > 192 +# error update this function. +#endif + +static inline int +sched_find_first_bit(unsigned long *b) +{ + unsigned long b0 = b[0], b1 = b[1], b2 = b[2]; + unsigned long offset = 128; + + if (unlikely(b0 | b1)) { + b2 = (b0 ? b0 : b1); + offset = (b0 ? 0 : 64); + } + + return __ffs(b2) + offset; +} + + extern inline unsigned long -__reload_thread(struct thread_struct *pcb) +__reload_thread(struct pcb_struct *pcb) { register unsigned long a0 __asm__("$16"); register unsigned long v0 __asm__("$0"); @@ -153,7 +179,7 @@ /* Always update the PCB ASN. Another thread may have allocated a new mm->context (via flush_tlb_mm) without the ASN serial number wrapping. We have no way to detect when this is needed. */ - next->thread.asn = mmc & HARDWARE_ASN_MASK; + next->thread_info->pcb.asn = mmc & HARDWARE_ASN_MASK; } __EXTERN_INLINE void @@ -228,7 +254,8 @@ for (i = 0; i < smp_num_cpus; i++) mm->context[cpu_logical_map(i)] = 0; - tsk->thread.ptbr = ((unsigned long)mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; + tsk->thread_info->pcb.ptbr + = ((unsigned long)mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; return 0; } @@ -241,7 +268,8 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk, unsigned cpu) { - tsk->thread.ptbr = ((unsigned long)mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; + tsk->thread_info->pcb.ptbr + = ((unsigned long)mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; } #ifdef __MMU_EXTERN_INLINE diff -Nru a/include/asm-alpha/page.h b/include/asm-alpha/page.h --- a/include/asm-alpha/page.h Tue Feb 19 18:09:00 2002 +++ b/include/asm-alpha/page.h Tue Feb 19 18:09:00 2002 @@ -59,11 +59,11 @@ #endif /* STRICT_MM_TYPECHECKS */ -#define BUG() \ -do { \ - printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ - __asm__ __volatile__("call_pal %0 # bugchk" : : "i" (PAL_bugchk)); \ -} while (0) +/* ??? Would be nice to use .gprel32 here, but we can't be sure that the + function loaded the GP, so this could fail in modules. */ +#define BUG() \ + __asm__ __volatile__("call_pal %0 # bugchk\n\t"".long %1\n\t.8byte %2" \ + : : "i" (PAL_bugchk), "i"(__LINE__), "i"(__FILE__)) #define PAGE_BUG(page) BUG() @@ -98,6 +98,9 @@ #define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT)) #define VALID_PAGE(page) (((page) - mem_map) < max_mapnr) #endif /* CONFIG_DISCONTIGMEM */ + +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) #endif /* __KERNEL__ */ diff -Nru a/include/asm-alpha/pgalloc.h b/include/asm-alpha/pgalloc.h --- a/include/asm-alpha/pgalloc.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-alpha/pgalloc.h Tue Feb 19 18:08:57 2002 @@ -70,8 +70,7 @@ } /* We need to flush the userspace icache after setting breakpoints in - ptrace. I don't think it's needed in do_swap_page, or do_no_page, - but I don't know how to get rid of it either. + ptrace. Instead of indiscriminately using imb, take advantage of the fact that icache entries are tagged with the ASN and load a new mm context. */ @@ -79,7 +78,8 @@ #ifndef CONFIG_SMP static inline void -flush_icache_page(struct vm_area_struct *vma, struct page *page) +flush_icache_user_range(struct vm_area_struct *vma, struct page *page, + unsigned long addr, int len) { if (vma->vm_flags & VM_EXEC) { struct mm_struct *mm = vma->vm_mm; @@ -90,8 +90,12 @@ } } #else -extern void flush_icache_page(struct vm_area_struct *vma, struct page *page); +extern void flush_icache_user_range(struct vm_area_struct *vma, + struct page *page, unsigned long addr, int len); #endif + +/* this is used only in do_no_page and do_swap_page */ +#define flush_icache_page(vma, page) flush_icache_user_range((vma), (page), 0, 0) /* * Flush just one page in the current TLB set. diff -Nru a/include/asm-alpha/pgtable.h b/include/asm-alpha/pgtable.h --- a/include/asm-alpha/pgtable.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-alpha/pgtable.h Tue Feb 19 18:08:58 2002 @@ -268,8 +268,6 @@ extern inline int pgd_present(pgd_t pgd) { return pgd_val(pgd) & _PAGE_VALID; } extern inline void pgd_clear(pgd_t * pgdp) { pgd_val(*pgdp) = 0; } -#define page_address(page) ((page)->virtual) - /* * The following only work if pte_present() is true. * Undefined behaviour if not.. diff -Nru a/include/asm-alpha/processor.h b/include/asm-alpha/processor.h --- a/include/asm-alpha/processor.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-alpha/processor.h Tue Feb 19 18:08:59 2002 @@ -38,83 +38,17 @@ unsigned long seg; } mm_segment_t; -struct thread_struct { - /* the fields below are used by PALcode and must match struct pcb: */ - unsigned long ksp; - unsigned long usp; - unsigned long ptbr; - unsigned int pcc; - unsigned int asn; - unsigned long unique; - /* - * bit 0: floating point enable - * bit 62: performance monitor enable - */ - unsigned long pal_flags; - unsigned long res1, res2; - - /* - * The fields below are Linux-specific: - * - * bit 1..5: IEEE_TRAP_ENABLE bits (see fpu.h) - * bit 6..8: UAC bits (see sysinfo.h) - * bit 17..21: IEEE_STATUS_MASK bits (see fpu.h) - * bit 63: die_if_kernel recursion lock - */ - unsigned long flags; - - /* Perform syscall argument validation (get/set_fs). */ - mm_segment_t fs; - - /* Breakpoint handling for ptrace. */ - unsigned long bpt_addr[2]; - unsigned int bpt_insn[2]; - int bpt_nsaved; -}; - -#define INIT_THREAD { \ - 0, 0, 0, \ - 0, 0, 0, \ - 0, 0, 0, \ - 0, \ - KERNEL_DS \ -} - -#define THREAD_SIZE (2*PAGE_SIZE) - -#include - -/* - * Return saved PC of a blocked thread. This assumes the frame - * pointer is the 6th saved long on the kernel stack and that the - * saved return address is the first long in the frame. This all - * holds provided the thread blocked through a call to schedule() ($15 - * is the frame pointer in schedule() and $15 is saved at offset 48 by - * entry.S:do_switch_stack). - * - * Under heavy swap load I've seen this lose in an ugly way. So do - * some extra sanity checking on the ranges we expect these pointers - * to be in so that we can fail gracefully. This is just for ps after - * all. -- r~ - */ -extern inline unsigned long thread_saved_pc(struct thread_struct *t) -{ - unsigned long fp, sp = t->ksp, base = (unsigned long)t; - - if (sp > base && sp+6*8 < base + 16*1024) { - fp = ((unsigned long*)sp)[6]; - if (fp > sp && fp < base + 16*1024) - return *(unsigned long *)fp; - } +/* This is dead. Everything has been moved to thread_info. */ +struct thread_struct { }; +#define INIT_THREAD { } - return 0; -} +/* Return saved PC of a blocked thread. */ +struct task_struct; +extern unsigned long thread_saved_pc(struct task_struct *); /* Do necessary setup to start up a newly executed thread. */ extern void start_thread(struct pt_regs *, unsigned long, unsigned long); -struct task_struct; - /* Free all resources held by a thread. */ extern void release_thread(struct task_struct *); @@ -127,26 +61,18 @@ unsigned long get_wchan(struct task_struct *p); /* See arch/alpha/kernel/ptrace.c for details. */ -#define PT_REG(reg) (PAGE_SIZE*2 - sizeof(struct pt_regs) \ - + (long)&((struct pt_regs *)0)->reg) +#define PT_REG(reg) \ + (PAGE_SIZE*2 - sizeof(struct pt_regs) + offsetof(struct pt_regs, reg)) -#define SW_REG(reg) (PAGE_SIZE*2 - sizeof(struct pt_regs) \ - - sizeof(struct switch_stack) \ - + (long)&((struct switch_stack *)0)->reg) +#define SW_REG(reg) \ + (PAGE_SIZE*2 - sizeof(struct pt_regs) - sizeof(struct switch_stack) \ + + offsetof(struct switch_stack, reg)) #define KSTK_EIP(tsk) \ - (*(unsigned long *)(PT_REG(pc) + (unsigned long)(tsk))) + (*(unsigned long *)(PT_REG(pc) + (unsigned long) ((tsk)->thread_info))) -#define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp) - -/* NOTE: The task struct and the stack go together! */ -#define alloc_task_struct() \ - ((struct task_struct *) __get_free_pages(GFP_KERNEL,1)) -#define free_task_struct(p) free_pages((unsigned long)(p),1) -#define get_task_struct(tsk) atomic_inc(&virt_to_page(tsk)->count) - -#define init_task (init_task_union.task) -#define init_stack (init_task_union.stack) +#define KSTK_ESP(tsk) \ + ((tsk) == current ? rdusp() : (tsk)->thread_info->pcb.usp) #define cpu_relax() do { } while (0) @@ -154,21 +80,36 @@ #define ARCH_HAS_PREFETCHW #define ARCH_HAS_SPINLOCK_PREFETCH +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) extern inline void prefetch(const void *ptr) { - __asm__ ("ldl $31,%0" : : "m"(*(char *)ptr)); + __builtin_prefetch(ptr, 0, 3); } extern inline void prefetchw(const void *ptr) { - __asm__ ("ldl $31,%0" : : "m"(*(char *)ptr)); + __builtin_prefetch(ptr, 1, 3); } extern inline void spin_lock_prefetch(const void *ptr) { + __builtin_prefetch(ptr, 1, 3); +} +#else +extern inline void prefetch(const void *ptr) +{ __asm__ ("ldl $31,%0" : : "m"(*(char *)ptr)); } - +extern inline void prefetchw(const void *ptr) +{ + __asm__ ("ldq $31,%0" : : "m"(*(char *)ptr)); +} + +extern inline void spin_lock_prefetch(const void *ptr) +{ + __asm__ ("ldq $31,%0" : : "m"(*(char *)ptr)); +} +#endif /* GCC 3.1 */ #endif /* __ASM_ALPHA_PROCESSOR_H */ diff -Nru a/include/asm-alpha/smp.h b/include/asm-alpha/smp.h --- a/include/asm-alpha/smp.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-alpha/smp.h Tue Feb 19 18:08:59 2002 @@ -2,6 +2,7 @@ #define __ASM_SMP_H #include +#include #include /* HACK: Cabrio WHAMI return value is bogus if more than 8 bits used.. :-( */ @@ -55,7 +56,7 @@ #define cpu_logical_map(cpu) __cpu_logical_map[cpu] #define hard_smp_processor_id() __hard_smp_processor_id() -#define smp_processor_id() (current->processor) +#define smp_processor_id() (current_thread_info()->cpu) extern unsigned long cpu_present_mask; #define cpu_online_map cpu_present_mask diff -Nru a/include/asm-alpha/spinlock.h b/include/asm-alpha/spinlock.h --- a/include/asm-alpha/spinlock.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-alpha/spinlock.h Tue Feb 19 18:08:57 2002 @@ -38,12 +38,12 @@ #define spin_unlock_wait(x) ({ do { barrier(); } while ((x)->lock); }) #if CONFIG_DEBUG_SPINLOCK -extern void spin_unlock(spinlock_t * lock); +extern void _raw_spin_unlock(spinlock_t * lock); extern void debug_spin_lock(spinlock_t * lock, const char *, int); extern int debug_spin_trylock(spinlock_t * lock, const char *, int); -#define spin_lock(LOCK) debug_spin_lock(LOCK, __BASE_FILE__, __LINE__) -#define spin_trylock(LOCK) debug_spin_trylock(LOCK, __BASE_FILE__, __LINE__) +#define _raw_spin_lock(LOCK) debug_spin_lock(LOCK, __BASE_FILE__, __LINE__) +#define _raw_spin_trylock(LOCK) debug_spin_trylock(LOCK, __BASE_FILE__, __LINE__) #define spin_lock_own(LOCK, LOCATION) \ do { \ @@ -54,13 +54,13 @@ (LOCK)->lock ? "taken" : "freed", (LOCK)->on_cpu); \ } while (0) #else -static inline void spin_unlock(spinlock_t * lock) +static inline void _raw_spin_unlock(spinlock_t * lock) { mb(); lock->lock = 0; } -static inline void spin_lock(spinlock_t * lock) +static inline void _raw_spin_lock(spinlock_t * lock) { long tmp; @@ -83,7 +83,11 @@ : "m"(lock->lock) : "memory"); } -#define spin_trylock(lock) (!test_and_set_bit(0,(lock))) +static inline int _raw_spin_trylock(spinlock_t *lock) +{ + return !test_and_set_bit(0, &lock->lock); +} + #define spin_lock_own(LOCK, LOCATION) ((void)0) #endif /* CONFIG_DEBUG_SPINLOCK */ @@ -98,10 +102,10 @@ #define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) #if CONFIG_DEBUG_RWLOCK -extern void write_lock(rwlock_t * lock); -extern void read_lock(rwlock_t * lock); +extern void _raw_write_lock(rwlock_t * lock); +extern void _raw_read_lock(rwlock_t * lock); #else -static inline void write_lock(rwlock_t * lock) +static inline void _raw_write_lock(rwlock_t * lock) { long regx; @@ -121,7 +125,7 @@ : "0" (*(volatile int *)lock) : "memory"); } -static inline void read_lock(rwlock_t * lock) +static inline void _raw_read_lock(rwlock_t * lock) { long regx; @@ -142,13 +146,13 @@ } #endif /* CONFIG_DEBUG_RWLOCK */ -static inline void write_unlock(rwlock_t * lock) +static inline void _raw_write_unlock(rwlock_t * lock) { mb(); *(volatile int *)lock = 0; } -static inline void read_unlock(rwlock_t * lock) +static inline void _raw_read_unlock(rwlock_t * lock) { long regx; __asm__ __volatile__( diff -Nru a/include/asm-alpha/sysinfo.h b/include/asm-alpha/sysinfo.h --- a/include/asm-alpha/sysinfo.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-alpha/sysinfo.h Tue Feb 19 18:08:57 2002 @@ -29,8 +29,8 @@ #ifdef __KERNEL__ -/* This is the shift that is applied to the UAC bits as stored in the - per-thread flags. */ +/* This is the shift that is applied to the UAC bits as stored in the + per-thread flags. See thread_info.h. */ #define UAC_SHIFT 6 #endif diff -Nru a/include/asm-alpha/system.h b/include/asm-alpha/system.h --- a/include/asm-alpha/system.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-alpha/system.h Tue Feb 19 18:08:57 2002 @@ -134,8 +134,7 @@ #define switch_to(prev,next,last) \ do { \ unsigned long pcbb; \ - current = (next); \ - pcbb = virt_to_phys(¤t->thread); \ + pcbb = virt_to_phys(&(next)->thread_info->pcb); \ (last) = alpha_switch_to(pcbb, (prev)); \ check_mmu_context(); \ } while (0) diff -Nru a/include/asm-alpha/thread_info.h b/include/asm-alpha/thread_info.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-alpha/thread_info.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,89 @@ +#ifndef _ALPHA_THREAD_INFO_H +#define _ALPHA_THREAD_INFO_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ +#include +#include +#include +#endif + +#ifndef __ASSEMBLY__ +struct thread_info { + struct pcb_struct pcb; /* palcode state */ + + struct task_struct *task; /* main task structure */ + unsigned int flags; /* low level flags */ + unsigned int ieee_state; /* see fpu.h */ + + struct exec_domain *exec_domain; /* execution domain */ + mm_segment_t addr_limit; /* thread address space */ + int cpu; /* current CPU */ + + int bpt_nsaved; + unsigned long bpt_addr[2]; /* breakpoint handling */ + unsigned int bpt_insn[2]; +}; + +/* + * Macros/functions for gaining access to the thread information structure. + */ +#define INIT_THREAD_INFO(tsk) \ +{ \ + task: &tsk, \ + exec_domain: &default_exec_domain, \ + addr_limit: KERNEL_DS, \ +} + +#define init_thread_info (init_thread_union.thread_info) +#define init_stack (init_thread_union.stack) + +/* How to get the thread information struct from C. */ +register struct thread_info *__current_thread_info __asm__("$8"); +#define current_thread_info() __current_thread_info + +/* Thread information allocation. */ +#define THREAD_SIZE (2*PAGE_SIZE) +#define alloc_thread_info() \ + ((struct thread_info *) __get_free_pages(GFP_KERNEL,1)) +#define free_thread_info(ti) free_pages((unsigned long) (ti), 1) +#define get_thread_info(ti) get_task_struct((ti)->task) +#define put_thread_info(ti) put_task_struct((ti)->task) + +#endif /* __ASSEMBLY__ */ + +/* + * Thread information flags: + * - these are process state flags and used from assembly + * - pending work-to-be-done flags come first to fit in and immediate operand. + * + * TIF_SYSCALL_TRACE is known to be 0 via blbs. + */ +#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ +#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ +#define TIF_SIGPENDING 2 /* signal pending */ +#define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_POLLING_NRFLAG 4 /* poll_idle is polling NEED_RESCHED */ +#define TIF_DIE_IF_KERNEL 5 /* dik recursion lock */ +#define TIF_UAC_NOPRINT 6 /* see sysinfo.h */ +#define TIF_UAC_NOFIX 7 +#define TIF_UAC_SIGBUS 8 + +#define _TIF_SYSCALL_TRACE (1<thread.fs) +#define get_fs() (current_thread_info()->addr_limit) #define get_ds() (KERNEL_DS) -#define set_fs(x) (current->thread.fs = (x)) +#define set_fs(x) (current_thread_info()->addr_limit = (x)) #define segment_eq(a,b) ((a).seg == (b).seg) diff -Nru a/include/asm-alpha/unistd.h b/include/asm-alpha/unistd.h --- a/include/asm-alpha/unistd.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-alpha/unistd.h Tue Feb 19 18:08:59 2002 @@ -506,6 +506,7 @@ #include #include +#include extern void sys_idle(void); static inline void idle(void) @@ -576,6 +577,8 @@ return sys_sync(); } +struct rusage; +extern asmlinkage long sys_wait4(pid_t, unsigned int *, int, struct rusage *); static inline pid_t waitpid(int pid, int * wait_stat, int flags) { return sys_wait4(pid, wait_stat, flags, NULL); diff -Nru a/include/asm-arm/page.h b/include/asm-arm/page.h --- a/include/asm-arm/page.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-arm/page.h Tue Feb 19 18:08:58 2002 @@ -106,6 +106,9 @@ #define VALID_PAGE(page) ((page - mem_map) < max_mapnr) #endif +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + #endif #endif diff -Nru a/include/asm-arm/pgtable.h b/include/asm-arm/pgtable.h --- a/include/asm-arm/pgtable.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-arm/pgtable.h Tue Feb 19 18:08:58 2002 @@ -99,7 +99,6 @@ /* * Permanent address of a page. We never have highmem, so this is trivial. */ -#define page_address(page) ((page)->virtual) #define pages_to_mb(x) ((x) >> (20 - PAGE_SHIFT)) /* diff -Nru a/include/asm-cris/page.h b/include/asm-cris/page.h --- a/include/asm-cris/page.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-cris/page.h Tue Feb 19 18:08:59 2002 @@ -118,6 +118,9 @@ extern unsigned long dram_start, dram_end; +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + #endif /* __KERNEL__ */ #endif /* _CRIS_PAGE_H */ diff -Nru a/include/asm-cris/pgtable.h b/include/asm-cris/pgtable.h --- a/include/asm-cris/pgtable.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-cris/pgtable.h Tue Feb 19 18:08:57 2002 @@ -125,6 +125,7 @@ #define flush_dcache_page(page) do { } while (0) #define flush_icache_range(start, end) do { } while (0) #define flush_icache_page(vma,pg) do { } while (0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) /* * TLB flushing (implemented in arch/cris/mm/tlb.c): @@ -438,7 +439,6 @@ /* permanent address of a page */ -#define page_address(page) ((page)->virtual) #define __page_address(page) (PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT)) #define pte_page(pte) (mem_map+pte_pagenr(pte)) diff -Nru a/include/asm-i386/bitops.h b/include/asm-i386/bitops.h --- a/include/asm-i386/bitops.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-i386/bitops.h Tue Feb 19 18:08:57 2002 @@ -6,6 +6,7 @@ */ #include +#include /* * These have to be done with inline assembly: that way the bit-setting @@ -358,7 +359,7 @@ * @offset: The bitnumber to start searching at * @size: The maximum size to search */ -static __inline__ int find_next_bit (void * addr, int size, int offset) +static __inline__ int find_next_bit(void * addr, int size, int offset) { unsigned long * p = ((unsigned long *) addr) + (offset >> 5); int set = 0, bit = offset & 31, res; @@ -414,6 +415,25 @@ } #ifdef __KERNEL__ + +/* + * Every architecture must define this function. It's the fastest + * way of searching a 140-bit bitmap where the first 100 bits are + * unlikely to be set. It's guaranteed that at least one of the 140 + * bits is cleared. + */ +static inline int sched_find_first_bit(unsigned long *b) +{ + if (unlikely(b[0])) + return __ffs(b[0]); + if (unlikely(b[1])) + return __ffs(b[1]) + 32; + if (unlikely(b[2])) + return __ffs(b[2]) + 64; + if (b[3]) + return __ffs(b[3]) + 96; + return __ffs(b[4]) + 128; +} /** * ffs - find first bit set diff -Nru a/include/asm-i386/highmem.h b/include/asm-i386/highmem.h --- a/include/asm-i386/highmem.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-i386/highmem.h Tue Feb 19 18:08:57 2002 @@ -26,12 +26,6 @@ #include #include -#ifdef CONFIG_DEBUG_HIGHMEM -#define HIGHMEM_DEBUG 1 -#else -#define HIGHMEM_DEBUG 0 -#endif - /* declarations for highmem.c */ extern unsigned long highstart_pfn, highend_pfn; @@ -94,7 +88,7 @@ idx = type + KM_TYPE_NR*smp_processor_id(); vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); -#if HIGHMEM_DEBUG +#if CONFIG_DEBUG_HIGHMEM if (!pte_none(*(kmap_pte-idx))) BUG(); #endif @@ -106,8 +100,8 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type) { -#if HIGHMEM_DEBUG - unsigned long vaddr = (unsigned long) kvaddr; +#if CONFIG_DEBUG_HIGHMEM + unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK; enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id(); if (vaddr < FIXADDR_START) { // FIXME diff -Nru a/include/asm-i386/kmap_types.h b/include/asm-i386/kmap_types.h --- a/include/asm-i386/kmap_types.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-i386/kmap_types.h Tue Feb 19 18:08:58 2002 @@ -1,14 +1,26 @@ #ifndef _ASM_KMAP_TYPES_H #define _ASM_KMAP_TYPES_H +#include + +#if CONFIG_DEBUG_HIGHMEM +# define D(n) __KM_FENCE_##n , +#else +# define D(n) +#endif + enum km_type { - KM_BOUNCE_READ, - KM_SKB_DATA, - KM_SKB_DATA_SOFTIRQ, - KM_USER0, - KM_USER1, - KM_BIO_IRQ, - KM_TYPE_NR +D(0) KM_BOUNCE_READ, +D(1) KM_SKB_DATA, +D(2) KM_SKB_DATA_SOFTIRQ, +D(3) KM_USER0, +D(4) KM_USER1, +D(5) KM_BIO_IRQ, +D(6) KM_PTE0, +D(7) KM_PTE1, +D(8) KM_TYPE_NR }; + +#undef D #endif diff -Nru a/include/asm-i386/mmu_context.h b/include/asm-i386/mmu_context.h --- a/include/asm-i386/mmu_context.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-i386/mmu_context.h Tue Feb 19 18:08:57 2002 @@ -7,30 +7,6 @@ #include /* - * Every architecture must define this function. It's the fastest - * way of searching a 168-bit bitmap where the first 128 bits are - * unlikely to be set. It's guaranteed that at least one of the 168 - * bits is cleared. - */ -#if MAX_RT_PRIO != 128 || MAX_PRIO != 168 -# error update this function. -#endif - -static inline int sched_find_first_bit(unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (unlikely(b[3])) - return __ffs(b[3]) + 96; - if (b[4]) - return __ffs(b[4]) + 128; - return __ffs(b[5]) + 32 + 128; -} -/* * possibly do the LDT unload here? */ #define destroy_context(mm) do { } while(0) diff -Nru a/include/asm-i386/page.h b/include/asm-i386/page.h --- a/include/asm-i386/page.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-i386/page.h Tue Feb 19 18:08:58 2002 @@ -91,16 +91,18 @@ /* * Tell the user there is some problem. Beep too, so we can * see^H^H^Hhear bugs in early bootup as well! + * The offending file and line are encoded after the "officially + * undefined" opcode for parsing in the trap handler. */ -#ifdef CONFIG_DEBUG_BUGVERBOSE -extern void do_BUG(const char *file, int line); -#define BUG() do { \ - do_BUG(__FILE__, __LINE__); \ - __asm__ __volatile__("ud2"); \ -} while (0) +#if 1 /* Set to zero for a slightly smaller kernel */ +#define BUG() \ + __asm__ __volatile__( "ud2\n" \ + "\t.word %c0\n" \ + "\t.long %c1\n" \ + : : "i" (__LINE__), "i" (__FILE__)) #else -#define BUG() __asm__ __volatile__(".byte 0x0f,0x0b") +#define BUG() __asm__ __volatile__("ud2\n") #endif #define PAGE_BUG(page) do { \ @@ -131,6 +133,9 @@ #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) #define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT)) #define VALID_PAGE(page) ((page - mem_map) < max_mapnr) + +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) #endif /* __KERNEL__ */ diff -Nru a/include/asm-i386/pgalloc.h b/include/asm-i386/pgalloc.h --- a/include/asm-i386/pgalloc.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-i386/pgalloc.h Tue Feb 19 18:08:57 2002 @@ -5,15 +5,17 @@ #include #include #include +#include -#define pgd_quicklist (current_cpu_data.pgd_quick) -#define pmd_quicklist (current_cpu_data.pmd_quick) -#define pte_quicklist (current_cpu_data.pte_quick) -#define pgtable_cache_size (current_cpu_data.pgtable_cache_sz) - -#define pmd_populate(mm, pmd, pte) \ +#define pmd_populate_kernel(mm, pmd, pte) \ set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte))) +static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte) +{ + set_pmd(pmd, __pmd(_PAGE_TABLE + + ((unsigned long long)(pte - mem_map) << + (unsigned long long) PAGE_SHIFT))); +} /* * Allocate and free page tables. */ @@ -29,7 +31,7 @@ extern void kmem_cache_free(struct kmem_cache_s *, void *); -static inline pgd_t *get_pgd_slow(void) +static inline pgd_t *pgd_alloc(struct mm_struct *mm) { int i; pgd_t *pgd = kmem_cache_alloc(pae_pgd_cachep, GFP_KERNEL); @@ -56,7 +58,7 @@ #else -static inline pgd_t *get_pgd_slow(void) +static inline pgd_t *pgd_alloc(struct mm_struct *mm) { pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL); @@ -71,33 +73,7 @@ #endif /* CONFIG_X86_PAE */ -static inline pgd_t *get_pgd_fast(void) -{ - unsigned long *ret; - - preempt_disable(); - if ((ret = pgd_quicklist) != NULL) { - pgd_quicklist = (unsigned long *)(*ret); - ret[0] = 0; - pgtable_cache_size--; - preempt_enable(); - } else { - preempt_enable(); - ret = (unsigned long *)get_pgd_slow(); - } - return (pgd_t *)ret; -} - -static inline void free_pgd_fast(pgd_t *pgd) -{ - preempt_disable(); - *(unsigned long *)pgd = (unsigned long) pgd_quicklist; - pgd_quicklist = (unsigned long *) pgd; - pgtable_cache_size++; - preempt_enable(); -} - -static inline void free_pgd_slow(pgd_t *pgd) +static inline void pgd_free(pgd_t *pgd) { #if defined(CONFIG_X86_PAE) int i; @@ -110,63 +86,63 @@ #endif } -static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address) +static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { + int count = 0; pte_t *pte; - - pte = (pte_t *) __get_free_page(GFP_KERNEL); - if (pte) - clear_page(pte); + + do { + pte = (pte_t *) __get_free_page(GFP_KERNEL); + if (pte) + clear_page(pte); + else { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ); + } + } while (!pte && (count++ < 10)); return pte; } -static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, - unsigned long address) +static inline struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) { - unsigned long *ret; - - preempt_disable(); - if ((ret = (unsigned long *)pte_quicklist) != NULL) { - pte_quicklist = (unsigned long *)(*ret); - ret[0] = ret[1]; - pgtable_cache_size--; - } - preempt_enable(); - return (pte_t *)ret; + int count = 0; + struct page *pte; + + do { +#if CONFIG_HIGHPTE + pte = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, 0); +#else + pte = alloc_pages(GFP_KERNEL, 0); +#endif + if (pte) + clear_highpage(pte); + else { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ); + } + } while (!pte && (count++ < 10)); + return pte; } -static inline void pte_free_fast(pte_t *pte) +static inline void pte_free_kernel(pte_t *pte) { - preempt_disable(); - *(unsigned long *)pte = (unsigned long) pte_quicklist; - pte_quicklist = (unsigned long *) pte; - pgtable_cache_size++; - preempt_enable(); + free_page((unsigned long)pte); } -static __inline__ void pte_free_slow(pte_t *pte) +static inline void pte_free(struct page *pte) { - free_page((unsigned long)pte); + __free_page(pte); } -#define pte_free(pte) pte_free_slow(pte) -#define pgd_free(pgd) free_pgd_slow(pgd) -#define pgd_alloc(mm) get_pgd_fast() - /* * allocating and freeing a pmd is trivial: the 1-entry pmd is * inside the pgd, so has no extra memory associated with it. * (In the PAE case we free the pmds as part of the pgd.) */ -#define pmd_alloc_one_fast(mm, addr) ({ BUG(); ((pmd_t *)1); }) #define pmd_alloc_one(mm, addr) ({ BUG(); ((pmd_t *)2); }) -#define pmd_free_slow(x) do { } while (0) -#define pmd_free_fast(x) do { } while (0) #define pmd_free(x) do { } while (0) #define pgd_populate(mm, pmd, pte) BUG() - -extern int do_check_pgt_cache(int, int); /* * TLB flushing: diff -Nru a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h --- a/include/asm-i386/pgtable.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-i386/pgtable.h Tue Feb 19 18:08:58 2002 @@ -33,6 +33,7 @@ #define flush_dcache_page(page) do { } while (0) #define flush_icache_range(start, end) do { } while (0) #define flush_icache_page(vma,pg) do { } while (0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) #define __flush_tlb() \ do { \ @@ -263,11 +264,7 @@ #define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0) #define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE) -/* - * Permanent address of a page. Obviously must never be - * called on a highmem page. - */ -#define page_address(page) ((page)->virtual) + #define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) /* @@ -315,9 +312,12 @@ #define page_pte(page) page_pte_prot(page, __pgprot(0)) -#define pmd_page(pmd) \ +#define pmd_page_kernel(pmd) \ ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) +#define pmd_page(pmd) \ + (mem_map + (pmd_val(pmd) >> PAGE_SHIFT)) + /* to find an entry in a page-table-directory. */ #define pgd_index(address) ((address >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) @@ -334,8 +334,14 @@ /* Find an entry in the third-level page table.. */ #define __pte_offset(address) \ ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) -#define pte_offset(dir, address) ((pte_t *) pmd_page(*(dir)) + \ - __pte_offset(address)) +#define pte_offset_kernel(dir, address) \ + ((pte_t *) pmd_page_kernel(*(dir)) + __pte_offset(address)) +#define pte_offset_map(dir, address) \ + ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE0) + __pte_offset(address)) +#define pte_offset_map_nested(dir, address) \ + ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE1) + __pte_offset(address)) +#define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0) +#define pte_unmap_nested(pte) kunmap_atomic(pte, KM_PTE1) /* * The i386 doesn't have any external MMU info: the kernel page diff -Nru a/include/asm-i386/processor.h b/include/asm-i386/processor.h --- a/include/asm-i386/processor.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-i386/processor.h Tue Feb 19 18:08:57 2002 @@ -49,10 +49,6 @@ int f00f_bug; int coma_bug; unsigned long loops_per_jiffy; - unsigned long *pgd_quick; - unsigned long *pmd_quick; - unsigned long *pte_quick; - unsigned long pgtable_cache_sz; } __attribute__((__aligned__(SMP_CACHE_BYTES))); #define X86_VENDOR_INTEL 0 @@ -436,13 +432,7 @@ extern void copy_segments(struct task_struct *p, struct mm_struct * mm); extern void release_segments(struct mm_struct * mm); -/* - * Return saved PC of a blocked thread. - */ -static inline unsigned long thread_saved_pc(struct task_struct *tsk) -{ - return ((unsigned long *)tsk->thread->esp)[3]; -} +extern unsigned long thread_saved_pc(struct task_struct *tsk); unsigned long get_wchan(struct task_struct *p); #define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)->thread_info))[1019]) diff -Nru a/include/asm-i386/smplock.h b/include/asm-i386/smplock.h --- a/include/asm-i386/smplock.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-i386/smplock.h Tue Feb 19 18:08:59 2002 @@ -12,10 +12,15 @@ #ifdef CONFIG_SMP #define kernel_locked() spin_is_locked(&kernel_flag) +#define check_irq_holder(cpu) \ +do { \ + if (global_irq_holder == (cpu)) \ + BUG(); \ +} while(0) #else #ifdef CONFIG_PREEMPT #define kernel_locked() preempt_get_count() -#define global_irq_holder 0 +#define check_irq_holder(cpu) do { } while(0) #else #define kernel_locked() 1 #endif @@ -28,8 +33,7 @@ do { \ if (unlikely(task->lock_depth >= 0)) { \ spin_unlock(&kernel_flag); \ - if (global_irq_holder == (cpu)) \ - BUG(); \ + check_irq_holder(cpu); \ } \ } while (0) diff -Nru a/include/asm-i386/system.h b/include/asm-i386/system.h --- a/include/asm-i386/system.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-i386/system.h Tue Feb 19 18:08:57 2002 @@ -13,24 +13,23 @@ extern void FASTCALL(__switch_to(struct task_struct *prev, struct task_struct *next)); #define prepare_to_switch() do { } while(0) -#define switch_to(prev,next,last) do { \ + +#define switch_to(prev,next) do { \ asm volatile("pushl %%esi\n\t" \ "pushl %%edi\n\t" \ "pushl %%ebp\n\t" \ "movl %%esp,%0\n\t" /* save ESP */ \ - "movl %3,%%esp\n\t" /* restore ESP */ \ + "movl %2,%%esp\n\t" /* restore ESP */ \ "movl $1f,%1\n\t" /* save EIP */ \ - "pushl %4\n\t" /* restore EIP */ \ + "pushl %3\n\t" /* restore EIP */ \ "jmp __switch_to\n" \ "1:\t" \ "popl %%ebp\n\t" \ "popl %%edi\n\t" \ "popl %%esi\n\t" \ - :"=m" (prev->thread.esp),"=m" (prev->thread.eip), \ - "=b" (last) \ + :"=m" (prev->thread.esp),"=m" (prev->thread.eip) \ :"m" (next->thread.esp),"m" (next->thread.eip), \ - "a" (prev), "d" (next), \ - "b" (prev)); \ + "a" (prev), "d" (next)); \ } while (0) #define _set_base(addr,base) do { unsigned long __pr; \ diff -Nru a/include/asm-ia64/page.h b/include/asm-ia64/page.h --- a/include/asm-ia64/page.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ia64/page.h Tue Feb 19 18:08:58 2002 @@ -148,6 +148,11 @@ # define __pgprot(x) (x) #endif /* !STRICT_MM_TYPECHECKS */ -#define PAGE_OFFSET 0xe000000000000000 +#define PAGE_OFFSET 0xe000000000000000 + +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC | \ + (((current->thread.flags & IA64_THREAD_XSTACK) != 0) \ + ? VM_EXEC : 0)) #endif /* _ASM_IA64_PAGE_H */ diff -Nru a/include/asm-ia64/pgtable.h b/include/asm-ia64/pgtable.h --- a/include/asm-ia64/pgtable.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ia64/pgtable.h Tue Feb 19 18:08:59 2002 @@ -165,11 +165,6 @@ * addresses: */ -/* - * Given a pointer to an mem_map[] entry, return the kernel virtual - * address corresponding to that page. - */ -#define page_address(page) ((page)->virtual) /* Quick test to see if ADDR is a (potentially) valid physical address. */ static inline long diff -Nru a/include/asm-m68k/page.h b/include/asm-m68k/page.h --- a/include/asm-m68k/page.h Tue Feb 19 18:09:00 2002 +++ b/include/asm-m68k/page.h Tue Feb 19 18:09:00 2002 @@ -181,6 +181,9 @@ #endif /* __ASSEMBLY__ */ +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + #endif /* __KERNEL__ */ #endif /* _M68K_PAGE_H */ diff -Nru a/include/asm-m68k/pgalloc.h b/include/asm-m68k/pgalloc.h --- a/include/asm-m68k/pgalloc.h Tue Feb 19 18:09:00 2002 +++ b/include/asm-m68k/pgalloc.h Tue Feb 19 18:09:00 2002 @@ -127,6 +127,7 @@ #define flush_dcache_page(page) do { } while (0) #define flush_icache_page(vma,pg) do { } while (0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) /* Push n pages at kernel virtual address and clear the icache */ /* RZ: use cpush %bc instead of cpush %dc, cinv %ic */ diff -Nru a/include/asm-mips/page.h b/include/asm-mips/page.h --- a/include/asm-mips/page.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-mips/page.h Tue Feb 19 18:08:59 2002 @@ -78,6 +78,9 @@ #define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT)) #define VALID_PAGE(page) ((page - mem_map) < max_mapnr) +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + #endif /* defined (__KERNEL__) */ #endif /* __ASM_PAGE_H */ diff -Nru a/include/asm-mips/pgtable.h b/include/asm-mips/pgtable.h --- a/include/asm-mips/pgtable.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-mips/pgtable.h Tue Feb 19 18:08:58 2002 @@ -51,6 +51,8 @@ #define flush_icache_range(start, end) _flush_icache_range(start,end) #define flush_icache_page(vma, page) _flush_icache_page(vma, page) +#define flush_icache_user_range(vma, page, addr, len) \ + _flush_icache_page((vma), (page)) /* @@ -329,11 +331,6 @@ extern inline int pgd_present(pgd_t pgd) { return 1; } extern inline void pgd_clear(pgd_t *pgdp) { } -/* - * Permanent address of a page. On MIPS we never have highmem, so this - * is simple. - */ -#define page_address(page) ((page)->virtual) #ifdef CONFIG_CPU_VR41XX #define pte_page(x) (mem_map+(unsigned long)((pte_val(x) >> (PAGE_SHIFT + 2)))) #else diff -Nru a/include/asm-mips64/page.h b/include/asm-mips64/page.h --- a/include/asm-mips64/page.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-mips64/page.h Tue Feb 19 18:08:57 2002 @@ -73,6 +73,9 @@ #define VALID_PAGE(page) ((page - mem_map) < max_mapnr) #endif +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + #endif /* defined (__KERNEL__) */ #endif /* _ASM_PAGE_H */ diff -Nru a/include/asm-mips64/pgtable.h b/include/asm-mips64/pgtable.h --- a/include/asm-mips64/pgtable.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-mips64/pgtable.h Tue Feb 19 18:08:59 2002 @@ -43,6 +43,8 @@ #define flush_page_to_ram(page) _flush_page_to_ram(page) #define flush_icache_range(start, end) _flush_cache_l1() +#define flush_icache_user_range(vma, page, addr, len) \ + flush_icache_page((vma), (page)) #define flush_icache_page(vma, page) \ do { \ @@ -66,6 +68,8 @@ #define flush_cache_page(vma,page) do { } while(0) #define flush_page_to_ram(page) do { } while(0) #define flush_icache_range(start, end) _flush_cache_l1() +#define flush_icache_user_range(vma, page, addr, len) \ + flush_icache_page((vma), (page)) #define flush_icache_page(vma, page) \ do { \ if ((vma)->vm_flags & VM_EXEC) \ @@ -366,11 +370,6 @@ pgd_val(*pgdp) = ((unsigned long) invalid_pmd_table); } -/* - * Permanent address of a page. On MIPS64 we never have highmem, so this - * is simple. - */ -#define page_address(page) ((page)->virtual) #ifndef CONFIG_DISCONTIGMEM #define pte_page(x) (mem_map+(unsigned long)((pte_val(x) >> PAGE_SHIFT))) #else diff -Nru a/include/asm-parisc/page.h b/include/asm-parisc/page.h --- a/include/asm-parisc/page.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-parisc/page.h Tue Feb 19 18:08:58 2002 @@ -81,6 +81,9 @@ #define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT)) #define VALID_PAGE(page) ((page - mem_map) < max_mapnr) +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + #endif /* __KERNEL__ */ #endif /* _PARISC_PAGE_H */ diff -Nru a/include/asm-parisc/pgalloc.h b/include/asm-parisc/pgalloc.h --- a/include/asm-parisc/pgalloc.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-parisc/pgalloc.h Tue Feb 19 18:08:59 2002 @@ -106,6 +106,9 @@ #define flush_icache_range(start, end) \ __flush_icache_range(start, end - start) +#define flush_icache_user_range(vma, page, addr, len) \ + flush_icache_page((vma), (page)) + #define flush_icache_page(vma, page) \ __flush_icache_range(page_address(page), PAGE_SIZE) diff -Nru a/include/asm-parisc/pgtable.h b/include/asm-parisc/pgtable.h --- a/include/asm-parisc/pgtable.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-parisc/pgtable.h Tue Feb 19 18:08:59 2002 @@ -275,7 +275,6 @@ * Permanent address of a page. Obviously must never be * called on a highmem page. */ -#define page_address(page) ({ if (!(page)->virtual) BUG(); (page)->virtual; }) #define __page_address(page) ({ if (PageHighMem(page)) BUG(); PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT); }) #define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) #define pte_page(x) (mem_map+pte_pagenr(x)) diff -Nru a/include/asm-ppc/8xx_immap.h b/include/asm-ppc/8xx_immap.h --- a/include/asm-ppc/8xx_immap.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/8xx_immap.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.8xx_immap.h 1.5 05/17/01 18:14:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* @@ -178,7 +178,7 @@ */ #define KAPWR_KEY ((unsigned int)0x55ccaa33) -/* LCD interface. MPC821 Only. +/* LCD interface. MPC821 and MPC823 Only. */ typedef struct lcd { ushort lcd_lcolr[16]; @@ -353,6 +353,12 @@ uint res9[0x1e]; } fec_t; +/* We need this as the fec and fb cmap use the same address space */ +union fec_lcd { + fec_t fl_un_fec; + u_char fl_un_cmap[0x200]; +}; + typedef struct comm_proc { /* General control and status registers. */ @@ -424,8 +430,12 @@ /* The fast ethernet controller is not really part of the CPM, * but it resides in the address space. + * + * The colormap for the LCD controller is also located here */ - fec_t cp_fec; + union fec_lcd fl_un; +#define cp_fec fl_un.fl_un_fec +#define lcd_cmap fl_un.fl_un_cmap char res18[0x1000]; /* Dual Ported RAM follows. @@ -447,7 +457,7 @@ car8xx_t im_clkrst; /* Clocks and reset */ sitk8xx_t im_sitk; /* Sys int timer keys */ cark8xx_t im_clkrstk; /* Clocks and reset keys */ - lcd8xx_t im_lcd; /* LCD (821 only) */ + lcd8xx_t im_lcd; /* LCD (821 and 823 only) */ i2c8xx_t im_i2c; /* I2C control/status */ sdma8xx_t im_sdma; /* SDMA control/status */ cpic8xx_t im_cpic; /* CPM Interrupt Controller */ diff -Nru a/include/asm-ppc/amigaints.h b/include/asm-ppc/amigaints.h --- a/include/asm-ppc/amigaints.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/amigaints.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.amigaints.h 1.5 05/17/01 18:14:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* ** amigaints.h -- Amiga Linux interrupt handling structs and prototypes @@ -110,12 +110,8 @@ #define IF_DSKBLK 0x0002 /* diskblock DMA finished */ #define IF_TBE 0x0001 /* serial transmit buffer empty interrupt */ -struct irq_server { - unsigned short count, reentrance; -}; - extern void amiga_do_irq(int irq, struct pt_regs *fp); -extern void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server); +extern void amiga_do_irq_list(int irq, struct pt_regs *fp); /* CIA interrupt control register bits */ diff -Nru a/include/asm-ppc/ans-lcd.h b/include/asm-ppc/ans-lcd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/ans-lcd.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,11 @@ +#ifndef _PPC_ANS_LCD_H +#define _PPC_ANS_LCD_H + +#define ANSLCD_MINOR 156 + +#define ANSLCD_CLEAR 0x01 +#define ANSLCD_SENDCTRL 0x02 +#define ANSLCD_SETSHORTDELAY 0x03 +#define ANSLCD_SETLONGDELAY 0x04 + +#endif diff -Nru a/include/asm-ppc/atomic.h b/include/asm-ppc/atomic.h --- a/include/asm-ppc/atomic.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/atomic.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.atomic.h 1.15 10/28/01 10:37:22 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * PowerPC atomic operations @@ -18,7 +18,6 @@ #define atomic_set(v,i) (((v)->counter) = (i)) extern void atomic_clear_mask(unsigned long mask, unsigned long *addr); -extern void atomic_set_mask(unsigned long mask, unsigned long *addr); #ifdef CONFIG_SMP #define SMP_ISYNC "\n\tisync" @@ -26,14 +25,24 @@ #define SMP_ISYNC #endif +/* Erratum #77 on the 405 means we need a sync or dcbt before every stwcx. + * The old ATOMIC_SYNC_FIX covered some but not all of this. + */ +#ifdef CONFIG_IBM405_ERR77 +#define PPC405_ERR77(ra,rb) "dcbt " #ra "," #rb ";" +#else +#define PPC405_ERR77(ra,rb) +#endif + static __inline__ void atomic_add(int a, atomic_t *v) { int t; __asm__ __volatile__( "1: lwarx %0,0,%3 # atomic_add\n\ - add %0,%2,%0\n\ - stwcx. %0,0,%3\n\ + add %0,%2,%0\n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ bne- 1b" : "=&r" (t), "=m" (v->counter) : "r" (a), "r" (&v->counter), "m" (v->counter) @@ -46,8 +55,9 @@ __asm__ __volatile__( "1: lwarx %0,0,%2 # atomic_add_return\n\ - add %0,%1,%0\n\ - stwcx. %0,0,%2\n\ + add %0,%1,%0\n" + PPC405_ERR77(0,%2) +" stwcx. %0,0,%2 \n\ bne- 1b" SMP_ISYNC : "=&r" (t) @@ -63,8 +73,9 @@ __asm__ __volatile__( "1: lwarx %0,0,%3 # atomic_sub\n\ - subf %0,%2,%0\n\ - stwcx. %0,0,%3\n\ + subf %0,%2,%0\n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ bne- 1b" : "=&r" (t), "=m" (v->counter) : "r" (a), "r" (&v->counter), "m" (v->counter) @@ -77,8 +88,9 @@ __asm__ __volatile__( "1: lwarx %0,0,%2 # atomic_sub_return\n\ - subf %0,%1,%0\n\ - stwcx. %0,0,%2\n\ + subf %0,%1,%0\n" + PPC405_ERR77(0,%2) +" stwcx. %0,0,%2 \n\ bne- 1b" SMP_ISYNC : "=&r" (t) @@ -94,8 +106,9 @@ __asm__ __volatile__( "1: lwarx %0,0,%2 # atomic_inc\n\ - addic %0,%0,1\n\ - stwcx. %0,0,%2\n\ + addic %0,%0,1\n" + PPC405_ERR77(0,%2) +" stwcx. %0,0,%2 \n\ bne- 1b" : "=&r" (t), "=m" (v->counter) : "r" (&v->counter), "m" (v->counter) @@ -108,8 +121,9 @@ __asm__ __volatile__( "1: lwarx %0,0,%1 # atomic_inc_return\n\ - addic %0,%0,1\n\ - stwcx. %0,0,%1\n\ + addic %0,%0,1\n" + PPC405_ERR77(0,%1) +" stwcx. %0,0,%1 \n\ bne- 1b" SMP_ISYNC : "=&r" (t) @@ -125,8 +139,9 @@ __asm__ __volatile__( "1: lwarx %0,0,%2 # atomic_dec\n\ - addic %0,%0,-1\n\ - stwcx. %0,0,%2\n\ + addic %0,%0,-1\n" + PPC405_ERR77(0,%2)\ +" stwcx. %0,0,%2\n\ bne- 1b" : "=&r" (t), "=m" (v->counter) : "r" (&v->counter), "m" (v->counter) @@ -139,8 +154,9 @@ __asm__ __volatile__( "1: lwarx %0,0,%1 # atomic_dec_return\n\ - addic %0,%0,-1\n\ - stwcx. %0,0,%1\n\ + addic %0,%0,-1\n" + PPC405_ERR77(0,%1) +" stwcx. %0,0,%1\n\ bne- 1b" SMP_ISYNC : "=&r" (t) @@ -164,8 +180,9 @@ __asm__ __volatile__( "1: lwarx %0,0,%1 # atomic_dec_if_positive\n\ addic. %0,%0,-1\n\ - blt- 2f\n\ - stwcx. %0,0,%1\n\ + blt- 2f\n" + PPC405_ERR77(0,%1) +" stwcx. %0,0,%1\n\ bne- 1b" SMP_ISYNC "\n\ diff -Nru a/include/asm-ppc/bitops.h b/include/asm-ppc/bitops.h --- a/include/asm-ppc/bitops.h Tue Feb 19 18:09:00 2002 +++ b/include/asm-ppc/bitops.h Tue Feb 19 18:09:00 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.bitops.h 1.9 05/26/01 14:48:14 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * bitops.h: Bit string operations on the ppc @@ -10,7 +10,9 @@ #define _PPC_BITOPS_H #include +#include #include +#include /* * The test_and_*_bit operations are taken to imply a memory barrier @@ -36,8 +38,9 @@ __asm__ __volatile__("\n\ 1: lwarx %0,0,%3 \n\ - or %0,%0,%2 \n\ - stwcx. %0,0,%3 \n\ + or %0,%0,%2 \n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ bne- 1b" : "=&r" (old), "=m" (*p) : "r" (mask), "r" (p), "m" (*p) @@ -69,8 +72,9 @@ __asm__ __volatile__("\n\ 1: lwarx %0,0,%3 \n\ - andc %0,%0,%2 \n\ - stwcx. %0,0,%3 \n\ + andc %0,%0,%2 \n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ bne- 1b" : "=&r" (old), "=m" (*p) : "r" (mask), "r" (p), "m" (*p) @@ -96,8 +100,9 @@ __asm__ __volatile__("\n\ 1: lwarx %0,0,%3 \n\ - xor %0,%0,%2 \n\ - stwcx. %0,0,%3 \n\ + xor %0,%0,%2 \n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ bne- 1b" : "=&r" (old), "=m" (*p) : "r" (mask), "r" (p), "m" (*p) @@ -126,8 +131,9 @@ __asm__ __volatile__(SMP_WMB "\n\ 1: lwarx %0,0,%4 \n\ - or %1,%0,%3 \n\ - stwcx. %1,0,%4 \n\ + or %1,%0,%3 \n" + PPC405_ERR77(0,%4) +" stwcx. %1,0,%4 \n\ bne 1b" SMP_MB : "=&r" (old), "=&r" (t), "=m" (*p) @@ -158,8 +164,9 @@ __asm__ __volatile__(SMP_WMB "\n\ 1: lwarx %0,0,%4 \n\ - andc %1,%0,%3 \n\ - stwcx. %1,0,%4 \n\ + andc %1,%0,%3 \n" + PPC405_ERR77(0,%4) +" stwcx. %1,0,%4 \n\ bne 1b" SMP_MB : "=&r" (old), "=&r" (t), "=m" (*p) @@ -190,8 +197,9 @@ __asm__ __volatile__(SMP_WMB "\n\ 1: lwarx %0,0,%4 \n\ - xor %1,%0,%3 \n\ - stwcx. %1,0,%4 \n\ + xor %1,%0,%3 \n" + PPC405_ERR77(0,%4) +" stwcx. %1,0,%4 \n\ bne 1b" SMP_MB : "=&r" (old), "=&r" (t), "=m" (*p) @@ -239,6 +247,11 @@ #ifdef __KERNEL__ +static inline int __ffs(unsigned long x) +{ + return __ilog2(x & -x); +} + /* * ffs: find first bit set. This is defined the same way as * the libc and compiler builtin ffs routines, therefore @@ -259,6 +272,79 @@ #define hweight8(x) generic_hweight8(x) #endif /* __KERNEL__ */ + +/* + * Find the first bit set in a 140-bit bitmap. + * The first 100 bits are unlikely to be set. + */ +static inline int sched_find_first_bit(unsigned long *b) +{ + if (unlikely(b[0])) + return __ffs(b[0]); + if (unlikely(b[1])) + return __ffs(b[1]) + 32; + if (unlikely(b[2])) + return __ffs(b[2]) + 64; + if (b[3]) + return __ffs(b[3]) + 96; + return __ffs(b[4]) + 128; +} + +/** + * find_next_bit - find the next set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +static __inline__ unsigned long find_next_bit(void *addr, + unsigned long size, unsigned long offset) +{ + unsigned int *p = ((unsigned int *) addr) + (offset >> 5); + unsigned int result = offset & ~31UL; + unsigned int tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31UL; + if (offset) { + tmp = *p++; + tmp &= ~0UL << offset; + if (size < 32) + goto found_first; + if (tmp) + goto found_middle; + size -= 32; + result += 32; + } + while (size >= 32) { + if ((tmp = *p++) != 0) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp &= ~0UL >> (32 - size); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __ffs(tmp); +} + +/** + * find_first_bit - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit-number of the first set bit, not the number of the byte + * containing a bit. + */ +#define find_first_bit(addr, size) \ + find_next_bit((addr), (size), 0) /* * This implementation of find_{first,next}_zero_bit was stolen from diff -Nru a/include/asm-ppc/board.h b/include/asm-ppc/board.h --- a/include/asm-ppc/board.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,47 +0,0 @@ -/* - * BK Id: SCCS/s.board.h 1.5 05/17/01 18:14:24 cort - */ -/* - * - * Copyright (c) 1999 Grant Erickson - * - * Module name: board.h - * - * Description: - * A generic include file which pulls in appropriate include files - * for specific board types based on configuration settings. - * - */ - -#ifdef __KERNEL__ -#ifndef __BOARD_H__ -#define __BOARD_H__ - -#include - -#if defined(CONFIG_OAK) -#include -#endif - -#if defined(CONFIG_WALNUT) -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * The "residual" board information structure the boot loader passes - * into the kernel. - */ - -extern unsigned char __res[]; - - -#ifdef __cplusplus -} -#endif - -#endif /* __BOARD_H__ */ -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/bootinfo.h b/include/asm-ppc/bootinfo.h --- a/include/asm-ppc/bootinfo.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/bootinfo.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.bootinfo.h 1.11 08/17/01 15:23:17 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * Non-machine dependent bootinfo structure. Basic idea @@ -19,9 +19,9 @@ #else struct bi_record { - unsigned long tag; /* tag ID */ - unsigned long size; /* size of record (in bytes) */ - unsigned long data[0]; /* data */ + unsigned long tag; /* tag ID */ + unsigned long size; /* size of record (in bytes) */ + unsigned long data[0]; /* data */ }; #define BI_FIRST 0x1010 /* first record - marker */ @@ -32,6 +32,9 @@ #define BI_SYSMAP 0x1015 #define BI_MACHTYPE 0x1016 #define BI_MEMSIZE 0x1017 + +extern struct bi_record *find_bootinfo(void); +extern void parse_bootinfo(struct bi_record *rec); #endif /* CONFIG_APUS */ diff -Nru a/include/asm-ppc/btext.h b/include/asm-ppc/btext.h --- a/include/asm-ppc/btext.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/btext.h Tue Feb 19 18:08:59 2002 @@ -17,11 +17,11 @@ extern unsigned long disp_BAT[2]; -extern boot_infos_t *disp_bi; +extern boot_infos_t disp_bi; extern int boot_text_mapped; void btext_init(boot_infos_t *bi); -void btext_welcome(boot_infos_t* bi); +void btext_welcome(void); void btext_prepare_BAT(void); void btext_setup_display(int width, int height, int depth, int pitch, unsigned long address); diff -Nru a/include/asm-ppc/byteorder.h b/include/asm-ppc/byteorder.h --- a/include/asm-ppc/byteorder.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/byteorder.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.byteorder.h 1.8 10/11/01 13:02:49 trini + * BK Id: %F% %I% %G% %U% %#% */ #ifndef _PPC_BYTEORDER_H #define _PPC_BYTEORDER_H @@ -35,17 +35,11 @@ __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr)); } -/* alas, egcs sounds like it has a bug in this code that doesn't use the - inline asm correctly, and can cause file corruption. Until I hear that - it's fixed, I can live without the extra speed. I hope. */ -#if 0 static __inline__ __const__ __u16 ___arch__swab16(__u16 value) { __u16 result; - __asm__("rlwimi %0,%1,8,16,23" - : "=r" (result) - : "r" (value), "0" (value >> 8)); + __asm__("rlwimi %0,%2,8,16,23" : "=&r" (result) : "0" (value >> 8), "r" (value)); return result; } @@ -53,16 +47,14 @@ { __u32 result; - __asm__("rlwimi %0,%1,24,16,23\n\t" - "rlwimi %0,%1,8,8,15\n\t" - "rlwimi %0,%1,24,0,7" - : "=r" (result) - : "r" (value), "0" (value >> 24)); + __asm__("rlwimi %0,%2,24,16,23" : "=&r" (result) : "0" (value>>24), "r" (value)); + __asm__("rlwimi %0,%2,8,8,15" : "=&r" (result) : "0" (result), "r" (value)); + __asm__("rlwimi %0,%2,24,0,7" : "=&r" (result) : "0" (result), "r" (value)); + return result; } #define __arch__swab32(x) ___arch__swab32(x) #define __arch__swab16(x) ___arch__swab16(x) -#endif /* 0 */ /* The same, but returns converted value from the location pointer by addr. */ #define __arch__swab16p(addr) ld_le16(addr) diff -Nru a/include/asm-ppc/cache.h b/include/asm-ppc/cache.h --- a/include/asm-ppc/cache.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/cache.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.cache.h 1.10 10/18/01 15:02:09 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * include/asm-ppc/cache.h @@ -15,15 +15,15 @@ #if defined(CONFIG_8xx) || defined(CONFIG_403GCX) #define L1_CACHE_LINE_SIZE 16 #define LG_L1_CACHE_LINE_SIZE 4 -#define MAX_L1_COPY_PREFETCH 1 +#define MAX_COPY_PREFETCH 1 #elif defined(CONFIG_PPC64BRIDGE) #define L1_CACHE_LINE_SIZE 128 #define LG_L1_CACHE_LINE_SIZE 7 -#define MAX_L1_COPY_PREFETCH 1 +#define MAX_COPY_PREFETCH 1 #else #define L1_CACHE_LINE_SIZE 32 #define LG_L1_CACHE_LINE_SIZE 5 -#define MAX_L1_COPY_PREFETCH 4 +#define MAX_COPY_PREFETCH 4 #endif #define L1_CACHE_BYTES L1_CACHE_LINE_SIZE @@ -41,7 +41,10 @@ #endif #if defined(__KERNEL__) && !defined(__ASSEMBLY__) +extern void clean_dcache_range(unsigned long start, unsigned long stop); extern void flush_dcache_range(unsigned long start, unsigned long stop); +extern void invalidate_dcache_range(unsigned long start, unsigned long stop); +extern void flush_dcache_all(void); #endif /* __ASSEMBLY__ */ diff -Nru a/include/asm-ppc/dbdma.h b/include/asm-ppc/dbdma.h --- a/include/asm-ppc/dbdma.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/dbdma.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.dbdma.h 1.5 05/17/01 18:14:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * Definitions for using the Apple Descriptor-Based DMA controller @@ -93,5 +93,13 @@ /* Align an address for a DBDMA command structure */ #define DBDMA_ALIGN(x) (((unsigned)(x) + sizeof(struct dbdma_cmd) - 1) \ & -sizeof(struct dbdma_cmd)) + +/* Useful macros */ +#define DBDMA_DO_STOP(regs) do { \ + out_le32(&((regs)->control), (RUN|FLUSH)<<16); \ + while(in_le32(&((regs)->status)) & (ACTIVE|FLUSH)) \ + ; \ +} while(0) + #endif /* _ASM_DBDMA_H_ */ #endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/delay.h b/include/asm-ppc/delay.h --- a/include/asm-ppc/delay.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/delay.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.delay.h 1.7 05/17/01 18:14:24 cort + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef _PPC_DELAY_H @@ -18,35 +18,36 @@ extern unsigned long loops_per_jiffy; -/* maximum permitted argument to udelay */ -#define __MAX_UDELAY 1000000 - extern void __delay(unsigned int loops); -/* N.B. the `secs' parameter here is a fixed-point number with - the binary point to the left of the most-significant bit. */ -extern __inline__ void __const_udelay(unsigned int secs) -{ - unsigned int loops; - - __asm__("mulhwu %0,%1,%2" : "=r" (loops) : - "r" (secs), "r" (loops_per_jiffy)); - __delay(loops * HZ); -} - /* - * note that 4294 == 2^32 / 10^6, multiplying by 4294 converts from - * microseconds to a 32-bit fixed-point number of seconds. + * Note that 19 * 226 == 4294 ==~ 2^32 / 10^6, so + * loops = (4294 * usecs * loops_per_jiffy * HZ) / 2^32. + * + * The mulhwu instruction gives us loops = (a * b) / 2^32. + * We choose a = usecs * 19 * HZ and b = loops_per_jiffy * 226 + * because this lets us support a wide range of HZ and + * loops_per_jiffy values without either a or b overflowing 2^32. + * Thus we need usecs * HZ <= (2^32 - 1) / 19 = 226050910 and + * loops_per_jiffy <= (2^32 - 1) / 226 = 19004280 + * (which corresponds to ~3800 bogomips at HZ = 100). + * -- paulus */ +#define __MAX_UDELAY (226050910/HZ) /* maximum udelay argument */ + extern __inline__ void __udelay(unsigned int usecs) { - __const_udelay(usecs * 4294); + unsigned int loops; + + __asm__("mulhwu %0,%1,%2" : "=r" (loops) : + "r" (usecs * (19 * HZ)), "r" (loops_per_jiffy * 226)); + __delay(loops); } extern void __bad_udelay(void); /* deliberately undefined */ #define udelay(n) (__builtin_constant_p(n)? \ - ((n) > __MAX_UDELAY? __bad_udelay(): __const_udelay((n) * 4294u)) : \ + ((n) > __MAX_UDELAY? __bad_udelay(): __udelay((n))) : \ __udelay(n)) #endif /* defined(_PPC_DELAY_H) */ diff -Nru a/include/asm-ppc/dma.h b/include/asm-ppc/dma.h --- a/include/asm-ppc/dma.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/dma.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.dma.h 1.8 05/17/01 18:14:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/include/asm/dma.h: Defines for using and allocating dma channels. @@ -27,7 +27,7 @@ * not valid for the PReP platform. Take what you read * with a grain of salt. */ - + #ifndef _ASM_DMA_H #define _ASM_DMA_H @@ -38,13 +38,12 @@ /* The maximum address that we can perform a DMA transfer to on this platform */ /* Doesn't really apply... */ -#define MAX_DMA_ADDRESS 0xFFFFFFFF +#define MAX_DMA_ADDRESS 0xFFFFFFFF /* in arch/ppc/kernel/setup.c -- Cort */ extern unsigned long DMA_MODE_WRITE, DMA_MODE_READ; extern unsigned long ISA_DMA_THRESHOLD; - #ifdef HAVE_REALLY_SLOW_DMA_CONTROLLER #define dma_outb outb_p #else @@ -69,7 +68,7 @@ * - page registers for 5-7 don't use data bit 0, represent 128K pages * - page registers for 0-3 use bit 0, represent 64K pages * - * On PReP, DMA transfers are limited to the lower 16MB of _physical_ memory. + * On PReP, DMA transfers are limited to the lower 16MB of _physical_ memory. * On CHRP, the W83C553F (and VLSI Tollgate?) support full 32 bit addressing. * Note that addresses loaded into registers must be _physical_ addresses, * not logical addresses (which may differ if paging is active). @@ -80,7 +79,7 @@ * | ... | | ... | | ... | * | ... | | ... | | ... | * | ... | | ... | | ... | - * P7 ... P0 A7 ... A0 A7 ... A0 + * P7 ... P0 A7 ... A0 A7 ... A0 * | Page | Addr MSB | Addr LSB | (DMA registers) * * Address mapping for channels 5-7: @@ -89,7 +88,7 @@ * | ... | \ \ ... \ \ \ ... \ \ * | ... | \ \ ... \ \ \ ... \ (not used) * | ... | \ \ ... \ \ \ ... \ - * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0 + * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0 * | Page | Addr MSB | Addr LSB | (DMA registers) * * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses @@ -98,23 +97,15 @@ * * Transfer count (_not # bytes_) is limited to 64K, represented as actual * count - 1 : 64K => 0xFFFF, 1 => 0x0000. Thus, count is always 1 or more, - * and up to 128K bytes may be transferred on channels 5-7 in one operation. + * and up to 128K bytes may be transferred on channels 5-7 in one operation. * */ -/* used in nasty hack for sound - see prep_setup_arch() -- Cort */ +/* see prep_setup_arch() for detailed informations */ +#if defined(CONFIG_SOUND_CS4232) && defined(CONFIG_ALL_PPC) extern long ppc_cs4232_dma, ppc_cs4232_dma2; -#if defined(CONFIG_CS4232) -#if defined(CONFIG_ALL_PPC) #define SND_DMA1 ppc_cs4232_dma #define SND_DMA2 ppc_cs4232_dma2 -#else /* !CONFIG_ALL_PPC */ -#define SND_DMA1 -1 -#define SND_DMA2 -1 -#endif /* CONFIG_ALL_PPC */ -#elif defined(CONFIG_MSS) -#define SND_DMA1 CONFIG_MSS_DMA -#define SND_DMA2 CONFIG_MSS_DMA2 #else #define SND_DMA1 -1 #define SND_DMA2 -1 @@ -127,67 +118,67 @@ /* DMA controller registers */ #define DMA1_CMD_REG 0x08 /* command register (w) */ #define DMA1_STAT_REG 0x08 /* status register (r) */ -#define DMA1_REQ_REG 0x09 /* request register (w) */ +#define DMA1_REQ_REG 0x09 /* request register (w) */ #define DMA1_MASK_REG 0x0A /* single-channel mask (w) */ #define DMA1_MODE_REG 0x0B /* mode register (w) */ #define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */ -#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ +#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ #define DMA1_RESET_REG 0x0D /* Master Clear (w) */ -#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ -#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ +#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ +#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ #define DMA2_CMD_REG 0xD0 /* command register (w) */ #define DMA2_STAT_REG 0xD0 /* status register (r) */ -#define DMA2_REQ_REG 0xD2 /* request register (w) */ +#define DMA2_REQ_REG 0xD2 /* request register (w) */ #define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */ #define DMA2_MODE_REG 0xD6 /* mode register (w) */ #define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */ -#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ +#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ #define DMA2_RESET_REG 0xDA /* Master Clear (w) */ -#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ -#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ +#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ +#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ -#define DMA_ADDR_0 0x00 /* DMA address registers */ -#define DMA_ADDR_1 0x02 -#define DMA_ADDR_2 0x04 -#define DMA_ADDR_3 0x06 -#define DMA_ADDR_4 0xC0 -#define DMA_ADDR_5 0xC4 -#define DMA_ADDR_6 0xC8 -#define DMA_ADDR_7 0xCC - -#define DMA_CNT_0 0x01 /* DMA count registers */ -#define DMA_CNT_1 0x03 -#define DMA_CNT_2 0x05 -#define DMA_CNT_3 0x07 -#define DMA_CNT_4 0xC2 -#define DMA_CNT_5 0xC6 -#define DMA_CNT_6 0xCA -#define DMA_CNT_7 0xCE - -#define DMA_LO_PAGE_0 0x87 /* DMA page registers */ -#define DMA_LO_PAGE_1 0x83 -#define DMA_LO_PAGE_2 0x81 -#define DMA_LO_PAGE_3 0x82 -#define DMA_LO_PAGE_5 0x8B -#define DMA_LO_PAGE_6 0x89 -#define DMA_LO_PAGE_7 0x8A - -#define DMA_HI_PAGE_0 0x487 /* DMA page registers */ -#define DMA_HI_PAGE_1 0x483 -#define DMA_HI_PAGE_2 0x481 -#define DMA_HI_PAGE_3 0x482 -#define DMA_HI_PAGE_5 0x48B -#define DMA_HI_PAGE_6 0x489 -#define DMA_HI_PAGE_7 0x48A +#define DMA_ADDR_0 0x00 /* DMA address registers */ +#define DMA_ADDR_1 0x02 +#define DMA_ADDR_2 0x04 +#define DMA_ADDR_3 0x06 +#define DMA_ADDR_4 0xC0 +#define DMA_ADDR_5 0xC4 +#define DMA_ADDR_6 0xC8 +#define DMA_ADDR_7 0xCC + +#define DMA_CNT_0 0x01 /* DMA count registers */ +#define DMA_CNT_1 0x03 +#define DMA_CNT_2 0x05 +#define DMA_CNT_3 0x07 +#define DMA_CNT_4 0xC2 +#define DMA_CNT_5 0xC6 +#define DMA_CNT_6 0xCA +#define DMA_CNT_7 0xCE + +#define DMA_LO_PAGE_0 0x87 /* DMA page registers */ +#define DMA_LO_PAGE_1 0x83 +#define DMA_LO_PAGE_2 0x81 +#define DMA_LO_PAGE_3 0x82 +#define DMA_LO_PAGE_5 0x8B +#define DMA_LO_PAGE_6 0x89 +#define DMA_LO_PAGE_7 0x8A + +#define DMA_HI_PAGE_0 0x487 /* DMA page registers */ +#define DMA_HI_PAGE_1 0x483 +#define DMA_HI_PAGE_2 0x481 +#define DMA_HI_PAGE_3 0x482 +#define DMA_HI_PAGE_5 0x48B +#define DMA_HI_PAGE_6 0x489 +#define DMA_HI_PAGE_7 0x48A -#define DMA1_EXT_REG 0x40B -#define DMA2_EXT_REG 0x4D6 +#define DMA1_EXT_REG 0x40B +#define DMA2_EXT_REG 0x4D6 -#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */ -#define DMA_AUTOINIT 0x10 +#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */ +#define DMA_AUTOINIT 0x10 -extern spinlock_t dma_spin_lock; +extern spinlock_t dma_spin_lock; static __inline__ unsigned long claim_dma_lock(void) { @@ -206,27 +197,23 @@ { unsigned char ucDmaCmd=0x00; - if (dmanr != 4) - { - dma_outb(0, DMA2_MASK_REG); /* This may not be enabled */ - dma_outb(ucDmaCmd, DMA2_CMD_REG); /* Enable group */ + if (dmanr != 4) { + dma_outb(0, DMA2_MASK_REG); /* This may not be enabled */ + dma_outb(ucDmaCmd, DMA2_CMD_REG); /* Enable group */ } - if (dmanr<=3) - { - dma_outb(dmanr, DMA1_MASK_REG); - dma_outb(ucDmaCmd, DMA1_CMD_REG); /* Enable group */ + if (dmanr <= 3) { + dma_outb(dmanr, DMA1_MASK_REG); + dma_outb(ucDmaCmd, DMA1_CMD_REG); /* Enable group */ } else - { - dma_outb(dmanr & 3, DMA2_MASK_REG); - } + dma_outb(dmanr & 3, DMA2_MASK_REG); } static __inline__ void disable_dma(unsigned int dmanr) { - if (dmanr<=3) - dma_outb(dmanr | 4, DMA1_MASK_REG); + if (dmanr <= 3) + dma_outb(dmanr | 4, DMA1_MASK_REG); else - dma_outb((dmanr & 3) | 4, DMA2_MASK_REG); + dma_outb((dmanr & 3) | 4, DMA2_MASK_REG); } /* Clear the 'DMA Pointer Flip Flop'. @@ -238,19 +225,19 @@ */ static __inline__ void clear_dma_ff(unsigned int dmanr) { - if (dmanr<=3) - dma_outb(0, DMA1_CLEAR_FF_REG); + if (dmanr <= 3) + dma_outb(0, DMA1_CLEAR_FF_REG); else - dma_outb(0, DMA2_CLEAR_FF_REG); + dma_outb(0, DMA2_CLEAR_FF_REG); } /* set mode (above) for a specific DMA channel */ static __inline__ void set_dma_mode(unsigned int dmanr, char mode) { - if (dmanr<=3) - dma_outb(mode | dmanr, DMA1_MODE_REG); + if (dmanr <= 3) + dma_outb(mode | dmanr, DMA1_MODE_REG); else - dma_outb(mode | (dmanr&3), DMA2_MODE_REG); + dma_outb(mode | (dmanr & 3), DMA2_MODE_REG); } /* Set only the page register bits of the transfer address. @@ -263,41 +250,41 @@ switch(dmanr) { case 0: dma_outb(pagenr, DMA_LO_PAGE_0); - dma_outb(pagenr>>8, DMA_HI_PAGE_0); + dma_outb(pagenr >> 8, DMA_HI_PAGE_0); break; case 1: dma_outb(pagenr, DMA_LO_PAGE_1); - dma_outb(pagenr>>8, DMA_HI_PAGE_1); + dma_outb(pagenr >> 8, DMA_HI_PAGE_1); break; case 2: dma_outb(pagenr, DMA_LO_PAGE_2); - dma_outb(pagenr>>8, DMA_HI_PAGE_2); + dma_outb(pagenr >> 8, DMA_HI_PAGE_2); break; case 3: dma_outb(pagenr, DMA_LO_PAGE_3); - dma_outb(pagenr>>8, DMA_HI_PAGE_3); + dma_outb(pagenr >> 8, DMA_HI_PAGE_3); break; - case 5: - if (SND_DMA1 == 5 || SND_DMA2 == 5) + case 5: + if (SND_DMA1 == 5 || SND_DMA2 == 5) dma_outb(pagenr, DMA_LO_PAGE_5); else dma_outb(pagenr & 0xfe, DMA_LO_PAGE_5); - dma_outb(pagenr>>8, DMA_HI_PAGE_5); + dma_outb(pagenr >> 8, DMA_HI_PAGE_5); break; case 6: - if (SND_DMA1 == 6 || SND_DMA2 == 6) + if (SND_DMA1 == 6 || SND_DMA2 == 6) dma_outb(pagenr, DMA_LO_PAGE_6); else dma_outb(pagenr & 0xfe, DMA_LO_PAGE_6); - dma_outb(pagenr>>8, DMA_HI_PAGE_6); + dma_outb(pagenr >> 8, DMA_HI_PAGE_6); break; case 7: if (SND_DMA1 == 7 || SND_DMA2 == 7) dma_outb(pagenr, DMA_LO_PAGE_7); else dma_outb(pagenr & 0xfe, DMA_LO_PAGE_7); - dma_outb(pagenr>>8, DMA_HI_PAGE_7); - break; + dma_outb(pagenr >> 8, DMA_HI_PAGE_7); + break; } } @@ -307,20 +294,19 @@ */ static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int phys) { - if (dmanr <= 3) { - dma_outb( phys & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE ); - dma_outb( (phys>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE ); - } else { - if (dmanr == SND_DMA1 || dmanr == SND_DMA2) { - dma_outb( phys & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE ); - dma_outb( (phys>>8) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE ); - dma_outb( (dmanr&3), DMA2_EXT_REG); - } else { - dma_outb( (phys>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE ); - dma_outb( (phys>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE ); - } + if (dmanr <= 3) { + dma_outb(phys & 0xff, ((dmanr & 3) << 1) + IO_DMA1_BASE ); + dma_outb((phys >> 8) & 0xff, ((dmanr & 3) << 1) + IO_DMA1_BASE); + } else if (dmanr == SND_DMA1 || dmanr == SND_DMA2) { + dma_outb(phys & 0xff, ((dmanr & 3) << 2) + IO_DMA2_BASE ); + dma_outb((phys >> 8) & 0xff, ((dmanr & 3) << 2) + + IO_DMA2_BASE); + dma_outb((dmanr & 3), DMA2_EXT_REG); + } else { + dma_outb((phys >> 1) & 0xff, ((dmanr & 3) << 2) + IO_DMA2_BASE); + dma_outb((phys >> 9) & 0xff, ((dmanr & 3) << 2) + IO_DMA2_BASE); } - set_dma_page(dmanr, phys>>16); + set_dma_page(dmanr, phys >> 16); } @@ -334,22 +320,23 @@ */ static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count) { - count--; - if (dmanr <= 3) { - dma_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE ); - dma_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE ); - } else { - if (dmanr == SND_DMA1 || dmanr == SND_DMA2) { - dma_outb( count & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE ); - dma_outb( (count>>8) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE ); - } else { - dma_outb( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE ); - dma_outb( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE ); - } - } + count--; + if (dmanr <= 3) { + dma_outb(count & 0xff, ((dmanr & 3) << 1) + 1 + IO_DMA1_BASE); + dma_outb((count >> 8) & 0xff, ((dmanr & 3) << 1) + 1 + + IO_DMA1_BASE); + } else if (dmanr == SND_DMA1 || dmanr == SND_DMA2) { + dma_outb( count & 0xff, ((dmanr & 3) << 2) + 2 + IO_DMA2_BASE); + dma_outb( (count >> 8) & 0xff, ((dmanr & 3) << 2) + 2 + + IO_DMA2_BASE); + } else { + dma_outb((count >> 1) & 0xff, ((dmanr & 3) << 2) + 2 + + IO_DMA2_BASE); + dma_outb((count >> 9) & 0xff, ((dmanr & 3) << 2) + 2 + + IO_DMA2_BASE); + } } - /* Get DMA residue count. After a DMA transfer, this * should return zero. Reading this while a DMA transfer is * still in progress will return unpredictable results. @@ -360,27 +347,32 @@ */ static __inline__ int get_dma_residue(unsigned int dmanr) { - unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE - : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE; + unsigned int io_port = (dmanr <= 3) ? + ((dmanr & 3) << 1) + 1 + IO_DMA1_BASE + : ((dmanr & 3) << 2) + 2 + IO_DMA2_BASE; /* using short to get 16-bit wrap around */ unsigned short count; count = 1 + dma_inb(io_port); count += dma_inb(io_port) << 8; - + return (dmanr <= 3 || dmanr == SND_DMA1 || dmanr == SND_DMA2) - ? count : (count<<1); + ? count : (count<<1); + } /* These are in kernel/dma.c: */ -extern int request_dma(unsigned int dmanr, const char * device_id); /* reserve a DMA channel */ -extern void free_dma(unsigned int dmanr); /* release it again */ + +/* reserve a DMA channel */ +extern int request_dma(unsigned int dmanr, const char * device_id); +/* release it again */ +extern void free_dma(unsigned int dmanr); #ifdef CONFIG_PCI -extern int isa_dma_bridge_buggy; -#else -#define isa_dma_bridge_buggy (0) +extern int isa_dma_bridge_buggy; +#else +#define isa_dma_bridge_buggy (0) #endif #endif /* _ASM_DMA_H */ #endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/est8260.h b/include/asm-ppc/est8260.h --- a/include/asm-ppc/est8260.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,31 +0,0 @@ -/* - * BK Id: SCCS/s.est8260.h 1.5 05/17/01 18:14:24 cort - */ - -/* Board information for the EST8260, which should be generic for - * all 8260 boards. The IMMR is now given to us so the hard define - * will soon be removed. All of the clock values are computed from - * the configuration SCMR and the Power-On-Reset word. - */ - -#define IMAP_ADDR ((uint)0xf0000000) - - -/* A Board Information structure that is given to a program when - * prom starts it up. - */ -typedef struct bd_info { - unsigned int bi_memstart; /* Memory start address */ - unsigned int bi_memsize; /* Memory (end) size in bytes */ - unsigned int bi_intfreq; /* Internal Freq, in Hz */ - unsigned int bi_busfreq; /* Bus Freq, in MHz */ - unsigned int bi_cpmfreq; /* CPM Freq, in MHz */ - unsigned int bi_brgfreq; /* BRG Freq, in MHz */ - unsigned int bi_vco; /* VCO Out from PLL */ - unsigned int bi_baudrate; /* Default console baud rate */ - unsigned int bi_immr; /* IMMR when called from boot rom */ - unsigned char bi_enetaddr[6]; -} bd_t; - -extern bd_t m8xx_board_info; - diff -Nru a/include/asm-ppc/fads.h b/include/asm-ppc/fads.h --- a/include/asm-ppc/fads.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,61 +0,0 @@ -/* - * BK Id: SCCS/s.fads.h 1.14 10/26/01 10:14:09 trini - */ - -/* - * A collection of structures, addresses, and values associated with - * the Motorola 860T FADS board. Copied from the MBX stuff. - * - * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) - */ -#ifdef __KERNEL__ -#ifndef __ASM_FADS_H__ -#define __ASM_FADS_H__ - -#include - -#include - -/* Memory map is configured by the PROM startup. - * I tried to follow the FADS manual, although the startup PROM - * dictates this and we simply have to move some of the physical - * addresses for Linux. - */ -#define BCSR_ADDR ((uint)0xff010000) -#define BCSR_SIZE ((uint)(64 * 1024)) -#define BCSR0 ((uint)0xff010000) -#define BCSR1 ((uint)0xff010004) -#define BCSR2 ((uint)0xff010008) -#define BCSR3 ((uint)0xff01000c) -#define BCSR4 ((uint)0xff010010) - -#define IMAP_ADDR ((uint)0xff000000) -#define IMAP_SIZE ((uint)(64 * 1024)) - -#define PCMCIA_MEM_ADDR ((uint)0xff020000) -#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) - -/* Bits of interest in the BCSRs. - */ -#define BCSR1_ETHEN ((uint)0x20000000) -#define BCSR1_RS232EN_1 ((uint)0x01000000) -#define BCSR1_RS232EN_2 ((uint)0x00040000) -#define BCSR4_ETHLOOP ((uint)0x80000000) /* EEST Loopback */ -#define BCSR4_EEFDX ((uint)0x40000000) /* EEST FDX enable */ -#define BCSR4_FETH_EN ((uint)0x08000000) /* PHY enable */ -#define BCSR4_FETHCFG0 ((uint)0x04000000) /* PHY autoneg mode */ -#define BCSR4_FETHCFG1 ((uint)0x00400000) /* PHY autoneg mode */ -#define BCSR4_FETHFDE ((uint)0x02000000) /* PHY FDX advertise */ -#define BCSR4_FETHRST ((uint)0x00200000) /* PHY Reset */ - -/* Interrupt level assignments. - */ -#define FEC_INTERRUPT SIU_LEVEL1 /* FEC interrupt */ -#define PHY_INTERRUPT SIU_IRQ2 /* PHY link change interrupt */ - -/* We don't use the 8259. - */ -#define NR_8259_INTS 0 - -#endif /* __ASM_FADS_H__ */ -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/feature.h b/include/asm-ppc/feature.h --- a/include/asm-ppc/feature.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,111 +0,0 @@ -/* - * BK Id: SCCS/s.feature.h 1.13 08/19/01 22:23:04 paulus - */ -/* - * Definitions for accessing the Feature Control Register (FCR) - * on Power Macintoshes and similar machines. The FCR lets us - * enable/disable, reset, and power up/down various peripherals. - * - * 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 archive - * for more details. - * - * Copyright (C) 1998 Paul Mackerras & - * Ben. Herrenschmidt. - * - * - */ -#ifdef __KERNEL__ -#ifndef __ASM_PPC_FEATURE_H -#define __ASM_PPC_FEATURE_H - -/* - * The FCR selector for particular features vary somewhat between - * different machines. So we abstract a list of features here - * and let the feature_* routines map them to the actual bits. - */ -enum system_feature { - FEATURE_null, - FEATURE_Serial_reset, - FEATURE_Serial_enable, - FEATURE_Serial_IO_A, - FEATURE_Serial_IO_B, - FEATURE_SWIM3_enable, - FEATURE_MESH_enable, - FEATURE_IDE0_enable, /* Internal IDE */ - FEATURE_IDE0_reset, /* Internal IDE */ - FEATURE_IOBUS_enable, /* Internal IDE */ - FEATURE_Mediabay_reset, - FEATURE_Mediabay_power, - FEATURE_Mediabay_PCI_enable, - FEATURE_IDE1_enable, /* MediaBay IDE */ - FEATURE_IDE1_reset, /* MediaBay IDE */ - FEATURE_Mediabay_floppy_enable, - FEATURE_BMac_reset, - FEATURE_BMac_IO_enable, - FEATURE_Modem_power, - FEATURE_Slow_SCC_PCLK, - FEATURE_Sound_power, - FEATURE_Sound_CLK_enable, - FEATURE_IDE2_enable, - FEATURE_IDE2_reset, - FEATURE_Mediabay_IDE_switch, /* MB IDE bus switch */ - FEATURE_Mediabay_content, /* MB content indicator enable */ - FEATURE_Airport_reset, /* Is it actually a reset ? */ - FEATURE_last, -}; - -/* Note about the device parameter: Each device gives it's own entry. If NULL, - the feature function will just do nothing and return -EINVAL. - The feature management will walk up the device tree until in reaches a recognized - chip for which features can be changed and it will then apply the necessary - features to that chip. If it's not found, -ENODEV is returned. - Note also that feature_test/set/clear are interrupt-safe provided that they are - called _after_ feature_init() is completed. - */ - -/* Test whether a particular feature is enabled. May return -ENODEV*/ -extern int feature_test(struct device_node* device, enum system_feature f); - -/* Set a particular feature. Returns 0 or -ENODEV */ -extern int feature_set(struct device_node* device, enum system_feature f); - -/* Clear a particular feature */ -extern int feature_clear(struct device_node* device, enum system_feature f); - -/* Initialize feature stuff */ -extern void feature_init(void); - - -/* - * Additional functions related to Core99 machines. We should extend the - * feature mecanism to make those fit into it. For now, they are still - * separate functions. - */ -extern void feature_set_gmac_power(struct device_node* device, int power); - - /* use constants in KeyLargo.h for the reset parameter */ -extern void feature_gmac_phy_reset(struct device_node* device); - -extern void feature_set_usb_power(struct device_node* device, int power); - -extern void feature_set_firewire_power(struct device_node* device, int power); -extern void feature_set_firewire_cable_power(struct device_node* device, int power); - -extern void feature_set_modem_power(struct device_node* device, int power); - -extern void feature_set_airport_power(struct device_node* device, int power); - -extern void feature_core99_kick_cpu(int cpu_nr); - -/* - * Sleep related functions. At term, they should be high-priority notifiers, - * but this would require some changes to the current sleep scheme that won't - * be done in 2.4. - */ -extern void feature_prepare_for_sleep(void); -extern void feature_wake_up(void); -extern int feature_can_sleep(void); - -#endif /* __ASM_PPC_FEATURE_H */ -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/gemini.h b/include/asm-ppc/gemini.h --- a/include/asm-ppc/gemini.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,171 +0,0 @@ -/* - * BK Id: SCCS/s.gemini.h 1.5 05/17/01 18:14:24 cort - */ -/* - * include/asm-ppc/gemini.h - * - * - * Onboard registers and descriptions for Synergy Microsystems' - * "Gemini" boards. - * - */ -#ifdef __KERNEL__ -#ifndef __PPC_GEMINI_H -#define __PPC_GEMINI_H - -/* Registers */ - -#define GEMINI_SERIAL_B (0xffeffb00) -#define GEMINI_SERIAL_A (0xffeffb08) -#define GEMINI_USWITCH (0xffeffd00) -#define GEMINI_BREV (0xffeffe00) -#define GEMINI_BECO (0xffeffe08) -#define GEMINI_FEAT (0xffeffe10) -#define GEMINI_BSTAT (0xffeffe18) -#define GEMINI_CPUSTAT (0xffeffe20) -#define GEMINI_L2CFG (0xffeffe30) -#define GEMINI_MEMCFG (0xffeffe38) -#define GEMINI_FLROM (0xffeffe40) -#define GEMINI_P0PCI (0xffeffe48) -#define GEMINI_FLWIN (0xffeffe50) -#define GEMINI_P0INTMASK (0xffeffe60) -#define GEMINI_P0INTAP (0xffeffe68) -#define GEMINI_PCIERR (0xffeffe70) -#define GEMINI_LEDBASE (0xffeffe80) -#define GEMINI_RTC (0xffe9fff8) -#define GEMINI_LEDS 8 -#define GEMINI_SWITCHES 8 - - -/* Flash ROM bit definitions */ -#define GEMINI_FLS_WEN (1<<0) -#define GEMINI_FLS_JMP (1<<6) -#define GEMINI_FLS_BOOT (1<<7) - -/* Memory bit definitions */ -#define GEMINI_MEM_TYPE_MASK 0xc0 -#define GEMINI_MEM_SIZE_MASK 0x38 -#define GEMINI_MEM_BANK_MASK 0x07 - -/* L2 cache bit definitions */ -#define GEMINI_L2_SIZE_MASK 0xc0 -#define GEMINI_L2_RATIO_MASK 0x03 - -/* Timebase register bit definitons */ -#define GEMINI_TIMEB0_EN (1<<0) -#define GEMINI_TIMEB1_EN (1<<1) -#define GEMINI_TIMEB2_EN (1<<2) -#define GEMINI_TIMEB3_EN (1<<3) - -/* CPU status bit definitions */ -#define GEMINI_CPU_ID_MASK 0x03 -#define GEMINI_CPU_COUNT_MASK 0x0c -#define GEMINI_CPU0_HALTED (1<<4) -#define GEMINI_CPU1_HALTED (1<<5) -#define GEMINI_CPU2_HALTED (1<<6) -#define GEMINI_CPU3_HALTED (1<<7) - -/* Board status bit definitions */ -#define GEMINI_BRD_FAIL (1<<0) /* FAIL led is lit */ -#define GEMINI_BRD_BUS_MASK 0x0c /* PowerPC bus speed */ - -/* Board family/feature bit descriptions */ -#define GEMINI_FEAT_HAS_FLASH (1<<0) -#define GEMINI_FEAT_HAS_ETH (1<<1) -#define GEMINI_FEAT_HAS_SCSI (1<<2) -#define GEMINI_FEAT_HAS_P0 (1<<3) -#define GEMINI_FEAT_FAM_MASK 0xf0 - -/* Mod/ECO bit definitions */ -#define GEMINI_ECO_LEVEL_MASK 0x0f -#define GEMINI_MOD_MASK 0xf0 - -/* Type/revision bit definitions */ -#define GEMINI_REV_MASK 0x0f -#define GEMINI_TYPE_MASK 0xf0 - -/* User switch definitions */ -#define GEMINI_SWITCH_VERBOSE 1 /* adds "debug" to boot cmd line */ -#define GEMINI_SWITCH_SINGLE_USER 7 /* boots into "single-user" mode */ - -#define SGS_RTC_CONTROL 0 -#define SGS_RTC_SECONDS 1 -#define SGS_RTC_MINUTES 2 -#define SGS_RTC_HOURS 3 -#define SGS_RTC_DAY 4 -#define SGS_RTC_DAY_OF_MONTH 5 -#define SGS_RTC_MONTH 6 -#define SGS_RTC_YEAR 7 - -#define SGS_RTC_SET 0x80 -#define SGS_RTC_IS_STOPPED 0x80 - -#define GRACKLE_CONFIG_ADDR_ADDR (0xfec00000) -#define GRACKLE_CONFIG_DATA_ADDR (0xfee00000) - -#define GEMINI_BOOT_INIT (0xfff00100) - -#ifndef __ASSEMBLY__ - -static inline void grackle_write( unsigned long addr, unsigned long data ) -{ - __asm__ __volatile__( - " stwbrx %1, 0, %0\n \ - sync\n \ - stwbrx %3, 0, %2\n \ - sync " - : /* no output */ - : "r" (GRACKLE_CONFIG_ADDR_ADDR), "r" (addr), - "r" (GRACKLE_CONFIG_DATA_ADDR), "r" (data)); -} - -static inline unsigned long grackle_read( unsigned long addr ) -{ - unsigned long val; - - __asm__ __volatile__( - " stwbrx %1, 0, %2\n \ - sync\n \ - lwbrx %0, 0, %3\n \ - sync " - : "=r" (val) - : "r" (addr), "r" (GRACKLE_CONFIG_ADDR_ADDR), - "r" (GRACKLE_CONFIG_DATA_ADDR)); - - return val; -} - -static inline void gemini_led_on( int led ) -{ - if (led >= 0 && led < GEMINI_LEDS) - *(unsigned char *)(GEMINI_LEDBASE + (led<<3)) = 1; -} - -static inline void gemini_led_off(int led) -{ - if (led >= 0 && led < GEMINI_LEDS) - *(unsigned char *)(GEMINI_LEDBASE + (led<<3)) = 0; -} - -static inline int gemini_led_val(int led) -{ - int val = 0; - if (led >= 0 && led < GEMINI_LEDS) - val = *(unsigned char *)(GEMINI_LEDBASE + (led<<3)); - return (val & 0x1); -} - -/* returns processor id from the board */ -static inline int gemini_processor(void) -{ - unsigned char cpu = *(unsigned char *)(GEMINI_CPUSTAT); - return (int) ((cpu == 0) ? 4 : (cpu & GEMINI_CPU_ID_MASK)); -} - - -extern void _gemini_reboot(void); -extern void gemini_prom_init(void); -extern void gemini_init_l2(void); -#endif /* __ASSEMBLY__ */ -#endif -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/gemini_serial.h b/include/asm-ppc/gemini_serial.h --- a/include/asm-ppc/gemini_serial.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,44 +0,0 @@ -/* - * BK Id: SCCS/s.gemini_serial.h 1.5 05/17/01 18:14:24 cort - */ -#ifdef __KERNEL__ -#ifndef __ASMPPC_GEMINI_SERIAL_H -#define __ASMPPC_GEMINI_SERIAL_H - -#include -#include - -#ifdef CONFIG_SERIAL_MANY_PORTS -#define RS_TABLE_SIZE 64 -#else -#define RS_TABLE_SIZE 4 -#endif - -/* Rate for the 24.576 Mhz clock for the onboard serial chip */ -#define BASE_BAUD (24576000 / 16) - -#ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) -#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ) -#else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) -#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF) -#endif - -#define STD_SERIAL_PORT_DEFNS \ - { 0, BASE_BAUD, GEMINI_SERIAL_A, 15, STD_COM_FLAGS }, /* ttyS0 */ \ - { 0, BASE_BAUD, GEMINI_SERIAL_B, 14, STD_COM_FLAGS }, /* ttyS1 */ \ - -#ifdef CONFIG_GEMINI_PU32 -#define PU32_SERIAL_PORT_DEFNS \ - { 0, BASE_BAUD, NULL, 0, STD_COM_FLAGS }, -#else -#define PU32_SERIAL_PORT_DEFNS -#endif - -#define SERIAL_PORT_DFNS \ - STD_SERIAL_PORT_DEFNS \ - PU32_SERIAL_PORT_DEFNS - -#endif -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/gg2.h b/include/asm-ppc/gg2.h --- a/include/asm-ppc/gg2.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/gg2.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.gg2.h 1.5 05/17/01 18:14:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * asm-ppc/gg2.h -- VLSI VAS96011/12 `Golden Gate 2' register definitions @@ -36,6 +36,8 @@ /* * GG2 specific PCI Registers */ + +extern unsigned long gg2_pci_config_base; /* kernel virtual address */ #define GG2_PCI_BUSNO 0x40 /* Bus number */ #define GG2_PCI_SUBBUSNO 0x41 /* Subordinate bus number */ diff -Nru a/include/asm-ppc/gt64260.h b/include/asm-ppc/gt64260.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/gt64260.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,324 @@ +/* + * include/asm-ppc/gt64260.h + * + * Prototypes, etc. for the Marvell/Galileo GT64260 host bridge routines. + * + * Author: Mark A. Greer + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef __ASMPPC_GT64260_H +#define __ASMPPC_GT64260_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +extern u32 gt64260_base; +extern u32 gt64260_irq_base; /* We handle the next 96 IRQs from here */ +extern u32 gt64260_revision; +extern u8 gt64260_pci_exclude_bridge; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* IRQs defined by the 64260 */ +#define GT64260_IRQ_MPSC0 40 +#define GT64260_IRQ_MPSC1 42 +#define GT64260_IRQ_SDMA 36 + +/* + * Define a default physical memory map to be set up on the bridge. + * Also define a struct to pass that info from board-specific routines to + * GT64260 generic set up routines. By passing this info in, the board + * support developer can modify it at will. + */ + +/* + * This is the default memory map: + * CPU PCI + * --- --- + * PCI 0 I/O: 0xfa000000-0xfaffffff 0x00000000-0x00ffffff + * PCI 1 I/O: 0xfb000000-0xfbffffff 0x01000000-0x01ffffff + * PCI 0 MEM: 0x80000000-0x8fffffff 0x80000000-0x8fffffff + * PCI 1 MEM: 0x90000000-0x9fffffff 0x90000000-0x9fffffff + */ + +/* Default physical memory map for the GT64260 bridge */ + +/* + * PCI Bus 0 Definitions + */ +#define GT64260_PCI_0_IO_SIZE 0x01000000U +#define GT64260_PCI_0_MEM_SIZE 0x10000000U + +/* Processor Physical addresses */ +#define GT64260_PCI_0_IO_START_PROC 0xfa000000U +#define GT64260_PCI_0_IO_END_PROC (GT64260_PCI_0_IO_START_PROC + \ + GT64260_PCI_0_IO_SIZE - 1) + +/* PCI 0 addresses */ +#define GT64260_PCI_0_IO_START 0x00000000U +#define GT64260_PCI_0_IO_END (GT64260_PCI_0_IO_START + \ + GT64260_PCI_0_IO_SIZE - 1) + +/* Processor Physical addresses */ +#define GT64260_PCI_0_MEM_START_PROC 0x80000000U +#define GT64260_PCI_0_MEM_END_PROC (GT64260_PCI_0_MEM_START_PROC + \ + GT64260_PCI_0_MEM_SIZE - 1) + +/* PCI 0 addresses */ +#define GT64260_PCI_0_MEM_START 0x80000000U +#define GT64260_PCI_0_MEM_END (GT64260_PCI_0_MEM_START + \ + GT64260_PCI_0_MEM_SIZE - 1) + +/* + * PCI Bus 1 Definitions + */ +#define GT64260_PCI_1_IO_SIZE 0x01000000U +#define GT64260_PCI_1_MEM_SIZE 0x10000000U + +/* PCI 1 addresses */ +#define GT64260_PCI_1_IO_START 0x01000000U +#define GT64260_PCI_1_IO_END (GT64260_PCI_1_IO_START + \ + GT64260_PCI_1_IO_SIZE - 1) + +/* Processor Physical addresses */ +#define GT64260_PCI_1_IO_START_PROC 0xfb000000U +#define GT64260_PCI_1_IO_END_PROC (GT64260_PCI_1_IO_START_PROC + \ + GT64260_PCI_1_IO_SIZE - 1) + +/* PCI 1 addresses */ +#define GT64260_PCI_1_MEM_START 0x90000000U +#define GT64260_PCI_1_MEM_END (GT64260_PCI_1_MEM_START + \ + GT64260_PCI_1_MEM_SIZE - 1) + +/* Processor Physical addresses */ +#define GT64260_PCI_1_MEM_START_PROC 0x90000000U +#define GT64260_PCI_1_MEM_END_PROC (GT64260_PCI_1_MEM_START_PROC + \ + GT64260_PCI_1_MEM_SIZE - 1) + +/* Define struct to pass mem-map info into gt64260_common.c code */ +typedef struct { + struct pci_controller *hose_a; + struct pci_controller *hose_b; + + u32 mem_size; + + u32 pci_0_io_start_proc; + u32 pci_0_io_start_pci; + u32 pci_0_io_size; + u32 pci_0_io_swap; + + u32 pci_0_mem_start_proc; + u32 pci_0_mem_start_pci_hi; + u32 pci_0_mem_start_pci_lo; + u32 pci_0_mem_size; + u32 pci_0_mem_swap; + + u32 pci_1_io_start_proc; + u32 pci_1_io_start_pci; + u32 pci_1_io_size; + u32 pci_1_io_swap; + + u32 pci_1_mem_start_proc; + u32 pci_1_mem_start_pci_hi; + u32 pci_1_mem_start_pci_lo; + u32 pci_1_mem_size; + u32 pci_1_mem_swap; +} gt64260_bridge_info_t; + +#define GT64260_BRIDGE_INFO_DEFAULT(ip, ms) { \ + (ip)->mem_size = (ms); \ + \ + (ip)->pci_0_io_start_proc = GT64260_PCI_0_IO_START_PROC; \ + (ip)->pci_0_io_start_pci = GT64260_PCI_0_IO_START; \ + (ip)->pci_0_io_size = GT64260_PCI_0_IO_SIZE; \ + (ip)->pci_0_io_swap = GT64260_CPU_PCI_SWAP_NONE; \ + \ + (ip)->pci_0_mem_start_proc = GT64260_PCI_0_MEM_START_PROC; \ + (ip)->pci_0_mem_start_pci_hi = 0x00000000; \ + (ip)->pci_0_mem_start_pci_lo = GT64260_PCI_0_MEM_START; \ + (ip)->pci_0_mem_size = GT64260_PCI_0_MEM_SIZE; \ + (ip)->pci_0_mem_swap = GT64260_CPU_PCI_SWAP_NONE; \ + \ + (ip)->pci_1_io_start_proc = GT64260_PCI_1_IO_START_PROC; \ + (ip)->pci_1_io_start_pci = GT64260_PCI_1_IO_START; \ + (ip)->pci_1_io_size = GT64260_PCI_1_IO_SIZE; \ + (ip)->pci_1_io_swap = GT64260_CPU_PCI_SWAP_NONE; \ + \ + (ip)->pci_1_mem_start_proc = GT64260_PCI_1_MEM_START_PROC; \ + (ip)->pci_1_mem_start_pci_hi = 0x00000000; \ + (ip)->pci_1_mem_start_pci_lo = GT64260_PCI_1_MEM_START; \ + (ip)->pci_1_mem_size = GT64260_PCI_1_MEM_SIZE; \ + (ip)->pci_1_mem_swap = GT64260_CPU_PCI_SWAP_NONE; \ +} + +/* + ***************************************************************************** + * + * I/O macros to access the 64260's registers + * + ***************************************************************************** + */ + +extern inline uint32_t gt_read(uint32_t offs){ + return (in_le32((volatile uint *)(gt64260_base + offs))); +} +extern inline void gt_write(uint32_t offs, uint32_t d){ + out_le32((volatile uint *)(gt64260_base + offs), d); +} + +#if 0 /* paranoid SMP version */ +extern inline void gt_modify(u32 offs, u32 data, u32 mask) \ +{ + uint32_t reg; + spin_lock(>64260_lock); + reg = gt_read(offs) & (~mask); /* zero any bits we care about*/ + reg |= data & mask; /* set bits from the data */ + gt_write(offs, reg); + spin_unlock(>64260_lock); +} +#else +extern inline void gt_modify(uint32_t offs, uint32_t data, uint32_t mask) +{ + uint32_t reg; + reg = gt_read(offs) & (~(mask)); /* zero any bits we care about*/ + reg |= (data) & (mask); /* set bits from the data */ + gt_write(offs, reg); +} +#endif +#define gt_set_bits(offs, bits) gt_modify(offs, ~0, bits) + +#define gt_clr_bits(offs, bits) gt_modify(offs, 0, bits) + + +/* + ***************************************************************************** + * + * Function Prototypes + * + ***************************************************************************** + */ + +int gt64260_find_bridges(u32 phys_base_addr, gt64260_bridge_info_t *info, + int ((*map_irq)(struct pci_dev *, unsigned char, unsigned char))); +int gt64260_bridge_init(gt64260_bridge_info_t *info); +int gt64260_cpu_scs_set_window(u32 window, + u32 base_addr, + u32 size); +int gt64260_cpu_cs_set_window(u32 window, + u32 base_addr, + u32 size); +int gt64260_cpu_boot_set_window(u32 base_addr, + u32 size); +int gt64260_cpu_set_pci_io_window(u32 pci_bus, + u32 cpu_base_addr, + u32 pci_base_addr, + u32 size, + u32 swap); +int gt64260_cpu_set_pci_mem_window(u32 pci_bus, + u32 window, + u32 cpu_base_addr, + u32 pci_base_addr_hi, + u32 pci_base_addr_lo, + u32 size, + u32 swap_64bit); +int gt64260_cpu_prot_set_window(u32 window, + u32 base_addr, + u32 size, + u32 access_bits); +int gt64260_cpu_snoop_set_window(u32 window, + u32 base_addr, + u32 size, + u32 snoop_type); +void gt64260_cpu_disable_all_windows(void); +int gt64260_pci_bar_enable(u32 pci_bus, u32 enable_bits); +int gt64260_pci_slave_scs_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr, + u32 cpu_base_addr, + u32 size); +int gt64260_pci_slave_cs_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr, + u32 cpu_base_addr, + u32 size); +int gt64260_pci_slave_boot_set_window(struct pci_controller *hose, + u32 pci_base_addr, + u32 cpu_base_addr, + u32 size); +int gt64260_pci_slave_p2p_mem_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr, + u32 other_bus_base_addr, + u32 size); +int gt64260_pci_slave_p2p_io_set_window(struct pci_controller *hose, + u32 pci_base_addr, + u32 other_bus_base_addr, + u32 size); +int gt64260_pci_slave_dac_scs_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr_hi, + u32 pci_base_addr_lo, + u32 cpu_base_addr, + u32 size); +int gt64260_pci_slave_dac_cs_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr_hi, + u32 pci_base_addr_lo, + u32 cpu_base_addr, + u32 size); +int gt64260_pci_slave_dac_boot_set_window(struct pci_controller *hose, + u32 pci_base_addr_hi, + u32 pci_base_addr_lo, + u32 cpu_base_addr, + u32 size); +int gt64260_pci_slave_dac_p2p_mem_set_window(struct pci_controller *hose, + u32 window, + u32 pci_base_addr_hi, + u32 pci_base_addr_lo, + u32 other_bus_base_addr, + u32 size); +int gt64260_pci_acc_cntl_set_window(u32 pci_bus, + u32 window, + u32 base_addr_hi, + u32 base_addr_lo, + u32 size, + u32 features); +int gt64260_pci_snoop_set_window(u32 pci_bus, + u32 window, + u32 base_addr_hi, + u32 base_addr_lo, + u32 size, + u32 snoop_type); +int gt64260_set_base(u32 new_base); +int gt64260_get_base(u32 *base); +int gt64260_pci_exclude_device(u8 bus, u8 devfn); + +void gt64260_init_irq(void); +int gt64260_get_irq(struct pt_regs *regs); + +void gt64260_mpsc_progress(char *s, unsigned short hex); + +#endif /* __ASMPPC_GT64260_H */ diff -Nru a/include/asm-ppc/gt64260_defs.h b/include/asm-ppc/gt64260_defs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/gt64260_defs.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1012 @@ +/* + * include/asm-ppc/gt64260_defs.h + * + * Register definitions for the Marvell/Galileo GT64260 host bridge. + * + * Author: Mark A. Greer + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef __ASMPPC_GT64260_DEFS_H +#define __ASMPPC_GT64260_DEFS_H + +/* + * Define a macro to represent the supported version of the 64260. + */ +#define GT64260 0x01 +#define GT64260A 0x10 + +/* + ***************************************************************************** + * + * CPU Interface Registers + * + ***************************************************************************** + */ + +/* CPU physical address of 64260's registers */ +#define GT64260_INTERNAL_SPACE_DECODE 0x0068 +#define GT64260_INTERNAL_SPACE_SIZE 0x10000 +#define GT64260_INTERNAL_SPACE_DEFAULT_ADDR 0x14000000 + +/* CPU Memory Controller Window Registers (4 windows) */ +#define GT64260_CPU_SCS_DECODE_WINDOWS 4 + +#define GT64260_CPU_SCS_DECODE_0_BOT 0x0008 +#define GT64260_CPU_SCS_DECODE_0_TOP 0x0010 +#define GT64260_CPU_SCS_DECODE_1_BOT 0x0208 +#define GT64260_CPU_SCS_DECODE_1_TOP 0x0210 +#define GT64260_CPU_SCS_DECODE_2_BOT 0x0018 +#define GT64260_CPU_SCS_DECODE_2_TOP 0x0020 +#define GT64260_CPU_SCS_DECODE_3_BOT 0x0218 +#define GT64260_CPU_SCS_DECODE_3_TOP 0x0220 + +/* CPU Device Controller Window Registers (4 windows) */ +#define GT64260_CPU_CS_DECODE_WINDOWS 4 + +#define GT64260_CPU_CS_DECODE_0_BOT 0x0028 +#define GT64260_CPU_CS_DECODE_0_TOP 0x0030 +#define GT64260_CPU_CS_DECODE_1_BOT 0x0228 +#define GT64260_CPU_CS_DECODE_1_TOP 0x0230 +#define GT64260_CPU_CS_DECODE_2_BOT 0x0248 +#define GT64260_CPU_CS_DECODE_2_TOP 0x0250 +#define GT64260_CPU_CS_DECODE_3_BOT 0x0038 +#define GT64260_CPU_CS_DECODE_3_TOP 0x0040 + +#define GT64260_CPU_BOOT_CS_DECODE_0_BOT 0x0238 +#define GT64260_CPU_BOOT_CS_DECODE_0_TOP 0x0240 + +/* CPU Windows to PCI space (2 PCI buses each w/ 1 I/O & 4 MEM windows) */ +#define GT64260_PCI_BUSES 2 +#define GT64260_PCI_IO_WINDOWS_PER_BUS 1 +#define GT64260_PCI_MEM_WINDOWS_PER_BUS 4 + +#define GT64260_CPU_PCI_SWAP_BYTE 0x00000000 +#define GT64260_CPU_PCI_SWAP_NONE 0x01000000 +#define GT64260_CPU_PCI_SWAP_BYTE_WORD 0x02000000 +#define GT64260_CPU_PCI_SWAP_WORD 0x03000000 +#define GT64260_CPU_PCI_SWAP_MASK 0x07000000 + +#define GT64260_CPU_PCI_MEM_REQ64 (1<<27) + +#define GT64260_CPU_PCI_0_IO_DECODE_BOT 0x0048 +#define GT64260_CPU_PCI_0_IO_DECODE_TOP 0x0050 +#define GT64260_CPU_PCI_0_MEM_0_DECODE_BOT 0x0058 +#define GT64260_CPU_PCI_0_MEM_0_DECODE_TOP 0x0060 +#define GT64260_CPU_PCI_0_MEM_1_DECODE_BOT 0x0080 +#define GT64260_CPU_PCI_0_MEM_1_DECODE_TOP 0x0088 +#define GT64260_CPU_PCI_0_MEM_2_DECODE_BOT 0x0258 +#define GT64260_CPU_PCI_0_MEM_2_DECODE_TOP 0x0260 +#define GT64260_CPU_PCI_0_MEM_3_DECODE_BOT 0x0280 +#define GT64260_CPU_PCI_0_MEM_3_DECODE_TOP 0x0288 + +#define GT64260_CPU_PCI_0_IO_REMAP 0x00f0 +#define GT64260_CPU_PCI_0_MEM_0_REMAP_LO 0x00f8 +#define GT64260_CPU_PCI_0_MEM_0_REMAP_HI 0x0320 +#define GT64260_CPU_PCI_0_MEM_1_REMAP_LO 0x0100 +#define GT64260_CPU_PCI_0_MEM_1_REMAP_HI 0x0328 +#define GT64260_CPU_PCI_0_MEM_2_REMAP_LO 0x02f8 +#define GT64260_CPU_PCI_0_MEM_2_REMAP_HI 0x0330 +#define GT64260_CPU_PCI_0_MEM_3_REMAP_LO 0x0300 +#define GT64260_CPU_PCI_0_MEM_3_REMAP_HI 0x0338 + +#define GT64260_CPU_PCI_1_IO_DECODE_BOT 0x0090 +#define GT64260_CPU_PCI_1_IO_DECODE_TOP 0x0098 +#define GT64260_CPU_PCI_1_MEM_0_DECODE_BOT 0x00a0 +#define GT64260_CPU_PCI_1_MEM_0_DECODE_TOP 0x00a8 +#define GT64260_CPU_PCI_1_MEM_1_DECODE_BOT 0x00b0 +#define GT64260_CPU_PCI_1_MEM_1_DECODE_TOP 0x00b8 +#define GT64260_CPU_PCI_1_MEM_2_DECODE_BOT 0x02a0 +#define GT64260_CPU_PCI_1_MEM_2_DECODE_TOP 0x02a8 +#define GT64260_CPU_PCI_1_MEM_3_DECODE_BOT 0x02b0 +#define GT64260_CPU_PCI_1_MEM_3_DECODE_TOP 0x02b8 + +#define GT64260_CPU_PCI_1_IO_REMAP 0x0108 +#define GT64260_CPU_PCI_1_MEM_0_REMAP_LO 0x0110 +#define GT64260_CPU_PCI_1_MEM_0_REMAP_HI 0x0340 +#define GT64260_CPU_PCI_1_MEM_1_REMAP_LO 0x0118 +#define GT64260_CPU_PCI_1_MEM_1_REMAP_HI 0x0348 +#define GT64260_CPU_PCI_1_MEM_2_REMAP_LO 0x0310 +#define GT64260_CPU_PCI_1_MEM_2_REMAP_HI 0x0350 +#define GT64260_CPU_PCI_1_MEM_3_REMAP_LO 0x0318 +#define GT64260_CPU_PCI_1_MEM_3_REMAP_HI 0x0358 + +/* CPU Control Registers */ +#define GT64260_CPU_CONFIG 0x0000 +#define GT64260_CPU_MODE 0x0120 +#define GT64260_CPU_MASTER_CNTL 0x0160 +#define GT64260_CPU_XBAR_CNTL_LO 0x0150 +#define GT64260_CPU_XBAR_CNTL_HI 0x0158 +#define GT64260_CPU_XBAR_TO 0x0168 +#define GT64260_CPU_RR_XBAR_CNTL_LO 0x0170 +#define GT64260_CPU_RR_XBAR_CNTL_HI 0x0178 + +/* CPU Sync Barrier Registers */ +#define GT64260_CPU_SYNC_BARRIER_PCI_0 0x00c0 +#define GT64260_CPU_SYNC_BARRIER_PCI_1 0x00c8 + +/* CPU Access Protection Registers */ +#define GT64260_CPU_PROT_WINDOWS 8 + +#define GT64260_CPU_PROT_ACCPROTECT (1<<16) +#define GT64260_CPU_PROT_WRPROTECT (1<<17) +#define GT64260_CPU_PROT_CACHEPROTECT (1<<18) + +#define GT64260_CPU_PROT_BASE_0 0x0180 +#define GT64260_CPU_PROT_TOP_0 0x0188 +#define GT64260_CPU_PROT_BASE_1 0x0190 +#define GT64260_CPU_PROT_TOP_1 0x0198 +#define GT64260_CPU_PROT_BASE_2 0x01a0 +#define GT64260_CPU_PROT_TOP_2 0x01a8 +#define GT64260_CPU_PROT_BASE_3 0x01b0 +#define GT64260_CPU_PROT_TOP_3 0x01b8 +#define GT64260_CPU_PROT_BASE_4 0x01c0 +#define GT64260_CPU_PROT_TOP_4 0x01c8 +#define GT64260_CPU_PROT_BASE_5 0x01d0 +#define GT64260_CPU_PROT_TOP_5 0x01d8 +#define GT64260_CPU_PROT_BASE_6 0x01e0 +#define GT64260_CPU_PROT_TOP_6 0x01e8 +#define GT64260_CPU_PROT_BASE_7 0x01f0 +#define GT64260_CPU_PROT_TOP_7 0x01f8 + +/* CPU Snoop Control Registers */ +#define GT64260_CPU_SNOOP_WINDOWS 4 + +#define GT64260_CPU_SNOOP_NONE 0x00000000 +#define GT64260_CPU_SNOOP_WT 0x00010000 +#define GT64260_CPU_SNOOP_WB 0x00020000 +#define GT64260_CPU_SNOOP_MASK 0x00030000 +#define GT64260_CPU_SNOOP_ALL_BITS GT64260_CPU_SNOOP_MASK + +#define GT64260_CPU_SNOOP_BASE_0 0x0380 +#define GT64260_CPU_SNOOP_TOP_0 0x0388 +#define GT64260_CPU_SNOOP_BASE_1 0x0390 +#define GT64260_CPU_SNOOP_TOP_1 0x0398 +#define GT64260_CPU_SNOOP_BASE_2 0x03a0 +#define GT64260_CPU_SNOOP_TOP_2 0x03a8 +#define GT64260_CPU_SNOOP_BASE_3 0x03b0 +#define GT64260_CPU_SNOOP_TOP_3 0x03b8 + +/* CPU Error Report Registers */ +#define GT64260_CPU_ERR_ADDR_LO 0x0070 +#define GT64260_CPU_ERR_ADDR_HI 0x0078 +#define GT64260_CPU_ERR_DATA_LO 0x0128 +#define GT64260_CPU_ERR_DATA_HI 0x0130 +#define GT64260_CPU_ERR_PARITY 0x0138 +#define GT64260_CPU_ERR_CAUSE 0x0140 +#define GT64260_CPU_ERR_MASK 0x0148 + + +/* + ***************************************************************************** + * + * SDRAM Cotnroller Registers + * + ***************************************************************************** + */ + +/* SDRAM Config Registers */ +#define GT64260_SDRAM_CONFIG 0x0448 +#define GT64260_SDRAM_OPERATION_MODE 0x0474 +#define GT64260_SDRAM_ADDR_CNTL 0x047c +#define GT64260_SDRAM_TIMING_PARAMS 0x04b4 +#define GT64260_SDRAM_UMA_CNTL 0x04a4 +#define GT64260_SDRAM_XBAR_CNTL_LO 0x04a8 +#define GT64260_SDRAM_XBAR_CNTL_HI 0x04ac +#define GT64260_SDRAM_XBAR_CNTL_TO 0x04b0 + +/* SDRAM Banks Parameters Registers */ +#define GT64260_SDRAM_BANK_PARAMS_0 0x044c +#define GT64260_SDRAM_BANK_PARAMS_1 0x0450 +#define GT64260_SDRAM_BANK_PARAMS_2 0x0454 +#define GT64260_SDRAM_BANK_PARAMS_3 0x0458 + +/* SDRAM Error Report Registers */ +#define GT64260_SDRAM_ERR_DATA_LO 0x0484 +#define GT64260_SDRAM_ERR_DATA_HI 0x0480 +#define GT64260_SDRAM_ERR_ADDR 0x0490 +#define GT64260_SDRAM_ERR_ECC_RCVD 0x0488 +#define GT64260_SDRAM_ERR_ECC_CALC 0x048c +#define GT64260_SDRAM_ERR_ECC_CNTL 0x0494 +#define GT64260_SDRAM_ERR_ECC_ERR_CNT 0x0498 + + +/* + ***************************************************************************** + * + * Device/BOOT Cotnroller Registers + * + ***************************************************************************** + */ + +/* Device Control Registers */ +#define GT64260_DEV_BANK_PARAMS_0 0x045c +#define GT64260_DEV_BANK_PARAMS_1 0x0460 +#define GT64260_DEV_BANK_PARAMS_2 0x0464 +#define GT64260_DEV_BANK_PARAMS_3 0x0468 +#define GT64260_DEV_BOOT_PARAMS 0x046c +#define GT64260_DEV_IF_CNTL 0x04c0 +#define GT64260_DEV_IF_XBAR_CNTL_LO 0x04c8 +#define GT64260_DEV_IF_XBAR_CNTL_HI 0x04cc +#define GT64260_DEV_IF_XBAR_CNTL_TO 0x04c4 + +/* Device Interrupt Registers */ +#define GT64260_DEV_INTR_CAUSE 0x04d0 +#define GT64260_DEV_INTR_MASK 0x04d4 +#define GT64260_DEV_INTR_ERR_ADDR 0x04d8 + + +/* + ***************************************************************************** + * + * PCI Bridge Interface Registers + * + ***************************************************************************** + */ + +/* PCI Configuration Access Registers */ +#define GT64260_PCI_0_CONFIG_ADDR 0x0cf8 +#define GT64260_PCI_0_CONFIG_DATA 0x0cfc +#define GT64260_PCI_0_IACK 0x0c34 + +#define GT64260_PCI_1_CONFIG_ADDR 0x0c78 +#define GT64260_PCI_1_CONFIG_DATA 0x0c7c +#define GT64260_PCI_1_IACK 0x0cb4 + +/* PCI Control Registers */ +#define GT64260_PCI_0_CMD 0x0c00 +#define GT64260_PCI_0_MODE 0x0d00 +#define GT64260_PCI_0_TO_RETRY 0x0c04 +#define GT64260_PCI_0_RD_BUF_DISCARD_TIMER 0x0d04 +#define GT64260_PCI_0_MSI_TRIGGER_TIMER 0x0c38 +#define GT64260_PCI_0_ARBITER_CNTL 0x1d00 +#define GT64260_PCI_0_XBAR_CNTL_LO 0x1d08 +#define GT64260_PCI_0_XBAR_CNTL_HI 0x1d0c +#define GT64260_PCI_0_XBAR_CNTL_TO 0x1d04 +#define GT64260_PCI_0_RD_RESP_XBAR_CNTL_LO 0x1d18 +#define GT64260_PCI_0_RD_RESP_XBAR_CNTL_HI 0x1d1c +#define GT64260_PCI_0_SYNC_BARRIER 0x1d10 +#define GT64260_PCI_0_P2P_CONFIG 0x1d14 +#define GT64260_PCI_0_P2P_SWAP_CNTL 0x1d54 + +#define GT64260_PCI_1_CMD 0x0c80 +#define GT64260_PCI_1_MODE 0x0d80 +#define GT64260_PCI_1_TO_RETRY 0x0c84 +#define GT64260_PCI_1_RD_BUF_DISCARD_TIMER 0x0d84 +#define GT64260_PCI_1_MSI_TRIGGER_TIMER 0x0cb8 +#define GT64260_PCI_1_ARBITER_CNTL 0x1d80 +#define GT64260_PCI_1_XBAR_CNTL_LO 0x1d88 +#define GT64260_PCI_1_XBAR_CNTL_HI 0x1d8c +#define GT64260_PCI_1_XBAR_CNTL_TO 0x1d84 +#define GT64260_PCI_1_RD_RESP_XBAR_CNTL_LO 0x1d98 +#define GT64260_PCI_1_RD_RESP_XBAR_CNTL_HI 0x1d9c +#define GT64260_PCI_1_SYNC_BARRIER 0x1d90 +#define GT64260_PCI_1_P2P_CONFIG 0x1d94 +#define GT64260_PCI_1_P2P_SWAP_CNTL 0x1dd4 + +/* PCI Access Control Regions Registers */ +#define GT64260_PCI_ACC_CNTL_WINDOWS 8 + +#define GT64260_PCI_ACC_CNTL_PREFETCHEN (1<<12) +#define GT64260_PCI_ACC_CNTL_DREADEN (1<<13) +#define GT64260_PCI_ACC_CNTL_RDPREFETCH (1<<16) +#define GT64260_PCI_ACC_CNTL_RDLINEPREFETCH (1<<17) +#define GT64260_PCI_ACC_CNTL_RDMULPREFETCH (1<<18) +#define GT64260_PCI_ACC_CNTL_MBURST_4_WORDS 0x00000000 +#define GT64260_PCI_ACC_CNTL_MBURST_8_WORDS 0x00100000 +#define GT64260_PCI_ACC_CNTL_MBURST_16_WORDS 0x00200000 +#define GT64260_PCI_ACC_CNTL_MBURST_MASK 0x00300000 +#define GT64260_PCI_ACC_CNTL_SWAP_BYTE 0x00000000 +#define GT64260_PCI_ACC_CNTL_SWAP_NONE 0x01000000 +#define GT64260_PCI_ACC_CNTL_SWAP_BYTE_WORD 0x02000000 +#define GT64260_PCI_ACC_CNTL_SWAP_WORD 0x03000000 +#define GT64260_PCI_ACC_CNTL_SWAP_MASK 0x03000000 +#define GT64260_PCI_ACC_CNTL_ACCPROT (1<<28) +#define GT64260_PCI_ACC_CNTL_WRPROT (1<<29) + +#define GT64260_PCI_ACC_CNTL_ALL_BITS (GT64260_PCI_ACC_CNTL_PREFETCHEN | \ + GT64260_PCI_ACC_CNTL_DREADEN | \ + GT64260_PCI_ACC_CNTL_RDPREFETCH | \ + GT64260_PCI_ACC_CNTL_RDLINEPREFETCH |\ + GT64260_PCI_ACC_CNTL_RDMULPREFETCH | \ + GT64260_PCI_ACC_CNTL_MBURST_MASK | \ + GT64260_PCI_ACC_CNTL_SWAP_MASK | \ + GT64260_PCI_ACC_CNTL_ACCPROT| \ + GT64260_PCI_ACC_CNTL_WRPROT) + +#define GT64260_PCI_0_ACC_CNTL_0_BASE_LO 0x1e00 +#define GT64260_PCI_0_ACC_CNTL_0_BASE_HI 0x1e04 +#define GT64260_PCI_0_ACC_CNTL_0_TOP 0x1e08 +#define GT64260_PCI_0_ACC_CNTL_1_BASE_LO 0x1e10 +#define GT64260_PCI_0_ACC_CNTL_1_BASE_HI 0x1e14 +#define GT64260_PCI_0_ACC_CNTL_1_TOP 0x1e18 +#define GT64260_PCI_0_ACC_CNTL_2_BASE_LO 0x1e20 +#define GT64260_PCI_0_ACC_CNTL_2_BASE_HI 0x1e24 +#define GT64260_PCI_0_ACC_CNTL_2_TOP 0x1e28 +#define GT64260_PCI_0_ACC_CNTL_3_BASE_LO 0x1e30 +#define GT64260_PCI_0_ACC_CNTL_3_BASE_HI 0x1e34 +#define GT64260_PCI_0_ACC_CNTL_3_TOP 0x1e38 +#define GT64260_PCI_0_ACC_CNTL_4_BASE_LO 0x1e40 +#define GT64260_PCI_0_ACC_CNTL_4_BASE_HI 0x1e44 +#define GT64260_PCI_0_ACC_CNTL_4_TOP 0x1e48 +#define GT64260_PCI_0_ACC_CNTL_5_BASE_LO 0x1e50 +#define GT64260_PCI_0_ACC_CNTL_5_BASE_HI 0x1e54 +#define GT64260_PCI_0_ACC_CNTL_5_TOP 0x1e58 +#define GT64260_PCI_0_ACC_CNTL_6_BASE_LO 0x1e60 +#define GT64260_PCI_0_ACC_CNTL_6_BASE_HI 0x1e64 +#define GT64260_PCI_0_ACC_CNTL_6_TOP 0x1e68 +#define GT64260_PCI_0_ACC_CNTL_7_BASE_LO 0x1e70 +#define GT64260_PCI_0_ACC_CNTL_7_BASE_HI 0x1e74 +#define GT64260_PCI_0_ACC_CNTL_7_TOP 0x1e78 + +#define GT64260_PCI_1_ACC_CNTL_0_BASE_LO 0x1e80 +#define GT64260_PCI_1_ACC_CNTL_0_BASE_HI 0x1e84 +#define GT64260_PCI_1_ACC_CNTL_0_TOP 0x1e88 +#define GT64260_PCI_1_ACC_CNTL_1_BASE_LO 0x1e90 +#define GT64260_PCI_1_ACC_CNTL_1_BASE_HI 0x1e94 +#define GT64260_PCI_1_ACC_CNTL_1_TOP 0x1e98 +#define GT64260_PCI_1_ACC_CNTL_2_BASE_LO 0x1ea0 +#define GT64260_PCI_1_ACC_CNTL_2_BASE_HI 0x1ea4 +#define GT64260_PCI_1_ACC_CNTL_2_TOP 0x1ea8 +#define GT64260_PCI_1_ACC_CNTL_3_BASE_LO 0x1eb0 +#define GT64260_PCI_1_ACC_CNTL_3_BASE_HI 0x1eb4 +#define GT64260_PCI_1_ACC_CNTL_3_TOP 0x1eb8 +#define GT64260_PCI_1_ACC_CNTL_4_BASE_LO 0x1ec0 +#define GT64260_PCI_1_ACC_CNTL_4_BASE_HI 0x1ec4 +#define GT64260_PCI_1_ACC_CNTL_4_TOP 0x1ec8 +#define GT64260_PCI_1_ACC_CNTL_5_BASE_LO 0x1ed0 +#define GT64260_PCI_1_ACC_CNTL_5_BASE_HI 0x1ed4 +#define GT64260_PCI_1_ACC_CNTL_5_TOP 0x1ed8 +#define GT64260_PCI_1_ACC_CNTL_6_BASE_LO 0x1ee0 +#define GT64260_PCI_1_ACC_CNTL_6_BASE_HI 0x1ee4 +#define GT64260_PCI_1_ACC_CNTL_6_TOP 0x1ee8 +#define GT64260_PCI_1_ACC_CNTL_7_BASE_LO 0x1ef0 +#define GT64260_PCI_1_ACC_CNTL_7_BASE_HI 0x1ef4 +#define GT64260_PCI_1_ACC_CNTL_7_TOP 0x1ef8 + +/* PCI Snoop Control Registers */ +#define GT64260_PCI_SNOOP_WINDOWS 4 + +#define GT64260_PCI_SNOOP_NONE 0x00000000 +#define GT64260_PCI_SNOOP_WT 0x00001000 +#define GT64260_PCI_SNOOP_WB 0x00002000 + +#define GT64260_PCI_0_SNOOP_0_BASE_LO 0x1f00 +#define GT64260_PCI_0_SNOOP_0_BASE_HI 0x1f04 +#define GT64260_PCI_0_SNOOP_0_TOP 0x1f08 +#define GT64260_PCI_0_SNOOP_1_BASE_LO 0x1f10 +#define GT64260_PCI_0_SNOOP_1_BASE_HI 0x1f14 +#define GT64260_PCI_0_SNOOP_1_TOP 0x1f18 +#define GT64260_PCI_0_SNOOP_2_BASE_LO 0x1f20 +#define GT64260_PCI_0_SNOOP_2_BASE_HI 0x1f24 +#define GT64260_PCI_0_SNOOP_2_TOP 0x1f28 +#define GT64260_PCI_0_SNOOP_3_BASE_LO 0x1f30 +#define GT64260_PCI_0_SNOOP_3_BASE_HI 0x1f34 +#define GT64260_PCI_0_SNOOP_3_TOP 0x1f38 + +#define GT64260_PCI_1_SNOOP_0_BASE_LO 0x1f80 +#define GT64260_PCI_1_SNOOP_0_BASE_HI 0x1f84 +#define GT64260_PCI_1_SNOOP_0_TOP 0x1f88 +#define GT64260_PCI_1_SNOOP_1_BASE_LO 0x1f90 +#define GT64260_PCI_1_SNOOP_1_BASE_HI 0x1f94 +#define GT64260_PCI_1_SNOOP_1_TOP 0x1f98 +#define GT64260_PCI_1_SNOOP_2_BASE_LO 0x1fa0 +#define GT64260_PCI_1_SNOOP_2_BASE_HI 0x1fa4 +#define GT64260_PCI_1_SNOOP_2_TOP 0x1fa8 +#define GT64260_PCI_1_SNOOP_3_BASE_LO 0x1fb0 +#define GT64260_PCI_1_SNOOP_3_BASE_HI 0x1fb4 +#define GT64260_PCI_1_SNOOP_3_TOP 0x1fb8 + +/* PCI Error Report Registers */ +#define GT64260_PCI_0_ERR_SERR_MASK 0x0c28 +#define GT64260_PCI_0_ERR_ADDR_LO 0x1d40 +#define GT64260_PCI_0_ERR_ADDR_HI 0x1d44 +#define GT64260_PCI_0_ERR_DATA_LO 0x1d48 +#define GT64260_PCI_0_ERR_DATA_HI 0x1d4c +#define GT64260_PCI_0_ERR_CMD 0x1d50 +#define GT64260_PCI_0_ERR_CAUSE 0x1d58 +#define GT64260_PCI_0_ERR_MASK 0x1d5c + +#define GT64260_PCI_1_ERR_SERR_MASK 0x0ca8 +#define GT64260_PCI_1_ERR_ADDR_LO 0x1dc0 +#define GT64260_PCI_1_ERR_ADDR_HI 0x1dc4 +#define GT64260_PCI_1_ERR_DATA_LO 0x1dc8 +#define GT64260_PCI_1_ERR_DATA_HI 0x1dcc +#define GT64260_PCI_1_ERR_CMD 0x1dd0 +#define GT64260_PCI_1_ERR_CAUSE 0x1dd8 +#define GT64260_PCI_1_ERR_MASK 0x1ddc + +/* PCI Slave Address Decoding Registers */ +#define GT64260_PCI_SCS_WINDOWS 4 +#define GT64260_PCI_CS_WINDOWS 4 +#define GT64260_PCI_BOOT_WINDOWS 1 +#define GT64260_PCI_P2P_MEM_WINDOWS 2 +#define GT64260_PCI_P2P_IO_WINDOWS 1 +#define GT64260_PCI_DAC_SCS_WINDOWS 4 +#define GT64260_PCI_DAC_CS_WINDOWS 4 +#define GT64260_PCI_DAC_BOOT_WINDOWS 1 +#define GT64260_PCI_DAC_P2P_MEM_WINDOWS 2 + +#define GT64260_PCI_0_SLAVE_SCS_0_SIZE 0x0c08 +#define GT64260_PCI_0_SLAVE_SCS_1_SIZE 0x0d08 +#define GT64260_PCI_0_SLAVE_SCS_2_SIZE 0x0c0c +#define GT64260_PCI_0_SLAVE_SCS_3_SIZE 0x0d0c +#define GT64260_PCI_0_SLAVE_CS_0_SIZE 0x0c10 +#define GT64260_PCI_0_SLAVE_CS_1_SIZE 0x0d10 +#define GT64260_PCI_0_SLAVE_CS_2_SIZE 0x0d18 +#define GT64260_PCI_0_SLAVE_CS_3_SIZE 0x0c14 +#define GT64260_PCI_0_SLAVE_BOOT_SIZE 0x0d14 +#define GT64260_PCI_0_SLAVE_P2P_MEM_0_SIZE 0x0d1c +#define GT64260_PCI_0_SLAVE_P2P_MEM_1_SIZE 0x0d20 +#define GT64260_PCI_0_SLAVE_P2P_IO_SIZE 0x0d24 +#define GT64260_PCI_0_SLAVE_CPU_SIZE 0x0d28 + +#define GT64260_PCI_0_SLAVE_DAC_SCS_0_SIZE 0x0e00 +#define GT64260_PCI_0_SLAVE_DAC_SCS_1_SIZE 0x0e04 +#define GT64260_PCI_0_SLAVE_DAC_SCS_2_SIZE 0x0e08 +#define GT64260_PCI_0_SLAVE_DAC_SCS_3_SIZE 0x0e0c +#define GT64260_PCI_0_SLAVE_DAC_CS_0_SIZE 0x0e10 +#define GT64260_PCI_0_SLAVE_DAC_CS_1_SIZE 0x0e14 +#define GT64260_PCI_0_SLAVE_DAC_CS_2_SIZE 0x0e18 +#define GT64260_PCI_0_SLAVE_DAC_CS_3_SIZE 0x0e1c +#define GT64260_PCI_0_SLAVE_DAC_BOOT_SIZE 0x0e20 +#define GT64260_PCI_0_SLAVE_DAC_P2P_MEM_0_SIZE 0x0e24 +#define GT64260_PCI_0_SLAVE_DAC_P2P_MEM_1_SIZE 0x0e28 +#define GT64260_PCI_0_SLAVE_DAC_CPU_SIZE 0x0e2c + +#define GT64260_PCI_0_SLAVE_EXP_ROM_SIZE 0x0d2c + +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_SCS_0 (1<<0) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_SCS_1 (1<<1) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_SCS_2 (1<<2) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_SCS_3 (1<<3) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_CS_0 (1<<4) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_CS_1 (1<<5) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_CS_2 (1<<6) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_CS_3 (1<<7) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_BOOT (1<<8) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_REG_MEM (1<<9) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_REG_IO (1<<10) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_P2P_MEM_0 (1<<11) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_P2P_MEM_1 (1<<12) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_P2P_IO (1<<13) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_CPU (1<<14) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_SCS_0 (1<<15) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_SCS_1 (1<<16) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_SCS_2 (1<<17) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_SCS_3 (1<<18) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_CS_0 (1<<19) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_CS_1 (1<<20) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_CS_2 (1<<21) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_CS_3 (1<<22) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_BOOT (1<<23) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_P2P_MEM_0 (1<<24) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_P2P_MEM_1 (1<<25) +#define GT64260_PCI_SLAVE_BAR_REG_ENABLES_DAC_CPU (1<<26) + +#define GT64260_PCI_0_SLAVE_BAR_REG_ENABLES 0x0c3c +#define GT64260_PCI_0_SLAVE_SCS_0_REMAP 0x0c48 +#define GT64260_PCI_0_SLAVE_SCS_1_REMAP 0x0d48 +#define GT64260_PCI_0_SLAVE_SCS_2_REMAP 0x0c4c +#define GT64260_PCI_0_SLAVE_SCS_3_REMAP 0x0d4c +#define GT64260_PCI_0_SLAVE_CS_0_REMAP 0x0c50 +#define GT64260_PCI_0_SLAVE_CS_1_REMAP 0x0d50 +#define GT64260_PCI_0_SLAVE_CS_2_REMAP 0x0d58 +#define GT64260_PCI_0_SLAVE_CS_3_REMAP 0x0c54 +#define GT64260_PCI_0_SLAVE_BOOT_REMAP 0x0d54 +#define GT64260_PCI_0_SLAVE_P2P_MEM_0_REMAP_LO 0x0d5c +#define GT64260_PCI_0_SLAVE_P2P_MEM_0_REMAP_HI 0x0d60 +#define GT64260_PCI_0_SLAVE_P2P_MEM_1_REMAP_LO 0x0d64 +#define GT64260_PCI_0_SLAVE_P2P_MEM_1_REMAP_HI 0x0d68 +#define GT64260_PCI_0_SLAVE_P2P_IO_REMAP 0x0d6c +#define GT64260_PCI_0_SLAVE_CPU_REMAP 0x0d70 + +#define GT64260_PCI_0_SLAVE_DAC_SCS_0_REMAP 0x0f00 +#define GT64260_PCI_0_SLAVE_DAC_SCS_1_REMAP 0x0f04 +#define GT64260_PCI_0_SLAVE_DAC_SCS_2_REMAP 0x0f08 +#define GT64260_PCI_0_SLAVE_DAC_SCS_3_REMAP 0x0f0c +#define GT64260_PCI_0_SLAVE_DAC_CS_0_REMAP 0x0f10 +#define GT64260_PCI_0_SLAVE_DAC_CS_1_REMAP 0x0f14 +#define GT64260_PCI_0_SLAVE_DAC_CS_2_REMAP 0x0f18 +#define GT64260_PCI_0_SLAVE_DAC_CS_3_REMAP 0x0f1c +#define GT64260_PCI_0_SLAVE_DAC_BOOT_REMAP 0x0f20 +#define GT64260_PCI_0_SLAVE_DAC_P2P_MEM_0_REMAP_LO 0x0f24 +#define GT64260_PCI_0_SLAVE_DAC_P2P_MEM_0_REMAP_HI 0x0f28 +#define GT64260_PCI_0_SLAVE_DAC_P2P_MEM_1_REMAP_LO 0x0f2c +#define GT64260_PCI_0_SLAVE_DAC_P2P_MEM_1_REMAP_HI 0x0f30 +#define GT64260_PCI_0_SLAVE_DAC_CPU_REMAP 0x0f34 + +#define GT64260_PCI_0_SLAVE_EXP_ROM_REMAP 0x0f38 +#define GT64260_PCI_0_SLAVE_PCI_DECODE_CNTL 0x0d3c + +#define GT64260_PCI_1_SLAVE_SCS_0_SIZE 0x0c88 +#define GT64260_PCI_1_SLAVE_SCS_1_SIZE 0x0d88 +#define GT64260_PCI_1_SLAVE_SCS_2_SIZE 0x0c8c +#define GT64260_PCI_1_SLAVE_SCS_3_SIZE 0x0d8c +#define GT64260_PCI_1_SLAVE_CS_0_SIZE 0x0c90 +#define GT64260_PCI_1_SLAVE_CS_1_SIZE 0x0d90 +#define GT64260_PCI_1_SLAVE_CS_2_SIZE 0x0d98 +#define GT64260_PCI_1_SLAVE_CS_3_SIZE 0x0c94 +#define GT64260_PCI_1_SLAVE_BOOT_SIZE 0x0d94 +#define GT64260_PCI_1_SLAVE_P2P_MEM_0_SIZE 0x0d9c +#define GT64260_PCI_1_SLAVE_P2P_MEM_1_SIZE 0x0da0 +#define GT64260_PCI_1_SLAVE_P2P_IO_SIZE 0x0da4 +#define GT64260_PCI_1_SLAVE_CPU_SIZE 0x0da8 + +#define GT64260_PCI_1_SLAVE_DAC_SCS_0_SIZE 0x0e80 +#define GT64260_PCI_1_SLAVE_DAC_SCS_1_SIZE 0x0e84 +#define GT64260_PCI_1_SLAVE_DAC_SCS_2_SIZE 0x0e88 +#define GT64260_PCI_1_SLAVE_DAC_SCS_3_SIZE 0x0e8c +#define GT64260_PCI_1_SLAVE_DAC_CS_0_SIZE 0x0e90 +#define GT64260_PCI_1_SLAVE_DAC_CS_1_SIZE 0x0e94 +#define GT64260_PCI_1_SLAVE_DAC_CS_2_SIZE 0x0e98 +#define GT64260_PCI_1_SLAVE_DAC_CS_3_SIZE 0x0e9c +#define GT64260_PCI_1_SLAVE_DAC_BOOT_SIZE 0x0ea0 +#define GT64260_PCI_1_SLAVE_DAC_P2P_MEM_0_SIZE 0x0ea4 +#define GT64260_PCI_1_SLAVE_DAC_P2P_MEM_1_SIZE 0x0ea8 +#define GT64260_PCI_1_SLAVE_DAC_CPU_SIZE 0x0eac + +#define GT64260_PCI_1_SLAVE_EXP_ROM_SIZE 0x0dac + +#define GT64260_PCI_1_SLAVE_BAR_REG_ENABLES 0x0cbc +#define GT64260_PCI_1_SLAVE_SCS_0_REMAP 0x0cc8 +#define GT64260_PCI_1_SLAVE_SCS_1_REMAP 0x0dc8 +#define GT64260_PCI_1_SLAVE_SCS_2_REMAP 0x0ccc +#define GT64260_PCI_1_SLAVE_SCS_3_REMAP 0x0dcc +#define GT64260_PCI_1_SLAVE_CS_0_REMAP 0x0cd0 +#define GT64260_PCI_1_SLAVE_CS_1_REMAP 0x0dd0 +#define GT64260_PCI_1_SLAVE_CS_2_REMAP 0x0dd8 +#define GT64260_PCI_1_SLAVE_CS_3_REMAP 0x0cd4 +#define GT64260_PCI_1_SLAVE_BOOT_REMAP 0x0dd4 +#define GT64260_PCI_1_SLAVE_P2P_MEM_0_REMAP_LO 0x0ddc +#define GT64260_PCI_1_SLAVE_P2P_MEM_0_REMAP_HI 0x0de0 +#define GT64260_PCI_1_SLAVE_P2P_MEM_1_REMAP_LO 0x0de4 +#define GT64260_PCI_1_SLAVE_P2P_MEM_1_REMAP_HI 0x0de8 +#define GT64260_PCI_1_SLAVE_P2P_IO_REMAP 0x0dec +#define GT64260_PCI_1_SLAVE_CPU_REMAP 0x0df0 + +#define GT64260_PCI_1_SLAVE_DAC_SCS_0_REMAP 0x0f80 +#define GT64260_PCI_1_SLAVE_DAC_SCS_1_REMAP 0x0f84 +#define GT64260_PCI_1_SLAVE_DAC_SCS_2_REMAP 0x0f88 +#define GT64260_PCI_1_SLAVE_DAC_SCS_3_REMAP 0x0f8c +#define GT64260_PCI_1_SLAVE_DAC_CS_0_REMAP 0x0f90 +#define GT64260_PCI_1_SLAVE_DAC_CS_1_REMAP 0x0f94 +#define GT64260_PCI_1_SLAVE_DAC_CS_2_REMAP 0x0f98 +#define GT64260_PCI_1_SLAVE_DAC_CS_3_REMAP 0x0f9c +#define GT64260_PCI_1_SLAVE_DAC_BOOT_REMAP 0x0fa0 +#define GT64260_PCI_1_SLAVE_DAC_P2P_MEM_0_REMAP_LO 0x0fa4 +#define GT64260_PCI_1_SLAVE_DAC_P2P_MEM_0_REMAP_HI 0x0fa8 +#define GT64260_PCI_1_SLAVE_DAC_P2P_MEM_1_REMAP_LO 0x0fac +#define GT64260_PCI_1_SLAVE_DAC_P2P_MEM_1_REMAP_HI 0x0fb0 +#define GT64260_PCI_1_SLAVE_DAC_CPU_REMAP 0x0fb4 + +#define GT64260_PCI_1_SLAVE_EXP_ROM_REMAP 0x0fb8 +#define GT64260_PCI_1_SLAVE_PCI_DECODE_CNTL 0x0dbc + + +/* + ***************************************************************************** + * + * I2O Controller Interface Registers + * + ***************************************************************************** + */ + +/* FIXME: fill in */ + + + +/* + ***************************************************************************** + * + * DMA Controller Interface Registers + * + ***************************************************************************** + */ + +/* FIXME: fill in */ + + +/* + ***************************************************************************** + * + * Timer/Counter Interface Registers + * + ***************************************************************************** + */ + +/* FIXME: fill in */ + + +/* + ***************************************************************************** + * + * Communications Controller (Enet, Serial, etc.) Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_ENET_0_CNTL_LO 0xf200 +#define GT64260_ENET_0_CNTL_HI 0xf204 +#define GT64260_ENET_0_RX_BUF_PCI_ADDR_HI 0xf208 +#define GT64260_ENET_0_TX_BUF_PCI_ADDR_HI 0xf20c +#define GT64260_ENET_0_RX_DESC_ADDR_HI 0xf210 +#define GT64260_ENET_0_TX_DESC_ADDR_HI 0xf214 +#define GT64260_ENET_0_HASH_TAB_PCI_ADDR_HI 0xf218 +#define GT64260_ENET_1_CNTL_LO 0xf220 +#define GT64260_ENET_1_CNTL_HI 0xf224 +#define GT64260_ENET_1_RX_BUF_PCI_ADDR_HI 0xf228 +#define GT64260_ENET_1_TX_BUF_PCI_ADDR_HI 0xf22c +#define GT64260_ENET_1_RX_DESC_ADDR_HI 0xf230 +#define GT64260_ENET_1_TX_DESC_ADDR_HI 0xf234 +#define GT64260_ENET_1_HASH_TAB_PCI_ADDR_HI 0xf238 +#define GT64260_ENET_2_CNTL_LO 0xf240 +#define GT64260_ENET_2_CNTL_HI 0xf244 +#define GT64260_ENET_2_RX_BUF_PCI_ADDR_HI 0xf248 +#define GT64260_ENET_2_TX_BUF_PCI_ADDR_HI 0xf24c +#define GT64260_ENET_2_RX_DESC_ADDR_HI 0xf250 +#define GT64260_ENET_2_TX_DESC_ADDR_HI 0xf254 +#define GT64260_ENET_2_HASH_TAB_PCI_ADDR_HI 0xf258 + +#define GT64260_MPSC_0_CNTL_LO 0xf280 +#define GT64260_MPSC_0_CNTL_HI 0xf284 +#define GT64260_MPSC_0_RX_BUF_PCI_ADDR_HI 0xf288 +#define GT64260_MPSC_0_TX_BUF_PCI_ADDR_HI 0xf28c +#define GT64260_MPSC_0_RX_DESC_ADDR_HI 0xf290 +#define GT64260_MPSC_0_TX_DESC_ADDR_HI 0xf294 +#define GT64260_MPSC_1_CNTL_LO 0xf2c0 +#define GT64260_MPSC_1_CNTL_HI 0xf2c4 +#define GT64260_MPSC_1_RX_BUF_PCI_ADDR_HI 0xf2c8 +#define GT64260_MPSC_1_TX_BUF_PCI_ADDR_HI 0xf2cc +#define GT64260_MPSC_1_RX_DESC_ADDR_HI 0xf2d0 +#define GT64260_MPSC_1_TX_DESC_ADDR_HI 0xf2d4 + +#define GT64260_SER_INIT_PCI_ADDR_HI 0xf320 +#define GT64260_SER_INIT_LAST_DATA 0xf324 +#define GT64260_SER_INIT_CONTROL 0xf328 +#define GT64260_SER_INIT_STATUS 0xf32c + +#define GT64260_COMM_ARBITER_CNTL 0xf300 +#define GT64260_COMM_CONFIG 0xb40c +#define GT64260_COMM_XBAR_TO 0xf304 +#define GT64260_COMM_INTR_CAUSE 0xf310 +#define GT64260_COMM_INTR_MASK 0xf314 +#define GT64260_COMM_ERR_ADDR 0xf318 + + +/* + ***************************************************************************** + * + * Fast Ethernet Controller Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_ENET_PHY_ADDR 0x2000 +#define GT64260_ENET_ESMIR 0x2010 + +#define GT64260_ENET_E0PCR 0x2400 +#define GT64260_ENET_E0PCXR 0x2408 +#define GT64260_ENET_E0PCMR 0x2410 +#define GT64260_ENET_E0PSR 0x2418 +#define GT64260_ENET_E0SPR 0x2420 +#define GT64260_ENET_E0HTPR 0x2428 +#define GT64260_ENET_E0FCSAL 0x2430 +#define GT64260_ENET_E0FCSAH 0x2438 +#define GT64260_ENET_E0SDCR 0x2440 +#define GT64260_ENET_E0SDCMR 0x2448 +#define GT64260_ENET_E0ICR 0x2450 +#define GT64260_ENET_E0IMR 0x2458 +#define GT64260_ENET_E0FRDP0 0x2480 +#define GT64260_ENET_E0FRDP1 0x2484 +#define GT64260_ENET_E0FRDP2 0x2488 +#define GT64260_ENET_E0FRDP3 0x248c +#define GT64260_ENET_E0CRDP0 0x24a0 +#define GT64260_ENET_E0CRDP1 0x24a4 +#define GT64260_ENET_E0CRDP2 0x24a8 +#define GT64260_ENET_E0CRDP3 0x24ac +#define GT64260_ENET_E0CTDP0 0x24e0 +#define GT64260_ENET_E0CTDP1 0x24e4 +#define GT64260_ENET_0_DSCP2P0L 0x2460 +#define GT64260_ENET_0_DSCP2P0H 0x2464 +#define GT64260_ENET_0_DSCP2P1L 0x2468 +#define GT64260_ENET_0_DSCP2P1H 0x246c +#define GT64260_ENET_0_VPT2P 0x2470 +#define GT64260_ENET_0_MIB_CTRS 0x2500 + +#define GT64260_ENET_E1PCR 0x2800 +#define GT64260_ENET_E1PCXR 0x2808 +#define GT64260_ENET_E1PCMR 0x2810 +#define GT64260_ENET_E1PSR 0x2818 +#define GT64260_ENET_E1SPR 0x2820 +#define GT64260_ENET_E1HTPR 0x2828 +#define GT64260_ENET_E1FCSAL 0x2830 +#define GT64260_ENET_E1FCSAH 0x2838 +#define GT64260_ENET_E1SDCR 0x2840 +#define GT64260_ENET_E1SDCMR 0x2848 +#define GT64260_ENET_E1ICR 0x2850 +#define GT64260_ENET_E1IMR 0x2858 +#define GT64260_ENET_E1FRDP0 0x2880 +#define GT64260_ENET_E1FRDP1 0x2884 +#define GT64260_ENET_E1FRDP2 0x2888 +#define GT64260_ENET_E1FRDP3 0x288c +#define GT64260_ENET_E1CRDP0 0x28a0 +#define GT64260_ENET_E1CRDP1 0x28a4 +#define GT64260_ENET_E1CRDP2 0x28a8 +#define GT64260_ENET_E1CRDP3 0x28ac +#define GT64260_ENET_E1CTDP0 0x28e0 +#define GT64260_ENET_E1CTDP1 0x28e4 +#define GT64260_ENET_1_DSCP2P0L 0x2860 +#define GT64260_ENET_1_DSCP2P0H 0x2864 +#define GT64260_ENET_1_DSCP2P1L 0x2868 +#define GT64260_ENET_1_DSCP2P1H 0x286c +#define GT64260_ENET_1_VPT2P 0x2870 +#define GT64260_ENET_1_MIB_CTRS 0x2900 + +#define GT64260_ENET_E2PCR 0x2c00 +#define GT64260_ENET_E2PCXR 0x2c08 +#define GT64260_ENET_E2PCMR 0x2c10 +#define GT64260_ENET_E2PSR 0x2c18 +#define GT64260_ENET_E2SPR 0x2c20 +#define GT64260_ENET_E2HTPR 0x2c28 +#define GT64260_ENET_E2FCSAL 0x2c30 +#define GT64260_ENET_E2FCSAH 0x2c38 +#define GT64260_ENET_E2SDCR 0x2c40 +#define GT64260_ENET_E2SDCMR 0x2c48 +#define GT64260_ENET_E2ICR 0x2c50 +#define GT64260_ENET_E2IMR 0x2c58 +#define GT64260_ENET_E2FRDP0 0x2c80 +#define GT64260_ENET_E2FRDP1 0x2c84 +#define GT64260_ENET_E2FRDP2 0x2c88 +#define GT64260_ENET_E2FRDP3 0x2c8c +#define GT64260_ENET_E2CRDP0 0x2ca0 +#define GT64260_ENET_E2CRDP1 0x2ca4 +#define GT64260_ENET_E2CRDP2 0x2ca8 +#define GT64260_ENET_E2CRDP3 0x2cac +#define GT64260_ENET_E2CTDP0 0x2ce0 +#define GT64260_ENET_E2CTDP1 0x2ce4 +#define GT64260_ENET_2_DSCP2P0L 0x2c60 +#define GT64260_ENET_2_DSCP2P0H 0x2c64 +#define GT64260_ENET_2_DSCP2P1L 0x2c68 +#define GT64260_ENET_2_DSCP2P1H 0x2c6c +#define GT64260_ENET_2_VPT2P 0x2c70 +#define GT64260_ENET_2_MIB_CTRS 0x2d00 + + +/* + ***************************************************************************** + * + * Multi-Protocol Serial Controller Interface Registers + * + ***************************************************************************** + */ + +/* Signal Routing */ +#define GT64260_MPSC_MRR 0xb400 +#define GT64260_MPSC_RCRR 0xb404 +#define GT64260_MPSC_TCRR 0xb408 + +/* Main Configuratino Registers */ +#define GT64260_MPSC_0_MMCRL 0x8000 +#define GT64260_MPSC_0_MMCRH 0x8004 +#define GT64260_MPSC_0_MPCR 0x8008 +#define GT64260_MPSC_0_CHR_1 0x800c +#define GT64260_MPSC_0_CHR_2 0x8010 +#define GT64260_MPSC_0_CHR_3 0x8014 +#define GT64260_MPSC_0_CHR_4 0x8018 +#define GT64260_MPSC_0_CHR_5 0x801c +#define GT64260_MPSC_0_CHR_6 0x8020 +#define GT64260_MPSC_0_CHR_7 0x8024 +#define GT64260_MPSC_0_CHR_8 0x8028 +#define GT64260_MPSC_0_CHR_9 0x802c +#define GT64260_MPSC_0_CHR_10 0x8030 +#define GT64260_MPSC_0_CHR_11 0x8034 + +#define GT64260_MPSC_1_MMCRL 0x9000 +#define GT64260_MPSC_1_MMCRH 0x9004 +#define GT64260_MPSC_1_MPCR 0x9008 +#define GT64260_MPSC_1_CHR_1 0x900c +#define GT64260_MPSC_1_CHR_2 0x9010 +#define GT64260_MPSC_1_CHR_3 0x9014 +#define GT64260_MPSC_1_CHR_4 0x9018 +#define GT64260_MPSC_1_CHR_5 0x901c +#define GT64260_MPSC_1_CHR_6 0x9020 +#define GT64260_MPSC_1_CHR_7 0x9024 +#define GT64260_MPSC_1_CHR_8 0x9028 +#define GT64260_MPSC_1_CHR_9 0x902c +#define GT64260_MPSC_1_CHR_10 0x9030 +#define GT64260_MPSC_1_CHR_11 0x9034 + +#define GT64260_MPSC_0_INTR_CAUSE 0xb804 +#define GT64260_MPSC_0_INTR_MASK 0xb884 +#define GT64260_MPSC_1_INTR_CAUSE 0xb80c +#define GT64260_MPSC_1_INTR_MASK 0xb88c + +#define GT64260_MPSC_UART_CR_TEV (1<<1) +#define GT64260_MPSC_UART_CR_TA (1<<7) +#define GT64260_MPSC_UART_CR_TTCS (1<<9) +#define GT64260_MPSC_UART_CR_REV (1<<17) +#define GT64260_MPSC_UART_CR_RA (1<<23) +#define GT64260_MPSC_UART_CR_CRD (1<<25) +#define GT64260_MPSC_UART_CR_EH (1<<31) + +#define GT64260_MPSC_UART_ESR_CTS (1<<0) +#define GT64260_MPSC_UART_ESR_CD (1<<1) +#define GT64260_MPSC_UART_ESR_TIDLE (1<<3) +#define GT64260_MPSC_UART_ESR_RHS (1<<5) +#define GT64260_MPSC_UART_ESR_RLS (1<<7) +#define GT64260_MPSC_UART_ESR_RLIDL (1<<11) + + +/* + ***************************************************************************** + * + * Serial DMA Controller Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_SDMA_0_SDC 0x4000 +#define GT64260_SDMA_0_SDCM 0x4008 +#define GT64260_SDMA_0_RX_DESC 0x4800 +#define GT64260_SDMA_0_RX_BUF_PTR 0x4808 +#define GT64260_SDMA_0_SCRDP 0x4810 +#define GT64260_SDMA_0_TX_DESC 0x4c00 +#define GT64260_SDMA_0_SCTDP 0x4c10 +#define GT64260_SDMA_0_SFTDP 0x4c14 + +#define GT64260_SDMA_1_SDC 0x6000 +#define GT64260_SDMA_1_SDCM 0x6008 +#define GT64260_SDMA_1_RX_DESC 0x6800 +#define GT64260_SDMA_1_RX_BUF_PTR 0x6808 +#define GT64260_SDMA_1_SCRDP 0x6810 +#define GT64260_SDMA_1_TX_DESC 0x6c00 +#define GT64260_SDMA_1_SCTDP 0x6c10 +#define GT64260_SDMA_1_SFTDP 0x6c14 + +#define GT64260_SDMA_INTR_CAUSE 0xb800 +#define GT64260_SDMA_INTR_MASK 0xb880 + +#define GT64260_SDMA_DESC_CMDSTAT_PE (1<<0) +#define GT64260_SDMA_DESC_CMDSTAT_CDL (1<<1) +#define GT64260_SDMA_DESC_CMDSTAT_FR (1<<3) +#define GT64260_SDMA_DESC_CMDSTAT_OR (1<<6) +#define GT64260_SDMA_DESC_CMDSTAT_BR (1<<9) +#define GT64260_SDMA_DESC_CMDSTAT_MI (1<<10) +#define GT64260_SDMA_DESC_CMDSTAT_A (1<<11) +#define GT64260_SDMA_DESC_CMDSTAT_AM (1<<12) +#define GT64260_SDMA_DESC_CMDSTAT_CT (1<<13) +#define GT64260_SDMA_DESC_CMDSTAT_C (1<<14) +#define GT64260_SDMA_DESC_CMDSTAT_ES (1<<15) +#define GT64260_SDMA_DESC_CMDSTAT_L (1<<16) +#define GT64260_SDMA_DESC_CMDSTAT_F (1<<17) +#define GT64260_SDMA_DESC_CMDSTAT_P (1<<18) +#define GT64260_SDMA_DESC_CMDSTAT_EI (1<<23) +#define GT64260_SDMA_DESC_CMDSTAT_O (1<<31) + +#define GT64260_SDMA_SDC_RFT (1<<0) +#define GT64260_SDMA_SDC_SFM (1<<1) +#define GT64260_SDMA_SDC_BLMR (1<<6) +#define GT64260_SDMA_SDC_BLMT (1<<7) +#define GT64260_SDMA_SDC_POVR (1<<8) +#define GT64260_SDMA_SDC_RIFB (1<<9) + +#define GT64260_SDMA_SDCM_ERD (1<<7) +#define GT64260_SDMA_SDCM_AR (1<<15) +#define GT64260_SDMA_SDCM_STD (1<<16) +#define GT64260_SDMA_SDCM_TXD (1<<23) +#define GT64260_SDMA_SDCM_AT (1<<31) + +#define GT64260_SDMA_0_CAUSE_RXBUF (1<<0) +#define GT64260_SDMA_0_CAUSE_RXERR (1<<1) +#define GT64260_SDMA_0_CAUSE_TXBUF (1<<2) +#define GT64260_SDMA_0_CAUSE_TXEND (1<<3) +#define GT64260_SDMA_1_CAUSE_RXBUF (1<<8) +#define GT64260_SDMA_1_CAUSE_RXERR (1<<9) +#define GT64260_SDMA_1_CAUSE_TXBUF (1<<10) +#define GT64260_SDMA_1_CAUSE_TXEND (1<<11) + + +/* + ***************************************************************************** + * + * Baud Rate Generator Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_BRG_0_BCR 0xb200 +#define GT64260_BRG_0_BTR 0xb204 +#define GT64260_BRG_1_BCR 0xb208 +#define GT64260_BRG_1_BTR 0xb20c +#define GT64260_BRG_2_BCR 0xb210 +#define GT64260_BRG_2_BTR 0xb214 + +#define GT64260_BRG_INTR_CAUSE 0xb834 +#define GT64260_BRG_INTR_MASK 0xb8b4 + + +/* + ***************************************************************************** + * + * Watchdog Timer Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_WDT_WDC 0xb410 +#define GT64260_WDT_WDV 0xb414 + + +/* + ***************************************************************************** + * + * General Purpose Pins Controller Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_GPP_IO_CNTL 0xf100 +#define GT64260_GPP_LEVEL_CNTL 0xf110 +#define GT64260_GPP_VALUE 0xf104 +#define GT64260_GPP_INTR_CAUSE 0xf108 +#define GT64260_GPP_INTR_MASK 0xf10c + + +/* + ***************************************************************************** + * + * Multi-Purpose Pins Controller Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_MPP_CNTL_0 0xf000 +#define GT64260_MPP_CNTL_1 0xf004 +#define GT64260_MPP_CNTL_2 0xf008 +#define GT64260_MPP_CNTL_3 0xf00c +#define GT64260_MPP_SERIAL_PORTS_MULTIPLEX 0xf010 + + +/* + ***************************************************************************** + * + * I2C Controller Interface Registers + * + ***************************************************************************** + */ + +/* FIXME: fill in */ + + +/* + ***************************************************************************** + * + * Interrupt Controller Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_IC_MAIN_CAUSE_LO 0x0c18 +#define GT64260_IC_MAIN_CAUSE_HI 0x0c68 +#define GT64260_IC_CPU_INTR_MASK_LO 0x0c1c +#define GT64260_IC_CPU_INTR_MASK_HI 0x0c6c +#define GT64260_IC_CPU_SELECT_CAUSE 0x0c70 +#define GT64260_IC_PCI_0_INTR_MASK_LO 0x0c24 +#define GT64260_IC_PCI_0_INTR_MASK_HI 0x0c64 +#define GT64260_IC_PCI_0_SELECT_CAUSE 0x0c74 +#define GT64260_IC_PCI_1_INTR_MASK_LO 0x0ca4 +#define GT64260_IC_PCI_1_INTR_MASK_HI 0x0ce4 +#define GT64260_IC_PCI_1_SELECT_CAUSE 0x0cf4 +#define GT64260_IC_CPU_INT_0_MASK 0x0e60 +#define GT64260_IC_CPU_INT_1_MASK 0x0e64 +#define GT64260_IC_CPU_INT_2_MASK 0x0e68 +#define GT64260_IC_CPU_INT_3_MASK 0x0e6c + + +#endif /* __ASMPPC_GT64260_DEFS_H */ diff -Nru a/include/asm-ppc/hardirq.h b/include/asm-ppc/hardirq.h --- a/include/asm-ppc/hardirq.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/hardirq.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.hardirq.h 1.12 07/10/01 11:26:58 trini + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef __ASM_HARDIRQ_H @@ -44,6 +44,7 @@ #define hardirq_exit(cpu) (local_irq_count(cpu)--) #define synchronize_irq() do { } while (0) +#define release_irqlock(cpu) do { } while (0) #else /* CONFIG_SMP */ @@ -51,7 +52,16 @@ extern unsigned char global_irq_holder; extern unsigned volatile long global_irq_lock; -extern atomic_t global_irq_count; + +static inline int irqs_running (void) +{ + int i; + + for (i = 0; i < smp_num_cpus; i++) + if (local_irq_count(i)) + return 1; + return 0; +} static inline void release_irqlock(int cpu) { @@ -67,7 +77,6 @@ unsigned int loops = 10000000; ++local_irq_count(cpu); - atomic_inc(&global_irq_count); while (test_bit(0,&global_irq_lock)) { if (cpu == global_irq_holder) { printk("uh oh, interrupt while we hold global irq lock! (CPU %d)\n", cpu); @@ -87,13 +96,12 @@ static inline void hardirq_exit(int cpu) { - atomic_dec(&global_irq_count); --local_irq_count(cpu); } static inline int hardirq_trylock(int cpu) { - return !atomic_read(&global_irq_count) && !test_bit(0,&global_irq_lock); + return !test_bit(0,&global_irq_lock); } #define hardirq_endlock(cpu) do { } while (0) diff -Nru a/include/asm-ppc/harrier.h b/include/asm-ppc/harrier.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/harrier.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,102 @@ +/* + * arch/ppc/kernel/harrier.h + * + * Definitions for Motorola MCG Harrier North Bridge & Memory controller + * + * Author: Dale Farnsworth + * dale.farnsworth@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __ASMPPC_HARRIER_H +#define __ASMPPC_HARRIER_H + +#include + +#define HARRIER_VEND_DEV_ID 0x480b1057 + +/* + * Define outbound register offsets. + */ +#define HARRIER_OTAD0_OFF 0x220 +#define HARRIER_OTOF0_OFF 0x224 +#define HARRIER_OTAD1_OFF 0x228 +#define HARRIER_OTOF1_OFF 0x22c +#define HARRIER_OTAD2_OFF 0x230 +#define HARRIER_OTOF2_OFF 0x234 +#define HARRIER_OTAD3_OFF 0x238 +#define HARRIER_OTOF3_OFF 0x23c + +/* + * Define inbound register offsets. + */ +#define HARRIER_ITSZ0_OFF 0x348 +#define HARRIER_ITSZ1_OFF 0x350 +#define HARRIER_ITSZ2_OFF 0x358 +#define HARRIER_ITSZ3_OFF 0x360 + +/* + * Define the Memory Controller register offsets. + */ +#define HARRIER_SDBA_OFF 0x110 +#define HARRIER_SDBB_OFF 0x114 +#define HARRIER_SDBC_OFF 0x118 +#define HARRIER_SDBD_OFF 0x11c +#define HARRIER_SDBE_OFF 0x120 +#define HARRIER_SDBF_OFF 0x124 +#define HARRIER_SDBG_OFF 0x128 +#define HARRIER_SDBH_OFF 0x12c + +#define HARRIER_SDB_ENABLE 0x00000100 +#define HARRIER_SDB_SIZE_MASK 0xf +#define HARRIER_SDB_SIZE_SHIFT 16 +#define HARRIER_SDB_BASE_MASK 0xff +#define HARRIER_SDB_BASE_SHIFT 24 + +#define HARRIER_SERIAL_0_OFF 0xc0 + +#define HARRIER_REVI_OFF 0x05 +#define HARRIER_UCTL_OFF 0xd0 +#define HARRIER_XTAL64_MASK 0x02 + +#define HARRIER_MISC_CSR_OFF 0x1c +#define HARRIER_RSTOUT_MASK 0x01 + +#define HARRIER_MBAR_OFF 0xe0 +#define HARRIER_MPIC_CSR_OFF 0xe4 +#define HARRIER_MPIC_OPI_ENABLE 0x40 +#define HARRIER_MPIC_IFEVP_OFF 0x10200 +#define HARRIER_MPIC_IFEDE_OFF 0x10210 +#define HARRIER_FEEN_OFF 0x40 +#define HARRIER_FEST_OFF 0x44 +#define HARRIER_FEMA_OFF 0x48 + +#define HARRIER_FE_DMA 0x80 +#define HARRIER_FE_MIDB 0x40 +#define HARRIER_FE_MIM0 0x20 +#define HARRIER_FE_MIM1 0x10 +#define HARRIER_FE_MIP 0x08 +#define HARRIER_FE_UA0 0x04 +#define HARRIER_FE_UA1 0x02 +#define HARRIER_FE_ABT 0x01 + + +int harrier_init(struct pci_controller *hose, + uint ppc_reg_base, + ulong processor_pci_mem_start, + ulong processor_pci_mem_end, + ulong processor_pci_io_start, + ulong processor_pci_io_end, + ulong processor_mpic_base); + +unsigned long harrier_get_mem_size(uint smc_base); + +int harrier_mpic_init(unsigned int pci_mem_offset); + +#endif /* __ASMPPC_HARRIER_H */ diff -Nru a/include/asm-ppc/heathrow.h b/include/asm-ppc/heathrow.h --- a/include/asm-ppc/heathrow.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/heathrow.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.heathrow.h 1.7 05/17/01 18:14:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * heathrow.h: definitions for using the "Heathrow" I/O controller chip. @@ -17,7 +17,9 @@ #define HEATHROW_CONTRAST_CNTL 0x33 /* offset from ohare base for feature control register */ -#define HEATHROW_FEATURE_REG 0x38 +#define HEATHROW_MBCR 0x34 /* Media bay control */ +#define HEATHROW_FCR 0x38 /* Feature control */ +#define HEATHROW_AUX_CNTL_REG 0x3c /* Aux control */ /* * Bits in feature control register. @@ -30,6 +32,7 @@ #define HRW_BAY_FLOPPY_ENABLE 0x00000010 #define HRW_IDE0_ENABLE 0x00000020 #define HRW_IDE0_RESET_N 0x00000040 +#define HRW_BAY_DEV_MASK 0x0000001c #define HRW_BAY_RESET_N 0x00000080 #define HRW_IOBUS_ENABLE 0x00000100 /* Internal IDE ? */ #define HRW_SCC_ENABLE 0x00000200 @@ -45,7 +48,7 @@ #define HRW_SWIM_CLONE_FLOPPY 0x00080000 /* ??? (0) */ #define HRW_AUD_RUN22 0x00100000 /* ??? (1) */ #define HRW_SCSI_LINK_MODE 0x00200000 /* Read ??? (1) */ -#define HRW_ARB_BYPASS 0x00400000 /* ??? (0 on main, 1 on gatwick) */ +#define HRW_ARB_BYPASS 0x00400000 /* Disable internal PCI arbitrer */ #define HRW_IDE1_RESET_N 0x00800000 /* Media bay */ #define HRW_SLOW_SCC_PCLK 0x01000000 /* ??? (0) */ #define HRW_MODEM_POWER_N 0x02000000 /* Used by internal modem on wallstreet */ @@ -60,3 +63,7 @@ /* Those seem to be different on paddington */ #define PADD_MODEM_POWER_N 0x00000001 /* modem power on paddington */ #define PADD_RESET_SCC 0x02000000 /* check this please */ + +/* Looks like Heathrow has some sort of GPIOs as well... */ +#define HRW_GPIO_MODEM_RESET 0x6d + diff -Nru a/include/asm-ppc/highmem.h b/include/asm-ppc/highmem.h --- a/include/asm-ppc/highmem.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/highmem.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.highmem.h 1.10 06/28/01 15:50:17 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * highmem.h: virtual kernel memory mappings for high memory @@ -44,13 +44,17 @@ * easily, subsequent pte tables have to be allocated in one physical * chunk of RAM. */ +#ifdef CONFIG_HIGHMEM_START_BOOL +#define PKMAP_BASE CONFIG_HIGHMEM_START +#else #define PKMAP_BASE (0xfe000000UL) +#endif /* CONFIG_HIGHMEM_START_BOOL */ #define LAST_PKMAP 1024 #define LAST_PKMAP_MASK (LAST_PKMAP-1) #define PKMAP_NR(virt) ((virt-PKMAP_BASE) >> PAGE_SHIFT) #define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT)) -#define KMAP_FIX_BEGIN (0xfe400000UL) +#define KMAP_FIX_BEGIN (PKMAP_BASE + 0x00400000UL) extern void *kmap_high(struct page *page); extern void kunmap_high(struct page *page); diff -Nru a/include/asm-ppc/hw_irq.h b/include/asm-ppc/hw_irq.h --- a/include/asm-ppc/hw_irq.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/hw_irq.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.hw_irq.h 1.10 05/17/01 18:14:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * Copyright (C) 1999 Cort Dougan @@ -10,7 +10,48 @@ extern unsigned long timer_interrupt_intercept; extern unsigned long do_IRQ_intercept; -int timer_interrupt(struct pt_regs *); +extern int timer_interrupt(struct pt_regs *); +extern void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); + +#define INLINE_IRQS + +#ifdef INLINE_IRQS + +#define mfmsr() ({unsigned int rval; \ + asm volatile("mfmsr %0" : "=r" (rval)); rval;}) +#define mtmsr(v) asm volatile("mtmsr %0" : : "r" (v)) + +#define __save_flags(flags) ((flags) = mfmsr()) +#define __restore_flags(flags) mtmsr(flags) + +static inline void __cli(void) +{ + unsigned long msr; + msr = mfmsr(); + mtmsr(msr & ~MSR_EE); + __asm__ __volatile__("": : :"memory"); +} + +static inline void __sti(void) +{ + unsigned long msr; + __asm__ __volatile__("": : :"memory"); + msr = mfmsr(); + mtmsr(msr | MSR_EE); +} + +static inline void __do_save_and_cli(unsigned long *flags) +{ + unsigned long msr; + msr = mfmsr(); + *flags = msr; + mtmsr(msr & ~MSR_EE); + __asm__ __volatile__("": : :"memory"); +} + +#define __save_and_cli(flags) __do_save_and_cli(&flags) + +#else extern void __sti(void); extern void __cli(void); @@ -20,6 +61,8 @@ #define __save_flags(flags) __save_flags_ptr((unsigned long *)&flags) #define __save_and_cli(flags) ({__save_flags(flags);__cli();}) + +#endif extern void do_lost_interrupts(unsigned long); diff -Nru a/include/asm-ppc/i8259.h b/include/asm-ppc/i8259.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/i8259.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,16 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ + +#ifndef _PPC_KERNEL_i8259_H +#define _PPC_KERNEL_i8259_H + +#include + +extern struct hw_interrupt_type i8259_pic; + +void i8259_init(long); +int i8259_irq(void); +int i8259_poll(void); + +#endif /* _PPC_KERNEL_i8259_H */ diff -Nru a/include/asm-ppc/iSeries/HvCall.h b/include/asm-ppc/iSeries/HvCall.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvCall.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,209 @@ +/* + * HvCall.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//=========================================================================== +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from the OS. +// +//=========================================================================== + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +#include + +//------------------------------------------------------------------- +// Constants +//------------------------------------------------------------------- +#ifndef _HVCALL_H +#define _HVCALL_H +/* +enum HvCall_ReturnCode +{ + HvCall_Good = 0, + HvCall_Partial = 1, + HvCall_NotOwned = 2, + HvCall_NotFreed = 3, + HvCall_UnspecifiedError = 4 +}; + +enum HvCall_TypeOfSIT +{ + HvCall_ReduceOnly = 0, + HvCall_Unconditional = 1 +}; + +enum HvCall_TypeOfYield +{ + HvCall_YieldTimed = 0, // Yield until specified time + HvCall_YieldToActive = 1, // Yield until all active procs have run + HvCall_YieldToProc = 2 // Yield until the specified processor has run +}; + +enum HvCall_InterruptMasks +{ + HvCall_MaskIPI = 0x00000001, + HvCall_MaskLpEvent = 0x00000002, + HvCall_MaskLpProd = 0x00000004, + HvCall_MaskTimeout = 0x00000008 +}; + +enum HvCall_VaryOffChunkRc +{ + HvCall_VaryOffSucceeded = 0, + HvCall_VaryOffWithdrawn = 1, + HvCall_ChunkInLoadArea = 2, + HvCall_ChunkInHPT = 3, + HvCall_ChunkNotAccessible = 4, + HvCall_ChunkInUse = 5 +}; +*/ + +/* Type of yield for HvCallBaseYieldProcessor */ +#define HvCall_YieldTimed 0 // Yield until specified time (tb) +#define HvCall_YieldToActive 1 // Yield until all active procs have run +#define HvCall_YieldToProc 2 // Yield until the specified processor has run + +/* interrupt masks for setEnabledInterrupts */ +#define HvCall_MaskIPI 0x00000001 +#define HvCall_MaskLpEvent 0x00000002 +#define HvCall_MaskLpProd 0x00000004 +#define HvCall_MaskTimeout 0x00000008 + +/* Log buffer formats */ +#define HvCall_LogBuffer_ASCII 0 +#define HvCall_LogBuffer_EBCDIC 1 + +#define HvCallBaseAckDeferredInts HvCallBase + 0 +#define HvCallBaseCpmPowerOff HvCallBase + 1 +#define HvCallBaseGetHwPatch HvCallBase + 2 +#define HvCallBaseReIplSpAttn HvCallBase + 3 +#define HvCallBaseSetASR HvCallBase + 4 +#define HvCallBaseSetASRAndRfi HvCallBase + 5 +#define HvCallBaseSetIMR HvCallBase + 6 +#define HvCallBaseSendIPI HvCallBase + 7 +#define HvCallBaseTerminateMachine HvCallBase + 8 +#define HvCallBaseTerminateMachineSrc HvCallBase + 9 +#define HvCallBaseProcessPlicInterrupts HvCallBase + 10 +#define HvCallBaseIsPrimaryCpmOrMsdIpl HvCallBase + 11 +#define HvCallBaseSetVirtualSIT HvCallBase + 12 +#define HvCallBaseVaryOffThisProcessor HvCallBase + 13 +#define HvCallBaseVaryOffMemoryChunk HvCallBase + 14 +#define HvCallBaseVaryOffInteractivePercentage HvCallBase + 15 +#define HvCallBaseSendLpProd HvCallBase + 16 +#define HvCallBaseSetEnabledInterrupts HvCallBase + 17 +#define HvCallBaseYieldProcessor HvCallBase + 18 +#define HvCallBaseVaryOffSharedProcUnits HvCallBase + 19 +#define HvCallBaseSetVirtualDecr HvCallBase + 20 +#define HvCallBaseClearLogBuffer HvCallBase + 21 +#define HvCallBaseGetLogBufferCodePage HvCallBase + 22 +#define HvCallBaseGetLogBufferFormat HvCallBase + 23 +#define HvCallBaseGetLogBufferLength HvCallBase + 24 +#define HvCallBaseReadLogBuffer HvCallBase + 25 +#define HvCallBaseSetLogBufferFormatAndCodePage HvCallBase + 26 +#define HvCallBaseWriteLogBuffer HvCallBase + 27 +#define HvCallBaseRouter28 HvCallBase + 28 +#define HvCallBaseRouter29 HvCallBase + 29 +#define HvCallBaseRouter30 HvCallBase + 30 +//===================================================================================== +static inline void HvCall_setVirtualDecr(void) +{ + // Ignore any error return codes - most likely means that the target value for the + // LP has been increased and this vary off would bring us below the new target. + HvCall0(HvCallBaseSetVirtualDecr); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//===================================================================== +static inline void HvCall_yieldProcessor(unsigned typeOfYield, u64 yieldParm) +{ + HvCall2( HvCallBaseYieldProcessor, typeOfYield, yieldParm ); +} +//===================================================================== +static inline void HvCall_setEnabledInterrupts(u64 enabledInterrupts) +{ + HvCall1(HvCallBaseSetEnabledInterrupts,enabledInterrupts); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} + +//===================================================================== +static inline void HvCall_clearLogBuffer(HvLpIndex lpindex) +{ + HvCall1(HvCallBaseClearLogBuffer,lpindex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} + +//===================================================================== +static inline u32 HvCall_getLogBufferCodePage(HvLpIndex lpindex) +{ + u32 retVal = HvCall1(HvCallBaseGetLogBufferCodePage,lpindex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} + +//===================================================================== +static inline int HvCall_getLogBufferFormat(HvLpIndex lpindex) +{ + int retVal = HvCall1(HvCallBaseGetLogBufferFormat,lpindex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} + +//===================================================================== +static inline u32 HvCall_getLogBufferLength(HvLpIndex lpindex) +{ + u32 retVal = HvCall1(HvCallBaseGetLogBufferLength,lpindex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} + +//===================================================================== +static inline void HvCall_setLogBufferFormatAndCodepage(int format, u32 codePage) +{ + HvCall2(HvCallBaseSetLogBufferFormatAndCodePage,format, codePage); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} + +//===================================================================== +int HvCall_readLogBuffer(HvLpIndex lpindex, void *buffer, u64 bufLen); +void HvCall_writeLogBuffer(const void *buffer, u64 bufLen); + +//===================================================================== +static inline void HvCall_sendIPI(struct Paca * targetPaca) +{ + HvCall1( HvCallBaseSendIPI, targetPaca->xPacaIndex ); +} + +//===================================================================== +static inline void HvCall_terminateMachineSrc(void) +{ + HvCall0( HvCallBaseTerminateMachineSrc ); +} + + +#endif // _HVCALL_H + diff -Nru a/include/asm-ppc/iSeries/HvCallCfg.h b/include/asm-ppc/iSeries/HvCallCfg.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvCallCfg.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,219 @@ +/* + * HvCallCfg.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//===================================================================================== +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from the OS. +// +//===================================================================================== + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +//------------------------------------------------------------------------------------- +// Constants +//------------------------------------------------------------------------------------- +#ifndef _HVCALLCFG_H +#define _HVCALLCFG_H + +enum HvCallCfg_ReqQual +{ + HvCallCfg_Cur = 0, + HvCallCfg_Init = 1, + HvCallCfg_Max = 2, + HvCallCfg_Min = 3 +}; + +#define HvCallCfgGetLps HvCallCfg + 0 +#define HvCallCfgGetActiveLpMap HvCallCfg + 1 +#define HvCallCfgGetLpVrmIndex HvCallCfg + 2 +#define HvCallCfgGetLpMinSupportedPlicVrmIndex HvCallCfg + 3 +#define HvCallCfgGetLpMinCompatablePlicVrmIndex HvCallCfg + 4 +#define HvCallCfgGetLpVrmName HvCallCfg + 5 +#define HvCallCfgGetSystemPhysicalProcessors HvCallCfg + 6 +#define HvCallCfgGetPhysicalProcessors HvCallCfg + 7 +#define HvCallCfgGetSystemMsChunks HvCallCfg + 8 +#define HvCallCfgGetMsChunks HvCallCfg + 9 +#define HvCallCfgGetInteractivePercentage HvCallCfg + 10 +#define HvCallCfgIsBusDedicated HvCallCfg + 11 +#define HvCallCfgGetBusOwner HvCallCfg + 12 +#define HvCallCfgGetBusAllocation HvCallCfg + 13 +#define HvCallCfgGetBusUnitOwner HvCallCfg + 14 +#define HvCallCfgGetBusUnitAllocation HvCallCfg + 15 +#define HvCallCfgGetVirtualBusPool HvCallCfg + 16 +#define HvCallCfgGetBusUnitInterruptProc HvCallCfg + 17 +#define HvCallCfgGetConfiguredBusUnitsForIntProc HvCallCfg + 18 +#define HvCallCfgGetRioSanBusPool HvCallCfg + 19 +#define HvCallCfgGetSharedPoolIndex HvCallCfg + 20 +#define HvCallCfgGetSharedProcUnits HvCallCfg + 21 +#define HvCallCfgGetNumProcsInSharedPool HvCallCfg + 22 +#define HvCallCfgRouter23 HvCallCfg + 23 +#define HvCallCfgRouter24 HvCallCfg + 24 +#define HvCallCfgRouter25 HvCallCfg + 25 +#define HvCallCfgRouter26 HvCallCfg + 26 +#define HvCallCfgRouter27 HvCallCfg + 27 +#define HvCallCfgGetMinRuntimeMsChunks HvCallCfg + 28 +#define HvCallCfgSetMinRuntimeMsChunks HvCallCfg + 29 +#define HvCallCfgGetVirtualLanIndexMap HvCallCfg + 30 +#define HvCallCfgGetLpExecutionMode HvCallCfg + 31 +#define HvCallCfgGetHostingLpIndex HvCallCfg + 32 + +//==================================================================== +static inline HvLpIndex HvCallCfg_getLps(void) +{ + HvLpIndex retVal = HvCall0(HvCallCfgGetLps); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline int HvCallCfg_isBusDedicated(u64 busIndex) +{ + int retVal = HvCall1(HvCallCfgIsBusDedicated,busIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpIndex HvCallCfg_getBusOwner(u64 busIndex) +{ + HvLpIndex retVal = HvCall1(HvCallCfgGetBusOwner,busIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpIndexMap HvCallCfg_getBusAllocation(u64 busIndex) +{ + HvLpIndexMap retVal = HvCall1(HvCallCfgGetBusAllocation,busIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpIndexMap HvCallCfg_getActiveLpMap(void) +{ + HvLpIndexMap retVal = HvCall0(HvCallCfgGetActiveLpMap); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpVirtualLanIndexMap HvCallCfg_getVirtualLanIndexMap(HvLpIndex lp) +{ + // This is a new function in V5R1 so calls to this on older + // hypervisors will return -1 + u64 retVal = HvCall1(HvCallCfgGetVirtualLanIndexMap, lp); + if(retVal == -1) + retVal = 0; + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getSystemMsChunks(void) +{ + u64 retVal = HvCall0(HvCallCfgGetSystemMsChunks); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getMsChunks(HvLpIndex lp,enum HvCallCfg_ReqQual qual) +{ + u64 retVal = HvCall2(HvCallCfgGetMsChunks,lp,qual); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getMinRuntimeMsChunks(HvLpIndex lp) +{ + // NOTE: This function was added in v5r1 so older hypervisors will return a -1 value + u64 retVal = HvCall1(HvCallCfgGetMinRuntimeMsChunks,lp); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_setMinRuntimeMsChunks(u64 chunks) +{ + u64 retVal = HvCall1(HvCallCfgSetMinRuntimeMsChunks,chunks); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getSystemPhysicalProcessors(void) +{ + u64 retVal = HvCall0(HvCallCfgGetSystemPhysicalProcessors); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getPhysicalProcessors(HvLpIndex lp,enum HvCallCfg_ReqQual qual) +{ + u64 retVal = HvCall2(HvCallCfgGetPhysicalProcessors,lp,qual); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getConfiguredBusUnitsForInterruptProc(HvLpIndex lp, + u16 hvLogicalProcIndex) +{ + u64 retVal = HvCall2(HvCallCfgGetConfiguredBusUnitsForIntProc,lp,hvLogicalProcIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; + +} +//================================================================== +static inline HvLpSharedPoolIndex HvCallCfg_getSharedPoolIndex(HvLpIndex lp) +{ + HvLpSharedPoolIndex retVal = + HvCall1(HvCallCfgGetSharedPoolIndex,lp); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; + +} +//================================================================== +static inline u64 HvCallCfg_getSharedProcUnits(HvLpIndex lp,enum HvCallCfg_ReqQual qual) +{ + u64 retVal = HvCall2(HvCallCfgGetSharedProcUnits,lp,qual); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; + +} +//================================================================== +static inline u64 HvCallCfg_getNumProcsInSharedPool(HvLpSharedPoolIndex sPI) +{ + u16 retVal = HvCall1(HvCallCfgGetNumProcsInSharedPool,sPI); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; + +} +//================================================================== +static inline HvLpIndex HvCallCfg_getHostingLpIndex(HvLpIndex lp) +{ + u64 retVal = HvCall1(HvCallCfgGetHostingLpIndex,lp); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; + +} + +#endif // _HVCALLCFG_H + diff -Nru a/include/asm-ppc/iSeries/HvCallEvent.h b/include/asm-ppc/iSeries/HvCallEvent.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvCallEvent.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,328 @@ +/* + * HvCallEvent.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//================================================================== +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from the OS. +// +//================================================================== + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include +#endif + +#ifndef _HVTYPES_H +#include +#endif + +#include + +//------------------------------------------------------------------- +// Constants +//------------------------------------------------------------------- +#ifndef _HVCALLEVENT_H +#define _HVCALLEVENT_H + +struct HvLpEvent; + +typedef u8 HvLpEvent_Type; +typedef u8 HvLpEvent_AckInd; +typedef u8 HvLpEvent_AckType; + +struct HvCallEvent_PackedParms +{ + u8 xAckType:1; + u8 xAckInd:1; + u8 xRsvd:1; + u8 xTargetLp:5; + u8 xType; + u16 xSubtype; + HvLpInstanceId xSourceInstId; + HvLpInstanceId xTargetInstId; +}; + +typedef u8 HvLpDma_Direction; +typedef u8 HvLpDma_AddressType; + +struct HvCallEvent_PackedDmaParms +{ + u8 xDirection:1; + u8 xLocalAddrType:1; + u8 xRemoteAddrType:1; + u8 xRsvd1:5; + HvLpIndex xRemoteLp; + u8 xType; + u8 xRsvd2; + HvLpInstanceId xLocalInstId; + HvLpInstanceId xRemoteInstId; +}; + +typedef u64 HvLpEvent_Rc; +typedef u64 HvLpDma_Rc; + +#define HvCallEventAckLpEvent HvCallEvent + 0 +#define HvCallEventCancelLpEvent HvCallEvent + 1 +#define HvCallEventCloseLpEventPath HvCallEvent + 2 +#define HvCallEventDmaBufList HvCallEvent + 3 +#define HvCallEventDmaSingle HvCallEvent + 4 +#define HvCallEventDmaToSp HvCallEvent + 5 +#define HvCallEventGetOverflowLpEvents HvCallEvent + 6 +#define HvCallEventGetSourceLpInstanceId HvCallEvent + 7 +#define HvCallEventGetTargetLpInstanceId HvCallEvent + 8 +#define HvCallEventOpenLpEventPath HvCallEvent + 9 +#define HvCallEventSetLpEventStack HvCallEvent + 10 +#define HvCallEventSignalLpEvent HvCallEvent + 11 +#define HvCallEventSignalLpEventParms HvCallEvent + 12 +#define HvCallEventSetInterLpQueueIndex HvCallEvent + 13 +#define HvCallEventSetLpEventQueueInterruptProc HvCallEvent + 14 +#define HvCallEventRouter15 HvCallEvent + 15 + +//====================================================================== +static inline void HvCallEvent_getOverflowLpEvents(u8 queueIndex) +{ + HvCall1(HvCallEventGetOverflowLpEvents,queueIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//====================================================================== +static inline void HvCallEvent_setInterLpQueueIndex(u8 queueIndex) +{ + HvCall1(HvCallEventSetInterLpQueueIndex,queueIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//====================================================================== +static inline void HvCallEvent_setLpEventStack(u8 queueIndex, + char * eventStackAddr, + u32 eventStackSize) +{ + u64 abs_addr; + abs_addr = virt_to_absolute_outline( (unsigned long) eventStackAddr ); + + HvCall3(HvCallEventSetLpEventStack, queueIndex, abs_addr, eventStackSize); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//====================================================================== +static inline void HvCallEvent_setLpEventQueueInterruptProc(u8 queueIndex, + u16 lpLogicalProcIndex) +{ + HvCall2(HvCallEventSetLpEventQueueInterruptProc,queueIndex,lpLogicalProcIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//===================================================================== +static inline HvLpEvent_Rc HvCallEvent_signalLpEvent(struct HvLpEvent* event) +{ + u64 abs_addr; + HvLpEvent_Rc retVal; + abs_addr = virt_to_absolute_outline( (unsigned long) event ); + retVal = (HvLpEvent_Rc)HvCall1(HvCallEventSignalLpEvent, abs_addr); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//===================================================================== +static inline HvLpEvent_Rc HvCallEvent_signalLpEventFast(HvLpIndex targetLp, + HvLpEvent_Type type, + u16 subtype, + HvLpEvent_AckInd ackInd, + HvLpEvent_AckType ackType, + HvLpInstanceId sourceInstanceId, + HvLpInstanceId targetInstanceId, + u64 correlationToken, + u64 eventData1, + u64 eventData2, + u64 eventData3, + u64 eventData4, + u64 eventData5) +{ + HvLpEvent_Rc retVal; + + // Pack the misc bits into a single Dword to pass to PLIC + union + { + struct HvCallEvent_PackedParms parms; + u64 dword; + } packed; + packed.parms.xAckType = ackType; + packed.parms.xAckInd = ackInd; + packed.parms.xRsvd = 0; + packed.parms.xTargetLp = targetLp; + packed.parms.xType = type; + packed.parms.xSubtype = subtype; + packed.parms.xSourceInstId = sourceInstanceId; + packed.parms.xTargetInstId = targetInstanceId; + + retVal = (HvLpEvent_Rc)HvCall7(HvCallEventSignalLpEventParms, + packed.dword, + correlationToken, + eventData1,eventData2, + eventData3,eventData4, + eventData5); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpEvent_Rc HvCallEvent_ackLpEvent(struct HvLpEvent* event) +{ + u64 abs_addr; + HvLpEvent_Rc retVal; + abs_addr = virt_to_absolute_outline( (unsigned long) event ); + + retVal = (HvLpEvent_Rc)HvCall1(HvCallEventAckLpEvent, abs_addr); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpEvent_Rc HvCallEvent_cancelLpEvent(struct HvLpEvent* event) +{ + u64 abs_addr; + HvLpEvent_Rc retVal; + abs_addr = virt_to_absolute_outline( (unsigned long) event ); + + retVal = (HvLpEvent_Rc)HvCall1(HvCallEventCancelLpEvent, abs_addr); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline HvLpInstanceId HvCallEvent_getSourceLpInstanceId(HvLpIndex targetLp, HvLpEvent_Type type) +{ + HvLpInstanceId retVal; + retVal = HvCall2(HvCallEventGetSourceLpInstanceId,targetLp,type); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline HvLpInstanceId HvCallEvent_getTargetLpInstanceId(HvLpIndex targetLp, HvLpEvent_Type type) +{ + HvLpInstanceId retVal; + retVal = HvCall2(HvCallEventGetTargetLpInstanceId,targetLp,type); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline void HvCallEvent_openLpEventPath(HvLpIndex targetLp, + HvLpEvent_Type type) +{ + HvCall2(HvCallEventOpenLpEventPath,targetLp,type); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//=================================================================== +static inline void HvCallEvent_closeLpEventPath(HvLpIndex targetLp, + HvLpEvent_Type type) +{ + HvCall2(HvCallEventCloseLpEventPath,targetLp,type); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//=================================================================== +static inline HvLpDma_Rc HvCallEvent_dmaBufList(HvLpEvent_Type type, + HvLpIndex remoteLp, + HvLpDma_Direction direction, + HvLpInstanceId localInstanceId, + HvLpInstanceId remoteInstanceId, + HvLpDma_AddressType localAddressType, + HvLpDma_AddressType remoteAddressType, + // Do these need to be converted to + // absolute addresses? + u64 localBufList, + u64 remoteBufList, + + u32 transferLength) +{ + HvLpDma_Rc retVal; + // Pack the misc bits into a single Dword to pass to PLIC + union + { + struct HvCallEvent_PackedDmaParms parms; + u64 dword; + } packed; + packed.parms.xDirection = direction; + packed.parms.xLocalAddrType = localAddressType; + packed.parms.xRemoteAddrType = remoteAddressType; + packed.parms.xRsvd1 = 0; + packed.parms.xRemoteLp = remoteLp; + packed.parms.xType = type; + packed.parms.xRsvd2 = 0; + packed.parms.xLocalInstId = localInstanceId; + packed.parms.xRemoteInstId = remoteInstanceId; + + retVal = (HvLpDma_Rc)HvCall4(HvCallEventDmaBufList, + packed.dword, + localBufList, + remoteBufList, + transferLength); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//================================================================= +static inline HvLpDma_Rc HvCallEvent_dmaSingle(HvLpEvent_Type type, + HvLpIndex remoteLp, + HvLpDma_Direction direction, + HvLpInstanceId localInstanceId, + HvLpInstanceId remoteInstanceId, + HvLpDma_AddressType localAddressType, + HvLpDma_AddressType remoteAddressType, + u64 localAddrOrTce, + u64 remoteAddrOrTce, + u32 transferLength) +{ + HvLpDma_Rc retVal; + // Pack the misc bits into a single Dword to pass to PLIC + union + { + struct HvCallEvent_PackedDmaParms parms; + u64 dword; + } packed; + packed.parms.xDirection = direction; + packed.parms.xLocalAddrType = localAddressType; + packed.parms.xRemoteAddrType = remoteAddressType; + packed.parms.xRsvd1 = 0; + packed.parms.xRemoteLp = remoteLp; + packed.parms.xType = type; + packed.parms.xRsvd2 = 0; + packed.parms.xLocalInstId = localInstanceId; + packed.parms.xRemoteInstId = remoteInstanceId; + + retVal = (HvLpDma_Rc)HvCall4(HvCallEventDmaSingle, + packed.dword, + localAddrOrTce, + remoteAddrOrTce, + transferLength); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//================================================================= +static inline HvLpDma_Rc HvCallEvent_dmaToSp(void* local, u32 remote, u32 length, HvLpDma_Direction dir) +{ + u64 abs_addr; + HvLpDma_Rc retVal; + abs_addr = virt_to_absolute_outline( (unsigned long) local ); + + retVal = (HvLpDma_Rc)HvCall4(HvCallEventDmaToSp, + abs_addr, + remote, + length, + dir); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//================================================================ + +#endif // _HVCALLEVENT_H + diff -Nru a/include/asm-ppc/iSeries/HvCallHpt.h b/include/asm-ppc/iSeries/HvCallHpt.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvCallHpt.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,137 @@ +/* + * HvCallHpt.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================ +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from the OS. +// +//============================================================================ + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +//------------------------------------------------------------------- +// Other Includes +//------------------------------------------------------------------- + +#ifndef _PPC_MMU_H +#include +#endif + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +#ifndef _HVCALLHPT_H +#define _HVCALLHPT_H + +#define HvCallHptGetHptAddress HvCallHpt + 0 +#define HvCallHptGetHptPages HvCallHpt + 1 +#define HvCallHptSetPp HvCallHpt + 5 +#define HvCallHptUpdate HvCallHpt + 7 +#define HvCallHptInvalidateNoSyncICache HvCallHpt + 8 +#define HvCallHptGet HvCallHpt + 11 +#define HvCallHptFindNextValid HvCallHpt + 12 +#define HvCallHptFindValid HvCallHpt + 13 +#define HvCallHptAddValidate HvCallHpt + 16 +#define HvCallHptInvalidateSetSwBitsGet HvCallHpt + 18 + + + +//============================================================================ +static inline u64 HvCallHpt_getHptAddress(void) +{ + u64 retval = HvCall0(HvCallHptGetHptAddress); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retval; +} +//============================================================================ +static inline u64 HvCallHpt_getHptPages(void) +{ + u64 retval = HvCall0(HvCallHptGetHptPages); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retval; +} +//============================================================================= +static inline void HvCallHpt_setPp(u32 hpteIndex, u8 value) +{ + HvCall2( HvCallHptSetPp, hpteIndex, value ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//============================================================================= +static inline void HvCallHpt_invalidateNoSyncICache(u32 hpteIndex) + +{ + HvCall1( HvCallHptInvalidateNoSyncICache, hpteIndex ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//============================================================================= +static inline u64 HvCallHpt_invalidateSetSwBitsGet(u32 hpteIndex, u8 bitson, u8 bitsoff ) + +{ + u64 compressedStatus; + compressedStatus = HvCall4( HvCallHptInvalidateSetSwBitsGet, hpteIndex, bitson, bitsoff, 1 ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return compressedStatus; +} +//============================================================================= +static inline u64 HvCallHpt_findValid( PTE *hpte, u64 vpn ) +{ + u64 retIndex = HvCall1Ret16( HvCallHptFindValid, hpte, vpn ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retIndex; +} +//============================================================================= +static inline u64 HvCallHpt_findNextValid( PTE *hpte, u32 hpteIndex, u8 bitson, u8 bitsoff ) +{ + u64 retIndex = HvCall3Ret16( HvCallHptFindNextValid, hpte, hpteIndex, bitson, bitsoff ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retIndex; +} +//============================================================================= +static inline void HvCallHpt_get( PTE *hpte, u32 hpteIndex ) +{ + HvCall2Ret16( HvCallHptFindValid, hpte, hpteIndex, 0 ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} + +//============================================================================ +static inline void HvCallHpt_addValidate( u32 hpteIndex, + u32 hBit, + PTE *hpte ) + +{ + HvCall4( HvCallHptAddValidate, hpteIndex, + hBit, (*((u64 *)hpte)), (*(((u64 *)hpte)+1)) ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} + + +//============================================================================= + +#endif // _HVCALLHPT_H + diff -Nru a/include/asm-ppc/iSeries/HvCallPci.h b/include/asm-ppc/iSeries/HvCallPci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvCallPci.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,689 @@ +/************************************************************************/ +/* Provides the Hypervisor PCI calls for iSeries Linux Parition. */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Jan 9, 2001 */ +/************************************************************************/ +//============================================================================ +// Header File Id +// Name______________: HvCallPci.H +// +// Description_______: +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from SLIC. +// +//============================================================================ + +//------------------------------------------------------------------- +// Forward declarations +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +//------------------------------------------------------------------- +// Other Includes +//------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +#ifndef _HVCALLPCI_H +#define _HVCALLPCI_H + +struct HvCallPci_DsaAddr { // make sure this struct size is 64-bits total + u16 busNumber; + u8 subBusNumber; + u8 deviceId; + u8 barNumber; + u8 reserved[3]; +}; + +struct HvCallPci_LoadReturn { + u64 rc; + u64 value; +}; + +enum HvCallPci_DeviceType {HvCallPci_NodeDevice = 1, + HvCallPci_SpDevice = 2, + HvCallPci_IopDevice = 3, + HvCallPci_BridgeDevice = 4, + HvCallPci_MultiFunctionDevice = 5, + HvCallPci_IoaDevice = 6 +}; + + +struct HvCallPci_DeviceInfo { + u32 deviceType; // See DeviceType enum for values +}; + +struct HvCallPci_BusUnitInfo { + u32 sizeReturned; // length of data returned + u32 deviceType; // see DeviceType enum for values +}; + +struct HvCallPci_BridgeInfo { + struct HvCallPci_BusUnitInfo busUnitInfo; // Generic bus unit info + u8 subBusNumber; // Bus number of secondary bus + u8 maxAgents; // Max idsels on secondary bus +}; + + +// Maximum BusUnitInfo buffer size. Provided for clients so they can allocate +// a buffer big enough for any type of bus unit. Increase as needed. +enum {HvCallPci_MaxBusUnitInfoSize = 128}; + +struct HvCallPci_BarParms { + u64 vaddr; + u64 raddr; + u64 size; + u64 protectStart; + u64 protectEnd; + u64 relocationOffset; + u64 pciAddress; + u64 reserved[3]; +}; + +enum HvCallPci_VpdType { + HvCallPci_BusVpd = 1, + HvCallPci_BusAdapterVpd = 2 +}; + +#define HvCallPciConfigLoad8 HvCallPci + 0 +#define HvCallPciConfigLoad16 HvCallPci + 1 +#define HvCallPciConfigLoad32 HvCallPci + 2 +#define HvCallPciConfigStore8 HvCallPci + 3 +#define HvCallPciConfigStore16 HvCallPci + 4 +#define HvCallPciConfigStore32 HvCallPci + 5 +#define HvCallPciEoi HvCallPci + 16 +#define HvCallPciGetBarParms HvCallPci + 18 +#define HvCallPciMaskFisr HvCallPci + 20 +#define HvCallPciUnmaskFisr HvCallPci + 21 +#define HvCallPciSetSlotReset HvCallPci + 25 +#define HvCallPciGetDeviceInfo HvCallPci + 27 +#define HvCallPciGetCardVpd HvCallPci + 28 +#define HvCallPciBarLoad8 HvCallPci + 40 +#define HvCallPciBarLoad16 HvCallPci + 41 +#define HvCallPciBarLoad32 HvCallPci + 42 +#define HvCallPciBarLoad64 HvCallPci + 43 +#define HvCallPciBarStore8 HvCallPci + 44 +#define HvCallPciBarStore16 HvCallPci + 45 +#define HvCallPciBarStore32 HvCallPci + 46 +#define HvCallPciBarStore64 HvCallPci + 47 +#define HvCallPciMaskInterrupts HvCallPci + 48 +#define HvCallPciUnmaskInterrupts HvCallPci + 49 +#define HvCallPciGetBusUnitInfo HvCallPci + 50 + +//============================================================================ +static inline u64 HvCallPci_configLoad8(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u8 *value) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + HvCall3Ret16(HvCallPciConfigLoad8, &retVal, *(u64 *)&dsa, offset, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *value = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_configLoad16(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u16 *value) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + HvCall3Ret16(HvCallPciConfigLoad16, &retVal, *(u64 *)&dsa, offset, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *value = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_configLoad32(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u32 *value) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + HvCall3Ret16(HvCallPciConfigLoad32, &retVal, *(u64 *)&dsa, offset, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *value = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_configStore8(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u8 value) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + retVal = HvCall4(HvCallPciConfigStore8, *(u64 *)&dsa, offset, value, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_configStore16(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u16 value) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + retVal = HvCall4(HvCallPciConfigStore16, *(u64 *)&dsa, offset, value, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_configStore32(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u32 value) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + retVal = HvCall4(HvCallPciConfigStore32, *(u64 *)&dsa, offset, value, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_barLoad8(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u8* valueParm) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + HvCall3Ret16(HvCallPciBarLoad8, &retVal, *(u64 *)&dsa, offsetParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *valueParm = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_barLoad16(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u16* valueParm) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + HvCall3Ret16(HvCallPciBarLoad16, &retVal, *(u64 *)&dsa, offsetParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *valueParm = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_barLoad32(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u32* valueParm) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + HvCall3Ret16(HvCallPciBarLoad32, &retVal, *(u64 *)&dsa, offsetParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *valueParm = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_barLoad64(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u64* valueParm) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + HvCall3Ret16(HvCallPciBarLoad64, &retVal, *(u64 *)&dsa, offsetParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *valueParm = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_barStore8(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u8 valueParm) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + retVal = HvCall4(HvCallPciBarStore8, *(u64 *)&dsa, offsetParm, valueParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_barStore16(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u16 valueParm) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + retVal = HvCall4(HvCallPciBarStore16, *(u64 *)&dsa, offsetParm, valueParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_barStore32(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u32 valueParm) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + retVal = HvCall4(HvCallPciBarStore32, *(u64 *)&dsa, offsetParm, valueParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_barStore64(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u64 valueParm) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + retVal = HvCall4(HvCallPciBarStore64, *(u64 *)&dsa, offsetParm, valueParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_eoi(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + HvCall1Ret16(HvCallPciEoi, &retVal, *(u64*)&dsa); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_getBarParms(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 parms, + u32 sizeofParms) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + retVal = HvCall3(HvCallPciGetBarParms, *(u64*)&dsa, parms, sizeofParms); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_maskFisr(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 fisrMask) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall2(HvCallPciMaskFisr, *(u64*)&dsa, fisrMask); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_unmaskFisr(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 fisrMask) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall2(HvCallPciUnmaskFisr, *(u64*)&dsa, fisrMask); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_setSlotReset(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 onNotOff) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall2(HvCallPciSetSlotReset, *(u64*)&dsa, onNotOff); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_getDeviceInfo(u16 busNumberParm, + u8 subBusParm, + u8 deviceNumberParm, + u64 parms, + u32 sizeofParms) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceNumberParm << 4; + + retVal = HvCall3(HvCallPciGetDeviceInfo, *(u64*)&dsa, parms, sizeofParms); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_maskInterrupts(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 interruptMask) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall2(HvCallPciMaskInterrupts, *(u64*)&dsa, interruptMask); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_unmaskInterrupts(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 interruptMask) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall2(HvCallPciUnmaskInterrupts, *(u64*)&dsa, interruptMask); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ + +static inline u64 HvCallPci_getBusUnitInfo(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 parms, + u32 sizeofParms) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall3(HvCallPciGetBusUnitInfo, *(u64*)&dsa, parms, sizeofParms); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ + +static inline int HvCallPci_getBusVpd(u16 busNumParm, u64 destParm, u16 sizeParm) { + int xRetSize; + u64 xRc = HvCall4(HvCallPciGetCardVpd, busNumParm, destParm, sizeParm, HvCallPci_BusVpd); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + if (xRc == -1) + xRetSize = -1; + else + xRetSize = xRc & 0xFFFF; + return xRetSize; +} +//============================================================================ + +static inline int HvCallPci_getBusAdapterVpd(u16 busNumParm, u64 destParm, u16 sizeParm) { + int xRetSize; + u64 xRc = HvCall4(HvCallPciGetCardVpd, busNumParm, destParm, sizeParm, HvCallPci_BusAdapterVpd); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + if (xRc == -1) + xRetSize = -1; + else + xRetSize = xRc & 0xFFFF; + return xRetSize; +} +//============================================================================ +#endif // _HVCALLPCI_H diff -Nru a/include/asm-ppc/iSeries/HvCallSc.h b/include/asm-ppc/iSeries/HvCallSc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvCallSc.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,53 @@ +/* + * HvCallSc.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _HVTYPES_H +#include +#endif + +#ifndef _HVCALLSC_H +#define _HVCALLSC_H + +#define HvCallBase 0x80000000 +#define HvCallCfg 0x80020000 +#define HvCallEvent 0x80030000 +#define HvCallHpt 0x80040000 +#define HvCallPci 0x80050000 +#define HvCallSm 0x80070000 +#define HvCallXm 0x80090000 + +u64 HvCall0( u32 ); +u64 HvCall1( u32, u64 ); +u64 HvCall2( u32, u64, u64 ); +u64 HvCall3( u32, u64, u64, u64 ); +u64 HvCall4( u32, u64, u64, u64, u64 ); +u64 HvCall5( u32, u64, u64, u64, u64, u64 ); +u64 HvCall6( u32, u64, u64, u64, u64, u64, u64 ); +u64 HvCall7( u32, u64, u64, u64, u64, u64, u64, u64 ); + +u64 HvCall0Ret16( u32, void * ); +u64 HvCall1Ret16( u32, void *, u64 ); +u64 HvCall2Ret16( u32, void *, u64, u64 ); +u64 HvCall3Ret16( u32, void *, u64, u64, u64 ); +u64 HvCall4Ret16( u32, void *, u64, u64, u64, u64 ); +u64 HvCall5Ret16( u32, void *, u64, u64, u64, u64, u64 ); +u64 HvCall6Ret16( u32, void *, u64, u64, u64, u64, u64, u64 ); +u64 HvCall7Ret16( u32, void *, u64, u64 ,u64 ,u64 ,u64 ,u64 ,u64 ); + +#endif /* _HVCALLSC_H */ diff -Nru a/include/asm-ppc/iSeries/HvCallSm.h b/include/asm-ppc/iSeries/HvCallSm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvCallSm.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,58 @@ +/* + * HvCallSm.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================ +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from the OS. +// +//============================================================================ + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +#ifndef _HVCALLSM_H +#define _HVCALLSM_H + +#define HvCallSmGet64BitsOfAccessMap HvCallSm + 11 + + +//============================================================================ +static inline u64 HvCallSm_get64BitsOfAccessMap( + HvLpIndex lpIndex, u64 indexIntoBitMap ) +{ + u64 retval = HvCall2(HvCallSmGet64BitsOfAccessMap, lpIndex, + indexIntoBitMap ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retval; +} +//============================================================================ +#endif // _HVCALLSM_H + diff -Nru a/include/asm-ppc/iSeries/HvCallXm.h b/include/asm-ppc/iSeries/HvCallXm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvCallXm.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,105 @@ +//============================================================================ +// Header File Id +// Name______________: HvCallXm.H +// +// Description_______: +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from SLIC. +// +//============================================================================ + +//------------------------------------------------------------------- +// Forward declarations +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +//------------------------------------------------------------------- +// Other Includes +//------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +#ifndef _HVCALLXM_H +#define _HVCALLXM_H + +#define HvCallXmGetTceTableParms HvCallXm + 0 +#define HvCallXmTestBus HvCallXm + 1 +#define HvCallXmConnectBusUnit HvCallXm + 2 +#define HvCallXmLoadTod HvCallXm + 8 +#define HvCallXmTestBusUnit HvCallXm + 9 +#define HvCallXmSetTce HvCallXm + 11 +#define HvCallXmSetTces HvCallXm + 13 + + + +//============================================================================ +static inline void HvCallXm_getTceTableParms(u64 cb) +{ + HvCall1(HvCallXmGetTceTableParms, cb); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//============================================================================ +static inline u64 HvCallXm_setTce(u64 tceTableToken, u64 tceOffset, u64 tce) +{ + u64 retval = HvCall3(HvCallXmSetTce, tceTableToken, tceOffset, tce ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retval; +} +//============================================================================ +static inline u64 HvCallXm_setTces(u64 tceTableToken, u64 tceOffset, u64 numTces, u64 tce1, u64 tce2, u64 tce3, u64 tce4) +{ + u64 retval = HvCall7(HvCallXmSetTces, tceTableToken, tceOffset, numTces, + tce1, tce2, tce3, tce4 ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retval; +} +//============================================================================= +static inline u64 HvCallXm_testBus(u16 busNumber) +{ + u64 retVal = HvCall1(HvCallXmTestBus, busNumber); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//===================================================================================== +static inline u64 HvCallXm_testBusUnit(u16 busNumber, u8 subBusNumber, u8 deviceId) { + u64 busUnitNumber = (subBusNumber << 8) | deviceId; + u64 retVal = HvCall2(HvCallXmTestBusUnit, busNumber, busUnitNumber); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//===================================================================================== +static inline u64 HvCallXm_connectBusUnit(u16 busNumber, u8 subBusNumber, u8 deviceId, + u64 interruptToken) +{ + u64 busUnitNumber = (subBusNumber << 8) | deviceId; + u64 queueIndex = 0; // HvLpConfig::mapDsaToQueueIndex(HvLpDSA(busNumber, xBoard, xCard)); + + u64 retVal = HvCall5(HvCallXmConnectBusUnit, busNumber, busUnitNumber, + interruptToken, 0, queueIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//===================================================================================== +static inline u64 HvCallXm_loadTod(void) +{ + u64 retVal = HvCall0(HvCallXmLoadTod); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//===================================================================================== + +#endif // _HVCALLXM_H + diff -Nru a/include/asm-ppc/iSeries/HvLpConfig.h b/include/asm-ppc/iSeries/HvLpConfig.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvLpConfig.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,292 @@ +/* + * HvLpConfig.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//=========================================================================== +// +// This file contains the interface to the LPAR configuration data +// to determine which resources should be allocated to each partition. +// +//=========================================================================== + +#ifndef _HVCALLCFG_H +#include "HvCallCfg.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +#ifndef _ITLPNACA_H +#include +#endif + +#ifndef _LPARDATA_H +#include +#endif + +#ifndef _HVLPCONFIG_H +#define _HVLPCONFIG_H + +//------------------------------------------------------------------- +// Constants +//------------------------------------------------------------------- + +extern HvLpIndex HvLpConfig_getLpIndex_outline(void); + +//=================================================================== +static inline HvLpIndex HvLpConfig_getLpIndex(void) +{ + return itLpNaca.xLpIndex; +} +//=================================================================== +static inline HvLpIndex HvLpConfig_getPrimaryLpIndex(void) +{ + return itLpNaca.xPrimaryLpIndex; +} +//================================================================= +static inline HvLpIndex HvLpConfig_getLps(void) +{ + return HvCallCfg_getLps(); +} +//================================================================= +static inline HvLpIndexMap HvLpConfig_getActiveLpMap(void) +{ + return HvCallCfg_getActiveLpMap(); +} +//================================================================= +static inline u64 HvLpConfig_getSystemMsMegs(void) +{ + return HvCallCfg_getSystemMsChunks() / HvChunksPerMeg; +} +//================================================================= +static inline u64 HvLpConfig_getSystemMsChunks(void) +{ + return HvCallCfg_getSystemMsChunks(); +} +//================================================================= +static inline u64 HvLpConfig_getSystemMsPages(void) +{ + return HvCallCfg_getSystemMsChunks() * HvPagesPerChunk; +} +//================================================================ +static inline u64 HvLpConfig_getMsMegs(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Cur) / HvChunksPerMeg; +} +//================================================================ +static inline u64 HvLpConfig_getMsChunks(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Cur); +} +//================================================================ +static inline u64 HvLpConfig_getMsPages(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Cur) * HvPagesPerChunk; +} +//================================================================ +static inline u64 HvLpConfig_getMinMsMegs(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Min) / HvChunksPerMeg; +} +//================================================================ +static inline u64 HvLpConfig_getMinMsChunks(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Min); +} +//================================================================ +static inline u64 HvLpConfig_getMinMsPages(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Min) * HvPagesPerChunk; +} +//================================================================ +static inline u64 HvLpConfig_getMinRuntimeMsMegs(void) +{ + return HvCallCfg_getMinRuntimeMsChunks(HvLpConfig_getLpIndex()) / HvChunksPerMeg; +} +//=============================================================== +static inline u64 HvLpConfig_getMinRuntimeMsChunks(void) +{ + return HvCallCfg_getMinRuntimeMsChunks(HvLpConfig_getLpIndex()); +} +//=============================================================== +static inline u64 HvLpConfig_getMinRuntimeMsPages(void) +{ + return HvCallCfg_getMinRuntimeMsChunks(HvLpConfig_getLpIndex()) * HvPagesPerChunk; +} +//=============================================================== +static inline u64 HvLpConfig_getMaxMsMegs(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Max) / HvChunksPerMeg; +} +//=============================================================== +static inline u64 HvLpConfig_getMaxMsChunks(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Max); +} +//=============================================================== +static inline u64 HvLpConfig_getMaxMsPages(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Max) * HvPagesPerChunk; +} +//=============================================================== +static inline u64 HvLpConfig_getInitMsMegs(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Init) / HvChunksPerMeg; +} +//=============================================================== +static inline u64 HvLpConfig_getInitMsChunks(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Init); +} +//=============================================================== +static inline u64 HvLpConfig_getInitMsPages(void) +{ return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Init) * HvPagesPerChunk; +} +//=============================================================== +static inline u64 HvLpConfig_getSystemPhysicalProcessors(void) +{ + return HvCallCfg_getSystemPhysicalProcessors(); +} +//=============================================================== +static inline u64 HvLpConfig_getSystemLogicalProcessors(void) +{ + return HvCallCfg_getSystemPhysicalProcessors() * (/*getPaca()->getSecondaryThreadCount() +*/ 1); +} +//=============================================================== +static inline u64 HvLpConfig_getNumProcsInSharedPool(HvLpSharedPoolIndex sPI) +{ + return HvCallCfg_getNumProcsInSharedPool(sPI); +} +//=============================================================== +static inline u64 HvLpConfig_getPhysicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Cur); +} +//=============================================================== +static inline u64 HvLpConfig_getLogicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Cur) * (/*getPaca()->getSecondaryThreadCount() +*/ 1); +} +//=============================================================== +static inline HvLpSharedPoolIndex HvLpConfig_getSharedPoolIndex(void) +{ + return HvCallCfg_getSharedPoolIndex(HvLpConfig_getLpIndex()); +} +//=============================================================== +static inline u64 HvLpConfig_getSharedProcUnits(void) +{ + return HvCallCfg_getSharedProcUnits(HvLpConfig_getLpIndex(),HvCallCfg_Cur); +} +//=============================================================== +static inline u64 HvLpConfig_getMinSharedProcUnits(void) +{ + return HvCallCfg_getSharedProcUnits(HvLpConfig_getLpIndex(),HvCallCfg_Min); +} +//=============================================================== +static inline u64 HvLpConfig_getMaxSharedProcUnits(void) +{ + return HvCallCfg_getSharedProcUnits(HvLpConfig_getLpIndex(),HvCallCfg_Max); +} +//=============================================================== +static inline u64 HvLpConfig_getMinPhysicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Min); +} +//=============================================================== +static inline u64 HvLpConfig_getMinLogicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Min) * (/*getPaca()->getSecondaryThreadCount() +*/ 1); +} +//=============================================================== +static inline u64 HvLpConfig_getMaxPhysicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Max); +} +//=============================================================== +static inline u64 HvLpConfig_getMaxLogicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Max) * (/*getPaca()->getSecondaryThreadCount() +*/ 1); +} +//=============================================================== +static inline u64 HvLpConfig_getInitPhysicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Init); +} +//=============================================================== +static inline u64 HvLpConfig_getInitLogicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Init) * (/*getPaca()->getSecondaryThreadCount() +*/ 1); +} +//================================================================ +static inline HvLpVirtualLanIndexMap HvLpConfig_getVirtualLanIndexMap(void) +{ + return HvCallCfg_getVirtualLanIndexMap(HvLpConfig_getLpIndex_outline()); +} +//=============================================================== +static inline HvLpVirtualLanIndexMap HvLpConfig_getVirtualLanIndexMapForLp(HvLpIndex lp) +{ + return HvCallCfg_getVirtualLanIndexMap(lp); +} +//================================================================ +static inline HvLpIndex HvLpConfig_getBusOwner(HvBusNumber busNumber) +{ + return HvCallCfg_getBusOwner(busNumber); +} +//=============================================================== +static inline int HvLpConfig_isBusDedicated(HvBusNumber busNumber) +{ + return HvCallCfg_isBusDedicated(busNumber); +} +//================================================================ +static inline HvLpIndexMap HvLpConfig_getBusAllocation(HvBusNumber busNumber) +{ + return HvCallCfg_getBusAllocation(busNumber); +} +//================================================================ +// returns the absolute real address of the load area +static inline u64 HvLpConfig_getLoadAddress(void) +{ + return itLpNaca.xLoadAreaAddr & 0x7fffffffffffffff; +} +//================================================================ +static inline u64 HvLpConfig_getLoadPages(void) +{ + return itLpNaca.xLoadAreaChunks * HvPagesPerChunk; +} +//================================================================ +static inline int HvLpConfig_isBusOwnedByThisLp(HvBusNumber busNumber) +{ + HvLpIndex busOwner = HvLpConfig_getBusOwner(busNumber); + return (busOwner == HvLpConfig_getLpIndex()); +} +//================================================================ +static inline int HvLpConfig_doLpsCommunicateOnVirtualLan(HvLpIndex lp1, HvLpIndex lp2) +{ + HvLpVirtualLanIndexMap virtualLanIndexMap1 = HvCallCfg_getVirtualLanIndexMap( lp1 ); + HvLpVirtualLanIndexMap virtualLanIndexMap2 = HvCallCfg_getVirtualLanIndexMap( lp2 ); + return ((virtualLanIndexMap1 & virtualLanIndexMap2) != 0); +} +//================================================================ +static inline HvLpIndex HvLpConfig_getHostingLpIndex(HvLpIndex lp) +{ + return HvCallCfg_getHostingLpIndex(lp); +} +//================================================================ + +#endif // _HVLPCONFIG_H diff -Nru a/include/asm-ppc/iSeries/HvLpEvent.h b/include/asm-ppc/iSeries/HvLpEvent.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvLpEvent.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,144 @@ +/* + * HvLpEvent.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//====================================================================== +// +// This file contains the class for HV events in the system. +// +//===================================================================== +#ifndef _HVLPEVENT_H +#define _HVLPEVENT_H + +#include +#include +#include +#ifndef _HVCALLEVENT_H +#include +#endif + + +//===================================================================== +// +// HvLpEvent is the structure for Lp Event messages passed between +// partitions through PLIC. +// +//===================================================================== + +struct HvEventFlags +{ + u8 xValid:1; // Indicates a valid request x00-x00 + u8 xRsvd1:4; // Reserved ... + u8 xAckType:1; // Immediate or deferred ... + u8 xAckInd:1; // Indicates if ACK required ... + u8 xFunction:1; // Interrupt or Acknowledge ... +}; + + +struct HvLpEvent +{ + struct HvEventFlags xFlags; // Event flags x00-x00 + u8 xType; // Type of message x01-x01 + u16 xSubtype; // Subtype for event x02-x03 + u8 xSourceLp; // Source LP x04-x04 + u8 xTargetLp; // Target LP x05-x05 + u8 xSizeMinus1; // Size of Derived class - 1 x06-x06 + u8 xRc; // RC for Ack flows x07-x07 + u16 xSourceInstanceId; // Source sides instance id x08-x09 + u16 xTargetInstanceId; // Target sides instance id x0A-x0B + union { + u32 xSubtypeData; // Data usable by the subtype x0C-x0F + u16 xSubtypeDataShort[2]; // Data as 2 shorts + u8 xSubtypeDataChar[4]; // Data as 4 chars + } x; + + u64 xCorrelationToken; // Unique value for source/type x10-x17 +}; + +// Lp Event handler function +typedef void (*LpEventHandler)(struct HvLpEvent *, struct pt_regs *); + +// Register a handler for an event type +// returns 0 on success +extern int HvLpEvent_registerHandler( HvLpEvent_Type eventType, LpEventHandler hdlr); + +// Unregister a handler for an event type +// returns 0 on success +// Unregister will fail if there are any paths open for the type +extern int HvLpEvent_unregisterHandler( HvLpEvent_Type eventType ); + +// Open an Lp Event Path for an event type +// returns 0 on success +// openPath will fail if there is no handler registered for the event type. +// The lpIndex specified is the partition index for the target partition +// (for VirtualIo, VirtualLan and SessionMgr) other types specify zero) +extern int HvLpEvent_openPath( HvLpEvent_Type eventType, HvLpIndex lpIndex ); + + +// Close an Lp Event Path for a type and partition +// returns 0 on sucess +extern int HvLpEvent_closePath( HvLpEvent_Type eventType, HvLpIndex lpIndex ); + +#define HvLpEvent_Type_Hypervisor 0 +#define HvLpEvent_Type_MachineFac 1 +#define HvLpEvent_Type_SessionMgr 2 +#define HvLpEvent_Type_SpdIo 3 +#define HvLpEvent_Type_VirtualBus 4 +#define HvLpEvent_Type_PciIo 5 +#define HvLpEvent_Type_RioIo 6 +#define HvLpEvent_Type_VirtualLan 7 +#define HvLpEvent_Type_VirtualIo 8 +#define HvLpEvent_Type_NumTypes 9 + +#define HvLpEvent_Rc_Good 0 +#define HvLpEvent_Rc_BufferNotAvailable 1 +#define HvLpEvent_Rc_Cancelled 2 +#define HvLpEvent_Rc_GenericError 3 +#define HvLpEvent_Rc_InvalidAddress 4 +#define HvLpEvent_Rc_InvalidPartition 5 +#define HvLpEvent_Rc_InvalidSize 6 +#define HvLpEvent_Rc_InvalidSubtype 7 +#define HvLpEvent_Rc_InvalidSubtypeData 8 +#define HvLpEvent_Rc_InvalidType 9 +#define HvLpEvent_Rc_PartitionDead 10 +#define HvLpEvent_Rc_PathClosed 11 +#define HvLpEvent_Rc_SubtypeError 12 + +#define HvLpEvent_Function_Ack 0 +#define HvLpEvent_Function_Int 1 + +#define HvLpEvent_AckInd_NoAck 0 +#define HvLpEvent_AckInd_DoAck 1 + +#define HvLpEvent_AckType_ImmediateAck 0 +#define HvLpEvent_AckType_DeferredAck 1 + +#define HvLpDma_Direction_LocalToRemote 0 +#define HvLpDma_Direction_RemoteToLocal 1 + +#define HvLpDma_AddressType_TceIndex 0 +#define HvLpDma_AddressType_RealAddress 1 + +#define HvLpDma_Rc_Good 0 +#define HvLpDma_Rc_Error 1 +#define HvLpDma_Rc_PartitionDead 2 +#define HvLpDma_Rc_PathClosed 3 +#define HvLpDma_Rc_InvalidAddress 4 +#define HvLpDma_Rc_InvalidLength 5 + +#endif // _HVLPEVENT_H diff -Nru a/include/asm-ppc/iSeries/HvReleaseData.h b/include/asm-ppc/iSeries/HvReleaseData.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvReleaseData.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,71 @@ +/* + * HvReleaseData.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================= +// +// This control block contains the critical information about the +// release so that it can be changed in the future (ie, the virtual +// address of the OS's NACA). +// +//----------------------------------------------------------------------------- +// Standard Includes +//----------------------------------------------------------------------------- +#ifndef _PPC_TYPES_H +#include +#endif + +#ifndef _HVRELEASEDATA_H +#define _HVRELEASEDATA_H + +//============================================================================= +// +// When we IPL a secondary partition, we will check if if the +// secondary xMinPlicVrmIndex > the primary xVrmIndex. +// If it is then this tells PLIC that this secondary is not +// supported running on this "old" of a level of PLIC. +// +// Likewise, we will compare the primary xMinSlicVrmIndex to +// the secondary xVrmIndex. +// If the primary xMinSlicVrmDelta > secondary xVrmDelta then we +// know that this PLIC does not support running an OS "that old". +// +//============================================================================= + +struct HvReleaseData +{ + u32 xDesc; // Descriptor "HvRD" ebcdic x00-x03 + u16 xSize; // Size of this control block x04-x05 + u16 xVpdAreasPtrOffset; // Offset in NACA of ItVpdAreas x06-x07 + u32 xSlicNacaAddr64; // Virtual address of OS's NACA x08-x0F + struct Naca * xSlicNacaAddr; // Virtual address of OS's NACA x08-x0F + u32 xMsNucDataOffset; // Offset of Linux Mapping Data x10-x13 + u32 xRsvd1; // Reserved x14-x17 + u16 xTagsMode:1; // 0 == tags active, 1 == tags inactive + u16 xAddressSize:1; // 0 == 64-bit, 1 == 32-bit + u16 xNoSharedProcs:1; // 0 == shared procs, 1 == no shared + u16 xNoHMT:1; // 0 == allow HMT, 1 == no HMT + u16 xRsvd2:12; // Reserved x18-x19 + u16 xVrmIndex; // VRM Index of OS image x1A-x1B + u16 xMinSupportedPlicVrmIndex;// Min PLIC level (soft) x1C-x1D + u16 xMinCompatablePlicVrmIndex;// Min PLIC levelP (hard) x1E-x1F + char xVrmName[12]; // Displayable name x20-x2B + char xRsvd3[20]; // Reserved x2C-x3F +}; + +#endif // _HVRELEASEDATA_H diff -Nru a/include/asm-ppc/iSeries/HvTypes.h b/include/asm-ppc/iSeries/HvTypes.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/HvTypes.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,161 @@ +/* + * HvTypes.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//=========================================================================== +// Header File Id +// Name______________: HvTypes.H +// +// Description_______: +// +// General typedefs for the hypervisor. +// +// Declared Class(es): +// +//=========================================================================== + +#ifndef _PPC_TYPES_H +#include +#endif + + +#ifndef _HVTYPES_H +#define _HVTYPES_H + +//------------------------------------------------------------------- +// Typedefs +//------------------------------------------------------------------- +typedef u8 HvLpIndex; +typedef u16 HvLpInstanceId; +typedef u64 HvLpTOD; +typedef u64 HvLpSystemSerialNum; +typedef u8 HvLpDeviceSerialNum[12]; +typedef u16 HvLpSanHwSet; +typedef u16 HvLpBus; +typedef u16 HvLpBoard; +typedef u16 HvLpCard; +typedef u8 HvLpDeviceType[4]; +typedef u8 HvLpDeviceModel[3]; +typedef u64 HvIoToken; +typedef u8 HvLpName[8]; +typedef u32 HvIoId; +typedef u64 HvRealMemoryIndex; +typedef u32 HvLpIndexMap; // Must hold HvMaxArchitectedLps bits!!! +typedef u16 HvLpVrmIndex; +typedef u32 HvXmGenerationId; +typedef u8 HvLpBusPool; +typedef u8 HvLpSharedPoolIndex; +typedef u16 HvLpSharedProcUnitsX100; +typedef u8 HvLpVirtualLanIndex; +typedef u16 HvLpVirtualLanIndexMap; // Must hold HvMaxArchitectedVirtualLans bits!!! +typedef u16 HvBusNumber; // Hypervisor Bus Number +typedef u8 HvSubBusNumber; // Hypervisor SubBus Number +typedef u8 HvAgentId; // Hypervisor DevFn + + +#define HVMAXARCHITECTEDLPS 32 +#define HVCHUNKSIZE 256 * 1024 +#define HVPAGESIZE 4 * 1024 +#define HVLPMINMEGSPRIMARY 256 +#define HVLPMINMEGSSECONDARY 64 +#define HVCHUNKSPERMEG 4 +#define HVPAGESPERMEG 256 +#define HVPAGESPERCHUNK 64 + + +static const HvLpIndexMap HvLpIndexMapDefault = (1 << (sizeof(HvLpIndexMap) * 8 - 1)); +static const HvLpIndex HvHardcodedPrimaryLpIndex = 0; +static const HvLpIndex HvMaxArchitectedLps = HVMAXARCHITECTEDLPS; +static const HvLpVirtualLanIndex HvMaxArchitectedVirtualLans = 16; +static const HvLpSharedPoolIndex HvMaxArchitectedSharedPools = 16; +static const HvLpSharedPoolIndex HvMaxSupportedSharedPools = 1; +static const HvLpIndex HvMaxRuntimeLpsPreCondor = 12; +static const HvLpIndex HvMaxRuntimeLps = HVMAXARCHITECTEDLPS; +static const HvLpIndex HvLpIndexInvalid = 0xff; +static const u16 HvInvalidProcIndex = 0xffff; +static const u32 HvVirtualFlashSize = 0x200; +static const u32 HvMaxBusesPreCondor = 32; +static const u32 HvMaxBusesCondor = 256; +static const u32 HvMaxArchitectedBuses = 512; +static const HvLpBus HvCspBusNumber = 1; +static const u32 HvMaxSanHwSets = 16; +static const HvLpCard HvMaxSystemIops = 200; +static const HvLpCard HvMaxBusIops = 20; +static const u16 HvMaxUnitsPerIop = 100; +static const u64 HvPageSize = 4 * 1024; +static const u64 HvChunkSize = HVCHUNKSIZE; +static const u64 HvChunksPerMeg = HVCHUNKSPERMEG; +static const u64 HvPagesPerChunk = HVPAGESPERCHUNK; +static const u64 HvPagesPerMeg = HVPAGESPERMEG; +static const u64 HvLpMinMegsPrimary = HVLPMINMEGSPRIMARY; +static const u64 HvLpMinMegsSecondary = HVLPMINMEGSSECONDARY; +static const u64 HvLpMinChunksPrimary = HVLPMINMEGSPRIMARY * HVCHUNKSPERMEG; +static const u64 HvLpMinChunksSecondary = HVLPMINMEGSSECONDARY * HVCHUNKSPERMEG; +static const u64 HvLpMinPagesPrimary = HVLPMINMEGSPRIMARY * HVPAGESPERMEG; +static const u64 HvLpMinPagesSecondary = HVLPMINMEGSSECONDARY * HVPAGESPERMEG; +static const u8 HvLpMinProcs = 1; +static const u8 HvLpConfigMinInteract = 1; +static const u16 HvLpMinSharedProcUnitsX100 = 10; +static const u16 HvLpMaxSharedProcUnitsX100 = 100; +static const HvLpSharedPoolIndex HvLpSharedPoolIndexInvalid = 0xff; + + +//-------------------------------------------------------------------- +// Enums for the sub-components under PLIC +// Used in HvCall and HvPrimaryCall +//-------------------------------------------------------------------- +enum HvCallCompIds +{ + HvCallCompId = 0, + HvCallCpuCtlsCompId = 1, + HvCallCfgCompId = 2, + HvCallEventCompId = 3, + HvCallHptCompId = 4, + HvCallPciCompId = 5, + HvCallSlmCompId = 6, + HvCallSmCompId = 7, + HvCallSpdCompId = 8, + HvCallXmCompId = 9, + HvCallRioCompId = 10, + HvCallRsvd3CompId = 11, + HvCallRsvd2CompId = 12, + HvCallRsvd1CompId = 13, + HvCallMaxCompId = 14, + HvPrimaryCallCompId = 0, + HvPrimaryCallCfgCompId = 1, + HvPrimaryCallPciCompId = 2, + HvPrimaryCallSmCompId = 3, + HvPrimaryCallSpdCompId = 4, + HvPrimaryCallXmCompId = 5, + HvPrimaryCallRioCompId = 6, + HvPrimaryCallRsvd7CompId = 7, + HvPrimaryCallRsvd6CompId = 8, + HvPrimaryCallRsvd5CompId = 9, + HvPrimaryCallRsvd4CompId = 10, + HvPrimaryCallRsvd3CompId = 11, + HvPrimaryCallRsvd2CompId = 12, + HvPrimaryCallRsvd1CompId = 13, + HvPrimaryCallMaxCompId = HvCallMaxCompId +}; + +struct HvLpBufferList { + u64 addr; + u64 len; +}; + +#endif // _HVTYPES_H diff -Nru a/include/asm-ppc/iSeries/IoHriMainStore.h b/include/asm-ppc/iSeries/IoHriMainStore.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/IoHriMainStore.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,65 @@ +/* + * IoHriMainStore.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _IOHRIMAINSTORE_H +#define _IOHRIMAINSTORE_H + +struct IoHriMainStoreSegment4 { + u8 msArea0Exists:1; + u8 msArea1Exists:1; + u8 msArea2Exists:1; + u8 msArea3Exists:1; + u8 reserved1:4; + u8 reserved2; + + u8 msArea0Functional:1; + u8 msArea1Functional:1; + u8 msArea2Functional:1; + u8 msArea3Functional:1; + u8 reserved3:4; + u8 reserved4; + + u32 totalMainStore; + + u64 msArea0Ptr; + u64 msArea1Ptr; + u64 msArea2Ptr; + u64 msArea3Ptr; + + u32 cardProductionLevel; + + u32 msAdrHole; + + u8 msArea0HasRiserVpd:1; + u8 msArea1HasRiserVpd:1; + u8 msArea2HasRiserVpd:1; + u8 msArea3HasRiserVpd:1; + u8 reserved5:4; + u8 reserved6; + u16 reserved7; + + u8 reserved8[28]; + + u64 nonInterleavedBlocksStartAdr; + u64 nonInterleavedBlocksEndAdr; +}; + + +#endif // _IOHRIMAINSTORE_H + diff -Nru a/include/asm-ppc/iSeries/IoHriProcessorVpd.h b/include/asm-ppc/iSeries/IoHriProcessorVpd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/IoHriProcessorVpd.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,90 @@ +/* + * IoHriProcessorVpd.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//=================================================================== +// +// This struct maps Processor Vpd that is DMAd to SLIC by CSP +// + +#ifndef _TYPES_H +#include +#endif + +#ifndef _IOHRIPROCESSORVPD_H +#define _IOHRIPROCESSORVPD_H + +struct IoHriProcessorVpd +{ + + u8 xFormat; // VPD format indicator x00-x00 + u8 xProcStatus:8; // Processor State x01-x01 + u8 xSecondaryThreadCount; // Secondary thread cnt x02-x02 + u8 xSrcType:1; // Src Type x03-x03 + u8 xSrcSoft:1; // Src stay soft ... + u8 xSrcParable:1; // Src parable ... + u8 xRsvd1:5; // Reserved ... + u16 xHvPhysicalProcIndex; // Hypervisor physical proc index04-x05 + u16 xRsvd2; // Reserved x06-x07 + u32 xHwNodeId; // Hardware node id x08-x0B + u32 xHwProcId; // Hardware processor id x0C-x0F + + u32 xTypeNum; // Card Type/CCIN number x10-x13 + u32 xModelNum; // Model/Feature number x14-x17 + u64 xSerialNum; // Serial number x18-x1F + char xPartNum[12]; // Book Part or FPU number x20-x2B + char xMfgID[4]; // Manufacturing ID x2C-x2F + + u32 xProcFreq; // Processor Frequency x30-x33 + u32 xTimeBaseFreq; // Time Base Frequency x34-x37 + + u32 xChipEcLevel; // Chip EC Levels x38-x3B + u32 xProcIdReg; // PIR SPR value x3C-x3F + u32 xPVR; // PVR value x40-x43 + u8 xRsvd3[12]; // Reserved x44-x4F + + u32 xInstCacheSize; // Instruction cache size in KB x50-x53 + u32 xInstBlockSize; // Instruction cache block size x54-x57 + u32 xDataCacheOperandSize; // Data cache operand size x58-x5B + u32 xInstCacheOperandSize; // Inst cache operand size x5C-x5F + + u32 xDataL1CacheSizeKB; // L1 data cache size in KB x60-x63 + u32 xDataL1CacheLineSize; // L1 data cache block size x64-x67 + u64 xRsvd4; // Reserved x68-x6F + + u32 xDataL2CacheSizeKB; // L2 data cache size in KB x70-x73 + u32 xDataL2CacheLineSize; // L2 data cache block size x74-x77 + u64 xRsvd5; // Reserved x78-x7F + + u32 xDataL3CacheSizeKB; // L3 data cache size in KB x80-x83 + u32 xDataL3CacheLineSize; // L3 data cache block size x84-x87 + u64 xRsvd6; // Reserved x88-x8F + + u64 xFruLabel; // Card Location Label x90-x97 + u8 xSlotsOnCard; // Slots on card (0=no slots) x98-x98 + u8 xPartLocFlag; // Location flag (0-pluggable 1-imbedded) x99-x99 + u16 xSlotMapIndex; // Index in slot map table x9A-x9B + u8 xSmartCardPortNo; // Smart card port number x9C-x9C + u8 xRsvd7; // Reserved x9D-x9D + u16 xFrameIdAndRackUnit; // Frame ID and rack unit adr x9E-x9F + + u8 xRsvd8[24]; // Reserved xA0-xB7 + + char xProcSrc[72]; // CSP format SRC xB8-xFF +}; +#endif // _IOHRIPROCESSORVPD_H diff -Nru a/include/asm-ppc/iSeries/ItIplParmsReal.h b/include/asm-ppc/iSeries/ItIplParmsReal.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/ItIplParmsReal.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,78 @@ +/* + * ItIplParmsReal.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================== +// +// This struct maps the IPL Parameters DMA'd from the SP. +// +// Warning: +// This data must map in exactly 64 bytes and match the architecture for +// the IPL parms +// +//============================================================================= + + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _PPC_TYPES_H +#include +#endif + +#ifndef _ITIPLPARMSREAL_H +#define _ITIPLPARMSREAL_H + +struct ItIplParmsReal +{ + u8 xFormat; // Defines format of IplParms x00-x00 + u8 xRsvd01:6; // Reserved x01-x01 + u8 xAlternateSearch:1; // Alternate search indicator ... + u8 xUaSupplied:1; // UA Supplied on programmed IPL ... + u8 xLsUaFormat; // Format byte for UA x02-x02 + u8 xRsvd02; // Reserved x03-x03 + u32 xLsUa; // LS UA x04-x07 + u32 xUnusedLsLid; // First OS LID to load x08-x0B + u16 xLsBusNumber; // LS Bus Number x0C-x0D + u8 xLsCardAdr; // LS Card Address x0E-x0E + u8 xLsBoardAdr; // LS Board Address x0F-x0F + u32 xRsvd03; // Reserved x10-x13 + u8 xSpcnPresent:1; // SPCN present x14-x14 + u8 xCpmPresent:1; // CPM present ... + u8 xRsvd04:6; // Reserved ... + u8 xRsvd05:4; // Reserved x15-x15 + u8 xKeyLock:4; // Keylock setting ... + u8 xRsvd06:6; // Reserved x16-x16 + u8 xIplMode:2; // Ipl mode (A|B|C|D) ... + u8 xHwIplType; // Fast v slow v slow EC HW IPL x17-x17 + u16 xCpmEnabledIpl:1; // CPM in effect when IPL initiated x18-x19 + u16 xPowerOnResetIpl:1; // Indicate POR condition ... + u16 xMainStorePreserved:1; // Main Storage is preserved ... + u16 xRsvd07:13; // Reserved ... + u16 xIplSource:16; // Ipl source x1A-x1B + u8 xIplReason:8; // Reason for this IPL x1C-x1C + u8 xRsvd08; // Reserved x1D-x1D + u16 xRsvd09; // Reserved x1E-x1F + u16 xSysBoxType; // System Box Type x20-x21 + u16 xSysProcType; // System Processor Type x22-x23 + u32 xRsvd10; // Reserved x24-x27 + u64 xRsvd11; // Reserved x28-x2F + u64 xRsvd12; // Reserved x30-x37 + u64 xRsvd13; // Reserved x38-x3F +}; +#endif // _ITIPLPARMSREAL_H diff -Nru a/include/asm-ppc/iSeries/ItLpNaca.h b/include/asm-ppc/iSeries/ItLpNaca.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/ItLpNaca.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,87 @@ +/* + * ItLpNaca.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================= +// +// This control block contains the data that is shared between the +// hypervisor (PLIC) and the OS. +// +//============================================================================= + + +#ifndef _ITLPNACA_H +#define _ITLPNACA_H + +struct ItLpNaca +{ +//============================================================================= +// CACHE_LINE_1 0x0000 - 0x007F Contains read-only data +//============================================================================= + u32 xDesc; // Eye catcher x00-x03 + u16 xSize; // Size of this class x04-x05 + u16 xIntHdlrOffset; // Offset to IntHdlr array x06-x07 + u8 xMaxIntHdlrEntries; // Number of entries in array x08-x08 + u8 xPrimaryLpIndex; // LP Index of Primary x09-x09 + u8 xServiceLpIndex; // LP Ind of Service Focal Pointx0A-x0A + u8 xLpIndex; // LP Index x0B-x0B + u16 xMaxLpQueues; // Number of allocated queues x0C-x0D + u16 xLpQueueOffset; // Offset to start of LP queues x0E-x0F + u8 xPirEnvironMode:8; // Piranha or hardware x10-x10 + u8 xPirConsoleMode:8; // Piranha console indicator x11-x11 + u8 xPirDasdMode:8; // Piranha dasd indicator x12-x12 + u8 xRsvd1_0[5]; // Reserved for Piranha related x13-x17 + u8 xLparInstalled:1; // Is LPAR installed on system x18-x1F + u8 xSysPartitioned:1; // Is the system partitioned ... + u8 xHwSyncedTBs:1; // Hardware synced TBs ... + u8 xIntProcUtilHmt:1; // Utilize HMT for interrupts ... + u8 xRsvd1_1:4; // Reserved ... + u8 xSpVpdFormat:8; // VPD areas are in CSP format ... + u8 xIntProcRatio:8; // Ratio of int procs to procs ... + u8 xRsvd1_2[5]; // Reserved ... + u16 xRsvd1_3; // Reserved x20-x21 + u16 xPlicVrmIndex; // VRM index of PLIC x22-x23 + u16 xMinSupportedSlicVrmInd;// Min supported OS VRM index x24-x25 + u16 xMinCompatableSlicVrmInd;// Min compatable OS VRM index x26-x27 + u64 xLoadAreaAddr; // ER address of load area x28-x2F + u32 xLoadAreaChunks; // Chunks for the load area x30-x33 + u32 xRsvd1_4; // x34-x37 + u8 xRsvd1_5[72]; // x38-x7F + +//============================================================================= +// CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data +//============================================================================= + u8 xRsvd2_0[128]; // Reserved x00-x7F + +//============================================================================= +// CACHE_LINE_3-6 0x0100 - 0x02FF Contains LP Queue indicators +// NB: Padding required to keep xInterrruptHdlr at x300 which is required +// for v4r4 PLIC. +//============================================================================= + u8 xOldLpQueue[128]; // LP Queue needed for v4r4 100-17F + u8 xRsvd3_0[384]; // Reserved 180-2FF +//============================================================================= +// CACHE_LINE_7-8 0x0300 - 0x03FF Contains the address of the OS interrupt +// handlers +//============================================================================= + u64 xInterruptHdlr[32]; // Interrupt handlers 300-x3FF +}; + +//============================================================================= + +#endif // _ITLPNACA_H diff -Nru a/include/asm-ppc/iSeries/ItLpPaca.h b/include/asm-ppc/iSeries/ItLpPaca.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/ItLpPaca.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,122 @@ +/* + * ItLpPaca.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================= +// +// This control block contains the data that is shared between the +// hypervisor (PLIC) and the OS. +// +// +//---------------------------------------------------------------------------- +#ifndef _PPC_TYPES_H +#include +#endif + +#ifndef _ITLPPACA_H +#define _ITLPPACA_H + + +struct ItLpPaca +{ +//============================================================================= +// CACHE_LINE_1 0x0000 - 0x007F Contains read-only data +// NOTE: The xDynXyz fields are fields that will be dynamically changed by +// PLIC when preparing to bring a processor online or when dispatching a +// virtual processor! +//============================================================================= + u32 xDesc; // Eye catcher 0xD397D781 x00-x03 + u16 xSize; // Size of this struct x04-x05 + u16 xRsvd1_0; // Reserved x06-x07 + u16 xRsvd1_1:14; // Reserved x08-x09 + u8 xSharedProc:1; // Shared processor indicator ... + u8 xSecondaryThread:1; // Secondary thread indicator ... + volatile u8 xDynProcStatus:8; // Dynamic Status of this proc x0A-x0A + u8 xSecondaryThreadCnt; // Secondary thread count x0B-x0B + volatile u16 xDynHvPhysicalProcIndex;// Dynamic HV Physical Proc Index0C-x0D + volatile u16 xDynHvLogicalProcIndex;// Dynamic HV Logical Proc Indexx0E-x0F + u32 xDecrVal; // Value for Decr programming x10-x13 + u32 xPMCVal; // Value for PMC regs x14-x17 + volatile u32 xDynHwNodeId; // Dynamic Hardware Node id x18-x1B + volatile u32 xDynHwProcId; // Dynamic Hardware Proc Id x1C-x1F + volatile u32 xDynPIR; // Dynamic ProcIdReg value x20-x23 + u32 xDseiData; // DSEI data x24-x27 + u64 xSPRG3; // SPRG3 value x28-x2F + u8 xRsvd1_3[80]; // Reserved x30-x7F + +//============================================================================= +// CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data +//============================================================================= + // This Dword contains a byte for each type of interrupt that can occur. + // The IPI is a count while the others are just a binary 1 or 0. + + u16 xRsvd; // Reserved - cleared by #mpasmbl + u8 xXirrInt; // Indicates xXirrValue is valid or Immed IO + u8 xIpiCnt; // IPI Count + u8 xDecrInt; // DECR interrupt occurred + u8 xPdcInt; // PDC interrupt occurred + u8 xQuantumInt; // Interrupt quantum reached + u8 xOldPlicDeferredExtInt; // Old PLIC has deferred XIRR pending + + u64 xPlicDeferIntsArea; + + // Used to pass the real SRR0/1 from PLIC to the OS as well as to + // pass the target SRR0/1 from the OS to PLIC on a SetAsrAndRfid. + u64 xSavedSrr0; // Saved SRR0 x10-x17 + u64 xSavedSrr1; // Saved SRR1 x18-x1F + + // Used to pass parms from the OS to PLIC for SetAsrAndRfid + u64 xSavedGpr3; // Saved GPR3 x20-x27 + u64 xSavedGpr4; // Saved GPR4 x28-x2F + u64 xSavedGpr5; // Saved GPR5 x30-x37 + + u8 xRsvd2_1; // Reserved x38-x38 + u8 xCpuCtlsTaskAttributes; // Task attributes for cpuctls x39-x39 + u8 xFPRegsInUse; // FP regs in use x3A-x3A + u8 xPMCRegsInUse; // PMC regs in use x3B-x3B + volatile u32 xSavedDecr; // Saved Decr Value x3C-x3F + volatile u64 xEmulatedTimeBase; // Emulated TB for this thread x40-x47 + volatile u64 xCurPLICLatency; // Unaccounted PLIC latency x48-x4F + u64 xTotPLICLatency; // Accumulated PLIC latency x50-x57 + u64 xWaitStateCycles; // Wait cycles for this proc x58-x5F + u64 xEndOfQuantum; // TB at end of quantum x60-x67 + u64 xPDCSavedSPRG1; // Saved SPRG1 for PMC int x68-x6F + u64 xPDCSavedSRR0; // Saved SRR0 for PMC int x70-x77 + volatile u32 xVirtualDecr; // Virtual DECR for shared procsx78-x7B + u32 xRsvd2_2; // Reserved x7C-x7F + +//============================================================================= +// CACHE_LINE_3 0x0100 - 0x007F: This line is shared with other processors +//============================================================================= + // This is the xYieldCount. An "odd" value (low bit on) means that + // the processor is yielded (either because of an OS yield or a PLIC + // preempt). An even value implies that the processor is currently + // executing. + // NOTE: This value will ALWAYS be zero for dedicated processors and + // will NEVER be zero for shared processors (ie, initialized to a 1). + volatile u32 xYieldCount; // PLIC increments each dispatchx00-x03 + u8 xRsvd3_0[124]; // Reserved x04-x7F + +//============================================================================= +// CACHE_LINE_4-5 0x0100 - 0x01FF Contains PMC interrupt data +//============================================================================= + u8 xPmcSaveArea[256]; // PMC interrupt Area x00-xFF + + +}; +#endif // _ITLPPACA_H diff -Nru a/include/asm-ppc/iSeries/ItLpQueue.h b/include/asm-ppc/iSeries/ItLpQueue.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/ItLpQueue.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,94 @@ +/* + * ItLpQueue.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================= +// +// This control block defines the simple LP queue structure that is +// shared between the hypervisor (PLIC) and the OS in order to send +// events to an LP. +// + +#ifndef _PPC_TYPES_H +#include +#endif +#include + + +struct HvLpEvent; + + +#ifndef _ITLPQUEUE_H +#define _ITLPQUEUE_H + +#define ITMaxLpQueues 8 + +#define NotUsed 0 // Queue will not be used by PLIC +#define DedicatedIo 1 // Queue dedicated to IO processor specified +#define DedicatedLp 2 // Queue dedicated to LP specified +#define Shared 3 // Queue shared for both IO and LP + +#define LpEventStackSize 4096 +#define LpEventMaxSize 256 +#define LpEventAlign 64 + +struct ItLpQueue +{ +// +// The xSlicCurEventPtr is the pointer to the next event stack entry that will +// become valid. The OS must peek at this entry to determine if it is valid. +// PLIC will set the valid indicator as the very last store into that entry. +// +// When the OS has completed processing of the event then it will mark the event +// as invalid so that PLIC knows it can store into that event location again. +// +// If the event stack fills and there are overflow events, then PLIC will set +// the xPlicOverflowIntPending flag in which case the OS will have to fetch the +// additional LP events once they have drained the event stack. +// +// The first 16-bytes are known by both the OS and PLIC. The remainder of the +// cache line is for use by the OS. +// +//============================================================================= + u8 xPlicOverflowIntPending; // 0x00 Overflow events are pending + u8 xPlicStatus; // 0x01 DedicatedIo or DedicatedLp or NotUsed + u16 xSlicLogicalProcIndex; // 0x02 Logical Proc Index for correlation + u8 xPlicRsvd[12]; // 0x04 + u32 xHSlicCurEventPtr; // 0x10 High 32 bits of ptr + char* xSlicCurEventPtr; // 0x14 Low 32 bits of ptr + u32 xHSlicLastValidEventPtr; // 0x18 High 32 bits of ptr + char* xSlicLastValidEventPtr; // 0x1C Low 32 bits of ptr + u32 xHSlicEventStackPtr; // 0x20 High 32 bits of ptr + char* xSlicEventStackPtr; // 0x24 Low 32 bits of ptr + u8 xIndex; // 0x28 unique sequential index. + u8 xSlicRsvd[3]; // 0x29-2B + u32 xInUseWord; // 0x2C + u64 xLpIntCount; // 0x30 Total Lp Int msgs processed + u64 xLpIntCountByType[9]; // 0x38-0x7F Event counts by type +}; + +extern struct ItLpQueue xItLpQueue; + +extern struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * ); +extern int ItLpQueue_isLpIntPending( struct ItLpQueue * ); +extern unsigned ItLpQueue_process( struct ItLpQueue *, struct pt_regs * ); +extern void ItLpQueue_clearValid( struct HvLpEvent * ); + + +//============================================================================= +#endif // _ITLPQUEUE_H diff -Nru a/include/asm-ppc/iSeries/ItLpRegSave.h b/include/asm-ppc/iSeries/ItLpRegSave.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/ItLpRegSave.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,87 @@ +/* + * ItLpRegSave.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//===================================================================================== +// +// This control block contains the data that is shared between PLIC +// and the OS +// +// + +#ifndef _ITLPREGSAVE_H +#define _ITLPREGSAVE_H + +struct ItLpRegSave +{ + u32 xDesc; // Eye catcher "LpRS" ebcdic 000-003 + u16 xSize; // Size of this class 004-005 + u8 xInUse; // Area is live 006-007 + u8 xRsvd1[9]; // Reserved 007-00F + + u8 xFixedRegSave[352]; // Fixed Register Save Area 010-16F + u32 xCTRL; // Control Register 170-173 + u32 xDEC; // Decrementer 174-177 + u32 xFPSCR; // FP Status and Control Reg 178-17B + u32 xPVR; // Processor Version Number 17C-17F + + u64 xMMCR0; // Monitor Mode Control Reg 0 180-187 + u32 xPMC1; // Perf Monitor Counter 1 188-18B + u32 xPMC2; // Perf Monitor Counter 2 18C-18F + u32 xPMC3; // Perf Monitor Counter 3 190-193 + u32 xPMC4; // Perf Monitor Counter 4 194-197 + u32 xPIR; // Processor ID Reg 198-19B + + u32 xMMCR1; // Monitor Mode Control Reg 1 19C-19F + u32 xMMCRA; // Monitor Mode Control Reg A 1A0-1A3 + u32 xPMC5; // Perf Monitor Counter 5 1A4-1A7 + u32 xPMC6; // Perf Monitor Counter 6 1A8-1AB + u32 xPMC7; // Perf Monitor Counter 7 1AC-1AF + u32 xPMC8; // Perf Monitor Counter 8 1B0-1B3 + u32 xTSC; // Thread Switch Control 1B4-1B7 + u32 xTST; // Thread Switch Timeout 1B8-1BB + u32 xRsvd; // Reserved 1BC-1BF + + u64 xACCR; // Address Compare Control Reg 1C0-1C7 + u64 xIMR; // Instruction Match Register 1C8-1CF + u64 xSDR1; // Storage Description Reg 1 1D0-1D7 + u64 xSPRG0; // Special Purpose Reg General0 1D8-1DF + u64 xSPRG1; // Special Purpose Reg General1 1E0-1E7 + u64 xSPRG2; // Special Purpose Reg General2 1E8-1EF + u64 xSPRG3; // Special Purpose Reg General3 1F0-1F7 + u64 xTB; // Time Base Register 1F8-1FF + + u64 xFPR[32]; // Floating Point Registers 200-2FF + + u64 xMSR; // Machine State Register 300-307 + u64 xNIA; // Next Instruction Address 308-30F + + u64 xDABR; // Data Address Breakpoint Reg 310-317 + u64 xIABR; // Inst Address Breakpoint Reg 318-31F + + u64 xHID0; // HW Implementation Dependent0 320-327 + + u64 xHID4; // HW Implementation Dependent4 328-32F + u64 xSCOMd; // SCON Data Reg (SPRG4) 330-337 + u64 xSCOMc; // SCON Command Reg (SPRG5) 338-33F + u64 xSDAR; // Sample Data Address Register 340-347 + u64 xSIAR; // Sample Inst Address Register 348-34F + + u8 xRsvd3[176]; // Reserved 350-3FF +}; +#endif // _ITLPREGSAVE_H diff -Nru a/include/asm-ppc/iSeries/ItSpCommArea.h b/include/asm-ppc/iSeries/ItSpCommArea.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/ItSpCommArea.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,39 @@ +/* + * ItSpCommArea.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _ITSPCOMMAREA_H +#define _ITSPCOMMAREA_H + + +struct SpCommArea +{ + u32 xDesc; // Descriptor (only in new formats) 000-003 + u8 xFormat; // Format (only in new formats) 004-004 + u8 xRsvd1[11]; // Reserved 005-00F + u64 xRawTbAtIplStart; // Raw HW TB value when IPL is started 010-017 + u64 xRawTodAtIplStart; // Raw HW TOD value when IPL is started 018-01F + u64 xBcdTimeAtIplStart; // BCD time when IPL is started 020-027 + u64 xBcdTimeAtOsStart; // BCD time when OS passed control 028-02F + u8 xRsvd2[80]; // Reserved 030-07F +}; + +extern struct SpCommArea xSpCommArea; + +#endif /* _ITSPCOMMAREA_H */ diff -Nru a/include/asm-ppc/iSeries/ItVpdAreas.h b/include/asm-ppc/iSeries/ItVpdAreas.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/ItVpdAreas.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,100 @@ +/* + * ItVpdAreas.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//===================================================================================== +// +// This file defines the address and length of all of the VPD area passed to +// the OS from PLIC (most of which start from the SP). +// + +#ifndef _PPC_TYPES_H +#include +#endif + + +#ifndef _ITVPDAREAS_H +#define _ITVPDAREAS_H + +// VPD Entry index is carved in stone - cannot be changed (easily). +#define ItVpdCecVpd 0 +#define ItVpdDynamicSpace 1 +#define ItVpdExtVpd 2 +#define ItVpdExtVpdOnPanel 3 +#define ItVpdFirstPaca 4 +#define ItVpdIoVpd 5 +#define ItVpdIplParms 6 +#define ItVpdMsVpd 7 +#define ItVpdPanelVpd 8 +#define ItVpdLpNaca 9 +#define ItVpdBackplaneAndMaybeClockCardVpd 10 +#define ItVpdRecoveryLogBuffer 11 +#define ItVpdSpCommArea 12 +#define ItVpdSpLogBuffer 13 +#define ItVpdSpLogBufferSave 14 +#define ItVpdSpCardVpd 15 +#define ItVpdFirstProcVpd 16 +#define ItVpdApModelVpd 17 +#define ItVpdClockCardVpd 18 +#define ItVpdBusExtCardVpd 19 +#define ItVpdProcCapacityVpd 20 +#define ItVpdInteractiveCapacityVpd 21 +#define ItVpdFirstSlotLabel 22 +#define ItVpdFirstLpQueue 23 +#define ItVpdFirstL3CacheVpd 24 +#define ItVpdFirstProcFruVpd 25 + +#define ItVpdMaxEntries 26 + + +#define ItDmaMaxEntries 10 + +#define ItVpdAreasMaxSlotLabels 192 + + +struct SlicVpdAdrs { + u32 pad1; + void * vpdAddr; +}; + + +struct ItVpdAreas +{ + u32 xSlicDesc; // Descriptor 000-003 + u16 xSlicSize; // Size of this control block 004-005 + u16 xPlicAdjustVpdLens:1; // Flag to indicate new interface 006-007 + u16 xRsvd1:15; // Reserved bits ... + u16 xSlicVpdEntries; // Number of VPD entries 008-009 + u16 xSlicDmaEntries; // Number of DMA entries 00A-00B + u16 xSlicMaxLogicalProcs; // Maximum logical processors 00C-00D + u16 xSlicMaxPhysicalProcs; // Maximum physical processors 00E-00F + u16 xSlicDmaToksOffset; // Offset into this of array 010-011 + u16 xSlicVpdAdrsOffset; // Offset into this of array 012-013 + u16 xSlicDmaLensOffset; // Offset into this of array 014-015 + u16 xSlicVpdLensOffset; // Offset into this of array 016-017 + u16 xSlicMaxSlotLabels; // Maximum number of slot labels 018-019 + u16 xSlicMaxLpQueues; // Maximum number of LP Queues 01A-01B + u8 xRsvd2[4]; // Reserved 01C-01F + u64 xRsvd3[12]; // Reserved 020-07F + u32 xPlicDmaLens[ItDmaMaxEntries];// Array of DMA lengths 080-0A7 + u32 xPlicDmaToks[ItDmaMaxEntries];// Array of DMA tokens 0A8-0CF + u32 xSlicVpdLens[ItVpdMaxEntries];// Array of VPD lengths 0D0-12F + struct SlicVpdAdrs xSlicVpdAdrs[ItVpdMaxEntries];// Array of VPD buffers 130-1EF +}; + +#endif // _ITVPDAREAS_H diff -Nru a/include/asm-ppc/iSeries/LparData.h b/include/asm-ppc/iSeries/LparData.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/LparData.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,75 @@ +/* + * LparData.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PPC_TYPES_H +#include +#endif + +#ifndef _LPARDATA_H +#define _LPARDATA_H + +#include + +extern u32 msChunks[]; + +#define phys_to_absolute( x ) (((u64)msChunks[((x)>>18)])<<18)+((u64)((x)&0x3ffff)) + +#define physRpn_to_absRpn( x ) (((u64)msChunks[((x)>>6)])<<6)+((u64)((x)&0x3f)) + +#define virt_to_absolute( x ) (((u64)msChunks[(((x)-KERNELBASE)>>18)])<<18)+((u64)((x)&0x3ffff)) + +extern u64 virt_to_absolute_outline(u32 address); + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct LparMap xLparMap; +extern struct Naca xNaca; +extern struct Paca xPaca[]; +extern struct HvReleaseData hvReleaseData; +extern struct ItLpNaca itLpNaca; +extern struct ItIplParmsReal xItIplParmsReal; +extern struct IoHriProcessorVpd xIoHriProcessorVpd[]; +extern struct ItLpQueue xItLpQueue; +extern struct ItVpdAreas itVpdAreas; +extern u64 xMsVpd[]; +extern u32 msChunks[]; +extern u32 totalLpChunks; +extern unsigned maxPacas; + +/* +#define phys_to_absolute( x ) (((u64)msChunks[((x)>>18)])<<18)+((u64)((x)&0x3ffff)) + +#define physRpn_to_absRpn( x ) (((u64)msChunks[((x)>>6)])<<6)+((u64)((x)&0x3f)) + +#define virt_to_absolute( x ) (((u64)msChunks[(((x)-KERNELBASE)>>18)])<<18)+((u64)((x)&0x3ffff)) +*/ + +#endif /* _LPAR_DATA_H */ diff -Nru a/include/asm-ppc/iSeries/LparMap.h b/include/asm-ppc/iSeries/LparMap.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/LparMap.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,76 @@ +/* + * LparMap.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PPC_TYPES_H +#include +#endif + +#ifndef _LPARMAP_H +#define _LPARMAP_H + +/* The iSeries hypervisor will set up mapping for one or more + * ESID/VSID pairs (in SLB/segment registers) and will set up + * mappings of one or more ranges of pages to VAs. + * We will have the hypervisor set up the ESID->VSID mapping + * for the four kernel segments (C-F). With shared processors, + * the hypervisor will clear all segment registers and reload + * these four whenever the processor is switched from one + * partition to another. + */ + +/* The Vsid and Esid identified below will be used by the hypervisor + * to set up a memory mapping for part of the load area before giving + * control to the Linux kernel. The load area is 64 MB, but this must + * not attempt to map the whole load area. The Hashed Page Table may + * need to be located within the load area (if the total partition size + * is 64 MB), but cannot be mapped. Typically, this should specify + * to map half (32 MB) of the load area. + * + * The hypervisor will set up page table entries for the number of + * pages specified. + * + * In 32-bit mode, the hypervisor will load all four of the + * segment registers (identified by the low-order four bits of the + * Esid field. In 64-bit mode, the hypervisor will load one SLB + * entry to map the Esid to the Vsid. +*/ + +// Hypervisor initially maps 32MB of the load area +#define HvPagesToMap 8192 + +struct LparMap +{ + u64 xNumberEsids; // Number of ESID/VSID pairs (4) + u64 xNumberRanges; // Number of VA ranges to map (1) + u64 xSegmentTableOffs; // Page number within load area of seg table (0) + u64 xRsvd[5]; // Reserved (0) + u64 xKernelEsidC; // Esid used to map kernel load (0x0C) + u64 xKernelVsidC; // Vsid used to map kernel load (0x0C) + u64 xKernelEsidD; // Esid used to map kernel load (0x0D) + u64 xKernelVsidD; // Vsid used to map kernel load (0x0D) + u64 xKernelEsidE; // Esid used to map kernel load (0x0E) + u64 xKernelVsidE; // Vsid used to map kernel load (0x0E) + u64 xKernelEsidF; // Esid used to map kernel load (0x0F) + u64 xKernelVsidF; // Vsid used to map kernel load (0x0F) + u64 xPages; // Number of pages to be mapped (8192) + u64 xOffset; // Offset from start of load area (0) + u64 xVPN; // Virtual Page Number (0x00000000000C0000) +}; + +#endif /* _LPARMAP_H */ diff -Nru a/include/asm-ppc/iSeries/Naca.h b/include/asm-ppc/iSeries/Naca.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/Naca.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,38 @@ +/* + * Naca.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PPC_TYPES_H +#include +#endif + +#ifndef _NACA_H +#define _NACA_H + + +struct Naca +{ + u32 xItVpdAreas64; // Address of ItVpdAreas + void * xItVpdAreas; + u32 xRamDisk64; // Address of initial Ramdisk + u32 xRamDisk; + u32 xRamDiskSize64; // Size of initial Ramdisk + u32 xRamDiskSize; // in pages +}; + +#endif /* _NACA_H */ diff -Nru a/include/asm-ppc/iSeries/Paca.h b/include/asm-ppc/iSeries/Paca.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/Paca.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,137 @@ +/* + * Paca.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +//============================================================================ +// +// This control block defines the OS's PACA which defines the processor +// specific data for each logical processor on the system. +// There are some pointers defined that are utilized by PLIC. +// + +#ifndef _PPC_TYPES_H +#include +#endif + +//----------------------------------------------------------------------------- +// Other Includes +//----------------------------------------------------------------------------- +#ifndef _ITLPPACA_H +#include +#endif + +#ifndef _ITLPREGSAVE_H +#include +#endif + +#ifndef _ITLPQUEUE_H +#include +#endif + + +#ifndef _PACA_H +#define _PACA_H + +/* + * The bolted stack structure is at the head of each bolted stack + * and is simply a singly linked list + */ + +//============================================================================ +// +// Defines the layout of the Paca. +// +// This structure is not directly accessed by PLIC or the SP except +// for the first two pointers that point to the ItLpPaca area and the +// ItLpRegSave area for this processor. +// +//============================================================================ +struct Paca +{ +//=========================================================================== +// Following two fields are read by PLIC to find the LpPaca and LpRegSave area +//=========================================================================== + + u32 pad1; // Pointer to LpPaca for proc + struct ItLpPaca * xLpPacaPtr; // Pointer to LpPaca for proc + u32 pad2; // Pointer to LpRegSave for proc + struct ItLpRegSave * xLpRegSavePtr; // Pointer to LpRegSave for proc + + u64 xR21; // Savearea for GPR21 + u64 xR22; // Savearea for GPR22 + u64 xKsave; // Saved Kernel stack addr or zero + + u16 xPacaIndex; // Index into Paca array of this + // Paca. This is processor number + u8 xProcStart; // At startup, processor spins until + // xProcStart becomes non-zero. + u8 xProcEnabled; // 1 - soft enabled, 0 - soft disabled + u32 xrsvd2; // was bolted stacks + u32 xSavedMsr; // old msr saved here by HvCall + // and flush_hash_page. + // HvCall uses 64-bit registers + // so it must disable external + // interrupts to avoid the high + // half of the regs getting lost + // It can't stack a frame because + // some of the callers can't + // tolerate hpt faults (which might + // occur on the stack) + u32 xSavedLr; // link register saved here by + // flush_hash_page + u8 xContextOverflow; // 1 - context overflow - use temporary + // context = processor# + 1 + u8 rsvd4; + u16 rsvd5; + u32 xSRR0; // Used as bolted copies of stack fields + u32 xSRR1; + u32 xGPR0; + u32 xGPR2; + u32 default_decr; // Default decrementer value + u32 ext_ints; // ext ints processed + u32 rsvd6; + u64 rsvd1[5]; // Rest of cache line reserved + +//=========================================================================== +// CACHE_LINE_2-3 0x0080 - 0x0180 +//=========================================================================== + + struct ItLpQueue * lpQueuePtr; // LpQueue handled by this processor + u32 breakpoint_loop; // Loop until this field is set + // non-zero by user. Then set it + // back to zero before continuing + + u64 debug_regs; // Pointer to pt_regs at breakpoint + u64 rsvd3[30]; // To be used by Linux + +//=========================================================================== +// CACHE_LINE_4-8 0x0180 - 0x03FF Contains ItLpPaca +//=========================================================================== + + struct ItLpPaca xLpPaca; // Space for ItLpPaca + +//=========================================================================== +// CACHE_LINE_9-16 0x0400 - 0x07FF Contains ItLpRegSave +//=========================================================================== + + struct ItLpRegSave xRegSav; // Register save for proc + +}; + +#endif /* _PACA_H */ diff -Nru a/include/asm-ppc/iSeries/XmPciLpEvent.h b/include/asm-ppc/iSeries/XmPciLpEvent.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/XmPciLpEvent.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,18 @@ + +#ifndef __XMPCILPEVENT_H__ +#define __XMPCILPEVENT_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +int XmPciLpEvent_init(void); +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); + + +#ifdef __cplusplus +} +#endif + +#endif /* __XMPCILPEVENT_H__ */ diff -Nru a/include/asm-ppc/iSeries/iSeries_FlightRecorder.h b/include/asm-ppc/iSeries/iSeries_FlightRecorder.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/iSeries_FlightRecorder.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,85 @@ +#ifndef _ISERIES_FLIGHTRECORDER_H +#define _ISERIES_FLIGHTRECORDER_H +/************************************************************************/ +/* File iSeries_FlightRecorder.h created by Allan Trautman Jan 22 2001. */ +/************************************************************************/ +/* This code supports the pci interface on the IBM iSeries systems. */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Jan 22, 2001 */ +/* Added Time stamp methods. Apr 12, 2001 */ +/* End Change Activity */ +/************************************************************************/ +/* This is a generic Flight Recorder, simply stuffs line entries into a */ +/* buffer for debug purposes. */ +/* */ +/* To use, */ +/* 1. Create one, make it global so it isn't on the stack. */ +/* FlightRecorder PciFlightRecorder; */ +/* */ +/* 2. Optionally create a pointer to it, just makes it easier to use. */ +/* FlightRecorder* PciFr = &PciFlightRecorder; */ +/* */ +/* 3. Initialize with you signature. */ +/* iSeries_Fr_Initialize(PciFr, "Pci Flight Recorder"); */ +/* */ +/* 4. Log entries. */ +/* PciFr->logEntry(PciFr,"In Main"); */ +/* */ +/* 5. Later, you can find the Flight Recorder by looking in the */ +/* System.map */ +/************************************************************************/ +struct iSeries_FlightRecorder; /* Forward declares */ +struct rtc_time; +void logEntry(struct iSeries_FlightRecorder*, char* Text); +void logTime( struct iSeries_FlightRecorder*, char* Text); +void logDate( struct iSeries_FlightRecorder*, char* Text); +#define FlightRecorderSize 4096 + +/************************************************************************/ +/* Generic Flight Recorder Structure */ +/************************************************************************/ +struct iSeries_FlightRecorder { /* Structure Defination */ + char Signature[16]; /* Eye Catcher */ + char* StartingPointer; /* Buffer Starting Address */ + char* CurrentPointer; /* Next Entry Address */ + int WrapCount; /* Number of Buffer Wraps */ + void (*logEntry)(struct iSeries_FlightRecorder*,char*); + void (*logTime) (struct iSeries_FlightRecorder*,char*); + void (*logDate) (struct iSeries_FlightRecorder*,char*); + char Buffer[FlightRecorderSize]; +}; + +typedef struct iSeries_FlightRecorder FlightRecorder; /* Short Name */ +extern void iSeries_Fr_Initialize(FlightRecorder*, char* Signature); +/************************************************************************/ +/* extern void iSeries_LogFr_Entry( FlightRecorder*, char* Text); */ +/* extern void iSeries_LogFr_Date( FlightRecorder*, char* Text); */ +/* extern void iSeries_LogFr_Time( FlightRecorder*, char* Text); */ +/************************************************************************/ +/* PCI Flight Recorder Helpers */ +/************************************************************************/ +extern FlightRecorder* PciFr; /* Ptr to Pci Fr */ +extern char* PciFrBuffer; /* Ptr to Fr Work Buffer */ +#define ISERIES_PCI_FR(buffer) PciFr->logEntry(PciFr,buffer); +#define ISERIES_PCI_FR_TIME(buffer) PciFr->logTime(PciFr,buffer); +#define ISERIES_PCI_FR_DATE(buffer) PciFr->logDate(PciFr,buffer); + +#endif /* _ISERIES_FLIGHTRECORDER_H */ diff -Nru a/include/asm-ppc/iSeries/iSeries_VpdInfo.h b/include/asm-ppc/iSeries/iSeries_VpdInfo.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/iSeries_VpdInfo.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,56 @@ +#ifndef _ISERIES_VPDINFO_H +#define _ISERIES_VPDINFO_H +/************************************************************************/ +/* File iSeries_VpdInfo.h created by Allan Trautman Feb 08 2001. */ +/************************************************************************/ +/* This code supports the location data fon on the IBM iSeries systems. */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Feg 8, 2001 */ +/* Reformated for Card, March 8, 2001 */ +/* End Change Activity */ +/************************************************************************/ + +struct pci_dev; /* Forward Declare */ +/************************************************************************/ +/* Location Data extracted from the VPD list and device info. */ +/************************************************************************/ +struct LocationDataStruct { /* Location data structure for device */ + u16 Bus; /* iSeries Bus Number 0x00*/ + u16 Board; /* iSeries Board 0x02*/ + u8 FrameId; /* iSeries spcn Frame Id 0x04*/ + u8 PhbId; /* iSeries Phb Location 0x05*/ + u16 Card; /* iSeries Card Slot 0x06*/ + char CardLocation[4]; /* Char format of planar vpd 0x08*/ + u8 AgentId; /* iSeries AgentId 0x0C*/ + u8 SecondaryAgentId; /* iSeries Secondary Agent Id 0x0D*/ + u8 LinuxBus; /* Linux Bus Number 0x0E*/ + u8 LinuxDevFn; /* Linux Device Function 0x0F*/ +}; +typedef struct LocationDataStruct LocationData; +#define LOCATION_DATA_SIZE 16 + +/************************************************************************/ +/* Protypes */ +/************************************************************************/ +extern LocationData* iSeries_GetLocationData(struct pci_dev* PciDev); +extern int iSeries_Device_Information(struct pci_dev*,char*, int); + +#endif /* _ISERIES_VPDINFO_H */ diff -Nru a/include/asm-ppc/iSeries/iSeries_dma.h b/include/asm-ppc/iSeries/iSeries_dma.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/iSeries_dma.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,97 @@ +/* + * iSeries_dma.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ISERIES_DMA_H +#define _ISERIES_DMA_H + +#include +#ifndef __LINUX_SPINLOCK_H +#include +#endif + +// NUM_TCE_LEVELS defines the largest contiguous block +// of dma (tce) space we can get. NUM_TCE_LEVELS = 10 +// allows up to 2**9 pages (512 * 4096) = 2 MB +#define NUM_TCE_LEVELS 10 + +#define NO_TCE ((dma_addr_t)-1) + +// Tces come in two formats, one for the virtual bus and a different +// format for PCI +#define TCE_VB 0 +#define TCE_PCI 1 + + +union Tce { + u64 wholeTce; + struct { + u64 cacheBits :6; /* Cache hash bits - not used */ + u64 rsvd :6; + u64 rpn :40; /* Absolute page number */ + u64 valid :1; /* Tce is valid (vb only) */ + u64 allIo :1; /* Tce is valid for all lps (vb only) */ + u64 lpIndex :8; /* LpIndex for user of TCE (vb only) */ + u64 pciWrite :1; /* Write allowed (pci only) */ + u64 readWrite :1; /* Read allowed (pci), Write allowed + (vb) */ + } tceBits; +}; + +struct Bitmap { + unsigned long numBits; + unsigned long numBytes; + unsigned char * map; +}; + +struct MultiLevelBitmap { + unsigned long maxLevel; + struct Bitmap level[NUM_TCE_LEVELS]; +}; + +struct TceTable { + u64 busNumber; + u64 size; + u64 startOffset; + u64 index; + spinlock_t lock; + struct MultiLevelBitmap mlbm; +}; + +struct HvTceTableManagerCB { + u64 busNumber; /* Bus number for this tce table */ + u64 start; /* Will be NULL for secondary */ + u64 totalSize; /* Size (in pages) of whole table */ + u64 startOffset; /* Index into real tce table of the + start of our section */ + u64 size; /* Size (in pages) of our section */ + u64 index; /* Index of this tce table (token?) */ + u16 maxTceTableIndex; /* Max number of tables for partition */ + u8 virtualBusFlag; /* Flag to indicate virtual bus */ + u8 rsvd[5]; +}; + +extern struct TceTable virtBusTceTable; /* Tce table for virtual bus */ + +extern struct TceTable * build_tce_table( struct HvTceTableManagerCB *, + struct TceTable *); +extern void create_virtual_bus_tce_table( void ); + +extern void create_pci_bus_tce_table( unsigned busNumber ); + +#endif // _ISERIES_DMA_H diff -Nru a/include/asm-ppc/iSeries/iSeries_fixup.h b/include/asm-ppc/iSeries/iSeries_fixup.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/iSeries_fixup.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,25 @@ + +#ifndef __ISERIES_FIXUP_H__ +#define __ISERIES_FIXUP_H__ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void iSeries_fixup (void); +void iSeries_fixup_bus (struct pci_bus*); +unsigned int iSeries_scan_slot (struct pci_dev*, u16, u8, u8); + + +/* Need to store information related to the PHB bucc and make it accessible to the hose */ +struct iSeries_hose_arch_data { + u32 hvBusNumber; +}; + + +#ifdef __cplusplus +} +#endif + +#endif /* __ISERIES_FIXUP_H__ */ diff -Nru a/include/asm-ppc/iSeries/iSeries_io.h b/include/asm-ppc/iSeries/iSeries_io.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/iSeries_io.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,42 @@ +#ifdef CONFIG_PPC_ISERIES +#ifndef _ISERIES_IO_H +#define _ISERIES_IO_H +/************************************************************************/ +/* File iSeries_io.h created by Allan Trautman on Thu Dec 28 2000. */ +/************************************************************************/ +/* Remaps the io.h for the iSeries Io */ +/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created December 28, 2000 */ +/* End Change Activity */ +/************************************************************************/ +extern u8 iSeries_Readb(u32* IoAddress); +extern u16 iSeries_Readw(u32* IoAddress); +extern u32 iSeries_Readl(u32* IoAddress); +extern void iSeries_Writeb(u8 IoData,u32* IoAddress); +extern void iSeries_Writew(u16 IoData,u32* IoAddress); +extern void iSeries_Writel(u32 IoData,u32* IoAddress); + +extern void* iSeries_memcpy_toio(void *dest, void *source, int n); +extern void* iSeries_memcpy_fromio(void *dest, void *source, int n); + +#endif /* _ISERIES_IO_H */ +#endif /* CONFIG_PPC_ISERIES */ + diff -Nru a/include/asm-ppc/iSeries/iSeries_irq.h b/include/asm-ppc/iSeries/iSeries_irq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/iSeries_irq.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,26 @@ + +#ifndef __ISERIES_IRQ_H__ +#define __ISERIES_IRQ_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned int iSeries_startup_IRQ(unsigned int); +void iSeries_shutdown_IRQ(unsigned int); +void iSeries_enable_IRQ(unsigned int); +void iSeries_disable_IRQ(unsigned int); +void iSeries_end_IRQ(unsigned int); +void iSeries_init_IRQ(void); +void iSeries_init_irqMap(int); +int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, HvAgentId); +int iSeries_assign_IRQ(int, HvBusNumber, HvSubBusNumber, HvAgentId); +void iSeries_activate_IRQs(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __ISERIES_IRQ_H__ */ diff -Nru a/include/asm-ppc/iSeries/iSeries_pci.h b/include/asm-ppc/iSeries/iSeries_pci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/iSeries_pci.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,123 @@ +#ifndef _ISERIES_32_PCI_H +#define _ISERIES_32_PCI_H +/************************************************************************/ +/* File iSeries_pci.h created by Allan Trautman on Tue Feb 20, 2001. */ +/************************************************************************/ +/* Define some useful macros for the iSeries pci routines. */ +/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created Feb 20, 2001 */ +/* Added device reset, March 22, 2001 */ +/* End Change Activity */ +/************************************************************************/ +#include +#include + +struct pci_dev; /* For Reference */ +/************************************************************************/ +/* Gets iSeries Bus, SubBus, of DevFn using pci_dev* structure */ +/************************************************************************/ +#define ISERIES_GET_BUS(DevPtr) iSeries_Get_Bus(DevPtr) +#define ISERIES_GET_SUBBUS(DevPtr) iSeries_Get_SubBus(DevPtr) +#define ISERIES_GET_DEVFUN(DevPtr) iSeries_Get_DevFn(DevPtr) +/************************************************************************/ +/* Functions */ +/************************************************************************/ +extern u8 iSeries_Get_Bus(struct pci_dev*); +extern u8 iSeries_Get_SubBus(struct pci_dev*); +extern u8 iSeries_Get_DevFn(struct pci_dev*); +/************************************************************************/ +/* Global Bus map */ +/************************************************************************/ +extern u8 iSeries_GlobalBusMap[256][2]; /* Global iSeries Bus Map */ + +/************************************************************************/ +/* iSeries Device Information */ +/************************************************************************/ +struct iSeries_Device_Struct { + struct pci_dev* PciDevPtr; /* Pointer to pci_dev structure */ + HvBusNumber BusNumber; /* Hypervisor Bus Number */ + HvSubBusNumber SubBus; /* Hypervisor SubBus Number */ + HvAgentId DevFn; /* Hypervisor DevFn */ + u8 BarNumber; /* Bar number on Xlates */ + u32 BarOffset; /* Offset within Bar on Xlates */ + int RCode; /* Return Code Holder */ +}; +typedef struct iSeries_Device_Struct iSeries_Device; +extern void build_iSeries_Device(iSeries_Device* Device, struct pci_dev* DevPtr); + +/************************************************************************/ +/* Formatting device information. */ +/************************************************************************/ +extern int iSeries_Device_Information(struct pci_dev*,char*,int ); + +/************************************************************************/ +/* Flight Recorder tracing */ +/************************************************************************/ +extern void iSeries_Set_PciFilter(struct pci_dev*); +extern int iSeries_Set_PciTraceFlag(int TraceFlag); +extern int iSeries_Get_PciTraceFlag(void); +extern int iSeries_Set_PciErpFlag(int ErpFlag); + +/************************************************************************/ +/* Structure to hold the data for PCI Register Save/Restore functions. */ +/************************************************************************/ +struct pci_config_reg_save_area { /* */ + u16 Flags; /* Control & Info Flags */ + u16 ByteCount; /* Number of Register Bytes to S*/ + struct pci_dev* PciDev; /* Pointer to device */ + u32 RCode; /* Holder for possible errors */ + u32 FailReg; /* Failing Register on error */ + u8 Regs[64]; /* Save Area */ +}; +typedef struct pci_config_reg_save_area PciReqsSaveArea; +/************************************************************************/ +/* Various flavors of reset device functions. */ +/************************************************************************/ +/* */ +/* iSeries_Device_Reset_NoIrq */ +/* IRQ is not disabled and default timings are used. */ +/* iSeries_Device_Reset_Generic */ +/* A generic reset, IRQ is disable and re-enabled. The assert and */ +/* wait timings will be the pci defaults. */ +/* iSeries_Device_Reset */ +/* A device Reset interface that client can control the timing of */ +/* the reset and wait delays. */ +/* */ +/* Parameters: */ +/* pci_dev = Device to reset. */ +/* AssertTime = Time in .1 seconds to hold the reset down. The */ +/* default (and minimum) is .5 seconds. */ +/* DelayTime = Time in .1 seconds to wait for device to come ready */ +/* after the reset. The default is 3 seconds. */ +/* IrgDisable = A non-zero will skip irq disable & enable. */ +/* */ +/* Return: */ +/* Zero return, reset is successful. */ +/* Non-zero return code indicates failure. */ +/************************************************************************/ +extern int iSeries_Device_Reset_NoIrq(struct pci_dev* PciDev); +extern int iSeries_Device_Reset_Generic(struct pci_dev* PciDev); +extern int iSeries_Device_Reset(struct pci_dev* PciDev, int AssertTime, int DelayTime, int IrqDisable); +extern int iSeries_Device_ToggleReset(struct pci_dev* PciDev, int AssertTime, int DelayTime); +extern int iSeries_Device_RestoreConfigRegs(PciReqsSaveArea* SaveArea); +extern PciReqsSaveArea* iSeries_Device_SaveConfigRegs(struct pci_dev* DevPtr); + +#endif /* _ISERIES_32_PCI_H */ diff -Nru a/include/asm-ppc/iSeries/iSeries_proc.h b/include/asm-ppc/iSeries/iSeries_proc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/iSeries_proc.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,37 @@ +/* + * iSeries_proc.h + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _ISERIES_PROC_H +#define _ISERIES_PROC_H + +#include + +extern void iSeries_proc_early_init(void); +extern void iSeries_proc_create(void); + +typedef void (*iSeriesProcFunction)(struct proc_dir_entry *iSeries_proc); + +extern void iSeries_proc_callback(iSeriesProcFunction initFunction); + +#endif /* _iSeries_PROC_H */ + diff -Nru a/include/asm-ppc/iSeries/mf.h b/include/asm-ppc/iSeries/mf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/mf.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,83 @@ +/* + * mf.h + * Copyright (C) 2001 Troy D. Armstrong IBM Corporation + * + * This modules exists as an interface between a Linux secondary partition + * running on an iSeries and the primary partition's Virtual Service + * Processor (VSP) object. The VSP has final authority over powering on/off + * all partitions in the iSeries. It also provides miscellaneous low-level + * machine facility type operations. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +#ifndef MF_H_INCLUDED +#define MF_H_INCLUDED + +#include +#include + +struct rtc_time; + +typedef void (*MFCompleteHandler)( void * clientToken, int returnCode ); + +extern void mf_allocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned size, + unsigned amount, + MFCompleteHandler hdlr, + void * userToken ); + +extern void mf_deallocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned count, + MFCompleteHandler hdlr, + void * userToken ); + +extern void mf_powerOff( void ); + +extern void mf_reboot( void ); + +extern void mf_displaySrc( u32 word ); +extern void mf_displayProgress( u16 value ); + +extern void mf_clearSrc( void ); + +extern void mf_init( void ); + +extern void mf_setSide(char side); + +extern char mf_getSide(void); + +extern void mf_setCmdLine(const char *cmdline, int size, u64 side); + +extern int mf_getCmdLine(char *cmdline, int *size, u64 side); + +extern void mf_getSrcHistory(char *buffer, int size); + +extern int mf_setVmlinuxChunk(const char *buffer, int size, int offset, u64 side); + +extern int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side); + +extern int mf_setRtcTime(unsigned long time); + +extern int mf_getRtcTime(unsigned long *time); + +extern int mf_getRtc( struct rtc_time * tm ); + +extern int mf_setRtc( struct rtc_time * tm ); + +#endif /* MF_H_INCLUDED */ diff -Nru a/include/asm-ppc/iSeries/mf_proc.h b/include/asm-ppc/iSeries/mf_proc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/mf_proc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,33 @@ +/* + * mf_proc.h + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _MF_PROC_H +#define _MF_PROC_H + +#include + +void mf_proc_init(struct proc_dir_entry *iSeries_proc); + + +#endif /* _MF_PROC_H */ + diff -Nru a/include/asm-ppc/iSeries/pmc_proc.h b/include/asm-ppc/iSeries/pmc_proc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/pmc_proc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,33 @@ +/* + * pmc_proc.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _PMC_PROC_H +#define _PMC_PROC_H + +#include + +void pmc_proc_init(struct proc_dir_entry *iSeries_proc); + + +#endif /* _PMC_PROC_H */ + diff -Nru a/include/asm-ppc/iSeries/veth-proc.h b/include/asm-ppc/iSeries/veth-proc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/iSeries/veth-proc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,32 @@ +/* + * veth-proc.h + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _VETH_PROC_H +#define _VETH_PROC_H + +#include + +void veth_proc_init(struct proc_dir_entry *iSeries_proc); + +#endif /* _VETH-PROC_H */ + diff -Nru a/include/asm-ppc/ibm403.h b/include/asm-ppc/ibm403.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/ibm403.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,509 @@ +/* + * ibm403.h + * + * This was dirived from the ibm4xx.h and all 403 specific definitions + * where moved here. + * + * Armin Kuster + * Tom Rini + * Oct, 2001 + * + * + * Copyright 2001 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * Version 1.0 Oct 10, 2001 - A. Kuster + * Initial version - moved 403 specific out of ibm4xx.h + * Version 1.1 Oct 25, 2001 - T. Rini + * Lots of cleanups. + */ + + +#ifdef __KERNEL__ +#ifndef __ASM_IBM403_H__ +#define __ASM_IBM403_H__ + +#include + +#if defined(CONFIG_403GCX) + +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRNCAP_DMA_CC 1 /* have DMA chained count capability */ +#define DCRN_DMASR_BASE 0x0E0 + +#define DCRN_EXIER_BASE 0x042 +#define DCRN_EXISR_BASE 0x040 +#define DCRN_IOCR_BASE 0x0A0 + + +/* ------------------------------------------------------------------------- */ +#endif + + + +#ifdef DCRN_BE_BASE +#define DCRN_BEAR (DCRN_BE_BASE + 0x0) /* Bus Error Address Register */ +#define DCRN_BESR (DCRN_BE_BASE + 0x1) /* Bus Error Syndrome Register*/ +#endif +/* DCRN_BESR */ +#define BESR_DSES 0x80000000 /* Data-Side Error Status */ +#define BESR_DMES 0x40000000 /* DMA Error Status */ +#define BESR_RWS 0x20000000 /* Read/Write Status */ +#define BESR_ETMASK 0x1C000000 /* Error Type */ +#define ET_PROT 0 +#define ET_PARITY 1 +#define ET_NCFG 2 +#define ET_BUSERR 4 +#define ET_BUSTO 6 + +#ifdef DCRN_CHCR_BASE +#define DCRN_CHCR0 (DCRN_CHCR_BASE + 0x0) /* Chip Control Register 1 */ +#define DCRN_CHCR1 (DCRN_CHCR_BASE + 0x1) /* Chip Control Register 2 */ +#endif +#define CHR1_CETE 0x00800000 /* CPU external timer enable */ +#define CHR1_PCIPW 0x00008000 /* PCI Int enable/Peripheral Write enable */ + +#ifdef DCRN_CHPSR_BASE +#define DCRN_CHPSR (DCRN_CHPSR_BASE + 0x0) /* Chip Pin Strapping */ +#endif + +#ifdef DCRN_CIC_BASE +#define DCRN_CICCR (DCRN_CIC_BASE + 0x0) /* CIC Control Register */ +#define DCRN_DMAS1 (DCRN_CIC_BASE + 0x1) /* DMA Select1 Register */ +#define DCRN_DMAS2 (DCRN_CIC_BASE + 0x2) /* DMA Select2 Register */ +#define DCRN_CICVCR (DCRN_CIC_BASE + 0x3) /* CIC Video COntro Register */ +#define DCRN_CICSEL3 (DCRN_CIC_BASE + 0x5) /* CIC Select 3 Register */ +#define DCRN_SGPO (DCRN_CIC_BASE + 0x6) /* CIC GPIO Output Register */ +#define DCRN_SGPOD (DCRN_CIC_BASE + 0x7) /* CIC GPIO OD Register */ +#define DCRN_SGPTC (DCRN_CIC_BASE + 0x8) /* CIC GPIO Tristate Ctrl Reg */ +#define DCRN_SGPI (DCRN_CIC_BASE + 0x9) /* CIC GPIO Input Reg */ +#endif + +#ifdef DCRN_CPMFR_BASE +#define DCRN_CPMFR (DCRN_CPMFR_BASE + 0x0) /* CPM Force */ +#endif + +#ifndef CPM_AUD +#define CPM_AUD 0x00000000 +#endif +#ifndef CPM_BRG +#define CPM_BRG 0x00000000 +#endif +#ifndef CPM_CBS +#define CPM_CBS 0x00000000 +#endif +#ifndef CPM_CPU +#define CPM_CPU 0x00000000 +#endif +#ifndef CPM_DCP +#define CPM_DCP 0x00000000 +#endif +#ifndef CPM_DCRX +#define CPM_DCRX 0x00000000 +#endif +#ifndef CPM_DENC +#define CPM_DENC 0x00000000 +#endif +#ifndef CPM_DMA +#define CPM_DMA 0x00000000 +#endif +#ifndef CPM_DSCR +#define CPM_DSCR 0x00000000 +#endif +#ifndef CPM_EBC +#define CPM_EBC 0x00000000 +#endif +#ifndef CPM_EBIU +#define CPM_EBIU 0x00000000 +#endif +#ifndef CPM_EMAC_MM +#define CPM_EMAC_MM 0x00000000 +#endif +#ifndef CPM_EMAC_RM +#define CPM_EMAC_RM 0x00000000 +#endif +#ifndef CPM_EMAC_TM +#define CPM_EMAC_TM 0x00000000 +#endif +#ifndef CPM_GPIO0 +#define CPM_GPIO0 0x00000000 +#endif +#ifndef CPM_GPT +#define CPM_GPT 0x00000000 +#endif +#ifndef CPM_I1284 +#define CPM_I1284 0x00000000 +#endif +#ifndef CPM_IIC0 +#define CPM_IIC0 0x00000000 +#endif +#ifndef CPM_IIC1 +#define CPM_IIC1 0x00000000 +#endif +#ifndef CPM_MSI +#define CPM_MSI 0x00000000 +#endif +#ifndef CPM_PCI +#define CPM_PCI 0x00000000 +#endif +#ifndef CPM_PLB +#define CPM_PLB 0x00000000 +#endif +#ifndef CPM_SC0 +#define CPM_SC0 0x00000000 +#endif +#ifndef CPM_SC1 +#define CPM_SC1 0x00000000 +#endif +#ifndef CPM_SDRAM0 +#define CPM_SDRAM0 0x00000000 +#endif +#ifndef CPM_SDRAM1 +#define CPM_SDRAM1 0x00000000 +#endif +#ifndef CPM_TMRCLK +#define CPM_TMRCLK 0x00000000 +#endif +#ifndef CPM_UART0 +#define CPM_UART0 0x00000000 +#endif +#ifndef CPM_UART1 +#define CPM_UART1 0x00000000 +#endif +#ifndef CPM_UART2 +#define CPM_UART2 0x00000000 +#endif +#ifndef CPM_UIC +#define CPM_UIC 0x00000000 +#endif +#ifndef CPM_VID2 +#define CPM_VID2 0x00000000 +#endif +#ifndef CPM_XPT27 +#define CPM_XPT27 0x00000000 +#endif +#ifndef CPM_XPT54 +#define CPM_XPT54 0x00000000 +#endif + +#ifdef DCRN_CPMSR_BASE +#define DCRN_CPMSR (DCRN_CPMSR_BASE + 0x0) /* CPM Status */ +#define DCRN_CPMER (DCRN_CPMSR_BASE + 0x1) /* CPM Enable */ +#endif + +#ifdef DCRN_DCP0_BASE +#define DCRN_DCP0_CFGADDR (DCRN_DCP0_BASE + 0x0) /* Decompression Controller Address */ +#define DCRN_DCP0_CFGDATA (DCRN_DCP0_BASE + 0x1) /* Decompression Controller Data */ +#endif + +#ifdef DCRN_DCRX_BASE +#define DCRN_DCRXICR (DCRN_DCRX_BASE + 0x0) /* Internal Control Register */ +#define DCRN_DCRXISR (DCRN_DCRX_BASE + 0x1) /* Internal Status Register */ +#define DCRN_DCRXECR (DCRN_DCRX_BASE + 0x2) /* External Control Register */ +#define DCRN_DCRXESR (DCRN_DCRX_BASE + 0x3) /* External Status Register */ +#define DCRN_DCRXTAR (DCRN_DCRX_BASE + 0x4) /* Target Address Register */ +#define DCRN_DCRXTDR (DCRN_DCRX_BASE + 0x5) /* Target Data Register */ +#define DCRN_DCRXIGR (DCRN_DCRX_BASE + 0x6) /* Interrupt Generation Register */ +#define DCRN_DCRXBCR (DCRN_DCRX_BASE + 0x7) /* Line Buffer Control Register */ +#endif + +#ifdef DCRN_DMA0_BASE +#define DCRN_DMACR0 (DCRN_DMA0_BASE + 0x0) /* DMA Channel Control Register 0 */ +#define DCRN_DMACT0 (DCRN_DMA0_BASE + 0x1) /* DMA Count Register 0 */ +#define DCRN_DMADA0 (DCRN_DMA0_BASE + 0x2) /* DMA Destination Address Register 0 */ +#define DCRN_DMASA0 (DCRN_DMA0_BASE + 0x3) /* DMA Source Address Register 0 */ +#ifdef DCRNCAP_DMA_CC +#define DCRN_DMACC0 (DCRN_DMA0_BASE + 0x4) /* DMA Chained Count Register 0 */ +#endif + +#ifdef DCRNCAP_DMA_SG +#define DCRN_ASG0 (DCRN_DMA0_BASE + 0x4) /* DMA Scatter/Gather Descriptor Addr 0 */ +#endif +#endif + +#ifdef DCRN_DMA1_BASE +#define DCRN_DMACR1 (DCRN_DMA1_BASE + 0x0) /* DMA Channel Control Register 1 */ +#define DCRN_DMACT1 (DCRN_DMA1_BASE + 0x1) /* DMA Count Register 1 */ +#define DCRN_DMADA1 (DCRN_DMA1_BASE + 0x2) /* DMA Destination Address Register 1 */ +#define DCRN_DMASA1 (DCRN_DMA1_BASE + 0x3) /* DMA Source Address Register 1 */ + +#ifdef DCRNCAP_DMA_CC +#define DCRN_DMACC1 (DCRN_DMA1_BASE + 0x4) /* DMA Chained Count Register 1 */ +#endif +#ifdef DCRNCAP_DMA_SG +#define DCRN_ASG1 (DCRN_DMA1_BASE + 0x4) /* DMA Scatter/Gather Descriptor Addr 1 */ +#endif +#endif + +#ifdef DCRN_DMA2_BASE +#define DCRN_DMACR2 (DCRN_DMA2_BASE + 0x0) /* DMA Channel Control Register 2 */ +#define DCRN_DMACT2 (DCRN_DMA2_BASE + 0x1) /* DMA Count Register 2 */ +#define DCRN_DMADA2 (DCRN_DMA2_BASE + 0x2) /* DMA Destination Address Register 2 */ +#define DCRN_DMASA2 (DCRN_DMA2_BASE + 0x3) /* DMA Source Address Register 2 */ +#ifdef DCRNCAP_DMA_CC +#define DCRN_DMACC2 (DCRN_DMA2_BASE + 0x4) /* DMA Chained Count Register 2 */ +#endif +#ifdef DCRNCAP_DMA_SG +#define DCRN_ASG2 (DCRN_DMA2_BASE + 0x4) /* DMA Scatter/Gather Descriptor Addr 2 */ +#endif +#endif + +#ifdef DCRN_DMA3_BASE +#define DCRN_DMACR3 (DCRN_DMA3_BASE + 0x0) /* DMA Channel Control Register 3 */ +#define DCRN_DMACT3 (DCRN_DMA3_BASE + 0x1) /* DMA Count Register 3 */ +#define DCRN_DMADA3 (DCRN_DMA3_BASE + 0x2) /* DMA Destination Address Register 3 */ +#define DCRN_DMASA3 (DCRN_DMA3_BASE + 0x3) /* DMA Source Address Register 3 */ +#ifdef DCRNCAP_DMA_CC +#define DCRN_DMACC3 (DCRN_DMA3_BASE + 0x4) /* DMA Chained Count Register 3 */ +#endif +#ifdef DCRNCAP_DMA_SG +#define DCRN_ASG3 (DCRN_DMA3_BASE + 0x4) /* DMA Scatter/Gather Descriptor Addr 3 */ +#endif +#endif + +#ifdef DCRN_DMASR_BASE +#define DCRN_DMASR (DCRN_DMASR_BASE + 0x0) /* DMA Status Register */ +#ifdef DCRNCAP_DMA_SG +#define DCRN_ASGC (DCRN_DMASR_BASE + 0x3) /* DMA Scatter/Gather Command */ +/* don't know if these two registers always exist if scatter/gather exists */ +#define DCRN_POL (DCRN_DMASR_BASE + 0x6) /* DMA Polarity Register */ +#define DCRN_SLP (DCRN_DMASR_BASE + 0x5) /* DMA Sleep Register */ +#endif +#endif + +#ifdef DCRN_EBC_BASE +#define DCRN_EBCCFGADR (DCRN_EBC_BASE + 0x0) /* Peripheral Controller Address */ +#define DCRN_EBCCFGDATA (DCRN_EBC_BASE + 0x1) /* Peripheral Controller Data */ +#endif + +#ifdef DCRN_EXIER_BASE +#define DCRN_EXIER (DCRN_EXIER_BASE + 0x0) /* External Interrupt Enable Register */ +#endif + +#ifdef DCRN_EBIMC_BASE +#define DCRN_BRCRH0 (DCRN_EBIMC_BASE + 0x0) /* Bus Region Config High 0 */ +#define DCRN_BRCRH1 (DCRN_EBIMC_BASE + 0x1) /* Bus Region Config High 1 */ +#define DCRN_BRCRH2 (DCRN_EBIMC_BASE + 0x2) /* Bus Region Config High 2 */ +#define DCRN_BRCRH3 (DCRN_EBIMC_BASE + 0x3) /* Bus Region Config High 3 */ +#define DCRN_BRCRH4 (DCRN_EBIMC_BASE + 0x4) /* Bus Region Config High 4 */ +#define DCRN_BRCRH5 (DCRN_EBIMC_BASE + 0x5) /* Bus Region Config High 5 */ +#define DCRN_BRCRH6 (DCRN_EBIMC_BASE + 0x6) /* Bus Region Config High 6 */ +#define DCRN_BRCRH7 (DCRN_EBIMC_BASE + 0x7) /* Bus Region Config High 7 */ +#define DCRN_BRCR0 (DCRN_EBIMC_BASE + 0x10)/* BRC 0 */ +#define DCRN_BRCR1 (DCRN_EBIMC_BASE + 0x11)/* BRC 1 */ +#define DCRN_BRCR2 (DCRN_EBIMC_BASE + 0x12)/* BRC 2 */ +#define DCRN_BRCR3 (DCRN_EBIMC_BASE + 0x13)/* BRC 3 */ +#define DCRN_BRCR4 (DCRN_EBIMC_BASE + 0x14)/* BRC 4 */ +#define DCRN_BRCR5 (DCRN_EBIMC_BASE + 0x15)/* BRC 5 */ +#define DCRN_BRCR6 (DCRN_EBIMC_BASE + 0x16)/* BRC 6 */ +#define DCRN_BRCR7 (DCRN_EBIMC_BASE + 0x17)/* BRC 7 */ +#define DCRN_BEAR0 (DCRN_EBIMC_BASE + 0x20)/* Bus Error Address Register */ +#define DCRN_BESR0 (DCRN_EBIMC_BASE + 0x21)/* Bus Error Status Register */ +#define DCRN_BIUCR (DCRN_EBIMC_BASE + 0x2A)/* Bus Interfac Unit Ctrl Reg */ +#endif + +#ifdef DCRN_EXISR_BASE +#define DCRN_EXISR (DCRN_EXISR_BASE + 0x0) /* External Interrupt Status Register */ +#endif +#define EXIER_CIE 0x80000000 /* Critical Interrupt Enable */ +#define EXIER_SRIE 0x08000000 /* Serial Port Rx Int. Enable */ +#define EXIER_STIE 0x04000000 /* Serial Port Tx Int. Enable */ +#define EXIER_JRIE 0x02000000 /* JTAG Serial Port Rx Int. Enable */ +#define EXIER_JTIE 0x01000000 /* JTAG Serial Port Tx Int. Enable */ +#define EXIER_D0IE 0x00800000 /* DMA Channel 0 Interrupt Enable */ +#define EXIER_D1IE 0x00400000 /* DMA Channel 1 Interrupt Enable */ +#define EXIER_D2IE 0x00200000 /* DMA Channel 2 Interrupt Enable */ +#define EXIER_D3IE 0x00100000 /* DMA Channel 3 Interrupt Enable */ +#define EXIER_E0IE 0x00000010 /* External Interrupt 0 Enable */ +#define EXIER_E1IE 0x00000008 /* External Interrupt 1 Enable */ +#define EXIER_E2IE 0x00000004 /* External Interrupt 2 Enable */ +#define EXIER_E3IE 0x00000002 /* External Interrupt 3 Enable */ +#define EXIER_E4IE 0x00000001 /* External Interrupt 4 Enable */ + +#ifdef DCRN_IOCR_BASE +#define DCRN_IOCR (DCRN_IOCR_BASE + 0x0) /* Input/Output Configuration Register */ +#endif +#define IOCR_E0TE 0x80000000 +#define IOCR_E0LP 0x40000000 +#define IOCR_E1TE 0x20000000 +#define IOCR_E1LP 0x10000000 +#define IOCR_E2TE 0x08000000 +#define IOCR_E2LP 0x04000000 +#define IOCR_E3TE 0x02000000 +#define IOCR_E3LP 0x01000000 +#define IOCR_E4TE 0x00800000 +#define IOCR_E4LP 0x00400000 +#define IOCR_EDT 0x00080000 +#define IOCR_SOR 0x00040000 +#define IOCR_EDO 0x00008000 +#define IOCR_2XC 0x00004000 +#define IOCR_ATC 0x00002000 +#define IOCR_SPD 0x00001000 +#define IOCR_BEM 0x00000800 +#define IOCR_PTD 0x00000400 +#define IOCR_ARE 0x00000080 +#define IOCR_DRC 0x00000020 +#define IOCR_RDM(x) (((x) & 0x3) << 3) +#define IOCR_TCS 0x00000004 +#define IOCR_SCS 0x00000002 +#define IOCR_SPC 0x00000001 + +#ifdef DCRN_MAL_BASE +#define DCRN_MALCR (DCRN_MAL_BASE + 0x0) /* MAL Configuration */ +#define DCRN_MALDBR (DCRN_MAL_BASE + 0x3) /* Debug Register */ +#define DCRN_MALESR (DCRN_MAL_BASE + 0x1) /* Error Status */ +#define DCRN_MALIER (DCRN_MAL_BASE + 0x2) /* Interrupt Enable */ +#define DCRN_MALTXCARR (DCRN_MAL_BASE + 0x5) /* TX Channed Active Reset Register */ +#define DCRN_MALTXCASR (DCRN_MAL_BASE + 0x4) /* TX Channel Active Set Register */ +#define DCRN_MALTXDEIR (DCRN_MAL_BASE + 0x7) /* Tx Descriptor Error Interrupt */ +#define DCRN_MALTXEOBISR (DCRN_MAL_BASE + 0x6) /* Tx End of Buffer Interrupt Status */ +#define DCRN_MALRXCARR (DCRN_MAL_BASE + 0x11) /* RX Channed Active Reset Register */ +#define DCRN_MALRXCASR (DCRN_MAL_BASE + 0x10) /* RX Channel Active Set Register */ +#define DCRN_MALRXDEIR (DCRN_MAL_BASE + 0x13) /* Rx Descriptor Error Interrupt */ +#define DCRN_MALRXEOBISR (DCRN_MAL_BASE + 0x12) /* Rx End of Buffer Interrupt Status */ +#define DCRN_MALRXCTP0R (DCRN_MAL_BASE + 0x40) /* Channel Rx 0 Channel Table Pointer */ +#define DCRN_MALTXCTP0R (DCRN_MAL_BASE + 0x20) /* Channel Tx 0 Channel Table Pointer */ +#define DCRN_MALTXCTP1R (DCRN_MAL_BASE + 0x21) /* Channel Tx 1 Channel Table Pointer */ +#define DCRN_MALRCBS0 (DCRN_MAL_BASE + 0x60) /* Channel Rx 0 Channel Buffer Size */ +#endif +/* DCRN_MALCR */ +#define MALCR_MMSR 0x80000000/* MAL Software reset */ +#define MALCR_PLBP_1 0x00400000 /* MAL reqest priority: */ +#define MALCR_PLBP_2 0x00800000 /* lowsest is 00 */ +#define MALCR_PLBP_3 0x00C00000 /* highest */ +#define MALCR_GA 0x00200000 /* Guarded Active Bit */ +#define MALCR_OA 0x00100000 /* Ordered Active Bit */ +#define MALCR_PLBLE 0x00080000 /* PLB Lock Error Bit */ +#define MALCR_PLBLT_1 0x00040000 /* PLB Latency Timer */ +#define MALCR_PLBLT_2 0x00020000 +#define MALCR_PLBLT_3 0x00010000 +#define MALCR_PLBLT_4 0x00008000 +#define MALCR_PLBLT_DEFAULT 0x00078000 /* JSP: Is this a valid default?? */ +#define MALCR_PLBB 0x00004000 /* PLB Burst Deactivation Bit */ +#define MALCR_OPBBL 0x00000080 /* OPB Lock Bit */ +#define MALCR_EOPIE 0x00000004 /* End Of Packet Interrupt Enable */ +#define MALCR_LEA 0x00000002 /* Locked Error Active */ +#define MALCR_MSD 0x00000001 /* MAL Scroll Descriptor Bit */ +/* DCRN_MALESR */ +#define MALESR_EVB 0x80000000 /* Error Valid Bit */ +#define MALESR_CIDRX 0x40000000 /* Channel ID Receive */ +#define MALESR_DE 0x00100000 /* Descriptor Error */ +#define MALESR_OEN 0x00080000 /* OPB Non-Fullword Error */ +#define MALESR_OTE 0x00040000 /* OPB Timeout Error */ +#define MALESR_OSE 0x00020000 /* OPB Slave Error */ +#define MALESR_PEIN 0x00010000 /* PLB Bus Error Indication */ +#define MALESR_DEI 0x00000010 /* Descriptor Error Interrupt */ +#define MALESR_ONEI 0x00000008 /* OPB Non-Fullword Error Interrupt */ +#define MALESR_OTEI 0x00000004 /* OPB Timeout Error Interrupt */ +#define MALESR_OSEI 0x00000002 /* OPB Slace Error Interrupt */ +#define MALESR_PBEI 0x00000001 /* PLB Bus Error Interrupt */ +/* DCRN_MALIER */ +#define MALIER_DE 0x00000010 /* Descriptor Error Interrupt Enable */ +#define MALIER_NE 0x00000008 /* OPB Non-word Transfer Int Enable */ +#define MALIER_TE 0x00000004 /* OPB Time Out Error Interrupt Enable */ +#define MALIER_OPBE 0x00000002 /* OPB Slave Error Interrupt Enable */ +#define MALIER_PLBE 0x00000001 /* PLB Error Interrupt Enable */ +/* DCRN_MALTXEOBISR */ +#define MALOBISR_CH0 0x80000000 /* EOB channel 1 bit */ +#define MALOBISR_CH2 0x40000000 /* EOB channel 2 bit */ + +#ifdef DCRN_OCM0_BASE +#define DCRN_OCMISARC (DCRN_OCM0_BASE + 0x0) /* OCM Instr Side Addr Range Compare */ +#define DCRN_OCMISCR (DCRN_OCM0_BASE + 0x1) /* OCM Instr Side Control */ +#define DCRN_OCMDSARC (DCRN_OCM0_BASE + 0x2) /* OCM Data Side Addr Range Compare */ +#define DCRN_OCMDSCR (DCRN_OCM0_BASE + 0x3) /* OCM Data Side Control */ +#endif + +#ifdef DCRN_PLB0_BASE +#define DCRN_PLB0_BESR (DCRN_PLB0_BASE + 0x0) +#define DCRN_PLB0_BEAR (DCRN_PLB0_BASE + 0x2) +/* doesn't exist on stb03xxx? */ +#define DCRN_PLB0_ACR (DCRN_PLB0_BASE + 0x3) +#endif + +#ifdef DCRN_PLB1_BASE +#define DCRN_PLB1_BESR (DCRN_PLB1_BASE + 0x0) +#define DCRN_PLB1_BEAR (DCRN_PLB1_BASE + 0x1) +/* doesn't exist on stb03xxx? */ +#define DCRN_PLB1_ACR (DCRN_PLB1_BASE + 0x2) +#endif + +#ifdef DCRN_PLLMR_BASE +#define DCRN_PLLMR (DCRN_PLLMR_BASE + 0x0) /* PL1 Mode */ +#endif + +#ifdef DCRN_POB0_BASE +#define DCRN_POB0_BESR0 (DCRN_POB0_BASE + 0x0) +#define DCRN_POB0_BEAR (DCRN_POB0_BASE + 0x2) +#define DCRN_POB0_BESR1 (DCRN_POB0_BASE + 0x4) +#endif + +#ifdef DCRN_SCCR_BASE +#define DCRN_SCCR (DCRN_SCCR_BASE + 0x0) +#endif + +#ifdef DCRN_SDRAM0_BASE +#define DCRN_SDRAM0_CFGADDR (DCRN_SDRAM0_BASE + 0x0) /* Mem Ctrlr Address */ +#define DCRN_SDRAM0_CFGDATA (DCRN_SDRAM0_BASE + 0x1) /* Mem Ctrlr Data */ +#endif + +#ifdef DCRN_UIC0_BASE +#define DCRN_UIC0_SR (DCRN_UIC0_BASE + 0x0) +#define DCRN_UIC0_ER (DCRN_UIC0_BASE + 0x2) +#define DCRN_UIC0_CR (DCRN_UIC0_BASE + 0x3) +#define DCRN_UIC0_PR (DCRN_UIC0_BASE + 0x4) +#define DCRN_UIC0_TR (DCRN_UIC0_BASE + 0x5) +#define DCRN_UIC0_MSR (DCRN_UIC0_BASE + 0x6) +#define DCRN_UIC0_VR (DCRN_UIC0_BASE + 0x7) +#define DCRN_UIC0_VCR (DCRN_UIC0_BASE + 0x8) +#endif + +#ifdef DCRN_UIC1_BASE +#define DCRN_UIC1_SR (DCRN_UIC1_BASE + 0x0) +#define DCRN_UIC1_SRS (DCRN_UIC1_BASE + 0x1) +#define DCRN_UIC1_ER (DCRN_UIC1_BASE + 0x2) +#define DCRN_UIC1_CR (DCRN_UIC1_BASE + 0x3) +#define DCRN_UIC1_PR (DCRN_UIC1_BASE + 0x4) +#define DCRN_UIC1_TR (DCRN_UIC1_BASE + 0x5) +#define DCRN_UIC1_MSR (DCRN_UIC1_BASE + 0x6) +#define DCRN_UIC1_VR (DCRN_UIC1_BASE + 0x7) +#define DCRN_UIC1_VCR (DCRN_UIC1_BASE + 0x8) +#endif + +#ifdef DCRN_SDRAM0_BASE +#define DCRN_SDRAM0_CFGADDR (DCRN_SDRAM0_BASE + 0x0) /* Memory Controller Address */ +#define DCRN_SDRAM0_CFGDATA (DCRN_SDRAM0_BASE + 0x1) /* Memory Controller Data */ +#endif + +#ifdef DCRN_OCM0_BASE +#define DCRN_OCMISARC (DCRN_OCM0_BASE + 0x0) /* OCM Instr Side Addr Range Compare */ +#define DCRN_OCMISCR (DCRN_OCM0_BASE + 0x1) /* OCM Instr Side Control */ +#define DCRN_OCMDSARC (DCRN_OCM0_BASE + 0x2) /* OCM Data Side Addr Range Compare */ +#define DCRN_OCMDSCR (DCRN_OCM0_BASE + 0x3) /* OCM Data Side Control */ +#endif + +#endif /* __ASM_IBM403_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/ibm4xx.h b/include/asm-ppc/ibm4xx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/ibm4xx.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,100 @@ +/* + * + * Copyright (c) 1999 Grant Erickson + * + * Module name: ibm4xx.h + * + * Description: + * A generic include file which pulls in appropriate include files + * for specific board types based on configuration settings. + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM4XX_H__ +#define __ASM_IBM4XX_H__ + +#include + +#ifdef CONFIG_4xx + +#if defined(CONFIG_ASH) +#include +#endif + +#if defined (CONFIG_CEDER) +#include +#endif + +#if defined(CONFIG_CPCI405) +#include +#endif + +#if defined(CONFIG_EP405) +#include +#endif + +#if defined(CONFIG_OAK) +#include +#endif + +#if defined(CONFIG_REDWOOD_4) +#include +#endif + +#if defined(CONFIG_REDWOOD_5) +#include +#endif + +#if defined(CONFIG_WALNUT) +#include +#endif + +#ifndef PPC4xx_MACHINE_NAME +#define PPC4xx_MACHINE_NAME "Unidentified 4xx class" +#endif + +#ifndef NR_BOARD_IRQS +#define NR_BOARD_IRQS 0 +#endif + +/* IO_BASE is for PCI I/O. + * ISA not supported, just here to resolve copilation. + */ + +#ifndef _IO_BASE +#define _IO_BASE 0xe8000000 /* The PCI address window */ +#define _ISA_MEM_BASE 0 +#define PCI_DRAM_OFFSET 0 +#endif + +/* + * The "residual" board information structure the boot loader passes + * into the kernel. + */ +#ifndef __ASSEMBLY__ +extern unsigned char __res[]; + +/* Device Control Registers */ + +#define stringify(s) tostring(s) +#define tostring(s) #s + +#define mfdcr(rn) mfdcr_or_dflt(rn, 0) + +#define mfdcr_or_dflt(rn,default_rval) \ + ({unsigned int rval; \ + if (rn == 0) \ + rval = default_rval; \ + else \ + asm volatile("mfdcr %0," stringify(rn) : "=r" (rval)); \ + rval;}) + +#define mtdcr(rn, v) \ + {if (rn != 0) \ + asm volatile("mtdcr " stringify(rn) ",%0" : : "r" (v));} + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_4xx */ +#endif /* __ASM_IBM4XX_H__ */ +#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/ide.h b/include/asm-ppc/ide.h --- a/include/asm-ppc/ide.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/ide.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ide.h 1.16 09/28/01 07:54:24 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/include/asm-ppc/ide.h @@ -73,10 +73,29 @@ return 0; } +/* + * This is only used for PC-style IDE controllers (e.g. as on PReP) + * or for PCI IDE devices, not for other types of IDE interface such + * as the pmac IDE interfaces. + */ static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) { + ide_ioreg_t reg = data_port; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) + hw->io_ports[i] = reg++; + if (ctrl_port) { + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + } else { + hw->io_ports[IDE_CONTROL_OFFSET] = + hw->io_ports[IDE_DATA_OFFSET] + 0x206; + } + if (irq != NULL) + *irq = 0; + hw->io_ports[IDE_IRQ_OFFSET] = 0; if (ppc_ide_md.ide_init_hwif != NULL) ppc_ide_md.ide_init_hwif(hw, data_port, ctrl_port, irq); } diff -Nru a/include/asm-ppc/io.h b/include/asm-ppc/io.h --- a/include/asm-ppc/io.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/io.h Tue Feb 19 18:08:58 2002 @@ -1,12 +1,14 @@ /* - * BK Id: SCCS/s.io.h 1.14 10/16/01 15:58:42 trini + * BK Id: %F% %I% %G% %U% %#% */ + #ifdef __KERNEL__ #ifndef _PPC_IO_H #define _PPC_IO_H #include #include + #include #include @@ -25,24 +27,40 @@ #define PREP_PCI_DRAM_OFFSET 0x80000000 #if defined(CONFIG_4xx) -#include +#include #elif defined(CONFIG_8xx) #include #elif defined(CONFIG_8260) #include #elif defined(CONFIG_APUS) -#define _IO_BASE 0 -#define _ISA_MEM_BASE 0 +#define _IO_BASE 0 +#define _ISA_MEM_BASE 0 #define PCI_DRAM_OFFSET 0 #else /* Everyone else */ -extern unsigned long isa_io_base; -extern unsigned long isa_mem_base; -extern unsigned long pci_dram_offset; #define _IO_BASE isa_io_base #define _ISA_MEM_BASE isa_mem_base #define PCI_DRAM_OFFSET pci_dram_offset #endif /* Platform-dependant I/O */ +extern unsigned long isa_io_base; +extern unsigned long isa_mem_base; +extern unsigned long pci_dram_offset; + +#if defined(CONFIG_PPC_ISERIES) +#include +#if defined(CONFIG_PCI) + #include + #endif /* defined(CONFIG_PCI) */ +#endif /* CONFIG_PPC_ISERIES */ + +#if defined(CONFIG_PPC_ISERIES) && defined(CONFIG_PCI) +#define readb(addr) iSeries_Readb((u32*)(addr)) +#define readw(addr) iSeries_Readw((u32*)(addr)) +#define readl(addr) iSeries_Readl((u32*)(addr)) +#define writeb(data, addr) iSeries_Writeb(data,(u32*)(addr)) +#define writew(data, addr) iSeries_Writew(data,(u32*)(addr)) +#define writel(data, addr) iSeries_Writel(data,(u32*)(addr)) +#else #define readb(addr) in_8((volatile u8 *)(addr)) #define writeb(b,addr) out_8((volatile u8 *)(addr), (b)) #if defined(CONFIG_APUS) @@ -56,6 +74,7 @@ #define writew(b,addr) out_le16((volatile u16 *)(addr),(b)) #define writel(b,addr) out_le32((volatile u32 *)(addr),(b)) #endif +#endif /* CONFIG_PPC_ISERIES && defined(CONFIG_PCI) */ #define __raw_readb(addr) (*(volatile unsigned char *)(addr)) @@ -80,9 +99,22 @@ #ifdef CONFIG_ALL_PPC /* - * We have to handle possible machine checks here on powermacs - * and potentially some CHRPs -- paulus. + * On powermacs, we will get a machine check exception if we + * try to read data from a non-existent I/O port. Because the + * machine check is an asynchronous exception, it isn't + * well-defined which instruction SRR0 will point to when the + * exception occurs. + * With the sequence below (twi; isync; nop), we have found that + * the machine check occurs on one of the three instructions on + * all PPC implementations tested so far. The twi and isync are + * needed on the 601 (in fact twi; sync works too), the isync and + * nop are needed on 604[e|r], and any of twi, sync or isync will + * work on 603[e], 750, 74x0. + * The twi creates an explicit data dependency on the returned + * value which seems to be needed to make the 601 wait for the + * load to finish. */ + #define __do_in_asm(name, op) \ extern __inline__ unsigned int name(unsigned int port) \ { \ @@ -137,6 +169,14 @@ #define inl(port) in_be32((u32 *)((port)+_IO_BASE)) #define outl(val, port) out_be32((u32 *)((port)+_IO_BASE), (val)) +#elif defined(CONFIG_PPC_ISERIES) && defined(CONFIG_PCI) +#define inb(addr) iSeries_Readb((u32*)(addr)) +#define inw(addr) iSeries_Readw((u32*)(addr)) +#define inl(addr) iSeries_Readl((u32*)(addr)) +#define outb(data,addr) iSeries_Writeb(data,(u32*)(addr)) +#define outw(data,addr) iSeries_Writew(data,(u32*)(addr)) +#define outl(data,addr) iSeries_Writel(data,(u32*)(addr)) + #else /* not APUS or ALL_PPC */ #define inb(port) in_8((u8 *)((port)+_IO_BASE)) #define outb(val, port) out_8((u8 *)((port)+_IO_BASE), (val)) @@ -178,8 +218,13 @@ #define IO_SPACE_LIMIT ~0 #define memset_io(a,b,c) memset((void *)(a),(b),(c)) +#ifdef CONFIG_PPC_ISERIES +#define memcpy_fromio(a,b,c) iSeries_memcpy_fromio((void *)(a), (void *)(b), (c)) +#define memcpy_toio(a,b,c) iSeries_memcpy_toio((void *)(a), (void *)(b), (c)) +#else #define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) #define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) +#endif #ifdef __KERNEL__ /* @@ -265,7 +310,7 @@ __asm__ __volatile__ ("eieio" : : : "memory"); } -/* Enforce in-order execution of data I/O. +/* Enforce in-order execution of data I/O. * No distinction between read/write on PPC; use eieio for all three. */ #define iobarrier_rw() eieio() diff -Nru a/include/asm-ppc/irq.h b/include/asm-ppc/irq.h --- a/include/asm-ppc/irq.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/irq.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.irq.h 1.9 05/17/01 18:14:24 cort + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef _ASM_IRQ_H @@ -14,7 +14,7 @@ extern void enable_irq(unsigned int); #if defined(CONFIG_4xx) - +#include /* * The PowerPC 403 cores' Asynchronous Interrupt Controller (AIC) has * 32 possible interrupts, a majority of which are not implemented on @@ -29,23 +29,19 @@ * */ -#define NR_IRQS 32 +#define NR_AIC_IRQS 32 +#define NR_IRQS (NR_AIC_IRQS + NR_BOARD_IRQS) + +static __inline__ int +irq_cannonicalize(int irq) +{ + return (irq); +} -#define AIC_INT0 (0) -#define AIC_INT4 (4) -#define AIC_INT5 (5) -#define AIC_INT6 (6) -#define AIC_INT7 (7) -#define AIC_INT8 (8) -#define AIC_INT9 (9) -#define AIC_INT10 (10) -#define AIC_INT11 (11) -#define AIC_INT27 (27) -#define AIC_INT28 (28) -#define AIC_INT29 (29) -#define AIC_INT30 (30) -#define AIC_INT31 (31) +#elif defined (CONFIG_NP405) +#define NR_AIC_IRQS 32 +#define NR_IRQS (NR_AIC_IRQS + NR_BOARD_IRQS) static __inline__ int irq_cannonicalize(int irq) @@ -124,48 +120,6 @@ } #else /* CONFIG_4xx + CONFIG_8xx */ - -#if defined(CONFIG_APUS) -/* - * This structure is used to chain together the ISRs for a particular - * interrupt source (if it supports chaining). - */ -typedef struct irq_node { - void (*handler)(int, void *, struct pt_regs *); - unsigned long flags; - void *dev_id; - const char *devname; - struct irq_node *next; -} irq_node_t; - -/* - * This structure has only 4 elements for speed reasons - */ -typedef struct irq_handler { - void (*handler)(int, void *, struct pt_regs *); - unsigned long flags; - void *dev_id; - const char *devname; -} irq_handler_t; - -/* count of spurious interrupts */ -extern volatile unsigned int num_spurious; - -extern int sys_request_irq(unsigned int, - void (*)(int, void *, struct pt_regs *), - unsigned long, const char *, void *); -extern void sys_free_irq(unsigned int, void *); - -/* - * This function returns a new irq_node_t - */ -extern irq_node_t *new_irq_node(void); - -/* Number of m68k interrupts */ -#define SYS_IRQS 8 - -#endif /* CONFIG_APUS */ - /* * this is the # irq's for all ppc arch's (pmac/chrp/prep) * so it is the max of them all @@ -211,19 +165,14 @@ static __inline__ int irq_cannonicalize(int irq) { if (ppc_md.irq_cannonicalize) - { return ppc_md.irq_cannonicalize(irq); - } - else - { - return irq; - } + return irq; } #endif #define NR_MASK_WORDS ((NR_IRQS + 31) / 32) -/* pendatic: these are long because they are used with set_bit --RR */ +/* pedantic: these are long because they are used with set_bit --RR */ extern unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; extern unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; extern atomic_t ppc_n_lost_interrupts; diff -Nru a/include/asm-ppc/ivms8.h b/include/asm-ppc/ivms8.h --- a/include/asm-ppc/ivms8.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,59 +0,0 @@ -/* - * BK Id: SCCS/s.ivms8.h 1.8 10/26/01 10:14:09 trini - */ -/* - * Speech Design Integrated Voicemail board specific definitions - * - IVMS8 (small, 8 channels) - * - IVML24 (large, 24 channels) - * - * In 2.5 when we force a new bootloader, we can merge these two, and add - * in _MACH_'s for them. -- Tom - * - * Copyright (c) 2000, 2001 Wolfgang Denk (wd@denx.de) - */ - -#ifdef __KERNEL__ -#ifndef __ASM_IVMS8_H__ -#define __ASM_IVMS8_H__ - -#include - -#include - -#define IVMS_IMMR_BASE 0xFFF00000 /* phys. addr of IMMR */ -#define IVMS_IMAP_SIZE (64 * 1024) /* size of mapped area */ - -#define IMAP_ADDR IVMS_IMMR_BASE /* phys. base address of IMMR area */ -#define IMAP_SIZE IVMS_IMAP_SIZE /* mapped size of IMMR area */ - -#define PCMCIA_MEM_ADDR ((uint)0xFE100000) -#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) - -#define FEC_INTERRUPT 9 /* = SIU_LEVEL4 */ -#define IDE0_INTERRUPT 10 /* = IRQ5 */ -#define CPM_INTERRUPT 11 /* = SIU_LEVEL5 (was: SIU_LEVEL2) */ -#define PHY_INTERRUPT 12 /* = IRQ6 */ - -/* override the default number of IDE hardware interfaces */ -#define MAX_HWIFS 1 - -/* - * Definitions for IDE0 Interface - */ -#define IDE0_BASE_OFFSET 0x0000 /* Offset in PCMCIA memory */ -#define IDE0_DATA_REG_OFFSET 0x0000 -#define IDE0_ERROR_REG_OFFSET 0x0081 -#define IDE0_NSECTOR_REG_OFFSET 0x0082 -#define IDE0_SECTOR_REG_OFFSET 0x0083 -#define IDE0_LCYL_REG_OFFSET 0x0084 -#define IDE0_HCYL_REG_OFFSET 0x0085 -#define IDE0_SELECT_REG_OFFSET 0x0086 -#define IDE0_STATUS_REG_OFFSET 0x0087 -#define IDE0_CONTROL_REG_OFFSET 0x0106 -#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ - -/* We don't use the 8259. */ -#define NR_8259_INTS 0 - -#endif /* __ASM_IVMS8_H__ */ -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/keyboard.h b/include/asm-ppc/keyboard.h --- a/include/asm-ppc/keyboard.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/keyboard.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.keyboard.h 1.11 08/29/01 10:07:29 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * linux/include/asm-ppc/keyboard.h @@ -13,8 +13,8 @@ * like the intel pc for prep systems, different for power macs. */ -#ifndef __ASMPPC_KEYBOARD_H -#define __ASMPPC_KEYBOARD_H +#ifndef __ASM_KEYBOARD_H__ +#define __ASM_KEYBOARD_H__ #ifdef __KERNEL__ @@ -25,8 +25,14 @@ #include #include #include +/* IBM Spruce platform is different. */ +#ifdef CONFIG_SPRUCE +#include +#endif +#ifndef KEYBOARD_IRQ #define KEYBOARD_IRQ 1 +#endif #define DISABLE_KBD_DURING_INTERRUPTS 0 #define INIT_KBD @@ -85,10 +91,12 @@ "keyboard", NULL) /* How to access the keyboard macros on this platform. */ +#ifndef kbd_read_input #define kbd_read_input() inb(KBD_DATA_REG) #define kbd_read_status() inb(KBD_STATUS_REG) #define kbd_write_output(val) outb(val, KBD_DATA_REG) #define kbd_write_command(val) outb(val, KBD_CNTL_REG) +#endif /* Some stoneage hardware needs delays after some operations. */ #define kbd_pause() do { } while(0) @@ -97,7 +105,9 @@ * Machine specific bits for the PS/2 driver */ +#ifndef AUX_IRQ #define AUX_IRQ 12 +#endif #define aux_request_irq(hand, dev_id) \ request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id) @@ -105,5 +115,4 @@ #define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id) #endif /* __KERNEL__ */ - -#endif /* __ASMPPC_KEYBOARD_H */ +#endif /* __ASM_KEYBOARD_H__ */ diff -Nru a/include/asm-ppc/keylargo.h b/include/asm-ppc/keylargo.h --- a/include/asm-ppc/keylargo.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/keylargo.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.keylargo.h 1.13 08/19/01 22:23:04 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * keylargo.h: definitions for using the "KeyLargo" I/O controller chip. @@ -27,14 +27,18 @@ #define KEYLARGO_GPIO_EXTINT_CNT 18 #define KEYLARGO_GPIO_0 0x6A #define KEYLARGO_GPIO_CNT 17 +#define KEYLARGO_GPIO_EXTINT_DUAL_EDGE 0x80 #define KEYLARGO_GPIO_OUTPUT_ENABLE 0x04 #define KEYLARGO_GPIO_OUTOUT_DATA 0x01 +#define KEYLARGO_GPIO_INPUT_DATA 0x02 /* Specific GPIO regs */ -#define KL_GPIO_MODEM_RESET (KEYLARGO_GPIO_0+0x03) /* Pangea */ +#define KL_GPIO_MODEM_RESET (KEYLARGO_GPIO_0+0x03) #define KL_GPIO_MODEM_POWER (KEYLARGO_GPIO_0+0x02) /* Pangea */ +#define KL_GPIO_SOUND_POWER (KEYLARGO_GPIO_0+0x05) + /* Hrm... this one is only to be used on Pismo. It seeem to also * control the timebase enable on other machines. Still to be * experimented... --BenH. @@ -54,7 +58,9 @@ #define KL_GPIO_RESET_CPU3 (KEYLARGO_GPIO_EXTINT_0+0x10) #define KL_GPIO_PMU_MESSAGE_IRQ (KEYLARGO_GPIO_EXTINT_0+0x09) -#define KL_GPIO_PMU_MESSAGE_BIT 0x02 +#define KL_GPIO_PMU_MESSAGE_BIT KEYLARGO_GPIO_INPUT_DATA + +#define KL_GPIO_MEDIABAY_IRQ (KEYLARGO_GPIO_EXTINT_0+0x0e) #define KL_GPIO_AIRPORT_0 (KEYLARGO_GPIO_EXTINT_0+0x0a) #define KL_GPIO_AIRPORT_1 (KEYLARGO_GPIO_EXTINT_0+0x0d) @@ -65,43 +71,47 @@ /* * Bits in feature control register */ -#define KL_MBCR_MB0_DEV_ENABLE 0x00001000 +#define KL_MBCR_MB0_PCI_ENABLE 0x00000800 /* exist ? */ +#define KL_MBCR_MB0_IDE_ENABLE 0x00001000 +#define KL_MBCR_MB0_FLOPPY_ENABLE 0x00002000 /* exist ? */ +#define KL_MBCR_MB0_SOUND_ENABLE 0x00004000 /* hrm... */ +#define KL_MBCR_MB0_DEV_MASK 0x00007800 #define KL_MBCR_MB0_DEV_POWER 0x00000400 #define KL_MBCR_MB0_DEV_RESET 0x00000200 #define KL_MBCR_MB0_ENABLE 0x00000100 -#define KL_MBCR_MB1_DEV_ENABLE 0x10000000 +#define KL_MBCR_MB1_PCI_ENABLE 0x08000000 /* exist ? */ +#define KL_MBCR_MB1_IDE_ENABLE 0x10000000 +#define KL_MBCR_MB1_FLOPPY_ENABLE 0x20000000 /* exist ? */ +#define KL_MBCR_MB1_SOUND_ENABLE 0x40000000 /* hrm... */ +#define KL_MBCR_MB1_DEV_MASK 0x78000000 #define KL_MBCR_MB1_DEV_POWER 0x04000000 #define KL_MBCR_MB1_DEV_RESET 0x02000000 #define KL_MBCR_MB1_ENABLE 0x01000000 -#define KL0_SCC_B_INTF_ENABLE 0x00000001 /* ??? */ -#define KL0_SCC_A_INTF_ENABLE 0x00000002 /* ??? */ +#define KL0_SCC_B_INTF_ENABLE 0x00000001 +#define KL0_SCC_A_INTF_ENABLE 0x00000002 #define KL0_SCC_SLOWPCLK 0x00000004 #define KL0_SCC_RESET 0x00000008 #define KL0_SCCA_ENABLE 0x00000010 #define KL0_SCCB_ENABLE 0x00000020 #define KL0_SCC_CELL_ENABLE 0x00000040 +#define KL0_IRDA_HIGH_BAND 0x00000100 +#define KL0_IRDA_SOURCE2_SEL 0x00000200 +#define KL0_IRDA_SOURCE1_SEL 0x00000400 +#define KL0_IRDA_RESET 0x00000800 +#define KL0_IRDA_DEFAULT1 0x00001000 +#define KL0_IRDA_DEFAULT0 0x00002000 +#define KL0_IRDA_FAST_CONNECT 0x00004000 +#define KL0_IRDA_ENABLE 0x00008000 +#define KL0_IRDA_CLK32_ENABLE 0x00010000 +#define KL0_IRDA_CLK19_ENABLE 0x00020000 #define KL0_USB0_PAD_SUSPEND0 0x00040000 #define KL0_USB0_PAD_SUSPEND1 0x00080000 #define KL0_USB0_CELL_ENABLE 0x00100000 #define KL0_USB1_PAD_SUSPEND0 0x00400000 #define KL0_USB1_PAD_SUSPEND1 0x00800000 #define KL0_USB1_CELL_ENABLE 0x01000000 -/* KL id 0x22 only */ #define KL0_USB_REF_SUSPEND 0x10000000 -#define KL0_IRDA_ENABLE 0x00008000 -#define KL0_IRDA_CLK32_ENABLE 0x00010000 -#define KL0_IRDA_CLK19_ENABLE 0x00020000 -/* KL id 0x25 (pangea) only */ -#define KL0_USB1_PAD_SUSPEND_SEL 0x00020000 -#define KL0_USB1_REF_SUSPEND 0x00010000 -#define KL0_USB1_REF_SUSPEND_SEL 0x00008000 -#define KL0_USB1_PMI 0x00004000 -#define KL0_USB0_PAD_SUSPEND_SEL 0x00002000 -#define KL0_USB0_REF_SUSPEND 0x00001000 -#define KL0_USB0_REF_SUSPEND_SEL 0x00000800 -#define KL0_USB0_PMI 0x00000400 - #define KL0_SERIAL_ENABLE (KL0_SCC_B_INTF_ENABLE | \ KL0_SCC_SLOWPCLK | \ @@ -128,8 +138,9 @@ #define KL2_IOBUS_ENABLE 0x00000002 #define KL2_SLEEP_STATE_BIT 0x00000100 #define KL2_MPIC_ENABLE 0x00020000 -#define KL2_MODEM_POWER_N 0x02000000 -#define KL2_AIRPORT_RESET_N 0x08000000 /* Or power ? */ +#define KL2_ALT_DATA_OUT 0x02000000 +#define KL2_MEM_IS_BIG 0x04000000 +#define KL2_CARDSEL_16 0x08000000 #define KL3_SHUTDOWN_PLL_TOTAL 0x00000001 #define KL3_SHUTDOWN_PLLKW6 0x00000002 @@ -149,11 +160,11 @@ #define KL3_STOPPING33_ENABLED 0x00080000 /* Port 0,1 : bus 0, port 2,3 : bus 1 */ -#define KL4_SET_PORT_ENABLE(p) (0x00000008 << ((p)<<3)) -#define KL4_SET_PORT_RESUME(p) (0x00000004 << ((p)<<3)) -#define KL4_SET_PORT_CONNECT(p) (0x00000002 << ((p)<<3)) -#define KL4_SET_PORT_DISCONNECT(p) (0x00000001 << ((p)<<3)) -#define KL4_GET_PORT_RESUME(p) (0x00000040 << ((p)<<3)) -#define KL4_GET_PORT_CONNECT(p) (0x00000020 << ((p)<<3)) -#define KL4_GET_PORT_DISCONNECT(p) (0x00000010 << ((p)<<3)) +#define KL4_PORT_WAKEUP_ENABLE(p) (0x00000008 << ((p)<<3)) +#define KL4_PORT_RESUME_WAKE_EN(p) (0x00000004 << ((p)<<3)) +#define KL4_PORT_CONNECT_WAKE_EN(p) (0x00000002 << ((p)<<3)) +#define KL4_PORT_DISCONNECT_WAKE_EN(p) (0x00000001 << ((p)<<3)) +#define KL4_PORT_RESUME_STAT(p) (0x00000040 << ((p)<<3)) +#define KL4_PORT_CONNECT_STAT(p) (0x00000020 << ((p)<<3)) +#define KL4_PORT_DISCONNECT_STAT(p) (0x00000010 << ((p)<<3)) diff -Nru a/include/asm-ppc/kgdb.h b/include/asm-ppc/kgdb.h --- a/include/asm-ppc/kgdb.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/kgdb.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.kgdb.h 1.5 05/17/01 18:14:24 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * kgdb.h: Defines and declarations for serial line source level @@ -38,8 +38,6 @@ void kgdb(struct pt_regs *regs); int kgdb_iabr_match(struct pt_regs *regs); int kgdb_dabr_match(struct pt_regs *regs); -static void kgdb_fault_handler(struct pt_regs *regs); -static void handle_exception (struct pt_regs *regs); /* * external low-level support routines (ie macserial.c) diff -Nru a/include/asm-ppc/machdep.h b/include/asm-ppc/machdep.h --- a/include/asm-ppc/machdep.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/machdep.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.machdep.h 1.25 11/13/01 21:26:07 paulus + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef _PPC_MACHDEP_H @@ -16,6 +16,11 @@ struct pci_dev; struct seq_file; +/* We export this macro for external modules like Alsa to know if + * ppc_md.feature_call is implemented or not + */ +#define CONFIG_PPC_HAS_FEATURE_CALLS + struct machdep_calls { void (*setup_arch)(void); /* Optional, may be NULL. */ @@ -94,6 +99,12 @@ /* this is for modules, since _machine can be a define -- Cort */ int ppc_machine; + + /* Motherboard/chipset features. This is a kind of general purpose + * hook used to control some machine specific features (like reset + * lines, chip power control, etc...). + */ + int (*feature_call)(unsigned int feature, ...); #ifdef CONFIG_SMP /* functions for dealing with other cpus */ diff -Nru a/include/asm-ppc/mbx.h b/include/asm-ppc/mbx.h --- a/include/asm-ppc/mbx.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,98 +0,0 @@ -/* - * BK Id: SCCS/s.mbx.h 1.11 08/17/01 15:23:17 paulus - */ -/* - * A collection of structures, addresses, and values associated with - * the Motorola MBX boards. This was originally created for the - * MBX860, and probably needs revisions for other boards (like the 821). - * When this file gets out of control, we can split it up into more - * meaningful pieces. - * - * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) - */ -#ifdef __KERNEL__ -#ifndef __MACH_MBX_DEFS -#define __MACH_MBX_DEFS - -#ifndef __ASSEMBLY__ -/* A Board Information structure that is given to a program when - * EPPC-Bug starts it up. - */ -typedef struct bd_info { - unsigned int bi_tag; /* Should be 0x42444944 "BDID" */ - unsigned int bi_size; /* Size of this structure */ - unsigned int bi_revision; /* revision of this structure */ - unsigned int bi_bdate; /* EPPCbug date, i.e. 0x11061997 */ - unsigned int bi_memstart; /* Memory start address */ - unsigned int bi_memsize; /* Memory (end) size in bytes */ - unsigned int bi_intfreq; /* Internal Freq, in Hz */ - unsigned int bi_busfreq; /* Bus Freq, in Hz */ - unsigned int bi_clun; /* Boot device controller */ - unsigned int bi_dlun; /* Boot device logical dev */ - - /* These fields are not part of the board information structure - * provided by the boot rom. They are filled in by embed_config.c - * so we have the information consistent with other platforms. - */ - unsigned char bi_enetaddr[6]; - unsigned int bi_baudrate; -} bd_t; - -/* Memory map for the MBX as configured by EPPC-Bug. We could reprogram - * The SIU and PCI bridge, and try to use larger MMU pages, but the - * performance gain is not measureable and it certainly complicates the - * generic MMU model. - * - * In a effort to minimize memory usage for embedded applications, any - * PCI driver or ISA driver must request or map the region required by - * the device. For convenience (and since we can map up to 4 Mbytes with - * a single page table page), the MMU initialization will map the - * NVRAM, Status/Control registers, CPM Dual Port RAM, and the PCI - * Bridge CSRs 1:1 into the kernel address space. - */ -#define PCI_ISA_IO_ADDR ((unsigned)0x80000000) -#define PCI_ISA_IO_SIZE ((uint)(512 * 1024 * 1024)) -#define PCI_IDE_ADDR ((unsigned)0x81000000) -#define PCI_ISA_MEM_ADDR ((unsigned)0xc0000000) -#define PCI_ISA_MEM_SIZE ((uint)(512 * 1024 * 1024)) -#define PCMCIA_MEM_ADDR ((uint)0xe0000000) -#define PCMCIA_MEM_SIZE ((uint)(64 * 1024 * 1024)) -#define PCMCIA_DMA_ADDR ((uint)0xe4000000) -#define PCMCIA_DMA_SIZE ((uint)(64 * 1024 * 1024)) -#define PCMCIA_ATTRB_ADDR ((uint)0xe8000000) -#define PCMCIA_ATTRB_SIZE ((uint)(64 * 1024 * 1024)) -#define PCMCIA_IO_ADDR ((uint)0xec000000) -#define PCMCIA_IO_SIZE ((uint)(64 * 1024 * 1024)) -#define NVRAM_ADDR ((uint)0xfa000000) -#define NVRAM_SIZE ((uint)(1 * 1024 * 1024)) -#define MBX_CSR_ADDR ((uint)0xfa100000) -#define MBX_CSR_SIZE ((uint)(1 * 1024 * 1024)) -#define IMAP_ADDR ((uint)0xfa200000) -#define IMAP_SIZE ((uint)(64 * 1024)) -#define PCI_CSR_ADDR ((uint)0xfa210000) -#define PCI_CSR_SIZE ((uint)(64 * 1024)) - -/* Map additional physical space into well known virtual addresses. Due - * to virtual address mapping, these physical addresses are not accessible - * in a 1:1 virtual to physical mapping. - */ -#define ISA_IO_VIRT_ADDR ((uint)0xfa220000) -#define ISA_IO_VIRT_SIZE ((uint)64 * 1024) - -/* Interrupt assignments. - * These are defined (and fixed) by the MBX hardware implementation. - */ -#define POWER_FAIL_INT SIU_IRQ0 /* Power fail */ -#define TEMP_HILO_INT SIU_IRQ1 /* Temperature sensor */ -#define QSPAN_INT SIU_IRQ2 /* PCI Bridge (DMA CTLR?) */ -#define ISA_BRIDGE_INT SIU_IRQ3 /* All those PC things */ -#define COMM_L_INT SIU_IRQ6 /* MBX Comm expansion connector pin */ -#define STOP_ABRT_INT SIU_IRQ7 /* Stop/Abort header pin */ -#endif /* !__ASSEMBLY__ */ - -/* The MBX uses the 8259. -*/ -#define NR_8259_INTS 16 - -#endif -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/mediabay.h b/include/asm-ppc/mediabay.h --- a/include/asm-ppc/mediabay.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/mediabay.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.mediabay.h 1.5 05/17/01 18:14:25 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * mediabay.h: definitions for using the media bay @@ -12,10 +12,13 @@ #ifdef __KERNEL__ -#define MB_FD 0 /* media bay contains floppy drive */ -#define MB_FD1 1 /* media bay contains floppy drive */ -#define MB_CD 3 /* media bay contains ATA drive such as CD */ -#define MB_NO 7 /* media bay contains nothing */ +#define MB_FD 0 /* media bay contains floppy drive (automatic eject ?) */ +#define MB_FD1 1 /* media bay contains floppy drive (manual eject ?) */ +#define MB_SOUND 2 /* sound device ? */ +#define MB_CD 3 /* media bay contains ATA drive such as CD or ZIP */ +#define MB_PCI 5 /* media bay contains a PCI device */ +#define MB_POWER 6 /* media bay contains a Power device (???) */ +#define MB_NO 7 /* media bay contains nothing */ void media_bay_init(void); int check_media_bay(struct device_node *which_bay, int what); diff -Nru a/include/asm-ppc/mmu.h b/include/asm-ppc/mmu.h --- a/include/asm-ppc/mmu.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/mmu.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.mmu.h 1.10 06/28/01 15:50:17 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * PowerPC memory management structures @@ -114,9 +114,6 @@ P601_BATU batu; /* Upper register */ P601_BATL batl; /* Lower register */ } P601_BAT; - -extern void _tlbie(unsigned long va); /* invalidate a TLB entry */ -extern void _tlbia(void); /* invalidate all TLB entries */ #endif /* __ASSEMBLY__ */ diff -Nru a/include/asm-ppc/mmu_context.h b/include/asm-ppc/mmu_context.h --- a/include/asm-ppc/mmu_context.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/mmu_context.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.mmu_context.h 1.18 09/26/01 16:02:49 paulus + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef __PPC_MMU_CONTEXT_H diff -Nru a/include/asm-ppc/mpc10x.h b/include/asm-ppc/mpc10x.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/mpc10x.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,166 @@ +/* + * arch/ppc/kernel/mpc10x.h + * + * Common routines for the Motorola SPS MPC106/8240/107 Host bridge/Mem + * ctlr/EPIC/etc. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef __PPC_KERNEL_MPC10X_H +#define __PPC_KERNEL_MPC10X_H + +#include +#include + +/* + * The values here don't completely map everything but should work in most + * cases. + * + * MAP A (PReP Map) + * Processor: 0x80000000 - 0x807fffff -> PCI I/O: 0x00000000 - 0x007fffff + * Processor: 0xc0000000 - 0xdfffffff -> PCI MEM: 0x00000000 - 0x1fffffff + * PCI MEM: 0x80000000 -> Processor System Memory: 0x00000000 + * EUMB mapped to: ioremap_base - 0x00100000 (ioremap_base - 1 MB) + * + * MAP B (CHRP Map) + * Processor: 0xfe000000 - 0xfebfffff -> PCI I/O: 0x00000000 - 0x00bfffff + * Processor: 0x80000000 - 0xbfffffff -> PCI MEM: 0x80000000 - 0xbfffffff + * PCI MEM: 0x00000000 -> Processor System Memory: 0x00000000 + * EUMB mapped to: ioremap_base - 0x00100000 (ioremap_base - 1 MB) + */ + +/* + * Define the vendor/device IDs for the various bridges--should be added to + * + */ +#define MPC10X_BRIDGE_106 ((PCI_DEVICE_ID_MOTOROLA_MPC106 << 16) | \ + PCI_VENDOR_ID_MOTOROLA) +#define MPC10X_BRIDGE_8240 ((0x0003 << 16) | PCI_VENDOR_ID_MOTOROLA) +#define MPC10X_BRIDGE_107 ((0x0004 << 16) | PCI_VENDOR_ID_MOTOROLA) +#define MPC10X_BRIDGE_8245 ((0x0006 << 16) | PCI_VENDOR_ID_MOTOROLA) + +/* Define the type of map to use */ +#define MPC10X_MEM_MAP_A 1 +#define MPC10X_MEM_MAP_B 2 + +/* Map A (PReP Map) Defines */ +#define MPC10X_MAPA_CNFG_ADDR 0x80000cf8 +#define MPC10X_MAPA_CNFG_DATA 0x80000cfc + +#define MPC10X_MAPA_ISA_IO_BASE 0x80000000 +#define MPC10X_MAPA_ISA_MEM_BASE 0xc0000000 +#define MPC10X_MAPA_DRAM_OFFSET 0x80000000 + +#define MPC10X_MAPA_PCI_IO_START 0x00000000 +#define MPC10X_MAPA_PCI_IO_END (0x00800000 - 1) +#define MPC10X_MAPA_PCI_MEM_START 0x00000000 +#define MPC10X_MAPA_PCI_MEM_END (0x20000000 - 1) + +#define MPC10X_MAPA_PCI_MEM_OFFSET (MPC10X_MAPA_ISA_MEM_BASE - \ + MPC10X_MAPA_PCI_MEM_START) + +/* Map B (CHRP Map) Defines */ +#define MPC10X_MAPB_CNFG_ADDR 0xfec00000 +#define MPC10X_MAPB_CNFG_DATA 0xfee00000 + +#define MPC10X_MAPB_ISA_IO_BASE 0xfe000000 +#define MPC10X_MAPB_ISA_MEM_BASE 0x80000000 +#define MPC10X_MAPB_DRAM_OFFSET 0x00000000 + +#define MPC10X_MAPB_PCI_IO_START 0x00000000 +#define MPC10X_MAPB_PCI_IO_END (0x00c00000 - 1) +#define MPC10X_MAPB_PCI_MEM_START 0x80000000 +#define MPC10X_MAPB_PCI_MEM_END (0xc0000000 - 1) + +#define MPC10X_MAPB_PCI_MEM_OFFSET (MPC10X_MAPB_ISA_MEM_BASE - \ + MPC10X_MAPB_PCI_MEM_START) + +/* Set hose members to values appropriate for the mem map used */ +#define MPC10X_SETUP_HOSE(hose, map) { \ + (hose)->pci_mem_offset = MPC10X_MAP##map##_PCI_MEM_OFFSET; \ + (hose)->io_space.start = MPC10X_MAP##map##_PCI_IO_START; \ + (hose)->io_space.end = MPC10X_MAP##map##_PCI_IO_END; \ + (hose)->mem_space.start = MPC10X_MAP##map##_PCI_MEM_START; \ + (hose)->mem_space.end = MPC10X_MAP##map##_PCI_MEM_END; \ + (hose)->io_base_virt = (void *)MPC10X_MAP##map##_ISA_IO_BASE; \ +} + + +/* Miscellaneous Configuration register offsets */ +#define MPC10X_CFG_PIR_REG 0x09 +#define MPC10X_CFG_PIR_HOST_BRIDGE 0x00 +#define MPC10X_CFG_PIR_AGENT 0x01 + +#define MPC10X_CFG_EUMBBAR 0x78 + +#define MPC10X_CFG_PICR1_REG 0xa8 +#define MPC10X_CFG_PICR1_ADDR_MAP_MASK 0x00010000 +#define MPC10X_CFG_PICR1_ADDR_MAP_A 0x00010000 +#define MPC10X_CFG_PICR1_ADDR_MAP_B 0x00000000 +#define MPC10X_CFG_PICR1_ST_GATH_EN 0x00000040 + +#define MPC10X_CFG_MAPB_OPTIONS_REG 0xe0 +#define MPC10X_CFG_MAPB_OPTIONS_CFAE 0x80 /* CPU_FD_ALIAS_EN */ +#define MPC10X_CFG_MAPB_OPTIONS_PFAE 0x40 /* PCI_FD_ALIAS_EN */ +#define MPC10X_CFG_MAPB_OPTIONS_DR 0x20 /* DLL_RESET */ +#define MPC10X_CFG_MAPB_OPTIONS_PCICH 0x80 /* PCI_COMPATIBILITY_HOLE */ +#define MPC10X_CFG_MAPB_OPTIONS_PROCCH 0x40 /* PROC_COMPATIBILITY_HOLE */ + +/* Define offsets for the memory controller registers in the config space */ +#define MPC10X_MCTLR_MEM_START_1 0x80 /* Banks 0-3 */ +#define MPC10X_MCTLR_MEM_START_2 0x84 /* Banks 4-7 */ +#define MPC10X_MCTLR_EXT_MEM_START_1 0x88 /* Banks 0-3 */ +#define MPC10X_MCTLR_EXT_MEM_START_2 0x8c /* Banks 4-7 */ + +#define MPC10X_MCTLR_MEM_END_1 0x90 /* Banks 0-3 */ +#define MPC10X_MCTLR_MEM_END_2i 0x94 /* Banks 4-7 */ +#define MPC10X_MCTLR_EXT_MEM_END_1 0x98 /* Banks 0-3 */ +#define MPC10X_MCTLR_EXT_MEM_END_2 0x9c /* Banks 4-7 */ + +#define MPC10X_MCTLR_MEM_BANK_ENABLES 0xa0 + +/* Define some offset in the EUMB */ +#define MPC10X_EUMB_SIZE 0x00100000 /* Total EUMB size (1MB) */ + +#define MPC10X_EUMB_MU_OFFSET 0x00000000 /* Msg Unit reg offset */ +#define MPC10X_EUMB_MU_SIZE 0x00001000 /* Msg Unit reg size */ +#define MPC10X_EUMB_DMA_OFFSET 0x00001000 /* DMA Unit reg offset */ +#define MPC10X_EUMB_DMA_SIZE 0x00001000 /* DMA Unit reg size */ +#define MPC10X_EUMB_ATU_OFFSET 0x00002000 /* Addr xlate reg offset */ +#define MPC10X_EUMB_ATU_SIZE 0x00001000 /* Addr xlate reg size */ +#define MPC10X_EUMB_I2C_OFFSET 0x00003000 /* I2C Unit reg offset */ +#define MPC10X_EUMB_I2C_SIZE 0x00001000 /* I2C Unit reg size */ +#define MPC10X_EUMB_DUART_OFFSET 0x00004000 /* DUART Unit reg offset (8245) */ +#define MPC10X_EUMB_DUART_SIZE 0x00001000 /* DUART Unit reg size (8245) */ +#define MPC10X_EUMB_EPIC_OFFSET 0x00040000 /* EPIC offset in EUMB */ +#define MPC10X_EUMB_EPIC_SIZE 0x00030000 /* EPIC size */ +#define MPC10X_EUMB_PM_OFFSET 0x000fe000 /* Performance Monitor reg offset (8245) */ +#define MPC10X_EUMB_PM_SIZE 0x00001000 /* Performance Monitor reg size (8245) */ +#define MPC10X_EUMB_WP_OFFSET 0x000ff000 /* Data path diagnostic, watchpoint reg offset */ +#define MPC10X_EUMB_WP_SIZE 0x00001000 /* Data path diagnostic, watchpoint reg size */ + +/* + * Define some recommended places to put the EUMB regs. + * For both maps, recommend putting the EUMB from 0xeff00000 to 0xefffffff. + */ +extern unsigned long ioremap_base; +#define MPC10X_MAPA_EUMB_BASE (ioremap_base - MPC10X_EUMB_SIZE) +#define MPC10X_MAPB_EUMB_BASE MPC10X_MAPA_EUMB_BASE + + +int mpc10x_bridge_init(struct pci_controller *hose, + uint current_map, + uint new_map, + uint phys_eumb_base); +unsigned long mpc10x_get_mem_size(uint mem_map); +int mpc10x_enable_store_gathering(struct pci_controller *hose); + +#endif /* __PPC_KERNEL_MPC10X_H */ diff -Nru a/include/asm-ppc/mpc8260.h b/include/asm-ppc/mpc8260.h --- a/include/asm-ppc/mpc8260.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/mpc8260.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.mpc8260.h 1.5 05/17/01 18:14:25 cort + * BK Id: %F% %I% %G% %U% %#% */ /* This is the single file included by all MPC8260 build options. @@ -17,7 +17,23 @@ #ifdef CONFIG_8260 #ifdef CONFIG_EST8260 -#include +#include +#endif + +#ifdef CONFIG_SBS8260 +#include +#endif + +#ifdef CONFIG_RPX6 +#include +#endif + +#ifdef CONFIG_WILLOW +#include +#endif + +#ifdef CONFIG_TQM8260 +#include #endif /* I don't yet have the ISA or PCI stuff done....no 8260 with @@ -43,5 +59,5 @@ void *dev_id); #endif /* CONFIG_8260 */ -#endif +#endif /* !__CONFIG_8260_DEFS */ #endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/mpc8xx.h b/include/asm-ppc/mpc8xx.h --- a/include/asm-ppc/mpc8xx.h Tue Feb 19 18:09:00 2002 +++ b/include/asm-ppc/mpc8xx.h Tue Feb 19 18:09:00 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.mpc8xx.h 1.15 11/01/01 12:48:53 trini + * BK Id: %F% %I% %G% %U% %#% */ /* This is the single file included by all MPC8xx build options. @@ -17,40 +17,60 @@ #ifdef CONFIG_8xx #ifdef CONFIG_MBX -#include +#include #endif #ifdef CONFIG_FADS -#include +#include #endif #ifdef CONFIG_RPXLITE -#include +#include #endif #ifdef CONFIG_BSEIP -#include +#include #endif #ifdef CONFIG_RPXCLASSIC -#include +#include #endif #if defined(CONFIG_TQM8xxL) -#include +#include #endif #if defined(CONFIG_SPD823TS) -#include +#include #endif #if defined(CONFIG_IVMS8) || defined(CONFIG_IVML24) -#include +#include #endif -/* I need this to get pt_regs....... -*/ -#include +#if defined(CONFIG_HERMES_PRO) +#include +#endif + +#if defined(CONFIG_IP860) +#include +#endif + +#if defined(CONFIG_LWMON) +#include +#endif + +#if defined(CONFIG_PCU_E) +#include +#endif + +#if defined(CONFIG_CCM) +#include +#endif + +#if defined(CONFIG_LANTEC) +#include +#endif /* Currently, all 8xx boards that support a processor to PCI/ISA bridge * use the same memory map. @@ -74,15 +94,12 @@ #endif #ifndef __ASSEMBLY__ -extern unsigned long isa_io_base; -extern unsigned long isa_mem_base; -extern unsigned long pci_dram_offset; - /* The "residual" data board information structure the boot loader * hands to us. */ extern unsigned char __res[]; +struct pt_regs; extern int request_8xxirq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, @@ -90,5 +107,5 @@ void *dev_id); #endif /* !__ASSEMBLY__ */ #endif /* CONFIG_8xx */ -#endif +#endif /* __CONFIG_8xx_DEFS */ #endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/oak.h b/include/asm-ppc/oak.h --- a/include/asm-ppc/oak.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,65 +0,0 @@ -/* - * BK Id: SCCS/s.oak.h 1.12 10/11/01 13:05:07 trini - */ -/* - * - * Copyright (c) 1999 Grant Erickson - * - * Module name: oak.h - * - * Description: - * Macros, definitions, and data structures specific to the IBM PowerPC - * 403G{A,B,C,CX} "Oak" evaluation board. Anything specific to the pro- - * cessor itself is defined elsewhere. - * - */ - -#ifdef __KERNEL__ -#ifndef __OAK_H__ -#define __OAK_H__ - -#define _IO_BASE 0 -#define _ISA_MEM_BASE 0 -#define PCI_DRAM_OFFSET 0 - -/* Memory map for the "Oak" evaluation board */ - -#define PPC403SPU_IO_BASE 0x40000000 /* 403 On-chip serial port */ -#define PPC403SPU_IO_SIZE 0x00000008 -#define OAKSERIAL_IO_BASE 0x7E000000 /* NS16550DV serial port */ -#define OAKSERIAL_IO_SIZE 0x00000008 -#define OAKNET_IO_BASE 0xF4000000 /* NS83902AV Ethernet */ -#define OAKNET_IO_SIZE 0x00000040 -#define OAKPROM_IO_BASE 0xFFFE0000 /* AMD 29F010 Flash ROM */ -#define OAKPROM_IO_SIZE 0x00020000 - - -/* Interrupt assignments fixed by the hardware implementation */ - -/* This is annoying kbuild-2.4 problem. -- Tom */ - -#define PPC403SPU_RX_INT 4 /* AIC_INT4 */ -#define PPC403SPU_TX_INT 5 /* AIC_INT5 */ -#define OAKNET_INT 27 /* AIC_INT27 */ -#define OAKSERIAL_INT 28 /* AIC_INT28 */ - -#ifndef __ASSEMBLY__ -/* - * Data structure defining board information maintained by the boot - * ROM on IBM's "Oak" evaluation board. An effort has been made to - * keep the field names consistent with the 8xx 'bd_t' board info - * structures. - */ - -typedef struct board_info { - unsigned char bi_s_version[4]; /* Version of this structure */ - unsigned char bi_r_version[30]; /* Version of the IBM ROM */ - unsigned int bi_memsize; /* DRAM installed, in bytes */ - unsigned char bi_enetaddr[6]; /* Ethernet MAC address */ - unsigned int bi_intfreq; /* Processor speed, in Hz */ - unsigned int bi_busfreq; /* Bus speed, in Hz */ -} bd_t; - -#endif /* !__ASSEMBLY__ */ -#endif /* __OAK_H__ */ -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/ohare.h b/include/asm-ppc/ohare.h --- a/include/asm-ppc/ohare.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/ohare.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ohare.h 1.5 05/17/01 18:14:25 cort + * BK Id: %F% %I% %G% %U% %#% */ /* * ohare.h: definitions for using the "O'Hare" I/O controller chip. @@ -11,7 +11,8 @@ */ /* offset from ohare base for feature control register */ -#define OHARE_FEATURE_REG 0x38 +#define OHARE_MBCR 0x34 +#define OHARE_FCR 0x38 /* * Bits in feature control register. @@ -25,6 +26,7 @@ #define OH_BAY_FLOPPY_ENABLE 0x10 #define OH_IDE0_ENABLE 0x20 #define OH_IDE0_RESET_N 0x40 /* a guess */ +#define OH_BAY_DEV_MASK 0x1c #define OH_BAY_RESET_N 0x80 #define OH_IOBUS_ENABLE 0x100 /* IOBUS seems to be IDE */ #define OH_SCC_ENABLE 0x200 diff -Nru a/include/asm-ppc/open_pic.h b/include/asm-ppc/open_pic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/open_pic.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,70 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * arch/ppc/kernel/open_pic.h -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * 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 archive + * for more details. + * + */ + +#ifndef _PPC_KERNEL_OPEN_PIC_H +#define _PPC_KERNEL_OPEN_PIC_H + +#include +#include + +#define OPENPIC_SIZE 0x40000 + +/* + * Non-offset'ed vector numbers + */ + +#define OPENPIC_VEC_TIMER 64 /* and up */ +#define OPENPIC_VEC_IPI 72 /* and up */ +#define OPENPIC_VEC_SPURIOUS 127 + +/* OpenPIC IRQ controller structure */ +extern struct hw_interrupt_type open_pic; + +/* OpenPIC IPI controller structure */ +#ifdef CONFIG_SMP +extern struct hw_interrupt_type open_pic_ipi; +#endif /* CONFIG_SMP */ + +extern u_int OpenPIC_NumInitSenses; +extern u_char *OpenPIC_InitSenses; +extern void* OpenPIC_Addr; + +/* Exported functions */ +extern void openpic_set_sources(int first_irq, int num_irqs, void *isr); +extern void openpic_init(int, int, unsigned char *, int); +extern u_int openpic_irq(void); +extern void openpic_eoi(void); +extern void openpic_request_IPIs(void); +extern void do_openpic_setup_cpu(void); +extern int openpic_get_irq(struct pt_regs *regs); +extern void openpic_reset_processor_phys(u_int cpumask); +extern void openpic_setup_ISU(int isu_num, unsigned long addr); +extern void openpic_cause_IPI(u_int ipi, u_int cpumask); +extern void smp_openpic_message_pass(int target, int msg, unsigned long data, + int wait); + +extern inline int openpic_to_irq(int irq) +{ + /* IRQ 0 usually means 'disabled'.. don't mess with it + * exceptions to this (sandpoint maybe?) + * shouldn't use openpic_to_irq + */ + if (irq != 0){ + return irq += NUM_8259_INTERRUPTS; + } else { + return 0; + } +} +/*extern int open_pic_irq_offset;*/ +#endif /* _PPC_KERNEL_OPEN_PIC_H */ diff -Nru a/include/asm-ppc/page.h b/include/asm-ppc/page.h --- a/include/asm-ppc/page.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/page.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.page.h 1.8 08/19/01 20:06:47 paulus + * BK Id: %F% %I% %G% %U% %#% */ #ifndef _PPC_PAGE_H #define _PPC_PAGE_H @@ -13,7 +13,11 @@ #include /* Be sure to change arch/ppc/Makefile to match */ +#ifdef CONFIG_KERNEL_START_BOOL +#define PAGE_OFFSET CONFIG_KERNEL_START +#else #define PAGE_OFFSET 0xc0000000 +#endif /* CONFIG_KERNEL_START_BOOL */ #define KERNELBASE PAGE_OFFSET #ifndef __ASSEMBLY__ @@ -83,9 +87,20 @@ extern void clear_page(void *page); extern void copy_page(void *to, void *from); -#define clear_user_page(page, vaddr) clear_page(page) -#define copy_user_page(to, from, vaddr) copy_page(to, from) +extern void clear_user_page(void *page, unsigned long vaddr); +extern void copy_user_page(void *to, void *from, unsigned long vaddr); +extern unsigned long ppc_memstart; +extern unsigned long ppc_memoffset; +#ifndef CONFIG_APUS +#define PPC_MEMSTART 0 +#define PPC_MEMOFFSET PAGE_OFFSET +#else +#define PPC_MEMSTART ppc_memstart +#define PPC_MEMOFFSET ppc_memoffset +#endif + +#if defined(CONFIG_APUS) && !defined(MODULE) /* map phys->virtual and virtual->phys for RAM pages */ static inline unsigned long ___pa(unsigned long v) { @@ -113,8 +128,13 @@ return (void*) v; } -#define __pa(x) ___pa ((unsigned long)(x)) -#define __va(x) ___va ((unsigned long)(x)) +#else +#define ___pa(vaddr) ((vaddr)-PPC_MEMOFFSET) +#define ___va(paddr) ((paddr)+PPC_MEMOFFSET) +#endif + +#define __pa(x) ___pa((unsigned long)(x)) +#define __va(x) ((void *)(___va((unsigned long)(x)))) #define MAP_PAGE_RESERVED (1<<15) #define virt_to_page(kaddr) (mem_map + (((unsigned long)kaddr-PAGE_OFFSET) >> PAGE_SHIFT)) @@ -137,5 +157,9 @@ } #endif /* __ASSEMBLY__ */ + +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + #endif /* __KERNEL__ */ #endif /* _PPC_PAGE_H */ diff -Nru a/include/asm-ppc/pc_serial.h b/include/asm-ppc/pc_serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/pc_serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,129 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + * + * include/asm-ppc/pc_serial.h + * + * This is basically a copy of include/asm-i386/serial.h. + * It is used on platforms which have an ISA bus and thus are likely + * to have PC-style serial ports at the legacy I/O port addresses. + * It also includes the definitions for the fourport, accent, boca + * and hub6 multiport serial cards, although I have never heard of + * anyone using any of those on a PPC platform. -- paulus + */ + +#include + +/* + * This assumes you have a 1.8432 MHz clock for your UART. + * + * It'd be nice if someone built a serial card with a 24.576 MHz + * clock, since the 16550A is capable of handling a top speed of 1.5 + * megabits/second; but this requires the faster clock. + */ +#define BASE_BAUD ( 1843200 / 16 ) + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +/* Standard COM flags (except for COM4, because of the 8514 problem) */ +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) +#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF +#endif + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define FOURPORT_FLAGS ASYNC_FOURPORT +#define ACCENT_FLAGS 0 +#define BOCA_FLAGS 0 +#define HUB6_FLAGS 0 +#endif + +/* + * The following define the access methods for the HUB6 card. All + * access is through two ports for all 24 possible chips. The card is + * selected through the high 2 bits, the port on that card with the + * "middle" 3 bits, and the register on that port with the bottom + * 3 bits. + * + * While the access port and interrupt is configurable, the default + * port locations are 0x302 for the port control register, and 0x303 + * for the data read/write register. Normally, the interrupt is at irq3 + * but can be anything from 3 to 7 inclusive. Note that using 3 will + * require disabling com2. + */ + +#define C_P(card,port) (((card)<<6|(port)<<3) + 1) + +#define STD_SERIAL_PORT_DEFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ \ + { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ \ + { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ + + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define EXTRA_SERIAL_PORT_DEFNS \ + { 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */ \ + { 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */ \ + { 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */ \ + { 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS }, /* ttyS7 */ \ + { 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS }, /* ttyS8 */ \ + { 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS }, /* ttyS9 */ \ + { 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */ \ + { 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */ \ + { 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */ \ + { 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */ \ + { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS14 (spare) */ \ + { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS15 (spare) */ \ + { 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */ \ + { 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */ \ + { 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS }, /* ttyS18 */ \ + { 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS }, /* ttyS19 */ \ + { 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS }, /* ttyS20 */ \ + { 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS }, /* ttyS21 */ \ + { 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS }, /* ttyS22 */ \ + { 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS }, /* ttyS23 */ \ + { 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS }, /* ttyS24 */ \ + { 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS }, /* ttyS25 */ \ + { 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS }, /* ttyS26 */ \ + { 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS }, /* ttyS27 */ \ + { 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS }, /* ttyS28 */ \ + { 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */ \ + { 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */ \ + { 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */ +#else +#define EXTRA_SERIAL_PORT_DEFNS +#endif + +/* You can have up to four HUB6's in the system, but I've only + * included two cards here for a total of twelve ports. + */ +#if (defined(CONFIG_HUB6) && defined(CONFIG_SERIAL_MANY_PORTS)) +#define HUB6_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS32 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS33 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS34 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS35 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS36 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS37 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS38 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS39 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS40 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS41 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */ +#else +#define HUB6_SERIAL_PORT_DFNS +#endif + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DEFNS \ + EXTRA_SERIAL_PORT_DEFNS \ + HUB6_SERIAL_PORT_DFNS diff -Nru a/include/asm-ppc/pci-bridge.h b/include/asm-ppc/pci-bridge.h --- a/include/asm-ppc/pci-bridge.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/pci-bridge.h Tue Feb 19 18:08:57 2002 @@ -1,10 +1,13 @@ /* - * BK Id: SCCS/s.pci-bridge.h 1.11 05/21/01 01:31:30 cort + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef _ASM_PCI_BRIDGE_H #define _ASM_PCI_BRIDGE_H +#include +#include + struct device_node; struct pci_controller; @@ -16,12 +19,20 @@ extern unsigned long pci_bus_io_base_phys(unsigned int bus); extern unsigned long pci_bus_mem_base_phys(unsigned int bus); +/* Allocate a new PCI host bridge structure */ +extern struct pci_controller* pcibios_alloc_controller(void); + +/* Helper function for setting up resources */ +extern void pci_init_resource(struct resource *res, unsigned long start, + unsigned long end, int flags, char *name); + /* * PCI <-> OF matching functions */ extern int pci_device_from_OF_node(struct device_node *node, u8* bus, u8* devfn); extern struct device_node* pci_device_to_OF_node(struct pci_dev *); +extern void pci_create_OF_bus_map(void); /* Get the PCI host controller for a bus */ extern struct pci_controller* pci_bus_to_hose(int bus); @@ -46,6 +57,7 @@ int first_busno; int last_busno; + int bus_offset; void *io_base_virt; unsigned long io_base_phys; @@ -65,16 +77,64 @@ struct resource io_resource; struct resource mem_resources[3]; int mem_resource_count; + + /* Host bridge I/O and Memory space + * Used for BAR placement algorithms + */ + struct resource io_space; + struct resource mem_space; }; /* These are used for config access before all the PCI probing has been done. */ -int early_read_config_byte(struct pci_controller *hose, int bus, int dev_fn, int where, u8 *val); -int early_read_config_word(struct pci_controller *hose, int bus, int dev_fn, int where, u16 *val); -int early_read_config_dword(struct pci_controller *hose, int bus, int dev_fn, int where, u32 *val); -int early_write_config_byte(struct pci_controller *hose, int bus, int dev_fn, int where, u8 val); -int early_write_config_word(struct pci_controller *hose, int bus, int dev_fn, int where, u16 val); -int early_write_config_dword(struct pci_controller *hose, int bus, int dev_fn, int where, u32 val); +int early_read_config_byte(struct pci_controller *hose, int bus, int dev_fn, + int where, u8 *val); +int early_read_config_word(struct pci_controller *hose, int bus, int dev_fn, + int where, u16 *val); +int early_read_config_dword(struct pci_controller *hose, int bus, int dev_fn, + int where, u32 *val); +int early_write_config_byte(struct pci_controller *hose, int bus, int dev_fn, + int where, u8 val); +int early_write_config_word(struct pci_controller *hose, int bus, int dev_fn, + int where, u16 val); +int early_write_config_dword(struct pci_controller *hose, int bus, int dev_fn, + int where, u32 val); + +extern void setup_indirect_pci(struct pci_controller* hose, + u32 cfg_addr, u32 cfg_data); +extern void setup_grackle(struct pci_controller *hose); + +extern unsigned char common_swizzle(struct pci_dev *, unsigned char *); + +/* + * The following code swizzles for exactly one bridge. The routine + * common_swizzle below handles multiple bridges. But there are a + * some boards that don't follow the PCI spec's suggestion so we + * break this piece out separately. + */ +static inline unsigned char bridge_swizzle(unsigned char pin, + unsigned char idsel) +{ + return (((pin-1) + idsel) % 4) + 1; +} + +/* + * The following macro is used to lookup irqs in a standard table + * format for those PPC systems that do not already have PCI + * interrupts properly routed. + */ +/* FIXME - double check this */ +#define PCI_IRQ_TABLE_LOOKUP \ +({ long _ctl_ = -1; \ + if (idsel >= min_idsel && idsel <= max_idsel && pin <= irqs_per_slot) \ + _ctl_ = pci_irq_table[idsel - min_idsel][pin-1]; \ + _ctl_; }) + +/* + * Scan the buses below a given PCI host bridge and assign suitable + * resources to all devices found. + */ +extern int pciauto_bus_scan(struct pci_controller *, int); #endif #endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/pci.h b/include/asm-ppc/pci.h --- a/include/asm-ppc/pci.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/pci.h Tue Feb 19 18:08:58 2002 @@ -1,10 +1,19 @@ /* - * BK Id: SCCS/s.pci.h 1.16 10/15/01 22:51:33 paulus + * BK Id: %F% %I% %G% %U% %#% */ #ifndef __PPC_PCI_H #define __PPC_PCI_H #ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include + +struct pci_dev; + /* Values for the `which' argument to sys_pciconfig_iobase syscall. */ #define IOBASE_BRIDGE_NUMBER 0 #define IOBASE_MEMORY 1 @@ -12,8 +21,13 @@ #define IOBASE_ISA_IO 3 #define IOBASE_ISA_MEM 4 +/* + * Set this to 1 if you want the kernel to re-assign all PCI + * bus numbers + */ +extern int pci_assign_all_busses; -extern int pcibios_assign_all_busses(void); +#define pcibios_assign_all_busses() (pci_assign_all_busses) #define PCIBIOS_MIN_IO 0x1000 #define PCIBIOS_MIN_MEM 0x10000000 @@ -159,7 +173,6 @@ for (i = 0; i < nents; i++) { if (!sg[i].page) BUG(); - sg[i].dma_address = page_to_bus(sg[i].page) + sg[i].offset; } @@ -250,14 +263,6 @@ { /* Nothing to do. */ } - -/* These macros should be used after a pci_map_sg call has been done - * to get bus addresses of each of the SG entries and their lengths. - * You should only work with the number of sg entries pci_map_sg - * returns. - */ -#define sg_dma_address(sg) ((sg)->dma_address) -#define sg_dma_len(sg) ((sg)->length) /* Return the index of the PCI controller for device PDEV. */ extern int pci_controller_num(struct pci_dev *pdev); diff -Nru a/include/asm-ppc/pgtable.h b/include/asm-ppc/pgtable.h --- a/include/asm-ppc/pgtable.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/pgtable.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.pgtable.h 1.15 09/22/01 11:26:52 trini + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef _PPC_PGTABLE_H @@ -14,16 +14,26 @@ #include #include +extern void _tlbie(unsigned long address); +extern void _tlbia(void); + #if defined(CONFIG_4xx) -extern void local_flush_tlb_all(void); -extern void local_flush_tlb_mm(struct mm_struct *mm); -extern void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr); -extern void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end); +#define __tlbia() asm volatile ("tlbia; sync" : : : "memory") + +static inline void local_flush_tlb_all(void) + { __tlbia(); } +static inline void local_flush_tlb_mm(struct mm_struct *mm) + { __tlbia(); } +static inline void local_flush_tlb_page(struct vm_area_struct *vma, + unsigned long vmaddr) + { _tlbie(vmaddr); } +static inline void local_flush_tlb_range(struct mm_struct *mm, + unsigned long start, unsigned long end) + { __tlbia(); } #define update_mmu_cache(vma, addr, pte) do { } while (0) #elif defined(CONFIG_8xx) -#define __tlbia() asm volatile ("tlbia" : : ) +#define __tlbia() asm volatile ("tlbia; sync" : : : "memory") static inline void local_flush_tlb_all(void) { __tlbia(); } @@ -31,8 +41,8 @@ { __tlbia(); } static inline void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) - { __tlbia(); } -static inline void local_flush_tlb_range(struct vm_area_struct *vma, + { _tlbie(vmaddr); } +static inline void local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { __tlbia(); } #define update_mmu_cache(vma, addr, pte) do { } while (0) @@ -82,13 +92,14 @@ #define flush_cache_mm(mm) do { } while (0) #define flush_cache_range(vma, a, b) do { } while (0) #define flush_cache_page(vma, p) do { } while (0) -#define flush_icache_page(vma, page) do { } while (0) +#define flush_page_to_ram(page) do { } while (0) +extern void flush_icache_user_range(struct vm_area_struct *vma, + struct page *page, unsigned long addr, int len); extern void flush_icache_range(unsigned long, unsigned long); -extern void __flush_page_to_ram(unsigned long page_va); -extern void flush_page_to_ram(struct page *page); - -#define flush_dcache_page(page) do { } while (0) +extern void __flush_dcache_icache(void *page_va); +extern void flush_dcache_page(struct page *page); +extern void flush_icache_page(struct vm_area_struct *vma, struct page *page); extern unsigned long va_to_phys(unsigned long address); extern pte_t *va_to_pte(unsigned long address); @@ -126,6 +137,12 @@ * that is where it exists in the MD_TWC, and bit 26 for writethrough. * These will get masked from the level 2 descriptor at TLB load time, and * copied to the MD_TWC before it gets loaded. + * Large page sizes added. We currently support two sizes, 4K and 8M. + * This also allows a TLB hander optimization because we can directly + * load the PMD into MD_TWC. The 8M pages are only used for kernel + * mapping of well known areas. The PMD (PGD) entries contain control + * flags in addition to the address, so care must be taken that the + * software no longer assumes these are only pointers. */ /* @@ -196,17 +213,44 @@ */ #if defined(CONFIG_4xx) + +/* There are several potential gotchas here. The 4xx hardware TLBLO + field looks like this: + + 0 1 2 3 4 ... 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + RPN..................... 0 0 EX WR ZSEL....... W I M G + + Where possible we make the Linux PTE bits match up with this + + - bits 20 and 21 must be cleared, because we use 4k pages (4xx can + support down to 1k pages), this is done in the TLBMiss exception + handler. + - We use only zones 0 (for kernel pages) and 1 (for user pages) + of the 16 available. Bit 24-26 of the TLB are cleared in the TLB + miss handler. Bit 27 is PAGE_USER, thus selecting the correct + zone. + - PRESENT *must* be in the bottom two bits because swap cache + entries use the top 30 bits. Because 4xx doesn't support SMP + anyway, M is irrelevant so we borrow it for PAGE_PRESENT. Bit 30 + is cleared in the TLB miss handler before the TLB entry is loaded. + - All other bits of the PTE are loaded into TLBLO without + modification, leaving us only the bits 20, 21, 24, 25, 26, 30 for + software PTE bits. We actually use use bits 20, 24, 25, 26, and + 30 respectively for the software bits: ACCESSED, DIRTY, RW, EXEC, + PRESENT. +*/ + /* Definitions for 4xx embedded chips. */ #define _PAGE_GUARDED 0x001 /* G: page is guarded from prefetch */ -#define _PAGE_COHERENT 0x002 /* M: enforece memory coherence */ +#define _PAGE_PRESENT 0x002 /* software: PTE contains a translation */ #define _PAGE_NO_CACHE 0x004 /* I: caching is inhibited */ #define _PAGE_WRITETHRU 0x008 /* W: caching is write-through */ #define _PAGE_USER 0x010 /* matches one of the zone permission bits */ -#define _PAGE_EXEC 0x020 /* software: i-cache coherency required */ -#define _PAGE_PRESENT 0x040 /* software: PTE contains a translation */ -#define _PAGE_DIRTY 0x100 /* C: page changed */ -#define _PAGE_RW 0x200 /* Writes permitted */ -#define _PAGE_ACCESSED 0x400 /* R: page referenced */ +#define _PAGE_RW 0x040 /* software: Writes permitted */ +#define _PAGE_DIRTY 0x080 /* software: dirty page */ +#define _PAGE_HWWRITE 0x100 /* hardware: Dirty & RW, set in exception */ +#define _PAGE_HWEXEC 0x200 /* hardware: EX permission */ +#define _PAGE_ACCESSED 0x400 /* software: R: page referenced */ #elif defined(CONFIG_8xx) /* Definitions for 8xx embedded chips. */ @@ -219,14 +263,21 @@ */ #define _PAGE_EXEC 0x0008 /* software: i-cache coherency required */ #define _PAGE_GUARDED 0x0010 /* software: guarded access */ -#define _PAGE_WRITETHRU 0x0020 /* software: use writethrough cache */ +#define _PAGE_DIRTY 0x0020 /* software: page changed */ #define _PAGE_RW 0x0040 /* software: user write access allowed */ #define _PAGE_ACCESSED 0x0080 /* software: page referenced */ +/* Setting any bits in the nibble with the follow two controls will + * require a TLB exception handler change. It is assumed unused bits + * are always zero. + */ #define _PAGE_HWWRITE 0x0100 /* h/w write enable: never set in Linux PTE */ -#define _PAGE_DIRTY 0x0200 /* software: page changed */ #define _PAGE_USER 0x0800 /* One of the PP bits, the other is USER&~RW */ +#define _PMD_PRESENT 0x0001 +#define _PMD_PAGE_MASK 0x000c +#define _PMD_PAGE_8M 0x000c + #else /* CONFIG_6xx */ /* Definitions for 60x, 740/750, etc. */ #define _PAGE_PRESENT 0x001 /* software: pte contains a translation */ @@ -262,14 +313,11 @@ #ifndef _PAGE_HWWRITE #define _PAGE_HWWRITE 0 #endif - -/* We can't use _PAGE_HWWRITE on any SMP due to the lack of ability - * to atomically manage _PAGE_HWWRITE and it's coordination flags, - * _PAGE_DIRTY or _PAGE_RW. The SMP systems must manage HWWRITE - * or its logical equivalent in the MMU management software. - */ -#if CONFIG_SMP && _PAGE_HWWRITE -#error "You can't configure SMP and HWWRITE" +#ifndef _PAGE_HWEXEC +#define _PAGE_HWEXEC 0 +#endif +#ifndef _PAGE_EXEC +#define _PAGE_EXEC 0 #endif #define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY) @@ -281,9 +329,9 @@ * another purpose. -- paulus. */ #define _PAGE_BASE _PAGE_PRESENT | _PAGE_ACCESSED -#define _PAGE_WRENABLE _PAGE_RW | _PAGE_DIRTY +#define _PAGE_WRENABLE _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE -#define _PAGE_KERNEL _PAGE_BASE | _PAGE_WRENABLE | _PAGE_SHARED +#define _PAGE_KERNEL _PAGE_BASE | _PAGE_WRENABLE | _PAGE_SHARED | _PAGE_HWEXEC #define _PAGE_IO _PAGE_KERNEL | _PAGE_NO_CACHE | _PAGE_GUARDED #define PAGE_NONE __pgprot(_PAGE_BASE) @@ -341,11 +389,7 @@ #define pmd_present(pmd) ((pmd_val(pmd) & PAGE_MASK) != 0) #define pmd_clear(pmdp) do { pmd_val(*(pmdp)) = 0; } while (0) -/* - * Permanent address of a page. - */ -#define page_address(page) ((page)->virtual) -#define pte_page(x) (mem_map+(unsigned long)((pte_val(x) >> PAGE_SHIFT))) +#define pte_page(x) (mem_map+(unsigned long)((pte_val(x)-PPC_MEMSTART) >> PAGE_SHIFT)) #ifndef __ASSEMBLY__ /* @@ -411,7 +455,7 @@ #define mk_pte(page,pgprot) \ ({ \ pte_t pte; \ - pte_val(pte) = ((page - mem_map) << PAGE_SHIFT) | pgprot_val(pgprot); \ + pte_val(pte) = (((page - mem_map) << PAGE_SHIFT) + PPC_MEMSTART) | pgprot_val(pgprot); \ pte; \ }) @@ -435,8 +479,9 @@ __asm__ __volatile__("\ 1: lwarx %0,0,%3\n\ andc %1,%0,%4\n\ - or %1,%1,%5\n\ - stwcx. %1,0,%3\n\ + or %1,%1,%5\n" + PPC405_ERR77(0,%3) +" stwcx. %1,0,%3\n\ bne- 1b" : "=&r" (old), "=&r" (tmp), "=m" (*p) : "r" (p), "r" (clr), "r" (set), "m" (*p) @@ -445,10 +490,18 @@ } /* - * Writing a new value into the PTE doesn't disturb the state of the - * _PAGE_HASHPTE bit, on those machines which use an MMU hash table. + * set_pte stores a linux PTE into the linux page table. + * On machines which use an MMU hash table we avoid changing the + * _PAGE_HASHPTE bit. */ -extern void set_pte(pte_t *ptep, pte_t pte); +static inline void set_pte(pte_t *ptep, pte_t pte) +{ +#if _PAGE_HASHPTE != 0 + pte_update(ptep, ~_PAGE_HASHPTE, pte_val(pte) & ~_PAGE_HASHPTE); +#else + *ptep = pte; +#endif +} static inline int ptep_test_and_clear_young(pte_t *ptep) { @@ -477,7 +530,7 @@ #define pte_same(A,B) (((pte_val(A) ^ pte_val(B)) & ~_PAGE_HASHPTE) == 0) -#define pmd_page(pmd) (pmd_val(pmd)) +#define pmd_page(pmd) (pmd_val(pmd) & PAGE_MASK) /* to find an entry in a kernel page-table-directory */ #define pgd_offset_k(address) pgd_offset(&init_mm, address) diff -Nru a/include/asm-ppc/pmac_feature.h b/include/asm-ppc/pmac_feature.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/pmac_feature.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,253 @@ +/* + * Definition of platform feature hooks for PowerMacs + * + * 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 archive + * for more details. + * + * Copyright (C) 1998 Paul Mackerras & + * Ben. Herrenschmidt. + * + * + * Note: I removed media-bay details from the feature stuff, I beleive it's + * not worth it, the media-bay driver can directly use the mac-io + * ASIC registers. + * + * Implementation note: Currently, none of these functions will block. + * However, they may internally protect themselves with a spinlock + * for way too long. Be prepared for at least some of these to block + * in the future. + * + * Unless specifically defined, the result code is assumed to be an + * error when negative, 0 is the default success result. Some functions + * may return additional positive result values. + * + * To keep implementation simple, all feature calls are assumed to have + * the prototype parameters (struct device_node* node, int value). + * When either is not used, pass 0. + */ + +#ifdef __KERNEL__ +#ifndef __PPC_ASM_PMAC_FEATURE_H +#define __PPC_ASM_PMAC_FEATURE_H + +/* + * Known Mac motherboard models + * + * Please, report any error here to benh@kernel.crashing.org, thanks ! + */ + +/* PowerSurge are the first generation of PCI Pmacs. This include + * all of the Grand-Central based machines + */ +#define PMAC_TYPE_PSURGE 0x10 /* PowerSurge */ + +/* Here is the infamous serie of OHare based machines + */ +#define PMAC_TYPE_COMET 0x20 /* Beleived to be PowerBook 2400 */ +#define PMAC_TYPE_HOOPER 0x21 /* Beleived to be PowerBook 3400 */ +#define PMAC_TYPE_KANGA 0x22 /* PowerBook 3500 (first G3) */ +#define PMAC_TYPE_ALCHEMY 0x23 /* Alchemy motherboard base */ +#define PMAC_TYPE_GAZELLE 0x24 /* Spartacus, some 5xxx/6xxx */ +#define PMAC_TYPE_UNKNOWN_OHARE 0x2f /* Unknown, but OHare based */ + +/* Here are the Heathrow based machines + * FIXME: Differenciate wallstreet,mainstreet,wallstreetII + */ +#define PMAC_TYPE_GOSSAMER 0x30 /* Gossamer motherboard */ +#define PMAC_TYPE_SILK 0x31 /* Desktop PowerMac G3 */ +#define PMAC_TYPE_WALLSTREET 0x32 /* Wallstreet/Mainstreet PowerBook*/ +#define PMAC_TYPE_UNKNOWN_HEATHROW 0x3f /* Unknown but heathrow based */ + +/* Here are newworld machines based on Paddington (heathrow derivative) + */ +#define PMAC_TYPE_101_PBOOK 0x40 /* 101 PowerBook (aka Lombard) */ +#define PMAC_TYPE_ORIG_IMAC 0x41 /* First generation iMac */ +#define PMAC_TYPE_YOSEMITE 0x42 /* B&W G3 */ +#define PMAC_TYPE_YIKES 0x43 /* Yikes G4 (PCI graphics) */ +#define PMAC_TYPE_UNKNOWN_PADDINGTON 0x4f /* Unknown but paddington based */ + +/* Core99 machines based on UniNorth 1.0 and 1.5 + * + * Note: A single entry here may cover several actual models according + * to the device-tree. (Sawtooth is most tower G4s, FW_IMAC is most + * FireWire based iMacs, etc...). Those machines are too similar to be + * distinguished here, when they need to be differencied, use the + * device-tree "model" or "compatible" property. + */ +#define PMAC_TYPE_ORIG_IBOOK 0x40 /* First iBook model (no firewire) */ +#define PMAC_TYPE_SAWTOOTH 0x41 /* Desktop G4s */ +#define PMAC_TYPE_FW_IMAC 0x42 /* FireWire iMacs (except Pangea based) */ +#define PMAC_TYPE_FW_IBOOK 0x43 /* FireWire iBooks (except iBook2) */ +#define PMAC_TYPE_CUBE 0x44 /* Cube PowerMac */ +#define PMAC_TYPE_QUICKSILVER 0x45 /* QuickSilver G4s */ +#define PMAC_TYPE_PISMO 0x46 /* Pismo PowerBook */ +#define PMAC_TYPE_TITANIUM 0x47 /* Titanium PowerBook */ +#define PMAC_TYPE_TITANIUM2 0x48 /* Titanium II PowerBook */ +#define PMAC_TYPE_UNKNOWN_CORE99 0x5f + +/* MacRISC2 machines based on the Pangea chipset + */ +#define PMAC_TYPE_PANGEA_IMAC 0x100 /* Flower Power iMac */ +#define PMAC_TYPE_IBOOK2 0x101 /* iBook2 (polycarbonate) */ +#define PMAC_TYPE_FLAT_PANEL_IMAC 0x102 /* Flat panel iMac */ +#define PMAC_TYPE_UNKNOWN_PANGEA 0x10f + +/* + * Motherboard flags + */ + +#define PMAC_MB_CAN_SLEEP 0x00000001 +#define PMAC_MB_HAS_FW_POWER 0x00000002 + +/* + * Feature calls supported on pmac + * + */ + +/* + * Use this inline wrapper + */ +struct device_node; + +static inline int pmac_call_feature(int selector, struct device_node* node, + int param, int value) +{ + if (!ppc_md.feature_call) + return -ENODEV; + return ppc_md.feature_call(selector, node, param, value); +} + +/* PMAC_FTR_SERIAL_ENABLE (struct device_node* node, int param, int value) + * enable/disable an SCC side. Pass the node corresponding to the + * channel side as a parameter. + * param is the type of port + * if param is ored with PMAC_SCC_FLAG_XMON, then the SCC is locked enabled + * for use by xmon. + */ +#define PMAC_FTR_SCC_ENABLE PMAC_FTR_DEF(0) + #define PMAC_SCC_ASYNC 0 + #define PMAC_SCC_IRDA 1 + #define PMAC_SCC_I2S1 2 + #define PMAC_SCC_FLAG_XMON 0x00001000 + +/* PMAC_FTR_MODEM_ENABLE (struct device_node* node, 0, int value) + * enable/disable the internal modem. + */ +#define PMAC_FTR_MODEM_ENABLE PMAC_FTR_DEF(1) + +/* PMAC_FTR_SWIM3_ENABLE (struct device_node* node, 0,int value) + * enable/disable the swim3 (floppy) cell of a mac-io ASIC + */ +#define PMAC_FTR_SWIM3_ENABLE PMAC_FTR_DEF(2) + +/* PMAC_FTR_MESH_ENABLE (struct device_node* node, 0, int value) + * enable/disable the mesh (scsi) cell of a mac-io ASIC + */ +#define PMAC_FTR_MESH_ENABLE PMAC_FTR_DEF(3) + +/* PMAC_FTR_IDE_ENABLE (struct device_node* node, int busID, int value) + * enable/disable an IDE port of a mac-io ASIC + * pass the busID parameter + */ +#define PMAC_FTR_IDE_ENABLE PMAC_FTR_DEF(4) + +/* PMAC_FTR_IDE_RESET (struct device_node* node, int busID, int value) + * assert(1)/release(0) an IDE reset line (mac-io IDE only) + */ +#define PMAC_FTR_IDE_RESET PMAC_FTR_DEF(5) + +/* PMAC_FTR_BMAC_ENABLE (struct device_node* node, 0, int value) + * enable/disable the bmac (ethernet) cell of a mac-io ASIC, also drive + * it's reset line + */ +#define PMAC_FTR_BMAC_ENABLE PMAC_FTR_DEF(6) + +/* PMAC_FTR_GMAC_ENABLE (struct device_node* node, 0, int value) + * enable/disable the gmac (ethernet) cell of an uninorth ASIC. This + * control the cell's clock. + */ +#define PMAC_FTR_GMAC_ENABLE PMAC_FTR_DEF(7) + +/* PMAC_FTR_GMAC_PHY_RESET (struct device_node* node, 0, 0) + * Perform a HW reset of the PHY connected to a gmac controller. + * Pass the gmac device node, not the PHY node. + */ +#define PMAC_FTR_GMAC_PHY_RESET PMAC_FTR_DEF(8) + +/* PMAC_FTR_SOUND_CHIP_ENABLE (struct device_node* node, 0, int value) + * enable/disable the sound chip, whatever it is and provided it can + * acually be controlled + */ +#define PMAC_FTR_SOUND_CHIP_ENABLE PMAC_FTR_DEF(9) + +/* -- add various tweaks related to sound routing -- */ + +/* PMAC_FTR_AIRPORT_ENABLE (struct device_node* node, 0, int value) + * enable/disable the airport card + */ +#define PMAC_FTR_AIRPORT_ENABLE PMAC_FTR_DEF(10) + +/* PMAC_FTR_RESET_CPU (NULL, int cpu_nr, 0) + * toggle the reset line of a CPU on an uninorth-based SMP machine + */ +#define PMAC_FTR_RESET_CPU PMAC_FTR_DEF(11) + +/* PMAC_FTR_USB_ENABLE (struct device_node* node, 0, int value) + * enable/disable an USB cell, along with the power of the USB "pad" + * on keylargo based machines + */ +#define PMAC_FTR_USB_ENABLE PMAC_FTR_DEF(12) + +/* PMAC_FTR_1394_ENABLE (struct device_node* node, 0, int value) + * enable/disable the firewire cell of an uninorth ASIC. + */ +#define PMAC_FTR_1394_ENABLE PMAC_FTR_DEF(13) + +/* PMAC_FTR_1394_CABLE_POWER (struct device_node* node, 0, int value) + * enable/disable the firewire cable power supply of the uninorth + * firewire cell + */ +#define PMAC_FTR_1394_CABLE_POWER PMAC_FTR_DEF(14) + +/* PMAC_FTR_SLEEP_STATE (struct device_node* node, 0, int value) + * set the sleep state of the motherboard. + * Pass -1 as value to query for sleep capability + */ +#define PMAC_FTR_SLEEP_STATE PMAC_FTR_DEF(15) + +/* PMAC_FTR_GET_MB_INFO (NULL, selector, 0) + * + * returns some motherboard infos. + * selector: 0 - model id + * 1 - model flags (capabilities) + * 2 - model name (cast to const char *) + */ +#define PMAC_FTR_GET_MB_INFO PMAC_FTR_DEF(16) +#define PMAC_MB_INFO_MODEL 0 +#define PMAC_MB_INFO_FLAGS 1 +#define PMAC_MB_INFO_NAME 2 + +/* PMAC_FTR_READ_GPIO (NULL, int index, 0) + * + * read a GPIO from a mac-io controller of type KeyLargo or Pangea. + * the value returned is a byte (positive), or a negative error code + */ +#define PMAC_FTR_READ_GPIO PMAC_FTR_DEF(17) + +/* PMAC_FTR_WRITE_GPIO (NULL, int index, int value) + * + * write a GPIO of a mac-io controller of type KeyLargo or Pangea. + */ +#define PMAC_FTR_WRITE_GPIO PMAC_FTR_DEF(18) + + +/* Don't use those directly, they are for the sake of pmac_setup.c */ +extern int pmac_do_feature_call(unsigned int selector, ...); +extern void pmac_feature_init(void); +extern void pmac_feature_late_init(void); + +#define PMAC_FTR_DEF(x) ((_MACH_Pmac << 16) | (x)) + +#endif /* __PPC_ASM_PMAC_FEATURE_H */ +#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/posix_types.h b/include/asm-ppc/posix_types.h --- a/include/asm-ppc/posix_types.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/posix_types.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.posix_types.h 1.5 05/17/01 18:14:25 cort + * BK Id: %F% %I% %G% %U% %#% */ #ifndef _PPC_POSIX_TYPES_H #define _PPC_POSIX_TYPES_H @@ -11,7 +11,7 @@ */ typedef unsigned int __kernel_dev_t; -typedef unsigned int __kernel_ino_t; +typedef unsigned long __kernel_ino_t; typedef unsigned int __kernel_mode_t; typedef unsigned short __kernel_nlink_t; typedef long __kernel_off_t; diff -Nru a/include/asm-ppc/ppc405_dma.h b/include/asm-ppc/ppc405_dma.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/ppc405_dma.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1275 @@ +/* + * + * Copyright 2000 MontaVista Software Inc. + * PPC405 modifications + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * + * Module name: ppc405_dma.h + * + * Description: + * Data structures specific to the IBM PowerPC 405 on-chip DMA controller + * and API. + * + */ + +#ifdef __KERNEL__ +#ifndef __ASMPPC_405_DMA_H +#define __ASMPPC_405_DMA_H + +#include + +/* #define DEBUG_405DMA */ + +#define TRUE 1 +#define FALSE 0 + +#define SGL_LIST_SIZE 4096 +/* #define PCI_ALLOC_IS_NONCONSISTENT */ + +#define MAX_405GP_DMA_CHANNELS 4 + +/* The maximum address that we can perform a DMA transfer to on this platform */ +/* Doesn't really apply... */ +#define MAX_DMA_ADDRESS 0xFFFFFFFF + +extern unsigned long ISA_DMA_THRESHOLD; + +#define dma_outb outb +#define dma_inb inb + + +/* + * Function return status codes + * These values are used to indicate whether or not the function + * call was successful, or a bad/invalid parameter was passed. + */ +#define DMA_STATUS_GOOD 0 +#define DMA_STATUS_BAD_CHANNEL 1 +#define DMA_STATUS_BAD_HANDLE 2 +#define DMA_STATUS_BAD_MODE 3 +#define DMA_STATUS_NULL_POINTER 4 +#define DMA_STATUS_OUT_OF_MEMORY 5 +#define DMA_STATUS_SGL_LIST_EMPTY 6 +#define DMA_STATUS_GENERAL_ERROR 7 + + +/* + * These indicate status as returned from the DMA Status Register. + */ +#define DMA_STATUS_NO_ERROR 0 +#define DMA_STATUS_CS 1 /* Count Status */ +#define DMA_STATUS_TS 2 /* Transfer Status */ +#define DMA_STATUS_DMA_ERROR 3 /* DMA Error Occurred */ +#define DMA_STATUS_DMA_BUSY 4 /* The channel is busy */ + + +/* + * Transfer Modes + * These modes are defined in a way that makes it possible to + * simply "or" in the value in the control register. + */ +#define DMA_MODE_READ DMA_TD /* Peripheral to Memory */ +#define DMA_MODE_WRITE 0 /* Memory to Peripheral */ +#define DMA_MODE_MM (SET_DMA_TM(TM_S_MM)) /* memory to memory */ + + /* Device-paced memory to memory, */ + /* device is at source address */ +#define DMA_MODE_MM_DEVATSRC (DMA_TD | SET_DMA_TM(TM_D_MM)) + + /* Device-paced memory to memory, */ + /* device is at destination address */ +#define DMA_MODE_MM_DEVATDST (SET_DMA_TM(TM_D_MM)) + + +/* + * DMA Polarity Configuration Register + */ +#define DMAReq0_ActiveLow (1<<31) +#define DMAAck0_ActiveLow (1<<30) +#define EOT0_ActiveLow (1<<29) /* End of Transfer */ + +#define DMAReq1_ActiveLow (1<<28) +#define DMAAck1_ActiveLow (1<<27) +#define EOT1_ActiveLow (1<<26) + +#define DMAReq2_ActiveLow (1<<25) +#define DMAAck2_ActiveLow (1<<24) +#define EOT2_ActiveLow (1<<23) + +#define DMAReq3_ActiveLow (1<<22) +#define DMAAck3_ActiveLow (1<<21) +#define EOT3_ActiveLow (1<<20) + +/* + * DMA Sleep Mode Register + */ +#define SLEEP_MODE_ENABLE (1<<21) + + +/* + * DMA Status Register + */ +#define DMA_CS0 (1<<31) /* Terminal Count has been reached */ +#define DMA_CS1 (1<<30) +#define DMA_CS2 (1<<29) +#define DMA_CS3 (1<<28) + +#define DMA_TS0 (1<<27) /* End of Transfer has been requested */ +#define DMA_TS1 (1<<26) +#define DMA_TS2 (1<<25) +#define DMA_TS3 (1<<24) + +#define DMA_CH0_ERR (1<<23) /* DMA Chanel 0 Error */ +#define DMA_CH1_ERR (1<<22) +#define DMA_CH2_ERR (1<<21) +#define DMA_CH3_ERR (1<<20) + +#define DMA_IN_DMA_REQ0 (1<<19) /* Internal DMA Request is pending */ +#define DMA_IN_DMA_REQ1 (1<<18) +#define DMA_IN_DMA_REQ2 (1<<17) +#define DMA_IN_DMA_REQ3 (1<<16) + +#define DMA_EXT_DMA_REQ0 (1<<15) /* External DMA Request is pending */ +#define DMA_EXT_DMA_REQ1 (1<<14) +#define DMA_EXT_DMA_REQ2 (1<<13) +#define DMA_EXT_DMA_REQ3 (1<<12) + +#define DMA_CH0_BUSY (1<<11) /* DMA Channel 0 Busy */ +#define DMA_CH1_BUSY (1<<10) +#define DMA_CH2_BUSY (1<<9) +#define DMA_CH3_BUSY (1<<8) + +#define DMA_SG0 (1<<7) /* DMA Channel 0 Scatter/Gather in progress */ +#define DMA_SG1 (1<<6) +#define DMA_SG2 (1<<5) +#define DMA_SG3 (1<<4) + + + +/* + * DMA Channel Control Registers + */ +#define DMA_CH_ENABLE (1<<31) /* DMA Channel Enable */ +#define SET_DMA_CH_ENABLE(x) (((x)&0x1)<<31) +#define GET_DMA_CH_ENABLE(x) (((x)&DMA_CH_ENABLE)>>31) + +#define DMA_CIE_ENABLE (1<<30) /* DMA Channel Interrupt Enable */ +#define SET_DMA_CIE_ENABLE(x) (((x)&0x1)<<30) +#define GET_DMA_CIE_ENABLE(x) (((x)&DMA_CIE_ENABLE)>>30) + +#define DMA_TD (1<<29) +#define SET_DMA_TD(x) (((x)&0x1)<<29) +#define GET_DMA_TD(x) (((x)&DMA_TD)>>29) + +#define DMA_PL (1<<28) /* Peripheral Location */ +#define SET_DMA_PL(x) (((x)&0x1)<<28) +#define GET_DMA_PL(x) (((x)&DMA_PL)>>28) + +#define EXTERNAL_PERIPHERAL 0 +#define INTERNAL_PERIPHERAL 1 + + +#define SET_DMA_PW(x) (((x)&0x3)<<26) /* Peripheral Width */ +#define DMA_PW_MASK SET_DMA_PW(3) +#define PW_8 0 +#define PW_16 1 +#define PW_32 2 +#define PW_64 3 +#define GET_DMA_PW(x) (((x)&DMA_PW_MASK)>>26) + +#define DMA_DAI (1<<25) /* Destination Address Increment */ +#define SET_DMA_DAI(x) (((x)&0x1)<<25) + +#define DMA_SAI (1<<24) /* Source Address Increment */ +#define SET_DMA_SAI(x) (((x)&0x1)<<24) + +#define DMA_BEN (1<<23) /* Buffer Enable */ +#define SET_DMA_BEN(x) (((x)&0x1)<<23) + +#define SET_DMA_TM(x) (((x)&0x3)<<21) /* Transfer Mode */ +#define DMA_TM_MASK SET_DMA_TM(3) +#define TM_PERIPHERAL 0 /* Peripheral */ +#define TM_RESERVED 1 /* Reserved */ +#define TM_S_MM 2 /* Memory to Memory */ +#define TM_D_MM 3 /* Device Paced Memory to Memory */ +#define GET_DMA_TM(x) (((x)&DMA_TM_MASK)>>21) + +#define SET_DMA_PSC(x) (((x)&0x3)<<19) /* Peripheral Setup Cycles */ +#define DMA_PSC_MASK SET_DMA_PSC(3) +#define GET_DMA_PSC(x) (((x)&DMA_PSC_MASK)>>19) + +#define SET_DMA_PWC(x) (((x)&0x3F)<<13) /* Peripheral Wait Cycles */ +#define DMA_PWC_MASK SET_DMA_PWC(0x3F) +#define GET_DMA_PWC(x) (((x)&DMA_PWC_MASK)>>13) + +#define SET_DMA_PHC(x) (((x)&0x7)<<10) /* Peripheral Hold Cycles */ +#define DMA_PHC_MASK SET_DMA_PHC(0x7) +#define GET_DMA_PHC(x) (((x)&DMA_PHC_MASK)>>10) + +#define DMA_ETD_OUTPUT (1<<9) /* EOT pin is a TC output */ +#define SET_DMA_ETD(x) (((x)&0x1)<<9) + +#define DMA_TCE_ENABLE (1<<8) +#define SET_DMA_TCE(x) (((x)&0x1)<<8) + +#define SET_DMA_PRIORITY(x) (((x)&0x3)<<6) /* DMA Channel Priority */ +#define DMA_PRIORITY_MASK SET_DMA_PRIORITY(3) +#define PRIORITY_LOW 0 +#define PRIORITY_MID_LOW 1 +#define PRIORITY_MID_HIGH 2 +#define PRIORITY_HIGH 3 +#define GET_DMA_PRIORITY(x) (((x)&DMA_PRIORITY_MASK)>>6) + +#define SET_DMA_PREFETCH(x) (((x)&0x3)<<4) /* Memory Read Prefetch */ +#define DMA_PREFETCH_MASK SET_DMA_PREFETCH(3) +#define PREFETCH_1 0 /* Prefetch 1 Double Word */ +#define PREFETCH_2 1 +#define PREFETCH_4 2 +#define GET_DMA_PREFETCH(x) (((x)&DMA_PREFETCH_MASK)>>4) + +#define DMA_PCE (1<<3) /* Parity Check Enable */ +#define SET_DMA_PCE(x) (((x)&0x1)<<3) +#define GET_DMA_PCE(x) (((x)&DMA_PCE)>>3) + +#define DMA_DEC (1<<2) /* Address Decrement */ +#define SET_DMA_DEC(x) (((x)&0x1)<<2) +#define GET_DMA_DEC(x) (((x)&DMA_DEC)>>2) + +/* + * DMA SG Command Register + */ +#define SSG0_ENABLE (1<<31) /* Start Scatter Gather */ +#define SSG1_ENABLE (1<<30) +#define SSG2_ENABLE (1<<29) +#define SSG3_ENABLE (1<<28) +#define SSG0_MASK_ENABLE (1<<15) /* Enable writing to SSG0 bit */ +#define SSG1_MASK_ENABLE (1<<14) +#define SSG2_MASK_ENABLE (1<<13) +#define SSG3_MASK_ENABLE (1<<12) + + +/* + * DMA Scatter/Gather Descriptor Bit fields + */ +#define SG_LINK (1<<31) /* Link */ +#define SG_TCI_ENABLE (1<<29) /* Enable Terminal Count Interrupt */ +#define SG_ETI_ENABLE (1<<28) /* Enable End of Transfer Interrupt */ +#define SG_ERI_ENABLE (1<<27) /* Enable Error Interrupt */ +#define SG_COUNT_MASK 0xFFFF /* Count Field */ + + + + +typedef uint32_t sgl_handle_t; + +typedef struct { + + /* + * Valid polarity settings: + * DMAReq0_ActiveLow + * DMAAck0_ActiveLow + * EOT0_ActiveLow + * + * DMAReq1_ActiveLow + * DMAAck1_ActiveLow + * EOT1_ActiveLow + * + * DMAReq2_ActiveLow + * DMAAck2_ActiveLow + * EOT2_ActiveLow + * + * DMAReq3_ActiveLow + * DMAAck3_ActiveLow + * EOT3_ActiveLow + */ + unsigned int polarity; + + char buffer_enable; /* Boolean: buffer enable */ + char tce_enable; /* Boolean: terminal count enable */ + char etd_output; /* Boolean: eot pin is a tc output */ + char pce; /* Boolean: parity check enable */ + + /* + * Peripheral location: + * INTERNAL_PERIPHERAL (UART0 on the 405GP) + * EXTERNAL_PERIPHERAL + */ + char pl; /* internal/external peripheral */ + + /* + * Valid pwidth settings: + * PW_8 + * PW_16 + * PW_32 + * PW_64 + */ + unsigned int pwidth; + + char dai; /* Boolean: dst address increment */ + char sai; /* Boolean: src address increment */ + + /* + * Valid psc settings: 0-3 + */ + unsigned int psc; /* Peripheral Setup Cycles */ + + /* + * Valid pwc settings: + * 0-63 + */ + unsigned int pwc; /* Peripheral Wait Cycles */ + + /* + * Valid phc settings: + * 0-7 + */ + unsigned int phc; /* Peripheral Hold Cycles */ + + /* + * Valid cp (channel priority) settings: + * PRIORITY_LOW + * PRIORITY_MID_LOW + * PRIORITY_MID_HIGH + * PRIORITY_HIGH + */ + unsigned int cp; /* channel priority */ + + /* + * Valid pf (memory read prefetch) settings: + * + * PREFETCH_1 + * PREFETCH_2 + * PREFETCH_4 + */ + unsigned int pf; /* memory read prefetch */ + + /* + * Boolean: channel interrupt enable + * NOTE: for sgl transfers, only the last descriptor will be setup to + * interrupt. + */ + char int_enable; + + char shift; /* easy access to byte_count shift, based on */ + /* the width of the channel */ + + uint32_t control; /* channel control word */ + + + /* These variabled are used ONLY in single dma transfers */ + unsigned int mode; /* transfer mode */ + dma_addr_t addr; + +} ppc_dma_ch_t; + + +typedef struct { + uint32_t control; + uint32_t src_addr; + uint32_t dst_addr; + uint32_t control_count; + uint32_t next; +} ppc_sgl_t; + + + +typedef struct { + unsigned int dmanr; + uint32_t control; /* channel ctrl word; loaded from each descrptr */ + uint32_t sgl_control; /* LK, TCI, ETI, and ERI bits in sgl descriptor */ + dma_addr_t dma_addr; /* dma (physical) address of this list */ + ppc_sgl_t *phead; + ppc_sgl_t *ptail; + +} sgl_list_info_t; + + +typedef struct { + unsigned int *src_addr; + unsigned int *dst_addr; + dma_addr_t dma_src_addr; + dma_addr_t dma_dst_addr; +} pci_alloc_desc_t; + + +extern ppc_dma_ch_t dma_channels[]; + +/* + * + * DMA API inline functions + * These functions are implemented here as inline functions for + * performance reasons. + * + */ + +static __inline__ int get_405gp_dma_status(void) +{ + return (mfdcr(DCRN_DMASR)); +} + + +static __inline__ int enable_405gp_dma(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + +#ifdef DEBUG_405DMA + if (dmanr >= MAX_405GP_DMA_CHANNELS) { + printk("enable_dma: bad channel: %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } +#endif + + + switch (dmanr) { + case 0: + if (p_dma_ch->mode == DMA_MODE_READ) { + /* peripheral to memory */ + mtdcr(DCRN_DMASA0, NULL); + mtdcr(DCRN_DMADA0, p_dma_ch->addr); + } + else if (p_dma_ch->mode == DMA_MODE_WRITE) { + /* memory to peripheral */ + mtdcr(DCRN_DMASA0, p_dma_ch->addr); + mtdcr(DCRN_DMADA0, NULL); + } + /* for other xfer modes, the addresses are already set */ + control = mfdcr(DCRN_DMACR0); + control &= ~(DMA_TM_MASK | DMA_TD); /* clear all mode bits */ + control |= (p_dma_ch->mode | DMA_CH_ENABLE); + mtdcr(DCRN_DMACR0, control); + break; + case 1: + if (p_dma_ch->mode == DMA_MODE_READ) { + mtdcr(DCRN_DMASA1, NULL); + mtdcr(DCRN_DMADA1, p_dma_ch->addr); + } else if (p_dma_ch->mode == DMA_MODE_WRITE) { + mtdcr(DCRN_DMASA1, p_dma_ch->addr); + mtdcr(DCRN_DMADA1, NULL); + } + control = mfdcr(DCRN_DMACR1); + control &= ~(DMA_TM_MASK | DMA_TD); + control |= (p_dma_ch->mode | DMA_CH_ENABLE); + mtdcr(DCRN_DMACR1, control); + break; + case 2: + if (p_dma_ch->mode == DMA_MODE_READ) { + mtdcr(DCRN_DMASA2, NULL); + mtdcr(DCRN_DMADA2, p_dma_ch->addr); + } else if (p_dma_ch->mode == DMA_MODE_WRITE) { + mtdcr(DCRN_DMASA2, p_dma_ch->addr); + mtdcr(DCRN_DMADA2, NULL); + } + control = mfdcr(DCRN_DMACR2); + control &= ~(DMA_TM_MASK | DMA_TD); + control |= (p_dma_ch->mode | DMA_CH_ENABLE); + mtdcr(DCRN_DMACR2, control); + break; + case 3: + if (p_dma_ch->mode == DMA_MODE_READ) { + mtdcr(DCRN_DMASA3, NULL); + mtdcr(DCRN_DMADA3, p_dma_ch->addr); + } else if (p_dma_ch->mode == DMA_MODE_WRITE) { + mtdcr(DCRN_DMASA3, p_dma_ch->addr); + mtdcr(DCRN_DMADA3, NULL); + } + control = mfdcr(DCRN_DMACR3); + control &= ~(DMA_TM_MASK | DMA_TD); + control |= (p_dma_ch->mode | DMA_CH_ENABLE); + mtdcr(DCRN_DMACR3, control); + break; + default: + return DMA_STATUS_BAD_CHANNEL; + } + return DMA_STATUS_GOOD; +} + + + +static __inline__ void disable_405gp_dma(unsigned int dmanr) +{ + unsigned int control; + + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + control &= ~DMA_CH_ENABLE; + mtdcr(DCRN_DMACR0, control); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + control &= ~DMA_CH_ENABLE; + mtdcr(DCRN_DMACR1, control); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + control &= ~DMA_CH_ENABLE; + mtdcr(DCRN_DMACR2, control); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + control &= ~DMA_CH_ENABLE; + mtdcr(DCRN_DMACR3, control); + break; + default: +#ifdef DEBUG_405DMA + printk("disable_dma: bad channel: %d\n", dmanr); +#endif + } +} + + + +/* + * Sets the dma mode for single DMA transfers only. + * For scatter/gather transfers, the mode is passed to the + * alloc_dma_handle() function as one of the parameters. + * + * The mode is simply saved and used later. This allows + * the driver to call set_dma_mode() and set_dma_addr() in + * any order. + * + * Valid mode values are: + * + * DMA_MODE_READ peripheral to memory + * DMA_MODE_WRITE memory to peripheral + * DMA_MODE_MM memory to memory + * DMA_MODE_MM_DEVATSRC device-paced memory to memory, device at src + * DMA_MODE_MM_DEVATDST device-paced memory to memory, device at dst + */ +static __inline__ int set_405gp_dma_mode(unsigned int dmanr, unsigned int mode) +{ + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + +#ifdef DEBUG_405DMA + switch (mode) { + case DMA_MODE_READ: + case DMA_MODE_WRITE: + case DMA_MODE_MM: + case DMA_MODE_MM_DEVATSRC: + case DMA_MODE_MM_DEVATDST: + break; + default: + printk("set_dma_mode: bad mode 0x%x\n", mode); + return DMA_STATUS_BAD_MODE; + } + if (dmanr >= MAX_405GP_DMA_CHANNELS) { + printk("set_dma_mode: bad channel 0x%x\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } +#endif + + p_dma_ch->mode = mode; + return DMA_STATUS_GOOD; +} + + + +/* + * Sets the DMA Count register. Note that 'count' is in bytes. + * However, the DMA Count register counts the number of "transfers", + * where each transfer is equal to the bus width. Thus, count + * MUST be a multiple of the bus width. + */ +static __inline__ void +set_405gp_dma_count(unsigned int dmanr, unsigned int count) +{ + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + +#ifdef DEBUG_405DMA + { + int error = 0; + switch(p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if (count & 0x1) + error = 1; + break; + case PW_32: + if (count & 0x3) + error = 1; + break; + case PW_64: + if (count & 0x7) + error = 1; + break; + default: + printk("set_dma_count: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return; + } + if (error) + printk("Warning: set_dma_count count 0x%x bus width %d\n", + count, p_dma_ch->pwidth); + } +#endif + + count = count >> p_dma_ch->shift; + switch (dmanr) { + case 0: + mtdcr(DCRN_DMACT0, count); + break; + case 1: + mtdcr(DCRN_DMACT1, count); + break; + case 2: + mtdcr(DCRN_DMACT2, count); + break; + case 3: + mtdcr(DCRN_DMACT3, count); + break; + default: +#ifdef DEBUG_405DMA + printk("set_dma_count: bad channel: %d\n", dmanr); +#endif + } +} + + + +/* + * Returns the number of bytes left to be transfered. + * After a DMA transfer, this should return zero. + * Reading this while a DMA transfer is still in progress will return + * unpredictable results. + */ +static __inline__ int get_405gp_dma_residue(unsigned int dmanr) +{ + unsigned int count; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + switch (dmanr) { + case 0: + count = mfdcr(DCRN_DMACT0); + break; + case 1: + count = mfdcr(DCRN_DMACT1); + break; + case 2: + count = mfdcr(DCRN_DMACT2); + break; + case 3: + count = mfdcr(DCRN_DMACT3); + break; + default: +#ifdef DEBUG_405DMA + printk("get_dma_residue: bad channel: %d\n", dmanr); +#endif + return 0; + } + + return (count << p_dma_ch->shift); +} + + + +/* + * Sets the DMA address for a memory to peripheral or peripheral + * to memory transfer. The address is just saved in the channel + * structure for now and used later in enable_dma(). + */ +static __inline__ void set_405gp_dma_addr(unsigned int dmanr, dma_addr_t addr) +{ + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; +#ifdef DEBUG_405DMA + { + int error = 0; + switch(p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if ((unsigned)addr & 0x1) + error = 1; + break; + case PW_32: + if ((unsigned)addr & 0x3) + error = 1; + break; + case PW_64: + if ((unsigned)addr & 0x7) + error = 1; + break; + default: + printk("set_dma_addr: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return; + } + if (error) + printk("Warning: set_dma_addr addr 0x%x bus width %d\n", + addr, p_dma_ch->pwidth); + } +#endif + + /* save dma address and program it later after we know the xfer mode */ + p_dma_ch->addr = addr; +} + + + + +/* + * Sets both DMA addresses for a memory to memory transfer. + * For memory to peripheral or peripheral to memory transfers + * the function set_dma_addr() should be used instead. + */ +static __inline__ void +set_405gp_dma_addr2(unsigned int dmanr, dma_addr_t src_dma_addr, + dma_addr_t dst_dma_addr) +{ +#ifdef DEBUG_405DMA + { + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + int error = 0; + switch(p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if (((unsigned)src_dma_addr & 0x1) || + ((unsigned)dst_dma_addr & 0x1) + ) + error = 1; + break; + case PW_32: + if (((unsigned)src_dma_addr & 0x3) || + ((unsigned)dst_dma_addr & 0x3) + ) + error = 1; + break; + case PW_64: + if (((unsigned)src_dma_addr & 0x7) || + ((unsigned)dst_dma_addr & 0x7) + ) + error = 1; + break; + default: + printk("set_dma_addr2: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return; + } + if (error) + printk("Warning: set_dma_addr2 src 0x%x dst 0x%x bus width %d\n", + src_dma_addr, dst_dma_addr, p_dma_ch->pwidth); + } +#endif + + switch (dmanr) { + case 0: + mtdcr(DCRN_DMASA0, src_dma_addr); + mtdcr(DCRN_DMADA0, dst_dma_addr); + break; + case 1: + mtdcr(DCRN_DMASA1, src_dma_addr); + mtdcr(DCRN_DMADA1, dst_dma_addr); + break; + case 2: + mtdcr(DCRN_DMASA2, src_dma_addr); + mtdcr(DCRN_DMADA2, dst_dma_addr); + break; + case 3: + mtdcr(DCRN_DMASA3, src_dma_addr); + mtdcr(DCRN_DMADA3, dst_dma_addr); + break; + default: +#ifdef DEBUG_405DMA + printk("set_dma_addr2: bad channel: %d\n", dmanr); +#endif + } +} + + + +/* + * Enables the channel interrupt. + * + * If performing a scatter/gatter transfer, this function + * MUST be called before calling alloc_dma_handle() and building + * the sgl list. Otherwise, interrupts will not be enabled, if + * they were previously disabled. + */ +static __inline__ int +enable_405gp_dma_interrupt(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + p_dma_ch->int_enable = TRUE; + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + control|= DMA_CIE_ENABLE; /* Channel Interrupt Enable */ + mtdcr(DCRN_DMACR0, control); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + control|= DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR1, control); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + control|= DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR2, control); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + control|= DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR3, control); + break; + default: +#ifdef DEBUG_405DMA + printk("enable_dma_interrupt: bad channel: %d\n", dmanr); +#endif + return DMA_STATUS_BAD_CHANNEL; + } + return DMA_STATUS_GOOD; +} + + + +/* + * Disables the channel interrupt. + * + * If performing a scatter/gatter transfer, this function + * MUST be called before calling alloc_dma_handle() and building + * the sgl list. Otherwise, interrupts will not be disabled, if + * they were previously enabled. + */ +static __inline__ int +disable_405gp_dma_interrupt(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + p_dma_ch->int_enable = TRUE; + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + control &= ~DMA_CIE_ENABLE; /* Channel Interrupt Enable */ + mtdcr(DCRN_DMACR0, control); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + control &= ~DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR1, control); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + control &= ~DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR2, control); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + control &= ~DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR3, control); + break; + default: +#ifdef DEBUG_405DMA + printk("enable_dma_interrupt: bad channel: %d\n", dmanr); +#endif + return DMA_STATUS_BAD_CHANNEL; + } + return DMA_STATUS_GOOD; +} + + +#ifdef DCRNCAP_DMA_SG + +/* + * Add a new sgl descriptor to the end of a scatter/gather list + * which was created by alloc_dma_handle(). + * + * For a memory to memory transfer, both dma addresses must be + * valid. For a peripheral to memory transfer, one of the addresses + * must be set to NULL, depending on the direction of the transfer: + * memory to peripheral: set dst_addr to NULL, + * peripheral to memory: set src_addr to NULL. + */ +static __inline__ int +add_405gp_dma_sgl(sgl_handle_t handle, dma_addr_t src_addr, dma_addr_t dst_addr, + unsigned int count) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *)handle; + ppc_dma_ch_t *p_dma_ch; + + if (!handle) { +#ifdef DEBUG_405DMA + printk("add_dma_sgl: null handle\n"); +#endif + return DMA_STATUS_BAD_HANDLE; + } + +#ifdef DEBUG_405DMA + if (psgl->dmanr >= MAX_405GP_DMA_CHANNELS) { + printk("add_dma_sgl error: psgl->dmanr == %d\n", psgl->dmanr); + return DMA_STATUS_BAD_CHANNEL; + } +#endif + + p_dma_ch = &dma_channels[psgl->dmanr]; + +#ifdef DEBUG_405DMA + { + int error = 0; + unsigned int aligned = (unsigned)src_addr | (unsigned)dst_addr | count; + switch(p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if (aligned & 0x1) + error = 1; + break; + case PW_32: + if (aligned & 0x3) + error = 1; + break; + case PW_64: + if (aligned & 0x7) + error = 1; + break; + default: + printk("add_dma_sgl: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return DMA_STATUS_GENERAL_ERROR; + } + if (error) + printk("Alignment warning: add_dma_sgl src 0x%x dst 0x%x count 0x%x bus width var %d\n", + src_addr, dst_addr, count, p_dma_ch->pwidth); + + } +#endif + + if ((unsigned)(psgl->ptail + 1) >= ((unsigned)psgl + SGL_LIST_SIZE)) { +#ifdef DEBUG_405DMA + printk("sgl handle out of memory \n"); +#endif + return DMA_STATUS_OUT_OF_MEMORY; + } + + + if (!psgl->ptail) { + psgl->phead = (ppc_sgl_t *) + ((unsigned)psgl + sizeof(sgl_list_info_t)); + psgl->ptail = psgl->phead; + } else { + psgl->ptail->next = virt_to_bus(psgl->ptail + 1); + psgl->ptail++; + } + + psgl->ptail->control = psgl->control; + psgl->ptail->src_addr = src_addr; + psgl->ptail->dst_addr = dst_addr; + psgl->ptail->control_count = (count >> p_dma_ch->shift) | + psgl->sgl_control; + psgl->ptail->next = (uint32_t)NULL; + + return DMA_STATUS_GOOD; +} + + + +/* + * Enable (start) the DMA described by the sgl handle. + */ +static __inline__ void enable_405gp_dma_sgl(sgl_handle_t handle) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *)handle; + ppc_dma_ch_t *p_dma_ch; + uint32_t sg_command; + +#ifdef DEBUG_405DMA + if (!handle) { + printk("enable_dma_sgl: null handle\n"); + return; + } else if (psgl->dmanr > (MAX_405GP_DMA_CHANNELS - 1)) { + printk("enable_dma_sgl: bad channel in handle %d\n", + psgl->dmanr); + return; + } else if (!psgl->phead) { + printk("enable_dma_sgl: sg list empty\n"); + return; + } +#endif + + p_dma_ch = &dma_channels[psgl->dmanr]; + psgl->ptail->control_count &= ~SG_LINK; /* make this the last dscrptr */ + sg_command = mfdcr(DCRN_ASGC); + + switch(psgl->dmanr) { + case 0: + mtdcr(DCRN_ASG0, virt_to_bus(psgl->phead)); + sg_command |= SSG0_ENABLE; + break; + case 1: + mtdcr(DCRN_ASG1, virt_to_bus(psgl->phead)); + sg_command |= SSG1_ENABLE; + break; + case 2: + mtdcr(DCRN_ASG2, virt_to_bus(psgl->phead)); + sg_command |= SSG2_ENABLE; + break; + case 3: + mtdcr(DCRN_ASG3, virt_to_bus(psgl->phead)); + sg_command |= SSG3_ENABLE; + break; + default: +#ifdef DEBUG_405DMA + printk("enable_dma_sgl: bad channel: %d\n", psgl->dmanr); +#endif + } + +#if 0 /* debug */ + printk("\n\nenable_dma_sgl at dma_addr 0x%x\n", + virt_to_bus(psgl->phead)); + { + ppc_sgl_t *pnext, *sgl_addr; + + pnext = psgl->phead; + while (pnext) { + printk("dma descriptor at 0x%x, dma addr 0x%x\n", + (unsigned)pnext, (unsigned)virt_to_bus(pnext)); + printk("control 0x%x src 0x%x dst 0x%x c_count 0x%x, next 0x%x\n", + (unsigned)pnext->control, (unsigned)pnext->src_addr, + (unsigned)pnext->dst_addr, + (unsigned)pnext->control_count, (unsigned)pnext->next); + + (unsigned)pnext = bus_to_virt(pnext->next); + } + printk("sg_command 0x%x\n", sg_command); + } +#endif + +#ifdef PCI_ALLOC_IS_NONCONSISTENT + /* + * This is temporary only, until pci_alloc_consistent() really does + * return "consistent" memory. + */ + flush_dcache_range((unsigned)handle, (unsigned)handle + SGL_LIST_SIZE); +#endif + + mtdcr(DCRN_ASGC, sg_command); /* start transfer */ +} + + + +/* + * Halt an active scatter/gather DMA operation. + */ +static __inline__ void disable_405gp_dma_sgl(sgl_handle_t handle) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *)handle; + uint32_t sg_command; + +#ifdef DEBUG_405DMA + if (!handle) { + printk("enable_dma_sgl: null handle\n"); + return; + } else if (psgl->dmanr > (MAX_405GP_DMA_CHANNELS - 1)) { + printk("enable_dma_sgl: bad channel in handle %d\n", + psgl->dmanr); + return; + } +#endif + sg_command = mfdcr(DCRN_ASGC); + switch(psgl->dmanr) { + case 0: + sg_command &= ~SSG0_ENABLE; + break; + case 1: + sg_command &= ~SSG1_ENABLE; + break; + case 2: + sg_command &= ~SSG2_ENABLE; + break; + case 3: + sg_command &= ~SSG3_ENABLE; + break; + default: +#ifdef DEBUG_405DMA + printk("enable_dma_sgl: bad channel: %d\n", psgl->dmanr); +#endif + } + + mtdcr(DCRN_ASGC, sg_command); /* stop transfer */ +} + + + +/* + * Returns number of bytes left to be transferred from the entire sgl list. + * *src_addr and *dst_addr get set to the source/destination address of + * the sgl descriptor where the DMA stopped. + * + * An sgl transfer must NOT be active when this function is called. + */ +static __inline__ int +get_405gp_dma_sgl_residue(sgl_handle_t handle, dma_addr_t *src_addr, + dma_addr_t *dst_addr) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *)handle; + ppc_dma_ch_t *p_dma_ch; + ppc_sgl_t *pnext, *sgl_addr; + uint32_t count_left; + +#ifdef DEBUG_405DMA + if (!handle) { + printk("get_dma_sgl_residue: null handle\n"); + return DMA_STATUS_BAD_HANDLE; + } else if (psgl->dmanr > (MAX_405GP_DMA_CHANNELS - 1)) { + printk("get_dma_sgl_residue: bad channel in handle %d\n", + psgl->dmanr); + return DMA_STATUS_BAD_CHANNEL; + } +#endif + + switch(psgl->dmanr) { + case 0: + sgl_addr = (ppc_sgl_t *)bus_to_virt(mfdcr(DCRN_ASG0)); + count_left = mfdcr(DCRN_DMACT0); + break; + case 1: + sgl_addr = (ppc_sgl_t *)bus_to_virt(mfdcr(DCRN_ASG1)); + count_left = mfdcr(DCRN_DMACT1); + break; + case 2: + sgl_addr = (ppc_sgl_t *)bus_to_virt(mfdcr(DCRN_ASG2)); + count_left = mfdcr(DCRN_DMACT2); + break; + case 3: + sgl_addr = (ppc_sgl_t *)bus_to_virt(mfdcr(DCRN_ASG3)); + count_left = mfdcr(DCRN_DMACT3); + break; + default: +#ifdef DEBUG_405DMA + printk("get_dma_sgl_residue: bad channel: %d\n", psgl->dmanr); +#endif + goto error; + } + + if (!sgl_addr) { +#ifdef DEBUG_405DMA + printk("get_dma_sgl_residue: sgl addr register is null\n"); +#endif + goto error; + } + + pnext = psgl->phead; + while (pnext && + ((unsigned)pnext < ((unsigned)psgl + SGL_LIST_SIZE) && + (pnext != sgl_addr)) + ) { + pnext = pnext++; + } + + if (pnext == sgl_addr) { /* found the sgl descriptor */ + + *src_addr = pnext->src_addr; + *dst_addr = pnext->dst_addr; + + /* + * Now search the remaining descriptors and add their count. + * We already have the remaining count from this descriptor in + * count_left. + */ + pnext++; + + while ((pnext != psgl->ptail) && + ((unsigned)pnext < ((unsigned)psgl + SGL_LIST_SIZE)) + ) { + count_left += pnext->control_count & SG_COUNT_MASK; + } + + if (pnext != psgl->ptail) { /* should never happen */ +#ifdef DEBUG_405DMA + printk("get_dma_sgl_residue error (1) psgl->ptail 0x%x handle 0x%x\n", + (unsigned int)psgl->ptail, + (unsigned int)handle); +#endif + goto error; + } + + /* success */ + p_dma_ch = &dma_channels[psgl->dmanr]; + return (count_left << p_dma_ch->shift); /* count in bytes */ + + } else { + /* this shouldn't happen */ +#ifdef DEBUG_405DMA + printk("get_dma_sgl_residue, unable to match current address 0x%x, handle 0x%x\n", + (unsigned int)sgl_addr, (unsigned int)handle); + +#endif + } + + +error: + *src_addr = (dma_addr_t)NULL; + *dst_addr = (dma_addr_t)NULL; + return 0; +} + + + + +/* + * Returns the address(es) of the buffer(s) contained in the head element of + * the scatter/gather list. The element is removed from the scatter/gather + * list and the next element becomes the head. + * + * This function should only be called when the DMA is not active. + */ +static __inline__ int +delete_405gp_dma_sgl_element(sgl_handle_t handle, dma_addr_t *src_dma_addr, + dma_addr_t *dst_dma_addr) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *)handle; + +#ifdef DEBUG_405DMA + if (!handle) { + printk("delete_sgl_element: null handle\n"); + return DMA_STATUS_BAD_HANDLE; + } else if (psgl->dmanr > (MAX_405GP_DMA_CHANNELS - 1)) { + printk("delete_sgl_element: bad channel in handle %d\n", + psgl->dmanr); + return DMA_STATUS_BAD_CHANNEL; + } +#endif + + if (!psgl->phead) { +#ifdef DEBUG_405DMA + printk("delete_sgl_element: sgl list empty\n"); +#endif + *src_dma_addr = (dma_addr_t)NULL; + *dst_dma_addr = (dma_addr_t)NULL; + return DMA_STATUS_SGL_LIST_EMPTY; + } + + *src_dma_addr = (dma_addr_t)psgl->phead->src_addr; + *dst_dma_addr = (dma_addr_t)psgl->phead->dst_addr; + + if (psgl->phead == psgl->ptail) { + /* last descriptor on the list */ + psgl->phead = NULL; + psgl->ptail = NULL; + } else { + psgl->phead++; + } + + return DMA_STATUS_GOOD; +} + +#endif /* DCRNCAP_DMA_SG */ + +/* + * The rest of the DMA API, in ppc405_dma.c + */ +extern int hw_init_dma_channel(unsigned int, ppc_dma_ch_t *); +extern int get_channel_config(unsigned int, ppc_dma_ch_t *); +extern int set_channel_priority(unsigned int, unsigned int); +extern unsigned int get_peripheral_width(unsigned int); +extern int alloc_dma_handle(sgl_handle_t *, unsigned int, unsigned int); +extern void free_dma_handle(sgl_handle_t); + +#endif +#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/ppc4xx.h b/include/asm-ppc/ppc4xx.h --- a/include/asm-ppc/ppc4xx.h Tue Feb 19 18:09:00 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,285 +0,0 @@ -/* - * BK Id: SCCS/s.ppc4xx.h 1.3 05/17/01 18:14:25 cort - */ -/* - * - * Copyright (c) 1999 Grant Erickson - * - * Module name: ppc4xx.h - * - * Description: - * A generic include file which pulls in appropriate include files - * for specific board types based on configuration settings. - * - */ - -#ifdef __KERNEL__ -#ifndef __PPC4XX_H__ -#define __PPC4XX_H__ - -#include - -#ifndef __ASSEMBLY__ - -#if defined(CONFIG_OAK) -#include -#endif - -#if defined(CONFIG_WALNUT) -#include -#endif - -/* IO_BASE is for PCI I/O. - * ISA not supported, just here to resolve copilation. - */ - -#define _IO_BASE 0xe8000000 /* The PCI address window */ -#define _ISA_MEM_BASE 0 -#define PCI_DRAM_OFFSET 0 - -extern unsigned long isa_io_base; - -/* - * The "residual" board information structure the boot loader passes - * into the kernel. - */ -extern unsigned char __res[]; - -/* I don't know if this is general to 4xx, or unique to a specific - * processor or board. In any case it is easy to move. - */ -#define PPC4xx_PCI_IO_ADDR ((uint)0xe8000000) -#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) -#define PPC4xx_PCI_CFG_ADDR ((uint)0xeec00000) -#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) -#define PPC4xx_PCI_LCFG_ADDR ((uint)0xef400000) -#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) -#define PPC4xx_ONB_IO_ADDR ((uint)0xef600000) -#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) - -#endif /* __ASSEMBLY__ */ - -/* Device Control Registers unique to 4xx */ - -#define DCRN_BEAR 0x090 /* Bus Error Address Register */ -#define DCRN_BESR 0x091 /* Bus Error Syndrome Register */ -#define BESR_DSES 0x80000000 /* Data-Side Error Status */ -#define BESR_DMES 0x40000000 /* DMA Error Status */ -#define BESR_RWS 0x20000000 /* Read/Write Status */ -#define BESR_ETMASK 0x1C000000 /* Error Type */ -#define ET_PROT 0 -#define ET_PARITY 1 -#define ET_NCFG 2 -#define ET_BUSERR 4 -#define ET_BUSTO 6 -#define DCRN_CHCR0 0x0B1 /* Chip Control Register 1 */ -#define DCRN_CHCR1 0x0B2 /* Chip Control Register 2 */ -#define DCRN_CHPSR 0x0B4 /* Chip Pin Strapping */ -#define DCRN_CPMER 0x0B9 /* CPM Enable */ -#define DCRN_CPMFR 0x0BA /* CPM Force */ -#define CPM_IIC 0x80000000 /* IIC interface */ -#define CPM_PCI 0x40000000 /* PCI bridge */ -#define CPM_CPU 0x20000000 /* processor core */ -#define CPM_DMA 0x10000000 /* DMA controller */ -#define CPM_BRG 0x08000000 /* PLB to OPB bridge */ -#define CPM_DCP 0x04000000 /* CodePack */ -#define CPM_EBC 0x02000000 /* ROM/SRAM peripheral controller */ -#define CPM_SDRAM 0x01000000 /* SDRAM memory controller */ -#define CPM_PLB 0x00800000 /* PLB bus arbiter */ -#define CPM_GPIO 0x00400000 /* General Purpose IO (??) */ -#define CPM_UART0 0x00200000 /* serial port 0 */ -#define CPM_UART1 0x00100000 /* serial port 1 */ -#define CPM_UIC 0x00080000 /* Universal Interrupt Controller */ -#define CPM_TMRCLK 0x00040000 /* CPU timers */ -#define CPM_EMAC_MM 0x00020000 /* on-chip ethernet MM unit */ -#define CPM_EMAC_RM 0x00010000 /* on-chip ethernet RM unit */ -#define CPM_EMAC_TM 0x00008000 /* on-chip ethernet TM unit */ -#define DCRN_CPMSR 0x0B8 /* CPM Status */ - -#define DCRN_DMACR0 0x100 /* DMA Channel Control Register 0 */ -#define DCRN_DMACT0 0x101 /* DMA Count Register 0 */ -#define DCRN_DMADA0 0x102 /* DMA Destination Address Register 0 */ -#define DCRN_DMASA0 0x103 /* DMA Source Address Register 0 */ -#define DCRN_ASG0 0x104 /* DMA Scatter/Gather Descriptor Addr 0 */ - -#define DCRN_DMACR1 0x108 /* DMA Channel Control Register 1 */ -#define DCRN_DMACT1 0x109 /* DMA Count Register 1 */ -#define DCRN_DMADA1 0x10A /* DMA Destination Address Register 1 */ -#define DCRN_DMASA1 0x10B /* DMA Source Address Register 1 */ -#define DCRN_ASG1 0x10C /* DMA Scatter/Gather Descriptor Addr 1 */ - -#define DCRN_DMACR2 0x110 /* DMA Channel Control Register 2 */ -#define DCRN_DMACT2 0x111 /* DMA Count Register 2 */ -#define DCRN_DMADA2 0x112 /* DMA Destination Address Register 2 */ -#define DCRN_DMASA2 0x113 /* DMA Source Address Register 2 */ -#define DCRN_ASG2 0x114 /* DMA Scatter/Gather Descriptor Addr 2 */ - -#define DCRN_DMACR3 0x118 /* DMA Channel Control Register 3 */ -#define DCRN_DMACT3 0x119 /* DMA Count Register 3 */ -#define DCRN_DMADA3 0x11A /* DMA Destination Address Register 3 */ -#define DCRN_DMASA3 0x11B /* DMA Source Address Register 3 */ -#define DCRN_ASG3 0x11C /* DMA Scatter/Gather Descriptor Addr 3 */ - -#define DCRN_DMASR 0x120 /* DMA Status Register */ -#define DCRN_ASGC 0x123 /* DMA Scatter/Gather Command */ -#define DCRN_ADR 0x124 /* DMA Address Decode */ - -#define DCRN_SLP 0x125 /* DMA Sleep Register */ -#define DCRN_POL 0x126 /* DMA Polarity Register */ - - -#define DCRN_EBCCFGADR 0x012 /* Peripheral Controller Address */ -#define DCRN_EBCCFGDATA 0x013 /* Peripheral Controller Data */ -#define DCRN_EXISR 0x040 /* External Interrupt Status Register */ -#define DCRN_EXIER 0x042 /* External Interrupt Enable Register */ -#define EXIER_CIE 0x80000000 /* Critical Interrupt Enable */ -#define EXIER_SRIE 0x08000000 /* Serial Port Rx Int. Enable */ -#define EXIER_STIE 0x04000000 /* Serial Port Tx Int. Enable */ -#define EXIER_JRIE 0x02000000 /* JTAG Serial Port Rx Int. Enable */ -#define EXIER_JTIE 0x01000000 /* JTAG Serial Port Tx Int. Enable */ -#define EXIER_D0IE 0x00800000 /* DMA Channel 0 Interrupt Enable */ -#define EXIER_D1IE 0x00400000 /* DMA Channel 1 Interrupt Enable */ -#define EXIER_D2IE 0x00200000 /* DMA Channel 2 Interrupt Enable */ -#define EXIER_D3IE 0x00100000 /* DMA Channel 3 Interrupt Enable */ -#define EXIER_E0IE 0x00000010 /* External Interrupt 0 Enable */ -#define EXIER_E1IE 0x00000008 /* External Interrupt 1 Enable */ -#define EXIER_E2IE 0x00000004 /* External Interrupt 2 Enable */ -#define EXIER_E3IE 0x00000002 /* External Interrupt 3 Enable */ -#define EXIER_E4IE 0x00000001 /* External Interrupt 4 Enable */ -#define DCRN_IOCR 0x0A0 /* Input/Output Configuration Register */ -#define IOCR_E0TE 0x80000000 -#define IOCR_E0LP 0x40000000 -#define IOCR_E1TE 0x20000000 -#define IOCR_E1LP 0x10000000 -#define IOCR_E2TE 0x08000000 -#define IOCR_E2LP 0x04000000 -#define IOCR_E3TE 0x02000000 -#define IOCR_E3LP 0x01000000 -#define IOCR_E4TE 0x00800000 -#define IOCR_E4LP 0x00400000 -#define IOCR_EDT 0x00080000 -#define IOCR_SOR 0x00040000 -#define IOCR_EDO 0x00008000 -#define IOCR_2XC 0x00004000 -#define IOCR_ATC 0x00002000 -#define IOCR_SPD 0x00001000 -#define IOCR_BEM 0x00000800 -#define IOCR_PTD 0x00000400 -#define IOCR_ARE 0x00000080 -#define IOCR_DRC 0x00000020 -#define IOCR_RDM(x) (((x) & 0x3) << 3) -#define IOCR_TCS 0x00000004 -#define IOCR_SCS 0x00000002 -#define IOCR_SPC 0x00000001 -#define DCRN_KIAR 0x014 /* Decompression Controller Address */ -#define DCRN_KIDR 0x015 /* Decompression Controller Data */ -#define DCRN_MALCR 0x180 /* MAL Configuration */ -#define MALCR_MMSR 0x80000000 /* MAL Software reset */ -#define MALCR_PLBP_1 0x00400000 /* MAL reqest priority: */ -#define MALCR_PLBP_2 0x00800000 /* lowsest is 00 */ -#define MALCR_PLBP_3 0x00C00000 /* highest */ -#define MALCR_GA 0x00200000 /* Guarded Active Bit */ -#define MALCR_OA 0x00100000 /* Ordered Active Bit */ -#define MALCR_PLBLE 0x00080000 /* PLB Lock Error Bit */ -#define MALCR_PLBLT_1 0x00040000 /* PLB Latency Timer */ -#define MALCR_PLBLT_2 0x00020000 -#define MALCR_PLBLT_3 0x00010000 -#define MALCR_PLBLT_4 0x00008000 -#define MALCR_PLBLT_DEFAULT 0x00078000 /* JSP: Is this a valid default?? */ -#define MALCR_PLBB 0x00004000 /* PLB Burst Deactivation Bit */ -#define MALCR_OPBBL 0x00000080 /* OPB Lock Bit */ -#define MALCR_EOPIE 0x00000004 /* End Of Packet Interrupt Enable */ -#define MALCR_LEA 0x00000002 /* Locked Error Active */ -#define MALCR_MSD 0x00000001 /* MAL Scroll Descriptor Bit */ -#define DCRN_MALDBR 0x183 /* Debug Register */ -#define DCRN_MALESR 0x181 /* Error Status */ -#define MALESR_EVB 0x80000000 /* Error Valid Bit */ -#define MALESR_CID 0x40000000 /* Channel ID Bit for channel 0 */ -#define MALESR_DE 0x00100000 /* Descriptor Error */ -#define MALESR_OEN 0x00080000 /* OPB Non-Fullword Error */ -#define MALESR_OTE 0x00040000 /* OPB Timeout Error */ -#define MALESR_OSE 0x00020000 /* OPB Slave Error */ -#define MALESR_PEIN 0x00010000 /* PLB Bus Error Indication */ -#define MALESR_DEI 0x00000010 /* Descriptor Error Interrupt */ -#define MALESR_ONEI 0x00000008 /* OPB Non-Fullword Error Interrupt */ -#define MALESR_OTEI 0x00000004 /* OPB Timeout Error Interrupt */ -#define MALESR_OSEI 0x00000002 /* OPB Slace Error Interrupt */ -#define MALESR_PBEI 0x00000001 /* PLB Bus Error Interrupt */ -#define DCRN_MALIER 0x182 /* Interrupt Enable */ -#define MALIER_DE 0x00000010 /* Descriptor Error Interrupt Enable */ -#define MALIER_NE 0x00000008 /* OPB Non-word Transfer Int Enable */ -#define MALIER_TE 0x00000004 /* OPB Time Out Error Interrupt Enable */ -#define MALIER_OPBE 0x00000002 /* OPB Slave Error Interrupt Enable */ -#define MALIER_PLBE 0x00000001 /* PLB Error Interrupt Enable */ -#define DCRN_MALTXCARR 0x185 /* TX Channed Active Reset Register */ -#define DCRN_MALTXCASR 0x184 /* TX Channel Active Set Register */ -#define DCRN_MALTXDEIR 0x187 /* Tx Descriptor Error Interrupt */ -#define DCRN_MALTXEOBISR 0x186 /* Tx End of Buffer Interrupt Status */ -#define MALOBISR_CH0 0x80000000 /* EOB channel 1 bit */ -#define MALOBISR_CH2 0x40000000 /* EOB channel 2 bit */ -#define DCRN_MALRXCARR 0x191 /* RX Channed Active Reset Register */ -#define DCRN_MALRXCASR 0x190 /* RX Channel Active Set Register */ -#define DCRN_MALRXDEIR 0x193 /* Rx Descriptor Error Interrupt */ -#define DCRN_MALRXEOBISR 0x192 /* Rx End of Buffer Interrupt Status */ -#define DCRN_MALRXCTP0R 0x1C0 /* Channel Rx 0 Channel Table Pointer */ -#define DCRN_MALTXCTP0R 0x1A0 /* Channel Tx 0 Channel Table Pointer */ -#define DCRN_MALTXCTP1R 0x1A1 /* Channel Tx 1 Channel Table Pointer */ -#define DCRN_MALRCBS0 0x1E0 /* Channel Rx 0 Channel Buffer Size */ -#define DCRN_MEMCFGADR 0x010 /* Memory Controller Address */ -#define DCRN_MEMCFGDATA 0x011 /* Memory Controller Data */ -#define DCRN_OCMISARC 0x018 /* OCM Instr Side Addr Range Compare */ -#define DCRN_OCMISCR 0x019 /* OCM Instr Side Control */ -#define DCRN_OCMDSARC 0x01A /* OCM Data Side Addr Range Compare */ -#define DCRN_OCMDSCR 0x01B /* OCM Data Side Control */ -#define DCRN_PLB0_ACR 0x087 /* PLB Arbiter Control */ -#define DCRN_PLB0_BEAR 0x086 /* PLB Error Address */ -#define DCRN_PLB0_BESR 0x084 /* PLB Error Status */ -#define DCRN_PLLMR 0x0B0 /* PLL Mode */ -#define DCRN_POB0_BEAR 0x0A2 /* PLB to OPB Error Address */ -#define DCRN_POB0_BESR0 0x0A0 /* PLB to OPB Error Status Register 1 */ -#define DCRN_POB0_BESR1 0x0A4 /* PLB to OPB Error Status Register 1 */ -#define DCRN_UICCR 0x0C3 /* UIC Critical */ -#define DCRN_UICER 0x0C2 /* UIC Enable */ -#define DCRN_UICPR 0x0C4 /* UIC Polarity */ -#define DCRN_UICSR 0x0C0 /* UIC Status */ -#define DCRN_UICTR 0x0C5 /* UIC Triggering */ -#define DCRN_UICMSR 0x0C6 /* UIC Masked Status */ -#define DCRN_UICVR 0x0C7 /* UIC Vector */ -#define DCRN_UICVCR 0x0C8 /* UIC Vector Configuration */ -#define UIC_U0 0x80000000 /* UART0 */ -#define UIC_U1 0x40000000 /* UART1 */ -#define UIC_IIC 0x20000000 /* IIC */ -#define UIC_EM 0x10000000 /* External Master */ -#define UIC_PCI 0x08000000 /* PCI */ -#define UIC_D0 0x04000000 /* DMA Channel 0 */ -#define UIC_D1 0x02000000 /* DMA Channel 1 */ -#define UIC_D2 0x01000000 /* DMA Channel 2 */ -#define UIC_D3 0x00800000 /* DMA Channel 3 */ -#define UIC_EW 0x00400000 /* Ethernet Wake-up */ -#define UIC_MS 0x00200000 /* MAL SERR */ -#define UIC_MTE 0x00100000 /* MAL TX EOB */ -#define UIC_MRE 0x00080000 /* MAL RX EOB */ -#define UIC_MTD 0x00040000 /* MAL TX DE */ -#define UIC_MRD 0x00020000 /* MAL RX DE */ -#define UIC_E 0x00010000 /* Ethernet */ -#define UIC_EPS 0x00008000 /* External PCI SERR */ -#define UIC_EC 0x00004000 /* ECC Correctable Error */ -#define UIC_PPM 0x00002000 /* PCI Power Management */ -/* -** 0x00001000 reserved -** 0x00000800 reserved -** 0x00000400 reserved -** 0x00000200 reserved -** 0x00000100 reserved -** 0x00000080 reserved -*/ -#define UIC_EIR0 0x00000040 /* External IRQ 0 */ -#define UIC_EIR1 0x00000020 /* External IRQ 0 */ -#define UIC_EIR2 0x00000010 /* External IRQ 0 */ -#define UIC_EIR3 0x00000008 /* External IRQ 0 */ -#define UIC_EIR4 0x00000004 /* External IRQ 0 */ -#define UIC_EIR5 0x00000002 /* External IRQ 0 */ -#define UIC_EIR6 0x00000001 /* External IRQ 0 */ - -#endif /* __PPC4XX_H__ */ -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/ppc4xx_pic.h b/include/asm-ppc/ppc4xx_pic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/ppc4xx_pic.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,30 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * + * Copyright (c) 1999 Grant Erickson + * + * Module name: ppc4xx_pic.h + * + * Description: + * Interrupt controller driver for PowerPC 4xx-based processors. + */ + +#ifndef __PPC4XX_PIC_H__ +#define __PPC4XX_PIC_H__ + +#include +#include + +/* External Global Variables */ + +extern struct hw_interrupt_type *ppc4xx_pic; + + +/* Function Prototypes */ + +extern void ppc4xx_pic_init(void); +extern int ppc4xx_pic_get_irq(struct pt_regs *regs); + +#endif /* __PPC4XX_PIC_H__ */ diff -Nru a/include/asm-ppc/ppc4xx_serial.h b/include/asm-ppc/ppc4xx_serial.h --- a/include/asm-ppc/ppc4xx_serial.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,104 +0,0 @@ -/* - * BK Id: SCCS/s.ppc4xx_serial.h 1.3 05/17/01 18:14:25 cort - */ -/* - * Copyright 2000 MontaVista Software Inc. - * PPC405GP modifications - * Author: MontaVista Software, Inc. - * frank_rowand@mvista.com or source@mvista.com - * debbie_chu@mvista.com - * - * Module name: ppc405_serial.h - * - * Description: - * Macros, definitions, and data structures specific to the IBM PowerPC - * 405 on-chip serial port devices. - */ - -#ifdef __KERNEL__ -#ifndef __ASMPPC_PPC4xx_SERIAL_H -#define __ASMPPC_PPC4xx_SERIAL_H - -#include - -#ifdef CONFIG_SERIAL_MANY_PORTS -#define RS_TABLE_SIZE 64 -#else -#define RS_TABLE_SIZE 4 -#endif - -#define PPC405GP_UART0_INT 0 -#define PPC405GP_UART1_INT 1 - -/* -** 405GP UARTs are *not* PCI devices, so need to specify a non-pci memory -** address and an io_type of SERIAL_IO_MEM. -*/ - -#define PPC405GP_UART0_IO_BASE (u8 *) 0xef600300 -#define PPC405GP_UART1_IO_BASE (u8 *) 0xef600400 - -/* -** - there is no config option for this -** - this name could be more informative -** - also see arch/ppc/kernel/ppc405_serial.c -** -** #define CONFIG_PPC405GP_INTERNAL_CLOCK -*/ -#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK -#define BASE_BAUD 201600 -#else -#define BASE_BAUD 691200 -#endif - - -#ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) -#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) -#else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) -#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF) -#endif - - -#ifdef CONFIG_STB03XXX - -#define UART0_IO_BASE 0x40040000 -#define UART0_INT 20 - -#define STD_SERIAL_PORT_DFNS \ - /* ttyS0 */ \ - { 0, BASE_BAUD, 0, UART0_INT, STD_COM_FLAGS, 0, 0, 0, 0, 0, 0, 0, \ - UART0_IO_BASE, 0, 0, 0, {}, {}, {}, SERIAL_IO_MEM, NULL }, - -#elif defined(CONFIG_UART1_DFLT_CONSOLE) - -#define STD_SERIAL_PORT_DFNS \ - /* ttyS1 */ \ - { 0, BASE_BAUD, 0, PPC405GP_UART1_INT, STD_COM_FLAGS, 0, 0, 0, 0, 0, 0, 0, \ - PPC405GP_UART1_IO_BASE, 0, 0, 0, {}, {}, {}, SERIAL_IO_MEM, NULL }, \ - /* ttyS0 */ \ - { 0, BASE_BAUD, 0, PPC405GP_UART0_INT, STD_COM_FLAGS, 0, 0, 0, 0, 0, 0, 0, \ - PPC405GP_UART0_IO_BASE, 0, 0, 0, {}, {}, {}, SERIAL_IO_MEM, NULL }, - -#else - -#define STD_SERIAL_PORT_DFNS \ - /* ttyS0 */ \ - { 0, BASE_BAUD, 0, PPC405GP_UART0_INT, STD_COM_FLAGS, 0, 0, 0, 0, 0, 0, 0, \ - PPC405GP_UART0_IO_BASE, 0, 0, 0, {}, {}, {}, SERIAL_IO_MEM, NULL }, \ - /* ttyS1 */ \ - { 0, BASE_BAUD, 0, PPC405GP_UART1_INT, STD_COM_FLAGS, 0, 0, 0, 0, 0, 0, 0, \ - PPC405GP_UART1_IO_BASE, 0, 0, 0, {}, {}, {}, SERIAL_IO_MEM, NULL }, - -#endif - - -#define SERIAL_PORT_DFNS \ - STD_SERIAL_PORT_DFNS \ - {} - - - -#endif /* __ASMPPC_PPC4xx_SERIAL_H */ -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/ppc_asm.h b/include/asm-ppc/ppc_asm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/ppc_asm.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,313 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * include/asm-ppc/ppc_asm.h + * + * Definitions used by various bits of low-level assembly code on PowerPC. + * + * Copyright (C) 1995-1999 Gary Thomas, Paul Mackerras, Cort Dougan. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +/* + * Macros for storing registers into and loading registers from + * exception frames. + */ +#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base) +#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) +#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) +#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) +#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) +#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base) +#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) +#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) +#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) +#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) + +#define SAVE_FPR(n, base) stfd n,THREAD_FPR0+8*(n)(base) +#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) +#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) +#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) +#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) +#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) +#define REST_FPR(n, base) lfd n,THREAD_FPR0+8*(n)(base) +#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) +#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) +#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) +#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) +#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) + +/* + * Once a version of gas that understands the AltiVec instructions + * is freely available, we can do this the normal way... - paulus + */ +#define LVX(r,a,b) .long (31<<26)+((r)<<21)+((a)<<16)+((b)<<11)+(103<<1) +#define STVX(r,a,b) .long (31<<26)+((r)<<21)+((a)<<16)+((b)<<11)+(231<<1) +#define MFVSCR(r) .long (4<<26)+((r)<<21)+(770<<1) +#define MTVSCR(r) .long (4<<26)+((r)<<11)+(802<<1) + +#define SAVE_VR(n,b,base) li b,THREAD_VR0+(16*(n)); STVX(n,b,base) +#define SAVE_2VR(n,b,base) SAVE_VR(n,b,base); SAVE_VR(n+1,b,base) +#define SAVE_4VR(n,b,base) SAVE_2VR(n,b,base); SAVE_2VR(n+2,b,base) +#define SAVE_8VR(n,b,base) SAVE_4VR(n,b,base); SAVE_4VR(n+4,b,base) +#define SAVE_16VR(n,b,base) SAVE_8VR(n,b,base); SAVE_8VR(n+8,b,base) +#define SAVE_32VR(n,b,base) SAVE_16VR(n,b,base); SAVE_16VR(n+16,b,base) +#define REST_VR(n,b,base) li b,THREAD_VR0+(16*(n)); LVX(n,b,base) +#define REST_2VR(n,b,base) REST_VR(n,b,base); REST_VR(n+1,b,base) +#define REST_4VR(n,b,base) REST_2VR(n,b,base); REST_2VR(n+2,b,base) +#define REST_8VR(n,b,base) REST_4VR(n,b,base); REST_4VR(n+4,b,base) +#define REST_16VR(n,b,base) REST_8VR(n,b,base); REST_8VR(n+8,b,base) +#define REST_32VR(n,b,base) REST_16VR(n,b,base); REST_16VR(n+16,b,base) + +#ifdef CONFIG_PPC601_SYNC_FIX +#define SYNC \ +BEGIN_FTR_SECTION \ + sync; \ + isync; \ +END_FTR_SECTION_IFSET(CPU_FTR_601) +#define SYNC_601 \ +BEGIN_FTR_SECTION \ + sync; \ +END_FTR_SECTION_IFSET(CPU_FTR_601) +#define ISYNC_601 \ +BEGIN_FTR_SECTION \ + isync; \ +END_FTR_SECTION_IFSET(CPU_FTR_601) +#else +#define SYNC +#define SYNC_601 +#define ISYNC_601 +#endif + +#ifndef CONFIG_SMP +#define TLBSYNC +#else /* CONFIG_SMP */ +/* tlbsync is not implemented on 601 */ +#define TLBSYNC \ +BEGIN_FTR_SECTION \ + tlbsync; \ + sync; \ +END_FTR_SECTION_IFCLR(CPU_FTR_601) +#endif + +/* + * This instruction is not implemented on the PPC 603 or 601; however, on + * the 403GCX and 405GP tlbia IS defined and tlbie is not. + * All of these instructions exist in the 8xx, they have magical powers, + * and they must be used. + */ + +#if !defined(CONFIG_4xx) && !defined(CONFIG_8xx) +#define tlbia \ + li r4,1024; \ + mtctr r4; \ + lis r4,KERNELBASE@h; \ +0: tlbie r4; \ + addi r4,r4,0x1000; \ + bdnz 0b +#endif + +#ifndef CONFIG_PPC_ISERIES +/* + * On APUS (Amiga PowerPC cpu upgrade board), we don't know the + * physical base address of RAM at compile time. + */ +#define tophys(rd,rs) \ +0: addis rd,rs,-KERNELBASE@h; \ + .section ".vtop_fixup","aw"; \ + .align 1; \ + .long 0b; \ + .previous + +#define tovirt(rd,rs) \ +0: addis rd,rs,KERNELBASE@h; \ + .section ".ptov_fixup","aw"; \ + .align 1; \ + .long 0b; \ + .previous +#else /* CONFIG_PPC_ISERIES */ + +#define tophys(rd,rs) \ + mr rd,rs + +#define tovirt(rd,rs) \ + mr rd,rs + +/* Macros to adjust thread priority for iSeries hardware multi-threading */ +#define HMT_LOW or 1,1,1 +#define HMT_MEDIUM or 2,2,2 +#define HMT_HIGH or 3,3,3 + +#endif /* CONFIG_PPC_ISERIES */ + +/* + * On 64-bit cpus, we use the rfid instruction instead of rfi, but + * we then have to make sure we preserve the top 32 bits except for + * the 64-bit mode bit, which we clear. + */ +#ifdef CONFIG_PPC64BRIDGE +#define FIX_SRR1(ra, rb) \ + mr rb,ra; \ + mfmsr ra; \ + clrldi ra,ra,1; /* turn off 64-bit mode */ \ + rldimi ra,rb,0,32 +#define RFI .long 0x4c000024 /* rfid instruction */ +#define MTMSRD(r) .long (0x7c000164 + ((r) << 21)) /* mtmsrd */ +#define CLR_TOP32(r) rlwinm (r),(r),0,0,31 /* clear top 32 bits */ + +#else +#define FIX_SRR1(ra, rb) +#define RFI rfi +#define MTMSRD(r) mtmsr r +#define CLR_TOP32(r) +#endif /* CONFIG_PPC64BRIDGE */ + +#ifdef CONFIG_PPC_ISERIES +#define HMT_LOW or 1,1,1 +#define HMT_MEDIUM or 2,2,2 +#define HMT_HIGH or 3,3,3 +#else /* CONFIG_PPC_ISERIES */ +#define HMT_LOW /* nothing */ +#define HMT_MEDIUM /* nothing */ +#define HMT_HIGH /* nothing */ + +#endif /* CONFIG_PPC_ISERIES */ + +#ifdef CONFIG_IBM405_ERR77 +#define PPC405_ERR77(ra,rb) dcbt ra, rb; +#define PPC405_ERR77_SYNC sync; +#else +#define PPC405_ERR77(ra,rb) +#define PPC405_ERR77_SYNC +#endif + +/* The boring bits... */ + +/* Condition Register Bit Fields */ + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + + +/* General Purpose Registers (GPRs) */ + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + + +/* Floating Point Registers (FPRs) */ + +#define fr0 0 +#define fr1 1 +#define fr2 2 +#define fr3 3 +#define fr4 4 +#define fr5 5 +#define fr6 6 +#define fr7 7 +#define fr8 8 +#define fr9 9 +#define fr10 10 +#define fr11 11 +#define fr12 12 +#define fr13 13 +#define fr14 14 +#define fr15 15 +#define fr16 16 +#define fr17 17 +#define fr18 18 +#define fr19 19 +#define fr20 20 +#define fr21 21 +#define fr22 22 +#define fr23 23 +#define fr24 24 +#define fr25 25 +#define fr26 26 +#define fr27 27 +#define fr28 28 +#define fr29 29 +#define fr30 30 +#define fr31 31 + +#define vr0 0 +#define vr1 1 +#define vr2 2 +#define vr3 3 +#define vr4 4 +#define vr5 5 +#define vr6 6 +#define vr7 7 +#define vr8 8 +#define vr9 9 +#define vr10 10 +#define vr11 11 +#define vr12 12 +#define vr13 13 +#define vr14 14 +#define vr15 15 +#define vr16 16 +#define vr17 17 +#define vr18 18 +#define vr19 19 +#define vr20 20 +#define vr21 21 +#define vr22 22 +#define vr23 23 +#define vr24 24 +#define vr25 25 +#define vr26 26 +#define vr27 27 +#define vr28 28 +#define vr29 29 +#define vr30 30 +#define vr31 31 + +/* some stab codes */ +#define N_FUN 36 +#define N_RSYM 64 +#define N_SLINE 68 +#define N_SO 100 diff -Nru a/include/asm-ppc/pplus.h b/include/asm-ppc/pplus.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/pplus.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,94 @@ +/* + * arch/ppc/kernel/pplus.h + * + * Definitions for Motorola MCG Falcon/Raven & HAWK North Bridge & Memory ctlr. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __ASMPPC_PPLUS_H +#define __ASMPPC_PPLUS_H + +#include + +/* + * The Falcon/Raven and HAWK have 4 sets of registers: + * 1) PPC Registers which define the mappings from PPC bus to PCI bus, + * etc. + * 2) PCI Registers which define the mappings from PCI bus to PPC bus and the + * MPIC base address. + * 3) MPIC registers + * 4) System Memory Controller (SMC) registers. + */ + +#define PPLUS_RAVEN_VEND_DEV_ID 0x48011057 +#define PPLUS_HAWK_VEND_DEV_ID 0x48031057 + +#define PPLUS_PCI_CONFIG_ADDR_OFF 0x00000cf8 +#define PPLUS_PCI_CONFIG_DATA_OFF 0x00000cfc + +#define PPLUS_MPIC_SIZE 0x00040000U +#define PPLUS_SMC_SIZE 0x00001000U + +/* + * Define PPC register offsets. + */ +#define PPLUS_PPC_XSADD0_OFF 0x40 +#define PPLUS_PPC_XSOFF0_OFF 0x44 +#define PPLUS_PPC_XSADD1_OFF 0x48 +#define PPLUS_PPC_XSOFF1_OFF 0x4c +#define PPLUS_PPC_XSADD2_OFF 0x50 +#define PPLUS_PPC_XSOFF2_OFF 0x54 +#define PPLUS_PPC_XSADD3_OFF 0x58 +#define PPLUS_PPC_XSOFF3_OFF 0x5c + +/* + * Define PCI register offsets. + */ +#define PPLUS_PCI_PSADD0_OFF 0x80 +#define PPLUS_PCI_PSOFF0_OFF 0x84 +#define PPLUS_PCI_PSADD1_OFF 0x88 +#define PPLUS_PCI_PSOFF1_OFF 0x8c +#define PPLUS_PCI_PSADD2_OFF 0x90 +#define PPLUS_PCI_PSOFF2_OFF 0x94 +#define PPLUS_PCI_PSADD3_OFF 0x98 +#define PPLUS_PCI_PSOFF3_OFF 0x9c + +/* + * Define the System Memory Controller (SMC) register offsets. + */ +#define PPLUS_SMC_RAM_A_SIZE_REG_OFF 0x10 +#define PPLUS_SMC_RAM_B_SIZE_REG_OFF 0x11 +#define PPLUS_SMC_RAM_C_SIZE_REG_OFF 0x12 +#define PPLUS_SMC_RAM_D_SIZE_REG_OFF 0x13 +#define PPLUS_SMC_RAM_E_SIZE_REG_OFF 0xc0 /* HAWK Only */ +#define PPLUS_SMC_RAM_F_SIZE_REG_OFF 0xc1 /* HAWK Only */ +#define PPLUS_SMC_RAM_G_SIZE_REG_OFF 0xc2 /* HAWK Only */ +#define PPLUS_SMC_RAM_H_SIZE_REG_OFF 0xc3 /* HAWK Only */ + +#define PPLUS_FALCON_SMC_REG_COUNT 4 +#define PPLUS_HAWK_SMC_REG_COUNT 8 + + + +int pplus_init(struct pci_controller *hose, + uint ppc_reg_base, + ulong processor_pci_mem_start, + ulong processor_pci_mem_end, + ulong processor_pci_io_start, + ulong processor_pci_io_end, + ulong processor_mpic_base); + +unsigned long pplus_get_mem_size(uint smc_base); + +int pplus_mpic_init(unsigned int pci_mem_offset); + +#endif /* __ASMPPC_PPLUS_H */ diff -Nru a/include/asm-ppc/processor.h b/include/asm-ppc/processor.h --- a/include/asm-ppc/processor.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/processor.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.processor.h 1.31 10/05/01 16:26:22 paulus + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef __ASM_PPC_PROCESSOR_H @@ -12,6 +12,7 @@ #define current_text_addr() ({ __label__ _l; _l: &&_l;}) #include +#include #include #include @@ -136,10 +137,32 @@ #define DBCR_JOI 0x00000002 /* JTAG Serial Outbound Int. Enable */ #define DBCR_JII 0x00000001 /* JTAG Serial Inbound Int. Enable */ #define SPRN_DBCR0 0x3F2 /* Debug Control Register 0 */ -#define SPRN_DBCR1 0x3BD /* Debug Control Register 1 */ -#define SPRN_DBSR 0x3F0 /* Debug Status Register */ -#define DBSR_IC 0x80000000 /* Instruction Completion */ -#define DBSR_TIE 0x10000000 /* Trap Instruction debug Event */ +#define DBCR0_EDM 0x80000000 /* External Debug Mode */ +#define DBCR0_IDM 0x40000000 /* Internal Debug Mode */ +#define DBCR0_RST 0x30000000 /* all the bits in the RST field */ +#define DBCR0_RST_SYSTEM 0x30000000 /* System Reset */ +#define DBCR0_RST_CHIP 0x20000000 /* Chip Reset */ +#define DBCR0_RST_CORE 0x10000000 /* Core Reset */ +#define DBCR0_RST_NONE 0x00000000 /* No Reset */ +#define DBCR0_IC 0x08000000 /* Instruction Completion */ +#define DBCR0_BT 0x04000000 /* Branch Taken */ +#define DBCR0_EDE 0x02000000 /* Exception Debug Event */ +#define DBCR0_TDE 0x01000000 /* TRAP Debug Event */ +#define DBCR0_IA1 0x00800000 /* Instr Addr compare 1 enable */ +#define DBCR0_IA2 0x00400000 /* Instr Addr compare 2 enable */ +#define DBCR0_IA12 0x00200000 /* Instr Addr 1-2 range enable */ +#define DBCR0_IA12X 0x00100000 /* Instr Addr 1-2 range eXclusive */ +#define DBCR0_IA3 0x00080000 /* Instr Addr compare 3 enable */ +#define DBCR0_IA4 0x00040000 /* Instr Addr compare 4 enable */ +#define DBCR0_IA34 0x00020000 /* Instr Addr 3-4 range Enable */ +#define DBCR0_IA34X 0x00010000 /* Instr Addr 3-4 range eXclusive */ +#define DBCR0_IA12T 0x00008000 /* Instr Addr 1-2 range Toggle */ +#define DBCR0_IA34T 0x00004000 /* Instr Addr 3-4 range Toggle */ +#define DBCR0_FT 0x00000001 /* Freeze Timers on debug event */ +#define SPRN_DBCR1 0x3BD /* Debug Control Register 1 */ +#define SPRN_DBSR 0x3F0 /* Debug Status Register */ +#define DBSR_IC 0x80000000 /* Instruction Completion */ +#define DBSR_TIE 0x10000000 /* Trap Instruction debug Event */ #define SPRN_DCCR 0x3FA /* Data Cache Cacheability Register */ #define DCCR_NOCACHE 0 /* Noncacheable */ #define DCCR_CACHE 1 /* Cacheable */ @@ -191,12 +214,17 @@ #define HID0_EBD (1<<28) /* Enable Bus Data Parity */ #define HID0_SBCLK (1<<27) #define HID0_EICE (1<<26) +#define HID0_TBEN (1<<26) /* Timebase enable - 7450 */ #define HID0_ECLK (1<<25) #define HID0_PAR (1<<24) +#define HID0_STEN (1<<24) /* S/W Tablewalk enable - 7450 */ #define HID0_DOZE (1<<23) #define HID0_NAP (1<<22) #define HID0_SLEEP (1<<21) #define HID0_DPM (1<<20) +#define HID0_BHTCLR (1<<18) /* Clear branch history table - 7450 */ +#define HID0_XAEN (1<<17) /* Extended addressing enable - 7450 */ +#define HID0_NHR (1<<16) /* Not hard reset (software bit-7450)*/ #define HID0_ICE (1<<15) /* Instruction Cache Enable */ #define HID0_DCE (1<<14) /* Data Cache Enable */ #define HID0_ILOCK (1<<13) /* Instruction Cache Lock */ @@ -207,11 +235,23 @@ #define HID0_SGE (1<<7) /* Store Gathering Enable */ #define HID0_SIED (1<<7) /* Serial Instr. Execution [Disable] */ #define HID0_DFCA (1<<6) /* Data Cache Flush Assist */ -#define HID0_BTIC (1<<5) /* Branch Target Instruction Cache Enable */ +#define HID0_BTIC (1<<5) /* Branch Target Instr Cache Enable */ +#define HID0_LRSTK (1<<4) /* Link Stack enable - 7450 */ #define HID0_ABE (1<<3) /* Address Broadcast Enable */ +#define HID0_FOLD (1<<3) /* Branch Folding enable - 7450 */ #define HID0_BHTE (1<<2) /* Branch History Table Enable */ #define HID0_BTCD (1<<1) /* Branch target cache disable */ +#define HID0_NOPDST (1<<1) /* No-op dst, dstt, etc. instr. */ +#define HID0_NOPTI (1<<0) /* No-op dcbt and dcbst instr. */ + #define SPRN_HID1 0x3F1 /* Hardware Implementation Register 1 */ +#define HID1_EMCP (1<<31) /* 7450 Machine Check Pin Enable */ +#define HID1_PC0 (1<<16) /* 7450 PLL_CFG[0] */ +#define HID1_PC1 (1<<15) /* 7450 PLL_CFG[1] */ +#define HID1_PC2 (1<<14) /* 7450 PLL_CFG[2] */ +#define HID1_PC3 (1<<13) /* 7450 PLL_CFG[3] */ +#define HID1_SYNCBE (1<<11) /* 7450 ABE for sync, eieio */ +#define HID1_ABE (1<<10) /* 7450 Address Broadcast Enable */ #define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */ #define SPRN_IAC1 0x3F4 /* Instruction Address Compare 1 */ #define SPRN_IAC2 0x3F5 /* Instruction Address Compare 2 */ @@ -229,6 +269,10 @@ #define SPRN_ICDBDR 0x3D3 /* Instruction Cache Debug Data Register */ #define SPRN_ICMP 0x3D5 /* Instruction TLB Compare Register */ #define SPRN_ICTC 0x3FB /* Instruction Cache Throttling Control Reg */ +#define SPRN_ICTRL 0x3F3 /* 1011 7450 icache and interrupt ctrl */ +#define ICTRL_EICE 0x08000000 /* enable icache parity errs */ +#define ICTRL_EDCE 0x04000000 /* enable dcache parity errs */ +#define ICTRL_EICP 0x00000100 /* enable icache par. check */ #define SPRN_IMISS 0x3D4 /* Instruction TLB Miss Register */ #define SPRN_IMMR 0x27E /* Internal Memory Map Register */ #define SPRN_L2CR 0x3F9 /* Level 2 Cache Control Regsiter */ @@ -261,6 +305,13 @@ #define L2CR_L2DF 0x00004000 /* L2 differential clock */ #define L2CR_L2BYP 0x00002000 /* L2 DLL bypass */ #define L2CR_L2IP 0x00000001 /* L2 GI in progress */ +#define SPRN_L2CR2 0x3f8 +#define SPRN_L3CR 0x3FA /* Level 3 Cache Control Regsiter (7450) */ +#define L3CR_L3E 0x80000000 /* L3 enable */ +#define SPRN_MSSCR0 0x3f6 /* Memory Subsystem Control Register 0 */ +#define SPRN_MSSSR0 0x3f7 /* Memory Subsystem Status Register 1 */ +#define SPRN_LDSTCR 0x3f8 /* Load/Store control register */ +#define SPRN_LDSTDB 0x3f4 /* */ #define SPRN_LR 0x008 /* Link Register */ #define SPRN_MMCR0 0x3B8 /* Monitor Mode Control Register 0 */ #define SPRN_MMCR1 0x3BC /* Monitor Mode Control Register 1 */ @@ -275,6 +326,8 @@ #define SPRN_PMC2 0x3BA /* Performance Counter Register 2 */ #define SPRN_PMC3 0x3BD /* Performance Counter Register 3 */ #define SPRN_PMC4 0x3BE /* Performance Counter Register 4 */ +#define SPRN_PTEHI 0x3D5 /* 981 7450 PTE HI word (S/W TLB load) */ +#define SPRN_PTELO 0x3D6 /* 982 7450 PTE LO word (S/W TLB load) */ #define SPRN_PVR 0x11F /* Processor Version Register */ #define SPRN_RPA 0x3D6 /* Required Physical Address Register */ #define SPRN_SDA 0x3BF /* Sampled Data Address Register */ @@ -295,17 +348,23 @@ #define SPRN_SRR1 0x01B /* Save/Restore Register 1 */ #define SPRN_SRR2 0x3DE /* Save/Restore Register 2 */ #define SPRN_SRR3 0x3DF /* Save/Restore Register 3 */ +#define SPRN_TBHI 0x3DC /* Time Base High (4xx) */ +#define SPRN_TBHU 0x3CC /* Time Base High User-mode (4xx) */ +#define SPRN_TBLO 0x3DD /* Time Base Low (4xx) */ +#define SPRN_TBLU 0x3CD /* Time Base Low User-mode (4xx) */ #define SPRN_TBRL 0x10C /* Time Base Read Lower Register (user, R/O) */ #define SPRN_TBRU 0x10D /* Time Base Read Upper Register (user, R/O) */ -#define SPRN_TBWL 0x11C /* Time Base Lower Register (supervisor, R/W) */ -#define SPRN_TBWU 0x11D /* Time Base Upper Register (supervisor, R/W) */ +#define SPRN_TBWL 0x11C /* Time Base Lower Register (super, R/W) */ +#define SPRN_TBWU 0x11D /* Time Base Upper Register (super, R/W) */ #define SPRN_TCR 0x3DA /* Timer Control Register */ #define TCR_WP(x) (((x)&0x3)<<30) /* WDT Period */ +#define TCR_WP_MASK TCR_WP(3) #define WP_2_17 0 /* 2^17 clocks */ #define WP_2_21 1 /* 2^21 clocks */ #define WP_2_25 2 /* 2^25 clocks */ #define WP_2_29 3 /* 2^29 clocks */ #define TCR_WRC(x) (((x)&0x3)<<28) /* WDT Reset Control */ +#define TCR_WRC_MASK TCR_WRC(3) #define WRC_NONE 0 /* No reset will occur */ #define WRC_CORE 1 /* Core reset will occur */ #define WRC_CHIP 2 /* Chip reset will occur */ @@ -313,6 +372,7 @@ #define TCR_WIE 0x08000000 /* WDT Interrupt Enable */ #define TCR_PIE 0x04000000 /* PIT Interrupt Enable */ #define TCR_FP(x) (((x)&0x3)<<24) /* FIT Period */ +#define TCR_FP_MASK TCR_FP(3) #define FP_2_9 0 /* 2^9 clocks */ #define FP_2_13 1 /* 2^13 clocks */ #define FP_2_17 2 /* 2^17 clocks */ @@ -331,6 +391,7 @@ #define SPRN_THRM2 0x3FD /* Thermal Management Register 2 */ #define SPRN_THRM3 0x3FE /* Thermal Management Register 3 */ #define THRM3_E (1<<0) +#define SPRN_TLBMISS 0x3D4 /* 980 7450 TLB Miss Register */ #define SPRN_TSR 0x3D8 /* Timer Status Register */ #define TSR_ENW 0x80000000 /* Enable Next Watchdog */ #define TSR_WIS 0x40000000 /* WDT Interrupt Status */ @@ -387,6 +448,7 @@ #define IMISS SPRN_IMISS /* Instruction TLB Miss Register */ #define IMMR SPRN_IMMR /* PPC 860/821 Internal Memory Map Register */ #define L2CR SPRN_L2CR /* PPC 750 L2 control register */ +#define L3CR SPRN_L3CR /* PPC 7450 L3 Cache control register */ #define LR SPRN_LR #define PVR SPRN_PVR /* Processor Version */ #define RPA SPRN_RPA /* Required Physical Address Register */ @@ -445,7 +507,9 @@ #define PVR_403GC 0x00200200 #define PVR_403GCX 0x00201400 #define PVR_405GP 0x40110000 -#define PVR_STB03XXX 0x40310000 +#define PVR_STB03XXX 0x40310000 +#define PVR_NP405H 0x41410000 +#define PVR_NP405L 0x41610000 #define PVR_601 0x00010000 #define PVR_602 0x00050000 #define PVR_603 0x00030000 @@ -462,6 +526,7 @@ #define PVR_750P PVR_740P #define PVR_7400 0x000C0000 #define PVR_7410 0x800C0000 +#define PVR_7450 0x80000000 /* * For the 8xx processors, all of them report the same PVR family for * the PowerPC core. The various versions of these processors must be @@ -473,6 +538,7 @@ #define PVR_850 PVR_821 #define PVR_860 PVR_821 #define PVR_8240 0x00810100 +#define PVR_8245 0x80811014 #define PVR_8260 PVR_8240 /* We only need to define a new _MACH_xxx for machines which are part of @@ -494,6 +560,7 @@ #define _CHRP_IBM 0x05 /* IBM chrp, the longtrail and longtrail 2 */ #define _GLOBAL(n)\ + .stabs __stringify(n:F-1),N_FUN,0,0,n;\ .globl n;\ n: @@ -502,11 +569,6 @@ #define stringify(s) tostring(s) #define tostring(s) #s -#define mfdcr(rn) ({unsigned int rval; \ - asm volatile("mfdcr %0," stringify(rn) \ - : "=r" (rval)); rval;}) -#define mtdcr(rn, v) asm volatile("mtdcr " stringify(rn) ",%0" : : "r" (v)) - #define mfmsr() ({unsigned int rval; \ asm volatile("mfmsr %0" : "=r" (rval)); rval;}) #define mtmsr(v) asm volatile("mtmsr %0" : : "r" (v)) @@ -516,6 +578,35 @@ : "=r" (rval)); rval;}) #define mtspr(rn, v) asm volatile("mtspr " stringify(rn) ",%0" : : "r" (v)) +#define mfsrin(v) ({unsigned int rval; \ + asm volatile("mfsrin %0,%1" : "=r" (rval) : "r" (v)); \ + rval;}) + +#define proc_trap() asm volatile("trap") + +#ifdef CONFIG_PPC_ISERIES +/* Macros for adjusting thread priority (hardware multi-threading) */ +#define HMT_PRIO_LOW "or 1,1,1\n" /* low prio, used for spin loops */ +#define HMT_PRIO_MED "or 2,2,2\n" /* medium prio, for normal code */ +#define HMT_PRIO_HIGH "or 3,3,3\n" /* high priority */ + +#define HMT_low() asm volatile("or 1,1,1") +#define HMT_medium() asm volatile("or 2,2,2") +#define HMT_high() asm volatile("or 3,3,3") + +/* iSeries CTRL register (for runlatch) */ + +#define CTRLT 0x098 +#define CTRLF 0x088 +#define RUNLATCH 0x0001 + +#else /* !CONFIG_PPC_ISERIES */ +#define HMT_PRIO_LOW +#define HMT_PRIO_MED +#define HMT_PRIO_HIGH + +#endif /* CONFIG_PPC_ISERIES */ + /* Segment Registers */ #define SR0 0 @@ -580,7 +671,11 @@ * as soon as I get around to remapping the io areas with the BATs * to match the mac we can raise this. -- Cort */ +#ifdef CONFIG_TASK_SIZE_BOOL +#define TASK_SIZE CONFIG_TASK_SIZE +#else #define TASK_SIZE (0x80000000UL) +#endif /* This decides where the kernel will search for a free chunk of vm * space during mmap's. @@ -593,10 +688,10 @@ struct thread_struct { unsigned long ksp; /* Kernel stack pointer */ - unsigned long wchan; /* Event task is sleeping on */ struct pt_regs *regs; /* Pointer to saved register state */ mm_segment_t fs; /* for get_fs() validation */ void *pgdir; /* root of page-table tree */ + int fpexc_mode; /* floating-point exception mode */ signed long last_syscall; double fpr[32]; /* Complete floating point set */ unsigned long fpscr_pad; /* fpr ... fpscr must be contiguous */ @@ -611,22 +706,16 @@ #define INIT_SP (sizeof(init_stack) + (unsigned long) &init_stack) #define INIT_THREAD { \ - INIT_SP, /* ksp */ \ - 0, /* wchan */ \ - 0, /* regs */ \ - KERNEL_DS, /*fs*/ \ - swapper_pg_dir, /* pgdir */ \ - 0, /* last_syscall */ \ - {0}, 0, 0 \ + ksp: INIT_SP, \ + fs: KERNEL_DS, \ + pgdir: swapper_pg_dir, \ } /* * Return saved PC of a blocked thread. For now, this is the "user" PC */ -static inline unsigned long thread_saved_pc(struct thread_struct *t) -{ - return (t->regs) ? t->regs->nip : 0; -} +#define thread_saved_pc(tsk) \ + ((tsk)->thread.regs? (tsk)->thread.regs->nip: 0) #define copy_segments(tsk, mm) do { } while (0) #define release_segments(mm) do { } while (0) @@ -636,25 +725,31 @@ #define KSTK_EIP(tsk) ((tsk)->thread.regs? (tsk)->thread.regs->nip: 0) #define KSTK_ESP(tsk) ((tsk)->thread.regs? (tsk)->thread.regs->gpr[1]: 0) -/* - * NOTE! The task struct and the stack go together - */ -#define THREAD_SIZE (2*PAGE_SIZE) -#define alloc_task_struct() \ - ((struct task_struct *) __get_free_pages(GFP_KERNEL,1)) -#define free_task_struct(p) free_pages((unsigned long)(p),1) -#define get_task_struct(tsk) atomic_inc(&virt_to_page(tsk)->count) +/* Get/set floating-point exception mode */ +#define GET_FP_EXC_MODE(tsk) __unpack_fe01((tsk)->thread.fpexc_mode) +#define SET_FP_EXC_MODE(tsk, val) set_fpexc_mode((tsk), (val)) + +extern int set_fpexc_mode(struct task_struct *tsk, unsigned int val); + +static inline unsigned int __unpack_fe01(unsigned int msr_bits) +{ + return ((msr_bits & MSR_FE0) >> 10) | ((msr_bits & MSR_FE1) >> 8); +} + +static inline unsigned int __pack_fe01(unsigned int fpmode) +{ + return ((fpmode << 10) & MSR_FE0) | ((fpmode << 8) & MSR_FE1); +} /* in process.c - for early bootup debug -- Cort */ int ll_printk(const char *, ...); void ll_puts(const char *); -#define init_task (init_task_union.task) -#define init_stack (init_task_union.stack) - /* In misc.c */ void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val); +#define have_of (_machine == _MACH_chrp || _machine == _MACH_Pmac) + #define cpu_relax() do { } while (0) /* @@ -677,8 +772,6 @@ #define spin_lock_prefetch(x) prefetchw(x) #endif /* !__ASSEMBLY__ */ - -#define have_of (_machine == _MACH_chrp || _machine == _MACH_Pmac) #endif /* __ASM_PPC_PROCESSOR_H */ #endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/prom.h b/include/asm-ppc/prom.h --- a/include/asm-ppc/prom.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/prom.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.prom.h 1.19 08/17/01 15:23:17 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * Definitions for talking to the Open Firmware PROM on @@ -86,6 +86,10 @@ extern int prom_n_addr_cells(struct device_node* np); extern int prom_n_size_cells(struct device_node* np); +extern struct resource* +request_OF_resource(struct device_node* node, int index, const char* name_postfix); +extern int release_OF_resource(struct device_node* node, int index); + extern void print_properties(struct device_node *node); extern int call_rtas(const char *service, int nargs, int nret, unsigned long *outputs, ...); @@ -103,10 +107,11 @@ * pointer values. See arch/ppc/kernel/prom.c for how these are used. */ extern unsigned long reloc_offset(void); +extern unsigned long add_reloc_offset(unsigned long); +extern unsigned long sub_reloc_offset(unsigned long); -#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) -#define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) - offset)) -#define RELOC(x) (*PTRRELOC(&(x))) +#define PTRRELOC(x) ((typeof(x))add_reloc_offset((unsigned long)(x))) +#define PTRUNRELOC(x) ((typeof(x))sub_reloc_offset((unsigned long)(x))) #endif /* _PPC_PROM_H */ #endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/ptrace.h b/include/asm-ppc/ptrace.h --- a/include/asm-ppc/ptrace.h Tue Feb 19 18:08:56 2002 +++ b/include/asm-ppc/ptrace.h Tue Feb 19 18:08:56 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ptrace.h 1.5 05/17/01 18:14:25 cort + * BK Id: %F% %I% %G% %U% %#% */ #ifndef _PPC_PTRACE_H #define _PPC_PTRACE_H @@ -39,6 +39,9 @@ }; #endif +/* iSeries uses mq field for soft enable flag */ +#define softEnable mq + #ifdef __KERNEL__ #define STACK_FRAME_OVERHEAD 16 /* size of minimum stack frame */ @@ -103,5 +106,8 @@ #define PT_FPR31 (PT_FPR0 + 2*31) #define PT_FPSCR (PT_FPR0 + 2*32 + 1) -#endif +/* Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go */ +#define PTRACE_GETVRREGS 18 +#define PTRACE_SETVRREGS 19 +#endif diff -Nru a/include/asm-ppc/rpxclassic.h b/include/asm-ppc/rpxclassic.h --- a/include/asm-ppc/rpxclassic.h Tue Feb 19 18:08:58 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,104 +0,0 @@ -/* - * BK Id: SCCS/s.rpxclassic.h 1.11 08/17/01 15:23:17 paulus - */ - -/* - * A collection of structures, addresses, and values associated with - * the RPCG RPX-Classic board. Copied from the RPX-Lite stuff. - * - * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) - */ -#ifdef __KERNEL__ -#ifndef __MACH_RPX_DEFS -#define __MACH_RPX_DEFS - -#include - -#ifndef __ASSEMBLY__ -/* A Board Information structure that is given to a program when - * prom starts it up. - */ -typedef struct bd_info { - unsigned int bi_memstart; /* Memory start address */ - unsigned int bi_memsize; /* Memory (end) size in bytes */ - unsigned int bi_intfreq; /* Internal Freq, in Hz */ - unsigned int bi_busfreq; /* Bus Freq, in Hz */ - unsigned char bi_enetaddr[6]; - unsigned int bi_baudrate; -} bd_t; - -extern bd_t m8xx_board_info; - -/* Memory map is configured by the PROM startup. - * We just map a few things we need. The CSR is actually 4 byte-wide - * registers that can be accessed as 8-, 16-, or 32-bit values. - */ -#define PCI_ISA_IO_ADDR ((unsigned)0x80000000) -#define PCI_ISA_IO_SIZE ((uint)(512 * 1024 * 1024)) -#define PCI_ISA_MEM_ADDR ((unsigned)0xc0000000) -#define PCI_ISA_MEM_SIZE ((uint)(512 * 1024 * 1024)) -#define RPX_CSR_ADDR ((uint)0xfa400000) -#define RPX_CSR_SIZE ((uint)(4 * 1024)) -#define IMAP_ADDR ((uint)0xfa200000) -#define IMAP_SIZE ((uint)(64 * 1024)) -#define PCI_CSR_ADDR ((uint)0x80000000) -#define PCI_CSR_SIZE ((uint)(64 * 1024)) -#define PCMCIA_MEM_ADDR ((uint)0xe0000000) -#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) -#define PCMCIA_IO_ADDR ((uint)0xe4000000) -#define PCMCIA_IO_SIZE ((uint)(4 * 1024)) -#define PCMCIA_ATTRB_ADDR ((uint)0xe8000000) -#define PCMCIA_ATTRB_SIZE ((uint)(4 * 1024)) - -/* Things of interest in the CSR. -*/ -#define BCSR0_ETHEN ((uint)0x80000000) -#define BCSR0_ETHLPBK ((uint)0x40000000) -#define BCSR0_COLTESTDIS ((uint)0x20000000) -#define BCSR0_FULLDPLXDIS ((uint)0x10000000) -#define BCSR0_ENFLSHSEL ((uint)0x04000000) -#define BCSR0_FLASH_SEL ((uint)0x02000000) -#define BCSR0_ENMONXCVR ((uint)0x01000000) - -#define BCSR0_PCMCIAVOLT ((uint)0x000f0000) /* CLLF */ -#define BCSR0_PCMCIA3VOLT ((uint)0x000a0000) /* CLLF */ -#define BCSR0_PCMCIA5VOLT ((uint)0x00060000) /* CLLF */ - -#define BCSR1_IPB5SEL ((uint)0x00100000) -#define BCSR1_PCVCTL4 ((uint)0x00080000) -#define BCSR1_PCVCTL5 ((uint)0x00040000) -#define BCSR1_PCVCTL6 ((uint)0x00020000) -#define BCSR1_PCVCTL7 ((uint)0x00010000) - -#define BCSR2_EN232XCVR ((uint)0x00008000) -#define BCSR2_QSPACESEL ((uint)0x00004000) -#define BCSR2_FETHLEDMODE ((uint)0x00000800) /* CLLF */ - -#if defined(CONFIG_HTDMSOUND) -#include -#endif - -/* define IO_BASE for pcmcia, CLLF only */ -#if !defined(CONFIG_PCI) -#define _IO_BASE 0x80000000 -#define _IO_BASE_SIZE 0x1000 - -/* for pcmcia sandisk */ -#ifdef CONFIG_IDE -#define MAX_HWIFS 1 -#define ide_request_irq(irq,hand,flg,dev,id) request_8xxirq((irq),(hand),(flg),(dev),(id)) -#endif -#endif - -/* Interrupt level assignments. -*/ -#define FEC_INTERRUPT SIU_LEVEL1 /* FEC interrupt */ - -#endif /* !__ASSEMBLY__ */ - -/* We don't use the 8259. -*/ -#define NR_8259_INTS 0 - -#endif -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/rpxhiox.h b/include/asm-ppc/rpxhiox.h --- a/include/asm-ppc/rpxhiox.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,45 +0,0 @@ -/* - * BK Id: SCCS/s.rpxhiox.h 1.3 05/17/01 18:14:25 cort - */ - -/* - * The Embedded Planet HIOX expansion card definitions. - * There were a few different versions of these cards, but only - * the one that escaped real production is defined here. - * - * Copyright (c) 2000 Dan Malek (dmalek@jlc.net) - */ -#ifndef __MACH_RPX_HIOX_DEFS -#define __MACH_RPX_HIOX_DEFS - -#define HIOX_CSR_ADDR ((uint)0xfac00000) -#define HIOX_CSR_SIZE ((uint)(4 * 1024)) -#define HIOX_CSR0_ADDR HIOX_CSR_ADDR -#define HIOX_CSR4_ADDR ((uint)0xfac00004) - -#define HIOX_CSR0_DEFAULT ((uint)0x380f3c00) -#define HIOX_CSR0_ENSCC2 ((uint)0x80000000) -#define HIOX_CSR0_ENSMC2 ((uint)0x04000000) -#define HIOX_CSR0_ENVDOCLK ((uint)0x02000000) -#define HIOX_CSR0_VDORST_HL ((uint)0x01000000) -#define HIOX_CSR0_RS232SEL ((uint)0x0000c000) -#define HIOX_CSR0_SCC3SEL ((uint)0x0000c000) -#define HIOX_CSR0_SMC1SEL ((uint)0x00008000) -#define HIOX_CSR0_SCC1SEL ((uint)0x00004000) -#define HIOX_CSR0_ENTOUCH ((uint)0x00000080) -#define HIOX_CSR0_PDOWN100 ((uint)0x00000060) -#define HIOX_CSR0_PDOWN10 ((uint)0x00000040) -#define HIOX_CSR0_PDOWN1 ((uint)0x00000020) -#define HIOX_CSR0_TSELSPI ((uint)0x00000010) -#define HIOX_CSR0_TIRQSTAT ((uint)0x00000008) -#define HIOX_CSR4_DEFAULT ((uint)0x00000000) -#define HIOX_CSR4_ENTIRQ2 ((uint)0x20000000) -#define HIOX_CSR4_ENTIRQ3 ((uint)0x10000000) -#define HIOX_CSR4_ENAUDIO ((uint)0x00000080) -#define HIOX_CSR4_RSTAUDIO ((uint)0x00000040) /* 0 == reset */ -#define HIOX_CSR4_AUDCLKHI ((uint)0x00000020) -#define HIOX_CSR4_AUDSPISEL ((uint)0x00000010) -#define HIOX_CSR4_AUDIRQSTAT ((uint)0x00000008) -#define HIOX_CSR4_AUDCLKSEL ((uint)0x00000007) - -#endif diff -Nru a/include/asm-ppc/rpxlite.h b/include/asm-ppc/rpxlite.h --- a/include/asm-ppc/rpxlite.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,84 +0,0 @@ -/* - * BK Id: SCCS/s.rpxlite.h 1.11 08/17/01 15:23:17 paulus - */ - -/* - * A collection of structures, addresses, and values associated with - * the RPCG RPX-Lite board. Copied from the MBX stuff. - * - * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) - */ -#ifdef __KERNEL__ -#ifndef __MACH_RPX_DEFS -#define __MACH_RPX_DEFS - -#include - -#ifndef __ASSEMBLY__ -/* A Board Information structure that is given to a program when - * prom starts it up. - */ -typedef struct bd_info { - unsigned int bi_memstart; /* Memory start address */ - unsigned int bi_memsize; /* Memory (end) size in bytes */ - unsigned int bi_intfreq; /* Internal Freq, in Hz */ - unsigned int bi_busfreq; /* Bus Freq, in Hz */ - unsigned char bi_enetaddr[6]; - unsigned int bi_baudrate; -} bd_t; - -extern bd_t m8xx_board_info; - -/* Memory map is configured by the PROM startup. - * We just map a few things we need. The CSR is actually 4 byte-wide - * registers that can be accessed as 8-, 16-, or 32-bit values. - */ -#define RPX_CSR_ADDR ((uint)0xfa400000) -#define RPX_CSR_SIZE ((uint)(4 * 1024)) -#define IMAP_ADDR ((uint)0xfa200000) -#define IMAP_SIZE ((uint)(64 * 1024)) -#define PCMCIA_MEM_ADDR ((uint)0x04000000) -#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) -#define PCMCIA_IO_ADDR ((uint)0x04400000) -#define PCMCIA_IO_SIZE ((uint)(4 * 1024)) - -/* Things of interest in the CSR. -*/ -#define BCSR0_ETHEN ((uint)0x80000000) -#define BCSR0_ETHLPBK ((uint)0x40000000) -#define BCSR0_COLTESTDIS ((uint)0x20000000) -#define BCSR0_FULLDPLXDIS ((uint)0x10000000) -#define BCSR0_LEDOFF ((uint)0x08000000) -#define BCSR0_USBDISABLE ((uint)0x04000000) -#define BCSR0_USBHISPEED ((uint)0x02000000) -#define BCSR0_USBPWREN ((uint)0x01000000) -#define BCSR0_PCMCIAVOLT ((uint)0x000f0000) -#define BCSR0_PCMCIA3VOLT ((uint)0x000a0000) -#define BCSR0_PCMCIA5VOLT ((uint)0x00060000) - -#define BCSR1_IPB5SEL ((uint)0x00100000) -#define BCSR1_PCVCTL4 ((uint)0x00080000) -#define BCSR1_PCVCTL5 ((uint)0x00040000) -#define BCSR1_PCVCTL6 ((uint)0x00020000) -#define BCSR1_PCVCTL7 ((uint)0x00010000) - -#if defined(CONFIG_HTDMSOUND) -#include -#endif -#endif /* !__ASSEMBLY__ */ - -/* define IO_BASE for pcmcia */ -#define _IO_BASE 0x80000000 -#define _IO_BASE_SIZE 0x1000 - -#ifdef CONFIG_IDE -#define MAX_HWIFS 1 -#define ide_request_irq(irq,hand,flg,dev,id) request_8xxirq((irq),(hand),(flg),(dev),(id)) -#endif - -/* We don't use the 8259. -*/ -#define NR_8259_INTS 0 - -#endif -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/scatterlist.h b/include/asm-ppc/scatterlist.h --- a/include/asm-ppc/scatterlist.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-ppc/scatterlist.h Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.scatterlist.h 1.9 10/15/01 22:51:33 paulus + * BK Id: %F% %I% %G% %U% %#% */ #ifdef __KERNEL__ #ifndef _PPC_SCATTERLIST_H @@ -8,11 +8,10 @@ #include struct scatterlist { - struct page * page; - unsigned int offset; - - dma_addr_t dma_address; /* phys/bus dma address */ - unsigned int length; /* length */ + struct page *page; + unsigned int offset; + dma_addr_t dma_address; + unsigned int length; }; /* diff -Nru a/include/asm-ppc/serial.h b/include/asm-ppc/serial.h --- a/include/asm-ppc/serial.h Tue Feb 19 18:09:00 2002 +++ b/include/asm-ppc/serial.h Tue Feb 19 18:09:00 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.serial.h 1.15 10/23/01 08:09:35 trini + * BK Id: %F% %I% %G% %U% %#% */ /* * include/asm-ppc/serial.h @@ -11,129 +11,42 @@ #include -#ifdef CONFIG_GEMINI -#include +#if defined(CONFIG_EV64260) +#include +#elif defined(CONFIG_GEMINI) +#include +#elif defined(CONFIG_POWERPMC250) +#include +#elif defined(CONFIG_LOPEC) +#include +#elif defined(CONFIG_MCPN765) +#include +#elif defined(CONFIG_MVME5100) +#include +#elif defined(CONFIG_PRPMC750) +#include +#elif defined(CONFIG_PRPMC800) +#include +#elif defined(CONFIG_SANDPOINT) +#include +#elif defined(CONFIG_SPRUCE) +#include +#elif defined(CONFIG_ZX4500) +#include #elif defined(CONFIG_4xx) -#include +#include #else /* - * This assumes you have a 1.8432 MHz clock for your UART. - * - * It'd be nice if someone built a serial card with a 24.576 MHz - * clock, since the 16550A is capable of handling a top speed of 1.5 - * megabits/second; but this requires the faster clock. + * XXX Assume for now it has PC-style ISA serial ports. + * This is true for PReP and CHRP at least. */ -#define BASE_BAUD ( 1843200 / 16 ) +#include +#include -#ifdef CONFIG_SERIAL_MANY_PORTS -#define RS_TABLE_SIZE 64 -#else -#define RS_TABLE_SIZE 4 -#endif - -/* Standard COM flags (except for COM4, because of the 8514 problem) */ -#ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) -#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) -#else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) -#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF -#endif - -#ifdef CONFIG_SERIAL_MANY_PORTS -#define FOURPORT_FLAGS ASYNC_FOURPORT -#define ACCENT_FLAGS 0 -#define BOCA_FLAGS 0 -#define HUB6_FLAGS 0 -#endif - -/* - * The following define the access methods for the HUB6 card. All - * access is through two ports for all 24 possible chips. The card is - * selected through the high 2 bits, the port on that card with the - * "middle" 3 bits, and the register on that port with the bottom - * 3 bits. - * - * While the access port and interrupt is configurable, the default - * port locations are 0x302 for the port control register, and 0x303 - * for the data read/write register. Normally, the interrupt is at irq3 - * but can be anything from 3 to 7 inclusive. Note that using 3 will - * require disabling com2. - */ - -#define C_P(card,port) (((card)<<6|(port)<<3) + 1) - -#define STD_SERIAL_PORT_DEFNS \ - /* UART CLK PORT IRQ FLAGS */ \ - { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \ - { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ \ - { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ \ - { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ - - -#ifdef CONFIG_SERIAL_MANY_PORTS -#define EXTRA_SERIAL_PORT_DEFNS \ - { 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */ \ - { 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */ \ - { 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */ \ - { 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS }, /* ttyS7 */ \ - { 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS }, /* ttyS8 */ \ - { 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS }, /* ttyS9 */ \ - { 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */ \ - { 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */ \ - { 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */ \ - { 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */ \ - { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS14 (spare) */ \ - { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS15 (spare) */ \ - { 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */ \ - { 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */ \ - { 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS }, /* ttyS18 */ \ - { 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS }, /* ttyS19 */ \ - { 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS }, /* ttyS20 */ \ - { 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS }, /* ttyS21 */ \ - { 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS }, /* ttyS22 */ \ - { 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS }, /* ttyS23 */ \ - { 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS }, /* ttyS24 */ \ - { 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS }, /* ttyS25 */ \ - { 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS }, /* ttyS26 */ \ - { 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS }, /* ttyS27 */ \ - { 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS }, /* ttyS28 */ \ - { 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */ \ - { 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */ \ - { 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */ -#else -#define EXTRA_SERIAL_PORT_DEFNS -#endif - -/* You can have up to four HUB6's in the system, but I've only - * included two cards here for a total of twelve ports. - */ -#if (defined(CONFIG_HUB6) && defined(CONFIG_SERIAL_MANY_PORTS)) -#define HUB6_SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS32 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS33 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS34 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS35 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS36 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS37 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS38 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS39 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS40 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS41 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */ \ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */ -#else -#define HUB6_SERIAL_PORT_DFNS +#if defined(CONFIG_MAC_SERIAL) +#define SERIAL_DEV_OFFSET ((_machine == _MACH_prep || _machine == _MACH_chrp) ? 0 : 2) #endif - -#define MCA_SERIAL_PORT_DFNS - -#define SERIAL_PORT_DFNS \ - STD_SERIAL_PORT_DEFNS \ - EXTRA_SERIAL_PORT_DEFNS \ - HUB6_SERIAL_PORT_DFNS \ - MCA_SERIAL_PORT_DFNS #endif /* !CONFIG_GEMINI and others */ #endif /* __ASM_SERIAL_H__ */ diff -Nru a/include/asm-ppc/smp.h b/include/asm-ppc/smp.h --- a/include/asm-ppc/smp.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/smp.h Tue Feb 19 18:08:58 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.smp.h 1.12 08/16/01 07:49:31 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* smp.h: PPC specific SMP stuff. * @@ -28,10 +28,10 @@ unsigned long pgtable_cache_sz; }; -extern struct cpuinfo_PPC cpu_data[NR_CPUS]; +extern struct cpuinfo_PPC cpu_data[]; extern unsigned long cpu_online_map; -extern unsigned long smp_proc_in_lock[NR_CPUS]; -extern volatile unsigned long cpu_callin_map[NR_CPUS]; +extern unsigned long smp_proc_in_lock[]; +extern volatile unsigned long cpu_callin_map[]; extern int smp_tb_synchronized; extern void smp_store_cpu_info(int id); @@ -48,9 +48,9 @@ #define cpu_logical_map(cpu) (cpu) #define cpu_number_map(x) (x) -#define smp_processor_id() (current->processor) +#define smp_processor_id() (current_thread_info()->cpu) -extern int smp_hw_index[NR_CPUS]; +extern int smp_hw_index[]; #define hard_smp_processor_id() (smp_hw_index[smp_processor_id()]) struct klock_info_struct { diff -Nru a/include/asm-ppc/spd8xx.h b/include/asm-ppc/spd8xx.h --- a/include/asm-ppc/spd8xx.h Tue Feb 19 18:08:57 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,69 +0,0 @@ -/* - * BK Id: SCCS/s.spd8xx.h 1.8 10/27/01 13:39:41 trini - */ -/* - * Speech Design SPD8xxTS board specific definitions - * - * Copyright (c) 2000,2001 Wolfgang Denk (wd@denx.de) - */ - -#ifdef __KERNEL__ -#ifndef __ASM_SPD8XX_H__ -#define __ASM_SPD8XX_H__ - -#include - -#include - -#define SPD_IMMR_BASE 0xFFF00000 /* phys. addr of IMMR */ -#define SPD_IMAP_SIZE (64 * 1024) /* size of mapped area */ - -#define IMAP_ADDR SPD_IMMR_BASE /* physical base address of IMMR area */ -#define IMAP_SIZE SPD_IMAP_SIZE /* mapped size of IMMR area */ - -#define PCMCIA_MEM_ADDR ((uint)0xFE100000) -#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) - -#define IDE0_INTERRUPT 10 /* = IRQ5 */ -#define IDE1_INTERRUPT 12 /* = IRQ6 */ -#define CPM_INTERRUPT 13 /* = SIU_LEVEL6 (was: SIU_LEVEL2) */ - -/* override the default number of IDE hardware interfaces */ -#define MAX_HWIFS 2 - -/* - * Definitions for IDE0 Interface - */ -#define IDE0_BASE_OFFSET 0x0000 /* Offset in PCMCIA memory */ -#define IDE0_DATA_REG_OFFSET 0x0000 -#define IDE0_ERROR_REG_OFFSET 0x0081 -#define IDE0_NSECTOR_REG_OFFSET 0x0082 -#define IDE0_SECTOR_REG_OFFSET 0x0083 -#define IDE0_LCYL_REG_OFFSET 0x0084 -#define IDE0_HCYL_REG_OFFSET 0x0085 -#define IDE0_SELECT_REG_OFFSET 0x0086 -#define IDE0_STATUS_REG_OFFSET 0x0087 -#define IDE0_CONTROL_REG_OFFSET 0x0106 -#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ - -/* - * Definitions for IDE1 Interface - */ -#define IDE1_BASE_OFFSET 0x0C00 /* Offset in PCMCIA memory */ -#define IDE1_DATA_REG_OFFSET 0x0000 -#define IDE1_ERROR_REG_OFFSET 0x0081 -#define IDE1_NSECTOR_REG_OFFSET 0x0082 -#define IDE1_SECTOR_REG_OFFSET 0x0083 -#define IDE1_LCYL_REG_OFFSET 0x0084 -#define IDE1_HCYL_REG_OFFSET 0x0085 -#define IDE1_SELECT_REG_OFFSET 0x0086 -#define IDE1_STATUS_REG_OFFSET 0x0087 -#define IDE1_CONTROL_REG_OFFSET 0x0106 -#define IDE1_IRQ_REG_OFFSET 0x000A /* not used */ - -/* We don't use the 8259. -*/ -#define NR_8259_INTS 0 - -#endif /* __ASM_SPD8XX_H__ */ -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc/spinlock.h b/include/asm-ppc/spinlock.h --- a/include/asm-ppc/spinlock.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/spinlock.h Tue Feb 19 18:08:59 2002 @@ -1,10 +1,11 @@ /* - * BK Id: SCCS/s.spinlock.h 1.9 08/21/01 16:07:48 trini + * BK Id: %F% %I% %G% %U% %#% */ #ifndef __ASM_SPINLOCK_H #define __ASM_SPINLOCK_H #include +#include #undef SPINLOCK_DEBUG @@ -35,19 +36,22 @@ #ifndef SPINLOCK_DEBUG -static inline void spin_lock(spinlock_t *lock) +static inline void _raw_spin_lock(spinlock_t *lock) { unsigned long tmp; __asm__ __volatile__( - "b 1f # spin_lock\n\ -2: lwzx %0,0,%1\n\ + "b 1f # spin_lock\n\ +2:" HMT_PRIO_LOW +" lwzx %0,0,%1\n\ cmpwi 0,%0,0\n\ - bne+ 2b\n\ -1: lwarx %0,0,%1\n\ + bne+ 2b\n" + HMT_PRIO_MED +"1: lwarx %0,0,%1\n\ cmpwi 0,%0,0\n\ - bne- 2b\n\ - stwcx. %2,0,%1\n\ + bne- 2b\n" + PPC405_ERR77(0,%1) +" stwcx. %2,0,%1\n\ bne- 2b\n\ isync" : "=&r"(tmp) @@ -55,24 +59,21 @@ : "cr0", "memory"); } -static inline void spin_unlock(spinlock_t *lock) +static inline void _raw_spin_unlock(spinlock_t *lock) { __asm__ __volatile__("eieio # spin_unlock": : :"memory"); lock->lock = 0; } -#define spin_trylock(lock) (!test_and_set_bit(0,(lock))) +#define _raw_spin_trylock(lock) (!test_and_set_bit(0,(lock))) #else -extern void _spin_lock(spinlock_t *lock); -extern void _spin_unlock(spinlock_t *lock); -extern int spin_trylock(spinlock_t *lock); +extern void _raw_spin_lock(spinlock_t *lock); +extern void _raw_spin_unlock(spinlock_t *lock); +extern int _raw_spin_trylock(spinlock_t *lock); extern unsigned long __spin_trylock(volatile unsigned long *lock); -#define spin_lock(lp) _spin_lock(lp) -#define spin_unlock(lp) _spin_unlock(lp) - #endif /* @@ -103,62 +104,69 @@ #ifndef SPINLOCK_DEBUG -static __inline__ void read_lock(rwlock_t *rw) +static __inline__ void _raw_read_lock(rwlock_t *rw) { unsigned int tmp; __asm__ __volatile__( - "b 2f # read_lock\n\ -1: lwzx %0,0,%1\n\ - cmpwi 0,%0,0\n\ - blt+ 1b\n\ -2: lwarx %0,0,%1\n\ - addic. %0,%0,1\n\ - ble- 1b\n\ - stwcx. %0,0,%1\n\ - bne- 2b\n\ + "b 2f # read_lock\n\ +1:" HMT_PRIO_LOW +" lwzx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + blt+ 1b\n" + HMT_PRIO_MED +"2: lwarx %0,0,%1\n\ + addic. %0,%0,1\n\ + ble- 1b\n" + PPC405_ERR77(0,%1) +" stwcx. %0,0,%1\n\ + bne- 2b\n\ isync" : "=&r"(tmp) : "r"(&rw->lock) : "cr0", "memory"); } -static __inline__ void read_unlock(rwlock_t *rw) +static __inline__ void _raw_read_unlock(rwlock_t *rw) { unsigned int tmp; __asm__ __volatile__( - "eieio # read_unlock\n\ -1: lwarx %0,0,%1\n\ - addic %0,%0,-1\n\ - stwcx. %0,0,%1\n\ - bne- 1b" + "eieio # read_unlock\n\ +1: lwarx %0,0,%1\n\ + addic %0,%0,-1\n" + PPC405_ERR77(0,%1) +" stwcx. %0,0,%1\n\ + bne- 1b" : "=&r"(tmp) : "r"(&rw->lock) : "cr0", "memory"); } -static __inline__ void write_lock(rwlock_t *rw) +static __inline__ void _raw_write_lock(rwlock_t *rw) { unsigned int tmp; __asm__ __volatile__( - "b 2f # write_lock\n\ -1: lwzx %0,0,%1\n\ - cmpwi 0,%0,0\n\ - bne+ 1b\n\ -2: lwarx %0,0,%1\n\ - cmpwi 0,%0,0\n\ - bne- 1b\n\ - stwcx. %2,0,%1\n\ - bne- 2b\n\ + "b 2f # write_lock\n\ +1:" HMT_PRIO_LOW +" lwzx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne+ 1b\n" + HMT_PRIO_MED +"2: lwarx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne- 1b\n" + PPC405_ERR77(0,%1) +" stwcx. %2,0,%1\n\ + bne- 2b\n\ isync" : "=&r"(tmp) : "r"(&rw->lock), "r"(-1) : "cr0", "memory"); } -static __inline__ void write_unlock(rwlock_t *rw) +static __inline__ void _raw_write_unlock(rwlock_t *rw) { __asm__ __volatile__("eieio # write_unlock": : :"memory"); rw->lock = 0; @@ -166,15 +174,10 @@ #else -extern void _read_lock(rwlock_t *rw); -extern void _read_unlock(rwlock_t *rw); -extern void _write_lock(rwlock_t *rw); -extern void _write_unlock(rwlock_t *rw); - -#define read_lock(rw) _read_lock(rw) -#define write_lock(rw) _write_lock(rw) -#define write_unlock(rw) _write_unlock(rw) -#define read_unlock(rw) _read_unlock(rw) +extern void _raw_read_lock(rwlock_t *rw); +extern void _raw_read_unlock(rwlock_t *rw); +extern void _raw_write_lock(rwlock_t *rw); +extern void _raw_write_unlock(rwlock_t *rw); #endif diff -Nru a/include/asm-ppc/system.h b/include/asm-ppc/system.h --- a/include/asm-ppc/system.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-ppc/system.h Tue Feb 19 18:08:59 2002 @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.system.h 1.14 08/20/01 14:34:41 paulus + * BK Id: %F% %I% %G% %U% %#% */ /* * Copyright (C) 1999 Cort Dougan @@ -59,7 +59,7 @@ extern long _get_L2CR(void); extern void _set_L2CR(unsigned long); #else -#define _get_L2CR() 0 +#define _get_L2CR() 0L #define _set_L2CR(val) do { } while(0) #endif extern void via_cuda_init(void); @@ -81,9 +81,7 @@ struct task_struct; #define prepare_to_switch() do { } while(0) -#define switch_to(prev,next,last) _switch_to((prev),(next),&(last)) -extern void _switch_to(struct task_struct *, struct task_struct *, - struct task_struct **); +extern void switch_to(struct task_struct *, struct task_struct *); struct thread_struct; extern struct task_struct *_switch(struct thread_struct *prev, @@ -120,18 +118,15 @@ #define local_irq_save(flags) __save_and_cli(flags) #define local_irq_restore(flags) __restore_flags(flags) -#endif /* __KERNEL__ */ - -#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) - static __inline__ unsigned long xchg_u32(volatile void *p, unsigned long val) { unsigned long prev; __asm__ __volatile__ ("\n\ -1: lwarx %0,0,%2 \n\ - stwcx. %3,0,%2 \n\ +1: lwarx %0,0,%2 \n" + PPC405_ERR77(0,%2) +" stwcx. %3,0,%2 \n\ bne- 1b" : "=&r" (prev), "=m" (*(volatile unsigned long *)p) : "r" (p), "r" (val), "m" (*(volatile unsigned long *)p) @@ -181,8 +176,9 @@ __asm__ __volatile__ ("\n\ 1: lwarx %0,0,%2 \n\ cmpw 0,%0,%3 \n\ - bne 2f \n\ - stwcx. %4,0,%2 \n\ + bne 2f \n" + PPC405_ERR77(0,%2) +" stwcx. %4,0,%2 \n\ bne- 1b\n" #ifdef CONFIG_SMP " sync\n" @@ -222,4 +218,5 @@ (unsigned long)_n_, sizeof(*(ptr))); \ }) +#endif /* __KERNEL__ */ #endif /* __PPC_SYSTEM_H */ diff -Nru a/include/asm-ppc/thread_info.h b/include/asm-ppc/thread_info.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/thread_info.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,89 @@ +/* thread_info.h: PPC low-level thread information + * adapted from the i386 version by Paul Mackerras + * + * Copyright (C) 2002 David Howells (dhowells@redhat.com) + * - Incorporating suggestions made by Linus Torvalds and Dave Miller + */ + +#ifndef _ASM_THREAD_INFO_H +#define _ASM_THREAD_INFO_H + +#ifdef __KERNEL__ + +#include + +#ifndef __ASSEMBLY__ +/* + * low level task data. + * If you change this, change the TI_* offsets below to match. + */ +struct thread_info { + struct task_struct *task; /* main task structure */ + struct exec_domain *exec_domain; /* execution domain */ + unsigned long flags; /* low level flags */ + int cpu; /* cpu we're on */ +}; + +/* + * macros/functions for gaining access to the thread information structure + */ +#define INIT_THREAD_INFO(tsk) \ +{ \ + task: &tsk, \ + exec_domain: &default_exec_domain, \ + flags: 0, \ + cpu: 0, \ +} + +#define init_thread_info (init_thread_union.thread_info) +#define init_stack (init_thread_union.stack) + +/* how to get the thread information struct from C */ +static inline struct thread_info *current_thread_info(void) +{ + struct thread_info *ti; + __asm__("rlwinm %0,1,0,0,18" : "=r"(ti)); + return ti; +} + +/* thread information allocation */ +#define alloc_thread_info() ((struct thread_info *) \ + __get_free_pages(GFP_KERNEL, 1)) +#define free_thread_info(ti) free_pages((unsigned long) (ti), 1) +#define get_thread_info(ti) get_task_struct((ti)->task) +#define put_thread_info(ti) put_task_struct((ti)->task) +#endif /* __ASSEMBLY__ */ + +/* + * Size of kernel stack for each process. + */ +#define THREAD_SIZE 8192 /* 2 pages */ + +/* + * Offsets in thread_info structure, used in assembly code + */ +#define TI_TASK 0 +#define TI_EXECDOMAIN 4 +#define TI_FLAGS 8 +#define TI_CPU 12 + +/* + * thread information flag bit numbers + */ +#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ +#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ +#define TIF_SIGPENDING 2 /* signal pending */ +#define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_POLLING_NRFLAG 4 /* true if poll_idle() is polling + TIF_NEED_RESCHED */ + +/* as above, but as bit values */ +#define _TIF_SYSCALL_TRACE (1< #include +#ifdef CONFIG_PPC_ISERIES +#include +#include +#endif #include /* time.c */ @@ -52,6 +56,24 @@ return; /* Have to let it auto-reload */ #elif defined(CONFIG_8xx_CPU6) set_dec_cpu6(val); +#elif defined(CONFIG_PPC_ISERIES) +/* + * Add code here to set the virtual decrementer in + * ItLpPaca if we have shared processors and to + * invoke the hypervisor as needed. + */ + struct Paca * paca; + int cur_dec; + + paca = (struct Paca *)mfspr(SPRG1); + if ( paca->xLpPaca.xSharedProc ) { + paca->xLpPaca.xVirtualDecr = val; + cur_dec = get_dec(); + if ( cur_dec > val ) + HvCall_setVirtualDecr(); + } + else + mtspr(SPRN_DEC, val); #else mtspr(SPRN_DEC, val); #endif @@ -69,13 +91,21 @@ extern __inline__ unsigned long get_tbl(void) { unsigned long tbl; +#if defined(CONFIG_403GCX) + asm volatile("mfspr %0, 0x3dd" : "=r" (tbl)); +#else asm volatile("mftb %0" : "=r" (tbl)); +#endif return tbl; } extern __inline__ unsigned long get_tbu(void) { unsigned long tbl; +#if defined(CONFIG_403GCX) + asm volatile("mfspr %0, 0x3dc" : "=r" (tbl)); +#else asm volatile("mftbu %0" : "=r" (tbl)); +#endif return tbl; } diff -Nru a/include/asm-ppc/todc.h b/include/asm-ppc/todc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc/todc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,380 @@ +/* + * include/asm-ppc/todc.h + * + * Definitions for the M48Txx and mc146818 series of Time of day/Real Time + * Clock chips. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * Support for the M48T37/M48T59/.../mc146818 Real Time Clock chips. + * Purpose is to make one generic file that handles all of these chips instead + * of every platform implementing the same code over & over again. + */ + +#ifndef __PPC_KERNEL_TODC_H +#define __PPC_KERNEL_TODC_H + +typedef struct { + uint rtc_type; /* your particular chip */ + + /* + * Following are the addresses of the AS0, AS1, and DATA registers + * of these chips. Note that these are board-specific. + */ + unsigned char *nvram_as0; + unsigned char *nvram_as1; + unsigned char *nvram_data; + + /* + * Define bits to stop external set of regs from changing so + * the chip can be read/written reliably. + */ + unsigned char enable_read; + unsigned char enable_write; + + /* + * Following is the number of AS0 address bits. This is normally + * 8 but some bad hardware routes address lines incorrectly. + */ + int as0_bits; + + /* Following are the register offsets for the particular chip */ + int year; + int month; + int day_of_month; + int day_of_week; + int hours; + int minutes; + int seconds; + int control_b; + int control_a; + int watchdog; + int interrupts; + int alarm_date; + int alarm_hour; + int alarm_minutes; + int alarm_seconds; + int century; + int flags; +} todc_info_t; + +/* + * Define the types of TODC/RTC variants that are supported in + * arch/ppc/kernel/todc_time.c + * Make a new one of these for any chip somehow differs from what's already + * defined. That way, if you ever need to put in code to touch those + * bits/registers in todc_time.c, you can put it inside an + * 'if (todc_info->rtc_type == TODC_TYPE_XXX)' so you won't break + * anyone else. + */ +#define TODC_TYPE_MK48T35 1 +#define TODC_TYPE_MK48T37 2 +#define TODC_TYPE_MK48T59 3 +#define TODC_TYPE_DS1693 4 /* Dallas DS1693 RTC */ +#define TODC_TYPE_DS1743 5 /* Dallas DS1743 RTC */ +#define TODC_TYPE_DS1746 6 /* Dallas DS1746 RTC */ +#define TODC_TYPE_DS1747 7 /* Dallas DS1747 RTC */ +#define TODC_TYPE_DS1501 8 /* Dallas DS1501 RTC */ +#define TODC_TYPE_DS1643 9 /* Dallas DS1643 RTC */ +#define TODC_TYPE_PC97307 10 /* PC97307 internal RTC */ +#define TODC_TYPE_DS1557 11 /* Dallas DS1557 RTC */ +#define TODC_TYPE_MC146818 100 /* Leave room for more m48txx's */ + +/* + * Bit to clear/set to enable reads/writes to the chip + */ +#define TODC_MK48TXX_CNTL_A_R 0x40 +#define TODC_MK48TXX_CNTL_A_W 0x80 +#define TODC_MK48TXX_DAY_CB 0x80 + +#define TODC_DS1501_CNTL_B_TE 0x80 + +/* + * Define the values for the various RTC's that should to into the todc_info + * table. + */ +#define TODC_TYPE_MK48T35_YEAR 0x7fff +#define TODC_TYPE_MK48T35_MONTH 0x7ffe +#define TODC_TYPE_MK48T35_DOM 0x7ffd /* Day of Month */ +#define TODC_TYPE_MK48T35_DOW 0x7ffc /* Day of Week */ +#define TODC_TYPE_MK48T35_HOURS 0x7ffb +#define TODC_TYPE_MK48T35_MINUTES 0x7ffa +#define TODC_TYPE_MK48T35_SECONDS 0x7ff9 +#define TODC_TYPE_MK48T35_CNTL_B 0x7ff9 +#define TODC_TYPE_MK48T35_CNTL_A 0x7ff8 +#define TODC_TYPE_MK48T35_WATCHDOG 0x0000 +#define TODC_TYPE_MK48T35_INTERRUPTS 0x0000 +#define TODC_TYPE_MK48T35_ALARM_DATE 0x0000 +#define TODC_TYPE_MK48T35_ALARM_HOUR 0x0000 +#define TODC_TYPE_MK48T35_ALARM_MINUTES 0x0000 +#define TODC_TYPE_MK48T35_ALARM_SECONDS 0x0000 +#define TODC_TYPE_MK48T35_CENTURY 0x0000 +#define TODC_TYPE_MK48T35_FLAGS 0x0000 + +#define TODC_TYPE_MK48T37_YEAR 0x7fff +#define TODC_TYPE_MK48T37_MONTH 0x7ffe +#define TODC_TYPE_MK48T37_DOM 0x7ffd /* Day of Month */ +#define TODC_TYPE_MK48T37_DOW 0x7ffc /* Day of Week */ +#define TODC_TYPE_MK48T37_HOURS 0x7ffb +#define TODC_TYPE_MK48T37_MINUTES 0x7ffa +#define TODC_TYPE_MK48T37_SECONDS 0x7ff9 +#define TODC_TYPE_MK48T37_CNTL_B 0x7ff9 +#define TODC_TYPE_MK48T37_CNTL_A 0x7ff8 +#define TODC_TYPE_MK48T37_WATCHDOG 0x7ff7 +#define TODC_TYPE_MK48T37_INTERRUPTS 0x7ff6 +#define TODC_TYPE_MK48T37_ALARM_DATE 0x7ff5 +#define TODC_TYPE_MK48T37_ALARM_HOUR 0x7ff4 +#define TODC_TYPE_MK48T37_ALARM_MINUTES 0x7ff3 +#define TODC_TYPE_MK48T37_ALARM_SECONDS 0x7ff2 +#define TODC_TYPE_MK48T37_CENTURY 0x7ff1 +#define TODC_TYPE_MK48T37_FLAGS 0x7ff0 + +#define TODC_TYPE_MK48T59_YEAR 0x1fff +#define TODC_TYPE_MK48T59_MONTH 0x1ffe +#define TODC_TYPE_MK48T59_DOM 0x1ffd /* Day of Month */ +#define TODC_TYPE_MK48T59_DOW 0x1ffc /* Day of Week */ +#define TODC_TYPE_MK48T59_HOURS 0x1ffb +#define TODC_TYPE_MK48T59_MINUTES 0x1ffa +#define TODC_TYPE_MK48T59_SECONDS 0x1ff9 +#define TODC_TYPE_MK48T59_CNTL_B 0x1ff9 +#define TODC_TYPE_MK48T59_CNTL_A 0x1ff8 +#define TODC_TYPE_MK48T59_WATCHDOG 0x1fff +#define TODC_TYPE_MK48T59_INTERRUPTS 0x1fff +#define TODC_TYPE_MK48T59_ALARM_DATE 0x1fff +#define TODC_TYPE_MK48T59_ALARM_HOUR 0x1fff +#define TODC_TYPE_MK48T59_ALARM_MINUTES 0x1fff +#define TODC_TYPE_MK48T59_ALARM_SECONDS 0x1fff +#define TODC_TYPE_MK48T59_CENTURY 0x1fff +#define TODC_TYPE_MK48T59_FLAGS 0x1fff + +#define TODC_TYPE_DS1501_YEAR 0x06 +#define TODC_TYPE_DS1501_MONTH 0x05 +#define TODC_TYPE_DS1501_DOM 0x04 /* Day of Month */ +#define TODC_TYPE_DS1501_DOW 0x03 /* Day of Week */ +#define TODC_TYPE_DS1501_HOURS 0x02 +#define TODC_TYPE_DS1501_MINUTES 0x01 +#define TODC_TYPE_DS1501_SECONDS 0x00 +#define TODC_TYPE_DS1501_CNTL_B 0x0f +#define TODC_TYPE_DS1501_CNTL_A 0x0f +#define TODC_TYPE_DS1501_WATCHDOG 0xff +#define TODC_TYPE_DS1501_INTERRUPTS 0xff +#define TODC_TYPE_DS1501_ALARM_DATE 0x0b +#define TODC_TYPE_DS1501_ALARM_HOUR 0x0a +#define TODC_TYPE_DS1501_ALARM_MINUTES 0x09 +#define TODC_TYPE_DS1501_ALARM_SECONDS 0x08 +#define TODC_TYPE_DS1501_CENTURY 0x07 +#define TODC_TYPE_DS1501_FLAGS 0xff + +#define TODC_TYPE_DS1557_YEAR 0x7ffff +#define TODC_TYPE_DS1557_MONTH 0x7fffe +#define TODC_TYPE_DS1557_DOM 0x7fffd /* Day of Month */ +#define TODC_TYPE_DS1557_DOW 0x7fffc /* Day of Week */ +#define TODC_TYPE_DS1557_HOURS 0x7fffb +#define TODC_TYPE_DS1557_MINUTES 0x7fffa +#define TODC_TYPE_DS1557_SECONDS 0x7fff9 +#define TODC_TYPE_DS1557_CNTL_B 0x7fff9 +#define TODC_TYPE_DS1557_CNTL_A 0x7fff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1557_WATCHDOG 0x7fff7 +#define TODC_TYPE_DS1557_INTERRUPTS 0x7fff6 +#define TODC_TYPE_DS1557_ALARM_DATE 0x7fff5 +#define TODC_TYPE_DS1557_ALARM_HOUR 0x7fff4 +#define TODC_TYPE_DS1557_ALARM_MINUTES 0x7fff3 +#define TODC_TYPE_DS1557_ALARM_SECONDS 0x7fff2 +#define TODC_TYPE_DS1557_CENTURY 0x7fff8 +#define TODC_TYPE_DS1557_FLAGS 0x7fff0 + +#define TODC_TYPE_DS1643_YEAR 0x1fff +#define TODC_TYPE_DS1643_MONTH 0x1ffe +#define TODC_TYPE_DS1643_DOM 0x1ffd /* Day of Month */ +#define TODC_TYPE_DS1643_DOW 0x1ffc /* Day of Week */ +#define TODC_TYPE_DS1643_HOURS 0x1ffb +#define TODC_TYPE_DS1643_MINUTES 0x1ffa +#define TODC_TYPE_DS1643_SECONDS 0x1ff9 +#define TODC_TYPE_DS1643_CNTL_B 0x1ff9 +#define TODC_TYPE_DS1643_CNTL_A 0x1ff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1643_WATCHDOG 0x1fff +#define TODC_TYPE_DS1643_INTERRUPTS 0x1fff +#define TODC_TYPE_DS1643_ALARM_DATE 0x1fff +#define TODC_TYPE_DS1643_ALARM_HOUR 0x1fff +#define TODC_TYPE_DS1643_ALARM_MINUTES 0x1fff +#define TODC_TYPE_DS1643_ALARM_SECONDS 0x1fff +#define TODC_TYPE_DS1643_CENTURY 0x1ff8 +#define TODC_TYPE_DS1643_FLAGS 0x1fff + +#define TODC_TYPE_DS1693_YEAR 0x09 +#define TODC_TYPE_DS1693_MONTH 0x08 +#define TODC_TYPE_DS1693_DOM 0x07 /* Day of Month */ +#define TODC_TYPE_DS1693_DOW 0x06 /* Day of Week */ +#define TODC_TYPE_DS1693_HOURS 0x04 +#define TODC_TYPE_DS1693_MINUTES 0x02 +#define TODC_TYPE_DS1693_SECONDS 0x00 +#define TODC_TYPE_DS1693_CNTL_B 0x0b +#define TODC_TYPE_DS1693_CNTL_A 0x0a +#define TODC_TYPE_DS1693_WATCHDOG 0xff +#define TODC_TYPE_DS1693_INTERRUPTS 0xff +#define TODC_TYPE_DS1693_ALARM_DATE 0x49 +#define TODC_TYPE_DS1693_ALARM_HOUR 0x05 +#define TODC_TYPE_DS1693_ALARM_MINUTES 0x03 +#define TODC_TYPE_DS1693_ALARM_SECONDS 0x01 +#define TODC_TYPE_DS1693_CENTURY 0x48 +#define TODC_TYPE_DS1693_FLAGS 0xff + +#define TODC_TYPE_DS1743_YEAR 0x1fff +#define TODC_TYPE_DS1743_MONTH 0x1ffe +#define TODC_TYPE_DS1743_DOM 0x1ffd /* Day of Month */ +#define TODC_TYPE_DS1743_DOW 0x1ffc /* Day of Week */ +#define TODC_TYPE_DS1743_HOURS 0x1ffb +#define TODC_TYPE_DS1743_MINUTES 0x1ffa +#define TODC_TYPE_DS1743_SECONDS 0x1ff9 +#define TODC_TYPE_DS1743_CNTL_B 0x1ff9 +#define TODC_TYPE_DS1743_CNTL_A 0x1ff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1743_WATCHDOG 0x1fff +#define TODC_TYPE_DS1743_INTERRUPTS 0x1fff +#define TODC_TYPE_DS1743_ALARM_DATE 0x1fff +#define TODC_TYPE_DS1743_ALARM_HOUR 0x1fff +#define TODC_TYPE_DS1743_ALARM_MINUTES 0x1fff +#define TODC_TYPE_DS1743_ALARM_SECONDS 0x1fff +#define TODC_TYPE_DS1743_CENTURY 0x1ff8 +#define TODC_TYPE_DS1743_FLAGS 0x1fff + +#define TODC_TYPE_DS1746_YEAR 0x1ffff +#define TODC_TYPE_DS1746_MONTH 0x1fffe +#define TODC_TYPE_DS1746_DOM 0x1fffd /* Day of Month */ +#define TODC_TYPE_DS1746_DOW 0x1fffc /* Day of Week */ +#define TODC_TYPE_DS1746_HOURS 0x1fffb +#define TODC_TYPE_DS1746_MINUTES 0x1fffa +#define TODC_TYPE_DS1746_SECONDS 0x1fff9 +#define TODC_TYPE_DS1746_CNTL_B 0x1fff9 +#define TODC_TYPE_DS1746_CNTL_A 0x1fff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1746_WATCHDOG 0x00000 +#define TODC_TYPE_DS1746_INTERRUPTS 0x00000 +#define TODC_TYPE_DS1746_ALARM_DATE 0x00000 +#define TODC_TYPE_DS1746_ALARM_HOUR 0x00000 +#define TODC_TYPE_DS1746_ALARM_MINUTES 0x00000 +#define TODC_TYPE_DS1746_ALARM_SECONDS 0x00000 +#define TODC_TYPE_DS1746_CENTURY 0x00000 +#define TODC_TYPE_DS1746_FLAGS 0x00000 + +#define TODC_TYPE_DS1747_YEAR 0x1ffff +#define TODC_TYPE_DS1747_MONTH 0x1fffe +#define TODC_TYPE_DS1747_DOM 0x1fffd /* Day of Month */ +#define TODC_TYPE_DS1747_DOW 0x1fffc /* Day of Week */ +#define TODC_TYPE_DS1747_HOURS 0x1fffb +#define TODC_TYPE_DS1747_MINUTES 0x1fffa +#define TODC_TYPE_DS1747_SECONDS 0x1fff9 +#define TODC_TYPE_DS1747_CNTL_B 0x1fff9 +#define TODC_TYPE_DS1747_CNTL_A 0x1fff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1747_WATCHDOG 0x00000 +#define TODC_TYPE_DS1747_INTERRUPTS 0x00000 +#define TODC_TYPE_DS1747_ALARM_DATE 0x00000 +#define TODC_TYPE_DS1747_ALARM_HOUR 0x00000 +#define TODC_TYPE_DS1747_ALARM_MINUTES 0x00000 +#define TODC_TYPE_DS1747_ALARM_SECONDS 0x00000 +#define TODC_TYPE_DS1747_CENTURY 0x00000 +#define TODC_TYPE_DS1747_FLAGS 0x00000 + +#define TODC_TYPE_MC146818_YEAR 0x09 +#define TODC_TYPE_MC146818_MONTH 0x08 +#define TODC_TYPE_MC146818_DOM 0x07 /* Day of Month */ +#define TODC_TYPE_MC146818_DOW 0x06 /* Day of Week */ +#define TODC_TYPE_MC146818_HOURS 0x04 +#define TODC_TYPE_MC146818_MINUTES 0x02 +#define TODC_TYPE_MC146818_SECONDS 0x00 +#define TODC_TYPE_MC146818_CNTL_B 0x0a +#define TODC_TYPE_MC146818_CNTL_A 0x0b /* control_a R/W regs */ +#define TODC_TYPE_MC146818_WATCHDOG 0x0c +#define TODC_TYPE_MC146818_INTERRUPTS 0x0d +#define TODC_TYPE_MC146818_ALARM_DATE 0xff +#define TODC_TYPE_MC146818_ALARM_HOUR 0x05 +#define TODC_TYPE_MC146818_ALARM_MINUTES 0x03 +#define TODC_TYPE_MC146818_ALARM_SECONDS 0x01 +#define TODC_TYPE_MC146818_CENTURY 0xff +#define TODC_TYPE_MC146818_FLAGS 0xff + +#define TODC_TYPE_PC97307_YEAR 0x09 +#define TODC_TYPE_PC97307_MONTH 0x08 +#define TODC_TYPE_PC97307_DOM 0x07 /* Day of Month */ +#define TODC_TYPE_PC97307_DOW 0x06 /* Day of Week */ +#define TODC_TYPE_PC97307_HOURS 0x04 +#define TODC_TYPE_PC97307_MINUTES 0x02 +#define TODC_TYPE_PC97307_SECONDS 0x00 +#define TODC_TYPE_PC97307_CNTL_B 0x0a +#define TODC_TYPE_PC97307_CNTL_A 0x0b /* control_a R/W regs */ +#define TODC_TYPE_PC97307_WATCHDOG 0x0c +#define TODC_TYPE_PC97307_INTERRUPTS 0x0d +#define TODC_TYPE_PC97307_ALARM_DATE 0xff +#define TODC_TYPE_PC97307_ALARM_HOUR 0x05 +#define TODC_TYPE_PC97307_ALARM_MINUTES 0x03 +#define TODC_TYPE_PC97307_ALARM_SECONDS 0x01 +#define TODC_TYPE_PC97307_CENTURY 0xff +#define TODC_TYPE_PC97307_FLAGS 0xff + +/* + * Define macros to allocate and init the todc_info_t table that will + * be used by the todc_time.c routines. + */ +#define TODC_ALLOC() \ + static todc_info_t todc_info_alloc; \ + todc_info_t *todc_info = &todc_info_alloc; + +#define TODC_INIT(clock_type, as0, as1, data, bits) { \ + todc_info->rtc_type = clock_type; \ + \ + todc_info->nvram_as0 = (unsigned char *)(as0); \ + todc_info->nvram_as1 = (unsigned char *)(as1); \ + todc_info->nvram_data = (unsigned char *)(data); \ + \ + todc_info->as0_bits = (bits); \ + \ + todc_info->year = clock_type ##_YEAR; \ + todc_info->month = clock_type ##_MONTH; \ + todc_info->day_of_month = clock_type ##_DOM; \ + todc_info->day_of_week = clock_type ##_DOW; \ + todc_info->hours = clock_type ##_HOURS; \ + todc_info->minutes = clock_type ##_MINUTES; \ + todc_info->seconds = clock_type ##_SECONDS; \ + todc_info->control_b = clock_type ##_CNTL_B; \ + todc_info->control_a = clock_type ##_CNTL_A; \ + todc_info->watchdog = clock_type ##_WATCHDOG; \ + todc_info->interrupts = clock_type ##_INTERRUPTS; \ + todc_info->alarm_date = clock_type ##_ALARM_DATE; \ + todc_info->alarm_hour = clock_type ##_ALARM_HOUR; \ + todc_info->alarm_minutes = clock_type ##_ALARM_MINUTES; \ + todc_info->alarm_seconds = clock_type ##_ALARM_SECONDS; \ + todc_info->century = clock_type ##_CENTURY; \ + todc_info->flags = clock_type ##_FLAGS; \ +} + +#ifndef BCD_TO_BIN +#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) +#endif + +#ifndef BIN_TO_BCD +#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10) +#endif + +extern todc_info_t *todc_info; + +unsigned char todc_direct_read_val(int addr); +void todc_direct_write_val(int addr, unsigned char val); +unsigned char todc_m48txx_read_val(int addr); +void todc_m48txx_write_val(int addr, unsigned char val); +unsigned char todc_mc146818_read_val(int addr); +void todc_mc146818_write_val(int addr, unsigned char val); + +long todc_time_init(void); +unsigned long todc_get_rtc_time(void); +int todc_set_rtc_time(unsigned long nowtime); +void todc_calibrate_decr(void); + +#endif /* __PPC_KERNEL_TODC_H */ diff -Nru a/include/asm-ppc/tqm8xx.h b/include/asm-ppc/tqm8xx.h --- a/include/asm-ppc/tqm8xx.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,54 +0,0 @@ -/* - * BK Id: SCCS/s.tqm8xx.h 1.8 08/30/01 09:01:04 trini - */ -/* - * TQM8xx(L) board specific definitions - * - * Copyright (c) 1999,2000,2001 Wolfgang Denk (wd@denx.de) - */ - -#ifndef __MACH_TQM8xx_H -#define __MACH_TQM8xx_H - -#include - -#include - -#define TQM_IMMR_BASE 0xFFF00000 /* phys. addr of IMMR */ -#define TQM_IMAP_SIZE (64 * 1024) /* size of mapped area */ - -#define IMAP_ADDR TQM_IMMR_BASE /* physical base address of IMMR area */ -#define IMAP_SIZE TQM_IMAP_SIZE /* mapped size of IMMR area */ - -/*----------------------------------------------------------------------- - * PCMCIA stuff - *----------------------------------------------------------------------- - * - */ -#define PCMCIA_MEM_SIZE ( 64 << 20 ) - -#define MAX_HWIFS 1 /* overwrite default in include/asm-ppc/ide.h */ - -/* - * Definitions for IDE0 Interface - */ -#define IDE0_BASE_OFFSET 0 -#define IDE0_DATA_REG_OFFSET (PCMCIA_MEM_SIZE + 0x320) -#define IDE0_ERROR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 1) -#define IDE0_NSECTOR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 2) -#define IDE0_SECTOR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 3) -#define IDE0_LCYL_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 4) -#define IDE0_HCYL_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 5) -#define IDE0_SELECT_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 6) -#define IDE0_STATUS_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 7) -#define IDE0_CONTROL_REG_OFFSET 0x0106 -#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ - -#define IDE0_INTERRUPT 13 - - -/* We don't use the 8259. -*/ -#define NR_8259_INTS 0 - -#endif /* __MACH_TQM8xx_H */ diff -Nru a/include/asm-ppc/unistd.h b/include/asm-ppc/unistd.h --- a/include/asm-ppc/unistd.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-ppc/unistd.h Tue Feb 19 18:08:58 2002 @@ -216,6 +216,18 @@ #define __NR_mincore 206 #define __NR_gettid 207 #define __NR_tkill 208 +#define __NR_setxattr 209 +#define __NR_lsetxattr 210 +#define __NR_fsetxattr 211 +#define __NR_getxattr 212 +#define __NR_lgetxattr 213 +#define __NR_fgetxattr 214 +#define __NR_listxattr 215 +#define __NR_llistxattr 216 +#define __NR_flistxattr 217 +#define __NR_removexattr 218 +#define __NR_lremovexattr 219 +#define __NR_fremovexattr 220 #define __NR(n) #n diff -Nru a/include/asm-ppc/walnut.h b/include/asm-ppc/walnut.h --- a/include/asm-ppc/walnut.h Tue Feb 19 18:08:59 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,58 +0,0 @@ -/* - * BK Id: SCCS/s.walnut.h 1.10 09/14/01 17:37:56 trini - */ -/* - * - * Copyright (c) 1999 Grant Erickson - * - * Copyright 2000 MontaVista Software Inc. - * PPC405 modifications - * Author: MontaVista Software, Inc. - * frank_rowand@mvista.com or source@mvista.com - * debbie_chu@mvista.com - * - * Module name: ppc405.h - * - * Description: - * Macros, definitions, and data structures specific to the IBM PowerPC - * based boards. - * - * This includes: - * - * 405GP "Walnut" evaluation board - * - */ - -#ifdef __KERNEL__ -#ifndef __WALNUT_H__ -#define __WALNUT_H__ - -#ifndef __ASSEMBLY__ -/* - * Data structure defining board information maintained by the boot - * ROM on IBM's "Walnut" evaluation board. An effort has been made to - * keep the field names consistent with the 8xx 'bd_t' board info - * structures. - */ - -typedef struct board_info { - unsigned char bi_s_version[4]; /* Version of this structure */ - unsigned char bi_r_version[30]; /* Version of the IBM ROM */ - unsigned int bi_memsize; /* DRAM installed, in bytes */ - unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ - unsigned char bi_pci_enetaddr[6]; /* PCI Ethernet MAC address */ - unsigned int bi_procfreq; /* Processor speed, in Hz */ - unsigned int bi_plb_busfreq; /* PLB Bus speed, in Hz */ - unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ -} bd_t; - -#endif /* !__ASSEMBLY__ */ - -/* Memory map for the IBM "Walnut" 405GP evaluation board. - * Generic 4xx plus RTC. - */ -#define WALNUT_RTC_ADDR ((uint)0xf0001000) -#define WALNUT_RTC_SIZE ((uint)4*1024) - -#endif /* __WALNUT_H__ */ -#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc64/Naca.h b/include/asm-ppc64/Naca.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/Naca.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,39 @@ +#ifndef _NACA_H +#define _NACA_H + +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +struct Naca +{ + void *xItVpdAreas; + void *xRamDisk; + u64 xRamDiskSize; /* In pages */ + struct Paca *paca; /* Ptr to an array of pacas */ + u64 debug_switch; /* Bits to control debug printing */ + u16 processorCount; /* # of physical processors */ + u16 dCacheL1LineSize; /* Line size of L1 DCache in bytes */ + u16 dCacheL1LogLineSize; /* Log-2 of DCache line size */ + u16 dCacheL1LinesPerPage; /* DCache lines per page */ + u16 iCacheL1LineSize; /* Line size of L1 ICache in bytes */ + u16 iCacheL1LogLineSize; /* Log-2 of ICache line size */ + u16 iCacheL1LinesPerPage; /* ICache lines per page */ + u16 slb_size; /* SLB size in entries */ + u64 physicalMemorySize; /* Size of real memory in bytes */ + u64 pftSize; /* Log base 2 of page table size */ + u64 serialPortAddr; /* Phyical address of serial port */ + u8 interrupt_controller; /* Type of interrupt controller */ + u8 resv0[6]; /* Padding */ +}; + +extern struct Naca *naca; + +#endif /* _NACA_H */ diff -Nru a/include/asm-ppc64/Paca.h b/include/asm-ppc64/Paca.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/Paca.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,164 @@ +#ifndef _PPC64_PACA_H +#define _PPC64_PACA_H + +/*============================================================================ + * Header File Id + * Name______________: Paca.H + * + * Description_______: + * + * This control block defines the PACA which defines the processor + * specific data for each logical processor on the system. + * There are some pointers defined that are utilized by PLIC. + * + * C 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include + +#define N_EXC_STACK 2 + +/*----------------------------------------------------------------------------- + * Other Includes + *----------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#include +#include + +/* A Paca entry is required for each logical processor. On systems + * that support hardware multi-threading, this is equal to twice the + * number of physical processors. On LPAR systems, we are required + * to have space for the maximum number of logical processors we + * could ever possibly have. Currently, we are limited to allocating + * 24 processors to a partition which gives 48 logical processors on + * an HMT box. Therefore, we reserve this many Paca entries. + */ +#define maxProcessors 24 +#define maxPacas maxProcessors * 2 + +extern struct Paca xPaca[]; +#define get_paca() ((struct Paca *)mfspr(SPRG3)) + +/*============================================================================ + * Name_______: Paca + * + * Description: + * + * Defines the layout of the Paca. + * + * This structure is not directly accessed by PLIC or the SP except + * for the first two pointers that point to the ItLpPaca area and the + * ItLpRegSave area for this processor. Both the ItLpPaca and + * ItLpRegSave objects are currently contained within the + * PACA but they do not need to be. + * + *============================================================================ + */ +struct Paca +{ +/*===================================================================================== + * CACHE_LINE_1 0x0000 - 0x007F + *===================================================================================== + */ + struct ItLpPaca *xLpPacaPtr; /* Pointer to LpPaca for PLIC 0x00 */ + struct ItLpRegSave *xLpRegSavePtr; /* Pointer to LpRegSave for PLIC 0x08 */ + u64 xCurrent; /* Pointer to current 0x10 */ + u16 xPacaIndex; /* Logical processor number 0x18 */ + u16 xHwProcNum; /* Actual Hardware Processor Number 0x1a */ + u32 default_decr; /* Default decrementer value 0x1c */ + u64 xHrdIntStack; /* Stack for hardware interrupts 0x20 */ + u64 xKsave; /* Saved Kernel stack addr or zero 0x28 */ + u64 pvr; /* Processor version register 0x30 */ + u8 *exception_sp; /* 0x38 */ + + struct ItLpQueue *lpQueuePtr; /* LpQueue handled by this processor 0x40 */ + u64 xTOC; /* Kernel TOC address 0x48 */ + STAB xStab_data; /* Segment table information 0x50,0x58,0x60 */ + u8 xSegments[STAB_CACHE_SIZE]; /* Cache of used stab entries 0x68,0x70 */ + u8 xProcEnabled; /* 1=soft enabled 0x78 */ + u8 xHrdIntCount; /* Count of active hardware interrupts 0x79 */ + u8 prof_enabled; /* 1=iSeries profiling enabled 0x7A */ + u8 resv1[5]; /* 0x7B-0x7F */ + +/*===================================================================================== + * CACHE_LINE_2 0x0080 - 0x00FF + *===================================================================================== + */ + u64 *pgd_cache; /* 0x00 */ + u64 *pmd_cache; /* 0x08 */ + u64 *pte_cache; /* 0x10 */ + u64 pgtable_cache_sz; /* 0x18 */ + u64 next_jiffy_update_tb; /* TB value for next jiffy update 0x20 */ + u32 lpEvent_count; /* lpEvents processed 0x28 */ + u32 prof_multiplier; /* 0x2C */ + u32 prof_counter; /* 0x30 */ + u32 prof_shift; /* iSeries shift for profile bucket size0x34 */ + u32 *prof_buffer; /* iSeries profiling buffer 0x38 */ + u32 *prof_stext; /* iSeries start of kernel text 0x40 */ + u32 prof_len; /* iSeries length of profile buffer -1 0x48 */ + u8 rsvd2[128-76]; /* 0x4C */ + +/*===================================================================================== + * CACHE_LINE_3 0x0100 - 0x017F + *===================================================================================== + */ + u8 xProcStart; /* At startup, processor spins until 0x100 */ + /* xProcStart becomes non-zero. */ + u8 rsvd3[127]; + +/*===================================================================================== + * CACHE_LINE_4-8 0x0180 - 0x03FF Contains ItLpPaca + *===================================================================================== + */ + struct ItLpPaca xLpPaca; /* Space for ItLpPaca */ + +/*===================================================================================== + * CACHE_LINE_9-16 0x0400 - 0x07FF Contains ItLpRegSave + *===================================================================================== + */ + struct ItLpRegSave xRegSav; /* Register save for proc */ + +/*===================================================================================== + * CACHE_LINE_17-18 0x0800 - 0x0EFF Reserved + *===================================================================================== + */ + struct rtas_args xRtas; /* Per processor RTAS struct */ + u64 xR1; /* r1 save for RTAS calls */ + u64 xSavedMsr; /* Old msr saved here by HvCall */ + u8 rsvd5[256-16-sizeof(struct rtas_args)]; + +/*===================================================================================== + * CACHE_LINE_19-30 0x0800 - 0x0EFF Reserved + *===================================================================================== + */ + u8 rsvd6[0x600]; + +/*===================================================================================== + * CACHE_LINE_31 0x0F00 - 0x0F7F Exception stack + *===================================================================================== + */ + u8 exception_stack[N_EXC_STACK*EXC_FRAME_SIZE]; + +/*===================================================================================== + * CACHE_LINE_32 0x0F80 - 0x0FFF Reserved + *===================================================================================== + */ + u8 rsvd7[0x80]; /* Give the stack some rope ... */ + +/*===================================================================================== + * Page 2 Reserved for guard page. Also used as a stack early in SMP boots before + * relocation is enabled. + *===================================================================================== + */ + u8 guard[0x1000]; /* ... and then hang 'em */ +}; + +#endif /* _PPC64_PACA_H */ diff -Nru a/include/asm-ppc64/a.out.h b/include/asm-ppc64/a.out.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/a.out.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,45 @@ +#ifndef __PPC64_A_OUT_H__ +#define __PPC64_A_OUT_H__ + +#include + +/* + * c 2001 PPC 64 Team, IBM Corp + * + * 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. + */ + +struct exec +{ + unsigned long a_info; /* Use macros N_MAGIC, etc for access */ + unsigned a_text; /* length of text, in bytes */ + unsigned a_data; /* length of data, in bytes */ + unsigned a_bss; /* length of uninitialized data area for file, in bytes */ + unsigned a_syms; /* length of symbol table data in file, in bytes */ + unsigned a_entry; /* start address */ + unsigned a_trsize; /* length of relocation info for text, in bytes */ + unsigned a_drsize; /* length of relocation info for data, in bytes */ +}; + +#define N_TRSIZE(a) ((a).a_trsize) +#define N_DRSIZE(a) ((a).a_drsize) +#define N_SYMSIZE(a) ((a).a_syms) + +#ifdef __KERNEL__ + +/* 64-bit user address space is less PGDIR_SIZE due to pgd_index() bug. */ +#define STACK_TOP_USER64 (TASK_SIZE_USER64 - PGDIR_SIZE) + +/* Give 32-bit user space a full 4G address space to live in. */ +#define STACK_TOP_USER32 (TASK_SIZE_USER32) + +#define STACK_TOP ((test_thread_flag(TIF_32BIT) || \ + (ppcdebugset(PPCDBG_BINFMT_32ADDR))) ? \ + STACK_TOP_USER32 : STACK_TOP_USER64) + +#endif /* __KERNEL__ */ + +#endif /* __PPC64_A_OUT_H__ */ diff -Nru a/include/asm-ppc64/abs_addr.h b/include/asm-ppc64/abs_addr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/abs_addr.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,121 @@ +#ifndef _ABS_ADDR_H +#define _ABS_ADDR_H + +#include + +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +typedef u32 msChunks_entry; +struct msChunks { + unsigned long num_chunks; + unsigned long chunk_size; + unsigned long chunk_shift; + unsigned long chunk_mask; + msChunks_entry *abs; +}; + +extern struct msChunks msChunks; + +extern unsigned long msChunks_alloc(unsigned long, unsigned long, unsigned long); +extern unsigned long reloc_offset(void); + +#ifdef CONFIG_MSCHUNKS + +static inline unsigned long +chunk_to_addr(unsigned long chunk) +{ + unsigned long offset = reloc_offset(); + struct msChunks *_msChunks = PTRRELOC(&msChunks); + + return chunk << _msChunks->chunk_shift; +} + +static inline unsigned long +addr_to_chunk(unsigned long addr) +{ + unsigned long offset = reloc_offset(); + struct msChunks *_msChunks = PTRRELOC(&msChunks); + + return addr >> _msChunks->chunk_shift; +} + +static inline unsigned long +chunk_offset(unsigned long addr) +{ + unsigned long offset = reloc_offset(); + struct msChunks *_msChunks = PTRRELOC(&msChunks); + + return addr & _msChunks->chunk_mask; +} + +static inline unsigned long +abs_chunk(unsigned long pchunk) +{ + unsigned long offset = reloc_offset(); + struct msChunks *_msChunks = PTRRELOC(&msChunks); + if ( pchunk >= _msChunks->num_chunks ) { + return pchunk; + } + return PTRRELOC(_msChunks->abs)[pchunk]; +} + + +static inline unsigned long +phys_to_absolute(unsigned long pa) +{ + return chunk_to_addr(abs_chunk(addr_to_chunk(pa))) + chunk_offset(pa); +} + +static inline unsigned long +physRpn_to_absRpn(unsigned long rpn) +{ + unsigned long pa = rpn << PAGE_SHIFT; + unsigned long aa = phys_to_absolute(pa); + return (aa >> PAGE_SHIFT); +} + +static inline unsigned long +absolute_to_phys(unsigned long aa) +{ + return lmb_abs_to_phys(aa); +} + +#else /* !CONFIG_MSCHUNKS */ + +#define chunk_to_addr(chunk) ((unsigned long)(chunk)) +#define addr_to_chunk(addr) (addr) +#define chunk_offset(addr) (0) +#define abs_chunk(pchunk) (pchunk) + +#define phys_to_absolute(pa) (pa) +#define physRpn_to_absRpn(rpn) (rpn) +#define absolute_to_phys(aa) (aa) + +#endif /* CONFIG_MSCHUNKS */ + + +static inline unsigned long +virt_to_absolute(unsigned long ea) +{ + return phys_to_absolute(__pa(ea)); +} + +static inline unsigned long +absolute_to_virt(unsigned long aa) +{ + return (unsigned long)__va(absolute_to_phys(aa)); +} + +#endif /* _ABS_ADDR_H */ diff -Nru a/include/asm-ppc64/atomic.h b/include/asm-ppc64/atomic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/atomic.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,180 @@ +/* + * PowerPC64 atomic operations + * + * Copyright (C) 2001 Paul Mackerras , IBM + * Copyright (C) 2001 Anton Blanchard , IBM + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _ASM_PPC64_ATOMIC_H_ +#define _ASM_PPC64_ATOMIC_H_ + +#include + +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +#define atomic_read(v) ((v)->counter) +#define atomic_set(v,i) (((v)->counter) = (i)) + +static __inline__ void atomic_add(int a, atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%3 # atomic_add\n\ + add %0,%2,%0\n\ + stwcx. %0,0,%3\n\ + bne- 1b" + : "=&r" (t), "=m" (v->counter) + : "r" (a), "r" (&v->counter), "m" (v->counter) + : "cc"); +} + +static __inline__ int atomic_add_return(int a, atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%2 # atomic_add_return\n\ + add %0,%1,%0\n\ + stwcx. %0,0,%2\n\ + bne- 1b" + ISYNC_ON_SMP + : "=&r" (t) + : "r" (a), "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +static __inline__ void atomic_sub(int a, atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%3 # atomic_sub\n\ + subf %0,%2,%0\n\ + stwcx. %0,0,%3\n\ + bne- 1b" + : "=&r" (t), "=m" (v->counter) + : "r" (a), "r" (&v->counter), "m" (v->counter) + : "cc"); +} + +static __inline__ int atomic_sub_return(int a, atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%2 # atomic_sub_return\n\ + subf %0,%1,%0\n\ + stwcx. %0,0,%2\n\ + bne- 1b" + ISYNC_ON_SMP + : "=&r" (t) + : "r" (a), "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +static __inline__ void atomic_inc(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%2 # atomic_inc\n\ + addic %0,%0,1\n\ + stwcx. %0,0,%2\n\ + bne- 1b" + : "=&r" (t), "=m" (v->counter) + : "r" (&v->counter), "m" (v->counter) + : "cc"); +} + +static __inline__ int atomic_inc_return(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%1 # atomic_inc_return\n\ + addic %0,%0,1\n\ + stwcx. %0,0,%1\n\ + bne- 1b" + ISYNC_ON_SMP + : "=&r" (t) + : "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +static __inline__ void atomic_dec(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%2 # atomic_dec\n\ + addic %0,%0,-1\n\ + stwcx. %0,0,%2\n\ + bne- 1b" + : "=&r" (t), "=m" (v->counter) + : "r" (&v->counter), "m" (v->counter) + : "cc"); +} + +static __inline__ int atomic_dec_return(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%1 # atomic_dec_return\n\ + addic %0,%0,-1\n\ + stwcx. %0,0,%1\n\ + bne- 1b" + ISYNC_ON_SMP + : "=&r" (t) + : "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +#define atomic_sub_and_test(a, v) (atomic_sub_return((a), (v)) == 0) +#define atomic_dec_and_test(v) (atomic_dec_return((v)) == 0) + +/* + * Atomically test *v and decrement if it is greater than 0. + * The function returns the old value of *v minus 1. + */ +static __inline__ int atomic_dec_if_positive(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%1 # atomic_dec_if_positive\n\ + addic. %0,%0,-1\n\ + blt- 2f\n\ + stwcx. %0,0,%1\n\ + bne- 1b" + ISYNC_ON_SMP + "\n\ +2:" : "=&r" (t) + : "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +#define smp_mb__before_atomic_dec() smp_mb() +#define smp_mb__after_atomic_dec() smp_mb() +#define smp_mb__before_atomic_inc() smp_mb() +#define smp_mb__after_atomic_inc() smp_mb() + +#endif /* _ASM_PPC64_ATOMIC_H_ */ diff -Nru a/include/asm-ppc64/bitops.h b/include/asm-ppc64/bitops.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/bitops.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,344 @@ +/* + * PowerPC64 atomic bit operations. + * Dave Engebretsen, Todd Inglett, Don Reed, Pat McCarthy, Peter Bergner, + * Anton Blanchard + * + * Originally taken from the 32b PPC code. Modified to use 64b values for + * the various counters & memory references. + * + * Bitops are odd when viewed on big-endian systems. They were designed + * on little endian so the size of the bitset doesn't matter (low order bytes + * come first) as long as the bit in question is valid. + * + * Bits are "tested" often using the C expression (val & (1< + +/* + * clear_bit doesn't imply a memory barrier + */ +#define smp_mb__before_clear_bit() smp_mb() +#define smp_mb__after_clear_bit() smp_mb() + +static __inline__ int test_bit(unsigned long nr, __const__ volatile void *addr) +{ + return (1UL & (((__const__ long *) addr)[nr >> 6] >> (nr & 63))); +} + +static __inline__ void set_bit(unsigned long nr, volatile void *addr) +{ + unsigned long old; + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + + __asm__ __volatile__( +"1: ldarx %0,0,%3 # set_bit\n\ + or %0,%0,%2\n\ + stdcx. %0,0,%3\n\ + bne- 1b" + : "=&r" (old), "=m" (*p) + : "r" (mask), "r" (p), "m" (*p) + : "cc"); +} + +static __inline__ void clear_bit(unsigned long nr, volatile void *addr) +{ + unsigned long old; + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + + __asm__ __volatile__( +"1: ldarx %0,0,%3 # clear_bit\n\ + andc %0,%0,%2\n\ + stdcx. %0,0,%3\n\ + bne- 1b" + : "=&r" (old), "=m" (*p) + : "r" (mask), "r" (p), "m" (*p) + : "cc"); +} + +static __inline__ void change_bit(unsigned long nr, volatile void *addr) +{ + unsigned long old; + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + + __asm__ __volatile__( +"1: ldarx %0,0,%3 # change_bit\n\ + xor %0,%0,%2\n\ + stdcx. %0,0,%3\n\ + bne- 1b" + : "=&r" (old), "=m" (*p) + : "r" (mask), "r" (p), "m" (*p) + : "cc"); +} + +static __inline__ int test_and_set_bit(unsigned long nr, volatile void *addr) +{ + unsigned long old, t; + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + + __asm__ __volatile__( + EIEIO_ON_SMP +"1: ldarx %0,0,%3 # test_and_set_bit\n\ + or %1,%0,%2 \n\ + stdcx. %1,0,%3 \n\ + bne- 1b" + ISYNC_ON_SMP + : "=&r" (old), "=&r" (t) + : "r" (mask), "r" (p) + : "cc", "memory"); + + return (old & mask) != 0; +} + +static __inline__ int test_and_clear_bit(unsigned long nr, volatile void *addr) +{ + unsigned long old, t; + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + + __asm__ __volatile__( + EIEIO_ON_SMP +"1: ldarx %0,0,%3 # test_and_clear_bit\n\ + andc %1,%0,%2\n\ + stdcx. %1,0,%3\n\ + bne- 1b" + ISYNC_ON_SMP + : "=&r" (old), "=&r" (t) + : "r" (mask), "r" (p) + : "cc", "memory"); + + return (old & mask) != 0; +} + +static __inline__ int test_and_change_bit(unsigned long nr, volatile void *addr) +{ + unsigned long old, t; + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + + __asm__ __volatile__( + EIEIO_ON_SMP +"1: ldarx %0,0,%3 # test_and_change_bit\n\ + xor %1,%0,%2\n\ + stdcx. %1,0,%3\n\ + bne- 1b" + ISYNC_ON_SMP + : "=&r" (old), "=&r" (t) + : "r" (mask), "r" (p) + : "cc", "memory"); + + return (old & mask) != 0; +} + +/* + * non-atomic versions + */ +static __inline__ void __set_bit(unsigned long nr, volatile void *addr) +{ + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + + *p |= mask; +} + +static __inline__ void __clear_bit(unsigned long nr, volatile void *addr) +{ + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + + *p &= ~mask; +} + +static __inline__ void __change_bit(unsigned long nr, volatile void *addr) +{ + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + + *p ^= mask; +} + +static __inline__ int __test_and_set_bit(unsigned long nr, volatile void *addr) +{ + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + unsigned long old = *p; + + *p = old | mask; + return (old & mask) != 0; +} + +static __inline__ int __test_and_clear_bit(unsigned long nr, volatile void *addr) +{ + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + unsigned long old = *p; + + *p = old & ~mask; + return (old & mask) != 0; +} + +static __inline__ int __test_and_change_bit(unsigned long nr, volatile void *addr) +{ + unsigned long mask = 1UL << (nr & 0x3f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 6); + unsigned long old = *p; + + *p = old ^ mask; + return (old & mask) != 0; +} + +/* + * Return the zero-based bit position (from RIGHT TO LEFT, 63 -> 0) of the + * most significant (left-most) 1-bit in a double word. + */ +static __inline__ int __ilog2(unsigned long x) +{ + int lz; + + asm ("cntlzd %0,%1" : "=r" (lz) : "r" (x)); + return 63 - lz; +} + +/* + * Determines the bit position of the least significant (rightmost) 0 bit + * in the specified double word. The returned bit position will be zero-based, + * starting from the right side (63 - 0). + */ +static __inline__ unsigned long ffz(unsigned long x) +{ + /* no zero exists anywhere in the 8 byte area. */ + if ((x = ~x) == 0) + return 64; + + /* + * Calculate the bit position of the least signficant '1' bit in x + * (since x has been changed this will actually be the least signficant + * '0' bit in * the original x). Note: (x & -x) gives us a mask that + * is the least significant * (RIGHT-most) 1-bit of the value in x. + */ + return __ilog2(x & -x); +} + +static __inline__ int __ffs(unsigned long x) +{ + return __ilog2(x & -x); +} + +/* + * ffs: find first bit set. This is defined the same way as + * the libc and compiler builtin ffs routines, therefore + * differs in spirit from the above ffz (man ffs). + */ +static __inline__ int ffs(int x) +{ + unsigned long i = (unsigned long)x; + return __ilog2(i & -i) + 1; +} + +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ +#define hweight32(x) generic_hweight32(x) +#define hweight16(x) generic_hweight16(x) +#define hweight8(x) generic_hweight8(x) + +extern unsigned long find_next_zero_bit(void *addr, unsigned long size, unsigned long offset); +#define find_first_zero_bit(addr, size) \ + find_next_zero_bit((addr), (size), 0) + +extern unsigned long find_next_bit(void *addr, unsigned long size, unsigned long offset); +#define find_first_bit(addr, size) \ + find_next_bit((addr), (size), 0) + +extern unsigned long find_next_zero_le_bit(void *addr, unsigned long size, unsigned long offset); +#define find_first_zero_le_bit(addr, size) \ + find_next_zero_le_bit((addr), (size), 0) + +static __inline__ int test_le_bit(unsigned long nr, __const__ void * addr) +{ + __const__ unsigned char *ADDR = (__const__ unsigned char *) addr; + return (ADDR[nr >> 3] >> (nr & 7)) & 1; +} + +/* + * non-atomic versions + */ +static __inline__ void __set_le_bit(unsigned long nr, void *addr) +{ + unsigned char *ADDR = (unsigned char *)addr; + + ADDR += nr >> 3; + *ADDR |= 1 << (nr & 0x07); +} + +static __inline__ void __clear_le_bit(unsigned long nr, void *addr) +{ + unsigned char *ADDR = (unsigned char *)addr; + + ADDR += nr >> 3; + *ADDR &= ~(1 << (nr & 0x07)); +} + +static __inline__ int __test_and_set_le_bit(unsigned long nr, void *addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *)addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = (mask & *ADDR) != 0; + *ADDR |= mask; + return retval; +} + +static __inline__ int __test_and_clear_le_bit(unsigned long nr, void *addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *)addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = (mask & *ADDR) != 0; + *ADDR &= ~mask; + return retval; +} + +#define ext2_set_bit __test_and_set_le_bit +#define ext2_clear_bit __test_and_clear_le_bit +#define ext2_test_bit test_le_bit +#define ext2_find_first_zero_bit find_first_zero_le_bit +#define ext2_find_next_zero_bit find_next_zero_le_bit + +#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_test_bit(nr,addr) test_bit(nr,addr) +#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) + +#endif /* __KERNEL__ */ +#endif /* _PPC64_BITOPS_H */ diff -Nru a/include/asm-ppc64/bootinfo.h b/include/asm-ppc64/bootinfo.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/bootinfo.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,70 @@ +/* + * Non-machine dependent bootinfo structure. Basic idea + * borrowed from the m68k. + * + * Copyright (C) 1999 Cort Dougan + * Copyright (c) 2001 PPC64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + + +#ifndef _PPC64_BOOTINFO_H +#define _PPC64_BOOTINFO_H + +#include + +/* We use a u32 for the type of the fields since they're written by + * the bootloader which is a 32-bit process and read by the kernel + * which is a 64-bit process. This way they can both agree on the + * size of the type. + */ +typedef u32 bi_rec_field; + +struct bi_record { + bi_rec_field tag; /* tag ID */ + bi_rec_field size; /* size of record (in bytes) */ + bi_rec_field data[0]; /* data */ +}; + +#define BI_FIRST 0x1010 /* first record - marker */ +#define BI_LAST 0x1011 /* last record - marker */ +#define BI_CMD_LINE 0x1012 +#define BI_BOOTLOADER_ID 0x1013 +#define BI_INITRD 0x1014 +#define BI_SYSMAP 0x1015 +#define BI_MACHTYPE 0x1016 + +static __inline__ struct bi_record * bi_rec_init(unsigned long addr) +{ + struct bi_record *bi_recs; + bi_recs = (struct bi_record *)_ALIGN(addr, PAGE_SIZE); + bi_recs->size = 0; + return bi_recs; +} + +static __inline__ struct bi_record * bi_rec_alloc(struct bi_record *rec, + unsigned long args) +{ + rec = (struct bi_record *)((unsigned long)rec + rec->size); + rec->size = sizeof(struct bi_record) + args*sizeof(bi_rec_field); + return rec; +} + +static __inline__ struct bi_record * bi_rec_alloc_bytes(struct bi_record *rec, + unsigned long bytes) +{ + rec = (struct bi_record *)((unsigned long)rec + rec->size); + rec->size = sizeof(struct bi_record) + bytes; + return rec; +} + +static __inline__ struct bi_record * bi_rec_next(struct bi_record *rec) +{ + return (struct bi_record *)((unsigned long)rec + rec->size); +} + +#endif /* _PPC64_BOOTINFO_H */ diff -Nru a/include/asm-ppc64/bugs.h b/include/asm-ppc64/bugs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/bugs.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,8 @@ +/* + * This file is included by 'init/main.c' to check for architecture-dependent + * bugs. + * + */ + +static void check_bugs(void) { +} diff -Nru a/include/asm-ppc64/byteorder.h b/include/asm-ppc64/byteorder.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/byteorder.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,95 @@ +#ifndef _PPC64_BYTEORDER_H +#define _PPC64_BYTEORDER_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +#ifdef __GNUC__ +#ifdef __KERNEL__ + +static __inline__ __u16 ld_le16(const volatile __u16 *addr) +{ + __u16 val; + + __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (addr), "m" (*addr)); + return val; +} + +static __inline__ void st_le16(volatile __u16 *addr, const __u16 val) +{ + __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr)); +} + +static __inline__ __u32 ld_le32(const volatile __u32 *addr) +{ + __u32 val; + + __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (addr), "m" (*addr)); + return val; +} + +static __inline__ void st_le32(volatile __u32 *addr, const __u32 val) +{ + __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr)); +} + +#if 0 +static __inline__ __const__ __u16 ___arch__swab16(__u16 value) +{ + __u16 result; + + __asm__("rlwimi %0,%1,8,16,23" + : "=r" (result) + : "r" (value), "0" (value >> 8)); + return result; +} + +static __inline__ __const__ __u32 ___arch__swab32(__u32 value) +{ + __u32 result; + + __asm__("rlwimi %0,%1,24,16,23\n\t" + "rlwimi %0,%1,8,8,15\n\t" + "rlwimi %0,%1,24,0,7" + : "=r" (result) + : "r" (value), "0" (value >> 24)); + return result; +} + +static __inline__ __const__ __u64 ___arch__swab64(__u64 value) +{ + __u64 result; +#error implement me +} + +#define __arch__swab16(x) ___arch__swab16(x) +#define __arch__swab32(x) ___arch__swab32(x) +#define __arch__swab64(x) ___arch__swab64(x) + +#endif + +/* The same, but returns converted value from the location pointer by addr. */ +#define __arch__swab16p(addr) ld_le16(addr) +#define __arch__swab32p(addr) ld_le32(addr) + +/* The same, but do the conversion in situ, ie. put the value back to addr. */ +#define __arch__swab16s(addr) st_le16(addr,*addr) +#define __arch__swab32s(addr) st_le32(addr,*addr) + +#endif /* __KERNEL__ */ + +#ifndef __STRICT_ANSI__ +#define __BYTEORDER_HAS_U64__ +#endif + +#endif /* __GNUC__ */ + +#include + +#endif /* _PPC64_BYTEORDER_H */ diff -Nru a/include/asm-ppc64/cache.h b/include/asm-ppc64/cache.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/cache.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,13 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef __ARCH_PPC64_CACHE_H +#define __ARCH_PPC64_CACHE_H + +/* bytes per L1 cache line */ +#define L1_CACHE_BYTES 128 + +#endif diff -Nru a/include/asm-ppc64/checksum.h b/include/asm-ppc64/checksum.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/checksum.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,115 @@ +#ifndef _PPC64_CHECKSUM_H +#define _PPC64_CHECKSUM_H + +/* + * 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 is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. ihl is the number + * of 32-bit words and is always >= 5. + */ +extern unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl); + +/* + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented + */ +extern unsigned short csum_tcpudp_magic(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum); + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * returns a 32-bit number suitable for feeding into itself + * or csum_tcpudp_magic + * + * this function must be called with even lengths, except + * for the last fragment, which may be odd + * + * it's best to have buff aligned on a 32-bit boundary + */ +extern unsigned int csum_partial(const unsigned char * buff, int len, + unsigned int sum); + +/* + * the same as csum_partial, but copies from src to dst while it + * checksums + */ +unsigned int csum_partial_copy(const char *src, char *dst, + int len, unsigned int sum); + +extern unsigned int csum_partial_copy_generic(const char *src, char *dst, + int len, unsigned int sum, + int *src_err, int *dst_err); +/* + * the same as csum_partial, but copies from user space. + */ + +unsigned int csum_partial_copy_fromuser(const char *src, + char *dst, + int len, + unsigned int sum, + int *src_err); + +unsigned int csum_partial_copy_nocheck(const char *src, + char *dst, + int len, + unsigned int sum); + +/* + * turns a 32-bit partial checksum (e.g. from csum_partial) into a + * 1's complement 16-bit checksum. + */ +static inline unsigned int csum_fold(unsigned int sum) +{ + unsigned int tmp; + + /* swap the two 16-bit halves of sum */ + __asm__("rlwinm %0,%1,16,0,31" : "=r" (tmp) : "r" (sum)); + /* if there is a carry from adding the two 16-bit halves, + it will carry from the lower half into the upper half, + giving us the correct sum in the upper half. */ + sum = ~(sum + tmp) >> 16; + return sum; +} + +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ +static inline unsigned short ip_compute_csum(unsigned char * buff, int len) +{ + return csum_fold(csum_partial(buff, len, 0)); +} + +#define csum_partial_copy_from_user(src, dst, len, sum, errp) \ + csum_partial_copy_generic((src), (dst), (len), (sum), (errp), 0) + +#define csum_partial_copy_nocheck(src, dst, len, sum) \ + csum_partial_copy_generic((src), (dst), (len), (sum), 0, 0) + +static inline u32 csum_tcpudp_nofold(u32 saddr, + u32 daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + unsigned long s = sum; + + s += saddr; + s += daddr; + s += (proto << 16) + len; + s += (s >> 32); + return (u32) s; +} + +#endif diff -Nru a/include/asm-ppc64/current.h b/include/asm-ppc64/current.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/current.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,15 @@ +#ifndef _PPC64_CURRENT_H +#define _PPC64_CURRENT_H + +/* + * 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. + * + * Use r13 for current since the ppc64 ABI reserves it - Anton + */ + +register struct task_struct *current asm ("r13"); + +#endif /* !(_PPC64_CURRENT_H) */ diff -Nru a/include/asm-ppc64/delay.h b/include/asm-ppc64/delay.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/delay.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,46 @@ +#ifndef _PPC64_DELAY_H +#define _PPC64_DELAY_H + +/* + * Copyright 1996, Paul Mackerras. + * + * 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. + * + * PPC64 Support added by Dave Engebretsen, Todd Inglett, Mike Corrigan, + * Anton Blanchard. + */ + +extern unsigned long tb_ticks_per_usec; + +/* define these here to prevent circular dependencies */ +#define __HMT_low() asm volatile("or 1,1,1") +#define __HMT_medium() asm volatile("or 2,2,2") + +static inline unsigned long __get_tb(void) +{ + unsigned long rval; + + asm volatile("mftb %0" : "=r" (rval)); + return rval; +} + +static inline void __delay(unsigned long loops) +{ + unsigned long start = __get_tb(); + + while((__get_tb()-start) < loops) + __HMT_low(); +} + +static inline void udelay(unsigned long usecs) +{ + unsigned long loops = tb_ticks_per_usec * usecs; + + __delay(loops); + __HMT_medium(); +} + +#endif /* _PPC64_DELAY_H */ diff -Nru a/include/asm-ppc64/div64.h b/include/asm-ppc64/div64.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/div64.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,18 @@ +#ifndef __PPC_DIV64 +#define __PPC_DIV64 + +/* Copyright 2001 PPC64 Team, IBM Corp + * + * 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. + */ + +#define do_div(n,base) ({ \ + int __res; \ + __res = ((unsigned long) (n)) % (unsigned) (base); \ + (n) = ((unsigned long) (n)) / (unsigned) (base); \ + __res; }) + +#endif diff -Nru a/include/asm-ppc64/dma.h b/include/asm-ppc64/dma.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/dma.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,326 @@ +/* + * linux/include/asm/dma.h: Defines for using and allocating dma channels. + * Written by Hennus Bergman, 1992. + * High DMA channel support & info by Hannu Savolainen + * and John Boyd, Nov. 1992. + * Changes for ppc sound by Christoph Nadig + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _ASM_DMA_H +#define _ASM_DMA_H + +#include +#include +#include +#include + +#ifndef MAX_DMA_CHANNELS +#define MAX_DMA_CHANNELS 8 +#endif + +/* The maximum address that we can perform a DMA transfer to on this platform */ +/* Doesn't really apply... */ +#define MAX_DMA_ADDRESS (~0UL) + +#define dma_outb outb +#define dma_inb inb + +/* + * NOTES about DMA transfers: + * + * controller 1: channels 0-3, byte operations, ports 00-1F + * controller 2: channels 4-7, word operations, ports C0-DF + * + * - ALL registers are 8 bits only, regardless of transfer size + * - channel 4 is not used - cascades 1 into 2. + * - channels 0-3 are byte - addresses/counts are for physical bytes + * - channels 5-7 are word - addresses/counts are for physical words + * - transfers must not cross physical 64K (0-3) or 128K (5-7) boundaries + * - transfer count loaded to registers is 1 less than actual count + * - controller 2 offsets are all even (2x offsets for controller 1) + * - page registers for 5-7 don't use data bit 0, represent 128K pages + * - page registers for 0-3 use bit 0, represent 64K pages + * + * On PReP, DMA transfers are limited to the lower 16MB of _physical_ memory. + * On CHRP, the W83C553F (and VLSI Tollgate?) support full 32 bit addressing. + * Note that addresses loaded into registers must be _physical_ addresses, + * not logical addresses (which may differ if paging is active). + * + * Address mapping for channels 0-3: + * + * A23 ... A16 A15 ... A8 A7 ... A0 (Physical addresses) + * | ... | | ... | | ... | + * | ... | | ... | | ... | + * | ... | | ... | | ... | + * P7 ... P0 A7 ... A0 A7 ... A0 + * | Page | Addr MSB | Addr LSB | (DMA registers) + * + * Address mapping for channels 5-7: + * + * A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0 (Physical addresses) + * | ... | \ \ ... \ \ \ ... \ \ + * | ... | \ \ ... \ \ \ ... \ (not used) + * | ... | \ \ ... \ \ \ ... \ + * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0 + * | Page | Addr MSB | Addr LSB | (DMA registers) + * + * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses + * and counts _must_ be word-aligned (the lowest address bit is _ignored_ at + * the hardware level, so odd-byte transfers aren't possible). + * + * Transfer count (_not # bytes_) is limited to 64K, represented as actual + * count - 1 : 64K => 0xFFFF, 1 => 0x0000. Thus, count is always 1 or more, + * and up to 128K bytes may be transferred on channels 5-7 in one operation. + * + */ + +/* 8237 DMA controllers */ +#define IO_DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */ +#define IO_DMA2_BASE 0xC0 /* 16 bit master DMA, ch 4(=slave input)..7 */ + +/* DMA controller registers */ +#define DMA1_CMD_REG 0x08 /* command register (w) */ +#define DMA1_STAT_REG 0x08 /* status register (r) */ +#define DMA1_REQ_REG 0x09 /* request register (w) */ +#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */ +#define DMA1_MODE_REG 0x0B /* mode register (w) */ +#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */ +#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ +#define DMA1_RESET_REG 0x0D /* Master Clear (w) */ +#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ +#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ + +#define DMA2_CMD_REG 0xD0 /* command register (w) */ +#define DMA2_STAT_REG 0xD0 /* status register (r) */ +#define DMA2_REQ_REG 0xD2 /* request register (w) */ +#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */ +#define DMA2_MODE_REG 0xD6 /* mode register (w) */ +#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */ +#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ +#define DMA2_RESET_REG 0xDA /* Master Clear (w) */ +#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ +#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ + +#define DMA_ADDR_0 0x00 /* DMA address registers */ +#define DMA_ADDR_1 0x02 +#define DMA_ADDR_2 0x04 +#define DMA_ADDR_3 0x06 +#define DMA_ADDR_4 0xC0 +#define DMA_ADDR_5 0xC4 +#define DMA_ADDR_6 0xC8 +#define DMA_ADDR_7 0xCC + +#define DMA_CNT_0 0x01 /* DMA count registers */ +#define DMA_CNT_1 0x03 +#define DMA_CNT_2 0x05 +#define DMA_CNT_3 0x07 +#define DMA_CNT_4 0xC2 +#define DMA_CNT_5 0xC6 +#define DMA_CNT_6 0xCA +#define DMA_CNT_7 0xCE + +#define DMA_LO_PAGE_0 0x87 /* DMA page registers */ +#define DMA_LO_PAGE_1 0x83 +#define DMA_LO_PAGE_2 0x81 +#define DMA_LO_PAGE_3 0x82 +#define DMA_LO_PAGE_5 0x8B +#define DMA_LO_PAGE_6 0x89 +#define DMA_LO_PAGE_7 0x8A + +#define DMA_HI_PAGE_0 0x487 /* DMA page registers */ +#define DMA_HI_PAGE_1 0x483 +#define DMA_HI_PAGE_2 0x481 +#define DMA_HI_PAGE_3 0x482 +#define DMA_HI_PAGE_5 0x48B +#define DMA_HI_PAGE_6 0x489 +#define DMA_HI_PAGE_7 0x48A + +#define DMA1_EXT_REG 0x40B +#define DMA2_EXT_REG 0x4D6 + +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ +#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */ + +#define DMA_AUTOINIT 0x10 + +extern spinlock_t dma_spin_lock; + +static __inline__ unsigned long claim_dma_lock(void) +{ + unsigned long flags; + spin_lock_irqsave(&dma_spin_lock, flags); + return flags; +} + +static __inline__ void release_dma_lock(unsigned long flags) +{ + spin_unlock_irqrestore(&dma_spin_lock, flags); +} + +/* enable/disable a specific DMA channel */ +static __inline__ void enable_dma(unsigned int dmanr) +{ + unsigned char ucDmaCmd=0x00; + + if (dmanr != 4) + { + dma_outb(0, DMA2_MASK_REG); /* This may not be enabled */ + dma_outb(ucDmaCmd, DMA2_CMD_REG); /* Enable group */ + } + if (dmanr<=3) + { + dma_outb(dmanr, DMA1_MASK_REG); + dma_outb(ucDmaCmd, DMA1_CMD_REG); /* Enable group */ + } else + { + dma_outb(dmanr & 3, DMA2_MASK_REG); + } +} + +static __inline__ void disable_dma(unsigned int dmanr) +{ + if (dmanr<=3) + dma_outb(dmanr | 4, DMA1_MASK_REG); + else + dma_outb((dmanr & 3) | 4, DMA2_MASK_REG); +} + +/* Clear the 'DMA Pointer Flip Flop'. + * Write 0 for LSB/MSB, 1 for MSB/LSB access. + * Use this once to initialize the FF to a known state. + * After that, keep track of it. :-) + * --- In order to do that, the DMA routines below should --- + * --- only be used while interrupts are disabled! --- + */ +static __inline__ void clear_dma_ff(unsigned int dmanr) +{ + if (dmanr<=3) + dma_outb(0, DMA1_CLEAR_FF_REG); + else + dma_outb(0, DMA2_CLEAR_FF_REG); +} + +/* set mode (above) for a specific DMA channel */ +static __inline__ void set_dma_mode(unsigned int dmanr, char mode) +{ + if (dmanr<=3) + dma_outb(mode | dmanr, DMA1_MODE_REG); + else + dma_outb(mode | (dmanr&3), DMA2_MODE_REG); +} + +/* Set only the page register bits of the transfer address. + * This is used for successive transfers when we know the contents of + * the lower 16 bits of the DMA current address register, but a 64k boundary + * may have been crossed. + */ +static __inline__ void set_dma_page(unsigned int dmanr, int pagenr) +{ + switch(dmanr) { + case 0: + dma_outb(pagenr, DMA_LO_PAGE_0); + dma_outb(pagenr>>8, DMA_HI_PAGE_0); + break; + case 1: + dma_outb(pagenr, DMA_LO_PAGE_1); + dma_outb(pagenr>>8, DMA_HI_PAGE_1); + break; + case 2: + dma_outb(pagenr, DMA_LO_PAGE_2); + dma_outb(pagenr>>8, DMA_HI_PAGE_2); + break; + case 3: + dma_outb(pagenr, DMA_LO_PAGE_3); + dma_outb(pagenr>>8, DMA_HI_PAGE_3); + break; + case 5: + dma_outb(pagenr & 0xfe, DMA_LO_PAGE_5); + dma_outb(pagenr>>8, DMA_HI_PAGE_5); + break; + case 6: + dma_outb(pagenr & 0xfe, DMA_LO_PAGE_6); + dma_outb(pagenr>>8, DMA_HI_PAGE_6); + break; + case 7: + dma_outb(pagenr & 0xfe, DMA_LO_PAGE_7); + dma_outb(pagenr>>8, DMA_HI_PAGE_7); + break; + } +} + + +/* Set transfer address & page bits for specific DMA channel. + * Assumes dma flipflop is clear. + */ +static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int phys) +{ + if (dmanr <= 3) { + dma_outb( phys & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE ); + dma_outb( (phys>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE ); + } else { + dma_outb( (phys>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE ); + dma_outb( (phys>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE ); + } + set_dma_page(dmanr, phys>>16); +} + + +/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for + * a specific DMA channel. + * You must ensure the parameters are valid. + * NOTE: from a manual: "the number of transfers is one more + * than the initial word count"! This is taken into account. + * Assumes dma flip-flop is clear. + * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7. + */ +static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count) +{ + count--; + if (dmanr <= 3) { + dma_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE ); + dma_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE ); + } else { + dma_outb( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE ); + dma_outb( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE ); + } +} + + +/* Get DMA residue count. After a DMA transfer, this + * should return zero. Reading this while a DMA transfer is + * still in progress will return unpredictable results. + * If called before the channel has been used, it may return 1. + * Otherwise, it returns the number of _bytes_ left to transfer. + * + * Assumes DMA flip-flop is clear. + */ +static __inline__ int get_dma_residue(unsigned int dmanr) +{ + unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE + : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE; + + /* using short to get 16-bit wrap around */ + unsigned short count; + + count = 1 + dma_inb(io_port); + count += dma_inb(io_port) << 8; + + return (dmanr <= 3)? count : (count<<1); +} + +/* These are in kernel/dma.c: */ +extern int request_dma(unsigned int dmanr, const char * device_id); /* reserve a DMA channel */ +extern void free_dma(unsigned int dmanr); /* release it again */ + +#ifdef CONFIG_PCI +extern int isa_dma_bridge_buggy; +#else +#define isa_dma_bridge_buggy (0) +#endif +#endif /* _ASM_DMA_H */ diff -Nru a/include/asm-ppc64/eeh.h b/include/asm-ppc64/eeh.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/eeh.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,150 @@ +/* + * eeh.h + * Copyright (C) 2001 Dave Engebretsen & Todd Inglett IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Start Change Log + * 2001/10/27 : engebret : Created. + * End Change Log + */ + +#ifndef _EEH_H +#define _EEH_H + +struct pci_dev; + +#define IO_UNMAPPED_REGION_ID 0xaUL + +#define IO_TOKEN_TO_ADDR(token) ((((unsigned long)(token)) & 0xFFFFFFFF) | (0xEUL << 60)) +/* Flag bits encoded in the 3 unused function bits of devfn */ +#define EEH_TOKEN_DISABLED (1UL << 34UL) /* eeh is disabled for this token */ +#define IS_EEH_TOKEN_DISABLED(token) ((unsigned long)(token) & EEH_TOKEN_DISABLED) + +#define EEH_STATE_OVERRIDE 1 /* IOA does not require eeh traps */ +#define EEH_STATE_FAILURE 16 /* */ + +/* This is for profiling only and should be removed */ +extern unsigned long eeh_total_mmio_reads; +extern unsigned long eeh_total_mmio_ffs; + +void eeh_init(void); +int eeh_get_state(unsigned long ea); +unsigned long eeh_check_failure(void *token, unsigned long val); + +#define EEH_DISABLE 0 +#define EEH_ENABLE 1 +#define EEH_RELEASE_LOADSTORE 2 +#define EEH_RELEASE_DMA 3 +int eeh_set_option(struct pci_dev *dev, int options); + +/* Given a PCI device check if eeh should be configured or not. + * This may look at firmware properties and/or kernel cmdline options. + */ +int is_eeh_configured(struct pci_dev *dev); + +/* Generate an EEH token. + * The high nibble of the offset is cleared, otherwise bounds checking is performed. + * Use IO_TOKEN_TO_ADDR(token) to translate this token back to a mapped virtual addr. + * Do NOT do this to perform IO -- use the read/write macros! + */ +unsigned long eeh_token(unsigned long phb, + unsigned long bus, + unsigned long devfn, + unsigned long offset); + +extern void *memcpy(void *, const void *, unsigned long); +extern void *memset(void *,int, unsigned long); + +/* EEH_POSSIBLE_ERROR() -- test for possible MMIO failure. + * + * Order this macro for performance. + * If EEH is off for a device and it is a memory BAR, ioremap will + * map it to the IOREGION. In this case addr == vaddr and since these + * should be in registers we compare them first. Next we check for + * all ones which is perhaps fastest as ~val. Finally we weed out + * EEH disabled IO BARs. + * + * If this macro yields TRUE, the caller relays to eeh_check_failure() + * which does further tests out of line. + */ +/* #define EEH_POSSIBLE_ERROR(addr, vaddr, val) ((vaddr) != (addr) && ~(val) == 0 && !IS_EEH_TOKEN_DISABLED(addr)) */ +/* This version is rearranged to collect some profiling data */ +#define EEH_POSSIBLE_ERROR(addr, vaddr, val) (++eeh_total_mmio_reads, (~(val) == 0 && (++eeh_total_mmio_ffs, (vaddr) != (addr) && !IS_EEH_TOKEN_DISABLED(addr)))) + +/* + * MMIO read/write operations with EEH support. + * + * addr: 64b token of the form 0xA0PPBBDDyyyyyyyy + * 0xA0 : Unmapped MMIO region + * PP : PHB index (starting at zero) + * BB : PCI Bus number under given PHB + * DD : PCI devfn under given bus + * yyyyyyyy : Virtual address offset + * + * An actual virtual address is produced from this token + * by masking into the form: + * 0xE0000000yyyyyyyy + */ +static inline u8 eeh_readb(void *addr) { + volatile u8 *vaddr = (volatile u8 *)IO_TOKEN_TO_ADDR(addr); + u8 val = in_8(vaddr); + if (EEH_POSSIBLE_ERROR(addr, vaddr, val)) + return eeh_check_failure(addr, val); + return val; +} +static inline void eeh_writeb(u8 val, void *addr) { + volatile u8 *vaddr = (volatile u8 *)IO_TOKEN_TO_ADDR(addr); + out_8(vaddr, val); +} +static inline u16 eeh_readw(void *addr) { + volatile u16 *vaddr = (volatile u16 *)IO_TOKEN_TO_ADDR(addr); + u16 val = in_le16(vaddr); + if (EEH_POSSIBLE_ERROR(addr, vaddr, val)) + return eeh_check_failure(addr, val); + return val; +} +static inline void eeh_writew(u16 val, void *addr) { + volatile u16 *vaddr = (volatile u16 *)IO_TOKEN_TO_ADDR(addr); + out_le16(vaddr, val); +} +static inline u32 eeh_readl(void *addr) { + volatile u32 *vaddr = (volatile u32 *)IO_TOKEN_TO_ADDR(addr); + u32 val = in_le32(vaddr); + if (EEH_POSSIBLE_ERROR(addr, vaddr, val)) + return eeh_check_failure(addr, val); + return val; +} +static inline void eeh_writel(u32 val, void *addr) { + volatile u32 *vaddr = (volatile u32 *)IO_TOKEN_TO_ADDR(addr); + out_le32(vaddr, val); +} + +static inline void eeh_memset_io(void *addr, int c, unsigned long n) { + void *vaddr = (void *)IO_TOKEN_TO_ADDR(addr); + memset(vaddr, c, n); +} +static inline void eeh_memcpy_fromio(void *dest, void *src, unsigned long n) { + void *vsrc = (void *)IO_TOKEN_TO_ADDR(src); + memcpy(dest, vsrc, n); + /* look for ffff's here at dest[n] */ +} +static inline void eeh_memcpy_toio(void *dest, void *src, unsigned long n) { + void *vdest = (void *)IO_TOKEN_TO_ADDR(dest); + memcpy(vdest, src, n); +} + +#endif /* _EEH_H */ diff -Nru a/include/asm-ppc64/elf.h b/include/asm-ppc64/elf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/elf.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,140 @@ +#ifndef __PPC64_ELF_H +#define __PPC64_ELF_H + +/* + * ELF register definitions.. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include + +#define ELF_NGREG 48 /* includes nip, msr, lr, etc. */ +#define ELF_NFPREG 33 /* includes fpscr */ +#define ELF_NVRREG 33 /* includes vscr */ + +typedef unsigned long elf_greg_t64; +typedef elf_greg_t64 elf_gregset_t64[ELF_NGREG]; + +typedef unsigned int elf_greg_t32; +typedef elf_greg_t32 elf_gregset_t32[ELF_NGREG]; + +/* + * These are used to set parameters in the core dumps. + */ +#ifndef ELF_ARCH +# define ELF_ARCH EM_PPC64 +# define ELF_CLASS ELFCLASS64 +# define ELF_DATA ELFDATA2MSB + typedef elf_greg_t64 elf_greg_t; + typedef elf_gregset_t64 elf_gregset_t; +# define elf_addr_t unsigned long +# define elf_caddr_t char * +#else + /* Assumption: ELF_ARCH == EM_PPC and ELF_CLASS == ELFCLASS32 */ + typedef elf_greg_t32 elf_greg_t; + typedef elf_gregset_t32 elf_gregset_t; +# define elf_addr_t u32 +# define elf_caddr_t u32 +#endif + +typedef double elf_fpreg_t; +typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(x) ((x)->e_machine == ELF_ARCH) + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +/* This is the location that an ET_DYN program is loaded if exec'ed. Typical + use of this is to invoke "./ld.so someprog" to test out a new version of + the loader. We need to make sure that it is out of the way of the program + that it will "exec", and that there is sufficient room for the brk. */ + +#define ELF_ET_DYN_BASE (0x08000000) + +/* Common routine for both 32-bit and 64-bit processes */ +#define ELF_CORE_COPY_REGS(gregs, regs) elf_core_copy_regs(gregs, regs); +static inline void +elf_core_copy_regs(elf_gregset_t dstRegs, struct pt_regs* srcRegs) +{ + int i; + + int numGPRS = ((sizeof(struct pt_regs)/sizeof(elf_greg_t64)) < ELF_NGREG) ? (sizeof(struct pt_regs)/sizeof(elf_greg_t64)) : ELF_NGREG; + + for (i=0; i < numGPRS; i++) + dstRegs[i] = (elf_greg_t)((elf_greg_t64 *)srcRegs)[i]; +} + +/* This yields a mask that user programs can use to figure out what + instruction set this cpu supports. This could be done in userspace, + but it's not easy, and we've already done it here. */ + +#define ELF_HWCAP (0) + +/* This yields a string that ld.so will use to load implementation + specific libraries for optimization. This is more specific in + intent than poking at uname or /proc/cpuinfo. + + For the moment, we have only optimizations for the Intel generations, + but that could change... */ + +#define ELF_PLATFORM (NULL) + +#ifdef __KERNEL__ +#define SET_PERSONALITY(ex, ibcs2) \ +do { if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \ + set_thread_flag(TIF_32BIT); \ + else \ + clear_thread_flag(TIF_32BIT); \ + if (ibcs2) \ + set_personality(PER_SVR4); \ + else if (current->personality != PER_LINUX32) \ + set_personality(PER_LINUX); \ +} while (0) +#endif + +/* + * We need to put in some extra aux table entries to tell glibc what + * the cache block size is, so it can use the dcbz instruction safely. + */ +#define AT_DCACHEBSIZE 19 +#define AT_ICACHEBSIZE 20 +#define AT_UCACHEBSIZE 21 +/* A special ignored type value for PPC, for glibc compatibility. */ +#define AT_IGNOREPPC 22 + +extern int dcache_bsize; +extern int icache_bsize; +extern int ucache_bsize; + +/* + * The requirements here are: + * - keep the final alignment of sp (sp & 0xf) + * - make sure the 32-bit value at the first 16 byte aligned position of + * AUXV is greater than 16 for glibc compatibility. + * AT_IGNOREPPC is used for that. + * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC, + * even if DLINFO_ARCH_ITEMS goes to zero or is undefined. + */ +#define DLINFO_ARCH_ITEMS 3 +#define ARCH_DLINFO \ +do { \ + sp -= DLINFO_ARCH_ITEMS * 2; \ + NEW_AUX_ENT(0, AT_DCACHEBSIZE, dcache_bsize); \ + NEW_AUX_ENT(1, AT_ICACHEBSIZE, icache_bsize); \ + NEW_AUX_ENT(2, AT_UCACHEBSIZE, ucache_bsize); \ + /* \ + * Now handle glibc compatibility. \ + */ \ + sp -= 2*2; \ + NEW_AUX_ENT(0, AT_IGNOREPPC, AT_IGNOREPPC); \ + NEW_AUX_ENT(1, AT_IGNOREPPC, AT_IGNOREPPC); \ + } while (0) + +#endif /* __PPC64_ELF_H */ diff -Nru a/include/asm-ppc64/errno.h b/include/asm-ppc64/errno.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/errno.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,145 @@ +#ifndef _PPC64_ERRNO_H +#define _PPC64_ERRNO_H + +/* + * 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. + */ + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ +#define EDEADLOCK 58 /* File locking deadlock error */ +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + +/* Should never be seen by user programs */ +#define ERESTARTSYS 512 +#define ERESTARTNOINTR 513 +#define ERESTARTNOHAND 514 /* restart if no handler.. */ +#define ENOIOCTLCMD 515 /* No ioctl command */ + +#define _LAST_ERRNO 515 + +#endif diff -Nru a/include/asm-ppc64/fcntl.h b/include/asm-ppc64/fcntl.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/fcntl.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,99 @@ +#ifndef _PPC64_FCNTL_H +#define _PPC64_FCNTL_H + +/* + * 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. + */ + +/* open/fcntl - O_SYNC is only implemented on blocks devices and on files + located on an ext2 file system */ +#define O_ACCMODE 0003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 +#define O_CREAT 0100 /* not fcntl */ +#define O_EXCL 0200 /* not fcntl */ +#define O_NOCTTY 0400 /* not fcntl */ +#define O_TRUNC 01000 /* not fcntl */ +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_NDELAY O_NONBLOCK +#define O_SYNC 010000 +#define FASYNC 020000 /* fcntl, for BSD compatibility */ +#define O_DIRECTORY 040000 /* must be a directory */ +#define O_NOFOLLOW 0100000 /* don't follow links */ +#define O_LARGEFILE 0200000 +#define O_DIRECT 0400000 /* direct disk access hint - currently ignored */ + +#define F_DUPFD 0 /* dup */ +#define F_GETFD 1 /* get close_on_exec */ +#define F_SETFD 2 /* set/clear close_on_exec */ +#define F_GETFL 3 /* get file->f_flags */ +#define F_SETFL 4 /* set file->f_flags */ +#define F_GETLK 5 +#define F_SETLK 6 +#define F_SETLKW 7 + +#define F_SETOWN 8 /* for sockets. */ +#define F_GETOWN 9 /* for sockets. */ +#define F_SETSIG 10 /* for sockets. */ +#define F_GETSIG 11 /* for sockets. */ + +#define F_GETLK64 12 /* using 'struct flock64' */ +#define F_SETLK64 13 +#define F_SETLKW64 14 + +/* for F_[GET|SET]FL */ +#define FD_CLOEXEC 1 /* actually anything with low bit set goes */ + +/* for posix fcntl() and lockf() */ +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +/* for old implementation of bsd flock () */ +#define F_EXLCK 4 /* or 3 */ +#define F_SHLCK 8 /* or 4 */ + +/* for leases */ +#define F_INPROGRESS 16 + +/* operations for bsd flock(), also used by the kernel implementation */ +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* or'd with one of the above to prevent + blocking */ +#define LOCK_UN 8 /* remove lock */ + +#define LOCK_MAND 32 /* This is a mandatory flock */ +#define LOCK_READ 64 /* ... Which allows concurrent read operations */ +#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */ +#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */ + +#ifdef __KERNEL__ +#define F_POSIX 1 +#define F_FLOCK 2 +#define F_BROKEN 4 /* broken flock() emulation */ +#endif /* __KERNEL__ */ + +struct flock { + short l_type; + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; +}; + +struct flock64 { + short l_type; + short l_whence; + loff_t l_start; + loff_t l_len; + pid_t l_pid; +}; + +#define F_LINUX_SPECIFIC_BASE 1024 +#endif /* _PPC64_FCNTL_H */ diff -Nru a/include/asm-ppc64/floppy.h b/include/asm-ppc64/floppy.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/floppy.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,107 @@ +/* + * Architecture specific parts of the Floppy driver + * + * 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 archive + * for more details. + * + * Copyright (C) 1995 + */ +#ifndef __ASM_PPC64_FLOPPY_H +#define __ASM_PPC64_FLOPPY_H + +#include + +#define fd_inb(port) inb_p(port) +#define fd_outb(port,value) outb_p(port,value) + +#define fd_enable_dma() enable_dma(FLOPPY_DMA) +#define fd_disable_dma() disable_dma(FLOPPY_DMA) +#define fd_request_dma() request_dma(FLOPPY_DMA,"floppy") +#define fd_free_dma() free_dma(FLOPPY_DMA) +#define fd_clear_dma_ff() clear_dma_ff(FLOPPY_DMA) +#define fd_set_dma_mode(mode) set_dma_mode(FLOPPY_DMA,mode) +#define fd_set_dma_count(count) set_dma_count(FLOPPY_DMA,count) +#define fd_enable_irq() enable_irq(FLOPPY_IRQ) +#define fd_disable_irq() disable_irq(FLOPPY_IRQ) +#define fd_cacheflush(addr,size) /* nothing */ +#define fd_request_irq() request_irq(FLOPPY_IRQ, floppy_interrupt, \ + SA_INTERRUPT|SA_SAMPLE_RANDOM, \ + "floppy", NULL) +#define fd_free_irq() free_irq(FLOPPY_IRQ, NULL); + +#ifdef CONFIG_PCI + +#include + +#define fd_dma_setup(addr,size,mode,io) ppc64_fd_dma_setup(addr,size,mode,io) + +extern struct pci_dev *ppc64_floppy_dev; + +static __inline__ int +ppc64_fd_dma_setup(char *addr, unsigned long size, int mode, int io) +{ + static unsigned long prev_size; + static dma_addr_t bus_addr = 0; + static char *prev_addr; + static int prev_dir; + int dir; + + dir = (mode == DMA_MODE_READ) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE; + + if (bus_addr + && (addr != prev_addr || size != prev_size || dir != prev_dir)) { + /* different from last time -- unmap prev */ + pci_unmap_single(ppc64_floppy_dev, bus_addr, prev_size, prev_dir); + bus_addr = 0; + } + + if (!bus_addr) /* need to map it */ { + bus_addr = pci_map_single(ppc64_floppy_dev, addr, size, dir); + } + + /* remember this one as prev */ + prev_addr = addr; + prev_size = size; + prev_dir = dir; + + fd_clear_dma_ff(); + fd_cacheflush(addr, size); + fd_set_dma_mode(mode); + set_dma_addr(FLOPPY_DMA, bus_addr); + fd_set_dma_count(size); + virtual_dma_port = io; + fd_enable_dma(); + + return 0; +} + +#endif /* CONFIG_PCI */ + +__inline__ void virtual_dma_init(void) +{ + /* Nothing to do on PowerPC */ +} + +static int FDC1 = 0x3f0; +static int FDC2 = -1; + +/* + * Again, the CMOS information not available + */ +#define FLOPPY0_TYPE 6 +#define FLOPPY1_TYPE 0 + +#define N_FDC 2 /* Don't change this! */ +#define N_DRIVE 8 + +#define FLOPPY_MOTOR_MASK 0xf0 + +/* + * The PowerPC has no problems with floppy DMA crossing 64k borders. + */ +#define CROSS_64KB(a,s) (0) + +#define EXTRA_FLOPPY_PARAMS + +#endif /* __ASM_PPC64_FLOPPY_H */ diff -Nru a/include/asm-ppc64/hardirq.h b/include/asm-ppc64/hardirq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/hardirq.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,101 @@ +#ifdef __KERNEL__ +#ifndef __ASM_HARDIRQ_H +#define __ASM_HARDIRQ_H + +/* + * Use a brlock for the global irq lock, based on sparc64. + * Anton Blanchard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include + + +typedef struct { + unsigned long __softirq_pending; +#ifndef CONFIG_SMP + unsigned int __local_irq_count; +#else + unsigned int __unused_on_SMP; /* We use brlocks on SMP */ +#endif + unsigned int __local_bh_count; + unsigned int __syscall_count; + unsigned long __unused; + struct task_struct * __ksoftirqd_task; +} ____cacheline_aligned irq_cpustat_t; + +#include /* Standard mappings for irq_cpustat_t above */ +/* Note that local_irq_count() is replaced by ppc64 specific version for SMP */ + +#ifndef CONFIG_SMP +#define irq_enter(cpu) (local_irq_count(cpu)++) +#define irq_exit(cpu) (local_irq_count(cpu)--) +#else +#undef local_irq_count +#define local_irq_count(cpu) (__brlock_array[cpu][BR_GLOBALIRQ_LOCK]) +#define irq_enter(cpu) br_read_lock(BR_GLOBALIRQ_LOCK) +#define irq_exit(cpu) br_read_unlock(BR_GLOBALIRQ_LOCK) +#endif + +/* + * Are we in an interrupt context? Either doing bottom half + * or hardware interrupt processing? + */ +#define in_interrupt() ({ int __cpu = smp_processor_id(); \ + (local_irq_count(__cpu) + local_bh_count(__cpu) != 0); }) + +/* This tests only the local processors hw IRQ context disposition. */ +#define in_irq() (local_irq_count(smp_processor_id()) != 0) + +#ifndef CONFIG_SMP + +#define hardirq_trylock(cpu) (local_irq_count(cpu) == 0) +#define hardirq_endlock(cpu) do { } while (0) + +#define synchronize_irq() barrier() +#define release_irqlock(cpu) do { } while (0) + +#else /* CONFIG_SMP */ + +static __inline__ int irqs_running(void) +{ + int i; + + for (i = 0; i < smp_num_cpus; i++) + if (local_irq_count(cpu_logical_map(i))) + return 1; + return 0; +} + +extern unsigned char global_irq_holder; + +static inline void release_irqlock(int cpu) +{ + /* if we didn't own the irq lock, just ignore... */ + if(global_irq_holder == (unsigned char) cpu) { + global_irq_holder = NO_PROC_ID; + br_write_unlock(BR_GLOBALIRQ_LOCK); + } +} + +static inline int hardirq_trylock(int cpu) +{ + spinlock_t *lock = &__br_write_locks[BR_GLOBALIRQ_LOCK].lock; + + return (!local_irq_count(cpu) && !spin_is_locked(lock)); +} + +#define hardirq_endlock(cpu) do { (void)(cpu); } while (0) + +extern void synchronize_irq(void); + +#endif /* CONFIG_SMP */ + +#endif /* __KERNEL__ */ +#endif /* __ASM_HARDIRQ_H */ diff -Nru a/include/asm-ppc64/hdreg.h b/include/asm-ppc64/hdreg.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/hdreg.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,22 @@ +/* + * linux/include/asm-ppc/hdreg.h + * + * Copyright (C) 1994-1996 Linus Torvalds & authors + * + * 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 file contains the ppc architecture specific IDE code. + */ + +#ifndef __ASMPPC64_HDREG_H +#define __ASMPPC64_HDREG_H + +typedef unsigned long ide_ioreg_t; + +#endif /* __ASMPPC64_HDREG_H */ + diff -Nru a/include/asm-ppc64/hw_irq.h b/include/asm-ppc64/hw_irq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/hw_irq.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,80 @@ +/* + * Copyright (C) 1999 Cort Dougan + * + * Use inline IRQs where possible - Anton Blanchard + * + * 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. + */ +#ifdef __KERNEL__ +#ifndef _PPC64_HW_IRQ_H +#define _PPC64_HW_IRQ_H + +#include +#include + +int timer_interrupt(struct pt_regs *); + +#ifdef CONFIG_PPC_ISERIES + +extern void __no_use_sti(void); +extern void __no_use_cli(void); +extern void __no_use_restore_flags(unsigned long); +extern unsigned long __no_use_save_flags(void); +extern void __no_use_set_lost(unsigned long); +extern void __no_lpq_restore_flags(unsigned long); + +#define __cli() __no_use_cli() +#define __sti() __no_use_sti() +#define __save_flags(flags) ((flags) = __no_use_save_flags()) +#define __restore_flags(flags) __no_use_restore_flags((unsigned long)flags) +#define __save_and_cli(flags) ({__save_flags(flags);__cli();}) + +#else + +#define __save_flags(flags) ((flags) = mfmsr()) +#define __restore_flags(flags) mtmsrd(flags) + +static inline void __cli(void) +{ + unsigned long msr; + msr = mfmsr(); + mtmsrd(msr & ~MSR_EE); + __asm__ __volatile__("": : :"memory"); +} + +static inline void __sti(void) +{ + unsigned long msr; + __asm__ __volatile__("": : :"memory"); + msr = mfmsr(); + mtmsrd(msr | MSR_EE); +} + +static inline void __do_save_and_cli(unsigned long *flags) +{ + unsigned long msr; + msr = mfmsr(); + *flags = msr; + mtmsrd(msr & ~MSR_EE); + __asm__ __volatile__("": : :"memory"); +} + +#define __save_and_cli(flags) __do_save_and_cli(&flags) + +#endif /* CONFIG_PPC_ISERIES */ + +#define mask_irq(irq) ({if (irq_desc[irq].handler && irq_desc[irq].handler->disable) irq_desc[irq].handler->disable(irq);}) +#define unmask_irq(irq) ({if (irq_desc[irq].handler && irq_desc[irq].handler->enable) irq_desc[irq].handler->enable(irq);}) +#define ack_irq(irq) ({if (irq_desc[irq].handler && irq_desc[irq].handler->ack) irq_desc[irq].handler->ack(irq);}) + +/* Should we handle this via lost interrupts and IPIs or should we don't care like + * we do now ? --BenH. + */ +struct hw_interrupt_type; +static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} + +#endif /* _PPC64_HW_IRQ_H */ +#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc64/iSeries/HvCall.h b/include/asm-ppc64/iSeries/HvCall.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvCall.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,209 @@ +/* + * HvCall.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//=========================================================================== +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from the OS. +// +//=========================================================================== + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +#include + +//------------------------------------------------------------------- +// Constants +//------------------------------------------------------------------- +#ifndef _HVCALL_H +#define _HVCALL_H +/* +enum HvCall_ReturnCode +{ + HvCall_Good = 0, + HvCall_Partial = 1, + HvCall_NotOwned = 2, + HvCall_NotFreed = 3, + HvCall_UnspecifiedError = 4 +}; + +enum HvCall_TypeOfSIT +{ + HvCall_ReduceOnly = 0, + HvCall_Unconditional = 1 +}; + +enum HvCall_TypeOfYield +{ + HvCall_YieldTimed = 0, // Yield until specified time + HvCall_YieldToActive = 1, // Yield until all active procs have run + HvCall_YieldToProc = 2 // Yield until the specified processor has run +}; + +enum HvCall_InterruptMasks +{ + HvCall_MaskIPI = 0x00000001, + HvCall_MaskLpEvent = 0x00000002, + HvCall_MaskLpProd = 0x00000004, + HvCall_MaskTimeout = 0x00000008 +}; + +enum HvCall_VaryOffChunkRc +{ + HvCall_VaryOffSucceeded = 0, + HvCall_VaryOffWithdrawn = 1, + HvCall_ChunkInLoadArea = 2, + HvCall_ChunkInHPT = 3, + HvCall_ChunkNotAccessible = 4, + HvCall_ChunkInUse = 5 +}; +*/ + +/* Type of yield for HvCallBaseYieldProcessor */ +#define HvCall_YieldTimed 0 // Yield until specified time (tb) +#define HvCall_YieldToActive 1 // Yield until all active procs have run +#define HvCall_YieldToProc 2 // Yield until the specified processor has run + +/* interrupt masks for setEnabledInterrupts */ +#define HvCall_MaskIPI 0x00000001 +#define HvCall_MaskLpEvent 0x00000002 +#define HvCall_MaskLpProd 0x00000004 +#define HvCall_MaskTimeout 0x00000008 + +/* Log buffer formats */ +#define HvCall_LogBuffer_ASCII 0 +#define HvCall_LogBuffer_EBCDIC 1 + +#define HvCallBaseAckDeferredInts HvCallBase + 0 +#define HvCallBaseCpmPowerOff HvCallBase + 1 +#define HvCallBaseGetHwPatch HvCallBase + 2 +#define HvCallBaseReIplSpAttn HvCallBase + 3 +#define HvCallBaseSetASR HvCallBase + 4 +#define HvCallBaseSetASRAndRfi HvCallBase + 5 +#define HvCallBaseSetIMR HvCallBase + 6 +#define HvCallBaseSendIPI HvCallBase + 7 +#define HvCallBaseTerminateMachine HvCallBase + 8 +#define HvCallBaseTerminateMachineSrc HvCallBase + 9 +#define HvCallBaseProcessPlicInterrupts HvCallBase + 10 +#define HvCallBaseIsPrimaryCpmOrMsdIpl HvCallBase + 11 +#define HvCallBaseSetVirtualSIT HvCallBase + 12 +#define HvCallBaseVaryOffThisProcessor HvCallBase + 13 +#define HvCallBaseVaryOffMemoryChunk HvCallBase + 14 +#define HvCallBaseVaryOffInteractivePercentage HvCallBase + 15 +#define HvCallBaseSendLpProd HvCallBase + 16 +#define HvCallBaseSetEnabledInterrupts HvCallBase + 17 +#define HvCallBaseYieldProcessor HvCallBase + 18 +#define HvCallBaseVaryOffSharedProcUnits HvCallBase + 19 +#define HvCallBaseSetVirtualDecr HvCallBase + 20 +#define HvCallBaseClearLogBuffer HvCallBase + 21 +#define HvCallBaseGetLogBufferCodePage HvCallBase + 22 +#define HvCallBaseGetLogBufferFormat HvCallBase + 23 +#define HvCallBaseGetLogBufferLength HvCallBase + 24 +#define HvCallBaseReadLogBuffer HvCallBase + 25 +#define HvCallBaseSetLogBufferFormatAndCodePage HvCallBase + 26 +#define HvCallBaseWriteLogBuffer HvCallBase + 27 +#define HvCallBaseRouter28 HvCallBase + 28 +#define HvCallBaseRouter29 HvCallBase + 29 +#define HvCallBaseRouter30 HvCallBase + 30 +//===================================================================================== +static inline void HvCall_setVirtualDecr(void) +{ + // Ignore any error return codes - most likely means that the target value for the + // LP has been increased and this vary off would bring us below the new target. + HvCall0(HvCallBaseSetVirtualDecr); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//===================================================================== +static inline void HvCall_yieldProcessor(unsigned typeOfYield, u64 yieldParm) +{ + HvCall2( HvCallBaseYieldProcessor, typeOfYield, yieldParm ); +} +//===================================================================== +static inline void HvCall_setEnabledInterrupts(u64 enabledInterrupts) +{ + HvCall1(HvCallBaseSetEnabledInterrupts,enabledInterrupts); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} + +//===================================================================== +static inline void HvCall_clearLogBuffer(HvLpIndex lpindex) +{ + HvCall1(HvCallBaseClearLogBuffer,lpindex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} + +//===================================================================== +static inline u32 HvCall_getLogBufferCodePage(HvLpIndex lpindex) +{ + u32 retVal = HvCall1(HvCallBaseGetLogBufferCodePage,lpindex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} + +//===================================================================== +static inline int HvCall_getLogBufferFormat(HvLpIndex lpindex) +{ + int retVal = HvCall1(HvCallBaseGetLogBufferFormat,lpindex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} + +//===================================================================== +static inline u32 HvCall_getLogBufferLength(HvLpIndex lpindex) +{ + u32 retVal = HvCall1(HvCallBaseGetLogBufferLength,lpindex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} + +//===================================================================== +static inline void HvCall_setLogBufferFormatAndCodepage(int format, u32 codePage) +{ + HvCall2(HvCallBaseSetLogBufferFormatAndCodePage,format, codePage); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} + +//===================================================================== +int HvCall_readLogBuffer(HvLpIndex lpindex, void *buffer, u64 bufLen); +void HvCall_writeLogBuffer(const void *buffer, u64 bufLen); + +//===================================================================== +static inline void HvCall_sendIPI(struct Paca * targetPaca) +{ + HvCall1( HvCallBaseSendIPI, targetPaca->xPacaIndex ); +} + +//===================================================================== +static inline void HvCall_terminateMachineSrc(void) +{ + HvCall0( HvCallBaseTerminateMachineSrc ); +} + + +#endif // _HVCALL_H + diff -Nru a/include/asm-ppc64/iSeries/HvCallCfg.h b/include/asm-ppc64/iSeries/HvCallCfg.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvCallCfg.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,219 @@ +/* + * HvCallCfg.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//===================================================================================== +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from the OS. +// +//===================================================================================== + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +//------------------------------------------------------------------------------------- +// Constants +//------------------------------------------------------------------------------------- +#ifndef _HVCALLCFG_H +#define _HVCALLCFG_H + +enum HvCallCfg_ReqQual +{ + HvCallCfg_Cur = 0, + HvCallCfg_Init = 1, + HvCallCfg_Max = 2, + HvCallCfg_Min = 3 +}; + +#define HvCallCfgGetLps HvCallCfg + 0 +#define HvCallCfgGetActiveLpMap HvCallCfg + 1 +#define HvCallCfgGetLpVrmIndex HvCallCfg + 2 +#define HvCallCfgGetLpMinSupportedPlicVrmIndex HvCallCfg + 3 +#define HvCallCfgGetLpMinCompatablePlicVrmIndex HvCallCfg + 4 +#define HvCallCfgGetLpVrmName HvCallCfg + 5 +#define HvCallCfgGetSystemPhysicalProcessors HvCallCfg + 6 +#define HvCallCfgGetPhysicalProcessors HvCallCfg + 7 +#define HvCallCfgGetSystemMsChunks HvCallCfg + 8 +#define HvCallCfgGetMsChunks HvCallCfg + 9 +#define HvCallCfgGetInteractivePercentage HvCallCfg + 10 +#define HvCallCfgIsBusDedicated HvCallCfg + 11 +#define HvCallCfgGetBusOwner HvCallCfg + 12 +#define HvCallCfgGetBusAllocation HvCallCfg + 13 +#define HvCallCfgGetBusUnitOwner HvCallCfg + 14 +#define HvCallCfgGetBusUnitAllocation HvCallCfg + 15 +#define HvCallCfgGetVirtualBusPool HvCallCfg + 16 +#define HvCallCfgGetBusUnitInterruptProc HvCallCfg + 17 +#define HvCallCfgGetConfiguredBusUnitsForIntProc HvCallCfg + 18 +#define HvCallCfgGetRioSanBusPool HvCallCfg + 19 +#define HvCallCfgGetSharedPoolIndex HvCallCfg + 20 +#define HvCallCfgGetSharedProcUnits HvCallCfg + 21 +#define HvCallCfgGetNumProcsInSharedPool HvCallCfg + 22 +#define HvCallCfgRouter23 HvCallCfg + 23 +#define HvCallCfgRouter24 HvCallCfg + 24 +#define HvCallCfgRouter25 HvCallCfg + 25 +#define HvCallCfgRouter26 HvCallCfg + 26 +#define HvCallCfgRouter27 HvCallCfg + 27 +#define HvCallCfgGetMinRuntimeMsChunks HvCallCfg + 28 +#define HvCallCfgSetMinRuntimeMsChunks HvCallCfg + 29 +#define HvCallCfgGetVirtualLanIndexMap HvCallCfg + 30 +#define HvCallCfgGetLpExecutionMode HvCallCfg + 31 +#define HvCallCfgGetHostingLpIndex HvCallCfg + 32 + +//==================================================================== +static inline HvLpIndex HvCallCfg_getLps(void) +{ + HvLpIndex retVal = HvCall0(HvCallCfgGetLps); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline int HvCallCfg_isBusDedicated(u64 busIndex) +{ + int retVal = HvCall1(HvCallCfgIsBusDedicated,busIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpIndex HvCallCfg_getBusOwner(u64 busIndex) +{ + HvLpIndex retVal = HvCall1(HvCallCfgGetBusOwner,busIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpIndexMap HvCallCfg_getBusAllocation(u64 busIndex) +{ + HvLpIndexMap retVal = HvCall1(HvCallCfgGetBusAllocation,busIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpIndexMap HvCallCfg_getActiveLpMap(void) +{ + HvLpIndexMap retVal = HvCall0(HvCallCfgGetActiveLpMap); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpVirtualLanIndexMap HvCallCfg_getVirtualLanIndexMap(HvLpIndex lp) +{ + // This is a new function in V5R1 so calls to this on older + // hypervisors will return -1 + u64 retVal = HvCall1(HvCallCfgGetVirtualLanIndexMap, lp); + if(retVal == -1) + retVal = 0; + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getSystemMsChunks(void) +{ + u64 retVal = HvCall0(HvCallCfgGetSystemMsChunks); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getMsChunks(HvLpIndex lp,enum HvCallCfg_ReqQual qual) +{ + u64 retVal = HvCall2(HvCallCfgGetMsChunks,lp,qual); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getMinRuntimeMsChunks(HvLpIndex lp) +{ + // NOTE: This function was added in v5r1 so older hypervisors will return a -1 value + u64 retVal = HvCall1(HvCallCfgGetMinRuntimeMsChunks,lp); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_setMinRuntimeMsChunks(u64 chunks) +{ + u64 retVal = HvCall1(HvCallCfgSetMinRuntimeMsChunks,chunks); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getSystemPhysicalProcessors(void) +{ + u64 retVal = HvCall0(HvCallCfgGetSystemPhysicalProcessors); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getPhysicalProcessors(HvLpIndex lp,enum HvCallCfg_ReqQual qual) +{ + u64 retVal = HvCall2(HvCallCfgGetPhysicalProcessors,lp,qual); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline u64 HvCallCfg_getConfiguredBusUnitsForInterruptProc(HvLpIndex lp, + u16 hvLogicalProcIndex) +{ + u64 retVal = HvCall2(HvCallCfgGetConfiguredBusUnitsForIntProc,lp,hvLogicalProcIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; + +} +//================================================================== +static inline HvLpSharedPoolIndex HvCallCfg_getSharedPoolIndex(HvLpIndex lp) +{ + HvLpSharedPoolIndex retVal = + HvCall1(HvCallCfgGetSharedPoolIndex,lp); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; + +} +//================================================================== +static inline u64 HvCallCfg_getSharedProcUnits(HvLpIndex lp,enum HvCallCfg_ReqQual qual) +{ + u64 retVal = HvCall2(HvCallCfgGetSharedProcUnits,lp,qual); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; + +} +//================================================================== +static inline u64 HvCallCfg_getNumProcsInSharedPool(HvLpSharedPoolIndex sPI) +{ + u16 retVal = HvCall1(HvCallCfgGetNumProcsInSharedPool,sPI); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; + +} +//================================================================== +static inline HvLpIndex HvCallCfg_getHostingLpIndex(HvLpIndex lp) +{ + u64 retVal = HvCall1(HvCallCfgGetHostingLpIndex,lp); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; + +} + +#endif // _HVCALLCFG_H + diff -Nru a/include/asm-ppc64/iSeries/HvCallEvent.h b/include/asm-ppc64/iSeries/HvCallEvent.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvCallEvent.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,335 @@ +/* + * HvCallEvent.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//================================================================== +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from the OS. +// +//================================================================== + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include +#endif + +#ifndef _HVTYPES_H +#include +#endif + +#include + +//------------------------------------------------------------------- +// Other Includes +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// Constants +//------------------------------------------------------------------- +#ifndef _HVCALLEVENT_H +#define _HVCALLEVENT_H + +struct HvLpEvent; + +typedef u8 HvLpEvent_Type; +typedef u8 HvLpEvent_AckInd; +typedef u8 HvLpEvent_AckType; + +struct HvCallEvent_PackedParms +{ + u8 xAckType:1; + u8 xAckInd:1; + u8 xRsvd:1; + u8 xTargetLp:5; + u8 xType; + u16 xSubtype; + HvLpInstanceId xSourceInstId; + HvLpInstanceId xTargetInstId; +}; + +typedef u8 HvLpDma_Direction; +typedef u8 HvLpDma_AddressType; + +struct HvCallEvent_PackedDmaParms +{ + u8 xDirection:1; + u8 xLocalAddrType:1; + u8 xRemoteAddrType:1; + u8 xRsvd1:5; + HvLpIndex xRemoteLp; + u8 xType; + u8 xRsvd2; + HvLpInstanceId xLocalInstId; + HvLpInstanceId xRemoteInstId; +}; + +typedef u64 HvLpEvent_Rc; +typedef u64 HvLpDma_Rc; + +#define HvCallEventAckLpEvent HvCallEvent + 0 +#define HvCallEventCancelLpEvent HvCallEvent + 1 +#define HvCallEventCloseLpEventPath HvCallEvent + 2 +#define HvCallEventDmaBufList HvCallEvent + 3 +#define HvCallEventDmaSingle HvCallEvent + 4 +#define HvCallEventDmaToSp HvCallEvent + 5 +#define HvCallEventGetOverflowLpEvents HvCallEvent + 6 +#define HvCallEventGetSourceLpInstanceId HvCallEvent + 7 +#define HvCallEventGetTargetLpInstanceId HvCallEvent + 8 +#define HvCallEventOpenLpEventPath HvCallEvent + 9 +#define HvCallEventSetLpEventStack HvCallEvent + 10 +#define HvCallEventSignalLpEvent HvCallEvent + 11 +#define HvCallEventSignalLpEventParms HvCallEvent + 12 +#define HvCallEventSetInterLpQueueIndex HvCallEvent + 13 +#define HvCallEventSetLpEventQueueInterruptProc HvCallEvent + 14 +#define HvCallEventRouter15 HvCallEvent + 15 + +//====================================================================== +static inline void HvCallEvent_getOverflowLpEvents(u8 queueIndex) +{ + HvCall1(HvCallEventGetOverflowLpEvents,queueIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//====================================================================== +static inline void HvCallEvent_setInterLpQueueIndex(u8 queueIndex) +{ + HvCall1(HvCallEventSetInterLpQueueIndex,queueIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//====================================================================== +static inline void HvCallEvent_setLpEventStack(u8 queueIndex, + char * eventStackAddr, + u32 eventStackSize) +{ + u64 abs_addr; + abs_addr = virt_to_absolute( (unsigned long) eventStackAddr ); + + HvCall3(HvCallEventSetLpEventStack, queueIndex, abs_addr, eventStackSize); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//====================================================================== +static inline void HvCallEvent_setLpEventQueueInterruptProc(u8 queueIndex, + u16 lpLogicalProcIndex) +{ + HvCall2(HvCallEventSetLpEventQueueInterruptProc,queueIndex,lpLogicalProcIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//===================================================================== +static inline HvLpEvent_Rc HvCallEvent_signalLpEvent(struct HvLpEvent* event) +{ + u64 abs_addr; + HvLpEvent_Rc retVal; +#ifdef DEBUG_SENDEVENT + printk("HvCallEvent_signalLpEvent: *event = %016lx\n ", (unsigned long)event); +#endif + abs_addr = virt_to_absolute( (unsigned long) event ); + retVal = (HvLpEvent_Rc)HvCall1(HvCallEventSignalLpEvent, abs_addr); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//===================================================================== +static inline HvLpEvent_Rc HvCallEvent_signalLpEventFast(HvLpIndex targetLp, + HvLpEvent_Type type, + u16 subtype, + HvLpEvent_AckInd ackInd, + HvLpEvent_AckType ackType, + HvLpInstanceId sourceInstanceId, + HvLpInstanceId targetInstanceId, + u64 correlationToken, + u64 eventData1, + u64 eventData2, + u64 eventData3, + u64 eventData4, + u64 eventData5) +{ + HvLpEvent_Rc retVal; + + // Pack the misc bits into a single Dword to pass to PLIC + union + { + struct HvCallEvent_PackedParms parms; + u64 dword; + } packed; + packed.parms.xAckType = ackType; + packed.parms.xAckInd = ackInd; + packed.parms.xRsvd = 0; + packed.parms.xTargetLp = targetLp; + packed.parms.xType = type; + packed.parms.xSubtype = subtype; + packed.parms.xSourceInstId = sourceInstanceId; + packed.parms.xTargetInstId = targetInstanceId; + + retVal = (HvLpEvent_Rc)HvCall7(HvCallEventSignalLpEventParms, + packed.dword, + correlationToken, + eventData1,eventData2, + eventData3,eventData4, + eventData5); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpEvent_Rc HvCallEvent_ackLpEvent(struct HvLpEvent* event) +{ + u64 abs_addr; + HvLpEvent_Rc retVal; + abs_addr = virt_to_absolute( (unsigned long) event ); + + retVal = (HvLpEvent_Rc)HvCall1(HvCallEventAckLpEvent, abs_addr); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//==================================================================== +static inline HvLpEvent_Rc HvCallEvent_cancelLpEvent(struct HvLpEvent* event) +{ + u64 abs_addr; + HvLpEvent_Rc retVal; + abs_addr = virt_to_absolute( (unsigned long) event ); + + retVal = (HvLpEvent_Rc)HvCall1(HvCallEventCancelLpEvent, abs_addr); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline HvLpInstanceId HvCallEvent_getSourceLpInstanceId(HvLpIndex targetLp, HvLpEvent_Type type) +{ + HvLpInstanceId retVal; + retVal = HvCall2(HvCallEventGetSourceLpInstanceId,targetLp,type); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline HvLpInstanceId HvCallEvent_getTargetLpInstanceId(HvLpIndex targetLp, HvLpEvent_Type type) +{ + HvLpInstanceId retVal; + retVal = HvCall2(HvCallEventGetTargetLpInstanceId,targetLp,type); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//=================================================================== +static inline void HvCallEvent_openLpEventPath(HvLpIndex targetLp, + HvLpEvent_Type type) +{ + HvCall2(HvCallEventOpenLpEventPath,targetLp,type); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//=================================================================== +static inline void HvCallEvent_closeLpEventPath(HvLpIndex targetLp, + HvLpEvent_Type type) +{ + HvCall2(HvCallEventCloseLpEventPath,targetLp,type); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//=================================================================== +static inline HvLpDma_Rc HvCallEvent_dmaBufList(HvLpEvent_Type type, + HvLpIndex remoteLp, + HvLpDma_Direction direction, + HvLpInstanceId localInstanceId, + HvLpInstanceId remoteInstanceId, + HvLpDma_AddressType localAddressType, + HvLpDma_AddressType remoteAddressType, + // Do these need to be converted to + // absolute addresses? + u64 localBufList, + u64 remoteBufList, + + u32 transferLength) +{ + HvLpDma_Rc retVal; + // Pack the misc bits into a single Dword to pass to PLIC + union + { + struct HvCallEvent_PackedDmaParms parms; + u64 dword; + } packed; + packed.parms.xDirection = direction; + packed.parms.xLocalAddrType = localAddressType; + packed.parms.xRemoteAddrType = remoteAddressType; + packed.parms.xRsvd1 = 0; + packed.parms.xRemoteLp = remoteLp; + packed.parms.xType = type; + packed.parms.xRsvd2 = 0; + packed.parms.xLocalInstId = localInstanceId; + packed.parms.xRemoteInstId = remoteInstanceId; + + retVal = (HvLpDma_Rc)HvCall4(HvCallEventDmaBufList, + packed.dword, + localBufList, + remoteBufList, + transferLength); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//================================================================= +static inline HvLpDma_Rc HvCallEvent_dmaSingle(HvLpEvent_Type type, + HvLpIndex remoteLp, + HvLpDma_Direction direction, + HvLpInstanceId localInstanceId, + HvLpInstanceId remoteInstanceId, + HvLpDma_AddressType localAddressType, + HvLpDma_AddressType remoteAddressType, + u64 localAddrOrTce, + u64 remoteAddrOrTce, + u32 transferLength) +{ + HvLpDma_Rc retVal; + // Pack the misc bits into a single Dword to pass to PLIC + union + { + struct HvCallEvent_PackedDmaParms parms; + u64 dword; + } packed; + packed.parms.xDirection = direction; + packed.parms.xLocalAddrType = localAddressType; + packed.parms.xRemoteAddrType = remoteAddressType; + packed.parms.xRsvd1 = 0; + packed.parms.xRemoteLp = remoteLp; + packed.parms.xType = type; + packed.parms.xRsvd2 = 0; + packed.parms.xLocalInstId = localInstanceId; + packed.parms.xRemoteInstId = remoteInstanceId; + + retVal = (HvLpDma_Rc)HvCall4(HvCallEventDmaSingle, + packed.dword, + localAddrOrTce, + remoteAddrOrTce, + transferLength); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//================================================================= +static inline HvLpDma_Rc HvCallEvent_dmaToSp(void* local, u32 remote, u32 length, HvLpDma_Direction dir) +{ + u64 abs_addr; + HvLpDma_Rc retVal; + abs_addr = virt_to_absolute( (unsigned long) local ); + + retVal = (HvLpDma_Rc)HvCall4(HvCallEventDmaToSp, + abs_addr, + remote, + length, + dir); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//================================================================ + +#endif // _HVCALLEVENT_H + diff -Nru a/include/asm-ppc64/iSeries/HvCallHpt.h b/include/asm-ppc64/iSeries/HvCallHpt.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvCallHpt.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,143 @@ +/* + * HvCallHpt.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================ +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from the OS. +// +//============================================================================ + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +//------------------------------------------------------------------- +// Other Includes +//------------------------------------------------------------------- + +#ifndef _PPC_MMU_H +#include +#endif + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +#ifndef _HVCALLHPT_H +#define _HVCALLHPT_H + +#define HvCallHptGetHptAddress HvCallHpt + 0 +#define HvCallHptGetHptPages HvCallHpt + 1 +#define HvCallHptSetPp HvCallHpt + 5 +#define HvCallHptSetSwBits HvCallHpt + 6 +#define HvCallHptUpdate HvCallHpt + 7 +#define HvCallHptInvalidateNoSyncICache HvCallHpt + 8 +#define HvCallHptGet HvCallHpt + 11 +#define HvCallHptFindNextValid HvCallHpt + 12 +#define HvCallHptFindValid HvCallHpt + 13 +#define HvCallHptAddValidate HvCallHpt + 16 +#define HvCallHptInvalidateSetSwBitsGet HvCallHpt + 18 + + +//============================================================================ +static inline u64 HvCallHpt_getHptAddress(void) +{ + u64 retval = HvCall0(HvCallHptGetHptAddress); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retval; +} +//============================================================================ +static inline u64 HvCallHpt_getHptPages(void) +{ + u64 retval = HvCall0(HvCallHptGetHptPages); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retval; +} +//============================================================================= +static inline void HvCallHpt_setPp(u32 hpteIndex, u8 value) +{ + HvCall2( HvCallHptSetPp, hpteIndex, value ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//============================================================================= +static inline void HvCallHpt_setSwBits(u32 hpteIndex, u8 bitson, u8 bitsoff ) +{ + HvCall3( HvCallHptSetSwBits, hpteIndex, bitson, bitsoff ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//============================================================================= +static inline void HvCallHpt_invalidateNoSyncICache(u32 hpteIndex) + +{ + HvCall1( HvCallHptInvalidateNoSyncICache, hpteIndex ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//============================================================================= +static inline u64 HvCallHpt_invalidateSetSwBitsGet(u32 hpteIndex, u8 bitson, u8 bitsoff ) + +{ + u64 compressedStatus; + compressedStatus = HvCall4( HvCallHptInvalidateSetSwBitsGet, hpteIndex, bitson, bitsoff, 1 ); + HvCall1( HvCallHptInvalidateNoSyncICache, hpteIndex ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return compressedStatus; +} +//============================================================================= +static inline u64 HvCallHpt_findValid( struct _HPTE *hpte, u64 vpn ) +{ + u64 retIndex = HvCall3Ret16( HvCallHptFindValid, hpte, vpn, 0, 0 ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retIndex; +} +//============================================================================= +static inline u64 HvCallHpt_findNextValid( struct _HPTE *hpte, u32 hpteIndex, u8 bitson, u8 bitsoff ) +{ + u64 retIndex = HvCall3Ret16( HvCallHptFindNextValid, hpte, hpteIndex, bitson, bitsoff ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retIndex; +} +//============================================================================= +static inline void HvCallHpt_get( struct _HPTE *hpte, u32 hpteIndex ) +{ + HvCall2Ret16( HvCallHptGet, hpte, hpteIndex, 0 ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//============================================================================ +static inline void HvCallHpt_addValidate( u32 hpteIndex, + u32 hBit, + struct _HPTE *hpte ) + +{ + HvCall4( HvCallHptAddValidate, hpteIndex, + hBit, (*((u64 *)hpte)), (*(((u64 *)hpte)+1)) ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} + + +//============================================================================= + +#endif // _HVCALLHPT_H + diff -Nru a/include/asm-ppc64/iSeries/HvCallPci.h b/include/asm-ppc64/iSeries/HvCallPci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvCallPci.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,695 @@ +/************************************************************************/ +/* Provides the Hypervisor PCI calls for iSeries Linux Parition. */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Jan 9, 2001 */ +/************************************************************************/ +//============================================================================ +// Header File Id +// Name______________: HvCallPci.H +// +// Description_______: +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from SLIC. +// +//============================================================================ + +//------------------------------------------------------------------- +// Forward declarations +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +//------------------------------------------------------------------- +// Other Includes +//------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +#ifndef _HVCALLPCI_H +#define _HVCALLPCI_H + +struct HvCallPci_DsaAddr { // make sure this struct size is 64-bits total + u16 busNumber; + u8 subBusNumber; + u8 deviceId; + u8 barNumber; + u8 reserved[3]; +}; +union HvDsaMap { + u64 DsaAddr; + struct HvCallPci_DsaAddr Dsa; +}; + +struct HvCallPci_LoadReturn { + u64 rc; + u64 value; +}; + +enum HvCallPci_DeviceType {HvCallPci_NodeDevice = 1, + HvCallPci_SpDevice = 2, + HvCallPci_IopDevice = 3, + HvCallPci_BridgeDevice = 4, + HvCallPci_MultiFunctionDevice = 5, + HvCallPci_IoaDevice = 6 +}; + + +struct HvCallPci_DeviceInfo { + u32 deviceType; // See DeviceType enum for values +}; + +struct HvCallPci_BusUnitInfo { + u32 sizeReturned; // length of data returned + u32 deviceType; // see DeviceType enum for values +}; + +struct HvCallPci_BridgeInfo { + struct HvCallPci_BusUnitInfo busUnitInfo; // Generic bus unit info + u8 subBusNumber; // Bus number of secondary bus + u8 maxAgents; // Max idsels on secondary bus +}; + + +// Maximum BusUnitInfo buffer size. Provided for clients so they can allocate +// a buffer big enough for any type of bus unit. Increase as needed. +enum {HvCallPci_MaxBusUnitInfoSize = 128}; + +struct HvCallPci_BarParms { + u64 vaddr; + u64 raddr; + u64 size; + u64 protectStart; + u64 protectEnd; + u64 relocationOffset; + u64 pciAddress; + u64 reserved[3]; +}; + +enum HvCallPci_VpdType { + HvCallPci_BusVpd = 1, + HvCallPci_BusAdapterVpd = 2 +}; + +#define HvCallPciConfigLoad8 HvCallPci + 0 +#define HvCallPciConfigLoad16 HvCallPci + 1 +#define HvCallPciConfigLoad32 HvCallPci + 2 +#define HvCallPciConfigStore8 HvCallPci + 3 +#define HvCallPciConfigStore16 HvCallPci + 4 +#define HvCallPciConfigStore32 HvCallPci + 5 +#define HvCallPciEoi HvCallPci + 16 +#define HvCallPciGetBarParms HvCallPci + 18 +#define HvCallPciMaskFisr HvCallPci + 20 +#define HvCallPciUnmaskFisr HvCallPci + 21 +#define HvCallPciSetSlotReset HvCallPci + 25 +#define HvCallPciGetDeviceInfo HvCallPci + 27 +#define HvCallPciGetCardVpd HvCallPci + 28 +#define HvCallPciBarLoad8 HvCallPci + 40 +#define HvCallPciBarLoad16 HvCallPci + 41 +#define HvCallPciBarLoad32 HvCallPci + 42 +#define HvCallPciBarLoad64 HvCallPci + 43 +#define HvCallPciBarStore8 HvCallPci + 44 +#define HvCallPciBarStore16 HvCallPci + 45 +#define HvCallPciBarStore32 HvCallPci + 46 +#define HvCallPciBarStore64 HvCallPci + 47 +#define HvCallPciMaskInterrupts HvCallPci + 48 +#define HvCallPciUnmaskInterrupts HvCallPci + 49 +#define HvCallPciGetBusUnitInfo HvCallPci + 50 + +//============================================================================ +static inline u64 HvCallPci_configLoad8(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u8 *value) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + HvCall3Ret16(HvCallPciConfigLoad8, &retVal, *(u64 *)&dsa, offset, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *value = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_configLoad16(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u16 *value) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + HvCall3Ret16(HvCallPciConfigLoad16, &retVal, *(u64 *)&dsa, offset, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *value = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_configLoad32(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u32 *value) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + HvCall3Ret16(HvCallPciConfigLoad32, &retVal, *(u64 *)&dsa, offset, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *value = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_configStore8(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u8 value) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + retVal = HvCall4(HvCallPciConfigStore8, *(u64 *)&dsa, offset, value, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_configStore16(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u16 value) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + retVal = HvCall4(HvCallPciConfigStore16, *(u64 *)&dsa, offset, value, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_configStore32(u16 busNumber, u8 subBusNumber, + u8 deviceId, u32 offset, + u32 value) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumber; + dsa.subBusNumber = subBusNumber; + dsa.deviceId = deviceId; + + retVal = HvCall4(HvCallPciConfigStore32, *(u64 *)&dsa, offset, value, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_barLoad8(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u8* valueParm) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + HvCall3Ret16(HvCallPciBarLoad8, &retVal, *(u64 *)&dsa, offsetParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *valueParm = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_barLoad16(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u16* valueParm) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + HvCall3Ret16(HvCallPciBarLoad16, &retVal, *(u64 *)&dsa, offsetParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *valueParm = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_barLoad32(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u32* valueParm) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + HvCall3Ret16(HvCallPciBarLoad32, &retVal, *(u64 *)&dsa, offsetParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *valueParm = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_barLoad64(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u64* valueParm) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + HvCall3Ret16(HvCallPciBarLoad64, &retVal, *(u64 *)&dsa, offsetParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + *valueParm = retVal.value; + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_barStore8(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u8 valueParm) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + retVal = HvCall4(HvCallPciBarStore8, *(u64 *)&dsa, offsetParm, valueParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_barStore16(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u16 valueParm) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + retVal = HvCall4(HvCallPciBarStore16, *(u64 *)&dsa, offsetParm, valueParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_barStore32(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u32 valueParm) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + retVal = HvCall4(HvCallPciBarStore32, *(u64 *)&dsa, offsetParm, valueParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_barStore64(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 offsetParm, + u64 valueParm) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + retVal = HvCall4(HvCallPciBarStore64, *(u64 *)&dsa, offsetParm, valueParm, 0); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_eoi(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm) +{ + struct HvCallPci_DsaAddr dsa; + struct HvCallPci_LoadReturn retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + HvCall1Ret16(HvCallPciEoi, &retVal, *(u64*)&dsa); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal.rc; +} +//============================================================================ +static inline u64 HvCallPci_getBarParms(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u8 barNumberParm, + u64 parms, + u32 sizeofParms) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + dsa.barNumber = barNumberParm; + + retVal = HvCall3(HvCallPciGetBarParms, *(u64*)&dsa, parms, sizeofParms); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_maskFisr(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 fisrMask) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall2(HvCallPciMaskFisr, *(u64*)&dsa, fisrMask); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_unmaskFisr(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 fisrMask) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall2(HvCallPciUnmaskFisr, *(u64*)&dsa, fisrMask); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_setSlotReset(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 onNotOff) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall2(HvCallPciSetSlotReset, *(u64*)&dsa, onNotOff); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_getDeviceInfo(u16 busNumberParm, + u8 subBusParm, + u8 deviceNumberParm, + u64 parms, + u32 sizeofParms) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceNumberParm << 4; + + retVal = HvCall3(HvCallPciGetDeviceInfo, *(u64*)&dsa, parms, sizeofParms); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_maskInterrupts(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 interruptMask) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall2(HvCallPciMaskInterrupts, *(u64*)&dsa, interruptMask); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ +static inline u64 HvCallPci_unmaskInterrupts(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 interruptMask) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall2(HvCallPciUnmaskInterrupts, *(u64*)&dsa, interruptMask); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ + +static inline u64 HvCallPci_getBusUnitInfo(u16 busNumberParm, + u8 subBusParm, + u8 deviceIdParm, + u64 parms, + u32 sizeofParms) +{ + struct HvCallPci_DsaAddr dsa; + u64 retVal; + + *((u64*)&dsa) = 0; + + dsa.busNumber = busNumberParm; + dsa.subBusNumber = subBusParm; + dsa.deviceId = deviceIdParm; + + retVal = HvCall3(HvCallPciGetBusUnitInfo, *(u64*)&dsa, parms, sizeofParms); + + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + + return retVal; +} +//============================================================================ + +static inline int HvCallPci_getBusVpd(u16 busNumParm, u64 destParm, u16 sizeParm) +{ + int xRetSize; + u64 xRc = HvCall4(HvCallPciGetCardVpd, busNumParm, destParm, sizeParm, HvCallPci_BusVpd); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + if (xRc == -1) + xRetSize = -1; + else + xRetSize = xRc & 0xFFFF; + return xRetSize; +} +//============================================================================ + +static inline int HvCallPci_getBusAdapterVpd(u16 busNumParm, u64 destParm, u16 sizeParm) +{ + int xRetSize; + u64 xRc = HvCall4(HvCallPciGetCardVpd, busNumParm, destParm, sizeParm, HvCallPci_BusAdapterVpd); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + if (xRc == -1) + xRetSize = -1; + else + xRetSize = xRc & 0xFFFF; + return xRetSize; +} +//============================================================================ +#endif // _HVCALLPCI_H diff -Nru a/include/asm-ppc64/iSeries/HvCallSc.h b/include/asm-ppc64/iSeries/HvCallSc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvCallSc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,53 @@ +/* + * HvCallSc.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _HVTYPES_H +#include +#endif + +#ifndef _HVCALLSC_H +#define _HVCALLSC_H + +#define HvCallBase 0x8000000000000000 +#define HvCallCfg 0x8002000000000000 +#define HvCallEvent 0x8003000000000000 +#define HvCallHpt 0x8004000000000000 +#define HvCallPci 0x8005000000000000 +#define HvCallSm 0x8007000000000000 +#define HvCallXm 0x8009000000000000 + +u64 HvCall0( u64 ); +u64 HvCall1( u64, u64 ); +u64 HvCall2( u64, u64, u64 ); +u64 HvCall3( u64, u64, u64, u64 ); +u64 HvCall4( u64, u64, u64, u64, u64 ); +u64 HvCall5( u64, u64, u64, u64, u64, u64 ); +u64 HvCall6( u64, u64, u64, u64, u64, u64, u64 ); +u64 HvCall7( u64, u64, u64, u64, u64, u64, u64, u64 ); + +u64 HvCall0Ret16( u64, void * ); +u64 HvCall1Ret16( u64, void *, u64 ); +u64 HvCall2Ret16( u64, void *, u64, u64 ); +u64 HvCall3Ret16( u64, void *, u64, u64, u64 ); +u64 HvCall4Ret16( u64, void *, u64, u64, u64, u64 ); +u64 HvCall5Ret16( u64, void *, u64, u64, u64, u64, u64 ); +u64 HvCall6Ret16( u64, void *, u64, u64, u64, u64, u64, u64 ); +u64 HvCall7Ret16( u64, void *, u64, u64 ,u64 ,u64 ,u64 ,u64 ,u64 ); + +#endif /* _HVCALLSC_H */ diff -Nru a/include/asm-ppc64/iSeries/HvCallSm.h b/include/asm-ppc64/iSeries/HvCallSm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvCallSm.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,58 @@ +/* + * HvCallSm.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================ +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from the OS. +// +//============================================================================ + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +#ifndef _HVCALLSM_H +#define _HVCALLSM_H + +#define HvCallSmGet64BitsOfAccessMap HvCallSm + 11 + + +//============================================================================ +static inline u64 HvCallSm_get64BitsOfAccessMap( + HvLpIndex lpIndex, u64 indexIntoBitMap ) +{ + u64 retval = HvCall2(HvCallSmGet64BitsOfAccessMap, lpIndex, + indexIntoBitMap ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retval; +} +//============================================================================ +#endif // _HVCALLSM_H + diff -Nru a/include/asm-ppc64/iSeries/HvCallXm.h b/include/asm-ppc64/iSeries/HvCallXm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvCallXm.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,106 @@ +//============================================================================ +// Header File Id +// Name______________: HvCallXm.H +// +// Description_______: +// +// This file contains the "hypervisor call" interface which is used to +// drive the hypervisor from SLIC. +// +//============================================================================ + +//------------------------------------------------------------------- +// Forward declarations +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _HVCALLSC_H +#include "HvCallSc.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +//------------------------------------------------------------------- +// Other Includes +//------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +#ifndef _HVCALLXM_H +#define _HVCALLXM_H + +#define HvCallXmGetTceTableParms HvCallXm + 0 +#define HvCallXmTestBus HvCallXm + 1 +#define HvCallXmConnectBusUnit HvCallXm + 2 +#define HvCallXmLoadTod HvCallXm + 8 +#define HvCallXmTestBusUnit HvCallXm + 9 +#define HvCallXmSetTce HvCallXm + 11 +#define HvCallXmSetTces HvCallXm + 13 + + + +//============================================================================ +static inline void HvCallXm_getTceTableParms(u64 cb) +{ + HvCall1(HvCallXmGetTceTableParms, cb); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); +} +//============================================================================ +static inline u64 HvCallXm_setTce(u64 tceTableToken, u64 tceOffset, u64 tce) +{ + u64 retval = HvCall3(HvCallXmSetTce, tceTableToken, tceOffset, tce ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retval; +} +//============================================================================ +static inline u64 HvCallXm_setTces(u64 tceTableToken, u64 tceOffset, u64 numTces, u64 tce1, u64 tce2, u64 tce3, u64 tce4) +{ + u64 retval = HvCall7(HvCallXmSetTces, tceTableToken, tceOffset, numTces, + tce1, tce2, tce3, tce4 ); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retval; +} +//============================================================================= +static inline u64 HvCallXm_testBus(u16 busNumber) +{ + u64 retVal = HvCall1(HvCallXmTestBus, busNumber); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//===================================================================================== +static inline u64 HvCallXm_testBusUnit(u16 busNumber, u8 subBusNumber, u8 deviceId) +{ + u64 busUnitNumber = (subBusNumber << 8) | deviceId; + u64 retVal = HvCall2(HvCallXmTestBusUnit, busNumber, busUnitNumber); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//===================================================================================== +static inline u64 HvCallXm_connectBusUnit(u16 busNumber, u8 subBusNumber, u8 deviceId, + u64 interruptToken) +{ + u64 busUnitNumber = (subBusNumber << 8) | deviceId; + u64 queueIndex = 0; // HvLpConfig::mapDsaToQueueIndex(HvLpDSA(busNumber, xBoard, xCard)); + + u64 retVal = HvCall5(HvCallXmConnectBusUnit, busNumber, busUnitNumber, + interruptToken, 0, queueIndex); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//===================================================================================== +static inline u64 HvCallXm_loadTod(void) +{ + u64 retVal = HvCall0(HvCallXmLoadTod); + // getPaca()->adjustHmtForNoOfSpinLocksHeld(); + return retVal; +} +//===================================================================================== + +#endif // _HVCALLXM_H + diff -Nru a/include/asm-ppc64/iSeries/HvLpConfig.h b/include/asm-ppc64/iSeries/HvLpConfig.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvLpConfig.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,292 @@ +/* + * HvLpConfig.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//=========================================================================== +// +// This file contains the interface to the LPAR configuration data +// to determine which resources should be allocated to each partition. +// +//=========================================================================== + +#ifndef _HVCALLCFG_H +#include "HvCallCfg.h" +#endif + +#ifndef _HVTYPES_H +#include +#endif + +#ifndef _ITLPNACA_H +#include +#endif + +#ifndef _LPARDATA_H +#include +#endif + +#ifndef _HVLPCONFIG_H +#define _HVLPCONFIG_H + +//------------------------------------------------------------------- +// Constants +//------------------------------------------------------------------- + +extern HvLpIndex HvLpConfig_getLpIndex_outline(void); + +//=================================================================== +static inline HvLpIndex HvLpConfig_getLpIndex(void) +{ + return itLpNaca.xLpIndex; +} +//=================================================================== +static inline HvLpIndex HvLpConfig_getPrimaryLpIndex(void) +{ + return itLpNaca.xPrimaryLpIndex; +} +//================================================================= +static inline HvLpIndex HvLpConfig_getLps(void) +{ + return HvCallCfg_getLps(); +} +//================================================================= +static inline HvLpIndexMap HvLpConfig_getActiveLpMap(void) +{ + return HvCallCfg_getActiveLpMap(); +} +//================================================================= +static inline u64 HvLpConfig_getSystemMsMegs(void) +{ + return HvCallCfg_getSystemMsChunks() / HVCHUNKSPERMEG; +} +//================================================================= +static inline u64 HvLpConfig_getSystemMsChunks(void) +{ + return HvCallCfg_getSystemMsChunks(); +} +//================================================================= +static inline u64 HvLpConfig_getSystemMsPages(void) +{ + return HvCallCfg_getSystemMsChunks() * HVPAGESPERCHUNK; +} +//================================================================ +static inline u64 HvLpConfig_getMsMegs(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Cur) / HVCHUNKSPERMEG; +} +//================================================================ +static inline u64 HvLpConfig_getMsChunks(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Cur); +} +//================================================================ +static inline u64 HvLpConfig_getMsPages(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Cur) * HVPAGESPERCHUNK; +} +//================================================================ +static inline u64 HvLpConfig_getMinMsMegs(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Min) / HVCHUNKSPERMEG; +} +//================================================================ +static inline u64 HvLpConfig_getMinMsChunks(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Min); +} +//================================================================ +static inline u64 HvLpConfig_getMinMsPages(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Min) * HVPAGESPERCHUNK; +} +//================================================================ +static inline u64 HvLpConfig_getMinRuntimeMsMegs(void) +{ + return HvCallCfg_getMinRuntimeMsChunks(HvLpConfig_getLpIndex()) / HVCHUNKSPERMEG; +} +//=============================================================== +static inline u64 HvLpConfig_getMinRuntimeMsChunks(void) +{ + return HvCallCfg_getMinRuntimeMsChunks(HvLpConfig_getLpIndex()); +} +//=============================================================== +static inline u64 HvLpConfig_getMinRuntimeMsPages(void) +{ + return HvCallCfg_getMinRuntimeMsChunks(HvLpConfig_getLpIndex()) * HVPAGESPERCHUNK; +} +//=============================================================== +static inline u64 HvLpConfig_getMaxMsMegs(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Max) / HVCHUNKSPERMEG; +} +//=============================================================== +static inline u64 HvLpConfig_getMaxMsChunks(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Max); +} +//=============================================================== +static inline u64 HvLpConfig_getMaxMsPages(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Max) * HVPAGESPERCHUNK; +} +//=============================================================== +static inline u64 HvLpConfig_getInitMsMegs(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Init) / HVCHUNKSPERMEG; +} +//=============================================================== +static inline u64 HvLpConfig_getInitMsChunks(void) +{ + return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Init); +} +//=============================================================== +static inline u64 HvLpConfig_getInitMsPages(void) +{ return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Init) * HVPAGESPERCHUNK; +} +//=============================================================== +static inline u64 HvLpConfig_getSystemPhysicalProcessors(void) +{ + return HvCallCfg_getSystemPhysicalProcessors(); +} +//=============================================================== +static inline u64 HvLpConfig_getSystemLogicalProcessors(void) +{ + return HvCallCfg_getSystemPhysicalProcessors() * (/*getPaca()->getSecondaryThreadCount() +*/ 1); +} +//=============================================================== +static inline u64 HvLpConfig_getNumProcsInSharedPool(HvLpSharedPoolIndex sPI) +{ + return HvCallCfg_getNumProcsInSharedPool(sPI); +} +//=============================================================== +static inline u64 HvLpConfig_getPhysicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Cur); +} +//=============================================================== +static inline u64 HvLpConfig_getLogicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Cur) * (/*getPaca()->getSecondaryThreadCount() +*/ 1); +} +//=============================================================== +static inline HvLpSharedPoolIndex HvLpConfig_getSharedPoolIndex(void) +{ + return HvCallCfg_getSharedPoolIndex(HvLpConfig_getLpIndex()); +} +//=============================================================== +static inline u64 HvLpConfig_getSharedProcUnits(void) +{ + return HvCallCfg_getSharedProcUnits(HvLpConfig_getLpIndex(),HvCallCfg_Cur); +} +//=============================================================== +static inline u64 HvLpConfig_getMinSharedProcUnits(void) +{ + return HvCallCfg_getSharedProcUnits(HvLpConfig_getLpIndex(),HvCallCfg_Min); +} +//=============================================================== +static inline u64 HvLpConfig_getMaxSharedProcUnits(void) +{ + return HvCallCfg_getSharedProcUnits(HvLpConfig_getLpIndex(),HvCallCfg_Max); +} +//=============================================================== +static inline u64 HvLpConfig_getMinPhysicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Min); +} +//=============================================================== +static inline u64 HvLpConfig_getMinLogicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Min) * (/*getPaca()->getSecondaryThreadCount() +*/ 1); +} +//=============================================================== +static inline u64 HvLpConfig_getMaxPhysicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Max); +} +//=============================================================== +static inline u64 HvLpConfig_getMaxLogicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Max) * (/*getPaca()->getSecondaryThreadCount() +*/ 1); +} +//=============================================================== +static inline u64 HvLpConfig_getInitPhysicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Init); +} +//=============================================================== +static inline u64 HvLpConfig_getInitLogicalProcessors(void) +{ + return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Init) * (/*getPaca()->getSecondaryThreadCount() +*/ 1); +} +//================================================================ +static inline HvLpVirtualLanIndexMap HvLpConfig_getVirtualLanIndexMap(void) +{ + return HvCallCfg_getVirtualLanIndexMap(HvLpConfig_getLpIndex_outline()); +} +//=============================================================== +static inline HvLpVirtualLanIndexMap HvLpConfig_getVirtualLanIndexMapForLp(HvLpIndex lp) +{ + return HvCallCfg_getVirtualLanIndexMap(lp); +} +//================================================================ +static inline HvLpIndex HvLpConfig_getBusOwner(HvBusNumber busNumber) +{ + return HvCallCfg_getBusOwner(busNumber); +} +//=============================================================== +static inline int HvLpConfig_isBusDedicated(HvBusNumber busNumber) +{ + return HvCallCfg_isBusDedicated(busNumber); +} +//================================================================ +static inline HvLpIndexMap HvLpConfig_getBusAllocation(HvBusNumber busNumber) +{ + return HvCallCfg_getBusAllocation(busNumber); +} +//================================================================ +// returns the absolute real address of the load area +static inline u64 HvLpConfig_getLoadAddress(void) +{ + return itLpNaca.xLoadAreaAddr & 0x7fffffffffffffff; +} +//================================================================ +static inline u64 HvLpConfig_getLoadPages(void) +{ + return itLpNaca.xLoadAreaChunks * HVPAGESPERCHUNK; +} +//================================================================ +static inline int HvLpConfig_isBusOwnedByThisLp(HvBusNumber busNumber) +{ + HvLpIndex busOwner = HvLpConfig_getBusOwner(busNumber); + return (busOwner == HvLpConfig_getLpIndex()); +} +//================================================================ +static inline int HvLpConfig_doLpsCommunicateOnVirtualLan(HvLpIndex lp1, HvLpIndex lp2) +{ + HvLpVirtualLanIndexMap virtualLanIndexMap1 = HvCallCfg_getVirtualLanIndexMap( lp1 ); + HvLpVirtualLanIndexMap virtualLanIndexMap2 = HvCallCfg_getVirtualLanIndexMap( lp2 ); + return ((virtualLanIndexMap1 & virtualLanIndexMap2) != 0); +} +//================================================================ +static inline HvLpIndex HvLpConfig_getHostingLpIndex(HvLpIndex lp) +{ + return HvCallCfg_getHostingLpIndex(lp); +} +//================================================================ + +#endif // _HVLPCONFIG_H diff -Nru a/include/asm-ppc64/iSeries/HvLpEvent.h b/include/asm-ppc64/iSeries/HvLpEvent.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvLpEvent.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,144 @@ +/* + * HvLpEvent.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//====================================================================== +// +// This file contains the class for HV events in the system. +// +//===================================================================== +#ifndef _HVLPEVENT_H +#define _HVLPEVENT_H + +#include +#include +#include +#ifndef _HVCALLEVENT_H +#include +#endif + + +//===================================================================== +// +// HvLpEvent is the structure for Lp Event messages passed between +// partitions through PLIC. +// +//===================================================================== + +struct HvEventFlags +{ + u8 xValid:1; // Indicates a valid request x00-x00 + u8 xRsvd1:4; // Reserved ... + u8 xAckType:1; // Immediate or deferred ... + u8 xAckInd:1; // Indicates if ACK required ... + u8 xFunction:1; // Interrupt or Acknowledge ... +}; + + +struct HvLpEvent +{ + struct HvEventFlags xFlags; // Event flags x00-x00 + u8 xType; // Type of message x01-x01 + u16 xSubtype; // Subtype for event x02-x03 + u8 xSourceLp; // Source LP x04-x04 + u8 xTargetLp; // Target LP x05-x05 + u8 xSizeMinus1; // Size of Derived class - 1 x06-x06 + u8 xRc; // RC for Ack flows x07-x07 + u16 xSourceInstanceId; // Source sides instance id x08-x09 + u16 xTargetInstanceId; // Target sides instance id x0A-x0B + union { + u32 xSubtypeData; // Data usable by the subtype x0C-x0F + u16 xSubtypeDataShort[2]; // Data as 2 shorts + u8 xSubtypeDataChar[4]; // Data as 4 chars + } x; + + u64 xCorrelationToken; // Unique value for source/type x10-x17 +}; + +// Lp Event handler function +typedef void (*LpEventHandler)(struct HvLpEvent *, struct pt_regs *); + +// Register a handler for an event type +// returns 0 on success +extern int HvLpEvent_registerHandler( HvLpEvent_Type eventType, LpEventHandler hdlr); + +// Unregister a handler for an event type +// returns 0 on success +// Unregister will fail if there are any paths open for the type +extern int HvLpEvent_unregisterHandler( HvLpEvent_Type eventType ); + +// Open an Lp Event Path for an event type +// returns 0 on success +// openPath will fail if there is no handler registered for the event type. +// The lpIndex specified is the partition index for the target partition +// (for VirtualIo, VirtualLan and SessionMgr) other types specify zero) +extern int HvLpEvent_openPath( HvLpEvent_Type eventType, HvLpIndex lpIndex ); + + +// Close an Lp Event Path for a type and partition +// returns 0 on sucess +extern int HvLpEvent_closePath( HvLpEvent_Type eventType, HvLpIndex lpIndex ); + +#define HvLpEvent_Type_Hypervisor 0 +#define HvLpEvent_Type_MachineFac 1 +#define HvLpEvent_Type_SessionMgr 2 +#define HvLpEvent_Type_SpdIo 3 +#define HvLpEvent_Type_VirtualBus 4 +#define HvLpEvent_Type_PciIo 5 +#define HvLpEvent_Type_RioIo 6 +#define HvLpEvent_Type_VirtualLan 7 +#define HvLpEvent_Type_VirtualIo 8 +#define HvLpEvent_Type_NumTypes 9 + +#define HvLpEvent_Rc_Good 0 +#define HvLpEvent_Rc_BufferNotAvailable 1 +#define HvLpEvent_Rc_Cancelled 2 +#define HvLpEvent_Rc_GenericError 3 +#define HvLpEvent_Rc_InvalidAddress 4 +#define HvLpEvent_Rc_InvalidPartition 5 +#define HvLpEvent_Rc_InvalidSize 6 +#define HvLpEvent_Rc_InvalidSubtype 7 +#define HvLpEvent_Rc_InvalidSubtypeData 8 +#define HvLpEvent_Rc_InvalidType 9 +#define HvLpEvent_Rc_PartitionDead 10 +#define HvLpEvent_Rc_PathClosed 11 +#define HvLpEvent_Rc_SubtypeError 12 + +#define HvLpEvent_Function_Ack 0 +#define HvLpEvent_Function_Int 1 + +#define HvLpEvent_AckInd_NoAck 0 +#define HvLpEvent_AckInd_DoAck 1 + +#define HvLpEvent_AckType_ImmediateAck 0 +#define HvLpEvent_AckType_DeferredAck 1 + +#define HvLpDma_Direction_LocalToRemote 0 +#define HvLpDma_Direction_RemoteToLocal 1 + +#define HvLpDma_AddressType_TceIndex 0 +#define HvLpDma_AddressType_RealAddress 1 + +#define HvLpDma_Rc_Good 0 +#define HvLpDma_Rc_Error 1 +#define HvLpDma_Rc_PartitionDead 2 +#define HvLpDma_Rc_PathClosed 3 +#define HvLpDma_Rc_InvalidAddress 4 +#define HvLpDma_Rc_InvalidLength 5 + +#endif // _HVLPEVENT_H diff -Nru a/include/asm-ppc64/iSeries/HvReleaseData.h b/include/asm-ppc64/iSeries/HvReleaseData.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvReleaseData.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,70 @@ +/* + * HvReleaseData.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================= +// +// This control block contains the critical information about the +// release so that it can be changed in the future (ie, the virtual +// address of the OS's NACA). +// +//----------------------------------------------------------------------------- +// Standard Includes +//----------------------------------------------------------------------------- +#ifndef _PPC64_TYPES_H +#include +#endif + +#ifndef _HVRELEASEDATA_H +#define _HVRELEASEDATA_H + +//============================================================================= +// +// When we IPL a secondary partition, we will check if if the +// secondary xMinPlicVrmIndex > the primary xVrmIndex. +// If it is then this tells PLIC that this secondary is not +// supported running on this "old" of a level of PLIC. +// +// Likewise, we will compare the primary xMinSlicVrmIndex to +// the secondary xVrmIndex. +// If the primary xMinSlicVrmDelta > secondary xVrmDelta then we +// know that this PLIC does not support running an OS "that old". +// +//============================================================================= + +struct HvReleaseData +{ + u32 xDesc; // Descriptor "HvRD" ebcdic x00-x03 + u16 xSize; // Size of this control block x04-x05 + u16 xVpdAreasPtrOffset; // Offset in NACA of ItVpdAreas x06-x07 + struct Naca * xSlicNacaAddr; // Virtual address of SLIC NACA x08-x0F + u32 xMsNucDataOffset; // Offset of Linux Mapping Data x10-x13 + u32 xRsvd1; // Reserved x14-x17 + u16 xTagsMode:1; // 0 == tags active, 1 == tags inactive + u16 xAddressSize:1; // 0 == 64-bit, 1 == 32-bit + u16 xNoSharedProcs:1; // 0 == shared procs, 1 == no shared + u16 xNoHMT:1; // 0 == allow HMT, 1 == no HMT + u16 xRsvd2:12; // Reserved x18-x19 + u16 xVrmIndex; // VRM Index of OS image x1A-x1B + u16 xMinSupportedPlicVrmIndex;// Min PLIC level (soft) x1C-x1D + u16 xMinCompatablePlicVrmIndex;// Min PLIC levelP (hard) x1E-x1F + char xVrmName[12]; // Displayable name x20-x2B + char xRsvd3[20]; // Reserved x2C-x3F +}; + +#endif // _HVRELEASEDATA_H diff -Nru a/include/asm-ppc64/iSeries/HvTypes.h b/include/asm-ppc64/iSeries/HvTypes.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/HvTypes.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,127 @@ +/* + * HvTypes.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//=========================================================================== +// Header File Id +// Name______________: HvTypes.H +// +// Description_______: +// +// General typedefs for the hypervisor. +// +// Declared Class(es): +// +//=========================================================================== + +#ifndef _PPC_TYPES_H +#include +#endif + + +#ifndef _HVTYPES_H +#define _HVTYPES_H + +//------------------------------------------------------------------- +// Typedefs +//------------------------------------------------------------------- +typedef u8 HvLpIndex; +typedef u16 HvLpInstanceId; +typedef u64 HvLpTOD; +typedef u64 HvLpSystemSerialNum; +typedef u8 HvLpDeviceSerialNum[12]; +typedef u16 HvLpSanHwSet; +typedef u16 HvLpBus; +typedef u16 HvLpBoard; +typedef u16 HvLpCard; +typedef u8 HvLpDeviceType[4]; +typedef u8 HvLpDeviceModel[3]; +typedef u64 HvIoToken; +typedef u8 HvLpName[8]; +typedef u32 HvIoId; +typedef u64 HvRealMemoryIndex; +typedef u32 HvLpIndexMap; // Must hold HvMaxArchitectedLps bits!!! +typedef u16 HvLpVrmIndex; +typedef u32 HvXmGenerationId; +typedef u8 HvLpBusPool; +typedef u8 HvLpSharedPoolIndex; +typedef u16 HvLpSharedProcUnitsX100; +typedef u8 HvLpVirtualLanIndex; +typedef u16 HvLpVirtualLanIndexMap; // Must hold HvMaxArchitectedVirtualLans bits!!! +typedef u16 HvBusNumber; // Hypervisor Bus Number +typedef u8 HvSubBusNumber; // Hypervisor SubBus Number +typedef u8 HvAgentId; // Hypervisor DevFn + + +#define HVMAXARCHITECTEDLPS 32 +#define HVCHUNKSIZE 256 * 1024 +#define HVPAGESIZE 4 * 1024 +#define HVLPMINMEGSPRIMARY 256 +#define HVLPMINMEGSSECONDARY 64 +#define HVCHUNKSPERMEG 4 +#define HVPAGESPERMEG 256 +#define HVPAGESPERCHUNK 64 + +#define HvMaxArchitectedLps ((HvLpIndex)HVMAXARCHITECTEDLPS) +#define HvMaxArchitectedVirtualLans ((HvLpVirtualLanIndex)16) +#define HvLpIndexInvalid ((HvLpIndex)0xff) + +//-------------------------------------------------------------------- +// Enums for the sub-components under PLIC +// Used in HvCall and HvPrimaryCall +//-------------------------------------------------------------------- +enum HvCallCompIds +{ + HvCallCompId = 0, + HvCallCpuCtlsCompId = 1, + HvCallCfgCompId = 2, + HvCallEventCompId = 3, + HvCallHptCompId = 4, + HvCallPciCompId = 5, + HvCallSlmCompId = 6, + HvCallSmCompId = 7, + HvCallSpdCompId = 8, + HvCallXmCompId = 9, + HvCallRioCompId = 10, + HvCallRsvd3CompId = 11, + HvCallRsvd2CompId = 12, + HvCallRsvd1CompId = 13, + HvCallMaxCompId = 14, + HvPrimaryCallCompId = 0, + HvPrimaryCallCfgCompId = 1, + HvPrimaryCallPciCompId = 2, + HvPrimaryCallSmCompId = 3, + HvPrimaryCallSpdCompId = 4, + HvPrimaryCallXmCompId = 5, + HvPrimaryCallRioCompId = 6, + HvPrimaryCallRsvd7CompId = 7, + HvPrimaryCallRsvd6CompId = 8, + HvPrimaryCallRsvd5CompId = 9, + HvPrimaryCallRsvd4CompId = 10, + HvPrimaryCallRsvd3CompId = 11, + HvPrimaryCallRsvd2CompId = 12, + HvPrimaryCallRsvd1CompId = 13, + HvPrimaryCallMaxCompId = HvCallMaxCompId +}; + +struct HvLpBufferList { + u64 addr; + u64 len; +}; + +#endif // _HVTYPES_H diff -Nru a/include/asm-ppc64/iSeries/IoHriMainStore.h b/include/asm-ppc64/iSeries/IoHriMainStore.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/IoHriMainStore.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,167 @@ +/* + * IoHriMainStore.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _IOHRIMAINSTORE_H +#define _IOHRIMAINSTORE_H + +/* Main Store Vpd for Condor,iStar,sStar */ +struct IoHriMainStoreSegment4 { + u8 msArea0Exists:1; + u8 msArea1Exists:1; + u8 msArea2Exists:1; + u8 msArea3Exists:1; + u8 reserved1:4; + u8 reserved2; + + u8 msArea0Functional:1; + u8 msArea1Functional:1; + u8 msArea2Functional:1; + u8 msArea3Functional:1; + u8 reserved3:4; + u8 reserved4; + + u32 totalMainStore; + + u64 msArea0Ptr; + u64 msArea1Ptr; + u64 msArea2Ptr; + u64 msArea3Ptr; + + u32 cardProductionLevel; + + u32 msAdrHole; + + u8 msArea0HasRiserVpd:1; + u8 msArea1HasRiserVpd:1; + u8 msArea2HasRiserVpd:1; + u8 msArea3HasRiserVpd:1; + u8 reserved5:4; + u8 reserved6; + u16 reserved7; + + u8 reserved8[28]; + + u64 nonInterleavedBlocksStartAdr; + u64 nonInterleavedBlocksEndAdr; +}; + +/* Main Store VPD for Power4 */ +struct IoHriMainStoreChipInfo1 { + u32 chipMfgID __attribute((packed)); + char chipECLevel[4] __attribute((packed)); +}; + +struct IoHriMainStoreVpdIdData { + char typeNumber[4]; + char modelNumber[4]; + char partNumber[12]; + char serialNumber[12]; +}; + +struct IoHriMainStoreVpdFruData { + char fruLabel[8] __attribute((packed)); + u8 numberOfSlots __attribute((packed)); + u8 pluggingType __attribute((packed)); + u16 slotMapIndex __attribute((packed)); +}; + +struct IoHriMainStoreAdrRangeBlock { + void * blockStart __attribute((packed)); + void * blockEnd __attribute((packed)); + u32 blockProcChipId __attribute((packed)); +}; + +#define MaxAreaAdrRangeBlocks 4 + +struct IoHriMainStoreArea4 { + u32 msVpdFormat __attribute((packed)); + u8 containedVpdType __attribute((packed)); + u8 reserved1 __attribute((packed)); + u16 reserved2 __attribute((packed)); + + u64 msExists __attribute((packed)); + u64 msFunctional __attribute((packed)); + + u32 memorySize __attribute((packed)); + u32 procNodeId __attribute((packed)); + + u32 numAdrRangeBlocks __attribute((packed)); + struct IoHriMainStoreAdrRangeBlock xAdrRangeBlock[MaxAreaAdrRangeBlocks] __attribute((packed)); + + struct IoHriMainStoreChipInfo1 chipInfo0 __attribute((packed)); + struct IoHriMainStoreChipInfo1 chipInfo1 __attribute((packed)); + struct IoHriMainStoreChipInfo1 chipInfo2 __attribute((packed)); + struct IoHriMainStoreChipInfo1 chipInfo3 __attribute((packed)); + struct IoHriMainStoreChipInfo1 chipInfo4 __attribute((packed)); + struct IoHriMainStoreChipInfo1 chipInfo5 __attribute((packed)); + struct IoHriMainStoreChipInfo1 chipInfo6 __attribute((packed)); + struct IoHriMainStoreChipInfo1 chipInfo7 __attribute((packed)); + + void * msRamAreaArray __attribute((packed)); + u32 msRamAreaArrayNumEntries __attribute((packed)); + u32 msRamAreaArrayEntrySize __attribute((packed)); + + u32 numaDimmExists __attribute((packed)); + u32 numaDimmFunctional __attribute((packed)); + void * numaDimmArray __attribute((packed)); + u32 numaDimmArrayNumEntries __attribute((packed)); + u32 numaDimmArrayEntrySize __attribute((packed)); + + struct IoHriMainStoreVpdIdData idData __attribute((packed)); + + u64 powerData __attribute((packed)); + u64 cardAssemblyPartNum __attribute((packed)); + u64 chipSerialNum __attribute((packed)); + + u64 reserved3 __attribute((packed)); + char reserved4[16] __attribute((packed)); + + struct IoHriMainStoreVpdFruData fruData __attribute((packed)); + + u8 vpdPortNum __attribute((packed)); + u8 reserved5 __attribute((packed)); + u8 frameId __attribute((packed)); + u8 rackUnit __attribute((packed)); + char asciiKeywordVpd[256] __attribute((packed)); + u32 reserved6 __attribute((packed)); +}; + + +struct IoHriMainStoreSegment5 { + u16 reserved1; + u8 reserved2; + u8 msVpdFormat; + + u32 totalMainStore; + u64 maxConfiguredMsAdr; + + struct IoHriMainStoreArea4* msAreaArray; + u32 msAreaArrayNumEntries; + u32 msAreaArrayEntrySize; + + u32 msAreaExists; + u32 msAreaFunctional; + + u64 reserved3; +}; + + + +#endif // _IOHRIMAINSTORE_H + diff -Nru a/include/asm-ppc64/iSeries/IoHriProcessorVpd.h b/include/asm-ppc64/iSeries/IoHriProcessorVpd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/IoHriProcessorVpd.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,90 @@ +/* + * IoHriProcessorVpd.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//=================================================================== +// +// This struct maps Processor Vpd that is DMAd to SLIC by CSP +// + +#ifndef _TYPES_H +#include +#endif + +#ifndef _IOHRIPROCESSORVPD_H +#define _IOHRIPROCESSORVPD_H + +struct IoHriProcessorVpd +{ + + u8 xFormat; // VPD format indicator x00-x00 + u8 xProcStatus:8; // Processor State x01-x01 + u8 xSecondaryThreadCount; // Secondary thread cnt x02-x02 + u8 xSrcType:1; // Src Type x03-x03 + u8 xSrcSoft:1; // Src stay soft ... + u8 xSrcParable:1; // Src parable ... + u8 xRsvd1:5; // Reserved ... + u16 xHvPhysicalProcIndex; // Hypervisor physical proc index04-x05 + u16 xRsvd2; // Reserved x06-x07 + u32 xHwNodeId; // Hardware node id x08-x0B + u32 xHwProcId; // Hardware processor id x0C-x0F + + u32 xTypeNum; // Card Type/CCIN number x10-x13 + u32 xModelNum; // Model/Feature number x14-x17 + u64 xSerialNum; // Serial number x18-x1F + char xPartNum[12]; // Book Part or FPU number x20-x2B + char xMfgID[4]; // Manufacturing ID x2C-x2F + + u32 xProcFreq; // Processor Frequency x30-x33 + u32 xTimeBaseFreq; // Time Base Frequency x34-x37 + + u32 xChipEcLevel; // Chip EC Levels x38-x3B + u32 xProcIdReg; // PIR SPR value x3C-x3F + u32 xPVR; // PVR value x40-x43 + u8 xRsvd3[12]; // Reserved x44-x4F + + u32 xInstCacheSize; // Instruction cache size in KB x50-x53 + u32 xInstBlockSize; // Instruction cache block size x54-x57 + u32 xDataCacheOperandSize; // Data cache operand size x58-x5B + u32 xInstCacheOperandSize; // Inst cache operand size x5C-x5F + + u32 xDataL1CacheSizeKB; // L1 data cache size in KB x60-x63 + u32 xDataL1CacheLineSize; // L1 data cache block size x64-x67 + u64 xRsvd4; // Reserved x68-x6F + + u32 xDataL2CacheSizeKB; // L2 data cache size in KB x70-x73 + u32 xDataL2CacheLineSize; // L2 data cache block size x74-x77 + u64 xRsvd5; // Reserved x78-x7F + + u32 xDataL3CacheSizeKB; // L3 data cache size in KB x80-x83 + u32 xDataL3CacheLineSize; // L3 data cache block size x84-x87 + u64 xRsvd6; // Reserved x88-x8F + + u64 xFruLabel; // Card Location Label x90-x97 + u8 xSlotsOnCard; // Slots on card (0=no slots) x98-x98 + u8 xPartLocFlag; // Location flag (0-pluggable 1-imbedded) x99-x99 + u16 xSlotMapIndex; // Index in slot map table x9A-x9B + u8 xSmartCardPortNo; // Smart card port number x9C-x9C + u8 xRsvd7; // Reserved x9D-x9D + u16 xFrameIdAndRackUnit; // Frame ID and rack unit adr x9E-x9F + + u8 xRsvd8[24]; // Reserved xA0-xB7 + + char xProcSrc[72]; // CSP format SRC xB8-xFF +}; +#endif // _IOHRIPROCESSORVPD_H diff -Nru a/include/asm-ppc64/iSeries/ItIplParmsReal.h b/include/asm-ppc64/iSeries/ItIplParmsReal.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/ItIplParmsReal.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,78 @@ +/* + * ItIplParmsReal.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================== +// +// This struct maps the IPL Parameters DMA'd from the SP. +// +// Warning: +// This data must map in exactly 64 bytes and match the architecture for +// the IPL parms +// +//============================================================================= + + +//------------------------------------------------------------------- +// Standard Includes +//------------------------------------------------------------------- +#ifndef _PPC_TYPES_H +#include +#endif + +#ifndef _ITIPLPARMSREAL_H +#define _ITIPLPARMSREAL_H + +struct ItIplParmsReal +{ + u8 xFormat; // Defines format of IplParms x00-x00 + u8 xRsvd01:6; // Reserved x01-x01 + u8 xAlternateSearch:1; // Alternate search indicator ... + u8 xUaSupplied:1; // UA Supplied on programmed IPL ... + u8 xLsUaFormat; // Format byte for UA x02-x02 + u8 xRsvd02; // Reserved x03-x03 + u32 xLsUa; // LS UA x04-x07 + u32 xUnusedLsLid; // First OS LID to load x08-x0B + u16 xLsBusNumber; // LS Bus Number x0C-x0D + u8 xLsCardAdr; // LS Card Address x0E-x0E + u8 xLsBoardAdr; // LS Board Address x0F-x0F + u32 xRsvd03; // Reserved x10-x13 + u8 xSpcnPresent:1; // SPCN present x14-x14 + u8 xCpmPresent:1; // CPM present ... + u8 xRsvd04:6; // Reserved ... + u8 xRsvd05:4; // Reserved x15-x15 + u8 xKeyLock:4; // Keylock setting ... + u8 xRsvd06:6; // Reserved x16-x16 + u8 xIplMode:2; // Ipl mode (A|B|C|D) ... + u8 xHwIplType; // Fast v slow v slow EC HW IPL x17-x17 + u16 xCpmEnabledIpl:1; // CPM in effect when IPL initiated x18-x19 + u16 xPowerOnResetIpl:1; // Indicate POR condition ... + u16 xMainStorePreserved:1; // Main Storage is preserved ... + u16 xRsvd07:13; // Reserved ... + u16 xIplSource:16; // Ipl source x1A-x1B + u8 xIplReason:8; // Reason for this IPL x1C-x1C + u8 xRsvd08; // Reserved x1D-x1D + u16 xRsvd09; // Reserved x1E-x1F + u16 xSysBoxType; // System Box Type x20-x21 + u16 xSysProcType; // System Processor Type x22-x23 + u32 xRsvd10; // Reserved x24-x27 + u64 xRsvd11; // Reserved x28-x2F + u64 xRsvd12; // Reserved x30-x37 + u64 xRsvd13; // Reserved x38-x3F +}; +#endif // _ITIPLPARMSREAL_H diff -Nru a/include/asm-ppc64/iSeries/ItLpNaca.h b/include/asm-ppc64/iSeries/ItLpNaca.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/ItLpNaca.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,90 @@ +/* + * ItLpNaca.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================= +// +// This control block contains the data that is shared between the +// hypervisor (PLIC) and the OS. +// +//============================================================================= + + +#ifndef _ITLPNACA_H +#define _ITLPNACA_H + +struct ItLpNaca +{ +//============================================================================= +// CACHE_LINE_1 0x0000 - 0x007F Contains read-only data +//============================================================================= + u32 xDesc; // Eye catcher x00-x03 + u16 xSize; // Size of this class x04-x05 + u16 xIntHdlrOffset; // Offset to IntHdlr array x06-x07 + u8 xMaxIntHdlrEntries; // Number of entries in array x08-x08 + u8 xPrimaryLpIndex; // LP Index of Primary x09-x09 + u8 xServiceLpIndex; // LP Ind of Service Focal Pointx0A-x0A + u8 xLpIndex; // LP Index x0B-x0B + u16 xMaxLpQueues; // Number of allocated queues x0C-x0D + u16 xLpQueueOffset; // Offset to start of LP queues x0E-x0F + u8 xPirEnvironMode:8; // Piranha or hardware x10-x10 + u8 xPirConsoleMode:8; // Piranha console indicator x11-x11 + u8 xPirDasdMode:8; // Piranha dasd indicator x12-x12 + u8 xRsvd1_0[5]; // Reserved for Piranha related x13-x17 + u8 xLparInstalled:1; // Is LPAR installed on system x18-x1F + u8 xSysPartitioned:1; // Is the system partitioned ... + u8 xHwSyncedTBs:1; // Hardware synced TBs ... + u8 xIntProcUtilHmt:1; // Utilize HMT for interrupts ... + u8 xRsvd1_1:4; // Reserved ... + u8 xSpVpdFormat:8; // VPD areas are in CSP format ... + u8 xIntProcRatio:8; // Ratio of int procs to procs ... + u8 xRsvd1_2[5]; // Reserved ... + u16 xRsvd1_3; // Reserved x20-x21 + u16 xPlicVrmIndex; // VRM index of PLIC x22-x23 + u16 xMinSupportedSlicVrmInd;// Min supported OS VRM index x24-x25 + u16 xMinCompatableSlicVrmInd;// Min compatable OS VRM index x26-x27 + u64 xLoadAreaAddr; // ER address of load area x28-x2F + u32 xLoadAreaChunks; // Chunks for the load area x30-x33 + u32 xPaseSysCallCRMask; // Mask used to test CR before x34-x37 + // doing an ASR switch on PASE + // system call. + u64 xSlicSegmentTablePtr; // Pointer to Slic seg table. x38-x3f + u8 xRsvd1_4[64]; // x40-x7F + +//============================================================================= +// CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data +//============================================================================= + u8 xRsvd2_0[128]; // Reserved x00-x7F + +//============================================================================= +// CACHE_LINE_3-6 0x0100 - 0x02FF Contains LP Queue indicators +// NB: Padding required to keep xInterrruptHdlr at x300 which is required +// for v4r4 PLIC. +//============================================================================= + u8 xOldLpQueue[128]; // LP Queue needed for v4r4 100-17F + u8 xRsvd3_0[384]; // Reserved 180-2FF +//============================================================================= +// CACHE_LINE_7-8 0x0300 - 0x03FF Contains the address of the OS interrupt +// handlers +//============================================================================= + u64 xInterruptHdlr[32]; // Interrupt handlers 300-x3FF +}; + +//============================================================================= + +#endif // _ITLPNACA_H diff -Nru a/include/asm-ppc64/iSeries/ItLpPaca.h b/include/asm-ppc64/iSeries/ItLpPaca.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/ItLpPaca.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,134 @@ +/* + * ItLpPaca.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================= +// +// This control block contains the data that is shared between the +// hypervisor (PLIC) and the OS. +// +// +//---------------------------------------------------------------------------- +#ifndef _PPC_TYPES_H +#include +#endif + +#ifndef _ITLPPACA_H +#define _ITLPPACA_H + + +struct ItLpPaca +{ +//============================================================================= +// CACHE_LINE_1 0x0000 - 0x007F Contains read-only data +// NOTE: The xDynXyz fields are fields that will be dynamically changed by +// PLIC when preparing to bring a processor online or when dispatching a +// virtual processor! +//============================================================================= + u32 xDesc; // Eye catcher 0xD397D781 x00-x03 + u16 xSize; // Size of this struct x04-x05 + u16 xRsvd1_0; // Reserved x06-x07 + u16 xRsvd1_1:14; // Reserved x08-x09 + u8 xSharedProc:1; // Shared processor indicator ... + u8 xSecondaryThread:1; // Secondary thread indicator ... + volatile u8 xDynProcStatus:8; // Dynamic Status of this proc x0A-x0A + u8 xSecondaryThreadCnt; // Secondary thread count x0B-x0B + volatile u16 xDynHvPhysicalProcIndex;// Dynamic HV Physical Proc Index0C-x0D + volatile u16 xDynHvLogicalProcIndex;// Dynamic HV Logical Proc Indexx0E-x0F + u32 xDecrVal; // Value for Decr programming x10-x13 + u32 xPMCVal; // Value for PMC regs x14-x17 + volatile u32 xDynHwNodeId; // Dynamic Hardware Node id x18-x1B + volatile u32 xDynHwProcId; // Dynamic Hardware Proc Id x1C-x1F + volatile u32 xDynPIR; // Dynamic ProcIdReg value x20-x23 + u32 xDseiData; // DSEI data x24-x27 + u64 xSPRG3; // SPRG3 value x28-x2F + u8 xRsvd1_3[80]; // Reserved x30-x7F + +//============================================================================= +// CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data +//============================================================================= + // This Dword contains a byte for each type of interrupt that can occur. + // The IPI is a count while the others are just a binary 1 or 0. + union { + u64 xAnyInt; + struct { + u16 xRsvd; // Reserved - cleared by #mpasmbl + u8 xXirrInt; // Indicates xXirrValue is valid or Immed IO + u8 xIpiCnt; // IPI Count + u8 xDecrInt; // DECR interrupt occurred + u8 xPdcInt; // PDC interrupt occurred + u8 xQuantumInt; // Interrupt quantum reached + u8 xOldPlicDeferredExtInt; // Old PLIC has a deferred XIRR pending + } xFields; + } xIntDword; + + // Whenever any fields in this Dword are set then PLIC will defer the + // processing of external interrupts. Note that PLIC will store the + // XIRR directly into the xXirrValue field so that another XIRR will + // not be presented until this one clears. The layout of the low + // 4-bytes of this Dword is upto SLIC - PLIC just checks whether the + // entire Dword is zero or not. A non-zero value in the low order + // 2-bytes will result in SLIC being granted the highest thread + // priority upon return. A 0 will return to SLIC as medium priority. + u64 xPlicDeferIntsArea; // Entire Dword + + // Used to pass the real SRR0/1 from PLIC to SLIC as well as to + // pass the target SRR0/1 from SLIC to PLIC on a SetAsrAndRfid. + u64 xSavedSrr0; // Saved SRR0 x10-x17 + u64 xSavedSrr1; // Saved SRR1 x18-x1F + + // Used to pass parms from the OS to PLIC for SetAsrAndRfid + u64 xSavedGpr3; // Saved GPR3 x20-x27 + u64 xSavedGpr4; // Saved GPR4 x28-x2F + u64 xSavedGpr5; // Saved GPR5 x30-x37 + + u8 xRsvd2_1; // Reserved x38-x38 + u8 xCpuCtlsTaskAttributes; // Task attributes for cpuctls x39-x39 + u8 xFPRegsInUse; // FP regs in use x3A-x3A + u8 xPMCRegsInUse; // PMC regs in use x3B-x3B + volatile u32 xSavedDecr; // Saved Decr Value x3C-x3F + volatile u64 xEmulatedTimeBase;// Emulated TB for this thread x40-x47 + volatile u64 xCurPLICLatency; // Unaccounted PLIC latency x48-x4F + u64 xTotPLICLatency; // Accumulated PLIC latency x50-x57 + u64 xWaitStateCycles; // Wait cycles for this proc x58-x5F + u64 xEndOfQuantum; // TB at end of quantum x60-x67 + u64 xPDCSavedSPRG1; // Saved SPRG1 for PMC int x68-x6F + u64 xPDCSavedSRR0; // Saved SRR0 for PMC int x70-x77 + volatile u32 xVirtualDecr; // Virtual DECR for shared procsx78-x7B + u32 xRsvd2_2; // Reserved x7C-x7F + +//============================================================================= +// CACHE_LINE_3 0x0100 - 0x007F: This line is shared with other processors +//============================================================================= + // This is the xYieldCount. An "odd" value (low bit on) means that + // the processor is yielded (either because of an OS yield or a PLIC + // preempt). An even value implies that the processor is currently + // executing. + // NOTE: This value will ALWAYS be zero for dedicated processors and + // will NEVER be zero for shared processors (ie, initialized to a 1). + volatile u32 xYieldCount; // PLIC increments each dispatchx00-x03 + u8 xRsvd3_0[124]; // Reserved x04-x7F + +//============================================================================= +// CACHE_LINE_4-5 0x0100 - 0x01FF Contains PMC interrupt data +//============================================================================= + u8 xPmcSaveArea[256]; // PMC interrupt Area x00-xFF + + +}; +#endif // _ITLPPACA_H diff -Nru a/include/asm-ppc64/iSeries/ItLpQueue.h b/include/asm-ppc64/iSeries/ItLpQueue.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/ItLpQueue.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,99 @@ +/* + * ItLpQueue.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//============================================================================= +// +// This control block defines the simple LP queue structure that is +// shared between the hypervisor (PLIC) and the OS in order to send +// events to an LP. +// + +#ifndef _PPC_TYPES_H +#include +#endif +#include + + +struct HvLpEvent; + + +#ifndef _ITLPQUEUE_H +#define _ITLPQUEUE_H + +#define ITMaxLpQueues 8 + +#define NotUsed 0 // Queue will not be used by PLIC +#define DedicatedIo 1 // Queue dedicated to IO processor specified +#define DedicatedLp 2 // Queue dedicated to LP specified +#define Shared 3 // Queue shared for both IO and LP + +#define LpEventStackSize 4096 +#define LpEventMaxSize 256 +#define LpEventAlign 64 + +struct ItLpQueue +{ +// +// The xSlicCurEventPtr is the pointer to the next event stack entry that will +// become valid. The OS must peek at this entry to determine if it is valid. +// PLIC will set the valid indicator as the very last store into that entry. +// +// When the OS has completed processing of the event then it will mark the event +// as invalid so that PLIC knows it can store into that event location again. +// +// If the event stack fills and there are overflow events, then PLIC will set +// the xPlicOverflowIntPending flag in which case the OS will have to fetch the +// additional LP events once they have drained the event stack. +// +// The first 16-bytes are known by both the OS and PLIC. The remainder of the +// cache line is for use by the OS. +// +//============================================================================= + u8 xPlicOverflowIntPending;// 0x00 Overflow events are pending + u8 xPlicStatus; // 0x01 DedicatedIo or DedicatedLp or NotUsed + u16 xSlicLogicalProcIndex; // 0x02 Logical Proc Index for correlation + u8 xPlicRsvd[12]; // 0x04 + char* xSlicCurEventPtr; // 0x10 + char* xSlicLastValidEventPtr; // 0x18 + char* xSlicEventStackPtr; // 0x20 + u8 xIndex; // 0x28 unique sequential index. + u8 xSlicRsvd[3]; // 0x29-2b + u32 xInUseWord; // 0x2C + u64 xLpIntCount; // 0x30 Total Lp Int msgs processed + u64 xLpIntCountByType[9]; // 0x38-0x7F Event counts by type +}; + +extern struct ItLpQueue xItLpQueue; + +extern struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * ); +extern int ItLpQueue_isLpIntPending( struct ItLpQueue * ); +extern unsigned ItLpQueue_process( struct ItLpQueue *, struct pt_regs * ); +extern void ItLpQueue_clearValid( struct HvLpEvent * ); + +static __inline__ void process_iSeries_events( void ) +{ + __asm__ __volatile__ ( + " li 0,0x5555 \n\ + sc" + : : : "r0", "r3" ); +} + + +//============================================================================= +#endif // _ITLPQUEUE_H diff -Nru a/include/asm-ppc64/iSeries/ItLpRegSave.h b/include/asm-ppc64/iSeries/ItLpRegSave.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/ItLpRegSave.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,87 @@ +/* + * ItLpRegSave.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//===================================================================================== +// +// This control block contains the data that is shared between PLIC +// and the OS +// +// + +#ifndef _ITLPREGSAVE_H +#define _ITLPREGSAVE_H + +struct ItLpRegSave +{ + u32 xDesc; // Eye catcher "LpRS" ebcdic 000-003 + u16 xSize; // Size of this class 004-005 + u8 xInUse; // Area is live 006-007 + u8 xRsvd1[9]; // Reserved 007-00F + + u8 xFixedRegSave[352]; // Fixed Register Save Area 010-16F + u32 xCTRL; // Control Register 170-173 + u32 xDEC; // Decrementer 174-177 + u32 xFPSCR; // FP Status and Control Reg 178-17B + u32 xPVR; // Processor Version Number 17C-17F + + u64 xMMCR0; // Monitor Mode Control Reg 0 180-187 + u32 xPMC1; // Perf Monitor Counter 1 188-18B + u32 xPMC2; // Perf Monitor Counter 2 18C-18F + u32 xPMC3; // Perf Monitor Counter 3 190-193 + u32 xPMC4; // Perf Monitor Counter 4 194-197 + u32 xPIR; // Processor ID Reg 198-19B + + u32 xMMCR1; // Monitor Mode Control Reg 1 19C-19F + u32 xMMCRA; // Monitor Mode Control Reg A 1A0-1A3 + u32 xPMC5; // Perf Monitor Counter 5 1A4-1A7 + u32 xPMC6; // Perf Monitor Counter 6 1A8-1AB + u32 xPMC7; // Perf Monitor Counter 7 1AC-1AF + u32 xPMC8; // Perf Monitor Counter 8 1B0-1B3 + u32 xTSC; // Thread Switch Control 1B4-1B7 + u32 xTST; // Thread Switch Timeout 1B8-1BB + u32 xRsvd; // Reserved 1BC-1BF + + u64 xACCR; // Address Compare Control Reg 1C0-1C7 + u64 xIMR; // Instruction Match Register 1C8-1CF + u64 xSDR1; // Storage Description Reg 1 1D0-1D7 + u64 xSPRG0; // Special Purpose Reg General0 1D8-1DF + u64 xSPRG1; // Special Purpose Reg General1 1E0-1E7 + u64 xSPRG2; // Special Purpose Reg General2 1E8-1EF + u64 xSPRG3; // Special Purpose Reg General3 1F0-1F7 + u64 xTB; // Time Base Register 1F8-1FF + + u64 xFPR[32]; // Floating Point Registers 200-2FF + + u64 xMSR; // Machine State Register 300-307 + u64 xNIA; // Next Instruction Address 308-30F + + u64 xDABR; // Data Address Breakpoint Reg 310-317 + u64 xIABR; // Inst Address Breakpoint Reg 318-31F + + u64 xHID0; // HW Implementation Dependent0 320-327 + + u64 xHID4; // HW Implementation Dependent4 328-32F + u64 xSCOMd; // SCON Data Reg (SPRG4) 330-337 + u64 xSCOMc; // SCON Command Reg (SPRG5) 338-33F + u64 xSDAR; // Sample Data Address Register 340-347 + u64 xSIAR; // Sample Inst Address Register 348-34F + + u8 xRsvd3[176]; // Reserved 350-3FF +}; +#endif // _ITLPREGSAVE_H diff -Nru a/include/asm-ppc64/iSeries/ItSpCommArea.h b/include/asm-ppc64/iSeries/ItSpCommArea.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/ItSpCommArea.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,39 @@ +/* + * ItSpCommArea.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _ITSPCOMMAREA_H +#define _ITSPCOMMAREA_H + + +struct SpCommArea +{ + u32 xDesc; // Descriptor (only in new formats) 000-003 + u8 xFormat; // Format (only in new formats) 004-004 + u8 xRsvd1[11]; // Reserved 005-00F + u64 xRawTbAtIplStart; // Raw HW TB value when IPL is started 010-017 + u64 xRawTodAtIplStart; // Raw HW TOD value when IPL is started 018-01F + u64 xBcdTimeAtIplStart; // BCD time when IPL is started 020-027 + u64 xBcdTimeAtOsStart; // BCD time when OS passed control 028-02F + u8 xRsvd2[80]; // Reserved 030-07F +}; + +extern struct SpCommArea xSpCommArea; + +#endif /* _ITSPCOMMAREA_H */ diff -Nru a/include/asm-ppc64/iSeries/ItVpdAreas.h b/include/asm-ppc64/iSeries/ItVpdAreas.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/ItVpdAreas.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,100 @@ +/* + * ItVpdAreas.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//===================================================================================== +// +// This file defines the address and length of all of the VPD area passed to +// the OS from PLIC (most of which start from the SP). +// + +#ifndef _PPC_TYPES_H +#include +#endif + + +#ifndef _ITVPDAREAS_H +#define _ITVPDAREAS_H + +// VPD Entry index is carved in stone - cannot be changed (easily). +#define ItVpdCecVpd 0 +#define ItVpdDynamicSpace 1 +#define ItVpdExtVpd 2 +#define ItVpdExtVpdOnPanel 3 +#define ItVpdFirstPaca 4 +#define ItVpdIoVpd 5 +#define ItVpdIplParms 6 +#define ItVpdMsVpd 7 +#define ItVpdPanelVpd 8 +#define ItVpdLpNaca 9 +#define ItVpdBackplaneAndMaybeClockCardVpd 10 +#define ItVpdRecoveryLogBuffer 11 +#define ItVpdSpCommArea 12 +#define ItVpdSpLogBuffer 13 +#define ItVpdSpLogBufferSave 14 +#define ItVpdSpCardVpd 15 +#define ItVpdFirstProcVpd 16 +#define ItVpdApModelVpd 17 +#define ItVpdClockCardVpd 18 +#define ItVpdBusExtCardVpd 19 +#define ItVpdProcCapacityVpd 20 +#define ItVpdInteractiveCapacityVpd 21 +#define ItVpdFirstSlotLabel 22 +#define ItVpdFirstLpQueue 23 +#define ItVpdFirstL3CacheVpd 24 +#define ItVpdFirstProcFruVpd 25 + +#define ItVpdMaxEntries 26 + + +#define ItDmaMaxEntries 10 + +#define ItVpdAreasMaxSlotLabels 192 + + +struct SlicVpdAdrs { + u32 pad1; + void * vpdAddr; +}; + + +struct ItVpdAreas +{ + u32 xSlicDesc; // Descriptor 000-003 + u16 xSlicSize; // Size of this control block 004-005 + u16 xPlicAdjustVpdLens:1; // Flag to indicate new interface 006-007 + u16 xRsvd1:15; // Reserved bits ... + u16 xSlicVpdEntries; // Number of VPD entries 008-009 + u16 xSlicDmaEntries; // Number of DMA entries 00A-00B + u16 xSlicMaxLogicalProcs; // Maximum logical processors 00C-00D + u16 xSlicMaxPhysicalProcs; // Maximum physical processors 00E-00F + u16 xSlicDmaToksOffset; // Offset into this of array 010-011 + u16 xSlicVpdAdrsOffset; // Offset into this of array 012-013 + u16 xSlicDmaLensOffset; // Offset into this of array 014-015 + u16 xSlicVpdLensOffset; // Offset into this of array 016-017 + u16 xSlicMaxSlotLabels; // Maximum number of slot labels 018-019 + u16 xSlicMaxLpQueues; // Maximum number of LP Queues 01A-01B + u8 xRsvd2[4]; // Reserved 01C-01F + u64 xRsvd3[12]; // Reserved 020-07F + u32 xPlicDmaLens[ItDmaMaxEntries];// Array of DMA lengths 080-0A7 + u32 xPlicDmaToks[ItDmaMaxEntries];// Array of DMA tokens 0A8-0CF + u32 xSlicVpdLens[ItVpdMaxEntries];// Array of VPD lengths 0D0-12F + void * xSlicVpdAdrs[ItVpdMaxEntries];// Array of VPD buffers 130-1EF +}; + +#endif // _ITVPDAREAS_H diff -Nru a/include/asm-ppc64/iSeries/LparData.h b/include/asm-ppc64/iSeries/LparData.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/LparData.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,57 @@ +/* + * LparData.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PPC_TYPES_H +#include +#endif + +#ifndef _LPARDATA_H +#define _LPARDATA_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct LparMap xLparMap; +extern struct Naca xNaca; +extern struct Paca xPaca[]; +extern struct HvReleaseData hvReleaseData; +extern struct ItLpNaca itLpNaca; +extern struct ItIplParmsReal xItIplParmsReal; +extern struct IoHriProcessorVpd xIoHriProcessorVpd[]; +extern struct ItLpQueue xItLpQueue; +extern struct ItVpdAreas itVpdAreas; +extern u64 xMsVpd[]; +extern struct msChunks msChunks; + + +#endif /* _LPARDATA_H */ diff -Nru a/include/asm-ppc64/iSeries/LparMap.h b/include/asm-ppc64/iSeries/LparMap.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/LparMap.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,70 @@ +/* + * LparMap.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PPC_TYPES_H +#include +#endif + +#ifndef _LPARMAP_H +#define _LPARMAP_H + +/* The iSeries hypervisor will set up mapping for one or more + * ESID/VSID pairs (in SLB/segment registers) and will set up + * mappings of one or more ranges of pages to VAs. + * We will have the hypervisor set up the ESID->VSID mapping + * for the four kernel segments (C-F). With shared processors, + * the hypervisor will clear all segment registers and reload + * these four whenever the processor is switched from one + * partition to another. + */ + +/* The Vsid and Esid identified below will be used by the hypervisor + * to set up a memory mapping for part of the load area before giving + * control to the Linux kernel. The load area is 64 MB, but this must + * not attempt to map the whole load area. The Hashed Page Table may + * need to be located within the load area (if the total partition size + * is 64 MB), but cannot be mapped. Typically, this should specify + * to map half (32 MB) of the load area. + * + * The hypervisor will set up page table entries for the number of + * pages specified. + * + * In 32-bit mode, the hypervisor will load all four of the + * segment registers (identified by the low-order four bits of the + * Esid field. In 64-bit mode, the hypervisor will load one SLB + * entry to map the Esid to the Vsid. +*/ + +// Hypervisor initially maps 32MB of the load area +#define HvPagesToMap 8192 + +struct LparMap +{ + u64 xNumberEsids; // Number of ESID/VSID pairs (1) + u64 xNumberRanges; // Number of VA ranges to map (1) + u64 xSegmentTableOffs; // Page number within load area of seg table (0) + u64 xRsvd[5]; // Reserved (0) + u64 xKernelEsid; // Esid used to map kernel load (0x0C00000000) + u64 xKernelVsid; // Vsid used to map kernel load (0x0C00000000) + u64 xPages; // Number of pages to be mapped (8192) + u64 xOffset; // Offset from start of load area (0) + u64 xVPN; // Virtual Page Number (0x000C000000000000) +}; + +#endif /* _LPARMAP_H */ diff -Nru a/include/asm-ppc64/iSeries/XmPciLpEvent.h b/include/asm-ppc64/iSeries/XmPciLpEvent.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/XmPciLpEvent.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,18 @@ + +#ifndef __XMPCILPEVENT_H__ +#define __XMPCILPEVENT_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +int XmPciLpEvent_init(void); +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); + + +#ifdef __cplusplus +} +#endif + +#endif /* __XMPCILPEVENT_H__ */ diff -Nru a/include/asm-ppc64/iSeries/iSeries_VpdInfo.h b/include/asm-ppc64/iSeries/iSeries_VpdInfo.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/iSeries_VpdInfo.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,56 @@ +#ifndef _ISERIES_VPDINFO_H +#define _ISERIES_VPDINFO_H +/************************************************************************/ +/* File iSeries_VpdInfo.h created by Allan Trautman Feb 08 2001. */ +/************************************************************************/ +/* This code supports the location data fon on the IBM iSeries systems. */ +/* Copyright (C) 20yy */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Feg 8, 2001 */ +/* Reformated for Card, March 8, 2001 */ +/* End Change Activity */ +/************************************************************************/ + +struct pci_dev; /* Forward Declare */ +/************************************************************************/ +/* Location Data extracted from the VPD list and device info. */ +/************************************************************************/ +struct LocationDataStruct { /* Location data structure for device */ + u16 Bus; /* iSeries Bus Number 0x00*/ + u16 Board; /* iSeries Board 0x02*/ + u8 FrameId; /* iSeries spcn Frame Id 0x04*/ + u8 PhbId; /* iSeries Phb Location 0x05*/ + u16 Card; /* iSeries Card Slot 0x06*/ + char CardLocation[4]; /* Char format of planar vpd 0x08*/ + u8 AgentId; /* iSeries AgentId 0x0C*/ + u8 SecondaryAgentId; /* iSeries Secondary Agent Id 0x0D*/ + u8 LinuxBus; /* Linux Bus Number 0x0E*/ + u8 LinuxDevFn; /* Linux Device Function 0x0F*/ +}; +typedef struct LocationDataStruct LocationData; +#define LOCATION_DATA_SIZE 16 + +/************************************************************************/ +/* Protypes */ +/************************************************************************/ +extern LocationData* iSeries_GetLocationData(struct pci_dev* PciDev); +extern int iSeries_Device_Information(struct pci_dev*,char*, int); + +#endif /* _ISERIES_VPDINFO_H */ diff -Nru a/include/asm-ppc64/iSeries/iSeries_dma.h b/include/asm-ppc64/iSeries/iSeries_dma.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/iSeries_dma.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,97 @@ +/* + * iSeries_dma.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ISERIES_DMA_H +#define _ISERIES_DMA_H + +#include +#ifndef __LINUX_SPINLOCK_H +#include +#endif + +// NUM_TCE_LEVELS defines the largest contiguous block +// of dma (tce) space we can get. NUM_TCE_LEVELS = 10 +// allows up to 2**9 pages (512 * 4096) = 2 MB +#define NUM_TCE_LEVELS 10 + +#define NO_TCE ((dma_addr_t)-1) + +// Tces come in two formats, one for the virtual bus and a different +// format for PCI +#define TCE_VB 0 +#define TCE_PCI 1 + + +union Tce { + u64 wholeTce; + struct { + u64 cacheBits :6; /* Cache hash bits - not used */ + u64 rsvd :6; + u64 rpn :40; /* Absolute page number */ + u64 valid :1; /* Tce is valid (vb only) */ + u64 allIo :1; /* Tce is valid for all lps (vb only) */ + u64 lpIndex :8; /* LpIndex for user of TCE (vb only) */ + u64 pciWrite :1; /* Write allowed (pci only) */ + u64 readWrite :1; /* Read allowed (pci), Write allowed + (vb) */ + } tceBits; +}; + +struct Bitmap { + unsigned long numBits; + unsigned long numBytes; + unsigned char * map; +}; + +struct MultiLevelBitmap { + unsigned long maxLevel; + struct Bitmap level[NUM_TCE_LEVELS]; +}; + +struct TceTable { + u64 busNumber; + u64 size; + u64 startOffset; + u64 index; + spinlock_t lock; + struct MultiLevelBitmap mlbm; +}; + +struct HvTceTableManagerCB { + u64 busNumber; /* Bus number for this tce table */ + u64 start; /* Will be NULL for secondary */ + u64 totalSize; /* Size (in pages) of whole table */ + u64 startOffset; /* Index into real tce table of the + start of our section */ + u64 size; /* Size (in pages) of our section */ + u64 index; /* Index of this tce table (token?) */ + u16 maxTceTableIndex; /* Max number of tables for partition */ + u8 virtualBusFlag; /* Flag to indicate virtual bus */ + u8 rsvd[5]; +}; + +extern struct TceTable virtBusTceTable; /* Tce table for virtual bus */ + +extern struct TceTable * build_tce_table( struct HvTceTableManagerCB *, + struct TceTable *); +extern void create_virtual_bus_tce_table( void ); + +extern void create_pci_bus_tce_table( unsigned busNumber ); + +#endif // _ISERIES_DMA_H diff -Nru a/include/asm-ppc64/iSeries/iSeries_fixup.h b/include/asm-ppc64/iSeries/iSeries_fixup.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/iSeries_fixup.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,25 @@ + +#ifndef __ISERIES_FIXUP_H__ +#define __ISERIES_FIXUP_H__ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void iSeries_fixup (void); +void iSeries_fixup_bus (struct pci_bus*); +unsigned int iSeries_scan_slot (struct pci_dev*, u16, u8, u8); + + +/* Need to store information related to the PHB bucc and make it accessible to the hose */ +struct iSeries_hose_arch_data { + u32 hvBusNumber; +}; + + +#ifdef __cplusplus +} +#endif + +#endif /* __ISERIES_FIXUP_H__ */ diff -Nru a/include/asm-ppc64/iSeries/iSeries_io.h b/include/asm-ppc64/iSeries/iSeries_io.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/iSeries_io.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,46 @@ +#include + +#ifdef CONFIG_PPC_ISERIES +#ifndef _ISERIES_IO_H +#define _ISERIES_IO_H +#include +/************************************************************************/ +/* File iSeries_io.h created by Allan Trautman on Thu Dec 28 2000. */ +/************************************************************************/ +/* Remaps the io.h for the iSeries Io */ +/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created December 28, 2000 */ +/* End Change Activity */ +/************************************************************************/ +extern u8 iSeries_Read_Byte(void* IoAddress); +extern u16 iSeries_Read_Word(void* IoAddress); +extern u32 iSeries_Read_Long(void* IoAddress); +extern void iSeries_Write_Byte(u8 IoData,void* IoAddress); +extern void iSeries_Write_Word(u16 IoData,void* IoAddress); +extern void iSeries_Write_Long(u32 IoData,void* IoAddress); + +extern void* iSeries_memset_io(void *dest, char x, size_t n); +extern void* iSeries_memcpy_toio(void *dest, void *source, size_t n); +extern void* iSeries_memcpy_fromio(void *dest, void *source, size_t n); + +#endif /* _ISERIES_IO_H */ +#endif /* CONFIG_PPC_ISERIES */ + diff -Nru a/include/asm-ppc64/iSeries/iSeries_irq.h b/include/asm-ppc64/iSeries/iSeries_irq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/iSeries_irq.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,27 @@ + +#ifndef __ISERIES_IRQ_H__ +#define __ISERIES_IRQ_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned int iSeries_startup_IRQ(unsigned int); +void iSeries_shutdown_IRQ(unsigned int); +void iSeries_enable_IRQ(unsigned int); +void iSeries_disable_IRQ(unsigned int); +void iSeries_end_IRQ(unsigned int); +void iSeries_init_IRQ(void); +void iSeries_init_irqMap(int); +int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, HvAgentId); +int iSeries_assign_IRQ(int, HvBusNumber, HvSubBusNumber, HvAgentId); +void iSeries_activate_IRQs(void); + +int XmPciLpEvent_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ISERIES_IRQ_H__ */ diff -Nru a/include/asm-ppc64/iSeries/iSeries_pci.h b/include/asm-ppc64/iSeries/iSeries_pci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/iSeries_pci.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,125 @@ +#ifndef _ISERIES_64_PCI_H +#define _ISERIES_64_PCI_H +/************************************************************************/ +/* File iSeries_pci.h created by Allan Trautman on Tue Feb 20, 2001. */ +/************************************************************************/ +/* Define some useful macros for the iSeries pci routines. */ +/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the: */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, */ +/* Boston, MA 02111-1307 USA */ +/************************************************************************/ +/* Change Activity: */ +/* Created Feb 20, 2001 */ +/* Added device reset, March 22, 2001 */ +/* Ported to ppc64, May 25, 2001 */ +/* End Change Activity */ +/************************************************************************/ +#include + +struct pci_dev; /* For Forward Reference */ +struct iSeries_Device_Node; +/************************************************************************/ +/* Gets iSeries Bus, SubBus, of DevFn using pci_dev* structure */ +/************************************************************************/ +#define ISERIES_BUS(DevPtr) DevPtr->DsaAddr.busNumber +#define ISERIES_SUBBUS(DevPtr) DevPtr->DsaAddr.subBusNumber +#define ISERIES_DEVICE(DevPtr) DevPtr->DsaAddr.deviceId +#define ISERIES_DEVFUN(DevPtr) DevPtr->DevFn +#define ISERIES_DSA(DevPtr) (*(u64*)&DevPtr->DsaAddr) + +#define EADsMaxAgents 7 +/************************************************************************************/ +/* Decodes Linux DevFn to iSeries DevFn, bridge device, or function. */ +/* For Linux, see PCI_SLOT and PCI_FUNC in include/linux/pci.h */ +/************************************************************************************/ +#define ISERIES_DECODE_DEVFN(linuxdevfn) (((linuxdevfn & 0x71) << 1) | (linuxdevfn & 0x07)) +#define ISERIES_DECODE_DEVICE(linuxdevfn) (((linuxdevfn & 0x38) >> 3) |(((linuxdevfn & 0x40) >> 2) + 0x10)) +#define ISERIES_DECODE_FUNCTION(linuxdevfn) (linuxdevfn & 0x07) +#define ISERIES_PCI_AGENTID(idsel,func) ((idsel & 0x0F) << 4) | (func & 0x07) + +#define ISERIES_GET_DEVICE_FROM_SUBBUS(subbus) ((subbus >> 5) & 0x7) +#define ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus) ((subbus >> 2) & 0x7) + +#define ISERIES_ENCODE_DEVICE(agentid) ((0x10) | ((agentid&0x20)>>2) | (agentid&07)) +/************************************************************************************/ +/* Converts Virtual Address to Real Address for Hypervisor calls */ +/************************************************************************************/ +#define REALADDR(virtaddr) (0x8000000000000000 | (virt_to_absolute((u64)virtaddr) )) + +/************************************************************************************/ +/* Define TRUE and FALSE Values for Al */ +/************************************************************************************/ +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/************************************************************************/ +/* iSeries Device Information */ +/************************************************************************/ +struct iSeries_Device_Node { + struct list_head Device_List; /* Must be first for cast to wo*/ + struct pci_dev* PciDev; /* Pointer to pci_dev structure*/ + struct HvCallPci_DsaAddr DsaAddr;/* Direct Select Address */ + /* busNumber,subBusNumber, */ + /* deviceId, barNumber */ + HvAgentId AgentId; /* Hypervisor DevFn */ + int DevFn; /* Linux devfn */ + int BarOffset; + int Irq; /* Assigned IRQ */ + int ReturnCode; /* Return Code Holder */ + int IoRetry; /* Current Retry Count */ + int Flags; /* Possible flags(disable/bist)*/ + u16 Vendor; /* Vendor ID */ + struct TceTable* DevTceTable; /* Device TCE Table */ + u8 PhbId; /* Phb Card is on. */ + u16 Board; /* Board Number */ + u8 FrameId; /* iSeries spcn Frame Id */ + char CardLocation[4];/* Char format of planar vpd */ + char Location[20]; /* Frame 1, Card C10 */ +}; +/************************************************************************/ +/* Location Data extracted from the VPD list and device info. */ +/************************************************************************/ +struct LocationDataStruct { /* Location data structure for device */ + u16 Bus; /* iSeries Bus Number 0x00*/ + u16 Board; /* iSeries Board 0x02*/ + u8 FrameId; /* iSeries spcn Frame Id 0x04*/ + u8 PhbId; /* iSeries Phb Location 0x05*/ + u8 AgentId; /* iSeries AgentId 0x06*/ + u8 Card; + char CardLocation[4]; +}; +typedef struct LocationDataStruct LocationData; +#define LOCATION_DATA_SIZE 48 +/************************************************************************/ +/* Flight Recorder tracing */ +/************************************************************************/ +extern int iSeries_Set_PciTraceFlag(int TraceFlag); +extern int iSeries_Get_PciTraceFlag(void); + +/************************************************************************/ +/* Functions */ +/************************************************************************/ +extern LocationData* iSeries_GetLocationData(struct pci_dev* PciDev); +extern int iSeries_Device_Information(struct pci_dev*,char*, int); +extern void iSeries_Get_Location_Code(struct iSeries_Device_Node*); +extern int iSeries_Device_ToggleReset(struct pci_dev* PciDev, int AssertTime, int DelayTime); + +#endif /* _ISERIES_64_PCI_H */ diff -Nru a/include/asm-ppc64/iSeries/iSeries_proc.h b/include/asm-ppc64/iSeries/iSeries_proc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/iSeries_proc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,37 @@ +/* + * iSeries_proc.h + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _ISERIES_PROC_H +#define _ISERIES_PROC_H + +#include + +extern void iSeries_proc_early_init(void); +extern void iSeries_proc_create(void); + +typedef void (*iSeriesProcFunction)(struct proc_dir_entry *iSeries_proc); + +extern void iSeries_proc_callback(iSeriesProcFunction initFunction); + +#endif /* _iSeries_PROC_H */ + diff -Nru a/include/asm-ppc64/iSeries/mf.h b/include/asm-ppc64/iSeries/mf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/mf.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,83 @@ +/* + * mf.h + * Copyright (C) 2001 Troy D. Armstrong IBM Corporation + * + * This modules exists as an interface between a Linux secondary partition + * running on an iSeries and the primary partition's Virtual Service + * Processor (VSP) object. The VSP has final authority over powering on/off + * all partitions in the iSeries. It also provides miscellaneous low-level + * machine facility type operations. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +#ifndef MF_H_INCLUDED +#define MF_H_INCLUDED + +#include +#include + +struct rtc_time; + +typedef void (*MFCompleteHandler)( void * clientToken, int returnCode ); + +extern void mf_allocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned size, + unsigned amount, + MFCompleteHandler hdlr, + void * userToken ); + +extern void mf_deallocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned count, + MFCompleteHandler hdlr, + void * userToken ); + +extern void mf_powerOff( void ); + +extern void mf_reboot( void ); + +extern void mf_displaySrc( u32 word ); +extern void mf_displayProgress( u16 value ); + +extern void mf_clearSrc( void ); + +extern void mf_init( void ); + +extern void mf_setSide(char side); + +extern char mf_getSide(void); + +extern void mf_setCmdLine(const char *cmdline, int size, u64 side); + +extern int mf_getCmdLine(char *cmdline, int *size, u64 side); + +extern void mf_getSrcHistory(char *buffer, int size); + +extern int mf_setVmlinuxChunk(const char *buffer, int size, int offset, u64 side); + +extern int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side); + +extern int mf_setRtcTime(unsigned long time); + +extern int mf_getRtcTime(unsigned long *time); + +extern int mf_getRtc( struct rtc_time * tm ); + +extern int mf_setRtc( struct rtc_time * tm ); + +#endif /* MF_H_INCLUDED */ diff -Nru a/include/asm-ppc64/iSeries/mf_proc.h b/include/asm-ppc64/iSeries/mf_proc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/mf_proc.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,33 @@ +/* + * mf_proc.h + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _MF_PROC_H +#define _MF_PROC_H + +#include + +void mf_proc_init(struct proc_dir_entry *iSeries_proc); + + +#endif /* _MF_PROC_H */ + diff -Nru a/include/asm-ppc64/iSeries/veth-proc.h b/include/asm-ppc64/iSeries/veth-proc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/iSeries/veth-proc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,32 @@ +/* + * veth-proc.h + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _VETH_PROC_H +#define _VETH_PROC_H + +#include + +void veth_proc_init(struct proc_dir_entry *iSeries_proc); + +#endif /* _VETH-PROC_H */ + diff -Nru a/include/asm-ppc64/ide.h b/include/asm-ppc64/ide.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/ide.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,95 @@ +/* + * linux/include/asm-ppc/ide.h + * + * Copyright (C) 1994-1996 Linus Torvalds & authors + * + * 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 file contains the ppc64 architecture specific IDE code. + */ + +#ifndef __ASMPPC64_IDE_H +#define __ASMPPC64_IDE_H + +#ifdef __KERNEL__ + +#ifndef MAX_HWIFS +#define MAX_HWIFS 4 +#endif + +#define ide__sti() __sti() + +void ppc64_ide_fix_driveid(struct hd_driveid *id); +#define ide_fix_driveid(id) ppc64_ide_fix_driveid((id)) + +static __inline__ int ide_default_irq(ide_ioreg_t base) { return 0; } +static __inline__ ide_ioreg_t ide_default_io_base(int index) { return 0; } + +static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) +{ + ide_ioreg_t reg = data_port; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = reg; + reg += 1; + } + if (ctrl_port) { + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + } else { + hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206; + } + if (irq != NULL) + *irq = 0; + hw->io_ports[IDE_IRQ_OFFSET] = 0; +} + +static __inline__ void ide_init_default_hwifs(void) +{ +} + +typedef union { + unsigned all : 8; /* all of the bits together */ + struct { + unsigned head : 4; /* always zeros here */ + unsigned unit : 1; /* drive select number, 0 or 1 */ + unsigned bit5 : 1; /* always 1 */ + unsigned lba : 1; /* using LBA instead of CHS */ + unsigned bit7 : 1; /* always 1 */ + } b; +} select_t; + +/* XXX is this correct? - Anton */ +typedef union { + unsigned all : 8; /* all of the bits together */ + struct { + unsigned HOB : 1; /* 48-bit address ordering */ + unsigned reserved456 : 3; + unsigned bit3 : 1; /* ATA-2 thingy */ + unsigned SRST : 1; /* host soft reset bit */ + unsigned nIEN : 1; /* device INTRQ to host */ + unsigned bit0 : 1; + } b; +} control_t; + +#define ide_request_irq(irq,hand,flg,dev,id) request_irq((irq),(hand),(flg),(dev),(id)) +#define ide_free_irq(irq,dev_id) free_irq((irq), (dev_id)) +#define ide_check_region(from,extent) check_region((from), (extent)) +#define ide_request_region(from,extent,name) request_region((from), (extent), (name)) +#define ide_release_region(from,extent) release_region((from), (extent)) + +/* + * The following are not needed for the non-m68k ports + */ +#define ide_ack_intr(hwif) (1) +#define ide_release_lock(lock) do {} while (0) +#define ide_get_lock(lock, hdlr, data) do {} while (0) + +#endif /* __KERNEL__ */ + +#endif /* __ASMPPC64_IDE_H */ diff -Nru a/include/asm-ppc64/init.h b/include/asm-ppc64/init.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/init.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,29 @@ +#ifndef _PPC64_INIT_H +#define _PPC64_INIT_H + +#include + +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 90 /* egcs */ +/* DRENG add back in when we get section attribute support */ +#define __chrp __attribute__ ((__section__ (".text.chrp"))) +#define __chrpdata __attribute__ ((__section__ (".data.chrp"))) +#define __chrpfunc(__argchrp) \ + __argchrp __chrp; \ + __argchrp + +/* this is actually just common chrp/pmac code, not OF code -- Cort */ +#define __openfirmware __attribute__ ((__section__ (".text.openfirmware"))) +#define __openfirmwaredata __attribute__ ((__section__ (".data.openfirmware"))) +#define __openfirmwarefunc(__argopenfirmware) \ + __argopenfirmware __openfirmware; \ + __argopenfirmware + +#else /* not egcs */ + +#define __openfirmware +#define __openfirmwaredata +#define __openfirmwarefunc(x) x + +#endif /* egcs */ + +#endif /* _PPC64_INIT_H */ diff -Nru a/include/asm-ppc64/io.h b/include/asm-ppc64/io.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/io.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,338 @@ +#ifndef _PPC64_IO_H +#define _PPC64_IO_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#ifdef CONFIG_PPC_ISERIES +#include +#endif +#include +#include + +#define SIO_CONFIG_RA 0x398 +#define SIO_CONFIG_RD 0x399 + +#define SLOW_DOWN_IO +/* Define this if you want to see virt_to_* messages */ +#undef __IO_DEBUG + +extern unsigned long isa_io_base; +extern unsigned long isa_mem_base; +extern unsigned long pci_io_base; +extern unsigned long pci_dram_offset; +extern int have_print; +#define _IO_BASE isa_io_base +#define _ISA_MEM_BASE isa_mem_base +#define PCI_DRAM_OFFSET pci_dram_offset + +#ifdef CONFIG_PPC_ISERIES +#define readb(addr) iSeries_Read_Byte((void*)(addr)) +#define readw(addr) iSeries_Read_Word((void*)(addr)) +#define readl(addr) iSeries_Read_Long((void*)(addr)) +#define writeb(data, addr) iSeries_Write_Byte(data,((void*)(addr))) +#define writew(data, addr) iSeries_Write_Word(data,((void*)(addr))) +#define writel(data, addr) iSeries_Write_Long(data,((void*)(addr))) +#define memset_io(a,b,c) iSeries_memset((void *)(a),(b),(c)) +#define memcpy_fromio(a,b,c) iSeries_memcpy_fromio((void *)(a), (void *)(b), (c)) +#define memcpy_toio(a,b,c) iSeries_memcpy_toio((void *)(a), (void *)(b), (c)) +#define inb(addr) readb(((unsigned long)(addr))) +#define inw(addr) readw(((unsigned long)(addr))) +#define inl(addr) readl(((unsigned long)(addr))) +#define outb(data,addr) writeb(data,((unsigned long)(addr))) +#define outw(data,addr) writew(data,((unsigned long)(addr))) +#define outl(data,addr) writel(data,((unsigned long)(addr))) +#else +#define IS_MAPPED_VADDR(port) ((unsigned long)(port) >> 60UL) +#ifdef CONFIG_PPC_EEH +#define readb(addr) eeh_readb((void*)(addr)) +#define readw(addr) eeh_readw((void*)(addr)) +#define readl(addr) eeh_readl((void*)(addr)) +#define writeb(data, addr) eeh_writeb((data), ((void*)(addr))) +#define writew(data, addr) eeh_writew((data), ((void*)(addr))) +#define writel(data, addr) eeh_writel((data), ((void*)(addr))) +#define memset_io(a,b,c) eeh_memset((void *)(a),(b),(c)) +#define memcpy_fromio(a,b,c) eeh_memcpy_fromio((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) eeh_memcpy_toio((void *)(a),(b),(c)) +#else +#define readb(addr) in_8((volatile u8 *)(addr)) +#define writeb(b,addr) out_8((volatile u8 *)(addr), (b)) +#define readw(addr) in_le16((volatile u16 *)(addr)) +#define readl(addr) in_le32((volatile u32 *)(addr)) +#define writew(b,addr) out_le16((volatile u16 *)(addr),(b)) +#define writel(b,addr) out_le32((volatile u32 *)(addr),(b)) +#define memset_io(a,b,c) memset((void *)(a),(b),(c)) +#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) +#endif +#define inb(port) _inb((unsigned long)port) +#define outb(val, port) _outb(val, (unsigned long)port) +#define inw(port) _inw((unsigned long)port) +#define outw(val, port) _outw(val, (unsigned long)port) +#define inl(port) _inl((unsigned long)port) +#define outl(val, port) _outl(val, (unsigned long)port) +#endif + + +/* + * output pause versions need a delay at least for the + * w83c105 ide controller in a p610. + */ +#define inb_p(port) inb(port) +#define outb_p(val, port) (udelay(1), outb((val), (port))) +#define inw_p(port) inw(port) +#define outw_p(val, port) (udelay(1), outw((val), (port))) +#define inl_p(port) inl(port) +#define outl_p(val, port) (udelay(1), outl((val, (port))) + +/* + * The insw/outsw/insl/outsl macros don't do byte-swapping. + * They are only used in practice for transferring buffers which + * are arrays of bytes, and byte-swapping is not appropriate in + * that case. - paulus */ +#define _IOMAP_VADDR(port) (IS_MAPPED_VADDR(port) ? (port) : (port)+_IO_BASE) +#define insb(port, buf, ns) _insb((u8 *)(_IOMAP_VADDR(port)), (buf), (ns)) +#define outsb(port, buf, ns) _outsb((u8 *)(_IOMAP_VADDR(port)), (buf), (ns)) +#define insw(port, buf, ns) _insw_ns((u16 *)(_IOMAP_VADDR(port)), (buf), (ns)) +#define outsw(port, buf, ns) _outsw_ns((u16 *)(_IOMAP_VADDR(port)), (buf), (ns)) +#define insl(port, buf, nl) _insl_ns((u32 *)(_IOMAP_VADDR(port)), (buf), (nl)) +#define outsl(port, buf, nl) _outsl_ns((u32 *)(_IOMAP_VADDR(port)), (buf), (nl)) + +extern void _insb(volatile u8 *port, void *buf, int ns); +extern void _outsb(volatile u8 *port, const void *buf, int ns); +extern void _insw(volatile u16 *port, void *buf, int ns); +extern void _outsw(volatile u16 *port, const void *buf, int ns); +extern void _insl(volatile u32 *port, void *buf, int nl); +extern void _outsl(volatile u32 *port, const void *buf, int nl); +extern void _insw_ns(volatile u16 *port, void *buf, int ns); +extern void _outsw_ns(volatile u16 *port, const void *buf, int ns); +extern void _insl_ns(volatile u32 *port, void *buf, int nl); +extern void _outsl_ns(volatile u32 *port, const void *buf, int nl); + +/* + * The *_ns versions below don't do byte-swapping. + * Neither do the standard versions now, these are just here + * for older code. + */ +#define insw_ns(port, buf, ns) _insw_ns((u16 *)(_IOMAP_VADDR(port)), (buf), (ns)) +#define outsw_ns(port, buf, ns) _outsw_ns((u16 *)(_IOMAP_VADDR(port)), (buf), (ns)) +#define insl_ns(port, buf, nl) _insl_ns((u32 *)(_IOMAP_VADDR(port)), (buf), (nl)) +#define outsl_ns(port, buf, nl) _outsl_ns((u32 *)(_IOMAP_VADDR(port)), (buf), (nl)) + + +#define IO_SPACE_LIMIT ~(0UL) +#define MEM_SPACE_LIMIT ~(0UL) + + +#ifdef __KERNEL__ +/* + * Map in an area of physical address space, for accessing + * I/O devices etc. + */ +extern void *__ioremap(unsigned long address, unsigned long size, + unsigned long flags); +extern void *ioremap(unsigned long address, unsigned long size); +#define ioremap_nocache(addr, size) ioremap((addr), (size)) +extern void iounmap(void *addr); + +/* + * Change virtual addresses to physical addresses and vv, for + * addresses in the area where the kernel has the RAM mapped. + */ +extern inline unsigned long virt_to_phys(volatile void * address) +{ +#ifdef __IO_DEBUG + printk("virt_to_phys: 0x%08lx -> 0x%08lx\n", + (unsigned long) address, + __pa((unsigned long)address)); +#endif + return __pa((unsigned long)address); +} + +extern inline void * phys_to_virt(unsigned long address) +{ +#ifdef __IO_DEBUG + printk("phys_to_virt: 0x%08lx -> 0x%08lx\n", address, __va(address)); +#endif + return (void *) __va(address); +} + +/* + * Change "struct page" to physical address. + */ +#define page_to_phys(page) ((page - mem_map) << PAGE_SHIFT) + +#if 0 +#define BIO_VMERGE_BOUNDARY 4096 +#endif + +#endif /* __KERNEL__ */ + +extern inline void iosync(void) +{ + __asm__ __volatile__ ("sync" : : : "memory"); +} + +/* Enforce in-order execution of data I/O. + * No distinction between read/write on PPC; use eieio for all three. + */ +#define iobarrier_rw() eieio() +#define iobarrier_r() eieio() +#define iobarrier_w() eieio() + +/* + * 8, 16 and 32 bit, big and little endian I/O operations, with barrier. + */ +extern inline int in_8(volatile unsigned char *addr) +{ + int ret; + + __asm__ __volatile__("eieio; lbz%U1%X1 %0,%1" : "=r" (ret) : "m" (*addr)); + return ret; +} + +extern inline void out_8(volatile unsigned char *addr, int val) +{ + __asm__ __volatile__("stb%U0%X0 %1,%0" : "=m" (*addr) : "r" (val)); +} + +extern inline int in_le16(volatile unsigned short *addr) +{ + int ret; + + __asm__ __volatile__("eieio; lhbrx %0,0,%1" : "=r" (ret) : + "r" (addr), "m" (*addr)); + return ret; +} + +extern inline int in_be16(volatile unsigned short *addr) +{ + int ret; + + __asm__ __volatile__("eieio; lhz%U1%X1 %0,%1" : "=r" (ret) : "m" (*addr)); + return ret; +} + +extern inline void out_le16(volatile unsigned short *addr, int val) +{ + __asm__ __volatile__("sthbrx %1,0,%2" : "=m" (*addr) : + "r" (val), "r" (addr)); +} + +extern inline void out_be16(volatile unsigned short *addr, int val) +{ + __asm__ __volatile__("sth%U0%X0 %1,%0" : "=m" (*addr) : "r" (val)); +} + +extern inline unsigned in_le32(volatile unsigned *addr) +{ + unsigned ret; + + __asm__ __volatile__("eieio; lwbrx %0,0,%1" : "=r" (ret) : + "r" (addr), "m" (*addr)); + return ret; +} + +extern inline unsigned in_be32(volatile unsigned *addr) +{ + unsigned ret; + + __asm__ __volatile__("eieio; lwz%U1%X1 %0,%1" : "=r" (ret) : "m" (*addr)); + return ret; +} + +extern inline void out_le32(volatile unsigned *addr, int val) +{ + __asm__ __volatile__("stwbrx %1,0,%2" : "=m" (*addr) : + "r" (val), "r" (addr)); +} + +extern inline void out_be32(volatile unsigned *addr, int val) +{ + __asm__ __volatile__("stw%U0%X0 %1,%0" : "=m" (*addr) : "r" (val)); +} + +#ifdef CONFIG_PPC_EEH +#include +#endif + +#ifndef CONFIG_PPC_ISERIES +static inline u8 _inb(unsigned long port) { + if (IS_MAPPED_VADDR(port)) + return readb((void *)port); + else if (_IO_BASE) + return in_8((u8 *)((port)+_IO_BASE)); + else + return 0xff; +} +static inline void _outb(u8 val, unsigned long port) { + if (IS_MAPPED_VADDR(port)) + return writeb(val, (void *)port); + else if (_IO_BASE) + out_8((u8 *)((port)+_IO_BASE), val); +} +static inline u16 _inw(unsigned long port) { + if (IS_MAPPED_VADDR(port)) + return readw((void *)port); + else if (_IO_BASE) + return in_le16((u16 *)((port)+_IO_BASE)); + else + return 0xffff; +} +static inline void _outw(u16 val, unsigned long port) { + if (IS_MAPPED_VADDR(port)) + return writew(val, (void *)port); + else if (_IO_BASE) + out_le16((u16 *)((port)+_IO_BASE), val); +} +static inline u32 _inl(unsigned long port) { + if (IS_MAPPED_VADDR(port)) + return readl((void *)port); + else if (_IO_BASE) + return in_le32((u32 *)((port)+_IO_BASE)); + else + return 0xffffffff; +} +static inline void _outl(u32 val, unsigned long port) { + if (IS_MAPPED_VADDR(port)) + return writel(val, (void *)port); + else if (_IO_BASE) + out_le32((u32 *)((port)+_IO_BASE), val); +} +#endif + +#ifdef __KERNEL__ +static inline int check_signature(unsigned long io_addr, + const unsigned char *signature, int length) +{ + int retval = 0; +#ifndef CONFIG_PPC_ISERIES + do { + if (readb(io_addr) != *signature) + goto out; + io_addr++; + signature++; + length--; + } while (length); + retval = 1; +out: +#endif + return retval; +} + +/* Nothing to do */ + +#define dma_cache_inv(_start,_size) do { } while (0) +#define dma_cache_wback(_start,_size) do { } while (0) +#define dma_cache_wback_inv(_start,_size) do { } while (0) + +#endif /* __KERNEL__ */ + +#endif /* _PPC64_IO_H */ diff -Nru a/include/asm-ppc64/ioctl.h b/include/asm-ppc64/ioctl.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/ioctl.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,64 @@ +#ifndef _PPC64_IOCTL_H +#define _PPC64_IOCTL_H + + +/* + * This was copied from the alpha as it's a bit cleaner there. + * -- Cort + * + * 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. + */ + +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 +#define _IOC_SIZEBITS 13 +#define _IOC_DIRBITS 3 + +#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) +#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) +#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) +#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + +/* + * Direction bits _IOC_NONE could be 0, but OSF/1 gives it a bit. + * And this turns out useful to catch old ioctl numbers in header + * files for us. + */ +#define _IOC_NONE 1U +#define _IOC_READ 2U +#define _IOC_WRITE 4U + +#define _IOC(dir,type,nr,size) \ + (((dir) << _IOC_DIRSHIFT) | \ + ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | \ + ((size) << _IOC_SIZESHIFT)) + +/* used to create numbers */ +#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) +#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) +#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) +#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) + +/* used to decode them.. */ +#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) +#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) +#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) +#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) + +/* various drivers, such as the pcmcia stuff, need these... */ +#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) +#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) +#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) +#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) +#define IOCSIZE_SHIFT (_IOC_SIZESHIFT) + +#endif /* _PPC64_IOCTL_H */ diff -Nru a/include/asm-ppc64/ioctls.h b/include/asm-ppc64/ioctls.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/ioctls.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,115 @@ +#ifndef _ASM_PPC64_IOCTLS_H +#define _ASM_PPC64_IOCTLS_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +#define FIOCLEX _IO('f', 1) +#define FIONCLEX _IO('f', 2) +#define FIOASYNC _IOW('f', 125, int) +#define FIONBIO _IOW('f', 126, int) +#define FIONREAD _IOR('f', 127, int) +#define TIOCINQ FIONREAD +#define FIOQSIZE _IOR('f', 128, loff_t) + +#define TIOCGETP _IOR('t', 8, struct sgttyb) +#define TIOCSETP _IOW('t', 9, struct sgttyb) +#define TIOCSETN _IOW('t', 10, struct sgttyb) /* TIOCSETP wo flush */ + +#define TIOCSETC _IOW('t', 17, struct tchars) +#define TIOCGETC _IOR('t', 18, struct tchars) +#define TCGETS _IOR('t', 19, struct termios) +#define TCSETS _IOW('t', 20, struct termios) +#define TCSETSW _IOW('t', 21, struct termios) +#define TCSETSF _IOW('t', 22, struct termios) + +#define TCGETA _IOR('t', 23, struct termio) +#define TCSETA _IOW('t', 24, struct termio) +#define TCSETAW _IOW('t', 25, struct termio) +#define TCSETAF _IOW('t', 28, struct termio) + +#define TCSBRK _IO('t', 29) +#define TCXONC _IO('t', 30) +#define TCFLSH _IO('t', 31) + +#define TIOCSWINSZ _IOW('t', 103, struct winsize) +#define TIOCGWINSZ _IOR('t', 104, struct winsize) +#define TIOCSTART _IO('t', 110) /* start output, like ^Q */ +#define TIOCSTOP _IO('t', 111) /* stop output, like ^S */ +#define TIOCOUTQ _IOR('t', 115, int) /* output queue size */ + +#define TIOCGLTC _IOR('t', 116, struct ltchars) +#define TIOCSLTC _IOW('t', 117, struct ltchars) +#define TIOCSPGRP _IOW('t', 118, int) +#define TIOCGPGRP _IOR('t', 119, int) + +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D +#define TIOCSCTTY 0x540E + +#define TIOCSTI 0x5412 +#define TIOCMGET 0x5415 +#define TIOCMBIS 0x5416 +#define TIOCMBIC 0x5417 +#define TIOCMSET 0x5418 +# define TIOCM_LE 0x001 +# define TIOCM_DTR 0x002 +# define TIOCM_RTS 0x004 +# define TIOCM_ST 0x008 +# define TIOCM_SR 0x010 +# define TIOCM_CTS 0x020 +# define TIOCM_CAR 0x040 +# define TIOCM_RNG 0x080 +# define TIOCM_DSR 0x100 +# define TIOCM_CD TIOCM_CAR +# define TIOCM_RI TIOCM_RNG + +#define TIOCGSOFTCAR 0x5419 +#define TIOCSSOFTCAR 0x541A +#define TIOCLINUX 0x541C +#define TIOCCONS 0x541D +#define TIOCGSERIAL 0x541E +#define TIOCSSERIAL 0x541F +#define TIOCPKT 0x5420 +# define TIOCPKT_DATA 0 +# define TIOCPKT_FLUSHREAD 1 +# define TIOCPKT_FLUSHWRITE 2 +# define TIOCPKT_STOP 4 +# define TIOCPKT_START 8 +# define TIOCPKT_NOSTOP 16 +# define TIOCPKT_DOSTOP 32 + + +#define TIOCNOTTY 0x5422 +#define TIOCSETD 0x5423 +#define TIOCGETD 0x5424 +#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TIOCSBRK 0x5427 /* BSD compatibility */ +#define TIOCCBRK 0x5428 /* BSD compatibility */ +#define TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ +#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ + +#define TIOCSERCONFIG 0x5453 +#define TIOCSERGWILD 0x5454 +#define TIOCSERSWILD 0x5455 +#define TIOCGLCKTRMIOS 0x5456 +#define TIOCSLCKTRMIOS 0x5457 +#define TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TIOCSERGETLSR 0x5459 /* Get line status register */ + /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ +# define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ +#define TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ +#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ + +#endif /* _ASM_PPC64_IOCTLS_H */ diff -Nru a/include/asm-ppc64/ipc.h b/include/asm-ppc64/ipc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/ipc.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,33 @@ +#ifndef __PPC64_IPC_H__ +#define __PPC64_IPC_H__ + +/* + * These are used to wrap system calls on PowerPC. + * + * See arch/ppc/kernel/syscalls.c for ugly details.. + * + * 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. + */ +struct ipc_kludge { + u32 msgp; + s32 msgtyp; +}; + +#define SEMOP 1 +#define SEMGET 2 +#define SEMCTL 3 +#define MSGSND 11 +#define MSGRCV 12 +#define MSGGET 13 +#define MSGCTL 14 +#define SHMAT 21 +#define SHMDT 22 +#define SHMGET 23 +#define SHMCTL 24 + +#define IPCCALL(version,op) ((version)<<16 | (op)) + +#endif /* __PPC64_IPC_H__ */ diff -Nru a/include/asm-ppc64/ipcbuf.h b/include/asm-ppc64/ipcbuf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/ipcbuf.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,28 @@ +#ifndef __PPC64_IPCBUF_H__ +#define __PPC64_IPCBUF_H__ + +/* + * The ipc64_perm structure for the PPC is identical to kern_ipc_perm + * as we have always had 32-bit UIDs and GIDs in the kernel. + * + * 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. + */ + +struct ipc64_perm +{ + __kernel_key_t key; + __kernel_uid_t uid; + __kernel_gid_t gid; + __kernel_uid_t cuid; + __kernel_gid_t cgid; + __kernel_mode_t mode; + unsigned int seq; + unsigned int __pad1; + unsigned long __unused1; + unsigned long __unused2; +}; + +#endif /* __PPC64_IPCBUF_H__ */ diff -Nru a/include/asm-ppc64/irq.h b/include/asm-ppc64/irq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/irq.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,55 @@ +#ifdef __KERNEL__ +#ifndef _ASM_IRQ_H +#define _ASM_IRQ_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +extern void disable_irq(unsigned int); +extern void disable_irq_nosync(unsigned int); +extern void enable_irq(unsigned int); + +/* + * this is the maximum number of virtual irqs we will use. + */ +#define NR_IRQS 512 + +#define NUM_8259_INTERRUPTS 16 + +/* Interrupt numbers are virtual in case they are sparsely + * distributed by the hardware. + */ +#define NR_HW_IRQS 8192 +extern unsigned short real_irq_to_virt_map[NR_HW_IRQS]; +extern unsigned short virt_irq_to_real_map[NR_IRQS]; +/* Create a mapping for a real_irq if it doesn't already exist. + * Return the virtual irq as a convenience. + */ +unsigned long virt_irq_create_mapping(unsigned long real_irq); + +/* These funcs map irqs between real and virtual */ +static inline unsigned long real_irq_to_virt(unsigned long real_irq) { + return real_irq_to_virt_map[real_irq]; +} +static inline unsigned long virt_irq_to_real(unsigned long virt_irq) { + return virt_irq_to_real_map[virt_irq]; +} + +/* + * This gets called from serial.c, which is now used on + * powermacs as well as prep/chrp boxes. + * Prep and chrp both have cascaded 8259 PICs. + */ +static __inline__ int irq_cannonicalize(int irq) +{ + return irq; +} + +#endif /* _ASM_IRQ_H */ +#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc64/keyboard.h b/include/asm-ppc64/keyboard.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/keyboard.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,115 @@ +/* + * linux/include/asm-ppc/keyboard.h + * + * Created 3 Nov 1996 by Geert Uytterhoeven + * Modified for Power Macintosh by Paul Mackerras + * + * 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 file contains the ppc architecture specific keyboard definitions - + * like the intel pc for prep systems, different for power macs. + */ + +#ifndef __ASMPPC64_KEYBOARD_H +#define __ASMPPC64_KEYBOARD_H + +#ifdef __KERNEL__ + +#include +#include + +#include +#include +#include +#include + +#define KEYBOARD_IRQ 1 +#define DISABLE_KBD_DURING_INTERRUPTS 0 +#define INIT_KBD + +static inline int kbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if ( ppc_md.kbd_setkeycode ) + return ppc_md.kbd_setkeycode(scancode, keycode); + else + return 0; +} + +static inline int kbd_getkeycode(unsigned int scancode) +{ + if ( ppc_md.kbd_getkeycode ) + return ppc_md.kbd_getkeycode(scancode); + else + return 0; +} + +static inline int kbd_translate(unsigned char keycode, unsigned char *keycodep, + char raw_mode) +{ + if ( ppc_md.kbd_translate ) + return ppc_md.kbd_translate(keycode, keycodep, raw_mode); + else + return 0; +} + +static inline int kbd_unexpected_up(unsigned char keycode) +{ + if ( ppc_md.kbd_unexpected_up ) + return ppc_md.kbd_unexpected_up(keycode); + else + return 0; +} + +static inline void kbd_leds(unsigned char leds) +{ + if ( ppc_md.kbd_leds ) + ppc_md.kbd_leds(leds); +} + +static inline void kbd_init_hw(void) +{ + if ( ppc_md.kbd_init_hw ) + ppc_md.kbd_init_hw(); +} + +#define kbd_sysrq_xlate (ppc_md.ppc_kbd_sysrq_xlate) + +extern unsigned long SYSRQ_KEY; +#define E1_PAUSE 119 /* PAUSE key */ + +/* resource allocation */ +#define kbd_request_region() +#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \ + "keyboard", NULL) + +/* How to access the keyboard macros on this platform. */ +#define kbd_read_input() inb(KBD_DATA_REG) +static inline int kbd_read_status(void) { + int ret = inb(0x64); + return (ret == 0xff) ? 0 : ret; /* translate float to bad status. */ +} +#define kbd_write_output(val) outb(val, KBD_DATA_REG) +#define kbd_write_command(val) outb(val, KBD_CNTL_REG) + +/* Some stoneage hardware needs delays after some operations. */ +#define kbd_pause() do { } while(0) + +/* + * Machine specific bits for the PS/2 driver + */ + +#define AUX_IRQ 12 + +#define aux_request_irq(hand, dev_id) \ + request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id) + +#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id) + +#endif /* __KERNEL__ */ + +#endif /* __ASMPPC64_KEYBOARD_H */ diff -Nru a/include/asm-ppc64/kgdb.h b/include/asm-ppc64/kgdb.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/kgdb.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,53 @@ +/* + * kgdb.h: Defines and declarations for serial line source level + * remote debugging of the Linux kernel using gdb. + * + * PPC Mods (C) 1998 Michael Tesch (tesch@cs.wisc.edu) + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _PPC64_KGDB_H +#define _PPC64_KGDB_H + +#ifndef __ASSEMBLY__ +/* To initialize the serial, first thing called */ +extern void zs_kgdb_hook(int tty_num); +/* To init the kgdb engine. (called by serial hook)*/ +extern void set_debug_traps(void); + +/* To enter the debugger explicitly. */ +extern void breakpoint(void); + +/* For taking exceptions + * these are defined in traps.c + */ +extern void (*debugger)(struct pt_regs *regs); +extern int (*debugger_bpt)(struct pt_regs *regs); +extern int (*debugger_sstep)(struct pt_regs *regs); +extern int (*debugger_iabr_match)(struct pt_regs *regs); +extern int (*debugger_dabr_match)(struct pt_regs *regs); +extern void (*debugger_fault_handler)(struct pt_regs *regs); + +/* What we bring to the party */ +int kgdb_bpt(struct pt_regs *regs); +int kgdb_sstep(struct pt_regs *regs); +void kgdb(struct pt_regs *regs); +int kgdb_iabr_match(struct pt_regs *regs); +int kgdb_dabr_match(struct pt_regs *regs); +static void kgdb_fault_handler(struct pt_regs *regs); +static void handle_exception (struct pt_regs *regs); + +/* + * external low-level support routines (ie macserial.c) + */ +extern void kgdb_interruptible(int); /* control interrupts from serial */ +extern void putDebugChar(char); /* write a single character */ +extern char getDebugChar(void); /* read and return a single char */ + +#endif /* !(__ASSEMBLY__) */ +#endif /* !(_PPC64_KGDB_H) */ diff -Nru a/include/asm-ppc64/linux_logo.h b/include/asm-ppc64/linux_logo.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/linux_logo.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,46 @@ +/* + * include/asm-ppc64/linux_logo.h: A linux logo to be displayed on boot + * (pinched from the sparc port). + * + * Copyright (C) 1996 Larry Ewing (lewing@isc.tamu.edu) + * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * You can put anything here, but: + * LINUX_LOGO_COLORS has to be less than 224 + * values have to start from 0x20 + * (i.e. linux_logo_{red,green,blue}[0] is color 0x20) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +#define linux_logo_banner "Linux/PPC-64 version " UTS_RELEASE + +#define LINUX_LOGO_HEIGHT 80 +#define LINUX_LOGO_WIDTH 80 +#define LINUX_LOGO_COLORS 214 + +#ifdef INCLUDE_LINUX_LOGO_DATA + +#define INCLUDE_LINUX_LOGOBW +#define INCLUDE_LINUX_LOGO16 +#include + +#else + +/* prototypes only */ +extern unsigned char linux_logo_red[]; +extern unsigned char linux_logo_green[]; +extern unsigned char linux_logo_blue[]; +extern unsigned char linux_logo[]; +extern unsigned char linux_logo_bw[]; +extern unsigned char linux_logo16_red[]; +extern unsigned char linux_logo16_green[]; +extern unsigned char linux_logo16_blue[]; +extern unsigned char linux_logo16[]; + +#endif diff -Nru a/include/asm-ppc64/lmb.h b/include/asm-ppc64/lmb.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/lmb.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,106 @@ +#ifndef _PPC64_LMB_H +#define _PPC64_LMB_H + +/* + * Definitions for talking to the Open Firmware PROM on + * Power Macintosh computers. + * + * Copyright (C) 2001 Peter Bergner, IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +extern unsigned long reloc_offset(void); + +#define MAX_LMB_REGIONS 32 + +union lmb_reg_property { + struct reg_property32 addr32[MAX_LMB_REGIONS]; + struct reg_property64 addr64[MAX_LMB_REGIONS]; +}; + +#define LMB_MEMORY_AREA 1 +#define LMB_IO_AREA 2 + +struct lmb_property { + unsigned long base; + unsigned long physbase; + unsigned long size; + unsigned long type; +}; + +struct lmb_region { + unsigned long cnt; + unsigned long size; + unsigned long lcd_size; /* Least Common Denominator */ + struct lmb_property region[MAX_LMB_REGIONS+1]; +}; + +struct lmb { + unsigned long debug; + struct lmb_region memory; + struct lmb_region reserved; +}; + +extern struct lmb lmb; + +extern void lmb_init(void); +extern void lmb_analyze(void); +extern long lmb_add(unsigned long, unsigned long); +extern long lmb_add_io(unsigned long base, unsigned long size); +extern long lmb_reserve(unsigned long, unsigned long); +extern unsigned long lmb_alloc(unsigned long, unsigned long); +extern unsigned long lmb_phys_mem_size(void); +extern unsigned long lmb_end_of_DRAM(void); +extern unsigned long lmb_abs_to_phys(unsigned long); +extern void lmb_dump(char *); + +static inline unsigned long +lmb_addrs_overlap(unsigned long base1, unsigned long size1, + unsigned long base2, unsigned long size2) +{ + return ((base1 < (base2+size2)) && (base2 < (base1+size1))); +} + +static inline long +lmb_regions_overlap(struct lmb_region *rgn, unsigned long r1, unsigned long r2) +{ + unsigned long base1 = rgn->region[r1].base; + unsigned long size1 = rgn->region[r1].size; + unsigned long base2 = rgn->region[r2].base; + unsigned long size2 = rgn->region[r2].size; + + return lmb_addrs_overlap(base1,size1,base2,size2); +} + +static inline long +lmb_addrs_adjacent(unsigned long base1, unsigned long size1, + unsigned long base2, unsigned long size2) +{ + if ( base2 == base1 + size1 ) { + return 1; + } else if ( base1 == base2 + size2 ) { + return -1; + } + return 0; +} + +static inline long +lmb_regions_adjacent(struct lmb_region *rgn, unsigned long r1, unsigned long r2) +{ + unsigned long base1 = rgn->region[r1].base; + unsigned long size1 = rgn->region[r1].size; + unsigned long type1 = rgn->region[r1].type; + unsigned long base2 = rgn->region[r2].base; + unsigned long size2 = rgn->region[r2].size; + unsigned long type2 = rgn->region[r2].type; + + return (type1 == type2) && lmb_addrs_adjacent(base1,size1,base2,size2); +} + +#endif /* _PPC64_LMB_H */ diff -Nru a/include/asm-ppc64/machdep.h b/include/asm-ppc64/machdep.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/machdep.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,164 @@ +#ifdef __KERNEL__ +#ifndef _PPC_MACHDEP_H +#define _PPC_MACHDEP_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +struct pt_regs; +struct pci_bus; +struct pci_dev; +struct kbd_repeat; +struct device_node; +struct TceTable; +struct rtc_time; + +struct machdep_calls { + /* High use functions in the first cachelines, low use functions + * follow. DRENG collect profile data. + */ + void (*hpte_invalidate)(unsigned long slot); + + void (*hpte_updatepp)(long slot, + unsigned long newpp, + unsigned long va); + void (*hpte_updateboltedpp)(unsigned long newpp, + unsigned long ea); + unsigned long (*hpte_getword0)(unsigned long slot); + + long (*hpte_find)( unsigned long vpn ); + + long (*hpte_selectslot)(unsigned long vpn); + + void (*hpte_create_valid)(unsigned long slot, + unsigned long vpn, + unsigned long prpn, + unsigned hash, + void * ptep, + unsigned hpteflags, + unsigned bolted); + void (*tce_build)(struct TceTable * tbl, + long tcenum, + unsigned long uaddr, + int direction); + void (*tce_free)(struct TceTable *tbl, + dma_addr_t dma_addr, + unsigned order, + unsigned numPages); + + void (*smp_message_pass)(int target, + int msg, + unsigned long data, + int wait); + int (*smp_probe)(void); + void (*smp_kick_cpu)(int nr); + void (*smp_setup_cpu)(int nr); + + void (*setup_arch)(void); + /* Optional, may be NULL. */ + void (*setup_residual)(struct seq_file *m, int cpu_id); + /* Optional, may be NULL. */ + void (*get_cpuinfo)(struct seq_file *m); + /* Optional, may be NULL. */ + unsigned int (*irq_cannonicalize)(unsigned int irq); + void (*init_IRQ)(void); + void (*init_ras_IRQ)(void); + int (*get_irq)(struct pt_regs *); + void (*post_irq)( struct pt_regs *, int ); + + /* A general init function, called by ppc_init in init/main.c. + May be NULL. */ + void (*init)(void); + + void (*restart)(char *cmd); + void (*power_off)(void); + void (*halt)(void); + + long (*time_init)(void); /* Optional, may be NULL */ + int (*set_rtc_time)(struct rtc_time *); + void (*get_rtc_time)(struct rtc_time *); + void (*get_boot_time)(struct rtc_time *); + void (*calibrate_decr)(void); + + void (*progress)(char *, unsigned short); + + unsigned char (*nvram_read_val)(int addr); + void (*nvram_write_val)(int addr, unsigned char val); + +/* Tons of keyboard stuff. */ + int (*kbd_setkeycode)(unsigned int scancode, + unsigned int keycode); + int (*kbd_getkeycode)(unsigned int scancode); + int (*kbd_translate)(unsigned char scancode, + unsigned char *keycode, + char raw_mode); + char (*kbd_unexpected_up)(unsigned char keycode); + void (*kbd_leds)(unsigned char leds); + void (*kbd_init_hw)(void); +#ifdef CONFIG_MAGIC_SYSRQ + unsigned char *ppc_kbd_sysrq_xlate; +#endif + + /* Debug interface. Low level I/O to some terminal device */ + void (*udbg_putc)(unsigned char c); + unsigned char (*udbg_getc)(void); + int (*udbg_getc_poll)(void); + + /* PCI interfaces */ + int (*pcibios_read_config_byte)(struct device_node *dn, int offset, u8 *val); + int (*pcibios_read_config_word)(struct device_node *dn, int offset, u16 *val); + int (*pcibios_read_config_dword)(struct device_node *dn, int offset, u32 *val); + int (*pcibios_write_config_byte)(struct device_node *dn, int offset, u8 val); + int (*pcibios_write_config_word)(struct device_node *dn, int offset, u16 val); + int (*pcibios_write_config_dword)(struct device_node *dn, int offset, u32 val); + + /* Called after scanning the bus, before allocating + * resources + */ + void (*pcibios_fixup)(void); + + /* Called for each PCI bus in the system + * when it's probed + */ + void (*pcibios_fixup_bus)(struct pci_bus *); + + /* Called when pci_enable_device() is called (initial=0) or + * when a device with no assigned resource is found (initial=1). + * Returns 0 to allow assignement/enabling of the device + */ + int (*pcibios_enable_device_hook)(struct pci_dev *, int initial); + + void* (*pci_dev_io_base)(unsigned char bus, unsigned char devfn, int physical); + void* (*pci_dev_mem_base)(unsigned char bus, unsigned char devfn); + int (*pci_dev_root_bridge)(unsigned char bus, unsigned char devfn); + + /* this is for modules, since _machine can be a define -- Cort */ + int ppc_machine; +}; + +extern struct machdep_calls ppc_md; +extern char cmd_line[512]; + +extern void setup_pci_ptrs(void); + +/* + * Power macintoshes have either a CUDA or a PMU controlling + * system reset, power, NVRAM, RTC. + */ +typedef enum sys_ctrler_kind { + SYS_CTRLER_UNKNOWN = 0, + SYS_CTRLER_CUDA = 1, + SYS_CTRLER_PMU = 2, +} sys_ctrler_t; + +extern sys_ctrler_t sys_ctrler; + +#endif /* _PPC_MACHDEP_H */ +#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc64/mc146818rtc.h b/include/asm-ppc64/mc146818rtc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/mc146818rtc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,32 @@ +/* + * Machine dependent access functions for RTC registers. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef __ASM_PPC64_MC146818RTC_H +#define __ASM_PPC64_MC146818RTC_H + +#include + +#ifndef RTC_PORT +#define RTC_PORT(x) (0x70 + (x)) +#define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */ +#endif + +/* + * The yet supported machines all access the RTC index register via + * an ISA port access but the way to access the date register differs ... + */ +#define CMOS_READ(addr) ({ \ +outb_p((addr),RTC_PORT(0)); \ +inb_p(RTC_PORT(1)); \ +}) +#define CMOS_WRITE(val, addr) ({ \ +outb_p((addr),RTC_PORT(0)); \ +outb_p((val),RTC_PORT(1)); \ +}) + +#endif /* __ASM_PPC64_MC146818RTC_H */ diff -Nru a/include/asm-ppc64/md.h b/include/asm-ppc64/md.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/md.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,17 @@ +/* + * md.h: High speed xor_block operation for RAID4/5 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __ASM_MD_H +#define __ASM_MD_H + +/* #define HAVE_ARCH_XORBLOCK */ + +#define MD_XORBLOCK_ALIGNMENT sizeof(long) + +#endif /* __ASM_MD_H */ diff -Nru a/include/asm-ppc64/memory.h b/include/asm-ppc64/memory.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/memory.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,44 @@ +#ifndef _ASM_PPC64_MEMORY_H_ +#define _ASM_PPC64_MEMORY_H_ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +/* + * Arguably the bitops and *xchg operations don't imply any memory barrier + * or SMP ordering, but in fact a lot of drivers expect them to imply + * both, since they do on x86 cpus. + */ +#ifdef CONFIG_SMP +#define EIEIO_ON_SMP "eieio\n" +#define ISYNC_ON_SMP "\n\tisync" +#else +#define EIEIO_ON_SMP +#define ISYNC_ON_SMP +#endif + +static inline void eieio(void) +{ + __asm__ __volatile__ ("eieio" : : : "memory"); +} + +static inline void isync(void) +{ + __asm__ __volatile__ ("isync" : : : "memory"); +} + +#ifdef CONFIG_SMP +#define eieio_on_smp() eieio() +#define isync_on_smp() isync() +#else +#define eieio_on_smp() __asm__ __volatile__("": : :"memory") +#define isync_on_smp() __asm__ __volatile__("": : :"memory") +#endif + +#endif diff -Nru a/include/asm-ppc64/mman.h b/include/asm-ppc64/mman.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/mman.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,45 @@ +#ifndef __PPC64_MMAN_H__ +#define __PPC64_MMAN_H__ + +/* + * 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. + */ + +#define PROT_READ 0x1 /* page can be read */ +#define PROT_WRITE 0x2 /* page can be written */ +#define PROT_EXEC 0x4 /* page can be executed */ +#define PROT_NONE 0x0 /* page can not be accessed */ + +#define MAP_SHARED 0x01 /* Share changes */ +#define MAP_PRIVATE 0x02 /* Changes are private */ +#define MAP_TYPE 0x0f /* Mask for type of mapping */ +#define MAP_FIXED 0x10 /* Interpret addr exactly */ +#define MAP_ANONYMOUS 0x20 /* don't use a file */ +#define MAP_RENAME MAP_ANONYMOUS /* In SunOS terminology */ +#define MAP_NORESERVE 0x40 /* don't reserve swap pages */ + +#define MAP_GROWSDOWN 0x0100 /* stack-like segment */ +#define MAP_DENYWRITE 0x0800 /* ETXTBSY */ +#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ + +#define MS_ASYNC 1 /* sync memory asynchronously */ +#define MS_INVALIDATE 2 /* invalidate the caches */ +#define MS_SYNC 4 /* synchronous memory sync */ + +#define MCL_CURRENT 0x2000 /* lock all currently mapped pages */ +#define MCL_FUTURE 0x4000 /* lock all additions to address space */ + +#define MADV_NORMAL 0x0 /* default page-in behavior */ +#define MADV_RANDOM 0x1 /* page-in minimum required */ +#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */ +#define MADV_WILLNEED 0x3 /* pre-fault pages */ +#define MADV_DONTNEED 0x4 /* discard these pages */ + +/* compatibility flags */ +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FILE 0 + +#endif /* __PPC64_MMAN_H__ */ diff -Nru a/include/asm-ppc64/mmu.h b/include/asm-ppc64/mmu.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/mmu.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,384 @@ +/* + * PowerPC memory management structures + * + * Dave Engebretsen & Mike Corrigan <{engebret|mikejc}@us.ibm.com> + * PPC64 rework. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _PPC64_MMU_H_ +#define _PPC64_MMU_H_ + +#ifndef __ASSEMBLY__ + +/* Default "unsigned long" context */ +typedef unsigned long mm_context_t; + +/* + * Define the size of the cache used for segment table entries. The first + * entry is used as a cache pointer, therefore the actual number of entries + * stored is one less than defined here. Do not change this value without + * considering the impact it will have on the layout of the paca in Paca.h. + */ +#define STAB_CACHE_SIZE 16 + +/* + * Hardware Segment Lookaside Buffer Entry + * This structure has been padded out to two 64b doublewords (actual SLBE's are + * 94 bits). This padding facilites use by the segment management + * instructions. + */ +typedef struct { + unsigned long esid: 36; /* Effective segment ID */ + unsigned long resv0:20; /* Reserved */ + unsigned long v: 1; /* Entry valid (v=1) or invalid */ + unsigned long resv1: 1; /* Reserved */ + unsigned long ks: 1; /* Supervisor (privileged) state storage key */ + unsigned long kp: 1; /* Problem state storage key */ + unsigned long n: 1; /* No-execute if n=1 */ + unsigned long resv2: 3; /* padding to a 64b boundary */ +} ste_dword0; + +typedef struct { + unsigned long vsid: 52; /* Virtual segment ID */ + unsigned long resv0:12; /* Padding to a 64b boundary */ +} ste_dword1; + +typedef struct _STE { + union { + unsigned long dword0; + ste_dword0 dw0; + } dw0; + + union { + unsigned long dword1; + ste_dword1 dw1; + } dw1; +} STE; + +typedef struct { + unsigned long esid: 36; /* Effective segment ID */ + unsigned long v: 1; /* Entry valid (v=1) or invalid */ + unsigned long null1:15; /* padding to a 64b boundary */ + unsigned long index:12; /* Index to select SLB entry. Used by slbmte */ +} slb_dword0; + +typedef struct { + unsigned long vsid: 52; /* Virtual segment ID */ + unsigned long ks: 1; /* Supervisor (privileged) state storage key */ + unsigned long kp: 1; /* Problem state storage key */ + unsigned long n: 1; /* No-execute if n=1 */ + unsigned long l: 1; /* Virt pages are large (l=1) or 4KB (l=0) */ + unsigned long c: 1; /* Class */ + unsigned long resv0: 7; /* Padding to a 64b boundary */ +} slb_dword1; + +typedef struct _SLBE { + union { + unsigned long dword0; + slb_dword0 dw0; + } dw0; + + union { + unsigned long dword1; + slb_dword1 dw1; + } dw1; +} SLBE; + +/* + * This structure is used in Paca.h where the layout depends on the + * size being 24B. + */ +typedef struct { + unsigned long real; + unsigned long virt; + unsigned long next_round_robin; +} STAB; + +/* Hardware Page Table Entry */ + +#define HPTES_PER_GROUP 8 + +typedef struct { + unsigned long avpn:57; /* vsid | api == avpn */ + unsigned long : 2; /* Software use */ + unsigned long bolted: 1; /* HPTE is "bolted" */ + unsigned long : 1; /* Software use */ + unsigned long : 1; /* Reserved */ + unsigned long h: 1; /* Hash function identifier */ + unsigned long v: 1; /* Valid (v=1) or invalid (v=0) */ +} Hpte_dword0; + +typedef struct { + unsigned long : 6; /* unused - padding */ + unsigned long ac: 1; /* Address compare */ + unsigned long r: 1; /* Referenced */ + unsigned long c: 1; /* Changed */ + unsigned long w: 1; /* Write-thru cache mode */ + unsigned long i: 1; /* Cache inhibited */ + unsigned long m: 1; /* Memory coherence required */ + unsigned long g: 1; /* Guarded */ + unsigned long n: 1; /* No-execute */ + unsigned long pp: 2; /* Page protection bits 1:2 */ +} Hpte_flags; + +typedef struct { + unsigned long pp0: 1; /* Page protection bit 0 */ + unsigned long : 1; /* Reserved */ + unsigned long rpn: 50; /* Real page number */ + unsigned long : 2; /* Reserved */ + unsigned long ac: 1; /* Address compare */ + unsigned long r: 1; /* Referenced */ + unsigned long c: 1; /* Changed */ + unsigned long w: 1; /* Write-thru cache mode */ + unsigned long i: 1; /* Cache inhibited */ + unsigned long m: 1; /* Memory coherence required */ + unsigned long g: 1; /* Guarded */ + unsigned long n: 1; /* No-execute */ + unsigned long pp: 2; /* Page protection bits 1:2 */ +} Hpte_dword1; + +typedef struct { + char padding[6]; /* padding */ + unsigned long : 6; /* padding */ + unsigned long flags: 10; /* HPTE flags */ +} Hpte_dword1_flags; + +typedef struct _HPTE { + union { + unsigned long dword0; + Hpte_dword0 dw0; + } dw0; + + union { + unsigned long dword1; + struct { + unsigned long pp0: 1; /* Page protection bit 0 */ + unsigned long ts: 1; /* Tag set bit */ + unsigned long rpn: 50; /* Real page number */ + unsigned long : 2; /* Unused */ + unsigned long ac: 1; /* Address compare bit */ + unsigned long r: 1; /* Referenced */ + unsigned long c: 1; /* Changed */ + unsigned long w: 1; /* Write-thru cache mode */ + unsigned long i: 1; /* Cache inhibited */ + unsigned long m: 1; /* Memory coherence */ + unsigned long g: 1; /* Guarded */ + unsigned long n: 1; /* No-execute page if N=1 */ + unsigned long pp: 2; /* Page protection bit 1:2 */ + } dw1; + } dw1; +} HPTE; + +/* Values for PP (assumes Ks=0, Kp=1) */ +/* pp0 will always be 0 for linux */ +#define PP_RWXX 0 /* Supervisor read/write, User none */ +#define PP_RWRX 1 /* Supervisor read/write, User read */ +#define PP_RWRW 2 /* Supervisor read/write, User read/write */ +#define PP_RXRX 3 /* Supervisor read, User read */ + + +typedef struct { + HPTE * htab; + unsigned long htab_num_ptegs; + unsigned long htab_hash_mask; + unsigned long next_round_robin; + unsigned long last_kernel_address; +} HTAB; + +extern HTAB htab_data; + +void invalidate_hpte( unsigned long slot ); +long select_hpte_slot( unsigned long vpn ); +void create_valid_hpte( unsigned long slot, unsigned long vpn, + unsigned long prpn, unsigned hash, + void * ptep, unsigned hpteflags, + unsigned bolted ); + +#define PD_SHIFT (10+12) /* Page directory */ +#define PD_MASK 0x02FF +#define PT_SHIFT (12) /* Page Table */ +#define PT_MASK 0x02FF + +static inline unsigned long hpt_hash(unsigned long vpn, int large) +{ + unsigned long vsid; + unsigned long page; + + if (large) { + vsid = vpn >> 4; + page = vpn & 0xf; + } else { + vsid = vpn >> 16; + page = vpn & 0xffff; + } + + return (vsid & 0x7fffffffff) ^ page; +} + +#define PG_SHIFT (12) /* Page Entry */ + +extern __inline__ void _tlbie( unsigned long va ) +{ + __asm__ __volatile__ ( " \n\ + clrldi %0,%0,16 \n\ + ptesync \n\ + tlbie %0 \n\ + eieio \n\ + tlbsync \n\ + ptesync" + : : "r" (va) : "memory" ); +} + +#endif /* __ASSEMBLY__ */ + +/* Block size masks */ +#define BL_128K 0x000 +#define BL_256K 0x001 +#define BL_512K 0x003 +#define BL_1M 0x007 +#define BL_2M 0x00F +#define BL_4M 0x01F +#define BL_8M 0x03F +#define BL_16M 0x07F +#define BL_32M 0x0FF +#define BL_64M 0x1FF +#define BL_128M 0x3FF +#define BL_256M 0x7FF + +/* Used to set up SDR1 register */ +#define HASH_TABLE_SIZE_64K 0x00010000 +#define HASH_TABLE_SIZE_128K 0x00020000 +#define HASH_TABLE_SIZE_256K 0x00040000 +#define HASH_TABLE_SIZE_512K 0x00080000 +#define HASH_TABLE_SIZE_1M 0x00100000 +#define HASH_TABLE_SIZE_2M 0x00200000 +#define HASH_TABLE_SIZE_4M 0x00400000 +#define HASH_TABLE_MASK_64K 0x000 +#define HASH_TABLE_MASK_128K 0x001 +#define HASH_TABLE_MASK_256K 0x003 +#define HASH_TABLE_MASK_512K 0x007 +#define HASH_TABLE_MASK_1M 0x00F +#define HASH_TABLE_MASK_2M 0x01F +#define HASH_TABLE_MASK_4M 0x03F + +/* These are the Ks and Kp from the PowerPC books. For proper operation, + * Ks = 0, Kp = 1. + */ +#define MI_AP 786 +#define MI_Ks 0x80000000 /* Should not be set */ +#define MI_Kp 0x40000000 /* Should always be set */ + +/* The effective page number register. When read, contains the information + * about the last instruction TLB miss. When MI_RPN is written, bits in + * this register are used to create the TLB entry. + */ +#define MI_EPN 787 +#define MI_EPNMASK 0xfffff000 /* Effective page number for entry */ +#define MI_EVALID 0x00000200 /* Entry is valid */ +#define MI_ASIDMASK 0x0000000f /* ASID match value */ + /* Reset value is undefined */ + +/* A "level 1" or "segment" or whatever you want to call it register. + * For the instruction TLB, it contains bits that get loaded into the + * TLB entry when the MI_RPN is written. + */ +#define MI_TWC 789 +#define MI_APG 0x000001e0 /* Access protection group (0) */ +#define MI_GUARDED 0x00000010 /* Guarded storage */ +#define MI_PSMASK 0x0000000c /* Mask of page size bits */ +#define MI_PS8MEG 0x0000000c /* 8M page size */ +#define MI_PS512K 0x00000004 /* 512K page size */ +#define MI_PS4K_16K 0x00000000 /* 4K or 16K page size */ +#define MI_SVALID 0x00000001 /* Segment entry is valid */ + /* Reset value is undefined */ + +/* Real page number. Defined by the pte. Writing this register + * causes a TLB entry to be created for the instruction TLB, using + * additional information from the MI_EPN, and MI_TWC registers. + */ +#define MI_RPN 790 + +/* Define an RPN value for mapping kernel memory to large virtual + * pages for boot initialization. This has real page number of 0, + * large page size, shared page, cache enabled, and valid. + * Also mark all subpages valid and write access. + */ +#define MI_BOOTINIT 0x000001fd + +#define MD_CTR 792 /* Data TLB control register */ +#define MD_GPM 0x80000000 /* Set domain manager mode */ +#define MD_PPM 0x40000000 /* Set subpage protection */ +#define MD_CIDEF 0x20000000 /* Set cache inhibit when MMU dis */ +#define MD_WTDEF 0x10000000 /* Set writethrough when MMU dis */ +#define MD_RSV4I 0x08000000 /* Reserve 4 TLB entries */ +#define MD_TWAM 0x04000000 /* Use 4K page hardware assist */ +#define MD_PPCS 0x02000000 /* Use MI_RPN prob/priv state */ +#define MD_IDXMASK 0x00001f00 /* TLB index to be loaded */ +#define MD_RESETVAL 0x04000000 /* Value of register at reset */ + +#define M_CASID 793 /* Address space ID (context) to match */ +#define MC_ASIDMASK 0x0000000f /* Bits used for ASID value */ + + +/* These are the Ks and Kp from the PowerPC books. For proper operation, + * Ks = 0, Kp = 1. + */ +#define MD_AP 794 +#define MD_Ks 0x80000000 /* Should not be set */ +#define MD_Kp 0x40000000 /* Should always be set */ + +/* The effective page number register. When read, contains the information + * about the last instruction TLB miss. When MD_RPN is written, bits in + * this register are used to create the TLB entry. + */ +#define MD_EPN 795 +#define MD_EPNMASK 0xfffff000 /* Effective page number for entry */ +#define MD_EVALID 0x00000200 /* Entry is valid */ +#define MD_ASIDMASK 0x0000000f /* ASID match value */ + /* Reset value is undefined */ + +/* The pointer to the base address of the first level page table. + * During a software tablewalk, reading this register provides the address + * of the entry associated with MD_EPN. + */ +#define M_TWB 796 +#define M_L1TB 0xfffff000 /* Level 1 table base address */ +#define M_L1INDX 0x00000ffc /* Level 1 index, when read */ + /* Reset value is undefined */ + +/* A "level 1" or "segment" or whatever you want to call it register. + * For the data TLB, it contains bits that get loaded into the TLB entry + * when the MD_RPN is written. It is also provides the hardware assist + * for finding the PTE address during software tablewalk. + */ +#define MD_TWC 797 +#define MD_L2TB 0xfffff000 /* Level 2 table base address */ +#define MD_L2INDX 0xfffffe00 /* Level 2 index (*pte), when read */ +#define MD_APG 0x000001e0 /* Access protection group (0) */ +#define MD_GUARDED 0x00000010 /* Guarded storage */ +#define MD_PSMASK 0x0000000c /* Mask of page size bits */ +#define MD_PS8MEG 0x0000000c /* 8M page size */ +#define MD_PS512K 0x00000004 /* 512K page size */ +#define MD_PS4K_16K 0x00000000 /* 4K or 16K page size */ +#define MD_WT 0x00000002 /* Use writethrough page attribute */ +#define MD_SVALID 0x00000001 /* Segment entry is valid */ + /* Reset value is undefined */ + + +/* Real page number. Defined by the pte. Writing this register + * causes a TLB entry to be created for the data TLB, using + * additional information from the MD_EPN, and MD_TWC registers. + */ +#define MD_RPN 798 + +/* This is a temporary storage register that could be used to save + * a processor working register during a tablewalk. + */ +#define M_TW 799 + +#endif /* _PPC64_MMU_H_ */ diff -Nru a/include/asm-ppc64/mmu_context.h b/include/asm-ppc64/mmu_context.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/mmu_context.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,208 @@ +#ifndef __PPC64_MMU_CONTEXT_H +#define __PPC64_MMU_CONTEXT_H + +#include +#include +#include +#include +#include + +/* + * Copyright (C) 2001 PPC 64 Team, IBM Corp + * + * 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. + */ + +/* + * Every architecture must define this function. It's the fastest + * way of searching a 140-bit bitmap where the first 100 bits are + * unlikely to be set. It's guaranteed that at least one of the 140 + * bits is cleared. + */ +static inline int sched_find_first_bit(unsigned long *b) +{ + if (unlikely(b[0])) + return __ffs(b[0]); + if (unlikely(b[1])) + return __ffs(b[1]) + 64; + return __ffs(b[2]) + 128; +} + +#define NO_CONTEXT 0 +#define FIRST_USER_CONTEXT 0x10 /* First 16 reserved for kernel */ +#define LAST_USER_CONTEXT 0x8000 /* Same as PID_MAX for now... */ +#define NUM_USER_CONTEXT (LAST_USER_CONTEXT-FIRST_USER_CONTEXT) + +/* Choose whether we want to implement our context + * number allocator as a LIFO or FIFO queue. + */ +#if 1 +#define MMU_CONTEXT_LIFO +#else +#define MMU_CONTEXT_FIFO +#endif + +struct mmu_context_queue_t { + spinlock_t lock; + long head; + long tail; + long size; + mm_context_t elements[LAST_USER_CONTEXT]; +}; + +extern struct mmu_context_queue_t mmu_context_queue; + +static inline void +enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk, unsigned cpu) +{ +} + +extern void flush_stab(void); + +/* + * The context number queue has underflowed. + * Meaning: we tried to push a context number that was freed + * back onto the context queue and the queue was already full. + */ +static inline void +mmu_context_underflow(void) +{ + printk(KERN_DEBUG "mmu_context_underflow\n"); + panic("mmu_context_underflow"); +} + + +/* + * Set up the context for a new address space. + */ +static inline int +init_new_context(struct task_struct *tsk, struct mm_struct *mm) +{ + long head, size; + + spin_lock( &mmu_context_queue.lock ); + + if ( (size = mmu_context_queue.size) <= 0 ) { + spin_unlock( &mmu_context_queue.lock ); + return -ENOMEM; + } + + head = mmu_context_queue.head; + mm->context = mmu_context_queue.elements[head]; + + head = (head < LAST_USER_CONTEXT-1) ? head+1 : 0; + mmu_context_queue.head = head; + mmu_context_queue.size = size-1; + + spin_unlock( &mmu_context_queue.lock ); + + return 0; +} + +/* + * We're finished using the context for an address space. + */ +static inline void +destroy_context(struct mm_struct *mm) +{ + long index, size = mmu_context_queue.size; + + spin_lock( &mmu_context_queue.lock ); + + if ( (size = mmu_context_queue.size) >= NUM_USER_CONTEXT ) { + spin_unlock( &mmu_context_queue.lock ); + mmu_context_underflow(); + } + +#ifdef MMU_CONTEXT_LIFO + index = mmu_context_queue.head; + index = (index > 0) ? index-1 : LAST_USER_CONTEXT-1; + mmu_context_queue.head = index; +#else + index = mmu_context_queue.tail; + index = (index < LAST_USER_CONTEXT-1) ? index+1 : 0; + mmu_context_queue.tail = index; +#endif + + mmu_context_queue.size = size+1; + mmu_context_queue.elements[index] = mm->context; + + spin_unlock( &mmu_context_queue.lock ); +} + + +/* + * switch_mm is the entry point called from the architecture independent + * code in kernel/sched.c + */ +static inline void +switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk, int cpu) +{ + tsk->thread.pgdir = next->pgd; /* cache the pgdir in the thread + maybe not needed any more */ + flush_stab(); +} + +/* + * After we have set current->mm to a new value, this activates + * the context for the new mm so we see the new mappings. + */ +static inline void +activate_mm(struct mm_struct *active_mm, struct mm_struct *mm) +{ + current->thread.pgdir = mm->pgd; + flush_stab(); +} + + +#define VSID_RANDOMIZER 42470972311 +#define VSID_MASK 0xfffffffff + + +/* This is only valid for kernel (including vmalloc, imalloc and bolted) EA's + */ +static inline unsigned long +get_kernel_vsid( unsigned long ea ) +{ + unsigned long ordinal, vsid; + + ordinal = (((ea >> 28) & 0x1fffff) * LAST_USER_CONTEXT) | (ea >> 60); + vsid = (ordinal * VSID_RANDOMIZER) & VSID_MASK; + + ifppcdebug(PPCDBG_HTABSTRESS) { + /* For debug, this path creates a very poor vsid distribuition. + * A user program can access virtual addresses in the form + * 0x0yyyyxxxx000 where yyyy = xxxx to cause multiple mappings + * to hash to the same page table group. + */ + ordinal = ((ea >> 28) & 0x1fff) | (ea >> 44); + vsid = ordinal & VSID_MASK; + } + + return vsid; +} + +/* This is only valid for user EA's (user EA's do not exceed 2^41 (EADDR_SIZE)) + */ +static inline unsigned long +get_vsid( unsigned long context, unsigned long ea ) +{ + unsigned long ordinal, vsid; + + ordinal = (((ea >> 28) & 0x1fffff) * LAST_USER_CONTEXT) | context; + vsid = (ordinal * VSID_RANDOMIZER) & VSID_MASK; + + ifppcdebug(PPCDBG_HTABSTRESS) { + /* See comment above. */ + ordinal = ((ea >> 28) & 0x1fff) | (context << 16); + vsid = ordinal & VSID_MASK; + } + + return vsid; +} + +#endif /* __PPC64_MMU_CONTEXT_H */ diff -Nru a/include/asm-ppc64/module.h b/include/asm-ppc64/module.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/module.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,18 @@ +#ifndef _ASM_PPC64_MODULE_H +#define _ASM_PPC64_MODULE_H +/* + * This file contains the PPC architecture specific module code. + * + * Copyright (C) 2001 PPC 64 Team, IBM Corp + * + * 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. + */ + +#define module_map(x) vmalloc(x) +#define module_unmap(x) vfree(x) +#define arch_init_modules(x) do { } while (0) +#define module_arch_init(x) (0) +#endif /* _ASM_PPC64_MODULE_H */ diff -Nru a/include/asm-ppc64/msgbuf.h b/include/asm-ppc64/msgbuf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/msgbuf.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,27 @@ +#ifndef _PPC64_MSGBUF_H +#define _PPC64_MSGBUF_H + +/* + * The msqid64_ds structure for the PPC architecture. + * + * 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. + */ + +struct msqid64_ds { + struct ipc64_perm msg_perm; + __kernel_time_t msg_stime; /* last msgsnd time */ + __kernel_time_t msg_rtime; /* last msgrcv time */ + __kernel_time_t msg_ctime; /* last change time */ + unsigned long msg_cbytes; /* current number of bytes on queue */ + unsigned long msg_qnum; /* number of messages in queue */ + unsigned long msg_qbytes; /* max number of bytes on queue */ + __kernel_pid_t msg_lspid; /* pid of last msgsnd */ + __kernel_pid_t msg_lrpid; /* last receive pid */ + unsigned long __unused1; + unsigned long __unused2; +}; + +#endif /* _PPC64_MSGBUF_H */ diff -Nru a/include/asm-ppc64/namei.h b/include/asm-ppc64/namei.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/namei.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,23 @@ +/* + * linux/include/asm-ppc/namei.h + * Adapted from linux/include/asm-alpha/namei.h + * + * Included from linux/fs/namei.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __PPC64_NAMEI_H +#define __PPC64_NAMEI_H + +/* This dummy routine maybe changed to something useful + * for /usr/gnemul/ emulation stuff. + * Look at asm-sparc/namei.h for details. + */ + +#define __emul_prefix() NULL + +#endif /* __PPC64_NAMEI_H */ diff -Nru a/include/asm-ppc64/nvram.h b/include/asm-ppc64/nvram.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/nvram.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,68 @@ +/* + * PreP compliant NVRAM access + * This needs to be updated for PPC64 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _PPC64_NVRAM_H +#define _PPC64_NVRAM_H + +#define NVRAM_AS0 0x74 +#define NVRAM_AS1 0x75 +#define NVRAM_DATA 0x77 + + +/* RTC Offsets */ + +#define MOTO_RTC_SECONDS 0x1FF9 +#define MOTO_RTC_MINUTES 0x1FFA +#define MOTO_RTC_HOURS 0x1FFB +#define MOTO_RTC_DAY_OF_WEEK 0x1FFC +#define MOTO_RTC_DAY_OF_MONTH 0x1FFD +#define MOTO_RTC_MONTH 0x1FFE +#define MOTO_RTC_YEAR 0x1FFF +#define MOTO_RTC_CONTROLA 0x1FF8 +#define MOTO_RTC_CONTROLB 0x1FF9 + +#ifndef BCD_TO_BIN +#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) +#endif + +#ifndef BIN_TO_BCD +#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10) +#endif + +/* PowerMac specific nvram stuffs */ + +enum { + pmac_nvram_OF, /* Open Firmware partition */ + pmac_nvram_XPRAM, /* MacOS XPRAM partition */ + pmac_nvram_NR /* MacOS Name Registry partition */ +}; + +/* Return partition offset in nvram */ +extern int pmac_get_partition(int partition); + +/* Direct access to XPRAM */ +extern u8 pmac_xpram_read(int xpaddr); +extern void pmac_xpram_write(int xpaddr, u8 data); + +/* Some offsets in XPRAM */ +#define PMAC_XPRAM_MACHINE_LOC 0xe4 +#define PMAC_XPRAM_SOUND_VOLUME 0x08 + +/* Machine location structure in XPRAM */ +struct pmac_machine_location { + u32 latitude; /* 2+30 bit Fractional number */ + u32 longitude; /* 2+30 bit Fractional number */ + u32 delta; /* mix of GMT delta and DLS */ +}; + +/* /dev/nvram ioctls */ +#define PMAC_NVRAM_GET_OFFSET _IOWR('p', 0x40, int) /* Get NVRAM partition offset */ + +#endif /* _PPC64_NVRAM_H */ diff -Nru a/include/asm-ppc64/page.h b/include/asm-ppc64/page.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/page.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,228 @@ +#ifndef _PPC64_PAGE_H +#define _PPC64_PAGE_H + +/* + * Copyright (C) 2001 PPC64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +/* PAGE_SHIFT determines the page size */ +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE-1)) +#define PAGE_OFFSET_MASK (PAGE_SIZE-1) + +#define SID_SHIFT 28 +#define SID_MASK 0xfffffffff +#define GET_ESID(x) (((x) >> SID_SHIFT) & SID_MASK) + +/* Define an illegal instr to trap on the bug. + * We don't use 0 because that marks the end of a function + * in the ELF ABI. That's "Boo Boo" in case you wonder... + */ +#define BUG_OPCODE .long 0x00b00b00 /* For asm */ +#define BUG_ILLEGAL_INSTR "0x00b00b00" /* For BUG macro */ + +#ifdef __KERNEL__ +#ifndef __ASSEMBLY__ +#include + +#define STRICT_MM_TYPECHECKS + +#define REGION_SIZE 4UL +#define OFFSET_SIZE 60UL +#define REGION_SHIFT 60UL +#define OFFSET_SHIFT 0UL +#define REGION_MASK (((1UL<dCacheL1LineSize; + lines = naca->dCacheL1LinesPerPage; + + __asm__ __volatile__( +" mtctr %1\n\ +1: dcbz 0,%0\n\ + add %0,%0,%3\n\ + bdnz+ 1b" + : "=r" (addr) + : "r" (lines), "0" (addr), "r" (line_size) + : "ctr", "memory"); +} + +extern void copy_page(void *to, void *from); +struct page; +extern void clear_user_page(void *page, unsigned long vaddr); +extern void copy_user_page(void *to, void *from, unsigned long vaddr); + +#ifdef STRICT_MM_TYPECHECKS +/* + * These are used to make use of C type-checking. + * Entries in the pte table are 64b, while entries in the pgd & pmd are 32b. + */ +typedef struct { unsigned long pte; } pte_t; +typedef struct { unsigned int pmd; } pmd_t; +typedef struct { unsigned int pgd; } pgd_t; +typedef struct { unsigned long pgprot; } pgprot_t; + +#define pte_val(x) ((x).pte) +#define pmd_val(x) ((x).pmd) +#define pgd_val(x) ((x).pgd) +#define pgprot_val(x) ((x).pgprot) + +#define __pte(x) ((pte_t) { (x) } ) +#define __pmd(x) ((pmd_t) { (x) } ) +#define __pgd(x) ((pgd_t) { (x) } ) +#define __pgprot(x) ((pgprot_t) { (x) } ) + +#else +/* + * .. while these make it easier on the compiler + */ +typedef unsigned long pte_t; +typedef unsigned int pmd_t; +typedef unsigned int pgd_t; +typedef unsigned long pgprot_t; + +#define pte_val(x) (x) +#define pmd_val(x) (x) +#define pgd_val(x) (x) +#define pgprot_val(x) (x) + +#define __pte(x) (x) +#define __pmd(x) (x) +#define __pgd(x) (x) +#define __pgprot(x) (x) + +#endif + +#ifdef CONFIG_XMON +#include +extern void xmon(struct pt_regs *excp); +#define BUG() do { \ + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ + xmon(0); \ +} while (0) +#else +#define BUG() do { \ + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ + __asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); \ +} while (0) +#endif + +#define PAGE_BUG(page) do { BUG(); } while (0) + +/* Pure 2^n version of get_order */ +extern __inline__ int get_order(unsigned long size) +{ + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + +#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) + +#endif /* __ASSEMBLY__ */ + +/* align addr on a size boundry - adjust address up/down if needed */ +#define _ALIGN_UP(addr,size) (((addr)+((size)-1))&(~((size)-1))) +#define _ALIGN_DOWN(addr,size) ((addr)&(~((size)-1))) + +/* align addr on a size boundry - adjust address up if needed */ +#define _ALIGN(addr,size) _ALIGN_UP(addr,size) + +/* to align the pointer to the (next) double word boundary */ +#define DOUBLEWORD_ALIGN(addr) _ALIGN(addr,sizeof(unsigned long)) + +/* to align the pointer to the (next) page boundary */ +#define PAGE_ALIGN(addr) _ALIGN(addr, PAGE_SIZE) + +#ifdef MODULE +#define __page_aligned __attribute__((__aligned__(PAGE_SIZE))) +#else +#define __page_aligned \ + __attribute__((__aligned__(PAGE_SIZE), \ + __section__(".data.page_aligned"))) +#endif + + +/* This must match the -Ttext linker address */ +/* Note: tophys & tovirt make assumptions about how */ +/* KERNELBASE is defined for performance reasons. */ +/* When KERNELBASE moves, those macros may have */ +/* to change! */ +#define PAGE_OFFSET 0xC000000000000000 +#define KERNELBASE PAGE_OFFSET +#define VMALLOCBASE 0xD000000000000000 +#define IOREGIONBASE 0xE000000000000000 + +#define IO_REGION_ID (IOREGIONBASE>>REGION_SHIFT) +#define VMALLOC_REGION_ID (VMALLOCBASE>>REGION_SHIFT) +#define KERNEL_REGION_ID (KERNELBASE>>REGION_SHIFT) +#define USER_REGION_ID (0UL) +#define REGION_ID(X) (((unsigned long)(X))>>REGION_SHIFT) + +/* + * Define valid/invalid EA bits (for all ranges) + */ +#define VALID_EA_BITS (0x000001ffffffffffUL) +#define INVALID_EA_BITS (~(REGION_MASK|VALID_EA_BITS)) + +#define IS_VALID_REGION_ID(x) \ + (((x) == USER_REGION_ID) || ((x) >= KERNEL_REGION_ID)) +#define IS_VALID_EA(x) \ + ((!((x) & INVALID_EA_BITS)) && IS_VALID_REGION_ID(REGION_ID(x))) + +#define __bpn_to_ba(x) ((((unsigned long)(x))<> PAGE_SHIFT) + +#define __va(x) ((void *)((unsigned long)(x) + KERNELBASE)) + +/* Given that physical addresses do not map 1-1 to absolute addresses, we + * use these macros to better specify exactly what we want to do. + * The only restriction on their use is that the absolute address + * macros cannot be used until after the LMB structure has been + * initialized in prom.c. -Peter + */ +#define __v2p(x) ((void *) __pa(x)) +#define __v2a(x) ((void *) phys_to_absolute(__pa(x))) +#define __p2a(x) ((void *) phys_to_absolute(x)) +#define __p2v(x) ((void *) __va(x)) +#define __a2p(x) ((void *) absolute_to_phys(x)) +#define __a2v(x) ((void *) __va(absolute_to_phys(x))) + +#define virt_to_page(kaddr) (mem_map+(__pa((unsigned long)kaddr) >> PAGE_SHIFT)) + +#define VALID_PAGE(page) ((page - mem_map) < max_mapnr) + +#define MAP_NR(addr) (__pa(addr) >> PAGE_SHIFT) + +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + +#endif /* __KERNEL__ */ +#endif /* _PPC64_PAGE_H */ diff -Nru a/include/asm-ppc64/param.h b/include/asm-ppc64/param.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/param.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,41 @@ +#ifndef _ASM_PPC64_PARAM_H +#define _ASM_PPC64_PARAM_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef HZ +#define HZ 1024 +#ifdef __KERNEL__ +#if HZ == 100 +/* ppc (like X86) is defined to provide userspace with a world where HZ=100 + We have to do this, (x*const)/const2 isnt optimised out because its not + a null operation as it might overflow.. */ +#define hz_to_std(a) (a) +#else +#define hz_to_std(a) ((a)*(100/HZ)+((a)*(100%HZ))/HZ) +#endif +#endif +#endif + +#define EXEC_PAGESIZE 4096 + +#ifndef NGROUPS +#define NGROUPS 32 +#endif + +#ifndef NOGROUP +#define NOGROUP (-1) +#endif + +#define MAXHOSTNAMELEN 64 /* max length of hostname */ + +#ifdef __KERNEL__ +# define CLOCKS_PER_SEC HZ /* frequency at which times() counts */ +#endif + +#endif /* _ASM_PPC64_PARAM_H */ diff -Nru a/include/asm-ppc64/parport.h b/include/asm-ppc64/parport.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/parport.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,18 @@ +/* + * parport.h: platform-specific PC-style parport initialisation + * + * Copyright (C) 1999, 2000 Tim Waugh + * + * This file should only be included by drivers/parport/parport_pc.c. + */ + +#ifndef _ASM_PPC64_PARPORT_H +#define _ASM_PPC64_PARPORT_H + +static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma); +static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma) +{ + return parport_pc_find_isa_ports (autoirq, autodma); +} + +#endif /* !(_ASM_PPC_PARPORT_H) */ diff -Nru a/include/asm-ppc64/pci-bridge.h b/include/asm-ppc64/pci-bridge.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/pci-bridge.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,114 @@ +#ifdef __KERNEL__ +#ifndef _ASM_PCI_BRIDGE_H +#define _ASM_PCI_BRIDGE_H + +/* + * 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. + */ + +struct device_node; +struct pci_controller; + +/* + * pci_io_base returns the memory address at which you can access + * the I/O space for PCI bus number `bus' (or NULL on error). + */ +extern void *pci_bus_io_base(unsigned int bus); +extern unsigned long pci_bus_io_base_phys(unsigned int bus); +extern unsigned long pci_bus_mem_base_phys(unsigned int bus); + +/* Get the PCI host controller for a bus */ +extern struct pci_controller* pci_bus_to_hose(int bus); + +/* Get the PCI host controller for an OF device */ +extern struct pci_controller* +pci_find_hose_for_OF_device(struct device_node* node); + +enum phb_types { + phb_type_unknown = 0x0, + phb_type_hypervisor = 0x1, + phb_type_python = 0x10, + phb_type_speedwagon = 0x11 +}; + +/* + * Structure of a PCI controller (host bridge) + */ +struct pci_controller { + char what[8]; /* Eye catcher */ + enum phb_types type; /* Type of hardware */ + struct pci_controller *next; + struct pci_bus *bus; + void *arch_data; + + int first_busno; + int last_busno; + + void *io_base_virt; + unsigned long io_base_phys; + + /* Some machines (PReP) have a non 1:1 mapping of + * the PCI memory space in the CPU bus space + */ + unsigned long pci_mem_offset; + unsigned long pci_io_offset; + + struct pci_ops *ops; + volatile unsigned long *cfg_addr; + volatile unsigned char *cfg_data; + volatile unsigned long *phb_regs; + volatile unsigned long *chip_regs; + + /* Currently, we limit ourselves to 1 IO range and 3 mem + * ranges since the common pci_bus structure can't handle more + */ + struct resource io_resource; + struct resource mem_resources[3]; + int mem_resource_count; + int global_number; + int local_number; + int system_bus_number; + unsigned long buid; + unsigned long dma_window_base_cur; + unsigned long dma_window_size; +}; + + +/* This version handles the new Uni-N host bridge, the iobase is now + * a per-device thing. I also added the memory base so PReP can + * be fixed to return 0xc0000000 (I didn't actually implement it) + * + * pci_dev_io_base() returns either a virtual (ioremap'ed) address or + * a physical address. In-kernel clients will use logical while the + * sys_pciconfig_iobase syscall returns a physical one to userland. + */ +void *pci_dev_io_base(unsigned char bus, unsigned char devfn, int physical); +void *pci_dev_mem_base(unsigned char bus, unsigned char devfn); + +/* Returns the root-bridge number (Uni-N number) of a device */ +int pci_dev_root_bridge(unsigned char bus, unsigned char devfn); + +/* + * pci_device_loc returns the bus number and device/function number + * for a device on a PCI bus, given its device_node struct. + * It returns 0 if OK, -1 on error. + */ +int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, + unsigned char *devfn_ptr); + +struct bridge_data { + volatile unsigned int *cfg_addr; + volatile unsigned char *cfg_data; + void *io_base; /* virtual */ + unsigned long io_base_phys; + int bus_number; + int max_bus; + struct bridge_data *next; + struct device_node *node; +}; + +#endif +#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc64/pci.h b/include/asm-ppc64/pci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/pci.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,151 @@ +#ifndef __PPC64_PCI_H +#define __PPC64_PCI_H +#ifdef __KERNEL__ + +/* + * 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. + */ + +/* Values for the `which' argument to sys_pciconfig_iobase syscall. */ +#define IOBASE_BRIDGE_NUMBER 0 +#define IOBASE_MEMORY 1 +#define IOBASE_IO 2 +#define IOBASE_ISA_IO 3 +#define IOBASE_ISA_MEM 4 + +/* Can be used to override the logic in pci_scan_bus for skipping + * already-configured bus numbers - to be used for buggy BIOSes + * or architectures with incomplete PCI setup by the loader. + */ +extern int pcibios_assign_all_busses(void); + +#define PCIBIOS_MIN_IO 0x1000 +#define PCIBIOS_MIN_MEM 0x10000000 + +extern inline void pcibios_set_master(struct pci_dev *dev) +{ + /* No special bus mastering setup handling */ +} + +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + +#include +#include +#include +#include +#include +#include + +struct pci_dev; +#define REG_SAVE_SIZE 64 +/************************************************************************ + * Structure to hold the data for PCI Register Save/Restore functions. * + ************************************************************************/ +struct pci_config_reg_save_area { + struct pci_dev* PciDev; /* Pointer to device(Sanity Check) */ + int Flags; /* Control & Info Flags */ + int RCode; /* Return Code on Save/Restore */ + int Register; /* Pointer to current register. */ + u8 Regs[REG_SAVE_SIZE]; /* Save Area */ +}; +/************************************************************************ + * Functions to support device reset * + ************************************************************************/ +extern int pci_reset_device(struct pci_dev*, int, int); +extern int pci_save_config_regs(struct pci_dev*,struct pci_config_reg_save_area*); +extern int pci_restore_config_regs(struct pci_dev*,struct pci_config_reg_save_area*); +extern char* pci_card_location(struct pci_dev*); + +extern void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle); +extern void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle); + +extern dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, + size_t size, int direction); +extern void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction); +extern int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction); +extern void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction); + +extern void pSeries_pcibios_init_early(void); + +extern inline void pci_dma_sync_single(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + /* nothing to do */ +} + +extern inline void pci_dma_sync_sg(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + /* nothing to do */ +} + +/* Return whether the given PCI device DMA address mask can + * be supported properly. For example, if your device can + * only drive the low 24-bits during PCI bus mastering, then + * you would pass 0x00ffffff as the mask to this function. + */ +extern inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) +{ + return 1; +} + +/* Return the index of the PCI controller for device PDEV. */ +extern int pci_controller_num(struct pci_dev *pdev); + +struct vm_area_struct; +/* Map a range of PCI memory or I/O space for a device into user space */ +int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine); + +/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ +#define HAVE_PCI_MMAP 1 + +#define sg_dma_address(sg) ((sg)->dma_address) +#define sg_dma_len(sg) ((sg)->dma_length) + +#define pci_map_page(dev, page, off, size, dir) \ + pci_map_single(dev, (page_address(page) + (off)), size, dir) +#define pci_unmap_page(dev,addr,sz,dir) pci_unmap_single(dev,addr,sz,dir) + +/* pci_unmap_{single,page} is not a nop, thus... */ +#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) \ + dma_addr_t ADDR_NAME; +#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) \ + __u32 LEN_NAME; +#define pci_unmap_addr(PTR, ADDR_NAME) \ + ((PTR)->ADDR_NAME) +#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) \ + (((PTR)->ADDR_NAME) = (VAL)) +#define pci_unmap_len(PTR, LEN_NAME) \ + ((PTR)->LEN_NAME) +#define pci_unmap_len_set(PTR, LEN_NAME, VAL) \ + (((PTR)->LEN_NAME) = (VAL)) + +#define pci_dac_dma_supported(pci_dev, mask) (0) + +/* The PCI address space does equal the physical memory + * address space. The networking and block device layers use + * this boolean for bounce buffer decisions. + */ +#define PCI_DMA_BUS_IS_PHYS (0) + +#endif /* __KERNEL__ */ + +#endif /* __PPC64_PCI_H */ diff -Nru a/include/asm-ppc64/pci_dma.h b/include/asm-ppc64/pci_dma.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/pci_dma.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,98 @@ +/* + * pci_dma.h + * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PCI_DMA_H +#define _PCI_DMA_H + +#include +#include + +/* + * NUM_TCE_LEVELS defines the largest contiguous block + * of dma (tce) space we can get. NUM_TCE_LEVELS = 10 + * allows up to 2**9 pages (512 * 4096) = 2 MB + */ +#define NUM_TCE_LEVELS 10 + +#define NO_TCE ((dma_addr_t)-1) + +/* + * Tces come in two formats, one for the virtual bus and a different + * format for PCI + */ +#define TCE_VB 0 +#define TCE_PCI 1 + +union Tce { + u64 wholeTce; + struct { + u64 cacheBits :6; /* Cache hash bits - not used */ + u64 rsvd :6; + u64 rpn :40; /* Absolute page number */ + u64 valid :1; /* Tce is valid (vb only) */ + u64 allIo :1; /* Tce is valid for all lps (vb only) */ + u64 lpIndex :8; /* LpIndex for user of TCE (vb only) */ + u64 pciWrite :1; /* Write allowed (pci only) */ + u64 readWrite :1; /* Read allowed (pci), Write allowed (vb) */ + } tceBits; +}; + +struct Bitmap { + unsigned long numBits; + unsigned long numBytes; + unsigned char * map; +}; + +struct MultiLevelBitmap { + unsigned long maxLevel; + struct Bitmap level[NUM_TCE_LEVELS]; +}; + +struct TceTable { + u64 busNumber; + u64 size; + u64 startOffset; + u64 base; /* pSeries native only */ + u64 index; + u64 tceType; + spinlock_t lock; + struct MultiLevelBitmap mlbm; +}; + +struct TceTableManagerCB { + u64 busNumber; /* Bus number for this tce table */ + u64 start; /* Will be NULL for secondary */ + u64 totalSize; /* Size (in pages) of whole table */ + u64 startOffset; /* Index into real tce table of the + start of our section */ + u64 size; /* Size (in pages) of our section */ + u64 index; /* Index of this tce table (token?) */ + u16 maxTceTableIndex; /* Max num of tables for partition */ + u8 virtualBusFlag; /* Flag to indicate virtual bus */ + u8 rsvd[5]; +}; + +extern struct TceTable virtBusTceTable; /* Tce table for virtual bus */ + +extern void create_tce_tables(void); + +void tce_init_pSeries(void); +void tce_init_iSeries(void); + +#endif diff -Nru a/include/asm-ppc64/pgalloc.h b/include/asm-ppc64/pgalloc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/pgalloc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,127 @@ +#ifndef _PPC64_PGALLOC_H +#define _PPC64_PGALLOC_H + +#include +#include +#include +#include + +/* + * 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. + */ +#define quicklists get_paca() + +#define pgd_quicklist (quicklists->pgd_cache) +#define pmd_quicklist (quicklists->pmd_cache) +#define pte_quicklist (quicklists->pte_cache) +#define pgtable_cache_size (quicklists->pgtable_cache_sz) + +static inline pgd_t* +pgd_alloc_one_fast (struct mm_struct *mm) +{ + unsigned long *ret = pgd_quicklist; + + if (ret != NULL) { + pgd_quicklist = (unsigned long *)(*ret); + ret[0] = 0; + --pgtable_cache_size; + } else + ret = NULL; + return (pgd_t *) ret; +} + +static inline pgd_t* +pgd_alloc (struct mm_struct *mm) +{ + /* the VM system never calls pgd_alloc_one_fast(), so we do it here. */ + pgd_t *pgd = pgd_alloc_one_fast(mm); + + if (pgd == NULL) { + pgd = (pgd_t *)__get_free_page(GFP_KERNEL); + if (pgd != NULL) + clear_page(pgd); + } + return pgd; +} + +static inline void +pgd_free (pgd_t *pgd) +{ + *(unsigned long *)pgd = (unsigned long) pgd_quicklist; + pgd_quicklist = (unsigned long *) pgd; + ++pgtable_cache_size; +} + +#define pgd_populate(MM, PGD, PMD) pgd_set(PGD, PMD) + +static inline pmd_t* +pmd_alloc_one_fast (struct mm_struct *mm, unsigned long addr) +{ + unsigned long *ret = (unsigned long *)pmd_quicklist; + + if (ret != NULL) { + pmd_quicklist = (unsigned long *)(*ret); + ret[0] = 0; + --pgtable_cache_size; + } + return (pmd_t *)ret; +} + +static inline pmd_t* +pmd_alloc_one (struct mm_struct *mm, unsigned long addr) +{ + pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL); + + if (pmd != NULL) + clear_page(pmd); + return pmd; +} + +static inline void +pmd_free (pmd_t *pmd) +{ + *(unsigned long *)pmd = (unsigned long) pmd_quicklist; + pmd_quicklist = (unsigned long *) pmd; + ++pgtable_cache_size; +} + +#define pmd_populate(MM, PMD, PTE) pmd_set(PMD, PTE) + +static inline pte_t* +pte_alloc_one_fast (struct mm_struct *mm, unsigned long addr) +{ + unsigned long *ret = (unsigned long *)pte_quicklist; + + if (ret != NULL) { + pte_quicklist = (unsigned long *)(*ret); + ret[0] = 0; + --pgtable_cache_size; + } + return (pte_t *)ret; +} + + +static inline pte_t* +pte_alloc_one (struct mm_struct *mm, unsigned long addr) +{ + pte_t *pte = (pte_t *) __get_free_page(GFP_KERNEL); + + if (pte != NULL) + clear_page(pte); + return pte; +} + +static inline void +pte_free (pte_t *pte) +{ + *(unsigned long *)pte = (unsigned long) pte_quicklist; + pte_quicklist = (unsigned long *) pte; + ++pgtable_cache_size; +} + +extern int do_check_pgt_cache(int, int); + +#endif /* _PPC64_PGALLOC_H */ diff -Nru a/include/asm-ppc64/pgtable.h b/include/asm-ppc64/pgtable.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/pgtable.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,441 @@ +#ifndef _PPC64_PGTABLE_H +#define _PPC64_PGTABLE_H + +/* + * This file contains the functions and defines necessary to modify and use + * the ppc64 hashed page table. + */ + +#ifndef __ASSEMBLY__ +#include /* For TASK_SIZE */ +#include +#include +#endif /* __ASSEMBLY__ */ + +/* Certain architectures need to do special things when pte's + * within a page table are directly modified. Thus, the following + * hook is made available. + */ + +/* PMD_SHIFT determines what a second-level page table entry can map */ +#define PMD_SHIFT (PAGE_SHIFT + PAGE_SHIFT - 3) +#define PMD_SIZE (1UL << PMD_SHIFT) +#define PMD_MASK (~(PMD_SIZE-1)) + +/* PGDIR_SHIFT determines what a third-level page table entry can map */ +#define PGDIR_SHIFT (PAGE_SHIFT + (PAGE_SHIFT - 3) + (PAGE_SHIFT - 2)) +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE-1)) + +/* + * Entries per page directory level. The PTE level must use a 64b record + * for each page table entry. The PMD and PGD level use a 32b record for + * each entry by assuming that each entry is page aligned. + */ +#define PTE_INDEX_SIZE 9 +#define PMD_INDEX_SIZE 10 +#define PGD_INDEX_SIZE 10 + +#define PTRS_PER_PTE (1 << PTE_INDEX_SIZE) +#define PTRS_PER_PMD (1 << PMD_INDEX_SIZE) +#define PTRS_PER_PGD (1 << PGD_INDEX_SIZE) + +#if 0 +/* DRENG / PPPBBB This is a compiler bug!!! */ +#define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE) +#else +#define USER_PTRS_PER_PGD (1024) +#endif +#define FIRST_USER_PGD_NR 0 + +#define EADDR_SIZE (PTE_INDEX_SIZE + PMD_INDEX_SIZE + \ + PGD_INDEX_SIZE + PAGE_SHIFT) + +/* + * Define the address range of the vmalloc VM area. + */ +#define VMALLOC_START (0xD000000000000000) +#define VMALLOC_VMADDR(x) ((unsigned long)(x)) +#define VMALLOC_END (VMALLOC_START + VALID_EA_BITS) + +/* + * Define the address range of the imalloc VM area. + * (used for ioremap) + */ +#define IMALLOC_START (ioremap_bot) +#define IMALLOC_VMADDR(x) ((unsigned long)(x)) +#define IMALLOC_BASE (0xE000000000000000) +#define IMALLOC_END (IMALLOC_BASE + VALID_EA_BITS) + +/* + * Define the address range mapped virt <-> physical + */ +#define KRANGE_START KERNELBASE +#define KRANGE_END (KRANGE_START + VALID_EA_BITS) + +/* + * Define the user address range + */ +#define USER_START (0UL) +#define USER_END (USER_START + VALID_EA_BITS) + + +/* + * Bits in a linux-style PTE. These match the bits in the + * (hardware-defined) PowerPC PTE as closely as possible. + */ +#define _PAGE_PRESENT 0x001UL /* software: pte contains a translation */ +#define _PAGE_USER 0x002UL /* matches one of the PP bits */ +#define _PAGE_RW 0x004UL /* software: user write access allowed */ +#define _PAGE_GUARDED 0x008UL +#define _PAGE_COHERENT 0x010UL /* M: enforce memory coherence (SMP systems) */ +#define _PAGE_NO_CACHE 0x020UL /* I: cache inhibit */ +#define _PAGE_WRITETHRU 0x040UL /* W: cache write-through */ +#define _PAGE_DIRTY 0x080UL /* C: page changed */ +#define _PAGE_ACCESSED 0x100UL /* R: page referenced */ +#define _PAGE_HPTENOIX 0x200UL /* software: pte HPTE slot unknown */ +#define _PAGE_HASHPTE 0x400UL /* software: pte has an associated HPTE */ +#define _PAGE_EXEC 0x800UL /* software: i-cache coherence required */ +#define _PAGE_SECONDARY 0x8000UL /* software: HPTE is in secondary group */ +#define _PAGE_GROUP_IX 0x7000UL /* software: HPTE index within group */ +/* Bits 0x7000 identify the index within an HPT Group */ +#define _PAGE_HPTEFLAGS (_PAGE_HASHPTE | _PAGE_HPTENOIX | _PAGE_SECONDARY | _PAGE_GROUP_IX) +/* PAGE_MASK gives the right answer below, but only by accident */ +/* It should be preserving the high 48 bits and then specifically */ +/* preserving _PAGE_SECONDARY | _PAGE_GROUP_IX */ +#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_HPTEFLAGS) + +#define _PAGE_BASE _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_COHERENT + +#define _PAGE_WRENABLE _PAGE_RW | _PAGE_DIRTY + +/* __pgprot defined in asm-ppc64/page.h */ +#define PAGE_NONE __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED) + +#define PAGE_SHARED __pgprot(_PAGE_BASE | _PAGE_RW | _PAGE_USER) +#define PAGE_SHARED_X __pgprot(_PAGE_BASE | _PAGE_RW | _PAGE_USER | _PAGE_EXEC) +#define PAGE_COPY __pgprot(_PAGE_BASE | _PAGE_USER) +#define PAGE_COPY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC) +#define PAGE_READONLY __pgprot(_PAGE_BASE | _PAGE_USER) +#define PAGE_READONLY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC) +#define PAGE_KERNEL __pgprot(_PAGE_BASE | _PAGE_WRENABLE) +#define PAGE_KERNEL_CI __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \ + _PAGE_WRENABLE | _PAGE_NO_CACHE | _PAGE_GUARDED) + +/* + * The PowerPC can only do execute protection on a segment (256MB) basis, + * not on a page basis. So we consider execute permission the same as read. + * Also, write permissions imply read permissions. + * This is the closest we can get.. + */ +#define __P000 PAGE_NONE +#define __P001 PAGE_READONLY_X +#define __P010 PAGE_COPY +#define __P011 PAGE_COPY_X +#define __P100 PAGE_READONLY +#define __P101 PAGE_READONLY_X +#define __P110 PAGE_COPY +#define __P111 PAGE_COPY_X + +#define __S000 PAGE_NONE +#define __S001 PAGE_READONLY_X +#define __S010 PAGE_SHARED +#define __S011 PAGE_SHARED_X +#define __S100 PAGE_READONLY +#define __S101 PAGE_READONLY_X +#define __S110 PAGE_SHARED +#define __S111 PAGE_SHARED_X + +#ifndef __ASSEMBLY__ + +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern unsigned long empty_zero_page[PAGE_SIZE/sizeof(unsigned long)]; +#define ZERO_PAGE(vaddr) (mem_map + MAP_NR(empty_zero_page)) +#endif /* __ASSEMBLY__ */ + +/* shift to put page number into pte */ +#define PTE_SHIFT (16) + +#ifndef __ASSEMBLY__ + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + * + * mk_pte_phys takes a physical address as input + * + * mk_pte takes a (struct page *) as input + */ + +#define mk_pte_phys(physpage,pgprot) \ +({ \ + pte_t pte; \ + pte_val(pte) = (((physpage)<<(PTE_SHIFT-PAGE_SHIFT)) | pgprot_val(pgprot)); \ + pte; \ +}) + +#define mk_pte(page,pgprot) \ +({ \ + pte_t pte; \ + pte_val(pte) = ((unsigned long)((page) - mem_map) << PTE_SHIFT) | \ + pgprot_val(pgprot); \ + pte; \ +}) + +#define pte_modify(_pte, newprot) \ + (__pte((pte_val(_pte) & _PAGE_CHG_MASK) | pgprot_val(newprot))) + +#define pte_none(pte) ((pte_val(pte) & ~_PAGE_HPTEFLAGS) == 0) +#define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT) + +/* pte_clear moved to later in this file */ + +#define pte_pagenr(x) ((unsigned long)((pte_val(x) >> PTE_SHIFT))) +#define pte_page(x) (mem_map+pte_pagenr(x)) + +#define pmd_set(pmdp, ptep) (pmd_val(*(pmdp)) = (__ba_to_bpn(ptep))) +#define pmd_none(pmd) (!pmd_val(pmd)) +#define pmd_bad(pmd) ((pmd_val(pmd)) == 0) +#define pmd_present(pmd) ((pmd_val(pmd)) != 0) +#define pmd_clear(pmdp) (pmd_val(*(pmdp)) = 0) +#define pmd_page(pmd) (__bpn_to_ba(pmd_val(pmd))) +#define pgd_set(pgdp, pmdp) (pgd_val(*(pgdp)) = (__ba_to_bpn(pmdp))) +#define pgd_none(pgd) (!pgd_val(pgd)) +#define pgd_bad(pgd) ((pgd_val(pgd)) == 0) +#define pgd_present(pgd) (pgd_val(pgd) != 0UL) +#define pgd_clear(pgdp) (pgd_val(*(pgdp)) = 0UL) +#define pgd_page(pgd) (__bpn_to_ba(pgd_val(pgd))) + +/* + * Find an entry in a page-table-directory. We combine the address region + * (the high order N bits) and the pgd portion of the address. + */ +#define pgd_index(address) (((address) >> (PGDIR_SHIFT)) & (PTRS_PER_PGD -1)) + +#define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address)) + +/* Find an entry in the second-level page table.. */ +#define pmd_offset(dir,addr) \ + ((pmd_t *) pgd_page(*(dir)) + (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))) + +/* Find an entry in the third-level page table.. */ +#define pte_offset(dir,addr) \ + ((pte_t *) pmd_page(*(dir)) + (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))) + +/* to find an entry in a kernel page-table-directory */ +/* This now only contains the vmalloc pages */ +#define pgd_offset_k(address) pgd_offset(&init_mm, address) + +/* to find an entry in the ioremap page-table-directory */ +#define pgd_offset_i(address) (ioremap_pgd + pgd_index(address)) + +/* + * Given a pointer to an mem_map[] entry, return the kernel virtual + * address corresponding to that page. + */ +#define page_address(page) ((page)->virtual) + +#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) + +/* + * The following only work if pte_present() is true. + * Undefined behaviour if not.. + */ +extern inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_USER;} +extern inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_RW;} +extern inline int pte_exec(pte_t pte) { return pte_val(pte) & _PAGE_EXEC;} +extern inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY;} +extern inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED;} + +extern inline void pte_uncache(pte_t pte) { pte_val(pte) |= _PAGE_NO_CACHE; } +extern inline void pte_cache(pte_t pte) { pte_val(pte) &= ~_PAGE_NO_CACHE; } + +extern inline pte_t pte_rdprotect(pte_t pte) { + pte_val(pte) &= ~_PAGE_USER; return pte; } +extern inline pte_t pte_exprotect(pte_t pte) { + pte_val(pte) &= ~_PAGE_EXEC; return pte; } +extern inline pte_t pte_wrprotect(pte_t pte) { + pte_val(pte) &= ~(_PAGE_RW); return pte; } +extern inline pte_t pte_mkclean(pte_t pte) { + pte_val(pte) &= ~(_PAGE_DIRTY); return pte; } +extern inline pte_t pte_mkold(pte_t pte) { + pte_val(pte) &= ~_PAGE_ACCESSED; return pte; } + +extern inline pte_t pte_mkread(pte_t pte) { + pte_val(pte) |= _PAGE_USER; return pte; } +extern inline pte_t pte_mkexec(pte_t pte) { + pte_val(pte) |= _PAGE_USER | _PAGE_EXEC; return pte; } +extern inline pte_t pte_mkwrite(pte_t pte) { + pte_val(pte) |= _PAGE_RW; return pte; } +extern inline pte_t pte_mkdirty(pte_t pte) { + pte_val(pte) |= _PAGE_DIRTY; return pte; } +extern inline pte_t pte_mkyoung(pte_t pte) { + pte_val(pte) |= _PAGE_ACCESSED; return pte; } + +/* Atomic PTE updates */ + +static inline unsigned long pte_update( pte_t *p, unsigned long clr, + unsigned long set ) +{ + unsigned long old, tmp; + + __asm__ __volatile__("\n\ +1: ldarx %0,0,%3 \n\ + andc %1,%0,%4 \n\ + or %1,%1,%5 \n\ + stdcx. %1,0,%3 \n\ + bne- 1b" + : "=&r" (old), "=&r" (tmp), "=m" (*p) + : "r" (p), "r" (clr), "r" (set), "m" (*p) + : "cc" ); + return old; +} + +static inline int ptep_test_and_clear_young(pte_t *ptep) +{ + return (pte_update(ptep, _PAGE_ACCESSED, 0) & _PAGE_ACCESSED) != 0; +} + +static inline int ptep_test_and_clear_dirty(pte_t *ptep) +{ + return (pte_update(ptep, _PAGE_DIRTY, 0) & _PAGE_DIRTY) != 0; +} + +static inline pte_t ptep_get_and_clear(pte_t *ptep) +{ + return __pte(pte_update(ptep, ~_PAGE_HPTEFLAGS, 0)); +} + +static inline void ptep_set_wrprotect(pte_t *ptep) +{ + pte_update(ptep, _PAGE_RW, 0); +} + +static inline void ptep_mkdirty(pte_t *ptep) +{ + pte_update(ptep, 0, _PAGE_DIRTY); +} + +#define pte_same(A,B) (((pte_val(A) ^ pte_val(B)) & ~_PAGE_HPTEFLAGS) == 0) + +/* + * set_pte stores a linux PTE into the linux page table. + * On machines which use an MMU hash table we avoid changing the + * _PAGE_HASHPTE bit. + */ +static inline void set_pte(pte_t *ptep, pte_t pte) +{ + pte_update(ptep, ~_PAGE_HPTEFLAGS, pte_val(pte) & ~_PAGE_HPTEFLAGS); +} + +static inline void pte_clear(pte_t * ptep) +{ + pte_update(ptep, ~_PAGE_HPTEFLAGS, 0); +} + +struct mm_struct; +struct vm_area_struct; +extern void local_flush_tlb_all(void); +extern void local_flush_tlb_mm(struct mm_struct *mm); +extern void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr); +extern void local_flush_tlb_range(struct mm_struct *mm, + unsigned long start, unsigned long end); + +#define flush_tlb_all local_flush_tlb_all +#define flush_tlb_mm local_flush_tlb_mm +#define flush_tlb_page local_flush_tlb_page +#define flush_tlb_range(vma, start, end) local_flush_tlb_range(vma->vm_mm, start, end) + +extern inline void flush_tlb_pgtables(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + /* PPC has hw page tables. */ +} + +/* + * No cache flushing is required when address mappings are + * changed, because the caches on PowerPCs are physically + * addressed. + */ +#define flush_cache_all() do { } while (0) +#define flush_cache_mm(mm) do { } while (0) +#define flush_cache_range(vma, a, b) do { } while (0) +#define flush_cache_page(vma, p) do { } while (0) +#define flush_page_to_ram(page) do { } while (0) + +extern void flush_icache_user_range(struct vm_area_struct *vma, + struct page *page, unsigned long addr, int len); +extern void flush_icache_range(unsigned long, unsigned long); +extern void __flush_dcache_icache(void *page_va); +extern void flush_dcache_page(struct page *page); +extern void flush_icache_page(struct vm_area_struct *vma, struct page *page); + +extern unsigned long va_to_phys(unsigned long address); +extern pte_t *va_to_pte(unsigned long address); +extern unsigned long ioremap_bot, ioremap_base; + +#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT) +#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS) + +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %016lx.\n", __FILE__, __LINE__, pte_val(e)) +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %08x.\n", __FILE__, __LINE__, pmd_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %08x.\n", __FILE__, __LINE__, pgd_val(e)) + +extern pgd_t swapper_pg_dir[1024]; +extern pgd_t ioremap_dir[1024]; + +extern void paging_init(void); + +/* + * Page tables may have changed. We don't need to do anything here + * as entries are faulted into the hash table by the low-level + * data/instruction access exception handlers. + */ +/* + * We won't be able to use update_mmu_cache to update the + * hardware page table because we need to update the pte + * as well, but we don't get the address of the pte, only + * its value. + */ +#define update_mmu_cache(vma, addr, pte) do { } while (0) + +extern void flush_hash_segments(unsigned low_vsid, unsigned high_vsid); +extern void flush_hash_page(unsigned long context, unsigned long ea, pte_t pte); + +/* Encode and de-code a swap entry */ +#define SWP_TYPE(entry) (((entry).val >> 1) & 0x3f) +#define SWP_OFFSET(entry) ((entry).val >> 8) +#define SWP_ENTRY(type, offset) ((swp_entry_t) { ((type) << 1) | ((offset) << 8) }) +#define pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) >> PTE_SHIFT }) +#define swp_entry_to_pte(x) ((pte_t) { (x).val << PTE_SHIFT }) + +/* + * kern_addr_valid is intended to indicate whether an address is a valid + * kernel address. Most 32-bit archs define it as always true (like this) + * but most 64-bit archs actually perform a test. What should we do here? + * The only use is in fs/ncpfs/dir.c + */ +#define kern_addr_valid(addr) (1) + +#define io_remap_page_range remap_page_range + +/* + * No page table caches to initialise + */ +#define pgtable_cache_init() do { } while (0) + +extern void updateBoltedHptePP(unsigned long newpp, unsigned long ea); +extern void hpte_init_pSeries(void); +extern void hpte_init_iSeries(void); + +extern void make_pte(HPTE * htab, unsigned long va, unsigned long pa, + int mode, unsigned long hash_mask, int large); + +#endif /* __ASSEMBLY__ */ +#endif /* _PPC64_PGTABLE_H */ diff -Nru a/include/asm-ppc64/pmc.h b/include/asm-ppc64/pmc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/pmc.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,113 @@ +/* + * pmc.h + * Copyright (C) 2001 Dave Engebretsen & Mike Corrigan IBM Corporation. + * + * The PPC64 PMC subsystem encompases both the hardware PMC registers and + * a set of software event counters. An interface is provided via the + * proc filesystem which can be used to access this subsystem. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Start Change Log + * 2001/06/05 : engebret : Created. + * End Change Log + */ + +#ifndef _PPC64_TYPES_H +#include +#endif + +#ifndef _PMC_H +#define _PMC_H + +#define STAB_ENTRY_MAX 64 + +struct _pmc_hw +{ + u64 mmcr0; + u64 mmcr1; + u64 mmcra; + + u64 pmc1; + u64 pmc2; + u64 pmc3; + u64 pmc4; + u64 pmc5; + u64 pmc6; + u64 pmc7; + u64 pmc8; +}; + +struct _pmc_sw +{ + u64 stab_faults; /* Count of faults on the stab */ + u64 stab_capacity_castouts;/* Count of castouts from the stab */ + u64 stab_invalidations; /* Count of invalidations from the */ + /* stab, not including castouts */ + u64 stab_entry_use[STAB_ENTRY_MAX]; + + u64 htab_primary_overflows; + u64 htab_capacity_castouts; + u64 htab_read_to_write_fault; +}; + +#define PMC_HW_TEXT_ENTRY_COUNT (sizeof(struct _pmc_hw) / sizeof(u64)) +#define PMC_SW_TEXT_ENTRY_COUNT (sizeof(struct _pmc_sw) / sizeof(u64)) +#define PMC_TEXT_ENTRY_SIZE 64 + +struct _pmc_sw_text { + char buffer[PMC_SW_TEXT_ENTRY_COUNT * PMC_TEXT_ENTRY_SIZE]; +}; + +struct _pmc_hw_text { + char buffer[PMC_HW_TEXT_ENTRY_COUNT * PMC_TEXT_ENTRY_SIZE]; +}; + +extern struct _pmc_sw pmc_sw_system; +extern struct _pmc_sw pmc_sw_cpu[]; + +extern struct _pmc_sw_text pmc_sw_text; +extern struct _pmc_hw_text pmc_hw_text; +extern char *ppc64_pmc_stab(int file); +extern char *ppc64_pmc_htab(int file); +extern char *ppc64_pmc_hw(int file); + +#if 1 +#define PMC_SW_PROCESSOR(F) pmc_sw_cpu[smp_processor_id()].F++ +#define PMC_SW_PROCESSOR_A(F, E) (pmc_sw_cpu[smp_processor_id()].F[(E)])++ +#define PMC_SW_SYSTEM(F) pmc_sw_system.F++ +#else +#define PMC_SW_PROCESSOR(F) do {;} while (0) +#define PMC_SW_PROCESSOR_A(F) do {;} while (0) +#define PMC_SW_SYSTEM(F) do {;} while (0) +#endif + +#define MMCR0 795 +#define MMCR1 798 +#define MMCRA 786 +#define PMC1 787 +#define PMC2 788 +#define PMC3 789 +#define PMC4 790 +#define PMC5 791 +#define PMC6 792 +#define PMC7 793 +#define PMC8 794 + +#define PMC_CONTROL_CPI 1 +#define PMC_CONTROL_TLB 2 + +#endif /* _PMC_H */ diff -Nru a/include/asm-ppc64/poll.h b/include/asm-ppc64/poll.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/poll.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,31 @@ +#ifndef __PPC64_POLL_H +#define __PPC64_POLL_H + +/* + * Copyright (C) 2001 PPC64 Team, IBM Corp + * + * 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. + */ + +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 +#define POLLRDNORM 0x0040 +#define POLLRDBAND 0x0080 +#define POLLWRNORM 0x0100 +#define POLLWRBAND 0x0200 +#define POLLMSG 0x0400 + +struct pollfd { + int fd; + short events; + short revents; +}; + +#endif /* __PPC64_POLL_H */ diff -Nru a/include/asm-ppc64/posix_types.h b/include/asm-ppc64/posix_types.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/posix_types.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,126 @@ +#ifndef _PPC64_POSIX_TYPES_H +#define _PPC64_POSIX_TYPES_H + +/* + * This file is generally used by user-level software, so you need to + * be a little careful about namespace pollution etc. Also, we cannot + * assume GCC is being used. + * + * 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. + */ +#if 0 +# define DRENG_TYPES +#endif + +#ifdef DRENG_TYPES +typedef unsigned long __kernel_dev_t; +typedef unsigned long __kernel_ino_t; +typedef unsigned long __kernel_nlink_t; +#else +typedef unsigned int __kernel_dev_t; +typedef unsigned int __kernel_ino_t; +typedef unsigned int __kernel_nlink_t; +#endif +typedef unsigned int __kernel_mode_t; +typedef long __kernel_off_t; +typedef long long __kernel_loff_t; +typedef int __kernel_pid_t; +typedef int __kernel_ipc_pid_t; +typedef unsigned int __kernel_uid_t; +typedef unsigned int __kernel_gid_t; +typedef unsigned long __kernel_size_t; +typedef long __kernel_ssize_t; +typedef long __kernel_ptrdiff_t; +typedef long __kernel_time_t; +typedef long __kernel_suseconds_t; +typedef long __kernel_clock_t; +typedef int __kernel_daddr_t; +typedef char * __kernel_caddr_t; +typedef unsigned short __kernel_uid16_t; +typedef unsigned short __kernel_gid16_t; +typedef unsigned int __kernel_uid32_t; +typedef unsigned int __kernel_gid32_t; + +typedef unsigned int __kernel_old_uid_t; +typedef unsigned int __kernel_old_gid_t; + +typedef struct { + int val[2]; +} __kernel_fsid_t; + +#ifndef __GNUC__ + +#define __FD_SET(d, set) ((set)->fds_bits[__FDELT(d)] |= __FDMASK(d)) +#define __FD_CLR(d, set) ((set)->fds_bits[__FDELT(d)] &= ~__FDMASK(d)) +#define __FD_ISSET(d, set) ((set)->fds_bits[__FDELT(d)] & __FDMASK(d)) +#define __FD_ZERO(set) \ + ((void) memset ((__ptr_t) (set), 0, sizeof (__kernel_fd_set))) + +#else /* __GNUC__ */ + +#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) \ + || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0) +/* With GNU C, use inline functions instead so args are evaluated only once: */ + +#undef __FD_SET +static __inline__ void __FD_SET(unsigned long fd, __kernel_fd_set *fdsetp) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + fdsetp->fds_bits[_tmp] |= (1UL<<_rem); +} + +#undef __FD_CLR +static __inline__ void __FD_CLR(unsigned long fd, __kernel_fd_set *fdsetp) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + fdsetp->fds_bits[_tmp] &= ~(1UL<<_rem); +} + +#undef __FD_ISSET +static __inline__ int __FD_ISSET(unsigned long fd, __kernel_fd_set *p) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + return (p->fds_bits[_tmp] & (1UL<<_rem)) != 0; +} + +/* + * This will unroll the loop for the normal constant case (8 ints, + * for a 256-bit fd_set) + */ +#undef __FD_ZERO +static __inline__ void __FD_ZERO(__kernel_fd_set *p) +{ + unsigned long *tmp = (unsigned long *)p->fds_bits; + int i; + + if (__builtin_constant_p(__FDSET_LONGS)) { + switch (__FDSET_LONGS) { + case 16: + tmp[12] = 0; tmp[13] = 0; tmp[14] = 0; tmp[15] = 0; + tmp[ 8] = 0; tmp[ 9] = 0; tmp[10] = 0; tmp[11] = 0; + + case 8: + tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0; + + case 4: + tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0; + return; + } + } + i = __FDSET_LONGS; + while (i) { + i--; + *tmp = 0; + tmp++; + } +} + +#endif /* defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) */ +#endif /* __GNUC__ */ +#endif /* _PPC64_POSIX_TYPES_H */ diff -Nru a/include/asm-ppc64/ppc32.h b/include/asm-ppc64/ppc32.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/ppc32.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,232 @@ +#ifndef _PPC64_PPC32_H +#define _PPC64_PPC32_H + +#include +#include + +/* + * Data types and macros for providing 32b PowerPC support. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __KERNEL_STRICT_NAMES +#include +typedef __kernel_fsid_t __kernel_fsid_t32; +#endif + +/* Use this to get at 32-bit user passed pointers. */ +/* Things to consider: the low-level assembly stub does + srl x, 0, x for first four arguments, so if you have + pointer to something in the first four arguments, just + declare it as a pointer, not u32. On the other side, + arguments from 5th onwards should be declared as u32 + for pointers, and need AA() around each usage. + A() macro should be used for places where you e.g. + have some internal variable u32 and just want to get + rid of a compiler warning. AA() has to be used in + places where you want to convert a function argument + to 32bit pointer or when you e.g. access pt_regs + structure and want to consider 32bit registers only. + - + */ +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) \ +({ unsigned long __ret; \ + __asm__ ("clrldi %0, %0, 32" \ + : "=r" (__ret) \ + : "0" (__x)); \ + __ret; \ +}) + +/* These are here to support 32-bit syscalls on a 64-bit kernel. */ +typedef unsigned int __kernel_size_t32; +typedef int __kernel_ssize_t32; +typedef int __kernel_ptrdiff_t32; +typedef int __kernel_time_t32; +typedef int __kernel_clock_t32; +typedef int __kernel_pid_t32; +typedef unsigned short __kernel_ipc_pid_t32; +typedef unsigned int __kernel_uid_t32; +typedef unsigned int __kernel_gid_t32; +typedef unsigned int __kernel_dev_t32; +typedef unsigned int __kernel_ino_t32; +typedef unsigned int __kernel_mode_t32; +typedef unsigned int __kernel_umode_t32; +typedef short __kernel_nlink_t32; +typedef int __kernel_daddr_t32; +typedef int __kernel_off_t32; +typedef unsigned int __kernel_caddr_t32; +typedef int __kernel_loff_t32; +/* typedef __kernel_fsid_t __kernel_fsid_t32; */ + +struct statfs32 { + int f_type; + int f_bsize; + int f_blocks; + int f_bfree; + int f_bavail; + int f_files; + int f_ffree; + __kernel_fsid_t32 f_fsid; + int f_namelen; /* SunOS ignores this field. */ + int f_spare[6]; +}; + +typedef union sigval32 { + int sival_int; + unsigned int sival_ptr; +} sigval_t32; + +typedef struct siginfo32 { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[SI_PAD_SIZE]; + + /* kill() */ + struct { + __kernel_pid_t32 _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + unsigned int _timer1; + unsigned int _timer2; + } _timer; + + /* POSIX.1b signals */ + struct { + __kernel_pid_t32 _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + sigval_t32 _sigval; + } _rt; + + /* SIGCHLD */ + struct { + __kernel_pid_t32 _pid; /* which child */ + unsigned int _uid; /* sender's uid */ + int _status; /* exit code */ + __kernel_clock_t32 _utime; + __kernel_clock_t32 _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGEMT */ + struct { + u32 _addr; /* faulting insn/memory ref. */ + int _trapno; + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} siginfo_t32; + +#define __old_sigset_t32 old_sigset_t32 +#define __old_sigaction32 old_sigaction32 + +typedef unsigned int __old_sigset_t32; +struct __old_sigaction32 { + unsigned sa_handler; + __old_sigset_t32 sa_mask; + unsigned int sa_flags; + unsigned sa_restorer; /* not used by Linux/SPARC yet */ +}; + + + +#define _PPC32_NSIG 64 +#define _PPC32_NSIG_BPW 32 +#define _PPC32_NSIG_WORDS (_PPC32_NSIG / _PPC32_NSIG_BPW) + +typedef struct { + unsigned int sig[_PPC32_NSIG_WORDS]; +} sigset32_t; + +struct sigaction32 { + unsigned int sa_handler; /* Really a pointer, but need to deal + with 32 bits */ + unsigned int sa_flags; + unsigned int sa_restorer; /* Another 32 bit pointer */ + sigset32_t sa_mask; /* A 32 bit mask */ +}; + +typedef struct sigaltstack_32 { + unsigned int ss_sp; + int ss_flags; + __kernel_size_t32 ss_size; +} stack_32_t; + +struct flock32 { + short l_type; + short l_whence; + __kernel_off_t32 l_start; + __kernel_off_t32 l_len; + __kernel_pid_t32 l_pid; + short __unused; +}; + +struct stat32 { + __kernel_dev_t32 st_dev; /* 2 */ + /* __kernel_dev_t32 __pad1; */ /* 2 */ + __kernel_ino_t32 st_ino; /* 4 */ + __kernel_mode_t32 st_mode; /* 2 */ + short st_nlink; /* 2 */ + __kernel_uid_t32 st_uid; /* 2 */ + __kernel_gid_t32 st_gid; /* 2 */ + __kernel_dev_t32 st_rdev; /* 2 */ + /* __kernel_dev_t32 __pad2; */ /* 2 */ + __kernel_off_t32 st_size; /* 4 */ + __kernel_off_t32 st_blksize; /* 4 */ + __kernel_off_t32 st_blocks; /* 4 */ + __kernel_time_t32 st_atime; /* 4 */ + unsigned int __unused1; /* 4 */ + __kernel_time_t32 st_mtime; /* 4 */ + unsigned int __unused2; /* 4 */ + __kernel_time_t32 st_ctime; /* 4 */ + unsigned int __unused3; /* 4 */ + unsigned int __unused4[2]; /* 2*4 */ +}; + +struct __old_kernel_stat32 +{ + unsigned short st_dev; + unsigned short st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned int st_size; + unsigned int st_atime; + unsigned int st_mtime; + unsigned int st_ctime; +}; + +struct sigcontext32_struct { + unsigned int _unused[4]; + int signal; + unsigned int handler; + unsigned int oldmask; + u32 regs; /* 4 byte pointer to the pt_regs32 structure. */ +}; + +struct ucontext32 { + unsigned int uc_flags; + unsigned int uc_link; + stack_32_t uc_stack; + struct sigcontext32_struct uc_mcontext; + sigset_t uc_sigmask; /* mask last for extensibility */ +}; + + +#endif /* _PPC64_PPC32_H */ diff -Nru a/include/asm-ppc64/ppcdebug.h b/include/asm-ppc64/ppcdebug.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/ppcdebug.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,111 @@ +#ifndef __PPCDEBUG_H +#define __PPCDEBUG_H +/******************************************************************** + * Author: Adam Litke, IBM Corp + * (c) 2001 + * + * This file contains definitions and macros for a runtime debugging + * system for ppc64 (This should also work on 32 bit with a few + * adjustments. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + ********************************************************************/ + +#include +#include +#include + +#define PPCDBG_BITVAL(X) ((1UL)<<((unsigned long)(X))) + +/* Defined below are the bit positions of various debug flags in the + * debug_switch variable (defined in Naca.h). + * -- When adding new values, please enter them into trace names below -- + * + * Values 62 & 63 can be used to stress the hardware page table management + * code. They must be set statically, any attempt to change them dynamically + * would be a very bad idea. + */ +#define PPCDBG_MMINIT PPCDBG_BITVAL(0) +#define PPCDBG_MM PPCDBG_BITVAL(1) +#define PPCDBG_SYS32 PPCDBG_BITVAL(2) +#define PPCDBG_SYS32NI PPCDBG_BITVAL(3) +#define PPCDBG_SYS32X PPCDBG_BITVAL(4) +#define PPCDBG_SYS32M PPCDBG_BITVAL(5) +#define PPCDBG_SYS64 PPCDBG_BITVAL(6) +#define PPCDBG_SYS64NI PPCDBG_BITVAL(7) +#define PPCDBG_SYS64X PPCDBG_BITVAL(8) +#define PPCDBG_SIGNAL PPCDBG_BITVAL(9) +#define PPCDBG_SIGNALXMON PPCDBG_BITVAL(10) +#define PPCDBG_BINFMT32 PPCDBG_BITVAL(11) +#define PPCDBG_BINFMT64 PPCDBG_BITVAL(12) +#define PPCDBG_BINFMTXMON PPCDBG_BITVAL(13) +#define PPCDBG_BINFMT_32ADDR PPCDBG_BITVAL(14) +#define PPCDBG_ALIGNFIXUP PPCDBG_BITVAL(15) +#define PPCDBG_TCEINIT PPCDBG_BITVAL(16) +#define PPCDBG_TCE PPCDBG_BITVAL(17) +#define PPCDBG_PHBINIT PPCDBG_BITVAL(18) +#define PPCDBG_SMP PPCDBG_BITVAL(19) +#define PPCDBG_BOOT PPCDBG_BITVAL(20) +#define PPCDBG_BUSWALK PPCDBG_BITVAL(21) +#define PPCDBG_HTABSTRESS PPCDBG_BITVAL(62) +#define PPCDBG_HTABSIZE PPCDBG_BITVAL(63) +#define PPCDBG_NONE (0UL) +#define PPCDBG_ALL (0xffffffffUL) + +/* The default initial value for the debug switch */ +#define PPC_DEBUG_DEFAULT 0 +/* #define PPC_DEBUG_DEFAULT PPCDBG_ALL */ + +#define PPCDBG_NUM_FLAGS 64 + +#ifdef WANT_PPCDBG_TAB +/* A table of debug switch names to allow name lookup in xmon + * (and whoever else wants it. + */ +char *trace_names[PPCDBG_NUM_FLAGS] = { + /* Known debug names */ + "mminit", "mm", + "syscall32", "syscall32_ni", "syscall32x", "syscall32m", + "syscall64", "syscall64_ni", "syscall64x", + "signal", "signal_xmon", + "binfmt32", "binfmt64", "binfmt_xmon", "binfmt_32addr", + "alignfixup", "tceinit", "tce", "phb_init", + "smp", "boot", "buswalk" +}; +#else +extern char *trace_names[64]; +#endif /* WANT_PPCDBG_TAB */ + +#ifdef CONFIG_PPCDBG +/* Macro to conditionally print debug based on debug_switch */ +#define PPCDBG(...) udbg_ppcdbg(__VA_ARGS__) + +/* Macro to conditionally call a debug routine based on debug_switch */ +#define PPCDBGCALL(FLAGS,FUNCTION) ifppcdebug(FLAGS) FUNCTION + +/* Macros to test for debug states */ +#define ifppcdebug(FLAGS) if (udbg_ifdebug(FLAGS)) +#define ppcdebugset(FLAGS) (udbg_ifdebug(FLAGS)) +#define PPCDBG_BINFMT (test_thread_flag(TIF_32BIT) ? PPCDBG_BINFMT32 : PPCDBG_BINFMT64) + +#ifdef CONFIG_XMON +#define PPCDBG_ENTER_DEBUGGER() xmon(0) +#define PPCDBG_ENTER_DEBUGGER_REGS(X) xmon(X) +#endif + +#else +#define PPCDBG(...) do {;} while (0) +#define PPCDBGCALL(FLAGS,FUNCTION) do {;} while (0) +#define ifppcdebug(...) if (0) +#define ppcdebugset(FLAGS) (0) +#endif /* CONFIG_PPCDBG */ + +#ifndef PPCDBG_ENTER_DEBUGGER +#define PPCDBG_ENTER_DEBUGGER() do {;} while(0) +#endif + +#endif /*__PPCDEBUG_H */ diff -Nru a/include/asm-ppc64/proc_fs.h b/include/asm-ppc64/proc_fs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/proc_fs.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,33 @@ +#ifndef _PPC64_PROC_FS_H +#define _PPC64_PROC_FS_H +/* + * proc_fs.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Change Activity: */ +/* tgall -- merge of iSeries/iSeries_proc.h and proc_pmc.h */ +/* End Change Activity */ + +#include + +void pmc_proc_init(struct proc_dir_entry *iSeries_proc); +void proc_ppc64_init(void); + +#include + +#endif diff -Nru a/include/asm-ppc64/proc_pmc.h b/include/asm-ppc64/proc_pmc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/proc_pmc.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,33 @@ +/* + * pmc_proc.h + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _PMC_PROC_H +#define _PMC_PROC_H + +#include + +void pmc_proc_init(struct proc_dir_entry *iSeries_proc); +void proc_ppc64_init(void); + +#endif /* _PMC_PROC_H */ + diff -Nru a/include/asm-ppc64/processor.h b/include/asm-ppc64/processor.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/processor.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,734 @@ +#ifndef __ASM_PPC64_PROCESSOR_H +#define __ASM_PPC64_PROCESSOR_H + +/* + * Copyright (C) 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#ifndef __ASSEMBLY__ +#include +#include +#include +#endif +#include +#include + +/* + * Default implementation of macro that returns current + * instruction pointer ("program counter"). + */ +#define current_text_addr() ({ __label__ _l; _l: &&_l;}) + +/* Machine State Register (MSR) Fields */ +#define MSR_SF_LG 63 /* Enable 64 bit mode */ +#define MSR_ISF_LG 61 /* Interrupt 64b mode valid on 630 */ +#define MSR_HV_LG 60 /* Hypervisor state */ +#define MSR_VEC_LG 25 /* Enable AltiVec */ +#define MSR_POW_LG 18 /* Enable Power Management */ +#define MSR_WE_LG 18 /* Wait State Enable */ +#define MSR_TGPR_LG 17 /* TLB Update registers in use */ +#define MSR_CE_LG 17 /* Critical Interrupt Enable */ +#define MSR_ILE_LG 16 /* Interrupt Little Endian */ +#define MSR_EE_LG 15 /* External Interrupt Enable */ +#define MSR_PR_LG 14 /* Problem State / Privilege Level */ +#define MSR_FP_LG 13 /* Floating Point enable */ +#define MSR_ME_LG 12 /* Machine Check Enable */ +#define MSR_FE0_LG 11 /* Floating Exception mode 0 */ +#define MSR_SE_LG 10 /* Single Step */ +#define MSR_BE_LG 9 /* Branch Trace */ +#define MSR_DE_LG 9 /* Debug Exception Enable */ +#define MSR_FE1_LG 8 /* Floating Exception mode 1 */ +#define MSR_IP_LG 6 /* Exception prefix 0x000/0xFFF */ +#define MSR_IR_LG 5 /* Instruction Relocate */ +#define MSR_DR_LG 4 /* Data Relocate */ +#define MSR_PE_LG 3 /* Protection Enable */ +#define MSR_PX_LG 2 /* Protection Exclusive Mode */ +#define MSR_RI_LG 1 /* Recoverable Exception */ +#define MSR_LE_LG 0 /* Little Endian */ + +#ifdef __ASSEMBLY__ +#define MASK(X) (1<<(X)) +#else +#define MASK(X) (1UL<<(X)) +#endif + +#define MSR_SF MASK(MSR_SF_LG) /* Enable 64 bit mode */ +#define MSR_ISF MASK(MSR_ISF_LG) /* Interrupt 64b mode valid on 630 */ +#define MSR_HV MASK(MSR_HV_LG) /* Hypervisor state */ +#define MSR_VEC MASK(MSR_VEC_LG) /* Enable AltiVec */ +#define MSR_POW MASK(MSR_POW_LG) /* Enable Power Management */ +#define MSR_WE MASK(MSR_WE_LG) /* Wait State Enable */ +#define MSR_TGPR MASK(MSR_TGPR_LG)/* TLB Update registers in use */ +#define MSR_CE MASK(MSR_CE_LG) /* Critical Interrupt Enable */ +#define MSR_ILE MASK(MSR_ILE_LG) /* Interrupt Little Endian */ +#define MSR_EE MASK(MSR_EE_LG) /* External Interrupt Enable */ +#define MSR_PR MASK(MSR_PR_LG) /* Problem State / Privilege Level */ +#define MSR_FP MASK(MSR_FP_LG) /* Floating Point enable */ +#define MSR_ME MASK(MSR_ME_LG) /* Machine Check Enable */ +#define MSR_FE0 MASK(MSR_FE0_LG) /* Floating Exception mode 0 */ +#define MSR_SE MASK(MSR_SE_LG) /* Single Step */ +#define MSR_BE MASK(MSR_BE_LG) /* Branch Trace */ +#define MSR_DE MASK(MSR_DE_LG) /* Debug Exception Enable */ +#define MSR_FE1 MASK(MSR_FE1_LG) /* Floating Exception mode 1 */ +#define MSR_IP MASK(MSR_IP_LG) /* Exception prefix 0x000/0xFFF */ +#define MSR_IR MASK(MSR_IR_LG) /* Instruction Relocate */ +#define MSR_DR MASK(MSR_DR_LG) /* Data Relocate */ +#define MSR_PE MASK(MSR_PE_LG) /* Protection Enable */ +#define MSR_PX MASK(MSR_PX_LG) /* Protection Exclusive Mode */ +#define MSR_RI MASK(MSR_RI_LG) /* Recoverable Exception */ +#define MSR_LE MASK(MSR_LE_LG) /* Little Endian */ + +#define MSR_ MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_ISF +#define MSR_KERNEL MSR_ | MSR_SF | MSR_HV + +#define MSR_USER32 MSR_ | MSR_PR | MSR_EE +#define MSR_USER64 MSR_USER32 | MSR_SF + +/* Floating Point Status and Control Register (FPSCR) Fields */ + +#define FPSCR_FX 0x80000000 /* FPU exception summary */ +#define FPSCR_FEX 0x40000000 /* FPU enabled exception summary */ +#define FPSCR_VX 0x20000000 /* Invalid operation summary */ +#define FPSCR_OX 0x10000000 /* Overflow exception summary */ +#define FPSCR_UX 0x08000000 /* Underflow exception summary */ +#define FPSCR_ZX 0x04000000 /* Zero-devide exception summary */ +#define FPSCR_XX 0x02000000 /* Inexact exception summary */ +#define FPSCR_VXSNAN 0x01000000 /* Invalid op for SNaN */ +#define FPSCR_VXISI 0x00800000 /* Invalid op for Inv - Inv */ +#define FPSCR_VXIDI 0x00400000 /* Invalid op for Inv / Inv */ +#define FPSCR_VXZDZ 0x00200000 /* Invalid op for Zero / Zero */ +#define FPSCR_VXIMZ 0x00100000 /* Invalid op for Inv * Zero */ +#define FPSCR_VXVC 0x00080000 /* Invalid op for Compare */ +#define FPSCR_FR 0x00040000 /* Fraction rounded */ +#define FPSCR_FI 0x00020000 /* Fraction inexact */ +#define FPSCR_FPRF 0x0001f000 /* FPU Result Flags */ +#define FPSCR_FPCC 0x0000f000 /* FPU Condition Codes */ +#define FPSCR_VXSOFT 0x00000400 /* Invalid op for software request */ +#define FPSCR_VXSQRT 0x00000200 /* Invalid op for square root */ +#define FPSCR_VXCVI 0x00000100 /* Invalid op for integer convert */ +#define FPSCR_VE 0x00000080 /* Invalid op exception enable */ +#define FPSCR_OE 0x00000040 /* IEEE overflow exception enable */ +#define FPSCR_UE 0x00000020 /* IEEE underflow exception enable */ +#define FPSCR_ZE 0x00000010 /* IEEE zero divide exception enable */ +#define FPSCR_XE 0x00000008 /* FP inexact exception enable */ +#define FPSCR_NI 0x00000004 /* FPU non IEEE-Mode */ +#define FPSCR_RN 0x00000003 /* FPU rounding control */ + +/* Special Purpose Registers (SPRNs)*/ + +#define SPRN_CDBCR 0x3D7 /* Cache Debug Control Register */ +#define SPRN_CTR 0x009 /* Count Register */ +#define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */ +#define SPRN_DAC1 0x3F6 /* Data Address Compare 1 */ +#define SPRN_DAC2 0x3F7 /* Data Address Compare 2 */ +#define SPRN_DAR 0x013 /* Data Address Register */ +#define SPRN_DBAT0L 0x219 /* Data BAT 0 Lower Register */ +#define SPRN_DBAT0U 0x218 /* Data BAT 0 Upper Register */ +#define SPRN_DBAT1L 0x21B /* Data BAT 1 Lower Register */ +#define SPRN_DBAT1U 0x21A /* Data BAT 1 Upper Register */ +#define SPRN_DBAT2L 0x21D /* Data BAT 2 Lower Register */ +#define SPRN_DBAT2U 0x21C /* Data BAT 2 Upper Register */ +#define SPRN_DBAT3L 0x21F /* Data BAT 3 Lower Register */ +#define SPRN_DBAT3U 0x21E /* Data BAT 3 Upper Register */ +#define SPRN_DBCR 0x3F2 /* Debug Control Regsiter */ +#define DBCR_EDM 0x80000000 +#define DBCR_IDM 0x40000000 +#define DBCR_RST(x) (((x) & 0x3) << 28) +#define DBCR_RST_NONE 0 +#define DBCR_RST_CORE 1 +#define DBCR_RST_CHIP 2 +#define DBCR_RST_SYSTEM 3 +#define DBCR_IC 0x08000000 /* Instruction Completion Debug Evnt */ +#define DBCR_BT 0x04000000 /* Branch Taken Debug Event */ +#define DBCR_EDE 0x02000000 /* Exception Debug Event */ +#define DBCR_TDE 0x01000000 /* TRAP Debug Event */ +#define DBCR_FER 0x00F80000 /* First Events Remaining Mask */ +#define DBCR_FT 0x00040000 /* Freeze Timers on Debug Event */ +#define DBCR_IA1 0x00020000 /* Instr. Addr. Compare 1 Enable */ +#define DBCR_IA2 0x00010000 /* Instr. Addr. Compare 2 Enable */ +#define DBCR_D1R 0x00008000 /* Data Addr. Compare 1 Read Enable */ +#define DBCR_D1W 0x00004000 /* Data Addr. Compare 1 Write Enable */ +#define DBCR_D1S(x) (((x) & 0x3) << 12) /* Data Adrr. Compare 1 Size */ +#define DAC_BYTE 0 +#define DAC_HALF 1 +#define DAC_WORD 2 +#define DAC_QUAD 3 +#define DBCR_D2R 0x00000800 /* Data Addr. Compare 2 Read Enable */ +#define DBCR_D2W 0x00000400 /* Data Addr. Compare 2 Write Enable */ +#define DBCR_D2S(x) (((x) & 0x3) << 8) /* Data Addr. Compare 2 Size */ +#define DBCR_SBT 0x00000040 /* Second Branch Taken Debug Event */ +#define DBCR_SED 0x00000020 /* Second Exception Debug Event */ +#define DBCR_STD 0x00000010 /* Second Trap Debug Event */ +#define DBCR_SIA 0x00000008 /* Second IAC Enable */ +#define DBCR_SDA 0x00000004 /* Second DAC Enable */ +#define DBCR_JOI 0x00000002 /* JTAG Serial Outbound Int. Enable */ +#define DBCR_JII 0x00000001 /* JTAG Serial Inbound Int. Enable */ +#define SPRN_DBCR0 0x3F2 /* Debug Control Register 0 */ +#define SPRN_DBCR1 0x3BD /* Debug Control Register 1 */ +#define SPRN_DBSR 0x3F0 /* Debug Status Register */ +#define SPRN_DCCR 0x3FA /* Data Cache Cacheability Register */ +#define DCCR_NOCACHE 0 /* Noncacheable */ +#define DCCR_CACHE 1 /* Cacheable */ +#define SPRN_DCMP 0x3D1 /* Data TLB Compare Register */ +#define SPRN_DCWR 0x3BA /* Data Cache Write-thru Register */ +#define DCWR_COPY 0 /* Copy-back */ +#define DCWR_WRITE 1 /* Write-through */ +#define SPRN_DEAR 0x3D5 /* Data Error Address Register */ +#define SPRN_DEC 0x016 /* Decrement Register */ +#define SPRN_DMISS 0x3D0 /* Data TLB Miss Register */ +#define SPRN_DSISR 0x012 /* Data Storage Interrupt Status Register */ +#define SPRN_EAR 0x11A /* External Address Register */ +#define SPRN_ESR 0x3D4 /* Exception Syndrome Register */ +#define ESR_IMCP 0x80000000 /* Instr. Machine Check - Protection */ +#define ESR_IMCN 0x40000000 /* Instr. Machine Check - Non-config */ +#define ESR_IMCB 0x20000000 /* Instr. Machine Check - Bus error */ +#define ESR_IMCT 0x10000000 /* Instr. Machine Check - Timeout */ +#define ESR_PIL 0x08000000 /* Program Exception - Illegal */ +#define ESR_PPR 0x04000000 /* Program Exception - Priveleged */ +#define ESR_PTR 0x02000000 /* Program Exception - Trap */ +#define ESR_DST 0x00800000 /* Storage Exception - Data miss */ +#define ESR_DIZ 0x00400000 /* Storage Exception - Zone fault */ +#define SPRN_EVPR 0x3D6 /* Exception Vector Prefix Register */ +#define SPRN_HASH1 0x3D2 /* Primary Hash Address Register */ +#define SPRN_HASH2 0x3D3 /* Secondary Hash Address Resgister */ +#define SPRN_HID0 0x3F0 /* Hardware Implementation Register 0 */ +#define HID0_EMCP (1<<31) /* Enable Machine Check pin */ +#define HID0_EBA (1<<29) /* Enable Bus Address Parity */ +#define HID0_EBD (1<<28) /* Enable Bus Data Parity */ +#define HID0_SBCLK (1<<27) +#define HID0_EICE (1<<26) +#define HID0_ECLK (1<<25) +#define HID0_PAR (1<<24) +#define HID0_DOZE (1<<23) +#define HID0_NAP (1<<22) +#define HID0_SLEEP (1<<21) +#define HID0_DPM (1<<20) +#define HID0_ICE (1<<15) /* Instruction Cache Enable */ +#define HID0_DCE (1<<14) /* Data Cache Enable */ +#define HID0_ILOCK (1<<13) /* Instruction Cache Lock */ +#define HID0_DLOCK (1<<12) /* Data Cache Lock */ +#define HID0_ICFI (1<<11) /* Instr. Cache Flash Invalidate */ +#define HID0_DCI (1<<10) /* Data Cache Invalidate */ +#define HID0_SPD (1<<9) /* Speculative disable */ +#define HID0_SGE (1<<7) /* Store Gathering Enable */ +#define HID0_SIED (1<<7) /* Serial Instr. Execution [Disable] */ +#define HID0_BTIC (1<<5) /* Branch Target Instruction Cache Enable */ +#define HID0_ABE (1<<3) /* Address Broadcast Enable */ +#define HID0_BHTE (1<<2) /* Branch History Table Enable */ +#define HID0_BTCD (1<<1) /* Branch target cache disable */ +#define SPRN_MSRDORM 0x3F1 /* Hardware Implementation Register 1 */ +#define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */ +#define SPRN_NIADORM 0x3F3 /* Hardware Implementation Register 2 */ +#define SPRN_TSC 0x3FD /* Thread switch control */ +#define SPRN_TST 0x3FC /* Thread switch timeout */ +#define SPRN_IAC1 0x3F4 /* Instruction Address Compare 1 */ +#define SPRN_IAC2 0x3F5 /* Instruction Address Compare 2 */ +#define SPRN_IBAT0L 0x211 /* Instruction BAT 0 Lower Register */ +#define SPRN_IBAT0U 0x210 /* Instruction BAT 0 Upper Register */ +#define SPRN_IBAT1L 0x213 /* Instruction BAT 1 Lower Register */ +#define SPRN_IBAT1U 0x212 /* Instruction BAT 1 Upper Register */ +#define SPRN_IBAT2L 0x215 /* Instruction BAT 2 Lower Register */ +#define SPRN_IBAT2U 0x214 /* Instruction BAT 2 Upper Register */ +#define SPRN_IBAT3L 0x217 /* Instruction BAT 3 Lower Register */ +#define SPRN_IBAT3U 0x216 /* Instruction BAT 3 Upper Register */ +#define SPRN_ICCR 0x3FB /* Instruction Cache Cacheability Register */ +#define ICCR_NOCACHE 0 /* Noncacheable */ +#define ICCR_CACHE 1 /* Cacheable */ +#define SPRN_ICDBDR 0x3D3 /* Instruction Cache Debug Data Register */ +#define SPRN_ICMP 0x3D5 /* Instruction TLB Compare Register */ +#define SPRN_ICTC 0x3FB /* Instruction Cache Throttling Control Reg */ +#define SPRN_IMISS 0x3D4 /* Instruction TLB Miss Register */ +#define SPRN_IMMR 0x27E /* Internal Memory Map Register */ +#define SPRN_L2CR 0x3F9 /* Level 2 Cache Control Regsiter */ +#define SPRN_LR 0x008 /* Link Register */ +#define SPRN_MMCR0 0x3B8 /* Monitor Mode Control Register 0 */ +#define SPRN_MMCR1 0x3BC /* Monitor Mode Control Register 1 */ +#define SPRN_PBL1 0x3FC /* Protection Bound Lower 1 */ +#define SPRN_PBL2 0x3FE /* Protection Bound Lower 2 */ +#define SPRN_PBU1 0x3FD /* Protection Bound Upper 1 */ +#define SPRN_PBU2 0x3FF /* Protection Bound Upper 2 */ +#define SPRN_PID 0x3B1 /* Process ID */ +#define SPRN_PIR 0x3FF /* Processor Identification Register */ +#define SPRN_PIT 0x3DB /* Programmable Interval Timer */ +#define SPRN_PMC1 0x3B9 /* Performance Counter Register 1 */ +#define SPRN_PMC2 0x3BA /* Performance Counter Register 2 */ +#define SPRN_PMC3 0x3BD /* Performance Counter Register 3 */ +#define SPRN_PMC4 0x3BE /* Performance Counter Register 4 */ +#define SPRN_PVR 0x11F /* Processor Version Register */ +#define SPRN_RPA 0x3D6 /* Required Physical Address Register */ +#define SPRN_SDA 0x3BF /* Sampled Data Address Register */ +#define SPRN_SDR1 0x019 /* MMU Hash Base Register */ +#define SPRN_SGR 0x3B9 /* Storage Guarded Register */ +#define SGR_NORMAL 0 +#define SGR_GUARDED 1 +#define SPRN_SIA 0x3BB /* Sampled Instruction Address Register */ +#define SPRN_SPRG0 0x110 /* Special Purpose Register General 0 */ +#define SPRN_SPRG1 0x111 /* Special Purpose Register General 1 */ +#define SPRN_SPRG2 0x112 /* Special Purpose Register General 2 */ +#define SPRN_SPRG3 0x113 /* Special Purpose Register General 3 */ +#define SPRN_SRR0 0x01A /* Save/Restore Register 0 */ +#define SPRN_SRR1 0x01B /* Save/Restore Register 1 */ +#define SPRN_SRR2 0x3DE /* Save/Restore Register 2 */ +#define SPRN_SRR3 0x3DF /* Save/Restore Register 3 */ +#define SPRN_TBHI 0x3DC /* Time Base High */ +#define SPRN_TBHU 0x3CC /* Time Base High User-mode */ +#define SPRN_TBLO 0x3DD /* Time Base Low */ +#define SPRN_TBLU 0x3CD /* Time Base Low User-mode */ +#define SPRN_TBRL 0x10D /* Time Base Read Lower Register */ +#define SPRN_TBRU 0x10C /* Time Base Read Upper Register */ +#define SPRN_TBWL 0x11D /* Time Base Write Lower Register */ +#define SPRN_TBWU 0x11C /* Time Base Write Upper Register */ +#define SPRN_TCR 0x3DA /* Timer Control Register */ +#define TCR_WP(x) (((x)&0x3)<<30) /* WDT Period */ +#define WP_2_17 0 /* 2^17 clocks */ +#define WP_2_21 1 /* 2^21 clocks */ +#define WP_2_25 2 /* 2^25 clocks */ +#define WP_2_29 3 /* 2^29 clocks */ +#define TCR_WRC(x) (((x)&0x3)<<28) /* WDT Reset Control */ +#define WRC_NONE 0 /* No reset will occur */ +#define WRC_CORE 1 /* Core reset will occur */ +#define WRC_CHIP 2 /* Chip reset will occur */ +#define WRC_SYSTEM 3 /* System reset will occur */ +#define TCR_WIE 0x08000000 /* WDT Interrupt Enable */ +#define TCR_PIE 0x04000000 /* PIT Interrupt Enable */ +#define TCR_FP(x) (((x)&0x3)<<24) /* FIT Period */ +#define FP_2_9 0 /* 2^9 clocks */ +#define FP_2_13 1 /* 2^13 clocks */ +#define FP_2_17 2 /* 2^17 clocks */ +#define FP_2_21 3 /* 2^21 clocks */ +#define TCR_FIE 0x00800000 /* FIT Interrupt Enable */ +#define TCR_ARE 0x00400000 /* Auto Reload Enable */ +#define SPRN_THRM1 0x3FC /* Thermal Management Register 1 */ +#define THRM1_TIN (1<<0) +#define THRM1_TIV (1<<1) +#define THRM1_THRES (0x7f<<2) +#define THRM1_TID (1<<29) +#define THRM1_TIE (1<<30) +#define THRM1_V (1<<31) +#define SPRN_THRM2 0x3FD /* Thermal Management Register 2 */ +#define SPRN_THRM3 0x3FE /* Thermal Management Register 3 */ +#define THRM3_E (1<<31) +#define SPRN_TSR 0x3D8 /* Timer Status Register */ +#define TSR_ENW 0x80000000 /* Enable Next Watchdog */ +#define TSR_WIS 0x40000000 /* WDT Interrupt Status */ +#define TSR_WRS(x) (((x)&0x3)<<28) /* WDT Reset Status */ +#define WRS_NONE 0 /* No WDT reset occurred */ +#define WRS_CORE 1 /* WDT forced core reset */ +#define WRS_CHIP 2 /* WDT forced chip reset */ +#define WRS_SYSTEM 3 /* WDT forced system reset */ +#define TSR_PIS 0x08000000 /* PIT Interrupt Status */ +#define TSR_FIS 0x04000000 /* FIT Interrupt Status */ +#define SPRN_UMMCR0 0x3A8 /* User Monitor Mode Control Register 0 */ +#define SPRN_UMMCR1 0x3AC /* User Monitor Mode Control Register 0 */ +#define SPRN_UPMC1 0x3A9 /* User Performance Counter Register 1 */ +#define SPRN_UPMC2 0x3AA /* User Performance Counter Register 2 */ +#define SPRN_UPMC3 0x3AD /* User Performance Counter Register 3 */ +#define SPRN_UPMC4 0x3AE /* User Performance Counter Register 4 */ +#define SPRN_USIA 0x3AB /* User Sampled Instruction Address Register */ +#define SPRN_XER 0x001 /* Fixed Point Exception Register */ +#define SPRN_ZPR 0x3B0 /* Zone Protection Register */ + +/* Short-hand versions for a number of the above SPRNs */ + +#define CTR SPRN_CTR /* Counter Register */ +#define DAR SPRN_DAR /* Data Address Register */ +#define DABR SPRN_DABR /* Data Address Breakpoint Register */ +#define DBAT0L SPRN_DBAT0L /* Data BAT 0 Lower Register */ +#define DBAT0U SPRN_DBAT0U /* Data BAT 0 Upper Register */ +#define DBAT1L SPRN_DBAT1L /* Data BAT 1 Lower Register */ +#define DBAT1U SPRN_DBAT1U /* Data BAT 1 Upper Register */ +#define DBAT2L SPRN_DBAT2L /* Data BAT 2 Lower Register */ +#define DBAT2U SPRN_DBAT2U /* Data BAT 2 Upper Register */ +#define DBAT3L SPRN_DBAT3L /* Data BAT 3 Lower Register */ +#define DBAT3U SPRN_DBAT3U /* Data BAT 3 Upper Register */ +#define DCMP SPRN_DCMP /* Data TLB Compare Register */ +#define DEC SPRN_DEC /* Decrement Register */ +#define DMISS SPRN_DMISS /* Data TLB Miss Register */ +#define DSISR SPRN_DSISR /* Data Storage Interrupt Status Register */ +#define EAR SPRN_EAR /* External Address Register */ +#define HASH1 SPRN_HASH1 /* Primary Hash Address Register */ +#define HASH2 SPRN_HASH2 /* Secondary Hash Address Register */ +#define HID0 SPRN_HID0 /* Hardware Implementation Register 0 */ +#define MSRDORM SPRN_MSRDORM /* MSR Dormant Register */ +#define NIADORM SPRN_NIADORM /* NIA Dormant Register */ +#define TSC SPRN_TSC /* Thread switch control */ +#define TST SPRN_TST /* Thread switch timeout */ +#define IABR SPRN_IABR /* Instruction Address Breakpoint Register */ +#define IBAT0L SPRN_IBAT0L /* Instruction BAT 0 Lower Register */ +#define IBAT0U SPRN_IBAT0U /* Instruction BAT 0 Upper Register */ +#define IBAT1L SPRN_IBAT1L /* Instruction BAT 1 Lower Register */ +#define IBAT1U SPRN_IBAT1U /* Instruction BAT 1 Upper Register */ +#define IBAT2L SPRN_IBAT2L /* Instruction BAT 2 Lower Register */ +#define IBAT2U SPRN_IBAT2U /* Instruction BAT 2 Upper Register */ +#define IBAT3L SPRN_IBAT3L /* Instruction BAT 3 Lower Register */ +#define IBAT3U SPRN_IBAT3U /* Instruction BAT 3 Upper Register */ +#define ICMP SPRN_ICMP /* Instruction TLB Compare Register */ +#define IMISS SPRN_IMISS /* Instruction TLB Miss Register */ +#define IMMR SPRN_IMMR /* PPC 860/821 Internal Memory Map Register */ +#define L2CR SPRN_L2CR /* PPC 750 L2 control register */ +#define LR SPRN_LR +#define PVR SPRN_PVR /* Processor Version */ +#define PIR SPRN_PIR /* Processor ID */ +#define RPA SPRN_RPA /* Required Physical Address Register */ +#define SDR1 SPRN_SDR1 /* MMU hash base register */ +#define SPR0 SPRN_SPRG0 /* Supervisor Private Registers */ +#define SPR1 SPRN_SPRG1 +#define SPR2 SPRN_SPRG2 +#define SPR3 SPRN_SPRG3 +#define SPRG0 SPRN_SPRG0 +#define SPRG1 SPRN_SPRG1 +#define SPRG2 SPRN_SPRG2 +#define SPRG3 SPRN_SPRG3 +#define SRR0 SPRN_SRR0 /* Save and Restore Register 0 */ +#define SRR1 SPRN_SRR1 /* Save and Restore Register 1 */ +#define TBRL SPRN_TBRL /* Time Base Read Lower Register */ +#define TBRU SPRN_TBRU /* Time Base Read Upper Register */ +#define TBWL SPRN_TBWL /* Time Base Write Lower Register */ +#define TBWU SPRN_TBWU /* Time Base Write Upper Register */ +#define ICTC 1019 +#define THRM1 SPRN_THRM1 /* Thermal Management Register 1 */ +#define THRM2 SPRN_THRM2 /* Thermal Management Register 2 */ +#define THRM3 SPRN_THRM3 /* Thermal Management Register 3 */ +#define XER SPRN_XER + + +/* Device Control Registers */ + +#define DCRN_BEAR 0x090 /* Bus Error Address Register */ +#define DCRN_BESR 0x091 /* Bus Error Syndrome Register */ +#define BESR_DSES 0x80000000 /* Data-Side Error Status */ +#define BESR_DMES 0x40000000 /* DMA Error Status */ +#define BESR_RWS 0x20000000 /* Read/Write Status */ +#define BESR_ETMASK 0x1C000000 /* Error Type */ +#define ET_PROT 0 +#define ET_PARITY 1 +#define ET_NCFG 2 +#define ET_BUSERR 4 +#define ET_BUSTO 6 +#define DCRN_DMACC0 0x0C4 /* DMA Chained Count Register 0 */ +#define DCRN_DMACC1 0x0CC /* DMA Chained Count Register 1 */ +#define DCRN_DMACC2 0x0D4 /* DMA Chained Count Register 2 */ +#define DCRN_DMACC3 0x0DC /* DMA Chained Count Register 3 */ +#define DCRN_DMACR0 0x0C0 /* DMA Channel Control Register 0 */ +#define DCRN_DMACR1 0x0C8 /* DMA Channel Control Register 1 */ +#define DCRN_DMACR2 0x0D0 /* DMA Channel Control Register 2 */ +#define DCRN_DMACR3 0x0D8 /* DMA Channel Control Register 3 */ +#define DCRN_DMACT0 0x0C1 /* DMA Count Register 0 */ +#define DCRN_DMACT1 0x0C9 /* DMA Count Register 1 */ +#define DCRN_DMACT2 0x0D1 /* DMA Count Register 2 */ +#define DCRN_DMACT3 0x0D9 /* DMA Count Register 3 */ +#define DCRN_DMADA0 0x0C2 /* DMA Destination Address Register 0 */ +#define DCRN_DMADA1 0x0CA /* DMA Destination Address Register 1 */ +#define DCRN_DMADA2 0x0D2 /* DMA Destination Address Register 2 */ +#define DCRN_DMADA3 0x0DA /* DMA Destination Address Register 3 */ +#define DCRN_DMASA0 0x0C3 /* DMA Source Address Register 0 */ +#define DCRN_DMASA1 0x0CB /* DMA Source Address Register 1 */ +#define DCRN_DMASA2 0x0D3 /* DMA Source Address Register 2 */ +#define DCRN_DMASA3 0x0DB /* DMA Source Address Register 3 */ +#define DCRN_DMASR 0x0E0 /* DMA Status Register */ +#define DCRN_EXIER 0x042 /* External Interrupt Enable Register */ +#define EXIER_CIE 0x80000000 /* Critical Interrupt Enable */ +#define EXIER_SRIE 0x08000000 /* Serial Port Rx Int. Enable */ +#define EXIER_STIE 0x04000000 /* Serial Port Tx Int. Enable */ +#define EXIER_JRIE 0x02000000 /* JTAG Serial Port Rx Int. Enable */ +#define EXIER_JTIE 0x01000000 /* JTAG Serial Port Tx Int. Enable */ +#define EXIER_D0IE 0x00800000 /* DMA Channel 0 Interrupt Enable */ +#define EXIER_D1IE 0x00400000 /* DMA Channel 1 Interrupt Enable */ +#define EXIER_D2IE 0x00200000 /* DMA Channel 2 Interrupt Enable */ +#define EXIER_D3IE 0x00100000 /* DMA Channel 3 Interrupt Enable */ +#define EXIER_E0IE 0x00000010 /* External Interrupt 0 Enable */ +#define EXIER_E1IE 0x00000008 /* External Interrupt 1 Enable */ +#define EXIER_E2IE 0x00000004 /* External Interrupt 2 Enable */ +#define EXIER_E3IE 0x00000002 /* External Interrupt 3 Enable */ +#define EXIER_E4IE 0x00000001 /* External Interrupt 4 Enable */ +#define DCRN_EXISR 0x040 /* External Interrupt Status Register */ +#define DCRN_IOCR 0x0A0 /* Input/Output Configuration Register */ +#define IOCR_E0TE 0x80000000 +#define IOCR_E0LP 0x40000000 +#define IOCR_E1TE 0x20000000 +#define IOCR_E1LP 0x10000000 +#define IOCR_E2TE 0x08000000 +#define IOCR_E2LP 0x04000000 +#define IOCR_E3TE 0x02000000 +#define IOCR_E3LP 0x01000000 +#define IOCR_E4TE 0x00800000 +#define IOCR_E4LP 0x00400000 +#define IOCR_EDT 0x00080000 +#define IOCR_SOR 0x00040000 +#define IOCR_EDO 0x00008000 +#define IOCR_2XC 0x00004000 +#define IOCR_ATC 0x00002000 +#define IOCR_SPD 0x00001000 +#define IOCR_BEM 0x00000800 +#define IOCR_PTD 0x00000400 +#define IOCR_ARE 0x00000080 +#define IOCR_DRC 0x00000020 +#define IOCR_RDM(x) (((x) & 0x3) << 3) +#define IOCR_TCS 0x00000004 +#define IOCR_SCS 0x00000002 +#define IOCR_SPC 0x00000001 + + +/* Processor Version Register */ + +/* Processor Version Register (PVR) field extraction */ + +#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */ +#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */ + +/* Processor Version Numbers */ +#define PV_PULSAR 0x0034 +#define PV_POWER4 0x0035 +#define PV_ICESTAR 0x0036 +#define PV_SSTAR 0x0037 +#define PV_630 0x0040 +#define PV_630p 0x0041 + +/* Platforms supported by PPC64. _machine is actually a set of flags */ +#define _MACH_pSeriesHW 0x00010000 +#define _MACH_iSeriesHW 0x00020000 +#define _MACH_LPAR 0x00000001 + +#define _MACH_unknown 0x00000000 +#define _MACH_pSeries (_MACH_pSeriesHW) +#define _MACH_pSeriesLP (_MACH_pSeriesHW | _MACH_LPAR) +#define _MACH_iSeries (_MACH_iSeriesHW | _MACH_LPAR) + +/* Compat defines for drivers */ +#define _MACH_Pmac 0xf0000000 /* bogus value */ + +/* + * List of interrupt controllers. + */ +#define IC_INVALID 0 +#define IC_OPEN_PIC 1 +#define IC_PPC_XIC 2 + +#define XGLUE(a,b) a##b +#define GLUE(a,b) XGLUE(a,b) + +/* + * Begining of traceback info work for asm functions. + */ +#define TB_ASM 0x000C000000000000 +#define TB_GLOBALLINK 0x0000800000000000 +#define TB_IS_EPROL 0x0000400000000000 +#define TB_HAS_TBOFF 0x0000200000000000 +#define TB_INT_PROC 0x0000100000000000 +#define TB_HAS_CTL 0x0000080000000000 +#define TB_TOCLESS 0x0000040000000000 +#define TB_FP_PRESENT 0x0000020000000000 +#define TB_LOG_ABORT 0x0000010000000000 +#define TB_INT_HNDL 0x0000008000000000 +#define TB_NAME_PRESENT 0x0000004000000000 +#define TB_SAVES_CR 0x0000000200000000 +#define TB_SAVES_LR 0x0000000100000000 +#define TB_STORES_BC 0x0000000080000000 +#define TB_PARMINFO 0x000000000000FFFF +#define TB_DEFAULT TB_ASM | TB_HAS_TBOFF | TB_NAME_PRESENT + +#ifdef __ASSEMBLY__ + +#define _GLOBAL(name) \ + .section ".text"; \ + .align 2 ; \ + .globl name; \ + .globl GLUE(.,name); \ + .section ".opd","aw"; \ +name: \ + .quad GLUE(.,name); \ + .quad .TOC.@tocbase; \ + .quad 0; \ + .previous; \ + .type GLUE(.,name),@function; \ +GLUE(.,name): + +#define _STATIC(name) \ + .section ".text"; \ + .align 2 ; \ + .section ".opd","aw"; \ +name: \ + .quad GLUE(.,name); \ + .quad .TOC.@tocbase; \ + .quad 0; \ + .previous; \ + .type GLUE(.,name),@function; \ +GLUE(.,name): + +#define _TRACEBACK(NAME) \ +GLUE(.LT,NAME): ;\ + .long 0 ;\ + .llong TB_DEFAULT ;\ + .long GLUE(.LT,NAME)-GLUE(.,NAME) ;\ + .short GLUE(GLUE(.LT,NAME),_procname_end)-GLUE(GLUE(.LT,NAME),_procname_start) ;\ +GLUE(GLUE(.LT,NAME),_procname_start): ;\ + .ascii __stringify(NAME) ;\ +GLUE(GLUE(.LT,NAME),_procname_end): + +#endif /* __ASSEMBLY__ */ + + +/* Macros for setting and retrieving special purpose registers */ + +#define mfmsr() ({unsigned long rval; \ + asm volatile("mfmsr %0" : "=r" (rval)); rval;}) + +#define mtmsrd(v) asm volatile("mtmsrd %0" : : "r" (v)) + +#define mfspr(rn) ({unsigned long rval; \ + asm volatile("mfspr %0," __stringify(rn) \ + : "=r" (rval)); rval;}) +#define mtspr(rn, v) asm volatile("mtspr " __stringify(rn) ",%0" : : "r" (v)) + +#define mftb() ({unsigned long rval; \ + asm volatile("mftb %0" : "=r" (rval)); rval;}) + +/* iSeries CTRL register (for runlatch) */ + +#define CTRLT 0x098 +#define CTRLF 0x088 +#define RUNLATCH 0x0001 + +/* Macros for adjusting thread priority (hardware multi-threading) */ +#define HMT_low() asm volatile("or 1,1,1") +#define HMT_medium() asm volatile("or 2,2,2") +#define HMT_high() asm volatile("or 3,3,3") + +/* Size of an exception stack frame contained in the paca. */ +#define EXC_FRAME_SIZE 64 + +#define mfasr() ({unsigned long rval; \ + asm volatile("mfasr %0" : "=r" (rval)); rval;}) + +#ifndef __ASSEMBLY__ +extern int _machine; +extern int have_of; + +struct task_struct; +void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp); +void release_thread(struct task_struct *); + +/* + * Create a new kernel thread. + */ +extern long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); + +/* + * Bus types + */ +#define EISA_bus 0 +#define EISA_bus__is_a_macro /* for versions in ksyms.c */ +#define MCA_bus 0 +#define MCA_bus__is_a_macro /* for versions in ksyms.c */ + +/* Lazy FPU handling on uni-processor */ +extern struct task_struct *last_task_used_math; + + +#ifdef __KERNEL__ +/* 64-bit user address space is 41-bits (2TBs user VM) */ +#define TASK_SIZE_USER64 (0x0000020000000000UL) + +/* + * 32-bit user address space is 4GB - 1 page + * (this 1 page is needed so referencing of 0xFFFFFFFF generates EFAULT + */ +#define TASK_SIZE_USER32 (0x0000000100000000UL - (1*PAGE_SIZE)) + +#define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \ + TASK_SIZE_USER32 : TASK_SIZE_USER64) +#endif /* __KERNEL__ */ + + +/* This decides where the kernel will search for a free chunk of vm + * space during mmap's. + */ +#define TASK_UNMAPPED_BASE_USER32 (STACK_TOP_USER32 / 4) +#define TASK_UNMAPPED_BASE_USER64 (STACK_TOP_USER64 / 4) + +#define TASK_UNMAPPED_BASE ((test_thread_flag(TIF_32BIT)||(ppcdebugset(PPCDBG_BINFMT_32ADDR))) ? \ + TASK_UNMAPPED_BASE_USER32 : TASK_UNMAPPED_BASE_USER64 ) + +typedef struct { + unsigned long seg; +} mm_segment_t; + +struct thread_struct { + unsigned long ksp; /* Kernel stack pointer */ + struct pt_regs *regs; /* Pointer to saved register state */ + mm_segment_t fs; /* for get_fs() validation */ + void *pgdir; /* root of page-table tree */ + signed long last_syscall; + double fpr[32]; /* Complete floating point set */ + unsigned long fpscr_pad; /* fpr ... fpscr must be contiguous */ + unsigned long fpscr; /* Floating point status */ +}; + +#define INIT_SP (sizeof(init_stack) + (unsigned long) &init_stack) + +#define INIT_THREAD { \ + INIT_SP, /* ksp */ \ + (struct pt_regs *)INIT_SP - 1, /* regs */ \ + KERNEL_DS, /*fs*/ \ + swapper_pg_dir, /* pgdir */ \ + 0, /* last_syscall */ \ + {0}, 0, 0 \ +} + +/* + * Note: the vm_start and vm_end fields here should *not* + * be in kernel space. (Could vm_end == vm_start perhaps?) + */ +#define IOREMAP_MMAP { &ioremap_mm, 0, 0x1000, NULL, \ + PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, \ + 1, NULL, NULL } + +extern struct mm_struct ioremap_mm; + +/* + * Return saved PC of a blocked thread. For now, this is the "user" PC + */ +static inline unsigned long thread_saved_pc(struct thread_struct *t) +{ + return (t->regs) ? t->regs->nip : 0; +} + +#define copy_segments(tsk, mm) do { } while (0) +#define release_segments(mm) do { } while (0) +#define forget_segments() do { } while (0) + +unsigned long get_wchan(struct task_struct *p); + +#define KSTK_EIP(tsk) ((tsk)->thread.regs? (tsk)->thread.regs->nip: 0) +#define KSTK_ESP(tsk) ((tsk)->thread.regs? (tsk)->thread.regs->gpr[1]: 0) + +#define cpu_relax() do { } while (0) + +/* + * Prefetch macros. + */ +#define ARCH_HAS_PREFETCH +#define ARCH_HAS_PREFETCHW +#define ARCH_HAS_SPINLOCK_PREFETCH + +extern inline void prefetch(const void *x) +{ + __asm__ __volatile__ ("dcbt 0,%0" : : "r" (x)); +} + +extern inline void prefetchw(const void *x) +{ + __asm__ __volatile__ ("dcbtst 0,%0" : : "r" (x)); +} + +#define spin_lock_prefetch(x) prefetchw(x) + +#endif /* ASSEMBLY */ + +#endif /* __ASM_PPC64_PROCESSOR_H */ diff -Nru a/include/asm-ppc64/prom.h b/include/asm-ppc64/prom.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/prom.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,202 @@ +#ifndef _PPC64_PROM_H +#define _PPC64_PROM_H + +/* + * Definitions for talking to the Open Firmware PROM on + * Power Macintosh computers. + * + * Copyright (C) 1996 Paul Mackerras. + * + * Updates for PPC64 by Peter Bergner & David Engebretsen, IBM Corp. + * + * 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. + */ + +#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) - offset)) +#define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) +#define RELOC(x) (*PTRRELOC(&(x))) + +#define LONG_LSW(X) (((unsigned long)X) & 0xffffffff) +#define LONG_MSW(X) (((unsigned long)X) >> 32) + +typedef u32 phandle; +typedef void *ihandle; +typedef u32 phandle32; +typedef u32 ihandle32; + +extern char *prom_display_paths[]; +extern unsigned int prom_num_displays; + +struct address_range { + unsigned long space; + unsigned long address; + unsigned long size; +}; + +struct interrupt_info { + int line; + int sense; /* +ve/-ve logic, edge or level, etc. */ +}; + +struct pci_address { + u32 a_hi; + u32 a_mid; + u32 a_lo; +}; + +struct pci_range32 { + struct pci_address child_addr; + unsigned int parent_addr; + unsigned long size; +}; + +struct pci_range64 { + struct pci_address child_addr; + unsigned long parent_addr; + unsigned long size; +}; + +union pci_range { + struct { + struct pci_address addr; + u32 phys; + u32 size_hi; + } pci32; + struct { + struct pci_address addr; + u32 phys_hi; + u32 phys_lo; + u32 size_hi; + u32 size_lo; + } pci64; +}; + +struct _of_tce_table { + phandle node; + unsigned long base; + unsigned long size; +}; + +struct reg_property { + unsigned long address; + unsigned long size; +}; + +struct reg_property32 { + unsigned int address; + unsigned int size; +}; + +struct reg_property64 { + unsigned long address; + unsigned long size; +}; + +struct translation_property { + unsigned long virt; + unsigned long size; + unsigned long phys; + unsigned int flags; +}; + +struct property { + char *name; + int length; + unsigned char *value; + struct property *next; +}; + +/* NOTE: the device_node contains PCI specific info for pci devices. + * This perhaps could be hung off the device_node with another struct, + * but for now it is directly in the node. The phb ptr is a good + * indication of a real PCI node. Other nodes leave these fields zeroed. + */ +struct pci_controller; +struct TceTable; +struct device_node { + char *name; + char *type; + phandle node; + int n_addrs; + struct address_range *addrs; + int n_intrs; + struct interrupt_info *intrs; + char *full_name; + int busno; /* for pci devices */ + int devfn; /* for pci devices */ + struct pci_controller *phb; /* for pci devices */ + int status; /* current status of device */ + struct TceTable *tce_table; /* for phb's or bridges */ +#define DN_STATUS_BIST_FAILED (1<<0) + struct property *properties; + struct device_node *parent; + struct device_node *child; + struct device_node *sibling; + struct device_node *next; /* next device of same type */ + struct device_node *allnext; /* next in list of all nodes */ +}; + +typedef u32 prom_arg_t; + +struct prom_args { + u32 service; + u32 nargs; + u32 nret; + prom_arg_t args[10]; + prom_arg_t *rets; /* Pointer to return values in args[16]. */ +}; + +typedef struct { + u32 printf; /* void (*printf)(char *, ...); */ + u32 memdump; /* void (*memdump)(unsigned char *, unsigned long); */ + u32 dummy; /* void (*dummy)(void); */ +} yaboot_debug_t; + +struct prom_t { + unsigned long entry; + ihandle chosen; + int cpu; + ihandle stdout; + ihandle disp_node; + struct prom_args args; + unsigned long version; + unsigned long encode_phys_size; + struct bi_record *bi_recs; +#ifdef DEBUG_YABOOT + yaboot_debug_t *yaboot; +#endif +}; + +extern struct prom_t prom; + +/* Prototypes */ +extern void abort(void); +extern unsigned long prom_init(unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, yaboot_debug_t *); +extern void prom_print(const char *msg); +extern void relocate_nodes(void); +extern void finish_device_tree(void); +extern struct device_node *find_devices(const char *name); +extern struct device_node *find_type_devices(const char *type); +extern struct device_node *find_path_device(const char *path); +extern struct device_node *find_compatible_devices(const char *type, + const char *compat); +extern struct device_node *find_pci_device_OFnode(unsigned char bus, + unsigned char dev_fn); +extern struct device_node *find_all_nodes(void); +extern int device_is_compatible(struct device_node *device, const char *); +extern int machine_is_compatible(const char *compat); +extern unsigned char *get_property(struct device_node *node, const char *name, + int *lenp); +extern void print_properties(struct device_node *node); +extern int prom_n_addr_cells(struct device_node* np); +extern int prom_n_size_cells(struct device_node* np); +extern void prom_get_irq_senses(unsigned char *senses, int off, int max); +extern void prom_drawstring(const char *c); +extern void prom_drawhex(unsigned long v); +extern void prom_drawchar(char c); + +#endif /* _PPC64_PROM_H */ diff -Nru a/include/asm-ppc64/ptrace.h b/include/asm-ppc64/ptrace.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/ptrace.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,144 @@ +#ifndef _PPC64_PTRACE_H +#define _PPC64_PTRACE_H + +/* + * Copyright (C) 2001 PPC64 Team, IBM Corp + * + * This struct defines the way the registers are stored on the + * kernel stack during a system call or other kernel entry. + * + * this should only contain volatile regs + * since we can keep non-volatile in the thread_struct + * should set this up when only volatiles are saved + * by intr code. + * + * Since this is going on the stack, *CARE MUST BE TAKEN* to insure + * that the overall structure is a multiple of 16 bytes in length. + * + * Note that the offsets of the fields in this struct correspond with + * the PT_* values below. This simplifies arch/ppc/kernel/ptrace.c. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __ASSEMBLY__ +#define PPC_REG unsigned long +struct pt_regs { + PPC_REG gpr[32]; + PPC_REG nip; + PPC_REG msr; + PPC_REG orig_gpr3; /* Used for restarting system calls */ + PPC_REG ctr; + PPC_REG link; + PPC_REG xer; + PPC_REG ccr; + PPC_REG softe; /* Soft enabled/disabled */ + PPC_REG trap; /* Reason for being here */ + PPC_REG dar; /* Fault registers */ + PPC_REG dsisr; + PPC_REG result; /* Result of a system call */ +}; + +#define PPC_REG_32 unsigned int +struct pt_regs32 { + PPC_REG_32 gpr[32]; + PPC_REG_32 nip; + PPC_REG_32 msr; + PPC_REG_32 orig_gpr3; /* Used for restarting system calls */ + PPC_REG_32 ctr; + PPC_REG_32 link; + PPC_REG_32 xer; + PPC_REG_32 ccr; + PPC_REG_32 mq; /* 601 only (not used at present) */ + /* Used on APUS to hold IPL value. */ + PPC_REG_32 trap; /* Reason for being here */ + PPC_REG_32 dar; /* Fault registers */ + PPC_REG_32 dsisr; + PPC_REG_32 result; /* Result of a system call */ +}; + +#endif + +#define STACK_FRAME_OVERHEAD 112 /* size of minimum stack frame */ + +/* Size of stack frame allocated when calling signal handler. */ +/* FIXME: What should this be on 64-bit kernel (64 for 32-bit) */ +#define __SIGNAL_FRAMESIZE 64 +#define __SIGNAL_FRAMESIZE32 64 + +#define instruction_pointer(regs) ((regs)->nip) +#define user_mode(regs) ((((regs)->msr) >> MSR_PR_LG) & 0x1) + +/* + * Offsets used by 'ptrace' system call interface. + */ +#define PT_R0 0 +#define PT_R1 1 +#define PT_R2 2 +#define PT_R3 3 +#define PT_R4 4 +#define PT_R5 5 +#define PT_R6 6 +#define PT_R7 7 +#define PT_R8 8 +#define PT_R9 9 +#define PT_R10 10 +#define PT_R11 11 +#define PT_R12 12 +#define PT_R13 13 +#define PT_R14 14 +#define PT_R15 15 +#define PT_R16 16 +#define PT_R17 17 +#define PT_R18 18 +#define PT_R19 19 +#define PT_R20 20 +#define PT_R21 21 +#define PT_R22 22 +#define PT_R23 23 +#define PT_R24 24 +#define PT_R25 25 +#define PT_R26 26 +#define PT_R27 27 +#define PT_R28 28 +#define PT_R29 29 +#define PT_R30 30 +#define PT_R31 31 + +#define PT_NIP 32 +#define PT_MSR 33 +#ifdef __KERNEL__ +#define PT_ORIG_R3 34 +#endif +#define PT_CTR 35 +#define PT_LNK 36 +#define PT_XER 37 +#define PT_CCR 38 +#define PT_SOFTE 39 +#define PT_RESULT 43 + +#define PT_FPR0 48 +#ifdef __KERNEL__ +#define PT_FPSCR (PT_FPR0 + 32 + 1) /* each FP reg occupies 1 slot in this space */ +#define PT_FPSCR32 (PT_FPR0 + 2*32 + 1) /* To the 32-bit user - each FP reg occupies 2 slots in this space */ +#else +#define PT_FPSCR (PT_FPR0 + 2*32 + 1) /* each FP reg occupies 2 slots in this space -- Fix when 64-bit apps. */ +#endif + +/* Additional PTRACE requests implemented on PowerPC. */ +#define PPC_PTRACE_GETREGS 0x99 /* Get GPRs 0 - 31 */ +#define PPC_PTRACE_SETREGS 0x98 /* Set GPRs 0 - 31 */ +#define PPC_PTRACE_GETFPREGS 0x97 /* Get FPRs 0 - 31 */ +#define PPC_PTRACE_SETFPREGS 0x96 /* Set FPRs 0 - 31 */ +#define PPC_PTRACE_PEEKTEXT_3264 0x95 /* Read word at location ADDR on a 64-bit process from a 32-bit process. */ +#define PPC_PTRACE_PEEKDATA_3264 0x94 /* Read word at location ADDR on a 64-bit process from a 32-bit process. */ +#define PPC_PTRACE_POKETEXT_3264 0x93 /* Write word at location ADDR on a 64-bit process from a 32-bit process. */ +#define PPC_PTRACE_POKEDATA_3264 0x92 /* Write word at location ADDR on a 64-bit process from a 32-bit process. */ +#define PPC_PTRACE_PEEKUSR_3264 0x91 /* Read a register (specified by ADDR) out of the "user area" on a 64-bit process from a 32-bit process. */ +#define PPC_PTRACE_POKEUSR_3264 0x90 /* Write DATA into location ADDR within the "user area" on a 64-bit process from a 32-bit process. */ + + +#endif /* _PPC64_PTRACE_H */ diff -Nru a/include/asm-ppc64/resource.h b/include/asm-ppc64/resource.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/resource.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,53 @@ +#ifndef _PPC64_RESOURCE_H +#define _PPC64_RESOURCE_H + +/* + * Copyright (C) 2001 PPC 64 Team, IBM Corp + * + * 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. + */ + +#define RLIMIT_CPU 0 /* CPU time in ms */ +#define RLIMIT_FSIZE 1 /* Maximum filesize */ +#define RLIMIT_DATA 2 /* max data size */ +#define RLIMIT_STACK 3 /* max stack size */ +#define RLIMIT_CORE 4 /* max core file size */ +#define RLIMIT_RSS 5 /* max resident set size */ +#define RLIMIT_NPROC 6 /* max number of processes */ +#define RLIMIT_NOFILE 7 /* max number of open files */ +#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ +#define RLIMIT_AS 9 /* address space limit(?) */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ + +#define RLIM_NLIMITS 11 + +#ifdef __KERNEL__ + +/* + * SuS says limits have to be unsigned. + * Which makes a ton more sense anyway. + */ +#define RLIM_INFINITY (~0UL) + + +#define INIT_RLIMITS \ +{ \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { _STK_LIM, RLIM_INFINITY }, \ + { 0, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { 0, 0 }, \ + { INR_OPEN, INR_OPEN }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ +} + +#endif /* __KERNEL__ */ + +#endif /* _PPC64_RESOURCE_H */ diff -Nru a/include/asm-ppc64/rtas.h b/include/asm-ppc64/rtas.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/rtas.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,141 @@ +#ifndef _PPC64_RTAS_H +#define _PPC64_RTAS_H + +#include + +/* + * Definitions for talking to the RTAS on CHRP machines. + * + * Copyright (C) 2001 Peter Bergner + * Copyright (C) 2001 PPC 64 Team, IBM Corp + * + * 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. + */ + +#define RTAS_UNKNOWN_SERVICE (-1) +/* + * In general to call RTAS use rtas_token("string") to lookup + * an RTAS token for the given string (e.g. "event-scan"). + * To actually perform the call use + * ret = rtas_call(token, n_in, n_out, ...) + * Where n_in is the number of input parameters and + * n_out is the number of output parameters + * + * If the "string" is invalid on this system, RTAS_UNKOWN_SERVICE + * will be returned as a token. rtas_call() does look for this + * token and error out gracefully so rtas_call(rtas_token("str"), ...) + * may be safely used for one-shot calls to RTAS. + * + */ + +typedef u32 rtas_arg_t; + +struct rtas_args { + u32 token; + u32 nargs; + u32 nret; + rtas_arg_t args[16]; + spinlock_t lock; + rtas_arg_t *rets; /* Pointer to return values in args[]. */ +}; + +struct rtas_t { + unsigned long entry; /* physical address pointer */ + unsigned long base; /* physical address pointer */ + unsigned long size; + spinlock_t lock; + + struct device_node *dev; /* virtual address pointer */ +}; + +/* Event classes */ +#define INTERNAL_ERROR 0x80000000 /* set bit 0 */ +#define EPOW_WARNING 0x40000000 /* set bit 1 */ +#define POWERMGM_EVENTS 0x20000000 /* set bit 2 */ +#define HOTPLUG_EVENTS 0x10000000 /* set bit 3 */ +#define EVENT_SCAN_ALL_EVENTS 0xf0000000 + +/* event-scan returns */ +#define SEVERITY_FATAL 0x5 +#define SEVERITY_ERROR 0x4 +#define SEVERITY_ERROR_SYNC 0x3 +#define SEVERITY_WARNING 0x2 +#define SEVERITY_EVENT 0x1 +#define SEVERITY_NO_ERROR 0x0 +#define DISP_FULLY_RECOVERED 0x0 +#define DISP_LIMITED_RECOVERY 0x1 +#define DISP_NOT_RECOVERED 0x2 +#define PART_PRESENT 0x0 +#define PART_NOT_PRESENT 0x1 +#define INITIATOR_UNKNOWN 0x0 +#define INITIATOR_CPU 0x1 +#define INITIATOR_PCI 0x2 +#define INITIATOR_ISA 0x3 +#define INITIATOR_MEMORY 0x4 +#define INITIATOR_POWERMGM 0x5 +#define TARGET_UNKNOWN 0x0 +#define TARGET_CPU 0x1 +#define TARGET_PCI 0x2 +#define TARGET_ISA 0x3 +#define TARGET_MEMORY 0x4 +#define TARGET_POWERMGM 0x5 +#define TYPE_RETRY 0x01 +#define TYPE_TCE_ERR 0x02 +#define TYPE_INTERN_DEV_FAIL 0x03 +#define TYPE_TIMEOUT 0x04 +#define TYPE_DATA_PARITY 0x05 +#define TYPE_ADDR_PARITY 0x06 +#define TYPE_CACHE_PARITY 0x07 +#define TYPE_ADDR_INVALID 0x08 +#define TYPE_ECC_UNCORR 0x09 +#define TYPE_ECC_CORR 0x0a +#define TYPE_EPOW 0x40 +/* I don't add PowerMGM events right now, this is a different topic */ +#define TYPE_PMGM_POWER_SW_ON 0x60 +#define TYPE_PMGM_POWER_SW_OFF 0x61 +#define TYPE_PMGM_LID_OPEN 0x62 +#define TYPE_PMGM_LID_CLOSE 0x63 +#define TYPE_PMGM_SLEEP_BTN 0x64 +#define TYPE_PMGM_WAKE_BTN 0x65 +#define TYPE_PMGM_BATTERY_WARN 0x66 +#define TYPE_PMGM_BATTERY_CRIT 0x67 +#define TYPE_PMGM_SWITCH_TO_BAT 0x68 +#define TYPE_PMGM_SWITCH_TO_AC 0x69 +#define TYPE_PMGM_KBD_OR_MOUSE 0x6a +#define TYPE_PMGM_ENCLOS_OPEN 0x6b +#define TYPE_PMGM_ENCLOS_CLOSED 0x6c +#define TYPE_PMGM_RING_INDICATE 0x6d +#define TYPE_PMGM_LAN_ATTENTION 0x6e +#define TYPE_PMGM_TIME_ALARM 0x6f +#define TYPE_PMGM_CONFIG_CHANGE 0x70 +#define TYPE_PMGM_SERVICE_PROC 0x71 + +struct rtas_error_log { + unsigned long version:8; /* Architectural version */ + unsigned long severity:3; /* Severity level of error */ + unsigned long disposition:2; /* Degree of recovery */ + unsigned long extended:1; /* extended log present? */ + unsigned long /* reserved */ :2; /* Reserved for future use */ + unsigned long initiator:4; /* Initiator of event */ + unsigned long target:4; /* Target of failed operation */ + unsigned long type:8; /* General event or error*/ + unsigned long extended_log_length:32; /* length in bytes */ + unsigned char buffer[1]; /* allocated by klimit bump */ +}; + +extern struct rtas_t rtas; + +extern void enter_rtas(struct rtas_args *); +extern int rtas_token(const char *service); +extern long rtas_call(int token, int, int, unsigned long *, ...); +extern void phys_call_rtas(int, int, int, ...); +extern void phys_call_rtas_display_status(char); +extern void call_rtas_display_status(char); +extern void rtas_restart(char *cmd); +extern void rtas_power_off(void); +extern void rtas_halt(void); + +#endif /* _PPC64_RTAS_H */ diff -Nru a/include/asm-ppc64/rwsem.h b/include/asm-ppc64/rwsem.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/rwsem.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,139 @@ +/* + * include/asm-ppc/rwsem.h: R/W semaphores for PPC using the stuff + * in lib/rwsem.c. Adapted largely from include/asm-i386/rwsem.h + * by Paul Mackerras . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _PPC64_RWSEM_H +#define _PPC64_RWSEM_H + +#ifdef __KERNEL__ +#include +#include +#include +#include + +/* + * the semaphore definition + */ +struct rw_semaphore { + /* XXX this should be able to be an atomic_t -- paulus */ + signed int count; +#define RWSEM_UNLOCKED_VALUE 0x00000000 +#define RWSEM_ACTIVE_BIAS 0x00000001 +#define RWSEM_ACTIVE_MASK 0x0000ffff +#define RWSEM_WAITING_BIAS (-0x00010000) +#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS +#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) + spinlock_t wait_lock; + struct list_head wait_list; +#if RWSEM_DEBUG + int debug; +#endif +}; + +/* + * initialisation + */ +#if RWSEM_DEBUG +#define __RWSEM_DEBUG_INIT , 0 +#else +#define __RWSEM_DEBUG_INIT /* */ +#endif + +#define __RWSEM_INITIALIZER(name) \ + { RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \ + LIST_HEAD_INIT((name).wait_list) \ + __RWSEM_DEBUG_INIT } + +#define DECLARE_RWSEM(name) \ + struct rw_semaphore name = __RWSEM_INITIALIZER(name) + +extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); + +static inline void init_rwsem(struct rw_semaphore *sem) +{ + sem->count = RWSEM_UNLOCKED_VALUE; + spin_lock_init(&sem->wait_lock); + INIT_LIST_HEAD(&sem->wait_list); +#if RWSEM_DEBUG + sem->debug = 0; +#endif +} + +/* + * lock for reading + */ +static inline void __down_read(struct rw_semaphore *sem) +{ + if (atomic_inc_return((atomic_t *)(&sem->count)) >= 0) + smp_wmb(); + else + rwsem_down_read_failed(sem); +} + +/* + * lock for writing + */ +static inline void __down_write(struct rw_semaphore *sem) +{ + int tmp; + + tmp = atomic_add_return(RWSEM_ACTIVE_WRITE_BIAS, + (atomic_t *)(&sem->count)); + if (tmp == RWSEM_ACTIVE_WRITE_BIAS) + smp_wmb(); + else + rwsem_down_write_failed(sem); +} + +/* + * unlock after reading + */ +static inline void __up_read(struct rw_semaphore *sem) +{ + int tmp; + + smp_wmb(); + tmp = atomic_dec_return((atomic_t *)(&sem->count)); + if (tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0) + rwsem_wake(sem); +} + +/* + * unlock after writing + */ +static inline void __up_write(struct rw_semaphore *sem) +{ + smp_wmb(); + if (atomic_sub_return(RWSEM_ACTIVE_WRITE_BIAS, + (atomic_t *)(&sem->count)) < 0) + rwsem_wake(sem); +} + +/* + * implement atomic add functionality + */ +static inline void rwsem_atomic_add(int delta, struct rw_semaphore *sem) +{ + atomic_add(delta, (atomic_t *)(&sem->count)); +} + +/* + * implement exchange and add functionality + */ +static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem) +{ + smp_mb(); + return atomic_add_return(delta, (atomic_t *)(&sem->count)); +} + +#endif /* __KERNEL__ */ +#endif /* _PPC_RWSEM_XADD_H */ diff -Nru a/include/asm-ppc64/scatterlist.h b/include/asm-ppc64/scatterlist.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/scatterlist.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,28 @@ +#ifndef _PPC64_SCATTERLIST_H +#define _PPC64_SCATTERLIST_H + +/* + * Copyright (C) 2001 PPC64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +struct scatterlist { + struct page *page; + unsigned int offset; + unsigned int length; + + /* For TCE support */ + u32 dma_address; + u32 dma_length; +}; + +#define ISA_DMA_THRESHOLD (~0UL) + +#endif /* !(_PPC64_SCATTERLIST_H) */ diff -Nru a/include/asm-ppc64/segment.h b/include/asm-ppc64/segment.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/segment.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,6 @@ +#ifndef __PPC64_SEGMENT_H +#define __PPC64_SEGMENT_H + +/* Only here because we have some old header files that expect it.. */ + +#endif /* __PPC64_SEGMENT_H */ diff -Nru a/include/asm-ppc64/semaphore.h b/include/asm-ppc64/semaphore.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/semaphore.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,129 @@ +#ifndef _PPC64_SEMAPHORE_H +#define _PPC64_SEMAPHORE_H + +/* + * Remove spinlock-based RW semaphores; RW semaphore definitions are + * now in rwsem.h and we use the the generic lib/rwsem.c implementation. + * Rework semaphores to use atomic_dec_if_positive. + * -- Paul Mackerras (paulus@samba.org) + */ + +#ifdef __KERNEL__ + +#include +#include +#include +#include + +struct semaphore { + /* + * Note that any negative value of count is equivalent to 0, + * but additionally indicates that some process(es) might be + * sleeping on `wait'. + */ + atomic_t count; + wait_queue_head_t wait; +#if WAITQUEUE_DEBUG + long __magic; +#endif +}; + +#if WAITQUEUE_DEBUG +# define __SEM_DEBUG_INIT(name) \ + , (long)&(name).__magic +#else +# define __SEM_DEBUG_INIT(name) +#endif + +#define __SEMAPHORE_INITIALIZER(name, count) \ + { ATOMIC_INIT(count), \ + __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ + __SEM_DEBUG_INIT(name) } + +#define __MUTEX_INITIALIZER(name) \ + __SEMAPHORE_INITIALIZER(name, 1) + +#define __DECLARE_SEMAPHORE_GENERIC(name, count) \ + struct semaphore name = __SEMAPHORE_INITIALIZER(name,count) + +#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name, 1) +#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name, 0) + +static inline void sema_init (struct semaphore *sem, int val) +{ + atomic_set(&sem->count, val); + init_waitqueue_head(&sem->wait); +#if WAITQUEUE_DEBUG + sem->__magic = (long)&sem->__magic; +#endif +} + +static inline void init_MUTEX (struct semaphore *sem) +{ + sema_init(sem, 1); +} + +static inline void init_MUTEX_LOCKED (struct semaphore *sem) +{ + sema_init(sem, 0); +} + +extern void __down(struct semaphore * sem); +extern int __down_interruptible(struct semaphore * sem); +extern void __up(struct semaphore * sem); + +extern inline void down(struct semaphore * sem) +{ +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + /* + * Try to get the semaphore, take the slow path if we fail. + */ + if (atomic_dec_return(&sem->count) < 0) + __down(sem); + smp_wmb(); +} + +extern inline int down_interruptible(struct semaphore * sem) +{ + int ret = 0; + +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + if (atomic_dec_return(&sem->count) < 0) + ret = __down_interruptible(sem); + smp_wmb(); + return ret; +} + +extern inline int down_trylock(struct semaphore * sem) +{ + int ret; + +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + ret = atomic_dec_if_positive(&sem->count) < 0; + smp_wmb(); + return ret; +} + +extern inline void up(struct semaphore * sem) +{ +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + smp_wmb(); + if (atomic_inc_return(&sem->count) <= 0) + __up(sem); +} + +#endif /* __KERNEL__ */ + +#endif /* !(_PPC64_SEMAPHORE_H) */ diff -Nru a/include/asm-ppc64/sembuf.h b/include/asm-ppc64/sembuf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/sembuf.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,27 @@ +#ifndef _PPC64_SEMBUF_H +#define _PPC64_SEMBUF_H + +/* + * The semid64_ds structure for PPC architecture. + * + * + * 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. + * + * Pad space is left for: + * - 2 miscellaneous 64-bit values + */ + +struct semid64_ds { + struct ipc64_perm sem_perm; /* permissions .. see ipc.h */ + __kernel_time_t sem_otime; /* last semop time */ + __kernel_time_t sem_ctime; /* last change time */ + unsigned long sem_nsems; /* no. of semaphores in array */ + + unsigned long __unused1; + unsigned long __unused2; +}; + +#endif /* _PPC64_SEMBUF_H */ diff -Nru a/include/asm-ppc64/serial.h b/include/asm-ppc64/serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,128 @@ +/* + * include/asm-ppc/serial.h + */ + +#include + +/* + * This assumes you have a 1.8432 MHz clock for your UART. + * + * It'd be nice if someone built a serial card with a 24.576 MHz + * clock, since the 16550A is capable of handling a top speed of 1.5 + * megabits/second; but this requires the faster clock. + * + * 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. + */ +#define BASE_BAUD ( 1843200 / 16 ) + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +/* Standard COM flags (except for COM4, because of the 8514 problem) */ +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) +#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF +#endif + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define FOURPORT_FLAGS ASYNC_FOURPORT +#define ACCENT_FLAGS 0 +#define BOCA_FLAGS 0 +#define HUB6_FLAGS 0 +#endif + +/* + * The following define the access methods for the HUB6 card. All + * access is through two ports for all 24 possible chips. The card is + * selected through the high 2 bits, the port on that card with the + * "middle" 3 bits, and the register on that port with the bottom + * 3 bits. + * + * While the access port and interrupt is configurable, the default + * port locations are 0x302 for the port control register, and 0x303 + * for the data read/write register. Normally, the interrupt is at irq3 + * but can be anything from 3 to 7 inclusive. Note that using 3 will + * require disabling com2. + */ + +#define C_P(card,port) (((card)<<6|(port)<<3) + 1) + +#define STD_SERIAL_PORT_DEFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ \ + { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ \ + { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ + + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define EXTRA_SERIAL_PORT_DEFNS \ + { 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */ \ + { 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */ \ + { 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */ \ + { 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS }, /* ttyS7 */ \ + { 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS }, /* ttyS8 */ \ + { 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS }, /* ttyS9 */ \ + { 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */ \ + { 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */ \ + { 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */ \ + { 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */ \ + { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS14 (spare) */ \ + { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS15 (spare) */ \ + { 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */ \ + { 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */ \ + { 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS }, /* ttyS18 */ \ + { 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS }, /* ttyS19 */ \ + { 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS }, /* ttyS20 */ \ + { 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS }, /* ttyS21 */ \ + { 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS }, /* ttyS22 */ \ + { 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS }, /* ttyS23 */ \ + { 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS }, /* ttyS24 */ \ + { 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS }, /* ttyS25 */ \ + { 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS }, /* ttyS26 */ \ + { 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS }, /* ttyS27 */ \ + { 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS }, /* ttyS28 */ \ + { 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */ \ + { 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */ \ + { 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */ +#else +#define EXTRA_SERIAL_PORT_DEFNS +#endif + +/* You can have up to four HUB6's in the system, but I've only + * included two cards here for a total of twelve ports. + */ +#if (defined(CONFIG_HUB6) && defined(CONFIG_SERIAL_MANY_PORTS)) +#define HUB6_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS32 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS33 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS34 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS35 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS36 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS37 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS38 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS39 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS40 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS41 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */ +#else +#define HUB6_SERIAL_PORT_DFNS +#endif + +#define MCA_SERIAL_PORT_DFNS + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DEFNS \ + EXTRA_SERIAL_PORT_DEFNS \ + HUB6_SERIAL_PORT_DFNS \ + MCA_SERIAL_PORT_DFNS diff -Nru a/include/asm-ppc64/setup.h b/include/asm-ppc64/setup.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/setup.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,6 @@ +#ifndef _PPC_SETUP_H +#define _PPC_SETUP_H + +/* This is a place holder include */ + +#endif /* _PPC_SETUP_H */ diff -Nru a/include/asm-ppc64/shmbuf.h b/include/asm-ppc64/shmbuf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/shmbuf.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,43 @@ +#ifndef _PPC64_SHMBUF_H +#define _PPC64_SHMBUF_H + +/* + * The shmid64_ds structure for PPC64 architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 2 miscellaneous 64-bit values + * + * 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. + */ + +struct shmid64_ds { + struct ipc64_perm shm_perm; /* operation perms */ + __kernel_time_t shm_atime; /* last attach time */ + __kernel_time_t shm_dtime; /* last detach time */ + __kernel_time_t shm_ctime; /* last change time */ + size_t shm_segsz; /* size of segment (bytes) */ + __kernel_pid_t shm_cpid; /* pid of creator */ + __kernel_pid_t shm_lpid; /* pid of last operator */ + unsigned long shm_nattch; /* no. of current attaches */ + unsigned long __unused1; + unsigned long __unused2; +}; + +struct shminfo64 { + unsigned long shmmax; + unsigned long shmmin; + unsigned long shmmni; + unsigned long shmseg; + unsigned long shmall; + unsigned long __unused1; + unsigned long __unused2; + unsigned long __unused3; + unsigned long __unused4; +}; + +#endif /* _PPC64_SHMBUF_H */ diff -Nru a/include/asm-ppc64/shmparam.h b/include/asm-ppc64/shmparam.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/shmparam.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,13 @@ +#ifndef _PPC64_SHMPARAM_H +#define _PPC64_SHMPARAM_H + +/* + * 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. + */ + +#define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ + +#endif /* _PPC64_SHMPARAM_H */ diff -Nru a/include/asm-ppc64/sigcontext.h b/include/asm-ppc64/sigcontext.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/sigcontext.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,21 @@ +#ifndef _ASM_PPC64_SIGCONTEXT_H +#define _ASM_PPC64_SIGCONTEXT_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +struct sigcontext_struct { + unsigned long _unused[4]; + int signal; + unsigned long handler; + unsigned long oldmask; + struct pt_regs *regs; +}; + +#endif /* _ASM_PPC64_SIGCONTEXT_H */ diff -Nru a/include/asm-ppc64/siginfo.h b/include/asm-ppc64/siginfo.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/siginfo.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,238 @@ +#ifndef _PPC64_SIGINFO_H +#define _PPC64_SIGINFO_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +typedef union sigval { + int sival_int; + void *sival_ptr; +} sigval_t; + +#define SI_MAX_SIZE 128 +#define SI_PAD_SIZE ((SI_MAX_SIZE/sizeof(int)) - 3) + +typedef struct siginfo { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[SI_PAD_SIZE]; + + /* kill() */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + unsigned int _timer1; + unsigned int _timer2; + } _timer; + + /* POSIX.1b signals */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + pid_t _pid; /* which child */ + uid_t _uid; /* sender's uid */ + int _status; /* exit code */ + clock_t _utime; + clock_t _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + void *_addr; /* faulting insn/memory ref. */ + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} siginfo_t; + +/* + * How these fields are to be accessed. + */ +#define si_pid _sifields._kill._pid +#define si_uid _sifields._kill._uid +#define si_status _sifields._sigchld._status +#define si_utime _sifields._sigchld._utime +#define si_stime _sifields._sigchld._stime +#define si_value _sifields._rt._sigval +#define si_int _sifields._rt._sigval.sival_int +#define si_ptr _sifields._rt._sigval.sival_ptr +#define si_addr _sifields._sigfault._addr +#define si_band _sifields._sigpoll._band +#define si_fd _sifields._sigpoll._fd + +#ifdef __KERNEL__ +#define __SI_MASK 0xffff0000 +#define __SI_KILL (0 << 16) +#define __SI_TIMER (1 << 16) +#define __SI_POLL (2 << 16) +#define __SI_FAULT (3 << 16) +#define __SI_CHLD (4 << 16) +#define __SI_RT (5 << 16) +#define __SI_CODE(T,N) ((T) << 16 | ((N) & 0xffff)) +#else +#define __SI_KILL 0 +#define __SI_TIMER 0 +#define __SI_POLL 0 +#define __SI_FAULT 0 +#define __SI_CHLD 0 +#define __SI_RT 0 +#define __SI_CODE(T,N) (N) +#endif + +/* + * si_code values + * Digital reserves positive values for kernel-generated signals. + */ +#define SI_USER 0 /* sent by kill, sigsend, raise */ +#define SI_KERNEL 0x80 /* sent by the kernel from somewhere */ +#define SI_QUEUE -1 /* sent by sigqueue */ +#define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */ +#define SI_MESGQ -3 /* sent by real time mesq state change */ +#define SI_ASYNCIO -4 /* sent by AIO completion */ +#define SI_SIGIO -5 /* sent by queued SIGIO */ +#define SI_TKILL -6 /* sent by tkill system call */ + +#define SI_FROMUSER(siptr) ((siptr)->si_code <= 0) +#define SI_FROMKERNEL(siptr) ((siptr)->si_code > 0) + +/* + * SIGILL si_codes + */ +#define ILL_ILLOPC (__SI_FAULT|1) /* illegal opcode */ +#define ILL_ILLOPN (__SI_FAULT|2) /* illegal operand */ +#define ILL_ILLADR (__SI_FAULT|3) /* illegal addressing mode */ +#define ILL_ILLTRP (__SI_FAULT|4) /* illegal trap */ +#define ILL_PRVOPC (__SI_FAULT|5) /* privileged opcode */ +#define ILL_PRVREG (__SI_FAULT|6) /* privileged register */ +#define ILL_COPROC (__SI_FAULT|7) /* coprocessor error */ +#define ILL_BADSTK (__SI_FAULT|8) /* internal stack error */ +#define NSIGILL 8 + +/* + * SIGFPE si_codes + */ +#define FPE_INTDIV (__SI_FAULT|1) /* integer divide by zero */ +#define FPE_INTOVF (__SI_FAULT|2) /* integer overflow */ +#define FPE_FLTDIV (__SI_FAULT|3) /* floating point divide by zero */ +#define FPE_FLTOVF (__SI_FAULT|4) /* floating point overflow */ +#define FPE_FLTUND (__SI_FAULT|5) /* floating point underflow */ +#define FPE_FLTRES (__SI_FAULT|6) /* floating point inexact result */ +#define FPE_FLTINV (__SI_FAULT|7) /* floating point invalid operation */ +#define FPE_FLTSUB (__SI_FAULT|8) /* subscript out of range */ +#define NSIGFPE 8 + +/* + * SIGSEGV si_codes + */ +#define SEGV_MAPERR (__SI_FAULT|1) /* address not mapped to object */ +#define SEGV_ACCERR (__SI_FAULT|2) /* invalid permissions for mapped object */ +#define NSIGSEGV 2 + +/* + * SIGBUS si_codes + */ +#define BUS_ADRALN (__SI_FAULT|1) /* invalid address alignment */ +#define BUS_ADRERR (__SI_FAULT|2) /* non-existant physical address */ +#define BUS_OBJERR (__SI_FAULT|3) /* object specific hardware error */ +#define NSIGBUS 3 + +/* + * SIGTRAP si_codes + */ +#define TRAP_BRKPT (__SI_FAULT|1) /* process breakpoint */ +#define TRAP_TRACE (__SI_FAULT|2) /* process trace trap */ +#define NSIGTRAP 2 + +/* + * SIGCHLD si_codes + */ +#define CLD_EXITED (__SI_CHLD|1) /* child has exited */ +#define CLD_KILLED (__SI_CHLD|2) /* child was killed */ +#define CLD_DUMPED (__SI_CHLD|3) /* child terminated abnormally */ +#define CLD_TRAPPED (__SI_CHLD|4) /* traced child has trapped */ +#define CLD_STOPPED (__SI_CHLD|5) /* child has stopped */ +#define CLD_CONTINUED (__SI_CHLD|6) /* stopped child has continued */ +#define NSIGCHLD 6 + +/* + * SIGPOLL si_codes + */ +#define POLL_IN (__SI_POLL|1) /* data input available */ +#define POLL_OUT (__SI_POLL|2) /* output buffers available */ +#define POLL_MSG (__SI_POLL|3) /* input message available */ +#define POLL_ERR (__SI_POLL|4) /* i/o error */ +#define POLL_PRI (__SI_POLL|5) /* high priority input available */ +#define POLL_HUP (__SI_POLL|6) /* device disconnected */ +#define NSIGPOLL 6 + +/* + * sigevent definitions + * + * It seems likely that SIGEV_THREAD will have to be handled from + * userspace, libpthread transmuting it to SIGEV_SIGNAL, which the + * thread manager then catches and does the appropriate nonsense. + * However, everything is written out here so as to not get lost. + */ +#define SIGEV_SIGNAL 0 /* notify via signal */ +#define SIGEV_NONE 1 /* other notification: meaningless */ +#define SIGEV_THREAD 2 /* deliver via thread creation */ + +#define SIGEV_MAX_SIZE 64 +#define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE/sizeof(int)) - 3) + +typedef struct sigevent { + sigval_t sigev_value; + int sigev_signo; + int sigev_notify; + union { + int _pad[SIGEV_PAD_SIZE]; + + struct { + void (*_function)(sigval_t); + void *_attribute; /* really pthread_attr_t */ + } _sigev_thread; + } _sigev_un; +} sigevent_t; + +#define sigev_notify_function _sigev_un._sigev_thread._function +#define sigev_notify_attributes _sigev_un._sigev_thread._attribute + +#ifdef __KERNEL__ +#include + +extern inline void copy_siginfo(siginfo_t *to, siginfo_t *from) +{ + if (from->si_code < 0) + memcpy(to, from, sizeof(siginfo_t)); + else + /* _sigchld is currently the largest know union member */ + memcpy(to, from, 3*sizeof(int) + sizeof(from->_sifields._sigchld)); +} + +extern int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from); + +#endif /* __KERNEL__ */ + +#endif /* _PPC64_SIGINFO_H */ diff -Nru a/include/asm-ppc64/signal.h b/include/asm-ppc64/signal.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/signal.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,152 @@ +#ifndef _ASMPPC64_SIGNAL_H +#define _ASMPPC64_SIGNAL_H + +#include + +/* Avoid too many header ordering problems. */ +struct siginfo; + +#define _NSIG 64 +#define _NSIG_BPW 64 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef unsigned long old_sigset_t; /* at least 32 bits */ + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +/* +#define SIGLOST 29 +*/ +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +/* These should not be considered constants from userland. */ +#define SIGRTMIN 32 +#define SIGRTMAX (_NSIG-1) + + + + + + + +/* + * SA_FLAGS values: + * + * SA_ONSTACK is not currently supported, but will allow sigaltstack(2). + * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the + * SA_RESTART flag to get restarting signals (which were the default long ago) + * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. + * SA_RESETHAND clears the handler when the signal is delivered. + * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. + * SA_NODEFER prevents the current signal from being masked in the handler. + * + * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single + * Unix names RESETHAND and NODEFER respectively. + */ +#define SA_NOCLDSTOP 0x00000001 +#define SA_NOCLDWAIT 0x00000002 /* not supported yet */ +#define SA_SIGINFO 0x00000004 +#define SA_ONSTACK 0x08000000 +#define SA_RESTART 0x10000000 +#define SA_NODEFER 0x40000000 +#define SA_RESETHAND 0x80000000 + +#define SA_NOMASK SA_NODEFER +#define SA_ONESHOT SA_RESETHAND +#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ + +#define SA_RESTORER 0x04000000 + +/* + * sigaltstack controls + */ +#define SS_ONSTACK 1 +#define SS_DISABLE 2 + +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 +#ifdef __KERNEL__ + +/* + * These values of sa_flags are used only by the kernel as part of the + * irq handling routines. + * + * SA_INTERRUPT is also used by the irq handling routines. + * SA_SHIRQ is for shared interrupt support on PCI and EISA. + */ +#define SA_PROBE SA_ONESHOT +#define SA_SAMPLE_RANDOM SA_RESTART +#define SA_SHIRQ 0x04000000 +#endif + +#define SIG_BLOCK 0 /* for blocking signals */ +#define SIG_UNBLOCK 1 /* for unblocking signals */ +#define SIG_SETMASK 2 /* for setting the signal mask */ + +/* Type of a signal handler. */ +typedef void (*__sighandler_t)(int); + +#define SIG_DFL ((__sighandler_t)0) /* default signal handling */ +#define SIG_IGN ((__sighandler_t)1) /* ignore signal */ +#define SIG_ERR ((__sighandler_t)-1) /* error return from signal */ + +struct old_sigaction { + __sighandler_t sa_handler; + old_sigset_t sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +}; + +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; + void (*sa_restorer)(void); + sigset_t sa_mask; /* mask last for extensibility */ +}; + +struct k_sigaction { + struct sigaction sa; +}; + +typedef struct sigaltstack { + void *ss_sp; + int ss_flags; + size_t ss_size; +} stack_t; + +#endif /* _ASMPPC64_SIGNAL_H */ diff -Nru a/include/asm-ppc64/smp.h b/include/asm-ppc64/smp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/smp.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,68 @@ +/* + * smp.h: PPC64 specific SMP code. + * + * Original was a copy of sparc smp.h. Now heavily modified + * for PPC. + * + * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996-2001 Cort Dougan + * + * 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. + */ + +#ifdef __KERNEL__ +#ifndef _PPC64_SMP_H +#define _PPC64_SMP_H + +#include +#include +#include + +#ifdef CONFIG_SMP + +#ifndef __ASSEMBLY__ + +#include + +extern unsigned long cpu_online_map; + +extern void smp_message_pass(int target, int msg, unsigned long data, int wait); +extern void smp_store_cpu_info(int id); +extern void smp_send_tlb_invalidate(int); +extern void smp_send_xmon_break(int cpu); +struct pt_regs; +extern void smp_message_recv(int, struct pt_regs *); +extern void smp_send_reschedule_all(void); + +#define NO_PROC_ID 0xFF /* No processor magic marker */ + +/* 1 to 1 mapping on PPC -- Cort */ +#define cpu_logical_map(cpu) (cpu) +#define cpu_number_map(x) (x) +extern volatile unsigned long cpu_callin_map[NR_CPUS]; + +#define smp_processor_id() (get_paca()->xPacaIndex) +#define hard_smp_processor_id() (get_paca()->xHwProcNum) +#define get_hard_smp_processor_id(CPU) (xPaca[(CPU)].xHwProcNum) + + + +/* Since OpenPIC has only 4 IPIs, we use slightly different message numbers. + * + * Make sure this matches openpic_request_IPIs in open_pic.c, or what shows up + * in /proc/interrupts will be wrong!!! --Troy */ +#define PPC_MSG_CALL_FUNCTION 0 +#define PPC_MSG_RESCHEDULE 1 +#define PPC_MSG_MIGRATE_TASK 2 +#define PPC_MSG_XMON_BREAK 3 + +void smp_init_iSeries(void); +void smp_init_pSeries(void); + +#endif /* __ASSEMBLY__ */ +#endif /* !(CONFIG_SMP) */ +#endif /* !(_PPC64_SMP_H) */ +#endif /* __KERNEL__ */ diff -Nru a/include/asm-ppc64/smplock.h b/include/asm-ppc64/smplock.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/smplock.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,58 @@ +/* + * + * + * Default SMP lock implementation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include + +extern spinlock_t kernel_flag; + +#define kernel_locked() spin_is_locked(&kernel_flag) + +/* + * Release global kernel lock and global interrupt lock + */ +#define release_kernel_lock(task, cpu) \ +do { \ + if (unlikely(task->lock_depth >= 0)) { \ + spin_unlock(&kernel_flag); \ + if (global_irq_holder == (cpu)) \ + BUG(); \ + } \ +} while (0) + +/* + * Re-acquire the kernel lock + */ +#define reacquire_kernel_lock(task) \ +do { \ + if (unlikely(task->lock_depth >= 0)) \ + spin_lock(&kernel_flag); \ +} while (0) + +/* + * Getting the big kernel lock. + * + * This cannot happen asynchronously, + * so we only need to worry about other + * CPU's. + */ +static __inline__ void lock_kernel(void) +{ + if (!++current->lock_depth) + spin_lock(&kernel_flag); +} + +static __inline__ void unlock_kernel(void) +{ + if (current->lock_depth < 0) + BUG(); + if (--current->lock_depth < 0) + spin_unlock(&kernel_flag); +} diff -Nru a/include/asm-ppc64/socket.h b/include/asm-ppc64/socket.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/socket.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,71 @@ +#ifndef _ASM_SOCKET_H +#define _ASM_SOCKET_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +/* For setsockoptions(2) */ +#define SOL_SOCKET 1 + +#define SO_DEBUG 1 +#define SO_REUSEADDR 2 +#define SO_TYPE 3 +#define SO_ERROR 4 +#define SO_DONTROUTE 5 +#define SO_BROADCAST 6 +#define SO_SNDBUF 7 +#define SO_RCVBUF 8 +#define SO_KEEPALIVE 9 +#define SO_OOBINLINE 10 +#define SO_NO_CHECK 11 +#define SO_PRIORITY 12 +#define SO_LINGER 13 +#define SO_BSDCOMPAT 14 +/* To add :#define SO_REUSEPORT 15 */ +#define SO_RCVLOWAT 16 +#define SO_SNDLOWAT 17 +#define SO_RCVTIMEO 18 +#define SO_SNDTIMEO 19 +#define SO_PASSCRED 20 +#define SO_PEERCRED 21 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define SO_SECURITY_AUTHENTICATION 22 +#define SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define SO_ATTACH_FILTER 26 +#define SO_DETACH_FILTER 27 + +#define SO_PEERNAME 28 +#define SO_TIMESTAMP 29 +#define SCM_TIMESTAMP SO_TIMESTAMP + +#define SO_ACCEPTCONN 30 + +/* Nast libc5 fixup - bletch */ +#if defined(__KERNEL__) +/* Socket types. */ +#define SOCK_STREAM 1 /* stream (connection) socket */ +#define SOCK_DGRAM 2 /* datagram (conn.less) socket */ +#define SOCK_RAW 3 /* raw socket */ +#define SOCK_RDM 4 /* reliably-delivered message */ +#define SOCK_SEQPACKET 5 /* sequential packet socket */ +#define SOCK_PACKET 10 /* linux specific way of */ + /* getting packets at the dev */ + /* level. For writing rarp and */ + /* other similar things on the */ + /* user level. */ +#define SOCK_MAX (SOCK_PACKET+1) +#endif + +#endif /* _ASM_SOCKET_H */ diff -Nru a/include/asm-ppc64/sockios.h b/include/asm-ppc64/sockios.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/sockios.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,19 @@ +#ifndef _ASM_PPC64_SOCKIOS_H +#define _ASM_PPC64_SOCKIOS_H + +/* + * 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. + */ + +/* Socket-level I/O control calls. */ +#define FIOSETOWN 0x8901 +#define SIOCSPGRP 0x8902 +#define FIOGETOWN 0x8903 +#define SIOCGPGRP 0x8904 +#define SIOCATMARK 0x8905 +#define SIOCGSTAMP 0x8906 /* Get stamp */ + +#endif /* _ASM_PPC64_SOCKIOS_H */ diff -Nru a/include/asm-ppc64/softirq.h b/include/asm-ppc64/softirq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/softirq.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,29 @@ +#ifndef __ASM_SOFTIRQ_H +#define __ASM_SOFTIRQ_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + + +#define local_bh_disable() do { local_bh_count(smp_processor_id())++; barrier(); } while (0) +#define __local_bh_enable() do { barrier(); local_bh_count(smp_processor_id())--; } while (0) + +#define local_bh_enable() \ +do { \ + if (!--local_bh_count(smp_processor_id()) \ + && softirq_pending(smp_processor_id())) { \ + do_softirq(); \ + } \ +} while (0) + + +#define in_softirq() (local_bh_count(smp_processor_id()) != 0) + +#endif /* __ASM_SOFTIRQ_H */ diff -Nru a/include/asm-ppc64/spinlock.h b/include/asm-ppc64/spinlock.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/spinlock.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,215 @@ +#ifndef __ASM_SPINLOCK_H +#define __ASM_SPINLOCK_H + +/* + * Simple spin lock operations. + * + * Copyright (C) 2001 Paul Mackerras , IBM + * Copyright (C) 2001 Anton Blanchard , IBM + * + * Type of int is used as a full 64b word is not necessary. + * + * 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. + */ +typedef struct { + volatile unsigned int lock; +} spinlock_t; + +#ifdef __KERNEL__ +#define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } + +#define spin_is_locked(x) ((x)->lock != 0) + +static __inline__ int _raw_spin_trylock(spinlock_t *lock) +{ + unsigned int tmp; + + __asm__ __volatile__( +"1: lwarx %0,0,%1 # spin_trylock\n\ + cmpwi 0,%0,0\n\ + li %0,0\n\ + bne- 2f\n\ + li %0,1\n\ + stwcx. %0,0,%1\n\ + bne- 1b\n\ + isync\n\ +2:" : "=&r"(tmp) + : "r"(&lock->lock) + : "cr0", "memory"); + + return tmp; +} + +static __inline__ void _raw_spin_lock(spinlock_t *lock) +{ + unsigned int tmp; + + __asm__ __volatile__( + "b 2f # spin_lock\n\ +1: or 1,1,1 # spin at low priority\n\ + lwzx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne+ 1b\n\ + or 2,2,2 # back to medium priority\n\ +2: lwarx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne- 1b\n\ + stwcx. %2,0,%1\n\ + bne- 2b\n\ + isync" + : "=&r"(tmp) + : "r"(&lock->lock), "r"(1) + : "cr0", "memory"); +} + +static __inline__ void _raw_spin_unlock(spinlock_t *lock) +{ + __asm__ __volatile__("eieio # spin_unlock": : :"memory"); + lock->lock = 0; +} + +/* + * Read-write spinlocks, allowing multiple readers + * but only one writer. + * + * NOTE! it is quite common to have readers in interrupts + * but no interrupt writers. For those circumstances we + * can "mix" irq-safe locks - any writer needs to get a + * irq-safe write-lock, but readers can get non-irqsafe + * read-locks. + */ +typedef struct { + volatile signed int lock; +} rwlock_t; + +#define RW_LOCK_UNLOCKED (rwlock_t) { 0 } + +static __inline__ int _raw_read_trylock(rwlock_t *rw) +{ + unsigned int tmp; + unsigned int ret; + + __asm__ __volatile__( +"1: lwarx %0,0,%2 # read_trylock\n\ + li %1,0\n\ + extsw %0,%0\n\ + addic. %0,%0,1\n\ + ble- 2f\n\ + stwcx. %0,0,%2\n\ + bne- 1b\n\ + li %1,1\n\ + isync\n\ +2:" : "=&r"(tmp), "=&r"(ret) + : "r"(&rw->lock) + : "cr0", "memory"); + + return ret; +} + +static __inline__ void _raw_read_lock(rwlock_t *rw) +{ + unsigned int tmp; + + __asm__ __volatile__( + "b 2f # read_lock\n\ +1: or 1,1,1 # spin at low priority\n\ + lwax %0,0,%1\n\ + cmpwi 0,%0,0\n\ + blt+ 1b\n\ + or 2,2,2 # back to medium priority\n\ +2: lwarx %0,0,%1\n\ + extsw %0,%0\n\ + addic. %0,%0,1\n\ + ble- 1b\n\ + stwcx. %0,0,%1\n\ + bne- 2b\n\ + isync" + : "=&r"(tmp) + : "r"(&rw->lock) + : "cr0", "memory"); +} + +static __inline__ void _raw_read_unlock(rwlock_t *rw) +{ + unsigned int tmp; + + __asm__ __volatile__( + "eieio # read_unlock\n\ +1: lwarx %0,0,%1\n\ + addic %0,%0,-1\n\ + stwcx. %0,0,%1\n\ + bne- 1b" + : "=&r"(tmp) + : "r"(&rw->lock) + : "cr0", "memory"); +} + +static __inline__ int _raw_write_trylock(rwlock_t *rw) +{ + unsigned int tmp; + unsigned int ret; + + __asm__ __volatile__( +"1: lwarx %0,0,%2 # write_trylock\n\ + cmpwi 0,%0,0\n\ + li %1,0\n\ + bne- 2f\n\ + stwcx. %3,0,%2\n\ + bne- 1b\n\ + li %1,1\n\ + isync\n\ +2:" : "=&r"(tmp), "=&r"(ret) + : "r"(&rw->lock), "r"(-1) + : "cr0", "memory"); + + return ret; +} + +static __inline__ void _raw_write_lock(rwlock_t *rw) +{ + unsigned int tmp; + + __asm__ __volatile__( + "b 2f # write_lock\n\ +1: or 1,1,1 # spin at low priority\n\ + lwax %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne+ 1b\n\ + or 2,2,2 # back to medium priority\n\ +2: lwarx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne- 1b\n\ + stwcx. %2,0,%1\n\ + bne- 2b\n\ + isync" + : "=&r"(tmp) + : "r"(&rw->lock), "r"(-1) + : "cr0", "memory"); +} + +static __inline__ void _raw_write_unlock(rwlock_t *rw) +{ + __asm__ __volatile__("eieio # write_unlock": : :"memory"); + rw->lock = 0; +} + +static __inline__ int is_read_locked(rwlock_t *rw) +{ + return rw->lock > 0; +} + +static __inline__ int is_write_locked(rwlock_t *rw) +{ + return rw->lock < 0; +} + +#define spin_lock_init(x) do { *(x) = SPIN_LOCK_UNLOCKED; } while(0) +#define spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x)) + +#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) + +#endif /* __KERNEL__ */ +#endif /* __ASM_SPINLOCK_H */ diff -Nru a/include/asm-ppc64/stat.h b/include/asm-ppc64/stat.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/stat.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,71 @@ +#ifndef _PPC64_STAT_H +#define _PPC64_STAT_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +struct __old_kernel_stat { + unsigned short st_dev; + unsigned short st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned long st_size; + unsigned long st_atime; + unsigned long st_mtime; + unsigned long st_ctime; +}; + +struct stat { + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + off_t st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long __unused1; + unsigned long st_mtime; + unsigned long __unused2; + unsigned long st_ctime; + unsigned long __unused3; + unsigned long __unused4; + unsigned long __unused5; +}; + +/* This matches struct stat64 in glibc2.1. */ +struct stat64 { + unsigned long st_dev; /* Device. */ + unsigned long st_ino; /* File serial number. */ + unsigned int st_mode; /* File mode. */ + unsigned int st_nlink; /* Link count. */ + unsigned int st_uid; /* User ID of the file's owner. */ + unsigned int st_gid; /* Group ID of the file's group. */ + unsigned long st_rdev; /* Device number, if device. */ + unsigned short __pad2; + long st_size; /* Size of file, in bytes. */ + int st_blksize; /* Optimal block size for I/O. */ + + long st_blocks; /* Number 512-byte blocks allocated. */ + int st_atime; /* Time of last access. */ + unsigned int __unused1; + int st_mtime; /* Time of last modification. */ + unsigned int __unused2; + int st_ctime; /* Time of last status change. */ + unsigned int __unused3; + unsigned int __unused4; + unsigned int __unused5; +}; +#endif diff -Nru a/include/asm-ppc64/statfs.h b/include/asm-ppc64/statfs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/statfs.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,29 @@ +#ifndef _PPC64_STATFS_H +#define _PPC64_STATFS_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __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]; +}; + +#endif /* _PPC64_STATFS_H */ diff -Nru a/include/asm-ppc64/string.h b/include/asm-ppc64/string.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/string.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,36 @@ +#ifndef _PPC64_STRING_H_ +#define _PPC64_STRING_H_ + +/* + * 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. + */ + +#define __HAVE_ARCH_STRCPY +#define __HAVE_ARCH_STRNCPY +#define __HAVE_ARCH_STRLEN +#define __HAVE_ARCH_STRCMP +#define __HAVE_ARCH_STRCAT +#define __HAVE_ARCH_MEMSET +#define __HAVE_ARCH_BCOPY +#define __HAVE_ARCH_MEMCPY +#define __HAVE_ARCH_MEMMOVE +#define __HAVE_ARCH_MEMCMP +#define __HAVE_ARCH_MEMCHR + +extern int strcasecmp(const char *, const char *); +extern int strncasecmp(const char *, const char *, int); +extern char * strcpy(char *,const char *); +extern char * strncpy(char *,const char *, __kernel_size_t); +extern __kernel_size_t strlen(const char *); +extern int strcmp(const char *,const char *); +extern char * strcat(char *, const char *); +extern void * memset(void *,int,__kernel_size_t); +extern void * memcpy(void *,const void *,__kernel_size_t); +extern void * memmove(void *,const void *,__kernel_size_t); +extern int memcmp(const void *,const void *,__kernel_size_t); +extern void * memchr(const void *,int,__kernel_size_t); + +#endif /* _PPC64_STRING_H_ */ diff -Nru a/include/asm-ppc64/system.h b/include/asm-ppc64/system.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/system.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,272 @@ +#ifndef __PPC64_SYSTEM_H +#define __PPC64_SYSTEM_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +/* + * System defines. + */ +#define KERNEL_START_PHYS 0x800000 +#define KERNEL_START (PAGE_OFFSET+KERNEL_START_PHYS) +#define START_ADDR (PAGE_OFFSET+KERNEL_START_PHYS+0x00000) + +/* + * Memory barrier. + * The sync instruction guarantees that all memory accesses initiated + * by this processor have been performed (with respect to all other + * mechanisms that access memory). The eieio instruction is a barrier + * providing an ordering (separately) for (a) cacheable stores and (b) + * loads and stores to non-cacheable memory (e.g. I/O devices). + * + * mb() prevents loads and stores being reordered across this point. + * rmb() prevents loads being reordered across this point. + * wmb() prevents stores being reordered across this point. + * + * We can use the eieio instruction for wmb, but since it doesn't + * give any ordering guarantees about loads, we have to use the + * stronger but slower sync instruction for mb and rmb. + */ +#define mb() __asm__ __volatile__ ("sync" : : : "memory") +#define rmb() __asm__ __volatile__ ("lwsync" : : : "memory") +#define wmb() __asm__ __volatile__ ("eieio" : : : "memory") + +#define set_mb(var, value) do { var = value; mb(); } while (0) +#define set_wmb(var, value) do { var = value; wmb(); } while (0) + +#ifdef CONFIG_SMP +#define smp_mb() mb() +#define smp_rmb() rmb() +#define smp_wmb() wmb() +#else +#define smp_mb() __asm__ __volatile__("": : :"memory") +#define smp_rmb() __asm__ __volatile__("": : :"memory") +#define smp_wmb() __asm__ __volatile__("": : :"memory") +#endif /* CONFIG_SMP */ + +#ifdef CONFIG_XMON +extern void xmon_irq(int, void *, struct pt_regs *); +extern void xmon(struct pt_regs *excp); +#endif + +extern void print_backtrace(unsigned long *); +extern void show_regs(struct pt_regs * regs); +extern void flush_instruction_cache(void); +extern void hard_reset_now(void); +extern void poweroff_now(void); +extern int _get_PVR(void); +extern long _get_L2CR(void); +extern void _set_L2CR(unsigned long); +extern void via_cuda_init(void); +extern void pmac_nvram_init(void); +extern void pmac_find_display(void); +extern void giveup_fpu(struct task_struct *); +extern void enable_kernel_fp(void); +extern void giveup_altivec(struct task_struct *); +extern void load_up_altivec(struct task_struct *); +extern void cvt_fd(float *from, double *to, unsigned long *fpscr); +extern void cvt_df(double *from, float *to, unsigned long *fpscr); +extern int abs(int); +extern void cacheable_memzero(void *p, unsigned int nb); + +struct device_node; + +struct task_struct; +#define prepare_to_switch() do { } while(0) +#define switch_to(prev,next) _switch_to((prev),(next)) +extern void _switch_to(struct task_struct *, struct task_struct *); + +struct thread_struct; +extern void _switch(struct thread_struct *prev, struct thread_struct *next); + +struct pt_regs; +extern void dump_regs(struct pt_regs *); + +#ifndef CONFIG_SMP + +#define cli() __cli() +#define sti() __sti() +#define save_flags(flags) __save_flags(flags) +#define restore_flags(flags) __restore_flags(flags) +#define save_and_cli(flags) __save_and_cli(flags) + +#else /* CONFIG_SMP */ + +extern void __global_cli(void); +extern void __global_sti(void); +extern unsigned long __global_save_flags(void); +extern void __global_restore_flags(unsigned long); +#define cli() __global_cli() +#define sti() __global_sti() +#define save_flags(x) ((x)=__global_save_flags()) +#define restore_flags(x) __global_restore_flags(x) + +#endif /* !CONFIG_SMP */ + +#define local_irq_disable() __cli() +#define local_irq_enable() __sti() +#define local_irq_save(flags) __save_and_cli(flags) +#define local_irq_restore(flags) __restore_flags(flags) + +static __inline__ int __is_processor(unsigned long pv) +{ + unsigned long pvr; + asm volatile("mfspr %0, 0x11F" : "=r" (pvr)); + return(PVR_VER(pvr) == pv); +} + +/* + * Atomic exchange + * + * Changes the memory location '*ptr' to be val and returns + * the previous value stored there. + * + * Inline asm pulled from arch/ppc/kernel/misc.S so ppc64 + * is more like most of the other architectures. + */ +static __inline__ unsigned long +__xchg_u32(volatile int *m, unsigned long val) +{ + unsigned long dummy; + + __asm__ __volatile__( + EIEIO_ON_SMP +"1: lwarx %0,0,%3 # __xchg_u32\n\ + stwcx. %2,0,%3\n\ +2: bne- 1b" + ISYNC_ON_SMP + : "=&r" (dummy), "=m" (*m) + : "r" (val), "r" (m) + : "cc", "memory"); + + return (dummy); +} + +static __inline__ unsigned long +__xchg_u64(volatile long *m, unsigned long val) +{ + unsigned long dummy; + + __asm__ __volatile__( + EIEIO_ON_SMP +"1: ldarx %0,0,%3 # __xchg_u64\n\ + stdcx. %2,0,%3\n\ +2: bne- 1b" + ISYNC_ON_SMP + : "=&r" (dummy), "=m" (*m) + : "r" (val), "r" (m) + : "cc", "memory"); + + return (dummy); +} + +/* + * This function doesn't exist, so you'll get a linker error + * if something tries to do an invalid xchg(). + */ +extern void __xchg_called_with_bad_pointer(void); + +static __inline__ unsigned long +__xchg(volatile void *ptr, unsigned long x, int size) +{ + switch (size) { + case 4: + return __xchg_u32(ptr, x); + case 8: + return __xchg_u64(ptr, x); + } + __xchg_called_with_bad_pointer(); + return x; +} + +#define xchg(ptr,x) \ + ({ \ + __typeof__(*(ptr)) _x_ = (x); \ + (__typeof__(*(ptr))) __xchg((ptr), (unsigned long)_x_, sizeof(*(ptr))); \ + }) + +#define tas(ptr) (xchg((ptr),1)) + +#define __HAVE_ARCH_CMPXCHG 1 + +static __inline__ unsigned long +__cmpxchg_u32(volatile int *p, int old, int new) +{ + int prev; + + __asm__ __volatile__ ( + EIEIO_ON_SMP +"1: lwarx %0,0,%2 # __cmpxchg_u32\n\ + cmpw 0,%0,%3\n\ + bne- 2f\n\ + stwcx. %4,0,%2\n\ + bne- 1b" + ISYNC_ON_SMP + "\n\ +2:" + : "=&r" (prev), "=m" (*p) + : "r" (p), "r" (old), "r" (new), "m" (*p) + : "cc", "memory"); + + return prev; +} + +static __inline__ unsigned long +__cmpxchg_u64(volatile long *p, unsigned long old, unsigned long new) +{ + int prev; + + __asm__ __volatile__ ( + EIEIO_ON_SMP +"1: ldarx %0,0,%2 # __cmpxchg_u64\n\ + cmpd 0,%0,%3\n\ + bne- 2f\n\ + stdcx. %4,0,%2\n\ + bne- 1b" + ISYNC_ON_SMP + "\n\ +2:" + : "=&r" (prev), "=m" (*p) + : "r" (p), "r" (old), "r" (new), "m" (*p) + : "cc", "memory"); + + return prev; +} + +/* This function doesn't exist, so you'll get a linker error + if something tries to do an invalid cmpxchg(). */ +extern void __cmpxchg_called_with_bad_pointer(void); + +static __inline__ unsigned long +__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) +{ + switch (size) { + case 4: + return __cmpxchg_u32(ptr, old, new); + case 8: + return __cmpxchg_u64(ptr, old, new); + } + __cmpxchg_called_with_bad_pointer(); + return old; +} + +#define cmpxchg(ptr,o,n) \ + ({ \ + __typeof__(*(ptr)) _o_ = (o); \ + __typeof__(*(ptr)) _n_ = (n); \ + (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ + (unsigned long)_n_, sizeof(*(ptr))); \ + }) + +#endif diff -Nru a/include/asm-ppc64/termbits.h b/include/asm-ppc64/termbits.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/termbits.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,192 @@ +#ifndef _PPC64_TERMBITS_H +#define _PPC64_TERMBITS_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +/* + * termios type and macro definitions. Be careful about adding stuff + * to this file since it's used in GNU libc and there are strict rules + * concerning namespace pollution. + */ + +#define NCCS 19 +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_cc[NCCS]; /* control characters */ + cc_t c_line; /* line discipline (== c_cc[19]) */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ +}; + +/* c_cc characters */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VMIN 5 +#define VEOL 6 +#define VTIME 7 +#define VEOL2 8 +#define VSWTC 9 +#define VWERASE 10 +#define VREPRINT 11 +#define VSUSP 12 +#define VSTART 13 +#define VSTOP 14 +#define VLNEXT 15 +#define VDISCARD 16 + +/* c_iflag bits */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IXON 0001000 +#define IXOFF 0002000 +#define IXANY 0004000 +#define IUCLC 0010000 +#define IMAXBEL 0020000 + +/* c_oflag bits */ +#define OPOST 0000001 +#define ONLCR 0000002 +#define OLCUC 0000004 + +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 + +#define OFILL 00000100 +#define OFDEL 00000200 +#define NLDLY 00001400 +#define NL0 00000000 +#define NL1 00000400 +#define NL2 00001000 +#define NL3 00001400 +#define TABDLY 00006000 +#define TAB0 00000000 +#define TAB1 00002000 +#define TAB2 00004000 +#define TAB3 00006000 +#define CRDLY 00030000 +#define CR0 00000000 +#define CR1 00010000 +#define CR2 00020000 +#define CR3 00030000 +#define FFDLY 00040000 +#define FF0 00000000 +#define FF1 00040000 +#define BSDLY 00100000 +#define BS0 00000000 +#define BS1 00100000 +#define VTDLY 00200000 +#define VT0 00000000 +#define VT1 00200000 +#define XTABS 01000000 /* Hmm.. Linux/i386 considers this part of TABDLY.. */ + +/* c_cflag bit meaning */ +#define CBAUD 0000377 +#define B0 0000000 /* hang up */ +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 +#define EXTA B19200 +#define EXTB B38400 +#define CBAUDEX 0000000 +#define B57600 00020 +#define B115200 00021 +#define B230400 00022 +#define B460800 00023 +#define B500000 00024 +#define B576000 00025 +#define B921600 00026 +#define B1000000 00027 +#define B1152000 00030 +#define B1500000 00031 +#define B2000000 00032 +#define B2500000 00033 +#define B3000000 00034 +#define B3500000 00035 +#define B4000000 00036 + +#define CSIZE 00001400 +#define CS5 00000000 +#define CS6 00000400 +#define CS7 00001000 +#define CS8 00001400 + +#define CSTOPB 00002000 +#define CREAD 00004000 +#define PARENB 00010000 +#define PARODD 00020000 +#define HUPCL 00040000 + +#define CLOCAL 00100000 +#define CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define ISIG 0x00000080 +#define ICANON 0x00000100 +#define XCASE 0x00004000 +#define ECHO 0x00000008 +#define ECHOE 0x00000002 +#define ECHOK 0x00000004 +#define ECHONL 0x00000010 +#define NOFLSH 0x80000000 +#define TOSTOP 0x00400000 +#define ECHOCTL 0x00000040 +#define ECHOPRT 0x00000020 +#define ECHOKE 0x00000001 +#define FLUSHO 0x00800000 +#define PENDIN 0x20000000 +#define IEXTEN 0x00000400 + +/* Values for the ACTION argument to `tcflow'. */ +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* Values for the QUEUE_SELECTOR argument to `tcflush'. */ +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* Values for the OPTIONAL_ACTIONS argument to `tcsetattr'. */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#endif /* _PPC64_TERMBITS_H */ diff -Nru a/include/asm-ppc64/termios.h b/include/asm-ppc64/termios.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/termios.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,236 @@ +#ifndef _PPC64_TERMIOS_H +#define _PPC64_TERMIOS_H + +/* + * Liberally adapted from alpha/termios.h. In particular, the c_cc[] + * fields have been reordered so that termio & termios share the + * common subset in the same order (for brain dead programs that don't + * know or care about the differences). + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +struct sgttyb { + char sg_ispeed; + char sg_ospeed; + char sg_erase; + char sg_kill; + short sg_flags; +}; + +struct tchars { + char t_intrc; + char t_quitc; + char t_startc; + char t_stopc; + char t_eofc; + char t_brkc; +}; + +struct ltchars { + char t_suspc; + char t_dsuspc; + char t_rprntc; + char t_flushc; + char t_werasc; + char t_lnextc; +}; + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +#define NCC 10 +struct termio { + unsigned short c_iflag; /* input mode flags */ + unsigned short c_oflag; /* output mode flags */ + unsigned short c_cflag; /* control mode flags */ + unsigned short c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control characters */ +}; + +/* c_cc characters */ +#define _VINTR 0 +#define _VQUIT 1 +#define _VERASE 2 +#define _VKILL 3 +#define _VEOF 4 +#define _VMIN 5 +#define _VEOL 6 +#define _VTIME 7 +#define _VEOL2 8 +#define _VSWTC 9 + +/* line disciplines */ +#define N_TTY 0 +#define N_SLIP 1 +#define N_MOUSE 2 +#define N_PPP 3 +#define N_STRIP 4 +#define N_AX25 5 +#define N_X25 6 /* X.25 async */ +#define N_6PACK 7 +#define N_MASC 8 /* Reserved for Mobitex module */ +#define N_R3964 9 /* Reserved for Simatic R3964 module */ +#define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ +#define N_IRDA 11 /* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */ +#define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ +#define N_SYNC_PPP 14 + +#ifdef __KERNEL__ +/* ^C ^\ del ^U ^D 1 0 0 0 0 ^W ^R ^Z ^Q ^S ^V ^U */ +#define INIT_C_CC "\003\034\177\025\004\001\000\000\000\000\027\022\032\021\023\026\025" +#endif + +#define FIOCLEX _IO('f', 1) +#define FIONCLEX _IO('f', 2) +#define FIOASYNC _IOW('f', 125, int) +#define FIONBIO _IOW('f', 126, int) +#define FIONREAD _IOR('f', 127, int) +#define TIOCINQ FIONREAD + +#define TIOCGETP _IOR('t', 8, struct sgttyb) +#define TIOCSETP _IOW('t', 9, struct sgttyb) +#define TIOCSETN _IOW('t', 10, struct sgttyb) /* TIOCSETP wo flush */ + +#define TIOCSETC _IOW('t', 17, struct tchars) +#define TIOCGETC _IOR('t', 18, struct tchars) +#define TCGETS _IOR('t', 19, struct termios) +#define TCSETS _IOW('t', 20, struct termios) +#define TCSETSW _IOW('t', 21, struct termios) +#define TCSETSF _IOW('t', 22, struct termios) + +#define TCGETA _IOR('t', 23, struct termio) +#define TCSETA _IOW('t', 24, struct termio) +#define TCSETAW _IOW('t', 25, struct termio) +#define TCSETAF _IOW('t', 28, struct termio) + +#define TCSBRK _IO('t', 29) +#define TCXONC _IO('t', 30) +#define TCFLSH _IO('t', 31) + +#define TIOCSWINSZ _IOW('t', 103, struct winsize) +#define TIOCGWINSZ _IOR('t', 104, struct winsize) +#define TIOCSTART _IO('t', 110) /* start output, like ^Q */ +#define TIOCSTOP _IO('t', 111) /* stop output, like ^S */ +#define TIOCOUTQ _IOR('t', 115, int) /* output queue size */ + +#define TIOCGLTC _IOR('t', 116, struct ltchars) +#define TIOCSLTC _IOW('t', 117, struct ltchars) +#define TIOCSPGRP _IOW('t', 118, int) +#define TIOCGPGRP _IOR('t', 119, int) + +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D +#define TIOCSCTTY 0x540E + +#define TIOCSTI 0x5412 +#define TIOCMGET 0x5415 +#define TIOCMBIS 0x5416 +#define TIOCMBIC 0x5417 +#define TIOCMSET 0x5418 +#define TIOCGSOFTCAR 0x5419 +#define TIOCSSOFTCAR 0x541A +#define TIOCLINUX 0x541C +#define TIOCCONS 0x541D +#define TIOCGSERIAL 0x541E +#define TIOCSSERIAL 0x541F +#define TIOCPKT 0x5420 + +#define TIOCNOTTY 0x5422 +#define TIOCSETD 0x5423 +#define TIOCGETD 0x5424 +#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TIOCTTYGSTRUCT 0x5426 /* For debugging only */ + +#define TIOCSERCONFIG 0x5453 +#define TIOCSERGWILD 0x5454 +#define TIOCSERSWILD 0x5455 +#define TIOCGLCKTRMIOS 0x5456 +#define TIOCSLCKTRMIOS 0x5457 +#define TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TIOCSERGETLSR 0x5459 /* Get line status register */ +#define TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ +#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ + +/* Used for packet mode */ +#define TIOCPKT_DATA 0 +#define TIOCPKT_FLUSHREAD 1 +#define TIOCPKT_FLUSHWRITE 2 +#define TIOCPKT_STOP 4 +#define TIOCPKT_START 8 +#define TIOCPKT_NOSTOP 16 +#define TIOCPKT_DOSTOP 32 + +/* modem lines */ +#define TIOCM_LE 0x001 +#define TIOCM_DTR 0x002 +#define TIOCM_RTS 0x004 +#define TIOCM_ST 0x008 +#define TIOCM_SR 0x010 +#define TIOCM_CTS 0x020 +#define TIOCM_CAR 0x040 +#define TIOCM_RNG 0x080 +#define TIOCM_DSR 0x100 +#define TIOCM_CD TIOCM_CAR +#define TIOCM_RI TIOCM_RNG +#define TIOCM_OUT1 0x2000 +#define TIOCM_OUT2 0x4000 +#define TIOCM_LOOP 0x8000 + +/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ +#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ + +#ifdef __KERNEL__ + +/* + * Translate a "termio" structure into a "termios". Ugh. + */ +#define SET_LOW_TERMIOS_BITS(termios, termio, x) { \ + unsigned short __tmp; \ + get_user(__tmp,&(termio)->x); \ + (termios)->x = (0xffff0000 & (termios)->x) | __tmp; \ +} + +#define user_termio_to_kernel_termios(termios, termio) \ +({ \ + SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); \ + copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \ +}) + +/* + * Translate a "termios" structure into a "termio". Ugh. + */ +#define kernel_termios_to_user_termio(termio, termios) \ +({ \ + put_user((termios)->c_iflag, &(termio)->c_iflag); \ + put_user((termios)->c_oflag, &(termio)->c_oflag); \ + put_user((termios)->c_cflag, &(termio)->c_cflag); \ + put_user((termios)->c_lflag, &(termio)->c_lflag); \ + put_user((termios)->c_line, &(termio)->c_line); \ + copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \ +}) + +#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios)) +#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios)) + +#endif /* __KERNEL__ */ + +#endif /* _PPC64_TERMIOS_H */ diff -Nru a/include/asm-ppc64/thread_info.h b/include/asm-ppc64/thread_info.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/thread_info.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,93 @@ +/* thread_info.h: PPC low-level thread information + * adapted from the i386 version by Paul Mackerras + * + * Copyright (C) 2002 David Howells (dhowells@redhat.com) + * - Incorporating suggestions made by Linus Torvalds and Dave Miller + */ + +#ifndef _ASM_THREAD_INFO_H +#define _ASM_THREAD_INFO_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ +#include +#include + +/* + * low level task data. + */ +struct thread_info { + struct task_struct *task; /* main task structure */ + struct exec_domain *exec_domain; /* execution domain */ + unsigned long flags; /* low level flags */ + int cpu; /* cpu we're on */ +}; + +/* + * macros/functions for gaining access to the thread information structure + */ +#define INIT_THREAD_INFO(tsk) \ +{ \ + task: &tsk, \ + exec_domain: &default_exec_domain, \ + flags: 0, \ + cpu: 0, \ +} + +#define init_thread_info (init_thread_union.thread_info) +#define init_stack (init_thread_union.stack) + +/* thread information allocation */ + +#define THREAD_ORDER 2 +#define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER) +#define THREAD_SHIFT (PAGE_SHIFT + THREAD_ORDER) + +#define alloc_thread_info() ((struct thread_info *) \ + __get_free_pages(GFP_KERNEL, THREAD_ORDER)) +#define free_thread_info(ti) free_pages((unsigned long) (ti), THREAD_ORDER) +#define get_thread_info(ti) get_task_struct((ti)->task) +#define put_thread_info(ti) put_task_struct((ti)->task) + +#if THREAD_SIZE != (4*PAGE_SIZE) +#error update vmlinux.lds and current_thread_info to match +#endif + +/* how to get the thread information struct from C */ +static inline struct thread_info *current_thread_info(void) +{ + struct thread_info *ti; + __asm__("clrrdi %0,1,14" : "=r"(ti)); + return ti; +} + +#endif /* __ASSEMBLY__ */ + +/* + * thread information flag bit numbers + */ +#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ +#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ +#define TIF_SIGPENDING 2 /* signal pending */ +#define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_POLLING_NRFLAG 4 /* true if poll_idle() is polling + TIF_NEED_RESCHED */ +#define TIF_32BIT 5 /* 32 bit binary */ +#define TIF_RUN_LIGHT 6 /* iSeries run light */ + +/* as above, but as bit values */ +#define _TIF_SYSCALL_TRACE (1< +#include + +#include +#include +#include + +/* time.c */ +extern unsigned long tb_ticks_per_jiffy; +extern unsigned long tb_ticks_per_usec; +extern unsigned long tb_ticks_per_sec; +extern unsigned long tb_to_xs; +extern unsigned tb_to_us; +extern unsigned long tb_last_stamp; + +struct rtc_time; +extern void to_tm(int tim, struct rtc_time * tm); +extern time_t last_rtc_update; + +/* + * By putting all of this stuff into a single struct we + * reduce the number of cache lines touched by do_gettimeofday. + * Both by collecting all of the data in one cache line and + * by touching only one TOC entry + */ +struct gettimeofday_vars { + unsigned long tb_to_xs; + unsigned long stamp_xsec; +}; + +struct gettimeofday_struct { + unsigned long tb_orig_stamp; + unsigned long tb_ticks_per_sec; + struct gettimeofday_vars vars[2]; + struct gettimeofday_vars * volatile varp; + unsigned tb_to_us; +}; + +struct div_result { + unsigned long result_high; + unsigned long result_low; +}; + +int via_calibrate_decr(void); + +static __inline__ unsigned long get_tb(void) +{ + return mftb(); +} + +/* Accessor functions for the decrementer register. */ +static __inline__ unsigned int get_dec(void) +{ + return (mfspr(SPRN_DEC)); +} + +static __inline__ void set_dec(int val) +{ + struct Paca * paca; + int cur_dec; + + paca = (struct Paca *)mfspr(SPRG3); + if ( paca->xLpPaca.xSharedProc ) { + paca->xLpPaca.xVirtualDecr = val; + cur_dec = get_dec(); + if ( cur_dec > val ) + HvCall_setVirtualDecr(); + } + else + mtspr(SPRN_DEC, val); +} + +extern __inline__ unsigned long tb_ticks_since(unsigned long tstamp) { + return get_tb() - tstamp; +} + +#define mulhwu(x,y) \ +({unsigned z; asm ("mulhwu %0,%1,%2" : "=r" (z) : "r" (x), "r" (y)); z;}) +#define mulhdu(x,y) \ +({unsigned long z; asm ("mulhdu %0,%1,%2" : "=r" (z) : "r" (x), "r" (y)); z;}) + + +unsigned mulhwu_scale_factor(unsigned, unsigned); +void div128_by_32( unsigned long dividend_high, unsigned long dividend_low, + unsigned divisor, struct div_result *dr ); +#endif /* __KERNEL__ */ +#endif /* __PPC64_TIME_H */ diff -Nru a/include/asm-ppc64/timex.h b/include/asm-ppc64/timex.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/timex.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,31 @@ +/* + * linux/include/asm-ppc/timex.h + * + * PPC64 architecture timex specifications + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _ASMPPC64_TIMEX_H +#define _ASMPPC64_TIMEX_H + +#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ +#define CLOCK_TICK_FACTOR 20 /* Factor of both 1000000 and CLOCK_TICK_RATE */ +#define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \ + (1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \ + << (SHIFT_SCALE-SHIFT_HZ)) / HZ) + +typedef unsigned long cycles_t; +extern cycles_t cacheflush_time; + +static inline cycles_t get_cycles(void) +{ + cycles_t ret; + + __asm__ __volatile__("mftb %0" : "=r" (ret) : ); + return ret; +} + +#endif diff -Nru a/include/asm-ppc64/tlb.h b/include/asm-ppc64/tlb.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/tlb.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1 @@ +#include diff -Nru a/include/asm-ppc64/types.h b/include/asm-ppc64/types.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/types.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,68 @@ +#ifndef _PPC64_TYPES_H +#define _PPC64_TYPES_H + +#ifndef __ASSEMBLY__ + +/* + * This file is never included by application software unless + * explicitly requested (e.g., via linux/types.h) in which case the + * application is Linux specific so (user-) name space pollution is + * not a major issue. However, for interoperability, libraries still + * need to be careful to avoid a name clashes. + * + * 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. + */ + +typedef unsigned int umode_t; + +/* + * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the + * header files exported to user space + */ + +typedef __signed__ char __s8; +typedef unsigned char __u8; + +typedef __signed__ short __s16; +typedef unsigned short __u16; + +typedef __signed__ int __s32; +typedef unsigned int __u32; + +typedef __signed__ long __s64; +typedef unsigned long __u64; + +typedef struct { + __u32 u[4]; +} __attribute((aligned(16))) __vector128; + +#ifdef __KERNEL__ +/* + * These aren't exported outside the kernel to avoid name space clashes + */ +typedef signed char s8; +typedef unsigned char u8; + +typedef signed short s16; +typedef unsigned short u16; + +typedef signed int s32; +typedef unsigned int u32; + +typedef signed long s64; +typedef unsigned long u64; + +typedef __vector128 vector128; + +#define BITS_PER_LONG 64 + +typedef u32 dma_addr_t; +typedef u64 dma64_addr_t; + +#endif /* __KERNEL__ */ +#endif /* __ASSEMBLY__ */ + +#endif /* _PPC64_TYPES_H */ diff -Nru a/include/asm-ppc64/uaccess.h b/include/asm-ppc64/uaccess.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/uaccess.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,285 @@ +#ifndef _PPC64_UACCESS_H +#define _PPC64_UACCESS_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __ASSEMBLY__ +#include +#include +#include + +#define VERIFY_READ 0 +#define VERIFY_WRITE 1 + +/* + * The fs value determines whether argument validity checking should be + * performed or not. If get_fs() == USER_DS, checking is performed, with + * get_fs() == KERNEL_DS, checking is bypassed. + * + * For historical reasons, these macros are grossly misnamed. + */ + +#define KERNEL_DS ((mm_segment_t) { 0 }) +#define USER_DS ((mm_segment_t) { 1 }) + +#define get_ds() (KERNEL_DS) +#define get_fs() (current->thread.fs) +#define set_fs(val) (current->thread.fs = (val)) + +#define segment_eq(a,b) ((a).seg == (b).seg) + +#define __kernel_ok (segment_eq(get_fs(), KERNEL_DS)) +#define __user_ok(addr,size) (((size) <= TASK_SIZE)&&((addr) <= TASK_SIZE-(size))) +#define __access_ok(addr,size) (__kernel_ok || __user_ok((addr),(size))) +#define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size)) + +extern inline int verify_area(int type, const void * addr, unsigned long size) +{ + return access_ok(type,addr,size) ? 0 : -EFAULT; +} + + +/* + * The exception table consists of pairs of addresses: the first is the + * address of an instruction that is allowed to fault, and the second is + * the address at which the program should continue. No registers are + * modified, so it is entirely up to the continuation code to figure out + * what to do. + * + * All the routines below use bits of fixup code that are out of line + * with the main instruction path. This means when everything is well, + * we don't even have to jump over them. Further, they do not intrude + * on our cache or tlb entries. + */ + +struct exception_table_entry +{ + unsigned long insn, fixup; +}; + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); +extern void sort_exception_table(void); + +/* + * These are the main single-value transfer routines. They automatically + * use the right size if we just have the right pointer type. + * + * This gets kind of ugly. We want to return _two_ values in "get_user()" + * and yet we don't want to do any pointers, because that is too much + * of a performance impact. Thus we have a few rather ugly macros here, + * and hide all the uglyness from the user. + * + * The "__xxx" versions of the user access functions are versions that + * do not verify the address space, that must have been done previously + * with a separate "access_ok()" call (this is used when we do multiple + * accesses to the same area of user memory). + * + * As we use the same address space for kernel and user data on the + * PowerPC, we can just do these as direct assignments. (Of course, the + * exception handling means that it's no longer "just"...) + */ +#define get_user(x,ptr) \ + __get_user_check((x),(ptr),sizeof(*(ptr))) +#define put_user(x,ptr) \ + __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) + +#define __get_user(x,ptr) \ + __get_user_nocheck((x),(ptr),sizeof(*(ptr))) +#define __put_user(x,ptr) \ + __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) + +extern long __put_user_bad(void); + +#define __put_user_nocheck(x,ptr,size) \ +({ \ + long __pu_err; \ + __put_user_size((x),(ptr),(size),__pu_err); \ + __pu_err; \ +}) + +#define __put_user_check(x,ptr,size) \ +({ \ + long __pu_err = -EFAULT; \ + __typeof__(*(ptr)) *__pu_addr = (ptr); \ + if (access_ok(VERIFY_WRITE,__pu_addr,size)) \ + __put_user_size((x),__pu_addr,(size),__pu_err); \ + __pu_err; \ +}) + +#define __put_user_size(x,ptr,size,retval) \ +do { \ + retval = 0; \ + switch (size) { \ + case 1: __put_user_asm(x,ptr,retval,"stb"); break; \ + case 2: __put_user_asm(x,ptr,retval,"sth"); break; \ + case 4: __put_user_asm(x,ptr,retval,"stw"); break; \ + case 8: __put_user_asm(x,ptr,retval,"std"); break; \ + default: __put_user_bad(); \ + } \ +} while (0) + +struct __large_struct { unsigned long buf[100]; }; +#define __m(x) (*(struct __large_struct *)(x)) + +/* + * We don't tell gcc that we are accessing memory, but this is OK + * because we do not write to any memory gcc knows about, so there + * are no aliasing issues. + */ +#define __put_user_asm(x, addr, err, op) \ + __asm__ __volatile__( \ + "1: "op" %1,0(%2)\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,%3\n" \ + " b 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 3\n" \ + " .llong 1b,3b\n" \ + ".previous" \ + : "=r"(err) \ + : "r"(x), "b"(addr), "i"(-EFAULT), "0"(err)) + + +#define __get_user_nocheck(x,ptr,size) \ +({ \ + long __gu_err, __gu_val; \ + __get_user_size(__gu_val,(ptr),(size),__gu_err); \ + (x) = (__typeof__(*(ptr)))__gu_val; \ + __gu_err; \ +}) + +#define __get_user_check(x,ptr,size) \ +({ \ + long __gu_err = -EFAULT, __gu_val = 0; \ + const __typeof__(*(ptr)) *__gu_addr = (ptr); \ + if (access_ok(VERIFY_READ,__gu_addr,size)) \ + __get_user_size(__gu_val,__gu_addr,(size),__gu_err); \ + (x) = (__typeof__(*(ptr)))__gu_val; \ + __gu_err; \ +}) + +extern long __get_user_bad(void); + +#define __get_user_size(x,ptr,size,retval) \ +do { \ + retval = 0; \ + switch (size) { \ + case 1: __get_user_asm(x,ptr,retval,"lbz"); break; \ + case 2: __get_user_asm(x,ptr,retval,"lhz"); break; \ + case 4: __get_user_asm(x,ptr,retval,"lwz"); break; \ + case 8: __get_user_asm(x,ptr,retval,"ld"); break; \ + default: (x) = __get_user_bad(); \ + } \ +} while (0) + +#define __get_user_asm(x, addr, err, op) \ + __asm__ __volatile__( \ + "1: "op" %1,0(%2)\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,%3\n" \ + " li %1,0\n" \ + " b 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 3\n" \ + " .llong 1b,3b\n" \ + ".previous" \ + : "=r"(err), "=r"(x) \ + : "b"(addr), "i"(-EFAULT), "0"(err)) + +/* more complex routines */ + +extern unsigned long __copy_tofrom_user(void *to, const void *from, unsigned long size); + +extern inline unsigned long +copy_from_user(void *to, const void *from, unsigned long n) +{ + unsigned long over; + + if (access_ok(VERIFY_READ, from, n)) + return __copy_tofrom_user(to, from, n); + if ((unsigned long)from < TASK_SIZE) { + over = (unsigned long)from + n - TASK_SIZE; + return __copy_tofrom_user(to, from, n - over) + over; + } + return n; +} + +extern inline unsigned long +copy_to_user(void *to, const void *from, unsigned long n) +{ + unsigned long over; + + if (access_ok(VERIFY_WRITE, to, n)) + return __copy_tofrom_user(to, from, n); + if ((unsigned long)to < TASK_SIZE) { + over = (unsigned long)to + n - TASK_SIZE; + return __copy_tofrom_user(to, from, n - over) + over; + } + return n; +} + +#define __copy_from_user(to, from, size) \ + __copy_tofrom_user((to), (from), (size)) +#define __copy_to_user(to, from, size) \ + __copy_tofrom_user((to), (from), (size)) + +extern unsigned long __clear_user(void *addr, unsigned long size); + +extern inline unsigned long +clear_user(void *addr, unsigned long size) +{ + if (access_ok(VERIFY_WRITE, addr, size)) + return __clear_user(addr, size); + return size? -EFAULT: 0; +} + +extern int __strncpy_from_user(char *dst, const char *src, long count); + +extern inline long +strncpy_from_user(char *dst, const char *src, long count) +{ + if (access_ok(VERIFY_READ, src, 1)) + return __strncpy_from_user(dst, src, count); + return -EFAULT; +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 for error + */ + +extern int __strnlen_user(const char *str, long len, unsigned long top); + +/* + * Returns the length of the string at str (including the null byte), + * or 0 if we hit a page we can't access, + * or something > len if we didn't find a null byte. + * + * The `top' parameter to __strnlen_user is to make sure that + * we can never overflow from the user area into kernel space. + */ +extern __inline__ int strnlen_user(const char *str, long len) +{ + unsigned long top = __kernel_ok? ~0UL: TASK_SIZE - 1; + + if ((unsigned long)str > top) + return 0; + return __strnlen_user(str, len, top); +} + +#define strlen_user(str) strnlen_user((str), 0x7ffffffe) + +#endif /* __ASSEMBLY__ */ + +#endif /* _PPC64_UACCESS_H */ diff -Nru a/include/asm-ppc64/ucontext.h b/include/asm-ppc64/ucontext.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/ucontext.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,20 @@ +#ifndef _ASMPPC64_UCONTEXT_H +#define _ASMPPC64_UCONTEXT_H + +/* Copied from i386. + * + * 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. + */ + +struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext_struct uc_mcontext; + sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +#endif /* _ASMPPC64_UCONTEXT_H */ diff -Nru a/include/asm-ppc64/udbg.h b/include/asm-ppc64/udbg.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/udbg.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,27 @@ +#ifndef __UDBG_HDR +#define __UDBG_HDR + +/* + * c 2001 PPC 64 Team, IBM Corp + * + * 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. + */ + +void udbg_init_uart(void *comport); +void udbg_putc(unsigned char c); +unsigned char udbg_getc(void); +int udbg_getc_poll(void); +void udbg_puts(const char *s); +int udbg_write(const char *s, int n); +int udbg_read(char *buf, int buflen); +void udbg_puthex(unsigned long val); +void udbg_printSP(const char *s); +void udbg_printf(const char *fmt, ...); +void udbg_ppcdbg(unsigned long flags, const char *fmt, ...); +unsigned long udbg_ifdebug(unsigned long flags); + +void udbg_init_uart(void *comport); +#endif diff -Nru a/include/asm-ppc64/unaligned.h b/include/asm-ppc64/unaligned.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/unaligned.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,21 @@ +#ifndef __PPC64_UNALIGNED_H +#define __PPC64_UNALIGNED_H + +/* + * The PowerPC can do unaligned accesses itself in big endian mode. + * + * The strange macros are there to make sure these can't + * be misused in a way that makes them not work on other + * architectures where unaligned accesses aren't as simple. + * + * 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. + */ + +#define get_unaligned(ptr) (*(ptr)) + +#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) )) + +#endif /* __PPC64_UNALIGNED_H */ diff -Nru a/include/asm-ppc64/unistd.h b/include/asm-ppc64/unistd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/unistd.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,445 @@ +#ifndef _ASM_PPC_UNISTD_H_ +#define _ASM_PPC_UNISTD_H_ + +/* + * This file contains the system call numbers. + * + * 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. + */ + +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_lchown 16 +#define __NR_break 17 +#define __NR_oldstat 18 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_oldfstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_stty 31 +#define __NR_gtty 32 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_ftime 35 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_prof 44 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_umount2 52 +#define __NR_lock 53 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_mpx 56 +#define __NR_setpgid 57 +#define __NR_ulimit 58 +#define __NR_oldolduname 59 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sgetmask 68 +#define __NR_ssetmask 69 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_select 82 +#define __NR_symlink 83 +#define __NR_oldlstat 84 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_profil 98 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_ioperm 101 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_olduname 109 +#define __NR_iopl 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_vm86 113 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_modify_ldt 123 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#define __NR__llseek 140 +#define __NR_getdents 141 +#define __NR__newselect 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR__sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_query_module 166 +#define __NR_poll 167 +#define __NR_nfsservctl 168 +#define __NR_setresgid 169 +#define __NR_getresgid 170 +#define __NR_prctl 171 +#define __NR_rt_sigreturn 172 +#define __NR_rt_sigaction 173 +#define __NR_rt_sigprocmask 174 +#define __NR_rt_sigpending 175 +#define __NR_rt_sigtimedwait 176 +#define __NR_rt_sigqueueinfo 177 +#define __NR_rt_sigsuspend 178 +#define __NR_pread 179 +#define __NR_pwrite 180 +#define __NR_chown 181 +#define __NR_getcwd 182 +#define __NR_capget 183 +#define __NR_capset 184 +#define __NR_sigaltstack 185 +#define __NR_sendfile 186 +#define __NR_getpmsg 187 /* some people actually want streams */ +#define __NR_putpmsg 188 /* some people actually want streams */ +#define __NR_vfork 189 +#define __NR_ugetrlimit 190 /* SuS compliant getrlimit */ +#define __NR_mmap2 192 +#define __NR_truncate64 193 +#define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 +#define __NR_pciconfig_read 198 +#define __NR_pciconfig_write 199 +#define __NR_pciconfig_iobase 200 +#define __NR_multiplexer 201 +#define __NR_getdents64 202 +#define __NR_pivot_root 203 +#define __NR_fcntl64 204 +#define __NR_madvise 205 +#define __NR_mincore 206 +#define __NR_gettid 207 +#define __NR_tkill 208 + +#if 0 /* Remind paulus to add these into ppc32 */ +__NR_security +__NR_readahead +__NR_setxattr +__NR_lsetxattr +__NR_fsetxattr +__NR_getxattr +__NR_lgetxattr +__NR_fgetxattr +__NR_listxattr +__NR_llistxattr +__NR_flistxattr +__NR_removexattr +__NR_lremovexattr +__NR_fremovexattr +#endif + +#define __NR(n) #n + + +#define __syscall_return(type) \ + return (__sc_err & 0x10000000 ? errno = __sc_ret, __sc_ret = -1 : 0), \ + (type) __sc_ret + +#define __syscall_clobbers \ + "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" + +#define _syscall0(type,name) \ +type name(void) \ +{ \ + unsigned long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + \ + __sc_0 = __NR_##name; \ + __asm__ __volatile__ \ + ("sc \n\t" \ + "mfcr %1 " \ + : "=&r" (__sc_3), "=&r" (__sc_0) \ + : "0" (__sc_3), "1" (__sc_0) \ + : __syscall_clobbers); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + __syscall_return (type); \ +} + +#define _syscall1(type,name,type1,arg1) \ +type name(type1 arg1) \ +{ \ + unsigned long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + \ + __sc_3 = (unsigned long) (arg1); \ + __sc_0 = __NR_##name; \ + __asm__ __volatile__ \ + ("sc \n\t" \ + "mfcr %1 " \ + : "=&r" (__sc_3), "=&r" (__sc_0) \ + : "0" (__sc_3), "1" (__sc_0) \ + : __syscall_clobbers); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + __syscall_return (type); \ +} + +#define _syscall2(type,name,type1,arg1,type2,arg2) \ +type name(type1 arg1, type2 arg2) \ +{ \ + unsigned long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + register unsigned long __sc_4 __asm__ ("r4"); \ + \ + __sc_3 = (unsigned long) (arg1); \ + __sc_4 = (unsigned long) (arg2); \ + __sc_0 = __NR_##name; \ + __asm__ __volatile__ \ + ("sc \n\t" \ + "mfcr %1 " \ + : "=&r" (__sc_3), "=&r" (__sc_0) \ + : "0" (__sc_3), "1" (__sc_0), \ + "r" (__sc_4) \ + : __syscall_clobbers); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + __syscall_return (type); \ +} + +#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ +type name(type1 arg1, type2 arg2, type3 arg3) \ +{ \ + unsigned long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + \ + __sc_3 = (unsigned long) (arg1); \ + __sc_4 = (unsigned long) (arg2); \ + __sc_5 = (unsigned long) (arg3); \ + __sc_0 = __NR_##name; \ + __asm__ __volatile__ \ + ("sc \n\t" \ + "mfcr %1 " \ + : "=&r" (__sc_3), "=&r" (__sc_0) \ + : "0" (__sc_3), "1" (__sc_0), \ + "r" (__sc_4), \ + "r" (__sc_5) \ + : __syscall_clobbers); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + __syscall_return (type); \ +} + +#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ +type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ + unsigned long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + \ + __sc_3 = (unsigned long) (arg1); \ + __sc_4 = (unsigned long) (arg2); \ + __sc_5 = (unsigned long) (arg3); \ + __sc_6 = (unsigned long) (arg4); \ + __sc_0 = __NR_##name; \ + __asm__ __volatile__ \ + ("sc \n\t" \ + "mfcr %1 " \ + : "=&r" (__sc_3), "=&r" (__sc_0) \ + : "0" (__sc_3), "1" (__sc_0), \ + "r" (__sc_4), \ + "r" (__sc_5), \ + "r" (__sc_6) \ + : __syscall_clobbers); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + __syscall_return (type); \ +} + +#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) \ +type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ +{ \ + unsigned long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + \ + __sc_3 = (unsigned long) (arg1); \ + __sc_4 = (unsigned long) (arg2); \ + __sc_5 = (unsigned long) (arg3); \ + __sc_6 = (unsigned long) (arg4); \ + __sc_7 = (unsigned long) (arg5); \ + __sc_0 = __NR_##name; \ + __asm__ __volatile__ \ + ("sc \n\t" \ + "mfcr %1 " \ + : "=&r" (__sc_3), "=&r" (__sc_0) \ + : "0" (__sc_3), "1" (__sc_0), \ + "r" (__sc_4), \ + "r" (__sc_5), \ + "r" (__sc_6), \ + "r" (__sc_7) \ + : __syscall_clobbers); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + __syscall_return (type); \ +} + + +#ifdef __KERNEL_SYSCALLS__ + +/* + * Forking from kernel space will result in the child getting a new, + * empty kernel stack area. Thus the child cannot access automatic + * variables set in the parent unless they are in registers, and the + * procedure where the fork was done cannot return to its caller in + * the child. + */ + +/* + * System call prototypes. + */ +#define __NR__exit __NR_exit +static inline _syscall0(int,pause) +static inline _syscall0(int,sync) +static inline _syscall0(pid_t,setsid) +static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) +static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) +static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count) +static inline _syscall1(int,dup,int,fd) +static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) +static inline _syscall3(int,open,const char *,file,int,flag,int,mode) +static inline _syscall1(int,close,int,fd) +static inline _syscall1(int,_exit,int,exitcode) +static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) +static inline _syscall1(int,delete_module,const char *,name) + +static inline pid_t wait(int * wait_stat) +{ + return waitpid(-1,wait_stat,0); +} + +#endif /* __KERNEL_SYSCALLS__ */ + +#endif /* _ASM_PPC_UNISTD_H_ */ diff -Nru a/include/asm-ppc64/user.h b/include/asm-ppc64/user.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/user.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,58 @@ +#ifndef _PPC_USER_H +#define _PPC_USER_H + +/* Adapted from + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +/* + * Core file format: The core file is written in such a way that gdb + * can understand it and provide useful information to the user (under + * linux we use the `trad-core' bfd, NOT the osf-core). The file contents + * are as follows: + * + * upage: 1 page consisting of a user struct that tells gdb + * what is present in the file. Directly after this is a + * copy of the task_struct, which is currently not used by gdb, + * but it may come in handy at some point. All of the registers + * are stored as part of the upage. The upage should always be + * only one page long. + * data: The data segment follows next. We use current->end_text to + * current->brk to pick up all of the user variables, plus any memory + * that may have been sbrk'ed. No attempt is made to determine if a + * page is demand-zero or if a page is totally unused, we just cover + * the entire range. All of the addresses are rounded in such a way + * that an integral number of pages is written. + * stack: We need the stack information in order to get a meaningful + * backtrace. We need to write the data from usp to + * current->start_stack, so we round each of these in order to be able + * to write an integer number of pages. + */ +struct user { + struct pt_regs regs; /* entire machine state */ + size_t u_tsize; /* text size (pages) */ + size_t u_dsize; /* data size (pages) */ + size_t u_ssize; /* stack size (pages) */ + unsigned long start_code; /* text starting address */ + unsigned long start_data; /* data starting address */ + unsigned long start_stack; /* stack starting address */ + long int signal; /* signal causing core dump */ + struct regs * u_ar0; /* help gdb find registers */ + unsigned long magic; /* identifies a core file */ + char u_comm[32]; /* user command name */ +}; + +#define NBPG PAGE_SIZE +#define UPAGES 1 +#define HOST_TEXT_START_ADDR (u.start_code) +#define HOST_DATA_START_ADDR (u.start_data) +#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) + +#endif /* _PPC_USER_H */ diff -Nru a/include/asm-ppc64/vc_ioctl.h b/include/asm-ppc64/vc_ioctl.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/vc_ioctl.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,50 @@ +#ifndef _LINUX_VC_IOCTL_H +#define _LINUX_VC_IOCTL_H + +/* + * 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. + */ + +struct vc_mode { + int height; + int width; + int depth; + int pitch; + int mode; + char name[32]; + unsigned long fb_address; + unsigned long cmap_adr_address; + unsigned long cmap_data_address; + unsigned long disp_reg_address; +}; + +#define VC_GETMODE 0x7667 +#define VC_SETMODE 0x7668 +#define VC_INQMODE 0x7669 + +#define VC_SETCMAP 0x766a +#define VC_GETCMAP 0x766b + +#define VC_POWERMODE 0x766c + +/* Values for the argument to the VC_POWERMODE ioctl */ +#define VC_POWERMODE_INQUIRY (-1) +#define VESA_NO_BLANKING 0 +#define VESA_VSYNC_SUSPEND 1 +#define VESA_HSYNC_SUSPEND 2 +#define VESA_POWERDOWN 3 + +#ifdef __KERNEL__ +extern int console_getmode(struct vc_mode *); +extern int console_setmode(struct vc_mode *, int); +extern int console_setcmap(int, unsigned char *, unsigned char *, + unsigned char *); +extern int console_powermode(int); +extern struct vc_mode display_info; +extern struct fb_info *console_fb_info; +#endif + +#endif /* _LINUX_VC_IOCTL_H */ diff -Nru a/include/asm-ppc64/vga.h b/include/asm-ppc64/vga.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/vga.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,50 @@ +/* + * Access to VGA videoram + * + * (c) 1998 Martin Mares + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_ASM_VGA_H_ +#define _LINUX_ASM_VGA_H_ + +#include + +#include + +#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_MDA_CONSOLE) + +#define VT_BUF_HAVE_RW +/* + * These are only needed for supporting VGA or MDA text mode, which use little + * endian byte ordering. + * In other cases, we can optimize by using native byte ordering and + * has already done the right job for us. + */ + +extern inline void scr_writew(u16 val, volatile u16 *addr) +{ + st_le16(addr, val); +} + +extern inline u16 scr_readw(volatile const u16 *addr) +{ + return ld_le16(addr); +} + +#define VT_BUF_HAVE_MEMCPYW +#define scr_memcpyw memcpy + +#endif /* !CONFIG_VGA_CONSOLE && !CONFIG_MDA_CONSOLE */ + +extern unsigned long vgacon_remap_base; +#define VGA_MAP_MEM(x) ((unsigned long) ioremap((x), 0)) + +#define vga_readb(x) (*(x)) +#define vga_writeb(x,y) (*(y) = (x)) + +#endif diff -Nru a/include/asm-ppc64/xor.h b/include/asm-ppc64/xor.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-ppc64/xor.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1 @@ +#include diff -Nru a/include/asm-s390/page.h b/include/asm-s390/page.h --- a/include/asm-s390/page.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-s390/page.h Tue Feb 19 18:08:58 2002 @@ -120,6 +120,9 @@ #define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT)) #define VALID_PAGE(page) ((page - mem_map) < max_mapnr) +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + #endif /* __KERNEL__ */ #endif /* _S390_PAGE_H */ diff -Nru a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h --- a/include/asm-s390/pgtable.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-s390/pgtable.h Tue Feb 19 18:08:58 2002 @@ -42,6 +42,7 @@ #define flush_dcache_page(page) do { } while (0) #define flush_icache_range(start, end) do { } while (0) #define flush_icache_page(vma,pg) do { } while (0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) /* * The S390 doesn't have any external MMU info: the kernel page @@ -238,10 +239,6 @@ *pteptr = pteval; } -/* - * Permanent address of a page. - */ -#define page_address(page) ((page)->virtual) #define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) /* diff -Nru a/include/asm-s390x/page.h b/include/asm-s390x/page.h --- a/include/asm-s390x/page.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-s390x/page.h Tue Feb 19 18:08:57 2002 @@ -117,6 +117,9 @@ #define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT)) #define VALID_PAGE(page) ((page - mem_map) < max_mapnr) +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + #endif /* __KERNEL__ */ #endif /* _S390_PAGE_H */ diff -Nru a/include/asm-s390x/pgtable.h b/include/asm-s390x/pgtable.h --- a/include/asm-s390x/pgtable.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-s390x/pgtable.h Tue Feb 19 18:08:59 2002 @@ -38,6 +38,7 @@ #define flush_dcache_page(page) do { } while (0) #define flush_icache_range(start, end) do { } while (0) #define flush_icache_page(vma,pg) do { } while (0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) /* * The S390 doesn't have any external MMU info: the kernel page @@ -233,10 +234,6 @@ *pteptr = pteval; } -/* - * Permanent address of a page. - */ -#define page_address(page) ((page)->virtual) #define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) /* diff -Nru a/include/asm-sh/pgtable.h b/include/asm-sh/pgtable.h --- a/include/asm-sh/pgtable.h Tue Feb 19 18:09:00 2002 +++ b/include/asm-sh/pgtable.h Tue Feb 19 18:09:00 2002 @@ -41,6 +41,7 @@ #define flush_dcache_page(page) do { } while (0) #define flush_icache_range(start, end) do { } while (0) #define flush_icache_page(vma,pg) do { } while (0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) #define flush_cache_sigtramp(vaddr) do { } while (0) #define p3_cache_init() do { } while (0) @@ -64,6 +65,7 @@ #define flush_page_to_ram(page) do { } while (0) #define flush_icache_page(vma,pg) do { } while (0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) /* Initialization of P3 area for copy_user_page */ extern void p3_cache_init(void); @@ -206,11 +208,6 @@ #define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0) #define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE) -/* - * Permanent address of a page. Obviously must never be - * called on a highmem page. - */ -#define page_address(page) ((page)->virtual) /* P1 address of the page */ #define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) #define pte_page(x) phys_to_page(pte_val(x)&PTE_PHYS_MASK) diff -Nru a/include/asm-sparc/page.h b/include/asm-sparc/page.h --- a/include/asm-sparc/page.h Tue Feb 19 18:08:58 2002 +++ b/include/asm-sparc/page.h Tue Feb 19 18:08:58 2002 @@ -179,6 +179,9 @@ #define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT)) #define VALID_PAGE(page) ((page - mem_map) < max_mapnr) +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + #endif /* __KERNEL__ */ #endif /* _SPARC_PAGE_H */ diff -Nru a/include/asm-sparc/pgtable.h b/include/asm-sparc/pgtable.h --- a/include/asm-sparc/pgtable.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-sparc/pgtable.h Tue Feb 19 18:08:57 2002 @@ -293,9 +293,6 @@ #define page_pte_prot(page, prot) mk_pte(page, prot) #define page_pte(page) page_pte_prot(page, __pgprot(0)) -/* Permanent address of a page. */ -#define page_address(page) ((page)->virtual) - BTFIXUPDEF_CALL(struct page *, pte_page, pte_t) #define pte_page(pte) BTFIXUP_CALL(pte_page)(pte) @@ -348,6 +345,7 @@ extern unsigned int pg_iobits; #define flush_icache_page(vma, pg) do { } while(0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) /* Certain architectures need to do special things when pte's * within a page table are directly modified. Thus, the following diff -Nru a/include/asm-sparc64/irq.h b/include/asm-sparc64/irq.h --- a/include/asm-sparc64/irq.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-sparc64/irq.h Tue Feb 19 18:08:57 2002 @@ -12,6 +12,7 @@ #include #include #include +#include /* You should not mess with this directly. That's the job of irq.c. * diff -Nru a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h --- a/include/asm-sparc64/page.h Tue Feb 19 18:08:59 2002 +++ b/include/asm-sparc64/page.h Tue Feb 19 18:08:59 2002 @@ -30,6 +30,9 @@ #define PAGE_BUG(page) BUG() +/* Sparc64 is slow at multiplication, we prefer to use some extra space. */ +#define WANT_PAGE_VIRTUAL 1 + extern void _clear_page(void *page); #define clear_page(X) _clear_page((void *)(X)) extern void clear_user_page(void *page, unsigned long vaddr); @@ -152,6 +155,9 @@ } #endif /* !(__ASSEMBLY__) */ + +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) #endif /* !(__KERNEL__) */ diff -Nru a/include/asm-sparc64/pgtable.h b/include/asm-sparc64/pgtable.h --- a/include/asm-sparc64/pgtable.h Tue Feb 19 18:08:57 2002 +++ b/include/asm-sparc64/pgtable.h Tue Feb 19 18:08:57 2002 @@ -243,8 +243,7 @@ #define pte_mkold(pte) (__pte(((pte_val(pte)<<1UL)>>1UL) & ~_PAGE_ACCESSED)) /* Permanent address of a page. */ -#define __page_address(page) ((page)->virtual) -#define page_address(page) ({ __page_address(page); }) +#define __page_address(page) page_address(page) #define pte_page(x) (mem_map+(((pte_val(x)&_PAGE_PADDR)-phys_base)>>PAGE_SHIFT)) @@ -277,6 +276,7 @@ extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t); #define flush_icache_page(vma, pg) do { } while(0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) /* Make a non-present pseudo-TTE. */ extern inline pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space) diff -Nru a/include/asm-x86_64/a.out.h b/include/asm-x86_64/a.out.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/a.out.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,30 @@ +#ifndef __X8664_A_OUT_H__ +#define __X8664_A_OUT_H__ + + +/* Note: a.out is not supported in 64bit mode. This is just here to + still let some old things compile. */ + +struct exec +{ + unsigned long a_info; /* Use macros N_MAGIC, etc for access */ + unsigned a_text; /* length of text, in bytes */ + unsigned a_data; /* length of data, in bytes */ + unsigned a_bss; /* length of uninitialized data area for file, in bytes */ + unsigned a_syms; /* length of symbol table data in file, in bytes */ + unsigned a_entry; /* start address */ + unsigned a_trsize; /* length of relocation info for text, in bytes */ + unsigned a_drsize; /* length of relocation info for data, in bytes */ +}; + +#define N_TRSIZE(a) ((a).a_trsize) +#define N_DRSIZE(a) ((a).a_drsize) +#define N_SYMSIZE(a) ((a).a_syms) + +#ifdef __KERNEL__ + +#define STACK_TOP TASK_SIZE + +#endif + +#endif /* __A_OUT_GNU_H__ */ diff -Nru a/include/asm-x86_64/apic.h b/include/asm-x86_64/apic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/apic.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,103 @@ +#ifndef __ASM_APIC_H +#define __ASM_APIC_H + +#include +#include +#include +#include + +#ifdef CONFIG_X86_LOCAL_APIC + +#define APIC_DEBUG 0 + +#if APIC_DEBUG +#define Dprintk(x...) printk(x) +#else +#define Dprintk(x...) +#endif + +/* + * Basic functions accessing APICs. + */ + +static __inline void apic_write(unsigned long reg, unsigned long v) +{ + *((volatile unsigned long *)(APIC_BASE+reg)) = v; +} + +static __inline void apic_write_atomic(unsigned long reg, unsigned long v) +{ + xchg((volatile unsigned long *)(APIC_BASE+reg), v); +} + +static __inline unsigned long apic_read(unsigned long reg) +{ + return *((volatile unsigned long *)(APIC_BASE+reg)); +} + +static __inline__ void apic_wait_icr_idle(void) +{ + do { } while ( apic_read( APIC_ICR ) & APIC_ICR_BUSY ); +} + +#ifdef CONFIG_X86_GOOD_APIC +# define FORCE_READ_AROUND_WRITE 0 +# define apic_read_around(x) +# define apic_write_around(x,y) apic_write((x),(y)) +#else +# define FORCE_READ_AROUND_WRITE 1 +# define apic_read_around(x) apic_read(x) +# define apic_write_around(x,y) apic_write_atomic((x),(y)) +#endif + +static inline void ack_APIC_irq(void) +{ + /* + * ack_APIC_irq() actually gets compiled as a single instruction: + * - a single rmw on Pentium/82489DX + * - a single write on P6+ cores (CONFIG_X86_GOOD_APIC) + * ... yummie. + */ + + /* Docs say use 0 for future compatibility */ + apic_write_around(APIC_EOI, 0); +} + +extern int get_maxlvt(void); +extern void clear_local_APIC(void); +extern void connect_bsp_APIC (void); +extern void disconnect_bsp_APIC (void); +extern void disable_local_APIC (void); +extern int verify_local_APIC (void); +extern void cache_APIC_registers (void); +extern void sync_Arb_IDs (void); +extern void init_bsp_APIC (void); +extern void setup_local_APIC (void); +extern void init_apic_mappings (void); +extern void smp_local_timer_interrupt (struct pt_regs * regs); +extern void setup_APIC_clocks (void); +extern void setup_apic_nmi_watchdog (void); +extern inline void nmi_watchdog_tick (struct pt_regs * regs); +extern int APIC_init_uniprocessor (void); +extern void disable_APIC_timer(void); +extern void enable_APIC_timer(void); + +extern struct pm_dev *apic_pm_register(pm_dev_t, unsigned long, pm_callback); +extern void apic_pm_unregister(struct pm_dev*); + +extern unsigned int apic_timer_irqs [NR_CPUS]; +extern int check_nmi_watchdog (void); + +extern unsigned int nmi_watchdog; +#define NMI_NONE 0 +#define NMI_IO_APIC 1 +#define NMI_LOCAL_APIC 2 +#define NMI_INVALID 3 + +#endif /* CONFIG_X86_LOCAL_APIC */ + +#define clustered_apic_mode 0 +#define esr_disable 0 +extern unsigned boot_cpu_id; + +#endif /* __ASM_APIC_H */ diff -Nru a/include/asm-x86_64/apicdef.h b/include/asm-x86_64/apicdef.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/apicdef.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,363 @@ +#ifndef __ASM_APICDEF_H +#define __ASM_APICDEF_H + +/* + * Constants for various Intel APICs. (local APIC, IOAPIC, etc.) + * + * Alan Cox , 1995. + * Ingo Molnar , 1999, 2000 + */ + +#define APIC_DEFAULT_PHYS_BASE 0xfee00000 + +#define APIC_ID 0x20 +#define APIC_ID_MASK (0x0F<<24) +#define GET_APIC_ID(x) (((x)>>24)&0x0F) +#define APIC_LVR 0x30 +#define APIC_LVR_MASK 0xFF00FF +#define GET_APIC_VERSION(x) ((x)&0xFF) +#define GET_APIC_MAXLVT(x) (((x)>>16)&0xFF) +#define APIC_INTEGRATED(x) ((x)&0xF0) +#define APIC_TASKPRI 0x80 +#define APIC_TPRI_MASK 0xFF +#define APIC_ARBPRI 0x90 +#define APIC_ARBPRI_MASK 0xFF +#define APIC_PROCPRI 0xA0 +#define APIC_EOI 0xB0 +#define APIC_EIO_ACK 0x0 /* Write this to the EOI register */ +#define APIC_RRR 0xC0 +#define APIC_LDR 0xD0 +#define APIC_LDR_MASK (0xFF<<24) +#define GET_APIC_LOGICAL_ID(x) (((x)>>24)&0xFF) +#define SET_APIC_LOGICAL_ID(x) (((x)<<24)) +#define APIC_ALL_CPUS 0xFF +#define APIC_DFR 0xE0 +#define APIC_SPIV 0xF0 +#define APIC_SPIV_FOCUS_DISABLED (1<<9) +#define APIC_SPIV_APIC_ENABLED (1<<8) +#define APIC_ISR 0x100 +#define APIC_TMR 0x180 +#define APIC_IRR 0x200 +#define APIC_ESR 0x280 +#define APIC_ESR_SEND_CS 0x00001 +#define APIC_ESR_RECV_CS 0x00002 +#define APIC_ESR_SEND_ACC 0x00004 +#define APIC_ESR_RECV_ACC 0x00008 +#define APIC_ESR_SENDILL 0x00020 +#define APIC_ESR_RECVILL 0x00040 +#define APIC_ESR_ILLREGA 0x00080 +#define APIC_ICR 0x300 +#define APIC_DEST_SELF 0x40000 +#define APIC_DEST_ALLINC 0x80000 +#define APIC_DEST_ALLBUT 0xC0000 +#define APIC_ICR_RR_MASK 0x30000 +#define APIC_ICR_RR_INVALID 0x00000 +#define APIC_ICR_RR_INPROG 0x10000 +#define APIC_ICR_RR_VALID 0x20000 +#define APIC_INT_LEVELTRIG 0x08000 +#define APIC_INT_ASSERT 0x04000 +#define APIC_ICR_BUSY 0x01000 +#define APIC_DEST_LOGICAL 0x00800 +#define APIC_DM_FIXED 0x00000 +#define APIC_DM_LOWEST 0x00100 +#define APIC_DM_SMI 0x00200 +#define APIC_DM_REMRD 0x00300 +#define APIC_DM_NMI 0x00400 +#define APIC_DM_INIT 0x00500 +#define APIC_DM_STARTUP 0x00600 +#define APIC_DM_EXTINT 0x00700 +#define APIC_VECTOR_MASK 0x000FF +#define APIC_ICR2 0x310 +#define GET_APIC_DEST_FIELD(x) (((x)>>24)&0xFF) +#define SET_APIC_DEST_FIELD(x) ((x)<<24) +#define APIC_LVTT 0x320 +#define APIC_LVTPC 0x340 +#define APIC_LVT0 0x350 +#define APIC_LVT_TIMER_BASE_MASK (0x3<<18) +#define GET_APIC_TIMER_BASE(x) (((x)>>18)&0x3) +#define SET_APIC_TIMER_BASE(x) (((x)<<18)) +#define APIC_TIMER_BASE_CLKIN 0x0 +#define APIC_TIMER_BASE_TMBASE 0x1 +#define APIC_TIMER_BASE_DIV 0x2 +#define APIC_LVT_TIMER_PERIODIC (1<<17) +#define APIC_LVT_MASKED (1<<16) +#define APIC_LVT_LEVEL_TRIGGER (1<<15) +#define APIC_LVT_REMOTE_IRR (1<<14) +#define APIC_INPUT_POLARITY (1<<13) +#define APIC_SEND_PENDING (1<<12) +#define GET_APIC_DELIVERY_MODE(x) (((x)>>8)&0x7) +#define SET_APIC_DELIVERY_MODE(x,y) (((x)&~0x700)|((y)<<8)) +#define APIC_MODE_FIXED 0x0 +#define APIC_MODE_NMI 0x4 +#define APIC_MODE_EXINT 0x7 +#define APIC_LVT1 0x360 +#define APIC_LVTERR 0x370 +#define APIC_TMICT 0x380 +#define APIC_TMCCT 0x390 +#define APIC_TDCR 0x3E0 +#define APIC_TDR_DIV_TMBASE (1<<2) +#define APIC_TDR_DIV_1 0xB +#define APIC_TDR_DIV_2 0x0 +#define APIC_TDR_DIV_4 0x1 +#define APIC_TDR_DIV_8 0x2 +#define APIC_TDR_DIV_16 0x3 +#define APIC_TDR_DIV_32 0x8 +#define APIC_TDR_DIV_64 0x9 +#define APIC_TDR_DIV_128 0xA + +#define APIC_BASE (fix_to_virt(FIX_APIC_BASE)) + +#define MAX_IO_APICS 8 + +/* + * the local APIC register structure, memory mapped. Not terribly well + * tested, but we might eventually use this one in the future - the + * problem why we cannot use it right now is the P5 APIC, it has an + * errata which cannot take 8-bit reads and writes, only 32-bit ones ... + */ +#define u32 unsigned int + +#define lapic ((volatile struct local_apic *)APIC_BASE) + +struct local_apic { + +/*000*/ struct { u32 __reserved[4]; } __reserved_01; + +/*010*/ struct { u32 __reserved[4]; } __reserved_02; + +/*020*/ struct { /* APIC ID Register */ + u32 __reserved_1 : 24, + phys_apic_id : 4, + __reserved_2 : 4; + u32 __reserved[3]; + } id; + +/*030*/ const + struct { /* APIC Version Register */ + u32 version : 8, + __reserved_1 : 8, + max_lvt : 8, + __reserved_2 : 8; + u32 __reserved[3]; + } version; + +/*040*/ struct { u32 __reserved[4]; } __reserved_03; + +/*050*/ struct { u32 __reserved[4]; } __reserved_04; + +/*060*/ struct { u32 __reserved[4]; } __reserved_05; + +/*070*/ struct { u32 __reserved[4]; } __reserved_06; + +/*080*/ struct { /* Task Priority Register */ + u32 priority : 8, + __reserved_1 : 24; + u32 __reserved_2[3]; + } tpr; + +/*090*/ const + struct { /* Arbitration Priority Register */ + u32 priority : 8, + __reserved_1 : 24; + u32 __reserved_2[3]; + } apr; + +/*0A0*/ const + struct { /* Processor Priority Register */ + u32 priority : 8, + __reserved_1 : 24; + u32 __reserved_2[3]; + } ppr; + +/*0B0*/ struct { /* End Of Interrupt Register */ + u32 eoi; + u32 __reserved[3]; + } eoi; + +/*0C0*/ struct { u32 __reserved[4]; } __reserved_07; + +/*0D0*/ struct { /* Logical Destination Register */ + u32 __reserved_1 : 24, + logical_dest : 8; + u32 __reserved_2[3]; + } ldr; + +/*0E0*/ struct { /* Destination Format Register */ + u32 __reserved_1 : 28, + model : 4; + u32 __reserved_2[3]; + } dfr; + +/*0F0*/ struct { /* Spurious Interrupt Vector Register */ + u32 spurious_vector : 8, + apic_enabled : 1, + focus_cpu : 1, + __reserved_2 : 22; + u32 __reserved_3[3]; + } svr; + +/*100*/ struct { /* In Service Register */ +/*170*/ u32 bitfield; + u32 __reserved[3]; + } isr [8]; + +/*180*/ struct { /* Trigger Mode Register */ +/*1F0*/ u32 bitfield; + u32 __reserved[3]; + } tmr [8]; + +/*200*/ struct { /* Interrupt Request Register */ +/*270*/ u32 bitfield; + u32 __reserved[3]; + } irr [8]; + +/*280*/ union { /* Error Status Register */ + struct { + u32 send_cs_error : 1, + receive_cs_error : 1, + send_accept_error : 1, + receive_accept_error : 1, + __reserved_1 : 1, + send_illegal_vector : 1, + receive_illegal_vector : 1, + illegal_register_address : 1, + __reserved_2 : 24; + u32 __reserved_3[3]; + } error_bits; + struct { + u32 errors; + u32 __reserved_3[3]; + } all_errors; + } esr; + +/*290*/ struct { u32 __reserved[4]; } __reserved_08; + +/*2A0*/ struct { u32 __reserved[4]; } __reserved_09; + +/*2B0*/ struct { u32 __reserved[4]; } __reserved_10; + +/*2C0*/ struct { u32 __reserved[4]; } __reserved_11; + +/*2D0*/ struct { u32 __reserved[4]; } __reserved_12; + +/*2E0*/ struct { u32 __reserved[4]; } __reserved_13; + +/*2F0*/ struct { u32 __reserved[4]; } __reserved_14; + +/*300*/ struct { /* Interrupt Command Register 1 */ + u32 vector : 8, + delivery_mode : 3, + destination_mode : 1, + delivery_status : 1, + __reserved_1 : 1, + level : 1, + trigger : 1, + __reserved_2 : 2, + shorthand : 2, + __reserved_3 : 12; + u32 __reserved_4[3]; + } icr1; + +/*310*/ struct { /* Interrupt Command Register 2 */ + union { + u32 __reserved_1 : 24, + phys_dest : 4, + __reserved_2 : 4; + u32 __reserved_3 : 24, + logical_dest : 8; + } dest; + u32 __reserved_4[3]; + } icr2; + +/*320*/ struct { /* LVT - Timer */ + u32 vector : 8, + __reserved_1 : 4, + delivery_status : 1, + __reserved_2 : 3, + mask : 1, + timer_mode : 1, + __reserved_3 : 14; + u32 __reserved_4[3]; + } lvt_timer; + +/*330*/ struct { u32 __reserved[4]; } __reserved_15; + +/*340*/ struct { /* LVT - Performance Counter */ + u32 vector : 8, + delivery_mode : 3, + __reserved_1 : 1, + delivery_status : 1, + __reserved_2 : 3, + mask : 1, + __reserved_3 : 15; + u32 __reserved_4[3]; + } lvt_pc; + +/*350*/ struct { /* LVT - LINT0 */ + u32 vector : 8, + delivery_mode : 3, + __reserved_1 : 1, + delivery_status : 1, + polarity : 1, + remote_irr : 1, + trigger : 1, + mask : 1, + __reserved_2 : 15; + u32 __reserved_3[3]; + } lvt_lint0; + +/*360*/ struct { /* LVT - LINT1 */ + u32 vector : 8, + delivery_mode : 3, + __reserved_1 : 1, + delivery_status : 1, + polarity : 1, + remote_irr : 1, + trigger : 1, + mask : 1, + __reserved_2 : 15; + u32 __reserved_3[3]; + } lvt_lint1; + +/*370*/ struct { /* LVT - Error */ + u32 vector : 8, + __reserved_1 : 4, + delivery_status : 1, + __reserved_2 : 3, + mask : 1, + __reserved_3 : 15; + u32 __reserved_4[3]; + } lvt_error; + +/*380*/ struct { /* Timer Initial Count Register */ + u32 initial_count; + u32 __reserved_2[3]; + } timer_icr; + +/*390*/ const + struct { /* Timer Current Count Register */ + u32 curr_count; + u32 __reserved_2[3]; + } timer_ccr; + +/*3A0*/ struct { u32 __reserved[4]; } __reserved_16; + +/*3B0*/ struct { u32 __reserved[4]; } __reserved_17; + +/*3C0*/ struct { u32 __reserved[4]; } __reserved_18; + +/*3D0*/ struct { u32 __reserved[4]; } __reserved_19; + +/*3E0*/ struct { /* Timer Divide Configuration Register */ + u32 divisor : 4, + __reserved_1 : 28; + u32 __reserved_2[3]; + } timer_dcr; + +/*3F0*/ struct { u32 __reserved[4]; } __reserved_20; + +} __attribute__ ((packed)); + +#undef u32 + +#endif diff -Nru a/include/asm-x86_64/atomic.h b/include/asm-x86_64/atomic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/atomic.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,206 @@ +#ifndef __ARCH_X86_64_ATOMIC__ +#define __ARCH_X86_64_ATOMIC__ + +#include + +/* atomic_t should be 32 bit signed type */ + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + */ + +#ifdef CONFIG_SMP +#define LOCK "lock ; " +#else +#define LOCK "" +#endif + +/* + * Make sure gcc doesn't try to be clever and move things around + * on us. We need to use _exactly_ the address the user gave us, + * not some alias that contains the same information. + */ +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +/** + * atomic_read - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_read(v) ((v)->counter) + +/** + * atomic_set - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_set(v,i) (((v)->counter) = (i)) + +/** + * atomic_add - add integer to atomic variable + * @i: integer value to add + * @v: pointer of type atomic_t + * + * Atomically adds @i to @v. Note that the guaranteed useful range + * of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_add(int i, atomic_t *v) +{ + __asm__ __volatile__( + LOCK "addl %1,%0" + :"=m" (v->counter) + :"ir" (i), "m" (v->counter)); +} + +/** + * atomic_sub - subtract the atomic variable + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_sub(int i, atomic_t *v) +{ + __asm__ __volatile__( + LOCK "subl %1,%0" + :"=m" (v->counter) + :"ir" (i), "m" (v->counter)); +} + +/** + * atomic_sub_and_test - subtract value from variable and test result + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v and returns + * true if the result is zero, or false for all + * other cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_sub_and_test(int i, atomic_t *v) +{ + unsigned char c; + + __asm__ __volatile__( + LOCK "subl %2,%0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"ir" (i), "m" (v->counter) : "memory"); + return c; +} + +/** + * atomic_inc - increment atomic variable + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_inc(atomic_t *v) +{ + __asm__ __volatile__( + LOCK "incl %0" + :"=m" (v->counter) + :"m" (v->counter)); +} + +/** + * atomic_dec - decrement atomic variable + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_dec(atomic_t *v) +{ + __asm__ __volatile__( + LOCK "decl %0" + :"=m" (v->counter) + :"m" (v->counter)); +} + +/** + * atomic_dec_and_test - decrement and test + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1 and + * returns true if the result is 0, or false for all other + * cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_dec_and_test(atomic_t *v) +{ + unsigned char c; + + __asm__ __volatile__( + LOCK "decl %0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"m" (v->counter) : "memory"); + return c != 0; +} + +/** + * atomic_inc_and_test - increment and test + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1 + * and returns true if the result is zero, or false for all + * other cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_inc_and_test(atomic_t *v) +{ + unsigned char c; + + __asm__ __volatile__( + LOCK "incl %0; sete %1" + :"=m" (v->counter), "=qm" (c) + :"m" (v->counter) : "memory"); + return c != 0; +} + +/** + * atomic_add_negative - add and test if negative + * @v: pointer of type atomic_t + * @i: integer value to add + * + * Atomically adds @i to @v and returns true + * if the result is negative, or false when + * result is greater than or equal to zero. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_add_negative(int i, atomic_t *v) +{ + unsigned char c; + + __asm__ __volatile__( + LOCK "addl %2,%0; sets %1" + :"=m" (v->counter), "=qm" (c) + :"ir" (i), "m" (v->counter) : "memory"); + return c; +} + +/* These are x86-specific, used by some header files */ +#define atomic_clear_mask(mask, addr) \ +__asm__ __volatile__(LOCK "andl %0,%1" \ +: : "r" (~(mask)),"m" (*addr) : "memory") + +#define atomic_set_mask(mask, addr) \ +__asm__ __volatile__(LOCK "orl %0,%1" \ +: : "r" ((unsigned)mask),"m" (*addr) : "memory") + +/* Atomic operations are already serializing on x86 */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#endif diff -Nru a/include/asm-x86_64/bitops.h b/include/asm-x86_64/bitops.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/bitops.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,465 @@ +#ifndef _X86_64_BITOPS_H +#define _X86_64_BITOPS_H + +/* + * Copyright 1992, Linus Torvalds. + */ + +#include + +/* + * These have to be done with inline assembly: that way the bit-setting + * is guaranteed to be atomic. All bit operations return 0 if the bit + * was cleared before the operation and != 0 if it was not. + * + * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). + */ + +#ifdef CONFIG_SMP +#define LOCK_PREFIX "lock ; " +#else +#define LOCK_PREFIX "" +#endif + +#define ADDR (*(volatile long *) addr) + +/** + * set_bit - Atomically set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * This function is atomic and may not be reordered. See __set_bit() + * if you do not require the atomic guarantees. + */ +static __inline__ void set_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__( LOCK_PREFIX + "btsl %1,%0" + :"=m" (ADDR) + :"dIr" (nr)); +} + +/** + * __set_bit - Set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * Unlike set_bit(), this function is non-atomic and may be reordered. + * If it's called on the same region of memory simultaneously, the effect + * may be that only one operation succeeds. + */ +static __inline__ void __set_bit(int nr, volatile void * addr) +{ + __asm__( + "btsl %1,%0" + :"=m" (ADDR) + :"dIr" (nr)); +} + +/** + * clear_bit - Clears a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + * + * clear_bit() is atomic and may not be reordered. However, it does + * not contain a memory barrier, so if it is used for locking purposes, + * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() + * in order to ensure changes are visible on other processors. + */ +static __inline__ void clear_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__( LOCK_PREFIX + "btrl %1,%0" + :"=m" (ADDR) + :"dIr" (nr)); +} + +static __inline__ void __clear_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__( + "btrl %1,%0" + :"=m" (ADDR) + :"Ir" (nr)); +} +#define smp_mb__before_clear_bit() barrier() +#define smp_mb__after_clear_bit() barrier() + +/** + * __change_bit - Toggle a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * Unlike change_bit(), this function is non-atomic and may be reordered. + * If it's called on the same region of memory simultaneously, the effect + * may be that only one operation succeeds. + */ +static __inline__ void __change_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__( + "btcl %1,%0" + :"=m" (ADDR) + :"dIr" (nr)); +} + +/** + * change_bit - Toggle a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + * + * change_bit() is atomic and may not be reordered. + * Note that @nr may be almost arbitrarily large; this function is not + * restricted to acting on a single-word quantity. + */ +static __inline__ void change_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__( LOCK_PREFIX + "btcl %1,%0" + :"=m" (ADDR) + :"dIr" (nr)); +} + +/** + * test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static __inline__ int test_and_set_bit(int nr, volatile void * addr) +{ + int oldbit; + + __asm__ __volatile__( LOCK_PREFIX + "btsl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"=m" (ADDR) + :"dIr" (nr) : "memory"); + return oldbit; +} + +/** + * __test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is non-atomic and can be reordered. + * If two examples of this operation race, one can appear to succeed + * but actually fail. You must protect multiple accesses with a lock. + */ +static __inline__ int __test_and_set_bit(int nr, volatile void * addr) +{ + int oldbit; + + __asm__( + "btsl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"=m" (ADDR) + :"dIr" (nr)); + return oldbit; +} + +/** + * test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static __inline__ int test_and_clear_bit(int nr, volatile void * addr) +{ + int oldbit; + + __asm__ __volatile__( LOCK_PREFIX + "btrl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"=m" (ADDR) + :"dIr" (nr) : "memory"); + return oldbit; +} + +/** + * __test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is non-atomic and can be reordered. + * If two examples of this operation race, one can appear to succeed + * but actually fail. You must protect multiple accesses with a lock. + */ +static __inline__ int __test_and_clear_bit(int nr, volatile void * addr) +{ + int oldbit; + + __asm__( + "btrl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"=m" (ADDR) + :"dIr" (nr)); + return oldbit; +} + +/* WARNING: non atomic and it can be reordered! */ +static __inline__ int __test_and_change_bit(int nr, volatile void * addr) +{ + int oldbit; + + __asm__ __volatile__( + "btcl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"=m" (ADDR) + :"dIr" (nr) : "memory"); + return oldbit; +} + +/** + * test_and_change_bit - Change a bit and return its new value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static __inline__ int test_and_change_bit(int nr, volatile void * addr) +{ + int oldbit; + + __asm__ __volatile__( LOCK_PREFIX + "btcl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"=m" (ADDR) + :"dIr" (nr) : "memory"); + return oldbit; +} + +#if 0 /* Fool kernel-doc since it doesn't do macros yet */ +/** + * test_bit - Determine whether a bit is set + * @nr: bit number to test + * @addr: Address to start counting from + */ +static int test_bit(int nr, const volatile void * addr); +#endif + +static __inline__ int constant_test_bit(int nr, const volatile void * addr) +{ + return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0; +} + +static __inline__ int variable_test_bit(int nr, volatile void * addr) +{ + int oldbit; + + __asm__ __volatile__( + "btl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit) + :"m" (ADDR),"dIr" (nr)); + return oldbit; +} + +#define test_bit(nr,addr) \ +(__builtin_constant_p(nr) ? \ + constant_test_bit((nr),(addr)) : \ + variable_test_bit((nr),(addr))) + +/** + * find_first_zero_bit - find the first zero bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit-number of the first zero bit, not the number of the byte + * containing a bit. + */ +static __inline__ int find_first_zero_bit(void * addr, unsigned size) +{ + int d0, d1, d2; + int res; + + if (!size) + return 0; + __asm__ __volatile__( + "movl $-1,%%eax\n\t" + "xorq %%rdx,%%rdx\n\t" + "repe; scasl\n\t" + "je 1f\n\t" + "xorl -4(%%rdi),%%eax\n\t" + "subq $4,%%rdi\n\t" + "bsfl %%eax,%%edx\n" + "1:\tsubq %%rbx,%%rdi\n\t" + "shlq $3,%%rdi\n\t" + "addq %%rdi,%%rdx" + :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2) + :"1" ((size + 31) >> 5), "2" (addr), "b" (addr) : "memory"); + return res; +} + +/** + * find_first_bit - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit-number of the first set bit, not the number of the byte + * containing a bit. + */ +static __inline__ int find_first_bit(void * addr, unsigned size) +{ + int d0, d1; + int res; + + /* This looks at memory. Mark it volatile to tell gcc not to move it around */ + /* Work in 32bit for now */ + __asm__ __volatile__( + "xorl %%eax,%%eax\n\t" + "repe; scasl\n\t" + "jz 1f\n\t" + "leaq -4(%%rdi),%%rdi\n\t" + "bsfl (%%rdi),%%eax\n" + "1:\tsubq %%rbx,%%rdi\n\t" + "shlq $3,%%rdi\n\t" + "addq %%rdi,%%rax" + :"=a" (res), "=&c" (d0), "=&D" (d1) + :"1" ((size + 31) >> 5), "2" (addr), "b" (addr)); + return res; +} + +/** + * find_next_zero_bit - find the first zero bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +static __inline__ int find_next_zero_bit (void * addr, int size, int offset) +{ + unsigned int * p = ((unsigned int *) addr) + (offset >> 5); + int set = 0, bit = offset & 31, res; + + if (bit) { + /* + * Look for zero in the first 32 bits. + */ + __asm__("bsfl %1,%0\n\t" + "jne 1f\n\t" + "movl $32, %0\n" + "1:" + : "=r" (set) + : "r" (~(*p >> bit))); + if (set < (32 - bit)) + return set + offset; + set = 32 - bit; + p++; + } + /* + * No zero yet, search remaining full bytes for a zero + */ + res = find_first_zero_bit (p, size - 32 * (p - (unsigned int *) addr)); + return (offset + set + res); +} + +/** + * find_next_bit - find the first set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +static __inline__ int find_next_bit (void * addr, int size, int offset) +{ + unsigned long * p = ((unsigned long *) addr) + (offset >> 5); + unsigned long set = 0, bit = offset & 63, res; + + if (bit) { + /* + * Look for nonzero in the first 64 bits: + */ + __asm__("bsfq %1,%0\n\t" + "jne 1f\n\t" + "movq $64, %0\n" + "1:" + : "=r" (set) + : "r" (*p >> bit)); + if (set < (64 - bit)) + return set + offset; + set = 64 - bit; + p++; + } + /* + * No set bit yet, search remaining full words for a bit + */ + res = find_first_bit (p, size - 64 * (p - (unsigned long *) addr)); + return (offset + set + res); +} + +/** + * ffz - find first zero in word. + * @word: The word to search + * + * Undefined if no zero exists, so code should check against ~0UL first. + */ +static __inline__ unsigned long ffz(unsigned long word) +{ + __asm__("bsfq %1,%0" + :"=r" (word) + :"r" (~word)); + return word; +} + +/** + * __ffs - find first bit in word. + * @word: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static __inline__ unsigned long __ffs(unsigned long word) +{ + __asm__("bsfq %1,%0" + :"=r" (word) + :"rm" (word)); + return word; +} + +#ifdef __KERNEL__ + +/** + * ffs - find first bit set + * @x: the word to search + * + * This is defined the same way as + * the libc and compiler builtin ffs routines, therefore + * differs in spirit from the above ffz (man ffs). + */ +static __inline__ int ffs(int x) +{ + int r; + + __asm__("bsfl %1,%0\n\t" + "jnz 1f\n\t" + "movl $-1,%0\n" + "1:" : "=r" (r) : "g" (x)); + return r+1; +} + +/** + * hweightN - returns the hamming weight of a N-bit word + * @x: the word to weigh + * + * The Hamming Weight of a number is the total number of bits set in it. + */ + +#define hweight32(x) generic_hweight32(x) +#define hweight16(x) generic_hweight16(x) +#define hweight8(x) generic_hweight8(x) + +#endif /* __KERNEL__ */ + +#ifdef __KERNEL__ + +#define ext2_set_bit __test_and_set_bit +#define ext2_clear_bit __test_and_clear_bit +#define ext2_test_bit test_bit +#define ext2_find_first_zero_bit find_first_zero_bit +#define ext2_find_next_zero_bit find_next_zero_bit + +/* Bitmap functions for the minix filesystem. */ +#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) __set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) +#define minix_test_bit(nr,addr) test_bit(nr,addr) +#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) + +#endif /* __KERNEL__ */ + +#endif /* _X86_64_BITOPS_H */ diff -Nru a/include/asm-x86_64/boot.h b/include/asm-x86_64/boot.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/boot.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,15 @@ +#ifndef _LINUX_BOOT_H +#define _LINUX_BOOT_H + +/* Don't touch these, unless you really know what you're doing. */ +#define DEF_INITSEG 0x9000 +#define DEF_SYSSEG 0x1000 +#define DEF_SETUPSEG 0x9020 +#define DEF_SYSSIZE 0x7F00 + +/* Internal svga startup constants */ +#define NORMAL_VGA 0xffff /* 80x25 mode */ +#define EXTENDED_VGA 0xfffe /* 80x50 mode */ +#define ASK_VGA 0xfffd /* ask for it at bootup */ + +#endif diff -Nru a/include/asm-x86_64/bootsetup.h b/include/asm-x86_64/bootsetup.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/bootsetup.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,34 @@ + +#ifndef _X86_64_BOOTSETUP_H +#define _X86_64_BOOTSETUP_H 1 + +extern char x86_boot_params[2048]; + +/* + * This is set up by the setup-routine at boot-time + */ +#define PARAM ((unsigned char *)x86_boot_params) +#define SCREEN_INFO (*(struct screen_info *) (PARAM+0)) +#define EXT_MEM_K (*(unsigned short *) (PARAM+2)) +#define ALT_MEM_K (*(unsigned int *) (PARAM+0x1e0)) +#define E820_MAP_NR (*(char*) (PARAM+E820NR)) +#define E820_MAP ((struct e820entry *) (PARAM+E820MAP)) +#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40)) +#define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) +#define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0)) +#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2)) +#define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) +#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) +#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) +#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210)) +#define KERNEL_START (*(unsigned int *) (PARAM+0x214)) +#define INITRD_START (*(unsigned int *) (PARAM+0x218)) +#define INITRD_SIZE (*(unsigned int *) (PARAM+0x21c)) +#define COMMAND_LINE saved_command_line +#define COMMAND_LINE_SIZE 256 + +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 + +#endif diff -Nru a/include/asm-x86_64/bugs.h b/include/asm-x86_64/bugs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/bugs.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,42 @@ +/* + * include/asm-x86_64/bugs.h + * + * Copyright (C) 1994 Linus Torvalds + * Copyright (C) 2000 SuSE + * + * This is included by init/main.c to check for architecture-dependent bugs. + * + * Needs: + * void check_bugs(void); + */ + +#include +#include +#include + +static inline void check_fpu(void) +{ + extern void __bad_fxsave_alignment(void); + if (offsetof(struct task_struct, thread.i387.fxsave) & 15) + __bad_fxsave_alignment(); + printk(KERN_INFO "Enabling fast FPU save and restore... "); + set_in_cr4(X86_CR4_OSFXSR); + printk("done.\n"); + printk(KERN_INFO "Enabling unmasked SIMD FPU exception support... "); + set_in_cr4(X86_CR4_OSXMMEXCPT); + printk("done.\n"); +} + +/* + * If we configured ourselves for FXSR, we'd better have it. + */ + +static void __init check_bugs(void) +{ + identify_cpu(&boot_cpu_data); + check_fpu(); +#if !defined(CONFIG_SMP) + printk("CPU: "); + print_cpu_info(&boot_cpu_data); +#endif +} diff -Nru a/include/asm-x86_64/byteorder.h b/include/asm-x86_64/byteorder.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/byteorder.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,32 @@ +#ifndef _X86_64_BYTEORDER_H +#define _X86_64_BYTEORDER_H + +#include + +#ifdef __GNUC__ + +static __inline__ __const__ __u64 ___arch__swab64(__u64 x) +{ + __asm__("bswapq %0" : "=r" (x) : "0" (x)); + return x; +} + +static __inline__ __const__ __u32 ___arch__swab32(__u32 x) +{ + __asm__("bswapl %0" : "=r" (x) : "0" (x)); + return x; +} + +/* Do not define swab16. Gcc is smart enought to recognize "C" version and + convert it into rotation or exhange. */ + +#define __arch__swab32(x) ___arch__swab32(x) +#define __arch__swab64(x) ___arch__swab64(x) + +#endif /* __GNUC__ */ + +#define __BYTEORDER_HAS_U64__ + +#include + +#endif /* _X86_64_BYTEORDER_H */ diff -Nru a/include/asm-x86_64/cache.h b/include/asm-x86_64/cache.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/cache.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,13 @@ +/* + * include/asm-x8664/cache.h + */ +#ifndef __ARCH_X8664_CACHE_H +#define __ARCH_X8664_CACHE_H + +#include + +/* L1 cache line size */ +#define L1_CACHE_SHIFT (CONFIG_X86_L1_CACHE_SHIFT) +#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) + +#endif diff -Nru a/include/asm-x86_64/calling.h b/include/asm-x86_64/calling.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/calling.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,100 @@ +/* Some macros to handle stack frames */ + + .macro SAVE_ARGS + pushq %rdi + pushq %rsi + pushq %rdx + pushq %rcx + pushq %rax + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + .endm + + .macro RESTORE_ARGS + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rax + popq %rcx + popq %rdx + popq %rsi + popq %rdi + .endm + + .macro LOAD_ARGS offset + movq \offset(%rsp),%r11 + movq \offset+8(%rsp),%r10 + movq \offset+16(%rsp),%r9 + movq \offset+24(%rsp),%r8 + movq \offset+40(%rsp),%rcx + movq \offset+48(%rsp),%rdx + movq \offset+56(%rsp),%rsi + movq \offset+64(%rsp),%rdi + movq \offset+72(%rsp),%rax + .endm + + .macro SAVE_REST + pushq %rbx + pushq %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + .endm + + .macro RESTORE_REST + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx + .endm + + .macro SAVE_ALL + SAVE_ARGS + SAVE_REST + .endm + + .macro RESTORE_ALL + RESTORE_REST + RESTORE_ARGS + .endm + + +R15 = 0 +R14 = 8 +R13 = 16 +R12 = 24 +RBP = 36 +RBX = 40 +/* arguments: interrupts/non tracing syscalls only save upto here*/ +R11 = 48 +R10 = 56 +R9 = 64 +R8 = 72 +RAX = 80 +RCX = 88 +RDX = 96 +RSI = 104 +RDI = 112 +ORIG_RAX = 120 /* = ERROR */ +/* end of arguments */ +/* cpu exception frame or undefined in case of fast syscall. */ +RIP = 128 +CS = 136 +EFLAGS = 144 +RSP = 152 +SS = 160 +ARGOFFSET = R11 + + .macro SYSRET32 + .byte 0x0f,0x07 + .endm + + .macro SYSRET64 + .byte 0x48,0x0f,0x07 + .endm diff -Nru a/include/asm-x86_64/checksum.h b/include/asm-x86_64/checksum.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/checksum.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,158 @@ +#ifndef _X86_64_CHECKSUM_H +#define _X86_64_CHECKSUM_H + + +/* + * This is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. + * + * By Jorge Cwik , adapted for linux by + * Arnt Gulbrandsen. + */ +static inline unsigned short ip_fast_csum(unsigned char * iph, + unsigned int ihl) { + unsigned int sum; + + __asm__ __volatile__( +"\n movl (%1), %0" +"\n subl $4, %2" +"\n jbe 2f" +"\n addl 4(%1), %0" +"\n adcl 8(%1), %0" +"\n adcl 12(%1), %0" +"\n1: adcl 16(%1), %0" +"\n lea 4(%1), %1" +"\n decl %2" +"\n jne 1b" +"\n adcl $0, %0" +"\n movl %0, %2" +"\n shrl $16, %0" +"\n addw %w2, %w0" +"\n adcl $0, %0" +"\n notl %0" +"\n2:" + /* Since the input registers which are loaded with iph and ipl + are modified, we must also specify them as outputs, or gcc + will assume they contain their original values. */ + : "=r" (sum), "=r" (iph), "=r" (ihl) + : "1" (iph), "2" (ihl)); + return(sum); +} + + + +/* + * Fold a partial checksum. Note this works on a 32bit unfolded checksum. Make sure + * to not mix with 64bit checksums! + */ + +static inline unsigned int csum_fold(unsigned int sum) +{ + __asm__( +"\n addl %1,%0" +"\n adcl $0xffff,%0" + : "=r" (sum) + : "r" (sum << 16), "0" (sum & 0xffff0000) + ); + return (~sum) >> 16; +} + + + + +static inline unsigned long csum_tcpudp_nofold(unsigned saddr, + unsigned daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + __asm__( +"\n addl %1, %0" +"\n adcl %2, %0" +"\n adcl %3, %0" +"\n adcl $0, %0" + : "=r" (sum) + : "g" (daddr), "g"(saddr), "g"((ntohs(len)<<16)+proto*256), "0"(sum)); + return sum; +} + +/* + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented + */ +static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum)); +} + + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * returns a 32-bit number suitable for feeding into itself + * or csum_tcpudp_magic + * + * this function must be called with even lengths, except + * for the last fragment, which may be odd + * + * it's best to have buff aligned on a 32-bit boundary + */ +extern unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum); + +/* + * the same as csum_partial, but copies from src while it + * checksums + * + * here even more important to align src and dst on a 32-bit (or even + * better 64-bit) boundary + */ +unsigned int csum_partial_copy(const char *src, char *dst, int len, unsigned int sum); + +/* + * this is a new version of the above that records errors it finds in *errp, + * but continues and zeros the rest of the buffer. + */ +unsigned int csum_partial_copy_from_user(const char *src, char *dst, int len, unsigned int sum, int *errp); + +unsigned int csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum); + + +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ + +extern unsigned short ip_compute_csum(unsigned char * buff, int len); + +#define _HAVE_ARCH_IPV6_CSUM +static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, + struct in6_addr *daddr, + __u32 len, + unsigned short proto, + unsigned int sum) +{ + __asm__( +"\n addl 0(%1), %0" +"\n adcl 4(%1), %0" +"\n adcl 8(%1), %0" +"\n adcl 12(%1), %0" +"\n adcl 0(%2), %0" +"\n adcl 4(%2), %0" +"\n adcl 8(%2), %0" +"\n adcl 12(%2), %0" +"\n adcl %3, %0" +"\n adcl %4, %0" +"\n adcl $0, %0" + : "=&r" (sum) + : "r" (saddr), "r" (daddr), + "r"(htonl(len)), "r"(htonl(proto)), "0"(sum)); + + return csum_fold(sum); +} + +#endif diff -Nru a/include/asm-x86_64/cpufeature.h b/include/asm-x86_64/cpufeature.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/cpufeature.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,73 @@ +/* + * cpufeature.h + * + * Defines x86 CPU feature bits + */ + +#ifndef __ASM_X8664_CPUFEATURE_H +#define __ASM_X8664_CPUFEATURE_H + +/* Sample usage: CPU_FEATURE_P(cpu.x86_capability, FPU) */ +#define CPU_FEATURE_P(CAP, FEATURE) test_bit(CAP, X86_FEATURE_##FEATURE ##_BIT) + +#define NCAPINTS 4 /* Currently we have 4 32-bit words worth of info */ + +/* Intel-defined CPU features, CPUID level 0x00000001, word 0 */ +#define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */ +#define X86_FEATURE_VME (0*32+ 1) /* Virtual Mode Extensions */ +#define X86_FEATURE_DE (0*32+ 2) /* Debugging Extensions */ +#define X86_FEATURE_PSE (0*32+ 3) /* Page Size Extensions */ +#define X86_FEATURE_TSC (0*32+ 4) /* Time Stamp Counter */ +#define X86_FEATURE_MSR (0*32+ 5) /* Model-Specific Registers, RDMSR, WRMSR */ +#define X86_FEATURE_PAE (0*32+ 6) /* Physical Address Extensions */ +#define X86_FEATURE_MCE (0*32+ 7) /* Machine Check Architecture */ +#define X86_FEATURE_CX8 (0*32+ 8) /* CMPXCHG8 instruction */ +#define X86_FEATURE_APIC (0*32+ 9) /* Onboard APIC */ +#define X86_FEATURE_SEP (0*32+11) /* SYSENTER/SYSEXIT */ +#define X86_FEATURE_MTRR (0*32+12) /* Memory Type Range Registers */ +#define X86_FEATURE_PGE (0*32+13) /* Page Global Enable */ +#define X86_FEATURE_MCA (0*32+14) /* Machine Check Architecture */ +#define X86_FEATURE_CMOV (0*32+15) /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */ +#define X86_FEATURE_PAT (0*32+16) /* Page Attribute Table */ +#define X86_FEATURE_PSE36 (0*32+17) /* 36-bit PSEs */ +#define X86_FEATURE_PN (0*32+18) /* Processor serial number */ +#define X86_FEATURE_CLFLSH (0*32+19) /* Supports the CLFLUSH instruction */ +#define X86_FEATURE_DTES (0*32+21) /* Debug Trace Store */ +#define X86_FEATURE_ACPI (0*32+22) /* ACPI via MSR */ +#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */ +#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions (fast save and restore */ + /* of FPU context), and CR4.OSFXSR available */ +#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */ +#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */ +#define X86_FEATURE_SELFSNOOP (0*32+27) /* CPU self snoop */ +#define X86_FEATURE_ACC (0*32+29) /* Automatic clock control */ +#define X86_FEATURE_IA64 (0*32+30) /* IA-64 processor */ + +/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */ +/* Don't duplicate feature flags which are redundant with Intel! */ +#define X86_FEATURE_SYSCALL (1*32+11) /* SYSCALL/SYSRET */ +#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */ +#define X86_FEATURE_LM (1*32+29) /* Long Mode (x86-64) */ +#define X86_FEATURE_3DNOWEXT (1*32+30) /* AMD 3DNow! extensions */ +#define X86_FEATURE_3DNOW (1*32+31) /* 3DNow! */ + +/* Transmeta-defined CPU features, CPUID level 0x80860001, word 2 */ +#define X86_FEATURE_RECOVERY (2*32+ 0) /* CPU in recovery mode */ +#define X86_FEATURE_LONGRUN (2*32+ 1) /* Longrun power control */ +#define X86_FEATURE_LRTI (2*32+ 3) /* LongRun table interface */ + +/* Other features, Linux-defined mapping, word 3 */ +/* This range is used for feature bits which conflict or are synthesized */ +#define X86_FEATURE_CXMMX (3*32+ 0) /* Cyrix MMX extensions */ +#define X86_FEATURE_K6_MTRR (3*32+ 1) /* AMD K6 nonstandard MTRRs */ +#define X86_FEATURE_CYRIX_ARR (3*32+ 2) /* Cyrix ARRs (= MTRRs) */ +#define X86_FEATURE_CENTAUR_MCR (3*32+ 3) /* Centaur MCRs (= MTRRs) */ + +#endif /* __ASM_X8664_CPUFEATURE_H */ + +/* + * Local Variables: + * mode:c + * comment-column:42 + * End: + */ diff -Nru a/include/asm-x86_64/current.h b/include/asm-x86_64/current.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/current.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,29 @@ +#ifndef _X86_64_CURRENT_H +#define _X86_64_CURRENT_H + +#if !defined(__ASSEMBLY__) +struct task_struct; + +#include + +static inline struct task_struct *get_current(void) +{ + struct task_struct *t = read_pda(pcurrent); + return t; +} + + + +#define current get_current() + +#else + +#ifndef ASM_OFFSET_H +#include +#endif + +#define GET_CURRENT(reg) movq %gs:(pda_pcurrent),reg + +#endif + +#endif /* !(_X86_64_CURRENT_H) */ diff -Nru a/include/asm-x86_64/debugreg.h b/include/asm-x86_64/debugreg.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/debugreg.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,65 @@ +#ifndef _X86_64_DEBUGREG_H +#define _X86_64_DEBUGREG_H + + +/* Indicate the register numbers for a number of the specific + debug registers. Registers 0-3 contain the addresses we wish to trap on */ +#define DR_FIRSTADDR 0 /* u_debugreg[DR_FIRSTADDR] */ +#define DR_LASTADDR 3 /* u_debugreg[DR_LASTADDR] */ + +#define DR_STATUS 6 /* u_debugreg[DR_STATUS] */ +#define DR_CONTROL 7 /* u_debugreg[DR_CONTROL] */ + +/* Define a few things for the status register. We can use this to determine + which debugging register was responsible for the trap. The other bits + are either reserved or not of interest to us. */ + +#define DR_TRAP0 (0x1) /* db0 */ +#define DR_TRAP1 (0x2) /* db1 */ +#define DR_TRAP2 (0x4) /* db2 */ +#define DR_TRAP3 (0x8) /* db3 */ + +#define DR_STEP (0x4000) /* single-step */ +#define DR_SWITCH (0x8000) /* task switch */ + +/* Now define a bunch of things for manipulating the control register. + The top two bytes of the control register consist of 4 fields of 4 + bits - each field corresponds to one of the four debug registers, + and indicates what types of access we trap on, and how large the data + field is that we are looking at */ + +#define DR_CONTROL_SHIFT 16 /* Skip this many bits in ctl register */ +#define DR_CONTROL_SIZE 4 /* 4 control bits per register */ + +#define DR_RW_EXECUTE (0x0) /* Settings for the access types to trap on */ +#define DR_RW_WRITE (0x1) +#define DR_RW_READ (0x3) + +#define DR_LEN_1 (0x0) /* Settings for data length to trap on */ +#define DR_LEN_2 (0x4) +#define DR_LEN_4 (0xC) +#define DR_LEN_8 (0x8) + +/* The low byte to the control register determine which registers are + enabled. There are 4 fields of two bits. One bit is "local", meaning + that the processor will reset the bit after a task switch and the other + is global meaning that we have to explicitly reset the bit. With linux, + you can use either one, since we explicitly zero the register when we enter + kernel mode. */ + +#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit */ +#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit */ +#define DR_ENABLE_SIZE 2 /* 2 enable bits per register */ + +#define DR_LOCAL_ENABLE_MASK (0x55) /* Set local bits for all 4 regs */ +#define DR_GLOBAL_ENABLE_MASK (0xAA) /* Set global bits for all 4 regs */ + +/* The second byte to the control register has a few special things. + We can slow the instruction pipeline for instructions coming via the + gdt or the ldt if we want to. I am not sure why this is an advantage */ + +#define DR_CONTROL_RESERVED (0xFC00) /* Reserved by Intel */ +#define DR_LOCAL_SLOWDOWN (0x100) /* Local slow the pipeline */ +#define DR_GLOBAL_SLOWDOWN (0x200) /* Global slow the pipeline */ + +#endif diff -Nru a/include/asm-x86_64/delay.h b/include/asm-x86_64/delay.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/delay.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,20 @@ +#ifndef _X8664_DELAY_H +#define _X8664_DELAY_H + +/* + * Copyright (C) 1993 Linus Torvalds + * + * Delay routines calling functions in arch/x86_64/lib/delay.c + */ + +extern void __bad_udelay(void); + +extern void __udelay(unsigned long usecs); +extern void __const_udelay(unsigned long usecs); +extern void __delay(unsigned long loops); + +#define udelay(n) (__builtin_constant_p(n) ? \ + ((n) > 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c6ul)) : \ + __udelay(n)) + +#endif /* defined(_X8664_DELAY_H) */ diff -Nru a/include/asm-x86_64/desc.h b/include/asm-x86_64/desc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/desc.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,187 @@ +/* Written 2000 by Andi Kleen */ +#ifndef __ARCH_DESC_H +#define __ARCH_DESC_H + +#include +#include + +#ifndef __ASSEMBLY__ + +/* Keep this syncronized with kernel/head.S */ +#define TSS_START (7 * 8) +#define LDT_START (TSS_START + NR_CPUS*16) + +#define __TSS(n) (TSS_START + (n)*16) +#define __LDT(n) (LDT_START + (n)*16) + +extern __u8 tss_start[]; +extern __u8 gdt_table[]; +extern __u8 ldt_start[]; +extern __u8 gdt_end[]; + +enum { + GATE_INTERRUPT = 0xE, + GATE_TRAP = 0xF, + GATE_CALL = 0xC, +}; + +// 16byte gate +struct gate_struct { + u16 offset_low; + u16 segment; + unsigned ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1; + u16 offset_middle; + u32 offset_high; + u32 zero1; +} __attribute__((packed)); + +#define PTR_LOW(x) ((unsigned long)(x) & 0xFFFF) +#define PTR_MIDDLE(x) (((unsigned long)(x) >> 16) & 0xFFFF) +#define PTR_HIGH(x) ((unsigned long)(x) >> 32) + +// 8 byte segment descriptor +struct desc_struct { + u16 limit0; + u16 base0; + unsigned base1 : 8, type : 4, s : 1, dpl : 2, p : 1; + unsigned limit : 4, avl : 1, l : 1, d : 1, g : 1, base2 : 8; +} __attribute__((packed)); + +enum { + DESC_TSS = 0x9, + DESC_LDT = 0x2, + TSSLIMIT = 0x67, +}; + +// LDT or TSS descriptor in the GDT. 16 bytes. +struct ldttss_desc { + u16 limit0; + u16 base0; + unsigned base1 : 8, type : 5, dpl : 2, p : 1; + unsigned limit1 : 4, zero0 : 3, g : 1, base2 : 8; + u32 base3; + u32 zero1; +} __attribute__((packed)); + +struct desc_ptr { + unsigned short size; + unsigned long address; +} __attribute__((packed)) ; + +/* FIXME: these should use more generic register classes */ +#define load_TR(n) asm volatile("ltr %%ax"::"a" (__TSS(n))) +#define __load_LDT(n) asm volatile("lldt %%ax"::"a" (__LDT(n))) + +/* + * This is the ldt that every process will get unless we need + * something other than this. + */ +extern struct desc_struct default_ldt[]; +extern struct gate_struct idt_table[]; + +static inline void _set_gate(void *adr, unsigned type, unsigned long func, unsigned dpl, unsigned ist) +{ + struct gate_struct s; + s.offset_low = PTR_LOW(func); + s.segment = __KERNEL_CS; + s.ist = ist; + s.p = 1; + s.dpl = dpl; + s.zero0 = 0; + s.zero1 = 0; + s.type = type; + s.offset_middle = PTR_MIDDLE(func); + s.offset_high = PTR_HIGH(func); + /* does not need to be atomic because it is only done once at setup time */ + memcpy(adr, &s, 16); +} + +static inline void set_intr_gate(int nr, void *func) +{ + _set_gate(&idt_table[nr], GATE_INTERRUPT, (unsigned long) func, 3, 0); +} + +static inline void set_intr_gate_ist(int nr, void *func, unsigned ist) +{ + _set_gate(&idt_table[nr], GATE_INTERRUPT, (unsigned long) func, 3, ist); +} + +static inline void set_system_gate(int nr, void *func) +{ + _set_gate(&idt_table[nr], GATE_INTERRUPT, (unsigned long) func, 3, 0); +} + +static inline void set_trap_gate(int nr, void *func) +{ + _set_gate(&idt_table[nr], GATE_TRAP, (unsigned long) func, 0, 0); +} + +static inline void set_call_gate(void *adr, void *func) +{ + _set_gate(adr, GATE_CALL, (unsigned long) func, 3, 0); +} + +static inline void set_priv_gate(int nr, void *func) +{ + _set_gate(&idt_table[nr], GATE_TRAP, (unsigned long) func, 0, 0); +} + +static inline void set_tssldt_descriptor(void *ptr, unsigned long tss, unsigned type, + unsigned size) +{ + struct ldttss_desc d; + memset(&d,0,sizeof(d)); + d.limit0 = size & 0xFFFF; + d.base0 = PTR_LOW(tss); + d.base1 = PTR_MIDDLE(tss) & 0xFF; + d.type = type; + d.p = 1; + d.limit1 = 0xF; + d.base2 = (PTR_MIDDLE(tss) >> 8) & 0xFF; + d.base3 = PTR_HIGH(tss); + memcpy(ptr, &d, 16); +} + +static inline void set_tss_desc(unsigned n, void *addr) +{ + set_tssldt_descriptor((__u8*)gdt_table + __TSS(n), (unsigned long)addr, DESC_TSS, + TSSLIMIT); +} + +static inline void set_ldt_desc(unsigned n, void *addr, int size) +{ + set_tssldt_descriptor((__u8*)gdt_table + __LDT(n), (unsigned long)addr, DESC_LDT, size); +} + + +#ifndef MINIKERNEL +extern inline void clear_LDT(void) +{ + int cpu = smp_processor_id(); + set_ldt_desc(cpu, &default_ldt[0], 5); + __load_LDT(cpu); +} + + +/* + * load one particular LDT into the current CPU + */ +extern inline void load_LDT (struct mm_struct *mm) +{ + int cpu = smp_processor_id(); + void *segments = mm->context.segments; + int count = LDT_ENTRIES; + + if (!segments) { + segments = &default_ldt[0]; + count = 5; + } + + set_ldt_desc(cpu, segments, count); + __load_LDT(cpu); +} +#endif + +#endif /* !__ASSEMBLY__ */ + +#endif diff -Nru a/include/asm-x86_64/div64.h b/include/asm-x86_64/div64.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/div64.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,14 @@ +#ifndef __X86_64_DIV64 +#define __X86_64_DIV64 + +/* + * Hey, we're already 64-bit, no + * need to play games.. + */ +#define do_div(n,base) ({ \ + int __res; \ + __res = ((unsigned long) (n)) % (unsigned) (base); \ + (n) = ((unsigned long) (n)) / (unsigned) (base); \ + __res; }) + +#endif diff -Nru a/include/asm-x86_64/dma.h b/include/asm-x86_64/dma.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/dma.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,298 @@ +/* $Id: dma.h,v 1.1.1.1 2001/04/19 20:00:38 ak Exp $ + * linux/include/asm/dma.h: Defines for using and allocating dma channels. + * Written by Hennus Bergman, 1992. + * High DMA channel support & info by Hannu Savolainen + * and John Boyd, Nov. 1992. + */ + +#ifndef _ASM_DMA_H +#define _ASM_DMA_H + +#include +#include /* And spinlocks */ +#include /* need byte IO */ +#include + + +#ifdef HAVE_REALLY_SLOW_DMA_CONTROLLER +#define dma_outb outb_p +#else +#define dma_outb outb +#endif + +#define dma_inb inb + +/* + * NOTES about DMA transfers: + * + * controller 1: channels 0-3, byte operations, ports 00-1F + * controller 2: channels 4-7, word operations, ports C0-DF + * + * - ALL registers are 8 bits only, regardless of transfer size + * - channel 4 is not used - cascades 1 into 2. + * - channels 0-3 are byte - addresses/counts are for physical bytes + * - channels 5-7 are word - addresses/counts are for physical words + * - transfers must not cross physical 64K (0-3) or 128K (5-7) boundaries + * - transfer count loaded to registers is 1 less than actual count + * - controller 2 offsets are all even (2x offsets for controller 1) + * - page registers for 5-7 don't use data bit 0, represent 128K pages + * - page registers for 0-3 use bit 0, represent 64K pages + * + * DMA transfers are limited to the lower 16MB of _physical_ memory. + * Note that addresses loaded into registers must be _physical_ addresses, + * not logical addresses (which may differ if paging is active). + * + * Address mapping for channels 0-3: + * + * A23 ... A16 A15 ... A8 A7 ... A0 (Physical addresses) + * | ... | | ... | | ... | + * | ... | | ... | | ... | + * | ... | | ... | | ... | + * P7 ... P0 A7 ... A0 A7 ... A0 + * | Page | Addr MSB | Addr LSB | (DMA registers) + * + * Address mapping for channels 5-7: + * + * A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0 (Physical addresses) + * | ... | \ \ ... \ \ \ ... \ \ + * | ... | \ \ ... \ \ \ ... \ (not used) + * | ... | \ \ ... \ \ \ ... \ + * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0 + * | Page | Addr MSB | Addr LSB | (DMA registers) + * + * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses + * and counts _must_ be word-aligned (the lowest address bit is _ignored_ at + * the hardware level, so odd-byte transfers aren't possible). + * + * Transfer count (_not # bytes_) is limited to 64K, represented as actual + * count - 1 : 64K => 0xFFFF, 1 => 0x0000. Thus, count is always 1 or more, + * and up to 128K bytes may be transferred on channels 5-7 in one operation. + * + */ + +#define MAX_DMA_CHANNELS 8 + +/* The maximum address that we can perform a DMA transfer to on this platform */ +#define MAX_DMA_ADDRESS (PAGE_OFFSET+0x1000000) + +/* 8237 DMA controllers */ +#define IO_DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */ +#define IO_DMA2_BASE 0xC0 /* 16 bit master DMA, ch 4(=slave input)..7 */ + +/* DMA controller registers */ +#define DMA1_CMD_REG 0x08 /* command register (w) */ +#define DMA1_STAT_REG 0x08 /* status register (r) */ +#define DMA1_REQ_REG 0x09 /* request register (w) */ +#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */ +#define DMA1_MODE_REG 0x0B /* mode register (w) */ +#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */ +#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ +#define DMA1_RESET_REG 0x0D /* Master Clear (w) */ +#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ +#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ + +#define DMA2_CMD_REG 0xD0 /* command register (w) */ +#define DMA2_STAT_REG 0xD0 /* status register (r) */ +#define DMA2_REQ_REG 0xD2 /* request register (w) */ +#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */ +#define DMA2_MODE_REG 0xD6 /* mode register (w) */ +#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */ +#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ +#define DMA2_RESET_REG 0xDA /* Master Clear (w) */ +#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ +#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ + +#define DMA_ADDR_0 0x00 /* DMA address registers */ +#define DMA_ADDR_1 0x02 +#define DMA_ADDR_2 0x04 +#define DMA_ADDR_3 0x06 +#define DMA_ADDR_4 0xC0 +#define DMA_ADDR_5 0xC4 +#define DMA_ADDR_6 0xC8 +#define DMA_ADDR_7 0xCC + +#define DMA_CNT_0 0x01 /* DMA count registers */ +#define DMA_CNT_1 0x03 +#define DMA_CNT_2 0x05 +#define DMA_CNT_3 0x07 +#define DMA_CNT_4 0xC2 +#define DMA_CNT_5 0xC6 +#define DMA_CNT_6 0xCA +#define DMA_CNT_7 0xCE + +#define DMA_PAGE_0 0x87 /* DMA page registers */ +#define DMA_PAGE_1 0x83 +#define DMA_PAGE_2 0x81 +#define DMA_PAGE_3 0x82 +#define DMA_PAGE_5 0x8B +#define DMA_PAGE_6 0x89 +#define DMA_PAGE_7 0x8A + +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ +#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */ + +#define DMA_AUTOINIT 0x10 + + +extern spinlock_t dma_spin_lock; + +static __inline__ unsigned long claim_dma_lock(void) +{ + unsigned long flags; + spin_lock_irqsave(&dma_spin_lock, flags); + return flags; +} + +static __inline__ void release_dma_lock(unsigned long flags) +{ + spin_unlock_irqrestore(&dma_spin_lock, flags); +} + +/* enable/disable a specific DMA channel */ +static __inline__ void enable_dma(unsigned int dmanr) +{ + if (dmanr<=3) + dma_outb(dmanr, DMA1_MASK_REG); + else + dma_outb(dmanr & 3, DMA2_MASK_REG); +} + +static __inline__ void disable_dma(unsigned int dmanr) +{ + if (dmanr<=3) + dma_outb(dmanr | 4, DMA1_MASK_REG); + else + dma_outb((dmanr & 3) | 4, DMA2_MASK_REG); +} + +/* Clear the 'DMA Pointer Flip Flop'. + * Write 0 for LSB/MSB, 1 for MSB/LSB access. + * Use this once to initialize the FF to a known state. + * After that, keep track of it. :-) + * --- In order to do that, the DMA routines below should --- + * --- only be used while holding the DMA lock ! --- + */ +static __inline__ void clear_dma_ff(unsigned int dmanr) +{ + if (dmanr<=3) + dma_outb(0, DMA1_CLEAR_FF_REG); + else + dma_outb(0, DMA2_CLEAR_FF_REG); +} + +/* set mode (above) for a specific DMA channel */ +static __inline__ void set_dma_mode(unsigned int dmanr, char mode) +{ + if (dmanr<=3) + dma_outb(mode | dmanr, DMA1_MODE_REG); + else + dma_outb(mode | (dmanr&3), DMA2_MODE_REG); +} + +/* Set only the page register bits of the transfer address. + * This is used for successive transfers when we know the contents of + * the lower 16 bits of the DMA current address register, but a 64k boundary + * may have been crossed. + */ +static __inline__ void set_dma_page(unsigned int dmanr, char pagenr) +{ + switch(dmanr) { + case 0: + dma_outb(pagenr, DMA_PAGE_0); + break; + case 1: + dma_outb(pagenr, DMA_PAGE_1); + break; + case 2: + dma_outb(pagenr, DMA_PAGE_2); + break; + case 3: + dma_outb(pagenr, DMA_PAGE_3); + break; + case 5: + dma_outb(pagenr & 0xfe, DMA_PAGE_5); + break; + case 6: + dma_outb(pagenr & 0xfe, DMA_PAGE_6); + break; + case 7: + dma_outb(pagenr & 0xfe, DMA_PAGE_7); + break; + } +} + + +/* Set transfer address & page bits for specific DMA channel. + * Assumes dma flipflop is clear. + */ +static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a) +{ + set_dma_page(dmanr, a>>16); + if (dmanr <= 3) { + dma_outb( a & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE ); + dma_outb( (a>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE ); + } else { + dma_outb( (a>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE ); + dma_outb( (a>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE ); + } +} + + +/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for + * a specific DMA channel. + * You must ensure the parameters are valid. + * NOTE: from a manual: "the number of transfers is one more + * than the initial word count"! This is taken into account. + * Assumes dma flip-flop is clear. + * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7. + */ +static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count) +{ + count--; + if (dmanr <= 3) { + dma_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE ); + dma_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE ); + } else { + dma_outb( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE ); + dma_outb( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE ); + } +} + + +/* Get DMA residue count. After a DMA transfer, this + * should return zero. Reading this while a DMA transfer is + * still in progress will return unpredictable results. + * If called before the channel has been used, it may return 1. + * Otherwise, it returns the number of _bytes_ left to transfer. + * + * Assumes DMA flip-flop is clear. + */ +static __inline__ int get_dma_residue(unsigned int dmanr) +{ + unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE + : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE; + + /* using short to get 16-bit wrap around */ + unsigned short count; + + count = 1 + dma_inb(io_port); + count += dma_inb(io_port) << 8; + + return (dmanr<=3)? count : (count<<1); +} + + +/* These are in kernel/dma.c: */ +extern int request_dma(unsigned int dmanr, const char * device_id); /* reserve a DMA channel */ +extern void free_dma(unsigned int dmanr); /* release it again */ + +/* From PCI */ + +#ifdef CONFIG_PCI +extern int isa_dma_bridge_buggy; +#else +#define isa_dma_bridge_buggy (0) +#endif + +#endif /* _ASM_DMA_H */ diff -Nru a/include/asm-x86_64/e820.h b/include/asm-x86_64/e820.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/e820.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,40 @@ +/* + * structures and definitions for the int 15, ax=e820 memory map + * scheme. + * + * In a nutshell, arch/x86_64/boot/setup.S populates a scratch table + * in the empty_zero_block that contains a list of usable address/size + * duples. In arch/x86_64/kernel/setup.c, this information is + * transferred into the e820map, and in arch/i386/x86_64/init.c, that + * new information is used to mark pages reserved or not. + * + */ +#ifndef __E820_HEADER +#define __E820_HEADER + +#define E820MAP 0x2d0 /* our map */ +#define E820MAX 32 /* number of entries in E820MAP */ +#define E820NR 0x1e8 /* # entries in E820MAP */ + +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */ +#define E820_NVS 4 + +#define HIGH_MEMORY (1024*1024) + +#ifndef __ASSEMBLY__ + +struct e820map { + int nr_map; + struct e820entry { + u64 addr __attribute__((packed)); /* start of memory segment */ + u64 size __attribute__((packed)); /* size of memory segment */ + u32 type __attribute__((packed)); /* type of memory segment */ + } map[E820MAX]; +}; + +extern struct e820map e820; +#endif/*!__ASSEMBLY__*/ + +#endif/*__E820_HEADER*/ diff -Nru a/include/asm-x86_64/elf.h b/include/asm-x86_64/elf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/elf.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,120 @@ +#ifndef __ASM_X86_64_ELF_H +#define __ASM_X86_64_ELF_H + +/* + * ELF register definitions.. + */ + +#include +#include + +typedef unsigned long elf_greg_t; + +#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t)) +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +typedef struct user_i387_struct elf_fpregset_t; +typedef struct user_fxsr_struct elf_fpxregset_t; + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(x) \ + ((x)->e_machine == EM_X8664) + +/* + * These are used to set parameters in the core dumps. + */ +#define ELF_CLASS ELFCLASS64 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_X8664 + +/* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program starts %edx + contains a pointer to a function which might be registered using `atexit'. + This provides a mean for the dynamic linker to call DT_FINI functions for + shared libraries that have been loaded before the code runs. + + A value of 0 tells we have no such handler. + + We might as well make sure everything else is cleared too (except for %rsp), + just to make things more deterministic. + */ +#define ELF_PLAT_INIT(_r) do { \ + struct task_struct *cur = current; \ + (_r)->rbx = 0; (_r)->rcx = 0; (_r)->rdx = 0; \ + (_r)->rsi = 0; (_r)->rdi = 0; (_r)->rbp = 0; \ + (_r)->rax = 0; \ + (_r)->r8 = 0; \ + (_r)->r9 = 0; \ + (_r)->r10 = 0; \ + (_r)->r11 = 0; \ + (_r)->r12 = 0; \ + (_r)->r13 = 0; \ + (_r)->r14 = 0; \ + (_r)->r15 = 0; \ + cur->thread.fs = 0; cur->thread.gs = 0; \ + cur->thread.fsindex = 0; cur->thread.gsindex = 0; \ + cur->thread.ds = 0; cur->thread.es = 0; \ +} while (0) + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +/* This is the location that an ET_DYN program is loaded if exec'ed. Typical + use of this is to invoke "./ld.so someprog" to test out a new version of + the loader. We need to make sure that it is out of the way of the program + that it will "exec", and that there is sufficient room for the brk. */ + +#define ELF_ET_DYN_BASE (2 * TASK_SIZE / 3) + +/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is + now struct_user_regs, they are different) */ + +#define ELF_CORE_COPY_REGS(pr_reg, regs) \ + (pr_reg)[0] = (regs)->r15; \ + (pr_reg)[1] = (regs)->r14; \ + (pr_reg)[2] = (regs)->r13; \ + (pr_reg)[3] = (regs)->r12; \ + (pr_reg)[4] = (regs)->rbp; \ + (pr_reg)[5] = (regs)->rbx; \ + (pr_reg)[6] = (regs)->r11; \ + (pr_reg)[7] = (regs)->r10; \ + (pr_reg)[8] = (regs)->r9; \ + (pr_reg)[9] = (regs)->r8; \ + (pr_reg)[10] = (regs)->rax; \ + (pr_reg)[11] = (regs)->rcx; \ + (pr_reg)[12] = (regs)->rdx; \ + (pr_reg)[13] = (regs)->rsi; \ + (pr_reg)[14] = (regs)->rdi; \ + (pr_reg)[15] = (regs)->orig_rax; \ + (pr_reg)[16] = (regs)->rip; \ + (pr_reg)[17] = (regs)->cs; \ + (pr_reg)[18] = (regs)->eflags; \ + (pr_reg)[19] = (regs)->rsp; \ + (pr_reg)[20] = (regs)->ss; \ + rdmsrl(MSR_FS_BASE, (pr_reg)[21]); \ + rdmsrl(MSR_KERNEL_GS_BASE, (pr_reg)[22]); + +/* This yields a mask that user programs can use to figure out what + instruction set this CPU supports. This could be done in user space, + but it's not easy, and we've already done it here. */ + +#define ELF_HWCAP (boot_cpu_data.x86_capability[0]) + +/* This yields a string that ld.so will use to load implementation + specific libraries for optimization. This is more specific in + intent than poking at uname or /proc/cpuinfo. + + For the moment, we have only optimizations for the Intel generations, + but that could change... */ + +/* I'm not sure if we can use '-' here */ +#define ELF_PLATFORM ("x86_64") + +#ifdef __KERNEL__ +extern void set_personality_64bit(void); +#define SET_PERSONALITY(ex, ibcs2) set_personality_64bit() + +#endif + +#endif diff -Nru a/include/asm-x86_64/errno.h b/include/asm-x86_64/errno.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/errno.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,132 @@ +#ifndef _X8664_ERRNO_H +#define _X8664_ERRNO_H + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + +#endif diff -Nru a/include/asm-x86_64/fcntl.h b/include/asm-x86_64/fcntl.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/fcntl.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,79 @@ +#ifndef _X86_64_FCNTL_H +#define _X86_64_FCNTL_H + +/* open/fcntl - O_SYNC is only implemented on blocks devices and on files + located on an ext2 file system */ +#define O_ACCMODE 0003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 +#define O_CREAT 0100 /* not fcntl */ +#define O_EXCL 0200 /* not fcntl */ +#define O_NOCTTY 0400 /* not fcntl */ +#define O_TRUNC 01000 /* not fcntl */ +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_NDELAY O_NONBLOCK +#define O_SYNC 010000 +#define FASYNC 020000 /* fcntl, for BSD compatibility */ +#define O_DIRECT 040000 /* direct disk access hint */ +#define O_LARGEFILE 0100000 +#define O_DIRECTORY 0200000 /* must be a directory */ +#define O_NOFOLLOW 0400000 /* don't follow links */ + +#define F_DUPFD 0 /* dup */ +#define F_GETFD 1 /* get close_on_exec */ +#define F_SETFD 2 /* set/clear close_on_exec */ +#define F_GETFL 3 /* get file->f_flags */ +#define F_SETFL 4 /* set file->f_flags */ +#define F_GETLK 5 +#define F_SETLK 6 +#define F_SETLKW 7 + +#define F_SETOWN 8 /* for sockets. */ +#define F_GETOWN 9 /* for sockets. */ +#define F_SETSIG 10 /* for sockets. */ +#define F_GETSIG 11 /* for sockets. */ + +/* for F_[GET|SET]FL */ +#define FD_CLOEXEC 1 /* actually anything with low bit set goes */ + +/* for posix fcntl() and lockf() */ +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +/* for old implementation of bsd flock () */ +#define F_EXLCK 4 /* or 3 */ +#define F_SHLCK 8 /* or 4 */ + +/* for leases */ +#define F_INPROGRESS 16 + +/* operations for bsd flock(), also used by the kernel implementation */ +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* or'd with one of the above to prevent + blocking */ +#define LOCK_UN 8 /* remove lock */ + +#define LOCK_MAND 32 /* This is a mandatory flock */ +#define LOCK_READ 64 /* ... Which allows concurrent read operations */ +#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */ +#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */ + +struct flock { + short l_type; + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; +}; + +#define F_LINUX_SPECIFIC_BASE 1024 + +#ifdef __KERNEL__ +#define flock64 flock +#endif + +#endif /* !_X86_64_FCNTL_H */ diff -Nru a/include/asm-x86_64/fixmap.h b/include/asm-x86_64/fixmap.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/fixmap.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,105 @@ +/* + * fixmap.h: compile-time virtual memory allocation + * + * 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 archive + * for more details. + * + * Copyright (C) 1998 Ingo Molnar + */ + +#ifndef _ASM_FIXMAP_H +#define _ASM_FIXMAP_H + +#include +#include +#include +#include +#include + +/* + * Here we define all the compile-time 'special' virtual + * addresses. The point is to have a constant address at + * compile time, but to set the physical address only + * in the boot process. We allocate these special addresses + * from the end of virtual memory (0xfffff000) backwards. + * Also this lets us do fail-safe vmalloc(), we + * can guarantee that these special addresses and + * vmalloc()-ed addresses never overlap. + * + * these 'compile-time allocated' memory buffers are + * fixed-size 4k pages. (or larger if used with an increment + * highger than 1) use fixmap_set(idx,phys) to associate + * physical memory with fixmap indices. + * + * TLB entries of such buffers will not be flushed across + * task switches. + */ + +/* + * on UP currently we will have no trace of the fixmap mechanizm, + * no page table allocations, etc. This might change in the + * future, say framebuffers for the console driver(s) could be + * fix-mapped? + */ +enum fixed_addresses { + VSYSCALL_LAST_PAGE, + VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1, +#ifdef CONFIG_X86_LOCAL_APIC + FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ +#endif +#ifdef CONFIG_X86_IO_APIC + FIX_IO_APIC_BASE_0, + FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS-1, +#endif + __end_of_fixed_addresses +}; + +extern void __set_fixmap (enum fixed_addresses idx, + unsigned long phys, pgprot_t flags); + +#define set_fixmap(idx, phys) \ + __set_fixmap(idx, phys, PAGE_KERNEL) +/* + * Some hardware wants to get fixmapped without caching. + */ +#define set_fixmap_nocache(idx, phys) \ + __set_fixmap(idx, phys, PAGE_KERNEL_NOCACHE) +/* + * used by vmalloc.c. + * + * Leave one empty page between vmalloc'ed areas and + * the start of the fixmap, and leave one page empty + * at the top of mem.. + */ +#define FIXADDR_TOP (VSYSCALL_END-PAGE_SIZE) +#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) +#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE) + +#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT)) + +extern void __this_fixmap_does_not_exist(void); + +/* + * 'index to address' translation. If anyone tries to use the idx + * directly without tranlation, we catch the bug with a NULL-deference + * kernel oops. Illegal ranges of incoming indices are caught too. + */ +extern inline unsigned long fix_to_virt(const unsigned int idx) +{ + /* + * this branch gets completely eliminated after inlining, + * except when someone tries to use fixaddr indices in an + * illegal way. (such as mixing up address types or using + * out-of-range indices). + * + * If it doesn't get removed, the linker will complain + * loudly with a reasonably clear error message.. + */ + if (idx >= __end_of_fixed_addresses) + __this_fixmap_does_not_exist(); + + return __fix_to_virt(idx); +} + +#endif diff -Nru a/include/asm-x86_64/floppy.h b/include/asm-x86_64/floppy.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/floppy.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,286 @@ +/* + * Architecture specific parts of the Floppy driver + * + * 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 archive + * for more details. + * + * Copyright (C) 1995 + */ +#ifndef __ASM_X86_64_FLOPPY_H +#define __ASM_X86_64_FLOPPY_H + +#include + + +/* + * The DMA channel used by the floppy controller cannot access data at + * addresses >= 16MB + * + * Went back to the 1MB limit, as some people had problems with the floppy + * driver otherwise. It doesn't matter much for performance anyway, as most + * floppy accesses go through the track buffer. + */ +#define _CROSS_64KB(a,s,vdma) \ +(!vdma && ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64)) + +#define CROSS_64KB(a,s) _CROSS_64KB(a,s,use_virtual_dma & 1) + + +#define SW fd_routine[use_virtual_dma&1] +#define CSW fd_routine[can_use_virtual_dma & 1] + + +#define fd_inb(port) inb_p(port) +#define fd_outb(port,value) outb_p(port,value) + +#define fd_request_dma() CSW._request_dma(FLOPPY_DMA,"floppy") +#define fd_free_dma() CSW._free_dma(FLOPPY_DMA) +#define fd_enable_irq() enable_irq(FLOPPY_IRQ) +#define fd_disable_irq() disable_irq(FLOPPY_IRQ) +#define fd_free_irq() free_irq(FLOPPY_IRQ, NULL) +#define fd_get_dma_residue() SW._get_dma_residue(FLOPPY_DMA) +#define fd_dma_mem_alloc(size) SW._dma_mem_alloc(size) +#define fd_dma_setup(addr, size, mode, io) SW._dma_setup(addr, size, mode, io) + +#define FLOPPY_CAN_FALLBACK_ON_NODMA + +static int virtual_dma_count; +static int virtual_dma_residue; +static char *virtual_dma_addr; +static int virtual_dma_mode; +static int doing_pdma; + +static void floppy_hardint(int irq, void *dev_id, struct pt_regs * regs) +{ + register unsigned char st; + +#undef TRACE_FLPY_INT + +#ifdef TRACE_FLPY_INT + static int calls=0; + static int bytes=0; + static int dma_wait=0; +#endif + if(!doing_pdma) { + floppy_interrupt(irq, dev_id, regs); + return; + } + +#ifdef TRACE_FLPY_INT + if(!calls) + bytes = virtual_dma_count; +#endif + + { + register int lcount; + register char *lptr; + + st = 1; + for(lcount=virtual_dma_count, lptr=virtual_dma_addr; + lcount; lcount--, lptr++) { + st=inb(virtual_dma_port+4) & 0xa0 ; + if(st != 0xa0) + break; + if(virtual_dma_mode) + outb_p(*lptr, virtual_dma_port+5); + else + *lptr = inb_p(virtual_dma_port+5); + } + virtual_dma_count = lcount; + virtual_dma_addr = lptr; + st = inb(virtual_dma_port+4); + } + +#ifdef TRACE_FLPY_INT + calls++; +#endif + if(st == 0x20) + return; + if(!(st & 0x20)) { + virtual_dma_residue += virtual_dma_count; + virtual_dma_count=0; +#ifdef TRACE_FLPY_INT + printk("count=%x, residue=%x calls=%d bytes=%d dma_wait=%d\n", + virtual_dma_count, virtual_dma_residue, calls, bytes, + dma_wait); + calls = 0; + dma_wait=0; +#endif + doing_pdma = 0; + floppy_interrupt(irq, dev_id, regs); + return; + } +#ifdef TRACE_FLPY_INT + if(!virtual_dma_count) + dma_wait++; +#endif +} + +static void fd_disable_dma(void) +{ + if(! (can_use_virtual_dma & 1)) + disable_dma(FLOPPY_DMA); + doing_pdma = 0; + virtual_dma_residue += virtual_dma_count; + virtual_dma_count=0; +} + +static int vdma_request_dma(unsigned int dmanr, const char * device_id) +{ + return 0; +} + +static void vdma_nop(unsigned int dummy) +{ +} + + +static int vdma_get_dma_residue(unsigned int dummy) +{ + return virtual_dma_count + virtual_dma_residue; +} + + +static int fd_request_irq(void) +{ + if(can_use_virtual_dma) + return request_irq(FLOPPY_IRQ, floppy_hardint,SA_INTERRUPT, + "floppy", NULL); + else + return request_irq(FLOPPY_IRQ, floppy_interrupt, + SA_INTERRUPT|SA_SAMPLE_RANDOM, + "floppy", NULL); + +} + +static unsigned long dma_mem_alloc(unsigned long size) +{ + return __get_dma_pages(GFP_KERNEL,get_order(size)); +} + + +static unsigned long vdma_mem_alloc(unsigned long size) +{ + return (unsigned long) vmalloc(size); + +} + +#define nodma_mem_alloc(size) vdma_mem_alloc(size) + +static void _fd_dma_mem_free(unsigned long addr, unsigned long size) +{ + if((unsigned long) addr >= (unsigned long) high_memory) + return vfree((void *)addr); + else + free_pages(addr, get_order(size)); +} + +#define fd_dma_mem_free(addr, size) _fd_dma_mem_free(addr, size) + +static void _fd_chose_dma_mode(char *addr, unsigned long size) +{ + if(can_use_virtual_dma == 2) { + if((unsigned long) addr >= (unsigned long) high_memory || + isa_virt_to_bus(addr) >= 0x1000000 || + _CROSS_64KB(addr, size, 0)) + use_virtual_dma = 1; + else + use_virtual_dma = 0; + } else { + use_virtual_dma = can_use_virtual_dma & 1; + } +} + +#define fd_chose_dma_mode(addr, size) _fd_chose_dma_mode(addr, size) + + +static int vdma_dma_setup(char *addr, unsigned long size, int mode, int io) +{ + doing_pdma = 1; + virtual_dma_port = io; + virtual_dma_mode = (mode == DMA_MODE_WRITE); + virtual_dma_addr = addr; + virtual_dma_count = size; + virtual_dma_residue = 0; + return 0; +} + +static int hard_dma_setup(char *addr, unsigned long size, int mode, int io) +{ +#ifdef FLOPPY_SANITY_CHECK + if (CROSS_64KB(addr, size)) { + printk("DMA crossing 64-K boundary %p-%p\n", addr, addr+size); + return -1; + } +#endif + /* actual, physical DMA */ + doing_pdma = 0; + clear_dma_ff(FLOPPY_DMA); + set_dma_mode(FLOPPY_DMA,mode); + set_dma_addr(FLOPPY_DMA,isa_virt_to_bus(addr)); + set_dma_count(FLOPPY_DMA,size); + enable_dma(FLOPPY_DMA); + return 0; +} + +struct fd_routine_l { + int (*_request_dma)(unsigned int dmanr, const char * device_id); + void (*_free_dma)(unsigned int dmanr); + int (*_get_dma_residue)(unsigned int dummy); + unsigned long (*_dma_mem_alloc) (unsigned long size); + int (*_dma_setup)(char *addr, unsigned long size, int mode, int io); +} fd_routine[] = { + { + request_dma, + free_dma, + get_dma_residue, + dma_mem_alloc, + hard_dma_setup + }, + { + vdma_request_dma, + vdma_nop, + vdma_get_dma_residue, + vdma_mem_alloc, + vdma_dma_setup + } +}; + + +static int FDC1 = 0x3f0; +static int FDC2 = -1; + +/* + * Floppy types are stored in the rtc's CMOS RAM and so rtc_lock + * is needed to prevent corrupted CMOS RAM in case "insmod floppy" + * coincides with another rtc CMOS user. Paul G. + */ +#define FLOPPY0_TYPE ({ \ + unsigned long flags; \ + unsigned char val; \ + spin_lock_irqsave(&rtc_lock, flags); \ + val = (CMOS_READ(0x10) >> 4) & 15; \ + spin_unlock_irqrestore(&rtc_lock, flags); \ + val; \ +}) + +#define FLOPPY1_TYPE ({ \ + unsigned long flags; \ + unsigned char val; \ + spin_lock_irqsave(&rtc_lock, flags); \ + val = CMOS_READ(0x10) & 15; \ + spin_unlock_irqrestore(&rtc_lock, flags); \ + val; \ +}) + +#define N_FDC 2 +#define N_DRIVE 8 + +#define FLOPPY_MOTOR_MASK 0xf0 + +#define AUTO_DMA + +#define EXTRA_FLOPPY_PARAMS + +#endif /* __ASM_X86_64_FLOPPY_H */ diff -Nru a/include/asm-x86_64/hardirq.h b/include/asm-x86_64/hardirq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/hardirq.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,93 @@ +#ifndef __ASM_HARDIRQ_H +#define __ASM_HARDIRQ_H + +#include +#include +#include +#include + +#define __ARCH_IRQ_STAT 1 + +/* Generate a lvalue for a pda member. Should fix softirq.c instead to use + special access macros. This would generate better code. */ +#define __IRQ_STAT(cpu,member) (read_pda(me)->member) + +typedef struct { + /* Empty. All the fields have moved to the PDA. */ +} irq_cpustat_t; + +#include /* Standard mappings for irq_cpustat_t above */ + +/* + * Are we in an interrupt context? Either doing bottom half + * or hardware interrupt processing? + */ +#define in_interrupt() \ + ((read_pda(__local_irq_count) + read_pda(__local_bh_count)) != 0) +#define in_irq() (read_pda(__local_irq_count) != 0) + +#ifndef CONFIG_SMP + +#define hardirq_trylock(cpu) (local_irq_count() == 0) +#define hardirq_endlock(cpu) do { } while (0) + +#define irq_enter(cpu, irq) (local_irq_count()++) +#define irq_exit(cpu, irq) (local_irq_count()--) + +#define synchronize_irq() barrier() + +#define release_irqlock(cpu) do { } while (0) + +#else + +#include +#include + +extern unsigned char global_irq_holder; +extern unsigned volatile long global_irq_lock; /* long for set_bit -RR */ + +static inline int irqs_running (void) +{ + int i; + + for (i = 0; i < smp_num_cpus; i++) + if (read_pda(__local_irq_count)) + return 1; + return 0; +} + +static inline void release_irqlock(int cpu) +{ + /* if we didn't own the irq lock, just ignore.. */ + if (global_irq_holder == (unsigned char) cpu) { + global_irq_holder = NO_PROC_ID; + clear_bit(0,&global_irq_lock); + } +} + +static inline void irq_enter(int cpu, int irq) +{ + add_pda(__local_irq_count, 1); + + while (test_bit(0,&global_irq_lock)) { + cpu_relax(); + } +} + +static inline void irq_exit(int cpu, int irq) +{ + sub_pda(__local_irq_count, 1); +} + +static inline int hardirq_trylock(int cpu) +{ + return !read_pda(__local_irq_count) && !test_bit(0,&global_irq_lock); +} + +#define hardirq_endlock(cpu) do { } while (0) + +extern void synchronize_irq(void); + +#endif /* CONFIG_SMP */ + +#endif /* __ASM_HARDIRQ_H */ diff -Nru a/include/asm-x86_64/hdreg.h b/include/asm-x86_64/hdreg.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/hdreg.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,12 @@ +/* + * linux/include/asm-x86_64/hdreg.h + * + * Copyright (C) 1994-1996 Linus Torvalds & authors + */ + +#ifndef __ASMx86_64_HDREG_H +#define __ASMx86_64_HDREG_H + +typedef unsigned short ide_ioreg_t; + +#endif /* __ASMx86_64_HDREG_H */ diff -Nru a/include/asm-x86_64/hw_irq.h b/include/asm-x86_64/hw_irq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/hw_irq.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,210 @@ +#ifndef _ASM_HW_IRQ_H +#define _ASM_HW_IRQ_H + +/* + * linux/include/asm/hw_irq.h + * + * (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar + * + * moved some of the old arch/i386/kernel/irq.h to here. VY + * + * IRQ/IPI changes taken from work by Thomas Radke + * + * + * hacked by Andi Kleen for x86-64. + * + * $Id: hw_irq.h,v 1.24 2001/09/14 20:55:03 vojtech Exp $ + */ + +#include +#include +#include + +/* + * IDT vectors usable for external interrupt sources start + * at 0x20: + */ +#define FIRST_EXTERNAL_VECTOR 0x20 + +#define IA32_SYSCALL_VECTOR 0x80 + + +/* + * Vectors 0x20-0x2f are used for ISA interrupts. + */ + +/* + * Special IRQ vectors used by the SMP architecture, 0xf0-0xff + * + * some of the following vectors are 'rare', they are merged + * into a single vector (CALL_FUNCTION_VECTOR) to save vector space. + * TLB, reschedule and local APIC vectors are performance-critical. + * + * Vectors 0xf0-0xf9 are free (reserved for future Linux use). + */ +#define SPURIOUS_APIC_VECTOR 0xff +#define ERROR_APIC_VECTOR 0xfe +#define INVALIDATE_TLB_VECTOR 0xfd +#define RESCHEDULE_VECTOR 0xfc +#define TASK_MIGRATION_VECTOR 0xfb +#define CALL_FUNCTION_VECTOR 0xfa + +/* + * Local APIC timer IRQ vector is on a different priority level, + * to work around the 'lost local interrupt if more than 2 IRQ + * sources per level' errata. + */ +#define LOCAL_TIMER_VECTOR 0xef + +/* + * First APIC vector available to drivers: (vectors 0x30-0xee) + * we start at 0x31 to spread out vectors evenly between priority + * levels. (0x80 is the syscall vector) + */ +#define FIRST_DEVICE_VECTOR 0x31 +#define FIRST_SYSTEM_VECTOR 0xef + +extern int irq_vector[NR_IRQS]; +#define IO_APIC_VECTOR(irq) irq_vector[irq] + +/* + * Various low-level irq details needed by irq.c, process.c, + * time.c, io_apic.c and smp.c + * + * Interrupt entry/exit code at both C and assembly level + */ + +extern void mask_irq(unsigned int irq); +extern void unmask_irq(unsigned int irq); +extern void disable_8259A_irq(unsigned int irq); +extern void enable_8259A_irq(unsigned int irq); +extern int i8259A_irq_pending(unsigned int irq); +extern void make_8259A_irq(unsigned int irq); +extern void init_8259A(int aeoi); +extern void FASTCALL(send_IPI_self(int vector)); +extern void init_VISWS_APIC_irqs(void); +extern void setup_IO_APIC(void); +extern void disable_IO_APIC(void); +extern void print_IO_APIC(void); +extern int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn); +extern void send_IPI(int dest, int vector); + +extern unsigned long io_apic_irqs; + +extern atomic_t irq_err_count; +extern atomic_t irq_mis_count; + +extern char _stext, _etext; + +#define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs)) + +#define __STR(x) #x +#define STR(x) __STR(x) + +#include + +#ifdef CONFIG_PREEMPT +#define PREEMPT_LOCK \ +" movq %rsp,%rdx ;" \ +" andq $-8192,%rdx ;" \ +" incl " __STR(threadinfo_preempt_count)"(%rdx) ;" +#else +#define PREEMPT_LOCK +#endif + +/* IF:off, stack contains irq number on origrax */ +#define IRQ_ENTER \ +" cld ;" \ +" pushq %rdi ;" \ +" pushq %rsi ;" \ +" pushq %rdx ;" \ +" pushq %rcx ;" \ +" pushq %rax ;" \ +" pushq %r8 ;" \ +" pushq %r9 ;" \ +" pushq %r10 ;" \ +" pushq %r11 ;" \ + PREEMPT_LOCK \ +" leaq -48(%rsp),%rdi # arg1 for handler ;" \ +" cmpq $ " __STR(__KERNEL_CS) ",88(%rsp) # CS - ARGOFFSET ;" \ +" je 1f ;" \ +" swapgs ;" \ +"1: addl $1,%gs: " __STR(pda_irqcount) ";" \ +" movq %gs: " __STR(pda_irqstackptr) ",%rax ;" \ +" cmoveq %rax,%rsp ;" + +#define IRQ_NAME2(nr) nr##_interrupt(void) +#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr) + +/* + * SMP has a few special interrupts for IPI messages + */ + + /* there is a second layer of macro just to get the symbolic + name for the vector evaluated. This change is for RTLinux */ +#define BUILD_SMP_INTERRUPT(x,v) XBUILD_SMP_INTERRUPT(x,v) +#define XBUILD_SMP_INTERRUPT(x,v)\ +asmlinkage void x(void); \ +asmlinkage void call_##x(void); \ +__asm__( \ +"\n"__ALIGN_STR"\n" \ +SYMBOL_NAME_STR(x) ":\n\t" \ + "push $" #v "-256;" \ + IRQ_ENTER \ + "pushq %rdi ; " \ + "call " SYMBOL_NAME_STR(smp_##x) " ; " \ + "jmp ret_from_intr") + +#define BUILD_COMMON_IRQ() + +#define BUILD_IRQ(nr) \ +asmlinkage void IRQ_NAME(nr); \ +__asm__( \ +"\n"__ALIGN_STR "\n" \ +SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \ + "push $" #nr "-256 ; " \ + "jmp common_interrupt"); + +extern unsigned long prof_cpu_mask; +extern unsigned int * prof_buffer; +extern unsigned long prof_len; +extern unsigned long prof_shift; + +/* + * x86 profiling function, SMP safe. We might want to do this in + * assembly totally? + */ +static inline void x86_do_profile (unsigned long rip) +{ + if (!prof_buffer) + return; + + /* + * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. + * (default is all CPUs.) + */ + if (!((1<>= prof_shift; + /* + * Don't ignore out-of-bounds EIP values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (rip > prof_len-1) + rip = prof_len-1; + atomic_inc((atomic_t *)&prof_buffer[rip]); +} + +#ifdef CONFIG_SMP /*more of this file should probably be ifdefed SMP */ +static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { + if (IO_APIC_IRQ(i)) + send_IPI_self(IO_APIC_VECTOR(i)); +} +#else +static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} +#endif + +#endif /* _ASM_HW_IRQ_H */ diff -Nru a/include/asm-x86_64/i387.h b/include/asm-x86_64/i387.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/i387.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,87 @@ +/* + * include/asm-x86_64/i387.h + * + * Copyright (C) 1994 Linus Torvalds + * + * Pentium III FXSR, SSE support + * General FPU state handling cleanups + * Gareth Hughes , May 2000 + */ + +#ifndef __ASM_X86_64_I387_H +#define __ASM_X86_64_I387_H + +#include +#include +#include +#include +#include + +extern void init_fpu(void); + +/* + * FPU lazy state save handling... + */ +extern void save_fpu( struct task_struct *tsk ); +extern void save_init_fpu( struct task_struct *tsk ); +extern void restore_fpu( struct task_struct *tsk ); + +extern void kernel_fpu_begin(void); +#define kernel_fpu_end() do { stts(); preempt_enable(); } while(0) + + +#define unlazy_fpu( tsk ) do { \ + if (test_tsk_thread_flag(tsk, TIF_USEDFPU)) \ + save_init_fpu( tsk ); \ +} while (0) + +#define clear_fpu( tsk ) \ +do { \ + if (test_tsk_thread_flag(tsk, TIF_USEDFPU)) { \ + asm volatile("fwait"); \ + clear_tsk_thread_flag(tsk, TIF_USEDFPU); \ + stts(); \ + } \ +} while (0) + +/* + * FPU state interaction... + */ +extern unsigned short get_fpu_cwd( struct task_struct *tsk ); +extern unsigned short get_fpu_swd( struct task_struct *tsk ); +extern unsigned short get_fpu_twd( struct task_struct *tsk ); +extern unsigned short get_fpu_mxcsr( struct task_struct *tsk ); + +extern void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd ); +extern void set_fpu_swd( struct task_struct *tsk, unsigned short swd ); +extern void set_fpu_twd( struct task_struct *tsk, unsigned short twd ); +extern void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr ); + +#define load_mxcsr( val ) do { \ + unsigned long __mxcsr = ((unsigned long)(val) & 0xffbf); \ + asm volatile( "ldmxcsr %0" : : "m" (__mxcsr) ); \ +} while (0) + +/* + * Signal frame handlers... + */ +extern int save_i387( struct _fpstate *buf ); +extern int restore_i387( struct _fpstate *buf ); + +/* + * ptrace request handers... + */ +extern int get_fpregs( struct user_i387_struct *buf, + struct task_struct *tsk ); +extern int set_fpregs( struct task_struct *tsk, + struct user_i387_struct *buf ); + +/* + * FPU state for core dumps... + */ +extern int dump_fpu( struct pt_regs *regs, + struct user_i387_struct *fpu ); +extern int dump_extended_fpu( struct pt_regs *regs, + struct user_i387_struct *fpu ); + +#endif /* __ASM_X86_64_I387_H */ diff -Nru a/include/asm-x86_64/ia32.h b/include/asm-x86_64/ia32.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/ia32.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,274 @@ +#ifndef _ASM_X86_64_IA32_H +#define _ASM_X86_64_IA32_H + +#include + +#ifdef CONFIG_IA32_EMULATION + +/* + * 32 bit structures for IA32 support. + */ + +/* 32bit compatibility types */ +typedef unsigned int __kernel_size_t32; +typedef int __kernel_ssize_t32; +typedef int __kernel_ptrdiff_t32; +typedef int __kernel_time_t32; +typedef int __kernel_clock_t32; +typedef int __kernel_pid_t32; +typedef unsigned short __kernel_ipc_pid_t32; +typedef unsigned short __kernel_uid_t32; +typedef unsigned short __kernel_gid_t32; +typedef unsigned short __kernel_dev_t32; +typedef unsigned int __kernel_ino_t32; +typedef unsigned short __kernel_mode_t32; +typedef unsigned short __kernel_umode_t32; +typedef short __kernel_nlink_t32; +typedef int __kernel_daddr_t32; +typedef int __kernel_off_t32; +typedef unsigned int __kernel_caddr_t32; +typedef long __kernel_loff_t32; +typedef __kernel_fsid_t __kernel_fsid_t32; + + +/* fcntl.h */ +struct flock32 { + short l_type; + short l_whence; + __kernel_off_t32 l_start; + __kernel_off_t32 l_len; + __kernel_pid_t32 l_pid; +}; + + +struct ia32_flock64 { + short l_type; + short l_whence; + loff_t l_start; /* unnatural alignment */ + loff_t l_len; + pid_t l_pid; +} __attribute__((packed)); + +#define F_GETLK64 12 /* using 'struct flock64' */ +#define F_SETLK64 13 +#define F_SETLKW64 14 + + + +/* sigcontext.h */ +/* The x86-64 port uses FXSAVE without prefix; thus a 32bit compatible + FXSAVE layout. The additional XMM registers are added, but they're + in currently unused space. Hopefully nobody else will use them*/ +#define _fpstate_ia32 _fpstate + +struct sigcontext_ia32 { + unsigned short gs, __gsh; + unsigned short fs, __fsh; + unsigned short es, __esh; + unsigned short ds, __dsh; + unsigned int edi; + unsigned int esi; + unsigned int ebp; + unsigned int esp; + unsigned int ebx; + unsigned int edx; + unsigned int ecx; + unsigned int eax; + unsigned int trapno; + unsigned int err; + unsigned int eip; + unsigned short cs, __csh; + unsigned int eflags; + unsigned int esp_at_signal; + unsigned short ss, __ssh; + unsigned int fpstate; /* really (struct _fpstate_ia32 *) */ + unsigned int oldmask; + unsigned int cr2; +}; + +/* signal.h */ +#define _IA32_NSIG 64 +#define _IA32_NSIG_BPW 32 +#define _IA32_NSIG_WORDS (_IA32_NSIG / _IA32_NSIG_BPW) + +typedef struct { + unsigned int sig[_IA32_NSIG_WORDS]; +} sigset32_t; + +struct sigaction32 { + unsigned int sa_handler; /* Really a pointer, but need to deal + with 32 bits */ + unsigned int sa_flags; + unsigned int sa_restorer; /* Another 32 bit pointer */ + sigset32_t sa_mask; /* A 32 bit mask */ +}; + +typedef unsigned int old_sigset32_t; /* at least 32 bits */ + +struct old_sigaction32 { + unsigned int sa_handler; /* Really a pointer, but need to deal + with 32 bits */ + old_sigset32_t sa_mask; /* A 32 bit mask */ + unsigned int sa_flags; + unsigned int sa_restorer; /* Another 32 bit pointer */ +}; + +typedef struct sigaltstack_ia32 { + unsigned int ss_sp; + int ss_flags; + unsigned int ss_size; +} stack_ia32_t; + +struct ucontext_ia32 { + unsigned int uc_flags; + unsigned int uc_link; + stack_ia32_t uc_stack; + struct sigcontext_ia32 uc_mcontext; + sigset32_t uc_sigmask; /* mask last for extensibility */ +}; + +struct stat32 { + unsigned short st_dev; + unsigned short __pad1; + unsigned int st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned short __pad2; + unsigned int st_size; + unsigned int st_blksize; + unsigned int st_blocks; + unsigned int st_atime; + unsigned int __unused1; + unsigned int st_mtime; + unsigned int __unused2; + unsigned int st_ctime; + unsigned int __unused3; + unsigned int __unused4; + unsigned int __unused5; +}; + + +/* This matches struct stat64 in glibc2.2, hence the absolutely + * insane amounts of padding around dev_t's. + */ +struct stat64 { + unsigned long long st_dev; + unsigned char __pad0[4]; + +#define STAT64_HAS_BROKEN_ST_INO 1 + unsigned int __st_ino; + + unsigned int st_mode; + unsigned int st_nlink; + + unsigned int st_uid; + unsigned int st_gid; + + unsigned long long st_rdev; + unsigned char __pad3[4]; + + long long st_size; + unsigned int st_blksize; + + long long st_blocks;/* Number 512-byte blocks allocated. */ + + unsigned long long st_atime; + unsigned long long st_mtime; + unsigned long long st_ctime; + + unsigned long long st_ino; +} __attribute__((packed)); + + +struct statfs32 { + int f_type; + int f_bsize; + int f_blocks; + int f_bfree; + int f_bavail; + int f_files; + int f_ffree; + __kernel_fsid_t32 f_fsid; + int f_namelen; /* SunOS ignores this field. */ + int f_spare[6]; +}; + +typedef union sigval32 { + int sival_int; + unsigned int sival_ptr; +} sigval_t32; + +typedef struct siginfo32 { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[((128/sizeof(int)) - 3)]; + + /* kill() */ + struct { + unsigned int _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + unsigned int _timer1; + unsigned int _timer2; + } _timer; + + /* POSIX.1b signals */ + struct { + unsigned int _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + sigval_t32 _sigval; + } _rt; + + /* SIGCHLD */ + struct { + unsigned int _pid; /* which child */ + unsigned int _uid; /* sender's uid */ + int _status; /* exit code */ + __kernel_clock_t32 _utime; + __kernel_clock_t32 _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + unsigned int _addr; /* faulting insn/memory ref. */ + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} siginfo_t32; + + +struct ustat32 { + __u32 f_tfree; + __kernel_ino_t32 f_tinode; + char f_fname[6]; + char f_fpack[6]; +}; + +struct iovec32 { + unsigned int iov_base; + int iov_len; +}; + + +#ifdef __KERNEL__ +struct iovec *get_iovec32(struct iovec32 *iov32, struct iovec *iov_buf, u32 count, int type); +#endif + + +#endif /* !CONFIG_IA32_SUPPORT */ + +#endif diff -Nru a/include/asm-x86_64/ia32_unistd.h b/include/asm-x86_64/ia32_unistd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/ia32_unistd.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,251 @@ +#ifndef _ASM_X86_64_IA32_UNISTD_H_ +#define _ASM_X86_64_IA32_UNISTD_H_ + +/* + * This file contains the system call numbers of the ia32 port, + * this is for the kernel only. + */ + +#define __NR_ia32_exit 1 +#define __NR_ia32_fork 2 +#define __NR_ia32_read 3 +#define __NR_ia32_write 4 +#define __NR_ia32_open 5 +#define __NR_ia32_close 6 +#define __NR_ia32_waitpid 7 +#define __NR_ia32_creat 8 +#define __NR_ia32_link 9 +#define __NR_ia32_unlink 10 +#define __NR_ia32_execve 11 +#define __NR_ia32_chdir 12 +#define __NR_ia32_time 13 +#define __NR_ia32_mknod 14 +#define __NR_ia32_chmod 15 +#define __NR_ia32_lchown 16 +#define __NR_ia32_break 17 +#define __NR_ia32_oldstat 18 +#define __NR_ia32_lseek 19 +#define __NR_ia32_getpid 20 +#define __NR_ia32_mount 21 +#define __NR_ia32_umount 22 +#define __NR_ia32_setuid 23 +#define __NR_ia32_getuid 24 +#define __NR_ia32_stime 25 +#define __NR_ia32_ptrace 26 +#define __NR_ia32_alarm 27 +#define __NR_ia32_oldfstat 28 +#define __NR_ia32_pause 29 +#define __NR_ia32_utime 30 +#define __NR_ia32_stty 31 +#define __NR_ia32_gtty 32 +#define __NR_ia32_access 33 +#define __NR_ia32_nice 34 +#define __NR_ia32_ftime 35 +#define __NR_ia32_sync 36 +#define __NR_ia32_kill 37 +#define __NR_ia32_rename 38 +#define __NR_ia32_mkdir 39 +#define __NR_ia32_rmdir 40 +#define __NR_ia32_dup 41 +#define __NR_ia32_pipe 42 +#define __NR_ia32_times 43 +#define __NR_ia32_prof 44 +#define __NR_ia32_brk 45 +#define __NR_ia32_setgid 46 +#define __NR_ia32_getgid 47 +#define __NR_ia32_signal 48 +#define __NR_ia32_geteuid 49 +#define __NR_ia32_getegid 50 +#define __NR_ia32_acct 51 +#define __NR_ia32_umount2 52 +#define __NR_ia32_lock 53 +#define __NR_ia32_ioctl 54 +#define __NR_ia32_fcntl 55 +#define __NR_ia32_mpx 56 +#define __NR_ia32_setpgid 57 +#define __NR_ia32_ulimit 58 +#define __NR_ia32_oldolduname 59 +#define __NR_ia32_umask 60 +#define __NR_ia32_chroot 61 +#define __NR_ia32_ustat 62 +#define __NR_ia32_dup2 63 +#define __NR_ia32_getppid 64 +#define __NR_ia32_getpgrp 65 +#define __NR_ia32_setsid 66 +#define __NR_ia32_sigaction 67 +#define __NR_ia32_sgetmask 68 +#define __NR_ia32_ssetmask 69 +#define __NR_ia32_setreuid 70 +#define __NR_ia32_setregid 71 +#define __NR_ia32_sigsuspend 72 +#define __NR_ia32_sigpending 73 +#define __NR_ia32_sethostname 74 +#define __NR_ia32_setrlimit 75 +#define __NR_ia32_getrlimit 76 /* Back compatible 2Gig limited rlimit */ +#define __NR_ia32_getrusage 77 +#define __NR_ia32_gettimeofday 78 +#define __NR_ia32_settimeofday 79 +#define __NR_ia32_getgroups 80 +#define __NR_ia32_setgroups 81 +#define __NR_ia32_select 82 +#define __NR_ia32_symlink 83 +#define __NR_ia32_oldlstat 84 +#define __NR_ia32_readlink 85 +#define __NR_ia32_uselib 86 +#define __NR_ia32_swapon 87 +#define __NR_ia32_reboot 88 +#define __NR_ia32_readdir 89 +#define __NR_ia32_mmap 90 +#define __NR_ia32_munmap 91 +#define __NR_ia32_truncate 92 +#define __NR_ia32_ftruncate 93 +#define __NR_ia32_fchmod 94 +#define __NR_ia32_fchown 95 +#define __NR_ia32_getpriority 96 +#define __NR_ia32_setpriority 97 +#define __NR_ia32_profil 98 +#define __NR_ia32_statfs 99 +#define __NR_ia32_fstatfs 100 +#define __NR_ia32_ioperm 101 +#define __NR_ia32_socketcall 102 +#define __NR_ia32_syslog 103 +#define __NR_ia32_setitimer 104 +#define __NR_ia32_getitimer 105 +#define __NR_ia32_stat 106 +#define __NR_ia32_lstat 107 +#define __NR_ia32_fstat 108 +#define __NR_ia32_olduname 109 +#define __NR_ia32_iopl 110 +#define __NR_ia32_vhangup 111 +#define __NR_ia32_idle 112 +#define __NR_ia32_vm86old 113 +#define __NR_ia32_wait4 114 +#define __NR_ia32_swapoff 115 +#define __NR_ia32_sysinfo 116 +#define __NR_ia32_ipc 117 +#define __NR_ia32_fsync 118 +#define __NR_ia32_sigreturn 119 +#define __NR_ia32_clone 120 +#define __NR_ia32_setdomainname 121 +#define __NR_ia32_uname 122 +#define __NR_ia32_modify_ldt 123 +#define __NR_ia32_adjtimex 124 +#define __NR_ia32_mprotect 125 +#define __NR_ia32_sigprocmask 126 +#define __NR_ia32_create_module 127 +#define __NR_ia32_init_module 128 +#define __NR_ia32_delete_module 129 +#define __NR_ia32_get_kernel_syms 130 +#define __NR_ia32_quotactl 131 +#define __NR_ia32_getpgid 132 +#define __NR_ia32_fchdir 133 +#define __NR_ia32_bdflush 134 +#define __NR_ia32_sysfs 135 +#define __NR_ia32_personality 136 +#define __NR_ia32_afs_syscall 137 /* Syscall for Andrew File System */ +#define __NR_ia32_setfsuid 138 +#define __NR_ia32_setfsgid 139 +#define __NR_ia32__llseek 140 +#define __NR_ia32_getdents 141 +#define __NR_ia32__newselect 142 +#define __NR_ia32_flock 143 +#define __NR_ia32_msync 144 +#define __NR_ia32_readv 145 +#define __NR_ia32_writev 146 +#define __NR_ia32_getsid 147 +#define __NR_ia32_fdatasync 148 +#define __NR_ia32__sysctl 149 +#define __NR_ia32_mlock 150 +#define __NR_ia32_munlock 151 +#define __NR_ia32_mlockall 152 +#define __NR_ia32_munlockall 153 +#define __NR_ia32_sched_setparam 154 +#define __NR_ia32_sched_getparam 155 +#define __NR_ia32_sched_setscheduler 156 +#define __NR_ia32_sched_getscheduler 157 +#define __NR_ia32_sched_yield 158 +#define __NR_ia32_sched_get_priority_max 159 +#define __NR_ia32_sched_get_priority_min 160 +#define __NR_ia32_sched_rr_get_interval 161 +#define __NR_ia32_nanosleep 162 +#define __NR_ia32_mremap 163 +#define __NR_ia32_setresuid 164 +#define __NR_ia32_getresuid 165 +#define __NR_ia32_vm86 166 +#define __NR_ia32_query_module 167 +#define __NR_ia32_poll 168 +#define __NR_ia32_nfsservctl 169 +#define __NR_ia32_setresgid 170 +#define __NR_ia32_getresgid 171 +#define __NR_ia32_prctl 172 +#define __NR_ia32_rt_sigreturn 173 +#define __NR_ia32_rt_sigaction 174 +#define __NR_ia32_rt_sigprocmask 175 +#define __NR_ia32_rt_sigpending 176 +#define __NR_ia32_rt_sigtimedwait 177 +#define __NR_ia32_rt_sigqueueinfo 178 +#define __NR_ia32_rt_sigsuspend 179 +#define __NR_ia32_pread 180 +#define __NR_ia32_pwrite 181 +#define __NR_ia32_chown 182 +#define __NR_ia32_getcwd 183 +#define __NR_ia32_capget 184 +#define __NR_ia32_capset 185 +#define __NR_ia32_sigaltstack 186 +#define __NR_ia32_sendfile 187 +#define __NR_ia32_getpmsg 188 /* some people actually want streams */ +#define __NR_ia32_putpmsg 189 /* some people actually want streams */ +#define __NR_ia32_vfork 190 +#define __NR_ia32_ugetrlimit 191 /* SuS compliant getrlimit */ +#define __NR_ia32_mmap2 192 +#define __NR_ia32_truncate64 193 +#define __NR_ia32_ftruncate64 194 +#define __NR_ia32_stat64 195 +#define __NR_ia32_lstat64 196 +#define __NR_ia32_fstat64 197 +#define __NR_ia32_lchown32 198 +#define __NR_ia32_getuid32 199 +#define __NR_ia32_getgid32 200 +#define __NR_ia32_geteuid32 201 +#define __NR_ia32_getegid32 202 +#define __NR_ia32_setreuid32 203 +#define __NR_ia32_setregid32 204 +#define __NR_ia32_getgroups32 205 +#define __NR_ia32_setgroups32 206 +#define __NR_ia32_fchown32 207 +#define __NR_ia32_setresuid32 208 +#define __NR_ia32_getresuid32 209 +#define __NR_ia32_setresgid32 210 +#define __NR_ia32_getresgid32 211 +#define __NR_ia32_chown32 212 +#define __NR_ia32_setuid32 213 +#define __NR_ia32_setgid32 214 +#define __NR_ia32_setfsuid32 215 +#define __NR_ia32_setfsgid32 216 +#define __NR_ia32_pivot_root 217 +#define __NR_ia32_mincore 218 +#define __NR_ia32_madvise 219 +#define __NR_ia32_madvise1 219 /* delete when C lib stub is removed */ +#define __NR_ia32_getdents64 220 +#define __NR_ia32_fcntl64 221 +#define __NR_ia32_tuxcall 222 +#define __NR_ia32_security 223 +#define __NR_ia32_gettid 224 +#define __NR_ia32_readahead 225 +#define __NR_ia32_setxattr 226 +#define __NR_ia32_lsetxattr 227 +#define __NR_ia32_fsetxattr 228 +#define __NR_ia32_getxattr 229 +#define __NR_ia32_lgetxattr 230 +#define __NR_ia32_fgetxattr 231 +#define __NR_ia32_listxattr 232 +#define __NR_ia32_llistxattr 233 +#define __NR_ia32_flistxattr 234 +#define __NR_ia32_removexattr 235 +#define __NR_ia32_lremovexattr 236 +#define __NR_ia32_fremovexattr 237 +#define __NR_ia32_tkill 238 + +#define IA32_NR_syscalls 240 /* must be > than biggest syscall! */ + +#endif /* _ASM_X86_64_IA32_UNISTD_H_ */ diff -Nru a/include/asm-x86_64/ide.h b/include/asm-x86_64/ide.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/ide.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,128 @@ +/* + * linux/include/asm-i386/ide.h + * + * Copyright (C) 1994-1996 Linus Torvalds & authors + */ + +/* + * This file contains the i386 architecture specific IDE code. + */ + +#ifndef __ASMi386_IDE_H +#define __ASMi386_IDE_H + +#ifdef __KERNEL__ + +#include + +#ifndef MAX_HWIFS +# ifdef CONFIG_BLK_DEV_IDEPCI +#define MAX_HWIFS 10 +# else +#define MAX_HWIFS 6 +# endif +#endif + +#define ide__sti() __sti() + +static __inline__ int ide_default_irq(ide_ioreg_t base) +{ + switch (base) { + case 0x1f0: return 14; + case 0x170: return 15; + case 0x1e8: return 11; + case 0x168: return 10; + case 0x1e0: return 8; + case 0x160: return 12; + default: + return 0; + } +} + +static __inline__ ide_ioreg_t ide_default_io_base(int index) +{ + switch (index) { + case 0: return 0x1f0; + case 1: return 0x170; + case 2: return 0x1e8; + case 3: return 0x168; + case 4: return 0x1e0; + case 5: return 0x160; + default: + return 0; + } +} + +static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) +{ + ide_ioreg_t reg = data_port; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = reg; + reg += 1; + } + if (ctrl_port) { + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + } else { + hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206; + } + if (irq != NULL) + *irq = 0; + hw->io_ports[IDE_IRQ_OFFSET] = 0; +} + +static __inline__ void ide_init_default_hwifs(void) +{ +#ifndef CONFIG_BLK_DEV_IDEPCI + hw_regs_t hw; + int index; + + for(index = 0; index < MAX_HWIFS; index++) { + ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); + hw.irq = ide_default_irq(ide_default_io_base(index)); + ide_register_hw(&hw, NULL); + } +#endif /* CONFIG_BLK_DEV_IDEPCI */ +} + +typedef union { + unsigned all : 8; /* all of the bits together */ + struct { + unsigned head : 4; /* always zeros here */ + unsigned unit : 1; /* drive select number, 0 or 1 */ + unsigned bit5 : 1; /* always 1 */ + unsigned lba : 1; /* using LBA instead of CHS */ + unsigned bit7 : 1; /* always 1 */ + } b; +} select_t; + +typedef union { + unsigned all : 8; /* all of the bits together */ + struct { + unsigned bit0 : 1; + unsigned nIEN : 1; /* device INTRQ to host */ + unsigned SRST : 1; /* host soft reset bit */ + unsigned bit3 : 1; /* ATA-2 thingy */ + unsigned reserved456 : 3; + unsigned HOB : 1; /* 48-bit address ordering */ + } b; +} control_t; + +#define ide_request_irq(irq,hand,flg,dev,id) request_irq((irq),(hand),(flg),(dev),(id)) +#define ide_free_irq(irq,dev_id) free_irq((irq), (dev_id)) +#define ide_check_region(from,extent) check_region((from), (extent)) +#define ide_request_region(from,extent,name) request_region((from), (extent), (name)) +#define ide_release_region(from,extent) release_region((from), (extent)) + +/* + * The following are not needed for the non-m68k ports + */ +#define ide_ack_intr(hwif) (1) +#define ide_fix_driveid(id) do {} while (0) +#define ide_release_lock(lock) do {} while (0) +#define ide_get_lock(lock, hdlr, data) do {} while (0) + +#endif /* __KERNEL__ */ + +#endif /* __ASMi386_IDE_H */ diff -Nru a/include/asm-x86_64/init.h b/include/asm-x86_64/init.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/init.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1 @@ +#error " should never be used - use instead" diff -Nru a/include/asm-x86_64/io.h b/include/asm-x86_64/io.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/io.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,270 @@ +#ifndef _ASM_IO_H +#define _ASM_IO_H + +/* + * This file contains the definitions for the x86 IO instructions + * inb/inw/inl/outb/outw/outl and the "string versions" of the same + * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing" + * versions of the single-IO instructions (inb_p/inw_p/..). + * + * This file is not meant to be obfuscating: it's just complicated + * to (a) handle it all in a way that makes gcc able to optimize it + * as well as possible and (b) trying to avoid writing the same thing + * over and over again with slight variations and possibly making a + * mistake somewhere. + */ + +/* + * Thanks to James van Artsdalen for a better timing-fix than + * the two short jumps: using outb's to a nonexistent port seems + * to guarantee better timings even on fast machines. + * + * On the other hand, I'd like to be sure of a non-existent port: + * I feel a bit unsafe about using 0x80 (should be safe, though) + * + * Linus + */ + + /* + * Bit simplified and optimized by Jan Hubicka + * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999. + * + * isa_memset_io, isa_memcpy_fromio, isa_memcpy_toio added, + * isa_read[wl] and isa_write[wl] fixed + * - Arnaldo Carvalho de Melo + */ + +#ifdef SLOW_IO_BY_JUMPING +#define __SLOW_DOWN_IO "\njmp 1f\n1:\tjmp 1f\n1:" +#else +#define __SLOW_DOWN_IO "\noutb %%al,$0x80" +#endif + +#ifdef REALLY_SLOW_IO +#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO +#else +#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO +#endif + +/* + * Talk about misusing macros.. + */ +#define __OUT1(s,x) \ +extern inline void out##s(unsigned x value, unsigned short port) { + +#define __OUT2(s,s1,s2) \ +__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" + +#define __OUT(s,s1,x) \ +__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ +__OUT1(s##_p,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} \ + +#define __IN1(s) \ +extern inline RETURN_TYPE in##s(unsigned short port) { RETURN_TYPE _v; + +#define __IN2(s,s1,s2) \ +__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" + +#define __IN(s,s1,i...) \ +__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ +__IN1(s##_p) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ + +#define __INS(s) \ +extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \ +{ __asm__ __volatile__ ("rep ; ins" #s \ +: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } + +#define __OUTS(s) \ +extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \ +{ __asm__ __volatile__ ("rep ; outs" #s \ +: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } + +#define RETURN_TYPE unsigned char +__IN(b,"") +#undef RETURN_TYPE +#define RETURN_TYPE unsigned short +__IN(w,"") +#undef RETURN_TYPE +#define RETURN_TYPE unsigned int +__IN(l,"") +#undef RETURN_TYPE + +__OUT(b,"b",char) +__OUT(w,"w",short) +__OUT(l,,int) + +__INS(b) +__INS(w) +__INS(l) + +__OUTS(b) +__OUTS(w) +__OUTS(l) + +#define IO_SPACE_LIMIT 0xffff + +#ifdef __KERNEL__ + +#include + +/* + * Temporary debugging check to catch old code using + * unmapped ISA addresses. Will be removed in 2.4. + */ +#ifdef CONFIG_IO_DEBUG + extern void *__io_virt_debug(unsigned long x, const char *file, int line); + #define __io_virt(x) __io_virt_debug((unsigned long)(x), __FILE__, __LINE__) +#else + #define __io_virt(x) ((void *)(x)) +#endif + +/* + * Change virtual addresses to physical addresses and vv. + * These are pretty trivial + */ +extern inline unsigned long virt_to_phys(volatile void * address) +{ + return __pa(address); +} + +extern inline void * phys_to_virt(unsigned long address) +{ + return __va(address); +} + +/* + * Change "struct page" to physical address. + */ +#define page_to_phys(page) ((page - mem_map) << PAGE_SHIFT) + +extern void * __ioremap(unsigned long offset, unsigned long size, unsigned long flags); + +extern inline void * ioremap (unsigned long offset, unsigned long size) +{ + return __ioremap(offset, size, 0); +} + +/* + * This one maps high address device memory and turns off caching for that area. + * it's useful if some control registers are in such an area and write combining + * or read caching is not desirable: + */ +extern inline void * ioremap_nocache (unsigned long offset, unsigned long size) +{ + return __ioremap(offset, size, _PAGE_PCD); +} + +extern void iounmap(void *addr); + +/* + * ISA I/O bus memory addresses are 1:1 with the physical address. + */ +#define isa_virt_to_bus virt_to_phys +#define isa_page_to_bus page_to_phys +#define isa_bus_to_virt phys_to_virt + +/* + * However PCI ones are not necessarily 1:1 and therefore these interfaces + * are forbidden in portable PCI drivers. + */ +extern unsigned long virt_to_bus_not_defined_use_pci_map(volatile void *addr); +#define virt_to_bus virt_to_bus_not_defined_use_pci_map +extern unsigned long bus_to_virt_not_defined_use_pci_map(volatile void *addr); +#define bus_to_virt bus_to_virt_not_defined_use_pci_map + +/* + * readX/writeX() are used to access memory mapped devices. On some + * architectures the memory mapped IO stuff needs to be accessed + * differently. On the x86 architecture, we just read/write the + * memory location directly. + */ + +#define readb(addr) (*(volatile unsigned char *) __io_virt(addr)) +#define readw(addr) (*(volatile unsigned short *) __io_virt(addr)) +#define readl(addr) (*(volatile unsigned int *) __io_virt(addr)) +#define __raw_readb readb +#define __raw_readw readw +#define __raw_readl readl + +#define writeb(b,addr) (*(volatile unsigned char *) __io_virt(addr) = (b)) +#define writew(b,addr) (*(volatile unsigned short *) __io_virt(addr) = (b)) +#define writel(b,addr) (*(volatile unsigned int *) __io_virt(addr) = (b)) +#define __raw_writeb writeb +#define __raw_writew writew +#define __raw_writel writel + +#define memset_io(a,b,c) memset(__io_virt(a),(b),(c)) +#define memcpy_fromio(a,b,c) memcpy((a),__io_virt(b),(c)) +#define memcpy_toio(a,b,c) memcpy(__io_virt(a),(b),(c)) + +/* + * ISA space is 'always mapped' on a typical x86 system, no need to + * explicitly ioremap() it. The fact that the ISA IO space is mapped + * to PAGE_OFFSET is pure coincidence - it does not mean ISA values + * are physical addresses. The following constant pointer can be + * used as the IO-area pointer (it can be iounmapped as well, so the + * analogy with PCI is quite large): + */ +#define __ISA_IO_base ((char *)(PAGE_OFFSET)) + +#define isa_readb(a) readb(__ISA_IO_base + (a)) +#define isa_readw(a) readw(__ISA_IO_base + (a)) +#define isa_readl(a) readl(__ISA_IO_base + (a)) +#define isa_writeb(b,a) writeb(b,__ISA_IO_base + (a)) +#define isa_writew(w,a) writew(w,__ISA_IO_base + (a)) +#define isa_writel(l,a) writel(l,__ISA_IO_base + (a)) +#define isa_memset_io(a,b,c) memset_io(__ISA_IO_base + (a),(b),(c)) +#define isa_memcpy_fromio(a,b,c) memcpy_fromio((a),__ISA_IO_base + (b),(c)) +#define isa_memcpy_toio(a,b,c) memcpy_toio(__ISA_IO_base + (a),(b),(c)) + + +/* + * Again, i386 does not require mem IO specific function. + */ + +#define eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),__io_virt(b),(c),(d)) +#define isa_eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),__io_virt(__ISA_IO_base + (b)),(c),(d)) + +static inline int check_signature(unsigned long io_addr, + const unsigned char *signature, int length) +{ + int retval = 0; + do { + if (readb(io_addr) != *signature) + goto out; + io_addr++; + signature++; + length--; + } while (length); + retval = 1; +out: + return retval; +} + +static inline int isa_check_signature(unsigned long io_addr, + const unsigned char *signature, int length) +{ + int retval = 0; + do { + if (isa_readb(io_addr) != *signature) + goto out; + io_addr++; + signature++; + length--; + } while (length); + retval = 1; +out: + return retval; +} + +/* Nothing to do */ + +#define dma_cache_inv(_start,_size) do { } while (0) +#define dma_cache_wback(_start,_size) do { } while (0) +#define dma_cache_wback_inv(_start,_size) do { } while (0) + +#define flush_write_buffers() + +#endif /* __KERNEL__ */ + +#endif diff -Nru a/include/asm-x86_64/io_apic.h b/include/asm-x86_64/io_apic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/io_apic.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,148 @@ +#ifndef __ASM_IO_APIC_H +#define __ASM_IO_APIC_H + +#include +#include +#include + +/* + * Intel IO-APIC support for SMP and UP systems. + * + * Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar + */ + +#ifdef CONFIG_X86_IO_APIC + +#define APIC_MISMATCH_DEBUG + +#define IO_APIC_BASE(idx) \ + ((volatile int *)__fix_to_virt(FIX_IO_APIC_BASE_0 + idx)) + +/* + * The structure of the IO-APIC: + */ +struct IO_APIC_reg_00 { + __u32 __reserved_2 : 24, + ID : 4, + __reserved_1 : 4; +} __attribute__ ((packed)); + +struct IO_APIC_reg_01 { + __u32 version : 8, + __reserved_2 : 7, + PRQ : 1, + entries : 8, + __reserved_1 : 8; +} __attribute__ ((packed)); + +struct IO_APIC_reg_02 { + __u32 __reserved_2 : 24, + arbitration : 4, + __reserved_1 : 4; +} __attribute__ ((packed)); + +/* + * # of IO-APICs and # of IRQ routing registers + */ +extern int nr_ioapics; +extern int nr_ioapic_registers[MAX_IO_APICS]; + +enum ioapic_irq_destination_types { + dest_Fixed = 0, + dest_LowestPrio = 1, + dest_SMI = 2, + dest__reserved_1 = 3, + dest_NMI = 4, + dest_INIT = 5, + dest__reserved_2 = 6, + dest_ExtINT = 7 +}; + +struct IO_APIC_route_entry { + __u32 vector : 8, + delivery_mode : 3, /* 000: FIXED + * 001: lowest prio + * 111: ExtINT + */ + dest_mode : 1, /* 0: physical, 1: logical */ + delivery_status : 1, + polarity : 1, + irr : 1, + trigger : 1, /* 0: edge, 1: level */ + mask : 1, /* 0: enabled, 1: disabled */ + __reserved_2 : 15; + + union { struct { __u32 + __reserved_1 : 24, + physical_dest : 4, + __reserved_2 : 4; + } physical; + + struct { __u32 + __reserved_1 : 24, + logical_dest : 8; + } logical; + } dest; + +} __attribute__ ((packed)); + +/* + * MP-BIOS irq configuration table structures: + */ + +/* I/O APIC entries */ +extern struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS]; + +/* # of MP IRQ source entries */ +extern int mp_irq_entries; + +/* MP IRQ source entries */ +extern struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; + +/* non-0 if default (table-less) MP configuration */ +extern int mpc_default_type; + +static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg) +{ + *IO_APIC_BASE(apic) = reg; + return *(IO_APIC_BASE(apic)+4); +} + +static inline void io_apic_write(unsigned int apic, unsigned int reg, unsigned int value) +{ + *IO_APIC_BASE(apic) = reg; + *(IO_APIC_BASE(apic)+4) = value; +} + +/* + * Re-write a value: to be used for read-modify-write + * cycles where the read already set up the index register. + */ +static inline void io_apic_modify(unsigned int apic, unsigned int value) +{ + *(IO_APIC_BASE(apic)+4) = value; +} + +/* + * Synchronize the IO-APIC and the CPU by doing + * a dummy read from the IO-APIC + */ +static inline void io_apic_sync(unsigned int apic) +{ + (void) *(IO_APIC_BASE(apic)+4); +} + +/* 1 if "noapic" boot option passed */ +extern int skip_ioapic_setup; + +/* + * If we use the IO-APIC for IRQ routing, disable automatic + * assignment of PCI IRQ's. + */ +#define io_apic_assign_pci_irqs (mp_irq_entries && !skip_ioapic_setup) + +#else /* !CONFIG_X86_IO_APIC */ +#define io_apic_assign_pci_irqs 0 +#endif + +#endif diff -Nru a/include/asm-x86_64/ioctl.h b/include/asm-x86_64/ioctl.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/ioctl.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,75 @@ +/* $Id: ioctl.h,v 1.2 2001/07/04 09:08:13 ak Exp $ + * + * linux/ioctl.h for Linux by H.H. Bergman. + */ + +#ifndef _ASMX8664_IOCTL_H +#define _ASMX8664_IOCTL_H + +/* ioctl command encoding: 32 bits total, command in lower 16 bits, + * size of the parameter structure in the lower 14 bits of the + * upper 16 bits. + * Encoding the size of the parameter structure in the ioctl request + * is useful for catching programs compiled with old versions + * and to avoid overwriting user space outside the user buffer area. + * The highest 2 bits are reserved for indicating the ``access mode''. + * NOTE: This limits the max parameter size to 16kB -1 ! + */ + +/* + * The following is for compatibility across the various Linux + * platforms. The i386 ioctl numbering scheme doesn't really enforce + * a type field. De facto, however, the top 8 bits of the lower 16 + * bits are indeed used as a type field, so we might just as well make + * this explicit here. Please be sure to use the decoding macros + * below from now on. + */ +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 +#define _IOC_SIZEBITS 14 +#define _IOC_DIRBITS 2 + +#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) +#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) +#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) +#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + +/* + * Direction bits. + */ +#define _IOC_NONE 0U +#define _IOC_WRITE 1U +#define _IOC_READ 2U + +#define _IOC(dir,type,nr,size) \ + (((dir) << _IOC_DIRSHIFT) | \ + ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | \ + ((size) << _IOC_SIZESHIFT)) + +/* used to create numbers */ +#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) +#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) +#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) +#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) + +/* used to decode ioctl numbers.. */ +#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) +#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) +#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) +#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) + +/* ...and for the drivers/sound files... */ + +#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) +#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) +#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) +#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) +#define IOCSIZE_SHIFT (_IOC_SIZESHIFT) + +#endif /* _ASMX8664_IOCTL_H */ diff -Nru a/include/asm-x86_64/ioctls.h b/include/asm-x86_64/ioctls.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/ioctls.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,82 @@ +#ifndef __ARCH_X8664_IOCTLS_H__ +#define __ARCH_X8664_IOCTLS_H__ + +#include + +/* 0x54 is just a magic number to make these relatively unique ('T') */ + +#define TCGETS 0x5401 +#define TCSETS 0x5402 +#define TCSETSW 0x5403 +#define TCSETSF 0x5404 +#define TCGETA 0x5405 +#define TCSETA 0x5406 +#define TCSETAW 0x5407 +#define TCSETAF 0x5408 +#define TCSBRK 0x5409 +#define TCXONC 0x540A +#define TCFLSH 0x540B +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D +#define TIOCSCTTY 0x540E +#define TIOCGPGRP 0x540F +#define TIOCSPGRP 0x5410 +#define TIOCOUTQ 0x5411 +#define TIOCSTI 0x5412 +#define TIOCGWINSZ 0x5413 +#define TIOCSWINSZ 0x5414 +#define TIOCMGET 0x5415 +#define TIOCMBIS 0x5416 +#define TIOCMBIC 0x5417 +#define TIOCMSET 0x5418 +#define TIOCGSOFTCAR 0x5419 +#define TIOCSSOFTCAR 0x541A +#define FIONREAD 0x541B +#define TIOCINQ FIONREAD +#define TIOCLINUX 0x541C +#define TIOCCONS 0x541D +#define TIOCGSERIAL 0x541E +#define TIOCSSERIAL 0x541F +#define TIOCPKT 0x5420 +#define FIONBIO 0x5421 +#define TIOCNOTTY 0x5422 +#define TIOCSETD 0x5423 +#define TIOCGETD 0x5424 +#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TIOCSBRK 0x5427 /* BSD compatibility */ +#define TIOCCBRK 0x5428 /* BSD compatibility */ +#define TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ +#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ + +#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ +#define FIOCLEX 0x5451 +#define FIOASYNC 0x5452 +#define TIOCSERCONFIG 0x5453 +#define TIOCSERGWILD 0x5454 +#define TIOCSERSWILD 0x5455 +#define TIOCGLCKTRMIOS 0x5456 +#define TIOCSLCKTRMIOS 0x5457 +#define TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TIOCSERGETLSR 0x5459 /* Get line status register */ +#define TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ +#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ +#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ + +/* Used for packet mode */ +#define TIOCPKT_DATA 0 +#define TIOCPKT_FLUSHREAD 1 +#define TIOCPKT_FLUSHWRITE 2 +#define TIOCPKT_STOP 4 +#define TIOCPKT_START 8 +#define TIOCPKT_NOSTOP 16 +#define TIOCPKT_DOSTOP 32 + +#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ + +#endif diff -Nru a/include/asm-x86_64/ipc.h b/include/asm-x86_64/ipc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/ipc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,32 @@ +#ifndef __i386_IPC_H__ +#define __i386_IPC_H__ + +/* + * These are used to wrap system calls on x86. + * + * See arch/i386/kernel/sys_i386.c for ugly details.. + */ + +struct ipc_kludge { + struct msgbuf *msgp; + long msgtyp; +}; + +#define SEMOP 1 +#define SEMGET 2 +#define SEMCTL 3 +#define MSGSND 11 +#define MSGRCV 12 +#define MSGGET 13 +#define MSGCTL 14 +#define SHMAT 21 +#define SHMDT 22 +#define SHMGET 23 +#define SHMCTL 24 + +/* Used by the DIPC package, try and avoid reusing it */ +#define DIPC 25 + +#define IPCCALL(version,op) ((version)<<16 | (op)) + +#endif diff -Nru a/include/asm-x86_64/ipcbuf.h b/include/asm-x86_64/ipcbuf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/ipcbuf.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,29 @@ +#ifndef __i386_IPCBUF_H__ +#define __i386_IPCBUF_H__ + +/* + * The ipc64_perm structure for i386 architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 32-bit mode_t and seq + * - 2 miscellaneous 32-bit values + */ + +struct ipc64_perm +{ + __kernel_key_t key; + __kernel_uid32_t uid; + __kernel_gid32_t gid; + __kernel_uid32_t cuid; + __kernel_gid32_t cgid; + __kernel_mode_t mode; + unsigned short __pad1; + unsigned short seq; + unsigned short __pad2; + unsigned long __unused1; + unsigned long __unused2; +}; + +#endif /* __i386_IPCBUF_H__ */ diff -Nru a/include/asm-x86_64/irq.h b/include/asm-x86_64/irq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/irq.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,35 @@ +#ifndef _ASM_IRQ_H +#define _ASM_IRQ_H + +/* + * linux/include/asm/irq.h + * + * (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar + * + * IRQ/IPI changes taken from work by Thomas Radke + * + */ + +#define TIMER_IRQ 0 + +/* + * 16 8259A IRQ's, 208 potential APIC interrupt sources. + * Right now the APIC is mostly only used for SMP. + * 256 vectors is an architectural limit. (we can have + * more than 256 devices theoretically, but they will + * have to use shared interrupts) + * Since vectors 0x00-0x1f are used/reserved for the CPU, + * the usable vector space is 0x20-0xff (224 vectors) + */ +#define NR_IRQS 224 + +static __inline__ int irq_cannonicalize(int irq) +{ + return ((irq == 2) ? 9 : irq); +} + +extern void disable_irq(unsigned int); +extern void disable_irq_nosync(unsigned int); +extern void enable_irq(unsigned int); + +#endif /* _ASM_IRQ_H */ diff -Nru a/include/asm-x86_64/kdebug.h b/include/asm-x86_64/kdebug.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/kdebug.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,23 @@ +#ifndef _X86_64_KDEBUG_H +#define _X86_64_KDEBUG_H 1 + +#include + +struct pt_regs; + +struct die_args { + struct pt_regs *regs; + const char *str; + long err; +}; + +extern struct notifier_block *die_chain; + +enum { + DIE_DIE = 1, + DIE_INT3, + DIE_DEBUG, + DIE_PANIC, +}; + +#endif diff -Nru a/include/asm-x86_64/keyboard.h b/include/asm-x86_64/keyboard.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/keyboard.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,71 @@ +/* + * linux/include/asm-x8664/keyboard.h + * + * Created 3 Nov 1996 by Geert Uytterhoeven + */ + +/* + * This file contains the x8664 architecture specific keyboard definitions + */ + +#ifndef _X8664_KEYBOARD_H +#define _X8664_KEYBOARD_H + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include + +#define KEYBOARD_IRQ 1 +#define DISABLE_KBD_DURING_INTERRUPTS 0 + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern int pckbd_pm_resume(struct pm_dev *dev, pm_request_t rqst, void *data); +extern unsigned char pckbd_sysrq_xlate[128]; + +#define kbd_setkeycode pckbd_setkeycode +#define kbd_getkeycode pckbd_getkeycode +#define kbd_translate pckbd_translate +#define kbd_unexpected_up pckbd_unexpected_up +#define kbd_leds pckbd_leds +#define kbd_init_hw pckbd_init_hw +#define kbd_sysrq_xlate pckbd_sysrq_xlate + +#define SYSRQ_KEY 0x54 + +/* resource allocation */ +#define kbd_request_region() +#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \ + "keyboard", NULL) + +/* How to access the keyboard macros on this platform. */ +#define kbd_read_input() inb(KBD_DATA_REG) +#define kbd_read_status() inb(KBD_STATUS_REG) +#define kbd_write_output(val) outb(val, KBD_DATA_REG) +#define kbd_write_command(val) outb(val, KBD_CNTL_REG) + +/* Some stoneage hardware needs delays after some operations. */ +#define kbd_pause() do { } while(0) + +/* + * Machine specific bits for the PS/2 driver + */ + +#define AUX_IRQ 12 + +#define aux_request_irq(hand, dev_id) \ + request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id) + +#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id) + +#endif /* __KERNEL__ */ +#endif /* _X8664_KEYBOARD_H */ diff -Nru a/include/asm-x86_64/kmap_types.h b/include/asm-x86_64/kmap_types.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/kmap_types.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,14 @@ +#ifndef _ASM_KMAP_TYPES_H +#define _ASM_KMAP_TYPES_H + +enum km_type { + KM_BOUNCE_READ, + KM_SKB_DATA, + KM_SKB_DATA_SOFTIRQ, + KM_USER0, + KM_USER1, + KM_BIO_IRQ, + KM_TYPE_NR +}; + +#endif diff -Nru a/include/asm-x86_64/ldt.h b/include/asm-x86_64/ldt.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/ldt.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,37 @@ +/* + * ldt.h + * + * Definitions of structures used with the modify_ldt system call. + */ +#ifndef _LINUX_LDT_H +#define _LINUX_LDT_H + +/* Is this to allow userland manipulate LDTs? It looks so. We should + consider disallowing LDT manipulations altogether: in long mode + there's no possibility of v86 mode, so something will have to + break, anyway. --pavel */ + +/* Maximum number of LDT entries supported. */ +#define LDT_ENTRIES 8192 +/* The size of each LDT entry. */ +#define LDT_ENTRY_SIZE 8 + +#ifndef __ASSEMBLY__ +struct modify_ldt_ldt_s { + unsigned int entry_number; + unsigned long base_addr; + unsigned int limit; + unsigned int seg_32bit:1; + unsigned int contents:2; + unsigned int read_exec_only:1; + unsigned int limit_in_pages:1; + unsigned int seg_not_present:1; + unsigned int useable:1; +}; + +#define MODIFY_LDT_CONTENTS_DATA 0 +#define MODIFY_LDT_CONTENTS_STACK 1 +#define MODIFY_LDT_CONTENTS_CODE 2 + +#endif /* !__ASSEMBLY__ */ +#endif diff -Nru a/include/asm-x86_64/linux_logo.h b/include/asm-x86_64/linux_logo.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/linux_logo.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,29 @@ +/* $Id: linux_logo.h,v 1.4 2001/07/05 23:44:45 ak Exp $ + * include/asm-x86_64/linux_logo.h: This is a linux logo + * to be displayed on boot. + * + * Copyright (C) 1996 Larry Ewing (lewing@isc.tamu.edu) + * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * You can put anything here, but: + * LINUX_LOGO_COLORS has to be less than 224 + * image size has to be 80x80 + * values have to start from 0x20 + * (i.e. RGB(linux_logo_red[0], + * linux_logo_green[0], + * linux_logo_blue[0]) is color 0x20) + * BW image has to be 80x80 as well, with MS bit + * on the left + * Serial_console ascii image can be any size, + * but should contain %s to display the version + */ + +#include +#include + +/* We should create logo of penguin with a big hammer (-: --pavel */ + +#define linux_logo_banner "Linux/x86-64 version " UTS_RELEASE + +#include + diff -Nru a/include/asm-x86_64/locks.h b/include/asm-x86_64/locks.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/locks.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,135 @@ +/* + * SMP locks primitives for building ix86 locks + * (not yet used). + * + * Alan Cox, alan@redhat.com, 1995 + */ + +/* + * This would be much easier but far less clear and easy + * to borrow for other processors if it was just assembler. + */ + +extern __inline__ void prim_spin_lock(struct spinlock *sp) +{ + int processor=smp_processor_id(); + + /* + * Grab the lock bit + */ + + while(lock_set_bit(0,&sp->lock)) + { + /* + * Failed, but that's cos we own it! + */ + + if(sp->cpu==processor) + { + sp->users++; + return 0; + } + /* + * Spin in the cache S state if possible + */ + while(sp->lock) + { + /* + * Wait for any invalidates to go off + */ + + if(smp_invalidate_needed&(1<spins++; + } + /* + * Someone wrote the line, we go 'I' and get + * the cache entry. Now try to regrab + */ + } + sp->users++;sp->cpu=processor; + return 1; +} + +/* + * Release a spin lock + */ + +extern __inline__ int prim_spin_unlock(struct spinlock *sp) +{ + /* This is safe. The decrement is still guarded by the lock. A multilock would + not be safe this way */ + if(!--sp->users) + { + sp->cpu= NO_PROC_ID;lock_clear_bit(0,&sp->lock); + return 1; + } + return 0; +} + + +/* + * Non blocking lock grab + */ + +extern __inline__ int prim_spin_lock_nb(struct spinlock *sp) +{ + if(lock_set_bit(0,&sp->lock)) + return 0; /* Locked already */ + sp->users++; + return 1; /* We got the lock */ +} + + +/* + * These wrap the locking primitives up for usage + */ + +extern __inline__ void spinlock(struct spinlock *sp) +{ + if(sp->prioritylock_order) + panic("lock order violation: %s (%d)\n", sp->name, current->lock_order); + if(prim_spin_lock(sp)) + { + /* + * We got a new lock. Update the priority chain + */ + sp->oldpri=current->lock_order; + current->lock_order=sp->priority; + } +} + +extern __inline__ void spinunlock(struct spinlock *sp) +{ + int pri; + if(current->lock_order!=sp->priority) + panic("lock release order violation %s (%d)\n", sp->name, current->lock_order); + pri=sp->oldpri; + if(prim_spin_unlock(sp)) + { + /* + * Update the debugging lock priority chain. We dumped + * our last right to the lock. + */ + current->lock_order=sp->pri; + } +} + +extern __inline__ void spintestlock(struct spinlock *sp) +{ + /* + * We do no sanity checks, it's legal to optimistically + * get a lower lock. + */ + prim_spin_lock_nb(sp); +} + +extern __inline__ void spintestunlock(struct spinlock *sp) +{ + /* + * A testlock doesn't update the lock chain so we + * must not update it on free + */ + prim_spin_unlock(sp); +} diff -Nru a/include/asm-x86_64/mc146818rtc.h b/include/asm-x86_64/mc146818rtc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/mc146818rtc.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,29 @@ +/* + * Machine dependent access functions for RTC registers. + */ +#ifndef _ASM_MC146818RTC_H +#define _ASM_MC146818RTC_H + +#include + +#ifndef RTC_PORT +#define RTC_PORT(x) (0x70 + (x)) +#define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */ +#endif + +/* + * The yet supported machines all access the RTC index register via + * an ISA port access but the way to access the date register differs ... + */ +#define CMOS_READ(addr) ({ \ +outb_p((addr),RTC_PORT(0)); \ +inb_p(RTC_PORT(1)); \ +}) +#define CMOS_WRITE(val, addr) ({ \ +outb_p((addr),RTC_PORT(0)); \ +outb_p((val),RTC_PORT(1)); \ +}) + +#define RTC_IRQ 8 + +#endif /* _ASM_MC146818RTC_H */ diff -Nru a/include/asm-x86_64/mman.h b/include/asm-x86_64/mman.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/mman.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,39 @@ +#ifndef __X8664_MMAN_H__ +#define __X8664_MMAN_H__ + +#define PROT_READ 0x1 /* page can be read */ +#define PROT_WRITE 0x2 /* page can be written */ +#define PROT_EXEC 0x4 /* page can be executed */ +#define PROT_NONE 0x0 /* page can not be accessed */ + +#define MAP_SHARED 0x01 /* Share changes */ +#define MAP_PRIVATE 0x02 /* Changes are private */ +#define MAP_TYPE 0x0f /* Mask for type of mapping */ +#define MAP_FIXED 0x10 /* Interpret addr exactly */ +#define MAP_ANONYMOUS 0x20 /* don't use a file */ +#define MAP_32BIT 0x40 /* only give out 32bit addresses */ + +#define MAP_GROWSDOWN 0x0100 /* stack-like segment */ +#define MAP_DENYWRITE 0x0800 /* ETXTBSY */ +#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ +#define MAP_LOCKED 0x2000 /* pages are locked */ +#define MAP_NORESERVE 0x4000 /* don't check for reservations */ + +#define MS_ASYNC 1 /* sync memory asynchronously */ +#define MS_INVALIDATE 2 /* invalidate the caches */ +#define MS_SYNC 4 /* synchronous memory sync */ + +#define MCL_CURRENT 1 /* lock all current mappings */ +#define MCL_FUTURE 2 /* lock all future mappings */ + +#define MADV_NORMAL 0x0 /* default page-in behavior */ +#define MADV_RANDOM 0x1 /* page-in minimum required */ +#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */ +#define MADV_WILLNEED 0x3 /* pre-fault pages */ +#define MADV_DONTNEED 0x4 /* discard these pages */ + +/* compatibility flags */ +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FILE 0 + +#endif diff -Nru a/include/asm-x86_64/mmu.h b/include/asm-x86_64/mmu.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/mmu.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,13 @@ +#ifndef __x86_64_MMU_H +#define __x86_64_MMU_H + +/* + * The x86_64 doesn't have a mmu context, but + * we put the segment information here. + */ +typedef struct { + void *segments; + unsigned long cpuvalid; +} mm_context_t; + +#endif diff -Nru a/include/asm-x86_64/mmu_context.h b/include/asm-x86_64/mmu_context.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/mmu_context.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,95 @@ +#ifndef __X86_64_MMU_CONTEXT_H +#define __X86_64_MMU_CONTEXT_H + +#include +#include +#include +#include + +/* + * Every architecture must define this function. It's the fastest + * way of searching a 168-bit bitmap where the first 128 bits are + * unlikely to be set. It's guaranteed that at least one of the 168 + * bits is cleared. + */ +#if MAX_RT_PRIO != 128 || MAX_PRIO != 168 +# error update this function. +#endif + +static inline int __sched_find_first_bit(unsigned long *b) +{ + if (b[0]) + return __ffs(b[0]); + if (b[1]) + return __ffs(b[1]) + 64; + if (b[2]) + return __ffs(b[2]) + 128; +} + +static inline int sched_find_first_bit(unsigned long *b) +{ + int n = __sched_find_first_bit(b); + BUG_ON((unsigned)n > 167); + return n; +} + +/* + * possibly do the LDT unload here? + */ +#define destroy_context(mm) do { } while(0) +#define init_new_context(tsk,mm) 0 + +#ifdef CONFIG_SMP + +static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk, unsigned cpu) +{ + if(cpu_tlbstate[cpu].state == TLBSTATE_OK) + cpu_tlbstate[cpu].state = TLBSTATE_LAZY; +} +#else +static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk, unsigned cpu) +{ +} +#endif + +static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk, unsigned cpu) +{ + if (likely(prev != next)) { + /* stop flush ipis for the previous mm */ + clear_bit(cpu, &prev->cpu_vm_mask); + /* + * Re-load LDT if necessary + */ + if (unlikely(prev->context.segments != next->context.segments)) + load_LDT(next); +#ifdef CONFIG_SMP + cpu_tlbstate[cpu].state = TLBSTATE_OK; + cpu_tlbstate[cpu].active_mm = next; +#endif + set_bit(cpu, &next->cpu_vm_mask); + set_bit(cpu, &next->context.cpuvalid); + /* Re-load page tables */ + asm volatile("movq %0,level4_pgt": :"r" (__pa(next->pgd) | 7)); + __flush_tlb(); + } +#ifdef CONFIG_SMP + else { + cpu_tlbstate[cpu].state = TLBSTATE_OK; + if(cpu_tlbstate[cpu].active_mm != next) + BUG(); + if(!test_and_set_bit(cpu, &next->cpu_vm_mask)) { + /* We were in lazy tlb mode and leave_mm disabled + * tlb flush IPI delivery. We must flush our tlb. + */ + local_flush_tlb(); + } + if (!test_and_set_bit(cpu, &next->context.cpuvalid)) + load_LDT(next); + } +#endif +} + +#define activate_mm(prev, next) \ + switch_mm((prev),(next),NULL,smp_processor_id()) + +#endif diff -Nru a/include/asm-x86_64/mmx.h b/include/asm-x86_64/mmx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/mmx.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,14 @@ +#ifndef _ASM_MMX_H +#define _ASM_MMX_H + +/* + * MMX 3Dnow! helper operations + */ + +#include + +extern void *_mmx_memcpy(void *to, const void *from, size_t size); +extern void mmx_clear_page(void *page); +extern void mmx_copy_page(void *to, void *from); + +#endif diff -Nru a/include/asm-x86_64/module.h b/include/asm-x86_64/module.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/module.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,12 @@ +#ifndef _ASM_X8664_MODULE_H +#define _ASM_X8664_MODULE_H +/* + * This file contains the x8664 architecture specific module code. + */ + +#define module_map(x) vmalloc(x) +#define module_unmap(x) vfree(x) +#define module_arch_init(x) (0) +#define arch_init_modules(x) do { } while (0) + +#endif diff -Nru a/include/asm-x86_64/mpspec.h b/include/asm-x86_64/mpspec.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/mpspec.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,188 @@ +#ifndef __ASM_MPSPEC_H +#define __ASM_MPSPEC_H + +/* + * Structure definitions for SMP machines following the + * Intel Multiprocessing Specification 1.1 and 1.4. + */ + +/* + * This tag identifies where the SMP configuration + * information is. + */ + +#define SMP_MAGIC_IDENT (('_'<<24)|('P'<<16)|('M'<<8)|'_') + +/* + * a maximum of 16 APICs with the current APIC ID architecture. + */ +#define MAX_APICS 16 + +struct intel_mp_floating +{ + char mpf_signature[4]; /* "_MP_" */ + unsigned int mpf_physptr; /* Configuration table address */ + unsigned char mpf_length; /* Our length (paragraphs) */ + unsigned char mpf_specification;/* Specification version */ + unsigned char mpf_checksum; /* Checksum (makes sum 0) */ + unsigned char mpf_feature1; /* Standard or configuration ? */ + unsigned char mpf_feature2; /* Bit7 set for IMCR|PIC */ + unsigned char mpf_feature3; /* Unused (0) */ + unsigned char mpf_feature4; /* Unused (0) */ + unsigned char mpf_feature5; /* Unused (0) */ +}; + +struct mp_config_table +{ + char mpc_signature[4]; +#define MPC_SIGNATURE "PCMP" + unsigned short mpc_length; /* Size of table */ + char mpc_spec; /* 0x01 */ + char mpc_checksum; + char mpc_oem[8]; + char mpc_productid[12]; + unsigned int mpc_oemptr; /* 0 if not present */ + unsigned short mpc_oemsize; /* 0 if not present */ + unsigned short mpc_oemcount; + unsigned int mpc_lapic; /* APIC address */ + unsigned int reserved; +}; + +/* Followed by entries */ + +#define MP_PROCESSOR 0 +#define MP_BUS 1 +#define MP_IOAPIC 2 +#define MP_INTSRC 3 +#define MP_LINTSRC 4 + +struct mpc_config_processor +{ + unsigned char mpc_type; + unsigned char mpc_apicid; /* Local APIC number */ + unsigned char mpc_apicver; /* Its versions */ + unsigned char mpc_cpuflag; +#define CPU_ENABLED 1 /* Processor is available */ +#define CPU_BOOTPROCESSOR 2 /* Processor is the BP */ + unsigned int mpc_cpufeature; +#define CPU_STEPPING_MASK 0x0F +#define CPU_MODEL_MASK 0xF0 +#define CPU_FAMILY_MASK 0xF00 + unsigned int mpc_featureflag; /* CPUID feature value */ + unsigned int mpc_reserved[2]; +}; + +struct mpc_config_bus +{ + unsigned char mpc_type; + unsigned char mpc_busid; + unsigned char mpc_bustype[6] __attribute((packed)); +}; + +/* List of Bus Type string values, Intel MP Spec. */ +#define BUSTYPE_EISA "EISA" +#define BUSTYPE_ISA "ISA" +#define BUSTYPE_INTERN "INTERN" /* Internal BUS */ +#define BUSTYPE_MCA "MCA" +#define BUSTYPE_VL "VL" /* Local bus */ +#define BUSTYPE_PCI "PCI" +#define BUSTYPE_PCMCIA "PCMCIA" +#define BUSTYPE_CBUS "CBUS" +#define BUSTYPE_CBUSII "CBUSII" +#define BUSTYPE_FUTURE "FUTURE" +#define BUSTYPE_MBI "MBI" +#define BUSTYPE_MBII "MBII" +#define BUSTYPE_MPI "MPI" +#define BUSTYPE_MPSA "MPSA" +#define BUSTYPE_NUBUS "NUBUS" +#define BUSTYPE_TC "TC" +#define BUSTYPE_VME "VME" +#define BUSTYPE_XPRESS "XPRESS" + +struct mpc_config_ioapic +{ + unsigned char mpc_type; + unsigned char mpc_apicid; + unsigned char mpc_apicver; + unsigned char mpc_flags; +#define MPC_APIC_USABLE 0x01 + unsigned int mpc_apicaddr; +}; + +struct mpc_config_intsrc +{ + unsigned char mpc_type; + unsigned char mpc_irqtype; + unsigned short mpc_irqflag; + unsigned char mpc_srcbus; + unsigned char mpc_srcbusirq; + unsigned char mpc_dstapic; + unsigned char mpc_dstirq; +}; + +enum mp_irq_source_types { + mp_INT = 0, + mp_NMI = 1, + mp_SMI = 2, + mp_ExtINT = 3 +}; + +#define MP_IRQDIR_DEFAULT 0 +#define MP_IRQDIR_HIGH 1 +#define MP_IRQDIR_LOW 3 + + +struct mpc_config_lintsrc +{ + unsigned char mpc_type; + unsigned char mpc_irqtype; + unsigned short mpc_irqflag; + unsigned char mpc_srcbusid; + unsigned char mpc_srcbusirq; + unsigned char mpc_destapic; +#define MP_APIC_ALL 0xFF + unsigned char mpc_destapiclint; +}; + +/* + * Default configurations + * + * 1 2 CPU ISA 82489DX + * 2 2 CPU EISA 82489DX neither IRQ 0 timer nor IRQ 13 DMA chaining + * 3 2 CPU EISA 82489DX + * 4 2 CPU MCA 82489DX + * 5 2 CPU ISA+PCI + * 6 2 CPU EISA+PCI + * 7 2 CPU MCA+PCI + */ + +#define MAX_IRQ_SOURCES 256 +#define MAX_MP_BUSSES 32 +enum mp_bustype { + MP_BUS_ISA = 1, + MP_BUS_EISA, + MP_BUS_PCI, + MP_BUS_MCA +}; +extern int mp_bus_id_to_type [MAX_MP_BUSSES]; +extern int mp_bus_id_to_pci_bus [MAX_MP_BUSSES]; + +extern unsigned int boot_cpu_physical_apicid; +extern unsigned long phys_cpu_present_map; +extern int smp_found_config; +extern void find_smp_config (void); +extern void get_smp_config (void); +extern int nr_ioapics; +extern int apic_version [MAX_APICS]; +extern int mp_bus_id_to_type [MAX_MP_BUSSES]; +extern int mp_irq_entries; +extern struct mpc_config_intsrc mp_irqs [MAX_IRQ_SOURCES]; +extern int mpc_default_type; +extern int mp_bus_id_to_pci_bus [MAX_MP_BUSSES]; +extern int mp_current_pci_id; +extern unsigned long mp_lapic_addr; +extern int pic_mode; +extern int using_apic_timer; + +#endif + diff -Nru a/include/asm-x86_64/msgbuf.h b/include/asm-x86_64/msgbuf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/msgbuf.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,27 @@ +#ifndef _X8664_MSGBUF_H +#define _X8664_MSGBUF_H + +/* + * The msqid64_ds structure for x86-64 architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 2 miscellaneous 64-bit values + */ + +struct msqid64_ds { + struct ipc64_perm msg_perm; + __kernel_time_t msg_stime; /* last msgsnd time */ + __kernel_time_t msg_rtime; /* last msgrcv time */ + __kernel_time_t msg_ctime; /* last change time */ + unsigned long msg_cbytes; /* current number of bytes on queue */ + unsigned long msg_qnum; /* number of messages in queue */ + unsigned long msg_qbytes; /* max number of bytes on queue */ + __kernel_pid_t msg_lspid; /* pid of last msgsnd */ + __kernel_pid_t msg_lrpid; /* last receive pid */ + unsigned long __unused4; + unsigned long __unused5; +}; + +#endif diff -Nru a/include/asm-x86_64/msr.h b/include/asm-x86_64/msr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/msr.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,140 @@ +#ifndef X86_64_MSR_H +#define X86_64_MSR_H 1 +/* + * Access to machine-specific registers (available on 586 and better only) + * Note: the rd* operations modify the parameters directly (without using + * pointer indirection), this allows gcc to optimize better + */ + +#define rdmsr(msr,val1,val2) \ + __asm__ __volatile__("rdmsr" \ + : "=a" (val1), "=d" (val2) \ + : "c" (msr)) + + +#define rdmsrl(msr,val) do { unsigned long a__,b__; \ + __asm__ __volatile__("rdmsr" \ + : "=a" (a__), "=d" (b__) \ + : "c" (msr)); \ + val = a__ | (b__<<32); \ +} while(0); + +#define wrmsr(msr,val1,val2) \ + __asm__ __volatile__("wrmsr" \ + : /* no outputs */ \ + : "c" (msr), "a" (val1), "d" (val2)) + +#define wrmsrl(msr,val) wrmsr(msr,(__u32)((__u64)(val)),((__u64)(val))>>32) + +/* wrmsrl with exception handling */ +#define checking_wrmsrl(msr,val) ({ int ret__; \ + asm volatile("2: wrmsr ; xorl %0,%0\n" \ + "1:\n\t" \ + ".section .fixup,\"ax\"\n\t" \ + "3: movl %4,%0 ; jmp 1b\n\t" \ + ".previous\n\t" \ + ".section __ex_table,\"a\"\n" \ + " .align 8\n\t" \ + " .quad 2b,3b\n\t" \ + ".previous" \ + : "=a" (ret__) \ + : "c" (msr), "0" ((__u32)val), "d" ((val)>>32), "i" (-EFAULT)); \ + ret__; }) + +#define rdtsc(low,high) \ + __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) + +#define rdtscl(low) \ + __asm__ __volatile__ ("rdtsc" : "=a" (low) : : "edx") + +#define rdtscll(val) \ + __asm__ __volatile__ ("rdtsc" : "=A" (val)) + +#define write_tsc(val1,val2) wrmsr(0x10, val1, val2) + +#define rdpmc(counter,low,high) \ + __asm__ __volatile__("rdpmc" \ + : "=a" (low), "=d" (high) \ + : "c" (counter)) + + +/* AMD/K8 specific MSRs */ +#define MSR_EFER 0xc0000080 /* extended feature register */ +#define MSR_STAR 0xc0000081 /* legacy mode SYSCALL target */ +#define MSR_LSTAR 0xc0000082 /* long mode SYSCALL target */ +#define MSR_CSTAR 0xc0000083 /* compatibility mode SYSCALL target */ +#define MSR_SYSCALL_MASK 0xc0000084 /* EFLAGS mask for syscall */ +#define MSR_FS_BASE 0xc0000100 /* 64bit GS base */ +#define MSR_GS_BASE 0xc0000101 /* 64bit FS base */ +#define MSR_KERNEL_GS_BASE 0xc0000102 /* SwapGS GS shadow (or USER_GS from kernel view) */ + + +/* Intel MSRs. Some also available on other CPUs */ +#define MSR_IA32_PLATFORM_ID 0x17 + +#define MSR_IA32_PERFCTR0 0xc1 +#define MSR_IA32_PERFCTR1 0xc2 + +#define MSR_IA32_BBL_CR_CTL 0x119 + +#define MSR_IA32_MCG_CAP 0x179 +#define MSR_IA32_MCG_STATUS 0x17a +#define MSR_IA32_MCG_CTL 0x17b + +#define MSR_IA32_EVNTSEL0 0x186 +#define MSR_IA32_EVNTSEL1 0x187 + +#define MSR_IA32_DEBUGCTLMSR 0x1d9 +#define MSR_IA32_LASTBRANCHFROMIP 0x1db +#define MSR_IA32_LASTBRANCHTOIP 0x1dc +#define MSR_IA32_LASTINTFROMIP 0x1dd +#define MSR_IA32_LASTINTTOIP 0x1de + +#define MSR_IA32_MC0_CTL 0x400 +#define MSR_IA32_MC0_STATUS 0x401 +#define MSR_IA32_MC0_ADDR 0x402 +#define MSR_IA32_MC0_MISC 0x403 + +/* K7 MSRs */ +#define MSR_K7_EVNTSEL0 0xC0010000 +#define MSR_K7_PERFCTR0 0xC0010004 + +/* K6 MSRs */ +#define MSR_K6_EFER 0xC0000080 +#define MSR_K6_STAR 0xC0000081 +#define MSR_K6_WHCR 0xC0000082 +#define MSR_K6_UWCCR 0xC0000085 +#define MSR_K6_PSOR 0xC0000087 +#define MSR_K6_PFIR 0xC0000088 + +/* Centaur-Hauls/IDT defined MSRs. */ +#define MSR_IDT_FCR1 0x107 +#define MSR_IDT_FCR2 0x108 +#define MSR_IDT_FCR3 0x109 +#define MSR_IDT_FCR4 0x10a + +#define MSR_IDT_MCR0 0x110 +#define MSR_IDT_MCR1 0x111 +#define MSR_IDT_MCR2 0x112 +#define MSR_IDT_MCR3 0x113 +#define MSR_IDT_MCR4 0x114 +#define MSR_IDT_MCR5 0x115 +#define MSR_IDT_MCR6 0x116 +#define MSR_IDT_MCR7 0x117 +#define MSR_IDT_MCR_CTRL 0x120 + +/* VIA Cyrix defined MSRs*/ +#define MSR_VIA_FCR 0x1107 + +/* Intel defined MSRs. */ +#define MSR_IA32_P5_MC_ADDR 0 +#define MSR_IA32_P5_MC_TYPE 1 +#define MSR_IA32_PLATFORM_ID 0x17 +#define MSR_IA32_EBL_CR_POWERON 0x2a + +#define MSR_IA32_APICBASE 0x1b +#define MSR_IA32_APICBASE_BSP (1<<8) +#define MSR_IA32_APICBASE_ENABLE (1<<11) +#define MSR_IA32_APICBASE_BASE (0xfffff<<12) + +#endif diff -Nru a/include/asm-x86_64/mtrr.h b/include/asm-x86_64/mtrr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/mtrr.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,127 @@ +/* Generic MTRR (Memory Type Range Register) ioctls. + + Copyright (C) 1997-1999 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. +*/ +#ifndef _LINUX_MTRR_H +#define _LINUX_MTRR_H + +#include +#include + +#define MTRR_IOCTL_BASE 'M' + +struct mtrr_sentry +{ + unsigned long base; /* Base address */ + unsigned long size; /* Size of region */ + unsigned int type; /* Type of region */ +}; + +struct mtrr_gentry +{ + unsigned int regnum; /* Register number */ + unsigned long base; /* Base address */ + unsigned long size; /* Size of region */ + unsigned int type; /* Type of region */ +}; + +/* These are the various ioctls */ +#define MTRRIOC_ADD_ENTRY _IOW(MTRR_IOCTL_BASE, 0, struct mtrr_sentry) +#define MTRRIOC_SET_ENTRY _IOW(MTRR_IOCTL_BASE, 1, struct mtrr_sentry) +#define MTRRIOC_DEL_ENTRY _IOW(MTRR_IOCTL_BASE, 2, struct mtrr_sentry) +#define MTRRIOC_GET_ENTRY _IOWR(MTRR_IOCTL_BASE, 3, struct mtrr_gentry) +#define MTRRIOC_KILL_ENTRY _IOW(MTRR_IOCTL_BASE, 4, struct mtrr_sentry) +#define MTRRIOC_ADD_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 5, struct mtrr_sentry) +#define MTRRIOC_SET_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 6, struct mtrr_sentry) +#define MTRRIOC_DEL_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 7, struct mtrr_sentry) +#define MTRRIOC_GET_PAGE_ENTRY _IOWR(MTRR_IOCTL_BASE, 8, struct mtrr_gentry) +#define MTRRIOC_KILL_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 9, struct mtrr_sentry) + +/* These are the region types */ +#define MTRR_TYPE_UNCACHABLE 0 +#define MTRR_TYPE_WRCOMB 1 +/*#define MTRR_TYPE_ 2*/ +/*#define MTRR_TYPE_ 3*/ +#define MTRR_TYPE_WRTHROUGH 4 +#define MTRR_TYPE_WRPROT 5 +#define MTRR_TYPE_WRBACK 6 +#define MTRR_NUM_TYPES 7 + +#ifdef MTRR_NEED_STRINGS +static char *mtrr_strings[MTRR_NUM_TYPES] = +{ + "uncachable", /* 0 */ + "write-combining", /* 1 */ + "?", /* 2 */ + "?", /* 3 */ + "write-through", /* 4 */ + "write-protect", /* 5 */ + "write-back", /* 6 */ +}; +#endif + +#ifdef __KERNEL__ + +/* The following functions are for use by other drivers */ +# ifdef CONFIG_MTRR +extern int mtrr_add (unsigned long base, unsigned long size, + unsigned int type, char increment); +extern int mtrr_add_page (unsigned long base, unsigned long size, + unsigned int type, char increment); +extern int mtrr_del (int reg, unsigned long base, unsigned long size); +extern int mtrr_del_page (int reg, unsigned long base, unsigned long size); +extern void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi); +# else +static __inline__ int mtrr_add (unsigned long base, unsigned long size, + unsigned int type, char increment) +{ + return -ENODEV; +} +static __inline__ int mtrr_add_page (unsigned long base, unsigned long size, + unsigned int type, char increment) +{ + return -ENODEV; +} +static __inline__ int mtrr_del (int reg, unsigned long base, + unsigned long size) +{ + return -ENODEV; +} +static __inline__ int mtrr_del_page (int reg, unsigned long base, + unsigned long size) +{ + return -ENODEV; +} + +static __inline__ void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) {;} + +# endif + +/* The following functions are for initialisation: don't use them! */ +extern int mtrr_init (void); +# if defined(CONFIG_SMP) && defined(CONFIG_MTRR) +extern void mtrr_init_boot_cpu (void); +extern void mtrr_init_secondary_cpu (void); +# endif + +#endif + +#endif /* _LINUX_MTRR_H */ diff -Nru a/include/asm-x86_64/namei.h b/include/asm-x86_64/namei.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/namei.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,17 @@ +/* $Id: namei.h,v 1.2 2001/07/04 09:08:13 ak Exp $ + * linux/include/asm-i386/namei.h + * + * Included from linux/fs/namei.c + */ + +#ifndef __X8664_NAMEI_H +#define __X8664_NAMEI_H + +/* This dummy routine maybe changed to something useful + * for /usr/gnemul/ emulation stuff. + * Look at asm-sparc/namei.h for details. + */ + +#define __emul_prefix() NULL + +#endif diff -Nru a/include/asm-x86_64/page.h b/include/asm-x86_64/page.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/page.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,118 @@ +#ifndef _X86_64_PAGE_H +#define _X86_64_PAGE_H + +/* PAGE_SHIFT determines the page size */ +#define PAGE_SHIFT 12 +#ifdef __ASSEMBLY__ +#define PAGE_SIZE (0x1 << PAGE_SHIFT) +#else +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#endif +#define PAGE_MASK (~(PAGE_SIZE-1)) +#define THREAD_SIZE (2*PAGE_SIZE) +#define CURRENT_MASK (~(THREAD_SIZE-1)) + +#ifdef __KERNEL__ +#ifndef __ASSEMBLY__ + +#include + +#ifdef CONFIG_X86_USE_3DNOW + +#include + +#define clear_page(page) mmx_clear_page((void *)(page)) +#define copy_page(to,from) mmx_copy_page(to,from) + +#else + +/* + * On older X86 processors its not a win to use MMX here it seems. + * Maybe the K6-III ? + */ + +#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE) + +#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE) + +#endif + +#define clear_user_page(page, vaddr) clear_page(page) +#define copy_user_page(to, from, vaddr) copy_page(to, from) + +/* + * These are used to make use of C type-checking.. + */ +typedef struct { unsigned long pte; } pte_t; +typedef struct { unsigned long pmd; } pmd_t; +typedef struct { unsigned long pgd; } pgd_t; +typedef struct { unsigned long level4; } level4_t; +#define PTE_MASK PAGE_MASK + +typedef struct { unsigned long pgprot; } pgprot_t; + +#define pte_val(x) ((x).pte) +#define pmd_val(x) ((x).pmd) +#define pgd_val(x) ((x).pgd) +#define level4_val(x) ((x).level4) +#define pgprot_val(x) ((x).pgprot) + +#define __pte(x) ((pte_t) { (x) } ) +#define __pmd(x) ((pmd_t) { (x) } ) +#define __pgd(x) ((pgd_t) { (x) } ) +#define __level4(x) ((level4_t) { (x) } ) +#define __pgprot(x) ((pgprot_t) { (x) } ) + +#endif /* !__ASSEMBLY__ */ + +/* to align the pointer to the (next) page boundary */ +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) + + +#define __START_KERNEL 0xffffffff80100000 +#define __START_KERNEL_map 0xffffffff80000000 +#define __PAGE_OFFSET 0xffff800000000000 + +#ifndef __ASSEMBLY__ + +/* + * Tell the user there is some problem. + */ + +struct bug_frame { + unsigned short ud2; + char *filename; /* should use 32bit offset instead, but the assembler doesn't like it */ + unsigned short line; +} __attribute__((packed)); + +#define BUG() asm volatile("ud2 ; .quad %c1 ; .short %c0" :: "i"(__LINE__), "i" (__FILE__)) +#define PAGE_BUG(page) BUG(); + +/* Pure 2^n version of get_order */ +extern __inline__ int get_order(unsigned long size) +{ + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + +static unsigned long start_kernel_map __attribute__((unused)) = __START_KERNEL_map; /* FIXME: workaround gcc bug */ + +#endif /* __ASSEMBLY__ */ + +#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET) +#define __pa(x) (((unsigned long)(x)>=start_kernel_map)?(unsigned long)(x) - (unsigned long)start_kernel_map:(unsigned long)(x) - PAGE_OFFSET) +#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) +#define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT)) +#define VALID_PAGE(page) ((page - mem_map) < max_mapnr) + + +#endif /* __KERNEL__ */ + +#endif /* _X86_64_PAGE_H */ diff -Nru a/include/asm-x86_64/param.h b/include/asm-x86_64/param.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/param.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,24 @@ +#ifndef _ASMx86_64_PARAM_H +#define _ASMx86_64_PARAM_H + +#ifndef HZ +#define HZ 100 +#endif + +#define EXEC_PAGESIZE 4096 + +#ifndef NGROUPS +#define NGROUPS 32 +#endif + +#ifndef NOGROUP +#define NOGROUP (-1) +#endif + +#define MAXHOSTNAMELEN 64 /* max length of hostname */ + +#ifdef __KERNEL__ +# define CLOCKS_PER_SEC 100 /* frequency at which times() counts */ +#endif + +#endif diff -Nru a/include/asm-x86_64/parport.h b/include/asm-x86_64/parport.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/parport.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,18 @@ +/* + * parport.h: ia32-specific parport initialisation + * + * Copyright (C) 1999, 2000 Tim Waugh + * + * This file should only be included by drivers/parport/parport_pc.c. + */ + +#ifndef _ASM_X8664_PARPORT_H +#define _ASM_X8664_PARPORT_H 1 + +static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma); +static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma) +{ + return parport_pc_find_isa_ports (autoirq, autodma); +} + +#endif diff -Nru a/include/asm-x86_64/pci.h b/include/asm-x86_64/pci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/pci.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,273 @@ +#ifndef __x8664_PCI_H +#define __x8664_PCI_H + +#include +#include + +#ifdef __KERNEL__ + +/* Can be used to override the logic in pci_scan_bus for skipping + already-configured bus numbers - to be used for buggy BIOSes + or architectures with incomplete PCI setup by the loader */ + +#ifdef CONFIG_PCI +extern unsigned int pcibios_assign_all_busses(void); +#else +#define pcibios_assign_all_busses() 0 +#endif + +extern unsigned long pci_mem_start; +#define PCIBIOS_MIN_IO 0x1000 +#define PCIBIOS_MIN_MEM (pci_mem_start) + +void pcibios_set_master(struct pci_dev *dev); +void pcibios_penalize_isa_irq(int irq); +struct irq_routing_table *pcibios_get_irq_routing_table(void); +int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); + +/* Dynamic DMA mapping stuff. + * x8664 has everything mapped statically. + */ + +#include +#include +#include +#include +#include + +struct pci_dev; + +/* The PCI address space does equal the physical memory + * address space. The networking and block device layers use + * this boolean for bounce buffer decisions. + */ +#define PCI_DMA_BUS_IS_PHYS (1) + +/* Allocate and map kernel buffer using consistent mode DMA for a device. + * hwdev should be valid struct pci_dev pointer for PCI devices, + * NULL for PCI-like buses (ISA, EISA). + * Returns non-NULL cpu-view pointer to the buffer if successful and + * sets *dma_addrp to the pci side dma address as well, else *dma_addrp + * is undefined. + */ +extern void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle); + +/* Free and unmap a consistent DMA buffer. + * cpu_addr is what was returned from pci_alloc_consistent, + * size must be the same as what as passed into pci_alloc_consistent, + * and likewise dma_addr must be the same as what *dma_addrp was set to. + * + * References to the memory and mappings associated with cpu_addr/dma_addr + * past this call are illegal. + */ +extern void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle); + +/* Map a single buffer of the indicated size for DMA in streaming mode. + * The 32-bit bus address to use is returned. + * + * Once the device is given the dma address, the device owns this memory + * until either pci_unmap_single or pci_dma_sync_single is performed. + */ +static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + flush_write_buffers(); + return virt_to_phys(ptr); +} + +/* Unmap a single streaming mode DMA translation. The dma_addr and size + * must match what was provided for in a previous pci_map_single call. All + * other usages are undefined. + * + * After this call, reads by the cpu to the buffer are guarenteed to see + * whatever the device wrote there. + */ +static inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + /* Nothing to do */ +} + +/* + * pci_{map,unmap}_single_page maps a kernel page to a dma_addr_t. identical + * to pci_map_single, but takes a struct page instead of a virtual address + */ +static inline dma_addr_t pci_map_page(struct pci_dev *hwdev, struct page *page, + unsigned long offset, size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + + return (page - mem_map) * PAGE_SIZE + offset; +} + +static inline void pci_unmap_page(struct pci_dev *hwdev, dma_addr_t dma_address, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + /* Nothing to do */ +} + +/* pci_unmap_{page,single} is a nop so... */ +#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) +#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) +#define pci_unmap_addr(PTR, ADDR_NAME) (0) +#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) do { } while (0) +#define pci_unmap_len(PTR, LEN_NAME) (0) +#define pci_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) + +/* Map a set of buffers described by scatterlist in streaming + * mode for DMA. This is the scather-gather version of the + * above pci_map_single interface. Here the scatter gather list + * elements are each tagged with the appropriate dma address + * and length. They are obtained via sg_dma_{address,length}(SG). + * + * NOTE: An implementation may be able to use a smaller number of + * DMA address/length pairs than there are SG table elements. + * (for example via virtual mapping capabilities) + * The routine returns the number of addr/length pairs actually + * used, at most nents. + * + * Device ownership issues as mentioned above for pci_map_single are + * the same here. + */ +static inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + int i; + + if (direction == PCI_DMA_NONE) + BUG(); + + for (i = 0; i < nents; i++ ) { + if (!sg[i].page) + BUG(); + + sg[i].dma_address = page_to_phys(sg[i].page) + sg[i].offset; + } + + flush_write_buffers(); + return nents; +} + +/* Unmap a set of streaming mode DMA translations. + * Again, cpu read rules concerning calls here are the same as for + * pci_unmap_single() above. + */ +static inline void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + /* Nothing to do */ +} + +/* Make physical memory consistent for a single + * streaming mode DMA translation after a transfer. + * + * If you perform a pci_map_single() but wish to interrogate the + * buffer using the cpu, yet do not wish to teardown the PCI dma + * mapping, you must call this function before doing so. At the + * next point you give the PCI dma address back to the card, the + * device again owns the buffer. + */ +static inline void pci_dma_sync_single(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + flush_write_buffers(); +} + +/* Make physical memory consistent for a set of streaming + * mode DMA translations after a transfer. + * + * The same as pci_dma_sync_single but for a scatter-gather list, + * same rules and usage. + */ +static inline void pci_dma_sync_sg(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + flush_write_buffers(); +} + +/* Return whether the given PCI device DMA address mask can + * be supported properly. For example, if your device can + * only drive the low 24-bits during PCI bus mastering, then + * you would pass 0x00ffffff as the mask to this function. + */ +static inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) +{ + /* + * we fall back to GFP_DMA when the mask isn't all 1s, + * so we can't guarantee allocations that must be + * within a tighter range than GFP_DMA.. + */ + if(mask < 0x00ffffff) + return 0; + + return 1; +} + +/* This is always fine. */ +#define pci_dac_dma_supported(pci_dev, mask) (1) + +static __inline__ dma64_addr_t +pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page, unsigned long offset, int direction) +{ + return ((dma64_addr_t) page_to_phys(page) + + (dma64_addr_t) offset); +} + +static __inline__ struct page * +pci_dac_dma_to_page(struct pci_dev *pdev, dma64_addr_t dma_addr) +{ + unsigned long poff = (dma_addr >> PAGE_SHIFT); + + return mem_map + poff; +} + +static __inline__ unsigned long +pci_dac_dma_to_offset(struct pci_dev *pdev, dma64_addr_t dma_addr) +{ + return (dma_addr & ~PAGE_MASK); +} + +static __inline__ void +pci_dac_dma_sync_single(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +{ + flush_write_buffers(); +} + +/* These macros should be used after a pci_map_sg call has been done + * to get bus addresses of each of the SG entries and their lengths. + * You should only work with the number of sg entries pci_map_sg + * returns. + */ +#define sg_dma_address(sg) ((sg)->dma_address) +#define sg_dma_len(sg) ((sg)->length) + +/* Return the index of the PCI controller for device. */ +static inline int pci_controller_num(struct pci_dev *dev) +{ + return 0; +} + +#define HAVE_PCI_MMAP +extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine); + +#endif /* __KERNEL__ */ + +#endif /* __x8664_PCI_H */ diff -Nru a/include/asm-x86_64/pda.h b/include/asm-x86_64/pda.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/pda.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,79 @@ +#ifndef X86_64_PDA_H +#define X86_64_PDA_H + +#include +#ifndef ASM_OFFSET_H +#include +#endif +#include + +struct task_struct; + +/* Per processor datastructure. %gs points to it while the kernel runs */ +/* To use a new field with the *_pda macros it needs to be added to tools/offset.c */ +struct x8664_pda { + struct x8664_pda *me; + unsigned long kernelstack; /* TOS for current process */ + unsigned long oldrsp; /* user rsp for system call */ + unsigned long irqrsp; /* Old rsp for interrupts. */ + struct task_struct *pcurrent; /* Current process */ + int irqcount; /* Irq nesting counter. Starts with -1 */ + int cpunumber; /* Logical CPU number */ + /* XXX: could be a single list */ + unsigned long *pgd_quick; + unsigned long *pmd_quick; + unsigned long *pte_quick; + unsigned long pgtable_cache_sz; + char *irqstackptr; + unsigned int __softirq_pending; + unsigned int __local_irq_count; + unsigned int __local_bh_count; + unsigned int __nmi_count; /* arch dependent */ + struct task_struct * __ksoftirqd_task; /* waitqueue is too large */ + char irqstack[16 * 1024]; /* Stack used by interrupts */ +} ____cacheline_aligned; + +#define PDA_STACKOFFSET (5*8) + +extern struct x8664_pda cpu_pda[]; + +/* + * There is no fast way to get the base address of the PDA, all the accesses + * have to mention %fs/%gs. So it needs to be done this Torvaldian way. + */ +#define sizeof_field(type,field) (sizeof(((type *)0)->field)) +#define typeof_field(type,field) typeof(((type *)0)->field) +#ifndef __STR +#define __STR(x) #x +#endif +#define __STR2(x) __STR(x) + +extern void __bad_pda_field(void); + +#define pda_to_op(op,field,val) do { \ + switch (sizeof_field(struct x8664_pda, field)) { \ + case 2: asm volatile(op "w %0,%%gs:" __STR2(pda_ ## field) ::"r" (val):"memory"); break; \ + case 4: asm volatile(op "l %0,%%gs:" __STR2(pda_ ## field) ::"r" (val):"memory"); break; \ + case 8: asm volatile(op "q %0,%%gs:" __STR2(pda_ ## field) ::"r" (val):"memory"); break; \ + default: __bad_pda_field(); \ + } \ + } while (0) + + +#define pda_from_op(op,field) ({ \ + typedef typeof_field(struct x8664_pda, field) T__; T__ ret__; \ + switch (sizeof_field(struct x8664_pda, field)) { \ + case 2: asm volatile (op "w %%gs:" __STR2(pda_ ## field) ",%0":"=r" (ret__)::"memory"); break; \ + case 4: asm volatile (op "l %%gs:" __STR2(pda_ ## field) ",%0":"=r" (ret__)::"memory"); break; \ + case 8: asm volatile (op "q %%gs:" __STR2(pda_ ## field) ",%0":"=r" (ret__)::"memory"); break; \ + default: __bad_pda_field(); \ + } \ + ret__; }) + + +#define read_pda(field) pda_from_op("mov",field) +#define write_pda(field,val) pda_to_op("mov",field,val) +#define add_pda(field,val) pda_to_op("add",field,val) +#define sub_pda(field,val) pda_to_op("sub",field,val) + +#endif diff -Nru a/include/asm-x86_64/pgalloc.h b/include/asm-x86_64/pgalloc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/pgalloc.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,258 @@ +#ifndef _X86_64_PGALLOC_H +#define _X86_64_PGALLOC_H + +#include +#include +#include +#include +#include +#include + +#define inc_pgcache_size() add_pda(pgtable_cache_sz,1UL) +#define dec_pgcache_size() sub_pda(pgtable_cache_sz,1UL) + +#define pmd_populate(mm, pmd, pte) \ + set_pmd(pmd, __pmd(_PAGE_TABLE | __pa(pte))) +#define pgd_populate(mm, pgd, pmd) \ + set_pgd(pgd, __pgd(_PAGE_TABLE | __pa(pmd))) + +extern __inline__ pmd_t *get_pmd_slow(void) +{ + pmd_t *ret = (pmd_t *)__get_free_page(GFP_KERNEL); + + if (ret) + memset(ret, 0, PAGE_SIZE); + return ret; +} + +extern __inline__ pmd_t *get_pmd_fast(void) +{ + unsigned long *ret; + + preempt_disable(); + ret = read_pda(pmd_quick); + if (ret) { + write_pda(pmd_quick, (unsigned long *)(*ret)); + ret[0] = 0; + dec_pgcache_size(); + } + preempt_enable(); + if (!ret) + ret = (unsigned long *)get_pmd_slow(); + return (pmd_t *)ret; +} + +extern __inline__ void pmd_free(pmd_t *pmd) +{ + preempt_disable(); + *(unsigned long *)pmd = (unsigned long) read_pda(pmd_quick); + write_pda(pmd_quick,(unsigned long *) pmd); + inc_pgcache_size(); + preempt_enable(); +} + +extern __inline__ void pmd_free_slow(pmd_t *pmd) +{ + if ((unsigned long)pmd & (PAGE_SIZE-1)) + BUG(); + free_page((unsigned long)pmd); +} + +static inline pmd_t *pmd_alloc_one_fast (struct mm_struct *mm, unsigned long addr) +{ + unsigned long *ret; + + preempt_disable(); + ret = (unsigned long *)read_pda(pmd_quick); + + if (__builtin_expect(ret != NULL, 1)) { + write_pda(pmd_quick, (unsigned long *)(*ret)); + ret[0] = 0; + dec_pgcache_size(); + } + preempt_enable(); + return (pmd_t *)ret; +} + +static inline pmd_t *pmd_alloc_one (struct mm_struct *mm, unsigned long addr) +{ + pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL); + + if (__builtin_expect(pmd != NULL, 1)) + clear_page(pmd); + return pmd; +} + + +static inline pgd_t *pgd_alloc_one_fast (void) +{ + unsigned long *ret; + + preempt_disable(); + ret = read_pda(pgd_quick); + if (likely(ret != NULL)) { + write_pda(pgd_quick,(unsigned long *)(*ret)); + ret[0] = 0; + dec_pgcache_size(); + } + preempt_enable(); + return (pgd_t *) ret; +} + +static inline pgd_t *pgd_alloc (struct mm_struct *mm) +{ + /* the VM system never calls pgd_alloc_one_fast(), so we do it here. */ + pgd_t *pgd = pgd_alloc_one_fast(); + + if (pgd == NULL) { + pgd = (pgd_t *)__get_free_page(GFP_KERNEL); + if (__builtin_expect(pgd != NULL, 1)) + clear_page(pgd); + } + return pgd; +} + +static inline void pgd_free (pgd_t *pgd) +{ + preempt_disable(); + *(unsigned long *)pgd = (unsigned long) read_pda(pgd_quick); + write_pda(pgd_quick,(unsigned long *) pgd); + inc_pgcache_size(); + preempt_enable(); +} + + +static inline void pgd_free_slow (pgd_t *pgd) +{ + if ((unsigned long)pgd & (PAGE_SIZE-1)) + BUG(); + free_page((unsigned long)pgd); +} + + +static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address) +{ + pte_t *pte; + + pte = (pte_t *) __get_free_page(GFP_KERNEL); + if (pte) + clear_page(pte); + return pte; +} + +extern __inline__ pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address) +{ + unsigned long *ret; + + preempt_disable(); + if(__builtin_expect((ret = read_pda(pte_quick)) != NULL, !0)) { + write_pda(pte_quick, (unsigned long *)(*ret)); + ret[0] = ret[1]; + dec_pgcache_size(); + } + preempt_enable(); + return (pte_t *)ret; +} + +/* Should really implement gc for free page table pages. This could be done with + a reference count in struct page. */ + +extern __inline__ void pte_free(pte_t *pte) +{ + preempt_disable(); + *(unsigned long *)pte = (unsigned long) read_pda(pte_quick); + write_pda(pte_quick, (unsigned long *) pte); + inc_pgcache_size(); + preempt_enable(); +} + +extern __inline__ void pte_free_slow(pte_t *pte) +{ + if ((unsigned long)pte & (PAGE_SIZE-1)) + BUG(); + free_page((unsigned long)pte); +} + + +extern int do_check_pgt_cache(int, int); + +/* + * TLB flushing: + * + * - flush_tlb() flushes the current mm struct TLBs + * - flush_tlb_all() flushes all processes TLBs + * - flush_tlb_mm(mm) flushes the specified mm context TLB's + * - flush_tlb_page(vma, vmaddr) flushes one page + * - flush_tlb_range(vma, start, end) flushes a range of pages + * - flush_tlb_pgtables(mm, start, end) flushes a range of page tables + * + * ..but the i386 has somewhat limited tlb flushing capabilities, + * and page-granular flushes are available only on i486 and up. + */ + +#ifndef CONFIG_SMP + +#define flush_tlb() __flush_tlb() +#define flush_tlb_all() __flush_tlb_all() +#define local_flush_tlb() __flush_tlb() + +static inline void flush_tlb_mm(struct mm_struct *mm) +{ + if (mm == current->active_mm) + __flush_tlb(); +} + +static inline void flush_tlb_page(struct vm_area_struct *vma, + unsigned long addr) +{ + if (vma->vm_mm == current->active_mm) + __flush_tlb_one(addr); +} + +static inline void flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + if (vma->vm_mm == current->active_mm) + __flush_tlb(); +} + +#else + +#include + +#define local_flush_tlb() \ + __flush_tlb() + +extern void flush_tlb_all(void); +extern void flush_tlb_current_task(void); +extern void flush_tlb_mm(struct mm_struct *); +extern void flush_tlb_page(struct vm_area_struct *, unsigned long); + +#define flush_tlb() flush_tlb_current_task() + +static inline void flush_tlb_range(struct vm_area_struct * vma, unsigned long start, unsigned long end) +{ + flush_tlb_mm(vma->vm_mm); +} + +#define TLBSTATE_OK 1 +#define TLBSTATE_LAZY 2 + +struct tlb_state +{ + struct mm_struct *active_mm; + int state; + char __cacheline_padding[24]; +}; +extern struct tlb_state cpu_tlbstate[NR_CPUS]; + + +#endif + +extern inline void flush_tlb_pgtables(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + /* i386 does not keep any page table caches in TLB */ +} + +#endif /* _X86_64_PGALLOC_H */ diff -Nru a/include/asm-x86_64/pgtable.h b/include/asm-x86_64/pgtable.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/pgtable.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,398 @@ +#ifndef _X86_64_PGTABLE_H +#define _X86_64_PGTABLE_H + +#include + +/* + * This file contains the functions and defines necessary to modify and use + * the x86-64 page table tree. + * + * x86-64 has a 4 level table setup. Generic linux MM only supports + * three levels. The fourth level is currently a single static page that + * is shared by everybody and just contains a pointer to the current + * three level page setup on the beginning and some kernel mappings at + * the end. For more details see Documentation/x86_64/mm.txt + */ +#ifndef __ASSEMBLY__ +#include +#include +#include +#include + +extern level4_t level4_pgt[512]; +extern pgd_t level3_kernel_pgt[512]; +extern pgd_t level3_physmem_pgt[512]; +extern pgd_t level3_ident_pgt[512], swapper_pg_dir[512]; +extern pmd_t level2_kernel_pgt[512]; +extern void paging_init(void); + +/* Caches aren't brain-dead on the intel. */ +#define flush_cache_all() do { } while (0) +#define flush_cache_mm(mm) do { } while (0) +#define flush_cache_range(vma, start, end) do { } while (0) +#define flush_cache_page(vma, vmaddr) do { } while (0) +#define flush_page_to_ram(page) do { } while (0) +#define flush_dcache_page(page) do { } while (0) +#define flush_icache_range(start, end) do { } while (0) +#define flush_icache_page(vma,pg) do { } while (0) + +#define __flush_tlb() \ + do { \ + unsigned long tmpreg; \ + \ + __asm__ __volatile__( \ + "movq %%cr3, %0; # flush TLB \n" \ + "movq %0, %%cr3; \n" \ + : "=r" (tmpreg) \ + :: "memory"); \ + } while (0) + +/* + * Global pages have to be flushed a bit differently. Not a real + * performance problem because this does not happen often. + */ +#define __flush_tlb_global() \ + do { \ + unsigned long tmpreg; \ + \ + __asm__ __volatile__( \ + "movq %1, %%cr4; # turn off PGE \n" \ + "movq %%cr3, %0; # flush TLB \n" \ + "movq %0, %%cr3; \n" \ + "movq %2, %%cr4; # turn PGE back on \n" \ + : "=&r" (tmpreg) \ + : "r" (mmu_cr4_features & ~X86_CR4_PGE), \ + "r" (mmu_cr4_features) \ + : "memory"); \ + } while (0) + +extern unsigned long pgkern_mask; + +/* + * Do not check the PGE bit unnecesserily if this is a PPro+ kernel. + * FIXME: This should be cleaned up + */ + +# define __flush_tlb_all() __flush_tlb_global() + +#define __flush_tlb_one(addr) __asm__ __volatile__("invlpg %0": :"m" (*(char *) addr)) + +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern unsigned long empty_zero_page[1024]; +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +#endif /* !__ASSEMBLY__ */ + +#define LEVEL4_SHIFT 39 +#define PTRS_PER_LEVEL4 512 + +/* + * PGDIR_SHIFT determines what a top-level page table entry can map + */ +#define PGDIR_SHIFT 30 +#define PTRS_PER_PGD 512 + +/* + * PMD_SHIFT determines the size of the area a middle-level + * page table can map + */ +#define PMD_SHIFT 21 +#define PTRS_PER_PMD 512 + +/* + * entries per page directory level + */ +#define PTRS_PER_PTE 512 + +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %p(%016lx).\n", __FILE__, __LINE__, &(e), pte_val(e)) +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %p(%016lx).\n", __FILE__, __LINE__, &(e), pmd_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %p(%016lx).\n", __FILE__, __LINE__, &(e), pgd_val(e)) + +#define level4_none(x) (!level4_val(x)) +#define pgd_none(x) (!pgd_val(x)) + +#define pgd_bad(x) ((pgd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE ) + +extern inline int pgd_present(pgd_t pgd) { return !pgd_none(pgd); } + +static inline void set_pte(pte_t *dst, pte_t val) +{ + *((unsigned long *)dst) = pte_val(val); +} + +static inline void set_pmd(pmd_t *dst, pmd_t val) +{ + *((unsigned long *)dst) = pmd_val(val); +} + +static inline void set_pgd(pgd_t *dst, pgd_t val) +{ + *((unsigned long *)dst) = pgd_val(val); +} + +extern inline void __pgd_clear (pgd_t * pgd) +{ + set_pgd(pgd, __pgd(0)); +} + +extern inline void pgd_clear (pgd_t * pgd) +{ + __pgd_clear(pgd); + __flush_tlb(); +} + +#define pgd_page(pgd) \ +((unsigned long) __va(pgd_val(pgd) & PAGE_MASK)) + +/* Find an entry in the second-level page table.. */ +#define pmd_offset(dir, address) ((pmd_t *) pgd_page(*(dir)) + \ + __pmd_offset(address)) + +#define ptep_get_and_clear(xp) __pte(xchg(&(xp)->pte, 0)) +#define pte_same(a, b) ((a).pte == (b).pte) +#define __mk_pte(page_nr,pgprot) __pte(((page_nr) << PAGE_SHIFT) | pgprot_val(pgprot)) + +#define PMD_SIZE (1UL << PMD_SHIFT) +#define PMD_MASK (~(PMD_SIZE-1)) +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE-1)) + +#define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE) +#define FIRST_USER_PGD_NR 0 + +#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT) +#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS) + +#define TWOLEVEL_PGDIR_SHIFT 20 +#define BOOT_USER_L4_PTRS 1 +#define BOOT_KERNEL_L4_PTRS 511 /* But we will do it in 4rd level */ + + + +#ifndef __ASSEMBLY__ +/* Just any arbitrary offset to the start of the vmalloc VM area: the + * current 8MB value just means that there will be a 8MB "hole" after the + * physical memory until the kernel virtual memory starts. That means that + * any out-of-bounds memory accesses will hopefully be caught. + * The vmalloc() routines leaves a hole of 4kB between each vmalloced + * area for the same reason. ;) + */ +#define VMALLOC_OFFSET (8*1024*1024) +#define VMALLOC_START (((unsigned long) high_memory + 2*VMALLOC_OFFSET-1) & \ + ~(VMALLOC_OFFSET-1)) +#define VMALLOC_VMADDR(x) ((unsigned long)(x)) +#define VMALLOC_END (__START_KERNEL_map-PAGE_SIZE) + +#define _PAGE_BIT_PRESENT 0 +#define _PAGE_BIT_RW 1 +#define _PAGE_BIT_USER 2 +#define _PAGE_BIT_PWT 3 +#define _PAGE_BIT_PCD 4 +#define _PAGE_BIT_ACCESSED 5 +#define _PAGE_BIT_DIRTY 6 +#define _PAGE_BIT_PSE 7 /* 4 MB (or 2MB) page, Pentium+, if present.. */ +#define _PAGE_BIT_GLOBAL 8 /* Global TLB entry PPro+ */ + +#define _PAGE_PRESENT 0x001 +#define _PAGE_RW 0x002 +#define _PAGE_USER 0x004 +#define _PAGE_PWT 0x008 +#define _PAGE_PCD 0x010 +#define _PAGE_ACCESSED 0x020 +#define _PAGE_DIRTY 0x040 +#define _PAGE_PSE 0x080 /* 2MB page */ +#define _PAGE_GLOBAL 0x100 /* Global TLB entry PPro+ */ + +#define _PAGE_PROTNONE 0x080 /* If not present */ + +#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY) +#define _KERNPG_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) + +#define _PAGE_CHG_MASK (PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY) + +#define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED) +#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED) +#define PAGE_COPY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED) +#define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED) + +#define __PAGE_KERNEL \ + (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED) +#define __PAGE_KERNEL_NOCACHE \ + (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_PCD | _PAGE_ACCESSED) +#define __PAGE_KERNEL_RO \ + (_PAGE_PRESENT | _PAGE_DIRTY | _PAGE_ACCESSED) +#define __PAGE_KERNEL_VSYSCALL \ + (_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED) + +#define MAKE_GLOBAL(x) __pgprot((x) | _PAGE_GLOBAL) + +#define PAGE_KERNEL MAKE_GLOBAL(__PAGE_KERNEL) +#define PAGE_KERNEL_RO MAKE_GLOBAL(__PAGE_KERNEL_RO) +#define PAGE_KERNEL_NOCACHE MAKE_GLOBAL(__PAGE_KERNEL_NOCACHE) +#define PAGE_KERNEL_VSYSCALL MAKE_GLOBAL(__PAGE_KERNEL_VSYSCALL) + +/* + * The i386 can't do page protection for execute, and considers that + * the same are read. Also, write permissions imply read permissions. + * This is the closest we can get.. + */ +#define __P000 PAGE_NONE +#define __P001 PAGE_READONLY +#define __P010 PAGE_COPY +#define __P011 PAGE_COPY +#define __P100 PAGE_READONLY +#define __P101 PAGE_READONLY +#define __P110 PAGE_COPY +#define __P111 PAGE_COPY + +#define __S000 PAGE_NONE +#define __S001 PAGE_READONLY +#define __S010 PAGE_SHARED +#define __S011 PAGE_SHARED +#define __S100 PAGE_READONLY +#define __S101 PAGE_READONLY +#define __S110 PAGE_SHARED +#define __S111 PAGE_SHARED + +/* + * Define this if things work differently on an i386 and an i486: + * it will (on an i486) warn about kernel memory accesses that are + * done without a 'verify_area(VERIFY_WRITE,..)' + */ +#undef TEST_VERIFY_AREA + +/* page table for 0-4MB for everybody */ +extern unsigned long pg0[1024]; + +/* + * Handling allocation failures during page table setup. + */ +extern void __handle_bad_pmd(pmd_t * pmd); +extern void __handle_bad_pmd_kernel(pmd_t * pmd); + +#define pte_none(x) (!pte_val(x)) +#define pte_present(x) (pte_val(x) & (_PAGE_PRESENT | _PAGE_PROTNONE)) +#define pte_clear(xp) do { set_pte(xp, __pte(0)); } while (0) + +#define pmd_none(x) (!pmd_val(x)) +#define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT) +#define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0) +#define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE ) + +/* + * Permanent address of a page. Obviously must never be + * called on a highmem page. + */ +#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) /* FIXME: is this + right? */ +#define pte_page(x) (mem_map+((unsigned long)((pte_val(x) >> PAGE_SHIFT)))) + +/* + * The following only work if pte_present() is true. + * Undefined behaviour if not.. + */ +extern inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_USER; } +extern inline int pte_exec(pte_t pte) { return pte_val(pte) & _PAGE_USER; } +extern inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; } +extern inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; } +extern inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_RW; } + +extern inline pte_t pte_rdprotect(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_USER)); return pte; } +extern inline pte_t pte_exprotect(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_USER)); return pte; } +extern inline pte_t pte_mkclean(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_DIRTY)); return pte; } +extern inline pte_t pte_mkold(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_ACCESSED)); return pte; } +extern inline pte_t pte_wrprotect(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_RW)); return pte; } +extern inline pte_t pte_mkread(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_USER)); return pte; } +extern inline pte_t pte_mkexec(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_USER)); return pte; } +extern inline pte_t pte_mkdirty(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_DIRTY)); return pte; } +extern inline pte_t pte_mkyoung(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_ACCESSED)); return pte; } +extern inline pte_t pte_mkwrite(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_RW)); return pte; } +static inline int ptep_test_and_clear_dirty(pte_t *ptep) { return test_and_clear_bit(_PAGE_BIT_DIRTY, ptep); } +static inline int ptep_test_and_clear_young(pte_t *ptep) { return test_and_clear_bit(_PAGE_BIT_ACCESSED, ptep); } +static inline void ptep_set_wrprotect(pte_t *ptep) { clear_bit(_PAGE_BIT_RW, ptep); } +static inline void ptep_mkdirty(pte_t *ptep) { set_bit(_PAGE_BIT_DIRTY, ptep); } + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + */ + +#define mk_pte(page,pgprot) \ +({ \ + pte_t __pte; \ + \ + set_pte(&__pte, __pte(((page)-mem_map) * \ + (unsigned long long)PAGE_SIZE + pgprot_val(pgprot))); \ + __pte; \ +}) + +/* This takes a physical page address that is used by the remapping functions */ +#define mk_pte_phys(physpage, pgprot) \ +({ pte_t __pte; set_pte(&__pte, __pte(physpage + pgprot_val(pgprot))); __pte; }) + +extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot) +{ set_pte(&pte, __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot))); return pte; } + +#define page_pte(page) page_pte_prot(page, __pgprot(0)) + +#define pmd_page(pmd) \ +((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) + +/* to find an entry in a page-table-directory. */ +#define pgd_index(address) ((address >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) + +#define __pgd_offset(address) pgd_index(address) + +#define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address)) + +/* to find an entry in a kernel page-table-directory */ +#define pgd_offset_k(address) pgd_offset(&init_mm, address) + +#define __pmd_offset(address) \ + (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1)) + +/* Find an entry in the third-level page table.. */ +#define __pte_offset(address) \ + ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) +#define pte_offset(dir, address) ((pte_t *) pmd_page(*(dir)) + \ + __pte_offset(address)) + +/* never use these in the common code */ +#define level4_page(level4) ((unsigned long) __va(level4_val(level4) & PAGE_MASK)) +#define level4_index(address) ((address >> LEVEL4_SHIFT) & (PTRS_PER_LEVEL4-1)) +#define level4_offset_k(address) (level4_pgt + level4_index(address)) +#define level3_offset_k(dir, address) ((pgd_t *) level4_page(*(dir)) + pgd_index(address)) + +/* + * The i386 doesn't have any external MMU info: the kernel page + * tables contain all the necessary information. + */ +#define update_mmu_cache(vma,address,pte) do { } while (0) + +/* Encode and de-code a swap entry */ +#define SWP_TYPE(x) (((x).val >> 1) & 0x3f) +#define SWP_OFFSET(x) ((x).val >> 8) +#define SWP_ENTRY(type, offset) ((swp_entry_t) { ((type) << 1) | ((offset) << 8) }) +#define pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) +#define swp_entry_to_pte(x) ((pte_t) { (x).val }) + +#endif /* !__ASSEMBLY__ */ + +/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ +#define PageSkip(page) (0) +#define kern_addr_valid(addr) (1) + +#define io_remap_page_range remap_page_range + +#define HAVE_ARCH_UNMAPPED_AREA + +#define pgtable_cache_init() do { } while (0) + + +#endif /* _X86_64_PGTABLE_H */ diff -Nru a/include/asm-x86_64/poll.h b/include/asm-x86_64/poll.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/poll.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,25 @@ +#ifndef __x86_64_POLL_H +#define __x86_64_POLL_H + +/* These are specified by iBCS2 */ +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +/* The rest seem to be more-or-less nonstandard. Check them! */ +#define POLLRDNORM 0x0040 +#define POLLRDBAND 0x0080 +#define POLLWRNORM 0x0100 +#define POLLWRBAND 0x0200 +#define POLLMSG 0x0400 + +struct pollfd { + int fd; + short events; + short revents; +}; + +#endif diff -Nru a/include/asm-x86_64/posix_types.h b/include/asm-x86_64/posix_types.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/posix_types.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,116 @@ +#ifndef _ASM_X86_64_POSIX_TYPES_H +#define _ASM_X86_64_POSIX_TYPES_H + +/* + * This file is generally used by user-level software, so you need to + * be a little careful about namespace pollution etc. Also, we cannot + * assume GCC is being used. + */ + +typedef unsigned long __kernel_dev_t; +typedef unsigned long __kernel_ino_t; +typedef unsigned int __kernel_mode_t; +typedef unsigned long __kernel_nlink_t; +typedef long __kernel_off_t; +typedef int __kernel_pid_t; +typedef int __kernel_ipc_pid_t; +typedef unsigned int __kernel_uid_t; +typedef unsigned int __kernel_gid_t; +typedef unsigned long __kernel_size_t; +typedef long __kernel_ssize_t; +typedef long __kernel_ptrdiff_t; +typedef long __kernel_time_t; +typedef long __kernel_suseconds_t; +typedef long __kernel_clock_t; +typedef int __kernel_daddr_t; +typedef char * __kernel_caddr_t; +typedef unsigned short __kernel_uid16_t; +typedef unsigned short __kernel_gid16_t; + +#ifdef __GNUC__ +typedef long long __kernel_loff_t; +#endif + +typedef struct { + int val[2]; +} __kernel_fsid_t; + +typedef __kernel_uid_t __kernel_old_uid_t; +typedef __kernel_gid_t __kernel_old_gid_t; +typedef __kernel_uid_t __kernel_uid32_t; +typedef __kernel_gid_t __kernel_gid32_t; + +#ifdef __KERNEL__ + +#undef __FD_SET +static __inline__ void __FD_SET(unsigned long fd, __kernel_fd_set *fdsetp) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + fdsetp->fds_bits[_tmp] |= (1UL<<_rem); +} + +#undef __FD_CLR +static __inline__ void __FD_CLR(unsigned long fd, __kernel_fd_set *fdsetp) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + fdsetp->fds_bits[_tmp] &= ~(1UL<<_rem); +} + +#undef __FD_ISSET +static __inline__ int __FD_ISSET(unsigned long fd, __const__ __kernel_fd_set *p) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + return (p->fds_bits[_tmp] & (1UL<<_rem)) != 0; +} + +/* + * This will unroll the loop for the normal constant cases (8 or 32 longs, + * for 256 and 1024-bit fd_sets respectively) + */ +#undef __FD_ZERO +static __inline__ void __FD_ZERO(__kernel_fd_set *p) +{ + unsigned long *tmp = p->fds_bits; + int i; + + if (__builtin_constant_p(__FDSET_LONGS)) { + switch (__FDSET_LONGS) { + case 32: + tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0; + tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0; + tmp[ 8] = 0; tmp[ 9] = 0; tmp[10] = 0; tmp[11] = 0; + tmp[12] = 0; tmp[13] = 0; tmp[14] = 0; tmp[15] = 0; + tmp[16] = 0; tmp[17] = 0; tmp[18] = 0; tmp[19] = 0; + tmp[20] = 0; tmp[21] = 0; tmp[22] = 0; tmp[23] = 0; + tmp[24] = 0; tmp[25] = 0; tmp[26] = 0; tmp[27] = 0; + tmp[28] = 0; tmp[29] = 0; tmp[30] = 0; tmp[31] = 0; + return; + case 16: + tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0; + tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0; + tmp[ 8] = 0; tmp[ 9] = 0; tmp[10] = 0; tmp[11] = 0; + tmp[12] = 0; tmp[13] = 0; tmp[14] = 0; tmp[15] = 0; + return; + case 8: + tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0; + tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0; + return; + case 4: + tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0; + return; + } + } + i = __FDSET_LONGS; + while (i) { + i--; + *tmp = 0; + tmp++; + } +} + +#endif /* defined(__KERNEL__) */ + +#endif diff -Nru a/include/asm-x86_64/prctl.h b/include/asm-x86_64/prctl.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/prctl.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,10 @@ +#ifndef X86_64_PRCTL_H +#define X86_64_PRCTL_H 1 + +#define ARCH_SET_GS 0x1001 +#define ARCH_SET_FS 0x1002 +#define ARCH_GET_FS 0x1003 +#define ARCH_GET_GS 0x1004 + + +#endif diff -Nru a/include/asm-x86_64/processor.h b/include/asm-x86_64/processor.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/processor.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,463 @@ +/* + * include/asm-x86_64/processor.h + * + * Copyright (C) 1994 Linus Torvalds + */ + +#ifndef __ASM_X86_64_PROCESSOR_H +#define __ASM_X86_64_PROCESSOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TF_MASK 0x00000100 +#define IF_MASK 0x00000200 +#define IOPL_MASK 0x00003000 +#define NT_MASK 0x00004000 +#define VM_MASK 0x00020000 +#define AC_MASK 0x00040000 +#define VIF_MASK 0x00080000 /* virtual interrupt flag */ +#define VIP_MASK 0x00100000 /* virtual interrupt pending */ +#define ID_MASK 0x00200000 + +#define current_text_addr() ({ void *pc; asm volatile("leaq 1f(%%rip),%0\n1:":"=r"(pc)); pc; }) + +/* + * CPU type and hardware bug flags. Kept separately for each CPU. + * Members of this structure are referenced in head.S, so think twice + * before touching them. [mj] + */ + +struct cpuinfo_x86 { + __u8 x86; /* CPU family */ + __u8 x86_vendor; /* CPU vendor */ + __u8 x86_model; + __u8 x86_mask; + /* We know that wp_works_ok = 1, hlt_works_ok = 1, hard_math = 1, + etc... */ + char wp_works_ok; /* It doesn't on 386's */ + char hlt_works_ok; /* Problems on some 486Dx4's and old 386's */ + char hard_math; + char rfu; + int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */ + __u32 x86_capability[NCAPINTS]; + char x86_vendor_id[16]; + char x86_model_id[64]; + int x86_cache_size; /* in KB - valid for CPUS which support this + call */ + int fdiv_bug; + int f00f_bug; + int coma_bug; + unsigned long loops_per_jiffy; +} ____cacheline_aligned; + +#define X86_VENDOR_INTEL 0 +#define X86_VENDOR_CYRIX 1 +#define X86_VENDOR_AMD 2 +#define X86_VENDOR_UMC 3 +#define X86_VENDOR_NEXGEN 4 +#define X86_VENDOR_CENTAUR 5 +#define X86_VENDOR_RISE 6 +#define X86_VENDOR_TRANSMETA 7 +#define X86_VENDOR_UNKNOWN 0xff + +extern struct cpuinfo_x86 boot_cpu_data; +extern struct tss_struct init_tss[NR_CPUS]; + +#ifdef CONFIG_SMP +extern struct cpuinfo_x86 cpu_data[]; +#define current_cpu_data cpu_data[smp_processor_id()] +#else +#define cpu_data &boot_cpu_data +#define current_cpu_data boot_cpu_data +#endif + +#define cpu_has_pge 1 +#define cpu_has_pse 1 +#define cpu_has_pae 1 +#define cpu_has_tsc 1 +#define cpu_has_de 1 +#define cpu_has_vme 1 +#define cpu_has_fxsr 1 +#define cpu_has_xmm 1 +#define cpu_has_apic 1 + +extern char ignore_irq13; + +extern void identify_cpu(struct cpuinfo_x86 *); +extern void print_cpu_info(struct cpuinfo_x86 *); +extern void dodgy_tsc(void); + +/* + * EFLAGS bits + */ +#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */ +#define X86_EFLAGS_PF 0x00000004 /* Parity Flag */ +#define X86_EFLAGS_AF 0x00000010 /* Auxillary carry Flag */ +#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */ +#define X86_EFLAGS_SF 0x00000080 /* Sign Flag */ +#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */ +#define X86_EFLAGS_IF 0x00000200 /* Interrupt Flag */ +#define X86_EFLAGS_DF 0x00000400 /* Direction Flag */ +#define X86_EFLAGS_OF 0x00000800 /* Overflow Flag */ +#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */ +#define X86_EFLAGS_NT 0x00004000 /* Nested Task */ +#define X86_EFLAGS_RF 0x00010000 /* Resume Flag */ +#define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */ +#define X86_EFLAGS_AC 0x00040000 /* Alignment Check */ +#define X86_EFLAGS_VIF 0x00080000 /* Virtual Interrupt Flag */ +#define X86_EFLAGS_VIP 0x00100000 /* Virtual Interrupt Pending */ +#define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */ + +/* + * Generic CPUID function + * FIXME: This really belongs to msr.h + */ +extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx) +{ + __asm__("cpuid" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "0" (op)); +} + +/* + * CPUID functions returning a single datum + */ +extern inline unsigned int cpuid_eax(unsigned int op) +{ + unsigned int eax; + + __asm__("cpuid" + : "=a" (eax) + : "0" (op) + : "bx", "cx", "dx"); + return eax; +} +extern inline unsigned int cpuid_ebx(unsigned int op) +{ + unsigned int eax, ebx; + + __asm__("cpuid" + : "=a" (eax), "=b" (ebx) + : "0" (op) + : "cx", "dx" ); + return ebx; +} +extern inline unsigned int cpuid_ecx(unsigned int op) +{ + unsigned int eax, ecx; + + __asm__("cpuid" + : "=a" (eax), "=c" (ecx) + : "0" (op) + : "bx", "dx" ); + return ecx; +} +extern inline unsigned int cpuid_edx(unsigned int op) +{ + unsigned int eax, edx; + + __asm__("cpuid" + : "=a" (eax), "=d" (edx) + : "0" (op) + : "bx", "cx"); + return edx; +} + +/* + * Intel CPU features in CR4 + */ +#define X86_CR4_VME 0x0001 /* enable vm86 extensions */ +#define X86_CR4_PVI 0x0002 /* virtual interrupts flag enable */ +#define X86_CR4_TSD 0x0004 /* disable time stamp at ipl 3 */ +#define X86_CR4_DE 0x0008 /* enable debugging extensions */ +#define X86_CR4_PSE 0x0010 /* enable page size extensions */ +#define X86_CR4_PAE 0x0020 /* enable physical address extensions */ +#define X86_CR4_MCE 0x0040 /* Machine check enable */ +#define X86_CR4_PGE 0x0080 /* enable global pages */ +#define X86_CR4_PCE 0x0100 /* enable performance counters at ipl 3 */ +#define X86_CR4_OSFXSR 0x0200 /* enable fast FPU save and restore */ +#define X86_CR4_OSXMMEXCPT 0x0400 /* enable unmasked SSE exceptions */ + +/* + * Save the cr4 feature set we're using (ie + * Pentium 4MB enable and PPro Global page + * enable), so that any CPU's that boot up + * after us can get the correct flags. + */ +extern unsigned long mmu_cr4_features; + +static inline void set_in_cr4 (unsigned long mask) +{ + mmu_cr4_features |= mask; + __asm__("movq %%cr4,%%rax\n\t" + "orq %0,%%rax\n\t" + "movq %%rax,%%cr4\n" + : : "irg" (mask) + :"ax"); +} + +static inline void clear_in_cr4 (unsigned long mask) +{ + mmu_cr4_features &= ~mask; + __asm__("movq %%cr4,%%rax\n\t" + "andq %0,%%rax\n\t" + "movq %%rax,%%cr4\n" + : : "irg" (~mask) + :"ax"); +} + +#define CX86_CCR0 0xc0 +#define CX86_CCR1 0xc1 +#define CX86_CCR2 0xc2 +#define CX86_CCR3 0xc3 +#define CX86_CCR4 0xe8 +#define CX86_CCR5 0xe9 +#define CX86_CCR6 0xea +#define CX86_CCR7 0xeb +#define CX86_DIR0 0xfe +#define CX86_DIR1 0xff +#define CX86_ARR_BASE 0xc4 +#define CX86_RCR_BASE 0xdc + +/* + * Cyrix CPU indexed register access macros + */ + +#define getCx86(reg) ({ outb((reg), 0x22); inb(0x23); }) + +#define setCx86(reg, data) do { \ + outb((reg), 0x22); \ + outb((data), 0x23); \ +} while (0) + + +/* + * Bus types + */ +#define EISA_bus 0 +#define MCA_bus 0 +#define MCA_bus__is_a_macro + + +/* + * User space process size: 512GB - 1GB (default). + */ +#define TASK_SIZE (0x0000007fc0000000) + +/* This decides where the kernel will search for a free chunk of vm + * space during mmap's. + */ +#define TASK_UNMAPPED_32 0x40000000 +#define TASK_UNMAPPED_64 (TASK_SIZE/3) +#define TASK_UNMAPPED_BASE \ + ((current->thread.flags & THREAD_IA32) ? TASK_UNMAPPED_32 : TASK_UNMAPPED_64) + +/* + * Size of io_bitmap in longwords: 32 is ports 0-0x3ff. + */ +#define IO_BITMAP_SIZE 32 +#define IO_BITMAP_OFFSET offsetof(struct tss_struct,io_bitmap) +#define INVALID_IO_BITMAP_OFFSET 0x8000 + +/* We'll have to decide which format to use for floating stores, and + kill all others... */ +struct i387_fsave_struct { + u32 cwd; + u32 swd; + u32 twd; + u32 fip; + u32 fcs; + u32 foo; + u32 fos; + u32 st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */ + u32 status; /* software status information */ +}; + +struct i387_fxsave_struct { + u16 cwd; + u16 swd; + u16 twd; + u16 fop; + u32 fip; + u32 fcs; + u32 foo; + u32 fos; + u32 mxcsr; + u32 reserved; + u32 st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ + u32 xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */ + u32 padding[56]; +} __attribute__ ((aligned (16))); + +struct i387_soft_struct { + u32 cwd; + u32 swd; + u32 twd; + u32 fip; + u32 fcs; + u32 foo; + u32 fos; + u32 st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */ + unsigned char ftop, changed, lookahead, no_update, rm, alimit; + struct info *info; + unsigned long entry_eip; +}; + +union i387_union { + struct i387_fsave_struct fsave; + struct i387_fxsave_struct fxsave; + struct i387_soft_struct soft; +}; + +typedef struct { + unsigned long seg; +} mm_segment_t; + +struct tss_struct { + u32 reserved1; + u64 rsp0; + u64 rsp1; + u64 rsp2; + u64 reserved2; + u64 ist[7]; + u32 reserved3; + u32 reserved4; + u16 reserved5; + u16 io_map_base; + u32 io_bitmap[IO_BITMAP_SIZE]; +} __attribute__((packed)); + +struct thread_struct { + unsigned long rsp0; + unsigned long rip; + unsigned long rsp; + unsigned long userrsp; /* Copy from PDA */ + unsigned long fs; + unsigned long gs; + unsigned short es, ds, fsindex, gsindex; +/* Hardware debugging registers */ + unsigned long debugreg[8]; /* %%db0-7 debug registers */ +/* fault info */ + unsigned long cr2, trap_no, error_code; +/* floating point info */ + union i387_union i387; +/* IO permissions. the bitmap could be moved into the GDT, that would make + switch faster for a limited number of ioperm using tasks. -AK */ + int ioperm; + u32 io_bitmap[IO_BITMAP_SIZE+1]; +}; + +#define INIT_THREAD { \ +} + +#define INIT_MMAP \ +{ &init_mm, 0, 0, NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL } + + +#ifndef CONFIG_SMP +extern char stackfault_stack[]; +#define STACKDESC rsp2: (unsigned long)stackfault_stack, +#define STACKFAULT_STACK 2 +#else +#define STACKFAULT_STACK 0 +#define STACKDESC +#endif + +/* Doublefault currently shares the same stack on all CPUs. Hopefully + only one gets into this unfortunate condition at a time. Cannot do + the same for SF because that can be easily triggered by user + space. */ +#define INIT_TSS { \ + rsp1: (unsigned long)doublefault_stack, \ + STACKDESC \ +} + +extern char doublefault_stack[]; + +#define start_thread(regs,new_rip,new_rsp) do { \ + __asm__("movl %0,%%fs; movl %0,%%es; movl %0,%%ds": :"r" (0)); \ + wrmsrl(MSR_KERNEL_GS_BASE, 0); \ + (regs)->rip = (new_rip); \ + (regs)->rsp = (new_rsp); \ + write_pda(oldrsp, (new_rsp)); \ + (regs)->cs = __USER_CS; \ + (regs)->ss = __USER_DS; \ + (regs)->eflags = 0x200; \ + set_fs(USER_DS); \ +} while(0) + +struct task_struct; +struct mm_struct; + +/* Free all resources held by a thread. */ +extern void release_thread(struct task_struct *); +/* + * create a kernel thread without removing it from tasklists + */ +extern long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); + +/* Copy and release all segment info associated with a VM */ +extern void copy_segments(struct task_struct *p, struct mm_struct * mm); +extern void release_segments(struct mm_struct * mm); + +/* + * Return saved PC of a blocked thread. + */ +extern inline unsigned long thread_saved_pc(struct task_struct *t) +{ + return -1; /* FIXME */ +} + +unsigned long get_wchan(struct task_struct *p); + + +/* FIXME: this is incorrect when the task is sleeping in a syscall entered + through SYSCALL. */ +#define __kstk_regs(tsk) \ + ((struct pt_regs *)\ + (((char *)(tsk)->thread_info) + THREAD_SIZE - sizeof(struct pt_regs))) +#define KSTK_EIP(tsk) (__kstk_regs(tsk)->rip) +#define KSTK_ESP(tsk) (__kstk_regs(tsk)->rsp) + +/* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */ +extern inline void rep_nop(void) +{ + __asm__ __volatile__("rep;nop"); +} + +#define cpu_has_fpu 1 + +/* 3d now! prefetch instructions. Could also use the SSE flavours; not sure + if it makes a difference. gcc 3.1 has __builtin_prefetch too, but I am + not sure it makes sense to use them. */ + +#define ARCH_HAS_PREFETCH +#define ARCH_HAS_PREFETCHW +#define ARCH_HAS_SPINLOCK_PREFETCH + +extern inline void prefetch(const void *x) +{ + __asm__ __volatile__ ("prefetch (%0)" : : "r"(x)); +} + +extern inline void prefetchw(const void *x) +{ + __asm__ __volatile__ ("prefetchw (%0)" : : "r"(x)); +} +#define spin_lock_prefetch(x) prefetchw(x) +#define cpu_relax() rep_nop() + + +#endif /* __ASM_X86_64_PROCESSOR_H */ diff -Nru a/include/asm-x86_64/ptrace.h b/include/asm-x86_64/ptrace.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/ptrace.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,114 @@ +#ifndef _X86_64_PTRACE_H +#define _X86_64_PTRACE_H + +#ifdef __ASSEMBLY__ +#define R15 0 +#define R14 8 +#define R13 16 +#define R12 24 +#define RBP 36 +#define RBX 40 +/* arguments: interrupts/non tracing syscalls only save upto here*/ +#define R11 48 +#define R10 56 +#define R9 64 +#define R8 72 +#define RAX 80 +#define RCX 88 +#define RDX 96 +#define RSI 104 +#define RDI 112 +#define ORIG_RAX 120 /* = ERROR */ +/* end of arguments */ +/* cpu exception frame or undefined in case of fast syscall. */ +#define RIP 128 +#define CS 136 +#define EFLAGS 144 +#define RSP 152 +#define SS 160 +#define ARGOFFSET R11 +#endif /* __ASSEMBLY__ */ + +/* top of stack page */ +#define FRAME_SIZE 168 + +#define PTRACE_SETOPTIONS 21 + +/* options set using PTRACE_SETOPTIONS */ +#define PTRACE_O_TRACESYSGOOD 0x00000001 + +/* Dummy values for ptrace */ +#define FS 1000 +#define GS 1008 + +#ifndef __ASSEMBLY__ + +struct pt_regs { + unsigned long r15; + unsigned long r14; + unsigned long r13; + unsigned long r12; + unsigned long rbp; + unsigned long rbx; +/* arguments: non interrupts/non tracing syscalls only save upto here*/ + unsigned long r11; + unsigned long r10; + unsigned long r9; + unsigned long r8; + unsigned long rax; + unsigned long rcx; + unsigned long rdx; + unsigned long rsi; + unsigned long rdi; + unsigned long orig_rax; +/* end of arguments */ +/* cpu exception frame or undefined */ + unsigned long rip; + unsigned long cs; + unsigned long eflags; + unsigned long rsp; + unsigned long ss; +/* top of stack page */ +}; + +#endif + +/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ +#define PTRACE_GETREGS 12 +#define PTRACE_SETREGS 13 +#define PTRACE_GETFPREGS 14 +#define PTRACE_SETFPREGS 15 +#define PTRACE_GETFPXREGS 18 +#define PTRACE_SETFPXREGS 19 + +#if defined(__KERNEL__) && !defined(__ASSEMBLY__) +#define user_mode(regs) ((regs)->rsp <= PAGE_OFFSET) +#define instruction_pointer(regs) ((regs)->eip) +extern void show_regs(struct pt_regs *); + +enum { + EF_CF = 0x00000001, + EF_PF = 0x00000004, + EF_AF = 0x00000010, + EF_ZF = 0x00000040, + EF_SF = 0x00000080, + EF_TF = 0x00000100, + EF_IE = 0x00000200, + EF_DF = 0x00000400, + EF_OF = 0x00000800, + EF_IOPL = 0x00003000, + EF_IOPL_RING0 = 0x00000000, + EF_IOPL_RING1 = 0x00001000, + EF_IOPL_RING2 = 0x00002000, + EF_NT = 0x00004000, /* nested task */ + EF_RF = 0x00010000, /* resume */ + EF_VM = 0x00020000, /* virtual mode */ + EF_AC = 0x00040000, /* alignment */ + EF_VIF = 0x00080000, /* virtual interrupt */ + EF_VIP = 0x00100000, /* virtual interrupt pending */ + EF_ID = 0x00200000, /* id */ +}; + +#endif + +#endif diff -Nru a/include/asm-x86_64/resource.h b/include/asm-x86_64/resource.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/resource.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,47 @@ +#ifndef _X8664_RESOURCE_H +#define _X8664_RESOURCE_H + +/* + * Resource limits + */ + +#define RLIMIT_CPU 0 /* CPU time in ms */ +#define RLIMIT_FSIZE 1 /* Maximum filesize */ +#define RLIMIT_DATA 2 /* max data size */ +#define RLIMIT_STACK 3 /* max stack size */ +#define RLIMIT_CORE 4 /* max core file size */ +#define RLIMIT_RSS 5 /* max resident set size */ +#define RLIMIT_NPROC 6 /* max number of processes */ +#define RLIMIT_NOFILE 7 /* max number of open files */ +#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ +#define RLIMIT_AS 9 /* address space limit */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ + +#define RLIM_NLIMITS 11 + +/* + * SuS says limits have to be unsigned. + * Which makes a ton more sense anyway. + */ +#define RLIM_INFINITY (~0UL) + +#ifdef __KERNEL__ + +#define INIT_RLIMITS \ +{ \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { _STK_LIM, RLIM_INFINITY }, \ + { 0, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { 0, 0 }, \ + { INR_OPEN, INR_OPEN }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ +} + +#endif /* __KERNEL__ */ + +#endif diff -Nru a/include/asm-x86_64/rwlock.h b/include/asm-x86_64/rwlock.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/rwlock.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,84 @@ +/* include/asm-x86_64/rwlock.h + * + * Helpers used by both rw spinlocks and rw semaphores. + * + * Based in part on code from semaphore.h and + * spinlock.h Copyright 1996 Linus Torvalds. + * + * Copyright 1999 Red Hat, Inc. + * Copyright 2001 SuSE labs + * + * Written by Benjamin LaHaise. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _ASM_X86_64_RWLOCK_H +#define _ASM_X86_64_RWLOCK_H + +#define RW_LOCK_BIAS 0x01000000 +#define RW_LOCK_BIAS_STR "0x01000000" + +#define __build_read_lock_ptr(rw, helper) \ + asm volatile(LOCK "subl $1,(%0)\n\t" \ + "js 2f\n" \ + "1:\n" \ + ".section .text.lock,\"ax\"\n" \ + "2:\tcall " helper "\n\t" \ + "jmp 1b\n" \ + ".previous" \ + ::"d" (rw) : "memory") + +#define __build_read_lock_const(rw, helper) \ + asm volatile(LOCK "subl $1,%0\n\t" \ + "js 2f\n" \ + "1:\n" \ + ".section .text.lock,\"ax\"\n" \ + "2:\tpushq %%rax\n\t" \ + "leal %0,%%eax\n\t" \ + "call " helper "\n\t" \ + "popq %%rax\n\t" \ + "jmp 1b\n" \ + ".previous" \ + :"=m" (*((volatile int *)rw))::"memory") + +#define __build_read_lock(rw, helper) do { \ + if (__builtin_constant_p(rw)) \ + __build_read_lock_const(rw, helper); \ + else \ + __build_read_lock_ptr(rw, helper); \ + } while (0) + +#define __build_write_lock_ptr(rw, helper) \ + asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \ + "jnz 2f\n" \ + "1:\n" \ + ".section .text.lock,\"ax\"\n" \ + "2:\tcall " helper "\n\t" \ + "jmp 1b\n" \ + ".previous" \ + ::"d" (rw) : "memory") + +#define __build_write_lock_const(rw, helper) \ + asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \ + "jnz 2f\n" \ + "1:\n" \ + ".section .text.lock,\"ax\"\n" \ + "2:\tpushq %%rax\n\t" \ + "leaq %0,%%rax\n\t" \ + "call " helper "\n\t" \ + "popq %%rax\n\t" \ + "jmp 1b\n" \ + ".previous" \ + :"=m" (*((volatile long *)rw))::"memory") + +#define __build_write_lock(rw, helper) do { \ + if (__builtin_constant_p(rw)) \ + __build_write_lock_const(rw, helper); \ + else \ + __build_write_lock_ptr(rw, helper); \ + } while (0) + +#endif diff -Nru a/include/asm-x86_64/rwsem.h b/include/asm-x86_64/rwsem.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/rwsem.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,217 @@ +/* rwsem.h: R/W semaphores implemented using XADD/CMPXCHG for x86_64+ + * + * Written by David Howells (dhowells@redhat.com). + * Ported by Andi Kleen to x86-64. + * + * Derived from asm-i386/semaphore.h and asm-i386/rwsem.h + * + * + * The MSW of the count is the negated number of active writers and waiting + * lockers, and the LSW is the total number of active locks + * + * The lock count is initialized to 0 (no active and no waiting lockers). + * + * When a writer subtracts WRITE_BIAS, it'll get 0xffff0001 for the case of an + * uncontended lock. This can be determined because XADD returns the old value. + * Readers increment by 1 and see a positive value when uncontended, negative + * if there are writers (and maybe) readers waiting (in which case it goes to + * sleep). + * + * The value of WAITING_BIAS supports up to 32766 waiting processes. This can + * be extended to 65534 by manually checking the whole MSW rather than relying + * on the S flag. + * + * The value of ACTIVE_BIAS supports up to 65535 active processes. + * + * This should be totally fair - if anything is waiting, a process that wants a + * lock will go to the back of the queue. When the currently active lock is + * released, if there's a writer at the front of the queue, then that and only + * that will be woken up; if there's a bunch of consequtive readers at the + * front, then they'll all be woken up, but no other readers will be. + */ + +#ifndef _X8664_RWSEM_H +#define _X8664_RWSEM_H + +#ifndef _LINUX_RWSEM_H +#error please dont include asm/rwsem.h directly, use linux/rwsem.h instead +#endif + +#ifdef __KERNEL__ + +#include +#include + +struct rwsem_waiter; + +extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); + +/* + * the semaphore definition + */ +struct rw_semaphore { + signed long count; +#define RWSEM_UNLOCKED_VALUE 0x00000000 +#define RWSEM_ACTIVE_BIAS 0x00000001 +#define RWSEM_ACTIVE_MASK 0x0000ffff +#define RWSEM_WAITING_BIAS (-0x00010000) +#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS +#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) + spinlock_t wait_lock; + struct list_head wait_list; +#if RWSEM_DEBUG + int debug; +#endif +}; + +/* + * initialisation + */ +#if RWSEM_DEBUG +#define __RWSEM_DEBUG_INIT , 0 +#else +#define __RWSEM_DEBUG_INIT /* */ +#endif + +#define __RWSEM_INITIALIZER(name) \ +{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) \ + __RWSEM_DEBUG_INIT } + +#define DECLARE_RWSEM(name) \ + struct rw_semaphore name = __RWSEM_INITIALIZER(name) + +static inline void init_rwsem(struct rw_semaphore *sem) +{ + sem->count = RWSEM_UNLOCKED_VALUE; + spin_lock_init(&sem->wait_lock); + INIT_LIST_HEAD(&sem->wait_list); +#if RWSEM_DEBUG + sem->debug = 0; +#endif +} + +/* + * lock for reading + */ +static inline void __down_read(struct rw_semaphore *sem) +{ + __asm__ __volatile__( + "# beginning down_read\n\t" +LOCK_PREFIX " incl (%%rax)\n\t" /* adds 0x00000001, returns the old value */ + " js 2f\n\t" /* jump if we weren't granted the lock */ + "1:\n\t" + ".section .text.lock,\"ax\"\n" + "2:\n\t" + " call rwsem_down_read_failed_thunk\n\t" + " jmp 1b\n" + ".previous" + "# ending down_read\n\t" + : "+m"(sem->count) + : "a"(sem) + : "memory", "cc"); +} + +/* + * lock for writing + */ +static inline void __down_write(struct rw_semaphore *sem) +{ + int tmp; + + tmp = RWSEM_ACTIVE_WRITE_BIAS; + __asm__ __volatile__( + "# beginning down_write\n\t" +LOCK_PREFIX " xadd %0,(%%rax)\n\t" /* subtract 0x0000ffff, returns the old value */ + " testl %0,%0\n\t" /* was the count 0 before? */ + " jnz 2f\n\t" /* jump if we weren't granted the lock */ + "1:\n\t" + ".section .text.lock,\"ax\"\n" + "2:\n\t" + " call rwsem_down_write_failed_thunk\n\t" + " jmp 1b\n" + ".previous\n" + "# ending down_write" + : "=r" (tmp) + : "0"(tmp), "a"(sem) + : "memory", "cc"); +} + +/* + * unlock after reading + */ +static inline void __up_read(struct rw_semaphore *sem) +{ + __s32 tmp = -RWSEM_ACTIVE_READ_BIAS; + __asm__ __volatile__( + "# beginning __up_read\n\t" +LOCK_PREFIX " xadd %%edx,(%%rax)\n\t" /* subtracts 1, returns the old value */ + " js 2f\n\t" /* jump if the lock is being waited upon */ + "1:\n\t" + ".section .text.lock,\"ax\"\n" + "2:\n\t" + " decw %%dx\n\t" /* do nothing if still outstanding active readers */ + " jnz 1b\n\t" + " call rwsem_wake_thunk\n\t" + " jmp 1b\n" + ".previous\n" + "# ending __up_read\n" + : "+m"(sem->count), "+d"(tmp) + : "a"(sem) + : "memory", "cc"); +} + +/* + * unlock after writing + */ +static inline void __up_write(struct rw_semaphore *sem) +{ + __asm__ __volatile__( + "# beginning __up_write\n\t" + " movl %2,%%edx\n\t" +LOCK_PREFIX " xaddl %%edx,(%%rax)\n\t" /* tries to transition 0xffff0001 -> 0x00000000 */ + " jnz 2f\n\t" /* jump if the lock is being waited upon */ + "1:\n\t" + ".section .text.lock,\"ax\"\n" + "2:\n\t" + " decw %%dx\n\t" /* did the active count reduce to 0? */ + " jnz 1b\n\t" /* jump back if not */ + " call rwsem_wake_thunk\n\t" + " jmp 1b\n" + ".previous\n" + "# ending __up_write\n" + : "+m"(sem->count) + : "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS) + : "memory", "cc", "edx"); +} + +/* + * implement atomic add functionality + */ +static inline void rwsem_atomic_add(int delta, struct rw_semaphore *sem) +{ + __asm__ __volatile__( +LOCK_PREFIX "addl %1,%0" + :"=m"(sem->count) + :"ir"(delta), "m"(sem->count)); +} + +/* + * implement exchange and add functionality + */ +static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem) +{ + int tmp = delta; + + __asm__ __volatile__( +LOCK_PREFIX "xadd %0,(%2)" + : "=r"(tmp), "=m"(sem->count) + : "r"(sem), "m"(sem->count), "0" (tmp) + : "memory"); + + return tmp+delta; +} + +#endif /* __KERNEL__ */ +#endif /* _I386_RWSEM_H */ diff -Nru a/include/asm-x86_64/scatterlist.h b/include/asm-x86_64/scatterlist.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/scatterlist.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,13 @@ +#ifndef _X8664_SCATTERLIST_H +#define _X8664_SCATTERLIST_H + +struct scatterlist { + struct page *page; + unsigned int offset; + dma_addr_t dma_address; + unsigned int length; +}; + +#define ISA_DMA_THRESHOLD (0x00ffffff) + +#endif diff -Nru a/include/asm-x86_64/segment.h b/include/asm-x86_64/segment.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/segment.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,19 @@ +#ifndef _ASM_SEGMENT_H +#define _ASM_SEGMENT_H + +#define __KERNEL_CS 0x10 +#define __KERNEL_DS 0x18 + +/* + * we cannot use the same code segment descriptor for user and kernel + * even not in the long flat model, because of different DPL /kkeil + * The segment offset needs to contain a RPL. Grr. -AK + * GDT layout to get 64bit syscall right (sysret hardcodes gdt offsets) + */ + +#define __USER32_CS 0x23 /* 4*8+3 */ +#define __USER_DS 0x2b /* 5*8+3 */ +#define __USER_CS 0x33 /* 6*8+3 */ +#define __USER32_DS __USER_DS + +#endif diff -Nru a/include/asm-x86_64/semaphore.h b/include/asm-x86_64/semaphore.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/semaphore.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,216 @@ +#ifndef _X86_64_SEMAPHORE_H +#define _X86_64_SEMAPHORE_H + +#include + +#ifdef __KERNEL__ + +/* + * SMP- and interrupt-safe semaphores.. + * + * (C) Copyright 1996 Linus Torvalds + * + * Modified 1996-12-23 by Dave Grothe to fix bugs in + * the original code and to make semaphore waits + * interruptible so that processes waiting on + * semaphores can be killed. + * Modified 1999-02-14 by Andrea Arcangeli, split the sched.c helper + * functions in asm/sempahore-helper.h while fixing a + * potential and subtle race discovered by Ulrich Schmid + * in down_interruptible(). Since I started to play here I + * also implemented the `trylock' semaphore operation. + * 1999-07-02 Artur Skawina + * Optimized "0(ecx)" -> "(ecx)" (the assembler does not + * do this). Changed calling sequences from push/jmp to + * traditional call/ret. + * Modified 2001-01-01 Andreas Franck + * Some hacks to ensure compatibility with recent + * GCC snapshots, to avoid stack corruption when compiling + * with -fomit-frame-pointer. It's not sure if this will + * be fixed in GCC, as our previous implementation was a + * bit dubious. + * + * If you would like to see an analysis of this implementation, please + * ftp to gcom.com and download the file + * /pub/linux/src/semaphore/semaphore-2.0.24.tar.gz. + * + */ + +#include +#include +#include +#include +#include + +struct semaphore { + atomic_t count; + int sleepers; + wait_queue_head_t wait; +#if WAITQUEUE_DEBUG + long __magic; +#endif +}; + +#if WAITQUEUE_DEBUG +# define __SEM_DEBUG_INIT(name) \ + , (int)&(name).__magic +#else +# define __SEM_DEBUG_INIT(name) +#endif + +#define __SEMAPHORE_INITIALIZER(name,count) \ +{ ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ + __SEM_DEBUG_INIT(name) } + +#define __MUTEX_INITIALIZER(name) \ + __SEMAPHORE_INITIALIZER(name,1) + +#define __DECLARE_SEMAPHORE_GENERIC(name,count) \ + struct semaphore name = __SEMAPHORE_INITIALIZER(name,count) + +#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1) +#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0) + +static inline void sema_init (struct semaphore *sem, int val) +{ +/* + * *sem = (struct semaphore)__SEMAPHORE_INITIALIZER((*sem),val); + * + * i'd rather use the more flexible initialization above, but sadly + * GCC 2.7.2.3 emits a bogus warning. EGCS doesnt. Oh well. + */ + atomic_set(&sem->count, val); + sem->sleepers = 0; + init_waitqueue_head(&sem->wait); +#if WAITQUEUE_DEBUG + sem->__magic = (int)&sem->__magic; +#endif +} + +static inline void init_MUTEX (struct semaphore *sem) +{ + sema_init(sem, 1); +} + +static inline void init_MUTEX_LOCKED (struct semaphore *sem) +{ + sema_init(sem, 0); +} + +asmlinkage void __down_failed(void /* special register calling convention */); +asmlinkage int __down_failed_interruptible(void /* params in registers */); +asmlinkage int __down_failed_trylock(void /* params in registers */); +asmlinkage void __up_wakeup(void /* special register calling convention */); + +asmlinkage void __down(struct semaphore * sem); +asmlinkage int __down_interruptible(struct semaphore * sem); +asmlinkage int __down_trylock(struct semaphore * sem); +asmlinkage void __up(struct semaphore * sem); + +/* + * This is ugly, but we want the default case to fall through. + * "__down_failed" is a special asm handler that calls the C + * routine that actually waits. See arch/i386/kernel/semaphore.c + */ +static inline void down(struct semaphore * sem) +{ +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + __asm__ __volatile__( + "# atomic down operation\n\t" + LOCK "decl %0\n\t" /* --sem->count */ + "js 2f\n" + "1:\n" + ".section .text.lock,\"ax\"\n" + "2:\tcall __down_failed\n\t" + "jmp 1b\n" + ".previous" + :"=m" (sem->count) + :"D" (sem) + :"memory"); +} + +/* + * Interruptible try to acquire a semaphore. If we obtained + * it, return zero. If we were interrupted, returns -EINTR + */ +static inline int down_interruptible(struct semaphore * sem) +{ + int result; + +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + __asm__ __volatile__( + "# atomic interruptible down operation\n\t" + LOCK "decl %1\n\t" /* --sem->count */ + "js 2f\n\t" + "xorl %0,%0\n" + "1:\n" + ".section .text.lock,\"ax\"\n" + "2:\tcall __down_failed_interruptible\n\t" + "jmp 1b\n" + ".previous" + :"=a" (result), "=m" (sem->count) + :"D" (sem) + :"memory"); + return result; +} + +/* + * Non-blockingly attempt to down() a semaphore. + * Returns zero if we acquired it + */ +static inline int down_trylock(struct semaphore * sem) +{ + int result; + +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + __asm__ __volatile__( + "# atomic interruptible down operation\n\t" + LOCK "decl %1\n\t" /* --sem->count */ + "js 2f\n\t" + "xorl %0,%0\n" + "1:\n" + ".section .text.lock,\"ax\"\n" + "2:\tcall __down_failed_trylock\n\t" + "jmp 1b\n" + ".previous" + :"=a" (result), "=m" (sem->count) + :"D" (sem) + :"memory"); + return result; +} + +/* + * Note! This is subtle. We jump to wake people up only if + * the semaphore was negative (== somebody was waiting on it). + * The default case (no contention) will result in NO + * jumps for both down() and up(). + */ +static inline void up(struct semaphore * sem) +{ +#if WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + __asm__ __volatile__( + "# atomic up operation\n\t" + LOCK "incl %0\n\t" /* ++sem->count */ + "jle 2f\n" + "1:\n" + ".section .text.lock,\"ax\"\n" + "2:\tcall __up_wakeup\n\t" + "jmp 1b\n" + ".previous" + :"=m" (sem->count) + :"D" (sem) + :"memory"); +} +#endif /* __KERNEL__ */ +#endif diff -Nru a/include/asm-x86_64/sembuf.h b/include/asm-x86_64/sembuf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/sembuf.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,25 @@ +#ifndef _X86_64_SEMBUF_H +#define _X86_64_SEMBUF_H + +/* + * The semid64_ds structure for x86_64 architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 64-bit time_t to solve y2038 problem + * - 2 miscellaneous 32-bit values + */ + +struct semid64_ds { + struct ipc64_perm sem_perm; /* permissions .. see ipc.h */ + __kernel_time_t sem_otime; /* last semop time */ + unsigned long __unused1; + __kernel_time_t sem_ctime; /* last change time */ + unsigned long __unused2; + unsigned long sem_nsems; /* no. of semaphores in array */ + unsigned long __unused3; + unsigned long __unused4; +}; + +#endif /* _X86_64_SEMBUF_H */ diff -Nru a/include/asm-x86_64/serial.h b/include/asm-x86_64/serial.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/serial.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,133 @@ +/* + * include/asm-x86_64/serial.h + */ + +#include + +/* + * This assumes you have a 1.8432 MHz clock for your UART. + * + * It'd be nice if someone built a serial card with a 24.576 MHz + * clock, since the 16550A is capable of handling a top speed of 1.5 + * megabits/second; but this requires the faster clock. + */ +#define BASE_BAUD ( 1843200 / 16 ) + +/* Standard COM flags (except for COM4, because of the 8514 problem) */ +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) +#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF +#endif + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define FOURPORT_FLAGS ASYNC_FOURPORT +#define ACCENT_FLAGS 0 +#define BOCA_FLAGS 0 +#define HUB6_FLAGS 0 +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE +#endif + +#define MCA_COM_FLAGS (STD_COM_FLAGS|ASYNC_BOOT_ONLYMCA) + +/* + * The following define the access methods for the HUB6 card. All + * access is through two ports for all 24 possible chips. The card is + * selected through the high 2 bits, the port on that card with the + * "middle" 3 bits, and the register on that port with the bottom + * 3 bits. + * + * While the access port and interrupt is configurable, the default + * port locations are 0x302 for the port control register, and 0x303 + * for the data read/write register. Normally, the interrupt is at irq3 + * but can be anything from 3 to 7 inclusive. Note that using 3 will + * require disabling com2. + */ + +#define C_P(card,port) (((card)<<6|(port)<<3) + 1) + +#define STD_SERIAL_PORT_DEFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ \ + { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ \ + { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ + + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define EXTRA_SERIAL_PORT_DEFNS \ + { 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */ \ + { 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */ \ + { 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */ \ + { 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS }, /* ttyS7 */ \ + { 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS }, /* ttyS8 */ \ + { 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS }, /* ttyS9 */ \ + { 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */ \ + { 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */ \ + { 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */ \ + { 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */ \ + { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS14 (spare) */ \ + { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS15 (spare) */ \ + { 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */ \ + { 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */ \ + { 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS }, /* ttyS18 */ \ + { 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS }, /* ttyS19 */ \ + { 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS }, /* ttyS20 */ \ + { 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS }, /* ttyS21 */ \ + { 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS }, /* ttyS22 */ \ + { 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS }, /* ttyS23 */ \ + { 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS }, /* ttyS24 */ \ + { 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS }, /* ttyS25 */ \ + { 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS }, /* ttyS26 */ \ + { 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS }, /* ttyS27 */ \ + { 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS }, /* ttyS28 */ \ + { 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */ \ + { 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */ \ + { 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */ +#else +#define EXTRA_SERIAL_PORT_DEFNS +#endif + +/* You can have up to four HUB6's in the system, but I've only + * included two cards here for a total of twelve ports. + */ +#if (defined(CONFIG_HUB6) && defined(CONFIG_SERIAL_MANY_PORTS)) +#define HUB6_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS32 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS33 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS34 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS35 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS36 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS37 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS38 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS39 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS40 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS41 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */ +#else +#define HUB6_SERIAL_PORT_DFNS +#endif + +#ifdef CONFIG_MCA +#define MCA_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, 0x3220, 3, MCA_COM_FLAGS }, \ + { 0, BASE_BAUD, 0x3228, 3, MCA_COM_FLAGS }, \ + { 0, BASE_BAUD, 0x4220, 3, MCA_COM_FLAGS }, \ + { 0, BASE_BAUD, 0x4228, 3, MCA_COM_FLAGS }, \ + { 0, BASE_BAUD, 0x5220, 3, MCA_COM_FLAGS }, \ + { 0, BASE_BAUD, 0x5228, 3, MCA_COM_FLAGS }, +#else +#define MCA_SERIAL_PORT_DFNS +#endif + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DEFNS \ + EXTRA_SERIAL_PORT_DEFNS \ + HUB6_SERIAL_PORT_DFNS \ + MCA_SERIAL_PORT_DFNS + diff -Nru a/include/asm-x86_64/setup.h b/include/asm-x86_64/setup.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/setup.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,10 @@ +/* + * Just a place holder. We don't want to have to test x86 before + * we include stuff + */ + +#ifndef _x8664_SETUP_H +#define _x8664_SETUP_H + + +#endif diff -Nru a/include/asm-x86_64/shmbuf.h b/include/asm-x86_64/shmbuf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/shmbuf.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,38 @@ +#ifndef _X8664_SHMBUF_H +#define _X8664_SHMBUF_H + +/* + * The shmid64_ds structure for x8664 architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 2 miscellaneous 64-bit values + */ + +struct shmid64_ds { + struct ipc64_perm shm_perm; /* operation perms */ + size_t shm_segsz; /* size of segment (bytes) */ + __kernel_time_t shm_atime; /* last attach time */ + __kernel_time_t shm_dtime; /* last detach time */ + __kernel_time_t shm_ctime; /* last change time */ + __kernel_pid_t shm_cpid; /* pid of creator */ + __kernel_pid_t shm_lpid; /* pid of last operator */ + unsigned long shm_nattch; /* no. of current attaches */ + unsigned long __unused4; + unsigned long __unused5; +}; + +struct shminfo64 { + unsigned long shmmax; + unsigned long shmmin; + unsigned long shmmni; + unsigned long shmseg; + unsigned long shmall; + unsigned long __unused1; + unsigned long __unused2; + unsigned long __unused3; + unsigned long __unused4; +}; + +#endif diff -Nru a/include/asm-x86_64/shmparam.h b/include/asm-x86_64/shmparam.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/shmparam.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,6 @@ +#ifndef _ASMX8664_SHMPARAM_H +#define _ASMX8664_SHMPARAM_H + +#define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ + +#endif /* _ASMX8664_SHMPARAM_H */ diff -Nru a/include/asm-x86_64/sigcontext.h b/include/asm-x86_64/sigcontext.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/sigcontext.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,97 @@ +#ifndef _ASM_X86_64_SIGCONTEXT_H +#define _ASM_X86_64_SIGCONTEXT_H + +#include + +/* + * The first part of "struct _fpstate" is just the normal i387 + * hardware setup, the extra "status" word is used to save the + * coprocessor status word before entering the handler. + * + * Pentium III FXSR, SSE support + * Gareth Hughes , May 2000 + * + * The FPU state data structure has had to grow to accomodate the + * extended FPU state required by the Streaming SIMD Extensions. + * There is no documented standard to accomplish this at the moment. + */ +struct _fpreg { + unsigned short significand[4]; + unsigned short exponent; +}; + +struct _fpxreg { + unsigned short significand[4]; + unsigned short exponent; + unsigned short padding[3]; +}; + +struct _xmmreg { + __u32 element[4]; +}; + + +/* This is FXSAVE layout without 64bit prefix thus 32bit compatible. + This means that the IP and DPs are only 32bit and are not useful + in 64bit space. + If someone used them we would need to switch to 64bit FXSAVE. +*/ +struct _fpstate { + /* Regular FPU environment */ + __u32 cw; + __u32 sw; + __u32 tag; + __u32 ipoff; + __u32 cssel; + __u32 dataoff; + __u32 datasel; + struct _fpreg _st[8]; + unsigned short status; + unsigned short magic; /* 0xffff = regular FPU data only */ + + /* FXSR FPU environment */ + __u32 _fxsr_env[6]; + __u32 mxcsr; + __u32 reserved; + struct _fpxreg _fxsr_st[8]; + struct _xmmreg _xmm[8]; /* It's actually 16 */ + __u32 padding[56]; +}; + +#define X86_FXSR_MAGIC 0x0000 + +struct sigcontext { + unsigned short gs, __gsh; + unsigned short fs, __fsh; + unsigned short es, __esh; + unsigned short ds, __dsh; + unsigned long r8; + unsigned long r9; + unsigned long r10; + unsigned long r12; + unsigned long r13; + unsigned long r14; + unsigned long r15; + unsigned long rdi; + unsigned long rsi; + unsigned long rbp; + unsigned long rbx; + unsigned long rdx; + unsigned long rax; + unsigned long trapno; + unsigned long err; + unsigned long rip; + unsigned short cs, __csh; + unsigned int __pad0; + unsigned long eflags; + unsigned long rsp_at_signal; + struct _fpstate * fpstate; + unsigned long oldmask; + unsigned long cr2; + unsigned long r11; + unsigned long rcx; + unsigned long rsp; +}; + + +#endif diff -Nru a/include/asm-x86_64/siginfo.h b/include/asm-x86_64/siginfo.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/siginfo.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,232 @@ +#ifndef _X8664_SIGINFO_H +#define _X8664_SIGINFO_H + +#include + +typedef union sigval { + int sival_int; + void *sival_ptr; +} sigval_t; + +#define SI_MAX_SIZE 128 +#define SI_PAD_SIZE ((SI_MAX_SIZE/sizeof(int)) - 3) + +typedef struct siginfo { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[SI_PAD_SIZE]; + + /* kill() */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + unsigned int _timer1; + unsigned int _timer2; + } _timer; + + /* POSIX.1b signals */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + pid_t _pid; /* which child */ + uid_t _uid; /* sender's uid */ + int _status; /* exit code */ + clock_t _utime; + clock_t _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + void *_addr; /* faulting insn/memory ref. */ + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} siginfo_t; + +/* + * How these fields are to be accessed. + */ +#define si_pid _sifields._kill._pid +#define si_uid _sifields._kill._uid +#define si_status _sifields._sigchld._status +#define si_utime _sifields._sigchld._utime +#define si_stime _sifields._sigchld._stime +#define si_value _sifields._rt._sigval +#define si_int _sifields._rt._sigval.sival_int +#define si_ptr _sifields._rt._sigval.sival_ptr +#define si_addr _sifields._sigfault._addr +#define si_band _sifields._sigpoll._band +#define si_fd _sifields._sigpoll._fd + +#ifdef __KERNEL__ +#define __SI_MASK 0xffff0000 +#define __SI_KILL (0 << 16) +#define __SI_TIMER (1 << 16) +#define __SI_POLL (2 << 16) +#define __SI_FAULT (3 << 16) +#define __SI_CHLD (4 << 16) +#define __SI_RT (5 << 16) +#define __SI_CODE(T,N) ((T) << 16 | ((N) & 0xffff)) +#else +#define __SI_KILL 0 +#define __SI_TIMER 0 +#define __SI_POLL 0 +#define __SI_FAULT 0 +#define __SI_CHLD 0 +#define __SI_RT 0 +#define __SI_CODE(T,N) (N) +#endif + +/* + * si_code values + * Digital reserves positive values for kernel-generated signals. + * ... And Linux ignores that convention -AK. + */ +#define SI_USER 0 /* sent by kill, sigsend, raise */ +#define SI_KERNEL 0x80 /* sent by the kernel from somewhere */ +#define SI_QUEUE -1 /* sent by sigqueue */ +#define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */ +#define SI_MESGQ -3 /* sent by real time mesq state change */ +#define SI_ASYNCIO -4 /* sent by AIO completion */ +#define SI_SIGIO -5 /* sent by queued SIGIO */ +#define SI_TKILL -6 /* sent by tkill system call */ + +#define SI_FROMUSER(siptr) ((siptr)->si_code <= 0) +#define SI_FROMKERNEL(siptr) ((siptr)->si_code > 0) + +/* + * SIGILL si_codes + */ +#define ILL_ILLOPC (__SI_FAULT|1) /* illegal opcode */ +#define ILL_ILLOPN (__SI_FAULT|2) /* illegal operand */ +#define ILL_ILLADR (__SI_FAULT|3) /* illegal addressing mode */ +#define ILL_ILLTRP (__SI_FAULT|4) /* illegal trap */ +#define ILL_PRVOPC (__SI_FAULT|5) /* privileged opcode */ +#define ILL_PRVREG (__SI_FAULT|6) /* privileged register */ +#define ILL_COPROC (__SI_FAULT|7) /* coprocessor error */ +#define ILL_BADSTK (__SI_FAULT|8) /* internal stack error */ +#define NSIGILL 8 + +/* + * SIGFPE si_codes + */ +#define FPE_INTDIV (__SI_FAULT|1) /* integer divide by zero */ +#define FPE_INTOVF (__SI_FAULT|2) /* integer overflow */ +#define FPE_FLTDIV (__SI_FAULT|3) /* floating point divide by zero */ +#define FPE_FLTOVF (__SI_FAULT|4) /* floating point overflow */ +#define FPE_FLTUND (__SI_FAULT|5) /* floating point underflow */ +#define FPE_FLTRES (__SI_FAULT|6) /* floating point inexact result */ +#define FPE_FLTINV (__SI_FAULT|7) /* floating point invalid operation */ +#define FPE_FLTSUB (__SI_FAULT|8) /* subscript out of range */ +#define NSIGFPE 8 + +/* + * SIGSEGV si_codes + */ +#define SEGV_MAPERR (__SI_FAULT|1) /* address not mapped to object */ +#define SEGV_ACCERR (__SI_FAULT|2) /* invalid permissions for mapped object */ +#define NSIGSEGV 2 + +/* + * SIGBUS si_codes + */ +#define BUS_ADRALN (__SI_FAULT|1) /* invalid address alignment */ +#define BUS_ADRERR (__SI_FAULT|2) /* non-existant physical address */ +#define BUS_OBJERR (__SI_FAULT|3) /* object specific hardware error */ +#define NSIGBUS 3 + +/* + * SIGTRAP si_codes + */ +#define TRAP_BRKPT (__SI_FAULT|1) /* process breakpoint */ +#define TRAP_TRACE (__SI_FAULT|2) /* process trace trap */ +#define NSIGTRAP 2 + +/* + * SIGCHLD si_codes + */ +#define CLD_EXITED (__SI_CHLD|1) /* child has exited */ +#define CLD_KILLED (__SI_CHLD|2) /* child was killed */ +#define CLD_DUMPED (__SI_CHLD|3) /* child terminated abnormally */ +#define CLD_TRAPPED (__SI_CHLD|4) /* traced child has trapped */ +#define CLD_STOPPED (__SI_CHLD|5) /* child has stopped */ +#define CLD_CONTINUED (__SI_CHLD|6) /* stopped child has continued */ +#define NSIGCHLD 6 + +/* + * SIGPOLL si_codes + */ +#define POLL_IN (__SI_POLL|1) /* data input available */ +#define POLL_OUT (__SI_POLL|2) /* output buffers available */ +#define POLL_MSG (__SI_POLL|3) /* input message available */ +#define POLL_ERR (__SI_POLL|4) /* i/o error */ +#define POLL_PRI (__SI_POLL|5) /* high priority input available */ +#define POLL_HUP (__SI_POLL|6) /* device disconnected */ +#define NSIGPOLL 6 + +/* + * sigevent definitions + * + * It seems likely that SIGEV_THREAD will have to be handled from + * userspace, libpthread transmuting it to SIGEV_SIGNAL, which the + * thread manager then catches and does the appropriate nonsense. + * However, everything is written out here so as to not get lost. + */ +#define SIGEV_SIGNAL 0 /* notify via signal */ +#define SIGEV_NONE 1 /* other notification: meaningless */ +#define SIGEV_THREAD 2 /* deliver via thread creation */ + +#define SIGEV_MAX_SIZE 64 +#define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE/sizeof(int)) - 3) + +typedef struct sigevent { + sigval_t sigev_value; + int sigev_signo; + int sigev_notify; + union { + int _pad[SIGEV_PAD_SIZE]; + + struct { + void (*_function)(sigval_t); + void *_attribute; /* really pthread_attr_t */ + } _sigev_thread; + } _sigev_un; +} sigevent_t; + +#define sigev_notify_function _sigev_un._sigev_thread._function +#define sigev_notify_attributes _sigev_un._sigev_thread._attribute + +#ifdef __KERNEL__ +#include + +extern inline void copy_siginfo(siginfo_t *to, siginfo_t *from) +{ + if (from->si_code < 0) + memcpy(to, from, sizeof(siginfo_t)); + else + /* _sigchld is currently the largest know union member */ + memcpy(to, from, 3*sizeof(int) + sizeof(from->_sifields._sigchld)); +} + +extern int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from); + +#endif /* __KERNEL__ */ + +#endif diff -Nru a/include/asm-x86_64/signal.h b/include/asm-x86_64/signal.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/signal.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,205 @@ +#ifndef _ASMx8664_SIGNAL_H +#define _ASMx8664_SIGNAL_H + +#include +#include + +/* Avoid too many header ordering problems. */ +struct siginfo; + +#ifdef __KERNEL__ +/* Most things should be clean enough to redefine this at will, if care + is taken to make libc match. */ + +#define _NSIG 64 +#define _NSIG_BPW 64 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef unsigned long old_sigset_t; /* at least 32 bits */ + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + + +struct pt_regs; +asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); + + +#else +/* Here we must cater to libcs that poke about in kernel headers. */ + +#define NSIG 32 +typedef unsigned long sigset_t; + +#endif /* __KERNEL__ */ + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +/* +#define SIGLOST 29 +*/ +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +/* These should not be considered constants from userland. */ +#define SIGRTMIN 32 +#define SIGRTMAX (_NSIG-1) + +/* + * SA_FLAGS values: + * + * SA_ONSTACK indicates that a registered stack_t will be used. + * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the + * SA_RESTART flag to get restarting signals (which were the default long ago) + * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. + * SA_RESETHAND clears the handler when the signal is delivered. + * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. + * SA_NODEFER prevents the current signal from being masked in the handler. + * + * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single + * Unix names RESETHAND and NODEFER respectively. + */ +#define SA_NOCLDSTOP 0x00000001 +#define SA_NOCLDWAIT 0x00000002 /* not supported yet */ +#define SA_SIGINFO 0x00000004 +#define SA_ONSTACK 0x08000000 +#define SA_RESTART 0x10000000 +#define SA_NODEFER 0x40000000 +#define SA_RESETHAND 0x80000000 + +#define SA_NOMASK SA_NODEFER +#define SA_ONESHOT SA_RESETHAND +#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ + +#define SA_RESTORER 0x04000000 + +/* + * sigaltstack controls + */ +#define SS_ONSTACK 1 +#define SS_DISABLE 2 + +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 + +#ifdef __KERNEL__ + +/* + * These values of sa_flags are used only by the kernel as part of the + * irq handling routines. + * + * SA_INTERRUPT is also used by the irq handling routines. + * SA_SHIRQ is for shared interrupt support on PCI and EISA. + */ +#define SA_PROBE SA_ONESHOT +#define SA_SAMPLE_RANDOM SA_RESTART +#define SA_SHIRQ 0x04000000 +#endif + +#define SIG_BLOCK 0 /* for blocking signals */ +#define SIG_UNBLOCK 1 /* for unblocking signals */ +#define SIG_SETMASK 2 /* for setting the signal mask */ + +/* Type of a signal handler. */ +typedef void (*__sighandler_t)(int); + +#define SIG_DFL ((__sighandler_t)0) /* default signal handling */ +#define SIG_IGN ((__sighandler_t)1) /* ignore signal */ +#define SIG_ERR ((__sighandler_t)-1) /* error return from signal */ + +#ifdef __KERNEL__ +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; + void (*sa_restorer)(void); + sigset_t sa_mask; /* mask last for extensibility */ +}; + +struct k_sigaction { + struct sigaction sa; +}; +#endif /* __KERNEL__ */ + +typedef struct sigaltstack { + void *ss_sp; + int ss_flags; + size_t ss_size; +} stack_t; + +#ifdef __KERNEL__ +#include + +#undef __HAVE_ARCH_SIG_BITOPS +#if 0 + +extern __inline__ void sigaddset(sigset_t *set, int _sig) +{ + __asm__("btsq %1,%0" : "=m"(*set) : "Ir"(_sig - 1) : "cc"); +} + +extern __inline__ void sigdelset(sigset_t *set, int _sig) +{ + __asm__("btrq %1,%0" : "=m"(*set) : "Ir"(_sig - 1) : "cc"); +} + +extern __inline__ int __const_sigismember(sigset_t *set, int _sig) +{ + unsigned long sig = _sig - 1; + return 1 & (set->sig[sig / _NSIG_BPW] >> (sig & ~(_NSIG_BPW-1))); +} + +extern __inline__ int __gen_sigismember(sigset_t *set, int _sig) +{ + int ret; + __asm__("btq %2,%1\n\tsbbq %0,%0" + : "=r"(ret) : "m"(*set), "Ir"(_sig-1) : "cc"); + return ret; +} + +#define sigismember(set,sig) \ + (__builtin_constant_p(sig) ? \ + __const_sigismember((set),(sig)) : \ + __gen_sigismember((set),(sig))) + +#define sigmask(sig) (1UL << ((sig) - 1)) + +extern __inline__ int sigfindinword(unsigned long word) +{ + __asm__("bsfq %1,%0" : "=r"(word) : "rm"(word) : "cc"); + return word; +} +#endif +#endif /* __KERNEL__ */ + +#endif diff -Nru a/include/asm-x86_64/smp.h b/include/asm-x86_64/smp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/smp.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,98 @@ +#ifndef __ASM_SMP_H +#define __ASM_SMP_H + +/* + * We need the APIC definitions automatically as part of 'smp.h' + */ +#ifndef __ASSEMBLY__ +#include +#include +#include +#endif + +#ifdef CONFIG_X86_LOCAL_APIC +#ifndef __ASSEMBLY__ +#include +#include +#include +#ifdef CONFIG_X86_IO_APIC +#include +#endif +#include +#endif +#endif + +#ifdef CONFIG_SMP +#ifndef ASSEMBLY + +#include + +/* + * Private routines/data + */ + +extern void smp_alloc_memory(void); +extern unsigned long phys_cpu_present_map; +extern unsigned long cpu_online_map; +extern volatile unsigned long smp_invalidate_needed; +extern int pic_mode; +extern void smp_flush_tlb(void); +extern void smp_message_irq(int cpl, void *dev_id, struct pt_regs *regs); +extern void smp_send_reschedule(int cpu); +extern void smp_send_reschedule_all(void); +extern void smp_invalidate_rcv(void); /* Process an NMI */ +extern void (*mtrr_hook) (void); +extern void zap_low_mappings (void); + +/* + * On x86 all CPUs are mapped 1:1 to the APIC space. + * This simplifies scheduling and IPI sending and + * compresses data structures. + */ +extern inline int cpu_logical_map(int cpu) +{ + return cpu; +} +extern inline int cpu_number_map(int cpu) +{ + return cpu; +} + +/* + * Some lowlevel functions might want to know about + * the real APIC ID <-> CPU # mapping. + */ +extern volatile int x86_apicid_to_cpu[NR_CPUS]; +extern volatile int x86_cpu_to_apicid[NR_CPUS]; + +/* + * General functions that each host system must provide. + */ + +extern void smp_boot_cpus(void); +extern void smp_store_cpu_info(int id); /* Store per CPU info (like the initial udelay numbers */ + +/* + * This function is needed by all SMP systems. It must _always_ be valid + * from the initial startup. We map APIC_BASE very early in page_setup(), + * so this is correct in the x86 case. + */ + +#define smp_processor_id() read_pda(cpunumber) + +extern __inline int hard_smp_processor_id(void) +{ + /* we don't want to mark this access volatile - bad code generation */ + return GET_APIC_ID(*(unsigned long *)(APIC_BASE+APIC_ID)); +} + +#endif /* !ASSEMBLY */ + +#define NO_PROC_ID 0xFF /* No processor magic marker */ + + + +#endif +#define INT_DELIVERY_MODE 1 /* logical delivery */ +#define TARGET_CPUS 1 +#endif diff -Nru a/include/asm-x86_64/smplock.h b/include/asm-x86_64/smplock.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/smplock.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,95 @@ +/* + * + */ +#include +#include +#include +#include + +extern spinlock_t kernel_flag; + +#ifdef CONFIG_SMP +#define kernel_locked() spin_is_locked(&kernel_flag) +#define check_irq_holder(cpu) \ + if (global_irq_holder == (cpu)) \ + BUG(); +#else +#ifdef CONFIG_PREEMPT +#define kernel_locked() preempt_get_count() +#define global_irq_holder 0 +#define check_irq_holder(cpu) do {} while(0) +#else +#define kernel_locked() 1 +#define check_irq_holder(cpu) \ + if (global_irq_holder == (cpu)) \ + BUG(); +#endif +#endif + +/* + * Release global kernel lock and global interrupt lock + */ +#define release_kernel_lock(task, cpu) \ +do { \ + if (unlikely(task->lock_depth >= 0)) { \ + spin_unlock(&kernel_flag); \ + check_irq_holder(cpu); \ + } \ +} while (0) + +/* + * Re-acquire the kernel lock + */ +#define reacquire_kernel_lock(task) \ +do { \ + if (unlikely(task->lock_depth >= 0)) \ + spin_lock(&kernel_flag); \ +} while (0) + + +/* + * Getting the big kernel lock. + * + * This cannot happen asynchronously, + * so we only need to worry about other + * CPU's. + */ +extern __inline__ void lock_kernel(void) +{ +#ifdef CONFIG_PREEMPT + if (current->lock_depth == -1) + spin_lock(&kernel_flag); + ++current->lock_depth; +#else +#if 1 + if (!++current->lock_depth) + spin_lock(&kernel_flag); +#else + __asm__ __volatile__( + "incl %1\n\t" + "jne 9f" + spin_lock_string + "\n9:" + :"=m" (__dummy_lock(&kernel_flag)), + "=m" (current->lock_depth)); +#endif +#endif +} + +extern __inline__ void unlock_kernel(void) +{ + if (current->lock_depth < 0) + BUG(); +#if 1 + if (--current->lock_depth < 0) + spin_unlock(&kernel_flag); +#else + __asm__ __volatile__( + "decl %1\n\t" + "jns 9f\n\t" + spin_unlock_string + "\n9:" + :"=m" (__dummy_lock(&kernel_flag)), + "=m" (current->lock_depth)); +#endif +} diff -Nru a/include/asm-x86_64/socket.h b/include/asm-x86_64/socket.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/socket.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,64 @@ +#ifndef _ASM_SOCKET_H +#define _ASM_SOCKET_H + +#include + +/* For setsockoptions(2) */ +#define SOL_SOCKET 1 + +#define SO_DEBUG 1 +#define SO_REUSEADDR 2 +#define SO_TYPE 3 +#define SO_ERROR 4 +#define SO_DONTROUTE 5 +#define SO_BROADCAST 6 +#define SO_SNDBUF 7 +#define SO_RCVBUF 8 +#define SO_KEEPALIVE 9 +#define SO_OOBINLINE 10 +#define SO_NO_CHECK 11 +#define SO_PRIORITY 12 +#define SO_LINGER 13 +#define SO_BSDCOMPAT 14 +/* To add :#define SO_REUSEPORT 15 */ +#define SO_PASSCRED 16 +#define SO_PEERCRED 17 +#define SO_RCVLOWAT 18 +#define SO_SNDLOWAT 19 +#define SO_RCVTIMEO 20 +#define SO_SNDTIMEO 21 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define SO_SECURITY_AUTHENTICATION 22 +#define SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define SO_ATTACH_FILTER 26 +#define SO_DETACH_FILTER 27 + +#define SO_PEERNAME 28 +#define SO_TIMESTAMP 29 +#define SCM_TIMESTAMP SO_TIMESTAMP + +#define SO_ACCEPTCONN 30 + +/* Nasty libc5 fixup - bletch */ +#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) +/* Socket types. */ +#define SOCK_STREAM 1 /* stream (connection) socket */ +#define SOCK_DGRAM 2 /* datagram (conn.less) socket */ +#define SOCK_RAW 3 /* raw socket */ +#define SOCK_RDM 4 /* reliably-delivered message */ +#define SOCK_SEQPACKET 5 /* sequential packet socket */ +#define SOCK_PACKET 10 /* linux specific way of */ + /* getting packets at the dev */ + /* level. For writing rarp and */ + /* other similar things on the */ + /* user level. */ +#define SOCK_MAX (SOCK_PACKET+1) +#endif + +#endif /* _ASM_SOCKET_H */ diff -Nru a/include/asm-x86_64/socket32.h b/include/asm-x86_64/socket32.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/socket32.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,70 @@ +#ifndef SOCKET32_H +#define SOCKET32_H 1 + +/* XXX This really belongs in some header file... -DaveM */ +#define MAX_SOCK_ADDR 128 /* 108 for Unix domain - + 16 for IP, 16 for IPX, + 24 for IPv6, + about 80 for AX.25 */ + +extern struct socket *sockfd_lookup(int fd, int *err); + +/* XXX This as well... */ +extern __inline__ void sockfd_put(struct socket *sock) +{ + fput(sock->file); +} + +struct msghdr32 { + u32 msg_name; + int msg_namelen; + u32 msg_iov; + __kernel_size_t32 msg_iovlen; + u32 msg_control; + __kernel_size_t32 msg_controllen; + unsigned msg_flags; +}; + +struct cmsghdr32 { + __kernel_size_t32 cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +/* Bleech... */ +#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen)) +#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen)) + +#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) ) + +#define CMSG32_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32)))) +#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len)) +#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len)) + +#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \ + (struct cmsghdr32 *)(ctl) : \ + (struct cmsghdr32 *)NULL) +#define CMSG32_FIRSTHDR(msg) __CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) + +__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size, + struct cmsghdr32 *__cmsg, int __cmsg_len) +{ + struct cmsghdr32 * __ptr; + + __ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) + + CMSG32_ALIGN(__cmsg_len)); + if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size) + return NULL; + + return __ptr; +} + +__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg, + struct cmsghdr32 *__cmsg, + int __cmsg_len) +{ + return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen, + __cmsg, __cmsg_len); +} + +#endif diff -Nru a/include/asm-x86_64/sockios.h b/include/asm-x86_64/sockios.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/sockios.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,12 @@ +#ifndef __ARCH_X8664_SOCKIOS__ +#define __ARCH_X8664_SOCKIOS__ + +/* Socket-level I/O control calls. */ +#define FIOSETOWN 0x8901 +#define SIOCSPGRP 0x8902 +#define FIOGETOWN 0x8903 +#define SIOCGPGRP 0x8904 +#define SIOCATMARK 0x8905 +#define SIOCGSTAMP 0x8906 /* Get stamp */ + +#endif diff -Nru a/include/asm-x86_64/softirq.h b/include/asm-x86_64/softirq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/softirq.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,34 @@ +#ifndef __ASM_SOFTIRQ_H +#define __ASM_SOFTIRQ_H + +#include +#include +#include + +#define __cpu_bh_enable() do { \ + barrier(); sub_pda(__local_bh_count,1); preempt_enable(); } while (0) +#define cpu_bh_disable() do { \ + preempt_disable(); add_pda(__local_bh_count,1); barrier(); } while (0) + +#define local_bh_disable() cpu_bh_disable() +#define __local_bh_enable() __cpu_bh_enable() + +#define in_softirq() (read_pda(__local_bh_count) != 0) + +#define _local_bh_enable() do { \ + asm volatile( \ + "decl %%gs:%c1;" \ + "jnz 1f;" \ + "cmpl $0,%%gs:%c0;" \ + "jnz 2f;" \ + "1:;" \ + ".section .text.lock,\"ax\";" \ + "2: call do_softirq_thunk;" \ + "jmp 1b;" \ + ".previous" \ + :: "i" (pda___softirq_pending), "i" (pda___local_bh_count) : \ + "memory"); \ +} while (0) +#define local_bh_enable() do { _local_bh_enable(); preempt_enable(); } while(0) + +#endif /* __ASM_SOFTIRQ_H */ diff -Nru a/include/asm-x86_64/spinlock.h b/include/asm-x86_64/spinlock.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/spinlock.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,181 @@ +#ifndef __ASM_SPINLOCK_H +#define __ASM_SPINLOCK_H + +#include +#include +#include +#include + +extern int printk(const char * fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +/* It seems that people are forgetting to + * initialize their spinlocks properly, tsk tsk. + * Remember to turn this off in 2.4. -ben + */ +#if defined(CONFIG_DEBUG_SPINLOCK) +#define SPINLOCK_DEBUG 1 +#else +#define SPINLOCK_DEBUG 0 +#endif + +/* + * Your basic SMP spinlocks, allowing only a single CPU anywhere + */ + +typedef struct { + volatile unsigned int lock; +#if SPINLOCK_DEBUG + unsigned magic; +#endif +} spinlock_t; + +#define SPINLOCK_MAGIC 0xdead4ead + +#if SPINLOCK_DEBUG +#define SPINLOCK_MAGIC_INIT , SPINLOCK_MAGIC +#else +#define SPINLOCK_MAGIC_INIT /* */ +#endif + +#define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 SPINLOCK_MAGIC_INIT } + +#define spin_lock_init(x) do { *(x) = SPIN_LOCK_UNLOCKED; } while(0) + +/* + * Simple spin lock operations. There are two variants, one clears IRQ's + * on the local processor, one does not. + * + * We make no fairness assumptions. They have a cost. + */ + +#define spin_is_locked(x) (*(volatile char *)(&(x)->lock) <= 0) +#define spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x)) + +#define spin_lock_string \ + "\n1:\t" \ + "lock ; decb %0\n\t" \ + "js 2f\n" \ + ".section .text.lock,\"ax\"\n" \ + "2:\t" \ + "cmpb $0,%0\n\t" \ + "rep;nop\n\t" \ + "jle 2b\n\t" \ + "jmp 1b\n" \ + ".previous" + +/* + * This works. Despite all the confusion. + */ +#define spin_unlock_string \ + "movb $1,%0" + +static inline int _raw_spin_trylock(spinlock_t *lock) +{ + char oldval; + __asm__ __volatile__( + "xchgb %b0,%1" + :"=q" (oldval), "=m" (lock->lock) + :"0" (0) : "memory"); + return oldval > 0; +} + +static inline void _raw_spin_lock(spinlock_t *lock) +{ +#if SPINLOCK_DEBUG + __label__ here; +here: + if (lock->magic != SPINLOCK_MAGIC) { +printk("eip: %p\n", &&here); + BUG(); + } +#endif + __asm__ __volatile__( + spin_lock_string + :"=m" (lock->lock) : : "memory"); +} + +static inline void _raw_spin_unlock(spinlock_t *lock) +{ +#if SPINLOCK_DEBUG + if (lock->magic != SPINLOCK_MAGIC) + BUG(); + if (!spin_is_locked(lock)) + BUG(); +#endif + __asm__ __volatile__( + spin_unlock_string + :"=m" (lock->lock) : : "memory"); +} + +/* + * Read-write spinlocks, allowing multiple readers + * but only one writer. + * + * NOTE! it is quite common to have readers in interrupts + * but no interrupt writers. For those circumstances we + * can "mix" irq-safe locks - any writer needs to get a + * irq-safe write-lock, but readers can get non-irqsafe + * read-locks. + */ +typedef struct { + volatile unsigned int lock; +#if SPINLOCK_DEBUG + unsigned magic; +#endif +} rwlock_t; + +#define RWLOCK_MAGIC 0xdeaf1eed + +#if SPINLOCK_DEBUG +#define RWLOCK_MAGIC_INIT , RWLOCK_MAGIC +#else +#define RWLOCK_MAGIC_INIT /* */ +#endif + +#define RW_LOCK_UNLOCKED (rwlock_t) { RW_LOCK_BIAS RWLOCK_MAGIC_INIT } + +#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) + +/* + * On x86, we implement read-write locks as a 32-bit counter + * with the high bit (sign) being the "contended" bit. + * + * The inline assembly is non-obvious. Think about it. + * + * Changed to use the same technique as rw semaphores. See + * semaphore.h for details. -ben + */ +/* the spinlock helpers are in arch/x86_64/kernel/semaphore.S */ + +extern inline void _raw_read_lock(rwlock_t *rw) +{ +#if SPINLOCK_DEBUG + if (rw->magic != RWLOCK_MAGIC) + BUG(); +#endif + __build_read_lock(rw, "__read_lock_failed"); +} + +static inline void _raw_write_lock(rwlock_t *rw) +{ +#if SPINLOCK_DEBUG + if (rw->magic != RWLOCK_MAGIC) + BUG(); +#endif + __build_write_lock(rw, "__write_lock_failed"); +} + +#define _raw_read_unlock(rw) asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory") +#define _raw_write_unlock(rw) asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory") + +static inline int _raw_write_trylock(rwlock_t *lock) +{ + atomic_t *count = (atomic_t *)lock; + if (atomic_sub_and_test(RW_LOCK_BIAS, count)) + return 1; + atomic_add(RW_LOCK_BIAS, count); + return 0; +} + +#endif /* __ASM_SPINLOCK_H */ diff -Nru a/include/asm-x86_64/stat.h b/include/asm-x86_64/stat.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/stat.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,27 @@ +#ifndef _ASM_X86_64_STAT_H +#define _ASM_X86_64_STAT_H + +struct stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned long st_nlink; + + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned int __pad0; + unsigned long st_rdev; + long st_size; + long st_blksize; + long st_blocks; /* Number 512-byte blocks allocated. */ + + unsigned long st_atime; + unsigned long __reserved0; /* reserved for atime.nanoseconds */ + unsigned long st_mtime; + unsigned long __reserved1; /* reserved for atime.nanoseconds */ + unsigned long st_ctime; + unsigned long __reserved2; /* reserved for atime.nanoseconds */ + long __unused[3]; +}; + +#endif diff -Nru a/include/asm-x86_64/statfs.h b/include/asm-x86_64/statfs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/statfs.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,25 @@ +#ifndef _X86_64_STATFS_H +#define _X86_64_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]; +}; + +#endif diff -Nru a/include/asm-x86_64/string.h b/include/asm-x86_64/string.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/string.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,38 @@ +#ifndef _X86_64_STRING_H_ +#define _X86_64_STRING_H_ + +#ifdef __KERNEL__ +#include + +#define struct_cpy(x,y) (*(x)=*(y)) + +#define __HAVE_ARCH_MEMCMP +#define __HAVE_ARCH_STRLEN + +#define memset __builtin_memset +#define memcpy __builtin_memcpy +#define memcmp __builtin_memcmp + +/* Work around "undefined reference to strlen" linker errors. */ +/* #define strlen __builtin_strlen */ + +#define __HAVE_ARCH_STRLEN +static inline size_t strlen(const char * s) +{ +int d0; +register int __res; +__asm__ __volatile__( + "repne\n\t" + "scasb\n\t" + "notl %0\n\t" + "decl %0" + :"=c" (__res), "=&D" (d0) :"1" (s),"a" (0), "0" (0xffffffff)); +return __res; +} + + +extern char *strstr(const char *cs, const char *ct); + +#endif /* __KERNEL__ */ + +#endif diff -Nru a/include/asm-x86_64/system.h b/include/asm-x86_64/system.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/system.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,283 @@ +#ifndef __ASM_SYSTEM_H +#define __ASM_SYSTEM_H + +#include +#include +#include + +#ifdef __KERNEL__ + +#ifdef CONFIG_SMP +#define LOCK_PREFIX "lock ; " +#else +#define LOCK_PREFIX "" +#endif + +struct task_struct; /* one of the stranger aspects of C forward declarations.. */ +extern void __switch_to(struct task_struct *prev, struct task_struct *next); + +#define prepare_to_switch() do { } while(0) + +#define switch_to(prev,next,last) do { \ + asm volatile("pushq %%rbp\n\t" \ + "pushq %%rbx\n\t" \ + "pushq %%r8\n\t" \ + "pushq %%r9\n\t" \ + "pushq %%r10\n\t" \ + "pushq %%r11\n\t" \ + "pushq %%r12\n\t" \ + "pushq %%r13\n\t" \ + "pushq %%r14\n\t" \ + "pushq %%r15\n\t" \ + "movq %%rsp,%0\n\t" /* save RSP */ \ + "movq %3,%%rsp\n\t" /* restore RSP */ \ + "leaq 1f(%%rip),%%rbp\n\t" \ + "movq %%rbp,%1\n\t" /* save RIP */ \ + "pushq %4\n\t" /* setup new RIP */ \ + "jmp __switch_to\n\t" \ + "1:\t" \ + "popq %%r15\n\t" \ + "popq %%r14\n\t" \ + "popq %%r13\n\t" \ + "popq %%r12\n\t" \ + "popq %%r11\n\t" \ + "popq %%r10\n\t" \ + "popq %%r9\n\t" \ + "popq %%r8\n\t" \ + "popq %%rbx\n\t" \ + "popq %%rbp\n\t" \ + :"=m" (prev->thread.rsp),"=m" (prev->thread.rip), \ + "=b" (last) \ + :"m" (next->thread.rsp),"m" (next->thread.rip), \ + "b" (prev), "S" (next), "D" (prev)); \ +} while (0) + +/* + * Load a segment. Fall back on loading the zero + * segment if something goes wrong.. + */ +#define loadsegment(seg,value) do { int v = value; \ + asm volatile("\n" \ + "1:\t" \ + "movl %0,%%" #seg "\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3:\t" \ + "pushq $0 ; popq %% " #seg "\n\t" \ + "jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n\t" \ + ".align 4\n\t" \ + ".quad 1b,3b\n" \ + ".previous" \ + : :"r" (v)); } while(0) + +#define set_debug(value,register) \ + __asm__("movq %0,%%db" #register \ + : /* no output */ \ + :"r" ((unsigned long) value)) + + +/* + * Clear and set 'TS' bit respectively + */ +#define clts() __asm__ __volatile__ ("clts") +#define read_cr0() ({ \ + unsigned long __dummy; \ + __asm__( \ + "movq %%cr0,%0\n\t" \ + :"=r" (__dummy)); \ + __dummy; \ +}) +#define write_cr0(x) \ + __asm__("movq %0,%%cr0": :"r" (x)); + +#define read_cr4() ({ \ + unsigned long __dummy; \ + __asm__( \ + "movq %%cr4,%0\n\t" \ + :"=r" (__dummy)); \ + __dummy; \ +}) +#define write_cr4(x) \ + __asm__("movq %0,%%cr4": :"r" (x)); +#define stts() write_cr0(8 | read_cr0()) + +#define wbinvd() \ + __asm__ __volatile__ ("wbinvd": : :"memory"); + +#endif /* __KERNEL__ */ + +#define nop() __asm__ __volatile__ ("nop") + +#define xchg(ptr,v) ((__typeof__(*(ptr)))__xchg((unsigned long)(v),(ptr),sizeof(*(ptr)))) + +#define tas(ptr) (xchg((ptr),1)) + +#define __xg(x) ((volatile long *)(x)) + +extern inline void set_64bit(volatile unsigned long *ptr, unsigned long val) +{ + *ptr = val; +} + +#define _set_64bit set_64bit + +/* + * Note: no "lock" prefix even on SMP: xchg always implies lock anyway + * Note 2: xchg has side effect, so that attribute volatile is necessary, + * but generally the primitive is invalid, *ptr is output argument. --ANK + */ +static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) +{ + switch (size) { + case 1: + __asm__ __volatile__("xchgb %b0,%1" + :"=q" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + case 2: + __asm__ __volatile__("xchgw %w0,%1" + :"=r" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + case 4: + __asm__ __volatile__("xchgl %k0,%1" + :"=r" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + case 8: + __asm__ __volatile__("xchgq %0,%1" + :"=r" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + } + return x; +} + +/* + * Atomic compare and exchange. Compare OLD with MEM, if identical, + * store NEW in MEM. Return the initial value in MEM. Success is + * indicated by comparing RETURN with OLD. + */ + +#define __HAVE_ARCH_CMPXCHG 1 + +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + unsigned long new, int size) +{ + unsigned long prev; + switch (size) { + case 1: + __asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + case 2: + __asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + case 4: + __asm__ __volatile__(LOCK_PREFIX "cmpxchgl %k1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + case 8: + __asm__ __volatile__(LOCK_PREFIX "cmpxchgq %1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + } + return old; +} + +#define cmpxchg(ptr,o,n)\ + ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ + (unsigned long)(n),sizeof(*(ptr)))) + + +#ifdef CONFIG_SMP +#define smp_mb() mb() +#define smp_rmb() rmb() +#define smp_wmb() wmb() +#else +#define smp_mb() barrier() +#define smp_rmb() barrier() +#define smp_wmb() barrier() +#endif + + +/* + * Force strict CPU ordering. + * And yes, this is required on UP too when we're talking + * to devices. + * + * For now, "wmb()" doesn't actually do anything, as all + * Intel CPU's follow what Intel calls a *Processor Order*, + * in which all writes are seen in the program order even + * outside the CPU. + * + * I expect future Intel CPU's to have a weaker ordering, + * but I'd also expect them to finally get their act together + * and add some real memory barriers if so. + */ +#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%rsp)": : :"memory") +#define rmb() mb() +#define wmb() __asm__ __volatile__ ("": : :"memory") +#define set_mb(var, value) do { xchg(&var, value); } while (0) +#define set_wmb(var, value) do { var = value; wmb(); } while (0) + +/* interrupt control.. */ +#define __save_flags(x) __asm__ __volatile__("# save_flags \n\t pushfq ; popq %q0":"=g" (x): /* no input */ :"memory") +#define __restore_flags(x) __asm__ __volatile__("# restore_flags \n\t pushq %0 ; popfq": /* no output */ :"g" (x):"memory", "cc") +#define __cli() __asm__ __volatile__("cli": : :"memory") +#define __sti() __asm__ __volatile__("sti": : :"memory") +/* used in the idle loop; sti takes one instruction cycle to complete */ +#define safe_halt() __asm__ __volatile__("sti; hlt": : :"memory") + +/* For spinlocks etc */ +#define local_irq_save(x) __asm__ __volatile__("# local_irq_save \n\t pushfq ; popq %0 ; cli":"=g" (x): /* no input */ :"memory") +#define local_irq_restore(x) __asm__ __volatile__("# local_irq_restore \n\t pushq %0 ; popfq": /* no output */ :"g" (x):"memory") +#define local_irq_disable() __asm__ __volatile__("cli": : :"memory") +#define local_irq_enable() __asm__ __volatile__("sti": : :"memory") + +#ifdef CONFIG_SMP + +extern void __global_cli(void); +extern void __global_sti(void); +extern unsigned long __global_save_flags(void); +extern void __global_restore_flags(unsigned long); +#define cli() __global_cli() +#define sti() __global_sti() +#define save_flags(x) ((x)=__global_save_flags()) +#define restore_flags(x) __global_restore_flags(x) + +#else + +#define cli() __cli() +#define sti() __sti() +#define save_flags(x) __save_flags(x) +#define restore_flags(x) __restore_flags(x) + +#endif + +#define icebp() asm volatile("xchg %bx,%bx") + + +/* + * disable hlt during certain critical i/o operations + */ +#define HAVE_DISABLE_HLT +void disable_hlt(void); +void enable_hlt(void); + +#endif diff -Nru a/include/asm-x86_64/termbits.h b/include/asm-x86_64/termbits.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/termbits.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,172 @@ +#ifndef __ARCH_X8664_TERMBITS_H__ +#define __ARCH_X8664_TERMBITS_H__ + +#include + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +#define NCCS 19 +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ +}; + +/* c_cc characters */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +/* c_iflag bits */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 + +/* c_oflag bits */ +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define XTABS 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0100000 +#define FF0 0000000 +#define FF1 0100000 + +/* c_cflag bit meaning */ +#define CBAUD 0010017 +#define B0 0000000 /* hang up */ +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 +#define EXTA B19200 +#define EXTB B38400 +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 +#define CBAUDEX 0010000 +#define B57600 0010001 +#define B115200 0010002 +#define B230400 0010003 +#define B460800 0010004 +#define B500000 0010005 +#define B576000 0010006 +#define B921600 0010007 +#define B1000000 0010010 +#define B1152000 0010011 +#define B1500000 0010012 +#define B2000000 0010013 +#define B2500000 0010014 +#define B3000000 0010015 +#define B3500000 0010016 +#define B4000000 0010017 +#define CIBAUD 002003600000 /* input baud rate (not used) */ +#define CMSPAR 010000000000 /* mark or space (stick) parity */ +#define CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 + +/* tcflow() and TCXONC use these */ +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* tcflush() and TCFLSH use these */ +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* tcsetattr uses these */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#endif diff -Nru a/include/asm-x86_64/termios.h b/include/asm-x86_64/termios.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/termios.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,106 @@ +#ifndef _X8664_TERMIOS_H +#define _X8664_TERMIOS_H + +#include +#include + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +#define NCC 8 +struct termio { + unsigned short c_iflag; /* input mode flags */ + unsigned short c_oflag; /* output mode flags */ + unsigned short c_cflag; /* control mode flags */ + unsigned short c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control characters */ +}; + +/* modem lines */ +#define TIOCM_LE 0x001 +#define TIOCM_DTR 0x002 +#define TIOCM_RTS 0x004 +#define TIOCM_ST 0x008 +#define TIOCM_SR 0x010 +#define TIOCM_CTS 0x020 +#define TIOCM_CAR 0x040 +#define TIOCM_RNG 0x080 +#define TIOCM_DSR 0x100 +#define TIOCM_CD TIOCM_CAR +#define TIOCM_RI TIOCM_RNG +#define TIOCM_OUT1 0x2000 +#define TIOCM_OUT2 0x4000 +#define TIOCM_LOOP 0x8000 + +/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ + +/* line disciplines */ +#define N_TTY 0 +#define N_SLIP 1 +#define N_MOUSE 2 +#define N_PPP 3 +#define N_STRIP 4 +#define N_AX25 5 +#define N_X25 6 /* X.25 async */ +#define N_6PACK 7 +#define N_MASC 8 /* Reserved for Mobitex module */ +#define N_R3964 9 /* Reserved for Simatic R3964 module */ +#define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ +#define N_IRDA 11 /* Linux IR - http://irda.sourceforge.net/ */ +#define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ +#define N_SYNC_PPP 14 /* synchronous PPP */ +#define N_HCI 15 /* Bluetooth HCI UART */ + +#ifdef __KERNEL__ + +/* intr=^C quit=^\ erase=del kill=^U + eof=^D vtime=\0 vmin=\1 sxtc=\0 + start=^Q stop=^S susp=^Z eol=\0 + reprint=^R discard=^U werase=^W lnext=^V + eol2=\0 +*/ +#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" + +/* + * Translate a "termio" structure into a "termios". Ugh. + */ +#define SET_LOW_TERMIOS_BITS(termios, termio, x) { \ + unsigned short __tmp; \ + get_user(__tmp,&(termio)->x); \ + *(unsigned short *) &(termios)->x = __tmp; \ +} + +#define user_termio_to_kernel_termios(termios, termio) \ +({ \ + SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); \ + copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \ +}) + +/* + * Translate a "termios" structure into a "termio". Ugh. + */ +#define kernel_termios_to_user_termio(termio, termios) \ +({ \ + put_user((termios)->c_iflag, &(termio)->c_iflag); \ + put_user((termios)->c_oflag, &(termio)->c_oflag); \ + put_user((termios)->c_cflag, &(termio)->c_cflag); \ + put_user((termios)->c_lflag, &(termio)->c_lflag); \ + put_user((termios)->c_line, &(termio)->c_line); \ + copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \ +}) + +#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios)) +#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios)) + +#endif /* __KERNEL__ */ + +#endif /* _X8664_TERMIOS_H */ diff -Nru a/include/asm-x86_64/thread_info.h b/include/asm-x86_64/thread_info.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/thread_info.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,116 @@ +/* thread_info.h: x86_64 low-level thread information + * + * Copyright (C) 2002 David Howells (dhowells@redhat.com) + * - Incorporating suggestions made by Linus Torvalds and Dave Miller + */ + +#ifndef _ASM_THREAD_INFO_H +#define _ASM_THREAD_INFO_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ +#include +#include +#include +#endif + +/* + * low level task data that entry.S needs immediate access to + * - this struct should fit entirely inside of one cache line + * - this struct shares the supervisor stack pages + */ +#ifndef __ASSEMBLY__ +struct thread_info { + struct task_struct *task; /* main task structure */ + struct exec_domain *exec_domain; /* execution domain */ + __u32 flags; /* low level flags */ + __u32 cpu; /* current CPU */ + int preempt_count; + + mm_segment_t addr_limit; +}; + +#endif + +/* + * macros/functions for gaining access to the thread information structure + */ +#ifndef __ASSEMBLY__ +#define INIT_THREAD_INFO(tsk) \ +{ \ + task: &tsk, \ + exec_domain: &default_exec_domain, \ + flags: 0, \ + cpu: 0, \ + addr_limit: KERNEL_DS, \ +} + +#define init_thread_info (init_thread_union.thread_info) +#define init_stack (init_thread_union.stack) + +/* how to get the thread information struct from C */ + +#ifdef CONFIG_PREEMPT +/* Preemptive kernels need to access this from interrupt context too. */ +static inline struct thread_info *current_thread_info(void) +{ + struct thread_info *ti; + ti = (void *)read_pda(kernelstack) + PDA_STACKOFFSET - THREAD_SIZE; + return ti; +} +#else +/* On others go for a minimally cheaper way. */ +static inline struct thread_info *current_thread_info(void) +{ + struct thread_info *ti; + __asm__("andq %%rsp,%0; ":"=r" (ti) : "0" (~8191UL)); + return ti; +} +#endif + +/* thread information allocation */ +#define THREAD_SIZE (2*PAGE_SIZE) +#define alloc_thread_info() ((struct thread_info *) __get_free_pages(GFP_KERNEL,1)) +#define free_thread_info(ti) free_pages((unsigned long) (ti), 1) +#define get_thread_info(ti) get_task_struct((ti)->task) +#define put_thread_info(ti) put_task_struct((ti)->task) + +#else /* !__ASSEMBLY__ */ + +/* how to get the thread information struct from ASM */ +#define GET_THREAD_INFO(reg) \ + movq $-8192, reg; \ + andq %rsp, reg + +#endif + +/* + * thread information flags + * - these are process state flags that various assembly files may need to access + * - pending work-to-be-done flags are in LSW + * - other flags in MSW + * Warning: layout of LSW is hardcoded in entry.S + */ +#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ +#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ +#define TIF_SIGPENDING 2 /* signal pending */ +#define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */ +#define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */ +#define TIF_IA32 18 /* 32bit process */ + +#define _TIF_SYSCALL_TRACE (1< +#include +#include + +#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ +#define CLOCK_TICK_FACTOR 20 /* Factor of both 1000000 and CLOCK_TICK_RATE */ +#define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \ + (1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \ + << (SHIFT_SCALE-SHIFT_HZ)) / HZ) + +/* + * Standard way to access the cycle counter on i586+ CPUs. + * Currently only used on SMP. + * + * If you really have a SMP machine with i486 chips or older, + * compile for that, and this will just always return zero. + * That's ok, it just means that the nicer scheduling heuristics + * won't work for you. + * + * We only use the low 32 bits, and we'd simply better make sure + * that we reschedule before that wraps. Scheduling at least every + * four billion cycles just basically sounds like a good idea, + * regardless of how fast the machine is. + */ +typedef unsigned long long cycles_t; + +extern cycles_t cacheflush_time; + +static inline cycles_t get_cycles (void) +{ +#ifndef CONFIG_X86_TSC + return 0; +#else + unsigned long long ret; + + rdtscll(ret); + return ret; +#endif +} + +extern unsigned long cpu_khz; + +#endif diff -Nru a/include/asm-x86_64/tlb.h b/include/asm-x86_64/tlb.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/tlb.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1 @@ +#include diff -Nru a/include/asm-x86_64/types.h b/include/asm-x86_64/types.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/types.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,47 @@ +#ifndef _X86_64_TYPES_H +#define _X86_64_TYPES_H + +typedef unsigned short umode_t; + +/* + * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the + * header files exported to user space + */ + +typedef __signed__ char __s8; +typedef unsigned char __u8; + +typedef __signed__ short __s16; +typedef unsigned short __u16; + +typedef __signed__ int __s32; +typedef unsigned int __u32; + +typedef __signed__ long __s64; +typedef unsigned long __u64; + +/* + * These aren't exported outside the kernel to avoid name space clashes + */ +#ifdef __KERNEL__ + +typedef signed char s8; +typedef unsigned char u8; + +typedef signed short s16; +typedef unsigned short u16; + +typedef signed int s32; +typedef unsigned int u32; + +typedef signed long s64; +typedef unsigned long u64; + +#define BITS_PER_LONG 64 + +typedef u32 dma64_addr_t; +typedef u64 dma_addr_t; + +#endif /* __KERNEL__ */ + +#endif diff -Nru a/include/asm-x86_64/uaccess.h b/include/asm-x86_64/uaccess.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/uaccess.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,373 @@ +#ifndef __X86_64_UACCESS_H +#define __X86_64_UACCESS_H + +/* + * User space memory access functions + */ +#include +#include +#include +#include +#include + +#define VERIFY_READ 0 +#define VERIFY_WRITE 1 + +/* + * The fs value determines whether argument validity checking should be + * performed or not. If get_fs() == USER_DS, checking is performed, with + * get_fs() == KERNEL_DS, checking is bypassed. + * + * For historical reasons, these macros are grossly misnamed. + */ + +#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) + +#define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFFFFFFFFFF) +#define USER_DS MAKE_MM_SEG(PAGE_OFFSET) + +#define get_ds() (KERNEL_DS) +#define get_fs() (current_thread_info()->addr_limit) +#define set_fs(x) (current_thread_info()->addr_limit = (x)) + +#define segment_eq(a,b) ((a).seg == (b).seg) + +#define __addr_ok(addr) (!((unsigned long)(addr) & (current_thread_info()->addr_limit.seg))) + +/* + * Uhhuh, this needs 65-bit arithmetic. We have a carry.. + */ +#define __range_not_ok(addr,size) ({ \ + unsigned long flag,sum; \ + asm("# range_ok\n\r" \ + "addq %3,%1 ; sbbq %0,%0 ; cmpq %1,%4 ; sbbq $0,%0" \ + :"=&r" (flag), "=r" (sum) \ + :"1" (addr),"g" ((long)(size)),"g" (current_thread_info()->addr_limit.seg)); \ + flag; }) + +#define access_ok(type,addr,size) (__range_not_ok(addr,size) == 0) + +extern inline int verify_area(int type, const void * addr, unsigned long size) +{ + return access_ok(type,addr,size) ? 0 : -EFAULT; +} + + +/* + * The exception table consists of pairs of addresses: the first is the + * address of an instruction that is allowed to fault, and the second is + * the address at which the program should continue. No registers are + * modified, so it is entirely up to the continuation code to figure out + * what to do. + * + * All the routines below use bits of fixup code that are out of line + * with the main instruction path. This means when everything is well, + * we don't even have to jump over them. Further, they do not intrude + * on our cache or tlb entries. + */ + +struct exception_table_entry +{ + unsigned long insn, fixup; +}; + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + + +/* + * These are the main single-value transfer routines. They automatically + * use the right size if we just have the right pointer type. + * + * This gets kind of ugly. We want to return _two_ values in "get_user()" + * and yet we don't want to do any pointers, because that is too much + * of a performance impact. Thus we have a few rather ugly macros here, + * and hide all the uglyness from the user. + * + * The "__xxx" versions of the user access functions are versions that + * do not verify the address space, that must have been done previously + * with a separate "access_ok()" call (this is used when we do multiple + * accesses to the same area of user memory). + */ + +extern void __get_user_1(void); +extern void __get_user_2(void); +extern void __get_user_4(void); +extern void __get_user_8(void); + +#define __get_user_x(size,ret,x,ptr) \ + __asm__ __volatile__("call __get_user_" #size \ + :"=a" (ret),"=d" (x) \ + :"0" (ptr) \ + :"rbx") + +/* Careful: we have to cast the result to the type of the pointer for sign reasons */ +#define get_user(x,ptr) \ +({ long __ret_gu,__val_gu; \ + switch(sizeof (*(ptr))) { \ + case 1: __get_user_x(1,__ret_gu,__val_gu,ptr); break; \ + case 2: __get_user_x(2,__ret_gu,__val_gu,ptr); break; \ + case 4: __get_user_x(4,__ret_gu,__val_gu,ptr); break; \ + case 8: __get_user_x(8,__ret_gu,__val_gu,ptr); break; \ + default: __get_user_bad(); break; \ + } \ + (x) = (__typeof__(*(ptr)))__val_gu; \ + __ret_gu; \ +}) + +extern void __put_user_1(void); +extern void __put_user_2(void); +extern void __put_user_4(void); +extern void __put_user_8(void); + +extern void __put_user_bad(void); + +#define __put_user_x(size,ret,x,ptr) \ + __asm__ __volatile__("call __put_user_" #size \ + :"=a" (ret) \ + :"0" (ptr),"d" (x) \ + :"rbx") + +#define put_user(x,ptr) \ + __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) + +#define __get_user(x,ptr) \ + __get_user_nocheck((x),(ptr),sizeof(*(ptr))) +#define __put_user(x,ptr) \ + __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) + +#define __put_user_nocheck(x,ptr,size) \ +({ \ + long __pu_err; \ + __put_user_size((x),(ptr),(size),__pu_err); \ + __pu_err; \ +}) + + +#define __put_user_check(x,ptr,size) \ +({ \ + long __pu_err = -EFAULT; \ + __typeof__(*(ptr)) *__pu_addr = (ptr); \ + if (access_ok(VERIFY_WRITE,__pu_addr,size)) \ + __put_user_size((x),__pu_addr,(size),__pu_err); \ + __pu_err; \ +}) + +#define __put_user_size(x,ptr,size,retval) \ +do { \ + retval = 0; \ + switch (size) { \ + case 1: __put_user_asm(x,ptr,retval,"b","b","iq"); break; \ + case 2: __put_user_asm(x,ptr,retval,"w","w","ir"); break; \ + case 4: __put_user_asm(x,ptr,retval,"l","k","ir"); break; \ + case 8: __put_user_asm(x,ptr,retval,"q","","ir"); break; \ + default: __put_user_bad(); \ + } \ +} while (0) + +/* FIXME: this hack is definitely wrong -AK */ +struct __large_struct { unsigned long buf[100]; }; +#define __m(x) (*(struct __large_struct *)(x)) + +/* + * Tell gcc we read from memory instead of writing: this is because + * we do not write to any memory gcc knows about, so there are no + * aliasing issues. + */ +#define __put_user_asm(x, addr, err, itype, rtype, ltype) \ + __asm__ __volatile__( \ + "1: mov"itype" %"rtype"1,%2\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: movq %3,%0\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 8\n" \ + " .quad 1b,3b\n" \ + ".previous" \ + : "=r"(err) \ + : ltype (x), "m"(__m(addr)), "i"(-EFAULT), "0"(err)) + + +#define __get_user_nocheck(x,ptr,size) \ +({ \ + long __gu_err, __gu_val; \ + __get_user_size(__gu_val,(ptr),(size),__gu_err); \ + (x) = (__typeof__(*(ptr)))__gu_val; \ + __gu_err; \ +}) + +extern long __get_user_bad(void); + +#define __get_user_size(x,ptr,size,retval) \ +do { \ + retval = 0; \ + switch (size) { \ + case 1: __get_user_asm(x,ptr,retval,"b","b","=q"); break; \ + case 2: __get_user_asm(x,ptr,retval,"w","w","=r"); break; \ + case 4: __get_user_asm(x,ptr,retval,"l","k","=r"); break; \ + case 8: __get_user_asm(x,ptr,retval,"q","","=r"); break; \ + default: (x) = __get_user_bad(); \ + } \ +} while (0) + +#define __get_user_asm(x, addr, err, itype, rtype, ltype) \ + __asm__ __volatile__( \ + "1: mov"itype" %2,%"rtype"1\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: mov %3,%0\n" \ + " xor"itype" %"rtype"1,%"rtype"1\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 8\n" \ + " .quad 1b,3b\n" \ + ".previous" \ + : "=r"(err), ltype (x) \ + : "m"(__m(addr)), "i"(-EFAULT), "0"(err)) + +/* + * Copy To/From Userspace + */ + +/* Generic arbitrary sized copy. */ + +/* Could do 8byte accesses, instead of 4bytes. */ + +/* Generic arbitrary sized copy. */ +#define __copy_user(to,from,size) \ +do { \ + long __d0, __d1; \ + __asm__ __volatile__( \ + "0: rep; movsl\n" \ + " movq %3,%0\n" \ + "1: rep; movsb\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: lea 0(%3,%0,4),%0\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 8\n" \ + " .quad 0b,3b\n" \ + " .quad 1b,2b\n" \ + ".previous" \ + : "=&c"(size), "=&D" (__d0), "=&S" (__d1) \ + : "r"(size & 3), "0"(size / 4), "1"(to), "2"(from) \ + : "memory"); \ +} while (0) + +#define __copy_user_zeroing(to,from,size) \ +do { \ + long __d0, __d1; \ + __asm__ __volatile__( \ + "0: rep; movsl\n" \ + " movq %3,%0\n" \ + "1: rep; movsb\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: lea 0(%3,%0,4),%0\n" \ + "4: pushq %0\n" \ + " pushq %%rax\n" \ + " xorq %%rax,%%rax\n" \ + " rep; stosb\n" \ + " popq %%rax\n" \ + " popq %0\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 8\n" \ + " .quad 0b,3b\n" \ + " .quad 1b,4b\n" \ + ".previous" \ + : "=&c"(size), "=&D" (__d0), "=&S" (__d1) \ + : "r"(size & 3), "0"(size / 4), "1"(to), "2"(from) \ + : "memory"); \ +} while (0) + + +/* We let the __ versions of copy_from/to_user inline, because they're often + * used in fast paths and have only a small space overhead. + */ +static inline unsigned long +__generic_copy_from_user_nocheck(void *to, const void *from, unsigned long n) +{ + __copy_user_zeroing(to,from,n); + return n; +} + +static inline unsigned long +__generic_copy_to_user_nocheck(void *to, const void *from, unsigned long n) +{ + prefetch(from); + __copy_user(to,from,n); + return n; +} + + + +unsigned long __generic_copy_to_user(void *, const void *, unsigned long); +unsigned long __generic_copy_from_user(void *, const void *, unsigned long); + +static inline unsigned long +__constant_copy_to_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __copy_user(to,from,n); + return n; +} + +static inline unsigned long +__constant_copy_from_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_READ, from, n)) + __copy_user_zeroing(to,from,n); + else + memset(to, 0, n); + return n; +} + +static inline unsigned long +__constant_copy_to_user_nocheck(void *to, const void *from, unsigned long n) +{ + __copy_user(to,from,n); + return n; +} + +static inline unsigned long +__constant_copy_from_user_nocheck(void *to, const void *from, unsigned long n) +{ + __copy_user_zeroing(to,from,n); + return n; +} + +#define copy_to_user(to,from,n) \ + (__builtin_constant_p(n) ? \ + __constant_copy_to_user((to),(from),(n)) : \ + __generic_copy_to_user((to),(from),(n))) + +#define copy_from_user(to,from,n) \ + (__builtin_constant_p(n) ? \ + __constant_copy_from_user((to),(from),(n)) : \ + __generic_copy_from_user((to),(from),(n))) + +#define __copy_to_user(to,from,n) \ + (__builtin_constant_p(n) ? \ + __constant_copy_to_user_nocheck((to),(from),(n)) : \ + __generic_copy_to_user_nocheck((to),(from),(n))) + +#define __copy_from_user(to,from,n) \ + (__builtin_constant_p(n) ? \ + __constant_copy_from_user_nocheck((to),(from),(n)) : \ + __generic_copy_from_user_nocheck((to),(from),(n))) + +long strncpy_from_user(char *dst, const char *src, long count); +long __strncpy_from_user(char *dst, const char *src, long count); +#define strlen_user(str) strnlen_user(str, ~0UL >> 1) +long strnlen_user(const char *str, long n); +unsigned long clear_user(void *mem, unsigned long len); +unsigned long __clear_user(void *mem, unsigned long len); + +#endif /* __X86_64_UACCESS_H */ diff -Nru a/include/asm-x86_64/ucontext.h b/include/asm-x86_64/ucontext.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/ucontext.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,12 @@ +#ifndef _ASMX8664_UCONTEXT_H +#define _ASMX8664_UCONTEXT_H + +struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +#endif diff -Nru a/include/asm-x86_64/unaligned.h b/include/asm-x86_64/unaligned.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/unaligned.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,37 @@ +#ifndef __X8664_UNALIGNED_H +#define __X8664_UNALIGNED_H + +/* + * The x86-64 can do unaligned accesses itself. + * + * The strange macros are there to make sure these can't + * be misused in a way that makes them not work on other + * architectures where unaligned accesses aren't as simple. + */ + +/** + * get_unaligned - get value from possibly mis-aligned location + * @ptr: pointer to value + * + * This macro should be used for accessing values larger in size than + * single bytes at locations that are expected to be improperly aligned, + * e.g. retrieving a u16 value from a location not u16-aligned. + * + * Note that unaligned accesses can be very expensive on some architectures. + */ +#define get_unaligned(ptr) (*(ptr)) + +/** + * put_unaligned - put value to a possibly mis-aligned location + * @val: value to place + * @ptr: pointer to location + * + * This macro should be used for placing values larger in size than + * single bytes at locations that are expected to be improperly aligned, + * e.g. writing a u16 value to a location not u16-aligned. + * + * Note that unaligned accesses can be very expensive on some architectures. + */ +#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) )) + +#endif diff -Nru a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/unistd.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,653 @@ +#ifndef _ASM_X86_64_UNISTD_H_ +#define _ASM_X86_64_UNISTD_H_ + +#ifndef __SYSCALL +#define __SYSCALL(a,b) +#endif + +/* + * This file contains the system call numbers. + * + * Note: holes are not allowed. + */ + +/* at least 8 syscall per cacheline */ +#define __NR_read 0 +__SYSCALL(__NR_read, sys_read) +#define __NR_write 1 +__SYSCALL(__NR_write, sys_write) +#define __NR_open 2 +__SYSCALL(__NR_open, sys_open) +#define __NR_close 3 +__SYSCALL(__NR_close, sys_close) +#define __NR_stat 4 +__SYSCALL(__NR_stat, sys_newstat) +#define __NR_fstat 5 +__SYSCALL(__NR_fstat, sys_newfstat) +#define __NR_lstat 6 +__SYSCALL(__NR_lstat, sys_newlstat) +#define __NR_poll 7 +__SYSCALL(__NR_poll, sys_poll) + +#define __NR_lseek 8 +__SYSCALL(__NR_lseek, sys_lseek) +#define __NR_mmap 9 +__SYSCALL(__NR_mmap, sys_mmap) +#define __NR_mprotect 10 +__SYSCALL(__NR_mprotect, sys_mprotect) +#define __NR_munmap 11 +__SYSCALL(__NR_munmap, sys_munmap) +#define __NR_brk 12 +__SYSCALL(__NR_brk, sys_brk) +#define __NR_rt_sigaction 13 +__SYSCALL(__NR_rt_sigaction, sys_rt_sigaction) +#define __NR_rt_sigprocmask 14 +__SYSCALL(__NR_rt_sigprocmask, sys_rt_sigprocmask) +#define __NR_rt_sigreturn 15 +__SYSCALL(__NR_rt_sigreturn, stub_rt_sigreturn) + +#define __NR_ioctl 16 +__SYSCALL(__NR_ioctl, sys_ioctl) +#define __NR_pread 17 +__SYSCALL(__NR_pread, sys_pread) +#define __NR_pwrite 18 +__SYSCALL(__NR_pwrite, sys_pwrite) +#define __NR_readv 19 +__SYSCALL(__NR_readv, sys_readv) +#define __NR_writev 20 +__SYSCALL(__NR_writev, sys_writev) +#define __NR_access 21 +__SYSCALL(__NR_access, sys_access) +#define __NR_pipe 22 +__SYSCALL(__NR_pipe, sys_pipe) +#define __NR_select 23 +__SYSCALL(__NR_select, sys_select) + +#define __NR_sched_yield 24 +__SYSCALL(__NR_sched_yield, sys_sched_yield) +#define __NR_mremap 25 +__SYSCALL(__NR_mremap, sys_mremap) +#define __NR_msync 26 +__SYSCALL(__NR_msync, sys_msync) +#define __NR_mincore 27 +__SYSCALL(__NR_mincore, sys_mincore) +#define __NR_madvise 28 +__SYSCALL(__NR_madvise, sys_madvise) +#define __NR_shmget 29 +__SYSCALL(__NR_shmget, sys_shmget) +#define __NR_shmat 30 +__SYSCALL(__NR_shmat, sys_shmat) +#define __NR_shmctl 31 +__SYSCALL(__NR_shmctl, sys_shmctl) + +#define __NR_dup 32 +__SYSCALL(__NR_dup, sys_dup) +#define __NR_dup2 33 +__SYSCALL(__NR_dup2, sys_dup2) +#define __NR_pause 34 +__SYSCALL(__NR_pause, sys_pause) +#define __NR_nanosleep 35 +__SYSCALL(__NR_nanosleep, sys_nanosleep) +#define __NR_getitimer 36 +__SYSCALL(__NR_getitimer, sys_getitimer) +#define __NR_alarm 37 +__SYSCALL(__NR_alarm, sys_alarm) +#define __NR_setitimer 38 +__SYSCALL(__NR_setitimer, sys_setitimer) +#define __NR_getpid 39 +__SYSCALL(__NR_getpid, sys_getpid) + +#define __NR_sendfile 40 +__SYSCALL(__NR_sendfile, sys_sendfile) +#define __NR_socket 41 +__SYSCALL(__NR_socket, sys_socket) +#define __NR_connect 42 +__SYSCALL(__NR_connect, sys_connect) +#define __NR_accept 43 +__SYSCALL(__NR_accept, sys_accept) +#define __NR_sendto 44 +__SYSCALL(__NR_sendto, sys_sendto) +#define __NR_recvfrom 45 +__SYSCALL(__NR_recvfrom, sys_recvfrom) +#define __NR_sendmsg 46 +__SYSCALL(__NR_sendmsg, sys_sendmsg) +#define __NR_recvmsg 47 +__SYSCALL(__NR_recvmsg, sys_recvmsg) + +#define __NR_shutdown 48 +__SYSCALL(__NR_shutdown, sys_shutdown) +#define __NR_bind 49 +__SYSCALL(__NR_bind, sys_bind) +#define __NR_listen 50 +__SYSCALL(__NR_listen, sys_listen) +#define __NR_getsockname 51 +__SYSCALL(__NR_getsockname, sys_getsockname) +#define __NR_getpeername 52 +__SYSCALL(__NR_getpeername, sys_getpeername) +#define __NR_socketpair 53 +__SYSCALL(__NR_socketpair, sys_socketpair) +#define __NR_setsockopt 54 +__SYSCALL(__NR_setsockopt, sys_setsockopt) +#define __NR_getsockopt 55 +__SYSCALL(__NR_getsockopt, sys_getsockopt) + +#define __NR_clone 56 +__SYSCALL(__NR_clone, stub_clone) +#define __NR_fork 57 +__SYSCALL(__NR_fork, stub_fork) +#define __NR_vfork 58 +__SYSCALL(__NR_vfork, stub_vfork) +#define __NR_execve 59 +__SYSCALL(__NR_execve, stub_execve) +#define __NR_exit 60 +__SYSCALL(__NR_exit, sys_exit) +#define __NR_wait4 61 +__SYSCALL(__NR_wait4, sys_wait4) +#define __NR_kill 62 +__SYSCALL(__NR_kill, sys_kill) +#define __NR_uname 63 +__SYSCALL(__NR_uname, sys_uname) + +#define __NR_semget 64 +__SYSCALL(__NR_semget, sys_semget) +#define __NR_semop 65 +__SYSCALL(__NR_semop, sys_semop) +#define __NR_semctl 66 +__SYSCALL(__NR_semctl, sys_semctl) +#define __NR_shmdt 67 +__SYSCALL(__NR_shmdt, sys_shmdt) +#define __NR_msgget 68 +__SYSCALL(__NR_msgget, sys_msgget) +#define __NR_msgsnd 69 +__SYSCALL(__NR_msgsnd, sys_msgsnd) +#define __NR_msgrcv 70 +__SYSCALL(__NR_msgrcv, sys_msgrcv) +#define __NR_msgctl 71 +__SYSCALL(__NR_msgctl, sys_msgctl) + +#define __NR_fcntl 72 +__SYSCALL(__NR_fcntl, sys_fcntl) +#define __NR_flock 73 +__SYSCALL(__NR_flock, sys_flock) +#define __NR_fsync 74 +__SYSCALL(__NR_fsync, sys_fsync) +#define __NR_fdatasync 75 +__SYSCALL(__NR_fdatasync, sys_fdatasync) +#define __NR_truncate 76 +__SYSCALL(__NR_truncate, sys_truncate) +#define __NR_ftruncate 77 +__SYSCALL(__NR_ftruncate, sys_ftruncate) +#define __NR_getdents 78 +__SYSCALL(__NR_getdents, sys_getdents) +#define __NR_getcwd 79 +__SYSCALL(__NR_getcwd, sys_getcwd) + +#define __NR_chdir 80 +__SYSCALL(__NR_chdir, sys_chdir) +#define __NR_fchdir 81 +__SYSCALL(__NR_fchdir, sys_fchdir) +#define __NR_rename 82 +__SYSCALL(__NR_rename, sys_rename) +#define __NR_mkdir 83 +__SYSCALL(__NR_mkdir, sys_mkdir) +#define __NR_rmdir 84 +__SYSCALL(__NR_rmdir, sys_rmdir) +#define __NR_creat 85 +__SYSCALL(__NR_creat, sys_creat) +#define __NR_link 86 +__SYSCALL(__NR_link, sys_link) +#define __NR_unlink 87 +__SYSCALL(__NR_unlink, sys_unlink) + +#define __NR_symlink 88 +__SYSCALL(__NR_symlink, sys_symlink) +#define __NR_readlink 89 +__SYSCALL(__NR_readlink, sys_readlink) +#define __NR_chmod 90 +__SYSCALL(__NR_chmod, sys_chmod) +#define __NR_fchmod 91 +__SYSCALL(__NR_fchmod, sys_fchmod) +#define __NR_chown 92 +__SYSCALL(__NR_chown, sys_chown) +#define __NR_fchown 93 +__SYSCALL(__NR_fchown, sys_fchown) +#define __NR_lchown 94 +__SYSCALL(__NR_lchown, sys_lchown) +#define __NR_umask 95 +__SYSCALL(__NR_umask, sys_umask) + +#define __NR_gettimeofday 96 +__SYSCALL(__NR_gettimeofday, sys_gettimeofday) +#define __NR_getrlimit 97 +__SYSCALL(__NR_getrlimit, sys_getrlimit) +#define __NR_getrusage 98 +__SYSCALL(__NR_getrusage, sys_getrusage) +#define __NR_sysinfo 99 +__SYSCALL(__NR_sysinfo, sys_sysinfo) +#define __NR_times 100 +__SYSCALL(__NR_times, sys_times) +#define __NR_ptrace 101 +__SYSCALL(__NR_ptrace, sys_ptrace) +#define __NR_getuid 102 +__SYSCALL(__NR_getuid, sys_getuid) +#define __NR_syslog 103 +__SYSCALL(__NR_syslog, sys_syslog) + +/* at the very end the stuff that never runs during the benchmarks */ +#define __NR_getgid 104 +__SYSCALL(__NR_getgid, sys_getgid) +#define __NR_setuid 105 +__SYSCALL(__NR_setuid, sys_setuid) +#define __NR_setgid 106 +__SYSCALL(__NR_setgid, sys_setgid) +#define __NR_geteuid 107 +__SYSCALL(__NR_geteuid, sys_geteuid) +#define __NR_getegid 108 +__SYSCALL(__NR_getegid, sys_getegid) +#define __NR_setpgid 109 +__SYSCALL(__NR_setpgid, sys_setpgid) +#define __NR_getppid 110 +__SYSCALL(__NR_getppid, sys_getppid) +#define __NR_getpgrp 111 +__SYSCALL(__NR_getpgrp, sys_getpgrp) + +#define __NR_setsid 112 +__SYSCALL(__NR_setsid, sys_setsid) +#define __NR_setreuid 113 +__SYSCALL(__NR_setreuid, sys_setreuid) +#define __NR_setregid 114 +__SYSCALL(__NR_setregid, sys_setregid) +#define __NR_getgroups 115 +__SYSCALL(__NR_getgroups, sys_getgroups) +#define __NR_setgroups 116 +__SYSCALL(__NR_setgroups, sys_setgroups) +#define __NR_setresuid 117 +__SYSCALL(__NR_setresuid, sys_setresuid) +#define __NR_getresuid 118 +__SYSCALL(__NR_getresuid, sys_getresuid) +#define __NR_setresgid 119 +__SYSCALL(__NR_setresgid, sys_setresgid) + +#define __NR_getresgid 120 +__SYSCALL(__NR_getresgid, sys_getresgid) +#define __NR_getpgid 121 +__SYSCALL(__NR_getpgid, sys_getpgid) +#define __NR_setfsuid 122 +__SYSCALL(__NR_setfsuid, sys_setfsuid) +#define __NR_setfsgid 123 +__SYSCALL(__NR_setfsgid, sys_setfsgid) +#define __NR_getsid 124 +__SYSCALL(__NR_getsid, sys_getsid) +#define __NR_capget 125 +__SYSCALL(__NR_capget, sys_capget) +#define __NR_capset 126 +__SYSCALL(__NR_capset, sys_capset) + +#define __NR_rt_sigpending 127 +__SYSCALL(__NR_rt_sigpending, sys_rt_sigpending) +#define __NR_rt_sigtimedwait 128 +__SYSCALL(__NR_rt_sigtimedwait, sys_rt_sigtimedwait) +#define __NR_rt_sigqueueinfo 129 +__SYSCALL(__NR_rt_sigqueueinfo, sys_rt_sigqueueinfo) +#define __NR_rt_sigsuspend 130 +__SYSCALL(__NR_rt_sigsuspend, stub_rt_sigsuspend) +#define __NR_sigaltstack 131 +__SYSCALL(__NR_sigaltstack, stub_sigaltstack) +#define __NR_utime 132 +__SYSCALL(__NR_utime, sys_utime) +#define __NR_mknod 133 +__SYSCALL(__NR_mknod, sys_mknod) + +#define __NR_uselib 134 +__SYSCALL(__NR_uselib, sys_uselib) +#define __NR_personality 135 +__SYSCALL(__NR_personality, sys_personality) + +#define __NR_ustat 136 +__SYSCALL(__NR_ustat, sys_ustat) +#define __NR_statfs 137 +__SYSCALL(__NR_statfs, sys_statfs) +#define __NR_fstatfs 138 +__SYSCALL(__NR_fstatfs, sys_fstatfs) +#define __NR_sysfs 139 +__SYSCALL(__NR_sysfs, sys_sysfs) + +#define __NR_getpriority 140 +__SYSCALL(__NR_getpriority, sys_getpriority) +#define __NR_setpriority 141 +__SYSCALL(__NR_setpriority, sys_setpriority) +#define __NR_sched_setparam 142 +__SYSCALL(__NR_sched_setparam, sys_sched_setparam) +#define __NR_sched_getparam 143 +__SYSCALL(__NR_sched_getparam, sys_sched_getparam) +#define __NR_sched_setscheduler 144 +__SYSCALL(__NR_sched_setscheduler, sys_sched_setscheduler) +#define __NR_sched_getscheduler 145 +__SYSCALL(__NR_sched_getscheduler, sys_sched_getscheduler) +#define __NR_sched_get_priority_max 146 +__SYSCALL(__NR_sched_get_priority_max, sys_sched_get_priority_max) +#define __NR_sched_get_priority_min 147 +__SYSCALL(__NR_sched_get_priority_min, sys_sched_get_priority_min) +#define __NR_sched_rr_get_interval 148 +__SYSCALL(__NR_sched_rr_get_interval, sys_sched_rr_get_interval) + +#define __NR_mlock 149 +__SYSCALL(__NR_mlock, sys_mlock) +#define __NR_munlock 150 +__SYSCALL(__NR_munlock, sys_munlock) +#define __NR_mlockall 151 +__SYSCALL(__NR_mlockall, sys_mlockall) +#define __NR_munlockall 152 +__SYSCALL(__NR_munlockall, sys_munlockall) + +#define __NR_vhangup 153 +__SYSCALL(__NR_vhangup, sys_vhangup) + +#define __NR_modify_ldt 154 +__SYSCALL(__NR_modify_ldt, sys_modify_ldt) + +#define __NR_pivot_root 155 +__SYSCALL(__NR_pivot_root, sys_pivot_root) + +#define __NR__sysctl 156 +__SYSCALL(__NR__sysctl, sys_sysctl) + +#define __NR_prctl 157 +__SYSCALL(__NR_prctl, sys_prctl) +#define __NR_arch_prctl 158 +__SYSCALL(__NR_arch_prctl, sys_arch_prctl) + +#define __NR_adjtimex 159 +__SYSCALL(__NR_adjtimex, sys_adjtimex) + +#define __NR_setrlimit 160 +__SYSCALL(__NR_setrlimit, sys_setrlimit) + +#define __NR_chroot 161 +__SYSCALL(__NR_chroot, sys_chroot) + +#define __NR_sync 162 +__SYSCALL(__NR_sync, sys_sync) + +#define __NR_acct 163 +__SYSCALL(__NR_acct, sys_acct) + +#define __NR_settimeofday 164 +__SYSCALL(__NR_settimeofday, sys_settimeofday) + +#define __NR_mount 165 +__SYSCALL(__NR_mount, sys_mount) +#define __NR_umount2 166 +__SYSCALL(__NR_umount2, sys_umount) + +#define __NR_swapon 167 +__SYSCALL(__NR_swapon, sys_swapon) +#define __NR_swapoff 168 +__SYSCALL(__NR_swapoff, sys_swapoff) + +#define __NR_reboot 169 +__SYSCALL(__NR_reboot, sys_reboot) + +#define __NR_sethostname 170 +__SYSCALL(__NR_sethostname, sys_sethostname) +#define __NR_setdomainname 171 +__SYSCALL(__NR_setdomainname, sys_setdomainname) + +#define __NR_iopl 172 +__SYSCALL(__NR_iopl, stub_iopl) +#define __NR_ioperm 173 +__SYSCALL(__NR_ioperm, sys_ioperm) + +#define __NR_create_module 174 +__SYSCALL(__NR_create_module, sys_create_module) +#define __NR_init_module 175 +__SYSCALL(__NR_init_module, sys_init_module) +#define __NR_delete_module 176 +__SYSCALL(__NR_delete_module, sys_delete_module) +#define __NR_get_kernel_syms 177 +__SYSCALL(__NR_get_kernel_syms, sys_get_kernel_syms) +#define __NR_query_module 178 +__SYSCALL(__NR_query_module, sys_query_module) + +#define __NR_quotactl 179 +__SYSCALL(__NR_quotactl, sys_quotactl) + +#define __NR_nfsservctl 180 +__SYSCALL(__NR_nfsservctl, sys_nfsservctl) + +#define __NR_getpmsg 181 /* reserved for LiS/STREAMS */ +__SYSCALL(__NR_getpmsg, sys_ni_syscall) +#define __NR_putpmsg 182 /* reserved for LiS/STREAMS */ +__SYSCALL(__NR_putpmsg, sys_ni_syscall) + +#define __NR_afs_syscall 183 /* reserved for AFS */ +__SYSCALL(__NR_afs_syscall, sys_ni_syscall) + +#define __NR_tuxcall 184 /* reserved for tux */ +__SYSCALL(__NR_tuxcall, sys_ni_syscall) + +#define __NR_security 185 /* reserved for LSM/security */ +__SYSCALL(__NR_security, sys_ni_syscall) + +#define __NR_gettid 186 +__SYSCALL(__NR_gettid, sys_gettid) + +#define __NR_readahead 187 +__SYSCALL(__NR_readahead, sys_readahead) +#define __NR_setxattr 188 +__SYSCALL(__NR_setxattr, sys_setxattr) +#define __NR_lsetxattr 189 +__SYSCALL(__NR_lsetxattr, sys_lsetxattr) +#define __NR_fsetxattr 190 +__SYSCALL(__NR_fsetxattr, sys_fsetxattr) +#define __NR_getxattr 191 +__SYSCALL(__NR_getxattr, sys_getxattr) +#define __NR_lgetxattr 192 +__SYSCALL(__NR_lgetxattr, sys_lgetxattr) +#define __NR_fgetxattr 193 +__SYSCALL(__NR_fgetxattr, sys_fgetxattr) +#define __NR_listxattr 194 +__SYSCALL(__NR_listxattr, sys_listxattr) +#define __NR_llistxattr 195 +__SYSCALL(__NR_llistxattr, sys_llistxattr) +#define __NR_flistxattr 196 +__SYSCALL(__NR_flistxattr, sys_flistxattr) +#define __NR_removexattr 197 +__SYSCALL(__NR_removexattr, sys_removexattr) +#define __NR_lremovexattr 198 +__SYSCALL(__NR_lremovexattr, sys_lremovexattr) +#define __NR_fremovexattr 199 +__SYSCALL(__NR_fremovexattr, sys_fremovexattr) +#define __NR_tkill 200 +__SYSCALL(__NR_tkill, sys_tkill) + +#define __NR_syscall_max __NR_tkill + +#ifndef __NO_STUBS + +/* user-visible error numbers are in the range -1 - -124: see */ + +#define __syscall_clobber "r11","rcx","memory" + +#define __syscall_return(type, res) \ +do { \ + if ((unsigned long)(res) >= (unsigned long)(-127)) { \ + errno = -(res); \ + res = -1; \ + } \ + return (type) (res); \ +} while (0) + +#ifndef __KERNEL_SYSCALLS__ + +#define __syscall "syscall" + +/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */ +#define _syscall0(type,name) \ +type name(void) \ +{ \ +long __res; \ +__asm__ volatile (__syscall \ + : "=a" (__res) \ + : "0" (__NR_##name) : __syscall_clobber ); \ +__syscall_return(type,__res); \ +} + +#define _syscall1(type,name,type1,arg1) \ +type name(type1 arg1) \ +{ \ +long __res; \ +__asm__ volatile (__syscall \ + : "=a" (__res) \ + : "0" (__NR_##name),"D" ((long)(arg1)) : __syscall_clobber ); \ +__syscall_return(type,__res); \ +} + +#define _syscall2(type,name,type1,arg1,type2,arg2) \ +type name(type1 arg1,type2 arg2) \ +{ \ +long __res; \ +__asm__ volatile (__syscall \ + : "=a" (__res) \ + : "0" (__NR_##name),"D" ((long)(arg1)),"S" ((long)(arg2)) : __syscall_clobber ); \ +__syscall_return(type,__res); \ +} + +#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ +type name(type1 arg1,type2 arg2,type3 arg3) \ +{ \ +long __res; \ +__asm__ volatile (__syscall \ + : "=a" (__res) \ + : "0" (__NR_##name),"D" ((long)(arg1)),"S" ((long)(arg2)), \ + "d" ((long)(arg3)) : __syscall_clobber, "r9" ); \ +__syscall_return(type,__res); \ +} + +#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ +type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ +long __res; \ +__asm__ volatile ("movq %5,%%r10 ;" __syscall \ + : "=a" (__res) \ + : "0" (__NR_##name),"D" ((long)(arg1)),"S" ((long)(arg2)), \ + "d" ((long)(arg3)),"g" ((long)(arg4)) : __syscall_clobber,"r10" ); \ +__syscall_return(type,__res); \ +} + +#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ +type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \ +{ \ +long __res; \ +__asm__ volatile ("movq %5,%%r10 ; movq %6,%%r9 ; " __syscall \ + : "=a" (__res) \ + : "0" (__NR_##name),"D" ((long)(arg1)),"S" ((long)(arg2)), \ + "d" ((long)(arg3)),"g" ((long)(arg4)),"g" ((long)(arg5)) : \ + __syscall_clobber,"r8","r9","r10" ); \ +__syscall_return(type,__res); \ +} + +#else /* __KERNEL_SYSCALLS__ */ + +/* + * we need this inline - forking from kernel space will result + * in NO COPY ON WRITE (!!!), until an execve is executed. This + * is no problem, but for the stack. This is handled by not letting + * main() use the stack at all after fork(). Thus, no function + * calls - which means inline code for fork too, as otherwise we + * would use the stack upon exit from 'fork()'. + * + * Actually only pause and fork are needed inline, so that there + * won't be any messing with the stack from main(), but we define + * some others too. + */ +#define __NR__exit __NR_exit + +extern long sys_pause(void); +static inline long pause(void) +{ + return sys_pause(); +} + +extern long sys_sync(void); +static inline long sync(void) +{ + return sys_sync(); +} + +extern pid_t sys_setsid(void); +static inline pid_t setsid(void) +{ + return sys_setsid(); +} + +extern ssize_t sys_write(unsigned int, char *, size_t); +static inline ssize_t write(unsigned int fd, char * buf, size_t count) +{ + return sys_write(fd, buf, count); +} + +extern ssize_t sys_read(unsigned int, char *, size_t); +static inline ssize_t read(unsigned int fd, char * buf, size_t count) +{ + return sys_read(fd, buf, count); +} + +extern off_t sys_lseek(unsigned int, off_t, unsigned int); +static inline off_t lseek(unsigned int fd, off_t offset, unsigned int origin) +{ + return sys_lseek(fd, offset, origin); +} + +extern long sys_dup(unsigned int); +static inline long dup(unsigned int fd) +{ + return sys_dup(fd); +} + +/* implemented in asm in arch/x86_64/kernel/entry.S */ +extern long execve(char *, char **, char **); + +extern long sys_open(const char *, int, int); +static inline long open(const char * filename, int flags, int mode) +{ + return sys_open(filename, flags, mode); +} + +extern long sys_close(unsigned int); +static inline long close(unsigned int fd) +{ + return sys_close(fd); +} + +extern long sys_exit(int) __attribute__((noreturn)); +extern inline long exit(int error_code) +{ + sys_exit(error_code); +} + +extern long sys_delete_module(const char *); +static inline long delete_module(const char *name_user) +{ + return sys_delete_module(name_user); +} + +struct rusage; +asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, + int options, struct rusage * ru); +static inline pid_t waitpid(int pid, int * wait_stat, int flags) +{ + return sys_wait4(pid, wait_stat, flags, NULL); +} + +static inline pid_t wait(int * wait_stat) +{ + return waitpid(-1,wait_stat,0); +} + +#endif /* __KERNEL_SYSCALLS__ */ + +#endif /* __NO_STUBS */ + +#endif diff -Nru a/include/asm-x86_64/user.h b/include/asm-x86_64/user.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/user.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,113 @@ +#ifndef _X86_64_USER_H +#define _X86_64_USER_H + +#include +#include +#include +/* Core file format: The core file is written in such a way that gdb + can understand it and provide useful information to the user (under + linux we use the 'trad-core' bfd). There are quite a number of + obstacles to being able to view the contents of the floating point + registers, and until these are solved you will not be able to view the + contents of them. Actually, you can read in the core file and look at + the contents of the user struct to find out what the floating point + registers contain. + The actual file contents are as follows: + UPAGE: 1 page consisting of a user struct that tells gdb what is present + in the file. Directly after this is a copy of the task_struct, which + is currently not used by gdb, but it may come in useful at some point. + All of the registers are stored as part of the upage. The upage should + always be only one page. + DATA: The data area is stored. We use current->end_text to + current->brk to pick up all of the user variables, plus any memory + that may have been malloced. No attempt is made to determine if a page + is demand-zero or if a page is totally unused, we just cover the entire + range. All of the addresses are rounded in such a way that an integral + number of pages is written. + STACK: We need the stack information in order to get a meaningful + backtrace. We need to write the data from (esp) to + current->start_stack, so we round each of these off in order to be able + to write an integer number of pages. + The minimum core file size is 3 pages, or 12288 bytes. +*/ + +/* This is not neccessary in first phase. It will have to be + synchronized with gdb later. */ + +/* + * Pentium III FXSR, SSE support + * Gareth Hughes , May 2000 + * + * Provide support for the GDB 5.0+ PTRACE_{GET|SET}FPXREGS requests for + * interacting with the FXSR-format floating point environment. Floating + * point data can be accessed in the regular format in the usual manner, + * and both the standard and SIMD floating point data can be accessed via + * the new ptrace requests. In either case, changes to the FPU environment + * will be reflected in the task's state as expected. + */ + +struct user_i387_struct { + unsigned short cwd; + unsigned short swd; + unsigned short twd; + unsigned short fop; + u32 fip; + u32 fcs; + u32 foo; + u32 fos; + u32 mxcsr; + u32 reserved; + u32 st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ + u32 xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */ + u32 padding[56]; +}; + +/* + * This is copy of the layout of "struct pt_regs", and + * is still the layout used by user mode (the new + * pt_regs doesn't have all registers as the kernel + * doesn't use the extra segment registers) + */ +struct user_regs_struct { + unsigned long r15,r14,r13,r12,rbp,rbx,r11,r10; + unsigned long r9,r8,rax,rcx,rdx,rsi,rdi,orig_rax; + unsigned long rip,cs,eflags; + unsigned long rsp,ss; + unsigned long fs_base, kernel_gs_base; +}; + +/* When the kernel dumps core, it starts by dumping the user struct - + this will be used by gdb to figure out where the data and stack segments + are within the file, and what virtual addresses to use. */ +struct user{ +/* We start with the registers, to mimic the way that "memory" is returned + from the ptrace(3,...) function. */ + struct user_regs_struct regs; /* Where the registers are actually stored */ +/* ptrace does not yet supply these. Someday.... */ + int u_fpvalid; /* True if math co-processor being used. */ + /* for this mess. Not yet used. */ + struct user_i387_struct i387; /* Math Co-processor registers. */ +/* The rest of this junk is to help gdb figure out what goes where */ + unsigned long int u_tsize; /* Text segment size (pages). */ + unsigned long int u_dsize; /* Data segment size (pages). */ + unsigned long int u_ssize; /* Stack segment size (pages). */ + unsigned long start_code; /* Starting virtual address of text. */ + unsigned long start_stack; /* Starting virtual address of stack area. + This is actually the bottom of the stack, + the top of the stack is always found in the + esp register. */ + long int signal; /* Signal that caused the core dump. */ + int reserved; /* No longer used */ + struct user_pt_regs * u_ar0; /* Used by gdb to help find the values for */ + /* the registers. */ + struct user_i387_struct* u_fpstate; /* Math Co-processor pointer. */ + unsigned long magic; /* To uniquely identify a core file */ + char u_comm[32]; /* User command that was responsible */ + int u_debugreg[8]; +}; +#define NBPG PAGE_SIZE +#define UPAGES 1 +#define HOST_TEXT_START_ADDR (u.start_code) +#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) + +#endif /* _X86_64_USER_H */ diff -Nru a/include/asm-x86_64/user32.h b/include/asm-x86_64/user32.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/user32.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,58 @@ +#ifndef USER32_H +#define USER32_H 1 + +/* IA32 compatible user structures for ptrace. These should be used for 32bit coredumps too. */ + +struct user_i387_ia32_struct { + u32 cwd; + u32 swd; + u32 twd; + u32 fip; + u32 fcs; + u32 foo; + u32 fos; + u32 st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */ +}; + +/* + * This is the old layout of "struct pt_regs", and + * is still the layout used by user mode (the new + * pt_regs doesn't have all registers as the kernel + * doesn't use the extra segment registers) + */ +struct user_regs_struct32 { + __u32 ebx, ecx, edx, esi, edi, ebp, eax; + unsigned short ds, __ds, es, __es; + unsigned short fs, __fs, gs, __gs; + __u32 orig_eax, eip; + unsigned short cs, __cs; + __u32 eflags, esp; + unsigned short ss, __ss; +}; + +struct user32 { + struct user_regs_struct32 regs; /* Where the registers are actually stored */ + int u_fpvalid; /* True if math co-processor being used. */ + /* for this mess. Not yet used. */ + struct user_i387_ia32_struct i387; /* Math Co-processor registers. */ +/* The rest of this junk is to help gdb figure out what goes where */ + __u32 u_tsize; /* Text segment size (pages). */ + __u32 u_dsize; /* Data segment size (pages). */ + __u32 u_ssize; /* Stack segment size (pages). */ + __u32 start_code; /* Starting virtual address of text. */ + __u32 start_stack; /* Starting virtual address of stack area. + This is actually the bottom of the stack, + the top of the stack is always found in the + esp register. */ + __u32 signal; /* Signal that caused the core dump. */ + int reserved; /* No __u32er used */ + __u32 u_ar0; /* Used by gdb to help find the values for */ + /* the registers. */ + __u32 u_fpstate; /* Math Co-processor pointer. */ + __u32 magic; /* To uniquely identify a core file */ + char u_comm[32]; /* User command that was responsible */ + int u_debugreg[8]; +}; + + +#endif diff -Nru a/include/asm-x86_64/vga.h b/include/asm-x86_64/vga.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/vga.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,20 @@ +/* + * Access to VGA videoram + * + * (c) 1998 Martin Mares + */ + +#ifndef _LINUX_ASM_VGA_H_ +#define _LINUX_ASM_VGA_H_ + +/* + * On the PC, we can just recalculate addresses and then + * access the videoram directly without any black magic. + */ + +#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x) + +#define vga_readb(x) (*(x)) +#define vga_writeb(x,y) (*(y) = (x)) + +#endif diff -Nru a/include/asm-x86_64/vsyscall.h b/include/asm-x86_64/vsyscall.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/vsyscall.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,48 @@ +#ifndef _ASM_X86_64_VSYSCALL_H_ +#define _ASM_X86_64_VSYSCALL_H_ + +enum vsyscall_num { + __NR_vgettimeofday, + __NR_vtime, +}; + +#define VSYSCALL_START (-10UL << 20) +#define VSYSCALL_SIZE 1024 +#define VSYSCALL_END (-2UL << 20) +#define VSYSCALL_ADDR(vsyscall_nr) (VSYSCALL_START+VSYSCALL_SIZE*(vsyscall_nr)) + +#ifdef __KERNEL__ + +#define __section_last_tsc_low __attribute__ ((unused, __section__ (".last_tsc_low"))) +#define __section_delay_at_last_interrupt __attribute__ ((unused, __section__ (".delay_at_last_interrupt"))) +#define __section_fast_gettimeoffset_quotient __attribute__ ((unused, __section__ (".fast_gettimeoffset_quotient"))) +#define __section_wall_jiffies __attribute__ ((unused, __section__ (".wall_jiffies"))) +#define __section_jiffies __attribute__ ((unused, __section__ (".jiffies"))) +#define __section_sys_tz __attribute__ ((unused, __section__ (".sys_tz"))) +#define __section_xtime __attribute__ ((unused, __section__ (".xtime"))) +#define __section_vxtime_sequence __attribute__ ((unused, __section__ (".vxtime_sequence"))) + +/* vsyscall space (readonly) */ +extern long __vxtime_sequence[2]; +extern int __delay_at_last_interrupt; +extern unsigned long __last_tsc_low; +extern unsigned long __fast_gettimeoffset_quotient; +extern struct timeval __xtime; +extern volatile unsigned long __jiffies; +extern unsigned long __wall_jiffies; +extern struct timezone __sys_tz; + +/* kernel space (writeable) */ +extern unsigned long last_tsc_low; +extern int delay_at_last_interrupt; +extern unsigned long fast_gettimeoffset_quotient; +extern unsigned long wall_jiffies; +extern struct timezone sys_tz; +extern long vxtime_sequence[2]; + +#define vxtime_lock() do { vxtime_sequence[0]++; wmb(); } while(0) +#define vxtime_unlock() do { wmb(); vxtime_sequence[1]++; } while (0) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_X86_64_VSYSCALL_H_ */ diff -Nru a/include/asm-x86_64/xor.h b/include/asm-x86_64/xor.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-x86_64/xor.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,859 @@ +/* + * include/asm-i386/xor.h + * + * Optimized RAID-5 checksumming functions for MMX and SSE. + * + * 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, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * High-speed RAID5 checksumming functions utilizing MMX instructions. + * Copyright (C) 1998 Ingo Molnar. + */ + +#define FPU_SAVE \ + do { \ + if (!test_thread_flag(TIF_USEDFPU)) \ + __asm__ __volatile__ (" clts;\n"); \ + __asm__ __volatile__ ("fsave %0; fwait": "=m"(fpu_save[0])); \ + } while (0) + +#define FPU_RESTORE \ + do { \ + __asm__ __volatile__ ("frstor %0": : "m"(fpu_save[0])); \ + if (!test_thread_flag(TIF_USEDFPU)) \ + stts(); \ + } while (0) + +#define LD(x,y) " movq 8*("#x")(%1), %%mm"#y" ;\n" +#define ST(x,y) " movq %%mm"#y", 8*("#x")(%1) ;\n" +#define XO1(x,y) " pxor 8*("#x")(%2), %%mm"#y" ;\n" +#define XO2(x,y) " pxor 8*("#x")(%3), %%mm"#y" ;\n" +#define XO3(x,y) " pxor 8*("#x")(%4), %%mm"#y" ;\n" +#define XO4(x,y) " pxor 8*("#x")(%5), %%mm"#y" ;\n" + + +static void +xor_pII_mmx_2(unsigned long bytes, unsigned long *p1, unsigned long *p2) +{ + unsigned long lines = bytes >> 7; + char fpu_save[108]; + + FPU_SAVE; + + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + XO1(i,0) \ + ST(i,0) \ + XO1(i+1,1) \ + ST(i+1,1) \ + XO1(i+2,2) \ + ST(i+2,2) \ + XO1(i+3,3) \ + ST(i+3,3) + + " .align 32 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addq $128, %1 ;\n" + " addq $128, %2 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (p1), "r" (p2) + : "memory"); + + FPU_RESTORE; +} + +static void +xor_pII_mmx_3(unsigned long bytes, unsigned long *p1, unsigned long *p2, + unsigned long *p3) +{ + unsigned long lines = bytes >> 7; + char fpu_save[108]; + + FPU_SAVE; + + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + XO2(i,0) \ + ST(i,0) \ + XO2(i+1,1) \ + ST(i+1,1) \ + XO2(i+2,2) \ + ST(i+2,2) \ + XO2(i+3,3) \ + ST(i+3,3) + + " .align 32 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addq $128, %1 ;\n" + " addq $128, %2 ;\n" + " addq $128, %3 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (p1), "r" (p2), "r" (p3) + : "memory"); + + FPU_RESTORE; +} + +static void +xor_pII_mmx_4(unsigned long bytes, unsigned long *p1, unsigned long *p2, + unsigned long *p3, unsigned long *p4) +{ + unsigned long lines = bytes >> 7; + char fpu_save[108]; + + FPU_SAVE; + + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + XO2(i,0) \ + XO2(i+1,1) \ + XO2(i+2,2) \ + XO2(i+3,3) \ + XO3(i,0) \ + ST(i,0) \ + XO3(i+1,1) \ + ST(i+1,1) \ + XO3(i+2,2) \ + ST(i+2,2) \ + XO3(i+3,3) \ + ST(i+3,3) + + " .align 32 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addq $128, %1 ;\n" + " addq $128, %2 ;\n" + " addq $128, %3 ;\n" + " addq $128, %4 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (p1), "r" (p2), "r" (p3), "r" (p4) + : "memory"); + + FPU_RESTORE; +} + +static void +xor_pII_mmx_5(unsigned long bytes, unsigned long *p1, unsigned long *p2, + unsigned long *p3, unsigned long *p4, unsigned long *p5) +{ + unsigned long lines = bytes >> 7; + char fpu_save[108]; + + FPU_SAVE; + + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + XO2(i,0) \ + XO2(i+1,1) \ + XO2(i+2,2) \ + XO2(i+3,3) \ + XO3(i,0) \ + XO3(i+1,1) \ + XO3(i+2,2) \ + XO3(i+3,3) \ + XO4(i,0) \ + ST(i,0) \ + XO4(i+1,1) \ + ST(i+1,1) \ + XO4(i+2,2) \ + ST(i+2,2) \ + XO4(i+3,3) \ + ST(i+3,3) + + " .align 32 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addq $128, %1 ;\n" + " addq $128, %2 ;\n" + " addq $128, %3 ;\n" + " addq $128, %4 ;\n" + " addq $128, %5 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "g" (lines), + "r" (p1), "r" (p2), "r" (p3), "r" (p4), "r" (p5) + : "memory"); + + FPU_RESTORE; +} + +#undef LD +#undef XO1 +#undef XO2 +#undef XO3 +#undef XO4 +#undef ST +#undef BLOCK + +static void +xor_p5_mmx_2(unsigned long bytes, unsigned long *p1, unsigned long *p2) +{ + unsigned long lines = bytes >> 6; + char fpu_save[108]; + + FPU_SAVE; + + __asm__ __volatile__ ( + " .align 32 ;\n" + " 1: ;\n" + " movq (%1), %%mm0 ;\n" + " movq 8(%1), %%mm1 ;\n" + " pxor (%2), %%mm0 ;\n" + " movq 16(%1), %%mm2 ;\n" + " movq %%mm0, (%1) ;\n" + " pxor 8(%2), %%mm1 ;\n" + " movq 24(%1), %%mm3 ;\n" + " movq %%mm1, 8(%1) ;\n" + " pxor 16(%2), %%mm2 ;\n" + " movq 32(%1), %%mm4 ;\n" + " movq %%mm2, 16(%1) ;\n" + " pxor 24(%2), %%mm3 ;\n" + " movq 40(%1), %%mm5 ;\n" + " movq %%mm3, 24(%1) ;\n" + " pxor 32(%2), %%mm4 ;\n" + " movq 48(%1), %%mm6 ;\n" + " movq %%mm4, 32(%1) ;\n" + " pxor 40(%2), %%mm5 ;\n" + " movq 56(%1), %%mm7 ;\n" + " movq %%mm5, 40(%1) ;\n" + " pxor 48(%2), %%mm6 ;\n" + " pxor 56(%2), %%mm7 ;\n" + " movq %%mm6, 48(%1) ;\n" + " movq %%mm7, 56(%1) ;\n" + + " addq $64, %1 ;\n" + " addq $64, %2 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (p1), "r" (p2) + : "memory"); + + FPU_RESTORE; +} + +static void +xor_p5_mmx_3(unsigned long bytes, unsigned long *p1, unsigned long *p2, + unsigned long *p3) +{ + unsigned long lines = bytes >> 6; + char fpu_save[108]; + + FPU_SAVE; + + __asm__ __volatile__ ( + " .align 32,0x90 ;\n" + " 1: ;\n" + " movq (%1), %%mm0 ;\n" + " movq 8(%1), %%mm1 ;\n" + " pxor (%2), %%mm0 ;\n" + " movq 16(%1), %%mm2 ;\n" + " pxor 8(%2), %%mm1 ;\n" + " pxor (%3), %%mm0 ;\n" + " pxor 16(%2), %%mm2 ;\n" + " movq %%mm0, (%1) ;\n" + " pxor 8(%3), %%mm1 ;\n" + " pxor 16(%3), %%mm2 ;\n" + " movq 24(%1), %%mm3 ;\n" + " movq %%mm1, 8(%1) ;\n" + " movq 32(%1), %%mm4 ;\n" + " movq 40(%1), %%mm5 ;\n" + " pxor 24(%2), %%mm3 ;\n" + " movq %%mm2, 16(%1) ;\n" + " pxor 32(%2), %%mm4 ;\n" + " pxor 24(%3), %%mm3 ;\n" + " pxor 40(%2), %%mm5 ;\n" + " movq %%mm3, 24(%1) ;\n" + " pxor 32(%3), %%mm4 ;\n" + " pxor 40(%3), %%mm5 ;\n" + " movq 48(%1), %%mm6 ;\n" + " movq %%mm4, 32(%1) ;\n" + " movq 56(%1), %%mm7 ;\n" + " pxor 48(%2), %%mm6 ;\n" + " movq %%mm5, 40(%1) ;\n" + " pxor 56(%2), %%mm7 ;\n" + " pxor 48(%3), %%mm6 ;\n" + " pxor 56(%3), %%mm7 ;\n" + " movq %%mm6, 48(%1) ;\n" + " movq %%mm7, 56(%1) ;\n" + + " addq $64, %1 ;\n" + " addq $64, %2 ;\n" + " addq $64, %3 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (p1), "r" (p2), "r" (p3) + : "memory" ); + + FPU_RESTORE; +} + +static void +xor_p5_mmx_4(unsigned long bytes, unsigned long *p1, unsigned long *p2, + unsigned long *p3, unsigned long *p4) +{ + unsigned long lines = bytes >> 6; + char fpu_save[108]; + + FPU_SAVE; + + __asm__ __volatile__ ( + " .align 32,0x90 ;\n" + " 1: ;\n" + " movq (%1), %%mm0 ;\n" + " movq 8(%1), %%mm1 ;\n" + " pxor (%2), %%mm0 ;\n" + " movq 16(%1), %%mm2 ;\n" + " pxor 8(%2), %%mm1 ;\n" + " pxor (%3), %%mm0 ;\n" + " pxor 16(%2), %%mm2 ;\n" + " pxor 8(%3), %%mm1 ;\n" + " pxor (%4), %%mm0 ;\n" + " movq 24(%1), %%mm3 ;\n" + " pxor 16(%3), %%mm2 ;\n" + " pxor 8(%4), %%mm1 ;\n" + " movq %%mm0, (%1) ;\n" + " movq 32(%1), %%mm4 ;\n" + " pxor 24(%2), %%mm3 ;\n" + " pxor 16(%4), %%mm2 ;\n" + " movq %%mm1, 8(%1) ;\n" + " movq 40(%1), %%mm5 ;\n" + " pxor 32(%2), %%mm4 ;\n" + " pxor 24(%3), %%mm3 ;\n" + " movq %%mm2, 16(%1) ;\n" + " pxor 40(%2), %%mm5 ;\n" + " pxor 32(%3), %%mm4 ;\n" + " pxor 24(%4), %%mm3 ;\n" + " movq %%mm3, 24(%1) ;\n" + " movq 56(%1), %%mm7 ;\n" + " movq 48(%1), %%mm6 ;\n" + " pxor 40(%3), %%mm5 ;\n" + " pxor 32(%4), %%mm4 ;\n" + " pxor 48(%2), %%mm6 ;\n" + " movq %%mm4, 32(%1) ;\n" + " pxor 56(%2), %%mm7 ;\n" + " pxor 40(%4), %%mm5 ;\n" + " pxor 48(%3), %%mm6 ;\n" + " pxor 56(%3), %%mm7 ;\n" + " movq %%mm5, 40(%1) ;\n" + " pxor 48(%4), %%mm6 ;\n" + " pxor 56(%4), %%mm7 ;\n" + " movq %%mm6, 48(%1) ;\n" + " movq %%mm7, 56(%1) ;\n" + + " addq $64, %1 ;\n" + " addq $64, %2 ;\n" + " addq $64, %3 ;\n" + " addq $64, %4 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (p1), "r" (p2), "r" (p3), "r" (p4) + : "memory"); + + FPU_RESTORE; +} + +static void +xor_p5_mmx_5(unsigned long bytes, unsigned long *p1, unsigned long *p2, + unsigned long *p3, unsigned long *p4, unsigned long *p5) +{ + unsigned long lines = bytes >> 6; + char fpu_save[108]; + + FPU_SAVE; + + __asm__ __volatile__ ( + " .align 32,0x90 ;\n" + " 1: ;\n" + " movq (%1), %%mm0 ;\n" + " movq 8(%1), %%mm1 ;\n" + " pxor (%2), %%mm0 ;\n" + " pxor 8(%2), %%mm1 ;\n" + " movq 16(%1), %%mm2 ;\n" + " pxor (%3), %%mm0 ;\n" + " pxor 8(%3), %%mm1 ;\n" + " pxor 16(%2), %%mm2 ;\n" + " pxor (%4), %%mm0 ;\n" + " pxor 8(%4), %%mm1 ;\n" + " pxor 16(%3), %%mm2 ;\n" + " movq 24(%1), %%mm3 ;\n" + " pxor (%5), %%mm0 ;\n" + " pxor 8(%5), %%mm1 ;\n" + " movq %%mm0, (%1) ;\n" + " pxor 16(%4), %%mm2 ;\n" + " pxor 24(%2), %%mm3 ;\n" + " movq %%mm1, 8(%1) ;\n" + " pxor 16(%5), %%mm2 ;\n" + " pxor 24(%3), %%mm3 ;\n" + " movq 32(%1), %%mm4 ;\n" + " movq %%mm2, 16(%1) ;\n" + " pxor 24(%4), %%mm3 ;\n" + " pxor 32(%2), %%mm4 ;\n" + " movq 40(%1), %%mm5 ;\n" + " pxor 24(%5), %%mm3 ;\n" + " pxor 32(%3), %%mm4 ;\n" + " pxor 40(%2), %%mm5 ;\n" + " movq %%mm3, 24(%1) ;\n" + " pxor 32(%4), %%mm4 ;\n" + " pxor 40(%3), %%mm5 ;\n" + " movq 48(%1), %%mm6 ;\n" + " movq 56(%1), %%mm7 ;\n" + " pxor 32(%5), %%mm4 ;\n" + " pxor 40(%4), %%mm5 ;\n" + " pxor 48(%2), %%mm6 ;\n" + " pxor 56(%2), %%mm7 ;\n" + " movq %%mm4, 32(%1) ;\n" + " pxor 48(%3), %%mm6 ;\n" + " pxor 56(%3), %%mm7 ;\n" + " pxor 40(%5), %%mm5 ;\n" + " pxor 48(%4), %%mm6 ;\n" + " pxor 56(%4), %%mm7 ;\n" + " movq %%mm5, 40(%1) ;\n" + " pxor 48(%5), %%mm6 ;\n" + " pxor 56(%5), %%mm7 ;\n" + " movq %%mm6, 48(%1) ;\n" + " movq %%mm7, 56(%1) ;\n" + + " addq $64, %1 ;\n" + " addq $64, %2 ;\n" + " addq $64, %3 ;\n" + " addq $64, %4 ;\n" + " addq $64, %5 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "g" (lines), + "r" (p1), "r" (p2), "r" (p3), "r" (p4), "r" (p5) + : "memory"); + + FPU_RESTORE; +} + +static struct xor_block_template xor_block_pII_mmx = { + name: "pII_mmx", + do_2: xor_pII_mmx_2, + do_3: xor_pII_mmx_3, + do_4: xor_pII_mmx_4, + do_5: xor_pII_mmx_5, +}; + +static struct xor_block_template xor_block_p5_mmx = { + name: "p5_mmx", + do_2: xor_p5_mmx_2, + do_3: xor_p5_mmx_3, + do_4: xor_p5_mmx_4, + do_5: xor_p5_mmx_5, +}; + +#undef FPU_SAVE +#undef FPU_RESTORE + +/* + * Cache avoiding checksumming functions utilizing KNI instructions + * Copyright (C) 1999 Zach Brown (with obvious credit due Ingo) + */ + +#define XMMS_SAVE \ + __asm__ __volatile__ ( \ + "movq %%cr0,%0 ;\n\t" \ + "clts ;\n\t" \ + "movups %%xmm0,(%1) ;\n\t" \ + "movups %%xmm1,0x10(%1) ;\n\t" \ + "movups %%xmm2,0x20(%1) ;\n\t" \ + "movups %%xmm3,0x30(%1) ;\n\t" \ + : "=r" (cr0) \ + : "r" (xmm_save) \ + : "memory") + +#define XMMS_RESTORE \ + __asm__ __volatile__ ( \ + "sfence ;\n\t" \ + "movups (%1),%%xmm0 ;\n\t" \ + "movups 0x10(%1),%%xmm1 ;\n\t" \ + "movups 0x20(%1),%%xmm2 ;\n\t" \ + "movups 0x30(%1),%%xmm3 ;\n\t" \ + "movq %0,%%cr0 ;\n\t" \ + : \ + : "r" (cr0), "r" (xmm_save) \ + : "memory") + +#define OFFS(x) "16*("#x")" +#define PF_OFFS(x) "256+16*("#x")" +#define PF0(x) " prefetchnta "PF_OFFS(x)"(%1) ;\n" +#define LD(x,y) " movaps "OFFS(x)"(%1), %%xmm"#y" ;\n" +#define ST(x,y) " movaps %%xmm"#y", "OFFS(x)"(%1) ;\n" +#define PF1(x) " prefetchnta "PF_OFFS(x)"(%2) ;\n" +#define PF2(x) " prefetchnta "PF_OFFS(x)"(%3) ;\n" +#define PF3(x) " prefetchnta "PF_OFFS(x)"(%4) ;\n" +#define PF4(x) " prefetchnta "PF_OFFS(x)"(%5) ;\n" +#define PF5(x) " prefetchnta "PF_OFFS(x)"(%6) ;\n" +#define XO1(x,y) " xorps "OFFS(x)"(%2), %%xmm"#y" ;\n" +#define XO2(x,y) " xorps "OFFS(x)"(%3), %%xmm"#y" ;\n" +#define XO3(x,y) " xorps "OFFS(x)"(%4), %%xmm"#y" ;\n" +#define XO4(x,y) " xorps "OFFS(x)"(%5), %%xmm"#y" ;\n" +#define XO5(x,y) " xorps "OFFS(x)"(%6), %%xmm"#y" ;\n" + + +static void +xor_sse_2(unsigned long bytes, unsigned long *p1, unsigned long *p2) +{ + unsigned long lines = bytes >> 8; + char xmm_save[16*4]; + unsigned long cr0; + + XMMS_SAVE; + + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + LD(i,0) \ + LD(i+1,1) \ + PF1(i) \ + PF1(i+2) \ + LD(i+2,2) \ + LD(i+3,3) \ + PF0(i+4) \ + PF0(i+6) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + ST(i,0) \ + ST(i+1,1) \ + ST(i+2,2) \ + ST(i+3,3) \ + + + PF0(0) + PF0(2) + + " .align 32 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addq $256, %1 ;\n" + " addq $256, %2 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (p1), "r" (p2) + : "memory"); + + XMMS_RESTORE; +} + +static void +xor_sse_3(unsigned long bytes, unsigned long *p1, unsigned long *p2, + unsigned long *p3) +{ + unsigned long lines = bytes >> 8; + char xmm_save[16*4]; + unsigned long cr0; + + XMMS_SAVE; + + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + PF1(i) \ + PF1(i+2) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + PF2(i) \ + PF2(i+2) \ + PF0(i+4) \ + PF0(i+6) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + XO2(i,0) \ + XO2(i+1,1) \ + XO2(i+2,2) \ + XO2(i+3,3) \ + ST(i,0) \ + ST(i+1,1) \ + ST(i+2,2) \ + ST(i+3,3) \ + + + PF0(0) + PF0(2) + + " .align 32 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addq $256, %1 ;\n" + " addq $256, %2 ;\n" + " addq $256, %3 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (p1), "r"(p2), "r"(p3) + : "memory" ); + + XMMS_RESTORE; +} + +static void +xor_sse_4(unsigned long bytes, unsigned long *p1, unsigned long *p2, + unsigned long *p3, unsigned long *p4) +{ + unsigned long lines = bytes >> 8; + char xmm_save[16*4]; + unsigned long cr0; + + XMMS_SAVE; + + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + PF1(i) \ + PF1(i+2) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + PF2(i) \ + PF2(i+2) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + PF3(i) \ + PF3(i+2) \ + PF0(i+4) \ + PF0(i+6) \ + XO2(i,0) \ + XO2(i+1,1) \ + XO2(i+2,2) \ + XO2(i+3,3) \ + XO3(i,0) \ + XO3(i+1,1) \ + XO3(i+2,2) \ + XO3(i+3,3) \ + ST(i,0) \ + ST(i+1,1) \ + ST(i+2,2) \ + ST(i+3,3) \ + + + PF0(0) + PF0(2) + + " .align 32 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addq $256, %1 ;\n" + " addq $256, %2 ;\n" + " addq $256, %3 ;\n" + " addq $256, %4 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (p1), "r" (p2), "r" (p3), "r" (p4) + : "memory" ); + + XMMS_RESTORE; +} + +static void +xor_sse_5(unsigned long bytes, unsigned long *p1, unsigned long *p2, + unsigned long *p3, unsigned long *p4, unsigned long *p5) +{ + unsigned long lines = bytes >> 8; + char xmm_save[16*4]; + unsigned long cr0; + + XMMS_SAVE; + + __asm__ __volatile__ ( +#undef BLOCK +#define BLOCK(i) \ + PF1(i) \ + PF1(i+2) \ + LD(i,0) \ + LD(i+1,1) \ + LD(i+2,2) \ + LD(i+3,3) \ + PF2(i) \ + PF2(i+2) \ + XO1(i,0) \ + XO1(i+1,1) \ + XO1(i+2,2) \ + XO1(i+3,3) \ + PF3(i) \ + PF3(i+2) \ + XO2(i,0) \ + XO2(i+1,1) \ + XO2(i+2,2) \ + XO2(i+3,3) \ + PF4(i) \ + PF4(i+2) \ + PF0(i+4) \ + PF0(i+6) \ + XO3(i,0) \ + XO3(i+1,1) \ + XO3(i+2,2) \ + XO3(i+3,3) \ + XO4(i,0) \ + XO4(i+1,1) \ + XO4(i+2,2) \ + XO4(i+3,3) \ + ST(i,0) \ + ST(i+1,1) \ + ST(i+2,2) \ + ST(i+3,3) \ + + + PF0(0) + PF0(2) + + " .align 32 ;\n" + " 1: ;\n" + + BLOCK(0) + BLOCK(4) + BLOCK(8) + BLOCK(12) + + " addq $256, %1 ;\n" + " addq $256, %2 ;\n" + " addq $256, %3 ;\n" + " addq $256, %4 ;\n" + " addq $256, %5 ;\n" + " decq %0 ;\n" + " jnz 1b ;\n" + : + : "r" (lines), + "r" (p1), "r" (p2), "r" (p3), "r" (p4), "r" (p5) + : "memory"); + + XMMS_RESTORE; +} + +static struct xor_block_template xor_block_pIII_sse = { + name: "pIII_sse", + do_2: xor_sse_2, + do_3: xor_sse_3, + do_4: xor_sse_4, + do_5: xor_sse_5, +}; + +/* Also try the generic routines. */ +#include + +#undef XOR_TRY_TEMPLATES +#define XOR_TRY_TEMPLATES \ + do { \ + xor_speed(&xor_block_8regs); \ + xor_speed(&xor_block_32regs); \ + if (cpu_has_xmm) \ + xor_speed(&xor_block_pIII_sse); \ + if (md_cpu_has_mmx()) { \ + xor_speed(&xor_block_pII_mmx); \ + xor_speed(&xor_block_p5_mmx); \ + } \ + } while (0) + +/* We force the use of the SSE xor block because it can write around L2. + We may also be able to load into the L1 only depending on how the cpu + deals with a load to a line that is being prefetched. */ +#define XOR_SELECT_TEMPLATE(FASTEST) \ + (cpu_has_xmm ? &xor_block_pIII_sse : FASTEST) diff -Nru a/include/linux/atalk.h b/include/linux/atalk.h --- a/include/linux/atalk.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/atalk.h Tue Feb 19 18:08:58 2002 @@ -176,5 +176,7 @@ extern void aarp_cleanup_module(void); #endif /* MODULE */ +#define at_sk(__sk) ((struct atalk_sock *)(__sk)->protinfo) + #endif /* __KERNEL__ */ #endif /* __LINUX_ATALK_H__ */ diff -Nru a/include/linux/atmdev.h b/include/linux/atmdev.h --- a/include/linux/atmdev.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/atmdev.h Tue Feb 19 18:08:58 2002 @@ -33,7 +33,8 @@ #define ATM_PDU_OVHD 0 /* number of bytes to charge against buffer quota per PDU */ -#define ATM_SD(s) ((s)->sk->protinfo.af_atm) +#define atm_sk(__sk) ((struct atm_vcc *)(__sk)->protinfo) +#define ATM_SD(s) (atm_sk((s)->sk)) #define __AAL_STAT_ITEMS \ diff -Nru a/include/linux/bio.h b/include/linux/bio.h --- a/include/linux/bio.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/bio.h Tue Feb 19 18:08:58 2002 @@ -50,7 +50,7 @@ * weee, c forward decl... */ struct bio; -typedef int (bio_end_io_t) (struct bio *, int); +typedef void (bio_end_io_t) (struct bio *); typedef void (bio_destructor_t) (struct bio *); /* @@ -159,7 +159,7 @@ #define BIO_SEG_BOUNDARY(q, b1, b2) \ BIOVEC_SEG_BOUNDARY((q), __BVEC_END((b1)), __BVEC_START((b2))) -#define bio_io_error(bio) bio_endio((bio), 0, bio_sectors((bio))) +#define bio_io_error(bio) bio_endio((bio), 0) /* * drivers should not use the __ version unless they _really_ want to @@ -192,7 +192,7 @@ extern struct bio *bio_alloc(int, int); extern void bio_put(struct bio *); -extern int bio_endio(struct bio *, int, int); +extern void bio_endio(struct bio *, int); struct request_queue; extern inline int bio_phys_segments(struct request_queue *, struct bio *); extern inline int bio_hw_segments(struct request_queue *, struct bio *); diff -Nru a/include/linux/blkdev.h b/include/linux/blkdev.h --- a/include/linux/blkdev.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/blkdev.h Tue Feb 19 18:08:58 2002 @@ -314,11 +314,8 @@ extern void generic_unplug_device(void *); extern int * blk_size[MAX_BLKDEV]; - extern int * blksize_size[MAX_BLKDEV]; -extern int * max_readahead[MAX_BLKDEV]; - #define MAX_PHYS_SEGMENTS 128 #define MAX_HW_SEGMENTS 128 #define MAX_SECTORS 255 @@ -340,8 +337,6 @@ blk_size_in_bytes[major] = NULL; #endif blksize_size[major] = NULL; - max_readahead[major] = NULL; - read_ahead[major] = 0; } extern inline int get_hardsect_size(kdev_t dev) diff -Nru a/include/linux/dcache.h b/include/linux/dcache.h --- a/include/linux/dcache.h Tue Feb 19 18:09:00 2002 +++ b/include/linux/dcache.h Tue Feb 19 18:09:00 2002 @@ -5,6 +5,7 @@ #include #include +#include /* for BUG() */ /* * linux/include/linux/dcache.h diff -Nru a/include/linux/device.h b/include/linux/device.h --- a/include/linux/device.h Tue Feb 19 18:08:59 2002 +++ b/include/linux/device.h Tue Feb 19 18:08:59 2002 @@ -54,14 +54,13 @@ }; struct device; -struct iobus; struct device_driver { int (*probe) (struct device * dev); int (*remove) (struct device * dev, u32 flags); - int (*suspend) (struct device * dev, u32 state, u32 stage); - int (*resume) (struct device * dev, u32 stage); + int (*suspend) (struct device * dev, u32 state, u32 level); + int (*resume) (struct device * dev, u32 level); }; struct device { @@ -94,23 +93,6 @@ unsigned char *saved_state; /* saved device state */ }; -/* - * struct bus_type - descriptor for a type of bus - * There are some instances when you need to know what type of bus a - * device is on. Instead of having some sort of enumerated integer type, - * each struct iobus will have a pointer to a struct bus_type that gives - * actually meaningful data. - * There should be one struct bus_type for each type of bus (one for PCI, - * one for USB, etc). - */ -struct iobus_driver { - char name[16]; /* ascii descriptor of type of bus */ - struct list_head node; /* node in global list of bus drivers */ - - int (*scan) (struct iobus*); - int (*add_device) (struct iobus*, char*); -}; - static inline struct device * list_to_dev(struct list_head *node) { @@ -121,8 +103,6 @@ * High level routines for use by the bus drivers */ extern int device_register(struct device * dev); - -extern int iobus_register(struct iobus * iobus); extern int device_create_file(struct device *device, struct driver_file_entry * entry); extern void device_remove_file(struct device * dev, const char * name); diff -Nru a/include/linux/dnotify.h b/include/linux/dnotify.h --- a/include/linux/dnotify.h Tue Feb 19 18:08:57 2002 +++ b/include/linux/dnotify.h Tue Feb 19 18:08:57 2002 @@ -25,3 +25,21 @@ if ((inode)->i_dnotify_mask & (event)) __inode_dir_notify(inode, event); } + +/* + * This is hopelessly wrong, but unfixable without API changes. At + * least it doesn't oops the kernel... + */ +static inline void dnotify_parent(struct dentry *dentry, unsigned long event) +{ + struct dentry *parent; + spin_lock(&dcache_lock); + parent = dentry->d_parent; + if (parent->d_inode->i_dnotify_mask & event) { + dget(parent); + spin_unlock(&dcache_lock); + __inode_dir_notify(parent->d_inode, event); + dput(parent); + } else + spin_unlock(&dcache_lock); +} diff -Nru a/include/linux/driverfs_fs.h b/include/linux/driverfs_fs.h --- a/include/linux/driverfs_fs.h Tue Feb 19 18:08:57 2002 +++ b/include/linux/driverfs_fs.h Tue Feb 19 18:08:57 2002 @@ -41,7 +41,6 @@ char * name; mode_t mode; struct dentry * dentry; - void * data; ssize_t (*show)(struct device * dev, char * buf, size_t count, loff_t off); ssize_t (*store)(struct device * dev, const char * buf, size_t count, loff_t off); diff -Nru a/include/linux/fs.h b/include/linux/fs.h --- a/include/linux/fs.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/fs.h Tue Feb 19 18:08:58 2002 @@ -173,10 +173,12 @@ #define BLKRRPART _IO(0x12,95) /* re-read partition table */ #define BLKGETSIZE _IO(0x12,96) /* return device size /512 (long *arg) */ #define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ -#define BLKRASET _IO(0x12,98) /* Set read ahead for block device */ +#if 0 /* Obsolete, these don't do anything. */ +#define BLKRASET _IO(0x12,98) /* set read ahead for block device */ #define BLKRAGET _IO(0x12,99) /* get current read ahead setting */ #define BLKFRASET _IO(0x12,100)/* set filesystem (mm/filemap.c) read-ahead */ #define BLKFRAGET _IO(0x12,101)/* get filesystem (mm/filemap.c) read-ahead */ +#endif #define BLKSECTSET _IO(0x12,102)/* set max sectors per request (ll_rw_blk.c) */ #define BLKSECTGET _IO(0x12,103)/* get max sectors per request (ll_rw_blk.c) */ #define BLKSSZGET _IO(0x12,104)/* get block device sector size */ @@ -425,7 +427,6 @@ unsigned long i_blocks; unsigned long i_version; struct semaphore i_sem; - struct semaphore i_zombie; struct inode_operations *i_op; struct file_operations *i_fop; /* former ->i_op->default_file_ops */ struct super_block *i_sb; @@ -759,6 +760,9 @@ extern int vfs_unlink(struct inode *, struct dentry *); extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); +extern struct dentry *lock_rename(struct dentry *, struct dentry *); +extern void unlock_rename(struct dentry *, struct dentry *); + /* * File types */ @@ -833,10 +837,10 @@ int (*revalidate) (struct dentry *); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct dentry *, struct iattr *); - int (*setxattr) (struct dentry *, char *, void *, size_t, int); - int (*getxattr) (struct dentry *, char *, void *, size_t); - int (*listxattr) (struct dentry *, char *, size_t); - int (*removexattr) (struct dentry *, char *); + int (*setxattr) (struct dentry *, const char *, void *, size_t, int); + ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); + ssize_t (*listxattr) (struct dentry *, char *, size_t); + int (*removexattr) (struct dentry *, const char *); }; struct seq_file; @@ -1228,8 +1232,8 @@ return fsync_buffers_list(&inode->i_dirty_data_buffers); } extern int inode_has_buffers(struct inode *); -extern void filemap_fdatasync(struct address_space *); -extern void filemap_fdatawait(struct address_space *); +extern int filemap_fdatasync(struct address_space *); +extern int filemap_fdatawait(struct address_space *); extern void sync_supers(kdev_t); extern int bmap(struct inode *, int); extern int notify_change(struct dentry *, struct iattr *); @@ -1438,8 +1442,9 @@ int generic_commit_write(struct file *, struct page *, unsigned, unsigned); int block_truncate_page(struct address_space *, loff_t, get_block_t *); extern int generic_direct_IO(int, struct inode *, struct kiobuf *, unsigned long, int, get_block_t *); +extern int waitfor_one_page(struct page *); +extern int writeout_one_page(struct page *); -extern int waitfor_one_page(struct page*); extern int generic_file_mmap(struct file *, struct vm_area_struct *); extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size); extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *); @@ -1490,8 +1495,6 @@ extern ssize_t char_read(struct file *, char *, size_t, loff_t *); extern ssize_t block_read(struct file *, char *, size_t, loff_t *); -extern int read_ahead[]; - extern ssize_t char_write(struct file *, const char *, size_t, loff_t *); extern ssize_t block_write(struct file *, const char *, size_t, loff_t *); @@ -1505,129 +1508,13 @@ extern int inode_change_ok(struct inode *, struct iattr *); extern int inode_setattr(struct inode *, struct iattr *); -/* - * Common dentry functions for inclusion in the VFS - * or in other stackable file systems. Some of these - * functions were in linux/fs/ C (VFS) files. - * - */ - -/* - * Locking the parent is needed to: - * - serialize directory operations - * - make sure the parent doesn't change from - * under us in the middle of an operation. - * - * NOTE! Right now we'd rather use a "struct inode" - * for this, but as I expect things to move toward - * using dentries instead for most things it is - * probably better to start with the conceptually - * better interface of relying on a path of dentries. - */ -static inline struct dentry *lock_parent(struct dentry *dentry) -{ - struct dentry *dir = dget(dentry->d_parent); - - down(&dir->d_inode->i_sem); - return dir; -} - -static inline struct dentry *get_parent(struct dentry *dentry) -{ - return dget(dentry->d_parent); -} - -static inline void unlock_dir(struct dentry *dir) -{ - up(&dir->d_inode->i_sem); - dput(dir); -} - -/* - * Whee.. Deadlock country. Happily there are only two VFS - * operations that does this.. - */ -static inline void double_down(struct semaphore *s1, struct semaphore *s2) -{ - if (s1 != s2) { - if ((unsigned long) s1 < (unsigned long) s2) { - struct semaphore *tmp = s2; - s2 = s1; s1 = tmp; - } - down(s1); - } - down(s2); -} - -/* - * Ewwwwwwww... _triple_ lock. We are guaranteed that the 3rd argument is - * not equal to 1st and not equal to 2nd - the first case (target is parent of - * source) would be already caught, the second is plain impossible (target is - * its own parent and that case would be caught even earlier). Very messy. - * I _think_ that it works, but no warranties - please, look it through. - * Pox on bloody lusers who mandated overwriting rename() for directories... - */ - -static inline void triple_down(struct semaphore *s1, - struct semaphore *s2, - struct semaphore *s3) -{ - if (s1 != s2) { - if ((unsigned long) s1 < (unsigned long) s2) { - if ((unsigned long) s1 < (unsigned long) s3) { - struct semaphore *tmp = s3; - s3 = s1; s1 = tmp; - } - if ((unsigned long) s1 < (unsigned long) s2) { - struct semaphore *tmp = s2; - s2 = s1; s1 = tmp; - } - } else { - if ((unsigned long) s1 < (unsigned long) s3) { - struct semaphore *tmp = s3; - s3 = s1; s1 = tmp; - } - if ((unsigned long) s2 < (unsigned long) s3) { - struct semaphore *tmp = s3; - s3 = s2; s2 = tmp; - } - } - down(s1); - } else if ((unsigned long) s2 < (unsigned long) s3) { - struct semaphore *tmp = s3; - s3 = s2; s2 = tmp; - } - down(s2); - down(s3); -} - -static inline void double_up(struct semaphore *s1, struct semaphore *s2) -{ - up(s1); - if (s1 != s2) - up(s2); -} - -static inline void triple_up(struct semaphore *s1, - struct semaphore *s2, - struct semaphore *s3) -{ - up(s1); - if (s1 != s2) - up(s2); - up(s3); -} - -static inline void double_lock(struct dentry *d1, struct dentry *d2) -{ - double_down(&d1->d_inode->i_sem, &d2->d_inode->i_sem); -} - -static inline void double_unlock(struct dentry *d1, struct dentry *d2) +static inline ino_t parent_ino(struct dentry *dentry) { - double_up(&d1->d_inode->i_sem,&d2->d_inode->i_sem); - dput(d1); - dput(d2); + ino_t res; + spin_lock(&dcache_lock); + res = dentry->d_parent->d_inode->i_ino; + spin_unlock(&dcache_lock); + return res; } #endif /* __KERNEL__ */ diff -Nru a/include/linux/hfs_fs.h b/include/linux/hfs_fs.h --- a/include/linux/hfs_fs.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/hfs_fs.h Tue Feb 19 18:08:58 2002 @@ -267,7 +267,7 @@ /* file.c */ extern hfs_s32 hfs_do_read(struct inode *, struct hfs_fork *, hfs_u32, - char *, hfs_u32, int); + char *, hfs_u32); extern hfs_s32 hfs_do_write(struct inode *, struct hfs_fork *, hfs_u32, const char *, hfs_u32); extern void hfs_file_fix_mode(struct hfs_cat_entry *entry); diff -Nru a/include/linux/highmem.h b/include/linux/highmem.h --- a/include/linux/highmem.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/highmem.h Tue Feb 19 18:08:58 2002 @@ -4,7 +4,6 @@ #include #include #include -#include #ifdef CONFIG_HIGHMEM @@ -16,6 +15,7 @@ unsigned int nr_free_highpages(void); extern void create_bounce(unsigned long pfn, int gfp, struct bio **bio_orig); +extern void check_highmem_ptes(void); static inline char *bh_kmap(struct buffer_head *bh) { @@ -92,8 +92,9 @@ static inline void clear_highpage(struct page *page) { - clear_page(kmap(page)); - kunmap(page); + void *kaddr = kmap_atomic(page, KM_USER0); + clear_page(kaddr); + kunmap_atomic(kaddr, KM_USER0); } /* @@ -101,15 +102,16 @@ */ static inline void memclear_highpage_flush(struct page *page, unsigned int offset, unsigned int size) { - char *kaddr; + void *kaddr; if (offset + size > PAGE_SIZE) BUG(); - kaddr = kmap(page); - memset(kaddr + offset, 0, size); + + kaddr = kmap_atomic(page, KM_USER0); + memset((char *)kaddr + offset, 0, size); flush_dcache_page(page); flush_page_to_ram(page); - kunmap(page); + kunmap_atomic(kaddr, KM_USER0); } static inline void copy_user_highpage(struct page *to, struct page *from, unsigned long vaddr) diff -Nru a/include/linux/ide.h b/include/linux/ide.h --- a/include/linux/ide.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/ide.h Tue Feb 19 18:08:58 2002 @@ -99,8 +99,8 @@ #undef REALLY_FAST_IO #endif -#define HWIF(drive) ((ide_hwif_t *)((drive)->hwif)) -#define HWGROUP(drive) ((ide_hwgroup_t *)(HWIF(drive)->hwgroup)) +#define HWIF(drive) ((drive)->hwif) +#define HWGROUP(drive) (HWIF(drive)->hwgroup) /* * Definitions for accessing IDE controller registers @@ -367,9 +367,11 @@ } b; } special_t; +struct ide_settings_s; + typedef struct ide_drive_s { request_queue_t queue; /* request queue */ - struct ide_drive_s *next; /* circular list of hwgroup drives */ + struct ide_drive_s *next; /* circular list of hwgroup drives */ unsigned long sleep; /* sleep until this time */ unsigned long service_start; /* time we started last request */ unsigned long service_time; /* service time of last request */ @@ -415,7 +417,7 @@ byte nowerr; /* used for ignoring WRERR_STAT */ byte sect0; /* offset of first sector for DM6:DDO */ unsigned int usage; /* current "open()" count for drive */ - byte head; /* "real" number of heads */ + byte head; /* "real" number of heads */ byte sect; /* "real" sectors per track */ byte bios_head; /* BIOS/fdisk/LILO number of heads */ byte bios_sect; /* BIOS/fdisk/LILO sectors per track */ @@ -424,16 +426,16 @@ unsigned long capacity; /* total number of sectors */ unsigned long long capacity48; /* total number of sectors */ unsigned int drive_data; /* for use by tuneproc/selectproc as needed */ - void *hwif; /* actually (ide_hwif_t *) */ + struct hwif_s *hwif; /* actually (ide_hwif_t *) */ wait_queue_head_t wqueue; /* used to wait for drive in open() */ struct hd_driveid *id; /* drive model identification info */ struct hd_struct *part; /* drive partition table */ char name[4]; /* drive name, such as "hda" */ - void *driver; /* (ide_driver_t *) */ + struct ide_driver_s *driver; /* (ide_driver_t *) */ void *driver_data; /* extra driver data */ devfs_handle_t de; /* directory for device */ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ - void *settings; /* /proc/ide/ drive settings */ + struct ide_settings_s *settings; /* /proc/ide/ drive settings */ char driver_req[10]; /* requests specific driver */ int last_lun; /* last logical unit */ int forced_lun; /* if hdxlun was given at boot */ @@ -529,7 +531,7 @@ typedef struct hwif_s { struct hwif_s *next; /* for linked-list in ide_hwgroup_t */ - void *hwgroup; /* actually (ide_hwgroup_t *) */ + struct hwgroup_s *hwgroup; /* actually (ide_hwgroup_t *) */ ide_ioreg_t io_ports[IDE_NR_PORTS]; /* task file registers */ hw_regs_t hw; /* Hardware info */ ide_drive_t drives[MAX_DRIVES]; /* drive info */ @@ -652,7 +654,6 @@ void ide_add_setting(ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set); void ide_remove_setting(ide_drive_t *drive, char *name); -ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name); int ide_read_setting(ide_drive_t *t, ide_settings_t *setting); int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val); void ide_add_generic_settings(ide_drive_t *drive); @@ -701,65 +702,35 @@ /* * Subdrivers support. */ -#define IDE_SUBDRIVER_VERSION 1 - -typedef int (ide_cleanup_proc)(ide_drive_t *); -typedef int (ide_standby_proc)(ide_drive_t *); -typedef int (ide_flushcache_proc)(ide_drive_t *); -typedef ide_startstop_t (ide_do_request_proc)(ide_drive_t *, struct request *, unsigned long); -typedef void (ide_end_request_proc)(byte, ide_hwgroup_t *); -typedef int (ide_ioctl_proc)(ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long); -typedef int (ide_open_proc)(struct inode *, struct file *, ide_drive_t *); -typedef void (ide_release_proc)(struct inode *, struct file *, ide_drive_t *); -typedef int (ide_check_media_change_proc)(ide_drive_t *); -typedef void (ide_revalidate_proc)(ide_drive_t *); -typedef void (ide_pre_reset_proc)(ide_drive_t *); -typedef unsigned long (ide_capacity_proc)(ide_drive_t *); -typedef ide_startstop_t (ide_special_proc)(ide_drive_t *); -typedef void (ide_setting_proc)(ide_drive_t *); -typedef int (ide_driver_reinit_proc)(ide_drive_t *); - typedef struct ide_driver_s { const char *name; - const char *version; byte media; unsigned busy : 1; unsigned supports_dma : 1; unsigned supports_dsc_overlap : 1; - ide_cleanup_proc *cleanup; - ide_standby_proc *standby; - ide_flushcache_proc *flushcache; - ide_do_request_proc *do_request; - ide_end_request_proc *end_request; - ide_ioctl_proc *ioctl; - ide_open_proc *open; - ide_release_proc *release; - ide_check_media_change_proc *media_change; - ide_revalidate_proc *revalidate; - ide_pre_reset_proc *pre_reset; - ide_capacity_proc *capacity; - ide_special_proc *special; + int (*cleanup)(ide_drive_t *); + int (*standby)(ide_drive_t *); + int (*flushcache)(ide_drive_t *); + ide_startstop_t (*do_request)(ide_drive_t *, struct request *, unsigned long); + int (*end_request)(ide_drive_t *drive, int uptodate); + int (*ioctl)(ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long); + int (*open)(struct inode *, struct file *, ide_drive_t *); + void (*release)(struct inode *, struct file *, ide_drive_t *); + int (*media_change)(ide_drive_t *); + void (*revalidate)(ide_drive_t *); + void (*pre_reset)(ide_drive_t *); + unsigned long (*capacity)(ide_drive_t *); + ide_startstop_t (*special)(ide_drive_t *); ide_proc_entry_t *proc; - ide_driver_reinit_proc *driver_reinit; -} ide_driver_t; - -#define DRIVER(drive) ((ide_driver_t *)((drive)->driver)) - -/* - * IDE modules. - */ -#define IDE_CHIPSET_MODULE 0 /* not supported yet */ -#define IDE_PROBE_MODULE 1 -#define IDE_DRIVER_MODULE 2 + int (*driver_init)(void); + int (*driver_reinit)(ide_drive_t *); -typedef int (ide_module_init_proc)(void); + /* FIXME: Single linked list of drivers for iteration. + */ + struct ide_driver_s *next; +} ide_driver_t; -typedef struct ide_module_s { - int type; - ide_module_init_proc *init; - void *info; - struct ide_module_s *next; -} ide_module_t; +#define DRIVER(drive) ((drive)->driver) /* * ide_hwifs[] is the master data structure used to keep track @@ -770,9 +741,8 @@ * */ #ifndef _IDE_C -extern ide_hwif_t ide_hwifs[]; /* master data repository */ -extern ide_module_t *ide_modules; -extern ide_module_t *ide_probe; +extern struct hwif_s ide_hwifs[]; /* master data repository */ +extern struct ide_driver_s *ide_drivers; #endif extern int noautodma; @@ -783,10 +753,8 @@ #define LOCAL_END_REQUEST /* Don't generate end_request in blk.h */ #include -inline int __ide_end_request(ide_hwgroup_t *, int, int); -int ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup); - -int drive_is_ready (ide_drive_t *drive); +extern int __ide_end_request(ide_drive_t *drive, int uptodate, int nr_secs); +extern int ide_end_request(ide_drive_t *drive, int uptodate); /* * This is used on exit from the driver, to designate the next irq handler @@ -1077,9 +1045,11 @@ int ide_reinit_drive (ide_drive_t *drive); #ifdef _IDE_C -#ifdef CONFIG_BLK_DEV_IDE -int ideprobe_init (void); -#endif /* CONFIG_BLK_DEV_IDE */ +# ifdef CONFIG_BLK_DEV_IDE +/* Probe for devices attached to the systems host controllers. + */ +extern int ideprobe_init (void); +# endif #ifdef CONFIG_BLK_DEV_IDEDISK int idedisk_reinit (ide_drive_t *drive); int idedisk_init (void); @@ -1102,12 +1072,12 @@ #endif /* CONFIG_BLK_DEV_IDESCSI */ #endif /* _IDE_C */ -int ide_register_module (ide_module_t *module); -void ide_unregister_module (ide_module_t *module); +extern int ide_register_module (struct ide_driver_s *d); +extern void ide_unregister_module (struct ide_driver_s *d); ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n); -int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version); -int ide_unregister_subdriver (ide_drive_t *drive); -int ide_replace_subdriver(ide_drive_t *drive, const char *driver); +extern int ide_register_subdriver(ide_drive_t *drive, ide_driver_t *driver); +extern int ide_unregister_subdriver(ide_drive_t *drive); +extern int ide_replace_subdriver(ide_drive_t *drive, const char *driver); #ifdef CONFIG_BLK_DEV_IDEPCI #define ON_BOARD 1 @@ -1118,7 +1088,6 @@ # define OFF_BOARD NEVER_BOARD #endif /* CONFIG_BLK_DEV_OFFBOARD */ -unsigned long ide_find_free_region (unsigned short size) __init; void ide_scan_pcibus (int scan_direction) __init; #endif #ifdef CONFIG_BLK_DEV_IDEDMA @@ -1132,14 +1101,12 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive); int ide_release_dma (ide_hwif_t *hwif); void ide_setup_dma (ide_hwif_t *hwif, unsigned long dmabase, unsigned int num_ports) __init; -unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init; +/* FIXME spilt this up into a get and set function */ +extern unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init; #endif -void hwif_unregister (ide_hwif_t *hwif); - -void export_ide_init_queue (ide_drive_t *drive); -byte export_probe_for_drive (ide_drive_t *drive); - extern spinlock_t ide_lock; + +extern int drive_is_ready(ide_drive_t *drive); #endif /* _IDE_H */ diff -Nru a/include/linux/if_ec.h b/include/linux/if_ec.h --- a/include/linux/if_ec.h Tue Feb 19 18:08:57 2002 +++ b/include/linux/if_ec.h Tue Feb 19 18:08:57 2002 @@ -55,6 +55,8 @@ unsigned char net; }; +#define ec_sk(__sk) ((struct econet_opt *)(__sk)->protinfo) + struct ec_device { unsigned char station, net; /* Econet protocol address */ diff -Nru a/include/linux/if_pppox.h b/include/linux/if_pppox.h --- a/include/linux/if_pppox.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/if_pppox.h Tue Feb 19 18:08:58 2002 @@ -113,6 +113,26 @@ } __attribute__ ((packed)); #ifdef __KERNEL__ +struct pppoe_opt { + struct net_device *dev; /* device associated with socket*/ + struct pppoe_addr pa; /* what this socket is bound to*/ + struct sockaddr_pppox relay; /* what socket data will be + relayed to (PPPoE relaying) */ +}; + +struct pppox_opt { + struct ppp_channel chan; + struct sock *sk; + struct pppox_opt *next; /* for hash table */ + union { + struct pppoe_opt pppoe; + } proto; +}; +#define pppoe_dev proto.pppoe.dev +#define pppoe_pa proto.pppoe.pa +#define pppoe_relay proto.pppoe.relay + +#define pppox_sk(__sk) ((struct pppox_opt *)(__sk)->protinfo) struct pppox_proto { int (*create)(struct socket *sock); diff -Nru a/include/linux/if_wanpipe.h b/include/linux/if_wanpipe.h --- a/include/linux/if_wanpipe.h Tue Feb 19 18:08:59 2002 +++ b/include/linux/if_wanpipe.h Tue Feb 19 18:08:59 2002 @@ -123,6 +123,9 @@ unsigned char force; /* Used to force sock release */ atomic_t packet_sent; }; + +#define wp_sk(__sk) ((struct wanpipe_opt *)(__sk)->protinfo) + #endif #endif diff -Nru a/include/linux/igmp.h b/include/linux/igmp.h --- a/include/linux/igmp.h Tue Feb 19 18:09:00 2002 +++ b/include/linux/igmp.h Tue Feb 19 18:09:00 2002 @@ -76,6 +76,8 @@ */ #ifdef __KERNEL__ +#include +#include /* ip_mc_socklist is real list now. Speed is not argument; this list never used in fast path code diff -Nru a/include/linux/init.h b/include/linux/init.h --- a/include/linux/init.h Tue Feb 19 18:08:57 2002 +++ b/include/linux/init.h Tue Feb 19 18:08:57 2002 @@ -174,11 +174,13 @@ #define __devinitdata #define __devexit #define __devexitdata +#define __devexit_p(x) &(x) #else #define __devinit __init #define __devinitdata __initdata #define __devexit __exit #define __devexitdata __exitdata +#define __devexit_p(x) 0 #endif #endif /* _LINUX_INIT_H */ diff -Nru a/include/linux/init_task.h b/include/linux/init_task.h --- a/include/linux/init_task.h Tue Feb 19 18:08:57 2002 +++ b/include/linux/init_task.h Tue Feb 19 18:08:57 2002 @@ -45,7 +45,8 @@ thread_info: &init_thread_info, \ flags: 0, \ lock_depth: -1, \ - __nice: DEF_USER_NICE, \ + prio: 120, \ + static_prio: 120, \ policy: SCHED_OTHER, \ cpus_allowed: -1, \ mm: NULL, \ diff -Nru a/include/linux/ip.h b/include/linux/ip.h --- a/include/linux/ip.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/ip.h Tue Feb 19 18:08:58 2002 @@ -89,6 +89,9 @@ #define IPOPT_TS_PRESPEC 3 /* specified modules only */ #ifdef __KERNEL__ +#include +#include +#include struct ip_options { __u32 faddr; /* Saved first hop address */ @@ -111,6 +114,37 @@ }; #define optlength(opt) (sizeof(struct ip_options) + opt->optlen) + +struct inet_opt { + int ttl; /* TTL setting */ + int tos; /* TOS */ + unsigned cmsg_flags; + struct ip_options *opt; + unsigned char hdrincl; /* Include headers ? */ + __u8 mc_ttl; /* Multicasting TTL */ + __u8 mc_loop; /* Loopback */ + unsigned recverr : 1, + freebind : 1; + __u16 id; /* ID counter for DF pkts */ + __u8 pmtudisc; + int mc_index; /* Multicast device index */ + __u32 mc_addr; + struct ip_mc_socklist *mc_list; /* Group array */ +}; + +struct ipv6_pinfo; + +/* WARNING: don't change the layout of the members in inet_sock! */ +struct inet_sock { + struct sock sk; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct ipv6_pinfo *pinet6; +#endif + struct inet_opt inet; +}; + +#define inet_sk(__sk) (&((struct inet_sock *)__sk)->inet) + #endif struct iphdr { diff -Nru a/include/linux/ipv6.h b/include/linux/ipv6.h --- a/include/linux/ipv6.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/ipv6.h Tue Feb 19 18:08:58 2002 @@ -101,6 +101,10 @@ }; #ifdef __KERNEL__ +#include /* struct sockaddr_in6 */ +#include +#include /* struct ipv6_mc_socklist */ +#include /* This structure contains results of exthdrs parsing @@ -118,6 +122,79 @@ __u16 dst1; }; +struct ipv6_pinfo { + struct in6_addr saddr; + struct in6_addr rcv_saddr; + struct in6_addr daddr; + struct in6_addr *daddr_cache; + + __u32 flow_label; + __u32 frag_size; + int hop_limit; + int mcast_hops; + int mcast_oif; + + /* pktoption flags */ + union { + struct { + __u8 srcrt:2, + rxinfo:1, + rxhlim:1, + hopopts:1, + dstopts:1, + authhdr:1, + rxflow:1; + } bits; + __u8 all; + } rxopt; + + /* sockopt flags */ + __u8 mc_loop:1, + recverr:1, + sndflow:1, + pmtudisc:2; + + struct ipv6_mc_socklist *ipv6_mc_list; + struct ipv6_fl_socklist *ipv6_fl_list; + __u32 dst_cookie; + + struct ipv6_txoptions *opt; + struct sk_buff *pktoptions; +}; + +struct raw6_opt { + __u32 checksum; /* perform checksum */ + __u32 offset; /* checksum offset */ + + struct icmp6_filter filter; +}; + +/* WARNING: don't change the layout of the members in {raw,udp,tcp}6_sock! */ +struct raw6_sock { + struct sock sk; + struct ipv6_pinfo *pinet6; + struct inet_opt inet; + struct raw6_opt raw6; + struct ipv6_pinfo inet6; +}; + +struct udp6_sock { + struct sock sk; + struct ipv6_pinfo *pinet6; + struct inet_opt inet; + struct ipv6_pinfo inet6; +}; + +struct tcp6_sock { + struct sock sk; + struct ipv6_pinfo *pinet6; + struct inet_opt inet; + struct tcp_opt tcp; + struct ipv6_pinfo inet6; +}; + +#define inet6_sk(__sk) ((struct raw6_sock *)__sk)->pinet6 +#define raw6_sk(__sk) (&((struct raw6_sock *)__sk)->raw6) #endif #endif diff -Nru a/include/linux/mm.h b/include/linux/mm.h --- a/include/linux/mm.h Tue Feb 19 18:08:57 2002 +++ b/include/linux/mm.h Tue Feb 19 18:08:57 2002 @@ -157,12 +157,23 @@ updated asynchronously */ struct list_head lru; /* Pageout list, eg. active_list; protected by pagemap_lru_lock !! */ - wait_queue_head_t wait; /* Page locked? Stand in line... */ struct page **pprev_hash; /* Complement to *next_hash. */ struct buffer_head * buffers; /* Buffer maps us to a disk block. */ + + /* + * On machines where all RAM is mapped into kernel address space, + * we can simply calculate the virtual address. On machines with + * highmem some memory is mapped into kernel virtual memory + * dynamically, so we need a place to store that address. + * Note that this field could be 16 bits on x86 ... ;) + * + * Architectures with slow multiplication can define + * WANT_PAGE_VIRTUAL in asm/page.h + */ +#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL) void *virtual; /* Kernel virtual address (NULL if not kmapped, ie. highmem) */ - struct zone_struct *zone; /* Memory zone we are in. */ +#endif /* CONFIG_HIGMEM || WANT_PAGE_VIRTUAL */ } mem_map_t; /* @@ -183,6 +194,11 @@ #define page_count(p) atomic_read(&(p)->count) #define set_page_count(p,v) atomic_set(&(p)->count, v) +static inline void init_page_count(struct page *page) +{ + page->count.counter = 0; +} + /* * Various page->flags bits: * @@ -237,7 +253,7 @@ * - private pages which have been modified may need to be swapped out * to swap space and (later) to be read back into memory. * During disk I/O, PG_locked is used. This bit is set before I/O - * and reset when I/O completes. page->wait is a wait queue of all + * and reset when I/O completes. page_waitqueue(page) is a wait queue of all * tasks waiting for the I/O on this page to complete. * PG_uptodate tells whether the page's contents is valid. * When a read completes, the page becomes uptodate, unless a disk I/O @@ -299,6 +315,61 @@ #define SetPageChecked(page) set_bit(PG_checked, &(page)->flags) #define PageLaunder(page) test_bit(PG_launder, &(page)->flags) #define SetPageLaunder(page) set_bit(PG_launder, &(page)->flags) +#define __SetPageReserved(page) __set_bit(PG_reserved, &(page)->flags) + +/* + * The zone field is never updated after free_area_init_core() + * sets it, so none of the operations on it need to be atomic. + */ +#define NODE_SHIFT 4 +#define ZONE_SHIFT (BITS_PER_LONG - 8) + +struct zone_struct; +extern struct zone_struct *zone_table[]; + +static inline zone_t *page_zone(struct page *page) +{ + return zone_table[page->flags >> ZONE_SHIFT]; +} + +static inline void set_page_zone(struct page *page, unsigned long zone_num) +{ + page->flags &= ~(~0UL << ZONE_SHIFT); + page->flags |= zone_num << ZONE_SHIFT; +} + +/* + * In order to avoid #ifdefs within C code itself, we define + * set_page_address to a noop for non-highmem machines, where + * the field isn't useful. + * The same is true for page_address() in arch-dependent code. + */ +#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL) + +#define set_page_address(page, address) \ + do { \ + (page)->virtual = (address); \ + } while(0) + +#else /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */ +#define set_page_address(page, address) do { } while(0) +#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */ + +/* + * Permanent address of a page. Obviously must never be + * called on a highmem page. + */ +#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL) + +#define page_address(page) ((page)->virtual) + +#else /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */ + +#define page_address(page) \ + __va( (((page) - page_zone(page)->zone_mem_map) << PAGE_SHIFT) \ + + page_zone(page)->zone_start_paddr) + +#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */ extern void FASTCALL(set_page_dirty(struct page *)); @@ -364,7 +435,8 @@ extern int vmtruncate(struct inode * inode, loff_t offset); extern pmd_t *FASTCALL(__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)); -extern pte_t *FASTCALL(pte_alloc(struct mm_struct *mm, pmd_t *pmd, unsigned long address)); +extern pte_t *FASTCALL(pte_alloc_kernel(struct mm_struct *mm, pmd_t *pmd, unsigned long address)); +extern pte_t *FASTCALL(pte_alloc_map(struct mm_struct *mm, pmd_t *pmd, unsigned long address)); extern int handle_mm_fault(struct mm_struct *mm,struct vm_area_struct *vma, unsigned long address, int write_access); extern int make_pages_present(unsigned long addr, unsigned long end); extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); @@ -380,7 +452,7 @@ /* * On a two-level page table, this ends up being trivial. Thus the - * inlining and the symmetry break with pte_alloc() that does all + * inlining and the symmetry break with pte_alloc_map() that does all * of this out-of-line. */ static inline pmd_t *pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address) @@ -390,9 +462,6 @@ return pmd_offset(pgd, address); } -extern int pgt_cache_water[2]; -extern int check_pgt_cache(void); - extern void free_area_init(unsigned long * zones_size); extern void free_area_init_node(int nid, pg_data_t *pgdat, struct page *pmap, unsigned long * zones_size, unsigned long zone_start_paddr, @@ -515,6 +584,8 @@ } extern struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr); + +extern struct page * vmalloc_to_page(unsigned long adr); #endif /* __KERNEL__ */ diff -Nru a/include/linux/mmzone.h b/include/linux/mmzone.h --- a/include/linux/mmzone.h Tue Feb 19 18:08:57 2002 +++ b/include/linux/mmzone.h Tue Feb 19 18:08:57 2002 @@ -7,6 +7,7 @@ #include #include #include +#include /* * Free memory management - zoned buddy allocator. @@ -48,6 +49,35 @@ free_area_t free_area[MAX_ORDER]; /* + * wait_table -- the array holding the hash table + * wait_table_size -- the size of the hash table array + * wait_table_shift -- wait_table_size + * == BITS_PER_LONG (1 << wait_table_bits) + * + * The purpose of all these is to keep track of the people + * waiting for a page to become available and make them + * runnable again when possible. The trouble is that this + * consumes a lot of space, especially when so few things + * wait on pages at a given time. So instead of using + * per-page waitqueues, we use a waitqueue hash table. + * + * The bucket discipline is to sleep on the same queue when + * colliding and wake all in that wait queue when removing. + * When something wakes, it must check to be sure its page is + * truly available, a la thundering herd. The cost of a + * collision is great, but given the expected load of the + * table, they should be so rare as to be outweighed by the + * benefits from the saved space. + * + * __wait_on_page() and unlock_page() in mm/filemap.c, are the + * primary users of these fields, and in mm/page_alloc.c + * free_area_init_core() performs the initialization of them. + */ + wait_queue_head_t * wait_table; + unsigned long wait_table_size; + unsigned long wait_table_shift; + + /* * Discontig memory support fields. */ struct pglist_data *zone_pgdat; @@ -132,10 +162,14 @@ #define NODE_DATA(nid) (&contig_page_data) #define NODE_MEM_MAP(nid) mem_map +#define MAX_NR_NODES 1 #else /* !CONFIG_DISCONTIGMEM */ #include + +/* page->zone is currently 8 bits ... */ +#define MAX_NR_NODES (255 / MAX_NR_ZONES) #endif /* !CONFIG_DISCONTIGMEM */ diff -Nru a/include/linux/nbd.h b/include/linux/nbd.h --- a/include/linux/nbd.h Tue Feb 19 18:08:59 2002 +++ b/include/linux/nbd.h Tue Feb 19 18:08:59 2002 @@ -52,7 +52,7 @@ blk_finished_io(nsect); req->bio = bio->bi_next; bio->bi_next = NULL; - bio_endio(bio, uptodate, nsect); + bio_endio(bio, uptodate); } blkdev_release_request(req); spin_unlock_irqrestore(q->queue_lock, flags); diff -Nru a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h --- a/include/linux/netfilter_ipv4.h Tue Feb 19 18:08:58 2002 +++ b/include/linux/netfilter_ipv4.h Tue Feb 19 18:08:58 2002 @@ -59,95 +59,20 @@ NF_IP_PRI_LAST = INT_MAX, }; -#ifdef CONFIG_NETFILTER_DEBUG -#ifdef __KERNEL__ -void nf_debug_ip_local_deliver(struct sk_buff *skb); -void nf_debug_ip_loopback_xmit(struct sk_buff *newskb); -void nf_debug_ip_finish_output2(struct sk_buff *skb); -#endif /*__KERNEL__*/ -#endif /*CONFIG_NETFILTER_DEBUG*/ - /* Arguments for setsockopt SOL_IP: */ /* 2.0 firewalling went from 64 through 71 (and +256, +512, etc). */ /* 2.2 firewalling (+ masq) went from 64 through 76 */ /* 2.4 firewalling went 64 through 67. */ #define SO_ORIGINAL_DST 80 +#ifdef __KERNEL__ +#ifdef CONFIG_NETFILTER_DEBUG +void nf_debug_ip_local_deliver(struct sk_buff *skb); +void nf_debug_ip_loopback_xmit(struct sk_buff *newskb); +void nf_debug_ip_finish_output2(struct sk_buff *skb); +#endif /*CONFIG_NETFILTER_DEBUG*/ -/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue - * - * Ideally this would be ins some netfilter_utility module, but creating this - * module for just one function doesn't make sense. -HW */ - -#include -#include -#include -#include -#include -#include - -static inline int route_me_harder(struct sk_buff **pskb) -{ - struct iphdr *iph = (*pskb)->nh.iph; - struct rtable *rt; - struct rt_key key = { dst:iph->daddr, - src:iph->saddr, - oif:(*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0, - tos:RT_TOS(iph->tos)|RTO_CONN, -#ifdef CONFIG_IP_ROUTE_FWMARK - fwmark:(*pskb)->nfmark -#endif - }; - struct net_device *dev_src = NULL; - int err; - - /* accomodate ip_route_output_slow(), which expects the key src to be - 0 or a local address; however some non-standard hacks like - ipt_REJECT.c:send_reset() can cause packets with foreign - saddr to be appear on the NF_IP_LOCAL_OUT hook -MB */ - if(key.src && !(dev_src = ip_dev_find(key.src))) - key.src = 0; - - if ((err=ip_route_output_key(&rt, &key)) != 0) { - printk("route_me_harder: ip_route_output_key(dst=%u.%u.%u.%u, src=%u.%u.%u.%u, oif=%d, tos=0x%x, fwmark=0x%lx) error %d\n", - NIPQUAD(iph->daddr), NIPQUAD(iph->saddr), - (*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0, - RT_TOS(iph->tos)|RTO_CONN, -#ifdef CONFIG_IP_ROUTE_FWMARK - (*pskb)->nfmark, -#else - 0UL, -#endif - err); - goto out; - } - - /* Drop old route. */ - dst_release((*pskb)->dst); - - (*pskb)->dst = &rt->u.dst; - - /* Change in oif may mean change in hh_len. */ - if (skb_headroom(*pskb) < (*pskb)->dst->dev->hard_header_len) { - struct sk_buff *nskb; - - nskb = skb_realloc_headroom(*pskb, - (*pskb)->dst->dev->hard_header_len); - if (!nskb) { - err = -ENOMEM; - goto out; - } - if ((*pskb)->sk) - skb_set_owner_w(nskb, (*pskb)->sk); - kfree_skb(*pskb); - *pskb = nskb; - } - -out: - if (dev_src) - dev_put(dev_src); - - return err; -} +extern int ip_route_me_harder(struct sk_buff **pskb); +#endif /*__KERNEL__*/ #endif /*__LINUX_IP_NETFILTER_H*/ diff -Nru a/include/linux/nfsd/interface.h b/include/linux/nfsd/interface.h --- a/include/linux/nfsd/interface.h Tue Feb 19 18:08:59 2002 +++ b/include/linux/nfsd/interface.h Tue Feb 19 18:08:59 2002 @@ -16,6 +16,7 @@ extern struct nfsd_linkage { long (*do_nfsservctl)(int cmd, void *argp, void *resp); + struct module *owner; } * nfsd_linkage; #endif diff -Nru a/include/linux/pagemap.h b/include/linux/pagemap.h --- a/include/linux/pagemap.h Tue Feb 19 18:08:57 2002 +++ b/include/linux/pagemap.h Tue Feb 19 18:08:57 2002 @@ -97,6 +97,8 @@ ___wait_on_page(page); } +extern void wake_up_page(struct page *); + extern struct page * grab_cache_page (struct address_space *, unsigned long); extern struct page * grab_cache_page_nowait (struct address_space *, unsigned long); diff -Nru a/include/linux/pci_ids.h b/include/linux/pci_ids.h --- a/include/linux/pci_ids.h Tue Feb 19 18:08:59 2002 +++ b/include/linux/pci_ids.h Tue Feb 19 18:08:59 2002 @@ -757,6 +757,7 @@ #define PCI_DEVICE_ID_PLX_SPCOM200 0x1103 #define PCI_DEVICE_ID_PLX_DJINN_ITOO 0x1151 #define PCI_DEVICE_ID_PLX_R753 0x1152 +#define PCI_DEVICE_ID_PLX_9030 0x9030 #define PCI_DEVICE_ID_PLX_9050 0x9050 #define PCI_DEVICE_ID_PLX_9060 0x9060 #define PCI_DEVICE_ID_PLX_9060ES 0x906E @@ -1506,6 +1507,14 @@ #define PCI_VENDOR_ID_PDC 0x15e9 #define PCI_DEVICE_ID_PDC_1841 0x1841 + +#define PCI_VENDOR_ID_MACROLINK 0x15ed +#define PCI_DEVICE_ID_MACROLINK_MCCS8 0x1000 +#define PCI_DEVICE_ID_MACROLINK_MCCS 0x1001 +#define PCI_DEVICE_ID_MACROLINK_MCCS8H 0x1002 +#define PCI_DEVICE_ID_MACROLINK_MCCSH 0x1003 +#define PCI_DEVICE_ID_MACROLINK_MCCR8 0x2000 +#define PCI_DEVICE_ID_MACROLINK_MCCR 0x2001 #define PCI_VENDOR_ID_SYMPHONY 0x1c1c #define PCI_DEVICE_ID_SYMPHONY_101 0x0001 diff -Nru a/include/linux/proc_fs.h b/include/linux/proc_fs.h --- a/include/linux/proc_fs.h Tue Feb 19 18:08:57 2002 +++ b/include/linux/proc_fs.h Tue Feb 19 18:08:57 2002 @@ -217,12 +217,12 @@ struct inode vfs_inode; }; -static inline struct proc_inode *PROC_I(struct inode *inode) +static inline struct proc_inode *PROC_I(const struct inode *inode) { return list_entry(inode, struct proc_inode, vfs_inode); } -static inline struct proc_dir_entry *PDE(struct inode *inode) +static inline struct proc_dir_entry *PDE(const struct inode *inode) { return PROC_I(inode)->pde; } diff -Nru a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h --- a/include/linux/reiserfs_fs.h Tue Feb 19 18:08:59 2002 +++ b/include/linux/reiserfs_fs.h Tue Feb 19 18:08:59 2002 @@ -337,7 +337,15 @@ #define REISERFS_VALID_FS 1 #define REISERFS_ERROR_FS 2 - +// +// there are 5 item types currently +// +#define TYPE_STAT_DATA 0 +#define TYPE_INDIRECT 1 +#define TYPE_DIRECT 2 +#define TYPE_DIRENTRY 3 +#define TYPE_MAXTYPE 3 +#define TYPE_ANY 15 // FIXME: comment is required /***************************************************************************/ /* KEY & ITEM HEAD */ @@ -373,7 +381,7 @@ { offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2; tmp.linear = le64_to_cpu( tmp.linear ); - return tmp.offset_v2.k_type; + return (tmp.offset_v2.k_type <= TYPE_MAXTYPE)?tmp.offset_v2.k_type:TYPE_ANY; } static inline void set_offset_v2_k_type( struct offset_v2 *v2, int type ) @@ -523,15 +531,6 @@ #define put_block_num(p, i, v) put_unaligned(cpu_to_le32(v), (p) + (i)) // -// there are 5 item types currently -// -#define TYPE_STAT_DATA 0 -#define TYPE_INDIRECT 1 -#define TYPE_DIRECT 2 -#define TYPE_DIRENTRY 3 -#define TYPE_ANY 15 // FIXME: comment is required - -// // in old version uniqueness field shows key type // #define V1_SD_UNIQUENESS 0 @@ -1498,7 +1497,7 @@ extern struct item_operations stat_data_ops, indirect_ops, direct_ops, direntry_ops; -extern struct item_operations * item_ops [4]; +extern struct item_operations * item_ops [TYPE_ANY + 1]; #define op_bytes_number(ih,bsize) item_ops[le_ih_k_type (ih)]->bytes_number (ih, bsize) #define op_is_left_mergeable(key,bsize) item_ops[le_key_k_type (le_key_version (key), key)]->is_left_mergeable (key, bsize) @@ -1928,8 +1927,14 @@ struct buffer_head * reiserfs_bread (struct super_block *super, int n_block); /* fix_nodes.c */ +#ifdef CONFIG_REISERFS_CHECK void * reiserfs_kmalloc (size_t size, int flags, struct super_block * s); void reiserfs_kfree (const void * vp, size_t size, struct super_block * s); +#else +#define reiserfs_kmalloc(x, y, z) kmalloc(x, y) +#define reiserfs_kfree(x, y, z) kfree(x) +#endif + int fix_nodes (int n_op_mode, struct tree_balance * p_s_tb, struct item_head * p_s_ins_ih, const void *); void unfix_nodes (struct tree_balance *); @@ -1960,6 +1965,7 @@ void check_leaf (struct buffer_head * bh); void check_internal (struct buffer_head * bh); void print_statistics (struct super_block * s); +char * reiserfs_hashname(int code); /* lbalance.c */ int leaf_move_items (int shift_mode, struct tree_balance * tb, int mov_num, int mov_bytes, struct buffer_head * Snew); diff -Nru a/include/linux/sched.h b/include/linux/sched.h --- a/include/linux/sched.h Tue Feb 19 18:08:57 2002 +++ b/include/linux/sched.h Tue Feb 19 18:08:57 2002 @@ -92,7 +92,6 @@ #define TASK_UNINTERRUPTIBLE 2 #define TASK_ZOMBIE 4 #define TASK_STOPPED 8 -#define PREEMPT_ACTIVE 0x4000000 #define __set_task_state(tsk, state_value) \ do { (tsk)->state = (state_value); } while (0) @@ -150,7 +149,7 @@ extern void update_process_times(int user); extern void update_one_process(struct task_struct *p, unsigned long user, unsigned long system, int cpu); -extern void scheduler_tick(struct task_struct *p); +extern void scheduler_tick(int user_tick, int system); extern void sched_task_migrated(struct task_struct *p); extern void smp_migrate_task(int cpu, task_t *task); extern unsigned long cache_decay_ticks; @@ -241,18 +240,16 @@ int lock_depth; /* Lock depth */ - int prio; - long __nice; + int prio, static_prio; list_t run_list; prio_array_t *array; - unsigned int time_slice; - unsigned long sleep_avg; unsigned long sleep_timestamp; unsigned long policy; unsigned long cpus_allowed; + unsigned int time_slice; struct task_struct *next_task, *prev_task; @@ -385,66 +382,12 @@ */ #define _STK_LIM (8*1024*1024) -/* - * RT priorites go from 0 to 99, but internally we max - * them out at 128 to make it easier to search the - * scheduler bitmap. - */ -#define MAX_RT_PRIO 128 -/* - * The lower the priority of a process, the more likely it is - * to run. Priority of a process goes from 0 to 167. The 0-99 - * priority range is allocated to RT tasks, the 128-167 range - * is for SCHED_OTHER tasks. - */ -#define MAX_PRIO (MAX_RT_PRIO + 40) - -/* - * Scales user-nice values [ -20 ... 0 ... 19 ] - * to static priority [ 128 ... 167 (MAX_PRIO-1) ] - * - * User-nice value of -20 == static priority 128, and - * user-nice value 19 == static priority 167. The lower - * the priority value, the higher the task's priority. - */ -#define NICE_TO_PRIO(n) (MAX_RT_PRIO + (n) + 20) -#define DEF_USER_NICE 0 - -/* - * Default timeslice is 150 msecs, maximum is 300 msecs. - * Minimum timeslice is 10 msecs. - * - * These are the 'tuning knobs' of the scheduler: - */ -#define MIN_TIMESLICE ( 10 * HZ / 1000) -#define MAX_TIMESLICE (300 * HZ / 1000) -#define CHILD_FORK_PENALTY 95 -#define PARENT_FORK_PENALTY 100 -#define EXIT_WEIGHT 3 -#define PRIO_INTERACTIVE_RATIO 20 -#define PRIO_CPU_HOG_RATIO 60 -#define PRIO_BONUS_RATIO 70 -#define INTERACTIVE_DELTA 3 -#define MAX_SLEEP_AVG (2*HZ) -#define STARVATION_LIMIT (2*HZ) - -#define USER_PRIO(p) ((p)-MAX_RT_PRIO) -#define MAX_USER_PRIO (USER_PRIO(MAX_PRIO)) - -/* - * NICE_TO_TIMESLICE scales nice values [ -20 ... 19 ] - * to time slice values. - * - * The higher a process's priority, the bigger timeslices - * it gets during one round of execution. But even the lowest - * priority process gets MIN_TIMESLICE worth of execution time. - */ - -#define NICE_TO_TIMESLICE(n) (MIN_TIMESLICE + \ - ((MAX_TIMESLICE - MIN_TIMESLICE) * (19-(n))) / 39) - extern void set_cpus_allowed(task_t *p, unsigned long new_mask); extern void set_user_nice(task_t *p, long nice); +extern int task_prio(task_t *p); +extern int task_nice(task_t *p); +extern int idle_cpu(int cpu); + asmlinkage long sys_sched_yield(void); #define yield() sys_sched_yield() @@ -526,6 +469,7 @@ signed long timeout)); extern int FASTCALL(wake_up_process(struct task_struct * tsk)); extern void FASTCALL(wake_up_forked_process(struct task_struct * tsk)); +extern void FASTCALL(sched_exit(task_t * p)); #define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1) #define wake_up_nr(x, nr) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, nr) @@ -902,9 +846,17 @@ This is required every time the blocked sigset_t changes. Athread cathreaders should have t->sigmask_lock. */ -static inline void recalc_sigpending(struct task_struct *t) +static inline void recalc_sigpending_tsk(struct task_struct *t) { if (has_pending_signals(&t->pending.signal, &t->blocked)) + set_tsk_thread_flag(t, TIF_SIGPENDING); + else + clear_tsk_thread_flag(t, TIF_SIGPENDING); +} + +static inline void recalc_sigpending(void) +{ + if (has_pending_signals(¤t->pending.signal, ¤t->blocked)) set_thread_flag(TIF_SIGPENDING); else clear_thread_flag(TIF_SIGPENDING); diff -Nru a/include/linux/spinlock.h b/include/linux/spinlock.h --- a/include/linux/spinlock.h Tue Feb 19 18:09:00 2002 +++ b/include/linux/spinlock.h Tue Feb 19 18:09:00 2002 @@ -177,9 +177,8 @@ do { \ --current_thread_info()->preempt_count; \ barrier(); \ - if (unlikely(!(current_thread_info()->preempt_count) && \ - test_thread_flag(TIF_NEED_RESCHED))) \ - preempt_schedule(); \ + if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ + preempt_schedule(); \ } while (0) #define spin_lock(lock) \ diff -Nru a/include/linux/tcp.h b/include/linux/tcp.h --- a/include/linux/tcp.h Tue Feb 19 18:09:00 2002 +++ b/include/linux/tcp.h Tue Feb 19 18:09:00 2002 @@ -17,8 +17,10 @@ #ifndef _LINUX_TCP_H #define _LINUX_TCP_H -#include +#include #include +#include +#include struct tcphdr { __u16 source; @@ -184,5 +186,198 @@ __u32 tcpi_advmss; __u32 tcpi_reordering; }; + +/* This defines a selective acknowledgement block. */ +struct tcp_sack_block { + __u32 start_seq; + __u32 end_seq; +}; + +struct tcp_opt { + int tcp_header_len; /* Bytes of tcp header to send */ + +/* + * Header prediction flags + * 0x5?10 << 16 + snd_wnd in net byte order + */ + __u32 pred_flags; + +/* + * RFC793 variables by their proper names. This means you can + * read the code and the spec side by side (and laugh ...) + * See RFC793 and RFC1122. The RFC writes these in capitals. + */ + __u32 rcv_nxt; /* What we want to receive next */ + __u32 snd_nxt; /* Next sequence we send */ + + __u32 snd_una; /* First byte we want an ack for */ + __u32 snd_sml; /* Last byte of the most recently transmitted small packet */ + __u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */ + __u32 lsndtime; /* timestamp of last sent data packet (for restart window) */ + + /* Delayed ACK control data */ + struct { + __u8 pending; /* ACK is pending */ + __u8 quick; /* Scheduled number of quick acks */ + __u8 pingpong; /* The session is interactive */ + __u8 blocked; /* Delayed ACK was blocked by socket lock*/ + __u32 ato; /* Predicted tick of soft clock */ + unsigned long timeout; /* Currently scheduled timeout */ + __u32 lrcvtime; /* timestamp of last received data packet*/ + __u16 last_seg_size; /* Size of last incoming segment */ + __u16 rcv_mss; /* MSS used for delayed ACK decisions */ + } ack; + + /* Data for direct copy to user */ + struct { + struct sk_buff_head prequeue; + int memory; + struct task_struct *task; + struct iovec *iov; + int len; + } ucopy; + + __u32 snd_wl1; /* Sequence for window update */ + __u32 snd_wnd; /* The window we expect to receive */ + __u32 max_window; /* Maximal window ever seen from peer */ + __u32 pmtu_cookie; /* Last pmtu seen by socket */ + __u16 mss_cache; /* Cached effective mss, not including SACKS */ + __u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ + __u16 ext_header_len; /* Network protocol overhead (IP/IPv6 options) */ + __u8 ca_state; /* State of fast-retransmit machine */ + __u8 retransmits; /* Number of unrecovered RTO timeouts. */ + + __u8 reordering; /* Packet reordering metric. */ + __u8 queue_shrunk; /* Write queue has been shrunk recently.*/ + __u8 defer_accept; /* User waits for some data after accept() */ + +/* RTT measurement */ + __u8 backoff; /* backoff */ + __u32 srtt; /* smothed round trip time << 3 */ + __u32 mdev; /* medium deviation */ + __u32 mdev_max; /* maximal mdev for the last rtt period */ + __u32 rttvar; /* smoothed mdev_max */ + __u32 rtt_seq; /* sequence number to update rttvar */ + __u32 rto; /* retransmit timeout */ + + __u32 packets_out; /* Packets which are "in flight" */ + __u32 left_out; /* Packets which leaved network */ + __u32 retrans_out; /* Retransmitted packets out */ + + +/* + * Slow start and congestion control (see also Nagle, and Karn & Partridge) + */ + __u32 snd_ssthresh; /* Slow start size threshold */ + __u32 snd_cwnd; /* Sending congestion window */ + __u16 snd_cwnd_cnt; /* Linear increase counter */ + __u16 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */ + __u32 snd_cwnd_used; + __u32 snd_cwnd_stamp; + + /* Two commonly used timers in both sender and receiver paths. */ + unsigned long timeout; + struct timer_list retransmit_timer; /* Resend (no ack) */ + struct timer_list delack_timer; /* Ack delay */ + + struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ + + struct tcp_func *af_specific; /* Operations which are AF_INET{4,6} specific */ + struct sk_buff *send_head; /* Front of stuff to transmit */ + struct page *sndmsg_page; /* Cached page for sendmsg */ + u32 sndmsg_off; /* Cached offset for sendmsg */ + + __u32 rcv_wnd; /* Current receiver window */ + __u32 rcv_wup; /* rcv_nxt on last window update sent */ + __u32 write_seq; /* Tail(+1) of data held in tcp send buffer */ + __u32 pushed_seq; /* Last pushed seq, required to talk to windows */ + __u32 copied_seq; /* Head of yet unread data */ +/* + * Options received (usually on last packet, some only on SYN packets). + */ + char tstamp_ok, /* TIMESTAMP seen on SYN packet */ + wscale_ok, /* Wscale seen on SYN packet */ + sack_ok; /* SACK seen on SYN packet */ + char saw_tstamp; /* Saw TIMESTAMP on last packet */ + __u8 snd_wscale; /* Window scaling received from sender */ + __u8 rcv_wscale; /* Window scaling to send to receiver */ + __u8 nonagle; /* Disable Nagle algorithm? */ + __u8 keepalive_probes; /* num of allowed keep alive probes */ + +/* PAWS/RTTM data */ + __u32 rcv_tsval; /* Time stamp value */ + __u32 rcv_tsecr; /* Time stamp echo reply */ + __u32 ts_recent; /* Time stamp to echo next */ + long ts_recent_stamp;/* Time we stored ts_recent (for aging) */ + +/* SACKs data */ + __u16 user_mss; /* mss requested by user in ioctl */ + __u8 dsack; /* D-SACK is scheduled */ + __u8 eff_sacks; /* Size of SACK array to send with next packet */ + struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */ + struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/ + + __u32 window_clamp; /* Maximal window to advertise */ + __u32 rcv_ssthresh; /* Current window clamp */ + __u8 probes_out; /* unanswered 0 window probes */ + __u8 num_sacks; /* Number of SACK blocks */ + __u16 advmss; /* Advertised MSS */ + + __u8 syn_retries; /* num of allowed syn retries */ + __u8 ecn_flags; /* ECN status bits. */ + __u16 prior_ssthresh; /* ssthresh saved at recovery start */ + __u32 lost_out; /* Lost packets */ + __u32 sacked_out; /* SACK'd packets */ + __u32 fackets_out; /* FACK'd packets */ + __u32 high_seq; /* snd_nxt at onset of congestion */ + + __u32 retrans_stamp; /* Timestamp of the last retransmit, + * also used in SYN-SENT to remember stamp of + * the first SYN. */ + __u32 undo_marker; /* tracking retrans started here. */ + int undo_retrans; /* number of undoable retransmissions. */ + __u32 urg_seq; /* Seq of received urgent pointer */ + __u16 urg_data; /* Saved octet of OOB data and control flags */ + __u8 pending; /* Scheduled timer event */ + __u8 urg_mode; /* In urgent mode */ + __u32 snd_up; /* Urgent pointer */ + + /* The syn_wait_lock is necessary only to avoid tcp_get_info having + * to grab the main lock sock while browsing the listening hash + * (otherwise it's deadlock prone). + * This lock is acquired in read mode only from tcp_get_info() and + * it's acquired in write mode _only_ from code that is actively + * changing the syn_wait_queue. All readers that are holding + * the master sock lock don't need to grab this lock in read mode + * too as the syn_wait_queue writes are always protected from + * the main sock lock. + */ + rwlock_t syn_wait_lock; + struct tcp_listen_opt *listen_opt; + + /* FIFO of established children */ + struct open_request *accept_queue; + struct open_request *accept_queue_tail; + + int write_pending; /* A write to socket waits to start. */ + + unsigned int keepalive_time; /* time before keep alive takes place */ + unsigned int keepalive_intvl; /* time interval between keep alive probes */ + int linger2; + + unsigned long last_synq_overflow; +}; + +/* WARNING: don't change the layout of the members in tcp_sock! */ +struct tcp_sock { + struct sock sk; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct ipv6_pinfo *pinet6; +#endif + struct inet_opt inet; + struct tcp_opt tcp; +}; + +#define tcp_sk(__sk) (&((struct tcp_sock *)__sk)->tcp) #endif /* _LINUX_TCP_H */ diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Tue Feb 19 18:08:59 2002 +++ b/include/linux/usb.h Tue Feb 19 18:08:59 2002 @@ -856,7 +856,7 @@ #define FILL_INT_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT,INTERVAL) \ usb_fill_int_urb(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT,INTERVAL) -extern struct urb *usb_alloc_urb(int iso_packets); +extern struct urb *usb_alloc_urb(int iso_packets, int mem_flags); extern void usb_free_urb(struct urb *urb); #define usb_put_urb usb_free_urb extern struct urb *usb_get_urb(struct urb *urb); @@ -1156,12 +1156,12 @@ #define PIPE_DEVEP_MASK 0x0007ff00 /* The D0/D1 toggle bits */ -#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> ep) & 1) -#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << ep)) -#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << ep)) | ((bit) << ep)) +#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1) +#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << (ep))) +#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | ((bit) << (ep))) /* Endpoint halt control/status */ -#define usb_endpoint_out(ep_dir) (((ep_dir >> 7) & 1) ^ 1) +#define usb_endpoint_out(ep_dir) ((((ep_dir) >> 7) & 1) ^ 1) #define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep))) #define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep))) #define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep))) diff -Nru a/include/net/af_unix.h b/include/net/af_unix.h --- a/include/net/af_unix.h Tue Feb 19 18:08:58 2002 +++ b/include/net/af_unix.h Tue Feb 19 18:08:58 2002 @@ -34,9 +34,27 @@ #define UNIXCB(skb) (*(struct unix_skb_parms*)&((skb)->cb)) #define UNIXCREDS(skb) (&UNIXCB((skb)).creds) -#define unix_state_rlock(s) read_lock(&(s)->protinfo.af_unix.lock) -#define unix_state_runlock(s) read_unlock(&(s)->protinfo.af_unix.lock) -#define unix_state_wlock(s) write_lock(&(s)->protinfo.af_unix.lock) -#define unix_state_wunlock(s) write_unlock(&(s)->protinfo.af_unix.lock) +#define unix_state_rlock(s) read_lock(&unix_sk(s)->lock) +#define unix_state_runlock(s) read_unlock(&unix_sk(s)->lock) +#define unix_state_wlock(s) write_lock(&unix_sk(s)->lock) +#define unix_state_wunlock(s) write_unlock(&unix_sk(s)->lock) +#ifdef __KERNEL__ +/* The AF_UNIX socket */ +struct unix_sock { + /* WARNING: sk has to be the first member */ + struct sock sk; + struct unix_address *addr; + struct dentry *dentry; + struct vfsmount *mnt; + struct semaphore readsem; + struct sock *other; + struct sock **list; + struct sock *gc_tree; + atomic_t inflight; + rwlock_t lock; + wait_queue_head_t peer_wait; +}; +#define unix_sk(__sk) ((struct unix_sock *)__sk) +#endif #endif diff -Nru a/include/net/ax25.h b/include/net/ax25.h --- a/include/net/ax25.h Tue Feb 19 18:08:57 2002 +++ b/include/net/ax25.h Tue Feb 19 18:08:57 2002 @@ -194,6 +194,8 @@ struct sock *sk; /* Backlink to socket */ } ax25_cb; +#define ax25_sk(__sk) ((ax25_cb *)(__sk)->protinfo) + /* af_ax25.c */ extern ax25_cb *volatile ax25_list; extern void ax25_free_cb(ax25_cb *); diff -Nru a/include/net/checksum.h b/include/net/checksum.h --- a/include/net/checksum.h Tue Feb 19 18:08:58 2002 +++ b/include/net/checksum.h Tue Feb 19 18:08:58 2002 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include diff -Nru a/include/net/dn.h b/include/net/dn.h --- a/include/net/dn.h Tue Feb 19 18:08:57 2002 +++ b/include/net/dn.h Tue Feb 19 18:08:57 2002 @@ -9,8 +9,6 @@ #define dn_ntohs(x) le16_to_cpu((unsigned short)(x)) #define dn_htons(x) cpu_to_le16((unsigned short)(x)) -#define DN_SK(sk) (&sk->protinfo.dn) - struct dn_scp /* Session Control Port */ { unsigned char state; @@ -134,6 +132,8 @@ void (*delack_fxn)(struct sock *sk); }; + +#define DN_SK(__sk) ((struct dn_scp *)(__sk)->protinfo) /* * src,dst : Source and Destination DECnet addresses diff -Nru a/include/net/dn_nsp.h b/include/net/dn_nsp.h --- a/include/net/dn_nsp.h Tue Feb 19 18:08:58 2002 +++ b/include/net/dn_nsp.h Tue Feb 19 18:08:58 2002 @@ -150,7 +150,7 @@ * numbers used in NSP. Similar in operation to the functions * of the same name in TCP. */ -static __inline__ int before(unsigned short seq1, unsigned short seq2) +static __inline__ int dn_before(unsigned short seq1, unsigned short seq2) { seq1 &= 0x0fff; seq2 &= 0x0fff; @@ -159,7 +159,7 @@ } -static __inline__ int after(unsigned short seq1, unsigned short seq2) +static __inline__ int dn_after(unsigned short seq1, unsigned short seq2) { seq1 &= 0x0fff; seq2 &= 0x0fff; @@ -167,14 +167,14 @@ return (int)((seq2 - seq1) & 0x0fff) > 2048; } -static __inline__ int equal(unsigned short seq1, unsigned short seq2) +static __inline__ int dn_equal(unsigned short seq1, unsigned short seq2) { return ((seq1 ^ seq2) & 0x0fff) == 0; } -static __inline__ int before_or_equal(unsigned short seq1, unsigned short seq2) +static __inline__ int dn_before_or_equal(unsigned short seq1, unsigned short seq2) { - return (before(seq1, seq2) || equal(seq1, seq2)); + return (dn_before(seq1, seq2) || dn_equal(seq1, seq2)); } static __inline__ void seq_add(unsigned short *seq, unsigned short off) @@ -185,7 +185,7 @@ static __inline__ int seq_next(unsigned short seq1, unsigned short seq2) { - return equal(seq1 + 1, seq2); + return dn_equal(seq1 + 1, seq2); } /* diff -Nru a/include/net/dn_route.h b/include/net/dn_route.h --- a/include/net/dn_route.h Tue Feb 19 18:08:57 2002 +++ b/include/net/dn_route.h Tue Feb 19 18:08:57 2002 @@ -113,7 +113,7 @@ static inline void dn_nsp_send(struct sk_buff *skb) { struct sock *sk = skb->sk; - struct dn_scp *scp = &sk->protinfo.dn; + struct dn_scp *scp = DN_SK(sk); struct dst_entry *dst; skb->h.raw = skb->data; diff -Nru a/include/net/icmp.h b/include/net/icmp.h --- a/include/net/icmp.h Tue Feb 19 18:08:58 2002 +++ b/include/net/icmp.h Tue Feb 19 18:08:58 2002 @@ -23,6 +23,7 @@ #include #include +#include struct icmp_err { int errno; @@ -42,5 +43,23 @@ /* Move into dst.h ? */ extern int xrlim_allow(struct dst_entry *dst, int timeout); + +struct raw_opt { + struct icmp_filter filter; +}; + +struct ipv6_pinfo; + +/* WARNING: don't change the layout of the members in raw_sock! */ +struct raw_sock { + struct sock sk; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct ipv6_pinfo *pinet6; +#endif + struct inet_opt inet; + struct raw_opt raw4; +}; + +#define raw4_sk(__sk) (&((struct raw_sock *)__sk)->raw4) #endif /* _ICMP_H */ diff -Nru a/include/net/inet_ecn.h b/include/net/inet_ecn.h --- a/include/net/inet_ecn.h Tue Feb 19 18:09:00 2002 +++ b/include/net/inet_ecn.h Tue Feb 19 18:09:00 2002 @@ -24,15 +24,15 @@ return outer; } -#define INET_ECN_xmit(sk) do { (sk)->protinfo.af_inet.tos |= 2; } while (0) -#define INET_ECN_dontxmit(sk) do { (sk)->protinfo.af_inet.tos &= ~3; } while (0) +#define INET_ECN_xmit(sk) do { inet_sk(sk)->tos |= 2; } while (0) +#define INET_ECN_dontxmit(sk) do { inet_sk(sk)->tos &= ~3; } while (0) #define IP6_ECN_flow_init(label) do { \ (label) &= ~htonl(3<<20); \ } while (0) #define IP6_ECN_flow_xmit(sk, label) do { \ - if (INET_ECN_is_capable((sk)->protinfo.af_inet.tos)) \ + if (INET_ECN_is_capable(inet_sk(sk)->tos)) \ (label) |= __constant_htons(2 << 4); \ } while (0) diff -Nru a/include/net/ip.h b/include/net/ip.h --- a/include/net/ip.h Tue Feb 19 18:08:58 2002 +++ b/include/net/ip.h Tue Feb 19 18:08:58 2002 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -181,8 +182,8 @@ static inline int ip_dont_fragment(struct sock *sk, struct dst_entry *dst) { - return (sk->protinfo.af_inet.pmtudisc == IP_PMTUDISC_DO || - (sk->protinfo.af_inet.pmtudisc == IP_PMTUDISC_WANT && + return (inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO || + (inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT && !(dst->mxlock&(1<id = ((sk && sk->daddr) ? htons(sk->protinfo.af_inet.id++) : 0); + iph->id = (sk && sk->daddr) ? htons(inet_sk(sk)->id++) : 0; } else __ip_select_ident(iph, dst); } diff -Nru a/include/net/ip6_route.h b/include/net/ip6_route.h --- a/include/net/ip6_route.h Tue Feb 19 18:08:58 2002 +++ b/include/net/ip6_route.h Tue Feb 19 18:08:58 2002 @@ -11,6 +11,8 @@ #include #include +#include +#include struct pol_chain { int type; @@ -97,7 +99,7 @@ static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, struct in6_addr *daddr) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct rt6_info *rt = (struct rt6_info *) dst; write_lock(&sk->dst_lock); diff -Nru a/include/net/ipx.h b/include/net/ipx.h --- a/include/net/ipx.h Tue Feb 19 18:08:57 2002 +++ b/include/net/ipx.h Tue Feb 19 18:08:57 2002 @@ -86,6 +86,22 @@ int index; } last_hop; }; + +struct ipx_opt { + ipx_address dest_addr; + ipx_interface *intrfc; + unsigned short port; +#ifdef CONFIG_IPX_INTERN + unsigned char node[IPX_NODE_LEN]; +#endif + unsigned short type; + /* To handle special ncp connection-handling sockets for mars_nwe, + * the connection number must be stored in the socket. */ + unsigned short ipx_ncp_conn; +}; + +#define ipx_sk(__sk) ((struct ipx_opt *)(__sk)->protinfo) +#define IPX_SKB_CB(__skb) ((struct ipx_cb *)&((__skb)->cb[0])) #endif #define IPX_MIN_EPHEMERAL_SOCKET 0x4000 #define IPX_MAX_EPHEMERAL_SOCKET 0x7fff diff -Nru a/include/net/irda/irda.h b/include/net/irda/irda.h --- a/include/net/irda/irda.h Tue Feb 19 18:08:59 2002 +++ b/include/net/irda/irda.h Tue Feb 19 18:08:59 2002 @@ -168,6 +168,8 @@ LOCAL_FLOW rx_flow; }; +#define irda_sk(__sk) ((struct irda_sock *)(__sk)->protinfo) + /* * This type is used by the protocols that transmit 16 bits words in * little endian format. A little endian machine stores MSB of word in diff -Nru a/include/net/netrom.h b/include/net/netrom.h --- a/include/net/netrom.h Tue Feb 19 18:08:57 2002 +++ b/include/net/netrom.h Tue Feb 19 18:08:57 2002 @@ -74,6 +74,8 @@ struct sock *sk; /* Backlink to socket */ } nr_cb; +#define nr_sk(__sk) ((nr_cb *)(__sk)->protinfo) + struct nr_neigh { struct nr_neigh *next; ax25_address callsign; diff -Nru a/include/net/rose.h b/include/net/rose.h --- a/include/net/rose.h Tue Feb 19 18:08:58 2002 +++ b/include/net/rose.h Tue Feb 19 18:08:58 2002 @@ -138,6 +138,8 @@ struct sock *sk; /* Backlink to socket */ } rose_cb; +#define rose_sk(__sk) ((rose_cb *)(__sk)->protinfo) + /* af_rose.c */ extern ax25_address rose_callsign; extern int sysctl_rose_restart_request_timeout; diff -Nru a/include/net/route.h b/include/net/route.h --- a/include/net/route.h Tue Feb 19 18:08:57 2002 +++ b/include/net/route.h Tue Feb 19 18:08:57 2002 @@ -43,7 +43,7 @@ /* RTO_CONN is not used (being alias for 0), but preserved not to break * some modules referring to it. */ -#define RT_CONN_FLAGS(sk) (RT_TOS(sk->protinfo.af_inet.tos) | sk->localroute) +#define RT_CONN_FLAGS(sk) (RT_TOS(inet_sk(sk)->tos) | sk->localroute) struct rt_key { diff -Nru a/include/net/sock.h b/include/net/sock.h --- a/include/net/sock.h Tue Feb 19 18:08:58 2002 +++ b/include/net/sock.h Tue Feb 19 18:08:58 2002 @@ -24,6 +24,11 @@ * Alan Cox : Eliminate low level recv/recvfrom * David S. Miller : New socket lookup architecture. * Steve Whitehouse: Default routines for sock_ops + * Arnaldo C. Melo : removed net_pinfo, tp_pinfo and made + * protinfo be just a void pointer, as the + * protocol specific parts were moved to + * respective headers and ipv4/v6, etc now + * use private slabcaches for its socks * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,68 +41,9 @@ #include #include #include -#include /* struct sockaddr_in */ - -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -#include /* struct sockaddr_in6 */ -#include /* dest_cache, inet6_options */ -#include -#include /* struct ipv6_mc_socklist */ -#endif - -#if defined(CONFIG_INET) || defined (CONFIG_INET_MODULE) -#include -#endif -#include /* struct tcphdr */ #include #include /* struct sk_buff */ -#include /* struct inet_protocol */ -#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE) -#include -#endif -#if defined(CONFIG_WAN_ROUTER) || defined(CONFIG_WAN_ROUTER_MODULE) -#include -#endif - -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) -#include -#endif -#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE) -#include -#endif -#endif - -#if defined(CONFIG_PPPOE) || defined(CONFIG_PPPOE_MODULE) -#include -#include /* struct ppp_channel */ -#endif - -#if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE) -#if defined(CONFIG_SPX) || defined(CONFIG_SPX_MODULE) -#include -#else -#include -#endif /* CONFIG_SPX */ -#endif /* CONFIG_IPX */ - -#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE) -#include -#endif - -#if defined(CONFIG_DECNET) || defined(CONFIG_DECNET_MODULE) -#include -#endif - -#if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE) -#include -#endif - -#if defined(CONFIG_ATM) || defined(CONFIG_ATM_MODULE) -struct atm_vcc; -#endif #ifdef CONFIG_FILTER #include @@ -106,364 +52,12 @@ #include #include -#include /* just for inode - yeuch.*/ - - -/* The AF_UNIX specific socket options */ -struct unix_opt { - struct unix_address *addr; - struct dentry * dentry; - struct vfsmount * mnt; - struct semaphore readsem; - struct sock * other; - struct sock ** list; - struct sock * gc_tree; - atomic_t inflight; - rwlock_t lock; - wait_queue_head_t peer_wait; -}; - - -/* Once the IPX ncpd patches are in these are going into protinfo. */ -#if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE) -struct ipx_opt { - ipx_address dest_addr; - ipx_interface *intrfc; - unsigned short port; -#ifdef CONFIG_IPX_INTERN - unsigned char node[IPX_NODE_LEN]; -#endif - unsigned short type; -/* - * To handle special ncp connection-handling sockets for mars_nwe, - * the connection number must be stored in the socket. - */ - unsigned short ipx_ncp_conn; -}; -#endif - -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -struct ipv6_pinfo { - struct in6_addr saddr; - struct in6_addr rcv_saddr; - struct in6_addr daddr; - struct in6_addr *daddr_cache; - - __u32 flow_label; - __u32 frag_size; - int hop_limit; - int mcast_hops; - int mcast_oif; - - /* pktoption flags */ - union { - struct { - __u8 srcrt:2, - rxinfo:1, - rxhlim:1, - hopopts:1, - dstopts:1, - authhdr:1, - rxflow:1; - } bits; - __u8 all; - } rxopt; - - /* sockopt flags */ - __u8 mc_loop:1, - recverr:1, - sndflow:1, - pmtudisc:2; - - struct ipv6_mc_socklist *ipv6_mc_list; - struct ipv6_fl_socklist *ipv6_fl_list; - __u32 dst_cookie; - - struct ipv6_txoptions *opt; - struct sk_buff *pktoptions; -}; - -struct raw6_opt { - __u32 checksum; /* perform checksum */ - __u32 offset; /* checksum offset */ - - struct icmp6_filter filter; -}; - -#endif /* IPV6 */ - -#if defined(CONFIG_INET) || defined(CONFIG_INET_MODULE) -struct raw_opt { - struct icmp_filter filter; -}; -#endif - -#if defined(CONFIG_INET) || defined (CONFIG_INET_MODULE) -struct inet_opt -{ - int ttl; /* TTL setting */ - int tos; /* TOS */ - unsigned cmsg_flags; - struct ip_options *opt; - unsigned char hdrincl; /* Include headers ? */ - __u8 mc_ttl; /* Multicasting TTL */ - __u8 mc_loop; /* Loopback */ - unsigned recverr : 1, - freebind : 1; - __u16 id; /* ID counter for DF pkts */ - __u8 pmtudisc; - int mc_index; /* Multicast device index */ - __u32 mc_addr; - struct ip_mc_socklist *mc_list; /* Group array */ -}; -#endif - -#if defined(CONFIG_PPPOE) || defined (CONFIG_PPPOE_MODULE) -struct pppoe_opt -{ - struct net_device *dev; /* device associated with socket*/ - struct pppoe_addr pa; /* what this socket is bound to*/ - struct sockaddr_pppox relay; /* what socket data will be - relayed to (PPPoE relaying) */ -}; - -struct pppox_opt -{ - struct ppp_channel chan; - struct sock *sk; - struct pppox_opt *next; /* for hash table */ - union { - struct pppoe_opt pppoe; - } proto; -}; -#define pppoe_dev proto.pppoe.dev -#define pppoe_pa proto.pppoe.pa -#define pppoe_relay proto.pppoe.relay -#endif - -/* This defines a selective acknowledgement block. */ -struct tcp_sack_block { - __u32 start_seq; - __u32 end_seq; -}; - -struct tcp_opt { - int tcp_header_len; /* Bytes of tcp header to send */ - -/* - * Header prediction flags - * 0x5?10 << 16 + snd_wnd in net byte order - */ - __u32 pred_flags; - -/* - * RFC793 variables by their proper names. This means you can - * read the code and the spec side by side (and laugh ...) - * See RFC793 and RFC1122. The RFC writes these in capitals. - */ - __u32 rcv_nxt; /* What we want to receive next */ - __u32 snd_nxt; /* Next sequence we send */ - - __u32 snd_una; /* First byte we want an ack for */ - __u32 snd_sml; /* Last byte of the most recently transmitted small packet */ - __u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */ - __u32 lsndtime; /* timestamp of last sent data packet (for restart window) */ - - /* Delayed ACK control data */ - struct { - __u8 pending; /* ACK is pending */ - __u8 quick; /* Scheduled number of quick acks */ - __u8 pingpong; /* The session is interactive */ - __u8 blocked; /* Delayed ACK was blocked by socket lock*/ - __u32 ato; /* Predicted tick of soft clock */ - unsigned long timeout; /* Currently scheduled timeout */ - __u32 lrcvtime; /* timestamp of last received data packet*/ - __u16 last_seg_size; /* Size of last incoming segment */ - __u16 rcv_mss; /* MSS used for delayed ACK decisions */ - } ack; - - /* Data for direct copy to user */ - struct { - struct sk_buff_head prequeue; - int memory; - struct task_struct *task; - struct iovec *iov; - int len; - } ucopy; - - __u32 snd_wl1; /* Sequence for window update */ - __u32 snd_wnd; /* The window we expect to receive */ - __u32 max_window; /* Maximal window ever seen from peer */ - __u32 pmtu_cookie; /* Last pmtu seen by socket */ - __u16 mss_cache; /* Cached effective mss, not including SACKS */ - __u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ - __u16 ext_header_len; /* Network protocol overhead (IP/IPv6 options) */ - __u8 ca_state; /* State of fast-retransmit machine */ - __u8 retransmits; /* Number of unrecovered RTO timeouts. */ - - __u8 reordering; /* Packet reordering metric. */ - __u8 queue_shrunk; /* Write queue has been shrunk recently.*/ - __u8 defer_accept; /* User waits for some data after accept() */ - -/* RTT measurement */ - __u8 backoff; /* backoff */ - __u32 srtt; /* smothed round trip time << 3 */ - __u32 mdev; /* medium deviation */ - __u32 mdev_max; /* maximal mdev for the last rtt period */ - __u32 rttvar; /* smoothed mdev_max */ - __u32 rtt_seq; /* sequence number to update rttvar */ - __u32 rto; /* retransmit timeout */ - - __u32 packets_out; /* Packets which are "in flight" */ - __u32 left_out; /* Packets which leaved network */ - __u32 retrans_out; /* Retransmitted packets out */ - - -/* - * Slow start and congestion control (see also Nagle, and Karn & Partridge) - */ - __u32 snd_ssthresh; /* Slow start size threshold */ - __u32 snd_cwnd; /* Sending congestion window */ - __u16 snd_cwnd_cnt; /* Linear increase counter */ - __u16 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */ - __u32 snd_cwnd_used; - __u32 snd_cwnd_stamp; - - /* Two commonly used timers in both sender and receiver paths. */ - unsigned long timeout; - struct timer_list retransmit_timer; /* Resend (no ack) */ - struct timer_list delack_timer; /* Ack delay */ - - struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ - - struct tcp_func *af_specific; /* Operations which are AF_INET{4,6} specific */ - struct sk_buff *send_head; /* Front of stuff to transmit */ - struct page *sndmsg_page; /* Cached page for sendmsg */ - u32 sndmsg_off; /* Cached offset for sendmsg */ - - __u32 rcv_wnd; /* Current receiver window */ - __u32 rcv_wup; /* rcv_nxt on last window update sent */ - __u32 write_seq; /* Tail(+1) of data held in tcp send buffer */ - __u32 pushed_seq; /* Last pushed seq, required to talk to windows */ - __u32 copied_seq; /* Head of yet unread data */ -/* - * Options received (usually on last packet, some only on SYN packets). - */ - char tstamp_ok, /* TIMESTAMP seen on SYN packet */ - wscale_ok, /* Wscale seen on SYN packet */ - sack_ok; /* SACK seen on SYN packet */ - char saw_tstamp; /* Saw TIMESTAMP on last packet */ - __u8 snd_wscale; /* Window scaling received from sender */ - __u8 rcv_wscale; /* Window scaling to send to receiver */ - __u8 nonagle; /* Disable Nagle algorithm? */ - __u8 keepalive_probes; /* num of allowed keep alive probes */ - -/* PAWS/RTTM data */ - __u32 rcv_tsval; /* Time stamp value */ - __u32 rcv_tsecr; /* Time stamp echo reply */ - __u32 ts_recent; /* Time stamp to echo next */ - long ts_recent_stamp;/* Time we stored ts_recent (for aging) */ - -/* SACKs data */ - __u16 user_mss; /* mss requested by user in ioctl */ - __u8 dsack; /* D-SACK is scheduled */ - __u8 eff_sacks; /* Size of SACK array to send with next packet */ - struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */ - struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/ - - __u32 window_clamp; /* Maximal window to advertise */ - __u32 rcv_ssthresh; /* Current window clamp */ - __u8 probes_out; /* unanswered 0 window probes */ - __u8 num_sacks; /* Number of SACK blocks */ - __u16 advmss; /* Advertised MSS */ - - __u8 syn_retries; /* num of allowed syn retries */ - __u8 ecn_flags; /* ECN status bits. */ - __u16 prior_ssthresh; /* ssthresh saved at recovery start */ - __u32 lost_out; /* Lost packets */ - __u32 sacked_out; /* SACK'd packets */ - __u32 fackets_out; /* FACK'd packets */ - __u32 high_seq; /* snd_nxt at onset of congestion */ - - __u32 retrans_stamp; /* Timestamp of the last retransmit, - * also used in SYN-SENT to remember stamp of - * the first SYN. */ - __u32 undo_marker; /* tracking retrans started here. */ - int undo_retrans; /* number of undoable retransmissions. */ - __u32 urg_seq; /* Seq of received urgent pointer */ - __u16 urg_data; /* Saved octet of OOB data and control flags */ - __u8 pending; /* Scheduled timer event */ - __u8 urg_mode; /* In urgent mode */ - __u32 snd_up; /* Urgent pointer */ - - /* The syn_wait_lock is necessary only to avoid tcp_get_info having - * to grab the main lock sock while browsing the listening hash - * (otherwise it's deadlock prone). - * This lock is acquired in read mode only from tcp_get_info() and - * it's acquired in write mode _only_ from code that is actively - * changing the syn_wait_queue. All readers that are holding - * the master sock lock don't need to grab this lock in read mode - * too as the syn_wait_queue writes are always protected from - * the main sock lock. - */ - rwlock_t syn_wait_lock; - struct tcp_listen_opt *listen_opt; - - /* FIFO of established children */ - struct open_request *accept_queue; - struct open_request *accept_queue_tail; - - int write_pending; /* A write to socket waits to start. */ - - unsigned int keepalive_time; /* time before keep alive takes place */ - unsigned int keepalive_intvl; /* time interval between keep alive probes */ - int linger2; - - unsigned long last_synq_overflow; -}; - - /* * This structure really needs to be cleaned up. * Most of it is for TCP, and not used by any of * the other protocols. */ -/* - * The idea is to start moving to a newer struct gradualy - * - * IMHO the newer struct should have the following format: - * - * struct sock { - * sockmem [mem, proto, callbacks] - * - * union or struct { - * ax25; - * } ll_pinfo; - * - * union { - * ipv4; - * ipv6; - * ipx; - * netrom; - * rose; - * x25; - * } net_pinfo; - * - * union { - * tcp; - * udp; - * spx; - * netrom; - * } tp_pinfo; - * - * } - * - * The idea failed because IPv6 transition asssumes dual IP/IPv6 sockets. - * So, net_pinfo is IPv6 are really, and protinfo unifies all another - * private areas. - */ - /* Define this to get the sk->debug debugging facility. */ #define SOCK_DEBUGGING #ifdef SOCK_DEBUGGING @@ -569,26 +163,6 @@ struct proto *prot; -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - union { - struct ipv6_pinfo af_inet6; - } net_pinfo; -#endif - - union { - struct tcp_opt af_tcp; -#if defined(CONFIG_INET) || defined (CONFIG_INET_MODULE) - struct raw_opt tp_raw4; -#endif -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) - struct raw6_opt tp_raw; -#endif /* CONFIG_IPV6 */ -#if defined(CONFIG_SPX) || defined (CONFIG_SPX_MODULE) - struct spx_opt af_spx; -#endif /* CONFIG_SPX */ - - } tp_pinfo; - int err, err_soft; /* Soft holds errors that don't cause failure but are the cause of a persistent failure not just @@ -612,54 +186,13 @@ /* This is where all the private (optional) areas that don't * overlap will eventually live. */ - union { - void *destruct_hook; - struct unix_opt af_unix; -#if defined(CONFIG_INET) || defined (CONFIG_INET_MODULE) - struct inet_opt af_inet; -#endif -#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE) - struct atalk_sock af_at; -#endif -#if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE) - struct ipx_opt af_ipx; -#endif -#if defined (CONFIG_DECNET) || defined(CONFIG_DECNET_MODULE) - struct dn_scp dn; -#endif -#if defined (CONFIG_PACKET) || defined(CONFIG_PACKET_MODULE) - struct packet_opt *af_packet; -#endif -#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE) - x25_cb *x25; -#endif -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - ax25_cb *ax25; -#endif -#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) - nr_cb *nr; -#endif -#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE) - rose_cb *rose; -#endif -#if defined(CONFIG_PPPOE) || defined(CONFIG_PPPOE_MODULE) - struct pppox_opt *pppox; -#endif - struct netlink_opt *af_netlink; -#if defined(CONFIG_ECONET) || defined(CONFIG_ECONET_MODULE) - struct econet_opt *af_econet; -#endif -#if defined(CONFIG_ATM) || defined(CONFIG_ATM_MODULE) - struct atm_vcc *af_atm; -#endif -#if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE) - struct irda_sock *irda; -#endif -#if defined(CONFIG_WAN_ROUTER) || defined(CONFIG_WAN_ROUTER_MODULE) - struct wanpipe_opt *af_wanpipe; -#endif - } protinfo; + void *protinfo; + /* The slabcache this instance was allocated from, it is sk_cachep for most + * protocols, but a private slab for protocols such as IPv4, IPv6, SPX + * and Unix. + */ + kmem_cache_t *slab; /* This part is used for the timeout functions. */ struct timer_list timer; /* This is the sock cleanup timer. */ @@ -768,6 +301,8 @@ #define SOCK_BINDADDR_LOCK 4 #define SOCK_BINDPORT_LOCK 8 +#include /* just for inode - yeuch.*/ + /* Used by processes to "lock" a socket state, so that * interrupts and bottom half handlers won't change it @@ -805,7 +340,8 @@ #define bh_lock_sock(__sk) spin_lock(&((__sk)->lock.slock)) #define bh_unlock_sock(__sk) spin_unlock(&((__sk)->lock.slock)) -extern struct sock * sk_alloc(int family, int priority, int zero_it); +extern struct sock * sk_alloc(int family, int priority, int zero_it, + kmem_cache_t *slab); extern void sk_free(struct sock *sk); extern struct sk_buff *sock_wmalloc(struct sock *sk, diff -Nru a/include/net/spx.h b/include/net/spx.h --- a/include/net/spx.h Tue Feb 19 18:08:57 2002 +++ b/include/net/spx.h Tue Feb 19 18:08:57 2002 @@ -48,6 +48,8 @@ struct sk_buff_head retransmit_queue; }; +#define spx_sk(__sk) ((struct spx_opt *)(((struct sock *)(__sk)) + 1)) + /* Packet connectino control defines */ #define CCTL_SPXII_XHD 0x01 /* SPX2 extended header */ #define CCTL_SPX_UNKNOWN 0x02 /* Unknown (unused ??) */ diff -Nru a/include/net/tcp.h b/include/net/tcp.h --- a/include/net/tcp.h Tue Feb 19 18:08:58 2002 +++ b/include/net/tcp.h Tue Feb 19 18:08:58 2002 @@ -29,6 +29,9 @@ #include #include #include +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) +#include +#endif /* This is for all connections with a full identity, no wildcards. * New scheme, half the table is for TIME_WAIT, the other half is @@ -134,6 +137,9 @@ #define tcp_lhash_wait (tcp_hashinfo.__tcp_lhash_wait) #define tcp_portalloc_lock (tcp_hashinfo.__tcp_portalloc_lock) +/* SLAB cache for TCP socks */ +extern kmem_cache_t *tcp_sk_cachep; + extern kmem_cache_t *tcp_bucket_cachep; extern struct tcp_bind_bucket *tcp_bucket_create(struct tcp_bind_hashbucket *head, unsigned short snum); @@ -242,11 +248,11 @@ (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif)))) #endif /* 64-bit arch */ -#define TCP_IPV6_MATCH(__sk, __saddr, __daddr, __ports, __dif) \ - (((*((__u32 *)&((__sk)->dport)))== (__ports)) && \ - ((__sk)->family == AF_INET6) && \ - !ipv6_addr_cmp(&(__sk)->net_pinfo.af_inet6.daddr, (__saddr)) && \ - !ipv6_addr_cmp(&(__sk)->net_pinfo.af_inet6.rcv_saddr, (__daddr)) && \ +#define TCP_IPV6_MATCH(__sk, __saddr, __daddr, __ports, __dif) \ + (((*((__u32 *)&((__sk)->dport)))== (__ports)) && \ + ((__sk)->family == AF_INET6) && \ + !ipv6_addr_cmp(&inet6_sk(__sk)->daddr, (__saddr)) && \ + !ipv6_addr_cmp(&inet6_sk(__sk)->rcv_saddr, (__daddr)) && \ (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif)))) /* These can have wildcards, don't try too hard. */ @@ -824,7 +830,7 @@ static inline void tcp_clear_xmit_timer(struct sock *sk, int what) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); switch (what) { case TCP_TIME_RETRANS: @@ -859,7 +865,7 @@ */ static inline void tcp_reset_xmit_timer(struct sock *sk, int what, unsigned long when) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); if (when > TCP_RTO_MAX) { #ifdef TCP_DEBUG @@ -895,7 +901,7 @@ static __inline__ unsigned int tcp_current_mss(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); struct dst_entry *dst = __sk_dst_get(sk); int mss_now = tp->mss_cache; @@ -918,7 +924,7 @@ static inline void tcp_initialize_rcv_mss(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); unsigned int hint = min(tp->advmss, tp->mss_cache); hint = min(hint, tp->rcv_wnd/2); @@ -1321,7 +1327,7 @@ */ static __inline__ int tcp_prequeue(struct sock *sk, struct sk_buff *skb) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); if (tp->ucopy.task) { __skb_queue_tail(&tp->ucopy.prequeue, skb); @@ -1572,7 +1578,7 @@ static inline void tcp_acceptq_queue(struct sock *sk, struct open_request *req, struct sock *child) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); req->sk = child; tcp_acceptq_added(sk); @@ -1598,7 +1604,7 @@ static inline void tcp_synq_removed(struct sock *sk, struct open_request *req) { - struct tcp_listen_opt *lopt = sk->tp_pinfo.af_tcp.listen_opt; + struct tcp_listen_opt *lopt = tcp_sk(sk)->listen_opt; if (--lopt->qlen == 0) tcp_delete_keepalive_timer(sk); @@ -1608,7 +1614,7 @@ static inline void tcp_synq_added(struct sock *sk) { - struct tcp_listen_opt *lopt = sk->tp_pinfo.af_tcp.listen_opt; + struct tcp_listen_opt *lopt = tcp_sk(sk)->listen_opt; if (lopt->qlen++ == 0) tcp_reset_keepalive_timer(sk, TCP_TIMEOUT_INIT); @@ -1617,17 +1623,17 @@ static inline int tcp_synq_len(struct sock *sk) { - return sk->tp_pinfo.af_tcp.listen_opt->qlen; + return tcp_sk(sk)->listen_opt->qlen; } static inline int tcp_synq_young(struct sock *sk) { - return sk->tp_pinfo.af_tcp.listen_opt->qlen_young; + return tcp_sk(sk)->listen_opt->qlen_young; } static inline int tcp_synq_is_full(struct sock *sk) { - return tcp_synq_len(sk)>>sk->tp_pinfo.af_tcp.listen_opt->max_qlen_log; + return tcp_synq_len(sk) >> tcp_sk(sk)->listen_opt->max_qlen_log; } static inline void tcp_synq_unlink(struct tcp_opt *tp, struct open_request *req, @@ -1641,7 +1647,7 @@ static inline void tcp_synq_drop(struct sock *sk, struct open_request *req, struct open_request **prev) { - tcp_synq_unlink(&sk->tp_pinfo.af_tcp, req, prev); + tcp_synq_unlink(tcp_sk(sk), req, prev); tcp_synq_removed(sk, req); tcp_openreq_free(req); } @@ -1667,7 +1673,7 @@ static inline void tcp_free_skb(struct sock *sk, struct sk_buff *skb) { - sk->tp_pinfo.af_tcp.queue_shrunk = 1; + tcp_sk(sk)->queue_shrunk = 1; sk->wmem_queued -= skb->truesize; sk->forward_alloc += skb->truesize; __kfree_skb(skb); diff -Nru a/include/net/udp.h b/include/net/udp.h --- a/include/net/udp.h Tue Feb 19 18:08:58 2002 +++ b/include/net/udp.h Tue Feb 19 18:08:58 2002 @@ -74,4 +74,6 @@ #define UDP_INC_STATS_BH(field) SNMP_INC_STATS_BH(udp_statistics, field) #define UDP_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_statistics, field) +#define udp_sock inet_sock + #endif /* _UDP_H */ diff -Nru a/include/net/x25.h b/include/net/x25.h --- a/include/net/x25.h Tue Feb 19 18:08:57 2002 +++ b/include/net/x25.h Tue Feb 19 18:08:57 2002 @@ -139,6 +139,8 @@ unsigned long vc_facil_mask; /* inc_call facilities mask */ } x25_cb; +#define x25_sk(__sk) ((x25_cb *)(__sk)->protinfo) + /* af_x25.c */ extern int sysctl_x25_restart_request_timeout; extern int sysctl_x25_call_request_timeout; diff -Nru a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/ac97_codec.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,186 @@ +#ifndef __SOUND_AC97_CODEC_H +#define __SOUND_AC97_CODEC_H + +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.1 + * by Intel Corporation (http://developer.intel.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "control.h" +#include "info.h" + +/* + * AC'97 codec registers + */ + +#define AC97_RESET 0x00 /* Reset */ +#define AC97_MASTER 0x02 /* Master Volume */ +#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ +#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ +#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ +#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ +#define AC97_PHONE 0x0c /* Phone Volume (optional) */ +#define AC97_MIC 0x0e /* MIC Volume */ +#define AC97_LINE 0x10 /* Line In Volume */ +#define AC97_CD 0x12 /* CD Volume */ +#define AC97_VIDEO 0x14 /* Video Volume (optional) */ +#define AC97_AUX 0x16 /* AUX Volume (optional) */ +#define AC97_PCM 0x18 /* PCM Volume */ +#define AC97_REC_SEL 0x1a /* Record Select */ +#define AC97_REC_GAIN 0x1c /* Record Gain */ +#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ +#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ +#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ +#define AC97_RESERVED 0x24 /* Reserved */ +#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ +/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ +#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status */ +#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR DAC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ +#define AC97_SPDIF 0x3a /* S/PDIF control */ +/* range 0x3c-0x58 - MODEM */ +/* range 0x5a-0x7b - Vendor Specific */ +#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ +#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ + +/* extended audio status and control bit defines */ +#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ +#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ +#define AC97_EA_SPDIF 0x0004 /* S/PDIF Enable bit */ +#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ +#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ +#define AC97_EA_SDAC 0x0040 /* PCM Surround DACs are ready (Read only) */ +#define AC97_EA_LDAC 0x0080 /* PCM LFE DAC is ready (Read only) */ +#define AC97_EA_MDAC 0x0100 /* MIC ADC is ready (Read only) */ +#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ +#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ +#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ +#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ +#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ +#define AC97_EA_SLOT_MASK 0xffcf /* Mask for slot assignment bits */ +#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ +#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ +#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ +#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ + +/* S/PDIF control bit defines */ +#define AC97_SC_PRO 0x0001 /* Professional status */ +#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ +#define AC97_SC_COPY 0x0004 /* Copyright status */ +#define AC97_SC_PRE 0x0008 /* Preemphasis status */ +#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ +#define AC97_SC_L 0x0800 /* Generation Level status */ +#define AC97_SC_SPSR_MASK 0xcfff /* S/PDIF Sample Rate bits */ +#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ +#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ +#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ +#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ +#define AC97_SC_V 0x8000 /* Validity status */ + +/* specific - SigmaTel */ +#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */ +#define AC97_SIGMATEL_DAC2INVERT 0x6e +#define AC97_SIGMATEL_BIAS1 0x70 +#define AC97_SIGMATEL_BIAS2 0x72 +#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */ +#define AC97_SIGMATEL_CIC1 0x76 +#define AC97_SIGMATEL_CIC2 0x78 + +/* specific - Analog Devices */ +#define AC97_AD_TEST 0x5a /* test register */ +#define AC97_AD_CODEC_CFG 0x70 /* codec configuration */ +#define AC97_AD_JACK_SPDIF 0x72 /* Jack Sense & S/PDIF */ +#define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */ +#define AC97_AD_MISC 0x76 /* Misc Control Bits */ + +/* ac97->scaps */ +#define AC97_SCAP_SURROUND_DAC (1<<0) /* surround L&R DACs are present */ +#define AC97_SCAP_CENTER_LFE_DAC (1<<1) /* center and LFE DACs are present */ + +/* ac97->flags */ +#define AC97_HAS_PC_BEEP (1<<0) +#define AC97_AD_MULTI (1<<1) /* Analog Devices - multi codecs */ + +/* + + */ + +typedef struct _snd_ac97 ac97_t; + +struct _snd_ac97 { + void (*write) (ac97_t *ac97, unsigned short reg, unsigned short val); + unsigned short (*read) (ac97_t *ac97, unsigned short reg); + void (*init) (ac97_t *ac97); + snd_info_entry_t *proc_entry; + snd_info_entry_t *proc_regs_entry; + void *private_data; + void (*private_free) (ac97_t *ac97); + /* --- */ + snd_card_t *card; + spinlock_t reg_lock; + unsigned short num; /* number of codec: 0 = primary, 1 = secondary */ + unsigned short addr; /* physical address of codec [0-3] */ + unsigned int id; /* identification of codec */ + unsigned short caps; /* capabilities (register 0) */ + unsigned short ext_id; /* extended feature identification (register 28) */ + unsigned int scaps; /* driver capabilities */ + unsigned int flags; /* specific code */ + unsigned int clock; /* AC'97 clock (usually 48000Hz) */ + unsigned int rates_front_dac; + unsigned int rates_surr_dac; + unsigned int rates_lfe_dac; + unsigned int rates_adc; + unsigned int rates_mic_adc; + unsigned int spdif_status; + unsigned short regs[0x80]; /* register cache */ + unsigned char reg_accessed[0x80 / 8]; /* bit flags */ + union { /* vendor specific code */ + struct { + unsigned short unchained[3]; // 0 = C34, 1 = C79, 2 = C69 + unsigned short chained[3]; // 0 = C34, 1 = C79, 2 = C69 + unsigned short id[3]; // codec IDs (lower 16-bit word) + unsigned short pcmreg[3]; // PCM registers + struct semaphore mutex; + } ad18xx; + } spec; +}; + +int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97); + +void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value); +unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg); +void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value); +int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value); +int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value); +int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate); +#ifdef CONFIG_PM +void snd_ac97_suspend(ac97_t *ac97); +void snd_ac97_resume(ac97_t *ac97); +#endif + +#endif /* __SOUND_AC97_CODEC_H */ diff -Nru a/include/sound/ad1816a.h b/include/sound/ad1816a.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/ad1816a.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,174 @@ +#ifndef __SOUND_AD1816A_H +#define __SOUND_AD1816A_H + +/* + ad1816a.h - definitions for ADI SoundPort AD1816A chip. + Copyright (C) 1999-2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "control.h" +#include "pcm.h" +#include "timer.h" + +#define AD1816A_REG(r) (chip->port + r) + +#define AD1816A_CHIP_STATUS 0x00 +#define AD1816A_INDIR_ADDR 0x00 +#define AD1816A_INTERRUPT_STATUS 0x01 +#define AD1816A_INDIR_DATA_LOW 0x02 +#define AD1816A_INDIR_DATA_HIGH 0x03 +#define AD1816A_PIO_DEBUG 0x04 +#define AD1816A_PIO_STATUS 0x05 +#define AD1816A_PIO_DATA 0x06 +#define AD1816A_RESERVED_7 0x07 +#define AD1816A_PLAYBACK_CONFIG 0x08 +#define AD1816A_CAPTURE_CONFIG 0x09 +#define AD1816A_RESERVED_10 0x0a +#define AD1816A_RESERVED_11 0x0b +#define AD1816A_JOYSTICK_RAW_DATA 0x0c +#define AD1816A_JOYSTICK_CTRL 0x0d +#define AD1816A_JOY_POS_DATA_LOW 0x0e +#define AD1816A_JOY_POS_DATA_HIGH 0x0f + +#define AD1816A_LOW_BYTE_TMP 0x00 +#define AD1816A_INTERRUPT_ENABLE 0x01 +#define AD1816A_EXTERNAL_CTRL 0x01 +#define AD1816A_PLAYBACK_SAMPLE_RATE 0x02 +#define AD1816A_CAPTURE_SAMPLE_RATE 0x03 +#define AD1816A_VOICE_ATT 0x04 +#define AD1816A_FM_ATT 0x05 +#define AD1816A_I2S_1_ATT 0x06 +#define AD1816A_I2S_0_ATT 0x07 +#define AD1816A_PLAYBACK_BASE_COUNT 0x08 +#define AD1816A_PLAYBACK_CURR_COUNT 0x09 +#define AD1816A_CAPTURE_BASE_COUNT 0x0a +#define AD1816A_CAPTURE_CURR_COUNT 0x0b +#define AD1816A_TIMER_BASE_COUNT 0x0c +#define AD1816A_TIMER_CURR_COUNT 0x0d +#define AD1816A_MASTER_ATT 0x0e +#define AD1816A_CD_GAIN_ATT 0x0f +#define AD1816A_SYNTH_GAIN_ATT 0x10 +#define AD1816A_VID_GAIN_ATT 0x11 +#define AD1816A_LINE_GAIN_ATT 0x12 +#define AD1816A_MIC_GAIN_ATT 0x13 +#define AD1816A_PHONE_IN_GAIN_ATT 0x13 +#define AD1816A_ADC_SOURCE_SEL 0x14 +#define AD1816A_ADC_PGA 0x14 +#define AD1816A_CHIP_CONFIG 0x20 +#define AD1816A_DSP_CONFIG 0x21 +#define AD1816A_FM_SAMPLE_RATE 0x22 +#define AD1816A_I2S_1_SAMPLE_RATE 0x23 +#define AD1816A_I2S_0_SAMPLE_RATE 0x24 +#define AD1816A_RESERVED_37 0x25 +#define AD1816A_PROGRAM_CLOCK_RATE 0x26 +#define AD1816A_3D_PHAT_CTRL 0x27 +#define AD1816A_PHONE_OUT_ATT 0x27 +#define AD1816A_RESERVED_40 0x28 +#define AD1816A_HW_VOL_BUT 0x29 +#define AD1816A_DSP_MAILBOX_0 0x2a +#define AD1816A_DSP_MAILBOX_1 0x2b +#define AD1816A_POWERDOWN_CTRL 0x2c +#define AD1816A_TIMER_CTRL 0x2c +#define AD1816A_VERSION_ID 0x2d +#define AD1816A_RESERVED_46 0x2e + +#define AD1816A_READY 0x80 + +#define AD1816A_PLAYBACK_IRQ_PENDING 0x80 +#define AD1816A_CAPTURE_IRQ_PENDING 0x40 +#define AD1816A_TIMER_IRQ_PENDING 0x20 + +#define AD1816A_PLAYBACK_ENABLE 0x01 +#define AD1816A_PLAYBACK_PIO 0x02 +#define AD1816A_CAPTURE_ENABLE 0x01 +#define AD1816A_CAPTURE_PIO 0x02 + +#define AD1816A_FMT_LINEAR_8 0x00 +#define AD1816A_FMT_ULAW_8 0x08 +#define AD1816A_FMT_LINEAR_16_LIT 0x10 +#define AD1816A_FMT_ALAW_8 0x18 +#define AD1816A_FMT_LINEAR_16_BIG 0x30 +#define AD1816A_FMT_ALL 0x38 +#define AD1816A_FMT_STEREO 0x04 + +#define AD1816A_PLAYBACK_IRQ_ENABLE 0x8000 +#define AD1816A_CAPTURE_IRQ_ENABLE 0x4000 +#define AD1816A_TIMER_IRQ_ENABLE 0x2000 +#define AD1816A_TIMER_ENABLE 0x0080 + +#define AD1816A_SRC_LINE 0x00 +#define AD1816A_SRC_OUT 0x10 +#define AD1816A_SRC_CD 0x20 +#define AD1816A_SRC_SYNTH 0x30 +#define AD1816A_SRC_VIDEO 0x40 +#define AD1816A_SRC_MIC 0x50 +#define AD1816A_SRC_MONO 0x50 +#define AD1816A_SRC_PHONE_IN 0x60 +#define AD1816A_SRC_MASK 0x70 + +#define AD1816A_CAPTURE_NOT_EQUAL 0x1000 +#define AD1816A_WSS_ENABLE 0x8000 + +typedef struct _snd_ad1816a ad1816a_t; + +struct _snd_ad1816a { + unsigned long port; + struct resource *res_port; + int irq; + int dma1; + int dma2; + + unsigned short hardware; + unsigned short version; + + spinlock_t lock; + + unsigned short mode; + + snd_card_t *card; + snd_pcm_t *pcm; + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p_dma_size; + unsigned int c_dma_size; + + snd_timer_t *timer; +}; + + +#define AD1816A_HW_AUTO 0 +#define AD1816A_HW_AD1816A 1 +#define AD1816A_HW_AD1815 2 +#define AD1816A_HW_AD18MAX10 3 + +#define AD1816A_MODE_PLAYBACK 0x01 +#define AD1816A_MODE_CAPTURE 0x02 +#define AD1816A_MODE_TIMER 0x04 +#define AD1816A_MODE_OPEN (AD1816A_MODE_PLAYBACK | \ + AD1816A_MODE_CAPTURE | \ + AD1816A_MODE_TIMER) + + +extern int snd_ad1816a_create(snd_card_t *card, unsigned long port, + int irq, int dma1, int dma2, + ad1816a_t **chip); + +extern int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm); +extern int snd_ad1816a_mixer(ad1816a_t *chip); + +#endif /* __SOUND_AD1816A_H */ diff -Nru a/include/sound/ad1848.h b/include/sound/ad1848.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/ad1848.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,192 @@ +#ifndef __SOUND_AD1848_H +#define __SOUND_AD1848_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for AD1847/AD1848/CS4248 chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "control.h" +#include "pcm.h" + +/* IO ports */ + +#define AD1848P( codec, x ) ( (chip) -> port + c_d_c_AD1848##x ) + +#define c_d_c_AD1848REGSEL 0 +#define c_d_c_AD1848REG 1 +#define c_d_c_AD1848STATUS 2 +#define c_d_c_AD1848PIO 3 + +/* codec registers */ + +#define AD1848_LEFT_INPUT 0x00 /* left input control */ +#define AD1848_RIGHT_INPUT 0x01 /* right input control */ +#define AD1848_AUX1_LEFT_INPUT 0x02 /* left AUX1 input control */ +#define AD1848_AUX1_RIGHT_INPUT 0x03 /* right AUX1 input control */ +#define AD1848_AUX2_LEFT_INPUT 0x04 /* left AUX2 input control */ +#define AD1848_AUX2_RIGHT_INPUT 0x05 /* right AUX2 input control */ +#define AD1848_LEFT_OUTPUT 0x06 /* left output control register */ +#define AD1848_RIGHT_OUTPUT 0x07 /* right output control register */ +#define AD1848_DATA_FORMAT 0x08 /* clock and data format - playback/capture - bits 7-0 MCE */ +#define AD1848_IFACE_CTRL 0x09 /* interface control - bits 7-2 MCE */ +#define AD1848_PIN_CTRL 0x0a /* pin control */ +#define AD1848_TEST_INIT 0x0b /* test and initialization */ +#define AD1848_MISC_INFO 0x0c /* miscellaneaous information */ +#define AD1848_LOOPBACK 0x0d /* loopback control */ +#define AD1848_DATA_UPR_CNT 0x0e /* playback/capture upper base count */ +#define AD1848_DATA_LWR_CNT 0x0f /* playback/capture lower base count */ + +/* definitions for codec register select port - CODECP( REGSEL ) */ + +#define AD1848_INIT 0x80 /* CODEC is initializing */ +#define AD1848_MCE 0x40 /* mode change enable */ +#define AD1848_TRD 0x20 /* transfer request disable */ + +/* definitions for codec status register - CODECP( STATUS ) */ + +#define AD1848_GLOBALIRQ 0x01 /* IRQ is active */ + +/* definitions for AD1848_LEFT_INPUT and AD1848_RIGHT_INPUT registers */ + +#define AD1848_ENABLE_MIC_GAIN 0x20 + +#define AD1848_MIXS_LINE1 0x00 +#define AD1848_MIXS_AUX1 0x40 +#define AD1848_MIXS_LINE2 0x80 +#define AD1848_MIXS_ALL 0xc0 + +/* definitions for clock and data format register - AD1848_PLAYBK_FORMAT */ + +#define AD1848_LINEAR_8 0x00 /* 8-bit unsigned data */ +#define AD1848_ALAW_8 0x60 /* 8-bit A-law companded */ +#define AD1848_ULAW_8 0x20 /* 8-bit U-law companded */ +#define AD1848_LINEAR_16 0x40 /* 16-bit twos complement data - little endian */ +#define AD1848_STEREO 0x10 /* stereo mode */ +/* bits 3-1 define frequency divisor */ +#define AD1848_XTAL1 0x00 /* 24.576 crystal */ +#define AD1848_XTAL2 0x01 /* 16.9344 crystal */ + +/* definitions for interface control register - AD1848_IFACE_CTRL */ + +#define AD1848_CAPTURE_PIO 0x80 /* capture PIO enable */ +#define AD1848_PLAYBACK_PIO 0x40 /* playback PIO enable */ +#define AD1848_CALIB_MODE 0x18 /* calibration mode bits */ +#define AD1848_AUTOCALIB 0x08 /* auto calibrate */ +#define AD1848_SINGLE_DMA 0x04 /* use single DMA channel */ +#define AD1848_CAPTURE_ENABLE 0x02 /* capture enable */ +#define AD1848_PLAYBACK_ENABLE 0x01 /* playback enable */ + +/* definitions for pin control register - AD1848_PIN_CTRL */ + +#define AD1848_IRQ_ENABLE 0x02 /* enable IRQ */ +#define AD1848_XCTL1 0x40 /* external control #1 */ +#define AD1848_XCTL0 0x80 /* external control #0 */ + +/* definitions for test and init register - AD1848_TEST_INIT */ + +#define AD1848_CALIB_IN_PROGRESS 0x20 /* auto calibrate in progress */ +#define AD1848_DMA_REQUEST 0x10 /* DMA request in progress */ + +/* defines for codec.mode */ + +#define AD1848_MODE_NONE 0x0000 +#define AD1848_MODE_PLAY 0x0001 +#define AD1848_MODE_CAPTURE 0x0002 +#define AD1848_MODE_TIMER 0x0004 +#define AD1848_MODE_OPEN (AD1848_MODE_PLAY|AD1848_MODE_CAPTURE|AD1848_MODE_TIMER) +#define AD1848_MODE_RUNNING 0x0010 + +/* defines for codec.hardware */ + +#define AD1848_HW_DETECT 0x0000 /* let AD1848 driver detect chip */ +#define AD1848_HW_AD1847 0x0001 /* AD1847 chip */ +#define AD1848_HW_AD1848 0x0002 /* AD1848 chip */ +#define AD1848_HW_CS4248 0x0003 /* CS4248 chip */ +#define AD1848_HW_CMI8330 0x0004 /* CMI8330 chip */ + +struct _snd_ad1848 { + unsigned long port; /* i/o port */ + struct resource *res_port; + int irq; /* IRQ line */ + int dma; /* data DMA */ + unsigned short version; /* version of CODEC chip */ + unsigned short mode; /* see to AD1848_MODE_XXXX */ + unsigned short hardware; /* see to AD1848_HW_XXXX */ + unsigned short single_dma:1; /* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */ + + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_card_t *card; + + unsigned char image[32]; /* SGalaxy needs an access to extended registers */ + int mce_bit; + int calibrate_mute; + int dma_size; + + spinlock_t reg_lock; + struct semaphore open_mutex; +}; + +typedef struct _snd_ad1848 ad1848_t; + +/* exported functions */ + +void snd_ad1848_out(ad1848_t *chip, unsigned char reg, unsigned char value); +void snd_ad1848_dout(ad1848_t *chip, unsigned char reg, unsigned char value); +unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg); +void snd_ad1848_mce_up(ad1848_t *chip); +void snd_ad1848_mce_down(ad1848_t *chip); + +int snd_ad1848_create(snd_card_t * card, + unsigned long port, + int irq, int dma, + unsigned short hardware, + ad1848_t ** chip); + +int snd_ad1848_pcm(ad1848_t * chip, int device, snd_pcm_t **rpcm); +int snd_ad1848_mixer(ad1848_t * chip); +void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ad1848_info_single, \ + get: snd_ad1848_get_single, put: snd_ad1848_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +#define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ad1848_info_double, \ + get: snd_ad1848_get_double, put: snd_ad1848_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +#ifdef CONFIG_SND_DEBUG +void snd_ad1848_debug(ad1848_t *chip); +#endif + +#endif /* __SOUND_AD1848_H */ diff -Nru a/include/sound/ainstr_fm.h b/include/sound/ainstr_fm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/ainstr_fm.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,132 @@ +/* + * Advanced Linux Sound Architecture + * + * FM (OPL2/3) Instrument Format + * Copyright (c) 2000 Uros Bizjak + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_FM_H +#define __SOUND_AINSTR_FM_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define FM_SHARE_FILE 0 + +/* + * FM operator + */ + +typedef struct fm_operator { + unsigned char am_vib; + unsigned char ksl_level; + unsigned char attack_decay; + unsigned char sustain_release; + unsigned char wave_select; +} fm_operator_t; + +/* + * Instrument + */ + +#define FM_PATCH_OPL2 0x01 /* OPL2 2 operators FM instrument */ +#define FM_PATCH_OPL3 0x02 /* OPL3 4 operators FM instrument */ + +typedef struct { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned char type; /* instrument type */ + + fm_operator_t op[4]; + unsigned char feedback_connection[2]; + + unsigned char echo_delay; + unsigned char echo_atten; + unsigned char chorus_spread; + unsigned char trnsps; + unsigned char fix_dur; + unsigned char modes; + unsigned char fix_key; +} fm_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * fm_xinstrument FM_STRU_INSTR + * + */ + +#define FM_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * FM operator + */ + +typedef struct fm_xoperator { + __u8 am_vib; + __u8 ksl_level; + __u8 attack_decay; + __u8 sustain_release; + __u8 wave_select; +} fm_xoperator_t; + +/* + * Instrument + */ + +typedef struct fm_xinstrument { + __u32 stype; /* structure type */ + + __u32 share_id[4]; /* share id - zero = no sharing */ + __u8 type; /* instrument type */ + + fm_xoperator_t op[4]; /* fm operators */ + __u8 feedback_connection[2]; + + __u8 echo_delay; + __u8 echo_atten; + __u8 chorus_spread; + __u8 trnsps; + __u8 fix_dur; + __u8 modes; + __u8 fix_key; +} fm_xinstrument_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_fm_id; + +int snd_seq_fm_init(snd_seq_kinstr_ops_t * ops, + snd_seq_kinstr_ops_t * next); + +#endif + +#endif /* __SOUND_AINSTR_FM_H */ diff -Nru a/include/sound/ainstr_gf1.h b/include/sound/ainstr_gf1.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/ainstr_gf1.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,227 @@ +/* + * Advanced Linux Sound Architecture + * + * GF1 (GUS) Patch Instrument Format + * Copyright (c) 1994-99 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_GF1_H +#define __SOUND_AINSTR_GF1_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define GF1_SHARE_FILE 0 + +/* + * wave formats + */ + +#define GF1_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define GF1_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define GF1_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define GF1_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */ +#define GF1_WAVE_LOOP 0x0008 /* loop mode */ +#define GF1_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define GF1_WAVE_STEREO 0x0100 /* stereo mode */ +#define GF1_WAVE_ULAW 0x0200 /* uLaw compression mode */ + +/* + * Wavetable definitions + */ + +typedef struct gf1_wave { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned int format; /* wave format */ + + struct { + unsigned int number; /* some other ID for this instrument */ + unsigned int memory; /* begin of waveform in onboard memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } address; + + unsigned int size; /* size of waveform in samples */ + unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + + unsigned char flags; /* GF1 patch flags */ + unsigned char pad; + unsigned int sample_rate; /* sample rate in Hz */ + unsigned int low_frequency; /* low frequency range */ + unsigned int high_frequency; /* high frequency range */ + unsigned int root_frequency; /* root frequency range */ + signed short tune; + unsigned char balance; + unsigned char envelope_rate[6]; + unsigned char envelope_offset[6]; + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + unsigned short scale_frequency; + unsigned short scale_factor; /* 0-2048 or 0-2 */ + + struct gf1_wave *next; +} gf1_wave_t; + +/* + * Instrument + */ + +#define IWFFFF_EXCLUDE_NONE 0x0000 /* exclusion mode - none */ +#define IWFFFF_EXCLUDE_SINGLE 0x0001 /* exclude single - single note from the instrument group */ +#define IWFFFF_EXCLUDE_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */ + +#define IWFFFF_EFFECT_NONE 0 +#define IWFFFF_EFFECT_REVERB 1 +#define IWFFFF_EFFECT_CHORUS 2 +#define IWFFFF_EFFECT_ECHO 3 + +typedef struct { + unsigned short exclusion; + unsigned short exclusion_group; /* 0 - none, 1-65535 */ + + unsigned char effect1; /* effect 1 */ + unsigned char effect1_depth; /* 0-127 */ + unsigned char effect2; /* effect 2 */ + unsigned char effect2_depth; /* 0-127 */ + + gf1_wave_t *wave; /* first waveform */ +} gf1_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * gf1_xinstrument IWFFFF_STRU_INSTR + * +gf1_xwave IWFFFF_STRU_WAVE + * + */ + +#define GF1_STRU_WAVE __cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E') +#define GF1_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * Wavetable definitions + */ + +typedef struct gf1_xwave { + __u32 stype; /* structure type */ + + __u32 share_id[4]; /* share id - zero = no sharing */ + __u32 format; /* wave format */ + + __u32 size; /* size of waveform in samples */ + __u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u16 loop_repeat; /* loop repeat - 0 = forever */ + + __u8 flags; /* GF1 patch flags */ + __u8 pad; + __u32 sample_rate; /* sample rate in Hz */ + __u32 low_frequency; /* low frequency range */ + __u32 high_frequency; /* high frequency range */ + __u32 root_frequency; /* root frequency range */ + __s16 tune; + __u8 balance; + __u8 envelope_rate[6]; + __u8 envelope_offset[6]; + __u8 tremolo_sweep; + __u8 tremolo_rate; + __u8 tremolo_depth; + __u8 vibrato_sweep; + __u8 vibrato_rate; + __u8 vibrato_depth; + __u16 scale_frequency; + __u16 scale_factor; /* 0-2048 or 0-2 */ +} gf1_xwave_t; + +/* + * Instrument + */ + +typedef struct gf1_xinstrument { + __u32 stype; + + __u16 exclusion; + __u16 exclusion_group; /* 0 - none, 1-65535 */ + + __u8 effect1; /* effect 1 */ + __u8 effect1_depth; /* 0-127 */ + __u8 effect2; /* effect 2 */ + __u8 effect2_depth; /* 0-127 */ +} gf1_xinstrument_t; + +/* + * Instrument info + */ + +#define GF1_INFO_ENVELOPE (1<<0) +#define GF1_INFO_TREMOLO (1<<1) +#define GF1_INFO_VIBRATO (1<<2) + +typedef struct gf1_info { + unsigned char flags; /* supported wave flags */ + unsigned char pad[3]; + unsigned int features; /* supported features */ + unsigned int max8_len; /* maximum 8-bit wave length */ + unsigned int max16_len; /* maximum 16-bit wave length */ +} gf1_info_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_gf1_id; + +typedef struct { + void *private_data; + int (*info)(void *private_data, gf1_info_t *info); + int (*put_sample)(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); + int (*get_sample)(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); + int (*remove_sample)(void *private_data, gf1_wave_t *wave, + int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what); + snd_seq_kinstr_ops_t kops; +} snd_gf1_ops_t; + +int snd_seq_gf1_init(snd_gf1_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next); + +#endif + +#endif /* __SOUND_AINSTR_GF1_H */ diff -Nru a/include/sound/ainstr_iw.h b/include/sound/ainstr_iw.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/ainstr_iw.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,375 @@ +/* + * Advanced Linux Sound Architecture + * + * InterWave FFFF Instrument Format + * Copyright (c) 1994-99 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_IW_H +#define __SOUND_AINSTR_IW_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define IWFFFF_SHARE_FILE 0 + +/* + * wave formats + */ + +#define IWFFFF_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define IWFFFF_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define IWFFFF_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define IWFFFF_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */ +#define IWFFFF_WAVE_LOOP 0x0008 /* loop mode */ +#define IWFFFF_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define IWFFFF_WAVE_ULAW 0x0020 /* uLaw compressed wave */ +#define IWFFFF_WAVE_RAM 0x0040 /* wave is _preloaded_ in RAM (it is used for ROM simulation) */ +#define IWFFFF_WAVE_ROM 0x0080 /* wave is in ROM */ +#define IWFFFF_WAVE_STEREO 0x0100 /* wave is stereo */ + +/* + * Wavetable definitions + */ + +typedef struct iwffff_wave { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned int format; /* wave format */ + + struct { + unsigned int number; /* some other ID for this wave */ + unsigned int memory; /* begin of waveform in onboard memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } address; + + unsigned int size; /* size of waveform in samples */ + unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + unsigned int sample_ratio; /* sample ratio (44100 * 1024 / rate) */ + unsigned char attenuation; /* 0 - 127 (no corresponding midi controller) */ + unsigned char low_note; /* lower frequency range for this waveform */ + unsigned char high_note; /* higher frequency range for this waveform */ + unsigned char pad; + + struct iwffff_wave *next; +} iwffff_wave_t; + +/* + * Layer + */ + +#define IWFFFF_LFO_SHAPE_TRIANGLE 0 +#define IWFFFF_LFO_SHAPE_POSTRIANGLE 1 + +typedef struct iwffff_lfo { + unsigned short freq; /* (0-2047) 0.01Hz - 21.5Hz */ + signed short depth; /* volume +- (0-255) 0.48675dB/step */ + signed short sweep; /* 0 - 950 deciseconds */ + unsigned char shape; /* see to IWFFFF_LFO_SHAPE_XXXX */ + unsigned char delay; /* 0 - 255 deciseconds */ +} iwffff_lfo_t; + +#define IWFFFF_ENV_FLAG_RETRIGGER 0x0001 /* flag - retrigger */ + +#define IWFFFF_ENV_MODE_ONE_SHOT 0x0001 /* mode - one shot */ +#define IWFFFF_ENV_MODE_SUSTAIN 0x0002 /* mode - sustain */ +#define IWFFFF_ENV_MODE_NO_SUSTAIN 0x0003 /* mode - no sustain */ + +#define IWFFFF_ENV_INDEX_VELOCITY 0x0001 /* index - velocity */ +#define IWFFFF_ENV_INDEX_FREQUENCY 0x0002 /* index - frequency */ + +typedef struct iwffff_env_point { + unsigned short offset; + unsigned short rate; +} iwffff_env_point_t; + +typedef struct iwffff_env_record { + unsigned short nattack; + unsigned short nrelease; + unsigned short sustain_offset; + unsigned short sustain_rate; + unsigned short release_rate; + unsigned char hirange; + unsigned char pad; + struct iwffff_env_record *next; + /* points are stored here */ + /* count of points = nattack + nrelease */ +} iwffff_env_record_t; + +typedef struct iwffff_env { + unsigned char flags; + unsigned char mode; + unsigned char index; + unsigned char pad; + struct iwffff_env_record *record; +} iwffff_env_t; + +#define IWFFFF_LAYER_FLAG_RETRIGGER 0x0001 /* retrigger */ + +#define IWFFFF_LAYER_VELOCITY_TIME 0x0000 /* velocity mode = time */ +#define IWFFFF_LAYER_VELOCITY_RATE 0x0001 /* velocity mode = rate */ + +#define IWFFFF_LAYER_EVENT_KUP 0x0000 /* layer event - key up */ +#define IWFFFF_LAYER_EVENT_KDOWN 0x0001 /* layer event - key down */ +#define IWFFFF_LAYER_EVENT_RETRIG 0x0002 /* layer event - retrigger */ +#define IWFFFF_LAYER_EVENT_LEGATO 0x0003 /* layer event - legato */ + +typedef struct iwffff_layer { + unsigned char flags; + unsigned char velocity_mode; + unsigned char layer_event; + unsigned char low_range; /* range for layer based */ + unsigned char high_range; /* on either velocity or frequency */ + unsigned char pan; /* pan offset from CC1 (0 left - 127 right) */ + unsigned char pan_freq_scale; /* position based on frequency (0-127) */ + unsigned char attenuation; /* 0-127 (no corresponding midi controller) */ + iwffff_lfo_t tremolo; /* tremolo effect */ + iwffff_lfo_t vibrato; /* vibrato effect */ + unsigned short freq_scale; /* 0-2048, 1024 is equal to semitone scaling */ + unsigned char freq_center; /* center for keyboard frequency scaling */ + unsigned char pad; + iwffff_env_t penv; /* pitch envelope */ + iwffff_env_t venv; /* volume envelope */ + + iwffff_wave_t *wave; + struct iwffff_layer *next; +} iwffff_layer_t; + +/* + * Instrument + */ + +#define IWFFFF_EXCLUDE_NONE 0x0000 /* exclusion mode - none */ +#define IWFFFF_EXCLUDE_SINGLE 0x0001 /* exclude single - single note from the instrument group */ +#define IWFFFF_EXCLUDE_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */ + +#define IWFFFF_LAYER_NONE 0x0000 /* not layered */ +#define IWFFFF_LAYER_ON 0x0001 /* layered */ +#define IWFFFF_LAYER_VELOCITY 0x0002 /* layered by velocity */ +#define IWFFFF_LAYER_FREQUENCY 0x0003 /* layered by frequency */ + +#define IWFFFF_EFFECT_NONE 0 +#define IWFFFF_EFFECT_REVERB 1 +#define IWFFFF_EFFECT_CHORUS 2 +#define IWFFFF_EFFECT_ECHO 3 + +typedef struct { + unsigned short exclusion; + unsigned short layer_type; + unsigned short exclusion_group; /* 0 - none, 1-65535 */ + + unsigned char effect1; /* effect 1 */ + unsigned char effect1_depth; /* 0-127 */ + unsigned char effect2; /* effect 2 */ + unsigned char effect2_depth; /* 0-127 */ + + iwffff_layer_t *layer; /* first layer */ +} iwffff_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * iwffff_xinstrument IWFFFF_STRU_INSTR + * +iwffff_xlayer IWFFFF_STRU_LAYER + * *iwffff_xenv_record IWFFFF_STRU_ENV_RECT (tremolo) + * *iwffff_xenv_record IWFFFF_STRU_EVN_RECT (vibrato) + * +iwffff_xwave IWFFFF_STRU_WAVE + * + */ + +#define IWFFFF_STRU_WAVE __cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E') +#define IWFFFF_STRU_ENV_RECP __cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'P') +#define IWFFFF_STRU_ENV_RECV __cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'V') +#define IWFFFF_STRU_LAYER __cpu_to_be32(('L'<<24)|('A'<<16)|('Y'<<8)|'R') +#define IWFFFF_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * Wavetable definitions + */ + +typedef struct iwffff_xwave { + __u32 stype; /* structure type */ + + __u32 share_id[4]; /* share id - zero = no sharing */ + + __u32 format; /* wave format */ + __u32 offset; /* offset to ROM (address) */ + + __u32 size; /* size of waveform in samples */ + __u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u16 loop_repeat; /* loop repeat - 0 = forever */ + __u32 sample_ratio; /* sample ratio (44100 * 1024 / rate) */ + __u8 attenuation; /* 0 - 127 (no corresponding midi controller) */ + __u8 low_note; /* lower frequency range for this waveform */ + __u8 high_note; /* higher frequency range for this waveform */ + __u8 pad; +} iwffff_xwave_t; + +/* + * Layer + */ + +typedef struct iwffff_xlfo { + __u16 freq; /* (0-2047) 0.01Hz - 21.5Hz */ + __s16 depth; /* volume +- (0-255) 0.48675dB/step */ + __s16 sweep; /* 0 - 950 deciseconds */ + __u8 shape; /* see to ULTRA_IW_LFO_SHAPE_XXXX */ + __u8 delay; /* 0 - 255 deciseconds */ +} iwffff_xlfo_t; + +typedef struct iwffff_xenv_point { + __u16 offset; + __u16 rate; +} iwffff_xenv_point_t; + +typedef struct iwffff_xenv_record { + __u32 stype; + __u16 nattack; + __u16 nrelease; + __u16 sustain_offset; + __u16 sustain_rate; + __u16 release_rate; + __u8 hirange; + __u8 pad; + /* points are stored here.. */ + /* count of points = nattack + nrelease */ +} iwffff_xenv_record_t; + +typedef struct iwffff_xenv { + __u8 flags; + __u8 mode; + __u8 index; + __u8 pad; +} iwffff_xenv_t; + +typedef struct iwffff_xlayer { + __u32 stype; + __u8 flags; + __u8 velocity_mode; + __u8 layer_event; + __u8 low_range; /* range for layer based */ + __u8 high_range; /* on either velocity or frequency */ + __u8 pan; /* pan offset from CC1 (0 left - 127 right) */ + __u8 pan_freq_scale; /* position based on frequency (0-127) */ + __u8 attenuation; /* 0-127 (no corresponding midi controller) */ + iwffff_xlfo_t tremolo; /* tremolo effect */ + iwffff_xlfo_t vibrato; /* vibrato effect */ + __u16 freq_scale; /* 0-2048, 1024 is equal to semitone scaling */ + __u8 freq_center; /* center for keyboard frequency scaling */ + __u8 pad; + iwffff_xenv_t penv; /* pitch envelope */ + iwffff_xenv_t venv; /* volume envelope */ +} iwffff_xlayer_t; + +/* + * Instrument + */ + +typedef struct iwffff_xinstrument { + __u32 stype; + + __u16 exclusion; + __u16 layer_type; + __u16 exclusion_group; /* 0 - none, 1-65535 */ + + __u8 effect1; /* effect 1 */ + __u8 effect1_depth; /* 0-127 */ + __u8 effect2; /* effect 2 */ + __u8 effect2_depth; /* 0-127 */ +} iwffff_xinstrument_t; + +/* + * ROM support + * InterWave ROMs are Little-Endian (x86) + */ + +#define IWFFFF_ROM_HDR_SIZE 512 + +typedef struct { + __u8 iwave[8]; + __u8 revision; + __u8 series_number; + __u8 series_name[16]; + __u8 date[10]; + __u16 vendor_revision_major; + __u16 vendor_revision_minor; + __u32 rom_size; + __u8 copyright[128]; + __u8 vendor_name[64]; + __u8 description[128]; +} iwffff_rom_header_t; + +/* + * Instrument info + */ + +#define IWFFFF_INFO_LFO_VIBRATO (1<<0) +#define IWFFFF_INFO_LFO_VIBRATO_SHAPE (1<<1) +#define IWFFFF_INFO_LFO_TREMOLO (1<<2) +#define IWFFFF_INFO_LFO_TREMOLO_SHAPE (1<<3) + +typedef struct iwffff_info { + unsigned int format; /* supported format bits */ + unsigned int effects; /* supported effects (1 << IWFFFF_EFFECT*) */ + unsigned int lfos; /* LFO effects */ + unsigned int max8_len; /* maximum 8-bit wave length */ + unsigned int max16_len; /* maximum 16-bit wave length */ +} iwffff_info_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_iwffff_id; + +typedef struct { + void *private_data; + int (*info)(void *private_data, iwffff_info_t *info); + int (*put_sample)(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); + int (*get_sample)(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); + int (*remove_sample)(void *private_data, iwffff_wave_t *wave, + int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what); + snd_seq_kinstr_ops_t kops; +} snd_iwffff_ops_t; + +int snd_seq_iwffff_init(snd_iwffff_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next); + +#endif + +#endif /* __SOUND_AINSTR_IW_H */ diff -Nru a/include/sound/ainstr_simple.h b/include/sound/ainstr_simple.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/ainstr_simple.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,158 @@ +/* + * Advanced Linux Sound Architecture + * + * Simple (MOD player) Instrument Format + * Copyright (c) 1994-99 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_SIMPLE_H +#define __SOUND_AINSTR_SIMPLE_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define SIMPLE_SHARE_FILE 0 + +/* + * wave formats + */ + +#define SIMPLE_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define SIMPLE_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define SIMPLE_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define SIMPLE_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */ +#define SIMPLE_WAVE_LOOP 0x0008 /* loop mode */ +#define SIMPLE_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define SIMPLE_WAVE_STEREO 0x0100 /* stereo wave */ +#define SIMPLE_WAVE_ULAW 0x0200 /* uLaw compression mode */ + +/* + * instrument effects + */ + +#define SIMPLE_EFFECT_NONE 0 +#define SIMPLE_EFFECT_REVERB 1 +#define SIMPLE_EFFECT_CHORUS 2 +#define SIMPLE_EFFECT_ECHO 3 + +/* + * instrument info + */ + +typedef struct simple_instrument_info { + unsigned int format; /* supported format bits */ + unsigned int effects; /* supported effects (1 << SIMPLE_EFFECT_*) */ + unsigned int max8_len; /* maximum 8-bit wave length */ + unsigned int max16_len; /* maximum 16-bit wave length */ +} simple_instrument_info_t; + +/* + * Instrument + */ + +typedef struct { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned int format; /* wave format */ + + struct { + unsigned int number; /* some other ID for this instrument */ + unsigned int memory; /* begin of waveform in onboard memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } address; + + unsigned int size; /* size of waveform in samples */ + unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop end offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + + unsigned char effect1; /* effect 1 */ + unsigned char effect1_depth; /* 0-127 */ + unsigned char effect2; /* effect 2 */ + unsigned char effect2_depth; /* 0-127 */ +} simple_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * simple_xinstrument SIMPLE_STRU_INSTR + * + */ + +#define SIMPLE_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * Instrument + */ + +typedef struct simple_xinstrument { + __u32 stype; + + __u32 share_id[4]; /* share id - zero = no sharing */ + __u32 format; /* wave format */ + + __u32 size; /* size of waveform in samples */ + __u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u16 loop_repeat; /* loop repeat - 0 = forever */ + + __u8 effect1; /* effect 1 */ + __u8 effect1_depth; /* 0-127 */ + __u8 effect2; /* effect 2 */ + __u8 effect2_depth; /* 0-127 */ +} simple_xinstrument_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_simple_id; + +typedef struct { + void *private_data; + int (*info)(void *private_data, simple_instrument_info_t *info); + int (*put_sample)(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); + int (*get_sample)(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); + int (*remove_sample)(void *private_data, simple_instrument_t *instr, + int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what); + snd_seq_kinstr_ops_t kops; +} snd_simple_ops_t; + +int snd_seq_simple_init(snd_simple_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next); + +#endif + +#endif /* __SOUND_AINSTR_SIMPLE_H */ diff -Nru a/include/sound/ak4531_codec.h b/include/sound/ak4531_codec.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/ak4531_codec.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,81 @@ +#ifndef __SOUND_AK4531_CODEC_H +#define __SOUND_AK4531_CODEC_H + +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.1 + * by Intel Corporation (http://developer.intel.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "info.h" +#include "control.h" + +/* + * ASAHI KASEI - AK4531 codec + * - not really AC'97 codec, but it uses very similar interface as AC'97 + */ + +/* + * AK4531 codec registers + */ + +#define AK4531_LMASTER 0x00 /* master volume left */ +#define AK4531_RMASTER 0x01 /* master volume right */ +#define AK4531_LVOICE 0x02 /* channel volume left */ +#define AK4531_RVOICE 0x03 /* channel volume right */ +#define AK4531_LFM 0x04 /* FM volume left */ +#define AK4531_RFM 0x05 /* FM volume right */ +#define AK4531_LCD 0x06 /* CD volume left */ +#define AK4531_RCD 0x07 /* CD volume right */ +#define AK4531_LLINE 0x08 /* LINE volume left */ +#define AK4531_RLINE 0x09 /* LINE volume right */ +#define AK4531_LAUXA 0x0a /* AUXA volume left */ +#define AK4531_RAUXA 0x0b /* AUXA volume right */ +#define AK4531_MONO1 0x0c /* MONO1 volume left */ +#define AK4531_MONO2 0x0d /* MONO1 volume right */ +#define AK4531_MIC 0x0e /* MIC volume */ +#define AK4531_MONO_OUT 0x0f /* Mono-out volume */ +#define AK4531_OUT_SW1 0x10 /* Output mixer switch 1 */ +#define AK4531_OUT_SW2 0x11 /* Output mixer switch 2 */ +#define AK4531_LIN_SW1 0x12 /* Input left mixer switch 1 */ +#define AK4531_RIN_SW1 0x13 /* Input right mixer switch 1 */ +#define AK4531_LIN_SW2 0x14 /* Input left mixer switch 2 */ +#define AK4531_RIN_SW2 0x15 /* Input right mixer switch 2 */ +#define AK4531_RESET 0x16 /* Reset & power down */ +#define AK4531_CLOCK 0x17 /* Clock select */ +#define AK4531_AD_IN 0x18 /* AD input select */ +#define AK4531_MIC_GAIN 0x19 /* MIC amplified gain */ + +typedef struct _snd_ak4531 ak4531_t; + +struct _snd_ak4531 { + void (*write) (ak4531_t *ak4531, unsigned short reg, unsigned short val); + snd_info_entry_t *proc_entry; + void *private_data; + void (*private_free) (ak4531_t *ak4531); + /* --- */ + unsigned char regs[0x20]; + spinlock_t reg_lock; +}; + +int snd_ak4531_mixer(snd_card_t * card, ak4531_t * _ak4531, ak4531_t ** rak4531); + +#endif /* __SOUND_AK4531_CODEC_H */ diff -Nru a/include/sound/asequencer.h b/include/sound/asequencer.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/asequencer.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,918 @@ +/* + * Main header file for the ALSA sequencer + * Copyright (c) 1998-1999 by Frank van de Pol + * (c) 1998-1999 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SOUND_ASEQUENCER_H +#define __SOUND_ASEQUENCER_H + +#ifndef __KERNEL__ +#include +#include +#endif + +#include + +/** version of the sequencer */ +#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 0) + +/** + * definition of sequencer event types + */ + +/** system messages + * event data type = #sndrv_seq_result_t + */ +#define SNDRV_SEQ_EVENT_SYSTEM 0 +#define SNDRV_SEQ_EVENT_RESULT 1 + +/** note messages (channel specific) + * event data type = #sndrv_seq_ev_note + */ +#define SNDRV_SEQ_EVENT_NOTE 5 +#define SNDRV_SEQ_EVENT_NOTEON 6 +#define SNDRV_SEQ_EVENT_NOTEOFF 7 +#define SNDRV_SEQ_EVENT_KEYPRESS 8 + +/** control messages (channel specific) + * event data type = #sndrv_seq_ev_ctrl + */ +#define SNDRV_SEQ_EVENT_CONTROLLER 10 +#define SNDRV_SEQ_EVENT_PGMCHANGE 11 +#define SNDRV_SEQ_EVENT_CHANPRESS 12 +#define SNDRV_SEQ_EVENT_PITCHBEND 13 /**< from -8192 to 8191 */ +#define SNDRV_SEQ_EVENT_CONTROL14 14 /**< 14 bit controller value */ +#define SNDRV_SEQ_EVENT_NONREGPARAM 15 /**< 14 bit NRPN */ +#define SNDRV_SEQ_EVENT_REGPARAM 16 /**< 14 bit RPN */ + +/** synchronisation messages + * event data type = #sndrv_seq_ev_ctrl + */ +#define SNDRV_SEQ_EVENT_SONGPOS 20 /* Song Position Pointer with LSB and MSB values */ +#define SNDRV_SEQ_EVENT_SONGSEL 21 /* Song Select with song ID number */ +#define SNDRV_SEQ_EVENT_QFRAME 22 /* midi time code quarter frame */ +#define SNDRV_SEQ_EVENT_TIMESIGN 23 /* SMF Time Signature event */ +#define SNDRV_SEQ_EVENT_KEYSIGN 24 /* SMF Key Signature event */ + +/** timer messages + * event data type = sndrv_seq_ev_queue_control_t + */ +#define SNDRV_SEQ_EVENT_START 30 /* midi Real Time Start message */ +#define SNDRV_SEQ_EVENT_CONTINUE 31 /* midi Real Time Continue message */ +#define SNDRV_SEQ_EVENT_STOP 32 /* midi Real Time Stop message */ +#define SNDRV_SEQ_EVENT_SETPOS_TICK 33 /* set tick queue position */ +#define SNDRV_SEQ_EVENT_SETPOS_TIME 34 /* set realtime queue position */ +#define SNDRV_SEQ_EVENT_TEMPO 35 /* (SMF) Tempo event */ +#define SNDRV_SEQ_EVENT_CLOCK 36 /* midi Real Time Clock message */ +#define SNDRV_SEQ_EVENT_TICK 37 /* midi Real Time Tick message */ +#define SNDRV_SEQ_EVENT_QUEUE_SKEW 38 /* skew queue tempo */ + +/** others + * event data type = none + */ +#define SNDRV_SEQ_EVENT_TUNE_REQUEST 40 /* tune request */ +#define SNDRV_SEQ_EVENT_RESET 41 /* reset to power-on state */ +#define SNDRV_SEQ_EVENT_SENSING 42 /* "active sensing" event */ + +/** echo back, kernel private messages + * event data type = any type + */ +#define SNDRV_SEQ_EVENT_ECHO 50 /* echo event */ +#define SNDRV_SEQ_EVENT_OSS 51 /* OSS raw event */ + +/** system status messages (broadcast for subscribers) + * event data type = sndrv_seq_addr_t + */ +#define SNDRV_SEQ_EVENT_CLIENT_START 60 /* new client has connected */ +#define SNDRV_SEQ_EVENT_CLIENT_EXIT 61 /* client has left the system */ +#define SNDRV_SEQ_EVENT_CLIENT_CHANGE 62 /* client status/info has changed */ +#define SNDRV_SEQ_EVENT_PORT_START 63 /* new port was created */ +#define SNDRV_SEQ_EVENT_PORT_EXIT 64 /* port was deleted from system */ +#define SNDRV_SEQ_EVENT_PORT_CHANGE 65 /* port status/info has changed */ + +/** port connection changes + * event data type = sndrv_seq_connect_t + */ +#define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED 66 /* ports connected */ +#define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67 /* ports disconnected */ + +/** synthesizer events + * event data type = sndrv_seq_eve_sample_control_t + */ +#define SNDRV_SEQ_EVENT_SAMPLE 70 /* sample select */ +#define SNDRV_SEQ_EVENT_SAMPLE_CLUSTER 71 /* sample cluster select */ +#define SNDRV_SEQ_EVENT_SAMPLE_START 72 /* voice start */ +#define SNDRV_SEQ_EVENT_SAMPLE_STOP 73 /* voice stop */ +#define SNDRV_SEQ_EVENT_SAMPLE_FREQ 74 /* playback frequency */ +#define SNDRV_SEQ_EVENT_SAMPLE_VOLUME 75 /* volume and balance */ +#define SNDRV_SEQ_EVENT_SAMPLE_LOOP 76 /* sample loop */ +#define SNDRV_SEQ_EVENT_SAMPLE_POSITION 77 /* sample position */ +#define SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1 78 /* private (hardware dependent) event */ + +/** user-defined events with fixed length + * event data type = any + */ +#define SNDRV_SEQ_EVENT_USR0 90 +#define SNDRV_SEQ_EVENT_USR1 91 +#define SNDRV_SEQ_EVENT_USR2 92 +#define SNDRV_SEQ_EVENT_USR3 93 +#define SNDRV_SEQ_EVENT_USR4 94 +#define SNDRV_SEQ_EVENT_USR5 95 +#define SNDRV_SEQ_EVENT_USR6 96 +#define SNDRV_SEQ_EVENT_USR7 97 +#define SNDRV_SEQ_EVENT_USR8 98 +#define SNDRV_SEQ_EVENT_USR9 99 + +/** instrument layer + * variable length data can be passed directly to the driver + */ +#define SNDRV_SEQ_EVENT_INSTR_BEGIN 100 /* begin of instrument management */ +#define SNDRV_SEQ_EVENT_INSTR_END 101 /* end of instrument management */ +#define SNDRV_SEQ_EVENT_INSTR_INFO 102 /* instrument interface info */ +#define SNDRV_SEQ_EVENT_INSTR_INFO_RESULT 103 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_FINFO 104 /* get format info */ +#define SNDRV_SEQ_EVENT_INSTR_FINFO_RESULT 105 /* get format info */ +#define SNDRV_SEQ_EVENT_INSTR_RESET 106 /* reset instrument memory */ +#define SNDRV_SEQ_EVENT_INSTR_STATUS 107 /* instrument interface status */ +#define SNDRV_SEQ_EVENT_INSTR_STATUS_RESULT 108 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_PUT 109 /* put instrument to port */ +#define SNDRV_SEQ_EVENT_INSTR_GET 110 /* get instrument from port */ +#define SNDRV_SEQ_EVENT_INSTR_GET_RESULT 111 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_FREE 112 /* free instrument(s) */ +#define SNDRV_SEQ_EVENT_INSTR_LIST 113 /* instrument list */ +#define SNDRV_SEQ_EVENT_INSTR_LIST_RESULT 114 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_CLUSTER 115 /* cluster parameters */ +#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_GET 116 /* get cluster parameters */ +#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_RESULT 117 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_CHANGE 118 /* instrument change */ +/* 119-129: reserved */ + +/* 130-139: variable length events + * event data type = sndrv_seq_ev_ext + * (SNDRV_SEQ_EVENT_LENGTH_VARIABLE must be set) + */ +#define SNDRV_SEQ_EVENT_SYSEX 130 /* system exclusive data (variable length) */ +#define SNDRV_SEQ_EVENT_BOUNCE 131 /* error event */ +/* 132-134: reserved */ +#define SNDRV_SEQ_EVENT_USR_VAR0 135 +#define SNDRV_SEQ_EVENT_USR_VAR1 136 +#define SNDRV_SEQ_EVENT_USR_VAR2 137 +#define SNDRV_SEQ_EVENT_USR_VAR3 138 +#define SNDRV_SEQ_EVENT_USR_VAR4 139 + +/* 140-149: IPC shared memory events (*NOT SUPPORTED YET*) + * event data type = sndrv_seq_ev_ipcshm + * (SNDRV_SEQ_EVENT_LENGTH_VARIPC must be set) + */ +#define SNDRV_SEQ_EVENT_IPCSHM 140 +/* 141-144: reserved */ +#define SNDRV_SEQ_EVENT_USR_VARIPC0 145 +#define SNDRV_SEQ_EVENT_USR_VARIPC1 146 +#define SNDRV_SEQ_EVENT_USR_VARIPC2 147 +#define SNDRV_SEQ_EVENT_USR_VARIPC3 148 +#define SNDRV_SEQ_EVENT_USR_VARIPC4 149 + +/* 150-151: kernel events with quote - DO NOT use in user clients */ +#define SNDRV_SEQ_EVENT_KERNEL_ERROR 150 +#define SNDRV_SEQ_EVENT_KERNEL_QUOTE 151 + +/* 152-191: reserved */ + +/* 192-254: hardware specific events */ + +/* 255: special event */ +#define SNDRV_SEQ_EVENT_NONE 255 + + +typedef unsigned char sndrv_seq_event_type_t; + +/** event address */ +struct sndrv_seq_addr { + unsigned char client; /**< Client number: 0..255, 255 = broadcast to all clients */ + unsigned char port; /**< Port within client: 0..255, 255 = broadcast to all ports */ +}; + +/** port connection */ +struct sndrv_seq_connect { + struct sndrv_seq_addr sender; + struct sndrv_seq_addr dest; +}; + + +#define SNDRV_SEQ_ADDRESS_UNKNOWN 253 /* unknown source */ +#define SNDRV_SEQ_ADDRESS_SUBSCRIBERS 254 /* send event to all subscribed ports */ +#define SNDRV_SEQ_ADDRESS_BROADCAST 255 /* send event to all queues/clients/ports/channels */ +#define SNDRV_SEQ_QUEUE_DIRECT 253 /* direct dispatch */ + + /* event mode flag - NOTE: only 8 bits available! */ +#define SNDRV_SEQ_TIME_STAMP_TICK (0<<0) /* timestamp in clock ticks */ +#define SNDRV_SEQ_TIME_STAMP_REAL (1<<0) /* timestamp in real time */ +#define SNDRV_SEQ_TIME_STAMP_MASK (1<<0) + +#define SNDRV_SEQ_TIME_MODE_ABS (0<<1) /* absolute timestamp */ +#define SNDRV_SEQ_TIME_MODE_REL (1<<1) /* relative to current time */ +#define SNDRV_SEQ_TIME_MODE_MASK (1<<1) + +#define SNDRV_SEQ_EVENT_LENGTH_FIXED (0<<2) /* fixed event size */ +#define SNDRV_SEQ_EVENT_LENGTH_VARIABLE (1<<2) /* variable event size */ +#define SNDRV_SEQ_EVENT_LENGTH_VARUSR (2<<2) /* variable event size - user memory space */ +#define SNDRV_SEQ_EVENT_LENGTH_VARIPC (3<<2) /* variable event size - IPC */ +#define SNDRV_SEQ_EVENT_LENGTH_MASK (3<<2) + +#define SNDRV_SEQ_PRIORITY_NORMAL (0<<4) /* normal priority */ +#define SNDRV_SEQ_PRIORITY_HIGH (1<<4) /* event should be processed before others */ +#define SNDRV_SEQ_PRIORITY_MASK (1<<4) + + + /* note event */ +struct sndrv_seq_ev_note { + unsigned char channel; + unsigned char note; + unsigned char velocity; + unsigned char off_velocity; /* only for SNDRV_SEQ_EVENT_NOTE */ + unsigned int duration; /* only for SNDRV_SEQ_EVENT_NOTE */ +}; + + /* controller event */ +struct sndrv_seq_ev_ctrl { + unsigned char channel; + unsigned char unused1, unused2, unused3; /* pad */ + unsigned int param; + signed int value; +}; + + /* generic set of bytes (12x8 bit) */ +struct sndrv_seq_ev_raw8 { + unsigned char d[12]; /* 8 bit value */ +}; + + /* generic set of integers (3x32 bit) */ +struct sndrv_seq_ev_raw32 { + unsigned int d[3]; /* 32 bit value */ +}; + + /* external stored data */ +struct sndrv_seq_ev_ext { + size_t len; /* length of data */ + void *ptr; /* pointer to data (note: maybe 64-bit) */ +}; + + /* external stored data - IPC shared memory */ +struct sndrv_seq_ev_ipcshm { + size_t len; /* length of data */ + key_t ipc; /* IPC key */ +}; + +/* Instrument cluster type */ +typedef unsigned int sndrv_seq_instr_cluster_t; + +/* Instrument type */ +struct sndrv_seq_instr { + sndrv_seq_instr_cluster_t cluster; + unsigned int std; /* the upper byte means a private instrument (owner - client #) */ + unsigned short bank; + unsigned short prg; +}; + + /* sample number */ +struct sndrv_seq_ev_sample { + unsigned int std; + unsigned short bank; + unsigned short prg; +}; + + /* sample cluster */ +struct sndrv_seq_ev_cluster { + sndrv_seq_instr_cluster_t cluster; +}; + + /* sample position */ +typedef unsigned int sndrv_seq_position_t; /* playback position (in samples) * 16 */ + + /* sample stop mode */ +enum sndrv_seq_stop_mode { + SAMPLE_STOP_IMMEDIATELY = 0, /* terminate playing immediately */ + SAMPLE_STOP_VENVELOPE = 1, /* finish volume envelope */ + SAMPLE_STOP_LOOP = 2 /* terminate loop and finish wave */ +}; + + /* sample frequency */ +typedef int sndrv_seq_frequency_t; /* playback frequency in HZ * 16 */ + + /* sample volume control; if any value is set to -1 == do not change */ +struct sndrv_seq_ev_volume { + signed short volume; /* range: 0-16383 */ + signed short lr; /* left-right balance; range: 0-16383 */ + signed short fr; /* front-rear balance; range: 0-16383 */ + signed short du; /* down-up balance; range: 0-16383 */ +}; + + /* simple loop redefinition */ +struct sndrv_seq_ev_loop { + unsigned int start; /* loop start (in samples) * 16 */ + unsigned int end; /* loop end (in samples) * 16 */ +}; + +struct sndrv_seq_ev_sample_control { + unsigned char channel; + unsigned char unused1, unused2, unused3; /* pad */ + union { + struct sndrv_seq_ev_sample sample; + struct sndrv_seq_ev_cluster cluster; + sndrv_seq_position_t position; + enum sndrv_seq_stop_mode stop_mode; + sndrv_seq_frequency_t frequency; + struct sndrv_seq_ev_volume volume; + struct sndrv_seq_ev_loop loop; + unsigned char raw8[8]; + } param; +}; + + + +/* INSTR_BEGIN event */ +struct sndrv_seq_ev_instr_begin { + int timeout; /* zero = forever, otherwise timeout in ms */ +}; + +struct sndrv_seq_result { + int event; /* processed event type */ + int result; +}; + + +struct sndrv_seq_real_time { + unsigned int tv_sec; /* seconds */ + unsigned int tv_nsec; /* nanoseconds */ +}; + +typedef unsigned int sndrv_seq_tick_time_t; /* midi ticks */ + +union sndrv_seq_timestamp { + sndrv_seq_tick_time_t tick; + struct sndrv_seq_real_time time; +}; + +struct sndrv_seq_queue_skew { + unsigned int value; + unsigned int base; +}; + + /* queue timer control */ +struct sndrv_seq_ev_queue_control { + unsigned char queue; /* affected queue */ + unsigned char pad[3]; /* reserved */ + union { + signed int value; /* affected value (e.g. tempo) */ + union sndrv_seq_timestamp time; /* time */ + unsigned int position; /* sync position */ + struct sndrv_seq_queue_skew skew; + unsigned int d32[2]; + unsigned char d8[8]; + } param; +}; + + /* quoted event - inside the kernel only */ +struct sndrv_seq_ev_quote { + struct sndrv_seq_addr origin; /* original sender */ + unsigned short value; /* optional data */ + struct sndrv_seq_event *event; /* quoted event */ +}; + + + /* sequencer event */ +struct sndrv_seq_event { + sndrv_seq_event_type_t type; /* event type */ + unsigned char flags; /* event flags */ + char tag; + + unsigned char queue; /* schedule queue */ + union sndrv_seq_timestamp time; /* schedule time */ + + + struct sndrv_seq_addr source; /* source address */ + struct sndrv_seq_addr dest; /* destination address */ + + union { /* event data... */ + struct sndrv_seq_ev_note note; + struct sndrv_seq_ev_ctrl control; + struct sndrv_seq_ev_raw8 raw8; + struct sndrv_seq_ev_raw32 raw32; + struct sndrv_seq_ev_ext ext; + struct sndrv_seq_ev_ipcshm ipcshm; + struct sndrv_seq_ev_queue_control queue; + union sndrv_seq_timestamp time; + struct sndrv_seq_addr addr; + struct sndrv_seq_connect connect; + struct sndrv_seq_result result; + struct sndrv_seq_ev_instr_begin instr_begin; + struct sndrv_seq_ev_sample_control sample; + struct sndrv_seq_ev_quote quote; + } data; +}; + + +/* + * bounce event - stored as variable size data + */ +struct sndrv_seq_event_bounce { + int err; + struct sndrv_seq_event event; + /* external data follows here. */ +}; + +#define sndrv_seq_event_bounce_ext_data(ev) ((void*)((char *)(ev)->data.ext.ptr + sizeof(sndrv_seq_event_bounce_t))) + +/* + * type check macros + */ +/* result events: 0-4 */ +#define sndrv_seq_ev_is_result_type(ev) ((ev)->type < 5) +/* channel specific events: 5-19 */ +#define sndrv_seq_ev_is_channel_type(ev) ((ev)->type >= 5 && (ev)->type < 20) +/* note events: 5-9 */ +#define sndrv_seq_ev_is_note_type(ev) ((ev)->type >= 5 && (ev)->type < 10) +/* control events: 10-19 */ +#define sndrv_seq_ev_is_control_type(ev) ((ev)->type >= 10 && (ev)->type < 20) +/* queue control events: 30-39 */ +#define sndrv_seq_ev_is_queue_type(ev) ((ev)->type >= 30 && (ev)->type < 40) +/* system status messages */ +#define sndrv_seq_ev_is_message_type(ev) ((ev)->type >= 60 && (ev)->type < 69) +/* sample messages */ +#define sndrv_seq_ev_is_sample_type(ev) ((ev)->type >= 70 && (ev)->type < 79) +/* user-defined messages */ +#define sndrv_seq_ev_is_user_type(ev) ((ev)->type >= 90 && (ev)->type < 99) +/* fixed length events: 0-99 */ +#define sndrv_seq_ev_is_fixed_type(ev) ((ev)->type < 100) +/* instrument layer events: 100-129 */ +#define sndrv_seq_ev_is_instr_type(ev) ((ev)->type >= 100 && (ev)->type < 130) +/* variable length events: 130-139 */ +#define sndrv_seq_ev_is_variable_type(ev) ((ev)->type >= 130 && (ev)->type < 140) +/* ipc shmem events: 140-149 */ +#define sndrv_seq_ev_is_varipc_type(ev) ((ev)->type >= 140 && (ev)->type < 150) +/* reserved for kernel */ +#define sndrv_seq_ev_is_reserved(ev) ((ev)->type >= 150) + +/* direct dispatched events */ +#define sndrv_seq_ev_is_direct(ev) ((ev)->queue == SNDRV_SEQ_QUEUE_DIRECT) + +/* + * macros to check event flags + */ +/* prior events */ +#define sndrv_seq_ev_is_prior(ev) (((ev)->flags & SNDRV_SEQ_PRIORITY_MASK) == SNDRV_SEQ_PRIORITY_HIGH) + +/* event length type */ +#define sndrv_seq_ev_length_type(ev) ((ev)->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) +#define sndrv_seq_ev_is_fixed(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_FIXED) +#define sndrv_seq_ev_is_variable(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) +#define sndrv_seq_ev_is_varusr(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARUSR) +#define sndrv_seq_ev_is_varipc(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARIPC) + +/* time-stamp type */ +#define sndrv_seq_ev_timestamp_type(ev) ((ev)->flags & SNDRV_SEQ_TIME_STAMP_MASK) +#define sndrv_seq_ev_is_tick(ev) (sndrv_seq_ev_timestamp_type(ev) == SNDRV_SEQ_TIME_STAMP_TICK) +#define sndrv_seq_ev_is_real(ev) (sndrv_seq_ev_timestamp_type(ev) == SNDRV_SEQ_TIME_STAMP_REAL) + +/* time-mode type */ +#define sndrv_seq_ev_timemode_type(ev) ((ev)->flags & SNDRV_SEQ_TIME_MODE_MASK) +#define sndrv_seq_ev_is_abstime(ev) (sndrv_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_ABS) +#define sndrv_seq_ev_is_reltime(ev) (sndrv_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_REL) + +/* queue sync port */ +#define sndrv_seq_queue_sync_port(q) ((q) + 16) + + /* system information */ +struct sndrv_seq_system_info { + int queues; /* maximum queues count */ + int clients; /* maximum clients count */ + int ports; /* maximum ports per client */ + int channels; /* maximum channels per port */ + int cur_clients; /* current clients */ + int cur_queues; /* current queues */ + char reserved[24]; +}; + + + /* known client numbers */ +#define SNDRV_SEQ_CLIENT_SYSTEM 0 +#define SNDRV_SEQ_CLIENT_DUMMY 62 /* dummy ports */ +#define SNDRV_SEQ_CLIENT_OSS 63 /* oss sequencer emulator */ + + + /* client types */ +enum sndrv_seq_client_type { + NO_CLIENT = 0, + USER_CLIENT = 1, + KERNEL_CLIENT = 2 +}; + + /* event filter flags */ +#define SNDRV_SEQ_FILTER_BROADCAST (1<<0) /* accept broadcast messages */ +#define SNDRV_SEQ_FILTER_MULTICAST (1<<1) /* accept multicast messages */ +#define SNDRV_SEQ_FILTER_BOUNCE (1<<2) /* accept bounce event in error */ +#define SNDRV_SEQ_FILTER_USE_EVENT (1<<31) /* use event filter */ + +struct sndrv_seq_client_info { + int client; /* client number to inquire */ + enum sndrv_seq_client_type type; /* client type */ + char name[64]; /* client name */ + unsigned int filter; /* filter flags */ + unsigned char multicast_filter[8]; /* multicast filter bitmap */ + unsigned char event_filter[32]; /* event filter bitmap */ + int num_ports; /* RO: number of ports */ + int event_lost; /* number of lost events */ + char reserved[64]; /* for future use */ +}; + + +/* client pool size */ +struct sndrv_seq_client_pool { + int client; /* client number to inquire */ + int output_pool; /* outgoing (write) pool size */ + int input_pool; /* incoming (read) pool size */ + int output_room; /* minimum free pool size for select/blocking mode */ + int output_free; /* unused size */ + int input_free; /* unused size */ + char reserved[64]; +}; + + +/* Remove events by specified criteria */ + +#define SNDRV_SEQ_REMOVE_INPUT (1<<0) /* Flush input queues */ +#define SNDRV_SEQ_REMOVE_OUTPUT (1<<1) /* Flush output queues */ +#define SNDRV_SEQ_REMOVE_DEST (1<<2) /* Restrict by destination q:client:port */ +#define SNDRV_SEQ_REMOVE_DEST_CHANNEL (1<<3) /* Restrict by channel */ +#define SNDRV_SEQ_REMOVE_TIME_BEFORE (1<<4) /* Restrict to before time */ +#define SNDRV_SEQ_REMOVE_TIME_AFTER (1<<5) /* Restrict to time or after */ +#define SNDRV_SEQ_REMOVE_TIME_TICK (1<<6) /* Time is in ticks */ +#define SNDRV_SEQ_REMOVE_EVENT_TYPE (1<<7) /* Restrict to event type */ +#define SNDRV_SEQ_REMOVE_IGNORE_OFF (1<<8) /* Do not flush off events */ +#define SNDRV_SEQ_REMOVE_TAG_MATCH (1<<9) /* Restrict to events with given tag */ + +struct sndrv_seq_remove_events { + unsigned int remove_mode; /* Flags that determine what gets removed */ + + union sndrv_seq_timestamp time; + + unsigned char queue; /* Queue for REMOVE_DEST */ + struct sndrv_seq_addr dest; /* Address for REMOVE_DEST */ + unsigned char channel; /* Channel for REMOVE_DEST */ + + int type; /* For REMOVE_EVENT_TYPE */ + char tag; /* Tag for REMOVE_TAG */ + + int reserved[10]; /* To allow for future binary compatibility */ + +}; + + + /* known port numbers */ +#define SNDRV_SEQ_PORT_SYSTEM_TIMER 0 +#define SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE 1 + + /* port capabilities (32 bits) */ +#define SNDRV_SEQ_PORT_CAP_READ (1<<0) /* readable from this port */ +#define SNDRV_SEQ_PORT_CAP_WRITE (1<<1) /* writable to this port */ + +#define SNDRV_SEQ_PORT_CAP_SYNC_READ (1<<2) +#define SNDRV_SEQ_PORT_CAP_SYNC_WRITE (1<<3) + +#define SNDRV_SEQ_PORT_CAP_DUPLEX (1<<4) + +#define SNDRV_SEQ_PORT_CAP_SUBS_READ (1<<5) /* allow read subscription */ +#define SNDRV_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /* allow write subscription */ +#define SNDRV_SEQ_PORT_CAP_NO_EXPORT (1<<7) /* routing not allowed */ + + /* port type */ +#define SNDRV_SEQ_PORT_TYPE_SPECIFIC (1<<0) /* hardware specific */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC (1<<1) /* generic MIDI device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GM (1<<2) /* General MIDI compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GS (1<<3) /* GS compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_XG (1<<4) /* XG compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) /* MT-32 compatible device */ + +/* other standards...*/ +#define SNDRV_SEQ_PORT_TYPE_SYNTH (1<<10) /* Synth device */ +#define SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE (1<<11) /* Sampling device (support sample download) */ +#define SNDRV_SEQ_PORT_TYPE_SAMPLE (1<<12) /* Sampling device (sample can be downloaded at any time) */ +/*...*/ +#define SNDRV_SEQ_PORT_TYPE_APPLICATION (1<<20) /* application (sequencer/editor) */ + +/* misc. conditioning flags */ +#define SNDRV_SEQ_PORT_FLG_GIVEN_PORT (1<<0) + +struct sndrv_seq_port_info { + struct sndrv_seq_addr addr; /* client/port numbers */ + char name[64]; /* port name */ + + unsigned int capability; /* port capability bits */ + unsigned int type; /* port type bits */ + int midi_channels; /* channels per MIDI port */ + int midi_voices; /* voices per MIDI port */ + int synth_voices; /* voices per SYNTH port */ + + int read_use; /* R/O: subscribers for output (from this port) */ + int write_use; /* R/O: subscribers for input (to this port) */ + + void *kernel; /* reserved for kernel use (must be NULL) */ + + unsigned int flags; /* misc. conditioning */ + char reserved[60]; /* for future use */ +}; + + +/* queue flags */ +#define SNDRV_SEQ_QUEUE_FLG_SYNC (1<<0) /* sync enabled */ + +/* queue information */ +struct sndrv_seq_queue_info { + int queue; /* queue id */ + + /* + * security settings, only owner of this queue can start/stop timer + * etc. if the queue is locked for other clients + */ + int owner; /* client id for owner of the queue */ + int locked:1; /* timing queue locked for other queues */ + char name[64]; /* name of this queue */ + unsigned int flags; /* flags */ + char reserved[60]; /* for future use */ + +}; + +/* queue info/status */ +struct sndrv_seq_queue_status { + int queue; /* queue id */ + int events; /* read-only - queue size */ + sndrv_seq_tick_time_t tick; /* current tick */ + struct sndrv_seq_real_time time; /* current time */ + int running; /* running state of queue */ + int flags; /* various flags */ + char reserved[64]; /* for the future */ +}; + + +/* queue tempo */ +struct sndrv_seq_queue_tempo { + int queue; /* sequencer queue */ + unsigned int tempo; /* current tempo, us/tick */ + int ppq; /* time resolution, ticks/quarter */ + unsigned int skew_value; /* queue skew */ + unsigned int skew_base; /* queue skew base */ + char reserved[24]; /* for the future */ +}; + + +/* sequencer timer sources */ +#define SNDRV_SEQ_TIMER_ALSA 0 /* ALSA timer */ +#define SNDRV_SEQ_TIMER_MIDI_CLOCK 1 /* Midi Clock (CLOCK event) */ +#define SNDRV_SEQ_TIMER_MIDI_TICK 2 /* Midi Timer Tick (TICK event) */ + +/* queue timer info */ +struct sndrv_seq_queue_timer { + int queue; /* sequencer queue */ + int type; /* source timer type */ + union { + struct { + struct sndrv_timer_id id; /* ALSA's timer ID */ + unsigned int resolution; /* resolution in Hz */ + } alsa; + } u; + char reserved[64]; /* for the future use */ +}; + + +struct sndrv_seq_queue_client { + int queue; /* sequencer queue */ + int client; /* sequencer client */ + int used; /* queue is used with this client + (must be set for accepting events) */ + /* per client watermarks */ + char reserved[64]; /* for future use */ +}; + + +#define SNDRV_SEQ_PORT_SUBS_EXCLUSIVE (1<<0) /* exclusive connection */ +#define SNDRV_SEQ_PORT_SUBS_TIMESTAMP (1<<1) +#define SNDRV_SEQ_PORT_SUBS_TIME_REAL (1<<2) + +struct sndrv_seq_port_subscribe { + struct sndrv_seq_addr sender; /* sender address */ + struct sndrv_seq_addr dest; /* destination address */ + unsigned int voices; /* number of voices to be allocated (0 = don't care) */ + unsigned int flags; /* modes */ + unsigned char queue; /* input time-stamp queue (optional) */ + unsigned char pad[3]; /* reserved */ + char reserved[64]; +}; + +/* type of query subscription */ +#define SNDRV_SEQ_QUERY_SUBS_READ 0 +#define SNDRV_SEQ_QUERY_SUBS_WRITE 1 + +struct sndrv_seq_query_subs { + struct sndrv_seq_addr root; /* client/port id to be searched */ + int type; /* READ or WRITE */ + int index; /* 0..N-1 */ + int num_subs; /* R/O: number of subscriptions on this port */ + struct sndrv_seq_addr addr; /* R/O: result */ + unsigned char queue; /* R/O: result */ + unsigned int flags; /* R/O: result */ + char reserved[64]; /* for future use */ +}; + + +/* + * Instrument abstraction layer + * - based on events + */ + +/* instrument types */ +#define SNDRV_SEQ_INSTR_ATYPE_DATA 0 /* instrument data */ +#define SNDRV_SEQ_INSTR_ATYPE_ALIAS 1 /* instrument alias */ + +/* instrument ASCII identifiers */ +#define SNDRV_SEQ_INSTR_ID_DLS1 "DLS1" +#define SNDRV_SEQ_INSTR_ID_DLS2 "DLS2" +#define SNDRV_SEQ_INSTR_ID_SIMPLE "Simple Wave" +#define SNDRV_SEQ_INSTR_ID_SOUNDFONT "SoundFont" +#define SNDRV_SEQ_INSTR_ID_GUS_PATCH "GUS Patch" +#define SNDRV_SEQ_INSTR_ID_INTERWAVE "InterWave FFFF" +#define SNDRV_SEQ_INSTR_ID_OPL2_3 "OPL2/3 FM" +#define SNDRV_SEQ_INSTR_ID_OPL4 "OPL4" + +/* instrument types */ +#define SNDRV_SEQ_INSTR_TYPE0_DLS1 (1<<0) /* MIDI DLS v1 */ +#define SNDRV_SEQ_INSTR_TYPE0_DLS2 (1<<1) /* MIDI DLS v2 */ +#define SNDRV_SEQ_INSTR_TYPE1_SIMPLE (1<<0) /* Simple Wave */ +#define SNDRV_SEQ_INSTR_TYPE1_SOUNDFONT (1<<1) /* EMU SoundFont */ +#define SNDRV_SEQ_INSTR_TYPE1_GUS_PATCH (1<<2) /* Gravis UltraSound Patch */ +#define SNDRV_SEQ_INSTR_TYPE1_INTERWAVE (1<<3) /* InterWave FFFF */ +#define SNDRV_SEQ_INSTR_TYPE2_OPL2_3 (1<<0) /* Yamaha OPL2/3 FM */ +#define SNDRV_SEQ_INSTR_TYPE2_OPL4 (1<<1) /* Yamaha OPL4 */ + +/* put commands */ +#define SNDRV_SEQ_INSTR_PUT_CMD_CREATE 0 +#define SNDRV_SEQ_INSTR_PUT_CMD_REPLACE 1 +#define SNDRV_SEQ_INSTR_PUT_CMD_MODIFY 2 +#define SNDRV_SEQ_INSTR_PUT_CMD_ADD 3 +#define SNDRV_SEQ_INSTR_PUT_CMD_REMOVE 4 + +/* get commands */ +#define SNDRV_SEQ_INSTR_GET_CMD_FULL 0 +#define SNDRV_SEQ_INSTR_GET_CMD_PARTIAL 1 + +/* query flags */ +#define SNDRV_SEQ_INSTR_QUERY_FOLLOW_ALIAS (1<<0) + +/* free commands */ +#define SNDRV_SEQ_INSTR_FREE_CMD_ALL 0 +#define SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE 1 +#define SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER 2 +#define SNDRV_SEQ_INSTR_FREE_CMD_SINGLE 3 + +/* size of ROM/RAM */ +typedef unsigned int sndrv_seq_instr_size_t; + +/* INSTR_INFO */ + +struct sndrv_seq_instr_info { + int result; /* operation result */ + unsigned int formats[8]; /* bitmap of supported formats */ + int ram_count; /* count of RAM banks */ + sndrv_seq_instr_size_t ram_sizes[16]; /* size of RAM banks */ + int rom_count; /* count of ROM banks */ + sndrv_seq_instr_size_t rom_sizes[8]; /* size of ROM banks */ + char reserved[128]; +}; + +/* INSTR_STATUS */ + +struct sndrv_seq_instr_status { + int result; /* operation result */ + sndrv_seq_instr_size_t free_ram[16]; /* free RAM in banks */ + int instrument_count; /* count of downloaded instruments */ + char reserved[128]; +}; + +/* INSTR_FORMAT_INFO */ + +struct sndrv_seq_instr_format_info { + char format[16]; /* format identifier - SNDRV_SEQ_INSTR_ID_* */ + unsigned int len; /* max data length (without this structure) */ +}; + +struct sndrv_seq_instr_format_info_result { + int result; /* operation result */ + char format[16]; /* format identifier */ + unsigned int len; /* filled data length (without this structure) */ +}; + +/* instrument data */ +struct sndrv_seq_instr_data { + char name[32]; /* instrument name */ + char reserved[16]; /* for the future use */ + int type; /* instrument type */ + union { + char format[16]; /* format identifier */ + struct sndrv_seq_instr alias; + } data; +}; + +/* INSTR_PUT/GET, data are stored in one block (extended or IPC), header + data */ + +struct sndrv_seq_instr_header { + union { + struct sndrv_seq_instr instr; + sndrv_seq_instr_cluster_t cluster; + } id; /* instrument identifier */ + unsigned int cmd; /* get/put/free command */ + unsigned int flags; /* query flags (only for get) */ + unsigned int len; /* real instrument data length (without header) */ + int result; /* operation result */ + char reserved[16]; /* for the future */ + struct sndrv_seq_instr_data data; /* instrument data (for put/get result) */ +}; + +/* INSTR_CLUSTER_SET */ + +struct sndrv_seq_instr_cluster_set { + sndrv_seq_instr_cluster_t cluster; /* cluster identifier */ + char name[32]; /* cluster name */ + int priority; /* cluster priority */ + char reserved[64]; /* for the future use */ +}; + +/* INSTR_CLUSTER_GET */ + +struct sndrv_seq_instr_cluster_get { + sndrv_seq_instr_cluster_t cluster; /* cluster identifier */ + char name[32]; /* cluster name */ + int priority; /* cluster priority */ + char reserved[64]; /* for the future use */ +}; + +/* + * IOCTL commands + */ + +#define SNDRV_SEQ_IOCTL_PVERSION _IOR ('S', 0x00, int) +#define SNDRV_SEQ_IOCTL_CLIENT_ID _IOR ('S', 0x01, int) +#define SNDRV_SEQ_IOCTL_SYSTEM_INFO _IOWR('S', 0x02, struct sndrv_seq_system_info) + +#define SNDRV_SEQ_IOCTL_GET_CLIENT_INFO _IOWR('S', 0x10, struct sndrv_seq_client_info) +#define SNDRV_SEQ_IOCTL_SET_CLIENT_INFO _IOW ('S', 0x11, struct sndrv_seq_client_info) + +#define SNDRV_SEQ_IOCTL_CREATE_PORT _IOWR('S', 0x20, struct sndrv_seq_port_info) +#define SNDRV_SEQ_IOCTL_DELETE_PORT _IOW ('S', 0x21, struct sndrv_seq_port_info) +#define SNDRV_SEQ_IOCTL_GET_PORT_INFO _IOWR('S', 0x22, struct sndrv_seq_port_info) +#define SNDRV_SEQ_IOCTL_SET_PORT_INFO _IOW ('S', 0x23, struct sndrv_seq_port_info) + +#define SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT _IOW ('S', 0x30, struct sndrv_seq_port_subscribe) +#define SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT _IOW ('S', 0x31, struct sndrv_seq_port_subscribe) + +#define SNDRV_SEQ_IOCTL_CREATE_QUEUE _IOWR('S', 0x32, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_DELETE_QUEUE _IOW ('S', 0x33, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_INFO _IOWR('S', 0x34, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_INFO _IOWR('S', 0x35, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE _IOWR('S', 0x36, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct sndrv_seq_queue_status) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO _IOWR('S', 0x41, struct sndrv_seq_queue_tempo) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO _IOW ('S', 0x42, struct sndrv_seq_queue_tempo) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER _IOWR('S', 0x43, struct sndrv_seq_queue_owner) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_OWNER _IOW ('S', 0x44, struct sndrv_seq_queue_owner) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER _IOWR('S', 0x45, struct sndrv_seq_queue_timer) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER _IOW ('S', 0x46, struct sndrv_seq_queue_timer) +/* XXX +#define SNDRV_SEQ_IOCTL_GET_QUEUE_SYNC _IOWR('S', 0x53, struct sndrv_seq_queue_sync) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_SYNC _IOW ('S', 0x54, struct sndrv_seq_queue_sync) +*/ +#define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT _IOWR('S', 0x49, struct sndrv_seq_queue_client) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT _IOW ('S', 0x4a, struct sndrv_seq_queue_client) +#define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL _IOWR('S', 0x4b, struct sndrv_seq_client_pool) +#define SNDRV_SEQ_IOCTL_SET_CLIENT_POOL _IOW ('S', 0x4c, struct sndrv_seq_client_pool) +#define SNDRV_SEQ_IOCTL_REMOVE_EVENTS _IOW ('S', 0x4e, struct sndrv_seq_remove_events) +#define SNDRV_SEQ_IOCTL_QUERY_SUBS _IOWR('S', 0x4f, struct sndrv_seq_query_subs) +#define SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION _IOWR('S', 0x50, struct sndrv_seq_port_subscribe) +#define SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT _IOWR('S', 0x51, struct sndrv_seq_client_info) +#define SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT _IOWR('S', 0x52, struct sndrv_seq_port_info) + +#endif /* __SOUND_ASEQUENCER_H */ diff -Nru a/include/sound/asound.h b/include/sound/asound.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/asound.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,774 @@ +/* + * Advanced Linux Sound Architecture - ALSA - Driver + * Copyright (c) 1994-2000 by Jaroslav Kysela , + * Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_ASOUND_H +#define __SOUND_ASOUND_H + +#if defined(LINUX) || defined(__LINUX__) || defined(__linux__) + +#include + +#ifdef __KERNEL__ + +#include +#include + +#if __LITTLE_ENDIAN == 1234 +#define SNDRV_LITTLE_ENDIAN +#elif __BIG_ENDIAN == 4321 +#define SNDRV_BIG_ENDIAN +#else +#error "Unsupported endian..." +#endif + +#else /* !__KERNEL__ */ + +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define SNDRV_LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +#define SNDRV_BIG_ENDIAN +#else +#error "Unsupported endian..." +#endif + +#endif /* __KERNEL **/ + +#endif /* LINUX */ + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * protocol version + */ + +#define SNDRV_PROTOCOL_VERSION(major, minor, subminor) (((major)<<16)|((minor)<<8)|(subminor)) +#define SNDRV_PROTOCOL_MAJOR(version) (((version)>>16)&0xffff) +#define SNDRV_PROTOCOL_MINOR(version) (((version)>>8)&0xff) +#define SNDRV_PROTOCOL_MICRO(version) ((version)&0xff) +#define SNDRV_PROTOCOL_INCOMPATIBLE(kversion, uversion) \ + (SNDRV_PROTOCOL_MAJOR(kversion) != SNDRV_PROTOCOL_MAJOR(uversion) || \ + (SNDRV_PROTOCOL_MAJOR(kversion) == SNDRV_PROTOCOL_MAJOR(uversion) && \ + SNDRV_PROTOCOL_MINOR(kversion) != SNDRV_PROTOCOL_MINOR(uversion))) + +/**************************************************************************** + * * + * Digital audio interface * + * * + ****************************************************************************/ + +struct sndrv_aes_iec958 { + unsigned char status[24]; /* AES/IEC958 channel status bits */ + unsigned char subcode[147]; /* AES/IEC958 subcode bits */ + unsigned char pad; /* nothing */ + unsigned char dig_subframe[4]; /* AES/IEC958 subframe bits */ +}; + +/**************************************************************************** + * * + * Section for driver hardware dependent interface - /dev/snd/hw? * + * * + ****************************************************************************/ + +#define SNDRV_HWDEP_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 0) + +enum sndrv_hwdep_iface { + SNDRV_HWDEP_IFACE_OPL2 = 0, + SNDRV_HWDEP_IFACE_OPL3, + SNDRV_HWDEP_IFACE_OPL4, + SNDRV_HWDEP_IFACE_SB16CSP, /* Creative Signal Processor */ + SNDRV_HWDEP_IFACE_EMU10K1, /* FX8010 processor in EMU10K1 chip */ + SNDRV_HWDEP_IFACE_YSS225, /* Yamaha FX processor */ + SNDRV_HWDEP_IFACE_ICS2115, /* Wavetable synth */ + + /* Don't forget to change the following: */ + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_ICS2115, +}; + +struct sndrv_hwdep_info { + unsigned int device; /* WR: device number */ + int card; /* R: card number */ + unsigned char id[64]; /* ID (user selectable) */ + unsigned char name[80]; /* hwdep name */ + enum sndrv_hwdep_iface iface; /* hwdep interface */ + unsigned char reserved[64]; /* reserved for future */ +}; + +enum { + SNDRV_HWDEP_IOCTL_PVERSION = _IOR ('H', 0x00, int), + SNDRV_HWDEP_IOCTL_INFO = _IOR ('H', 0x01, struct sndrv_hwdep_info), +}; + +/***************************************************************************** + * * + * Digital Audio (PCM) interface - /dev/snd/pcm?? * + * * + *****************************************************************************/ + +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) + +typedef unsigned long sndrv_pcm_uframes_t; +typedef long sndrv_pcm_sframes_t; + +enum sndrv_pcm_class { + SNDRV_PCM_CLASS_GENERIC = 0, /* standard mono or stereo device */ + SNDRV_PCM_CLASS_MULTI, /* multichannel device */ + SNDRV_PCM_CLASS_MODEM, /* software modem class */ + SNDRV_PCM_CLASS_DIGITIZER, /* digitizer class */ + /* Don't forget to change the following: */ + SNDRV_PCM_CLASS_LAST = SNDRV_PCM_CLASS_DIGITIZER, +}; + +enum sndrv_pcm_subclass { + SNDRV_PCM_SUBCLASS_GENERIC_MIX = 0, /* mono or stereo subdevices are mixed together */ + SNDRV_PCM_SUBCLASS_MULTI_MIX, /* multichannel subdevices are mixed together */ + /* Don't forget to change the following: */ + SNDRV_PCM_SUBCLASS_LAST = SNDRV_PCM_SUBCLASS_MULTI_MIX, +}; + +enum sndrv_pcm_stream { + SNDRV_PCM_STREAM_PLAYBACK = 0, + SNDRV_PCM_STREAM_CAPTURE, + SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE, +}; + +enum sndrv_pcm_access { + SNDRV_PCM_ACCESS_MMAP_INTERLEAVED = 0, /* interleaved mmap */ + SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED, /* noninterleaved mmap */ + SNDRV_PCM_ACCESS_MMAP_COMPLEX, /* complex mmap */ + SNDRV_PCM_ACCESS_RW_INTERLEAVED, /* readi/writei */ + SNDRV_PCM_ACCESS_RW_NONINTERLEAVED, /* readn/writen */ + SNDRV_PCM_ACCESS_LAST = SNDRV_PCM_ACCESS_RW_NONINTERLEAVED, +}; + +enum sndrv_pcm_format { + SNDRV_PCM_FORMAT_S8 = 0, + SNDRV_PCM_FORMAT_U8, + SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_LE, /* low three bytes */ + SNDRV_PCM_FORMAT_S24_BE, /* low three bytes */ + SNDRV_PCM_FORMAT_U24_LE, /* low three bytes */ + SNDRV_PCM_FORMAT_U24_BE, /* low three bytes */ + SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_FLOAT_LE, /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT_BE, /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT64_LE, /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT64_BE, /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE, /* IEC-958 subframe, Little Endian */ + SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, /* IEC-958 subframe, Big Endian */ + SNDRV_PCM_FORMAT_MU_LAW, + SNDRV_PCM_FORMAT_A_LAW, + SNDRV_PCM_FORMAT_IMA_ADPCM, + SNDRV_PCM_FORMAT_MPEG, + SNDRV_PCM_FORMAT_GSM, + SNDRV_PCM_FORMAT_SPECIAL = 31, + SNDRV_PCM_FORMAT_LAST = 31, + +#ifdef SNDRV_LITTLE_ENDIAN + SNDRV_PCM_FORMAT_S16 = SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_U16 = SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_S24 = SNDRV_PCM_FORMAT_S24_LE, + SNDRV_PCM_FORMAT_U24 = SNDRV_PCM_FORMAT_U24_LE, + SNDRV_PCM_FORMAT_S32 = SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_U32 = SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_FLOAT = SNDRV_PCM_FORMAT_FLOAT_LE, + SNDRV_PCM_FORMAT_FLOAT64 = SNDRV_PCM_FORMAT_FLOAT64_LE, + SNDRV_PCM_FORMAT_IEC958_SUBFRAME = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE, +#endif +#ifdef SNDRV_BIG_ENDIAN + SNDRV_PCM_FORMAT_S16 = SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16 = SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24 = SNDRV_PCM_FORMAT_S24_BE, + SNDRV_PCM_FORMAT_U24 = SNDRV_PCM_FORMAT_U24_BE, + SNDRV_PCM_FORMAT_S32 = SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32 = SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_FLOAT = SNDRV_PCM_FORMAT_FLOAT_BE, + SNDRV_PCM_FORMAT_FLOAT64 = SNDRV_PCM_FORMAT_FLOAT64_BE, + SNDRV_PCM_FORMAT_IEC958_SUBFRAME = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, +#endif +}; + +enum sndrv_pcm_subformat { + SNDRV_PCM_SUBFORMAT_STD = 0, + SNDRV_PCM_SUBFORMAT_LAST = SNDRV_PCM_SUBFORMAT_STD, +}; + +#define SNDRV_PCM_INFO_MMAP 0x00000001 /* hardware supports mmap */ +#define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ +#define SNDRV_PCM_INFO_DOUBLE 0x00000004 /* Double buffering needed for PCM start/stop */ +#define SNDRV_PCM_INFO_BATCH 0x00000010 /* double buffering */ +#define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 /* channels are interleaved */ +#define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 /* channels are not interleaved */ +#define SNDRV_PCM_INFO_COMPLEX 0x00000400 /* complex frame organization (mmap only) */ +#define SNDRV_PCM_INFO_BLOCK_TRANSFER 0x00010000 /* hardware transfer block of samples */ +#define SNDRV_PCM_INFO_OVERRANGE 0x00020000 /* hardware supports ADC (capture) overrange detection */ +#define SNDRV_PCM_INFO_RESUME 0x00040000 /* hardware supports stream resume after suspend */ +#define SNDRV_PCM_INFO_PAUSE 0x00080000 /* pause ioctl is supported */ +#define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 /* only half duplex */ +#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ +#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ + +enum sndrv_pcm_state { + SNDRV_PCM_STATE_OPEN = 0, /* stream is open */ + SNDRV_PCM_STATE_SETUP, /* stream has a setup */ + SNDRV_PCM_STATE_PREPARED, /* stream is ready to start */ + SNDRV_PCM_STATE_RUNNING, /* stream is running */ + SNDRV_PCM_STATE_XRUN, /* stream reached an xrun */ + SNDRV_PCM_STATE_DRAINING, /* stream is draining */ + SNDRV_PCM_STATE_PAUSED, /* stream is paused */ + SNDRV_PCM_STATE_SUSPENDED, /* hardware is suspended */ + SNDRV_PCM_STATE_LAST = SNDRV_PCM_STATE_SUSPENDED, +}; + +enum { + SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000, + SNDRV_PCM_MMAP_OFFSET_STATUS = 0x80000000, + SNDRV_PCM_MMAP_OFFSET_CONTROL = 0x81000000, +}; + +union sndrv_pcm_sync_id { + unsigned char id[16]; + unsigned short id16[8]; + unsigned int id32[4]; +}; + +struct sndrv_pcm_info { + unsigned int device; /* RO/WR (control): device number */ + unsigned int subdevice; /* RO/WR (control): subdevice number */ + enum sndrv_pcm_stream stream; /* RO/WR (control): stream number */ + int card; /* R: card number */ + unsigned char id[64]; /* ID (user selectable) */ + unsigned char name[80]; /* name of this device */ + unsigned char subname[32]; /* subdevice name */ + enum sndrv_pcm_class dev_class; /* SNDRV_PCM_CLASS_* */ + enum sndrv_pcm_subclass dev_subclass; /* SNDRV_PCM_SUBCLASS_* */ + unsigned int subdevices_count; + unsigned int subdevices_avail; + union sndrv_pcm_sync_id sync; /* hardware synchronization ID */ + unsigned char reserved[64]; /* reserved for future... */ +}; + +enum sndrv_pcm_hw_param { + SNDRV_PCM_HW_PARAM_ACCESS = 0, /* Access type */ + SNDRV_PCM_HW_PARAM_FIRST_MASK = SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, /* Format */ + SNDRV_PCM_HW_PARAM_SUBFORMAT, /* Subformat */ + SNDRV_PCM_HW_PARAM_LAST_MASK = SNDRV_PCM_HW_PARAM_SUBFORMAT, + + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, /* Bits per sample */ + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL = SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + SNDRV_PCM_HW_PARAM_FRAME_BITS, /* Bits per frame */ + SNDRV_PCM_HW_PARAM_CHANNELS, /* Channels */ + SNDRV_PCM_HW_PARAM_RATE, /* Approx rate */ + SNDRV_PCM_HW_PARAM_PERIOD_TIME, /* Approx distance between interrupts + in us */ + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, /* Approx frames between interrupts */ + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, /* Approx bytes between interrupts */ + SNDRV_PCM_HW_PARAM_PERIODS, /* Approx interrupts per buffer */ + SNDRV_PCM_HW_PARAM_BUFFER_TIME, /* Approx duration of buffer in us */ + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, /* Size of buffer in frames */ + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, /* Size of buffer in bytes */ + SNDRV_PCM_HW_PARAM_TICK_TIME, /* Approx tick duration in us */ + SNDRV_PCM_HW_PARAM_LAST_INTERVAL = SNDRV_PCM_HW_PARAM_TICK_TIME, + SNDRV_PCM_HW_PARAM_LAST = SNDRV_PCM_HW_PARAM_LAST_INTERVAL, +}; + +#define SNDRV_PCM_HW_PARAMS_RUNTIME (1<<0) + +struct sndrv_interval { + unsigned int min, max; + unsigned int openmin:1, + openmax:1, + integer:1, + empty:1; +}; + +struct sndrv_pcm_hw_params { + unsigned int flags; + unsigned int masks[SNDRV_PCM_HW_PARAM_LAST_MASK - + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + unsigned int rmask; + unsigned int cmask; + unsigned int info; /* R: Info flags for returned setup */ + unsigned int msbits; /* R: used most significant bits */ + unsigned int rate_num; /* R: rate numerator */ + unsigned int rate_den; /* R: rate denominator */ + sndrv_pcm_uframes_t fifo_size; /* R: chip FIFO size in frames */ + unsigned char reserved[64]; +}; + +enum sndrv_pcm_tstamp { + SNDRV_PCM_TSTAMP_NONE = 0, + SNDRV_PCM_TSTAMP_MMAP, + SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_MMAP, +}; + +struct sndrv_pcm_sw_params { + enum sndrv_pcm_tstamp tstamp_mode; /* timestamp mode */ + unsigned int period_step; + unsigned int sleep_min; /* min ticks to sleep */ + sndrv_pcm_uframes_t avail_min; /* min avail frames for wakeup */ + sndrv_pcm_uframes_t xfer_align; /* xfer size need to be a multiple */ + sndrv_pcm_uframes_t start_threshold; /* min hw_avail frames for automatic start */ + sndrv_pcm_uframes_t stop_threshold; /* min avail frames for automatic stop */ + sndrv_pcm_uframes_t silence_threshold; /* min distance from noise for silence filling */ + sndrv_pcm_uframes_t silence_size; /* silence block size */ + sndrv_pcm_uframes_t boundary; /* pointers wrap point */ + unsigned char reserved[64]; +}; + +struct sndrv_pcm_channel_info { + unsigned int channel; + off_t offset; /* mmap offset */ + unsigned int first; /* offset to first sample in bits */ + unsigned int step; /* samples distance in bits */ +}; + +struct sndrv_pcm_status { + enum sndrv_pcm_state state; /* stream state */ + struct timeval trigger_tstamp; /* time when stream was started/stopped/paused */ + struct timeval tstamp; /* reference timestamp */ + sndrv_pcm_uframes_t appl_ptr; /* appl ptr */ + sndrv_pcm_uframes_t hw_ptr; /* hw ptr */ + sndrv_pcm_sframes_t delay; /* current delay in frames */ + sndrv_pcm_uframes_t avail; /* number of frames available */ + sndrv_pcm_uframes_t avail_max; /* max frames available on hw since last status */ + sndrv_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ + enum sndrv_pcm_state suspended_state; /* suspended stream state */ + unsigned char reserved[60]; /* must be filled with zero */ +}; + +struct sndrv_pcm_mmap_status { + enum sndrv_pcm_state state; /* RO: state - SNDRV_PCM_STATE_XXXX */ + int pad1; /* Needed for 64 bit alignment */ + sndrv_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */ + struct timeval tstamp; /* Timestamp */ + enum sndrv_pcm_state suspended_state; /* RO: suspended stream state */ +}; + +struct sndrv_pcm_mmap_control { + sndrv_pcm_uframes_t appl_ptr; /* RW: appl ptr (0...boundary-1) */ + sndrv_pcm_uframes_t avail_min; /* RW: min available frames for wakeup */ +}; + +struct sndrv_xferi { + sndrv_pcm_sframes_t result; + void *buf; + sndrv_pcm_uframes_t frames; +}; + +struct sndrv_xfern { + sndrv_pcm_sframes_t result; + void **bufs; + sndrv_pcm_uframes_t frames; +}; + +enum { + SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int), + SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct sndrv_pcm_info), + SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct sndrv_pcm_hw_params), + SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct sndrv_pcm_hw_params), + SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12), + SNDRV_PCM_IOCTL_SW_PARAMS = _IOWR('A', 0x13, struct sndrv_pcm_sw_params), + SNDRV_PCM_IOCTL_STATUS = _IOR('A', 0x20, struct sndrv_pcm_status), + SNDRV_PCM_IOCTL_DELAY = _IOR('A', 0x21, sndrv_pcm_sframes_t), + SNDRV_PCM_IOCTL_CHANNEL_INFO = _IOR('A', 0x32, struct sndrv_pcm_channel_info), + SNDRV_PCM_IOCTL_PREPARE = _IO('A', 0x40), + SNDRV_PCM_IOCTL_RESET = _IO('A', 0x41), + SNDRV_PCM_IOCTL_START = _IO('A', 0x42), + SNDRV_PCM_IOCTL_DROP = _IO('A', 0x43), + SNDRV_PCM_IOCTL_DRAIN = _IO('A', 0x44), + SNDRV_PCM_IOCTL_PAUSE = _IOW('A', 0x45, int), + SNDRV_PCM_IOCTL_REWIND = _IOW('A', 0x46, sndrv_pcm_uframes_t), + SNDRV_PCM_IOCTL_RESUME = _IO('A', 0x47), + SNDRV_PCM_IOCTL_WRITEI_FRAMES = _IOW('A', 0x50, struct sndrv_xferi), + SNDRV_PCM_IOCTL_READI_FRAMES = _IOR('A', 0x51, struct sndrv_xferi), + SNDRV_PCM_IOCTL_WRITEN_FRAMES = _IOW('A', 0x52, struct sndrv_xfern), + SNDRV_PCM_IOCTL_READN_FRAMES = _IOR('A', 0x53, struct sndrv_xfern), + SNDRV_PCM_IOCTL_LINK = _IOW('A', 0x60, int), + SNDRV_PCM_IOCTL_UNLINK = _IO('A', 0x61), +}; + +/* Trick to make alsa-lib/acinclude.m4 happy */ +#define SNDRV_PCM_IOCTL_REWIND SNDRV_PCM_IOCTL_REWIND + +/***************************************************************************** + * * + * MIDI v1.0 interface * + * * + *****************************************************************************/ + +/* + * Raw MIDI section - /dev/snd/midi?? + */ + +#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) + +enum sndrv_rawmidi_stream { + SNDRV_RAWMIDI_STREAM_OUTPUT = 0, + SNDRV_RAWMIDI_STREAM_INPUT, + SNDRV_RAWMIDI_STREAM_LAST = SNDRV_RAWMIDI_STREAM_INPUT, +}; + +#define SNDRV_RAWMIDI_INFO_OUTPUT 0x00000001 +#define SNDRV_RAWMIDI_INFO_INPUT 0x00000002 +#define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004 + +struct sndrv_rawmidi_info { + unsigned int device; /* RO/WR (control): device number */ + unsigned int subdevice; /* RO/WR (control): subdevice number */ + enum sndrv_rawmidi_stream stream; /* WR: stream */ + int card; /* R: card number */ + unsigned int flags; /* SNDRV_RAWMIDI_INFO_XXXX */ + unsigned char id[64]; /* ID (user selectable) */ + unsigned char name[80]; /* name of device */ + unsigned char subname[32]; /* name of active or selected subdevice */ + unsigned int subdevices_count; + unsigned int subdevices_avail; + unsigned char reserved[64]; /* reserved for future use */ +}; + +struct sndrv_rawmidi_params { + enum sndrv_rawmidi_stream stream; + size_t buffer_size; /* queue size in bytes */ + size_t avail_min; /* minimum avail bytes for wakeup */ + unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */ + unsigned char reserved[16]; /* reserved for future use */ +}; + +struct sndrv_rawmidi_status { + enum sndrv_rawmidi_stream stream; + struct timeval tstamp; /* Timestamp */ + size_t avail; /* available bytes */ + size_t xruns; /* count of overruns since last status (in bytes) */ + unsigned char reserved[16]; /* reserved for future use */ +}; + +enum { + SNDRV_RAWMIDI_IOCTL_PVERSION = _IOR('W', 0x00, int), + SNDRV_RAWMIDI_IOCTL_INFO = _IOR('W', 0x01, struct sndrv_rawmidi_info), + SNDRV_RAWMIDI_IOCTL_PARAMS = _IOWR('W', 0x10, struct sndrv_rawmidi_params), + SNDRV_RAWMIDI_IOCTL_STATUS = _IOWR('W', 0x20, struct sndrv_rawmidi_status), + SNDRV_RAWMIDI_IOCTL_DROP = _IOW('W', 0x30, int), + SNDRV_RAWMIDI_IOCTL_DRAIN = _IOW('W', 0x31, int), +}; + +/* + * Timer section - /dev/snd/timer + */ + +#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) + +enum sndrv_timer_class { + SNDRV_TIMER_CLASS_NONE = -1, + SNDRV_TIMER_CLASS_SLAVE = 0, + SNDRV_TIMER_CLASS_GLOBAL, + SNDRV_TIMER_CLASS_CARD, + SNDRV_TIMER_CLASS_PCM, + SNDRV_TIMER_CLASS_LAST = SNDRV_TIMER_CLASS_PCM, +}; + +/* slave timer classes */ +enum sndrv_timer_slave_class { + SNDRV_TIMER_SCLASS_NONE = 0, + SNDRV_TIMER_SCLASS_APPLICATION, + SNDRV_TIMER_SCLASS_SEQUENCER, /* alias */ + SNDRV_TIMER_SCLASS_OSS_SEQUENCER, /* alias */ + SNDRV_TIMER_SCLASS_LAST = SNDRV_TIMER_SCLASS_OSS_SEQUENCER, +}; + +/* global timers (device member) */ +#define SNDRV_TIMER_GLOBAL_SYSTEM 0 +#define SNDRV_TIMER_GLOBAL_RTC 1 + +struct sndrv_timer_id { + enum sndrv_timer_class dev_class; + enum sndrv_timer_slave_class dev_sclass; + int card; + int device; + int subdevice; +}; + +struct sndrv_timer_select { + struct sndrv_timer_id id; /* bind to timer ID */ + unsigned char reserved[32]; /* reserved */ +}; + +#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */ + +struct sndrv_timer_info { + unsigned int flags; /* timer flags - SNDRV_TIMER_FLG_* */ + int card; /* R: card number */ + unsigned char id[64]; /* timer identificator */ + unsigned char name[80]; /* timer name */ + unsigned long ticks; /* maximum ticks */ + unsigned long resolution; /* average resolution */ + unsigned char reserved[64]; /* reserved */ +}; + +#define SNDRV_TIMER_PSFLG_AUTO (1<<0) /* supports auto start */ + +struct sndrv_timer_params { + unsigned int flags; /* flags - SNDRV_MIXER_PSFLG_* */ + unsigned int ticks; /* requested resolution in ticks */ + unsigned int queue_size; /* total size of queue (32-1024) */ + unsigned int reserved0; /* reserved, was: failure locations */ + unsigned char reserved[64]; /* reserved */ +}; + +struct sndrv_timer_status { + struct timeval tstamp; /* Timestamp */ + unsigned int resolution; /* current resolution */ + unsigned int lost; /* counter of master tick lost */ + unsigned int overrun; /* count of read queue overruns */ + unsigned int queue; /* used queue size */ + unsigned char reserved[64]; /* reserved */ +}; + +enum { + SNDRV_TIMER_IOCTL_PVERSION = _IOR('T', 0x00, int), + SNDRV_TIMER_IOCTL_NEXT_DEVICE = _IOWR('T', 0x01, struct sndrv_timer_id), + SNDRV_TIMER_IOCTL_SELECT = _IOW('T', 0x10, struct sndrv_timer_select), + SNDRV_TIMER_IOCTL_INFO = _IOR('T', 0x11, struct sndrv_timer_info), + SNDRV_TIMER_IOCTL_PARAMS = _IOW('T', 0x12, struct sndrv_timer_params), + SNDRV_TIMER_IOCTL_STATUS = _IOW('T', 0x14, struct sndrv_timer_status), + SNDRV_TIMER_IOCTL_START = _IO('T', 0x20), + SNDRV_TIMER_IOCTL_STOP = _IO('T', 0x21), + SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0x22), +}; + +struct sndrv_timer_read { + unsigned int resolution; + unsigned int ticks; +}; + +/**************************************************************************** + * * + * Section for driver control interface - /dev/snd/control? * + * * + ****************************************************************************/ + +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) + +struct sndrv_ctl_card_info { + int card; /* card number */ + int pad; /* reserved for future (was type) */ + unsigned char id[16]; /* ID of card (user selectable) */ + unsigned char driver[16]; /* Driver name */ + unsigned char name[32]; /* Short name of soundcard */ + unsigned char longname[80]; /* name + info text about soundcard */ + unsigned char reserved_[16]; /* reserved for future (was ID of mixer) */ + unsigned char mixername[80]; /* visual mixer identification */ + unsigned char components[80]; /* card components / fine identification, delimited with one space (AC97 etc..) */ + unsigned char reserved[48]; /* reserved for future */ +}; + +enum sndrv_ctl_elem_type { + SNDRV_CTL_ELEM_TYPE_NONE = 0, /* invalid */ + SNDRV_CTL_ELEM_TYPE_BOOLEAN, /* boolean type */ + SNDRV_CTL_ELEM_TYPE_INTEGER, /* integer type */ + SNDRV_CTL_ELEM_TYPE_ENUMERATED, /* enumerated type */ + SNDRV_CTL_ELEM_TYPE_BYTES, /* byte array */ + SNDRV_CTL_ELEM_TYPE_IEC958, /* IEC958 (S/PDIF) setup */ + SNDRV_CTL_ELEM_TYPE_LAST = SNDRV_CTL_ELEM_TYPE_IEC958, +}; + +enum sndrv_ctl_elem_iface { + SNDRV_CTL_ELEM_IFACE_CARD = 0, /* global control */ + SNDRV_CTL_ELEM_IFACE_HWDEP, /* hardware dependent device */ + SNDRV_CTL_ELEM_IFACE_MIXER, /* virtual mixer device */ + SNDRV_CTL_ELEM_IFACE_PCM, /* PCM device */ + SNDRV_CTL_ELEM_IFACE_RAWMIDI, /* RawMidi device */ + SNDRV_CTL_ELEM_IFACE_TIMER, /* timer device */ + SNDRV_CTL_ELEM_IFACE_SEQUENCER, /* sequencer client */ + SNDRV_CTL_ELEM_IFACE_LAST = SNDRV_CTL_ELEM_IFACE_SEQUENCER, +}; + +#define SNDRV_CTL_ELEM_ACCESS_READ (1<<0) +#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) +#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) +#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */ +#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */ +#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */ +#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */ +#define SNDRV_CTL_ELEM_ACCESS_INDIRECT (1<<31) /* indirect access */ + +/* for further details see the ACPI and PCI power management specification */ +#define SNDRV_CTL_POWER_D0 0x0000 /* full On */ +#define SNDRV_CTL_POWER_D1 0x0100 /* partial On */ +#define SNDRV_CTL_POWER_D2 0x0200 /* partial On */ +#define SNDRV_CTL_POWER_D3 0x0300 /* Off */ +#define SNDRV_CTL_POWER_D3hot (SNDRV_CTL_POWER_D3|0x0000) /* Off, with power */ +#define SNDRV_CTL_POWER_D3cold (SNDRV_CTL_POWER_D3|0x0001) /* Off, without power */ + +struct sndrv_ctl_elem_id { + unsigned int numid; /* numeric identifier, zero = invalid */ + enum sndrv_ctl_elem_iface iface; /* interface identifier */ + unsigned int device; /* device/client number */ + unsigned int subdevice; /* subdevice (substream) number */ + unsigned char name[44]; /* ASCII name of item */ + unsigned int index; /* index of item */ +}; + +struct sndrv_ctl_elem_list { + unsigned int offset; /* W: first element ID to get */ + unsigned int space; /* W: count of element IDs to get */ + unsigned int used; /* R: count of element IDs set */ + unsigned int count; /* R: count of all elements */ + struct sndrv_ctl_elem_id *pids; /* R: IDs */ + unsigned char reserved[50]; +}; + +struct sndrv_ctl_elem_info { + struct sndrv_ctl_elem_id id; /* W: element ID */ + enum sndrv_ctl_elem_type type; /* R: value type - SNDRV_CTL_ELEM_TYPE_* */ + unsigned int access; /* R: value access (bitmask) - SNDRV_CTL_ELEM_ACCESS_* */ + unsigned int count; /* count of values */ + pid_t owner; /* owner's PID of this control */ + union { + struct { + long min; /* R: minimum value */ + long max; /* R: maximum value */ + long step; /* R: step (0 variable) */ + } integer; + struct { + unsigned int items; /* R: number of items */ + unsigned int item; /* W: item number */ + char name[64]; /* R: value name */ + } enumerated; + unsigned char reserved[128]; + } value; + unsigned char reserved[64]; +}; + +struct sndrv_ctl_elem_value { + struct sndrv_ctl_elem_id id; /* W: element ID */ + unsigned int indirect: 1; /* W: use indirect pointer (xxx_ptr member) */ + union { + union { + long value[128]; + long *value_ptr; + } integer; + union { + unsigned int item[128]; + unsigned int *item_ptr; + } enumerated; + union { + unsigned char data[512]; + unsigned char *data_ptr; + } bytes; + struct sndrv_aes_iec958 iec958; + } value; /* RO */ + unsigned char reserved[128]; +}; + +enum { + SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), + SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct sndrv_ctl_card_info), + SNDRV_CTL_IOCTL_ELEM_LIST = _IOWR('U', 0x10, struct sndrv_ctl_elem_list), + SNDRV_CTL_IOCTL_ELEM_INFO = _IOWR('U', 0x11, struct sndrv_ctl_elem_info), + SNDRV_CTL_IOCTL_ELEM_READ = _IOWR('U', 0x12, struct sndrv_ctl_elem_value), + SNDRV_CTL_IOCTL_ELEM_WRITE = _IOWR('U', 0x13, struct sndrv_ctl_elem_value), + SNDRV_CTL_IOCTL_ELEM_LOCK = _IOW('U', 0x14, struct sndrv_ctl_elem_id), + SNDRV_CTL_IOCTL_ELEM_UNLOCK = _IOW('U', 0x15, struct sndrv_ctl_elem_id), + SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS = _IOWR('U', 0x16, int), + SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), + SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct sndrv_hwdep_info), + SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), + SNDRV_CTL_IOCTL_PCM_INFO = _IOWR('U', 0x31, struct sndrv_pcm_info), + SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE = _IOW('U', 0x32, int), + SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE = _IOWR('U', 0x40, int), + SNDRV_CTL_IOCTL_RAWMIDI_INFO = _IOWR('U', 0x41, struct sndrv_rawmidi_info), + SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE = _IOW('U', 0x42, int), + SNDRV_CTL_IOCTL_POWER = _IOWR('U', 0xd0, int), + SNDRV_CTL_IOCTL_POWER_STATE = _IOR('U', 0xd1, int), +}; + +/* + * Read interface. + */ + +enum sndrv_ctl_event_type { + SNDRV_CTL_EVENT_ELEM = 0, + SNDRV_CTL_EVENT_LAST = SNDRV_CTL_EVENT_ELEM, +}; + +#define SNDRV_CTL_EVENT_MASK_VALUE (1<<0) /* element value was changed */ +#define SNDRV_CTL_EVENT_MASK_INFO (1<<1) /* element info was changed */ +#define SNDRV_CTL_EVENT_MASK_ADD (1<<2) /* element was added */ +#define SNDRV_CTL_EVENT_MASK_REMOVE (~0U) /* element was removed */ + +struct sndrv_ctl_event { + enum sndrv_ctl_event_type type; /* event type - SNDRV_CTL_EVENT_* */ + union { + struct { + unsigned int mask; + struct sndrv_ctl_elem_id id; + } elem; + unsigned char data8[60]; + } data; +}; + +/* + * Control names + */ + +#define SNDRV_CTL_NAME_NONE "" +#define SNDRV_CTL_NAME_PLAYBACK "Playback " +#define SNDRV_CTL_NAME_CAPTURE "Capture " + +#define SNDRV_CTL_NAME_IEC958_NONE "" +#define SNDRV_CTL_NAME_IEC958_SWITCH "Switch" +#define SNDRV_CTL_NAME_IEC958_VOLUME "Volume" +#define SNDRV_CTL_NAME_IEC958_DEFAULT "Default" +#define SNDRV_CTL_NAME_IEC958_MASK "Mask" +#define SNDRV_CTL_NAME_IEC958_CON_MASK "Con Mask" +#define SNDRV_CTL_NAME_IEC958_PRO_MASK "Pro Mask" +#define SNDRV_CTL_NAME_IEC958_PCM_STREAM "PCM Stream" +#define SNDRV_CTL_NAME_IEC958(expl,direction,what) "IEC958 " expl SNDRV_CTL_NAME_##direction SNDRV_CTL_NAME_IEC958_##what + +/* + * + */ + +struct sndrv_xferv { + const struct iovec *vector; + unsigned long count; +}; + +enum { + SNDRV_IOCTL_READV = _IOW('K', 0x00, struct sndrv_xferv), + SNDRV_IOCTL_WRITEV = _IOW('K', 0x01, struct sndrv_xferv), +}; + +#endif /* __SOUND_ASOUND_H */ diff -Nru a/include/sound/asound_fm.h b/include/sound/asound_fm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/asound_fm.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,119 @@ +#ifndef __SOUND_ASOUND_FM_H +#define __SOUND_ASOUND_FM_H + +/* + * Advanced Linux Sound Architecture - ALSA + * + * Interface file between ALSA driver & user space + * Copyright (c) 1994-98 by Jaroslav Kysela , + * 4Front Technologies + * + * Direct FM control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_DM_FM_MODE_OPL2 0x00 +#define SNDRV_DM_FM_MODE_OPL3 0x01 + +typedef struct snd_dm_fm_info { + unsigned char fm_mode; /* OPL mode, see SNDRV_DM_FM_MODE_XXX */ + unsigned char rhythm; /* percussion mode flag */ +} snd_dm_fm_info_t; + +/* + * Data structure composing an FM "note" or sound event. + */ + +typedef struct snd_dm_fm_voice { + unsigned char op; /* operator cell (0 or 1) */ + unsigned char voice; /* FM voice (0 to 17) */ + + unsigned char am; /* amplitude modulation */ + unsigned char vibrato; /* vibrato effect */ + unsigned char do_sustain; /* sustain phase */ + unsigned char kbd_scale; /* keyboard scaling */ + unsigned char harmonic; /* 4 bits: harmonic and multiplier */ + unsigned char scale_level; /* 2 bits: decrease output freq rises */ + unsigned char volume; /* 6 bits: volume */ + + unsigned char attack; /* 4 bits: attack rate */ + unsigned char decay; /* 4 bits: decay rate */ + unsigned char sustain; /* 4 bits: sustain level */ + unsigned char release; /* 4 bits: release rate */ + + unsigned char feedback; /* 3 bits: feedback for op0 */ + unsigned char connection; /* 0 for serial, 1 for parallel */ + unsigned char left; /* stereo left */ + unsigned char right; /* stereo right */ + unsigned char waveform; /* 3 bits: waveform shape */ +} snd_dm_fm_voice_t; + +/* + * This describes an FM note by its voice, octave, frequency number (10bit) + * and key on/off. + */ + +typedef struct snd_dm_fm_note { + unsigned char voice; /* 0-17 voice channel */ + unsigned char octave; /* 3 bits: what octave to play */ + unsigned int fnum; /* 10 bits: frequency number */ + unsigned char key_on; /* set for active, clear for silent */ +} snd_dm_fm_note_t; + +/* + * FM parameters that apply globally to all voices, and thus are not "notes" + */ + +typedef struct snd_dm_fm_params { + unsigned char am_depth; /* amplitude modulation depth (1=hi) */ + unsigned char vib_depth; /* vibrato depth (1=hi) */ + unsigned char kbd_split; /* keyboard split */ + unsigned char rhythm; /* percussion mode select */ + + /* This block is the percussion instrument data */ + unsigned char bass; + unsigned char snare; + unsigned char tomtom; + unsigned char cymbal; + unsigned char hihat; +} snd_dm_fm_params_t; + +/* + * FM mode ioctl settings + */ + +#define SNDRV_DM_FM_IOCTL_INFO _IOR('H', 0x20, snd_dm_fm_info_t) +#define SNDRV_DM_FM_IOCTL_RESET _IO ('H', 0x21) +#define SNDRV_DM_FM_IOCTL_PLAY_NOTE _IOW('H', 0x22, snd_dm_fm_note_t) +#define SNDRV_DM_FM_IOCTL_SET_VOICE _IOW('H', 0x23, snd_dm_fm_voice_t) +#define SNDRV_DM_FM_IOCTL_SET_PARAMS _IOW('H', 0x24, snd_dm_fm_params_t) +#define SNDRV_DM_FM_IOCTL_SET_MODE _IOW('H', 0x25, int) +/* for OPL3 only */ +#define SNDRV_DM_FM_IOCTL_SET_CONNECTION _IOW('H', 0x26, int) + +#ifdef __SND_OSS_COMPAT__ + +#define SNDRV_DM_FM_OSS_IOCTL_RESET 0x20 +#define SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE 0x21 +#define SNDRV_DM_FM_OSS_IOCTL_SET_VOICE 0x22 +#define SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS 0x23 +#define SNDRV_DM_FM_OSS_IOCTL_SET_MODE 0x24 +#define SNDRV_DM_FM_OSS_IOCTL_SET_OPL 0x25 + +#endif + +#endif /* __SOUND_ASOUND_FM_H */ diff -Nru a/include/sound/asoundef.h b/include/sound/asoundef.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/asoundef.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,227 @@ +#ifndef __SOUND_ASOUNDEF_H +#define __SOUND_ASOUNDEF_H + +/* + * Advanced Linux Sound Architecture - ALSA - Driver + * Copyright (c) 1994-2000 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/**************************************************************************** + * * + * Digital audio interface * + * * + ****************************************************************************/ + +/* AES/IEC958 channel status bits */ +#define IEC958_AES0_PROFESSIONAL (1<<0) /* 0 = consumer, 1 = professional */ +#define IEC958_AES0_NONAUDIO (1<<1) /* 0 = audio, 1 = non-audio */ +#define IEC958_AES0_PRO_EMPHASIS (7<<2) /* mask - emphasis */ +#define IEC958_AES0_PRO_EMPHASIS_NOTID (0<<2) /* emphasis not indicated */ +#define IEC958_AES0_PRO_EMPHASIS_NONE (1<<2) /* none emphasis */ +#define IEC958_AES0_PRO_EMPHASIS_5015 (3<<2) /* 50/15us emphasis */ +#define IEC958_AES0_PRO_EMPHASIS_CCITT (7<<2) /* CCITT J.17 emphasis */ +#define IEC958_AES0_PRO_FREQ_UNLOCKED (1<<5) /* source sample frequency: 0 = locked, 1 = unlocked */ +#define IEC958_AES0_PRO_FS (3<<6) /* mask - sample frequency */ +#define IEC958_AES0_PRO_FS_NOTID (0<<6) /* fs not indicated */ +#define IEC958_AES0_PRO_FS_44100 (1<<6) /* 44.1kHz */ +#define IEC958_AES0_PRO_FS_48000 (2<<6) /* 48kHz */ +#define IEC958_AES0_PRO_FS_32000 (3<<6) /* 32kHz */ +#define IEC958_AES0_CON_NOT_COPYRIGHT (1<<2) /* 0 = copyright, 1 = not copyright */ +#define IEC958_AES0_CON_EMPHASIS (7<<3) /* mask - emphasis */ +#define IEC958_AES0_CON_EMPHASIS_NONE (0<<3) /* none emphasis */ +#define IEC958_AES0_CON_EMPHASIS_5015 (1<<3) /* 50/15us emphasis */ +#define IEC958_AES0_CON_MODE (3<<6) /* mask - mode */ +#define IEC958_AES1_PRO_MODE (15<<0) /* mask - channel mode */ +#define IEC958_AES1_PRO_MODE_NOTID (0<<0) /* not indicated */ +#define IEC958_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */ +#define IEC958_AES1_PRO_MODE_SINGLE (4<<0) /* single channel */ +#define IEC958_AES1_PRO_MODE_TWO (8<<0) /* two channels */ +#define IEC958_AES1_PRO_MODE_PRIMARY (12<<0) /* primary/secondary */ +#define IEC958_AES1_PRO_MODE_BYTE3 (15<<0) /* vector to byte 3 */ +#define IEC958_AES1_PRO_USERBITS (15<<4) /* mask - user bits */ +#define IEC958_AES1_PRO_USERBITS_NOTID (0<<4) /* not indicated */ +#define IEC958_AES1_PRO_USERBITS_192 (8<<4) /* 192-bit structure */ +#define IEC958_AES1_PRO_USERBITS_UDEF (12<<4) /* user defined application */ +#define IEC958_AES1_CON_CATEGORY 0x7f +#define IEC958_AES1_CON_GENERAL 0x00 +#define IEC958_AES1_CON_EXPERIMENTAL 0x40 +#define IEC958_AES1_CON_SOLIDMEM_MASK 0x0f +#define IEC958_AES1_CON_SOLIDMEM_ID 0x08 +#define IEC958_AES1_CON_BROADCAST1_MASK 0x07 +#define IEC958_AES1_CON_BROADCAST1_ID 0x04 +#define IEC958_AES1_CON_DIGDIGCONV_MASK 0x07 +#define IEC958_AES1_CON_DIGDIGCONV_ID 0x02 +#define IEC958_AES1_CON_ADC_COPYRIGHT_MASK 0x1f +#define IEC958_AES1_CON_ADC_COPYRIGHT_ID 0x06 +#define IEC958_AES1_CON_ADC_MASK 0x1f +#define IEC958_AES1_CON_ADC_ID 0x16 +#define IEC958_AES1_CON_BROADCAST2_MASK 0x0f +#define IEC958_AES1_CON_BROADCAST2_ID 0x0e +#define IEC958_AES1_CON_LASEROPT_MASK 0x07 +#define IEC958_AES1_CON_LASEROPT_ID 0x01 +#define IEC958_AES1_CON_MUSICAL_MASK 0x07 +#define IEC958_AES1_CON_MUSICAL_ID 0x05 +#define IEC958_AES1_CON_MAGNETIC_MASK 0x07 +#define IEC958_AES1_CON_MAGNETIC_ID 0x03 +#define IEC958_AES1_CON_IEC908_CD (IEC958_AES1_CON_LASEROPT_ID|0x00) +#define IEC958_AES1_CON_NON_IEC908_CD (IEC958_AES1_CON_LASEROPT_ID|0x08) +#define IEC958_AES1_CON_PCM_CODER (IEC958_AES1_CON_DIGDIGCONV_ID|0x00) +#define IEC958_AES1_CON_SAMPLER (IEC958_AES1_CON_DIGDIGCONV_ID|0x20) +#define IEC958_AES1_CON_MIXER (IEC958_AES1_CON_DIGDIGCONV_ID|0x10) +#define IEC958_AES1_CON_RATE_CONVERTER (IEC958_AES1_CON_DIGDIGCONV_ID|0x18) +#define IEC958_AES1_CON_SYNTHESIZER (IEC958_AES1_CON_MUSICAL_ID|0x00) +#define IEC958_AES1_CON_MICROPHONE (IEC958_AES1_CON_MUSICAL_ID|0x08) +#define IEC958_AES1_CON_DAT (IEC958_AES1_CON_MAGNETIC_ID|0x00) +#define IEC958_AES1_CON_VCR (IEC958_AES1_CON_MAGNETIC_ID|0x08) +#define IEC958_AES1_CON_ORIGINAL (1<<7) /* this bits depends on the category code */ +#define IEC958_AES2_PRO_SBITS (7<<0) /* mask - sample bits */ +#define IEC958_AES2_PRO_SBITS_20 (2<<0) /* 20-bit - coordination */ +#define IEC958_AES2_PRO_SBITS_24 (4<<0) /* 24-bit - main audio */ +#define IEC958_AES2_PRO_SBITS_UDEF (6<<0) /* user defined application */ +#define IEC958_AES2_PRO_WORDLEN (7<<3) /* mask - source word length */ +#define IEC958_AES2_PRO_WORDLEN_NOTID (0<<3) /* not indicated */ +#define IEC958_AES2_PRO_WORDLEN_22_18 (2<<3) /* 22-bit or 18-bit */ +#define IEC958_AES2_PRO_WORDLEN_23_19 (4<<3) /* 23-bit or 19-bit */ +#define IEC958_AES2_PRO_WORDLEN_24_20 (5<<3) /* 24-bit or 20-bit */ +#define IEC958_AES2_PRO_WORDLEN_20_16 (6<<3) /* 20-bit or 16-bit */ +#define IEC958_AES2_CON_SOURCE (15<<0) /* mask - source number */ +#define IEC958_AES2_CON_SOURCE_UNSPEC (0<<0) /* unspecified */ +#define IEC958_AES2_CON_CHANNEL (15<<4) /* mask - channel number */ +#define IEC958_AES2_CON_CHANNEL_UNSPEC (0<<4) /* unspecified */ +#define IEC958_AES3_CON_FS (15<<0) /* mask - sample frequency */ +#define IEC958_AES3_CON_FS_44100 (0<<0) /* 44.1kHz */ +#define IEC958_AES3_CON_FS_48000 (2<<0) /* 48kHz */ +#define IEC958_AES3_CON_FS_32000 (3<<0) /* 32kHz */ +#define IEC958_AES3_CON_CLOCK (3<<4) /* mask - clock accuracy */ +#define IEC958_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */ +#define IEC958_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */ +#define IEC958_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */ + +/***************************************************************************** + * * + * MIDI v1.0 interface * + * * + *****************************************************************************/ + +#define MIDI_CHANNELS 16 +#define MIDI_GM_DRUM_CHANNEL (10-1) + +/* + * MIDI commands + */ + +#define MIDI_CMD_NOTE_OFF 0x80 +#define MIDI_CMD_NOTE_ON 0x90 +#define MIDI_CMD_NOTE_PRESSURE 0xa0 +#define MIDI_CMD_CONTROL 0xb0 +#define MIDI_CMD_PGM_CHANGE 0xc0 +#define MIDI_CMD_CHANNEL_PRESSURE 0xd0 +#define MIDI_CMD_BENDER 0xe0 + +#define MIDI_CMD_COMMON_SYSEX 0xf0 +#define MIDI_CMD_COMMON_MTC_QUARTER 0xf1 +#define MIDI_CMD_COMMON_SONG_POS 0xf2 +#define MIDI_CMD_COMMON_SONG_SELECT 0xf3 +#define MIDI_CMD_COMMON_TUNE_REQUEST 0xf6 +#define MIDI_CMD_COMMON_SYSEX_END 0xf7 +#define MIDI_CMD_COMMON_CLOCK 0xf8 +#define MIDI_CMD_COMMON_START 0xfa +#define MIDI_CMD_COMMON_CONTINUE 0xfb +#define MIDI_CMD_COMMON_STOP 0xfc +#define MIDI_CMD_COMMON_SENSING 0xfe +#define MIDI_CMD_COMMON_RESET 0xff + +/* + * MIDI controllers + */ + +#define MIDI_CTL_MSB_BANK 0x00 +#define MIDI_CTL_MSB_MODWHEEL 0x01 +#define MIDI_CTL_MSB_BREATH 0x02 +#define MIDI_CTL_MSB_FOOT 0x04 +#define MIDI_CTL_MSB_PORTNAMENTO_TIME 0x05 +#define MIDI_CTL_MSB_DATA_ENTRY 0x06 +#define MIDI_CTL_MSB_MAIN_VOLUME 0x07 +#define MIDI_CTL_MSB_BALANCE 0x08 +#define MIDI_CTL_MSB_PAN 0x0a +#define MIDI_CTL_MSB_EXPRESSION 0x0b +#define MIDI_CTL_MSB_EFFECT1 0x0c +#define MIDI_CTL_MSB_EFFECT2 0x0d +#define MIDI_CTL_MSB_GENERAL_PURPOSE1 0x10 +#define MIDI_CTL_MSB_GENERAL_PURPOSE2 0x11 +#define MIDI_CTL_MSB_GENERAL_PURPOSE3 0x12 +#define MIDI_CTL_MSB_GENERAL_PURPOSE4 0x13 +#define MIDI_CTL_LSB_BANK 0x20 +#define MIDI_CTL_LSB_MODWHEEL 0x21 +#define MIDI_CTL_LSB_BREATH 0x22 +#define MIDI_CTL_LSB_FOOT 0x24 +#define MIDI_CTL_LSB_PORTNAMENTO_TIME 0x25 +#define MIDI_CTL_LSB_DATA_ENTRY 0x26 +#define MIDI_CTL_LSB_MAIN_VOLUME 0x27 +#define MIDI_CTL_LSB_BALANCE 0x28 +#define MIDI_CTL_LSB_PAN 0x2a +#define MIDI_CTL_LSB_EXPRESSION 0x2b +#define MIDI_CTL_LSB_EFFECT1 0x2c +#define MIDI_CTL_LSB_EFFECT2 0x2d +#define MIDI_CTL_LSB_GENERAL_PURPOSE1 0x30 +#define MIDI_CTL_LSB_GENERAL_PURPOSE2 0x31 +#define MIDI_CTL_LSB_GENERAL_PURPOSE3 0x32 +#define MIDI_CTL_LSB_GENERAL_PURPOSE4 0x33 +#define MIDI_CTL_SUSTAIN 0x40 +#define MIDI_CTL_PORTAMENTO 0x41 +#define MIDI_CTL_SUSTENUTO 0x42 +#define MIDI_CTL_SOFT_PEDAL 0x43 +#define MIDI_CTL_LEGATO_FOOTSWITCH 0x44 +#define MIDI_CTL_HOLD2 0x45 +#define MIDI_CTL_SC1_SOUND_VARIATION 0x46 +#define MIDI_CTL_SC2_TIMBRE 0x47 +#define MIDI_CTL_SC3_RELEASE_TIME 0x48 +#define MIDI_CTL_SC4_ATTACK_TIME 0x49 +#define MIDI_CTL_SC5_BRIGHTNESS 0x4a +#define MIDI_CTL_SC6 0x4b +#define MIDI_CTL_SC7 0x4c +#define MIDI_CTL_SC8 0x4d +#define MIDI_CTL_SC9 0x4e +#define MIDI_CTL_SC10 0x4f +#define MIDI_CTL_GENERAL_PURPOSE5 0x50 +#define MIDI_CTL_GENERAL_PURPOSE6 0x51 +#define MIDI_CTL_GENERAL_PURPOSE7 0x52 +#define MIDI_CTL_GENERAL_PURPOSE8 0x53 +#define MIDI_CTL_PORNAMENTO_CONTROL 0x54 +#define MIDI_CTL_E1_REVERB_DEPTH 0x5b +#define MIDI_CTL_E2_TREMOLO_DEPTH 0x5c +#define MIDI_CTL_E3_CHORUS_DEPTH 0x5d +#define MIDI_CTL_E4_DETUNE_DEPTH 0x5e +#define MIDI_CTL_E5_PHASER_DEPTH 0x5f +#define MIDI_CTL_DATA_INCREMENT 0x60 +#define MIDI_CTL_DATA_DECREMENT 0x61 +#define MIDI_CTL_NONREG_PARM_NUM_LSB 0x62 +#define MIDI_CTL_NONREG_PARM_NUM_MSB 0x63 +#define MIDI_CTL_REGIST_PARM_NUM_LSB 0x64 +#define MIDI_CTL_REGIST_PARM_NUM_MSB 0x65 +#define MIDI_CTL_ALL_SOUNDS_OFF 0x78 +#define MIDI_CTL_RESET_CONTROLLERS 0x79 +#define MIDI_CTL_LOCAL_CONTROL_SWITCH 0x7a +#define MIDI_CTL_ALL_NOTES_OFF 0x7b +#define MIDI_CTL_OMNI_OFF 0x7c +#define MIDI_CTL_OMNI_ON 0x7d +#define MIDI_CTL_MONO1 0x7e +#define MIDI_CTL_MONO2 0x7f + +#endif /* __SOUND_ASOUNDEF_H */ diff -Nru a/include/sound/control.h b/include/sound/control.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/control.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,118 @@ +#ifndef __SOUND_CONTROL_H +#define __SOUND_CONTROL_H + +/* + * Header file for control interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +typedef struct sndrv_aes_iec958 snd_aes_iec958_t; +typedef struct sndrv_ctl_card_info snd_ctl_card_info_t; +typedef enum sndrv_ctl_elem_type snd_ctl_elem_type_t; +typedef enum sndrv_ctl_elem_iface snd_ctl_elem_iface_t; +typedef struct sndrv_ctl_elem_id snd_ctl_elem_id_t; +typedef struct sndrv_ctl_elem_list snd_ctl_elem_list_t; +typedef struct sndrv_ctl_elem_info snd_ctl_elem_info_t; +typedef struct sndrv_ctl_elem_value snd_ctl_elem_value_t; +typedef enum sndrv_ctl_event_type snd_ctl_event_type_t; +typedef struct sndrv_ctl_event snd_ctl_event_t; + +#define _snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data) +#define snd_kcontrol_chip(kcontrol) snd_magic_cast1(chip_t, _snd_kcontrol_chip(kcontrol), return -ENXIO) + +typedef int (snd_kcontrol_info_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo); +typedef int (snd_kcontrol_get_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +typedef int (snd_kcontrol_put_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +typedef struct _snd_kcontrol_new { + snd_ctl_elem_iface_t iface; /* interface identifier */ + unsigned int device; /* device/client number */ + unsigned int subdevice; /* subdevice (substream) number */ + unsigned char *name; /* ASCII name of item */ + unsigned int index; /* index of item */ + unsigned int access; /* access rights */ + snd_kcontrol_info_t *info; + snd_kcontrol_get_t *get; + snd_kcontrol_put_t *put; + unsigned long private_value; +} snd_kcontrol_new_t; + +struct _snd_kcontrol { + struct list_head list; /* list of controls */ + snd_ctl_elem_id_t id; + snd_ctl_file_t *owner; /* locked */ + pid_t owner_pid; + unsigned int access; /* access rights */ + snd_kcontrol_info_t *info; + snd_kcontrol_get_t *get; + snd_kcontrol_put_t *put; + unsigned long private_value; + void *private_data; + void (*private_free)(snd_kcontrol_t *kcontrol); +}; + +#define snd_kcontrol(n) list_entry(n, snd_kcontrol_t, list) + +typedef struct _snd_kctl_event { + struct list_head list; /* list of events */ + snd_ctl_elem_id_t id; + unsigned int mask; +} snd_kctl_event_t; + +#define snd_kctl_event(n) list_entry(n, snd_kctl_event_t, list) + +struct _snd_ctl_file { + struct list_head list; /* list of all control files */ + snd_card_t *card; + pid_t pid; + int prefer_pcm_subdevice; + int prefer_rawmidi_subdevice; + wait_queue_head_t change_sleep; + spinlock_t read_lock; + struct fasync_struct *fasync; + int subscribed; /* read interface is activated */ + struct list_head events; /* waiting events for read */ +}; + +#define snd_ctl_file(n) list_entry(n, snd_ctl_file_t, list) + +typedef int (*snd_kctl_ioctl_func_t) (snd_card_t * card, + snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg); + +void snd_ctl_notify(snd_card_t * card, unsigned int mask, snd_ctl_elem_id_t * id); + +snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * kcontrol); +snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * kcontrolnew, void * private_data); +void snd_ctl_free_one(snd_kcontrol_t * kcontrol); +int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol); +int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol); +int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id); +int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id); +snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid); +snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id); + +int snd_ctl_register(snd_card_t *card); +int snd_ctl_unregister(snd_card_t *card); +int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn); +int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn); + +#endif /* __SOUND_CONTROL_H */ diff -Nru a/include/sound/core.h b/include/sound/core.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/core.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,344 @@ +#ifndef __SOUND_CORE_H +#define __SOUND_CORE_H + +/* + * Main header file for the ALSA driver + * Copyright (c) 1994-2001 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Typedef's */ +typedef struct timeval snd_timestamp_t; +typedef struct sndrv_interval snd_interval_t; +typedef enum sndrv_card_type snd_card_type; +typedef struct sndrv_xferi snd_xferi_t; +typedef struct sndrv_xfern snd_xfern_t; +typedef struct sndrv_xferv snd_xferv_t; + +/* device allocation stuff */ + +#define SNDRV_DEV_TYPE_RANGE_SIZE 0x1000 + +typedef enum { + SNDRV_DEV_TOPLEVEL = (0*SNDRV_DEV_TYPE_RANGE_SIZE), + SNDRV_DEV_LOWLEVEL_PRE, + SNDRV_DEV_LOWLEVEL_NORMAL = (1*SNDRV_DEV_TYPE_RANGE_SIZE), + SNDRV_DEV_PCM, + SNDRV_DEV_RAWMIDI, + SNDRV_DEV_TIMER, + SNDRV_DEV_SEQUENCER, + SNDRV_DEV_HWDEP, + SNDRV_DEV_LOWLEVEL = (2*SNDRV_DEV_TYPE_RANGE_SIZE) +} snd_device_type_t; + +typedef enum { + SNDRV_DEV_BUILD = 0, + SNDRV_DEV_REGISTERED = 1 +} snd_device_state_t; + +typedef enum { + SNDRV_DEV_CMD_PRE = 0, + SNDRV_DEV_CMD_NORMAL = 1, + SNDRV_DEV_CMD_POST = 2 +} snd_device_cmd_t; + +typedef struct _snd_card snd_card_t; +typedef struct _snd_device snd_device_t; + +typedef int (snd_dev_free_t)(snd_device_t *device); +typedef int (snd_dev_register_t)(snd_device_t *device); +typedef int (snd_dev_unregister_t)(snd_device_t *device); + +typedef struct { + snd_dev_free_t *dev_free; + snd_dev_register_t *dev_register; + snd_dev_unregister_t *dev_unregister; +} snd_device_ops_t; + +struct _snd_device { + struct list_head list; /* list of registered devices */ + snd_card_t *card; /* card which holds this device */ + snd_device_state_t state; /* state of the device */ + snd_device_type_t type; /* device type */ + void *device_data; /* device structure */ + snd_device_ops_t *ops; /* operations */ +}; + +#define snd_device(n) list_entry(n, snd_device_t, list) + +/* various typedefs */ + +typedef struct snd_info_entry snd_info_entry_t; +typedef struct _snd_pcm snd_pcm_t; +typedef struct _snd_pcm_str snd_pcm_str_t; +typedef struct _snd_pcm_substream snd_pcm_substream_t; +typedef struct _snd_mixer snd_kmixer_t; +typedef struct _snd_rawmidi snd_rawmidi_t; +typedef struct _snd_ctl_file snd_ctl_file_t; +typedef struct _snd_kcontrol snd_kcontrol_t; +typedef struct _snd_timer snd_timer_t; +typedef struct _snd_timer_instance snd_timer_instance_t; +typedef struct _snd_hwdep snd_hwdep_t; +#ifdef CONFIG_SND_OSSEMUL +typedef struct _snd_oss_mixer snd_mixer_oss_t; +#endif + +/* main structure for soundcard */ + +struct _snd_card { + int number; /* number of soundcard (index to snd_cards) */ + + char id[16]; /* id string of this card */ + char driver[16]; /* driver name */ + char shortname[32]; /* short name of this soundcard */ + char longname[80]; /* name of this soundcard */ + char mixername[80]; /* mixer name */ + char components[80]; /* card components delimited with space */ + + struct module *module; /* top-level module */ + + void *private_data; /* private data for soundcard */ + void (*private_free) (snd_card_t *card); /* callback for freeing of private data */ + + struct list_head devices; /* devices */ + + unsigned int last_numid; /* last used numeric ID */ + rwlock_t control_rwlock; /* control list lock */ + rwlock_t control_owner_lock; /* control list lock */ + int controls_count; /* count of all controls */ + struct list_head controls; /* all controls for this card */ + struct list_head ctl_files; /* active control files */ + + snd_info_entry_t *proc_root; /* root for soundcard specific files */ + snd_info_entry_t *proc_id; /* the card id */ + struct proc_dir_entry *proc_root_link; /* number link to real id */ + +#ifdef CONFIG_PM + int (*set_power_state) (snd_card_t *card, unsigned int state); + void *power_state_private_data; + unsigned int power_state; /* power state */ + struct semaphore power_lock; /* power lock */ + wait_queue_head_t power_sleep; +#endif + +#ifdef CONFIG_SND_OSSEMUL + snd_mixer_oss_t *mixer_oss; + int mixer_oss_change_count; +#endif +}; + +#ifdef CONFIG_PM +static inline void snd_power_lock(snd_card_t *card) +{ + down(&card->power_lock); +} + +static inline void snd_power_unlock(snd_card_t *card) +{ + up(&card->power_lock); +} + +void snd_power_wait(snd_card_t *card); + +static inline unsigned int snd_power_get_state(snd_card_t *card) +{ + return card->power_state; +} + +static inline void snd_power_change_state(snd_card_t *card, unsigned int state) +{ + card->power_state = state; + wake_up(&card->power_sleep); +} +#else +#define snd_power_lock(card) do { ; } while (0) +#define snd_power_unlock(card) do { ; } while (0) +#define snd_power_wait(card) do { ; } while (0) +#define snd_power_get_state(card) SNDRV_CTL_POWER_D0 +#define snd_power_change_state(card, state) do { ; } while (0) +#endif + +/* device.c */ + +struct _snd_minor { + struct list_head list; /* list of all minors per card */ + int number; /* minor number */ + int device; /* device number */ + const char *comment; /* for /proc/asound/devices */ + snd_info_entry_t *dev; /* for /proc/asound/dev */ + struct file_operations *f_ops; /* file operations */ +}; + +typedef struct _snd_minor snd_minor_t; + +/* sound.c */ + +extern int snd_ecards_limit; +extern int snd_device_mode; +extern int snd_device_gid; +extern int snd_device_uid; + +void snd_request_card(int card); + +int snd_register_device(int type, snd_card_t *card, int dev, snd_minor_t *reg, const char *name); +int snd_unregister_device(int type, snd_card_t *card, int dev); + +#ifdef CONFIG_SND_OSSEMUL +int snd_register_oss_device(int type, snd_card_t *card, int dev, snd_minor_t *reg, const char *name); +int snd_unregister_oss_device(int type, snd_card_t *card, int dev); +#endif + +int snd_minor_info_init(void); +int snd_minor_info_done(void); + +/* sound_oss.c */ + +#ifdef CONFIG_SND_OSSEMUL + +int snd_minor_info_oss_init(void); +int snd_minor_info_oss_done(void); + +int snd_oss_init_module(void); +void snd_oss_cleanup_module(void); + +#endif + +/* memory.c */ + +#ifdef CONFIG_SND_DEBUG_MEMORY +void snd_memory_init(void); +void snd_memory_done(void); +int snd_memory_info_init(void); +int snd_memory_info_done(void); +void *snd_hidden_kmalloc(size_t size, int flags); +void snd_hidden_kfree(const void *obj); +void *snd_hidden_vmalloc(unsigned long size); +void snd_hidden_vfree(void *obj); +#define kmalloc(size, flags) snd_hidden_kmalloc(size, flags) +#define kfree(obj) snd_hidden_kfree(obj) +#define kfree_nocheck(obj) snd_wrapper_kfree(obj) +#define vmalloc(size) snd_hidden_vmalloc(size) +#define vfree(obj) snd_hidden_vfree(obj) +#else +#define kfree_nocheck(obj) kfree(obj) +#endif +void *snd_kcalloc(size_t size, int flags); +char *snd_kmalloc_strdup(const char *string, int flags); +void *snd_malloc_pages(unsigned long size, unsigned int dma_flags); +void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsigned long *res_size); +void snd_free_pages(void *ptr, unsigned long size); +#ifdef CONFIG_ISA +void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr); +void *snd_malloc_isa_pages_fallback(unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size); +#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size) +#endif +#ifdef CONFIG_PCI +void *snd_malloc_pci_pages(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr); +void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size); +void snd_free_pci_pages(struct pci_dev *pci, unsigned long size, void *ptr, dma_addr_t dma_addr); +#endif +int copy_to_user_fromio(void *dst, unsigned long src, size_t count); +int copy_from_user_toio(unsigned long dst, const void *src, size_t count); + +/* init.c */ + +extern int snd_cards_count; +extern snd_card_t *snd_cards[SNDRV_CARDS]; +extern rwlock_t snd_card_rwlock; +#ifdef CONFIG_SND_OSSEMUL +extern int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag); +#endif + +snd_card_t *snd_card_new(int idx, const char *id, + struct module *module, int extra_size); +int snd_card_free(snd_card_t *card); +int snd_card_register(snd_card_t *card); +int snd_card_info_init(void); +int snd_card_info_done(void); +int snd_component_add(snd_card_t *card, const char *component); + +/* device.c */ + +int snd_device_new(snd_card_t *card, snd_device_type_t type, + void *device_data, snd_device_ops_t *ops); +int snd_device_register(snd_card_t *card, void *device_data); +int snd_device_register_all(snd_card_t *card); +int snd_device_free(snd_card_t *card, void *device_data); +int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd); + +/* isadma.c */ + +#define DMA_MODE_NO_ENABLE 0x0100 + +void snd_dma_program(unsigned long dma, unsigned long addr, unsigned int size, unsigned short mode); +void snd_dma_disable(unsigned long dma); +unsigned int snd_dma_residue(unsigned long dma); + +/* misc.c */ + +int snd_task_name(struct task_struct *task, char *name, size_t size); + +/* --- */ + +#define snd_printk(format, args...) do { \ + printk("ALSA %s:%d: ", __FILE__, __LINE__); \ + printk(format, ##args); \ +} while (0) + +#ifdef CONFIG_SND_DEBUG + +#define __ASTRING__(x) #x + +#define snd_printd(format, args...) snd_printk(format, ##args) +#define snd_assert(expr, args...) do {\ + if (!(expr)) {\ + snd_printk("BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ + args;\ + }\ +} while (0) +#define snd_runtime_check(expr, args...) do {\ + if (!(expr)) {\ + snd_printk("ERROR (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ + args;\ + }\ +} while (0) + +#else /* !CONFIG_SND_DEBUG */ + +#define snd_printd(format, args...) /* nothing */ +#define snd_assert(expr, args...) /* nothing */ +#define snd_runtime_check(expr, args...) do { if (!(expr)) { args; } } while (0) + +#endif /* CONFIG_SND_DEBUG */ + +#ifdef CONFIG_SND_DEBUG_DETECT +#define snd_printdd(format, args...) snd_printk(format, ##args) +#else +#define snd_printdd(format, args...) /* nothing */ +#endif + +#define snd_BUG() snd_assert(0, ) + + +#define snd_timestamp_now(tstamp) do_gettimeofday(tstamp) +#define snd_timestamp_zero(tstamp) do { (tstamp)->tv_sec = 0; (tstamp)->tv_usec = 0; } while (0) +#define snd_timestamp_null(tstamp) ((tstamp)->tv_sec == 0 && (tstamp)->tv_usec ==0) + +#define SNDRV_OSS_VERSION ((3<<16)|(8<<8)|(1<<4)|(0)) /* 3.8.1a */ + +#endif /* __SOUND_CORE_H */ diff -Nru a/include/sound/cs4231.h b/include/sound/cs4231.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/cs4231.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,324 @@ +#ifndef __SOUND_CS4231_H +#define __SOUND_CS4231_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for CS4231 & InterWave chips & compatible chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "control.h" +#include "pcm.h" +#include "timer.h" + +/* IO ports */ + +#define CS4231P(chip, x) ((chip)->port + c_d_c_CS4231##x) + +#define c_d_c_CS4231REGSEL 0 +#define c_d_c_CS4231REG 1 +#define c_d_c_CS4231STATUS 2 +#define c_d_c_CS4231PIO 3 + +/* codec registers */ + +#define CS4231_LEFT_INPUT 0x00 /* left input control */ +#define CS4231_RIGHT_INPUT 0x01 /* right input control */ +#define CS4231_AUX1_LEFT_INPUT 0x02 /* left AUX1 input control */ +#define CS4231_AUX1_RIGHT_INPUT 0x03 /* right AUX1 input control */ +#define CS4231_AUX2_LEFT_INPUT 0x04 /* left AUX2 input control */ +#define CS4231_AUX2_RIGHT_INPUT 0x05 /* right AUX2 input control */ +#define CS4231_LEFT_OUTPUT 0x06 /* left output control register */ +#define CS4231_RIGHT_OUTPUT 0x07 /* right output control register */ +#define CS4231_PLAYBK_FORMAT 0x08 /* clock and data format - playback - bits 7-0 MCE */ +#define CS4231_IFACE_CTRL 0x09 /* interface control - bits 7-2 MCE */ +#define CS4231_PIN_CTRL 0x0a /* pin control */ +#define CS4231_TEST_INIT 0x0b /* test and initialization */ +#define CS4231_MISC_INFO 0x0c /* miscellaneaous information */ +#define CS4231_LOOPBACK 0x0d /* loopback control */ +#define CS4231_PLY_UPR_CNT 0x0e /* playback upper base count */ +#define CS4231_PLY_LWR_CNT 0x0f /* playback lower base count */ +#define CS4231_ALT_FEATURE_1 0x10 /* alternate #1 feature enable */ +#define CS4231_ALT_FEATURE_2 0x11 /* alternate #2 feature enable */ +#define CS4231_LEFT_LINE_IN 0x12 /* left line input control */ +#define CS4231_RIGHT_LINE_IN 0x13 /* right line input control */ +#define CS4231_TIMER_LOW 0x14 /* timer low byte */ +#define CS4231_TIMER_HIGH 0x15 /* timer high byte */ +#define CS4231_LEFT_MIC_INPUT 0x16 /* left MIC input control register (InterWave only) */ +#define CS4231_RIGHT_MIC_INPUT 0x17 /* right MIC input control register (InterWave only) */ +#define CS4236_EXT_REG 0x17 /* extended register access */ +#define CS4231_IRQ_STATUS 0x18 /* irq status register */ +#define CS4231_LINE_LEFT_OUTPUT 0x19 /* left line output control register (InterWave only) */ +#define CS4231_VERSION 0x19 /* CS4231(A) - version values */ +#define CS4231_MONO_CTRL 0x1a /* mono input/output control */ +#define CS4231_LINE_RIGHT_OUTPUT 0x1b /* right line output control register (InterWave only) */ +#define CS4235_LEFT_MASTER 0x1b /* left master output control */ +#define CS4231_REC_FORMAT 0x1c /* clock and data format - record - bits 7-0 MCE */ +#define CS4231_PLY_VAR_FREQ 0x1d /* playback variable frequency */ +#define CS4235_RIGHT_MASTER 0x1d /* right master output control */ +#define CS4231_REC_UPR_CNT 0x1e /* record upper count */ +#define CS4231_REC_LWR_CNT 0x1f /* record lower count */ + +/* definitions for codec register select port - CODECP( REGSEL ) */ + +#define CS4231_INIT 0x80 /* CODEC is initializing */ +#define CS4231_MCE 0x40 /* mode change enable */ +#define CS4231_TRD 0x20 /* transfer request disable */ + +/* definitions for codec status register - CODECP( STATUS ) */ + +#define CS4231_GLOBALIRQ 0x01 /* IRQ is active */ + +/* definitions for codec irq status */ + +#define CS4231_PLAYBACK_IRQ 0x10 +#define CS4231_RECORD_IRQ 0x20 +#define CS4231_TIMER_IRQ 0x40 +#define CS4231_ALL_IRQS 0x70 +#define CS4231_REC_UNDERRUN 0x08 +#define CS4231_REC_OVERRUN 0x04 +#define CS4231_PLY_OVERRUN 0x02 +#define CS4231_PLY_UNDERRUN 0x01 + +/* definitions for CS4231_LEFT_INPUT and CS4231_RIGHT_INPUT registers */ + +#define CS4231_ENABLE_MIC_GAIN 0x20 + +#define CS4231_MIXS_LINE 0x00 +#define CS4231_MIXS_AUX1 0x40 +#define CS4231_MIXS_MIC 0x80 +#define CS4231_MIXS_ALL 0xc0 + +/* definitions for clock and data format register - CS4231_PLAYBK_FORMAT */ + +#define CS4231_LINEAR_8 0x00 /* 8-bit unsigned data */ +#define CS4231_ALAW_8 0x60 /* 8-bit A-law companded */ +#define CS4231_ULAW_8 0x20 /* 8-bit U-law companded */ +#define CS4231_LINEAR_16 0x40 /* 16-bit twos complement data - little endian */ +#define CS4231_LINEAR_16_BIG 0xc0 /* 16-bit twos complement data - big endian */ +#define CS4231_ADPCM_16 0xa0 /* 16-bit ADPCM */ +#define CS4231_STEREO 0x10 /* stereo mode */ +/* bits 3-1 define frequency divisor */ +#define CS4231_XTAL1 0x00 /* 24.576 crystal */ +#define CS4231_XTAL2 0x01 /* 16.9344 crystal */ + +/* definitions for interface control register - CS4231_IFACE_CTRL */ + +#define CS4231_RECORD_PIO 0x80 /* record PIO enable */ +#define CS4231_PLAYBACK_PIO 0x40 /* playback PIO enable */ +#define CS4231_CALIB_MODE 0x18 /* calibration mode bits */ +#define CS4231_AUTOCALIB 0x08 /* auto calibrate */ +#define CS4231_SINGLE_DMA 0x04 /* use single DMA channel */ +#define CS4231_RECORD_ENABLE 0x02 /* record enable */ +#define CS4231_PLAYBACK_ENABLE 0x01 /* playback enable */ + +/* definitions for pin control register - CS4231_PIN_CTRL */ + +#define CS4231_IRQ_ENABLE 0x02 /* enable IRQ */ +#define CS4231_XCTL1 0x40 /* external control #1 */ +#define CS4231_XCTL0 0x80 /* external control #0 */ + +/* definitions for test and init register - CS4231_TEST_INIT */ + +#define CS4231_CALIB_IN_PROGRESS 0x20 /* auto calibrate in progress */ +#define CS4231_DMA_REQUEST 0x10 /* DMA request in progress */ + +/* definitions for misc control register - CS4231_MISC_INFO */ + +#define CS4231_MODE2 0x40 /* MODE 2 */ +#define CS4231_IW_MODE3 0x6c /* MODE 3 - InterWave enhanced mode */ +#define CS4231_4236_MODE3 0xe0 /* MODE 3 - CS4236+ enhanced mode */ + +/* definitions for alternate feature 1 register - CS4231_ALT_FEATURE_1 */ + +#define CS4231_DACZ 0x01 /* zero DAC when underrun */ +#define CS4231_TIMER_ENABLE 0x40 /* codec timer enable */ +#define CS4231_OLB 0x80 /* output level bit */ + +/* definitions for Extended Registers - CS4236+ */ + +#define CS4236_REG(i23val) (((i23val << 2) & 0x10) | ((i23val >> 4) & 0x0f)) +#define CS4236_I23VAL(reg) ((((reg)&0xf) << 4) | (((reg)&0x10) >> 2) | 0x8) + +#define CS4236_LEFT_LINE 0x08 /* left LINE alternate volume */ +#define CS4236_RIGHT_LINE 0x18 /* right LINE alternate volume */ +#define CS4236_LEFT_MIC 0x28 /* left MIC volume */ +#define CS4236_RIGHT_MIC 0x38 /* right MIC volume */ +#define CS4236_LEFT_MIX_CTRL 0x48 /* synthesis and left input mixer control */ +#define CS4236_RIGHT_MIX_CTRL 0x58 /* right input mixer control */ +#define CS4236_LEFT_FM 0x68 /* left FM volume */ +#define CS4236_RIGHT_FM 0x78 /* right FM volume */ +#define CS4236_LEFT_DSP 0x88 /* left DSP serial port volume */ +#define CS4236_RIGHT_DSP 0x98 /* right DSP serial port volume */ +#define CS4236_RIGHT_LOOPBACK 0xa8 /* right loopback monitor volume */ +#define CS4236_DAC_MUTE 0xb8 /* DAC mute and IFSE enable */ +#define CS4236_ADC_RATE 0xc8 /* indenpendent ADC sample frequency */ +#define CS4236_DAC_RATE 0xd8 /* indenpendent DAC sample frequency */ +#define CS4236_LEFT_MASTER 0xe8 /* left master digital audio volume */ +#define CS4236_RIGHT_MASTER 0xf8 /* right master digital audio volume */ +#define CS4236_LEFT_WAVE 0x0c /* left wavetable serial port volume */ +#define CS4236_RIGHT_WAVE 0x1c /* right wavetable serial port volume */ +#define CS4236_VERSION 0x9c /* chip version and ID */ + +/* defines for codec.mode */ + +#define CS4231_MODE_NONE 0x0000 +#define CS4231_MODE_PLAY 0x0001 +#define CS4231_MODE_RECORD 0x0002 +#define CS4231_MODE_TIMER 0x0004 +#define CS4231_MODE_OPEN (CS4231_MODE_PLAY|CS4231_MODE_RECORD|CS4231_MODE_TIMER) + +/* defines for codec.hardware */ + +#define CS4231_HW_DETECT 0x0000 /* let CS4231 driver detect chip */ +#define CS4231_HW_DETECT3 0x0001 /* allow mode 3 */ +#define CS4231_HW_TYPE_MASK 0xff00 /* type mask */ +#define CS4231_HW_CS4231_MASK 0x0100 /* CS4231 serie */ +#define CS4231_HW_CS4231 0x0100 /* CS4231 chip */ +#define CS4231_HW_CS4231A 0x0101 /* CS4231A chip */ +#define CS4231_HW_CS4232_MASK 0x0200 /* CS4232 serie (has control ports) */ +#define CS4231_HW_CS4232 0x0200 /* CS4232 */ +#define CS4231_HW_CS4232A 0x0201 /* CS4232A */ +#define CS4231_HW_CS4236 0x0202 /* CS4236 */ +#define CS4231_HW_CS4236B_MASK 0x0400 /* CS4236B serie (has extended control regs) */ +#define CS4231_HW_CS4235 0x0400 /* CS4235 - Crystal Clear (tm) stereo enhancement */ +#define CS4231_HW_CS4236B 0x0401 /* CS4236B */ +#define CS4231_HW_CS4237B 0x0402 /* CS4237B - SRS 3D */ +#define CS4231_HW_CS4238B 0x0403 /* CS4238B - QSOUND 3D */ +#define CS4231_HW_CS4239 0x0404 /* CS4239 - Crystal Clear (tm) stereo enhancement */ +/* compatible, but clones */ +#define CS4231_HW_INTERWAVE 0x1000 /* InterWave chip */ +#define CS4231_HW_OPL3SA2 0x1001 /* OPL3-SA2 chip */ + +/* defines for codec.hwshare */ +#define CS4231_HWSHARE_IRQ (1<<0) +#define CS4231_HWSHARE_DMA1 (1<<1) +#define CS4231_HWSHARE_DMA2 (1<<2) + +typedef struct _snd_cs4231 cs4231_t; + +struct _snd_cs4231 { + unsigned long port; /* base i/o port */ + struct resource *res_port; + unsigned long cport; /* control base i/o port (CS4236) */ + struct resource *res_cport; + int irq; /* IRQ line */ + int dma1; /* playback DMA */ + int dma2; /* record DMA */ + unsigned short version; /* version of CODEC chip */ + unsigned short mode; /* see to CS4231_MODE_XXXX */ + unsigned short hardware; /* see to CS4231_HW_XXXX */ + unsigned short hwshare; /* shared resources */ + unsigned short single_dma:1; /* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */ + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_timer_t *timer; + + unsigned char image[32]; /* registers image */ + unsigned char eimage[32]; /* extended registers image */ + unsigned char cimage[16]; /* control registers image */ + int mce_bit; + int calibrate_mute; + int sw_3d_bit; + unsigned int p_dma_size; + unsigned int c_dma_size; + + spinlock_t reg_lock; + struct semaphore mce_mutex; + struct semaphore open_mutex; + + int (*rate_constraint) (snd_pcm_runtime_t *runtime); + void (*set_playback_format) (cs4231_t *chip, snd_pcm_hw_params_t *hw_params, unsigned char pdfr); + void (*set_capture_format) (cs4231_t *chip, snd_pcm_hw_params_t *hw_params, unsigned char cdfr); +#ifdef CONFIG_PM + struct pm_dev *pm_dev; + void (*suspend) (cs4231_t *chip); + void (*resume) (cs4231_t *chip); +#endif + void *dma_private_data; + int (*claim_dma) (cs4231_t *chip, void *dma_private_data, int dma); + int (*release_dma) (cs4231_t *chip, void *dma_private_data, int dma); +}; + +/* exported functions */ + +void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char val); +unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg); +void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, unsigned char mask, unsigned char val); +void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val); +unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg); +void snd_cs4231_mce_up(cs4231_t *chip); +void snd_cs4231_mce_down(cs4231_t *chip); + +void snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +const char *snd_cs4231_chip_id(cs4231_t *chip); + +int snd_cs4231_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip); +int snd_cs4231_pcm(cs4231_t * chip, int device, snd_pcm_t **rpcm); +int snd_cs4231_timer(cs4231_t * chip, int device, snd_timer_t **rtimer); +int snd_cs4231_mixer(cs4231_t * chip); + +int snd_cs4236_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip); +int snd_cs4236_pcm(cs4231_t * chip, int device, snd_pcm_t **rpcm); +int snd_cs4236_mixer(cs4231_t * chip); + +/* + * mixer library + */ + +#define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4231_info_single, \ + get: snd_cs4231_get_single, put: snd_cs4231_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +#define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4231_info_double, \ + get: snd_cs4231_get_double, put: snd_cs4231_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +#ifdef CONFIG_SND_DEBUG +void snd_cs4231_debug(cs4231_t *chip); +#endif + +#endif /* __SOUND_CS4231_H */ diff -Nru a/include/sound/cs46xx.h b/include/sound/cs46xx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/cs46xx.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1720 @@ +#ifndef __SOUND_CS46XX_H +#define __SOUND_CS46XX_H + +/* + * Copyright (c) by Jaroslav Kysela , + * Cirrus Logic, Inc. + * Definitions for Cirrus Logic CS46xx chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include "ac97_codec.h" + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4610 +#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4612 +#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4615 +#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 +#endif + +/* + * Direct registers + */ + +/* + * The following define the offsets of the registers accessed via base address + * register zero on the CS46xx part. + */ +#define BA0_HISR 0x00000000 +#define BA0_HSR0 0x00000004 +#define BA0_HICR 0x00000008 +#define BA0_DMSR 0x00000100 +#define BA0_HSAR 0x00000110 +#define BA0_HDAR 0x00000114 +#define BA0_HDMR 0x00000118 +#define BA0_HDCR 0x0000011C +#define BA0_PFMC 0x00000200 +#define BA0_PFCV1 0x00000204 +#define BA0_PFCV2 0x00000208 +#define BA0_PCICFG00 0x00000300 +#define BA0_PCICFG04 0x00000304 +#define BA0_PCICFG08 0x00000308 +#define BA0_PCICFG0C 0x0000030C +#define BA0_PCICFG10 0x00000310 +#define BA0_PCICFG14 0x00000314 +#define BA0_PCICFG18 0x00000318 +#define BA0_PCICFG1C 0x0000031C +#define BA0_PCICFG20 0x00000320 +#define BA0_PCICFG24 0x00000324 +#define BA0_PCICFG28 0x00000328 +#define BA0_PCICFG2C 0x0000032C +#define BA0_PCICFG30 0x00000330 +#define BA0_PCICFG34 0x00000334 +#define BA0_PCICFG38 0x00000338 +#define BA0_PCICFG3C 0x0000033C +#define BA0_CLKCR1 0x00000400 +#define BA0_CLKCR2 0x00000404 +#define BA0_PLLM 0x00000408 +#define BA0_PLLCC 0x0000040C +#define BA0_FRR 0x00000410 +#define BA0_CFL1 0x00000414 +#define BA0_CFL2 0x00000418 +#define BA0_SERMC1 0x00000420 +#define BA0_SERMC2 0x00000424 +#define BA0_SERC1 0x00000428 +#define BA0_SERC2 0x0000042C +#define BA0_SERC3 0x00000430 +#define BA0_SERC4 0x00000434 +#define BA0_SERC5 0x00000438 +#define BA0_SERBSP 0x0000043C +#define BA0_SERBST 0x00000440 +#define BA0_SERBCM 0x00000444 +#define BA0_SERBAD 0x00000448 +#define BA0_SERBCF 0x0000044C +#define BA0_SERBWP 0x00000450 +#define BA0_SERBRP 0x00000454 +#ifndef NO_CS4612 +#define BA0_ASER_FADDR 0x00000458 +#endif +#define BA0_ACCTL 0x00000460 +#define BA0_ACSTS 0x00000464 +#define BA0_ACOSV 0x00000468 +#define BA0_ACCAD 0x0000046C +#define BA0_ACCDA 0x00000470 +#define BA0_ACISV 0x00000474 +#define BA0_ACSAD 0x00000478 +#define BA0_ACSDA 0x0000047C +#define BA0_JSPT 0x00000480 +#define BA0_JSCTL 0x00000484 +#define BA0_JSC1 0x00000488 +#define BA0_JSC2 0x0000048C +#define BA0_MIDCR 0x00000490 +#define BA0_MIDSR 0x00000494 +#define BA0_MIDWP 0x00000498 +#define BA0_MIDRP 0x0000049C +#define BA0_JSIO 0x000004A0 +#ifndef NO_CS4612 +#define BA0_ASER_MASTER 0x000004A4 +#endif +#define BA0_CFGI 0x000004B0 +#define BA0_SSVID 0x000004B4 +#define BA0_GPIOR 0x000004B8 +#ifndef NO_CS4612 +#define BA0_EGPIODR 0x000004BC +#define BA0_EGPIOPTR 0x000004C0 +#define BA0_EGPIOTR 0x000004C4 +#define BA0_EGPIOWR 0x000004C8 +#define BA0_EGPIOSR 0x000004CC +#define BA0_SERC6 0x000004D0 +#define BA0_SERC7 0x000004D4 +#define BA0_SERACC 0x000004D8 +#define BA0_ACCTL2 0x000004E0 +#define BA0_ACSTS2 0x000004E4 +#define BA0_ACOSV2 0x000004E8 +#define BA0_ACCAD2 0x000004EC +#define BA0_ACCDA2 0x000004F0 +#define BA0_ACISV2 0x000004F4 +#define BA0_ACSAD2 0x000004F8 +#define BA0_ACSDA2 0x000004FC +#define BA0_IOTAC0 0x00000500 +#define BA0_IOTAC1 0x00000504 +#define BA0_IOTAC2 0x00000508 +#define BA0_IOTAC3 0x0000050C +#define BA0_IOTAC4 0x00000510 +#define BA0_IOTAC5 0x00000514 +#define BA0_IOTAC6 0x00000518 +#define BA0_IOTAC7 0x0000051C +#define BA0_IOTAC8 0x00000520 +#define BA0_IOTAC9 0x00000524 +#define BA0_IOTAC10 0x00000528 +#define BA0_IOTAC11 0x0000052C +#define BA0_IOTFR0 0x00000540 +#define BA0_IOTFR1 0x00000544 +#define BA0_IOTFR2 0x00000548 +#define BA0_IOTFR3 0x0000054C +#define BA0_IOTFR4 0x00000550 +#define BA0_IOTFR5 0x00000554 +#define BA0_IOTFR6 0x00000558 +#define BA0_IOTFR7 0x0000055C +#define BA0_IOTFIFO 0x00000580 +#define BA0_IOTRRD 0x00000584 +#define BA0_IOTFP 0x00000588 +#define BA0_IOTCR 0x0000058C +#define BA0_DPCID 0x00000590 +#define BA0_DPCIA 0x00000594 +#define BA0_DPCIC 0x00000598 +#define BA0_PCPCIR 0x00000600 +#define BA0_PCPCIG 0x00000604 +#define BA0_PCPCIEN 0x00000608 +#define BA0_EPCIPMC 0x00000610 +#endif + +/* + * The following define the offsets of the registers and memories accessed via + * base address register one on the CS46xx part. + */ +#define BA1_SP_DMEM0 0x00000000 +#define BA1_SP_DMEM1 0x00010000 +#define BA1_SP_PMEM 0x00020000 +#define BA1_SP_REG 0x00030000 +#define BA1_SPCR 0x00030000 +#define BA1_DREG 0x00030004 +#define BA1_DSRWP 0x00030008 +#define BA1_TWPR 0x0003000C +#define BA1_SPWR 0x00030010 +#define BA1_SPIR 0x00030014 +#define BA1_FGR1 0x00030020 +#define BA1_SPCS 0x00030028 +#define BA1_SDSR 0x0003002C +#define BA1_FRMT 0x00030030 +#define BA1_FRCC 0x00030034 +#define BA1_FRSC 0x00030038 +#define BA1_OMNI_MEM 0x000E0000 + +/* + * The following defines are for the flags in the host interrupt status + * register. + */ +#define HISR_VC_MASK 0x0000FFFF +#define HISR_VC0 0x00000001 +#define HISR_VC1 0x00000002 +#define HISR_VC2 0x00000004 +#define HISR_VC3 0x00000008 +#define HISR_VC4 0x00000010 +#define HISR_VC5 0x00000020 +#define HISR_VC6 0x00000040 +#define HISR_VC7 0x00000080 +#define HISR_VC8 0x00000100 +#define HISR_VC9 0x00000200 +#define HISR_VC10 0x00000400 +#define HISR_VC11 0x00000800 +#define HISR_VC12 0x00001000 +#define HISR_VC13 0x00002000 +#define HISR_VC14 0x00004000 +#define HISR_VC15 0x00008000 +#define HISR_INT0 0x00010000 +#define HISR_INT1 0x00020000 +#define HISR_DMAI 0x00040000 +#define HISR_FROVR 0x00080000 +#define HISR_MIDI 0x00100000 +#ifdef NO_CS4612 +#define HISR_RESERVED 0x0FE00000 +#else +#define HISR_SBINT 0x00200000 +#define HISR_RESERVED 0x0FC00000 +#endif +#define HISR_H0P 0x40000000 +#define HISR_INTENA 0x80000000 + +/* + * The following defines are for the flags in the host signal register 0. + */ +#define HSR0_VC_MASK 0xFFFFFFFF +#define HSR0_VC16 0x00000001 +#define HSR0_VC17 0x00000002 +#define HSR0_VC18 0x00000004 +#define HSR0_VC19 0x00000008 +#define HSR0_VC20 0x00000010 +#define HSR0_VC21 0x00000020 +#define HSR0_VC22 0x00000040 +#define HSR0_VC23 0x00000080 +#define HSR0_VC24 0x00000100 +#define HSR0_VC25 0x00000200 +#define HSR0_VC26 0x00000400 +#define HSR0_VC27 0x00000800 +#define HSR0_VC28 0x00001000 +#define HSR0_VC29 0x00002000 +#define HSR0_VC30 0x00004000 +#define HSR0_VC31 0x00008000 +#define HSR0_VC32 0x00010000 +#define HSR0_VC33 0x00020000 +#define HSR0_VC34 0x00040000 +#define HSR0_VC35 0x00080000 +#define HSR0_VC36 0x00100000 +#define HSR0_VC37 0x00200000 +#define HSR0_VC38 0x00400000 +#define HSR0_VC39 0x00800000 +#define HSR0_VC40 0x01000000 +#define HSR0_VC41 0x02000000 +#define HSR0_VC42 0x04000000 +#define HSR0_VC43 0x08000000 +#define HSR0_VC44 0x10000000 +#define HSR0_VC45 0x20000000 +#define HSR0_VC46 0x40000000 +#define HSR0_VC47 0x80000000 + +/* + * The following defines are for the flags in the host interrupt control + * register. + */ +#define HICR_IEV 0x00000001 +#define HICR_CHGM 0x00000002 + +/* + * The following defines are for the flags in the DMA status register. + */ +#define DMSR_HP 0x00000001 +#define DMSR_HR 0x00000002 +#define DMSR_SP 0x00000004 +#define DMSR_SR 0x00000008 + +/* + * The following defines are for the flags in the host DMA source address + * register. + */ +#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HSAR_DSP_ADDR_MASK 0x0000FFFF +#define HSAR_MEMID_MASK 0x000F0000 +#define HSAR_MEMID_SP_DMEM0 0x00000000 +#define HSAR_MEMID_SP_DMEM1 0x00010000 +#define HSAR_MEMID_SP_PMEM 0x00020000 +#define HSAR_MEMID_SP_DEBUG 0x00030000 +#define HSAR_MEMID_OMNI_MEM 0x000E0000 +#define HSAR_END 0x40000000 +#define HSAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA destination address + * register. + */ +#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HDAR_DSP_ADDR_MASK 0x0000FFFF +#define HDAR_MEMID_MASK 0x000F0000 +#define HDAR_MEMID_SP_DMEM0 0x00000000 +#define HDAR_MEMID_SP_DMEM1 0x00010000 +#define HDAR_MEMID_SP_PMEM 0x00020000 +#define HDAR_MEMID_SP_DEBUG 0x00030000 +#define HDAR_MEMID_OMNI_MEM 0x000E0000 +#define HDAR_END 0x40000000 +#define HDAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDMR_AC_MASK 0x0000F000 +#define HDMR_AC_8_16 0x00001000 +#define HDMR_AC_M_S 0x00002000 +#define HDMR_AC_B_L 0x00004000 +#define HDMR_AC_S_U 0x00008000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDCR_COUNT_MASK 0x000003FF +#define HDCR_DONE 0x00004000 +#define HDCR_OPT 0x00008000 +#define HDCR_WBD 0x00400000 +#define HDCR_WBS 0x00800000 +#define HDCR_DMS_MASK 0x07000000 +#define HDCR_DMS_LINEAR 0x00000000 +#define HDCR_DMS_16_DWORDS 0x01000000 +#define HDCR_DMS_32_DWORDS 0x02000000 +#define HDCR_DMS_64_DWORDS 0x03000000 +#define HDCR_DMS_128_DWORDS 0x04000000 +#define HDCR_DMS_256_DWORDS 0x05000000 +#define HDCR_DMS_512_DWORDS 0x06000000 +#define HDCR_DMS_1024_DWORDS 0x07000000 +#define HDCR_DH 0x08000000 +#define HDCR_SMS_MASK 0x70000000 +#define HDCR_SMS_LINEAR 0x00000000 +#define HDCR_SMS_16_DWORDS 0x10000000 +#define HDCR_SMS_32_DWORDS 0x20000000 +#define HDCR_SMS_64_DWORDS 0x30000000 +#define HDCR_SMS_128_DWORDS 0x40000000 +#define HDCR_SMS_256_DWORDS 0x50000000 +#define HDCR_SMS_512_DWORDS 0x60000000 +#define HDCR_SMS_1024_DWORDS 0x70000000 +#define HDCR_SH 0x80000000 +#define HDCR_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the performance monitor control + * register. + */ +#define PFMC_C1SS_MASK 0x0000001F +#define PFMC_C1EV 0x00000020 +#define PFMC_C1RS 0x00008000 +#define PFMC_C2SS_MASK 0x001F0000 +#define PFMC_C2EV 0x00200000 +#define PFMC_C2RS 0x80000000 +#define PFMC_C1SS_SHIFT 0 +#define PFMC_C2SS_SHIFT 16 +#define PFMC_BUS_GRANT 0 +#define PFMC_GRANT_AFTER_REQ 1 +#define PFMC_TRANSACTION 2 +#define PFMC_DWORD_TRANSFER 3 +#define PFMC_SLAVE_READ 4 +#define PFMC_SLAVE_WRITE 5 +#define PFMC_PREEMPTION 6 +#define PFMC_DISCONNECT_RETRY 7 +#define PFMC_INTERRUPT 8 +#define PFMC_BUS_OWNERSHIP 9 +#define PFMC_TRANSACTION_LAG 10 +#define PFMC_PCI_CLOCK 11 +#define PFMC_SERIAL_CLOCK 12 +#define PFMC_SP_CLOCK 13 + +/* + * The following defines are for the flags in the performance counter value 1 + * register. + */ +#define PFCV1_PC1V_MASK 0xFFFFFFFF +#define PFCV1_PC1V_SHIFT 0 + +/* + * The following defines are for the flags in the performance counter value 2 + * register. + */ +#define PFCV2_PC2V_MASK 0xFFFFFFFF +#define PFCV2_PC2V_SHIFT 0 + +/* + * The following defines are for the flags in the clock control register 1. + */ +#define CLKCR1_OSCS 0x00000001 +#define CLKCR1_OSCP 0x00000002 +#define CLKCR1_PLLSS_MASK 0x0000000C +#define CLKCR1_PLLSS_SERIAL 0x00000000 +#define CLKCR1_PLLSS_CRYSTAL 0x00000004 +#define CLKCR1_PLLSS_PCI 0x00000008 +#define CLKCR1_PLLSS_RESERVED 0x0000000C +#define CLKCR1_PLLP 0x00000010 +#define CLKCR1_SWCE 0x00000020 +#define CLKCR1_PLLOS 0x00000040 + +/* + * The following defines are for the flags in the clock control register 2. + */ +#define CLKCR2_PDIVS_MASK 0x0000000F +#define CLKCR2_PDIVS_1 0x00000001 +#define CLKCR2_PDIVS_2 0x00000002 +#define CLKCR2_PDIVS_4 0x00000004 +#define CLKCR2_PDIVS_7 0x00000007 +#define CLKCR2_PDIVS_8 0x00000008 +#define CLKCR2_PDIVS_16 0x00000000 + +/* + * The following defines are for the flags in the PLL multiplier register. + */ +#define PLLM_MASK 0x000000FF +#define PLLM_SHIFT 0 + +/* + * The following defines are for the flags in the PLL capacitor coefficient + * register. + */ +#define PLLCC_CDR_MASK 0x00000007 +#ifndef NO_CS4610 +#define PLLCC_CDR_240_350_MHZ 0x00000000 +#define PLLCC_CDR_184_265_MHZ 0x00000001 +#define PLLCC_CDR_144_205_MHZ 0x00000002 +#define PLLCC_CDR_111_160_MHZ 0x00000003 +#define PLLCC_CDR_87_123_MHZ 0x00000004 +#define PLLCC_CDR_67_96_MHZ 0x00000005 +#define PLLCC_CDR_52_74_MHZ 0x00000006 +#define PLLCC_CDR_45_58_MHZ 0x00000007 +#endif +#ifndef NO_CS4612 +#define PLLCC_CDR_271_398_MHZ 0x00000000 +#define PLLCC_CDR_227_330_MHZ 0x00000001 +#define PLLCC_CDR_167_239_MHZ 0x00000002 +#define PLLCC_CDR_150_215_MHZ 0x00000003 +#define PLLCC_CDR_107_154_MHZ 0x00000004 +#define PLLCC_CDR_98_140_MHZ 0x00000005 +#define PLLCC_CDR_73_104_MHZ 0x00000006 +#define PLLCC_CDR_63_90_MHZ 0x00000007 +#endif +#define PLLCC_LPF_MASK 0x000000F8 +#ifndef NO_CS4610 +#define PLLCC_LPF_23850_60000_KHZ 0x00000000 +#define PLLCC_LPF_7960_26290_KHZ 0x00000008 +#define PLLCC_LPF_4160_10980_KHZ 0x00000018 +#define PLLCC_LPF_1740_4580_KHZ 0x00000038 +#define PLLCC_LPF_724_1910_KHZ 0x00000078 +#define PLLCC_LPF_317_798_KHZ 0x000000F8 +#endif +#ifndef NO_CS4612 +#define PLLCC_LPF_25580_64530_KHZ 0x00000000 +#define PLLCC_LPF_14360_37270_KHZ 0x00000008 +#define PLLCC_LPF_6100_16020_KHZ 0x00000018 +#define PLLCC_LPF_2540_6690_KHZ 0x00000038 +#define PLLCC_LPF_1050_2780_KHZ 0x00000078 +#define PLLCC_LPF_450_1160_KHZ 0x000000F8 +#endif + +/* + * The following defines are for the flags in the feature reporting register. + */ +#define FRR_FAB_MASK 0x00000003 +#define FRR_MASK_MASK 0x0000001C +#ifdef NO_CS4612 +#define FRR_CFOP_MASK 0x000000E0 +#else +#define FRR_CFOP_MASK 0x00000FE0 +#endif +#define FRR_CFOP_NOT_DVD 0x00000020 +#define FRR_CFOP_A3D 0x00000040 +#define FRR_CFOP_128_PIN 0x00000080 +#ifndef NO_CS4612 +#define FRR_CFOP_CS4280 0x00000800 +#endif +#define FRR_FAB_SHIFT 0 +#define FRR_MASK_SHIFT 2 +#define FRR_CFOP_SHIFT 5 + +/* + * The following defines are for the flags in the configuration load 1 + * register. + */ +#define CFL1_CLOCK_SOURCE_MASK 0x00000003 +#define CFL1_CLOCK_SOURCE_CS423X 0x00000000 +#define CFL1_CLOCK_SOURCE_AC97 0x00000001 +#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002 +#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003 +#define CFL1_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the configuration load 2 + * register. + */ +#define CFL2_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the serial port master control + * register 1. + */ +#define SERMC1_MSPE 0x00000001 +#define SERMC1_PTC_MASK 0x0000000E +#define SERMC1_PTC_CS423X 0x00000000 +#define SERMC1_PTC_AC97 0x00000002 +#define SERMC1_PTC_DAC 0x00000004 +#define SERMC1_PLB 0x00000010 +#define SERMC1_XLB 0x00000020 + +/* + * The following defines are for the flags in the serial port master control + * register 2. + */ +#define SERMC2_LROE 0x00000001 +#define SERMC2_MCOE 0x00000002 +#define SERMC2_MCDIV 0x00000004 + +/* + * The following defines are for the flags in the serial port 1 configuration + * register. + */ +#define SERC1_SO1EN 0x00000001 +#define SERC1_SO1F_MASK 0x0000000E +#define SERC1_SO1F_CS423X 0x00000000 +#define SERC1_SO1F_AC97 0x00000002 +#define SERC1_SO1F_DAC 0x00000004 +#define SERC1_SO1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 2 configuration + * register. + */ +#define SERC2_SI1EN 0x00000001 +#define SERC2_SI1F_MASK 0x0000000E +#define SERC2_SI1F_CS423X 0x00000000 +#define SERC2_SI1F_AC97 0x00000002 +#define SERC2_SI1F_ADC 0x00000004 +#define SERC2_SI1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 3 configuration + * register. + */ +#define SERC3_SO2EN 0x00000001 +#define SERC3_SO2F_MASK 0x00000006 +#define SERC3_SO2F_DAC 0x00000000 +#define SERC3_SO2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 4 configuration + * register. + */ +#define SERC4_SO3EN 0x00000001 +#define SERC4_SO3F_MASK 0x00000006 +#define SERC4_SO3F_DAC 0x00000000 +#define SERC4_SO3F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 5 configuration + * register. + */ +#define SERC5_SI2EN 0x00000001 +#define SERC5_SI2F_MASK 0x00000006 +#define SERC5_SI2F_ADC 0x00000000 +#define SERC5_SI2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor sample + * pointer register. + */ +#define SERBSP_FSP_MASK 0x0000000F +#define SERBSP_FSP_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor status + * register. + */ +#define SERBST_RRDY 0x00000001 +#define SERBST_WBSY 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor command + * register. + */ +#define SERBCM_RDC 0x00000001 +#define SERBCM_WRC 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor address + * register. + */ +#ifdef NO_CS4612 +#define SERBAD_FAD_MASK 0x000000FF +#else +#define SERBAD_FAD_MASK 0x000001FF +#endif +#define SERBAD_FAD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor + * configuration register. + */ +#define SERBCF_HBP 0x00000001 + +/* + * The following defines are for the flags in the serial port backdoor write + * port register. + */ +#define SERBWP_FWD_MASK 0x000FFFFF +#define SERBWP_FWD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor read + * port register. + */ +#define SERBRP_FRD_MASK 0x000FFFFF +#define SERBRP_FRD_SHIFT 0 + +/* + * The following defines are for the flags in the async FIFO address register. + */ +#ifndef NO_CS4612 +#define ASER_FADDR_A1_MASK 0x000001FF +#define ASER_FADDR_EN1 0x00008000 +#define ASER_FADDR_A2_MASK 0x01FF0000 +#define ASER_FADDR_EN2 0x80000000 +#define ASER_FADDR_A1_SHIFT 0 +#define ASER_FADDR_A2_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the AC97 control register. + */ +#define ACCTL_RSTN 0x00000001 +#define ACCTL_ESYN 0x00000002 +#define ACCTL_VFRM 0x00000004 +#define ACCTL_DCV 0x00000008 +#define ACCTL_CRW 0x00000010 +#define ACCTL_ASYN 0x00000020 +#ifndef NO_CS4612 +#define ACCTL_TC 0x00000040 +#endif + +/* + * The following defines are for the flags in the AC97 status register. + */ +#define ACSTS_CRDY 0x00000001 +#define ACSTS_VSTS 0x00000002 +#ifndef NO_CS4612 +#define ACSTS_WKUP 0x00000004 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register. + */ +#define ACOSV_SLV3 0x00000001 +#define ACOSV_SLV4 0x00000002 +#define ACOSV_SLV5 0x00000004 +#define ACOSV_SLV6 0x00000008 +#define ACOSV_SLV7 0x00000010 +#define ACOSV_SLV8 0x00000020 +#define ACOSV_SLV9 0x00000040 +#define ACOSV_SLV10 0x00000080 +#define ACOSV_SLV11 0x00000100 +#define ACOSV_SLV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 command address + * register. + */ +#define ACCAD_CI_MASK 0x0000007F +#define ACCAD_CI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 command data register. + */ +#define ACCDA_CD_MASK 0x0000FFFF +#define ACCDA_CD_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 input slot valid + * register. + */ +#define ACISV_ISV3 0x00000001 +#define ACISV_ISV4 0x00000002 +#define ACISV_ISV5 0x00000004 +#define ACISV_ISV6 0x00000008 +#define ACISV_ISV7 0x00000010 +#define ACISV_ISV8 0x00000020 +#define ACISV_ISV9 0x00000040 +#define ACISV_ISV10 0x00000080 +#define ACISV_ISV11 0x00000100 +#define ACISV_ISV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 status address + * register. + */ +#define ACSAD_SI_MASK 0x0000007F +#define ACSAD_SI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 status data register. + */ +#define ACSDA_SD_MASK 0x0000FFFF +#define ACSDA_SD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick poll/trigger + * register. + */ +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* + * The following defines are for the flags in the joystick control register. + */ +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* + * The following defines are for the flags in the joystick coordinate pair 1 + * readback register. + */ +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 + +/* + * The following defines are for the flags in the joystick coordinate pair 2 + * readback register. + */ +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* + * The following defines are for the flags in the MIDI control register. + */ +#define MIDCR_TXE 0x00000001 /* Enable transmitting. */ +#define MIDCR_RXE 0x00000002 /* Enable receiving. */ +#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */ +#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */ +#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */ +#define MIDCR_MRST 0x00000020 /* Reset interface. */ + +/* + * The following defines are for the flags in the MIDI status register. + */ +#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */ +#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */ + +/* + * The following defines are for the flags in the MIDI write port register. + */ +#define MIDWP_MWD_MASK 0x000000FF +#define MIDWP_MWD_SHIFT 0 + +/* + * The following defines are for the flags in the MIDI read port register. + */ +#define MIDRP_MRD_MASK 0x000000FF +#define MIDRP_MRD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick GPIO register. + */ +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + * The following defines are for the flags in the master async/sync serial + * port enable register. + */ +#ifndef NO_CS4612 +#define ASER_MASTER_ME 0x00000001 +#endif + +/* + * The following defines are for the flags in the configuration interface + * register. + */ +#define CFGI_CLK 0x00000001 +#define CFGI_DOUT 0x00000002 +#define CFGI_DIN_EEN 0x00000004 +#define CFGI_EELD 0x00000008 + +/* + * The following defines are for the flags in the subsystem ID and vendor ID + * register. + */ +#define SSVID_VID_MASK 0x0000FFFF +#define SSVID_SID_MASK 0xFFFF0000 +#define SSVID_VID_SHIFT 0 +#define SSVID_SID_SHIFT 16 + +/* + * The following defines are for the flags in the GPIO pin interface register. + */ +#define GPIOR_VOLDN 0x00000001 +#define GPIOR_VOLUP 0x00000002 +#define GPIOR_SI2D 0x00000004 +#define GPIOR_SI2OE 0x00000008 + +/* + * The following defines are for the flags in the extended GPIO pin direction + * register. + */ +#ifndef NO_CS4612 +#define EGPIODR_GPOE0 0x00000001 +#define EGPIODR_GPOE1 0x00000002 +#define EGPIODR_GPOE2 0x00000004 +#define EGPIODR_GPOE3 0x00000008 +#define EGPIODR_GPOE4 0x00000010 +#define EGPIODR_GPOE5 0x00000020 +#define EGPIODR_GPOE6 0x00000040 +#define EGPIODR_GPOE7 0x00000080 +#define EGPIODR_GPOE8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin polarity/ + * type register. + */ +#ifndef NO_CS4612 +#define EGPIOPTR_GPPT0 0x00000001 +#define EGPIOPTR_GPPT1 0x00000002 +#define EGPIOPTR_GPPT2 0x00000004 +#define EGPIOPTR_GPPT3 0x00000008 +#define EGPIOPTR_GPPT4 0x00000010 +#define EGPIOPTR_GPPT5 0x00000020 +#define EGPIOPTR_GPPT6 0x00000040 +#define EGPIOPTR_GPPT7 0x00000080 +#define EGPIOPTR_GPPT8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin sticky + * register. + */ +#ifndef NO_CS4612 +#define EGPIOTR_GPS0 0x00000001 +#define EGPIOTR_GPS1 0x00000002 +#define EGPIOTR_GPS2 0x00000004 +#define EGPIOTR_GPS3 0x00000008 +#define EGPIOTR_GPS4 0x00000010 +#define EGPIOTR_GPS5 0x00000020 +#define EGPIOTR_GPS6 0x00000040 +#define EGPIOTR_GPS7 0x00000080 +#define EGPIOTR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO ping wakeup + * register. + */ +#ifndef NO_CS4612 +#define EGPIOWR_GPW0 0x00000001 +#define EGPIOWR_GPW1 0x00000002 +#define EGPIOWR_GPW2 0x00000004 +#define EGPIOWR_GPW3 0x00000008 +#define EGPIOWR_GPW4 0x00000010 +#define EGPIOWR_GPW5 0x00000020 +#define EGPIOWR_GPW6 0x00000040 +#define EGPIOWR_GPW7 0x00000080 +#define EGPIOWR_GPW8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin status + * register. + */ +#ifndef NO_CS4612 +#define EGPIOSR_GPS0 0x00000001 +#define EGPIOSR_GPS1 0x00000002 +#define EGPIOSR_GPS2 0x00000004 +#define EGPIOSR_GPS3 0x00000008 +#define EGPIOSR_GPS4 0x00000010 +#define EGPIOSR_GPS5 0x00000020 +#define EGPIOSR_GPS6 0x00000040 +#define EGPIOSR_GPS7 0x00000080 +#define EGPIOSR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the serial port 6 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC6_ASDO2EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the serial port 7 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC7_ASDI2EN 0x00000001 +#define SERC7_POSILB 0x00000002 +#define SERC7_SIPOLB 0x00000004 +#define SERC7_SOSILB 0x00000008 +#define SERC7_SISOLB 0x00000010 +#endif + +/* + * The following defines are for the flags in the serial port AC link + * configuration register. + */ +#ifndef NO_CS4612 +#define SERACC_CHIP_TYPE_MASK 0x00000001 +#define SERACC_CHIP_TYPE_1_03 0x00000000 +#define SERACC_CHIP_TYPE_2_0 0x00000001 +#define SERACC_TWO_CODECS 0x00000002 +#define SERACC_MDM 0x00000004 +#define SERACC_HSP 0x00000008 +#endif + +/* + * The following defines are for the flags in the AC97 control register 2. + */ +#ifndef NO_CS4612 +#define ACCTL2_RSTN 0x00000001 +#define ACCTL2_ESYN 0x00000002 +#define ACCTL2_VFRM 0x00000004 +#define ACCTL2_DCV 0x00000008 +#define ACCTL2_CRW 0x00000010 +#define ACCTL2_ASYN 0x00000020 +#endif + +/* + * The following defines are for the flags in the AC97 status register 2. + */ +#ifndef NO_CS4612 +#define ACSTS2_CRDY 0x00000001 +#define ACSTS2_VSTS 0x00000002 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACOSV2_SLV3 0x00000001 +#define ACOSV2_SLV4 0x00000002 +#define ACOSV2_SLV5 0x00000004 +#define ACOSV2_SLV6 0x00000008 +#define ACOSV2_SLV7 0x00000010 +#define ACOSV2_SLV8 0x00000020 +#define ACOSV2_SLV9 0x00000040 +#define ACOSV2_SLV10 0x00000080 +#define ACOSV2_SLV11 0x00000100 +#define ACOSV2_SLV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 command address + * register 2. + */ +#ifndef NO_CS4612 +#define ACCAD2_CI_MASK 0x0000007F +#define ACCAD2_CI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 command data register + * 2. + */ +#ifndef NO_CS4612 +#define ACCDA2_CD_MASK 0x0000FFFF +#define ACCDA2_CD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 input slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACISV2_ISV3 0x00000001 +#define ACISV2_ISV4 0x00000002 +#define ACISV2_ISV5 0x00000004 +#define ACISV2_ISV6 0x00000008 +#define ACISV2_ISV7 0x00000010 +#define ACISV2_ISV8 0x00000020 +#define ACISV2_ISV9 0x00000040 +#define ACISV2_ISV10 0x00000080 +#define ACISV2_ISV11 0x00000100 +#define ACISV2_ISV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 status address + * register 2. + */ +#ifndef NO_CS4612 +#define ACSAD2_SI_MASK 0x0000007F +#define ACSAD2_SI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 status data register 2. + */ +#ifndef NO_CS4612 +#define ACSDA2_SD_MASK 0x0000FFFF +#define ACSDA2_SD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap address and control + * registers (all 12). + */ +#ifndef NO_CS4612 +#define IOTAC_SA_MASK 0x0000FFFF +#define IOTAC_MSK_MASK 0x000F0000 +#define IOTAC_IODC_MASK 0x06000000 +#define IOTAC_IODC_16_BIT 0x00000000 +#define IOTAC_IODC_10_BIT 0x02000000 +#define IOTAC_IODC_12_BIT 0x04000000 +#define IOTAC_WSPI 0x08000000 +#define IOTAC_RSPI 0x10000000 +#define IOTAC_WSE 0x20000000 +#define IOTAC_WE 0x40000000 +#define IOTAC_RE 0x80000000 +#define IOTAC_SA_SHIFT 0 +#define IOTAC_MSK_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap fast read registers + * (all 8). + */ +#ifndef NO_CS4612 +#define IOTFR_D_MASK 0x0000FFFF +#define IOTFR_A_MASK 0x000F0000 +#define IOTFR_R_MASK 0x0F000000 +#define IOTFR_ALL 0x40000000 +#define IOTFR_VL 0x80000000 +#define IOTFR_D_SHIFT 0 +#define IOTFR_A_SHIFT 16 +#define IOTFR_R_SHIFT 24 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO register. + */ +#ifndef NO_CS4612 +#define IOTFIFO_BA_MASK 0x00003FFF +#define IOTFIFO_S_MASK 0x00FF0000 +#define IOTFIFO_OF 0x40000000 +#define IOTFIFO_SPIOF 0x80000000 +#define IOTFIFO_BA_SHIFT 0 +#define IOTFIFO_S_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap retry read data + * register. + */ +#ifndef NO_CS4612 +#define IOTRRD_D_MASK 0x0000FFFF +#define IOTRRD_RDV 0x80000000 +#define IOTRRD_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO pointer + * register. + */ +#ifndef NO_CS4612 +#define IOTFP_CA_MASK 0x00003FFF +#define IOTFP_PA_MASK 0x3FFF0000 +#define IOTFP_CA_SHIFT 0 +#define IOTFP_PA_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap control register. + */ +#ifndef NO_CS4612 +#define IOTCR_ITD 0x00000001 +#define IOTCR_HRV 0x00000002 +#define IOTCR_SRV 0x00000004 +#define IOTCR_DTI 0x00000008 +#define IOTCR_DFI 0x00000010 +#define IOTCR_DDP 0x00000020 +#define IOTCR_JTE 0x00000040 +#define IOTCR_PPE 0x00000080 +#endif + +/* + * The following defines are for the flags in the direct PCI data register. + */ +#ifndef NO_CS4612 +#define DPCID_D_MASK 0xFFFFFFFF +#define DPCID_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI address register. + */ +#ifndef NO_CS4612 +#define DPCIA_A_MASK 0xFFFFFFFF +#define DPCIA_A_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI command register. + */ +#ifndef NO_CS4612 +#define DPCIC_C_MASK 0x0000000F +#define DPCIC_C_IOREAD 0x00000002 +#define DPCIC_C_IOWRITE 0x00000003 +#define DPCIC_BE_MASK 0x000000F0 +#endif + +/* + * The following defines are for the flags in the PC/PCI request register. + */ +#ifndef NO_CS4612 +#define PCPCIR_RDC_MASK 0x00000007 +#define PCPCIR_C_MASK 0x00007000 +#define PCPCIR_REQ 0x00008000 +#define PCPCIR_RDC_SHIFT 0 +#define PCPCIR_C_SHIFT 12 +#endif + +/* + * The following defines are for the flags in the PC/PCI grant register. + */ +#ifndef NO_CS4612 +#define PCPCIG_GDC_MASK 0x00000007 +#define PCPCIG_VL 0x00008000 +#define PCPCIG_GDC_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the PC/PCI master enable + * register. + */ +#ifndef NO_CS4612 +#define PCPCIEN_EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the extended PCI power + * management control register. + */ +#ifndef NO_CS4612 +#define EPCIPMC_GWU 0x00000001 +#define EPCIPMC_FSPC 0x00000002 +#endif + +/* + * The following defines are for the flags in the SP control register. + */ +#define SPCR_RUN 0x00000001 +#define SPCR_STPFR 0x00000002 +#define SPCR_RUNFR 0x00000004 +#define SPCR_TICK 0x00000008 +#define SPCR_DRQEN 0x00000020 +#define SPCR_RSTSP 0x00000040 +#define SPCR_OREN 0x00000080 +#ifndef NO_CS4612 +#define SPCR_PCIINT 0x00000100 +#define SPCR_OINTD 0x00000200 +#define SPCR_CRE 0x00008000 +#endif + +/* + * The following defines are for the flags in the debug index register. + */ +#define DREG_REGID_MASK 0x0000007F +#define DREG_DEBUG 0x00000080 +#define DREG_RGBK_MASK 0x00000700 +#define DREG_TRAP 0x00000800 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000 +#endif +#endif +#define DREG_REGID_SHIFT 0 +#define DREG_RGBK_SHIFT 8 +#define DREG_RGBK_REGID_MASK 0x0000077F +#define DREG_REGID_R0 0x00000010 +#define DREG_REGID_R1 0x00000011 +#define DREG_REGID_R2 0x00000012 +#define DREG_REGID_R3 0x00000013 +#define DREG_REGID_R4 0x00000014 +#define DREG_REGID_R5 0x00000015 +#define DREG_REGID_R6 0x00000016 +#define DREG_REGID_R7 0x00000017 +#define DREG_REGID_R8 0x00000018 +#define DREG_REGID_R9 0x00000019 +#define DREG_REGID_RA 0x0000001A +#define DREG_REGID_RB 0x0000001B +#define DREG_REGID_RC 0x0000001C +#define DREG_REGID_RD 0x0000001D +#define DREG_REGID_RE 0x0000001E +#define DREG_REGID_RF 0x0000001F +#define DREG_REGID_RA_BUS_LOW 0x00000020 +#define DREG_REGID_RA_BUS_HIGH 0x00000038 +#define DREG_REGID_YBUS_LOW 0x00000050 +#define DREG_REGID_YBUS_HIGH 0x00000058 +#define DREG_REGID_TRAP_0 0x00000100 +#define DREG_REGID_TRAP_1 0x00000101 +#define DREG_REGID_TRAP_2 0x00000102 +#define DREG_REGID_TRAP_3 0x00000103 +#define DREG_REGID_TRAP_4 0x00000104 +#define DREG_REGID_TRAP_5 0x00000105 +#define DREG_REGID_TRAP_6 0x00000106 +#define DREG_REGID_TRAP_7 0x00000107 +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E +#define DREG_REGID_TOP_OF_STACK 0x0000010F +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110 +#define DREG_REGID_TRAP_9 0x00000111 +#define DREG_REGID_TRAP_10 0x00000112 +#define DREG_REGID_TRAP_11 0x00000113 +#define DREG_REGID_TRAP_12 0x00000114 +#define DREG_REGID_TRAP_13 0x00000115 +#define DREG_REGID_TRAP_14 0x00000116 +#define DREG_REGID_TRAP_15 0x00000117 +#define DREG_REGID_TRAP_16 0x00000118 +#define DREG_REGID_TRAP_17 0x00000119 +#define DREG_REGID_TRAP_18 0x0000011A +#define DREG_REGID_TRAP_19 0x0000011B +#define DREG_REGID_TRAP_20 0x0000011C +#define DREG_REGID_TRAP_21 0x0000011D +#define DREG_REGID_TRAP_22 0x0000011E +#define DREG_REGID_TRAP_23 0x0000011F +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200 +#define DREG_REGID_RSA0_HIGH 0x00000201 +#define DREG_REGID_RSA1_LOW 0x00000202 +#define DREG_REGID_RSA1_HIGH 0x00000203 +#define DREG_REGID_RSA2 0x00000204 +#define DREG_REGID_RSA3 0x00000205 +#define DREG_REGID_RSI0_LOW 0x00000206 +#define DREG_REGID_RSI0_HIGH 0x00000207 +#define DREG_REGID_RSI1 0x00000208 +#define DREG_REGID_RSI2 0x00000209 +#define DREG_REGID_SAGUSTATUS 0x0000020A +#define DREG_REGID_RSCONFIG01_LOW 0x0000020B +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C +#define DREG_REGID_RSCONFIG23_LOW 0x0000020D +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E +#define DREG_REGID_RSDMA01E 0x0000020F +#define DREG_REGID_RSDMA23E 0x00000210 +#define DREG_REGID_RSD0_LOW 0x00000211 +#define DREG_REGID_RSD0_HIGH 0x00000212 +#define DREG_REGID_RSD1_LOW 0x00000213 +#define DREG_REGID_RSD1_HIGH 0x00000214 +#define DREG_REGID_RSD2_LOW 0x00000215 +#define DREG_REGID_RSD2_HIGH 0x00000216 +#define DREG_REGID_RSD3_LOW 0x00000217 +#define DREG_REGID_RSD3_HIGH 0x00000218 +#define DREG_REGID_SRAR_HIGH 0x0000021A +#define DREG_REGID_SRAR_LOW 0x0000021B +#define DREG_REGID_DMA_STATE 0x0000021C +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E +#define DREG_REGID_CPU_STATUS 0x00000300 +#define DREG_REGID_MAC_MODE 0x00000301 +#define DREG_REGID_STACK_AND_REPEAT 0x00000302 +#define DREG_REGID_INDEX0 0x00000304 +#define DREG_REGID_INDEX1 0x00000305 +#define DREG_REGID_DMA_STATE_0_3 0x00000400 +#define DREG_REGID_DMA_STATE_4_7 0x00000404 +#define DREG_REGID_DMA_STATE_8_11 0x00000408 +#define DREG_REGID_DMA_STATE_12_15 0x0000040C +#define DREG_REGID_DMA_STATE_16_19 0x00000410 +#define DREG_REGID_DMA_STATE_20_23 0x00000414 +#define DREG_REGID_DMA_STATE_24_27 0x00000418 +#define DREG_REGID_DMA_STATE_28_31 0x0000041C +#define DREG_REGID_DMA_STATE_32_35 0x00000420 +#define DREG_REGID_DMA_STATE_36_39 0x00000424 +#define DREG_REGID_DMA_STATE_40_43 0x00000428 +#define DREG_REGID_DMA_STATE_44_47 0x0000042C +#define DREG_REGID_DMA_STATE_48_51 0x00000430 +#define DREG_REGID_DMA_STATE_52_55 0x00000434 +#define DREG_REGID_DMA_STATE_56_59 0x00000438 +#define DREG_REGID_DMA_STATE_60_63 0x0000043C +#define DREG_REGID_DMA_STATE_64_67 0x00000440 +#define DREG_REGID_DMA_STATE_68_71 0x00000444 +#define DREG_REGID_DMA_STATE_72_75 0x00000448 +#define DREG_REGID_DMA_STATE_76_79 0x0000044C +#define DREG_REGID_DMA_STATE_80_83 0x00000450 +#define DREG_REGID_DMA_STATE_84_87 0x00000454 +#define DREG_REGID_DMA_STATE_88_91 0x00000458 +#define DREG_REGID_DMA_STATE_92_95 0x0000045C +#define DREG_REGID_TRAP_SELECT 0x00000500 +#define DREG_REGID_TRAP_WRITE_0 0x00000500 +#define DREG_REGID_TRAP_WRITE_1 0x00000501 +#define DREG_REGID_TRAP_WRITE_2 0x00000502 +#define DREG_REGID_TRAP_WRITE_3 0x00000503 +#define DREG_REGID_TRAP_WRITE_4 0x00000504 +#define DREG_REGID_TRAP_WRITE_5 0x00000505 +#define DREG_REGID_TRAP_WRITE_6 0x00000506 +#define DREG_REGID_TRAP_WRITE_7 0x00000507 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510 +#define DREG_REGID_TRAP_WRITE_9 0x00000511 +#define DREG_REGID_TRAP_WRITE_10 0x00000512 +#define DREG_REGID_TRAP_WRITE_11 0x00000513 +#define DREG_REGID_TRAP_WRITE_12 0x00000514 +#define DREG_REGID_TRAP_WRITE_13 0x00000515 +#define DREG_REGID_TRAP_WRITE_14 0x00000516 +#define DREG_REGID_TRAP_WRITE_15 0x00000517 +#define DREG_REGID_TRAP_WRITE_16 0x00000518 +#define DREG_REGID_TRAP_WRITE_17 0x00000519 +#define DREG_REGID_TRAP_WRITE_18 0x0000051A +#define DREG_REGID_TRAP_WRITE_19 0x0000051B +#define DREG_REGID_TRAP_WRITE_20 0x0000051C +#define DREG_REGID_TRAP_WRITE_21 0x0000051D +#define DREG_REGID_TRAP_WRITE_22 0x0000051E +#define DREG_REGID_TRAP_WRITE_23 0x0000051F +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600 +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601 +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602 +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603 +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604 +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605 +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606 +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607 +#define DREG_REGID_MAC0_ACC0_MID 0x00000608 +#define DREG_REGID_MAC0_ACC1_MID 0x00000609 +#define DREG_REGID_MAC0_ACC2_MID 0x0000060A +#define DREG_REGID_MAC0_ACC3_MID 0x0000060B +#define DREG_REGID_MAC1_ACC0_MID 0x0000060C +#define DREG_REGID_MAC1_ACC1_MID 0x0000060D +#define DREG_REGID_MAC1_ACC2_MID 0x0000060E +#define DREG_REGID_MAC1_ACC3_MID 0x0000060F +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610 +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611 +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612 +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613 +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614 +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615 +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616 +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617 +#define DREG_REGID_RSHOUT_LOW 0x00000620 +#define DREG_REGID_RSHOUT_MID 0x00000628 +#define DREG_REGID_RSHOUT_HIGH 0x00000630 + +/* + * The following defines are for the flags in the DMA stream requestor write + */ +#define DSRWP_DSR_MASK 0x0000000F +#define DSRWP_DSR_BG_RQ 0x00000001 +#define DSRWP_DSR_PRIORITY_MASK 0x00000006 +#define DSRWP_DSR_PRIORITY_0 0x00000000 +#define DSRWP_DSR_PRIORITY_1 0x00000002 +#define DSRWP_DSR_PRIORITY_2 0x00000004 +#define DSRWP_DSR_PRIORITY_3 0x00000006 +#define DSRWP_DSR_RQ_PENDING 0x00000008 + +/* + * The following defines are for the flags in the trap write port register. + */ +#define TWPR_TW_MASK 0x0000FFFF +#define TWPR_TW_SHIFT 0 + +/* + * The following defines are for the flags in the stack pointer write + * register. + */ +#define SPWR_STKP_MASK 0x0000000F +#define SPWR_STKP_SHIFT 0 + +/* + * The following defines are for the flags in the SP interrupt register. + */ +#define SPIR_FRI 0x00000001 +#define SPIR_DOI 0x00000002 +#define SPIR_GPI2 0x00000004 +#define SPIR_GPI3 0x00000008 +#define SPIR_IP0 0x00000010 +#define SPIR_IP1 0x00000020 +#define SPIR_IP2 0x00000040 +#define SPIR_IP3 0x00000080 + +/* + * The following defines are for the flags in the functional group 1 register. + */ +#define FGR1_F1S_MASK 0x0000FFFF +#define FGR1_F1S_SHIFT 0 + +/* + * The following defines are for the flags in the SP clock status register. + */ +#define SPCS_FRI 0x00000001 +#define SPCS_DOI 0x00000002 +#define SPCS_GPI2 0x00000004 +#define SPCS_GPI3 0x00000008 +#define SPCS_IP0 0x00000010 +#define SPCS_IP1 0x00000020 +#define SPCS_IP2 0x00000040 +#define SPCS_IP3 0x00000080 +#define SPCS_SPRUN 0x00000100 +#define SPCS_SLEEP 0x00000200 +#define SPCS_FG 0x00000400 +#define SPCS_ORUN 0x00000800 +#define SPCS_IRQ 0x00001000 +#define SPCS_FGN_MASK 0x0000E000 +#define SPCS_FGN_SHIFT 13 + +/* + * The following defines are for the flags in the SP DMA requestor status + * register. + */ +#define SDSR_DCS_MASK 0x000000FF +#define SDSR_DCS_SHIFT 0 +#define SDSR_DCS_NONE 0x00000007 + +/* + * The following defines are for the flags in the frame timer register. + */ +#define FRMT_FTV_MASK 0x0000FFFF +#define FRMT_FTV_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer current count + * register. + */ +#define FRCC_FCC_MASK 0x0000FFFF +#define FRCC_FCC_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer save count + * register. + */ +#define FRSC_FCS_MASK 0x0000FFFF +#define FRSC_FCS_SHIFT 0 + +/* + * The following define the various flags stored in the scatter/gather + * descriptors. + */ +#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8 +#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000 +#define DMA_SG_SAMPLE_END_FLAG 0x10000000 +#define DMA_SG_LOOP_END_FLAG 0x20000000 +#define DMA_SG_SIGNAL_END_FLAG 0x40000000 +#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000 +#define DMA_SG_NEXT_ENTRY_SHIFT 3 +#define DMA_SG_SAMPLE_END_SHIFT 16 + +/* + * The following define the offsets of the fields within the on-chip generic + * DMA requestor. + */ +#define DMA_RQ_CONTROL1 0x00000000 +#define DMA_RQ_CONTROL2 0x00000004 +#define DMA_RQ_SOURCE_ADDR 0x00000008 +#define DMA_RQ_DESTINATION_ADDR 0x0000000C +#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010 +#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014 +#define DMA_RQ_LOOP_START_ADDR 0x00000018 +#define DMA_RQ_POST_LOOP_ADDR 0x0000001C +#define DMA_RQ_PAGE_MAP_ADDR 0x00000020 + +/* + * The following defines are for the flags in the first control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C1_COUNT_MASK 0x000003FF +#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000 +#define DMA_RQ_C1_SOURCE_GATHER 0x00002000 +#define DMA_RQ_C1_DONE_FLAG 0x00004000 +#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000 +#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000 +#define DMA_RQ_C1_FULL_PAGE 0x00000000 +#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000 +#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000 +#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000 +#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000 +#define DMA_RQ_C1_NOT_LOOP_END 0x00000000 +#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000 +#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000 +#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000 +#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000 +#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000 +#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000 +#define DMA_RQ_C1_PM_RESERVED 0x00200000 +#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000 +#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000 +#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000 +#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000 +#define DMA_RQ_C1_DEST_LINEAR 0x00000000 +#define DMA_RQ_C1_DEST_MOD16 0x01000000 +#define DMA_RQ_C1_DEST_MOD32 0x02000000 +#define DMA_RQ_C1_DEST_MOD64 0x03000000 +#define DMA_RQ_C1_DEST_MOD128 0x04000000 +#define DMA_RQ_C1_DEST_MOD256 0x05000000 +#define DMA_RQ_C1_DEST_MOD512 0x06000000 +#define DMA_RQ_C1_DEST_MOD1024 0x07000000 +#define DMA_RQ_C1_DEST_ON_HOST 0x08000000 +#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000 +#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000 +#define DMA_RQ_C1_SOURCE_MOD16 0x10000000 +#define DMA_RQ_C1_SOURCE_MOD32 0x20000000 +#define DMA_RQ_C1_SOURCE_MOD64 0x30000000 +#define DMA_RQ_C1_SOURCE_MOD128 0x40000000 +#define DMA_RQ_C1_SOURCE_MOD256 0x50000000 +#define DMA_RQ_C1_SOURCE_MOD512 0x60000000 +#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000 +#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000 +#define DMA_RQ_C1_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the second control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F +#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300 +#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000 +#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100 +#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200 +#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300 +#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000 +#define DMA_RQ_C2_AC_NONE 0x00000000 +#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000 +#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000 +#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000 +#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000 +#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000 +#define DMA_RQ_C2_LOOP_MASK 0x30000000 +#define DMA_RQ_C2_NO_LOOP 0x00000000 +#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000 +#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000 +#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000 +#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000 +#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000 +#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0 +#define DMA_RQ_C2_LOOP_END_SHIFT 16 + +/* + * The following defines are for the flags in the source and destination words + * of the on-chip generic DMA requestor. + */ +#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF +#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000 +#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000 +#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000 +#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000 +#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000 +#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000 +#define DMA_RQ_SD_END_FLAG 0x40000000 +#define DMA_RQ_SD_ERROR_FLAG 0x80000000 +#define DMA_RQ_SD_ADDRESS_SHIFT 0 + +/* + * The following defines are for the flags in the page map address word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8 +#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000 +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3 +#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12 + +#define BA1_VARIDEC_BUF_1 0x000 + +#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */ +#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */ +#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */ +#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */ +#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */ +#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */ + +#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */ +#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */ +#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */ +#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */ +#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */ +#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */ +#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */ + +#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */ +#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */ +#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ +#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ + +/* + * + */ + +#define CS46XX_MODE_OUTPUT (1<<0) /* MIDI UART - output */ +#define CS46XX_MODE_INPUT (1<<1) /* MIDI UART - input */ + +/* + * + */ + +#define SAVE_REG_MAX 0x10 +#define POWER_DOWN_ALL 0x7f0f + +/* + * + */ + +typedef struct _snd_cs46xx cs46xx_t; + +typedef struct { + char name[24]; + unsigned long base; + unsigned long remap_addr; + unsigned long size; + struct resource *resource; + void *proc_entry; +} snd_cs46xx_region_t; + +struct _snd_cs46xx { + int irq; + unsigned long ba0_addr; + unsigned long ba1_addr; + union { + struct { + snd_cs46xx_region_t ba0; + snd_cs46xx_region_t data0; + snd_cs46xx_region_t data1; + snd_cs46xx_region_t pmem; + snd_cs46xx_region_t reg; + } name; + snd_cs46xx_region_t idx[5]; + } region; + + unsigned int mode; + + struct { + unsigned char *hw_area; + dma_addr_t hw_addr; /* PCI bus address, not accessible */ + unsigned long hw_size; + + unsigned int ctl; + unsigned int shift; /* Shift count to trasform frames in bytes */ + unsigned int sw_bufsize; + unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */ + unsigned int sw_io; + int sw_ready; /* Bytes ready to be transferred to/from hw */ + unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */ + unsigned int hw_io; /* Ring buffer hw pointer */ + int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */ + size_t appl_ptr; /* Last seen appl_ptr */ + snd_pcm_substream_t *substream; + } play, capt; + + + ac97_t *ac97; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_input; + snd_rawmidi_substream_t *midi_output; + + spinlock_t reg_lock; + unsigned int midcr; + unsigned int uartm; + snd_info_entry_t *proc_entry; + + int amplifier; + void (*amplifier_ctrl)(cs46xx_t *, int); + void (*active_ctrl)(cs46xx_t *, int); + struct pci_dev *acpi_dev; + int acpi_port; + snd_kcontrol_t *eapd_switch; /* for amplifier hack */ + +#ifdef CONFIG_PM + struct pm_dev *pm_dev; +#endif +}; + +int snd_cs46xx_create(snd_card_t *card, + struct pci_dev *pci, + int external_amp, int thinkpad, + cs46xx_t **rcodec); + +int snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t **rpcm); +int snd_cs46xx_mixer(cs46xx_t *chip); +int snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rmidi); + +#ifdef CONFIG_PM +void snd_cs46xx_suspend(cs46xx_t *chip); +void snd_cs46xx_resume(cs46xx_t *chip); +#endif + +#endif /* __SOUND_CS46XX_H */ diff -Nru a/include/sound/cs8403.h b/include/sound/cs8403.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/cs8403.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,257 @@ +#ifndef __SOUND_CS8403_H +#define __SOUND_CS8403_H + +/* + * Routines for Cirrus Logic CS8403/CS8404A IEC958 (S/PDIF) Transmitter + * Copyright (c) by Jaroslav Kysela , + * Takashi Iwai + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef SND_CS8403 + +#ifndef SND_CS8403_DECL +#define SND_CS8403_DECL static +#endif +#ifndef SND_CS8403_DECODE +#define SND_CS8403_DECODE snd_cs8403_decode_spdif_bits +#endif +#ifndef SND_CS8403_ENCODE +#define SND_CS8403_ENCODE snd_cs8403_encode_spdif_bits +#endif + + +SND_CS8403_DECL void SND_CS8403_DECODE(snd_aes_iec958_t *diga, unsigned char bits) +{ + if (bits & 0x01) { /* consumer */ + if (!(bits & 0x02)) + diga->status[0] |= IEC958_AES0_NONAUDIO; + if (!(bits & 0x08)) + diga->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; + switch (bits & 0x10) { + case 0x10: diga->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; break; + case 0x00: diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015; break; + } + if (!(bits & 0x80)) + diga->status[1] |= IEC958_AES1_CON_ORIGINAL; + switch (bits & 0x60) { + case 0x00: diga->status[1] |= IEC958_AES1_CON_MAGNETIC_ID; break; + case 0x20: diga->status[1] |= IEC958_AES1_CON_DIGDIGCONV_ID; break; + case 0x40: diga->status[1] |= IEC958_AES1_CON_LASEROPT_ID; break; + case 0x60: diga->status[1] |= IEC958_AES1_CON_GENERAL; break; + } + switch (bits & 0x06) { + case 0x00: diga->status[3] |= IEC958_AES3_CON_FS_44100; break; + case 0x02: diga->status[3] |= IEC958_AES3_CON_FS_48000; break; + case 0x04: diga->status[3] |= IEC958_AES3_CON_FS_32000; break; + } + } else { + diga->status[0] = IEC958_AES0_PROFESSIONAL; + switch (bits & 0x18) { + case 0x00: diga->status[0] |= IEC958_AES0_PRO_FS_32000; break; + case 0x10: diga->status[0] |= IEC958_AES0_PRO_FS_44100; break; + case 0x08: diga->status[0] |= IEC958_AES0_PRO_FS_48000; break; + case 0x18: diga->status[0] |= IEC958_AES0_PRO_FS_NOTID; break; + } + switch (bits & 0x60) { + case 0x20: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; break; + case 0x40: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; break; + case 0x00: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; break; + case 0x60: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; break; + } + if (bits & 0x80) + diga->status[1] |= IEC958_AES1_PRO_MODE_STEREOPHONIC; + } +} + +SND_CS8403_DECL unsigned char SND_CS8403_ENCODE(snd_aes_iec958_t *diga) +{ + unsigned char bits; + + if (!(diga->status[0] & IEC958_AES0_PROFESSIONAL)) { + bits = 0x01; /* consumer mode */ + if (diga->status[0] & IEC958_AES0_NONAUDIO) + bits &= ~0x02; + else + bits |= 0x02; + if (diga->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) + bits &= ~0x08; + else + bits |= 0x08; + switch (diga->status[0] & IEC958_AES0_CON_EMPHASIS) { + default: + case IEC958_AES0_CON_EMPHASIS_NONE: bits |= 0x10; break; + case IEC958_AES0_CON_EMPHASIS_5015: bits |= 0x00; break; + } + if (diga->status[1] & IEC958_AES1_CON_ORIGINAL) + bits &= ~0x80; + else + bits |= 0x80; + if ((diga->status[1] & IEC958_AES1_CON_CATEGORY) == IEC958_AES1_CON_GENERAL) + bits |= 0x60; + else { + switch(diga->status[1] & IEC958_AES1_CON_MAGNETIC_MASK) { + case IEC958_AES1_CON_MAGNETIC_ID: + bits |= 0x00; break; + case IEC958_AES1_CON_DIGDIGCONV_ID: + bits |= 0x20; break; + default: + case IEC958_AES1_CON_LASEROPT_ID: + bits |= 0x40; break; + } + } + switch (diga->status[3] & IEC958_AES3_CON_FS) { + default: + case IEC958_AES3_CON_FS_44100: bits |= 0x00; break; + case IEC958_AES3_CON_FS_48000: bits |= 0x02; break; + case IEC958_AES3_CON_FS_32000: bits |= 0x04; break; + } + } else { + bits = 0x00; /* professional mode */ + if (diga->status[0] & IEC958_AES0_NONAUDIO) + bits &= ~0x02; + else + bits |= 0x02; + /* CHECKME: I'm not sure about the bit order in val here */ + switch (diga->status[0] & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_32000: bits |= 0x00; break; + case IEC958_AES0_PRO_FS_44100: bits |= 0x10; break; /* 44.1kHz */ + case IEC958_AES0_PRO_FS_48000: bits |= 0x08; break; /* 48kHz */ + default: + case IEC958_AES0_PRO_FS_NOTID: bits |= 0x18; break; + } + switch (diga->status[0] & IEC958_AES0_PRO_EMPHASIS) { + case IEC958_AES0_PRO_EMPHASIS_NONE: bits |= 0x20; break; + case IEC958_AES0_PRO_EMPHASIS_5015: bits |= 0x40; break; + case IEC958_AES0_PRO_EMPHASIS_CCITT: bits |= 0x00; break; + default: + case IEC958_AES0_PRO_EMPHASIS_NOTID: bits |= 0x60; break; + } + switch (diga->status[1] & IEC958_AES1_PRO_MODE) { + case IEC958_AES1_PRO_MODE_TWO: + case IEC958_AES1_PRO_MODE_STEREOPHONIC: bits |= 0x00; break; + default: bits |= 0x80; break; + } + } + return bits; +} + +#endif /* SND_CS8403 */ + +#ifdef SND_CS8404 + +#ifndef SND_CS8404_DECL +#define SND_CS8404_DECL static +#endif +#ifndef SND_CS8404_DECODE +#define SND_CS8404_DECODE snd_cs8404_decode_spdif_bits +#endif +#ifndef SND_CS8404_ENCODE +#define SND_CS8404_ENCODE snd_cs8404_encode_spdif_bits +#endif + + +SND_CS8404_DECL void SND_CS8404_DECODE(snd_aes_iec958_t *diga, unsigned char bits) +{ + if (bits & 0x10) { /* consumer */ + if (!(bits & 0x20)) + diga->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; + if (!(bits & 0x40)) + diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015; + if (!(bits & 0x80)) + diga->status[1] |= IEC958_AES1_CON_ORIGINAL; + switch (bits & 0x03) { + case 0x00: diga->status[1] |= IEC958_AES1_CON_DAT; break; + case 0x03: diga->status[1] |= IEC958_AES1_CON_GENERAL; break; + } + switch (bits & 0x06) { + case 0x02: diga->status[3] |= IEC958_AES3_CON_FS_32000; break; + case 0x04: diga->status[3] |= IEC958_AES3_CON_FS_48000; break; + case 0x06: diga->status[3] |= IEC958_AES3_CON_FS_44100; break; + } + } else { + diga->status[0] = IEC958_AES0_PROFESSIONAL; + if (!(bits & 0x04)) + diga->status[0] |= IEC958_AES0_NONAUDIO; + switch (bits & 0x60) { + case 0x00: diga->status[0] |= IEC958_AES0_PRO_FS_32000; break; + case 0x40: diga->status[0] |= IEC958_AES0_PRO_FS_44100; break; + case 0x20: diga->status[0] |= IEC958_AES0_PRO_FS_48000; break; + case 0x60: diga->status[0] |= IEC958_AES0_PRO_FS_NOTID; break; + } + switch (bits & 0x03) { + case 0x02: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; break; + case 0x01: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; break; + case 0x00: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; break; + case 0x03: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; break; + } + if (!(bits & 0x80)) + diga->status[1] |= IEC958_AES1_PRO_MODE_STEREOPHONIC; + } +} + +SND_CS8404_DECL unsigned char SND_CS8404_ENCODE(snd_aes_iec958_t *diga) +{ + unsigned char bits; + + if (!(diga->status[0] & IEC958_AES0_PROFESSIONAL)) { + bits = 0x10; /* consumer mode */ + if (!(diga->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT)) + bits |= 0x20; + if ((diga->status[0] & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_NONE) + bits |= 0x40; + if (!(diga->status[1] & IEC958_AES1_CON_ORIGINAL)) + bits |= 0x80; + if ((diga->status[1] & IEC958_AES1_CON_CATEGORY) == IEC958_AES1_CON_GENERAL) + bits |= 0x03; + switch (diga->status[3] & IEC958_AES3_CON_FS) { + default: + case IEC958_AES3_CON_FS_44100: bits |= 0x06; break; + case IEC958_AES3_CON_FS_48000: bits |= 0x04; break; + case IEC958_AES3_CON_FS_32000: bits |= 0x02; break; + } + } else { + bits = 0x00; /* professional mode */ + if (!(diga->status[0] & IEC958_AES0_NONAUDIO)) + bits |= 0x04; + switch (diga->status[0] & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_32000: bits |= 0x00; break; + case IEC958_AES0_PRO_FS_44100: bits |= 0x40; break; /* 44.1kHz */ + case IEC958_AES0_PRO_FS_48000: bits |= 0x20; break; /* 48kHz */ + default: + case IEC958_AES0_PRO_FS_NOTID: bits |= 0x00; break; + } + switch (diga->status[0] & IEC958_AES0_PRO_EMPHASIS) { + case IEC958_AES0_PRO_EMPHASIS_NONE: bits |= 0x02; break; + case IEC958_AES0_PRO_EMPHASIS_5015: bits |= 0x01; break; + case IEC958_AES0_PRO_EMPHASIS_CCITT: bits |= 0x00; break; + default: + case IEC958_AES0_PRO_EMPHASIS_NOTID: bits |= 0x03; break; + } + switch (diga->status[1] & IEC958_AES1_PRO_MODE) { + case IEC958_AES1_PRO_MODE_TWO: + case IEC958_AES1_PRO_MODE_STEREOPHONIC: bits |= 0x00; break; + default: bits |= 0x80; break; + } + } + return bits; +} + +#endif /* SND_CS8404 */ + +#endif /* __SOUND_CS8403_H */ diff -Nru a/include/sound/cs8427.h b/include/sound/cs8427.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/cs8427.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,195 @@ +#ifndef __SOUND_CS8427_H +#define __SOUND_CS8427_H + +/* + * Routines for Cirrus Logic CS8427 + * Copyright (c) by Jaroslav Kysela , + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#define CS8427_BASE_ADDR 0x10 /* base I2C address */ + +#define CS8427_REG_AUTOINC 0x80 /* flag - autoincrement */ +#define CS8427_REG_CONTROL1 0x01 +#define CS8427_REG_CONTROL2 0x02 +#define CS8427_REG_DATAFLOW 0x03 +#define CS8427_REG_CLOCKSOURCE 0x04 +#define CS8427_REG_SERIALINPUT 0x05 +#define CS8427_REG_SERIALOUTPUT 0x06 +#define CS8427_REG_INT1STATUS 0x07 +#define CS8427_REG_INT2STATUS 0x08 +#define CS8427_REG_INT1MASK 0x09 +#define CS8427_REG_INT1MODEMSB 0x0a +#define CS8427_REG_INT1MODELSB 0x0b +#define CS8427_REG_INT2MASK 0x0c +#define CS8427_REG_INT2MODEMSB 0x0d +#define CS8427_REG_INT2MODELSB 0x0e +#define CS8427_REG_RECVCSDATA 0x0f +#define CS8427_REG_RECVERRORS 0x10 +#define CS8427_REG_RECVERRMASK 0x11 +#define CS8427_REG_CSDATABUF 0x12 +#define CS8427_REG_UDATABUF 0x13 +#define CS8427_REG_QSUBCODE 0x14 /* 0x14-0x1d (10 bytes) */ +#define CS8427_REG_OMCKRMCKRATIO 0x1e +#define CS8427_REG_CORU_DATABUF 0x20 +#define CS8427_REG_ID_AND_VER 0x7f + +/* CS8427_REG_CONTROL1 bits */ +#define CS8427_SWCLK (1<<7) /* 0 = RMCK default, 1 = OMCK output on RMCK pin */ +#define CS8427_VSET (1<<6) /* 0 = valid PCM data, 1 = invalid PCM data */ +#define CS8427_MUTESAO (1<<5) /* mute control for the serial audio output port, 0 = disabled, 1 = enabled */ +#define CS8427_MUTEAES (1<<4) /* mute control for the AES transmitter output, 0 = disabled, 1 = enabled */ +#define CS8427_INTMASK (3<<1) /* interrupt output pin setup mask */ +#define CS8427_INTACTHIGH (0<<1) /* active high */ +#define CS8427_INTACTLOW (1<<1) /* active low */ +#define CS8427_INTOPENDRAIN (2<<1) /* open drain, active low */ +#define CS8427_TCBLDIR (1<<0) /* 0 = TCBL is an input, 1 = TCBL is an output */ + +/* CS8427_REQ_CONTROL2 bits */ +#define CS8427_HOLDMASK (3<<5) /* action when a receiver error occurs */ +#define CS8427_HOLDLASTSAMPLE (0<<5) /* hold the last valid sample */ +#define CS8427_HOLDZERO (1<<5) /* replace the current audio sample with zero (mute) */ +#define CS8427_HOLDNOCHANGE (2<<5) /* do not change the received audio sample */ +#define CS8427_RMCKF (1<<4) /* 0 = 256*Fsi, 1 = 128*Fsi */ +#define CS8427_MMR (1<<3) /* AES3 receiver operation, 0 = stereo, 1 = mono */ +#define CS8427_MMT (1<<2) /* AES3 transmitter operation, 0 = stereo, 1 = mono */ +#define CS8427_MMTCS (1<<1) /* 0 = use A + B CS data, 1 = use MMTLR CS data */ +#define CS8427_MMTLR (1<<0) /* 0 = use A CS data, 1 = use B CS data */ + +/* CS8427_REG_DATAFLOW */ +#define CS8427_TXOFF (1<<6) /* AES3 transmitter Output, 0 = normal operation, 1 = off (0V) */ +#define CS8427_AESBP (1<<5) /* AES3 hardware bypass mode, 0 = normal, 1 = bypass (RX->TX) */ +#define CS8427_TXDMASK (3<<3) /* AES3 Transmitter Data Source Mask */ +#define CS8427_TXDSERIAL (1<<3) /* TXD - serial audio input port */ +#define CS8427_TXAES3DRECEIVER (2<<3) /* TXD - AES3 receiver */ +#define CS8427_SPDMASK (3<<1) /* Serial Audio Output Port Data Source Mask */ +#define CS8427_SPDSERIAL (1<<1) /* SPD - serial audio input port */ +#define CS8427_SPDAES3RECEIVER (2<<1) /* SPD - AES3 receiver */ + +/* CS8427_REG_CLOCKSOURCE */ +#define CS8427_RUN (1<<6) /* 0 = clock off, 1 = clock on */ +#define CS8427_CLKMASK (3<<4) /* OMCK frequency mask */ +#define CS8427_CLK256 (0<<4) /* 256*Fso */ +#define CS8427_CLK384 (1<<4) /* 384*Fso */ +#define CS8427_CLK512 (2<<4) /* 512*Fso */ +#define CS8427_OUTC (1<<3) /* Output Time Base, 0 = OMCK, 1 = recovered input clock */ +#define CS8427_INC (1<<2) /* Input Time Base Clock Source, 0 = recoverd input clock, 1 = OMCK input pin */ +#define CS8427_RXDMASK (3<<0) /* Recovered Input Clock Source Mask */ +#define CS8427_RXDILRCK (0<<0) /* 256*Fsi from ILRCK pin */ +#define CS8427_RXDAES3INPUT (1<<0) /* 256*Fsi from AES3 input */ +#define CS8427_EXTCLOCKRESET (2<<0) /* bypass PLL, 256*Fsi clock, synchronous reset */ +#define CS8427_EXTCLOCK (3<<0) /* bypass PLL, 256*Fsi clock */ + +/* CS8427_REG_SERIALINPUT */ +#define CS8427_SIMS (1<<7) /* 0 = slave, 1 = master mode */ +#define CS8427_SISF (1<<6) /* ISCLK freq, 0 = 64*Fsi, 1 = 128*Fsi */ +#define CS8427_SIRESMASK (3<<4) /* Resolution of the input data for right justified formats */ +#define CS8427_SIRES24 (0<<4) /* SIRES 24-bit */ +#define CS8427_SIRES20 (1<<4) /* SIRES 20-bit */ +#define CS8427_SIRES16 (2<<4) /* SIRES 16-bit */ +#define CS8427_SIJUST (1<<3) /* Justification of SDIN data relative to ILRCK, 0 = left-justified, 1 = right-justified */ +#define CS8427_SIDEL (1<<2) /* Delay of SDIN data relative to ILRCK for left-justified data formats, 0 = first ISCLK period, 1 = second ISCLK period */ +#define CS8427_SISPOL (1<<1) /* ICLK clock polarity, 0 = rising edge of ISCLK, 1 = falling edge of ISCLK */ +#define CS8427_SILRPOL (1<<0) /* ILRCK clock polarity, 0 = SDIN data left channel when ILRCK is high, 1 = SDIN right when ILRCK is high */ + +/* CS8427_REG_SERIALOUTPUT */ +#define CS8427_SOMS (1<<7) /* 0 = slave, 1 = master mode */ +#define CS8427_SOSF (1<<6) /* OSCLK freq, 0 = 64*Fso, 1 = 128*Fso */ +#define CS8427_SORESMASK (3<<4) /* Resolution of the output data on SDOUT and AES3 output */ +#define CS8427_SORES24 (0<<4) /* SIRES 24-bit */ +#define CS8427_SORES20 (1<<4) /* SIRES 20-bit */ +#define CS8427_SORES16 (2<<4) /* SIRES 16-bit */ +#define CS8427_SORESDIRECT (2<<4) /* SIRES direct copy from AES3 receiver */ +#define CS8427_SOJUST (1<<3) /* Justification of SDOUT data relative to OLRCK, 0 = left-justified, 1 = right-justified */ +#define CS8427_SODEL (1<<2) /* Delay of SDOUT data relative to OLRCK for left-justified data formats, 0 = first OSCLK period, 1 = second OSCLK period */ +#define CS8427_SOSPOL (1<<1) /* OSCLK clock polarity, 0 = rising edge of ISCLK, 1 = falling edge of ISCLK */ +#define CS8427_SOLRPOL (1<<0) /* OLRCK clock polarity, 0 = SDOUT data left channel when OLRCK is high, 1 = SDOUT right when OLRCK is high */ + +/* CS8427_REG_INT1STATUS */ +#define CS8427_TSLIP (1<<7) /* AES3 transmitter source data slip interrupt */ +#define CS8427_OSLIP (1<<6) /* Serial audio output port data slip interrupt */ +#define CS8427_DETC (1<<2) /* D to E C-buffer transfer interrupt */ +#define CS8427_EFTC (1<<1) /* E to F C-buffer transfer interrupt */ +#define CS8427_RERR (1<<0) /* A receiver error has occurred */ + +/* CS8427_REG_INT2STATUS */ +#define CS8427_DETU (1<<3) /* D to E U-buffer transfer interrupt */ +#define CS8427_EFTU (1<<2) /* E to F U-buffer transfer interrupt */ +#define CS8427_QCH (1<<1) /* A new block of Q-subcode data is available for reading */ + +/* CS8427_REG_INT1MODEMSB && CS8427_REG_INT1MODELSB */ +/* bits are defined in CS8427_REG_INT1STATUS */ +/* CS8427_REG_INT2MODEMSB && CS8427_REG_INT2MODELSB */ +/* bits are defined in CS8427_REG_INT2STATUS */ +#define CS8427_INTMODERISINGMSB 0 +#define CS8427_INTMODERESINGLSB 0 +#define CS8427_INTMODEFALLINGMSB 0 +#define CS8427_INTMODEFALLINGLSB 1 +#define CS8427_INTMODELEVELMSB 1 +#define CS8427_INTMODELEVELLSB 0 + +/* CS8427_REG_RECVCSDATA */ +#define CS8427_AUXMASK (15<<4) /* auxiliary data field width */ +#define CS8427_AUXSHIFT 4 +#define CS8427_PRO (1<<3) /* Channel status block format indicator */ +#define CS8427_AUDIO (1<<2) /* Audio indicator (0 = audio, 1 = nonaudio */ +#define CS8427_COPY (1<<1) /* 0 = copyright asserted, 1 = copyright not asserted */ +#define CS8427_ORIG (1<<0) /* SCMS generation indicator, 0 = 1st generation or highter, 1 = original */ + +/* CS8427_REG_RECVERRORS */ +/* CS8427_REG_RECVERRMASK for CS8427_RERR */ +#define CS8427_QCRC (1<<6) /* Q-subcode data CRC error indicator */ +#define CS8427_CCRC (1<<5) /* Chancnel Status Block Cyclick Redundancy Check Bit */ +#define CS8427_UNLOCK (1<<4) /* PLL lock status bit */ +#define CS8427_V (1<<3) /* 0 = valid data */ +#define CS8427_CONF (1<<2) /* Confidence bit */ +#define CS8427_BIP (1<<1) /* Bi-phase error bit */ +#define CS8427_PAR (1<<0) /* Parity error */ + +/* CS8427_REG_CSDATABUF */ +#define CS8427_BSEL (1<<5) /* 0 = CS data, 1 = U data */ +#define CS8427_CBMR (1<<4) /* 0 = overwrite first 5 bytes for CS D to E buffer, 1 = prevent */ +#define CS8427_DETCI (1<<3) /* D to E CS data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ +#define CS8427_EFTCI (1<<2) /* E to F CS data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ +#define CS8427_CAM (1<<1) /* CS data buffer control port access mode bit, 0 = one byte, 1 = two byte */ +#define CS8427_CHS (1<<0) /* Channel select bit, 0 = Channel A, 1 = Channel B */ + +/* CS8427_REG_UDATABUF */ +#define CS8427_UD (1<<4) /* User data pin (U) direction, 0 = input, 1 = output */ +#define CS8427_UBMMASK (3<<2) /* Operating mode of the AES3 U bit manager */ +#define CS8427_UBMZEROS (0<<2) /* transmit all zeros mode */ +#define CS8427_UBMBLOCK (1<<2) /* block mode */ +#define CS8427_DETUI (1<<1) /* D to E U-data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ +#define CS8427_EFTUI (1<<1) /* E to F U-data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ + +/* CS8427_REG_ID_AND_VER */ +#define CS8427_IDMASK (15<<4) +#define CS8427_IDSHIFT 4 +#define CS8427_VERMASK (15<<0) +#define CS8427_VERSHIFT 0 +#define CS8427_VER8427A 0x71 + +int snd_cs8427_detect(snd_i2c_bus_t *bus, unsigned char addr); +int snd_cs8427_create(snd_i2c_bus_t *bus, unsigned char addr, snd_i2c_device_t **r_cs8427); +int snd_cs8427_iec958_build(snd_i2c_device_t *cs8427, snd_pcm_substream_t *playback_substream, snd_pcm_substream_t *capture_substream); +int snd_cs8427_iec958_active(snd_i2c_device_t *cs8427, int active); +int snd_cs8427_iec958_pcm(snd_i2c_device_t *cs8427, unsigned int rate); + +#endif /* __SOUND_CS8427_H */ diff -Nru a/include/sound/driver.h b/include/sound/driver.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/driver.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,89 @@ +#ifndef __SOUND_DRIVER_H +#define __SOUND_DRIVER_H + +/* + * Main header file for the ALSA driver + * Copyright (c) 1994-2000 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef ALSA_BUILD +#include "config.h" +#endif + +#include +#include + +#define SNDRV_CARDS 8 /* number of supported soundcards - don't change - minor numbers */ + +#ifndef CONFIG_SND_MAJOR /* standard configuration */ +#define CONFIG_SND_MAJOR 116 +#endif + +#ifndef CONFIG_SND_DEBUG +#undef CONFIG_SND_DEBUG_MEMORY +#endif + +#ifdef ALSA_BUILD +#include "adriver.h" +#endif + +#include + +/* + * ========================================================================== + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) +#if defined(__i386__) || defined(__ppc__) +/* + * Here a dirty hack for 2.4 kernels.. See kernel/memory.c. + */ +#define HACK_PCI_ALLOC_CONSISTENT +#include +void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle); +#undef pci_alloc_consistent +#define pci_alloc_consistent snd_pci_hack_alloc_consistent +#endif /* i386 or ppc */ +#endif /* 2.4.0 */ + +#ifdef CONFIG_SND_DEBUG_MEMORY +#include +#include +void *snd_wrapper_kmalloc(size_t, int); +#undef kmalloc +void snd_wrapper_kfree(const void *); +#undef kfree +void *snd_wrapper_vmalloc(size_t); +#undef vmalloc +void snd_wrapper_vfree(void *); +#undef vfree +#endif + +#include "sndmagic.h" + +/* + * Temporary hack, until linux/init.h is fixed. + */ +#include +#ifndef __devexit_p +#define __devexit_p(x) x +#endif + +#endif /* __SOUND_DRIVER_H */ diff -Nru a/include/sound/emu10k1.h b/include/sound/emu10k1.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/emu10k1.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1294 @@ +#ifndef __SOUND_EMU10K1_H +#define __SOUND_EMU10K1_H + +/* + * Copyright (c) by Jaroslav Kysela , + * Creative Labs, Inc. + * Definitions for EMU10K1 (SB Live!) chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef __KERNEL__ + +#include "pcm.h" +#include "rawmidi.h" +#include "hwdep.h" +#include "ac97_codec.h" +#include "util_mem.h" +#include + +#ifndef PCI_VENDOR_ID_CREATIVE +#define PCI_VENDOR_ID_CREATIVE 0x1102 +#endif +#ifndef PCI_DEVICE_ID_CREATIVE_EMU10K1 +#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 +#endif + +/* ------------------- DEFINES -------------------- */ + +#define EMUPAGESIZE 4096 +#define MAXREQVOICES 8 +#define MAXPAGES 8192 +#define RESERVED 0 +#define NUM_MIDI 16 +#define NUM_G 64 /* use all channels */ +#define NUM_FXSENDS 4 + + +#define TMEMSIZE 256*1024 +#define TMEMSIZEREG 4 + +#define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL)) + +// Audigy specify registers are prefixed with 'A_' + +/************************************************************************************************/ +/* PCI function 0 registers, address = + PCIBASE0 */ +/************************************************************************************************/ + +#define PTR 0x00 /* Indexed register set pointer register */ + /* NOTE: The CHANNELNUM and ADDRESS words can */ + /* be modified independently of each other. */ +#define PTR_CHANNELNUM_MASK 0x0000003f /* For each per-channel register, indicates the */ + /* channel number of the register to be */ + /* accessed. For non per-channel registers the */ + /* value should be set to zero. */ +#define PTR_ADDRESS_MASK 0x07ff0000 /* Register index */ +#define A_PTR_ADDRESS_MASK 0x0fff0000 + +#define DATA 0x04 /* Indexed register set data register */ + +#define IPR 0x08 /* Global interrupt pending register */ + /* Clear pending interrupts by writing a 1 to */ + /* the relevant bits and zero to the other bits */ + +/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ +#define IPR_A_MIDITRANSBUFEMPTY2 0x10000000 /* MIDI UART transmit buffer empty */ +#define IPR_A_MIDIRECVBUFEMPTY2 0x08000000 /* MIDI UART receive buffer empty */ + +#define IPR_SAMPLERATETRACKER 0x01000000 /* Sample rate tracker lock status change */ +#define IPR_FXDSP 0x00800000 /* Enable FX DSP interrupts */ +#define IPR_FORCEINT 0x00400000 /* Force Sound Blaster interrupt */ +#define IPR_PCIERROR 0x00200000 /* PCI bus error */ +#define IPR_VOLINCR 0x00100000 /* Volume increment button pressed */ +#define IPR_VOLDECR 0x00080000 /* Volume decrement button pressed */ +#define IPR_MUTE 0x00040000 /* Mute button pressed */ +#define IPR_MICBUFFULL 0x00020000 /* Microphone buffer full */ +#define IPR_MICBUFHALFFULL 0x00010000 /* Microphone buffer half full */ +#define IPR_ADCBUFFULL 0x00008000 /* ADC buffer full */ +#define IPR_ADCBUFHALFFULL 0x00004000 /* ADC buffer half full */ +#define IPR_EFXBUFFULL 0x00002000 /* Effects buffer full */ +#define IPR_EFXBUFHALFFULL 0x00001000 /* Effects buffer half full */ +#define IPR_GPSPDIFSTATUSCHANGE 0x00000800 /* GPSPDIF channel status change */ +#define IPR_CDROMSTATUSCHANGE 0x00000400 /* CD-ROM channel status change */ +#define IPR_INTERVALTIMER 0x00000200 /* Interval timer terminal count */ +#define IPR_MIDITRANSBUFEMPTY 0x00000100 /* MIDI UART transmit buffer empty */ +#define IPR_MIDIRECVBUFEMPTY 0x00000080 /* MIDI UART receive buffer empty */ +#define IPR_CHANNELLOOP 0x00000040 /* One or more channel loop interrupts pending */ +#define IPR_CHANNELNUMBERMASK 0x0000003f /* When IPR_CHANNELLOOP is set, indicates the */ + /* Highest set channel in CLIPL or CLIPH. When */ + /* IP is written with CL set, the bit in CLIPL */ + /* or CLIPH corresponding to the CIN value */ + /* written will be cleared. */ + +#define INTE 0x0c /* Interrupt enable register */ +#define INTE_VIRTUALSB_MASK 0xc0000000 /* Virtual Soundblaster I/O port capture */ +#define INTE_VIRTUALSB_220 0x00000000 /* Capture at I/O base address 0x220-0x22f */ +#define INTE_VIRTUALSB_240 0x40000000 /* Capture at I/O base address 0x240 */ +#define INTE_VIRTUALSB_260 0x80000000 /* Capture at I/O base address 0x260 */ +#define INTE_VIRTUALSB_280 0xc0000000 /* Capture at I/O base address 0x280 */ +#define INTE_VIRTUALMPU_MASK 0x30000000 /* Virtual MPU I/O port capture */ +#define INTE_VIRTUALMPU_300 0x00000000 /* Capture at I/O base address 0x300-0x301 */ +#define INTE_VIRTUALMPU_310 0x10000000 /* Capture at I/O base address 0x310 */ +#define INTE_VIRTUALMPU_320 0x20000000 /* Capture at I/O base address 0x320 */ +#define INTE_VIRTUALMPU_330 0x30000000 /* Capture at I/O base address 0x330 */ +#define INTE_MASTERDMAENABLE 0x08000000 /* Master DMA emulation at 0x000-0x00f */ +#define INTE_SLAVEDMAENABLE 0x04000000 /* Slave DMA emulation at 0x0c0-0x0df */ +#define INTE_MASTERPICENABLE 0x02000000 /* Master PIC emulation at 0x020-0x021 */ +#define INTE_SLAVEPICENABLE 0x01000000 /* Slave PIC emulation at 0x0a0-0x0a1 */ +#define INTE_VSBENABLE 0x00800000 /* Enable virtual Soundblaster */ +#define INTE_ADLIBENABLE 0x00400000 /* Enable AdLib emulation at 0x388-0x38b */ +#define INTE_MPUENABLE 0x00200000 /* Enable virtual MPU */ +#define INTE_FORCEINT 0x00100000 /* Continuously assert INTAN */ + +#define INTE_MRHANDENABLE 0x00080000 /* Enable the "Mr. Hand" logic */ + /* NOTE: There is no reason to use this under */ + /* Linux, and it will cause odd hardware */ + /* behavior and possibly random segfaults and */ + /* lockups if enabled. */ + +/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ +#define INTE_A_MIDITXENABLE2 0x00020000 /* Enable MIDI transmit-buffer-empty interrupts */ +#define INTE_A_MIDIRXENABLE2 0x00010000 /* Enable MIDI receive-buffer-empty interrupts */ + + +#define INTE_SAMPLERATETRACKER 0x00002000 /* Enable sample rate tracker interrupts */ + /* NOTE: This bit must always be enabled */ +#define INTE_FXDSPENABLE 0x00001000 /* Enable FX DSP interrupts */ +#define INTE_PCIERRORENABLE 0x00000800 /* Enable PCI bus error interrupts */ +#define INTE_VOLINCRENABLE 0x00000400 /* Enable volume increment button interrupts */ +#define INTE_VOLDECRENABLE 0x00000200 /* Enable volume decrement button interrupts */ +#define INTE_MUTEENABLE 0x00000100 /* Enable mute button interrupts */ +#define INTE_MICBUFENABLE 0x00000080 /* Enable microphone buffer interrupts */ +#define INTE_ADCBUFENABLE 0x00000040 /* Enable ADC buffer interrupts */ +#define INTE_EFXBUFENABLE 0x00000020 /* Enable Effects buffer interrupts */ +#define INTE_GPSPDIFENABLE 0x00000010 /* Enable GPSPDIF status interrupts */ +#define INTE_CDSPDIFENABLE 0x00000008 /* Enable CDSPDIF status interrupts */ +#define INTE_INTERVALTIMERENB 0x00000004 /* Enable interval timer interrupts */ +#define INTE_MIDITXENABLE 0x00000002 /* Enable MIDI transmit-buffer-empty interrupts */ +#define INTE_MIDIRXENABLE 0x00000001 /* Enable MIDI receive-buffer-empty interrupts */ + +#define WC 0x10 /* Wall Clock register */ +#define WC_SAMPLECOUNTER_MASK 0x03FFFFC0 /* Sample periods elapsed since reset */ +#define WC_SAMPLECOUNTER 0x14060010 +#define WC_CURRENTCHANNEL 0x0000003F /* Channel [0..63] currently being serviced */ + /* NOTE: Each channel takes 1/64th of a sample */ + /* period to be serviced. */ + +#define HCFG 0x14 /* Hardware config register */ + /* NOTE: There is no reason to use the legacy */ + /* SoundBlaster emulation stuff described below */ + /* under Linux, and all kinds of weird hardware */ + /* behavior can result if you try. Don't. */ +#define HCFG_LEGACYFUNC_MASK 0xe0000000 /* Legacy function number */ +#define HCFG_LEGACYFUNC_MPU 0x00000000 /* Legacy MPU */ +#define HCFG_LEGACYFUNC_SB 0x40000000 /* Legacy SB */ +#define HCFG_LEGACYFUNC_AD 0x60000000 /* Legacy AD */ +#define HCFG_LEGACYFUNC_MPIC 0x80000000 /* Legacy MPIC */ +#define HCFG_LEGACYFUNC_MDMA 0xa0000000 /* Legacy MDMA */ +#define HCFG_LEGACYFUNC_SPCI 0xc0000000 /* Legacy SPCI */ +#define HCFG_LEGACYFUNC_SDMA 0xe0000000 /* Legacy SDMA */ +#define HCFG_IOCAPTUREADDR 0x1f000000 /* The 4 LSBs of the captured I/O address. */ +#define HCFG_LEGACYWRITE 0x00800000 /* 1 = write, 0 = read */ +#define HCFG_LEGACYWORD 0x00400000 /* 1 = word, 0 = byte */ +#define HCFG_LEGACYINT 0x00200000 /* 1 = legacy event captured. Write 1 to clear. */ + /* NOTE: The rest of the bits in this register */ + /* _are_ relevant under Linux. */ +#define HCFG_CODECFORMAT_MASK 0x00070000 /* CODEC format */ +#define HCFG_CODECFORMAT_AC97 0x00000000 /* AC97 CODEC format -- Primary Output */ +#define HCFG_CODECFORMAT_I2S 0x00010000 /* I2S CODEC format -- Secondary (Rear) Output */ +#define HCFG_GPINPUT0 0x00004000 /* External pin112 */ +#define HCFG_GPINPUT1 0x00002000 /* External pin110 */ +#define HCFG_GPOUTPUT_MASK 0x00001c00 /* External pins which may be controlled */ +#define HCFG_GPOUT0 0x00001000 /* External pin? (spdif enable on 5.1) */ +#define HCFG_GPOUT1 0x00000800 /* External pin? (IR) */ +#define HCFG_GPOUT2 0x00000400 /* External pin? (IR) */ +#define HCFG_JOYENABLE 0x00000200 /* Internal joystick enable */ +#define HCFG_PHASETRACKENABLE 0x00000100 /* Phase tracking enable */ + /* 1 = Force all 3 async digital inputs to use */ + /* the same async sample rate tracker (ZVIDEO) */ +#define HCFG_AC3ENABLE_MASK 0x000000e0 /* AC3 async input control - Not implemented */ +#define HCFG_AC3ENABLE_ZVIDEO 0x00000080 /* Channels 0 and 1 replace ZVIDEO */ +#define HCFG_AC3ENABLE_CDSPDIF 0x00000040 /* Channels 0 and 1 replace CDSPDIF */ +#define HCFG_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */ +#define HCFG_AUTOMUTE 0x00000010 /* When set, the async sample rate convertors */ + /* will automatically mute their output when */ + /* they are not rate-locked to the external */ + /* async audio source */ +#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE_MASK 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE 0x01020014 +#define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Master mute button sets AUDIOENABLE = 0. */ + /* NOTE: This is a 'cheap' way to implement a */ + /* master mute function on the mute button, and */ + /* in general should not be used unless a more */ + /* sophisticated master mute function has not */ + /* been written. */ +#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ + /* Should be set to 1 when the EMU10K1 is */ + /* completely initialized. */ + +//For Audigy, MPU port move to 0x70-0x74 ptr register + +#define MUDATA 0x18 /* MPU401 data register (8 bits) */ + +#define MUCMD 0x19 /* MPU401 command register (8 bits) */ +#define MUCMD_RESET 0xff /* RESET command */ +#define MUCMD_ENTERUARTMODE 0x3f /* Enter_UART_mode command */ + /* NOTE: All other commands are ignored */ + +#define MUSTAT MUCMD /* MPU401 status register (8 bits) */ +#define MUSTAT_IRDYN 0x80 /* 0 = MIDI data or command ACK */ +#define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ + +#define A_IOCFG 0x18 /* GPIO on Audigy card (16bits) */ +#define A_GPINPUT_MASK 0xff00 +#define A_GPOUTPUT_MASK 0x00ff + +#define TIMER 0x1a /* Timer terminal count register */ + /* NOTE: After the rate is changed, a maximum */ + /* of 1024 sample periods should be allowed */ + /* before the new rate is guaranteed accurate. */ +#define TIMER_RATE_MASK 0x000003ff /* Timer interrupt rate in sample periods */ + /* 0 == 1024 periods, [1..4] are not useful */ +#define TIMER_RATE 0x0a00001a + +#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ + +#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ +#define AC97ADDRESS_READY 0x80 /* Read-only bit, reflects CODEC READY signal */ +#define AC97ADDRESS_ADDRESS 0x7f /* Address of indexed AC97 register */ + +/************************************************************************************************/ +/* PCI function 1 registers, address = + PCIBASE1 */ +/************************************************************************************************/ + +#define JOYSTICK1 0x00 /* Analog joystick port register */ +#define JOYSTICK2 0x01 /* Analog joystick port register */ +#define JOYSTICK3 0x02 /* Analog joystick port register */ +#define JOYSTICK4 0x03 /* Analog joystick port register */ +#define JOYSTICK5 0x04 /* Analog joystick port register */ +#define JOYSTICK6 0x05 /* Analog joystick port register */ +#define JOYSTICK7 0x06 /* Analog joystick port register */ +#define JOYSTICK8 0x07 /* Analog joystick port register */ + +/* When writing, any write causes JOYSTICK_COMPARATOR output enable to be pulsed on write. */ +/* When reading, use these bitfields: */ +#define JOYSTICK_BUTTONS 0x0f /* Joystick button data */ +#define JOYSTICK_COMPARATOR 0xf0 /* Joystick comparator data */ + + +/********************************************************************************************************/ +/* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers */ +/********************************************************************************************************/ + +#define CPF 0x00 /* Current pitch and fraction register */ +#define CPF_CURRENTPITCH_MASK 0xffff0000 /* Current pitch (linear, 0x4000 == unity pitch shift) */ +#define CPF_CURRENTPITCH 0x10100000 +#define CPF_STEREO_MASK 0x00008000 /* 1 = Even channel interleave, odd channel locked */ +#define CPF_STOP_MASK 0x00004000 /* 1 = Current pitch forced to 0 */ +#define CPF_FRACADDRESS_MASK 0x00003fff /* Linear fractional address of the current channel */ + +#define PTRX 0x01 /* Pitch target and send A/B amounts register */ +#define PTRX_PITCHTARGET_MASK 0xffff0000 /* Pitch target of specified channel */ +#define PTRX_PITCHTARGET 0x10100001 +#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00 /* Linear level of channel output sent to FX send bus A */ +#define PTRX_FXSENDAMOUNT_A 0x08080001 +#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff /* Linear level of channel output sent to FX send bus B */ +#define PTRX_FXSENDAMOUNT_B 0x08000001 + +#define CVCF 0x02 /* Current volume and filter cutoff register */ +#define CVCF_CURRENTVOL_MASK 0xffff0000 /* Current linear volume of specified channel */ +#define CVCF_CURRENTVOL 0x10100002 +#define CVCF_CURRENTFILTER_MASK 0x0000ffff /* Current filter cutoff frequency of specified channel */ +#define CVCF_CURRENTFILTER 0x10000002 + +#define VTFT 0x03 /* Volume target and filter cutoff target register */ +#define VTFT_VOLUMETARGET_MASK 0xffff0000 /* Volume target of specified channel */ +#define VTFT_VOLUMETARGET 0x10100003 +#define VTFT_FILTERTARGET_MASK 0x0000ffff /* Filter cutoff target of specified channel */ +#define VTFT_FILTERTARGET 0x10000003 + +#define Z1 0x05 /* Filter delay memory 1 register */ + +#define Z2 0x04 /* Filter delay memory 2 register */ + +#define PSST 0x06 /* Send C amount and loop start address register */ +#define PSST_FXSENDAMOUNT_C_MASK 0xff000000 /* Linear level of channel output sent to FX send bus C */ + +#define PSST_FXSENDAMOUNT_C 0x08180006 + +#define PSST_LOOPSTARTADDR_MASK 0x00ffffff /* Loop start address of the specified channel */ +#define PSST_LOOPSTARTADDR 0x18000006 + +#define DSL 0x07 /* Send D amount and loop start address register */ +#define DSL_FXSENDAMOUNT_D_MASK 0xff000000 /* Linear level of channel output sent to FX send bus D */ + +#define DSL_FXSENDAMOUNT_D 0x08180007 + +#define DSL_LOOPENDADDR_MASK 0x00ffffff /* Loop end address of the specified channel */ +#define DSL_LOOPENDADDR 0x18000007 + +#define CCCA 0x08 /* Filter Q, interp. ROM, byte size, cur. addr register */ +#define CCCA_RESONANCE 0xf0000000 /* Lowpass filter resonance (Q) height */ +#define CCCA_INTERPROMMASK 0x0e000000 /* Selects passband of interpolation ROM */ + /* 1 == full band, 7 == lowpass */ + /* ROM 0 is used when pitch shifting downward or less */ + /* then 3 semitones upward. Increasingly higher ROM */ + /* numbers are used, typically in steps of 3 semitones, */ + /* as upward pitch shifting is performed. */ +#define CCCA_INTERPROM_0 0x00000000 /* Select interpolation ROM 0 */ +#define CCCA_INTERPROM_1 0x02000000 /* Select interpolation ROM 1 */ +#define CCCA_INTERPROM_2 0x04000000 /* Select interpolation ROM 2 */ +#define CCCA_INTERPROM_3 0x06000000 /* Select interpolation ROM 3 */ +#define CCCA_INTERPROM_4 0x08000000 /* Select interpolation ROM 4 */ +#define CCCA_INTERPROM_5 0x0a000000 /* Select interpolation ROM 5 */ +#define CCCA_INTERPROM_6 0x0c000000 /* Select interpolation ROM 6 */ +#define CCCA_INTERPROM_7 0x0e000000 /* Select interpolation ROM 7 */ +#define CCCA_8BITSELECT 0x01000000 /* 1 = Sound memory for this channel uses 8-bit samples */ +#define CCCA_CURRADDR_MASK 0x00ffffff /* Current address of the selected channel */ +#define CCCA_CURRADDR 0x18000008 + +#define CCR 0x09 /* Cache control register */ +#define CCR_CACHEINVALIDSIZE 0x07190009 +#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000 /* Number of invalid samples cache for this channel */ +#define CCR_CACHELOOPFLAG 0x01000000 /* 1 = Cache has a loop service pending */ +#define CCR_INTERLEAVEDSAMPLES 0x00800000 /* 1 = A cache service will fetch interleaved samples */ +#define CCR_WORDSIZEDSAMPLES 0x00400000 /* 1 = A cache service will fetch word sized samples */ +#define CCR_READADDRESS 0x06100009 +#define CCR_READADDRESS_MASK 0x003f0000 /* Location of cache just beyond current cache service */ +#define CCR_LOOPINVALSIZE 0x0000fe00 /* Number of invalid samples in cache prior to loop */ + /* NOTE: This is valid only if CACHELOOPFLAG is set */ +#define CCR_LOOPFLAG 0x00000100 /* Set for a single sample period when a loop occurs */ +#define CCR_CACHELOOPADDRHI 0x000000ff /* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */ + +#define CLP 0x0a /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */ + /* NOTE: This register is normally not used */ +#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address (DSL_LOOPSTARTADDR [0..15]) */ + +#define FXRT 0x0b /* Effects send routing register */ + /* NOTE: It is illegal to assign the same routing to */ + /* two effects sends. */ +#define FXRT_CHANNELA 0x000f0000 /* Effects send bus number for channel's effects send A */ +#define FXRT_CHANNELB 0x00f00000 /* Effects send bus number for channel's effects send B */ +#define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */ +#define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */ + +#define MAPA 0x0c /* Cache map A */ + +#define MAPB 0x0d /* Cache map B */ + +#define MAP_PTE_MASK 0xffffe000 /* The 19 MSBs of the PTE indexed by the PTI */ +#define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */ + +#define ENVVOL 0x10 /* Volume envelope register */ +#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ATKHLDV 0x11 /* Volume envelope hold and attack register */ +#define ATKHLDV_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDV_HOLDTIME_MASK 0x00007f00 /* Envelope hold time (127-n == n*88.2msec) */ +#define ATKHLDV_ATTACKTIME_MASK 0x0000007f /* Envelope attack time, log encoded */ + /* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec */ + +#define DCYSUSV 0x12 /* Volume envelope sustain and decay register */ +#define DCYSUSV_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSV_CHANNELENABLE_MASK 0x00000080 /* 1 = Inhibit envelope engine from writing values in */ + /* this channel and from writing to pitch, filter and */ + /* volume targets. */ +#define DCYSUSV_DECAYTIME_MASK 0x0000007f /* Volume envelope decay time, log encoded */ + /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL1 0x13 /* Modulation LFO value */ +#define LFOVAL_MASK 0x0000ffff /* Current value of modulation LFO state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ENVVAL 0x14 /* Modulation envelope register */ +#define ENVVAL_MASK 0x0000ffff /* Current value of modulation envelope state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ATKHLDM 0x15 /* Modulation envelope hold and attack register */ +#define ATKHLDM_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDM_HOLDTIME 0x00007f00 /* Envelope hold time (127-n == n*42msec) */ +#define ATKHLDM_ATTACKTIME 0x0000007f /* Envelope attack time, log encoded */ + /* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec */ + +#define DCYSUSM 0x16 /* Modulation envelope decay and sustain register */ +#define DCYSUSM_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSM_DECAYTIME_MASK 0x0000007f /* Envelope decay time, log encoded */ + /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL2 0x17 /* Vibrato LFO register */ +#define LFOVAL2_MASK 0x0000ffff /* Current value of vibrato LFO state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define IP 0x18 /* Initial pitch register */ +#define IP_MASK 0x0000ffff /* Exponential initial pitch shift */ + /* 4 bits of octave, 12 bits of fractional octave */ +#define IP_UNITY 0x0000e000 /* Unity pitch shift */ + +#define IFATN 0x19 /* Initial filter cutoff and attenuation register */ +#define IFATN_FILTERCUTOFF_MASK 0x0000ff00 /* Initial filter cutoff frequency in exponential units */ + /* 6 most significant bits are semitones */ + /* 2 least significant bits are fractions */ +#define IFATN_FILTERCUTOFF 0x08080019 +#define IFATN_ATTENUATION_MASK 0x000000ff /* Initial attenuation in 0.375dB steps */ +#define IFATN_ATTENUATION 0x08000019 + + +#define PEFE 0x1a /* Pitch envelope and filter envelope amount register */ +#define PEFE_PITCHAMOUNT_MASK 0x0000ff00 /* Pitch envlope amount */ + /* Signed 2's complement, +/- one octave peak extremes */ +#define PEFE_PITCHAMOUNT 0x0808001a +#define PEFE_FILTERAMOUNT_MASK 0x000000ff /* Filter envlope amount */ + /* Signed 2's complement, +/- six octaves peak extremes */ +#define PEFE_FILTERAMOUNT 0x0800001a +#define FMMOD 0x1b /* Vibrato/filter modulation from LFO register */ +#define FMMOD_MODVIBRATO 0x0000ff00 /* Vibrato LFO modulation depth */ + /* Signed 2's complement, +/- one octave extremes */ +#define FMMOD_MOFILTER 0x000000ff /* Filter LFO modulation depth */ + /* Signed 2's complement, +/- three octave extremes */ + + +#define TREMFRQ 0x1c /* Tremolo amount and modulation LFO frequency register */ +#define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */ + /* Signed 2's complement, with +/- 12dB extremes */ + +#define TREMFRQ_FREQUENCY 0x000000ff /* Tremolo LFO frequency */ + /* ??Hz steps, maximum of ?? Hz. */ +#define FM2FRQ2 0x1d /* Vibrato amount and vibrato LFO frequency register */ +#define FM2FRQ2_DEPTH 0x0000ff00 /* Vibrato LFO vibrato depth */ + /* Signed 2's complement, +/- one octave extremes */ +#define FM2FRQ2_FREQUENCY 0x000000ff /* Vibrato LFO frequency */ + /* 0.039Hz steps, maximum of 9.85 Hz. */ + +#define TEMPENV 0x1e /* Tempory envelope register */ +#define TEMPENV_MASK 0x0000ffff /* 16-bit value */ + /* NOTE: All channels contain internal variables; do */ + /* not write to these locations. */ + +#define CD0 0x20 /* Cache data 0 register */ +#define CD1 0x21 /* Cache data 1 register */ +#define CD2 0x22 /* Cache data 2 register */ +#define CD3 0x23 /* Cache data 3 register */ +#define CD4 0x24 /* Cache data 4 register */ +#define CD5 0x25 /* Cache data 5 register */ +#define CD6 0x26 /* Cache data 6 register */ +#define CD7 0x27 /* Cache data 7 register */ +#define CD8 0x28 /* Cache data 8 register */ +#define CD9 0x29 /* Cache data 9 register */ +#define CDA 0x2a /* Cache data A register */ +#define CDB 0x2b /* Cache data B register */ +#define CDC 0x2c /* Cache data C register */ +#define CDD 0x2d /* Cache data D register */ +#define CDE 0x2e /* Cache data E register */ +#define CDF 0x2f /* Cache data F register */ + +#define PTB 0x40 /* Page table base register */ +#define PTB_MASK 0xfffff000 /* Physical address of the page table in host memory */ + +#define TCB 0x41 /* Tank cache base register */ +#define TCB_MASK 0xfffff000 /* Physical address of the bottom of host based TRAM */ + +#define ADCCR 0x42 /* ADC sample rate/stereo control register */ +#define ADCCR_RCHANENABLE 0x00000010 /* Enables right channel for writing to the host */ +#define ADCCR_LCHANENABLE 0x00000008 /* Enables left channel for writing to the host */ + /* NOTE: To guarantee phase coherency, both channels */ + /* must be disabled prior to enabling both channels. */ +#define A_ADCCR_RCHANENABLE 0x00000020 +#define A_ADCCR_LCHANENABLE 0x00000010 + +#define A_ADCCR_SAMPLERATE_MASK 0x0000000F /* Audigy sample rate convertor output rate */ +#define ADCCR_SAMPLERATE_MASK 0x00000007 /* Sample rate convertor output rate */ +#define ADCCR_SAMPLERATE_48 0x00000000 /* 48kHz sample rate */ +#define ADCCR_SAMPLERATE_44 0x00000001 /* 44.1kHz sample rate */ +#define ADCCR_SAMPLERATE_32 0x00000002 /* 32kHz sample rate */ +#define ADCCR_SAMPLERATE_24 0x00000003 /* 24kHz sample rate */ +#define ADCCR_SAMPLERATE_22 0x00000004 /* 22.05kHz sample rate */ +#define ADCCR_SAMPLERATE_16 0x00000005 /* 16kHz sample rate */ +#define ADCCR_SAMPLERATE_11 0x00000006 /* 11.025kHz sample rate */ +#define ADCCR_SAMPLERATE_8 0x00000007 /* 8kHz sample rate */ +#define A_ADCCR_SAMPLERATE_12 0x00000006 /* 12kHz sample rate */ +#define A_ADCCR_SAMPLERATE_11 0x00000007 /* 11.025kHz sample rate */ +#define A_ADCCR_SAMPLERATE_8 0x00000008 /* 8kHz sample rate */ + +#define FXWC 0x43 /* FX output write channels register */ + /* When set, each bit enables the writing of the */ + /* corresponding FX output channel into host memory */ +#define FXWC_DEFAULTROUTE_C (1<<0) /* left emu out? */ +#define FXWC_DEFAULTROUTE_B (1<<1) /* right emu out? */ +#define FXWC_DEFAULTROUTE_A (1<<12) +#define FXWC_DEFAULTROUTE_D (1<<13) +#define FXWC_ADCLEFT (1<<18) +#define FXWC_CDROMSPDIFLEFT (1<<18) +#define FXWC_ADCRIGHT (1<<19) +#define FXWC_CDROMSPDIFRIGHT (1<<19) +#define FXWC_MIC (1<<20) +#define FXWC_ZOOMLEFT (1<<20) +#define FXWC_ZOOMRIGHT (1<<21) +#define FXWC_SPDIFLEFT (1<<22) /* 0x00400000 */ +#define FXWC_SPDIFRIGHT (1<<23) /* 0x00800000 */ + +#define TCBS 0x44 /* Tank cache buffer size register */ +#define TCBS_MASK 0x00000007 /* Tank cache buffer size field */ +#define TCBS_BUFFSIZE_16K 0x00000000 +#define TCBS_BUFFSIZE_32K 0x00000001 +#define TCBS_BUFFSIZE_64K 0x00000002 +#define TCBS_BUFFSIZE_128K 0x00000003 +#define TCBS_BUFFSIZE_256K 0x00000004 +#define TCBS_BUFFSIZE_512K 0x00000005 +#define TCBS_BUFFSIZE_1024K 0x00000006 +#define TCBS_BUFFSIZE_2048K 0x00000007 + +#define MICBA 0x45 /* AC97 microphone buffer address register */ +#define MICBA_MASK 0xfffff000 /* 20 bit base address */ + +#define ADCBA 0x46 /* ADC buffer address register */ +#define ADCBA_MASK 0xfffff000 /* 20 bit base address */ + +#define FXBA 0x47 /* FX Buffer Address */ +#define FXBA_MASK 0xfffff000 /* 20 bit base address */ + +#define MICBS 0x49 /* Microphone buffer size register */ + +#define ADCBS 0x4a /* ADC buffer size register */ + +#define FXBS 0x4b /* FX buffer size register */ + +/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */ +#define ADCBS_BUFSIZE_NONE 0x00000000 +#define ADCBS_BUFSIZE_384 0x00000001 +#define ADCBS_BUFSIZE_448 0x00000002 +#define ADCBS_BUFSIZE_512 0x00000003 +#define ADCBS_BUFSIZE_640 0x00000004 +#define ADCBS_BUFSIZE_768 0x00000005 +#define ADCBS_BUFSIZE_896 0x00000006 +#define ADCBS_BUFSIZE_1024 0x00000007 +#define ADCBS_BUFSIZE_1280 0x00000008 +#define ADCBS_BUFSIZE_1536 0x00000009 +#define ADCBS_BUFSIZE_1792 0x0000000a +#define ADCBS_BUFSIZE_2048 0x0000000b +#define ADCBS_BUFSIZE_2560 0x0000000c +#define ADCBS_BUFSIZE_3072 0x0000000d +#define ADCBS_BUFSIZE_3584 0x0000000e +#define ADCBS_BUFSIZE_4096 0x0000000f +#define ADCBS_BUFSIZE_5120 0x00000010 +#define ADCBS_BUFSIZE_6144 0x00000011 +#define ADCBS_BUFSIZE_7168 0x00000012 +#define ADCBS_BUFSIZE_8192 0x00000013 +#define ADCBS_BUFSIZE_10240 0x00000014 +#define ADCBS_BUFSIZE_12288 0x00000015 +#define ADCBS_BUFSIZE_14366 0x00000016 +#define ADCBS_BUFSIZE_16384 0x00000017 +#define ADCBS_BUFSIZE_20480 0x00000018 +#define ADCBS_BUFSIZE_24576 0x00000019 +#define ADCBS_BUFSIZE_28672 0x0000001a +#define ADCBS_BUFSIZE_32768 0x0000001b +#define ADCBS_BUFSIZE_40960 0x0000001c +#define ADCBS_BUFSIZE_49152 0x0000001d +#define ADCBS_BUFSIZE_57344 0x0000001e +#define ADCBS_BUFSIZE_65536 0x0000001f + + +#define CDCS 0x50 /* CD-ROM digital channel status register */ + +#define GPSCS 0x51 /* General Purpose SPDIF channel status register*/ + +#define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ + +#define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ + +#define A_DBG 0x53 +#define A_DBG_SINGLE_STEP_ADDR 0x00020000 /* Set to zero to start dsp */ +#define A_DBG_ZC 0x40000000 /* zero tram counter */ +#define A_DBG_STEP_ADDR 0x000003ff +#define A_DBG_SATURATION_OCCURED 0x20000000 +#define A_DBG_SATURATION_ADDR 0x0ffc0000 + +#define SPCS0 0x54 /* SPDIF output Channel Status 0 register */ + +#define SPCS1 0x55 /* SPDIF output Channel Status 1 register */ + +#define SPCS2 0x56 /* SPDIF output Channel Status 2 register */ + +#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */ +#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */ +#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */ +#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */ +#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */ +#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */ +#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */ +#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */ +#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */ +#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */ +#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */ +#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */ +#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */ +#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */ +#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */ +#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */ +#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */ +#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */ +#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */ +#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */ +#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */ +#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */ +#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */ + +/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status */ +#define CLIEL 0x58 /* Channel loop interrupt enable low register */ + +#define CLIEH 0x59 /* Channel loop interrupt enable high register */ + +#define CLIPL 0x5a /* Channel loop interrupt pending low register */ + +#define CLIPH 0x5b /* Channel loop interrupt pending high register */ + +#define SOLEL 0x5c /* Stop on loop enable low register */ + +#define SOLEH 0x5d /* Stop on loop enable high register */ + +#define SPBYPASS 0x5e /* SPDIF BYPASS mode register */ +#define SPBYPASS_ENABLE 0x00000001 /* Enable SPDIF bypass mode */ + +#define AC97SLOT 0x5f /* additional AC97 slots enable bits */ +#define AC97SLOT_CNTR 0x10 /* Center enable */ +#define AC97SLOT_LFE 0x20 /* LFE enable */ + +#define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */ + +#define GPSRCS 0x61 /* General Purpose SPDIF sample rate cvt status */ + +#define ZVSRCS 0x62 /* ZVideo sample rate converter status */ + /* NOTE: This one has no SPDIFLOCKED field */ + /* Assumes sample lock */ + +/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS. */ +#define SRCS_SPDIFLOCKED 0x02000000 /* SPDIF stream locked */ +#define SRCS_RATELOCKED 0x01000000 /* Sample rate locked */ +#define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */ + +#define MICIDX 0x63 /* Microphone recording buffer index register */ +#define MICIDX_MASK 0x0000ffff /* 16-bit value */ +#define MICIDX_IDX 0x10000063 + +#define ADCIDX 0x64 /* ADC recording buffer index register */ +#define ADCIDX_MASK 0x0000ffff /* 16 bit index field */ +#define ADCIDX_IDX 0x10000064 + +#define A_ADCIDX 0x63 +#define A_ADCIDX_IDX 0x10000063 + +#define FXIDX 0x65 /* FX recording buffer index register */ +#define FXIDX_MASK 0x0000ffff /* 16-bit value */ +#define FXIDX_IDX 0x10000065 + +/* This is the MPU port on the card (via the game port) */ +#define A_MUDATA1 0x70 +#define A_MUCMD1 0x71 +#define A_MUSTAT1 A_MUCMD1 + +/* This is the MPU port on the Audigy Drive */ +#define A_MUDATA2 0x72 +#define A_MUCMD2 0x73 +#define A_MUSTAT2 A_MUCMD2 + +#define A_SPDIF_SAMPLERATE 0x76 /* Set the sample rate of SPDIF output */ +#define A_SPDIF_48000 0x00000000 +#define A_SPDIF_44100 0x00000040 +#define A_SPDIF_96000 0x00000080 + +#define A_FXRT2 0x7c +#define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send A */ +#define A_FXRT_CHANNELF 0x00003f00 /* Effects send bus number for channel's effects send B */ +#define A_FXRT_CHANNELG 0x003f0000 /* Effects send bus number for channel's effects send C */ +#define A_FXRT_CHANNELH 0x3f000000 /* Effects send bus number for channel's effects send D */ + +#define A_SENDAMOUNTS 0x7d +#define A_FXSENDAMOUNT_E_MASK 0xFF000000 +#define A_FXSENDAMOUNT_F_MASK 0x00FF0000 +#define A_FXSENDAMOUNT_G_MASK 0x0000FF00 +#define A_FXSENDAMOUNT_H_MASK 0x000000FF + +/* The send amounts for this one are the same as used with the emu10k1 */ +#define A_FXRT1 0x7e +#define A_FXRT_CHANNELA 0x0000003f +#define A_FXRT_CHANNELB 0x00003f00 +#define A_FXRT_CHANNELC 0x003f0000 +#define A_FXRT_CHANNELD 0x3f000000 + + +/* Each FX general purpose register is 32 bits in length, all bits are used */ +#define FXGPREGBASE 0x100 /* FX general purpose registers base */ +#define A_FXGPREGBASE 0x400 /* Audigy GPRs, 0x400 to 0x5ff */ + +/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */ +/* decompressed back to 20 bits on a read. There are a total of 160 locations, the last 32 */ +/* locations are for external TRAM. */ +#define TANKMEMDATAREGBASE 0x200 /* Tank memory data registers base */ +#define TANKMEMDATAREG_MASK 0x000fffff /* 20 bit tank audio data field */ + +/* Combined address field and memory opcode or flag field. 160 locations, last 32 are external */ +#define TANKMEMADDRREGBASE 0x300 /* Tank memory address registers base */ +#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ +#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ +#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ +#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ +#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ + +#define MICROCODEBASE 0x400 /* Microcode data base address */ + +/* Each DSP microcode instruction is mapped into 2 doublewords */ +/* NOTE: When writing, always write the LO doubleword first. Reads can be in either order. */ +#define LOWORD_OPX_MASK 0x000ffc00 /* Instruction operand X */ +#define LOWORD_OPY_MASK 0x000003ff /* Instruction operand Y */ +#define HIWORD_OPCODE_MASK 0x00f00000 /* Instruction opcode */ +#define HIWORD_RESULT_MASK 0x000ffc00 /* Instruction result */ +#define HIWORD_OPA_MASK 0x000003ff /* Instruction operand A */ + + +/* Audigy Soundcard have a different instruction format */ +#define A_MICROCODEBASE 0x600 +#define A_LOWORD_OPY_MASK 0x000007ff +#define A_LOWORD_OPX_MASK 0x007ff000 +#define A_HIWORD_OPCODE_MASK 0x0f000000 +#define A_HIWORD_RESULT_MASK 0x007ff000 +#define A_HIWORD_OPA_MASK 0x000007ff + + +/* ------------------- STRUCTURES -------------------- */ + +typedef struct _snd_emu10k1 emu10k1_t; +typedef struct _snd_emu10k1_voice emu10k1_voice_t; +typedef struct _snd_emu10k1_pcm emu10k1_pcm_t; + +typedef enum { + EMU10K1_PCM, + EMU10K1_SYNTH, + EMU10K1_MIDI +} emu10k1_voice_type_t; + +struct _snd_emu10k1_voice { + emu10k1_t *emu; + int number; + int use: 1, + pcm: 1, + synth: 1, + midi: 1; + void (*interrupt)(emu10k1_t *emu, emu10k1_voice_t *pvoice); + + emu10k1_pcm_t *epcm; +}; + +typedef enum { + PLAYBACK_EMUVOICE, + CAPTURE_AC97ADC, + CAPTURE_AC97MIC, + CAPTURE_EFX +} snd_emu10k1_pcm_type_t; + +struct _snd_emu10k1_pcm { + emu10k1_t *emu; + snd_emu10k1_pcm_type_t type; + snd_pcm_substream_t *substream; + emu10k1_voice_t *voices[2]; + emu10k1_voice_t *extra; + int running; + snd_util_memblk_t *memblk; + unsigned int start_addr; + unsigned int ccca_start_addr; + unsigned int capture_ipr; /* interrupt acknowledge mask */ + unsigned int capture_inte; /* interrupt enable mask */ + unsigned int capture_ba_reg; /* buffer address register */ + unsigned int capture_bs_reg; /* buffer size register */ + unsigned int capture_idx_reg; /* buffer index register */ + unsigned int capture_cr_val; /* control value */ + unsigned int capture_bs_val; /* buffer size value */ + unsigned int capture_bufsize; /* buffer size in bytes */ +}; + +typedef struct { + unsigned long send_routing[3]; + unsigned char send_volume[3][4]; + unsigned short attn[3]; + snd_kcontrol_t *ctl_send_routing; + snd_kcontrol_t *ctl_send_volume; + snd_kcontrol_t *ctl_attn; + emu10k1_pcm_t *epcm; +} emu10k1_pcm_mixer_t; + +typedef struct snd_emu10k1_memblk { + snd_util_memblk_t mem; + /* private part */ + short first_page, last_page, pages, mapped_page; + unsigned int map_locked; + struct list_head mapped_link; + struct list_head mapped_order_link; +} emu10k1_memblk_t; + +#define snd_emu10k1_memblk_offset(blk) (((blk)->mapped_page << PAGE_SHIFT) | ((blk)->mem.offset & (PAGE_SIZE - 1))) + +#define EMU10K1_MAX_TRAM_BLOCKS_PER_CODE 16 + +typedef struct { + struct list_head list; /* list link container */ + unsigned int vcount; + unsigned int count; /* count of GPR (1..16) */ + unsigned char gpr[32]; /* GPR number(s) */ + unsigned int value[32]; + unsigned int min; /* minimum range */ + unsigned int max; /* maximum range */ + unsigned int translation; /* translation type (EMU10K1_GRP_TRANSLATION*) */ + snd_kcontrol_t *kcontrol; +} snd_emu10k1_fx8010_ctl_t; + +typedef void (snd_fx8010_irq_handler_t)(emu10k1_t *emu, void *private_data); + +typedef struct _snd_emu10k1_fx8010_irq { + struct _snd_emu10k1_fx8010_irq *next; + snd_fx8010_irq_handler_t *handler; + unsigned char gpr_running; + void *private_data; +} snd_emu10k1_fx8010_irq_t; + +typedef struct { + unsigned int valid: 1, + opened: 1, + active: 1; + unsigned int channels; /* 16-bit channels count */ + unsigned int tram_start; /* initial ring buffer position in TRAM (in samples) */ + unsigned int buffer_size; /* count of buffered samples */ + unsigned char gpr_size; /* GPR containing size of ring buffer in samples (host) */ + unsigned char gpr_ptr; /* GPR containing current pointer in the ring buffer (host = reset, FX8010) */ + unsigned char gpr_count; /* GPR containing count of samples between two interrupts (host) */ + unsigned char gpr_tmpcount; /* GPR containing current count of samples to interrupt (host = set, FX8010) */ + unsigned char gpr_trigger; /* GPR containing trigger (activate) information (host) */ + unsigned char gpr_running; /* GPR containing info if PCM is running (FX8010) */ + unsigned char etram[32]; /* external TRAM address & data */ + unsigned int sw_data, hw_data; + unsigned int sw_io, hw_io; + unsigned int sw_ready, hw_ready; + unsigned int appl_ptr; + unsigned int tram_pos; + unsigned int tram_shift; + snd_emu10k1_fx8010_irq_t *irq; +} snd_emu10k1_fx8010_pcm_t; + +typedef struct { + unsigned short fxbus_mask; /* used FX buses (bitmask) */ + unsigned short extin_mask; /* used external inputs (bitmask) */ + unsigned short extout_mask; /* used external outputs (bitmask) */ + unsigned short pad1; + unsigned int itram_size; /* internal TRAM size in samples */ + unsigned int etram_size; /* external TRAM size in samples */ + void *etram_pages; /* allocated pages for external TRAM */ + dma_addr_t etram_pages_dmaaddr; + unsigned int dbg; /* FX debugger register */ + unsigned char name[128]; + int gpr_size; /* size of allocated GPR controls */ + int gpr_count; /* count of used kcontrols */ + struct list_head gpr_ctl; /* GPR controls */ + struct semaphore lock; + snd_emu10k1_fx8010_pcm_t pcm[8]; + spinlock_t irq_lock; + snd_emu10k1_fx8010_irq_t *irq_handlers; +} snd_emu10k1_fx8010_t; + +#define emu10k1_gpr_ctl(n) list_entry(n, snd_emu10k1_fx8010_ctl_t, list) + +typedef struct { + struct _snd_emu10k1 *emu; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *substream_input; + snd_rawmidi_substream_t *substream_output; + unsigned int midi_mode; + spinlock_t input_lock; + spinlock_t output_lock; + spinlock_t open_lock; + int tx_enable, rx_enable; + int port; + int ipr_tx, ipr_rx; + void (*interrupt)(emu10k1_t *emu, unsigned int status); +} emu10k1_midi_t; + +struct _snd_emu10k1 { + int irq; + + unsigned long port; /* I/O port number */ + struct resource *res_port; + int APS: 1, /* APS flag */ + tos_link: 1; /* tos link detected */ + unsigned int audigy; /* is Audigy? */ + unsigned int revision; /* chip revision */ + unsigned int serial; /* serial number */ + unsigned short model; /* subsystem id */ + unsigned int card_type; /* EMU10K1_CARD_* */ + unsigned int ecard_ctrl; /* ecard control bits */ + int max_cache_pages; /* max memory size / PAGE_SIZE */ + void *silent_page; /* silent page */ + dma_addr_t silent_page_dmaaddr; + volatile unsigned int *ptb_pages; /* page table pages */ + dma_addr_t ptb_pages_dmaaddr; + snd_util_memhdr_t *memhdr; /* page allocation list */ + emu10k1_memblk_t *reserved_page; /* reserved page */ + + struct list_head mapped_link_head; + struct list_head mapped_order_link_head; + void **page_ptr_table; + unsigned long *page_addr_table; + spinlock_t memblk_lock; + + unsigned int spdif_bits[3]; /* s/pdif out setup */ + + snd_emu10k1_fx8010_t fx8010; /* FX8010 info */ + int gpr_base; + + ac97_t *ac97; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_t *pcm_mic; + snd_pcm_t *pcm_efx; + snd_pcm_t *pcm_fx8010; + + spinlock_t synth_lock; + void *synth; + int (*get_synth_voice)(emu10k1_t *emu); + + spinlock_t reg_lock; + spinlock_t emu_lock; + spinlock_t voice_lock; + struct semaphore ptb_lock; + + emu10k1_voice_t voices[64]; + emu10k1_pcm_mixer_t pcm_mixer[32]; + + void (*hwvol_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_mic_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_efx_interrupt)(emu10k1_t *emu, unsigned int status); + void (*timer_interrupt)(emu10k1_t *emu); + void (*spdif_interrupt)(emu10k1_t *emu, unsigned int status); + void (*dsp_interrupt)(emu10k1_t *emu); + + snd_pcm_substream_t *pcm_capture_substream; + snd_pcm_substream_t *pcm_capture_mic_substream; + snd_pcm_substream_t *pcm_capture_efx_substream; + + emu10k1_midi_t midi; + emu10k1_midi_t midi2; /* for audigy */ + + unsigned int efx_voices_mask; + + snd_info_entry_t *proc_entry; + snd_info_entry_t *proc_entry_fx8010_gpr; + snd_info_entry_t *proc_entry_fx8010_tram_data; + snd_info_entry_t *proc_entry_fx8010_tram_addr; + snd_info_entry_t *proc_entry_fx8010_code; + snd_info_entry_t *proc_entry_fx8010_iblocks; +}; + +int snd_emu10k1_create(snd_card_t * card, + struct pci_dev *pci, + unsigned short extin_mask, + unsigned short extout_mask, + long max_cache_bytes, + int enable_ir, + emu10k1_t ** remu); + +int snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_mixer(emu10k1_t * emu); +int snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t ** rhwdep); + +void snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +/* initialization */ +void snd_emu10k1_voice_init(emu10k1_t * emu, int voice); +int snd_emu10k1_init_efx(emu10k1_t *emu); +void snd_emu10k1_free_efx(emu10k1_t *emu); +int snd_emu10k1_fx8010_tram_setup(emu10k1_t *emu, u32 size); + +/* I/O functions */ +unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn); +void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data); +void snd_emu10k1_efx_write(emu10k1_t *emu, unsigned int pc, unsigned int data); +unsigned int snd_emu10k1_efx_read(emu10k1_t *emu, unsigned int pc); +void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb); +void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb); +void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait); +static inline unsigned int snd_emu10k1_wc(emu10k1_t *emu) { return (inl(emu->port + WC) >> 6) & 0xfffff; } +unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg); +void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data); +unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate); +unsigned char snd_emu10k1_sum_vol_attn(unsigned int value); + +/* memory allocation */ +snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, dma_addr_t addr, unsigned long size); +int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk); +snd_util_memblk_t *snd_emu10k1_synth_alloc(emu10k1_t *emu, unsigned int size); +int snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *blk); +int snd_emu10k1_synth_bzero(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, int size); +int snd_emu10k1_synth_copy_from_user(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, const char *data, int size); +int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk); + +/* voice allocation */ +int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice); +int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice); + +/* MIDI uart */ +int snd_emu10k1_midi(emu10k1_t * emu); +int snd_emu10k1_audigy_midi(emu10k1_t * emu); + +/* proc interface */ +int snd_emu10k1_proc_init(emu10k1_t * emu); +int snd_emu10k1_proc_done(emu10k1_t * emu); + +#endif /* __KERNEL__ */ + +/* + * ---- FX8010 ---- + */ + +#define EMU10K1_CARD_CREATIVE 0x00000000 +#define EMU10K1_CARD_EMUAPS 0x00000001 + +#define EMU10K1_FX8010_PCM_COUNT 8 + +/* instruction set */ +#define iMAC0 0x00 /* R = A + (X * Y >> 31) ; saturation */ +#define iMAC1 0x01 /* R = A + (-X * Y >> 31) ; saturation */ +#define iMAC2 0x02 /* R = A + (X * Y >> 31) ; wraparound */ +#define iMAC3 0x03 /* R = A + (-X * Y >> 31) ; wraparound */ +#define iMACINT0 0x04 /* R = A + X * Y ; saturation */ +#define iMACINT1 0x05 /* R = A + X * Y ; wraparound (31-bit) */ +#define iACC3 0x06 /* R = A + X + Y ; saturation */ +#define iMACMV 0x07 /* R = A, acc += X * Y >> 31 */ +#define iANDXOR 0x08 /* R = (A & X) ^ Y */ +#define iTSTNEG 0x09 /* R = (A >= Y) ? X : ~X */ +#define iLIMITGE 0x0a /* R = (A >= Y) ? X : Y */ +#define iLIMITLT 0x0b /* R = (A < Y) ? X : Y */ +#define iLOG 0x0c /* R = linear_data, A (log_data), X (max_exp), Y (format_word) */ +#define iEXP 0x0d /* R = log_data, A (linear_data), X (max_exp), Y (format_word) */ +#define iINTERP 0x0e /* R = A + (X * (Y - A) >> 31) ; saturation */ +#define iSKIP 0x0f /* R = A (cc_reg), X (count), Y (cc_test) */ + +/* GPRs */ +#define FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x0f */ +#define EXTIN(x) (0x10 + (x)) /* x = 0x00 - 0x0f */ +#define EXTOUT(x) (0x20 + (x)) /* x = 0x00 - 0x0f */ +#define C_00000000 0x40 +#define C_00000001 0x41 +#define C_00000002 0x42 +#define C_00000003 0x43 +#define C_00000004 0x44 +#define C_00000008 0x45 +#define C_00000010 0x46 +#define C_00000020 0x47 +#define C_00000100 0x48 +#define C_00010000 0x49 +#define C_00080000 0x4a +#define C_10000000 0x4b +#define C_20000000 0x4c +#define C_40000000 0x4d +#define C_80000000 0x4e +#define C_7fffffff 0x4f +#define C_ffffffff 0x50 +#define C_fffffffe 0x51 +#define C_c0000000 0x52 +#define C_4f1bbcdc 0x53 +#define C_5a7ef9db 0x54 +#define C_00100000 0x55 /* ?? */ +#define GPR_ACCU 0x56 /* ACCUM, accumulator */ +#define GPR_COND 0x57 /* CCR, condition register */ +#define GPR_NOISE0 0x58 /* noise source */ +#define GPR_NOISE1 0x59 /* noise source */ +#define GPR_IRQ 0x5a /* IRQ register */ +#define GPR_DBAC 0x5b /* TRAM Delay Base Address Counter */ +#define GPR(x) (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */ +#define ITRAM_DATA(x) (TANKMEMDATAREGBASE + (x)) /* x = 0x00 - 0x7f */ +#define ETRAM_DATA(x) (TANKMEMDATAREGBASE + 80 + (x)) /* x = 0x00 - 0x1f */ +#define ITRAM_ADDR(x) (TANKMEMADDRREGBASE + (x)) /* x = 0x00 - 0x7f */ +#define ETRAM_ADDR(x) (TANKMEMADDRREGBASE + 80 + (x)) /* x = 0x00 - 0x1f */ + +#define A_FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x3f? */ +#define A_EXTIN(x) (0x40 + (x)) /* x = 0x00 - 0x1f? */ +#define A_EXTOUT(x) (0x60 + (x)) /* x = 0x00 - 0x1f? */ +#define A_GPR(x) (A_FXGPREGBASE + (x)) + +/* cc_reg constants */ +#define CC_REG_NORMALIZED C_00000001 +#define CC_REG_BORROW C_00000002 +#define CC_REG_MINUS C_00000004 +#define CC_REG_ZERO C_00000008 +#define CC_REG_SATURATE C_00000010 +#define CC_REG_NONZERO C_00000100 + +/* FX buses */ +#define FXBUS_PCM_LEFT 0x00 +#define FXBUS_PCM_RIGHT 0x01 +#define FXBUS_PCM_LEFT_REAR 0x02 +#define FXBUS_PCM_RIGHT_REAR 0x03 +#define FXBUS_MIDI_LEFT 0x04 +#define FXBUS_MIDI_RIGHT 0x05 +#define FXBUS_PCM_CENTER 0x06 +#define FXBUS_PCM_LFE 0x07 +#define FXBUS_MIDI_REVERB 0x0c +#define FXBUS_MIDI_CHORUS 0x0d + +/* Inputs */ +#define EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ +#define EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ +#define EXTIN_SPDIF_CD_L 0x02 /* internal S/PDIF CD - onboard - left */ +#define EXTIN_SPDIF_CD_R 0x03 /* internal S/PDIF CD - onboard - right */ +#define EXTIN_ZOOM_L 0x04 /* Zoom Video I2S - left */ +#define EXTIN_ZOOM_R 0x05 /* Zoom Video I2S - right */ +#define EXTIN_TOSLINK_L 0x06 /* LiveDrive - TOSLink Optical - left */ +#define EXTIN_TOSLINK_R 0x07 /* LiveDrive - TOSLink Optical - right */ +#define EXTIN_LINE1_L 0x08 /* LiveDrive - Line/Mic 1 - left */ +#define EXTIN_LINE1_R 0x09 /* LiveDrive - Line/Mic 1 - right */ +#define EXTIN_COAX_SPDIF_L 0x0a /* LiveDrive - Coaxial S/PDIF - left */ +#define EXTIN_COAX_SPDIF_R 0x0b /* LiveDrive - Coaxial S/PDIF - right */ +#define EXTIN_LINE2_L 0x0c /* LiveDrive - Line/Mic 2 - left */ +#define EXTIN_LINE2_R 0x0d /* LiveDrive - Line/Mic 2 - right */ + +/* Outputs */ +#define EXTOUT_AC97_L 0x00 /* AC'97 playback channel - left */ +#define EXTOUT_AC97_R 0x01 /* AC'97 playback channel - right */ +#define EXTOUT_TOSLINK_L 0x02 /* LiveDrive - TOSLink Optical - left */ +#define EXTOUT_TOSLINK_R 0x03 /* LiveDrive - TOSLink Optical - right */ +#define EXTOUT_CENTER 0x04 /* SB Live 5.1 - center */ +#define EXTOUT_LFE 0x05 /* SB Live 5.1 - LFE */ +#define EXTOUT_HEADPHONE_L 0x06 /* LiveDrive - Headphone - left */ +#define EXTOUT_HEADPHONE_R 0x07 /* LiveDrive - Headphone - right */ +#define EXTOUT_REAR_L 0x08 /* Rear channel - left */ +#define EXTOUT_REAR_R 0x09 /* Rear channel - right */ +#define EXTOUT_ADC_CAP_L 0x0a /* ADC Capture buffer - left */ +#define EXTOUT_ADC_CAP_R 0x0b /* ADC Capture buffer - right */ +#define EXTOUT_MIC_CAP 0x0c /* MIC Capture buffer */ +#define EXTOUT_ACENTER 0x11 /* Analog Center */ +#define EXTOUT_ALFE 0x12 /* Analog LFE */ + +/* Audigy Inputs */ +#define A_EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ +#define A_EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ +#define A_EXTIN_SPDIF_CD_L 0x02 /* digital CD left */ +#define A_EXTIN_SPDIF_CD_R 0x03 /* digital CD left */ + +/* Audigiy Outputs */ +#define A_EXTOUT_FRONT_L 0x00 /* digital front left */ +#define A_EXTOUT_FRONT_R 0x01 /* right */ +#define A_EXTOUT_CENTER 0x02 /* digital front center */ +#define A_EXTOUT_LFE 0x03 /* digital front lfe */ +#define A_EXTOUT_HEADPHONE_L 0x04 /* headphone audigy drive left */ +#define A_EXTOUT_HEADPHONE_R 0x05 /* right */ +#define A_EXTOUT_REAR_L 0x06 /* digital rear left */ +#define A_EXTOUT_REAR_R 0x07 /* right */ +#define A_EXTOUT_AFRONT_L 0x08 /* analog front left */ +#define A_EXTOUT_AFRONT_R 0x09 /* right */ +#define A_EXTOUT_ACENTER 0x0a /* analog center */ +#define A_EXTOUT_ALFE 0x0b /* analog LFE */ +/* 0x0c ?? */ +/* 0x0d ?? */ +#define A_EXTOUT_AREAR_L 0x0e /* analog rear left */ +#define A_EXTOUT_AREAR_R 0x0f /* right */ +/* 0x10-0x15 ?? */ +#define A_EXTOUT_ADC_CAP_L 0x16 /* ADC capture buffer left */ +#define A_EXTOUT_ADC_CAP_R 0x17 /* right */ + +/* definitions for debug register */ +#define EMU10K1_DBG_ZC 0x80000000 /* zero tram counter */ +#define EMU10K1_DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ +#define EMU10K1_DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ +#define EMU10K1_DBG_SINGLE_STEP 0x00008000 /* single step mode */ +#define EMU10K1_DBG_STEP 0x00004000 /* start single step */ +#define EMU10K1_DBG_CONDITION_CODE 0x00003e00 /* condition code */ +#define EMU10K1_DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ + +/* tank memory address line */ +#ifndef __KERNEL__ +#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ +#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ +#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ +#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ +#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ +#endif + +typedef struct { + unsigned int card; /* card type */ + unsigned int internal_tram_size; /* in samples */ + unsigned int external_tram_size; /* in samples */ + char fxbus_names[16][32]; /* names of FXBUSes */ + char extin_names[16][32]; /* names of external inputs */ + char extout_names[32][32]; /* names of external outputs */ + unsigned int gpr_controls; /* count of GPR controls */ +} emu10k1_fx8010_info_t; + +#define EMU10K1_GPR_TRANSLATION_NONE 0 +#define EMU10K1_GPR_TRANSLATION_TABLE100 1 +#define EMU10K1_GRP_TRANSLATION_BASS 2 +#define EMU10K1_GRP_TRANSLATION_TREBLE 3 +#define EMU10K1_GPR_TRANSLATION_ONOFF 4 + +typedef struct { + snd_ctl_elem_id_t id; /* full control ID definition */ + unsigned int vcount; /* visible count */ + unsigned int count; /* count of GPR (1..16) */ + unsigned char gpr[32]; /* GPR number(s) */ + unsigned int value[32]; /* initial values */ + unsigned int min; /* minimum range */ + unsigned int max; /* maximum range */ + unsigned int translation; /* translation type (EMU10K1_GRP_TRANSLATION*) */ +} emu10k1_fx8010_control_gpr_t; + +typedef struct { + char name[128]; + unsigned int gpr_valid[0x100/32]; /* bitmask of valid initializers */ + unsigned int gpr_map[0x100]; /* initializers */ + unsigned int gpr_add_control_count; /* count of GPR controls to add/replace */ + emu10k1_fx8010_control_gpr_t *gpr_add_controls; /* GPR controls to add/replace */ + unsigned int gpr_del_control_count; /* count of GPR controls to remove */ + snd_ctl_elem_id_t *gpr_del_controls; /* IDs of GPR controls to remove */ + unsigned int tram_valid[0xa0/32]; /* bitmask of valid initializers */ + unsigned int tram_data_map[0xa0]; /* data initializers */ + unsigned int tram_addr_map[0xa0]; /* map initializers */ + unsigned int code_valid[512/32]; /* bitmask of valid instructions */ + unsigned int code[512][2]; /* one instruction - 64 bits */ +} emu10k1_fx8010_code_t; + +typedef struct { + unsigned int address; /* 31.bit == 1 -> external TRAM */ + unsigned int size; /* size in samples (4 bytes) */ + unsigned int *samples; /* pointer to samples (20-bit) */ + /* NULL->clear memory */ +} emu10k1_fx8010_tram_t; + +typedef struct { + unsigned int substream; /* substream number */ + unsigned int res1; /* reserved */ + unsigned int channels; /* 16-bit channels count, zero = remove this substream */ + unsigned int tram_start; /* ring buffer position in TRAM (in samples) */ + unsigned int buffer_size; /* count of buffered samples */ + unsigned char gpr_size; /* GPR containing size of ringbuffer in samples (host) */ + unsigned char gpr_ptr; /* GPR containing current pointer in the ring buffer (host = reset, FX8010) */ + unsigned char gpr_count; /* GPR containing count of samples between two interrupts (host) */ + unsigned char gpr_tmpcount; /* GPR containing current count of samples to interrupt (host = set, FX8010) */ + unsigned char gpr_trigger; /* GPR containing trigger (activate) information (host) */ + unsigned char gpr_running; /* GPR containing info if PCM is running (FX8010) */ + unsigned char pad; /* reserved */ + unsigned char etram[32]; /* external TRAM address & data (one per channel) */ + unsigned int res2; /* reserved */ +} emu10k1_fx8010_pcm_t; + +#define SNDRV_EMU10K1_IOCTL_INFO _IOR ('H', 0x10, emu10k1_fx8010_info_t) +#define SNDRV_EMU10K1_IOCTL_CODE_POKE _IOW ('H', 0x11, emu10k1_fx8010_code_t) +#define SNDRV_EMU10K1_IOCTL_CODE_PEEK _IOW ('H', 0x12, emu10k1_fx8010_code_t) +#define SNDRV_EMU10K1_IOCTL_TRAM_SETUP _IOW ('H', 0x20, int) +#define SNDRV_EMU10K1_IOCTL_TRAM_POKE _IOW ('H', 0x21, emu10k1_fx8010_tram_t) +#define SNDRV_EMU10K1_IOCTL_TRAM_PEEK _IOR ('H', 0x22, emu10k1_fx8010_tram_t) +#define SNDRV_EMU10K1_IOCTL_PCM_POKE _IOW ('H', 0x30, emu10k1_fx8010_pcm_t) +#define SNDRV_EMU10K1_IOCTL_PCM_PEEK _IOWR('H', 0x31, emu10k1_fx8010_pcm_t) +#define SNDRV_EMU10K1_IOCTL_STOP _IO ('H', 0x80) +#define SNDRV_EMU10K1_IOCTL_CONTINUE _IO ('H', 0x81) +#define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82) +#define SNDRV_EMU10K1_IOCTL_SINGLE_STEP _IOW ('H', 0x83, int) +#define SNDRV_EMU10K1_IOCTL_DBG_READ _IOR ('H', 0x84, int) + +#endif /* __SOUND_EMU10K1_H */ diff -Nru a/include/sound/emu10k1_synth.h b/include/sound/emu10k1_synth.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/emu10k1_synth.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,39 @@ +#ifndef __EMU10K1_SYNTH_H +#define __EMU10K1_SYNTH_H +/* + * Defines for the Emu10k1 WaveTable synth + * + * Copyright (C) 2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu10k1.h" +#include "emux_synth.h" + +/* sequencer device id */ +#define SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH "emu10k1-synth" + +/* argument for snd_seq_device_new */ +typedef struct snd_emu10k1_synth_arg { + emu10k1_t *hwptr; /* chip */ + int index; /* sequencer client index */ + int seq_ports; /* number of sequencer ports to be created */ + int max_voices; /* maximum number of voices for wavetable */ +} snd_emu10k1_synth_arg_t; + +#define EMU10K1_MAX_MEMSIZE (32 * 1024 * 1024) /* 32MB */ + +#endif diff -Nru a/include/sound/emu8000.h b/include/sound/emu8000.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/emu8000.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,116 @@ +#ifndef __SOUND_EMU8000_H +#define __SOUND_EMU8000_H +/* + * Defines for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emux_synth.h" +#include "seq_kernel.h" + +/* + * Hardware parameters. + */ +#define EMU8000_MAX_DRAM (28 * 1024 * 1024) /* Max on-board mem is 28Mb ???*/ +#define EMU8000_DRAM_OFFSET 0x200000 /* Beginning of on board ram */ +#define EMU8000_CHANNELS 32 /* Number of hardware channels */ +#define EMU8000_DRAM_VOICES 30 /* number of normal voices */ + +/* Flags to set a dma channel to read or write */ +#define EMU8000_RAM_READ 0 +#define EMU8000_RAM_WRITE 1 +#define EMU8000_RAM_CLOSE 2 + +enum { + EMU8000_CONTROL_BASS = 0, + EMU8000_CONTROL_TREBLE, + EMU8000_CONTROL_CHORUS_MODE, + EMU8000_CONTROL_REVERB_MODE, + EMU8000_CONTROL_FM_CHORUS_DEPTH, + EMU8000_CONTROL_FM_REVERB_DEPTH, + EMU8000_NUM_CONTROLS, +}; + +/* + * Structure to hold all state information for the emu8000 driver. + * + * Note 1: The chip supports 32 channels in hardware this is max_channels + * some of the channels may be used for other things so max_channels is + * the number in use for wave voices. + */ +typedef struct snd_emu8000 { + + snd_emux_t *emu; + + int index; /* sequencer client index */ + int seq_ports; /* number of sequencer ports */ + int fm_chorus_depth; /* FM OPL3 chorus depth */ + int fm_reverb_depth; /* FM OPL3 reverb depth */ + + int mem_size; /* memory size */ + unsigned long port1; /* Port usually base+0 */ + unsigned long port2; /* Port usually at base+0x400 */ + unsigned long port3; /* Port usually at base+0x800 */ + struct resource *res_port1; + struct resource *res_port2; + struct resource *res_port3; + unsigned short last_reg;/* Last register command */ + spinlock_t reg_lock; + + int dram_checked; + + snd_card_t *card; /* The card that this belongs to */ + + int chorus_mode; + int reverb_mode; + int bass_level; + int treble_level; + + snd_util_memhdr_t *memhdr; + + spinlock_t control_lock; + snd_kcontrol_t *controls[EMU8000_NUM_CONTROLS]; + +} emu8000_t; + +/* sequencer device id */ +#define SNDRV_SEQ_DEV_ID_EMU8000 "emu8000-synth" + + +/* exported functions */ +int snd_emu8000_new(snd_card_t *card, int device, long port, int seq_ports, snd_seq_device_t **ret); +void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, + unsigned int val); +unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, + unsigned int reg); +void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, + unsigned int val); +unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, + unsigned int reg); +void snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode); + +void snd_emu8000_init_fm(emu8000_t *emu); + +void snd_emu8000_update_chorus_mode(emu8000_t *emu); +void snd_emu8000_update_reverb_mode(emu8000_t *emu); +void snd_emu8000_update_equalizer(emu8000_t *emu); +int snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len); +int snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len); + +#endif /* __SOUND_EMU8000_H */ diff -Nru a/include/sound/emu8000_reg.h b/include/sound/emu8000_reg.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/emu8000_reg.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,207 @@ +#ifndef __SOUND_EMU8000_REG_H +#define __SOUND_EMU8000_REG_H +/* + * Register operations for the EMU8000 + * + * Copyright (C) 1999 Steve Ratcliffe + * + * Based on awe_wave.c by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Data port addresses relative to the EMU base. + */ +#define EMU8000_DATA0(e) ((e)->port1) +#define EMU8000_DATA1(e) ((e)->port2) +#define EMU8000_DATA2(e) ((e)->port2+2) +#define EMU8000_DATA3(e) ((e)->port3) +#define EMU8000_PTR(e) ((e)->port3+2) + +/* + * Make a command from a register and channel. + */ +#define EMU8000_CMD(reg, chan) ((reg)<<5 | (chan)) + +/* + * Commands to read and write the EMU8000 registers. + * These macros should be used for all register accesses. + */ +#define EMU8000_CPF_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(0, (chan))) +#define EMU8000_PTRX_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (chan))) +#define EMU8000_CVCF_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_VTFT_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_PSST_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(6, (chan))) +#define EMU8000_CSL_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(7, (chan))) +#define EMU8000_CCCA_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(0, (chan))) +#define EMU8000_HWCF4_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 9)) +#define EMU8000_HWCF5_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 10)) +#define EMU8000_HWCF6_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 13)) +#define EMU8000_SMALR_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 20)) +#define EMU8000_SMARR_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 21)) +#define EMU8000_SMALW_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 22)) +#define EMU8000_SMARW_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 23)) +#define EMU8000_SMLD_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 26)) +#define EMU8000_SMRD_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 26)) +#define EMU8000_WC_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 27)) +#define EMU8000_HWCF1_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 29)) +#define EMU8000_HWCF2_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 30)) +#define EMU8000_HWCF3_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 31)) +#define EMU8000_INIT1_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_INIT2_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_INIT3_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_INIT4_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_ENVVOL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(4, (chan))) +#define EMU8000_DCYSUSV_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(5, (chan))) +#define EMU8000_ENVVAL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(6, (chan))) +#define EMU8000_DCYSUS_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(7, (chan))) +#define EMU8000_ATKHLDV_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(4, (chan))) +#define EMU8000_LFO1VAL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(5, (chan))) +#define EMU8000_ATKHLD_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(6, (chan))) +#define EMU8000_LFO2VAL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(7, (chan))) +#define EMU8000_IP_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(0, (chan))) +#define EMU8000_IFATN_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(1, (chan))) +#define EMU8000_PEFE_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_FMMOD_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_TREMFRQ_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(4, (chan))) +#define EMU8000_FM2FRQ2_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(5, (chan))) + + +#define EMU8000_CPF_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(0, (chan)), (val)) +#define EMU8000_PTRX_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (chan)), (val)) +#define EMU8000_CVCF_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_VTFT_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_PSST_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(6, (chan)), (val)) +#define EMU8000_CSL_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(7, (chan)), (val)) +#define EMU8000_CCCA_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(0, (chan)), (val)) +#define EMU8000_HWCF4_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 9), (val)) +#define EMU8000_HWCF5_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 10), (val)) +#define EMU8000_HWCF6_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 13), (val)) +/* this register is not documented */ +#define EMU8000_HWCF7_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 14), (val)) +#define EMU8000_SMALR_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 20), (val)) +#define EMU8000_SMARR_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 21), (val)) +#define EMU8000_SMALW_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 22), (val)) +#define EMU8000_SMARW_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 23), (val)) +#define EMU8000_SMLD_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 26), (val)) +#define EMU8000_SMRD_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 26), (val)) +#define EMU8000_WC_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 27), (val)) +#define EMU8000_HWCF1_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 29), (val)) +#define EMU8000_HWCF2_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 30), (val)) +#define EMU8000_HWCF3_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 31), (val)) +#define EMU8000_INIT1_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_INIT2_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_INIT3_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_INIT4_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_ENVVOL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_DCYSUSV_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(5, (chan)), (val)) +#define EMU8000_ENVVAL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(6, (chan)), (val)) +#define EMU8000_DCYSUS_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(7, (chan)), (val)) +#define EMU8000_ATKHLDV_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_LFO1VAL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(5, (chan)), (val)) +#define EMU8000_ATKHLD_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(6, (chan)), (val)) +#define EMU8000_LFO2VAL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(7, (chan)), (val)) +#define EMU8000_IP_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(0, (chan)), (val)) +#define EMU8000_IFATN_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(1, (chan)), (val)) +#define EMU8000_PEFE_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_FMMOD_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_TREMFRQ_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_FM2FRQ2_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(5, (chan)), (val)) + +#define EMU8000_0080_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_00A0_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(5, (chan)), (val)) + +#endif /* __SOUND_EMU8000_REG_H */ diff -Nru a/include/sound/emux_legacy.h b/include/sound/emux_legacy.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/emux_legacy.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,146 @@ +#ifndef __SOUND_EMUX_LEGACY_H +#define __SOUND_EMUX_LEGACY_H + +/* + * Copyright (c) 1999-2000 Takashi Iwai + * + * Definitions of OSS compatible headers for Emu8000 device informations + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "seq_oss_legacy.h" + +/* + * awe hardware controls + */ + +#define _EMUX_OSS_DEBUG_MODE 0x00 +#define _EMUX_OSS_REVERB_MODE 0x01 +#define _EMUX_OSS_CHORUS_MODE 0x02 +#define _EMUX_OSS_REMOVE_LAST_SAMPLES 0x03 +#define _EMUX_OSS_INITIALIZE_CHIP 0x04 +#define _EMUX_OSS_SEND_EFFECT 0x05 +#define _EMUX_OSS_TERMINATE_CHANNEL 0x06 +#define _EMUX_OSS_TERMINATE_ALL 0x07 +#define _EMUX_OSS_INITIAL_VOLUME 0x08 +#define _EMUX_OSS_INITIAL_ATTEN _EMUX_OSS_INITIAL_VOLUME +#define _EMUX_OSS_RESET_CHANNEL 0x09 +#define _EMUX_OSS_CHANNEL_MODE 0x0a +#define _EMUX_OSS_DRUM_CHANNELS 0x0b +#define _EMUX_OSS_MISC_MODE 0x0c +#define _EMUX_OSS_RELEASE_ALL 0x0d +#define _EMUX_OSS_NOTEOFF_ALL 0x0e +#define _EMUX_OSS_CHN_PRESSURE 0x0f +#define _EMUX_OSS_EQUALIZER 0x11 + +#define _EMUX_OSS_MODE_FLAG 0x80 +#define _EMUX_OSS_COOKED_FLAG 0x40 /* not supported */ +#define _EMUX_OSS_MODE_VALUE_MASK 0x3F + + +/* + * mode type definitions + */ +enum { +/* 0*/ EMUX_MD_EXCLUSIVE_OFF, /* obsolete */ +/* 1*/ EMUX_MD_EXCLUSIVE_ON, /* obsolete */ +/* 2*/ EMUX_MD_VERSION, /* read only */ +/* 3*/ EMUX_MD_EXCLUSIVE_SOUND, /* 0/1: exclusive note on (default=1) */ +/* 4*/ EMUX_MD_REALTIME_PAN, /* 0/1: do realtime pan change (default=1) */ +/* 5*/ EMUX_MD_GUS_BANK, /* bank number for GUS patches (default=0) */ +/* 6*/ EMUX_MD_KEEP_EFFECT, /* 0/1: keep effect values, (default=0) */ +/* 7*/ EMUX_MD_ZERO_ATTEN, /* attenuation of max volume (default=32) */ +/* 8*/ EMUX_MD_CHN_PRIOR, /* 0/1: set MIDI channel priority mode (default=1) */ +/* 9*/ EMUX_MD_MOD_SENSE, /* integer: modwheel sensitivity (def=18) */ +/*10*/ EMUX_MD_DEF_PRESET, /* integer: default preset number (def=0) */ +/*11*/ EMUX_MD_DEF_BANK, /* integer: default bank number (def=0) */ +/*12*/ EMUX_MD_DEF_DRUM, /* integer: default drumset number (def=0) */ +/*13*/ EMUX_MD_TOGGLE_DRUM_BANK, /* 0/1: toggle drum flag with bank# (def=0) */ +/*14*/ EMUX_MD_NEW_VOLUME_CALC, /* 0/1: volume calculation mode (def=1) */ +/*15*/ EMUX_MD_CHORUS_MODE, /* integer: chorus mode (def=2) */ +/*16*/ EMUX_MD_REVERB_MODE, /* integer: chorus mode (def=4) */ +/*17*/ EMUX_MD_BASS_LEVEL, /* integer: bass level (def=5) */ +/*18*/ EMUX_MD_TREBLE_LEVEL, /* integer: treble level (def=9) */ +/*19*/ EMUX_MD_DEBUG_MODE, /* integer: debug level (def=0) */ +/*20*/ EMUX_MD_PAN_EXCHANGE, /* 0/1: exchange panning direction (def=0) */ + EMUX_MD_END, +}; + + +/* + * effect parameters + */ +enum { + +/* modulation envelope parameters */ +/* 0*/ EMUX_FX_ENV1_DELAY, /* WORD: ENVVAL */ +/* 1*/ EMUX_FX_ENV1_ATTACK, /* BYTE: up ATKHLD */ +/* 2*/ EMUX_FX_ENV1_HOLD, /* BYTE: lw ATKHLD */ +/* 3*/ EMUX_FX_ENV1_DECAY, /* BYTE: lw DCYSUS */ +/* 4*/ EMUX_FX_ENV1_RELEASE, /* BYTE: lw DCYSUS */ +/* 5*/ EMUX_FX_ENV1_SUSTAIN, /* BYTE: up DCYSUS */ +/* 6*/ EMUX_FX_ENV1_PITCH, /* BYTE: up PEFE */ +/* 7*/ EMUX_FX_ENV1_CUTOFF, /* BYTE: lw PEFE */ + +/* volume envelope parameters */ +/* 8*/ EMUX_FX_ENV2_DELAY, /* WORD: ENVVOL */ +/* 9*/ EMUX_FX_ENV2_ATTACK, /* BYTE: up ATKHLDV */ +/*10*/ EMUX_FX_ENV2_HOLD, /* BYTE: lw ATKHLDV */ +/*11*/ EMUX_FX_ENV2_DECAY, /* BYTE: lw DCYSUSV */ +/*12*/ EMUX_FX_ENV2_RELEASE, /* BYTE: lw DCYSUSV */ +/*13*/ EMUX_FX_ENV2_SUSTAIN, /* BYTE: up DCYSUSV */ + +/* LFO1 (tremolo & vibrato) parameters */ +/*14*/ EMUX_FX_LFO1_DELAY, /* WORD: LFO1VAL */ +/*15*/ EMUX_FX_LFO1_FREQ, /* BYTE: lo TREMFRQ */ +/*16*/ EMUX_FX_LFO1_VOLUME, /* BYTE: up TREMFRQ */ +/*17*/ EMUX_FX_LFO1_PITCH, /* BYTE: up FMMOD */ +/*18*/ EMUX_FX_LFO1_CUTOFF, /* BYTE: lo FMMOD */ + +/* LFO2 (vibrato) parameters */ +/*19*/ EMUX_FX_LFO2_DELAY, /* WORD: LFO2VAL */ +/*20*/ EMUX_FX_LFO2_FREQ, /* BYTE: lo FM2FRQ2 */ +/*21*/ EMUX_FX_LFO2_PITCH, /* BYTE: up FM2FRQ2 */ + +/* Other overall effect parameters */ +/*22*/ EMUX_FX_INIT_PITCH, /* SHORT: pitch offset */ +/*23*/ EMUX_FX_CHORUS, /* BYTE: chorus effects send (0-255) */ +/*24*/ EMUX_FX_REVERB, /* BYTE: reverb effects send (0-255) */ +/*25*/ EMUX_FX_CUTOFF, /* BYTE: up IFATN */ +/*26*/ EMUX_FX_FILTERQ, /* BYTE: up CCCA */ + +/* Sample / loop offset changes */ +/*27*/ EMUX_FX_SAMPLE_START, /* SHORT: offset */ +/*28*/ EMUX_FX_LOOP_START, /* SHORT: offset */ +/*29*/ EMUX_FX_LOOP_END, /* SHORT: offset */ +/*30*/ EMUX_FX_COARSE_SAMPLE_START, /* SHORT: upper word offset */ +/*31*/ EMUX_FX_COARSE_LOOP_START, /* SHORT: upper word offset */ +/*32*/ EMUX_FX_COARSE_LOOP_END, /* SHORT: upper word offset */ +/*33*/ EMUX_FX_ATTEN, /* BYTE: lo IFATN */ + + EMUX_FX_END, +}; +/* number of effects */ +#define EMUX_NUM_EFFECTS EMUX_FX_END + +/* effect flag values */ +#define EMUX_FX_FLAG_OFF 0 +#define EMUX_FX_FLAG_SET 1 +#define EMUX_FX_FLAG_ADD 2 + + +#endif /* __SOUND_EMUX_LEGACY_H */ diff -Nru a/include/sound/emux_synth.h b/include/sound/emux_synth.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/emux_synth.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,239 @@ +#ifndef __SOUND_EMUX_SYNTH_H +#define __SOUND_EMUX_SYNTH_H + +/* + * Defines for the Emu-series WaveTable chip + * + * Copyright (C) 2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_kernel.h" +#include "seq_device.h" +#include "soundfont.h" +#include "seq_midi_emul.h" +#ifdef CONFIG_SND_OSSEMUL +#include "seq_oss.h" +#endif +#include "emux_legacy.h" +#include "seq_virmidi.h" + +/* + * compile flags + */ +#define SNDRV_EMUX_USE_RAW_EFFECT + + +/* + * typedefs + */ +typedef struct snd_emux_effect_table snd_emux_effect_table_t; +typedef struct snd_emux_port snd_emux_port_t; +typedef struct snd_emux_voice snd_emux_voice_t; +typedef struct snd_emux snd_emux_t; + + +/* + * operators + */ +typedef struct snd_emux_operators { + struct module *owner; + snd_emux_voice_t *(*get_voice)(snd_emux_t *emu, snd_emux_port_t *port); + int (*prepare)(snd_emux_voice_t *vp); + void (*trigger)(snd_emux_voice_t *vp); + void (*release)(snd_emux_voice_t *vp); + void (*update)(snd_emux_voice_t *vp, int update); + void (*terminate)(snd_emux_voice_t *vp); + void (*free_voice)(snd_emux_voice_t *vp); + void (*reset)(snd_emux_t *emu, int ch); + /* the first parameters are snd_emux_t */ + int (*sample_new)(snd_emux_t *emu, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *data, long count); + int (*sample_free)(snd_emux_t *emu, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); + void (*sample_reset)(snd_emux_t *emu); + int (*load_fx)(snd_emux_t *emu, int type, int arg, const void *data, long count); + void (*sysex)(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +#ifdef CONFIG_SND_OSSEMUL + int (*oss_ioctl)(snd_emux_t *emu, int cmd, int p1, int p2); +#endif +} snd_emux_operators_t; + + +/* + * constant values + */ +#define SNDRV_EMUX_MAX_PORTS 32 /* max # of sequencer ports */ +#define SNDRV_EMUX_MAX_VOICES 64 /* max # of voices */ +#define SNDRV_EMUX_MAX_MULTI_VOICES 16 /* max # of playable voices + * simultineously + */ + +/* + * flags + */ +#define SNDRV_EMUX_ACCEPT_ROM (1<<0) + +/* + * emuX wavetable + */ +struct snd_emux { + + snd_card_t *card; /* assigned card */ + + /* following should be initialized before registration */ + int max_voices; /* Number of voices */ + int mem_size; /* memory size (in byte) */ + int num_ports; /* number of ports to be created */ + int pitch_shift; /* pitch shift value (for Emu10k1) */ + snd_emux_operators_t ops; /* operators */ + void *hw; /* hardware */ + unsigned long flags; /* other conditions */ + int midi_ports; /* number of virtual midi devices */ + int midi_devidx; /* device offset of virtual midi */ + + /* private */ + int num_voices; /* current number of voices */ + snd_sf_list_t *sflist; /* root of SoundFont list */ + snd_emux_voice_t *voices; /* Voices (EMU 'channel') */ + int use_time; /* allocation counter */ + spinlock_t voice_lock; /* Lock for voice access */ + struct semaphore register_mutex; + int client; /* For the sequencer client */ + int ports[SNDRV_EMUX_MAX_PORTS]; /* The ports for this device */ + int used; /* use counter */ + char *name; /* name of the device (internal) */ + snd_rawmidi_t **vmidi; + struct timer_list tlist; /* for pending note-offs */ + int timer_active; + + snd_util_memhdr_t *memhdr; /* memory chunk information */ + +#ifdef CONFIG_PROC_FS + snd_info_entry_t *proc; +#endif + +#ifdef CONFIG_SND_OSSEMUL + snd_seq_device_t *oss_synth; +#endif +}; + + +/* + * sequencer port information + */ +struct snd_emux_port { + + snd_midi_channel_set_t chset; + snd_emux_t *emu; + + char port_mode; /* operation mode */ + int volume_atten; /* emuX raw attenuation */ + unsigned long drum_flags; /* drum bitmaps */ + int ctrls[EMUX_MD_END]; /* control parameters */ +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_effect_table_t *effect; +#endif +#ifdef CONFIG_SND_OSSEMUL + snd_seq_oss_arg_t *oss_arg; +#endif +}; + +/* port_mode */ +#define SNDRV_EMUX_PORT_MODE_MIDI 0 /* normal MIDI port */ +#define SNDRV_EMUX_PORT_MODE_OSS_SYNTH 1 /* OSS synth port */ +#define SNDRV_EMUX_PORT_MODE_OSS_MIDI 2 /* OSS multi channel synth port */ + +/* + * A structure to keep track of each hardware voice + */ +struct snd_emux_voice { + int ch; /* Hardware channel number */ + + int state; /* status */ +#define SNDRV_EMUX_ST_OFF 0x00 /* Not playing, and inactive */ +#define SNDRV_EMUX_ST_ON 0x01 /* Note on */ +#define SNDRV_EMUX_ST_RELEASED (0x02|SNDRV_EMUX_ST_ON) /* Note released */ +#define SNDRV_EMUX_ST_SUSTAINED (0x04|SNDRV_EMUX_ST_ON) /* Note sustained */ +#define SNDRV_EMUX_ST_STANDBY (0x08|SNDRV_EMUX_ST_ON) /* Waiting to be triggered */ +#define SNDRV_EMUX_ST_PENDING (0x10|SNDRV_EMUX_ST_ON) /* Note will be released */ +#define SNDRV_EMUX_ST_LOCKED 0x100 /* Not accessible */ + + unsigned int time; /* An allocation time */ + unsigned char note; /* Note currently assigned to this voice */ + unsigned char key; + unsigned char velocity; /* Velocity of current note */ + + snd_sf_zone_t *zone; /* Zone assigned to this note */ + void *block; /* sample block pointer (optional) */ + snd_midi_channel_t *chan; /* Midi channel for this note */ + snd_emux_port_t *port; /* associated port */ + snd_emux_t *emu; /* assigned root info */ + void *hw; /* hardware pointer (emu8000_t or emu10k1_t) */ + unsigned long ontime; /* jiffies at note triggered */ + + /* Emu8k/Emu10k1 registers */ + soundfont_voice_info_t reg; + + /* additional registers */ + int avol; /* volume attenuation */ + int acutoff; /* cutoff target */ + int apitch; /* pitch offset */ + int apan; /* pan/aux pair */ + int aaux; + int ptarget; /* pitch target */ + int vtarget; /* volume target */ + int ftarget; /* filter target */ + +}; + +/* + * update flags (can be combined) + */ +#define SNDRV_EMUX_UPDATE_VOLUME (1<<0) +#define SNDRV_EMUX_UPDATE_PITCH (1<<1) +#define SNDRV_EMUX_UPDATE_PAN (1<<2) +#define SNDRV_EMUX_UPDATE_FMMOD (1<<3) +#define SNDRV_EMUX_UPDATE_TREMFREQ (1<<4) +#define SNDRV_EMUX_UPDATE_FM2FRQ2 (1<<5) +#define SNDRV_EMUX_UPDATE_Q (1<<6) + + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT +/* + * effect table + */ +struct snd_emux_effect_table { + /* Emu8000 specific effects */ + short val[EMUX_NUM_EFFECTS]; + unsigned char flag[EMUX_NUM_EFFECTS]; +}; +#endif /* SNDRV_EMUX_USE_RAW_EFFECT */ + + +/* + * prototypes - interface to Emu10k1 and Emu8k routines + */ +int snd_emux_new(snd_emux_t **remu); +int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name); +int snd_emux_free(snd_emux_t *emu); + +/* + * exported functions + */ +void snd_emux_terminate_all(snd_emux_t *emu); +void snd_emux_lock_voice(snd_emux_t *emu, int voice); +void snd_emux_unlock_voice(snd_emux_t *emu, int voice); + +#endif /* __SOUND_EMUX_SYNTH_H */ diff -Nru a/include/sound/es1688.h b/include/sound/es1688.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/es1688.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,125 @@ +#ifndef __SOUND_ES1688_H +#define __SOUND_ES1688_H + +/* + * Header file for ES488/ES1688 + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "control.h" +#include "pcm.h" + +#define ES1688_HW_AUTO 0x0000 +#define ES1688_HW_688 0x0001 +#define ES1688_HW_1688 0x0002 + +struct _snd_es1688 { + unsigned long port; /* port of ESS chip */ + struct resource *res_port; + unsigned long mpu_port; /* MPU-401 port of ESS chip */ + int irq; /* IRQ number of ESS chip */ + int mpu_irq; /* MPU IRQ */ + int dma8; /* 8-bit DMA */ + unsigned short version; /* version of ESS chip */ + unsigned short hardware; /* see to ES1688_HW_XXXX */ + + unsigned short trigger_value; + unsigned char pad; + unsigned int dma_size; + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + spinlock_t reg_lock; + spinlock_t mixer_lock; +}; + +typedef struct _snd_es1688 es1688_t; + +/* I/O ports */ + +#define ES1688P(codec, x) ((codec)->port + e_s_s_ESS1688##x) + +#define e_s_s_ESS1688RESET 0x6 +#define e_s_s_ESS1688READ 0xa +#define e_s_s_ESS1688WRITE 0xc +#define e_s_s_ESS1688COMMAND 0xc +#define e_s_s_ESS1688STATUS 0xc +#define e_s_s_ESS1688DATA_AVAIL 0xe +#define e_s_s_ESS1688DATA_AVAIL_16 0xf +#define e_s_s_ESS1688MIXER_ADDR 0x4 +#define e_s_s_ESS1688MIXER_DATA 0x5 +#define e_s_s_ESS1688OPL3_LEFT 0x0 +#define e_s_s_ESS1688OPL3_RIGHT 0x2 +#define e_s_s_ESS1688OPL3_BOTH 0x8 +#define e_s_s_ESS1688ENABLE0 0x0 +#define e_s_s_ESS1688ENABLE1 0x9 +#define e_s_s_ESS1688ENABLE2 0xb +#define e_s_s_ESS1688INIT1 0x7 + +#define ES1688_DSP_CMD_DMAOFF 0xd0 +#define ES1688_DSP_CMD_SPKON 0xd1 +#define ES1688_DSP_CMD_SPKOFF 0xd3 +#define ES1688_DSP_CMD_DMAON 0xd4 + +#define ES1688_PCM_DEV 0x14 +#define ES1688_MIC_DEV 0x1a +#define ES1688_REC_DEV 0x1c +#define ES1688_MASTER_DEV 0x32 +#define ES1688_FM_DEV 0x36 +#define ES1688_CD_DEV 0x38 +#define ES1688_AUX_DEV 0x3a +#define ES1688_SPEAKER_DEV 0x3c +#define ES1688_LINE_DEV 0x3e +#define ES1688_RECLEV_DEV 0xb4 + +#define ES1688_MIXS_MASK 0x17 +#define ES1688_MIXS_MIC 0x00 +#define ES1688_MIXS_MIC_MASTER 0x01 +#define ES1688_MIXS_CD 0x02 +#define ES1688_MIXS_AOUT 0x03 +#define ES1688_MIXS_MIC1 0x04 +#define ES1688_MIXS_REC_MIX 0x05 +#define ES1688_MIXS_LINE 0x06 +#define ES1688_MIXS_MASTER 0x07 +#define ES1688_MIXS_MUTE 0x10 + +/* + + */ + +void snd_es1688_mixer_write(es1688_t *chip, unsigned char reg, unsigned char data); +unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg); + +void snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +int snd_es1688_create(snd_card_t * card, + unsigned long port, + unsigned long mpu_port, + int irq, + int mpu_irq, + int dma8, + unsigned short hardware, + es1688_t ** rchip); +int snd_es1688_pcm(es1688_t *chip, int device, snd_pcm_t ** rpcm); +int snd_es1688_mixer(es1688_t *chip); + +#endif /* __SOUND_ES1688_H */ diff -Nru a/include/sound/gus.h b/include/sound/gus.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/gus.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,730 @@ +#ifndef __SOUND_GUS_H +#define __SOUND_GUS_H + +/* + * Global structures used for GUS part of ALSA driver + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include "timer.h" +#include "seq_midi_emul.h" +#include "seq_device.h" +#include "ainstr_iw.h" +#include "ainstr_gf1.h" +#include "ainstr_simple.h" +#include + +#define SNDRV_SEQ_DEV_ID_GUS "gus-synth" + +/* IO ports */ + +#define GUSP(gus, x) ((gus)->gf1.port + SNDRV_g_u_s_##x) + +#define SNDRV_g_u_s_MIDICTRL (0x320-0x220) +#define SNDRV_g_u_s_MIDISTAT (0x320-0x220) +#define SNDRV_g_u_s_MIDIDATA (0x321-0x220) + +#define SNDRV_g_u_s_GF1PAGE (0x322-0x220) +#define SNDRV_g_u_s_GF1REGSEL (0x323-0x220) +#define SNDRV_g_u_s_GF1DATALOW (0x324-0x220) +#define SNDRV_g_u_s_GF1DATAHIGH (0x325-0x220) +#define SNDRV_g_u_s_IRQSTAT (0x226-0x220) +#define SNDRV_g_u_s_TIMERCNTRL (0x228-0x220) +#define SNDRV_g_u_s_TIMERDATA (0x229-0x220) +#define SNDRV_g_u_s_DRAM (0x327-0x220) +#define SNDRV_g_u_s_MIXCNTRLREG (0x220-0x220) +#define SNDRV_g_u_s_IRQDMACNTRLREG (0x22b-0x220) +#define SNDRV_g_u_s_REGCNTRLS (0x22f-0x220) +#define SNDRV_g_u_s_BOARDVERSION (0x726-0x220) +#define SNDRV_g_u_s_MIXCNTRLPORT (0x726-0x220) +#define SNDRV_g_u_s_IVER (0x325-0x220) +#define SNDRV_g_u_s_MIXDATAPORT (0x326-0x220) +#define SNDRV_g_u_s_MAXCNTRLPORT (0x326-0x220) + +/* GF1 registers */ + +/* global registers */ +#define SNDRV_GF1_GB_ACTIVE_VOICES 0x0e +#define SNDRV_GF1_GB_VOICES_IRQ 0x0f +#define SNDRV_GF1_GB_GLOBAL_MODE 0x19 +#define SNDRV_GF1_GW_LFO_BASE 0x1a +#define SNDRV_GF1_GB_VOICES_IRQ_READ 0x1f +#define SNDRV_GF1_GB_DRAM_DMA_CONTROL 0x41 +#define SNDRV_GF1_GW_DRAM_DMA_LOW 0x42 +#define SNDRV_GF1_GW_DRAM_IO_LOW 0x43 +#define SNDRV_GF1_GB_DRAM_IO_HIGH 0x44 +#define SNDRV_GF1_GB_SOUND_BLASTER_CONTROL 0x45 +#define SNDRV_GF1_GB_ADLIB_TIMER_1 0x46 +#define SNDRV_GF1_GB_ADLIB_TIMER_2 0x47 +#define SNDRV_GF1_GB_RECORD_RATE 0x48 +#define SNDRV_GF1_GB_REC_DMA_CONTROL 0x49 +#define SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL 0x4b +#define SNDRV_GF1_GB_RESET 0x4c +#define SNDRV_GF1_GB_DRAM_DMA_HIGH 0x50 +#define SNDRV_GF1_GW_DRAM_IO16 0x51 +#define SNDRV_GF1_GW_MEMORY_CONFIG 0x52 +#define SNDRV_GF1_GB_MEMORY_CONTROL 0x53 +#define SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR 0x54 +#define SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR 0x55 +#define SNDRV_GF1_GW_FIFO_SIZE 0x56 +#define SNDRV_GF1_GW_INTERLEAVE 0x57 +#define SNDRV_GF1_GB_COMPATIBILITY 0x59 +#define SNDRV_GF1_GB_DECODE_CONTROL 0x5a +#define SNDRV_GF1_GB_VERSION_NUMBER 0x5b +#define SNDRV_GF1_GB_MPU401_CONTROL_A 0x5c +#define SNDRV_GF1_GB_MPU401_CONTROL_B 0x5d +#define SNDRV_GF1_GB_EMULATION_IRQ 0x60 +/* voice specific registers */ +#define SNDRV_GF1_VB_ADDRESS_CONTROL 0x00 +#define SNDRV_GF1_VW_FREQUENCY 0x01 +#define SNDRV_GF1_VW_START_HIGH 0x02 +#define SNDRV_GF1_VW_START_LOW 0x03 +#define SNDRV_GF1_VA_START SNDRV_GF1_VW_START_HIGH +#define SNDRV_GF1_VW_END_HIGH 0x04 +#define SNDRV_GF1_VW_END_LOW 0x05 +#define SNDRV_GF1_VA_END SNDRV_GF1_VW_END_HIGH +#define SNDRV_GF1_VB_VOLUME_RATE 0x06 +#define SNDRV_GF1_VB_VOLUME_START 0x07 +#define SNDRV_GF1_VB_VOLUME_END 0x08 +#define SNDRV_GF1_VW_VOLUME 0x09 +#define SNDRV_GF1_VW_CURRENT_HIGH 0x0a +#define SNDRV_GF1_VW_CURRENT_LOW 0x0b +#define SNDRV_GF1_VA_CURRENT SNDRV_GF1_VW_CURRENT_HIGH +#define SNDRV_GF1_VB_PAN 0x0c +#define SNDRV_GF1_VW_OFFSET_RIGHT 0x0c +#define SNDRV_GF1_VB_VOLUME_CONTROL 0x0d +#define SNDRV_GF1_VB_UPPER_ADDRESS 0x10 +#define SNDRV_GF1_VW_EFFECT_HIGH 0x11 +#define SNDRV_GF1_VW_EFFECT_LOW 0x12 +#define SNDRV_GF1_VA_EFFECT SNDRV_GF1_VW_EFFECT_HIGH +#define SNDRV_GF1_VW_OFFSET_LEFT 0x13 +#define SNDRV_GF1_VB_ACCUMULATOR 0x14 +#define SNDRV_GF1_VB_MODE 0x15 +#define SNDRV_GF1_VW_EFFECT_VOLUME 0x16 +#define SNDRV_GF1_VB_FREQUENCY_LFO 0x17 +#define SNDRV_GF1_VB_VOLUME_LFO 0x18 +#define SNDRV_GF1_VW_OFFSET_RIGHT_FINAL 0x1b +#define SNDRV_GF1_VW_OFFSET_LEFT_FINAL 0x1c +#define SNDRV_GF1_VW_EFFECT_VOLUME_FINAL 0x1d + +/* ICS registers */ + +#define SNDRV_ICS_MIC_DEV 0 +#define SNDRV_ICS_LINE_DEV 1 +#define SNDRV_ICS_CD_DEV 2 +#define SNDRV_ICS_GF1_DEV 3 +#define SNDRV_ICS_NONE_DEV 4 +#define SNDRV_ICS_MASTER_DEV 5 + +/* LFO */ + +#define SNDRV_LFO_TREMOLO 0 +#define SNDRV_LFO_VIBRATO 1 + +/* misc */ + +#define SNDRV_GF1_DMA_UNSIGNED 0x80 +#define SNDRV_GF1_DMA_16BIT 0x40 +#define SNDRV_GF1_DMA_IRQ 0x20 +#define SNDRV_GF1_DMA_WIDTH16 0x04 +#define SNDRV_GF1_DMA_READ 0x02 /* read from GUS's DRAM */ +#define SNDRV_GF1_DMA_ENABLE 0x01 + +/* ramp ranges */ + +#define SNDRV_GF1_ATTEN(x) (snd_gf1_atten_table[x]) +#define SNDRV_GF1_MIN_VOLUME 1800 +#define SNDRV_GF1_MAX_VOLUME 4095 +#define SNDRV_GF1_MIN_OFFSET (SNDRV_GF1_MIN_VOLUME>>4) +#define SNDRV_GF1_MAX_OFFSET 255 +#define SNDRV_GF1_MAX_TDEPTH 90 + +/* defines for memory manager */ + +#define SNDRV_GF1_MEM_BLOCK_16BIT 0x0001 + +#define SNDRV_GF1_MEM_OWNER_DRIVER 0x0001 +#define SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE 0x0002 +#define SNDRV_GF1_MEM_OWNER_WAVE_GF1 0x0003 +#define SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF 0x0004 + +/* constants for interrupt handlers */ + +#define SNDRV_GF1_HANDLER_MIDI_OUT 0x00010000 +#define SNDRV_GF1_HANDLER_MIDI_IN 0x00020000 +#define SNDRV_GF1_HANDLER_TIMER1 0x00040000 +#define SNDRV_GF1_HANDLER_TIMER2 0x00080000 +#define SNDRV_GF1_HANDLER_VOICE 0x00100000 +#define SNDRV_GF1_HANDLER_DMA_WRITE 0x00200000 +#define SNDRV_GF1_HANDLER_DMA_READ 0x00400000 +#define SNDRV_GF1_HANDLER_ALL (0xffff0000&~SNDRV_GF1_HANDLER_VOICE) + +/* constants for DMA flags */ + +#define SNDRV_GF1_DMA_TRIGGER 1 + +/* --- */ + +struct _snd_gus_card; +typedef struct _snd_gus_card snd_gus_card_t; + +/* GF1 specific structure */ + +typedef struct _snd_gf1_bank_info { + unsigned int address; + unsigned int size; +} snd_gf1_bank_info_t; + +typedef struct _snd_gf1_mem_block { + unsigned short flags; /* flags - SNDRV_GF1_MEM_BLOCK_XXXX */ + unsigned short owner; /* owner - SNDRV_GF1_MEM_OWNER_XXXX */ + unsigned int share; /* share count */ + unsigned int share_id[4]; /* share ID */ + unsigned int ptr; + unsigned int size; + char *name; + struct _snd_gf1_mem_block *next; + struct _snd_gf1_mem_block *prev; +} snd_gf1_mem_block_t; + +typedef struct _snd_gf1_mem { + snd_gf1_bank_info_t banks_8[4]; + snd_gf1_bank_info_t banks_16[4]; + snd_gf1_mem_block_t *first; + snd_gf1_mem_block_t *last; + snd_info_entry_t *info_entry; + struct semaphore memory_mutex; +} snd_gf1_mem_t; + +typedef struct snd_gf1_dma_block { + void *buffer; /* buffer in computer's RAM */ + unsigned long buf_addr; /* buffer address */ + unsigned int addr; /* address in onboard memory */ + unsigned int count; /* count in bytes */ + unsigned int cmd; /* DMA command (format) */ + void (*ack)(snd_gus_card_t * gus, void *private_data); + void *private_data; + struct snd_gf1_dma_block *next; +} snd_gf1_dma_block_t; + +typedef struct { + snd_midi_channel_set_t * chset; + snd_gus_card_t * gus; + int mode; /* operation mode */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + int midi_has_voices: 1; +} snd_gus_port_t; + +typedef struct _snd_gus_voice snd_gus_voice_t; + +typedef struct { + void (*sample_start)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position); + void (*sample_stop)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode); + void (*sample_freq)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq); + void (*sample_volume)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume); + void (*sample_loop)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop); + void (*sample_pos)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position); + void (*sample_private1)(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data); +} snd_gus_sample_ops_t; + +#define SNDRV_GF1_VOICE_TYPE_PCM 0 +#define SNDRV_GF1_VOICE_TYPE_SYNTH 1 +#define SNDRV_GF1_VOICE_TYPE_MIDI 2 + +#define SNDRV_GF1_VFLG_RUNNING (1<<0) +#define SNDRV_GF1_VFLG_EFFECT_TIMER1 (1<<1) +#define SNDRV_GF1_VFLG_PAN (1<<2) + +typedef enum { + VENV_BEFORE, + VENV_ATTACK, + VENV_SUSTAIN, + VENV_RELEASE, + VENV_DONE, + VENV_VOLUME +} snd_gus_volume_state_t; + +struct _snd_gus_voice { + int number; + int use: 1, + pcm: 1, + synth:1, + midi: 1; + unsigned int flags; + unsigned char client; + unsigned char port; + unsigned char index; + unsigned char pad; + +#ifdef CONFIG_SND_DEBUG + unsigned int interrupt_stat_wave; + unsigned int interrupt_stat_volume; +#endif + void (*handler_wave) (snd_gus_card_t * gus, snd_gus_voice_t * voice); + void (*handler_volume) (snd_gus_card_t * gus, snd_gus_voice_t * voice); + void (*handler_effect) (snd_gus_card_t * gus, snd_gus_voice_t * voice); + void (*volume_change) (snd_gus_card_t * gus); + + snd_gus_sample_ops_t *sample_ops; + + snd_seq_instr_t instr; + + /* running status / registers */ + + snd_seq_ev_volume_t sample_volume; + + unsigned short fc_register; + unsigned short fc_lfo; + unsigned short gf1_volume; + unsigned char control; + unsigned char mode; + unsigned char gf1_pan; + unsigned char effect_accumulator; + unsigned char volume_control; + unsigned char venv_value_next; + snd_gus_volume_state_t venv_state; + snd_gus_volume_state_t venv_state_prev; + unsigned short vlo; + unsigned short vro; + unsigned short gf1_effect_volume; + + /* --- */ + + void *private_data; + void (*private_free)(snd_gus_voice_t *voice); +}; + +struct _snd_gf1 { + + unsigned int enh_mode:1, /* enhanced mode (GFA1) */ + hw_lfo:1, /* use hardware LFO */ + sw_lfo:1, /* use software LFO */ + effect:1; /* use effect voices */ + + unsigned long port; /* port of GF1 chip */ + struct resource *res_port1; + struct resource *res_port2; + int irq; /* IRQ number */ + int dma1; /* DMA1 number */ + int dma2; /* DMA2 number */ + unsigned int memory; /* GUS's DRAM size in bytes */ + unsigned int rom_memory; /* GUS's ROM size in bytes */ + unsigned int rom_present; /* bitmask */ + unsigned int rom_banks; /* GUS's ROM banks */ + + snd_gf1_mem_t mem_alloc; + snd_info_entry_t *ram_entries[4]; + snd_info_entry_t *rom_entries[4]; + + /* registers */ + unsigned short reg_page; + unsigned short reg_regsel; + unsigned short reg_data8; + unsigned short reg_data16; + unsigned short reg_irqstat; + unsigned short reg_dram; + unsigned short reg_timerctrl; + unsigned short reg_timerdata; + unsigned char ics_regs[6][2]; + /* --------- */ + + unsigned char active_voices; /* active voices */ + unsigned char active_voice; /* selected voice (GF1PAGE register) */ + + snd_gus_voice_t voices[32]; /* GF1 voices */ + + unsigned int default_voice_address; + + unsigned short playback_freq; /* GF1 playback (mixing) frequency */ + unsigned short mode; /* see to SNDRV_GF1_MODE_XXXX */ + unsigned char volume_ramp; + unsigned char smooth_pan; + unsigned char full_range_pan; + unsigned char pad0; + + unsigned char *lfos; + + /* interrupt handlers */ + + void (*interrupt_handler_midi_out) (snd_gus_card_t * gus); + void (*interrupt_handler_midi_in) (snd_gus_card_t * gus); + void (*interrupt_handler_timer1) (snd_gus_card_t * gus); + void (*interrupt_handler_timer2) (snd_gus_card_t * gus); + void (*interrupt_handler_dma_write) (snd_gus_card_t * gus); + void (*interrupt_handler_dma_read) (snd_gus_card_t * gus); + +#ifdef CONFIG_SND_DEBUG + unsigned int interrupt_stat_midi_out; + unsigned int interrupt_stat_midi_in; + unsigned int interrupt_stat_timer1; + unsigned int interrupt_stat_timer2; + unsigned int interrupt_stat_dma_write; + unsigned int interrupt_stat_dma_read; + unsigned int interrupt_stat_voice_lost; +#endif + + /* synthesizer */ + + int seq_client; + snd_gus_port_t seq_ports[4]; + snd_seq_kinstr_list_t *ilist; + snd_iwffff_ops_t iwffff_ops; + snd_gf1_ops_t gf1_ops; + snd_simple_ops_t simple_ops; + + /* timer */ + + unsigned short timer_enabled; + snd_timer_t *timer1; + snd_timer_t *timer2; + + /* midi */ + + unsigned short uart_cmd; + unsigned int uart_framing; + unsigned int uart_overrun; + + /* dma operations */ + + unsigned int dma_flags; + unsigned int dma_shared; + snd_gf1_dma_block_t *dma_data_pcm; + snd_gf1_dma_block_t *dma_data_pcm_last; + snd_gf1_dma_block_t *dma_data_synth; + snd_gf1_dma_block_t *dma_data_synth_last; + void (*dma_ack)(snd_gus_card_t * gus, void *private_data); + void *dma_private_data; + + /* pcm */ + int pcm_channels; + int pcm_alloc_voices; + unsigned short pcm_volume_level_left; + unsigned short pcm_volume_level_right; + unsigned short pcm_volume_level_left1; + unsigned short pcm_volume_level_right1; + + unsigned char pcm_rcntrl_reg; + unsigned char pad_end; +}; + +/* main structure for GUS card */ + +struct _snd_gus_card { + snd_card_t *card; + + unsigned int + initialized: 1, /* resources were initialized */ + equal_irq:1, /* GF1 and CODEC shares IRQ (GUS MAX only) */ + equal_dma:1, /* if dma channels are equal (not valid for daughter board) */ + ics_flag:1, /* have we ICS mixer chip */ + ics_flipped:1, /* ICS mixer have flipped some channels? */ + codec_flag:1, /* have we CODEC chip? */ + max_flag:1, /* have we GUS MAX card? */ + max_ctrl_flag:1, /* have we original GUS MAX card? */ + daughter_flag:1, /* have we daughter board? */ + interwave:1, /* hey - we have InterWave card */ + ess_flag:1, /* ESS chip found... GUS Extreme */ + ace_flag:1, /* GUS ACE detected */ + uart_enable:1; /* enable MIDI UART */ + unsigned short revision; /* revision of chip */ + unsigned short max_cntrl_val; /* GUS MAX control value */ + unsigned short mix_cntrl_reg; /* mixer control register */ + unsigned short joystick_dac; /* joystick DAC level */ + int timer_dev; /* timer device */ + + struct _snd_gf1 gf1; /* gf1 specific variables */ +#ifdef CONFIG_SND_DEBUG + snd_info_entry_t *irq_entry; +#endif + snd_pcm_t *pcm; + snd_pcm_substream_t *pcm_cap_substream; + unsigned int c_dma_size; + unsigned int c_period_size; + unsigned int c_pos; + + snd_rawmidi_t *midi_uart; + snd_rawmidi_substream_t *midi_substream_output; + snd_rawmidi_substream_t *midi_substream_input; + + snd_seq_device_t *seq_dev; + + spinlock_t reg_lock; + spinlock_t voice_alloc; + spinlock_t active_voice_lock; + spinlock_t event_lock; + spinlock_t dma_lock; + spinlock_t pcm_volume_level_lock; + spinlock_t uart_cmd_lock; + struct semaphore dma_mutex; + struct semaphore register_mutex; +}; + +/* I/O functions for GF1/InterWave chip - gus_io.c */ + +static inline void snd_gf1_select_voice(snd_gus_card_t * gus, int voice) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->active_voice_lock, flags); + if (voice != gus->gf1.active_voice) { + gus->gf1.active_voice = voice; + outb(voice, GUSP(gus, GF1PAGE)); + } + spin_unlock_irqrestore(&gus->active_voice_lock, flags); +} + +static inline void snd_gf1_uart_cmd(snd_gus_card_t * gus, unsigned char b) +{ + outb(gus->gf1.uart_cmd = b, GUSP(gus, MIDICTRL)); +} + +static inline unsigned char snd_gf1_uart_stat(snd_gus_card_t * gus) +{ + return inb(GUSP(gus, MIDISTAT)); +} + +static inline void snd_gf1_uart_put(snd_gus_card_t * gus, unsigned char b) +{ + outb(b, GUSP(gus, MIDIDATA)); +} + +static inline unsigned char snd_gf1_uart_get(snd_gus_card_t * gus) +{ + return inb(GUSP(gus, MIDIDATA)); +} + +extern void snd_gf1_delay(snd_gus_card_t * gus); + +extern void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg); + +extern void snd_gf1_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg); +extern inline unsigned char snd_gf1_read8(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_look8(gus, reg | 0x80); +} +extern void snd_gf1_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data); +extern unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg); +extern inline unsigned short snd_gf1_read16(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_look16(gus, reg | 0x80); +} +extern void snd_gf1_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr); +extern void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data); +extern unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr); +extern void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data); +extern unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr); +extern void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, unsigned short value, unsigned int count); +extern void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit); +extern unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit); +extern void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg); +extern void snd_gf1_i_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg); +extern void snd_gf1_i_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data); +extern inline unsigned char snd_gf1_i_read8(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_i_look8(gus, reg | 0x80); +} +extern unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg); +extern inline unsigned short snd_gf1_i_read16(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_i_look16(gus, reg | 0x80); +} +extern void snd_gf1_i_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit); +extern unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit); + +extern void snd_gf1_select_active_voices(snd_gus_card_t * gus); + +/* gus_lfo.c */ + +struct _SND_IW_LFO_PROGRAM { + unsigned short freq_and_control; + unsigned char depth_final; + unsigned char depth_inc; + unsigned short twave; + unsigned short depth; +}; + +#if 0 +extern void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice); +#endif +extern void snd_gf1_lfo_init(snd_gus_card_t * gus); +extern void snd_gf1_lfo_done(snd_gus_card_t * gus); +extern void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type, struct _SND_IW_LFO_PROGRAM *program); +extern void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type); +extern void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type); +extern void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice, int lfo_type, int freq); +extern void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice, int lfo_type, int depth); +extern void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type, int freq, int current_depth, int depth, int sweep, int shape); +extern void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type); +#if 0 +extern void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *command); +#endif + +/* gus_mem.c */ + +void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup); +int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block); +snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc, + unsigned int address); +snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc, + unsigned int *share_id); +snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner, + char *name, int size, int w_16, + int align, unsigned int *share_id); +int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address); +int snd_gf1_mem_free_owner(snd_gf1_mem_t * alloc, int owner); +int snd_gf1_mem_init(snd_gus_card_t * gus); +int snd_gf1_mem_done(snd_gus_card_t * gus); + +/* gus_mem_proc.c */ + +int snd_gf1_mem_proc_init(snd_gus_card_t * gus); +int snd_gf1_mem_proc_done(snd_gus_card_t * gus); + +/* gus_dma.c */ + +void snd_gf1_dma_program(snd_gus_card_t * gus, unsigned int addr, + unsigned long buf_addr, unsigned int count, + unsigned int cmd); +void snd_gf1_dma_ack(snd_gus_card_t * gus); +int snd_gf1_dma_init(snd_gus_card_t * gus); +int snd_gf1_dma_done(snd_gus_card_t * gus); +int snd_gf1_dma_transfer_block(snd_gus_card_t * gus, + snd_gf1_dma_block_t * block, + int atomic, + int synth); + +/* gus_volume.c */ + +unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol); +unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol); +unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus, + unsigned short start, + unsigned short end, + unsigned int us); +unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq2); +unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens); +unsigned short snd_gf1_compute_freq(unsigned int freq, + unsigned int rate, + unsigned short mix_rate); + +/* gus_reset.c */ + +void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what); +void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice); +void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice); +void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max); +void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max); +snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port); +void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice); +int snd_gf1_start(snd_gus_card_t * gus); +int snd_gf1_stop(snd_gus_card_t * gus); + +/* gus_mixer.c */ + +int snd_gf1_new_mixer(snd_gus_card_t * gus); + +/* gus_pcm.c */ + +int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm); + +#ifdef CONFIG_SND_DEBUG +extern void snd_gf1_print_voice_registers(snd_gus_card_t * gus); +extern void snd_gf1_print_global_registers(snd_gus_card_t * gus); +extern void snd_gf1_print_setup_registers(snd_gus_card_t * gus); +extern void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit); +#endif + +/* gus.c */ + +int snd_gus_use_inc(snd_gus_card_t * gus); +void snd_gus_use_dec(snd_gus_card_t * gus); +int snd_gus_create(snd_card_t * card, + unsigned long port, + int irq, int dma1, int dma2, + int timer_dev, + int voices, + int pcm_channels, + int effect, + snd_gus_card_t ** rgus); +int snd_gus_initialize(snd_gus_card_t * gus); + +/* gus_irq.c */ + +void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs); +#ifdef CONFIG_SND_DEBUG +void snd_gus_irq_profile_init(snd_gus_card_t *gus); +void snd_gus_irq_profile_done(snd_gus_card_t *gus); +#endif + +/* gus_uart.c */ + +int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t **rrawmidi); + +#if 0 +extern void snd_engine_instrument_register(unsigned short mode, + struct _SND_INSTRUMENT_VOICE_COMMANDS *voice_cmds, + struct _SND_INSTRUMENT_NOTE_COMMANDS *note_cmds, + struct _SND_INSTRUMENT_CHANNEL_COMMANDS *channel_cmds); +extern int snd_engine_instrument_register_ask(unsigned short mode); +#endif + +/* gus_dram.c */ +int snd_gus_dram_write(snd_gus_card_t *gus, char *ptr, + unsigned int addr, unsigned int size); +int snd_gus_dram_read(snd_gus_card_t *gus, char *ptr, + unsigned int addr, unsigned int size, int rom); + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + +/* gus_sample.c */ +void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p); + +/* gus_simple.c */ +void snd_gf1_simple_init(snd_gus_voice_t *voice); + +/* gus_instr.c */ +int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave, + int atomic); +int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave, + int atomic); +int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); +int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); +int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr, + int atomic); + +#endif /* CONFIG_SND_SEQUENCER */ + +#endif /* __SOUND_GUS_H */ diff -Nru a/include/sound/hwdep.h b/include/sound/hwdep.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/hwdep.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,63 @@ +#ifndef __SOUND_HWDEP_H +#define __SOUND_HWDEP_H + +/* + * Hardware dependent layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +typedef enum sndrv_hwdep_iface snd_hwdep_iface_t; +typedef struct sndrv_hwdep_info snd_hwdep_info_t; + +typedef struct _snd_hwdep_ops { + long long (*llseek) (snd_hwdep_t *hw, struct file * file, long long offset, int orig); + long (*read) (snd_hwdep_t * hw, char *buf, long count, loff_t *offset); + long (*write) (snd_hwdep_t * hw, const char *buf, long count, loff_t *offset); + int (*open) (snd_hwdep_t * hw, struct file * file); + int (*release) (snd_hwdep_t * hw, struct file * file); + unsigned int (*poll) (snd_hwdep_t * hw, struct file * file, poll_table * wait); + int (*ioctl) (snd_hwdep_t * hw, struct file * file, unsigned int cmd, unsigned long arg); + int (*mmap) (snd_hwdep_t * hw, struct file * file, struct vm_area_struct * vma); +} snd_hwdep_ops_t; + +struct _snd_hwdep { + snd_card_t *card; + int device; + char id[32]; + char name[80]; + int iface; + +#ifdef CONFIG_SND_OSSEMUL + char oss_dev[32]; + int oss_type; + int ossreg; +#endif + + snd_hwdep_ops_t ops; + wait_queue_head_t open_wait; + void *private_data; + void (*private_free) (snd_hwdep_t *hwdep); +}; + +extern int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep); + +#endif /* __SOUND_HWDEP_H */ diff -Nru a/include/sound/i2c.h b/include/sound/i2c.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/i2c.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,92 @@ +#ifndef __SOUND_I2C_H +#define __SOUND_I2C_H + +/* + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ + +typedef struct _snd_i2c_device snd_i2c_device_t; +typedef struct _snd_i2c_bus snd_i2c_bus_t; + +#define SND_I2C_DEVICE_ADDRTEN (1<<0) /* 10-bit I2C address */ + +struct _snd_i2c_device { + struct list_head list; + snd_i2c_bus_t *bus; /* I2C bus */ + char name[32]; /* some useful device name */ + unsigned short flags; /* device flags */ + unsigned short addr; /* device address (might be 10-bit) */ + unsigned long private_value; + void *private_data; + void (*private_free)(snd_i2c_device_t *device); +}; + +#define snd_i2c_device(n) list_entry(n, snd_i2c_device_t, list) + +typedef struct _snd_i2c_bit_ops { + void (*start)(snd_i2c_bus_t *bus); /* transfer start */ + void (*stop)(snd_i2c_bus_t *bus); /* transfer stop */ + void (*direction)(snd_i2c_bus_t *bus, int clock, int data); /* set line direction (0 = write, 1 = read) */ + void (*setlines)(snd_i2c_bus_t *bus, int clock, int data); + int (*getclock)(snd_i2c_bus_t *bus); + int (*getdata)(snd_i2c_bus_t *bus, int ack); +} snd_i2c_bit_ops_t; + +typedef struct _snd_i2c_ops { + int (*sendbytes)(snd_i2c_device_t *device, unsigned char *bytes, int count); + int (*readbytes)(snd_i2c_device_t *device, unsigned char *bytes, int count); + int (*probeaddr)(snd_i2c_bus_t *bus, unsigned short addr); +} snd_i2c_ops_t; + +struct _snd_i2c_bus { + snd_card_t *card; /* card which I2C belongs to */ + char name[32]; /* some useful label */ + + spinlock_t lock; + + snd_i2c_bus_t *master; /* master bus when SCK/SCL is shared */ + struct list_head buses; /* master: slave buses sharing SCK/SCL, slave: link list */ + + struct list_head devices; /* attached devices to this bus */ + + union { + snd_i2c_bit_ops_t *bit; + void *ops; + } hw_ops; /* lowlevel operations */ + snd_i2c_ops_t *ops; /* midlevel operations */ + + unsigned long private_value; + void *private_data; + void (*private_free)(snd_i2c_bus_t *bus); +}; + +#define snd_i2c_slave_bus(n) list_entry(n, snd_i2c_bus_t, buses) + +int snd_i2c_bus_create(snd_card_t *card, const char *name, snd_i2c_bus_t *master, snd_i2c_bus_t **ri2c); +int snd_i2c_device_create(snd_i2c_bus_t *bus, const char *name, unsigned char addr, snd_i2c_device_t **rdevice); +int snd_i2c_device_free(snd_i2c_device_t *device); + +static inline void snd_i2c_lock(snd_i2c_bus_t *bus) { spin_lock(&(bus->master ? bus->master->lock : bus->lock)); } +static inline void snd_i2c_unlock(snd_i2c_bus_t *bus) { spin_unlock(&(bus->master ? bus->master->lock : bus->lock)); } + +int snd_i2c_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +int snd_i2c_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +int snd_i2c_probeaddr(snd_i2c_bus_t *bus, unsigned short addr); + +#endif /* __SOUND_I2C_H */ diff -Nru a/include/sound/info.h b/include/sound/info.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/info.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,186 @@ +#ifndef __SOUND_INFO_H +#define __SOUND_INFO_H + +/* + * Header file for info interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +/* buffer for information */ +struct snd_info_buffer { + char *buffer; /* pointer to begin of buffer */ + char *curr; /* current position in buffer */ + unsigned long size; /* current size */ + unsigned long len; /* total length of buffer */ + int stop; /* stop flag */ + int error; /* error code */ +}; + +typedef struct snd_info_buffer snd_info_buffer_t; + +#define SNDRV_INFO_CONTENT_TEXT 0 +#define SNDRV_INFO_CONTENT_DATA 1 +#define SNDRV_INFO_CONTENT_DEVICE 2 + +struct snd_info_entry; + +struct snd_info_entry_text { + unsigned long read_size; + unsigned long write_size; + void (*read) (snd_info_entry_t *entry, snd_info_buffer_t * buffer); + void (*write) (snd_info_entry_t *entry, snd_info_buffer_t * buffer); +}; + +struct snd_info_entry_ops { + int (*open) (snd_info_entry_t *entry, + unsigned short mode, void **file_private_data); + int (*release) (snd_info_entry_t * entry, + unsigned short mode, void *file_private_data); + long (*read) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, char *buf, long count); + long (*write) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, const char *buf, long count); + long long (*llseek) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, long long offset, int orig); + unsigned int (*poll) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, poll_table * wait); + int (*ioctl) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, unsigned int cmd, unsigned long arg); + int (*mmap) (snd_info_entry_t *entry, void *file_private_data, + struct inode * inode, struct file * file, + struct vm_area_struct * vma); +}; + +struct snd_info_entry_device { + unsigned short major; + unsigned short minor; +}; + +struct snd_info_entry { + const char *name; + mode_t mode; + long size; + unsigned short content; + union { + struct snd_info_entry_text text; + struct snd_info_entry_ops *ops; + struct snd_info_entry_device device; + } c; + snd_info_entry_t *parent; + snd_card_t *card; + struct module *module; + void *private_data; + void (*private_free)(snd_info_entry_t *entry); + struct proc_dir_entry *p; + struct semaphore access; +}; + +extern int snd_info_check_reserved_words(const char *str); + +#ifdef CONFIG_SND_OSSEMUL +extern int snd_info_minor_register(void); +extern int snd_info_minor_unregister(void); +#endif + + +#ifdef CONFIG_PROC_FS + +extern snd_info_entry_t *snd_seq_root; + +int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3))); +int snd_info_init(void); +int snd_info_done(void); + +int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len); +char *snd_info_get_str(char *dest, char *src, int len); +snd_info_entry_t *snd_info_create_module_entry(struct module * module, + const char *name, + snd_info_entry_t * parent); +snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, + const char *name, + snd_info_entry_t * parent); +void snd_info_free_entry(snd_info_entry_t * entry); +snd_info_entry_t *snd_info_create_device(const char *name, + unsigned int number, + unsigned int mode); +void snd_info_free_device(snd_info_entry_t * entry); +int snd_info_store_text(snd_info_entry_t * entry); +int snd_info_restore_text(snd_info_entry_t * entry); + +int snd_info_card_register(snd_card_t * card); +int snd_info_card_unregister(snd_card_t * card); +int snd_info_register(snd_info_entry_t * entry); +int snd_info_unregister(snd_info_entry_t * entry); + +struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, + struct proc_dir_entry *parent); +void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de); + +#else + +#define snd_seq_root NULL + +static inline int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) { return 0; } +static inline int snd_info_init(void) { return 0; } +static inline int snd_info_done(void) { return 0; } + +static inline int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) { return 0; } +static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; } +static inline snd_info_entry_t *snd_info_create_module_entry(struct module * module, const char *name) { return NULL; } +static inline snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, const char *name) { return NULL; } +static inline void snd_info_free_entry(snd_info_entry_t * entry) { ; } +static inline snd_info_entry_t *snd_info_create_device(const char *name, + unsigned int number, + unsigned int mode) { return NULL; } +static inline void snd_info_free_device(snd_info_entry_t * entry) { ; } + +static inline int snd_info_card_register(snd_card_t * card) { return 0; } +static inline int snd_info_card_unregister(snd_card_t * card) { return 0; } +static inline int snd_info_register(snd_info_entry_t * entry) { return 0; } +static inline int snd_info_unregister(snd_info_entry_t * entry) { return 0; } + +static inline struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { return 0; } +static inline void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de) { ; } + +#endif + +/* + * OSS info part + */ + +#ifdef CONFIG_SND_OSSEMUL + +#define SNDRV_OSS_INFO_DEV_AUDIO 0 +#define SNDRV_OSS_INFO_DEV_SYNTH 1 +#define SNDRV_OSS_INFO_DEV_MIDI 2 +#define SNDRV_OSS_INFO_DEV_TIMERS 4 +#define SNDRV_OSS_INFO_DEV_MIXERS 5 + +#define SNDRV_OSS_INFO_DEV_COUNT 6 + +extern int snd_oss_info_register(int dev, int num, char *string); +#define snd_oss_info_unregister(dev, num) snd_oss_info_register(dev, num, NULL) + +#endif /* CONFIG_SND_OSSEMUL */ + +#endif /* __SOUND_INFO_H */ diff -Nru a/include/sound/initval.h b/include/sound/initval.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/initval.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,171 @@ +#ifndef __SOUND_INITVAL_H +#define __SOUND_INITVAL_H + +/* + * Init values for soundcard modules + * Copyright (c) by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef MODULE_GENERIC_STRING +#ifdef MODULE +#define MODULE_GENERIC_STRING(name, string) \ +static const char __module_generic_string_##name [] \ + __attribute__ ((section(".modstring"))) = #name "=" string; +#else +#define MODULE_GENERIC_STRING(name, string) +#endif +#endif + +#define MODULE_CLASSES(val) MODULE_GENERIC_STRING(info_classes, val) +#define MODULE_DEVICES(val) MODULE_GENERIC_STRING(info_devices, val) +#define MODULE_PARM_SYNTAX(id, val) MODULE_GENERIC_STRING(info_parm_##id, val) + +#define SNDRV_AUTO_PORT 0xffff +#define SNDRV_AUTO_IRQ 0xffff +#define SNDRV_AUTO_DMA 0xffff +#define SNDRV_AUTO_DMA_SIZE (0x7fffffff) + +#define SNDRV_DEFAULT_IDX1 (-1) +#define SNDRV_DEFAULT_STR1 NULL +#define SNDRV_DEFAULT_ENABLE1 1 +#define SNDRV_DEFAULT_PORT1 SNDRV_AUTO_PORT +#define SNDRV_DEFAULT_IRQ1 SNDRV_AUTO_IRQ +#define SNDRV_DEFAULT_DMA1 SNDRV_AUTO_DMA +#define SNDRV_DEFAULT_DMA_SIZE1 SNDRV_AUTO_DMA_SIZE +#define SNDRV_DEFAULT_PTR1 SNDRV_DEFAULT_STR1 + +#define SNDRV_DEFAULT_IDX { [0 ... (SNDRV_CARDS-1)] = -1 } +#define SNDRV_DEFAULT_STR { [0 ... (SNDRV_CARDS-1)] = NULL } +#define SNDRV_DEFAULT_ENABLE { 1, [1 ... (SNDRV_CARDS-1)] = 0 } +#define SNDRV_DEFAULT_ENABLE_PNP { [0 ... (SNDRV_CARDS-1)] = 1 } +#ifdef __ISAPNP__ +#define SNDRV_DEFAULT_ENABLE_ISAPNP SNDRV_DEFAULT_ENABLE_PNP +#else +#define SNDRV_DEFAULT_ENABLE_ISAPNP SNDRV_DEFAULT_ENABLE +#endif +#define SNDRV_DEFAULT_PORT { SNDRV_AUTO_PORT, [1 ... (SNDRV_CARDS-1)] = -1 } +#define SNDRV_DEFAULT_IRQ { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_IRQ } +#define SNDRV_DEFAULT_DMA { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA } +#define SNDRV_DEFAULT_DMA_SIZE { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA_SIZE } +#define SNDRV_DEFAULT_PTR SNDRV_DEFAULT_STR + +#define SNDRV_BOOLEAN_TRUE_DESC "allows:{{0,Disabled},{1,Enabled}},default:1,dialog:check" +#define SNDRV_BOOLEAN_FALSE_DESC "allows:{{0,Disabled},{1,Enabled}},default:0,dialog:check" + +#define SNDRV_ENABLED "enable:(snd_enable)" + +#define SNDRV_INDEX_DESC SNDRV_ENABLED ",allows:{{0,7}},unique,skill:required,dialog:list" +#define SNDRV_ID_DESC SNDRV_ENABLED ",unique" +#define SNDRV_ENABLE_DESC SNDRV_BOOLEAN_FALSE_DESC +#define SNDRV_ISAPNP_DESC SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC +#define SNDRV_DMA8_DESC SNDRV_ENABLED ",allows:{{0,1},{3}},dialog:list" +#define SNDRV_DMA16_DESC SNDRV_ENABLED ",allows:{{5,7}},dialog:list" +#define SNDRV_DMA_DESC SNDRV_ENABLED ",allows:{{0,1},{3},{5,7}},dialog:list" +#define SNDRV_IRQ_DESC SNDRV_ENABLED ",allows:{{5},{7},{9},{10,12},{14,15}},dialog:list" +#define SNDRV_DMA_SIZE_DESC SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced" +#define SNDRV_DMA8_SIZE_DESC SNDRV_ENABLED ",allows:{{4, 64}},default:64,skill:advanced" +#define SNDRV_DMA16_SIZE_DESC SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced" +#define SNDRV_PORT12_DESC SNDRV_ENABLED ",allows:{{0,0x3fff}},base:16" +#define SNDRV_PORT_DESC SNDRV_ENABLED ",allows:{{0,0xffff}},base:16" + +#ifdef SNDRV_LEGACY_AUTO_PROBE +static int snd_legacy_auto_probe(unsigned long *ports, int (*probe)(unsigned long port)) +{ + int result = 0; /* number of detected cards */ + + while ((signed long)*ports != -1) { + if (probe(*ports) >= 0) + result++; + ports++; + } + return result; +} +#endif + +#ifdef SNDRV_LEGACY_FIND_FREE_IOPORT +static long snd_legacy_find_free_ioport(long *port_table, long size) +{ + while (*port_table != -1) { + if (!check_region(*port_table, size)) + return *port_table; + port_table++; + } + return -1; +} +#endif + +#ifdef SNDRV_LEGACY_FIND_FREE_IRQ +static void snd_legacy_empty_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int snd_legacy_find_free_irq(int *irq_table) +{ + while (*irq_table != -1) { + if (!request_irq(*irq_table, snd_legacy_empty_irq_handler, + SA_INTERRUPT, "ALSA Test IRQ", (void *) irq_table)) { + free_irq(*irq_table, (void *) irq_table); + return *irq_table; + } + irq_table++; + } + return -1; +} +#endif + +#ifdef SNDRV_LEGACY_FIND_FREE_DMA +static int snd_legacy_find_free_dma(int *dma_table) +{ + while (*dma_table != -1) { + if (!request_dma(*dma_table, "ALSA Test DMA")) { + free_dma(*dma_table); + return *dma_table; + } + dma_table++; + } + return -1; +} +#endif + +#if defined(SNDRV_GET_ID) && !defined(MODULE) +#include +static int __init get_id(char **str, char **dst) +{ + char *s, *d; + + if (!(*str) || !(**str)) + return 0; + for (s = *str; isalpha(*s) || isdigit(*s) || *s == '_'; s++); + if (s != *str) { + *dst = (char *)kmalloc(s - *str, GFP_KERNEL); + if ((d = *dst) != NULL) { + s = *str; + while (isalpha(*s) || isdigit(*s) || *s == '_') + *d++ = *s++; + } + } + *str = s; + if (*s == ',') { + (*str)++; + return 2; + } + return 1; +} +#endif + +#endif /* __SOUND_INITVAL_H */ diff -Nru a/include/sound/minors.h b/include/sound/minors.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/minors.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,86 @@ +#ifndef __SOUND_MINORS_H +#define __SOUND_MINORS_H + +/* + * MINOR numbers + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_MINOR_DEVICES 32 +#define SNDRV_MINOR_CARD(minor) ((minor) >> 5) +#define SNDRV_MINOR_DEVICE(minor) ((minor) & 0x001f) +#define SNDRV_MINOR(card, dev) (((card) << 5) | (dev)) + +#define SNDRV_MINOR_CONTROL 0 /* 0 - 0 */ +#define SNDRV_MINOR_SEQUENCER 1 +#define SNDRV_MINOR_TIMER (1+32) +#define SNDRV_MINOR_HWDEP 4 /* 4 - 7 */ +#define SNDRV_MINOR_HWDEPS 4 +#define SNDRV_MINOR_RAWMIDI 8 /* 8 - 11 */ +#define SNDRV_MINOR_RAWMIDIS 4 +#define SNDRV_MINOR_PCM_PLAYBACK 16 /* 16 - 23 */ +#define SNDRV_MINOR_PCM_CAPTURE 24 /* 24 - 31 */ +#define SNDRV_MINOR_PCMS 8 + +#define SNDRV_DEVICE_TYPE_CONTROL SNDRV_MINOR_CONTROL +#define SNDRV_DEVICE_TYPE_HWDEP SNDRV_MINOR_HWDEP +#define SNDRV_DEVICE_TYPE_MIXER SNDRV_MINOR_MIXER +#define SNDRV_DEVICE_TYPE_RAWMIDI SNDRV_MINOR_RAWMIDI +#define SNDRV_DEVICE_TYPE_PCM_PLAYBACK SNDRV_MINOR_PCM_PLAYBACK +#define SNDRV_DEVICE_TYPE_PCM_PLOOP SNDRV_MINOR_PCM_PLOOP +#define SNDRV_DEVICE_TYPE_PCM_CAPTURE SNDRV_MINOR_PCM_CAPTURE +#define SNDRV_DEVICE_TYPE_PCM_CLOOP SNDRV_MINOR_PCM_CLOOP +#define SNDRV_DEVICE_TYPE_SEQUENCER SNDRV_MINOR_SEQUENCER +#define SNDRV_DEVICE_TYPE_TIMER SNDRV_MINOR_TIMER + +#ifdef CONFIG_SND_OSSEMUL + +#define SNDRV_MINOR_OSS_DEVICES 16 +#define SNDRV_MINOR_OSS_CARD(minor) ((minor) >> 4) +#define SNDRV_MINOR_OSS_DEVICE(minor) ((minor) & 0x000f) +#define SNDRV_MINOR_OSS(card, dev) (((card) << 4) | (dev)) + +#define SNDRV_MINOR_OSS_MIXER 0 /* /dev/mixer - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_SEQUENCER 1 /* /dev/sequencer - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_MIDI 2 /* /dev/midi - native midi interface - OSS 3.XX compatible - UART */ +#define SNDRV_MINOR_OSS_PCM 3 /* alias */ +#define SNDRV_MINOR_OSS_PCM_8 3 /* /dev/dsp - 8bit PCM - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_AUDIO 4 /* /dev/audio - SunSparc compatible */ +#define SNDRV_MINOR_OSS_PCM_16 5 /* /dev/dsp16 - 16bit PCM - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_SNDSTAT 6 /* /dev/sndstat - for compatibility with OSS */ +#define SNDRV_MINOR_OSS_RESERVED7 7 /* reserved for future use */ +#define SNDRV_MINOR_OSS_MUSIC 8 /* /dev/music - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_DMMIDI 9 /* /dev/dmmidi0 - this device can have another minor # with OSS */ +#define SNDRV_MINOR_OSS_DMFM 10 /* /dev/dmfm0 - this device can have another minor # with OSS */ +#define SNDRV_MINOR_OSS_MIXER1 11 /* alternate mixer */ +#define SNDRV_MINOR_OSS_PCM1 12 /* alternate PCM (GF-A-1) */ +#define SNDRV_MINOR_OSS_MIDI1 13 /* alternate midi - SYNTH */ +#define SNDRV_MINOR_OSS_DMMIDI1 14 /* alternate dmmidi - SYNTH */ +#define SNDRV_MINOR_OSS_RESERVED15 15 /* reserved for future use */ + +#define SNDRV_OSS_DEVICE_TYPE_MIXER 0 +#define SNDRV_OSS_DEVICE_TYPE_SEQUENCER 1 +#define SNDRV_OSS_DEVICE_TYPE_PCM 2 +#define SNDRV_OSS_DEVICE_TYPE_MIDI 3 +#define SNDRV_OSS_DEVICE_TYPE_DMFM 4 +#define SNDRV_OSS_DEVICE_TYPE_SNDSTAT 5 +#define SNDRV_OSS_DEVICE_TYPE_MUSIC 6 + +#endif + +#endif /* __SOUND_MINORS_H */ diff -Nru a/include/sound/mixer_oss.h b/include/sound/mixer_oss.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/mixer_oss.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,71 @@ +#ifndef __SOUND_MIXER_OSS_H +#define __SOUND_MIXER_OSS_H + +/* + * OSS MIXER API + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef CONFIG_SND_OSSEMUL + +typedef struct _snd_oss_mixer_slot snd_mixer_oss_slot_t; +typedef struct _snd_oss_file snd_mixer_oss_file_t; + +typedef int (*snd_mixer_oss_get_volume_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int *left, int *right); +typedef int (*snd_mixer_oss_put_volume_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int left, int right); +typedef int (*snd_mixer_oss_get_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int *active); +typedef int (*snd_mixer_oss_put_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int active); +typedef int (*snd_mixer_oss_get_recsrce_t)(snd_mixer_oss_file_t *fmixer, int *active_index); +typedef int (*snd_mixer_oss_put_recsrce_t)(snd_mixer_oss_file_t *fmixer, int active_index); + +struct _snd_oss_mixer_slot { + int number; + int stereo: 1; + snd_mixer_oss_get_volume_t get_volume; + snd_mixer_oss_put_volume_t put_volume; + snd_mixer_oss_get_recsrc_t get_recsrc; + snd_mixer_oss_put_recsrc_t put_recsrc; + unsigned long private_value; + void *private_data; + void (*private_free)(snd_mixer_oss_slot_t *slot); +}; + +struct _snd_oss_mixer { + snd_card_t *card; + char id[16]; + char name[32]; + snd_mixer_oss_slot_t slots[32]; /* OSS mixer slots */ + unsigned int mask_recsrc; /* exclusive recsrc mask */ + snd_mixer_oss_get_recsrce_t get_recsrc; + snd_mixer_oss_put_recsrce_t put_recsrc; + void *private_data_recsrc; + void (*private_free_recsrc)(snd_mixer_oss_t *mixer); + /* --- */ + int oss_recsrc; +}; + +struct _snd_oss_file { + int volume[32][2]; + snd_card_t *card; + snd_mixer_oss_t *mixer; +}; + +#endif /* CONFIG_SND_OSSEMUL */ + +#endif /* __SOUND_MIXER_OSS_H */ diff -Nru a/include/sound/mpu401.h b/include/sound/mpu401.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/mpu401.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,109 @@ +#ifndef __SOUND_MPU401_H +#define __SOUND_MPU401_H + +/* + * Header file for MPU-401 and compatible cards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "rawmidi.h" + +#define MPU401_HW_MPU401 1 /* native MPU401 */ +#define MPU401_HW_SB 2 /* SoundBlaster MPU-401 UART */ +#define MPU401_HW_ES1688 3 /* AudioDrive ES1688 MPU-401 UART */ +#define MPU401_HW_OPL3SA2 4 /* Yamaha OPL3-SA2 */ +#define MPU401_HW_SONICVIBES 5 /* S3 SonicVibes */ +#define MPU401_HW_CS4232 6 /* CS4232 */ +#define MPU401_HW_ES18XX 7 /* AudioDrive ES18XX MPU-401 UART */ +#define MPU401_HW_FM801 8 /* ForteMedia FM801 */ +#define MPU401_HW_TRID4DWAVE 9 /* Trident 4DWave */ +#define MPU401_HW_AZT2320 10 /* Aztech AZT2320 */ +#define MPU401_HW_ALS100 11 /* Avance Logic ALS100 */ +#define MPU401_HW_ICE1712 12 /* Envy24 */ +#define MPU401_HW_VIA686A 13 /* VIA 82C686A */ +#define MPU401_HW_YMFPCI 14 /* YMF DS-XG PCI */ +#define MPU401_HW_CMIPCI 15 /* CMIPCI MPU-401 UART */ +#define MPU401_HW_ALS4000 16 /* Avance Logic ALS4000 */ + +#define MPU401_MODE_BIT_INPUT 0 +#define MPU401_MODE_BIT_OUTPUT 1 +#define MPU401_MODE_BIT_INPUT_TRIGGER 2 +#define MPU401_MODE_BIT_OUTPUT_TRIGGER 3 +#define MPU401_MODE_BIT_RX_LOOP 4 +#define MPU401_MODE_BIT_TX_LOOP 5 + +#define MPU401_MODE_INPUT (1<port + 1) +#define MPU401D(mpu) ((mpu)->port + 0) + +/* + + */ + +void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +int snd_mpu401_uart_new(snd_card_t * card, + int device, + unsigned short hardware, + unsigned long port, + int integrated, + int irq, + int irq_flags, + snd_rawmidi_t ** rrawmidi); + +#endif /* __SOUND_MPU401_H */ diff -Nru a/include/sound/opl3.h b/include/sound/opl3.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/opl3.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,332 @@ +#ifndef __SOUND_OPL3_H +#define __SOUND_OPL3_H + +/* + * Definitions of the OPL-3 registers. + * + * Copyright (c) by Jaroslav Kysela , + * Hannu Savolainen 1993-1996 + * + * + * The OPL-3 mode is switched on by writing 0x01, to the offset 5 + * of the right side. + * + * Another special register at the right side is at offset 4. It contains + * a bit mask defining which voices are used as 4 OP voices. + * + * The percussive mode is implemented in the left side only. + * + * With the above exceptions the both sides can be operated independently. + * + * A 4 OP voice can be created by setting the corresponding + * bit at offset 4 of the right side. + * + * For example setting the rightmost bit (0x01) changes the + * first voice on the right side to the 4 OP mode. The fourth + * voice is made inaccessible. + * + * If a voice is set to the 2 OP mode, it works like 2 OP modes + * of the original YM3812 (AdLib). In addition the voice can + * be connected the left, right or both stereo channels. It can + * even be left unconnected. This works with 4 OP voices also. + * + * The stereo connection bits are located in the FEEDBACK_CONNECTION + * register of the voice (0xC0-0xC8). In 4 OP voices these bits are + * in the second half of the voice. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "driver.h" +#include +#include "core.h" +#include "hwdep.h" +#include "timer.h" +#include "seq_midi_emul.h" +#ifdef CONFIG_SND_OSSEMUL +#include "seq_oss.h" +#include "seq_oss_legacy.h" +#endif +#include "seq_device.h" +#include "ainstr_fm.h" + +/* + * Register numbers for the global registers + */ + +#define OPL3_REG_TEST 0x01 +#define OPL3_ENABLE_WAVE_SELECT 0x20 + +#define OPL3_REG_TIMER1 0x02 +#define OPL3_REG_TIMER2 0x03 +#define OPL3_REG_TIMER_CONTROL 0x04 /* Left side */ +#define OPL3_IRQ_RESET 0x80 +#define OPL3_TIMER1_MASK 0x40 +#define OPL3_TIMER2_MASK 0x20 +#define OPL3_TIMER1_START 0x01 +#define OPL3_TIMER2_START 0x02 + +#define OPL3_REG_CONNECTION_SELECT 0x04 /* Right side */ +#define OPL3_LEFT_4OP_0 0x01 +#define OPL3_LEFT_4OP_1 0x02 +#define OPL3_LEFT_4OP_2 0x04 +#define OPL3_RIGHT_4OP_0 0x08 +#define OPL3_RIGHT_4OP_1 0x10 +#define OPL3_RIGHT_4OP_2 0x20 + +#define OPL3_REG_MODE 0x05 /* Right side */ +#define OPL3_OPL3_ENABLE 0x01 /* OPL3 mode */ +#define OPL3_OPL4_ENABLE 0x02 /* OPL4 mode */ + +#define OPL3_REG_KBD_SPLIT 0x08 /* Left side */ +#define OPL3_COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ +#define OPL3_KEYBOARD_SPLIT 0x40 + +#define OPL3_REG_PERCUSSION 0xbd /* Left side only */ +#define OPL3_TREMOLO_DEPTH 0x80 +#define OPL3_VIBRATO_DEPTH 0x40 +#define OPL3_PERCUSSION_ENABLE 0x20 +#define OPL3_BASSDRUM_ON 0x10 +#define OPL3_SNAREDRUM_ON 0x08 +#define OPL3_TOMTOM_ON 0x04 +#define OPL3_CYMBAL_ON 0x02 +#define OPL3_HIHAT_ON 0x01 + +/* + * Offsets to the register banks for operators. To get the + * register number just add the operator offset to the bank offset + * + * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) + */ +#define OPL3_REG_AM_VIB 0x20 +#define OPL3_TREMOLO_ON 0x80 +#define OPL3_VIBRATO_ON 0x40 +#define OPL3_SUSTAIN_ON 0x20 +#define OPL3_KSR 0x10 /* Key scaling rate */ +#define OPL3_MULTIPLE_MASK 0x0f /* Frequency multiplier */ + + /* + * KSL/Total level (0x40 to 0x55) + */ +#define OPL3_REG_KSL_LEVEL 0x40 +#define OPL3_KSL_MASK 0xc0 /* Envelope scaling bits */ +#define OPL3_TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ + +/* + * Attack / Decay rate (0x60 to 0x75) + */ +#define OPL3_REG_ATTACK_DECAY 0x60 +#define OPL3_ATTACK_MASK 0xf0 +#define OPL3_DECAY_MASK 0x0f + +/* + * Sustain level / Release rate (0x80 to 0x95) + */ +#define OPL3_REG_SUSTAIN_RELEASE 0x80 +#define OPL3_SUSTAIN_MASK 0xf0 +#define OPL3_RELEASE_MASK 0x0f + +/* + * Wave select (0xE0 to 0xF5) + */ +#define OPL3_REG_WAVE_SELECT 0xe0 +#define OPL3_WAVE_SELECT_MASK 0x07 + +/* + * Offsets to the register banks for voices. Just add to the + * voice number to get the register number. + * + * F-Number low bits (0xA0 to 0xA8). + */ +#define OPL3_REG_FNUM_LOW 0xa0 + +/* + * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) + */ +#define OPL3_REG_KEYON_BLOCK 0xb0 +#define OPL3_KEYON_BIT 0x20 +#define OPL3_BLOCKNUM_MASK 0x1c +#define OPL3_FNUM_HIGH_MASK 0x03 + +/* + * Feedback / Connection (0xc0 to 0xc8) + * + * These registers have two new bits when the OPL-3 mode + * is selected. These bits controls connecting the voice + * to the stereo channels. For 4 OP voices this bit is + * defined in the second half of the voice (add 3 to the + * register offset). + * + * For 4 OP voices the connection bit is used in the + * both halves (gives 4 ways to connect the operators). + */ +#define OPL3_REG_FEEDBACK_CONNECTION 0xc0 +#define OPL3_FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ +#define OPL3_CONNECTION_BIT 0x01 +/* + * In the 4 OP mode there is four possible configurations how the + * operators can be connected together (in 2 OP modes there is just + * AM or FM). The 4 OP connection mode is defined by the rightmost + * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves. + * + * First half Second half Mode + * + * +---+ + * v | + * 0 0 >+-1-+--2--3--4--> + * + * + * + * +---+ + * | | + * 0 1 >+-1-+--2-+ + * |-> + * >--3----4-+ + * + * +---+ + * | | + * 1 0 >+-1-+-----+ + * |-> + * >--2--3--4-+ + * + * +---+ + * | | + * 1 1 >+-1-+--+ + * | + * >--2--3-+-> + * | + * >--4----+ + */ +#define OPL3_STEREO_BITS 0x30 /* OPL-3 only */ +#define OPL3_VOICE_TO_LEFT 0x10 +#define OPL3_VOICE_TO_RIGHT 0x20 + +/* + + */ + +#define OPL3_LEFT 0x0000 +#define OPL3_RIGHT 0x0100 + +#define OPL3_HW_AUTO 0x0000 +#define OPL3_HW_OPL2 0x0200 +#define OPL3_HW_OPL3 0x0300 +#define OPL3_HW_OPL3_SV 0x0301 /* S3 SonicVibes */ +#define OPL3_HW_OPL3_CS 0x0302 /* CS4232/CS4236+ */ +#define OPL3_HW_OPL3_FM801 0x0303 /* FM801 */ +#define OPL3_HW_OPL3_CS4281 0x0304 /* CS4281 */ +#define OPL3_HW_OPL4 0x0400 +#define OPL3_HW_MASK 0xff00 + +#define MAX_OPL2_VOICES 9 +#define MAX_OPL3_VOICES 18 + +typedef struct snd_opl3 opl3_t; + +/* + * A structure to keep track of each hardware voice + */ +typedef struct snd_opl3_voice { + int state; /* status */ +#define SNDRV_OPL3_ST_OFF 0 /* Not playing */ +#define SNDRV_OPL3_ST_ON_2OP 1 /* 2op voice is allocated */ +#define SNDRV_OPL3_ST_ON_4OP 2 /* 4op voice is allocated */ +#define SNDRV_OPL3_ST_NOT_AVAIL -1 /* voice is not available */ + + unsigned int time; /* An allocation time */ + unsigned char note; /* Note currently assigned to this voice */ + + unsigned long note_off; /* note-off time */ + int note_off_check; /* check note-off time */ + + unsigned char keyon_reg; /* KON register shadow */ + + snd_midi_channel_t *chan; /* Midi channel for this note */ +} snd_opl3_voice_t; + +struct snd_opl3 { + unsigned long l_port; + unsigned long r_port; + struct resource *res_l_port; + struct resource *res_r_port; + unsigned short hardware; + /* hardware access */ + void (*command) (opl3_t * opl3, unsigned short cmd, unsigned char val); + unsigned short timer_enable; + int seq_dev_num; /* sequencer device number */ + snd_timer_t *timer1; + snd_timer_t *timer2; + spinlock_t timer_lock; + + spinlock_t reg_lock; + snd_card_t *card; /* The card that this belongs to */ + int used; /* usage flag - exclusive */ + unsigned char fm_mode; /* OPL mode, see SNDRV_DM_FM_MODE_XXX */ + unsigned char rhythm; /* percussion mode flag */ + unsigned char max_voices; /* max number of voices */ +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#define SNDRV_OPL3_MODE_SYNTH 0 /* OSS - voices allocated by application */ +#define SNDRV_OPL3_MODE_SEQ 1 /* ALSA - driver handles voice allocation */ + int synth_mode; /* synth mode */ + int seq_client; + + snd_seq_device_t *seq_dev; /* sequencer device */ + snd_midi_channel_set_t * chset; + +#ifdef CONFIG_SND_OSSEMUL + snd_seq_device_t *oss_seq_dev; /* OSS sequencer device, WIP */ + snd_midi_channel_set_t * oss_chset; +#endif + + snd_seq_kinstr_ops_t fm_ops; + snd_seq_kinstr_list_t *ilist; + + snd_opl3_voice_t voices[MAX_OPL3_VOICES]; /* Voices (OPL3 'channel') */ + int use_time; /* allocation counter */ + + unsigned short connection_reg; /* connection reg shadow */ + unsigned char drum_reg; /* percussion reg shadow */ + + spinlock_t voice_lock; /* Lock for voice access */ + + struct timer_list tlist; /* timer for note-offs and effects */ + int sys_timer_status; /* system timer run status */ + spinlock_t sys_timer_lock; /* Lock for system timer access */ +#endif + struct semaphore access_mutex; /* locking */ +}; + +/* opl3.c */ +void snd_opl3_interrupt(snd_hwdep_t * hw); +int snd_opl3_create(snd_card_t * card, + unsigned long l_port, unsigned long r_port, + unsigned short hardware, + int integrated, + opl3_t ** opl3); +int snd_opl3_timer_new(opl3_t * opl3, int timer1_dev, int timer2_dev); +int snd_opl3_hwdep_new(opl3_t * opl3, int device, int seq_device, + snd_hwdep_t ** rhwdep); + +/* opl3_synth */ +int snd_opl3_open(snd_hwdep_t * hw, struct file *file); +int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file, + unsigned int cmd, unsigned long arg); +int snd_opl3_release(snd_hwdep_t * hw, struct file *file); + +void snd_opl3_reset(opl3_t * opl3); + +#endif /* __SOUND_OPL3_H */ diff -Nru a/include/sound/pcm.h b/include/sound/pcm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/pcm.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,863 @@ +#ifndef __SOUND_PCM_H +#define __SOUND_PCM_H + +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +typedef sndrv_pcm_uframes_t snd_pcm_uframes_t; +typedef sndrv_pcm_sframes_t snd_pcm_sframes_t; +typedef enum sndrv_pcm_class snd_pcm_class_t; +typedef enum sndrv_pcm_subclass snd_pcm_subclass_t; +typedef enum sndrv_pcm_stream snd_pcm_stream_t; +typedef enum sndrv_pcm_access snd_pcm_access_t; +typedef enum sndrv_pcm_format snd_pcm_format_t; +typedef enum sndrv_pcm_subformat snd_pcm_subformat_t; +typedef enum sndrv_pcm_state snd_pcm_state_t; +typedef union sndrv_pcm_sync_id snd_pcm_sync_id_t; +typedef struct sndrv_pcm_info snd_pcm_info_t; +typedef enum sndrv_pcm_hw_param snd_pcm_hw_param_t; +typedef struct sndrv_pcm_hw_params snd_pcm_hw_params_t; +typedef enum sndrv_pcm_start snd_pcm_start_t; +typedef enum sndrv_pcm_xrun snd_pcm_xrun_t; +typedef enum sndrv_pcm_tstamp snd_pcm_tstamp_t; +typedef struct sndrv_pcm_sw_params snd_pcm_sw_params_t; +typedef struct sndrv_pcm_channel_info snd_pcm_channel_info_t; +typedef struct sndrv_pcm_status snd_pcm_status_t; +typedef struct sndrv_pcm_mmap_status snd_pcm_mmap_status_t; +typedef struct sndrv_pcm_mmap_control snd_pcm_mmap_control_t; + +#define _snd_pcm_substream_chip(substream) ((substream)->pcm->private_data) +#define snd_pcm_substream_chip(substream) snd_magic_cast1(chip_t, _snd_pcm_substream_chip(substream), return -ENXIO) +#define _snd_pcm_chip(pcm) ((pcm)->private_data) +#define snd_pcm_chip(pcm) snd_magic_cast1(chip_t, _snd_pcm_chip(pcm), return -ENXIO) + +typedef struct _snd_pcm_file snd_pcm_file_t; +typedef struct _snd_pcm_runtime snd_pcm_runtime_t; + +#ifdef CONFIG_SND_OSSEMUL +#include "pcm_oss.h" +#endif + +/* + * Hardware (lowlevel) section + */ + +typedef struct _snd_pcm_hardware { + unsigned int info; /* SNDRV_PCM_INFO_* */ + unsigned int formats; /* SNDRV_PCM_FMTBIT_* */ + unsigned int rates; /* SNDRV_PCM_RATE_* */ + unsigned int rate_min; /* min rate */ + unsigned int rate_max; /* max rate */ + unsigned int channels_min; /* min channels */ + unsigned int channels_max; /* max channels */ + size_t buffer_bytes_max; /* max buffer size */ + size_t period_bytes_min; /* min period size */ + size_t period_bytes_max; /* max period size */ + unsigned int periods_min; /* min # of periods */ + unsigned int periods_max; /* max # of periods */ + size_t fifo_size; /* fifo size in bytes */ +} snd_pcm_hardware_t; + +typedef struct _snd_pcm_ops { + int (*open)(snd_pcm_substream_t *substream); + int (*close)(snd_pcm_substream_t *substream); + int (*ioctl)(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg); + int (*hw_params)(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * params); + int (*hw_free)(snd_pcm_substream_t *substream); + int (*prepare)(snd_pcm_substream_t * substream); + int (*trigger)(snd_pcm_substream_t * substream, int cmd); + snd_pcm_uframes_t (*pointer)(snd_pcm_substream_t * substream); + int (*copy)(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos, + void *buf, snd_pcm_uframes_t count); + int (*silence)(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count); +} snd_pcm_ops_t; + +/* + * + */ + +#define SNDRV_PCM_DEVICES 8 + +#define SNDRV_PCM_IOCTL1_FALSE ((void *)0) +#define SNDRV_PCM_IOCTL1_TRUE ((void *)1) + +#define SNDRV_PCM_IOCTL1_RESET 0 +#define SNDRV_PCM_IOCTL1_INFO 1 +#define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 +#define SNDRV_PCM_IOCTL1_GSTATE 3 + +#define SNDRV_PCM_TRIGGER_STOP 0 +#define SNDRV_PCM_TRIGGER_START 1 +#define SNDRV_PCM_TRIGGER_PAUSE_PUSH 3 +#define SNDRV_PCM_TRIGGER_PAUSE_RELEASE 4 +#define SNDRV_PCM_TRIGGER_SUSPEND 5 +#define SNDRV_PCM_TRIGGER_RESUME 6 + +#define SNDRV_PCM_DMA_TYPE_CONTINUOUS 0 /* continuous no-DMA memory */ +#define SNDRV_PCM_DMA_TYPE_ISA 1 /* ISA continuous */ +#define SNDRV_PCM_DMA_TYPE_PCI 2 /* PCI continuous */ + +/* If you change this don't forget to changed snd_pcm_rates table in pcm_lib.c */ +#define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */ +#define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */ +#define SNDRV_PCM_RATE_11025 (1<<2) /* 11025Hz */ +#define SNDRV_PCM_RATE_16000 (1<<3) /* 16000Hz */ +#define SNDRV_PCM_RATE_22050 (1<<4) /* 22050Hz */ +#define SNDRV_PCM_RATE_32000 (1<<5) /* 32000Hz */ +#define SNDRV_PCM_RATE_44100 (1<<6) /* 44100Hz */ +#define SNDRV_PCM_RATE_48000 (1<<7) /* 48000Hz */ +#define SNDRV_PCM_RATE_64000 (1<<8) /* 64000Hz */ +#define SNDRV_PCM_RATE_88200 (1<<9) /* 88200Hz */ +#define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */ +#define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */ +#define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */ + +#define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */ +#define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */ + +#define SNDRV_PCM_RATE_8000_44100 (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|\ + SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|\ + SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100) +#define SNDRV_PCM_RATE_8000_48000 (SNDRV_PCM_RATE_8000_44100|SNDRV_PCM_RATE_48000) +#define SNDRV_PCM_RATE_8000_96000 (SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_64000|\ + SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000) +#define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\ + SNDRV_PCM_RATE_192000) +#define SNDRV_PCM_FMTBIT_S8 (1 << SNDRV_PCM_FORMAT_S8) +#define SNDRV_PCM_FMTBIT_U8 (1 << SNDRV_PCM_FORMAT_U8) +#define SNDRV_PCM_FMTBIT_S16_LE (1 << SNDRV_PCM_FORMAT_S16_LE) +#define SNDRV_PCM_FMTBIT_S16_BE (1 << SNDRV_PCM_FORMAT_S16_BE) +#define SNDRV_PCM_FMTBIT_U16_LE (1 << SNDRV_PCM_FORMAT_U16_LE) +#define SNDRV_PCM_FMTBIT_U16_BE (1 << SNDRV_PCM_FORMAT_U16_BE) +#define SNDRV_PCM_FMTBIT_S24_LE (1 << SNDRV_PCM_FORMAT_S24_LE) +#define SNDRV_PCM_FMTBIT_S24_BE (1 << SNDRV_PCM_FORMAT_S24_BE) +#define SNDRV_PCM_FMTBIT_U24_LE (1 << SNDRV_PCM_FORMAT_U24_LE) +#define SNDRV_PCM_FMTBIT_U24_BE (1 << SNDRV_PCM_FORMAT_U24_BE) +#define SNDRV_PCM_FMTBIT_S32_LE (1 << SNDRV_PCM_FORMAT_S32_LE) +#define SNDRV_PCM_FMTBIT_S32_BE (1 << SNDRV_PCM_FORMAT_S32_BE) +#define SNDRV_PCM_FMTBIT_U32_LE (1 << SNDRV_PCM_FORMAT_U32_LE) +#define SNDRV_PCM_FMTBIT_U32_BE (1 << SNDRV_PCM_FORMAT_U32_BE) +#define SNDRV_PCM_FMTBIT_FLOAT_LE (1 << SNDRV_PCM_FORMAT_FLOAT_LE) +#define SNDRV_PCM_FMTBIT_FLOAT_BE (1 << SNDRV_PCM_FORMAT_FLOAT_BE) +#define SNDRV_PCM_FMTBIT_FLOAT64_LE (1 << SNDRV_PCM_FORMAT_FLOAT64_LE) +#define SNDRV_PCM_FMTBIT_FLOAT64_BE (1 << SNDRV_PCM_FORMAT_FLOAT64_BE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1 << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1 << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE) +#define SNDRV_PCM_FMTBIT_MU_LAW (1 << SNDRV_PCM_FORMAT_MU_LAW) +#define SNDRV_PCM_FMTBIT_A_LAW (1 << SNDRV_PCM_FORMAT_A_LAW) +#define SNDRV_PCM_FMTBIT_IMA_ADPCM (1 << SNDRV_PCM_FORMAT_IMA_ADPCM) +#define SNDRV_PCM_FMTBIT_MPEG (1 << SNDRV_PCM_FORMAT_MPEG) +#define SNDRV_PCM_FMTBIT_GSM (1 << SNDRV_PCM_FORMAT_GSM) +#define SNDRV_PCM_FMTBIT_SPECIAL (1 << SNDRV_PCM_FORMAT_SPECIAL) + +#ifdef SNDRV_LITTLE_ENDIAN +#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE +#define SNDRV_PCM_FMTBIT_U16 SNDRV_PCM_FMTBIT_U16_LE +#define SNDRV_PCM_FMTBIT_S24 SNDRV_PCM_FMTBIT_S24_LE +#define SNDRV_PCM_FMTBIT_U24 SNDRV_PCM_FMTBIT_U24_LE +#define SNDRV_PCM_FMTBIT_S32 SNDRV_PCM_FMTBIT_S32_LE +#define SNDRV_PCM_FMTBIT_U32 SNDRV_PCM_FMTBIT_U32_LE +#define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_LE +#define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_LE +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE +#endif +#ifdef SNDRV_BIG_ENDIAN +#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_BE +#define SNDRV_PCM_FMTBIT_U16 SNDRV_PCM_FMTBIT_U16_BE +#define SNDRV_PCM_FMTBIT_S24 SNDRV_PCM_FMTBIT_S24_BE +#define SNDRV_PCM_FMTBIT_U24 SNDRV_PCM_FMTBIT_U24_BE +#define SNDRV_PCM_FMTBIT_S32 SNDRV_PCM_FMTBIT_S32_BE +#define SNDRV_PCM_FMTBIT_U32 SNDRV_PCM_FMTBIT_U32_BE +#define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_BE +#define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_BE +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE +#endif + +struct _snd_pcm_file { + snd_pcm_substream_t * substream; + struct _snd_pcm_file * next; +}; + +typedef struct _snd_pcm_hw_rule snd_pcm_hw_rule_t; + +typedef int (*snd_pcm_hw_rule_func_t)(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule); + +struct _snd_pcm_hw_rule { + unsigned int cond; + snd_pcm_hw_rule_func_t func; + int var; + int deps[4]; + void *private; +}; + +typedef struct _snd_pcm_hw_constraints { + unsigned int masks[SNDRV_PCM_HW_PARAM_LAST_MASK - + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + snd_interval_t intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + unsigned int rules_num; + unsigned int rules_all; + snd_pcm_hw_rule_t *rules; +} snd_pcm_hw_constraints_t; + +static inline unsigned int *constrs_mask(snd_pcm_hw_constraints_t *constrs, + snd_pcm_hw_param_t var) +{ + return &constrs->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; +} + +static inline snd_interval_t *constrs_interval(snd_pcm_hw_constraints_t *constrs, + snd_pcm_hw_param_t var) +{ + return &constrs->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; +} + +typedef struct { + unsigned int num; + unsigned int den_min, den_max, den_step; +} ratnum_t; + +typedef struct { + unsigned int num_min, num_max, num_step; + unsigned int den; +} ratden_t; + +typedef struct { + int nrats; + ratnum_t *rats; +} snd_pcm_hw_constraint_ratnums_t; + +typedef struct { + int nrats; + ratden_t *rats; +} snd_pcm_hw_constraint_ratdens_t; + +typedef struct { + unsigned int count; + unsigned int *list; + unsigned int mask; +} snd_pcm_hw_constraint_list_t; + +struct _snd_pcm_runtime { + /* -- Status -- */ + snd_pcm_substream_t *trigger_master; + snd_timestamp_t trigger_tstamp; /* trigger timestamp */ + int overrange; + snd_pcm_uframes_t avail_max; + snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ + snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/ + + /* -- HW params -- */ + snd_pcm_access_t access; /* access mode */ + snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */ + snd_pcm_subformat_t subformat; /* subformat */ + unsigned int rate; /* rate in Hz */ + unsigned int channels; /* channels */ + snd_pcm_uframes_t period_size; /* period size */ + unsigned int periods; /* periods */ + snd_pcm_uframes_t buffer_size; /* buffer size */ + unsigned int tick_time; /* tick time */ + snd_pcm_uframes_t min_align; /* Min alignment for the format */ + size_t byte_align; + unsigned int frame_bits; + unsigned int sample_bits; + unsigned int info; + unsigned int rate_num; + unsigned int rate_den; + + /* -- SW params -- */ + snd_pcm_tstamp_t tstamp_mode; /* mmap timestamp is updated */ + unsigned int period_step; + unsigned int sleep_min; /* min ticks to sleep */ + snd_pcm_uframes_t xfer_align; /* xfer size need to be a multiple */ + snd_pcm_uframes_t start_threshold; + snd_pcm_uframes_t stop_threshold; + snd_pcm_uframes_t silence_threshold; /* Silence filling happens when + noise is nearest than this */ + snd_pcm_uframes_t silence_size; /* Silence filling size */ + snd_pcm_uframes_t boundary; /* pointers wrap point */ + + snd_pcm_uframes_t silenced_start; + snd_pcm_uframes_t silenced_size; + + snd_pcm_sync_id_t sync; /* hardware synchronization ID */ + + /* -- mmap -- */ + volatile snd_pcm_mmap_status_t *status; + volatile snd_pcm_mmap_control_t *control; + atomic_t mmap_count; + + /* -- locking / scheduling -- */ + spinlock_t lock; + wait_queue_head_t sleep; + struct timer_list tick_timer; + struct fasync_struct *fasync; + + /* -- private section -- */ + void *private_data; + void (*private_free)(snd_pcm_runtime_t *runtime); + + /* -- hardware description -- */ + snd_pcm_hardware_t hw; + snd_pcm_hw_constraints_t hw_constraints; + + /* -- interrupt callbacks -- */ + void (*transfer_ack_begin)(snd_pcm_substream_t *substream); + void (*transfer_ack_end)(snd_pcm_substream_t *substream); + + /* -- timer -- */ + unsigned int timer_resolution; /* timer resolution */ + + /* -- DMA -- */ + unsigned char *dma_area; /* DMA area */ + dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ + unsigned long dma_bytes; /* size of DMA area */ + +#ifdef CONFIG_SND_OSSEMUL + /* -- OSS things -- */ + snd_pcm_oss_runtime_t oss; +#endif +}; + +struct _snd_pcm_substream { + snd_pcm_t *pcm; + snd_pcm_str_t *pstr; + int number; + char name[32]; /* substream name */ + int stream; /* stream (direction) */ + size_t buffer_bytes_max; /* limit ring buffer size */ + int dma_type; + void *dma_area; + dma_addr_t dma_addr; + size_t dma_bytes; + size_t dma_max; + void *dma_private; + /* -- hardware operations -- */ + snd_pcm_ops_t *ops; + /* -- runtime information -- */ + snd_pcm_runtime_t *runtime; + /* -- timer section -- */ + snd_timer_t *timer; /* timer */ + int timer_running; /* time is running */ + spinlock_t timer_lock; + /* -- next substream -- */ + snd_pcm_substream_t *next; + /* -- linked substreams -- */ + snd_pcm_substream_t *link_next; + snd_pcm_substream_t *link_prev; + snd_pcm_file_t *file; + struct file *ffile; +#ifdef CONFIG_SND_OSSEMUL + /* -- OSS things -- */ + snd_pcm_oss_substream_t oss; +#endif + snd_info_entry_t *proc_root; + snd_info_entry_t *proc_info_entry; + snd_info_entry_t *proc_hw_params_entry; + snd_info_entry_t *proc_sw_params_entry; + snd_info_entry_t *proc_status_entry; + snd_info_entry_t *proc_prealloc_entry; +}; + +#ifdef CONFIG_SND_OSSEMUL +#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL || ((substream)->oss.file != NULL)) +#else +#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL) +#endif + + +struct _snd_pcm_str { + int stream; /* stream (direction) */ + snd_pcm_t *pcm; + /* -- substreams -- */ + unsigned int substream_count; + unsigned int substream_opened; + snd_pcm_substream_t *substream; +#ifdef CONFIG_SND_OSSEMUL + /* -- OSS things -- */ + snd_pcm_oss_stream_t oss; +#endif + snd_pcm_file_t *files; + snd_minor_t *reg; + snd_info_entry_t *proc_root; + snd_info_entry_t *proc_info_entry; +}; + +struct _snd_pcm { + snd_card_t *card; + unsigned int device; /* device number */ + unsigned int info_flags; + unsigned short dev_class; + unsigned short dev_subclass; + char id[64]; + char name[80]; + snd_pcm_str_t streams[2]; + struct semaphore open_mutex; + wait_queue_head_t open_wait; + void *private_data; + void (*private_free) (snd_pcm_t *pcm); +#ifdef CONFIG_SND_OSSEMUL + snd_pcm_oss_t oss; +#endif +}; + +typedef struct _snd_pcm_notify { + int (*n_register) (unsigned short minor, snd_pcm_t * pcm); + int (*n_unregister) (unsigned short minor, snd_pcm_t * pcm); + struct list_head list; +} snd_pcm_notify_t; + +/* + * Registering + */ + +extern snd_pcm_t *snd_pcm_devices[]; +extern snd_minor_t snd_pcm_reg[2]; + +void snd_pcm_lock(int unlock); + +int snd_pcm_new(snd_card_t * card, char *id, int device, + int playback_count, int capture_count, + snd_pcm_t **rpcm); + +int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree); + +/* + * Native I/O + */ + +int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info); +int snd_pcm_info_user(snd_pcm_substream_t * substream, snd_pcm_info_t *info); +int snd_pcm_status(snd_pcm_substream_t * substream, snd_pcm_status_t *status); +int snd_pcm_prepare(snd_pcm_substream_t *substream); +int snd_pcm_start(snd_pcm_substream_t *substream); +int snd_pcm_stop(snd_pcm_substream_t *substream, int status); +#ifdef CONFIG_PM +int snd_pcm_suspend(snd_pcm_substream_t *substream); +int snd_pcm_suspend_all(snd_pcm_t *pcm); +#endif +int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); +int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); +int snd_pcm_kernel_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); +int snd_pcm_open(struct inode *inode, struct file *file); +int snd_pcm_release(struct inode *inode, struct file *file); +unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait); +unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait); +int snd_pcm_open_substream(snd_pcm_t *pcm, int stream, snd_pcm_substream_t **rsubstream); +void snd_pcm_release_substream(snd_pcm_substream_t *substream); +void snd_pcm_vma_notify_data(void *client, void *data); +int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file, struct vm_area_struct *area); + +#if BITS_PER_LONG >= 64 + +static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) +{ + *rem = *n % div; + *n /= div; +} + +#elif defined(i386) + +static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) +{ + u_int32_t low, high; + low = *n & 0xffffffff; + high = *n >> 32; + if (high) { + u_int32_t high1 = high % div; + high /= div; + asm("divl %2":"=a" (low), "=d" (*rem):"rm" (div), "a" (low), "d" (high1)); + *n = (u_int64_t)high << 32 | low; + } else { + *n = low / div; + *rem = low % div; + } +} +#else + +static inline void divl(u_int32_t high, u_int32_t low, + u_int32_t div, + u_int32_t *q, u_int32_t *r) +{ + u_int64_t n = (u_int64_t)high << 32 | low; + u_int64_t d = (u_int64_t)div << 31; + u_int32_t q1 = 0; + int c = 32; + while (n > 0xffffffffU) { + q1 <<= 1; + if (n > d) { + n -= d; + q1 |= 1; + } + d >>= 1; + c--; + } + q1 <<= c; + if (n) { + low = n; + *q = q1 | (low / div); + *r = low % div; + } else { + *r = 0; + *q = q1; + } + return; +} + +static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) +{ + u_int32_t low, high; + low = *n & 0xffffffff; + high = *n >> 32; + if (high) { + u_int32_t high1 = high % div; + u_int32_t low1 = low; + high /= div; + divl(high1, low1, div, &low, rem); + *n = (u_int64_t)high << 32 | low; + } else { + *n = low / div; + *rem = low % div; + } +} +#endif + +/* + * PCM library + */ + +static inline int snd_pcm_running(snd_pcm_substream_t *substream) +{ + return (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING || + (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK)); +} + +static inline ssize_t bytes_to_samples(snd_pcm_runtime_t *runtime, ssize_t size) +{ + return size * 8 / runtime->sample_bits; +} + +static inline snd_pcm_sframes_t bytes_to_frames(snd_pcm_runtime_t *runtime, ssize_t size) +{ + return size * 8 / runtime->frame_bits; +} + +static inline ssize_t samples_to_bytes(snd_pcm_runtime_t *runtime, ssize_t size) +{ + return size * runtime->sample_bits / 8; +} + +static inline ssize_t frames_to_bytes(snd_pcm_runtime_t *runtime, snd_pcm_sframes_t size) +{ + return size * runtime->frame_bits / 8; +} + +static inline int frame_aligned(snd_pcm_runtime_t *runtime, ssize_t bytes) +{ + return bytes % runtime->byte_align == 0; +} + +static inline size_t snd_pcm_lib_buffer_bytes(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return frames_to_bytes(runtime, runtime->buffer_size); +} + +static inline size_t snd_pcm_lib_period_bytes(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return frames_to_bytes(runtime, runtime->period_size); +} + +/* + * result is: 0 ... (boundary - 1) + */ +static inline snd_pcm_uframes_t snd_pcm_playback_avail(snd_pcm_runtime_t *runtime) +{ + snd_pcm_sframes_t avail = runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr; + if (avail < 0) + avail += runtime->boundary; + else if ((snd_pcm_uframes_t) avail >= runtime->boundary) + avail -= runtime->boundary; + return avail; +} + +/* + * result is: 0 ... (boundary - 1) + */ +static inline snd_pcm_uframes_t snd_pcm_capture_avail(snd_pcm_runtime_t *runtime) +{ + snd_pcm_sframes_t avail = runtime->status->hw_ptr - runtime->control->appl_ptr; + if (avail < 0) + avail += runtime->boundary; + return avail; +} + +static inline snd_pcm_sframes_t snd_pcm_playback_hw_avail(snd_pcm_runtime_t *runtime) +{ + return runtime->buffer_size - snd_pcm_playback_avail(runtime); +} + +static inline snd_pcm_sframes_t snd_pcm_capture_hw_avail(snd_pcm_runtime_t *runtime) +{ + return runtime->buffer_size - snd_pcm_capture_avail(runtime); +} + +static inline void snd_pcm_trigger_done(snd_pcm_substream_t *substream, + snd_pcm_substream_t *master) +{ + substream->runtime->trigger_master = master; +} + +static inline int hw_is_mask(int var) +{ + return var >= SNDRV_PCM_HW_PARAM_FIRST_MASK && + var <= SNDRV_PCM_HW_PARAM_LAST_MASK; +} + +static inline int hw_is_interval(int var) +{ + return var >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL && + var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; +} + +typedef unsigned int snd_mask_t; +#define SND_MASK_MAX 32 + +static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return (snd_mask_t*)¶ms->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; +} + +static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; +} + +static inline const snd_mask_t *hw_param_mask_c(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return (const snd_mask_t *)hw_param_mask((snd_pcm_hw_params_t*) params, var); +} + +static inline const snd_interval_t *hw_param_interval_c(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return (const snd_interval_t *)hw_param_interval((snd_pcm_hw_params_t*) params, var); +} + +#define params_access(p) (ffs(*hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)) - 1) +#define params_format(p) (ffs(*hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)) - 1) +#define params_subformat(p) (ffs(*hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT)) - 1) +#define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min +#define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min +#define params_period_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min +#define params_period_bytes(p) ((params_period_size(p)*snd_pcm_format_physical_width(params_format(p))*params_channels(p))/8) +#define params_periods(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIODS)->min +#define params_buffer_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min +#define params_buffer_bytes(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min +#define params_tick_time(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_TICK_TIME)->min + + +int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v); +void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c); +void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c); +void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, + unsigned int k, snd_interval_t *c); +void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, + const snd_interval_t *b, snd_interval_t *c); +int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask); +int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step); +int snd_interval_ratnum(snd_interval_t *i, + unsigned int rats_count, ratnum_t *rats, + unsigned int *nump, unsigned int *denp); +int snd_interval_ratden(snd_interval_t *i, + unsigned int rats_count, ratden_t *rats, + unsigned int *nump, unsigned int *denp); + +void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params); +void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var); +int snd_pcm_hw_param_min(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int *dir); +int snd_pcm_hw_param_max(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int *dir); +int snd_pcm_hw_param_setinteger(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var); +int snd_pcm_hw_param_first(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +int snd_pcm_hw_param_last(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +int snd_pcm_hw_param_near(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int *dir); +int snd_pcm_hw_params_choose(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params); + +int snd_pcm_hw_refine(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params); + +int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream); +int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream); + +int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + unsigned int mask); +int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + unsigned int min, unsigned int max); +int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var); +int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_list_t *l); +int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratnums_t *r); +int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratdens_t *r); +int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, + unsigned int cond, + unsigned int width, + unsigned int msbits); +int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + unsigned long step); +int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, + unsigned int cond, + int var, + snd_pcm_hw_rule_func_t func, void *private, + int dep, ...); + +int snd_pcm_format_signed(snd_pcm_format_t format); +int snd_pcm_format_unsigned(snd_pcm_format_t format); +int snd_pcm_format_linear(snd_pcm_format_t format); +int snd_pcm_format_little_endian(snd_pcm_format_t format); +int snd_pcm_format_big_endian(snd_pcm_format_t format); +int snd_pcm_format_width(snd_pcm_format_t format); /* in bits */ +int snd_pcm_format_physical_width(snd_pcm_format_t format); /* in bits */ +u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format); +int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames); +snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian); +ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples); + +void snd_pcm_set_ops(snd_pcm_t * pcm, int direction, snd_pcm_ops_t *ops); +void snd_pcm_set_sync(snd_pcm_substream_t * substream); +int snd_pcm_lib_interleave_len(snd_pcm_substream_t *substream); +int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg); +int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream); +int snd_pcm_playback_xrun_check(snd_pcm_substream_t *substream); +int snd_pcm_capture_xrun_check(snd_pcm_substream_t *substream); +int snd_pcm_playback_xrun_asap(snd_pcm_substream_t *substream); +int snd_pcm_capture_xrun_asap(snd_pcm_substream_t *substream); +void snd_pcm_playback_silence(snd_pcm_substream_t *substream); +int snd_pcm_playback_ready(snd_pcm_substream_t *substream); +int snd_pcm_capture_ready(snd_pcm_substream_t *substream); +long snd_pcm_playback_ready_jiffies(snd_pcm_substream_t *substream); +long snd_pcm_capture_ready_jiffies(snd_pcm_substream_t *substream); +int snd_pcm_playback_data(snd_pcm_substream_t *substream); +int snd_pcm_playback_empty(snd_pcm_substream_t *substream); +int snd_pcm_capture_empty(snd_pcm_substream_t *substream); +void snd_pcm_tick_prepare(snd_pcm_substream_t *substream); +void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks); +void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream); +void snd_pcm_period_elapsed(snd_pcm_substream_t *substream); +snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream, + const void *buf, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream, + void *buf, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream, + void **bufs, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, + void **bufs, snd_pcm_uframes_t frames); + +/* + * Timer interface + */ + +void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream); +void snd_pcm_timer_init(snd_pcm_substream_t * substream); +void snd_pcm_timer_done(snd_pcm_substream_t * substream); + +/* + * Memory + */ + +int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream); +int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm); +int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, + size_t size, size_t max, + unsigned int flags); +int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max, + unsigned int flags); +int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size); +int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream); + +#ifdef CONFIG_ISA +int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, + size_t size, size_t max); +int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max); +#endif +#ifdef CONFIG_PCI +int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, + snd_pcm_substream_t *substream, + size_t size, size_t max); +int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, + snd_pcm_t *pcm, + size_t size, + size_t max); +#endif + +static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) +{ + *max = dma < 4 ? 64 * 1024 : 128 * 1024; +} + +/* + * Misc + */ + +#define SNDRV_PCM_DEFAULT_CON_SPDIF (IEC958_AES0_CON_EMPHASIS_NONE|\ + (IEC958_AES1_CON_ORIGINAL<<8)|\ + (IEC958_AES1_CON_PCM_CODER<<8)|\ + (IEC958_AES3_CON_FS_48000<<24)) + +#endif /* __SOUND_PCM_H */ diff -Nru a/include/sound/pcm_oss.h b/include/sound/pcm_oss.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/pcm_oss.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,82 @@ +#ifndef __SOUND_PCM_OSS_H +#define __SOUND_PCM_OSS_H + +/* + * Digital Audio (PCM) - OSS compatibility abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +typedef struct _snd_pcm_plugin snd_pcm_plugin_t; +typedef struct _snd_pcm_oss_setup snd_pcm_oss_setup_t; + +struct _snd_pcm_oss_setup { + char *task_name; + unsigned int disable:1, + direct:1, + block:1, + nonblock:1; + unsigned int periods; + unsigned int period_size; + snd_pcm_oss_setup_t *next; +}; + +typedef struct _snd_pcm_oss_runtime { + int params: 1, /* format/parameter change */ + prepare: 1, /* need to prepare the operation */ + trigger: 1, /* trigger flag */ + sync_trigger: 1; /* sync trigger flag */ + int rate; /* requested rate */ + int format; /* requested OSS format */ + unsigned int channels; /* requested channels */ + unsigned int fragshift; + unsigned int maxfrags; + unsigned int subdivision; /* requested subdivision */ + size_t period_bytes; /* requested period size */ + unsigned int periods; + size_t buffer_bytes; /* requested period size */ + size_t bytes; /* total # bytes processed */ + size_t mmap_bytes; + char *buffer; /* vmallocated period */ + size_t buffer_used; /* used length from buffer */ + snd_pcm_plugin_t *plugin_first; + snd_pcm_plugin_t *plugin_last; + unsigned int prev_hw_ptr_interrupt; +} snd_pcm_oss_runtime_t; + +typedef struct _snd_pcm_oss_file { + snd_pcm_substream_t *streams[2]; +} snd_pcm_oss_file_t; + +typedef struct _snd_pcm_oss_substream { + int oss: 1; /* oss mode */ + snd_pcm_oss_setup_t *setup; /* active setup */ + snd_pcm_oss_file_t *file; +} snd_pcm_oss_substream_t; + +typedef struct _snd_pcm_oss_stream { + snd_pcm_oss_setup_t *setup_list; /* setup list */ + struct semaphore setup_mutex; + snd_info_entry_t *proc_entry; +} snd_pcm_oss_stream_t; + +typedef struct _snd_pcm_oss { + int reg; +} snd_pcm_oss_t; + +#endif /* __SOUND_PCM_OSS_H */ diff -Nru a/include/sound/pcm_params.h b/include/sound/pcm_params.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/pcm_params.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,336 @@ +#ifndef __SOUND_PCM_PARAMS_H +#define __SOUND_PCM_PARAMS_H + +/* + * PCM params helpers + * Copyright (c) by Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +extern int snd_pcm_hw_param_mask(snd_pcm_substream_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, const snd_mask_t *val); +extern unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +extern unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +extern int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir); +extern int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var); +extern int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir); + +/* To share the same code we have alsa-lib */ +#define snd_mask_bits(mask) (*(mask)) +#define INLINE static inline +#define assert(a) + +INLINE unsigned int ld2(u_int32_t v) +{ + unsigned r = 0; + + if (v >= 0x10000) { + v >>= 16; + r += 16; + } + if (v >= 0x100) { + v >>= 8; + r += 8; + } + if (v >= 0x10) { + v >>= 4; + r += 4; + } + if (v >= 4) { + v >>= 2; + r += 2; + } + if (v >= 2) + r++; + return r; +} + +INLINE size_t snd_mask_sizeof(void) +{ + return sizeof(snd_mask_t); +} + +INLINE void snd_mask_none(snd_mask_t *mask) +{ + snd_mask_bits(mask) = 0; +} + +INLINE void snd_mask_any(snd_mask_t *mask) +{ + snd_mask_bits(mask) = ~0U; +} + +INLINE void snd_mask_load(snd_mask_t *mask, unsigned int msk) +{ + snd_mask_bits(mask) = msk; +} + +INLINE int snd_mask_empty(const snd_mask_t *mask) +{ + return snd_mask_bits(mask) == 0; +} + +INLINE unsigned int snd_mask_min(const snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + return ffs(snd_mask_bits(mask)) - 1; +} + +INLINE unsigned int snd_mask_max(const snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + return ld2(snd_mask_bits(mask)); +} + +INLINE void snd_mask_set(snd_mask_t *mask, unsigned int val) +{ + assert(val <= SND_MASK_MAX); + snd_mask_bits(mask) |= (1U << val); +} + +INLINE void snd_mask_reset(snd_mask_t *mask, unsigned int val) +{ + assert(val <= SND_MASK_MAX); + snd_mask_bits(mask) &= ~(1U << val); +} + +INLINE void snd_mask_set_range(snd_mask_t *mask, unsigned int from, unsigned int to) +{ + assert(to <= SND_MASK_MAX && from <= to); + snd_mask_bits(mask) |= ((1U << (from - to + 1)) - 1) << from; +} + +INLINE void snd_mask_reset_range(snd_mask_t *mask, unsigned int from, unsigned int to) +{ + assert(to <= SND_MASK_MAX && from <= to); + snd_mask_bits(mask) &= ~(((1U << (from - to + 1)) - 1) << from); +} + +INLINE void snd_mask_leave(snd_mask_t *mask, unsigned int val) +{ + assert(val <= SND_MASK_MAX); + snd_mask_bits(mask) &= 1U << val; +} + +INLINE void snd_mask_intersect(snd_mask_t *mask, const snd_mask_t *v) +{ + snd_mask_bits(mask) &= snd_mask_bits(v); +} + +INLINE int snd_mask_eq(const snd_mask_t *mask, const snd_mask_t *v) +{ + return snd_mask_bits(mask) == snd_mask_bits(v); +} + +INLINE void snd_mask_copy(snd_mask_t *mask, const snd_mask_t *v) +{ + snd_mask_bits(mask) = snd_mask_bits(v); +} + +INLINE int snd_mask_test(const snd_mask_t *mask, unsigned int val) +{ + assert(val <= SND_MASK_MAX); + return snd_mask_bits(mask) & (1U << val); +} + +INLINE int snd_mask_single(const snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + return !(snd_mask_bits(mask) & (snd_mask_bits(mask) - 1)); +} + +INLINE int snd_mask_refine(snd_mask_t *mask, const snd_mask_t *v) +{ + snd_mask_t old; + assert(!snd_mask_empty(mask)); + snd_mask_copy(&old, mask); + snd_mask_intersect(mask, v); + if (snd_mask_empty(mask)) + return -EINVAL; + return !snd_mask_eq(mask, &old); +} + +INLINE int snd_mask_refine_first(snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_single(mask)) + return 0; + snd_mask_leave(mask, snd_mask_min(mask)); + return 1; +} + +INLINE int snd_mask_refine_last(snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_single(mask)) + return 0; + snd_mask_leave(mask, snd_mask_max(mask)); + return 1; +} + +INLINE int snd_mask_refine_min(snd_mask_t *mask, unsigned int val) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_min(mask) >= val) + return 0; + snd_mask_reset_range(mask, 0, val - 1); + if (snd_mask_empty(mask)) + return -EINVAL; + return 1; +} + +INLINE int snd_mask_refine_max(snd_mask_t *mask, unsigned int val) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_max(mask) <= val) + return 0; + snd_mask_reset_range(mask, val + 1, SND_MASK_MAX); + if (snd_mask_empty(mask)) + return -EINVAL; + return 1; +} + +INLINE int snd_mask_refine_set(snd_mask_t *mask, unsigned int val) +{ + int changed; + assert(!snd_mask_empty(mask)); + changed = !snd_mask_single(mask); + snd_mask_leave(mask, val); + if (snd_mask_empty(mask)) + return -EINVAL; + return changed; +} + +INLINE int snd_mask_value(const snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + return snd_mask_min(mask); +} + +INLINE void snd_interval_any(snd_interval_t *i) +{ + i->min = 0; + i->openmin = 0; + i->max = UINT_MAX; + i->openmax = 0; + i->integer = 0; + i->empty = 0; +} + +INLINE void snd_interval_none(snd_interval_t *i) +{ + i->empty = 1; +} + +INLINE int snd_interval_checkempty(const snd_interval_t *i) +{ + return (i->min > i->max || + (i->min == i->max && (i->openmin || i->openmax))); +} + +INLINE int snd_interval_empty(const snd_interval_t *i) +{ + return i->empty; +} + +INLINE int snd_interval_single(const snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + return (i->min == i->max || + (i->min + 1 == i->max && i->openmax)); +} + +INLINE int snd_interval_value(const snd_interval_t *i) +{ + assert(snd_interval_single(i)); + return i->min; +} + +INLINE int snd_interval_min(const snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + return i->min; +} + +INLINE int snd_interval_max(const snd_interval_t *i) +{ + unsigned int v; + assert(!snd_interval_empty(i)); + v = i->max; + if (i->openmax) + v--; + return v; +} + +INLINE int snd_interval_test(const snd_interval_t *i, unsigned int val) +{ + return !((i->min > val || (i->min == val && i->openmin) || + i->max < val || (i->max == val && i->openmax))); +} + +INLINE void snd_interval_copy(snd_interval_t *d, const snd_interval_t *s) +{ + *d = *s; +} + +INLINE int snd_interval_setinteger(snd_interval_t *i) +{ + if (i->integer) + return 0; + if (i->openmin && i->openmax && i->min == i->max) + return -EINVAL; + i->integer = 1; + return 1; +} + +INLINE int snd_interval_eq(const snd_interval_t *i1, const snd_interval_t *i2) +{ + if (i1->empty) + return i2->empty; + if (i2->empty) + return i1->empty; + return i1->min == i2->min && i1->openmin == i2->openmin && + i1->max == i2->max && i1->openmax == i2->openmax; +} + +static inline unsigned int add(unsigned int a, unsigned int b) +{ + if (a >= UINT_MAX - b) + return UINT_MAX; + return a + b; +} + +static inline unsigned int sub(unsigned int a, unsigned int b) +{ + if (a > b) + return a - b; + return 0; +} + +#undef INLINE +#undef assert + +#endif /* __SOUND_PCM_PARAMS_H */ + diff -Nru a/include/sound/rawmidi.h b/include/sound/rawmidi.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/rawmidi.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,181 @@ +#ifndef __SOUND_RAWMIDI_H +#define __SOUND_RAWMIDI_H + +/* + * Abstract layer for MIDI v1.0 stream + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#include "seq_device.h" +#endif + +/* + * Raw MIDI interface + */ + +typedef enum sndrv_rawmidi_stream snd_rawmidi_stream_t; +typedef struct sndrv_rawmidi_info snd_rawmidi_info_t; +typedef struct sndrv_rawmidi_params snd_rawmidi_params_t; +typedef struct sndrv_rawmidi_status snd_rawmidi_status_t; + +#define SNDRV_RAWMIDI_DEVICES 4 + +#define SNDRV_RAWMIDI_LFLG_OUTPUT (1<<0) +#define SNDRV_RAWMIDI_LFLG_INPUT (1<<1) +#define SNDRV_RAWMIDI_LFLG_OPEN (3<<0) +#define SNDRV_RAWMIDI_LFLG_APPEND (1<<2) + +typedef struct _snd_rawmidi_runtime snd_rawmidi_runtime_t; +typedef struct _snd_rawmidi_substream snd_rawmidi_substream_t; +typedef struct _snd_rawmidi_str snd_rawmidi_str_t; + +typedef struct _snd_rawmidi_ops { + int (*open) (snd_rawmidi_substream_t * substream); + int (*close) (snd_rawmidi_substream_t * substream); + void (*trigger) (snd_rawmidi_substream_t * substream, int up); + void (*drain) (snd_rawmidi_substream_t * substream); +} snd_rawmidi_ops_t; + +typedef struct _snd_rawmidi_global_ops { + int (*dev_register) (snd_rawmidi_t * rmidi); + int (*dev_unregister) (snd_rawmidi_t * rmidi); +} snd_rawmidi_global_ops_t; + +struct _snd_rawmidi_runtime { + unsigned int trigger: 1, /* transfer is running */ + drain: 1, /* drain stage */ + oss: 1; /* OSS compatible mode */ + /* midi stream buffer */ + unsigned char *buffer; /* buffer for MIDI data */ + size_t buffer_size; /* size of buffer */ + size_t appl_ptr; /* application pointer */ + size_t hw_ptr; /* hardware pointer */ + size_t avail_min; /* min avail for wakeup */ + size_t avail; /* max used buffer for wakeup */ + size_t xruns; /* over/underruns counter */ + /* misc */ + spinlock_t lock; + wait_queue_head_t sleep; + /* event handler (room [output] or new bytes [input]) */ + void (*event)(snd_rawmidi_substream_t *substream); + /* private data */ + void *private_data; + void (*private_free)(snd_rawmidi_substream_t *substream); +}; + +struct _snd_rawmidi_substream { + struct list_head list; /* list of all substream for given stream */ + int stream; /* direction */ + int number; /* substream number */ + unsigned int opened: 1, /* open flag */ + append: 1, /* append flag (merge more streams) */ + active_sensing: 1; /* send active sensing when close */ + int use_count; /* use counter (for output) */ + size_t bytes; + snd_rawmidi_t *rmidi; + snd_rawmidi_str_t *pstr; + char name[32]; + snd_rawmidi_runtime_t *runtime; + /* hardware layer */ + snd_rawmidi_ops_t *ops; +}; + +typedef struct _snd_rawmidi_file { + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *input; + snd_rawmidi_substream_t *output; +} snd_rawmidi_file_t; + +struct _snd_rawmidi_str { + unsigned int substream_count; + unsigned int substream_opened; + struct list_head substreams; +}; + +struct _snd_rawmidi { + snd_card_t *card; + + unsigned int device; /* device number */ + unsigned int info_flags; /* SNDRV_RAWMIDI_INFO_XXXX */ + char id[64]; + char name[80]; + +#ifdef CONFIG_SND_OSSEMUL + int ossreg; +#endif + + snd_rawmidi_global_ops_t *ops; + + snd_rawmidi_str_t streams[2]; + + void *private_data; + void (*private_free) (snd_rawmidi_t *rmidi); + + struct semaphore open_mutex; + wait_queue_head_t open_wait; + + snd_info_entry_t *dev; + snd_info_entry_t *proc_entry; + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + snd_seq_device_t *seq_dev; +#endif +}; + +/* main rawmidi functions */ + +int snd_rawmidi_new(snd_card_t * card, char *id, int device, + int output_count, int input_count, + snd_rawmidi_t ** rmidi); +void snd_rawmidi_set_ops(snd_rawmidi_t * rmidi, int stream, snd_rawmidi_ops_t * ops); + +/* control functions */ + +int snd_rawmidi_control_ioctl(snd_card_t * card, + snd_ctl_file_t * control, + unsigned int cmd, + unsigned long arg); + +/* callbacks */ + +void snd_rawmidi_receive_reset(snd_rawmidi_substream_t * substream); +int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count); +void snd_rawmidi_transmit_reset(snd_rawmidi_substream_t * substream); +int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream); +int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count); +int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count); +int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count); + +/* main midi functions */ + +int snd_rawmidi_info_select(snd_card_t *card, snd_rawmidi_info_t *info); +int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, int mode, snd_rawmidi_file_t * rfile); +int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile); +int snd_rawmidi_output_params(snd_rawmidi_substream_t * substream, snd_rawmidi_params_t * params); +int snd_rawmidi_input_params(snd_rawmidi_substream_t * substream, snd_rawmidi_params_t * params); +int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream); +int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream); +int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream); +long snd_rawmidi_kernel_read(snd_rawmidi_substream_t * substream, unsigned char *buf, long count); +long snd_rawmidi_kernel_write(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count); + +#endif /* __SOUND_RAWMIDI_H */ diff -Nru a/include/sound/sb.h b/include/sound/sb.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/sb.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,289 @@ +#ifndef __SOUND_SB_H +#define __SOUND_SB_H + +/* + * Header file for SoundBlaster cards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include + +enum sb_hw_type { + SB_HW_AUTO, + SB_HW_10, + SB_HW_20, + SB_HW_201, + SB_HW_PRO, + SB_HW_16, + SB_HW_16CSP, /* SB16 with CSP chip */ + SB_HW_ALS100, /* Avance Logic ALS100 chip */ + SB_HW_ALS4000, /* Avance Logic ALS4000 chip */ +}; + +#define SB_OPEN_PCM 0x01 +#define SB_OPEN_MIDI_INPUT 0x02 +#define SB_OPEN_MIDI_OUTPUT 0x04 +#define SB_OPEN_MIDI_TRIGGER 0x08 + +#define SB_MODE_HALT 0x00 +#define SB_MODE_PLAYBACK_8 0x01 +#define SB_MODE_PLAYBACK_16 0x02 +#define SB_MODE_PLAYBACK (SB_MODE_PLAYBACK_8 | SB_MODE_PLAYBACK_16) +#define SB_MODE_CAPTURE_8 0x04 +#define SB_MODE_CAPTURE_16 0x08 +#define SB_MODE_CAPTURE (SB_MODE_CAPTURE_8 | SB_MODE_CAPTURE_16) + +#define SB_RATE_LOCK_PLAYBACK 0x10 +#define SB_RATE_LOCK_CAPTURE 0x20 +#define SB_RATE_LOCK (SB_RATE_LOCK_PLAYBACK | SB_RATE_LOCK_CAPTURE) + +#define SB_MPU_INPUT 1 + +struct _snd_sb { + unsigned long port; /* base port of DSP chip */ + struct resource *res_port; + unsigned long alt_port; /* alternate port (ALS4000) */ + struct resource *res_alt_port; + unsigned long mpu_port; /* MPU port for SB DSP 4.0+ */ + int irq; /* IRQ number of DSP chip */ + int dma8; /* 8-bit DMA */ + int dma16; /* 16-bit DMA */ + unsigned short version; /* version of DSP chip */ + enum sb_hw_type hardware; /* see to SB_HW_XXXX */ + + struct pci_dev *pci; /* ALS4000 */ + + unsigned int open; /* see to SB_OPEN_XXXX for sb8 */ + /* also SNDRV_SB_CSP_MODE_XXX for sb16_csp */ + unsigned int mode; /* current mode of stream */ + unsigned int force_mode16; /* force 16-bit mode of streams */ + unsigned int locked_rate; /* sb16 duplex */ + unsigned int playback_format; + unsigned int capture_format; + struct timer_list midi_timer; + unsigned int p_dma_size; + unsigned int p_period_size; + unsigned int c_dma_size; + unsigned int c_period_size; + + spinlock_t mixer_lock; + + char name[32]; + + void *csp; /* used only when CONFIG_SND_SB16_CSP is set */ + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_substream_input; + snd_rawmidi_substream_t *midi_substream_output; + + spinlock_t reg_lock; + spinlock_t open_lock; + spinlock_t midi_input_lock; + + snd_info_entry_t *proc_entry; +}; + +typedef struct _snd_sb sb_t; + +/* I/O ports */ + +#define SBP(chip, x) ((chip)->port + s_b_SB_##x) +#define SBP1(port, x) ((port) + s_b_SB_##x) + +#define s_b_SB_RESET 0x6 +#define s_b_SB_READ 0xa +#define s_b_SB_WRITE 0xc +#define s_b_SB_COMMAND 0xc +#define s_b_SB_STATUS 0xc +#define s_b_SB_DATA_AVAIL 0xe +#define s_b_SB_DATA_AVAIL_16 0xf +#define s_b_SB_MIXER_ADDR 0x4 +#define s_b_SB_MIXER_DATA 0x5 +#define s_b_SB_OPL3_LEFT 0x0 +#define s_b_SB_OPL3_RIGHT 0x2 +#define s_b_SB_OPL3_BOTH 0x8 + +#define SB_DSP_OUTPUT 0x14 +#define SB_DSP_INPUT 0x24 +#define SB_DSP_BLOCK_SIZE 0x48 +#define SB_DSP_HI_OUTPUT 0x91 +#define SB_DSP_HI_INPUT 0x99 +#define SB_DSP_LO_OUTPUT_AUTO 0x1c +#define SB_DSP_LO_INPUT_AUTO 0x2c +#define SB_DSP_HI_OUTPUT_AUTO 0x90 +#define SB_DSP_HI_INPUT_AUTO 0x98 +#define SB_DSP_IMMED_INT 0xf2 +#define SB_DSP_GET_VERSION 0xe1 +#define SB_DSP_SPEAKER_ON 0xd1 +#define SB_DSP_SPEAKER_OFF 0xd3 +#define SB_DSP_DMA8_OFF 0xd0 +#define SB_DSP_DMA8_ON 0xd4 +#define SB_DSP_DMA8_EXIT 0xda +#define SB_DSP_DMA16_OFF 0xd5 +#define SB_DSP_DMA16_ON 0xd6 +#define SB_DSP_DMA16_EXIT 0xd9 +#define SB_DSP_SAMPLE_RATE 0x40 +#define SB_DSP_SAMPLE_RATE_OUT 0x41 +#define SB_DSP_SAMPLE_RATE_IN 0x42 +#define SB_DSP_MONO_8BIT 0xa0 +#define SB_DSP_MONO_16BIT 0xa4 +#define SB_DSP_STEREO_8BIT 0xa8 +#define SB_DSP_STEREO_16BIT 0xac + +#define SB_DSP_MIDI_INPUT_IRQ 0x31 +#define SB_DSP_MIDI_OUTPUT 0x38 + +#define SB_DSP4_OUT8_AI 0xc6 +#define SB_DSP4_IN8_AI 0xce +#define SB_DSP4_OUT16_AI 0xb6 +#define SB_DSP4_IN16_AI 0xbe +#define SB_DSP4_MODE_UNS_MONO 0x00 +#define SB_DSP4_MODE_SIGN_MONO 0x10 +#define SB_DSP4_MODE_UNS_STEREO 0x20 +#define SB_DSP4_MODE_SIGN_STEREO 0x30 + +#define SB_DSP4_OUTPUT 0x3c +#define SB_DSP4_INPUT_LEFT 0x3d +#define SB_DSP4_INPUT_RIGHT 0x3e + +/* registers for SB 2.0 mixer */ +#define SB_DSP20_MASTER_DEV 0x02 +#define SB_DSP20_PCM_DEV 0x0A +#define SB_DSP20_CD_DEV 0x08 +#define SB_DSP20_FM_DEV 0x06 + +/* registers for SB PRO mixer */ +#define SB_DSP_MASTER_DEV 0x22 +#define SB_DSP_PCM_DEV 0x04 +#define SB_DSP_LINE_DEV 0x2e +#define SB_DSP_CD_DEV 0x28 +#define SB_DSP_FM_DEV 0x26 +#define SB_DSP_MIC_DEV 0x0a +#define SB_DSP_CAPTURE_SOURCE 0x0c +#define SB_DSP_CAPTURE_FILT 0x0c +#define SB_DSP_PLAYBACK_FILT 0x0e +#define SB_DSP_STEREO_SW 0x0e + +#define SB_DSP_MIXS_MIC0 0x00 /* same as MIC */ +#define SB_DSP_MIXS_CD 0x01 +#define SB_DSP_MIXS_MIC 0x02 +#define SB_DSP_MIXS_LINE 0x03 + +/* registers (only for left channel) for SB 16 mixer */ +#define SB_DSP4_MASTER_DEV 0x30 +#define SB_DSP4_BASS_DEV 0x46 +#define SB_DSP4_TREBLE_DEV 0x44 +#define SB_DSP4_SYNTH_DEV 0x34 +#define SB_DSP4_PCM_DEV 0x32 +#define SB_DSP4_SPEAKER_DEV 0x3b +#define SB_DSP4_LINE_DEV 0x38 +#define SB_DSP4_MIC_DEV 0x3a +#define SB_DSP4_OUTPUT_SW 0x3c +#define SB_DSP4_CD_DEV 0x36 +#define SB_DSP4_IGAIN_DEV 0x3f +#define SB_DSP4_OGAIN_DEV 0x41 +#define SB_DSP4_MIC_AGC 0x43 + +/* additional registers for SB 16 mixer */ +#define SB_DSP4_IRQSETUP 0x80 +#define SB_DSP4_DMASETUP 0x81 +#define SB_DSP4_IRQSTATUS 0x82 +#define SB_DSP4_MPUSETUP 0x84 + +#define SB_DSP4_3DSE 0x90 + +/* IRQ setting bitmap */ +#define SB_IRQSETUP_IRQ9 0x01 +#define SB_IRQSETUP_IRQ5 0x02 +#define SB_IRQSETUP_IRQ7 0x04 +#define SB_IRQSETUP_IRQ10 0x08 + +/* IRQ types */ +#define SB_IRQTYPE_8BIT 0x01 +#define SB_IRQTYPE_16BIT 0x02 +#define SB_IRQTYPE_MPUIN 0x04 + +/* DMA setting bitmap */ +#define SB_DMASETUP_DMA0 0x01 +#define SB_DMASETUP_DMA1 0x02 +#define SB_DMASETUP_DMA3 0x08 +#define SB_DMASETUP_DMA5 0x20 +#define SB_DMASETUP_DMA6 0x40 +#define SB_DMASETUP_DMA7 0x80 + +/* + * + */ + +static inline void snd_sb_ack_8bit(sb_t *chip) +{ + inb(SBP(chip, DATA_AVAIL)); +} + +static inline void snd_sb_ack_16bit(sb_t *chip) +{ + inb(SBP(chip, DATA_AVAIL_16)); +} + +/* sb_common.c */ +int snd_sbdsp_command(sb_t *chip, unsigned char val); +int snd_sbdsp_get_byte(sb_t *chip); +int snd_sbdsp_reset(sb_t *chip); +int snd_sbdsp_create(snd_card_t *card, + unsigned long port, + int irq, + void (*irq_handler)(int, void *, struct pt_regs *), + int dma8, int dma16, + unsigned short hardware, + sb_t **r_chip); +/* sb_mixer.c */ +void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data); +unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg); +int snd_sbmixer_new(sb_t *chip); + +/* sb8_init.c */ +int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm); +/* sb8.c */ +void snd_sb8dsp_interrupt(sb_t *chip); +int snd_sb8_playback_open(snd_pcm_substream_t *substream); +int snd_sb8_capture_open(snd_pcm_substream_t *substream); +int snd_sb8_playback_close(snd_pcm_substream_t *substream); +int snd_sb8_capture_close(snd_pcm_substream_t *substream); +/* midi8.c */ +void snd_sb8dsp_midi_interrupt(sb_t *chip); +int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi); + +/* sb16_init.c */ +int snd_sb16dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm); +int snd_sb16dsp_configure(sb_t *chip); +/* sb16.c */ +void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs); +int snd_sb16_playback_open(snd_pcm_substream_t *substream); +int snd_sb16_capture_open(snd_pcm_substream_t *substream); +int snd_sb16_playback_close(snd_pcm_substream_t *substream); +int snd_sb16_capture_close(snd_pcm_substream_t *substream); + +#endif /* __SOUND_SB_H */ diff -Nru a/include/sound/sb16_csp.h b/include/sound/sb16_csp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/sb16_csp.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,168 @@ +#ifndef __SOUND_SB16_CSP_H +#define __SOUND_SB16_CSP_H + +/* + * Copyright (c) 1999 by Uros Bizjak + * Takashi Iwai + * + * SB16ASP/AWE32 CSP control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* CSP modes */ +#define SNDRV_SB_CSP_MODE_NONE 0x00 +#define SNDRV_SB_CSP_MODE_DSP_READ 0x01 /* Record from DSP */ +#define SNDRV_SB_CSP_MODE_DSP_WRITE 0x02 /* Play to DSP */ +#define SNDRV_SB_CSP_MODE_QSOUND 0x04 /* QSound */ + +/* CSP load flags */ +#define SNDRV_SB_CSP_LOAD_FROMUSER 0x01 +#define SNDRV_SB_CSP_LOAD_INITBLOCK 0x02 + +/* CSP sample width */ +#define SNDRV_SB_CSP_SAMPLE_8BIT 0x01 +#define SNDRV_SB_CSP_SAMPLE_16BIT 0x02 + +/* CSP channels */ +#define SNDRV_SB_CSP_MONO 0x01 +#define SNDRV_SB_CSP_STEREO 0x02 + +/* CSP rates */ +#define SNDRV_SB_CSP_RATE_8000 0x01 +#define SNDRV_SB_CSP_RATE_11025 0x02 +#define SNDRV_SB_CSP_RATE_22050 0x04 +#define SNDRV_SB_CSP_RATE_44100 0x08 +#define SNDRV_SB_CSP_RATE_ALL 0x0f + +/* CSP running state */ +#define SNDRV_SB_CSP_ST_IDLE 0x00 +#define SNDRV_SB_CSP_ST_LOADED 0x01 +#define SNDRV_SB_CSP_ST_RUNNING 0x02 +#define SNDRV_SB_CSP_ST_PAUSED 0x04 +#define SNDRV_SB_CSP_ST_AUTO 0x08 +#define SNDRV_SB_CSP_ST_QSOUND 0x10 + +/* maximum QSound value (180 degrees right) */ +#define SNDRV_SB_CSP_QSOUND_MAX_RIGHT 0x20 + +/* maximum microcode RIFF file size */ +#define SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE 0x3000 + +/* microcode header */ +typedef struct snd_sb_csp_mc_header { + char codec_name[16]; /* id name of codec */ + unsigned short func_req; /* requested function */ +} snd_sb_csp_mc_header_t; + +/* microcode to be loaded */ +typedef struct snd_sb_csp_microcode { + snd_sb_csp_mc_header_t info; + unsigned char data[SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE]; +} snd_sb_csp_microcode_t; + +/* start CSP with sample_width in mono/stereo */ +typedef struct snd_sb_csp_start { + int sample_width; /* sample width, look above */ + int channels; /* channels, look above */ +} snd_sb_csp_start_t; + +/* CSP information */ +typedef struct snd_sb_csp_info { + char codec_name[16]; /* id name of codec */ + unsigned short func_nr; /* function number */ + unsigned int acc_format; /* accepted PCM formats */ + unsigned short acc_channels; /* accepted channels */ + unsigned short acc_width; /* accepted sample width */ + unsigned short acc_rates; /* accepted sample rates */ + unsigned short csp_mode; /* CSP mode, see above */ + unsigned short run_channels; /* current channels */ + unsigned short run_width; /* current sample width */ + unsigned short version; /* version id: 0x10 - 0x1f */ + unsigned short state; /* state bits */ +} snd_sb_csp_info_t; + +/* HWDEP controls */ +/* get CSP information */ +#define SNDRV_SB_CSP_IOCTL_INFO _IOR('H', 0x10, snd_sb_csp_info_t) +/* load microcode to CSP */ +#define SNDRV_SB_CSP_IOCTL_LOAD_CODE _IOW('H', 0x11, snd_sb_csp_microcode_t) +/* unload microcode from CSP */ +#define SNDRV_SB_CSP_IOCTL_UNLOAD_CODE _IO('H', 0x12) +/* start CSP */ +#define SNDRV_SB_CSP_IOCTL_START _IOW('H', 0x13, snd_sb_csp_start_t) +/* stop CSP */ +#define SNDRV_SB_CSP_IOCTL_STOP _IO('H', 0x14) +/* pause CSP and DMA transfer */ +#define SNDRV_SB_CSP_IOCTL_PAUSE _IO('H', 0x15) +/* restart CSP and DMA transfer */ +#define SNDRV_SB_CSP_IOCTL_RESTART _IO('H', 0x16) + +#ifdef __KERNEL__ +#include "sb.h" +#include "hwdep.h" + +typedef struct snd_sb_csp snd_sb_csp_t; + +/* + * CSP operators + */ +typedef struct { + int (*csp_use) (snd_sb_csp_t * p); + int (*csp_unuse) (snd_sb_csp_t * p); + int (*csp_autoload) (snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode); + int (*csp_start) (snd_sb_csp_t * p, int sample_width, int channels); + int (*csp_stop) (snd_sb_csp_t * p); + int (*csp_qsound_transfer) (snd_sb_csp_t * p); +} snd_sb_csp_ops_t; + +/* + * CSP private data + */ +struct snd_sb_csp { + sb_t *chip; /* SB16 DSP */ + int used; /* usage flag - exclusive */ + char codec_name[16]; /* name of codec */ + unsigned short func_nr; /* function number */ + unsigned int acc_format; /* accepted PCM formats */ + int acc_channels; /* accepted channels */ + int acc_width; /* accepted sample width */ + int acc_rates; /* accepted sample rates */ + int mode; /* MODE */ + int run_channels; /* current CSP channels */ + int run_width; /* current sample width */ + int version; /* CSP version (0x10 - 0x1f) */ + int running; /* running state */ + + snd_sb_csp_ops_t ops; /* operators */ + + spinlock_t q_lock; /* locking */ + int q_enabled; /* enabled flag */ + int qpos_left; /* left position */ + int qpos_right; /* right position */ + int qpos_changed; /* position changed flag */ + + snd_kcontrol_t *qsound_switch; + snd_kcontrol_t *qsound_space; + + struct semaphore access_mutex; /* locking */ + snd_info_entry_t *proc; /* proc interface */ +}; + +int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep); +#endif + +#endif /* __SOUND_SB16_CSP */ diff -Nru a/include/sound/seq_device.h b/include/sound/seq_device.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/seq_device.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,88 @@ +#ifndef __SOUND_SEQ_DEVICE_H +#define __SOUND_SEQ_DEVICE_H + +/* + * ALSA sequencer device management + * Copyright (c) 1999 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +typedef struct snd_seq_device snd_seq_device_t; +typedef struct snd_seq_dev_ops snd_seq_dev_ops_t; + +/* + * registered device information + */ + +#define ID_LEN 32 + +/* status flag */ +#define SNDRV_SEQ_DEVICE_FREE 0 +#define SNDRV_SEQ_DEVICE_REGISTERED 1 + +struct snd_seq_device { + /* device info */ + snd_card_t *card; /* sound card */ + int device; /* device number */ + char id[ID_LEN]; /* driver id */ + char name[80]; /* device name */ + int argsize; /* size of the argument */ + void *driver_data; /* private data for driver */ + int status; /* flag - read only */ + void *private_data; /* private data for the caller */ + void (*private_free)(snd_seq_device_t *device); + struct list_head list; /* link to next device */ +}; + + +/* driver operators + * init_device: + * Initialize the device with given parameters. + * Typically, + * 1. call snd_hwdep_new + * 2. allocate private data and initialize it + * 3. call snd_hwdep_register + * 4. store the instance to dev->driver_data pointer. + * + * free_device: + * Release the private data. + * Typically, call snd_device_free(dev->card, dev->driver_data) + */ +struct snd_seq_dev_ops { + int (*init_device)(snd_seq_device_t *dev); + int (*free_device)(snd_seq_device_t *dev); +}; + +/* + * prototypes + */ +void snd_seq_device_load_drivers(void); +int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize, snd_seq_device_t **result); +int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize); +int snd_seq_device_unregister_driver(char *id); + +#define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(snd_seq_device_t)) + + +/* + * id strings for generic devices + */ +#define SNDRV_SEQ_DEV_ID_MIDISYNTH "seq-midi" +#define SNDRV_SEQ_DEV_ID_OPL3 "opl3-synth" + + +#endif /* __SOUND_SEQ_DEVICE_H */ diff -Nru a/include/sound/seq_instr.h b/include/sound/seq_instr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/seq_instr.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,112 @@ +#ifndef __SOUND_SEQ_INSTR_H +#define __SOUND_SEQ_INSTR_H + +/* + * Main kernel header file for the ALSA sequencer + * Copyright (c) 1999 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "seq_kernel.h" + +/* Instrument cluster */ +typedef struct _snd_seq_kcluster { + snd_seq_instr_cluster_t cluster; + char name[32]; + int priority; + struct _snd_seq_kcluster *next; +} snd_seq_kcluster_t; + +/* return pointer to private data */ +#define KINSTR_DATA(kinstr) (void *)(((char *)kinstr) + sizeof(snd_seq_kinstr_t)) + +typedef struct snd_seq_kinstr_ops snd_seq_kinstr_ops_t; + +/* Instrument structure */ +typedef struct _snd_seq_kinstr { + snd_seq_instr_t instr; + char name[32]; + int type; /* instrument type */ + int use; /* use count */ + int busy; /* not useable */ + int add_len; /* additional length */ + snd_seq_kinstr_ops_t *ops; /* operations */ + struct _snd_seq_kinstr *next; +} snd_seq_kinstr_t; + +#define SNDRV_SEQ_INSTR_HASH_SIZE 32 + +/* Instrument flags */ +#define SNDRV_SEQ_INSTR_FLG_DIRECT (1<<0) /* accept only direct events */ + +/* List of all instruments */ +typedef struct { + snd_seq_kinstr_t *hash[SNDRV_SEQ_INSTR_HASH_SIZE]; + int count; /* count of all instruments */ + + snd_seq_kcluster_t *chash[SNDRV_SEQ_INSTR_HASH_SIZE]; + int ccount; /* count of all clusters */ + + int owner; /* current owner of the instrument list */ + unsigned int flags; + + spinlock_t lock; + spinlock_t ops_lock; + struct semaphore ops_mutex; + unsigned long ops_flags; +} snd_seq_kinstr_list_t; + +#define SNDRV_SEQ_INSTR_NOTIFY_REMOVE 0 +#define SNDRV_SEQ_INSTR_NOTIFY_CHANGE 1 + +struct snd_seq_kinstr_ops { + void *private_data; + long add_len; /* additional length */ + char *instr_type; + int (*info)(void *private_data, char *info_data, long len); + int (*put)(void *private_data, snd_seq_kinstr_t *kinstr, + char *instr_data, long len, int atomic, int cmd); + int (*get)(void *private_data, snd_seq_kinstr_t *kinstr, + char *instr_data, long len, int atomic, int cmd); + int (*get_size)(void *private_data, snd_seq_kinstr_t *kinstr, long *size); + int (*remove)(void *private_data, snd_seq_kinstr_t *kinstr, int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *kinstr, int what); + struct snd_seq_kinstr_ops *next; +}; + + +/* instrument operations */ +snd_seq_kinstr_list_t *snd_seq_instr_list_new(void); +void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list); +int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list, + snd_seq_instr_header_t *ifree, + int client, + int atomic); +snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list, + snd_seq_instr_t *instr, + int exact, + int follow_alias); +void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list, + snd_seq_kinstr_t *instr); +int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int client, + int atomic, + int hop); + +#endif /* __SOUND_SEQ_INSTR_H */ diff -Nru a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/seq_kernel.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,182 @@ +#ifndef __SOUND_SEQ_KERNEL_H +#define __SOUND_SEQ_KERNEL_H + +/* + * Main kernel header file for the ALSA sequencer + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include "asequencer.h" + +typedef sndrv_seq_tick_time_t snd_seq_tick_time_t; +typedef sndrv_seq_position_t snd_seq_position_t; +typedef sndrv_seq_frequency_t snd_seq_frequency_t; +typedef sndrv_seq_instr_cluster_t snd_seq_instr_cluster_t; +typedef enum sndrv_seq_client_type snd_seq_client_type_t; +typedef enum sndrv_seq_stop_mode snd_seq_stop_mode_t; +typedef struct sndrv_seq_port_info snd_seq_port_info_t; +typedef struct sndrv_seq_port_subscribe snd_seq_port_subscribe_t; +typedef struct sndrv_seq_event snd_seq_event_t; +typedef struct sndrv_seq_addr snd_seq_addr_t; +typedef struct sndrv_seq_ev_volume snd_seq_ev_volume_t; +typedef struct sndrv_seq_ev_loop snd_seq_ev_loop_t; +typedef struct sndrv_seq_remove_events snd_seq_remove_events_t; +typedef struct sndrv_seq_query_subs snd_seq_query_subs_t; +typedef struct sndrv_seq_real_time snd_seq_real_time_t; +typedef struct sndrv_seq_system_info snd_seq_system_info_t; +typedef struct sndrv_seq_client_info snd_seq_client_info_t; +typedef struct sndrv_seq_queue_info snd_seq_queue_info_t; +typedef struct sndrv_seq_queue_status snd_seq_queue_status_t; +typedef struct sndrv_seq_queue_tempo snd_seq_queue_tempo_t; +typedef struct sndrv_seq_queue_owner snd_seq_queue_owner_t; +typedef struct sndrv_seq_queue_timer snd_seq_queue_timer_t; +typedef struct sndrv_seq_queue_client snd_seq_queue_client_t; +typedef struct sndrv_seq_client_pool snd_seq_client_pool_t; +typedef struct sndrv_seq_instr snd_seq_instr_t; +typedef struct sndrv_seq_instr_data snd_seq_instr_data_t; +typedef struct sndrv_seq_instr_header snd_seq_instr_header_t; +typedef union sndrv_seq_timestamp snd_seq_timestamp_t; + +#define snd_seq_event_bounce_ext_data sndrv_seq_event_bounce_ext_data +#define snd_seq_ev_is_result_type sndrv_seq_ev_is_result_type +#define snd_seq_ev_is_channel_type sndrv_seq_ev_is_channel_type +#define snd_seq_ev_is_note_type sndrv_seq_ev_is_note_type +#define snd_seq_ev_is_control_type sndrv_seq_ev_is_control_type +#define snd_seq_ev_is_queue_type sndrv_seq_ev_is_queue_type +#define snd_seq_ev_is_message_type sndrv_seq_ev_is_message_type +#define snd_seq_ev_is_sample_type sndrv_seq_ev_is_sample_type +#define snd_seq_ev_is_user_type sndrv_seq_ev_is_user_type +#define snd_seq_ev_is_fixed_type sndrv_seq_ev_is_fixed_type +#define snd_seq_ev_is_instr_type sndrv_seq_ev_is_instr_type +#define snd_seq_ev_is_variable_type sndrv_seq_ev_is_variable_type +#define snd_seq_ev_is_varipc_type sndrv_seq_ev_is_varipc_type +#define snd_seq_ev_is_reserved sndrv_seq_ev_is_reserved +#define snd_seq_ev_is_direct sndrv_seq_ev_is_direct +#define snd_seq_ev_is_prior sndrv_seq_ev_is_prior +#define snd_seq_ev_length_type sndrv_seq_ev_length_type +#define snd_seq_ev_is_fixed sndrv_seq_ev_is_fixed +#define snd_seq_ev_is_variable sndrv_seq_ev_is_variable +#define snd_seq_ev_is_varusr sndrv_seq_ev_is_varusr +#define snd_seq_ev_is_varipc sndrv_seq_ev_is_varipc +#define snd_seq_ev_timestamp_type sndrv_seq_ev_timestamp_type +#define snd_seq_ev_is_tick sndrv_seq_ev_is_tick +#define snd_seq_ev_is_real sndrv_seq_ev_is_real +#define snd_seq_ev_timemode_type sndrv_seq_ev_timemode_type +#define snd_seq_ev_is_abstime sndrv_seq_ev_is_abstime +#define snd_seq_ev_is_reltime sndrv_seq_ev_is_reltime +#define snd_seq_queue_sync_port sndrv_seq_queue_sync_port +#define snd_seq_queue_owner sndrv_seq_queue_owner + +/* maximum number of events dequeued per schedule interval */ +#define SNDRV_SEQ_MAX_DEQUEUE 50 + +/* maximum number of queues */ +#define SNDRV_SEQ_MAX_QUEUES 8 + +/* max number of concurrent clients */ +#define SNDRV_SEQ_MAX_CLIENTS 192 + +/* max number of concurrent ports */ +#define SNDRV_SEQ_MAX_PORTS 254 + +/* max number of events in memory pool */ +#define SNDRV_SEQ_MAX_EVENTS 2000 + +/* default number of events in memory chunk */ +#define SNDRV_SEQ_DEFAULT_CHUNK_EVENTS 64 + +/* default number of events in memory pool */ +#define SNDRV_SEQ_DEFAULT_EVENTS 500 + +/* max number of events in memory pool for one client (outqueue) */ +#define SNDRV_SEQ_MAX_CLIENT_EVENTS 2000 + +/* default number of events in memory pool for one client (outqueue) */ +#define SNDRV_SEQ_DEFAULT_CLIENT_EVENTS 200 + +/* max delivery path length */ +#define SNDRV_SEQ_MAX_HOPS 10 + +/* max size of event size */ +#define SNDRV_SEQ_MAX_EVENT_LEN 0x3fffffff + +/* typedefs */ +struct _snd_seq_user_client; +struct _snd_seq_kernel_client; +struct _snd_seq_client; +struct _snd_seq_queue; + +typedef struct _snd_seq_user_client user_client_t; +typedef struct _snd_seq_kernel_client kernel_client_t; +typedef struct _snd_seq_client client_t; +typedef struct _snd_seq_queue queue_t; + +/* call-backs for kernel client */ + +typedef struct { + void *private_data; + int allow_input: 1, + allow_output: 1; + /*...*/ +} snd_seq_client_callback_t; + +/* call-backs for kernel port */ +typedef int (snd_seq_kernel_port_open_t)(void *private_data, snd_seq_port_subscribe_t *info); +typedef int (snd_seq_kernel_port_close_t)(void *private_data, snd_seq_port_subscribe_t *info); +typedef int (snd_seq_kernel_port_input_t)(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop); +typedef void (snd_seq_kernel_port_private_free_t)(void *private_data); + +typedef struct { + struct module *owner; + void *private_data; + snd_seq_kernel_port_open_t *subscribe; + snd_seq_kernel_port_close_t *unsubscribe; + snd_seq_kernel_port_open_t *use; + snd_seq_kernel_port_close_t *unuse; + snd_seq_kernel_port_input_t *event_input; + snd_seq_kernel_port_private_free_t *private_free; + unsigned int callback_all; /* call subscribe callbacks at each connection/disconnection */ + /*...*/ +} snd_seq_port_callback_t; + +/* interface for kernel client */ +extern int snd_seq_create_kernel_client(snd_card_t *card, int client_index, snd_seq_client_callback_t *callback); +extern int snd_seq_delete_kernel_client(int client); +extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop); +extern int snd_seq_kernel_client_dispatch(int client, snd_seq_event_t *ev, int atomic, int hop); +extern int snd_seq_kernel_client_ctl(int client, unsigned int cmd, void *arg); + +#define SNDRV_SEQ_EXT_MASK 0xc0000000 +#define SNDRV_SEQ_EXT_USRPTR 0x80000000 +#define SNDRV_SEQ_EXT_CHAINED 0x40000000 + +typedef int (*snd_seq_dump_func_t)(void *ptr, void *buf, int count); +int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned); +int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data); + +/* port callback routines */ +void snd_port_init_callback(snd_seq_port_callback_t *p); +snd_seq_port_callback_t *snd_port_alloc_callback(void); + +/* port attach/detach */ +int snd_seq_event_port_attach(int client, snd_seq_port_callback_t *pcbp, + int cap, int type, char *portname); +int snd_seq_event_port_detach(int client, int port); + +#endif /* __SOUND_SEQ_KERNEL_H */ diff -Nru a/include/sound/seq_midi_emul.h b/include/sound/seq_midi_emul.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/seq_midi_emul.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,197 @@ +#ifndef __SOUND_SEQ_MIDI_EMUL_H +#define __SOUND_SEQ_MIDI_EMUL_H + +/* + * Midi channel definition for optional channel management. + * + * Copyright (C) 1999 Steve Ratcliffe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "seq_kernel.h" + +/* + * This structure is used to keep track of the current state on each + * channel. All drivers for hardware that does not understand midi + * directly will probably need to use this structure. + */ +typedef struct snd_midi_channel { + void *private; /* A back pointer to driver data */ + int number; /* The channel number */ + int client; /* The client associated with this channel */ + int port; /* The port associated with this channel */ + + unsigned char midi_mode; /* GM, GS, XG etc */ + unsigned int + drum_channel:1, /* Drum channel */ + param_type:1 /* RPN/NRPN */ + ; + + unsigned char midi_aftertouch; /* Aftertouch (key pressure) */ + unsigned char midi_pressure; /* Channel pressure */ + unsigned char midi_program; /* Instrument number */ + short midi_pitchbend; /* Pitch bend amount */ + + unsigned char control[128]; /* Current value of all controls */ + unsigned char note[128]; /* Current status for all notes */ + + short gm_rpn_pitch_bend_range; /* Pitch bend range */ + short gm_rpn_fine_tuning; /* Master fine tuning */ + short gm_rpn_coarse_tuning; /* Master coarse tuning */ + +} snd_midi_channel_t; + +/* + * A structure that represets a set of channels bound to a port. There + * would usually be 16 channels per port. But fewer could be used for + * particular cases. + * The channel set consists of information describing the client and + * port for this midi synth and an array of snd_midi_channel_t structures. + * A driver that had no need for snd_midi_channel_t could still use the + * channel set type if it wished with the channel array null. + */ +typedef struct snd_midi_channel_set { + void *private_data; /* Driver data */ + int client; /* Client for this port */ + int port; /* The port number */ + + int max_channels; /* Size of the channels array */ + snd_midi_channel_t *channels; + + unsigned char midi_mode; /* MIDI operating mode */ + unsigned char gs_master_volume; /* SYSEX master volume: 0-127 */ + unsigned char gs_chorus_mode; + unsigned char gs_reverb_mode; + +} snd_midi_channel_set_t; + +typedef struct snd_seq_midi_op { + void (*note_on)(void *private_data, int note, int vel, snd_midi_channel_t *chan); + void (*note_off)(void *private_data,int note, int vel, snd_midi_channel_t *chan); /* release note */ + void (*key_press)(void *private_data, int note, int vel, snd_midi_channel_t *chan); + void (*note_terminate)(void *private_data, int note, snd_midi_channel_t *chan); /* terminate note immediately */ + void (*control)(void *private_data, int type, snd_midi_channel_t *chan); + void (*nrpn)(void *private_data, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); + void (*sysex)(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +} snd_midi_op_t; + +/* + * These defines are used so that pitchbend, aftertouch etc, can be + * distinguished from controller values. + */ +/* 0-127 controller values */ +#define MIDI_CTL_PITCHBEND 0x80 +#define MIDI_CTL_AFTERTOUCH 0x81 +#define MIDI_CTL_CHAN_PRESSURE 0x82 + +/* + * These names exist to allow symbolic access to the controls array. + * The usage is eg: chan->gm_bank_select. Another implementation would + * be really have these members in the struct, and not the array. + */ +#define gm_bank_select control[0] +#define gm_modulation control[1] +#define gm_breath control[2] +#define gm_foot_pedal control[4] +#define gm_portamento_time control[5] +#define gm_data_entry control[6] +#define gm_volume control[7] +#define gm_balance control[8] +#define gm_pan control[10] +#define gm_expression control[11] +#define gm_effect_control1 control[12] +#define gm_effect_control2 control[13] +#define gm_slider1 control[16] +#define gm_slider2 control[17] +#define gm_slider3 control[18] +#define gm_slider4 control[19] + +#define gm_bank_select_lsb control[32] +#define gm_modulation_wheel_lsb control[33] +#define gm_breath_lsb control[34] +#define gm_foot_pedal_lsb control[36] +#define gm_portamento_time_lsb control[37] +#define gm_data_entry_lsb control[38] +#define gm_volume_lsb control[39] +#define gm_balance_lsb control[40] +#define gm_pan_lsb control[42] +#define gm_expression_lsb control[43] +#define gm_effect_control1_lsb control[44] +#define gm_effect_control2_lsb control[45] + +#define gm_sustain control[MIDI_CTL_SUSTAIN] +#define gm_hold gm_sustain +#define gm_portamento control[MIDI_CTL_PORTAMENTO] +#define gm_sustenuto control[MIDI_CTL_SUSTENUTO] + +/* + * These macros give the complete value of the controls that consist + * of coarse and fine pairs. Of course the fine controls are seldom used + * but there is no harm in being complete. + */ +#define SNDRV_GM_BANK_SELECT(cp) (((cp)->control[0]<<7)|((cp)->control[32])) +#define SNDRV_GM_MODULATION_WHEEL(cp) (((cp)->control[1]<<7)|((cp)->control[33])) +#define SNDRV_GM_BREATH(cp) (((cp)->control[2]<<7)|((cp)->control[34])) +#define SNDRV_GM_FOOT_PEDAL(cp) (((cp)->control[4]<<7)|((cp)->control[36])) +#define SNDRV_GM_PORTAMENTO_TIME(cp) (((cp)->control[5]<<7)|((cp)->control[37])) +#define SNDRV_GM_DATA_ENTRY(cp) (((cp)->control[6]<<7)|((cp)->control[38])) +#define SNDRV_GM_VOLUME(cp) (((cp)->control[7]<<7)|((cp)->control[39])) +#define SNDRV_GM_BALANCE(cp) (((cp)->control[8]<<7)|((cp)->control[40])) +#define SNDRV_GM_PAN(cp) (((cp)->control[10]<<7)|((cp)->control[42])) +#define SNDRV_GM_EXPRESSION(cp) (((cp)->control[11]<<7)|((cp)->control[43])) + + +/* MIDI mode */ +#define SNDRV_MIDI_MODE_NONE 0 /* Generic midi */ +#define SNDRV_MIDI_MODE_GM 1 +#define SNDRV_MIDI_MODE_GS 2 +#define SNDRV_MIDI_MODE_XG 3 +#define SNDRV_MIDI_MODE_MT32 4 + +/* MIDI note state */ +#define SNDRV_MIDI_NOTE_OFF 0x00 +#define SNDRV_MIDI_NOTE_ON 0x01 +#define SNDRV_MIDI_NOTE_RELEASED 0x02 +#define SNDRV_MIDI_NOTE_SUSTENUTO 0x04 + +#define SNDRV_MIDI_PARAM_TYPE_REGISTERED 0 +#define SNDRV_MIDI_PARAM_TYPE_NONREGISTERED 1 + +/* SYSEX parse flag */ +enum { + SNDRV_MIDI_SYSEX_NOT_PARSED = 0, + SNDRV_MIDI_SYSEX_GM_ON, + SNDRV_MIDI_SYSEX_GS_ON, + SNDRV_MIDI_SYSEX_GS_RESET, + SNDRV_MIDI_SYSEX_GS_CHORUS_MODE, + SNDRV_MIDI_SYSEX_GS_REVERB_MODE, + SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME, + SNDRV_MIDI_SYSEX_GS_PROGRAM, + SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL, + SNDRV_MIDI_SYSEX_XG_ON, +}; + +/* Prototypes for midi_process.c */ +void snd_midi_process_event(snd_midi_op_t *ops, snd_seq_event_t *ev, + snd_midi_channel_set_t *chanset); +void snd_midi_channel_set_clear(snd_midi_channel_set_t *chset); +void snd_midi_channel_init(snd_midi_channel_t *p, int n); +snd_midi_channel_t *snd_midi_channel_init_set(int n); +snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n); +void snd_midi_channel_free_set(snd_midi_channel_set_t *chset); + +#endif /* __SOUND_SEQ_MIDI_EMUL_H */ diff -Nru a/include/sound/seq_midi_event.h b/include/sound/seq_midi_event.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/seq_midi_event.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,54 @@ +#ifndef __SOUND_SEQ_MIDI_EVENT_H +#define __SOUND_SEQ_MIDI_EVENT_H + +/* + * MIDI byte <-> sequencer event coder + * + * Copyright (C) 1998,99 Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "asequencer.h" + +#define MAX_MIDI_EVENT_BUF 256 + +typedef struct snd_midi_event_t snd_midi_event_t; + +/* midi status */ +struct snd_midi_event_t { + int qlen; /* queue length */ + int read; /* chars read */ + int type; /* current event type */ + unsigned char lastcmd; + int bufsize; + unsigned char *buf; /* input buffer */ + spinlock_t lock; +}; + +int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev); +int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize); +void snd_midi_event_free(snd_midi_event_t *dev); +void snd_midi_event_init(snd_midi_event_t *dev); +void snd_midi_event_reset_encode(snd_midi_event_t *dev); +void snd_midi_event_reset_decode(snd_midi_event_t *dev); +/* encode from byte stream - return number of written bytes if success */ +long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev); +int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev); +/* decode from event to bytes - return number of written bytes if success */ +long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev); + +#endif /* __SOUND_SEQ_MIDI_EVENT_H */ diff -Nru a/include/sound/seq_oss.h b/include/sound/seq_oss.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/seq_oss.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,102 @@ +#ifndef __SOUND_SEQ_OSS_H +#define __SOUND_SEQ_OSS_H + +/* + * OSS compatible sequencer driver + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "asequencer.h" +#include "seq_kernel.h" + +/* + * type definitions + */ +typedef struct snd_seq_oss_arg_t snd_seq_oss_arg_t; +typedef struct snd_seq_oss_callback_t snd_seq_oss_callback_t; + +/* + * argument structure for synthesizer operations + */ +struct snd_seq_oss_arg_t { + /* given by OSS sequencer */ + int app_index; /* application unique index */ + int file_mode; /* file mode - see below */ + int seq_mode; /* sequencer mode - see below */ + + /* following must be initialized in open callback */ + snd_seq_addr_t addr; /* opened port address */ + void *private_data; /* private data for lowlevel drivers */ + + /* note-on event passing mode: initially given by OSS seq, + * but configurable by drivers - see below + */ + int event_passing; +}; + + +/* + * synthesizer operation callbacks + */ +struct snd_seq_oss_callback_t { + struct module *owner; + int (*open)(snd_seq_oss_arg_t *p, void *closure); + int (*close)(snd_seq_oss_arg_t *p); + int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg); + int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count); + int (*reset)(snd_seq_oss_arg_t *p); + int (*raw_event)(snd_seq_oss_arg_t *p, unsigned char *data); +}; + +/* flag: file_mode */ +#define SNDRV_SEQ_OSS_FILE_ACMODE 3 +#define SNDRV_SEQ_OSS_FILE_READ 1 +#define SNDRV_SEQ_OSS_FILE_WRITE 2 +#define SNDRV_SEQ_OSS_FILE_NONBLOCK 4 + +/* flag: seq_mode */ +#define SNDRV_SEQ_OSS_MODE_SYNTH 0 +#define SNDRV_SEQ_OSS_MODE_MUSIC 1 + +/* flag: event_passing */ +#define SNDRV_SEQ_OSS_PROCESS_EVENTS 0 /* key == 255 is processed as velocity change */ +#define SNDRV_SEQ_OSS_PASS_EVENTS 1 /* pass all events to callback */ +#define SNDRV_SEQ_OSS_PROCESS_KEYPRESS 2 /* key >= 128 will be processed as key-pressure */ + +/* default control rate: fixed */ +#define SNDRV_SEQ_OSS_CTRLRATE 100 + +/* default max queue length: configurable by module option */ +#define SNDRV_SEQ_OSS_MAX_QLEN 1024 + + +/* + * data pointer to snd_seq_register_device + */ +typedef struct snd_seq_oss_reg { + int type; + int subtype; + int nvoices; + snd_seq_oss_callback_t oper; + void *private_data; +} snd_seq_oss_reg_t; + +/* device id */ +#define SNDRV_SEQ_DEV_ID_OSS "seq-oss" + +#endif /* __SOUND_SEQ_OSS_H */ diff -Nru a/include/sound/seq_oss_legacy.h b/include/sound/seq_oss_legacy.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/seq_oss_legacy.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,31 @@ +#ifndef __SOUND_SEQ_OSS_LEGACY_H +#define __SOUND_SEQ_OSS_LEGACY_H + +/* + * OSS compatible macro definitions + * + * Copyright (C) 2000 Abramo Bagnara + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#ifndef SAMPLE_TYPE_AWE32 +#define SAMPLE_TYPE_AWE32 0x20 +#endif + +#endif /* __SOUND_SEQ_OSS_LEGACY_H */ + diff -Nru a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/seq_virmidi.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,82 @@ +#ifndef __SOUND_SEQ_VIRMIDI_H +#define __SOUND_SEQ_VIRMIDI_H + +/* + * Virtual Raw MIDI client on Sequencer + * Copyright (c) 2000 by Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "rawmidi.h" +#include "seq_midi_event.h" + +typedef struct _snd_virmidi_dev snd_virmidi_dev_t; + +/* + * device file instance: + * This instance is created at each time the midi device file is + * opened. Each instance has its own input buffer and MIDI parser + * (buffer), and is associated with the device instance. + */ +typedef struct _snd_virmidi { + struct list_head list; + int seq_mode; + int client; + int port; + int trigger: 1; + snd_midi_event_t *parser; + snd_seq_event_t event; + snd_virmidi_dev_t *rdev; + snd_rawmidi_substream_t *substream; +} snd_virmidi_t; + +#define SNDRV_VIRMIDI_SUBSCRIBE (1<<0) +#define SNDRV_VIRMIDI_USE (1<<1) + +/* + * device record: + * Each virtual midi device has one device instance. It contains + * common information and the linked-list of opened files, + */ +struct _snd_virmidi_dev { + snd_card_t *card; /* associated card */ + snd_rawmidi_t *rmidi; /* rawmidi device */ + int seq_mode; /* SNDRV_VIRMIDI_XXX */ + int device; /* sequencer device */ + int client; /* created/attached client */ + int port; /* created/attached port */ + unsigned int flags; /* SNDRV_VIRMIDI_* */ + rwlock_t filelist_lock; + struct list_head filelist; +}; + +/* sequencer mode: + * ATTACH = input/output events from midi device are routed to the + * attached sequencer port. sequencer port is not created + * by virmidi itself. + * DISPATCH = input/output events are routed to subscribers. + * sequencer port is created in virmidi. + */ +#define SNDRV_VIRMIDI_SEQ_NONE 0 +#define SNDRV_VIRMIDI_SEQ_ATTACH 1 +#define SNDRV_VIRMIDI_SEQ_DISPATCH 2 + +int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi); + +#endif /* __SOUND_SEQ_VIRMIDI */ + diff -Nru a/include/sound/sfnt_info.h b/include/sound/sfnt_info.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/sfnt_info.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,184 @@ +#ifndef __SOUND_SFNT_INFO_H +#define __SOUND_SFNT_INFO_H + +/* + * Patch record compatible with AWE driver on OSS + * + * Copyright (C) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "seq_oss_legacy.h" + +/* + * patch information record + */ + +/* patch interface header: 16 bytes */ +typedef struct soundfont_patch_info_t { + unsigned short key; /* use the key below */ +#define SNDRV_OSS_SOUNDFONT_PATCH _PATCHKEY(0x07) + + short device_no; /* synthesizer number */ + unsigned short sf_id; /* file id (should be zero) */ + short optarg; /* optional argument */ + int len; /* data length (without this header) */ + + short type; /* patch operation type */ +#define SNDRV_SFNT_LOAD_INFO 0 /* awe_voice_rec */ +#define SNDRV_SFNT_LOAD_DATA 1 /* awe_sample_info */ +#define SNDRV_SFNT_OPEN_PATCH 2 /* awe_open_parm */ +#define SNDRV_SFNT_CLOSE_PATCH 3 /* none */ + /* 4 is obsolete */ +#define SNDRV_SFNT_REPLACE_DATA 5 /* awe_sample_info (optarg=#channels)*/ +#define SNDRV_SFNT_MAP_PRESET 6 /* awe_voice_map */ + /* 7 is not used */ +#define SNDRV_SFNT_PROBE_DATA 8 /* optarg=sample */ +#define SNDRV_SFNT_REMOVE_INFO 9 /* optarg=(bank<<8)|instr */ + + short reserved; /* word alignment data */ + + /* the actual patch data begins after this */ +} soundfont_patch_info_t; + + +/* + * open patch + */ + +#define SNDRV_SFNT_PATCH_NAME_LEN 32 + +typedef struct soundfont_open_parm_t { + unsigned short type; /* sample type */ +#define SNDRV_SFNT_PAT_TYPE_MISC 0 +#define SNDRV_SFNT_PAT_TYPE_GUS 6 +#define SNDRV_SFNT_PAT_TYPE_MAP 7 +#define SNDRV_SFNT_PAT_LOCKED 0x100 /* lock the samples */ +#define SNDRV_SFNT_PAT_SHARED 0x200 /* sample is shared */ + + short reserved; + char name[SNDRV_SFNT_PATCH_NAME_LEN]; +} soundfont_open_parm_t; + + +/* + * raw voice information record + */ + +/* wave table envelope & effect parameters to control EMU8000 */ +typedef struct soundfont_voice_parm_t { + unsigned short moddelay; /* modulation delay (0x8000) */ + unsigned short modatkhld; /* modulation attack & hold time (0x7f7f) */ + unsigned short moddcysus; /* modulation decay & sustain (0x7f7f) */ + unsigned short modrelease; /* modulation release time (0x807f) */ + short modkeyhold, modkeydecay; /* envelope change per key (not used) */ + unsigned short voldelay; /* volume delay (0x8000) */ + unsigned short volatkhld; /* volume attack & hold time (0x7f7f) */ + unsigned short voldcysus; /* volume decay & sustain (0x7f7f) */ + unsigned short volrelease; /* volume release time (0x807f) */ + short volkeyhold, volkeydecay; /* envelope change per key (not used) */ + unsigned short lfo1delay; /* LFO1 delay (0x8000) */ + unsigned short lfo2delay; /* LFO2 delay (0x8000) */ + unsigned short pefe; /* modulation pitch & cutoff (0x0000) */ + unsigned short fmmod; /* LFO1 pitch & cutoff (0x0000) */ + unsigned short tremfrq; /* LFO1 volume & freq (0x0000) */ + unsigned short fm2frq2; /* LFO2 pitch & freq (0x0000) */ + unsigned char cutoff; /* initial cutoff (0xff) */ + unsigned char filterQ; /* initial filter Q [0-15] (0x0) */ + unsigned char chorus; /* chorus send (0x00) */ + unsigned char reverb; /* reverb send (0x00) */ + unsigned short reserved[4]; /* not used */ +} soundfont_voice_parm_t; + + +/* wave table parameters: 92 bytes */ +typedef struct soundfont_voice_info_t { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* sample offset correction */ + int loopstart, loopend; /* loop offset correction */ + short rate_offset; /* sample rate pitch offset */ + unsigned short mode; /* sample mode */ +#define SNDRV_SFNT_MODE_ROMSOUND 0x8000 +#define SNDRV_SFNT_MODE_STEREO 1 +#define SNDRV_SFNT_MODE_LOOPING 2 +#define SNDRV_SFNT_MODE_NORELEASE 4 /* obsolete */ +#define SNDRV_SFNT_MODE_INIT_PARM 8 + + short root; /* midi root key */ + short tune; /* pitch tuning (in cents) */ + unsigned char low, high; /* key note range */ + unsigned char vellow, velhigh; /* velocity range */ + signed char fixkey, fixvel; /* fixed key, velocity */ + signed char pan, fixpan; /* panning, fixed panning */ + short exclusiveClass; /* exclusive class (0 = none) */ + unsigned char amplitude; /* sample volume (127 max) */ + unsigned char attenuation; /* attenuation (0.375dB) */ + short scaleTuning; /* pitch scale tuning(%), normally 100 */ + soundfont_voice_parm_t parm; /* voice envelope parameters */ + unsigned short sample_mode; /* sample mode_flag (set by driver) */ +} soundfont_voice_info_t; + + +/* instrument info header: 4 bytes */ +typedef struct soundfont_voice_rec_hdr_t { + unsigned char bank; /* midi bank number */ + unsigned char instr; /* midi preset number */ + char nvoices; /* number of voices */ + char write_mode; /* write mode; normally 0 */ +#define SNDRV_SFNT_WR_APPEND 0 /* append anyway */ +#define SNDRV_SFNT_WR_EXCLUSIVE 1 /* skip if already exists */ +#define SNDRV_SFNT_WR_REPLACE 2 /* replace if already exists */ +} soundfont_voice_rec_hdr_t; + + +/* + * sample wave information + */ + +/* wave table sample header: 32 bytes */ +typedef struct soundfont_sample_info_t { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* start & end offset */ + int loopstart, loopend; /* loop start & end offset */ + int size; /* size (0 = ROM) */ + short dummy; /* not used */ + unsigned short mode_flags; /* mode flags */ +#define SNDRV_SFNT_SAMPLE_8BITS 1 /* wave data is 8bits */ +#define SNDRV_SFNT_SAMPLE_UNSIGNED 2 /* wave data is unsigned */ +#define SNDRV_SFNT_SAMPLE_NO_BLANK 4 /* no blank loop is attached */ +#define SNDRV_SFNT_SAMPLE_SINGLESHOT 8 /* single-shot w/o loop */ +#define SNDRV_SFNT_SAMPLE_BIDIR_LOOP 16 /* bidirectional looping */ +#define SNDRV_SFNT_SAMPLE_STEREO_LEFT 32 /* stereo left sound */ +#define SNDRV_SFNT_SAMPLE_STEREO_RIGHT 64 /* stereo right sound */ +#define SNDRV_SFNT_SAMPLE_REVERSE_LOOP 128 /* reverse looping */ + unsigned int truesize; /* used memory size (set by driver) */ +} soundfont_sample_info_t; + + +/* + * voice preset mapping (aliasing) + */ + +typedef struct soundfont_voice_map_t { + int map_bank, map_instr, map_key; /* key = -1 means all keys */ + int src_bank, src_instr, src_key; +} soundfont_voice_map_t; + + +#endif /* __SOUND_SFNT_INFO_H */ diff -Nru a/include/sound/snd_wavefront.h b/include/sound/snd_wavefront.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/snd_wavefront.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,141 @@ +#ifndef __SOUND_SND_WAVEFRONT_H__ +#define __SOUND_SND_WAVEFRONT_H__ + +#include "cs4231.h" +#include "mpu401.h" +#include "hwdep.h" +#include "rawmidi.h" +#include "wavefront.h" /* generic OSS/ALSA/user-level wavefront header */ + +/* MIDI interface */ + +struct _snd_wavefront_midi; +struct _snd_wavefront_card; +struct _snd_wavefront; + +typedef struct _snd_wavefront_midi snd_wavefront_midi_t; +typedef struct _snd_wavefront_card snd_wavefront_card_t; +typedef struct _snd_wavefront snd_wavefront_t; + +typedef enum { internal_mpu = 0, external_mpu = 1 } snd_wavefront_mpu_id; + +struct _snd_wavefront_midi { + unsigned long base; /* I/O port address */ + char isvirtual; /* doing virtual MIDI stuff ? */ + char istimer; /* timer is used */ + snd_wavefront_mpu_id output_mpu; /* most-recently-used */ + snd_wavefront_mpu_id input_mpu; /* most-recently-used */ + unsigned int mode[2]; /* MPU401_MODE_XXX */ + snd_rawmidi_substream_t *substream_output[2]; + snd_rawmidi_substream_t *substream_input[2]; + struct timer_list timer; + spinlock_t open; + spinlock_t virtual; /* protects isvirtual */ +}; + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define UART_MODE_ON 0x3F + +extern snd_rawmidi_ops_t snd_wavefront_midi_output; +extern snd_rawmidi_ops_t snd_wavefront_midi_input; + +extern void snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *); +extern void snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *); +extern void snd_wavefront_midi_interrupt (snd_wavefront_card_t *); +extern int snd_wavefront_midi_start (snd_wavefront_card_t *); + +struct _snd_wavefront { + unsigned long irq; /* "you were one, one of the few ..." */ + unsigned long base; /* low i/o port address */ + struct resource *res_base; /* i/o port resource allocation */ + +#define mpu_data_port base +#define mpu_command_port base + 1 /* write semantics */ +#define mpu_status_port base + 1 /* read semantics */ +#define data_port base + 2 +#define status_port base + 3 /* read semantics */ +#define control_port base + 3 /* write semantics */ +#define block_port base + 4 /* 16 bit, writeonly */ +#define last_block_port base + 6 /* 16 bit, writeonly */ + + /* FX ports. These are mapped through the ICS2115 to the YS225. + The ICS2115 takes care of flipping the relevant pins on the + YS225 so that access to each of these ports does the right + thing. Note: these are NOT documented by Turtle Beach. + */ + +#define fx_status base + 8 +#define fx_op base + 8 +#define fx_lcr base + 9 +#define fx_dsp_addr base + 0xa +#define fx_dsp_page base + 0xb +#define fx_dsp_lsb base + 0xc +#define fx_dsp_msb base + 0xd +#define fx_mod_addr base + 0xe +#define fx_mod_data base + 0xf + + volatile int irq_ok; /* set by interrupt handler */ + volatile int irq_cnt; /* ditto */ + char debug; /* debugging flags */ + int freemem; /* installed RAM, in bytes */ + + char fw_version[2]; /* major = [0], minor = [1] */ + char hw_version[2]; /* major = [0], minor = [1] */ + char israw; /* needs Motorola microcode */ + char has_fx; /* has FX processor (Tropez+) */ + char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ + char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ + char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ + int samples_used; /* how many */ + char interrupts_are_midi; /* h/w MPU interrupts enabled ? */ + char rom_samples_rdonly; /* can we write on ROM samples */ + wait_queue_head_t interrupt_sleeper; + snd_wavefront_midi_t midi; /* ICS2115 MIDI interface */ +}; + +struct _snd_wavefront_card { + snd_wavefront_t wavefront; +#ifdef __ISAPNP__ + struct isapnp_dev *wss; + struct isapnp_dev *ctrl; + struct isapnp_dev *mpu; + struct isapnp_dev *synth; +#endif /* CONFIG_ISAPNP */ +}; + +extern void snd_wavefront_internal_interrupt (snd_wavefront_card_t *card); +extern int snd_wavefront_interrupt_bits (int irq); +extern int snd_wavefront_detect_irq (snd_wavefront_t *dev) ; +extern int snd_wavefront_check_irq (snd_wavefront_t *dev, int irq); +extern int snd_wavefront_restart (snd_wavefront_t *dev); +extern int snd_wavefront_start (snd_wavefront_t *dev); +extern int snd_wavefront_detect (snd_wavefront_card_t *card); +extern int snd_wavefront_config_midi (snd_wavefront_t *dev) ; +extern int snd_wavefront_cmd (snd_wavefront_t *, int, unsigned char *, + unsigned char *); + +extern int snd_wavefront_synth_ioctl (snd_hwdep_t *, + struct file *, + unsigned int cmd, + unsigned long arg); +extern int snd_wavefront_synth_open (snd_hwdep_t *, struct file *); +extern int snd_wavefront_synth_release (snd_hwdep_t *, struct file *); + +/* FX processor - see also yss225.[ch] */ + +extern int snd_wavefront_fx_start (snd_wavefront_t *); +extern int snd_wavefront_fx_detect (snd_wavefront_t *); +extern int snd_wavefront_fx_ioctl (snd_hwdep_t *, + struct file *, + unsigned int cmd, + unsigned long arg); +extern int snd_wavefront_fx_open (snd_hwdep_t *, struct file *); +extern int snd_wavefront_fx_release (snd_hwdep_t *, struct file *); + +/* prefix in all snd_printk() delivered messages */ + +#define LOGNAME "WaveFront: " + +#endif /* __SOUND_SND_WAVEFRONT_H__ */ diff -Nru a/include/sound/sndmagic.h b/include/sound/sndmagic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/sndmagic.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,145 @@ +#ifndef __SOUND_SNDMAGIC_H +#define __SOUND_SNDMAGIC_H + +/* + * Magic allocation, deallocation, check + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifdef CONFIG_SND_DEBUG_MEMORY + +void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags); +void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags); +void snd_magic_kfree(void *ptr); + +#define snd_magic_kcalloc(type, extra, flags) (type *) _snd_magic_kcalloc(type##_magic, sizeof(type) + extra, flags) +#define snd_magic_kmalloc(type, extra, flags) (type *) _snd_magic_kmalloc(type##_magic, sizeof(type) + extra, flags) + +static inline unsigned long _snd_magic_value(void *obj) +{ + return obj == NULL ? -1 : *(((unsigned long *)obj) - 1); +} + +static inline int _snd_magic_bad(void *obj, unsigned long magic) +{ + return _snd_magic_value(obj) != magic; +} + +#define snd_magic_cast1(t, expr, cmd) snd_magic_cast(t, expr, cmd) + +#define snd_magic_cast(type, ptr, action...) (type *) ({\ + void *__ptr = ptr;\ + unsigned long __magic = _snd_magic_value(__ptr);\ + if (__magic != type##_magic) {\ + snd_printk("bad MAGIC (0x%lx)\n", __magic);\ + action;\ + }\ + __ptr;\ +}) + +#define snd_device_t_magic 0xa15a00ff +#define snd_pcm_t_magic 0xa15a0101 +#define snd_pcm_file_t_magic 0xa15a0102 +#define snd_pcm_substream_t_magic 0xa15a0103 +#define snd_pcm_proc_private_t_magic 0xa15a0104 +#define snd_pcm_oss_file_t_magic 0xa15a0105 +#define snd_mixer_oss_t_magic 0xa15a0106 + +#define snd_info_private_data_t_magic 0xa15a0201 +#define snd_ctl_file_t_magic 0xa15a0301 +#define snd_kcontrol_t_magic 0xa15a0302 +#define snd_rawmidi_t_magic 0xa15a0401 +#define snd_rawmidi_file_t_magic 0xa15a0402 +#define snd_virmidi_t_magic 0xa15a0403 +#define snd_virmidi_dev_t_magic 0xa15a0404 +#define snd_timer_t_magic 0xa15a0501 +#define snd_timer_user_t_magic 0xa15a0502 +#define snd_hwdep_t_magic 0xa15a0601 +#define snd_seq_device_t_magic 0xa15a0701 + +#define es18xx_t_magic 0xa15a1101 +#define trident_t_magic 0xa15a1201 +#define es1938_t_magic 0xa15a1301 +#define cs46xx_t_magic 0xa15a1401 +#define ensoniq_t_magic 0xa15a1501 +#define sonicvibes_t_magic 0xa15a1601 +#define mpu401_t_magic 0xa15a1701 +#define fm801_t_magic 0xa15a1801 +#define ac97_t_magic 0xa15a1901 +#define ak4531_t_magic 0xa15a1a01 +#define snd_uart16550_t_magic 0xa15a1b01 +#define emu10k1_t_magic 0xa15a1c01 +#define emu10k1_pcm_t_magic 0xa15a1c02 +#define emu10k1_midi_t_magic 0xa15a1c03 +#define snd_gus_card_t_magic 0xa15a1d01 +#define gus_pcm_private_t_magic 0xa15a1d02 +#define gus_proc_private_t_magic 0xa15a1d03 +#define tea6330t_t_magic 0xa15a1e01 +#define ad1848_t_magic 0xa15a1f01 +#define cs4231_t_magic 0xa15a2001 +#define es1688_t_magic 0xa15a2101 +#define opti93x_t_magic 0xa15a2201 +#define emu8000_t_magic 0xa15a2301 +#define emu8000_proc_private_t_magic 0xa15a2302 +#define snd_emux_t_magic 0xa15a2303 +#define snd_emux_port_t_magic 0xa15a2304 +#define sb_t_magic 0xa15a2401 +#define snd_sb_csp_t_magic 0xa15a2402 +#define snd_card_dummy_t_magic 0xa15a2501 +#define snd_card_dummy_pcm_t_magic 0xa15a2502 +#define opl3_t_magic 0xa15a2601 +#define snd_seq_dummy_port_t_magic 0xa15a2701 +#define ice1712_t_magic 0xa15a2801 +#define ad1816a_t_magic 0xa15a2901 +#define intel8x0_t_magic 0xa15a2a01 +#define es1968_t_magic 0xa15a2b01 +#define esschan_t_magic 0xa15a2b02 +#define via686a_t_magic 0xa15a2c01 +#define pdplus_t_magic 0xa15a2d01 +#define cmipci_t_magic 0xa15a2e01 +#define ymfpci_t_magic 0xa15a2f01 +#define ymfpci_pcm_t_magic 0xa15a2f02 +#define cs4281_t_magic 0xa15a3001 +#define snd_i2c_bus_t_magic 0xa15a3101 +#define snd_i2c_device_t_magic 0xa15a3102 +#define cs8427_t_magic 0xa15a3111 +#define m3_t_magic 0xa15a3201 +#define m3_dma_t_magic 0xa15a3202 +#define nm256_t_magic 0xa15a3301 +#define nm256_dma_t_magic 0xa15a3302 +#define via8233_t_magic 0xa15a3401 +#define pmac_t_magic 0xa15a3501 +#define ali_t_magic 0xa15a3601 +#define mtpav_t_magic 0xa15a3701 +#define mtpav_port_t_magic 0xa15a3702 +#define korg1212_t_magic 0xa15a3800 +#define opl3sa2_t_magic 0xa15a3900 + +#else + +#define snd_magic_kcalloc(type, extra, flags) (type *) snd_kcalloc(sizeof(type) + extra, flags) +#define snd_magic_kmalloc(type, extra, flags) (type *) kmalloc(sizeof(type) + extra, flags) +#define snd_magic_cast(type, ptr, retval) (type *) ptr +#define snd_magic_cast1(type, ptr, retval) snd_magic_cast(type, ptr, retval) +#define snd_magic_kfree kfree + +#endif + +#endif /* __SOUND_SNDMAGIC_H */ diff -Nru a/include/sound/soundfont.h b/include/sound/soundfont.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/soundfont.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,130 @@ +#ifndef __SOUND_SOUNDFONT_H +#define __SOUND_SOUNDFONT_H + +/* + * Soundfont defines and definitions. + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sfnt_info.h" +#include "util_mem.h" + +#define SF_MAX_INSTRUMENTS 128 /* maximum instrument number */ +#define SF_MAX_PRESETS 256 /* drums are mapped from 128 to 256 */ +#define SF_IS_DRUM_BANK(z) ((z) == 128) + +typedef struct snd_sf_zone { + struct snd_sf_zone *next; /* Link to next */ + unsigned char bank; /* Midi bank for this zone */ + unsigned char instr; /* Midi program for this zone */ + unsigned char mapped; /* True if mapped to something else */ + + soundfont_voice_info_t v; /* All the soundfont parameters */ + int counter; + struct snd_sf_sample *sample; /* Link to sample */ + + /* The following deals with preset numbers (programs) */ + struct snd_sf_zone *next_instr; /* Next zone of this instrument */ + struct snd_sf_zone *next_zone; /* Next zone in play list */ +} snd_sf_zone_t; + +typedef struct snd_sf_sample { + soundfont_sample_info_t v; + int counter; + snd_util_memblk_t *block; /* allocated data block */ + struct snd_sf_sample *next; +} snd_sf_sample_t; + +/* + * This represents all the information relating to a soundfont. + */ +typedef struct snd_soundfont { + struct snd_soundfont *next; /* Link to next */ + /*struct snd_soundfont *prev;*/ /* Link to previous */ + short id; /* file id */ + short type; /* font type */ + unsigned char name[SNDRV_SFNT_PATCH_NAME_LEN]; /* identifier */ + snd_sf_zone_t *zones; /* Font information */ + snd_sf_sample_t *samples; /* The sample headers */ +} snd_soundfont_t; + +/* + * Type of the sample access callback + */ +typedef int (*snd_sf_sample_new_t)(void *private_data, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr, const void *buf, long count); +typedef int (*snd_sf_sample_free_t)(void *private_data, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr); +typedef void (*snd_sf_sample_reset_t)(void *private); + +typedef struct snd_sf_callback { + void *private_data; + snd_sf_sample_new_t sample_new; + snd_sf_sample_free_t sample_free; + snd_sf_sample_reset_t sample_reset; +} snd_sf_callback_t; + +/* + * List of soundfonts. + */ +typedef struct snd_sf_list { + snd_soundfont_t *currsf; /* The currently open soundfont */ + int open_client; /* client pointer for lock */ + int mem_used; /* used memory size */ + snd_sf_zone_t *presets[SF_MAX_PRESETS]; + snd_soundfont_t *fonts; /* The list of soundfonts */ + int fonts_size; /* number of fonts allocated */ + int zone_counter; /* last allocated time for zone */ + int sample_counter; /* last allocated time for sample */ + int zone_locked; /* locked time for zone */ + int sample_locked; /* locked time for sample */ + snd_sf_callback_t callback; /* callback functions */ + char sf_locked; /* font lock flag */ + struct semaphore presets_mutex; + spinlock_t lock; + snd_util_memhdr_t *memhdr; +} snd_sf_list_t; + +/* Prototypes for soundfont.c */ +int snd_soundfont_load(snd_sf_list_t *sflist, const void *data, long count, int client); +int snd_soundfont_load_guspatch(snd_sf_list_t *sflist, const char *data, + long count, int client); +int snd_soundfont_close_check(snd_sf_list_t *sflist, int client); + +snd_sf_list_t *snd_sf_new(snd_sf_callback_t *callback, snd_util_memhdr_t *hdr); +void snd_sf_free(snd_sf_list_t *sflist); + +int snd_soundfont_remove_samples(snd_sf_list_t *sflist); +int snd_soundfont_remove_unlocked(snd_sf_list_t *sflist); +int snd_soundfont_mem_used(snd_sf_list_t *sflist); + +int snd_soundfont_search_zone(snd_sf_list_t *sflist, int *notep, int vel, + int preset, int bank, + int def_preset, int def_bank, + snd_sf_zone_t **table, int max_layers); + +/* Parameter conversions */ +int snd_sf_calc_parm_hold(int msec); +int snd_sf_calc_parm_attack(int msec); +int snd_sf_calc_parm_decay(int msec); +#define snd_sf_calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725); +extern int snd_sf_vol_table[128]; + + +#endif /* __SOUND_SOUNDFONT_H */ diff -Nru a/include/sound/soundmem.h b/include/sound/soundmem.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/soundmem.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,8 @@ +/* + * Onboard memory management + */ + +struct SNDRV_STRU_BANK_INFO { + unsigned int address; + unsigned int size; +}; diff -Nru a/include/sound/tea6330t.h b/include/sound/tea6330t.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/tea6330t.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,42 @@ +#ifndef __SOUND_TEA6330T_H +#define __SOUND_TEA6330T_H + +/* + * Routines for control of TEA6330T circuit. + * Sound fader control circuit for car radios. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ + +#include "control.h" +#include "i2c.h" /* generic i2c support */ + +typedef struct { + snd_i2c_device_t *device; + snd_i2c_bus_t *bus; + int equalizer; + int fader; + unsigned char regs[8]; + unsigned char mleft, mright; + unsigned char bass, treble; + unsigned char max_bass, max_treble; +} tea6330t_t; + +extern int snd_tea6330t_detect(snd_i2c_bus_t *bus, int equalizer); +extern int snd_tea6330t_update_mixer(snd_card_t * card, snd_i2c_bus_t * bus, int equalizer, int fader); + +#endif /* __SOUND_TEA6330T_H */ diff -Nru a/include/sound/timer.h b/include/sound/timer.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/timer.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,137 @@ +#ifndef __SOUND_TIMER_H +#define __SOUND_TIMER_H + +/* + * Timer abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +typedef enum sndrv_timer_class snd_timer_class_t; +typedef enum sndrv_timer_slave_class snd_timer_slave_class_t; +typedef enum sndrv_timer_global snd_timer_global_t; +typedef struct sndrv_timer_id snd_timer_id_t; +typedef struct sndrv_timer_select snd_timer_select_t; +typedef struct sndrv_timer_info snd_timer_info_t; +typedef struct sndrv_timer_params snd_timer_params_t; +typedef struct sndrv_timer_status snd_timer_status_t; +typedef struct sndrv_timer_read snd_timer_read_t; + +#define _snd_timer_chip(timer) ((timer)->private_data) +#define snd_timer_chip(timer) snd_magic_cast1(chip_t, _snd_timer_chip(timer), return -ENXIO) + +#define SNDRV_TIMER_DEVICES 16 + +#define SNDRV_TIMER_DEV_FLG_PCM 0x10000000 + +#define SNDRV_TIMER_HW_AUTO 0x00000001 /* auto trigger is supported */ +#define SNDRV_TIMER_HW_STOP 0x00000002 /* call stop before start */ +#define SNDRV_TIMER_HW_SLAVE 0x00000004 /* only slave timer (variable resolution) */ +#define SNDRV_TIMER_HW_FIRST 0x00000008 /* first tick can be incomplete */ + +#define SNDRV_TIMER_IFLG_SLAVE 0x00000001 +#define SNDRV_TIMER_IFLG_RUNNING 0x00000002 +#define SNDRV_TIMER_IFLG_START 0x00000004 +#define SNDRV_TIMER_IFLG_AUTO 0x00000008 /* auto restart */ + +#define SNDRV_TIMER_FLG_SYSTEM 0x00000001 /* system timer */ +#define SNDRV_TIMER_FLG_CHANGE 0x00000002 +#define SNDRV_TIMER_FLG_RESCHED 0x00000004 /* need reschedule */ + +typedef void (*snd_timer_callback_t) (snd_timer_instance_t * timeri, unsigned long ticks, unsigned long resolution, void *data); + +struct _snd_timer_hardware { + /* -- must be filled with low-level driver */ + unsigned int flags; /* various flags */ + unsigned long resolution; /* average timer resolution for one tick in nsec */ + unsigned long ticks; /* max timer ticks per interrupt */ + /* -- low-level functions -- */ + int (*open) (snd_timer_t * timer); + int (*close) (snd_timer_t * timer); + unsigned long (*c_resolution) (snd_timer_t * timer); + int (*start) (snd_timer_t * timer); + int (*stop) (snd_timer_t * timer); +}; + +struct _snd_timer { + snd_timer_class_t tmr_class; + snd_card_t *card; + int tmr_device; + int tmr_subdevice; + char id[64]; + char name[80]; + unsigned int flags; + int running; /* running instances */ + unsigned long sticks; /* schedule ticks */ + void *private_data; + void (*private_free) (snd_timer_t *timer); + struct _snd_timer_hardware hw; + spinlock_t lock; + struct list_head device_list; + struct list_head open_list_head; + struct list_head active_list_head; +}; + +struct _snd_timer_instance { + snd_timer_t * timer; + char *owner; + unsigned int flags; + void *private_data; + void (*private_free) (snd_timer_instance_t *ti); + snd_timer_callback_t callback; + void *callback_data; + unsigned long ticks; + unsigned long cticks; + unsigned long lost; /* lost ticks */ + snd_timer_slave_class_t slave_class; + unsigned int slave_id; + struct list_head open_list; + struct list_head active_list; + struct list_head slave_list_head; + struct list_head slave_active_head; + snd_timer_instance_t *master; + atomic_t in_use; /* don't free */ +}; + +/* + * Registering + */ + +extern int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer); +extern int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer); +extern int snd_timer_global_free(snd_timer_t *timer); +extern int snd_timer_global_register(snd_timer_t *timer); +extern int snd_timer_global_unregister(snd_timer_t *timer); + +extern snd_timer_instance_t *snd_timer_open(char *owner, snd_timer_id_t *tid, unsigned int slave_id); +extern int snd_timer_close(snd_timer_instance_t * timeri); +extern int snd_timer_set_owner(snd_timer_instance_t * timeri, pid_t pid, gid_t gid); +extern int snd_timer_reset_owner(snd_timer_instance_t * timeri); +extern int snd_timer_set_resolution(snd_timer_instance_t * timeri, unsigned long resolution); +extern unsigned long snd_timer_resolution(snd_timer_instance_t * timeri); +extern int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks); +extern int snd_timer_stop(snd_timer_instance_t * timeri); +extern int snd_timer_continue(snd_timer_instance_t * timeri); + +extern void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left); + +extern unsigned int snd_timer_system_resolution(void); + +#endif /* __SOUND_TIMER_H */ diff -Nru a/include/sound/trident.h b/include/sound/trident.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/trident.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,476 @@ +#ifndef __SOUND_TRIDENT_H +#define __SOUND_TRIDENT_H + +/* + * audio@tridentmicro.com + * Fri Feb 19 15:55:28 MST 1999 + * Definitions for Trident 4DWave DX/NX chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "mpu401.h" +#include "ac97_codec.h" +#include "seq_midi_emul.h" +#include "seq_device.h" +#include "util_mem.h" +//#include "ainstr_iw.h" +//#include "ainstr_gf1.h" +#include "ainstr_simple.h" + +#ifndef PCI_VENDOR_ID_TRIDENT +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#endif +#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 +#endif +#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 +#endif + +#ifndef PCI_VENDOR_ID_SI +#define PCI_VENDOR_ID_SI 0x1039 +#endif +#ifndef PCI_DEVICE_ID_SI_7018 +#define PCI_DEVICE_ID_SI_7018 0x7018 +#endif + +#define TRIDENT_DEVICE_ID_DX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX) +#define TRIDENT_DEVICE_ID_NX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) +#define TRIDENT_DEVICE_ID_SI7018 ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018) + +/* Trident chipsets have 1GB memory limit */ +#ifdef __alpha__ +#define TRIDENT_DMA_TYPE SNDRV_DMA_TYPE_PCI_16MB +#define TRIDENT_GFP_FLAGS GFP_DMA +#else +#define TRIDENT_DMA_TYPE SNDRV_DMA_TYPE_PCI +#if defined(__i386__) && !defined(CONFIG_1GB) +#define TRIDENT_GFP_FLAGS GFP_DMA +#else +#define TRIDENT_GFP_FLAGS 0 +#endif +#endif + +#define SNDRV_SEQ_DEV_ID_TRIDENT "trident-synth" + +#define SNDRV_TRIDENT_VOICE_TYPE_PCM 0 +#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH 1 +#define SNDRV_TRIDENT_VOICE_TYPE_MIDI 2 + +#define SNDRV_TRIDENT_VFLG_RUNNING (1<<0) + +/* TLB code constants */ +#define SNDRV_TRIDENT_PAGE_SIZE 4096 +#define SNDRV_TRIDENT_PAGE_SHIFT 12 +#define SNDRV_TRIDENT_PAGE_MASK ((1<port + (x)) + +#define ID_4DWAVE_DX 0x2000 +#define ID_4DWAVE_NX 0x2001 + +/* Bank definitions */ + +#define T4D_BANK_A 0 +#define T4D_BANK_B 1 +#define T4D_NUM_BANKS 2 + +/* Register definitions */ + +/* Global registers */ + +enum global_control_bits { + CHANNEL_IDX = 0x0000003f, OVERRUN_IE = 0x00000400, + UNDERRUN_IE = 0x00000800, ENDLP_IE = 0x00001000, + MIDLP_IE = 0x00002000, ETOG_IE = 0x00004000, + EDROP_IE = 0x00008000, BANK_B_EN = 0x00010000 +}; + +enum miscint_bits { + PB_UNDERRUN_IRQ = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, + SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, + OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020, + ENVELOPE_IRQ = 0x00000040, PB_UNDERRUN = 0x00000100, + REC_OVERRUN = 0x00000200, MIXER_UNDERFLOW = 0x00000400, + MIXER_OVERFLOW = 0x00000800, NX_SB_IRQ_DISABLE = 0x00001000, + ST_TARGET_REACHED = 0x00008000, + PB_24K_MODE = 0x00010000, ST_IRQ_EN = 0x00800000, + ACGPIO_IRQ = 0x01000000 +}; + +/* T2 legacy dma control registers. */ +#define LEGACY_DMAR0 0x00 // ADR0 +#define LEGACY_DMAR4 0x04 // CNT0 +#define LEGACY_DMAR6 0x06 // CNT0 - High bits +#define LEGACY_DMAR11 0x0b // MOD +#define LEGACY_DMAR15 0x0f // MMR + +#define T4D_START_A 0x80 +#define T4D_STOP_A 0x84 +#define T4D_DLY_A 0x88 +#define T4D_SIGN_CSO_A 0x8c +#define T4D_CSPF_A 0x90 +#define T4D_CSPF_B 0xbc +#define T4D_CEBC_A 0x94 +#define T4D_AINT_A 0x98 +#define T4D_AINTEN_A 0x9c +#define T4D_LFO_GC_CIR 0xa0 +#define T4D_MUSICVOL_WAVEVOL 0xa8 +#define T4D_SBDELTA_DELTA_R 0xac +#define T4D_MISCINT 0xb0 +#define T4D_START_B 0xb4 +#define T4D_STOP_B 0xb8 +#define T4D_SBBL_SBCL 0xc0 +#define T4D_SBCTRL_SBE2R_SBDD 0xc4 +#define T4D_STIMER 0xc8 +#define T4D_AINT_B 0xd8 +#define T4D_AINTEN_B 0xdc +#define T4D_RCI 0x70 + +/* MPU-401 UART */ +#define T4D_MPU401_BASE 0x20 +#define T4D_MPUR0 0x20 +#define T4D_MPUR1 0x21 +#define T4D_MPUR2 0x22 +#define T4D_MPUR3 0x23 + +/* S/PDIF Registers */ +#define NX_SPCTRL_SPCSO 0x24 +#define NX_SPLBA 0x28 +#define NX_SPESO 0x2c +#define NX_SPCSTATUS 0x64 + +/* NX Specific Registers */ +#define NX_TLBC 0x6c + +/* Channel Registers */ + +#define CH_START 0xe0 + +#define CH_DX_CSO_ALPHA_FMS 0xe0 +#define CH_DX_ESO_DELTA 0xe8 +#define CH_DX_FMC_RVOL_CVOL 0xec + +#define CH_NX_DELTA_CSO 0xe0 +#define CH_NX_DELTA_ESO 0xe8 +#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec + +#define CH_LBA 0xe4 +#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0 +#define CH_EBUF1 0xf4 +#define CH_EBUF2 0xf8 + +/* AC-97 Registers */ + +#define DX_ACR0_AC97_W 0x40 +#define DX_ACR1_AC97_R 0x44 +#define DX_ACR2_AC97_COM_STAT 0x48 + +#define NX_ACR0_AC97_COM_STAT 0x40 +#define NX_ACR1_AC97_W 0x44 +#define NX_ACR2_AC97_R_PRIMARY 0x48 +#define NX_ACR3_AC97_R_SECONDARY 0x4c + +#define SI_AC97_WRITE 0x40 +#define SI_AC97_READ 0x44 +#define SI_SERIAL_INTF_CTRL 0x48 +#define SI_AC97_GPIO 0x4c +#define SI_ASR0 0x50 +#define SI_SPDIF_CS 0x70 +#define SI_GPIO 0x7c + +enum trident_nx_ac97_bits { + /* ACR1-3 */ + NX_AC97_BUSY_WRITE = 0x0800, + NX_AC97_BUSY_READ = 0x0800, + NX_AC97_BUSY_DATA = 0x0400, + NX_AC97_WRITE_SECONDARY = 0x0100, + /* ACR0 */ + NX_AC97_SECONDARY_READY = 0x0040, + NX_AC97_SECONDARY_RECORD = 0x0020, + NX_AC97_SURROUND_OUTPUT = 0x0010, + NX_AC97_PRIMARY_READY = 0x0008, + NX_AC97_PRIMARY_RECORD = 0x0004, + NX_AC97_PCM_OUTPUT = 0x0002, + NX_AC97_WARM_RESET = 0x0001 +}; + +enum trident_dx_ac97_bits { + DX_AC97_BUSY_WRITE = 0x8000, + DX_AC97_BUSY_READ = 0x8000, + DX_AC97_READY = 0x0010, + DX_AC97_RECORD = 0x0008, + DX_AC97_PLAYBACK = 0x0002 +}; + +enum sis7018_ac97_bits { + SI_AC97_BUSY_WRITE = 0x00008000, + SI_AC97_AUDIO_BUSY = 0x00004000, + SI_AC97_MODEM_BUSY = 0x00002000, + SI_AC97_BUSY_READ = 0x00008000, + SI_AC97_SECONDARY = 0x00000080, +}; + +enum serial_intf_ctrl_bits { + WARM_RESET = 0x00000001, + COLD_RESET = 0x00000002, + I2S_CLOCK = 0x00000004, + PCM_SEC_AC97 = 0x00000008, + AC97_DBL_RATE = 0x00000010, + SPDIF_EN = 0x00000020, + I2S_OUTPUT_EN = 0x00000040, + I2S_INPUT_EN = 0x00000080, + PCMIN = 0x00000100, + LINE1IN = 0x00000200, + MICIN = 0x00000400, + LINE2IN = 0x00000800, + HEAD_SET_IN = 0x00001000, + GPIOIN = 0x00002000, + /* 7018 spec says id = 01 but the demo board routed to 10 + SECONDARY_ID= 0x00004000, */ + SECONDARY_ID = 0x00004000, + PCMOUT = 0x00010000, + SURROUT = 0x00020000, + CENTEROUT = 0x00040000, + LFEOUT = 0x00080000, + LINE1OUT = 0x00100000, + LINE2OUT = 0x00200000, + GPIOOUT = 0x00400000, + SI_AC97_PRIMARY_READY = 0x01000000, + SI_AC97_SECONDARY_READY = 0x02000000, + SI_AC97_POWERDOWN = 0x04000000, +}; + +/* PCM defaults */ + +#define T4D_DEFAULT_PCM_VOL 10 /* 0 - 255 */ +#define T4D_DEFAULT_PCM_PAN 0 /* 0 - 127 */ +#define T4D_DEFAULT_PCM_RVOL 127 /* 0 - 127 */ +#define T4D_DEFAULT_PCM_CVOL 127 /* 0 - 127 */ + +typedef struct _snd_trident trident_t; +typedef struct _snd_trident_voice snd_trident_voice_t; +typedef struct _snd_trident_pcm_mixer snd_trident_pcm_mixer_t; + +typedef struct { + void (*sample_start)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_position_t position); + void (*sample_stop)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_stop_mode_t mode); + void (*sample_freq)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_frequency_t freq); + void (*sample_volume)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_ev_volume_t *volume); + void (*sample_loop)(trident_t *card, snd_trident_voice_t *voice, snd_seq_ev_loop_t *loop); + void (*sample_pos)(trident_t *card, snd_trident_voice_t *voice, snd_seq_position_t position); + void (*sample_private1)(trident_t *card, snd_trident_voice_t *voice, unsigned char *data); +} snd_trident_sample_ops_t; + +typedef struct { + snd_midi_channel_set_t * chset; + trident_t * trident; + int mode; /* operation mode */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + int midi_has_voices: 1; +} snd_trident_port_t; + +typedef struct snd_trident_memblk_arg { + short first_page, last_page; +} snd_trident_memblk_arg_t; + +typedef struct { + unsigned int * entries; /* 16k-aligned TLB table */ + dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */ + unsigned long * shadow_entries; /* shadow entries with virtual addresses */ + void * buffer; /* pointer for table calloc */ + dma_addr_t buffer_dmaaddr; /* not accessible PCI BUS physical address */ + snd_util_memhdr_t * memhdr; /* page allocation list */ + void * silent_page; /* silent page */ + dma_addr_t silent_page_dmaaddr; /* not accessible PCI BUS physical address */ +} snd_trident_tlb_t; + +struct _snd_trident_voice { + unsigned int number; + int use: 1, + pcm: 1, + synth:1, + midi: 1; + unsigned int flags; + unsigned char client; + unsigned char port; + unsigned char index; + + snd_seq_instr_t instr; + snd_trident_sample_ops_t *sample_ops; + + /* channel parameters */ + unsigned int CSO; /* 24 bits (16 on DX) */ + unsigned int ESO; /* 24 bits (16 on DX) */ + unsigned int LBA; /* 30 bits */ + unsigned short EC; /* 12 bits */ + unsigned short Alpha; /* 12 bits */ + unsigned short Delta; /* 16 bits */ + unsigned short Attribute; /* 16 bits - SiS 7018 */ + unsigned short Vol; /* 12 bits (6.6) */ + unsigned char Pan; /* 7 bits (1.4.2) */ + unsigned char GVSel; /* 1 bit */ + unsigned char RVol; /* 7 bits (5.2) */ + unsigned char CVol; /* 7 bits (5.2) */ + unsigned char FMC; /* 2 bits */ + unsigned char CTRL; /* 4 bits */ + unsigned char FMS; /* 4 bits */ + unsigned char LFO; /* 8 bits */ + + unsigned int negCSO; /* nonzero - use negative CSO */ + + snd_util_memblk_t *memblk; /* memory block if TLB enabled */ + + /* PCM data */ + + trident_t *trident; + snd_pcm_substream_t *substream; + snd_trident_voice_t *extra; /* extra PCM voice (acts as interrupt generator) */ + int running: 1, + capture: 1, + spdif: 1, + foldback: 1; + int foldback_chan; /* foldback subdevice number */ + unsigned int stimer; /* global sample timer (to detect spurious interrupts) */ + unsigned int spurious_threshold; /* spurious threshold */ + + /* --- */ + + void *private_data; + void (*private_free)(snd_trident_voice_t *voice); +}; + +struct _snd_4dwave { + int seq_client; + + snd_trident_port_t seq_ports[4]; + snd_simple_ops_t simple_ops; + snd_seq_kinstr_list_t *ilist; + + snd_trident_voice_t voices[64]; + + int ChanSynthCount; /* number of allocated synth channels */ + int max_size; /* maximum synth memory size in bytes */ + int current_size; /* current allocated synth mem in bytes */ +}; + +struct _snd_trident_pcm_mixer { + snd_trident_voice_t *voice; /* active voice */ + unsigned short vol; /* front volume */ + unsigned char pan; /* pan control */ + unsigned char rvol; /* rear volume */ + unsigned char cvol; /* center volume */ + unsigned char pad; + snd_kcontrol_t *ctl_vol; /* front volume */ + snd_kcontrol_t *ctl_pan; /* pan */ + snd_kcontrol_t *ctl_rvol; /* rear volume */ + snd_kcontrol_t *ctl_cvol; /* center volume */ +}; + +struct _snd_trident { + int irq; + + unsigned int device; /* device ID */ + + unsigned char bDMAStart; + + unsigned long port; + struct resource *res_port; + unsigned long midi_port; + + unsigned int spurious_irq_count; + unsigned int spurious_irq_max_delta; + + snd_trident_tlb_t tlb; /* TLB entries for NX cards */ + + unsigned char spdif_ctrl; + unsigned char spdif_pcm_ctrl; + unsigned int spdif_bits; + unsigned int spdif_pcm_bits; + snd_kcontrol_t *spdif_pcm_ctl; /* S/PDIF settings */ + unsigned int ac97_ctrl; + + unsigned int ChanMap[2]; /* allocation map for hardware channels */ + + int ChanPCM; /* max number of PCM channels */ + int ChanPCMcnt; /* actual number of PCM channels */ + + struct _snd_4dwave synth; /* synth specific variables */ + + spinlock_t event_lock; + spinlock_t voice_alloc; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; /* ADC/DAC PCM */ + snd_pcm_t *foldback; /* Foldback PCM */ + snd_pcm_t *spdif; /* SPDIF PCM */ + snd_rawmidi_t *rmidi; + snd_seq_device_t *seq_dev; + + ac97_t *ac97; + + unsigned int musicvol_wavevol; + snd_trident_pcm_mixer_t pcm_mixer[32]; + + spinlock_t reg_lock; + snd_info_entry_t *proc_entry; +}; + +int snd_trident_create(snd_card_t * card, + struct pci_dev *pci, + int pcm_streams, + int pcm_spdif_device, + int max_wavetable_size, + trident_t ** rtrident); +int snd_trident_free(trident_t *trident); + +int snd_trident_pcm(trident_t * trident, int device, snd_pcm_t **rpcm); +int snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_t **rpcm); +int snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t **rpcm); +int snd_trident_attach_synthesizer(trident_t * trident); +int snd_trident_detach_synthesizer(trident_t * trident); +snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int client, int port); +void snd_trident_free_voice(trident_t * trident, snd_trident_voice_t *voice); +void snd_trident_start_voice(trident_t * trident, unsigned int voice); +void snd_trident_stop_voice(trident_t * trident, unsigned int voice); +void snd_trident_write_voice_regs(trident_t * trident, snd_trident_voice_t *voice); +void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max); + +/* TLB memory allocation */ +snd_util_memblk_t *snd_trident_alloc_pages(trident_t *trident, void *pages, dma_addr_t addr, unsigned long size); +int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk); +snd_util_memblk_t *snd_trident_synth_alloc(trident_t *trident, unsigned int size); +int snd_trident_synth_free(trident_t *trident, snd_util_memblk_t *blk); +int snd_trident_synth_bzero(trident_t *trident, snd_util_memblk_t *blk, int offset, int size); +int snd_trident_synth_copy_from_user(trident_t *trident, snd_util_memblk_t *blk, int offset, const char *data, int size); + +/* Power Management */ +#ifdef CONFIG_PM +void snd_trident_suspend(trident_t *trident); +void snd_trident_resume(trident_t *trident); +#endif + +#endif /* __SOUND_TRIDENT_H */ diff -Nru a/include/sound/util_mem.h b/include/sound/util_mem.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/util_mem.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,64 @@ +#ifndef __SOUND_UTIL_MEM_H +#define __SOUND_UTIL_MEM_H +/* + * Copyright (C) 2000 Takashi Iwai + * + * Generic memory management routines for soundcard memory allocation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +typedef struct snd_util_memblk snd_util_memblk_t; +typedef struct snd_util_memhdr snd_util_memhdr_t; +typedef unsigned int snd_util_unit_t; + +/* + * memory block + */ +struct snd_util_memblk { + snd_util_unit_t size; /* size of this block */ + snd_util_unit_t offset; /* zero-offset of this block */ + struct list_head list; /* link */ +}; + +#define snd_util_memblk_argptr(blk) (void*)((char*)(blk) + sizeof(snd_util_memblk_t)) + +/* + * memory management information + */ +struct snd_util_memhdr { + snd_util_unit_t size; /* size of whole data */ + struct list_head block; /* block linked-list header */ + int nblocks; /* # of allocated blocks */ + snd_util_unit_t used; /* used memory size */ + int block_extra_size; /* extra data size of chunk */ + struct semaphore block_mutex; /* lock */ +}; + +/* + * prototypes + */ +snd_util_memhdr_t *snd_util_memhdr_new(int memsize); +void snd_util_memhdr_free(snd_util_memhdr_t *hdr); +snd_util_memblk_t *snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size); +int snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk); +int snd_util_mem_avail(snd_util_memhdr_t *hdr); + +/* functions without mutex */ +snd_util_memblk_t *__snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size); +void __snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk); +snd_util_memblk_t *__snd_util_memblk_new(snd_util_memhdr_t *hdr, snd_util_unit_t units, struct list_head *prev); + +#endif /* __SOUND_UTIL_MEM_H */ diff -Nru a/include/sound/version.h b/include/sound/version.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/version.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,3 @@ +/* include/version.h. Generated automatically by configure. */ +#define CONFIG_SND_VERSION "0.9.0beta11" +#define CONFIG_SND_DATE " (Tue Feb 19 08:14:59 2002 UTC)" diff -Nru a/include/sound/wavefront.h b/include/sound/wavefront.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/wavefront.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,695 @@ +#ifndef __SOUND_WAVEFRONT_H__ +#define __SOUND_WAVEFRONT_H__ + +/* + * Driver for Turtle Beach Wavefront cards (Maui,Tropez,Tropez+) + * + * Copyright (c) by Paul Barton-Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if (!defined(__GNUC__) && !defined(__GNUG__)) + + You will not be able to compile this file correctly without gcc, because + it is necessary to pack the "wavefront_alias" structure to a size + of 22 bytes, corresponding to 16-bit alignment (as would have been + the case on the original platform, MS-DOS). If this is not done, + then WavePatch-format files cannot be read/written correctly. + The method used to do this here ("__attribute__((packed)") is + completely compiler dependent. + + All other wavefront_* types end up aligned to 32 bit values and + still have the same (correct) size. + +#else + + /* However, note that as of G++ 2.7.3.2, g++ was unable to + correctly parse *type* __attribute__ tags. It will do the + right thing if we use the "packed" attribute on each struct + member, which has the same semantics anyway. + */ + +#endif /* __GNUC__ */ + +/***************************** WARNING ******************************** + PLEASE DO NOT MODIFY THIS FILE IN ANY WAY THAT AFFECTS ITS ABILITY TO + BE USED WITH EITHER C *OR* C++. + **********************************************************************/ + +#ifndef NUM_MIDIKEYS +#define NUM_MIDIKEYS 128 +#endif /* NUM_MIDIKEYS */ + +#ifndef NUM_MIDICHANNELS +#define NUM_MIDICHANNELS 16 +#endif /* NUM_MIDICHANNELS */ + +/* These are very useful/important. the original wavefront interface + was developed on a 16 bit system, where sizeof(int) = 2 + bytes. Defining things like this makes the code much more portable, and + easier to understand without having to toggle back and forth + between a 16-bit view of the world and a 32-bit one. + */ + +#ifndef __KERNEL__ +/* keep them for compatibility */ +typedef short s16; +typedef unsigned short u16; +typedef int s32; +typedef unsigned int u32; +typedef char s8; +typedef unsigned char u8; +typedef s16 INT16; +typedef u16 UINT16; +typedef s32 INT32; +typedef u32 UINT32; +typedef s8 CHAR8; +typedef u8 UCHAR8; +#endif + +/* Pseudo-commands not part of the WaveFront command set. + These are used for various driver controls and direct + hardware control. + */ + +#define WFC_DEBUG_DRIVER 0 +#define WFC_FX_IOCTL 1 +#define WFC_PATCH_STATUS 2 +#define WFC_PROGRAM_STATUS 3 +#define WFC_SAMPLE_STATUS 4 +#define WFC_DISABLE_INTERRUPTS 5 +#define WFC_ENABLE_INTERRUPTS 6 +#define WFC_INTERRUPT_STATUS 7 +#define WFC_ROMSAMPLES_RDONLY 8 +#define WFC_IDENTIFY_SLOT_TYPE 9 + +/* Wavefront synth commands + */ + +#define WFC_DOWNLOAD_SAMPLE 0x80 +#define WFC_DOWNLOAD_BLOCK 0x81 +#define WFC_DOWNLOAD_MULTISAMPLE 0x82 +#define WFC_DOWNLOAD_SAMPLE_ALIAS 0x83 +#define WFC_DELETE_SAMPLE 0x84 +#define WFC_REPORT_FREE_MEMORY 0x85 +#define WFC_DOWNLOAD_PATCH 0x86 +#define WFC_DOWNLOAD_PROGRAM 0x87 +#define WFC_SET_SYNTHVOL 0x89 +#define WFC_SET_NVOICES 0x8B +#define WFC_DOWNLOAD_DRUM 0x90 +#define WFC_GET_SYNTHVOL 0x92 +#define WFC_GET_NVOICES 0x94 +#define WFC_DISABLE_CHANNEL 0x9A +#define WFC_ENABLE_CHANNEL 0x9B +#define WFC_MISYNTH_OFF 0x9D +#define WFC_MISYNTH_ON 0x9E +#define WFC_FIRMWARE_VERSION 0x9F +#define WFC_GET_NSAMPLES 0xA0 +#define WFC_DISABLE_DRUM_PROGRAM 0xA2 +#define WFC_UPLOAD_PATCH 0xA3 +#define WFC_UPLOAD_PROGRAM 0xA4 +#define WFC_SET_TUNING 0xA6 +#define WFC_GET_TUNING 0xA7 +#define WFC_VMIDI_ON 0xA8 +#define WFC_VMIDI_OFF 0xA9 +#define WFC_MIDI_STATUS 0xAA +#define WFC_GET_CHANNEL_STATUS 0xAB +#define WFC_DOWNLOAD_SAMPLE_HEADER 0xAC +#define WFC_UPLOAD_SAMPLE_HEADER 0xAD +#define WFC_UPLOAD_MULTISAMPLE 0xAE +#define WFC_UPLOAD_SAMPLE_ALIAS 0xAF +#define WFC_IDENTIFY_SAMPLE_TYPE 0xB0 +#define WFC_DOWNLOAD_EDRUM_PROGRAM 0xB1 +#define WFC_UPLOAD_EDRUM_PROGRAM 0xB2 +#define WFC_SET_EDRUM_CHANNEL 0xB3 +#define WFC_INSTOUT_LEVELS 0xB4 +#define WFC_PEAKOUT_LEVELS 0xB5 +#define WFC_REPORT_CHANNEL_PROGRAMS 0xB6 +#define WFC_HARDWARE_VERSION 0xCF +#define WFC_UPLOAD_SAMPLE_PARAMS 0xD7 +#define WFC_DOWNLOAD_OS 0xF1 +#define WFC_NOOP 0xFF + +#define WF_MAX_SAMPLE 512 +#define WF_MAX_PATCH 256 +#define WF_MAX_PROGRAM 128 + +#define WF_SECTION_MAX 44 /* longest OS section length */ + +/* # of bytes we send to the board when sending it various kinds of + substantive data, such as samples, patches and programs. +*/ + +#define WF_PROGRAM_BYTES 32 +#define WF_PATCH_BYTES 132 +#define WF_SAMPLE_BYTES 27 +#define WF_SAMPLE_HDR_BYTES 25 +#define WF_ALIAS_BYTES 25 +#define WF_DRUM_BYTES 9 +#define WF_MSAMPLE_BYTES 259 /* (MIDI_KEYS * 2) + 3 */ + +#define WF_ACK 0x80 +#define WF_DMA_ACK 0x81 + +/* OR-values for MIDI status bits */ + +#define WF_MIDI_VIRTUAL_ENABLED 0x1 +#define WF_MIDI_VIRTUAL_IS_EXTERNAL 0x2 +#define WF_MIDI_IN_TO_SYNTH_DISABLED 0x4 + +/* slot indexes for struct address_info: makes code a little more mnemonic */ + +#define WF_SYNTH_SLOT 0 +#define WF_INTERNAL_MIDI_SLOT 1 +#define WF_EXTERNAL_MIDI_SLOT 2 + +/* Magic MIDI bytes used to switch I/O streams on the ICS2115 MPU401 + emulation. Note these NEVER show up in output from the device and + should NEVER be used in input unless Virtual MIDI mode has been + disabled. If they do show up as input, the results are unpredictable. +*/ + +#define WF_EXTERNAL_SWITCH 0xFD +#define WF_INTERNAL_SWITCH 0xF9 + +/* Debugging flags */ + +#define WF_DEBUG_CMD 0x1 +#define WF_DEBUG_DATA 0x2 +#define WF_DEBUG_LOAD_PATCH 0x4 +#define WF_DEBUG_IO 0x8 + +/* WavePatch file format stuff */ + +#define WF_WAVEPATCH_VERSION 120; /* Current version number (1.2) */ +#define WF_MAX_COMMENT 64 /* Comment length */ +#define WF_NUM_LAYERS 4 +#define WF_NAME_LENGTH 32 +#define WF_SOURCE_LENGTH 260 + +#define BankFileID "Bank" +#define DrumkitFileID "DrumKit" +#define ProgramFileID "Program" + +struct wf_envelope +{ + u8 attack_time:7; + u8 Unused1:1; + + u8 decay1_time:7; + u8 Unused2:1; + + u8 decay2_time:7; + u8 Unused3:1; + + u8 sustain_time:7; + u8 Unused4:1; + + u8 release_time:7; + u8 Unused5:1; + + u8 release2_time:7; + u8 Unused6:1; + + s8 attack_level; + s8 decay1_level; + s8 decay2_level; + s8 sustain_level; + s8 release_level; + + u8 attack_velocity:7; + u8 Unused7:1; + + u8 volume_velocity:7; + u8 Unused8:1; + + u8 keyboard_scaling:7; + u8 Unused9:1; +}; +typedef struct wf_envelope wavefront_envelope; + +struct wf_lfo +{ + u8 sample_number; + + u8 frequency:7; + u8 Unused1:1; + + u8 am_src:4; + u8 fm_src:4; + + s8 fm_amount; + s8 am_amount; + s8 start_level; + s8 end_level; + + u8 ramp_delay:7; + u8 wave_restart:1; /* for LFO2 only */ + + u8 ramp_time:7; + u8 Unused2:1; +}; +typedef struct wf_lfo wavefront_lfo; + +struct wf_patch +{ + s16 frequency_bias; /* ** THIS IS IN MOTOROLA FORMAT!! ** */ + + u8 amplitude_bias:7; + u8 Unused1:1; + + u8 portamento:7; + u8 Unused2:1; + + u8 sample_number; + + u8 pitch_bend:4; + u8 sample_msb:1; + u8 Unused3:3; + + u8 mono:1; + u8 retrigger:1; + u8 nohold:1; + u8 restart:1; + u8 filterconfig:2; /* SDK says "not used" */ + u8 reuse:1; + u8 reset_lfo:1; + + u8 fm_src2:4; + u8 fm_src1:4; + + s8 fm_amount1; + s8 fm_amount2; + + u8 am_src:4; + u8 Unused4:4; + + s8 am_amount; + + u8 fc1_mode:4; + u8 fc2_mode:4; + + s8 fc1_mod_amount; + s8 fc1_keyboard_scaling; + s8 fc1_bias; + s8 fc2_mod_amount; + s8 fc2_keyboard_scaling; + s8 fc2_bias; + + u8 randomizer:7; + u8 Unused5:1; + + struct wf_envelope envelope1; + struct wf_envelope envelope2; + struct wf_lfo lfo1; + struct wf_lfo lfo2; +}; +typedef struct wf_patch wavefront_patch; + +struct wf_layer +{ + u8 patch_number; + + u8 mix_level:7; + u8 mute:1; + + u8 split_point:7; + u8 play_below:1; + + u8 pan_mod_src:2; + u8 pan_or_mod:1; + u8 pan:4; + u8 split_type:1; +}; +typedef struct wf_layer wavefront_layer; + +struct wf_program +{ + struct wf_layer layer[WF_NUM_LAYERS]; +}; +typedef struct wf_program wavefront_program; + +struct wf_sample_offset +{ + s32 Fraction:4; + s32 Integer:20; + s32 Unused:8; +}; +typedef struct wf_sample_offset wavefront_sample_offset; + +/* Sample slot types */ + +#define WF_ST_SAMPLE 0 +#define WF_ST_MULTISAMPLE 1 +#define WF_ST_ALIAS 2 +#define WF_ST_EMPTY 3 + +/* pseudo's */ + +#define WF_ST_DRUM 4 +#define WF_ST_PROGRAM 5 +#define WF_ST_PATCH 6 +#define WF_ST_SAMPLEHDR 7 + +#define WF_ST_MASK 0xf + +/* Flags for slot status. These occupy the upper bits of the same byte + as a sample type. +*/ + +#define WF_SLOT_USED 0x80 /* XXX don't rely on this being accurate */ +#define WF_SLOT_FILLED 0x40 +#define WF_SLOT_ROM 0x20 + +#define WF_SLOT_MASK 0xf0 + +/* channel constants */ + +#define WF_CH_MONO 0 +#define WF_CH_LEFT 1 +#define WF_CH_RIGHT 2 + +/* Sample formats */ + +#define LINEAR_16BIT 0 +#define WHITE_NOISE 1 +#define LINEAR_8BIT 2 +#define MULAW_8BIT 3 + +#define WF_SAMPLE_IS_8BIT(smpl) ((smpl)->SampleResolution&2) + + +/* + + Because most/all of the sample data we pass in via pointers has + never been copied (just mmap-ed into user space straight from the + disk), it would be nice to allow handling of multi-channel sample + data without forcing user-level extraction of the relevant bytes. + + So, we need a way of specifying which channel to use (the WaveFront + only handles mono samples in a given slot), and the only way to do + this without using some struct other than wavefront_sample as the + interface is the awful hack of using the unused bits in a + wavefront_sample: + + Val Meaning + --- ------- + 0 no channel selection (use channel 1, sample is MONO) + 1 use first channel, and skip one + 2 use second channel, and skip one + 3 use third channel, and skip two + 4 use fourth channel, skip three + 5 use fifth channel, skip four + 6 use six channel, skip five + + + This can handle up to 4 channels, and anyone downloading >4 channels + of sample data just to select one of them needs to find some tools + like sox ... + + NOTE: values 0, 1 and 2 correspond to WF_CH_* above. This is + important. + +*/ + +#define WF_SET_CHANNEL(samp,chn) \ + (samp)->Unused1 = chn & 0x1; \ + (samp)->Unused2 = chn & 0x2; \ + (samp)->Unused3 = chn & 0x4 + +#define WF_GET_CHANNEL(samp) \ + (((samp)->Unused3 << 2)|((samp)->Unused2<<1)|(samp)->Unused1) + +typedef struct wf_sample { + struct wf_sample_offset sampleStartOffset; + struct wf_sample_offset loopStartOffset; + struct wf_sample_offset loopEndOffset; + struct wf_sample_offset sampleEndOffset; + s16 FrequencyBias; + u8 SampleResolution:2; /* sample_format */ + u8 Unused1:1; + u8 Loop:1; + u8 Bidirectional:1; + u8 Unused2:1; + u8 Reverse:1; + u8 Unused3:1; +} wavefront_sample; + +typedef struct wf_multisample { + s16 NumberOfSamples; /* log2 of the number of samples */ + s16 SampleNumber[NUM_MIDIKEYS]; +} wavefront_multisample; + +typedef struct wf_alias { + s16 OriginalSample __attribute__ ((packed)); + + struct wf_sample_offset sampleStartOffset __attribute__ ((packed)); + struct wf_sample_offset loopStartOffset __attribute__ ((packed)); + struct wf_sample_offset sampleEndOffset __attribute__ ((packed)); + struct wf_sample_offset loopEndOffset __attribute__ ((packed)); + + s16 FrequencyBias __attribute__ ((packed)); + + u8 SampleResolution:2 __attribute__ ((packed)); + u8 Unused1:1 __attribute__ ((packed)); + u8 Loop:1 __attribute__ ((packed)); + u8 Bidirectional:1 __attribute__ ((packed)); + u8 Unused2:1 __attribute__ ((packed)); + u8 Reverse:1 __attribute__ ((packed)); + u8 Unused3:1 __attribute__ ((packed)); + + /* This structure is meant to be padded only to 16 bits on their + original. Of course, whoever wrote their documentation didn't + realize that sizeof(struct) can be >= + sum(sizeof(struct-fields)) and so thought that giving a C level + description of the structs used in WavePatch files was + sufficient. I suppose it was, as long as you remember the + standard 16->32 bit issues. + */ + + u8 sixteen_bit_padding __attribute__ ((packed)); +} wavefront_alias; + +typedef struct wf_drum { + u8 PatchNumber; + u8 MixLevel:7; + u8 Unmute:1; + u8 Group:4; + u8 Unused1:4; + u8 PanModSource:2; + u8 PanModulated:1; + u8 PanAmount:4; + u8 Unused2:1; +} wavefront_drum; + +typedef struct wf_drumkit { + struct wf_drum drum[NUM_MIDIKEYS]; +} wavefront_drumkit; + +typedef struct wf_channel_programs { + u8 Program[NUM_MIDICHANNELS]; +} wavefront_channel_programs; + +/* How to get MIDI channel status from the data returned by + a WFC_GET_CHANNEL_STATUS command (a struct wf_channel_programs) +*/ + +#define WF_CHANNEL_STATUS(ch,wcp) (wcp)[(ch/7)] & (1<<((ch)%7)) + +typedef union wf_any { + wavefront_sample s; + wavefront_multisample ms; + wavefront_alias a; + wavefront_program pr; + wavefront_patch p; + wavefront_drum d; +} wavefront_any; + +/* Hannu Solvainen hoped that his "patch_info" struct in soundcard.h + might work for other wave-table based patch loading situations. + Alas, his fears were correct. The WaveFront doesn't even come with + just "patches", but several different kind of structures that + control the sound generation process. + */ + +typedef struct wf_patch_info { + + /* the first two fields are used by the OSS "patch loading" interface + only, and are unused by the current user-level library. + */ + + s16 key; /* Use WAVEFRONT_PATCH here */ + u16 devno; /* fill in when sending */ + u8 subkey; /* WF_ST_{SAMPLE,ALIAS,etc.} */ + +#define WAVEFRONT_FIND_FREE_SAMPLE_SLOT 999 + + u16 number; /* patch/sample/prog number */ + + u32 size; /* size of any data included in + one of the fields in `hdrptr', or + as `dataptr'. + + NOTE: for actual samples, this is + the size of the *SELECTED CHANNEL* + even if more data is actually available. + + So, a stereo sample (2 channels) of + 6000 bytes total has `size' = 3000. + + See the macros and comments for + WF_{GET,SET}_CHANNEL above. + + */ + wavefront_any *hdrptr; /* user-space ptr to hdr bytes */ + u16 *dataptr; /* actual sample data */ + + wavefront_any hdr; /* kernel-space copy of hdr bytes */ +} wavefront_patch_info; + +/* The maximum number of bytes we will ever move to or from user space + in response to a WFC_* command. This obviously doesn't cover + actual sample data. +*/ + +#define WF_MAX_READ sizeof(wavefront_multisample) +#define WF_MAX_WRITE sizeof(wavefront_multisample) + +/* + This allows us to execute any WF command except the download/upload + ones, which are handled differently due to copyin/copyout issues as + well as data-nybbling to/from the card. + */ + +typedef struct wavefront_control { + int cmd; /* WFC_* */ + char status; /* return status to user-space */ + unsigned char rbuf[WF_MAX_READ]; /* bytes read from card */ + unsigned char wbuf[WF_MAX_WRITE]; /* bytes written to card */ +} wavefront_control; + +#define WFCTL_WFCMD 0x1 +#define WFCTL_LOAD_SPP 0x2 + +/* Modulator table */ + +#define WF_MOD_LFO1 0 +#define WF_MOD_LFO2 1 +#define WF_MOD_ENV1 2 +#define WF_MOD_ENV2 3 +#define WF_MOD_KEYBOARD 4 +#define WF_MOD_LOGKEY 5 +#define WF_MOD_VELOCITY 6 +#define WF_MOD_LOGVEL 7 +#define WF_MOD_RANDOM 8 +#define WF_MOD_PRESSURE 9 +#define WF_MOD_MOD_WHEEL 10 +#define WF_MOD_1 WF_MOD_MOD_WHEEL +#define WF_MOD_BREATH 11 +#define WF_MOD_2 WF_MOD_BREATH +#define WF_MOD_FOOT 12 +#define WF_MOD_4 WF_MOD_FOOT +#define WF_MOD_VOLUME 13 +#define WF_MOD_7 WF_MOD_VOLUME +#define WF_MOD_PAN 14 +#define WF_MOD_10 WF_MOD_PAN +#define WF_MOD_EXPR 15 +#define WF_MOD_11 WF_MOD_EXPR + +/* FX-related material */ + +typedef struct wf_fx_info { + int request; /* see list below */ + long data[4]; /* we don't need much */ +} wavefront_fx_info; + +/* support for each of these will be forthcoming once I or someone + else has figured out which of the addresses on page 6 and page 7 of + the YSS225 control each parameter. Incidentally, these come from + the Windows driver interface, but again, Turtle Beach didn't + document the API to use them. +*/ + +#define WFFX_SETOUTGAIN 0 +#define WFFX_SETSTEREOOUTGAIN 1 +#define WFFX_SETREVERBIN1GAIN 2 +#define WFFX_SETREVERBIN2GAIN 3 +#define WFFX_SETREVERBIN3GAIN 4 +#define WFFX_SETCHORUSINPORT 5 +#define WFFX_SETREVERBIN1PORT 6 +#define WFFX_SETREVERBIN2PORT 7 +#define WFFX_SETREVERBIN3PORT 8 +#define WFFX_SETEFFECTPORT 9 +#define WFFX_SETAUXPORT 10 +#define WFFX_SETREVERBTYPE 11 +#define WFFX_SETREVERBDELAY 12 +#define WFFX_SETCHORUSLFO 13 +#define WFFX_SETCHORUSPMD 14 +#define WFFX_SETCHORUSAMD 15 +#define WFFX_SETEFFECT 16 +#define WFFX_SETBASEALL 17 +#define WFFX_SETREVERBALL 18 +#define WFFX_SETCHORUSALL 20 +#define WFFX_SETREVERBDEF 22 +#define WFFX_SETCHORUSDEF 23 +#define WFFX_DELAYSETINGAIN 24 +#define WFFX_DELAYSETFBGAIN 25 +#define WFFX_DELAYSETFBLPF 26 +#define WFFX_DELAYSETGAIN 27 +#define WFFX_DELAYSETTIME 28 +#define WFFX_DELAYSETFBTIME 29 +#define WFFX_DELAYSETALL 30 +#define WFFX_DELAYSETDEF 32 +#define WFFX_SDELAYSETINGAIN 33 +#define WFFX_SDELAYSETFBGAIN 34 +#define WFFX_SDELAYSETFBLPF 35 +#define WFFX_SDELAYSETGAIN 36 +#define WFFX_SDELAYSETTIME 37 +#define WFFX_SDELAYSETFBTIME 38 +#define WFFX_SDELAYSETALL 39 +#define WFFX_SDELAYSETDEF 41 +#define WFFX_DEQSETINGAIN 42 +#define WFFX_DEQSETFILTER 43 +#define WFFX_DEQSETALL 44 +#define WFFX_DEQSETDEF 46 +#define WFFX_MUTE 47 +#define WFFX_FLANGESETBALANCE 48 +#define WFFX_FLANGESETDELAY 49 +#define WFFX_FLANGESETDWFFX_TH 50 +#define WFFX_FLANGESETFBGAIN 51 +#define WFFX_FLANGESETINGAIN 52 +#define WFFX_FLANGESETLFO 53 +#define WFFX_FLANGESETALL 54 +#define WFFX_FLANGESETDEF 56 +#define WFFX_PITCHSETSHIFT 57 +#define WFFX_PITCHSETBALANCE 58 +#define WFFX_PITCHSETALL 59 +#define WFFX_PITCHSETDEF 61 +#define WFFX_SRSSETINGAIN 62 +#define WFFX_SRSSETSPACE 63 +#define WFFX_SRSSETCENTER 64 +#define WFFX_SRSSETGAIN 65 +#define WFFX_SRSSETMODE 66 +#define WFFX_SRSSETDEF 68 + +/* Allow direct user-space control over FX memory/coefficient data. + In theory this could be used to download the FX microprogram, + but it would be a little slower, and involve some wierd code. + */ + +#define WFFX_MEMSET 69 + +#endif /* __SOUND_WAVEFRONT_H__ */ diff -Nru a/include/sound/wavefront_fx.h b/include/sound/wavefront_fx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/wavefront_fx.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,9 @@ +#ifndef __SOUND_WAVEFRONT_FX_H +#define __SOUND_WAVEFRONT_FX_H + +extern int snd_wavefront_fx_detect (snd_wavefront_t *); +extern void snd_wavefront_fx_ioctl (snd_synth_t *sdev, + unsigned int cmd, + unsigned long arg); + +#endif __SOUND_WAVEFRONT_FX_H diff -Nru a/include/sound/ymfpci.h b/include/sound/ymfpci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/ymfpci.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,369 @@ +#ifndef __SOUND_YMFPCI_H +#define __SOUND_YMFPCI_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for Yahama YMF724/740/744/754 chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include "ac97_codec.h" + +#ifndef PCI_VENDOR_ID_YAMAHA +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_724 +#define PCI_DEVICE_ID_YAMAHA_724 0x0004 +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_724F +#define PCI_DEVICE_ID_YAMAHA_724F 0x000d +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_740 +#define PCI_DEVICE_ID_YAMAHA_740 0x000a +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_740C +#define PCI_DEVICE_ID_YAMAHA_740C 0x000c +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_744 +#define PCI_DEVICE_ID_YAMAHA_744 0x0010 +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_754 +#define PCI_DEVICE_ID_YAMAHA_754 0x0012 +#endif + +/* + * Direct registers + */ + +#define YMFREG(chip, reg) (chip->port + YDSXGR_##reg) + +#define YDSXGR_INTFLAG 0x0004 +#define YDSXGR_ACTIVITY 0x0006 +#define YDSXGR_GLOBALCTRL 0x0008 +#define YDSXGR_ZVCTRL 0x000A +#define YDSXGR_TIMERCTRL 0x0010 +#define YDSXGR_TIMERCOUNT 0x0012 +#define YDSXGR_SPDIFOUTCTRL 0x0018 +#define YDSXGR_SPDIFOUTSTATUS 0x001C +#define YDSXGR_EEPROMCTRL 0x0020 +#define YDSXGR_SPDIFINCTRL 0x0034 +#define YDSXGR_SPDIFINSTATUS 0x0038 +#define YDSXGR_DSPPROGRAMDL 0x0048 +#define YDSXGR_DLCNTRL 0x004C +#define YDSXGR_GPIOININTFLAG 0x0050 +#define YDSXGR_GPIOININTENABLE 0x0052 +#define YDSXGR_GPIOINSTATUS 0x0054 +#define YDSXGR_GPIOOUTCTRL 0x0056 +#define YDSXGR_GPIOFUNCENABLE 0x0058 +#define YDSXGR_GPIOTYPECONFIG 0x005A +#define YDSXGR_AC97CMDDATA 0x0060 +#define YDSXGR_AC97CMDADR 0x0062 +#define YDSXGR_PRISTATUSDATA 0x0064 +#define YDSXGR_PRISTATUSADR 0x0066 +#define YDSXGR_SECSTATUSDATA 0x0068 +#define YDSXGR_SECSTATUSADR 0x006A +#define YDSXGR_SECCONFIG 0x0070 +#define YDSXGR_LEGACYOUTVOL 0x0080 +#define YDSXGR_LEGACYOUTVOLL 0x0080 +#define YDSXGR_LEGACYOUTVOLR 0x0082 +#define YDSXGR_NATIVEDACOUTVOL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLR 0x0086 +#define YDSXGR_ZVOUTVOL 0x0088 +#define YDSXGR_ZVOUTVOLL 0x0088 +#define YDSXGR_ZVOUTVOLR 0x008A +#define YDSXGR_SECADCOUTVOL 0x008C +#define YDSXGR_SECADCOUTVOLL 0x008C +#define YDSXGR_SECADCOUTVOLR 0x008E +#define YDSXGR_PRIADCOUTVOL 0x0090 +#define YDSXGR_PRIADCOUTVOLL 0x0090 +#define YDSXGR_PRIADCOUTVOLR 0x0092 +#define YDSXGR_LEGACYLOOPVOL 0x0094 +#define YDSXGR_LEGACYLOOPVOLL 0x0094 +#define YDSXGR_LEGACYLOOPVOLR 0x0096 +#define YDSXGR_NATIVEDACLOOPVOL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLR 0x009A +#define YDSXGR_ZVLOOPVOL 0x009C +#define YDSXGR_ZVLOOPVOLL 0x009E +#define YDSXGR_ZVLOOPVOLR 0x009E +#define YDSXGR_SECADCLOOPVOL 0x00A0 +#define YDSXGR_SECADCLOOPVOLL 0x00A0 +#define YDSXGR_SECADCLOOPVOLR 0x00A2 +#define YDSXGR_PRIADCLOOPVOL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLR 0x00A6 +#define YDSXGR_NATIVEADCINVOL 0x00A8 +#define YDSXGR_NATIVEADCINVOLL 0x00A8 +#define YDSXGR_NATIVEADCINVOLR 0x00AA +#define YDSXGR_NATIVEDACINVOL 0x00AC +#define YDSXGR_NATIVEDACINVOLL 0x00AC +#define YDSXGR_NATIVEDACINVOLR 0x00AE +#define YDSXGR_BUF441OUTVOL 0x00B0 +#define YDSXGR_BUF441OUTVOLL 0x00B0 +#define YDSXGR_BUF441OUTVOLR 0x00B2 +#define YDSXGR_BUF441LOOPVOL 0x00B4 +#define YDSXGR_BUF441LOOPVOLL 0x00B4 +#define YDSXGR_BUF441LOOPVOLR 0x00B6 +#define YDSXGR_SPDIFOUTVOL 0x00B8 +#define YDSXGR_SPDIFOUTVOLL 0x00B8 +#define YDSXGR_SPDIFOUTVOLR 0x00BA +#define YDSXGR_SPDIFLOOPVOL 0x00BC +#define YDSXGR_SPDIFLOOPVOLL 0x00BC +#define YDSXGR_SPDIFLOOPVOLR 0x00BE +#define YDSXGR_ADCSLOTSR 0x00C0 +#define YDSXGR_RECSLOTSR 0x00C4 +#define YDSXGR_ADCFORMAT 0x00C8 +#define YDSXGR_RECFORMAT 0x00CC +#define YDSXGR_P44SLOTSR 0x00D0 +#define YDSXGR_STATUS 0x0100 +#define YDSXGR_CTRLSELECT 0x0104 +#define YDSXGR_MODE 0x0108 +#define YDSXGR_SAMPLECOUNT 0x010C +#define YDSXGR_NUMOFSAMPLES 0x0110 +#define YDSXGR_CONFIG 0x0114 +#define YDSXGR_PLAYCTRLSIZE 0x0140 +#define YDSXGR_RECCTRLSIZE 0x0144 +#define YDSXGR_EFFCTRLSIZE 0x0148 +#define YDSXGR_WORKSIZE 0x014C +#define YDSXGR_MAPOFREC 0x0150 +#define YDSXGR_MAPOFEFFECT 0x0154 +#define YDSXGR_PLAYCTRLBASE 0x0158 +#define YDSXGR_RECCTRLBASE 0x015C +#define YDSXGR_EFFCTRLBASE 0x0160 +#define YDSXGR_WORKBASE 0x0164 +#define YDSXGR_DSPINSTRAM 0x1000 +#define YDSXGR_CTRLINSTRAM 0x4000 + +#define YDSXG_AC97READCMD 0x8000 +#define YDSXG_AC97WRITECMD 0x0000 + +#define PCIR_DSXG_LEGACY 0x40 +#define PCIR_DSXG_ELEGACY 0x42 +#define PCIR_DSXG_CTRL 0x48 +#define PCIR_DSXG_PWRCTRL1 0x4a +#define PCIR_DSXG_PWRCTRL2 0x4e +#define PCIR_DSXG_FMBASE 0x60 +#define PCIR_DSXG_SBBASE 0x62 +#define PCIR_DSXG_MPU401BASE 0x64 +#define PCIR_DSXG_JOYBASE 0x66 + +#define YDSXG_DSPLENGTH 0x0080 +#define YDSXG_CTRLLENGTH 0x3000 + +#define YDSXG_DEFAULT_WORK_SIZE 0x0400 + +#define YDSXG_PLAYBACK_VOICES 64 +#define YDSXG_CAPTURE_VOICES 2 +#define YDSXG_EFFECT_VOICES 5 + +/* + * + */ + +typedef struct _snd_ymfpci_playback_bank { + u32 format; + u32 loop_default; + u32 base; /* 32-bit address */ + u32 loop_start; /* 32-bit offset */ + u32 loop_end; /* 32-bit offset */ + u32 loop_frac; /* 8-bit fraction - loop_start */ + u32 delta_end; /* pitch delta end */ + u32 lpfK_end; + u32 eg_gain_end; + u32 left_gain_end; + u32 right_gain_end; + u32 eff1_gain_end; + u32 eff2_gain_end; + u32 eff3_gain_end; + u32 lpfQ; + u32 status; + u32 num_of_frames; + u32 loop_count; + u32 start; + u32 start_frac; + u32 delta; + u32 lpfK; + u32 eg_gain; + u32 left_gain; + u32 right_gain; + u32 eff1_gain; + u32 eff2_gain; + u32 eff3_gain; + u32 lpfD1; + u32 lpfD2; +} snd_ymfpci_playback_bank_t; + +typedef struct _snd_ymfpci_capture_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 num_of_loops; /* counter */ +} snd_ymfpci_capture_bank_t; + +typedef struct _snd_ymfpci_effect_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 temp; +} snd_ymfpci_effect_bank_t; + +typedef struct _snd_ymfpci_voice ymfpci_voice_t; +typedef struct _snd_ymfpci_pcm ymfpci_pcm_t; +typedef struct _snd_ymfpci ymfpci_t; + +typedef enum { + YMFPCI_PCM, + YMFPCI_SYNTH, + YMFPCI_MIDI +} ymfpci_voice_type_t; + +struct _snd_ymfpci_voice { + ymfpci_t *chip; + int number; + int use: 1, + pcm: 1, + synth: 1, + midi: 1; + snd_ymfpci_playback_bank_t *bank; + dma_addr_t bank_addr; + void (*interrupt)(ymfpci_t *chip, ymfpci_voice_t *voice); + ymfpci_pcm_t *ypcm; +}; + +typedef enum { + PLAYBACK_VOICE, + CAPTURE_REC, + CAPTURE_AC97, + EFFECT_DRY_LEFT, + EFFECT_DRY_RIGHT, + EFFECT_EFF1, + EFFECT_EFF2, + EFFECT_EFF3 +} snd_ymfpci_pcm_type_t; + +struct _snd_ymfpci_pcm { + ymfpci_t *chip; + snd_ymfpci_pcm_type_t type; + snd_pcm_substream_t *substream; + ymfpci_voice_t *voices[2]; /* playback only */ + int running: 1, + spdif: 1, + mode4ch : 1; + u32 period_size; /* cached from runtime->period_size */ + u32 buffer_size; /* cached from runtime->buffer_size */ + u32 period_pos; + u32 last_pos; + u32 capture_bank_number; + u32 shift; +}; + +struct _snd_ymfpci { + int irq; + + unsigned int device_id; /* PCI device ID */ + unsigned int rev; /* PCI revision */ + unsigned long reg_area_phys; + unsigned long reg_area_virt; + struct resource *res_reg_area; + + unsigned short old_legacy_ctrl; + unsigned int joystick_port; + + void *work_ptr; + dma_addr_t work_ptr_addr; + unsigned long work_ptr_size; + + unsigned int bank_size_playback; + unsigned int bank_size_capture; + unsigned int bank_size_effect; + unsigned int work_size; + + void *bank_base_playback; + void *bank_base_capture; + void *bank_base_effect; + void *work_base; + dma_addr_t bank_base_playback_addr; + dma_addr_t bank_base_capture_addr; + dma_addr_t bank_base_effect_addr; + dma_addr_t work_base_addr; + void *ac3_tmp_base; + dma_addr_t ac3_tmp_base_addr; + + u32 *ctrl_playback; + snd_ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2]; + snd_ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2]; + snd_ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2]; + + int start_count; + + u32 active_bank; + ymfpci_voice_t voices[64]; + + ac97_t *ac97; + snd_rawmidi_t *rawmidi; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_t *pcm2; + snd_pcm_t *pcm_spdif; + snd_pcm_t *pcm_4ch; + snd_pcm_substream_t *capture_substream[YDSXG_CAPTURE_VOICES]; + snd_pcm_substream_t *effect_substream[YDSXG_EFFECT_VOICES]; + snd_kcontrol_t *ctl_vol_recsrc; + snd_kcontrol_t *ctl_vol_adcrec; + snd_kcontrol_t *ctl_vol_spdifrec; + unsigned short spdif_bits, spdif_pcm_bits; + snd_kcontrol_t *spdif_pcm_ctl; + + spinlock_t reg_lock; + spinlock_t voice_lock; + wait_queue_head_t interrupt_sleep; + atomic_t interrupt_sleep_count; + snd_info_entry_t *proc_entry; + +#ifdef CONFIG_PM + u32 *saved_regs; + u32 saved_ydsxgr_mode; +#endif +}; + +int snd_ymfpci_create(snd_card_t * card, + struct pci_dev *pci, + unsigned short old_legacy_ctrl, + ymfpci_t ** rcodec); + +int snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_mixer(ymfpci_t *chip); +int snd_ymfpci_joystick(ymfpci_t *chip); + +int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice); +int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice); + +#ifdef CONFIG_PM +void snd_ymfpci_suspend(ymfpci_t *chip); +void snd_ymfpci_resume(ymfpci_t *chip); +#endif + +#endif /* __SOUND_YMFPCI_H */ diff -Nru a/include/sound/yss225.h b/include/sound/yss225.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/yss225.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,23 @@ +#ifndef __SOUND_YSS225_H +#define __SOUND_YSS225_H + +extern unsigned char page_zero[256]; +extern unsigned char page_one[256]; +extern unsigned char page_two[128]; +extern unsigned char page_three[128]; +extern unsigned char page_four[128]; +extern unsigned char page_six[192]; +extern unsigned char page_seven[256]; +extern unsigned char page_zero_v2[96]; +extern unsigned char page_one_v2[96]; +extern unsigned char page_two_v2[48]; +extern unsigned char page_three_v2[48]; +extern unsigned char page_four_v2[48]; +extern unsigned char page_seven_v2[96]; +extern unsigned char mod_v2[304]; +extern unsigned char coefficients[364]; +extern unsigned char coefficients2[56]; +extern unsigned char coefficients3[404]; + + +#endif /* __SOUND_YSS225_H */ diff -Nru a/kernel/context.c b/kernel/context.c --- a/kernel/context.c Tue Feb 19 18:08:59 2002 +++ b/kernel/context.c Tue Feb 19 18:08:59 2002 @@ -77,7 +77,7 @@ spin_lock_irq(&curtask->sigmask_lock); siginitsetinv(&curtask->blocked, sigmask(SIGCHLD)); - recalc_sigpending(curtask); + recalc_sigpending(); spin_unlock_irq(&curtask->sigmask_lock); complete((struct completion *)startup); @@ -106,7 +106,7 @@ ; spin_lock_irq(&curtask->sigmask_lock); flush_signals(curtask); - recalc_sigpending(curtask); + recalc_sigpending(); spin_unlock_irq(&curtask->sigmask_lock); } } diff -Nru a/kernel/exit.c b/kernel/exit.c --- a/kernel/exit.c Tue Feb 19 18:08:59 2002 +++ b/kernel/exit.c Tue Feb 19 18:08:59 2002 @@ -31,8 +31,6 @@ static void release_task(struct task_struct * p) { - unsigned long flags; - if (p == current) BUG(); #ifdef CONFIG_SMP @@ -46,25 +44,7 @@ current->cmin_flt += p->min_flt + p->cmin_flt; current->cmaj_flt += p->maj_flt + p->cmaj_flt; current->cnswap += p->nswap + p->cnswap; - /* - * Potentially available timeslices are retrieved - * here - this way the parent does not get penalized - * for creating too many processes. - * - * (this cannot be used to artificially 'generate' - * timeslices, because any timeslice recovered here - * was given away by the parent in the first place.) - */ - __save_flags(flags); - __cli(); - current->time_slice += p->time_slice; - if (current->time_slice > MAX_TIMESLICE) - current->time_slice = MAX_TIMESLICE; - if (p->sleep_avg < current->sleep_avg) - current->sleep_avg = (current->sleep_avg * EXIT_WEIGHT + - p->sleep_avg) / (EXIT_WEIGHT + 1); - __restore_flags(flags); - + sched_exit(p); p->pid = 0; put_task_struct(p); } @@ -172,9 +152,8 @@ current->exit_signal = SIGCHLD; current->ptrace = 0; - if ((current->policy == SCHED_OTHER) && - (current->__nice < DEF_USER_NICE)) - set_user_nice(current, DEF_USER_NICE); + if ((current->policy == SCHED_OTHER) && (task_nice(current) < 0)) + set_user_nice(current, 0); /* cpus_allowed? */ /* rt_priority? */ /* signals? */ @@ -497,7 +476,12 @@ write_lock_irq(&tasklist_lock); } } - write_unlock_irq(&tasklist_lock); + /* + * No need to unlock IRQs, we'll schedule() immediately + * anyway. In the preemption case this also makes it + * impossible for the task to get runnable again. + */ + write_unlock(&tasklist_lock); } NORET_TYPE void do_exit(long code) diff -Nru a/kernel/fork.c b/kernel/fork.c --- a/kernel/fork.c Tue Feb 19 18:08:57 2002 +++ b/kernel/fork.c Tue Feb 19 18:08:57 2002 @@ -129,6 +129,7 @@ { static int next_safe = PID_MAX; struct task_struct *p; + int pid; if (flags & CLONE_PID) return current->pid; @@ -164,9 +165,10 @@ } read_unlock(&tasklist_lock); } + pid = last_pid; spin_unlock(&lastpid_lock); - return last_pid; + return pid; } static inline int dup_mmap(struct mm_struct * mm) @@ -749,13 +751,10 @@ * runqueue lock is not a problem. */ current->time_slice = 1; - scheduler_tick(current); + scheduler_tick(0, 0); } p->sleep_timestamp = jiffies; __restore_flags(flags); - - if (p->policy == SCHED_OTHER) - p->prio = MAX_PRIO - 1 - ((MAX_PRIO - 1 - p->prio) * 1) / 3; /* * Ok, add it to the run-queues and make it diff -Nru a/kernel/kmod.c b/kernel/kmod.c --- a/kernel/kmod.c Tue Feb 19 18:08:59 2002 +++ b/kernel/kmod.c Tue Feb 19 18:08:59 2002 @@ -113,7 +113,7 @@ sigemptyset(&curtask->blocked); flush_signals(curtask); flush_signal_handlers(curtask); - recalc_sigpending(curtask); + recalc_sigpending(); spin_unlock_irq(&curtask->sigmask_lock); for (i = 0; i < curtask->files->max_fds; i++ ) { @@ -228,7 +228,7 @@ spin_lock_irq(¤t->sigmask_lock); tmpsig = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); waitpid_result = waitpid(pid, NULL, __WCLONE); @@ -237,7 +237,7 @@ /* Allow signals again.. */ spin_lock_irq(¤t->sigmask_lock); current->blocked = tmpsig; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (waitpid_result != pid) { diff -Nru a/kernel/ksyms.c b/kernel/ksyms.c --- a/kernel/ksyms.c Tue Feb 19 18:08:57 2002 +++ b/kernel/ksyms.c Tue Feb 19 18:08:57 2002 @@ -213,7 +213,6 @@ EXPORT_SYMBOL(generic_commit_write); EXPORT_SYMBOL(block_truncate_page); EXPORT_SYMBOL(generic_block_bmap); -EXPORT_SYMBOL(waitfor_one_page); EXPORT_SYMBOL(generic_file_read); EXPORT_SYMBOL(do_generic_file_read); EXPORT_SYMBOL(generic_file_write); @@ -253,6 +252,8 @@ EXPORT_SYMBOL(vfs_fstat); EXPORT_SYMBOL(vfs_stat); EXPORT_SYMBOL(vfs_lstat); +EXPORT_SYMBOL(lock_rename); +EXPORT_SYMBOL(unlock_rename); EXPORT_SYMBOL(generic_read_dir); EXPORT_SYMBOL(generic_file_llseek); EXPORT_SYMBOL(remote_llseek); @@ -321,7 +322,6 @@ EXPORT_SYMBOL(tq_disk); EXPORT_SYMBOL(init_buffer); EXPORT_SYMBOL(refile_buffer); -EXPORT_SYMBOL(max_readahead); EXPORT_SYMBOL(wipe_partitions); /* tty routines */ @@ -455,6 +455,8 @@ EXPORT_SYMBOL(schedule_timeout); EXPORT_SYMBOL(sys_sched_yield); EXPORT_SYMBOL(set_user_nice); +EXPORT_SYMBOL(task_nice); +EXPORT_SYMBOL_GPL(idle_cpu); EXPORT_SYMBOL(jiffies); EXPORT_SYMBOL(xtime); EXPORT_SYMBOL(do_gettimeofday); @@ -521,7 +523,6 @@ EXPORT_SYMBOL(clear_inode); EXPORT_SYMBOL(___strtok); EXPORT_SYMBOL(init_special_inode); -EXPORT_SYMBOL(read_ahead); EXPORT_SYMBOL(__get_hash_table); EXPORT_SYMBOL(new_inode); EXPORT_SYMBOL(insert_inode_hash); diff -Nru a/kernel/ptrace.c b/kernel/ptrace.c --- a/kernel/ptrace.c Tue Feb 19 18:09:00 2002 +++ b/kernel/ptrace.c Tue Feb 19 18:09:00 2002 @@ -151,7 +151,7 @@ if (write) { memcpy(maddr + offset, buf, bytes); flush_page_to_ram(page); - flush_icache_page(vma, page); + flush_icache_user_range(vma, page, addr, bytes); } else { memcpy(buf, maddr + offset, bytes); flush_page_to_ram(page); diff -Nru a/kernel/sched.c b/kernel/sched.c --- a/kernel/sched.c Tue Feb 19 18:08:58 2002 +++ b/kernel/sched.c Tue Feb 19 18:08:58 2002 @@ -20,8 +20,108 @@ #include #include #include +#include +#include -#define BITMAP_SIZE ((((MAX_PRIO+7)/8)+sizeof(long)-1)/sizeof(long)) +/* + * Priority of a process goes from 0 to 139. The 0-99 + * priority range is allocated to RT tasks, the 100-139 + * range is for SCHED_OTHER tasks. Priority values are + * inverted: lower p->prio value means higher priority. + */ +#define MAX_RT_PRIO 100 +#define MAX_PRIO (MAX_RT_PRIO + 40) + +/* + * Convert user-nice values [ -20 ... 0 ... 19 ] + * to static priority [ 100 ... 139 (MAX_PRIO-1) ], + * and back. + */ +#define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 20) +#define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20) +#define TASK_NICE(p) PRIO_TO_NICE((p)->static_prio) + +/* + * 'User priority' is the nice value converted to something we + * can work with better when scaling various scheduler parameters, + * it's a [ 0 ... 39 ] range. + */ +#define USER_PRIO(p) ((p)-MAX_RT_PRIO) +#define TASK_USER_PRIO(p) USER_PRIO((p)->static_prio) +#define MAX_USER_PRIO (USER_PRIO(MAX_PRIO)) + +/* + * These are the 'tuning knobs' of the scheduler: + * + * Minimum timeslice is 10 msecs, default timeslice is 150 msecs, + * maximum timeslice is 300 msecs. Timeslices get refilled after + * they expire. + */ +#define MIN_TIMESLICE ( 10 * HZ / 1000) +#define MAX_TIMESLICE (300 * HZ / 1000) +#define CHILD_PENALTY 95 +#define PARENT_PENALTY 100 +#define EXIT_WEIGHT 3 +#define PRIO_BONUS_RATIO 25 +#define INTERACTIVE_DELTA 2 +#define MAX_SLEEP_AVG (2*HZ) +#define STARVATION_LIMIT (2*HZ) + +/* + * If a task is 'interactive' then we reinsert it in the active + * array after it has expired its current timeslice. (it will not + * continue to run immediately, it will still roundrobin with + * other interactive tasks.) + * + * This part scales the interactivity limit depending on niceness. + * + * We scale it linearly, offset by the INTERACTIVE_DELTA delta. + * Here are a few examples of different nice levels: + * + * TASK_INTERACTIVE(-20): [1,1,1,1,1,1,1,1,1,0,0] + * TASK_INTERACTIVE(-10): [1,1,1,1,1,1,1,0,0,0,0] + * TASK_INTERACTIVE( 0): [1,1,1,1,0,0,0,0,0,0,0] + * TASK_INTERACTIVE( 10): [1,1,0,0,0,0,0,0,0,0,0] + * TASK_INTERACTIVE( 19): [0,0,0,0,0,0,0,0,0,0,0] + * + * (the X axis represents the possible -5 ... 0 ... +5 dynamic + * priority range a task can explore, a value of '1' means the + * task is rated interactive.) + * + * Ie. nice +19 tasks can never get 'interactive' enough to be + * reinserted into the active array. And only heavily CPU-hog nice -20 + * tasks will be expired. Default nice 0 tasks are somewhere between, + * it takes some effort for them to get interactive, but it's not + * too hard. + */ + +#define SCALE(v1,v1_max,v2_max) \ + (v1) * (v2_max) / (v1_max) + +#define DELTA(p) \ + (SCALE(TASK_NICE(p), 40, MAX_USER_PRIO*PRIO_BONUS_RATIO/100) + \ + INTERACTIVE_DELTA) + +#define TASK_INTERACTIVE(p) \ + ((p)->prio <= (p)->static_prio - DELTA(p)) + +/* + * TASK_TIMESLICE scales user-nice values [ -20 ... 19 ] + * to time slice values. + * + * The higher a process's priority, the bigger timeslices + * it gets during one round of execution. But even the lowest + * priority process gets MIN_TIMESLICE worth of execution time. + */ + +#define TASK_TIMESLICE(p) (MIN_TIMESLICE + \ + ((MAX_TIMESLICE - MIN_TIMESLICE) * (MAX_PRIO-1-(p)->static_prio)/39)) + +/* + * These are the runqueue data structures: + */ + +#define BITMAP_SIZE ((((MAX_PRIO+1+7)/8)+sizeof(long)-1)/sizeof(long)) typedef struct runqueue runqueue_t; @@ -54,8 +154,7 @@ #define this_rq() cpu_rq(smp_processor_id()) #define task_rq(p) cpu_rq((p)->thread_info->cpu) #define cpu_curr(cpu) (cpu_rq(cpu)->curr) -#define rt_task(p) ((p)->policy != SCHED_OTHER) - +#define rt_task(p) ((p)->prio < MAX_RT_PRIO) static inline runqueue_t *lock_task_rq(task_t *p, unsigned long *flags) { @@ -78,6 +177,7 @@ spin_unlock_irqrestore(&rq->lock, *flags); preempt_enable(); } + /* * Adding/removing a task to/from a priority array: */ @@ -97,66 +197,25 @@ p->array = array; } -/* - * A task is 'heavily interactive' if it either has reached the - * bottom 25% of the SCHED_OTHER priority range, or if it is below - * its default priority by at least 3 priority levels. In this - * case we favor it by reinserting it on the active array, - * even after it expired its current timeslice. - * - * A task is a 'CPU hog' if it's either in the upper 25% of the - * SCHED_OTHER priority range, or if's not an interactive task. - * - * A task can get a priority bonus by being 'somewhat - * interactive' - and it will get a priority penalty for - * being a CPU hog. - * - */ - -#define PRIO_INTERACTIVE \ - (MAX_RT_PRIO + MAX_USER_PRIO*PRIO_INTERACTIVE_RATIO/100) -#define PRIO_CPU_HOG \ - (MAX_RT_PRIO + MAX_USER_PRIO*PRIO_CPU_HOG_RATIO/100) - -#define TASK_INTERACTIVE(p) \ - (((p)->prio <= PRIO_INTERACTIVE) || \ - (((p)->prio < PRIO_CPU_HOG) && \ - ((p)->prio <= NICE_TO_PRIO((p)->__nice) - INTERACTIVE_DELTA))) - -/* - * We place interactive tasks back into the active array, if possible. - * - * To guarantee that this does not starve expired tasks we ignore the - * interactivity of a task if the first expired task had to wait more - * than a 'reasonable' amount of time. This deadline timeout is - * load-dependent, as the frequency of array switched decreases with - * increasing number of running tasks: - */ -#define EXPIRED_STARVING(rq) \ - ((rq)->expired_timestamp && \ - (jiffies - (rq)->expired_timestamp >= \ - STARVATION_LIMIT * ((rq)->nr_running) + 1)) - static inline int effective_prio(task_t *p) { int bonus, prio; /* * Here we scale the actual sleep average [0 .... MAX_SLEEP_AVG] - * into the -14 ... +14 bonus/penalty range. + * into the -5 ... 0 ... +5 bonus/penalty range. * - * We use 70% of the full 0...39 priority range so that: + * We use 25% of the full 0...39 priority range so that: * - * 1) nice +19 CPU hogs do not preempt nice 0 CPU hogs. - * 2) nice -20 interactive tasks do not get preempted by - * nice 0 interactive tasks. + * 1) nice +19 interactive tasks do not preempt nice 0 CPU hogs. + * 2) nice -20 CPU hogs do not get preempted by nice 0 tasks. * * Both properties are important to certain workloads. */ bonus = MAX_USER_PRIO*PRIO_BONUS_RATIO*p->sleep_avg/MAX_SLEEP_AVG/100 - MAX_USER_PRIO*PRIO_BONUS_RATIO/100/2; - prio = NICE_TO_PRIO(p->__nice) - bonus; + prio = p->static_prio - bonus; if (prio < MAX_RT_PRIO) prio = MAX_RT_PRIO; if (prio > MAX_PRIO-1) @@ -191,7 +250,6 @@ rq->nr_running--; dequeue_task(p, p->array); p->array = NULL; - p->sleep_timestamp = jiffies; } static inline void resched_task(task_t *p) @@ -305,16 +363,20 @@ void wake_up_forked_process(task_t * p) { runqueue_t *rq; - + preempt_disable(); rq = this_rq(); p->state = TASK_RUNNING; if (!rt_task(p)) { - p->sleep_avg = p->sleep_avg * CHILD_FORK_PENALTY / 100; + /* + * We decrease the sleep average of forking parents + * and children as well, to keep max-interactive tasks + * from forking tasks that are max-interactive. + */ + current->sleep_avg = current->sleep_avg * PARENT_PENALTY / 100; + p->sleep_avg = p->sleep_avg * CHILD_PENALTY / 100; p->prio = effective_prio(p); - - current->sleep_avg = current->sleep_avg * PARENT_FORK_PENALTY / 100; } spin_lock_irq(&rq->lock); p->thread_info->cpu = smp_processor_id(); @@ -323,10 +385,37 @@ preempt_enable(); } -asmlinkage void schedule_tail(task_t *prev) +/* + * Potentially available exiting-child timeslices are + * retrieved here - this way the parent does not get + * penalized for creating too many processes. + * + * (this cannot be used to 'generate' timeslices + * artificially, because any timeslice recovered here + * was given away by the parent in the first place.) + */ +void sched_exit(task_t * p) +{ + __cli(); + current->time_slice += p->time_slice; + if (unlikely(current->time_slice > MAX_TIMESLICE)) + current->time_slice = MAX_TIMESLICE; + __sti(); + /* + * If the child was a (relative-) CPU hog then decrease + * the sleep_avg of the parent as well. + */ + if (p->sleep_avg < current->sleep_avg) + current->sleep_avg = (current->sleep_avg * EXIT_WEIGHT + + p->sleep_avg) / (EXIT_WEIGHT + 1); +} + +#if CONFIG_SMP +asmlinkage void schedule_tail(void) { spin_unlock_irq(&this_rq()->lock); } +#endif static inline void context_switch(task_t *prev, task_t *next) { @@ -347,17 +436,8 @@ mmdrop(oldmm); } - /* - * Here we just switch the register state and the stack. There are - * 3 processes affected by a context switch: - * - * prev ==> .... ==> (last => next) - * - * It's the 'much more previous' 'prev' that is on next's stack, - * but prev is set to (the just run) 'last' process by switch_to(). - * This might sound slightly confusing but makes tons of sense. - */ - switch_to(prev, next, prev); + /* Here we just switch the register state and the stack. */ + switch_to(prev, next); } unsigned long nr_running(void) @@ -403,6 +483,7 @@ } return nr_running; } + /* * Current runqueue is empty, or rebalance tick: if there is an * inbalance (current runqueue is too short) then pull from @@ -477,7 +558,7 @@ * Make sure nothing changed since we checked the * runqueue length. */ - if (busiest->nr_running <= this_rq->nr_running + 1) + if (busiest->nr_running <= nr_running + 1) goto out_unlock; /* @@ -492,13 +573,13 @@ array = busiest->active; new_array: - /* - * Load-balancing does not affect RT tasks, so we start the - * searching at priority 128. - */ - idx = MAX_RT_PRIO; + /* Start searching at priority 0: */ + idx = 0; skip_bitmap: - idx = find_next_bit(array->bitmap, MAX_PRIO, idx); + if (!idx) + idx = sched_find_first_bit(array->bitmap); + else + idx = find_next_bit(array->bitmap, MAX_PRIO, idx); if (idx == MAX_PRIO) { if (array == busiest->expired) { array = busiest->active; @@ -577,18 +658,43 @@ #endif /* + * We place interactive tasks back into the active array, if possible. + * + * To guarantee that this does not starve expired tasks we ignore the + * interactivity of a task if the first expired task had to wait more + * than a 'reasonable' amount of time. This deadline timeout is + * load-dependent, as the frequency of array switched decreases with + * increasing number of running tasks: + */ +#define EXPIRED_STARVING(rq) \ + ((rq)->expired_timestamp && \ + (jiffies - (rq)->expired_timestamp >= \ + STARVATION_LIMIT * ((rq)->nr_running) + 1)) + +/* * This function gets called by the timer code, with HZ frequency. * We call it with interrupts disabled. */ -void scheduler_tick(task_t *p) +void scheduler_tick(int user_tick, int system) { + int cpu = smp_processor_id(); runqueue_t *rq = this_rq(); -#if CONFIG_SMP - unsigned long now = jiffies; + task_t *p = current; - if (p == rq->idle) - return idle_tick(); + if (p == rq->idle) { + if (local_bh_count(cpu) || local_irq_count(cpu) > 1) + kstat.per_cpu_system[cpu] += system; +#if CONFIG_SMP + idle_tick(); #endif + return; + } + if (TASK_NICE(p) > 0) + kstat.per_cpu_nice[cpu] += user_tick; + else + kstat.per_cpu_user[cpu] += user_tick; + kstat.per_cpu_system[cpu] += system; + /* Task might have expired already, but not scheduled off yet */ if (p->array != rq->active) { set_tsk_need_resched(p); @@ -601,7 +707,7 @@ * FIFO tasks have no timeslices. */ if ((p->policy == SCHED_RR) && !--p->time_slice) { - p->time_slice = NICE_TO_TIMESLICE(p->__nice); + p->time_slice = TASK_TIMESLICE(p); set_tsk_need_resched(p); /* put it at the end of the queue: */ @@ -624,7 +730,7 @@ dequeue_task(p, rq->active); set_tsk_need_resched(p); p->prio = effective_prio(p); - p->time_slice = NICE_TO_TIMESLICE(p->__nice); + p->time_slice = TASK_TIMESLICE(p); if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) { if (!rq->expired_timestamp) @@ -635,7 +741,7 @@ } out: #if CONFIG_SMP - if (!(now % BUSY_REBALANCE_TICK)) + if (!(jiffies % BUSY_REBALANCE_TICK)) load_balance(rq, 0); #endif spin_unlock(&rq->lock); @@ -656,37 +762,30 @@ if (unlikely(in_interrupt())) BUG(); - +#if CONFIG_DEBUG_HIGHMEM + check_highmem_ptes(); +#endif +need_resched: preempt_disable(); prev = current; rq = this_rq(); - + release_kernel_lock(prev, smp_processor_id()); + prev->sleep_timestamp = jiffies; spin_lock_irq(&rq->lock); -#ifdef CONFIG_PREEMPT - /* - * if entering from preempt_schedule, off a kernel preemption, - * go straight to picking the next task. - */ - if (unlikely(preempt_get_count() & PREEMPT_ACTIVE)) - goto pick_next_task; -#endif - switch (prev->state) { - case TASK_RUNNING: - prev->sleep_timestamp = jiffies; - break; case TASK_INTERRUPTIBLE: if (unlikely(signal_pending(prev))) { prev->state = TASK_RUNNING; - prev->sleep_timestamp = jiffies; break; } default: deactivate_task(prev, rq); + case TASK_RUNNING: + ; } -#if CONFIG_SMP || CONFIG_PREEMPT +#if CONFIG_SMP pick_next_task: #endif if (unlikely(!rq->nr_running)) { @@ -735,6 +834,8 @@ reacquire_kernel_lock(current); preempt_enable_no_resched(); + if (test_thread_flag(TIF_NEED_RESCHED)) + goto need_resched; return; } @@ -744,12 +845,10 @@ */ asmlinkage void preempt_schedule(void) { - do { - current_thread_info()->preempt_count += PREEMPT_ACTIVE; - schedule(); - current_thread_info()->preempt_count -= PREEMPT_ACTIVE; - barrier(); - } while (test_thread_flag(TIF_NEED_RESCHED)); + if (unlikely(preempt_get_count())) + return; + current->state = TASK_RUNNING; + schedule(); } #endif /* CONFIG_PREEMPT */ @@ -905,6 +1004,8 @@ new_mask &= cpu_online_map; if (!new_mask) BUG(); + if (p != current) + BUG(); p->cpus_allowed = new_mask; /* @@ -929,7 +1030,7 @@ prio_array_t *array; runqueue_t *rq; - if (p->__nice == nice) + if (TASK_NICE(p) == nice || nice < -20 || nice > 19) return; /* * We have to be careful, if called from sys_setpriority(), @@ -937,13 +1038,13 @@ */ rq = lock_task_rq(p, &flags); if (rt_task(p)) { - p->__nice = nice; + p->static_prio = NICE_TO_PRIO(nice); goto out_unlock; } array = p->array; if (array) dequeue_task(p, array); - p->__nice = nice; + p->static_prio = NICE_TO_PRIO(nice); p->prio = NICE_TO_PRIO(nice); if (array) { enqueue_task(p, array); @@ -951,8 +1052,7 @@ * If the task is running and lowered its priority, * or increased its priority then reschedule its CPU: */ - if ((nice < p->__nice) || - ((p->__nice < nice) && (p == rq->curr))) + if ((NICE_TO_PRIO(nice) < p->static_prio) || (p == rq->curr)) resched_task(rq->curr); } out_unlock: @@ -985,7 +1085,7 @@ if (increment > 40) increment = 40; - nice = current->__nice + increment; + nice = PRIO_TO_NICE(current->static_prio) + increment; if (nice < -20) nice = -20; if (nice > 19) @@ -996,6 +1096,27 @@ #endif +/* + * This is the priority value as seen by users in /proc + * + * RT tasks are offset by -200. Normal tasks are centered + * around 0, value goes from -16 to +15. + */ +int task_prio(task_t *p) +{ + return p->prio - 100; +} + +int task_nice(task_t *p) +{ + return TASK_NICE(p); +} + +int idle_cpu(int cpu) +{ + return cpu_curr(cpu) == cpu_rq(cpu)->idle; +} + static inline task_t *find_process_by_pid(pid_t pid) { return pid ? find_task_by_pid(pid) : current; @@ -1069,9 +1190,9 @@ p->policy = policy; p->rt_priority = lp.sched_priority; if (rt_task(p)) - p->prio = 99-p->rt_priority; + p->prio = 99 - p->rt_priority; else - p->prio = NICE_TO_PRIO(p->__nice); + p->prio = p->static_prio; if (array) activate_task(p, task_rq(p)); @@ -1186,7 +1307,6 @@ return 0; } - asmlinkage long sys_sched_get_priority_max(int policy) { int ret = -EINVAL; @@ -1232,7 +1352,7 @@ p = find_process_by_pid(pid); if (p) jiffies_to_timespec(p->policy & SCHED_FIFO ? - 0 : NICE_TO_TIMESLICE(p->__nice), &t); + 0 : TASK_TIMESLICE(p), &t); read_unlock(&tasklist_lock); if (p) retval = copy_to_user(interval, &t, sizeof(t)) ? -EFAULT : 0; diff -Nru a/kernel/signal.c b/kernel/signal.c --- a/kernel/signal.c Tue Feb 19 18:08:59 2002 +++ b/kernel/signal.c Tue Feb 19 18:08:59 2002 @@ -153,7 +153,7 @@ struct task_struct *t; sigaddset(¤t->pending.signal, sig); - recalc_sigpending(current); + recalc_sigpending(); current->flags |= PF_SIGNALED; /* Propagate the signal to all the tasks in @@ -202,7 +202,7 @@ spin_lock_irqsave(¤t->sigmask_lock, flags); current->notifier = NULL; current->notifier_data = NULL; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } @@ -288,7 +288,7 @@ /* XXX: Once POSIX.1b timers are in, if si_code == SI_TIMER, we need to xchg out the timer overrun values. */ } - recalc_sigpending(current); + recalc_sigpending(); #if DEBUG_SIG printk(" %d -> %d\n", signal_pending(current), sig); @@ -598,7 +598,7 @@ if (t->sig->action[sig-1].sa.sa_handler == SIG_IGN) t->sig->action[sig-1].sa.sa_handler = SIG_DFL; sigdelset(&t->blocked, sig); - recalc_sigpending(t); + recalc_sigpending_tsk(t); spin_unlock_irqrestore(&t->sigmask_lock, flags); return send_sig_info(sig, info, t); @@ -835,7 +835,6 @@ EXPORT_SYMBOL(kill_sl); EXPORT_SYMBOL(kill_sl_info); EXPORT_SYMBOL(notify_parent); -EXPORT_SYMBOL(recalc_sigpending); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); EXPORT_SYMBOL(block_all_signals); @@ -887,7 +886,7 @@ } current->blocked = new_set; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (error) goto out; @@ -979,7 +978,7 @@ * be awakened when they arrive. */ sigset_t oldblocked = current->blocked; sigandsets(¤t->blocked, ¤t->blocked, &these); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); current->state = TASK_INTERRUPTIBLE; @@ -988,7 +987,7 @@ spin_lock_irq(¤t->sigmask_lock); sig = dequeue_signal(&these, &info); current->blocked = oldblocked; - recalc_sigpending(current); + recalc_sigpending(); } } spin_unlock_irq(¤t->sigmask_lock); @@ -1114,7 +1113,7 @@ sig == SIGWINCH))) { spin_lock_irq(¤t->sigmask_lock); if (rm_sig_from_queue(sig, current)) - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); } } @@ -1227,7 +1226,7 @@ break; } - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); if (error) goto out; @@ -1295,7 +1294,7 @@ siginitset(¤t->blocked, newmask & ~(sigmask(SIGKILL)| sigmask(SIGSTOP))); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); return old; diff -Nru a/kernel/sys.c b/kernel/sys.c --- a/kernel/sys.c Tue Feb 19 18:08:57 2002 +++ b/kernel/sys.c Tue Feb 19 18:08:57 2002 @@ -221,7 +221,7 @@ } if (error == -ESRCH) error = 0; - if (niceval < p->__nice && !capable(CAP_SYS_NICE)) + if (niceval < task_nice(p) && !capable(CAP_SYS_NICE)) error = -EACCES; else set_user_nice(p, niceval); @@ -250,7 +250,7 @@ long niceval; if (!proc_sel(p, which, who)) continue; - niceval = 20 - p->__nice; + niceval = 20 - task_nice(p); if (niceval > retval) retval = niceval; } diff -Nru a/kernel/sysctl.c b/kernel/sysctl.c --- a/kernel/sysctl.c Tue Feb 19 18:08:57 2002 +++ b/kernel/sysctl.c Tue Feb 19 18:08:57 2002 @@ -97,8 +97,6 @@ extern int acct_parm[]; #endif -extern int pgt_cache_water[]; - static int parse_table(int *, int, void *, size_t *, void *, size_t, ctl_table *, void **); static int proc_doutsstring(ctl_table *table, int write, struct file *filp, @@ -268,8 +266,6 @@ sizeof(sysctl_overcommit_memory), 0644, NULL, &proc_dointvec}, {VM_PAGERDAEMON, "kswapd", &pager_daemon, sizeof(pager_daemon_t), 0644, NULL, &proc_dointvec}, - {VM_PGT_CACHE, "pagetable_cache", - &pgt_cache_water, 2*sizeof(int), 0644, NULL, &proc_dointvec}, {VM_PAGE_CLUSTER, "page-cluster", &page_cluster, sizeof(int), 0644, NULL, &proc_dointvec}, {0} diff -Nru a/kernel/timer.c b/kernel/timer.c --- a/kernel/timer.c Tue Feb 19 18:08:58 2002 +++ b/kernel/timer.c Tue Feb 19 18:08:58 2002 @@ -584,17 +584,7 @@ int cpu = smp_processor_id(), system = user_tick ^ 1; update_one_process(p, user_tick, system, cpu); - if (p->pid) { - if (p->__nice > 0) - kstat.per_cpu_nice[cpu] += user_tick; - else - kstat.per_cpu_user[cpu] += user_tick; - kstat.per_cpu_system[cpu] += system; - } else { - if (local_bh_count(cpu) || local_irq_count(cpu) > 1) - kstat.per_cpu_system[cpu] += system; - } - scheduler_tick(p); + scheduler_tick(user_tick, system); } /* diff -Nru a/mm/Makefile b/mm/Makefile --- a/mm/Makefile Tue Feb 19 18:08:57 2002 +++ b/mm/Makefile Tue Feb 19 18:08:57 2002 @@ -9,7 +9,7 @@ O_TARGET := mm.o -export-objs := shmem.o filemap.o mempool.o +export-objs := shmem.o filemap.o mempool.o page_alloc.o obj-y := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \ vmalloc.o slab.o bootmem.o swap.o vmscan.o page_io.o \ diff -Nru a/mm/filemap.c b/mm/filemap.c --- a/mm/filemap.c Tue Feb 19 18:08:59 2002 +++ b/mm/filemap.c Tue Feb 19 18:08:59 2002 @@ -449,41 +449,6 @@ return page; } -/* - * By the time this is called, the page is locked and - * we don't have to worry about any races any more. - * - * Start the IO.. - */ -static int writeout_one_page(struct page *page) -{ - struct buffer_head *bh, *head = page->buffers; - - bh = head; - do { - if (buffer_locked(bh) || !buffer_dirty(bh) || !buffer_uptodate(bh)) - continue; - - bh->b_flushtime = jiffies; - ll_rw_block(WRITE, 1, &bh); - } while ((bh = bh->b_this_page) != head); - return 0; -} - -int waitfor_one_page(struct page *page) -{ - int error = 0; - struct buffer_head *bh, *head = page->buffers; - - bh = head; - do { - wait_on_buffer(bh); - if (buffer_req(bh) && !buffer_uptodate(bh)) - error = -EIO; - } while ((bh = bh->b_this_page) != head); - return error; -} - static int do_buffer_fdatasync(struct list_head *head, unsigned long start, unsigned long end, int (*fn)(struct page *)) { struct list_head *curr; @@ -577,8 +542,9 @@ * @mapping: address space structure to write * */ -void filemap_fdatasync(struct address_space * mapping) +int filemap_fdatasync(struct address_space * mapping) { + int ret = 0; int (*writepage)(struct page *) = mapping->a_ops->writepage; spin_lock(&pagecache_lock); @@ -598,8 +564,11 @@ lock_page(page); if (PageDirty(page)) { + int err; ClearPageDirty(page); - writepage(page); + err = writepage(page); + if (err && !ret) + ret = err; } else UnlockPage(page); @@ -607,6 +576,7 @@ spin_lock(&pagecache_lock); } spin_unlock(&pagecache_lock); + return ret; } /** @@ -616,8 +586,10 @@ * @mapping: address space structure to wait for * */ -void filemap_fdatawait(struct address_space * mapping) +int filemap_fdatawait(struct address_space * mapping) { + int ret = 0; + spin_lock(&pagecache_lock); while (!list_empty(&mapping->locked_pages)) { @@ -633,11 +605,14 @@ spin_unlock(&pagecache_lock); ___wait_on_page(page); + if (PageError(page)) + ret = -EIO; page_cache_release(page); spin_lock(&pagecache_lock); } spin_unlock(&pagecache_lock); + return ret; } /* @@ -765,6 +740,67 @@ return 0; } +/* + * Knuth recommends primes in approximately golden ratio to the maximum + * integer representable by a machine word for multiplicative hashing. + * Chuck Lever verified the effectiveness of this technique: + * http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf + * + * These primes are chosen to be bit-sparse, that is operations on + * them can use shifts and additions instead of multiplications for + * machines where multiplications are slow. + */ +#if BITS_PER_LONG == 32 +/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ +#define GOLDEN_RATIO_PRIME 0x9e370001UL +#elif BITS_PER_LONG == 64 +/* 2^63 + 2^61 - 2^57 + 2^54 - 2^51 - 2^18 + 1 */ +#define GOLDEN_RATIO_PRIME 0x9e37fffffffc0001UL +#else +#error Define GOLDEN_RATIO_PRIME for your wordsize. +#endif + +/* + * In order to wait for pages to become available there must be + * waitqueues associated with pages. By using a hash table of + * waitqueues where the bucket discipline is to maintain all + * waiters on the same queue and wake all when any of the pages + * become available, and for the woken contexts to check to be + * sure the appropriate page became available, this saves space + * at a cost of "thundering herd" phenomena during rare hash + * collisions. + */ +static inline wait_queue_head_t *page_waitqueue(struct page *page) +{ + const zone_t *zone = page_zone(page); + wait_queue_head_t *wait = zone->wait_table; + unsigned long hash = (unsigned long)page; + +#if BITS_PER_LONG == 64 + /* Sigh, gcc can't optimise this alone like it does for 32 bits. */ + unsigned long n = hash; + n <<= 18; + hash -= n; + n <<= 33; + hash -= n; + n <<= 3; + hash += n; + n <<= 3; + hash -= n; + n <<= 4; + hash += n; + n <<= 2; + hash += n; +#else + /* On some cpus multiply is faster, on others gcc will do shifts */ + hash *= GOLDEN_RATIO_PRIME; +#endif + + hash >>= zone->wait_table_shift; + + return &wait[hash]; +} + /* * Wait for a page to get unlocked. * @@ -774,10 +810,11 @@ */ void ___wait_on_page(struct page *page) { + wait_queue_head_t *waitqueue = page_waitqueue(page); struct task_struct *tsk = current; DECLARE_WAITQUEUE(wait, tsk); - add_wait_queue(&page->wait, &wait); + add_wait_queue(waitqueue, &wait); do { set_task_state(tsk, TASK_UNINTERRUPTIBLE); if (!PageLocked(page)) @@ -785,19 +822,23 @@ sync_page(page); schedule(); } while (PageLocked(page)); - tsk->state = TASK_RUNNING; - remove_wait_queue(&page->wait, &wait); + __set_task_state(tsk, TASK_RUNNING); + remove_wait_queue(waitqueue, &wait); } +/* + * Unlock the page and wake up sleepers in ___wait_on_page. + */ void unlock_page(struct page *page) { + wait_queue_head_t *waitqueue = page_waitqueue(page); clear_bit(PG_launder, &(page)->flags); smp_mb__before_clear_bit(); if (!test_and_clear_bit(PG_locked, &(page)->flags)) BUG(); smp_mb__after_clear_bit(); - if (waitqueue_active(&(page)->wait)) - wake_up(&(page)->wait); + if (waitqueue_active(waitqueue)) + wake_up_all(waitqueue); } /* @@ -806,10 +847,11 @@ */ static void __lock_page(struct page *page) { + wait_queue_head_t *waitqueue = page_waitqueue(page); struct task_struct *tsk = current; DECLARE_WAITQUEUE(wait, tsk); - add_wait_queue_exclusive(&page->wait, &wait); + add_wait_queue_exclusive(waitqueue, &wait); for (;;) { set_task_state(tsk, TASK_UNINTERRUPTIBLE); if (PageLocked(page)) { @@ -819,10 +861,15 @@ if (!TryLockPage(page)) break; } - tsk->state = TASK_RUNNING; - remove_wait_queue(&page->wait, &wait); + __set_task_state(tsk, TASK_RUNNING); + remove_wait_queue(waitqueue, &wait); } - + +void wake_up_page(struct page *page) +{ + wake_up(page_waitqueue(page)); +} +EXPORT_SYMBOL(wake_up_page); /* * Get an exclusive lock on the page, optimistically @@ -1120,7 +1167,7 @@ * * Asynchronous read-ahead risks: * ------------------------------ - * In order to maximize overlapping, we must start some asynchronous read + * In order to maximize overlapping, we must start some asynchronous read * request from the device, as soon as possible. * We must be very careful about: * - The number of effective pending IO read requests. @@ -1131,13 +1178,6 @@ * 64k if defined (4K page size assumed). */ -static inline int get_max_readahead(struct inode * inode) -{ - if (kdev_none(inode->i_dev) || !max_readahead[major(inode->i_dev)]) - return MAX_READAHEAD; - return max_readahead[major(inode->i_dev)][minor(inode->i_dev)]; -} - static void generic_file_readahead(int reada_ok, struct file * filp, struct inode * inode, struct page * page) @@ -1146,7 +1186,6 @@ unsigned long index = page->index; unsigned long max_ahead, ahead; unsigned long raend; - int max_readahead = get_max_readahead(inode); end_index = inode->i_size >> PAGE_CACHE_SHIFT; @@ -1231,8 +1270,8 @@ filp->f_ramax += filp->f_ramax; - if (filp->f_ramax > max_readahead) - filp->f_ramax = max_readahead; + if (filp->f_ramax > MAX_READAHEAD) + filp->f_ramax = MAX_READAHEAD; #ifdef PROFILE_READAHEAD profile_readahead((reada_ok == 2), filp); @@ -1278,7 +1317,6 @@ struct page *cached_page; int reada_ok; int error; - int max_readahead = get_max_readahead(inode); cached_page = NULL; index = *ppos >> PAGE_CACHE_SHIFT; @@ -1318,9 +1356,9 @@ filp->f_ramax = needed; if (reada_ok && filp->f_ramax < MIN_READAHEAD) - filp->f_ramax = MIN_READAHEAD; - if (filp->f_ramax > max_readahead) - filp->f_ramax = max_readahead; + filp->f_ramax = MIN_READAHEAD; + if (filp->f_ramax > MAX_READAHEAD) + filp->f_ramax = MAX_READAHEAD; } for (;;) { @@ -1514,12 +1552,14 @@ goto out_free; /* - * Flush to disk exlusively the _data_, metadata must remains + * Flush to disk exclusively the _data_, metadata must remain * completly asynchronous or performance will go to /dev/null. */ - filemap_fdatasync(mapping); - retval = fsync_inode_data_buffers(inode); - filemap_fdatawait(mapping); + retval = filemap_fdatasync(mapping); + if (retval == 0) + retval = fsync_inode_data_buffers(inode); + if (retval == 0) + retval = filemap_fdatawait(mapping); if (retval < 0) goto out_free; @@ -1808,8 +1848,7 @@ { unsigned long ra_window; - ra_window = get_max_readahead(vma->vm_file->f_dentry->d_inode); - ra_window = CLUSTER_OFFSET(ra_window + CLUSTER_PAGES - 1); + ra_window = CLUSTER_OFFSET(MAX_READAHEAD + CLUSTER_PAGES - 1); /* vm_raend is zero if we haven't read ahead in this area yet. */ if (vma->vm_raend == 0) @@ -2007,7 +2046,7 @@ /* Called with mm->page_table_lock held to protect against other * threads/the swapper from ripping pte's out from under us. */ -static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma, +static inline int filemap_sync_pte(pte_t *ptep, pmd_t *pmdp, struct vm_area_struct *vma, unsigned long address, unsigned int flags) { pte_t pte = *ptep; @@ -2023,11 +2062,10 @@ } static inline int filemap_sync_pte_range(pmd_t * pmd, - unsigned long address, unsigned long size, - struct vm_area_struct *vma, unsigned long offset, unsigned int flags) + unsigned long address, unsigned long end, + struct vm_area_struct *vma, unsigned int flags) { - pte_t * pte; - unsigned long end; + pte_t *pte; int error; if (pmd_none(*pmd)) @@ -2037,27 +2075,26 @@ pmd_clear(pmd); return 0; } - pte = pte_offset(pmd, address); - offset += address & PMD_MASK; - address &= ~PMD_MASK; - end = address + size; - if (end > PMD_SIZE) - end = PMD_SIZE; + pte = pte_offset_map(pmd, address); + if ((address & PMD_MASK) != (end & PMD_MASK)) + end = (address & PMD_MASK) + PMD_SIZE; error = 0; do { - error |= filemap_sync_pte(pte, vma, address + offset, flags); + error |= filemap_sync_pte(pte, pmd, vma, address, flags); address += PAGE_SIZE; pte++; } while (address && (address < end)); + + pte_unmap(pte - 1); + return error; } static inline int filemap_sync_pmd_range(pgd_t * pgd, - unsigned long address, unsigned long size, + unsigned long address, unsigned long end, struct vm_area_struct *vma, unsigned int flags) { pmd_t * pmd; - unsigned long offset, end; int error; if (pgd_none(*pgd)) @@ -2068,14 +2105,11 @@ return 0; } pmd = pmd_offset(pgd, address); - offset = address & PGDIR_MASK; - address &= ~PGDIR_MASK; - end = address + size; - if (end > PGDIR_SIZE) - end = PGDIR_SIZE; + if ((address & PGDIR_MASK) != (end & PGDIR_MASK)) + end = (address & PGDIR_MASK) + PGDIR_SIZE; error = 0; do { - error |= filemap_sync_pte_range(pmd, address, end - address, vma, offset, flags); + error |= filemap_sync_pte_range(pmd, address, end, vma, flags); address = (address + PMD_SIZE) & PMD_MASK; pmd++; } while (address && (address < end)); @@ -2095,11 +2129,11 @@ spin_lock(&vma->vm_mm->page_table_lock); dir = pgd_offset(vma->vm_mm, address); - flush_cache_range(vma, end - size, end); + flush_cache_range(vma, address, end); if (address >= end) BUG(); do { - error |= filemap_sync_pmd_range(dir, address, end - address, vma, flags); + error |= filemap_sync_pmd_range(dir, address, end, vma, flags); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; } while (address && (address < end)); @@ -2136,26 +2170,45 @@ * The msync() system call. */ +/* + * MS_SYNC syncs the entire file - including mappings. + * + * MS_ASYNC initiates writeout of just the dirty mapped data. + * This provides no guarantee of file integrity - things like indirect + * blocks may not have started writeout. MS_ASYNC is primarily useful + * where the application knows that it has finished with the data and + * wishes to intelligently schedule its own I/O traffic. + */ static int msync_interval(struct vm_area_struct * vma, unsigned long start, unsigned long end, int flags) { + int ret = 0; struct file * file = vma->vm_file; + if (file && (vma->vm_flags & VM_SHARED)) { - int error; - error = filemap_sync(vma, start, end-start, flags); + ret = filemap_sync(vma, start, end-start, flags); - if (!error && (flags & MS_SYNC)) { + if (!ret && (flags & (MS_SYNC|MS_ASYNC))) { struct inode * inode = file->f_dentry->d_inode; + down(&inode->i_sem); - filemap_fdatasync(inode->i_mapping); - if (file->f_op && file->f_op->fsync) - error = file->f_op->fsync(file, file->f_dentry, 1); - filemap_fdatawait(inode->i_mapping); + ret = filemap_fdatasync(inode->i_mapping); + if (flags & MS_SYNC) { + int err; + + if (file->f_op && file->f_op->fsync) { + err = file->f_op->fsync(file, file->f_dentry, 1); + if (err && !ret) + ret = err; + } + err = filemap_fdatawait(inode->i_mapping); + if (err && !ret) + ret = err; + } up(&inode->i_sem); } - return error; } - return 0; + return ret; } asmlinkage long sys_msync(unsigned long start, size_t len, int flags) @@ -2999,7 +3052,7 @@ kaddr = kmap(page); status = mapping->a_ops->prepare_write(file, page, offset, offset+bytes); if (status) - goto unlock; + goto sync_failure; page_fault = __copy_from_user(kaddr+offset, buf, bytes); flush_dcache_page(page); status = mapping->a_ops->commit_write(file, page, offset, offset+bytes); @@ -3024,6 +3077,7 @@ if (status < 0) break; } while (count); +done: *ppos = pos; if (cached_page) @@ -3045,6 +3099,18 @@ fail_write: status = -EFAULT; goto unlock; + +sync_failure: + /* + * If blocksize < pagesize, prepare_write() may have instantiated a + * few blocks outside i_size. Trim these off again. + */ + kunmap(page); + UnlockPage(page); + page_cache_release(page); + if (pos + bytes > inode->i_size) + vmtruncate(inode, inode->i_size); + goto done; o_direct: written = generic_file_direct_IO(WRITE, file, (char *) buf, count, pos); diff -Nru a/mm/highmem.c b/mm/highmem.c --- a/mm/highmem.c Tue Feb 19 18:08:57 2002 +++ b/mm/highmem.c Tue Feb 19 18:08:57 2002 @@ -20,6 +20,7 @@ #include #include #include +#include static mempool_t *page_pool, *isa_page_pool; @@ -288,11 +289,11 @@ } } -static inline int bounce_end_io (struct bio *bio, int nr_sectors, mempool_t *pool) +static inline void bounce_end_io(struct bio *bio, mempool_t *pool) { struct bio *bio_orig = bio->bi_private; struct bio_vec *bvec, *org_vec; - int ret, i; + int i; if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) goto out_eio; @@ -311,41 +312,38 @@ } out_eio: - ret = bio_orig->bi_end_io(bio_orig, nr_sectors); - + bio_orig->bi_end_io(bio_orig); bio_put(bio); - return ret; } -static int bounce_end_io_write(struct bio *bio, int nr_sectors) +static void bounce_end_io_write(struct bio *bio) { - return bounce_end_io(bio, nr_sectors, page_pool); + bounce_end_io(bio, page_pool); } -static int bounce_end_io_write_isa(struct bio *bio, int nr_sectors) +static void bounce_end_io_write_isa(struct bio *bio) { - return bounce_end_io(bio, nr_sectors, isa_page_pool); + bounce_end_io(bio, isa_page_pool); } -static inline int __bounce_end_io_read(struct bio *bio, int nr_sectors, - mempool_t *pool) +static inline void __bounce_end_io_read(struct bio *bio, mempool_t *pool) { struct bio *bio_orig = bio->bi_private; if (test_bit(BIO_UPTODATE, &bio->bi_flags)) copy_to_high_bio_irq(bio_orig, bio); - return bounce_end_io(bio, nr_sectors, pool); + bounce_end_io(bio, pool); } -static int bounce_end_io_read(struct bio *bio, int nr_sectors) +static void bounce_end_io_read(struct bio *bio) { - return __bounce_end_io_read(bio, nr_sectors, page_pool); + __bounce_end_io_read(bio, page_pool); } -static int bounce_end_io_read_isa(struct bio *bio, int nr_sectors) +static void bounce_end_io_read_isa(struct bio *bio) { - return __bounce_end_io_read(bio, nr_sectors, isa_page_pool); + return __bounce_end_io_read(bio, isa_page_pool); } void create_bounce(unsigned long pfn, int gfp, struct bio **bio_orig) @@ -381,7 +379,7 @@ /* * is destination page below bounce pfn? */ - if ((page - page->zone->zone_mem_map) + (page->zone->zone_start_paddr >> PAGE_SHIFT) < pfn) + if ((page - page_zone(page)->zone_mem_map) + (page_zone(page)->zone_start_paddr >> PAGE_SHIFT) < pfn) continue; /* @@ -448,3 +446,19 @@ bio->bi_private = *bio_orig; *bio_orig = bio; } + +#if CONFIG_DEBUG_HIGHMEM +void check_highmem_ptes(void) +{ + int idx, type; + + for (type = 0; type < KM_TYPE_NR; type++) { + idx = type + KM_TYPE_NR*smp_processor_id(); + if (!pte_none(*(kmap_pte-idx))) { + printk("scheduling with KM_TYPE %d held!\n", type); + BUG(); + } + } +} +#endif + diff -Nru a/mm/memory.c b/mm/memory.c --- a/mm/memory.c Tue Feb 19 18:08:59 2002 +++ b/mm/memory.c Tue Feb 19 18:08:59 2002 @@ -90,7 +90,7 @@ */ static inline void free_one_pmd(pmd_t * dir) { - pte_t * pte; + struct page *pte; if (pmd_none(*dir)) return; @@ -99,7 +99,7 @@ pmd_clear(dir); return; } - pte = pte_offset(dir, 0); + pte = pmd_page(*dir); pmd_clear(dir); pte_free(pte); } @@ -125,18 +125,6 @@ pmd_free(pmd); } -/* Low and high watermarks for page table cache. - The system should try to have pgt_water[0] <= cache elements <= pgt_water[1] - */ -int pgt_cache_water[2] = { 25, 50 }; - -/* Returns the number of pages freed */ -int check_pgt_cache(void) -{ - return do_check_pgt_cache(pgt_cache_water[0], pgt_cache_water[1]); -} - - /* * This function clears all user-level page tables of a process - this * is needed by execve(), so that old pages aren't in the way. @@ -152,11 +140,59 @@ page_dir++; } while (--nr); spin_unlock(&mm->page_table_lock); +} - /* keep the page table cache within bounds */ - check_pgt_cache(); +pte_t * pte_alloc_map(struct mm_struct *mm, pmd_t *pmd, unsigned long address) +{ + if (!pmd_present(*pmd)) { + struct page *new; + + spin_unlock(&mm->page_table_lock); + new = pte_alloc_one(mm, address); + spin_lock(&mm->page_table_lock); + if (!new) + return NULL; + + /* + * Because we dropped the lock, we should re-check the + * entry, as somebody else could have populated it.. + */ + if (pmd_present(*pmd)) { + pte_free(new); + goto out; + } + pmd_populate(mm, pmd, new); + } +out: + if (pmd_present(*pmd)) + return pte_offset_map(pmd, address); + return NULL; } +pte_t * pte_alloc_kernel(struct mm_struct *mm, pmd_t *pmd, unsigned long address) +{ + if (!pmd_present(*pmd)) { + pte_t *new; + + spin_unlock(&mm->page_table_lock); + new = pte_alloc_one_kernel(mm, address); + spin_lock(&mm->page_table_lock); + if (!new) + return NULL; + + /* + * Because we dropped the lock, we should re-check the + * entry, as somebody else could have populated it.. + */ + if (pmd_present(*pmd)) { + pte_free_kernel(new); + goto out; + } + pmd_populate_kernel(mm, pmd, new); + } +out: + return pte_offset_kernel(pmd, address); +} #define PTE_TABLE_MASK ((PTRS_PER_PTE-1) * sizeof(pte_t)) #define PMD_TABLE_MASK ((PTRS_PER_PMD-1) * sizeof(pmd_t)) @@ -169,7 +205,7 @@ * variable count and make things faster. -jj * * dst->page_table_lock is held on entry and exit, - * but may be dropped within pmd_alloc() and pte_alloc(). + * but may be dropped within pmd_alloc() and pte_alloc_map(). */ int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma) @@ -177,7 +213,7 @@ pgd_t * src_pgd, * dst_pgd; unsigned long address = vma->vm_start; unsigned long end = vma->vm_end; - unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE; + unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE; src_pgd = pgd_offset(src, address)-1; dst_pgd = pgd_offset(dst, address)-1; @@ -221,12 +257,11 @@ goto cont_copy_pmd_range; } - src_pte = pte_offset(src_pmd, address); - dst_pte = pte_alloc(dst, dst_pmd, address); + dst_pte = pte_alloc_map(dst, dst_pmd, address); if (!dst_pte) goto nomem; - spin_lock(&src->page_table_lock); + src_pte = pte_offset_map_nested(src_pmd, address); do { pte_t pte = *src_pte; struct page *ptepage; @@ -245,7 +280,7 @@ goto cont_copy_pte_range; /* If it's a COW mapping, write protect it both in the parent and the child */ - if (cow) { + if (cow && pte_write(pte)) { ptep_set_wrprotect(src_pte); pte = *src_pte; } @@ -259,11 +294,16 @@ cont_copy_pte_range: set_pte(dst_pte, pte); cont_copy_pte_range_noset: address += PAGE_SIZE; - if (address >= end) + if (address >= end) { + pte_unmap_nested(src_pte); + pte_unmap(dst_pte); goto out_unlock; + } src_pte++; dst_pte++; } while ((unsigned long)src_pte & PTE_TABLE_MASK); + pte_unmap_nested(src_pte-1); + pte_unmap(dst_pte-1); spin_unlock(&src->page_table_lock); cont_copy_pmd_range: src_pmd++; @@ -292,7 +332,7 @@ static inline int zap_pte_range(mmu_gather_t *tlb, pmd_t * pmd, unsigned long address, unsigned long size) { unsigned long offset; - pte_t * ptep; + pte_t *ptep; int freed = 0; if (pmd_none(*pmd)) @@ -302,7 +342,7 @@ pmd_clear(pmd); return 0; } - ptep = pte_offset(pmd, address); + ptep = pte_offset_map(pmd, address); offset = address & ~PMD_MASK; if (offset + size > PMD_SIZE) size = PMD_SIZE - offset; @@ -322,6 +362,7 @@ pte_clear(ptep); } } + pte_unmap(ptep-1); return freed; } @@ -415,11 +456,16 @@ if (pmd_none(*pmd) || pmd_bad(*pmd)) goto out; - ptep = pte_offset(pmd, address); - if (!ptep) + preempt_disable(); + ptep = pte_offset_map(pmd, address); + if (!ptep) { + preempt_enable(); goto out; + } pte = *ptep; + pte_unmap(ptep); + preempt_enable(); if (pte_present(pte)) { if (!write || (pte_write(pte) && pte_dirty(pte))) @@ -446,20 +492,25 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas) { - int i = 0; + int i; + unsigned int flags; + + /* + * Require read or write permissions. + * If 'force' is set, we only require the "MAY" flags. + */ + flags = write ? (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD); + flags &= force ? (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE); + i = 0; + do { struct vm_area_struct * vma; vma = find_extend_vma(mm, start); - if ( !vma || - (!force && - ((write && (!(vma->vm_flags & VM_WRITE))) || - (!write && (!(vma->vm_flags & VM_READ))) ) )) { - if (i) return i; - return -EFAULT; - } + if ( !vma || !(flags & vma->vm_flags) ) + return i ? : -EFAULT; spin_lock(&mm->page_table_lock); do { @@ -748,10 +799,11 @@ if (end > PGDIR_SIZE) end = PGDIR_SIZE; do { - pte_t * pte = pte_alloc(mm, pmd, address); + pte_t * pte = pte_alloc_map(mm, pmd, address); if (!pte) return -ENOMEM; zeromap_pte_range(pte, address, end - address, prot); + pte_unmap(pte); address = (address + PMD_SIZE) & PMD_MASK; pmd++; } while (address && (address < end)); @@ -828,10 +880,11 @@ end = PGDIR_SIZE; phys_addr -= address; do { - pte_t * pte = pte_alloc(mm, pmd, address); + pte_t * pte = pte_alloc_map(mm, pmd, address); if (!pte) return -ENOMEM; remap_pte_range(pte, address, end - address, address + phys_addr, prot); + pte_unmap(pte); address = (address + PMD_SIZE) & PMD_MASK; pmd++; } while (address && (address < end)); @@ -917,7 +970,7 @@ * with the page_table_lock released. */ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct * vma, - unsigned long address, pte_t *page_table, pte_t pte) + unsigned long address, pte_t *page_table, pmd_t *pmd, pte_t pte) { struct page *old_page, *new_page; @@ -931,10 +984,12 @@ if (reuse) { flush_cache_page(vma, address); establish_pte(vma, address, page_table, pte_mkyoung(pte_mkdirty(pte_mkwrite(pte)))); + pte_unmap(page_table); spin_unlock(&mm->page_table_lock); return 1; /* Minor fault */ } } + pte_unmap(page_table); /* * Ok, we need to copy. Oh, well.. @@ -951,6 +1006,7 @@ * Re-check the pte - we dropped the lock */ spin_lock(&mm->page_table_lock); + page_table = pte_offset_map(pmd, address); if (pte_same(*page_table, pte)) { if (PageReserved(old_page)) ++mm->rss; @@ -960,12 +1016,14 @@ /* Free the old page.. */ new_page = old_page; } + pte_unmap(page_table); spin_unlock(&mm->page_table_lock); page_cache_release(new_page); page_cache_release(old_page); return 1; /* Minor fault */ bad_wp_page: + pte_unmap(page_table); spin_unlock(&mm->page_table_lock); printk("do_wp_page: bogus page at address %08lx (page 0x%lx)\n",address,(unsigned long)old_page); return -1; @@ -1048,11 +1106,8 @@ inode->i_size = offset; out_truncate: - if (inode->i_op && inode->i_op->truncate) { - lock_kernel(); + if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); - unlock_kernel(); - } out: return 0; } @@ -1089,13 +1144,14 @@ */ static int do_swap_page(struct mm_struct * mm, struct vm_area_struct * vma, unsigned long address, - pte_t * page_table, pte_t orig_pte, int write_access) + pte_t *page_table, pmd_t *pmd, pte_t orig_pte, int write_access) { struct page *page; swp_entry_t entry = pte_to_swp_entry(orig_pte); pte_t pte; int ret = 1; + pte_unmap(page_table); spin_unlock(&mm->page_table_lock); page = lookup_swap_cache(entry); if (!page) { @@ -1108,7 +1164,9 @@ */ int retval; spin_lock(&mm->page_table_lock); + page_table = pte_offset_map(pmd, address); retval = pte_same(*page_table, orig_pte) ? -1 : 1; + pte_unmap(page_table); spin_unlock(&mm->page_table_lock); return retval; } @@ -1124,7 +1182,9 @@ * released the page table lock. */ spin_lock(&mm->page_table_lock); + page_table = pte_offset_map(pmd, address); if (!pte_same(*page_table, orig_pte)) { + pte_unmap(page_table); spin_unlock(&mm->page_table_lock); unlock_page(page); page_cache_release(page); @@ -1149,6 +1209,7 @@ /* No need to invalidate - it was non-present before */ update_mmu_cache(vma, address, pte); + pte_unmap(page_table); spin_unlock(&mm->page_table_lock); return ret; } @@ -1158,7 +1219,7 @@ * spinlock held to protect against concurrent faults in * multithreaded programs. */ -static int do_anonymous_page(struct mm_struct * mm, struct vm_area_struct * vma, pte_t *page_table, int write_access, unsigned long addr) +static int do_anonymous_page(struct mm_struct * mm, struct vm_area_struct * vma, pte_t *page_table, pmd_t *pmd, int write_access, unsigned long addr) { pte_t entry; @@ -1170,6 +1231,7 @@ struct page *page; /* Allocate our own private page. */ + pte_unmap(page_table); spin_unlock(&mm->page_table_lock); page = alloc_page(GFP_HIGHUSER); @@ -1178,7 +1240,10 @@ clear_user_highpage(page, addr); spin_lock(&mm->page_table_lock); + page_table = pte_offset_map(pmd, addr); + if (!pte_none(*page_table)) { + pte_unmap(page_table); page_cache_release(page); spin_unlock(&mm->page_table_lock); return 1; @@ -1190,6 +1255,7 @@ } set_pte(page_table, entry); + pte_unmap(page_table); /* No need to invalidate - it was non-present before */ update_mmu_cache(vma, addr, entry); @@ -1213,13 +1279,14 @@ * spinlock held. Exit with the spinlock released. */ static int do_no_page(struct mm_struct * mm, struct vm_area_struct * vma, - unsigned long address, int write_access, pte_t *page_table) + unsigned long address, int write_access, pte_t *page_table, pmd_t *pmd) { struct page * new_page; pte_t entry; if (!vma->vm_ops || !vma->vm_ops->nopage) - return do_anonymous_page(mm, vma, page_table, write_access, address); + return do_anonymous_page(mm, vma, page_table, pmd, write_access, address); + pte_unmap(page_table); spin_unlock(&mm->page_table_lock); new_page = vma->vm_ops->nopage(vma, address & PAGE_MASK, 0); @@ -1245,6 +1312,8 @@ } spin_lock(&mm->page_table_lock); + page_table = pte_offset_map(pmd, address); + /* * This silly early PAGE_DIRTY setting removes a race * due to the bad i386 page protection. But it's valid @@ -1264,8 +1333,10 @@ if (write_access) entry = pte_mkwrite(pte_mkdirty(entry)); set_pte(page_table, entry); + pte_unmap(page_table); } else { /* One of our sibling threads was faster, back out. */ + pte_unmap(page_table); page_cache_release(new_page); spin_unlock(&mm->page_table_lock); return 1; @@ -1300,7 +1371,7 @@ */ static inline int handle_pte_fault(struct mm_struct *mm, struct vm_area_struct * vma, unsigned long address, - int write_access, pte_t * pte) + int write_access, pte_t *pte, pmd_t *pmd) { pte_t entry; @@ -1312,18 +1383,19 @@ * drop the lock. */ if (pte_none(entry)) - return do_no_page(mm, vma, address, write_access, pte); - return do_swap_page(mm, vma, address, pte, entry, write_access); + return do_no_page(mm, vma, address, write_access, pte, pmd); + return do_swap_page(mm, vma, address, pte, pmd, entry, write_access); } if (write_access) { if (!pte_write(entry)) - return do_wp_page(mm, vma, address, pte, entry); + return do_wp_page(mm, vma, address, pte, pmd, entry); entry = pte_mkdirty(entry); } entry = pte_mkyoung(entry); establish_pte(vma, address, pte, entry); + pte_unmap(pte); spin_unlock(&mm->page_table_lock); return 1; } @@ -1348,9 +1420,9 @@ pmd = pmd_alloc(mm, pgd, address); if (pmd) { - pte_t * pte = pte_alloc(mm, pmd, address); + pte_t * pte = pte_alloc_map(mm, pmd, address); if (pte) - return handle_pte_fault(mm, vma, address, write_access, pte); + return handle_pte_fault(mm, vma, address, write_access, pte, pmd); } spin_unlock(&mm->page_table_lock); return -1; @@ -1369,64 +1441,25 @@ { pmd_t *new; - /* "fast" allocation can happen without dropping the lock.. */ - new = pmd_alloc_one_fast(mm, address); - if (!new) { - spin_unlock(&mm->page_table_lock); - new = pmd_alloc_one(mm, address); - spin_lock(&mm->page_table_lock); - if (!new) - return NULL; + spin_unlock(&mm->page_table_lock); + new = pmd_alloc_one(mm, address); + spin_lock(&mm->page_table_lock); + if (!new) + return NULL; - /* - * Because we dropped the lock, we should re-check the - * entry, as somebody else could have populated it.. - */ - if (!pgd_none(*pgd)) { - pmd_free(new); - goto out; - } + /* + * Because we dropped the lock, we should re-check the + * entry, as somebody else could have populated it.. + */ + if (pgd_present(*pgd)) { + pmd_free(new); + goto out; } pgd_populate(mm, pgd, new); out: return pmd_offset(pgd, address); } -/* - * Allocate the page table directory. - * - * We've already handled the fast-path in-line, and we own the - * page table lock. - */ -pte_t *pte_alloc(struct mm_struct *mm, pmd_t *pmd, unsigned long address) -{ - if (pmd_none(*pmd)) { - pte_t *new; - - /* "fast" allocation can happen without dropping the lock.. */ - new = pte_alloc_one_fast(mm, address); - if (!new) { - spin_unlock(&mm->page_table_lock); - new = pte_alloc_one(mm, address); - spin_lock(&mm->page_table_lock); - if (!new) - return NULL; - - /* - * Because we dropped the lock, we should re-check the - * entry, as somebody else could have populated it.. - */ - if (!pmd_none(*pmd)) { - pte_free(new); - goto out; - } - } - pmd_populate(mm, pmd, new); - } -out: - return pte_offset(pmd, address); -} - int make_pages_present(unsigned long addr, unsigned long end) { int ret, len, write; @@ -1442,4 +1475,29 @@ ret = get_user_pages(current, current->mm, addr, len, write, 0, NULL, NULL); return ret == len ? 0 : -1; +} + +/* + * Map a vmalloc()-space virtual address to the physical page. + */ +struct page * vmalloc_to_page(unsigned long addr) +{ + struct page *page = NULL; + pgd_t *pgd = pgd_offset_k(addr); + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, addr); + if (!pmd_none(*pmd)) { + preempt_disable(); + ptep = pte_offset_map(pmd, addr); + pte = *ptep; + if (pte_present(pte)) + page = pte_page(pte); + pte_unmap(ptep); + preempt_enable(); + } + } + return page; } diff -Nru a/mm/mmap.c b/mm/mmap.c --- a/mm/mmap.c Tue Feb 19 18:08:57 2002 +++ b/mm/mmap.c Tue Feb 19 18:08:57 2002 @@ -620,7 +620,7 @@ { if (flags & MAP_FIXED) { if (addr > TASK_SIZE - len) - return -EINVAL; + return -ENOMEM; if (addr & ~PAGE_MASK) return -EINVAL; return addr; @@ -1046,10 +1046,7 @@ if (!vm_enough_memory(len >> PAGE_SHIFT)) return -ENOMEM; - flags = calc_vm_flags(PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE) | mm->def_flags; - - flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; + flags = VM_DATA_DEFAULT_FLAGS | mm->def_flags; /* Can we just expand an old anonymous mapping? */ if (rb_parent && vma_merge(mm, prev, rb_parent, addr, addr + len, flags)) diff -Nru a/mm/mprotect.c b/mm/mprotect.c --- a/mm/mprotect.c Tue Feb 19 18:08:59 2002 +++ b/mm/mprotect.c Tue Feb 19 18:08:59 2002 @@ -13,6 +13,7 @@ #include #include #include +#include static inline void change_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, pgprot_t newprot) @@ -27,7 +28,7 @@ pmd_clear(pmd); return; } - pte = pte_offset(pmd, address); + pte = pte_offset_map(pmd, address); address &= ~PMD_MASK; end = address + size; if (end > PMD_SIZE) @@ -46,6 +47,7 @@ address += PAGE_SIZE; pte++; } while (address && (address < end)); + pte_unmap(pte - 1); } static inline void change_pmd_range(pgd_t * pgd, unsigned long address, diff -Nru a/mm/mremap.c b/mm/mremap.c --- a/mm/mremap.c Tue Feb 19 18:08:57 2002 +++ b/mm/mremap.c Tue Feb 19 18:08:57 2002 @@ -17,7 +17,7 @@ extern int vm_enough_memory(long pages); -static inline pte_t *get_one_pte(struct mm_struct *mm, unsigned long addr) +static inline pte_t *get_one_pte_map_nested(struct mm_struct *mm, unsigned long addr) { pgd_t * pgd; pmd_t * pmd; @@ -41,21 +41,23 @@ goto end; } - pte = pte_offset(pmd, addr); - if (pte_none(*pte)) + pte = pte_offset_map_nested(pmd, addr); + if (pte_none(*pte)) { + pte_unmap_nested(pte); pte = NULL; + } end: return pte; } -static inline pte_t *alloc_one_pte(struct mm_struct *mm, unsigned long addr) +static inline pte_t *alloc_one_pte_map(struct mm_struct *mm, unsigned long addr) { pmd_t * pmd; pte_t * pte = NULL; pmd = pmd_alloc(mm, pgd_offset(mm, addr), addr); if (pmd) - pte = pte_alloc(mm, pmd, addr); + pte = pte_alloc_map(mm, pmd, addr); return pte; } @@ -79,12 +81,16 @@ static int move_one_page(struct mm_struct *mm, unsigned long old_addr, unsigned long new_addr) { int error = 0; - pte_t * src; + pte_t *src, *dst; spin_lock(&mm->page_table_lock); - src = get_one_pte(mm, old_addr); - if (src) - error = copy_one_pte(mm, src, alloc_one_pte(mm, new_addr)); + src = get_one_pte_map_nested(mm, old_addr); + if (src) { + dst = alloc_one_pte_map(mm, new_addr); + error = copy_one_pte(mm, src, dst); + pte_unmap_nested(src); + pte_unmap(dst); + } spin_unlock(&mm->page_table_lock); return error; } diff -Nru a/mm/oom_kill.c b/mm/oom_kill.c --- a/mm/oom_kill.c Tue Feb 19 18:08:57 2002 +++ b/mm/oom_kill.c Tue Feb 19 18:08:57 2002 @@ -82,7 +82,7 @@ * Niced processes are most likely less important, so double * their badness points. */ - if (p->__nice > 0) + if (task_nice(p) > 0) points *= 2; /* @@ -146,7 +146,7 @@ * all the memory it needs. That way it should be able to * exit() and clear out its resources quickly... */ - p->time_slice = 2 * MAX_TIMESLICE; + p->time_slice = HZ; p->flags |= PF_MEMALLOC | PF_MEMDIE; /* This process has hardware access, be more careful. */ diff -Nru a/mm/page_alloc.c b/mm/page_alloc.c --- a/mm/page_alloc.c Tue Feb 19 18:08:57 2002 +++ b/mm/page_alloc.c Tue Feb 19 18:08:57 2002 @@ -1,6 +1,9 @@ /* * linux/mm/page_alloc.c * + * Manages the free list, the system allocates free pages here. + * Note that kmalloc() lives in slab.c + * * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds * Swap reorganised 29.12.95, Stephen Tweedie * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 @@ -18,6 +21,7 @@ #include #include #include +#include int nr_swap_pages; int nr_active_pages; @@ -26,6 +30,10 @@ struct list_head active_list; pg_data_t *pgdat_list; +/* Used to look up the address of the struct zone encoded in page->zone */ +zone_t *zone_table[MAX_NR_ZONES*MAX_NR_NODES]; +EXPORT_SYMBOL(zone_table); + static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" }; static int zone_balance_ratio[MAX_NR_ZONES] __initdata = { 128, 128, 128, }; static int zone_balance_min[MAX_NR_ZONES] __initdata = { 20 , 20, 20, }; @@ -54,12 +62,31 @@ /* * Temporary debugging check. */ -#define BAD_RANGE(zone,x) (((zone) != (x)->zone) || (((x)-mem_map) < (zone)->zone_start_mapnr) || (((x)-mem_map) >= (zone)->zone_start_mapnr+(zone)->size)) +#define BAD_RANGE(zone, page) \ +( \ + (((page) - mem_map) >= ((zone)->zone_start_mapnr+(zone)->size)) \ + || (((page) - mem_map) < (zone)->zone_start_mapnr) \ + || ((zone) != page_zone(page)) \ +) /* - * Buddy system. Hairy. You really aren't expected to understand this + * Freeing function for a buddy system allocator. + * + * The concept of a buddy system is to maintain direct-mapped table + * (containing bit values) for memory blocks of various "orders". + * The bottom level table contains the map for the smallest allocatable + * units of memory (here, pages), and each level above it describes + * pairs of units from the levels below, hence, "buddies". + * At a high level, all that happens here is marking the table entry + * at the bottom level available, and propagating the changes upward + * as necessary, plus some accounting needed to play nicely with other + * parts of the VM system. + * + * TODO: give references to descriptions of buddy system allocators, + * describe precisely the silly trick buddy allocators use to avoid + * storing an extra bit, utilizing entry point information. * - * Hint: -mask = 1+~mask + * -- wli */ static void FASTCALL(__free_pages_ok (struct page *page, unsigned int order)); @@ -90,7 +117,7 @@ goto local_freelist; back_local_freelist: - zone = page->zone; + zone = page_zone(page); mask = (~0UL) << order; base = zone->zone_mem_map; @@ -117,6 +144,8 @@ break; /* * Move the buddy up one level. + * This code is taking advantage of the identity: + * -mask = 1+~mask */ buddy1 = base + (page_idx ^ -mask); buddy2 = base + page_idx; @@ -255,7 +284,7 @@ entry = local_pages->next; do { tmp = list_entry(entry, struct page, list); - if (tmp->index == order && memclass(tmp->zone, classzone)) { + if (tmp->index == order && memclass(page_zone(tmp), classzone)) { list_del(entry); current->nr_local_pages--; set_page_count(tmp, 1); @@ -625,6 +654,48 @@ } } +/* + * Helper functions to size the waitqueue hash table. + * Essentially these want to choose hash table sizes sufficiently + * large so that collisions trying to wait on pages are rare. + * But in fact, the number of active page waitqueues on typical + * systems is ridiculously low, less than 200. So this is even + * conservative, even though it seems large. + * + * The constant PAGES_PER_WAITQUEUE specifies the ratio of pages to + * waitqueues, i.e. the size of the waitq table given the number of pages. + */ +#define PAGES_PER_WAITQUEUE 256 + +static inline unsigned long wait_table_size(unsigned long pages) +{ + unsigned long size = 1; + + pages /= PAGES_PER_WAITQUEUE; + + while (size < pages) + size <<= 1; + + /* + * Once we have dozens or even hundreds of threads sleeping + * on IO we've got bigger problems than wait queue collision. + * Limit the size of the wait table to a reasonable size. + */ + size = min(size, 4096UL); + + return size; +} + +/* + * This is an integer logarithm so that shifts can be used later + * to extract the more random high bits from the multiplicative + * hash function before the remainder is taken. + */ +static inline unsigned long wait_table_bits(unsigned long size) +{ + return ffz(~size); +} + #define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1)) /* @@ -637,7 +708,6 @@ unsigned long *zones_size, unsigned long zone_start_paddr, unsigned long *zholes_size, struct page *lmem_map) { - struct page *p; unsigned long i, j; unsigned long map_size; unsigned long totalpages, offset, realtotalpages; @@ -680,24 +750,13 @@ pgdat->node_start_mapnr = (lmem_map - mem_map); pgdat->nr_zones = 0; - /* - * Initially all pages are reserved - free ones are freed - * up by free_all_bootmem() once the early boot process is - * done. - */ - for (p = lmem_map; p < lmem_map + totalpages; p++) { - set_page_count(p, 0); - SetPageReserved(p); - init_waitqueue_head(&p->wait); - memlist_init(&p->list); - } - offset = lmem_map - mem_map; for (j = 0; j < MAX_NR_ZONES; j++) { zone_t *zone = pgdat->node_zones + j; unsigned long mask; unsigned long size, realsize; + zone_table[nid * MAX_NR_ZONES + j] = zone; realsize = size = zones_size[j]; if (zholes_size) realsize -= zholes_size[j]; @@ -712,6 +771,20 @@ if (!size) continue; + /* + * The per-page waitqueue mechanism uses hashed waitqueues + * per zone. + */ + zone->wait_table_size = wait_table_size(size); + zone->wait_table_shift = + BITS_PER_LONG - wait_table_bits(zone->wait_table_size); + zone->wait_table = (wait_queue_head_t *) + alloc_bootmem_node(pgdat, zone->wait_table_size + * sizeof(wait_queue_head_t)); + + for(i = 0; i < zone->wait_table_size; ++i) + init_waitqueue_head(zone->wait_table + i); + pgdat->nr_zones = j+1; mask = (realsize / zone_balance_ratio[j]); @@ -730,11 +803,19 @@ if ((zone_start_paddr >> PAGE_SHIFT) & (zone_required_alignment-1)) printk("BUG: wrong zone alignment, it will crash\n"); + /* + * Initially all pages are reserved - free ones are freed + * up by free_all_bootmem() once the early boot process is + * done. Non-atomic initialization, single-pass. + */ for (i = 0; i < size; i++) { struct page *page = mem_map + offset + i; - page->zone = zone; + set_page_zone(page, nid * MAX_NR_ZONES + j); + init_page_count(page); + __SetPageReserved(page); + memlist_init(&page->list); if (j != ZONE_HIGHMEM) - page->virtual = __va(zone_start_paddr); + set_page_address(page, __va(zone_start_paddr)); zone_start_paddr += PAGE_SIZE; } diff -Nru a/mm/shmem.c b/mm/shmem.c --- a/mm/shmem.c Tue Feb 19 18:08:59 2002 +++ b/mm/shmem.c Tue Feb 19 18:08:59 2002 @@ -311,6 +311,7 @@ return shmem_truncate_direct(base, start, len); } +/* SMP-safe */ static void shmem_truncate (struct inode * inode) { unsigned long index; @@ -973,6 +974,7 @@ * Lookup the data. This is trivial - if the dentry didn't already * exist, we know it is negative. */ +/* SMP-safe */ static struct dentry * shmem_lookup(struct inode *dir, struct dentry *dentry) { d_add(dentry, NULL); @@ -982,6 +984,7 @@ /* * File creation. Allocate an inode, and we're done.. */ +/* SMP-safe */ static int shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) { struct inode * inode = shmem_get_inode(dir->i_sb, mode, dev); @@ -1018,9 +1021,6 @@ { struct inode *inode = old_dentry->d_inode; - if (S_ISDIR(inode->i_mode)) - return -EPERM; - inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; inode->i_nlink++; atomic_inc(&inode->i_count); /* New dentry reference */ @@ -1105,22 +1105,22 @@ static int shmem_symlink(struct inode * dir, struct dentry *dentry, const char * symname) { - int error; int len; struct inode *inode; struct page *page; char *kaddr; struct shmem_inode_info * info; - error = shmem_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0); - if (error) - return error; + inode = shmem_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0); + if (!inode) + return -ENOSPC; len = strlen(symname) + 1; - if (len > PAGE_CACHE_SIZE) + if (len > PAGE_CACHE_SIZE) { + iput(inode); return -ENAMETOOLONG; + } - inode = dentry->d_inode; info = SHMEM_I(inode); inode->i_size = len-1; if (len <= sizeof(struct shmem_inode_info)) { @@ -1135,6 +1135,7 @@ page = shmem_getpage_locked(info, inode, 0); if (IS_ERR(page)) { up(&info->sem); + iput(inode); return PTR_ERR(page); } kaddr = kmap(page); @@ -1147,6 +1148,8 @@ inode->i_op = &shmem_symlink_inode_operations; } dir->i_ctime = dir->i_mtime = CURRENT_TIME; + d_instantiate(dentry, inode); + dget(dentry); return 0; } diff -Nru a/mm/swapfile.c b/mm/swapfile.c --- a/mm/swapfile.c Tue Feb 19 18:08:58 2002 +++ b/mm/swapfile.c Tue Feb 19 18:08:58 2002 @@ -393,7 +393,7 @@ pmd_clear(dir); return; } - pte = pte_offset(dir, address); + pte = pte_offset_map(dir, address); offset += address & PMD_MASK; address &= ~PMD_MASK; end = address + size; @@ -404,6 +404,7 @@ address += PAGE_SIZE; pte++; } while (address && (address < end)); + pte_unmap(pte - 1); } /* mmlist_lock and vma->vm_mm->page_table_lock are held */ diff -Nru a/mm/vmalloc.c b/mm/vmalloc.c --- a/mm/vmalloc.c Tue Feb 19 18:08:57 2002 +++ b/mm/vmalloc.c Tue Feb 19 18:08:57 2002 @@ -30,7 +30,7 @@ pmd_clear(pmd); return; } - pte = pte_offset(pmd, address); + pte = pte_offset_kernel(pmd, address); address &= ~PMD_MASK; end = address + size; if (end > PMD_SIZE) @@ -125,7 +125,7 @@ if (end > PGDIR_SIZE) end = PGDIR_SIZE; do { - pte_t * pte = pte_alloc(&init_mm, pmd, address); + pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; if (alloc_area_pte(pte, address, end - address, gfp_mask, prot)) diff -Nru a/mm/vmscan.c b/mm/vmscan.c --- a/mm/vmscan.c Tue Feb 19 18:08:58 2002 +++ b/mm/vmscan.c Tue Feb 19 18:08:58 2002 @@ -59,7 +59,7 @@ return 0; /* Don't bother replenishing zones not under pressure.. */ - if (!memclass(page->zone, classzone)) + if (!memclass(page_zone(page), classzone)) return 0; if (TryLockPage(page)) @@ -167,7 +167,7 @@ return count; } - pte = pte_offset(dir, address); + pte = pte_offset_map(dir, address); pmd_end = (address + PMD_SIZE) & PMD_MASK; if (end > pmd_end) @@ -181,6 +181,7 @@ count -= try_to_swap_out(mm, vma, address, pte, page, classzone); if (!count) { address += PAGE_SIZE; + pte++; break; } } @@ -188,6 +189,7 @@ address += PAGE_SIZE; pte++; } while (address && (address < end)); + pte_unmap(pte - 1); mm->swap_address = address; return count; } @@ -370,7 +372,7 @@ if (unlikely(!page_count(page))) continue; - if (!memclass(page->zone, classzone)) + if (!memclass(page_zone(page), classzone)) continue; /* Racy check to avoid trylocking when not worthwhile */ diff -Nru a/net/appletalk/ddp.c b/net/appletalk/ddp.c --- a/net/appletalk/ddp.c Tue Feb 19 18:09:00 2002 +++ b/net/appletalk/ddp.c Tue Feb 19 18:09:00 2002 @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -152,16 +153,18 @@ spin_lock_bh(&atalk_sockets_lock); for (s = atalk_sockets; s; s = s->next) { - if (to->sat_port != s->protinfo.af_at.src_port) + struct atalk_sock *at = at_sk(s); + + if (to->sat_port != at->src_port) continue; if (to->sat_addr.s_net == ATADDR_ANYNET && to->sat_addr.s_node == ATADDR_BCAST && - s->protinfo.af_at.src_net == atif->address.s_net) + at->src_net == atif->address.s_net) break; - if (to->sat_addr.s_net == s->protinfo.af_at.src_net && - (to->sat_addr.s_node == s->protinfo.af_at.src_node || + if (to->sat_addr.s_net == at->src_net && + (to->sat_addr.s_node == at->src_node || to->sat_addr.s_node == ATADDR_BCAST || to->sat_addr.s_node == ATADDR_ANYNODE)) break; @@ -170,7 +173,7 @@ * that the node is appropriately set. */ if (to->sat_addr.s_node == ATADDR_ANYNODE && to->sat_addr.s_net != ATADDR_ANYNET && - atif->address.s_node == s->protinfo.af_at.src_node) { + atif->address.s_node == at->src_node) { to->sat_addr.s_node = atif->address.s_node; break; } @@ -192,11 +195,14 @@ struct sock *s; spin_lock_bh(&atalk_sockets_lock); - for (s = atalk_sockets; s; s = s->next) - if (s->protinfo.af_at.src_net == sat->sat_addr.s_net && - s->protinfo.af_at.src_node == sat->sat_addr.s_node && - s->protinfo.af_at.src_port == sat->sat_port) + for (s = atalk_sockets; s; s = s->next) { + struct atalk_sock *at = at_sk(s); + + if (at->src_net == sat->sat_addr.s_net && + at->src_node == sat->sat_addr.s_node && + at->src_port == sat->sat_port) break; + } if (!s) { /* Wheee, it's free, assign and insert. */ @@ -255,15 +261,14 @@ spin_lock_bh(&atalk_sockets_lock); for (s = atalk_sockets; s; s = s->next) { + struct atalk_sock *at = at_sk(s); + len += sprintf(buffer + len,"%02X ", s->type); len += sprintf(buffer + len,"%04X:%02X:%02X ", - ntohs(s->protinfo.af_at.src_net), - s->protinfo.af_at.src_node, - s->protinfo.af_at.src_port); + ntohs(at->src_net), at->src_node, at->src_port); len += sprintf(buffer + len,"%04X:%02X:%02X ", - ntohs(s->protinfo.af_at.dest_net), - s->protinfo.af_at.dest_node, - s->protinfo.af_at.dest_port); + ntohs(at->dest_net), at->dest_node, + at->dest_port); len += sprintf(buffer + len,"%08X:%08X ", atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc)); @@ -1112,42 +1117,33 @@ */ static int atalk_create(struct socket *sock, int protocol) { - struct sock *sk = sk_alloc(PF_APPLETALK, GFP_KERNEL, 1); - - if (!sk) - return -ENOMEM; - - switch (sock->type) { - /* - * We permit SOCK_DGRAM and RAW is an extension. It is - * trivial to do and gives you the full ELAP frame. - * Should be handy for CAP 8) - */ - case SOCK_RAW: - case SOCK_DGRAM: - sock->ops = &atalk_dgram_ops; - break; - - case SOCK_STREAM: - /* - * TODO: if you want to implement ADSP, here's the - * place to start - */ - /* - sock->ops = &atalk_stream_ops; - break; - */ - default: - sk_free(sk); - return -ESOCKTNOSUPPORT; - } + struct sock *sk; + struct atalk_sock *at; + int rc = -ESOCKTNOSUPPORT; MOD_INC_USE_COUNT; + /* + * We permit SOCK_DGRAM and RAW is an extension. It is trivial to do + * and gives you the full ELAP frame. Should be handy for CAP 8) + */ + if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) + goto decmod; + rc = -ENOMEM; + sk = sk_alloc(PF_APPLETALK, GFP_KERNEL, 1, NULL); + if (!sk) + goto decmod; + at = at_sk(sk) = kmalloc(sizeof(*at), GFP_KERNEL); + if (!at) + goto outsk; + rc = 0; + sock->ops = &atalk_dgram_ops; sock_init_data(sock, sk); - sk->destruct = NULL; /* Checksums on by default */ sk->zapped = 1; - return 0; +out: return rc; +outsk: sk_free(sk); +decmod: MOD_DEC_USE_COUNT; + goto out; } /* Free a socket. No work needed */ @@ -1156,15 +1152,13 @@ struct sock *sk = sock->sk; if (!sk) - return 0; - + goto out; if (!sk->dead) sk->state_change(sk); - sk->dead = 1; sock->sk = NULL; atalk_destroy_socket(sk); - return 0; +out: return 0; } /* @@ -1185,10 +1179,11 @@ sat->sat_port < ATPORT_LAST; sat->sat_port++) { for (s = atalk_sockets; s; s = s->next) { - if (s->protinfo.af_at.src_net == sat->sat_addr.s_net && - s->protinfo.af_at.src_node == - sat->sat_addr.s_node && - s->protinfo.af_at.src_port == sat->sat_port) + struct atalk_sock *at = at_sk(s); + + if (at->src_net == sat->sat_addr.s_net && + at->src_node == sat->sat_addr.s_node && + at->src_port == sat->sat_port) goto try_next_port; } @@ -1198,7 +1193,7 @@ atalk_sockets->pprev = &sk->next; atalk_sockets = sk; sk->pprev = &atalk_sockets; - sk->protinfo.af_at.src_port = sat->sat_port; + at_sk(sk)->src_port = sat->sat_port; retval = 0; goto out; @@ -1213,6 +1208,7 @@ static int atalk_autobind(struct sock *sk) { + struct atalk_sock *at = at_sk(sk); struct sockaddr_at sat; int n; struct at_addr *ap = atalk_find_primary(); @@ -1220,8 +1216,8 @@ if (!ap || ap->s_net == htons(ATADDR_ANYNET)) return -EADDRNOTAVAIL; - sk->protinfo.af_at.src_net = sat.sat_addr.s_net = ap->s_net; - sk->protinfo.af_at.src_node = sat.sat_addr.s_node = ap->s_node; + at->src_net = sat.sat_addr.s_net = ap->s_net; + at->src_node = sat.sat_addr.s_node = ap->s_node; n = atalk_pick_and_bind_port(sk, &sat); if (n < 0) @@ -1236,6 +1232,7 @@ { struct sockaddr_at *addr = (struct sockaddr_at *)uaddr; struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); if (!sk->zapped || addr_len != sizeof(struct sockaddr_at)) return -EINVAL; @@ -1249,15 +1246,15 @@ if (!ap) return -EADDRNOTAVAIL; - sk->protinfo.af_at.src_net = addr->sat_addr.s_net = ap->s_net; - sk->protinfo.af_at.src_node = addr->sat_addr.s_node= ap->s_node; + at->src_net = addr->sat_addr.s_net = ap->s_net; + at->src_node = addr->sat_addr.s_node= ap->s_node; } else { if (!atalk_find_interface(addr->sat_addr.s_net, addr->sat_addr.s_node)) return -EADDRNOTAVAIL; - sk->protinfo.af_at.src_net = addr->sat_addr.s_net; - sk->protinfo.af_at.src_node = addr->sat_addr.s_node; + at->src_net = addr->sat_addr.s_net; + at->src_node = addr->sat_addr.s_node; } if (addr->sat_port == ATADDR_ANYPORT) { @@ -1266,7 +1263,7 @@ if (n < 0) return n; } else { - sk->protinfo.af_at.src_port = addr->sat_port; + at->src_port = addr->sat_port; if (atalk_find_or_insert_socket(sk, addr)) return -EADDRINUSE; @@ -1281,6 +1278,7 @@ int addr_len, int flags) { struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); struct sockaddr_at *addr; sk->state = TCP_CLOSE; @@ -1312,9 +1310,9 @@ if (!atrtr_get_dev(&addr->sat_addr)) return -ENETUNREACH; - sk->protinfo.af_at.dest_port = addr->sat_port; - sk->protinfo.af_at.dest_net = addr->sat_addr.s_net; - sk->protinfo.af_at.dest_node = addr->sat_addr.s_node; + at->dest_port = addr->sat_port; + at->dest_net = addr->sat_addr.s_net; + at->dest_node = addr->sat_addr.s_node; sock->state = SS_CONNECTED; sk->state = TCP_ESTABLISHED; @@ -1331,6 +1329,7 @@ { struct sockaddr_at sat; struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); if (sk->zapped) if (atalk_autobind(sk) < 0) @@ -1342,13 +1341,13 @@ if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; - sat.sat_addr.s_net = sk->protinfo.af_at.dest_net; - sat.sat_addr.s_node = sk->protinfo.af_at.dest_node; - sat.sat_port = sk->protinfo.af_at.dest_port; + sat.sat_addr.s_net = at->dest_net; + sat.sat_addr.s_node = at->dest_node; + sat.sat_port = at->dest_port; } else { - sat.sat_addr.s_net = sk->protinfo.af_at.src_net; - sat.sat_addr.s_node = sk->protinfo.af_at.src_node; - sat.sat_port = sk->protinfo.af_at.src_port; + sat.sat_addr.s_net = at->src_net; + sat.sat_addr.s_node = at->src_node; + sat.sat_port = at->src_port; } sat.sat_family = AF_APPLETALK; @@ -1597,6 +1596,7 @@ struct scm_cookie *scm) { struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); struct sockaddr_at *usat = (struct sockaddr_at *)msg->msg_name; int flags = msg->msg_flags; int loopback = 0; @@ -1635,10 +1635,10 @@ if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; usat = &local_satalk; - usat->sat_family = AF_APPLETALK; - usat->sat_port = sk->protinfo.af_at.dest_port; - usat->sat_addr.s_node = sk->protinfo.af_at.dest_node; - usat->sat_addr.s_net = sk->protinfo.af_at.dest_net; + usat->sat_family = AF_APPLETALK; + usat->sat_port = at->dest_port; + usat->sat_addr.s_node = at->dest_node; + usat->sat_addr.s_net = at->dest_net; } /* Build a packet */ @@ -1657,7 +1657,7 @@ struct at_addr at_hint; at_hint.s_node = 0; - at_hint.s_net = sk->protinfo.af_at.src_net; + at_hint.s_net = at->src_net; rt = atrtr_find(&at_hint); if (!rt) @@ -1693,11 +1693,11 @@ *((__u16 *)ddp) = ntohs(*((__u16 *)ddp)); ddp->deh_dnet = usat->sat_addr.s_net; - ddp->deh_snet = sk->protinfo.af_at.src_net; + ddp->deh_snet = at->src_net; ddp->deh_dnode = usat->sat_addr.s_node; - ddp->deh_snode = sk->protinfo.af_at.src_node; + ddp->deh_snode = at->src_node; ddp->deh_dport = usat->sat_port; - ddp->deh_sport = sk->protinfo.af_at.src_port; + ddp->deh_sport = at->src_port; SOCK_DEBUG(sk, "SK %p: Copy user data (%d bytes).\n", sk, len); @@ -1894,10 +1894,9 @@ return put_user(amount, (int *)arg); } -static struct net_proto_family atalk_family_ops = -{ - PF_APPLETALK, - atalk_create +static struct net_proto_family atalk_family_ops = { + family: PF_APPLETALK, + create: atalk_create, }; static struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops)= diff -Nru a/net/atm/common.c b/net/atm/common.c --- a/net/atm/common.c Tue Feb 19 18:09:00 2002 +++ b/net/atm/common.c Tue Feb 19 18:09:00 2002 @@ -105,7 +105,7 @@ sock->sk = NULL; if (sock->type == SOCK_STREAM) return -EINVAL; if (!(sk = alloc_atm_vcc_sk(family))) return -ENOMEM; - vcc = sk->protinfo.af_atm; + vcc = atm_sk(sk); memset(&vcc->flags,0,sizeof(vcc->flags)); vcc->dev = NULL; vcc->family = sock->ops->family; @@ -133,10 +133,9 @@ void atm_release_vcc_sk(struct sock *sk,int free_sk) { - struct atm_vcc *vcc; + struct atm_vcc *vcc = atm_sk(sk); struct sk_buff *skb; - vcc = sk->protinfo.af_atm; clear_bit(ATM_VF_READY,&vcc->flags); if (vcc->dev) { if (vcc->dev->ops->close) vcc->dev->ops->close(vcc); diff -Nru a/net/atm/mpc.c b/net/atm/mpc.c --- a/net/atm/mpc.c Tue Feb 19 18:08:58 2002 +++ b/net/atm/mpc.c Tue Feb 19 18:08:58 2002 @@ -13,7 +13,7 @@ #include #include #include -#include /* for ip_fast_csum() */ +#include /* for ip_fast_csum() */ #include #include #include diff -Nru a/net/atm/pvc.c b/net/atm/pvc.c --- a/net/atm/pvc.c Tue Feb 19 18:08:59 2002 +++ b/net/atm/pvc.c Tue Feb 19 18:08:59 2002 @@ -111,11 +111,8 @@ static struct net_proto_family pvc_family_ops = { - PF_ATMPVC, - pvc_create, - 0, /* no authentication */ - 0, /* no encryption */ - 0 /* no encrypt_net */ + family: PF_ATMPVC, + create: pvc_create, }; diff -Nru a/net/atm/resources.c b/net/atm/resources.c --- a/net/atm/resources.c Tue Feb 19 18:08:59 2002 +++ b/net/atm/resources.c Tue Feb 19 18:08:59 2002 @@ -2,6 +2,11 @@ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ +/* Fixes + * Arnaldo Carvalho de Melo + * 2002/01 - don't free the whole struct sock on sk->destruct time, + * use the default destruct function initialized by sock_init_data */ + #include #include @@ -138,28 +143,19 @@ atm_dev_deregister(dev); } - -/* Handler for sk->destruct, invoked by sk_free() */ -static void atm_free_sock(struct sock *sk) -{ - kfree(sk->protinfo.af_atm); -} - - struct sock *alloc_atm_vcc_sk(int family) { struct sock *sk; struct atm_vcc *vcc; - sk = sk_alloc(family, GFP_KERNEL, 1); + sk = sk_alloc(family, GFP_KERNEL, 1, NULL); if (!sk) return NULL; - vcc = sk->protinfo.af_atm = kmalloc(sizeof(*vcc),GFP_KERNEL); + vcc = atm_sk(sk) = kmalloc(sizeof(*vcc), GFP_KERNEL); if (!vcc) { sk_free(sk); return NULL; } sock_init_data(NULL,sk); - sk->destruct = atm_free_sock; memset(vcc,0,sizeof(*vcc)); vcc->sk = sk; if (nodev_vccs) nodev_vccs->prev = vcc; @@ -185,7 +181,7 @@ void free_atm_vcc_sk(struct sock *sk) { - unlink_vcc(sk->protinfo.af_atm,NULL); + unlink_vcc(atm_sk(sk), NULL); sk_free(sk); } diff -Nru a/net/atm/svc.c b/net/atm/svc.c --- a/net/atm/svc.c Tue Feb 19 18:08:57 2002 +++ b/net/atm/svc.c Tue Feb 19 18:08:57 2002 @@ -430,11 +430,8 @@ static struct net_proto_family svc_family_ops = { - PF_ATMSVC, - svc_create, - 0, /* no authentication */ - 0, /* no encryption */ - 0 /* no encrypt_net */ + family: PF_ATMSVC, + create: svc_create, }; diff -Nru a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c --- a/net/ax25/af_ax25.c Tue Feb 19 18:08:59 2002 +++ b/net/ax25/af_ax25.c Tue Feb 19 18:08:59 2002 @@ -134,6 +134,7 @@ #include #include #include +#include #include #include @@ -161,7 +162,7 @@ static void ax25_free_sock(struct sock *sk) { - ax25_free_cb(sk->protinfo.ax25); + ax25_free_cb(ax25_sk(sk)); } /* @@ -423,9 +424,11 @@ if (ax25->sk != NULL) { while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) { if (skb->sk != ax25->sk) { /* A pending connection */ + ax25_cb *sax25 = ax25_sk(skb->sk); + skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - ax25_start_heartbeat(skb->sk->protinfo.ax25); - skb->sk->protinfo.ax25->state = AX25_STATE_0; + ax25_start_heartbeat(sax25); + sax25->state = AX25_STATE_0; } kfree_skb(skb); @@ -631,6 +634,7 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { struct sock *sk = sock->sk; + ax25_cb *ax25 = ax25_sk(sk); struct net_device *dev; char devname[IFNAMSIZ]; int opt; @@ -646,69 +650,69 @@ switch (optname) { case AX25_WINDOW: - if (sk->protinfo.ax25->modulus == AX25_MODULUS) { + if (ax25->modulus == AX25_MODULUS) { if (opt < 1 || opt > 7) return -EINVAL; } else { if (opt < 1 || opt > 63) return -EINVAL; } - sk->protinfo.ax25->window = opt; + ax25->window = opt; return 0; case AX25_T1: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->rtt = (opt * HZ) / 2; - sk->protinfo.ax25->t1 = opt * HZ; + ax25->rtt = (opt * HZ) / 2; + ax25->t1 = opt * HZ; return 0; case AX25_T2: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->t2 = opt * HZ; + ax25->t2 = opt * HZ; return 0; case AX25_N2: if (opt < 1 || opt > 31) return -EINVAL; - sk->protinfo.ax25->n2 = opt; + ax25->n2 = opt; return 0; case AX25_T3: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->t3 = opt * HZ; + ax25->t3 = opt * HZ; return 0; case AX25_IDLE: if (opt < 0) return -EINVAL; - sk->protinfo.ax25->idle = opt * 60 * HZ; + ax25->idle = opt * 60 * HZ; return 0; case AX25_BACKOFF: if (opt < 0 || opt > 2) return -EINVAL; - sk->protinfo.ax25->backoff = opt; + ax25->backoff = opt; return 0; case AX25_EXTSEQ: - sk->protinfo.ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS; + ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS; return 0; case AX25_PIDINCL: - sk->protinfo.ax25->pidincl = opt ? 1 : 0; + ax25->pidincl = opt ? 1 : 0; return 0; case AX25_IAMDIGI: - sk->protinfo.ax25->iamdigi = opt ? 1 : 0; + ax25->iamdigi = opt ? 1 : 0; return 0; case AX25_PACLEN: if (opt < 16 || opt > 65535) return -EINVAL; - sk->protinfo.ax25->paclen = opt; + ax25->paclen = opt; return 0; case SO_BINDTODEVICE: @@ -723,8 +727,8 @@ (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) return -EADDRNOTAVAIL; - sk->protinfo.ax25->ax25_dev = ax25_dev_ax25dev(dev); - ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev); + ax25->ax25_dev = ax25_dev_ax25dev(dev); + ax25_fillin_cb(ax25, ax25->ax25_dev); return 0; default: @@ -735,6 +739,7 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; + ax25_cb *ax25 = ax25_sk(sk); struct ax25_dev *ax25_dev; char devname[IFNAMSIZ]; void *valptr; @@ -755,51 +760,51 @@ switch (optname) { case AX25_WINDOW: - val = sk->protinfo.ax25->window; + val = ax25->window; break; case AX25_T1: - val = sk->protinfo.ax25->t1 / HZ; + val = ax25->t1 / HZ; break; case AX25_T2: - val = sk->protinfo.ax25->t2 / HZ; + val = ax25->t2 / HZ; break; case AX25_N2: - val = sk->protinfo.ax25->n2; + val = ax25->n2; break; case AX25_T3: - val = sk->protinfo.ax25->t3 / HZ; + val = ax25->t3 / HZ; break; case AX25_IDLE: - val = sk->protinfo.ax25->idle / (60 * HZ); + val = ax25->idle / (60 * HZ); break; case AX25_BACKOFF: - val = sk->protinfo.ax25->backoff; + val = ax25->backoff; break; case AX25_EXTSEQ: - val = (sk->protinfo.ax25->modulus == AX25_EMODULUS); + val = (ax25->modulus == AX25_EMODULUS); break; case AX25_PIDINCL: - val = sk->protinfo.ax25->pidincl; + val = ax25->pidincl; break; case AX25_IAMDIGI: - val = sk->protinfo.ax25->iamdigi; + val = ax25->iamdigi; break; case AX25_PACLEN: - val = sk->protinfo.ax25->paclen; + val = ax25->paclen; break; case SO_BINDTODEVICE: - ax25_dev = sk->protinfo.ax25->ax25_dev; + ax25_dev = ax25->ax25_dev; if (ax25_dev != NULL && ax25_dev->dev != NULL) { strncpy(devname, ax25_dev->dev->name, IFNAMSIZ); @@ -884,10 +889,11 @@ return -ESOCKTNOSUPPORT; } - if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) + if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1, NULL)) == NULL) return -ENOMEM; - if ((ax25 = ax25_create_cb()) == NULL) { + ax25 = ax25_sk(sk) = ax25_create_cb(); + if (!ax25) { sk_free(sk); return -ENOMEM; } @@ -898,8 +904,7 @@ sock->ops = &ax25_proto_ops; sk->protocol = protocol; - ax25->sk = sk; - sk->protinfo.ax25 = ax25; + ax25->sk = sk; return 0; } @@ -907,9 +912,9 @@ struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev) { struct sock *sk; - ax25_cb *ax25; + ax25_cb *ax25, *oax25; - if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) + if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1, NULL)) == NULL) return NULL; if ((ax25 = ax25_create_cb()) == NULL) { @@ -942,33 +947,35 @@ sk->sleep = osk->sleep; sk->zapped = osk->zapped; - ax25->modulus = osk->protinfo.ax25->modulus; - ax25->backoff = osk->protinfo.ax25->backoff; - ax25->pidincl = osk->protinfo.ax25->pidincl; - ax25->iamdigi = osk->protinfo.ax25->iamdigi; - ax25->rtt = osk->protinfo.ax25->rtt; - ax25->t1 = osk->protinfo.ax25->t1; - ax25->t2 = osk->protinfo.ax25->t2; - ax25->t3 = osk->protinfo.ax25->t3; - ax25->n2 = osk->protinfo.ax25->n2; - ax25->idle = osk->protinfo.ax25->idle; - ax25->paclen = osk->protinfo.ax25->paclen; - ax25->window = osk->protinfo.ax25->window; + oax25 = ax25_sk(osk); + + ax25->modulus = oax25->modulus; + ax25->backoff = oax25->backoff; + ax25->pidincl = oax25->pidincl; + ax25->iamdigi = oax25->iamdigi; + ax25->rtt = oax25->rtt; + ax25->t1 = oax25->t1; + ax25->t2 = oax25->t2; + ax25->t3 = oax25->t3; + ax25->n2 = oax25->n2; + ax25->idle = oax25->idle; + ax25->paclen = oax25->paclen; + ax25->window = oax25->window; ax25->ax25_dev = ax25_dev; - ax25->source_addr = osk->protinfo.ax25->source_addr; + ax25->source_addr = oax25->source_addr; - if (osk->protinfo.ax25->digipeat != NULL) { + if (oax25->digipeat != NULL) { if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { sk_free(sk); return NULL; } - memcpy(ax25->digipeat, osk->protinfo.ax25->digipeat, sizeof(ax25_digi)); + memcpy(ax25->digipeat, oax25->digipeat, sizeof(ax25_digi)); } - sk->protinfo.ax25 = ax25; - ax25->sk = sk; + ax25_sk(sk) = ax25; + ax25->sk = sk; return sk; } @@ -976,45 +983,51 @@ static int ax25_release(struct socket *sock) { struct sock *sk = sock->sk; + ax25_cb *ax25; if (sk == NULL) return 0; + ax25 = ax25_sk(sk); + if (sk->type == SOCK_SEQPACKET) { - switch (sk->protinfo.ax25->state) { + switch (ax25->state) { case AX25_STATE_0: - ax25_disconnect(sk->protinfo.ax25, 0); - ax25_destroy_socket(sk->protinfo.ax25); + ax25_disconnect(ax25, 0); + ax25_destroy_socket(ax25); break; case AX25_STATE_1: case AX25_STATE_2: - ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(sk->protinfo.ax25, 0); - ax25_destroy_socket(sk->protinfo.ax25); + ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); + ax25_disconnect(ax25, 0); + ax25_destroy_socket(ax25); break; case AX25_STATE_3: case AX25_STATE_4: - ax25_clear_queues(sk->protinfo.ax25); - sk->protinfo.ax25->n2count = 0; - switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + ax25_clear_queues(ax25); + ax25->n2count = 0; + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_DUPLEX: - ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_stop_t2timer(sk->protinfo.ax25); - ax25_stop_t3timer(sk->protinfo.ax25); - ax25_stop_idletimer(sk->protinfo.ax25); + ax25_send_control(ax25, + AX25_DISC, + AX25_POLLON, + AX25_COMMAND); + ax25_stop_t2timer(ax25); + ax25_stop_t3timer(ax25); + ax25_stop_idletimer(ax25); break; #ifdef CONFIG_AX25_DAMA_SLAVE case AX25_PROTO_DAMA_SLAVE: - ax25_stop_t3timer(sk->protinfo.ax25); - ax25_stop_idletimer(sk->protinfo.ax25); + ax25_stop_t3timer(ax25); + ax25_stop_idletimer(ax25); break; #endif } - ax25_calculate_t1(sk->protinfo.ax25); - ax25_start_t1timer(sk->protinfo.ax25); - sk->protinfo.ax25->state = AX25_STATE_2; + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); + ax25->state = AX25_STATE_2; sk->state = TCP_CLOSE; sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); @@ -1030,7 +1043,7 @@ sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); sk->dead = 1; - ax25_destroy_socket(sk->protinfo.ax25); + ax25_destroy_socket(ax25); } sock->sk = NULL; @@ -1048,6 +1061,7 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; + ax25_cb *ax25 = ax25_sk(sk); struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; ax25_address *call; ax25_dev *ax25_dev = NULL; @@ -1074,15 +1088,15 @@ return -EACCES; if (call == NULL) - sk->protinfo.ax25->source_addr = addr->fsa_ax25.sax25_call; + ax25->source_addr = addr->fsa_ax25.sax25_call; else - sk->protinfo.ax25->source_addr = *call; + ax25->source_addr = *call; /* * User already set interface with SO_BINDTODEVICE */ - if (sk->protinfo.ax25->ax25_dev != NULL) + if (ax25->ax25_dev != NULL) goto done; if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) { @@ -1095,10 +1109,10 @@ } if (ax25_dev != NULL) - ax25_fillin_cb(sk->protinfo.ax25, ax25_dev); + ax25_fillin_cb(ax25, ax25_dev); done: - ax25_insert_socket(sk->protinfo.ax25); + ax25_insert_socket(ax25); sk->zapped = 0; return 0; } @@ -1109,6 +1123,7 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { struct sock *sk = sock->sk; + ax25_cb *ax25 = ax25_sk(sk); struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; ax25_digi *digi = NULL; int ct = 0, err; @@ -1157,9 +1172,9 @@ if (fsa->fsa_ax25.sax25_family != AF_AX25) return -EINVAL; - if (sk->protinfo.ax25->digipeat != NULL) { - kfree(sk->protinfo.ax25->digipeat); - sk->protinfo.ax25->digipeat = NULL; + if (ax25->digipeat != NULL) { + kfree(ax25->digipeat); + ax25->digipeat = NULL; } /* @@ -1177,7 +1192,8 @@ digi->lastrepeat = -1; while (ct < fsa->fsa_ax25.sax25_ndigis) { - if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) { + if ((fsa->fsa_digipeater[ct].ax25_call[6] & + AX25_HBIT) && ax25->iamdigi) { digi->repeated[ct] = 1; digi->lastrepeat = ct; } else { @@ -1197,22 +1213,24 @@ /* check if we can remove this feature. It is broken. */ printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@yaina.de\n", current->comm); - if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0) + if ((err = ax25_rt_autobind(ax25, &fsa->fsa_ax25.sax25_call)) < 0) return err; - ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev); - ax25_insert_socket(sk->protinfo.ax25); + ax25_fillin_cb(ax25, ax25->ax25_dev); + ax25_insert_socket(ax25); } else { - if (sk->protinfo.ax25->ax25_dev == NULL) + if (ax25->ax25_dev == NULL) return -EHOSTUNREACH; } - if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->ax25_dev->dev) != NULL) { + if (sk->type == SOCK_SEQPACKET && + ax25_find_cb(&ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, + ax25->ax25_dev->dev)) { if (digi != NULL) kfree(digi); return -EADDRINUSE; /* Already such a connection */ } - sk->protinfo.ax25->dest_addr = fsa->fsa_ax25.sax25_call; - sk->protinfo.ax25->digipeat = digi; + ax25->dest_addr = fsa->fsa_ax25.sax25_call; + ax25->digipeat = digi; /* First the easy one */ if (sk->type != SOCK_SEQPACKET) { @@ -1225,27 +1243,28 @@ sock->state = SS_CONNECTING; sk->state = TCP_SYN_SENT; - switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_DUPLEX: - ax25_std_establish_data_link(sk->protinfo.ax25); + ax25_std_establish_data_link(ax25); break; #ifdef CONFIG_AX25_DAMA_SLAVE case AX25_PROTO_DAMA_SLAVE: - sk->protinfo.ax25->modulus = AX25_MODULUS; - sk->protinfo.ax25->window = sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - if (sk->protinfo.ax25->ax25_dev->dama.slave) - ax25_ds_establish_data_link(sk->protinfo.ax25); + ax25->modulus = AX25_MODULUS; + ax25->window = + ax25->ax25_dev->values[AX25_VALUES_WINDOW]; + if (ax25->ax25_dev->dama.slave) + ax25_ds_establish_data_link(ax25); else - ax25_std_establish_data_link(sk->protinfo.ax25); + ax25_std_establish_data_link(ax25); break; #endif } - sk->protinfo.ax25->state = AX25_STATE_1; + ax25->state = AX25_STATE_1; - ax25_start_heartbeat(sk->protinfo.ax25); + ax25_start_heartbeat(ax25); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) @@ -1327,6 +1346,7 @@ static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { struct sock *sk = sock->sk; + ax25_cb *ax25 = ax25_sk(sk); struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; unsigned char ndigi, i; @@ -1335,21 +1355,23 @@ return -ENOTCONN; fsa->fsa_ax25.sax25_family = AF_AX25; - fsa->fsa_ax25.sax25_call = sk->protinfo.ax25->dest_addr; + fsa->fsa_ax25.sax25_call = ax25->dest_addr; fsa->fsa_ax25.sax25_ndigis = 0; - if (sk->protinfo.ax25->digipeat != NULL) { - ndigi = sk->protinfo.ax25->digipeat->ndigi; + if (ax25->digipeat != NULL) { + ndigi = ax25->digipeat->ndigi; fsa->fsa_ax25.sax25_ndigis = ndigi; for (i = 0; i < ndigi; i++) - fsa->fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i]; + fsa->fsa_digipeater[i] = + ax25->digipeat->calls[i]; } } else { fsa->fsa_ax25.sax25_family = AF_AX25; - fsa->fsa_ax25.sax25_call = sk->protinfo.ax25->source_addr; + fsa->fsa_ax25.sax25_call = ax25->source_addr; fsa->fsa_ax25.sax25_ndigis = 1; - if (sk->protinfo.ax25->ax25_dev != NULL) { - memcpy(&fsa->fsa_digipeater[0], sk->protinfo.ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN); + if (ax25->ax25_dev != NULL) { + memcpy(&fsa->fsa_digipeater[0], + ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN); } else { fsa->fsa_digipeater[0] = null_ax25_address; } @@ -1361,6 +1383,7 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; + ax25_cb *ax25 = ax25_sk(sk); struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name; int err; struct sockaddr_ax25 sax; @@ -1383,7 +1406,7 @@ return -EPIPE; } - if (sk->protinfo.ax25->ax25_dev == NULL) + if (ax25->ax25_dev == NULL) return -ENETUNREACH; if (usax != NULL) { @@ -1424,7 +1447,7 @@ } sax = *usax; - if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->protinfo.ax25->dest_addr, &sax.sax25_call) != 0) + if (sk->type == SOCK_SEQPACKET && ax25cmp(&ax25->dest_addr, &sax.sax25_call) != 0) return -EISCONN; if (usax->sax25_ndigis == 0) dp = NULL; @@ -1439,8 +1462,8 @@ if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; sax.sax25_family = AF_AX25; - sax.sax25_call = sk->protinfo.ax25->dest_addr; - dp = sk->protinfo.ax25->digipeat; + sax.sax25_call = ax25->dest_addr; + dp = ax25->digipeat; } SOCK_DEBUG(sk, "AX.25: sendto: Addresses built.\n"); @@ -1463,7 +1486,7 @@ skb->nh.raw = skb->data; /* Add the PID if one is not supplied by the user in the skb */ - if (!sk->protinfo.ax25->pidincl) { + if (!ax25->pidincl) { asmptr = skb_push(skb, 1); *asmptr = sk->protocol; } @@ -1477,7 +1500,8 @@ return -ENOTCONN; } - ax25_output(sk->protinfo.ax25, sk->protinfo.ax25->paclen, skb); /* Shove it onto the queue and kick */ + /* Shove it onto the queue and kick */ + ax25_output(ax25, ax25->paclen, skb); return len; } else { @@ -1489,7 +1513,9 @@ SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi); /* Build an AX.25 header */ - asmptr += (lv = ax25_addr_build(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, AX25_COMMAND, AX25_MODULUS)); + asmptr += (lv = ax25_addr_build(asmptr, &ax25->source_addr, + &sax.sax25_call, dp, + AX25_COMMAND, AX25_MODULUS)); SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv); @@ -1500,7 +1526,7 @@ *asmptr = AX25_UI; /* Datagram frames go straight out of the door as UI */ - skb->dev = sk->protinfo.ax25->ax25_dev->dev; + skb->dev = ax25->ax25_dev->dev; ax25_queue_xmit(skb); @@ -1526,7 +1552,7 @@ if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) return er; - if (!sk->protinfo.ax25->pidincl) + if (!ax25_sk(sk)->pidincl) skb_pull(skb, 1); /* Remove PID */ skb->h.raw = skb->data; @@ -1639,27 +1665,28 @@ case SIOCAX25GETINFO: case SIOCAX25GETINFOOLD: { + ax25_cb *ax25 = ax25_sk(sk); struct ax25_info_struct ax25_info; - ax25_info.t1 = sk->protinfo.ax25->t1 / HZ; - ax25_info.t2 = sk->protinfo.ax25->t2 / HZ; - ax25_info.t3 = sk->protinfo.ax25->t3 / HZ; - ax25_info.idle = sk->protinfo.ax25->idle / (60 * HZ); - ax25_info.n2 = sk->protinfo.ax25->n2; - ax25_info.t1timer = ax25_display_timer(&sk->protinfo.ax25->t1timer) / HZ; - ax25_info.t2timer = ax25_display_timer(&sk->protinfo.ax25->t2timer) / HZ; - ax25_info.t3timer = ax25_display_timer(&sk->protinfo.ax25->t3timer) / HZ; - ax25_info.idletimer = ax25_display_timer(&sk->protinfo.ax25->idletimer) / (60 * HZ); - ax25_info.n2count = sk->protinfo.ax25->n2count; - ax25_info.state = sk->protinfo.ax25->state; + ax25_info.t1 = ax25->t1 / HZ; + ax25_info.t2 = ax25->t2 / HZ; + ax25_info.t3 = ax25->t3 / HZ; + ax25_info.idle = ax25->idle / (60 * HZ); + ax25_info.n2 = ax25->n2; + ax25_info.t1timer = ax25_display_timer(&ax25->t1timer) / HZ; + ax25_info.t2timer = ax25_display_timer(&ax25->t2timer) / HZ; + ax25_info.t3timer = ax25_display_timer(&ax25->t3timer) / HZ; + ax25_info.idletimer = ax25_display_timer(&ax25->idletimer) / (60 * HZ); + ax25_info.n2count = ax25->n2count; + ax25_info.state = ax25->state; ax25_info.rcv_q = atomic_read(&sk->rmem_alloc); ax25_info.snd_q = atomic_read(&sk->wmem_alloc); - ax25_info.vs = sk->protinfo.ax25->vs; - ax25_info.vr = sk->protinfo.ax25->vr; - ax25_info.va = sk->protinfo.ax25->va; - ax25_info.vs_max = sk->protinfo.ax25->vs; /* reserved */ - ax25_info.paclen = sk->protinfo.ax25->paclen; - ax25_info.window = sk->protinfo.ax25->window; + ax25_info.vs = ax25->vs; + ax25_info.vr = ax25->vr; + ax25_info.va = ax25->va; + ax25_info.vs_max = ax25->vs; /* reserved */ + ax25_info.paclen = ax25->paclen; + ax25_info.window = ax25->window; /* old structure? */ if (cmd == SIOCAX25GETINFOOLD) { diff -Nru a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c --- a/net/ax25/ax25_ds_in.c Tue Feb 19 18:08:58 2002 +++ b/net/ax25/ax25_ds_in.c Tue Feb 19 18:08:58 2002 @@ -38,6 +38,7 @@ #include #include #include /* For ip_rcv */ +#include #include #include #include diff -Nru a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c --- a/net/ax25/ax25_ds_timer.c Tue Feb 19 18:08:59 2002 +++ b/net/ax25/ax25_ds_timer.c Tue Feb 19 18:08:59 2002 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff -Nru a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c --- a/net/ax25/ax25_in.c Tue Feb 19 18:08:58 2002 +++ b/net/ax25/ax25_in.c Tue Feb 19 18:08:58 2002 @@ -57,6 +57,7 @@ #include #include #include /* For ip_rcv */ +#include #include /* For arp_rcv */ #include #include @@ -388,7 +389,7 @@ return 0; } - ax25 = make->protinfo.ax25; + ax25 = ax25_sk(make); skb_set_owner_r(skb, make); skb_queue_head(&sk->receive_queue, skb); diff -Nru a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c --- a/net/ax25/ax25_std_in.c Tue Feb 19 18:08:58 2002 +++ b/net/ax25/ax25_std_in.c Tue Feb 19 18:08:58 2002 @@ -53,6 +53,7 @@ #include #include #include /* For ip_rcv */ +#include #include #include #include diff -Nru a/net/ax25/ax25_std_timer.c b/net/ax25/ax25_std_timer.c --- a/net/ax25/ax25_std_timer.c Tue Feb 19 18:08:58 2002 +++ b/net/ax25/ax25_std_timer.c Tue Feb 19 18:08:58 2002 @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff -Nru a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c --- a/net/ax25/ax25_subr.c Tue Feb 19 18:08:59 2002 +++ b/net/ax25/ax25_subr.c Tue Feb 19 18:08:59 2002 @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include diff -Nru a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c --- a/net/bluetooth/af_bluetooth.c Tue Feb 19 18:08:58 2002 +++ b/net/bluetooth/af_bluetooth.c Tue Feb 19 18:08:58 2002 @@ -122,9 +122,9 @@ write_unlock(&l->lock); } -struct net_proto_family bluez_sock_family_ops = -{ - PF_BLUETOOTH, bluez_sock_create +struct net_proto_family bluez_sock_family_ops = { + family: PF_BLUETOOTH, + create: bluez_sock_create, }; int bluez_init(void) diff -Nru a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c --- a/net/bluetooth/hci_sock.c Tue Feb 19 18:08:57 2002 +++ b/net/bluetooth/hci_sock.c Tue Feb 19 18:08:57 2002 @@ -453,7 +453,7 @@ sock->ops = &hci_sock_ops; - if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1))) + if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1, NULL))) return -ENOMEM; sock->state = SS_UNCONNECTED; @@ -519,7 +519,7 @@ struct net_proto_family hci_sock_family_ops = { family: PF_BLUETOOTH, - create: hci_sock_create + create: hci_sock_create, }; struct notifier_block hci_sock_nblock = { diff -Nru a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c --- a/net/bluetooth/l2cap_core.c Tue Feb 19 18:08:58 2002 +++ b/net/bluetooth/l2cap_core.c Tue Feb 19 18:08:58 2002 @@ -640,7 +640,7 @@ { struct sock *sk; - if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1))) + if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1, NULL))) return NULL; sock_init_data(sock, sk); @@ -2238,7 +2238,7 @@ struct net_proto_family l2cap_sock_family_ops = { family: PF_BLUETOOTH, - create: l2cap_sock_create + create: l2cap_sock_create, }; struct hci_proto l2cap_hci_proto = { diff -Nru a/net/core/datagram.c b/net/core/datagram.c --- a/net/core/datagram.c Tue Feb 19 18:08:57 2002 +++ b/net/core/datagram.c Tue Feb 19 18:08:57 2002 @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff -Nru a/net/core/netfilter.c b/net/core/netfilter.c --- a/net/core/netfilter.c Tue Feb 19 18:08:59 2002 +++ b/net/core/netfilter.c Tue Feb 19 18:08:59 2002 @@ -20,6 +20,10 @@ #include #include #include +#include +#include +#include +#include #define __KERNEL_SYSCALLS__ #include @@ -122,9 +126,10 @@ down(&nf_sockopt_mutex); if (reg->use != 0) { /* To be woken by nf_sockopt call... */ + /* FIXME: Stuart Young's name appears gratuitously. */ + set_current_state(TASK_UNINTERRUPTIBLE); reg->cleanup_task = current; up(&nf_sockopt_mutex); - set_current_state(TASK_UNINTERRUPTIBLE); schedule(); goto restart; } @@ -552,6 +557,73 @@ kfree(info); return; } + +#ifdef CONFIG_INET +/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */ +int ip_route_me_harder(struct sk_buff **pskb) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct rtable *rt; + struct rt_key key = { dst:iph->daddr, + src:iph->saddr, + oif:(*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0, + tos:RT_TOS(iph->tos)|RTO_CONN, +#ifdef CONFIG_IP_ROUTE_FWMARK + fwmark:(*pskb)->nfmark +#endif + }; + struct net_device *dev_src = NULL; + int err; + + /* accomodate ip_route_output_slow(), which expects the key src to be + 0 or a local address; however some non-standard hacks like + ipt_REJECT.c:send_reset() can cause packets with foreign + saddr to be appear on the NF_IP_LOCAL_OUT hook -MB */ + if(key.src && !(dev_src = ip_dev_find(key.src))) + key.src = 0; + + if ((err=ip_route_output_key(&rt, &key)) != 0) { + printk("route_me_harder: ip_route_output_key(dst=%u.%u.%u.%u, src=%u.%u.%u.%u, oif=%d, tos=0x%x, fwmark=0x%lx) error %d\n", + NIPQUAD(iph->daddr), NIPQUAD(iph->saddr), + (*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0, + RT_TOS(iph->tos)|RTO_CONN, +#ifdef CONFIG_IP_ROUTE_FWMARK + (*pskb)->nfmark, +#else + 0UL, +#endif + err); + goto out; + } + + /* Drop old route. */ + dst_release((*pskb)->dst); + + (*pskb)->dst = &rt->u.dst; + + /* Change in oif may mean change in hh_len. */ + if (skb_headroom(*pskb) < (*pskb)->dst->dev->hard_header_len) { + struct sk_buff *nskb; + + nskb = skb_realloc_headroom(*pskb, + (*pskb)->dst->dev->hard_header_len); + if (!nskb) { + err = -ENOMEM; + goto out; + } + if ((*pskb)->sk) + skb_set_owner_w(nskb, (*pskb)->sk); + kfree_skb(*pskb); + *pskb = nskb; + } + +out: + if (dev_src) + dev_put(dev_src); + + return err; +} +#endif /*CONFIG_INET*/ /* This does not belong here, but ipt_REJECT needs it if connection tracking in use: without this, connection may not be in hash table, diff -Nru a/net/core/sock.c b/net/core/sock.c --- a/net/core/sock.c Tue Feb 19 18:08:58 2002 +++ b/net/core/sock.c Tue Feb 19 18:08:58 2002 @@ -7,7 +7,7 @@ * handler for protocols to use and generic option handler. * * - * Version: $Id: sock.c,v 1.116 2001/11/08 04:20:06 davem Exp $ + * Version: $Id: sock.c,v 1.117 2002/02/01 22:01:03 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -574,19 +574,33 @@ static kmem_cache_t *sk_cachep; -/* - * All socket objects are allocated here. This is for future - * usage. +/** + * sk_alloc - All socket objects are allocated here + * @family - protocol family + * @priority - for allocation (%GFP_KERNEL, %GFP_ATOMIC, etc) + * @zero_it - zeroes the allocated sock + * @slab - alternate slab + * + * All socket objects are allocated here. If @zero_it is non-zero + * it should have the size of the are to be zeroed, because the + * private slabcaches have different sizes of the generic struct sock. + * 1 has been kept as a way to say sizeof(struct sock). */ - -struct sock *sk_alloc(int family, int priority, int zero_it) +struct sock *sk_alloc(int family, int priority, int zero_it, kmem_cache_t *slab) { - struct sock *sk = kmem_cache_alloc(sk_cachep, priority); - - if(sk && zero_it) { - memset(sk, 0, sizeof(struct sock)); - sk->family = family; - sock_lock_init(sk); + struct sock *sk; + + if (!slab) + slab = sk_cachep; + sk = kmem_cache_alloc(slab, priority); + if (sk) { + if (zero_it) { + memset(sk, 0, + zero_it == 1 ? sizeof(struct sock) : zero_it); + sk->family = family; + sock_lock_init(sk); + } + sk->slab = slab; } return sk; @@ -612,7 +626,7 @@ if (atomic_read(&sk->omem_alloc)) printk(KERN_DEBUG "sk_free: optmem leakage (%d bytes) detected.\n", atomic_read(&sk->omem_alloc)); - kmem_cache_free(sk_cachep, sk); + kmem_cache_free(sk->slab, sk); } void __init sk_init(void) @@ -1160,8 +1174,8 @@ void sock_def_destruct(struct sock *sk) { - if (sk->protinfo.destruct_hook) - kfree(sk->protinfo.destruct_hook); + if (sk->protinfo) + kfree(sk->protinfo); } void sock_init_data(struct socket *sock, struct sock *sk) diff -Nru a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c --- a/net/decnet/af_decnet.c Tue Feb 19 18:08:56 2002 +++ b/net/decnet/af_decnet.c Tue Feb 19 18:08:56 2002 @@ -113,6 +113,7 @@ #include #include #include +#include #include #include #include @@ -472,14 +473,17 @@ struct sock *sk; struct dn_scp *scp; - if ((sk = sk_alloc(PF_DECnet, gfp, 1)) == NULL) + if ((sk = sk_alloc(PF_DECnet, gfp, 1, NULL)) == NULL) goto no_sock; + scp = kmalloc(sizeof(*scp), gfp); + if (!scp) + goto free_sock; if (sock) { sock->ops = &dn_proto_ops; } sock_init_data(sock,sk); - scp = DN_SK(sk); + DN_SK(sk) = scp; sk->backlog_rcv = dn_nsp_backlog_rcv; sk->destruct = dn_destruct; @@ -540,6 +544,8 @@ MOD_INC_USE_COUNT; return sk; +free_sock: + sk_free(sk); no_sock: return NULL; } diff -Nru a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c --- a/net/decnet/dn_nsp_in.c Tue Feb 19 18:08:59 2002 +++ b/net/decnet/dn_nsp_in.c Tue Feb 19 18:08:59 2002 @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -99,7 +101,7 @@ switch(type) { case 0: /* ACK - Data */ - if (after(ack, scp->ackrcv_dat)) { + if (dn_after(ack, scp->ackrcv_dat)) { scp->ackrcv_dat = ack & 0x0fff; wakeup |= dn_nsp_check_xmit_queue(sk, skb, &scp->data_xmit_queue, ack); } @@ -107,7 +109,7 @@ case 1: /* NAK - Data */ break; case 2: /* ACK - OtherData */ - if (after(ack, scp->ackrcv_oth)) { + if (dn_after(ack, scp->ackrcv_oth)) { scp->ackrcv_oth = ack & 0x0fff; wakeup |= dn_nsp_check_xmit_queue(sk, skb, &scp->other_xmit_queue, ack); } diff -Nru a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c --- a/net/decnet/dn_nsp_out.c Tue Feb 19 18:08:59 2002 +++ b/net/decnet/dn_nsp_out.c Tue Feb 19 18:08:59 2002 @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -401,7 +402,7 @@ while(list != skb2) { struct dn_skb_cb *cb2 = DN_SKB_CB(skb2); - if (before_or_equal(cb2->segnum, acknum)) + if (dn_before_or_equal(cb2->segnum, acknum)) ack = skb2; /* printk(KERN_DEBUG "ack: %s %04x %04x\n", ack ? "ACK" : "SKIP", (int)cb2->segnum, (int)acknum); */ @@ -438,7 +439,7 @@ * further. */ if (xmit_count == 1) { - if (equal(segnum, acknum)) + if (dn_equal(segnum, acknum)) dn_nsp_rtt(sk, (long)(pkttime - reftime)); if (scp->snd_window < scp->max_window) diff -Nru a/net/econet/af_econet.c b/net/econet/af_econet.c --- a/net/econet/af_econet.c Tue Feb 19 18:09:00 2002 +++ b/net/econet/af_econet.c Tue Feb 19 18:09:00 2002 @@ -167,6 +167,7 @@ { struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr; struct sock *sk=sock->sk; + struct econet_opt *eo = ec_sk(sk); /* * Check legality @@ -176,10 +177,10 @@ sec->sec_family != AF_ECONET) return -EINVAL; - sk->protinfo.af_econet->cb = sec->cb; - sk->protinfo.af_econet->port = sec->port; - sk->protinfo.af_econet->station = sec->addr.station; - sk->protinfo.af_econet->net = sec->addr.net; + eo->cb = sec->cb; + eo->port = sec->port; + eo->station = sec->addr.station; + eo->net = sec->addr.net; return 0; } @@ -265,10 +266,12 @@ */ if (saddr == NULL) { - addr.station = sk->protinfo.af_econet->station; - addr.net = sk->protinfo.af_econet->net; - port = sk->protinfo.af_econet->port; - cb = sk->protinfo.af_econet->cb; + struct econet_opt *eo = ec_sk(sk); + + addr.station = eo->station; + addr.net = eo->net; + port = eo->port; + cb = eo->cb; } else { if (msg->msg_namelen < sizeof(struct sockaddr_ec)) return -EINVAL; @@ -449,15 +452,16 @@ int *uaddr_len, int peer) { struct sock *sk = sock->sk; + struct econet_opt *eo = ec_sk(sk); struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr; if (peer) return -EOPNOTSUPP; - sec->sec_family = AF_ECONET; - sec->port = sk->protinfo.af_econet->port; - sec->addr.station = sk->protinfo.af_econet->station; - sec->addr.net = sk->protinfo.af_econet->net; + sec->sec_family = AF_ECONET; + sec->port = eo->port; + sec->addr.station = eo->station; + sec->addr.net = eo->net; *uaddr_len = sizeof(*sec); return 0; @@ -525,6 +529,7 @@ static int econet_create(struct socket *sock, int protocol) { struct sock *sk; + struct econet_opt *eo; int err; /* Econet only provides datagram services. */ @@ -535,7 +540,7 @@ MOD_INC_USE_COUNT; err = -ENOBUFS; - sk = sk_alloc(PF_ECONET, GFP_KERNEL, 1); + sk = sk_alloc(PF_ECONET, GFP_KERNEL, 1, NULL); if (sk == NULL) goto out; @@ -543,10 +548,10 @@ sock->ops = &econet_ops; sock_init_data(sock,sk); - sk->protinfo.af_econet = kmalloc(sizeof(struct econet_opt), GFP_KERNEL); - if (sk->protinfo.af_econet == NULL) + eo = ec_sk(sk) = kmalloc(sizeof(*eo), GFP_KERNEL); + if (!eo) goto out_free; - memset(sk->protinfo.af_econet, 0, sizeof(struct econet_opt)); + memset(eo, 0, sizeof(*eo)); sk->zapped=0; sk->family = PF_ECONET; sk->num = protocol; @@ -731,7 +736,7 @@ while (sk) { - struct econet_opt *opt = sk->protinfo.af_econet; + struct econet_opt *opt = ec_sk(sk); if ((opt->port == port || opt->port == 0) && (opt->station == station || opt->station == 0) && (opt->net == net || opt->net == 0)) diff -Nru a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c --- a/net/ipv4/af_inet.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv4/af_inet.c Tue Feb 19 18:08:58 2002 @@ -5,7 +5,7 @@ * * PF_INET protocol family socket handler. * - * Version: $Id: af_inet.c,v 1.136 2001/11/06 22:21:08 davem Exp $ + * Version: $Id: af_inet.c,v 1.137 2002/02/01 22:01:03 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -147,6 +147,11 @@ int (*vlan_ioctl_hook)(unsigned long arg); #endif +/* Per protocol sock slabcache */ +kmem_cache_t *tcp_sk_cachep; +static kmem_cache_t *udp_sk_cachep; +static kmem_cache_t *raw4_sk_cachep; + /* The inetsw table contains everything that inet_create needs to * build a new socket. */ @@ -156,6 +161,8 @@ void inet_sock_destruct(struct sock *sk) { + struct inet_opt *inet = inet_sk(sk); + __skb_queue_purge(&sk->receive_queue); __skb_queue_purge(&sk->error_queue); @@ -175,8 +182,8 @@ BUG_TRAP(sk->wmem_queued == 0); BUG_TRAP(sk->forward_alloc == 0); - if (sk->protinfo.af_inet.opt) - kfree(sk->protinfo.af_inet.opt); + if (inet->opt) + kfree(inet->opt); dst_release(sk->dst_cache); #ifdef INET_REFCNT_DEBUG atomic_dec(&inet_sock_nr); @@ -312,6 +319,28 @@ return err; } +static __inline__ kmem_cache_t *inet_sk_slab(int protocol) +{ + kmem_cache_t* rc = tcp_sk_cachep; + + if (protocol == IPPROTO_UDP) + rc = udp_sk_cachep; + else if (protocol == IPPROTO_RAW) + rc = raw4_sk_cachep; + return rc; +} + +static __inline__ int inet_sk_size(int protocol) +{ + int rc = sizeof(struct tcp_sock); + + if (protocol == IPPROTO_UDP) + rc = sizeof(struct udp_sock); + else if (protocol == IPPROTO_RAW) + rc = sizeof(struct raw_sock); + return rc; +} + /* * Create an inet socket. */ @@ -321,9 +350,11 @@ struct sock *sk; struct list_head *p; struct inet_protosw *answer; + struct inet_opt *inet; sock->state = SS_UNCONNECTED; - sk = sk_alloc(PF_INET, GFP_KERNEL, 1); + sk = sk_alloc(PF_INET, GFP_KERNEL, inet_sk_size(protocol), + inet_sk_slab(protocol)); if (sk == NULL) goto do_oom; @@ -363,18 +394,20 @@ if (INET_PROTOSW_REUSE & answer->flags) sk->reuse = 1; + inet = inet_sk(sk); + if (SOCK_RAW == sock->type) { sk->num = protocol; if (IPPROTO_RAW == protocol) - sk->protinfo.af_inet.hdrincl = 1; + inet->hdrincl = 1; } if (ipv4_config.no_pmtu_disc) - sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_DONT; + inet->pmtudisc = IP_PMTUDISC_DONT; else - sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_WANT; + inet->pmtudisc = IP_PMTUDISC_WANT; - sk->protinfo.af_inet.id = 0; + inet->id = 0; sock_init_data(sock,sk); @@ -386,12 +419,12 @@ sk->backlog_rcv = sk->prot->backlog_rcv; - sk->protinfo.af_inet.ttl = sysctl_ip_default_ttl; + inet->ttl = sysctl_ip_default_ttl; - sk->protinfo.af_inet.mc_loop = 1; - sk->protinfo.af_inet.mc_ttl = 1; - sk->protinfo.af_inet.mc_index = 0; - sk->protinfo.af_inet.mc_list = NULL; + inet->mc_loop = 1; + inet->mc_ttl = 1; + inet->mc_index = 0; + inet->mc_list = NULL; #ifdef INET_REFCNT_DEBUG atomic_inc(&inet_sock_nr); @@ -474,6 +507,7 @@ { struct sockaddr_in *addr=(struct sockaddr_in *)uaddr; struct sock *sk=sock->sk; + struct inet_opt *inet = inet_sk(sk); unsigned short snum; int chk_addr_ret; int err; @@ -495,7 +529,7 @@ * is temporarily down) */ if (sysctl_ip_nonlocal_bind == 0 && - sk->protinfo.af_inet.freebind == 0 && + inet->freebind == 0 && addr->sin_addr.s_addr != INADDR_ANY && chk_addr_ret != RTN_LOCAL && chk_addr_ret != RTN_MULTICAST && @@ -992,8 +1026,8 @@ }; struct net_proto_family inet_family_ops = { - family: PF_INET, - create: inet_create + family: PF_INET, + create: inet_create, }; @@ -1120,6 +1154,18 @@ return -EINVAL; } + tcp_sk_cachep = kmem_cache_create("tcp_sock", + sizeof(struct tcp_sock), 0, + SLAB_HWCACHE_ALIGN, 0, 0); + udp_sk_cachep = kmem_cache_create("udp_sock", + sizeof(struct udp_sock), 0, + SLAB_HWCACHE_ALIGN, 0, 0); + raw4_sk_cachep = kmem_cache_create("raw4_sock", + sizeof(struct raw_sock), 0, + SLAB_HWCACHE_ALIGN, 0, 0); + if (!tcp_sk_cachep || !udp_sk_cachep || !raw4_sk_cachep) + printk(KERN_CRIT __FUNCTION__ + ": Can't create protocol sock SLAB caches!\n"); /* * Tell SOCKET that we are alive... */ diff -Nru a/net/ipv4/icmp.c b/net/ipv4/icmp.c --- a/net/ipv4/icmp.c Tue Feb 19 18:08:59 2002 +++ b/net/ipv4/icmp.c Tue Feb 19 18:08:59 2002 @@ -3,7 +3,7 @@ * * Alan Cox, * - * Version: $Id: icmp.c,v 1.83 2001/12/13 09:00:19 davem Exp $ + * Version: $Id: icmp.c,v 1.85 2002/02/01 22:01:03 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -338,6 +338,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) { struct sock *sk=icmp_socket->sk; + struct inet_opt *inet = inet_sk(sk); struct ipcm_cookie ipc; struct rtable *rt = (struct rtable*)skb->dst; u32 daddr; @@ -352,7 +353,7 @@ icmp_param->csum=0; icmp_out_count(icmp_param->data.icmph.type); - sk->protinfo.af_inet.tos = skb->nh.iph->tos; + inet->tos = skb->nh.iph->tos; daddr = ipc.addr = rt->rt_src; ipc.opt = NULL; if (icmp_param->replyopts.optlen) { @@ -496,7 +497,7 @@ icmp_param.skb=skb_in; icmp_param.offset=skb_in->nh.raw - skb_in->data; icmp_out_count(icmp_param.data.icmph.type); - icmp_socket->sk->protinfo.af_inet.tos = tos; + inet_sk(icmp_socket->sk)->tos = tos; ipc.addr = iph->saddr; ipc.opt = &icmp_param.replyopts; if (icmp_param.replyopts.srr) { @@ -978,14 +979,16 @@ void __init icmp_init(struct net_proto_family *ops) { + struct inet_opt *inet; int err = sock_create(PF_INET, SOCK_RAW, IPPROTO_ICMP, &icmp_socket); if (err < 0) panic("Failed to create the ICMP control socket.\n"); icmp_socket->sk->allocation=GFP_ATOMIC; icmp_socket->sk->sndbuf = SK_WMEM_MAX*2; - icmp_socket->sk->protinfo.af_inet.ttl = MAXTTL; - icmp_socket->sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_DONT; + inet = inet_sk(icmp_socket->sk); + inet->ttl = MAXTTL; + inet->pmtudisc = IP_PMTUDISC_DONT; /* Unhash it so that IP input processing does not even * see it, we do not wish this socket to see incoming diff -Nru a/net/ipv4/igmp.c b/net/ipv4/igmp.c --- a/net/ipv4/igmp.c Tue Feb 19 18:08:59 2002 +++ b/net/ipv4/igmp.c Tue Feb 19 18:08:59 2002 @@ -8,7 +8,7 @@ * the older version didn't come out right using gcc 2.5.8, the newer one * seems to fall out with gcc 2.6.2. * - * Version: $Id: igmp.c,v 1.46 2001/07/27 09:27:29 davem Exp $ + * Version: $Id: igmp.c,v 1.47 2002/02/01 22:01:03 davem Exp $ * * Authors: * Alan Cox @@ -644,6 +644,7 @@ u32 addr = imr->imr_multiaddr.s_addr; struct ip_mc_socklist *iml, *i; struct in_device *in_dev; + struct inet_opt *inet = inet_sk(sk); int count = 0; if (!MULTICAST(addr)) @@ -668,7 +669,7 @@ iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); err = -EADDRINUSE; - for (i=sk->protinfo.af_inet.mc_list; i; i=i->next) { + for (i = inet->mc_list; i; i = i->next) { if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) { /* New style additions are reference counted */ if (imr->imr_address.s_addr == 0) { @@ -683,9 +684,9 @@ if (iml == NULL || count >= sysctl_igmp_max_memberships) goto done; memcpy(&iml->multi, imr, sizeof(*imr)); - iml->next = sk->protinfo.af_inet.mc_list; + iml->next = inet->mc_list; iml->count = 1; - sk->protinfo.af_inet.mc_list = iml; + inet->mc_list = iml; ip_mc_inc_group(in_dev, addr); iml = NULL; err = 0; @@ -703,10 +704,11 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) { + struct inet_opt *inet = inet_sk(sk); struct ip_mc_socklist *iml, **imlp; rtnl_lock(); - for (imlp=&sk->protinfo.af_inet.mc_list; (iml=*imlp)!=NULL; imlp=&iml->next) { + for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) { if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr && iml->multi.imr_address.s_addr==imr->imr_address.s_addr && (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) { @@ -738,15 +740,16 @@ void ip_mc_drop_socket(struct sock *sk) { + struct inet_opt *inet = inet_sk(sk); struct ip_mc_socklist *iml; - if (sk->protinfo.af_inet.mc_list == NULL) + if (inet->mc_list == NULL) return; rtnl_lock(); - while ((iml=sk->protinfo.af_inet.mc_list) != NULL) { + while ((iml = inet->mc_list) != NULL) { struct in_device *in_dev; - sk->protinfo.af_inet.mc_list = iml->next; + inet->mc_list = iml->next; if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL) { ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); diff -Nru a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c --- a/net/ipv4/ip_output.c Tue Feb 19 18:08:59 2002 +++ b/net/ipv4/ip_output.c Tue Feb 19 18:08:59 2002 @@ -5,7 +5,7 @@ * * The Internet Protocol (IP) output module. * - * Version: $Id: ip_output.c,v 1.99 2001/10/15 12:34:50 davem Exp $ + * Version: $Id: ip_output.c,v 1.100 2002/02/01 22:01:03 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -122,6 +122,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, u32 saddr, u32 daddr, struct ip_options *opt) { + struct inet_opt *inet = inet_sk(sk); struct rtable *rt = (struct rtable *)skb->dst; struct iphdr *iph; @@ -133,11 +134,11 @@ iph->version = 4; iph->ihl = 5; - iph->tos = sk->protinfo.af_inet.tos; + iph->tos = inet->tos; iph->frag_off = 0; if (ip_dont_fragment(sk, &rt->u.dst)) iph->frag_off |= htons(IP_DF); - iph->ttl = sk->protinfo.af_inet.ttl; + iph->ttl = inet->ttl; iph->daddr = rt->rt_dst; iph->saddr = rt->rt_src; iph->protocol = sk->protocol; @@ -214,7 +215,7 @@ */ if (rt->rt_flags&RTCF_MULTICAST) { - if ((!sk || sk->protinfo.af_inet.mc_loop) + if ((!sk || inet_sk(sk)->mc_loop) #ifdef CONFIG_IP_MROUTE /* Small optimization: do not loopback not local frames, which returned after forwarding; they will be dropped @@ -341,7 +342,8 @@ int ip_queue_xmit(struct sk_buff *skb) { struct sock *sk = skb->sk; - struct ip_options *opt = sk->protinfo.af_inet.opt; + struct inet_opt *inet = inet_sk(sk); + struct ip_options *opt = inet->opt; struct rtable *rt; struct iphdr *iph; @@ -381,10 +383,10 @@ /* OK, we know where to send it, allocate and build IP header. */ iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); - *((__u16 *)iph) = htons((4 << 12) | (5 << 8) | (sk->protinfo.af_inet.tos & 0xff)); + *((__u16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff)); iph->tot_len = htons(skb->len); iph->frag_off = 0; - iph->ttl = sk->protinfo.af_inet.ttl; + iph->ttl = inet->ttl; iph->protocol = sk->protocol; iph->saddr = rt->rt_src; iph->daddr = rt->rt_dst; @@ -436,6 +438,7 @@ struct rtable *rt, int flags) { + struct inet_opt *inet = inet_sk(sk); unsigned int fraglen, maxfraglen, fragheaderlen; int err; int offset, mf; @@ -499,7 +502,7 @@ * Don't fragment packets for path mtu discovery. */ - if (offset > 0 && sk->protinfo.af_inet.pmtudisc==IP_PMTUDISC_DO) { + if (offset > 0 && inet->pmtudisc == IP_PMTUDISC_DO) { ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu); return -EMSGSIZE; } @@ -510,7 +513,7 @@ * Begin outputting the bytes. */ - id = sk->protinfo.af_inet.id++; + id = inet->id++; do { char *data; @@ -553,7 +556,7 @@ ip_options_build(skb, opt, ipc->addr, rt, offset); } - iph->tos = sk->protinfo.af_inet.tos; + iph->tos = inet->tos; iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4); iph->frag_off = htons(offset>>3)|mf|df; iph->id = id; @@ -573,9 +576,9 @@ mf = htons(IP_MF); } if (rt->rt_type == RTN_MULTICAST) - iph->ttl = sk->protinfo.af_inet.mc_ttl; + iph->ttl = inet->mc_ttl; else - iph->ttl = sk->protinfo.af_inet.ttl; + iph->ttl = inet->ttl; iph->protocol = sk->protocol; iph->check = 0; iph->saddr = rt->rt_src; @@ -603,7 +606,7 @@ skb->dst->dev, output_maybe_reroute); if (err) { if (err > 0) - err = sk->protinfo.af_inet.recverr ? net_xmit_errno(err) : 0; + err = inet->recverr ? net_xmit_errno(err) : 0; if (err) goto error; } @@ -635,6 +638,7 @@ struct rtable *rt, int flags) { + struct inet_opt *inet = inet_sk(sk); int err; struct sk_buff *skb; int df; @@ -645,7 +649,7 @@ * choice RAW frames within 20 bytes of maximum size(rare) to the long path */ - if (!sk->protinfo.af_inet.hdrincl) { + if (!inet->hdrincl) { length += sizeof(struct iphdr); /* @@ -687,16 +691,16 @@ skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length); - if(!sk->protinfo.af_inet.hdrincl) { + if (!inet->hdrincl) { iph->version=4; iph->ihl=5; - iph->tos=sk->protinfo.af_inet.tos; + iph->tos = inet->tos; iph->tot_len = htons(length); iph->frag_off = df; - iph->ttl=sk->protinfo.af_inet.mc_ttl; + iph->ttl = inet->mc_ttl; ip_select_ident(iph, &rt->u.dst, sk); if (rt->rt_type != RTN_MULTICAST) - iph->ttl=sk->protinfo.af_inet.ttl; + iph->ttl = inet->ttl; iph->protocol=sk->protocol; iph->saddr=rt->rt_src; iph->daddr=rt->rt_dst; @@ -713,7 +717,7 @@ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, output_maybe_reroute); if (err > 0) - err = sk->protinfo.af_inet.recverr ? net_xmit_errno(err) : 0; + err = inet->recverr ? net_xmit_errno(err) : 0; if (err) goto error; out: @@ -943,6 +947,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *arg, unsigned int len) { + struct inet_opt *inet = inet_sk(sk); struct { struct ip_options opt; char data[40]; @@ -974,7 +979,7 @@ with locally disabled BH and that sk cannot be already spinlocked. */ bh_lock_sock(sk); - sk->protinfo.af_inet.tos = skb->nh.iph->tos; + inet->tos = skb->nh.iph->tos; sk->priority = skb->priority; sk->protocol = skb->nh.iph->protocol; ip_build_xmit(sk, ip_reply_glue_bits, arg, len, &ipc, rt, MSG_DONTWAIT); diff -Nru a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c --- a/net/ipv4/ip_sockglue.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv4/ip_sockglue.c Tue Feb 19 18:08:58 2002 @@ -5,7 +5,7 @@ * * The IP to API glue. * - * Version: $Id: ip_sockglue.c,v 1.61 2001/10/20 00:00:11 davem Exp $ + * Version: $Id: ip_sockglue.c,v 1.62 2002/02/01 22:01:04 davem Exp $ * * Authors: see ip.c * @@ -110,7 +110,8 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) { - unsigned flags = skb->sk->protinfo.af_inet.cmsg_flags; + struct inet_opt *inet = inet_sk(skb->sk); + unsigned flags = inet->cmsg_flags; /* Ordered by supposed usage frequency */ if (flags & 1) @@ -234,9 +235,10 @@ void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, u16 port, u32 info, u8 *payload) { + struct inet_opt *inet = inet_sk(sk); struct sock_exterr_skb *serr; - if (!sk->protinfo.af_inet.recverr) + if (!inet->recverr) return; skb = skb_clone(skb, GFP_ATOMIC); @@ -262,11 +264,12 @@ void ip_local_error(struct sock *sk, int err, u32 daddr, u16 port, u32 info) { + struct inet_opt *inet = inet_sk(sk); struct sock_exterr_skb *serr; struct iphdr *iph; struct sk_buff *skb; - if (!sk->protinfo.af_inet.recverr) + if (!inet->recverr) return; skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC); @@ -340,11 +343,13 @@ sin = &errhdr.offender; sin->sin_family = AF_UNSPEC; if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) { + struct inet_opt *inet = inet_sk(sk); + sin->sin_family = AF_INET; sin->sin_addr.s_addr = skb->nh.iph->saddr; sin->sin_port = 0; memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); - if (sk->protinfo.af_inet.cmsg_flags) + if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); } @@ -380,6 +385,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) { + struct inet_opt *inet = inet_sk(sk); int val=0,err; if (level != SOL_IP) @@ -425,7 +431,7 @@ if (err) break; if (sk->type == SOCK_STREAM) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) if (sk->family == PF_INET || (!((1<state)&(TCPF_LISTEN|TCPF_CLOSE)) @@ -438,53 +444,53 @@ } #endif } - opt = xchg(&sk->protinfo.af_inet.opt, opt); + opt = xchg(&inet->opt, opt); if (opt) kfree(opt); break; } case IP_PKTINFO: if (val) - sk->protinfo.af_inet.cmsg_flags |= IP_CMSG_PKTINFO; + inet->cmsg_flags |= IP_CMSG_PKTINFO; else - sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_PKTINFO; + inet->cmsg_flags &= ~IP_CMSG_PKTINFO; break; case IP_RECVTTL: if (val) - sk->protinfo.af_inet.cmsg_flags |= IP_CMSG_TTL; + inet->cmsg_flags |= IP_CMSG_TTL; else - sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_TTL; + inet->cmsg_flags &= ~IP_CMSG_TTL; break; case IP_RECVTOS: if (val) - sk->protinfo.af_inet.cmsg_flags |= IP_CMSG_TOS; + inet->cmsg_flags |= IP_CMSG_TOS; else - sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_TOS; + inet->cmsg_flags &= ~IP_CMSG_TOS; break; case IP_RECVOPTS: if (val) - sk->protinfo.af_inet.cmsg_flags |= IP_CMSG_RECVOPTS; + inet->cmsg_flags |= IP_CMSG_RECVOPTS; else - sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_RECVOPTS; + inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; break; case IP_RETOPTS: if (val) - sk->protinfo.af_inet.cmsg_flags |= IP_CMSG_RETOPTS; + inet->cmsg_flags |= IP_CMSG_RETOPTS; else - sk->protinfo.af_inet.cmsg_flags &= ~IP_CMSG_RETOPTS; + inet->cmsg_flags &= ~IP_CMSG_RETOPTS; break; case IP_TOS: /* This sets both TOS and Precedence */ if (sk->type == SOCK_STREAM) { val &= ~3; - val |= sk->protinfo.af_inet.tos & 3; + val |= inet->tos & 3; } if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && !capable(CAP_NET_ADMIN)) { err = -EPERM; break; } - if (sk->protinfo.af_inet.tos != val) { - sk->protinfo.af_inet.tos=val; + if (inet->tos != val) { + inet->tos = val; sk->priority = rt_tos2priority(val); sk_dst_reset(sk); } @@ -496,22 +502,22 @@ val = sysctl_ip_default_ttl; if(val<1||val>255) goto e_inval; - sk->protinfo.af_inet.ttl=val; + inet->ttl = val; break; case IP_HDRINCL: if(sk->type!=SOCK_RAW) { err = -ENOPROTOOPT; break; } - sk->protinfo.af_inet.hdrincl=val?1:0; + inet->hdrincl = val ? 1 : 0; break; case IP_MTU_DISCOVER: if (val<0 || val>2) goto e_inval; - sk->protinfo.af_inet.pmtudisc = val; + inet->pmtudisc = val; break; case IP_RECVERR: - sk->protinfo.af_inet.recverr = !!val; + inet->recverr = !!val; if (!val) skb_queue_purge(&sk->error_queue); break; @@ -524,12 +530,12 @@ val = 1; if (val < 0 || val > 255) goto e_inval; - sk->protinfo.af_inet.mc_ttl=val; + inet->mc_ttl = val; break; case IP_MULTICAST_LOOP: if (optlen<1) goto e_inval; - sk->protinfo.af_inet.mc_loop = val ? 1 : 0; + inet->mc_loop = !!val; break; case IP_MULTICAST_IF: { @@ -555,8 +561,8 @@ if (!mreq.imr_ifindex) { if (mreq.imr_address.s_addr == INADDR_ANY) { - sk->protinfo.af_inet.mc_index = 0; - sk->protinfo.af_inet.mc_addr = 0; + inet->mc_index = 0; + inet->mc_addr = 0; err = 0; break; } @@ -577,8 +583,8 @@ if (sk->bound_dev_if && mreq.imr_ifindex != sk->bound_dev_if) break; - sk->protinfo.af_inet.mc_index = mreq.imr_ifindex; - sk->protinfo.af_inet.mc_addr = mreq.imr_address.s_addr; + inet->mc_index = mreq.imr_ifindex; + inet->mc_addr = mreq.imr_address.s_addr; err = 0; break; } @@ -613,7 +619,7 @@ case IP_FREEBIND: if (optlen<1) goto e_inval; - sk->protinfo.af_inet.freebind = !!val; + inet->freebind = !!val; break; default: @@ -640,6 +646,7 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) { + struct inet_opt *inet = inet_sk(sk); int val; int len; @@ -666,10 +673,10 @@ unsigned char optbuf[sizeof(struct ip_options)+40]; struct ip_options * opt = (struct ip_options*)optbuf; opt->optlen = 0; - if (sk->protinfo.af_inet.opt) - memcpy(optbuf, sk->protinfo.af_inet.opt, + if (inet->opt) + memcpy(optbuf, inet->opt, sizeof(struct ip_options)+ - sk->protinfo.af_inet.opt->optlen); + inet->opt->optlen); release_sock(sk); if (opt->optlen == 0) @@ -685,31 +692,31 @@ return 0; } case IP_PKTINFO: - val = (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_PKTINFO) != 0; + val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; break; case IP_RECVTTL: - val = (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_TTL) != 0; + val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; break; case IP_RECVTOS: - val = (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_TOS) != 0; + val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; break; case IP_RECVOPTS: - val = (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_RECVOPTS) != 0; + val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; break; case IP_RETOPTS: - val = (sk->protinfo.af_inet.cmsg_flags & IP_CMSG_RETOPTS) != 0; + val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; break; case IP_TOS: - val=sk->protinfo.af_inet.tos; + val = inet->tos; break; case IP_TTL: - val=sk->protinfo.af_inet.ttl; + val = inet->ttl; break; case IP_HDRINCL: - val=sk->protinfo.af_inet.hdrincl; + val = inet->hdrincl; break; case IP_MTU_DISCOVER: - val=sk->protinfo.af_inet.pmtudisc; + val = inet->pmtudisc; break; case IP_MTU: { @@ -727,19 +734,19 @@ break; } case IP_RECVERR: - val=sk->protinfo.af_inet.recverr; + val = inet->recverr; break; case IP_MULTICAST_TTL: - val=sk->protinfo.af_inet.mc_ttl; + val = inet->mc_ttl; break; case IP_MULTICAST_LOOP: - val=sk->protinfo.af_inet.mc_loop; + val = inet->mc_loop; break; case IP_MULTICAST_IF: { struct in_addr addr; len = min_t(unsigned int, len, sizeof(struct in_addr)); - addr.s_addr = sk->protinfo.af_inet.mc_addr; + addr.s_addr = inet->mc_addr; release_sock(sk); if(put_user(len, optlen)) @@ -761,23 +768,23 @@ msg.msg_controllen = len; msg.msg_flags = 0; - if (sk->protinfo.af_inet.cmsg_flags&IP_CMSG_PKTINFO) { + if (inet->cmsg_flags & IP_CMSG_PKTINFO) { struct in_pktinfo info; info.ipi_addr.s_addr = sk->rcv_saddr; info.ipi_spec_dst.s_addr = sk->rcv_saddr; - info.ipi_ifindex = sk->protinfo.af_inet.mc_index; + info.ipi_ifindex = inet->mc_index; put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); } - if (sk->protinfo.af_inet.cmsg_flags&IP_CMSG_TTL) { - int hlim = sk->protinfo.af_inet.mc_ttl; + if (inet->cmsg_flags & IP_CMSG_TTL) { + int hlim = inet->mc_ttl; put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); } len -= msg.msg_controllen; return put_user(len, optlen); } case IP_FREEBIND: - val = sk->protinfo.af_inet.freebind; + val = inet->freebind; break; default: #ifdef CONFIG_NETFILTER diff -Nru a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c --- a/net/ipv4/ipconfig.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv4/ipconfig.c Tue Feb 19 18:08:57 2002 @@ -1,5 +1,5 @@ /* - * $Id: ipconfig.c,v 1.45 2002/01/08 16:00:20 davem Exp $ + * $Id: ipconfig.c,v 1.46 2002/02/01 22:01:04 davem Exp $ * * Automatic Configuration of IP -- use DHCP, BOOTP, RARP, or * user-supplied information to configure own IP address and routes. @@ -53,7 +53,7 @@ #include #include -#include +#include #include /* Define this to allow debugging output */ diff -Nru a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c --- a/net/ipv4/netfilter/ip_conntrack_core.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv4/netfilter/ip_conntrack_core.c Tue Feb 19 18:08:57 2002 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff -Nru a/net/ipv4/netfilter/ip_conntrack_irc.c b/net/ipv4/netfilter/ip_conntrack_irc.c --- a/net/ipv4/netfilter/ip_conntrack_irc.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv4/netfilter/ip_conntrack_irc.c Tue Feb 19 18:08:58 2002 @@ -1,8 +1,8 @@ -/* IRC extension for IP connection tracking, Version 1.20 - * (C) 2000-2001 by Harald Welte +/* IRC extension for IP connection tracking, Version 1.21 + * (C) 2000-2002 by Harald Welte * based on RR's ip_conntrack_ftp.c * - * ip_conntrack_irc.c,v 1.20 2001/12/06 07:42:10 laforge Exp + * ip_conntrack_irc.c,v 1.21 2002/02/05 14:49:26 laforge Exp * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -112,9 +112,9 @@ struct ip_ct_irc *info = &ct->help.ct_irc_info; - memset(&mask, 0, sizeof(struct ip_conntrack_tuple)); - mask.dst.u.tcp.port = 0xFFFF; - mask.dst.protonum = 0xFFFF; + mask = ((struct ip_conntrack_tuple) + { { 0, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); DEBUGP("entered\n"); /* Can't track connections formed before we registered */ diff -Nru a/net/ipv4/netfilter/ip_nat_snmp_basic.c b/net/ipv4/netfilter/ip_nat_snmp_basic.c --- a/net/ipv4/netfilter/ip_nat_snmp_basic.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv4/netfilter/ip_nat_snmp_basic.c Tue Feb 19 18:08:58 2002 @@ -52,10 +52,11 @@ #include #include #include +#include #include #include #include -#include +#include diff -Nru a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c --- a/net/ipv4/netfilter/ip_nat_standalone.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv4/netfilter/ip_nat_standalone.c Tue Feb 19 18:08:58 2002 @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -188,7 +189,7 @@ if (ret != NF_DROP && ret != NF_STOLEN && ((*pskb)->nh.iph->saddr != saddr || (*pskb)->nh.iph->daddr != daddr)) - return route_me_harder(pskb) == 0 ? ret : NF_DROP; + return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP; return ret; } diff -Nru a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c --- a/net/ipv4/netfilter/ip_queue.c Tue Feb 19 18:09:00 2002 +++ b/net/ipv4/netfilter/ip_queue.c Tue Feb 19 18:09:00 2002 @@ -261,7 +261,7 @@ if (!(iph->tos == e->rt_info.tos && iph->daddr == e->rt_info.daddr && iph->saddr == e->rt_info.saddr)) - return route_me_harder(&e->skb); + return ip_route_me_harder(&e->skb); } return 0; } diff -Nru a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c --- a/net/ipv4/netfilter/iptable_mangle.c Tue Feb 19 18:08:59 2002 +++ b/net/ipv4/netfilter/iptable_mangle.c Tue Feb 19 18:08:59 2002 @@ -162,7 +162,7 @@ || (*pskb)->nh.iph->daddr != daddr || (*pskb)->nfmark != nfmark || (*pskb)->nh.iph->tos != tos)) - return route_me_harder(pskb) == 0 ? ret : NF_DROP; + return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP; return ret; } diff -Nru a/net/ipv4/raw.c b/net/ipv4/raw.c --- a/net/ipv4/raw.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv4/raw.c Tue Feb 19 18:08:57 2002 @@ -5,7 +5,7 @@ * * RAW - implementation of IP "raw" sockets. * - * Version: $Id: raw.c,v 1.63 2001/07/10 04:29:01 davem Exp $ + * Version: $Id: raw.c,v 1.64 2002/02/01 22:01:04 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -55,7 +55,7 @@ #include #include #include -#include +#include #include #include #include @@ -122,7 +122,7 @@ type = skb->h.icmph->type; if (type < 32) { - __u32 data = sk->tp_pinfo.tp_raw4.filter.data; + __u32 data = raw4_sk(sk)->filter.data; return ((1 << type) & data) != 0; } @@ -176,6 +176,7 @@ void raw_err (struct sock *sk, struct sk_buff *skb, u32 info) { + struct inet_opt *inet = inet_sk(sk); int type = skb->h.icmph->type; int code = skb->h.icmph->code; int err = 0; @@ -186,7 +187,7 @@ 2. Socket is connected (otherwise the error indication is useless without ip_recverr and error is hard. */ - if (!sk->protinfo.af_inet.recverr && sk->state != TCP_ESTABLISHED) + if (!inet->recverr && sk->state != TCP_ESTABLISHED) return; switch (type) { @@ -207,22 +208,21 @@ err = icmp_err_convert[code].errno; harderr = icmp_err_convert[code].fatal; if (code == ICMP_FRAG_NEEDED) { - harderr = sk->protinfo.af_inet.pmtudisc != - IP_PMTUDISC_DONT; + harderr = inet->pmtudisc != IP_PMTUDISC_DONT; err = EMSGSIZE; } } - if (sk->protinfo.af_inet.recverr) { + if (inet->recverr) { struct iphdr *iph = (struct iphdr*)skb->data; u8 *payload = skb->data + (iph->ihl << 2); - if (sk->protinfo.af_inet.hdrincl) + if (inet->hdrincl) payload = skb->data; ip_icmp_error(sk, skb, err, 0, info, payload); } - if (sk->protinfo.af_inet.recverr || harderr) { + if (inet->recverr || harderr) { sk->err = err; sk->error_report(sk); } @@ -304,6 +304,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len) { + struct inet_opt *inet = inet_sk(sk); struct ipcm_cookie ipc; struct rawfakehdr rfh; struct rtable *rt = NULL; @@ -382,14 +383,14 @@ ipc.addr = daddr; if (!ipc.opt) - ipc.opt = sk->protinfo.af_inet.opt; + ipc.opt = inet->opt; if (ipc.opt) { err = -EINVAL; /* Linux does not mangle headers on raw sockets, * so that IP options + IP_HDRINCL is non-sense. */ - if (sk->protinfo.af_inet.hdrincl) + if (inet->hdrincl) goto done; if (ipc.opt->srr) { if (!daddr) @@ -397,15 +398,15 @@ daddr = ipc.opt->faddr; } } - tos = RT_TOS(sk->protinfo.af_inet.tos) | sk->localroute; + tos = RT_TOS(inet->tos) | sk->localroute; if (msg->msg_flags & MSG_DONTROUTE) tos |= RTO_ONLINK; if (MULTICAST(daddr)) { if (!ipc.oif) - ipc.oif = sk->protinfo.af_inet.mc_index; + ipc.oif = inet->mc_index; if (!rfh.saddr) - rfh.saddr = sk->protinfo.af_inet.mc_addr; + rfh.saddr = inet->mc_addr; } err = ip_route_output(&rt, daddr, rfh.saddr, tos, ipc.oif); @@ -426,7 +427,7 @@ rfh.dst = &rt->u.dst; if (!ipc.addr) ipc.addr = rt->rt_dst; - err = ip_build_xmit(sk, sk->protinfo.af_inet.hdrincl ? raw_getrawfrag : + err = ip_build_xmit(sk, inet->hdrincl ? raw_getrawfrag : raw_getfrag, &rfh, len, &ipc, rt, msg->msg_flags); done: @@ -484,6 +485,7 @@ int raw_recvmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, int flags, int *addr_len) { + struct inet_opt *inet = inet_sk(sk); int copied = 0; int err = -EOPNOTSUPP; struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; @@ -522,7 +524,7 @@ sin->sin_addr.s_addr = skb->nh.iph->saddr; memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); } - if (sk->protinfo.af_inet.cmsg_flags) + if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); done: skb_free_datagram(sk, skb); @@ -531,7 +533,7 @@ static int raw_init(struct sock *sk) { - struct raw_opt *tp = &(sk->tp_pinfo.tp_raw4); + struct raw_opt *tp = raw4_sk(sk); if (sk->num == IPPROTO_ICMP) memset(&tp->filter, 0, sizeof(tp->filter)); return 0; @@ -541,7 +543,7 @@ { if (optlen > sizeof(struct icmp_filter)) optlen = sizeof(struct icmp_filter); - if (copy_from_user(&sk->tp_pinfo.tp_raw4.filter, optval, optlen)) + if (copy_from_user(&raw4_sk(sk)->filter, optval, optlen)) return -EFAULT; return 0; } @@ -559,7 +561,7 @@ len = sizeof(struct icmp_filter); ret = -EFAULT; if (put_user(len, optlen) || - copy_to_user(optval, &sk->tp_pinfo.tp_raw4.filter, len)) + copy_to_user(optval, &raw4_sk(sk)->filter, len)) goto out; ret = 0; out: return ret; diff -Nru a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c --- a/net/ipv4/syncookies.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv4/syncookies.c Tue Feb 19 18:08:57 2002 @@ -9,7 +9,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * $Id: syncookies.c,v 1.17 2001/10/26 14:55:41 davem Exp $ + * $Id: syncookies.c,v 1.18 2002/02/01 22:01:04 davem Exp $ * * Missing: IPv6 support. */ @@ -48,11 +48,12 @@ */ __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) { + struct tcp_opt *tp = tcp_sk(sk); int mssind; const __u16 mss = *mssp; - sk->tp_pinfo.af_tcp.last_synq_overflow = jiffies; + tp->last_synq_overflow = jiffies; /* XXX sort msstab[] by probability? Binary search? */ for (mssind = 0; mss > msstab[mssind + 1]; mssind++) @@ -98,7 +99,7 @@ struct open_request *req, struct dst_entry *dst) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sock *child; child = tp->af_specific->syn_recv_sock(sk, skb, req, dst); @@ -113,6 +114,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt) { + struct tcp_opt *tp = tcp_sk(sk); __u32 cookie = ntohl(skb->h.th->ack_seq) - 1; struct sock *ret = sk; struct open_request *req; @@ -123,7 +125,7 @@ if (!sysctl_tcp_syncookies || !skb->h.th->ack) goto out; - if (time_after(jiffies, sk->tp_pinfo.af_tcp.last_synq_overflow + TCP_TIMEOUT_INIT) || + if (time_after(jiffies, tp->last_synq_overflow + TCP_TIMEOUT_INIT) || (mss = cookie_check(skb, cookie)) == 0) { NET_INC_STATS_BH(SyncookiesFailed); goto out; diff -Nru a/net/ipv4/tcp.c b/net/ipv4/tcp.c --- a/net/ipv4/tcp.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv4/tcp.c Tue Feb 19 18:08:58 2002 @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp.c,v 1.215 2001/10/31 08:17:58 davem Exp $ + * Version: $Id: tcp.c,v 1.216 2002/02/01 22:01:04 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -363,7 +363,7 @@ */ static __inline__ unsigned int tcp_listen_poll(struct sock *sk, poll_table *wait) { - return sk->tp_pinfo.af_tcp.accept_queue ? (POLLIN | POLLRDNORM) : 0; + return tcp_sk(sk)->accept_queue ? (POLLIN | POLLRDNORM) : 0; } /* @@ -377,7 +377,7 @@ { unsigned int mask; struct sock *sk = sock->sk; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); poll_wait(file, sk->sleep, wait); if (sk->state == TCP_LISTEN) @@ -477,7 +477,7 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int answ; switch(cmd) { @@ -524,7 +524,7 @@ int tcp_listen_start(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct tcp_listen_opt *lopt; sk->max_ack_backlog = 0; @@ -576,7 +576,7 @@ static void tcp_listen_stop (struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct tcp_listen_opt *lopt = tp->listen_opt; struct open_request *acc_req = tp->accept_queue; struct open_request *req; @@ -647,6 +647,7 @@ */ static int wait_for_tcp_connect(struct sock * sk, int flags, long *timeo_p) { + struct tcp_opt *tp = tcp_sk(sk); struct task_struct *tsk = current; DECLARE_WAITQUEUE(wait, tsk); @@ -663,7 +664,7 @@ __set_task_state(tsk, TASK_INTERRUPTIBLE); add_wait_queue(sk->sleep, &wait); - sk->tp_pinfo.af_tcp.write_pending++; + tp->write_pending++; release_sock(sk); *timeo_p = schedule_timeout(*timeo_p); @@ -671,7 +672,7 @@ __set_task_state(tsk, TASK_RUNNING); remove_wait_queue(sk->sleep, &wait); - sk->tp_pinfo.af_tcp.write_pending--; + tp->write_pending--; } return 0; } @@ -686,6 +687,7 @@ */ static int wait_for_tcp_memory(struct sock * sk, long *timeo) { + struct tcp_opt *tp = tcp_sk(sk); int err = 0; long vm_wait = 0; long current_timeo = *timeo; @@ -711,12 +713,12 @@ break; set_bit(SOCK_NOSPACE, &sk->socket->flags); - sk->tp_pinfo.af_tcp.write_pending++; + tp->write_pending++; release_sock(sk); if (!tcp_memory_free(sk) || vm_wait) current_timeo = schedule_timeout(current_timeo); lock_sock(sk); - sk->tp_pinfo.af_tcp.write_pending--; + tp->write_pending--; if (vm_wait) { vm_wait -= current_timeo; @@ -825,7 +827,7 @@ ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int mss_now; int err; ssize_t copied; @@ -950,8 +952,8 @@ return res; } -#define TCP_PAGE(sk) (sk->tp_pinfo.af_tcp.sndmsg_page) -#define TCP_OFF(sk) (sk->tp_pinfo.af_tcp.sndmsg_off) +#define TCP_PAGE(sk) (tcp_sk(sk)->sndmsg_page) +#define TCP_OFF(sk) (tcp_sk(sk)->sndmsg_off) static inline int tcp_copy_to_page(struct sock *sk, char *from, struct sk_buff *skb, @@ -1008,15 +1010,13 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, int size) { struct iovec *iov; - struct tcp_opt *tp; + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb; int iovlen, flags; int mss_now; int err, copied; long timeo; - tp = &(sk->tp_pinfo.af_tcp); - lock_sock(sk); TCP_CHECK_TIMER(sk); @@ -1216,7 +1216,7 @@ struct msghdr *msg, int len, int flags, int *addr_len) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); /* No URG data to read. */ if (sk->urginline || !tp->urg_data || tp->urg_data == TCP_URG_READ) @@ -1277,7 +1277,7 @@ */ static void cleanup_rbuf(struct sock *sk, int copied) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int time_to_ack = 0; #if TCP_DEBUG @@ -1362,7 +1362,7 @@ static void tcp_prequeue_process(struct sock *sk) { struct sk_buff *skb; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); net_statistics[smp_processor_id()*2+1].TCPPrequeued += skb_queue_len(&tp->ucopy.prequeue); @@ -1387,7 +1387,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, int len, int nonblock, int flags, int *addr_len) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int copied = 0; u32 peek_seq; u32 *seq; @@ -1936,7 +1936,7 @@ */ if (sk->state == TCP_FIN_WAIT2) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); if (tp->linger2 < 0) { tcp_set_state(sk, TCP_CLOSE); tcp_send_active_reset(sk, GFP_ATOMIC); @@ -1988,7 +1988,7 @@ int tcp_disconnect(struct sock *sk, int flags) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); int old_state; int err = 0; @@ -2021,8 +2021,12 @@ sk->rcv_saddr = 0; sk->saddr = 0; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - memset(&sk->net_pinfo.af_inet6.saddr, 0, 16); - memset(&sk->net_pinfo.af_inet6.rcv_saddr, 0, 16); + if (sk->family == PF_INET6) { + struct ipv6_pinfo *np = inet6_sk(sk); + + memset(&np->saddr, 0, 16); + memset(&np->rcv_saddr, 0, 16); + } #endif } @@ -2057,6 +2061,7 @@ */ static int wait_for_connect(struct sock * sk, long timeo) { + struct tcp_opt *tp = tcp_sk(sk); DECLARE_WAITQUEUE(wait, current); int err; @@ -2078,11 +2083,11 @@ for (;;) { current->state = TASK_INTERRUPTIBLE; release_sock(sk); - if (sk->tp_pinfo.af_tcp.accept_queue == NULL) + if (tp->accept_queue == NULL) timeo = schedule_timeout(timeo); lock_sock(sk); err = 0; - if (sk->tp_pinfo.af_tcp.accept_queue) + if (tp->accept_queue) break; err = -EINVAL; if (sk->state != TCP_LISTEN) @@ -2105,7 +2110,7 @@ struct sock *tcp_accept(struct sock *sk, int flags, int *err) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); struct open_request *req; struct sock *newsk; int error; @@ -2157,7 +2162,7 @@ int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int val; int err = 0; @@ -2316,7 +2321,7 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int val, len; if(level != SOL_TCP) diff -Nru a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c --- a/net/ipv4/tcp_diag.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv4/tcp_diag.c Tue Feb 19 18:08:57 2002 @@ -1,7 +1,7 @@ /* * tcp_diag.c Module for monitoring TCP sockets. * - * Version: $Id: tcp_diag.c,v 1.2 2001/11/05 09:42:22 davem Exp $ + * Version: $Id: tcp_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $ * * Authors: Alexey Kuznetsov, * @@ -44,7 +44,7 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, int ext, u32 pid, u32 seq) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); struct tcpdiagmsg *r; struct nlmsghdr *nlh; struct tcp_info *info = NULL; @@ -96,8 +96,10 @@ #ifdef CONFIG_IPV6 if (r->tcpdiag_family == AF_INET6) { - memcpy(r->id.tcpdiag_src, &sk->net_pinfo.af_inet6.rcv_saddr, 16); - memcpy(r->id.tcpdiag_dst, &sk->net_pinfo.af_inet6.daddr, 16); + struct ipv6_pinfo *np = inet6_sk(sk); + + memcpy(r->id.tcpdiag_src, &np->rcv_saddr, 16); + memcpy(r->id.tcpdiag_dst, &np->daddr, 16); } #endif @@ -329,10 +331,12 @@ #ifdef CONFIG_IPV6 if (sk->family == AF_INET6) { + struct ipv6_pinfo *np = inet6_sk(sk); + if (op->code == TCPDIAG_BC_S_COND) - addr = (u32*)&sk->net_pinfo.af_inet6.rcv_saddr; + addr = (u32*)&np->rcv_saddr; else - addr = (u32*)&sk->net_pinfo.af_inet6.daddr; + addr = (u32*)&np->daddr; } else #endif { @@ -441,7 +445,7 @@ goto skip_listen_ht; tcp_listen_lock(); for (i = s_i; i < TCP_LHTABLE_SIZE; i++) { - struct sock *sk = tcp_listening_hash[i]; + struct sock *sk; if (i > s_i) s_num = 0; diff -Nru a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c --- a/net/ipv4/tcp_input.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv4/tcp_input.c Tue Feb 19 18:08:58 2002 @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.242 2001/12/11 06:11:53 davem Exp $ + * Version: $Id: tcp_input.c,v 1.243 2002/02/01 22:01:04 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -190,8 +190,8 @@ static void tcp_fixup_sndbuf(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - int sndmem = tp->mss_clamp+MAX_TCP_HEADER+16+sizeof(struct sk_buff); + int sndmem = tcp_sk(sk)->mss_clamp + MAX_TCP_HEADER + 16 + + sizeof(struct sk_buff); if (sk->sndbuf < 3*sndmem) sk->sndbuf = min(3*sndmem, sysctl_tcp_wmem[2]); @@ -268,8 +268,8 @@ static void tcp_fixup_rcvbuf(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - int rcvmem = tp->advmss+MAX_TCP_HEADER+16+sizeof(struct sk_buff); + struct tcp_opt *tp = tcp_sk(sk); + int rcvmem = tp->advmss + MAX_TCP_HEADER + 16 + sizeof(struct sk_buff); /* Try to select rcvbuf so that 4 mss-sized segments * will fit to window and correspoding skbs will fit to our rcvbuf. @@ -286,7 +286,7 @@ */ static void tcp_init_buffer_space(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int maxwin; if (!(sk->userlocks&SOCK_RCVBUF_LOCK)) @@ -299,15 +299,17 @@ if (tp->window_clamp >= maxwin) { tp->window_clamp = maxwin; - if (sysctl_tcp_app_win && maxwin>4*tp->advmss) - tp->window_clamp = max(maxwin-(maxwin>>sysctl_tcp_app_win), 4*tp->advmss); + if (sysctl_tcp_app_win && maxwin > 4 * tp->advmss) + tp->window_clamp = max(maxwin - + (maxwin >> sysctl_tcp_app_win), + 4 * tp->advmss); } /* Force reservation of one segment. */ if (sysctl_tcp_app_win && - tp->window_clamp > 2*tp->advmss && + tp->window_clamp > 2 * tp->advmss && tp->window_clamp + tp->advmss > maxwin) - tp->window_clamp = max(2*tp->advmss, maxwin-tp->advmss); + tp->window_clamp = max(2 * tp->advmss, maxwin - tp->advmss); tp->rcv_ssthresh = min(tp->rcv_ssthresh, tp->window_clamp); tp->snd_cwnd_stamp = tcp_time_stamp; @@ -512,7 +514,7 @@ */ void tcp_update_metrics(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct dst_entry *dst = __sk_dst_get(sk); dst_confirm(dst); @@ -562,8 +564,8 @@ /* Slow start still did not finish. */ if (dst->ssthresh && !(dst->mxlock&(1<snd_cwnd>>1) > dst->ssthresh) - dst->ssthresh = (tp->snd_cwnd>>1); + (tp->snd_cwnd >> 1) > dst->ssthresh) + dst->ssthresh = tp->snd_cwnd >> 1; if (!(dst->mxlock&(1<snd_cwnd > dst->cwnd) dst->cwnd = tp->snd_cwnd; @@ -571,15 +573,16 @@ tp->ca_state == TCP_CA_Open) { /* Cong. avoidance phase, cwnd is reliable. */ if (!(dst->mxlock&(1<ssthresh = max(tp->snd_cwnd>>1, tp->snd_ssthresh); + dst->ssthresh = max(tp->snd_cwnd >> 1, + tp->snd_ssthresh); if (!(dst->mxlock&(1<cwnd = (dst->cwnd + tp->snd_cwnd)>>1; + dst->cwnd = (dst->cwnd + tp->snd_cwnd) >> 1; } else { /* Else slow start did not finish, cwnd is non-sense, ssthresh may be also invalid. */ if (!(dst->mxlock&(1<cwnd = (dst->cwnd + tp->snd_ssthresh)>>1; + dst->cwnd = (dst->cwnd + tp->snd_ssthresh) >> 1; if (dst->ssthresh && !(dst->mxlock&(1<snd_ssthresh > dst->ssthresh) @@ -620,7 +623,7 @@ static void tcp_init_metrics(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct dst_entry *dst = __sk_dst_get(sk); if (dst == NULL) @@ -643,7 +646,7 @@ if (dst->rtt == 0) goto reset; - if (!tp->srtt && dst->rtt < (TCP_TIMEOUT_INIT<<3)) + if (!tp->srtt && dst->rtt < (TCP_TIMEOUT_INIT << 3)) goto reset; /* Initial rtt is determined from SYN,SYN-ACK. @@ -762,7 +765,7 @@ static int tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_una) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); unsigned char *ptr = ack_skb->h.raw + TCP_SKB_CB(ack_skb)->sacked; struct tcp_sack_block *sp = (struct tcp_sack_block *)(ptr+2); int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE)>>3; @@ -810,7 +813,7 @@ * account more or less fresh ones, they can * contain valid SACK info. */ - if (before(ack, prior_snd_una-tp->max_window)) + if (before(ack, prior_snd_una - tp->max_window)) return 0; } @@ -930,7 +933,9 @@ if ((TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_RETRANS) && after(lost_retrans, TCP_SKB_CB(skb)->ack_seq) && (IsFack(tp) || - !before(lost_retrans, TCP_SKB_CB(skb)->ack_seq+tp->reordering*tp->mss_cache))) { + !before(lost_retrans, + TCP_SKB_CB(skb)->ack_seq + tp->reordering * + tp->mss_cache))) { TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS; tp->retrans_out--; @@ -947,7 +952,7 @@ tp->left_out = tp->sacked_out + tp->lost_out; if (reord < tp->fackets_out && tp->ca_state != TCP_CA_Loss) - tcp_update_reordering(tp, (tp->fackets_out+1)-reord, 0); + tcp_update_reordering(tp, (tp->fackets_out + 1) - reord, 0); #if FASTRETRANS_DEBUG > 0 BUG_TRAP((int)tp->sacked_out >= 0); @@ -977,19 +982,18 @@ */ void tcp_enter_loss(struct sock *sk, int how) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb; int cnt = 0; /* Reduce ssthresh if it has not yet been made inside this window. */ - if (tp->ca_state <= TCP_CA_Disorder || - tp->snd_una == tp->high_seq || + if (tp->ca_state <= TCP_CA_Disorder || tp->snd_una == tp->high_seq || (tp->ca_state == TCP_CA_Loss && !tp->retransmits)) { tp->prior_ssthresh = tcp_current_ssthresh(tp); tp->snd_ssthresh = tcp_recalc_ssthresh(tp); } - tp->snd_cwnd = 1; - tp->snd_cwnd_cnt = 0; + tp->snd_cwnd = 1; + tp->snd_cwnd_cnt = 0; tp->snd_cwnd_stamp = tcp_time_stamp; tcp_clear_retrans(tp); @@ -1015,7 +1019,8 @@ } tcp_sync_left_out(tp); - tp->reordering = min_t(unsigned int, tp->reordering, sysctl_tcp_reordering); + tp->reordering = min_t(unsigned int, tp->reordering, + sysctl_tcp_reordering); tp->ca_state = TCP_CA_Loss; tp->high_seq = tp->snd_nxt; TCP_ECN_queue_cwr(tp); @@ -1493,7 +1498,7 @@ tcp_fastretrans_alert(struct sock *sk, u32 prior_snd_una, int prior_packets, int flag) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int is_dupack = (tp->snd_una == prior_snd_una && !(flag&FLAG_NOT_DUP)); /* Some technical things: @@ -1735,13 +1740,13 @@ /* Remove acknowledged frames from the retransmission queue. */ static int tcp_clean_rtx_queue(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb; __u32 now = tcp_time_stamp; int acked = 0; __s32 seq_rtt = -1; - while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head)) { + while((skb = skb_peek(&sk->write_queue)) && (skb != tp->send_head)) { struct tcp_skb_cb *scb = TCP_SKB_CB(skb); __u8 sacked = scb->sacked; @@ -1784,7 +1789,7 @@ } } else if (seq_rtt < 0) seq_rtt = now - scb->when; - if(tp->fackets_out) + if (tp->fackets_out) tp->fackets_out--; tp->packets_out--; __skb_unlink(skb, skb->list); @@ -1800,17 +1805,20 @@ BUG_TRAP((int)tp->sacked_out >= 0); BUG_TRAP((int)tp->lost_out >= 0); BUG_TRAP((int)tp->retrans_out >= 0); - if (tp->packets_out==0 && tp->sack_ok) { + if (!tp->packets_out && tp->sack_ok) { if (tp->lost_out) { - printk(KERN_DEBUG "Leak l=%u %d\n", tp->lost_out, tp->ca_state); + printk(KERN_DEBUG "Leak l=%u %d\n", tp->lost_out, + tp->ca_state); tp->lost_out = 0; } if (tp->sacked_out) { - printk(KERN_DEBUG "Leak s=%u %d\n", tp->sacked_out, tp->ca_state); + printk(KERN_DEBUG "Leak s=%u %d\n", tp->sacked_out, + tp->ca_state); tp->sacked_out = 0; } if (tp->retrans_out) { - printk(KERN_DEBUG "Leak r=%u %d\n", tp->retrans_out, tp->ca_state); + printk(KERN_DEBUG "Leak r=%u %d\n", tp->retrans_out, + tp->ca_state); tp->retrans_out = 0; } } @@ -1820,11 +1828,12 @@ static void tcp_ack_probe(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); /* Was it a usable window open? */ - if (!after(TCP_SKB_CB(tp->send_head)->end_seq, tp->snd_una + tp->snd_wnd)) { + if (!after(TCP_SKB_CB(tp->send_head)->end_seq, + tp->snd_una + tp->snd_wnd)) { tp->backoff = 0; tcp_clear_xmit_timer(sk, TCP_TIME_PROBE0); /* Socket must be waked up by subsequent tcp_data_snd_check(). @@ -1897,7 +1906,7 @@ /* This routine deals with incoming acks, but not outgoing ones. */ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); u32 prior_snd_una = tp->snd_una; u32 ack_seq = TCP_SKB_CB(skb)->seq; u32 ack = TCP_SKB_CB(skb)->ack_seq; @@ -1943,7 +1952,8 @@ */ sk->err_soft = 0; tp->rcv_tstamp = tcp_time_stamp; - if ((prior_packets = tp->packets_out) == 0) + prior_packets = tp->packets_out; + if (!prior_packets) goto no_queue; prior_in_flight = tcp_packets_in_flight(tp); @@ -1953,12 +1963,13 @@ if (tcp_ack_is_dubious(tp, flag)) { /* Advanve CWND, if state allows this. */ - if ((flag&FLAG_DATA_ACKED) && prior_in_flight >= tp->snd_cwnd && + if ((flag & FLAG_DATA_ACKED) && + prior_in_flight >= tp->snd_cwnd && tcp_may_raise_cwnd(tp, flag)) tcp_cong_avoid(tp); tcp_fastretrans_alert(sk, prior_snd_una, prior_packets, flag); } else { - if ((flag&FLAG_DATA_ACKED) && prior_in_flight >= tp->snd_cwnd) + if ((flag & FLAG_DATA_ACKED) && prior_in_flight >= tp->snd_cwnd) tcp_cong_avoid(tp); } @@ -2230,7 +2241,7 @@ */ static void tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); tcp_schedule_ack(tp); @@ -2333,7 +2344,7 @@ static void tcp_send_dupack(struct sock *sk, struct sk_buff *skb) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) { @@ -2396,7 +2407,7 @@ static void tcp_sack_new_ofo_skb(struct sock *sk, u32 seq, u32 end_seq) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct tcp_sack_block *sp = &tp->selective_acks[0]; int cur_sacks = tp->num_sacks; int this_sack; @@ -2434,7 +2445,7 @@ sp->start_seq = seq; sp->end_seq = end_seq; tp->num_sacks++; - tp->eff_sacks = min(tp->num_sacks+tp->dsack, 4-tp->tstamp_ok); + tp->eff_sacks = min(tp->num_sacks + tp->dsack, 4 - tp->tstamp_ok); } /* RCV.NXT advances, some SACKs should be eaten. */ @@ -2480,7 +2491,7 @@ */ static void tcp_ofo_queue(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); __u32 dsack_high = tp->rcv_nxt; struct sk_buff *skb; @@ -2524,7 +2535,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) { struct tcphdr *th = skb->h.th; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int eaten = -1; if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) @@ -2537,7 +2548,8 @@ if (tp->dsack) { tp->dsack = 0; - tp->eff_sacks = min_t(unsigned int, tp->num_sacks, 4-tp->tstamp_ok); + tp->eff_sacks = min_t(unsigned int, tp->num_sacks, + 4 - tp->tstamp_ok); } /* Queue data for delivery to the user. @@ -2550,16 +2562,16 @@ /* Ok. In sequence. In window. */ if (tp->ucopy.task == current && - tp->copied_seq == tp->rcv_nxt && - tp->ucopy.len && - sk->lock.users && - !tp->urg_data) { - int chunk = min_t(unsigned int, skb->len, tp->ucopy.len); + tp->copied_seq == tp->rcv_nxt && tp->ucopy.len && + sk->lock.users && !tp->urg_data) { + int chunk = min_t(unsigned int, skb->len, + tp->ucopy.len); __set_current_state(TASK_RUNNING); local_bh_enable(); - if (skb_copy_datagram_iovec(skb, 0, tp->ucopy.iov, chunk)) { + if (skb_copy_datagram_iovec(skb, 0, tp->ucopy.iov, + chunk)) { sk->err = EFAULT; sk->error_report(sk); } @@ -2592,11 +2604,11 @@ /* RFC2581. 4.2. SHOULD send immediate ACK, when * gap in queue is filled. */ - if (skb_queue_len(&tp->out_of_order_queue) == 0) + if (!skb_queue_len(&tp->out_of_order_queue)) tp->ack.pingpong = 0; } - if(tp->num_sacks) + if (tp->num_sacks) tcp_sack_remove(tp); tcp_fast_path_check(sk, tp); @@ -2622,7 +2634,7 @@ } /* Out of window. F.e. zero window probe. */ - if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt+tcp_receive_window(tp))) + if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt + tcp_receive_window(tp))) goto out_of_window; tcp_enter_quickack_mode(tp); @@ -2660,25 +2672,26 @@ tcp_set_owner_r(skb, sk); - if (skb_peek(&tp->out_of_order_queue) == NULL) { + if (!skb_peek(&tp->out_of_order_queue)) { /* Initial out of order segment, build 1 SACK. */ - if(tp->sack_ok) { + if (tp->sack_ok) { tp->num_sacks = 1; - tp->dsack = 0; + tp->dsack = 0; tp->eff_sacks = 1; tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq; - tp->selective_acks[0].end_seq = TCP_SKB_CB(skb)->end_seq; + tp->selective_acks[0].end_seq = + TCP_SKB_CB(skb)->end_seq; } __skb_queue_head(&tp->out_of_order_queue,skb); } else { - struct sk_buff *skb1=tp->out_of_order_queue.prev; + struct sk_buff *skb1 = tp->out_of_order_queue.prev; u32 seq = TCP_SKB_CB(skb)->seq; u32 end_seq = TCP_SKB_CB(skb)->end_seq; if (seq == TCP_SKB_CB(skb1)->end_seq) { __skb_append(skb1, skb); - if (tp->num_sacks == 0 || + if (!tp->num_sacks || tp->selective_acks[0].end_seq != seq) goto add_sack; @@ -2691,7 +2704,8 @@ do { if (!after(TCP_SKB_CB(skb1)->seq, seq)) break; - } while ((skb1=skb1->prev) != (struct sk_buff*)&tp->out_of_order_queue); + } while ((skb1 = skb1->prev) != + (struct sk_buff*)&tp->out_of_order_queue); /* Do skb overlap to previous one? */ if (skb1 != (struct sk_buff*)&tp->out_of_order_queue && @@ -2712,7 +2726,8 @@ __skb_insert(skb, skb1, skb1->next, &tp->out_of_order_queue); /* And clean segments covered by new one as whole. */ - while ((skb1 = skb->next) != (struct sk_buff*)&tp->out_of_order_queue && + while ((skb1 = skb->next) != + (struct sk_buff*)&tp->out_of_order_queue && after(end_seq, TCP_SKB_CB(skb1)->seq)) { if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) { tcp_dsack_extend(tp, TCP_SKB_CB(skb1)->seq, end_seq); @@ -2828,7 +2843,7 @@ */ static void tcp_collapse_ofo_queue(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb = skb_peek(&tp->out_of_order_queue); struct sk_buff *head; u32 start, end; @@ -2873,7 +2888,7 @@ */ static int tcp_prune_queue(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); SOCK_DEBUG(sk, "prune_queue: c=%x\n", tp->copied_seq); @@ -2882,7 +2897,7 @@ if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) tcp_clamp_window(sk, tp); else if (tcp_memory_pressure) - tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U*tp->advmss); + tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss); tcp_collapse_ofo_queue(sk); tcp_collapse(sk, sk->receive_queue.next, @@ -2898,7 +2913,8 @@ /* First, purge the out_of_order queue. */ if (skb_queue_len(&tp->out_of_order_queue)) { - net_statistics[smp_processor_id()*2].OfoPruned += skb_queue_len(&tp->out_of_order_queue); + net_statistics[smp_processor_id() * 2].OfoPruned += + skb_queue_len(&tp->out_of_order_queue); __skb_queue_purge(&tp->out_of_order_queue); /* Reset SACK state. A conforming SACK implementation will @@ -2906,7 +2922,7 @@ * is in a sad state like this, we care only about integrity * of the connection not performance. */ - if(tp->sack_ok) + if (tp->sack_ok) tcp_sack_reset(tp); tcp_mem_reclaim(sk); } @@ -2932,7 +2948,7 @@ */ void tcp_cwnd_application_limited(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); if (tp->ca_state == TCP_CA_Open && sk->socket && !test_bit(SOCK_NOSPACE, &sk->socket->flags)) { @@ -2940,7 +2956,7 @@ u32 win_used = max(tp->snd_cwnd_used, 2U); if (win_used < tp->snd_cwnd) { tp->snd_ssthresh = tcp_current_ssthresh(tp); - tp->snd_cwnd = (tp->snd_cwnd+win_used)>>1; + tp->snd_cwnd = (tp->snd_cwnd + win_used) >> 1; } tp->snd_cwnd_used = 0; } @@ -2954,16 +2970,16 @@ */ static void tcp_new_space(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); if (tp->packets_out < tp->snd_cwnd && !(sk->userlocks&SOCK_SNDBUF_LOCK) && !tcp_memory_pressure && atomic_read(&tcp_memory_allocated) < sysctl_tcp_mem[0]) { - int sndmem, demanded; - - sndmem = tp->mss_clamp+MAX_TCP_HEADER+16+sizeof(struct sk_buff); - demanded = max_t(unsigned int, tp->snd_cwnd, tp->reordering+1); + int sndmem = tp->mss_clamp + MAX_TCP_HEADER + 16 + + sizeof(struct sk_buff), + demanded = max_t(unsigned int, tp->snd_cwnd, + tp->reordering + 1); sndmem *= 2*demanded; if (sndmem > sk->sndbuf) sk->sndbuf = min(sndmem, sysctl_tcp_wmem[2]); @@ -2975,7 +2991,7 @@ static inline void tcp_check_space(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); if (tp->queue_shrunk) { tp->queue_shrunk = 0; @@ -2986,7 +3002,7 @@ static void __tcp_data_snd_check(struct sock *sk, struct sk_buff *skb) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); if (after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) || tcp_packets_in_flight(tp) >= tp->snd_cwnd || @@ -2996,7 +3012,8 @@ static __inline__ void tcp_data_snd_check(struct sock *sk) { - struct sk_buff *skb = sk->tp_pinfo.af_tcp.send_head; + struct tcp_opt *tp = tcp_sk(sk); + struct sk_buff *skb = tp->send_head; if (skb != NULL) __tcp_data_snd_check(sk, skb); @@ -3008,7 +3025,7 @@ */ static __inline__ void __tcp_ack_snd_check(struct sock *sk, int ofo_possible) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); /* More than one full frame received... */ if (((tp->rcv_nxt - tp->rcv_wup) > tp->ack.rcv_mss @@ -3020,7 +3037,7 @@ tcp_in_quickack_mode(tp) || /* We have out of order data. */ (ofo_possible && - skb_peek(&tp->out_of_order_queue) != NULL)) { + skb_peek(&tp->out_of_order_queue))) { /* Then ack it now */ tcp_send_ack(sk); } else { @@ -3031,7 +3048,7 @@ static __inline__ void tcp_ack_snd_check(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); if (!tcp_ack_scheduled(tp)) { /* We sent a data segment already. */ return; @@ -3051,7 +3068,7 @@ static void tcp_check_urg(struct sock * sk, struct tcphdr * th) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); u32 ptr = ntohs(th->urg_ptr); if (ptr && !sysctl_tcp_stdurg) @@ -3114,8 +3131,8 @@ } } - tp->urg_data = TCP_URG_NOTYET; - tp->urg_seq = ptr; + tp->urg_data = TCP_URG_NOTYET; + tp->urg_seq = ptr; /* Disable header prediction. */ tp->pred_flags = 0; @@ -3124,7 +3141,7 @@ /* This is the 'fast' part of urgent handling. */ static inline void tcp_urg(struct sock *sk, struct sk_buff *skb, struct tcphdr *th) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); /* Check if we get a new urgent pointer - normally not. */ if (th->urg) @@ -3132,7 +3149,8 @@ /* Do we wait for any urgent data? - normally not... */ if (tp->urg_data == TCP_URG_NOTYET) { - u32 ptr = tp->urg_seq - ntohl(th->seq) + (th->doff*4) - th->syn; + u32 ptr = tp->urg_seq - ntohl(th->seq) + (th->doff * 4) - + th->syn; /* Is the urgent pointer pointing into this packet? */ if (ptr < skb->len) { @@ -3148,7 +3166,7 @@ static int tcp_copy_to_iovec(struct sock *sk, struct sk_buff *skb, int hlen) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int chunk = skb->len - hlen; int err; @@ -3156,11 +3174,12 @@ if (skb->ip_summed==CHECKSUM_UNNECESSARY) err = skb_copy_datagram_iovec(skb, hlen, tp->ucopy.iov, chunk); else - err = skb_copy_and_csum_datagram_iovec(skb, hlen, tp->ucopy.iov); + err = skb_copy_and_csum_datagram_iovec(skb, hlen, + tp->ucopy.iov); if (!err) { update: - tp->ucopy.len -= chunk; + tp->ucopy.len -= chunk; tp->copied_seq += chunk; local_bh_disable(); return 0; @@ -3223,7 +3242,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, struct tcphdr *th, unsigned len) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); /* * Header prediction. @@ -3441,7 +3460,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, struct tcphdr *th, unsigned len) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int saved_clamp = tp->mss_clamp; tcp_parse_options(skb, tp, 0); @@ -3462,7 +3481,8 @@ goto reset_and_undo; if (tp->saw_tstamp && tp->rcv_tsecr && - !between(tp->rcv_tsecr, tp->retrans_stamp, tcp_time_stamp)) { + !between(tp->rcv_tsecr, tp->retrans_stamp, + tcp_time_stamp)) { NET_INC_STATS_BH(PAWSActiveRejected); goto reset_and_undo; } @@ -3505,8 +3525,8 @@ /* Ok.. it's good. Set up sequence numbers and * move to established. */ - tp->rcv_nxt = TCP_SKB_CB(skb)->seq+1; - tp->rcv_wup = TCP_SKB_CB(skb)->seq+1; + tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; + tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1; /* RFC1323: The window in SYN & SYN/ACK segments is * never scaled. @@ -3514,16 +3534,16 @@ tp->snd_wnd = ntohs(th->window); tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(skb)->seq); - if (tp->wscale_ok == 0) { + if (!tp->wscale_ok) { tp->snd_wscale = tp->rcv_wscale = 0; tp->window_clamp = min(tp->window_clamp, 65535U); } if (tp->saw_tstamp) { - tp->tstamp_ok = 1; + tp->tstamp_ok = 1; tp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED; - tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; + tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; tcp_store_ts_recent(tp); } else { tp->tcp_header_len = sizeof(struct tcphdr); @@ -3540,7 +3560,7 @@ if (sk->keepopen) tcp_reset_keepalive_timer(sk, keepalive_time_when(tp)); - if (tp->snd_wscale == 0) + if (!tp->snd_wscale) __tcp_fast_path_on(tp, tp->snd_wnd); else tp->pred_flags = 0; @@ -3567,7 +3587,7 @@ */ tcp_schedule_ack(tp); tp->ack.lrcvtime = tcp_time_stamp; - tp->ack.ato = TCP_ATO_MIN; + tp->ack.ato = TCP_ATO_MIN; tcp_incr_quickack(tp); tcp_enter_quickack_mode(tp); tcp_reset_xmit_timer(sk, TCP_TIME_DACK, TCP_DELACK_MAX); @@ -3619,8 +3639,8 @@ /* RFC1323: The window in SYN & SYN/ACK segments is * never scaled. */ - tp->snd_wnd = ntohs(th->window); - tp->snd_wl1 = TCP_SKB_CB(skb)->seq; + tp->snd_wnd = ntohs(th->window); + tp->snd_wl1 = TCP_SKB_CB(skb)->seq; tp->max_window = tp->snd_wnd; tcp_sync_mss(sk, tp->pmtu_cookie); @@ -3670,7 +3690,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, struct tcphdr *th, unsigned len) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int queued = 0; tp->saw_tstamp = 0; @@ -3778,14 +3798,17 @@ } tp->snd_una = TCP_SKB_CB(skb)->ack_seq; - tp->snd_wnd = ntohs(th->window) << tp->snd_wscale; - tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(skb)->seq); + tp->snd_wnd = ntohs(th->window) << + tp->snd_wscale; + tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq, + TCP_SKB_CB(skb)->seq); /* tcp_ack considers this ACK as duplicate * and does not calculate rtt. * Fix it at least with timestamps. */ - if (tp->saw_tstamp && tp->rcv_tsecr && !tp->srtt) + if (tp->saw_tstamp && tp->rcv_tsecr && + !tp->srtt) tcp_ack_saw_tstamp(tp, 0); if (tp->tstamp_ok) diff -Nru a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c --- a/net/ipv4/tcp_ipv4.c Tue Feb 19 18:08:59 2002 +++ b/net/ipv4/tcp_ipv4.c Tue Feb 19 18:08:59 2002 @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.238 2002/01/15 08:49:21 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.240 2002/02/01 22:01:04 davem Exp $ * * IPv4 specific functions * @@ -549,7 +549,7 @@ tw = (struct tcp_tw_bucket*)sk2; if(TCP_IPV4_MATCH(sk2, acookie, saddr, daddr, ports, dif)) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); /* With PAWS, it is safe from the viewpoint of data integrity. Even without PAWS it @@ -566,9 +566,10 @@ timestamp retrieved from peer table. */ if (tw->ts_recent_stamp) { - if ((tp->write_seq = tw->snd_nxt+65535+2) == 0) + if ((tp->write_seq = + tw->snd_nxt + 65535 + 2) == 0) tp->write_seq = 1; - tp->ts_recent = tw->ts_recent; + tp->ts_recent = tw->ts_recent; tp->ts_recent_stamp = tw->ts_recent_stamp; sock_hold(sk2); skp = &head->chain; @@ -644,7 +645,8 @@ /* This will initiate an outgoing connection. */ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct inet_opt *inet = inet_sk(sk); + struct tcp_opt *tp = tcp_sk(sk); struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; struct sk_buff *buff; struct rtable *rt; @@ -659,10 +661,10 @@ return(-EAFNOSUPPORT); nexthop = daddr = usin->sin_addr.s_addr; - if (sk->protinfo.af_inet.opt && sk->protinfo.af_inet.opt->srr) { + if (inet->opt && inet->opt->srr) { if (daddr == 0) return -EINVAL; - nexthop = sk->protinfo.af_inet.opt->faddr; + nexthop = inet->opt->faddr; } tmp = ip_route_connect(&rt, nexthop, sk->saddr, @@ -678,7 +680,7 @@ __sk_dst_set(sk, &rt->u.dst); sk->route_caps = rt->u.dst.dev->features; - if (!sk->protinfo.af_inet.opt || !sk->protinfo.af_inet.opt->srr) + if (!inet->opt || !inet->opt->srr) daddr = rt->rt_dst; err = -ENOBUFS; @@ -693,9 +695,9 @@ if (tp->ts_recent_stamp && sk->daddr != daddr) { /* Reset inherited state */ - tp->ts_recent = 0; + tp->ts_recent = 0; tp->ts_recent_stamp = 0; - tp->write_seq = 0; + tp->write_seq = 0; } if (sysctl_tcp_tw_recycle && @@ -719,12 +721,13 @@ if (!tp->write_seq) tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr, - sk->sport, usin->sin_port); + sk->sport, + usin->sin_port); tp->ext_header_len = 0; - if (sk->protinfo.af_inet.opt) - tp->ext_header_len = sk->protinfo.af_inet.opt->optlen; - sk->protinfo.af_inet.id = tp->write_seq^jiffies; + if (inet->opt) + tp->ext_header_len = inet->opt->optlen; + inet->id = tp->write_seq ^ jiffies; tp->mss_clamp = 536; @@ -778,7 +781,7 @@ static void tcp_v4_synq_add(struct sock *sk, struct open_request *req) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); struct tcp_listen_opt *lopt = tp->listen_opt; unsigned h = tcp_v4_synq_hash(req->af.v4_req.rmt_addr, req->rmt_port); @@ -799,10 +802,12 @@ /* * This routine does path mtu discovery as defined in RFC1191. */ -static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *ip, unsigned mtu) +static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *iph, + unsigned mtu) { struct dst_entry *dst; - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct inet_opt *inet = inet_sk(sk); + struct tcp_opt *tp = tcp_sk(sk); /* We are not interested in TCP_LISTEN and open_requests (SYN-ACKs * send out by Linux are always <576bytes so they should go through @@ -828,7 +833,7 @@ if (mtu < dst->pmtu && ip_dont_fragment(sk, dst)) sk->err_soft = EMSGSIZE; - if (sk->protinfo.af_inet.pmtudisc != IP_PMTUDISC_DONT && + if (inet->pmtudisc != IP_PMTUDISC_DONT && tp->pmtu_cookie > dst->pmtu) { tcp_sync_mss(sk, dst->pmtu); @@ -862,6 +867,7 @@ struct iphdr *iph = (struct iphdr*)skb->data; struct tcphdr *th = (struct tcphdr*)(skb->data+(iph->ihl<<2)); struct tcp_opt *tp; + struct inet_opt *inet; int type = skb->h.icmph->type; int code = skb->h.icmph->code; struct sock *sk; @@ -893,9 +899,10 @@ if (sk->state == TCP_CLOSE) goto out; - tp = &sk->tp_pinfo.af_tcp; + tp = tcp_sk(sk); seq = ntohl(th->seq); - if (sk->state != TCP_LISTEN && !between(seq, tp->snd_una, tp->snd_nxt)) { + if (sk->state != TCP_LISTEN && + !between(seq, tp->snd_una, tp->snd_nxt)) { NET_INC_STATS(OutOfWindowIcmps); goto out; } @@ -994,7 +1001,8 @@ * --ANK (980905) */ - if (sk->lock.users == 0 && sk->protinfo.af_inet.recverr) { + inet = inet_sk(sk); + if (sk->lock.users == 0 && inet->recverr) { sk->err = err; sk->error_report(sk); } else { /* Only an error on timeout */ @@ -1313,7 +1321,7 @@ tcp_clear_options(&tp); tp.mss_clamp = 536; - tp.user_mss = sk->tp_pinfo.af_tcp.user_mss; + tp.user_mss = tcp_sk(sk)->user_mss; tcp_parse_options(skb, &tp, 0); @@ -1421,6 +1429,7 @@ struct open_request *req, struct dst_entry *dst) { + struct inet_opt *newinet; struct tcp_opt *newtp; struct sock *newsk; @@ -1438,18 +1447,19 @@ newsk->dst_cache = dst; newsk->route_caps = dst->dev->features; - newtp = &(newsk->tp_pinfo.af_tcp); + newtp = tcp_sk(newsk); newsk->daddr = req->af.v4_req.rmt_addr; newsk->saddr = req->af.v4_req.loc_addr; newsk->rcv_saddr = req->af.v4_req.loc_addr; - newsk->protinfo.af_inet.opt = req->af.v4_req.opt; + newinet = inet_sk(newsk); + newinet->opt = req->af.v4_req.opt; req->af.v4_req.opt = NULL; - newsk->protinfo.af_inet.mc_index = tcp_v4_iif(skb); - newsk->protinfo.af_inet.mc_ttl = skb->nh.iph->ttl; + newinet->mc_index = tcp_v4_iif(skb); + newinet->mc_ttl = skb->nh.iph->ttl; newtp->ext_header_len = 0; - if (newsk->protinfo.af_inet.opt) - newtp->ext_header_len = newsk->protinfo.af_inet.opt->optlen; - newsk->protinfo.af_inet.id = newtp->write_seq^jiffies; + if (newinet->opt) + newtp->ext_header_len = newinet->opt->optlen; + newinet->id = newtp->write_seq ^ jiffies; tcp_sync_mss(newsk, dst->pmtu); newtp->advmss = dst->advmss; @@ -1473,7 +1483,7 @@ struct open_request *req, **prev; struct tcphdr *th = skb->h.th; struct iphdr *iph = skb->nh.iph; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sock *nsk; /* Find possible connection requests. */ @@ -1723,18 +1733,19 @@ static int tcp_v4_reselect_saddr(struct sock *sk) { + struct inet_opt *inet = inet_sk(sk); int err; struct rtable *rt; __u32 old_saddr = sk->saddr; __u32 new_saddr; __u32 daddr = sk->daddr; - if(sk->protinfo.af_inet.opt && sk->protinfo.af_inet.opt->srr) - daddr = sk->protinfo.af_inet.opt->faddr; + if (inet->opt && inet->opt->srr) + daddr = inet->opt->faddr; /* Query new route. */ err = ip_route_connect(&rt, daddr, 0, - RT_TOS(sk->protinfo.af_inet.tos)|sk->localroute, + RT_TOS(inet->tos) | sk->localroute, sk->bound_dev_if); if (err) return err; @@ -1770,6 +1781,7 @@ int tcp_v4_rebuild_header(struct sock *sk) { + struct inet_opt *inet = inet_sk(sk); struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0); u32 daddr; int err; @@ -1780,8 +1792,8 @@ /* Reroute. */ daddr = sk->daddr; - if(sk->protinfo.af_inet.opt && sk->protinfo.af_inet.opt->srr) - daddr = sk->protinfo.af_inet.opt->faddr; + if (inet->opt && inet->opt->srr) + daddr = inet->opt->faddr; err = ip_route_output(&rt, daddr, sk->saddr, RT_CONN_FLAGS(sk), sk->bound_dev_if); @@ -1820,7 +1832,7 @@ int tcp_v4_remember_stamp(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); struct rtable *rt = (struct rtable*)__sk_dst_get(sk); struct inet_peer *peer = NULL; int release_it = 0; @@ -1890,7 +1902,7 @@ */ static int tcp_v4_init_sock(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); skb_queue_head_init(&tp->out_of_order_queue); tcp_init_xmit_timers(sk); @@ -1920,7 +1932,7 @@ sk->write_space = tcp_write_space; sk->use_write_queue = 1; - sk->tp_pinfo.af_tcp.af_specific = &ipv4_specific; + tp->af_specific = &ipv4_specific; sk->sndbuf = sysctl_tcp_wmem[1]; sk->rcvbuf = sysctl_tcp_rmem[1]; @@ -1932,7 +1944,7 @@ static int tcp_v4_destroy_sock(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); tcp_clear_xmit_timers(sk); @@ -1950,7 +1962,7 @@ tcp_put_port(sk); /* If sendmsg cached page exists, toss it. */ - if (tp->sndmsg_page != NULL) + if (tp->sndmsg_page) __free_page(tp->sndmsg_page); atomic_dec(&tcp_sockets_allocated); @@ -1989,7 +2001,7 @@ __u16 destp, srcp; int timer_active; unsigned long timer_expires; - struct tcp_opt *tp = &sp->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sp); dest = sp->daddr; src = sp->rcv_saddr; @@ -2070,7 +2082,7 @@ for (sk = tcp_listening_hash[i]; sk; sk = sk->next, num++) { struct open_request *req; int uid; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); if (!TCP_INET_FAMILY(sk->family)) goto skip_listen; @@ -2197,7 +2209,7 @@ if (err < 0) panic("Failed to create the TCP control socket.\n"); tcp_socket->sk->allocation=GFP_ATOMIC; - tcp_socket->sk->protinfo.af_inet.ttl = MAXTTL; + inet_sk(tcp_socket->sk)->ttl = MAXTTL; /* Unhash it so that IP input processing does not even * see it, we do not wish this socket to see incoming diff -Nru a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c --- a/net/ipv4/tcp_minisocks.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv4/tcp_minisocks.c Tue Feb 19 18:08:58 2002 @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_minisocks.c,v 1.14 2001/09/21 21:27:34 davem Exp $ + * Version: $Id: tcp_minisocks.c,v 1.15 2002/02/01 22:01:04 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -347,7 +347,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) { struct tcp_tw_bucket *tw = NULL; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int recycle_ok = 0; if (sysctl_tcp_tw_recycle && tp->ts_recent_stamp) @@ -383,11 +383,11 @@ #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) if(tw->family == PF_INET6) { - memcpy(&tw->v6_daddr, - &sk->net_pinfo.af_inet6.daddr, + struct ipv6_pinfo *np = inet6_sk(sk); + + memcpy(&tw->v6_daddr, &np->daddr, sizeof(struct in6_addr)); - memcpy(&tw->v6_rcv_saddr, - &sk->net_pinfo.af_inet6.rcv_saddr, + memcpy(&tw->v6_rcv_saddr, &np->rcv_saddr, sizeof(struct in6_addr)); } #endif @@ -641,7 +641,10 @@ */ struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb) { - struct sock *newsk = sk_alloc(PF_INET, GFP_ATOMIC, 0); + /* allocate the newsk from the same slab of the master sock, + * if not, at sk_free time we'll try to free it from the wrong + * slabcache (i.e. is it TCPv4 or v6?) -acme */ + struct sock *newsk = sk_alloc(PF_INET, GFP_ATOMIC, 0, sk->slab); if(newsk != NULL) { struct tcp_opt *newtp; @@ -649,7 +652,7 @@ struct sk_filter *filter; #endif - memcpy(newsk, sk, sizeof(*newsk)); + memcpy(newsk, sk, sizeof(struct tcp_sock)); newsk->state = TCP_SYN_RECV; /* SANITY */ @@ -684,7 +687,7 @@ #endif /* Now setup tcp_opt */ - newtp = &(newsk->tp_pinfo.af_tcp); + newtp = tcp_sk(newsk); newtp->pred_flags = 0; newtp->rcv_nxt = req->rcv_isn + 1; newtp->snd_nxt = req->snt_isn + 1; @@ -797,7 +800,7 @@ struct open_request **prev) { struct tcphdr *th = skb->h.th; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); u32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); int paws_reject = 0; struct tcp_opt ttp; diff -Nru a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c --- a/net/ipv4/tcp_output.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv4/tcp_output.c Tue Feb 19 18:08:58 2002 @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_output.c,v 1.145 2002/01/11 08:45:37 davem Exp $ + * Version: $Id: tcp_output.c,v 1.146 2002/02/01 22:01:04 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -84,7 +84,7 @@ */ static __u16 tcp_advertise_mss(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct dst_entry *dst = __sk_dst_get(sk); int mss = tp->advmss; @@ -132,7 +132,7 @@ static __inline__ void tcp_event_ack_sent(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); tcp_dec_quickack_mode(tp); tcp_clear_xmit_timer(sk, TCP_TIME_DACK); @@ -145,7 +145,7 @@ */ static __inline__ u16 tcp_select_window(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); u32 cur_win = tcp_receive_window(tp); u32 new_win = __tcp_select_window(sk); @@ -188,7 +188,7 @@ int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb) { if(skb != NULL) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); int tcp_header_size = tp->tcp_header_len; struct tcphdr *th; @@ -303,7 +303,7 @@ */ void tcp_send_skb(struct sock *sk, struct sk_buff *skb, int force_queue, unsigned cur_mss) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); /* Advance write_seq and place onto the write_queue. */ tp->write_seq = TCP_SKB_CB(skb)->end_seq; @@ -331,7 +331,7 @@ */ void tcp_push_one(struct sock *sk, unsigned cur_mss) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb = tp->send_head; if (tcp_snd_test(tp, skb, cur_mss, 1)) { @@ -418,7 +418,7 @@ */ static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *buff; int nsize = skb->len - len; u16 flags; @@ -501,7 +501,7 @@ int tcp_sync_mss(struct sock *sk, u32 pmtu) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); int mss_now; /* Calculate base mss without TCP options: @@ -509,7 +509,6 @@ */ mss_now = pmtu - tp->af_specific->net_header_len - sizeof(struct tcphdr); - /* Clamp it (mss_clamp does not include tcp options) */ if (mss_now > tp->mss_clamp) mss_now = tp->mss_clamp; @@ -544,7 +543,7 @@ */ int tcp_write_xmit(struct sock *sk, int nonagle) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); unsigned int mss_now; /* If we are closed, the bytes will have to remain here. @@ -642,7 +641,7 @@ */ u32 __tcp_select_window(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); /* MSS for the peer's data. Previous verions used mss_clamp * here. I don't know if the value based on our guesses * of peer's MSS is better for the performance. It's more correct @@ -688,7 +687,7 @@ /* Attempt to collapse two adjacent SKB's during retransmission. */ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb, int mss_now) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *next_skb = skb->next; /* The first test we must make is that neither of these two @@ -764,7 +763,7 @@ */ void tcp_simple_retransmit(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb; unsigned int mss = tcp_current_mss(sk); int lost = 0; @@ -810,7 +809,7 @@ */ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); unsigned int cur_mss = tcp_current_mss(sk); int err; @@ -909,7 +908,7 @@ */ void tcp_xmit_retransmit_queue(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb; int packet_cnt = tp->lost_out; @@ -989,7 +988,7 @@ */ void tcp_send_fin(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb = skb_peek_tail(&sk->write_queue); unsigned int mss_now; @@ -1033,7 +1032,7 @@ */ void tcp_send_active_reset(struct sock *sk, int priority) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb; /* NOTE: No TCP options attached and we never retransmit this. */ @@ -1084,7 +1083,7 @@ } TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_ACK; - TCP_ECN_send_synack(&sk->tp_pinfo.af_tcp, skb); + TCP_ECN_send_synack(tcp_sk(sk), skb); } TCP_SKB_CB(skb)->when = tcp_time_stamp; return tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC)); @@ -1096,7 +1095,7 @@ struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst, struct open_request *req) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct tcphdr *th; int tcp_header_size; struct sk_buff *skb; @@ -1159,7 +1158,7 @@ int tcp_connect(struct sock *sk, struct sk_buff *buff) { struct dst_entry *dst = __sk_dst_get(sk); - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); /* Reserve space for headers. */ skb_reserve(buff, MAX_TCP_HEADER); @@ -1246,7 +1245,7 @@ */ void tcp_send_delayed_ack(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); int ato = tp->ack.ato; unsigned long timeout; @@ -1299,7 +1298,7 @@ { /* If we have been reset, we may not send again. */ if(sk->state != TCP_CLOSE) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *buff; /* We are not putting this on the write queue, so @@ -1340,7 +1339,7 @@ */ static int tcp_xmit_probe_skb(struct sock *sk, int urgent) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb; /* We don't queue it, tcp_transmit_skb() sets ownership. */ @@ -1367,7 +1366,7 @@ int tcp_write_wakeup(struct sock *sk) { if (sk->state != TCP_CLOSE) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sk_buff *skb; if ((skb = tp->send_head) != NULL && @@ -1412,7 +1411,7 @@ */ void tcp_send_probe0(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int err; err = tcp_write_wakeup(sk); diff -Nru a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c --- a/net/ipv4/tcp_timer.c Tue Feb 19 18:08:59 2002 +++ b/net/ipv4/tcp_timer.c Tue Feb 19 18:08:59 2002 @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_timer.c,v 1.87 2001/09/21 21:27:34 davem Exp $ + * Version: $Id: tcp_timer.c,v 1.88 2002/02/01 22:01:04 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -45,7 +45,7 @@ void tcp_init_xmit_timers(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); init_timer(&tp->retransmit_timer); tp->retransmit_timer.function=&tcp_write_timer; @@ -64,7 +64,7 @@ void tcp_clear_xmit_timers(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); tp->pending = 0; if (timer_pending(&tp->retransmit_timer) && @@ -103,7 +103,7 @@ */ static int tcp_out_of_resources(struct sock *sk, int do_reset) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int orphans = atomic_read(&tcp_orphan_count); /* If peer does not open window for long time, or did not transmit @@ -156,7 +156,7 @@ /* A write timeout has occurred. Process the after effects. */ static int tcp_write_timeout(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); int retry_until; if ((1<state)&(TCPF_SYN_SENT|TCPF_SYN_RECV)) { @@ -210,7 +210,7 @@ static void tcp_delack_timer(unsigned long data) { struct sock *sk = (struct sock*)data; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); bh_lock_sock(sk); if (sk->lock.users) { @@ -271,7 +271,7 @@ static void tcp_probe_timer(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); int max_probes; if (tp->packets_out || !tp->send_head) { @@ -319,7 +319,7 @@ static void tcp_retransmit_timer(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); if (tp->packets_out == 0) goto out; @@ -415,7 +415,7 @@ static void tcp_write_timer(unsigned long data) { struct sock *sk = (struct sock*)data; - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); int event; bh_lock_sock(sk); @@ -461,7 +461,7 @@ static void tcp_synack_timer(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct tcp_listen_opt *lopt = tp->listen_opt; int max_retries = tp->syn_retries ? : sysctl_tcp_synack_retries; int thresh = max_retries; @@ -565,7 +565,7 @@ return; if (val && !sk->keepopen) - tcp_reset_keepalive_timer(sk, keepalive_time_when(&sk->tp_pinfo.af_tcp)); + tcp_reset_keepalive_timer(sk, keepalive_time_when(tcp_sk(sk))); else if (!val) tcp_delete_keepalive_timer(sk); } @@ -574,7 +574,7 @@ static void tcp_keepalive_timer (unsigned long data) { struct sock *sk = (struct sock *) data; - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); __u32 elapsed; /* Only process if socket is not in use. */ diff -Nru a/net/ipv4/udp.c b/net/ipv4/udp.c --- a/net/ipv4/udp.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv4/udp.c Tue Feb 19 18:08:58 2002 @@ -5,7 +5,7 @@ * * The User Datagram Protocol (UDP). * - * Version: $Id: udp.c,v 1.101 2002/01/12 07:39:45 davem Exp $ + * Version: $Id: udp.c,v 1.102 2002/02/01 22:01:04 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -84,7 +84,7 @@ #include #include #include -#include +#include #include #include #include @@ -293,6 +293,7 @@ void udp_err(struct sk_buff *skb, u32 info) { + struct inet_opt *inet; struct iphdr *iph = (struct iphdr*)skb->data; struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2)); int type = skb->h.icmph->type; @@ -309,6 +310,7 @@ err = 0; harderr = 0; + inet = inet_sk(sk); switch (type) { default: @@ -323,7 +325,7 @@ break; case ICMP_DEST_UNREACH: if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ - if (sk->protinfo.af_inet.pmtudisc != IP_PMTUDISC_DONT) { + if (inet->pmtudisc != IP_PMTUDISC_DONT) { err = EMSGSIZE; harderr = 1; break; @@ -342,7 +344,7 @@ * RFC1122: OK. Passes ICMP errors back to application, as per * 4.1.3.3. */ - if (!sk->protinfo.af_inet.recverr) { + if (!inet->recverr) { if (!harderr || sk->state != TCP_ESTABLISHED) goto out; } else { @@ -415,6 +417,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) { + struct inet_opt *inet = inet_sk(sk); int ulen = len + sizeof(struct udphdr); struct ipcm_cookie ipc; struct udpfakehdr ufh; @@ -487,7 +490,7 @@ connected = 0; } if (!ipc.opt) - ipc.opt = sk->protinfo.af_inet.opt; + ipc.opt = inet->opt; ufh.saddr = ipc.addr; ipc.addr = daddr = ufh.daddr; @@ -498,7 +501,7 @@ daddr = ipc.opt->faddr; connected = 0; } - tos = RT_TOS(sk->protinfo.af_inet.tos); + tos = RT_TOS(inet->tos); if (sk->localroute || (msg->msg_flags&MSG_DONTROUTE) || (ipc.opt && ipc.opt->is_strictroute)) { tos |= RTO_ONLINK; @@ -507,9 +510,9 @@ if (MULTICAST(daddr)) { if (!ipc.oif) - ipc.oif = sk->protinfo.af_inet.mc_index; + ipc.oif = inet->mc_index; if (!ufh.saddr) - ufh.saddr = sk->protinfo.af_inet.mc_addr; + ufh.saddr = inet->mc_addr; connected = 0; } @@ -627,6 +630,7 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, int flags, int *addr_len) { + struct inet_opt *inet = inet_sk(sk); struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; struct sk_buff *skb; int copied, err; @@ -678,7 +682,7 @@ sin->sin_addr.s_addr = skb->nh.iph->saddr; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } - if (sk->protinfo.af_inet.cmsg_flags) + if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); err = copied; @@ -710,6 +714,7 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { + struct inet_opt *inet = inet_sk(sk); struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; struct rtable *rt; int err; @@ -738,7 +743,7 @@ sk->daddr = rt->rt_dst; sk->dport = usin->sin_port; sk->state = TCP_ESTABLISHED; - sk->protinfo.af_inet.id = jiffies; + inet->id = jiffies; sk_dst_set(sk, &rt->u.dst); return(0); @@ -758,8 +763,12 @@ sk->rcv_saddr = 0; sk->saddr = 0; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - memset(&sk->net_pinfo.af_inet6.saddr, 0, 16); - memset(&sk->net_pinfo.af_inet6.rcv_saddr, 0, 16); + if (sk->family == PF_INET6) { + struct ipv6_pinfo *np = inet6_sk(sk); + + memset(&np->saddr, 0, 16); + memset(&np->rcv_saddr, 0, 16); + } #endif } if (!(sk->userlocks&SOCK_BINDPORT_LOCK)) { diff -Nru a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c --- a/net/ipv6/af_inet6.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv6/af_inet6.c Tue Feb 19 18:08:57 2002 @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.65 2001/10/02 02:22:36 davem Exp $ + * $Id: af_inet6.c,v 1.66 2002/02/01 22:01:04 davem Exp $ * * Fixes: * piggy, Karl Knutson : Socket protocol table @@ -92,6 +92,11 @@ atomic_t inet6_sock_nr; #endif +/* Per protocol sock slabcache */ +kmem_cache_t *tcp6_sk_cachep; +kmem_cache_t *udp6_sk_cachep; +kmem_cache_t *raw6_sk_cachep; + /* The inetsw table contains everything that inet_create needs to * build a new socket. */ @@ -107,13 +112,50 @@ MOD_DEC_USE_COUNT; } +static __inline__ kmem_cache_t *inet6_sk_slab(int protocol) +{ + kmem_cache_t* rc = tcp6_sk_cachep; + + if (protocol == IPPROTO_UDP) + rc = udp6_sk_cachep; + else if (protocol == IPPROTO_RAW) + rc = raw6_sk_cachep; + return rc; +} + +static __inline__ int inet6_sk_size(int protocol) +{ + int rc = sizeof(struct tcp6_sock); + + if (protocol == IPPROTO_UDP) + rc = sizeof(struct udp6_sock); + else if (protocol == IPPROTO_RAW) + rc = sizeof(struct raw6_sock); + return rc; +} + +static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) +{ + struct ipv6_pinfo *rc = (&((struct tcp6_sock *)sk)->inet6); + + if (sk->protocol == IPPROTO_UDP) + rc = (&((struct udp6_sock *)sk)->inet6); + else if (sk->protocol == IPPROTO_RAW) + rc = (&((struct raw6_sock *)sk)->inet6); + return rc; +} + static int inet6_create(struct socket *sock, int protocol) { + struct inet_opt *inet; + struct ipv6_pinfo *np; struct sock *sk; + struct tcp6_sock* tcp6sk; struct list_head *p; struct inet_protosw *answer; - sk = sk_alloc(PF_INET6, GFP_KERNEL, 1); + sk = sk_alloc(PF_INET6, GFP_KERNEL, inet6_sk_size(protocol), + inet6_sk_slab(protocol)); if (sk == NULL) goto do_oom; @@ -155,10 +197,12 @@ if (INET_PROTOSW_REUSE & answer->flags) sk->reuse = 1; + inet = inet_sk(sk); + if (SOCK_RAW == sock->type) { sk->num = protocol; if (IPPROTO_RAW == protocol) - sk->protinfo.af_inet.hdrincl = 1; + inet->hdrincl = 1; } sk->destruct = inet6_sock_destruct; @@ -168,25 +212,27 @@ sk->backlog_rcv = answer->prot->backlog_rcv; - sk->net_pinfo.af_inet6.hop_limit = -1; - sk->net_pinfo.af_inet6.mcast_hops = -1; - sk->net_pinfo.af_inet6.mc_loop = 1; - sk->net_pinfo.af_inet6.pmtudisc = IPV6_PMTUDISC_WANT; + tcp6sk = (struct tcp6_sock *)sk; + tcp6sk->pinet6 = np = inet6_sk_generic(sk); + np->hop_limit = -1; + np->mcast_hops = -1; + np->mc_loop = 1; + np->pmtudisc = IPV6_PMTUDISC_WANT; /* Init the ipv4 part of the socket since we can have sockets * using v6 API for ipv4. */ - sk->protinfo.af_inet.ttl = 64; + inet->ttl = 64; - sk->protinfo.af_inet.mc_loop = 1; - sk->protinfo.af_inet.mc_ttl = 1; - sk->protinfo.af_inet.mc_index = 0; - sk->protinfo.af_inet.mc_list = NULL; + inet->mc_loop = 1; + inet->mc_ttl = 1; + inet->mc_index = 0; + inet->mc_list = NULL; if (ipv4_config.no_pmtu_disc) - sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_DONT; + inet->pmtudisc = IP_PMTUDISC_DONT; else - sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_WANT; + inet->pmtudisc = IP_PMTUDISC_WANT; #ifdef INET_REFCNT_DEBUG @@ -232,6 +278,7 @@ { struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr; struct sock *sk = sock->sk; + struct ipv6_pinfo *np = inet6_sk(sk); __u32 v4addr = 0; unsigned short snum; int addr_type = 0; @@ -296,17 +343,17 @@ sk->rcv_saddr = v4addr; sk->saddr = v4addr; - ipv6_addr_copy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr); + ipv6_addr_copy(&np->rcv_saddr, &addr->sin6_addr); if (!(addr_type & IPV6_ADDR_MULTICAST)) - ipv6_addr_copy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr); + ipv6_addr_copy(&np->saddr, &addr->sin6_addr); /* Make sure we are allowed to bind here. */ if (sk->prot->get_port(sk, snum) != 0) { sk->rcv_saddr = 0; sk->saddr = 0; - memset(&sk->net_pinfo.af_inet6.rcv_saddr, 0, sizeof(struct in6_addr)); - memset(&sk->net_pinfo.af_inet6.saddr, 0, sizeof(struct in6_addr)); + memset(&np->rcv_saddr, 0, sizeof(struct in6_addr)); + memset(&np->saddr, 0, sizeof(struct in6_addr)); release_sock(sk); return -EADDRINUSE; @@ -339,6 +386,7 @@ int inet6_destroy_sock(struct sock *sk) { + struct ipv6_pinfo *np = inet6_sk(sk); struct sk_buff *skb; struct ipv6_txoptions *opt; @@ -350,7 +398,7 @@ /* Release rx options */ - if ((skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, NULL)) != NULL) + if ((skb = xchg(&np->pktoptions, NULL)) != NULL) kfree_skb(skb); /* Free flowlabels */ @@ -358,7 +406,7 @@ /* Free tx options */ - if ((opt = xchg(&sk->net_pinfo.af_inet6.opt, NULL)) != NULL) + if ((opt = xchg(&np->opt, NULL)) != NULL) sock_kfree_s(sk, opt, opt->tot_len); return 0; @@ -373,6 +421,7 @@ { struct sockaddr_in6 *sin=(struct sockaddr_in6 *)uaddr; struct sock *sk = sock->sk; + struct ipv6_pinfo *np = inet6_sk(sk); sin->sin6_family = AF_INET6; sin->sin6_flowinfo = 0; @@ -383,18 +432,15 @@ if (((1<state)&(TCPF_CLOSE|TCPF_SYN_SENT)) && peer == 1) return -ENOTCONN; sin->sin6_port = sk->dport; - memcpy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.daddr, - sizeof(struct in6_addr)); - if (sk->net_pinfo.af_inet6.sndflow) - sin->sin6_flowinfo = sk->net_pinfo.af_inet6.flow_label; + memcpy(&sin->sin6_addr, &np->daddr, sizeof(struct in6_addr)); + if (np->sndflow) + sin->sin6_flowinfo = np->flow_label; } else { - if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_ANY) - memcpy(&sin->sin6_addr, - &sk->net_pinfo.af_inet6.saddr, + if (ipv6_addr_type(&np->rcv_saddr) == IPV6_ADDR_ANY) + memcpy(&sin->sin6_addr, &np->saddr, sizeof(struct in6_addr)); else - memcpy(&sin->sin6_addr, - &sk->net_pinfo.af_inet6.rcv_saddr, + memcpy(&sin->sin6_addr, &np->rcv_saddr, sizeof(struct in6_addr)); sin->sin6_port = sk->sport; @@ -502,8 +548,8 @@ }; struct net_proto_family inet6_family_ops = { - PF_INET6, - inet6_create + family: PF_INET6, + create: inet6_create, }; #ifdef MODULE @@ -606,6 +652,19 @@ printk(KERN_CRIT "inet6_proto_init: size fault\n"); return -EINVAL; } + /* allocate our sock slab caches */ + tcp6_sk_cachep = kmem_cache_create("tcp6_sock", + sizeof(struct tcp6_sock), 0, + SLAB_HWCACHE_ALIGN, 0, 0); + udp6_sk_cachep = kmem_cache_create("udp6_sock", + sizeof(struct udp6_sock), 0, + SLAB_HWCACHE_ALIGN, 0, 0); + raw6_sk_cachep = kmem_cache_create("raw6_sock", + sizeof(struct raw6_sock), 0, + SLAB_HWCACHE_ALIGN, 0, 0); + if (!tcp6_sk_cachep || !udp6_sk_cachep || !raw6_sk_cachep) + printk(KERN_CRIT __FUNCTION__ + ": Can't create protocol sock SLAB caches!\n"); /* Register the socket-side information for inet6_create. */ for(r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r) diff -Nru a/net/ipv6/datagram.c b/net/ipv6/datagram.c --- a/net/ipv6/datagram.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv6/datagram.c Tue Feb 19 18:08:57 2002 @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: datagram.c,v 1.23 2001/09/01 00:31:50 davem Exp $ + * $Id: datagram.c,v 1.24 2002/02/01 22:01:04 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -35,10 +35,11 @@ void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, u16 port, u32 info, u8 *payload) { + struct ipv6_pinfo *np = inet6_sk(sk); struct icmp6hdr *icmph = (struct icmp6hdr *)skb->h.raw; struct sock_exterr_skb *serr; - if (!sk->net_pinfo.af_inet6.recverr) + if (!np->recverr) return; skb = skb_clone(skb, GFP_ATOMIC); @@ -65,11 +66,12 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info) { + struct ipv6_pinfo *np = inet6_sk(sk); struct sock_exterr_skb *serr; struct ipv6hdr *iph; struct sk_buff *skb; - if (!sk->net_pinfo.af_inet6.recverr) + if (!np->recverr) return; skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC); @@ -103,6 +105,7 @@ */ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) { + struct ipv6_pinfo *np = inet6_sk(sk); struct sock_exterr_skb *serr; struct sk_buff *skb, *skb2; struct sockaddr_in6 *sin; @@ -139,7 +142,7 @@ sin->sin6_scope_id = 0; if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) { memcpy(&sin->sin6_addr, skb->nh.raw + serr->addr_offset, 16); - if (sk->net_pinfo.af_inet6.sndflow) + if (np->sndflow) sin->sin6_flowinfo = *(u32*)(skb->nh.raw + serr->addr_offset - 24) & IPV6_FLOWINFO_MASK; if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) { struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; @@ -160,17 +163,19 @@ sin->sin6_flowinfo = 0; if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) { memcpy(&sin->sin6_addr, &skb->nh.ipv6h->saddr, 16); - if (sk->net_pinfo.af_inet6.rxopt.all) + if (np->rxopt.all) datagram_recv_ctl(sk, msg, skb); if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) { struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; sin->sin6_scope_id = opt->iif; } } else { + struct inet_opt *inet = inet_sk(sk); + ipv6_addr_set(&sin->sin6_addr, 0, 0, __constant_htonl(0xffff), skb->nh.iph->saddr); - if (sk->protinfo.af_inet.cmsg_flags) + if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); } } @@ -203,7 +208,7 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; if (np->rxopt.bits.rxinfo) { diff -Nru a/net/ipv6/icmp.c b/net/ipv6/icmp.c --- a/net/ipv6/icmp.c Tue Feb 19 18:08:59 2002 +++ b/net/ipv6/icmp.c Tue Feb 19 18:08:59 2002 @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: icmp.c,v 1.37 2001/09/18 22:29:10 davem Exp $ + * $Id: icmp.c,v 1.38 2002/02/08 03:57:19 davem Exp $ * * Based on net/ipv4/icmp.c * diff -Nru a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c --- a/net/ipv6/ip6_flowlabel.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv6/ip6_flowlabel.c Tue Feb 19 18:08:58 2002 @@ -180,7 +180,7 @@ struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, u32 label) { struct ipv6_fl_socklist *sfl; - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); label &= IPV6_FLOWLABEL_MASK; @@ -197,7 +197,7 @@ void fl6_free_socklist(struct sock *sk) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_fl_socklist *sfl; while ((sfl = np->ipv6_fl_list) != NULL) { @@ -354,6 +354,7 @@ static int mem_check(struct sock *sk) { + struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_fl_socklist *sfl; int room = FL_MAX_SIZE - atomic_read(&fl_size); int count = 0; @@ -361,7 +362,7 @@ if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK) return 0; - for (sfl = sk->net_pinfo.af_inet6.ipv6_fl_list; sfl; sfl = sfl->next) + for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) count++; if (room <= 0 || @@ -404,7 +405,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char *optval, int optlen) { int err; - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct in6_flowlabel_req freq; struct ipv6_fl_socklist *sfl1=NULL; struct ipv6_fl_socklist *sfl, **sflp; diff -Nru a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c --- a/net/ipv6/ip6_output.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv6/ip6_output.c Tue Feb 19 18:08:57 2002 @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: ip6_output.c,v 1.33 2001/09/20 00:35:35 davem Exp $ + * $Id: ip6_output.c,v 1.34 2002/02/01 22:01:04 davem Exp $ * * Based on linux/net/ipv4/ip_output.c * @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -105,8 +106,9 @@ skb->dev = dev; if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) { - if (!(dev->flags&IFF_LOOPBACK) && - (skb->sk == NULL || skb->sk->net_pinfo.af_inet6.mc_loop) && + struct ipv6_pinfo* np = skb->sk ? inet6_sk(skb->sk) : NULL; + + if (!(dev->flags & IFF_LOOPBACK) && (!np || np->mc_loop) && ipv6_chk_mcast_addr(dev, &skb->nh.ipv6h->daddr)) { struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); @@ -182,7 +184,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, struct ipv6_txoptions *opt) { - struct ipv6_pinfo * np = sk ? &sk->net_pinfo.af_inet6 : NULL; + struct ipv6_pinfo *np = sk ? inet6_sk(sk) : NULL; struct in6_addr *first_hop = fl->nl_u.ip6_u.daddr; struct dst_entry *dst = skb->dst; struct ipv6hdr *hdr; @@ -258,7 +260,7 @@ struct in6_addr *saddr, struct in6_addr *daddr, int proto, int len) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6hdr *hdr; int totlen; @@ -500,7 +502,8 @@ struct flowi *fl, unsigned length, struct ipv6_txoptions *opt, int hlimit, int flags) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct inet_opt *inet = inet_sk(sk); + struct ipv6_pinfo *np = inet6_sk(sk); struct in6_addr *final_dst = NULL; struct dst_entry *dst; int err = 0; @@ -582,7 +585,7 @@ jumbolen = 0; - if (!sk->protinfo.af_inet.hdrincl) { + if (!inet->hdrincl) { pktlength += sizeof(struct ipv6hdr); if (opt) pktlength += opt->opt_flen + opt->opt_nflen; @@ -642,7 +645,7 @@ hdr = (struct ipv6hdr *) skb->tail; skb->nh.ipv6h = hdr; - if (!sk->protinfo.af_inet.hdrincl) { + if (!inet->hdrincl) { ip6_bld_1(sk, skb, fl, hlimit, jumbolen ? sizeof(struct ipv6hdr) : pktlength); @@ -667,7 +670,7 @@ kfree_skb(skb); } } else { - if (sk->protinfo.af_inet.hdrincl || jumbolen || + if (inet->hdrincl || jumbolen || np->pmtudisc == IPV6_PMTUDISC_DO) { ipv6_local_error(sk, EMSGSIZE, fl, mtu); err = -EMSGSIZE; diff -Nru a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c --- a/net/ipv6/ipv6_sockglue.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv6/ipv6_sockglue.c Tue Feb 19 18:08:57 2002 @@ -7,7 +7,7 @@ * * Based on linux/net/ipv4/ip_sockglue.c * - * $Id: ipv6_sockglue.c,v 1.40 2001/09/18 22:29:10 davem Exp $ + * $Id: ipv6_sockglue.c,v 1.41 2002/02/01 22:01:04 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -122,7 +122,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); int val, valbool; int retv = -ENOPROTOOPT; @@ -166,7 +166,7 @@ ipv6_sock_mc_close(sk); if (sk->protocol == IPPROTO_TCP) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); local_bh_disable(); sock_prot_dec_use(sk->prot); @@ -281,7 +281,7 @@ retv = 0; if (sk->type == SOCK_STREAM) { if (opt) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); if (!((1<state)&(TCPF_LISTEN|TCPF_CLOSE)) && sk->daddr != LOOPBACK4_IPV6) { tp->ext_header_len = opt->opt_flen + opt->opt_nflen; @@ -401,7 +401,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); int len; int val; diff -Nru a/net/ipv6/mcast.c b/net/ipv6/mcast.c --- a/net/ipv6/mcast.c Tue Feb 19 18:08:59 2002 +++ b/net/ipv6/mcast.c Tue Feb 19 18:08:59 2002 @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: mcast.c,v 1.38 2001/08/15 07:36:31 davem Exp $ + * $Id: mcast.c,v 1.40 2002/02/08 03:57:19 davem Exp $ * * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c * @@ -77,7 +77,7 @@ { struct net_device *dev = NULL; struct ipv6_mc_socklist *mc_lst; - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); int err; if (!(ipv6_addr_type(addr) & IPV6_ADDR_MULTICAST)) @@ -136,7 +136,7 @@ */ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_mc_socklist *mc_lst, **lnk; write_lock_bh(&ipv6_sk_mc_lock); @@ -163,7 +163,7 @@ void ipv6_sock_mc_close(struct sock *sk) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_mc_socklist *mc_lst; write_lock_bh(&ipv6_sk_mc_lock); @@ -188,10 +188,11 @@ int inet6_mc_check(struct sock *sk, struct in6_addr *addr) { + struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_mc_socklist *mc; read_lock(&ipv6_sk_mc_lock); - for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) { + for (mc = np->ipv6_mc_list; mc; mc = mc->next) { if (ipv6_addr_cmp(&mc->addr, addr) == 0) { read_unlock(&ipv6_sk_mc_lock); return 1; @@ -748,6 +749,7 @@ int __init igmp6_init(struct net_proto_family *ops) { + struct ipv6_pinfo *np; struct sock *sk; int err; @@ -764,7 +766,8 @@ sk->allocation = GFP_ATOMIC; sk->prot->unhash(sk); - sk->net_pinfo.af_inet6.hop_limit = 1; + np = inet6_sk(sk); + np->hop_limit = 1; #ifdef CONFIG_PROC_FS create_proc_read_entry("net/igmp6", 0, 0, igmp6_read_proc, NULL); #endif diff -Nru a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c --- a/net/ipv6/ndisc.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv6/ndisc.c Tue Feb 19 18:08:57 2002 @@ -1211,6 +1211,7 @@ int __init ndisc_init(struct net_proto_family *ops) { + struct ipv6_pinfo *np; struct sock *sk; int err; @@ -1224,10 +1225,11 @@ } sk = ndisc_socket->sk; + np = inet6_sk(sk); sk->allocation = GFP_ATOMIC; - sk->net_pinfo.af_inet6.hop_limit = 255; + np->hop_limit = 255; /* Do not loopback ndisc messages */ - sk->net_pinfo.af_inet6.mc_loop = 0; + np->mc_loop = 0; sk->prot->unhash(sk); /* diff -Nru a/net/ipv6/proc.c b/net/ipv6/proc.c --- a/net/ipv6/proc.c Tue Feb 19 18:08:59 2002 +++ b/net/ipv6/proc.c Tue Feb 19 18:08:59 2002 @@ -7,7 +7,7 @@ * PROC file system. This is very similar to the IPv4 version, * except it reports the sockets in the INET6 address family. * - * Version: $Id: proc.c,v 1.16 2002/01/24 15:45:51 davem Exp $ + * Version: $Id: proc.c,v 1.17 2002/02/01 22:01:04 davem Exp $ * * Authors: David S. Miller (davem@caip.rutgers.edu) * @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include diff -Nru a/net/ipv6/raw.c b/net/ipv6/raw.c --- a/net/ipv6/raw.c Tue Feb 19 18:08:59 2002 +++ b/net/ipv6/raw.c Tue Feb 19 18:08:59 2002 @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/raw.c * - * $Id: raw.c,v 1.50 2001/09/18 22:29:10 davem Exp $ + * $Id: raw.c,v 1.51 2002/02/01 22:01:04 davem Exp $ * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support @@ -86,14 +86,14 @@ for(s = sk; s; s = s->next) { if(s->num == num) { - struct ipv6_pinfo *np = &s->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(s); if (!ipv6_addr_any(&np->daddr) && ipv6_addr_cmp(&np->daddr, rmt_addr)) continue; if (!ipv6_addr_any(&np->rcv_saddr)) { - if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0) + if (!ipv6_addr_cmp(&np->rcv_saddr, loc_addr)) break; if ((addr_type & IPV6_ADDR_MULTICAST) && inet6_mc_check(s, loc_addr)) @@ -113,9 +113,8 @@ static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb) { struct icmp6hdr *icmph; - struct raw6_opt *opt; + struct raw6_opt *opt = raw6_sk(sk); - opt = &sk->tp_pinfo.tp_raw; if (pskb_may_pull(skb, sizeof(struct icmp6hdr))) { __u32 *data = &opt->filter.data[0]; int bit_nr; @@ -187,6 +186,7 @@ /* This cleans up af_inet6 a bit. -DaveM */ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) { + struct ipv6_pinfo *np = inet6_sk(sk); struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; __u32 v4addr = 0; int addr_type; @@ -235,9 +235,9 @@ sk->rcv_saddr = v4addr; sk->saddr = v4addr; - ipv6_addr_copy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr); + ipv6_addr_copy(&np->rcv_saddr, &addr->sin6_addr); if (!(addr_type & IPV6_ADDR_MULTICAST)) - ipv6_addr_copy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr); + ipv6_addr_copy(&np->saddr, &addr->sin6_addr); err = 0; out: release_sock(sk); @@ -248,6 +248,8 @@ struct inet6_skb_parm *opt, int type, int code, int offset, u32 info) { + struct inet_opt *inet = inet_sk(sk); + struct ipv6_pinfo *np = inet6_sk(sk); int err; int harderr; @@ -256,21 +258,21 @@ 2. Socket is connected (otherwise the error indication is useless without recverr and error is hard. */ - if (!sk->net_pinfo.af_inet6.recverr && sk->state != TCP_ESTABLISHED) + if (!np->recverr && sk->state != TCP_ESTABLISHED) return; harderr = icmpv6_err_convert(type, code, &err); if (type == ICMPV6_PKT_TOOBIG) - harderr = (sk->net_pinfo.af_inet6.pmtudisc == IPV6_PMTUDISC_DO); + harderr = (np->pmtudisc == IPV6_PMTUDISC_DO); - if (sk->net_pinfo.af_inet6.recverr) { + if (np->recverr) { u8 *payload = skb->data; - if (!sk->protinfo.af_inet.hdrincl) + if (!inet->hdrincl) payload += offset; ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload); } - if (sk->net_pinfo.af_inet6.recverr || harderr) { + if (np->recverr || harderr) { sk->err = err; sk->error_report(sk); } @@ -298,7 +300,9 @@ */ int rawv6_rcv(struct sock *sk, struct sk_buff *skb) { - if (sk->protinfo.af_inet.hdrincl) { + struct inet_opt *inet = inet_sk(sk); + + if (inet->hdrincl) { __skb_push(skb, skb->nh.raw - skb->data); skb->h.raw = skb->nh.raw; } @@ -316,6 +320,7 @@ int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, int flags, int *addr_len) { + struct ipv6_pinfo *np = inet6_sk(sk); struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)msg->msg_name; struct sk_buff *skb; int copied, err; @@ -358,7 +363,7 @@ sock_recv_timestamp(msg, sk, skb); - if (sk->net_pinfo.af_inet6.rxopt.all) + if (np->rxopt.all) datagram_recv_ctl(sk, msg, skb); err = copied; @@ -405,7 +410,7 @@ struct in6_addr *daddr; sk = hdr->sk; - opt = &sk->tp_pinfo.tp_raw; + opt = raw6_sk(sk); if (hdr->daddr) daddr = hdr->daddr; @@ -434,7 +439,7 @@ { struct ipv6_txoptions opt_space; struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name; - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_txoptions *opt = NULL; struct ip6_flowlabel *flowlabel = NULL; struct flowi fl; @@ -491,8 +496,8 @@ /* Otherwise it will be difficult to maintain sk->dst_cache. */ if (sk->state == TCP_ESTABLISHED && - !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr)) - daddr = &sk->net_pinfo.af_inet6.daddr; + !ipv6_addr_cmp(daddr, &np->daddr)) + daddr = &np->daddr; if (addr_len >= sizeof(struct sockaddr_in6) && sin6->sin6_scope_id && @@ -503,7 +508,7 @@ return(-EINVAL); proto = sk->num; - daddr = &(sk->net_pinfo.af_inet6.daddr); + daddr = &np->daddr; fl.fl6_flowlabel = np->flow_label; } @@ -541,7 +546,7 @@ if (flowlabel) opt = fl6_merge_options(&opt_space, flowlabel, opt); - raw_opt = &sk->tp_pinfo.tp_raw; + raw_opt = raw6_sk(sk); fl.proto = proto; fl.fl6_dst = daddr; @@ -583,7 +588,7 @@ case ICMPV6_FILTER: if (optlen > sizeof(struct icmp6_filter)) optlen = sizeof(struct icmp6_filter); - if (copy_from_user(&sk->tp_pinfo.tp_raw.filter, optval, optlen)) + if (copy_from_user(&raw6_sk(sk)->filter, optval, optlen)) return -EFAULT; return 0; default: @@ -608,7 +613,7 @@ len = sizeof(struct icmp6_filter); if (put_user(len, optlen)) return -EFAULT; - if (copy_to_user(optval, &sk->tp_pinfo.tp_raw.filter, len)) + if (copy_to_user(optval, &raw6_sk(sk)->filter, len)) return -EFAULT; return 0; default: @@ -622,7 +627,7 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) { - struct raw6_opt *opt = &sk->tp_pinfo.tp_raw; + struct raw6_opt *opt = raw6_sk(sk); int val; switch(level) { @@ -665,7 +670,7 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) { - struct raw6_opt *opt = &sk->tp_pinfo.tp_raw; + struct raw6_opt *opt = raw6_sk(sk); int val, len; switch(level) { @@ -752,11 +757,12 @@ static void get_raw6_sock(struct sock *sp, char *tmpbuf, int i) { + struct ipv6_pinfo *np = inet6_sk(sp); struct in6_addr *dest, *src; __u16 destp, srcp; - dest = &sp->net_pinfo.af_inet6.daddr; - src = &sp->net_pinfo.af_inet6.rcv_saddr; + dest = &np->daddr; + src = &np->rcv_saddr; destp = 0; srcp = sp->num; sprintf(tmpbuf, diff -Nru a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c --- a/net/ipv6/tcp_ipv6.c Tue Feb 19 18:08:57 2002 +++ b/net/ipv6/tcp_ipv6.c Tue Feb 19 18:08:57 2002 @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: tcp_ipv6.c,v 1.143 2001/12/21 05:05:41 davem Exp $ + * $Id: tcp_ipv6.c,v 1.144 2002/02/01 22:01:04 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -75,8 +76,9 @@ static __inline__ int tcp_v6_sk_hashfn(struct sock *sk) { - struct in6_addr *laddr = &sk->net_pinfo.af_inet6.rcv_saddr; - struct in6_addr *faddr = &sk->net_pinfo.af_inet6.daddr; + struct ipv6_pinfo *np = inet6_sk(sk); + struct in6_addr *laddr = &np->rcv_saddr; + struct in6_addr *faddr = &np->daddr; __u16 lport = sk->num; __u16 fport = sk->dport; return tcp_v6_hashfn(laddr, lport, faddr, fport); @@ -136,12 +138,15 @@ if (tb->fastreuse != 0 && sk->reuse != 0 && sk->state != TCP_LISTEN) { goto success; } else { + struct ipv6_pinfo *np = inet6_sk(sk); struct sock *sk2 = tb->owners; int sk_reuse = sk->reuse; - int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr); + int addr_type = ipv6_addr_type(&np->rcv_saddr); /* We must walk the whole port owner list in this case. -DaveM */ for( ; sk2 != NULL; sk2 = sk2->bind_next) { + struct ipv6_pinfo *np2 = inet6_sk(sk2); + if (sk != sk2 && sk->bound_dev_if == sk2->bound_dev_if) { if (!sk_reuse || @@ -150,9 +155,9 @@ /* NOTE: IPv6 tw bucket have different format */ if (!sk2->rcv_saddr || addr_type == IPV6_ADDR_ANY || - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, + !ipv6_addr_cmp(&np->rcv_saddr, sk2->state != TCP_TIME_WAIT ? - &sk2->net_pinfo.af_inet6.rcv_saddr : + &np2->rcv_saddr : &((struct tcp_tw_bucket*)sk)->v6_rcv_saddr) || (addr_type==IPV6_ADDR_MAPPED && sk2->family==AF_INET && sk->rcv_saddr==sk2->rcv_saddr)) @@ -228,7 +233,9 @@ static void tcp_v6_hash(struct sock *sk) { if(sk->state != TCP_CLOSE) { - if (sk->tp_pinfo.af_tcp.af_specific == &ipv6_mapped) { + struct tcp_opt *tp = tcp_sk(sk); + + if (tp->af_specific == &ipv6_mapped) { tcp_prot.hash(sk); return; } @@ -249,11 +256,11 @@ sk = tcp_listening_hash[tcp_lhashfn(hnum)]; for(; sk; sk = sk->next) { if((sk->num == hnum) && (sk->family == PF_INET6)) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); score = 1; - if(!ipv6_addr_any(&np->rcv_saddr)) { - if(ipv6_addr_cmp(&np->rcv_saddr, daddr)) + if (!ipv6_addr_any(&np->rcv_saddr)) { + if (ipv6_addr_cmp(&np->rcv_saddr, daddr)) continue; score++; } @@ -417,8 +424,9 @@ static int tcp_v6_check_established(struct sock *sk) { - struct in6_addr *daddr = &sk->net_pinfo.af_inet6.rcv_saddr; - struct in6_addr *saddr = &sk->net_pinfo.af_inet6.daddr; + struct ipv6_pinfo *np = inet6_sk(sk); + struct in6_addr *daddr = &np->rcv_saddr; + struct in6_addr *saddr = &np->daddr; int dif = sk->bound_dev_if; u32 ports = TCP_COMBINED_PORTS(sk->dport, sk->num); int hash = tcp_v6_hashfn(daddr, sk->num, saddr, sk->dport); @@ -436,7 +444,7 @@ !ipv6_addr_cmp(&tw->v6_daddr, saddr) && !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr) && sk2->bound_dev_if == sk->bound_dev_if) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); if (tw->ts_recent_stamp) { /* See comment in tcp_ipv4.c */ @@ -514,8 +522,8 @@ int addr_len) { struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct ipv6_pinfo *np = inet6_sk(sk); + struct tcp_opt *tp = tcp_sk(sk); struct in6_addr *saddr = NULL; struct in6_addr saddr_buf; struct flowi fl; @@ -574,7 +582,8 @@ return -EINVAL; } - if (tp->ts_recent_stamp && ipv6_addr_cmp(&np->daddr, &usin->sin6_addr)) { + if (tp->ts_recent_stamp && + ipv6_addr_cmp(&np->daddr, &usin->sin6_addr)) { tp->ts_recent = 0; tp->ts_recent_stamp = 0; tp->write_seq = 0; @@ -597,20 +606,20 @@ sin.sin_port = usin->sin6_port; sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3]; - sk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped; + tp->af_specific = &ipv6_mapped; sk->backlog_rcv = tcp_v4_do_rcv; err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin)); if (err) { tp->ext_header_len = exthdrlen; - sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific; + tp->af_specific = &ipv6_specific; sk->backlog_rcv = tcp_v6_do_rcv; goto failure; } else { - ipv6_addr_set(&np->saddr, 0, 0, __constant_htonl(0x0000FFFF), + ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000FFFF), sk->saddr); - ipv6_addr_set(&np->rcv_saddr, 0, 0, __constant_htonl(0x0000FFFF), + ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000FFFF), sk->rcv_saddr); } @@ -628,7 +637,7 @@ fl.uli_u.ports.sport = sk->sport; if (np->opt && np->opt->srcrt) { - struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; + struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt; fl.nl_u.ip6_u.daddr = rt0->addr; } @@ -657,7 +666,7 @@ tp->ext_header_len = 0; if (np->opt) - tp->ext_header_len = np->opt->opt_flen+np->opt->opt_nflen; + tp->ext_header_len = np->opt->opt_flen + np->opt->opt_nflen; tp->mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); err = -ENOBUFS; @@ -720,14 +729,14 @@ if (sk->state == TCP_CLOSE) goto out; - tp = &sk->tp_pinfo.af_tcp; + tp = tcp_sk(sk); seq = ntohl(th->seq); if (sk->state != TCP_LISTEN && !between(seq, tp->snd_una, tp->snd_nxt)) { NET_INC_STATS_BH(OutOfWindowIcmps); goto out; } - np = &sk->net_pinfo.af_inet6; + np = inet6_sk(sk); if (type == ICMPV6_PKT_TOOBIG) { struct dst_entry *dst = NULL; @@ -829,6 +838,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct open_request *req, struct dst_entry *dst) { + struct ipv6_pinfo *np = inet6_sk(sk); struct sk_buff * skb; struct ipv6_txoptions *opt = NULL; struct flowi fl; @@ -843,9 +853,9 @@ fl.uli_u.ports.sport = sk->sport; if (dst == NULL) { - opt = sk->net_pinfo.af_inet6.opt; + opt = np->opt; if (opt == NULL && - sk->net_pinfo.af_inet6.rxopt.bits.srcrt == 2 && + np->rxopt.bits.srcrt == 2 && req->af.v6_req.pktopts) { struct sk_buff *pktopts = req->af.v6_req.pktopts; struct inet6_skb_parm *rxopt = (struct inet6_skb_parm *)pktopts->cb; @@ -879,7 +889,7 @@ done: dst_release(dst); - if (opt && opt != sk->net_pinfo.af_inet6.opt) + if (opt && opt != np->opt) sock_kfree_s(sk, opt, opt->tot_len); return err; } @@ -900,14 +910,15 @@ static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb) { + struct ipv6_pinfo *np = inet6_sk(sk); struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; - if (sk->net_pinfo.af_inet6.rxopt.all) { - if ((opt->hop && sk->net_pinfo.af_inet6.rxopt.bits.hopopts) || + if (np->rxopt.all) { + if ((opt->hop && np->rxopt.bits.hopopts) || ((IPV6_FLOWINFO_MASK&*(u32*)skb->nh.raw) && - sk->net_pinfo.af_inet6.rxopt.bits.rxflow) || - (opt->srcrt && sk->net_pinfo.af_inet6.rxopt.bits.srcrt) || - ((opt->dst1 || opt->dst0) && sk->net_pinfo.af_inet6.rxopt.bits.dstopts)) + np->rxopt.bits.rxflow) || + (opt->srcrt && np->rxopt.bits.srcrt) || + ((opt->dst1 || opt->dst0) && np->rxopt.bits.dstopts)) return 1; } return 0; @@ -917,7 +928,7 @@ static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, struct sk_buff *skb) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); if (skb->ip_summed == CHECKSUM_HW) { th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0); @@ -1084,7 +1095,7 @@ { struct open_request *req, **prev; struct tcphdr *th = skb->h.th; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); struct sock *nsk; /* Find possible connection requests. */ @@ -1116,7 +1127,7 @@ static void tcp_v6_synq_add(struct sock *sk, struct open_request *req) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sk); struct tcp_listen_opt *lopt = tp->listen_opt; unsigned h = tcp_v6_synq_hash(&req->af.v6_req.rmt_addr, req->rmt_port); @@ -1139,7 +1150,8 @@ */ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) { - struct tcp_opt tp; + struct ipv6_pinfo *np = inet6_sk(sk); + struct tcp_opt tmptp, *tp = tcp_sk(sk); struct open_request *req = NULL; __u32 isn = TCP_SKB_CB(skb)->when; @@ -1166,14 +1178,14 @@ if (req == NULL) goto drop; - tcp_clear_options(&tp); - tp.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); - tp.user_mss = sk->tp_pinfo.af_tcp.user_mss; + tcp_clear_options(&tmptp); + tmptp.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); + tmptp.user_mss = tp->user_mss; - tcp_parse_options(skb, &tp, 0); + tcp_parse_options(skb, &tmptp, 0); - tp.tstamp_ok = tp.saw_tstamp; - tcp_openreq_init(req, &tp, skb); + tmptp.tstamp_ok = tmptp.saw_tstamp; + tcp_openreq_init(req, &tmptp, skb); req->class = &or_ipv6; ipv6_addr_copy(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr); @@ -1181,8 +1193,8 @@ TCP_ECN_create_request(req, skb->h.th); req->af.v6_req.pktopts = NULL; if (ipv6_opt_accepted(sk, skb) || - sk->net_pinfo.af_inet6.rxopt.bits.rxinfo || - sk->net_pinfo.af_inet6.rxopt.bits.rxhlim) { + np->rxopt.bits.rxinfo || + np->rxopt.bits.rxhlim) { atomic_inc(&skb->users); req->af.v6_req.pktopts = skb; } @@ -1216,8 +1228,9 @@ struct open_request *req, struct dst_entry *dst) { - struct ipv6_pinfo *np; + struct ipv6_pinfo *newnp, *np = inet6_sk(sk); struct flowi fl; + struct inet_opt *newinet; struct tcp_opt *newtp; struct sock *newsk; struct ipv6_txoptions *opt; @@ -1232,22 +1245,23 @@ if (newsk == NULL) return NULL; - np = &newsk->net_pinfo.af_inet6; + newnp = inet6_sk(newsk); + newtp = tcp_sk(newsk); - ipv6_addr_set(&np->daddr, 0, 0, __constant_htonl(0x0000FFFF), + ipv6_addr_set(&newnp->daddr, 0, 0, htonl(0x0000FFFF), newsk->daddr); - ipv6_addr_set(&np->saddr, 0, 0, __constant_htonl(0x0000FFFF), + ipv6_addr_set(&newnp->saddr, 0, 0, htonl(0x0000FFFF), newsk->saddr); - ipv6_addr_copy(&np->rcv_saddr, &np->saddr); + ipv6_addr_copy(&newnp->rcv_saddr, &newnp->saddr); - newsk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped; + newtp->af_specific = &ipv6_mapped; newsk->backlog_rcv = tcp_v4_do_rcv; - newsk->net_pinfo.af_inet6.pktoptions = NULL; - newsk->net_pinfo.af_inet6.opt = NULL; - newsk->net_pinfo.af_inet6.mcast_oif = tcp_v6_iif(skb); - newsk->net_pinfo.af_inet6.mcast_hops = skb->nh.ipv6h->hop_limit; + newnp->pktoptions = NULL; + newnp->opt = NULL; + newnp->mcast_oif = tcp_v6_iif(skb); + newnp->mcast_hops = skb->nh.ipv6h->hop_limit; /* Charge newly allocated IPv6 socket. Though it is mapped, * it is IPv6 yet. @@ -1261,17 +1275,17 @@ worked with IPv6 af_tcp.af_specific. Sync it now. */ - tcp_sync_mss(newsk, newsk->tp_pinfo.af_tcp.pmtu_cookie); + tcp_sync_mss(newsk, newtp->pmtu_cookie); return newsk; } - opt = sk->net_pinfo.af_inet6.opt; + opt = np->opt; if (tcp_acceptq_is_full(sk)) goto out_overflow; - if (sk->net_pinfo.af_inet6.rxopt.bits.srcrt == 2 && + if (np->rxopt.bits.srcrt == 2 && opt == NULL && req->af.v6_req.pktopts) { struct inet6_skb_parm *rxopt = (struct inet6_skb_parm *)req->af.v6_req.pktopts->cb; if (rxopt->srcrt) @@ -1310,35 +1324,36 @@ ip6_dst_store(newsk, dst, NULL); sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; - newtp = &(newsk->tp_pinfo.af_tcp); - - np = &newsk->net_pinfo.af_inet6; - ipv6_addr_copy(&np->daddr, &req->af.v6_req.rmt_addr); - ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr); - ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr); + newtp = tcp_sk(newsk); + newinet = inet_sk(newsk); + newnp = inet6_sk(newsk); + ipv6_addr_copy(&newnp->daddr, &req->af.v6_req.rmt_addr); + ipv6_addr_copy(&newnp->saddr, &req->af.v6_req.loc_addr); + ipv6_addr_copy(&newnp->rcv_saddr, &req->af.v6_req.loc_addr); newsk->bound_dev_if = req->af.v6_req.iif; /* Now IPv6 options... First: no IPv4 options. */ - newsk->protinfo.af_inet.opt = NULL; + newinet->opt = NULL; /* Clone RX bits */ - np->rxopt.all = sk->net_pinfo.af_inet6.rxopt.all; + newnp->rxopt.all = np->rxopt.all; /* Clone pktoptions received with SYN */ - np->pktoptions = NULL; + newnp->pktoptions = NULL; if (req->af.v6_req.pktopts) { - np->pktoptions = skb_clone(req->af.v6_req.pktopts, GFP_ATOMIC); + newnp->pktoptions = skb_clone(req->af.v6_req.pktopts, + GFP_ATOMIC); kfree_skb(req->af.v6_req.pktopts); req->af.v6_req.pktopts = NULL; - if (np->pktoptions) - skb_set_owner_r(np->pktoptions, newsk); + if (newnp->pktoptions) + skb_set_owner_r(newnp->pktoptions, newsk); } - np->opt = NULL; - np->mcast_oif = tcp_v6_iif(skb); - np->mcast_hops = skb->nh.ipv6h->hop_limit; + newnp->opt = NULL; + newnp->mcast_oif = tcp_v6_iif(skb); + newnp->mcast_hops = skb->nh.ipv6h->hop_limit; /* Clone native IPv6 options from listening socket (if any) @@ -1347,22 +1362,23 @@ to newsk. */ if (opt) { - np->opt = ipv6_dup_options(newsk, opt); - if (opt != sk->net_pinfo.af_inet6.opt) + newnp->opt = ipv6_dup_options(newsk, opt); + if (opt != np->opt) sock_kfree_s(sk, opt, opt->tot_len); } newtp->ext_header_len = 0; - if (np->opt) - newtp->ext_header_len = np->opt->opt_nflen + np->opt->opt_flen; + if (newnp->opt) + newtp->ext_header_len = newnp->opt->opt_nflen + + newnp->opt->opt_flen; tcp_sync_mss(newsk, dst->pmtu); newtp->advmss = dst->advmss; tcp_initialize_rcv_mss(newsk); - newsk->daddr = LOOPBACK4_IPV6; - newsk->saddr = LOOPBACK4_IPV6; - newsk->rcv_saddr= LOOPBACK4_IPV6; + newsk->daddr = LOOPBACK4_IPV6; + newsk->saddr = LOOPBACK4_IPV6; + newsk->rcv_saddr = LOOPBACK4_IPV6; __tcp_v6_hash(newsk); tcp_inherit_port(sk, newsk); @@ -1373,7 +1389,7 @@ NET_INC_STATS_BH(ListenOverflows); out: NET_INC_STATS_BH(ListenDrops); - if (opt && opt != sk->net_pinfo.af_inet6.opt) + if (opt && opt != np->opt) sock_kfree_s(sk, opt, opt->tot_len); dst_release(dst); return NULL; @@ -1410,6 +1426,8 @@ */ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) { + struct ipv6_pinfo *np = inet6_sk(sk); + struct tcp_opt *tp; #ifdef CONFIG_FILTER struct sk_filter *filter; #endif @@ -1452,7 +1470,7 @@ by tcp. Feel free to propose better solution. --ANK (980728) */ - if (sk->net_pinfo.af_inet6.rxopt.all) + if (np->rxopt.all) opt_skb = skb_clone(skb, GFP_ATOMIC); if (sk->state == TCP_ESTABLISHED) { /* Fast path */ @@ -1515,18 +1533,19 @@ 3. socket is not in passive state. 4. Finally, it really contains options, which user wants to receive. */ - if (TCP_SKB_CB(opt_skb)->end_seq == sk->tp_pinfo.af_tcp.rcv_nxt && + tp = tcp_sk(sk); + if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt && !((1<state)&(TCPF_CLOSE|TCPF_LISTEN))) { - if (sk->net_pinfo.af_inet6.rxopt.bits.rxinfo) - sk->net_pinfo.af_inet6.mcast_oif = tcp_v6_iif(opt_skb); - if (sk->net_pinfo.af_inet6.rxopt.bits.rxhlim) - sk->net_pinfo.af_inet6.mcast_hops = opt_skb->nh.ipv6h->hop_limit; + if (np->rxopt.bits.rxinfo) + np->mcast_oif = tcp_v6_iif(opt_skb); + if (np->rxopt.bits.rxhlim) + np->mcast_hops = opt_skb->nh.ipv6h->hop_limit; if (ipv6_opt_accepted(sk, opt_skb)) { skb_set_owner_r(opt_skb, sk); - opt_skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, opt_skb); + opt_skb = xchg(&np->pktoptions, opt_skb); } else { __kfree_skb(opt_skb); - opt_skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, NULL); + opt_skb = xchg(&np->pktoptions, NULL); } } @@ -1656,7 +1675,7 @@ { int err; struct dst_entry *dst; - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); dst = __sk_dst_check(sk, np->dst_cookie); @@ -1695,7 +1714,7 @@ static int tcp_v6_xmit(struct sk_buff *skb) { struct sock *sk = skb->sk; - struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct flowi fl; struct dst_entry *dst; @@ -1737,7 +1756,7 @@ static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr) { - struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) uaddr; sin6->sin6_family = AF_INET6; @@ -1799,7 +1818,7 @@ */ static int tcp_v6_init_sock(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); skb_queue_head_init(&tp->out_of_order_queue); tcp_init_xmit_timers(sk); @@ -1826,7 +1845,7 @@ sk->state = TCP_CLOSE; - sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific; + tp->af_specific = &ipv6_specific; sk->write_space = tcp_write_space; sk->use_write_queue = 1; @@ -1841,7 +1860,7 @@ static int tcp_v6_destroy_sock(struct sock *sk) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); tcp_clear_xmit_timers(sk); @@ -1905,10 +1924,11 @@ __u16 destp, srcp; int timer_active; unsigned long timer_expires; - struct tcp_opt *tp = &sp->tp_pinfo.af_tcp; + struct tcp_opt *tp = tcp_sk(sp); + struct ipv6_pinfo *np = inet6_sk(sp); - dest = &sp->net_pinfo.af_inet6.daddr; - src = &sp->net_pinfo.af_inet6.rcv_saddr; + dest = &np->daddr; + src = &np->rcv_saddr; destp = ntohs(sp->dport); srcp = ntohs(sp->sport); if (tp->pending == TCP_TIME_RETRANS) { @@ -2004,7 +2024,7 @@ for (sk = tcp_listening_hash[i]; sk; sk = sk->next, num++) { struct open_request *req; int uid; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct tcp_opt *tp = tcp_sk(sk); if (sk->family != PF_INET6) continue; @@ -2122,13 +2142,10 @@ static struct inet6_protocol tcpv6_protocol = { - tcp_v6_rcv, /* TCP handler */ - tcp_v6_err, /* TCP error control */ - NULL, /* next */ - IPPROTO_TCP, /* protocol ID */ - 0, /* copy */ - NULL, /* data */ - "TCPv6" /* name */ + handler: tcp_v6_rcv, + err_handler: tcp_v6_err, + protocol: IPPROTO_TCP, + name: "TCPv6", }; extern struct proto_ops inet6_stream_ops; diff -Nru a/net/ipv6/udp.c b/net/ipv6/udp.c --- a/net/ipv6/udp.c Tue Feb 19 18:08:58 2002 +++ b/net/ipv6/udp.c Tue Feb 19 18:08:58 2002 @@ -7,7 +7,7 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.64 2001/09/01 00:31:50 davem Exp $ + * $Id: udp.c,v 1.65 2002/02/01 22:01:04 davem Exp $ * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support @@ -98,18 +98,20 @@ udp_port_rover = snum = result; } else { struct sock *sk2; - int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr); + struct ipv6_pinfo *np = inet6_sk(sk); + int addr_type = ipv6_addr_type(&np->rcv_saddr); for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) { + struct ipv6_pinfo *np2 = inet6_sk(sk2); + if (sk2->num == snum && sk2 != sk && sk2->bound_dev_if == sk->bound_dev_if && (!sk2->rcv_saddr || addr_type == IPV6_ADDR_ANY || - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, - &sk2->net_pinfo.af_inet6.rcv_saddr) || + !ipv6_addr_cmp(&np->rcv_saddr, &np2->rcv_saddr) || (addr_type == IPV6_ADDR_MAPPED && sk2->family == AF_INET && sk->rcv_saddr == sk2->rcv_saddr)) && @@ -167,20 +169,20 @@ for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) { if((sk->num == hnum) && (sk->family == PF_INET6)) { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); int score = 0; if(sk->dport) { if(sk->dport != sport) continue; score++; } - if(!ipv6_addr_any(&np->rcv_saddr)) { - if(ipv6_addr_cmp(&np->rcv_saddr, daddr)) + if (!ipv6_addr_any(&np->rcv_saddr)) { + if (ipv6_addr_cmp(&np->rcv_saddr, daddr)) continue; score++; } - if(!ipv6_addr_any(&np->daddr)) { - if(ipv6_addr_cmp(&np->daddr, saddr)) + if (!ipv6_addr_any(&np->daddr)) { + if (ipv6_addr_cmp(&np->daddr, saddr)) continue; score++; } @@ -211,7 +213,7 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct in6_addr *daddr; struct in6_addr saddr; struct dst_entry *dst; @@ -266,19 +268,15 @@ if (err < 0) return err; - ipv6_addr_set(&np->daddr, 0, 0, - __constant_htonl(0x0000ffff), - sk->daddr); - - if(ipv6_addr_any(&np->saddr)) { - ipv6_addr_set(&np->saddr, 0, 0, - __constant_htonl(0x0000ffff), + ipv6_addr_set(&np->daddr, 0, 0, htonl(0x0000ffff), sk->daddr); + + if (ipv6_addr_any(&np->saddr)) { + ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000ffff), sk->saddr); } - if(ipv6_addr_any(&np->rcv_saddr)) { - ipv6_addr_set(&np->rcv_saddr, 0, 0, - __constant_htonl(0x0000ffff), + if (ipv6_addr_any(&np->rcv_saddr)) { + ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000ffff), sk->rcv_saddr); } return 0; @@ -322,7 +320,7 @@ fl.fl6_dst = rt0->addr; } } else if (np->opt && np->opt->srcrt) { - struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; + struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt; fl.fl6_dst = rt0->addr; } @@ -341,10 +339,10 @@ err = ipv6_get_saddr(dst, daddr, &saddr); if (err == 0) { - if(ipv6_addr_any(&np->saddr)) + if (ipv6_addr_any(&np->saddr)) ipv6_addr_copy(&np->saddr, &saddr); - if(ipv6_addr_any(&np->rcv_saddr)) { + if (ipv6_addr_any(&np->rcv_saddr)) { ipv6_addr_copy(&np->rcv_saddr, &saddr); sk->rcv_saddr = LOOPBACK4_IPV6; } @@ -368,6 +366,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, int flags, int *addr_len) { + struct ipv6_pinfo *np = inet6_sk(sk); struct sk_buff *skb; int copied, err; @@ -416,15 +415,17 @@ sin6->sin6_scope_id = 0; if (skb->protocol == __constant_htons(ETH_P_IP)) { + struct inet_opt *inet = inet_sk(sk); + ipv6_addr_set(&sin6->sin6_addr, 0, 0, __constant_htonl(0xffff), skb->nh.iph->saddr); - if (sk->protinfo.af_inet.cmsg_flags) + if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); } else { memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr, sizeof(struct in6_addr)); - if (sk->net_pinfo.af_inet6.rxopt.all) + if (np->rxopt.all) datagram_recv_ctl(sk, msg, skb); if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; @@ -464,6 +465,7 @@ void udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, int type, int code, int offset, __u32 info) { + struct ipv6_pinfo *np; struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data; struct net_device *dev = skb->dev; struct in6_addr *saddr = &hdr->saddr; @@ -477,15 +479,15 @@ if (sk == NULL) return; - if (!icmpv6_err_convert(type, code, &err) && - !sk->net_pinfo.af_inet6.recverr) + np = inet6_sk(sk); + + if (!icmpv6_err_convert(type, code, &err) && !np->recverr) goto out; - if (sk->state!=TCP_ESTABLISHED && - !sk->net_pinfo.af_inet6.recverr) + if (sk->state != TCP_ESTABLISHED && !np->recverr) goto out; - if (sk->net_pinfo.af_inet6.recverr) + if (np->recverr) ipv6_icmp_error(sk, skb, err, uh->dest, ntohl(info), (u8 *)(uh+1)); sk->err = err; @@ -527,20 +529,20 @@ unsigned short num = ntohs(loc_port); for(; s; s = s->next) { if(s->num == num) { - struct ipv6_pinfo *np = &s->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(s); if(s->dport) { if(s->dport != rmt_port) continue; } - if(!ipv6_addr_any(&np->daddr) && - ipv6_addr_cmp(&np->daddr, rmt_addr)) + if (!ipv6_addr_any(&np->daddr) && + ipv6_addr_cmp(&np->daddr, rmt_addr)) continue; if (s->bound_dev_if && s->bound_dev_if != dif) continue; - if(!ipv6_addr_any(&np->rcv_saddr)) { - if(ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0) + if (!ipv6_addr_any(&np->rcv_saddr)) { + if (!ipv6_addr_cmp(&np->rcv_saddr, loc_addr)) return s; } if(!inet6_mc_check(s, loc_addr)) @@ -755,7 +757,7 @@ { struct ipv6_txoptions opt_space; struct udpv6fakehdr udh; - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + struct ipv6_pinfo *np = inet6_sk(sk); struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name; struct ipv6_txoptions *opt = NULL; struct ip6_flowlabel *flowlabel = NULL; @@ -805,8 +807,8 @@ /* Otherwise it will be difficult to maintain sk->dst_cache. */ if (sk->state == TCP_ESTABLISHED && - !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr)) - daddr = &sk->net_pinfo.af_inet6.daddr; + !ipv6_addr_cmp(daddr, &np->daddr)) + daddr = &np->daddr; if (addr_len >= sizeof(struct sockaddr_in6) && sin6->sin6_scope_id && @@ -817,7 +819,7 @@ return -ENOTCONN; udh.uh.dest = sk->dport; - daddr = &sk->net_pinfo.af_inet6.daddr; + daddr = &np->daddr; fl.fl6_flowlabel = np->flow_label; } @@ -891,15 +893,11 @@ return ulen; } -static struct inet6_protocol udpv6_protocol = -{ - udpv6_rcv, /* UDP handler */ - udpv6_err, /* UDP error control */ - NULL, /* next */ - IPPROTO_UDP, /* protocol ID */ - 0, /* copy */ - NULL, /* data */ - "UDPv6" /* name */ +static struct inet6_protocol udpv6_protocol = { + handler: udpv6_rcv, + err_handler: udpv6_err, + protocol: IPPROTO_UDP, + name: "UDPv6", }; #define LINE_LEN 190 @@ -907,11 +905,12 @@ static void get_udp6_sock(struct sock *sp, char *tmpbuf, int i) { + struct ipv6_pinfo *np = inet6_sk(sp); struct in6_addr *dest, *src; __u16 destp, srcp; - dest = &sp->net_pinfo.af_inet6.daddr; - src = &sp->net_pinfo.af_inet6.rcv_saddr; + dest = &np->daddr; + src = &np->rcv_saddr; destp = ntohs(sp->dport); srcp = ntohs(sp->sport); sprintf(tmpbuf, diff -Nru a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c --- a/net/ipx/af_ipx.c Tue Feb 19 18:08:58 2002 +++ b/net/ipx/af_ipx.c Tue Feb 19 18:08:58 2002 @@ -73,6 +73,9 @@ * Petr Vandrovec for review and good suggestions. (acme) * Revision 047: Cleanups, CodingStyle changes, move the ncp connection * hack out of line (acme) + * Revision 048: Use sk->protinfo to store the pointer to IPX private + * area, remove af_ipx from sk->protinfo and move ipx_opt + * to include/net/ipx.h, use IPX_SK like DecNET, etc * * Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT * pair. Also, now usage count is managed this way @@ -103,6 +106,7 @@ #include #include #include +#include #include #include #include @@ -151,8 +155,6 @@ static ipx_interface *ipx_primary_net; static ipx_interface *ipx_internal_net; -#define IPX_SKB_CB(__skb) ((struct ipx_cb *)&((__skb)->cb[0])) - #undef IPX_REFCNT_DEBUG #ifdef IPX_REFCNT_DEBUG atomic_t ipx_sock_nr; @@ -218,10 +220,9 @@ void ipx_remove_socket(struct sock *sk) { struct sock *s; - ipx_interface *intrfc; - /* Determine interface with which socket is associated */ - intrfc = sk->protinfo.af_ipx.intrfc; + ipx_interface *intrfc = ipx_sk(sk)->intrfc; + if (!intrfc) goto out; @@ -323,7 +324,7 @@ ipxitf_hold(intrfc); sock_hold(sk); spin_lock_bh(&intrfc->if_sklist_lock); - sk->protinfo.af_ipx.intrfc = intrfc; + ipx_sk(sk)->intrfc = intrfc; sk->next = NULL; if (!intrfc->if_sklist) intrfc->if_sklist = sk; @@ -343,7 +344,7 @@ { struct sock *s = intrfc->if_sklist; - while (s && s->protinfo.af_ipx.port != port) + while (s && ipx_sk(s)->port != port) s = s->next; return s; @@ -375,8 +376,10 @@ s = intrfc->if_sklist; while (s) { - if (s->protinfo.af_ipx.port == port && - !memcmp(node, s->protinfo.af_ipx.node, IPX_NODE_LEN)) + struct ipx_opt *ipxs = ipx_sk(s); + + if (ipxs->port == port && + !memcmp(node, ipxs->node, IPX_NODE_LEN)) break; s = s->next; } @@ -399,10 +402,12 @@ spin_lock_bh(&intrfc->if_sklist_lock); /* error sockets */ for (s = intrfc->if_sklist; s;) { + struct ipx_opt *ipxs = ipx_sk(s); + s->err = ENOLINK; s->error_report(s); - s->protinfo.af_ipx.intrfc = NULL; - s->protinfo.af_ipx.port = 0; + ipxs->intrfc = NULL; + ipxs->port = 0; s->zapped = 1; /* Indicates it is no longer bound */ t = s; s = s->next; @@ -491,10 +496,11 @@ s = intrfc->if_sklist; while (s) { - if (s->protinfo.af_ipx.port == ipx->ipx_dest.sock && + struct ipx_opt *ipxs = ipx_sk(s); + + if (ipxs->port == ipx->ipx_dest.sock && (is_broadcast || !memcmp(ipx->ipx_dest.node, - s->protinfo.af_ipx.node, - IPX_NODE_LEN))) { + ipxs->node, IPX_NODE_LEN))) { /* We found a socket to which to send */ struct sk_buff *skb1; @@ -550,7 +556,7 @@ * SIOCIPXNCPCONN. */ spin_lock_bh(&intrfc->if_sklist_lock); for (sk = intrfc->if_sklist; - sk && sk->protinfo.af_ipx.ipx_ncp_conn != connection; + sk && ipx_sk(sk)->ipx_ncp_conn != connection; sk = sk->next); if (sk) sock_hold(sk); @@ -769,7 +775,7 @@ /* set up data link and physical headers */ skb->dev = dev; - skb->protocol = __constant_htons(ETH_P_IPX); + skb->protocol = htons(ETH_P_IPX); dl->datalink_header(dl, skb, dest_node); /* Send it out */ @@ -1042,17 +1048,13 @@ switch (type) { case IPX_FRAME_ETHERII: - ret = __constant_htons(ETH_P_IPX); - break; + ret = htons(ETH_P_IPX); break; case IPX_FRAME_8022: - ret = __constant_htons(ETH_P_802_2); - break; + ret = htons(ETH_P_802_2); break; case IPX_FRAME_SNAP: - ret = __constant_htons(ETH_P_SNAP); - break; + ret = htons(ETH_P_SNAP); break; case IPX_FRAME_8023: - ret = __constant_htons(ETH_P_802_3); - break; + ret = htons(ETH_P_802_3); break; } return ret; @@ -1096,12 +1098,12 @@ "obsolete Use 802.2 instead.\n"); /* fall through */ case IPX_FRAME_8022: - dlink_type = __constant_htons(ETH_P_802_2); + dlink_type = htons(ETH_P_802_2); datalink = p8022_datalink; break; case IPX_FRAME_ETHERII: if (dev->type != ARPHRD_IEEE802) { - dlink_type = __constant_htons(ETH_P_IPX); + dlink_type = htons(ETH_P_IPX); datalink = pEII_datalink; break; } else @@ -1110,11 +1112,11 @@ "instead.\n"); /* fall through */ case IPX_FRAME_SNAP: - dlink_type = __constant_htons(ETH_P_SNAP); + dlink_type = htons(ETH_P_SNAP); datalink = pSNAP_datalink; break; case IPX_FRAME_8023: - dlink_type = __constant_htons(ETH_P_802_3); + dlink_type = htons(ETH_P_802_3); datalink = p8023_datalink; break; case IPX_FRAME_NONE: @@ -1489,7 +1491,7 @@ sum += *p++; /* Add on the last part word if it exists */ - if (packet->ipx_pktsize & __constant_htons(1)) + if (packet->ipx_pktsize & htons(1)) sum += ntohs(0xff00) & *p; /* Do final fixup */ @@ -1509,6 +1511,7 @@ struct iovec *iov, int len, int noblock) { struct sk_buff *skb; + struct ipx_opt *ipxs = ipx_sk(sk); ipx_interface *intrfc; struct ipxhdr *ipx; int size; @@ -1548,21 +1551,21 @@ IPX_SKB_CB(skb)->last_hop.index = -1; #ifdef CONFIG_IPX_INTERN - IPX_SKB_CB(skb)->ipx_source_net = sk->protinfo.af_ipx.intrfc->if_netnum; - memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.node, IPX_NODE_LEN); + IPX_SKB_CB(skb)->ipx_source_net = ipxs->intrfc->if_netnum; + memcpy(ipx->ipx_source.node, ipxs->node, IPX_NODE_LEN); #else - err = ntohs(sk->protinfo.af_ipx.port); + err = ntohs(ipxs->port); if (err == 0x453 || err == 0x452) { /* RIP/SAP special handling for mars_nwe */ IPX_SKB_CB(skb)->ipx_source_net = intrfc->if_netnum; memcpy(ipx->ipx_source.node, intrfc->if_node, IPX_NODE_LEN); } else { - IPX_SKB_CB(skb)->ipx_source_net = - sk->protinfo.af_ipx.intrfc->if_netnum; - memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN); + IPX_SKB_CB(skb)->ipx_source_net = ipxs->intrfc->if_netnum; + memcpy(ipx->ipx_source.node, ipxs->intrfc->if_node, + IPX_NODE_LEN); } #endif /* CONFIG_IPX_INTERN */ - ipx->ipx_source.sock = sk->protinfo.af_ipx.port; + ipx->ipx_source.sock = ipxs->port; IPX_SKB_CB(skb)->ipx_dest_net = usipx->sipx_network; memcpy(ipx->ipx_dest.node, usipx->sipx_node, IPX_NODE_LEN); ipx->ipx_dest.sock = usipx->sipx_port; @@ -1743,21 +1746,19 @@ ipxitf_hold(i); spin_lock_bh(&i->if_sklist_lock); for (s = i->if_sklist; s; s = s->next) { + struct ipx_opt *ipxs = ipx_sk(s); #ifdef CONFIG_IPX_INTERN len += sprintf(buffer + len, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ", - (unsigned long) htonl(s->protinfo.af_ipx.intrfc->if_netnum), - s->protinfo.af_ipx.node[0], - s->protinfo.af_ipx.node[1], - s->protinfo.af_ipx.node[2], - s->protinfo.af_ipx.node[3], - s->protinfo.af_ipx.node[4], - s->protinfo.af_ipx.node[5], - htons(s->protinfo.af_ipx.port)); + (unsigned long)htonl(ipxs->intrfc->if_netnum), + ipxs->node[0], ipxs->node[1], + ipxs->node[2], ipxs->node[3], + ipxs->node[4], ipxs->node[5], + htons(ipxs->port)); #else len += sprintf(buffer + len, "%08lX:%04X ", (unsigned long) htonl(i->if_netnum), - htons(s->protinfo.af_ipx.port)); + htons(ipxs->port)); #endif /* CONFIG_IPX_INTERN */ if (s->state != TCP_ESTABLISHED) len += sprintf(buffer + len, "%-28s", @@ -1765,14 +1766,14 @@ else { len += sprintf(buffer + len, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ", - (unsigned long) htonl(s->protinfo.af_ipx.dest_addr.net), - s->protinfo.af_ipx.dest_addr.node[0], - s->protinfo.af_ipx.dest_addr.node[1], - s->protinfo.af_ipx.dest_addr.node[2], - s->protinfo.af_ipx.dest_addr.node[3], - s->protinfo.af_ipx.dest_addr.node[4], - s->protinfo.af_ipx.dest_addr.node[5], - htons(s->protinfo.af_ipx.dest_addr.sock)); + (unsigned long)htonl(ipxs->dest_addr.net), + ipxs->dest_addr.node[0], + ipxs->dest_addr.node[1], + ipxs->dest_addr.node[2], + ipxs->dest_addr.node[3], + ipxs->dest_addr.node[4], + ipxs->dest_addr.node[5], + htons(ipxs->dest_addr.sock)); } len += sprintf(buffer + len, "%08X %08X ", @@ -1868,7 +1869,7 @@ if (!(level == SOL_IPX && optname == IPX_TYPE)) goto out; - sk->protinfo.af_ipx.type = opt; + ipx_sk(sk)->type = opt; ret = 0; out: return ret; } @@ -1884,7 +1885,7 @@ if (!(level == SOL_IPX && optname == IPX_TYPE)) goto out; - val = sk->protinfo.af_ipx.type; + val = ipx_sk(sk)->type; ret = -EFAULT; if (get_user(len, optlen)) @@ -1906,14 +1907,20 @@ static int ipx_create(struct socket *sock, int protocol) { int ret = -ESOCKTNOSUPPORT; + struct ipx_opt *ipx = NULL; struct sock *sk; + MOD_INC_USE_COUNT; switch (sock->type) { case SOCK_DGRAM: - sk = sk_alloc(PF_IPX, GFP_KERNEL, 1); + sk = sk_alloc(PF_IPX, GFP_KERNEL, 1, NULL); ret = -ENOMEM; if (!sk) - goto out; + goto decmod; + ipx = ipx_sk(sk) = kmalloc(sizeof(*ipx), GFP_KERNEL); + if (!ipx) + goto outsk; + memset(ipx, 0, sizeof(*ipx)); sock->ops = &ipx_dgram_ops; break; @@ -1924,12 +1931,12 @@ */ if (spx_family_ops) { ret = spx_family_ops->create(sock, protocol); - goto out; + goto decmod; } /* Fall through if SPX is not loaded */ case SOCK_STREAM: /* Allow higher levels to piggyback */ default: - goto out; + goto decmod; } #ifdef IPX_REFCNT_DEBUG atomic_inc(&ipx_sock_nr); @@ -1937,12 +1944,12 @@ atomic_read(&ipx_sock_nr)); #endif sock_init_data(sock, sk); - sk->destruct = NULL; - sk->no_check = 1; /* Checksum off by default */ - - MOD_INC_USE_COUNT; + sk->no_check = 1; /* Checksum off by default */ ret = 0; out: return ret; +outsk: sk_free(sk); +decmod: MOD_DEC_USE_COUNT; + goto out; } static int ipx_release(struct socket *sock) @@ -1991,6 +1998,7 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; + struct ipx_opt *ipxs = ipx_sk(sk); ipx_interface *intrfc; struct sockaddr_ipx *addr = (struct sockaddr_ipx *)uaddr; int ret = -EINVAL; @@ -2016,7 +2024,7 @@ !capable(CAP_NET_ADMIN)) goto out_put; - sk->protinfo.af_ipx.port = addr->sipx_port; + ipxs->port = addr->sipx_port; #ifdef CONFIG_IPX_INTERN if (intrfc == ipx_internal_net) { @@ -2029,16 +2037,13 @@ if (!memcmp(addr->sipx_node, ipx_broadcast_node, IPX_NODE_LEN)) goto out_put; if (!memcmp(addr->sipx_node, ipx_this_node, IPX_NODE_LEN)) - memcpy(sk->protinfo.af_ipx.node, intrfc->if_node, - IPX_NODE_LEN); + memcpy(ipxs->node, intrfc->if_node, IPX_NODE_LEN); else - memcpy(sk->protinfo.af_ipx.node, addr->sipx_node, - IPX_NODE_LEN); + memcpy(ipxs->node, addr->sipx_node, IPX_NODE_LEN); ret = -EADDRINUSE; - if (ipxitf_find_internal_socket(intrfc, - sk->protinfo.af_ipx.node, - sk->protinfo.af_ipx.port)) { + if (ipxitf_find_internal_socket(intrfc, ipxs->node, + ipxs->port)) { SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n", ntohs((int)addr->sipx_port)); @@ -2050,8 +2055,7 @@ * with the ipx routing ioctl() */ - memcpy(sk->protinfo.af_ipx.node, intrfc->if_node, - IPX_NODE_LEN); + memcpy(ipxs->node, intrfc->if_node, IPX_NODE_LEN); ret = -EADDRINUSE; if (ipxitf_find_socket(intrfc, addr->sipx_port)) { @@ -2090,6 +2094,7 @@ int addr_len, int flags) { struct sock *sk = sock->sk; + struct ipx_opt *ipxs = ipx_sk(sk); struct sockaddr_ipx *addr; int ret = -EINVAL; ipx_route *rt; @@ -2102,7 +2107,7 @@ addr = (struct sockaddr_ipx *)uaddr; /* put the autobinding in */ - if (!sk->protinfo.af_ipx.port) { + if (!ipxs->port) { struct sockaddr_ipx uaddr; uaddr.sipx_port = 0; @@ -2110,9 +2115,9 @@ #ifdef CONFIG_IPX_INTERN ret = -ENETDOWN; - if (!sk->protinfo.af_ipx.intrfc) + if (!ipxs->intrfc) goto out; /* Someone zonked the iface */ - memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc->if_node, + memcpy(uaddr.sipx_node, ipxs->intrfc->if_node, IPX_NODE_LEN); #endif /* CONFIG_IPX_INTERN */ @@ -2129,11 +2134,10 @@ if (!rt && !(!addr->sipx_network && ipx_primary_net)) goto out; - sk->protinfo.af_ipx.dest_addr.net = addr->sipx_network; - sk->protinfo.af_ipx.dest_addr.sock = addr->sipx_port; - memcpy(sk->protinfo.af_ipx.dest_addr.node, - addr->sipx_node, IPX_NODE_LEN); - sk->protinfo.af_ipx.type = addr->sipx_type; + ipxs->dest_addr.net = addr->sipx_network; + ipxs->dest_addr.sock = addr->sipx_port; + memcpy(ipxs->dest_addr.node, addr->sipx_node, IPX_NODE_LEN); + ipxs->type = addr->sipx_type; if (sock->type == SOCK_DGRAM) { sock->state = SS_CONNECTED; @@ -2153,6 +2157,7 @@ ipx_address *addr; struct sockaddr_ipx sipx; struct sock *sk = sock->sk; + struct ipx_opt *ipxs = ipx_sk(sk); int ret; *uaddr_len = sizeof(struct sockaddr_ipx); @@ -2162,20 +2167,17 @@ if (sk->state != TCP_ESTABLISHED) goto out; - addr = &sk->protinfo.af_ipx.dest_addr; + addr = &ipxs->dest_addr; sipx.sipx_network = addr->net; sipx.sipx_port = addr->sock; memcpy(sipx.sipx_node, addr->node, IPX_NODE_LEN); } else { - if (sk->protinfo.af_ipx.intrfc) { - sipx.sipx_network = - sk->protinfo.af_ipx.intrfc->if_netnum; + if (ipxs->intrfc) { + sipx.sipx_network = ipxs->intrfc->if_netnum; #ifdef CONFIG_IPX_INTERN - memcpy(sipx.sipx_node, sk->protinfo.af_ipx.node, - IPX_NODE_LEN); + memcpy(sipx.sipx_node, ipxs->node, IPX_NODE_LEN); #else - memcpy(sipx.sipx_node, - sk->protinfo.af_ipx.intrfc->if_node, + memcpy(sipx.sipx_node, ipxs->intrfc->if_node, IPX_NODE_LEN); #endif /* CONFIG_IPX_INTERN */ @@ -2184,11 +2186,11 @@ memset(sipx.sipx_node, '\0', IPX_NODE_LEN); } - sipx.sipx_port = sk->protinfo.af_ipx.port; + sipx.sipx_port = ipxs->port; } sipx.sipx_family = AF_IPX; - sipx.sipx_type = sk->protinfo.af_ipx.type; + sipx.sipx_type = ipxs->type; memcpy(uaddr, &sipx, sizeof(sipx)); ret = 0; @@ -2251,6 +2253,7 @@ struct scm_cookie *scm) { struct sock *sk = sock->sk; + struct ipx_opt *ipxs = ipx_sk(sk); struct sockaddr_ipx *usipx = (struct sockaddr_ipx *)msg->msg_name; struct sockaddr_ipx local_sipx; int ret = -EINVAL; @@ -2263,17 +2266,16 @@ goto out; if (usipx) { - if (!sk->protinfo.af_ipx.port) { + if (!ipxs->port) { struct sockaddr_ipx uaddr; uaddr.sipx_port = 0; uaddr.sipx_network = 0; #ifdef CONFIG_IPX_INTERN ret = -ENETDOWN; - if (!sk->protinfo.af_ipx.intrfc) + if (!ipxs->intrfc) goto out; /* Someone zonked the iface */ - memcpy(uaddr.sipx_node, - sk->protinfo.af_ipx.intrfc->if_node, + memcpy(uaddr.sipx_node, ipxs->intrfc->if_node, IPX_NODE_LEN); #endif ret = ipx_bind(sock, (struct sockaddr *)&uaddr, @@ -2293,11 +2295,10 @@ usipx = &local_sipx; usipx->sipx_family = AF_IPX; - usipx->sipx_type = sk->protinfo.af_ipx.type; - usipx->sipx_port = sk->protinfo.af_ipx.dest_addr.sock; - usipx->sipx_network = sk->protinfo.af_ipx.dest_addr.net; - memcpy(usipx->sipx_node, sk->protinfo.af_ipx.dest_addr.node, - IPX_NODE_LEN); + usipx->sipx_type = ipxs->type; + usipx->sipx_port = ipxs->dest_addr.sock; + usipx->sipx_network = ipxs->dest_addr.net; + memcpy(usipx->sipx_node, ipxs->dest_addr.node, IPX_NODE_LEN); } ret = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len, @@ -2312,13 +2313,14 @@ int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; + struct ipx_opt *ipxs = ipx_sk(sk); struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)msg->msg_name; struct ipxhdr *ipx = NULL; struct sk_buff *skb; int copied, err; /* put the autobinding in */ - if (!sk->protinfo.af_ipx.port) { + if (!ipxs->port) { struct sockaddr_ipx uaddr; uaddr.sipx_port = 0; @@ -2326,10 +2328,9 @@ #ifdef CONFIG_IPX_INTERN err = -ENETDOWN; - if (!sk->protinfo.af_ipx.intrfc) + if (!ipxs->intrfc) goto out; /* Someone zonked the iface */ - memcpy(uaddr.sipx_node, - sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN); + memcpy(uaddr.sipx_node, ipxs->intrfc->if_node, IPX_NODE_LEN); #endif /* CONFIG_IPX_INTERN */ err = ipx_bind(sock, (struct sockaddr *)&uaddr, @@ -2423,7 +2424,7 @@ */ if (!capable(CAP_NET_ADMIN)) return -EPERM; - return get_user(sk->protinfo.af_ipx.ipx_ncp_conn, + return get_user(ipx_sk(sk)->ipx_ncp_conn, (const unsigned short *)(arg)); case SIOCGSTAMP: { @@ -2536,10 +2537,14 @@ static unsigned char ipx_8022_type = 0xE0; static unsigned char ipx_snap_id[5] = { 0x0, 0x0, 0x0, 0x81, 0x37 }; -static char banner[] __initdata = - KERN_INFO "NET4: Linux IPX 0.47 for NET4.0\n" +static char ipx_banner[] __initdata = + KERN_INFO "NET4: Linux IPX 0.48 for NET4.0\n" KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n" \ KERN_INFO "IPX Portions Copyright (c) 2000, 2001 Conectiva, Inc.\n"; +static char ipx_llc_err_msg[] __initdata = + KERN_CRIT "IPX: Unable to register with 802.2\n"; +static char ipx_snap_err_msg[] __initdata = + KERN_CRIT "IPX: Unable to register with SNAP\n"; static int __init ipx_init(void) { @@ -2553,11 +2558,11 @@ p8022_datalink = register_8022_client(ipx_8022_type, ipx_rcv); if (!p8022_datalink) - printk(KERN_CRIT "IPX: Unable to register with 802.2\n"); + printk(ipx_llc_err_msg); pSNAP_datalink = register_snap_client(ipx_snap_id, ipx_rcv); if (!pSNAP_datalink) - printk(KERN_CRIT "IPX: Unable to register with SNAP\n"); + printk(ipx_snap_err_msg); register_netdevice_notifier(&ipx_dev_notifier); ipx_register_sysctl(); @@ -2566,7 +2571,7 @@ proc_net_create("ipx_interface", 0, ipx_interface_get_info); proc_net_create("ipx_route", 0, ipx_rt_get_info); #endif - printk(banner); + printk(ipx_banner); return 0; } diff -Nru a/net/ipx/af_spx.c b/net/ipx/af_spx.c --- a/net/ipx/af_spx.c Tue Feb 19 18:09:00 2002 +++ b/net/ipx/af_spx.c Tue Feb 19 18:09:00 2002 @@ -20,6 +20,8 @@ * Added spx_datagram_poll() so that select() * works now on SPX sockets. Added updating * of the alloc count to follow rmt_seq. + * Arnaldo C. Melo : Use a private slabcache for the old tp_pinfo + * struct sock member, use spx_sk and ipx_sk * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -34,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -60,7 +63,7 @@ static unsigned int spx_datagram_poll(struct file * file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); unsigned int mask; poll_wait(file, sk->sleep, wait); @@ -95,7 +98,7 @@ /* Create the SPX specific data */ static int spx_sock_init(struct sock *sk) { - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); pdata->state = SPX_CLOSED; pdata->sequence = 0; @@ -159,7 +162,7 @@ void spx_close_socket(struct sock *sk) { - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); pdata->state = SPX_CLOSED; sk->state = TCP_CLOSE; @@ -169,7 +172,7 @@ void spx_destroy_socket(struct sock *sk) { - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); struct sk_buff *skb; ipx_remove_socket(sk); @@ -190,7 +193,7 @@ static int spx_release(struct socket *sock) { struct sock *sk = sock->sk; - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); if(sk == NULL) return (0); @@ -285,7 +288,7 @@ sk->ack_backlog--; newsock->sk = newsk; newsk->state = TCP_ESTABLISHED; - newsk->protinfo.af_ipx.dest_addr = newsk->tp_pinfo.af_spx.dest_addr; + ipx_sk(newsk)->dest_addr = spx_sk(newsk)->dest_addr; return (0); } @@ -295,7 +298,7 @@ int addr_len, int flags) { struct sock *sk = sock->sk; - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); struct sockaddr_ipx src; struct sk_buff *skb; int size, err; @@ -313,7 +316,7 @@ if(err) return (err); - pdata->dest_addr = sk->protinfo.af_ipx.dest_addr; + pdata->dest_addr = ipx_sk(sk)->dest_addr; pdata->state = SPX_CONNECTING; sock->state = SS_CONNECTING; sk->state = TCP_SYN_SENT; @@ -423,7 +426,7 @@ /* SPX packet transmit engine */ static int spx_transmit(struct sock *sk, struct sk_buff *skb, int type, int len) { - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); struct ipxspxhdr *ipxh; unsigned long flags; int err; @@ -520,7 +523,7 @@ static void spx_watchdog(unsigned long data) { struct sock *sk = (struct sock*)data; - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); del_timer(&pdata->watchdog); if(pdata->state == SPX_CLOSED) @@ -541,7 +544,7 @@ static void spx_retransmit(unsigned long data) { struct sock *sk = (struct sock*)data; - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); struct sk_buff *skb; unsigned long flags; int err; @@ -614,7 +617,7 @@ { struct sk_buff *skb; struct ipxspxhdr *ipxh; - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); skb = skb_dequeue(&sk->receive_queue); if(skb == NULL) @@ -736,7 +739,7 @@ if(flags&~MSG_DONTWAIT) return (-EINVAL); - offset = ipx_if_offset(sk->tp_pinfo.af_spx.dest_addr.net); + offset = ipx_if_offset(spx_sk(sk)->dest_addr.net); size = offset + sizeof(struct ipxspxhdr) + len; cli(); @@ -770,7 +773,7 @@ struct sk_buff *skb; struct ipxspxhdr *ispxh; struct sock *sk = sock->sk; - struct spx_opt *pdata = &sk->tp_pinfo.af_spx; + struct spx_opt *pdata = spx_sk(sk); struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)msg->msg_name; int copied, err; @@ -906,8 +909,10 @@ SOCKOPS_WRAP(spx, PF_IPX); static struct net_proto_family spx_family_ops = { - family: PF_IPX, - create: spx_create, + family: PF_IPX, + create: spx_create, + sk_size: sizeof(struct sock) + sizeof(struct ipx_opt) + + sizeof(struct spx_opt), }; static char banner[] __initdata = KERN_INFO "NET4: Sequenced Packet eXchange (SPX) 0.02 for Linux NET4.0\n"; @@ -917,6 +922,16 @@ int error; connids = (__u16)jiffies; /* initalize random */ + + /* allocate our sock slab cache */ + + spx_family_ops.sk_cachep = kmem_cache_create("spx_sock", + spx_family_ops.sk_size, + 0, SLAB_HWCACHE_ALIGN, + 0, 0); + if (!spx_family_ops.sk_cachep) + printk(KERN_CRIT __FUNCTION__ + ": Cannot create spx_sock SLAB cache!\n"); error = ipx_register_spx(&ipx_operations, &spx_family_ops); if (error) diff -Nru a/net/irda/af_irda.c b/net/irda/af_irda.c --- a/net/irda/af_irda.c Tue Feb 19 18:08:59 2002 +++ b/net/irda/af_irda.c Tue Feb 19 18:08:59 2002 @@ -56,6 +56,7 @@ #include #include +#include #include #include @@ -728,7 +729,7 @@ { struct sockaddr_irda saddr; struct sock *sk = sock->sk; - struct irda_sock *self = sk->protinfo.irda; + struct irda_sock *self = irda_sk(sk); if (peer) { if (sk->state != TCP_ESTABLISHED) @@ -789,10 +790,9 @@ { struct sock *sk = sock->sk; struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; - struct irda_sock *self; + struct irda_sock *self = irda_sk(sk); int err; - self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); IRDA_DEBUG(2, __FUNCTION__ "(%p)\n", self); @@ -845,15 +845,14 @@ */ static int irda_accept(struct socket *sock, struct socket *newsock, int flags) { - struct irda_sock *self, *new; struct sock *sk = sock->sk; + struct irda_sock *new, *self = irda_sk(sk); struct sock *newsk; struct sk_buff *skb; int err; IRDA_DEBUG(2, __FUNCTION__ "()\n"); - self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); err = irda_create(newsock, sk->protocol); @@ -891,7 +890,7 @@ newsk = newsock->sk; newsk->state = TCP_ESTABLISHED; - new = newsk->protinfo.irda; + new = irda_sk(newsk); ASSERT(new != NULL, return -1;); /* Now attach up the new socket */ @@ -953,10 +952,8 @@ { struct sock *sk = sock->sk; struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; - struct irda_sock *self; + struct irda_sock *self = irda_sk(sk); int err; - - self = sk->protinfo.irda; IRDA_DEBUG(2, __FUNCTION__ "(%p)\n", self); @@ -1078,11 +1075,11 @@ } /* Allocate networking socket */ - if ((sk = sk_alloc(PF_IRDA, GFP_ATOMIC, 1)) == NULL) + if ((sk = sk_alloc(PF_IRDA, GFP_ATOMIC, 1, NULL)) == NULL) return -ENOMEM; /* Allocate IrDA socket */ - self = kmalloc(sizeof(struct irda_sock), GFP_ATOMIC); + self = irda_sk(sk) = kmalloc(sizeof(struct irda_sock), GFP_ATOMIC); if (self == NULL) { sk_free(sk); return -ENOMEM; @@ -1098,7 +1095,6 @@ sk->family = PF_IRDA; sk->protocol = protocol; /* Link networking socket and IrDA socket structs together */ - sk->protinfo.irda = self; self->sk = sk; switch (sock->type) { @@ -1208,9 +1204,9 @@ sk->state_change(sk); /* Destroy IrDA socket */ - irda_destroy_socket(sk->protinfo.irda); + irda_destroy_socket(irda_sk(sk)); /* Prevent sock_def_destruct() to create havoc */ - sk->protinfo.irda = NULL; + irda_sk(sk) = NULL; sock_orphan(sk); sock->sk = NULL; @@ -1281,7 +1277,7 @@ if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; - self = sk->protinfo.irda; + self = irda_sk(sk); ASSERT(self != NULL, return -1;); /* Check if IrTTP is wants us to slow down */ @@ -1334,14 +1330,13 @@ static int irda_recvmsg_dgram(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) { - struct irda_sock *self; struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); struct sk_buff *skb; int copied, err; IRDA_DEBUG(4, __FUNCTION__ "()\n"); - self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, @@ -1404,15 +1399,14 @@ static int irda_recvmsg_stream(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) { - struct irda_sock *self; struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); int noblock = flags & MSG_DONTWAIT; int copied = 0; int target = 1; IRDA_DEBUG(3, __FUNCTION__ "()\n"); - self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); if (sock->flags & __SO_ACCEPTCON) @@ -1531,7 +1525,7 @@ if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; - self = sk->protinfo.irda; + self = irda_sk(sk); ASSERT(self != NULL, return -1;); /* @@ -1594,7 +1588,7 @@ return -EPIPE; } - self = sk->protinfo.irda; + self = irda_sk(sk); ASSERT(self != NULL, return -1;); /* @@ -1636,10 +1630,9 @@ */ static int irda_shutdown(struct socket *sock, int how) { - struct irda_sock *self; struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); - self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); IRDA_DEBUG(1, __FUNCTION__ "(%p)\n", self); @@ -1677,12 +1670,11 @@ poll_table *wait) { struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); unsigned int mask; - struct irda_sock *self; IRDA_DEBUG(4, __FUNCTION__ "()\n"); - self = sk->protinfo.irda; poll_wait(file, sk->sleep, wait); mask = 0; @@ -1808,13 +1800,12 @@ char *optval, int optlen) { struct sock *sk = sock->sk; - struct irda_sock *self; + struct irda_sock *self = irda_sk(sk); struct irda_ias_set *ias_opt; struct ias_object *ias_obj; struct ias_attrib * ias_attr; /* Attribute in IAS object */ int opt; - self = sk->protinfo.irda; ASSERT(self != NULL, return -1;); IRDA_DEBUG(2, __FUNCTION__ "(%p)\n", self); @@ -2107,7 +2098,7 @@ char *optval, int *optlen) { struct sock *sk = sock->sk; - struct irda_sock *self; + struct irda_sock *self = irda_sk(sk); struct irda_device_list list; struct irda_device_info *discoveries; struct irda_ias_set * ias_opt; /* IAS get/query params */ @@ -2119,8 +2110,6 @@ int err; int offset, total; - self = sk->protinfo.irda; - IRDA_DEBUG(2, __FUNCTION__ "(%p)\n", self); if (level != SOL_IRLMP) @@ -2408,10 +2397,9 @@ return 0; } -static struct net_proto_family irda_family_ops = -{ - PF_IRDA, - irda_create +static struct net_proto_family irda_family_ops = { + family: PF_IRDA, + create: irda_create, }; static struct proto_ops SOCKOPS_WRAPPED(irda_stream_ops) = { diff -Nru a/net/khttpd/accept.c b/net/khttpd/accept.c --- a/net/khttpd/accept.c Tue Feb 19 18:08:57 2002 +++ b/net/khttpd/accept.c Tue Feb 19 18:08:57 2002 @@ -42,6 +42,7 @@ int AcceptConnections(const int CPUNR, struct socket *Socket) { + struct tcp_opt *tp; struct http_request *NewRequest; struct socket *NewSock; int count = 0; @@ -57,13 +58,14 @@ if (Socket==NULL) return 0; + tp = tcp_sk(Socket->sk); /* Quick test to see if there are connections on the queue. This is cheaper than accept() itself because this saves us the allocation of a new socket. (Which doesn't seem to be used anyway) */ - if (Socket->sk->tp_pinfo.af_tcp.accept_queue==NULL) + if (tp->accept_queue==NULL) { return 0; } diff -Nru a/net/khttpd/datasending.c b/net/khttpd/datasending.c --- a/net/khttpd/datasending.c Tue Feb 19 18:08:58 2002 +++ b/net/khttpd/datasending.c Tue Feb 19 18:08:58 2002 @@ -171,8 +171,10 @@ if (CurrentRequest->sock->sk->state == TCP_ESTABLISHED || CurrentRequest->sock->sk->state == TCP_CLOSE_WAIT) { - CurrentRequest->sock->sk->tp_pinfo.af_tcp.nonagle = 0; - tcp_push_pending_frames(CurrentRequest->sock->sk,&(CurrentRequest->sock->sk->tp_pinfo.af_tcp)); + struct tcp_opt *tp = + tcp_sk(CurrentRequest->sock->sk); + tp->nonagle = 0; + tcp_push_pending_frames(CurrentRequest->sock->sk, tp); } release_sock(CurrentRequest->sock->sk); diff -Nru a/net/khttpd/main.c b/net/khttpd/main.c --- a/net/khttpd/main.c Tue Feb 19 18:08:57 2002 +++ b/net/khttpd/main.c Tue Feb 19 18:08:57 2002 @@ -115,7 +115,7 @@ spin_lock_irq(¤t->sigmask_lock); tmpsig = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)| sigmask(SIGHUP)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); @@ -203,7 +203,7 @@ spin_lock_irq(¤t->sigmask_lock); tmpsig = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) ); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); diff -Nru a/net/khttpd/prototypes.h b/net/khttpd/prototypes.h --- a/net/khttpd/prototypes.h Tue Feb 19 18:08:57 2002 +++ b/net/khttpd/prototypes.h Tue Feb 19 18:08:57 2002 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "structure.h" diff -Nru a/net/khttpd/waitheaders.c b/net/khttpd/waitheaders.c --- a/net/khttpd/waitheaders.c Tue Feb 19 18:08:59 2002 +++ b/net/khttpd/waitheaders.c Tue Feb 19 18:08:59 2002 @@ -255,7 +255,9 @@ } else /* Normal Case */ { - Request->sock->sk->tp_pinfo.af_tcp.nonagle = 2; /* this is TCP_CORK */ + struct tcp_opt *tp = tcp_sk(Request->sock->sk); + + tp->nonagle = 2; /* this is TCP_CORK */ if (Request->HTTPVER!=9) /* HTTP/0.9 doesn't allow a header */ SendHTTPHeader(Request); } diff -Nru a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c --- a/net/netlink/af_netlink.c Tue Feb 19 18:09:00 2002 +++ b/net/netlink/af_netlink.c Tue Feb 19 18:09:00 2002 @@ -11,6 +11,8 @@ * * Tue Jun 26 14:36:48 MEST 2001 Herbert "herp" Rosmanith * added netlink_proto_exit + * Tue Jan 22 18:32:44 BRST 2002 Arnaldo C. de Melo + * use nlk_sk, as sk->protinfo is on a diet 8) * */ @@ -62,6 +64,8 @@ void (*data_ready)(struct sock *sk, int bytes); }; +#define nlk_sk(__sk) ((struct netlink_opt *)(__sk)->protinfo) + static struct sock *nl_table[MAX_LINKS]; static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait); @@ -87,9 +91,9 @@ } BUG_TRAP(atomic_read(&sk->rmem_alloc)==0); BUG_TRAP(atomic_read(&sk->wmem_alloc)==0); - BUG_TRAP(sk->protinfo.af_netlink->cb==NULL); + BUG_TRAP(!nlk_sk(sk)->cb); - kfree(sk->protinfo.af_netlink); + kfree(nlk_sk(sk)); atomic_dec(&netlink_sock_nr); #ifdef NETLINK_REFCNT_DEBUG @@ -154,7 +158,7 @@ read_lock(&nl_table_lock); for (sk=nl_table[protocol]; sk; sk=sk->next) { - if (sk->protinfo.af_netlink->pid == pid) { + if (nlk_sk(sk)->pid == pid) { sock_hold(sk); read_unlock(&nl_table_lock); return sk; @@ -174,13 +178,13 @@ netlink_table_grab(); for (osk=nl_table[sk->protocol]; osk; osk=osk->next) { - if (osk->protinfo.af_netlink->pid == pid) + if (nlk_sk(osk)->pid == pid) break; } if (osk == NULL) { err = -EBUSY; - if (sk->protinfo.af_netlink->pid == 0) { - sk->protinfo.af_netlink->pid = pid; + if (nlk_sk(sk)->pid == 0) { + nlk_sk(sk)->pid = pid; sk->next = nl_table[sk->protocol]; nl_table[sk->protocol] = sk; sock_hold(sk); @@ -209,6 +213,7 @@ static int netlink_create(struct socket *sock, int protocol) { struct sock *sk; + struct netlink_opt *nlk; sock->state = SS_UNCONNECTED; @@ -220,21 +225,21 @@ sock->ops = &netlink_ops; - sk = sk_alloc(PF_NETLINK, GFP_KERNEL, 1); + sk = sk_alloc(PF_NETLINK, GFP_KERNEL, 1, NULL); if (!sk) return -ENOMEM; sock_init_data(sock,sk); - sk->protinfo.af_netlink = kmalloc(sizeof(struct netlink_opt), GFP_KERNEL); - if (sk->protinfo.af_netlink == NULL) { + nlk = nlk_sk(sk) = kmalloc(sizeof(*nlk), GFP_KERNEL); + if (!nlk) { sk_free(sk); return -ENOMEM; } - memset(sk->protinfo.af_netlink, 0, sizeof(struct netlink_opt)); + memset(nlk, 0, sizeof(*nlk)); - spin_lock_init(&sk->protinfo.af_netlink->cb_lock); - init_waitqueue_head(&sk->protinfo.af_netlink->wait); + spin_lock_init(&nlk->cb_lock); + init_waitqueue_head(&nlk->wait); sk->destruct = netlink_sock_destruct; atomic_inc(&netlink_sock_nr); @@ -245,27 +250,29 @@ static int netlink_release(struct socket *sock) { struct sock *sk = sock->sk; + struct netlink_opt *nlk; if (!sk) return 0; netlink_remove(sk); + nlk = nlk_sk(sk); - spin_lock(&sk->protinfo.af_netlink->cb_lock); - if (sk->protinfo.af_netlink->cb) { - sk->protinfo.af_netlink->cb->done(sk->protinfo.af_netlink->cb); - netlink_destroy_callback(sk->protinfo.af_netlink->cb); - sk->protinfo.af_netlink->cb = NULL; + spin_lock(&nlk->cb_lock); + if (nlk->cb) { + nlk->cb->done(nlk->cb); + netlink_destroy_callback(nlk->cb); + nlk->cb = NULL; __sock_put(sk); } - spin_unlock(&sk->protinfo.af_netlink->cb_lock); + spin_unlock(&nlk->cb_lock); /* OK. Socket is unlinked, and, therefore, no new packets will arrive */ sock_orphan(sk); sock->sk = NULL; - wake_up_interruptible_all(&sk->protinfo.af_netlink->wait); + wake_up_interruptible_all(&nlk->wait); skb_queue_purge(&sk->write_queue); @@ -283,7 +290,7 @@ retry: netlink_table_grab(); for (osk=nl_table[sk->protocol]; osk; osk=osk->next) { - if (osk->protinfo.af_netlink->pid == pid) { + if (nlk_sk(osk)->pid == pid) { /* Bind collision, search negative pid values. */ if (pid > 0) pid = -4096; @@ -297,15 +304,16 @@ err = netlink_insert(sk, pid); if (err == -EADDRINUSE) goto retry; - sk->protinfo.af_netlink->groups = 0; + nlk_sk(sk)->groups = 0; return 0; } static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sock *sk = sock->sk; + struct netlink_opt *nlk = nlk_sk(sk); + struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; int err; - struct sockaddr_nl *nladdr=(struct sockaddr_nl *)addr; if (nladdr->nl_family != AF_NETLINK) return -EINVAL; @@ -314,23 +322,23 @@ if (nladdr->nl_groups && !capable(CAP_NET_ADMIN)) return -EPERM; - if (sk->protinfo.af_netlink->pid) { - if (nladdr->nl_pid != sk->protinfo.af_netlink->pid) + if (nlk->pid) { + if (nladdr->nl_pid != nlk->pid) return -EINVAL; - sk->protinfo.af_netlink->groups = nladdr->nl_groups; + nlk->groups = nladdr->nl_groups; return 0; } if (nladdr->nl_pid == 0) { err = netlink_autobind(sock); if (err == 0) - sk->protinfo.af_netlink->groups = nladdr->nl_groups; + nlk->groups = nladdr->nl_groups; return err; } err = netlink_insert(sk, nladdr->nl_pid); if (err == 0) - sk->protinfo.af_netlink->groups = nladdr->nl_groups; + nlk->groups = nladdr->nl_groups; return err; } @@ -339,11 +347,12 @@ { int err = 0; struct sock *sk = sock->sk; + struct netlink_opt *nlk = nlk_sk(sk); struct sockaddr_nl *nladdr=(struct sockaddr_nl*)addr; if (addr->sa_family == AF_UNSPEC) { - sk->protinfo.af_netlink->dst_pid = 0; - sk->protinfo.af_netlink->dst_groups = 0; + nlk->dst_pid = 0; + nlk->dst_groups = 0; return 0; } if (addr->sa_family != AF_NETLINK) @@ -353,12 +362,12 @@ if (nladdr->nl_groups && !capable(CAP_NET_ADMIN)) return -EPERM; - if (!sk->protinfo.af_netlink->pid) + if (!nlk->pid) err = netlink_autobind(sock); if (err == 0) { - sk->protinfo.af_netlink->dst_pid = nladdr->nl_pid; - sk->protinfo.af_netlink->dst_groups = nladdr->nl_groups; + nlk->dst_pid = nladdr->nl_pid; + nlk->dst_groups = nladdr->nl_groups; } return 0; @@ -367,24 +376,25 @@ static int netlink_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer) { struct sock *sk = sock->sk; + struct netlink_opt *nlk = nlk_sk(sk); struct sockaddr_nl *nladdr=(struct sockaddr_nl *)addr; nladdr->nl_family = AF_NETLINK; *addr_len = sizeof(*nladdr); if (peer) { - nladdr->nl_pid = sk->protinfo.af_netlink->dst_pid; - nladdr->nl_groups = sk->protinfo.af_netlink->dst_groups; + nladdr->nl_pid = nlk->dst_pid; + nladdr->nl_groups = nlk->dst_groups; } else { - nladdr->nl_pid = sk->protinfo.af_netlink->pid; - nladdr->nl_groups = sk->protinfo.af_netlink->groups; + nladdr->nl_pid = nlk->pid; + nladdr->nl_groups = nlk->groups; } return 0; } static void netlink_overrun(struct sock *sk) { - if (!test_and_set_bit(0, &sk->protinfo.af_netlink->state)) { + if (!test_and_set_bit(0, &nlk_sk(sk)->state)) { sk->err = ENOBUFS; sk->error_report(sk); } @@ -393,6 +403,7 @@ int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock) { struct sock *sk; + struct netlink_opt *nlk; int len = skb->len; int protocol = ssk->protocol; long timeo; @@ -404,20 +415,21 @@ sk = netlink_lookup(protocol, pid); if (sk == NULL) goto no_dst; + nlk = nlk_sk(sk); #ifdef NL_EMULATE_DEV - if (sk->protinfo.af_netlink->handler) { + if (nlk->handler) { skb_orphan(skb); - len = sk->protinfo.af_netlink->handler(protocol, skb); + len = nlk->handler(protocol, skb); sock_put(sk); return len; } #endif if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf || - test_bit(0, &sk->protinfo.af_netlink->state)) { + test_bit(0, &nlk->state)) { if (!timeo) { - if (ssk->protinfo.af_netlink->pid == 0) + if (!nlk->pid) netlink_overrun(sk); sock_put(sk); kfree_skb(skb); @@ -425,15 +437,15 @@ } __set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&sk->protinfo.af_netlink->wait, &wait); + add_wait_queue(&nlk->wait, &wait); if ((atomic_read(&sk->rmem_alloc) > sk->rcvbuf || - test_bit(0, &sk->protinfo.af_netlink->state)) && + test_bit(0, &nlk->state)) && !sk->dead) timeo = schedule_timeout(timeo); __set_current_state(TASK_RUNNING); - remove_wait_queue(&sk->protinfo.af_netlink->wait, &wait); + remove_wait_queue(&nlk->wait, &wait); sock_put(sk); if (signal_pending(current)) { @@ -457,15 +469,16 @@ static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) { + struct netlink_opt *nlk = nlk_sk(sk); #ifdef NL_EMULATE_DEV - if (sk->protinfo.af_netlink->handler) { + if (nlk->handler) { skb_orphan(skb); - sk->protinfo.af_netlink->handler(sk->protocol, skb); + nlk->handler(sk->protocol, skb); return 0; } else #endif if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf && - !test_bit(0, &sk->protinfo.af_netlink->state)) { + !test_bit(0, &nlk->state)) { skb_orphan(skb); skb_set_owner_r(skb, sk); skb_queue_tail(&sk->receive_queue, skb); @@ -488,11 +501,12 @@ netlink_lock_table(); for (sk = nl_table[protocol]; sk; sk = sk->next) { + struct netlink_opt *nlk = nlk_sk(sk); + if (ssk == sk) continue; - if (sk->protinfo.af_netlink->pid == pid || - !(sk->protinfo.af_netlink->groups&group)) + if (nlk->pid == pid || !(nlk->groups & group)) continue; if (failure) { @@ -534,11 +548,11 @@ read_lock(&nl_table_lock); for (sk = nl_table[protocol]; sk; sk = sk->next) { + struct netlink_opt *nlk = nlk_sk(sk); if (ssk == sk) continue; - if (sk->protinfo.af_netlink->pid == pid || - !(sk->protinfo.af_netlink->groups&group)) + if (nlk->pid == pid || !(nlk->groups & group)) continue; sk->err = code; @@ -551,6 +565,7 @@ struct scm_cookie *scm) { struct sock *sk = sock->sk; + struct netlink_opt *nlk = nlk_sk(sk); struct sockaddr_nl *addr=msg->msg_name; u32 dst_pid; u32 dst_groups; @@ -568,11 +583,11 @@ if (dst_groups && !capable(CAP_NET_ADMIN)) return -EPERM; } else { - dst_pid = sk->protinfo.af_netlink->dst_pid; - dst_groups = sk->protinfo.af_netlink->dst_groups; + dst_pid = nlk->dst_pid; + dst_groups = nlk->dst_groups; } - if (!sk->protinfo.af_netlink->pid) { + if (!nlk->pid) { err = netlink_autobind(sock); if (err) goto out; @@ -586,8 +601,8 @@ if (skb==NULL) goto out; - NETLINK_CB(skb).pid = sk->protinfo.af_netlink->pid; - NETLINK_CB(skb).groups = sk->protinfo.af_netlink->groups; + NETLINK_CB(skb).pid = nlk->pid; + NETLINK_CB(skb).groups = nlk->groups; NETLINK_CB(skb).dst_pid = dst_pid; NETLINK_CB(skb).dst_groups = dst_groups; memcpy(NETLINK_CREDS(skb), &scm->creds, sizeof(struct ucred)); @@ -619,6 +634,7 @@ int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; + struct netlink_opt *nlk = nlk_sk(sk); int noblock = flags&MSG_DONTWAIT; int copied; struct sk_buff *skb; @@ -655,30 +671,31 @@ scm->creds = *NETLINK_CREDS(skb); skb_free_datagram(sk, skb); - if (sk->protinfo.af_netlink->cb - && atomic_read(&sk->rmem_alloc) <= sk->rcvbuf/2) + if (nlk->cb && atomic_read(&sk->rmem_alloc) <= sk->rcvbuf / 2) netlink_dump(sk); out: if (skb_queue_len(&sk->receive_queue) <= sk->rcvbuf/2) { if (skb_queue_len(&sk->receive_queue) == 0) - clear_bit(0, &sk->protinfo.af_netlink->state); - if (!test_bit(0, &sk->protinfo.af_netlink->state)) - wake_up_interruptible(&sk->protinfo.af_netlink->wait); + clear_bit(0, &nlk->state); + if (!test_bit(0, &nlk->state)) + wake_up_interruptible(&nlk->wait); } return err ? : copied; } void netlink_data_ready(struct sock *sk, int len) { - if (sk->protinfo.af_netlink->data_ready) - sk->protinfo.af_netlink->data_ready(sk, len); + struct netlink_opt *nlk = nlk_sk(sk); + + if (nlk->data_ready) + nlk->data_ready(sk, len); if (skb_queue_len(&sk->receive_queue) <= sk->rcvbuf/2) { if (skb_queue_len(&sk->receive_queue) == 0) - clear_bit(0, &sk->protinfo.af_netlink->state); - if (!test_bit(0, &sk->protinfo.af_netlink->state)) - wake_up_interruptible(&sk->protinfo.af_netlink->wait); + clear_bit(0, &nlk->state); + if (!test_bit(0, &nlk->state)) + wake_up_interruptible(&nlk->wait); } } @@ -709,7 +726,7 @@ sk = sock->sk; sk->data_ready = netlink_data_ready; if (input) - sk->protinfo.af_netlink->data_ready = input; + nlk_sk(sk)->data_ready = input; netlink_insert(sk, 0); return sk; @@ -729,6 +746,7 @@ static int netlink_dump(struct sock *sk) { + struct netlink_opt *nlk = nlk_sk(sk); struct netlink_callback *cb; struct sk_buff *skb; struct nlmsghdr *nlh; @@ -738,11 +756,11 @@ if (!skb) return -ENOBUFS; - spin_lock(&sk->protinfo.af_netlink->cb_lock); + spin_lock(&nlk->cb_lock); - cb = sk->protinfo.af_netlink->cb; + cb = nlk->cb; if (cb == NULL) { - spin_unlock(&sk->protinfo.af_netlink->cb_lock); + spin_unlock(&nlk->cb_lock); kfree_skb(skb); return -EINVAL; } @@ -750,7 +768,7 @@ len = cb->dump(skb, cb); if (len > 0) { - spin_unlock(&sk->protinfo.af_netlink->cb_lock); + spin_unlock(&nlk->cb_lock); skb_queue_tail(&sk->receive_queue, skb); sk->data_ready(sk, len); return 0; @@ -763,8 +781,8 @@ sk->data_ready(sk, skb->len); cb->done(cb); - sk->protinfo.af_netlink->cb = NULL; - spin_unlock(&sk->protinfo.af_netlink->cb_lock); + nlk->cb = NULL; + spin_unlock(&nlk->cb_lock); netlink_destroy_callback(cb); sock_put(sk); @@ -778,6 +796,7 @@ { struct netlink_callback *cb; struct sock *sk; + struct netlink_opt *nlk; cb = kmalloc(sizeof(*cb), GFP_KERNEL); if (cb == NULL) @@ -795,16 +814,17 @@ netlink_destroy_callback(cb); return -ECONNREFUSED; } + nlk = nlk_sk(sk); /* A dump is in progress... */ - spin_lock(&sk->protinfo.af_netlink->cb_lock); - if (sk->protinfo.af_netlink->cb) { - spin_unlock(&sk->protinfo.af_netlink->cb_lock); + spin_lock(&nlk->cb_lock); + if (nlk->cb) { + spin_unlock(&nlk->cb_lock); netlink_destroy_callback(cb); sock_put(sk); return -EBUSY; } - sk->protinfo.af_netlink->cb = cb; - spin_unlock(&sk->protinfo.af_netlink->cb_lock); + nlk->cb = cb; + spin_unlock(&nlk->cb_lock); netlink_dump(sk); return 0; @@ -848,7 +868,7 @@ struct sock *sk = netlink_kernel_create(unit, NULL); if (sk == NULL) return -ENOBUFS; - sk->protinfo.af_netlink->handler = function; + nlk_sk(sk)->handler = function; write_lock_bh(&nl_emu_lock); netlink_kernel[unit] = sk->socket; write_unlock_bh(&nl_emu_lock); @@ -907,14 +927,16 @@ for (i=0; inext) { + struct netlink_opt *nlk = nlk_sk(s); + len+=sprintf(buffer+len,"%p %-3d %-6d %08x %-8d %-8d %p %d", s, s->protocol, - s->protinfo.af_netlink->pid, - s->protinfo.af_netlink->groups, + nlk->pid, + nlk->groups, atomic_read(&s->rmem_alloc), atomic_read(&s->wmem_alloc), - s->protinfo.af_netlink->cb, + nlk->cb, atomic_read(&s->refcnt) ); @@ -967,8 +989,8 @@ }; struct net_proto_family netlink_family_ops = { - PF_NETLINK, - netlink_create + family: PF_NETLINK, + create: netlink_create, }; static int __init netlink_proto_init(void) diff -Nru a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c --- a/net/netrom/af_netrom.c Tue Feb 19 18:09:00 2002 +++ b/net/netrom/af_netrom.c Tue Feb 19 18:09:00 2002 @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -97,22 +98,23 @@ struct sock *sk; nr_cb *nr; - if ((sk = sk_alloc(PF_NETROM, GFP_ATOMIC, 1)) == NULL) - return NULL; - - if ((nr = kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) { - sk_free(sk); - return NULL; - } - MOD_INC_USE_COUNT; + if ((sk = sk_alloc(PF_NETROM, GFP_ATOMIC, 1, NULL)) == NULL) + goto decmod; + + nr = nr_sk(sk) = kmalloc(sizeof(*nr), GFP_ATOMIC); + if (!nr) + goto frees; memset(nr, 0x00, sizeof(*nr)); - sk->protinfo.nr = nr; nr->sk = sk; - return sk; +out: return sk; +frees: sk_free(sk); + sk = NULL; +decmod: MOD_DEC_USE_COUNT; + goto out; } /* @@ -152,7 +154,7 @@ struct sock *s; for (s = nr_list; s != NULL; s = s->next) { - if (s->protinfo.nr->device == dev) + if (nr_sk(s)->device == dev) nr_disconnect(s, ENETUNREACH); } } @@ -201,7 +203,8 @@ cli(); for (s = nr_list; s != NULL; s = s->next) { - if (ax25cmp(&s->protinfo.nr->source_addr, addr) == 0 && s->state == TCP_LISTEN) { + if (!ax25cmp(&nr_sk(s)->source_addr, addr) && + s->state == TCP_LISTEN) { restore_flags(flags); return s; } @@ -223,7 +226,9 @@ cli(); for (s = nr_list; s != NULL; s = s->next) { - if (s->protinfo.nr->my_index == index && s->protinfo.nr->my_id == id) { + nr_cb *nr = nr_sk(s); + + if (nr->my_index == index && nr->my_id == id) { restore_flags(flags); return s; } @@ -246,7 +251,10 @@ cli(); for (s = nr_list; s != NULL; s = s->next) { - if (s->protinfo.nr->your_index == index && s->protinfo.nr->your_id == id && ax25cmp(&s->protinfo.nr->dest_addr, dest) == 0) { + nr_cb *nr = nr_sk(s); + + if (nr->your_index == index && nr->your_id == id && + !ax25cmp(&nr->dest_addr, dest)) { restore_flags(flags); return s; } @@ -318,7 +326,7 @@ if (skb->sk != sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ nr_start_heartbeat(skb->sk); - skb->sk->protinfo.nr->state = NR_STATE_0; + nr_sk(skb->sk)->state = NR_STATE_0; } kfree_skb(skb); @@ -347,6 +355,7 @@ char *optval, int optlen) { struct sock *sk = sock->sk; + nr_cb *nr = nr_sk(sk); int opt; if (level != SOL_NETROM) @@ -362,31 +371,31 @@ case NETROM_T1: if (opt < 1) return -EINVAL; - sk->protinfo.nr->t1 = opt * HZ; + nr->t1 = opt * HZ; return 0; case NETROM_T2: if (opt < 1) return -EINVAL; - sk->protinfo.nr->t2 = opt * HZ; + nr->t2 = opt * HZ; return 0; case NETROM_N2: if (opt < 1 || opt > 31) return -EINVAL; - sk->protinfo.nr->n2 = opt; + nr->n2 = opt; return 0; case NETROM_T4: if (opt < 1) return -EINVAL; - sk->protinfo.nr->t4 = opt * HZ; + nr->t4 = opt * HZ; return 0; case NETROM_IDLE: if (opt < 0) return -EINVAL; - sk->protinfo.nr->idle = opt * 60 * HZ; + nr->idle = opt * 60 * HZ; return 0; default: @@ -398,6 +407,7 @@ char *optval, int *optlen) { struct sock *sk = sock->sk; + nr_cb *nr = nr_sk(sk); int val = 0; int len; @@ -412,23 +422,23 @@ switch (optname) { case NETROM_T1: - val = sk->protinfo.nr->t1 / HZ; + val = nr->t1 / HZ; break; case NETROM_T2: - val = sk->protinfo.nr->t2 / HZ; + val = nr->t2 / HZ; break; case NETROM_N2: - val = sk->protinfo.nr->n2; + val = nr->n2; break; case NETROM_T4: - val = sk->protinfo.nr->t4 / HZ; + val = nr->t4 / HZ; break; case NETROM_IDLE: - val = sk->protinfo.nr->idle / (60 * HZ); + val = nr->idle / (60 * HZ); break; default: @@ -448,7 +458,7 @@ struct sock *sk = sock->sk; if (sk->state != TCP_LISTEN) { - memset(&sk->protinfo.nr->user_addr, '\0', AX25_ADDR_LEN); + memset(&nr_sk(sk)->user_addr, 0, AX25_ADDR_LEN); sk->max_ack_backlog = backlog; sk->state = TCP_LISTEN; return 0; @@ -468,7 +478,7 @@ if ((sk = nr_alloc_sock()) == NULL) return -ENOMEM; - nr = sk->protinfo.nr; + nr = nr_sk(sk); sock_init_data(sock, sk); @@ -500,7 +510,7 @@ static struct sock *nr_make_new(struct sock *osk) { struct sock *sk; - nr_cb *nr; + nr_cb *nr, *onr; if (osk->type != SOCK_SEQPACKET) return NULL; @@ -508,7 +518,7 @@ if ((sk = nr_alloc_sock()) == NULL) return NULL; - nr = sk->protinfo.nr; + nr = nr_sk(sk); sock_init_data(NULL, sk); @@ -532,15 +542,17 @@ init_timer(&nr->t4timer); init_timer(&nr->idletimer); - nr->t1 = osk->protinfo.nr->t1; - nr->t2 = osk->protinfo.nr->t2; - nr->n2 = osk->protinfo.nr->n2; - nr->t4 = osk->protinfo.nr->t4; - nr->idle = osk->protinfo.nr->idle; - nr->window = osk->protinfo.nr->window; + onr = nr_sk(osk); + + nr->t1 = onr->t1; + nr->t2 = onr->t2; + nr->n2 = onr->n2; + nr->t4 = onr->t4; + nr->idle = onr->idle; + nr->window = onr->window; - nr->device = osk->protinfo.nr->device; - nr->bpqext = osk->protinfo.nr->bpqext; + nr->device = onr->device; + nr->bpqext = onr->bpqext; return sk; } @@ -548,10 +560,13 @@ static int nr_release(struct socket *sock) { struct sock *sk = sock->sk; + nr_cb *nr; if (sk == NULL) return 0; - switch (sk->protinfo.nr->state) { + nr = nr_sk(sk); + + switch (nr->state) { case NR_STATE_0: case NR_STATE_1: @@ -562,19 +577,19 @@ case NR_STATE_3: nr_clear_queues(sk); - sk->protinfo.nr->n2count = 0; + nr->n2count = 0; nr_write_internal(sk, NR_DISCREQ); nr_start_t1timer(sk); nr_stop_t2timer(sk); nr_stop_t4timer(sk); nr_stop_idletimer(sk); - sk->protinfo.nr->state = NR_STATE_2; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; + nr->state = NR_STATE_2; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - sk->dead = 1; - sk->destroy = 1; - sk->socket = NULL; + sk->dead = 1; + sk->destroy = 1; + sk->socket = NULL; break; default: @@ -590,6 +605,7 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; + nr_cb *nr = nr_sk(sk); struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; struct net_device *dev; ax25_address *user, *source; @@ -618,8 +634,8 @@ if (addr->fsa_ax25.sax25_ndigis == 1) { if (!capable(CAP_NET_BIND_SERVICE)) return -EACCES; - sk->protinfo.nr->user_addr = addr->fsa_digipeater[0]; - sk->protinfo.nr->source_addr = addr->fsa_ax25.sax25_call; + nr->user_addr = addr->fsa_digipeater[0]; + nr->source_addr = addr->fsa_ax25.sax25_call; } else { source = &addr->fsa_ax25.sax25_call; @@ -629,11 +645,11 @@ user = source; } - sk->protinfo.nr->user_addr = *user; - sk->protinfo.nr->source_addr = *source; + nr->user_addr = *user; + nr->source_addr = *source; } - sk->protinfo.nr->device = dev; + nr->device = dev; nr_insert_socket(sk); sk->zapped = 0; @@ -645,6 +661,7 @@ int addr_len, int flags) { struct sock *sk = sock->sk; + nr_cb *nr = nr_sk(sk); struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr; ax25_address *user, *source = NULL; struct net_device *dev; @@ -685,19 +702,19 @@ user = source; } - sk->protinfo.nr->user_addr = *user; - sk->protinfo.nr->source_addr = *source; - sk->protinfo.nr->device = dev; + nr->user_addr = *user; + nr->source_addr = *source; + nr->device = dev; nr_insert_socket(sk); /* Finish the bind */ } - sk->protinfo.nr->dest_addr = addr->sax25_call; + nr->dest_addr = addr->sax25_call; circuit = nr_find_next_circuit(); - sk->protinfo.nr->my_index = circuit / 256; - sk->protinfo.nr->my_id = circuit % 256; + nr->my_index = circuit / 256; + nr->my_id = circuit % 256; circuit++; @@ -707,7 +724,7 @@ nr_establish_data_link(sk); - sk->protinfo.nr->state = NR_STATE_1; + nr->state = NR_STATE_1; nr_start_heartbeat(sk); @@ -794,19 +811,20 @@ { struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr; struct sock *sk = sock->sk; + nr_cb *nr = nr_sk(sk); if (peer != 0) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; sax->fsa_ax25.sax25_family = AF_NETROM; sax->fsa_ax25.sax25_ndigis = 1; - sax->fsa_ax25.sax25_call = sk->protinfo.nr->user_addr; - sax->fsa_digipeater[0] = sk->protinfo.nr->dest_addr; + sax->fsa_ax25.sax25_call = nr->user_addr; + sax->fsa_digipeater[0] = nr->dest_addr; *uaddr_len = sizeof(struct full_sockaddr_ax25); } else { sax->fsa_ax25.sax25_family = AF_NETROM; sax->fsa_ax25.sax25_ndigis = 0; - sax->fsa_ax25.sax25_call = sk->protinfo.nr->source_addr; + sax->fsa_ax25.sax25_call = nr->source_addr; *uaddr_len = sizeof(struct sockaddr_ax25); } @@ -817,6 +835,7 @@ { struct sock *sk; struct sock *make; + nr_cb *nr_make; ax25_address *src, *dest, *user; unsigned short circuit_index, circuit_id; unsigned short peer_circuit_index, peer_circuit_id; @@ -874,9 +893,9 @@ skb->h.raw = skb->data; if (frametype == NR_CONNACK && skb->len == 22) - sk->protinfo.nr->bpqext = 1; + nr_sk(sk)->bpqext = 1; else - sk->protinfo.nr->bpqext = 0; + nr_sk(sk)->bpqext = 0; return nr_process_rx_frame(sk, skb); } @@ -916,42 +935,43 @@ make->state = TCP_ESTABLISHED; /* Fill in his circuit details */ - make->protinfo.nr->source_addr = *dest; - make->protinfo.nr->dest_addr = *src; - make->protinfo.nr->user_addr = *user; + nr_make = nr_sk(make); + nr_make->source_addr = *dest; + nr_make->dest_addr = *src; + nr_make->user_addr = *user; - make->protinfo.nr->your_index = circuit_index; - make->protinfo.nr->your_id = circuit_id; + nr_make->your_index = circuit_index; + nr_make->your_id = circuit_id; circuit = nr_find_next_circuit(); - make->protinfo.nr->my_index = circuit / 256; - make->protinfo.nr->my_id = circuit % 256; + nr_make->my_index = circuit / 256; + nr_make->my_id = circuit % 256; circuit++; /* Window negotiation */ - if (window < make->protinfo.nr->window) - make->protinfo.nr->window = window; + if (window < nr_make->window) + nr_make->window = window; /* L4 timeout negotiation */ if (skb->len == 37) { timeout = skb->data[36] * 256 + skb->data[35]; - if (timeout * HZ < make->protinfo.nr->t1) - make->protinfo.nr->t1 = timeout * HZ; - make->protinfo.nr->bpqext = 1; + if (timeout * HZ < nr_make->t1) + nr_make->t1 = timeout * HZ; + nr_make->bpqext = 1; } else { - make->protinfo.nr->bpqext = 0; + nr_make->bpqext = 0; } nr_write_internal(make, NR_CONNACK); - make->protinfo.nr->condition = 0x00; - make->protinfo.nr->vs = 0; - make->protinfo.nr->va = 0; - make->protinfo.nr->vr = 0; - make->protinfo.nr->vl = 0; - make->protinfo.nr->state = NR_STATE_3; + nr_make->condition = 0x00; + nr_make->vs = 0; + nr_make->va = 0; + nr_make->vr = 0; + nr_make->vl = 0; + nr_make->state = NR_STATE_3; sk->ack_backlog++; make->pair = sk; @@ -971,6 +991,7 @@ static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; + nr_cb *nr = nr_sk(sk); struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name; int err; struct sockaddr_ax25 sax; @@ -989,14 +1010,14 @@ return -EPIPE; } - if (sk->protinfo.nr->device == NULL) + if (nr->device == NULL) return -ENETUNREACH; if (usax) { if (msg->msg_namelen < sizeof(sax)) return -EINVAL; sax = *usax; - if (ax25cmp(&sk->protinfo.nr->dest_addr, &sax.sax25_call) != 0) + if (ax25cmp(&nr->dest_addr, &sax.sax25_call) != 0) return -EISCONN; if (sax.sax25_family != AF_NETROM) return -EINVAL; @@ -1004,7 +1025,7 @@ if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; sax.sax25_family = AF_NETROM; - sax.sax25_call = sk->protinfo.nr->dest_addr; + sax.sax25_call = nr->dest_addr; } SOCK_DEBUG(sk, "NET/ROM: sendto: Addresses built.\n"); @@ -1027,8 +1048,8 @@ /* Build a NET/ROM Transport header */ - *asmptr++ = sk->protinfo.nr->your_index; - *asmptr++ = sk->protinfo.nr->your_id; + *asmptr++ = nr->your_index; + *asmptr++ = nr->your_id; *asmptr++ = 0; /* To be filled in later */ *asmptr++ = 0; /* Ditto */ *asmptr++ = NR_INFO; @@ -1171,37 +1192,39 @@ len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 t4 idle n2 wnd Snd-Q Rcv-Q inode\n"); for (s = nr_list; s != NULL; s = s->next) { - if ((dev = s->protinfo.nr->device) == NULL) + nr_cb *nr = nr_sk(s); + + if ((dev = nr->device) == NULL) devname = "???"; else devname = dev->name; len += sprintf(buffer + len, "%-9s ", - ax2asc(&s->protinfo.nr->user_addr)); + ax2asc(&nr->user_addr)); len += sprintf(buffer + len, "%-9s ", - ax2asc(&s->protinfo.nr->dest_addr)); + ax2asc(&nr->dest_addr)); len += sprintf(buffer + len, "%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3lu/%03lu %2lu/%02lu %3lu/%03lu %3lu/%03lu %2d/%02d %3d %5d %5d %ld\n", - ax2asc(&s->protinfo.nr->source_addr), + ax2asc(&nr->source_addr), devname, - s->protinfo.nr->my_index, - s->protinfo.nr->my_id, - s->protinfo.nr->your_index, - s->protinfo.nr->your_id, - s->protinfo.nr->state, - s->protinfo.nr->vs, - s->protinfo.nr->vr, - s->protinfo.nr->va, - ax25_display_timer(&s->protinfo.nr->t1timer) / HZ, - s->protinfo.nr->t1 / HZ, - ax25_display_timer(&s->protinfo.nr->t2timer) / HZ, - s->protinfo.nr->t2 / HZ, - ax25_display_timer(&s->protinfo.nr->t4timer) / HZ, - s->protinfo.nr->t4 / HZ, - ax25_display_timer(&s->protinfo.nr->idletimer) / (60 * HZ), - s->protinfo.nr->idle / (60 * HZ), - s->protinfo.nr->n2count, - s->protinfo.nr->n2, - s->protinfo.nr->window, + nr->my_index, + nr->my_id, + nr->your_index, + nr->your_id, + nr->state, + nr->vs, + nr->vr, + nr->va, + ax25_display_timer(&nr->t1timer) / HZ, + nr->t1 / HZ, + ax25_display_timer(&nr->t2timer) / HZ, + nr->t2 / HZ, + ax25_display_timer(&nr->t4timer) / HZ, + nr->t4 / HZ, + ax25_display_timer(&nr->idletimer) / (60 * HZ), + nr->idle / (60 * HZ), + nr->n2count, + nr->n2, + nr->window, atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc), s->socket != NULL ? SOCK_INODE(s->socket)->i_ino : 0L); diff -Nru a/net/netrom/nr_in.c b/net/netrom/nr_in.c --- a/net/netrom/nr_in.c Tue Feb 19 18:08:59 2002 +++ b/net/netrom/nr_in.c Tue Feb 19 18:08:59 2002 @@ -39,6 +39,7 @@ #include #include #include +#include #include /* For ip_rcv */ #include #include @@ -50,32 +51,33 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) { struct sk_buff *skbo, *skbn = skb; + nr_cb *nr = nr_sk(sk); skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); nr_start_idletimer(sk); if (more) { - sk->protinfo.nr->fraglen += skb->len; - skb_queue_tail(&sk->protinfo.nr->frag_queue, skb); + nr->fraglen += skb->len; + skb_queue_tail(&nr->frag_queue, skb); return 0; } - if (!more && sk->protinfo.nr->fraglen > 0) { /* End of fragment */ - sk->protinfo.nr->fraglen += skb->len; - skb_queue_tail(&sk->protinfo.nr->frag_queue, skb); + if (!more && nr->fraglen > 0) { /* End of fragment */ + nr->fraglen += skb->len; + skb_queue_tail(&nr->frag_queue, skb); - if ((skbn = alloc_skb(sk->protinfo.nr->fraglen, GFP_ATOMIC)) == NULL) + if ((skbn = alloc_skb(nr->fraglen, GFP_ATOMIC)) == NULL) return 1; skbn->h.raw = skbn->data; - while ((skbo = skb_dequeue(&sk->protinfo.nr->frag_queue)) != NULL) { + while ((skbo = skb_dequeue(&nr->frag_queue)) != NULL) { memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); kfree_skb(skbo); } - sk->protinfo.nr->fraglen = 0; + nr->fraglen = 0; } return sock_queue_rcv_skb(sk, skbn); @@ -90,22 +92,25 @@ { switch (frametype) { - case NR_CONNACK: + case NR_CONNACK: { + nr_cb *nr = nr_sk(sk); + nr_stop_t1timer(sk); nr_start_idletimer(sk); - sk->protinfo.nr->your_index = skb->data[17]; - sk->protinfo.nr->your_id = skb->data[18]; - sk->protinfo.nr->vs = 0; - sk->protinfo.nr->va = 0; - sk->protinfo.nr->vr = 0; - sk->protinfo.nr->vl = 0; - sk->protinfo.nr->state = NR_STATE_3; - sk->protinfo.nr->n2count = 0; - sk->protinfo.nr->window = skb->data[20]; - sk->state = TCP_ESTABLISHED; + nr->your_index = skb->data[17]; + nr->your_id = skb->data[18]; + nr->vs = 0; + nr->va = 0; + nr->vr = 0; + nr->vl = 0; + nr->state = NR_STATE_3; + nr->n2count = 0; + nr->window = skb->data[20]; + sk->state = TCP_ESTABLISHED; if (!sk->dead) sk->state_change(sk); break; + } case NR_CONNACK | NR_CHOKE_FLAG: nr_disconnect(sk, ECONNREFUSED); @@ -152,6 +157,7 @@ */ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + nr_cb *nrom = nr_sk(sk); struct sk_buff_head temp_queue; struct sk_buff *skbn; unsigned short save_vr; @@ -182,10 +188,10 @@ case NR_INFOACK | NR_NAK_FLAG: case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG: if (frametype & NR_CHOKE_FLAG) { - sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY; + nrom->condition |= NR_COND_PEER_RX_BUSY; nr_start_t4timer(sk); } else { - sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; + nrom->condition &= ~NR_COND_PEER_RX_BUSY; nr_stop_t4timer(sk); } if (!nr_validate_nr(sk, nr)) { @@ -195,7 +201,7 @@ nr_frames_acked(sk, nr); nr_send_nak_frame(sk); } else { - if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) { + if (nrom->condition & NR_COND_PEER_RX_BUSY) { nr_frames_acked(sk, nr); } else { nr_check_iframes_acked(sk, nr); @@ -212,10 +218,10 @@ case NR_INFO | NR_NAK_FLAG | NR_MORE_FLAG: case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG: if (frametype & NR_CHOKE_FLAG) { - sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY; + nrom->condition |= NR_COND_PEER_RX_BUSY; nr_start_t4timer(sk); } else { - sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; + nrom->condition &= ~NR_COND_PEER_RX_BUSY; nr_stop_t4timer(sk); } if (nr_validate_nr(sk, nr)) { @@ -223,7 +229,7 @@ nr_frames_acked(sk, nr); nr_send_nak_frame(sk); } else { - if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) { + if (nrom->condition & NR_COND_PEER_RX_BUSY) { nr_frames_acked(sk, nr); } else { nr_check_iframes_acked(sk, nr); @@ -231,19 +237,19 @@ } } queued = 1; - skb_queue_head(&sk->protinfo.nr->reseq_queue, skb); - if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY) + skb_queue_head(&nrom->reseq_queue, skb); + if (nrom->condition & NR_COND_OWN_RX_BUSY) break; skb_queue_head_init(&temp_queue); do { - save_vr = sk->protinfo.nr->vr; - while ((skbn = skb_dequeue(&sk->protinfo.nr->reseq_queue)) != NULL) { + save_vr = nrom->vr; + while ((skbn = skb_dequeue(&nrom->reseq_queue)) != NULL) { ns = skbn->data[17]; - if (ns == sk->protinfo.nr->vr) { + if (ns == nrom->vr) { if (nr_queue_rx_frame(sk, skbn, frametype & NR_MORE_FLAG) == 0) { - sk->protinfo.nr->vr = (sk->protinfo.nr->vr + 1) % NR_MODULUS; + nrom->vr = (nrom->vr + 1) % NR_MODULUS; } else { - sk->protinfo.nr->condition |= NR_COND_OWN_RX_BUSY; + nrom->condition |= NR_COND_OWN_RX_BUSY; skb_queue_tail(&temp_queue, skbn); } } else if (nr_in_rx_window(sk, ns)) { @@ -253,17 +259,17 @@ } } while ((skbn = skb_dequeue(&temp_queue)) != NULL) { - skb_queue_tail(&sk->protinfo.nr->reseq_queue, skbn); + skb_queue_tail(&nrom->reseq_queue, skbn); } - } while (save_vr != sk->protinfo.nr->vr); + } while (save_vr != nrom->vr); /* * Window is full, ack it immediately. */ - if (((sk->protinfo.nr->vl + sk->protinfo.nr->window) % NR_MODULUS) == sk->protinfo.nr->vr) { + if (((nrom->vl + nrom->window) % NR_MODULUS) == nrom->vr) { nr_enquiry_response(sk); } else { - if (!(sk->protinfo.nr->condition & NR_COND_ACK_PENDING)) { - sk->protinfo.nr->condition |= NR_COND_ACK_PENDING; + if (!(nrom->condition & NR_COND_ACK_PENDING)) { + nrom->condition |= NR_COND_ACK_PENDING; nr_start_t2timer(sk); } } @@ -279,14 +285,15 @@ /* Higher level upcall for a LAPB frame */ int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb) { + nr_cb *nr = nr_sk(sk); int queued = 0, frametype; - if (sk->protinfo.nr->state == NR_STATE_0) + if (nr->state == NR_STATE_0) return 0; frametype = skb->data[19]; - switch (sk->protinfo.nr->state) { + switch (nr->state) { case NR_STATE_1: queued = nr_state1_machine(sk, skb, frametype); break; diff -Nru a/net/netrom/nr_out.c b/net/netrom/nr_out.c --- a/net/netrom/nr_out.c Tue Feb 19 18:09:00 2002 +++ b/net/netrom/nr_out.c Tue Feb 19 18:09:00 2002 @@ -91,13 +91,15 @@ */ static void nr_send_iframe(struct sock *sk, struct sk_buff *skb) { + nr_cb *nr = nr_sk(sk); + if (skb == NULL) return; - skb->data[2] = sk->protinfo.nr->vs; - skb->data[3] = sk->protinfo.nr->vr; + skb->data[2] = nr->vs; + skb->data[3] = nr->vr; - if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY) + if (nr->condition & NR_COND_OWN_RX_BUSY) skb->data[4] |= NR_CHOKE_FLAG; nr_start_idletimer(sk); @@ -108,48 +110,50 @@ void nr_send_nak_frame(struct sock *sk) { struct sk_buff *skb, *skbn; + nr_cb *nr = nr_sk(sk); - if ((skb = skb_peek(&sk->protinfo.nr->ack_queue)) == NULL) + if ((skb = skb_peek(&nr->ack_queue)) == NULL) return; if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) return; - skbn->data[2] = sk->protinfo.nr->va; - skbn->data[3] = sk->protinfo.nr->vr; + skbn->data[2] = nr->va; + skbn->data[3] = nr->vr; - if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY) + if (nr->condition & NR_COND_OWN_RX_BUSY) skbn->data[4] |= NR_CHOKE_FLAG; nr_transmit_buffer(sk, skbn); - sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; - sk->protinfo.nr->vl = sk->protinfo.nr->vr; + nr->condition &= ~NR_COND_ACK_PENDING; + nr->vl = nr->vr; nr_stop_t1timer(sk); } void nr_kick(struct sock *sk) { + nr_cb *nr = nr_sk(sk); struct sk_buff *skb, *skbn; unsigned short start, end; - if (sk->protinfo.nr->state != NR_STATE_3) + if (nr->state != NR_STATE_3) return; - if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) + if (nr->condition & NR_COND_PEER_RX_BUSY) return; if (skb_peek(&sk->write_queue) == NULL) return; - start = (skb_peek(&sk->protinfo.nr->ack_queue) == NULL) ? sk->protinfo.nr->va : sk->protinfo.nr->vs; - end = (sk->protinfo.nr->va + sk->protinfo.nr->window) % NR_MODULUS; + start = (skb_peek(&nr->ack_queue) == NULL) ? nr->va : nr->vs; + end = (nr->va + nr->window) % NR_MODULUS; if (start == end) return; - sk->protinfo.nr->vs = start; + nr->vs = start; /* * Transmit data until either we're out of data to send or @@ -174,17 +178,17 @@ */ nr_send_iframe(sk, skbn); - sk->protinfo.nr->vs = (sk->protinfo.nr->vs + 1) % NR_MODULUS; + nr->vs = (nr->vs + 1) % NR_MODULUS; /* * Requeue the original data frame. */ - skb_queue_tail(&sk->protinfo.nr->ack_queue, skb); + skb_queue_tail(&nr->ack_queue, skb); - } while (sk->protinfo.nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + } while (nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); - sk->protinfo.nr->vl = sk->protinfo.nr->vr; - sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; + nr->vl = nr->vr; + nr->condition &= ~NR_COND_ACK_PENDING; if (!nr_t1timer_running(sk)) nr_start_t1timer(sk); @@ -192,6 +196,7 @@ void nr_transmit_buffer(struct sock *sk, struct sk_buff *skb) { + nr_cb *nr = nr; unsigned char *dptr; /* @@ -199,13 +204,13 @@ */ dptr = skb_push(skb, NR_NETWORK_LEN); - memcpy(dptr, &sk->protinfo.nr->source_addr, AX25_ADDR_LEN); + memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); dptr[6] &= ~AX25_CBIT; dptr[6] &= ~AX25_EBIT; dptr[6] |= AX25_SSSID_SPARE; dptr += AX25_ADDR_LEN; - memcpy(dptr, &sk->protinfo.nr->dest_addr, AX25_ADDR_LEN); + memcpy(dptr, &nr->dest_addr, AX25_ADDR_LEN); dptr[6] &= ~AX25_CBIT; dptr[6] |= AX25_EBIT; dptr[6] |= AX25_SSSID_SPARE; @@ -226,8 +231,10 @@ void nr_establish_data_link(struct sock *sk) { - sk->protinfo.nr->condition = 0x00; - sk->protinfo.nr->n2count = 0; + nr_cb *nr = nr_sk(sk); + + nr->condition = 0x00; + nr->n2count = 0; nr_write_internal(sk, NR_CONNREQ); @@ -242,29 +249,32 @@ */ void nr_enquiry_response(struct sock *sk) { + nr_cb *nr = nr_sk(sk); int frametype = NR_INFOACK; - if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY) { + if (nr->condition & NR_COND_OWN_RX_BUSY) { frametype |= NR_CHOKE_FLAG; } else { - if (skb_peek(&sk->protinfo.nr->reseq_queue) != NULL) + if (skb_peek(&nr->reseq_queue) != NULL) frametype |= NR_NAK_FLAG; } nr_write_internal(sk, frametype); - sk->protinfo.nr->vl = sk->protinfo.nr->vr; - sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; + nr->vl = nr->vr; + nr->condition &= ~NR_COND_ACK_PENDING; } void nr_check_iframes_acked(struct sock *sk, unsigned short nr) { - if (sk->protinfo.nr->vs == nr) { + nr_cb *nrom = nr_sk(sk); + + if (nrom->vs == nr) { nr_frames_acked(sk, nr); nr_stop_t1timer(sk); - sk->protinfo.nr->n2count = 0; + nrom->n2count = 0; } else { - if (sk->protinfo.nr->va != nr) { + if (nrom->va != nr) { nr_frames_acked(sk, nr); nr_start_t1timer(sk); } diff -Nru a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c --- a/net/netrom/nr_subr.c Tue Feb 19 18:09:00 2002 +++ b/net/netrom/nr_subr.c Tue Feb 19 18:09:00 2002 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -42,10 +43,12 @@ */ void nr_clear_queues(struct sock *sk) { + nr_cb *nr = nr_sk(sk); + skb_queue_purge(&sk->write_queue); - skb_queue_purge(&sk->protinfo.nr->ack_queue); - skb_queue_purge(&sk->protinfo.nr->reseq_queue); - skb_queue_purge(&sk->protinfo.nr->frag_queue); + skb_queue_purge(&nr->ack_queue); + skb_queue_purge(&nr->reseq_queue); + skb_queue_purge(&nr->frag_queue); } /* @@ -55,16 +58,17 @@ */ void nr_frames_acked(struct sock *sk, unsigned short nr) { + nr_cb *nrom = nr_sk(sk); struct sk_buff *skb; /* * Remove all the ack-ed frames from the ack queue. */ - if (sk->protinfo.nr->va != nr) { - while (skb_peek(&sk->protinfo.nr->ack_queue) != NULL && sk->protinfo.nr->va != nr) { - skb = skb_dequeue(&sk->protinfo.nr->ack_queue); + if (nrom->va != nr) { + while (skb_peek(&nrom->ack_queue) != NULL && nrom->va != nr) { + skb = skb_dequeue(&nrom->ack_queue); kfree_skb(skb); - sk->protinfo.nr->va = (sk->protinfo.nr->va + 1) % NR_MODULUS; + nrom->va = (nrom->va + 1) % NR_MODULUS; } } } @@ -78,7 +82,7 @@ { struct sk_buff *skb, *skb_prev = NULL; - while ((skb = skb_dequeue(&sk->protinfo.nr->ack_queue)) != NULL) { + while ((skb = skb_dequeue(&nr_sk(sk)->ack_queue)) != NULL) { if (skb_prev == NULL) skb_queue_head(&sk->write_queue, skb); else @@ -93,16 +97,15 @@ */ int nr_validate_nr(struct sock *sk, unsigned short nr) { - unsigned short vc = sk->protinfo.nr->va; + nr_cb *nrom = nr_sk(sk); + unsigned short vc = nrom->va; - while (vc != sk->protinfo.nr->vs) { + while (vc != nrom->vs) { if (nr == vc) return 1; vc = (vc + 1) % NR_MODULUS; } - if (nr == sk->protinfo.nr->vs) return 1; - - return 0; + return nr == nrom->vs; } /* @@ -110,8 +113,9 @@ */ int nr_in_rx_window(struct sock *sk, unsigned short ns) { - unsigned short vc = sk->protinfo.nr->vr; - unsigned short vt = (sk->protinfo.nr->vl + sk->protinfo.nr->window) % NR_MODULUS; + nr_cb *nr = nr_sk(sk); + unsigned short vc = nr->vr; + unsigned short vt = (nr->vl + nr->window) % NR_MODULUS; while (vc != vt) { if (ns == vc) return 1; @@ -127,6 +131,7 @@ */ void nr_write_internal(struct sock *sk, int frametype) { + nr_cb *nr = nr_sk(sk); struct sk_buff *skb; unsigned char *dptr; int len, timeout; @@ -138,7 +143,7 @@ len += 17; break; case NR_CONNACK: - len += (sk->protinfo.nr->bpqext) ? 2 : 1; + len += (nr->bpqext) ? 2 : 1; break; case NR_DISCREQ: case NR_DISCACK: @@ -162,19 +167,19 @@ switch (frametype & 0x0F) { case NR_CONNREQ: - timeout = sk->protinfo.nr->t1 / HZ; - *dptr++ = sk->protinfo.nr->my_index; - *dptr++ = sk->protinfo.nr->my_id; + timeout = nr->t1 / HZ; + *dptr++ = nr->my_index; + *dptr++ = nr->my_id; *dptr++ = 0; *dptr++ = 0; *dptr++ = frametype; - *dptr++ = sk->protinfo.nr->window; - memcpy(dptr, &sk->protinfo.nr->user_addr, AX25_ADDR_LEN); + *dptr++ = nr->window; + memcpy(dptr, &nr->user_addr, AX25_ADDR_LEN); dptr[6] &= ~AX25_CBIT; dptr[6] &= ~AX25_EBIT; dptr[6] |= AX25_SSSID_SPARE; dptr += AX25_ADDR_LEN; - memcpy(dptr, &sk->protinfo.nr->source_addr, AX25_ADDR_LEN); + memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); dptr[6] &= ~AX25_CBIT; dptr[6] &= ~AX25_EBIT; dptr[6] |= AX25_SSSID_SPARE; @@ -184,29 +189,29 @@ break; case NR_CONNACK: - *dptr++ = sk->protinfo.nr->your_index; - *dptr++ = sk->protinfo.nr->your_id; - *dptr++ = sk->protinfo.nr->my_index; - *dptr++ = sk->protinfo.nr->my_id; + *dptr++ = nr->your_index; + *dptr++ = nr->your_id; + *dptr++ = nr->my_index; + *dptr++ = nr->my_id; *dptr++ = frametype; - *dptr++ = sk->protinfo.nr->window; - if (sk->protinfo.nr->bpqext) *dptr++ = sysctl_netrom_network_ttl_initialiser; + *dptr++ = nr->window; + if (nr->bpqext) *dptr++ = sysctl_netrom_network_ttl_initialiser; break; case NR_DISCREQ: case NR_DISCACK: - *dptr++ = sk->protinfo.nr->your_index; - *dptr++ = sk->protinfo.nr->your_id; + *dptr++ = nr->your_index; + *dptr++ = nr->your_id; *dptr++ = 0; *dptr++ = 0; *dptr++ = frametype; break; case NR_INFOACK: - *dptr++ = sk->protinfo.nr->your_index; - *dptr++ = sk->protinfo.nr->your_id; + *dptr++ = nr->your_index; + *dptr++ = nr->your_id; *dptr++ = 0; - *dptr++ = sk->protinfo.nr->vr; + *dptr++ = nr->vr; *dptr++ = frametype; break; } @@ -275,7 +280,7 @@ nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; + nr_sk(sk)->state = NR_STATE_0; sk->state = TCP_CLOSE; sk->err = reason; diff -Nru a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c --- a/net/netrom/nr_timer.c Tue Feb 19 18:08:58 2002 +++ b/net/netrom/nr_timer.c Tue Feb 19 18:08:58 2002 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -45,47 +46,55 @@ void nr_start_t1timer(struct sock *sk) { - del_timer(&sk->protinfo.nr->t1timer); + nr_cb *nr = nr_sk(sk); - sk->protinfo.nr->t1timer.data = (unsigned long)sk; - sk->protinfo.nr->t1timer.function = &nr_t1timer_expiry; - sk->protinfo.nr->t1timer.expires = jiffies + sk->protinfo.nr->t1; + del_timer(&nr->t1timer); - add_timer(&sk->protinfo.nr->t1timer); + nr->t1timer.data = (unsigned long)sk; + nr->t1timer.function = &nr_t1timer_expiry; + nr->t1timer.expires = jiffies + nr->t1; + + add_timer(&nr->t1timer); } void nr_start_t2timer(struct sock *sk) { - del_timer(&sk->protinfo.nr->t2timer); + nr_cb *nr = nr_sk(sk); + + del_timer(&nr->t2timer); - sk->protinfo.nr->t2timer.data = (unsigned long)sk; - sk->protinfo.nr->t2timer.function = &nr_t2timer_expiry; - sk->protinfo.nr->t2timer.expires = jiffies + sk->protinfo.nr->t2; + nr->t2timer.data = (unsigned long)sk; + nr->t2timer.function = &nr_t2timer_expiry; + nr->t2timer.expires = jiffies + nr->t2; - add_timer(&sk->protinfo.nr->t2timer); + add_timer(&nr->t2timer); } void nr_start_t4timer(struct sock *sk) { - del_timer(&sk->protinfo.nr->t4timer); + nr_cb *nr = nr_sk(sk); - sk->protinfo.nr->t4timer.data = (unsigned long)sk; - sk->protinfo.nr->t4timer.function = &nr_t4timer_expiry; - sk->protinfo.nr->t4timer.expires = jiffies + sk->protinfo.nr->t4; + del_timer(&nr->t4timer); - add_timer(&sk->protinfo.nr->t4timer); + nr->t4timer.data = (unsigned long)sk; + nr->t4timer.function = &nr_t4timer_expiry; + nr->t4timer.expires = jiffies + nr->t4; + + add_timer(&nr->t4timer); } void nr_start_idletimer(struct sock *sk) { - del_timer(&sk->protinfo.nr->idletimer); + nr_cb *nr = nr_sk(sk); + + del_timer(&nr->idletimer); - if (sk->protinfo.nr->idle > 0) { - sk->protinfo.nr->idletimer.data = (unsigned long)sk; - sk->protinfo.nr->idletimer.function = &nr_idletimer_expiry; - sk->protinfo.nr->idletimer.expires = jiffies + sk->protinfo.nr->idle; + if (nr->idle > 0) { + nr->idletimer.data = (unsigned long)sk; + nr->idletimer.function = &nr_idletimer_expiry; + nr->idletimer.expires = jiffies + nr->idle; - add_timer(&sk->protinfo.nr->idletimer); + add_timer(&nr->idletimer); } } @@ -102,22 +111,22 @@ void nr_stop_t1timer(struct sock *sk) { - del_timer(&sk->protinfo.nr->t1timer); + del_timer(&nr_sk(sk)->t1timer); } void nr_stop_t2timer(struct sock *sk) { - del_timer(&sk->protinfo.nr->t2timer); + del_timer(&nr_sk(sk)->t2timer); } void nr_stop_t4timer(struct sock *sk) { - del_timer(&sk->protinfo.nr->t4timer); + del_timer(&nr_sk(sk)->t4timer); } void nr_stop_idletimer(struct sock *sk) { - del_timer(&sk->protinfo.nr->idletimer); + del_timer(&nr_sk(sk)->idletimer); } void nr_stop_heartbeat(struct sock *sk) @@ -127,14 +136,15 @@ int nr_t1timer_running(struct sock *sk) { - return timer_pending(&sk->protinfo.nr->t1timer); + return timer_pending(&nr_sk(sk)->t1timer); } static void nr_heartbeat_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; + nr_cb *nr = nr_sk(sk); - switch (sk->protinfo.nr->state) { + switch (nr->state) { case NR_STATE_0: /* Magic here: If we listen() and a new link dies before it @@ -150,10 +160,10 @@ * Check for the state of the receive buffer. */ if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) && - (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)) { - sk->protinfo.nr->condition &= ~NR_COND_OWN_RX_BUSY; - sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; - sk->protinfo.nr->vl = sk->protinfo.nr->vr; + (nr->condition & NR_COND_OWN_RX_BUSY)) { + nr->condition &= ~NR_COND_OWN_RX_BUSY; + nr->condition &= ~NR_COND_ACK_PENDING; + nr->vl = nr->vr; nr_write_internal(sk, NR_INFOACK); break; } @@ -166,9 +176,10 @@ static void nr_t2timer_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; + nr_cb *nr = nr_sk(sk); - if (sk->protinfo.nr->condition & NR_COND_ACK_PENDING) { - sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; + if (nr->condition & NR_COND_ACK_PENDING) { + nr->condition &= ~NR_COND_ACK_PENDING; nr_enquiry_response(sk); } } @@ -177,18 +188,19 @@ { struct sock *sk = (struct sock *)param; - sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; + nr_sk(sk)->condition &= ~NR_COND_PEER_RX_BUSY; } static void nr_idletimer_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; + nr_cb *nr = nr_sk(sk); nr_clear_queues(sk); - sk->protinfo.nr->n2count = 0; + nr->n2count = 0; nr_write_internal(sk, NR_DISCREQ); - sk->protinfo.nr->state = NR_STATE_2; + nr->state = NR_STATE_2; nr_start_t1timer(sk); nr_stop_t2timer(sk); @@ -207,35 +219,36 @@ static void nr_t1timer_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; + nr_cb *nr = nr_sk(sk); - switch (sk->protinfo.nr->state) { + switch (nr->state) { case NR_STATE_1: - if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) { + if (nr->n2count == nr->n2) { nr_disconnect(sk, ETIMEDOUT); return; } else { - sk->protinfo.nr->n2count++; + nr->n2count++; nr_write_internal(sk, NR_CONNREQ); } break; case NR_STATE_2: - if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) { + if (nr->n2count == nr->n2) { nr_disconnect(sk, ETIMEDOUT); return; } else { - sk->protinfo.nr->n2count++; + nr->n2count++; nr_write_internal(sk, NR_DISCREQ); } break; case NR_STATE_3: - if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) { + if (nr->n2count == nr->n2) { nr_disconnect(sk, ETIMEDOUT); return; } else { - sk->protinfo.nr->n2count++; + nr->n2count++; nr_requeue_frames(sk); } break; diff -Nru a/net/netsyms.c b/net/netsyms.c --- a/net/netsyms.c Tue Feb 19 18:08:59 2002 +++ b/net/netsyms.c Tue Feb 19 18:08:59 2002 @@ -577,6 +577,10 @@ EXPORT_SYMBOL(nf_setsockopt); EXPORT_SYMBOL(nf_getsockopt); EXPORT_SYMBOL(ip_ct_attach); +#ifdef CONFIG_INET +#include +EXPORT_SYMBOL(ip_route_me_harder); +#endif #endif EXPORT_SYMBOL(register_gifconf); diff -Nru a/net/packet/af_packet.c b/net/packet/af_packet.c --- a/net/packet/af_packet.c Tue Feb 19 18:08:57 2002 +++ b/net/packet/af_packet.c Tue Feb 19 18:08:58 2002 @@ -5,7 +5,7 @@ * * PACKET - implements raw packet sockets. * - * Version: $Id: af_packet.c,v 1.59 2001/12/21 04:56:17 davem Exp $ + * Version: $Id: af_packet.c,v 1.61 2002/02/08 03:57:19 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -199,6 +199,8 @@ #endif }; +#define pkt_sk(__sk) ((struct packet_opt *)(__sk)->protinfo) + void packet_sock_destruct(struct sock *sk) { BUG_TRAP(atomic_read(&sk->rmem_alloc)==0); @@ -209,8 +211,8 @@ return; } - if (sk->protinfo.destruct_hook) - kfree(sk->protinfo.destruct_hook); + if (pkt_sk(sk)) + kfree(pkt_sk(sk)); atomic_dec(&packet_socks_nr); #ifdef PACKET_REFCNT_DEBUG printk(KERN_DEBUG "PACKET socket %p is free, %d are alive\n", sk, atomic_read(&packet_socks_nr)); @@ -413,7 +415,7 @@ goto drop; sk = (struct sock *) pt->data; - po = sk->protinfo.af_packet; + po = pkt_sk(sk); skb->dev = dev; @@ -528,7 +530,7 @@ goto drop; sk = (struct sock *) pt->data; - po = sk->protinfo.af_packet; + po = pkt_sk(sk); if (dev->hard_header) { if (sk->type != SOCK_DGRAM) @@ -676,7 +678,7 @@ */ if (saddr == NULL) { - ifindex = sk->protinfo.af_packet->ifindex; + ifindex = pkt_sk(sk)->ifindex; proto = sk->num; addr = NULL; } else { @@ -761,6 +763,7 @@ static int packet_release(struct socket *sock) { struct sock *sk = sock->sk; + struct packet_opt *po = pkt_sk(sk); struct sock **skp; if (!sk) @@ -780,12 +783,12 @@ * Unhook packet receive handler. */ - if (sk->protinfo.af_packet->running) { + if (po->running) { /* * Remove the protocol hook */ - dev_remove_pack(&sk->protinfo.af_packet->prot_hook); - sk->protinfo.af_packet->running = 0; + dev_remove_pack(&po->prot_hook); + po->running = 0; __sock_put(sk); } @@ -794,7 +797,7 @@ #endif #ifdef CONFIG_PACKET_MMAP - if (sk->protinfo.af_packet->pg_vec) { + if (po->pg_vec) { struct tpacket_req req; memset(&req, 0, sizeof(req)); packet_set_ring(sk, &req, 1); @@ -822,46 +825,47 @@ static int packet_do_bind(struct sock *sk, struct net_device *dev, int protocol) { + struct packet_opt *po = pkt_sk(sk); /* * Detach an existing hook if present. */ lock_sock(sk); - spin_lock(&sk->protinfo.af_packet->bind_lock); - if (sk->protinfo.af_packet->running) { - dev_remove_pack(&sk->protinfo.af_packet->prot_hook); + spin_lock(&po->bind_lock); + if (po->running) { + dev_remove_pack(&po->prot_hook); __sock_put(sk); - sk->protinfo.af_packet->running = 0; + po->running = 0; } sk->num = protocol; - sk->protinfo.af_packet->prot_hook.type = protocol; - sk->protinfo.af_packet->prot_hook.dev = dev; + po->prot_hook.type = protocol; + po->prot_hook.dev = dev; - sk->protinfo.af_packet->ifindex = dev ? dev->ifindex : 0; + po->ifindex = dev ? dev->ifindex : 0; if (protocol == 0) goto out_unlock; if (dev) { if (dev->flags&IFF_UP) { - dev_add_pack(&sk->protinfo.af_packet->prot_hook); + dev_add_pack(&po->prot_hook); sock_hold(sk); - sk->protinfo.af_packet->running = 1; + po->running = 1; } else { sk->err = ENETDOWN; if (!sk->dead) sk->error_report(sk); } } else { - dev_add_pack(&sk->protinfo.af_packet->prot_hook); + dev_add_pack(&po->prot_hook); sock_hold(sk); - sk->protinfo.af_packet->running = 1; + po->running = 1; } out_unlock: - spin_unlock(&sk->protinfo.af_packet->bind_lock); + spin_unlock(&po->bind_lock); release_sock(sk); return 0; } @@ -936,6 +940,7 @@ static int packet_create(struct socket *sock, int protocol) { struct sock *sk; + struct packet_opt *po; int err; if (!capable(CAP_NET_RAW)) @@ -951,7 +956,7 @@ MOD_INC_USE_COUNT; err = -ENOBUFS; - sk = sk_alloc(PF_PACKET, GFP_KERNEL, 1); + sk = sk_alloc(PF_PACKET, GFP_KERNEL, 1, NULL); if (sk == NULL) goto out; @@ -962,10 +967,10 @@ #endif sock_init_data(sock,sk); - sk->protinfo.af_packet = kmalloc(sizeof(struct packet_opt), GFP_KERNEL); - if (sk->protinfo.af_packet == NULL) + po = pkt_sk(sk) = kmalloc(sizeof(*po), GFP_KERNEL); + if (!po) goto out_free; - memset(sk->protinfo.af_packet, 0, sizeof(struct packet_opt)); + memset(po, 0, sizeof(*po)); sk->family = PF_PACKET; sk->num = protocol; @@ -976,19 +981,19 @@ * Attach a protocol block */ - spin_lock_init(&sk->protinfo.af_packet->bind_lock); - sk->protinfo.af_packet->prot_hook.func = packet_rcv; + spin_lock_init(&po->bind_lock); + po->prot_hook.func = packet_rcv; #ifdef CONFIG_SOCK_PACKET if (sock->type == SOCK_PACKET) - sk->protinfo.af_packet->prot_hook.func = packet_rcv_spkt; + po->prot_hook.func = packet_rcv_spkt; #endif - sk->protinfo.af_packet->prot_hook.data = (void *)sk; + po->prot_hook.data = (void *)sk; if (protocol) { - sk->protinfo.af_packet->prot_hook.type = protocol; - dev_add_pack(&sk->protinfo.af_packet->prot_hook); + po->prot_hook.type = protocol; + dev_add_pack(&po->prot_hook); sock_hold(sk); - sk->protinfo.af_packet->running = 1; + po->running = 1; } write_lock_bh(&packet_sklist_lock); @@ -1023,7 +1028,7 @@ #if 0 /* What error should we return now? EUNATTACH? */ - if (sk->protinfo.af_packet->ifindex < 0) + if (pkt_sk(sk)->ifindex < 0) return -ENODEV; #endif @@ -1101,7 +1106,7 @@ return -EOPNOTSUPP; uaddr->sa_family = AF_PACKET; - dev = dev_get_by_index(sk->protinfo.af_packet->ifindex); + dev = dev_get_by_index(pkt_sk(sk)->ifindex); if (dev) { strncpy(uaddr->sa_data, dev->name, 15); dev_put(dev); @@ -1118,15 +1123,16 @@ { struct net_device *dev; struct sock *sk = sock->sk; + struct packet_opt *po = pkt_sk(sk); struct sockaddr_ll *sll = (struct sockaddr_ll*)uaddr; if (peer) return -EOPNOTSUPP; sll->sll_family = AF_PACKET; - sll->sll_ifindex = sk->protinfo.af_packet->ifindex; + sll->sll_ifindex = po->ifindex; sll->sll_protocol = sk->num; - dev = dev_get_by_index(sk->protinfo.af_packet->ifindex); + dev = dev_get_by_index(po->ifindex); if (dev) { sll->sll_hatype = dev->type; sll->sll_halen = dev->addr_len; @@ -1171,6 +1177,7 @@ static int packet_mc_add(struct sock *sk, struct packet_mreq *mreq) { + struct packet_opt *po = pkt_sk(sk); struct packet_mclist *ml, *i; struct net_device *dev; int err; @@ -1192,7 +1199,7 @@ goto done; err = 0; - for (ml=sk->protinfo.af_packet->mclist; ml; ml=ml->next) { + for (ml = po->mclist; ml; ml = ml->next) { if (ml->ifindex == mreq->mr_ifindex && ml->type == mreq->mr_type && ml->alen == mreq->mr_alen && @@ -1209,8 +1216,8 @@ i->alen = mreq->mr_alen; memcpy(i->addr, mreq->mr_address, i->alen); i->count = 1; - i->next = sk->protinfo.af_packet->mclist; - sk->protinfo.af_packet->mclist = i; + i->next = po->mclist; + po->mclist = i; packet_dev_mc(dev, i, +1); done: @@ -1224,7 +1231,7 @@ rtnl_lock(); - for (mlp=&sk->protinfo.af_packet->mclist; (ml=*mlp)!=NULL; mlp=&ml->next) { + for (mlp = &pkt_sk(sk)->mclist; (ml = *mlp) != NULL; mlp = &ml->next) { if (ml->ifindex == mreq->mr_ifindex && ml->type == mreq->mr_type && ml->alen == mreq->mr_alen && @@ -1249,15 +1256,17 @@ static void packet_flush_mclist(struct sock *sk) { + struct packet_opt *po = pkt_sk(sk); struct packet_mclist *ml; - if (sk->protinfo.af_packet->mclist == NULL) + if (!po->mclist) return; rtnl_lock(); - while ((ml=sk->protinfo.af_packet->mclist) != NULL) { + while ((ml = po->mclist) != NULL) { struct net_device *dev; - sk->protinfo.af_packet->mclist = ml->next; + + po->mclist = ml->next; if ((dev = dev_get_by_index(ml->ifindex)) != NULL) { packet_dev_mc(dev, ml, -1); dev_put(dev); @@ -1314,7 +1323,7 @@ if (copy_from_user(&val,optval,sizeof(val))) return -EFAULT; - sk->protinfo.af_packet->copy_thresh = val; + pkt_sk(sk)->copy_thresh = val; return 0; } #endif @@ -1328,6 +1337,7 @@ { int len; struct sock *sk = sock->sk; + struct packet_opt *po = pkt_sk(sk); if (level != SOL_PACKET) return -ENOPROTOOPT; @@ -1346,8 +1356,8 @@ if (len > sizeof(struct tpacket_stats)) len = sizeof(struct tpacket_stats); spin_lock_bh(&sk->receive_queue.lock); - st = sk->protinfo.af_packet->stats; - memset(&sk->protinfo.af_packet->stats, 0, sizeof(st)); + st = po->stats; + memset(&po->stats, 0, sizeof(st)); spin_unlock_bh(&sk->receive_queue.lock); st.tp_packets += st.tp_drops; @@ -1368,12 +1378,11 @@ static int packet_notifier(struct notifier_block *this, unsigned long msg, void *data) { struct sock *sk; - struct packet_opt *po; struct net_device *dev = (struct net_device*)data; read_lock(&packet_sklist_lock); for (sk = packet_sklist; sk; sk = sk->next) { - po = sk->protinfo.af_packet; + struct packet_opt *po = pkt_sk(sk); switch (msg) { case NETDEV_DOWN: @@ -1552,7 +1561,7 @@ unsigned int packet_poll(struct file * file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; - struct packet_opt *po = sk->protinfo.af_packet; + struct packet_opt *po = pkt_sk(sk); unsigned int mask = datagram_poll(file, sock, wait); spin_lock_bh(&sk->receive_queue.lock); @@ -1579,7 +1588,7 @@ struct sock *sk = sock->sk; if (sk) - atomic_inc(&sk->protinfo.af_packet->mapped); + atomic_inc(&pkt_sk(sk)->mapped); } static void packet_mm_close(struct vm_area_struct *vma) @@ -1590,7 +1599,7 @@ struct sock *sk = sock->sk; if (sk) - atomic_dec(&sk->protinfo.af_packet->mapped); + atomic_dec(&pkt_sk(sk)->mapped); } static struct vm_operations_struct packet_mmap_ops = { @@ -1620,7 +1629,7 @@ { unsigned long *pg_vec = NULL; struct tpacket_hdr **io_vec = NULL; - struct packet_opt *po = sk->protinfo.af_packet; + struct packet_opt *po = pkt_sk(sk); int order = 0; int err = 0; @@ -1742,7 +1751,7 @@ static int packet_mmap(struct file *file, struct socket *sock, struct vm_area_struct *vma) { struct sock *sk = sock->sk; - struct packet_opt *po = sk->protinfo.af_packet; + struct packet_opt *po = pkt_sk(sk); unsigned long size; unsigned long start; int err = -EINVAL; @@ -1846,13 +1855,15 @@ read_lock(&packet_sklist_lock); for (s = packet_sklist; s; s = s->next) { + struct packet_opt *po = pkt_sk(s); + len+=sprintf(buffer+len,"%p %-6d %-4d %04x %-5d %1d %-6u %-6u %-6lu", s, atomic_read(&s->refcnt), s->type, ntohs(s->num), - s->protinfo.af_packet->ifindex, - s->protinfo.af_packet->running, + po->ifindex, + po->running, atomic_read(&s->rmem_alloc), sock_i_uid(s), sock_i_ino(s) diff -Nru a/net/rose/af_rose.c b/net/rose/af_rose.c --- a/net/rose/af_rose.c Tue Feb 19 18:09:00 2002 +++ b/net/rose/af_rose.c Tue Feb 19 18:09:00 2002 @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -148,22 +149,22 @@ struct sock *sk; rose_cb *rose; - if ((sk = sk_alloc(PF_ROSE, GFP_ATOMIC, 1)) == NULL) - return NULL; - - if ((rose = kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) { - sk_free(sk); - return NULL; - } - MOD_INC_USE_COUNT; - memset(rose, 0x00, sizeof(*rose)); + if ((sk = sk_alloc(PF_ROSE, GFP_ATOMIC, 1, NULL)) == NULL) + goto decmod; - sk->protinfo.rose = rose; - rose->sk = sk; + rose = rose_sk(sk) = kmalloc(sizeof(*rose), GFP_ATOMIC); + if (!rose) + goto frees; - return sk; + memset(rose, 0x00, sizeof(*rose)); + rose->sk = sk; +out: return sk; +frees: sk_free(sk); + sk = NULL; +decmod: MOD_DEC_USE_COUNT; + goto out; } /* @@ -204,10 +205,12 @@ struct sock *s; for (s = rose_list; s != NULL; s = s->next) { - if (s->protinfo.rose->neighbour == neigh) { + rose_cb *rose = rose_sk(s); + + if (rose->neighbour == neigh) { rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); - s->protinfo.rose->neighbour->use--; - s->protinfo.rose->neighbour = NULL; + rose->neighbour->use--; + rose->neighbour = NULL; } } } @@ -220,10 +223,12 @@ struct sock *s; for (s = rose_list; s != NULL; s = s->next) { - if (s->protinfo.rose->device == dev) { + rose_cb *rose = rose_sk(s); + + if (rose->device == dev) { rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); - s->protinfo.rose->neighbour->use--; - s->protinfo.rose->device = NULL; + rose->neighbour->use--; + rose->device = NULL; } } } @@ -278,14 +283,22 @@ save_flags(flags); cli(); for (s = rose_list; s != NULL; s = s->next) { - if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) { + rose_cb *rose = rose_sk(s); + + if (!rosecmp(&rose->source_addr, addr) && + !ax25cmp(&rose->source_call, call) && + !rose->source_ndigis && s->state == TCP_LISTEN) { restore_flags(flags); return s; } } for (s = rose_list; s != NULL; s = s->next) { - if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0 && s->state == TCP_LISTEN) { + rose_cb *rose = rose_sk(s); + + if (!rosecmp(&rose->source_addr, addr) && + !ax25cmp(&rose->source_call, &null_ax25_address) && + s->state == TCP_LISTEN) { restore_flags(flags); return s; } @@ -306,7 +319,9 @@ save_flags(flags); cli(); for (s = rose_list; s != NULL; s = s->next) { - if (s->protinfo.rose->lci == lci && s->protinfo.rose->neighbour == neigh) { + rose_cb *rose = rose_sk(s); + + if (rose->lci == lci && rose->neighbour == neigh) { restore_flags(flags); return s; } @@ -374,7 +389,7 @@ if (skb->sk != sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ rose_start_heartbeat(skb->sk); - skb->sk->protinfo.rose->state = ROSE_STATE_0; + rose_sk(skb->sk)->state = ROSE_STATE_0; } kfree_skb(skb); @@ -403,6 +418,7 @@ char *optval, int optlen) { struct sock *sk = sock->sk; + rose_cb *rose = rose_sk(sk); int opt; if (level != SOL_ROSE) @@ -416,41 +432,41 @@ switch (optname) { case ROSE_DEFER: - sk->protinfo.rose->defer = opt ? 1 : 0; + rose->defer = opt ? 1 : 0; return 0; case ROSE_T1: if (opt < 1) return -EINVAL; - sk->protinfo.rose->t1 = opt * HZ; + rose->t1 = opt * HZ; return 0; case ROSE_T2: if (opt < 1) return -EINVAL; - sk->protinfo.rose->t2 = opt * HZ; + rose->t2 = opt * HZ; return 0; case ROSE_T3: if (opt < 1) return -EINVAL; - sk->protinfo.rose->t3 = opt * HZ; + rose->t3 = opt * HZ; return 0; case ROSE_HOLDBACK: if (opt < 1) return -EINVAL; - sk->protinfo.rose->hb = opt * HZ; + rose->hb = opt * HZ; return 0; case ROSE_IDLE: if (opt < 0) return -EINVAL; - sk->protinfo.rose->idle = opt * 60 * HZ; + rose->idle = opt * 60 * HZ; return 0; case ROSE_QBITINCL: - sk->protinfo.rose->qbitincl = opt ? 1 : 0; + rose->qbitincl = opt ? 1 : 0; return 0; default: @@ -462,6 +478,7 @@ char *optval, int *optlen) { struct sock *sk = sock->sk; + rose_cb *rose = rose_sk(sk); int val = 0; int len; @@ -476,31 +493,31 @@ switch (optname) { case ROSE_DEFER: - val = sk->protinfo.rose->defer; + val = rose->defer; break; case ROSE_T1: - val = sk->protinfo.rose->t1 / HZ; + val = rose->t1 / HZ; break; case ROSE_T2: - val = sk->protinfo.rose->t2 / HZ; + val = rose->t2 / HZ; break; case ROSE_T3: - val = sk->protinfo.rose->t3 / HZ; + val = rose->t3 / HZ; break; case ROSE_HOLDBACK: - val = sk->protinfo.rose->hb / HZ; + val = rose->hb / HZ; break; case ROSE_IDLE: - val = sk->protinfo.rose->idle / (60 * HZ); + val = rose->idle / (60 * HZ); break; case ROSE_QBITINCL: - val = sk->protinfo.rose->qbitincl; + val = rose->qbitincl; break; default: @@ -520,10 +537,12 @@ struct sock *sk = sock->sk; if (sk->state != TCP_LISTEN) { - sk->protinfo.rose->dest_ndigis = 0; - memset(&sk->protinfo.rose->dest_addr, '\0', ROSE_ADDR_LEN); - memset(&sk->protinfo.rose->dest_call, '\0', AX25_ADDR_LEN); - memset(sk->protinfo.rose->dest_digis, '\0', AX25_ADDR_LEN*ROSE_MAX_DIGIS); + rose_cb *rose = rose_sk(sk); + + rose->dest_ndigis = 0; + memset(&rose->dest_addr, 0, ROSE_ADDR_LEN); + memset(&rose->dest_call, 0, AX25_ADDR_LEN); + memset(rose->dest_digis, 0, AX25_ADDR_LEN * ROSE_MAX_DIGIS); sk->max_ack_backlog = backlog; sk->state = TCP_LISTEN; return 0; @@ -543,7 +562,7 @@ if ((sk = rose_alloc_sock()) == NULL) return -ENOMEM; - rose = sk->protinfo.rose; + rose = rose_sk(sk); sock_init_data(sock, sk); @@ -573,7 +592,7 @@ static struct sock *rose_make_new(struct sock *osk) { struct sock *sk; - rose_cb *rose; + rose_cb *rose, *orose; if (osk->type != SOCK_SEQPACKET) return NULL; @@ -581,7 +600,7 @@ if ((sk = rose_alloc_sock()) == NULL) return NULL; - rose = sk->protinfo.rose; + rose = rose_sk(sk); sock_init_data(NULL, sk); @@ -605,15 +624,15 @@ init_timer(&rose->timer); init_timer(&rose->idletimer); - rose->t1 = osk->protinfo.rose->t1; - rose->t2 = osk->protinfo.rose->t2; - rose->t3 = osk->protinfo.rose->t3; - rose->hb = osk->protinfo.rose->hb; - rose->idle = osk->protinfo.rose->idle; - - rose->defer = osk->protinfo.rose->defer; - rose->device = osk->protinfo.rose->device; - rose->qbitincl = osk->protinfo.rose->qbitincl; + orose = rose_sk(osk); + rose->t1 = orose->t1; + rose->t2 = orose->t2; + rose->t3 = orose->t3; + rose->hb = orose->hb; + rose->idle = orose->idle; + rose->defer = orose->defer; + rose->device = orose->device; + rose->qbitincl = orose->qbitincl; return sk; } @@ -621,10 +640,13 @@ static int rose_release(struct socket *sock) { struct sock *sk = sock->sk; + rose_cb *rose; if (sk == NULL) return 0; - switch (sk->protinfo.rose->state) { + rose = rose_sk(sk); + + switch (rose->state) { case ROSE_STATE_0: rose_disconnect(sk, 0, -1, -1); @@ -632,7 +654,7 @@ break; case ROSE_STATE_2: - sk->protinfo.rose->neighbour->use--; + rose->neighbour->use--; rose_disconnect(sk, 0, -1, -1); rose_destroy_socket(sk); break; @@ -645,12 +667,12 @@ rose_stop_idletimer(sk); rose_write_internal(sk, ROSE_CLEAR_REQUEST); rose_start_t3timer(sk); - sk->protinfo.rose->state = ROSE_STATE_2; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; + rose->state = ROSE_STATE_2; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - sk->dead = 1; - sk->destroy = 1; + sk->dead = 1; + sk->destroy = 1; break; default: @@ -666,6 +688,7 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; + rose_cb *rose = rose_sk(sk); struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; struct net_device *dev; ax25_address *user, *source; @@ -699,18 +722,18 @@ user = source; } - sk->protinfo.rose->source_addr = addr->srose_addr; - sk->protinfo.rose->source_call = *user; - sk->protinfo.rose->device = dev; - sk->protinfo.rose->source_ndigis = addr->srose_ndigis; + rose->source_addr = addr->srose_addr; + rose->source_call = *user; + rose->device = dev; + rose->source_ndigis = addr->srose_ndigis; if (addr_len == sizeof(struct full_sockaddr_rose)) { struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr; for (n = 0 ; n < addr->srose_ndigis ; n++) - sk->protinfo.rose->source_digis[n] = full_addr->srose_digis[n]; + rose->source_digis[n] = full_addr->srose_digis[n]; } else { - if (sk->protinfo.rose->source_ndigis == 1) { - sk->protinfo.rose->source_digis[0] = addr->srose_digi; + if (rose->source_ndigis == 1) { + rose->source_digis[0] = addr->srose_digi; } } @@ -724,6 +747,7 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { struct sock *sk = sock->sk; + rose_cb *rose = rose_sk(sk); struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; unsigned char cause, diagnostic; ax25_address *user; @@ -759,13 +783,16 @@ return -EINVAL; /* Source + Destination digis should not exceed ROSE_MAX_DIGIS */ - if ((sk->protinfo.rose->source_ndigis + addr->srose_ndigis) > ROSE_MAX_DIGIS) + if ((rose->source_ndigis + addr->srose_ndigis) > ROSE_MAX_DIGIS) return -EINVAL; - if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL) + rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, + &diagnostic); + if (!rose->neighbour) return -ENETUNREACH; - if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0) + rose->lci = rose_new_lci(rose->neighbour); + if (!rose->lci) return -ENETUNREACH; if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */ @@ -777,25 +804,25 @@ if ((user = ax25_findbyuid(current->euid)) == NULL) return -EINVAL; - memcpy(&sk->protinfo.rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN); - sk->protinfo.rose->source_call = *user; - sk->protinfo.rose->device = dev; + memcpy(&rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN); + rose->source_call = *user; + rose->device = dev; rose_insert_socket(sk); /* Finish the bind */ } - sk->protinfo.rose->dest_addr = addr->srose_addr; - sk->protinfo.rose->dest_call = addr->srose_call; - sk->protinfo.rose->rand = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci; - sk->protinfo.rose->dest_ndigis = addr->srose_ndigis; + rose->dest_addr = addr->srose_addr; + rose->dest_call = addr->srose_call; + rose->rand = ((int)rose & 0xFFFF) + rose->lci; + rose->dest_ndigis = addr->srose_ndigis; if (addr_len == sizeof(struct full_sockaddr_rose)) { struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr; for (n = 0 ; n < addr->srose_ndigis ; n++) - sk->protinfo.rose->dest_digis[n] = full_addr->srose_digis[n]; + rose->dest_digis[n] = full_addr->srose_digis[n]; } else { - if (sk->protinfo.rose->dest_ndigis == 1) { - sk->protinfo.rose->dest_digis[0] = addr->srose_digi; + if (rose->dest_ndigis == 1) { + rose->dest_digis[0] = addr->srose_digi; } } @@ -803,9 +830,9 @@ sock->state = SS_CONNECTING; sk->state = TCP_SYN_SENT; - sk->protinfo.rose->state = ROSE_STATE_1; + rose->state = ROSE_STATE_1; - sk->protinfo.rose->neighbour->use++; + rose->neighbour->use++; rose_write_internal(sk, ROSE_CALL_REQUEST); rose_start_heartbeat(sk); @@ -895,24 +922,25 @@ { struct full_sockaddr_rose *srose = (struct full_sockaddr_rose *)uaddr; struct sock *sk = sock->sk; + rose_cb *rose = rose_sk(sk); int n; if (peer != 0) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; srose->srose_family = AF_ROSE; - srose->srose_addr = sk->protinfo.rose->dest_addr; - srose->srose_call = sk->protinfo.rose->dest_call; - srose->srose_ndigis = sk->protinfo.rose->dest_ndigis; - for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++) - srose->srose_digis[n] = sk->protinfo.rose->dest_digis[n]; + srose->srose_addr = rose->dest_addr; + srose->srose_call = rose->dest_call; + srose->srose_ndigis = rose->dest_ndigis; + for (n = 0; n < rose->dest_ndigis; n++) + srose->srose_digis[n] = rose->dest_digis[n]; } else { srose->srose_family = AF_ROSE; - srose->srose_addr = sk->protinfo.rose->source_addr; - srose->srose_call = sk->protinfo.rose->source_call; - srose->srose_ndigis = sk->protinfo.rose->source_ndigis; - for (n = 0 ; n < sk->protinfo.rose->source_ndigis ; n++) - srose->srose_digis[n] = sk->protinfo.rose->source_digis[n]; + srose->srose_addr = rose->source_addr; + srose->srose_call = rose->source_call; + srose->srose_ndigis = rose->source_ndigis; + for (n = 0; n < rose->source_ndigis; n++) + srose->srose_digis[n] = rose->source_digis[n]; } *uaddr_len = sizeof(struct full_sockaddr_rose); @@ -923,6 +951,7 @@ { struct sock *sk; struct sock *make; + rose_cb *make_rose; struct rose_facilities_struct facilities; int n, len; @@ -952,37 +981,38 @@ skb->sk = make; make->state = TCP_ESTABLISHED; + make_rose = rose_sk(make); - make->protinfo.rose->lci = lci; - make->protinfo.rose->dest_addr = facilities.dest_addr; - make->protinfo.rose->dest_call = facilities.dest_call; - make->protinfo.rose->dest_ndigis = facilities.dest_ndigis; + make_rose->lci = lci; + make_rose->dest_addr = facilities.dest_addr; + make_rose->dest_call = facilities.dest_call; + make_rose->dest_ndigis = facilities.dest_ndigis; for (n = 0 ; n < facilities.dest_ndigis ; n++) - make->protinfo.rose->dest_digis[n] = facilities.dest_digis[n]; - make->protinfo.rose->source_addr = facilities.source_addr; - make->protinfo.rose->source_call = facilities.source_call; - make->protinfo.rose->source_ndigis = facilities.source_ndigis; + make_rose->dest_digis[n] = facilities.dest_digis[n]; + make_rose->source_addr = facilities.source_addr; + make_rose->source_call = facilities.source_call; + make_rose->source_ndigis = facilities.source_ndigis; for (n = 0 ; n < facilities.source_ndigis ; n++) - make->protinfo.rose->source_digis[n]= facilities.source_digis[n]; - make->protinfo.rose->neighbour = neigh; - make->protinfo.rose->device = dev; - make->protinfo.rose->facilities = facilities; + make_rose->source_digis[n]= facilities.source_digis[n]; + make_rose->neighbour = neigh; + make_rose->device = dev; + make_rose->facilities = facilities; - make->protinfo.rose->neighbour->use++; + make_rose->neighbour->use++; - if (sk->protinfo.rose->defer) { - make->protinfo.rose->state = ROSE_STATE_5; + if (rose_sk(sk)->defer) { + make_rose->state = ROSE_STATE_5; } else { rose_write_internal(make, ROSE_CALL_ACCEPTED); - make->protinfo.rose->state = ROSE_STATE_3; + make_rose->state = ROSE_STATE_3; rose_start_idletimer(make); } - make->protinfo.rose->condition = 0x00; - make->protinfo.rose->vs = 0; - make->protinfo.rose->va = 0; - make->protinfo.rose->vr = 0; - make->protinfo.rose->vl = 0; + make_rose->condition = 0x00; + make_rose->vs = 0; + make_rose->va = 0; + make_rose->vr = 0; + make_rose->vl = 0; sk->ack_backlog++; make->pair = sk; @@ -1002,6 +1032,7 @@ struct scm_cookie *scm) { struct sock *sk = sock->sk; + rose_cb *rose = rose_sk(sk); struct sockaddr_rose *usrose = (struct sockaddr_rose *)msg->msg_name; int err; struct full_sockaddr_rose srose; @@ -1020,7 +1051,7 @@ return -EPIPE; } - if (sk->protinfo.rose->neighbour == NULL || sk->protinfo.rose->device == NULL) + if (rose->neighbour == NULL || rose->device == NULL) return -ENETUNREACH; if (usrose != NULL) { @@ -1028,14 +1059,15 @@ return -EINVAL; memset(&srose, 0, sizeof(struct full_sockaddr_rose)); memcpy(&srose, usrose, msg->msg_namelen); - if (rosecmp(&sk->protinfo.rose->dest_addr, &srose.srose_addr) != 0 || - ax25cmp(&sk->protinfo.rose->dest_call, &srose.srose_call) != 0) + if (rosecmp(&rose->dest_addr, &srose.srose_addr) != 0 || + ax25cmp(&rose->dest_call, &srose.srose_call) != 0) return -EISCONN; - if (srose.srose_ndigis != sk->protinfo.rose->dest_ndigis) + if (srose.srose_ndigis != rose->dest_ndigis) return -EISCONN; - if (srose.srose_ndigis == sk->protinfo.rose->dest_ndigis) { + if (srose.srose_ndigis == rose->dest_ndigis) { for (n = 0 ; n < srose.srose_ndigis ; n++) - if (ax25cmp(&sk->protinfo.rose->dest_digis[n], &srose.srose_digis[n]) != 0) + if (ax25cmp(&rose->dest_digis[n], + &srose.srose_digis[n])) return -EISCONN; } if (srose.srose_family != AF_ROSE) @@ -1045,11 +1077,11 @@ return -ENOTCONN; srose.srose_family = AF_ROSE; - srose.srose_addr = sk->protinfo.rose->dest_addr; - srose.srose_call = sk->protinfo.rose->dest_call; - srose.srose_ndigis = sk->protinfo.rose->dest_ndigis; - for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++) - srose.srose_digis[n] = sk->protinfo.rose->dest_digis[n]; + srose.srose_addr = rose->dest_addr; + srose.srose_call = rose->dest_call; + srose.srose_ndigis = rose->dest_ndigis; + for (n = 0 ; n < rose->dest_ndigis ; n++) + srose.srose_digis[n] = rose->dest_digis[n]; } SOCK_DEBUG(sk, "ROSE: sendto: Addresses built.\n"); @@ -1076,7 +1108,7 @@ * If the Q BIT Include socket option is in force, the first * byte of the user data is the logical value of the Q Bit. */ - if (sk->protinfo.rose->qbitincl) { + if (rose->qbitincl) { qbit = skb->data[0]; skb_pull(skb, 1); } @@ -1089,8 +1121,8 @@ SOCK_DEBUG(sk, "ROSE: Building Network Header.\n"); /* Build a ROSE Network header */ - asmptr[0] = ((sk->protinfo.rose->lci >> 8) & 0x0F) | ROSE_GFI; - asmptr[1] = (sk->protinfo.rose->lci >> 0) & 0xFF; + asmptr[0] = ((rose->lci >> 8) & 0x0F) | ROSE_GFI; + asmptr[1] = (rose->lci >> 0) & 0xFF; asmptr[2] = ROSE_DATA; if (qbit) @@ -1164,6 +1196,7 @@ int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; + rose_cb *rose = rose_sk(sk); struct sockaddr_rose *srose = (struct sockaddr_rose *)msg->msg_name; int copied, qbit; unsigned char *asmptr; @@ -1185,7 +1218,7 @@ skb_pull(skb, ROSE_MIN_LEN); - if (sk->protinfo.rose->qbitincl) { + if (rose->qbitincl) { asmptr = skb_push(skb, 1); *asmptr = qbit; } @@ -1202,18 +1235,18 @@ if (srose != NULL) { srose->srose_family = AF_ROSE; - srose->srose_addr = sk->protinfo.rose->dest_addr; - srose->srose_call = sk->protinfo.rose->dest_call; - srose->srose_ndigis = sk->protinfo.rose->dest_ndigis; + srose->srose_addr = rose->dest_addr; + srose->srose_call = rose->dest_call; + srose->srose_ndigis = rose->dest_ndigis; if (msg->msg_namelen >= sizeof(struct full_sockaddr_rose)) { struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)msg->msg_name; - for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++) - full_srose->srose_digis[n] = sk->protinfo.rose->dest_digis[n]; + for (n = 0 ; n < rose->dest_ndigis ; n++) + full_srose->srose_digis[n] = rose->dest_digis[n]; msg->msg_namelen = sizeof(struct full_sockaddr_rose); } else { - if (sk->protinfo.rose->dest_ndigis >= 1) { + if (rose->dest_ndigis >= 1) { srose->srose_ndigis = 1; - srose->srose_digi = sk->protinfo.rose->dest_digis[0]; + srose->srose_digi = rose->dest_digis[0]; } msg->msg_namelen = sizeof(struct sockaddr_rose); } @@ -1228,6 +1261,7 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; + rose_cb *rose = rose_sk(sk); switch (cmd) { case TIOCOUTQ: { @@ -1275,8 +1309,8 @@ case SIOCRSGCAUSE: { struct rose_cause_struct rose_cause; - rose_cause.cause = sk->protinfo.rose->cause; - rose_cause.diagnostic = sk->protinfo.rose->diagnostic; + rose_cause.cause = rose->cause; + rose_cause.diagnostic = rose->diagnostic; return copy_to_user((void *)arg, &rose_cause, sizeof(struct rose_cause_struct)) ? -EFAULT : 0; } @@ -1284,8 +1318,8 @@ struct rose_cause_struct rose_cause; if (copy_from_user(&rose_cause, (void *)arg, sizeof(struct rose_cause_struct))) return -EFAULT; - sk->protinfo.rose->cause = rose_cause.cause; - sk->protinfo.rose->diagnostic = rose_cause.diagnostic; + rose->cause = rose_cause.cause; + rose->diagnostic = rose_cause.diagnostic; return 0; } @@ -1303,15 +1337,15 @@ return copy_to_user((void *)arg, &rose_callsign, sizeof(ax25_address)) ? -EFAULT : 0; case SIOCRSACCEPT: - if (sk->protinfo.rose->state == ROSE_STATE_5) { + if (rose->state == ROSE_STATE_5) { rose_write_internal(sk, ROSE_CALL_ACCEPTED); rose_start_idletimer(sk); - sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->vs = 0; - sk->protinfo.rose->va = 0; - sk->protinfo.rose->vr = 0; - sk->protinfo.rose->vl = 0; - sk->protinfo.rose->state = ROSE_STATE_3; + rose->condition = 0x00; + rose->vs = 0; + rose->va = 0; + rose->vr = 0; + rose->vl = 0; + rose->state = ROSE_STATE_3; } return 0; @@ -1337,37 +1371,39 @@ len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci neigh st vs vr va t t1 t2 t3 hb idle Snd-Q Rcv-Q inode\n"); for (s = rose_list; s != NULL; s = s->next) { - if ((dev = s->protinfo.rose->device) == NULL) + rose_cb *rose = rose_sk(s); + + if ((dev = rose->device) == NULL) devname = "???"; else devname = dev->name; len += sprintf(buffer + len, "%-10s %-9s ", - rose2asc(&s->protinfo.rose->dest_addr), - ax2asc(&s->protinfo.rose->dest_call)); + rose2asc(&rose->dest_addr), + ax2asc(&rose->dest_call)); - if (ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0) + if (ax25cmp(&rose->source_call, &null_ax25_address) == 0) callsign = "??????-?"; else - callsign = ax2asc(&s->protinfo.rose->source_call); + callsign = ax2asc(&rose->source_call); len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %05d %d %d %d %d %3lu %3lu %3lu %3lu %3lu %3lu/%03lu %5d %5d %ld\n", - rose2asc(&s->protinfo.rose->source_addr), + rose2asc(&rose->source_addr), callsign, devname, - s->protinfo.rose->lci & 0x0FFF, - (s->protinfo.rose->neighbour) ? s->protinfo.rose->neighbour->number : 0, - s->protinfo.rose->state, - s->protinfo.rose->vs, - s->protinfo.rose->vr, - s->protinfo.rose->va, - ax25_display_timer(&s->protinfo.rose->timer) / HZ, - s->protinfo.rose->t1 / HZ, - s->protinfo.rose->t2 / HZ, - s->protinfo.rose->t3 / HZ, - s->protinfo.rose->hb / HZ, - ax25_display_timer(&s->protinfo.rose->idletimer) / (60 * HZ), - s->protinfo.rose->idle / (60 * HZ), + rose->lci & 0x0FFF, + (rose->neighbour) ? rose->neighbour->number : 0, + rose->state, + rose->vs, + rose->vr, + rose->va, + ax25_display_timer(&rose->timer) / HZ, + rose->t1 / HZ, + rose->t2 / HZ, + rose->t3 / HZ, + rose->hb / HZ, + ax25_display_timer(&rose->idletimer) / (60 * HZ), + rose->idle / (60 * HZ), atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc), s->socket != NULL ? SOCK_INODE(s->socket)->i_ino : 0L); diff -Nru a/net/rose/rose_in.c b/net/rose/rose_in.c --- a/net/rose/rose_in.c Tue Feb 19 18:08:57 2002 +++ b/net/rose/rose_in.c Tue Feb 19 18:08:57 2002 @@ -38,6 +38,7 @@ #include #include #include /* For ip_rcv */ +#include #include #include #include @@ -51,17 +52,19 @@ */ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + rose_cb *rose = rose_sk(sk); + switch (frametype) { case ROSE_CALL_ACCEPTED: rose_stop_timer(sk); rose_start_idletimer(sk); - sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->vs = 0; - sk->protinfo.rose->va = 0; - sk->protinfo.rose->vr = 0; - sk->protinfo.rose->vl = 0; - sk->protinfo.rose->state = ROSE_STATE_3; + rose->condition = 0x00; + rose->vs = 0; + rose->va = 0; + rose->vr = 0; + rose->vl = 0; + rose->state = ROSE_STATE_3; sk->state = TCP_ESTABLISHED; if (!sk->dead) sk->state_change(sk); @@ -70,7 +73,7 @@ case ROSE_CLEAR_REQUEST: rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); - sk->protinfo.rose->neighbour->use--; + rose->neighbour->use--; break; default: @@ -87,17 +90,19 @@ */ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + rose_cb *rose = rose_sk(sk); + switch (frametype) { case ROSE_CLEAR_REQUEST: rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - sk->protinfo.rose->neighbour->use--; + rose->neighbour->use--; break; case ROSE_CLEAR_CONFIRMATION: rose_disconnect(sk, 0, -1, -1); - sk->protinfo.rose->neighbour->use--; + rose->neighbour->use--; break; default: @@ -114,6 +119,7 @@ */ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m) { + rose_cb *rose = rose_sk(sk); int queued = 0; switch (frametype) { @@ -122,88 +128,88 @@ rose_stop_timer(sk); rose_start_idletimer(sk); rose_write_internal(sk, ROSE_RESET_CONFIRMATION); - sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->vs = 0; - sk->protinfo.rose->vr = 0; - sk->protinfo.rose->va = 0; - sk->protinfo.rose->vl = 0; + rose->condition = 0x00; + rose->vs = 0; + rose->vr = 0; + rose->va = 0; + rose->vl = 0; rose_requeue_frames(sk); break; case ROSE_CLEAR_REQUEST: rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - sk->protinfo.rose->neighbour->use--; + rose->neighbour->use--; break; case ROSE_RR: case ROSE_RNR: if (!rose_validate_nr(sk, nr)) { rose_write_internal(sk, ROSE_RESET_REQUEST); - sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->vs = 0; - sk->protinfo.rose->vr = 0; - sk->protinfo.rose->va = 0; - sk->protinfo.rose->vl = 0; - sk->protinfo.rose->state = ROSE_STATE_4; + rose->condition = 0x00; + rose->vs = 0; + rose->vr = 0; + rose->va = 0; + rose->vl = 0; + rose->state = ROSE_STATE_4; rose_start_t2timer(sk); rose_stop_idletimer(sk); } else { rose_frames_acked(sk, nr); if (frametype == ROSE_RNR) { - sk->protinfo.rose->condition |= ROSE_COND_PEER_RX_BUSY; + rose->condition |= ROSE_COND_PEER_RX_BUSY; } else { - sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY; + rose->condition &= ~ROSE_COND_PEER_RX_BUSY; } } break; case ROSE_DATA: /* XXX */ - sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY; + rose->condition &= ~ROSE_COND_PEER_RX_BUSY; if (!rose_validate_nr(sk, nr)) { rose_write_internal(sk, ROSE_RESET_REQUEST); - sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->vs = 0; - sk->protinfo.rose->vr = 0; - sk->protinfo.rose->va = 0; - sk->protinfo.rose->vl = 0; - sk->protinfo.rose->state = ROSE_STATE_4; + rose->condition = 0x00; + rose->vs = 0; + rose->vr = 0; + rose->va = 0; + rose->vl = 0; + rose->state = ROSE_STATE_4; rose_start_t2timer(sk); rose_stop_idletimer(sk); break; } rose_frames_acked(sk, nr); - if (ns == sk->protinfo.rose->vr) { + if (ns == rose->vr) { rose_start_idletimer(sk); if (sock_queue_rcv_skb(sk, skb) == 0) { - sk->protinfo.rose->vr = (sk->protinfo.rose->vr + 1) % ROSE_MODULUS; + rose->vr = (rose->vr + 1) % ROSE_MODULUS; queued = 1; } else { /* Should never happen ! */ rose_write_internal(sk, ROSE_RESET_REQUEST); - sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->vs = 0; - sk->protinfo.rose->vr = 0; - sk->protinfo.rose->va = 0; - sk->protinfo.rose->vl = 0; - sk->protinfo.rose->state = ROSE_STATE_4; + rose->condition = 0x00; + rose->vs = 0; + rose->vr = 0; + rose->va = 0; + rose->vl = 0; + rose->state = ROSE_STATE_4; rose_start_t2timer(sk); rose_stop_idletimer(sk); break; } if (atomic_read(&sk->rmem_alloc) > (sk->rcvbuf / 2)) - sk->protinfo.rose->condition |= ROSE_COND_OWN_RX_BUSY; + rose->condition |= ROSE_COND_OWN_RX_BUSY; } /* * If the window is full, ack the frame, else start the * acknowledge hold back timer. */ - if (((sk->protinfo.rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == sk->protinfo.rose->vr) { - sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; + if (((rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == rose->vr) { + rose->condition &= ~ROSE_COND_ACK_PENDING; rose_stop_timer(sk); rose_enquiry_response(sk); } else { - sk->protinfo.rose->condition |= ROSE_COND_ACK_PENDING; + rose->condition |= ROSE_COND_ACK_PENDING; rose_start_hbtimer(sk); } break; @@ -223,6 +229,8 @@ */ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + rose_cb *rose = rose_sk(sk); + switch (frametype) { case ROSE_RESET_REQUEST: @@ -230,19 +238,19 @@ case ROSE_RESET_CONFIRMATION: rose_stop_timer(sk); rose_start_idletimer(sk); - sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->va = 0; - sk->protinfo.rose->vr = 0; - sk->protinfo.rose->vs = 0; - sk->protinfo.rose->vl = 0; - sk->protinfo.rose->state = ROSE_STATE_3; + rose->condition = 0x00; + rose->va = 0; + rose->vr = 0; + rose->vs = 0; + rose->vl = 0; + rose->state = ROSE_STATE_3; rose_requeue_frames(sk); break; case ROSE_CLEAR_REQUEST: rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - sk->protinfo.rose->neighbour->use--; + rose->neighbour->use--; break; default: @@ -262,7 +270,7 @@ if (frametype == ROSE_CLEAR_REQUEST) { rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - sk->protinfo.rose->neighbour->use--; + rose_sk(sk)->neighbour->use--; } return 0; @@ -271,14 +279,15 @@ /* Higher level upcall for a LAPB frame */ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) { + rose_cb *rose = rose_sk(sk); int queued = 0, frametype, ns, nr, q, d, m; - if (sk->protinfo.rose->state == ROSE_STATE_0) + if (rose->state == ROSE_STATE_0) return 0; frametype = rose_decode(skb, &ns, &nr, &q, &d, &m); - switch (sk->protinfo.rose->state) { + switch (rose->state) { case ROSE_STATE_1: queued = rose_state1_machine(sk, skb, frametype); break; diff -Nru a/net/rose/rose_out.c b/net/rose/rose_out.c --- a/net/rose/rose_out.c Tue Feb 19 18:09:00 2002 +++ b/net/rose/rose_out.c Tue Feb 19 18:09:00 2002 @@ -42,38 +42,41 @@ */ static void rose_send_iframe(struct sock *sk, struct sk_buff *skb) { + rose_cb *rose = rose_sk(sk); + if (skb == NULL) return; - skb->data[2] |= (sk->protinfo.rose->vr << 5) & 0xE0; - skb->data[2] |= (sk->protinfo.rose->vs << 1) & 0x0E; + skb->data[2] |= (rose->vr << 5) & 0xE0; + skb->data[2] |= (rose->vs << 1) & 0x0E; rose_start_idletimer(sk); - rose_transmit_link(skb, sk->protinfo.rose->neighbour); + rose_transmit_link(skb, rose->neighbour); } void rose_kick(struct sock *sk) { + rose_cb *rose = rose_sk(sk); struct sk_buff *skb, *skbn; unsigned short start, end; - if (sk->protinfo.rose->state != ROSE_STATE_3) + if (rose->state != ROSE_STATE_3) return; - if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) + if (rose->condition & ROSE_COND_PEER_RX_BUSY) return; if (skb_peek(&sk->write_queue) == NULL) return; - start = (skb_peek(&sk->protinfo.rose->ack_queue) == NULL) ? sk->protinfo.rose->va : sk->protinfo.rose->vs; - end = (sk->protinfo.rose->va + sysctl_rose_window_size) % ROSE_MODULUS; + start = (skb_peek(&rose->ack_queue) == NULL) ? rose->va : rose->vs; + end = (rose->va + sysctl_rose_window_size) % ROSE_MODULUS; if (start == end) return; - sk->protinfo.rose->vs = start; + rose->vs = start; /* * Transmit data until either we're out of data to send or @@ -95,17 +98,17 @@ */ rose_send_iframe(sk, skbn); - sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS; + rose->vs = (rose->vs + 1) % ROSE_MODULUS; /* * Requeue the original data frame. */ - skb_queue_tail(&sk->protinfo.rose->ack_queue, skb); + skb_queue_tail(&rose->ack_queue, skb); - } while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + } while (rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); - sk->protinfo.rose->vl = sk->protinfo.rose->vr; - sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; + rose->vl = rose->vr; + rose->condition &= ~ROSE_COND_ACK_PENDING; rose_stop_timer(sk); } @@ -117,13 +120,15 @@ void rose_enquiry_response(struct sock *sk) { - if (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY) + rose_cb *rose = rose_sk(sk); + + if (rose->condition & ROSE_COND_OWN_RX_BUSY) rose_write_internal(sk, ROSE_RNR); else rose_write_internal(sk, ROSE_RR); - sk->protinfo.rose->vl = sk->protinfo.rose->vr; - sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; + rose->vl = rose->vr; + rose->condition &= ~ROSE_COND_ACK_PENDING; rose_stop_timer(sk); } diff -Nru a/net/rose/rose_route.c b/net/rose/rose_route.c --- a/net/rose/rose_route.c Tue Feb 19 18:08:59 2002 +++ b/net/rose/rose_route.c Tue Feb 19 18:08:59 2002 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -809,20 +810,21 @@ */ if ((sk = rose_find_socket(lci, rose_neigh)) != NULL) { if (frametype == ROSE_CALL_REQUEST) { + rose_cb *rose = rose_sk(sk); /* Remove an existing unused socket */ rose_clear_queues(sk); - sk->protinfo.rose->cause = ROSE_NETWORK_CONGESTION; - sk->protinfo.rose->diagnostic = 0; - sk->protinfo.rose->neighbour->use--; - sk->protinfo.rose->neighbour = NULL; - sk->protinfo.rose->lci = 0; - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; + rose->cause = ROSE_NETWORK_CONGESTION; + rose->diagnostic = 0; + rose->neighbour->use--; + rose->neighbour = NULL; + rose->lci = 0; + rose->state = ROSE_STATE_0; + sk->state = TCP_CLOSE; + sk->err = 0; + sk->shutdown |= SEND_SHUTDOWN; if (!sk->dead) sk->state_change(sk); - sk->dead = 1; + sk->dead = 1; } else { skb->h.raw = skb->data; diff -Nru a/net/rose/rose_subr.c b/net/rose/rose_subr.c --- a/net/rose/rose_subr.c Tue Feb 19 18:08:59 2002 +++ b/net/rose/rose_subr.c Tue Feb 19 18:08:59 2002 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,7 @@ void rose_clear_queues(struct sock *sk) { skb_queue_purge(&sk->write_queue); - skb_queue_purge(&sk->protinfo.rose->ack_queue); + skb_queue_purge(&rose_sk(sk)->ack_queue); } /* @@ -53,15 +54,16 @@ void rose_frames_acked(struct sock *sk, unsigned short nr) { struct sk_buff *skb; + rose_cb *rose = rose_sk(sk); /* * Remove all the ack-ed frames from the ack queue. */ - if (sk->protinfo.rose->va != nr) { - while (skb_peek(&sk->protinfo.rose->ack_queue) != NULL && sk->protinfo.rose->va != nr) { - skb = skb_dequeue(&sk->protinfo.rose->ack_queue); + if (rose->va != nr) { + while (skb_peek(&rose->ack_queue) != NULL && rose->va != nr) { + skb = skb_dequeue(&rose->ack_queue); kfree_skb(skb); - sk->protinfo.rose->va = (sk->protinfo.rose->va + 1) % ROSE_MODULUS; + rose->va = (rose->va + 1) % ROSE_MODULUS; } } } @@ -75,7 +77,7 @@ * up by rose_kick. This arrangement handles the possibility of an * empty output queue. */ - while ((skb = skb_dequeue(&sk->protinfo.rose->ack_queue)) != NULL) { + while ((skb = skb_dequeue(&rose_sk(sk)->ack_queue)) != NULL) { if (skb_prev == NULL) skb_queue_head(&sk->write_queue, skb); else @@ -90,16 +92,15 @@ */ int rose_validate_nr(struct sock *sk, unsigned short nr) { - unsigned short vc = sk->protinfo.rose->va; + rose_cb *rose = rose_sk(sk); + unsigned short vc = rose->va; - while (vc != sk->protinfo.rose->vs) { + while (vc != rose->vs) { if (nr == vc) return 1; vc = (vc + 1) % ROSE_MODULUS; } - if (nr == sk->protinfo.rose->vs) return 1; - - return 0; + return nr == rose->vs; } /* @@ -108,6 +109,7 @@ */ void rose_write_internal(struct sock *sk, int frametype) { + rose_cb *rose = rose_sk(sk); struct sk_buff *skb; unsigned char *dptr; unsigned char lci1, lci2; @@ -119,7 +121,7 @@ switch (frametype) { case ROSE_CALL_REQUEST: len += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN; - faclen = rose_create_facilities(buffer, sk->protinfo.rose); + faclen = rose_create_facilities(buffer, rose); len += faclen; break; case ROSE_CALL_ACCEPTED: @@ -139,8 +141,8 @@ dptr = skb_put(skb, skb_tailroom(skb)); - lci1 = (sk->protinfo.rose->lci >> 8) & 0x0F; - lci2 = (sk->protinfo.rose->lci >> 0) & 0xFF; + lci1 = (rose->lci >> 8) & 0x0F; + lci2 = (rose->lci >> 0) & 0xFF; switch (frametype) { @@ -149,9 +151,9 @@ *dptr++ = lci2; *dptr++ = frametype; *dptr++ = 0xAA; - memcpy(dptr, &sk->protinfo.rose->dest_addr, ROSE_ADDR_LEN); + memcpy(dptr, &rose->dest_addr, ROSE_ADDR_LEN); dptr += ROSE_ADDR_LEN; - memcpy(dptr, &sk->protinfo.rose->source_addr, ROSE_ADDR_LEN); + memcpy(dptr, &rose->source_addr, ROSE_ADDR_LEN); dptr += ROSE_ADDR_LEN; memcpy(dptr, buffer, faclen); dptr += faclen; @@ -169,8 +171,8 @@ *dptr++ = ROSE_GFI | lci1; *dptr++ = lci2; *dptr++ = frametype; - *dptr++ = sk->protinfo.rose->cause; - *dptr++ = sk->protinfo.rose->diagnostic; + *dptr++ = rose->cause; + *dptr++ = rose->diagnostic; break; case ROSE_RESET_REQUEST: @@ -186,7 +188,7 @@ *dptr++ = ROSE_GFI | lci1; *dptr++ = lci2; *dptr = frametype; - *dptr++ |= (sk->protinfo.rose->vr << 5) & 0xE0; + *dptr++ |= (rose->vr << 5) & 0xE0; break; case ROSE_CLEAR_CONFIRMATION: @@ -202,7 +204,7 @@ return; } - rose_transmit_link(skb, sk->protinfo.rose->neighbour); + rose_transmit_link(skb, rose->neighbour); } int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m) @@ -497,19 +499,21 @@ void rose_disconnect(struct sock *sk, int reason, int cause, int diagnostic) { + rose_cb *rose = rose_sk(sk); + rose_stop_timer(sk); rose_stop_idletimer(sk); rose_clear_queues(sk); - sk->protinfo.rose->lci = 0; - sk->protinfo.rose->state = ROSE_STATE_0; + rose->lci = 0; + rose->state = ROSE_STATE_0; if (cause != -1) - sk->protinfo.rose->cause = cause; + rose->cause = cause; if (diagnostic != -1) - sk->protinfo.rose->diagnostic = diagnostic; + rose->diagnostic = diagnostic; sk->state = TCP_CLOSE; sk->err = reason; diff -Nru a/net/rose/rose_timer.c b/net/rose/rose_timer.c --- a/net/rose/rose_timer.c Tue Feb 19 18:08:57 2002 +++ b/net/rose/rose_timer.c Tue Feb 19 18:08:57 2002 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -53,58 +54,68 @@ void rose_start_t1timer(struct sock *sk) { - del_timer(&sk->protinfo.rose->timer); + rose_cb *rose = rose_sk(sk); - sk->protinfo.rose->timer.data = (unsigned long)sk; - sk->protinfo.rose->timer.function = &rose_timer_expiry; - sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t1; + del_timer(&rose->timer); - add_timer(&sk->protinfo.rose->timer); + rose->timer.data = (unsigned long)sk; + rose->timer.function = &rose_timer_expiry; + rose->timer.expires = jiffies + rose->t1; + + add_timer(&rose->timer); } void rose_start_t2timer(struct sock *sk) { - del_timer(&sk->protinfo.rose->timer); + rose_cb *rose = rose_sk(sk); + + del_timer(&rose->timer); - sk->protinfo.rose->timer.data = (unsigned long)sk; - sk->protinfo.rose->timer.function = &rose_timer_expiry; - sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t2; + rose->timer.data = (unsigned long)sk; + rose->timer.function = &rose_timer_expiry; + rose->timer.expires = jiffies + rose->t2; - add_timer(&sk->protinfo.rose->timer); + add_timer(&rose->timer); } void rose_start_t3timer(struct sock *sk) { - del_timer(&sk->protinfo.rose->timer); + rose_cb *rose = rose_sk(sk); + + del_timer(&rose->timer); - sk->protinfo.rose->timer.data = (unsigned long)sk; - sk->protinfo.rose->timer.function = &rose_timer_expiry; - sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t3; + rose->timer.data = (unsigned long)sk; + rose->timer.function = &rose_timer_expiry; + rose->timer.expires = jiffies + rose->t3; - add_timer(&sk->protinfo.rose->timer); + add_timer(&rose->timer); } void rose_start_hbtimer(struct sock *sk) { - del_timer(&sk->protinfo.rose->timer); + rose_cb *rose = rose_sk(sk); - sk->protinfo.rose->timer.data = (unsigned long)sk; - sk->protinfo.rose->timer.function = &rose_timer_expiry; - sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->hb; + del_timer(&rose->timer); - add_timer(&sk->protinfo.rose->timer); + rose->timer.data = (unsigned long)sk; + rose->timer.function = &rose_timer_expiry; + rose->timer.expires = jiffies + rose->hb; + + add_timer(&rose->timer); } void rose_start_idletimer(struct sock *sk) { - del_timer(&sk->protinfo.rose->idletimer); + rose_cb *rose = rose_sk(sk); + + del_timer(&rose->idletimer); - if (sk->protinfo.rose->idle > 0) { - sk->protinfo.rose->idletimer.data = (unsigned long)sk; - sk->protinfo.rose->idletimer.function = &rose_idletimer_expiry; - sk->protinfo.rose->idletimer.expires = jiffies + sk->protinfo.rose->idle; + if (rose->idle > 0) { + rose->idletimer.data = (unsigned long)sk; + rose->idletimer.function = &rose_idletimer_expiry; + rose->idletimer.expires = jiffies + rose->idle; - add_timer(&sk->protinfo.rose->idletimer); + add_timer(&rose->idletimer); } } @@ -115,19 +126,20 @@ void rose_stop_timer(struct sock *sk) { - del_timer(&sk->protinfo.rose->timer); + del_timer(&rose_sk(sk)->timer); } void rose_stop_idletimer(struct sock *sk) { - del_timer(&sk->protinfo.rose->idletimer); + del_timer(&rose_sk(sk)->idletimer); } static void rose_heartbeat_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; + rose_cb *rose = rose_sk(sk); - switch (sk->protinfo.rose->state) { + switch (rose->state) { case ROSE_STATE_0: /* Magic here: If we listen() and a new link dies before it @@ -143,10 +155,10 @@ * Check for the state of the receive buffer. */ if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) && - (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)) { - sk->protinfo.rose->condition &= ~ROSE_COND_OWN_RX_BUSY; - sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; - sk->protinfo.rose->vl = sk->protinfo.rose->vr; + (rose->condition & ROSE_COND_OWN_RX_BUSY)) { + rose->condition &= ~ROSE_COND_OWN_RX_BUSY; + rose->condition &= ~ROSE_COND_ACK_PENDING; + rose->vl = rose->vr; rose_write_internal(sk, ROSE_RR); rose_stop_timer(sk); /* HB */ break; @@ -160,24 +172,25 @@ static void rose_timer_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; + rose_cb *rose = rose_sk(sk); - switch (sk->protinfo.rose->state) { + switch (rose->state) { case ROSE_STATE_1: /* T1 */ case ROSE_STATE_4: /* T2 */ rose_write_internal(sk, ROSE_CLEAR_REQUEST); - sk->protinfo.rose->state = ROSE_STATE_2; + rose->state = ROSE_STATE_2; rose_start_t3timer(sk); break; case ROSE_STATE_2: /* T3 */ - sk->protinfo.rose->neighbour->use--; + rose->neighbour->use--; rose_disconnect(sk, ETIMEDOUT, -1, -1); break; case ROSE_STATE_3: /* HB */ - if (sk->protinfo.rose->condition & ROSE_COND_ACK_PENDING) { - sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; + if (rose->condition & ROSE_COND_ACK_PENDING) { + rose->condition &= ~ROSE_COND_ACK_PENDING; rose_enquiry_response(sk); } break; @@ -191,7 +204,7 @@ rose_clear_queues(sk); rose_write_internal(sk, ROSE_CLEAR_REQUEST); - sk->protinfo.rose->state = ROSE_STATE_2; + rose_sk(sk)->state = ROSE_STATE_2; rose_start_t3timer(sk); diff -Nru a/net/socket.c b/net/socket.c --- a/net/socket.c Tue Feb 19 18:08:59 2002 +++ b/net/socket.c Tue Feb 19 18:08:59 2002 @@ -280,6 +280,15 @@ if (!ei) return NULL; init_waitqueue_head(&ei->socket.wait); + + ei->socket.fasync_list = NULL; + ei->socket.state = SS_UNCONNECTED; + ei->socket.flags = 0; + ei->socket.ops = NULL; + ei->socket.sk = NULL; + ei->socket.file = NULL; + ei->socket.passcred = 0; + return &ei->vfs_inode; } @@ -494,13 +503,6 @@ inode->i_sock = 1; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; - - sock->fasync_list = NULL; - sock->state = SS_UNCONNECTED; - sock->flags = 0; - sock->ops = NULL; - sock->sk = NULL; - sock->file = NULL; sockets_in_use[smp_processor_id()].counter++; return sock; diff -Nru a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c --- a/net/sunrpc/clnt.c Tue Feb 19 18:08:59 2002 +++ b/net/sunrpc/clnt.c Tue Feb 19 18:08:59 2002 @@ -221,7 +221,7 @@ spin_lock_irqsave(¤t->sigmask_lock, irqflags); *oldset = current->blocked; siginitsetinv(¤t->blocked, sigallow & ~oldset->sig[0]); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, irqflags); } @@ -231,7 +231,7 @@ spin_lock_irqsave(¤t->sigmask_lock, irqflags); current->blocked = *oldset; - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, irqflags); } diff -Nru a/net/sunrpc/sched.c b/net/sunrpc/sched.c --- a/net/sunrpc/sched.c Tue Feb 19 18:08:57 2002 +++ b/net/sunrpc/sched.c Tue Feb 19 18:08:57 2002 @@ -1063,7 +1063,7 @@ spin_lock_irq(¤t->sigmask_lock); siginitsetinv(¤t->blocked, sigmask(SIGKILL)); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irq(¤t->sigmask_lock); strcpy(current->comm, "rpciod"); @@ -1119,7 +1119,7 @@ } spin_lock_irqsave(¤t->sigmask_lock, flags); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } @@ -1197,7 +1197,7 @@ interruptible_sleep_on(&rpciod_killer); } spin_lock_irqsave(¤t->sigmask_lock, flags); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); out: up(&rpciod_sema); diff -Nru a/net/sunrpc/svc.c b/net/sunrpc/svc.c --- a/net/sunrpc/svc.c Tue Feb 19 18:08:59 2002 +++ b/net/sunrpc/svc.c Tue Feb 19 18:08:59 2002 @@ -201,7 +201,7 @@ if (!port) { spin_lock_irqsave(¤t->sigmask_lock, flags); - recalc_sigpending(current); + recalc_sigpending(); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } diff -Nru a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c --- a/net/sunrpc/svcsock.c Tue Feb 19 18:08:57 2002 +++ b/net/sunrpc/svcsock.c Tue Feb 19 18:08:57 2002 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff -Nru a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c --- a/net/sunrpc/xprt.c Tue Feb 19 18:08:58 2002 +++ b/net/sunrpc/xprt.c Tue Feb 19 18:08:58 2002 @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include diff -Nru a/net/unix/af_unix.c b/net/unix/af_unix.c --- a/net/unix/af_unix.c Tue Feb 19 18:08:57 2002 +++ b/net/unix/af_unix.c Tue Feb 19 18:08:57 2002 @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.131 2002/01/14 07:08:27 davem Exp $ + * Version: $Id: af_unix.c,v 1.133 2002/02/08 03:57:19 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. @@ -109,18 +109,19 @@ #include #include #include - -#include +#include int sysctl_unix_max_dgram_qlen = 10; +kmem_cache_t *unix_sk_cachep; + unix_socket *unix_socket_table[UNIX_HASH_SIZE+1]; rwlock_t unix_table_lock = RW_LOCK_UNLOCKED; static atomic_t unix_nr_socks = ATOMIC_INIT(0); #define unix_sockets_unbound (unix_socket_table[UNIX_HASH_SIZE]) -#define UNIX_ABSTRACT(sk) ((sk)->protinfo.af_unix.addr->hash!=UNIX_HASH_SIZE) +#define UNIX_ABSTRACT(sk) (unix_sk(sk)->addr->hash != UNIX_HASH_SIZE) /* * SMP locking strategy: @@ -201,7 +202,9 @@ static void __unix_remove_socket(unix_socket *sk) { - unix_socket **list = sk->protinfo.af_unix.list; + struct unix_sock *u = unix_sk(sk); + unix_socket **list = u->list; + if (list) { if (sk->next) sk->next->prev = sk->prev; @@ -209,7 +212,7 @@ sk->prev->next = sk->next; if (*list == sk) *list = sk->next; - sk->protinfo.af_unix.list = NULL; + u->list = NULL; sk->prev = NULL; sk->next = NULL; __sock_put(sk); @@ -218,9 +221,10 @@ static void __unix_insert_socket(unix_socket **list, unix_socket *sk) { - BUG_TRAP(sk->protinfo.af_unix.list==NULL); + struct unix_sock *u = unix_sk(sk); + BUG_TRAP(!u->list); - sk->protinfo.af_unix.list = list; + u->list = list; sk->prev = NULL; sk->next = *list; if (*list) @@ -249,11 +253,13 @@ unix_socket *s; for (s=unix_socket_table[hash^type]; s; s=s->next) { - if(s->protinfo.af_unix.addr->len==len && - memcmp(s->protinfo.af_unix.addr->name, sunname, len) == 0) - return s; + struct unix_sock *u = unix_sk(s); + + if (u->addr->len == len && + !memcmp(u->addr->name, sunname, len)) + break; } - return NULL; + return s; } static inline unix_socket * @@ -277,7 +283,7 @@ read_lock(&unix_table_lock); for (s=unix_socket_table[i->i_ino & (UNIX_HASH_SIZE-1)]; s; s=s->next) { - struct dentry *dentry = s->protinfo.af_unix.dentry; + struct dentry *dentry = unix_sk(s)->dentry; if(dentry && dentry->d_inode == i) { @@ -313,7 +319,7 @@ { if (skb_queue_len(&sk->receive_queue)) { skb_queue_purge(&sk->receive_queue); - wake_up_interruptible_all(&sk->protinfo.af_unix.peer_wait); + wake_up_interruptible_all(&unix_sk(sk)->peer_wait); /* If one link of bidirectional dgram pipe is disconnected, * we signal error. Messages are lost. Do not make this, @@ -328,18 +334,20 @@ static void unix_sock_destructor(struct sock *sk) { + struct unix_sock *u = unix_sk(sk); + skb_queue_purge(&sk->receive_queue); BUG_TRAP(atomic_read(&sk->wmem_alloc) == 0); - BUG_TRAP(sk->protinfo.af_unix.list==NULL); + BUG_TRAP(!u->list); BUG_TRAP(sk->socket==NULL); if (sk->dead==0) { printk("Attempt to release alive unix socket: %p\n", sk); return; } - if (sk->protinfo.af_unix.addr) - unix_release_addr(sk->protinfo.af_unix.addr); + if (u->addr) + unix_release_addr(u->addr); atomic_dec(&unix_nr_socks); #ifdef UNIX_REFCNT_DEBUG @@ -350,6 +358,7 @@ static int unix_release_sock (unix_socket *sk, int embrion) { + struct unix_sock *u = unix_sk(sk); struct dentry *dentry; struct vfsmount *mnt; unix_socket *skpair; @@ -362,15 +371,15 @@ unix_state_wlock(sk); sock_orphan(sk); sk->shutdown = SHUTDOWN_MASK; - dentry = sk->protinfo.af_unix.dentry; - sk->protinfo.af_unix.dentry=NULL; - mnt = sk->protinfo.af_unix.mnt; - sk->protinfo.af_unix.mnt=NULL; + dentry = u->dentry; + u->dentry = NULL; + mnt = u->mnt; + u->mnt = NULL; state = sk->state; sk->state = TCP_CLOSE; unix_state_wunlock(sk); - wake_up_interruptible_all(&sk->protinfo.af_unix.peer_wait); + wake_up_interruptible_all(&u->peer_wait); skpair=unix_peer(sk); @@ -430,18 +439,19 @@ { int err; struct sock *sk = sock->sk; + struct unix_sock *u = unix_sk(sk); err = -EOPNOTSUPP; if (sock->type!=SOCK_STREAM) goto out; /* Only stream sockets accept */ err = -EINVAL; - if (!sk->protinfo.af_unix.addr) + if (!u->addr) goto out; /* No listens on an unbound socket */ unix_state_wlock(sk); if (sk->state != TCP_CLOSE && sk->state != TCP_LISTEN) goto out_unlock; if (backlog > sk->max_ack_backlog) - wake_up_interruptible_all(&sk->protinfo.af_unix.peer_wait); + wake_up_interruptible_all(&u->peer_wait); sk->max_ack_backlog=backlog; sk->state=TCP_LISTEN; /* set credentials so connect can copy them */ @@ -462,12 +472,14 @@ static struct sock * unix_create1(struct socket *sock) { struct sock *sk; + struct unix_sock *u; if (atomic_read(&unix_nr_socks) >= 2*files_stat.max_files) return NULL; MOD_INC_USE_COUNT; - sk = sk_alloc(PF_UNIX, GFP_KERNEL, 1); + sk = sk_alloc(PF_UNIX, GFP_KERNEL, sizeof(struct unix_sock), + unix_sk_cachep); if (!sk) { MOD_DEC_USE_COUNT; return NULL; @@ -481,13 +493,14 @@ sk->max_ack_backlog = sysctl_unix_max_dgram_qlen; sk->destruct = unix_sock_destructor; - sk->protinfo.af_unix.dentry=NULL; - sk->protinfo.af_unix.mnt=NULL; - sk->protinfo.af_unix.lock = RW_LOCK_UNLOCKED; - atomic_set(&sk->protinfo.af_unix.inflight, sock ? 0 : -1); - init_MUTEX(&sk->protinfo.af_unix.readsem);/* single task reading lock */ - init_waitqueue_head(&sk->protinfo.af_unix.peer_wait); - sk->protinfo.af_unix.list=NULL; + u = unix_sk(sk); + u->dentry = NULL; + u->mnt = NULL; + u->list = NULL; + rwlock_init(&u->lock); + atomic_set(&u->inflight, sock ? 0 : -1); + init_MUTEX(&u->readsem); /* single task reading lock */ + init_waitqueue_head(&u->peer_wait); unix_insert_socket(&unix_sockets_unbound, sk); return sk; @@ -535,14 +548,15 @@ static int unix_autobind(struct socket *sock) { struct sock *sk = sock->sk; + struct unix_sock *u = unix_sk(sk); static u32 ordernum = 1; struct unix_address * addr; int err; - down(&sk->protinfo.af_unix.readsem); + down(&u->readsem); err = 0; - if (sk->protinfo.af_unix.addr) + if (u->addr) goto out; err = -ENOMEM; @@ -572,13 +586,12 @@ addr->hash ^= sk->type; __unix_remove_socket(sk); - sk->protinfo.af_unix.addr = addr; + u->addr = addr; __unix_insert_socket(&unix_socket_table[addr->hash], sk); write_unlock(&unix_table_lock); err = 0; -out: - up(&sk->protinfo.af_unix.readsem); +out: up(&u->readsem); return err; } @@ -632,6 +645,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; + struct unix_sock *u = unix_sk(sk); struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr; struct dentry * dentry = NULL; struct nameidata nd; @@ -654,10 +668,10 @@ goto out; addr_len = err; - down(&sk->protinfo.af_unix.readsem); + down(&u->readsem); err = -EINVAL; - if (sk->protinfo.af_unix.addr) + if (u->addr) goto out_up; err = -ENOMEM; @@ -735,19 +749,19 @@ list = &unix_socket_table[addr->hash]; } else { list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)]; - sk->protinfo.af_unix.dentry = nd.dentry; - sk->protinfo.af_unix.mnt = nd.mnt; + u->dentry = nd.dentry; + u->mnt = nd.mnt; } err = 0; __unix_remove_socket(sk); - sk->protinfo.af_unix.addr = addr; + u->addr = addr; __unix_insert_socket(list, sk); out_unlock: write_unlock(&unix_table_lock); out_up: - up(&sk->protinfo.af_unix.readsem); + up(&u->readsem); out: return err; @@ -779,7 +793,7 @@ goto out; alen = err; - if (sock->passcred && !sk->protinfo.af_unix.addr && + if (sock->passcred && !unix_sk(sk)->addr && (err = unix_autobind(sock)) != 0) goto out; @@ -826,11 +840,12 @@ static long unix_wait_for_peer(unix_socket *other, long timeo) { + struct unix_sock *u = unix_sk(other); int sched; DECLARE_WAITQUEUE(wait, current); __set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue_exclusive(&other->protinfo.af_unix.peer_wait, &wait); + add_wait_queue_exclusive(&u->peer_wait, &wait); sched = (!other->dead && !(other->shutdown&RCV_SHUTDOWN) && @@ -842,7 +857,7 @@ timeo = schedule_timeout(timeo); __set_current_state(TASK_RUNNING); - remove_wait_queue(&other->protinfo.af_unix.peer_wait, &wait); + remove_wait_queue(&u->peer_wait, &wait); return timeo; } @@ -851,6 +866,7 @@ { struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr; struct sock *sk = sock->sk; + struct unix_sock *u = unix_sk(sk), *newu, *otheru; struct sock *newsk = NULL; unix_socket *other = NULL; struct sk_buff *skb = NULL; @@ -864,8 +880,7 @@ goto out; addr_len = err; - if (sock->passcred && !sk->protinfo.af_unix.addr && - (err = unix_autobind(sock)) != 0) + if (sock->passcred && !u->addr && (err = unix_autobind(sock)) != 0) goto out; timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); @@ -965,17 +980,18 @@ newsk->peercred.pid = current->pid; newsk->peercred.uid = current->euid; newsk->peercred.gid = current->egid; - newsk->sleep = &newsk->protinfo.af_unix.peer_wait; + newu = unix_sk(newsk); + newsk->sleep = &newu->peer_wait; + otheru = unix_sk(other); /* copy address information from listening to new sock*/ - if (other->protinfo.af_unix.addr) - { - atomic_inc(&other->protinfo.af_unix.addr->refcnt); - newsk->protinfo.af_unix.addr=other->protinfo.af_unix.addr; - } - if (other->protinfo.af_unix.dentry) { - newsk->protinfo.af_unix.dentry=dget(other->protinfo.af_unix.dentry); - newsk->protinfo.af_unix.mnt=mntget(other->protinfo.af_unix.mnt); + if (otheru->addr) { + atomic_inc(&otheru->addr->refcnt); + newu->addr = otheru->addr; + } + if (otheru->dentry) { + newu->dentry = dget(otheru->dentry); + newu->mnt = mntget(otheru->mnt); } /* Set credentials */ @@ -993,7 +1009,7 @@ __skb_queue_tail(&other->receive_queue,skb); /* Undo artificially decreased inflight after embrion * is installed to listening socket. */ - atomic_inc(&newsk->protinfo.af_unix.inflight); + atomic_inc(&newu->inflight); spin_unlock(&other->receive_queue.lock); unix_state_runlock(other); other->data_ready(other, 0); @@ -1066,7 +1082,7 @@ tsk = skb->sk; skb_free_datagram(sk, skb); - wake_up_interruptible(&sk->protinfo.af_unix.peer_wait); + wake_up_interruptible(&unix_sk(sk)->peer_wait); /* attach accepted sock to socket */ unix_state_wlock(tsk); @@ -1083,6 +1099,7 @@ static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { struct sock *sk = sock->sk; + struct unix_sock *u = unix_sk(sk); struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr; int err = 0; @@ -1098,12 +1115,12 @@ } unix_state_rlock(sk); - if (!sk->protinfo.af_unix.addr) { + if (!u->addr) { sunaddr->sun_family = AF_UNIX; sunaddr->sun_path[0] = 0; *uaddr_len = sizeof(short); } else { - struct unix_address *addr = sk->protinfo.af_unix.addr; + struct unix_address *addr = u->addr; *uaddr_len = addr->len; memcpy(sunaddr, addr->name, *uaddr_len); @@ -1156,6 +1173,7 @@ struct scm_cookie *scm) { struct sock *sk = sock->sk; + struct unix_sock *u = unix_sk(sk); struct sockaddr_un *sunaddr=msg->msg_name; unix_socket *other = NULL; int namelen = 0; /* fake GCC */ @@ -1181,8 +1199,7 @@ goto out; } - if (sock->passcred && !sk->protinfo.af_unix.addr && - (err = unix_autobind(sock)) != 0) + if (sock->passcred && !u->addr && (err = unix_autobind(sock)) != 0) goto out; err = -EMSGSIZE; @@ -1383,12 +1400,12 @@ static void unix_copy_addr(struct msghdr *msg, struct sock *sk) { + struct unix_sock *u = unix_sk(sk); + msg->msg_namelen = sizeof(short); - if (sk->protinfo.af_unix.addr) { - msg->msg_namelen=sk->protinfo.af_unix.addr->len; - memcpy(msg->msg_name, - sk->protinfo.af_unix.addr->name, - sk->protinfo.af_unix.addr->len); + if (u->addr) { + msg->msg_namelen = u->addr->len; + memcpy(msg->msg_name, u->addr->name, u->addr->len); } } @@ -1396,6 +1413,7 @@ int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; + struct unix_sock *u = unix_sk(sk); int noblock = flags & MSG_DONTWAIT; struct sk_buff *skb; int err; @@ -1410,7 +1428,7 @@ if (!skb) goto out; - wake_up_interruptible(&sk->protinfo.af_unix.peer_wait); + wake_up_interruptible(&u->peer_wait); if (msg->msg_name) unix_copy_addr(msg, skb->sk); @@ -1497,6 +1515,7 @@ int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; + struct unix_sock *u = unix_sk(sk); struct sockaddr_un *sunaddr=msg->msg_name; int copied = 0; int check_creds = 0; @@ -1521,7 +1540,7 @@ * while sleeps in memcpy_tomsg */ - down(&sk->protinfo.af_unix.readsem); + down(&u->readsem); do { @@ -1545,7 +1564,7 @@ err = -EAGAIN; if (!timeo) break; - up(&sk->protinfo.af_unix.readsem); + up(&u->readsem); timeo = unix_stream_data_wait(sk, timeo); @@ -1553,7 +1572,7 @@ err = sock_intr_errno(timeo); goto out; } - down(&sk->protinfo.af_unix.readsem); + down(&u->readsem); continue; } @@ -1619,7 +1638,7 @@ } } while (size); - up(&sk->protinfo.af_unix.readsem); + up(&u->readsem); out: return copied ? : err; } @@ -1748,6 +1767,7 @@ read_lock(&unix_table_lock); forall_unix_sockets (i,s) { + struct unix_sock *u = unix_sk(s); unix_state_rlock(s); len+=sprintf(buffer+len,"%p: %08X %08X %08X %04X %02X %5ld", @@ -1761,16 +1781,15 @@ (s->state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING), sock_i_ino(s)); - if (s->protinfo.af_unix.addr) - { + if (u->addr) { buffer[len++] = ' '; - memcpy(buffer+len, s->protinfo.af_unix.addr->name->sun_path, - s->protinfo.af_unix.addr->len-sizeof(short)); + memcpy(buffer+len, u->addr->name->sun_path, + u->addr->len-sizeof(short)); if (!UNIX_ABSTRACT(s)) len--; else buffer[len] = '@'; - len += s->protinfo.af_unix.addr->len - sizeof(short); + len += u->addr->len - sizeof(short); } unix_state_runlock(s); @@ -1841,8 +1860,8 @@ }; struct net_proto_family unix_family_ops = { - family: PF_UNIX, - create: unix_create + family: PF_UNIX, + create: unix_create, }; #ifdef CONFIG_SYSCTL @@ -1865,6 +1884,14 @@ printk(KERN_CRIT "unix_proto_init: panic\n"); return -1; } + /* allocate our sock slab cache */ + unix_sk_cachep = kmem_cache_create("unix_sock", + sizeof(struct unix_sock), 0, + SLAB_HWCACHE_ALIGN, 0, 0); + if (!unix_sk_cachep) + printk(KERN_CRIT __FUNCTION__ + ": Cannot create unix_sock SLAB cache!\n"); + sock_register(&unix_family_ops); #ifdef CONFIG_PROC_FS create_proc_read_entry("net/unix", 0, 0, unix_read_proc, NULL); @@ -1878,6 +1905,7 @@ sock_unregister(PF_UNIX); unix_sysctl_unregister(); remove_proc_entry("net/unix", 0); + kmem_cache_destroy(unix_sk_cachep); } module_init(af_unix_init); diff -Nru a/net/unix/garbage.c b/net/unix/garbage.c --- a/net/unix/garbage.c Tue Feb 19 18:08:58 2002 +++ b/net/unix/garbage.c Tue Feb 19 18:08:58 2002 @@ -122,7 +122,7 @@ { unix_socket *s=unix_get_socket(fp); if(s) { - atomic_inc(&s->protinfo.af_unix.inflight); + atomic_inc(&unix_sk(s)->inflight); atomic_inc(&unix_tot_inflight); } } @@ -131,7 +131,7 @@ { unix_socket *s=unix_get_socket(fp); if(s) { - atomic_dec(&s->protinfo.af_unix.inflight); + atomic_dec(&unix_sk(s)->inflight); atomic_dec(&unix_tot_inflight); } } @@ -144,7 +144,7 @@ extern inline unix_socket *pop_stack(void) { unix_socket *p=gc_current; - gc_current = p->protinfo.af_unix.gc_tree; + gc_current = unix_sk(p)->gc_tree; return p; } @@ -155,10 +155,12 @@ extern inline void maybe_unmark_and_push(unix_socket *x) { - if (x->protinfo.af_unix.gc_tree != GC_ORPHAN) + struct unix_sock *u = unix_sk(x); + + if (u->gc_tree != GC_ORPHAN) return; sock_hold(x); - x->protinfo.af_unix.gc_tree = gc_current; + u->gc_tree = gc_current; gc_current = x; } @@ -184,7 +186,7 @@ forall_unix_sockets(i, s) { - s->protinfo.af_unix.gc_tree=GC_ORPHAN; + unix_sk(s)->gc_tree = GC_ORPHAN; } /* * Everything is now marked @@ -219,7 +221,7 @@ */ if(s->socket && s->socket->file) open_count = file_count(s->socket->file); - if (open_count > atomic_read(&s->protinfo.af_unix.inflight)) + if (open_count > atomic_read(&unix_sk(s)->inflight)) maybe_unmark_and_push(s); } @@ -277,8 +279,9 @@ forall_unix_sockets(i, s) { - if (s->protinfo.af_unix.gc_tree == GC_ORPHAN) - { + struct unix_sock *u = unix_sk(s); + + if (u->gc_tree == GC_ORPHAN) { struct sk_buff *nextsk; spin_lock(&s->receive_queue.lock); skb=skb_peek(&s->receive_queue); @@ -297,7 +300,7 @@ } spin_unlock(&s->receive_queue.lock); } - s->protinfo.af_unix.gc_tree = GC_ORPHAN; + u->gc_tree = GC_ORPHAN; } read_unlock(&unix_table_lock); diff -Nru a/net/wanrouter/af_wanpipe.c b/net/wanrouter/af_wanpipe.c --- a/net/wanrouter/af_wanpipe.c Tue Feb 19 18:08:59 2002 +++ b/net/wanrouter/af_wanpipe.c Tue Feb 19 18:08:59 2002 @@ -326,7 +326,7 @@ static int wanpipe_listen_rcv (struct sk_buff *skb, struct sock *sk) { - + wanpipe_opt *wp = wp_sk(sk), *newwp; struct wan_sockaddr_ll *sll = (struct wan_sockaddr_ll*)skb->cb; struct sock *newsk; netdevice_t *dev; @@ -337,7 +337,7 @@ /* Find a free device, if none found, all svc's are busy */ - card = (sdla_t*)sk->protinfo.af_wanpipe->card; + card = (sdla_t*)wp->card; if (!card){ printk(KERN_INFO "wansock: LISTEN ERROR, No Card\n"); return -ENODEV; @@ -364,7 +364,8 @@ /* Initialize the new sock structure */ newsk->bound_dev_if = dev->ifindex; - newsk->protinfo.af_wanpipe->card = sk->protinfo.af_wanpipe->card; + newwp = wp_sk(newsk); + newwp->card = wp->card; /* Insert the sock into the main wanpipe * sock list. @@ -389,8 +390,8 @@ * whic lcn to clear */ - newsk->protinfo.af_wanpipe->lcn = mbox_ptr->cmd.lcn; - newsk->protinfo.af_wanpipe->mbox = (void *)mbox_ptr; + newwp->lcn = mbox_ptr->cmd.lcn; + newwp->mbox = (void *)mbox_ptr; DBG_PRINTK(KERN_INFO "NEWSOCK : Device %s, bind to lcn %i\n", dev->name,mbox_ptr->cmd.lcn); @@ -497,7 +498,7 @@ struct sock *sk; struct wanpipe_opt *wan_opt; - if ((sk = sk_alloc(PF_WANPIPE, GFP_ATOMIC, 1)) == NULL) + if ((sk = sk_alloc(PF_WANPIPE, GFP_ATOMIC, 1, NULL)) == NULL) return NULL; if ((wan_opt = kmalloc(sizeof(struct wanpipe_opt), GFP_ATOMIC)) == NULL) { @@ -506,13 +507,12 @@ } memset(wan_opt, 0x00, sizeof(struct wanpipe_opt)); - sk->protinfo.af_wanpipe = wan_opt; - sk->protinfo.destruct_hook = wan_opt; + wp_sk(sk) = wan_opt; /* Use timer to send data to the driver. This will act * as a BH handler for sendmsg functions */ - sk->protinfo.af_wanpipe->tx_timer.data=(unsigned long)sk; - sk->protinfo.af_wanpipe->tx_timer.function=wanpipe_delayed_transmit; + wan_opt->tx_timer.data = (unsigned long)sk; + wan_opt->tx_timer.function = wanpipe_delayed_transmit; MOD_INC_USE_COUNT; @@ -542,6 +542,7 @@ static int wanpipe_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { + wanpipe_opt *wp; struct sock *sk = sock->sk; struct wan_sockaddr_ll *saddr=(struct wan_sockaddr_ll *)msg->msg_name; struct sk_buff *skb; @@ -647,12 +648,13 @@ } skb_queue_tail(&sk->write_queue,skb); - atomic_inc(&sk->protinfo.af_wanpipe->packet_sent); + wp = wp_sk(sk); + atomic_inc(&wp->packet_sent); - if (!(test_and_set_bit(0,&sk->protinfo.af_wanpipe->timer))){ - del_timer(&sk->protinfo.af_wanpipe->tx_timer); - sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+1; - add_timer(&sk->protinfo.af_wanpipe->tx_timer); + if (!(test_and_set_bit(0, &wp->timer))){ + del_timer(&wp->tx_timer); + wp->tx_timer.expires = jiffies + 1; + add_timer(&wp->tx_timer); } return(len); @@ -683,17 +685,18 @@ { struct sock *sk=(struct sock *)data; struct sk_buff *skb; - netdevice_t *dev = sk->protinfo.af_wanpipe->dev; - sdla_t *card = (sdla_t*)sk->protinfo.af_wanpipe->card; + wanpipe_opt *wp = wp_sk(sk); + netdevice_t *dev = wp->dev; + sdla_t *card = (sdla_t*)wp->card; if (!card || !dev){ - clear_bit (0,&sk->protinfo.af_wanpipe->timer); + clear_bit(0, &wp->timer); DBG_PRINTK(KERN_INFO "wansock: Transmit delay, no dev or card\n"); return; } if (sk->state != WANSOCK_CONNECTED || !sk->zapped){ - clear_bit (0,&sk->protinfo.af_wanpipe->timer); + clear_bit(0, &wp->timer); DBG_PRINTK(KERN_INFO "wansock: Tx Timer, State not CONNECTED\n"); return; } @@ -703,8 +706,8 @@ * pending command will never get a free buffer * to execute */ if (atomic_read(&card->u.x.command_busy)){ - sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+SLOW_BACKOFF; - add_timer(&sk->protinfo.af_wanpipe->tx_timer); + wp->tx_timer.expires = jiffies + SLOW_BACKOFF; + add_timer(&wp->tx_timer); DBG_PRINTK(KERN_INFO "wansock: Tx Timer, command bys BACKOFF\n"); return; } @@ -712,8 +715,8 @@ if (test_and_set_bit(0,&wanpipe_tx_critical)){ printk(KERN_INFO "WanSock: Tx timer critical %s\n",dev->name); - sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+SLOW_BACKOFF; - add_timer(&sk->protinfo.af_wanpipe->tx_timer); + wp->tx_timer.expires = jiffies + SLOW_BACKOFF; + add_timer(&wp->tx_timer); return; } @@ -733,18 +736,18 @@ * if more packets, re-trigger the transmit routine * other wise exit */ - atomic_dec(&sk->protinfo.af_wanpipe->packet_sent); + atomic_dec(&wp->packet_sent); if (skb_peek(&sk->write_queue) == NULL){ /* If there is nothing to send, kick * the poll routine, which will trigger * the application to send more data */ sk->data_ready(sk,0); - clear_bit (0,&sk->protinfo.af_wanpipe->timer); + clear_bit(0, &wp->timer); }else{ /* Reschedule as fast as possible */ - sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+1; - add_timer(&sk->protinfo.af_wanpipe->tx_timer); + wp->tx_timer.expires = jiffies + 1; + add_timer(&wp->tx_timer); } } } @@ -758,7 +761,7 @@ * chan->command is used to indicate to the driver that * command is pending for exection. The acutal command * structure is placed into a sock mbox structure - * (sk->protinfo.af_wanpipe->mbox). + * (wp_sk(sk)->mbox). * * The sock private structure, mbox is * used as shared memory between sock and the driver. @@ -774,6 +777,7 @@ static int execute_command(struct sock *sk, unsigned char cmd, unsigned int flags) { + wanpipe_opt *wp = wp_sk(sk); netdevice_t *dev; wanpipe_common_t *chan=NULL; int err=0; @@ -798,15 +802,14 @@ return -EINVAL; } - if (!sk->protinfo.af_wanpipe->mbox){ + if (!wp->mbox) { printk(KERN_INFO "wansock: In execute without MBOX\n"); return -EINVAL; } - ((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.command=cmd; - ((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.lcn = - sk->protinfo.af_wanpipe->lcn; - ((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.result=0x7F; + ((mbox_cmd_t*)wp->mbox)->cmd.command = cmd; + ((mbox_cmd_t*)wp->mbox)->cmd.lcn = wp->lcn; + ((mbox_cmd_t*)wp->mbox)->cmd.result = 0x7F; if (flags & O_NONBLOCK){ @@ -819,7 +822,7 @@ add_wait_queue(sk->sleep,&wait); current->state = TASK_INTERRUPTIBLE; for (;;){ - if (((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.result != 0x7F) { + if (((mbox_cmd_t*)wp->mbox)->cmd.result != 0x7F) { err = 0; break; } @@ -845,17 +848,16 @@ static void wanpipe_destroy_timer(unsigned long data) { struct sock *sk=(struct sock *)data; + wanpipe_opt *wp = wp_sk(sk); if ((!atomic_read(&sk->wmem_alloc) && !atomic_read(&sk->rmem_alloc)) || - (++sk->protinfo.af_wanpipe->force == 5)) { + (++wp->force == 5)) { if (atomic_read(&sk->wmem_alloc) || atomic_read(&sk->rmem_alloc)) printk(KERN_INFO "wansock: Warning, Packet Discarded due to sock shutdown!\n"); - if (sk->protinfo.af_wanpipe){ - kfree(sk->protinfo.af_wanpipe); - sk->protinfo.af_wanpipe=NULL; - } + kfree(wp); + wp_sk(sk) = NULL; #ifdef LINUX_2_4 if (atomic_read(&sk->refcnt) != 1){ @@ -891,7 +893,7 @@ sk->zapped=0; sk->state = WANSOCK_DISCONNECTED; - sk->protinfo.af_wanpipe->dev = NULL; + wp_sk(sk)->dev = NULL; dev = dev_get_by_index(sk->bound_dev_if); if (!dev){ @@ -928,15 +930,16 @@ static void wanpipe_link_driver (netdevice_t *dev, struct sock *sk) { + wanpipe_opt *wp = wp_sk(sk); wanpipe_common_t *chan = dev->priv; if (!chan) return; set_bit(0,&chan->common_critical); chan->sk=sk; chan->func=wanpipe_rcv; - chan->mbox=sk->protinfo.af_wanpipe->mbox; - chan->tx_timer = &sk->protinfo.af_wanpipe->tx_timer; - sk->protinfo.af_wanpipe->dev=dev; + chan->mbox = wp->mbox; + chan->tx_timer = &wp->tx_timer; + wp->dev = dev; sk->zapped = 1; clear_bit(0,&chan->common_critical); } @@ -974,12 +977,14 @@ #ifndef LINUX_2_4 struct sk_buff *skb; #endif + wanpipe_opt *wp; struct sock *sk = sock->sk; struct sock **skp; if (!sk) return 0; + wp = wp_sk(sk); check_write_queue(sk); /* Kill the tx timer, if we don't kill it now, the timer @@ -987,7 +992,7 @@ * try to access the sock which has been killed and cause * kernel panic */ - del_timer(&sk->protinfo.af_wanpipe->tx_timer); + del_timer(&wp->tx_timer); /* * Unhook packet receive handler. @@ -1060,10 +1065,8 @@ return 0; } - if (sk->protinfo.af_wanpipe){ - kfree(sk->protinfo.af_wanpipe); - sk->protinfo.af_wanpipe=NULL; - } + kfree(wp); + wp_sk(sk) = NULL; #ifdef LINUX_2_4 if (atomic_read(&sk->refcnt) != 1){ @@ -1113,6 +1116,7 @@ static void release_driver(struct sock *sk) { + wanpipe_opt *wp; struct sk_buff *skb=NULL; struct sock *deadsk=NULL; @@ -1134,12 +1138,11 @@ sk->state = WANSOCK_DISCONNECTED; sk->bound_dev_if = 0; sk->zapped=0; + wp = wp_sk(sk); - if (sk->protinfo.af_wanpipe){ - if (sk->protinfo.af_wanpipe->mbox){ - kfree(sk->protinfo.af_wanpipe->mbox); - sk->protinfo.af_wanpipe->mbox=NULL; - } + if (wp && wp->mbox) { + kfree(wp->mbox); + wp->mbox = NULL; } } @@ -1244,9 +1247,9 @@ return; } - if (sk->protinfo.af_wanpipe){ - kfree(sk->protinfo.af_wanpipe); - sk->protinfo.af_wanpipe=NULL; + if (wp_sk(sk)) { + kfree(wp_sk(sk)); + wp_sk(sk) = NULL; } #ifdef LINUX_2_4 @@ -1288,9 +1291,9 @@ sk->socket = NULL; - if (sk->protinfo.af_wanpipe){ - kfree(sk->protinfo.af_wanpipe); - sk->protinfo.af_wanpipe=NULL; + if (wp_sk(sk)) { + kfree(wp_sk(sk)); + wp_sk(sk) = NULL; } #ifdef LINUX_2_4 @@ -1317,9 +1320,9 @@ sk->socket = NULL; - if (sk->protinfo.af_wanpipe){ - kfree(sk->protinfo.af_wanpipe); - sk->protinfo.af_wanpipe=NULL; + if (wp_sk(sk)) { + kfree(wp_sk(sk)); + wp_sk(sk) = NULL; } #ifdef LINUX_2_4 @@ -1386,7 +1389,7 @@ /* X25 Specific option */ if (sk->num == htons(X25_PROT)) - sk->protinfo.af_wanpipe->svc = chan->svc; + wp_sk(sk)->svc = chan->svc; } else { sk->err = ENETDOWN; @@ -1440,7 +1443,7 @@ printk(KERN_INFO "wansock: Wanpipe card not found: %s\n",sll->sll_card); return -ENODEV; }else{ - sk->protinfo.af_wanpipe->card = (void *)card; + wp_sk(sk)->card = (void *)card; } if (!strcmp(sll->sll_device,"svc_listen")){ @@ -1808,7 +1811,7 @@ for (sk = wanpipe_sklist; sk; sk = sk->next) { - if ((po = sk->protinfo.af_wanpipe)==NULL) + if ((po = wp_sk(sk)) == NULL) continue; if (dev == NULL) continue; @@ -2005,6 +2008,7 @@ wan_debug_t *dbg_data = (wan_debug_t *)arg; for (sk = wanpipe_sklist; sk; sk = sk->next){ + wanpipe_opt *wp = wp_sk(sk); if (sk == origsk){ continue; @@ -2024,8 +2028,7 @@ return err; if ((err=put_user(sk_count, &dbg_data->debug[cnt].sk_count))) return err; - if ((err=put_user(sk->protinfo.af_wanpipe->poll_cnt, - &dbg_data->debug[cnt].poll_cnt))) + if ((err=put_user(wp->poll_cnt, &dbg_data->debug[cnt].poll_cnt))) return err; if ((err=put_user(sk->bound_dev_if, &dbg_data->debug[cnt].bound))) return err; @@ -2048,8 +2051,8 @@ return err; - if (sk->protinfo.af_wanpipe){ - sdla_t *card = (sdla_t*)sk->protinfo.af_wanpipe->card; + if (wp){ + sdla_t *card = (sdla_t*)wp->card; if (card){ if ((err=put_user(atomic_read(&card->u.x.command_busy), @@ -2057,11 +2060,11 @@ return err; } - if ((err=put_user(sk->protinfo.af_wanpipe->lcn, - &dbg_data->debug[cnt].lcn))) + if ((err=put_user(wp->lcn, + &dbg_data->debug[cnt].lcn))) return err; - if (sk->protinfo.af_wanpipe->mbox){ + if (wp->mbox) { if ((err=put_user(1, &dbg_data->debug[cnt].mbox))) return err; } @@ -2096,11 +2099,11 @@ if (usr_data == NULL) return -EINVAL; - if (!sk->protinfo.af_wanpipe->mbox){ + if (!wp_sk(sk)->mbox) { return -EINVAL; } - mbox_ptr = (mbox_cmd_t *)sk->protinfo.af_wanpipe->mbox; + mbox_ptr = (mbox_cmd_t *)wp_sk(sk)->mbox; if ((err=put_user(mbox_ptr->cmd.qdm, &usr_data->hdr.qdm))) return err; @@ -2140,7 +2143,7 @@ mbox_cmd_t *mbox_ptr; int err; - if (!sk->protinfo.af_wanpipe->mbox){ + if (!wp_sk(sk)->mbox) { void *mbox_ptr; netdevice_t *dev = dev_get_by_index(sk->bound_dev_if); if (!dev) @@ -2152,12 +2155,12 @@ return -ENOMEM; memset(mbox_ptr, 0, sizeof(mbox_cmd_t)); - sk->protinfo.af_wanpipe->mbox = mbox_ptr; + wp_sk(sk)->mbox = mbox_ptr; wanpipe_link_driver(dev,sk); } - mbox_ptr = (mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox; + mbox_ptr = (mbox_cmd_t*)wp_sk(sk)->mbox; memset(mbox_ptr, 0, sizeof(mbox_cmd_t)); if (usr_data == NULL){ @@ -2204,7 +2207,7 @@ struct sock *sk = sock->sk; unsigned int mask; - ++sk->protinfo.af_wanpipe->poll_cnt; + ++wp_sk(sk)->poll_cnt; poll_wait(file, sk->sleep, wait); mask = 0; @@ -2240,7 +2243,7 @@ * transmit queue */ if (sk->num == htons(X25_PROT)){ - if (atomic_read(&sk->protinfo.af_wanpipe->packet_sent)) + if (atomic_read(&wp_sk(sk)->packet_sent)) return mask; } @@ -2294,9 +2297,8 @@ static int wanpipe_link_card (struct sock *sk) { - sdla_t *card; + sdla_t *card = (sdla_t*)wp_sk(sk)->card; - card = (sdla_t*)sk->protinfo.af_wanpipe->card; if (!card) return -ENOMEM; @@ -2321,9 +2323,7 @@ static void wanpipe_unlink_card (struct sock *sk) { - sdla_t *card; - - card = (sdla_t*)sk->protinfo.af_wanpipe->card; + sdla_t *card = (sdla_t*)wp_sk(sk)->card; if (card){ card->sk=NULL; @@ -2342,7 +2342,8 @@ static int wanpipe_exec_cmd(struct sock *sk, int cmd, unsigned int flags) { int err = -EINVAL; - mbox_cmd_t *mbox_ptr = (mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox; + wanpipe_opt *wp = wp_sk(sk); + mbox_cmd_t *mbox_ptr = (mbox_cmd_t*)wp->mbox; if (!mbox_ptr){ printk(KERN_INFO "NO MBOX PTR !!!!!\n"); @@ -2373,16 +2374,15 @@ * it is done in wanpipe_listen_rcv(). */ if (sk->state == WANSOCK_CONNECTED){ - sk->protinfo.af_wanpipe->lcn = - ((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.lcn; + wp->lcn = ((mbox_cmd_t*)wp->mbox)->cmd.lcn; DBG_PRINTK(KERN_INFO "\nwansock: Accept OK %i\n", - sk->protinfo.af_wanpipe->lcn ); + wp->lcn); err = 0; }else{ DBG_PRINTK (KERN_INFO "\nwansock: Accept Failed %i\n", - sk->protinfo.af_wanpipe->lcn); - sk->protinfo.af_wanpipe->lcn = 0; + wp->lcn); + wp->lcn = 0; err = -ECONNREFUSED; } break; @@ -2400,7 +2400,7 @@ * is transmitted, or clear a call and drop packets */ if (atomic_read(&sk->wmem_alloc) || check_driver_busy(sk)){ - mbox_cmd_t *mbox = sk->protinfo.af_wanpipe->mbox; + mbox_cmd_t *mbox = wp->mbox; if (mbox->cmd.qdm & 0x80){ mbox->cmd.result = 0x35; err = -EAGAIN; @@ -2417,8 +2417,8 @@ err = -ECONNREFUSED; if (sk->state == WANSOCK_DISCONNECTED){ DBG_PRINTK(KERN_INFO "\nwansock: CLEAR OK %i\n", - sk->protinfo.af_wanpipe->lcn); - sk->protinfo.af_wanpipe->lcn=0; + wp->lcn); + wp->lcn = 0; err = 0; } break; @@ -2436,7 +2436,7 @@ * is transmitted, or reset a call and drop packets */ if (atomic_read(&sk->wmem_alloc) || check_driver_busy(sk)){ - mbox_cmd_t *mbox = sk->protinfo.af_wanpipe->mbox; + mbox_cmd_t *mbox = wp->mbox; if (mbox->cmd.qdm & 0x80){ mbox->cmd.result = 0x35; err = -EAGAIN; @@ -2461,18 +2461,16 @@ if (sk->state == WANSOCK_CONNECTED){ - sk->protinfo.af_wanpipe->lcn = - ((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.lcn; + wp->lcn = ((mbox_cmd_t*)wp->mbox)->cmd.lcn; DBG_PRINTK(KERN_INFO "\nwansock: PLACE CALL OK %i\n", - sk->protinfo.af_wanpipe->lcn); + wp->lcn); err = 0; }else if (sk->state == WANSOCK_CONNECTING && (flags & O_NONBLOCK)){ - sk->protinfo.af_wanpipe->lcn = - ((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.lcn; + wp->lcn = ((mbox_cmd_t*)wp->mbox)->cmd.lcn; DBG_PRINTK(KERN_INFO "\nwansock: Place Call OK: Waiting %i\n", - sk->protinfo.af_wanpipe->lcn); + wp->lcn); err = 0; @@ -2583,7 +2581,8 @@ kfree_skb(skb); - DBG_PRINTK(KERN_INFO "\nwansock: ACCEPT Got LCN %i\n",newsk->protinfo.af_wanpipe->lcn); + DBG_PRINTK(KERN_INFO "\nwansock: ACCEPT Got LCN %i\n", + wp_sk(newsk)->lcn); return 0; } @@ -2660,10 +2659,10 @@ sock->state = SS_CONNECTING; sk->state = WANSOCK_CONNECTING; - if (!sk->protinfo.af_wanpipe->mbox){ - if (sk->protinfo.af_wanpipe->svc){ + if (!wp_sk(sk)->mbox) { + if (wp_sk (sk)->svc) return -EINVAL; - }else{ + else { int err; if ((err=set_ioctl_cmd(sk,NULL)) < 0) return err; @@ -2733,14 +2732,12 @@ static struct net_proto_family wanpipe_family_ops = { - PF_WANPIPE, - wanpipe_create + family: PF_WANPIPE, + create: wanpipe_create, }; -struct notifier_block wanpipe_netdev_notifier={ - wanpipe_notifier, - NULL, - 0 +struct notifier_block wanpipe_netdev_notifier = { + notifier_call: wanpipe_notifier, }; diff -Nru a/net/x25/af_x25.c b/net/x25/af_x25.c --- a/net/x25/af_x25.c Tue Feb 19 18:08:59 2002 +++ b/net/x25/af_x25.c Tue Feb 19 18:08:59 2002 @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -181,8 +182,7 @@ struct sock *s; for (s = x25_list; s != NULL; s = s->next) - if (s->protinfo.x25->neighbour && - s->protinfo.x25->neighbour->dev == dev) + if (x25_sk(s)->neighbour && x25_sk(s)->neighbour->dev == dev) x25_disconnect(s, ENETUNREACH, 0, 0); } @@ -247,7 +247,7 @@ cli(); for (s = x25_list; s != NULL; s = s->next) { - if ((strcmp(addr->x25_addr, s->protinfo.x25->source_addr.x25_addr) == 0 || + if ((!strcmp(addr->x25_addr, x25_sk(s)->source_addr.x25_addr) || strcmp(addr->x25_addr, null_x25_address.x25_addr) == 0) && s->state == TCP_LISTEN) { restore_flags(flags); @@ -271,7 +271,7 @@ cli(); for (s = x25_list; s != NULL; s = s->next) { - if (s->protinfo.x25->lci == lci && s->protinfo.x25->neighbour == neigh) { + if (x25_sk(s)->lci == lci && x25_sk(s)->neighbour == neigh) { restore_flags(flags); return s; } @@ -333,7 +333,7 @@ if (skb->sk != sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ x25_start_heartbeat(skb->sk); - skb->sk->protinfo.x25->state = X25_STATE_0; + x25_sk(skb->sk)->state = X25_STATE_0; } kfree_skb(skb); @@ -376,7 +376,7 @@ switch (optname) { case X25_QBITINCL: - sk->protinfo.x25->qbitincl = opt ? 1 : 0; + x25_sk(sk)->qbitincl = opt ? 1 : 0; return 0; default: @@ -399,7 +399,7 @@ switch (optname) { case X25_QBITINCL: - val = sk->protinfo.x25->qbitincl; + val = x25_sk(sk)->qbitincl; break; default: @@ -422,7 +422,7 @@ struct sock *sk = sock->sk; if (sk->state != TCP_LISTEN) { - memset(&sk->protinfo.x25->dest_addr, '\0', X25_ADDR_LEN); + memset(&x25_sk(sk)->dest_addr, 0, X25_ADDR_LEN); sk->max_ack_backlog = backlog; sk->state = TCP_LISTEN; return 0; @@ -436,20 +436,18 @@ struct sock *sk; x25_cb *x25; - if ((sk = sk_alloc(AF_X25, GFP_ATOMIC, 1)) == NULL) - return NULL; + MOD_INC_USE_COUNT; - if ((x25 = kmalloc(sizeof(*x25), GFP_ATOMIC)) == NULL) { - sk_free(sk); - return NULL; - } + if ((sk = sk_alloc(AF_X25, GFP_ATOMIC, 1, NULL)) == NULL) + goto decmod; - memset(x25, 0x00, sizeof(*x25)); + x25 = x25_sk(sk) = kmalloc(sizeof(*x25), GFP_ATOMIC); + if (!x25) + goto frees; - x25->sk = sk; - sk->protinfo.x25 = x25; + memset(x25, 0x00, sizeof(*x25)); - MOD_INC_USE_COUNT; + x25->sk = sk; sock_init_data(NULL, sk); @@ -457,8 +455,11 @@ skb_queue_head_init(&x25->fragment_queue); skb_queue_head_init(&x25->interrupt_in_queue); skb_queue_head_init(&x25->interrupt_out_queue); - - return sk; +out: return sk; +frees: sk_free(sk); + sk = NULL; +decmod: MOD_DEC_USE_COUNT; + goto out; } static int x25_create(struct socket *sock, int protocol) @@ -472,7 +473,7 @@ if ((sk = x25_alloc_socket()) == NULL) return -ENOMEM; - x25 = sk->protinfo.x25; + x25 = x25_sk(sk); sock_init_data(sock, sk); @@ -501,7 +502,7 @@ static struct sock *x25_make_new(struct sock *osk) { struct sock *sk; - x25_cb *x25; + x25_cb *x25, *ox25; if (osk->type != SOCK_SEQPACKET) return NULL; @@ -509,7 +510,7 @@ if ((sk = x25_alloc_socket()) == NULL) return NULL; - x25 = sk->protinfo.x25; + x25 = x25_sk(sk); sk->type = osk->type; sk->socket = osk->socket; @@ -523,14 +524,13 @@ sk->zapped = osk->zapped; sk->backlog_rcv = osk->backlog_rcv; - x25->t21 = osk->protinfo.x25->t21; - x25->t22 = osk->protinfo.x25->t22; - x25->t23 = osk->protinfo.x25->t23; - x25->t2 = osk->protinfo.x25->t2; - - x25->facilities = osk->protinfo.x25->facilities; - - x25->qbitincl = osk->protinfo.x25->qbitincl; + ox25 = x25_sk(osk); + x25->t21 = ox25->t21; + x25->t22 = ox25->t22; + x25->t23 = ox25->t23; + x25->t2 = ox25->t2; + x25->facilities = ox25->facilities; + x25->qbitincl = ox25->qbitincl; init_timer(&x25->timer); @@ -540,10 +540,13 @@ static int x25_release(struct socket *sock) { struct sock *sk = sock->sk; + x25_cb *x25; if (sk == NULL) return 0; - switch (sk->protinfo.x25->state) { + x25 = x25_sk(sk); + + switch (x25->state) { case X25_STATE_0: case X25_STATE_2: @@ -557,7 +560,7 @@ x25_clear_queues(sk); x25_write_internal(sk, X25_CLEAR_REQUEST); x25_start_t23timer(sk); - sk->protinfo.x25->state = X25_STATE_2; + x25->state = X25_STATE_2; sk->state = TCP_CLOSE; sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); @@ -589,7 +592,7 @@ if (addr->sx25_family != AF_X25) return -EINVAL; - sk->protinfo.x25->source_addr = addr->sx25_addr; + x25_sk(sk)->source_addr = addr->sx25_addr; x25_insert_socket(sk); @@ -603,6 +606,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { struct sock *sk = sock->sk; + x25_cb *x25 = x25_sk(sk); struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr; struct net_device *dev; @@ -631,28 +635,27 @@ if ((dev = x25_get_route(&addr->sx25_addr)) == NULL) return -ENETUNREACH; - if ((sk->protinfo.x25->neighbour = x25_get_neigh(dev)) == NULL) + if ((x25->neighbour = x25_get_neigh(dev)) == NULL) return -ENETUNREACH; - x25_limit_facilities(&sk->protinfo.x25->facilities, - sk->protinfo.x25->neighbour); + x25_limit_facilities(&x25->facilities, x25->neighbour); - if ((sk->protinfo.x25->lci = x25_new_lci(sk->protinfo.x25->neighbour)) == 0) + if ((x25->lci = x25_new_lci(x25->neighbour)) == 0) return -ENETUNREACH; if (sk->zapped) /* Must bind first - autobinding does not work */ return -EINVAL; - if (strcmp(sk->protinfo.x25->source_addr.x25_addr, null_x25_address.x25_addr) == 0) - memset(&sk->protinfo.x25->source_addr, '\0', X25_ADDR_LEN); + if (!strcmp(x25->source_addr.x25_addr, null_x25_address.x25_addr)) + memset(&x25->source_addr, '\0', X25_ADDR_LEN); - sk->protinfo.x25->dest_addr = addr->sx25_addr; + x25->dest_addr = addr->sx25_addr; /* Move to connecting socket, start sending Connect Requests */ sock->state = SS_CONNECTING; sk->state = TCP_SYN_SENT; - sk->protinfo.x25->state = X25_STATE_1; + x25->state = X25_STATE_1; x25_write_internal(sk, X25_CALL_REQUEST); @@ -743,13 +746,14 @@ { struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)uaddr; struct sock *sk = sock->sk; + x25_cb *x25 = x25_sk(sk); if (peer != 0) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; - sx25->sx25_addr = sk->protinfo.x25->dest_addr; + sx25->sx25_addr = x25->dest_addr; } else { - sx25->sx25_addr = sk->protinfo.x25->source_addr; + sx25->sx25_addr = x25->source_addr; } sx25->sx25_family = AF_X25; @@ -762,6 +766,7 @@ { struct sock *sk; struct sock *make; + x25_cb *makex25; x25_address source_addr, dest_addr; struct x25_facilities facilities; int len; @@ -821,12 +826,13 @@ skb->sk = make; make->state = TCP_ESTABLISHED; - make->protinfo.x25->lci = lci; - make->protinfo.x25->dest_addr = dest_addr; - make->protinfo.x25->source_addr = source_addr; - make->protinfo.x25->neighbour = neigh; - make->protinfo.x25->facilities = facilities; - make->protinfo.x25->vc_facil_mask = sk->protinfo.x25->vc_facil_mask; + makex25 = x25_sk(make); + makex25->lci = lci; + makex25->dest_addr = dest_addr; + makex25->source_addr = source_addr; + makex25->neighbour = neigh; + makex25->facilities = facilities; + makex25->vc_facil_mask = x25_sk(sk)->vc_facil_mask; x25_write_internal(make, X25_CALL_ACCEPTED); @@ -834,11 +840,11 @@ * Incoming Call User Data. */ if (skb->len >= 0) { - memcpy(make->protinfo.x25->calluserdata.cuddata, skb->data, skb->len); - make->protinfo.x25->calluserdata.cudlength = skb->len; + memcpy(makex25->calluserdata.cuddata, skb->data, skb->len); + makex25->calluserdata.cudlength = skb->len; } - make->protinfo.x25->state = X25_STATE_3; + makex25->state = X25_STATE_3; sk->ack_backlog++; make->pair = sk; @@ -858,6 +864,7 @@ static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; + x25_cb *x25 = x25_sk(sk); struct sockaddr_x25 *usx25 = (struct sockaddr_x25 *)msg->msg_name; int err; struct sockaddr_x25 sx25; @@ -880,14 +887,14 @@ return -EPIPE; } - if (sk->protinfo.x25->neighbour == NULL) + if (x25->neighbour == NULL) return -ENETUNREACH; if (usx25 != NULL) { if (msg->msg_namelen < sizeof(sx25)) return -EINVAL; sx25 = *usx25; - if (strcmp(sk->protinfo.x25->dest_addr.x25_addr, sx25.sx25_addr.x25_addr) != 0) + if (strcmp(x25->dest_addr.x25_addr, sx25.sx25_addr.x25_addr)) return -EISCONN; if (sx25.sx25_family != AF_X25) return -EINVAL; @@ -901,7 +908,7 @@ return -ENOTCONN; sx25.sx25_family = AF_X25; - sx25.sx25_addr = sk->protinfo.x25->dest_addr; + sx25.sx25_addr = x25->dest_addr; } SOCK_DEBUG(sk, "x25_sendmsg: sendto: Addresses built.\n"); @@ -933,7 +940,7 @@ * If the Q BIT Include socket option is in force, the first * byte of the user data is the logical value of the Q Bit. */ - if (sk->protinfo.x25->qbitincl) { + if (x25->qbitincl) { qbit = skb->data[0]; skb_pull(skb, 1); } @@ -944,30 +951,30 @@ SOCK_DEBUG(sk, "x25_sendmsg: Building X.25 Header.\n"); if (msg->msg_flags & MSG_OOB) { - if (sk->protinfo.x25->neighbour->extended) { + if (x25->neighbour->extended) { asmptr = skb_push(skb, X25_STD_MIN_LEN); - *asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ; - *asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF; + *asmptr++ = ((x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ; + *asmptr++ = (x25->lci >> 0) & 0xFF; *asmptr++ = X25_INTERRUPT; } else { asmptr = skb_push(skb, X25_STD_MIN_LEN); - *asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ; - *asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF; + *asmptr++ = ((x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ; + *asmptr++ = (x25->lci >> 0) & 0xFF; *asmptr++ = X25_INTERRUPT; } } else { - if (sk->protinfo.x25->neighbour->extended) { + if (x25->neighbour->extended) { /* Build an Extended X.25 header */ asmptr = skb_push(skb, X25_EXT_MIN_LEN); - *asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ; - *asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF; + *asmptr++ = ((x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ; + *asmptr++ = (x25->lci >> 0) & 0xFF; *asmptr++ = X25_DATA; *asmptr++ = X25_DATA; } else { /* Build an Standard X.25 header */ asmptr = skb_push(skb, X25_STD_MIN_LEN); - *asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ; - *asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF; + *asmptr++ = ((x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ; + *asmptr++ = (x25->lci >> 0) & 0xFF; *asmptr++ = X25_DATA; } @@ -984,13 +991,14 @@ } if (msg->msg_flags & MSG_OOB) { - skb_queue_tail(&sk->protinfo.x25->interrupt_out_queue, skb); + skb_queue_tail(&x25->interrupt_out_queue, skb); } else { len = x25_output(sk, skb); if(len<0){ kfree_skb(skb); } else { - if(sk->protinfo.x25->qbitincl) len++; + if (x25->qbitincl) + len++; } } @@ -1019,6 +1027,7 @@ static int x25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; + x25_cb *x25 = x25_sk(sk); struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)msg->msg_name; int copied, qbit; struct sk_buff *skb; @@ -1033,17 +1042,17 @@ return -ENOTCONN; if (flags & MSG_OOB) { - if (sk->urginline || skb_peek(&sk->protinfo.x25->interrupt_in_queue) == NULL) + if (sk->urginline || !skb_peek(&x25->interrupt_in_queue)) return -EINVAL; - skb = skb_dequeue(&sk->protinfo.x25->interrupt_in_queue); + skb = skb_dequeue(&x25->interrupt_in_queue); skb_pull(skb, X25_STD_MIN_LEN); /* * No Q bit information on Interrupt data. */ - if (sk->protinfo.x25->qbitincl) { + if (x25->qbitincl) { asmptr = skb_push(skb, 1); *asmptr = 0x00; } @@ -1056,9 +1065,10 @@ qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT; - skb_pull(skb, (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN); + skb_pull(skb, x25->neighbour->extended ? + X25_EXT_MIN_LEN : X25_STD_MIN_LEN); - if (sk->protinfo.x25->qbitincl) { + if (x25->qbitincl) { asmptr = skb_push(skb, 1); *asmptr = qbit; } @@ -1080,7 +1090,7 @@ if (sx25 != NULL) { sx25->sx25_family = AF_X25; - sx25->sx25_addr = sk->protinfo.x25->dest_addr; + sx25->sx25_addr = x25->dest_addr; } msg->msg_namelen = sizeof(struct sockaddr_x25); @@ -1097,6 +1107,7 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; + x25_cb *x25 = x25_sk(sk); switch (cmd) { case TIOCOUTQ: { @@ -1150,7 +1161,7 @@ case SIOCX25GFACILITIES: { struct x25_facilities facilities; - facilities = sk->protinfo.x25->facilities; + facilities = x25->facilities; return copy_to_user((void *)arg, &facilities, sizeof(facilities)) ? -EFAULT : 0; } @@ -1170,13 +1181,13 @@ return -EINVAL; if (facilities.reverse != 0 && facilities.reverse != 1) return -EINVAL; - sk->protinfo.x25->facilities = facilities; + x25->facilities = facilities; return 0; } case SIOCX25GCALLUSERDATA: { struct x25_calluserdata calluserdata; - calluserdata = sk->protinfo.x25->calluserdata; + calluserdata = x25->calluserdata; return copy_to_user((void *)arg, &calluserdata, sizeof(calluserdata)) ? -EFAULT : 0; } @@ -1186,13 +1197,13 @@ return -EFAULT; if (calluserdata.cudlength > X25_MAX_CUD_LEN) return -EINVAL; - sk->protinfo.x25->calluserdata = calluserdata; + x25->calluserdata = calluserdata; return 0; } case SIOCX25GCAUSEDIAG: { struct x25_causediag causediag; - causediag = sk->protinfo.x25->causediag; + causediag = x25->causediag; return copy_to_user((void *)arg, &causediag, sizeof(causediag)) ? -EFAULT : 0; } @@ -1218,25 +1229,29 @@ len += sprintf(buffer, "dest_addr src_addr dev lci st vs vr va t t2 t21 t22 t23 Snd-Q Rcv-Q inode\n"); for (s = x25_list; s != NULL; s = s->next) { - if (s->protinfo.x25->neighbour == NULL || (dev = s->protinfo.x25->neighbour->dev) == NULL) + x25_cb *x25 = x25_sk(s); + + if (!x25->neighbour || (dev = x25->neighbour->dev) == NULL) devname = "???"; else - devname = s->protinfo.x25->neighbour->dev->name; + devname = x25->neighbour->dev->name; len += sprintf(buffer + len, "%-10s %-10s %-5s %3.3X %d %d %d %d %3lu %3lu %3lu %3lu %3lu %5d %5d %ld\n", - (s->protinfo.x25->dest_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->dest_addr.x25_addr, - (s->protinfo.x25->source_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->source_addr.x25_addr, + !x25->dest_addr.x25_addr[0] ? "*" : + x25->dest_addr.x25_addr, + !x25->source_addr.x25_addr[0] ? "*" : + x25->source_addr.x25_addr, devname, - s->protinfo.x25->lci & 0x0FFF, - s->protinfo.x25->state, - s->protinfo.x25->vs, - s->protinfo.x25->vr, - s->protinfo.x25->va, + x25->lci & 0x0FFF, + x25->state, + x25->vs, + x25->vr, + x25->va, x25_display_timer(s) / HZ, - s->protinfo.x25->t2 / HZ, - s->protinfo.x25->t21 / HZ, - s->protinfo.x25->t22 / HZ, - s->protinfo.x25->t23 / HZ, + x25->t2 / HZ, + x25->t21 / HZ, + x25->t22 / HZ, + x25->t23 / HZ, atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc), s->socket != NULL ? SOCK_INODE(s->socket)->i_ino : 0L); @@ -1306,7 +1321,7 @@ struct sock *s; for( s=x25_list; s != NULL; s=s->next){ - if( s->protinfo.x25->neighbour == neigh ) + if (x25_sk(s)->neighbour == neigh) x25_disconnect(s, ENETUNREACH, 0, 0); } } diff -Nru a/net/x25/x25_facilities.c b/net/x25/x25_facilities.c --- a/net/x25/x25_facilities.c Tue Feb 19 18:08:57 2002 +++ b/net/x25/x25_facilities.c Tue Feb 19 18:08:57 2002 @@ -156,17 +156,18 @@ */ int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk, struct x25_facilities *new) { + x25_cb *x25 = x25_sk(sk); struct x25_facilities *ours; struct x25_facilities theirs; int len; memset(&theirs, 0x00, sizeof(struct x25_facilities)); - ours = &sk->protinfo.x25->facilities; + ours = &x25->facilities; *new = *ours; - len = x25_parse_facilities(skb, &theirs, &sk->protinfo.x25->vc_facil_mask); + len = x25_parse_facilities(skb, &theirs, &x25->vc_facil_mask); /* * They want reverse charging, we won't accept it. diff -Nru a/net/x25/x25_in.c b/net/x25/x25_in.c --- a/net/x25/x25_in.c Tue Feb 19 18:08:59 2002 +++ b/net/x25/x25_in.c Tue Feb 19 18:08:59 2002 @@ -37,6 +37,7 @@ #include #include #include /* For ip_rcv */ +#include #include #include #include @@ -46,37 +47,40 @@ static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) { struct sk_buff *skbo, *skbn = skb; + x25_cb *x25 = x25_sk(sk); if (more) { - sk->protinfo.x25->fraglen += skb->len; - skb_queue_tail(&sk->protinfo.x25->fragment_queue, skb); + x25->fraglen += skb->len; + skb_queue_tail(&x25->fragment_queue, skb); skb_set_owner_r(skb, sk); return 0; } - if (!more && sk->protinfo.x25->fraglen > 0) { /* End of fragment */ - int len = sk->protinfo.x25->fraglen + skb->len; + if (!more && x25->fraglen > 0) { /* End of fragment */ + int len = x25->fraglen + skb->len; if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL){ kfree_skb(skb); return 1; } - skb_queue_tail(&sk->protinfo.x25->fragment_queue, skb); + skb_queue_tail(&x25->fragment_queue, skb); skbn->h.raw = skbn->data; - skbo = skb_dequeue(&sk->protinfo.x25->fragment_queue); + skbo = skb_dequeue(&x25->fragment_queue); memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); kfree_skb(skbo); - while ((skbo = skb_dequeue(&sk->protinfo.x25->fragment_queue)) != NULL) { - skb_pull(skbo, (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN); + while ((skbo = + skb_dequeue(&x25->fragment_queue)) != NULL) { + skb_pull(skbo, (x25->neighbour->extended) ? + X25_EXT_MIN_LEN : X25_STD_MIN_LEN); memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); kfree_skb(skbo); } - sk->protinfo.x25->fraglen = 0; + x25->fraglen = 0; } skb_set_owner_r(skbn, sk); @@ -97,33 +101,37 @@ x25_address source_addr, dest_addr; switch (frametype) { + case X25_CALL_ACCEPTED: { + x25_cb *x25 = x25_sk(sk); - case X25_CALL_ACCEPTED: x25_stop_timer(sk); - sk->protinfo.x25->condition = 0x00; - sk->protinfo.x25->vs = 0; - sk->protinfo.x25->va = 0; - sk->protinfo.x25->vr = 0; - sk->protinfo.x25->vl = 0; - sk->protinfo.x25->state = X25_STATE_3; - sk->state = TCP_ESTABLISHED; + x25->condition = 0x00; + x25->vs = 0; + x25->va = 0; + x25->vr = 0; + x25->vl = 0; + x25->state = X25_STATE_3; + sk->state = TCP_ESTABLISHED; /* * Parse the data in the frame. */ skb_pull(skb, X25_STD_MIN_LEN); skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr)); - skb_pull(skb, x25_parse_facilities(skb, &sk->protinfo.x25->facilities, &sk->protinfo.x25->vc_facil_mask)); + skb_pull(skb, + x25_parse_facilities(skb, &x25->facilities, + &x25->vc_facil_mask)); /* * Copy any Call User Data. */ if (skb->len >= 0) { - memcpy(sk->protinfo.x25->calluserdata.cuddata, skb->data, skb->len); - sk->protinfo.x25->calluserdata.cudlength = skb->len; + memcpy(x25->calluserdata.cuddata, skb->data, + skb->len); + x25->calluserdata.cudlength = skb->len; } if (!sk->dead) sk->state_change(sk); break; - + } case X25_CLEAR_REQUEST: x25_write_internal(sk, X25_CLEAR_CONFIRMATION); x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); @@ -170,19 +178,20 @@ { int queued = 0; int modulus; + x25_cb *x25 = x25_sk(sk); - modulus = (sk->protinfo.x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS; + modulus = (x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS; switch (frametype) { case X25_RESET_REQUEST: x25_write_internal(sk, X25_RESET_CONFIRMATION); x25_stop_timer(sk); - sk->protinfo.x25->condition = 0x00; - sk->protinfo.x25->vs = 0; - sk->protinfo.x25->vr = 0; - sk->protinfo.x25->va = 0; - sk->protinfo.x25->vl = 0; + x25->condition = 0x00; + x25->vs = 0; + x25->vr = 0; + x25->va = 0; + x25->vl = 0; x25_requeue_frames(sk); break; @@ -197,74 +206,73 @@ x25_clear_queues(sk); x25_write_internal(sk, X25_RESET_REQUEST); x25_start_t22timer(sk); - sk->protinfo.x25->condition = 0x00; - sk->protinfo.x25->vs = 0; - sk->protinfo.x25->vr = 0; - sk->protinfo.x25->va = 0; - sk->protinfo.x25->vl = 0; - sk->protinfo.x25->state = X25_STATE_4; + x25->condition = 0x00; + x25->vs = 0; + x25->vr = 0; + x25->va = 0; + x25->vl = 0; + x25->state = X25_STATE_4; } else { x25_frames_acked(sk, nr); if (frametype == X25_RNR) { - sk->protinfo.x25->condition |= X25_COND_PEER_RX_BUSY; + x25->condition |= X25_COND_PEER_RX_BUSY; } else { - sk->protinfo.x25->condition &= ~X25_COND_PEER_RX_BUSY; + x25->condition &= ~X25_COND_PEER_RX_BUSY; } } break; case X25_DATA: /* XXX */ - sk->protinfo.x25->condition &= ~X25_COND_PEER_RX_BUSY; - if ((ns!=sk->protinfo.x25->vr) || - !x25_validate_nr(sk, nr)) { + x25->condition &= ~X25_COND_PEER_RX_BUSY; + if ((ns != x25->vr) || !x25_validate_nr(sk, nr)) { x25_clear_queues(sk); x25_write_internal(sk, X25_RESET_REQUEST); x25_start_t22timer(sk); - sk->protinfo.x25->condition = 0x00; - sk->protinfo.x25->vs = 0; - sk->protinfo.x25->vr = 0; - sk->protinfo.x25->va = 0; - sk->protinfo.x25->vl = 0; - sk->protinfo.x25->state = X25_STATE_4; + x25->condition = 0x00; + x25->vs = 0; + x25->vr = 0; + x25->va = 0; + x25->vl = 0; + x25->state = X25_STATE_4; break; } x25_frames_acked(sk, nr); - if (ns == sk->protinfo.x25->vr) { + if (ns == x25->vr) { if (x25_queue_rx_frame(sk, skb, m) == 0) { - sk->protinfo.x25->vr = (sk->protinfo.x25->vr + 1) % modulus; + x25->vr = (x25->vr + 1) % modulus; queued = 1; } else { /* Should never happen */ x25_clear_queues(sk); x25_write_internal(sk, X25_RESET_REQUEST); x25_start_t22timer(sk); - sk->protinfo.x25->condition = 0x00; - sk->protinfo.x25->vs = 0; - sk->protinfo.x25->vr = 0; - sk->protinfo.x25->va = 0; - sk->protinfo.x25->vl = 0; - sk->protinfo.x25->state = X25_STATE_4; + x25->condition = 0x00; + x25->vs = 0; + x25->vr = 0; + x25->va = 0; + x25->vl = 0; + x25->state = X25_STATE_4; break; } if (atomic_read(&sk->rmem_alloc) > (sk->rcvbuf / 2)) - sk->protinfo.x25->condition |= X25_COND_OWN_RX_BUSY; + x25->condition |= X25_COND_OWN_RX_BUSY; } /* * If the window is full Ack it immediately, else * start the holdback timer. */ - if (((sk->protinfo.x25->vl + sk->protinfo.x25->facilities.winsize_in) % modulus) == sk->protinfo.x25->vr) { - sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; + if (((x25->vl + x25->facilities.winsize_in) % modulus) == x25->vr) { + x25->condition &= ~X25_COND_ACK_PENDING; x25_stop_timer(sk); x25_enquiry_response(sk); } else { - sk->protinfo.x25->condition |= X25_COND_ACK_PENDING; + x25->condition |= X25_COND_ACK_PENDING; x25_start_t2timer(sk); } break; case X25_INTERRUPT_CONFIRMATION: - sk->protinfo.x25->intflag = 0; + x25->intflag = 0; break; case X25_INTERRUPT: @@ -272,7 +280,7 @@ queued = (sock_queue_rcv_skb(sk, skb) == 0); } else { skb_set_owner_r(skb, sk); - skb_queue_tail(&sk->protinfo.x25->interrupt_in_queue, skb); + skb_queue_tail(&x25->interrupt_in_queue, skb); queued = 1; } if (sk->proc != 0) { @@ -304,17 +312,19 @@ case X25_RESET_REQUEST: x25_write_internal(sk, X25_RESET_CONFIRMATION); - case X25_RESET_CONFIRMATION: + case X25_RESET_CONFIRMATION: { + x25_cb *x25 = x25_sk(sk); + x25_stop_timer(sk); - sk->protinfo.x25->condition = 0x00; - sk->protinfo.x25->va = 0; - sk->protinfo.x25->vr = 0; - sk->protinfo.x25->vs = 0; - sk->protinfo.x25->vl = 0; - sk->protinfo.x25->state = X25_STATE_3; + x25->condition = 0x00; + x25->va = 0; + x25->vr = 0; + x25->vs = 0; + x25->vl = 0; + x25->state = X25_STATE_3; x25_requeue_frames(sk); break; - + } case X25_CLEAR_REQUEST: x25_write_internal(sk, X25_CLEAR_CONFIRMATION); x25_disconnect(sk, 0, skb->data[3], skb->data[4]); @@ -330,14 +340,15 @@ /* Higher level upcall for a LAPB frame */ int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb) { + x25_cb *x25 = x25_sk(sk); int queued = 0, frametype, ns, nr, q, d, m; - if (sk->protinfo.x25->state == X25_STATE_0) + if (x25->state == X25_STATE_0) return 0; frametype = x25_decode(sk, skb, &ns, &nr, &q, &d, &m); - switch (sk->protinfo.x25->state) { + switch (x25->state) { case X25_STATE_1: queued = x25_state1_machine(sk, skb, frametype); break; diff -Nru a/net/x25/x25_out.c b/net/x25/x25_out.c --- a/net/x25/x25_out.c Tue Feb 19 18:08:57 2002 +++ b/net/x25/x25_out.c Tue Feb 19 18:08:57 2002 @@ -64,11 +64,12 @@ { struct sk_buff *skbn; unsigned char header[X25_EXT_MIN_LEN]; - int err, frontlen, len, header_len, max_len; + int err, frontlen, len; int sent=0, noblock = X25_SKB_CB(skb)->flags & MSG_DONTWAIT; - - header_len = (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN; - max_len = x25_pacsize_to_bytes(sk->protinfo.x25->facilities.pacsize_out); + x25_cb *x25 = x25_sk(sk); + int header_len = x25->neighbour->extended ? X25_EXT_MIN_LEN : + X25_STD_MIN_LEN; + int max_len = x25_pacsize_to_bytes(x25->facilities.pacsize_out); if (skb->len - header_len > max_len) { /* Save a copy of the Header */ @@ -100,7 +101,7 @@ memcpy(skbn->data, header, header_len); if (skb->len > 0) { - if (sk->protinfo.x25->neighbour->extended) + if (x25->neighbour->extended) skbn->data[3] |= X25_EXT_M_BIT; else skbn->data[2] |= X25_STD_M_BIT; @@ -124,20 +125,22 @@ */ static void x25_send_iframe(struct sock *sk, struct sk_buff *skb) { + x25_cb *x25 = x25_sk(sk); + if (skb == NULL) return; - if (sk->protinfo.x25->neighbour->extended) { - skb->data[2] = (sk->protinfo.x25->vs << 1) & 0xFE; + if (x25->neighbour->extended) { + skb->data[2] = (x25->vs << 1) & 0xFE; skb->data[3] &= X25_EXT_M_BIT; - skb->data[3] |= (sk->protinfo.x25->vr << 1) & 0xFE; + skb->data[3] |= (x25->vr << 1) & 0xFE; } else { skb->data[2] &= X25_STD_M_BIT; - skb->data[2] |= (sk->protinfo.x25->vs << 1) & 0x0E; - skb->data[2] |= (sk->protinfo.x25->vr << 5) & 0xE0; + skb->data[2] |= (x25->vs << 1) & 0x0E; + skb->data[2] |= (x25->vr << 5) & 0xE0; } - x25_transmit_link(skb, sk->protinfo.x25->neighbour); + x25_transmit_link(skb, x25->neighbour); } void x25_kick(struct sock *sk) @@ -145,34 +148,35 @@ struct sk_buff *skb, *skbn; unsigned short start, end; int modulus; + x25_cb *x25 = x25_sk(sk); - if (sk->protinfo.x25->state != X25_STATE_3) + if (x25->state != X25_STATE_3) return; /* * Transmit interrupt data. */ - if (!sk->protinfo.x25->intflag && skb_peek(&sk->protinfo.x25->interrupt_out_queue) != NULL) { - sk->protinfo.x25->intflag = 1; - skb = skb_dequeue(&sk->protinfo.x25->interrupt_out_queue); - x25_transmit_link(skb, sk->protinfo.x25->neighbour); + if (!x25->intflag && skb_peek(&x25->interrupt_out_queue) != NULL) { + x25->intflag = 1; + skb = skb_dequeue(&x25->interrupt_out_queue); + x25_transmit_link(skb, x25->neighbour); } - if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) + if (x25->condition & X25_COND_PEER_RX_BUSY) return; if (skb_peek(&sk->write_queue) == NULL) return; - modulus = (sk->protinfo.x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS; + modulus = (x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS; - start = (skb_peek(&sk->protinfo.x25->ack_queue) == NULL) ? sk->protinfo.x25->va : sk->protinfo.x25->vs; - end = (sk->protinfo.x25->va + sk->protinfo.x25->facilities.winsize_out) % modulus; + start = (skb_peek(&x25->ack_queue) == NULL) ? x25->va : x25->vs; + end = (x25->va + x25->facilities.winsize_out) % modulus; if (start == end) return; - sk->protinfo.x25->vs = start; + x25->vs = start; /* * Transmit data until either we're out of data to send or @@ -194,17 +198,17 @@ */ x25_send_iframe(sk, skbn); - sk->protinfo.x25->vs = (sk->protinfo.x25->vs + 1) % modulus; + x25->vs = (x25->vs + 1) % modulus; /* * Requeue the original data frame. */ - skb_queue_tail(&sk->protinfo.x25->ack_queue, skb); + skb_queue_tail(&x25->ack_queue, skb); - } while (sk->protinfo.x25->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + } while (x25->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); - sk->protinfo.x25->vl = sk->protinfo.x25->vr; - sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; + x25->vl = x25->vr; + x25->condition &= ~X25_COND_ACK_PENDING; x25_stop_timer(sk); } @@ -216,13 +220,15 @@ void x25_enquiry_response(struct sock *sk) { - if (sk->protinfo.x25->condition & X25_COND_OWN_RX_BUSY) + x25_cb *x25 = x25_sk(sk); + + if (x25->condition & X25_COND_OWN_RX_BUSY) x25_write_internal(sk, X25_RNR); else x25_write_internal(sk, X25_RR); - sk->protinfo.x25->vl = sk->protinfo.x25->vr; - sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; + x25->vl = x25->vr; + x25->condition &= ~X25_COND_ACK_PENDING; x25_stop_timer(sk); } diff -Nru a/net/x25/x25_subr.c b/net/x25/x25_subr.c --- a/net/x25/x25_subr.c Tue Feb 19 18:08:58 2002 +++ b/net/x25/x25_subr.c Tue Feb 19 18:08:58 2002 @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -45,11 +46,13 @@ */ void x25_clear_queues(struct sock *sk) { + x25_cb *x25 = x25_sk(sk); + skb_queue_purge(&sk->write_queue); - skb_queue_purge(&sk->protinfo.x25->ack_queue); - skb_queue_purge(&sk->protinfo.x25->interrupt_in_queue); - skb_queue_purge(&sk->protinfo.x25->interrupt_out_queue); - skb_queue_purge(&sk->protinfo.x25->fragment_queue); + skb_queue_purge(&x25->ack_queue); + skb_queue_purge(&x25->interrupt_in_queue); + skb_queue_purge(&x25->interrupt_out_queue); + skb_queue_purge(&x25->fragment_queue); } @@ -61,19 +64,17 @@ void x25_frames_acked(struct sock *sk, unsigned short nr) { struct sk_buff *skb; - int modulus = sk->protinfo.x25->neighbour->extended ? X25_EMODULUS : - X25_SMODULUS; + x25_cb *x25 = x25_sk(sk); + int modulus = x25->neighbour->extended ? X25_EMODULUS : X25_SMODULUS; /* * Remove all the ack-ed frames from the ack queue. */ - if (sk->protinfo.x25->va != nr) - while (skb_peek(&sk->protinfo.x25->ack_queue) != NULL && - sk->protinfo.x25->va != nr) { - skb = skb_dequeue(&sk->protinfo.x25->ack_queue); + if (x25->va != nr) + while (skb_peek(&x25->ack_queue) && x25->va != nr) { + skb = skb_dequeue(&x25->ack_queue); kfree_skb(skb); - sk->protinfo.x25->va = (sk->protinfo.x25->va + 1) % - modulus; + x25->va = (x25->va + 1) % modulus; } } @@ -86,7 +87,7 @@ * up by x25_kick. This arrangement handles the possibility of an empty * output queue. */ - while ((skb = skb_dequeue(&sk->protinfo.x25->ack_queue)) != NULL) { + while ((skb = skb_dequeue(&x25_sk(sk)->ack_queue)) != NULL) { if (skb_prev == NULL) skb_queue_head(&sk->write_queue, skb); else @@ -101,16 +102,16 @@ */ int x25_validate_nr(struct sock *sk, unsigned short nr) { - unsigned short vc = sk->protinfo.x25->va; - int modulus = sk->protinfo.x25->neighbour->extended ? X25_EMODULUS : - X25_SMODULUS; + x25_cb *x25 = x25_sk(sk); + unsigned short vc = x25->va; + int modulus = x25->neighbour->extended ? X25_EMODULUS : X25_SMODULUS; - while (vc != sk->protinfo.x25->vs) { + while (vc != x25->vs) { if (nr == vc) return 1; vc = (vc + 1) % modulus; } - return nr == sk->protinfo.x25->vs ? 1 : 0; + return nr == x25->vs ? 1 : 0; } /* @@ -119,6 +120,7 @@ */ void x25_write_internal(struct sock *sk, int frametype) { + x25_cb *x25 = x25_sk(sk); struct sk_buff *skb; unsigned char *dptr; unsigned char facilities[X25_MAX_FAC_LEN]; @@ -172,10 +174,10 @@ */ dptr = skb_put(skb, 2); - lci1 = (sk->protinfo.x25->lci >> 8) & 0x0F; - lci2 = (sk->protinfo.x25->lci >> 0) & 0xFF; + lci1 = (x25->lci >> 8) & 0x0F; + lci2 = (x25->lci >> 0) & 0xFF; - if (sk->protinfo.x25->neighbour->extended) { + if (x25->neighbour->extended) { *dptr++ = lci1 | X25_GFI_EXTSEQ; *dptr++ = lci2; } else { @@ -191,27 +193,34 @@ case X25_CALL_REQUEST: dptr = skb_put(skb, 1); *dptr++ = X25_CALL_REQUEST; - len = x25_addr_aton(addresses, &sk->protinfo.x25->dest_addr, &sk->protinfo.x25->source_addr); + len = x25_addr_aton(addresses, &x25->dest_addr, + &x25->source_addr); dptr = skb_put(skb, len); memcpy(dptr, addresses, len); - len = x25_create_facilities(facilities, &sk->protinfo.x25->facilities, sk->protinfo.x25->neighbour->global_facil_mask); + len = x25_create_facilities(facilities, + &x25->facilities, + x25->neighbour->global_facil_mask); dptr = skb_put(skb, len); memcpy(dptr, facilities, len); - dptr = skb_put(skb, sk->protinfo.x25->calluserdata.cudlength); - memcpy(dptr, sk->protinfo.x25->calluserdata.cuddata, sk->protinfo.x25->calluserdata.cudlength); - sk->protinfo.x25->calluserdata.cudlength = 0; + dptr = skb_put(skb, x25->calluserdata.cudlength); + memcpy(dptr, x25->calluserdata.cuddata, + x25->calluserdata.cudlength); + x25->calluserdata.cudlength = 0; break; case X25_CALL_ACCEPTED: dptr = skb_put(skb, 2); *dptr++ = X25_CALL_ACCEPTED; *dptr++ = 0x00; /* Address lengths */ - len = x25_create_facilities(facilities, &sk->protinfo.x25->facilities, sk->protinfo.x25->vc_facil_mask); + len = x25_create_facilities(facilities, + &x25->facilities, + x25->vc_facil_mask); dptr = skb_put(skb, len); memcpy(dptr, facilities, len); - dptr = skb_put(skb, sk->protinfo.x25->calluserdata.cudlength); - memcpy(dptr, sk->protinfo.x25->calluserdata.cuddata, sk->protinfo.x25->calluserdata.cudlength); - sk->protinfo.x25->calluserdata.cudlength = 0; + dptr = skb_put(skb, x25->calluserdata.cudlength); + memcpy(dptr, x25->calluserdata.cuddata, + x25->calluserdata.cudlength); + x25->calluserdata.cudlength = 0; break; case X25_CLEAR_REQUEST: @@ -225,14 +234,14 @@ case X25_RR: case X25_RNR: case X25_REJ: - if (sk->protinfo.x25->neighbour->extended) { + if (x25->neighbour->extended) { dptr = skb_put(skb, 2); *dptr++ = frametype; - *dptr++ = (sk->protinfo.x25->vr << 1) & 0xFE; + *dptr++ = (x25->vr << 1) & 0xFE; } else { dptr = skb_put(skb, 1); *dptr = frametype; - *dptr++ |= (sk->protinfo.x25->vr << 5) & 0xE0; + *dptr++ |= (x25->vr << 5) & 0xE0; } break; @@ -244,7 +253,7 @@ break; } - x25_transmit_link(skb, sk->protinfo.x25->neighbour); + x25_transmit_link(skb, x25->neighbour); } /* @@ -253,6 +262,7 @@ int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m) { + x25_cb *x25 = x25_sk(sk); unsigned char *frame = skb->data; *ns = *nr = *q = *d = *m = 0; @@ -274,7 +284,7 @@ return frame[2]; } - if (sk->protinfo.x25->neighbour->extended) { + if (x25->neighbour->extended) { if (frame[2] == X25_RR || frame[2] == X25_RNR || frame[2] == X25_REJ) { @@ -290,7 +300,7 @@ } } - if (sk->protinfo.x25->neighbour->extended) { + if (x25->neighbour->extended) { if ((frame[2] & 0x01) == X25_DATA) { *q = (frame[0] & X25_Q_BIT) == X25_Q_BIT; *d = (frame[0] & X25_D_BIT) == X25_D_BIT; @@ -319,14 +329,16 @@ void x25_disconnect(struct sock *sk, int reason, unsigned char cause, unsigned char diagnostic) { + x25_cb *x25 = x25_sk(sk); + x25_clear_queues(sk); x25_stop_timer(sk); - sk->protinfo.x25->lci = 0; - sk->protinfo.x25->state = X25_STATE_0; + x25->lci = 0; + x25->state = X25_STATE_0; - sk->protinfo.x25->causediag.cause = cause; - sk->protinfo.x25->causediag.diagnostic = diagnostic; + x25->causediag.cause = cause; + x25->causediag.diagnostic = diagnostic; sk->state = TCP_CLOSE; sk->err = reason; @@ -344,11 +356,13 @@ */ void x25_check_rbuf(struct sock *sk) { + x25_cb *x25 = x25_sk(sk); + if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) && - (sk->protinfo.x25->condition & X25_COND_OWN_RX_BUSY)) { - sk->protinfo.x25->condition &= ~X25_COND_OWN_RX_BUSY; - sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; - sk->protinfo.x25->vl = sk->protinfo.x25->vr; + (x25->condition & X25_COND_OWN_RX_BUSY)) { + x25->condition &= ~X25_COND_OWN_RX_BUSY; + x25->condition &= ~X25_COND_ACK_PENDING; + x25->vl = x25->vr; x25_write_internal(sk, X25_RR); x25_stop_timer(sk); } diff -Nru a/net/x25/x25_timer.c b/net/x25/x25_timer.c --- a/net/x25/x25_timer.c Tue Feb 19 18:08:57 2002 +++ b/net/x25/x25_timer.c Tue Feb 19 18:08:57 2002 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -59,59 +60,69 @@ void x25_start_t2timer(struct sock *sk) { - del_timer(&sk->protinfo.x25->timer); + x25_cb *x25 = x25_sk(sk); - sk->protinfo.x25->timer.data = (unsigned long)sk; - sk->protinfo.x25->timer.function = &x25_timer_expiry; - sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t2; + del_timer(&x25->timer); - add_timer(&sk->protinfo.x25->timer); + x25->timer.data = (unsigned long)sk; + x25->timer.function = &x25_timer_expiry; + x25->timer.expires = jiffies + x25->t2; + + add_timer(&x25->timer); } void x25_start_t21timer(struct sock *sk) { - del_timer(&sk->protinfo.x25->timer); + x25_cb *x25 = x25_sk(sk); + + del_timer(&x25->timer); - sk->protinfo.x25->timer.data = (unsigned long)sk; - sk->protinfo.x25->timer.function = &x25_timer_expiry; - sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t21; + x25->timer.data = (unsigned long)sk; + x25->timer.function = &x25_timer_expiry; + x25->timer.expires = jiffies + x25->t21; - add_timer(&sk->protinfo.x25->timer); + add_timer(&x25->timer); } void x25_start_t22timer(struct sock *sk) { - del_timer(&sk->protinfo.x25->timer); + x25_cb *x25 = x25_sk(sk); + + del_timer(&x25->timer); - sk->protinfo.x25->timer.data = (unsigned long)sk; - sk->protinfo.x25->timer.function = &x25_timer_expiry; - sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t22; + x25->timer.data = (unsigned long)sk; + x25->timer.function = &x25_timer_expiry; + x25->timer.expires = jiffies + x25->t22; - add_timer(&sk->protinfo.x25->timer); + add_timer(&x25->timer); } void x25_start_t23timer(struct sock *sk) { - del_timer(&sk->protinfo.x25->timer); + x25_cb *x25 = x25_sk(sk); - sk->protinfo.x25->timer.data = (unsigned long)sk; - sk->protinfo.x25->timer.function = &x25_timer_expiry; - sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t23; + del_timer(&x25->timer); - add_timer(&sk->protinfo.x25->timer); + x25->timer.data = (unsigned long)sk; + x25->timer.function = &x25_timer_expiry; + x25->timer.expires = jiffies + x25->t23; + + add_timer(&x25->timer); } void x25_stop_timer(struct sock *sk) { - del_timer(&sk->protinfo.x25->timer); + del_timer(&x25_sk(sk)->timer); } unsigned long x25_display_timer(struct sock *sk) { - if (!timer_pending(&sk->protinfo.x25->timer)) + x25_cb *x25 = x25_sk(sk); + + if (!timer_pending(&x25->timer)) return 0; - return sk->protinfo.x25->timer.expires - jiffies; + return x25->timer.expires - jiffies; } static void x25_heartbeat_expiry(unsigned long param) @@ -123,7 +134,7 @@ goto restart_heartbeat; } - switch (sk->protinfo.x25->state) { + switch (x25_sk(sk)->state) { case X25_STATE_0: /* Magic here: If we listen() and a new link dies before it @@ -153,11 +164,13 @@ */ static inline void x25_do_timer_expiry(struct sock * sk) { - switch (sk->protinfo.x25->state) { + x25_cb *x25 = x25_sk(sk); + + switch (x25->state) { case X25_STATE_3: /* T2 */ - if (sk->protinfo.x25->condition & X25_COND_ACK_PENDING) { - sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; + if (x25->condition & X25_COND_ACK_PENDING) { + x25->condition &= ~X25_COND_ACK_PENDING; x25_enquiry_response(sk); } break; @@ -165,7 +178,7 @@ case X25_STATE_1: /* T21 */ case X25_STATE_4: /* T22 */ x25_write_internal(sk, X25_CLEAR_REQUEST); - sk->protinfo.x25->state = X25_STATE_2; + x25->state = X25_STATE_2; x25_start_t23timer(sk); break; @@ -181,7 +194,7 @@ bh_lock_sock(sk); if (sk->lock.users) { /* can currently only occur in state 3 */ - if (sk->protinfo.x25->state == X25_STATE_3) { + if (x25_sk(sk)->state == X25_STATE_3) { x25_start_t2timer(sk); } } else { diff -Nru a/scripts/Menuconfig b/scripts/Menuconfig --- a/scripts/Menuconfig Tue Feb 19 18:08:58 2002 +++ b/scripts/Menuconfig Tue Feb 19 18:08:58 2002 @@ -689,7 +689,7 @@ # Call awk, and watch for error codes, etc. # function callawk () { -awk "$1" || echo "Awk died with error code $?. Giving up." || exit 1 +awk "$1" || { echo "Awk died with error code $?. Giving up."; exit 1; } } # diff -Nru a/sound/Config.in b/sound/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/Config.in Tue Feb 19 18:09:00 2002 @@ -0,0 +1,32 @@ +# sound/Config.in +# + +mainmenu_option next_comment +comment 'Open Sound System' + +dep_tristate 'Open Sound System' CONFIG_SOUND_PRIME $CONFIG_SOUND +if [ "$CONFIG_SOUND_PRIME" != "n" ]; then + source sound/oss/Config.in +fi + +endmenu + +mainmenu_option next_comment +comment 'Advanced Linux Sound Architecture' + +dep_tristate 'Advanced Linux Sound Architecture' CONFIG_SND $CONFIG_SOUND +if [ "$CONFIG_SND" != "n" ]; then + source sound/core/Config.in + source sound/drivers/Config.in +fi +if [ "$CONFIG_SND" != "n" -a "$CONFIG_ISA" = "y" ]; then + source sound/isa/Config.in +fi +if [ "$CONFIG_SND" != "n" -a "$CONFIG_PCI" = "y" ]; then + source sound/pci/Config.in +fi +if [ "$CONFIG_SND" != "n" -a "$CONFIG_PPC" = "y" ]; then + source sound/ppc/Config.in +fi + +endmenu diff -Nru a/sound/Makefile b/sound/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,60 @@ +# Makefile for the Linux sound card driver +# + +O_TARGET := sound.o + +export-objs := sound_core.o + +obj-$(CONFIG_SOUND) += soundcore.o + +subdir-$(CONFIG_SOUND_PRIME) += oss + +ifeq ($(CONFIG_SOUND_PRIME),y) + obj-y += oss/sounddrivers.o +endif + +_subdirs := core i2c drivers isa pci ppc synth + +subdir-$(CONFIG_SND) += $(_subdirs) + +ifeq ($(CONFIG_SND),y) + subdir-m += $(_subdirs) + obj-y += core/core.o i2c/_i2c.o + obj-y += drivers/drivers.o \ + drivers/mpu401/_mpu401.o \ + drivers/opl3/_opl3.o + obj-y += isa/isa.o \ + isa/ad1816a/_ad1816a.o \ + isa/ad1848/_ad1848.o \ + isa/cs423x/_cs423x.o \ + isa/es1688/_es1688.o \ + isa/gus/_gus.o \ + isa/opti9xx/_opti9xx.o \ + isa/sb/_sb.o \ + isa/wavefront/_wavefront.o + obj-y += pci/pci.o \ + pci/ac97/_ac97.o \ + pci/ali5451/_ali5451.o \ + pci/cs46xx/_cs46xx.o \ + pci/emu10k1/_emu10k1.o \ + pci/korg1212/_korg1212.o \ + pci/nm256/_nm256.o \ + pci/rme9652/_rme9652.o \ + pci/trident/_trident.o \ + pci/ymfpci/_ymfpci.o + obj-y += ppc/ppc.o + obj-y += synth/synth.o \ + synth/emux/_emux.o + obj-y += last.o +endif + +list-multi := soundcore.o + +soundcore-objs := sound_core.o sound_firmware.o + + +include $(TOPDIR)/Rules.make + + +soundcore.o: $(soundcore-objs) + $(LD) -r -o $@ $(soundcore-objs) diff -Nru a/sound/core/Config.in b/sound/core/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/Config.in Tue Feb 19 18:09:00 2002 @@ -0,0 +1,21 @@ +# ALSA soundcard-configuration + +dep_tristate ' RTC Timer support' CONFIG_SND_RTCTIMER $CONFIG_SND +dep_tristate ' Sequencer support' CONFIG_SND_SEQUENCER $CONFIG_SND +if [ "$CONFIG_SND_SEQUENCER" != "n" ]; then + dep_tristate ' Sequencer dummy client' CONFIG_SND_SEQ_DUMMY $CONFIG_SND_SEQUENCER +fi +bool ' OSS API emulation' CONFIG_SND_OSSEMUL $CONFIG_SND +if [ "$CONFIG_SND_OSSEMUL" = "y" ]; then + dep_tristate ' OSS Mixer API' CONFIG_SND_MIXER_OSS $CONFIG_SND + dep_tristate ' OSS PCM API' CONFIG_SND_PCM_OSS $CONFIG_SND + if [ "$CONFIG_SND_SEQUENCER" != "n" ]; then + dep_tristate ' OSS Sequencer API' CONFIG_SND_SEQUENCER_OSS $CONFIG_SND_SEQUENCER + fi +fi +bool ' Debug' CONFIG_SND_DEBUG +if [ "$CONFIG_SND_DEBUG" = "y" ]; then + bool ' Debug memory' CONFIG_SND_DEBUG_MEMORY + bool ' Debug full' CONFIG_SND_DEBUG_FULL + bool ' Debug detection' CONFIG_SND_DEBUG_DETECT +fi diff -Nru a/sound/core/Makefile b/sound/core/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,120 @@ +# +# Makefile for ALSA +# Copyright (c) 1999,2001 by Jaroslav Kysela +# + +O_TARGET := core.o + +export-objs := sound.o pcm.o pcm_lib.o rawmidi.o timer.o rtctimer.o hwdep.o + +list-multi := snd.o snd-pcm.o snd-rawmidi.o snd-timer.o snd-rtctimer.o snd-hwdep.o + +snd-objs := sound.o init.o isadma.o memory.o info.o control.o misc.o \ + device.o wrappers.o +ifeq ($(CONFIG_SND_OSSEMUL),y) +snd-objs += sound_oss.o info_oss.o +endif + +snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ + pcm_memory.o + +snd-rawmidi-objs := rawmidi.o +snd-timer-objs := timer.o +snd-rtctimer-objs := rtctimer.o +snd-hwdep-objs := hwdep.o + +obj-$(CONFIG_SND) += snd.o +ifeq ($(CONFIG_RTC),y) + obj-$(CONFIG_SND_RTCTIMER) += snd-timer.o + obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o +endif +obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o + +subdir-$(CONFIG_SND_MIXER_OSS) += oss +subdir-$(CONFIG_SND_PCM_OSS) += oss +ifeq ($(filter $(subdir-y),oss),oss) + obj-y += oss/oss.o +endif + +subdir-$(CONFIG_SND_SEQUENCER) += seq +ifeq ($(CONFIG_SND_SEQUENCER),y) + ifeq ($(CONFIG_SND),y) + obj-y += seq/sq.o + endif +endif + +# Toplevel Module Dependency +obj-$(CONFIG_SND_DUMMY) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_VIRMIDI) += snd-rawmidi.o snd.o snd-timer.o +obj-$(CONFIG_SND_MTPAV) += snd-rawmidi.o snd.o snd-timer.o +obj-$(CONFIG_SND_MPU401) += snd-rawmidi.o snd.o snd-timer.o +obj-$(CONFIG_SND_ALS100) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_AZT2320) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CMI8330) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_DT0197H) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ES18XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_OPL3SA2) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SGALAXY) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_AD1816A) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_AD1848) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_CS4231) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_CS4232) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CS4236) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ES1688) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_GUSCLASSIC) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_GUSMAX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_INTERWAVE) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_OPTI93X) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SB) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd.o snd-hwdep.o snd-rawmidi.o +obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd.o snd-hwdep.o snd-rawmidi.o +obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CMIPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CS4281) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ENS1370) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_ENS1371) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_ES1938) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_VIA686) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_VIA8233) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-pcm.o +obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_KORG1212) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_NM256) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_RME9652) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd.o + +include $(TOPDIR)/Rules.make + +snd.o: $(snd-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-objs) + +snd-pcm.o: $(snd-pcm-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-pcm-objs) + +snd-rawmidi.o: $(snd-rawmidi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rawmidi-objs) + +snd-timer.o: $(snd-timer-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-timer-objs) + +snd-rtctimer.o: $(snd-rtctimer-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rtctimer-objs) + +snd-hwdep.o: $(snd-hwdep-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-hwdep-objs) diff -Nru a/sound/core/control.c b/sound/core/control.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/control.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,815 @@ +/* + * Routines for driver control interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _snd_kctl_ioctl { + struct list_head list; /* list of all ioctls */ + snd_kctl_ioctl_func_t fioctl; +} snd_kctl_ioctl_t; + +#define snd_kctl_ioctl(n) list_entry(n, snd_kctl_ioctl_t, list) + +static rwlock_t snd_ioctl_rwlock = RW_LOCK_UNLOCKED; +static LIST_HEAD(snd_control_ioctls); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +static int snd_ctl_open(struct inode *inode, struct file *file) +{ + int cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + unsigned long flags; + snd_card_t *card; + snd_ctl_file_t *ctl; + int err; + +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + card = snd_cards[cardnum]; + if (!card) { + err = -ENODEV; + goto __error1; + } + if (!try_inc_mod_count(card->module)) { + err = -EFAULT; + goto __error1; + } + ctl = snd_magic_kcalloc(snd_ctl_file_t, 0, GFP_KERNEL); + if (ctl == NULL) { + err = -ENOMEM; + goto __error; + } + INIT_LIST_HEAD(&ctl->events); + init_waitqueue_head(&ctl->change_sleep); + spin_lock_init(&ctl->read_lock); + ctl->card = card; + ctl->pid = current->pid; + file->private_data = ctl; + write_lock_irqsave(&card->control_rwlock, flags); + list_add_tail(&ctl->list, &card->ctl_files); + write_unlock_irqrestore(&card->control_rwlock, flags); + return 0; + + __error: + dec_mod_count(card->module); + __error1: +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return err; +} + +static void snd_ctl_empty_read_queue(snd_ctl_file_t * ctl) +{ + snd_kctl_event_t *cread; + + spin_lock(&ctl->read_lock); + while (!list_empty(&ctl->events)) { + cread = snd_kctl_event(ctl->events.next); + list_del(&cread->list); + kfree(cread); + } + spin_unlock(&ctl->read_lock); +} + +static int snd_ctl_release(struct inode *inode, struct file *file) +{ + unsigned long flags; + struct list_head *list; + snd_card_t *card; + snd_ctl_file_t *ctl; + snd_kcontrol_t *control; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + fasync_helper(-1, file, 0, &ctl->fasync); + file->private_data = NULL; + card = ctl->card; + write_lock_irqsave(&card->control_rwlock, flags); + list_del(&ctl->list); + write_unlock_irqrestore(&card->control_rwlock, flags); + write_lock(&card->control_owner_lock); + list_for_each(list, &card->controls) { + control = snd_kcontrol(list); + if (control->owner == ctl) + control->owner = NULL; + } + write_unlock(&card->control_owner_lock); + snd_ctl_empty_read_queue(ctl); + snd_magic_kfree(ctl); + dec_mod_count(card->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +void snd_ctl_notify(snd_card_t *card, unsigned int mask, snd_ctl_elem_id_t *id) +{ + unsigned long flags; + struct list_head *flist; + snd_ctl_file_t *ctl; + snd_kctl_event_t *ev; + + snd_runtime_check(card != NULL && id != NULL, return); + read_lock_irqsave(&card->control_rwlock, flags); +#ifdef CONFIG_SND_OSSEMUL + card->mixer_oss_change_count++; +#endif + list_for_each(flist, &card->ctl_files) { + struct list_head *elist; + ctl = snd_ctl_file(flist); + if (!ctl->subscribed) + continue; + spin_lock(&ctl->read_lock); + list_for_each(elist, &ctl->events) { + ev = snd_kctl_event(elist); + if (ev->id.numid == id->numid) { + ev->mask |= mask; + goto _found; + } + } + ev = snd_kcalloc(sizeof(*ev), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (ev) { + ev->id = *id; + ev->mask = mask; + list_add_tail(&ev->list, &ctl->events); + } else { + snd_printk("No memory available to allocate event\n"); + } + _found: + wake_up(&ctl->change_sleep); + kill_fasync(&ctl->fasync, SIGIO, POLL_IN); + spin_unlock(&ctl->read_lock); + } + read_unlock_irqrestore(&card->control_rwlock, flags); +} + +snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control) +{ + snd_kcontrol_t *kctl; + + snd_runtime_check(control != NULL, return NULL); + kctl = (snd_kcontrol_t *)snd_magic_kmalloc(snd_kcontrol_t, 0, GFP_KERNEL); + if (kctl == NULL) + return NULL; + *kctl = *control; + return kctl; +} + +snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data) +{ + snd_kcontrol_t kctl; + + snd_runtime_check(ncontrol != NULL, return NULL); + snd_assert(ncontrol->info != NULL, return NULL); + memset(&kctl, 0, sizeof(kctl)); + kctl.id.iface = ncontrol->iface; + kctl.id.device = ncontrol->device; + kctl.id.subdevice = ncontrol->subdevice; + strncpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)-1); + kctl.id.index = ncontrol->index; + kctl.access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : + (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); + kctl.info = ncontrol->info; + kctl.get = ncontrol->get; + kctl.put = ncontrol->put; + kctl.private_value = ncontrol->private_value; + kctl.private_data = private_data; + return snd_ctl_new(&kctl); +} + +void snd_ctl_free_one(snd_kcontrol_t * kcontrol) +{ + if (kcontrol) { + if (kcontrol->private_free) + kcontrol->private_free(kcontrol); + snd_magic_kfree(kcontrol); + } +} + +int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol) +{ + snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); + snd_assert(kcontrol->info != NULL, return -EINVAL); + snd_assert(!(kcontrol->access & SNDRV_CTL_ELEM_ACCESS_READ) || kcontrol->get != NULL, return -EINVAL); + snd_assert(!(kcontrol->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kcontrol->put != NULL, return -EINVAL); + write_lock(&card->control_rwlock); + list_add_tail(&kcontrol->list, &card->controls); + card->controls_count++; + kcontrol->id.numid = ++card->last_numid; + write_unlock(&card->control_rwlock); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &kcontrol->id); + return 0; +} + +int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol) +{ + snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); + write_lock(&card->control_rwlock); + list_del(&kcontrol->list); + card->controls_count--; + write_unlock(&card->control_rwlock); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &kcontrol->id); + snd_ctl_free_one(kcontrol); + return 0; +} + +int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id) +{ + snd_kcontrol_t *kctl; + + kctl = snd_ctl_find_id(card, id); + if (kctl == NULL) + return -ENOENT; + return snd_ctl_remove(card, kctl); +} + +int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id) +{ + snd_kcontrol_t *kctl; + + kctl = snd_ctl_find_id(card, src_id); + if (kctl == NULL) + return -ENOENT; + write_lock(&card->control_rwlock); + kctl->id = *dst_id; + kctl->id.numid = ++card->last_numid; + write_unlock(&card->control_rwlock); + return 0; +} + +snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid) +{ + struct list_head *list; + snd_kcontrol_t *kctl; + + snd_runtime_check(card != NULL && numid != 0, return NULL); + read_lock(&card->control_rwlock); + list_for_each(list, &card->controls) { + kctl = snd_kcontrol(list); + if (kctl->id.numid == numid) { + read_unlock(&card->control_rwlock); + return kctl; + } + } + read_unlock(&card->control_rwlock); + return NULL; +} + +snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) +{ + struct list_head *list; + snd_kcontrol_t *kctl; + + snd_runtime_check(card != NULL && id != NULL, return NULL); + if (id->numid != 0) + return snd_ctl_find_numid(card, id->numid); + read_lock(&card->control_rwlock); + list_for_each(list, &card->controls) { + kctl = snd_kcontrol(list); + if (kctl->id.iface != id->iface) + continue; + if (kctl->id.device != id->device) + continue; + if (kctl->id.subdevice != id->subdevice) + continue; + if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name))) + continue; + if (kctl->id.index != id->index) + continue; + read_unlock(&card->control_rwlock); + return kctl; + } + read_unlock(&card->control_rwlock); + return NULL; +} + +static int snd_ctl_card_info(snd_card_t * card, snd_ctl_file_t * ctl, + unsigned int cmd, unsigned long arg) +{ + snd_ctl_card_info_t info; + + memset(&info, 0, sizeof(info)); + read_lock(&snd_ioctl_rwlock); + info.card = card->number; + strncpy(info.id, card->id, sizeof(info.id) - 1); + strncpy(info.driver, card->driver, sizeof(info.driver) - 1); + strncpy(info.name, card->shortname, sizeof(info.name) - 1); + strncpy(info.longname, card->longname, sizeof(info.longname) - 1); + strncpy(info.mixername, card->mixername, sizeof(info.mixername) - 1); + strncpy(info.components, card->components, sizeof(info.components) - 1); + read_unlock(&snd_ioctl_rwlock); + if (copy_to_user((void *) arg, &info, sizeof(snd_ctl_card_info_t))) + return -EFAULT; + return 0; +} + +static int snd_ctl_elem_list(snd_card_t *card, snd_ctl_elem_list_t *_list) +{ + struct list_head *plist; + snd_ctl_elem_list_t list; + snd_kcontrol_t *kctl; + snd_ctl_elem_id_t *dst, *id; + int offset, space; + + if (copy_from_user(&list, _list, sizeof(list))) + return -EFAULT; + offset = list.offset; + space = list.space; + /* try limit maximum space */ + if (space > 16384) + return -ENOMEM; + if (space > 0) { + /* allocate temporary buffer for atomic operation */ + dst = vmalloc(space * sizeof(snd_ctl_elem_id_t)); + if (dst == NULL) + return -ENOMEM; + read_lock(&card->control_rwlock); + list.count = card->controls_count; + plist = card->controls.next; + while (offset-- > 0 && plist != &card->controls) + plist = plist->next; + list.used = 0; + id = dst; + while (space > 0 && plist != &card->controls) { + kctl = snd_kcontrol(plist); + memcpy(id, &kctl->id, sizeof(snd_ctl_elem_id_t)); + id++; + plist = plist->next; + space--; + list.used++; + } + read_unlock(&card->control_rwlock); + if (list.used > 0 && copy_to_user(list.pids, dst, list.used * sizeof(snd_ctl_elem_id_t))) + return -EFAULT; + vfree(dst); + } else { + read_lock(&card->control_rwlock); + list.count = card->controls_count; + read_unlock(&card->control_rwlock); + } + if (copy_to_user(_list, &list, sizeof(list))) + return -EFAULT; + return 0; +} + +static int snd_ctl_elem_info(snd_ctl_file_t *ctl, snd_ctl_elem_info_t *_info) +{ + snd_card_t *card = ctl->card; + snd_ctl_elem_info_t info; + snd_kcontrol_t *kctl; + int result; + + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + read_lock(&card->control_rwlock); + kctl = snd_ctl_find_id(card, &info.id); + if (kctl == NULL) { + read_unlock(&card->control_rwlock); + return -ENOENT; + } +#ifdef CONFIG_SND_DEBUG + info.access = 0; +#endif + result = kctl->info(kctl, &info); + if (result >= 0) { + snd_assert(info.access == 0, ); + info.id = kctl->id; + info.access = kctl->access; + if (kctl->owner) { + info.access |= SNDRV_CTL_ELEM_ACCESS_LOCK; + if (kctl->owner == ctl) + info.access |= SNDRV_CTL_ELEM_ACCESS_OWNER; + info.owner = kctl->owner_pid; + } else { + info.owner = -1; + } + } + read_unlock(&card->control_rwlock); + if (result >= 0) + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return result; +} + +static int snd_ctl_elem_read(snd_card_t *card, snd_ctl_elem_value_t *_control) +{ + snd_ctl_elem_value_t control; + snd_kcontrol_t *kctl; + int result, indirect; + + if (copy_from_user(&control, _control, sizeof(control))) + return -EFAULT; + read_lock(&card->control_rwlock); + kctl = snd_ctl_find_id(card, &control.id); + if (kctl == NULL) { + result = -ENOENT; + } else { + indirect = kctl->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0; + if (control.indirect != indirect) { + result = -EACCES; + } else { + if ((kctl->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) { + result = kctl->get(kctl, &control); + if (result >= 0) + control.id = kctl->id; + } else + result = -EPERM; + } + } + read_unlock(&card->control_rwlock); + if (result >= 0) + if (copy_to_user(_control, &control, sizeof(control))) + return -EFAULT; + return result; +} + +static int snd_ctl_elem_write(snd_ctl_file_t *file, snd_ctl_elem_value_t *_control) +{ + snd_card_t *card = file->card; + snd_ctl_elem_value_t control; + snd_kcontrol_t *kctl; + int result, indirect; + + if (copy_from_user(&control, _control, sizeof(control))) + return -EFAULT; + read_lock(&card->control_rwlock); + kctl = snd_ctl_find_id(card, &control.id); + if (kctl == NULL) { + result = -ENOENT; + } else { + indirect = kctl->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0; + if (control.indirect != indirect) { + result = -EACCES; + } else { + read_lock(&card->control_owner_lock); + if (!(kctl->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || + kctl->put == NULL || + (kctl->owner != NULL && kctl->owner != file)) { + result = -EPERM; + } else { + result = kctl->put(kctl, &control); + if (result >= 0) + control.id = kctl->id; + } + read_unlock(&card->control_owner_lock); + if (result > 0) { + result = 0; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + } + } + } + read_unlock(&card->control_rwlock); + if (result >= 0) + if (copy_to_user(_control, &control, sizeof(control))) + return -EFAULT; + return result; +} + +static int snd_ctl_elem_lock(snd_ctl_file_t *file, snd_ctl_elem_id_t *_id) +{ + snd_card_t *card = file->card; + snd_ctl_elem_id_t id; + snd_kcontrol_t *kctl; + int result; + + if (copy_from_user(&id, _id, sizeof(id))) + return -EFAULT; + read_lock(&card->control_rwlock); + kctl = snd_ctl_find_id(card, &id); + if (kctl == NULL) { + result = -ENOENT; + } else { + write_lock(&card->control_owner_lock); + if (kctl->owner != NULL) + result = -EBUSY; + else { + kctl->owner = file; + kctl->owner_pid = current->pid; + result = 0; + } + write_unlock(&card->control_owner_lock); + } + read_unlock(&card->control_rwlock); + return result; +} + +static int snd_ctl_elem_unlock(snd_ctl_file_t *file, snd_ctl_elem_id_t *_id) +{ + snd_card_t *card = file->card; + snd_ctl_elem_id_t id; + snd_kcontrol_t *kctl; + int result; + + if (copy_from_user(&id, _id, sizeof(id))) + return -EFAULT; + read_lock(&card->control_rwlock); + kctl = snd_ctl_find_id(card, &id); + if (kctl == NULL) { + result = -ENOENT; + } else { + write_lock(&card->control_owner_lock); + if (kctl->owner == NULL) + result = -EINVAL; + else if (kctl->owner != file) + result = -EPERM; + else { + kctl->owner = NULL; + kctl->owner_pid = 0; + result = 0; + } + write_unlock(&card->control_owner_lock); + } + read_unlock(&card->control_rwlock); + return result; +} + +static int snd_ctl_subscribe_events(snd_ctl_file_t *file, int *ptr) +{ + int subscribe; + if (get_user(subscribe, ptr)) + return -EFAULT; + if (subscribe < 0) { + subscribe = file->subscribed; + if (put_user(subscribe, ptr)) + return -EFAULT; + return 0; + } + if (subscribe) { + file->subscribed = 1; + return 0; + } else if (file->subscribed) { + snd_ctl_empty_read_queue(file); + file->subscribed = 0; + } + return 0; +} + +static int snd_ctl_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_ctl_file_t *ctl; + snd_card_t *card; + struct list_head *list; + snd_kctl_ioctl_t *p; + int err; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + card = ctl->card; + snd_assert(card != NULL, return -ENXIO); + switch (cmd) { + case SNDRV_CTL_IOCTL_PVERSION: + return put_user(SNDRV_CTL_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_CTL_IOCTL_CARD_INFO: + return snd_ctl_card_info(card, ctl, cmd, arg); + case SNDRV_CTL_IOCTL_ELEM_LIST: + return snd_ctl_elem_list(ctl->card, (snd_ctl_elem_list_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_INFO: + return snd_ctl_elem_info(ctl, (snd_ctl_elem_info_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_READ: + return snd_ctl_elem_read(ctl->card, (snd_ctl_elem_value_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_WRITE: + return snd_ctl_elem_write(ctl, (snd_ctl_elem_value_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_LOCK: + return snd_ctl_elem_lock(ctl, (snd_ctl_elem_id_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_UNLOCK: + return snd_ctl_elem_unlock(ctl, (snd_ctl_elem_id_t *) arg); + case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: + return snd_ctl_subscribe_events(ctl, (int *) arg); + case SNDRV_CTL_IOCTL_POWER: + if (get_user(err, (int *)arg)) + return -EFAULT; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; +#ifdef CONFIG_PM + if (card->set_power_state == NULL) + return -ENOPROTOOPT; + return card->set_power_state(card, err); +#else + return -ENOPROTOOPT; +#endif + case SNDRV_CTL_IOCTL_POWER_STATE: +#ifdef CONFIG_PM + return put_user(card->power_state, (int *)arg) ? -EFAULT : 0; +#else + return put_user(SNDRV_CTL_POWER_D0, (int *)arg) ? -EFAULT : 0; +#endif + } + read_lock(&snd_ioctl_rwlock); + list_for_each(list, &snd_control_ioctls) { + p = list_entry(list, snd_kctl_ioctl_t, list); + err = p->fioctl(card, ctl, cmd, arg); + if (err != -ENOIOCTLCMD) { + read_unlock(&snd_ioctl_rwlock); + return err; + } + } + read_unlock(&snd_ioctl_rwlock); + snd_printd("unknown ioctl = 0x%x\n", cmd); + return -ENOTTY; +} + +static ssize_t snd_ctl_read(struct file *file, char *buffer, size_t count, loff_t * offset) +{ + snd_ctl_file_t *ctl; + int err = 0; + ssize_t result = 0; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO); + if (!ctl->subscribed) + return -EBADFD; + if (count < sizeof(snd_ctl_event_t)) + return -EINVAL; + spin_lock_irq(&ctl->read_lock); + while (count >= sizeof(snd_ctl_event_t)) { + snd_ctl_event_t ev; + snd_kctl_event_t *kev; + while (list_empty(&ctl->events)) { + wait_queue_t wait; + if (file->f_flags & O_NONBLOCK) { + err = -EAGAIN; + goto __end; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&ctl->change_sleep, &wait); + spin_unlock_irq(&ctl->read_lock); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&ctl->change_sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + spin_lock_irq(&ctl->read_lock); + } + kev = snd_kctl_event(ctl->events.next); + ev.type = SNDRV_CTL_EVENT_ELEM; + ev.data.elem.mask = kev->mask; + ev.data.elem.id = kev->id; + list_del(&kev->list); + spin_unlock_irq(&ctl->read_lock); + kfree(kev); + if (copy_to_user(buffer, &ev, sizeof(snd_ctl_event_t))) { + err = -EFAULT; + goto __end; + } + spin_lock_irq(&ctl->read_lock); + buffer += sizeof(snd_ctl_event_t); + count -= sizeof(snd_ctl_event_t); + result += sizeof(snd_ctl_event_t); + } + __end: + spin_unlock_irq(&ctl->read_lock); + return result > 0 ? result : err; +} + +static unsigned int snd_ctl_poll(struct file *file, poll_table * wait) +{ + unsigned int mask; + snd_ctl_file_t *ctl; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return 0); + if (!ctl->subscribed) + return 0; + poll_wait(file, &ctl->change_sleep, wait); + + mask = 0; + if (!list_empty(&ctl->events)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) +{ + snd_kctl_ioctl_t *pn; + + pn = (snd_kctl_ioctl_t *) + snd_kcalloc(sizeof(snd_kctl_ioctl_t), GFP_KERNEL); + if (pn == NULL) + return -ENOMEM; + pn->fioctl = fcn; + write_lock(&snd_ioctl_rwlock); + list_add_tail(&pn->list, &snd_control_ioctls); + write_unlock(&snd_ioctl_rwlock); + return 0; +} + +int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) +{ + struct list_head *list; + snd_kctl_ioctl_t *p; + + snd_runtime_check(fcn != NULL, return -EINVAL); + write_lock(&snd_ioctl_rwlock); + list_for_each(list, &snd_control_ioctls) { + p = list_entry(list, snd_kctl_ioctl_t, list); + if (p->fioctl == fcn) { + list_del(&p->list); + write_unlock(&snd_ioctl_rwlock); + kfree(p); + return 0; + } + } + write_unlock(&snd_ioctl_rwlock); + snd_BUG(); + return -EINVAL; +} + +static int snd_ctl_fasync(int fd, struct file * file, int on) +{ + snd_ctl_file_t *ctl; + int err; + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + err = fasync_helper(fd, file, on, &ctl->fasync); + if (err < 0) + return err; + return 0; +} + +/* + * INIT PART + */ + +static struct file_operations snd_ctl_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_ctl_read, + open: snd_ctl_open, + release: snd_ctl_release, + poll: snd_ctl_poll, + ioctl: snd_ctl_ioctl, + fasync: snd_ctl_fasync, +}; + +static snd_minor_t snd_ctl_reg = +{ + comment: "ctl", + f_ops: &snd_ctl_f_ops, +}; + +int snd_ctl_register(snd_card_t *card) +{ + int err, cardnum; + char name[16]; + + snd_assert(card != NULL, return -ENXIO); + cardnum = card->number; + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + sprintf(name, "controlC%i", cardnum); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, + card, 0, &snd_ctl_reg, name)) < 0) + return err; + return 0; +} + +int snd_ctl_unregister(snd_card_t *card) +{ + int err, cardnum; + snd_kcontrol_t *control; + + snd_assert(card != NULL, return -ENXIO); + cardnum = card->number; + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, card, 0)) < 0) + return err; + while (!list_empty(&card->controls)) { + control = snd_kcontrol(card->controls.next); + snd_ctl_remove(card, control); + } + return 0; +} diff -Nru a/sound/core/device.c b/sound/core/device.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/device.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,135 @@ +/* + * Device management routines + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +int snd_device_new(snd_card_t *card, snd_device_type_t type, + void *device_data, snd_device_ops_t *ops) +{ + snd_device_t *dev; + + snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO); + dev = (snd_device_t *) snd_magic_kcalloc(snd_device_t, 0, GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + dev->card = card; + dev->type = type; + dev->state = SNDRV_DEV_BUILD; + dev->device_data = device_data; + dev->ops = ops; + list_add(&dev->list, &card->devices); /* add to the head of list */ + return 0; +} + +int snd_device_free(snd_card_t *card, void *device_data) +{ + struct list_head *list; + snd_device_t *dev; + + snd_assert(card != NULL, return -ENXIO); + snd_assert(device_data != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->device_data != device_data) + continue; + /* unlink */ + list_del(&dev->list); + if (dev->state == SNDRV_DEV_REGISTERED && dev->ops->dev_unregister) { + if (dev->ops->dev_unregister(dev)) + snd_printk("device unregister failure\n"); + } else { + if (dev->ops->dev_free) { + if (dev->ops->dev_free(dev)) + snd_printk("device free failure\n"); + } + } + snd_magic_kfree(dev); + return 0; + } + snd_printd("device free %p (from %p), not found\n", device_data, __builtin_return_address(0)); + return -ENXIO; +} + +int snd_device_register(snd_card_t *card, void *device_data) +{ + struct list_head *list; + snd_device_t *dev; + int err; + + snd_assert(card != NULL && device_data != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->device_data != device_data) + continue; + if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { + if ((err = dev->ops->dev_register(dev)) < 0) + return err; + dev->state = SNDRV_DEV_REGISTERED; + return 0; + } + return -EBUSY; + } + snd_BUG(); + return -ENXIO; +} + +int snd_device_register_all(snd_card_t *card) +{ + struct list_head *list; + snd_device_t *dev; + int err; + + snd_assert(card != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { + if ((err = dev->ops->dev_register(dev)) < 0) + return err; + dev->state = SNDRV_DEV_REGISTERED; + } + } + return 0; +} + +int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd) +{ + snd_device_t *dev; + struct list_head *list; + int err, range_low, range_high; + + snd_assert(card != NULL, return -ENXIO); + range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE; + range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1; + __again: + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->type >= range_low && dev->type <= range_high) { + if ((err = snd_device_free(card, dev->device_data)) < 0) + return err; + goto __again; + } + } + return 0; +} diff -Nru a/sound/core/hwdep.c b/sound/core/hwdep.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/hwdep.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,436 @@ +/* + * Hardware dependent layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Hardware dependent layer"); +MODULE_LICENSE("GPL"); + +snd_hwdep_t *snd_hwdep_devices[SNDRV_CARDS * SNDRV_MINOR_HWDEPS]; + +static DECLARE_MUTEX(register_mutex); + +static int snd_hwdep_free(snd_hwdep_t *hwdep); +static int snd_hwdep_dev_free(snd_device_t *device); +static int snd_hwdep_dev_register(snd_device_t *device); +static int snd_hwdep_dev_unregister(snd_device_t *device); + +/* + + */ + +static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.llseek) + return hw->ops.llseek(hw, file, offset, orig); + return -ENXIO; +} + +static ssize_t snd_hwdep_read(struct file * file, char *buf, size_t count, loff_t *offset) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.read) + return hw->ops.read(hw, buf, count, offset); + return -ENXIO; +} + +static ssize_t snd_hwdep_write(struct file * file, const char *buf, size_t count, loff_t *offset) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.write) + return hw->ops.write(hw, buf, count, offset); + return -ENXIO; +} + +static int snd_hwdep_open(struct inode *inode, struct file * file) +{ + int major = major(inode->i_rdev); + int cardnum; + int device; + snd_hwdep_t *hw; + int err; + wait_queue_t wait; + + switch (major) { + case CONFIG_SND_MAJOR: + cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)) - SNDRV_MINOR_HWDEP; + break; +#ifdef CONFIG_SND_OSSEMUL + case SOUND_MAJOR: + cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev)); + device = 0; + break; +#endif + default: + return -ENXIO; + } + cardnum %= SNDRV_CARDS; + device %= SNDRV_MINOR_HWDEPS; + hw = snd_hwdep_devices[(cardnum * SNDRV_MINOR_HWDEPS) + device]; + + snd_assert(hw != NULL, return -ENODEV); + if (!hw->ops.open) + return -ENXIO; +#ifdef CONFIG_SND_OSSEMUL + if (major == SOUND_MAJOR && hw->oss_type < 0) + return -ENXIO; +#endif + init_waitqueue_entry(&wait, current); + add_wait_queue(&hw->open_wait, &wait); + while (1) { + err = hw->ops.open(hw, file); + if (err >= 0) + break; + if (err == -EAGAIN) { + if (file->f_flags & O_NONBLOCK) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&hw->open_wait, &wait); + if (err >= 0) + file->private_data = hw; + return err; +} + +static int snd_hwdep_release(struct inode *inode, struct file * file) +{ + int err; + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.release) { + err = hw->ops.release(hw, file); + wake_up(&hw->open_wait); + return err; + } + return -ENXIO; +} + +static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return 0); + if (hw->ops.poll) + return hw->ops.poll(hw, file, wait); + return 0; +} + +static int snd_hwdep_info(snd_hwdep_t *hw, snd_hwdep_info_t *_info) +{ + snd_hwdep_info_t info; + + memset(&info, 0, sizeof(info)); + info.card = hw->card->number; + strncpy(info.id, hw->id, sizeof(info.id) - 1); + strncpy(info.name, hw->name, sizeof(info.name) - 1); + info.iface = hw->iface; + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_hwdep_ioctl(struct inode *inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (cmd == SNDRV_HWDEP_IOCTL_PVERSION) + return put_user(SNDRV_HWDEP_VERSION, (int *)arg); + if (cmd == SNDRV_HWDEP_IOCTL_INFO) + return snd_hwdep_info(hw, (snd_hwdep_info_t *)arg); + if (hw->ops.ioctl) + return hw->ops.ioctl(hw, file, cmd, arg); + return -ENOTTY; +} + +static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.mmap) + return hw->ops.mmap(hw, file, vma); + return -ENXIO; +} + +static int snd_hwdep_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg) +{ + unsigned int tmp; + + tmp = card->number * SNDRV_MINOR_HWDEPS; + switch (cmd) { + case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE: + { + int device; + + if (get_user(device, (int *)arg)) + return -EFAULT; + device = device < 0 ? 0 : device + 1; + while (device < SNDRV_MINOR_HWDEPS) { + if (snd_hwdep_devices[tmp + device]) + break; + device++; + } + if (device >= SNDRV_MINOR_HWDEPS) + device = -1; + if (put_user(device, (int *)arg)) + return -EFAULT; + return 0; + } + case SNDRV_CTL_IOCTL_HWDEP_INFO: + { + snd_hwdep_info_t *info = (snd_hwdep_info_t *)arg; + int device; + snd_hwdep_t *hwdep; + + if (get_user(device, &info->device)) + return -EFAULT; + if (device < 0 || device >= SNDRV_MINOR_HWDEPS) + return -ENXIO; + hwdep = snd_hwdep_devices[tmp + device]; + if (hwdep == NULL) + return -ENXIO; + return snd_hwdep_info(hwdep, info); + } + } + return -ENOIOCTLCMD; +} + +/* + + */ + +static struct file_operations snd_hwdep_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + llseek: snd_hwdep_llseek, + read: snd_hwdep_read, + write: snd_hwdep_write, + open: snd_hwdep_open, + release: snd_hwdep_release, + poll: snd_hwdep_poll, + ioctl: snd_hwdep_ioctl, + mmap: snd_hwdep_mmap, +}; + +static snd_minor_t snd_hwdep_reg = +{ + comment: "hardware dependent", + f_ops: &snd_hwdep_f_ops, +}; + +int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep) +{ + snd_hwdep_t *hwdep; + int err; + static snd_device_ops_t ops = { + dev_free: snd_hwdep_dev_free, + dev_register: snd_hwdep_dev_register, + dev_unregister: snd_hwdep_dev_unregister + }; + + snd_assert(rhwdep != NULL, return -EINVAL); + *rhwdep = NULL; + snd_assert(card != NULL, return -ENXIO); + hwdep = snd_magic_kcalloc(snd_hwdep_t, 0, GFP_KERNEL); + if (hwdep == NULL) + return -ENOMEM; + hwdep->card = card; + hwdep->device = device; + if (id) { + strncpy(hwdep->id, id, sizeof(hwdep->id) - 1); + } +#ifdef CONFIG_SND_OSSEMUL + hwdep->oss_type = -1; +#endif + if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) { + snd_hwdep_free(hwdep); + return err; + } + init_waitqueue_head(&hwdep->open_wait); + *rhwdep = hwdep; + return 0; +} + +static int snd_hwdep_free(snd_hwdep_t *hwdep) +{ + snd_assert(hwdep != NULL, return -ENXIO); + if (hwdep->private_free) + hwdep->private_free(hwdep); + snd_magic_kfree(hwdep); + return 0; +} + +static int snd_hwdep_dev_free(snd_device_t *device) +{ + snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + return snd_hwdep_free(hwdep); +} + +static int snd_hwdep_dev_register(snd_device_t *device) +{ + snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + int idx, err; + char name[32]; + + down(®ister_mutex); + idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device; + if (snd_hwdep_devices[idx]) { + up(®ister_mutex); + return -EBUSY; + } + snd_hwdep_devices[idx] = hwdep; + sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP, + hwdep->card, hwdep->device, + &snd_hwdep_reg, name)) < 0) { + snd_printk("unable to register hardware dependant device %i:%i\n", + hwdep->card->number, hwdep->device); + snd_hwdep_devices[idx] = NULL; + up(®ister_mutex); + return err; + } +#ifdef CONFIG_SND_OSSEMUL + hwdep->ossreg = 0; + if (hwdep->oss_type >= 0) { + if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) { + snd_printk ("only hwdep device 0 can be registered as OSS direct FM device!\n"); + } else { + if (snd_register_oss_device(hwdep->oss_type, + hwdep->card, hwdep->device, + &snd_hwdep_reg, hwdep->oss_dev) < 0) { + snd_printk("unable to register OSS compatibility device %i:%i\n", + hwdep->card->number, hwdep->device); + } else { + snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, hwdep->card->number, hwdep->name); + hwdep->ossreg = 1; + } + } + } +#endif + up(®ister_mutex); + return 0; +} + +static int snd_hwdep_dev_unregister(snd_device_t *device) +{ + snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + int idx; + + snd_assert(hwdep != NULL, return -ENXIO); + down(®ister_mutex); + idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device; + if (snd_hwdep_devices[idx] != hwdep) { + up(®ister_mutex); + return -EINVAL; + } +#ifdef CONFIG_SND_OSSEMUL + if (hwdep->ossreg) { + snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, hwdep->card->number); + } +#endif + snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device); + snd_hwdep_devices[idx] = NULL; + up(®ister_mutex); + return snd_hwdep_free(hwdep); +} + +/* + * Info interface + */ + +static void snd_hwdep_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + int idx; + snd_hwdep_t *hwdep; + + down(®ister_mutex); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_MINOR_HWDEPS; idx++) { + hwdep = snd_hwdep_devices[idx]; + if (hwdep == NULL) + continue; + snd_iprintf(buffer, "%02i-%02i: %s\n", + idx / SNDRV_MINOR_HWDEPS, + idx % SNDRV_MINOR_HWDEPS, + hwdep->name); + } + up(®ister_mutex); +} + +/* + * ENTRY functions + */ + +static snd_info_entry_t *snd_hwdep_proc_entry = NULL; + +static int __init alsa_hwdep_init(void) +{ + snd_info_entry_t *entry; + + memset(snd_hwdep_devices, 0, sizeof(snd_hwdep_devices)); + if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 512; + entry->c.text.read = snd_hwdep_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_hwdep_proc_entry = entry; + snd_ctl_register_ioctl(snd_hwdep_control_ioctl); + return 0; +} + +static void __exit alsa_hwdep_exit(void) +{ + snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl); + if (snd_hwdep_proc_entry) { + snd_info_unregister(snd_hwdep_proc_entry); + snd_hwdep_proc_entry = NULL; + } +} + +module_init(alsa_hwdep_init) +module_exit(alsa_hwdep_exit) + +EXPORT_SYMBOL(snd_hwdep_new); diff -Nru a/sound/core/info.c b/sound/core/info.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/info.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1039 @@ +/* + * Information interface for ALSA driver + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DEVFS_FS +#include +#endif +#include + +/* + * + */ + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +int snd_info_check_reserved_words(const char *str) +{ + static char *reserved[] = + { + "dev", + "version", + "meminfo", + "memdebug", + "detect", + "devices", + "oss-devices", + "cards", + "timers", + "synth", + "pcm", + "seq", + NULL + }; + char **xstr = reserved; + + while (*xstr) { + if (!strcmp(*xstr, str)) + return 0; + xstr++; + } + if (!strncmp(str, "card", 4)) + return 0; + return 1; +} + +#ifdef CONFIG_PROC_FS + +extern int snd_major; +extern struct file_operations snd_fops; + +static DECLARE_MUTEX(info_mutex); + +typedef struct _snd_info_private_data { + snd_info_buffer_t *rbuffer; + snd_info_buffer_t *wbuffer; + snd_info_entry_t *entry; + void *file_private_data; +} snd_info_private_data_t; + +static int snd_info_version_init(void); +static int snd_info_version_done(void); + +/* + + */ + +int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) +{ + va_list args; + int res; + char sbuffer[512]; + + if (buffer->stop || buffer->error) + return 0; + va_start(args, fmt); + res = vsprintf(sbuffer, fmt, args); + va_end(args); + if (buffer->size + res >= buffer->len) { + buffer->stop = 1; + return 0; + } + strcpy(buffer->curr, sbuffer); + buffer->curr += res; + buffer->size += res; + return res; +} + +/* + + */ + +struct proc_dir_entry *snd_proc_root = NULL; +struct proc_dir_entry *snd_proc_dev = NULL; +snd_info_entry_t *snd_seq_root = NULL; + +#ifdef LINUX_2_2 +static void snd_info_fill_inode(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} + +static inline void snd_info_entry_prepare(struct proc_dir_entry *de) +{ + de->fill_inode = snd_info_fill_inode; +} + +void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de) +{ + if (parent && de) + proc_unregister(parent, de->low_ino); +} +#else +static inline void snd_info_entry_prepare(struct proc_dir_entry *de) +{ + de->owner = THIS_MODULE; +} + +void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de) +{ + if (de) + remove_proc_entry(de->name, parent); +} +#endif + +static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + switch (orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + return file->f_pos; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + return file->f_pos; + case 2: /* SEEK_END */ + default: + return -EINVAL; + } + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->llseek) + return entry->c.ops->llseek(entry, + data->file_private_data, + file, offset, orig); + break; + } + return -ENXIO; +} + +static ssize_t snd_info_entry_read(struct file *file, char *buffer, + size_t count, loff_t * offset) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + snd_info_buffer_t *buf; + long size = 0, size1; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + snd_assert(data != NULL, return -ENXIO); + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + buf = data->rbuffer; + if (buf == NULL) + return -EIO; + if (file->f_pos >= buf->size) + return 0; + size = buf->size < count ? buf->size : count; + size1 = buf->size - file->f_pos; + if (size1 < size) + size = size1; + if (copy_to_user(buffer, buf->buffer + file->f_pos, size)) + return -EFAULT; + file->f_pos += size; + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->read) + return entry->c.ops->read(entry, + data->file_private_data, + file, buffer, count); + if (size > 0) + file->f_pos += size; + break; + } + return size; +} + +static ssize_t snd_info_entry_write(struct file *file, const char *buffer, + size_t count, loff_t * offset) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + snd_info_buffer_t *buf; + long size = 0, size1; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + snd_assert(data != NULL, return -ENXIO); + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + buf = data->wbuffer; + if (buf == NULL) + return -EIO; + if (file->f_pos < 0) + return -EINVAL; + if (file->f_pos >= buf->len) + return -ENOMEM; + size = buf->len < count ? buf->len : count; + size1 = buf->len - file->f_pos; + if (size1 < size) + size = size1; + if (copy_from_user(buf->buffer + file->f_pos, buffer, size)) + return -EFAULT; + if (buf->size < file->f_pos + size) + buf->size = file->f_pos + size; + file->f_pos += size; + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->write) + return entry->c.ops->write(entry, + data->file_private_data, + file, buffer, count); + if (size > 0) + file->f_pos += size; + break; + } + return size; +} + +static int snd_info_entry_open(struct inode *inode, struct file *file) +{ + snd_info_entry_t *entry; + snd_info_private_data_t *data; + snd_info_buffer_t *buffer; + struct proc_dir_entry *p; + int mode, err; + + down(&info_mutex); + p = PDE(inode); + entry = p == NULL ? NULL : (snd_info_entry_t *)p->data; + if (entry == NULL) { + up(&info_mutex); + return -ENODEV; + } +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + if (entry->module && !try_inc_mod_count(entry->module)) { + err = -EFAULT; + goto __error1; + } + mode = file->f_flags & O_ACCMODE; + if (mode == O_RDONLY || mode == O_RDWR) { + if ((entry->content == SNDRV_INFO_CONTENT_TEXT && + !entry->c.text.read_size) || + (entry->content == SNDRV_INFO_CONTENT_DATA && + entry->c.ops->read == NULL) || + entry->content == SNDRV_INFO_CONTENT_DEVICE) { + err = -ENODEV; + goto __error; + } + } + if (mode == O_WRONLY || mode == O_RDWR) { + if ((entry->content == SNDRV_INFO_CONTENT_TEXT && + !entry->c.text.write_size) || + (entry->content == SNDRV_INFO_CONTENT_DATA && + entry->c.ops->write == NULL) || + entry->content == SNDRV_INFO_CONTENT_DEVICE) { + err = -ENODEV; + goto __error; + } + } + data = snd_magic_kcalloc(snd_info_private_data_t, 0, GFP_KERNEL); + if (data == NULL) { + err = -ENOMEM; + goto __error; + } + data->entry = entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + if (mode == O_RDONLY || mode == O_RDWR) { + buffer = (snd_info_buffer_t *) + snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL); + if (buffer == NULL) { + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->len = (entry->c.text.read_size + + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); + buffer->buffer = vmalloc(buffer->len); + if (buffer->buffer == NULL) { + kfree(buffer); + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->curr = buffer->buffer; + data->rbuffer = buffer; + } + if (mode == O_WRONLY || mode == O_RDWR) { + buffer = (snd_info_buffer_t *) + snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL); + if (buffer == NULL) { + if (mode == O_RDWR) { + vfree(data->rbuffer->buffer); + kfree(data->rbuffer); + } + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->len = (entry->c.text.write_size + + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); + buffer->buffer = vmalloc(buffer->len); + if (buffer->buffer == NULL) { + if (mode == O_RDWR) { + vfree(data->rbuffer->buffer); + kfree(data->rbuffer); + } + kfree(buffer); + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->curr = buffer->buffer; + data->wbuffer = buffer; + } + break; + case SNDRV_INFO_CONTENT_DATA: /* data */ + if (entry->c.ops->open) { + if ((err = entry->c.ops->open(entry, mode, + &data->file_private_data)) < 0) { + snd_magic_kfree(data); + goto __error; + } + } + break; + } + file->private_data = data; + up(&info_mutex); + if (entry->content == SNDRV_INFO_CONTENT_TEXT && + (mode == O_RDONLY || mode == O_RDWR)) { + if (entry->c.text.read) { + down(&entry->access); + entry->c.text.read(entry, data->rbuffer); + up(&entry->access); + } + } + return 0; + + __error: + dec_mod_count(entry->module); + __error1: +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + up(&info_mutex); + return err; +} + +static int snd_info_entry_release(struct inode *inode, struct file *file) +{ + snd_info_entry_t *entry; + snd_info_private_data_t *data; + int mode; + + mode = file->f_flags & O_ACCMODE; + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + if (mode == O_RDONLY || mode == O_RDWR) { + vfree(data->rbuffer->buffer); + kfree(data->rbuffer); + } + if (mode == O_WRONLY || mode == O_RDWR) { + if (entry->c.text.write) { + entry->c.text.write(entry, data->wbuffer); + if (data->wbuffer->error) { + snd_printk("data write error to %s (%i)\n", + entry->name, + data->wbuffer->error); + } + } + vfree(data->wbuffer->buffer); + kfree(data->wbuffer); + } + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->release) + entry->c.ops->release(entry, mode, + data->file_private_data); + break; + } + dec_mod_count(entry->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + snd_magic_kfree(data); + return 0; +} + +static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + unsigned int mask; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + if (data == NULL) + return 0; + entry = data->entry; + mask = 0; + switch (entry->content) { + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->poll) + return entry->c.ops->poll(entry, + data->file_private_data, + file, wait); + if (entry->c.ops->read) + mask |= POLLIN | POLLRDNORM; + if (entry->c.ops->write) + mask |= POLLOUT | POLLWRNORM; + break; + } + return mask; +} + +static int snd_info_entry_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + if (data == NULL) + return 0; + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->ioctl) + return entry->c.ops->ioctl(entry, + data->file_private_data, + file, cmd, arg); + break; + } + return -ENOTTY; +} + +static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode = file->f_dentry->d_inode; + snd_info_private_data_t *data; + struct snd_info_entry *entry; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + if (data == NULL) + return 0; + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->mmap) + return entry->c.ops->mmap(entry, + data->file_private_data, + inode, file, vma); + break; + } + return -ENXIO; +} + +static struct file_operations snd_info_entry_operations = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + llseek: snd_info_entry_llseek, + read: snd_info_entry_read, + write: snd_info_entry_write, + poll: snd_info_entry_poll, + ioctl: snd_info_entry_ioctl, + mmap: snd_info_entry_mmap, + open: snd_info_entry_open, + release: snd_info_entry_release, +}; + +#ifdef LINUX_2_2 +static struct inode_operations snd_info_entry_inode_operations = +{ + &snd_info_entry_operations, /* default sound info directory file-ops */ +}; + +static struct inode_operations snd_info_device_inode_operations = +{ + &snd_fops, /* default sound info directory file-ops */ +}; +#endif /* LINUX_2_2 */ + +static int snd_info_card_readlink(struct dentry *dentry, + char *buffer, int buflen) +{ + char *s = PDE(dentry->d_inode)->data; +#ifndef LINUX_2_2 + return vfs_readlink(dentry, buffer, buflen, s); +#else + int len; + + if (s == NULL) + return -EIO; + len = strlen(s); + if (len > buflen) + len = buflen; + if (copy_to_user(buffer, s, len)) + return -EFAULT; + return len; +#endif +} + +#ifndef LINUX_2_2 +static int snd_info_card_followlink(struct dentry *dentry, + struct nameidata *nd) +{ + char *s = PDE(dentry->d_inode)->data; + return vfs_follow_link(nd, s); +} +#else +static struct dentry *snd_info_card_followlink(struct dentry *dentry, + struct dentry *base, + unsigned int follow) +{ + char *s = PDE(dentry->d_inode)->data; + return lookup_dentry(s, base, follow); +} +#endif + +#ifdef LINUX_2_2 +static struct file_operations snd_info_card_link_operations = +{ + NULL +}; +#endif + +struct inode_operations snd_info_card_link_inode_operations = +{ +#ifdef LINUX_2_2 + default_file_ops: &snd_info_card_link_operations, +#endif + readlink: snd_info_card_readlink, + follow_link: snd_info_card_followlink, +}; + +struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, + struct proc_dir_entry *parent) +{ + struct proc_dir_entry *p; + p = create_proc_entry(name, mode, parent); + if (p) + snd_info_entry_prepare(p); + return p; +} + +int __init snd_info_init(void) +{ + struct proc_dir_entry *p; + + p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root); + if (p == NULL) + return -ENOMEM; + snd_proc_root = p; + p = snd_create_proc_entry("dev", S_IFDIR | S_IRUGO | S_IXUGO, snd_proc_root); + if (p == NULL) + return -ENOMEM; + snd_proc_dev = p; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + { + snd_info_entry_t *entry; + if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_seq_root = entry; + } +#endif + snd_info_version_init(); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_info_init(); +#endif + snd_minor_info_init(); +#ifdef CONFIG_SND_OSSEMUL + snd_minor_info_oss_init(); +#endif + snd_card_info_init(); + return 0; +} + +int __exit snd_info_done(void) +{ + snd_card_info_done(); +#ifdef CONFIG_SND_OSSEMUL + snd_minor_info_oss_done(); +#endif + snd_minor_info_done(); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_info_done(); +#endif + snd_info_version_done(); + if (snd_proc_root) { +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_root) + snd_info_unregister(snd_seq_root); +#endif + snd_remove_proc_entry(snd_proc_root, snd_proc_dev); + snd_remove_proc_entry(&proc_root, snd_proc_root); + } + return 0; +} + +/* + + */ + + +int snd_info_card_register(snd_card_t * card) +{ + char str[8]; + char *s; + snd_info_entry_t *entry; + struct proc_dir_entry *p; + + snd_assert(card != NULL, return -ENXIO); + + sprintf(str, "card%i", card->number); + if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + card->proc_root = entry; + + if (!strcmp(card->id, str)) + return 0; + + s = snd_kmalloc_strdup(str, GFP_KERNEL); + if (s == NULL) + return -ENOMEM; + p = snd_create_proc_entry(card->id, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, snd_proc_root); + if (p == NULL) + return -ENOMEM; + p->data = s; +#ifndef LINUX_2_2 + p->owner = card->module; + p->proc_iops = &snd_info_card_link_inode_operations; +#else + p->ops = &snd_info_card_link_inode_operations; +#endif + card->proc_root_link = p; + return 0; +} + +int snd_info_card_unregister(snd_card_t * card) +{ + void *data; + + snd_assert(card != NULL, return -ENXIO); + if (card->proc_root_link) { + data = card->proc_root_link->data; + card->proc_root_link->data = NULL; + kfree(data); + snd_remove_proc_entry(snd_proc_root, card->proc_root_link); + card->proc_root_link = NULL; + } + if (card->proc_root) { + snd_info_unregister(card->proc_root); + card->proc_root = NULL; + } + return 0; +} + +/* + + */ + +int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) +{ + int c = -1; + + if (len <= 0 || buffer->stop || buffer->error) + return 1; + while (--len > 0) { + c = *buffer->curr++; + if (c == '\n') { + if ((buffer->curr - buffer->buffer) >= buffer->size) { + buffer->stop = 1; + } + break; + } + *line++ = c; + if ((buffer->curr - buffer->buffer) >= buffer->size) { + buffer->stop = 1; + break; + } + } + while (c != '\n' && !buffer->stop) { + c = *buffer->curr++; + if ((buffer->curr - buffer->buffer) >= buffer->size) { + buffer->stop = 1; + } + } + *line = '\0'; + return 0; +} + +char *snd_info_get_str(char *dest, char *src, int len) +{ + int c; + + while (*src == ' ' || *src == '\t') + src++; + if (*src == '"' || *src == '\'') { + c = *src++; + while (--len > 0 && *src && *src != c) { + *dest++ = *src++; + } + if (*src == c) + src++; + } else { + while (--len > 0 && *src && *src != ' ' && *src != '\t') { + *dest++ = *src++; + } + } + *dest = 0; + while (*src == ' ' || *src == '\t') + src++; + return src; +} + +static snd_info_entry_t *snd_info_create_entry(const char *name) +{ + snd_info_entry_t *entry; + entry = (snd_info_entry_t *) snd_kcalloc(sizeof(snd_info_entry_t), GFP_KERNEL); + if (entry == NULL) + return NULL; + entry->name = snd_kmalloc_strdup(name, GFP_KERNEL); + if (entry->name == NULL) { + kfree(entry); + return NULL; + } + entry->mode = S_IFREG | S_IRUGO; + entry->content = SNDRV_INFO_CONTENT_TEXT; + init_MUTEX(&entry->access); + return entry; +} + +snd_info_entry_t *snd_info_create_module_entry(struct module * module, + const char *name, + snd_info_entry_t *parent) +{ + snd_info_entry_t *entry = snd_info_create_entry(name); + if (entry) { + entry->module = module; + entry->parent = parent; + } + return entry; +} + +snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, + const char *name, + snd_info_entry_t * parent) +{ + snd_info_entry_t *entry = snd_info_create_entry(name); + if (entry) { + entry->module = card->module; + entry->card = card; + entry->parent = parent; + } + return entry; +} + +void snd_info_free_entry(snd_info_entry_t * entry) +{ + if (entry == NULL) + return; + if (entry->name) + kfree((char *)entry->name); + if (entry->private_free) + entry->private_free(entry); + kfree(entry); +} + +#ifdef LINUX_2_2 +static void snd_info_device_fill_inode(struct inode *inode, int fill) +{ + struct proc_dir_entry *de; + snd_info_entry_t *entry; + + if (!fill) { + MOD_DEC_USE_COUNT; + return; + } + MOD_INC_USE_COUNT; + de = PDE(inode); + if (de == NULL) + return; + entry = (snd_info_entry_t *) de->data; + if (entry == NULL) + return; + inode->i_gid = snd_device_gid; + inode->i_uid = snd_device_uid; + inode->i_rdev = MKDEV(entry->c.device.major, entry->c.device.minor); +} + +static inline void snd_info_device_entry_prepare(struct proc_dir_entry *de, snd_info_entry_t *entry) +{ + de->fill_inode = snd_info_device_fill_inode; +} +#else +static inline void snd_info_device_entry_prepare(struct proc_dir_entry *de, snd_info_entry_t *entry) +{ + de->rdev = mk_kdev(entry->c.device.major, entry->c.device.minor); + de->owner = THIS_MODULE; +} +#endif /* LINUX_2_2 */ + +snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, unsigned int mode) +{ +#ifdef CONFIG_DEVFS_FS + char dname[32]; +#endif + unsigned short major = number >> 16; + unsigned short minor = (unsigned short) number; + snd_info_entry_t *entry; + struct proc_dir_entry *p = NULL; + + if (!major) + major = snd_major; + if (!mode) + mode = S_IFCHR | S_IRUGO | S_IWUGO; + mode &= (snd_device_mode & (S_IRUGO | S_IWUGO)) | S_IFCHR | S_IFBLK; + entry = snd_info_create_module_entry(THIS_MODULE, name, NULL); + if (entry == NULL) + return NULL; + entry->content = SNDRV_INFO_CONTENT_DEVICE; + entry->mode = mode; + entry->c.device.major = major; + entry->c.device.minor = minor; + down(&info_mutex); + p = create_proc_entry(entry->name, entry->mode, snd_proc_dev); + if (p) { + snd_info_device_entry_prepare(p, entry); +#ifndef LINUX_2_2 + /* we should not set this - at least on 2.4.14 or later it causes + problems! */ + /* p->proc_fops = &snd_fops; */ +#else + p->ops = &snd_info_device_inode_operations; +#endif + } else { + up(&info_mutex); + snd_info_free_entry(entry); + return NULL; + } + p->gid = snd_device_gid; + p->uid = snd_device_uid; + p->data = (void *) entry; + entry->p = p; + up(&info_mutex); +#ifdef CONFIG_DEVFS_FS + if (strncmp(name, "controlC", 8)) { /* created in sound.c */ + sprintf(dname, "snd/%s", name); + devfs_register(NULL, dname, DEVFS_FL_DEFAULT, + major, minor, mode, + &snd_fops, NULL); + } +#endif + return entry; +} + +void snd_info_free_device(snd_info_entry_t * entry) +{ +#ifdef CONFIG_DEVFS_FS + char dname[32]; + devfs_handle_t master; +#endif + + snd_runtime_check(entry, return); + down(&info_mutex); + snd_remove_proc_entry(snd_proc_dev, entry->p); + up(&info_mutex); +#ifdef CONFIG_DEVFS_FS + if (entry->p && strncmp(entry->name, "controlC", 8)) { + sprintf(dname, "snd/%s", entry->name); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + master = devfs_find_handle(NULL, dname, strlen(dname), 0, 0, DEVFS_SPECIAL_CHR, 0); +#else + master = devfs_find_handle(NULL, dname, 0, 0, DEVFS_SPECIAL_CHR, 0); +#endif + devfs_unregister(master); + } +#endif + snd_info_free_entry(entry); +} + +int snd_info_register(snd_info_entry_t * entry) +{ + struct proc_dir_entry *root, *p = NULL; + + snd_assert(entry != NULL, return -ENXIO); + root = entry->parent == NULL ? snd_proc_root : entry->parent->p; + down(&info_mutex); + p = snd_create_proc_entry(entry->name, entry->mode, root); + if (!p) { + up(&info_mutex); + return -ENOMEM; + } +#ifndef LINUX_2_2 + p->owner = entry->module; +#endif + if (!S_ISDIR(entry->mode)) { +#ifndef LINUX_2_2 + p->proc_fops = &snd_info_entry_operations; +#else + p->ops = &snd_info_entry_inode_operations; +#endif + } + p->size = entry->size; + p->data = entry; + entry->p = p; + up(&info_mutex); + return 0; +} + +int snd_info_unregister(snd_info_entry_t * entry) +{ + struct proc_dir_entry *root; + + snd_assert(entry != NULL && entry->p != NULL, return -ENXIO); + root = entry->parent == NULL ? snd_proc_root : entry->parent->p; + snd_assert(root, return -ENXIO); + down(&info_mutex); + snd_remove_proc_entry(root, entry->p); + up(&info_mutex); + snd_info_free_entry(entry); + return 0; +} + +/* + + */ + +static snd_info_entry_t *snd_info_version_entry = NULL; + +static void snd_info_version_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + static char *kernel_version = UTS_RELEASE; + + snd_iprintf(buffer, + "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n" + "Compiled on " __DATE__ " for kernel %s" +#ifdef __SMP__ + " (SMP)" +#endif +#ifdef MODVERSIONS + " with versioned symbols" +#endif + ".\n", kernel_version); +} + +static int __init snd_info_version_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); + if (entry == NULL) + return -ENOMEM; + entry->c.text.read_size = 256; + entry->c.text.read = snd_info_version_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_info_version_entry = entry; + return 0; +} + +static int __exit snd_info_version_done(void) +{ + if (snd_info_version_entry) + snd_info_unregister(snd_info_version_entry); + return 0; +} + +#endif /* CONFIG_PROC_FS */ diff -Nru a/sound/core/info_oss.c b/sound/core/info_oss.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/info_oss.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,151 @@ +/* + * Information interface for ALSA driver + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SND_OSSEMUL + +/* + * OSS compatible part + */ + +static DECLARE_MUTEX(strings); +static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT]; +static snd_info_entry_t *snd_sndstat_proc_entry; + +int snd_oss_info_register(int dev, int num, char *string) +{ + char *x; + + snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO); + snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO); + down(&strings); + if (string == NULL) { + if ((x = snd_sndstat_strings[num][dev]) != NULL) { + kfree(x); + x = NULL; + } + } else { + x = snd_kmalloc_strdup(string, GFP_KERNEL); + if (x == NULL) { + up(&strings); + return -ENOMEM; + } + } + snd_sndstat_strings[num][dev] = x; + up(&strings); + return 0; +} + +extern void snd_card_info_read_oss(snd_info_buffer_t * buffer); + +static int snd_sndstat_show_strings(snd_info_buffer_t * buf, char *id, int dev) +{ + int idx, ok = -1; + char *str; + + snd_iprintf(buf, "\n%s:", id); + down(&strings); + for (idx = 0; idx < SNDRV_CARDS; idx++) { + str = snd_sndstat_strings[idx][dev]; + if (str) { + if (ok < 0) { + snd_iprintf(buf, "\n"); + ok++; + } + snd_iprintf(buf, "%i: %s\n", idx, str); + } + } + up(&strings); + if (ok < 0) + snd_iprintf(buf, " NOT ENABLED IN CONFIG\n"); + return ok; +} + +static void snd_sndstat_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n"); + snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n", + system_utsname.sysname, + system_utsname.nodename, + system_utsname.release, + system_utsname.version, + system_utsname.machine); + snd_iprintf(buffer, "Config options: 0\n"); + snd_iprintf(buffer, "\nInstalled drivers: \n"); + snd_iprintf(buffer, "Type 10: ALSA emulation\n"); + snd_iprintf(buffer, "\nCard config: \n"); + snd_card_info_read_oss(buffer); + snd_sndstat_show_strings(buffer, "Audio devices", SNDRV_OSS_INFO_DEV_AUDIO); + snd_sndstat_show_strings(buffer, "Synth devices", SNDRV_OSS_INFO_DEV_SYNTH); + snd_sndstat_show_strings(buffer, "Midi devices", SNDRV_OSS_INFO_DEV_MIDI); + snd_sndstat_show_strings(buffer, "Timers", SNDRV_OSS_INFO_DEV_TIMERS); + snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS); +} + +int snd_info_minor_register(void) +{ + snd_info_entry_t *entry; + + memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings)); + if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", NULL)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 2048; + entry->c.text.read = snd_sndstat_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_sndstat_proc_entry = entry; + return 0; +} + +int snd_info_minor_unregister(void) +{ + if (snd_sndstat_proc_entry) { + snd_info_unregister(snd_sndstat_proc_entry); + snd_sndstat_proc_entry = NULL; + } + return 0; +} + +#else + +int snd_info_minor_register(void) +{ + return 0; +} + +int snd_info_minor_unregister(void) +{ + return 0; +} + +#endif /* CONFIG_SND_OSSEMUL */ diff -Nru a/sound/core/init.c b/sound/core/init.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/init.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,295 @@ +/* + * Initialization routines + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +int snd_cards_count = 0; +static unsigned int snd_cards_lock = 0; /* locked for registering/using */ +snd_card_t *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL}; +rwlock_t snd_card_rwlock = RW_LOCK_UNLOCKED; + +#ifdef CONFIG_SND_OSSEMUL +int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag); +#endif + +static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_iprintf(buffer, "%s\n", entry->card->id); +} + +snd_card_t *snd_card_new(int idx, const char *xid, + struct module *module, int extra_size) +{ + snd_card_t *card; + snd_info_entry_t *entry; + int err; + + if (extra_size < 0) + extra_size = 0; + card = (snd_card_t *) snd_kcalloc(sizeof(snd_card_t) + extra_size, GFP_KERNEL); + if (card == NULL) + return NULL; + if (xid) { + if (!snd_info_check_reserved_words(xid)) + goto __error; + strncpy(card->id, xid, sizeof(card->id) - 1); + } + write_lock(&snd_card_rwlock); + if (idx < 0) { + int idx2; + for (idx2 = 0; idx2 < snd_ecards_limit; idx2++) + if (!(snd_cards_lock & (1 << idx2))) { + idx = idx2; + break; + } + } else if (idx < snd_ecards_limit) { + if (snd_cards_lock & (1 << idx)) + idx = -1; /* invalid */ + } + if (idx < 0 || idx >= snd_ecards_limit) { + write_unlock(&snd_card_rwlock); + if (idx >= snd_ecards_limit) + snd_printk("card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1); + goto __error; + } + snd_cards_lock |= 1 << idx; /* lock it */ + write_unlock(&snd_card_rwlock); + card->number = idx; + if (!card->id[0]) + sprintf(card->id, "card%i", card->number); + card->module = module; + INIT_LIST_HEAD(&card->devices); + rwlock_init(&card->control_rwlock); + rwlock_init(&card->control_owner_lock); + INIT_LIST_HEAD(&card->controls); + INIT_LIST_HEAD(&card->ctl_files); +#ifdef CONFIG_PM + init_MUTEX(&card->power_lock); + init_waitqueue_head(&card->power_sleep); +#endif + /* the control interface cannot be accessed from the user space until */ + /* snd_cards_bitmask and snd_cards are set with snd_card_register */ + if ((err = snd_ctl_register(card)) < 0) { + snd_printd("unable to register control minors\n"); + goto __error; + } + if ((err = snd_info_card_register(card)) < 0) { + snd_printd("unable to register card info\n"); + goto __error_ctl; + } + if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { + snd_printd("unable to create card entry\n"); + goto __error_info; + } + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_card_id_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + goto __error_info; + } + card->proc_id = entry; + if (extra_size > 0) + card->private_data = (char *)card + sizeof(snd_card_t); + return card; + + __error_info: + snd_info_card_unregister(card); + __error_ctl: + snd_ctl_unregister(card); + __error: + kfree(card); + return NULL; +} + +int snd_card_free(snd_card_t * card) +{ + if (card == NULL) + return -EINVAL; + write_lock(&snd_card_rwlock); + snd_cards[card->number] = NULL; + snd_cards_count--; + write_unlock(&snd_card_rwlock); +#ifdef CONFIG_SND_OSSEMUL + if (snd_mixer_oss_notify_callback) + snd_mixer_oss_notify_callback(card, 1); +#endif + if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { + snd_printk("unable to free all devices (pre)\n"); + /* Fatal, but this situation should never occur */ + } + if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { + snd_printk("unable to free all devices (normal)\n"); + /* Fatal, but this situation should never occur */ + } + if (snd_ctl_unregister(card) < 0) { + snd_printk("unable to unregister control minors\n"); + /* Not fatal error */ + } + if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { + snd_printk("unable to free all devices (post)\n"); + /* Fatal, but this situation should never occur */ + } + if (card->private_free) + card->private_free(card); + snd_info_free_entry(card->proc_id); + if (snd_info_card_unregister(card) < 0) { + snd_printk("unable to unregister card info\n"); + /* Not fatal error */ + } + write_lock(&snd_card_rwlock); + snd_cards_lock &= ~(1 << card->number); + write_unlock(&snd_card_rwlock); + kfree(card); + return 0; +} + +int snd_card_register(snd_card_t * card) +{ + int err; + + snd_runtime_check(card != NULL, return -EINVAL); + if ((err = snd_device_register_all(card)) < 0) + return err; + write_lock(&snd_card_rwlock); + snd_cards[card->number] = card; + snd_cards_count++; + write_unlock(&snd_card_rwlock); +#ifdef CONFIG_SND_OSSEMUL + if (snd_mixer_oss_notify_callback) + snd_mixer_oss_notify_callback(card, 0); +#endif + return 0; +} + +static snd_info_entry_t *snd_card_info_entry = NULL; + +static void snd_card_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int idx, count; + snd_card_t *card; + + for (idx = count = 0; idx < SNDRV_CARDS; idx++) { + read_lock(&snd_card_rwlock); + if ((card = snd_cards[idx]) != NULL) { + count++; + snd_iprintf(buffer, "%i [%-15s]: %s - %s\n", + idx, + card->id, + card->driver, + card->shortname); + snd_iprintf(buffer, " %s\n", + card->longname); + } + read_unlock(&snd_card_rwlock); + } + if (!count) + snd_iprintf(buffer, "--- no soundcards ---\n"); +} + +#ifdef CONFIG_SND_OSSEMUL + +void snd_card_info_read_oss(snd_info_buffer_t * buffer) +{ + int idx, count; + snd_card_t *card; + + for (idx = count = 0; idx < SNDRV_CARDS; idx++) { + read_lock(&snd_card_rwlock); + if ((card = snd_cards[idx]) != NULL) { + count++; + snd_iprintf(buffer, "%s\n", card->longname); + } + read_unlock(&snd_card_rwlock); + } + if (!count) { + snd_iprintf(buffer, "--- no soundcards ---\n"); + } +} + +#endif + +int __init snd_card_info_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); + snd_runtime_check(entry != NULL, return -ENOMEM); + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_card_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_card_info_entry = entry; + + return 0; +} + +int __exit snd_card_info_done(void) +{ + if (snd_card_info_entry) + snd_info_unregister(snd_card_info_entry); + return 0; +} + +int snd_component_add(snd_card_t *card, const char *component) +{ + char *ptr; + int len = strlen(component); + + ptr = strstr(card->components, component); + if (ptr != NULL) { + if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ + return 1; + } + if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { + snd_BUG(); + return -ENOMEM; + } + if (card->components[0] != '\0') + strcat(card->components, " "); + strcat(card->components, component); + return 0; +} + +#ifdef CONFIG_PM +/* the power lock must be active before call */ +void snd_power_wait(snd_card_t *card) +{ + wait_queue_t wait; + + init_waitqueue_entry(&wait, current); + add_wait_queue(&card->power_sleep, &wait); + snd_power_unlock(card); + schedule_timeout(30 * HZ); + remove_wait_queue(&card->power_sleep, &wait); + snd_power_lock(card); +} +#endif /* CONFIG_PM */ diff -Nru a/sound/core/isadma.c b/sound/core/isadma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/isadma.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,83 @@ +/* + * ISA DMA support functions + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Defining following add some delay. Maybe this helps for some broken + * ISA DMA controllers. + */ + +#undef HAVE_REALLY_SLOW_DMA_CONTROLLER + +#define __NO_VERSION__ +#include +#include +#include + +#ifdef CONFIG_ISA + +/* + * + */ + +void snd_dma_program(unsigned long dma, + unsigned long addr, unsigned int size, + unsigned short mode) +{ + unsigned long flags; + + flags = claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma, mode); + set_dma_addr(dma, addr); + set_dma_count(dma, size); + if (!(mode & DMA_MODE_NO_ENABLE)) + enable_dma(dma); + release_dma_lock(flags); +} + +void snd_dma_disable(unsigned long dma) +{ + unsigned long flags; + + flags = claim_dma_lock(); + clear_dma_ff(dma); + disable_dma(dma); + release_dma_lock(flags); +} + +unsigned int snd_dma_residue(unsigned long dma) +{ + unsigned long flags; + unsigned int result; + + flags = claim_dma_lock(); + clear_dma_ff(dma); + if (!isa_dma_bridge_buggy) + disable_dma(dma); + result = get_dma_residue(dma); + if (!isa_dma_bridge_buggy) + enable_dma(dma); + release_dma_lock(flags); + return result; +} + +#endif /* CONFIG_ISA */ diff -Nru a/sound/core/memory.c b/sound/core/memory.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/memory.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,545 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Memory allocation routines. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * memory allocation helpers and debug routines + */ + +#ifdef CONFIG_SND_DEBUG_MEMORY + +struct snd_alloc_track { + unsigned long magic; + void *caller; + size_t size; + struct list_head list; + long data[0]; +}; + +#define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data) + +static long snd_alloc_pages; +static long snd_alloc_kmalloc; +static long snd_alloc_vmalloc; +static LIST_HEAD(snd_alloc_kmalloc_list); +static LIST_HEAD(snd_alloc_vmalloc_list); +static spinlock_t snd_alloc_kmalloc_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t snd_alloc_vmalloc_lock = SPIN_LOCK_UNLOCKED; +#define KMALLOC_MAGIC 0x87654321 +#define VMALLOC_MAGIC 0x87654320 +static snd_info_entry_t *snd_memory_info_entry; + +void snd_memory_init(void) +{ + snd_alloc_pages = 0; + snd_alloc_kmalloc = 0; + snd_alloc_vmalloc = 0; +} + +void snd_memory_done(void) +{ + struct list_head *head; + struct snd_alloc_track *t; + if (snd_alloc_pages > 0) + snd_printk("Not freed snd_alloc_pages = %li\n", snd_alloc_pages); + if (snd_alloc_kmalloc > 0) + snd_printk("Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc); + if (snd_alloc_vmalloc > 0) + snd_printk("Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc); + for (head = snd_alloc_kmalloc_list.prev; + head != &snd_alloc_kmalloc_list; head = head->prev) { + t = list_entry(head, struct snd_alloc_track, list); + if (t->magic != KMALLOC_MAGIC) { + snd_printk("Corrupted kmalloc\n"); + break; + } + snd_printk("kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); + } + for (head = snd_alloc_vmalloc_list.prev; + head != &snd_alloc_vmalloc_list; head = head->prev) { + t = list_entry(head, struct snd_alloc_track, list); + if (t->magic != VMALLOC_MAGIC) { + snd_printk("Corrupted vmalloc\n"); + break; + } + snd_printk("vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); + } +} + +void *__snd_kmalloc(size_t size, int flags, void *caller) +{ + unsigned long cpu_flags; + struct snd_alloc_track *t; + void *ptr; + + ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags); + if (ptr != NULL) { + t = (struct snd_alloc_track *)ptr; + t->magic = KMALLOC_MAGIC; + t->caller = caller; + spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags); + list_add_tail(&t->list, &snd_alloc_kmalloc_list); + spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags); + t->size = size; + snd_alloc_kmalloc += size; + ptr = t->data; + } + return ptr; +} + +#define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0)); +void *snd_hidden_kmalloc(size_t size, int flags) +{ + return _snd_kmalloc(size, flags); +} + +void snd_hidden_kfree(const void *obj) +{ + unsigned long flags; + struct snd_alloc_track *t; + if (obj == NULL) { + snd_printk("null kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + t = snd_alloc_track_entry(obj); + if (t->magic != KMALLOC_MAGIC) { + snd_printk("bad kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags); + list_del(&t->list); + spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags); + t->magic = 0; + snd_alloc_kmalloc -= t->size; + obj = t; + snd_wrapper_kfree(obj); +} + +void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags) +{ + unsigned long *ptr; + ptr = _snd_kmalloc(size + sizeof(unsigned long), flags); + if (ptr) { + *ptr++ = magic; + memset(ptr, 0, size); + } + return ptr; +} + +void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags) +{ + unsigned long *ptr; + ptr = _snd_kmalloc(size + sizeof(unsigned long), flags); + if (ptr) + *ptr++ = magic; + return ptr; +} + +void snd_magic_kfree(void *_ptr) +{ + unsigned long *ptr = _ptr; + if (ptr == NULL) { + snd_printk("null snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + *--ptr = 0; + { + struct snd_alloc_track *t; + t = snd_alloc_track_entry(ptr); + if (t->magic != KMALLOC_MAGIC) { + snd_printk("bad snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + } + snd_hidden_kfree(ptr); + return; +} + +void *snd_hidden_vmalloc(unsigned long size) +{ + void *ptr; + ptr = snd_wrapper_vmalloc(size + sizeof(struct snd_alloc_track)); + if (ptr) { + struct snd_alloc_track *t = (struct snd_alloc_track *)ptr; + t->magic = VMALLOC_MAGIC; + t->caller = __builtin_return_address(0); + spin_lock(&snd_alloc_vmalloc_lock); + list_add_tail(&t->list, &snd_alloc_vmalloc_list); + spin_unlock(&snd_alloc_vmalloc_lock); + t->size = size; + snd_alloc_vmalloc += size; + ptr = t->data; + } + return ptr; +} + +void snd_hidden_vfree(void *obj) +{ + struct snd_alloc_track *t; + if (obj == NULL) { + snd_printk("null vfree (called from %p)\n", __builtin_return_address(0)); + return; + } + t = snd_alloc_track_entry(obj); + if (t->magic != VMALLOC_MAGIC) { + snd_printk("bad vfree (called from %p)\n", __builtin_return_address(0)); + return; + } + spin_lock(&snd_alloc_vmalloc_lock); + list_del(&t->list); + spin_unlock(&snd_alloc_vmalloc_lock); + t->magic = 0; + snd_alloc_vmalloc -= t->size; + obj = t; + snd_wrapper_vfree(obj); +} + +static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + long pages = snd_alloc_pages >> (PAGE_SHIFT-12); + snd_iprintf(buffer, "pages : %li bytes (%li pages per %likB)\n", pages * PAGE_SIZE, pages, PAGE_SIZE / 1024); + snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc); + snd_iprintf(buffer, "vmalloc: %li bytes\n", snd_alloc_vmalloc); +} + +int __init snd_memory_info_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 256; + entry->c.text.read = snd_memory_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_memory_info_entry = entry; + return 0; +} + +int __exit snd_memory_info_done(void) +{ + if (snd_memory_info_entry) + snd_info_unregister(snd_memory_info_entry); + return 0; +} + +#else + +#define _snd_kmalloc kmalloc + +#endif /* CONFIG_SND_DEBUG_MEMORY */ + + + +void *snd_malloc_pages(unsigned long size, unsigned int dma_flags) +{ + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(dma_flags != 0, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + if ((res = (void *) __get_free_pages(dma_flags, pg)) != NULL) { + mem_map_t *page = virt_to_page(res); + mem_map_t *last_page = page + (1 << pg); + while (page < last_page) + SetPageReserved(page++); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages += 1 << pg; +#endif + } + return res; +} + +void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsigned long *res_size) +{ + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_pages(size, dma_flags)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +void snd_free_pages(void *ptr, unsigned long size) +{ + int pg; + mem_map_t *page, *last_page; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + page = virt_to_page(ptr); + last_page = page + (1 << pg); + while (page < last_page) + ClearPageReserved(page++); + free_pages((unsigned long) ptr, pg); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages -= 1 << pg; +#endif +} + +#ifdef CONFIG_ISA + +#ifndef CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT +#ifdef CONFIG_PCI +#define CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT +#endif +#endif + +void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr) +{ + void *dma_area; + +#ifndef CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT + dma_area = snd_malloc_pages(size, GFP_ATOMIC|GFP_DMA); + *dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL; +#else + { + int pg; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + dma_area = pci_alloc_consistent(NULL, size, dma_addr); + if (dma_area != NULL) { + mem_map_t *page = virt_to_page(dma_area); + mem_map_t *last_page = page + (1 << pg); + while (page < last_page) + SetPageReserved(page++); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages += 1 << pg; +#endif + } + } +#endif + return dma_area; +} + +void *snd_malloc_isa_pages_fallback(unsigned long size, + dma_addr_t *dma_addr, + unsigned long *res_size) +{ + void *dma_area; + +#ifndef CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT + dma_area = snd_malloc_pages_fallback(size, GFP_ATOMIC|GFP_DMA, res_size); + *dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL; + return dma_area; +#else + snd_assert(size > 0, return NULL); + snd_assert(res_size != NULL, return NULL); + do { + if ((dma_area = snd_malloc_isa_pages(size, dma_addr)) != NULL) { + *res_size = size; + return dma_area; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +#endif +} + +#endif + +#ifdef CONFIG_PCI + +void *snd_malloc_pci_pages(struct pci_dev *pci, + unsigned long size, + dma_addr_t *dma_addr) +{ + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(dma_addr != NULL, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + res = pci_alloc_consistent(pci, PAGE_SIZE * (1 << pg), dma_addr); + if (res != NULL) { + mem_map_t *page = virt_to_page(res); + mem_map_t *last_page = page + (1 << pg); + while (page < last_page) + SetPageReserved(page++); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages += 1 << pg; +#endif + } + return res; +} + +void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, + unsigned long size, + dma_addr_t *dma_addr, + unsigned long *res_size) +{ + void *res; + + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_pci_pages(pci, size, dma_addr)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +void snd_free_pci_pages(struct pci_dev *pci, + unsigned long size, + void *ptr, + dma_addr_t dma_addr) +{ + int pg; + mem_map_t *page, *last_page; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + page = virt_to_page(ptr); + last_page = page + (1 << pg); + while (page < last_page) + ClearPageReserved(page++); + pci_free_consistent(pci, PAGE_SIZE * (1 << pg), ptr, dma_addr); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages -= 1 << pg; +#endif +} + +#endif /* CONFIG_PCI */ + +void *snd_kcalloc(size_t size, int flags) +{ + void *ptr; + + ptr = _snd_kmalloc(size, flags); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +char *snd_kmalloc_strdup(const char *string, int flags) +{ + size_t len; + char *ptr; + + if (!string) + return NULL; + len = strlen(string) + 1; + ptr = _snd_kmalloc(len, flags); + if (ptr) + memcpy(ptr, string, len); + return ptr; +} + +int copy_to_user_fromio(void *dst, unsigned long src, size_t count) +{ +#if defined(__i386_) || defined(CONFIG_SPARC32) + return copy_to_user(dst, (const void*) src, count); +#else + char buf[1024]; + while (count) { + size_t c = count; + int err; + if (c > sizeof(buf)) + c = sizeof(buf); + memcpy_fromio(buf, src, c); + err = copy_to_user(dst, buf, c); + if (err) + return err; + count -= c; + dst += c; + src += c; + } + return 0; +#endif +} + +int copy_from_user_toio(unsigned long dst, const void *src, size_t count) +{ +#if defined(__i386_) || defined(CONFIG_SPARC32) + return copy_from_user((void*)dst, src, count); +#else + char buf[1024]; + while (count) { + size_t c = count; + int err; + if (c > sizeof(buf)) + c = sizeof(buf); + err = copy_from_user(buf, src, c); + if (err) + return err; + memcpy_toio(dst, buf, c); + count -= c; + dst += c; + src += c; + } + return 0; +#endif +} + +#ifdef HACK_PCI_ALLOC_CONSISTENT +/* + * A dirty hack... when the kernel code is fixed this should be removed. + * + * since pci_alloc_consistent always tries GFP_ATOMIC when the requested + * pci memory region is below 32bit, it happens quite often that even + * 2 order or pages cannot be allocated. + * + * so in the following, GFP_ATOMIC is used only when the first allocation + * doesn't match the requested region. + */ +void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC; + + ret = (void *)__get_free_pages(gfp, get_order(size)); + if (ret) { + if (hwdev && ((virt_to_phys(ret) + size - 1) & ~hwdev->dma_mask)) { + free_pages((unsigned long)ret, get_order(size)); + ret = (void *)__get_free_pages(gfp | GFP_DMA, get_order(size)); + } + } + if (ret) { + memset(ret, 0, size); + *dma_handle = virt_to_phys(ret); + } + return ret; +} +#endif /* hack */ diff -Nru a/sound/core/misc.c b/sound/core/misc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/misc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,38 @@ +/* + * Misc and compatibility things + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +int snd_task_name(struct task_struct *task, char *name, size_t size) +{ + unsigned int idx; + + snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL); + for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++) + name[idx] = task->comm[idx]; + name[idx] = '\0'; + return 0; +} diff -Nru a/sound/core/oss/Makefile b/sound/core/oss/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,26 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := oss.o + +export-objs := mixer_oss.o + +list-multi := snd-mixer-oss.o snd-pcm-oss.o + +snd-mixer-oss-objs := mixer_oss.o + +snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \ + io.o copy.o linear.o mulaw.o route.o rate.o + +obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o +obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o + +include $(TOPDIR)/Rules.make + +snd-mixer-oss.o: $(snd-mixer-oss-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mixer-oss-objs) + +snd-pcm-oss.o: $(snd-pcm-oss-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-pcm-oss-objs) diff -Nru a/sound/core/oss/copy.c b/sound/core/oss/copy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/copy.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,88 @@ +/* + * Linear conversion Plug-In + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pcm_plugin.h" + +static snd_pcm_sframes_t copy_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + unsigned int channel; + unsigned int nchannels; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; + nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; channel++) { + snd_assert(src_channels->area.first % 8 == 0 && + src_channels->area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels->area.first % 8 == 0 && + dst_channels->area.step % 8 == 0, + return -ENXIO); + if (!src_channels->enabled) { + if (dst_channels->wanted) + snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format); + dst_channels->enabled = 0; + continue; + } + dst_channels->enabled = 1; + snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format); + src_channels++; + dst_channels++; + } + return frames; +} + +int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + snd_pcm_plugin_t *plugin; + int width; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->format == dst_format->format, return -ENXIO); + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + + width = snd_pcm_format_physical_width(src_format->format); + snd_assert(width > 0, return -ENXIO); + + err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format, + 0, &plugin); + if (err < 0) + return err; + plugin->transfer = copy_transfer; + *r_plugin = plugin; + return 0; +} diff -Nru a/sound/core/oss/io.c b/sound/core/oss/io.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/io.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,134 @@ +/* + * PCM I/O Plug-In Interface + * Copyright (c) 1999 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1) +#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1) +#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1) +#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1) + +/* + * Basic io plugin + */ + +static snd_pcm_sframes_t io_playback_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED, + snd_pcm_uframes_t frames) +{ + snd_assert(plugin != NULL, return -ENXIO); + snd_assert(src_channels != NULL, return -ENXIO); + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + return pcm_write(plugin->plug, src_channels->area.addr, frames); + } else { + int channel, channels = plugin->dst_format.channels; + void **bufs = (void**)plugin->extra_data; + snd_assert(bufs != NULL, return -ENXIO); + for (channel = 0; channel < channels; channel++) { + if (src_channels[channel].enabled) + bufs[channel] = src_channels[channel].area.addr; + else + bufs[channel] = NULL; + } + return pcm_writev(plugin->plug, bufs, frames); + } +} + +static snd_pcm_sframes_t io_capture_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + snd_assert(plugin != NULL, return -ENXIO); + snd_assert(dst_channels != NULL, return -ENXIO); + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + return pcm_read(plugin->plug, dst_channels->area.addr, frames); + } else { + int channel, channels = plugin->dst_format.channels; + void **bufs = (void**)plugin->extra_data; + snd_assert(bufs != NULL, return -ENXIO); + for (channel = 0; channel < channels; channel++) { + if (dst_channels[channel].enabled) + bufs[channel] = dst_channels[channel].area.addr; + else + bufs[channel] = NULL; + } + return pcm_readv(plugin->plug, bufs, frames); + } + return 0; +} + +static snd_pcm_sframes_t io_src_channels(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels) +{ + int err; + unsigned int channel; + snd_pcm_plugin_channel_t *v; + err = snd_pcm_plugin_client_channels(plugin, frames, &v); + if (err < 0) + return err; + *channels = v; + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v) + v->wanted = 1; + } + return frames; +} + +int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug, + snd_pcm_hw_params_t *params, + snd_pcm_plugin_t **r_plugin) +{ + int err; + snd_pcm_plugin_format_t format; + snd_pcm_plugin_t *plugin; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + snd_assert(plug != NULL && params != NULL, return -ENXIO); + format.format = params_format(params); + format.rate = params_rate(params); + format.channels = params_channels(params); + err = snd_pcm_plugin_build(plug, "I/O io", + &format, &format, + sizeof(void *) * format.channels, + &plugin); + if (err < 0) + return err; + plugin->access = params_access(params); + if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { + plugin->transfer = io_playback_transfer; + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) + plugin->client_channels = io_src_channels; + } else { + plugin->transfer = io_capture_transfer; + } + + *r_plugin = plugin; + return 0; +} diff -Nru a/sound/core/oss/linear.c b/sound/core/oss/linear.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/linear.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,159 @@ +/* + * Linear conversion Plug-In + * Copyright (c) 1999 by Jaroslav Kysela , + * Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pcm_plugin.h" + +/* + * Basic linear conversion plugin + */ + +typedef struct linear_private_data { + int conv; +} linear_t; + +static void convert(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ +#define CONV_LABELS +#include "plugin_ops.h" +#undef CONV_LABELS + linear_t *data = (linear_t *)plugin->extra_data; + void *conv = conv_labels[data->conv]; + int channel; + int nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + snd_pcm_uframes_t frames1; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + frames1 = frames; + while (frames1-- > 0) { + goto *conv; +#define CONV_END after +#include "plugin_ops.h" +#undef CONV_END + after: + src += src_step; + dst += dst_step; + } + } +} + +static snd_pcm_sframes_t linear_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + linear_t *data; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + data = (linear_t *)plugin->extra_data; + if (frames == 0) + return 0; +#ifdef CONFIG_SND_DEBUG + { + unsigned int channel; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + snd_assert(src_channels[channel].area.first % 8 == 0 && + src_channels[channel].area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels[channel].area.first % 8 == 0 && + dst_channels[channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + convert(plugin, src_channels, dst_channels, frames); + return frames; +} + +int conv_index(int src_format, int dst_format) +{ + int src_endian, dst_endian, sign, src_width, dst_width; + + sign = (snd_pcm_format_signed(src_format) != + snd_pcm_format_signed(dst_format)); +#ifdef SNDRV_LITTLE_ENDIAN + src_endian = snd_pcm_format_big_endian(src_format); + dst_endian = snd_pcm_format_big_endian(dst_format); +#else + src_endian = snd_pcm_format_little_endian(src_format); + dst_endian = snd_pcm_format_little_endian(dst_format); +#endif + + if (src_endian < 0) + src_endian = 0; + if (dst_endian < 0) + dst_endian = 0; + + src_width = snd_pcm_format_width(src_format) / 8 - 1; + dst_width = snd_pcm_format_width(dst_format) / 8 - 1; + + return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian; +} + +int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + struct linear_private_data *data; + snd_pcm_plugin_t *plugin; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + snd_assert(snd_pcm_format_linear(src_format->format) && + snd_pcm_format_linear(dst_format->format), return -ENXIO); + + err = snd_pcm_plugin_build(plug, "linear format conversion", + src_format, dst_format, + sizeof(linear_t), &plugin); + if (err < 0) + return err; + data = (linear_t *)plugin->extra_data; + data->conv = conv_index(src_format->format, dst_format->format); + plugin->transfer = linear_transfer; + *r_plugin = plugin; + return 0; +} diff -Nru a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/mixer_oss.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1045 @@ +/* + * OSS emulation layer for the mixer interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Mixer OSS emulation for ALSA."); +MODULE_LICENSE("GPL"); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +static int snd_mixer_oss_open(struct inode *inode, struct file *file) +{ + int cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev)); + snd_card_t *card; + snd_mixer_oss_file_t *fmixer; + + if ((card = snd_cards[cardnum]) == NULL) + return -ENODEV; + if (card->mixer_oss == NULL) + return -ENODEV; + fmixer = (snd_mixer_oss_file_t *)snd_kcalloc(sizeof(*fmixer), GFP_KERNEL); + if (fmixer == NULL) + return -ENOMEM; + fmixer->card = card; + fmixer->mixer = card->mixer_oss; + file->private_data = fmixer; +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + if (!try_inc_mod_count(card->module)) { + kfree(fmixer); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return -EFAULT; + } + return 0; +} + +static int snd_mixer_oss_release(struct inode *inode, struct file *file) +{ + snd_mixer_oss_file_t *fmixer; + + if (file->private_data) { + fmixer = (snd_mixer_oss_file_t *) file->private_data; + dec_mod_count(fmixer->card->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + kfree(fmixer); + } + return 0; +} + +static int snd_mixer_oss_info(snd_mixer_oss_file_t *fmixer, + mixer_info *_info) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + struct mixer_info info; + + memset(&info, 0, sizeof(info)); + strncpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id) - 1); + strncpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name) - 1); + info.modify_counter = card->mixer_oss_change_count; + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_mixer_oss_info_obsolete(snd_mixer_oss_file_t *fmixer, + _old_mixer_info *_info) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + _old_mixer_info info; + + memset(&info, 0, sizeof(info)); + strncpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id) - 1); + strncpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name) - 1); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_mixer_oss_caps(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->get_recsrc && mixer->put_recsrc) + result |= SOUND_CAP_EXCL_INPUT; + return result; +} + +static int snd_mixer_oss_devmask(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, chn; + + if (mixer == NULL) + return -EIO; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_volume || pslot->put_recsrc) + result |= 1 << chn; + } + return result; +} + +static int snd_mixer_oss_stereodevs(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, chn; + + if (mixer == NULL) + return -EIO; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_volume && pslot->stereo) + result |= 1 << chn; + } + return result; +} + +static int snd_mixer_oss_recmask(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ + result = mixer->mask_recsrc; + } else { + snd_mixer_oss_slot_t *pslot; + int chn; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_recsrc) + result |= 1 << chn; + } + } + return result; +} + +static int snd_mixer_oss_get_recsrc(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ + int err; + if ((err = mixer->get_recsrc(fmixer, &result)) < 0) + return err; + result = 1 << result; + } else { + snd_mixer_oss_slot_t *pslot; + int chn; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->get_recsrc) { + int active = 0; + pslot->get_recsrc(fmixer, pslot, &active); + if (active) + result |= 1 << chn; + } + } + } + return mixer->oss_recsrc = result; +} + +static int snd_mixer_oss_set_recsrc(snd_mixer_oss_file_t *fmixer, int recsrc) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */ + if (recsrc & ~mixer->oss_recsrc) + recsrc &= ~mixer->oss_recsrc; + mixer->put_recsrc(fmixer, ffz(~recsrc)); + mixer->get_recsrc(fmixer, &result); + result = 1 << result; + } else { + snd_mixer_oss_slot_t *pslot; + int chn, active; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_recsrc) { + active = (recsrc & (1 << chn)) ? 1 : 0; + pslot->put_recsrc(fmixer, pslot, active); + } + } + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->get_recsrc) { + active = 0; + pslot->get_recsrc(fmixer, pslot, &active); + if (active) + result |= 1 << chn; + } + } + } + return result; +} + +static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, left, right; + + if (mixer == NULL || slot > 30) + return -EIO; + pslot = &mixer->slots[slot]; + left = fmixer->volume[slot][0]; + right = fmixer->volume[slot][1]; + if (pslot->get_volume) + result = pslot->get_volume(fmixer, pslot, &left, &right); + if (!pslot->stereo) + right = left; + snd_assert(left >= 0 && left <= 100, return -EIO); + snd_assert(right >= 0 && right <= 100, return -EIO); + if (result >= 0) { + fmixer->volume[slot][0] = left; + fmixer->volume[slot][1] = right; + result = (left & 0xff) | ((right & 0xff) << 8); + } + return result; +} + +static int snd_mixer_oss_set_volume(snd_mixer_oss_file_t *fmixer, + int slot, int volume) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff; + + if (mixer == NULL || slot > 30) + return -EIO; + pslot = &mixer->slots[slot]; + if (left > 100) + left = 100; + if (right > 100) + right = 100; + if (!pslot->stereo) + left = right = left; + if (pslot->put_volume) + result = pslot->put_volume(fmixer, pslot, left, right); + if (result < 0) + return result; + fmixer->volume[slot][0] = left; + fmixer->volume[slot][1] = right; + return (left & 0xff) | ((right & 0xff) << 8); +} + +static int snd_mixer_oss_ioctl1(snd_mixer_oss_file_t *fmixer, unsigned int cmd, unsigned long arg) +{ + int tmp; + + snd_assert(fmixer != NULL, return -ENXIO); + if (((cmd >> 8) & 0xff) == 'M') { + switch (cmd) { + case SOUND_MIXER_INFO: + return snd_mixer_oss_info(fmixer, (mixer_info *)arg); + case SOUND_OLD_MIXER_INFO: + return snd_mixer_oss_info_obsolete(fmixer, (_old_mixer_info *)arg); + case SOUND_MIXER_WRITE_RECSRC: + if (get_user(tmp, (int *)arg)) + return -EFAULT; + tmp = snd_mixer_oss_set_recsrc(fmixer, tmp); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case OSS_GETVERSION: + return put_user(SNDRV_OSS_VERSION, (int *) arg); + case SOUND_MIXER_READ_DEVMASK: + tmp = snd_mixer_oss_devmask(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_STEREODEVS: + tmp = snd_mixer_oss_stereodevs(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_RECMASK: + tmp = snd_mixer_oss_recmask(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_CAPS: + tmp = snd_mixer_oss_caps(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_RECSRC: + tmp = snd_mixer_oss_get_recsrc(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + } + } + if (cmd & IOC_IN) { + if (get_user(tmp, (int *)arg)) + return -EFAULT; + tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + } else if (cmd & IOC_OUT) { + tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + } + return -ENXIO; +} + +int snd_mixer_oss_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg); +} + +int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg) +{ + snd_mixer_oss_file_t fmixer; + + snd_assert(card != NULL, return -ENXIO); + if (card->mixer_oss == NULL) + return -ENXIO; + memset(&fmixer, 0, sizeof(fmixer)); + fmixer.card = card; + fmixer.mixer = card->mixer_oss; + return snd_mixer_oss_ioctl1(&fmixer, cmd, arg); +} + +/* + * REGISTRATION PART + */ + +static struct file_operations snd_mixer_oss_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + open: snd_mixer_oss_open, + release: snd_mixer_oss_release, + ioctl: snd_mixer_oss_ioctl, +}; + +static snd_minor_t snd_mixer_oss_reg = +{ + comment: "mixer", + f_ops: &snd_mixer_oss_f_ops, +}; + +/* + * utilities + */ + +static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax) +{ + long orange = omax - omin, nrange = nmax - nmin; + + if (orange == 0) + return 0; + return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin; +} + +static long snd_mixer_oss_conv1(long val, long min, long max, int *old) +{ + if (val == snd_mixer_oss_conv(*old, 0, 100, min, max)) + return *old; + return snd_mixer_oss_conv(val, min, max, 0, 100); +} + +static long snd_mixer_oss_conv2(long val, long min, long max) +{ + return snd_mixer_oss_conv(val, 0, 100, min, max); +} + +#if 0 +static void snd_mixer_oss_recsrce_set(snd_card_t *card, int slot) +{ + snd_mixer_oss_t *mixer = card->mixer_oss; + if (mixer) + mixer->mask_recsrc |= 1 << slot; +} + +static int snd_mixer_oss_recsrce_get(snd_card_t *card, int slot) +{ + snd_mixer_oss_t *mixer = card->mixer_oss; + if (mixer && (mixer->mask_recsrc & (1 << slot))) + return 1; + return 0; +} +#endif + +#define SNDRV_MIXER_OSS_SIGNATURE 0x65999250 + +#define SNDRV_MIXER_OSS_ITEM_GLOBAL 0 +#define SNDRV_MIXER_OSS_ITEM_GSWITCH 1 +#define SNDRV_MIXER_OSS_ITEM_GROUTE 2 +#define SNDRV_MIXER_OSS_ITEM_GVOLUME 3 +#define SNDRV_MIXER_OSS_ITEM_PSWITCH 4 +#define SNDRV_MIXER_OSS_ITEM_PROUTE 5 +#define SNDRV_MIXER_OSS_ITEM_PVOLUME 6 +#define SNDRV_MIXER_OSS_ITEM_CSWITCH 7 +#define SNDRV_MIXER_OSS_ITEM_CROUTE 8 +#define SNDRV_MIXER_OSS_ITEM_CVOLUME 9 +#define SNDRV_MIXER_OSS_ITEM_CAPTURE 10 + +#define SNDRV_MIXER_OSS_ITEM_COUNT 11 + +#define SNDRV_MIXER_OSS_PRESENT_GLOBAL (1<<0) +#define SNDRV_MIXER_OSS_PRESENT_GSWITCH (1<<1) +#define SNDRV_MIXER_OSS_PRESENT_GROUTE (1<<2) +#define SNDRV_MIXER_OSS_PRESENT_GVOLUME (1<<3) +#define SNDRV_MIXER_OSS_PRESENT_PSWITCH (1<<4) +#define SNDRV_MIXER_OSS_PRESENT_PROUTE (1<<5) +#define SNDRV_MIXER_OSS_PRESENT_PVOLUME (1<<6) +#define SNDRV_MIXER_OSS_PRESENT_CSWITCH (1<<7) +#define SNDRV_MIXER_OSS_PRESENT_CROUTE (1<<8) +#define SNDRV_MIXER_OSS_PRESENT_CVOLUME (1<<9) +#define SNDRV_MIXER_OSS_PRESENT_CAPTURE (1<<10) + +struct slot { + unsigned int signature; + unsigned int present; + int channels; + snd_kcontrol_t *kcontrol[SNDRV_MIXER_OSS_ITEM_COUNT]; + unsigned int capture_item; +}; + +static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index) +{ + snd_card_t * card = mixer->card; + snd_ctl_elem_id_t id; + + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, name); + id.index = index; + return snd_ctl_find_id(card, &id); +} + +static void snd_mixer_oss_get_volume1_vol(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int *left, int *right) +{ + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + + snd_runtime_check(kctl != NULL, return); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!kctl->info(kctl, &uinfo), return); + snd_runtime_check(!kctl->get(kctl, &uctl), return); + snd_runtime_check(uinfo.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo.value.integer.min != 0 || uinfo.value.integer.max != 1, return); + *left = snd_mixer_oss_conv1(uctl.value.integer.value[0], uinfo.value.integer.min, uinfo.value.integer.max, &fmixer->volume[pslot->number][0]); + if (uinfo.count > 1) + *right = snd_mixer_oss_conv1(uctl.value.integer.value[1], uinfo.value.integer.min, uinfo.value.integer.max, &fmixer->volume[pslot->number][1]); +} + +static void snd_mixer_oss_get_volume1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int *left, int *right, + int route) +{ + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + + snd_runtime_check(kctl != NULL, return); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!kctl->info(kctl, &uinfo), return); + snd_runtime_check(!kctl->get(kctl, &uctl), return); + if (!uctl.value.integer.value[0]) { + *left = 0; + if (uinfo.count == 1) + *right = 0; + } + if (uinfo.count > 1 && !uctl.value.integer.value[route ? 3 : 1]) + *right = 0; +} + +static int snd_mixer_oss_get_volume1(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int *left, int *right) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + *left = *right = 100; + read_lock(&card->control_rwlock); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { + snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { + snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { + snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right); + } + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); + } + read_unlock(&card->control_rwlock); + return 0; +} + +static void snd_mixer_oss_put_volume1_vol(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int left, int right) +{ + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + int res; + + snd_runtime_check(kctl != NULL, return); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!kctl->info(kctl, &uinfo), return); + snd_runtime_check(uinfo.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo.value.integer.min != 0 || uinfo.value.integer.max != 1, return); + uctl.value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo.value.integer.min, uinfo.value.integer.max); + if (uinfo.count > 1) + uctl.value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo.value.integer.min, uinfo.value.integer.max); + snd_runtime_check((res = kctl->put(kctl, &uctl)) >= 0, return); + if (res > 0) + snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); +} + +static void snd_mixer_oss_put_volume1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int left, int right, + int route) +{ + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + int res; + + snd_runtime_check(kctl != NULL, return); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!kctl->info(kctl, &uinfo), return); + if (uinfo.count > 1) { + uctl.value.integer.value[0] = left > 0 ? 1 : 0; + uctl.value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0; + if (route) { + uctl.value.integer.value[1] = + uctl.value.integer.value[2] = 0; + } + } else { + uctl.value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0; + } + snd_runtime_check((res = kctl->put(kctl, &uctl)) >= 0, return); + if (res > 0) + snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); +} + +static int snd_mixer_oss_put_volume1(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int left, int right) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + read_lock(&card->control_rwlock); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right); + } + if (left || right) { + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); + } else { + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); + } + } + read_unlock(&card->control_rwlock); + return 0; +} + +static int snd_mixer_oss_get_recsrc1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int *active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + int left, right; + + left = right = 1; + read_lock(&card->control_rwlock); + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0); + read_unlock(&card->control_rwlock); + *active = (left || right) ? 1 : 0; + return 0; +} + +static int snd_mixer_oss_get_recsrc1_route(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int *active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + int left, right; + + left = right = 1; + read_lock(&card->control_rwlock); + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1); + read_unlock(&card->control_rwlock); + *active = (left || right) ? 1 : 0; + return 0; +} + +static int snd_mixer_oss_put_recsrc1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + read_lock(&card->control_rwlock); + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0); + read_unlock(&card->control_rwlock); + return 0; +} + +static int snd_mixer_oss_put_recsrc1_route(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + read_lock(&card->control_rwlock); + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1); + read_unlock(&card->control_rwlock); + return 0; +} + +static int snd_mixer_oss_get_recsrc2(snd_mixer_oss_file_t *fmixer, int *active_index) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_kcontrol_t *kctl; + snd_mixer_oss_slot_t *pslot; + struct slot *slot; + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + int err, idx; + + read_lock(&card->control_rwlock); + kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); + snd_runtime_check(kctl != NULL, return -ENOENT); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!(err = kctl->info(kctl, &uinfo)), read_unlock(&card->control_rwlock); return err); + snd_runtime_check(!(err = kctl->get(kctl, &uctl)), read_unlock(&card->control_rwlock); return err); + read_unlock(&card->control_rwlock); + for (idx = 0; idx < 32; idx++) { + if (!(mixer->mask_recsrc & (1 << idx))) + continue; + pslot = &fmixer->mixer->slots[idx]; + slot = (struct slot *)pslot->private_data; + if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) + continue; + if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) + continue; + if (slot->capture_item == uctl.value.enumerated.item[0]) { + *active_index = idx; + break; + } + } + return 0; +} + +static int snd_mixer_oss_put_recsrc2(snd_mixer_oss_file_t *fmixer, int active_index) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_kcontrol_t *kctl; + snd_mixer_oss_slot_t *pslot; + struct slot *slot = NULL; + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + int err, idx; + + read_lock(&card->control_rwlock); + kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); + snd_runtime_check(kctl != NULL, read_unlock(&card->control_rwlock); return -ENOENT); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!(err = kctl->info(kctl, &uinfo)), read_unlock(&card->control_rwlock); return err); + for (idx = 0; idx < 32; idx++) { + if (!(mixer->mask_recsrc & (1 << idx))) + continue; + pslot = &fmixer->mixer->slots[idx]; + slot = (struct slot *)pslot->private_data; + if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) + continue; + if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) + continue; + if (idx == active_index) + break; + slot = NULL; + } + snd_runtime_check(slot != NULL, goto __unlock); + for (idx = 0; idx < uinfo.count; idx++) + uctl.value.enumerated.item[idx] = slot->capture_item; + snd_runtime_check((err = kctl->put(kctl, &uctl)) >= 0, ); + if (err > 0) + snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + __unlock: + read_unlock(&card->control_rwlock); + return 0; +} + +struct snd_mixer_oss_assign_table { + int oss_id; + const char *name; + int index; +}; + +static int snd_mixer_oss_build_test(snd_mixer_oss_t *mixer, struct slot *slot, const char *name, int index, int item) +{ + snd_ctl_elem_info_t info; + snd_kcontrol_t *kcontrol; + int err; + + kcontrol = snd_mixer_oss_test_id(mixer, name, index); + if (kcontrol == NULL) + return 0; + snd_runtime_check((err = kcontrol->info(kcontrol, &info)) >= 0, return err); + slot->kcontrol[item] = kcontrol; + if (info.count > slot->channels) + slot->channels = info.count; + slot->present |= 1 << item; + return 0; +} + +static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn) +{ + kfree(chn->private_data); +} + +static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr) +{ + struct slot slot; + struct slot *pslot; + snd_kcontrol_t *kctl; + snd_mixer_oss_slot_t *rslot; + char str[64]; + + memset(&slot, 0, sizeof(slot)); + if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index, + SNDRV_MIXER_OSS_ITEM_GLOBAL)) + return; + sprintf(str, "%s Switch", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_GSWITCH)) + return; + sprintf(str, "%s Route", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_GROUTE)) + return; + sprintf(str, "%s Volume", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_GVOLUME)) + return; + sprintf(str, "%s Playback Switch", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_PSWITCH)) + return; + sprintf(str, "%s Playback Route", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_PROUTE)) + return; + sprintf(str, "%s Playback Volume", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_PVOLUME)) + return; + sprintf(str, "%s Capture Switch", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_CSWITCH)) + return; + sprintf(str, "%s Capture Route", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_CROUTE)) + return; + sprintf(str, "%s Capture Volume", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_CVOLUME)) + return; + if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) { + snd_ctl_elem_info_t uinfo; + + memset(&uinfo, 0, sizeof(uinfo)); + if (kctl->info(kctl, &uinfo)) + return; + strcpy(str, ptr->name); + if (!strcmp(str, "Master")) + strcpy(str, "Mix"); + if (!strcmp(str, "Master Mono")) + strcpy(str, "Mix Mono"); + slot.capture_item = 0; + if (!strcmp(uinfo.value.enumerated.name, str)) { + slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; + } else { + for (slot.capture_item = 1; slot.capture_item < uinfo.value.enumerated.items; slot.capture_item++) { + uinfo.value.enumerated.item = slot.capture_item; + if (kctl->info(kctl, &uinfo)) + return; + if (!strcmp(uinfo.value.enumerated.name, str)) { + slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; + break; + } + } + } + } + if (slot.present != 0) { + pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL); + snd_runtime_check(pslot != NULL, return); + *pslot = slot; + pslot->signature = SNDRV_MIXER_OSS_SIGNATURE; + rslot = &mixer->slots[ptr->oss_id]; + rslot->stereo = slot.channels > 1 ? 1 : 0; + rslot->get_volume = snd_mixer_oss_get_volume1; + rslot->put_volume = snd_mixer_oss_put_volume1; + /* note: ES18xx have both Capture Source and XX Capture Volume !!! */ + if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) { + rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw; + rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw; + } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) { + rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route; + rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route; + } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) { + mixer->mask_recsrc |= 1 << ptr->oss_id; + } + rslot->private_data = pslot; + rslot->private_free = snd_mixer_oss_slot_free; + } +} + +static void snd_mixer_oss_build(snd_mixer_oss_t *mixer) +{ + static struct snd_mixer_oss_assign_table table[] = { + { SOUND_MIXER_VOLUME, "Master", 0 }, + { SOUND_MIXER_BASS, "Tone Control - Bass", 0 }, + { SOUND_MIXER_TREBLE, "Tone Control - Treble", 0 }, + { SOUND_MIXER_SYNTH, "Synth", 0 }, + { SOUND_MIXER_PCM, "PCM", 0 }, + { SOUND_MIXER_SPEAKER, "PC Speaker", 0 }, + { SOUND_MIXER_LINE, "Line", 0 }, + { SOUND_MIXER_MIC, "Mic", 0 }, + { SOUND_MIXER_CD, "CD", 0 }, + { SOUND_MIXER_IMIX, "Monitor Mix", 0 }, + { SOUND_MIXER_ALTPCM, "PCM", 1 }, + { SOUND_MIXER_RECLEV, "-- nothing --", 0 }, + { SOUND_MIXER_IGAIN, "Capture", 0 }, + { SOUND_MIXER_OGAIN, "Playback", 0 }, + { SOUND_MIXER_LINE1, "Aux", 0 }, + { SOUND_MIXER_LINE2, "Aux", 1 }, + { SOUND_MIXER_LINE3, "Aux", 2 }, + { SOUND_MIXER_DIGITAL1, "Digital", 0 }, + { SOUND_MIXER_DIGITAL2, "Digital", 1 }, + { SOUND_MIXER_DIGITAL3, "Digital", 2 }, + { SOUND_MIXER_PHONEIN, "Phone", 0 }, + { SOUND_MIXER_PHONEOUT, "Phone", 1 }, + { SOUND_MIXER_VIDEO, "Video", 0 }, + { SOUND_MIXER_RADIO, "Radio", 0 }, + { SOUND_MIXER_MONITOR, "Monitor", 0 } + }; + static struct snd_mixer_oss_assign_table fm_table = { + SOUND_MIXER_SYNTH, "FM", 0 + }; + int idx; + + for (idx = 0; idx < sizeof(table) / sizeof(struct snd_mixer_oss_assign_table); idx++) + snd_mixer_oss_build_input(mixer, &table[idx]); + if (mixer->slots[SOUND_MIXER_SYNTH].get_volume == NULL) + snd_mixer_oss_build_input(mixer, &fm_table); + if (mixer->mask_recsrc) { + mixer->get_recsrc = snd_mixer_oss_get_recsrc2; + mixer->put_recsrc = snd_mixer_oss_put_recsrc2; + } +} + +/* + * + */ + +static int snd_mixer_oss_free1(void *private) +{ + snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, private, return -ENXIO); + snd_card_t * card; + int idx; + + snd_assert(mixer != NULL, return -ENXIO); + card = mixer->card; + snd_assert(mixer == card->mixer_oss, return -ENXIO); + card->mixer_oss = NULL; + for (idx = 0; idx < 31; idx++) { + snd_mixer_oss_slot_t *chn = &mixer->slots[idx]; + if (chn->private_free) + chn->private_free(chn); + } + snd_magic_kfree(mixer); + return 0; +} + +static int snd_mixer_oss_notify_handler(snd_card_t * card, int free_flag) +{ + if (!free_flag) { + snd_mixer_oss_t *mixer; + char name[128]; + int idx, err; + + mixer = snd_magic_kcalloc(snd_mixer_oss_t, sizeof(snd_mixer_oss_t), GFP_KERNEL); + if (mixer == NULL) + return -ENOMEM; + sprintf(name, "mixer%i%i", card->number, 0); + if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, + card, 0, + &snd_mixer_oss_reg, + name)) < 0) { + snd_printk("unable to register OSS mixer device %i:%i\n", card->number, 0); + snd_magic_kfree(mixer); + return err; + } + mixer->card = card; + strcpy(mixer->name, name); + snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS, + card->number, + name); + for (idx = 0; idx < 31; idx++) + mixer->slots[idx].number = idx; + card->mixer_oss = mixer; + snd_mixer_oss_build(mixer); + } else { + snd_mixer_oss_t *mixer = card->mixer_oss; + if (mixer == NULL) + return 0; + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); + return snd_mixer_oss_free1(mixer); + } + return 0; +} + +static int __init alsa_mixer_oss_init(void) +{ + int idx; + + snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler; + for (idx = 0; idx < SNDRV_CARDS; idx++) { + if (snd_cards[idx]) + snd_mixer_oss_notify_handler(snd_cards[idx], 0); + } + return 0; +} + +static void __exit alsa_mixer_oss_exit(void) +{ + int idx; + + snd_mixer_oss_notify_callback = NULL; + for (idx = 0; idx < SNDRV_CARDS; idx++) { + if (snd_cards[idx]) + snd_mixer_oss_notify_handler(snd_cards[idx], 1); + } +} + +module_init(alsa_mixer_oss_init) +module_exit(alsa_mixer_oss_exit) + +EXPORT_SYMBOL(snd_mixer_oss_ioctl_card); diff -Nru a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/mulaw.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,308 @@ +/* + * Mu-Law conversion Plug-In Interface + * Copyright (c) 1999 by Jaroslav Kysela + * Uros Bizjak + * + * Based on reference implementation by Sun Microsystems, Inc. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of u-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +static inline int val_seg(int val) +{ + int r = 0; + val >>= 7; + if (val & 0xf0) { + val >>= 4; + r += 4; + } + if (val & 0x0c) { + val >>= 2; + r += 2; + } + if (val & 0x02) + r += 1; + return r; +} + +#define BIAS (0x84) /* Bias for linear code. */ + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = BIAS - pcm_val; + mask = 0x7F; + } else { + pcm_val += BIAS; + mask = 0xFF; + } + if (pcm_val > 0x7FFF) + pcm_val = 0x7FFF; + + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); + return uval ^ mask; +} + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +static int ulaw2linear(unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + + return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +} + +/* + * Basic Mu-Law plugin + */ + +typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames); + +typedef struct mulaw_private_data { + mulaw_f func; + int conv; +} mulaw_t; + +static void mulaw_decode(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef PUT_S16_LABELS + mulaw_t *data = (mulaw_t *)plugin->extra_data; + void *put = put_s16_labels[data->conv]; + int channel; + int nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + snd_pcm_uframes_t frames1; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + frames1 = frames; + while (frames1-- > 0) { + signed short sample = ulaw2linear(*src); + goto *put; +#define PUT_S16_END after +#include "plugin_ops.h" +#undef PUT_S16_END + after: + src += src_step; + dst += dst_step; + } + } +} + +static void mulaw_encode(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ +#define GET_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS + mulaw_t *data = (mulaw_t *)plugin->extra_data; + void *get = get_s16_labels[data->conv]; + int channel; + int nchannels = plugin->src_format.channels; + signed short sample = 0; + for (channel = 0; channel < nchannels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + snd_pcm_uframes_t frames1; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + frames1 = frames; + while (frames1-- > 0) { + goto *get; +#define GET_S16_END after +#include "plugin_ops.h" +#undef GET_S16_END + after: + *dst = linear2ulaw(sample); + src += src_step; + dst += dst_step; + } + } +} + +static snd_pcm_sframes_t mulaw_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + mulaw_t *data; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; +#ifdef CONFIG_SND_DEBUG + { + unsigned int channel; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + snd_assert(src_channels[channel].area.first % 8 == 0 && + src_channels[channel].area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels[channel].area.first % 8 == 0 && + dst_channels[channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + data = (mulaw_t *)plugin->extra_data; + data->func(plugin, src_channels, dst_channels, frames); + return frames; +} + +int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + mulaw_t *data; + snd_pcm_plugin_t *plugin; + snd_pcm_plugin_format_t *format; + mulaw_f func; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + + if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) { + format = src_format; + func = mulaw_encode; + } + else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) { + format = dst_format; + func = mulaw_decode; + } + else { + snd_BUG(); + return -EINVAL; + } + snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO); + + err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion", + src_format, dst_format, + sizeof(mulaw_t), &plugin); + if (err < 0) + return err; + data = (mulaw_t*)plugin->extra_data; + data->func = func; + data->conv = getput_index(format->format); + plugin->transfer = mulaw_transfer; + *r_plugin = plugin; + return 0; +} diff -Nru a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/pcm_oss.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2185 @@ +/* + * Digital Audio (PCM) abstract layer / OSS compatible + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if 0 +#define PLUGIN_DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcm_plugin.h" +#include +#include +#include + +static int snd_dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; +static int snd_adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; +static int snd_nonblock_open = 0; + +MODULE_AUTHOR("Jaroslav Kysela , Abramo Bagnara "); +MODULE_DESCRIPTION("PCM OSS emulation for ALSA."); +MODULE_LICENSE("GPL"); +MODULE_PARM(snd_dsp_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dsp_map, "PCM device number assigned to 1st OSS device."); +MODULE_PARM_SYNTAX(snd_dsp_map, "default:0,skill:advanced"); +MODULE_PARM(snd_adsp_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_adsp_map, "PCM device number assigned to 2nd OSS device."); +MODULE_PARM_SYNTAX(snd_adsp_map, "default:1,skill:advanced"); +MODULE_PARM(snd_nonblock_open, "i"); +MODULE_PARM_DESC(snd_nonblock_open, "Don't block opening busy PCM devices."); +MODULE_PARM_SYNTAX(snd_nonblock_open, "default:0,skill:advanced"); + +extern int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg); +static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file); +static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file); +static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file); + +EXPORT_NO_SYMBOLS; + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +int snd_pcm_oss_plugin_clear(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_plugin_t *plugin, *next; + + plugin = runtime->oss.plugin_first; + while (plugin) { + next = plugin->next; + snd_pcm_plugin_free(plugin); + plugin = next; + } + runtime->oss.plugin_first = runtime->oss.plugin_last = NULL; + return 0; +} + +int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin) +{ + snd_pcm_runtime_t *runtime = plugin->plug->runtime; + plugin->next = runtime->oss.plugin_first; + plugin->prev = NULL; + if (runtime->oss.plugin_first) { + runtime->oss.plugin_first->prev = plugin; + runtime->oss.plugin_first = plugin; + } else { + runtime->oss.plugin_last = + runtime->oss.plugin_first = plugin; + } + return 0; +} + +int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin) +{ + snd_pcm_runtime_t *runtime = plugin->plug->runtime; + plugin->next = NULL; + plugin->prev = runtime->oss.plugin_last; + if (runtime->oss.plugin_last) { + runtime->oss.plugin_last->next = plugin; + runtime->oss.plugin_last = plugin; + } else { + runtime->oss.plugin_last = + runtime->oss.plugin_first = plugin; + } + return 0; +} + +static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->period_size == runtime->oss.period_bytes) + return frames; + if (runtime->period_size < runtime->oss.period_bytes) + return frames * (runtime->oss.period_bytes / runtime->period_size); + return frames / (runtime->period_size / runtime->oss.period_bytes); +} + +static int snd_pcm_oss_format_from(int format) +{ + switch (format) { + case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW; + case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW; + case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM; + case AFMT_U8: return SNDRV_PCM_FORMAT_U8; + case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE; + case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE; + case AFMT_S8: return SNDRV_PCM_FORMAT_S8; + case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE; + case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE; + case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG; + default: return SNDRV_PCM_FORMAT_U8; + } +} + +static int snd_pcm_oss_format_to(int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; + case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW; + case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM; + case SNDRV_PCM_FORMAT_U8: return AFMT_U8; + case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE; + case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE; + case SNDRV_PCM_FORMAT_S8: return AFMT_S8; + case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE; + case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE; + case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG; + default: return -EINVAL; + } +} + +static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *oss_params, + snd_pcm_hw_params_t *slave_params) +{ + size_t s; + size_t oss_buffer_size, oss_period_size, oss_periods; + size_t min_period_size, max_period_size; + snd_pcm_runtime_t *runtime = substream->runtime; + size_t oss_frame_size; + + oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) * + params_channels(oss_params) / 8; + + oss_buffer_size = snd_pcm_plug_client_size(substream, + snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0)) * oss_frame_size; + oss_buffer_size = 1 << ld2(oss_buffer_size); + if (atomic_read(&runtime->mmap_count)) { + if (oss_buffer_size > runtime->oss.mmap_bytes) + oss_buffer_size = runtime->oss.mmap_bytes; + } + + if (substream->oss.setup && + substream->oss.setup->period_size > 16) + oss_period_size = substream->oss.setup->period_size; + else if (runtime->oss.fragshift) { + oss_period_size = 1 << runtime->oss.fragshift; + if (oss_period_size > oss_buffer_size / 2) + oss_period_size = oss_buffer_size / 2; + } else { + int sd; + size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8; + + oss_period_size = oss_buffer_size; + do { + oss_period_size /= 2; + } while (oss_period_size > bytes_per_sec); + if (runtime->oss.subdivision == 0) { + sd = 4; + if (oss_period_size / sd > 4096) + sd *= 2; + if (oss_period_size / sd < 4096) + sd = 1; + } else + sd = runtime->oss.subdivision; + oss_period_size /= sd; + if (oss_period_size < 16) + oss_period_size = 16; + } + + min_period_size = snd_pcm_plug_client_size(substream, + snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0)); + min_period_size *= oss_frame_size; + min_period_size = 1 << (ld2(min_period_size - 1) + 1); + if (oss_period_size < min_period_size) + oss_period_size = min_period_size; + + max_period_size = snd_pcm_plug_client_size(substream, + snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0)); + max_period_size *= oss_frame_size; + max_period_size = 1 << ld2(max_period_size); + if (oss_period_size > max_period_size) + oss_period_size = max_period_size; + + oss_periods = oss_buffer_size / oss_period_size; + + if (substream->oss.setup) { + if (substream->oss.setup->periods > 1) + oss_periods = substream->oss.setup->periods; + } + + s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, 0); + if (runtime->oss.maxfrags && s > runtime->oss.maxfrags) + s = runtime->oss.maxfrags; + if (oss_periods > s) + oss_periods = s; + + s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, 0); + if (s < 2) + s = 2; + if (oss_periods < s) + oss_periods = s; + + while (oss_period_size * oss_periods > oss_buffer_size) + oss_period_size /= 2; + + snd_assert(oss_period_size >= 16, return -EINVAL); + runtime->oss.period_bytes = oss_period_size; + runtime->oss.periods = oss_periods; + return 0; +} + +static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_hw_params_t params, sparams; + snd_pcm_sw_params_t sw_params; + ssize_t oss_buffer_size, oss_period_size; + size_t oss_frame_size; + int err; + int direct; + int format, sformat, n; + unsigned int sformat_mask; + unsigned int mask; + + if (atomic_read(&runtime->mmap_count)) { + direct = 1; + } else { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + direct = (setup != NULL && setup->direct); + } + + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_param_setinteger(&sparams, SNDRV_PCM_HW_PARAM_PERIODS); + _snd_pcm_hw_param_min(&sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); + if (atomic_read(&runtime->mmap_count)) + mask = 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; + else { + mask = 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; + if (!direct) + mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + } + err = snd_pcm_hw_param_mask(substream, &sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); + if (err < 0) { + snd_printd("No usable accesses\n"); + return -EINVAL; + } + snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_RATE, runtime->oss.rate, 0); + snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); + + format = snd_pcm_oss_format_from(runtime->oss.format); + + sformat_mask = *hw_param_mask(&sparams, SNDRV_PCM_HW_PARAM_FORMAT); + if (direct) + sformat = format; + else + sformat = snd_pcm_plug_slave_format(format, sformat_mask); + + if (sformat < 0 || !(sformat_mask & (1 << sformat))) { + for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) { + if ((sformat_mask & (1 << sformat)) && + snd_pcm_oss_format_to(sformat) >= 0) + break; + } + if (sformat > SNDRV_PCM_FORMAT_LAST) { + snd_printd("Cannot find a format!!!\n"); + return -EINVAL; + } + } + err = _snd_pcm_hw_param_set(&sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); + snd_assert(err >= 0, return err); + + if (direct) { + params = sparams; + } else { + _snd_pcm_hw_params_any(¶ms); + _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); + _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, + snd_pcm_oss_format_from(runtime->oss.format), 0); + _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, + runtime->oss.channels, 0); + _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_RATE, + runtime->oss.rate, 0); + pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n", + params_access(¶ms), params_format(¶ms), + params_channels(¶ms), params_rate(¶ms)); + } + pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n", + params_access(&sparams), params_format(&sparams), + params_channels(&sparams), params_rate(&sparams)); + + oss_frame_size = snd_pcm_format_physical_width(params_format(¶ms)) * + params_channels(¶ms) / 8; + + snd_pcm_oss_plugin_clear(substream); + if (!direct) { + /* add necessary plugins */ + snd_pcm_oss_plugin_clear(substream); + if ((err = snd_pcm_plug_format_plugins(substream, + ¶ms, + &sparams)) < 0) { + snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err); + snd_pcm_oss_plugin_clear(substream); + return err; + } + if (runtime->oss.plugin_first) { + snd_pcm_plugin_t *plugin; + if ((err = snd_pcm_plugin_build_io(substream, &sparams, &plugin)) < 0) { + snd_printd("snd_pcm_plugin_build_io failed: %i\n", err); + snd_pcm_oss_plugin_clear(substream); + return err; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + err = snd_pcm_plugin_append(plugin); + } else { + err = snd_pcm_plugin_insert(plugin); + } + if (err < 0) { + snd_pcm_oss_plugin_clear(substream); + return err; + } + } + } + + err = snd_pcm_oss_period_size(substream, ¶ms, &sparams); + if (err < 0) + return err; + + n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size); + err = snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, 0); + snd_assert(err >= 0, return err); + + err = snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_PERIODS, + runtime->oss.periods, 0); + snd_assert(err >= 0, return err); + + snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + + if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, &sparams)) < 0) { + snd_printd("HW_PARAMS failed: %i\n", err); + return err; + } + + memset(&sw_params, 0, sizeof(sw_params)); + if (runtime->oss.trigger) { + sw_params.start_threshold = 1; + } else { + sw_params.start_threshold = runtime->boundary; + } + if (atomic_read(&runtime->mmap_count)) + sw_params.stop_threshold = runtime->boundary; + else + sw_params.stop_threshold = runtime->buffer_size; + sw_params.tstamp_mode = SNDRV_PCM_TSTAMP_NONE; + sw_params.period_step = 1; + sw_params.sleep_min = 0; + sw_params.avail_min = runtime->period_size; + sw_params.xfer_align = 1; + sw_params.silence_threshold = 0; + sw_params.silence_size = 0; + + if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params)) < 0) { + snd_printd("SW_PARAMS failed: %i\n", err); + return err; + } + runtime->control->avail_min = runtime->period_size; + + runtime->oss.periods = params_periods(&sparams); + oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(&sparams)); + snd_assert(oss_period_size >= 0, return -EINVAL); + if (runtime->oss.plugin_first) { + err = snd_pcm_plug_alloc(substream, oss_period_size); + if (err < 0) + return err; + } + oss_period_size *= oss_frame_size; + + oss_buffer_size = oss_period_size * runtime->oss.periods; + snd_assert(oss_buffer_size >= 0, return -EINVAL); + + runtime->oss.period_bytes = oss_period_size; + runtime->oss.buffer_bytes = oss_buffer_size; + + pdprintf("oss: period bytes = %i, buffer bytes = %i\n", + runtime->oss.period_bytes, + runtime->oss.buffer_bytes); + pdprintf("slave: period_size = %i, buffer_size = %i\n", + params_period_size(&sparams), + params_buffer_size(&sparams)); + + runtime->oss.format = snd_pcm_oss_format_to(params_format(¶ms)); + runtime->oss.channels = params_channels(¶ms); + runtime->oss.rate = params_rate(¶ms); + + runtime->oss.params = 0; + runtime->oss.prepare = 1; + if (runtime->oss.buffer != NULL) + vfree(runtime->oss.buffer); + runtime->oss.buffer = vmalloc(runtime->oss.period_bytes); + runtime->oss.buffer_used = 0; + snd_assert(runtime->dma_area != NULL, return -EIO); + snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes)); + return 0; +} + +static int snd_pcm_oss_get_active_substream(snd_pcm_oss_file_t *pcm_oss_file, snd_pcm_substream_t **r_substream) +{ + int idx, err; + snd_pcm_substream_t *asubstream = NULL, *substream; + + for (idx = 0; idx < 2; idx++) { + substream = pcm_oss_file->streams[idx]; + if (substream == NULL) + continue; + if (asubstream == NULL) + asubstream = substream; + if (substream->runtime->oss.params) { + err = snd_pcm_oss_change_params(substream); + if (err < 0) + return err; + } + } + snd_assert(asubstream != NULL, return -EIO); + if (r_substream) + *r_substream = asubstream; + return 0; +} + +static int snd_pcm_oss_prepare(snd_pcm_substream_t *substream) +{ + int err; + snd_pcm_runtime_t *runtime = substream->runtime; + + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, 0); + if (err < 0) { + snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n"); + return err; + } + runtime->oss.prepare = 0; + runtime->oss.prev_hw_ptr_interrupt = 0; + + return 0; +} + +static int snd_pcm_oss_make_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + int err; + + if (substream == NULL) + return 0; + runtime = substream->runtime; + if (runtime->oss.params) { + err = snd_pcm_oss_change_params(substream); + if (err < 0) + return err; + } + if (runtime->oss.prepare) { + err = snd_pcm_oss_prepare(substream); + if (err < 0) + return err; + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + mm_segment_t fs; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) + fs = snd_enter_user(); + ret = snd_pcm_lib_write(substream, ptr, frames); + if (in_kernel) + snd_leave_user(fs); + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + /* test, if we can't store new data, because the stream */ + /* has not been started */ + if (runtime->status->state == SNDRV_PCM_STATE_PREPARED) + return -EAGAIN; + } + return ret; +} + +snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + mm_segment_t fs; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0); + if (ret < 0) + break; + } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) + fs = snd_enter_user(); + ret = snd_pcm_lib_read(substream, ptr, frames); + if (in_kernel) + snd_leave_user(fs); + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + } + return ret; +} + +snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + mm_segment_t fs; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) + fs = snd_enter_user(); + ret = snd_pcm_lib_writev(substream, bufs, frames); + if (in_kernel) + snd_leave_user(fs); + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + /* test, if we can't store new data, because the stream */ + /* has not been started */ + if (runtime->status->state == SNDRV_PCM_STATE_PREPARED) + return -EAGAIN; + } + return ret; +} + +snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + mm_segment_t fs; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0); + if (ret < 0) + break; + } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) + fs = snd_enter_user(); + ret = snd_pcm_lib_readv(substream, bufs, frames); + if (in_kernel) + snd_leave_user(fs); + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + } + return ret; +} + +static ssize_t snd_pcm_oss_write2(snd_pcm_substream_t *substream, const char *buf, size_t bytes, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t frames, frames1; + if (runtime->oss.plugin_first) { + snd_pcm_plugin_channel_t *channels; + size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8; + if (!in_kernel) { + if (copy_from_user(runtime->oss.buffer, buf, bytes)) + return -EFAULT; + buf = runtime->oss.buffer; + } + frames = bytes / oss_frame_bytes; + frames1 = snd_pcm_plug_client_channels_buf(substream, (char *)buf, frames, &channels); + if (frames1 < 0) + return frames1; + frames1 = snd_pcm_plug_write_transfer(substream, channels, frames1); + if (frames1 <= 0) + return frames1; + bytes = frames1 * oss_frame_bytes; + } else { + frames = bytes_to_frames(runtime, bytes); + frames1 = snd_pcm_oss_write3(substream, buf, frames, in_kernel); + if (frames1 <= 0) + return frames1; + bytes = frames_to_bytes(runtime, frames1); + } + return bytes; +} + +static ssize_t snd_pcm_oss_write1(snd_pcm_substream_t *substream, const char *buf, size_t bytes) +{ + size_t xfer = 0; + ssize_t tmp; + snd_pcm_runtime_t *runtime = substream->runtime; + + if (atomic_read(&runtime->mmap_count)) + return -ENXIO; + + if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) + return tmp; + while (bytes > 0) { + if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { + tmp = bytes; + if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes) + tmp = runtime->oss.period_bytes - runtime->oss.buffer_used; + if (tmp > 0) { + if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) + return xfer > 0 ? xfer : -EFAULT; + } + runtime->oss.buffer_used += tmp; + buf += tmp; + bytes -= tmp; + xfer += tmp; + if (runtime->oss.buffer_used == runtime->oss.period_bytes) { + tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); + if (tmp <= 0) + return xfer > 0 ? xfer : tmp; + runtime->oss.bytes += tmp; + runtime->oss.buffer_used = 0; + } + } else { + tmp = snd_pcm_oss_write2(substream, (char *)buf, runtime->oss.period_bytes, 0); + if (tmp <= 0) + return xfer > 0 ? xfer : tmp; + runtime->oss.bytes += tmp; + buf += tmp; + bytes -= tmp; + xfer += tmp; + } + } + return xfer; +} + +static ssize_t snd_pcm_oss_read2(snd_pcm_substream_t *substream, char *buf, size_t bytes, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t frames, frames1; + char *final_dst = buf; + if (runtime->oss.plugin_first) { + snd_pcm_plugin_channel_t *channels; + size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8; + if (!in_kernel) + buf = runtime->oss.buffer; + frames = bytes / oss_frame_bytes; + frames1 = snd_pcm_plug_client_channels_buf(substream, buf, frames, &channels); + if (frames1 < 0) + return frames1; + frames1 = snd_pcm_plug_read_transfer(substream, channels, frames1); + if (frames1 <= 0) + return frames1; + bytes = frames1 * oss_frame_bytes; + if (!in_kernel && copy_to_user(final_dst, buf, bytes)) + return -EFAULT; + } else { + frames = bytes_to_frames(runtime, bytes); + frames1 = snd_pcm_oss_read3(substream, buf, frames, in_kernel); + if (frames1 <= 0) + return frames1; + bytes = frames_to_bytes(runtime, frames1); + } + return bytes; +} + +static ssize_t snd_pcm_oss_read1(snd_pcm_substream_t *substream, char *buf, size_t bytes) +{ + size_t xfer = 0; + ssize_t tmp; + snd_pcm_runtime_t *runtime = substream->runtime; + + if (atomic_read(&runtime->mmap_count)) + return -ENXIO; + + if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) + return tmp; + while (bytes > 0) { + if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { + if (runtime->oss.buffer_used == 0) { + tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); + if (tmp <= 0) + return xfer > 0 ? xfer : tmp; + runtime->oss.bytes += tmp; + runtime->oss.buffer_used = runtime->oss.period_bytes; + } + tmp = bytes; + if ((size_t) tmp > runtime->oss.buffer_used) + tmp = runtime->oss.buffer_used; + if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_bytes - runtime->oss.buffer_used), tmp)) + return xfer > 0 ? xfer : -EFAULT; + buf += tmp; + bytes -= tmp; + xfer += tmp; + runtime->oss.buffer_used -= tmp; + } else { + tmp = snd_pcm_oss_read2(substream, (char *)buf, runtime->oss.period_bytes, 0); + if (tmp <= 0) + return xfer > 0 ? xfer : tmp; + runtime->oss.bytes += tmp; + buf += tmp; + bytes -= tmp; + xfer += tmp; + } + } + return xfer; +} + +static int snd_pcm_oss_reset(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream != NULL) { + snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + substream->runtime->oss.prepare = 1; + } + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream != NULL) { + snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + substream->runtime->oss.prepare = 1; + } + return 0; +} + +static int snd_pcm_oss_sync(snd_pcm_oss_file_t *pcm_oss_file) +{ + int err = 0; + unsigned int saved_f_flags; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + ssize_t result; + wait_queue_t wait; + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream != NULL) { + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + + runtime = substream->runtime; + if (runtime->oss.buffer_used > 0) { + snd_pcm_format_set_silence(runtime->format, + runtime->oss.buffer + runtime->oss.buffer_used, + bytes_to_samples(runtime, runtime->oss.period_bytes - runtime->oss.buffer_used)); + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + result = snd_pcm_oss_write2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); + if (result > 0) { + runtime->oss.buffer_used = 0; + break; + } + if (result != 0 && result != -EAGAIN) + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + result = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (result < 0) + return result; + } + saved_f_flags = substream->ffile->f_flags; + substream->ffile->f_flags &= ~O_NONBLOCK; + err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0); + substream->ffile->f_flags = saved_f_flags; + if (err < 0) + return err; + runtime->oss.prepare = 1; + } + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream != NULL) { + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + runtime = substream->runtime; + err = snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + if (err < 0) + return err; + runtime->oss.buffer_used = 0; + runtime->oss.prepare = 1; + } + return 0; +} + +static int snd_pcm_oss_set_rate(snd_pcm_oss_file_t *pcm_oss_file, int rate) +{ + int idx; + + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + if (rate < 1000) + rate = 1000; + else if (rate > 48000) + rate = 48000; + if (runtime->oss.rate != rate) { + runtime->oss.params = 1; + runtime->oss.rate = rate; + } + } + return snd_pcm_oss_get_rate(pcm_oss_file); +} + +static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.rate; +} + +static int snd_pcm_oss_set_channels(snd_pcm_oss_file_t *pcm_oss_file, int channels) +{ + int idx; + if (channels < 1) + channels = 1; + if (channels > 128) + return -EINVAL; + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + if (runtime->oss.channels != channels) { + runtime->oss.params = 1; + runtime->oss.channels = channels; + } + } + return snd_pcm_oss_get_channels(pcm_oss_file); +} + +static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.channels; +} + +static int snd_pcm_oss_get_block_size(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.period_bytes; +} + +static int snd_pcm_oss_get_formats(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + int direct; + snd_pcm_hw_params_t params; + unsigned int formats = 0; + unsigned int format_mask; + int fmt; + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + if (atomic_read(&substream->runtime->mmap_count)) { + direct = 1; + } else { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + direct = (setup != NULL && setup->direct); + } + if (!direct) + return AFMT_MU_LAW | AFMT_U8 | + AFMT_S16_LE | AFMT_S16_BE | + AFMT_S8 | AFMT_U16_LE | + AFMT_U16_BE; + _snd_pcm_hw_params_any(¶ms); + err = snd_pcm_hw_refine(substream, ¶ms); + snd_assert(err >= 0, return err); + format_mask = *hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT); + for (fmt = 0; fmt < 32; ++fmt) { + if (format_mask & (1 << fmt)) { + int f = snd_pcm_oss_format_to(fmt); + if (f >= 0) + formats |= f; + } + } + return formats; +} + +static int snd_pcm_oss_set_format(snd_pcm_oss_file_t *pcm_oss_file, int format) +{ + int formats, idx; + + if (format != AFMT_QUERY) { + formats = snd_pcm_oss_get_formats(pcm_oss_file); + if (!(formats & format)) + format = AFMT_U8; + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + if (runtime->oss.format != format) { + runtime->oss.params = 1; + runtime->oss.format = format; + } + } + } + return snd_pcm_oss_get_format(pcm_oss_file); +} + +static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.format; +} + +static int snd_pcm_oss_set_subdivide1(snd_pcm_substream_t *substream, int subdivide) +{ + snd_pcm_runtime_t *runtime; + + if (substream == NULL) + return 0; + runtime = substream->runtime; + if (subdivide == 0) { + subdivide = runtime->oss.subdivision; + if (subdivide == 0) + subdivide = 1; + return subdivide; + } + if (runtime->oss.subdivision || runtime->oss.fragshift) + return -EINVAL; + if (subdivide != 1 && subdivide != 2 && subdivide != 4 && + subdivide != 8 && subdivide != 16) + return -EINVAL; + runtime->oss.subdivision = subdivide; + runtime->oss.params = 1; + return subdivide; +} + +static int snd_pcm_oss_set_subdivide(snd_pcm_oss_file_t *pcm_oss_file, int subdivide) +{ + int err = -EINVAL, idx; + + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + if (substream == NULL) + continue; + if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0) + return err; + } + return err; +} + +static int snd_pcm_oss_set_fragment1(snd_pcm_substream_t *substream, unsigned int val) +{ + snd_pcm_runtime_t *runtime; + + if (substream == NULL) + return 0; + runtime = substream->runtime; + if (runtime->oss.subdivision || runtime->oss.fragshift) + return -EINVAL; + runtime->oss.fragshift = val & 0xffff; + runtime->oss.maxfrags = (val >> 16) & 0xffff; + if (runtime->oss.fragshift < 4) /* < 16 */ + runtime->oss.fragshift = 4; + if (runtime->oss.maxfrags < 2) + runtime->oss.maxfrags = 2; + runtime->oss.params = 1; + return 0; +} + +static int snd_pcm_oss_set_fragment(snd_pcm_oss_file_t *pcm_oss_file, unsigned int val) +{ + int err = -EINVAL, idx; + + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + if (substream == NULL) + continue; + if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0) + return err; + } + return err; +} + +static int snd_pcm_oss_nonblock(struct file * file) +{ + file->f_flags |= O_NONBLOCK; + return 0; +} + +static int snd_pcm_oss_get_caps1(snd_pcm_substream_t *substream, int res) +{ + + if (substream == NULL) { + res &= ~DSP_CAP_DUPLEX; + return res; + } +#ifdef DSP_CAP_MULTI + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->pstr->substream_count > 1) + res |= DSP_CAP_MULTI; +#endif + /* DSP_CAP_REALTIME is set all times: */ + /* all ALSA drivers can return actual pointer in ring buffer */ +#if defined(DSP_CAP_REALTIME) && 0 + { + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH)) + res &= ~DSP_CAP_REALTIME; + } +#endif + return res; +} + +static int snd_pcm_oss_get_caps(snd_pcm_oss_file_t *pcm_oss_file) +{ + int result, idx; + + result = DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_DUPLEX | DSP_CAP_REALTIME; + for (idx = 0; idx < 2; idx++) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + result = snd_pcm_oss_get_caps1(substream, result); + } + result |= 0x0001; /* revision - same as SB AWE 64 */ + return result; +} + +static void snd_pcm_oss_simulate_fill(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t appl_ptr; + appl_ptr = runtime->hw_ptr_interrupt + runtime->buffer_size; + appl_ptr %= runtime->boundary; + runtime->control->appl_ptr = appl_ptr; +} + +static int snd_pcm_oss_set_trigger(snd_pcm_oss_file_t *pcm_oss_file, int trigger) +{ + snd_pcm_runtime_t *runtime; + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + int err, cmd; + + psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + + if (psubstream) { + if ((err = snd_pcm_oss_make_ready(psubstream)) < 0) + return err; + if (atomic_read(&psubstream->runtime->mmap_count)) + snd_pcm_oss_simulate_fill(psubstream); + } + if (csubstream) { + if ((err = snd_pcm_oss_make_ready(csubstream)) < 0) + return err; + } + if (psubstream) { + runtime = psubstream->runtime; + if (trigger & PCM_ENABLE_OUTPUT) { + if (runtime->oss.trigger) + goto _skip1; + runtime->oss.trigger = 1; + runtime->start_threshold = 1; + cmd = SNDRV_PCM_IOCTL_START; + } else { + if (!runtime->oss.trigger) + goto _skip1; + runtime->oss.trigger = 0; + runtime->start_threshold = runtime->boundary; + cmd = SNDRV_PCM_IOCTL_DROP; + runtime->oss.prepare = 1; + } + err = snd_pcm_kernel_playback_ioctl(psubstream, cmd, 0); + if (err < 0) + return err; + } + _skip1: + if (csubstream) { + runtime = csubstream->runtime; + if (trigger & PCM_ENABLE_INPUT) { + if (runtime->oss.trigger) + goto _skip2; + runtime->oss.trigger = 1; + runtime->start_threshold = 1; + cmd = SNDRV_PCM_IOCTL_START; + } else { + if (!runtime->oss.trigger) + goto _skip2; + runtime->oss.trigger = 0; + runtime->start_threshold = runtime->boundary; + cmd = SNDRV_PCM_IOCTL_DROP; + runtime->oss.prepare = 1; + } + err = snd_pcm_kernel_capture_ioctl(csubstream, cmd, 0); + if (err < 0) + return err; + } + _skip2: + return 0; +} + +static int snd_pcm_oss_get_trigger(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + int result = 0; + + psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger) + result |= PCM_ENABLE_OUTPUT; + if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger) + result |= PCM_ENABLE_INPUT; + return result; +} + +static int snd_pcm_oss_get_odelay(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t delay; + int err; + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream == NULL) + return -EINVAL; + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + runtime = substream->runtime; + if (runtime->oss.params || runtime->oss.prepare) + return 0; + err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay); + if (err == -EPIPE) + delay = 0; /* hack for broken OSS applications */ + else if (err < 0) + return err; + return snd_pcm_oss_bytes(substream, delay); +} + +static int snd_pcm_oss_get_ptr(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct count_info * _info) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_status_t status; + struct count_info info; + int err; + + if (_info == NULL) + return -EFAULT; + substream = pcm_oss_file->streams[stream]; + if (substream == NULL) + return -EINVAL; + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + runtime = substream->runtime; + if (runtime->oss.params || runtime->oss.prepare) { + memset(&info, 0, sizeof(info)); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; + } + memset(&status, 0, sizeof(status)); + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_STATUS, &status); + if (err < 0) + return err; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + info.bytes = runtime->oss.bytes - snd_pcm_oss_bytes(substream, runtime->buffer_size - status.avail); + } else { + info.bytes = runtime->oss.bytes + snd_pcm_oss_bytes(substream, status.avail); + } + info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size); + if (atomic_read(&runtime->mmap_count)) { + snd_pcm_sframes_t n; + n = runtime->hw_ptr_interrupt - runtime->oss.prev_hw_ptr_interrupt; + if (n < 0) + n += runtime->boundary; + info.blocks = n / runtime->period_size; + runtime->oss.prev_hw_ptr_interrupt = runtime->hw_ptr_interrupt; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_pcm_oss_simulate_fill(substream); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + info.blocks = (runtime->buffer_size - status.avail) / runtime->period_size; + else + info.blocks = status.avail / runtime->period_size; + } + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_pcm_oss_get_space(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct audio_buf_info *_info) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_status_t status; + struct audio_buf_info info; + int err; + + if (_info == NULL) + return -EFAULT; + substream = pcm_oss_file->streams[stream]; + if (substream == NULL) + return -EINVAL; + runtime = substream->runtime; + + if (runtime->oss.params && + (err = snd_pcm_oss_change_params(substream)) < 0) + return err; + + info.fragsize = runtime->oss.period_bytes; + info.fragstotal = runtime->periods; + memset(&status, 0, sizeof(status)); + if (runtime->oss.prepare) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + info.bytes = runtime->oss.period_bytes * runtime->periods; + info.fragments = runtime->periods; + } else { + info.bytes = 0; + info.fragments = 0; + } + } else { + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_STATUS, &status); + if (err < 0) + return err; + info.bytes = snd_pcm_oss_bytes(substream, status.avail); + info.fragments = status.avail / runtime->period_size; + } + +#if 0 + /* very experimental stuff to get Quake2 working */ + runtime->oss.period = (info.periods - 1) << 16; + for (tmp = info.fragsize; tmp > 1; tmp >>= 1) + runtime->oss.period++; + runtime->oss.subdivision = 1; /* disable SUBDIVIDE */ +#endif + // printk("space: bytes = %i, periods = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.periods, info.fragstotal, info.fragsize); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_pcm_oss_get_mapbuf(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct buffmem_desc * _info) +{ + // it won't be probably implemented + // snd_printd("TODO: snd_pcm_oss_get_mapbuf\n"); + return -EINVAL; +} + +static snd_pcm_oss_setup_t *snd_pcm_oss_look_for_setup(snd_pcm_t *pcm, int stream, const char *task_name) +{ + const char *ptr, *ptrl; + snd_pcm_oss_setup_t *setup; + + down(&pcm->streams[stream].oss.setup_mutex); + for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) { + if (!strcmp(setup->task_name, task_name)) { + up(&pcm->streams[stream].oss.setup_mutex); + return setup; + } + } + ptr = ptrl = task_name; + while (*ptr) { + if (*ptr == '/') + ptrl = ptr + 1; + ptr++; + } + if (ptrl == task_name) { + goto __not_found; + return NULL; + } + for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) { + if (!strcmp(setup->task_name, ptrl)) { + up(&pcm->streams[stream].oss.setup_mutex); + return setup; + } + } + __not_found: + up(&pcm->streams[stream].oss.setup_mutex); + return NULL; +} + +static void snd_pcm_oss_init_substream(snd_pcm_substream_t *substream, + snd_pcm_oss_setup_t *setup, + int minor) +{ + snd_pcm_runtime_t *runtime; + + substream->oss.oss = 1; + substream->oss.setup = setup; + runtime = substream->runtime; + runtime->oss.params = 1; + runtime->oss.trigger = 1; + runtime->oss.rate = 8000; + switch (SNDRV_MINOR_OSS_DEVICE(minor)) { + case SNDRV_MINOR_OSS_PCM_8: + runtime->oss.format = AFMT_U8; + break; + case SNDRV_MINOR_OSS_PCM_16: + runtime->oss.format = AFMT_S16_LE; + break; + default: + runtime->oss.format = AFMT_MU_LAW; + } + runtime->oss.channels = 1; + runtime->oss.fragshift = 0; + runtime->oss.maxfrags = 0; + runtime->oss.subdivision = 0; +} + +static void snd_pcm_oss_release_substream(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + runtime = substream->runtime; + if (runtime->oss.buffer) + vfree(runtime->oss.buffer); + snd_pcm_oss_plugin_clear(substream); + substream->oss.file = NULL; + substream->oss.oss = 0; +} + +static int snd_pcm_oss_release_file(snd_pcm_oss_file_t *pcm_oss_file) +{ + int cidx; + snd_assert(pcm_oss_file != NULL, return -ENXIO); + for (cidx = 0; cidx < 2; ++cidx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[cidx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + + spin_lock_irq(&runtime->lock); + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + spin_unlock_irq(&runtime->lock); + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + substream->ops->close(substream); + substream->ffile = NULL; + snd_pcm_oss_release_substream(substream); + snd_pcm_release_substream(substream); + } + snd_magic_kfree(pcm_oss_file); + return 0; +} + +static int snd_pcm_oss_open_file(struct file *file, + snd_pcm_t *pcm, + snd_pcm_oss_file_t **rpcm_oss_file, + int minor, + snd_pcm_oss_setup_t *psetup, + snd_pcm_oss_setup_t *csetup) +{ + int err = 0; + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + unsigned int f_mode = file->f_mode; + + snd_assert(rpcm_oss_file != NULL, return -EINVAL); + *rpcm_oss_file = NULL; + + pcm_oss_file = snd_magic_kcalloc(snd_pcm_oss_file_t, 0, GFP_KERNEL); + if (pcm_oss_file == NULL) + return -ENOMEM; + + if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) && + (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX)) + f_mode = FMODE_WRITE; + if ((f_mode & FMODE_WRITE) && !(psetup && psetup->disable)) { + if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &psubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK] = psubstream; + } + if ((f_mode & FMODE_READ) && !(csetup && csetup->disable)) { + if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE, + &csubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE] = csubstream; + } + + if (psubstream == NULL && csubstream == NULL) { + snd_pcm_oss_release_file(pcm_oss_file); + return -EINVAL; + } + if (psubstream != NULL) { + psubstream->oss.file = pcm_oss_file; + err = snd_pcm_hw_constraints_init(psubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_init failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + if ((err = psubstream->ops->open(psubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + err = snd_pcm_hw_constraints_complete(psubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_complete failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + psubstream->ffile = file; + snd_pcm_oss_init_substream(psubstream, psetup, minor); + } + if (csubstream != NULL) { + csubstream->oss.file = pcm_oss_file; + err = snd_pcm_hw_constraints_init(csubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_init failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + if ((err = csubstream->ops->open(csubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + err = snd_pcm_hw_constraints_complete(csubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_complete failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + csubstream->ffile = file; + snd_pcm_oss_init_substream(csubstream, csetup, minor); + } + + file->private_data = pcm_oss_file; + *rpcm_oss_file = pcm_oss_file; + return 0; +} + + +static int snd_pcm_oss_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + int cardnum = SNDRV_MINOR_OSS_CARD(minor); + int device; + int err; + char task_name[32]; + snd_pcm_t *pcm; + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_oss_setup_t *psetup = NULL, *csetup = NULL; + int nonblock; + wait_queue_t wait; + + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + device = SNDRV_MINOR_OSS_DEVICE(minor) == SNDRV_MINOR_OSS_PCM1 ? + snd_adsp_map[cardnum] : snd_dsp_map[cardnum]; + +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + device]; + if (pcm == NULL) { + err = -ENODEV; + goto __error1; + } + if (!try_inc_mod_count(pcm->card->module)) { + err = -EFAULT; + goto __error1; + } + if (snd_task_name(current, task_name, sizeof(task_name)) < 0) { + err = -EFAULT; + goto __error1; + } + if (file->f_mode & FMODE_WRITE) + psetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK, task_name); + if (file->f_mode & FMODE_READ) + csetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE, task_name); + + nonblock = !!(file->f_flags & O_NONBLOCK); + if (psetup && !psetup->disable) { + if (psetup->nonblock) + nonblock = 1; + else if (psetup->block) + nonblock = 0; + else if (!nonblock) + nonblock = snd_nonblock_open; + } else if (csetup && !csetup->disable) { + if (csetup->nonblock) + nonblock = 1; + else if (csetup->block) + nonblock = 0; + else if (!nonblock) + nonblock = snd_nonblock_open; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&pcm->open_wait, &wait); + while (1) { + down(&pcm->open_mutex); + err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file, + minor, psetup, csetup); + if (err >= 0) + break; + up(&pcm->open_mutex); + if (err == -EAGAIN) { + if (nonblock) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&pcm->open_wait, &wait); + if (err < 0) + goto __error; + up(&pcm->open_mutex); + return err; + + __error: + dec_mod_count(pcm->card->module); + __error1: +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return err; +} + +static int snd_pcm_oss_release(struct inode *inode, struct file *file) +{ + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + snd_pcm_oss_file_t *pcm_oss_file; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream == NULL) + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + snd_assert(substream != NULL, return -ENXIO); + pcm = substream->pcm; + snd_pcm_oss_sync(pcm_oss_file); + down(&pcm->open_mutex); + snd_pcm_oss_release_file(pcm_oss_file); + up(&pcm->open_mutex); + wake_up(&pcm->open_wait); + dec_mod_count(pcm->card->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +static int snd_pcm_oss_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_pcm_oss_file_t *pcm_oss_file; + int res; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + if (cmd == OSS_GETVERSION) + return put_user(SNDRV_OSS_VERSION, (int *)arg) ? -EFAULT : 0; + if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS (grrr) compatibility */ + snd_pcm_substream_t *substream; + int idx; + for (idx = 0; idx < 2; ++idx) { + substream = pcm_oss_file->streams[idx]; + if (substream != NULL) + break; + } + snd_assert(substream != NULL, return -ENXIO); + return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg); + } + if (((cmd >> 8) & 0xff) != 'P') + return -EINVAL; + switch (cmd) { + case SNDCTL_DSP_RESET: + return snd_pcm_oss_reset(pcm_oss_file); + case SNDCTL_DSP_SYNC: + return snd_pcm_oss_sync(pcm_oss_file); + case SNDCTL_DSP_SPEED: + if (get_user(res, (int *)arg)) + return -EFAULT; + if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_READ_RATE: + res = snd_pcm_oss_get_rate(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_STEREO: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = res > 0 ? 2 : 1; + if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0) + return res; + return put_user(--res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_GETBLKSIZE: + res = snd_pcm_oss_get_block_size(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_SETFMT: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = snd_pcm_oss_set_format(pcm_oss_file, res); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_READ_BITS: + res = snd_pcm_oss_get_format(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_CHANNELS: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = snd_pcm_oss_set_channels(pcm_oss_file, res); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_READ_CHANNELS: + res = snd_pcm_oss_get_channels(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EIO; + case SNDCTL_DSP_POST: /* to do */ + return 0; + case SNDCTL_DSP_SUBDIVIDE: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = snd_pcm_oss_set_subdivide(pcm_oss_file, res); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(res, (int *)arg)) + return -EFAULT; + return snd_pcm_oss_set_fragment(pcm_oss_file, res); + case SNDCTL_DSP_GETFMTS: + res = snd_pcm_oss_get_formats(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + return snd_pcm_oss_get_space(pcm_oss_file, + cmd == SNDCTL_DSP_GETISPACE ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, + (struct audio_buf_info *) arg); + case SNDCTL_DSP_NONBLOCK: + return snd_pcm_oss_nonblock(file); + case SNDCTL_DSP_GETCAPS: + res = snd_pcm_oss_get_caps(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_GETTRIGGER: + res = snd_pcm_oss_get_trigger(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_SETTRIGGER: + if (get_user(res, (int *)arg)) + return -EFAULT; + return snd_pcm_oss_set_trigger(pcm_oss_file, res); + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + return snd_pcm_oss_get_ptr(pcm_oss_file, + cmd == SNDCTL_DSP_GETIPTR ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, + (struct count_info *) arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + return snd_pcm_oss_get_mapbuf(pcm_oss_file, + cmd == SNDCTL_DSP_MAPINBUF ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, + (struct buffmem_desc *) arg); + case SNDCTL_DSP_SETSYNCRO: + /* stop DMA now.. */ + return 0; + case SNDCTL_DSP_SETDUPLEX: + if (snd_pcm_oss_get_caps(pcm_oss_file) & DSP_CAP_DUPLEX) + return 0; + return -EIO; + case SNDCTL_DSP_GETODELAY: + res = snd_pcm_oss_get_odelay(pcm_oss_file); + if (res < 0) { + /* it's for sure, some broken apps don't check for error codes */ + put_user(0, (int *)arg); + return res; + } + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_PROFILE: + return 0; /* silently ignore */ + default: + snd_printd("pcm_oss: unknown command = 0x%x\n", cmd); + } + return -EINVAL; +} + +static ssize_t snd_pcm_oss_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *substream; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream == NULL) + return -ENXIO; + return snd_pcm_oss_read1(substream, buf, count); +} + +static ssize_t snd_pcm_oss_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *substream; + long result; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream == NULL) + return -ENXIO; + up(&file->f_dentry->d_inode->i_sem); + result = snd_pcm_oss_write1(substream, buf, count); + down(&file->f_dentry->d_inode->i_sem); + return result; +} + +static int snd_pcm_oss_playback_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (atomic_read(&runtime->mmap_count)) + return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + else + return snd_pcm_playback_ready(substream); +} + +static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (atomic_read(&runtime->mmap_count)) + return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + else + return snd_pcm_capture_ready(substream); +} + +static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) +{ + snd_pcm_oss_file_t *pcm_oss_file; + unsigned int mask; + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return 0); + + psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + + mask = 0; + if (psubstream != NULL) { + snd_pcm_runtime_t *runtime = psubstream->runtime; + spin_lock_irq(&runtime->lock); + poll_wait(file, &runtime->sleep, wait); + if (runtime->status->state != SNDRV_PCM_STATE_DRAINING && + (runtime->status->state != SNDRV_PCM_STATE_RUNNING || + snd_pcm_oss_playback_ready(psubstream))) + mask |= POLLOUT | POLLWRNORM; + spin_unlock_irq(&runtime->lock); + } + if (csubstream != NULL) { + snd_pcm_runtime_t *runtime = csubstream->runtime; + spin_lock_irq(&runtime->lock); + poll_wait(file, &runtime->sleep, wait); + if (runtime->status->state != SNDRV_PCM_STATE_RUNNING || + snd_pcm_oss_capture_ready(csubstream)) + mask |= POLLIN | POLLRDNORM; + spin_unlock_irq(&runtime->lock); + } + + return mask; +} + +static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area) +{ + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *substream = NULL; + snd_pcm_runtime_t *runtime; + int err; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + switch ((area->vm_flags & (VM_READ | VM_WRITE))) { + case VM_READ | VM_WRITE: + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream) + break; + /* Fall through */ + case VM_READ: + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + break; + case VM_WRITE: + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + break; + default: + return -EINVAL; + } + /* set VM_READ access as well to fix memset() routines that do + reads before writes (to improve performance) */ + area->vm_flags |= VM_READ; + if (substream == NULL) + return -ENXIO; + runtime = substream->runtime; + if (!(runtime->info & SNDRV_PCM_INFO_MMAP_VALID)) + return -EIO; + if (runtime->info & SNDRV_PCM_INFO_INTERLEAVED) + runtime->access = SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; + else + return -EIO; + + if (runtime->oss.params) { + if ((err = snd_pcm_oss_change_params(substream)) < 0) + return err; + } + if (runtime->oss.plugin_first != NULL) + return -EIO; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + if (area->vm_pgoff != 0) +#else + if (area->vm_offset != 0) +#endif + return -EINVAL; + + err = snd_pcm_mmap_data(substream, file, area); + if (err < 0) + return err; + runtime->oss.mmap_bytes = area->vm_end - area->vm_start; + /* In mmap mode we never stop */ + runtime->stop_threshold = runtime->boundary; + + return 0; +} + +/* + * /proc interface + */ + +static void snd_pcm_oss_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; + snd_pcm_oss_setup_t *setup = pstr->oss.setup_list; + down(&pstr->oss.setup_mutex); + while (setup) { + snd_iprintf(buffer, "%s %u %u%s%s%s%s\n", + setup->task_name, + setup->periods, + setup->period_size, + setup->disable ? " disable" : "", + setup->direct ? " direct" : "", + setup->block ? " block" : "", + setup->nonblock ? " non-block" : ""); + setup = setup->next; + } + up(&pstr->oss.setup_mutex); +} + +static void snd_pcm_oss_proc_free_setup_list(snd_pcm_str_t * pstr) +{ + unsigned int idx; + snd_pcm_substream_t *substream; + snd_pcm_oss_setup_t *setup, *setupn; + + for (idx = 0, substream = pstr->substream; + idx < pstr->substream_count; idx++, substream = substream->next) + substream->oss.setup = NULL; + for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL; + setup; setup = setupn) { + setupn = setup->next; + kfree(setup->task_name); + kfree(setup); + } + pstr->oss.setup_list = NULL; +} + +static void snd_pcm_oss_proc_write(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; + char line[512], str[32], task_name[32], *ptr; + int idx1; + snd_pcm_oss_setup_t *setup, *setup1, template; + + while (!snd_info_get_line(buffer, line, sizeof(line))) { + down(&pstr->oss.setup_mutex); + memset(&template, 0, sizeof(template)); + ptr = snd_info_get_str(task_name, line, sizeof(task_name)); + if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) { + snd_pcm_oss_proc_free_setup_list(pstr); + up(&pstr->oss.setup_mutex); + continue; + } + for (setup = pstr->oss.setup_list; setup; setup = setup->next) { + if (!strcmp(setup->task_name, task_name)) { + template = *setup; + break; + } + } + ptr = snd_info_get_str(str, ptr, sizeof(str)); + template.periods = simple_strtoul(str, NULL, 10); + ptr = snd_info_get_str(str, ptr, sizeof(str)); + template.period_size = simple_strtoul(str, NULL, 10); + for (idx1 = 31; idx1 >= 0; idx1--) + if (template.period_size & (1 << idx1)) + break; + for (idx1--; idx1 >= 0; idx1--) + template.period_size &= ~(1 << idx1); + do { + ptr = snd_info_get_str(str, ptr, sizeof(str)); + if (!strcmp(str, "disable")) { + template.disable = 1; + } else if (!strcmp(str, "direct")) { + template.direct = 1; + } else if (!strcmp(str, "block")) { + template.block = 1; + } else if (!strcmp(str, "non-block")) { + template.nonblock = 1; + } + } while (*str); + if (setup == NULL) { + setup = (snd_pcm_oss_setup_t *) kmalloc(sizeof(snd_pcm_oss_setup_t), GFP_KERNEL); + if (setup) { + if (pstr->oss.setup_list == NULL) { + pstr->oss.setup_list = setup; + } else { + for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next); + setup1->next = setup; + } + template.task_name = snd_kmalloc_strdup(task_name, GFP_KERNEL); + } else { + buffer->error = -ENOMEM; + } + } + if (setup) + *setup = template; + up(&pstr->oss.setup_mutex); + } +} + +static void snd_pcm_oss_proc_init(snd_pcm_t *pcm) +{ + int stream; + for (stream = 0; stream < 2; ++stream) { + snd_info_entry_t *entry; + snd_pcm_str_t *pstr = &pcm->streams[stream]; + if (pstr->substream_count == 0) + continue; + if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 8192; + entry->c.text.read = snd_pcm_oss_proc_read; + entry->c.text.write_size = 8192; + entry->c.text.write = snd_pcm_oss_proc_write; + entry->private_data = pstr; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + pstr->oss.proc_entry = entry; + } +} + +static void snd_pcm_oss_proc_done(snd_pcm_t *pcm) +{ + int stream; + for (stream = 0; stream < 2; ++stream) { + snd_pcm_str_t *pstr = &pcm->streams[stream]; + if (pstr->oss.proc_entry) { + snd_info_unregister(pstr->oss.proc_entry); + pstr->oss.proc_entry = NULL; + snd_pcm_oss_proc_free_setup_list(pstr); + } + } +} + +/* + * ENTRY functions + */ + +static struct file_operations snd_pcm_oss_f_reg = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_pcm_oss_read, + write: snd_pcm_oss_write, + open: snd_pcm_oss_open, + release: snd_pcm_oss_release, + poll: snd_pcm_oss_poll, + ioctl: snd_pcm_oss_ioctl, + mmap: snd_pcm_oss_mmap, +}; + +static snd_minor_t snd_pcm_oss_reg = +{ + comment: "digital audio", + f_ops: &snd_pcm_oss_f_reg, +}; + +static void register_oss_dsp(unsigned short native_minor, snd_pcm_t *pcm, int index) +{ + char name[128]; + sprintf(name, "dsp%i%i", pcm->card->number, pcm->device); + if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, + pcm->card, index, &snd_pcm_oss_reg, + name) < 0) { + snd_printk("unable to register OSS PCM device %i:%i\n", pcm->card->number, pcm->device); + } +} + +static int snd_pcm_oss_register_minor(unsigned short native_minor, + snd_pcm_t * pcm) +{ + pcm->oss.reg = 0; + if (snd_dsp_map[pcm->card->number] == pcm->device) { + char name[128]; + int duplex; + register_oss_dsp(native_minor, pcm, 0); + duplex = (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count > 0 && + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count && + !(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX)); + sprintf(name, "%s%s", pcm->name, duplex ? " (DUPLEX)" : ""); + snd_oss_info_register(SNDRV_OSS_INFO_DEV_AUDIO, + pcm->card->number, + name); + pcm->oss.reg++; + } + if (snd_adsp_map[pcm->card->number] == pcm->device) { + register_oss_dsp(native_minor, pcm, 1); + pcm->oss.reg++; + } + + if (pcm->oss.reg) + snd_pcm_oss_proc_init(pcm); + + return 0; +} + +static int snd_pcm_oss_unregister_minor(unsigned short native_minor, + snd_pcm_t * pcm) +{ + if (pcm->oss.reg) { + if (snd_dsp_map[pcm->card->number] == pcm->device) { + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, + pcm->card, 0); + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number); + } + if (snd_adsp_map[pcm->card->number] == pcm->device) + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, + pcm->card, 1); + pcm->oss.reg = 0; + snd_pcm_oss_proc_done(pcm); + } + return 0; +} + +static snd_pcm_notify_t snd_pcm_oss_notify = +{ + n_register: snd_pcm_oss_register_minor, + n_unregister: snd_pcm_oss_unregister_minor, +}; + +static int __init alsa_pcm_oss_init(void) +{ + int i; + int err; + + if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0) + return err; + /* check device map table */ + for (i = 0; i < SNDRV_CARDS; i++) { + if (snd_dsp_map[i] < 0 || snd_dsp_map[i] >= SNDRV_PCM_DEVICES) { + snd_printk("invalid dsp_map[%d] = %d\n", i, snd_dsp_map[i]); + snd_dsp_map[i] = 0; + } + if (snd_adsp_map[i] < 0 || snd_adsp_map[i] >= SNDRV_PCM_DEVICES) { + snd_printk("invalid adsp_map[%d] = %d\n", i, snd_adsp_map[i]); + snd_adsp_map[i] = 1; + } + } + return 0; +} + +static void __exit alsa_pcm_oss_exit(void) +{ + snd_pcm_notify(&snd_pcm_oss_notify, 1); +} + +module_init(alsa_pcm_oss_init) +module_exit(alsa_pcm_oss_exit) diff -Nru a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/pcm_plugin.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1089 @@ +/* + * PCM Plug-In shared (kernel/library) code + * Copyright (c) 1999 by Jaroslav Kysela + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if 0 +#define PLUGIN_DEBUG +#endif + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first) +#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last) + +static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask) +{ + bitset_t *vmask = plugin->src_vmask; + bitset_copy(vmask, dst_vmask, plugin->src_format.channels); + *src_vmask = vmask; + return 0; +} + +static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask) +{ + bitset_t *vmask = plugin->dst_vmask; + bitset_copy(vmask, src_vmask, plugin->dst_format.channels); + *dst_vmask = vmask; + return 0; +} + +static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) +{ + snd_pcm_plugin_format_t *format; + ssize_t width; + size_t size; + unsigned int channel; + snd_pcm_plugin_channel_t *c; + + if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) { + format = &plugin->src_format; + } else { + format = &plugin->dst_format; + } + if ((width = snd_pcm_format_physical_width(format->format)) < 0) + return width; + size = frames * format->channels * width; + snd_assert((size % 8) == 0, return -ENXIO); + size /= 8; + if (plugin->buf_frames < frames) { + if (plugin->buf) + vfree(plugin->buf); + plugin->buf = vmalloc(size); + plugin->buf_frames = frames; + } + if (!plugin->buf) + return -ENOMEM; + c = plugin->buf_channels; + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + for (channel = 0; channel < format->channels; channel++, c++) { + c->enabled = 1; + c->wanted = 0; + c->area.addr = plugin->buf; + c->area.first = channel * width; + c->area.step = format->channels * width; + } + } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { + snd_assert((size % format->channels) == 0,); + size /= format->channels; + for (channel = 0; channel < format->channels; channel++, c++) { + c->enabled = 1; + c->wanted = 0; + c->area.addr = plugin->buf + (channel * size); + c->area.first = 0; + c->area.step = width; + } + } else + return -EINVAL; + return 0; +} + +int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames) +{ + int err; + snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO); + if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); + while (plugin->next) { + if (plugin->dst_frames) + frames = plugin->dst_frames(plugin, frames); + snd_assert(frames > 0, return -ENXIO); + plugin = plugin->next; + err = snd_pcm_plugin_alloc(plugin, frames); + if (err < 0) + return err; + } + } else { + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); + while (plugin->prev) { + if (plugin->src_frames) + frames = plugin->src_frames(plugin, frames); + snd_assert(frames > 0, return -ENXIO); + plugin = plugin->prev; + err = snd_pcm_plugin_alloc(plugin, frames); + if (err < 0) + return err; + } + } + return 0; +} + + +snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels) +{ + *channels = plugin->buf_channels; + return frames; +} + +int snd_pcm_plugin_build(snd_pcm_plug_t *plug, + const char *name, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + size_t extra, + snd_pcm_plugin_t **ret) +{ + snd_pcm_plugin_t *plugin; + unsigned int channels; + + snd_assert(plug != NULL, return -ENXIO); + snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); + plugin = (snd_pcm_plugin_t *)snd_kcalloc(sizeof(*plugin) + extra, GFP_KERNEL); + if (plugin == NULL) + return -ENOMEM; + plugin->name = name; + plugin->plug = plug; + plugin->stream = snd_pcm_plug_stream(plug); + plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + plugin->src_format = *src_format; + plugin->src_width = snd_pcm_format_physical_width(src_format->format); + snd_assert(plugin->src_width > 0, ); + plugin->dst_format = *dst_format; + plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); + snd_assert(plugin->dst_width > 0, ); + if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) + channels = src_format->channels; + else + channels = dst_format->channels; + plugin->buf_channels = snd_kcalloc(channels * sizeof(*plugin->buf_channels), GFP_KERNEL); + if (plugin->buf_channels == NULL) { + snd_pcm_plugin_free(plugin); + return -ENOMEM; + } + plugin->src_vmask = bitset_alloc(src_format->channels); + if (plugin->src_vmask == NULL) { + snd_pcm_plugin_free(plugin); + return -ENOMEM; + } + plugin->dst_vmask = bitset_alloc(dst_format->channels); + if (plugin->dst_vmask == NULL) { + snd_pcm_plugin_free(plugin); + return -ENOMEM; + } + plugin->client_channels = snd_pcm_plugin_client_channels; + plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask; + plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask; + *ret = plugin; + return 0; +} + +int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin) +{ + if (! plugin) + return 0; + if (plugin->private_free) + plugin->private_free(plugin); + if (plugin->buf_channels) + kfree(plugin->buf_channels); + if (plugin->buf) + vfree(plugin->buf); + if (plugin->src_vmask) + kfree(plugin->src_vmask); + if (plugin->dst_vmask) + kfree(plugin->dst_vmask); + kfree(plugin); + return 0; +} + +snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames) +{ + snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; + int stream = snd_pcm_plug_stream(plug); + + snd_assert(plug != NULL, return -ENXIO); + if (drv_frames == 0) + return 0; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + plugin = snd_pcm_plug_last(plug); + while (plugin && drv_frames > 0) { + plugin_prev = plugin->prev; + if (plugin->src_frames) + drv_frames = plugin->src_frames(plugin, drv_frames); + plugin = plugin_prev; + } + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + plugin = snd_pcm_plug_first(plug); + while (plugin && drv_frames > 0) { + plugin_next = plugin->next; + if (plugin->dst_frames) + drv_frames = plugin->dst_frames(plugin, drv_frames); + plugin = plugin_next; + } + } else + snd_BUG(); + return drv_frames; +} + +snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames) +{ + snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; + snd_pcm_sframes_t frames; + int stream = snd_pcm_plug_stream(plug); + + snd_assert(plug != NULL, return -ENXIO); + if (clt_frames == 0) + return 0; + frames = clt_frames; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + plugin = snd_pcm_plug_first(plug); + while (plugin && frames > 0) { + plugin_next = plugin->next; + if (plugin->dst_frames) { + frames = plugin->dst_frames(plugin, frames); + if (frames < 0) + return frames; + } + plugin = plugin_next; + } + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + plugin = snd_pcm_plug_last(plug); + while (plugin) { + plugin_prev = plugin->prev; + if (plugin->src_frames) { + frames = plugin->src_frames(plugin, frames); + if (frames < 0) + return frames; + } + plugin = plugin_prev; + } + } else + snd_BUG(); + return frames; +} + +unsigned int snd_pcm_plug_formats(unsigned int formats) +{ + int linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); + formats |= SNDRV_PCM_FMTBIT_MU_LAW; + + if (formats & linfmts) + formats |= linfmts; + return formats; +} + +static int preferred_formats[] = { + SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_LE, + SNDRV_PCM_FORMAT_S24_BE, + SNDRV_PCM_FORMAT_U24_LE, + SNDRV_PCM_FORMAT_U24_BE, + SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_S8, + SNDRV_PCM_FORMAT_U8 +}; + +int snd_pcm_plug_slave_format(int format, unsigned int format_mask) +{ + if (format_mask & (1 << format)) + return format; + if ((snd_pcm_plug_formats(format_mask) & (1 << format)) == 0) + return -EINVAL; + if (snd_pcm_format_linear(format)) { + int width = snd_pcm_format_width(format); + int unsignd = snd_pcm_format_unsigned(format); + int big = snd_pcm_format_big_endian(format); + int format1; + int wid, width1=width; + int dwidth1 = 8; + for (wid = 0; wid < 4; ++wid) { + int end, big1 = big; + for (end = 0; end < 2; ++end) { + int sgn, unsignd1 = unsignd; + for (sgn = 0; sgn < 2; ++sgn) { + format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); + if (format1 >= 0 && + format_mask & (1 << format1)) + goto _found; + unsignd1 = !unsignd1; + } + big1 = !big1; + } + if (width1 == 32) { + dwidth1 = -dwidth1; + width1 = width; + } + width1 += dwidth1; + } + return -EINVAL; + _found: + return format1; + } else { + unsigned int i; + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: + for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) { + int format1 = preferred_formats[i]; + if (format_mask & (1 << format1)) + return format1; + } + default: + return -EINVAL; + } + } +} + +int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug, + snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *slave_params) +{ + snd_pcm_plugin_format_t tmpformat; + snd_pcm_plugin_format_t dstformat; + snd_pcm_plugin_format_t srcformat; + int src_access, dst_access; + snd_pcm_plugin_t *plugin = NULL; + int err, first; + int stream = snd_pcm_plug_stream(plug); + int slave_interleaved = (params_channels(slave_params) == 1 || + params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED); + + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + dstformat.format = params_format(slave_params); + dstformat.rate = params_rate(slave_params); + dstformat.channels = params_channels(slave_params); + srcformat.format = params_format(params); + srcformat.rate = params_rate(params); + srcformat.channels = params_channels(params); + src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : + SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + break; + case SNDRV_PCM_STREAM_CAPTURE: + dstformat.format = params_format(params); + dstformat.rate = params_rate(params); + dstformat.channels = params_channels(params); + srcformat.format = params_format(slave_params); + srcformat.rate = params_rate(slave_params); + srcformat.channels = params_channels(slave_params); + src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : + SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + break; + default: + snd_BUG(); + return -EINVAL; + } + tmpformat = srcformat; + + pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", + srcformat.format, + srcformat.rate, + srcformat.channels); + pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", + dstformat.format, + dstformat.rate, + dstformat.channels); + + /* Format change (linearization) */ + if ((srcformat.format != dstformat.format || + srcformat.rate != dstformat.rate || + srcformat.channels != dstformat.channels) && + !snd_pcm_format_linear(srcformat.format)) { + if (snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + else + tmpformat.format = SNDRV_PCM_FORMAT_S16; + first = plugin == NULL; + switch (srcformat.format) { + case SNDRV_PCM_FORMAT_MU_LAW: + err = snd_pcm_plugin_build_mulaw(plug, + &srcformat, &tmpformat, + &plugin); + break; + default: + return -EINVAL; + } + pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); + if (err < 0) + return err; + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* channels reduction */ + if (srcformat.channels > dstformat.channels) { + int sv = srcformat.channels; + int dv = dstformat.channels; + route_ttable_entry_t *ttable = snd_kcalloc(dv*sv*sizeof(*ttable), GFP_KERNEL); + if (ttable == NULL) + return -ENOMEM; +#if 1 + if (sv == 2 && dv == 1) { + ttable[0] = HALF; + ttable[1] = HALF; + } else +#endif + { + int v; + for (v = 0; v < dv; ++v) + ttable[v * sv + v] = FULL; + } + tmpformat.channels = dstformat.channels; + if (srcformat.rate == dstformat.rate && + snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + err = snd_pcm_plugin_build_route(plug, + &srcformat, &tmpformat, + ttable, &plugin); + kfree(ttable); + pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* rate resampling */ + if (srcformat.rate != dstformat.rate) { + tmpformat.rate = dstformat.rate; + if (srcformat.channels == dstformat.channels && + snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + err = snd_pcm_plugin_build_rate(plug, + &srcformat, &tmpformat, + &plugin); + pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* channels extension */ + if (srcformat.channels < dstformat.channels) { + int sv = srcformat.channels; + int dv = dstformat.channels; + route_ttable_entry_t *ttable = snd_kcalloc(dv * sv * sizeof(*ttable), GFP_KERNEL); + if (ttable == NULL) + return -ENOMEM; +#if 0 + { + int v; + for (v = 0; v < sv; ++v) + ttable[v * sv + v] = FULL; + } +#else + { + /* Playback is spreaded on all channels */ + int vd, vs; + for (vd = 0, vs = 0; vd < dv; ++vd) { + ttable[vd * sv + vs] = FULL; + vs++; + if (vs == sv) + vs = 0; + } + } +#endif + tmpformat.channels = dstformat.channels; + if (snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + err = snd_pcm_plugin_build_route(plug, + &srcformat, &tmpformat, + ttable, &plugin); + kfree(ttable); + pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* format change */ + if (srcformat.format != dstformat.format) { + tmpformat.format = dstformat.format; + if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) { + err = snd_pcm_plugin_build_mulaw(plug, + &srcformat, &tmpformat, + &plugin); + } + else if (snd_pcm_format_linear(srcformat.format) && + snd_pcm_format_linear(tmpformat.format)) { + err = snd_pcm_plugin_build_linear(plug, + &srcformat, &tmpformat, + &plugin); + } + else + return -EINVAL; + pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); + if (err < 0) + return err; + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* de-interleave */ + if (src_access != dst_access) { + err = snd_pcm_plugin_build_copy(plug, + &srcformat, + &tmpformat, + &plugin); + pdprintf("interleave change (copy: returns %i)\n", err); + if (err < 0) + return err; + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + } + + return 0; +} + +snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug, + char *buf, + snd_pcm_uframes_t count, + snd_pcm_plugin_channel_t **channels) +{ + snd_pcm_plugin_t *plugin; + snd_pcm_plugin_channel_t *v; + snd_pcm_plugin_format_t *format; + int width, nchannels, channel; + int stream = snd_pcm_plug_stream(plug); + + snd_assert(buf != NULL, return -ENXIO); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + plugin = snd_pcm_plug_first(plug); + format = &plugin->src_format; + } else { + plugin = snd_pcm_plug_last(plug); + format = &plugin->dst_format; + } + v = plugin->buf_channels; + *channels = v; + if ((width = snd_pcm_format_physical_width(format->format)) < 0) + return width; + nchannels = format->channels; + snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO); + for (channel = 0; channel < nchannels; channel++, v++) { + v->enabled = 1; + v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE); + v->area.addr = buf; + v->area.first = channel * width; + v->area.step = nchannels * width; + } + return count; +} + +int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug, + bitset_t *client_vmask) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); + if (plugin == NULL) { + return 0; + } else { + int schannels = plugin->dst_format.channels; + bitset_t bs[bitset_size(schannels)]; + bitset_t *srcmask; + bitset_t *dstmask = bs; + int err; + bitset_one(dstmask, schannels); + if (plugin == NULL) { + bitset_and(client_vmask, dstmask, schannels); + return 0; + } + while (1) { + err = plugin->src_channels_mask(plugin, dstmask, &srcmask); + if (err < 0) + return err; + dstmask = srcmask; + if (plugin->prev == NULL) + break; + plugin = plugin->prev; + } + bitset_and(client_vmask, dstmask, plugin->src_format.channels); + return 0; + } +} + +int snd_pcm_plug_capture_channels_mask(snd_pcm_plug_t *plug, + bitset_t *client_vmask) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); + if (plugin == NULL) { + return 0; + } else { + int schannels = plugin->src_format.channels; + bitset_t bs[bitset_size(schannels)]; + bitset_t *srcmask = bs; + bitset_t *dstmask; + int err; + bitset_one(srcmask, schannels); + while (1) { + err = plugin->dst_channels_mask(plugin, srcmask, &dstmask); + if (err < 0) + return err; + srcmask = dstmask; + if (plugin->next == NULL) + break; + plugin = plugin->next; + } + bitset_and(client_vmask, srcmask, plugin->dst_format.channels); + return 0; + } +} + +static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug, + snd_pcm_plugin_channel_t *src_channels) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); + unsigned int nchannels = plugin->src_format.channels; + bitset_t bs[bitset_size(nchannels)]; + bitset_t *srcmask = bs; + int err; + unsigned int channel; + for (channel = 0; channel < nchannels; channel++) { + if (src_channels[channel].enabled) + bitset_set(srcmask, channel); + else + bitset_reset(srcmask, channel); + } + err = snd_pcm_plug_playback_channels_mask(plug, srcmask); + if (err < 0) + return err; + for (channel = 0; channel < nchannels; channel++) { + if (!bitset_get(srcmask, channel)) + src_channels[channel].enabled = 0; + } + return 0; +} + +static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug, + snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *client_channels) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); + unsigned int nchannels = plugin->dst_format.channels; + bitset_t bs[bitset_size(nchannels)]; + bitset_t *dstmask = bs; + bitset_t *srcmask; + int err; + unsigned int channel; + for (channel = 0; channel < nchannels; channel++) { + if (client_channels[channel].enabled) + bitset_set(dstmask, channel); + else + bitset_reset(dstmask, channel); + } + while (plugin) { + err = plugin->src_channels_mask(plugin, dstmask, &srcmask); + if (err < 0) + return err; + dstmask = srcmask; + plugin = plugin->prev; + } + plugin = snd_pcm_plug_first(plug); + nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; channel++) { + if (!bitset_get(dstmask, channel)) + src_channels[channel].enabled = 0; + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size) +{ + snd_pcm_plugin_t *plugin, *next; + snd_pcm_plugin_channel_t *dst_channels; + int err; + snd_pcm_sframes_t frames = size; + + if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0) + return err; + + plugin = snd_pcm_plug_first(plug); + while (plugin && frames > 0) { + if ((next = plugin->next) != NULL) { + snd_pcm_sframes_t frames1 = frames; + if (plugin->dst_frames) + frames1 = plugin->dst_frames(plugin, frames); + if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { + return err; + } + if (err != frames1) { + frames = err; + if (plugin->src_frames) + frames = plugin->src_frames(plugin, frames1); + } + } else + dst_channels = 0; + pdprintf("write plugin: %s, %li\n", plugin->name, frames); + if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) + return frames; + src_channels = dst_channels; + plugin = next; + } + return snd_pcm_plug_client_size(plug, frames); +} + +snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size) +{ + snd_pcm_plugin_t *plugin, *next; + snd_pcm_plugin_channel_t *src_channels, *dst_channels; + snd_pcm_sframes_t frames = size; + int err; + + frames = snd_pcm_plug_slave_size(plug, frames); + if (frames < 0) + return frames; + + src_channels = 0; + plugin = snd_pcm_plug_first(plug); + while (plugin && frames > 0) { + if ((next = plugin->next) != NULL) { + if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) { + return err; + } + frames = err; + if (!plugin->prev) { + if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final) < 0)) + return err; + } + } else { + dst_channels = dst_channels_final; + } + pdprintf("read plugin: %s, %li\n", plugin->name, frames); + if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) + return frames; + plugin = next; + src_channels = dst_channels; + } + return frames; +} + +int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset, + size_t samples, int format) +{ + /* FIXME: sub byte resolution and odd dst_offset */ + char *dst; + unsigned int dst_step; + int width; + u_int64_t silence; + if (!dst_area->addr) + return 0; + dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; + width = snd_pcm_format_physical_width(format); + silence = snd_pcm_format_silence_64(format); + if (dst_area->step == (unsigned int) width) { + size_t dwords = samples * width / 64; + samples -= dwords * 64 / width; + while (dwords-- > 0) + *((u_int64_t*)dst)++ = silence; + if (samples == 0) + return 0; + } + dst_step = dst_area->step / 8; + switch (width) { + case 4: { + u_int8_t s0 = silence & 0xf0; + u_int8_t s1 = silence & 0x0f; + int dstbit = dst_area->first % 8; + int dstbit_step = dst_area->step % 8; + while (samples-- > 0) { + if (dstbit) { + *dst &= 0xf0; + *dst |= s1; + } else { + *dst &= 0x0f; + *dst |= s0; + } + dst += dst_step; + dstbit += dstbit_step; + if (dstbit == 8) { + dst++; + dstbit = 0; + } + } + break; + } + case 8: { + u_int8_t sil = silence; + while (samples-- > 0) { + *dst = sil; + dst += dst_step; + } + break; + } + case 16: { + u_int16_t sil = silence; + while (samples-- > 0) { + *(u_int16_t*)dst = sil; + dst += dst_step; + } + break; + } + case 32: { + u_int32_t sil = silence; + while (samples-- > 0) { + *(u_int32_t*)dst = sil; + dst += dst_step; + } + break; + } + case 64: { + while (samples-- > 0) { + *(u_int64_t*)dst = silence; + dst += dst_step; + } + break; + } + default: + snd_BUG(); + } + return 0; +} + +int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format) +{ + int width = snd_pcm_format_physical_width(format); + while (channels > 0) { + void *addr = dst_areas->addr; + unsigned int step = dst_areas->step; + const snd_pcm_channel_area_t *begin = dst_areas; + int vc = channels; + unsigned int v = 0; + int err; + while (1) { + vc--; + v++; + dst_areas++; + if (vc == 0 || + dst_areas->addr != addr || + dst_areas->step != step || + dst_areas->first != dst_areas[-1].first + width) + break; + } + if (v > 1 && v * width == step) { + /* Collapse the areas */ + snd_pcm_channel_area_t d; + d.addr = begin->addr; + d.first = begin->first; + d.step = width; + err = snd_pcm_area_silence(&d, dst_offset * v, frames * v, format); + channels -= v; + } else { + err = snd_pcm_area_silence(begin, dst_offset, frames, format); + dst_areas = begin + 1; + channels--; + } + if (err < 0) + return err; + } + return 0; +} + + +int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset, + const snd_pcm_channel_area_t *dst_area, size_t dst_offset, + size_t samples, int format) +{ + /* FIXME: sub byte resolution and odd dst_offset */ + char *src, *dst; + int width; + int src_step, dst_step; + src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8; + if (!src_area->addr) + return snd_pcm_area_silence(dst_area, dst_offset, samples, format); + dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; + if (!dst_area->addr) + return 0; + width = snd_pcm_format_physical_width(format); + if (src_area->step == (unsigned int) width && + dst_area->step == (unsigned int) width) { + size_t bytes = samples * width / 8; + samples -= bytes * 8 / width; + memcpy(dst, src, bytes); + if (samples == 0) + return 0; + } + src_step = src_area->step / 8; + dst_step = dst_area->step / 8; + switch (width) { + case 4: { + int srcbit = src_area->first % 8; + int srcbit_step = src_area->step % 8; + int dstbit = dst_area->first % 8; + int dstbit_step = dst_area->step % 8; + while (samples-- > 0) { + unsigned char srcval; + if (srcbit) + srcval = *src & 0x0f; + else + srcval = *src & 0xf0; + if (dstbit) + *dst &= 0xf0; + else + *dst &= 0x0f; + *dst |= srcval; + src += src_step; + srcbit += srcbit_step; + if (srcbit == 8) { + src++; + srcbit = 0; + } + dst += dst_step; + dstbit += dstbit_step; + if (dstbit == 8) { + dst++; + dstbit = 0; + } + } + break; + } + case 8: { + while (samples-- > 0) { + *dst = *src; + src += src_step; + dst += dst_step; + } + break; + } + case 16: { + while (samples-- > 0) { + *(u_int16_t*)dst = *(u_int16_t*)src; + src += src_step; + dst += dst_step; + } + break; + } + case 32: { + while (samples-- > 0) { + *(u_int32_t*)dst = *(u_int32_t*)src; + src += src_step; + dst += dst_step; + } + break; + } + case 64: { + while (samples-- > 0) { + *(u_int64_t*)dst = *(u_int64_t*)src; + src += src_step; + dst += dst_step; + } + break; + } + default: + snd_BUG(); + } + return 0; +} + +int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset, + const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format) +{ + int width = snd_pcm_format_physical_width(format); + while (channels > 0) { + unsigned int step = src_areas->step; + void *src_addr = src_areas->addr; + const snd_pcm_channel_area_t *src_start = src_areas; + void *dst_addr = dst_areas->addr; + const snd_pcm_channel_area_t *dst_start = dst_areas; + int vc = channels; + unsigned int v = 0; + while (dst_areas->step == step) { + vc--; + v++; + src_areas++; + dst_areas++; + if (vc == 0 || + src_areas->step != step || + src_areas->addr != src_addr || + dst_areas->addr != dst_addr || + src_areas->first != src_areas[-1].first + width || + dst_areas->first != dst_areas[-1].first + width) + break; + } + if (v > 1 && v * width == step) { + /* Collapse the areas */ + snd_pcm_channel_area_t s, d; + s.addr = src_start->addr; + s.first = src_start->first; + s.step = width; + d.addr = dst_start->addr; + d.first = dst_start->first; + d.step = width; + snd_pcm_area_copy(&s, src_offset * v, &d, dst_offset * v, frames * v, format); + channels -= v; + } else { + snd_pcm_area_copy(src_start, src_offset, dst_start, dst_offset, frames, format); + src_areas = src_start + 1; + dst_areas = dst_start + 1; + channels--; + } + } + return 0; +} diff -Nru a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/pcm_plugin.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,257 @@ +#ifndef __PCM_PLUGIN_H +#define __PCM_PLUGIN_H + +/* + * Digital Audio (Plugin interface) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif + +typedef unsigned int bitset_t; + +static inline size_t bitset_size(int nbits) +{ + return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8); +} + +static inline bitset_t *bitset_alloc(int nbits) +{ + return snd_kcalloc(bitset_size(nbits) * sizeof(bitset_t), GFP_KERNEL); +} + +static inline void bitset_set(bitset_t *bitmap, unsigned int pos) +{ + size_t bits = sizeof(*bitmap) * 8; + bitmap[pos / bits] |= 1 << (pos % bits); +} + +static inline void bitset_reset(bitset_t *bitmap, unsigned int pos) +{ + size_t bits = sizeof(*bitmap) * 8; + bitmap[pos / bits] &= ~(1 << (pos % bits)); +} + +static inline int bitset_get(bitset_t *bitmap, unsigned int pos) +{ + size_t bits = sizeof(*bitmap) * 8; + return !!(bitmap[pos / bits] & (1 << (pos % bits))); +} + +static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits) +{ + memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t)); +} + +static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ &= *bs++; +} + +static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ |= *bs++; +} + +static inline void bitset_zero(bitset_t *dst, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ = 0; +} + +static inline void bitset_one(bitset_t *dst, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ = ~(bitset_t)0; +} + +#define snd_pcm_plug_t snd_pcm_substream_t +#define snd_pcm_plug_stream(plug) ((plug)->stream) + +typedef enum { + INIT = 0, + PREPARE = 1, +} snd_pcm_plugin_action_t; + +typedef struct _snd_pcm_channel_area { + void *addr; /* base address of channel samples */ + unsigned int first; /* offset to first sample in bits */ + unsigned int step; /* samples distance in bits */ +} snd_pcm_channel_area_t; + +typedef struct _snd_pcm_plugin_channel { + void *aptr; /* pointer to the allocated area */ + snd_pcm_channel_area_t area; + unsigned int enabled:1; /* channel need to be processed */ + unsigned int wanted:1; /* channel is wanted */ +} snd_pcm_plugin_channel_t; + +typedef struct _snd_pcm_plugin_format { + int format; + unsigned int rate; + unsigned int channels; +} snd_pcm_plugin_format_t; + +struct _snd_pcm_plugin { + const char *name; /* plug-in name */ + int stream; + snd_pcm_plugin_format_t src_format; /* source format */ + snd_pcm_plugin_format_t dst_format; /* destination format */ + int src_width; /* sample width in bits */ + int dst_width; /* sample width in bits */ + int access; + snd_pcm_sframes_t (*src_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t dst_frames); + snd_pcm_sframes_t (*dst_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t src_frames); + snd_pcm_sframes_t (*client_channels)(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels); + int (*src_channels_mask)(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask); + int (*dst_channels_mask)(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask); + snd_pcm_sframes_t (*transfer)(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames); + int (*action)(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_action_t action, + unsigned long data); + snd_pcm_plugin_t *prev; + snd_pcm_plugin_t *next; + snd_pcm_plug_t *plug; + void *private_data; + void (*private_free)(snd_pcm_plugin_t *plugin); + char *buf; + snd_pcm_uframes_t buf_frames; + snd_pcm_plugin_channel_t *buf_channels; + bitset_t *src_vmask; + bitset_t *dst_vmask; + char extra_data[0]; +}; + +int snd_pcm_plugin_build(snd_pcm_plug_t *handle, + const char *name, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + size_t extra, + snd_pcm_plugin_t **ret); +int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin); +int snd_pcm_plugin_clear(snd_pcm_plugin_t **first); +int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t drv_size); +snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t clt_size); + +#define ROUTE_PLUGIN_USE_FLOAT 0 +#define FULL ROUTE_PLUGIN_RESOLUTION +#define HALF ROUTE_PLUGIN_RESOLUTION / 2 +typedef int route_ttable_entry_t; + +int snd_pcm_plugin_build_io(snd_pcm_plug_t *handle, + snd_pcm_hw_params_t *params, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_linear(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_rate(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_route(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + route_ttable_entry_t *ttable, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_copy(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); + +unsigned int snd_pcm_plug_formats(unsigned int formats); + +int snd_pcm_plug_format_plugins(snd_pcm_plug_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *slave_params); + +int snd_pcm_plug_slave_format(int format, unsigned int format_mask); + +int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin); + +snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size); +snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size); + +snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *handle, + char *buf, snd_pcm_uframes_t count, + snd_pcm_plugin_channel_t **channels); + +snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels); + +int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset, + size_t samples, int format); +int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format); +int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset, + const snd_pcm_channel_area_t *dst_channel, size_t dst_offset, + size_t samples, int format); +int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_channels, snd_pcm_uframes_t src_offset, + const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format); + +void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t size); +void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *plug, void *ptr); +snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel); +snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel); +snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel); +snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel); + + + +#define ROUTE_PLUGIN_RESOLUTION 16 + +int getput_index(int format); +int copy_index(int format); +int conv_index(int src_format, int dst_format); + +void zero_channel(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *dst_channel, + size_t samples); + +#ifdef PLUGIN_DEBUG +#define pdprintf( args... ) printk( "plugin: " ##args) +#else +#define pdprintf( args... ) { ; } +#endif + +#endif /* __PCM_PLUGIN_H */ diff -Nru a/sound/core/oss/plugin_ops.h b/sound/core/oss/plugin_ops.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/plugin_ops.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,536 @@ +/* + * Plugin sample operators with fast switch + * Copyright (c) 2000 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#define as_u8(ptr) (*(u_int8_t*)(ptr)) +#define as_u16(ptr) (*(u_int16_t*)(ptr)) +#define as_u32(ptr) (*(u_int32_t*)(ptr)) +#define as_u64(ptr) (*(u_int64_t*)(ptr)) +#define as_s8(ptr) (*(int8_t*)(ptr)) +#define as_s16(ptr) (*(int16_t*)(ptr)) +#define as_s32(ptr) (*(int32_t*)(ptr)) +#define as_s64(ptr) (*(int64_t*)(ptr)) + +#ifdef COPY_LABELS +static void *copy_labels[4] = { + &©_8, + &©_16, + &©_32, + &©_64 +}; +#endif + +#ifdef COPY_END +while(0) { +copy_8: as_s8(dst) = as_s8(src); goto COPY_END; +copy_16: as_s16(dst) = as_s16(src); goto COPY_END; +copy_32: as_s32(dst) = as_s32(src); goto COPY_END; +copy_64: as_s64(dst) = as_s64(src); goto COPY_END; +} +#endif + +#ifdef CONV_LABELS +/* src_wid src_endswap sign_toggle dst_wid dst_endswap */ +static void *conv_labels[4 * 2 * 2 * 4 * 2] = { + &&conv_xxx1_xxx1, /* 8h -> 8h */ + &&conv_xxx1_xxx1, /* 8h -> 8s */ + &&conv_xxx1_xx10, /* 8h -> 16h */ + &&conv_xxx1_xx01, /* 8h -> 16s */ + &&conv_xxx1_x100, /* 8h -> 24h */ + &&conv_xxx1_001x, /* 8h -> 24s */ + &&conv_xxx1_1000, /* 8h -> 32h */ + &&conv_xxx1_0001, /* 8h -> 32s */ + &&conv_xxx1_xxx9, /* 8h ^> 8h */ + &&conv_xxx1_xxx9, /* 8h ^> 8s */ + &&conv_xxx1_xx90, /* 8h ^> 16h */ + &&conv_xxx1_xx09, /* 8h ^> 16s */ + &&conv_xxx1_x900, /* 8h ^> 24h */ + &&conv_xxx1_009x, /* 8h ^> 24s */ + &&conv_xxx1_9000, /* 8h ^> 32h */ + &&conv_xxx1_0009, /* 8h ^> 32s */ + &&conv_xxx1_xxx1, /* 8s -> 8h */ + &&conv_xxx1_xxx1, /* 8s -> 8s */ + &&conv_xxx1_xx10, /* 8s -> 16h */ + &&conv_xxx1_xx01, /* 8s -> 16s */ + &&conv_xxx1_x100, /* 8s -> 24h */ + &&conv_xxx1_001x, /* 8s -> 24s */ + &&conv_xxx1_1000, /* 8s -> 32h */ + &&conv_xxx1_0001, /* 8s -> 32s */ + &&conv_xxx1_xxx9, /* 8s ^> 8h */ + &&conv_xxx1_xxx9, /* 8s ^> 8s */ + &&conv_xxx1_xx90, /* 8s ^> 16h */ + &&conv_xxx1_xx09, /* 8s ^> 16s */ + &&conv_xxx1_x900, /* 8s ^> 24h */ + &&conv_xxx1_009x, /* 8s ^> 24s */ + &&conv_xxx1_9000, /* 8s ^> 32h */ + &&conv_xxx1_0009, /* 8s ^> 32s */ + &&conv_xx12_xxx1, /* 16h -> 8h */ + &&conv_xx12_xxx1, /* 16h -> 8s */ + &&conv_xx12_xx12, /* 16h -> 16h */ + &&conv_xx12_xx21, /* 16h -> 16s */ + &&conv_xx12_x120, /* 16h -> 24h */ + &&conv_xx12_021x, /* 16h -> 24s */ + &&conv_xx12_1200, /* 16h -> 32h */ + &&conv_xx12_0021, /* 16h -> 32s */ + &&conv_xx12_xxx9, /* 16h ^> 8h */ + &&conv_xx12_xxx9, /* 16h ^> 8s */ + &&conv_xx12_xx92, /* 16h ^> 16h */ + &&conv_xx12_xx29, /* 16h ^> 16s */ + &&conv_xx12_x920, /* 16h ^> 24h */ + &&conv_xx12_029x, /* 16h ^> 24s */ + &&conv_xx12_9200, /* 16h ^> 32h */ + &&conv_xx12_0029, /* 16h ^> 32s */ + &&conv_xx12_xxx2, /* 16s -> 8h */ + &&conv_xx12_xxx2, /* 16s -> 8s */ + &&conv_xx12_xx21, /* 16s -> 16h */ + &&conv_xx12_xx12, /* 16s -> 16s */ + &&conv_xx12_x210, /* 16s -> 24h */ + &&conv_xx12_012x, /* 16s -> 24s */ + &&conv_xx12_2100, /* 16s -> 32h */ + &&conv_xx12_0012, /* 16s -> 32s */ + &&conv_xx12_xxxA, /* 16s ^> 8h */ + &&conv_xx12_xxxA, /* 16s ^> 8s */ + &&conv_xx12_xxA1, /* 16s ^> 16h */ + &&conv_xx12_xx1A, /* 16s ^> 16s */ + &&conv_xx12_xA10, /* 16s ^> 24h */ + &&conv_xx12_01Ax, /* 16s ^> 24s */ + &&conv_xx12_A100, /* 16s ^> 32h */ + &&conv_xx12_001A, /* 16s ^> 32s */ + &&conv_x123_xxx1, /* 24h -> 8h */ + &&conv_x123_xxx1, /* 24h -> 8s */ + &&conv_x123_xx12, /* 24h -> 16h */ + &&conv_x123_xx21, /* 24h -> 16s */ + &&conv_x123_x123, /* 24h -> 24h */ + &&conv_x123_321x, /* 24h -> 24s */ + &&conv_x123_1230, /* 24h -> 32h */ + &&conv_x123_0321, /* 24h -> 32s */ + &&conv_x123_xxx9, /* 24h ^> 8h */ + &&conv_x123_xxx9, /* 24h ^> 8s */ + &&conv_x123_xx92, /* 24h ^> 16h */ + &&conv_x123_xx29, /* 24h ^> 16s */ + &&conv_x123_x923, /* 24h ^> 24h */ + &&conv_x123_329x, /* 24h ^> 24s */ + &&conv_x123_9230, /* 24h ^> 32h */ + &&conv_x123_0329, /* 24h ^> 32s */ + &&conv_123x_xxx3, /* 24s -> 8h */ + &&conv_123x_xxx3, /* 24s -> 8s */ + &&conv_123x_xx32, /* 24s -> 16h */ + &&conv_123x_xx23, /* 24s -> 16s */ + &&conv_123x_x321, /* 24s -> 24h */ + &&conv_123x_123x, /* 24s -> 24s */ + &&conv_123x_3210, /* 24s -> 32h */ + &&conv_123x_0123, /* 24s -> 32s */ + &&conv_123x_xxxB, /* 24s ^> 8h */ + &&conv_123x_xxxB, /* 24s ^> 8s */ + &&conv_123x_xxB2, /* 24s ^> 16h */ + &&conv_123x_xx2B, /* 24s ^> 16s */ + &&conv_123x_xB21, /* 24s ^> 24h */ + &&conv_123x_12Bx, /* 24s ^> 24s */ + &&conv_123x_B210, /* 24s ^> 32h */ + &&conv_123x_012B, /* 24s ^> 32s */ + &&conv_1234_xxx1, /* 32h -> 8h */ + &&conv_1234_xxx1, /* 32h -> 8s */ + &&conv_1234_xx12, /* 32h -> 16h */ + &&conv_1234_xx21, /* 32h -> 16s */ + &&conv_1234_x123, /* 32h -> 24h */ + &&conv_1234_321x, /* 32h -> 24s */ + &&conv_1234_1234, /* 32h -> 32h */ + &&conv_1234_4321, /* 32h -> 32s */ + &&conv_1234_xxx9, /* 32h ^> 8h */ + &&conv_1234_xxx9, /* 32h ^> 8s */ + &&conv_1234_xx92, /* 32h ^> 16h */ + &&conv_1234_xx29, /* 32h ^> 16s */ + &&conv_1234_x923, /* 32h ^> 24h */ + &&conv_1234_329x, /* 32h ^> 24s */ + &&conv_1234_9234, /* 32h ^> 32h */ + &&conv_1234_4329, /* 32h ^> 32s */ + &&conv_1234_xxx4, /* 32s -> 8h */ + &&conv_1234_xxx4, /* 32s -> 8s */ + &&conv_1234_xx43, /* 32s -> 16h */ + &&conv_1234_xx34, /* 32s -> 16s */ + &&conv_1234_x432, /* 32s -> 24h */ + &&conv_1234_234x, /* 32s -> 24s */ + &&conv_1234_4321, /* 32s -> 32h */ + &&conv_1234_1234, /* 32s -> 32s */ + &&conv_1234_xxxC, /* 32s ^> 8h */ + &&conv_1234_xxxC, /* 32s ^> 8s */ + &&conv_1234_xxC3, /* 32s ^> 16h */ + &&conv_1234_xx3C, /* 32s ^> 16s */ + &&conv_1234_xC32, /* 32s ^> 24h */ + &&conv_1234_23Cx, /* 32s ^> 24s */ + &&conv_1234_C321, /* 32s ^> 32h */ + &&conv_1234_123C, /* 32s ^> 32s */ +}; +#endif + +#ifdef CONV_END +while(0) { +conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END; +conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END; +conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END; +conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END; +conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END; +conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END; +conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END; +conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END; +conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; +conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END; +conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END; +conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; +conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END; +conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END; +conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END; +conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END; +conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END; +conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; +conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END; +conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END; +conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END; +conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END; +conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END; +conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END; +conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END; +conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END; +conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END; +conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END; +conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END; +conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END; +conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; +conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END; +conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END; +conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END; +conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END; +conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END; +conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END; +conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END; +conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END; +conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END; +conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END; +conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END; +conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END; +conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END; +conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; +conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END; +conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END; +conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END; +conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END; +conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END; +conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END; +conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END; +conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END; +conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END; +conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END; +conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END; +conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END; +conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; +conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END; +conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END; +conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; +conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END; +conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END; +conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END; +conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END; +conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END; +conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END; +conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END; +conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END; +conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END; +conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END; +conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; +conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END; +conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END; +conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; +conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END; +conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END; +conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END; +conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END; +conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END; +conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END; +conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END; +conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END; +conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END; +conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END; +conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END; +conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END; +conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END; +conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END; +conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END; +conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END; +conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END; +conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END; +conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END; +} +#endif + +#ifdef GET_S16_LABELS +/* src_wid src_endswap unsigned */ +static void *get_s16_labels[4 * 2 * 2] = { + &&get_s16_xxx1_xx10, /* 8h -> 16h */ + &&get_s16_xxx1_xx90, /* 8h ^> 16h */ + &&get_s16_xxx1_xx10, /* 8s -> 16h */ + &&get_s16_xxx1_xx90, /* 8s ^> 16h */ + &&get_s16_xx12_xx12, /* 16h -> 16h */ + &&get_s16_xx12_xx92, /* 16h ^> 16h */ + &&get_s16_xx12_xx21, /* 16s -> 16h */ + &&get_s16_xx12_xxA1, /* 16s ^> 16h */ + &&get_s16_x123_xx12, /* 24h -> 16h */ + &&get_s16_x123_xx92, /* 24h ^> 16h */ + &&get_s16_123x_xx32, /* 24s -> 16h */ + &&get_s16_123x_xxB2, /* 24s ^> 16h */ + &&get_s16_1234_xx12, /* 32h -> 16h */ + &&get_s16_1234_xx92, /* 32h ^> 16h */ + &&get_s16_1234_xx43, /* 32s -> 16h */ + &&get_s16_1234_xxC3, /* 32s ^> 16h */ +}; +#endif + +#ifdef GET_S16_END +while(0) { +get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END; +get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END; +get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END; +get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END; +get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END; +get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END; +get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END; +get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END; +get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END; +get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END; +get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END; +get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END; +get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END; +get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END; +} +#endif + +#ifdef PUT_S16_LABELS +/* dst_wid dst_endswap unsigned */ +static void *put_s16_labels[4 * 2 * 2 * 4 * 2] = { + &&put_s16_xx12_xxx1, /* 16h -> 8h */ + &&put_s16_xx12_xxx9, /* 16h ^> 8h */ + &&put_s16_xx12_xxx1, /* 16h -> 8s */ + &&put_s16_xx12_xxx9, /* 16h ^> 8s */ + &&put_s16_xx12_xx12, /* 16h -> 16h */ + &&put_s16_xx12_xx92, /* 16h ^> 16h */ + &&put_s16_xx12_xx21, /* 16h -> 16s */ + &&put_s16_xx12_xx29, /* 16h ^> 16s */ + &&put_s16_xx12_x120, /* 16h -> 24h */ + &&put_s16_xx12_x920, /* 16h ^> 24h */ + &&put_s16_xx12_021x, /* 16h -> 24s */ + &&put_s16_xx12_029x, /* 16h ^> 24s */ + &&put_s16_xx12_1200, /* 16h -> 32h */ + &&put_s16_xx12_9200, /* 16h ^> 32h */ + &&put_s16_xx12_0021, /* 16h -> 32s */ + &&put_s16_xx12_0029, /* 16h ^> 32s */ +}; +#endif + +#ifdef PUT_S16_END +while (0) { +put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END; +put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END; +put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END; +put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END; +put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END; +put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END; +put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END; +put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END; +put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END; +put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END; +put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END; +put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END; +put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END; +put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END; +} +#endif + +#if 0 +#ifdef GET32_LABELS +/* src_wid src_endswap unsigned */ +static void *get32_labels[4 * 2 * 2] = { + &&get32_xxx1_1000, /* 8h -> 32h */ + &&get32_xxx1_9000, /* 8h ^> 32h */ + &&get32_xxx1_1000, /* 8s -> 32h */ + &&get32_xxx1_9000, /* 8s ^> 32h */ + &&get32_xx12_1200, /* 16h -> 32h */ + &&get32_xx12_9200, /* 16h ^> 32h */ + &&get32_xx12_2100, /* 16s -> 32h */ + &&get32_xx12_A100, /* 16s ^> 32h */ + &&get32_x123_1230, /* 24h -> 32h */ + &&get32_x123_9230, /* 24h ^> 32h */ + &&get32_123x_3210, /* 24s -> 32h */ + &&get32_123x_B210, /* 24s ^> 32h */ + &&get32_1234_1234, /* 32h -> 32h */ + &&get32_1234_9234, /* 32h ^> 32h */ + &&get32_1234_4321, /* 32s -> 32h */ + &&get32_1234_C321, /* 32s ^> 32h */ +}; +#endif + +#ifdef GET32_END +while (0) { +get32_xxx1_1000: sample = (u_int32_t)as_u8(src) << 24; goto GET32_END; +get32_xxx1_9000: sample = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto GET32_END; +get32_xx12_1200: sample = (u_int32_t)as_u16(src) << 16; goto GET32_END; +get32_xx12_9200: sample = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto GET32_END; +get32_xx12_2100: sample = (u_int32_t)swab16(as_u16(src)) << 16; goto GET32_END; +get32_xx12_A100: sample = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto GET32_END; +get32_x123_1230: sample = as_u32(src) << 8; goto GET32_END; +get32_x123_9230: sample = (as_u32(src) << 8) ^ 0x80000000; goto GET32_END; +get32_123x_3210: sample = swab32(as_u32(src) >> 8); goto GET32_END; +get32_123x_B210: sample = swab32((as_u32(src) >> 8) ^ 0x80); goto GET32_END; +get32_1234_1234: sample = as_u32(src); goto GET32_END; +get32_1234_9234: sample = as_u32(src) ^ 0x80000000; goto GET32_END; +get32_1234_4321: sample = swab32(as_u32(src)); goto GET32_END; +get32_1234_C321: sample = swab32(as_u32(src) ^ 0x80); goto GET32_END; +} +#endif +#endif + +#ifdef PUT_U32_LABELS +/* dst_wid dst_endswap unsigned */ +static void *put_u32_labels[4 * 2 * 2] = { + &&put_u32_1234_xxx9, /* u32h -> s8h */ + &&put_u32_1234_xxx1, /* u32h -> u8h */ + &&put_u32_1234_xxx9, /* u32h -> s8s */ + &&put_u32_1234_xxx1, /* u32h -> u8s */ + &&put_u32_1234_xx92, /* u32h -> s16h */ + &&put_u32_1234_xx12, /* u32h -> u16h */ + &&put_u32_1234_xx29, /* u32h -> s16s */ + &&put_u32_1234_xx21, /* u32h -> u16s */ + &&put_u32_1234_x923, /* u32h -> s24h */ + &&put_u32_1234_x123, /* u32h -> u24h */ + &&put_u32_1234_329x, /* u32h -> s24s */ + &&put_u32_1234_321x, /* u32h -> u24s */ + &&put_u32_1234_9234, /* u32h -> s32h */ + &&put_u32_1234_1234, /* u32h -> u32h */ + &&put_u32_1234_4329, /* u32h -> s32s */ + &&put_u32_1234_4321, /* u32h -> u32s */ +}; +#endif + +#ifdef PUT_U32_END +while (0) { +put_u32_1234_xxx1: as_u8(dst) = sample >> 24; goto PUT_U32_END; +put_u32_1234_xxx9: as_u8(dst) = (sample >> 24) ^ 0x80; goto PUT_U32_END; +put_u32_1234_xx12: as_u16(dst) = sample >> 16; goto PUT_U32_END; +put_u32_1234_xx92: as_u16(dst) = (sample >> 16) ^ 0x8000; goto PUT_U32_END; +put_u32_1234_xx21: as_u16(dst) = swab16(sample >> 16); goto PUT_U32_END; +put_u32_1234_xx29: as_u16(dst) = swab16(sample >> 16) ^ 0x80; goto PUT_U32_END; +put_u32_1234_x123: as_u32(dst) = sample >> 8; goto PUT_U32_END; +put_u32_1234_x923: as_u32(dst) = (sample >> 8) ^ 0x800000; goto PUT_U32_END; +put_u32_1234_321x: as_u32(dst) = swab32(sample) << 8; goto PUT_U32_END; +put_u32_1234_329x: as_u32(dst) = (swab32(sample) ^ 0x80) << 8; goto PUT_U32_END; +put_u32_1234_1234: as_u32(dst) = sample; goto PUT_U32_END; +put_u32_1234_9234: as_u32(dst) = sample ^ 0x80000000; goto PUT_U32_END; +put_u32_1234_4321: as_u32(dst) = swab32(sample); goto PUT_U32_END; +put_u32_1234_4329: as_u32(dst) = swab32(sample) ^ 0x80; goto PUT_U32_END; +} +#endif + +#ifdef GET_U_LABELS +/* width endswap unsigned*/ +static void *get_u_labels[4 * 2 * 2] = { + &&get_u_s8, /* s8 -> u8 */ + &&get_u_u8, /* u8 -> u8 */ + &&get_u_s8, /* s8 -> u8 */ + &&get_u_u8, /* u8 -> u8 */ + &&get_u_s16h, /* s16h -> u16h */ + &&get_u_u16h, /* u16h -> u16h */ + &&get_u_s16s, /* s16s -> u16h */ + &&get_u_u16s, /* u16s -> u16h */ + &&get_u_s24h, /* s24h -> u32h */ + &&get_u_u24h, /* u24h -> u32h */ + &&get_u_s24s, /* s24s -> u32h */ + &&get_u_u24s, /* u24s -> u32h */ + &&get_u_s32h, /* s32h -> u32h */ + &&get_u_u32h, /* u32h -> u32h */ + &&get_u_s32s, /* s32s -> u32h */ + &&get_u_u32s, /* u32s -> u32h */ +}; +#endif + +#ifdef GET_U_END +while (0) { +get_u_s8: sample = as_u8(src) ^ 0x80; goto GET_U_END; +get_u_u8: sample = as_u8(src); goto GET_U_END; +get_u_s16h: sample = as_u16(src) ^ 0x8000; goto GET_U_END; +get_u_u16h: sample = as_u16(src); goto GET_U_END; +get_u_s16s: sample = swab16(as_u16(src) ^ 0x80); goto GET_U_END; +get_u_u16s: sample = swab16(as_u16(src)); goto GET_U_END; +get_u_s24h: sample = (as_u32(src) ^ 0x800000); goto GET_U_END; +get_u_u24h: sample = as_u32(src); goto GET_U_END; +get_u_s24s: sample = swab32(as_u32(src) ^ 0x800000); goto GET_U_END; +get_u_u24s: sample = swab32(as_u32(src)); goto GET_U_END; +get_u_s32h: sample = as_u32(src) ^ 0x80000000; goto GET_U_END; +get_u_u32h: sample = as_u32(src); goto GET_U_END; +get_u_s32s: sample = swab32(as_u32(src) ^ 0x80); goto GET_U_END; +get_u_u32s: sample = swab32(as_u32(src)); goto GET_U_END; +} +#endif + +#if 0 +#ifdef PUT_LABELS +/* width endswap unsigned */ +static void *put_labels[4 * 2 * 2] = { + &&put_s8, /* s8 -> s8 */ + &&put_u8, /* u8 -> s8 */ + &&put_s8, /* s8 -> s8 */ + &&put_u8, /* u8 -> s8 */ + &&put_s16h, /* s16h -> s16h */ + &&put_u16h, /* u16h -> s16h */ + &&put_s16s, /* s16s -> s16h */ + &&put_u16s, /* u16s -> s16h */ + &&put_s24h, /* s24h -> s32h */ + &&put_u24h, /* u24h -> s32h */ + &&put_s24s, /* s24s -> s32h */ + &&put_u24s, /* u24s -> s32h */ + &&put_s32h, /* s32h -> s32h */ + &&put_u32h, /* u32h -> s32h */ + &&put_s32s, /* s32s -> s32h */ + &&put_u32s, /* u32s -> s32h */ +}; +#endif + +#ifdef PUT_END +put_s8: as_s8(dst) = sample; goto PUT_END; +put_u8: as_u8(dst) = sample ^ 0x80; goto PUT_END; +put_s16h: as_s16(dst) = sample; goto PUT_END; +put_u16h: as_u16(dst) = sample ^ 0x8000; goto PUT_END; +put_s16s: as_s16(dst) = swab16(sample); goto PUT_END; +put_u16s: as_u16(dst) = swab16(sample ^ 0x80); goto PUT_END; +put_s24h: as_s24(dst) = sample & 0xffffff; goto PUT_END; +put_u24h: as_u24(dst) = sample ^ 0x80000000; goto PUT_END; +put_s24s: as_s24(dst) = swab32(sample & 0xffffff); goto PUT_END; +put_u24s: as_u24(dst) = swab32(sample ^ 0x80); goto PUT_END; +put_s32h: as_s32(dst) = sample; goto PUT_END; +put_u32h: as_u32(dst) = sample ^ 0x80000000; goto PUT_END; +put_s32s: as_s32(dst) = swab32(sample); goto PUT_END; +put_u32s: as_u32(dst) = swab32(sample ^ 0x80); goto PUT_END; +#endif +#endif + +#undef as_u8 +#undef as_u16 +#undef as_u32 +#undef as_s8 +#undef as_s16 +#undef as_s32 diff -Nru a/sound/core/oss/rate.c b/sound/core/oss/rate.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/rate.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,387 @@ +/* + * Rate conversion Plug-In + * Copyright (c) 1999 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define SHIFT 11 +#define BITS (1<extra_data; + data->pos = 0; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + data->channels[channel].last_S1 = 0; + data->channels[channel].last_S2 = 0; + } +} + +static void resample_expand(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + int src_frames, int dst_frames) +{ + unsigned int pos = 0; + signed int val; + signed short S1, S2; + char *src, *dst; + unsigned int channel; + int src_step, dst_step; + int src_frames1, dst_frames1; + rate_t *data = (rate_t *)plugin->extra_data; + rate_channel_t *rchannels = data->channels; + +#define GET_S16_LABELS +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS +#undef PUT_S16_LABELS + void *get = get_s16_labels[data->get]; + void *put = put_s16_labels[data->put]; + void *get_s16_end = 0; + signed short sample = 0; +#define GET_S16_END *get_s16_end +#include "plugin_ops.h" +#undef GET_S16_END + + for (channel = 0; channel < plugin->src_format.channels; channel++) { + pos = data->pos; + S1 = rchannels->last_S1; + S2 = rchannels->last_S2; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + src_frames1 = src_frames; + dst_frames1 = dst_frames; + if (pos & ~R_MASK) { + get_s16_end = &&after_get1; + goto *get; + after_get1: + pos &= R_MASK; + S1 = S2; + S2 = sample; + src += src_step; + src_frames1--; + } + while (dst_frames1-- > 0) { + if (pos & ~R_MASK) { + pos &= R_MASK; + S1 = S2; + if (src_frames1-- > 0) { + get_s16_end = &&after_get2; + goto *get; + after_get2: + S2 = sample; + src += src_step; + } + } + val = S1 + ((S2 - S1) * (signed int)pos) / BITS; + if (val < -32768) + val = -32768; + else if (val > 32767) + val = 32767; + sample = val; + goto *put; +#define PUT_S16_END after_put +#include "plugin_ops.h" +#undef PUT_S16_END + after_put: + dst += dst_step; + pos += data->pitch; + } + rchannels->last_S1 = S1; + rchannels->last_S2 = S2; + rchannels++; + } + data->pos = pos; +} + +static void resample_shrink(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + int src_frames, int dst_frames) +{ + unsigned int pos = 0; + signed int val; + signed short S1, S2; + char *src, *dst; + unsigned int channel; + int src_step, dst_step; + int src_frames1, dst_frames1; + rate_t *data = (rate_t *)plugin->extra_data; + rate_channel_t *rchannels = data->channels; + +#define GET_S16_LABELS +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS +#undef PUT_S16_LABELS + void *get = get_s16_labels[data->get]; + void *put = put_s16_labels[data->put]; + signed short sample = 0; + + for (channel = 0; channel < plugin->src_format.channels; ++channel) { + pos = data->pos; + S1 = rchannels->last_S1; + S2 = rchannels->last_S2; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + src_frames1 = src_frames; + dst_frames1 = dst_frames; + while (dst_frames1 > 0) { + S1 = S2; + if (src_frames1-- > 0) { + goto *get; +#define GET_S16_END after_get +#include "plugin_ops.h" +#undef GET_S16_END + after_get: + S2 = sample; + src += src_step; + } + if (pos & ~R_MASK) { + pos &= R_MASK; + val = S1 + ((S2 - S1) * (signed int)pos) / BITS; + if (val < -32768) + val = -32768; + else if (val > 32767) + val = 32767; + sample = val; + goto *put; +#define PUT_S16_END after_put +#include "plugin_ops.h" +#undef PUT_S16_END + after_put: + dst += dst_step; + dst_frames1--; + } + pos += data->pitch; + } + rchannels->last_S1 = S1; + rchannels->last_S2 = S2; + rchannels++; + } + data->pos = pos; +} + +static snd_pcm_sframes_t rate_src_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) +{ + rate_t *data; + snd_pcm_sframes_t res; + + snd_assert(plugin != NULL, return -ENXIO); + if (frames == 0) + return 0; + data = (rate_t *)plugin->extra_data; + if (plugin->src_format.rate < plugin->dst_format.rate) { + res = (((frames * data->pitch) + (BITS/2)) >> SHIFT); + } else { + res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch); + } + if (data->old_src_frames > 0) { + snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames; + while (data->old_src_frames < frames1) { + frames1 >>= 1; + res1 <<= 1; + } + while (data->old_src_frames > frames1) { + frames1 <<= 1; + res1 >>= 1; + } + if (data->old_src_frames == frames1) + return res1; + } + data->old_src_frames = frames; + data->old_dst_frames = res; + return res; +} + +static snd_pcm_sframes_t rate_dst_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) +{ + rate_t *data; + snd_pcm_sframes_t res; + + snd_assert(plugin != NULL, return -ENXIO); + if (frames == 0) + return 0; + data = (rate_t *)plugin->extra_data; + if (plugin->src_format.rate < plugin->dst_format.rate) { + res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch); + } else { + res = (((frames * data->pitch) + (BITS/2)) >> SHIFT); + } + if (data->old_dst_frames > 0) { + snd_pcm_sframes_t frames1 = frames, res1 = data->old_src_frames; + while (data->old_dst_frames < frames1) { + frames1 >>= 1; + res1 <<= 1; + } + while (data->old_dst_frames > frames1) { + frames1 <<= 1; + res1 >>= 1; + } + if (data->old_dst_frames == frames1) + return res1; + } + data->old_dst_frames = frames; + data->old_src_frames = res; + return res; +} + +static snd_pcm_sframes_t rate_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + snd_pcm_uframes_t dst_frames; + rate_t *data; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; +#ifdef CONFIG_SND_DEBUG + { + unsigned int channel; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + snd_assert(src_channels[channel].area.first % 8 == 0 && + src_channels[channel].area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels[channel].area.first % 8 == 0 && + dst_channels[channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + + dst_frames = rate_dst_frames(plugin, frames); + data = (rate_t *)plugin->extra_data; + data->func(plugin, src_channels, dst_channels, frames, dst_frames); + return dst_frames; +} + +static int rate_action(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_action_t action, + unsigned long udata ATTRIBUTE_UNUSED) +{ + snd_assert(plugin != NULL, return -ENXIO); + switch (action) { + case INIT: + case PREPARE: + rate_init(plugin); + break; + default: + break; + } + return 0; /* silenty ignore other actions */ +} + +int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + rate_t *data; + snd_pcm_plugin_t *plugin; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + snd_assert(src_format->channels > 0, return -ENXIO); + snd_assert(snd_pcm_format_linear(src_format->format) != 0, return -ENXIO); + snd_assert(snd_pcm_format_linear(dst_format->format) != 0, return -ENXIO); + snd_assert(src_format->rate != dst_format->rate, return -ENXIO); + + err = snd_pcm_plugin_build(plug, "rate conversion", + src_format, dst_format, + sizeof(rate_t) + src_format->channels * sizeof(rate_channel_t), + &plugin); + if (err < 0) + return err; + data = (rate_t *)plugin->extra_data; + data->get = getput_index(src_format->format); + data->put = getput_index(dst_format->format); + + if (src_format->rate < dst_format->rate) { + data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate; + data->func = resample_expand; + } else { + data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate; + data->func = resample_shrink; + } + data->pos = 0; + rate_init(plugin); + data->old_src_frames = data->old_dst_frames = 0; + plugin->transfer = rate_transfer; + plugin->src_frames = rate_src_frames; + plugin->dst_frames = rate_dst_frames; + plugin->action = rate_action; + *r_plugin = plugin; + return 0; +} diff -Nru a/sound/core/oss/route.c b/sound/core/oss/route.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/oss/route.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,581 @@ +/* + * Attenuated route Plug-In + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "pcm_plugin.h" + +/* The best possible hack to support missing optimization in gcc 2.7.2.3 */ +#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0 +#define div(a) a /= ROUTE_PLUGIN_RESOLUTION +#elif ROUTE_PLUGIN_RESOLUTION == 16 +#define div(a) a >>= 4 +#else +#error "Add some code here" +#endif + +typedef struct ttable_dst ttable_dst_t; +typedef struct route_private_data route_t; + +typedef void (*route_channel_f)(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable, snd_pcm_uframes_t frames); + +typedef struct { + int channel; + int as_int; +#if ROUTE_PLUGIN_USE_FLOAT + float as_float; +#endif +} ttable_src_t; + +struct ttable_dst { + int att; /* Attenuated */ + unsigned int nsrcs; + ttable_src_t* srcs; + route_channel_f func; +}; + +struct route_private_data { + enum {R_UINT32=0, R_UINT64=1, R_FLOAT=2} sum_type; + int get, put; + int conv; + int src_sample_size; + ttable_dst_t ttable[0]; +}; + +typedef union { + u_int32_t as_uint32; + u_int64_t as_uint64; +#if ROUTE_PLUGIN_USE_FLOAT + float as_float; +#endif +} sum_t; + + +static void route_to_channel_from_zero(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable ATTRIBUTE_UNUSED, snd_pcm_uframes_t frames) +{ + if (dst_channel->wanted) + snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format); + dst_channel->enabled = 0; +} + +static void route_to_channel_from_one(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable, snd_pcm_uframes_t frames) +{ +#define CONV_LABELS +#include "plugin_ops.h" +#undef CONV_LABELS + route_t *data = (route_t *)plugin->extra_data; + void *conv; + const snd_pcm_plugin_channel_t *src_channel = 0; + unsigned int srcidx; + char *src, *dst; + int src_step, dst_step; + for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) { + src_channel = &src_channels[ttable->srcs[srcidx].channel]; + if (src_channel->area.addr != NULL) + break; + } + if (srcidx == ttable->nsrcs) { + route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); + return; + } + + dst_channel->enabled = 1; + conv = conv_labels[data->conv]; + src = src_channel->area.addr + src_channel->area.first / 8; + src_step = src_channel->area.step / 8; + dst = dst_channel->area.addr + dst_channel->area.first / 8; + dst_step = dst_channel->area.step / 8; + while (frames-- > 0) { + goto *conv; +#define CONV_END after +#include "plugin_ops.h" +#undef CONV_END + after: + src += src_step; + dst += dst_step; + } +} + +static void route_to_channel(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable, snd_pcm_uframes_t frames) +{ +#define GET_U_LABELS +#define PUT_U32_LABELS +#include "plugin_ops.h" +#undef GET_U_LABELS +#undef PUT_U32_LABELS + static void *zero_labels[3] = { &&zero_int32, &&zero_int64, +#if ROUTE_PLUGIN_USE_FLOAT + &&zero_float +#endif + }; + /* sum_type att */ + static void *add_labels[3 * 2] = { &&add_int32_noatt, &&add_int32_att, + &&add_int64_noatt, &&add_int64_att, +#if ROUTE_PLUGIN_USE_FLOAT + &&add_float_noatt, &&add_float_att +#endif + }; + /* sum_type att shift */ + static void *norm_labels[3 * 2 * 4] = { 0, + &&norm_int32_8_noatt, + &&norm_int32_16_noatt, + &&norm_int32_24_noatt, + 0, + &&norm_int32_8_att, + &&norm_int32_16_att, + &&norm_int32_24_att, + &&norm_int64_0_noatt, + &&norm_int64_8_noatt, + &&norm_int64_16_noatt, + &&norm_int64_24_noatt, + &&norm_int64_0_att, + &&norm_int64_8_att, + &&norm_int64_16_att, + &&norm_int64_24_att, +#if ROUTE_PLUGIN_USE_FLOAT + &&norm_float_0, + &&norm_float_8, + &&norm_float_16, + &&norm_float_24, + &&norm_float_0, + &&norm_float_8, + &&norm_float_16, + &&norm_float_24, +#endif + }; + route_t *data = (route_t *)plugin->extra_data; + void *zero, *get, *add, *norm, *put_u32; + int nsrcs = ttable->nsrcs; + char *dst; + int dst_step; + char *srcs[nsrcs]; + int src_steps[nsrcs]; + ttable_src_t src_tt[nsrcs]; + u_int32_t sample = 0; + int srcidx, srcidx1 = 0; + for (srcidx = 0; srcidx < nsrcs; ++srcidx) { + const snd_pcm_plugin_channel_t *src_channel = &src_channels[ttable->srcs[srcidx].channel]; + if (!src_channel->enabled) + continue; + srcs[srcidx1] = src_channel->area.addr + src_channel->area.first / 8; + src_steps[srcidx1] = src_channel->area.step / 8; + src_tt[srcidx1] = ttable->srcs[srcidx]; + srcidx1++; + } + nsrcs = srcidx1; + if (nsrcs == 0) { + route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); + return; + } else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) { + route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames); + return; + } + + dst_channel->enabled = 1; + zero = zero_labels[data->sum_type]; + get = get_u_labels[data->get]; + add = add_labels[data->sum_type * 2 + ttable->att]; + norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size]; + put_u32 = put_u32_labels[data->put]; + dst = dst_channel->area.addr + dst_channel->area.first / 8; + dst_step = dst_channel->area.step / 8; + + while (frames-- > 0) { + ttable_src_t *ttp = src_tt; + sum_t sum; + + /* Zero sum */ + goto *zero; + zero_int32: + sum.as_uint32 = 0; + goto zero_end; + zero_int64: + sum.as_uint64 = 0; + goto zero_end; +#if ROUTE_PLUGIN_USE_FLOAT + zero_float: + sum.as_float = 0.0; + goto zero_end; +#endif + zero_end: + for (srcidx = 0; srcidx < nsrcs; ++srcidx) { + char *src = srcs[srcidx]; + + /* Get sample */ + goto *get; +#define GET_U_END after_get +#include "plugin_ops.h" +#undef GET_U_END + after_get: + + /* Sum */ + goto *add; + add_int32_att: + sum.as_uint32 += sample * ttp->as_int; + goto after_sum; + add_int32_noatt: + if (ttp->as_int) + sum.as_uint32 += sample; + goto after_sum; + add_int64_att: + sum.as_uint64 += (u_int64_t) sample * ttp->as_int; + goto after_sum; + add_int64_noatt: + if (ttp->as_int) + sum.as_uint64 += sample; + goto after_sum; +#if ROUTE_PLUGIN_USE_FLOAT + add_float_att: + sum.as_float += sample * ttp->as_float; + goto after_sum; + add_float_noatt: + if (ttp->as_int) + sum.as_float += sample; + goto after_sum; +#endif + after_sum: + srcs[srcidx] += src_steps[srcidx]; + ttp++; + } + + /* Normalization */ + goto *norm; + norm_int32_8_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_8_att: + sum.as_uint64 <<= 8; + norm_int64_0_att: + div(sum.as_uint64); + goto norm_int; + + norm_int32_16_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_16_att: + sum.as_uint64 <<= 16; + div(sum.as_uint64); + goto norm_int; + + norm_int32_24_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_24_att: + sum.as_uint64 <<= 24; + div(sum.as_uint64); + goto norm_int; + + norm_int32_8_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_8_noatt: + sum.as_uint64 <<= 8; + goto norm_int; + + norm_int32_16_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_16_noatt: + sum.as_uint64 <<= 16; + goto norm_int; + + norm_int32_24_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_24_noatt: + sum.as_uint64 <<= 24; + goto norm_int; + + norm_int64_0_noatt: + norm_int: + if (sum.as_uint64 > (u_int32_t)0xffffffff) + sample = (u_int32_t)0xffffffff; + else + sample = sum.as_uint64; + goto after_norm; + +#if ROUTE_PLUGIN_USE_FLOAT + norm_float_8: + sum.as_float *= 1 << 8; + goto norm_float; + norm_float_16: + sum.as_float *= 1 << 16; + goto norm_float; + norm_float_24: + sum.as_float *= 1 << 24; + goto norm_float; + norm_float_0: + norm_float: + sum.as_float = floor(sum.as_float + 0.5); + if (sum.as_float > (u_int32_t)0xffffffff) + sample = (u_int32_t)0xffffffff; + else + sample = sum.as_float; + goto after_norm; +#endif + after_norm: + + /* Put sample */ + goto *put_u32; +#define PUT_U32_END after_put_u32 +#include "plugin_ops.h" +#undef PUT_U32_END + after_put_u32: + + dst += dst_step; + } +} + +int route_src_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask) +{ + route_t *data = (route_t *)plugin->extra_data; + int schannels = plugin->src_format.channels; + int dchannels = plugin->dst_format.channels; + bitset_t *vmask = plugin->src_vmask; + int channel; + ttable_dst_t *dp = data->ttable; + bitset_zero(vmask, schannels); + for (channel = 0; channel < dchannels; channel++, dp++) { + unsigned int src; + ttable_src_t *sp; + if (!bitset_get(dst_vmask, channel)) + continue; + sp = dp->srcs; + for (src = 0; src < dp->nsrcs; src++, sp++) + bitset_set(vmask, sp->channel); + } + *src_vmask = vmask; + return 0; +} + +int route_dst_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask) +{ + route_t *data = (route_t *)plugin->extra_data; + int dchannels = plugin->dst_format.channels; + bitset_t *vmask = plugin->dst_vmask; + int channel; + ttable_dst_t *dp = data->ttable; + bitset_zero(vmask, dchannels); + for (channel = 0; channel < dchannels; channel++, dp++) { + unsigned int src; + ttable_src_t *sp; + sp = dp->srcs; + for (src = 0; src < dp->nsrcs; src++, sp++) { + if (bitset_get(src_vmask, sp->channel)) { + bitset_set(vmask, channel); + break; + } + } + } + *dst_vmask = vmask; + return 0; +} + +static void route_free(snd_pcm_plugin_t *plugin) +{ + route_t *data = (route_t *)plugin->extra_data; + unsigned int dst_channel; + for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { + if (data->ttable[dst_channel].srcs != NULL) + kfree(data->ttable[dst_channel].srcs); + } +} + +static int route_load_ttable(snd_pcm_plugin_t *plugin, + const route_ttable_entry_t* src_ttable) +{ + route_t *data; + unsigned int src_channel, dst_channel; + const route_ttable_entry_t *sptr; + ttable_dst_t *dptr; + if (src_ttable == NULL) + return 0; + data = (route_t *)plugin->extra_data; + dptr = data->ttable; + sptr = src_ttable; + plugin->private_free = route_free; + for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { + route_ttable_entry_t t = 0; + int att = 0; + int nsrcs = 0; + ttable_src_t srcs[plugin->src_format.channels]; + for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) { + snd_assert(*sptr >= 0 || *sptr <= FULL, return -ENXIO); + if (*sptr != 0) { + srcs[nsrcs].channel = src_channel; +#if ROUTE_PLUGIN_USE_FLOAT + /* Also in user space for non attenuated */ + srcs[nsrcs].as_int = (*sptr == FULL ? ROUTE_PLUGIN_RESOLUTION : 0); + srcs[nsrcs].as_float = *sptr; +#else + srcs[nsrcs].as_int = *sptr; +#endif + if (*sptr != FULL) + att = 1; + t += *sptr; + nsrcs++; + } + sptr++; + } + dptr->att = att; + dptr->nsrcs = nsrcs; + if (nsrcs == 0) + dptr->func = route_to_channel_from_zero; + else if (nsrcs == 1 && !att) + dptr->func = route_to_channel_from_one; + else + dptr->func = route_to_channel; + if (nsrcs > 0) { + int srcidx; + dptr->srcs = snd_kcalloc(nsrcs * sizeof(*srcs), GFP_KERNEL); + for(srcidx = 0; srcidx < nsrcs; srcidx++) + dptr->srcs[srcidx] = srcs[srcidx]; + } else + dptr->srcs = 0; + dptr++; + } + return 0; +} + +static snd_pcm_sframes_t route_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + route_t *data; + int src_nchannels, dst_nchannels; + int dst_channel; + ttable_dst_t *ttp; + snd_pcm_plugin_channel_t *dvp; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; + data = (route_t *)plugin->extra_data; + + src_nchannels = plugin->src_format.channels; + dst_nchannels = plugin->dst_format.channels; + +#ifdef CONFIG_SND_DEBUG + { + int src_channel; + for (src_channel = 0; src_channel < src_nchannels; ++src_channel) { + snd_assert(src_channels[src_channel].area.first % 8 == 0 || + src_channels[src_channel].area.step % 8 == 0, + return -ENXIO); + } + for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { + snd_assert(dst_channels[dst_channel].area.first % 8 == 0 || + dst_channels[dst_channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + + ttp = data->ttable; + dvp = dst_channels; + for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { + ttp->func(plugin, src_channels, dvp, ttp, frames); + dvp++; + ttp++; + } + return frames; +} + +int getput_index(int format) +{ + int sign, width, endian; + sign = !snd_pcm_format_signed(format); + width = snd_pcm_format_width(format) / 8 - 1; +#ifdef SNDRV_LITTLE_ENDIAN + endian = snd_pcm_format_big_endian(format); +#else + endian = snd_pcm_format_little_endian(format); +#endif + if (endian < 0) + endian = 0; + return width * 4 + endian * 2 + sign; +} + +int snd_pcm_plugin_build_route(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + route_ttable_entry_t *ttable, + snd_pcm_plugin_t **r_plugin) +{ + route_t *data; + snd_pcm_plugin_t *plugin; + int err; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(snd_pcm_format_linear(src_format->format) != 0 && + snd_pcm_format_linear(dst_format->format) != 0, + return -ENXIO); + + err = snd_pcm_plugin_build(plug, "attenuated route conversion", + src_format, dst_format, + sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->channels, + &plugin); + if (err < 0) + return err; + + data = (route_t *) plugin->extra_data; + + data->get = getput_index(src_format->format); + data->put = getput_index(dst_format->format); + data->conv = conv_index(src_format->format, dst_format->format); + +#if ROUTE_PLUGIN_USE_FLOAT + data->sum_type = R_FLOAT; +#else + if (snd_pcm_format_width(src_format->format) == 32) + data->sum_type = R_UINT64; + else + data->sum_type = R_UINT32; +#endif + data->src_sample_size = snd_pcm_format_width(src_format->format) / 8; + + if ((err = route_load_ttable(plugin, ttable)) < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + plugin->transfer = route_transfer; + plugin->src_channels_mask = route_src_channels_mask; + plugin->dst_channels_mask = route_dst_channels_mask; + *r_plugin = plugin; + return 0; +} diff -Nru a/sound/core/pcm.c b/sound/core/pcm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/pcm.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,994 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela , Abramo Bagnara "); +MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); +MODULE_LICENSE("GPL"); + +snd_pcm_t *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES]; +static LIST_HEAD(snd_pcm_notify_list); +static DECLARE_MUTEX(register_mutex); + +int snd_pcm_free(snd_pcm_t *pcm); +static int snd_pcm_dev_free(snd_device_t *device); +static int snd_pcm_dev_register(snd_device_t *device); +static int snd_pcm_dev_unregister(snd_device_t *device); + +void snd_pcm_lock(int xup) +{ + if (!xup) { + down(®ister_mutex); + } else { + up(®ister_mutex); + } +} + +static int snd_pcm_control_ioctl(snd_card_t * card, + snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg) +{ + unsigned int tmp; + + tmp = card->number * SNDRV_PCM_DEVICES; + switch (cmd) { + case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: + { + int device; + + if (get_user(device, (int *)arg)) + return -EFAULT; + device = device < 0 ? 0 : device + 1; + while (device < SNDRV_PCM_DEVICES) { + if (snd_pcm_devices[tmp + device]) + break; + device++; + } + if (device == SNDRV_PCM_DEVICES) + device = -1; + if (put_user(device, (int *)arg)) + return -EFAULT; + return 0; + } + case SNDRV_CTL_IOCTL_PCM_INFO: + { + snd_pcm_info_t *info = (snd_pcm_info_t *)arg; + unsigned int device, subdevice; + snd_pcm_stream_t stream; + snd_pcm_t *pcm; + snd_pcm_str_t *pstr; + snd_pcm_substream_t *substream; + if (get_user(device, &info->device)) + return -EFAULT; + if (device >= SNDRV_PCM_DEVICES) + return -ENXIO; + pcm = snd_pcm_devices[tmp + device]; + if (pcm == NULL) + return -ENXIO; + if (get_user(stream, &info->stream)) + return -EFAULT; + if (stream < 0 || stream > 1) + return -EINVAL; + pstr = &pcm->streams[stream]; + if (pstr->substream_count == 0) + return -ENOENT; + if (get_user(subdevice, &info->subdevice)) + return -EFAULT; + if (subdevice >= pstr->substream_count) + return -ENXIO; + for (substream = pstr->substream; substream; substream = substream->next) + if (substream->number == subdevice) + break; + if (substream == NULL) + return -ENXIO; + return snd_pcm_info_user(substream, info); + } + case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: + { + int val; + + if (get_user(val, (int *)arg)) + return -EFAULT; + control->prefer_pcm_subdevice = val; + return 0; + } + } + return -ENOIOCTLCMD; +} +#define STATE(v) [SNDRV_PCM_STATE_##v] = #v +#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v +#define READY(v) [SNDRV_PCM_READY_##v] = #v +#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v +#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v +#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v +#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v +#define START(v) [SNDRV_PCM_START_##v] = #v +#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v +#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v + +char *snd_pcm_stream_names[] = { + STREAM(PLAYBACK), + STREAM(CAPTURE), +}; + +char *snd_pcm_state_names[] = { + STATE(OPEN), + STATE(SETUP), + STATE(PREPARED), + STATE(RUNNING), + STATE(XRUN), + STATE(PAUSED), +}; + +char *snd_pcm_access_names[] = { + ACCESS(MMAP_INTERLEAVED), + ACCESS(MMAP_NONINTERLEAVED), + ACCESS(MMAP_COMPLEX), + ACCESS(RW_INTERLEAVED), + ACCESS(RW_NONINTERLEAVED), +}; + +char *snd_pcm_format_names[] = { + FORMAT(S8), + FORMAT(U8), + FORMAT(S16_LE), + FORMAT(S16_BE), + FORMAT(U16_LE), + FORMAT(U16_BE), + FORMAT(S24_LE), + FORMAT(S24_BE), + FORMAT(U24_LE), + FORMAT(U24_BE), + FORMAT(S32_LE), + FORMAT(S32_BE), + FORMAT(U32_LE), + FORMAT(U32_BE), + FORMAT(FLOAT_LE), + FORMAT(FLOAT_BE), + FORMAT(FLOAT64_LE), + FORMAT(FLOAT64_BE), + FORMAT(IEC958_SUBFRAME_LE), + FORMAT(IEC958_SUBFRAME_BE), + FORMAT(MU_LAW), + FORMAT(A_LAW), + FORMAT(IMA_ADPCM), + FORMAT(MPEG), + FORMAT(GSM), + FORMAT(SPECIAL), +}; + +char *snd_pcm_subformat_names[] = { + SUBFORMAT(STD), +}; + +char *snd_pcm_tstamp_mode_names[] = { + TSTAMP(NONE), + TSTAMP(MMAP), +}; + +const char *snd_pcm_stream_name(snd_pcm_stream_t stream) +{ + snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return 0); + return snd_pcm_stream_names[stream]; +} + +const char *snd_pcm_access_name(snd_pcm_access_t access) +{ + snd_assert(access <= SNDRV_PCM_ACCESS_LAST, return 0); + return snd_pcm_access_names[access]; +} + +const char *snd_pcm_format_name(snd_pcm_format_t format) +{ + snd_assert(format <= SNDRV_PCM_FORMAT_LAST, return 0); + return snd_pcm_format_names[format]; +} + +const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) +{ + snd_assert(subformat <= SNDRV_PCM_SUBFORMAT_LAST, return 0); + return snd_pcm_subformat_names[subformat]; +} + +const char *snd_pcm_tstamp_mode_name(snd_pcm_tstamp_t mode) +{ + snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return 0); + return snd_pcm_tstamp_mode_names[mode]; +} + +const char *snd_pcm_state_name(snd_pcm_state_t state) +{ + snd_assert(state <= SNDRV_PCM_STATE_LAST, return 0); + return snd_pcm_state_names[state]; +} + +#ifdef CONFIG_SND_OSSEMUL +#include +const char *snd_pcm_oss_format_name(int format) +{ + switch (format) { + case AFMT_MU_LAW: + return "MU_LAW"; + case AFMT_A_LAW: + return "A_LAW"; + case AFMT_IMA_ADPCM: + return "IMA_ADPCM"; + case AFMT_U8: + return "U8"; + case AFMT_S16_LE: + return "S16_LE"; + case AFMT_S16_BE: + return "S16_BE"; + case AFMT_S8: + return "S8"; + case AFMT_U16_LE: + return "U16_LE"; + case AFMT_U16_BE: + return "U16_BE"; + case AFMT_MPEG: + return "MPEG"; + default: + return "unknown"; + } +} +#endif + + +static void snd_pcm_proc_info_read(snd_pcm_substream_t *substream, snd_info_buffer_t *buffer) +{ + snd_pcm_info_t info; + int err; + snd_runtime_check(substream, return); + err = snd_pcm_info(substream, &info); + if (err < 0) { + snd_iprintf(buffer, "error %d\n", err); + return; + } + snd_iprintf(buffer, "card: %d\n", info.card); + snd_iprintf(buffer, "device: %d\n", info.device); + snd_iprintf(buffer, "subdevice: %d\n", info.subdevice); + snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info.stream)); + snd_iprintf(buffer, "id: %s\n", info.id); + snd_iprintf(buffer, "name: %s\n", info.name); + snd_iprintf(buffer, "subname: %s\n", info.subname); + snd_iprintf(buffer, "class: %d\n", info.dev_class); + snd_iprintf(buffer, "subclass: %d\n", info.dev_subclass); + snd_iprintf(buffer, "subdevices_count: %d\n", info.subdevices_count); + snd_iprintf(buffer, "subdevices_avail: %d\n", info.subdevices_avail); +} + +static void snd_pcm_stream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_proc_info_read(((snd_pcm_str_t *)entry->private_data)->substream, buffer); +} + +static void snd_pcm_substream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_proc_info_read((snd_pcm_substream_t *)entry->private_data, buffer); +} + +static void snd_pcm_substream_proc_hw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + if (!runtime) { + snd_iprintf(buffer, "closed\n"); + return; + } + spin_lock_irq(&runtime->lock); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + snd_iprintf(buffer, "no setup\n"); + spin_unlock_irq(&runtime->lock); + return; + } + snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); + snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); + snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); + snd_iprintf(buffer, "channels: %u\n", runtime->channels); + snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); + snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); + snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); + snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time); +#ifdef CONFIG_SND_OSSEMUL + if (substream->oss.oss) { + snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); + snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); + snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); + snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); + snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); + } +#endif + spin_unlock_irq(&runtime->lock); +} + +static void snd_pcm_substream_proc_sw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + if (!runtime) { + snd_iprintf(buffer, "closed\n"); + return; + } + spin_lock_irq(&runtime->lock); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + snd_iprintf(buffer, "no setup\n"); + spin_unlock_irq(&runtime->lock); + return; + } + snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); + snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); + snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min); + snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); + snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align); + snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); + snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); + snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); + snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); + snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); + spin_unlock_irq(&runtime->lock); +} + +static void snd_pcm_substream_proc_status_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_status_t status; + int err; + if (!runtime) { + snd_iprintf(buffer, "closed\n"); + return; + } + err = snd_pcm_status(substream, &status); + if (err < 0) { + snd_iprintf(buffer, "error %d\n", err); + return; + } + snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); + snd_iprintf(buffer, "trigger_time: %ld.%06ld\n", + status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_usec); + snd_iprintf(buffer, "tstamp : %ld.%06ld\n", + status.tstamp.tv_sec, status.tstamp.tv_usec); + snd_iprintf(buffer, "delay : %ld\n", status.delay); + snd_iprintf(buffer, "avail : %ld\n", status.avail); + snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); + snd_iprintf(buffer, "-----\n"); + snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); + snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); +} + +static int snd_pcm_stream_proc_init(snd_pcm_str_t *pstr) +{ + snd_pcm_t *pcm = pstr->pcm; + snd_info_entry_t *entry; + char name[16]; + + sprintf(name, "pcm%i%c", pcm->device, + pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); + if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + pstr->proc_root = entry; + + if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_stream_proc_info_read; + entry->private_data = pstr; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + pstr->proc_info_entry = entry; + + return 0; +} + +static int snd_pcm_stream_proc_done(snd_pcm_str_t *pstr) +{ + if (pstr->proc_info_entry) { + snd_info_unregister(pstr->proc_info_entry); + pstr->proc_info_entry = NULL; + } + if (pstr->proc_root) { + snd_info_unregister(pstr->proc_root); + pstr->proc_root = NULL; + } + return 0; +} + +static int snd_pcm_substream_proc_init(snd_pcm_substream_t *substream) +{ + snd_info_entry_t *entry; + snd_card_t *card; + char name[16]; + + card = substream->pcm->card; + + sprintf(name, "sub%i", substream->number); + if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + substream->proc_root = entry; + + if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_info_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_info_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_hw_params_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_hw_params_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_sw_params_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_sw_params_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_status_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_status_entry = entry; + + return 0; +} + +static int snd_pcm_substream_proc_done(snd_pcm_substream_t *substream) +{ + if (substream->proc_info_entry) { + snd_info_unregister(substream->proc_info_entry); + substream->proc_info_entry = 0; + } + if (substream->proc_hw_params_entry) { + snd_info_unregister(substream->proc_hw_params_entry); + substream->proc_hw_params_entry = 0; + } + if (substream->proc_sw_params_entry) { + snd_info_unregister(substream->proc_sw_params_entry); + substream->proc_sw_params_entry = 0; + } + if (substream->proc_status_entry) { + snd_info_unregister(substream->proc_status_entry); + substream->proc_status_entry = 0; + } + if (substream->proc_root) { + snd_info_unregister(substream->proc_root); + substream->proc_root = 0; + } + return 0; +} + +static int snd_pcm_new_stream(snd_pcm_t *pcm, + snd_pcm_str_t *pstr, + int substream_count, + int stream) +{ + int idx, err; + snd_pcm_substream_t *substream, *prev; + +#ifdef CONFIG_SND_OSSEMUL + init_MUTEX(&pstr->oss.setup_mutex); +#endif + pstr->stream = stream; + pstr->pcm = pcm; + pstr->substream_count = substream_count; + pstr->reg = &snd_pcm_reg[stream]; + if (substream_count > 0) { + err = snd_pcm_stream_proc_init(pstr); + if (err < 0) + return err; + } + prev = NULL; + for (idx = 0, prev = NULL; idx < substream_count; idx++) { + substream = snd_magic_kcalloc(snd_pcm_substream_t, 0, GFP_KERNEL); + if (substream == NULL) + return -ENOMEM; + substream->pcm = pcm; + substream->pstr = pstr; + substream->number = idx; + substream->stream = stream; + sprintf(substream->name, "subdevice #%i", idx); + substream->buffer_bytes_max = UINT_MAX; + if (prev == NULL) + pstr->substream = substream; + else + prev->next = substream; + substream->link_next = substream; + substream->link_prev = substream; + err = snd_pcm_substream_proc_init(substream); + if (err < 0) { + snd_magic_kfree(substream); + return err; + } + substream->dma_type = SNDRV_PCM_DMA_TYPE_ISA; + substream->dma_private = NULL; + spin_lock_init(&substream->timer_lock); + prev = substream; + } + return 0; +} + +int snd_pcm_new(snd_card_t * card, char *id, int device, + int playback_count, int capture_count, + snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + static snd_device_ops_t ops = { + dev_free: snd_pcm_dev_free, + dev_register: snd_pcm_dev_register, + dev_unregister: snd_pcm_dev_unregister + }; + + snd_assert(rpcm != NULL, return -EINVAL); + *rpcm = NULL; + snd_assert(card != NULL, return -ENXIO); + pcm = snd_magic_kcalloc(snd_pcm_t, 0, GFP_KERNEL); + if (pcm == NULL) + return -ENOMEM; + pcm->card = card; + pcm->device = device; + if (id) { + strncpy(pcm->id, id, sizeof(pcm->id) - 1); + } + if ((err = snd_pcm_new_stream(pcm, &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK], playback_count, SNDRV_PCM_STREAM_PLAYBACK)) < 0) { + snd_pcm_free(pcm); + return err; + } + if ((err = snd_pcm_new_stream(pcm, &pcm->streams[SNDRV_PCM_STREAM_CAPTURE], capture_count, SNDRV_PCM_STREAM_CAPTURE)) < 0) { + snd_pcm_free(pcm); + return err; + } + init_MUTEX(&pcm->open_mutex); + init_waitqueue_head(&pcm->open_wait); + if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { + snd_pcm_free(pcm); + return err; + } + *rpcm = pcm; + return 0; +} + +static void snd_pcm_free_stream(snd_pcm_str_t * pstr) +{ + snd_pcm_substream_t *substream, *substream_next; +#ifdef CONFIG_SND_OSSEMUL + snd_pcm_oss_setup_t *setup, *setupn; +#endif + substream = pstr->substream; + while (substream) { + substream_next = substream->next; + snd_pcm_substream_proc_done(substream); + snd_magic_kfree(substream); + substream = substream_next; + } + snd_pcm_stream_proc_done(pstr); +#ifdef CONFIG_SND_OSSEMUL + for (setup = pstr->oss.setup_list; setup; setup = setupn) { + setupn = setup->next; + kfree(setup->task_name); + kfree(setup); + } +#endif +} + +int snd_pcm_free(snd_pcm_t *pcm) +{ + snd_assert(pcm != NULL, return -ENXIO); + if (pcm->private_free) + pcm->private_free(pcm); + snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); + snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); + snd_magic_kfree(pcm); + return 0; +} + +int snd_pcm_dev_free(snd_device_t *device) +{ + snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + return snd_pcm_free(pcm); +} + +static void snd_pcm_tick_timer_func(unsigned long data) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t*) data; + snd_pcm_tick_elapsed(substream); +} + +int snd_pcm_open_substream(snd_pcm_t *pcm, int stream, + snd_pcm_substream_t **rsubstream) +{ + snd_pcm_str_t * pstr; + snd_pcm_substream_t * substream; + snd_pcm_runtime_t * runtime; + snd_ctl_file_t *kctl; + snd_card_t *card; + struct list_head *list; + int prefer_subdevice = -1; + size_t size; + + snd_assert(rsubstream != NULL, return -EINVAL); + *rsubstream = NULL; + snd_assert(pcm != NULL, return -ENXIO); + pstr = &pcm->streams[stream]; + if (pstr->substream == NULL) + return -ENODEV; + + card = pcm->card; + read_lock(&card->control_rwlock); + list_for_each(list, &card->ctl_files) { + kctl = snd_ctl_file(list); + if (kctl->pid == current->pid) { + prefer_subdevice = kctl->prefer_pcm_subdevice; + break; + } + } + read_unlock(&card->control_rwlock); + + if (pstr->substream_count == 0) + return -ENODEV; + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { + for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { + if (SUBSTREAM_BUSY(substream)) + return -EAGAIN; + } + } + break; + case SNDRV_PCM_STREAM_CAPTURE: + if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { + for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { + if (SUBSTREAM_BUSY(substream)) + return -EAGAIN; + } + } + break; + default: + return -EINVAL; + } + + if (prefer_subdevice >= 0) { + for (substream = pstr->substream; substream; substream = substream->next) + if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) + goto __ok; + } + for (substream = pstr->substream; substream; substream = substream->next) + if (!SUBSTREAM_BUSY(substream)) + break; + __ok: + if (substream == NULL) + return -EAGAIN; + + runtime = snd_kcalloc(sizeof(snd_pcm_runtime_t), GFP_KERNEL); + if (runtime == NULL) + return -ENOMEM; + + size = PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)); + runtime->status = snd_malloc_pages(size, GFP_KERNEL); + if (runtime->status == NULL) { + kfree(runtime); + return -ENOMEM; + } + + size = PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)); + runtime->control = snd_malloc_pages(size, GFP_KERNEL); + if (runtime->control == NULL) { + kfree((void *)runtime->status); + kfree(runtime); + return -ENOMEM; + } + + memset((void*)runtime->status, 0, size); + memset((void*)runtime->control, 0, size); + + init_waitqueue_head(&runtime->sleep); + spin_lock_init(&runtime->lock); + atomic_set(&runtime->mmap_count, 0); + init_timer(&runtime->tick_timer); + runtime->tick_timer.function = snd_pcm_tick_timer_func; + runtime->tick_timer.data = (unsigned long) substream; + + runtime->status->state = SNDRV_PCM_STATE_OPEN; + + substream->runtime = runtime; + pstr->substream_opened++; + *rsubstream = substream; + return 0; +} + +void snd_pcm_release_substream(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t * runtime; + substream->file = NULL; + runtime = substream->runtime; + snd_assert(runtime != NULL, return); + if (runtime->private_free != NULL) + runtime->private_free(runtime); + snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t))); + snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t))); + kfree(runtime->hw_constraints.rules); + kfree(runtime); + substream->runtime = NULL; + substream->pstr->substream_opened--; +} + +int snd_pcm_dev_register(snd_device_t *device) +{ + int idx, cidx, err; + unsigned short minor; + snd_pcm_substream_t *substream; + struct list_head *list; + char str[16]; + snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + + snd_assert(pcm != NULL && device != NULL, return -ENXIO); + snd_pcm_lock(0); + idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; + if (snd_pcm_devices[idx]) { + snd_pcm_lock(1); + return -EBUSY; + } + snd_pcm_devices[idx] = pcm; + for (cidx = 0; cidx < 2; cidx++) { + int devtype = -1; + if (pcm->streams[cidx].substream == NULL) + continue; + switch (cidx) { + case SNDRV_PCM_STREAM_PLAYBACK: + sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); + minor = SNDRV_MINOR_PCM_PLAYBACK + idx; + devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; + break; + case SNDRV_PCM_STREAM_CAPTURE: + sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); + minor = SNDRV_MINOR_PCM_CAPTURE + idx; + devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; + break; + } + if ((err = snd_register_device(devtype, pcm->card, pcm->device, pcm->streams[cidx].reg, str)) < 0) { + snd_pcm_devices[idx] = NULL; + snd_pcm_lock(1); + return err; + } + for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) + snd_pcm_timer_init(substream); + } + list_for_each(list, &snd_pcm_notify_list) { + snd_pcm_notify_t *notify; + notify = list_entry(list, snd_pcm_notify_t, list); + if (notify->n_register) + notify->n_register(-1 /* idx + SNDRV_MINOR_PCM */, pcm); + } + snd_pcm_lock(1); + return 0; +} + +static int snd_pcm_dev_unregister(snd_device_t *device) +{ + int idx, cidx, devtype; + snd_pcm_substream_t *substream; + struct list_head *list; + snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + + snd_assert(pcm != NULL, return -ENXIO); + snd_pcm_lock(0); + idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; + if (snd_pcm_devices[idx] != pcm) { + snd_pcm_lock(1); + return -EINVAL; + } + for (cidx = 0; cidx < 2; cidx++) { + devtype = -1; + switch (cidx) { + case SNDRV_PCM_STREAM_PLAYBACK: + devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; + break; + case SNDRV_PCM_STREAM_CAPTURE: + devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; + break; + } + snd_unregister_device(devtype, pcm->card, pcm->device); + for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) + snd_pcm_timer_done(substream); + } + list_for_each(list, &snd_pcm_notify_list) { + snd_pcm_notify_t *notify; + notify = list_entry(list, snd_pcm_notify_t, list); + if (notify->n_unregister) + notify->n_unregister(-1 /* SNDRV_MINOR_PCM + idx */, pcm); + } + snd_pcm_devices[idx] = NULL; + snd_pcm_lock(1); + return snd_pcm_free(pcm); +} + +int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree) +{ + int idx; + + snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL); + snd_pcm_lock(0); + if (nfree) { + list_del(¬ify->list); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { + if (snd_pcm_devices[idx] == NULL) + continue; + notify->n_unregister(-1 /* idx + SNDRV_MINOR_PCM */, + snd_pcm_devices[idx]); + } + } else { + list_add_tail(¬ify->list, &snd_pcm_notify_list); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { + if (snd_pcm_devices[idx] == NULL) + continue; + notify->n_register(-1 /* idx + SNDRV_MINOR_PCM */, + snd_pcm_devices[idx]); + } + } + snd_pcm_lock(1); + return 0; +} + +/* + * Info interface + */ + +static void snd_pcm_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int idx; + snd_pcm_t *pcm; + + down(®ister_mutex); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { + pcm = snd_pcm_devices[idx]; + if (pcm == NULL) + continue; + snd_iprintf(buffer, "%02i-%02i: %s : %s", idx / SNDRV_PCM_DEVICES, + idx % SNDRV_PCM_DEVICES, pcm->id, pcm->name); + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) + snd_iprintf(buffer, " : playback %i", pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) + snd_iprintf(buffer, " : capture %i", pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); + snd_iprintf(buffer, "\n"); + } + up(®ister_mutex); +} + +/* + * ENTRY functions + */ + +static snd_info_entry_t *snd_pcm_proc_entry = NULL; + +static int __init alsa_pcm_init(void) +{ + snd_info_entry_t *entry; + + snd_ctl_register_ioctl(snd_pcm_control_ioctl); + if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = SNDRV_CARDS * SNDRV_PCM_DEVICES * 128; + entry->c.text.read = snd_pcm_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_pcm_proc_entry = entry; + return 0; +} + +static void __exit alsa_pcm_exit(void) +{ + snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); + if (snd_pcm_proc_entry) { + snd_info_unregister(snd_pcm_proc_entry); + snd_pcm_proc_entry = NULL; + } +} + +module_init(alsa_pcm_init) +module_exit(alsa_pcm_exit) + +EXPORT_SYMBOL(snd_pcm_lock); +EXPORT_SYMBOL(snd_pcm_devices); +EXPORT_SYMBOL(snd_pcm_new); +EXPORT_SYMBOL(snd_pcm_notify); +EXPORT_SYMBOL(snd_pcm_open_substream); +EXPORT_SYMBOL(snd_pcm_release_substream); + /* pcm_native.c */ +EXPORT_SYMBOL(snd_pcm_start); +#ifdef CONFIG_PM +EXPORT_SYMBOL(snd_pcm_suspend); +EXPORT_SYMBOL(snd_pcm_suspend_all); +#endif +EXPORT_SYMBOL(snd_pcm_kernel_playback_ioctl); +EXPORT_SYMBOL(snd_pcm_kernel_capture_ioctl); +EXPORT_SYMBOL(snd_pcm_kernel_ioctl); +EXPORT_SYMBOL(snd_pcm_open); +EXPORT_SYMBOL(snd_pcm_release); +EXPORT_SYMBOL(snd_pcm_playback_poll); +EXPORT_SYMBOL(snd_pcm_capture_poll); +EXPORT_SYMBOL(snd_pcm_mmap_data); + /* pcm_misc.c */ +EXPORT_SYMBOL(snd_pcm_format_signed); +EXPORT_SYMBOL(snd_pcm_format_unsigned); +EXPORT_SYMBOL(snd_pcm_format_linear); +EXPORT_SYMBOL(snd_pcm_format_little_endian); +EXPORT_SYMBOL(snd_pcm_format_big_endian); +EXPORT_SYMBOL(snd_pcm_format_width); +EXPORT_SYMBOL(snd_pcm_format_physical_width); +EXPORT_SYMBOL(snd_pcm_format_size); +EXPORT_SYMBOL(snd_pcm_format_silence_64); +EXPORT_SYMBOL(snd_pcm_format_set_silence); +EXPORT_SYMBOL(snd_pcm_build_linear_format); diff -Nru a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/pcm_lib.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2376 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void snd_pcm_playback_silence(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t frames, ofs; + snd_pcm_sframes_t noise_dist; + if (runtime->silenced_start != runtime->control->appl_ptr) { + snd_pcm_sframes_t n = runtime->control->appl_ptr - runtime->silenced_start; + if (n < 0) + n += runtime->boundary; + if ((snd_pcm_uframes_t)n < runtime->silenced_size) + runtime->silenced_size -= n; + else + runtime->silenced_size = 0; + runtime->silenced_start = runtime->control->appl_ptr; + } + if (runtime->silenced_size == runtime->buffer_size) + return; + snd_assert(runtime->silenced_size <= runtime->buffer_size, return); + noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silenced_size; + if (noise_dist > (snd_pcm_sframes_t) runtime->silence_threshold) + return; + frames = runtime->silence_threshold - noise_dist; + if (frames < runtime->silence_size) + frames = runtime->silence_size; + if (runtime->silenced_size + frames > runtime->buffer_size) + frames = runtime->buffer_size - runtime->silenced_size; + ofs = runtime->silenced_start % runtime->buffer_size + runtime->silenced_size; + if (ofs >= runtime->buffer_size) + ofs -= runtime->buffer_size; + if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + if (substream->ops->silence) { + int err; + err = substream->ops->silence(substream, -1, ofs, frames); + snd_assert(err >= 0, ); + } else { + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); + snd_pcm_format_set_silence(runtime->format, hwbuf, frames * runtime->channels); + } + } else { + unsigned int c; + unsigned int channels = runtime->channels; + if (substream->ops->silence) { + for (c = 0; c < channels; ++c) { + int err; + err = substream->ops->silence(substream, c, ofs, frames); + snd_assert(err >= 0, ); + } + } else { + size_t dma_csize = runtime->dma_bytes / channels; + for (c = 0; c < channels; ++c) { + char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); + snd_pcm_format_set_silence(runtime->format, hwbuf, frames); + } + } + } + runtime->silenced_size += frames; +} + +int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t pos; + snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt; + snd_pcm_uframes_t avail; + snd_pcm_sframes_t delta; + + old_hw_ptr = runtime->status->hw_ptr; + pos = substream->ops->pointer(substream); + if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp); +#ifdef CONFIG_SND_DEBUG + if (pos > runtime->buffer_size) { + snd_printk("BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); + } else +#endif + snd_runtime_check(pos <= runtime->buffer_size, return 0); + + pos -= pos % runtime->min_align; + new_hw_ptr = runtime->hw_ptr_base + pos; + + hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; + + delta = hw_ptr_interrupt - new_hw_ptr; + if (delta > 0) { + if (delta < runtime->buffer_size / 2) { + snd_printd("Unexpected hw_pointer value (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); + return 0; + } + runtime->hw_ptr_base += runtime->buffer_size; + if (runtime->hw_ptr_base == runtime->boundary) + runtime->hw_ptr_base = 0; + new_hw_ptr = runtime->hw_ptr_base + pos; + } + runtime->status->hw_ptr = new_hw_ptr; + runtime->hw_ptr_interrupt = new_hw_ptr - (runtime->hw_ptr_base + pos) % runtime->period_size; + +#if 0 + if (hw_ptr_interrupt == runtime->boundary) + hw_ptr_interrupt = 0; + + if (runtime->hw_ptr_interrupt != hw_ptr_interrupt) + snd_printd("Lost interrupt: hw_ptr = %d expected %d\n", runtime->hw_ptr_interrupt, hw_ptr_interrupt); +#endif + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + avail = snd_pcm_playback_avail(runtime); + else + avail = snd_pcm_capture_avail(runtime); + if (avail > runtime->avail_max) + runtime->avail_max = avail; + if (avail >= runtime->stop_threshold) { + snd_pcm_stop(substream, + runtime->status->state == SNDRV_PCM_STATE_DRAINING ? + SNDRV_PCM_STATE_SETUP : SNDRV_PCM_STATE_XRUN); + return -EPIPE; + } + if (avail >= runtime->control->avail_min) + wake_up(&runtime->sleep); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream); + return 0; +} + +/* CAUTION: call it with irq disabled */ +int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t pos; + snd_pcm_uframes_t old_hw_ptr, new_hw_ptr; + snd_pcm_uframes_t avail; + snd_pcm_sframes_t delta; + old_hw_ptr = runtime->status->hw_ptr; + pos = substream->ops->pointer(substream); + if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp); +#ifdef CONFIG_SND_DEBUG + if (pos > runtime->buffer_size) { + snd_printk("BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); + } else +#endif + snd_runtime_check(pos <= runtime->buffer_size, return 0); + + pos -= pos % runtime->min_align; + new_hw_ptr = runtime->hw_ptr_base + pos; + + delta = old_hw_ptr - new_hw_ptr; + if (delta > 0) { + if (delta < runtime->buffer_size / 2) { + snd_printd("Unexpected hw_pointer value (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); + return 0; + } + runtime->hw_ptr_base += runtime->buffer_size; + if (runtime->hw_ptr_base == runtime->boundary) + runtime->hw_ptr_base = 0; + new_hw_ptr = runtime->hw_ptr_base + pos; + } + runtime->status->hw_ptr = new_hw_ptr; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + avail = snd_pcm_playback_avail(runtime); + else + avail = snd_pcm_capture_avail(runtime); + if (avail > runtime->avail_max) + runtime->avail_max = avail; + if (avail >= runtime->stop_threshold) { + snd_pcm_stop(substream, + runtime->status->state == SNDRV_PCM_STATE_DRAINING ? + SNDRV_PCM_STATE_SETUP : SNDRV_PCM_STATE_XRUN); + return -EPIPE; + } + if (avail >= runtime->control->avail_min) + wake_up(&runtime->sleep); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream); + return 0; +} + +/* + * Operations + */ + +void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops) +{ + snd_pcm_str_t *stream = &pcm->streams[direction]; + snd_pcm_substream_t *substream; + + for (substream = stream->substream; substream != NULL; substream = substream->next) + substream->ops = ops; +} + +/* + * Sync + */ + +void snd_pcm_set_sync(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + runtime->sync.id32[0] = substream->pcm->card->number; + runtime->sync.id32[1] = -1; + runtime->sync.id32[2] = -1; + runtime->sync.id32[3] = -1; +} + +/* + * Standard ioctl routine + */ + +/* Code taken from alsa-lib */ +#define assert(a) snd_assert((a), return -EINVAL) + +static inline unsigned int div32(unsigned int a, unsigned int b, + unsigned int *r) +{ + if (b == 0) { + *r = 0; + return UINT_MAX; + } + *r = a % b; + return a / b; +} + +static inline unsigned int div_down(unsigned int a, unsigned int b) +{ + if (b == 0) + return UINT_MAX; + return a / b; +} + +static inline unsigned int div_up(unsigned int a, unsigned int b) +{ + unsigned int r; + unsigned int q; + if (b == 0) + return UINT_MAX; + q = div32(a, b, &r); + if (r) + ++q; + return q; +} + +static inline unsigned int mul(unsigned int a, unsigned int b) +{ + if (a == 0) + return 0; + if (div_down(UINT_MAX, a) < b) + return UINT_MAX; + return a * b; +} + +static inline unsigned int muldiv32(unsigned int a, unsigned int b, + unsigned int c, unsigned int *r) +{ + u_int64_t n = (u_int64_t) a * b; + if (c == 0) { + snd_assert(n > 0, ); + *r = 0; + return UINT_MAX; + } + div64_32(&n, c, r); + if (n >= UINT_MAX) { + *r = 0; + return UINT_MAX; + } + return n; +} + +int snd_interval_refine_min(snd_interval_t *i, unsigned int min, int openmin) +{ + int changed = 0; + assert(!snd_interval_empty(i)); + if (i->min < min) { + i->min = min; + i->openmin = openmin; + changed = 1; + } else if (i->min == min && !i->openmin && openmin) { + i->openmin = 1; + changed = 1; + } + if (i->integer) { + if (i->openmin) { + i->min++; + i->openmin = 0; + } + } + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +int snd_interval_refine_max(snd_interval_t *i, unsigned int max, int openmax) +{ + int changed = 0; + assert(!snd_interval_empty(i)); + if (i->max > max) { + i->max = max; + i->openmax = openmax; + changed = 1; + } else if (i->max == max && !i->openmax && openmax) { + i->openmax = 1; + changed = 1; + } + if (i->integer) { + if (i->openmax) { + i->max--; + i->openmax = 0; + } + } + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +/* r <- v */ +int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v) +{ + int changed = 0; + assert(!snd_interval_empty(i)); + if (i->min < v->min) { + i->min = v->min; + i->openmin = v->openmin; + changed = 1; + } else if (i->min == v->min && !i->openmin && v->openmin) { + i->openmin = 1; + changed = 1; + } + if (i->max > v->max) { + i->max = v->max; + i->openmax = v->openmax; + changed = 1; + } else if (i->max == v->max && !i->openmax && v->openmax) { + i->openmax = 1; + changed = 1; + } + if (!i->integer && v->integer) { + i->integer = 1; + changed = 1; + } + if (i->integer) { + if (i->openmin) { + i->min++; + i->openmin = 0; + } + if (i->openmax) { + i->max--; + i->openmax = 0; + } + } else if (!i->openmin && !i->openmax && i->min == i->max) + i->integer = 1; + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +int snd_interval_refine_first(snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + if (snd_interval_single(i)) + return 0; + i->max = i->min; + i->openmax = i->openmin; + if (i->openmax) + i->max++; + return 1; +} + +int snd_interval_refine_last(snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + if (snd_interval_single(i)) + return 0; + i->min = i->max; + i->openmin = i->openmax; + if (i->openmin) + i->min--; + return 1; +} + +int snd_interval_refine_set(snd_interval_t *i, unsigned int val) +{ + snd_interval_t t; + t.empty = 0; + t.min = t.max = val; + t.openmin = t.openmax = 0; + t.integer = 1; + return snd_interval_refine(i, &t); +} + +void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) +{ + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = mul(a->min, b->min); + c->openmin = (a->openmin || b->openmin); + c->max = mul(a->max, b->max); + c->openmax = (a->openmax || b->openmax); + c->integer = (a->integer && b->integer); +} + +void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = div32(a->min, b->max, &r); + c->openmin = (r || a->openmin || b->openmax); + if (b->min > 0) { + c->max = div32(a->max, b->min, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmin); + } else { + c->max = UINT_MAX; + c->openmax = 0; + } + c->integer = 0; +} + +/* a * b / k */ +void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, + unsigned int k, snd_interval_t *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = muldiv32(a->min, b->min, k, &r); + c->openmin = (r || a->openmin || b->openmin); + c->max = muldiv32(a->max, b->max, k, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmax); + c->integer = 0; +} + +/* a * k / b */ +void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, + const snd_interval_t *b, snd_interval_t *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = muldiv32(a->min, k, b->max, &r); + c->openmin = (r || a->openmin || b->openmax); + if (b->min > 0) { + c->max = muldiv32(a->max, k, b->min, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmin); + } else { + c->max = UINT_MAX; + c->openmax = 0; + } + c->integer = 0; +} + +#undef assert +/* ---- */ + + +int snd_interval_ratnum(snd_interval_t *i, + unsigned int rats_count, ratnum_t *rats, + unsigned int *nump, unsigned int *denp) +{ + unsigned int best_num, best_diff, best_den; + unsigned int k; + snd_interval_t t; + int err; + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num = rats[k].num; + unsigned int den; + unsigned int q = i->min; + int diff; + if (q == 0) + q = 1; + den = div_down(num, q); + if (den < rats[k].den_min) + continue; + if (den > rats[k].den_max) + den = rats[k].den_max; + else { + unsigned int r; + r = (den - rats[k].den_min) % rats[k].den_step; + if (r != 0) + den -= r; + } + diff = num - q * den; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.min = div_down(best_num, best_den); + t.openmin = !!(best_num % best_den); + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num = rats[k].num; + unsigned int den; + unsigned int q = i->max; + int diff; + if (q == 0) { + i->empty = 1; + return -EINVAL; + } + den = div_up(num, q); + if (den > rats[k].den_max) + continue; + if (den < rats[k].den_min) + den = rats[k].den_min; + else { + unsigned int r; + r = (den - rats[k].den_min) % rats[k].den_step; + if (r != 0) + den += rats[k].den_step - r; + } + diff = q * den - num; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.max = div_up(best_num, best_den); + t.openmax = !!(best_num % best_den); + t.integer = 0; + err = snd_interval_refine(i, &t); + if (err < 0) + return err; + + if (snd_interval_single(i)) { + if (nump) + *nump = best_num; + if (denp) + *denp = best_den; + } + return err; +} + +int snd_interval_ratden(snd_interval_t *i, + unsigned int rats_count, ratden_t *rats, + unsigned int *nump, unsigned int *denp) +{ + unsigned int best_num, best_diff, best_den; + unsigned int k; + snd_interval_t t; + int err; + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num; + unsigned int den = rats[k].den; + unsigned int q = i->min; + int diff; + num = mul(q, den); + if (num > rats[k].num_max) + continue; + if (num < rats[k].num_min) + num = rats[k].num_max; + else { + unsigned int r; + r = (num - rats[k].num_min) % rats[k].num_step; + if (r != 0) + num += rats[k].num_step - r; + } + diff = num - q * den; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.min = div_down(best_num, best_den); + t.openmin = !!(best_num % best_den); + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num; + unsigned int den = rats[k].den; + unsigned int q = i->max; + int diff; + num = mul(q, den); + if (num < rats[k].num_min) + continue; + if (num > rats[k].num_max) + num = rats[k].num_max; + else { + unsigned int r; + r = (num - rats[k].num_min) % rats[k].num_step; + if (r != 0) + num -= r; + } + diff = q * den - num; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.max = div_up(best_num, best_den); + t.openmax = !!(best_num % best_den); + t.integer = 0; + err = snd_interval_refine(i, &t); + if (err < 0) + return err; + + if (snd_interval_single(i)) { + if (nump) + *nump = best_num; + if (denp) + *denp = best_den; + } + return err; +} + +int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask) +{ + unsigned int k; + int changed = 0; + for (k = 0; k < count; k++) { + if (mask && !(mask & (1 << k))) + continue; + if (i->min == list[k] && !i->openmin) + goto _l1; + if (i->min < list[k]) { + i->min = list[k]; + i->openmin = 0; + changed = 1; + goto _l1; + } + } + i->empty = 1; + return -EINVAL; + _l1: + for (k = count; k-- > 0;) { + if (mask && !(mask & (1 << k))) + continue; + if (i->max == list[k] && !i->openmax) + goto _l2; + if (i->max > list[k]) { + i->max = list[k]; + i->openmax = 0; + changed = 1; + goto _l2; + } + } + i->empty = 1; + return -EINVAL; + _l2: + if (snd_interval_checkempty(i)) { + i->empty = 1; + return -EINVAL; + } + return changed; +} + +int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step) +{ + unsigned int n; + int changed = 0; + n = (i->min - min) % step; + if (n != 0 || i->openmin) { + i->min += step - n; + changed = 1; + } + n = (i->max - min) % step; + if (n != 0 || i->openmax) { + i->max -= n; + changed = 1; + } + if (snd_interval_checkempty(i)) { + i->empty = 1; + return -EINVAL; + } + return changed; +} + +/* Info constraints helpers */ + +int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond, + int var, + snd_pcm_hw_rule_func_t func, void *private, + int dep, ...) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + snd_pcm_hw_rule_t *c; + unsigned int k; + va_list args; + va_start(args, dep); + if (constrs->rules_num >= constrs->rules_all) { + snd_pcm_hw_rule_t *old = constrs->rules; + if (constrs->rules_all == 0) + constrs->rules_all = 32; + else { + old = constrs->rules; + constrs->rules_all += 10; + } + constrs->rules = snd_kcalloc(constrs->rules_all * sizeof(*c), + GFP_KERNEL); + if (!constrs->rules) + return -ENOMEM; + if (old) { + memcpy(constrs->rules, old, + constrs->rules_num * sizeof(*c)); + kfree(old); + } + } + c = &constrs->rules[constrs->rules_num]; + c->cond = cond; + c->func = func; + c->var = var; + c->private = private; + k = 0; + while (1) { + snd_assert(k < sizeof(c->deps) / sizeof(c->deps[0]), return -EINVAL); + c->deps[k++] = dep; + if (dep < 0) + break; + dep = va_arg(args, int); + } + constrs->rules_num++; + va_end(args); + return 0; +} + +int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + unsigned int mask) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + unsigned int *maskp = constrs_mask(constrs, var); + *maskp &= mask; + if (*maskp == 0) + return -EINVAL; + return 0; +} + +int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + return snd_interval_setinteger(constrs_interval(constrs, var)); +} + +int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + unsigned int min, unsigned int max) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + snd_interval_t t; + t.min = min; + t.max = max; + t.openmin = t.openmax = 0; + t.integer = 0; + return snd_interval_refine(constrs_interval(constrs, var), &t); +} + +static int snd_pcm_hw_rule_list(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hw_constraint_list_t *list = rule->private; + return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); +} + + +int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_list_t *l) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_list, l, + var, -1); +} + +static int snd_pcm_hw_rule_ratnums(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hw_constraint_ratnums_t *r = rule->private; + unsigned int num = 0, den = 0; + int err; + err = snd_interval_ratnum(hw_param_interval(params, rule->var), + r->nrats, r->rats, &num, &den); + if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { + params->rate_num = num; + params->rate_den = den; + } + return err; +} + +int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratnums_t *r) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_ratnums, r, + var, -1); +} + +static int snd_pcm_hw_rule_ratdens(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hw_constraint_ratdens_t *r = rule->private; + unsigned int num = 0, den = 0; + int err = snd_interval_ratden(hw_param_interval(params, rule->var), + r->nrats, r->rats, &num, &den); + if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { + params->rate_num = num; + params->rate_den = den; + } + return err; +} + +int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratdens_t *r) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_ratdens, r, + var, -1); +} + +static int snd_pcm_hw_rule_msbits(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned int l = (unsigned long) rule->private; + unsigned int width = l & 0xffff; + unsigned int msbits = l >> 16; + snd_interval_t *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + if (snd_interval_single(i) && snd_interval_value(i) == width) + params->msbits = msbits; + return 0; +} + +int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, + unsigned int cond, + unsigned int width, + unsigned int msbits) +{ + unsigned long l = (msbits << 16) | width; + return snd_pcm_hw_rule_add(runtime, cond, -1, + snd_pcm_hw_rule_msbits, + (void*) l, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); +} + +static int snd_pcm_hw_rule_step(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned long step = (unsigned long) rule->private; + return snd_interval_step(hw_param_interval(params, rule->var), 0, step); +} + +int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + unsigned long step) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_step, (void *) step, + var, -1); +} + + +/* To use the same code we have in alsa-lib */ +#define snd_pcm_t snd_pcm_substream_t +#define assert(i) snd_assert((i), return -EINVAL) +#ifndef INT_MIN +#define INT_MIN ((int)((unsigned int)INT_MAX+1)) +#endif + +void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) +{ + if (hw_is_mask(var)) { + snd_mask_any(hw_param_mask(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + return; + } + if (hw_is_interval(var)) { + snd_interval_any(hw_param_interval(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + return; + } + snd_BUG(); +} + +int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + _snd_pcm_hw_param_any(params, var); + return snd_pcm_hw_refine(pcm, params); +} + +void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params) +{ + unsigned int k; + memset(params, 0, sizeof(*params)); + for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST; k++) + _snd_pcm_hw_param_any(params, k); + params->info = ~0U; +} + +/* Fill PARAMS with full configuration space boundaries */ +int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + _snd_pcm_hw_params_any(params); + return snd_pcm_hw_refine(pcm, params); +} + +/* Return the value for field PAR if it's fixed in configuration space + defined by PARAMS. Return -EINVAL otherwise +*/ +int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + const snd_mask_t *mask = hw_param_mask_c(params, var); + if (!snd_mask_single(mask)) + return -EINVAL; + if (dir) + *dir = 0; + return snd_mask_value(mask); + } + if (hw_is_interval(var)) { + const snd_interval_t *i = hw_param_interval_c(params, var); + if (!snd_interval_single(i)) + return -EINVAL; + if (dir) + *dir = i->openmin; + return snd_interval_value(i); + } + assert(0); + return -EINVAL; +} + +/* Return the minimum value for field PAR. */ +unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + if (dir) + *dir = 0; + return snd_mask_min(hw_param_mask_c(params, var)); + } + if (hw_is_interval(var)) { + const snd_interval_t *i = hw_param_interval_c(params, var); + if (dir) + *dir = i->openmin; + return snd_interval_min(i); + } + assert(0); + return -EINVAL; +} + +/* Return the maximum value for field PAR. */ +unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + if (dir) + *dir = 0; + return snd_mask_max(hw_param_mask_c(params, var)); + } + if (hw_is_interval(var)) { + const snd_interval_t *i = hw_param_interval_c(params, var); + if (dir) + *dir = - (int) i->openmax; + return snd_interval_max(i); + } + assert(0); + return -EINVAL; +} + +void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + if (hw_is_mask(var)) { + snd_mask_none(hw_param_mask(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } else if (hw_is_interval(var)) { + snd_interval_none(hw_param_interval(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } else { + snd_BUG(); + } +} + +int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed; + assert(hw_is_interval(var)); + changed = snd_interval_setinteger(hw_param_interval(params, var)); + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + non integer values. Reduce configuration space accordingly. + Return -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_param_setinteger(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed = _snd_pcm_hw_param_setinteger(params, var); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return 0; +} + +int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed; + if (hw_is_mask(var)) + changed = snd_mask_refine_first(hw_param_mask(params, var)); + else if (hw_is_interval(var)) + changed = snd_interval_refine_first(hw_param_interval(params, var)); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + + +/* Inside configuration space defined by PARAMS remove from PAR all + values > minimum. Reduce configuration space accordingly. + Return the minimum. +*/ +int snd_pcm_hw_param_first(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + int changed = _snd_pcm_hw_param_first(params, var); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + assert(err >= 0); + } + return snd_pcm_hw_param_value(params, var, dir); +} + +int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed; + if (hw_is_mask(var)) + changed = snd_mask_refine_last(hw_param_mask(params, var)); + else if (hw_is_interval(var)) + changed = snd_interval_refine_last(hw_param_interval(params, var)); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + + +/* Inside configuration space defined by PARAMS remove from PAR all + values < maximum. Reduce configuration space accordingly. + Return the maximum. +*/ +int snd_pcm_hw_param_last(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + int changed = _snd_pcm_hw_param_last(params, var); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + assert(err >= 0); + } + return snd_pcm_hw_param_value(params, var, dir); +} + +int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed; + int open = 0; + if (dir) { + if (dir > 0) { + open = 1; + } else if (dir < 0) { + if (val > 0) { + open = 1; + val--; + } + } + } + if (hw_is_mask(var)) + changed = snd_mask_refine_min(hw_param_mask(params, var), val + !!open); + else if (hw_is_interval(var)) + changed = snd_interval_refine_min(hw_param_interval(params, var), val, open); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + values < VAL. Reduce configuration space accordingly. + Return new minimum or -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int *dir) +{ + int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value_min(params, var, dir); +} + +int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed; + int open = 0; + if (dir) { + if (dir < 0) { + open = 1; + } else if (dir > 0) { + open = 1; + val++; + } + } + if (hw_is_mask(var)) { + if (val == 0 && open) { + snd_mask_none(hw_param_mask(params, var)); + changed = -EINVAL; + } else + changed = snd_mask_refine_max(hw_param_mask(params, var), val - !!open); + } else if (hw_is_interval(var)) + changed = snd_interval_refine_max(hw_param_interval(params, var), val, open); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + values >= VAL + 1. Reduce configuration space accordingly. + Return new maximum or -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int *dir) +{ + int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value_max(params, var, dir); +} + +int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed; + if (hw_is_mask(var)) { + snd_mask_t *m = hw_param_mask(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_mask_none(m); + } else { + if (dir > 0) + val++; + else if (dir < 0) + val--; + changed = snd_mask_refine_set(hw_param_mask(params, var), val); + } + } else if (hw_is_interval(var)) { + snd_interval_t *i = hw_param_interval(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_interval_none(i); + } else if (dir == 0) + changed = snd_interval_refine_set(i, val); + else { + snd_interval_t t; + t.openmin = 1; + t.openmax = 1; + t.empty = 0; + t.integer = 0; + if (dir < 0) { + t.min = val - 1; + t.max = val; + } else { + t.min = val; + t.max = val+1; + } + changed = snd_interval_refine(i, &t); + } + } else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + values != VAL. Reduce configuration space accordingly. + Return VAL or -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed = _snd_pcm_hw_param_set(params, var, val, dir); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value(params, var, 0); +} + +int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, const snd_mask_t *val) +{ + int changed; + assert(hw_is_mask(var)); + changed = snd_mask_refine(hw_param_mask(params, var), val); + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all values + not contained in MASK. Reduce configuration space accordingly. + This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + Return 0 on success or -EINVAL + if the configuration space is empty +*/ +int snd_pcm_hw_param_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, const snd_mask_t *val) +{ + int changed = _snd_pcm_hw_param_mask(params, var, val); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return 0; +} + +static int boundary_sub(int a, int adir, + int b, int bdir, + int *c, int *cdir) +{ + adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0); + bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0); + *c = a - b; + *cdir = adir - bdir; + if (*cdir == -2) { + assert(*c > INT_MIN); + (*c)--; + } else if (*cdir == 2) { + assert(*c < INT_MAX); + (*c)++; + } + return 0; +} + +static int boundary_lt(unsigned int a, int adir, + unsigned int b, int bdir) +{ + assert(a > 0 || adir >= 0); + assert(b > 0 || bdir >= 0); + if (adir < 0) { + a--; + adir = 1; + } else if (adir > 0) + adir = 1; + if (bdir < 0) { + b--; + bdir = 1; + } else if (bdir > 0) + bdir = 1; + return a < b || (a == b && adir < bdir); +} + +/* Return 1 if min is nearer to best than max */ +static int boundary_nearer(int min, int mindir, + int best, int bestdir, + int max, int maxdir) +{ + int dmin, dmindir; + int dmax, dmaxdir; + boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir); + boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir); + return boundary_lt(dmin, dmindir, dmax, dmaxdir); +} + +/* Inside configuration space defined by PARAMS set PAR to the available value + nearest to VAL. Reduce configuration space accordingly. + This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + Return the value found. + */ +int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int best, int *dir) +{ + snd_pcm_hw_params_t save; + int v; + unsigned int saved_min; + int last = 0; + int min, max; + int mindir, maxdir; + int valdir = dir ? *dir : 0; + /* FIXME */ + if (best > INT_MAX) + best = INT_MAX; + min = max = best; + mindir = maxdir = valdir; + if (maxdir > 0) + maxdir = 0; + else if (maxdir == 0) + maxdir = -1; + else { + maxdir = 1; + max--; + } + save = *params; + saved_min = min; + min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir); + if (min >= 0) { + snd_pcm_hw_params_t params1; + if (max < 0) + goto _end; + if ((unsigned int)min == saved_min && mindir == valdir) + goto _end; + params1 = save; + max = snd_pcm_hw_param_max(pcm, ¶ms1, var, max, &maxdir); + if (max < 0) + goto _end; + if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) { + *params = params1; + last = 1; + } + } else { + *params = save; + max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); + assert(max >= 0); + last = 1; + } + _end: + if (last) + v = snd_pcm_hw_param_last(pcm, params, var, dir); + else + v = snd_pcm_hw_param_first(pcm, params, var, dir); + assert(v >= 0); + return v; +} + +/* Choose one configuration from configuration space defined by PARAMS + The configuration choosen is that obtained fixing in this order: + first access + first format + first subformat + min channels + min rate + min period time + max buffer size + min tick time +*/ +int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + int err; + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_ACCESS, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_FORMAT, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_SUBFORMAT, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_CHANNELS, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_RATE, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_last(pcm, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_TICK_TIME, 0); + assert(err >= 0); + + return 0; +} + +#undef snd_pcm_t +#undef assert + +static int snd_pcm_lib_ioctl_reset(snd_pcm_substream_t *substream, + void *arg) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (snd_pcm_running(substream) && + snd_pcm_update_hw_ptr(substream) >= 0) { + runtime->status->hw_ptr %= runtime->buffer_size; + return 0; + } + runtime->status->hw_ptr = 0; + return 0; +} + +static int snd_pcm_lib_ioctl_channel_info(snd_pcm_substream_t *substream, + void *arg) +{ + snd_pcm_channel_info_t *info = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + int width; + if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { + info->offset = -1; + return 0; + } + width = snd_pcm_format_physical_width(runtime->format); + if (width < 0) + return width; + info->offset = 0; + switch (runtime->access) { + case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: + case SNDRV_PCM_ACCESS_RW_INTERLEAVED: + info->first = info->channel * width; + info->step = runtime->channels * width; + break; + case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: + case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: + { + size_t size = runtime->dma_bytes / runtime->channels; + info->first = info->channel * size * 8; + info->step = width; + break; + } + default: + snd_BUG(); + break; + } + return 0; +} + +int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case SNDRV_PCM_IOCTL1_INFO: + return 0; + case SNDRV_PCM_IOCTL1_RESET: + return snd_pcm_lib_ioctl_reset(substream, arg); + case SNDRV_PCM_IOCTL1_CHANNEL_INFO: + return snd_pcm_lib_ioctl_channel_info(substream, arg); + } + return -ENXIO; +} + +/* + * Conditions + */ + +int snd_pcm_playback_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_playback_avail(runtime) >= runtime->control->avail_min; +} + +int snd_pcm_capture_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_capture_avail(runtime) >= runtime->control->avail_min; +} + +int snd_pcm_playback_data(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_playback_avail(runtime) < runtime->buffer_size; +} + +int snd_pcm_playback_empty(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_playback_avail(runtime) >= runtime->buffer_size; +} + +int snd_pcm_capture_empty(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_capture_avail(runtime) == 0; +} + +static void snd_pcm_system_tick_set(snd_pcm_substream_t *substream, + unsigned long ticks) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (ticks == 0) + del_timer(&runtime->tick_timer); + else { + ticks /= (1000000 / HZ); + if (ticks % (1000000 / HZ)) + ticks++; + mod_timer(&runtime->tick_timer, jiffies + ticks); + } +} + +/* Temporary alias */ +void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks) +{ + snd_pcm_system_tick_set(substream, ticks); +} + +void snd_pcm_tick_prepare(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t frames = ULONG_MAX; + snd_pcm_uframes_t avail, dist; + unsigned int ticks; + u_int64_t n; + u_int32_t r; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (runtime->silence_size > 0 && + runtime->silenced_size < runtime->buffer_size) { + snd_pcm_sframes_t noise_dist; + noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silenced_size; + snd_assert(noise_dist <= runtime->silence_threshold, ); + frames = noise_dist - runtime->silence_threshold; + } + avail = snd_pcm_playback_avail(runtime); + } else { + avail = snd_pcm_capture_avail(runtime); + } + if (avail < runtime->control->avail_min) { + snd_pcm_sframes_t n = runtime->control->avail_min - avail; + if (n > 0 && frames > n) + frames = n; + } + if (avail < runtime->buffer_size) { + snd_pcm_sframes_t n = runtime->buffer_size - avail; + if (n > 0 && frames > n) + frames = n; + } + if (frames == ULONG_MAX) { + snd_pcm_tick_set(substream, 0); + return; + } + dist = runtime->status->hw_ptr - runtime->hw_ptr_base; + /* Distance to next interrupt */ + dist = runtime->period_size - dist % runtime->period_size; + if (dist <= frames) { + snd_pcm_tick_set(substream, 0); + return; + } + /* the base time is us */ + n = frames; + n *= 1000000; + div64_32(&n, runtime->tick_time * runtime->rate, &r); + ticks = n + (r > 0 ? 1 : 0); + if (ticks < runtime->sleep_min) + ticks = runtime->sleep_min; + snd_pcm_tick_set(substream, (unsigned long) ticks); +} + +void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + snd_assert(substream != NULL, return); + runtime = substream->runtime; + snd_assert(runtime != NULL, return); + + spin_lock_irq(&runtime->lock); + if (!snd_pcm_running(substream) || + snd_pcm_update_hw_ptr(substream) < 0) + goto _end; + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + _end: + spin_unlock_irq(&runtime->lock); +} + +void snd_pcm_period_elapsed(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + snd_assert(substream != NULL, return); + runtime = substream->runtime; + snd_assert(runtime != NULL, return); + + if (runtime->transfer_ack_begin) + runtime->transfer_ack_begin(substream); + + spin_lock(&runtime->lock); + if (!snd_pcm_running(substream) || + snd_pcm_update_hw_ptr_interrupt(substream) < 0) + goto _end; + + if (substream->timer_running) + snd_timer_interrupt(substream->timer, 1); + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + _end: + spin_unlock(&runtime->lock); + if (runtime->transfer_ack_end) + runtime->transfer_ack_end(substream); + kill_fasync(&runtime->fasync, SIGIO, POLL_IN); +} + +static int snd_pcm_lib_write_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + char *buf = (char *) data + frames_to_bytes(runtime, off); + if (substream->ops->copy) { + if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) + return err; + } else { + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); + snd_assert(runtime->dma_area, return -EFAULT); + if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) + return -EFAULT; + } + return 0; +} + +typedef int (*transfer_f)(snd_pcm_substream_t *substream, unsigned int hwoff, + void *data, unsigned int off, snd_pcm_uframes_t size); + +static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, + const void *data, snd_pcm_uframes_t size, + int nonblock, + transfer_f transfer) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t xfer = 0; + snd_pcm_uframes_t offset = 0; + int err = 0; + + if (size == 0) + return 0; + if (size > runtime->xfer_align) + size -= size % runtime->xfer_align; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PAUSED: + break; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + err = -EBADFD; + goto _end_unlock; + } + + while (size > 0) { + snd_pcm_uframes_t frames, appl_ptr, appl_ofs; + snd_pcm_uframes_t avail; + snd_pcm_uframes_t cont; + if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_update_hw_ptr(substream); + avail = snd_pcm_playback_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_PAUSED || + runtime->status->state == SNDRV_PCM_STATE_PREPARED) { + if (avail < runtime->xfer_align) { + err = -EPIPE; + goto _end_unlock; + } + } else if (((avail < runtime->control->avail_min && size > avail) || + (size >= runtime->xfer_align && avail < runtime->xfer_align))) { + wait_queue_t wait; + enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; + if (nonblock) { + err = -EAGAIN; + goto _end_unlock; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + state = SIGNALED; + break; + } + spin_unlock_irq(&runtime->lock); + if (schedule_timeout(10 * HZ) == 0) { + spin_lock_irq(&runtime->lock); + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + state = ERROR; + goto _end_loop; + case SNDRV_PCM_STATE_SUSPENDED: + state = SUSPENDED; + goto _end_loop; + default: + break; + } + avail = snd_pcm_playback_avail(runtime); + if (avail >= runtime->control->avail_min) { + state = READY; + break; + } + } + _end_loop: + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + + switch (state) { + case ERROR: + err = -EPIPE; + goto _end_unlock; + case SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + case SIGNALED: + err = -ERESTARTSYS; + goto _end_unlock; + case EXPIRED: + snd_printd("playback write error (DMA or IRQ trouble?)\n"); + err = -EIO; + goto _end_unlock; + default: + break; + } + } + if (avail > runtime->xfer_align) + avail -= avail % runtime->xfer_align; + frames = size > avail ? avail : size; + cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; + if (frames > cont) + frames = cont; + if (frames == 0 && runtime->status->state == SNDRV_PCM_STATE_PAUSED) { + err = -EPIPE; + goto _end; + } + snd_assert(frames != 0, + spin_unlock_irq(&runtime->lock); + return -EINVAL); + appl_ptr = runtime->control->appl_ptr; + appl_ofs = appl_ptr % runtime->buffer_size; + spin_unlock_irq(&runtime->lock); + if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0) + goto _end; + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + break; + } + appl_ptr += frames; + if (appl_ptr >= runtime->boundary) { + runtime->control->appl_ptr = 0; + } else { + runtime->control->appl_ptr = appl_ptr; + } + + offset += frames; + size -= frames; + xfer += frames; + if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && + snd_pcm_playback_hw_avail(runtime) >= runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; + } + if (runtime->sleep_min && + runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_tick_prepare(substream); + } + _end_unlock: + spin_unlock_irq(&runtime->lock); + _end: + return xfer > 0 ? xfer : err; +} + +snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream, const void *buf, snd_pcm_uframes_t size) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#ifdef CONFIG_SND_OSSEMUL + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) + return -EINVAL; + return snd_pcm_lib_write1(substream, buf, size, nonblock, + snd_pcm_lib_write_transfer); +} + +static int snd_pcm_lib_writev_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + void **bufs = data; + int channels = runtime->channels; + int c; + if (substream->ops->copy) { + snd_assert(substream->ops->silence != NULL, return -EINVAL); + for (c = 0; c < channels; ++c, ++bufs) { + if (*bufs == NULL) { + if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) + return err; + } else { + char *buf = *bufs + samples_to_bytes(runtime, off); + if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) + return err; + } + } + } else { + /* default transfer behaviour */ + size_t dma_csize = runtime->dma_bytes / channels; + snd_assert(runtime->dma_area, return -EFAULT); + for (c = 0; c < channels; ++c, ++bufs) { + char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); + if (*bufs == NULL) { + snd_pcm_format_set_silence(runtime->format, hwbuf, frames); + } else { + char *buf = *bufs + samples_to_bytes(runtime, off); + if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) + return -EFAULT; + } + } + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream, void **bufs, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#ifdef CONFIG_SND_OSSEMUL + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + return snd_pcm_lib_write1(substream, bufs, frames, nonblock, + snd_pcm_lib_writev_transfer); +} + +static int snd_pcm_lib_read_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + char *buf = (char *) data + frames_to_bytes(runtime, off); + if (substream->ops->copy) { + if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) + return err; + } else { + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); + snd_assert(runtime->dma_area, return -EFAULT); + if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) + return -EFAULT; + } + return 0; +} + +static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, void *data, snd_pcm_uframes_t size, int nonblock, + transfer_f transfer) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t xfer = 0; + snd_pcm_uframes_t offset = 0; + int err = 0; + + if (size == 0) + return 0; + if (size > runtime->xfer_align) + size -= size % runtime->xfer_align; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + if (size >= runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; + } + break; + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PAUSED: + break; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + err = -EBADFD; + goto _end_unlock; + } + + while (size > 0) { + snd_pcm_uframes_t frames, appl_ptr, appl_ofs; + snd_pcm_uframes_t avail; + snd_pcm_uframes_t cont; + if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_update_hw_ptr(substream); + avail = snd_pcm_capture_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) { + if (avail < runtime->xfer_align) { + err = -EPIPE; + goto _end_unlock; + } + } else if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { + if (avail < runtime->xfer_align) { + runtime->status->state = SNDRV_PCM_STATE_SETUP; + err = -EPIPE; + goto _end_unlock; + } + } else if ((avail < runtime->control->avail_min && size > avail) || + (size >= runtime->xfer_align && avail < runtime->xfer_align)) { + wait_queue_t wait; + enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; + if (nonblock) { + err = -EAGAIN; + goto _end_unlock; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + state = SIGNALED; + break; + } + spin_unlock_irq(&runtime->lock); + if (schedule_timeout(10 * HZ) == 0) { + spin_lock_irq(&runtime->lock); + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + state = ERROR; + goto _end_loop; + case SNDRV_PCM_STATE_SUSPENDED: + state = SUSPENDED; + goto _end_loop; + default: + break; + } + avail = snd_pcm_capture_avail(runtime); + if (avail >= runtime->control->avail_min) { + state = READY; + break; + } + } + _end_loop: + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + + switch (state) { + case ERROR: + err = -EPIPE; + goto _end_unlock; + case SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + case SIGNALED: + err = -ERESTARTSYS; + goto _end_unlock; + case EXPIRED: + snd_printd("capture read error (DMA or IRQ trouble?)\n"); + err = -EIO; + goto _end_unlock; + default: + break; + } + } + if (avail > runtime->xfer_align) + avail -= avail % runtime->xfer_align; + frames = size > avail ? avail : size; + cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; + if (frames > cont) + frames = cont; + snd_assert(frames != 0, + spin_unlock_irq(&runtime->lock); + return -EINVAL); + appl_ptr = runtime->control->appl_ptr; + appl_ofs = appl_ptr % runtime->buffer_size; + spin_unlock_irq(&runtime->lock); + if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0) + goto _end; + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + break; + } + appl_ptr += frames; + if (appl_ptr >= runtime->boundary) { + runtime->control->appl_ptr = 0; + } else { + runtime->control->appl_ptr = appl_ptr; + } + + offset += frames; + size -= frames; + xfer += frames; + if (runtime->sleep_min && + runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_tick_prepare(substream); + } + _end_unlock: + spin_unlock_irq(&runtime->lock); + _end: + return xfer > 0 ? xfer : err; +} + +snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream, void *buf, snd_pcm_uframes_t size) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#ifdef CONFIG_SND_OSSEMUL + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) + return -EINVAL; + return snd_pcm_lib_read1(substream, buf, size, nonblock, snd_pcm_lib_read_transfer); +} + +static int snd_pcm_lib_readv_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + void **bufs = data; + int channels = runtime->channels; + int c; + if (substream->ops->copy) { + for (c = 0; c < channels; ++c, ++bufs) { + char *buf; + if (*bufs == NULL) + continue; + buf = *bufs + samples_to_bytes(runtime, off); + if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) + return err; + } + } else { + snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; + snd_assert(runtime->dma_area, return -EFAULT); + for (c = 0; c < channels; ++c, ++bufs) { + char *hwbuf, *buf; + if (*bufs == NULL) + continue; + + hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); + buf = *bufs + samples_to_bytes(runtime, off); + if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) + return -EFAULT; + } + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, void **bufs, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#ifdef CONFIG_SND_OSSEMUL + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + return snd_pcm_lib_read1(substream, bufs, frames, nonblock, snd_pcm_lib_readv_transfer); +} + +/* + * Exported symbols + */ + +EXPORT_SYMBOL(snd_interval_refine); +EXPORT_SYMBOL(snd_interval_list); +EXPORT_SYMBOL(snd_interval_ratnum); +EXPORT_SYMBOL(snd_interval_ratden); +EXPORT_SYMBOL(snd_interval_muldivk); +EXPORT_SYMBOL(snd_interval_mulkdiv); +EXPORT_SYMBOL(snd_interval_div); +EXPORT_SYMBOL(_snd_pcm_hw_params_any); +EXPORT_SYMBOL(_snd_pcm_hw_param_min); +EXPORT_SYMBOL(_snd_pcm_hw_param_set); +EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); +EXPORT_SYMBOL(_snd_pcm_hw_param_setinteger); +EXPORT_SYMBOL(snd_pcm_hw_param_value_min); +EXPORT_SYMBOL(snd_pcm_hw_param_value_max); +EXPORT_SYMBOL(snd_pcm_hw_param_mask); +EXPORT_SYMBOL(snd_pcm_hw_param_first); +EXPORT_SYMBOL(snd_pcm_hw_param_last); +EXPORT_SYMBOL(snd_pcm_hw_param_near); +EXPORT_SYMBOL(snd_pcm_hw_refine); +EXPORT_SYMBOL(snd_pcm_hw_constraints_init); +EXPORT_SYMBOL(snd_pcm_hw_constraints_complete); +EXPORT_SYMBOL(snd_pcm_hw_constraint_list); +EXPORT_SYMBOL(snd_pcm_hw_constraint_step); +EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); +EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); +EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); +EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); +EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); +EXPORT_SYMBOL(snd_pcm_hw_rule_add); +EXPORT_SYMBOL(snd_pcm_set_ops); +EXPORT_SYMBOL(snd_pcm_set_sync); +EXPORT_SYMBOL(snd_pcm_lib_ioctl); +EXPORT_SYMBOL(snd_pcm_playback_ready); +EXPORT_SYMBOL(snd_pcm_capture_ready); +EXPORT_SYMBOL(snd_pcm_playback_data); +EXPORT_SYMBOL(snd_pcm_capture_empty); +EXPORT_SYMBOL(snd_pcm_stop); +EXPORT_SYMBOL(snd_pcm_period_elapsed); +EXPORT_SYMBOL(snd_pcm_lib_write); +EXPORT_SYMBOL(snd_pcm_lib_read); +EXPORT_SYMBOL(snd_pcm_lib_writev); +EXPORT_SYMBOL(snd_pcm_lib_readv); +EXPORT_SYMBOL(snd_pcm_lib_buffer_bytes); +EXPORT_SYMBOL(snd_pcm_lib_period_bytes); +/* pcm_memory.c */ +EXPORT_SYMBOL(snd_pcm_lib_preallocate_free); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); +EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); +EXPORT_SYMBOL(snd_pcm_lib_free_pages); +#ifdef CONFIG_ISA +EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages_for_all); +#endif +#ifdef CONFIG_PCI +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages_for_all); +#endif diff -Nru a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/pcm_memory.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,357 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +static int snd_preallocate_dma = 1; +MODULE_PARM(snd_preallocate_dma, "i"); +MODULE_PARM_DESC(snd_preallocate_dma, "Preallocate DMA memory when the PCM devices are initialized."); +MODULE_PARM_SYNTAX(snd_preallocate_dma, SNDRV_BOOLEAN_TRUE_DESC); + +static int snd_maximum_substreams = 4; +MODULE_PARM(snd_maximum_substreams, "i"); +MODULE_PARM_DESC(snd_maximum_substreams, "Maximum substreams with preallocated DMA memory."); +MODULE_PARM_SYNTAX(snd_maximum_substreams, SNDRV_BOOLEAN_TRUE_DESC); + +static int snd_minimum_buffer = 16384; + + +static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream) +{ + if (substream->dma_area == NULL) + return; + switch (substream->dma_type) { + case SNDRV_PCM_DMA_TYPE_CONTINUOUS: + snd_free_pages(substream->dma_area, substream->dma_bytes); + break; +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + snd_free_isa_pages(substream->dma_bytes, substream->dma_area, substream->dma_addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + snd_free_pci_pages((struct pci_dev *)substream->dma_private, substream->dma_bytes, substream->dma_area, substream->dma_addr); + break; +#endif + } + substream->dma_area = NULL; +} + +int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream) +{ + snd_pcm_lib_preallocate_dma_free(substream); + if (substream->proc_prealloc_entry) { + snd_info_unregister(substream->proc_prealloc_entry); + substream->proc_prealloc_entry = NULL; + } + return 0; +} + +int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm) +{ + snd_pcm_substream_t *substream; + int stream; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + snd_pcm_lib_preallocate_free(substream); + return 0; +} + +static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_bytes / 1024); +} + +static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + char line[64], str[64]; + size_t size; + void *dma_area; + dma_addr_t dma_addr; + + if (substream->runtime) { + buffer->error = -EBUSY; + return; + } + if (!snd_info_get_line(buffer, line, sizeof(line))) { + snd_info_get_str(str, line, sizeof(str)); + size = simple_strtoul(str, NULL, 10) * 1024; + if ((size != 0 && size < 8192) || size > substream->dma_max) { + buffer->error = -EINVAL; + return; + } + if (substream->dma_bytes == size) + return; + if (size > 0) { + switch (substream->dma_type) { + case SNDRV_PCM_DMA_TYPE_CONTINUOUS: + dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff)); + dma_addr = 0UL; /* not valid */ + break; +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + dma_area = snd_malloc_isa_pages(size, &dma_addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, &dma_addr); + break; +#endif + default: + dma_area = NULL; + dma_addr = 0UL; + } + if (dma_area == NULL) { + buffer->error = -ENOMEM; + return; + } + substream->buffer_bytes_max = size; + } else { + dma_area = NULL; + substream->buffer_bytes_max = UINT_MAX; + } + snd_pcm_lib_preallocate_dma_free(substream); + substream->dma_area = dma_area; + substream->dma_addr = dma_addr; + substream->dma_bytes = size; + } else { + buffer->error = -EINVAL; + } +} + +static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + unsigned long rsize = 0; + void *dma_area = NULL; + dma_addr_t dma_addr = 0UL; + snd_info_entry_t *entry; + + if (!size || !snd_preallocate_dma || substream->number >= snd_maximum_substreams) { + size = 0; + } else { + switch (substream->dma_type) { + case SNDRV_PCM_DMA_TYPE_CONTINUOUS: + dma_area = snd_malloc_pages_fallback(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff), &rsize); + dma_addr = 0UL; /* not valid */ + break; +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + dma_area = snd_malloc_isa_pages_fallback(size, &dma_addr, &rsize); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + dma_area = snd_malloc_pci_pages_fallback((struct pci_dev *)substream->dma_private, size, &dma_addr, &rsize); + break; +#endif + default: + size = 0; + } + if (rsize < snd_minimum_buffer) { + snd_pcm_lib_preallocate_dma_free(substream); + size = 0; + } + } + substream->dma_area = dma_area; + substream->dma_addr = dma_addr; + substream->dma_bytes = rsize; + if (rsize > 0) + substream->buffer_bytes_max = rsize; + substream->dma_max = max; + if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) { + entry->c.text.read_size = 64; + entry->c.text.read = snd_pcm_lib_preallocate_proc_read; + entry->c.text.write_size = 64; + entry->c.text.write = snd_pcm_lib_preallocate_proc_write; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_prealloc_entry = entry; + return 0; +} + +int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, + size_t size, size_t max, + unsigned int flags) +{ + substream->dma_type = SNDRV_PCM_DMA_TYPE_CONTINUOUS; + substream->dma_private = (void *)(unsigned long)flags; + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max, + unsigned int flags) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_pages(substream, size, max, flags)) < 0) + return err; + return 0; +} + +#ifdef CONFIG_ISA +int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + substream->dma_type = SNDRV_PCM_DMA_TYPE_ISA; + substream->dma_private = NULL; + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_isa_pages(substream, size, max)) < 0) + return err; + return 0; +} +#endif /* CONFIG_ISA */ + +int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) +{ + snd_pcm_runtime_t *runtime; + void *dma_area = NULL; + dma_addr_t dma_addr = 0UL; + + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + if (runtime->dma_area != NULL) { + /* perphaps, we might free the large DMA memory region + to save some space here, but the actual solution + costs us less time */ + if (runtime->dma_bytes >= size) + return 0; /* ok, do not change */ + snd_pcm_lib_free_pages(substream); + } + if (substream->dma_area != NULL && substream->dma_bytes >= size) { + dma_area = substream->dma_area; + dma_addr = substream->dma_addr; + } else { + switch (substream->dma_type) { + case SNDRV_PCM_DMA_TYPE_CONTINUOUS: + dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff)); + dma_addr = 0UL; /* not valid */ + break; +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + dma_area = snd_malloc_isa_pages(size, &dma_addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, &dma_addr); + break; +#endif + default: + return -ENXIO; + } + } + if (! dma_area) + return -ENOMEM; + runtime->dma_area = dma_area; + runtime->dma_addr = dma_addr; + runtime->dma_bytes = size; + return 1; /* area was changed */ +} + +int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + if (runtime->dma_area == NULL) + return 0; + if (runtime->dma_area != substream->dma_area) { + switch (substream->dma_type) { +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + snd_free_isa_pages(runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + snd_free_pci_pages((struct pci_dev *)substream->dma_private, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + break; +#endif + } + } + runtime->dma_area = NULL; + runtime->dma_addr = 0UL; + runtime->dma_bytes = 0; + return 0; +} + +#ifdef CONFIG_PCI + +int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, + snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + substream->dma_type = SNDRV_PCM_DMA_TYPE_PCI; + substream->dma_private = pci; + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, + snd_pcm_t *pcm, + size_t size, size_t max) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_pci_pages(pci, substream, size, max)) < 0) + return err; + return 0; +} + +#endif /* CONFIG_PCI */ diff -Nru a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/pcm_misc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,442 @@ +/* + * PCM Interface - misc routines + * Copyright (c) 1998 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#define bswap_16 swab16 +#define bswap_32 swab32 +#define bswap_64 swab64 +#define SND_PCM_FORMAT_UNKNOWN (-1) +#define snd_enum_to_int(v) (v) +#define snd_int_to_enum(v) (v) + +int snd_pcm_format_signed(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + return 1; + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + return 0; + default: + return -EINVAL; + } +} + +int snd_pcm_format_unsigned(snd_pcm_format_t format) +{ + int val; + + val = snd_pcm_format_signed(format); + if (val < 0) + return val; + return !val; +} + +int snd_pcm_format_linear(snd_pcm_format_t format) +{ + return snd_pcm_format_signed(format) >= 0; +} + +int snd_pcm_format_little_endian(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + return 1; + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_BE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 0; + default: + return -EINVAL; + } +} + +int snd_pcm_format_big_endian(snd_pcm_format_t format) +{ + int val; + + val = snd_pcm_format_little_endian(format); + if (val < 0) + return val; + return !val; +} + +int snd_pcm_format_cpu_endian(snd_pcm_format_t format) +{ +#ifdef SNDRV_LITTLE_ENDIAN + return snd_pcm_format_little_endian(format); +#else + return snd_pcm_format_big_endian(format); +#endif +} + +int snd_pcm_format_width(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return 8; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return 16; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + return 24; + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + return 32; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return 64; + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 24; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return 8; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + return 4; + default: + return -EINVAL; + } +} + +int snd_pcm_format_physical_width(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return 8; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return 16; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 32; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return 64; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return 8; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + return 4; + default: + return -EINVAL; + } +} + +ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return samples; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return samples * 2; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + return samples * 4; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return samples * 8; + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return samples * 4; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return samples; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + if (samples & 1) + return -EINVAL; + return samples / 2; + default: + return -EINVAL; + } +} + +u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + return 0; + case SNDRV_PCM_FORMAT_U8: + return 0x8080808080808080ULL; +#ifdef SNDRV_LITTLE_ENDIAN + case SNDRV_PCM_FORMAT_U16_LE: + return 0x8000800080008000ULL; + case SNDRV_PCM_FORMAT_U24_LE: + return 0x0080000000800000ULL; + case SNDRV_PCM_FORMAT_U32_LE: + return 0x8000000080000000ULL; + case SNDRV_PCM_FORMAT_U16_BE: + return 0x0080008000800080ULL; + case SNDRV_PCM_FORMAT_U24_BE: + return 0x0000800000008000ULL; + case SNDRV_PCM_FORMAT_U32_BE: + return 0x0000008000000080ULL; +#else + case SNDRV_PCM_FORMAT_U16_LE: + return 0x0080008000800080ULL; + case SNDRV_PCM_FORMAT_U24_LE: + return 0x0000800000008000ULL; + case SNDRV_PCM_FORMAT_U32_LE: + return 0x0000008000000080ULL; + case SNDRV_PCM_FORMAT_U16_BE: + return 0x8000800080008000ULL; + case SNDRV_PCM_FORMAT_U24_BE: + return 0x0080000000800000ULL; + case SNDRV_PCM_FORMAT_U32_BE: + return 0x8000000080000000ULL; +#endif + case SNDRV_PCM_FORMAT_FLOAT_LE: + { + union { + float f; + u_int32_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return u.i; +#else + return bswap_32(u.i); +#endif + } + case SNDRV_PCM_FORMAT_FLOAT64_LE: + { + union { + double f; + u_int64_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return u.i; +#else + return bswap_64(u.i); +#endif + } + case SNDRV_PCM_FORMAT_FLOAT_BE: + { + union { + float f; + u_int32_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return bswap_32(u.i); +#else + return u.i; +#endif + } + case SNDRV_PCM_FORMAT_FLOAT64_BE: + { + union { + double f; + u_int64_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return bswap_64(u.i); +#else + return u.i; +#endif + } + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 0; + case SNDRV_PCM_FORMAT_MU_LAW: + return 0x7f7f7f7f7f7f7f7fULL; + case SNDRV_PCM_FORMAT_A_LAW: + return 0x5555555555555555ULL; + case SNDRV_PCM_FORMAT_IMA_ADPCM: /* special case */ + case SNDRV_PCM_FORMAT_MPEG: + case SNDRV_PCM_FORMAT_GSM: + case SNDRV_PCM_FORMAT_SPECIAL: + return 0; + default: + return -EINVAL; + } + return 0; +} + +u_int32_t snd_pcm_format_silence_32(snd_pcm_format_t format) +{ + return (u_int32_t)snd_pcm_format_silence_64(format); +} + +u_int16_t snd_pcm_format_silence_16(snd_pcm_format_t format) +{ + return (u_int16_t)snd_pcm_format_silence_64(format); +} + +u_int8_t snd_pcm_format_silence(snd_pcm_format_t format) +{ + return (u_int8_t)snd_pcm_format_silence_64(format); +} + +int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples) +{ + if (samples == 0) + return 0; + switch (snd_pcm_format_width(format)) { + case 4: { + u_int8_t silence = snd_pcm_format_silence_64(format); + unsigned int samples1; + if (samples % 2 != 0) + return -EINVAL; + samples1 = samples / 2; + memset(data, silence, samples1); + break; + } + case 8: { + u_int8_t silence = snd_pcm_format_silence_64(format); + memset(data, silence, samples); + break; + } + case 16: { + u_int16_t silence = snd_pcm_format_silence_64(format); + while (samples-- > 0) + *((u_int16_t *)data)++ = silence; + break; + } + case 32: { + u_int32_t silence = snd_pcm_format_silence_64(format); + while (samples-- > 0) + *((u_int32_t *)data)++ = silence; + break; + } + case 64: { + u_int64_t silence = snd_pcm_format_silence_64(format); + while (samples-- > 0) + *((u_int64_t *)data)++ = silence; + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int linear_formats[4*2*2] = { + SNDRV_PCM_FORMAT_S8, + SNDRV_PCM_FORMAT_S8, + SNDRV_PCM_FORMAT_U8, + SNDRV_PCM_FORMAT_U8, + SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_LE, + SNDRV_PCM_FORMAT_S24_BE, + SNDRV_PCM_FORMAT_U24_LE, + SNDRV_PCM_FORMAT_U24_BE, + SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_U32_BE +}; + +snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian) +{ + switch (width) { + case 8: + width = 0; + break; + case 16: + width = 1; + break; + case 24: + width = 2; + break; + case 32: + width = 3; + break; + default: + return SND_PCM_FORMAT_UNKNOWN; + } + return snd_int_to_enum(((int(*)[2][2])linear_formats)[width][!!unsignd][!!big_endian]); +} diff -Nru a/sound/core/pcm_native.c b/sound/core/pcm_native.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/pcm_native.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2726 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +spinlock_t pcm_link_lock = SPIN_LOCK_UNLOCKED; + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info) +{ + snd_pcm_runtime_t * runtime; + snd_pcm_t *pcm = substream->pcm; + snd_pcm_str_t *pstr = substream->pstr; + + snd_assert(substream != NULL, return -ENXIO); + memset(info, 0, sizeof(*info)); + info->card = pcm->card->number; + info->device = pcm->device; + info->stream = substream->stream; + info->subdevice = substream->number; + strncpy(info->id, pcm->id, sizeof(info->id)-1); + strncpy(info->name, pcm->name, sizeof(info->name)-1); + info->dev_class = pcm->dev_class; + info->dev_subclass = pcm->dev_subclass; + info->subdevices_count = pstr->substream_count; + info->subdevices_avail = pstr->substream_count - pstr->substream_opened; + strncpy(info->subname, substream->name, sizeof(info->subname)-1); + runtime = substream->runtime; + /* AB: FIXME!!! This is definitely nonsense */ + if (runtime) { + info->sync = runtime->sync; + substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); + } + return 0; +} + +int snd_pcm_info_user(snd_pcm_substream_t * substream, snd_pcm_info_t * _info) +{ + snd_pcm_info_t info; + int err = snd_pcm_info(substream, &info); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return err; +} + +#undef RULES_DEBUG + +#ifdef RULES_DEBUG +#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v +char *snd_pcm_hw_param_names[] = { + HW_PARAM(ACCESS), + HW_PARAM(FORMAT), + HW_PARAM(SUBFORMAT), + HW_PARAM(SAMPLE_BITS), + HW_PARAM(FRAME_BITS), + HW_PARAM(CHANNELS), + HW_PARAM(RATE), + HW_PARAM(PERIOD_TIME), + HW_PARAM(PERIOD_SIZE), + HW_PARAM(PERIOD_BYTES), + HW_PARAM(PERIODS), + HW_PARAM(BUFFER_TIME), + HW_PARAM(BUFFER_SIZE), + HW_PARAM(BUFFER_BYTES), + HW_PARAM(TICK_TIME), +}; +#endif + +int snd_pcm_hw_refine(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned int k; + snd_pcm_hardware_t *hw; + snd_interval_t *i = NULL; + snd_mask_t *m = NULL; + snd_pcm_hw_constraints_t *constrs = &substream->runtime->hw_constraints; + unsigned int rstamps[constrs->rules_num]; + unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST + 1]; + unsigned int stamp = 2; + int changed, again; + + params->info = 0; + params->fifo_size = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) + params->msbits = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { + params->rate_num = 0; + params->rate_den = 0; + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { + m = hw_param_mask(params, k); + if (snd_mask_empty(m)) + return -EINVAL; + if (!(params->rmask & (1 << k))) + continue; +#ifdef RULES_DEBUG + printk("%s = ", snd_pcm_hw_param_names[k]); + printk("%x -> ", *m); +#endif + changed = snd_mask_refine(m, constrs_mask(constrs, k)); +#ifdef RULES_DEBUG + printk("%x\n", *m); +#endif + if (changed) + params->cmask |= 1 << k; + if (changed < 0) + return changed; + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { + i = hw_param_interval(params, k); + if (snd_interval_empty(i)) + return -EINVAL; + if (!(params->rmask & (1 << k))) + continue; +#ifdef RULES_DEBUG + printk("%s = ", snd_pcm_hw_param_names[k]); + if (i->empty) + printk("empty"); + else + printk("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + printk(" -> "); +#endif + changed = snd_interval_refine(i, constrs_interval(constrs, k)); +#ifdef RULES_DEBUG + if (i->empty) + printk("empty\n"); + else + printk("%c%u %u%c\n", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); +#endif + if (changed) + params->cmask |= 1 << k; + if (changed < 0) + return changed; + } + + for (k = 0; k < constrs->rules_num; k++) + rstamps[k] = 0; + for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST; k++) + vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; + do { + again = 0; + for (k = 0; k < constrs->rules_num; k++) { + snd_pcm_hw_rule_t *r = &constrs->rules[k]; + unsigned int d; + int doit = 0; + if (r->cond && !(r->cond & params->flags)) + continue; + for (d = 0; r->deps[d] >= 0; d++) { + if (vstamps[r->deps[d]] > rstamps[k]) { + doit = 1; + break; + } + } + if (!doit) + continue; +#ifdef RULES_DEBUG + printk("Rule %d [%p]: ", k, r->func); + if (r->var >= 0) { + printk("%s = ", snd_pcm_hw_param_names[r->var]); + if (hw_is_mask(r->var)) { + m = hw_param_mask(params, r->var); + printk("%x", *m); + } else { + i = hw_param_interval(params, r->var); + if (i->empty) + printk("empty"); + else + printk("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + } + } +#endif + changed = r->func(params, r); +#ifdef RULES_DEBUG + if (r->var >= 0) { + printk(" -> "); + if (hw_is_mask(r->var)) + printk("%x", *m); + else { + if (i->empty) + printk("empty"); + else + printk("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + } + } + printk("\n"); +#endif + rstamps[k] = stamp; + if (changed && r->var >= 0) { + params->cmask |= (1 << r->var); + vstamps[r->var] = stamp; + again = 1; + } + if (changed < 0) + return changed; + stamp++; + } + } while (again); + if (!params->msbits) { + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + if (snd_interval_single(i)) + params->msbits = snd_interval_value(i); + } + + if (!params->rate_den) { + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (snd_interval_single(i)) { + params->rate_num = snd_interval_value(i); + params->rate_den = 1; + } + } + + hw = &substream->runtime->hw; + if (!params->info) + params->info = hw->info; + if (!params->fifo_size) + params->fifo_size = hw->fifo_size; + params->rmask = 0; + return 0; +} + +static int snd_pcm_hw_refine_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * _params) +{ + snd_pcm_hw_params_t params; + int err; + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + err = snd_pcm_hw_refine(substream, ¶ms); + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +static int snd_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + snd_pcm_runtime_t *runtime; + int err; + unsigned int bits; + snd_pcm_uframes_t frames; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + break; + default: + return -EBADFD; + } +#ifdef CONFIG_SND_OSSEMUL + if (!substream->oss.oss) +#endif + if (atomic_read(&runtime->mmap_count)) + return -EBADFD; + + params->rmask = ~0U; + err = snd_pcm_hw_refine(substream, params); + if (err < 0) + goto _error; + + err = snd_pcm_hw_params_choose(substream, params); + if (err < 0) + goto _error; + + if (substream->ops->hw_params != NULL) { + err = substream->ops->hw_params(substream, params); + if (err < 0) + goto _error; + } + + runtime->access = params_access(params); + runtime->format = params_format(params); + runtime->subformat = params_subformat(params); + runtime->channels = params_channels(params); + runtime->rate = params_rate(params); + runtime->period_size = params_period_size(params); + runtime->periods = params_periods(params); + runtime->buffer_size = params_buffer_size(params); + runtime->tick_time = params_tick_time(params); + runtime->info = params->info; + runtime->rate_num = params->rate_num; + runtime->rate_den = params->rate_den; + + bits = snd_pcm_format_physical_width(runtime->format); + runtime->sample_bits = bits; + bits *= runtime->channels; + runtime->frame_bits = bits; + frames = 1; + while (bits % 8 != 0) { + bits *= 2; + frames *= 2; + } + runtime->byte_align = bits / 8; + runtime->min_align = frames; + + /* Default sw params */ + runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; + runtime->period_step = 1; + runtime->sleep_min = 0; + runtime->control->avail_min = runtime->period_size; + runtime->xfer_align = runtime->period_size; + runtime->start_threshold = 1; + runtime->stop_threshold = runtime->buffer_size; + runtime->silence_threshold = 0; + runtime->silence_size = 0; + runtime->boundary = runtime->buffer_size; + while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) + runtime->boundary *= 2; + + snd_pcm_timer_resolution_change(substream); + runtime->status->state = SNDRV_PCM_STATE_SETUP; + return 0; + _error: + /* hardware might be unuseable from this time, + so we force application to retry to set + the correct hardware parameter settings */ + runtime->status->state = SNDRV_PCM_STATE_OPEN; + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + return err; +} + +static int snd_pcm_hw_params_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * _params) +{ + snd_pcm_hw_params_t params; + int err; + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + err = snd_pcm_hw_params(substream, ¶ms); + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +static int snd_pcm_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime; + int result; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + break; + default: + return -EBADFD; + } + if (atomic_read(&runtime->mmap_count)) + return -EBADFD; + if (substream->ops->hw_free == NULL) + return 0; + result = substream->ops->hw_free(substream); + runtime->status->state = SNDRV_PCM_STATE_OPEN; + return result; +} + +static int snd_pcm_sw_params(snd_pcm_substream_t * substream, snd_pcm_sw_params_t *params) +{ + snd_pcm_runtime_t *runtime; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) + return -EINVAL; + if (params->avail_min == 0) + return -EINVAL; + if (params->xfer_align == 0 || + params->xfer_align % runtime->min_align != 0) + return -EINVAL; + if (params->silence_threshold + params->silence_size > runtime->buffer_size) + return -EINVAL; + runtime->tstamp_mode = params->tstamp_mode; + runtime->sleep_min = params->sleep_min; + runtime->period_step = params->period_step; + runtime->control->avail_min = params->avail_min; + runtime->start_threshold = params->start_threshold; + runtime->stop_threshold = params->stop_threshold; + runtime->silence_threshold = params->silence_threshold; + runtime->silence_size = params->silence_size; + runtime->xfer_align = params->xfer_align; + params->boundary = runtime->boundary; + spin_lock_irq(&runtime->lock); + if (snd_pcm_running(substream)) { + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + else + snd_pcm_tick_set(substream, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream); + wake_up(&runtime->sleep); + } + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_pcm_sw_params_user(snd_pcm_substream_t * substream, snd_pcm_sw_params_t * _params) +{ + snd_pcm_sw_params_t params; + int err; + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + err = snd_pcm_sw_params(substream, ¶ms); + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +int snd_pcm_status(snd_pcm_substream_t *substream, + snd_pcm_status_t *status) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irq(&runtime->lock); + status->state = runtime->status->state; + status->suspended_state = runtime->status->suspended_state; + if (status->state == SNDRV_PCM_STATE_OPEN) + goto _end; + status->trigger_tstamp = runtime->trigger_tstamp; + if (snd_pcm_running(substream)) { + snd_pcm_update_hw_ptr(substream); + if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + status->tstamp = runtime->status->tstamp; + else + snd_timestamp_now(&status->tstamp); + } else + snd_timestamp_now(&status->tstamp); + status->appl_ptr = runtime->control->appl_ptr; + status->hw_ptr = runtime->status->hw_ptr; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + status->avail = snd_pcm_playback_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING || + runtime->status->state == SNDRV_PCM_STATE_DRAINING) + status->delay = runtime->buffer_size - status->avail; + } else { + status->avail = snd_pcm_capture_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) + status->delay = status->avail; + } + status->avail_max = runtime->avail_max; + status->overrange = runtime->overrange; + runtime->avail_max = 0; + runtime->overrange = 0; + _end: + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_pcm_status_user(snd_pcm_substream_t * substream, snd_pcm_status_t * _status) +{ + snd_pcm_status_t status; + snd_pcm_runtime_t *runtime; + int res; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + memset(&status, 0, sizeof(status)); + res = snd_pcm_status(substream, &status); + if (res < 0) + return res; + if (copy_to_user(_status, &status, sizeof(status))) + return -EFAULT; + return 0; +} + +static int snd_pcm_channel_info(snd_pcm_substream_t * substream, snd_pcm_channel_info_t * _info) +{ + snd_pcm_channel_info_t info; + snd_pcm_runtime_t *runtime; + int res; + unsigned int channel; + + snd_assert(substream != NULL, return -ENXIO); + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + channel = info.channel; + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (channel >= runtime->channels) + return -EINVAL; + memset(&info, 0, sizeof(info)); + info.channel = channel; + res = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, &info); + if (res < 0) + return res; + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static void snd_pcm_trigger_time(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->trigger_master == NULL) + return; + if (runtime->trigger_master == substream) { + snd_timestamp_now(&runtime->trigger_tstamp); + } else { + snd_pcm_trigger_time(runtime->trigger_master); + runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; + } + runtime->trigger_master = NULL; +} + +#define _SND_PCM_ACTION(aname, substream, state, res, check_master) { \ + snd_pcm_substream_t *s; \ + res = 0; \ + spin_lock(&pcm_link_lock); \ + s = substream; \ + do { \ + if (s != substream) \ + spin_lock(&s->runtime->lock); \ + res = snd_pcm_pre_##aname(s, state); \ + if (res < 0) \ + break; \ + s = s->link_next; \ + } while (s != substream); \ + if (res < 0) { \ + /* Clean all spin_lock */ \ + while (s != substream) { \ + spin_unlock(&s->runtime->lock); \ + s = s->link_prev; \ + } \ + goto _end; \ + } \ + s = substream; \ + do { \ + snd_pcm_runtime_t *runtime = s->runtime; \ + int err; \ + if (check_master && runtime->trigger_master != s) \ + goto _done; \ + err = snd_pcm_do_##aname(s, state); \ + if (err < 0) { \ + if (res == 0) \ + res = err; \ + } else { \ + _done: \ + snd_pcm_post_##aname(s, state); \ + } \ + if (s != substream) \ + spin_unlock(&runtime->lock); \ + s = s->link_next; \ + } while (s != substream); \ + _end: \ + spin_unlock(&pcm_link_lock); \ +} + +#define SND_PCM_ACTION(aname, substream, state) { \ + int res; \ + _SND_PCM_ACTION(aname, substream, state, res, 1); \ + return res; \ +} + +static inline int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->state != SNDRV_PCM_STATE_PREPARED) + return -EBADFD; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !snd_pcm_playback_data(substream)) + return -EPIPE; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_start(snd_pcm_substream_t *substream, int state) +{ + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); +} + +static inline void snd_pcm_post_start(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_time(substream); + runtime->status->state = SNDRV_PCM_STATE_RUNNING; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream); + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); +} + +int snd_pcm_start(snd_pcm_substream_t *substream) +{ + SND_PCM_ACTION(start, substream, 0); +} + +static inline int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (!snd_pcm_running(substream)) + return -EBADFD; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state) +{ + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); +} + +static inline void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_time(substream); + runtime->status->state = state; + snd_pcm_tick_set(substream, 0); + wake_up(&runtime->sleep); +} + +int snd_pcm_stop(snd_pcm_substream_t *substream, int state) +{ + SND_PCM_ACTION(stop, substream, state); +} + +static inline int snd_pcm_pre_pause(snd_pcm_substream_t *substream, int push) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (!(runtime->info & SNDRV_PCM_INFO_PAUSE)) + return -ENOSYS; + if (push) { + if (runtime->status->state != SNDRV_PCM_STATE_RUNNING) + return -EBADFD; + } else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED) + return -EBADFD; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push) +{ + return substream->ops->trigger(substream, + push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH : + SNDRV_PCM_TRIGGER_PAUSE_RELEASE); +} + +static inline void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_time(substream); + if (push) { + runtime->status->state = SNDRV_PCM_STATE_PAUSED; + snd_pcm_tick_set(substream, 0); + wake_up(&runtime->sleep); + } else { + runtime->status->state = SNDRV_PCM_STATE_RUNNING; + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + } +} + +static int snd_pcm_pause(snd_pcm_substream_t *substream, int push) +{ + SND_PCM_ACTION(pause, substream, push); +} + +#ifdef CONFIG_PM +/* suspend */ + +static inline int snd_pcm_pre_suspend(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) + return -EBUSY; + runtime->status->suspended_state = runtime->status->state; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING) + return 0; + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND); +} + +static inline void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_time(substream); + runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; + snd_pcm_tick_set(substream, 0); + wake_up(&runtime->sleep); +} + +int snd_pcm_suspend(snd_pcm_substream_t *substream) +{ + SND_PCM_ACTION(suspend, substream, 0); +} + +int snd_pcm_suspend_all(snd_pcm_t *pcm) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + int stream, err; + + for (stream = 0; stream < 2; stream++) { + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) { + /* FIXME: the open/close code should lock this as well */ + if ((runtime = substream->runtime) == NULL) + continue; + spin_lock(&runtime->lock); + if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + spin_unlock(&runtime->lock); + continue; + } + if ((err = snd_pcm_suspend(substream)) < 0) { + spin_unlock(&runtime->lock); + return err; + } + spin_unlock(&runtime->lock); + } + } + return 0; +} + +/* resume */ + +static inline int snd_pcm_pre_resume(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (!(runtime->info & SNDRV_PCM_INFO_RESUME)) + return -ENOSYS; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING) + return 0; + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME); +} + +static inline void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_time(substream); + runtime->status->state = runtime->status->suspended_state; + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); +} + +static int snd_pcm_resume(snd_pcm_substream_t *substream) +{ + snd_card_t *card = substream->pcm->card; + int res; + + snd_power_lock(card); + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _power_unlock; + } + snd_power_wait(card); + } + + _SND_PCM_ACTION(resume, substream, 0, res, 1); + + _power_unlock: + snd_power_unlock(card); + return res; +} + +#else + +static int snd_pcm_resume(snd_pcm_substream_t *substream) +{ + return -ENOSYS; +} + +#endif /* CONFIG_PM */ + +static inline int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: + case SNDRV_PCM_STATE_SUSPENDED: + return 0; + default: + return -EBADFD; + } +} + +static inline int snd_pcm_do_reset(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, 0); + if (err < 0) + return err; + snd_assert(runtime->status->hw_ptr < runtime->buffer_size, ); + runtime->hw_ptr_base = 0; + runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size; + return 0; +} + +static inline void snd_pcm_post_reset(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + runtime->control->appl_ptr = runtime->status->hw_ptr; + +} + +static int snd_pcm_reset(snd_pcm_substream_t *substream) +{ + int res; + _SND_PCM_ACTION(reset, substream, 0, res, 0); + return res; +} + +static inline int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + return -EBADFD; + case SNDRV_PCM_STATE_RUNNING: + return -EBUSY; + default: + return 0; + } +} + +static inline int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state) +{ + int err; + err = substream->ops->prepare(substream); + if (err < 0) + return err; + return snd_pcm_do_reset(substream, 0); +} + +static inline void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + runtime->control->appl_ptr = runtime->status->hw_ptr; + runtime->status->state = SNDRV_PCM_STATE_PREPARED; +} + +int snd_pcm_prepare(snd_pcm_substream_t *substream) +{ + int res; + snd_card_t *card = substream->pcm->card; + snd_power_lock(card); + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _power_unlock; + } + snd_power_wait(card); + } + + _SND_PCM_ACTION(prepare, substream, 0, res, 0); + + _power_unlock: + snd_power_unlock(card); + return res; +} + +static void snd_pcm_change_state(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_substream_t *s; + spin_lock(&pcm_link_lock); + s = substream->link_next; + while (s != substream) { + spin_lock(&s->runtime->lock); + s = s->link_next; + } + s = substream; + do { + snd_pcm_runtime_t *runtime = s->runtime; + runtime->status->state = state; + if (s != substream) + spin_unlock(&runtime->lock); + s = s->link_next; + } while (s != substream); + spin_unlock(&pcm_link_lock); +} + +static int snd_pcm_playback_drop(snd_pcm_substream_t *substream); + +static int snd_pcm_playback_drain(snd_pcm_substream_t * substream) +{ + snd_card_t *card; + snd_pcm_runtime_t *runtime; + int err, result = 0; + wait_queue_t wait; + enum { READY, EXPIRED, SUSPENDED, SIGNALED } state = READY; + + snd_assert(substream != NULL, return -ENXIO); + snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); + runtime = substream->runtime; + card = substream->pcm->card; + + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + result = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + case SNDRV_PCM_STATE_OPEN: + result = -EBADFD; + goto _end; + case SNDRV_PCM_STATE_PREPARED: + if (!snd_pcm_playback_empty(substream)) { + err = snd_pcm_start(substream); + if (err < 0) { + result = err; + goto _end; + } + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + _xrun_recovery: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + /* Fall through */ + case SNDRV_PCM_STATE_SETUP: + goto _end; + } + + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) { + if (snd_pcm_playback_empty(substream)) { + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + goto _end; + } + snd_pcm_change_state(substream, SNDRV_PCM_STATE_DRAINING); + } + + if (substream->ffile->f_flags & O_NONBLOCK) { + result = -EAGAIN; + goto _end; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + state = SIGNALED; + break; + } + spin_unlock_irq(&runtime->lock); + if (schedule_timeout(10 * HZ) == 0) { + spin_lock_irq(&runtime->lock); + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } + spin_lock_irq(&runtime->lock); + if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) { + state = READY; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + + switch (state) { + case SIGNALED: + result = -ERESTARTSYS; + goto _end; + case SUSPENDED: + result = -ESTRPIPE; + goto _end; + case EXPIRED: + snd_printd("playback drain error (DMA or IRQ trouble?)\n"); + result = -EIO; + goto _end; + default: + break; + } + + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + if (state == EXPIRED) + snd_pcm_playback_drop(substream); + + return result; +} + +static int snd_pcm_playback_drop(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_t *card = substream->pcm->card; + int res = 0; + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + res = -EBADFD; + break; + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + if (snd_pcm_update_hw_ptr(substream) >= 0) { + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_XRUN: + _xrun_recovery: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + } + runtime->control->appl_ptr = runtime->status->hw_ptr; + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return res; +} + +static int snd_pcm_capture_drain(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_t *card = substream->pcm->card; + int res = 0; + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + res = -EBADFD; + break; + case SNDRV_PCM_STATE_PREPARED: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_DRAINING: + break; + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) { + snd_pcm_stop(substream, + snd_pcm_capture_avail(runtime) > 0 ? + SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP); + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + _xrun_recovery: + snd_pcm_change_state(substream, + snd_pcm_capture_avail(runtime) > 0 ? + SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + } + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return res; +} + +static int snd_pcm_capture_drop(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_t *card = substream->pcm->card; + int res = 0; + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + res = -EBADFD; + break; + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + /* Fall through */ + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_XRUN: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + break; + } + runtime->control->appl_ptr = runtime->status->hw_ptr; + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return res; +} + +/* WARNING: Don't forget to fput back the file */ +static struct file *snd_pcm_file_fd(int fd) +{ + struct file *file; + struct inode *inode; + unsigned short minor; + file = fget(fd); + if (!file) + return 0; + inode = file->f_dentry->d_inode; + if (!S_ISCHR(inode->i_mode) || + major(inode->i_rdev) != CONFIG_SND_MAJOR) { + fput(file); + return 0; + } + minor = minor(inode->i_rdev); + if (minor >= 256 || + minor % SNDRV_MINOR_DEVICES < SNDRV_MINOR_PCM_PLAYBACK) { + fput(file); + return 0; + } + return file; +} + +static int snd_pcm_link(snd_pcm_substream_t *substream, int fd) +{ + int res = 0; + struct file *file; + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *s, *substream1; + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + file = snd_pcm_file_fd(fd); + if (!file) + return -EBADFD; + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream1 = pcm_file->substream; + spin_lock_irq(&pcm_link_lock); + if (substream->runtime->status->state != substream1->runtime->status->state) { + res = -EBADFD; + goto _end; + } + s = substream; + do { + if (s == substream1) { + res = -EALREADY; + goto _end; + } + s = s->link_next; + } while (s != substream); + substream1->link_prev->link_next = substream->link_next; + substream->link_next->link_prev = substream1->link_prev; + substream->link_next = substream1; + substream1->link_prev = substream; + _end: + spin_unlock_irq(&pcm_link_lock); + fput(file); + return res; +} + +static int snd_pcm_unlink(snd_pcm_substream_t *substream) +{ + spin_lock_irq(&pcm_link_lock); + substream->link_prev->link_next = substream->link_next; + substream->link_next->link_prev = substream->link_prev; + substream->link_prev = substream; + substream->link_next = substream; + spin_unlock_irq(&pcm_link_lock); + return 0; +} + + +static int snd_pcm_hw_rule_mul(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_mul(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_div(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_div(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_muldivk(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), + (unsigned long) rule->private, &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_mulkdiv(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]), + (unsigned long) rule->private, + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_format(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned int k; + snd_interval_t *i = hw_param_interval(params, rule->deps[0]); + unsigned int m = ~0U; + unsigned int *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { + int bits; + if (!(*mask & (1U << k))) + continue; + bits = snd_pcm_format_physical_width(k); + snd_assert(bits > 0, continue); + if ((unsigned)bits < i->min || (unsigned)bits > i->max) + m &= ~(1U << k); + } + return snd_mask_refine(mask, &m); +} + +static int snd_pcm_hw_rule_sample_bits(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + unsigned int k; + t.min = UINT_MAX; + t.max = 0; + t.openmin = 0; + t.openmax = 0; + for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { + int bits; + if (!(*hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT) & (1U << k))) + continue; + bits = snd_pcm_format_physical_width(k); + snd_assert(bits > 0, continue); + if (t.min > (unsigned)bits) + t.min = bits; + if (t.max < (unsigned)bits) + t.max = bits; + } + t.integer = 1; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 +#error "Change this table" +#endif + +static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, + 48000, 64000, 88200, 96000, 176400, 192000 }; + +#define RATES (sizeof(rates) / sizeof(rates[0])) + +static int snd_pcm_hw_rule_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hardware_t *hw = rule->private; + return snd_interval_list(hw_param_interval(params, rule->var), RATES, rates, hw->rates); +} + +static int snd_pcm_hw_rule_buffer_bytes_max(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_pcm_substream_t *substream = rule->private; + t.min = 0; + t.max = substream->buffer_bytes_max; + t.openmin = 0; + t.openmax = 0; + t.integer = 1; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + int k, err; + + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { + snd_mask_any(constrs_mask(constrs, k)); + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { + snd_interval_any(constrs_interval(constrs, k)); + } + + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS)); + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + snd_pcm_hw_rule_format, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + snd_pcm_hw_rule_sample_bits, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mul, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_muldivk, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_mul, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_muldivk, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + snd_pcm_hw_rule_muldivk, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + snd_pcm_hw_rule_muldivk, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + return 0; +} + +int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_hardware_t *hw = &runtime->hw; + int err; + unsigned int mask = 0; + + if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_MMAP) { + if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_COMPLEX) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX; + } + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, + hw->channels_min, hw->channels_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, + hw->rate_min, hw->rate_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + hw->period_bytes_min, hw->period_bytes_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS, + hw->periods_min, hw->periods_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + hw->period_bytes_min, hw->buffer_bytes_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + snd_pcm_hw_rule_buffer_bytes_max, substream, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1); + if (err < 0) + return err; + + /* FIXME: remove */ + if (runtime->dma_bytes) { + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); + snd_assert(err >= 0, return -EINVAL); + } + + if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_rate, hw, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + } + + /* FIXME: this belong to lowlevel */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME, + 1000000 / HZ, 1000000 / HZ); + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + + return 0; +} + +static void snd_pcm_add_file(snd_pcm_str_t *str, + snd_pcm_file_t *pcm_file) +{ + pcm_file->next = str->files; + str->files = pcm_file; +} + +static void snd_pcm_remove_file(snd_pcm_str_t *str, + snd_pcm_file_t *pcm_file) +{ + snd_pcm_file_t * pcm_file1; + if (str->files == pcm_file) { + str->files = pcm_file->next; + } else { + pcm_file1 = str->files; + while (pcm_file1 && pcm_file1->next != pcm_file) + pcm_file1 = pcm_file1->next; + if (pcm_file1 != NULL) + pcm_file1->next = pcm_file->next; + } +} + +static int snd_pcm_release_file(snd_pcm_file_t * pcm_file) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_str_t * str; + + snd_assert(pcm_file != NULL, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + str = substream->pstr; + snd_pcm_unlink(substream); + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + substream->ops->close(substream); + substream->ffile = NULL; + snd_pcm_remove_file(str, pcm_file); + snd_pcm_release_substream(substream); + snd_magic_kfree(pcm_file); + return 0; +} + +static int snd_pcm_open_file(struct file *file, + snd_pcm_t *pcm, + int stream, + snd_pcm_file_t **rpcm_file) +{ + int err = 0; + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_str_t *str; + + snd_assert(rpcm_file != NULL, return -EINVAL); + *rpcm_file = NULL; + + pcm_file = snd_magic_kcalloc(snd_pcm_file_t, 0, GFP_KERNEL); + if (pcm_file == NULL) { + return -ENOMEM; + } + + if ((err = snd_pcm_open_substream(pcm, stream, &substream)) < 0) { + snd_magic_kfree(pcm_file); + return err; + } + + str = substream->pstr; + substream->file = pcm_file; + + pcm_file->substream = substream; + + snd_pcm_add_file(str, pcm_file); + + err = snd_pcm_hw_constraints_init(substream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraints_init failed\n"); + snd_pcm_release_file(pcm_file); + return err; + } + + if ((err = substream->ops->open(substream)) < 0) { + snd_pcm_release_file(pcm_file); + return err; + } + + err = snd_pcm_hw_constraints_complete(substream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraints_complete failed\n"); + substream->ops->close(substream); + snd_pcm_release_file(pcm_file); + return err; + } + + substream->ffile = file; + + file->private_data = pcm_file; + *rpcm_file = pcm_file; + return 0; +} + +int snd_pcm_open(struct inode *inode, struct file *file) +{ + int cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + int device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)); + int err; + snd_pcm_t *pcm; + snd_pcm_file_t *pcm_file; + wait_queue_t wait; + +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + snd_runtime_check(device >= SNDRV_MINOR_PCM_PLAYBACK && device < SNDRV_MINOR_DEVICES, return -ENXIO); + pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + (device % SNDRV_MINOR_PCMS)]; + if (pcm == NULL) { + err = -ENODEV; + goto __error1; + } + if (!try_inc_mod_count(pcm->card->module)) { + err = -EFAULT; + goto __error1; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&pcm->open_wait, &wait); + while (1) { + down(&pcm->open_mutex); + err = snd_pcm_open_file(file, pcm, device >= SNDRV_MINOR_PCM_CAPTURE ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, &pcm_file); + if (err >= 0) + break; + up(&pcm->open_mutex); + if (err == -EAGAIN) { + if (file->f_flags & O_NONBLOCK) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&pcm->open_wait, &wait); + if (err < 0) + goto __error; + up(&pcm->open_mutex); + return err; + + __error: + dec_mod_count(pcm->card->module); + __error1: +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return err; +} + +int snd_pcm_release(struct inode *inode, struct file *file) +{ + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + snd_pcm_file_t *pcm_file; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + snd_assert(!atomic_read(&substream->runtime->mmap_count), ); + pcm = substream->pcm; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if ((substream->ffile->f_flags & O_NONBLOCK) || + snd_pcm_playback_drain(substream) == -ERESTARTSYS) + snd_pcm_playback_drop(substream); + } else + snd_pcm_capture_drop(substream); + fasync_helper(-1, file, 0, &substream->runtime->fasync); + down(&pcm->open_mutex); + snd_pcm_release_file(pcm_file); + up(&pcm->open_mutex); + wake_up(&pcm->open_wait); + dec_mod_count(pcm->card->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +snd_pcm_sframes_t snd_pcm_playback_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + snd_pcm_sframes_t ret; + snd_pcm_sframes_t hw_avail; + + if (frames == 0) + return 0; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + break; + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) + break; + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + ret = -EPIPE; + goto __end; + default: + ret = -EBADFD; + goto __end; + } + + hw_avail = snd_pcm_playback_hw_avail(runtime); + if (hw_avail <= 0) { + ret = 0; + goto __end; + } + if (frames > hw_avail) + frames = hw_avail; + else + frames -= frames % runtime->xfer_align; + appl_ptr = runtime->control->appl_ptr - frames; + if (appl_ptr < 0) + appl_ptr += runtime->boundary; + runtime->control->appl_ptr = appl_ptr; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && + runtime->sleep_min) + snd_pcm_tick_prepare(substream); + ret = frames; + __end: + spin_unlock_irq(&runtime->lock); + return ret; +} + +snd_pcm_sframes_t snd_pcm_capture_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + snd_pcm_sframes_t ret; + snd_pcm_sframes_t hw_avail; + + if (frames == 0) + return 0; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_DRAINING: + break; + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) + break; + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + ret = -EPIPE; + goto __end; + default: + ret = -EBADFD; + goto __end; + } + + hw_avail = snd_pcm_capture_hw_avail(runtime); + if (hw_avail <= 0) { + ret = 0; + goto __end; + } + if (frames > hw_avail) + frames = hw_avail; + else + frames -= frames % runtime->xfer_align; + appl_ptr = runtime->control->appl_ptr - frames; + if (appl_ptr < 0) + appl_ptr += runtime->boundary; + runtime->control->appl_ptr = appl_ptr; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && + runtime->sleep_min) + snd_pcm_tick_prepare(substream); + ret = frames; + __end: + spin_unlock_irq(&runtime->lock); + return ret; +} + +static int snd_pcm_playback_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err = 0; + snd_pcm_sframes_t n; + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + if (snd_pcm_update_hw_ptr(substream) >= 0) { + n = snd_pcm_playback_hw_avail(runtime); + if (put_user(n, res)) + err = -EFAULT; + break; + } else { + err = SNDRV_PCM_STATE_RUNNING ? -EPIPE : -EBADFD; + } + break; + case SNDRV_PCM_STATE_SUSPENDED: + if (runtime->status->suspended_state == SNDRV_PCM_STATE_RUNNING) { + n = snd_pcm_playback_hw_avail(runtime); + if (put_user(n, res)) + err = -EFAULT; + } else { + err = -EBADFD; + } + break; + default: + err = -EBADFD; + break; + } + spin_unlock_irq(&runtime->lock); + return err; +} + +static int snd_pcm_capture_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err = 0; + snd_pcm_sframes_t n; + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) { + n = snd_pcm_capture_avail(runtime); + if (put_user(n, res)) + err = -EFAULT; + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + break; + case SNDRV_PCM_STATE_SUSPENDED: + if (runtime->status->suspended_state == SNDRV_PCM_STATE_RUNNING) { + n = snd_pcm_capture_avail(runtime); + if (put_user(n, res)) + err = -EFAULT; + } else { + err = -EBADFD; + } + break; + default: + err = -EBADFD; + break; + } + spin_unlock_irq(&runtime->lock); + return err; +} + +static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg); +static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg); + +static int snd_pcm_common_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + snd_assert(substream != NULL, return -ENXIO); + + switch (cmd) { + case SNDRV_PCM_IOCTL_PVERSION: + return put_user(SNDRV_PCM_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_PCM_IOCTL_INFO: + return snd_pcm_info_user(substream, (snd_pcm_info_t *) arg); + case SNDRV_PCM_IOCTL_HW_REFINE: + return snd_pcm_hw_refine_user(substream, (snd_pcm_hw_params_t *) arg); + case SNDRV_PCM_IOCTL_HW_PARAMS: + return snd_pcm_hw_params_user(substream, (snd_pcm_hw_params_t *) arg); + case SNDRV_PCM_IOCTL_HW_FREE: + return snd_pcm_hw_free(substream); + case SNDRV_PCM_IOCTL_SW_PARAMS: + return snd_pcm_sw_params_user(substream, (snd_pcm_sw_params_t *) arg); + case SNDRV_PCM_IOCTL_STATUS: + return snd_pcm_status_user(substream, (snd_pcm_status_t *) arg); + case SNDRV_PCM_IOCTL_CHANNEL_INFO: + return snd_pcm_channel_info(substream, (snd_pcm_channel_info_t *) arg); + case SNDRV_PCM_IOCTL_PREPARE: + { + int res; + spin_lock_irq(&substream->runtime->lock); + res = snd_pcm_prepare(substream); + spin_unlock_irq(&substream->runtime->lock); + return res; + } + case SNDRV_PCM_IOCTL_RESET: + { + int res; + spin_lock_irq(&substream->runtime->lock); + res = snd_pcm_reset(substream); + spin_unlock_irq(&substream->runtime->lock); + return res; + } + case SNDRV_PCM_IOCTL_START: + { + int res; + spin_lock_irq(&substream->runtime->lock); + res = snd_pcm_start(substream); + spin_unlock_irq(&substream->runtime->lock); + return res; + } + case SNDRV_PCM_IOCTL_LINK: + return snd_pcm_link(substream, (long) arg); + case SNDRV_PCM_IOCTL_UNLINK: + return snd_pcm_unlink(substream); + } + snd_printd("unknown ioctl = 0x%x\n", cmd); + return -ENOTTY; +} + +static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + snd_assert(substream != NULL, return -ENXIO); + snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); + switch (cmd) { + case SNDRV_PCM_IOCTL_WRITEI_FRAMES: + { + snd_xferi_t xferi, *_xferi = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (put_user(0, &_xferi->result)) + return -EFAULT; + if (copy_from_user(&xferi, _xferi, sizeof(xferi))) + return -EFAULT; + result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames); + __put_user(result, &_xferi->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + { + snd_xfern_t xfern, *_xfern = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + void *bufs[128]; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (runtime->channels > 128) + return -EINVAL; + if (put_user(0, &_xfern->result)) + return -EFAULT; + if (copy_from_user(&xfern, _xfern, sizeof(xfern))) + return -EFAULT; + if (copy_from_user(bufs, xfern.bufs, sizeof(*bufs) * runtime->channels)) + return -EFAULT; + result = snd_pcm_lib_writev(substream, bufs, xfern.frames); + __put_user(result, &_xfern->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_REWIND: + { + snd_pcm_uframes_t frames, *_frames = arg; + snd_pcm_sframes_t result; + if (get_user(frames, _frames)) + return -EFAULT; + if (put_user(0, _frames)) + return -EFAULT; + result = snd_pcm_playback_rewind(substream, frames); + __put_user(result, _frames); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_PAUSE: + { + int res; + spin_lock_irq(&substream->runtime->lock); + res = snd_pcm_pause(substream, (long) arg); + spin_unlock_irq(&substream->runtime->lock); + return res; + } + case SNDRV_PCM_IOCTL_DRAIN: + return snd_pcm_playback_drain(substream); + case SNDRV_PCM_IOCTL_DROP: + return snd_pcm_playback_drop(substream); + case SNDRV_PCM_IOCTL_DELAY: + return snd_pcm_playback_delay(substream, (snd_pcm_sframes_t*) arg); + case SNDRV_PCM_IOCTL_RESUME: + return snd_pcm_resume(substream); + } + return snd_pcm_common_ioctl1(substream, cmd, arg); +} + +static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + snd_assert(substream != NULL, return -ENXIO); + snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL); + switch (cmd) { + case SNDRV_PCM_IOCTL_READI_FRAMES: + { + snd_xferi_t xferi, *_xferi = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (put_user(0, &_xferi->result)) + return -EFAULT; + if (copy_from_user(&xferi, _xferi, sizeof(xferi))) + return -EFAULT; + result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames); + __put_user(result, &_xferi->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_READN_FRAMES: + { + snd_xfern_t xfern, *_xfern = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + void *bufs[128]; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (runtime->channels > 128) + return -EINVAL; + if (put_user(0, &_xfern->result)) + return -EFAULT; + if (copy_from_user(&xfern, _xfern, sizeof(xfern))) + return -EFAULT; + if (copy_from_user(bufs, xfern.bufs, sizeof(*bufs) * runtime->channels)) + return -EFAULT; + result = snd_pcm_lib_readv(substream, bufs, xfern.frames); + __put_user(result, &_xfern->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_REWIND: + { + snd_pcm_uframes_t frames, *_frames = arg; + snd_pcm_sframes_t result; + if (get_user(frames, _frames)) + return -EFAULT; + if (put_user(0, _frames)) + return -EFAULT; + result = snd_pcm_capture_rewind(substream, frames); + __put_user(result, _frames); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_DRAIN: + return snd_pcm_capture_drain(substream); + case SNDRV_PCM_IOCTL_DROP: + return snd_pcm_capture_drop(substream); + case SNDRV_PCM_IOCTL_DELAY: + return snd_pcm_capture_delay(substream, (snd_pcm_sframes_t*) arg); + case SNDRV_PCM_IOCTL_RESUME: + return snd_pcm_resume(substream); + } + return snd_pcm_common_ioctl1(substream, cmd, arg); +} + +static int snd_pcm_playback_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_pcm_file_t *pcm_file; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + + if (((cmd >> 8) & 0xff) != 'A') + return -ENOTTY; + + return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void *) arg); +} + +static int snd_pcm_capture_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_pcm_file_t *pcm_file; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + + if (((cmd >> 8) & 0xff) != 'A') + return -ENOTTY; + + return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void *) arg); +} + +int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + mm_segment_t fs; + int result; + + fs = snd_enter_user(); + result = snd_pcm_playback_ioctl1(substream, cmd, arg); + snd_leave_user(fs); + return result; +} + +int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + mm_segment_t fs; + int result; + + fs = snd_enter_user(); + result = snd_pcm_capture_ioctl1(substream, cmd, arg); + snd_leave_user(fs); + return result; +} + +int snd_pcm_kernel_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + return snd_pcm_kernel_playback_ioctl(substream, cmd, arg); + case SNDRV_PCM_STREAM_CAPTURE: + return snd_pcm_kernel_capture_ioctl(substream, cmd, arg); + default: + return -EINVAL; + } +} + +static ssize_t snd_pcm_read(struct file *file, char *buf, size_t count, loff_t * offset) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (!frame_aligned(runtime, count)) + return -EINVAL; + count = bytes_to_frames(runtime, count); + result = snd_pcm_lib_read(substream, buf, count); + if (result > 0) + result = frames_to_bytes(runtime, result); + return result; +} + +static ssize_t snd_pcm_write(struct file *file, const char *buf, size_t count, loff_t * offset) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + + up(&file->f_dentry->d_inode->i_sem); + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end); + substream = pcm_file->substream; + snd_assert(substream != NULL, result = -ENXIO; goto end); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + result = -EBADFD; + goto end; + } + if (!frame_aligned(runtime, count)) { + result = -EINVAL; + goto end; + } + count = bytes_to_frames(runtime, count); + result = snd_pcm_lib_write(substream, buf, count); + if (result > 0) + result = frames_to_bytes(runtime, result); + end: + down(&file->f_dentry->d_inode->i_sem); + return result; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44) +static ssize_t snd_pcm_readv(struct file *file, const struct iovec *_vector, + unsigned long count, loff_t * offset) + +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + unsigned long i; + void *bufs[128]; + snd_pcm_uframes_t frames; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (count > 128 || count != runtime->channels) + return -EINVAL; + if (!frame_aligned(runtime, _vector->iov_len)) + return -EINVAL; + frames = bytes_to_samples(runtime, _vector->iov_len); + for (i = 0; i < count; ++i) + bufs[i] = _vector[i].iov_base; + result = snd_pcm_lib_readv(substream, bufs, frames); + if (result > 0) + result = frames_to_bytes(runtime, result); + return result; +} + +static ssize_t snd_pcm_writev(struct file *file, const struct iovec *_vector, + unsigned long count, loff_t * offset) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + unsigned long i; + void *bufs[128]; + snd_pcm_uframes_t frames; + + up(&file->f_dentry->d_inode->i_sem); + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end); + substream = pcm_file->substream; + snd_assert(substream != NULL, result = -ENXIO; goto end); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + result = -EBADFD; + goto end; + } + if (count > 128 || count != runtime->channels || + !frame_aligned(runtime, _vector->iov_len)) { + result = -EINVAL; + goto end; + } + frames = bytes_to_samples(runtime, _vector->iov_len); + for (i = 0; i < count; ++i) + bufs[i] = _vector[i].iov_base; + result = snd_pcm_lib_writev(substream, bufs, frames); + if (result > 0) + result = frames_to_bytes(runtime, result); + end: + down(&file->f_dentry->d_inode->i_sem); + return result; +} +#endif + +unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + unsigned int mask; + snd_pcm_uframes_t avail; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return 0); + + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + + poll_wait(file, &runtime->sleep, wait); + + spin_lock_irq(&runtime->lock); + avail = snd_pcm_playback_avail(runtime); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + if (avail >= runtime->control->avail_min) { + mask = POLLOUT | POLLWRNORM; + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_DRAINING: + mask = 0; + break; + case SNDRV_PCM_STATE_PREPARED: + if (avail > 0) { + mask = POLLOUT | POLLWRNORM; + break; + } + /* Fall through */ + default: + mask = POLLOUT | POLLWRNORM | POLLERR; + break; + } + spin_unlock_irq(&runtime->lock); + return mask; +} + +unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + unsigned int mask; + snd_pcm_uframes_t avail; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return 0); + + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + + poll_wait(file, &runtime->sleep, wait); + + spin_lock_irq(&runtime->lock); + avail = snd_pcm_capture_avail(runtime); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + if (avail >= runtime->control->avail_min) { + mask = POLLIN | POLLRDNORM; + break; + } + mask = 0; + break; + case SNDRV_PCM_STATE_DRAINING: + if (avail > 0) { + mask = POLLIN | POLLRDNORM; + break; + } + /* Fall through */ + default: + mask = POLLIN | POLLRDNORM | POLLERR; + break; + } + spin_unlock_irq(&runtime->lock); + return mask; +} + +#ifndef VM_RESERVED +#ifndef LINUX_2_2 +static int snd_pcm_mmap_swapout(struct page * page, struct file * file) +#else +static int snd_pcm_mmap_swapout(struct vm_area_struct * area, struct page * page) +#endif +{ + return 0; +} +#endif + +#ifndef LINUX_2_2 +static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#else +static unsigned long snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#endif +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + snd_pcm_runtime_t *runtime; + struct page * page; + + if (substream == NULL) + return NOPAGE_OOM; + runtime = substream->runtime; + page = virt_to_page(runtime->status); + get_page(page); +#ifndef LINUX_2_2 + return page; +#else + return page_address(page); +#endif +} + +static struct vm_operations_struct snd_pcm_vm_ops_status = +{ + nopage: snd_pcm_mmap_status_nopage, +#ifndef VM_RESERVED + swapout: snd_pcm_mmap_swapout, +#endif +}; + +int snd_pcm_mmap_status(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) +{ + snd_pcm_runtime_t *runtime; + long size; + if (!(area->vm_flags & VM_READ)) + return -EINVAL; + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EAGAIN); + size = area->vm_end - area->vm_start; + if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t))) + return -EINVAL; + area->vm_ops = &snd_pcm_vm_ops_status; +#ifndef LINUX_2_2 + area->vm_private_data = substream; +#else + area->vm_private_data = (long)substream; +#endif +#ifdef VM_RESERVED + area->vm_flags |= VM_RESERVED; +#endif + return 0; +} + +#ifndef LINUX_2_2 +static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#else +static unsigned long snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#endif +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + snd_pcm_runtime_t *runtime; + struct page * page; + + if (substream == NULL) + return NOPAGE_OOM; + runtime = substream->runtime; + page = virt_to_page(runtime->control); + get_page(page); +#ifndef LINUX_2_2 + return page; +#else + return page_address(page); +#endif +} + +static struct vm_operations_struct snd_pcm_vm_ops_control = +{ + nopage: snd_pcm_mmap_control_nopage, +#ifndef VM_RESERVED + swapout: snd_pcm_mmap_swapout, +#endif +}; + +static int snd_pcm_mmap_control(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) +{ + snd_pcm_runtime_t *runtime; + long size; + if (!(area->vm_flags & VM_READ)) + return -EINVAL; + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EAGAIN); + size = area->vm_end - area->vm_start; + if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t))) + return -EINVAL; + area->vm_ops = &snd_pcm_vm_ops_control; +#ifndef LINUX_2_2 + area->vm_private_data = substream; +#else + area->vm_private_data = (long)substream; +#endif +#ifdef VM_RESERVED + area->vm_flags |= VM_RESERVED; +#endif + return 0; +} + +static void snd_pcm_mmap_data_open(struct vm_area_struct *area) +{ +#if 0 + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + atomic_inc(&substream->runtime->mmap_count); +#endif +} + +static void snd_pcm_mmap_data_close(struct vm_area_struct *area) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + atomic_dec(&substream->runtime->mmap_count); +} + +#ifndef LINUX_2_2 +static struct page * snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#else +static unsigned long snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#endif +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + snd_pcm_runtime_t *runtime; + unsigned long offset; + struct page * page; + size_t dma_bytes; + + if (substream == NULL) + return NOPAGE_OOM; + runtime = substream->runtime; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + offset = area->vm_pgoff << PAGE_SHIFT; +#else + offset = area->vm_offset; +#endif + offset += address - area->vm_start; + snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM); + dma_bytes = PAGE_ALIGN(runtime->dma_bytes); + if (offset > dma_bytes - PAGE_SIZE) + return NOPAGE_SIGBUS; + page = virt_to_page(runtime->dma_area + offset); + get_page(page); +#ifndef LINUX_2_2 + return page; +#else + return page_address(page); +#endif +} + +static struct vm_operations_struct snd_pcm_vm_ops_data = +{ + open: snd_pcm_mmap_data_open, + close: snd_pcm_mmap_data_close, + nopage: snd_pcm_mmap_data_nopage, +#ifndef VM_RESERVED + swapout: snd_pcm_mmap_swapout, +#endif +}; + +int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) +{ + snd_pcm_runtime_t *runtime; + long size; + unsigned long offset; + size_t dma_bytes; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (!(area->vm_flags & (VM_WRITE|VM_READ))) + return -EINVAL; + } else { + if (!(area->vm_flags & VM_READ)) + return -EINVAL; + } + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EAGAIN); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) + return -ENXIO; + if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + size = area->vm_end - area->vm_start; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + offset = area->vm_pgoff << PAGE_SHIFT; +#else + offset = area->vm_offset; +#endif + dma_bytes = PAGE_ALIGN(runtime->dma_bytes); + if (size > dma_bytes) + return -EINVAL; + if (offset > dma_bytes - size) + return -EINVAL; + + area->vm_ops = &snd_pcm_vm_ops_data; +#ifndef LINUX_2_2 + area->vm_private_data = substream; +#else + area->vm_private_data = (long)substream; +#endif +#ifdef VM_RESERVED + area->vm_flags |= VM_RESERVED; +#endif + atomic_inc(&runtime->mmap_count); + return 0; +} + +static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) +{ + snd_pcm_file_t * pcm_file; + snd_pcm_substream_t *substream; + unsigned long offset; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + offset = area->vm_pgoff << PAGE_SHIFT; +#else + offset = area->vm_offset; +#endif + switch (offset) { + case SNDRV_PCM_MMAP_OFFSET_STATUS: + return snd_pcm_mmap_status(substream, file, area); + case SNDRV_PCM_MMAP_OFFSET_CONTROL: + return snd_pcm_mmap_control(substream, file, area); + default: + return snd_pcm_mmap_data(substream, file, area); + } + return 0; +} + +static int snd_pcm_fasync(int fd, struct file * file, int on) +{ + snd_pcm_file_t * pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + int err; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + + err = fasync_helper(fd, file, on, &runtime->fasync); + if (err < 0) + return err; + return 0; +} + +/* + * Register section + */ + +static struct file_operations snd_pcm_f_ops_playback = { +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + write: snd_pcm_write, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44) + writev: snd_pcm_writev, +#endif + open: snd_pcm_open, + release: snd_pcm_release, + poll: snd_pcm_playback_poll, + ioctl: snd_pcm_playback_ioctl, + mmap: snd_pcm_mmap, + fasync: snd_pcm_fasync, +}; + +static struct file_operations snd_pcm_f_ops_capture = { +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_pcm_read, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44) + readv: snd_pcm_readv, +#endif + open: snd_pcm_open, + release: snd_pcm_release, + poll: snd_pcm_capture_poll, + ioctl: snd_pcm_capture_ioctl, + mmap: snd_pcm_mmap, + fasync: snd_pcm_fasync, +}; + +snd_minor_t snd_pcm_reg[2] = +{ + { + comment: "digital audio playback", + f_ops: &snd_pcm_f_ops_playback, + }, + { + comment: "digital audio capture", + f_ops: &snd_pcm_f_ops_capture, + } +}; diff -Nru a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/pcm_timer.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,159 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#define chip_t snd_pcm_substream_t + +/* + * Timer functions + */ + +/* Greatest common divisor */ +static int gcd(int a, int b) +{ + int r; + if (a < b) { + r = a; + a = b; + b = r; + } + while ((r = a % b) != 0) { + a = b; + b = r; + } + return b; +} + +void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream) +{ + unsigned int rate, mult, fsize, l; + snd_pcm_runtime_t *runtime = substream->runtime; + + mult = 1000000000; + rate = runtime->rate; + snd_assert(rate != 0, return); + l = gcd(mult, rate); + mult /= l; + rate /= l; + fsize = runtime->period_size; + snd_assert(fsize != 0, return); + l = gcd(rate, fsize); + rate /= l; + fsize /= l; + while ((mult * fsize) / fsize != mult) { + mult /= 2; + rate /= 2; + } + snd_assert(rate != 0, return); + runtime->timer_resolution = mult * fsize / rate; +} + +static unsigned long snd_pcm_timer_resolution(snd_timer_t * timer) +{ + snd_pcm_substream_t * substream; + + substream = snd_magic_cast(snd_pcm_substream_t, timer->private_data, return -ENXIO); + return substream->runtime ? substream->runtime->timer_resolution : 0; +} + +static int snd_pcm_timer_start(snd_timer_t * timer) +{ + unsigned long flags; + snd_pcm_substream_t * substream; + + substream = snd_timer_chip(timer); + spin_lock_irqsave(&substream->timer_lock, flags); + substream->timer_running = 1; + spin_unlock_irqrestore(&substream->timer_lock, flags); + return 0; +} + +static int snd_pcm_timer_stop(snd_timer_t * timer) +{ + unsigned long flags; + snd_pcm_substream_t * substream; + + substream = snd_timer_chip(timer); + spin_lock_irqsave(&substream->timer_lock, flags); + substream->timer_running = 0; + spin_unlock_irqrestore(&substream->timer_lock, flags); + return 0; +} + +static struct _snd_timer_hardware snd_pcm_timer = +{ + flags: SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE, + resolution: 0, + ticks: 1, + c_resolution: snd_pcm_timer_resolution, + start: snd_pcm_timer_start, + stop: snd_pcm_timer_stop, +}; + +/* + * Init functions + */ + +static void snd_pcm_timer_free(snd_timer_t *timer) +{ + snd_pcm_substream_t *substream = snd_magic_cast(snd_pcm_substream_t, timer->private_data, return); + substream->timer = NULL; +} + +void snd_pcm_timer_init(snd_pcm_substream_t *substream) +{ + snd_timer_id_t tid; + snd_timer_t *timer; + + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.dev_class = SNDRV_TIMER_CLASS_PCM; + tid.card = substream->pcm->card->number; + tid.device = substream->pcm->device; + tid.subdevice = (substream->number << 1) | (substream->stream & 1); + if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0) + return; + sprintf(timer->name, "PCM %s %i-%i-%i", + substream->stream == SNDRV_PCM_STREAM_CAPTURE ? + "capture" : "playback", + tid.card, tid.device, tid.subdevice); + timer->hw = snd_pcm_timer; + if (snd_device_register(timer->card, timer) < 0) { + snd_device_free(timer->card, timer); + return; + } + timer->private_data = substream; + timer->private_free = snd_pcm_timer_free; + substream->timer = timer; +} + +void snd_pcm_timer_done(snd_pcm_substream_t *substream) +{ + if (substream->timer) { + snd_device_free(substream->pcm->card, substream->timer); + substream->timer = NULL; + } +} diff -Nru a/sound/core/rawmidi.c b/sound/core/rawmidi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/rawmidi.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1574 @@ +/* + * Abstract layer for MIDI v1.0 stream + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA."); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_SND_OSSEMUL +static int snd_midi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; +static int snd_amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; +MODULE_PARM(snd_midi_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_midi_map, "Raw MIDI device number assigned to 1st OSS device."); +MODULE_PARM_SYNTAX(snd_midi_map, "default:0,skill:advanced"); +MODULE_PARM(snd_amidi_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_amidi_map, "Raw MIDI device number assigned to 2nd OSS device."); +MODULE_PARM_SYNTAX(snd_amidi_map, "default:1,skill:advanced"); +#endif /* CONFIG_SND_OSSEMUL */ + +static int snd_rawmidi_free(snd_rawmidi_t *rawmidi); +static int snd_rawmidi_dev_free(snd_device_t *device); +static int snd_rawmidi_dev_register(snd_device_t *device); +static int snd_rawmidi_dev_unregister(snd_device_t *device); + +snd_rawmidi_t *snd_rawmidi_devices[SNDRV_CARDS * SNDRV_RAWMIDI_DEVICES]; + +static DECLARE_MUTEX(register_mutex); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +static inline unsigned short snd_rawmidi_file_flags(struct file *file) +{ + switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) { + case FMODE_WRITE: + return SNDRV_RAWMIDI_LFLG_OUTPUT; + case FMODE_READ: + return SNDRV_RAWMIDI_LFLG_INPUT; + default: + return SNDRV_RAWMIDI_LFLG_OPEN; + } +} + +static inline int snd_rawmidi_ready(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + return runtime->avail >= runtime->avail_min; +} + +static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream, size_t count) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + return runtime->avail >= runtime->avail_min && runtime->avail >= count; +} + +static int snd_rawmidi_init(snd_rawmidi_substream_t *substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + spin_lock_init(&runtime->lock); + init_waitqueue_head(&runtime->sleep); + runtime->event = NULL; + runtime->buffer_size = PAGE_SIZE; + runtime->avail_min = 1; + if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) + runtime->avail = 0; + else + runtime->avail = runtime->buffer_size; + if ((runtime->buffer = kmalloc(runtime->buffer_size, GFP_KERNEL)) == NULL) + return -ENOMEM; + runtime->appl_ptr = runtime->hw_ptr = 0; + return 0; +} + +static int snd_rawmidi_done_buffer(snd_rawmidi_runtime_t *runtime) +{ + if (runtime->buffer) { + kfree(runtime->buffer); + runtime->buffer = NULL; + } + return 0; +} + +int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + substream->ops->trigger(substream, 0); + runtime->trigger = 0; + runtime->drain = 0; + /* interrupts are not enabled at this moment, + so spinlock is not required */ + runtime->appl_ptr = runtime->hw_ptr = 0; + runtime->avail = runtime->buffer_size; + return 0; +} + +int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream) +{ + int err; + long timeout; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + err = 0; + runtime->drain = 1; + while (runtime->avail < runtime->buffer_size) { + timeout = interruptible_sleep_on_timeout(&runtime->sleep, 10 * HZ); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + if (runtime->avail < runtime->buffer_size && !timeout) { + err = -EIO; + break; + } + } + runtime->drain = 0; + if (err != -ERESTARTSYS) { + /* we need wait a while to make sure that Tx FIFOs are empty */ + if (substream->ops->drain) + substream->ops->drain(substream); + else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + snd_rawmidi_drop_output(substream); + } + return err; +} + +int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + substream->ops->trigger(substream, 0); + runtime->trigger = 0; + runtime->drain = 0; + /* interrupts aren't enabled at this moment, so spinlock isn't needed */ + runtime->appl_ptr = runtime->hw_ptr = 0; + runtime->avail = 0; + return 0; +} + +int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, + int mode, snd_rawmidi_file_t * rfile) +{ + snd_rawmidi_t *rmidi; + struct list_head *list1, *list2; + snd_rawmidi_substream_t *sinput, *soutput; + snd_rawmidi_runtime_t *input = NULL, *output = NULL; + int err; + + if (rfile) + rfile->input = rfile->output = NULL; +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device]; + if (rmidi == NULL) { + err = -ENODEV; + goto __error1; + } + if (!try_inc_mod_count(rmidi->card->module)) { + err = -EFAULT; + goto __error1; + } + down(&rmidi->open_mutex); + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) { + err = -ENXIO; + goto __error; + } + if (subdevice >= 0 && subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) { + err = -ENODEV; + goto __error; + } + if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >= + rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) { + err = -EAGAIN; + goto __error; + } + } + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) { + err = -ENXIO; + goto __error; + } + if (subdevice >= 0 && subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) { + err = -ENODEV; + goto __error; + } + if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >= + rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) { + err = -EAGAIN; + goto __error; + } + } + list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next; + while (1) { + if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { + sinput = NULL; + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + err = -EAGAIN; + goto __error; + } + break; + } + sinput = list_entry(list1, snd_rawmidi_substream_t, list); + if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened) + goto __nexti; + if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number)) + break; + __nexti: + list1 = list1->next; + } + list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next; + while (1) { + if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { + soutput = NULL; + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + err = -EAGAIN; + goto __error; + } + break; + } + soutput = list_entry(list2, snd_rawmidi_substream_t, list); + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + if (mode & SNDRV_RAWMIDI_LFLG_APPEND) { + if (soutput->opened && !soutput->append) + goto __nexto; + } else { + if (soutput->opened) + goto __nexto; + } + } + if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number)) + break; + __nexto: + list2 = list2->next; + } + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + input = snd_kcalloc(sizeof(snd_rawmidi_runtime_t), GFP_KERNEL); + if (input == NULL) { + err = -ENOMEM; + goto __error; + } + sinput->runtime = input; + if (snd_rawmidi_init(sinput) < 0) { + err = -ENOMEM; + goto __error; + } + if ((err = sinput->ops->open(sinput)) < 0) { + sinput->runtime = NULL; + goto __error; + } + sinput->opened = 1; + rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++; + } else { + sinput = NULL; + } + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + if (soutput->opened) + goto __skip_output; + output = snd_kcalloc(sizeof(snd_rawmidi_runtime_t), GFP_KERNEL); + if (output == NULL) { + err = -ENOMEM; + goto __error; + } + soutput->runtime = output; + if (snd_rawmidi_init(soutput) < 0) { + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + sinput->ops->close(sinput); + sinput->runtime = NULL; + } + err = -ENOMEM; + goto __error; + } + if ((err = soutput->ops->open(soutput)) < 0) { + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + sinput->ops->close(sinput); + sinput->runtime = NULL; + } + soutput->runtime = NULL; + goto __error; + } + __skip_output: + soutput->opened = 1; + if (mode & SNDRV_RAWMIDI_LFLG_APPEND) + soutput->append = 1; + if (soutput->use_count++ == 0) + soutput->active_sensing = 1; + rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++; + } else { + soutput = NULL; + } + up(&rmidi->open_mutex); + if (rfile) { + rfile->rmidi = rmidi; + rfile->input = sinput; + rfile->output = soutput; + } + return 0; + + __error: + if (input != NULL) + kfree(input); + if (output != NULL) + kfree(output); + dec_mod_count(rmidi->card->module); + up(&rmidi->open_mutex); + __error1: +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return err; +} + +static int snd_rawmidi_open(struct inode *inode, struct file *file) +{ + int maj = major(inode->i_rdev); + int cardnum; + snd_card_t *card; + int device, subdevice; + unsigned short fflags; + int err; + snd_rawmidi_t *rmidi; + snd_rawmidi_file_t *rawmidi_file; + wait_queue_t wait; + struct list_head *list; + snd_ctl_file_t *kctl; + + switch (maj) { + case CONFIG_SND_MAJOR: + cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + cardnum %= SNDRV_CARDS; + device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)) - SNDRV_MINOR_RAWMIDI; + device %= SNDRV_MINOR_RAWMIDIS; + break; +#ifdef CONFIG_SND_OSSEMUL + case SOUND_MAJOR: + cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev)); + cardnum %= SNDRV_CARDS; + device = SNDRV_MINOR_OSS_DEVICE(minor(inode->i_rdev)) == SNDRV_MINOR_OSS_MIDI ? + snd_midi_map[cardnum] : snd_amidi_map[cardnum]; + break; +#endif + default: + return -ENXIO; + } + + rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device]; + if (rmidi == NULL) + return -ENODEV; + card = rmidi->card; +#ifdef CONFIG_SND_OSSEMUL + if (maj == SOUND_MAJOR && !rmidi->ossreg) + return -ENXIO; +#endif + fflags = snd_rawmidi_file_flags(file); + if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) + return -EINVAL; /* invalid combination */ + if ((file->f_flags & O_APPEND) || maj != CONFIG_SND_MAJOR) /* OSS emul? */ + fflags |= SNDRV_RAWMIDI_LFLG_APPEND; + rawmidi_file = snd_magic_kmalloc(snd_rawmidi_file_t, 0, GFP_KERNEL); + if (rawmidi_file == NULL) + return -ENOMEM; + init_waitqueue_entry(&wait, current); + add_wait_queue(&rmidi->open_wait, &wait); + while (1) { + subdevice = -1; + read_lock(&card->control_rwlock); + list_for_each(list, &card->ctl_files) { + kctl = snd_ctl_file(list); + if (kctl->pid == current->pid) { + subdevice = kctl->prefer_rawmidi_subdevice; + break; + } + } + read_unlock(&card->control_rwlock); + err = snd_rawmidi_kernel_open(cardnum, device, subdevice, fflags, rawmidi_file); + if (err >= 0) + break; + if (err == -EAGAIN) { + if (file->f_flags & O_NONBLOCK) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } +#ifdef CONFIG_SND_OSSEMUL + if (rawmidi_file->input && rawmidi_file->input->runtime) + rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR); + if (rawmidi_file->output && rawmidi_file->output->runtime) + rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR); +#endif + set_current_state(TASK_RUNNING); + remove_wait_queue(&rmidi->open_wait, &wait); + if (err >= 0) { + file->private_data = rawmidi_file; + } else { + snd_magic_kfree(rawmidi_file); + } + return err; +} + +int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile) +{ + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *substream; + snd_rawmidi_runtime_t *runtime; + + snd_assert(rfile != NULL, return -ENXIO); + snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO); + rmidi = rfile->rmidi; + down(&rmidi->open_mutex); + if (rfile->input != NULL) { + substream = rfile->input; + rfile->input = NULL; + runtime = substream->runtime; + runtime->trigger = 0; + substream->ops->trigger(substream, 0); + substream->ops->close(substream); + snd_rawmidi_done_buffer(runtime); + if (runtime->private_free != NULL) + runtime->private_free(substream); + kfree(runtime); + substream->runtime = NULL; + substream->opened = 0; + rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--; + } + if (rfile->output != NULL) { + substream = rfile->output; + rfile->output = NULL; + if (--substream->use_count == 0) { + runtime = substream->runtime; + if (substream->active_sensing) { + unsigned char buf = 0xfe; + /* sending single active sensing message to shut the device up */ + snd_rawmidi_kernel_write(substream, &buf, 1); + } + if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS) + substream->ops->trigger(substream, 0); + substream->ops->close(substream); + snd_rawmidi_done_buffer(runtime); + if (runtime->private_free != NULL) + runtime->private_free(substream); + kfree(runtime); + substream->runtime = NULL; + substream->opened = 0; + substream->append = 0; + } + rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--; + } + up(&rmidi->open_mutex); + dec_mod_count(rmidi->card->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +static int snd_rawmidi_release(struct inode *inode, struct file *file) +{ + snd_rawmidi_file_t *rfile; + int err; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + err = snd_rawmidi_kernel_release(rfile); + wake_up(&rfile->rmidi->open_wait); + snd_magic_kfree(rfile); + return err; +} + +int snd_rawmidi_info(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t *info) +{ + snd_rawmidi_t *rmidi = substream->rmidi; + memset(info, 0, sizeof(*info)); + info->card = rmidi->card->number; + info->device = rmidi->device; + info->subdevice = substream->number; + info->stream = substream->stream; + info->flags = rmidi->info_flags; + strcpy(info->id, rmidi->id); + strcpy(info->name, rmidi->name); + strcpy(info->subname, substream->name); + info->subdevices_count = substream->pstr->substream_count; + info->subdevices_avail = (substream->pstr->substream_count - + substream->pstr->substream_opened); + return 0; +} + +static int snd_rawmidi_info_user(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t * _info) +{ + snd_rawmidi_info_t info; + int err; + if ((err = snd_rawmidi_info(substream, &info)) < 0) + return err; + if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t))) + return -EFAULT; + return 0; +} + +int snd_rawmidi_info_select(snd_card_t *card, snd_rawmidi_info_t *info) +{ + snd_rawmidi_t *rmidi; + snd_rawmidi_str_t *pstr; + snd_rawmidi_substream_t *substream; + struct list_head *list; + if (info->device >= SNDRV_RAWMIDI_DEVICES) + return -ENXIO; + rmidi = snd_rawmidi_devices[card->number * SNDRV_RAWMIDI_DEVICES + info->device]; + if (info->stream < 0 || info->stream > 1) + return -EINVAL; + pstr = &rmidi->streams[info->stream]; + if (pstr->substream_count == 0) + return -ENOENT; + if (info->subdevice >= pstr->substream_count) + return -ENXIO; + list_for_each(list, &pstr->substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + if (substream->number == info->subdevice) + return snd_rawmidi_info(substream, info); + } + return -ENXIO; +} + +static int snd_rawmidi_info_select_user(snd_card_t *card, + snd_rawmidi_info_t *_info) +{ + int err; + snd_rawmidi_info_t info; + if (get_user(info.device, &_info->device)) + return -EFAULT; + if (get_user(info.stream, &_info->stream)) + return -EFAULT; + if (get_user(info.subdevice, &_info->subdevice)) + return -EFAULT; + if ((err = snd_rawmidi_info_select(card, &info)) < 0) + return err; + if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t))) + return -EFAULT; + return 0; +} + +int snd_rawmidi_output_params(snd_rawmidi_substream_t * substream, + snd_rawmidi_params_t * params) +{ + char *newbuf; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (substream->append && substream->use_count > 1) + return -EBUSY; + snd_rawmidi_drain_output(substream); + if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { + return -EINVAL; + } + if (params->avail_min < 1 || params->avail_min > params->buffer_size) { + return -EINVAL; + } + if (params->buffer_size != runtime->buffer_size) { + if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL) + return -ENOMEM; + kfree(runtime->buffer); + runtime->buffer = newbuf; + runtime->buffer_size = params->buffer_size; + } + runtime->avail_min = params->avail_min; + substream->active_sensing = !params->no_active_sensing; + return 0; +} + +int snd_rawmidi_input_params(snd_rawmidi_substream_t * substream, + snd_rawmidi_params_t * params) +{ + char *newbuf; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + snd_rawmidi_drain_input(substream); + if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { + return -EINVAL; + } + if (params->avail_min < 1 || params->avail_min > params->buffer_size) { + return -EINVAL; + } + if (params->buffer_size != runtime->buffer_size) { + if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL) + return -ENOMEM; + kfree(runtime->buffer); + runtime->buffer = newbuf; + runtime->buffer_size = params->buffer_size; + } + runtime->avail_min = params->avail_min; + return 0; +} + +static int snd_rawmidi_output_status(snd_rawmidi_substream_t * substream, + snd_rawmidi_status_t * status) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + memset(status, 0, sizeof(*status)); + status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT; + spin_lock_irq(&runtime->lock); + status->avail = runtime->avail; + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_rawmidi_input_status(snd_rawmidi_substream_t * substream, + snd_rawmidi_status_t * status) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + memset(status, 0, sizeof(*status)); + status->stream = SNDRV_RAWMIDI_STREAM_INPUT; + spin_lock_irq(&runtime->lock); + status->avail = runtime->avail; + status->xruns = runtime->xruns; + runtime->xruns = 0; + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_rawmidi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_rawmidi_file_t *rfile; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + if (((cmd >> 8) & 0xff) != 'W') + return -ENOTTY; + switch (cmd) { + case SNDRV_RAWMIDI_IOCTL_PVERSION: + return put_user(SNDRV_RAWMIDI_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_RAWMIDI_IOCTL_INFO: + { + snd_rawmidi_stream_t stream; + snd_rawmidi_info_t *info = (snd_rawmidi_info_t *) arg; + if (get_user(stream, &info->stream)) + return -EFAULT; + switch (stream) { + case SNDRV_RAWMIDI_STREAM_INPUT: + return snd_rawmidi_info_user(rfile->input, info); + case SNDRV_RAWMIDI_STREAM_OUTPUT: + return snd_rawmidi_info_user(rfile->output, info); + default: + return -EINVAL; + } + } + case SNDRV_RAWMIDI_IOCTL_PARAMS: + { + snd_rawmidi_params_t params; + int err; + if (copy_from_user(¶ms, (snd_rawmidi_params_t *) arg, sizeof(snd_rawmidi_params_t))) + return -EFAULT; + switch (params.stream) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + return snd_rawmidi_output_params(rfile->output, ¶ms); + case SNDRV_RAWMIDI_STREAM_INPUT: + if (rfile->input == NULL) + return -EINVAL; + return snd_rawmidi_input_params(rfile->input, ¶ms); + default: + return -EINVAL; + } + if (copy_to_user((snd_rawmidi_params_t *) arg, ¶ms, sizeof(snd_rawmidi_params_t))) + return -EFAULT; + return err; + } + case SNDRV_RAWMIDI_IOCTL_STATUS: + { + int err = 0; + snd_rawmidi_status_t status; + if (copy_from_user(&status, (snd_rawmidi_status_t *) arg, sizeof(snd_rawmidi_status_t))) + return -EFAULT; + switch (status.stream) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + err = snd_rawmidi_output_status(rfile->output, &status); + break; + case SNDRV_RAWMIDI_STREAM_INPUT: + if (rfile->input == NULL) + return -EINVAL; + err = snd_rawmidi_input_status(rfile->input, &status); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + if (copy_to_user((snd_rawmidi_status_t *) arg, &status, sizeof(snd_rawmidi_status_t))) + return -EFAULT; + return 0; + } + case SNDRV_RAWMIDI_IOCTL_DROP: + { + int val; + if (get_user(val, (long *) arg)) + return -EFAULT; + switch (val) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + return snd_rawmidi_drop_output(rfile->output); + default: + return -EINVAL; + } + } + case SNDRV_RAWMIDI_IOCTL_DRAIN: + { + int val; + if (get_user(val, (long *) arg)) + return -EFAULT; + switch (val) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + return snd_rawmidi_drain_output(rfile->output); + case SNDRV_RAWMIDI_STREAM_INPUT: + if (rfile->input == NULL) + return -EINVAL; + return snd_rawmidi_drain_input(rfile->input); + default: + return -EINVAL; + } + } +#ifdef CONFIG_SND_DEBUG + default: + snd_printk("rawmidi: unknown command = 0x%x\n", cmd); +#endif + } + return -ENOTTY; +} + +int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg) +{ + unsigned int tmp; + + tmp = card->number * SNDRV_RAWMIDI_DEVICES; + switch (cmd) { + case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE: + { + int device; + + if (get_user(device, (int *)arg)) + return -EFAULT; + device = device < 0 ? 0 : device + 1; + while (device < SNDRV_RAWMIDI_DEVICES) { + if (snd_rawmidi_devices[tmp + device]) + break; + device++; + } + if (device == SNDRV_RAWMIDI_DEVICES) + device = -1; + if (put_user(device, (int *)arg)) + return -EFAULT; + return 0; + } + case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE: + { + int val; + + if (get_user(val, (int *)arg)) + return -EFAULT; + control->prefer_rawmidi_subdevice = val; + return 0; + } + case SNDRV_CTL_IOCTL_RAWMIDI_INFO: + return snd_rawmidi_info_select_user(card, (snd_rawmidi_info_t *)arg); + } + return -ENOIOCTLCMD; +} + +void snd_rawmidi_receive_reset(snd_rawmidi_substream_t * substream) +{ + /* TODO: reset current state */ +} + +int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) +{ + unsigned long flags; + int result = 0, count1; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_receive: input is not active!!!\n"); + return -EINVAL; + } + spin_lock_irqsave(&runtime->lock, flags); + if (count == 1) { /* special case, faster code */ + substream->bytes++; + if (runtime->avail < runtime->buffer_size) { + runtime->buffer[runtime->hw_ptr++] = buffer[0]; + runtime->hw_ptr %= runtime->buffer_size; + runtime->avail++; + result++; + } else { + runtime->xruns++; + } + } else { + substream->bytes += count; + count1 = runtime->buffer_size - runtime->hw_ptr; + if (count1 > count) + count1 = count; + if (count1 > runtime->buffer_size - runtime->avail) + count1 = runtime->buffer_size - runtime->avail; + memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1); + runtime->hw_ptr += count1; + runtime->hw_ptr %= runtime->buffer_size; + runtime->avail += count1; + count -= count1; + result += count1; + if (count > 0) { + buffer += count1; + count1 = count; + if (count1 > runtime->buffer_size - runtime->avail) { + count1 = runtime->buffer_size - runtime->avail; + runtime->xruns = count - count1; + } + if (count1 > 0) { + memcpy(runtime->buffer, buffer, count1); + runtime->hw_ptr = count1; + runtime->avail += count1; + result += count1; + } + } + } + if (result > 0 && runtime->event == NULL) { + if (snd_rawmidi_ready(substream)) + wake_up(&runtime->sleep); + } + spin_unlock_irqrestore(&runtime->lock, flags); + if (result > 0 && runtime->event) + runtime->event(substream); + return result; +} + +static long snd_rawmidi_kernel_read1(snd_rawmidi_substream_t *substream, + unsigned char *buf, long count, int kernel) +{ + unsigned long flags; + long result = 0, count1; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + while (count > 0 && runtime->avail) { + count1 = runtime->buffer_size - runtime->appl_ptr; + if (count1 > count) + count1 = count; + spin_lock_irqsave(&runtime->lock, flags); + if (count1 > runtime->avail) + count1 = runtime->avail; + if (kernel) { + memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1); + } else { + if (copy_to_user(buf + result, runtime->buffer + runtime->appl_ptr, count1)) { + spin_unlock_irqrestore(&runtime->lock, flags); + return result > 0 ? result : -EFAULT; + } + } + runtime->appl_ptr += count1; + runtime->appl_ptr %= runtime->buffer_size; + runtime->avail -= count1; + spin_unlock_irqrestore(&runtime->lock, flags); + result += count1; + count -= count1; + } + return result; +} + +long snd_rawmidi_kernel_read(snd_rawmidi_substream_t *substream, unsigned char *buf, long count) +{ + substream->runtime->trigger = 1; + substream->ops->trigger(substream, 1); + return snd_rawmidi_kernel_read1(substream, buf, count, 1); +} + +static ssize_t snd_rawmidi_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + long result; + int count1; + snd_rawmidi_file_t *rfile; + snd_rawmidi_substream_t *substream; + snd_rawmidi_runtime_t *runtime; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + substream = rfile->input; + if (substream == NULL) + return -EIO; + runtime = substream->runtime; + runtime->trigger = 1; + substream->ops->trigger(substream, 1); + result = 0; + while (count > 0) { + spin_lock_irq(&runtime->lock); + while (!snd_rawmidi_ready(substream)) { + wait_queue_t wait; + if (file->f_flags & O_NONBLOCK) { + spin_unlock_irq(&runtime->lock); + return result > 0 ? result : -EAGAIN; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + spin_unlock_irq(&runtime->lock); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (!runtime->avail) + return result > 0 ? result : -EIO; + spin_lock_irq(&runtime->lock); + } + spin_unlock_irq(&runtime->lock); + count1 = snd_rawmidi_kernel_read1(substream, buf, count, 0); + result += count1; + buf += count1; + count -= count1; + } + return result; +} + +void snd_rawmidi_transmit_reset(snd_rawmidi_substream_t * substream) +{ + /* TODO: reset current state */ +} + +int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + int result; + unsigned long flags; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_transmit_empty: output is not active!!!\n"); + return 1; + } + spin_lock_irqsave(&runtime->lock, flags); + result = runtime->avail >= runtime->buffer_size; + if (result) + runtime->trigger = 1; + spin_unlock_irqrestore(&runtime->lock, flags); + return result; +} + +int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) +{ + unsigned long flags; + int result, count1; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_transmit_peek: output is not active!!!\n"); + return -EINVAL; + } + result = 0; + spin_lock_irqsave(&runtime->lock, flags); + if (runtime->avail >= runtime->buffer_size) { + /* warning: lowlevel layer MUST trigger down the hardware */ + runtime->trigger = 0; + goto __skip; + } + if (count == 1) { /* special case, faster code */ + *buffer = runtime->buffer[runtime->hw_ptr]; + result++; + } else { + count1 = runtime->buffer_size - runtime->hw_ptr; + if (count1 > count) + count1 = count; + if (count1 > runtime->buffer_size - runtime->avail) + count1 = runtime->buffer_size - runtime->avail; + memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1); + count -= count1; + result += count1; + if (count > 0) + memcpy(buffer + count1, runtime->buffer, count); + } + __skip: + spin_unlock_irqrestore(&runtime->lock, flags); + return result; +} + +int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count) +{ + unsigned long flags; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_transmit_ack: output is not active!!!\n"); + return -EINVAL; + } + spin_lock_irqsave(&runtime->lock, flags); + snd_assert(runtime->avail + count <= runtime->buffer_size, ); + runtime->hw_ptr += count; + runtime->hw_ptr %= runtime->buffer_size; + runtime->avail += count; + substream->bytes += count; + if (runtime->drain) + wake_up(&runtime->sleep); + else + if (count > 0 && runtime->event == NULL) + if (snd_rawmidi_ready(substream)) + wake_up(&runtime->sleep); + spin_unlock_irqrestore(&runtime->lock, flags); + if (count > 0 && runtime->event) + runtime->event(substream); + return count; +} + +int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) +{ + count = snd_rawmidi_transmit_peek(substream, buffer, count); + if (count < 0) + return count; + return snd_rawmidi_transmit_ack(substream, count); +} + +static long snd_rawmidi_kernel_write1(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count, int kernel) +{ + unsigned long flags; + long count1, result; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + snd_assert(buf != NULL, return -EINVAL); + snd_assert(runtime->buffer != NULL, return -EINVAL); + + result = 0; + spin_lock_irqsave(&runtime->lock, flags); + if (substream->append) { + if (runtime->avail < count) { + spin_unlock_irqrestore(&runtime->lock, flags); + return -EAGAIN; + } + } + while (count > 0 && runtime->avail > 0) { + count1 = runtime->buffer_size - runtime->appl_ptr; + if (count1 > count) + count1 = count; + if (count1 > runtime->avail) + count1 = runtime->avail; + if (kernel) { + memcpy(runtime->buffer + runtime->appl_ptr, buf, count1); + } else { + if (copy_from_user(runtime->buffer + runtime->appl_ptr, buf, count1)) { + result = result > 0 ? result : -EFAULT; + goto __end; + } + } + runtime->appl_ptr += count1; + runtime->appl_ptr %= runtime->buffer_size; + runtime->avail -= count1; + result += count1; + buf += count1; + count -= count1; + } + __end: + if (result > 0) + runtime->trigger = 1; + spin_unlock_irqrestore(&runtime->lock, flags); + substream->ops->trigger(substream, 1); + return result; +} + +long snd_rawmidi_kernel_write(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count) +{ + return snd_rawmidi_kernel_write1(substream, buf, count, 1); +} + +static ssize_t snd_rawmidi_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + long result, timeout; + int count1; + snd_rawmidi_file_t *rfile; + snd_rawmidi_runtime_t *runtime; + snd_rawmidi_substream_t *substream; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + substream = rfile->output; + runtime = substream->runtime; + /* we cannot put an atomic message to our buffer */ + if (substream->append && count > runtime->buffer_size) + return -EIO; + result = 0; + while (count > 0) { + spin_lock_irq(&runtime->lock); + while (!snd_rawmidi_ready_append(substream, count)) { + wait_queue_t wait; + if (file->f_flags & O_NONBLOCK) { + spin_unlock_irq(&runtime->lock); + return result > 0 ? result : -EAGAIN; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + spin_unlock_irq(&runtime->lock); + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(30 * HZ); + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (!runtime->avail && !timeout) + return result > 0 ? result : -EIO; + spin_lock_irq(&runtime->lock); + } + spin_unlock_irq(&runtime->lock); + count1 = snd_rawmidi_kernel_write1(substream, buf, count, 0); + if (count1 < 0) + continue; + result += count1; + buf += count1; + if (count1 < count && (file->f_flags & O_NONBLOCK)) + break; + count -= count1; + } + while (file->f_flags & O_SYNC) { + spin_lock_irq(&runtime->lock); + while (runtime->avail != runtime->buffer_size) { + wait_queue_t wait; + unsigned int last_avail = runtime->avail; + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + spin_unlock_irq(&runtime->lock); + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(30 * HZ); + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (runtime->avail == last_avail && !timeout) + return result > 0 ? result : -EIO; + spin_lock_irq(&runtime->lock); + } + spin_unlock_irq(&runtime->lock); + } + return result; +} + +static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait) +{ + snd_rawmidi_file_t *rfile; + snd_rawmidi_runtime_t *runtime; + unsigned int mask; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return 0); + if (rfile->input != NULL) { + runtime = rfile->input->runtime; + runtime->trigger = 1; + rfile->input->ops->trigger(rfile->input, 1); + poll_wait(file, &runtime->sleep, wait); + } + if (rfile->output != NULL) { + runtime = rfile->output->runtime; + poll_wait(file, &runtime->sleep, wait); + } + mask = 0; + if (rfile->input != NULL) { + if (snd_rawmidi_ready(rfile->input)) + mask |= POLLIN | POLLRDNORM; + } + if (rfile->output != NULL) { + if (snd_rawmidi_ready(rfile->output)) + mask |= POLLOUT | POLLWRNORM; + } + return mask; +} + +/* + + */ + +static void snd_rawmidi_proc_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *substream; + snd_rawmidi_runtime_t *runtime; + struct list_head *list; + + rmidi = snd_magic_cast(snd_rawmidi_t, entry->private_data, return); + snd_iprintf(buffer, "%s\n\n", rmidi->name); + down(&rmidi->open_mutex); + if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) { + list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_iprintf(buffer, + "Output %d\n" + " Tx bytes : %lu\n", + substream->number, + (unsigned long) substream->bytes); + if (substream->opened) { + runtime = substream->runtime; + snd_iprintf(buffer, + " Mode : %s\n" + " Buffer size : %lu\n" + " Avail : %lu\n", + runtime->oss ? "OSS compatible" : "native", + (unsigned long) runtime->buffer_size, + (unsigned long) runtime->avail); + } + } + } + if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) { + list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_iprintf(buffer, + "Input %d\n" + " Rx bytes : %lu\n", + substream->number, + (unsigned long) substream->bytes); + if (substream->opened) { + runtime = substream->runtime; + snd_iprintf(buffer, + " Buffer size : %lu\n" + " Avail : %lu\n" + " Overruns : %lu\n", + (unsigned long) runtime->buffer_size, + (unsigned long) runtime->avail, + (unsigned long) runtime->xruns); + } + } + } + up(&rmidi->open_mutex); +} + +/* + * Register functions + */ + +static struct file_operations snd_rawmidi_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_rawmidi_read, + write: snd_rawmidi_write, + open: snd_rawmidi_open, + release: snd_rawmidi_release, + poll: snd_rawmidi_poll, + ioctl: snd_rawmidi_ioctl, +}; + +static snd_minor_t snd_rawmidi_reg = +{ + comment: "raw midi", + f_ops: &snd_rawmidi_f_ops, +}; + +static int snd_rawmidi_alloc_substreams(snd_rawmidi_t *rmidi, + snd_rawmidi_str_t *stream, + int direction, + int count) +{ + snd_rawmidi_substream_t *substream; + int idx; + + INIT_LIST_HEAD(&stream->substreams); + for (idx = 0; idx < count; idx++) { + substream = snd_kcalloc(sizeof(snd_rawmidi_substream_t), GFP_KERNEL); + if (substream == NULL) + return -ENOMEM; + substream->stream = direction; + substream->number = idx; + substream->rmidi = rmidi; + substream->pstr = stream; + list_add_tail(&substream->list, &stream->substreams); + stream->substream_count++; + } + return 0; +} + +int snd_rawmidi_new(snd_card_t * card, char *id, int device, + int output_count, int input_count, + snd_rawmidi_t ** rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + static snd_device_ops_t ops = { + dev_free: snd_rawmidi_dev_free, + dev_register: snd_rawmidi_dev_register, + dev_unregister: snd_rawmidi_dev_unregister + }; + + snd_assert(rrawmidi != NULL, return -EINVAL); + *rrawmidi = NULL; + snd_assert(card != NULL, return -ENXIO); + rmidi = snd_magic_kcalloc(snd_rawmidi_t, 0, GFP_KERNEL); + if (rmidi == NULL) + return -ENOMEM; + rmidi->card = card; + rmidi->device = device; + init_MUTEX(&rmidi->open_mutex); + init_waitqueue_head(&rmidi->open_wait); + if (id != NULL) + strncpy(rmidi->id, id, sizeof(rmidi->id) - 1); + if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], SNDRV_RAWMIDI_STREAM_INPUT, input_count)) < 0) { + snd_rawmidi_free(rmidi); + return err; + } + if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], SNDRV_RAWMIDI_STREAM_OUTPUT, output_count)) < 0) { + snd_rawmidi_free(rmidi); + return err; + } + if ((err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)) < 0) { + snd_rawmidi_free(rmidi); + return err; + } + *rrawmidi = rmidi; + return 0; +} + +static void snd_rawmidi_free_substreams(snd_rawmidi_str_t *stream) +{ + snd_rawmidi_substream_t *substream; + + while (!list_empty(&stream->substreams)) { + substream = list_entry(stream->substreams.next, snd_rawmidi_substream_t, list); + list_del(&substream->list); + kfree(substream); + } +} + +static int snd_rawmidi_free(snd_rawmidi_t *rmidi) +{ + snd_assert(rmidi != NULL, return -ENXIO); + snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); + snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); + if (rmidi->private_free) + rmidi->private_free(rmidi); + snd_magic_kfree(rmidi); + return 0; +} + +static int snd_rawmidi_dev_free(snd_device_t *device) +{ + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + return snd_rawmidi_free(rmidi); +} + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +static void snd_rawmidi_dev_seq_free(snd_seq_device_t *device) +{ + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->private_data, return); + rmidi->seq_dev = NULL; +} +#endif + +static int snd_rawmidi_dev_register(snd_device_t *device) +{ + int idx, err; + snd_info_entry_t *entry; + char name[16]; + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + + if (rmidi->device >= SNDRV_RAWMIDI_DEVICES) + return -ENOMEM; + down(®ister_mutex); + idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device; + if (snd_rawmidi_devices[idx] != NULL) { + up(®ister_mutex); + return -EBUSY; + } + snd_rawmidi_devices[idx] = rmidi; + sprintf(name, "midiC%iD%i", rmidi->card->number, rmidi->device); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI, + rmidi->card, rmidi->device, + &snd_rawmidi_reg, name)) < 0) { + snd_printk("unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device); + snd_rawmidi_devices[idx] = NULL; + up(®ister_mutex); + return err; + } + if (rmidi->ops && rmidi->ops->dev_register && + (err = rmidi->ops->dev_register(rmidi)) < 0) { + snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); + snd_rawmidi_devices[idx] = NULL; + up(®ister_mutex); + return err; + } +#ifdef CONFIG_SND_OSSEMUL + rmidi->ossreg = 0; + if (rmidi->device == snd_midi_map[rmidi->card->number]) { + if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, + rmidi->card, 0, &snd_rawmidi_reg, name) < 0) { + snd_printk("unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0); + } else { + rmidi->ossreg++; + snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number, rmidi->name); + } + } + if (rmidi->device == snd_amidi_map[rmidi->card->number]) { + if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, + rmidi->card, 1, &snd_rawmidi_reg, name) < 0) { + snd_printk("unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1); + } else { + rmidi->ossreg++; + } + } +#endif + up(®ister_mutex); + sprintf(name, "midi%d", rmidi->device); + entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = rmidi; + entry->c.text.read_size = 1024; + entry->c.text.read = snd_rawmidi_proc_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + rmidi->proc_entry = entry; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */ + if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) { + rmidi->seq_dev->private_data = rmidi; + rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free; + sprintf(rmidi->seq_dev->name, "MIDI %d-%d", rmidi->card->number, rmidi->device); + snd_device_register(rmidi->card, rmidi->seq_dev); + } + } +#endif + return 0; +} + +static int snd_rawmidi_dev_unregister(snd_device_t *device) +{ + int idx; + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + + snd_assert(rmidi != NULL, return -ENXIO); + down(®ister_mutex); + idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device; + if (snd_rawmidi_devices[idx] != rmidi) { + up(®ister_mutex); + return -EINVAL; + } + if (rmidi->proc_entry) { + snd_info_unregister(rmidi->proc_entry); + rmidi->proc_entry = NULL; + } +#ifdef CONFIG_SND_OSSEMUL + if (rmidi->ossreg) { + if (rmidi->device == snd_midi_map[rmidi->card->number]) { + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0); + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number); + } + if (rmidi->device == snd_amidi_map[rmidi->card->number]) + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 1); + rmidi->ossreg = 0; + } +#endif + if (rmidi->ops && rmidi->ops->dev_unregister) + rmidi->ops->dev_unregister(rmidi); + snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); + snd_rawmidi_devices[idx] = NULL; + up(®ister_mutex); +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (rmidi->seq_dev) { + snd_device_free(rmidi->card, rmidi->seq_dev); + rmidi->seq_dev = NULL; + } +#endif + return snd_rawmidi_free(rmidi); +} + +void snd_rawmidi_set_ops(snd_rawmidi_t *rmidi, int stream, snd_rawmidi_ops_t *ops) +{ + struct list_head *list; + snd_rawmidi_substream_t *substream; + + list_for_each(list, &rmidi->streams[stream].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + substream->ops = ops; + } +} + +/* + * ENTRY functions + */ + +static int __init alsa_rawmidi_init(void) +{ + int i; + + snd_ctl_register_ioctl(snd_rawmidi_control_ioctl); +#ifdef CONFIG_SND_OSSEMUL + /* check device map table */ + for (i = 0; i < SNDRV_CARDS; i++) { + if (snd_midi_map[i] < 0 || snd_midi_map[i] >= SNDRV_RAWMIDI_DEVICES) { + snd_printk("invalid midi_map[%d] = %d\n", i, snd_midi_map[i]); + snd_midi_map[i] = 0; + } + if (snd_amidi_map[i] < 0 || snd_amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) { + snd_printk("invalid amidi_map[%d] = %d\n", i, snd_amidi_map[i]); + snd_amidi_map[i] = 1; + } + } +#endif /* CONFIG_SND_OSSEMUL */ + return 0; +} + +static void __exit alsa_rawmidi_exit(void) +{ + snd_ctl_unregister_ioctl(snd_rawmidi_control_ioctl); +} + +module_init(alsa_rawmidi_init) +module_exit(alsa_rawmidi_exit) + +EXPORT_SYMBOL(snd_rawmidi_output_params); +EXPORT_SYMBOL(snd_rawmidi_input_params); +EXPORT_SYMBOL(snd_rawmidi_drop_output); +EXPORT_SYMBOL(snd_rawmidi_drain_output); +EXPORT_SYMBOL(snd_rawmidi_drain_input); +EXPORT_SYMBOL(snd_rawmidi_receive_reset); +EXPORT_SYMBOL(snd_rawmidi_receive); +EXPORT_SYMBOL(snd_rawmidi_transmit_reset); +EXPORT_SYMBOL(snd_rawmidi_transmit_empty); +EXPORT_SYMBOL(snd_rawmidi_transmit_peek); +EXPORT_SYMBOL(snd_rawmidi_transmit_ack); +EXPORT_SYMBOL(snd_rawmidi_transmit); +EXPORT_SYMBOL(snd_rawmidi_new); +EXPORT_SYMBOL(snd_rawmidi_set_ops); +EXPORT_SYMBOL(snd_rawmidi_info); +EXPORT_SYMBOL(snd_rawmidi_info_select); +EXPORT_SYMBOL(snd_rawmidi_kernel_open); +EXPORT_SYMBOL(snd_rawmidi_kernel_release); +EXPORT_SYMBOL(snd_rawmidi_kernel_read); +EXPORT_SYMBOL(snd_rawmidi_kernel_write); diff -Nru a/sound/core/rtctimer.c b/sound/core/rtctimer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/rtctimer.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,225 @@ +/* + * RTC based high-frequency timer + * + * Copyright (C) 2000 Takashi Iwai + * based on rtctimer.c by Steve Ratcliffe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + *================================================================ + * For enabling this timer, apply the patch file to your kernel. + * The configure script checks the patch automatically. + * The patches, rtc-xxx.dif, are found under utils/patches, where + * xxx is the kernel version. + *================================================================ + * + */ + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 12) /* FIXME: which 2.2.x kernel? */ +#include +#else +#include +#endif + +/* use tasklet for interrupt handling */ +#define USE_TASKLET + +#define RTC_FREQ 1024 /* default frequency */ +#define NANO_SEC 1000000000L /* 10^9 in sec */ + +/* + * prototypes + */ +static int rtctimer_open(snd_timer_t *t); +static int rtctimer_close(snd_timer_t *t); +static int rtctimer_start(snd_timer_t *t); +static int rtctimer_stop(snd_timer_t *t); + + +/* + * The harware depenant description for this timer. + */ +static struct _snd_timer_hardware rtc_hw = { + flags: SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO, + ticks: 100000000L, /* FIXME: XXX */ + open: rtctimer_open, + close: rtctimer_close, + start: rtctimer_start, + stop: rtctimer_stop, +}; + +int rtctimer_freq = RTC_FREQ; /* frequency */ +static snd_timer_t *rtctimer; +static volatile int rtc_inc = 0; +static rtc_task_t rtc_task; + +/* tasklet */ +#ifdef USE_TASKLET +static struct tasklet_struct rtc_tq; +#endif + +static int +rtctimer_open(snd_timer_t *t) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int +rtctimer_close(snd_timer_t *t) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static int +rtctimer_start(snd_timer_t *timer) +{ + rtc_task_t *rtc = timer->private_data; + snd_assert(rtc != NULL, return -EINVAL); + rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); + rtc_control(rtc, RTC_PIE_ON, 0); + rtc_inc = 0; + return 0; +} + +static int +rtctimer_stop(snd_timer_t *timer) +{ + rtc_task_t *rtc = timer->private_data; + snd_assert(rtc != NULL, return -EINVAL); + rtc_control(rtc, RTC_PIE_OFF, 0); + return 0; +} + +/* + * interrupt + */ +static void rtctimer_interrupt(void *private_data) +{ + rtc_inc++; +#ifdef USE_TASKLET + tasklet_hi_schedule(&rtc_tq); +#else + snd_timer_interrupt((snd_timer_t*)private_data, rtc_inc); + rtc_inc = 0; +#endif /* USE_TASKLET */ +} + +#ifdef USE_TASKLET +static void rtctimer_interrupt2(unsigned long private_data) +{ + snd_timer_t *timer = (snd_timer_t *)private_data; + snd_assert(timer != NULL, return); + do { + snd_timer_interrupt(timer, 1); + } while (--rtc_inc > 0); +} +#endif /* USE_TASKLET */ + +static void rtctimer_private_free(snd_timer_t *timer) +{ + rtc_task_t *rtc = timer->private_data; + if (rtc) + rtc_unregister(rtc); +} + + +/* + * ENTRY functions + */ +static int __init rtctimer_init(void) +{ + int order, err; + snd_timer_t *timer; + + if (rtctimer_freq < 2 || rtctimer_freq > 8192) { + snd_printk("rtctimer: invalid frequency %d\n", rtctimer_freq); + return -EINVAL; + } + for (order = 1; rtctimer_freq > order; order <<= 1) + ; + if (rtctimer_freq != order) { + snd_printk("rtctimer: invalid frequency %d\n", rtctimer_freq); + return -EINVAL; + } + + /* Create a new timer and set up the fields */ + err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer); + if (err < 0) + return err; + +#ifdef USE_TASKLET + tasklet_init(&rtc_tq, rtctimer_interrupt2, (unsigned long)timer); +#endif /* USE_TASKLET */ + + strcpy(timer->name, "RTC timer"); + timer->hw = rtc_hw; + timer->hw.resolution = NANO_SEC / rtctimer_freq; + + /* register RTC callback */ + rtc_task.func = rtctimer_interrupt; + rtc_task.private_data = timer; + err = rtc_register(&rtc_task); + if (err < 0) { + snd_timer_global_free(timer); + return err; + } + timer->private_data = &rtc_task; + timer->private_free = rtctimer_private_free; + + err = snd_timer_global_register(timer); + if (err < 0) { + snd_timer_global_free(timer); + return err; + } + rtctimer = timer; + + return 0; +} + +static void __exit rtctimer_exit(void) +{ + if (rtctimer) { + snd_timer_global_unregister(rtctimer); + rtctimer = NULL; + } +} + + +/* + * exported stuffs + */ +module_init(rtctimer_init) +module_exit(rtctimer_exit) + +MODULE_PARM(rtctimer_freq, "i"); +MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz"); + +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; + +#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */ diff -Nru a/sound/core/seq/Makefile b/sound/core/seq/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,105 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := sq.o + +subdir-y := instr +subdir-m := $(subdir-y) +obj-y += instr/instr.o +subdir-$(CONFIG_SND_SEQUENCER_OSS) += oss +ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) + obj-y += oss/oss.o +endif + +list-multi := snd-seq-device.o snd-seq.o snd-seq-midi.o snd-seq-midi-emul.o \ + snd-seq-midi-event.o snd-seq-instr.o snd-seq-dummy.o \ + snd-seq-virmidi.o + +export-objs := seq_device.o seq.o seq_ports.o seq_instr.o seq_midi_emul.o \ + seq_midi_event.o seq_virmidi.o + +snd-seq-device-objs := seq_device.o +snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ + seq_fifo.o seq_prioq.o seq_timer.o \ + seq_system.o seq_ports.o seq_info.o seq_sync.o \ + seq_midi_clock.o seq_mtc.o seq_dtl.o +snd-seq-midi-objs := seq_midi.o +snd-seq-midi-emul-objs := seq_midi_emul.o +snd-seq-midi-event-objs := seq_midi_event.o +snd-seq-instr-objs := seq_instr.o +snd-seq-dummy-objs := seq_dummy.o +snd-seq-virmidi-objs := seq_virmidi.o + +obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_MTPAV) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_MPU401) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ALS100) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_AZT2320) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_DT0197H) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ES18XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPL3SA2) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_AD1816A) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CS4231) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_CS4232) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CS4236) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ES1688) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_GUSCLASSIC) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_GUSMAX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_INTERWAVE) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPTI93X) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_SB) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_SB16) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_SBAWE) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o snd-seq-virmidi.o +obj-$(CONFIG_SND_ES968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ALS4000) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CMIPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CS4281) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ENS1370) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ENS1371) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ES1938) += snd-seq-device.o snd-seq-midi-emul.o snd-seq.o snd-seq-instr.o +obj-$(CONFIG_SND_ES1968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_FM801) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ICE1712) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_SONICVIBES) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_VIA686) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ALI5451) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_CS46XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o +obj-$(CONFIG_SND_TRIDENT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_YMFPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o + +include $(TOPDIR)/Rules.make + +snd-seq-device.o: $(snd-seq-device-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-device-objs) + +snd-seq.o: $(snd-seq-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-objs) + +snd-seq-midi.o: $(snd-seq-midi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-objs) + +snd-seq-midi-emul.o: $(snd-seq-midi-emul-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-emul-objs) + +snd-seq-midi-event.o: $(snd-seq-midi-event-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-event-objs) + +snd-seq-instr.o: $(snd-seq-instr-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-instr-objs) + +snd-seq-dummy.o: $(snd-seq-dummy-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-dummy-objs) + +snd-seq-virmidi.o: $(snd-seq-virmidi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-virmidi-objs) diff -Nru a/sound/core/seq/instr/Makefile b/sound/core/seq/instr/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/instr/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,60 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := instr.o + +list-multi := snd-ainstr-fm.o snd-ainstr-simple.o snd-ainstr-gf1.o \ + snd-ainstr-iw.o + +export-objs := ainstr_fm.o ainstr_simple.o ainstr_gf1.o ainstr_iw.o + +snd-ainstr-fm-objs := ainstr_fm.o +snd-ainstr-simple-objs := ainstr_simple.o +snd-ainstr-gf1-objs := ainstr_gf1.o +snd-ainstr-iw-objs := ainstr_iw.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-ainstr-fm.o +obj-$(CONFIG_SND_AZT2320) += snd-ainstr-fm.o +obj-$(CONFIG_SND_DT0197H) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ES18XX) += snd-ainstr-fm.o +obj-$(CONFIG_SND_OPL3SA2) += snd-ainstr-fm.o +obj-$(CONFIG_SND_AD1816A) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CS4232) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CS4236) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ES1688) += snd-ainstr-fm.o +obj-$(CONFIG_SND_GUSCLASSIC) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_GUSMAX) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-fm.o +obj-$(CONFIG_SND_INTERWAVE) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ainstr-fm.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-ainstr-fm.o +obj-$(CONFIG_SND_OPTI93X) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SB) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SB16) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SBAWE) += snd-ainstr-fm.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ALS4000) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CMIPCI) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CS4281) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ES1938) += snd-ainstr-fm.o +obj-$(CONFIG_SND_FM801) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SONICVIBES) += snd-ainstr-fm.o +obj-$(CONFIG_SND_TRIDENT) += snd-ainstr-simple.o + +include $(TOPDIR)/Rules.make + +snd-ainstr-fm.o: $(snd-ainstr-fm-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-fm-objs) + +snd-ainstr-simple.o: $(snd-ainstr-simple-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-simple-objs) + +snd-ainstr-gf1.o: $(snd-ainstr-gf1-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-gf1-objs) + +snd-ainstr-iw.o: $(snd-ainstr-iw-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-iw-objs) diff -Nru a/sound/core/seq/instr/ainstr_fm.c b/sound/core/seq/instr/ainstr_fm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/instr/ainstr_fm.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,160 @@ +/* + * FM (OPL2/3) Instrument routines + * Copyright (c) 2000 Uros Bizjak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Uros Bizjak "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_fm_id = SNDRV_SEQ_INSTR_ID_OPL2_3; + +static int snd_seq_fm_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + fm_instrument_t *ip; + fm_xinstrument_t ix; + int idx; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + /* copy instrument data */ + if (len < sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != FM_STRU_INSTR) + return -EINVAL; + ip = (fm_instrument_t *)KINSTR_DATA(instr); + ip->share_id[0] = le32_to_cpu(ix.share_id[0]); + ip->share_id[1] = le32_to_cpu(ix.share_id[1]); + ip->share_id[2] = le32_to_cpu(ix.share_id[2]); + ip->share_id[3] = le32_to_cpu(ix.share_id[3]); + ip->type = ix.type; + for (idx = 0; idx < 4; idx++) { + ip->op[idx].am_vib = ix.op[idx].am_vib; + ip->op[idx].ksl_level = ix.op[idx].ksl_level; + ip->op[idx].attack_decay = ix.op[idx].attack_decay; + ip->op[idx].sustain_release = ix.op[idx].sustain_release; + ip->op[idx].wave_select = ix.op[idx].wave_select; + } + for (idx = 0; idx < 2; idx++) { + ip->feedback_connection[idx] = ix.feedback_connection[idx]; + } + ip->echo_delay = ix.echo_delay; + ip->echo_atten = ix.echo_atten; + ip->chorus_spread = ix.chorus_spread; + ip->trnsps = ix.trnsps; + ip->fix_dur = ix.fix_dur; + ip->modes = ix.modes; + ip->fix_key = ix.fix_key; + return 0; +} + +static int snd_seq_fm_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + fm_instrument_t *ip; + fm_xinstrument_t ix; + int idx; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (fm_instrument_t *)KINSTR_DATA(instr); + ix.stype = FM_STRU_INSTR; + ix.share_id[0] = cpu_to_le32(ip->share_id[0]); + ix.share_id[1] = cpu_to_le32(ip->share_id[1]); + ix.share_id[2] = cpu_to_le32(ip->share_id[2]); + ix.share_id[3] = cpu_to_le32(ip->share_id[3]); + ix.type = ip->type; + for (idx = 0; idx < 4; idx++) { + ix.op[idx].am_vib = ip->op[idx].am_vib; + ix.op[idx].ksl_level = ip->op[idx].ksl_level; + ix.op[idx].attack_decay = ip->op[idx].attack_decay; + ix.op[idx].sustain_release = ip->op[idx].sustain_release; + ix.op[idx].wave_select = ip->op[idx].wave_select; + } + for (idx = 0; idx < 2; idx++) { + ix.feedback_connection[idx] = ip->feedback_connection[idx]; + } + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + ix.echo_delay = ip->echo_delay; + ix.echo_atten = ip->echo_atten; + ix.chorus_spread = ip->chorus_spread; + ix.trnsps = ip->trnsps; + ix.fix_dur = ip->fix_dur; + ix.modes = ip->modes; + ix.fix_key = ip->fix_key; + return 0; +} + +static int snd_seq_fm_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + *size = sizeof(fm_xinstrument_t); + return 0; +} + +int snd_seq_fm_init(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + // ops->private_data = private_data; + ops->add_len = sizeof(fm_instrument_t); + ops->instr_type = snd_seq_fm_id; + ops->put = snd_seq_fm_put; + ops->get = snd_seq_fm_get; + ops->get_size = snd_seq_fm_get_size; + // ops->remove = snd_seq_fm_remove; + // ops->notify = snd_seq_fm_notify; + ops->next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_fm_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_fm_exit(void) +{ +} + +module_init(alsa_ainstr_fm_init) +module_exit(alsa_ainstr_fm_exit) + +EXPORT_SYMBOL(snd_seq_fm_id); +EXPORT_SYMBOL(snd_seq_fm_init); diff -Nru a/sound/core/seq/instr/ainstr_gf1.c b/sound/core/seq/instr/ainstr_gf1.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/instr/ainstr_gf1.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,361 @@ +/* + * GF1 (GUS) Patch - Instrument routines + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_gf1_id = SNDRV_SEQ_INSTR_ID_GUS_PATCH; + +static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format) +{ + unsigned int result = size; + + if (format & GF1_WAVE_16BIT) + result <<= 1; + if (format & GF1_WAVE_STEREO) + result <<= 1; + return format; +} + +static int snd_seq_gf1_copy_wave_from_stream(snd_gf1_ops_t *ops, + gf1_instrument_t *ip, + char **data, + long *len, + int atomic) +{ + gf1_wave_t *wp, *prev; + gf1_xwave_t xp; + int err, gfp_mask; + unsigned int real_size; + + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + if (*len < sizeof(xp)) + return -EINVAL; + if (copy_from_user(&xp, *data, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + wp = (gf1_wave_t *)snd_kcalloc(sizeof(*wp), gfp_mask); + if (wp == NULL) + return -ENOMEM; + wp->share_id[0] = le32_to_cpu(xp.share_id[0]); + wp->share_id[1] = le32_to_cpu(xp.share_id[1]); + wp->share_id[2] = le32_to_cpu(xp.share_id[2]); + wp->share_id[3] = le32_to_cpu(xp.share_id[3]); + wp->format = le32_to_cpu(xp.format); + wp->size = le32_to_cpu(xp.size); + wp->start = le32_to_cpu(xp.start); + wp->loop_start = le32_to_cpu(xp.loop_start); + wp->loop_end = le32_to_cpu(xp.loop_end); + wp->loop_repeat = le16_to_cpu(xp.loop_repeat); + wp->flags = xp.flags; + wp->sample_rate = le32_to_cpu(xp.sample_rate); + wp->low_frequency = le32_to_cpu(xp.low_frequency); + wp->high_frequency = le32_to_cpu(xp.high_frequency); + wp->root_frequency = le32_to_cpu(xp.root_frequency); + wp->tune = le16_to_cpu(xp.tune); + wp->balance = xp.balance; + memcpy(wp->envelope_rate, xp.envelope_rate, 6); + memcpy(wp->envelope_offset, xp.envelope_offset, 6); + wp->tremolo_sweep = xp.tremolo_sweep; + wp->tremolo_rate = xp.tremolo_rate; + wp->tremolo_depth = xp.tremolo_depth; + wp->vibrato_sweep = xp.vibrato_sweep; + wp->vibrato_rate = xp.vibrato_rate; + wp->vibrato_depth = xp.vibrato_depth; + wp->scale_frequency = le16_to_cpu(xp.scale_frequency); + wp->scale_factor = le16_to_cpu(xp.scale_factor); + real_size = snd_seq_gf1_size(wp->size, wp->format); + if (real_size > *len) { + kfree(wp); + return -ENOMEM; + } + if (ops->put_sample) { + err = ops->put_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) { + kfree(wp); + return err; + } + } + *data += real_size; + *len -= real_size; + prev = ip->wave; + if (prev) { + while (prev->next) prev = prev->next; + prev->next = wp; + } else { + ip->wave = wp; + } + return 0; +} + +static void snd_seq_gf1_wave_free(snd_gf1_ops_t *ops, + gf1_wave_t *wave, + int atomic) +{ + if (ops->remove_sample) + ops->remove_sample(ops->private_data, wave, atomic); + kfree(wave); +} + +static void snd_seq_gf1_instr_free(snd_gf1_ops_t *ops, + gf1_instrument_t *ip, + int atomic) +{ + gf1_wave_t *wave; + + while ((wave = ip->wave) != NULL) { + ip->wave = wave->next; + snd_seq_gf1_wave_free(ops, wave, atomic); + } +} + +static int snd_seq_gf1_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + gf1_instrument_t *ip; + gf1_xinstrument_t ix; + int err, gfp_mask; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + /* copy instrument data */ + if (len < sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != GF1_STRU_INSTR) + return -EINVAL; + instr_data += sizeof(ix); + len -= sizeof(ix); + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + ip->exclusion = le16_to_cpu(ix.exclusion); + ip->exclusion_group = le16_to_cpu(ix.exclusion_group); + ip->effect1 = ix.effect1; + ip->effect1_depth = ix.effect1_depth; + ip->effect2 = ix.effect2; + ip->effect2_depth = ix.effect2_depth; + /* copy layers */ + while (len > sizeof(__u32)) { + __u32 stype; + + if (copy_from_user(&stype, instr_data, sizeof(stype))) + return -EFAULT; + if (stype != GF1_STRU_WAVE) { + snd_seq_gf1_instr_free(ops, ip, atomic); + return -EINVAL; + } + err = snd_seq_gf1_copy_wave_from_stream(ops, + ip, + &instr_data, + &len, + atomic); + if (err < 0) { + snd_seq_gf1_instr_free(ops, ip, atomic); + return err; + } + } + return 0; +} + +static int snd_seq_gf1_copy_wave_to_stream(snd_gf1_ops_t *ops, + gf1_instrument_t *ip, + char **data, + long *len, + int atomic) +{ + gf1_wave_t *wp; + gf1_xwave_t xp; + int err; + unsigned int real_size; + + for (wp = ip->wave; wp; wp = wp->next) { + if (*len < sizeof(xp)) + return -ENOMEM; + memset(&xp, 0, sizeof(xp)); + xp.stype = GF1_STRU_WAVE; + xp.share_id[0] = cpu_to_le32(wp->share_id[0]); + xp.share_id[1] = cpu_to_le32(wp->share_id[1]); + xp.share_id[2] = cpu_to_le32(wp->share_id[2]); + xp.share_id[3] = cpu_to_le32(wp->share_id[3]); + xp.format = cpu_to_le32(wp->format); + xp.size = cpu_to_le32(wp->size); + xp.start = cpu_to_le32(wp->start); + xp.loop_start = cpu_to_le32(wp->loop_start); + xp.loop_end = cpu_to_le32(wp->loop_end); + xp.loop_repeat = cpu_to_le32(wp->loop_repeat); + xp.flags = wp->flags; + xp.sample_rate = cpu_to_le32(wp->sample_rate); + xp.low_frequency = cpu_to_le32(wp->low_frequency); + xp.high_frequency = cpu_to_le32(wp->high_frequency); + xp.root_frequency = cpu_to_le32(wp->root_frequency); + xp.tune = cpu_to_le16(wp->tune); + xp.balance = wp->balance; + memcpy(xp.envelope_rate, wp->envelope_rate, 6); + memcpy(xp.envelope_offset, wp->envelope_offset, 6); + xp.tremolo_sweep = wp->tremolo_sweep; + xp.tremolo_rate = wp->tremolo_rate; + xp.tremolo_depth = wp->tremolo_depth; + xp.vibrato_sweep = wp->vibrato_sweep; + xp.vibrato_rate = wp->vibrato_rate; + xp.vibrato_depth = wp->vibrato_depth; + xp.scale_frequency = cpu_to_le16(wp->scale_frequency); + xp.scale_factor = cpu_to_le16(wp->scale_factor); + if (copy_to_user(*data, &xp, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + real_size = snd_seq_gf1_size(wp->size, wp->format); + if (*len < real_size) + return -ENOMEM; + if (ops->get_sample) { + err = ops->get_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) + return err; + } + *data += wp->size; + *len -= wp->size; + } + return 0; +} + +static int snd_seq_gf1_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + gf1_instrument_t *ip; + gf1_xinstrument_t ix; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + ix.stype = GF1_STRU_INSTR; + ix.exclusion = cpu_to_le16(ip->exclusion); + ix.exclusion_group = cpu_to_le16(ip->exclusion_group); + ix.effect1 = cpu_to_le16(ip->effect1); + ix.effect1_depth = cpu_to_le16(ip->effect1_depth); + ix.effect2 = ip->effect2; + ix.effect2_depth = ip->effect2_depth; + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + instr_data += sizeof(ix); + len -= sizeof(ix); + return snd_seq_gf1_copy_wave_to_stream(ops, + ip, + &instr_data, + &len, + atomic); +} + +static int snd_seq_gf1_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + long result; + gf1_instrument_t *ip; + gf1_wave_t *wp; + + *size = 0; + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + result = sizeof(gf1_xinstrument_t); + for (wp = ip->wave; wp; wp = wp->next) { + result += sizeof(gf1_xwave_t); + result += wp->size; + } + *size = result; + return 0; +} + +static int snd_seq_gf1_remove(void *private_data, + snd_seq_kinstr_t *instr, + int atomic) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + gf1_instrument_t *ip; + + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + snd_seq_gf1_instr_free(ops, ip, atomic); + return 0; +} + +static void snd_seq_gf1_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + + if (ops->notify) + ops->notify(ops->private_data, instr, what); +} + +int snd_seq_gf1_init(snd_gf1_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + ops->private_data = private_data; + ops->kops.private_data = ops; + ops->kops.add_len = sizeof(gf1_instrument_t); + ops->kops.instr_type = snd_seq_gf1_id; + ops->kops.put = snd_seq_gf1_put; + ops->kops.get = snd_seq_gf1_get; + ops->kops.get_size = snd_seq_gf1_get_size; + ops->kops.remove = snd_seq_gf1_remove; + ops->kops.notify = snd_seq_gf1_notify; + ops->kops.next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_gf1_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_gf1_exit(void) +{ +} + +module_init(alsa_ainstr_gf1_init) +module_exit(alsa_ainstr_gf1_exit) + +EXPORT_SYMBOL(snd_seq_gf1_id); +EXPORT_SYMBOL(snd_seq_gf1_init); diff -Nru a/sound/core/seq/instr/ainstr_iw.c b/sound/core/seq/instr/ainstr_iw.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/instr/ainstr_iw.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,626 @@ +/* + * IWFFFF - AMD InterWave (tm) - Instrument routines + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_iwffff_id = SNDRV_SEQ_INSTR_ID_INTERWAVE; + +static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format) +{ + unsigned int result = size; + + if (format & IWFFFF_WAVE_16BIT) + result <<= 1; + if (format & IWFFFF_WAVE_STEREO) + result <<= 1; + return result; +} + +static void snd_seq_iwffff_copy_lfo_from_stream(iwffff_lfo_t *fp, + iwffff_xlfo_t *fx) +{ + fp->freq = le16_to_cpu(fx->freq); + fp->depth = le16_to_cpu(fx->depth); + fp->sweep = le16_to_cpu(fx->sweep); + fp->shape = fx->shape; + fp->delay = fx->delay; +} + +static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype, + iwffff_layer_t *lp, + iwffff_env_t *ep, + iwffff_xenv_t *ex, + char **data, + long *len, + int gfp_mask) +{ + __u32 stype; + iwffff_env_record_t *rp, *rp_last; + iwffff_xenv_record_t rx; + iwffff_env_point_t *pp; + iwffff_xenv_point_t px; + int points_size, idx; + + ep->flags = ex->flags; + ep->mode = ex->mode; + ep->index = ex->index; + rp_last = NULL; + while (1) { + if (*len < sizeof(__u32)) + return -EINVAL; + if (copy_from_user(&stype, data, sizeof(stype))) + return -EFAULT; + if (stype == IWFFFF_STRU_WAVE) + return 0; + if (req_stype != stype) { + if (stype == IWFFFF_STRU_ENV_RECP || + stype == IWFFFF_STRU_ENV_RECV) + return 0; + } + if (*len < sizeof(rx)) + return -EINVAL; + if (copy_from_user(&rx, *data, sizeof(rx))) + return -EFAULT; + *data += sizeof(rx); + *len -= sizeof(rx); + points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16); + if (points_size > *len) + return -EINVAL; + rp = (iwffff_env_record_t *)snd_kcalloc(sizeof(*rp) + points_size, gfp_mask); + if (rp == NULL) + return -ENOMEM; + rp->nattack = le16_to_cpu(rx.nattack); + rp->nrelease = le16_to_cpu(rx.nrelease); + rp->sustain_offset = le16_to_cpu(rx.sustain_offset); + rp->sustain_rate = le16_to_cpu(rx.sustain_rate); + rp->release_rate = le16_to_cpu(rx.release_rate); + rp->hirange = rx.hirange; + pp = (iwffff_env_point_t *)(rp + 1); + for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { + if (copy_from_user(&px, *data, sizeof(px))) + return -EFAULT; + *data += sizeof(px); + *len -= sizeof(px); + pp->offset = le16_to_cpu(px.offset); + pp->rate = le16_to_cpu(px.rate); + } + if (ep->record == NULL) { + ep->record = rp; + } else { + rp_last = rp; + } + rp_last = rp; + } + return 0; +} + +static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t *ops, + iwffff_layer_t *lp, + char **data, + long *len, + int atomic) +{ + iwffff_wave_t *wp, *prev; + iwffff_xwave_t xp; + int err, gfp_mask; + unsigned int real_size; + + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + if (*len < sizeof(xp)) + return -EINVAL; + if (copy_from_user(&xp, *data, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + wp = (iwffff_wave_t *)snd_kcalloc(sizeof(*wp), gfp_mask); + if (wp == NULL) + return -ENOMEM; + wp->share_id[0] = le32_to_cpu(xp.share_id[0]); + wp->share_id[1] = le32_to_cpu(xp.share_id[1]); + wp->share_id[2] = le32_to_cpu(xp.share_id[2]); + wp->share_id[3] = le32_to_cpu(xp.share_id[3]); + wp->format = le32_to_cpu(xp.format); + wp->address.memory = le32_to_cpu(xp.offset); + wp->size = le32_to_cpu(xp.size); + wp->start = le32_to_cpu(xp.start); + wp->loop_start = le32_to_cpu(xp.loop_start); + wp->loop_end = le32_to_cpu(xp.loop_end); + wp->loop_repeat = le16_to_cpu(xp.loop_repeat); + wp->sample_ratio = le32_to_cpu(xp.sample_ratio); + wp->attenuation = xp.attenuation; + wp->low_note = xp.low_note; + wp->high_note = xp.high_note; + real_size = snd_seq_iwffff_size(wp->size, wp->format); + if (!(wp->format & IWFFFF_WAVE_ROM)) { + if (real_size > *len) { + kfree(wp); + return -ENOMEM; + } + } + if (ops->put_sample) { + err = ops->put_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) { + kfree(wp); + return err; + } + } + if (!(wp->format & IWFFFF_WAVE_ROM)) { + *data += real_size; + *len -= real_size; + } + prev = lp->wave; + if (prev) { + while (prev->next) prev = prev->next; + prev->next = wp; + } else { + lp->wave = wp; + } + return 0; +} + +static void snd_seq_iwffff_env_free(snd_iwffff_ops_t *ops, + iwffff_env_t *env, + int atomic) +{ + iwffff_env_record_t *rec; + + while ((rec = env->record) != NULL) { + env->record = rec->next; + kfree(rec); + } +} + +static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t *ops, + iwffff_wave_t *wave, + int atomic) +{ + if (ops->remove_sample) + ops->remove_sample(ops->private_data, wave, atomic); + kfree(wave); +} + +static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t *ops, + iwffff_instrument_t *ip, + int atomic) +{ + iwffff_layer_t *layer; + iwffff_wave_t *wave; + + while ((layer = ip->layer) != NULL) { + ip->layer = layer->next; + snd_seq_iwffff_env_free(ops, &layer->penv, atomic); + snd_seq_iwffff_env_free(ops, &layer->venv, atomic); + while ((wave = layer->wave) != NULL) { + layer->wave = wave->next; + snd_seq_iwffff_wave_free(ops, wave, atomic); + } + kfree(layer); + } +} + +static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + iwffff_xinstrument_t ix; + iwffff_layer_t *lp, *prev_lp; + iwffff_xlayer_t lx; + int err, gfp_mask; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + /* copy instrument data */ + if (len < sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != IWFFFF_STRU_INSTR) + return -EINVAL; + instr_data += sizeof(ix); + len -= sizeof(ix); + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + ip->exclusion = le16_to_cpu(ix.exclusion); + ip->layer_type = le16_to_cpu(ix.layer_type); + ip->exclusion_group = le16_to_cpu(ix.exclusion_group); + ip->effect1 = ix.effect1; + ip->effect1_depth = ix.effect1_depth; + ip->effect2 = ix.effect2; + ip->effect2_depth = ix.effect2_depth; + /* copy layers */ + prev_lp = NULL; + while (len > 0) { + if (len < sizeof(iwffff_xlayer_t)) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -EINVAL; + } + if (copy_from_user(&lx, instr_data, sizeof(lx))) + return -EFAULT; + instr_data += sizeof(lx); + len -= sizeof(lx); + if (lx.stype != IWFFFF_STRU_LAYER) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -EINVAL; + } + lp = (iwffff_layer_t *)snd_kcalloc(sizeof(*lp), gfp_mask); + if (lp == NULL) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -ENOMEM; + } + if (prev_lp) { + prev_lp->next = lp; + } else { + ip->layer = lp; + } + prev_lp = lp; + lp->flags = lx.flags; + lp->velocity_mode = lx.velocity_mode; + lp->layer_event = lx.layer_event; + lp->low_range = lx.low_range; + lp->high_range = lx.high_range; + lp->pan = lx.pan; + lp->pan_freq_scale = lx.pan_freq_scale; + lp->attenuation = lx.attenuation; + snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo); + snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato); + lp->freq_scale = le16_to_cpu(lx.freq_scale); + lp->freq_center = lx.freq_center; + err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP, + lp, + &lp->penv, &lx.penv, + &instr_data, &len, + gfp_mask); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV, + lp, + &lp->venv, &lx.venv, + &instr_data, &len, + gfp_mask); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + while (len > sizeof(__u32)) { + __u32 stype; + + if (copy_from_user(&stype, instr_data, sizeof(stype))) + return -EFAULT; + if (stype != IWFFFF_STRU_WAVE) + break; + err = snd_seq_iwffff_copy_wave_from_stream(ops, + lp, + &instr_data, + &len, + atomic); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + } + } + return 0; +} + +static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t *fx, + iwffff_lfo_t *fp) +{ + fx->freq = cpu_to_le16(fp->freq); + fx->depth = cpu_to_le16(fp->depth); + fx->sweep = cpu_to_le16(fp->sweep); + fp->shape = fx->shape; + fp->delay = fx->delay; +} + +static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype, + iwffff_layer_t *lp, + iwffff_xenv_t *ex, + iwffff_env_t *ep, + char **data, + long *len) +{ + iwffff_env_record_t *rp; + iwffff_xenv_record_t rx; + iwffff_env_point_t *pp; + iwffff_xenv_point_t px; + int points_size, idx; + + ex->flags = ep->flags; + ex->mode = ep->mode; + ex->index = ep->index; + for (rp = ep->record; rp; rp = rp->next) { + if (*len < sizeof(rx)) + return -ENOMEM; + memset(&rx, 0, sizeof(rx)); + rx.stype = req_stype; + rx.nattack = cpu_to_le16(rp->nattack); + rx.nrelease = cpu_to_le16(rp->nrelease); + rx.sustain_offset = cpu_to_le16(rp->sustain_offset); + rx.sustain_rate = cpu_to_le16(rp->sustain_rate); + rx.release_rate = cpu_to_le16(rp->release_rate); + rx.hirange = cpu_to_le16(rp->hirange); + if (copy_to_user(*data, &rx, sizeof(rx))) + return -EFAULT; + *data += sizeof(rx); + *len -= sizeof(rx); + points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); + if (*len < points_size) + return -ENOMEM; + pp = (iwffff_env_point_t *)(rp + 1); + for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { + px.offset = cpu_to_le16(pp->offset); + px.rate = cpu_to_le16(pp->rate); + if (copy_to_user(*data, &px, sizeof(px))) + return -EFAULT; + *data += sizeof(px); + *len -= sizeof(px); + } + } + return 0; +} + +static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t *ops, + iwffff_layer_t *lp, + char **data, + long *len, + int atomic) +{ + iwffff_wave_t *wp; + iwffff_xwave_t xp; + int err; + unsigned int real_size; + + for (wp = lp->wave; wp; wp = wp->next) { + if (*len < sizeof(xp)) + return -ENOMEM; + memset(&xp, 0, sizeof(xp)); + xp.stype = IWFFFF_STRU_WAVE; + xp.share_id[0] = cpu_to_le32(wp->share_id[0]); + xp.share_id[1] = cpu_to_le32(wp->share_id[1]); + xp.share_id[2] = cpu_to_le32(wp->share_id[2]); + xp.share_id[3] = cpu_to_le32(wp->share_id[3]); + xp.format = cpu_to_le32(wp->format); + if (wp->format & IWFFFF_WAVE_ROM) + xp.offset = cpu_to_le32(wp->address.memory); + xp.size = cpu_to_le32(wp->size); + xp.start = cpu_to_le32(wp->start); + xp.loop_start = cpu_to_le32(wp->loop_start); + xp.loop_end = cpu_to_le32(wp->loop_end); + xp.loop_repeat = cpu_to_le32(wp->loop_repeat); + xp.sample_ratio = cpu_to_le32(wp->sample_ratio); + xp.attenuation = wp->attenuation; + xp.low_note = wp->low_note; + xp.high_note = wp->high_note; + if (copy_to_user(*data, &xp, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + real_size = snd_seq_iwffff_size(wp->size, wp->format); + if (!(wp->format & IWFFFF_WAVE_ROM)) { + if (*len < real_size) + return -ENOMEM; + } + if (ops->get_sample) { + err = ops->get_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) + return err; + } + if (!(wp->format & IWFFFF_WAVE_ROM)) { + *data += real_size; + *len -= real_size; + } + } + return 0; +} + +static int snd_seq_iwffff_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + iwffff_xinstrument_t ix; + iwffff_layer_t *lp; + iwffff_xlayer_t lx; + char *layer_instr_data; + int err; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + ix.stype = IWFFFF_STRU_INSTR; + ix.exclusion = cpu_to_le16(ip->exclusion); + ix.layer_type = cpu_to_le16(ip->layer_type); + ix.exclusion_group = cpu_to_le16(ip->exclusion_group); + ix.effect1 = cpu_to_le16(ip->effect1); + ix.effect1_depth = cpu_to_le16(ip->effect1_depth); + ix.effect2 = ip->effect2; + ix.effect2_depth = ip->effect2_depth; + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + instr_data += sizeof(ix); + len -= sizeof(ix); + for (lp = ip->layer; lp; lp = lp->next) { + if (len < sizeof(lx)) + return -ENOMEM; + memset(&lx, 0, sizeof(lx)); + lx.stype = IWFFFF_STRU_LAYER; + lx.flags = lp->flags; + lx.velocity_mode = lp->velocity_mode; + lx.layer_event = lp->layer_event; + lx.low_range = lp->low_range; + lx.high_range = lp->high_range; + lx.pan = lp->pan; + lx.pan_freq_scale = lp->pan_freq_scale; + lx.attenuation = lp->attenuation; + snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo); + snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato); + layer_instr_data = instr_data; + instr_data += sizeof(lx); + len -= sizeof(lx); + err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP, + lp, + &lx.penv, &lp->penv, + &instr_data, &len); + if (err < 0) + return err; + err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV, + lp, + &lx.venv, &lp->venv, + &instr_data, &len); + if (err < 0) + return err; + /* layer structure updating is now finished */ + if (copy_to_user(layer_instr_data, &lx, sizeof(lx))) + return -EFAULT; + err = snd_seq_iwffff_copy_wave_to_stream(ops, + lp, + &instr_data, + &len, + atomic); + if (err < 0) + return err; + } + return 0; +} + +static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t *ep) +{ + long result = 0; + iwffff_env_record_t *rp; + + for (rp = ep->record; rp; rp = rp->next) { + result += sizeof(iwffff_xenv_record_t); + result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); + } + return 0; +} + +static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t *lp) +{ + long result = 0; + iwffff_wave_t *wp; + + for (wp = lp->wave; wp; wp = wp->next) { + result += sizeof(iwffff_xwave_t); + if (!(wp->format & IWFFFF_WAVE_ROM)) + result += wp->size; + } + return result; +} + +static int snd_seq_iwffff_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + long result; + iwffff_instrument_t *ip; + iwffff_layer_t *lp; + + *size = 0; + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + result = sizeof(iwffff_xinstrument_t); + for (lp = ip->layer; lp; lp = lp->next) { + result += sizeof(iwffff_xlayer_t); + result += snd_seq_iwffff_env_size_in_stream(&lp->penv); + result += snd_seq_iwffff_env_size_in_stream(&lp->venv); + result += snd_seq_iwffff_wave_size_in_stream(lp); + } + *size = result; + return 0; +} + +static int snd_seq_iwffff_remove(void *private_data, + snd_seq_kinstr_t *instr, + int atomic) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + snd_seq_iwffff_instr_free(ops, ip, atomic); + return 0; +} + +static void snd_seq_iwffff_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + + if (ops->notify) + ops->notify(ops->private_data, instr, what); +} + +int snd_seq_iwffff_init(snd_iwffff_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + ops->private_data = private_data; + ops->kops.private_data = ops; + ops->kops.add_len = sizeof(iwffff_instrument_t); + ops->kops.instr_type = snd_seq_iwffff_id; + ops->kops.put = snd_seq_iwffff_put; + ops->kops.get = snd_seq_iwffff_get; + ops->kops.get_size = snd_seq_iwffff_get_size; + ops->kops.remove = snd_seq_iwffff_remove; + ops->kops.notify = snd_seq_iwffff_notify; + ops->kops.next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_iw_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_iw_exit(void) +{ +} + +module_init(alsa_ainstr_iw_init) +module_exit(alsa_ainstr_iw_exit) + +EXPORT_SYMBOL(snd_seq_iwffff_id); +EXPORT_SYMBOL(snd_seq_iwffff_init); diff -Nru a/sound/core/seq/instr/ainstr_simple.c b/sound/core/seq/instr/ainstr_simple.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/instr/ainstr_simple.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,218 @@ +/* + * Simple (MOD player) - Instrument routines + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_simple_id = SNDRV_SEQ_INSTR_ID_SIMPLE; + +static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format) +{ + unsigned int result = size; + + if (format & SIMPLE_WAVE_16BIT) + result <<= 1; + if (format & SIMPLE_WAVE_STEREO) + result <<= 1; + return result; +} + +static void snd_seq_simple_instr_free(snd_simple_ops_t *ops, + simple_instrument_t *ip, + int atomic) +{ + if (ops->remove_sample) + ops->remove_sample(ops->private_data, ip, atomic); +} + +static int snd_seq_simple_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + simple_instrument_t *ip; + simple_xinstrument_t ix; + int err, gfp_mask; + unsigned int real_size; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + /* copy instrument data */ + if (len < sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != SIMPLE_STRU_INSTR) + return -EINVAL; + instr_data += sizeof(ix); + len -= sizeof(ix); + ip = (simple_instrument_t *)KINSTR_DATA(instr); + ip->share_id[0] = le32_to_cpu(ix.share_id[0]); + ip->share_id[1] = le32_to_cpu(ix.share_id[1]); + ip->share_id[2] = le32_to_cpu(ix.share_id[2]); + ip->share_id[3] = le32_to_cpu(ix.share_id[3]); + ip->format = le32_to_cpu(ix.format); + ip->size = le32_to_cpu(ix.size); + ip->start = le32_to_cpu(ix.start); + ip->loop_start = le32_to_cpu(ix.loop_start); + ip->loop_end = le32_to_cpu(ix.loop_end); + ip->loop_repeat = le16_to_cpu(ix.loop_repeat); + ip->effect1 = ix.effect1; + ip->effect1_depth = ix.effect1_depth; + ip->effect2 = ix.effect2; + ip->effect2_depth = ix.effect2_depth; + real_size = snd_seq_simple_size(ip->size, ip->format); + if (len < real_size) + return -EINVAL; + if (ops->put_sample) { + err = ops->put_sample(ops->private_data, ip, + instr_data, real_size, atomic); + if (err < 0) + return err; + } + return 0; +} + +static int snd_seq_simple_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + simple_instrument_t *ip; + simple_xinstrument_t ix; + int err; + unsigned int real_size; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (simple_instrument_t *)KINSTR_DATA(instr); + ix.stype = SIMPLE_STRU_INSTR; + ix.share_id[0] = cpu_to_le32(ip->share_id[0]); + ix.share_id[1] = cpu_to_le32(ip->share_id[1]); + ix.share_id[2] = cpu_to_le32(ip->share_id[2]); + ix.share_id[3] = cpu_to_le32(ip->share_id[3]); + ix.format = cpu_to_le32(ip->format); + ix.size = cpu_to_le32(ip->size); + ix.start = cpu_to_le32(ip->start); + ix.loop_start = cpu_to_le32(ip->loop_start); + ix.loop_end = cpu_to_le32(ip->loop_end); + ix.loop_repeat = cpu_to_le32(ip->loop_repeat); + ix.effect1 = cpu_to_le16(ip->effect1); + ix.effect1_depth = cpu_to_le16(ip->effect1_depth); + ix.effect2 = ip->effect2; + ix.effect2_depth = ip->effect2_depth; + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + instr_data += sizeof(ix); + len -= sizeof(ix); + real_size = snd_seq_simple_size(ip->size, ip->format); + if (len < real_size) + return -ENOMEM; + if (ops->get_sample) { + err = ops->get_sample(ops->private_data, ip, + instr_data, real_size, atomic); + if (err < 0) + return err; + } + return 0; +} + +static int snd_seq_simple_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + simple_instrument_t *ip; + + ip = (simple_instrument_t *)KINSTR_DATA(instr); + *size = sizeof(simple_xinstrument_t) + snd_seq_simple_size(ip->size, ip->format); + return 0; +} + +static int snd_seq_simple_remove(void *private_data, + snd_seq_kinstr_t *instr, + int atomic) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + simple_instrument_t *ip; + + ip = (simple_instrument_t *)KINSTR_DATA(instr); + snd_seq_simple_instr_free(ops, ip, atomic); + return 0; +} + +static void snd_seq_simple_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + + if (ops->notify) + ops->notify(ops->private_data, instr, what); +} + +int snd_seq_simple_init(snd_simple_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + ops->private_data = private_data; + ops->kops.private_data = ops; + ops->kops.add_len = sizeof(simple_instrument_t); + ops->kops.instr_type = snd_seq_simple_id; + ops->kops.put = snd_seq_simple_put; + ops->kops.get = snd_seq_simple_get; + ops->kops.get_size = snd_seq_simple_get_size; + ops->kops.remove = snd_seq_simple_remove; + ops->kops.notify = snd_seq_simple_notify; + ops->kops.next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_simple_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_simple_exit(void) +{ +} + +module_init(alsa_ainstr_simple_init) +module_exit(alsa_ainstr_simple_exit) + +EXPORT_SYMBOL(snd_seq_simple_id); +EXPORT_SYMBOL(snd_seq_simple_init); diff -Nru a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,19 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := oss.o + +list-multi := snd-seq-oss.o + +snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \ + seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \ + seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o + +obj-$(CONFIG_SND_SEQUENCER_OSS) += snd-seq-oss.o + +include $(TOPDIR)/Rules.make + +snd-seq-oss.o: $(snd-seq-oss-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-oss-objs) diff -Nru a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,308 @@ +/* + * OSS compatible sequencer driver + * + * registration of device and proc + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "seq_oss_device.h" +#include "seq_oss_synth.h" + +/* + * module option + */ +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("OSS-compatible sequencer module"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +#ifdef SNDRV_SEQ_OSS_DEBUG +MODULE_PARM(seq_oss_debug, "i"); +MODULE_PARM_DESC(seq_oss_debug, "debug option"); +int seq_oss_debug = 0; +#endif + + +/* + * prototypes + */ +static int register_device(void); +static void unregister_device(void); +static int register_proc(void); +static void unregister_proc(void); + +static int odev_open(struct inode *inode, struct file *file); +static int odev_release(struct inode *inode, struct file *file); +static ssize_t odev_read(struct file *file, char *buf, size_t count, loff_t *offset); +static ssize_t odev_write(struct file *file, const char *buf, size_t count, loff_t *offset); +static int odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static unsigned int odev_poll(struct file *file, poll_table * wait); +#ifdef CONFIG_PROC_FS +static void info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf); +#endif + + +/* + * module interface + */ + +static int __init alsa_seq_oss_init(void) +{ + int rc; + static snd_seq_dev_ops_t ops = { + snd_seq_oss_synth_register, + snd_seq_oss_synth_unregister, + }; + + if ((rc = register_device()) < 0) + return rc; + if ((rc = register_proc()) < 0) { + unregister_device(); + return rc; + } + if ((rc = snd_seq_oss_create_client()) < 0) { + unregister_proc(); + unregister_device(); + return rc; + } + + if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops, + sizeof(snd_seq_oss_reg_t))) < 0) { + snd_seq_oss_delete_client(); + unregister_proc(); + unregister_device(); + return rc; + } + + /* success */ + snd_seq_oss_synth_init(); + return 0; +} + +static void __exit alsa_seq_oss_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS); + snd_seq_oss_delete_client(); + unregister_proc(); + unregister_device(); +} + +module_init(alsa_seq_oss_init) +module_exit(alsa_seq_oss_exit) + +/* + * ALSA minor device interface + */ + +static DECLARE_MUTEX(register_mutex); + +static int +odev_open(struct inode *inode, struct file *file) +{ + int level, rc; + + if (minor(inode->i_rdev) == SNDRV_MINOR_OSS_MUSIC) + level = SNDRV_SEQ_OSS_MODE_MUSIC; + else + level = SNDRV_SEQ_OSS_MODE_SYNTH; + + down(®ister_mutex); + rc = snd_seq_oss_open(file, level); + up(®ister_mutex); + + return rc; +} + +static int +odev_release(struct inode *inode, struct file *file) +{ + seq_oss_devinfo_t *dp; + + if ((dp = file->private_data) == NULL) + return 0; + + snd_seq_oss_drain_write(dp); + + down(®ister_mutex); + snd_seq_oss_release(dp); + up(®ister_mutex); + + return 0; +} + +static ssize_t +odev_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return -EIO); + return snd_seq_oss_read(dp, buf, count); +} + + +static ssize_t +odev_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return -EIO); + return snd_seq_oss_write(dp, buf, count, file); +} + +static int +odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return -EIO); + return snd_seq_oss_ioctl(dp, cmd, arg); +} + + +static unsigned int +odev_poll(struct file *file, poll_table * wait) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return 0); + return snd_seq_oss_poll(dp, file, wait); +} + +/* + * registration of sequencer minor device + */ + +static struct file_operations seq_oss_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: odev_read, + write: odev_write, + open: odev_open, + release: odev_release, + poll: odev_poll, + ioctl: odev_ioctl, +}; + +static snd_minor_t seq_oss_reg = { + comment: "sequencer", + f_ops: &seq_oss_f_ops, +}; + +static int __init +register_device(void) +{ + int rc; + + down(®ister_mutex); + if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, + NULL, 0, + &seq_oss_reg, + SNDRV_SEQ_OSS_DEVNAME)) < 0) { + snd_printk("can't register device seq\n"); + up(®ister_mutex); + return rc; + } + if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, + NULL, 0, + &seq_oss_reg, + SNDRV_SEQ_OSS_DEVNAME)) < 0) { + snd_printk("can't register device music\n"); + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0); + up(®ister_mutex); + return rc; + } + debug_printk(("device registered\n")); + up(®ister_mutex); + return 0; +} + +static void +unregister_device(void) +{ + down(®ister_mutex); + debug_printk(("device unregistered\n")); + if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0) + + snd_printk("error unregister device music\n"); + if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0) + snd_printk("error unregister device seq\n"); + up(®ister_mutex); +} + +/* + * /proc interface + */ + +#ifdef CONFIG_PROC_FS + +static snd_info_entry_t *info_entry; + +static void +info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf) +{ + down(®ister_mutex); + snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR); + snd_seq_oss_system_info_read(buf); + snd_seq_oss_synth_info_read(buf); + snd_seq_oss_midi_info_read(buf); + up(®ister_mutex); +} + +#endif /* CONFIG_PROC_FS */ + +static int __init +register_proc(void) +{ +#ifdef CONFIG_PROC_FS + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, SNDRV_SEQ_OSS_PROCNAME, snd_seq_root); + if (entry == NULL) + return -ENOMEM; + + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = NULL; + entry->c.text.read_size = 1024; + entry->c.text.read = info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + info_entry = entry; +#endif + return 0; +} + +static void +unregister_proc(void) +{ +#ifdef CONFIG_PROC_FS + if (info_entry) + snd_info_unregister(info_entry); + info_entry = NULL; +#endif +} diff -Nru a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_device.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,199 @@ +/* + * OSS compatible sequencer driver + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_DEVICE_H +#define __SEQ_OSS_DEVICE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* enable debug print */ +#define SNDRV_SEQ_OSS_DEBUG + +/* max. applications */ +#define SNDRV_SEQ_OSS_MAX_CLIENTS 16 +#define SNDRV_SEQ_OSS_MAX_SYNTH_DEVS 16 +#define SNDRV_SEQ_OSS_MAX_MIDI_DEVS 32 + +/* version */ +#define SNDRV_SEQ_OSS_MAJOR_VERSION 0 +#define SNDRV_SEQ_OSS_MINOR_VERSION 1 +#define SNDRV_SEQ_OSS_TINY_VERSION 8 +#define SNDRV_SEQ_OSS_VERSION_STR "0.1.8" + +/* device and proc interface name */ +#define SNDRV_SEQ_OSS_DEVNAME "seq_oss" +#define SNDRV_SEQ_OSS_PROCNAME "oss" + + +/* + * type definitions + */ + +typedef struct seq_oss_devinfo_t seq_oss_devinfo_t; +typedef struct seq_oss_writeq_t seq_oss_writeq_t; +typedef struct seq_oss_readq_t seq_oss_readq_t; +typedef struct seq_oss_timer_t seq_oss_timer_t; +typedef struct seq_oss_synthinfo_t seq_oss_synthinfo_t; +typedef struct seq_oss_synth_sysex_t seq_oss_synth_sysex_t; +typedef struct seq_oss_chinfo_t seq_oss_chinfo_t; +typedef unsigned int reltime_t; +typedef unsigned int abstime_t; +typedef union evrec_t evrec_t; + + +/* + * synthesizer channel information + */ +struct seq_oss_chinfo_t { + int note, vel; +}; + +/* + * synthesizer information + */ +struct seq_oss_synthinfo_t { + snd_seq_oss_arg_t arg; + seq_oss_chinfo_t *ch; + seq_oss_synth_sysex_t *sysex; + int nr_voices; + int opened; + int is_midi; + int midi_mapped; +}; + + +/* + * sequencer client information + */ + +struct seq_oss_devinfo_t { + + int index; /* application index */ + int cseq; /* sequencer client number */ + int port; /* sequencer port number */ + int queue; /* sequencer queue number */ + + snd_seq_addr_t addr; /* address of this device */ + + int seq_mode; /* sequencer mode */ + int file_mode; /* file access */ + + /* midi device table */ + int max_mididev; + + /* synth device table */ + int max_synthdev; + seq_oss_synthinfo_t synths[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; + int synth_opened; + + /* output queue */ + seq_oss_writeq_t *writeq; + + /* midi input queue */ + seq_oss_readq_t *readq; + + /* timer */ + seq_oss_timer_t *timer; +}; + + +/* + * function prototypes + */ + +/* create/delete OSS sequencer client */ +int snd_seq_oss_create_client(void); +int snd_seq_oss_delete_client(void); + +/* device file interface */ +int snd_seq_oss_open(struct file *file, int level); +void snd_seq_oss_release(seq_oss_devinfo_t *dp); +int snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long arg); +int snd_seq_oss_read(seq_oss_devinfo_t *dev, char *buf, int count); +int snd_seq_oss_write(seq_oss_devinfo_t *dp, const char *buf, int count, struct file *opt); +unsigned int snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait); + +void snd_seq_oss_reset(seq_oss_devinfo_t *dp); +void snd_seq_oss_drain_write(seq_oss_devinfo_t *dp); + +/* */ +void snd_seq_oss_process_queue(seq_oss_devinfo_t *dp, abstime_t time); + + +/* proc interface */ +void snd_seq_oss_system_info_read(snd_info_buffer_t *buf); +void snd_seq_oss_midi_info_read(snd_info_buffer_t *buf); +void snd_seq_oss_synth_info_read(snd_info_buffer_t *buf); +void snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf); + +/* file mode macros */ +#define is_read_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_READ) +#define is_write_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_WRITE) +#define is_nonblock_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_NONBLOCK) + +/* dispatch event */ +inline static int +snd_seq_oss_dispatch(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int atomic, int hop) +{ + return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop); +} + +/* ioctl */ +inline static int +snd_seq_oss_control(seq_oss_devinfo_t *dp, unsigned int type, void *arg) +{ + return snd_seq_kernel_client_ctl(dp->cseq, type, arg); +} + +/* fill the addresses in header */ +inline static void +snd_seq_oss_fill_addr(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, + int dest_client, int dest_port) +{ + ev->queue = dp->queue; + ev->source = dp->addr; + ev->dest.client = dest_client; + ev->dest.port = dest_port; +} + + +/* misc. functions for proc interface */ +char *enabled_str(int bool); +char *filemode_str(int fmode); + + +/* for debug */ +#ifdef SNDRV_SEQ_OSS_DEBUG +extern int seq_oss_debug; +#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printk x; } while (0) +#else +#define debug_printk(x) /**/ +#endif + +#endif /* __SEQ_OSS_DEVICE_H */ diff -Nru a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_event.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,448 @@ +/* + * OSS compatible sequencer driver + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_device.h" +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "seq_oss_event.h" +#include "seq_oss_timer.h" +#include +#include "seq_oss_readq.h" +#include "seq_oss_writeq.h" + + +/* + * prototypes + */ +static int extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); +static int chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int chn_common_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int timing_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int local_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); +static int note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev); +static int note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev); +static int set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev); +static int set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev); +static int set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev); + + +/* + * convert an OSS event to ALSA event + * return 0 : enqueued + * non-zero : invalid - ignored + */ + +int +snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + switch (q->s.code) { + case SEQ_EXTENDED: + return extended_event(dp, q, ev); + + case EV_CHN_VOICE: + return chn_voice_event(dp, q, ev); + + case EV_CHN_COMMON: + return chn_common_event(dp, q, ev); + + case EV_TIMING: + return timing_event(dp, q, ev); + + case EV_SEQ_LOCAL: + return local_event(dp, q, ev); + + case EV_SYSEX: + return snd_seq_oss_synth_sysex(dp, q->x.dev, q->x.buf, ev); + + case SEQ_MIDIPUTC: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + /* put a midi byte */ + if (! is_write_mode(dp->file_mode)) + break; + if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE)) + break; + if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE) + return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev); + break; + + case SEQ_ECHO: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + return set_echo_event(dp, q, ev); + + case SEQ_PRIVATE: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + return snd_seq_oss_synth_raw_event(dp, q->c[1], q->c, ev); + + default: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + return old_event(dp, q, ev); + } + return -EINVAL; +} + +/* old type events: mode1 only */ +static int +old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + switch (q->s.code) { + case SEQ_NOTEOFF: + return note_off_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev); + + case SEQ_NOTEON: + return note_on_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev); + + case SEQ_WAIT: + /* skip */ + break; + + case SEQ_PGMCHANGE: + return set_control_event(dp, 0, SNDRV_SEQ_EVENT_PGMCHANGE, + q->n.chn, 0, q->n.note, ev); + + case SEQ_SYNCTIMER: + return snd_seq_oss_timer_reset(dp->timer); + } + + return -EINVAL; +} + +/* 8bytes extended event: mode1 only */ +static int +extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + int val; + + switch (q->e.cmd) { + case SEQ_NOTEOFF: + return note_off_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev); + + case SEQ_NOTEON: + return note_on_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev); + + case SEQ_PGMCHANGE: + return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_PGMCHANGE, + q->e.chn, 0, q->e.p1, ev); + + case SEQ_AFTERTOUCH: + return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CHANPRESS, + q->e.chn, 0, q->e.p1, ev); + + case SEQ_BALANCE: + /* convert -128:127 to 0:127 */ + val = (char)q->e.p1; + val = (val + 128) / 2; + return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CONTROLLER, + q->e.chn, CTL_PAN, val, ev); + + case SEQ_CONTROLLER: + val = ((short)q->e.p3 << 8) | (short)q->e.p2; + switch (q->e.p1) { + case CTRL_PITCH_BENDER: /* SEQ1 V2 control */ + /* -0x2000:0x1fff */ + return set_control_event(dp, q->e.dev, + SNDRV_SEQ_EVENT_PITCHBEND, + q->e.chn, 0, val, ev); + case CTRL_PITCH_BENDER_RANGE: + /* conversion: 100/semitone -> 128/semitone */ + return set_control_event(dp, q->e.dev, + SNDRV_SEQ_EVENT_REGPARAM, + q->e.chn, 0, val*128/100, ev); + default: + return set_control_event(dp, q->e.dev, + SNDRV_SEQ_EVENT_CONTROL14, + q->e.chn, q->e.p1, val, ev); + } + + case SEQ_VOLMODE: + return snd_seq_oss_synth_raw_event(dp, q->e.dev, q->c, ev); + + } + return -EINVAL; +} + +/* channel voice events: mode1 and 2 */ +static int +chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + if (q->v.chn >= 32) + return -EINVAL; + switch (q->v.cmd) { + case MIDI_NOTEON: + return note_on_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev); + + case MIDI_NOTEOFF: + return note_off_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev); + + case MIDI_KEY_PRESSURE: + return set_note_event(dp, q->v.dev, SNDRV_SEQ_EVENT_KEYPRESS, + q->v.chn, q->v.note, q->v.parm, ev); + + } + return -EINVAL; +} + +/* channel common events: mode1 and 2 */ +static int +chn_common_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + if (q->l.chn >= 32) + return -EINVAL; + switch (q->l.cmd) { + case MIDI_PGM_CHANGE: + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PGMCHANGE, + q->l.chn, 0, q->l.p1, ev); + + case MIDI_CTL_CHANGE: + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CONTROLLER, + q->l.chn, q->l.p1, q->l.val, ev); + + case MIDI_PITCH_BEND: + /* conversion: 0:0x3fff -> -0x2000:0x1fff */ + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PITCHBEND, + q->l.chn, 0, q->l.val - 8192, ev); + + case MIDI_CHN_PRESSURE: + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CHANPRESS, + q->l.chn, 0, q->l.val, ev); + } + return -EINVAL; +} + +/* timer events: mode1 and mode2 */ +static int +timing_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + switch (q->t.cmd) { + case TMR_ECHO: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return set_echo_event(dp, q, ev); + else { + evrec_t tmp; + memset(&tmp, 0, sizeof(tmp)); + /* XXX: only for little-endian! */ + tmp.echo = (q->t.time << 8) | SEQ_ECHO; + return set_echo_event(dp, &tmp, ev); + } + + case TMR_STOP: + if (dp->seq_mode) + return snd_seq_oss_timer_stop(dp->timer); + return 0; + + case TMR_CONTINUE: + if (dp->seq_mode) + return snd_seq_oss_timer_continue(dp->timer); + return 0; + + case TMR_TEMPO: + if (dp->seq_mode) + return snd_seq_oss_timer_tempo(dp->timer, q->t.time); + return 0; + } + + return -EINVAL; +} + +/* local events: mode1 and 2 */ +static int +local_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + return -EINVAL; +} + +/* + * process note-on event for OSS synth + * three different modes are available: + * - SNDRV_SEQ_OSS_PROCESS_EVENTS (for one-voice per channel mode) + * Accept note 255 as volume change. + * - SNDRV_SEQ_OSS_PASS_EVENTS + * Pass all events to lowlevel driver anyway + * - SNDRV_SEQ_OSS_PROCESS_KEYPRESS (mostly for Emu8000) + * Use key-pressure if note >= 128 + */ +static int +note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev) +{ + seq_oss_synthinfo_t *info = &dp->synths[dev]; + switch (info->arg.event_passing) { + case SNDRV_SEQ_OSS_PROCESS_EVENTS: + if (! info->ch || ch < 0 || ch >= info->nr_voices) { + /* pass directly */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + } + + if (note == 255 && info->ch[ch].note >= 0) { + /* volume control */ + int type; + //if (! vel) + /* set volume to zero -- note off */ + // type = SNDRV_SEQ_EVENT_NOTEOFF; + //else + if (info->ch[ch].vel) + /* sample already started -- volume change */ + type = SNDRV_SEQ_EVENT_KEYPRESS; + else + /* sample not started -- start now */ + type = SNDRV_SEQ_EVENT_NOTEON; + info->ch[ch].vel = vel; + return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev); + } else if (note >= 128) + return -EINVAL; /* invalid */ + + if (note != info->ch[ch].note && info->ch[ch].note >= 0) + /* note changed - note off at beginning */ + set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, info->ch[ch].note, 0, ev); + /* set current status */ + info->ch[ch].note = note; + info->ch[ch].vel = vel; + if (vel) /* non-zero velocity - start the note now */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + return -EINVAL; + + case SNDRV_SEQ_OSS_PASS_EVENTS: + /* pass the event anyway */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + + case SNDRV_SEQ_OSS_PROCESS_KEYPRESS: + if (note >= 128) /* key pressure: shifted by 128 */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_KEYPRESS, ch, note - 128, vel, ev); + else /* normal note-on event */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + } + return -EINVAL; +} + +/* + * process note-off event for OSS synth + */ +static int +note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev) +{ + seq_oss_synthinfo_t *info = &dp->synths[dev]; + switch (info->arg.event_passing) { + case SNDRV_SEQ_OSS_PROCESS_EVENTS: + if (! info->ch || ch < 0 || ch >= info->nr_voices) { + /* pass directly */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + } + + if (info->ch[ch].note >= 0) { + note = info->ch[ch].note; + info->ch[ch].vel = 0; + info->ch[ch].note = -1; + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev); + } + return -EINVAL; /* invalid */ + + case SNDRV_SEQ_OSS_PASS_EVENTS: + case SNDRV_SEQ_OSS_PROCESS_KEYPRESS: + /* pass the event anyway */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev); + + } + return -EINVAL; +} + +/* + * create a note event + */ +static int +set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + ev->type = type; + snd_seq_oss_synth_addr(dp, dev, ev); + ev->data.note.channel = ch; + ev->data.note.note = note; + ev->data.note.velocity = vel; + + return 0; +} + +/* + * create a control event + */ +static int +set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + ev->type = type; + snd_seq_oss_synth_addr(dp, dev, ev); + ev->data.control.channel = ch; + ev->data.control.param = param; + ev->data.control.value = val; + + return 0; +} + +/* + * create an echo event + */ +static int +set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev) +{ + ev->type = SNDRV_SEQ_EVENT_ECHO; + /* echo back to itself */ + snd_seq_oss_fill_addr(dp, ev, dp->addr.client, dp->addr.port); + memcpy(&ev->data, rec, LONG_EVENT_SIZE); + return 0; +} + +/* + * event input callback from ALSA sequencer: + * the echo event is processed here. + */ +int +snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, + int atomic, int hop) +{ + seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data; + evrec_t *rec; + + if (ev->type != SNDRV_SEQ_EVENT_ECHO) + return snd_seq_oss_midi_input(ev, direct, private_data); + + if (ev->source.client != dp->cseq) + return 0; /* ignored */ + + rec = (evrec_t*)&ev->data; + if (rec->s.code == SEQ_SYNCTIMER) { + /* sync echo back */ + snd_seq_oss_writeq_wakeup(dp->writeq, rec->t.time); + + } else { + /* echo back event */ + if (dp->readq == NULL) + return 0; + snd_seq_oss_readq_put_event(dp->readq, rec); + } + return 0; +} + diff -Nru a/sound/core/seq/oss/seq_oss_event.h b/sound/core/seq/oss/seq_oss_event.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_event.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,112 @@ +/* + * OSS compatible sequencer driver + * + * seq_oss_event.h - OSS event queue record + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_EVENT_H +#define __SEQ_OSS_EVENT_H + +#include "seq_oss_device.h" + +#define SHORT_EVENT_SIZE 4 +#define LONG_EVENT_SIZE 8 + +/* short event (4bytes) */ +typedef struct evrec_short_t { + unsigned char code; + unsigned char parm1; + unsigned char dev; + unsigned char parm2; +} evrec_short_t; + +/* short note events (4bytes) */ +typedef struct evrec_note_t { + unsigned char code; + unsigned char chn; + unsigned char note; + unsigned char vel; +} evrec_note_t; + +/* long timer events (8bytes) */ +typedef struct evrec_timer_t { + unsigned char code; + unsigned char cmd; + unsigned char dummy1, dummy2; + unsigned int time; +} evrec_timer_t; + +/* long extended events (8bytes) */ +typedef struct evrec_extended_t { + unsigned char code; + unsigned char cmd; + unsigned char dev; + unsigned char chn; + unsigned char p1, p2, p3, p4; +} evrec_extended_t; + +/* long channel events (8bytes) */ +typedef struct evrec_long_t { + unsigned char code; + unsigned char dev; + unsigned char cmd; + unsigned char chn; + unsigned char p1, p2; + unsigned short val; +} evrec_long_t; + +/* channel voice events (8bytes) */ +typedef struct evrec_voice_t { + unsigned char code; + unsigned char dev; + unsigned char cmd; + unsigned char chn; + unsigned char note, parm; + unsigned short dummy; +} evrec_voice_t; + +/* sysex events (8bytes) */ +typedef struct evrec_sysex_t { + unsigned char code; + unsigned char dev; + unsigned char buf[6]; +} evrec_sysex_t; + +/* event record */ +union evrec_t { + evrec_short_t s; + evrec_note_t n; + evrec_long_t l; + evrec_voice_t v; + evrec_timer_t t; + evrec_extended_t e; + evrec_sysex_t x; + unsigned int echo; + unsigned char c[LONG_EVENT_SIZE]; +}; + +#define ev_is_long(ev) ((ev)->s.code >= 128) +#define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE) + +int snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); +int snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *q); +int snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop); + + +#endif /* __SEQ_OSS_EVENT_H */ diff -Nru a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_init.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,540 @@ +/* + * OSS compatible sequencer driver + * + * open/close and reset interface + * + * Copyright (C) 1998-1999 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_device.h" +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "seq_oss_writeq.h" +#include "seq_oss_readq.h" +#include "seq_oss_timer.h" +#include "seq_oss_event.h" +#include + +/* + * common variables + */ +MODULE_PARM(maxqlen, "i"); +MODULE_PARM_DESC(maxqlen, "maximum queue length"); + +static int system_client = -1; /* ALSA sequencer client number */ +static int system_port = -1; + +int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN; +static int num_clients = 0; +static seq_oss_devinfo_t *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS]; + + +/* + * prototypes + */ +static int receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); +static int translate_mode(struct file *file); +static int create_port(seq_oss_devinfo_t *dp); +static int delete_port(seq_oss_devinfo_t *dp); +static int alloc_seq_queue(seq_oss_devinfo_t *dp); +static int delete_seq_queue(seq_oss_devinfo_t *dp); +static void free_devinfo(void *private); + +#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec) + + +/* + * create sequencer client for OSS sequencer + */ +int __init +snd_seq_oss_create_client(void) +{ + int rc; + snd_seq_client_callback_t callback; + snd_seq_client_info_t info; + snd_seq_port_info_t port; + snd_seq_port_callback_t port_callback; + + /* create ALSA client */ + memset(&callback, 0, sizeof(callback)); + + callback.private_data = NULL; + callback.allow_input = 1; + callback.allow_output = 1; + + rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS, &callback); + if (rc < 0) + return rc; + + system_client = rc; + debug_printk(("new client = %d\n", rc)); + + /* set client information */ + memset(&info, 0, sizeof(info)); + info.client = system_client; + info.type = KERNEL_CLIENT; + strcpy(info.name, "OSS sequencer"); + + rc = call_ctl(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info); + + /* look up midi devices */ + snd_seq_oss_midi_lookup_ports(system_client); + + /* create annoucement receiver port */ + memset(&port, 0, sizeof(port)); + strcpy(port.name, "Receiver"); + port.addr.client = system_client; + port.capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */ + port.type = 0; + + memset(&port_callback, 0, sizeof(port_callback)); + /* don't set port_callback.owner here. otherwise the module counter + * is incremented and we can no longer release the module.. + */ + port_callback.event_input = receive_announce; + port.kernel = &port_callback; + + call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + if ((system_port = port.addr.port) >= 0) { + snd_seq_port_subscribe_t subs; + + memset(&subs, 0, sizeof(subs)); + subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; + subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + subs.dest.client = system_client; + subs.dest.port = system_port; + call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs); + } + + + return 0; +} + + +/* + * receive annoucement from system port, and check the midi device + */ +static int +receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop) +{ + snd_seq_port_info_t pinfo; + + if (atomic) + return 0; /* it must not happen */ + + switch (ev->type) { + case SNDRV_SEQ_EVENT_PORT_START: + case SNDRV_SEQ_EVENT_PORT_CHANGE: + if (ev->data.addr.client == system_client) + break; /* ignore myself */ + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.addr = ev->data.addr; + if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0) + snd_seq_oss_midi_check_new_port(&pinfo); + break; + + case SNDRV_SEQ_EVENT_PORT_EXIT: + if (ev->data.addr.client == system_client) + break; /* ignore myself */ + snd_seq_oss_midi_check_exit_port(ev->data.addr.client, + ev->data.addr.port); + break; + } + return 0; +} + + +/* + * delete OSS sequencer client + */ +int +snd_seq_oss_delete_client(void) +{ + if (system_client >= 0) + snd_seq_delete_kernel_client(system_client); + + snd_seq_oss_midi_clear_all(); + + return 0; +} + + +/* + * open sequencer device + */ +int +snd_seq_oss_open(struct file *file, int level) +{ + int i, rc; + seq_oss_devinfo_t *dp; + + if ((dp = snd_kcalloc(sizeof(*dp), GFP_KERNEL)) == NULL) { + snd_printk("can't malloc device info\n"); + return -ENOMEM; + } + + for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) { + if (client_table[i] == NULL) + break; + } + if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) { + snd_printk("too many applications\n"); + return -ENOMEM; + } + + dp->index = i; + dp->cseq = system_client; + dp->port = -1; + dp->queue = -1; + dp->readq = NULL; + dp->writeq = NULL; + + /* look up synth and midi devices */ + snd_seq_oss_synth_setup(dp); + snd_seq_oss_midi_setup(dp); + + if (dp->synth_opened == 0 && dp->max_mididev == 0) { + snd_printk("no device found\n"); + kfree(dp); + return -ENODEV; + } + + /* create port */ + if ((rc = create_port(dp)) < 0) { + snd_printk("can't create port\n"); + free_devinfo(dp); + return rc; + } + + /* allocate queue */ + if ((rc = alloc_seq_queue(dp)) < 0) { + delete_port(dp); + return rc; + } + + /* set address */ + dp->addr.client = dp->cseq; + dp->addr.port = dp->port; + /*dp->addr.queue = dp->queue;*/ + /*dp->addr.channel = 0;*/ + + dp->seq_mode = level; + + /* set up file mode */ + dp->file_mode = translate_mode(file); + + /* initialize read queue */ + if (is_read_mode(dp->file_mode)) { + if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) { + delete_seq_queue(dp); + delete_port(dp); + return -ENOMEM; + } + } + + /* initialize write queue */ + if (is_write_mode(dp->file_mode)) { + dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen); + if (dp->writeq == NULL) { + delete_seq_queue(dp); + delete_port(dp); + return -ENOMEM; + } + } + + /* initialize timer */ + if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) { + snd_printk("can't alloc timer\n"); + delete_seq_queue(dp); + delete_port(dp); + return -ENOMEM; + } + + /* set private data pointer */ + file->private_data = dp; + + /* set up for mode2 */ + if (level == SNDRV_SEQ_OSS_MODE_MUSIC) + snd_seq_oss_synth_setup_midi(dp); + else if (is_read_mode(dp->file_mode)) + snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ); + + client_table[dp->index] = dp; + num_clients++; +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + + debug_printk(("open done\n")); + + return 0; +} + +/* + * translate file flags to private mode + */ +static int +translate_mode(struct file *file) +{ + int file_mode = 0; + if ((file->f_flags & O_ACCMODE) != O_RDONLY) + file_mode |= SNDRV_SEQ_OSS_FILE_WRITE; + if ((file->f_flags & O_ACCMODE) != O_WRONLY) + file_mode |= SNDRV_SEQ_OSS_FILE_READ; + if (file->f_flags & O_NONBLOCK) + file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK; + return file_mode; +} + + +/* + * create sequencer port + */ +static int +create_port(seq_oss_devinfo_t *dp) +{ + int rc; + snd_seq_port_info_t port; + snd_seq_port_callback_t callback; + + memset(&port, 0, sizeof(port)); + port.addr.client = dp->cseq; + sprintf(port.name, "Sequencer-%d", dp->index); + port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */ + port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; + port.midi_channels = 128; + port.synth_voices = 128; + + memset(&callback, 0, sizeof(callback)); + callback.owner = THIS_MODULE; + callback.private_data = dp; + callback.event_input = snd_seq_oss_event_input; + callback.private_free = free_devinfo; + port.kernel = &callback; + + rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + if (rc < 0) + return rc; + + dp->port = port.addr.port; + debug_printk(("new port = %d\n", port.addr.port)); + + return 0; +} + +/* + * delete ALSA port + */ +static int +delete_port(seq_oss_devinfo_t *dp) +{ + snd_seq_port_info_t port_info; + + if (dp->port < 0) + return 0; + + memset(&port_info, 0, sizeof(port_info)); + port_info.addr.client = dp->cseq; + port_info.addr.port = dp->port; + return snd_seq_kernel_client_ctl(dp->cseq, + SNDRV_SEQ_IOCTL_DELETE_PORT, + &port_info); +} + +/* + * allocate a queue + */ +static int +alloc_seq_queue(seq_oss_devinfo_t *dp) +{ + snd_seq_queue_info_t qinfo; + int rc; + + memset(&qinfo, 0, sizeof(qinfo)); + qinfo.owner = system_client; + qinfo.locked = 1; + strcpy(qinfo.name, "OSS Sequencer Emulation"); + if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0) + return rc; + dp->queue = qinfo.queue; + return 0; +} + +/* + * release queue + */ +static int +delete_seq_queue(seq_oss_devinfo_t *dp) +{ + snd_seq_queue_info_t qinfo; + + if (dp->queue < 0) + return 0; + memset(&qinfo, 0, sizeof(qinfo)); + qinfo.queue = dp->queue; + return call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo); +} + + +/* + * free device informations - private_free callback of port + */ +static void +free_devinfo(void *private) +{ + seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private; + + if (dp->timer) + snd_seq_oss_timer_delete(dp->timer); + + if (dp->writeq) + snd_seq_oss_writeq_delete(dp->writeq); + + if (dp->readq) + snd_seq_oss_readq_delete(dp->readq); + + kfree(dp); +} + + +/* + * close sequencer device + */ +void +snd_seq_oss_release(seq_oss_devinfo_t *dp) +{ + client_table[dp->index] = NULL; + num_clients--; + + debug_printk(("resetting..\n")); + snd_seq_oss_reset(dp); + + debug_printk(("cleaning up..\n")); + snd_seq_oss_synth_cleanup(dp); + snd_seq_oss_midi_cleanup(dp); + + /* clear slot */ + debug_printk(("releasing resource..\n")); + if (dp->port >= 0) + delete_port(dp); + if (dp->queue >= 0) + delete_seq_queue(dp); + +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + debug_printk(("release done\n")); +} + + +/* + * Wait until the queue is empty (if we don't have nonblock) + */ +void +snd_seq_oss_drain_write(seq_oss_devinfo_t *dp) +{ + if (! dp->timer->running) + return; + if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) && + dp->writeq) { + debug_printk(("syncing..\n")); + while (snd_seq_oss_writeq_sync(dp->writeq)) + ; + } +} + + +/* + * reset sequencer devices + */ +void +snd_seq_oss_reset(seq_oss_devinfo_t *dp) +{ + int i; + + /* reset all synth devices */ + for (i = 0; i < dp->max_synthdev; i++) + snd_seq_oss_synth_reset(dp, i); + + /* reset all midi devices */ + if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) { + for (i = 0; i < dp->max_mididev; i++) + snd_seq_oss_midi_reset(dp, i); + } + + /* remove queues */ + if (dp->readq) + snd_seq_oss_readq_clear(dp->readq); + if (dp->writeq) + snd_seq_oss_writeq_clear(dp->writeq); + + /* reset timer */ + snd_seq_oss_timer_stop(dp->timer); +} + +/* + * proc interface + */ +void +snd_seq_oss_system_info_read(snd_info_buffer_t *buf) +{ + int i; + seq_oss_devinfo_t *dp; + + snd_iprintf(buf, "ALSA client number %d\n", system_client); + snd_iprintf(buf, "ALSA receiver port %d\n", system_port); + + snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients); + for (i = 0; i < num_clients; i++) { + snd_iprintf(buf, "\nApplication %d: ", i); + if ((dp = client_table[i]) == NULL) { + snd_iprintf(buf, "*empty*\n"); + continue; + } + snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue); + snd_iprintf(buf, " sequencer mode = %s : file open mode = %s\n", + (dp->seq_mode ? "music" : "synth"), + filemode_str(dp->file_mode)); + if (dp->seq_mode) + snd_iprintf(buf, " timer tempo = %d, timebase = %d\n", + dp->timer->oss_tempo, dp->timer->oss_timebase); + snd_iprintf(buf, " max queue length %d\n", maxqlen); + if (is_read_mode(dp->file_mode) && dp->readq) + snd_seq_oss_readq_info_read(dp->readq, buf); + } +} + +/* + * misc. functions for proc interface + */ +char * +enabled_str(int bool) +{ + return bool ? "enabled" : "disabled"; +} + +char * +filemode_str(int val) +{ + static char *str[] = { + "none", "read", "write", "read/write", + }; + return str[val & SNDRV_SEQ_OSS_FILE_ACMODE]; +} + + diff -Nru a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_ioctl.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,191 @@ +/* + * OSS compatible sequencer driver + * + * OSS compatible i/o control + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_device.h" +#include "seq_oss_readq.h" +#include "seq_oss_writeq.h" +#include "seq_oss_timer.h" +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "seq_oss_event.h" + +int +snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long carg) +{ + int dev, val; + struct synth_info inf; + struct midi_info minf; + unsigned char ev[8]; + void *arg = (void*)carg; + snd_seq_event_t tmpev; + + switch (cmd) { + case SNDCTL_TMR_TIMEBASE: + case SNDCTL_TMR_TEMPO: + case SNDCTL_TMR_START: + case SNDCTL_TMR_STOP: + case SNDCTL_TMR_CONTINUE: + case SNDCTL_TMR_METRONOME: + case SNDCTL_TMR_SOURCE: + case SNDCTL_TMR_SELECT: + case SNDCTL_SEQ_CTRLRATE: + return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg); + + case SNDCTL_SEQ_PANIC: + debug_printk(("panic\n")); + snd_seq_oss_reset(dp); + return -EINVAL; + + case SNDCTL_SEQ_SYNC: + debug_printk(("sync\n")); + if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) + return 0; + while (snd_seq_oss_writeq_sync(dp->writeq)) + ; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; + + case SNDCTL_SEQ_RESET: + debug_printk(("reset\n")); + snd_seq_oss_reset(dp); + return 0; + + case SNDCTL_SEQ_TESTMIDI: + debug_printk(("test midi\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + return snd_seq_oss_midi_open(dp, dev, dp->file_mode); + + case SNDCTL_SEQ_GETINCOUNT: + debug_printk(("get in count\n")); + if (dp->readq == NULL || ! is_read_mode(dp->file_mode)) + return 0; + return put_user(dp->readq->qlen, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_GETOUTCOUNT: + debug_printk(("get out count\n")); + if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) + return 0; + return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_GETTIME: + debug_printk(("get time\n")); + return put_user(snd_seq_oss_timer_cur_tick(dp->timer), (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_RESETSAMPLES: + debug_printk(("reset samples\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); + + case SNDCTL_SEQ_NRSYNTHS: + debug_printk(("nr synths\n")); + return put_user(dp->max_synthdev, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_NRMIDIS: + debug_printk(("nr midis\n")); + return put_user(dp->max_mididev, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SYNTH_MEMAVL: + debug_printk(("mem avail\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); + return put_user(val, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_FM_4OP_ENABLE: + debug_printk(("4op\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); + return 0; + + case SNDCTL_SYNTH_INFO: + case SNDCTL_SYNTH_ID: + debug_printk(("synth info\n")); + if (copy_from_user(&inf, arg, sizeof(inf))) + return -EFAULT; + if (snd_seq_oss_synth_make_info(dp, inf.device, &inf) < 0) + return -EINVAL; + if (copy_to_user(arg, &inf, sizeof(inf))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_OUTOFBAND: + debug_printk(("out of bound\n")); + if (copy_from_user(ev, arg, 8)) + return -EFAULT; + memset(&tmpev, 0, sizeof(tmpev)); + snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client); + tmpev.time.tick = 0; + if (! snd_seq_oss_process_event(dp, (evrec_t*)ev, &tmpev)) { + snd_seq_oss_dispatch(dp, &tmpev, 0, 0); + } + return 0; + + case SNDCTL_MIDI_INFO: + debug_printk(("midi info\n")); + if (copy_from_user(&minf, arg, sizeof(minf))) + return -EFAULT; + if (snd_seq_oss_midi_make_info(dp, minf.device, &minf) < 0) + return -EINVAL; + if (copy_to_user(arg, &minf, sizeof(minf))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_THRESHOLD: + debug_printk(("threshold\n")); + if (! is_write_mode(dp->file_mode)) + return 0; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 1) + val = 1; + if (val >= dp->writeq->maxlen) + val = dp->writeq->maxlen - 1; + snd_seq_oss_writeq_set_output(dp->writeq, val); + return 0; + + case SNDCTL_MIDI_PRETIME: + debug_printk(("pretime\n")); + if (dp->readq == NULL || !is_read_mode(dp->file_mode)) + return 0; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val <= 0) + val = -1; + else + val = (HZ * val) / 10; + dp->readq->pre_event_timeout = val; + return put_user(val, (int *)arg) ? -EFAULT : 0; + + default: + debug_printk(("others\n")); + if (! is_write_mode(dp->file_mode)) + return -EIO; + return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg); + } + return 0; +} + diff -Nru a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_midi.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,697 @@ +/* + * OSS compatible sequencer driver + * + * MIDI device handlers + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_midi.h" +#include "seq_oss_readq.h" +#include "seq_oss_timer.h" +#include "seq_oss_event.h" +#include +#include "../seq_lock.h" +#include + + +/* + * constants + */ +#define SNDRV_SEQ_OSS_MAX_MIDI_NAME 30 + +/* + * definition of midi device record + */ +struct seq_oss_midi_t { + int seq_device; /* device number */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + unsigned int flags; /* port capability */ + int opened; /* flag for opening */ + unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME]; + snd_midi_event_t *coder; /* MIDI event coder */ + seq_oss_devinfo_t *devinfo; /* assigned OSSseq device */ + snd_use_lock_t use_lock; +}; + + +/* + * midi device table + */ +static int max_midi_devs = 0; +static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS]; + +static spinlock_t register_lock = SPIN_LOCK_UNLOCKED; + +/* + * prototypes + */ +static seq_oss_midi_t *get_mdev(int dev); +static seq_oss_midi_t *get_mididev(seq_oss_devinfo_t *dp, int dev); +static int send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev); +static int send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev); + +/* + * look up the existing ports + * this looks a very exhausting job. + */ +int __init +snd_seq_oss_midi_lookup_ports(int client) +{ + snd_seq_system_info_t sysinfo; + snd_seq_client_info_t clinfo; + snd_seq_port_info_t pinfo; + int rc; + + rc = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SYSTEM_INFO, &sysinfo); + if (rc < 0) + return rc; + + memset(&clinfo, 0, sizeof(clinfo)); + memset(&pinfo, 0, sizeof(pinfo)); + clinfo.client = -1; + while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, &clinfo) == 0) { + if (clinfo.client == client) + continue; /* ignore myself */ + pinfo.addr.client = clinfo.client; + pinfo.addr.port = -1; + while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, &pinfo) == 0) + snd_seq_oss_midi_check_new_port(&pinfo); + } + return 0; +} + + +/* + */ +static seq_oss_midi_t * +get_mdev(int dev) +{ + seq_oss_midi_t *mdev; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + mdev = midi_devs[dev]; + if (mdev) + snd_use_lock_use(&mdev->use_lock); + spin_unlock_irqrestore(®ister_lock, flags); + return mdev; +} + +/* + * look for the identical slot + */ +static seq_oss_midi_t * +find_slot(int client, int port) +{ + int i; + seq_oss_midi_t *mdev; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_midi_devs; i++) { + mdev = midi_devs[i]; + if (mdev && mdev->client == client && mdev->port == port) { + /* found! */ + snd_use_lock_use(&mdev->use_lock); + spin_unlock_irqrestore(®ister_lock, flags); + return mdev; + } + } + spin_unlock_irqrestore(®ister_lock, flags); + return NULL; +} + + +#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE) +#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ) +/* + * register a new port if it doesn't exist yet + */ +int +snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo) +{ + int i; + seq_oss_midi_t *mdev; + unsigned long flags; + + debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port)); + /* the port must include generic midi */ + if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC)) + return 0; + /* either read or write subscribable */ + if ((pinfo->capability & PERM_WRITE) != PERM_WRITE && + (pinfo->capability & PERM_READ) != PERM_READ) + return 0; + + /* + * look for the identical slot + */ + if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) { + /* already exists */ + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + /* + * allocate midi info record + */ + if ((mdev = snd_kcalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) { + snd_printk("can't malloc midi info\n"); + return -ENOMEM; + } + + /* copy the port information */ + mdev->client = pinfo->addr.client; + mdev->port = pinfo->addr.port; + mdev->flags = pinfo->capability; + mdev->opened = 0; + snd_use_lock_init(&mdev->use_lock); + + /* copy and truncate the name of synth device */ + strncpy(mdev->name, pinfo->name, sizeof(mdev->name)); + mdev->name[sizeof(mdev->name) - 1] = 0; + + /* create MIDI coder */ + if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) { + snd_printk("can't malloc midi coder\n"); + kfree(mdev); + return -ENOMEM; + } + + /* + * look for en empty slot + */ + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_midi_devs; i++) { + if (midi_devs[i] == NULL) + break; + } + if (i >= max_midi_devs) { + if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) { + snd_midi_event_free(mdev->coder); + kfree(mdev); + spin_unlock_irqrestore(®ister_lock, flags); + return -ENOMEM; + } + max_midi_devs++; + } + mdev->seq_device = i; + midi_devs[mdev->seq_device] = mdev; + spin_unlock_irqrestore(®ister_lock, flags); + + /*MOD_INC_USE_COUNT;*/ + return 0; +} + +/* + * release the midi device if it was registered + */ +int +snd_seq_oss_midi_check_exit_port(int client, int port) +{ + seq_oss_midi_t *mdev; + unsigned long flags; + int index; + + if ((mdev = find_slot(client, port)) != NULL) { + spin_lock_irqsave(®ister_lock, flags); + midi_devs[mdev->seq_device] = NULL; + spin_unlock_irqrestore(®ister_lock, flags); + snd_use_lock_free(&mdev->use_lock); + snd_use_lock_sync(&mdev->use_lock); + if (mdev->coder) + snd_midi_event_free(mdev->coder); + kfree(mdev); + } + spin_lock_irqsave(®ister_lock, flags); + for (index = max_midi_devs - 1; index >= 0; index--) { + if (midi_devs[index]) + break; + } + max_midi_devs = index + 1; + spin_unlock_irqrestore(®ister_lock, flags); + return 0; +} + + +/* + * release the midi device if it was registered + */ +void +snd_seq_oss_midi_clear_all(void) +{ + int i; + seq_oss_midi_t *mdev; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_midi_devs; i++) { + if ((mdev = midi_devs[i]) != NULL) { + if (mdev->coder) + snd_midi_event_free(mdev->coder); + kfree(mdev); + midi_devs[i] = NULL; + } + } + max_midi_devs = 0; + spin_unlock_irqrestore(®ister_lock, flags); +} + + +/* + * set up midi tables + */ +void +snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp) +{ + dp->max_mididev = max_midi_devs; +} + +/* + * clean up midi tables + */ +void +snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp) +{ + int i; + for (i = 0; i < dp->max_mididev; i++) + snd_seq_oss_midi_close(dp, i); + dp->max_mididev = 0; +} + + +/* + * open all midi devices. ignore errors. + */ +void +snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode) +{ + int i; + for (i = 0; i < dp->max_mididev; i++) + snd_seq_oss_midi_open(dp, i, file_mode); +} + + +/* + * get the midi device information + */ +static seq_oss_midi_t * +get_mididev(seq_oss_devinfo_t *dp, int dev) +{ + if (dev < 0 || dev >= dp->max_mididev) + return NULL; + return get_mdev(dev); +} + + +/* + * open the midi device if not opened yet + */ +int +snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode) +{ + int perm; + seq_oss_midi_t *mdev; + snd_seq_port_subscribe_t subs; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENODEV; + + /* already used? */ + if (mdev->opened && mdev->devinfo != dp) { + snd_use_lock_free(&mdev->use_lock); + return -EBUSY; + } + + perm = 0; + if (is_write_mode(fmode)) + perm |= PERM_WRITE; + if (is_read_mode(fmode)) + perm |= PERM_READ; + perm &= mdev->flags; + if (perm == 0) { + snd_use_lock_free(&mdev->use_lock); + return -ENXIO; + } + + /* already opened? */ + if ((mdev->opened & perm) == perm) { + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + perm &= ~mdev->opened; + + memset(&subs, 0, sizeof(subs)); + + if (perm & PERM_WRITE) { + subs.sender = dp->addr; + subs.dest.client = mdev->client; + subs.dest.port = mdev->port; + if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) + mdev->opened |= PERM_WRITE; + } + if (perm & PERM_READ) { + subs.sender.client = mdev->client; + subs.sender.port = mdev->port; + subs.dest = dp->addr; + if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) + mdev->opened |= PERM_READ; + } + + if (! mdev->opened) { + snd_use_lock_free(&mdev->use_lock); + return -ENXIO; + } + + mdev->devinfo = dp; + snd_use_lock_free(&mdev->use_lock); + return 0; +} + +/* + * close the midi device if already opened + */ +int +snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_midi_t *mdev; + snd_seq_port_subscribe_t subs; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENODEV; + if (! mdev->opened || mdev->devinfo != dp) { + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened)); + memset(&subs, 0, sizeof(subs)); + if (mdev->opened & PERM_WRITE) { + subs.sender = dp->addr; + subs.dest.client = mdev->client; + subs.dest.port = mdev->port; + snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs); + } + if (mdev->opened & PERM_READ) { + subs.sender.client = mdev->client; + subs.sender.port = mdev->port; + subs.dest = dp->addr; + snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs); + } + + mdev->opened = 0; + mdev->devinfo = NULL; + + snd_use_lock_free(&mdev->use_lock); + return 0; +} + +/* + * change seq capability flags to file mode flags + */ +int +snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_midi_t *mdev; + int mode; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return 0; + + mode = 0; + if (mdev->opened & PERM_WRITE) + mode |= SNDRV_SEQ_OSS_FILE_WRITE; + if (mdev->opened & PERM_READ) + mode |= SNDRV_SEQ_OSS_FILE_READ; + + snd_use_lock_free(&mdev->use_lock); + return mode; +} + +/* + * reset the midi device and close it: + * so far, only close the device. + */ +void +snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return; + if (! mdev->opened) { + snd_use_lock_free(&mdev->use_lock); + return; + } + + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC && + (mdev->opened & PERM_WRITE)) { + snd_seq_event_t ev; + int c; + + debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port)); + memset(&ev, 0, sizeof(ev)); + ev.dest.client = mdev->client; + ev.dest.port = mdev->port; + ev.queue = dp->queue; + ev.source.port = dp->port; + for (c = 0; c < 16; c++) { + ev.type = SNDRV_SEQ_EVENT_CONTROLLER; + ev.data.control.channel = c; + ev.data.control.param = 123; + snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */ + ev.data.control.param = 121; + snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */ + ev.type = SNDRV_SEQ_EVENT_PITCHBEND; + ev.data.control.value = 0; + snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */ + } + } + snd_seq_oss_midi_close(dp, dev); + snd_use_lock_free(&mdev->use_lock); +} + + +/* + * get client/port of the specified MIDI device + */ +void +snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return; + addr->client = mdev->client; + addr->port = mdev->port; + snd_use_lock_free(&mdev->use_lock); +} + + +/* + * input callback - this can be atomic + */ +int +snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private_data) +{ + seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data; + seq_oss_midi_t *mdev; + int rc; + + if (dp->readq == NULL) + return 0; + if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL) + return 0; + if (! (mdev->opened & PERM_READ)) { + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + rc = send_synth_event(dp, ev, mdev->seq_device); + else + rc = send_midi_event(dp, ev, mdev); + + snd_use_lock_free(&mdev->use_lock); + return rc; +} + +/* + * convert ALSA sequencer event to OSS synth event + */ +static int +send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev) +{ + evrec_t ossev; + + memset(&ossev, 0, sizeof(ossev)); + + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEON: + ossev.v.cmd = MIDI_NOTEON; break; + case SNDRV_SEQ_EVENT_NOTEOFF: + ossev.v.cmd = MIDI_NOTEOFF; break; + case SNDRV_SEQ_EVENT_KEYPRESS: + ossev.v.cmd = MIDI_KEY_PRESSURE; break; + case SNDRV_SEQ_EVENT_CONTROLLER: + ossev.l.cmd = MIDI_CTL_CHANGE; break; + case SNDRV_SEQ_EVENT_PGMCHANGE: + ossev.l.cmd = MIDI_PGM_CHANGE; break; + case SNDRV_SEQ_EVENT_CHANPRESS: + ossev.l.cmd = MIDI_CHN_PRESSURE; break; + case SNDRV_SEQ_EVENT_PITCHBEND: + ossev.l.cmd = MIDI_PITCH_BEND; break; + default: + return 0; /* not supported */ + } + + ossev.v.dev = dev; + + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEON: + case SNDRV_SEQ_EVENT_NOTEOFF: + case SNDRV_SEQ_EVENT_KEYPRESS: + ossev.v.code = EV_CHN_VOICE; + ossev.v.note = ev->data.note.note; + ossev.v.parm = ev->data.note.velocity; + break; + case SNDRV_SEQ_EVENT_CONTROLLER: + case SNDRV_SEQ_EVENT_PGMCHANGE: + case SNDRV_SEQ_EVENT_CHANPRESS: + ossev.l.code = EV_CHN_COMMON; + ossev.l.p1 = ev->data.control.param; + ossev.l.val = ev->data.control.value; + break; + case SNDRV_SEQ_EVENT_PITCHBEND: + ossev.l.code = EV_CHN_COMMON; + ossev.l.val = ev->data.control.value + 8192; + break; + } + + snd_seq_oss_readq_put_event(dp->readq, &ossev); + + return 0; +} + +/* + * decode event and send MIDI bytes to read queue + */ +static int +send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev) +{ + char msg[32]; /* enough except for sysex? */ + int len; + + snd_seq_oss_readq_put_timestamp(dp->readq, snd_seq_oss_timer_cur_tick(dp->timer), dp->seq_mode); + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) + snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, + ev->data.ext.ptr, ev->data.ext.len); + } else { + len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev); + if (len > 0) + snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len); + } + + return 0; +} + + +/* + * dump midi data + * return 0 : enqueued + * non-zero : invalid - ignored + */ +int +snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENODEV; + if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) { + snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port); + snd_use_lock_free(&mdev->use_lock); + return 0; + } + snd_use_lock_free(&mdev->use_lock); + return -EINVAL; +} + +/* + * create OSS compatible midi_info record + */ +int +snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENXIO; + inf->device = dev; + inf->dev_type = 0; /* FIXME: ?? */ + inf->capabilities = 0; /* FIXME: ?? */ + strncpy(inf->name, mdev->name, sizeof(inf->name)); + snd_use_lock_free(&mdev->use_lock); + return 0; +} + + +/* + * proc interface + */ +static char * +capmode_str(int val) +{ + val &= PERM_READ|PERM_WRITE; + if (val == (PERM_READ|PERM_WRITE)) + return "read/write"; + else if (val == PERM_READ) + return "read"; + else if (val == PERM_WRITE) + return "write"; + else + return "none"; +} + +void +snd_seq_oss_midi_info_read(snd_info_buffer_t *buf) +{ + int i; + seq_oss_midi_t *mdev; + + snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs); + for (i = 0; i < max_midi_devs; i++) { + snd_iprintf(buf, "\nmidi %d: ", i); + mdev = get_mdev(i); + if (mdev == NULL) { + snd_iprintf(buf, "*empty*\n"); + continue; + } + snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name, + mdev->client, mdev->port); + snd_iprintf(buf, " capability %s / opened %s\n", + capmode_str(mdev->flags), + capmode_str(mdev->opened)); + snd_use_lock_free(&mdev->use_lock); + } +} + diff -Nru a/sound/core/seq/oss/seq_oss_midi.h b/sound/core/seq/oss/seq_oss_midi.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_midi.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,49 @@ +/* + * OSS compatible sequencer driver + * + * midi device information + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_MIDI_H +#define __SEQ_OSS_MIDI_H + +#include "seq_oss_device.h" +#include + +typedef struct seq_oss_midi_t seq_oss_midi_t; + +int snd_seq_oss_midi_lookup_ports(int client); +int snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo); +int snd_seq_oss_midi_check_exit_port(int client, int port); +void snd_seq_oss_midi_clear_all(void); + +void snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp); +void snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp); + +int snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int file_mode); +void snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode); +int snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev); +void snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev); +int snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private); +int snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf); +void snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr); + +#endif diff -Nru a/sound/core/seq/oss/seq_oss_misc.c b/sound/core/seq/oss/seq_oss_misc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_misc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,110 @@ +/*---------------------------------------------------------------- + * miscellaneous functions + *----------------------------------------------------------------*/ + +unsigned short snd_seq_oss_semitone_tuning[24] = +{ +/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, +/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, +/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 +}; + +unsigned short snd_seq_oss_cent_tuning[100] = +{ +/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, +/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, +/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, +/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, +/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, +/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, +/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, +/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, +/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, +/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, +/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, +/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, +/* 96 */ 10570, 10576, 10582, 10589 +}; + +/* convert from MIDI note to frequency */ +int +snd_seq_oss_note_to_freq(int note_num) +{ + + /* + * This routine converts a midi note to a frequency (multiplied by 1000) + */ + + int note, octave, note_freq; + static int notes[] = { + 261632, 277189, 293671, 311132, 329632, 349232, + 369998, 391998, 415306, 440000, 466162, 493880 + }; + +#define BASE_OCTAVE 5 + + octave = note_num / 12; + note = note_num % 12; + + note_freq = notes[note]; + + if (octave < BASE_OCTAVE) + note_freq >>= (BASE_OCTAVE - octave); + else if (octave > BASE_OCTAVE) + note_freq <<= (octave - BASE_OCTAVE); + + /* + * note_freq >>= 1; + */ + + return note_freq; +} + +unsigned long +snd_seq_oss_compute_finetune(unsigned long base_freq, int bend, int range, int vibrato_cents) +{ + unsigned long amount; + int negative, semitones, cents, multiplier = 1; + + if (!bend || !range || !base_freq) + return base_freq; + + if (range >= 8192) + range = 8192; + + bend = bend * range / 8192; /* Convert to cents */ + bend += vibrato_cents; + + if (!bend) + return base_freq; + + negative = bend < 0 ? 1 : 0; + + if (bend < 0) + bend *= -1; + if (bend > range) + bend = range; + + /* + if (bend > 2399) + bend = 2399; + */ + while (bend > 2399) { + multiplier *= 4; + bend -= 2400; + } + + semitones = bend / 100; + if (semitones > 99) + semitones = 99; + cents = bend % 100; + + amount = (int) (snd_seq_oss_semitone_tuning[semitones] * multiplier * + snd_seq_oss_cent_tuning[cents]) / 10000; + + if (negative) + return (base_freq * 10000) / amount; /* Bend down */ + else + return (base_freq * amount) / 10000; /* Bend up */ +} + diff -Nru a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_readq.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,245 @@ +/* + * OSS compatible sequencer driver + * + * seq_oss_readq.c - MIDI input queue + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_readq.h" +#include "seq_oss_event.h" +#include +#include "../seq_lock.h" + +/* + * constants + */ +//#define SNDRV_SEQ_OSS_MAX_TIMEOUT (unsigned long)(-1) +#define SNDRV_SEQ_OSS_MAX_TIMEOUT (HZ * 3600) + + +/* + * prototypes + */ + + +/* + * create a read queue + */ +seq_oss_readq_t * +snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen) +{ + seq_oss_readq_t *q; + + if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL) { + snd_printk("can't malloc read queue\n"); + return NULL; + } + + if ((q->q = snd_kcalloc(sizeof(evrec_t) * maxlen, GFP_KERNEL)) == NULL) { + snd_printk("can't malloc read queue buffer\n"); + kfree(q); + return NULL; + } + + q->maxlen = maxlen; + q->qlen = 0; + q->head = q->tail = 0; + init_waitqueue_head(&q->midi_sleep); + spin_lock_init(&q->lock); + q->pre_event_timeout = SNDRV_SEQ_OSS_MAX_TIMEOUT; + q->input_time = (unsigned long)-1; + + return q; +} + +/* + * delete the read queue + */ +void +snd_seq_oss_readq_delete(seq_oss_readq_t *q) +{ + if (q) { + snd_seq_oss_readq_clear(q); /* to be sure */ + if (q->q) + kfree(q->q); + kfree(q); + } +} + +/* + * reset the read queue + */ +void +snd_seq_oss_readq_clear(seq_oss_readq_t *q) +{ + if (q->qlen) { + q->qlen = 0; + q->head = q->tail = 0; + } + /* if someone sleeping, wake'em up */ + if (waitqueue_active(&q->midi_sleep)) + wake_up(&q->midi_sleep); + q->input_time = (unsigned long)-1; +} + +/* + * put a midi byte + */ +int +snd_seq_oss_readq_puts(seq_oss_readq_t *q, int dev, unsigned char *data, int len) +{ + evrec_t rec; + int result; + + rec.c[0] = SEQ_MIDIPUTC; + rec.c[2] = dev; + rec.c[3] = 0; + + while (len-- > 0) { + rec.c[1] = *data++; + result = snd_seq_oss_readq_put_event(q, &rec); + if (result < 0) + return result; + } + return 0; +} + +/* + * copy an event to input queue: + * return zero if enqueued + */ +int +snd_seq_oss_readq_put_event(seq_oss_readq_t *q, evrec_t *ev) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + if (q->qlen >= q->maxlen - 1) { + spin_unlock_irqrestore(&q->lock, flags); + return -ENOMEM; + } + + memcpy(&q->q[q->tail], ev, ev_length(ev)); + q->tail = (q->tail + 1) % q->maxlen; + q->qlen++; + + /* wake up sleeper */ + if (waitqueue_active(&q->midi_sleep)) + wake_up(&q->midi_sleep); + + spin_unlock_irqrestore(&q->lock, flags); + + return 0; +} + + +/* + * pop queue + */ +evrec_t * +snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags) +{ + evrec_t *p; + + spin_lock_irqsave(&q->lock, *rflags); + if (q->qlen == 0) { + if (blocking) { + snd_seq_sleep_timeout_in_lock(&q->midi_sleep, + &q->lock, + q->pre_event_timeout); + } + if (q->qlen == 0) { + spin_unlock_irqrestore(&q->lock, *rflags); + return NULL; + } + } + p = q->q + q->head; + + return p; +} + +/* + * unlock queue + */ +void +snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags) +{ + spin_unlock_irqrestore(&q->lock, flags); +} + +/* + * drain one record and unlock queue + */ +void +snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags) +{ + if (q->qlen > 0) { + q->head = (q->head + 1) % q->maxlen; + q->qlen--; + } + spin_unlock_irqrestore(&q->lock, flags); +} + +/* + * polling/select: + * return non-zero if readq is not empty. + */ +unsigned int +snd_seq_oss_readq_poll(seq_oss_readq_t *q, struct file *file, poll_table *wait) +{ + poll_wait(file, &q->midi_sleep, wait); + return q->qlen; +} + +/* + * put a timestamp + */ +int +snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *q, unsigned long curt, int seq_mode) +{ + if (curt != q->input_time) { + evrec_t rec; + switch (seq_mode) { + case SNDRV_SEQ_OSS_MODE_SYNTH: + rec.echo = (curt << 8) | SEQ_WAIT; + snd_seq_oss_readq_put_event(q, &rec); + break; + case SNDRV_SEQ_OSS_MODE_MUSIC: + rec.t.code = EV_TIMING; + rec.t.cmd = TMR_WAIT_ABS; + rec.t.time = curt; + snd_seq_oss_readq_put_event(q, &rec); + break; + } + q->input_time = curt; + } + return 0; +} + + +/* + * proc interface + */ +void +snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf) +{ + snd_iprintf(buf, " read queue [%s] length = %d : tick = %ld\n", + (waitqueue_active(&q->midi_sleep) ? "sleeping":"running"), + q->qlen, q->input_time); +} diff -Nru a/sound/core/seq/oss/seq_oss_readq.h b/sound/core/seq/oss/seq_oss_readq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_readq.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,53 @@ +/* + * OSS compatible sequencer driver + * read fifo queue + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_READQ_H +#define __SEQ_OSS_READQ_H + +#include "seq_oss_device.h" + + +/* + * definition of read queue + */ +struct seq_oss_readq_t { + evrec_t *q; + int qlen; + int maxlen; + int head, tail; + unsigned long pre_event_timeout; + unsigned long input_time; + wait_queue_head_t midi_sleep; + spinlock_t lock; +}; + +seq_oss_readq_t *snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen); +void snd_seq_oss_readq_delete(seq_oss_readq_t *q); +void snd_seq_oss_readq_clear(seq_oss_readq_t *readq); +unsigned int snd_seq_oss_readq_poll(seq_oss_readq_t *readq, struct file *file, poll_table *wait); +int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len); +int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev); +int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode); +evrec_t *snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags); +void snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags); +void snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags); + +#endif diff -Nru a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_rw.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,197 @@ +/* + * OSS compatible sequencer driver + * + * read/write/select interface to device file + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_device.h" +#include "seq_oss_readq.h" +#include "seq_oss_writeq.h" +#include "seq_oss_synth.h" +#include +#include "seq_oss_event.h" +#include "seq_oss_timer.h" +#include "../seq_clientmgr.h" + + +/* + * protoypes + */ +static int insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt); + + +/* + * read interface + */ + +int +snd_seq_oss_read(seq_oss_devinfo_t *dp, char *buf, int count) +{ + seq_oss_readq_t *readq = dp->readq; + int cnt, pos; + evrec_t *q; + unsigned long flags; + + if (readq == NULL || ! is_read_mode(dp->file_mode)) + return -EIO; + + /* copy queued events to read buffer */ + cnt = count; + pos = 0; + q = snd_seq_oss_readq_pick(readq, !is_nonblock_mode(dp->file_mode), &flags); + if (q == NULL) + return 0; + do { + int ev_len; + /* tansfer the data */ + ev_len = ev_length(q); + if (copy_to_user(buf + pos, q, ev_len)) { + snd_seq_oss_readq_unlock(readq, flags); + break; + } + snd_seq_oss_readq_free(readq, flags); + pos += ev_len; + cnt -= ev_len; + if (cnt < ev_len) + break; + } while ((q = snd_seq_oss_readq_pick(readq, 0, &flags)) != NULL); + + return count - cnt; +} + + +/* + * write interface + */ + +int +snd_seq_oss_write(seq_oss_devinfo_t *dp, const char *buf, int count, struct file *opt) +{ + int rc, c, p, ev_size; + evrec_t rec; + + if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) + return -EIO; + + c = count; + p = 0; + while (c >= SHORT_EVENT_SIZE) { + if (copy_from_user(rec.c, buf + p, SHORT_EVENT_SIZE)) + break; + p += SHORT_EVENT_SIZE; + + if (rec.s.code == SEQ_FULLSIZE) { + /* load patch */ + int fmt = (*(unsigned short *)rec.c) & 0xffff; + return snd_seq_oss_synth_load_patch(dp, rec.s.dev, fmt, buf, p, c); + + } + if (ev_is_long(&rec)) { + /* extended code */ + if (rec.s.code == SEQ_EXTENDED && + dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + ev_size = LONG_EVENT_SIZE; + if (c < ev_size) + break; + /* copy the reset 4 bytes */ + if (copy_from_user(rec.c + SHORT_EVENT_SIZE, buf + p, + LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) + break; + p += LONG_EVENT_SIZE - SHORT_EVENT_SIZE; + + } else { + /* old-type code */ + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + ev_size = SHORT_EVENT_SIZE; + } + + /* insert queue */ + if ((rc = insert_queue(dp, &rec, opt)) < 0) + break; + + c -= ev_size; + } + + if (count == c && is_nonblock_mode(dp->file_mode)) + return -EAGAIN; + return count - c; +} + + +/* + * insert event record to write queue + * return: 0 = OK, non-zero = NG + */ +static int +insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt) +{ + int rc = 0; + snd_seq_event_t event; + + /* if this is a timing event, process the current time */ + if (snd_seq_oss_process_timer_event(dp->timer, rec)) + return 0; /* no need to insert queue */ + + /* parse this event */ + memset(&event, 0, sizeof(event)); + /* set dummy -- to be sure */ + event.type = SNDRV_SEQ_EVENT_NOTEOFF; + snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client); + + if (snd_seq_oss_process_event(dp, rec, &event)) + return 0; /* invalid event - no need to insert queue */ + + event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer); + if (dp->timer->realtime || !dp->timer->running) { + snd_seq_oss_dispatch(dp, &event, 0, 0); + } else { + if (is_nonblock_mode(dp->file_mode)) + rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0); + else + rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0); + } + return rc; +} + + +/* + * select / poll + */ + +unsigned int +snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + + /* input */ + if (dp->readq && is_read_mode(dp->file_mode)) { + if (snd_seq_oss_readq_poll(dp->readq, file, wait)) + mask |= POLLIN | POLLRDNORM; + } + + /* output */ + if (dp->writeq && is_write_mode(dp->file_mode)) { + if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait)) + mask |= POLLOUT | POLLWRNORM; + } + return mask; +} diff -Nru a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_synth.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,642 @@ +/* + * OSS compatible sequencer driver + * + * synth device handlers + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "../seq_lock.h" +#include + +/* + * constants + */ +#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME 30 +#define MAX_SYSEX_BUFLEN 128 + + +/* + * definition of synth info records + */ + +/* sysex buffer */ +struct seq_oss_synth_sysex_t { + int len; + int skip; + unsigned char buf[MAX_SYSEX_BUFLEN]; +}; + +/* synth info */ +struct seq_oss_synth_t { + int seq_device; + + /* for synth_info */ + int synth_type; + int synth_subtype; + int nr_voices; + + char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME]; + snd_seq_oss_callback_t oper; + + int opened; + + void *private_data; + snd_use_lock_t use_lock; +}; + + +/* + * device table + */ +static int max_synth_devs = 0; +static seq_oss_synth_t *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; +static seq_oss_synth_t midi_synth_dev = { + -1, /* seq_device */ + SYNTH_TYPE_MIDI, /* synth_type */ + 0, /* synth_subtype */ + 16, /* nr_voices */ + "MIDI", /* name */ +}; + +static spinlock_t register_lock = SPIN_LOCK_UNLOCKED; + +/* + * prototypes + */ +static seq_oss_synth_t *get_synthdev(seq_oss_devinfo_t *dp, int dev); +static void reset_channels(seq_oss_synthinfo_t *info); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +/* + * global initialization + */ +void __init +snd_seq_oss_synth_init(void) +{ + snd_use_lock_init(&midi_synth_dev.use_lock); +} + +/* + * registration of the synth device + */ +int +snd_seq_oss_synth_register(snd_seq_device_t *dev) +{ + int i; + seq_oss_synth_t *rec; + snd_seq_oss_reg_t *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + unsigned long flags; + + if ((rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL)) == NULL) { + snd_printk("can't malloc synth info\n"); + return -ENOMEM; + } + rec->seq_device = -1; + rec->synth_type = reg->type; + rec->synth_subtype = reg->subtype; + rec->nr_voices = reg->nvoices; + rec->oper = reg->oper; + rec->private_data = reg->private_data; + rec->opened = 0; + snd_use_lock_init(&rec->use_lock); + + /* copy and truncate the name of synth device */ + strncpy(rec->name, dev->name, sizeof(rec->name)); + rec->name[sizeof(rec->name)-1] = 0; + + /* registration */ + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_synth_devs; i++) { + if (synth_devs[i] == NULL) + break; + } + if (i >= max_synth_devs) { + if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) { + spin_unlock_irqrestore(®ister_lock, flags); + snd_printk("no more synth slot\n"); + kfree(rec); + return -ENOMEM; + } + max_synth_devs++; + } + rec->seq_device = i; + synth_devs[i] = rec; + debug_printk(("synth %s registered %d\n", rec->name, i)); + spin_unlock_irqrestore(®ister_lock, flags); + dev->driver_data = rec; + return 0; +} + + +int +snd_seq_oss_synth_unregister(snd_seq_device_t *dev) +{ + int index; + seq_oss_synth_t *rec = dev->driver_data; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + for (index = 0; index < max_synth_devs; index++) { + if (synth_devs[index] == rec) + break; + } + if (index >= max_synth_devs) { + spin_unlock_irqrestore(®ister_lock, flags); + snd_printk("can't unregister synth\n"); + return -EINVAL; + } + synth_devs[index] = NULL; + if (index == max_synth_devs - 1) { + for (index--; index >= 0; index--) { + if (synth_devs[index]) + break; + } + max_synth_devs = index + 1; + } + spin_unlock_irqrestore(®ister_lock, flags); + + snd_use_lock_sync(&rec->use_lock); + kfree(rec); + + return 0; +} + + +/* + */ +static seq_oss_synth_t * +get_sdev(int dev) +{ + seq_oss_synth_t *rec; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + rec = synth_devs[dev]; + if (rec) + snd_use_lock_use(&rec->use_lock); + spin_unlock_irqrestore(®ister_lock, flags); + return rec; +} + + +/* + * set up synth tables + */ + +void +snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp) +{ + int i; + seq_oss_synth_t *rec; + seq_oss_synthinfo_t *info; + + dp->max_synthdev = max_synth_devs; + dp->synth_opened = 0; + memset(dp->synths, 0, sizeof(dp->synths)); + for (i = 0; i < dp->max_synthdev; i++) { + rec = get_sdev(i); + if (rec == NULL) + continue; + if (rec->oper.open == NULL || rec->oper.close == NULL) { + snd_use_lock_free(&rec->use_lock); + continue; + } + info = &dp->synths[i]; + info->arg.app_index = dp->port; + info->arg.file_mode = dp->file_mode; + info->arg.seq_mode = dp->seq_mode; + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) + info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS; + else + info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS; + info->opened = 0; + if (!try_inc_mod_count(rec->oper.owner)) { + snd_use_lock_free(&rec->use_lock); + continue; + } + if (rec->oper.open(&info->arg, rec->private_data) < 0) { + dec_mod_count(rec->oper.owner); + snd_use_lock_free(&rec->use_lock); + continue; + } + info->nr_voices = rec->nr_voices; + if (info->nr_voices > 0) { + info->ch = snd_kcalloc(sizeof(seq_oss_chinfo_t) * info->nr_voices, GFP_KERNEL); + reset_channels(info); + } + debug_printk(("synth %d assigned\n", i)); + info->opened++; + rec->opened++; + dp->synth_opened++; + snd_use_lock_free(&rec->use_lock); + } +} + + +/* + * set up synth tables for MIDI emulation - /dev/music mode only + */ + +void +snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp) +{ + int i; + + if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) + return; + + for (i = 0; i < dp->max_mididev; i++) { + seq_oss_synthinfo_t *info; + info = &dp->synths[dp->max_synthdev]; + if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0) + continue; + info->arg.app_index = dp->port; + info->arg.file_mode = dp->file_mode; + info->arg.seq_mode = dp->seq_mode; + info->arg.private_data = info; + info->is_midi = 1; + info->midi_mapped = i; + info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS; + snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr); + info->opened = 1; + midi_synth_dev.opened++; + dp->max_synthdev++; + if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) + break; + } +} + + +/* + * clean up synth tables + */ + +void +snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp) +{ + int i; + seq_oss_synth_t *rec; + seq_oss_synthinfo_t *info; + + for (i = 0; i < dp->max_synthdev; i++) { + info = &dp->synths[i]; + if (! info->opened) + continue; + if (info->is_midi) { + snd_seq_oss_midi_close(dp, info->midi_mapped); + midi_synth_dev.opened--; + } else { + rec = get_sdev(i); + if (rec == NULL) + continue; + if (rec->opened) { + debug_printk(("synth %d closed\n", i)); + rec->oper.close(&info->arg); + dec_mod_count(rec->oper.owner); + rec->opened--; + } + snd_use_lock_free(&rec->use_lock); + } + if (info->sysex) + kfree(info->sysex); + if (info->ch) + kfree(info->ch); + } + dp->synth_opened = 0; + dp->max_synthdev = 0; +} + +/* + * check if the specified device is MIDI mapped device + */ +static int +is_midi_dev(seq_oss_devinfo_t *dp, int dev) +{ + if (dev < 0 || dev >= dp->max_synthdev) + return 0; + if (dp->synths[dev].is_midi) + return 1; + return 0; +} + +/* + * return synth device information pointer + */ +static seq_oss_synth_t * +get_synthdev(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_synth_t *rec; + if (dev < 0 || dev >= dp->max_synthdev) + return NULL; + if (! dp->synths[dev].opened) + return NULL; + if (dp->synths[dev].is_midi) + return &midi_synth_dev; + if ((rec = get_sdev(dev)) == NULL) + return NULL; + if (! rec->opened) { + snd_use_lock_free(&rec->use_lock); + return NULL; + } + return rec; +} + + +/* + * reset note and velocity on each channel. + */ +static void +reset_channels(seq_oss_synthinfo_t *info) +{ + int i; + if (info->ch == NULL || ! info->nr_voices) + return; + for (i = 0; i < info->nr_voices; i++) { + info->ch[i].note = -1; + info->ch[i].vel = 0; + } +} + + +/* + * reset synth device: + * call reset callback. if no callback is defined, send a heartbeat + * event to the corresponding port. + */ +void +snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_synth_t *rec; + seq_oss_synthinfo_t *info; + + snd_assert(dev >= 0 && dev < dp->max_synthdev, return); + info = &dp->synths[dev]; + if (! info->opened) + return; + if (info->sysex) + info->sysex->len = 0; /* reset sysex */ + reset_channels(info); + if (info->is_midi) { + snd_seq_oss_midi_reset(dp, info->midi_mapped); + if (snd_seq_oss_midi_open(dp, info->midi_mapped, + dp->file_mode) < 0) { + midi_synth_dev.opened--; + info->opened = 0; + if (info->sysex) + kfree(info->sysex); + if (info->ch) + kfree(info->ch); + } + return; + } + + rec = get_sdev(dev); + if (rec == NULL) + return; + if (rec->oper.reset) { + rec->oper.reset(&info->arg); + } else { + snd_seq_event_t ev; + memset(&ev, 0, sizeof(ev)); + snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client, + info->arg.addr.port); + ev.type = SNDRV_SEQ_EVENT_RESET; + snd_seq_oss_dispatch(dp, &ev, 0, 0); + } + snd_use_lock_free(&rec->use_lock); +} + + +/* + * load a patch record: + * call load_patch callback function + */ +int +snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, + const char *buf, int p, int c) +{ + seq_oss_synth_t *rec; + int rc; + + if (dev < 0 || dev >= dp->max_synthdev) + return -ENXIO; + + if (is_midi_dev(dp, dev)) + return 0; + if ((rec = get_synthdev(dp, dev)) == NULL) + return -ENXIO; + + if (rec->oper.load_patch == NULL) + rc = -ENXIO; + else + rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c); + snd_use_lock_free(&rec->use_lock); + return rc; +} + +/* + * check if the device is valid synth device + */ +int +snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_synth_t *rec; + rec = get_synthdev(dp, dev); + if (rec) { + snd_use_lock_free(&rec->use_lock); + return 1; + } + return 0; +} + + +/* + * receive OSS 6 byte sysex packet: + * the full sysex message will be sent if it reaches to the end of data + * (0xff). + */ +int +snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev) +{ + int i, send; + unsigned char *dest; + seq_oss_synth_sysex_t *sysex; + + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + sysex = dp->synths[dev].sysex; + if (sysex == NULL) { + sysex = snd_kcalloc(sizeof(*sysex), GFP_KERNEL); + if (sysex == NULL) + return -ENOMEM; + dp->synths[dev].sysex = sysex; + } + + send = 0; + dest = sysex->buf + sysex->len; + /* copy 6 byte packet to the buffer */ + for (i = 0; i < 6; i++) { + if (buf[i] == 0xff) { + send = 1; + break; + } + dest[i] = buf[i]; + sysex->len++; + if (sysex->len >= MAX_SYSEX_BUFLEN) { + sysex->len = 0; + sysex->skip = 1; + break; + } + } + + if (sysex->len && send) { + if (sysex->skip) { + sysex->skip = 0; + sysex->len = 0; + return -EINVAL; /* skip */ + } + /* copy the data to event record and send it */ + ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + if (snd_seq_oss_synth_addr(dp, dev, ev)) + return -EINVAL; + ev->data.ext.len = sysex->len; + ev->data.ext.ptr = sysex->buf; + sysex->len = 0; + return 0; + } + + return -EINVAL; /* skip */ +} + +/* + * fill the event source/destination addresses + */ +int +snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -EINVAL; + snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client, + dp->synths[dev].arg.addr.port); + return 0; +} + + +/* + * OSS compatible ioctl + */ +int +snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr) +{ + seq_oss_synth_t *rec; + int rc; + + if (is_midi_dev(dp, dev)) + return -ENXIO; + if ((rec = get_synthdev(dp, dev)) == NULL) + return -ENXIO; + if (rec->oper.ioctl == NULL) + rc = -ENXIO; + else + rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr); + snd_use_lock_free(&rec->use_lock); + return rc; +} + + +/* + * send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME + */ +int +snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev)) + return -ENXIO; + ev->type = SNDRV_SEQ_EVENT_OSS; + memcpy(ev->data.raw8.d, data, 8); + return snd_seq_oss_synth_addr(dp, dev, ev); +} + + +/* + * create OSS compatible synth_info record + */ +int +snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf) +{ + seq_oss_synth_t *rec; + + if (dp->synths[dev].is_midi) { + struct midi_info minf; + snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf); + inf->synth_type = SYNTH_TYPE_MIDI; + inf->synth_subtype = 0; + inf->nr_voices = 16; + inf->device = dev; + strncpy(inf->name, minf.name, sizeof(inf->name)); + } else { + if ((rec = get_synthdev(dp, dev)) == NULL) + return -ENXIO; + inf->synth_type = rec->synth_type; + inf->synth_subtype = rec->synth_subtype; + inf->nr_voices = rec->nr_voices; + inf->device = dev; + strncpy(inf->name, rec->name, sizeof(inf->name)); + snd_use_lock_free(&rec->use_lock); + } + return 0; +} + + +/* + * proc interface + */ +void +snd_seq_oss_synth_info_read(snd_info_buffer_t *buf) +{ + int i; + seq_oss_synth_t *rec; + + snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs); + for (i = 0; i < max_synth_devs; i++) { + snd_iprintf(buf, "\nsynth %d: ", i); + rec = get_sdev(i); + if (rec == NULL) { + snd_iprintf(buf, "*empty*\n"); + continue; + } + snd_iprintf(buf, "[%s]\n", rec->name); + snd_iprintf(buf, " type 0x%x : subtype 0x%x : voices %d\n", + rec->synth_type, rec->synth_subtype, + rec->nr_voices); + snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n", + enabled_str((long)rec->oper.ioctl), + enabled_str((long)rec->oper.load_patch)); + snd_use_lock_free(&rec->use_lock); + } +} + diff -Nru a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_synth.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,49 @@ +/* + * OSS compatible sequencer driver + * + * synth device information + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_SYNTH_H +#define __SEQ_OSS_SYNTH_H + +#include "seq_oss_device.h" +#include +#include + +typedef struct seq_oss_synth_t seq_oss_synth_t; + +void snd_seq_oss_synth_init(void); +int snd_seq_oss_synth_register(snd_seq_device_t *dev); +int snd_seq_oss_synth_unregister(snd_seq_device_t *dev); +void snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp); +void snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp); +void snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp); + +void snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, const char *buf, int p, int c); +int snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev); +int snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev); +int snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr); +int snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev); + +int snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf); + +#endif diff -Nru a/sound/core/seq/oss/seq_oss_timer.c b/sound/core/seq/oss/seq_oss_timer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_timer.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,284 @@ +/* + * OSS compatible sequencer driver + * + * Timer control routines + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_timer.h" +#include "seq_oss_event.h" +#include + +/* + */ +#define MIN_OSS_TEMPO 8 +#define MAX_OSS_TEMPO 360 +#define MIN_OSS_TIMEBASE 1 +#define MAX_OSS_TIMEBASE 1000 + +/* + */ +static void calc_alsa_tempo(seq_oss_timer_t *timer); +static int send_timer_event(seq_oss_devinfo_t *dp, int type, int value); + + +/* + * create and register a new timer. + * if queue is not started yet, start it. + */ +seq_oss_timer_t * +snd_seq_oss_timer_new(seq_oss_devinfo_t *dp) +{ + seq_oss_timer_t *rec; + + rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL); + if (rec == NULL) + return NULL; + + rec->dp = dp; + rec->cur_tick = 0; + rec->realtime = 0; + rec->running = 0; + rec->oss_tempo = 60; + rec->oss_timebase = 100; + calc_alsa_tempo(rec); + + return rec; +} + + +/* + * delete timer. + * if no more timer exists, stop the queue. + */ +void +snd_seq_oss_timer_delete(seq_oss_timer_t *rec) +{ + if (rec) { + snd_seq_oss_timer_stop(rec); + kfree(rec); + } +} + + +/* + * process one timing event + * return 1 : event proceseed -- skip this event + * 0 : not a timer event -- enqueue this event + */ +int +snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *ev) +{ + abstime_t parm = ev->t.time; + + if (ev->t.code == EV_TIMING) { + switch (ev->t.cmd) { + case TMR_WAIT_REL: + parm += rec->cur_tick; + rec->realtime = 0; + /* continue to next */ + case TMR_WAIT_ABS: + if (parm == 0) { + rec->realtime = 1; + } else if (parm >= rec->cur_tick) { + rec->realtime = 0; + rec->cur_tick = parm; + } + return 1; /* skip this event */ + + case TMR_START: + snd_seq_oss_timer_start(rec); + return 1; + + } + } else if (ev->s.code == SEQ_WAIT) { + /* time = from 1 to 3 bytes */ + parm = (ev->echo >> 8) & 0xffffff; + if (parm > rec->cur_tick) { + /* set next event time */ + rec->cur_tick = parm; + rec->realtime = 0; + } + return 1; + } + + return 0; +} + + +/* + * convert tempo units + */ +static void +calc_alsa_tempo(seq_oss_timer_t *timer) +{ + timer->tempo = (60 * 1000000) / timer->oss_tempo; + timer->ppq = timer->oss_timebase; +} + + +/* + * dispatch a timer event + */ +static int +send_timer_event(seq_oss_devinfo_t *dp, int type, int value) +{ + snd_seq_event_t ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = type; + ev.source.client = dp->cseq; + ev.source.port = 0; + ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM; + ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; + ev.queue = dp->queue; + ev.data.queue.queue = dp->queue; + ev.data.queue.param.value = value; + return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 0, 0); +} + +/* + * set queue tempo and start queue + */ +int +snd_seq_oss_timer_start(seq_oss_timer_t *timer) +{ + seq_oss_devinfo_t *dp = timer->dp; + snd_seq_queue_tempo_t tmprec; + + if (timer->running) + snd_seq_oss_timer_stop(timer); + + memset(&tmprec, 0, sizeof(tmprec)); + tmprec.queue = dp->queue; + tmprec.ppq = timer->ppq; + tmprec.tempo = timer->tempo; + snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, &tmprec); + + send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0); + timer->running = 1; + timer->cur_tick = 0; + return 0; +} + + +/* + * stop queue + */ +int +snd_seq_oss_timer_stop(seq_oss_timer_t *timer) +{ + if (! timer->running) + return 0; + send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0); + timer->running = 0; + return 0; +} + + +/* + * continue queue + */ +int +snd_seq_oss_timer_continue(seq_oss_timer_t *timer) +{ + if (timer->running) + return 0; + send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0); + timer->running = 1; + return 0; +} + + +/* + * change queue tempo + */ +int +snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value) +{ + if (value < MIN_OSS_TEMPO) + value = MIN_OSS_TEMPO; + else if (value > MAX_OSS_TEMPO) + value = MAX_OSS_TEMPO; + timer->oss_tempo = value; + calc_alsa_tempo(timer); + if (timer->running) + send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo); + return 0; +} + + +/* + * ioctls + */ +int +snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, void *arg) +{ + int value; + + if (cmd == SNDCTL_SEQ_CTRLRATE) { + debug_printk(("ctrl rate\n")); + /* if *arg == 0, just return the current rate */ + if (get_user(value, (int *)arg)) + return -EFAULT; + if (value) + return -EINVAL; + value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60; + return put_user(value, (int *)arg) ? -EFAULT : 0; + } + + if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) + return 0; + + switch (cmd) { + case SNDCTL_TMR_START: + debug_printk(("timer start\n")); + return snd_seq_oss_timer_start(timer); + case SNDCTL_TMR_STOP: + debug_printk(("timer stop\n")); + return snd_seq_oss_timer_stop(timer); + case SNDCTL_TMR_CONTINUE: + debug_printk(("timer continue\n")); + return snd_seq_oss_timer_continue(timer); + case SNDCTL_TMR_TEMPO: + debug_printk(("timer tempo\n")); + if (get_user(value, (int *)arg)) + return -EFAULT; + return snd_seq_oss_timer_tempo(timer, value); + case SNDCTL_TMR_TIMEBASE: + debug_printk(("timer timebase\n")); + if (get_user(value, (int *)arg)) + return -EFAULT; + if (value < MIN_OSS_TIMEBASE) + value = MIN_OSS_TIMEBASE; + else if (value > MAX_OSS_TIMEBASE) + value = MAX_OSS_TIMEBASE; + timer->oss_timebase = value; + calc_alsa_tempo(timer); + return 0; + + case SNDCTL_TMR_METRONOME: + case SNDCTL_TMR_SELECT: + case SNDCTL_TMR_SOURCE: + debug_printk(("timer XXX\n")); + /* not supported */ + return 0; + } + return 0; +} diff -Nru a/sound/core/seq/oss/seq_oss_timer.h b/sound/core/seq/oss/seq_oss_timer.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_timer.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,70 @@ +/* + * OSS compatible sequencer driver + * timer handling routines + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_TIMER_H +#define __SEQ_OSS_TIMER_H + +#include "seq_oss_device.h" + +/* + * timer information definition + */ +struct seq_oss_timer_t { + seq_oss_devinfo_t *dp; + reltime_t cur_tick; + int realtime; + int running; + int tempo, ppq; /* ALSA queue */ + int oss_tempo, oss_timebase; +}; + + +seq_oss_timer_t *snd_seq_oss_timer_new(seq_oss_devinfo_t *dp); +void snd_seq_oss_timer_delete(seq_oss_timer_t *dp); + +int snd_seq_oss_timer_start(seq_oss_timer_t *timer); +int snd_seq_oss_timer_stop(seq_oss_timer_t *timer); +int snd_seq_oss_timer_continue(seq_oss_timer_t *timer); +int snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value); +#define snd_seq_oss_timer_reset snd_seq_oss_timer_start + +int snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, void *arg); + +/* + * get current processed time + */ +static inline abstime_t +snd_seq_oss_timer_cur_tick(seq_oss_timer_t *timer) +{ + return timer->cur_tick; +} + + +/* + * is realtime event? + */ +static inline int +snd_seq_oss_timer_is_realtime(seq_oss_timer_t *timer) +{ + return timer->realtime; +} + +#endif diff -Nru a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_writeq.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,183 @@ +/* + * OSS compatible sequencer driver + * + * seq_oss_writeq.c - write queue and sync + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_writeq.h" +#include "seq_oss_event.h" +#include "seq_oss_timer.h" +#include +#include "../seq_lock.h" +#include "../seq_clientmgr.h" + + +/* + * create a write queue record + */ +seq_oss_writeq_t * +snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen) +{ + seq_oss_writeq_t *q; + snd_seq_client_pool_t pool; + + if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL) + return NULL; + q->dp = dp; + q->maxlen = maxlen; + spin_lock_init(&q->sync_lock); + q->sync_event_put = 0; + q->sync_time = 0; + init_waitqueue_head(&q->sync_sleep); + + memset(&pool, 0, sizeof(pool)); + pool.client = dp->cseq; + pool.output_pool = maxlen; + pool.output_room = maxlen / 2; + + snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool); + + return q; +} + +/* + * delete the write queue + */ +void +snd_seq_oss_writeq_delete(seq_oss_writeq_t *q) +{ + snd_seq_oss_writeq_clear(q); /* to be sure */ + kfree(q); +} + + +/* + * reset the write queue + */ +void +snd_seq_oss_writeq_clear(seq_oss_writeq_t *q) +{ + snd_seq_remove_events_t reset; + + memset(&reset, 0, sizeof(reset)); + reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */ + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset); + + /* wake up sleepers if any */ + snd_seq_oss_writeq_wakeup(q, 0); +} + +/* + * wait until the write buffer has enough room + */ +int +snd_seq_oss_writeq_sync(seq_oss_writeq_t *q) +{ + seq_oss_devinfo_t *dp = q->dp; + abstime_t time; + unsigned long flags; + + time = snd_seq_oss_timer_cur_tick(dp->timer); + if (q->sync_time >= time) + return 0; /* already finished */ + + if (! q->sync_event_put) { + snd_seq_event_t ev; + evrec_t *rec; + + /* put echoback event */ + memset(&ev, 0, sizeof(ev)); + ev.flags = 0; + ev.type = SNDRV_SEQ_EVENT_ECHO; + ev.time.tick = time; + /* echo back to itself */ + snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port); + rec = (evrec_t*)&ev.data; + rec->t.code = SEQ_SYNCTIMER; + rec->t.time = time; + q->sync_event_put = 1; + snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0); + } + + spin_lock_irqsave(&q->sync_lock, flags); + if (! q->sync_event_put) { /* echoback event has been received */ + spin_unlock_irqrestore(&q->sync_lock, flags); + return 0; + } + + /* wait for echo event */ + snd_seq_sleep_timeout_in_lock(&q->sync_sleep, &q->sync_lock, HZ); + if (signal_pending(current)) { + /* interrupted - return 0 to finish sync */ + q->sync_event_put = 0; + spin_unlock_irqrestore(&q->sync_lock, flags); + return 0; + } + spin_unlock_irqrestore(&q->sync_lock, flags); + if (q->sync_time >= time) + return 0; + else + return 1; +} + +/* + * wake up sync - echo event was catched + */ +void +snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time) +{ + unsigned long flags; + + spin_lock_irqsave(&q->sync_lock, flags); + q->sync_time = time; + q->sync_event_put = 0; + if (waitqueue_active(&q->sync_sleep)) { + wake_up(&q->sync_sleep); + } + spin_unlock_irqrestore(&q->sync_lock, flags); +} + + +/* + * return the unused pool size + */ +int +snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q) +{ + snd_seq_client_pool_t pool; + pool.client = q->dp->cseq; + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool); + return pool.output_free; +} + + +/* + * set output threshold size from ioctl + */ +void +snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int val) +{ + snd_seq_client_pool_t pool; + pool.client = q->dp->cseq; + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool); + pool.output_room = val; + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool); +} + diff -Nru a/sound/core/seq/oss/seq_oss_writeq.h b/sound/core/seq/oss/seq_oss_writeq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/oss/seq_oss_writeq.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,50 @@ +/* + * OSS compatible sequencer driver + * write priority queue + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_WRITEQ_H +#define __SEQ_OSS_WRITEQ_H + +#include "seq_oss_device.h" + + +struct seq_oss_writeq_t { + seq_oss_devinfo_t *dp; + int maxlen; + abstime_t sync_time; + int sync_event_put; + wait_queue_head_t sync_sleep; + spinlock_t sync_lock; +}; + + +/* + * seq_oss_writeq.c + */ +seq_oss_writeq_t *snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen); +void snd_seq_oss_writeq_delete(seq_oss_writeq_t *q); +void snd_seq_oss_writeq_clear(seq_oss_writeq_t *q); +int snd_seq_oss_writeq_sync(seq_oss_writeq_t *q); +void snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time); +int snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q); +void snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int size); + + +#endif diff -Nru a/sound/core/seq/seq.c b/sound/core/seq/seq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,141 @@ +/* + * ALSA sequencer main module + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#include +#include "seq_clientmgr.h" +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_lock.h" +#include "seq_timer.h" +#include "seq_system.h" +#include "seq_info.h" +#include + +int snd_seq_client_load[64] = {[0 ... 63] = -1}; +int snd_seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL; +int snd_seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE; +int snd_seq_default_timer_card = -1; +int snd_seq_default_timer_device = SNDRV_TIMER_GLOBAL_SYSTEM; +int snd_seq_default_timer_subdevice = 0; +int snd_seq_default_timer_resolution = 0; /* Hz */ + +MODULE_AUTHOR("Frank van de Pol , Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +MODULE_PARM(snd_seq_client_load, "i"); +MODULE_PARM_DESC(snd_seq_client_load, "The numbers of global (system) clients to load through kmod."); +MODULE_PARM(snd_seq_default_timer_class, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_class, "The default timer class."); +MODULE_PARM(snd_seq_default_timer_sclass, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_sclass, "The default timer slave class."); +MODULE_PARM(snd_seq_default_timer_card, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_card, "The default timer card number."); +MODULE_PARM(snd_seq_default_timer_device, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_device, "The default timer device number."); +MODULE_PARM(snd_seq_default_timer_subdevice, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_subdevice, "The default timer subdevice number."); +MODULE_PARM(snd_seq_default_timer_resolution, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_resolution, "The default timer resolution in Hz."); + +/* + * INIT PART + */ + + +static int __init alsa_seq_init(void) +{ + int err; + + if ((err = client_init_data()) < 0) + return err; + + /* init memory, room for selected events */ + if ((err = snd_sequencer_memory_init()) < 0) + return err; + + /* init event queues */ + if ((err = snd_seq_queues_init()) < 0) + return err; + + /* register sequencer device */ + if ((err = snd_sequencer_device_init()) < 0) + return err; + + /* register proc interface */ + if ((err = snd_seq_info_init()) < 0) + return err; + + /* register our internal client */ + if ((err = snd_seq_system_client_init()) < 0) + return err; + + return 0; +} + +static void __exit alsa_seq_exit(void) +{ + /* unregister our internal client */ + snd_seq_system_client_done(); + + /* unregister proc interface */ + snd_seq_info_done(); + + /* delete timing queues */ + snd_seq_queues_delete(); + + /* unregister sequencer device */ + snd_sequencer_device_done(); + + /* release event memory */ + snd_sequencer_memory_done(); +} + +module_init(alsa_seq_init) +module_exit(alsa_seq_exit) + + /* seq_clientmgr.c */ +EXPORT_SYMBOL(snd_seq_create_kernel_client); +EXPORT_SYMBOL(snd_seq_delete_kernel_client); +EXPORT_SYMBOL(snd_seq_kernel_client_enqueue); +EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking); +EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); +EXPORT_SYMBOL(snd_seq_kernel_client_ctl); +EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); + /* seq_memory.c */ +EXPORT_SYMBOL(snd_seq_expand_var_event); +EXPORT_SYMBOL(snd_seq_dump_var_event); + /* seq_ports.c */ +EXPORT_SYMBOL(snd_seq_event_port_attach); +EXPORT_SYMBOL(snd_seq_event_port_detach); + /* seq_lock.c */ +#if defined(__SMP__) || defined(CONFIG_SND_DEBUG) +EXPORT_SYMBOL(snd_seq_sleep_in_lock); +EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock); +EXPORT_SYMBOL(snd_use_lock_sync_helper); +#endif diff -Nru a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_clientmgr.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2470 @@ +/* + * ALSA sequencer Client Manager + * Copyright (c) 1998-2001 by Frank van de Pol + * Jaroslav Kysela + * Takashi Iwai + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +#include +#include "seq_clientmgr.h" +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_timer.h" +#include "seq_info.h" +#include "seq_system.h" +#include + +/* Client Manager + + * this module handles the connections of userland and kernel clients + * + */ + +#define SNDRV_SEQ_LFLG_INPUT 0x0001 +#define SNDRV_SEQ_LFLG_OUTPUT 0x0002 +#define SNDRV_SEQ_LFLG_OPEN (SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT) + +static spinlock_t clients_lock = SPIN_LOCK_UNLOCKED; +static DECLARE_MUTEX(register_mutex); + +/* + * client table + */ +static char clienttablock[SNDRV_SEQ_MAX_CLIENTS]; +static client_t *clienttab[SNDRV_SEQ_MAX_CLIENTS]; +static usage_t client_usage = {0, 0}; + +/* + * prototypes + */ +static int bounce_error_event(client_t *client, snd_seq_event_t *event, int err, int atomic, int hop); +static int snd_seq_deliver_single_event(client_t *client, snd_seq_event_t *event, int filter, int atomic, int hop); + +/* + */ + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +/* + */ +static inline unsigned short snd_seq_file_flags(struct file *file) +{ + switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) { + case FMODE_WRITE: + return SNDRV_SEQ_LFLG_OUTPUT; + case FMODE_READ: + return SNDRV_SEQ_LFLG_INPUT; + default: + return SNDRV_SEQ_LFLG_OPEN; + } +} + +static inline int snd_seq_write_pool_allocated(client_t *client) +{ + return snd_seq_total_cells(client->pool) > 0; +} + +/* return pointer to client structure for specified id */ +static client_t *clientptr(int clientid) +{ + if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { + snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid); + return NULL; + } + return clienttab[clientid]; +} + +extern int snd_seq_client_load[]; + +client_t *snd_seq_client_use_ptr(int clientid) +{ + unsigned long flags; + client_t *client; + + if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { + snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid); + return NULL; + } + spin_lock_irqsave(&clients_lock, flags); + client = clientptr(clientid); + if (client) + goto __lock; + if (clienttablock[clientid]) { + spin_unlock_irqrestore(&clients_lock, flags); + return NULL; + } + spin_unlock_irqrestore(&clients_lock, flags); +#ifdef CONFIG_KMOD + if (!in_interrupt()) { + if (clientid < 64) { + int idx; + char name[32]; + + for (idx = 0; idx < 64; idx++) { + if (snd_seq_client_load[idx] < 0) + break; + if (snd_seq_client_load[idx] == clientid) { + sprintf(name, "snd-seq-client-%i", clientid); + request_module(name); + break; + } + } + } else if (clientid >= 64 && clientid < 128) { + int card = (clientid - 64) / 8; + if (card < snd_ecards_limit) { +#ifndef MODULE + if (current->fs->root) { +#endif + snd_request_card(card); + snd_seq_device_load_drivers(); +#ifndef MODULE + } +#endif + } + } + spin_lock_irqsave(&clients_lock, flags); + client = clientptr(clientid); + if (client) + goto __lock; + spin_unlock_irqrestore(&clients_lock, flags); + } +#endif + return NULL; + + __lock: + snd_use_lock_use(&client->use_lock); + spin_unlock_irqrestore(&clients_lock, flags); + return client; +} + +static void usage_alloc(usage_t * res, int num) +{ + res->cur += num; + if (res->cur > res->peak) + res->peak = res->cur; +} + +static void usage_free(usage_t * res, int num) +{ + res->cur -= num; +} + +/* initialise data structures */ +int __init client_init_data(void) +{ + /* zap out the client table */ + memset(&clienttablock, 0, sizeof(clienttablock)); + memset(&clienttab, 0, sizeof(clienttab)); + return 0; +} + + +static client_t *seq_create_client1(int client_index, int poolsize) +{ + unsigned long flags; + int c; + client_t *client; + + /* init client data */ + client = snd_kcalloc(sizeof(client_t), GFP_KERNEL); + if (client == NULL) + return NULL; + client->pool = snd_seq_pool_new(poolsize); + if (client->pool == NULL) { + kfree(client); + return NULL; + } + client->type = NO_CLIENT; + snd_use_lock_init(&client->use_lock); + rwlock_init(&client->ports_lock); + init_MUTEX(&client->ports_mutex); + INIT_LIST_HEAD(&client->ports_list_head); + + /* find free slot in the client table */ + spin_lock_irqsave(&clients_lock, flags); + if (client_index < 0) { + for (c = 128; c < SNDRV_SEQ_MAX_CLIENTS; c++) { + if (clienttab[c] || clienttablock[c]) + continue; + clienttab[client->number = c] = client; + spin_unlock_irqrestore(&clients_lock, flags); + return client; + } + } else { + if (clienttab[client_index] == NULL && !clienttablock[client_index]) { + clienttab[client->number = client_index] = client; + spin_unlock_irqrestore(&clients_lock, flags); + return client; + } + } + spin_unlock_irqrestore(&clients_lock, flags); + snd_seq_pool_delete(&client->pool); + kfree(client); + return NULL; /* no free slot found or busy, return failure code */ +} + + +static int seq_free_client1(client_t *client) +{ + unsigned long flags; + + snd_assert(client != NULL, return -EINVAL); + snd_seq_delete_all_ports(client); + snd_seq_queue_client_leave(client->number); + spin_lock_irqsave(&clients_lock, flags); + clienttablock[client->number] = 1; + clienttab[client->number] = NULL; + spin_unlock_irqrestore(&clients_lock, flags); + snd_use_lock_sync(&client->use_lock); + snd_seq_queue_client_termination(client->number); + if (client->pool) + snd_seq_pool_delete(&client->pool); + spin_lock_irqsave(&clients_lock, flags); + clienttablock[client->number] = 0; + spin_unlock_irqrestore(&clients_lock, flags); + return 0; +} + + +static void seq_free_client(client_t * client) +{ + down(®ister_mutex); + switch (client->type) { + case NO_CLIENT: + snd_printk("Seq: Trying to free unused client %d\n", client->number); + break; + case USER_CLIENT: + case KERNEL_CLIENT: + seq_free_client1(client); + usage_free(&client_usage, 1); + break; + + default: + snd_printk("Seq: Trying to free client %d with undefined type = %d\n", client->number, client->type); + } + up(®ister_mutex); + + snd_seq_system_client_ev_client_exit(client->number); +} + + + +/* -------------------------------------------------------- */ + +/* create a user client */ +static int snd_seq_open(struct inode *inode, struct file *file) +{ + int c, mode; /* client id */ + client_t *client; + user_client_t *user; + + if (down_interruptible(®ister_mutex)) + return -ERESTARTSYS; + client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS); + if (client == NULL) { + up(®ister_mutex); + return -ENOMEM; /* failure code */ + } + + mode = snd_seq_file_flags(file); + if (mode & SNDRV_SEQ_LFLG_INPUT) + client->accept_input = 1; + if (mode & SNDRV_SEQ_LFLG_OUTPUT) + client->accept_output = 1; + + user = &client->data.user; + user->fifo = NULL; + user->fifo_pool_size = 0; + + if (mode & SNDRV_SEQ_LFLG_INPUT) { + user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS; + user->fifo = snd_seq_fifo_new(user->fifo_pool_size); + if (user->fifo == NULL) { + seq_free_client1(client); + kfree(client); + up(®ister_mutex); + return -ENOMEM; + } + } + + usage_alloc(&client_usage, 1); + client->type = USER_CLIENT; + up(®ister_mutex); + + c = client->number; + (user_client_t *) file->private_data = client; + + /* fill client data */ + user->file = file; + sprintf(client->name, "Client-%d", c); + + /* make others aware this new client */ + snd_seq_system_client_ev_client_start(c); + +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + + return 0; +} + +/* delete a user client */ +static int snd_seq_release(struct inode *inode, struct file *file) +{ + client_t *client = (client_t *) file->private_data; + + if (client) { + seq_free_client(client); + if (client->data.user.fifo) + snd_seq_fifo_delete(&client->data.user.fifo); + kfree(client); + } + +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + + +/* handle client read() */ +/* possible error values: + * -ENXIO invalid client or file open mode + * -ENOSPC FIFO overflow (the flag is cleared after this error report) + * -EINVAL no enough user-space buffer to write the whole event + * -EFAULT seg. fault during copy to user space + */ +static ssize_t snd_seq_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + client_t *client = (client_t *) file->private_data; + fifo_t *fifo; + int err; + long result = 0; + snd_seq_event_cell_t *cell; + + if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT)) + return -ENXIO; + + if (verify_area(VERIFY_WRITE, buf, count)) + return -EFAULT; + + /* check client structures are in place */ + snd_assert(client != NULL, return -ENXIO); + + if (!client->accept_input || (fifo = client->data.user.fifo) == NULL) + return -ENXIO; + + if (atomic_read(&fifo->overflow) > 0) { + /* buffer overflow is detected */ + snd_seq_fifo_clear(fifo); + /* return error code */ + return -ENOSPC; + } + + cell = NULL; + err = 0; + snd_seq_fifo_lock(fifo); + + /* while data available in queue */ + while (count >= sizeof(snd_seq_event_t)) { + int nonblock; + + nonblock = (file->f_flags & O_NONBLOCK) || result > 0; + if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) { + break; + } + if (snd_seq_ev_is_variable(&cell->event)) { + snd_seq_event_t tmpev; + tmpev = cell->event; + tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK; + if (copy_to_user(buf, &tmpev, sizeof(snd_seq_event_t))) { + err = -EFAULT; + break; + } + count -= sizeof(snd_seq_event_t); + buf += sizeof(snd_seq_event_t); + err = snd_seq_expand_var_event(&cell->event, count, buf, 0, sizeof(snd_seq_event_t)); + if (err < 0) + break; + result += err; + count -= err; + buf += err; + } else { + copy_to_user(buf, &cell->event, sizeof(snd_seq_event_t)); + count -= sizeof(snd_seq_event_t); + buf += sizeof(snd_seq_event_t); + } + snd_seq_cell_free(cell); + cell = NULL; /* to be sure */ + result += sizeof(snd_seq_event_t); + } + + if (err < 0) { + if (cell) + snd_seq_fifo_cell_putback(fifo, cell); + if (err == -EAGAIN && result > 0) + err = 0; + } + snd_seq_fifo_unlock(fifo); + + return (err < 0) ? err : result; +} + + +/* + * check access permission to the port + */ +static int check_port_perm(client_port_t *port, unsigned int flags) +{ + if ((port->capability & flags) != flags) + return 0; + return flags; +} + +/* + * check if the destination client is available, and return the pointer + * if filter is non-zero, client filter bitmap is tested. + */ +static client_t *get_event_dest_client(snd_seq_event_t *event, int filter) +{ + client_t *dest; + + dest = snd_seq_client_use_ptr(event->dest.client); + if (dest == NULL) + return NULL; + if (! dest->accept_input) + goto __not_avail; + if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) && + ! test_bit(event->type, &dest->event_filter)) + goto __not_avail; + if (filter && !(dest->filter & filter)) + goto __not_avail; + + return dest; /* ok - accessible */ +__not_avail: + snd_seq_client_unlock(dest); + return NULL; +} + + +/* + * Return the error event. + * + * If the receiver client is a user client, the original event is + * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. If + * the original event is also variable length, the external data is + * copied after the event record. + * If the receiver client is a kernel client, the original event is + * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra + * kmalloc. + */ +static int bounce_error_event(client_t *client, snd_seq_event_t *event, + int err, int atomic, int hop) +{ + snd_seq_event_t bounce_ev; + int result; + + if (client == NULL || + ! (client->filter & SNDRV_SEQ_FILTER_BOUNCE) || + ! client->accept_input) + return 0; /* ignored */ + + /* set up quoted error */ + memset(&bounce_ev, 0, sizeof(bounce_ev)); + bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR; + bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; + bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT; + bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM; + bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + bounce_ev.dest.client = client->number; + bounce_ev.dest.port = event->source.port; + bounce_ev.data.quote.origin = event->dest; + bounce_ev.data.quote.event = event; + bounce_ev.data.quote.value = -err; /* use positive value */ + result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1); + if (result < 0) { + client->event_lost++; + return result; + } + + return result; +} + + +/* + * deliver an event to the specified destination. + * if filter is non-zero, client filter bitmap is tested. + * + * RETURN VALUE: 0 : if succeeded + * <0 : error + */ +static int snd_seq_deliver_single_event(client_t *client, + snd_seq_event_t *event, + int filter, int atomic, int hop) +{ + client_t *dest = NULL; + client_port_t *dest_port = NULL; + int result = -ENOENT; + int direct, quoted = 0; + + direct = snd_seq_ev_is_direct(event); + + dest = get_event_dest_client(event, filter); + if (dest == NULL) + goto __skip; + dest_port = snd_seq_port_use_ptr(dest, event->dest.port); + if (dest_port == NULL) + goto __skip; + + /* check permission */ + if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) { + result = -EPERM; + goto __skip; + } + + /* expand the quoted event */ + if (event->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) { + quoted = 1; + event = event->data.quote.event; + if (event == NULL) { + snd_printd("seq: quoted event is NULL\n"); + result = 0; /* do not send bounce error */ + goto __skip; + } + } + + switch (dest->type) { + case USER_CLIENT: + if (dest->data.user.fifo) + result = snd_seq_fifo_event_in(dest->data.user.fifo, event); + break; + + case KERNEL_CLIENT: + if (dest_port->event_input == NULL) + break; + result = dest_port->event_input(event, direct, dest_port->private_data, atomic, hop); + break; + default: + break; + } + + __skip: + if (dest_port) + snd_seq_port_unlock(dest_port); + if (dest) + snd_seq_client_unlock(dest); + + if (result < 0 && !direct) { + if (quoted) { + /* return directly to the original source */ + dest = snd_seq_client_use_ptr(event->source.client); + result = bounce_error_event(dest, event, result, atomic, hop); + snd_seq_client_unlock(dest); + } else { + result = bounce_error_event(client, event, result, atomic, hop); + } + } + return result; +} + + +static void snd_seq_subs_update_event_header(subscribers_t *subs, snd_seq_event_t *event) +{ + if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) { + /* convert time according to flag with subscription */ + queue_t *q; + q = queueptr(subs->info.queue); + if (q) { + event->queue = subs->info.queue; + event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK; + if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) { + event->time.time = snd_seq_timer_get_cur_time(q->timer); + event->flags |= SNDRV_SEQ_TIME_STAMP_REAL; + } else { + event->time.tick = snd_seq_timer_get_cur_tick(q->timer); + event->flags |= SNDRV_SEQ_TIME_STAMP_TICK; + } + queuefree(q); + } + } +} + +/* + * send the event to all subscribers: + */ +static int deliver_to_subscribers(client_t *client, + snd_seq_event_t *event, + int atomic, int hop) +{ + subscribers_t *subs; + int err = 0, num_ev = 0; + snd_seq_event_t event_saved; + client_port_t *src_port; + struct list_head *p; + port_subs_info_t *grp; + + src_port = snd_seq_port_use_ptr(client, event->source.port); + if (src_port == NULL) + return -EINVAL; /* invalid source port */ + /* save original event record */ + event_saved = *event; + grp = &src_port->c_src; + + /* lock list */ + if (atomic) + read_lock(&grp->list_lock); + else + down_read(&grp->list_mutex); + list_for_each(p, &grp->list_head) { + subs = list_entry(p, subscribers_t, src_list); + event->dest = subs->info.dest; + snd_seq_subs_update_event_header(subs, event); + err = snd_seq_deliver_single_event(client, event, + 0, atomic, hop); + if (err < 0) + break; + num_ev++; + /* restore original event record */ + *event = event_saved; + } + if (atomic) + read_unlock(&grp->list_lock); + else + up_read(&grp->list_mutex); + *event = event_saved; /* restore */ + snd_seq_port_unlock(src_port); + return (err < 0) ? err : num_ev; +} + + +#ifdef SUPPORT_BROADCAST +/* + * broadcast to all ports: + */ +static int port_broadcast_event(client_t *client, + snd_seq_event_t *event, + int atomic, int hop) +{ + int num_ev = 0, err = 0; + client_t *dest_client; + struct list_head *p; + + dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST); + if (dest_client == NULL) + return 0; /* no matching destination */ + + read_lock(&client->ports_lock); + list_for_each(p, &client->ports_list_head) { + client_port_t *port = list_entry(p, client_port_t, list); + event->dest.port = port->addr.port; + /* pass NULL as source client to avoid error bounce */ + err = snd_seq_deliver_single_event(NULL, event, + SNDRV_SEQ_FILTER_BROADCAST, + atomic, hop); + if (err < 0) + break; + num_ev++; + } + read_unlock(&client->ports_lock); + snd_seq_client_unlock(dest_client); + event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */ + return (err < 0) ? err : num_ev; +} + +/* + * send the event to all clients: + * if destination port is also ADDRESS_BROADCAST, deliver to all ports. + */ +static int broadcast_event(client_t *client, + snd_seq_event_t *event, int atomic, int hop) +{ + int err = 0, num_ev = 0; + int dest; + snd_seq_addr_t addr; + + addr = event->dest; /* save */ + + for (dest = 0; dest < SNDRV_SEQ_MAX_CLIENTS; dest++) { + /* don't send to itself */ + if (dest == client->number) + continue; + event->dest.client = dest; + event->dest.port = addr.port; + if (addr.port == SNDRV_SEQ_ADDRESS_BROADCAST) + err = port_broadcast_event(client, event, atomic, hop); + else + /* pass NULL as source client to avoid error bounce */ + err = snd_seq_deliver_single_event(NULL, event, + SNDRV_SEQ_FILTER_BROADCAST, + atomic, hop); + if (err < 0) + break; + num_ev += err; + } + event->dest = addr; /* restore */ + return (err < 0) ? err : num_ev; +} + + +/* multicast - not supported yet */ +static int multicast_event(client_t *client, snd_seq_event_t *event, + int atomic, int hop) +{ + snd_printd("seq: multicast not supported yet.\n"); + return 0; /* ignored */ +} +#endif /* SUPPORT_BROADCAST */ + + +/* deliver an event to the destination port(s). + * if the event is to subscribers or broadcast, the event is dispatched + * to multiple targets. + * + * RETURN VALUE: n > 0 : the number of delivered events. + * n == 0 : the event was not passed to any client. + * n < 0 : error - event was not processed. + */ +int snd_seq_deliver_event(client_t *client, snd_seq_event_t *event, + int atomic, int hop) +{ + int result; + + hop++; + if (hop >= SNDRV_SEQ_MAX_HOPS) { + snd_printd("too long delivery path (%d:%d->%d:%d)\n", + event->source.client, event->source.port, + event->dest.client, event->dest.port); + return -EMLINK; + } + + if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS || + event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) + result = deliver_to_subscribers(client, event, atomic, hop); +#ifdef SUPPORT_BROADCAST + else if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST || + event->dest.client == SNDRV_SEQ_ADDRESS_BROADCAST) + result = broadcast_event(client, event, atomic, hop); + else if (event->dest.client >= SNDRV_SEQ_MAX_CLIENTS) + result = multicast_event(client, event, atomic, hop); + else if (event->dest.port == SNDRV_SEQ_ADDRESS_BROADCAST) + result = port_broadcast_event(client, event, atomic, hop); +#endif + else + result = snd_seq_deliver_single_event(client, event, 0, atomic, hop); + + return result; +} + +/* + * dispatch an event cell: + * This function is called only from queue check routines in timer + * interrupts or after enqueued. + * The event cell shall be released or re-queued in this function. + * + * RETURN VALUE: n > 0 : the number of delivered events. + * n == 0 : the event was not passed to any client. + * n < 0 : error - event was not processed. + */ +int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop) +{ + client_t *client; + int result; + + snd_assert(cell != NULL, return -EINVAL); + + client = snd_seq_client_use_ptr(cell->event.source.client); + if (client == NULL) { + snd_seq_cell_free(cell); /* release this cell */ + return -EINVAL; + } + + if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) { + /* NOTE event: + * the event cell is re-used as a NOTE-OFF event and + * enqueued again. + */ + snd_seq_event_t tmpev, *ev; + + /* reserve this event to enqueue note-off later */ + tmpev = cell->event; + tmpev.type = SNDRV_SEQ_EVENT_NOTEON; + result = snd_seq_deliver_event(client, &tmpev, atomic, hop); + + /* + * This was originally a note event. We now re-use the + * cell for the note-off event. + */ + + ev = &cell->event; + ev->type = SNDRV_SEQ_EVENT_NOTEOFF; + ev->flags |= SNDRV_SEQ_PRIORITY_HIGH; + + /* add the duration time */ + switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + ev->time.tick += ev->data.note.duration; + break; + case SNDRV_SEQ_TIME_STAMP_REAL: + /* unit for duration is ms */ + ev->time.time.tv_nsec += 1000000 * (ev->data.note.duration % 1000); + ev->time.time.tv_sec += ev->data.note.duration / 1000 + + ev->time.time.tv_nsec / 1000000000; + ev->time.time.tv_nsec %= 1000000000; + break; + } + ev->data.note.velocity = ev->data.note.off_velocity; + + /* Now queue this cell as the note off event */ + if (snd_seq_enqueue_event(cell, atomic, hop) < 0) + snd_seq_cell_free(cell); /* release this cell */ + + } else { + /* Normal events: + * event cell is freed after processing the event + */ + + result = snd_seq_deliver_event(client, &cell->event, atomic, hop); + snd_seq_cell_free(cell); + } + + snd_seq_client_unlock(client); + return result; +} + + +/* Allocate a cell from client pool and enqueue it to queue: + * if pool is empty and blocking is TRUE, sleep until a new cell is + * available. + */ +static int snd_seq_client_enqueue_event(client_t *client, + snd_seq_event_t *event, + struct file *file, int blocking, + int atomic, int hop) +{ + snd_seq_event_cell_t *cell; + int err; + + /* special queue values - force direct passing */ + if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) { + event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + event->queue = SNDRV_SEQ_QUEUE_DIRECT; + } else +#ifdef SUPPORT_BROADCAST + if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST) { + event->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST; + event->queue = SNDRV_SEQ_QUEUE_DIRECT; + } +#endif + if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) { + /* check presence of source port */ + client_port_t *src_port = snd_seq_port_use_ptr(client, event->source.port); + if (src_port == NULL) + return -EINVAL; + snd_seq_port_unlock(src_port); + } + + /* direct event processing without enqueued */ + if (snd_seq_ev_is_direct(event)) { + if (event->type == SNDRV_SEQ_EVENT_NOTE) + return -EINVAL; /* this event must be enqueued! */ + return snd_seq_deliver_event(client, event, atomic, hop); + } + + /* Not direct, normal queuing */ + if (snd_seq_queue_is_used(event->queue, client->number) <= 0) + return -EINVAL; /* invalid queue */ + if (! snd_seq_write_pool_allocated(client)) + return -ENXIO; /* queue is not allocated */ + + /* allocate an event cell */ + err = snd_seq_event_dup(client->pool, event, &cell, !blocking && !atomic, file); + if (err < 0) + return err; + + /* we got a cell. enqueue it. */ + if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) { + snd_seq_cell_free(cell); + return err; + } + + return 0; +} + + +/* + * check validity of event type and data length. + * return non-zero if invalid. + */ +static int check_event_type_and_length(snd_seq_event_t *ev) +{ + switch (snd_seq_ev_length_type(ev)) { + case SNDRV_SEQ_EVENT_LENGTH_FIXED: + if (snd_seq_ev_is_variable_type(ev) || + snd_seq_ev_is_varipc_type(ev)) + return -EINVAL; + break; + case SNDRV_SEQ_EVENT_LENGTH_VARIABLE: + if (! snd_seq_ev_is_variable_type(ev) || + (ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK) >= SNDRV_SEQ_MAX_EVENT_LEN) + return -EINVAL; + break; + case SNDRV_SEQ_EVENT_LENGTH_VARUSR: + if (! snd_seq_ev_is_instr_type(ev) || + ! snd_seq_ev_is_direct(ev)) + return -EINVAL; + break; + case SNDRV_SEQ_EVENT_LENGTH_VARIPC: + if (! snd_seq_ev_is_varipc_type(ev)) + return -EINVAL; + break; + } + return 0; +} + + +/* handle write() */ +/* possible error values: + * -ENXIO invalid client or file open mode + * -ENOMEM malloc failed + * -EFAULT seg. fault during copy from user space + * -EINVAL invalid event + * -EAGAIN no space in output pool + * -EINTR interrupts while sleep + * -EMLINK too many hops + * others depends on return value from driver callback + */ +static ssize_t snd_seq_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + client_t *client = (client_t *) file->private_data; + int written = 0, len; + int err = -EINVAL; + snd_seq_event_t event; + + if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT)) + return -ENXIO; + + /* check client structures are in place */ + snd_assert(client != NULL, return -ENXIO); + + if (!client->accept_output || client->pool == NULL) + return -ENXIO; + + /* allocate the pool now if the pool is not allocated yet */ + if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) { + if (snd_seq_pool_init(client->pool) < 0) + return -ENOMEM; + } + + /* only process whole events */ + while (count >= sizeof(snd_seq_event_t)) { + /* Read in the event header from the user */ + len = sizeof(event); + if (copy_from_user(&event, buf, len)) { + err = -EFAULT; + break; + } + event.source.client = client->number; /* fill in client number */ + /* Check for extension data length */ + if (check_event_type_and_length(&event)) { + err = -EINVAL; + break; + } + + /* check for special events */ + if (event.type == SNDRV_SEQ_EVENT_NONE) + goto __skip_event; + else if (snd_seq_ev_is_reserved(&event)) { + err = -EINVAL; + break; + } + + if (snd_seq_ev_is_variable(&event)) { + int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK; + if (extlen + len > count) { + /* back out, will get an error this time or next */ + err = -EINVAL; + break; + } + /* set user space pointer */ + event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR; + event.data.ext.ptr = (char*)buf + sizeof(snd_seq_event_t); + len += extlen; /* increment data length */ + } + + /* ok, enqueue it */ + err = snd_seq_client_enqueue_event(client, &event, file, + !(file->f_flags & O_NONBLOCK), + 0, 0); + if (err < 0) + break; + + __skip_event: + /* Update pointers and counts */ + count -= len; + buf += len; + written += len; + } + + return written ? written : err; +} + + +/* + * handle polling + */ +static unsigned int snd_seq_poll(struct file *file, poll_table * wait) +{ + client_t *client = (client_t *) file->private_data; + unsigned int mask = 0; + + /* check client structures are in place */ + snd_assert(client != NULL, return -ENXIO); + + if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) && + client->data.user.fifo) { + + /* check if data is available in the outqueue */ + if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait)) + mask |= POLLIN | POLLRDNORM; + } + + if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) { + + /* check if data is available in the pool */ + if (!snd_seq_write_pool_allocated(client) || + snd_seq_pool_poll_wait(client->pool, file, wait)) + mask |= POLLOUT | POLLWRNORM; + } + + return mask; +} + + +/*-----------------------------------------------------*/ + + +/* SYSTEM_INFO ioctl() */ +static int snd_seq_ioctl_system_info(client_t *client, unsigned long arg) +{ + snd_seq_system_info_t info; + + memset(&info, 0, sizeof(info)); + /* fill the info fields */ + info.queues = SNDRV_SEQ_MAX_QUEUES; + info.clients = SNDRV_SEQ_MAX_CLIENTS; + info.ports = 256; /* fixed limit */ + info.channels = 256; /* fixed limit */ + info.cur_clients = client_usage.cur; + info.cur_queues = snd_seq_queue_get_cur_queues(); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + + +/* CLIENT_INFO ioctl() */ +static void get_client_info(client_t *cptr, snd_seq_client_info_t *info) +{ + info->client = cptr->number; + + /* fill the info fields */ + info->type = cptr->type; + strcpy(info->name, cptr->name); + info->filter = cptr->filter; + info->event_lost = cptr->event_lost; + *info->event_filter = *cptr->event_filter; + info->num_ports = cptr->num_ports; + memset(info->reserved, 0, sizeof(info->reserved)); +} + +static int snd_seq_ioctl_get_client_info(client_t * client, unsigned long arg) +{ + client_t *cptr; + snd_seq_client_info_t client_info; + + if (copy_from_user(&client_info, (void*)arg, sizeof(client_info))) + return -EFAULT; + + /* requested client number */ + cptr = snd_seq_client_use_ptr(client_info.client); + if (cptr == NULL) + return -ENOENT; /* don't change !!! */ + + get_client_info(cptr, &client_info); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &client_info, sizeof(client_info))) + return -EFAULT; + return 0; +} + + +/* CLIENT_INFO ioctl() */ +static int snd_seq_ioctl_set_client_info(client_t * client, unsigned long arg) +{ + snd_seq_client_info_t client_info; + + if (copy_from_user(&client_info, (void*)arg, sizeof(client_info))) + return -EFAULT; + + /* it is not allowed to set the info fields for an another client */ + if (client->number != client_info.client) + return -EPERM; + /* also client type must be set now */ + if (client->type != client_info.type) + return -EINVAL; + + /* fill the info fields */ + if (client_info.name[0]) { + strncpy(client->name, client_info.name, sizeof(client->name)-1); + client->name[sizeof(client->name)-1] = '\0'; + } + client->filter = client_info.filter; + client->event_lost = client_info.event_lost; + *client->event_filter = *client_info.event_filter; + + return 0; +} + + +/* + * CREATE PORT ioctl() + */ +static int snd_seq_ioctl_create_port(client_t * client, unsigned long arg) +{ + client_port_t *port; + snd_seq_port_info_t info; + snd_seq_port_callback_t *callback; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* it is not allowed to create the port for an another client */ + if (info.addr.client != client->number) + return -EPERM; + + port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1); + if (port == NULL) + return -ENOMEM; + + if (client->type == USER_CLIENT && info.kernel) { + snd_seq_delete_port(client, port->addr.port); + return -EINVAL; + } + if (client->type == KERNEL_CLIENT) { + if ((callback = info.kernel) != NULL) { + if (callback->owner) + port->owner = callback->owner; + port->private_data = callback->private_data; + port->private_free = callback->private_free; + port->callback_all = callback->callback_all; + port->event_input = callback->event_input; + port->c_src.open = callback->subscribe; + port->c_src.close = callback->unsubscribe; + port->c_dest.open = callback->use; + port->c_dest.close = callback->unuse; + } + } + + info.addr = port->addr; + + snd_seq_set_port_info(port, &info); + snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* + * DELETE PORT ioctl() + */ +static int snd_seq_ioctl_delete_port(client_t * client, unsigned long arg) +{ + snd_seq_port_info_t info; + int err; + + /* set passed parameters */ + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* it is not allowed to remove the port for an another client */ + if (info.addr.client != client->number) + return -EPERM; + + err = snd_seq_delete_port(client, info.addr.port); + if (err >= 0) + snd_seq_system_client_ev_port_exit(client->number, info.addr.port); + return err; +} + + +/* + * GET_PORT_INFO ioctl() (on any client) + */ +static int snd_seq_ioctl_get_port_info(client_t *client, unsigned long arg) +{ + client_t *cptr; + client_port_t *port; + snd_seq_port_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + cptr = snd_seq_client_use_ptr(info.addr.client); + if (cptr == NULL) + return -ENXIO; + + port = snd_seq_port_use_ptr(cptr, info.addr.port); + if (port == NULL) { + snd_seq_client_unlock(cptr); + return -ENOENT; /* don't change */ + } + + /* get port info */ + snd_seq_get_port_info(port, &info); + snd_seq_port_unlock(port); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + + +/* + * SET_PORT_INFO ioctl() (only ports on this/own client) + */ +static int snd_seq_ioctl_set_port_info(client_t * client, unsigned long arg) +{ + client_port_t *port; + snd_seq_port_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (info.addr.client != client->number) /* only set our own ports ! */ + return -EPERM; + port = snd_seq_port_use_ptr(client, info.addr.port); + if (port) { + snd_seq_set_port_info(port, &info); + snd_seq_port_unlock(port); + } + return 0; +} + + +/* + * port subscription (connection) + */ +#define PERM_RD (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ) +#define PERM_WR (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE) + +static int check_subscription_permission(client_t *client, client_port_t *sport, + client_port_t *dport, + snd_seq_port_subscribe_t *subs) +{ + if (client->number != subs->sender.client && + client->number != subs->dest.client) { + /* connection by third client - check export permission */ + if (check_port_perm(sport, SNDRV_SEQ_PORT_CAP_NO_EXPORT)) + return -EPERM; + if (check_port_perm(dport, SNDRV_SEQ_PORT_CAP_NO_EXPORT)) + return -EPERM; + } + + /* check read permission */ + /* if sender or receiver is the subscribing client itself, + * no permission check is necessary + */ + if (client->number != subs->sender.client) { + if (! check_port_perm(sport, PERM_RD)) + return -EPERM; + } + /* check write permission */ + if (client->number != subs->dest.client) { + if (! check_port_perm(dport, PERM_WR)) + return -EPERM; + } + return 0; +} + +/* + * send an subscription notify event to user client: + * client must be user client. + */ +int snd_seq_client_notify_subscription(int client, int port, + snd_seq_port_subscribe_t *info, int evtype) +{ + snd_seq_event_t event; + + memset(&event, 0, sizeof(event)); + event.type = evtype; + event.data.connect.dest = info->dest; + event.data.connect.sender = info->sender; + + return snd_seq_system_notify(client, port, &event); /* non-atomic */ +} + + +/* + * add to port's subscription list IOCTL interface + */ +static int snd_seq_ioctl_subscribe_port(client_t * client, unsigned long arg) +{ + int result = -EINVAL; + client_t *receiver = NULL, *sender = NULL; + client_port_t *sport = NULL, *dport = NULL; + snd_seq_port_subscribe_t subs; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL) + goto __end; + if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + goto __end; + if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + goto __end; + if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL) + goto __end; + + result = check_subscription_permission(client, sport, dport, &subs); + if (result < 0) + goto __end; + + /* connect them */ + result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs); + if (! result) /* broadcast announce */ + snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0, + &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); + __end: + if (sport) + snd_seq_port_unlock(sport); + if (dport) + snd_seq_port_unlock(dport); + if (sender) + snd_seq_client_unlock(sender); + if (receiver) + snd_seq_client_unlock(receiver); + return result; +} + + +/* + * remove from port's subscription list + */ +static int snd_seq_ioctl_unsubscribe_port(client_t * client, unsigned long arg) +{ + int result = -ENXIO; + client_t *receiver = NULL, *sender = NULL; + client_port_t *sport = NULL, *dport = NULL; + snd_seq_port_subscribe_t subs; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL) + goto __end; + if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + goto __end; + if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + goto __end; + if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL) + goto __end; + + result = check_subscription_permission(client, sport, dport, &subs); + if (result < 0) + goto __end; + + result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs); + if (! result) /* broadcast announce */ + snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0, + &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); + __end: + if (sport) + snd_seq_port_unlock(sport); + if (dport) + snd_seq_port_unlock(dport); + if (sender) + snd_seq_client_unlock(sender); + if (receiver) + snd_seq_client_unlock(receiver); + return result; +} + + +/* CREATE_QUEUE ioctl() */ +static int snd_seq_ioctl_create_queue(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + int result; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + result = snd_seq_queue_alloc(client->number, info.locked, info.flags); + if (result < 0) + return result; + + q = queueptr(result); + if (q == NULL) + return -EINVAL; + + info.queue = q->queue; + info.locked = q->locked; + info.owner = q->owner; + + /* set queue name */ + if (! info.name[0]) + sprintf(info.name, "Queue-%d", q->queue); + strncpy(q->name, info.name, sizeof(q->name)-1); + q->name[sizeof(q->name)-1] = 0; + queuefree(q); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* DELETE_QUEUE ioctl() */ +static int snd_seq_ioctl_delete_queue(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + return snd_seq_queue_delete(client->number, info.queue); +} + +/* GET_QUEUE_INFO ioctl() */ +static int snd_seq_ioctl_get_queue_info(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + q = queueptr(info.queue); + if (q == NULL) + return -EINVAL; + + memset(&info, 0, sizeof(info)); + info.queue = q->queue; + info.owner = q->owner; + info.locked = q->locked; + strncpy(info.name, q->name, sizeof(info.name) - 1); + info.name[sizeof(info.name)-1] = 0; + queuefree(q); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* SET_QUEUE_INFO ioctl() */ +static int snd_seq_ioctl_set_queue_info(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (info.owner != client->number) + return -EINVAL; + + /* change owner/locked permission */ + if (snd_seq_queue_check_access(info.queue, client->number)) { + if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0) + return -EPERM; + if (info.locked) + snd_seq_queue_use(info.queue, client->number, 1); + } else { + return -EPERM; + } + + q = queueptr(info.queue); + if (! q) + return -EINVAL; + if (q->owner != client->number) { + queuefree(q); + return -EPERM; + } + strncpy(q->name, info.name, sizeof(q->name) - 1); + q->name[sizeof(q->name)-1] = 0; + queuefree(q); + + return 0; +} + +/* GET_NAMED_QUEUE ioctl() */ +static int snd_seq_ioctl_get_named_queue(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + q = snd_seq_queue_find_name(info.name); + if (q == NULL) + return -EINVAL; + info.queue = q->queue; + info.owner = q->owner; + info.locked = q->locked; + queuefree(q); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* GET_QUEUE_STATUS ioctl() */ +static int snd_seq_ioctl_get_queue_status(client_t * client, unsigned long arg) +{ + snd_seq_queue_status_t status; + queue_t *queue; + seq_timer_t *tmr; + + if (copy_from_user(&status, (void*)arg, sizeof(status))) + return -EFAULT; + + queue = queueptr(status.queue); + if (queue == NULL) + return -EINVAL; + memset(&status, 0, sizeof(status)); + status.queue = queue->queue; + + tmr = queue->timer; + status.events = queue->tickq->cells + queue->timeq->cells; + + status.time = snd_seq_timer_get_cur_time(tmr); + status.tick = snd_seq_timer_get_cur_tick(tmr); + + status.running = tmr->running; + + status.flags = queue->flags; + queuefree(queue); + + if (copy_to_user((void*)arg, &status, sizeof(status))) + return -EFAULT; + return 0; +} + + +/* GET_QUEUE_TEMPO ioctl() */ +static int snd_seq_ioctl_get_queue_tempo(client_t * client, unsigned long arg) +{ + snd_seq_queue_tempo_t tempo; + queue_t *queue; + seq_timer_t *tmr; + + if (copy_from_user(&tempo, (void*)arg, sizeof(tempo))) + return -EFAULT; + + queue = queueptr(tempo.queue); + if (queue == NULL) + return -EINVAL; + memset(&tempo, 0, sizeof(tempo)); + tempo.queue = queue->queue; + + tmr = queue->timer; + + tempo.tempo = tmr->tempo; + tempo.ppq = tmr->ppq; + tempo.skew_value = tmr->skew; + tempo.skew_base = tmr->skew_base; + queuefree(queue); + + if (copy_to_user((void*)arg, &tempo, sizeof(tempo))) + return -EFAULT; + return 0; +} + + +/* SET_QUEUE_TEMPO ioctl() */ +static int snd_seq_ioctl_set_queue_tempo(client_t * client, unsigned long arg) +{ + int result; + snd_seq_queue_tempo_t tempo; + + if (copy_from_user(&tempo, (void*)arg, sizeof(tempo))) + return -EFAULT; + + if (snd_seq_queue_check_access(tempo.queue, client->number)) { + result = snd_seq_queue_timer_set_tempo(tempo.queue, client->number, &tempo); + if (result < 0) + return result; + } else { + return -EPERM; + } + + return 0; +} + + +/* GET_QUEUE_TIMER ioctl() */ +static int snd_seq_ioctl_get_queue_timer(client_t * client, unsigned long arg) +{ + snd_seq_queue_timer_t timer; + queue_t *queue; + seq_timer_t *tmr; + + if (copy_from_user(&timer, (void*)arg, sizeof(timer))) + return -EFAULT; + + queue = queueptr(timer.queue); + if (queue == NULL) + return -EINVAL; + + if (down_interruptible(&queue->timer_mutex)) { + queuefree(queue); + return -ERESTARTSYS; + } + tmr = queue->timer; + memset(&timer, 0, sizeof(timer)); + timer.queue = queue->queue; + + timer.type = tmr->type; + if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { + timer.u.alsa.id = tmr->alsa_id; + timer.u.alsa.resolution = tmr->preferred_resolution; + } + up(&queue->timer_mutex); + queuefree(queue); + + if (copy_to_user((void*)arg, &timer, sizeof(timer))) + return -EFAULT; + return 0; +} + + +/* SET_QUEUE_TIMER ioctl() */ +static int snd_seq_ioctl_set_queue_timer(client_t * client, unsigned long arg) +{ + int result = 0; + snd_seq_queue_timer_t timer; + + if (copy_from_user(&timer, (void*)arg, sizeof(timer))) + return -EFAULT; + + if (timer.type != SNDRV_SEQ_TIMER_ALSA) + return -EINVAL; + + if (snd_seq_queue_check_access(timer.queue, client->number)) { + queue_t *q; + seq_timer_t *tmr; + + q = queueptr(timer.queue); + if (q == NULL) + return -ENXIO; + if (down_interruptible(&q->timer_mutex)) { + queuefree(q); + return -ERESTARTSYS; + } + tmr = q->timer; + snd_seq_queue_timer_close(timer.queue); + tmr->type = timer.type; + if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { + tmr->alsa_id = timer.u.alsa.id; + tmr->preferred_resolution = timer.u.alsa.resolution; + } + result = snd_seq_queue_timer_open(timer.queue); + up(&q->timer_mutex); + queuefree(q); + } else { + return -EPERM; + } + + return result; +} + + +/* GET_QUEUE_CLIENT ioctl() */ +static int snd_seq_ioctl_get_queue_client(client_t * client, unsigned long arg) +{ + snd_seq_queue_client_t info; + int used; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + used = snd_seq_queue_is_used(info.queue, client->number); + if (used < 0) + return -EINVAL; + info.used = used; + info.client = client->number; + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + + +/* SET_QUEUE_CLIENT ioctl() */ +static int snd_seq_ioctl_set_queue_client(client_t * client, unsigned long arg) +{ + int err; + snd_seq_queue_client_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (info.used >= 0) { + err = snd_seq_queue_use(info.queue, client->number, info.used); + if (err < 0) + return err; + } + + return snd_seq_ioctl_get_queue_client(client, arg); +} + + +/* GET_CLIENT_POOL ioctl() */ +static int snd_seq_ioctl_get_client_pool(client_t * client, unsigned long arg) +{ + snd_seq_client_pool_t info; + client_t *cptr; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + cptr = snd_seq_client_use_ptr(info.client); + if (cptr == NULL) + return -ENOENT; + memset(&info, 0, sizeof(info)); + info.output_pool = cptr->pool->size; + info.output_room = cptr->pool->room; + info.output_free = info.output_pool; + if (cptr->pool) + info.output_free = snd_seq_unused_cells(cptr->pool); + if (cptr->type == USER_CLIENT) { + info.input_pool = cptr->data.user.fifo_pool_size; + info.input_free = info.input_pool; + if (cptr->data.user.fifo) + info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool); + } else { + info.input_pool = 0; + info.input_free = 0; + } + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +/* SET_CLIENT_POOL ioctl() */ +static int snd_seq_ioctl_set_client_pool(client_t * client, unsigned long arg) +{ + snd_seq_client_pool_t info; + int rc; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (client->number != info.client) + return -EINVAL; /* can't change other clients */ + + if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS && + (! snd_seq_write_pool_allocated(client) || + info.output_pool != client->pool->size)) { + if (snd_seq_write_pool_allocated(client)) { + /* remove all existing cells */ + snd_seq_queue_client_leave_cells(client->number); + snd_seq_pool_done(client->pool); + } + client->pool->size = info.output_pool; + rc = snd_seq_pool_init(client->pool); + if (rc < 0) + return rc; + } + if (client->type == USER_CLIENT && client->data.user.fifo != NULL && + info.input_pool >= 1 && + info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS && + info.input_pool != client->data.user.fifo_pool_size) { + /* change pool size */ + rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool); + if (rc < 0) + return rc; + client->data.user.fifo_pool_size = info.input_pool; + } + if (info.output_room >= 1 && + info.output_room <= client->pool->size) { + client->pool->room = info.output_room; + } + + return snd_seq_ioctl_get_client_pool(client, arg); +} + + +/* REMOVE_EVENTS ioctl() */ +static int snd_seq_ioctl_remove_events(client_t * client, unsigned long arg) +{ + snd_seq_remove_events_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* + * Input mostly not implemented XXX. + */ + if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) { + /* + * No restrictions so for a user client we can clear + * the whole fifo + */ + if (client->type == USER_CLIENT) + snd_seq_fifo_clear(client->data.user.fifo); + } + + if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT) + snd_seq_queue_remove_cells(client->number, &info); + + return 0; +} + + +/* + * get subscription info + */ +static int snd_seq_ioctl_get_subscription(client_t *client, unsigned long arg) +{ + int result; + client_t *sender = NULL; + client_port_t *sport = NULL; + snd_seq_port_subscribe_t subs; + subscribers_t *p; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + result = -EINVAL; + if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + goto __end; + if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + goto __end; + p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest); + if (p) { + result = 0; + subs = p->info; + } else + result = -ENOENT; + + __end: + if (sport) + snd_seq_port_unlock(sport); + if (sender) + snd_seq_client_unlock(sender); + if (result >= 0) { + if (copy_to_user((void*)arg, &subs, sizeof(subs))) + return -EFAULT; + } + return result; +} + + +/* + * get subscription info - check only its presence + */ +static int snd_seq_ioctl_query_subs(client_t *client, unsigned long arg) +{ + int result = -ENXIO; + client_t *cptr = NULL; + client_port_t *port = NULL; + snd_seq_query_subs_t subs; + port_subs_info_t *group; + struct list_head *p; + int i; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL) + goto __end; + if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL) + goto __end; + + switch (subs.type) { + case SNDRV_SEQ_QUERY_SUBS_READ: + group = &port->c_src; + break; + case SNDRV_SEQ_QUERY_SUBS_WRITE: + group = &port->c_dest; + break; + default: + goto __end; + } + + down_read(&group->list_mutex); + /* search for the subscriber */ + subs.num_subs = group->count; + i = 0; + result = -ENOENT; + list_for_each(p, &group->list_head) { + if (i++ == subs.index) { + /* found! */ + subscribers_t *s; + if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) { + s = list_entry(p, subscribers_t, src_list); + subs.addr = s->info.dest; + } else { + s = list_entry(p, subscribers_t, dest_list); + subs.addr = s->info.sender; + } + subs.flags = s->info.flags; + subs.queue = s->info.queue; + result = 0; + break; + } + } + up_read(&group->list_mutex); + + __end: + if (port) + snd_seq_port_unlock(port); + if (cptr) + snd_seq_client_unlock(cptr); + if (result >= 0) { + if (copy_to_user((void*)arg, &subs, sizeof(subs))) + return -EFAULT; + } + return result; +} + + +/* + * query next client + */ +static int snd_seq_ioctl_query_next_client(client_t *client, unsigned long arg) +{ + client_t *cptr = NULL; + snd_seq_client_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* search for next client */ + info.client++; + if (info.client < 0) + info.client = 0; + for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) { + cptr = snd_seq_client_use_ptr(info.client); + if (cptr) + break; /* found */ + } + if (cptr == NULL) + return -ENOENT; + + get_client_info(cptr, &info); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +/* + * query next port + */ +static int snd_seq_ioctl_query_next_port(client_t *client, unsigned long arg) +{ + client_t *cptr; + client_port_t *port = NULL; + snd_seq_port_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + cptr = snd_seq_client_use_ptr(info.addr.client); + if (cptr == NULL) + return -ENXIO; + + /* search for next port */ + info.addr.port++; + port = snd_seq_port_query_nearest(cptr, &info); + if (port == NULL) { + snd_seq_client_unlock(cptr); + return -ENOENT; + } + + /* get port info */ + info.addr = port->addr; + snd_seq_get_port_info(port, &info); + snd_seq_port_unlock(port); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +/* -------------------------------------------------------- */ + +static struct seq_ioctl_table { + unsigned int cmd; + int (*func)(client_t *client, unsigned long arg); +} ioctl_tables[] = { + { SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info }, + { SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info }, + { SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, snd_seq_ioctl_set_client_info }, + { SNDRV_SEQ_IOCTL_CREATE_PORT, snd_seq_ioctl_create_port }, + { SNDRV_SEQ_IOCTL_DELETE_PORT, snd_seq_ioctl_delete_port }, + { SNDRV_SEQ_IOCTL_GET_PORT_INFO, snd_seq_ioctl_get_port_info }, + { SNDRV_SEQ_IOCTL_SET_PORT_INFO, snd_seq_ioctl_set_port_info }, + { SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, snd_seq_ioctl_subscribe_port }, + { SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, snd_seq_ioctl_unsubscribe_port }, + { SNDRV_SEQ_IOCTL_CREATE_QUEUE, snd_seq_ioctl_create_queue }, + { SNDRV_SEQ_IOCTL_DELETE_QUEUE, snd_seq_ioctl_delete_queue }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, snd_seq_ioctl_get_queue_info }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, snd_seq_ioctl_set_queue_info }, + { SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, snd_seq_ioctl_get_named_queue }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, snd_seq_ioctl_get_queue_status }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, snd_seq_ioctl_get_queue_tempo }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, snd_seq_ioctl_set_queue_tempo }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, snd_seq_ioctl_get_queue_timer }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, snd_seq_ioctl_set_queue_timer }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, snd_seq_ioctl_get_queue_client }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, snd_seq_ioctl_set_queue_client }, + { SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, snd_seq_ioctl_get_client_pool }, + { SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, snd_seq_ioctl_set_client_pool }, + { SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, snd_seq_ioctl_get_subscription }, + { SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, snd_seq_ioctl_query_next_client }, + { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, snd_seq_ioctl_query_next_port }, + { SNDRV_SEQ_IOCTL_REMOVE_EVENTS, snd_seq_ioctl_remove_events }, + { SNDRV_SEQ_IOCTL_QUERY_SUBS, snd_seq_ioctl_query_subs }, + { 0, NULL }, +}; + +static int snd_seq_do_ioctl(client_t *client, unsigned int cmd, unsigned long arg) +{ + struct seq_ioctl_table *p; + + switch (cmd) { + case SNDRV_SEQ_IOCTL_PVERSION: + /* return sequencer version number */ + return put_user(SNDRV_SEQ_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_SEQ_IOCTL_CLIENT_ID: + /* return the id of this client */ + return put_user(client->number, (int *)arg) ? -EFAULT : 0; + } + + if (! arg) + return -EFAULT; + for (p = ioctl_tables; p->cmd; p++) { + if (p->cmd == cmd) + return p->func(client, arg); + } + snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n", + cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); + return -ENOTTY; +} + + +static int snd_seq_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + client_t *client = (client_t *) file->private_data; + + snd_assert(client != NULL, return -ENXIO); + + return snd_seq_do_ioctl(client, cmd, arg); +} + + +/* -------------------------------------------------------- */ + + +/* exported to kernel modules */ +int snd_seq_create_kernel_client(snd_card_t *card, int client_index, snd_seq_client_callback_t * callback) +{ + client_t *client; + + snd_assert(! in_interrupt(), return -EBUSY); + + if (callback == NULL) + return -EINVAL; + if (card && client_index > 7) + return -EINVAL; + if (card == NULL && client_index > 63) + return -EINVAL; + if (card) + client_index += 64 + (card->number << 3); + + if (down_interruptible(®ister_mutex)) + return -ERESTARTSYS; + /* empty write queue as default */ + client = seq_create_client1(client_index, 0); + if (client == NULL) { + up(®ister_mutex); + return -EBUSY; /* failure code */ + } + usage_alloc(&client_usage, 1); + + client->accept_input = callback->allow_output; + client->accept_output = callback->allow_input; + + /* fill client data */ + client->data.kernel.card = card; + client->data.kernel.private_data = callback->private_data; + sprintf(client->name, "Client-%d", client->number); + + client->type = KERNEL_CLIENT; + up(®ister_mutex); + + /* make others aware this new client */ + snd_seq_system_client_ev_client_start(client->number); + + /* return client number to caller */ + return client->number; +} + +/* exported to kernel modules */ +int snd_seq_delete_kernel_client(int client) +{ + client_t *ptr; + + snd_assert(! in_interrupt(), return -EBUSY); + + ptr = clientptr(client); + if (ptr == NULL) + return -EINVAL; + + seq_free_client(ptr); + kfree(ptr); + return 0; +} + + +/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue + * and snd_seq_kernel_client_enqueue_blocking + */ +static int kernel_client_enqueue(int client, snd_seq_event_t *ev, + struct file *file, int blocking, + int atomic, int hop) +{ + client_t *cptr; + int result; + + snd_assert(ev != NULL, return -EINVAL); + + if (ev->type == SNDRV_SEQ_EVENT_NONE) + return 0; /* ignore this */ + if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR || + ev->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) + return -EINVAL; /* quoted events can't be enqueued */ + + /* fill in client number */ + ev->source.client = client; + + if (check_event_type_and_length(ev)) + return -EINVAL; + + cptr = snd_seq_client_use_ptr(client); + if (cptr == NULL) + return -EINVAL; + + if (! cptr->accept_output) + result = -EPERM; + else /* send it */ + result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop); + + snd_seq_client_unlock(cptr); + return result; +} + +/* + * exported, called by kernel clients to enqueue events (w/o blocking) + * + * RETURN VALUE: zero if succeed, negative if error + */ +int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t * ev, + int atomic, int hop) +{ + return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop); +} + +/* + * exported, called by kernel clients to enqueue events (with blocking) + * + * RETURN VALUE: zero if succeed, negative if error + */ +int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, + struct file *file, + int atomic, int hop) +{ + return kernel_client_enqueue(client, ev, file, 1, atomic, hop); +} + + +/* + * exported, called by kernel clients to dispatch events directly to other + * clients, bypassing the queues. Event time-stamp will be updated. + * + * RETURN VALUE: negative = delivery failed, + * zero, or positive: the number of delivered events + */ +int snd_seq_kernel_client_dispatch(int client, snd_seq_event_t * ev, + int atomic, int hop) +{ + client_t *cptr; + int result; + + snd_assert(ev != NULL, return -EINVAL); + + /* fill in client number */ + ev->queue = SNDRV_SEQ_QUEUE_DIRECT; + ev->source.client = client; + + if (check_event_type_and_length(ev)) + return -EINVAL; + + cptr = snd_seq_client_use_ptr(client); + if (cptr == NULL) + return -EINVAL; + + if (!cptr->accept_output) + result = -EPERM; + else + result = snd_seq_deliver_event(cptr, ev, atomic, hop); + + snd_seq_client_unlock(cptr); + return result; +} + + +/* + * exported, called by kernel clients to perform same functions as with + * userland ioctl() + */ +int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) +{ + client_t *client; + mm_segment_t fs; + int result; + + client = clientptr(clientid); + if (client == NULL) + return -ENXIO; + fs = snd_enter_user(); + result = snd_seq_do_ioctl(client, cmd, (unsigned long)arg); + snd_leave_user(fs); + return result; +} + + +/* exported (for OSS emulator) */ +int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait) +{ + client_t *client; + + client = clientptr(clientid); + if (client == NULL) + return -ENXIO; + + if (! snd_seq_write_pool_allocated(client)) + return 1; + if (snd_seq_pool_poll_wait(client->pool, file, wait)) + return 1; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +/* + * /proc interface + */ +static void snd_seq_info_dump_subscribers(snd_info_buffer_t *buffer, port_subs_info_t *group, int is_src, char *msg) +{ + struct list_head *p; + subscribers_t *s; + int count = 0; + + down_read(&group->list_mutex); + if (list_empty(&group->list_head)) { + up_read(&group->list_mutex); + return; + } + snd_iprintf(buffer, msg); + list_for_each(p, &group->list_head) { + if (is_src) + s = list_entry(p, subscribers_t, src_list); + else + s = list_entry(p, subscribers_t, dest_list); + if (count++) + snd_iprintf(buffer, ", "); + snd_iprintf(buffer, "%d:%d", + is_src ? s->info.dest.client : s->info.sender.client, + is_src ? s->info.dest.port : s->info.sender.port); + if (s->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) + snd_iprintf(buffer, "[%c:%d]", ((s->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) ? 'r' : 't'), s->info.queue); + if (group->exclusive) + snd_iprintf(buffer, "[ex]"); + } + up_read(&group->list_mutex); + snd_iprintf(buffer, "\n"); +} + +#define FLAG_PERM_RD(perm) ((perm) & SNDRV_SEQ_PORT_CAP_READ ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_READ ? 'R' : 'r') : '-') +#define FLAG_PERM_WR(perm) ((perm) & SNDRV_SEQ_PORT_CAP_WRITE ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_WRITE ? 'W' : 'w') : '-') +#define FLAG_PERM_EX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_NO_EXPORT ? '-' : 'e') + +#define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-') + +static void snd_seq_info_dump_ports(snd_info_buffer_t *buffer, client_t *client) +{ + struct list_head *l; + + down(&client->ports_mutex); + list_for_each(l, &client->ports_list_head) { + client_port_t *p = list_entry(l, client_port_t, list); + snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c)\n", + p->addr.port, p->name, + FLAG_PERM_RD(p->capability), + FLAG_PERM_WR(p->capability), + FLAG_PERM_EX(p->capability), + FLAG_PERM_DUPLEX(p->capability)); + snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, " Connecting To: "); + snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, " Connected From: "); + } + up(&client->ports_mutex); +} + + +/* exported to seq_info.c */ +void snd_seq_info_clients_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + extern void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t * pool, char *space); + int c; + client_t *client; + + snd_iprintf(buffer, "Client info\n"); + snd_iprintf(buffer, " cur clients : %d\n", client_usage.cur); + snd_iprintf(buffer, " peak clients : %d\n", client_usage.peak); + snd_iprintf(buffer, " max clients : %d\n", SNDRV_SEQ_MAX_CLIENTS); + snd_iprintf(buffer, "\n"); + + /* list the client table */ + for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) { + client = snd_seq_client_use_ptr(c); + if (client == NULL) + continue; + if (client->type == NO_CLIENT) + continue; + + snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n", + c, client->name, + client->type == USER_CLIENT ? "User" : "Kernel"); + snd_seq_info_dump_ports(buffer, client); + if (snd_seq_write_pool_allocated(client)) { + snd_iprintf(buffer, " Output pool :\n"); + snd_seq_info_pool(buffer, client->pool, " "); + } + if (client->type == USER_CLIENT && client->data.user.fifo && + client->data.user.fifo->pool) { + snd_iprintf(buffer, " Input pool :\n"); + snd_seq_info_pool(buffer, client->data.user.fifo->pool, " "); + } + snd_seq_client_unlock(client); + } +} + + +/*---------------------------------------------------------------------------*/ + + +/* + * REGISTRATION PART + */ + +static struct file_operations snd_seq_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_seq_read, + write: snd_seq_write, + open: snd_seq_open, + release: snd_seq_release, + poll: snd_seq_poll, + ioctl: snd_seq_ioctl, +}; + +static snd_minor_t snd_seq_reg = +{ + comment: "sequencer", + f_ops: &snd_seq_f_ops, +}; + + +/* + * register sequencer device + */ +int __init snd_sequencer_device_init(void) +{ + int err; + + if (down_interruptible(®ister_mutex)) + return -ERESTARTSYS; + + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0, &snd_seq_reg, "seq")) < 0) { + up(®ister_mutex); + return err; + } + + up(®ister_mutex); + + return 0; +} + + + +/* + * unregister sequencer device + */ +void __exit snd_sequencer_device_done(void) +{ + snd_unregister_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0); +} diff -Nru a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_clientmgr.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,104 @@ +/* + * ALSA sequencer Client Manager + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_CLIENTMGR_H +#define __SND_SEQ_CLIENTMGR_H + +#include +#include "seq_fifo.h" +#include "seq_ports.h" +#include "seq_lock.h" + + +/* client manager */ + +struct _snd_seq_user_client { + struct file *file; /* file struct of client */ + /* ... */ + + /* fifo */ + fifo_t *fifo; /* queue for incoming events */ + int fifo_pool_size; +}; + +struct _snd_seq_kernel_client { + snd_card_t *card; + /* pointer to client functions */ + void *private_data; /* private data for client */ + /* ... */ +}; + + +struct _snd_seq_client { + snd_seq_client_type_t type; + unsigned int accept_input: 1, + accept_output: 1; + char name[64]; /* client name */ + int number; /* client number */ + unsigned int filter; /* filter flags */ + unsigned char client_filter[32]; + unsigned char event_filter[32]; + snd_use_lock_t use_lock; + int event_lost; + /* ports */ + int num_ports; /* number of ports */ + struct list_head ports_list_head; + rwlock_t ports_lock; + struct semaphore ports_mutex; + + /* output pool */ + pool_t *pool; /* memory pool for this client */ + + union { + user_client_t user; + kernel_client_t kernel; + } data; +}; + +/* usage statistics */ +typedef struct { + int cur; + int peak; +} usage_t; + + +extern int client_init_data(void); +extern int snd_sequencer_device_init(void); +extern void snd_sequencer_device_done(void); + +/* get locked pointer to client */ +extern client_t *snd_seq_client_use_ptr(int clientid); + +/* unlock pointer to client */ +#define snd_seq_client_unlock(client) snd_use_lock_free(&(client)->use_lock) + +/* dispatch event to client(s) */ +extern int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop); + +/* exported to other modules */ +extern int snd_seq_register_kernel_client(snd_seq_client_callback_t *callback, void *private_data); +extern int snd_seq_unregister_kernel_client(int client); +extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop); +int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, struct file *file, int atomic, int hop); +int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait); +int snd_seq_client_notify_subscription(int client, int port, snd_seq_port_subscribe_t *info, int evtype); +int snd_seq_deliver_event(client_t *client, snd_seq_event_t *event, int atomic, int hop); + +#endif diff -Nru a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_device.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,529 @@ +/* + * ALSA sequencer device management + * Copyright (c) 1999 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + *---------------------------------------------------------------- + * + * This device handler seperates the card driver module from sequencer + * stuff (sequencer core, synth drivers, etc), so that user can avoid + * to spend unnecessary resources e.g. if he needs only listening to + * MP3s. + * + * The card (or lowlevel) driver creates a sequencer device entry + * via snd_seq_device_new(). This is an entry pointer to communicate + * with the sequencer device "driver", which is involved with the + * actual part to communicate with the sequencer core. + * Each sequencer device entry has an id string and the corresponding + * driver with the same id is loaded when required. For example, + * lowlevel codes to access emu8000 chip on sbawe card are included in + * emu8000-synth module. To activate this module, the hardware + * resources like i/o port are passed via snd_seq_device argument. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("ALSA sequencer device management"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +/* + * driver list + */ +typedef struct ops_list ops_list_t; + +/* driver state */ +#define DRIVER_EMPTY 0 +#define DRIVER_LOADED (1<<0) +#define DRIVER_REQUESTED (1<<1) +#define DRIVER_LOCKED (1<<2) + +struct ops_list { + char id[ID_LEN]; /* driver id */ + int driver; /* driver state */ + int used; /* reference counter */ + int argsize; /* argument size */ + + /* operators */ + snd_seq_dev_ops_t ops; + + /* registred devices */ + struct list_head dev_list; /* list of devices */ + int num_devices; /* number of associated devices */ + int num_init_devices; /* number of initialized devices */ + struct semaphore reg_mutex; + + struct list_head list; /* next driver */ +}; + + +static LIST_HEAD(opslist); +static int num_ops = 0; +static DECLARE_MUTEX(ops_mutex); +static snd_info_entry_t *info_entry = NULL; + +/* + * prototypes + */ +static int snd_seq_device_free(snd_seq_device_t *dev); +static int snd_seq_device_dev_free(snd_device_t *device); +static int snd_seq_device_dev_register(snd_device_t *device); +static int snd_seq_device_dev_unregister(snd_device_t *device); + +static int init_device(snd_seq_device_t *dev, ops_list_t *ops); +static int free_device(snd_seq_device_t *dev, ops_list_t *ops); +static ops_list_t *find_driver(char *id, int create_if_empty); +static ops_list_t *create_driver(char *id); +static void unlock_driver(ops_list_t *ops); +static void remove_drivers(void); + +/* + * show all drivers and their status + */ + +static void snd_seq_device_info(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + struct list_head *head; + + down(&ops_mutex); + list_for_each(head, &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", + ops->id, + ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), + ops->driver & DRIVER_REQUESTED ? ",requested" : "", + ops->driver & DRIVER_LOCKED ? ",locked" : "", + ops->num_devices); + } + up(&ops_mutex); +} + +/* + * load all registered drivers (called from seq_clientmgr.c) + */ + +void snd_seq_device_load_drivers(void) +{ +#ifdef CONFIG_KMOD + struct list_head *head; + char modname[64]; + + down(&ops_mutex); + list_for_each(head, &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + if (! (ops->driver & DRIVER_LOADED) && + ! (ops->driver & DRIVER_REQUESTED)) { + ops->used++; + up(&ops_mutex); + ops->driver |= DRIVER_REQUESTED; + sprintf(modname, "snd-%s", ops->id); + request_module(modname); + down(&ops_mutex); + ops->used--; + } + } + up(&ops_mutex); +#endif +} + +/* + * register a sequencer device + * card = card info (NULL allowed) + * device = device number (if any) + * id = id of driver + * result = return pointer (NULL allowed if unnecessary) + */ +int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize, + snd_seq_device_t **result) +{ + snd_seq_device_t *dev; + ops_list_t *ops; + int err; + static snd_device_ops_t dops = { + dev_free: snd_seq_device_dev_free, + dev_register: snd_seq_device_dev_register, + dev_unregister: snd_seq_device_dev_unregister + }; + + if (result) + *result = NULL; + + snd_assert(id != NULL, return -EINVAL); + + ops = find_driver(id, 1); + if (ops == NULL) + return -ENOMEM; + + dev = snd_magic_kcalloc(snd_seq_device_t, sizeof(*dev) + argsize, GFP_KERNEL); + if (dev == NULL) { + unlock_driver(ops); + return -ENOMEM; + } + + /* set up device info */ + dev->card = card; + dev->device = device; + strncpy(dev->id, id, sizeof(dev->id) - 1); + dev->id[sizeof(dev->id) - 1] = 0; + dev->argsize = argsize; + dev->status = SNDRV_SEQ_DEVICE_FREE; + + /* add this device to the list */ + down(&ops->reg_mutex); + list_add_tail(&dev->list, &ops->dev_list); + ops->num_devices++; + up(&ops->reg_mutex); + + unlock_driver(ops); + + if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { + snd_seq_device_free(dev); + return err; + } + + if (result) + *result = dev; + + return 0; +} + +/* + * free the existing device + */ +static int snd_seq_device_free(snd_seq_device_t *dev) +{ + ops_list_t *ops; + + snd_assert(dev != NULL, return -EINVAL); + + ops = find_driver(dev->id, 0); + if (ops == NULL) + return -ENXIO; + + /* remove the device from the list */ + down(&ops->reg_mutex); + list_del(&dev->list); + ops->num_devices--; + up(&ops->reg_mutex); + + free_device(dev, ops); + if (dev->private_free) + dev->private_free(dev); + snd_magic_kfree(dev); + + unlock_driver(ops); + + return 0; +} + +static int snd_seq_device_dev_free(snd_device_t *device) +{ + snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + return snd_seq_device_free(dev); +} + +/* + * register the device + */ +static int snd_seq_device_dev_register(snd_device_t *device) +{ + snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + ops_list_t *ops; + + ops = find_driver(dev->id, 0); + if (ops == NULL) + return -ENOENT; + + /* initialize this device if the corresponding driver was + * already loaded + */ + if (ops->driver & DRIVER_LOADED) + init_device(dev, ops); + + unlock_driver(ops); + return 0; +} + +/* + * unregister the existing device + */ +static int snd_seq_device_dev_unregister(snd_device_t *device) +{ + snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + return snd_seq_device_free(dev); +} + +/* + * register device driver + * id = driver id + * entry = driver operators - duplicated to each instance + */ +int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize) +{ + struct list_head *head; + ops_list_t *ops; + + if (id == NULL || entry == NULL || + entry->init_device == NULL || entry->free_device == NULL) + return -EINVAL; + + ops = find_driver(id, 1); + if (ops == NULL) + return -ENOMEM; + if (ops->driver & DRIVER_LOADED) { + snd_printk("driver_register: driver '%s' already exists\n", id); + unlock_driver(ops); + return -EBUSY; + } + + down(&ops->reg_mutex); + /* copy driver operators */ + ops->ops = *entry; + ops->driver |= DRIVER_LOADED; + ops->argsize = argsize; + + /* initialize existing devices if necessary */ + list_for_each(head, &ops->dev_list) { + snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list); + init_device(dev, ops); + } + up(&ops->reg_mutex); + + unlock_driver(ops); + + return 0; +} + + +/* + * create driver record + */ +static ops_list_t * create_driver(char *id) +{ + ops_list_t *ops; + + ops = kmalloc(sizeof(*ops), GFP_KERNEL); + if (ops == NULL) + return ops; + memset(ops, 0, sizeof(*ops)); + + /* set up driver entry */ + strncpy(ops->id, id, sizeof(ops->id) - 1); + ops->id[sizeof(ops->id) - 1] = 0; + init_MUTEX(&ops->reg_mutex); + ops->driver = DRIVER_EMPTY; + INIT_LIST_HEAD(&ops->dev_list); + /* lock this instance */ + ops->used = 1; + + /* register driver entry */ + down(&ops_mutex); + list_add_tail(&ops->list, &opslist); + num_ops++; + up(&ops_mutex); + + return ops; +} + + +/* + * unregister the specified driver + */ +int snd_seq_device_unregister_driver(char *id) +{ + struct list_head *head; + ops_list_t *ops; + + ops = find_driver(id, 0); + if (ops == NULL) + return -ENXIO; + if (! (ops->driver & DRIVER_LOADED) || + (ops->driver & DRIVER_LOCKED)) { + snd_printk("driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver); + unlock_driver(ops); + return -EBUSY; + } + + /* close and release all devices associated with this driver */ + down(&ops->reg_mutex); + ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ + list_for_each(head, &ops->dev_list) { + snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list); + free_device(dev, ops); + } + + ops->driver = 0; + if (ops->num_init_devices > 0) + snd_printk("free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices); + up(&ops->reg_mutex); + + unlock_driver(ops); + + /* remove empty driver entries */ + remove_drivers(); + + return 0; +} + + +/* + * remove empty driver entries + */ +static void remove_drivers(void) +{ + struct list_head *head; + + down(&ops_mutex); + head = opslist.next; + while (head != &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + if (! (ops->driver & DRIVER_LOADED) && + ops->used == 0 && ops->num_devices == 0) { + head = head->next; + list_del(&ops->list); + kfree(ops); + num_ops--; + } else + head = head->next; + } + up(&ops_mutex); +} + +/* + * initialize the device - call init_device operator + */ +static int init_device(snd_seq_device_t *dev, ops_list_t *ops) +{ + if (! (ops->driver & DRIVER_LOADED)) + return 0; /* driver is not loaded yet */ + if (dev->status != SNDRV_SEQ_DEVICE_FREE) + return 0; /* already initialized */ + if (ops->argsize != dev->argsize) { + snd_printk("incompatible device '%s' for plug-in '%s'\n", dev->name, ops->id); +printk(" %d %d\n", ops->argsize, dev->argsize); + return -EINVAL; + } + if (ops->ops.init_device(dev) >= 0) { + dev->status = SNDRV_SEQ_DEVICE_REGISTERED; + ops->num_init_devices++; + } else { + snd_printk("init_device failed: %s: %s\n", dev->name, dev->id); + } + + return 0; +} + +/* + * release the device - call free_device operator + */ +static int free_device(snd_seq_device_t *dev, ops_list_t *ops) +{ + int result; + + if (! (ops->driver & DRIVER_LOADED)) + return 0; /* driver is not loaded yet */ + if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) + return 0; /* not registered */ + if (ops->argsize != dev->argsize) { + snd_printk("incompatible device '%s' for plug-in '%s'\n", dev->name, ops->id); + return -EINVAL; + } + if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { + dev->status = SNDRV_SEQ_DEVICE_FREE; + dev->driver_data = NULL; + ops->num_init_devices--; + } else { + snd_printk("free_device failed: %s: %s\n", dev->name, dev->id); + } + + return 0; +} + +/* + * find the matching driver with given id + */ +static ops_list_t * find_driver(char *id, int create_if_empty) +{ + struct list_head *head; + + down(&ops_mutex); + list_for_each(head, &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + if (strcmp(ops->id, id) == 0) { + ops->used++; + up(&ops_mutex); + return ops; + } + } + up(&ops_mutex); + if (create_if_empty) + return create_driver(id); + return NULL; +} + +static void unlock_driver(ops_list_t *ops) +{ + down(&ops_mutex); + ops->used--; + up(&ops_mutex); +} + + +/* + * module part + */ + +static int __init alsa_seq_device_init(void) +{ + info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", snd_seq_root); + if (info_entry == NULL) + return -ENOMEM; + info_entry->content = SNDRV_INFO_CONTENT_TEXT; + info_entry->c.text.read_size = 2048; + info_entry->c.text.read = snd_seq_device_info; + if (snd_info_register(info_entry) < 0) { + snd_info_free_entry(info_entry); + return -ENOMEM; + } + return 0; +} + +static void __exit alsa_seq_device_exit(void) +{ + remove_drivers(); + snd_info_unregister(info_entry); + if (num_ops) + snd_printk("drivers not released (%d)\n", num_ops); +} + +module_init(alsa_seq_device_init) +module_exit(alsa_seq_device_exit) + +EXPORT_SYMBOL(snd_seq_device_load_drivers); +EXPORT_SYMBOL(snd_seq_device_new); +EXPORT_SYMBOL(snd_seq_device_register_driver); +EXPORT_SYMBOL(snd_seq_device_unregister_driver); diff -Nru a/sound/core/seq/seq_dtl.c b/sound/core/seq/seq_dtl.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_dtl.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,193 @@ +/* + * DTL(e) event converter + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include "seq_queue.h" + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + +typedef struct dtl_out { + int out_mtp_network; + unsigned int time_format; + unsigned int full_frame_count; + unsigned char sysex[11]; +} dtl_out_t; + +typedef struct dtl_in { + unsigned int time_format; + unsigned int cur_pos; +} dtl_in_t; + + +static int dtl_open_out(queue_sync_t *sync_info, seq_sync_arg_t *retp) +{ + dtl_out_t *arg; + + if (sync_info->time_format >= 4) + return -EINVAL; + if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL) + return -ENOMEM; + arg->out_mtp_network = sync_info->opt_info[0]; + arg->full_frame_count = sync_info->opt_info[1]; + arg->time_format = sync_info->time_format; + sync_info->param.time.subframes = 1; /* MTC uses quarter frame */ + sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format); + *retp = arg; + return 0; +} + +static int dtl_open_in(queue_sync_t *sync_info, seq_sync_arg_t *retp) +{ + dtl_in_t *arg; + + if (sync_info->time_format >= 4) + return -EINVAL; + if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL) + return -ENOMEM; + arg->time_format = sync_info->time_format; + arg->cur_pos = 0; + sync_info->param.time.subframes = 1; /* MTC uses quarter frame */ + sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format); + *retp = arg; + return 0; +} + + +/* decode sync position */ +static int sync_pos_out(dtl_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + sndrv_seq_time_frame_t cur_out; + unsigned char *buf = arg->sysex; + + if (arg->time_format != src->data.queue.sync_time_format) + return -EINVAL; + + cur_out = snd_seq_position_to_time_frame(arg->time_format, 1, src->data.queue.param.position); + buf[0] = 0xf0; /* SYSEX */ + buf[1] = 0x00; /* MOTU */ + buf[2] = 0x33; /* MOTU */ + buf[3] = 0x7f; + buf[4] = 0x0c; /* DTL full frame */ + buf[5] = arg->out_mtp_network; /* 0x00 or 0x08 */ + buf[6] = cur_out.hour | (arg->time_format << 5); + buf[7] = cur_out.min; + buf[8] = cur_out.sec; + buf[9] = cur_out.frame; + buf[10] = 0xf7; + + ev->type = SNDRV_SEQ_EVENT_SYSEX; + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + ev->data.ext.len = 11; + ev->data.ext.ptr = buf; + + return 1; +} + +/* decode sync signal */ +static int sync_out(dtl_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + unsigned int pos; + + if (arg->time_format != src->data.queue.sync_time_format) + return -EINVAL; + pos = src->data.queue.param.position; + if (arg->full_frame_count && + (pos % arg->full_frame_count) == 0) + /* send full frame */ + return sync_pos_out(arg, src, ev); + ev->type = SNDRV_SEQ_EVENT_CLOCK; + return 1; +} + +static int dtl_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + dtl_out_t *arg = _arg; + switch (src->type) { + case SNDRV_SEQ_EVENT_SYNC: + return sync_out(arg, src, ev); + case SNDRV_SEQ_EVENT_SYNC_POS: + return sync_pos_out(arg, src, ev); + } + return 0; +} + +/* decode sync position */ +static int sync_pos_in(dtl_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + unsigned time_format; + static unsigned char id[] = { + 0xf0, 0x00, 0x33, 0x7f, 0x0c, + }; + sndrv_seq_time_frame_t cur_in; + char buf[11]; + + if (snd_seq_expand_var_event(src, 11, buf, 1, 0) != 11) + return 0; + if (memcmp(buf, id, sizeof(id)) != 0) + return 0; + time_format = (buf[6] >> 5) & 3; + if (time_format != arg->time_format) + return -EINVAL; + cur_in.hour = buf[6] & 0x1f; + cur_in.min = buf[7]; + cur_in.sec = buf[8]; + cur_in.frame = buf[9]; + arg->cur_pos = snd_seq_time_frame_to_position(time_format, 1, &cur_in); + + ev->type = SNDRV_SEQ_EVENT_SYNC_POS; + ev->data.queue.sync_time_format = time_format; + ev->data.queue.param.position = arg->cur_pos; + + return 1; +} + +static int dtl_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + dtl_in_t *arg = _arg; + switch (src->type) { + case SNDRV_SEQ_EVENT_CLOCK: + arg->cur_pos++; + ev->type = SNDRV_SEQ_EVENT_SYNC; + ev->data.queue.param.position = arg->cur_pos; + return 1; + case SNDRV_SEQ_EVENT_SYSEX: + return sync_pos_in(arg, src, ev); + } + return 0; +} + +/* exported */ +seq_sync_parser_t snd_seq_dtl_parser = { + format: SNDRV_SEQ_SYNC_FMT_DTL, + in: { + open: dtl_open_in, + sync: dtl_sync_in, + }, + out: { + open: dtl_open_out, + sync: dtl_sync_out, + }, +}; + +#endif /* SNDRV_SEQ_SYNC_SUPPORT */ diff -Nru a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_dummy.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,274 @@ +/* + * ALSA sequencer MIDI-through client + * Copyright (c) 1999-2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "seq_clientmgr.h" +#include +#include + +/* + + Sequencer MIDI-through client + + This gives a simple midi-through client. All the normal input events + are redirected to output port immediately. + The routing can be done via aconnect program in alsa-utils. + + Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY). + If you want to auto-load this module, you may add the following alias + in your /etc/conf.modules file. + + alias snd-seq-client-62 snd-seq-dummy + + The module is loaded on demand for client 62, or /proc/asound/seq/ + is accessed. If you don't need this module to be loaded, alias + snd-seq-client-62 as "off". This will help modprobe. + + The number of ports to be created can be specified via the module + paramter "ports". For example, to create four ports, add the + following option in /etc/modules.conf: + + option snd-seq-dummy ports=4 + + The modle option "duplex=1" enables duplex operation to the port. + In duplex mode, a pair of ports are created instead of single port, + and events are tunneled between pair-ports. For example, input to + port A is sent to output port of another port B and vice versa. + In duplex mode, each port has DUPLEX capability. + + */ + + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("ALSA sequencer MIDI-through client"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); +MODULE_PARM(ports, "i"); +MODULE_PARM_DESC(ports, "number of ports to be created"); +MODULE_PARM(duplex, "i"); +MODULE_PARM_DESC(duplex, "create DUPLEX ports"); +int ports = 1; +int duplex = 0; + +typedef struct snd_seq_dummy_port { + int client; + int port; + int duplex; + int connect; +} snd_seq_dummy_port_t; + +static int my_client = -1; + +/* + * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events + * to subscribers. + * Note: this callback is called only after all subscribers are removed. + */ +static int +dummy_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_seq_dummy_port_t *p; + int i; + snd_seq_event_t ev; + + p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return -EINVAL); + memset(&ev, 0, sizeof(ev)); + if (p->duplex) + ev.source.port = p->connect; + else + ev.source.port = p->port; + ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + ev.type = SNDRV_SEQ_EVENT_CONTROLLER; + for (i = 0; i < 16; i++) { + ev.data.control.channel = i; + ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF; + snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0); + ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS; + snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0); + } + return 0; +} + +/* + * event input callback - just redirect events to subscribers + */ +static int +dummy_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop) +{ + snd_seq_dummy_port_t *p; + snd_seq_event_t tmpev; + + p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return -EINVAL); + if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM || + ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR) + return 0; /* ignore system messages */ + /* save the original sender */ + tmpev.type = SNDRV_SEQ_EVENT_KERNEL_QUOTE; + tmpev.flags = (ev->flags & ~SNDRV_SEQ_EVENT_LENGTH_MASK) + | SNDRV_SEQ_EVENT_LENGTH_FIXED; + tmpev.tag = ev->tag; + tmpev.time = ev->time; + tmpev.data.quote.origin = ev->source; + tmpev.data.quote.event = ev; + if (p->duplex) + tmpev.source.port = p->connect; + else + tmpev.source.port = p->port; + tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop); +} + +/* + * free_private callback + */ +static void +dummy_free(void *private_data) +{ + snd_seq_dummy_port_t *p; + + p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return); + snd_magic_kfree(p); +} + +/* + * create a port + */ +static snd_seq_dummy_port_t __init * +create_port(int idx, int type) +{ + snd_seq_port_info_t pinfo; + snd_seq_port_callback_t pcb; + snd_seq_dummy_port_t *rec; + + if ((rec = snd_magic_kcalloc(snd_seq_dummy_port_t, 0, GFP_KERNEL)) == NULL) + return NULL; + + rec->client = my_client; + rec->duplex = duplex; + rec->connect = 0; + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.addr.client = my_client; + if (duplex) + sprintf(pinfo.name, "Midi Through Port-%d:%c", idx, + (type ? 'B' : 'A')); + else + sprintf(pinfo.name, "Midi Through Port-%d", idx); + pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; + pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + if (duplex) + pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; + pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + memset(&pcb, 0, sizeof(pcb)); + pcb.owner = THIS_MODULE; + pcb.unuse = dummy_unuse; + pcb.event_input = dummy_input; + pcb.private_free = dummy_free; + pcb.private_data = rec; + pinfo.kernel = &pcb; + if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) { + snd_magic_kfree(rec); + return NULL; + } + rec->port = pinfo.addr.port; + return rec; +} + +/* + * register client and create ports + */ +static int __init +register_client(void) +{ + snd_seq_client_callback_t cb; + snd_seq_client_info_t cinfo; + snd_seq_dummy_port_t *rec1, *rec2; + int i; + + if (ports < 1) { + snd_printk("invalid number of ports %d\n", ports); + return -EINVAL; + } + + /* create client */ + memset(&cb, 0, sizeof(cb)); + cb.allow_input = 1; + cb.allow_output = 1; + my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY, &cb); + if (my_client < 0) + return my_client; + + /* set client name */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = my_client; + cinfo.type = KERNEL_CLIENT; + strcpy(cinfo.name, "Midi Through"); + snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + /* create ports */ + for (i = 0; i < ports; i++) { + rec1 = create_port(i, 0); + if (rec1 == NULL) { + snd_seq_delete_kernel_client(my_client); + return -ENOMEM; + } + if (duplex) { + rec2 = create_port(i, 1); + if (rec2 == NULL) { + snd_seq_delete_kernel_client(my_client); + return -ENOMEM; + } + rec1->connect = rec2->port; + rec2->connect = rec1->port; + } + } + + return 0; +} + +/* + * delete client if exists + */ +static void __exit +delete_client(void) +{ + if (my_client >= 0) + snd_seq_delete_kernel_client(my_client); +} + +/* + * Init part + */ + +static int __init alsa_seq_dummy_init(void) +{ + return register_client(); +} + +static void __exit alsa_seq_dummy_exit(void) +{ + delete_client(); +} + +module_init(alsa_seq_dummy_init) +module_exit(alsa_seq_dummy_exit) diff -Nru a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_fifo.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,259 @@ +/* + * ALSA sequencer FIFO + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include "seq_fifo.h" +#include "seq_lock.h" + + +/* FIFO */ + +/* create new fifo */ +fifo_t *snd_seq_fifo_new(int poolsize) +{ + fifo_t *f; + + f = snd_kcalloc(sizeof(fifo_t), GFP_KERNEL); + if (f == NULL) { + snd_printd("malloc failed for snd_seq_fifo_new() \n"); + return NULL; + } + + f->pool = snd_seq_pool_new(poolsize); + if (f->pool == NULL) { + kfree(f); + return NULL; + } + if (snd_seq_pool_init(f->pool) < 0) { + snd_seq_pool_delete(&f->pool); + kfree(f); + return NULL; + } + + spin_lock_init(&f->lock); + snd_use_lock_init(&f->use_lock); + init_waitqueue_head(&f->input_sleep); + atomic_set(&f->overflow, 0); + + f->head = NULL; + f->tail = NULL; + f->cells = 0; + + return f; +} + +void snd_seq_fifo_delete(fifo_t **fifo) +{ + fifo_t *f; + + snd_assert(fifo != NULL, return); + f = *fifo; + snd_assert(f != NULL, return); + *fifo = NULL; + + snd_seq_fifo_clear(f); + + /* wake up clients if any */ + if (waitqueue_active(&f->input_sleep)) + wake_up(&f->input_sleep); + + /* release resources...*/ + /*....................*/ + + if (f->pool) { + snd_seq_pool_done(f->pool); + snd_seq_pool_delete(&f->pool); + } + + kfree(f); +} + +static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f); + +/* clear queue */ +void snd_seq_fifo_clear(fifo_t *f) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + + /* clear overflow flag */ + atomic_set(&f->overflow, 0); + + snd_use_lock_sync(&f->use_lock); + spin_lock_irqsave(&f->lock, flags); + /* drain the fifo */ + while ((cell = fifo_cell_out(f)) != NULL) { + snd_seq_cell_free(cell); + } + spin_unlock_irqrestore(&f->lock, flags); +} + + +/* enqueue event to fifo */ +int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + int err; + + snd_assert(f != NULL, return -EINVAL); + + snd_use_lock_use(&f->use_lock); + err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */ + if (err < 0) { + if (err == -ENOMEM) + atomic_inc(&f->overflow); + snd_use_lock_free(&f->use_lock); + return err; + } + + /* append new cells to fifo */ + spin_lock_irqsave(&f->lock, flags); + if (f->tail != NULL) + f->tail->next = cell; + f->tail = cell; + if (f->head == NULL) + f->head = cell; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); + + /* wakeup client */ + if (waitqueue_active(&f->input_sleep)) + wake_up(&f->input_sleep); + + snd_use_lock_free(&f->use_lock); + + return 0; /* success */ + +} + +/* dequeue cell from fifo */ +static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f) +{ + snd_seq_event_cell_t *cell; + + if ((cell = f->head) != NULL) { + f->head = cell->next; + + /* reset tail if this was the last element */ + if (f->tail == cell) + f->tail = NULL; + + cell->next = NULL; + f->cells--; + } + + return cell; +} + +/* dequeue cell from fifo and copy on user space */ +int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + + snd_assert(f != NULL, return -EINVAL); + + *cellp = NULL; + spin_lock_irqsave(&f->lock, flags); + while ((cell = fifo_cell_out(f)) == NULL) { + if (nonblock) { + /* non-blocking - return immediately */ + spin_unlock_irqrestore(&f->lock, flags); + return -EAGAIN; + } + snd_seq_sleep_in_lock(&f->input_sleep, &f->lock); + + if (signal_pending(current)) { + spin_unlock_irqrestore(&f->lock, flags); + return -ERESTARTSYS; + } + } + *cellp = cell; + spin_unlock_irqrestore(&f->lock, flags); + + return 0; +} + + +void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell) +{ + unsigned long flags; + + if (cell) { + spin_lock_irqsave(&f->lock, flags); + cell->next = f->head; + f->head = cell; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); + } +} + + +/* polling; return non-zero if queue is available */ +int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait) +{ + poll_wait(file, &f->input_sleep, wait); + return (f->cells > 0); +} + +/* change the size of pool; all old events are removed */ +int snd_seq_fifo_resize(fifo_t *f, int poolsize) +{ + unsigned long flags; + pool_t *newpool, *oldpool; + snd_seq_event_cell_t *cell, *next, *oldhead; + + snd_assert(f != NULL && f->pool != NULL, return -EINVAL); + + /* allocate new pool */ + newpool = snd_seq_pool_new(poolsize); + if (newpool == NULL) + return -ENOMEM; + if (snd_seq_pool_init(newpool) < 0) { + snd_seq_pool_delete(&newpool); + return -ENOMEM; + } + + spin_lock_irqsave(&f->lock, flags); + /* remember old pool */ + oldpool = f->pool; + oldhead = f->head; + /* exchange pools */ + f->pool = newpool; + f->head = NULL; + f->tail = NULL; + f->cells = 0; + /* NOTE: overflow flag is not cleared */ + spin_unlock_irqrestore(&f->lock, flags); + + /* release cells in old pool */ + for (cell = oldhead; cell; cell = next) { + next = cell->next; + snd_seq_cell_free(cell); + } + snd_seq_pool_delete(&oldpool); + + return 0; +} diff -Nru a/sound/core/seq/seq_fifo.h b/sound/core/seq/seq_fifo.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_fifo.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,72 @@ +/* + * ALSA sequencer FIFO + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_FIFO_H +#define __SND_SEQ_FIFO_H + +#include "seq_memory.h" +#include "seq_lock.h" + + +/* === FIFO === */ + +typedef struct { + pool_t *pool; /* FIFO pool */ + snd_seq_event_cell_t* head; /* pointer to head of fifo */ + snd_seq_event_cell_t* tail; /* pointer to tail of fifo */ + int cells; + spinlock_t lock; + snd_use_lock_t use_lock; + wait_queue_head_t input_sleep; + atomic_t overflow; + +} fifo_t; + +/* create new fifo (constructor) */ +extern fifo_t *snd_seq_fifo_new(int poolsize); + +/* delete fifo (destructor) */ +extern void snd_seq_fifo_delete(fifo_t **f); + + +/* enqueue event to fifo */ +extern int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event); + +/* lock fifo from release */ +#define snd_seq_fifo_lock(fifo) snd_use_lock_use(&(fifo)->use_lock) +#define snd_seq_fifo_unlock(fifo) snd_use_lock_free(&(fifo)->use_lock) + +/* get a cell from fifo - fifo should be locked */ +int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock); + +/* free dequeued cell - fifo should be locked */ +extern void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell); + +/* clean up queue */ +extern void snd_seq_fifo_clear(fifo_t *f); + +/* polling */ +extern int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait); + +/* resize pool in fifo */ +int snd_seq_fifo_resize(fifo_t *f, int poolsize); + + +#endif diff -Nru a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_info.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,76 @@ +/* + * ALSA sequencer /proc interface + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include + +#include "seq_info.h" +#include "seq_clientmgr.h" +#include "seq_timer.h" + + +static snd_info_entry_t *queues_entry; +static snd_info_entry_t *clients_entry; +static snd_info_entry_t *timer_entry; + + +static snd_info_entry_t * __init +create_info_entry(char *name, int size, void (*read)(snd_info_entry_t *, snd_info_buffer_t *)) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, name, snd_seq_root); + if (entry == NULL) + return NULL; + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = size; + entry->c.text.read = read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return NULL; + } + return entry; +} + + +/* create all our /proc entries */ +int __init snd_seq_info_init(void) +{ + queues_entry = create_info_entry("queues", 512 + (256 * SNDRV_SEQ_MAX_QUEUES), + snd_seq_info_queues_read); + clients_entry = create_info_entry("clients", 512 + (256 * SNDRV_SEQ_MAX_CLIENTS), + snd_seq_info_clients_read); + timer_entry = create_info_entry("timer", 1024, snd_seq_info_timer_read); + return 0; +} + +int __exit snd_seq_info_done(void) +{ + if (queues_entry) + snd_info_unregister(queues_entry); + if (clients_entry) + snd_info_unregister(clients_entry); + if (timer_entry) + snd_info_unregister(timer_entry); + return 0; +} diff -Nru a/sound/core/seq/seq_info.h b/sound/core/seq/seq_info.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_info.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,36 @@ +/* + * ALSA sequencer /proc info + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_INFO_H +#define __SND_SEQ_INFO_H + +#include +#include + +void snd_seq_info_clients_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); +void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); +void snd_seq_info_queues_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); + + +int snd_seq_info_init( void ); +int snd_seq_info_done( void ); + + +#endif diff -Nru a/sound/core/seq/seq_instr.c b/sound/core/seq/seq_instr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_instr.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,671 @@ +/* + * Generic Instrument routines for ALSA sequencer + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "seq_clientmgr.h" +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + + +static void snd_instr_lock_ops(snd_seq_kinstr_list_t *list) +{ + if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) { + spin_lock_irqsave(&list->ops_lock, list->ops_flags); + } else { + down(&list->ops_mutex); + } +} + +static void snd_instr_unlock_ops(snd_seq_kinstr_list_t *list) +{ + if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) { + spin_unlock_irqrestore(&list->ops_lock, list->ops_flags); + } else { + up(&list->ops_mutex); + } +} + +snd_seq_kcluster_t *snd_seq_cluster_new(int atomic) +{ + snd_seq_kcluster_t *cluster; + + cluster = (snd_seq_kcluster_t *) snd_kcalloc(sizeof(snd_seq_kcluster_t), atomic ? GFP_ATOMIC : GFP_KERNEL); + return cluster; +} + +void snd_seq_cluster_free(snd_seq_kcluster_t *cluster, int atomic) +{ + if (cluster == NULL) + return; + kfree(cluster); +} + +snd_seq_kinstr_t *snd_seq_instr_new(int add_len, int atomic) +{ + snd_seq_kinstr_t *instr; + + instr = (snd_seq_kinstr_t *) snd_kcalloc(sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL); + if (instr == NULL) + return NULL; + instr->add_len = add_len; + return instr; +} + +int snd_seq_instr_free(snd_seq_kinstr_t *instr, int atomic) +{ + int result = 0; + + if (instr == NULL) + return -EINVAL; + if (instr->ops && instr->ops->remove) + result = instr->ops->remove(instr->ops->private_data, instr, 1); + if (!result) + kfree(instr); + return result; +} + +snd_seq_kinstr_list_t *snd_seq_instr_list_new(void) +{ + snd_seq_kinstr_list_t *list; + + list = (snd_seq_kinstr_list_t *) snd_kcalloc(sizeof(snd_seq_kinstr_list_t), GFP_KERNEL); + if (list == NULL) + return NULL; + spin_lock_init(&list->lock); + spin_lock_init(&list->ops_lock); + init_MUTEX(&list->ops_mutex); + list->owner = -1; + return list; +} + +void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list_ptr) +{ + snd_seq_kinstr_list_t *list; + snd_seq_kinstr_t *instr; + snd_seq_kcluster_t *cluster; + int idx; + unsigned long flags; + + if (list_ptr == NULL) + return; + list = *list_ptr; + *list_ptr = NULL; + if (list == NULL) + return; + + for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) { + while ((instr = list->hash[idx]) != NULL) { + list->hash[idx] = instr->next; + list->count--; + spin_lock_irqsave(&list->lock, flags); + while (instr->use) { + spin_unlock_irqrestore(&list->lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + spin_lock_irqsave(&list->lock, flags); + } + spin_unlock_irqrestore(&list->lock, flags); + if (snd_seq_instr_free(instr, 0)<0) + snd_printk("instrument free problem\n"); + } + while ((cluster = list->chash[idx]) != NULL) { + list->chash[idx] = cluster->next; + list->ccount--; + snd_seq_cluster_free(cluster, 0); + } + } + kfree(list); +} + +static int instr_free_compare(snd_seq_kinstr_t *instr, + snd_seq_instr_header_t *ifree, + int client) +{ + switch (ifree->cmd) { + case SNDRV_SEQ_INSTR_FREE_CMD_ALL: + /* all, except private for other clients */ + if ((instr->instr.std & 0xff000000) == 0) + return 0; + if (((instr->instr.std >> 24) & 0xff) == client) + return 0; + return 1; + case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE: + /* all my private instruments */ + if ((instr->instr.std & 0xff000000) == 0) + return 1; + if (((instr->instr.std >> 24) & 0xff) == client) + return 0; + return 1; + case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER: + /* all my private instruments */ + if ((instr->instr.std & 0xff000000) == 0) { + if (instr->instr.cluster == ifree->id.cluster) + return 0; + return 1; + } + if (((instr->instr.std >> 24) & 0xff) == client) { + if (instr->instr.cluster == ifree->id.cluster) + return 0; + } + return 1; + } + return 1; +} + +int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list, + snd_seq_instr_header_t *ifree, + int client, + int atomic) +{ + snd_seq_kinstr_t *instr, *prev, *next, *flist; + int idx; + unsigned long flags; + + snd_instr_lock_ops(list); + for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) { + spin_lock_irqsave(&list->lock, flags); + instr = list->hash[idx]; + prev = flist = NULL; + while (instr) { + while (instr && instr_free_compare(instr, ifree, client)) { + prev = instr; + instr = instr->next; + } + if (instr == NULL) + continue; + if (instr->ops && instr->ops->notify) + instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE); + next = instr->next; + if (prev == NULL) { + list->hash[idx] = next; + } else { + prev->next = next; + } + list->count--; + instr->next = flist; + flist = instr; + instr = next; + } + spin_unlock_irqrestore(&list->lock, flags); + while (flist) { + instr = flist; + flist = instr->next; + while (instr->use) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + if (snd_seq_instr_free(instr, atomic)<0) + snd_printk("instrument free problem\n"); + instr = next; + } + } + snd_instr_unlock_ops(list); + return 0; +} + +static int compute_hash_instr_key(snd_seq_instr_t *instr) +{ + int result; + + result = instr->bank | (instr->prg << 16); + result += result >> 24; + result += result >> 16; + result += result >> 8; + return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1); +} + +#if 0 +static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster) +{ + int result; + + result = cluster; + result += result >> 24; + result += result >> 16; + result += result >> 8; + return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1); +} +#endif + +static int compare_instr(snd_seq_instr_t *i1, snd_seq_instr_t *i2, int exact) +{ + if (exact) { + if (i1->cluster != i2->cluster || + i1->bank != i2->bank || + i1->prg != i2->prg) + return 1; + if ((i1->std & 0xff000000) != (i2->std & 0xff000000)) + return 1; + if (!(i1->std & i2->std)) + return 1; + return 0; + } else { + unsigned int client_check; + + if (i2->cluster && i1->cluster != i2->cluster) + return 1; + client_check = i2->std & 0xff000000; + if (client_check) { + if ((i1->std & 0xff000000) != client_check) + return 1; + } else { + if ((i1->std & i2->std) != i2->std) + return 1; + } + return i1->bank != i2->bank || i1->prg != i2->prg; + } +} + +snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list, + snd_seq_instr_t *instr, + int exact, + int follow_alias) +{ + unsigned long flags; + int depth = 0; + snd_seq_kinstr_t *result; + + if (list == NULL || instr == NULL) + return NULL; + spin_lock_irqsave(&list->lock, flags); + __again: + result = list->hash[compute_hash_instr_key(instr)]; + while (result) { + if (!compare_instr(&result->instr, instr, exact)) { + if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) { + instr = (snd_seq_instr_t *)KINSTR_DATA(result); + if (++depth > 10) + goto __not_found; + goto __again; + } + result->use++; + spin_unlock_irqrestore(&list->lock, flags); + return result; + } + result = result->next; + } + __not_found: + spin_unlock_irqrestore(&list->lock, flags); + return NULL; +} + +void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list, + snd_seq_kinstr_t *instr) +{ + unsigned long flags; + + if (list == NULL || instr == NULL) + return; + spin_lock_irqsave(&list->lock, flags); + if (instr->use <= 0) { + snd_printk("free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name); + } else { + instr->use--; + } + spin_unlock_irqrestore(&list->lock, flags); +} + +static snd_seq_kinstr_ops_t *instr_ops(snd_seq_kinstr_ops_t *ops, char *instr_type) +{ + while (ops) { + if (!strcmp(ops->instr_type, instr_type)) + return ops; + ops = ops->next; + } + return NULL; +} + +static int instr_result(snd_seq_event_t *ev, + int type, int result, + int atomic) +{ + snd_seq_event_t sev; + + memset(&sev, 0, sizeof(sev)); + sev.type = SNDRV_SEQ_EVENT_RESULT; + sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED | + SNDRV_SEQ_PRIORITY_NORMAL; + sev.source = ev->dest; + sev.dest = ev->source; + sev.data.result.event = type; + sev.data.result.result = result; +#if 0 + printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n", + type, result, + sev.queue, + sev.source.client, sev.source.port, + sev.dest.client, sev.dest.port); +#endif + return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0); +} + +static int instr_begin(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + unsigned long flags; + + spin_lock_irqsave(&list->lock, flags); + if (list->owner >= 0 && list->owner != ev->source.client) { + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic); + } + list->owner = ev->source.client; + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic); +} + +static int instr_end(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + unsigned long flags; + + /* TODO: timeout handling */ + spin_lock_irqsave(&list->lock, flags); + if (list->owner == ev->source.client) { + list->owner = -1; + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic); + } + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic); +} + +static int instr_info(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_format_info(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_reset(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_status(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_put(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + unsigned long flags; + snd_seq_instr_header_t put; + snd_seq_kinstr_t *instr; + int result = -EINVAL, len, key; + + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR) + goto __return; + + if (ev->data.ext.len < sizeof(snd_seq_instr_header_t)) + goto __return; + if (copy_from_user(&put, ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) { + result = -EFAULT; + goto __return; + } + snd_instr_lock_ops(list); + if (put.id.instr.std & 0xff000000) { /* private instrument */ + put.id.instr.std &= 0x00ffffff; + put.id.instr.std |= (unsigned int)ev->source.client << 24; + } + if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) { + snd_seq_instr_free_use(list, instr); + snd_instr_unlock_ops(list); + result = -EBUSY; + goto __return; + } + ops = instr_ops(ops, put.data.data.format); + if (ops == NULL) { + snd_instr_unlock_ops(list); + goto __return; + } + len = ops->add_len; + if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS) + len = sizeof(snd_seq_instr_t); + instr = snd_seq_instr_new(len, atomic); + if (instr == NULL) { + snd_instr_unlock_ops(list); + result = -ENOMEM; + goto __return; + } + instr->ops = ops; + instr->instr = put.id.instr; + strncpy(instr->name, put.data.name, sizeof(instr->name)-1); + instr->name[sizeof(instr->name)-1] = '\0'; + instr->type = put.data.type; + if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) { + result = ops->put(ops->private_data, + instr, + ev->data.ext.ptr + sizeof(snd_seq_instr_header_t), + ev->data.ext.len - sizeof(snd_seq_instr_header_t), + atomic, + put.cmd); + if (result < 0) { + snd_seq_instr_free(instr, atomic); + snd_instr_unlock_ops(list); + goto __return; + } + } + key = compute_hash_instr_key(&instr->instr); + spin_lock_irqsave(&list->lock, flags); + instr->next = list->hash[key]; + list->hash[key] = instr; + list->count++; + spin_unlock_irqrestore(&list->lock, flags); + snd_instr_unlock_ops(list); + result = 0; + __return: + instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic); + return result; +} + +static int instr_get(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_free(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + snd_seq_instr_header_t ifree; + snd_seq_kinstr_t *instr, *prev; + int result = -EINVAL; + unsigned long flags; + unsigned int hash; + + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR) + goto __return; + + if (ev->data.ext.len < sizeof(snd_seq_instr_header_t)) + goto __return; + if (copy_from_user(&ifree, ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) { + result = -EFAULT; + goto __return; + } + if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL || + ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE || + ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) { + result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic); + goto __return; + } + if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) { + if (ifree.id.instr.std & 0xff000000) { + ifree.id.instr.std &= 0x00ffffff; + ifree.id.instr.std |= (unsigned int)ev->source.client << 24; + } + hash = compute_hash_instr_key(&ifree.id.instr); + snd_instr_lock_ops(list); + spin_lock_irqsave(&list->lock, flags); + instr = list->hash[hash]; + prev = NULL; + while (instr) { + if (!compare_instr(&instr->instr, &ifree.id.instr, 1)) + goto __free_single; + prev = instr; + instr = instr->next; + } + result = -ENOENT; + spin_unlock_irqrestore(&list->lock, flags); + snd_instr_unlock_ops(list); + goto __return; + + __free_single: + if (prev) { + prev->next = instr->next; + } else { + list->hash[hash] = instr->next; + } + if (instr->ops && instr->ops->notify) + instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE); + while (instr->use) { + spin_unlock_irqrestore(&list->lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + spin_lock_irqsave(&list->lock, flags); + } + spin_unlock_irqrestore(&list->lock, flags); + result = snd_seq_instr_free(instr, atomic); + snd_instr_unlock_ops(list); + goto __return; + } + + __return: + instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic); + return result; +} + +static int instr_list(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_cluster(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int client, + int atomic, + int hop) +{ + int direct = 0; + + snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL); + if (snd_seq_ev_is_direct(ev)) { + direct = 1; + switch (ev->type) { + case SNDRV_SEQ_EVENT_INSTR_BEGIN: + return instr_begin(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_END: + return instr_end(ops, list, ev, atomic, hop); + } + } + if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct) + return -EINVAL; + switch (ev->type) { + case SNDRV_SEQ_EVENT_INSTR_INFO: + return instr_info(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_FINFO: + return instr_format_info(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_RESET: + return instr_reset(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_STATUS: + return instr_status(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_PUT: + return instr_put(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_GET: + return instr_get(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_FREE: + return instr_free(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_LIST: + return instr_list(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_CLUSTER: + return instr_cluster(ops, list, ev, atomic, hop); + } + return -EINVAL; +} + +/* + * Init part + */ + +static int __init alsa_seq_instr_init(void) +{ + return 0; +} + +static void __exit alsa_seq_instr_exit(void) +{ +} + +module_init(alsa_seq_instr_init) +module_exit(alsa_seq_instr_exit) + +EXPORT_SYMBOL(snd_seq_instr_list_new); +EXPORT_SYMBOL(snd_seq_instr_list_free); +EXPORT_SYMBOL(snd_seq_instr_list_free_cond); +EXPORT_SYMBOL(snd_seq_instr_find); +EXPORT_SYMBOL(snd_seq_instr_free_use); +EXPORT_SYMBOL(snd_seq_instr_event); diff -Nru a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_lock.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,85 @@ +/* + * Do sleep inside a spin-lock + * Copyright (c) 1999 by Takashi Iwai + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include "seq_lock.h" + +#if defined(__SMP__) || defined(CONFIG_SND_DEBUG) + +/* (interruptible) sleep_on during the specified spinlock */ +void snd_seq_sleep_in_lock(wait_queue_head_t *p, spinlock_t *lock) +{ + wait_queue_t wait; + + set_current_state(TASK_INTERRUPTIBLE); + init_waitqueue_entry(&wait, current); + + add_wait_queue(p, &wait); + + spin_unlock(lock); + schedule(); + spin_lock_irq(lock); + + remove_wait_queue(p, &wait); +} + +/* (interruptible) sleep_on with timeout during the specified spinlock */ +long snd_seq_sleep_timeout_in_lock(wait_queue_head_t *p, spinlock_t *lock, long timeout) +{ + wait_queue_t wait; + + set_current_state(TASK_INTERRUPTIBLE); + init_waitqueue_entry(&wait, current); + + add_wait_queue(p, &wait); + + spin_unlock(lock); + timeout = schedule_timeout(timeout); + spin_lock_irq(lock); + + remove_wait_queue(p, &wait); + + return timeout; +} + +/* wait until all locks are released */ +void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line) +{ + int max_count = 5 * HZ; + + if (atomic_read(lockp) < 0) { + printk("seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line); + return; + } + while (atomic_read(lockp) > 0) { + if (max_count == 0) { + snd_printk("seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line); + break; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + max_count--; + } +} + +#endif diff -Nru a/sound/core/seq/seq_lock.h b/sound/core/seq/seq_lock.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_lock.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,42 @@ +#ifndef __SND_SEQ_LOCK_H +#define __SND_SEQ_LOCK_H + +#include + +#if defined(__SMP__) || defined(CONFIG_SND_DEBUG) + +typedef atomic_t snd_use_lock_t; + +/* initialize lock */ +#define snd_use_lock_init(lockp) atomic_set(lockp, 0) + +/* increment lock */ +#define snd_use_lock_use(lockp) atomic_inc(lockp) + +/* release lock */ +#define snd_use_lock_free(lockp) atomic_dec(lockp) + +/* wait until all locks are released */ +void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line); +#define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__) + +/* (interruptible) sleep_on during the specified spinlock */ +void snd_seq_sleep_in_lock(wait_queue_head_t *p, spinlock_t *lock); + +/* (interruptible) sleep_on with timeout during the specified spinlock */ +long snd_seq_sleep_timeout_in_lock(wait_queue_head_t *p, spinlock_t *lock, long timeout); + +#else /* SMP || CONFIG_SND_DEBUG */ + +typedef spinlock_t snd_use_lock_t; /* dummy */ +#define snd_use_lock_init(lockp) /**/ +#define snd_use_lock_use(lockp) /**/ +#define snd_use_lock_free(lockp) /**/ +#define snd_use_lock_sync(lockp) /**/ + +#define snd_seq_sleep_in_lock(p,lock) interruptible_sleep_on(p) +#define snd_seq_sleep_timeout_in_lock(p,lock,timeout) interruptible_sleep_on_timeout(p,timeout) + +#endif /* SMP || CONFIG_SND_DEBUG */ + +#endif /* __SND_SEQ_LOCK_H */ diff -Nru a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_memory.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,514 @@ +/* + * ALSA sequencer Memory Manager + * Copyright (c) 1998 by Frank van de Pol + * Jaroslav Kysela + * 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#include +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_info.h" +#include "seq_lock.h" + +/* semaphore in struct file record */ +#define semaphore_of(fp) ((fp)->f_dentry->d_inode->i_sem) + + +inline static int snd_seq_pool_available(pool_t *pool) +{ + return pool->total_elements - atomic_read(&pool->counter); +} + +inline static int snd_seq_output_ok(pool_t *pool) +{ + return snd_seq_pool_available(pool) >= pool->room; +} + +/* + * Variable length event: + * The event like sysex uses variable length type. + * The external data may be stored in three different formats. + * 1) kernel space + * This is the normal case. + * ext.data.len = length + * ext.data.ptr = buffer pointer + * 2) user space + * When an event is generated via read(), the external data is + * kept in user space until expanded. + * ext.data.len = length | SNDRV_SEQ_EXT_USRPTR + * ext.data.ptr = userspace pointer + * 3) chained cells + * When the variable length event is enqueued (in prioq or fifo), + * the external data is decomposed to several cells. + * ext.data.len = length | SNDRV_SEQ_EXT_CHAINED + * ext.data.ptr = the additiona cell head + * -> cell.next -> cell.next -> .. + */ + +/* + * exported: + * call dump function to expand external data. + */ + +static int get_var_len(const snd_seq_event_t *event) +{ + if ((event->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) + return -EINVAL; + + return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK; +} + +int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data) +{ + int len, err; + snd_seq_event_cell_t *cell; + + if ((len = get_var_len(event)) <= 0) + return len; + + if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { + char buf[32]; + char *curptr = event->data.ext.ptr; + while (len > 0) { + int size = sizeof(buf); + if (len < size) + size = len; + if (copy_from_user(buf, curptr, size) < 0) + return -EFAULT; + err = func(private_data, buf, size); + if (err < 0) + return err; + curptr += size; + len -= size; + } + return 0; + } if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) { + return func(private_data, event->data.ext.ptr, len); + } + + cell = (snd_seq_event_cell_t*)event->data.ext.ptr; + for (; len > 0 && cell; cell = cell->next) { + int size = sizeof(snd_seq_event_t); + if (len < size) + size = len; + err = func(private_data, &cell->event, size); + if (err < 0) + return err; + len -= size; + } + return 0; +} + + +/* + * exported: + * expand the variable length event to linear buffer space. + */ + +static int copy_in_kernel(char **bufptr, const void *src, int size) +{ + memcpy(*bufptr, src, size); + *bufptr += size; + return 0; +} + +static int copy_in_user(char **bufptr, const void *src, int size) +{ + if (copy_to_user(*bufptr, src, size)) + return -EFAULT; + *bufptr += size; + return 0; +} + +int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned) +{ + int len, newlen; + int err; + + if ((len = get_var_len(event)) < 0) + return len; + newlen = len; + if (size_aligned > 0) + newlen = ((len + size_aligned - 1) / size_aligned) * size_aligned; + if (count < newlen) + return -EAGAIN; + + if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { + if (! in_kernel) + return -EINVAL; + if (copy_from_user(buf, event->data.ext.ptr, len) < 0) + return -EFAULT; + return newlen; + } + err = snd_seq_dump_var_event(event, + in_kernel ? (snd_seq_dump_func_t)copy_in_kernel : + (snd_seq_dump_func_t)copy_in_user, + &buf); + return err < 0 ? err : newlen; +} + + +/* + * release this cell, free extended data if available + */ + +static inline void free_cell(pool_t *pool, snd_seq_event_cell_t *cell) +{ + cell->next = pool->free; + pool->free = cell; + atomic_dec(&pool->counter); +} + +void snd_seq_cell_free(snd_seq_event_cell_t * cell) +{ + unsigned long flags; + pool_t *pool; + + snd_assert(cell != NULL, return); + pool = cell->pool; + snd_assert(pool != NULL, return); + + spin_lock_irqsave(&pool->lock, flags); + free_cell(pool, cell); + if (snd_seq_ev_is_variable(&cell->event)) { + if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) { + snd_seq_event_cell_t *curp, *nextptr; + curp = cell->event.data.ext.ptr; + for (; curp; curp = nextptr) { + nextptr = curp->next; + curp->next = pool->free; + free_cell(pool, curp); + } + } + } + if (waitqueue_active(&pool->output_sleep)) { + /* has enough space now? */ + if (snd_seq_output_ok(pool)) + wake_up(&pool->output_sleep); + } + spin_unlock_irqrestore(&pool->lock, flags); +} + + +/* + * allocate an event cell. + */ +int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + int err = -EAGAIN; + + if (pool == NULL) + return -EINVAL; + + *cellp = NULL; + + spin_lock_irqsave(&pool->lock, flags); + if (pool->ptr == NULL) { /* not initialized */ + snd_printd("seq: pool is not initialized\n"); + err = -EINVAL; + goto __error; + } + while (pool->free == NULL && ! nonblock && ! pool->closing) { + /* change semaphore to allow other clients + to access device file */ + if (file) + up(&semaphore_of(file)); + + snd_seq_sleep_in_lock(&pool->output_sleep, &pool->lock); + + /* restore semaphore again */ + if (file) + down(&semaphore_of(file)); + + /* interrupted? */ + if (signal_pending(current)) { + err = -ERESTARTSYS; + goto __error; + } + } + if (pool->closing) { /* closing.. */ + err = -ENOMEM; + goto __error; + } + + cell = pool->free; + if (cell) { + int used; + pool->free = cell->next; + atomic_inc(&pool->counter); + used = atomic_read(&pool->counter); + if (pool->max_used < used) + pool->max_used = used; + pool->event_alloc_success++; + /* clear cell pointers */ + cell->next = NULL; + err = 0; + } else + pool->event_alloc_failures++; + *cellp = cell; + +__error: + spin_unlock_irqrestore(&pool->lock, flags); + return err; +} + + +/* + * duplicate the event to a cell. + * if the event has external data, the data is decomposed to additional + * cells. + */ +int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file) +{ + int ncells, err; + unsigned int extlen; + snd_seq_event_cell_t *cell; + + *cellp = NULL; + + ncells = 0; + extlen = 0; + if (snd_seq_ev_is_variable(event)) { + extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK; + ncells = (extlen + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t); + } + if (ncells >= pool->total_elements) + return -ENOMEM; + + err = snd_seq_cell_alloc(pool, &cell, nonblock, file); + if (err < 0) + return err; + + /* copy the event */ + cell->event = *event; + + /* decompose */ + if (snd_seq_ev_is_variable(event)) { + int len = extlen; + int is_chained = event->data.ext.len & SNDRV_SEQ_EXT_CHAINED; + int is_usrptr = event->data.ext.len & SNDRV_SEQ_EXT_USRPTR; + snd_seq_event_cell_t *src, *tmp, *tail; + char *buf; + + cell->event.data.ext.len = extlen | SNDRV_SEQ_EXT_CHAINED; + cell->event.data.ext.ptr = NULL; + + src = (snd_seq_event_cell_t*)event->data.ext.ptr; + buf = (char *)event->data.ext.ptr; + tail = NULL; + + while (ncells-- > 0) { + int size = sizeof(snd_seq_event_t); + if (len < size) + size = len; + err = snd_seq_cell_alloc(pool, &tmp, nonblock, file); + if (err < 0) + goto __error; + if (cell->event.data.ext.ptr == NULL) + cell->event.data.ext.ptr = tmp; + if (tail) + tail->next = tmp; + tail = tmp; + /* copy chunk */ + if (is_chained && src) { + tmp->event = src->event; + src = src->next; + } else if (is_usrptr) { + if (copy_from_user(&tmp->event, buf, size)) { + err = -EFAULT; + goto __error; + } + } else { + memcpy(&tmp->event, buf, size); + } + buf += size; + len -= size; + } + } + + *cellp = cell; + return 0; + +__error: + snd_seq_cell_free(cell); + return err; +} + + +/* poll wait */ +int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait) +{ + poll_wait(file, &pool->output_sleep, wait); + return snd_seq_output_ok(pool); +} + + +/* allocate room specified number of events */ +int snd_seq_pool_init(pool_t *pool) +{ + int cell; + snd_seq_event_cell_t *cellptr; + unsigned long flags; + + snd_assert(pool != NULL, return -EINVAL); + if (pool->ptr) /* should be atomic? */ + return 0; + + pool->ptr = vmalloc(sizeof(snd_seq_event_cell_t) * pool->size); + if (pool->ptr == NULL) { + snd_printd("seq: malloc for sequencer events failed\n"); + return -ENOMEM; + } + + /* add new cells to the free cell list */ + spin_lock_irqsave(&pool->lock, flags); + pool->free = NULL; + + for (cell = 0; cell < pool->size; cell++) { + cellptr = pool->ptr + cell; + cellptr->pool = pool; + cellptr->next = pool->free; + pool->free = cellptr; + } + pool->room = (pool->size + 1) / 2; + + /* init statistics */ + pool->max_used = 0; + pool->total_elements = pool->size; + spin_unlock_irqrestore(&pool->lock, flags); + return 0; +} + +/* remove events */ +int snd_seq_pool_done(pool_t *pool) +{ + unsigned long flags; + snd_seq_event_cell_t *ptr; + int max_count = 5 * HZ; + + snd_assert(pool != NULL, return -EINVAL); + + /* wait for closing all threads */ + spin_lock_irqsave(&pool->lock, flags); + pool->closing = 1; + spin_unlock_irqrestore(&pool->lock, flags); + + if (waitqueue_active(&pool->output_sleep)) + wake_up(&pool->output_sleep); + + while (atomic_read(&pool->counter) > 0) { + if (max_count == 0) { + snd_printk("snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter)); + break; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + max_count--; + } + + /* release all resources */ + spin_lock_irqsave(&pool->lock, flags); + ptr = pool->ptr; + pool->ptr = NULL; + pool->free = NULL; + pool->total_elements = 0; + spin_unlock_irqrestore(&pool->lock, flags); + + if (ptr) + vfree(ptr); + + spin_lock_irqsave(&pool->lock, flags); + pool->closing = 0; + spin_unlock_irqrestore(&pool->lock, flags); + + return 0; +} + + +/* init new memory pool */ +pool_t *snd_seq_pool_new(int poolsize) +{ + pool_t *pool; + + /* create pool block */ + pool = snd_kcalloc(sizeof(pool_t), GFP_KERNEL); + if (pool == NULL) { + snd_printd("seq: malloc failed for pool\n"); + return NULL; + } + spin_lock_init(&pool->lock); + pool->ptr = NULL; + pool->free = NULL; + pool->total_elements = 0; + atomic_set(&pool->counter, 0); + pool->closing = 0; + init_waitqueue_head(&pool->output_sleep); + + pool->size = poolsize; + + /* init statistics */ + pool->max_used = 0; + return pool; +} + +/* remove memory pool */ +int snd_seq_pool_delete(pool_t **ppool) +{ + pool_t *pool = *ppool; + + *ppool = NULL; + if (pool == NULL) + return 0; + snd_seq_pool_done(pool); + kfree(pool); + return 0; +} + +/* initialize sequencer memory */ +int __init snd_sequencer_memory_init(void) +{ + return 0; +} + +/* release sequencer memory */ +void __exit snd_sequencer_memory_done(void) +{ +} + + +/* exported to seq_clientmgr.c */ +void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t *pool, char *space) +{ + if (pool == NULL) + return; + snd_iprintf(buffer, "%sPool size : %d\n", space, pool->total_elements); + snd_iprintf(buffer, "%sCells in use : %d\n", space, atomic_read(&pool->counter)); + snd_iprintf(buffer, "%sPeak cells in use : %d\n", space, pool->max_used); + snd_iprintf(buffer, "%sAlloc success : %d\n", space, pool->event_alloc_success); + snd_iprintf(buffer, "%sAlloc failures : %d\n", space, pool->event_alloc_failures); +} diff -Nru a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_memory.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,105 @@ +/* + * ALSA sequencer Memory Manager + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_MEMORYMGR_H +#define __SND_SEQ_MEMORYMGR_H + +#include +#include + +typedef struct pool pool_t; + +/* container for sequencer event (internal use) */ +typedef struct snd_seq_event_cell_t { + snd_seq_event_t event; + pool_t *pool; /* used pool */ + struct snd_seq_event_cell_t *next; /* next cell */ +} snd_seq_event_cell_t; + +/* design note: the pool is a contigious block of memory, if we dynamicly + want to add additional cells to the pool be better store this in another + pool as we need to know the base address of the pool when releasing + memory. */ + +struct pool { + snd_seq_event_cell_t *ptr; /* pointer to first event chunk */ + snd_seq_event_cell_t *free; /* pointer to the head of the free list */ + + int total_elements; /* pool size actually allocated */ + atomic_t counter; /* cells free */ + + int size; /* pool size to be allocated */ + int room; /* watermark for sleep/wakeup */ + + int closing; + + /* statistics */ + int max_used; + int event_alloc_nopool; + int event_alloc_failures; + int event_alloc_success; + + /* Write locking */ + wait_queue_head_t output_sleep; + + /* Pool lock */ + spinlock_t lock; +}; + +extern void snd_seq_cell_free(snd_seq_event_cell_t* cell); +int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file); + +int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file); + +/* return number of unused (free) cells */ +static inline int snd_seq_unused_cells(pool_t *pool) +{ + return pool ? pool->total_elements - atomic_read(&pool->counter) : 0; +} + +/* return total number of allocated cells */ +static inline int snd_seq_total_cells(pool_t *pool) +{ + return pool ? pool->total_elements : 0; +} + +/* init pool - allocate events */ +int snd_seq_pool_init(pool_t *pool); + +/* done pool - free events */ +int snd_seq_pool_done(pool_t *pool); + +/* create pool */ +pool_t *snd_seq_pool_new(int poolsize); + +/* remove pool */ +int snd_seq_pool_delete(pool_t **pool); + +/* init memory */ +int snd_sequencer_memory_init(void); + +/* release event memory */ +void snd_sequencer_memory_done(void); + +/* polling */ +int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait); + + +#endif diff -Nru a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_midi.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,461 @@ +/* + * Generic MIDI synth driver for ALSA sequencer + * Copyright (c) 1998 by Frank van de Pol + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* +Possible options for midisynth module: + - automatic opening of midi ports on first received event or subscription + (close will be performed when client leaves) +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Frank van de Pol , Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); +int output_buffer_size = PAGE_SIZE; +MODULE_PARM(output_buffer_size, "i"); +MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes."); +int input_buffer_size = PAGE_SIZE; +MODULE_PARM(input_buffer_size, "i"); +MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes."); + +/* data for this midi synth driver */ +typedef struct { + snd_card_t *card; + int device; + int subdevice; + snd_rawmidi_file_t input_rfile; + snd_rawmidi_file_t output_rfile; + int seq_client; + int seq_port; + snd_midi_event_t *parser; +} seq_midisynth_t; + +typedef struct { + int seq_client; + int num_ports; + int ports_per_device[SNDRV_RAWMIDI_DEVICES]; + seq_midisynth_t *ports[SNDRV_RAWMIDI_DEVICES]; +} seq_midisynth_client_t; + +static seq_midisynth_client_t *synths[SNDRV_CARDS]; +static DECLARE_MUTEX(register_mutex); + +/* handle rawmidi input event (MIDI v1.0 stream) */ +static void snd_midi_input_event(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime; + seq_midisynth_t *msynth; + snd_seq_event_t ev; + char buf[16], *pbuf; + long res, count; + + if (substream == NULL) + return; + runtime = substream->runtime; + msynth = (seq_midisynth_t *) runtime->private_data; + if (msynth == NULL) + return; + memset(&ev, 0, sizeof(ev)); + while (runtime->avail > 0) { + res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf)); + if (res <= 0) + continue; + if (msynth->parser == NULL) + continue; + pbuf = buf; + while (res > 0) { + count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev); + if (count < 0) + break; + pbuf += count; + res -= count; + if (ev.type != SNDRV_SEQ_EVENT_NONE) { + ev.source.port = msynth->seq_port; + ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0); + /* clear event and reset header */ + memset(&ev, 0, sizeof(ev)); + } + } + } +} + +static int dump_midi(snd_rawmidi_substream_t *substream, const char *buf, int count) +{ + snd_rawmidi_runtime_t *runtime; + int tmp; + + snd_assert(substream != NULL || buf != NULL, return -EINVAL); + runtime = substream->runtime; + if ((tmp = runtime->avail) < count) { + snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp); + return -ENOMEM; + } + if (snd_rawmidi_kernel_write(substream, buf, count) < count) + return -EINVAL; + return 0; +} + +static int event_process_midi(snd_seq_event_t * ev, int direct, + void *private_data, int atomic, int hop) +{ + seq_midisynth_t *msynth = (seq_midisynth_t *) private_data; + unsigned char msg[10]; /* buffer for constructing midi messages */ + snd_rawmidi_substream_t *substream; + int res; + + snd_assert(msynth != NULL, return -EINVAL); + substream = msynth->output_rfile.output; + if (substream == NULL) + return -ENODEV; + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */ + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { + /* invalid event */ + snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); + return 0; + } + res = snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream); + snd_midi_event_reset_decode(msynth->parser); + if (res < 0) + return res; + } else { + if (msynth->parser == NULL) + return -EIO; + res = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev); + if (res < 0) + return res; + if ((res = dump_midi(substream, msg, res)) < 0) { + snd_midi_event_reset_decode(msynth->parser); + return res; + } + } + return 0; +} + + +static int snd_seq_midisynth_new(seq_midisynth_t *msynth, + snd_card_t *card, + int device, + int subdevice) +{ + if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0) + return -ENOMEM; + msynth->card = card; + msynth->device = device; + msynth->subdevice = subdevice; + return 0; +} + +/* open associated midi device for input */ +static int midisynth_subscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + int err; + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + snd_rawmidi_runtime_t *runtime; + snd_rawmidi_params_t params; + + /* open midi port */ + if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, &msynth->input_rfile)) < 0) { + snd_printd("midi input open failed!!!\n"); + return err; + } + runtime = msynth->input_rfile.input->runtime; + memset(¶ms, 0, sizeof(params)); + params.avail_min = 1; + params.buffer_size = input_buffer_size; + if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms)) < 0) { + snd_rawmidi_kernel_release(&msynth->input_rfile); + return err; + } + runtime->event = snd_midi_input_event; + runtime->private_data = msynth; + snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0); + return 0; +} + +/* close associated midi device for input */ +static int midisynth_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + int err; + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + + snd_assert(msynth->input_rfile.input != NULL, return -EINVAL); + err = snd_rawmidi_kernel_release(&msynth->input_rfile); + return err; +} + +/* open associated midi device for output */ +static int midisynth_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + int err; + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + snd_rawmidi_params_t params; + + /* open midi port */ + if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, &msynth->output_rfile)) < 0) { + snd_printd("midi output open failed!!!\n"); + return err; + } + memset(¶ms, 0, sizeof(params)); + params.avail_min = 1; + params.buffer_size = output_buffer_size; + if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms)) < 0) { + snd_rawmidi_kernel_release(&msynth->output_rfile); + return err; + } + return 0; +} + +/* close associated midi device for output */ +static int midisynth_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + unsigned char buf = 0xff; /* MIDI reset */ + + snd_assert(msynth->output_rfile.output != NULL, return -EINVAL); + /* sending single MIDI reset message to shut the device up */ + snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1); + snd_rawmidi_drain_output(msynth->output_rfile.output); + return snd_rawmidi_kernel_release(&msynth->output_rfile); +} + +/* delete given midi synth port */ +static void snd_seq_midisynth_delete(seq_midisynth_t *msynth) +{ + snd_seq_port_info_t port; + + if (msynth == NULL) + return; + + if (msynth->seq_client > 0) { + /* delete port */ + memset(&port, 0, sizeof(port)); + port.addr.client = msynth->seq_client; + port.addr.port = msynth->seq_port; + snd_seq_kernel_client_ctl(port.addr.client, SNDRV_SEQ_IOCTL_DELETE_PORT, &port); + } + + if (msynth->parser) + snd_midi_event_free(msynth->parser); +} + +/* register new midi synth port */ +int +snd_seq_midisynth_register_port(snd_seq_device_t *dev) +{ + seq_midisynth_client_t *client; + seq_midisynth_t *msynth, *ms; + snd_seq_port_info_t port; + snd_rawmidi_info_t info; + int newclient = 0, p, ports; + snd_seq_client_callback_t callbacks; + snd_seq_port_callback_t pcallbacks; + snd_seq_client_info_t inf; + snd_card_t *card = dev->card; + int device = dev->device; + unsigned int input_count = 0, output_count = 0; + + snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL); + info.device = device; + info.stream = SNDRV_RAWMIDI_STREAM_OUTPUT; + info.subdevice = 0; + if (snd_rawmidi_info_select(card, &info) >= 0) + output_count = info.subdevices_count; + info.stream = SNDRV_RAWMIDI_STREAM_INPUT; + if (snd_rawmidi_info_select(card, &info) >= 0) { + input_count = info.subdevices_count; + } + ports = output_count; + if (ports < input_count) + ports = input_count; + if (ports == 0) + return -ENODEV; + if (ports > (256 / SNDRV_RAWMIDI_DEVICES)) + ports = 256 / SNDRV_RAWMIDI_DEVICES; + + down(®ister_mutex); + client = synths[card->number]; + if (client == NULL) { + newclient = 1; + client = snd_kcalloc(sizeof(seq_midisynth_client_t), GFP_KERNEL); + if (client == NULL) { + up(®ister_mutex); + return -ENOMEM; + } + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = client; + callbacks.allow_input = callbacks.allow_output = 1; + client->seq_client = snd_seq_create_kernel_client(card, 0, &callbacks); + if (client->seq_client < 0) { + kfree(client); + up(®ister_mutex); + return -ENOMEM; + } + /* set our client name */ + memset(&inf,0,sizeof(snd_seq_client_info_t)); + inf.client = client->seq_client; + inf.type = KERNEL_CLIENT; + sprintf(inf.name, "External MIDI %i", card->number); + snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &inf); + } + + msynth = snd_kcalloc(sizeof(seq_midisynth_t) * ports, GFP_KERNEL); + if (msynth == NULL) + goto __nomem; + + for (p = 0; p < ports; p++) { + ms = &msynth[p]; + + if (snd_seq_midisynth_new(ms, card, device, p) < 0) + goto __nomem; + + /* declare port */ + memset(&port, 0, sizeof(port)); + port.addr.client = client->seq_client; + port.addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + memset(&info, 0, sizeof(info)); + info.device = device; + if (p < output_count) + info.stream = SNDRV_RAWMIDI_STREAM_OUTPUT; + else + info.stream = SNDRV_RAWMIDI_STREAM_INPUT; + info.subdevice = p; + if (snd_rawmidi_info_select(card, &info) >= 0) + strcpy(port.name, info.subname); + if (! port.name[0]) { + if (ports > 1) + sprintf(port.name, "MIDI %d-%d-%d", card->number, device, p); + else + sprintf(port.name, "MIDI %d-%d", card->number, device); + } + if ((info.flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count) + port.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + if ((info.flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count) + port.capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; + if ((port.capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) && + info.flags & SNDRV_RAWMIDI_INFO_DUPLEX) + port.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; + port.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + port.midi_channels = 16; + memset(&pcallbacks, 0, sizeof(pcallbacks)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.private_data = ms; + pcallbacks.subscribe = midisynth_subscribe; + pcallbacks.unsubscribe = midisynth_unsubscribe; + pcallbacks.use = midisynth_use; + pcallbacks.unuse = midisynth_unuse; + pcallbacks.event_input = event_process_midi; + port.kernel = &pcallbacks; + if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &port)<0) + goto __nomem; + ms->seq_client = client->seq_client; + ms->seq_port = port.addr.port; + } + client->ports_per_device[device] = ports; + client->ports[device] = msynth; + client->num_ports++; + if (newclient) + synths[card->number] = client; + up(®ister_mutex); + return 0; /* success */ + + __nomem: + if (msynth != NULL) { + for (p = 0; p < ports; p++) + snd_seq_midisynth_delete(&msynth[p]); + kfree(msynth); + } + if (newclient) { + snd_seq_delete_kernel_client(client->seq_client); + kfree(client); + } + up(®ister_mutex); + return -ENOMEM; +} + +/* release midi synth port */ +int +snd_seq_midisynth_unregister_port(snd_seq_device_t *dev) +{ + seq_midisynth_client_t *client; + seq_midisynth_t *msynth; + snd_card_t *card = dev->card; + int device = dev->device, p, ports; + + down(®ister_mutex); + client = synths[card->number]; + if (client == NULL || client->ports[device] == NULL) { + up(®ister_mutex); + return -ENODEV; + } + snd_seq_event_port_detach(client->seq_client, client->ports[device]->seq_port); + ports = client->ports_per_device[device]; + client->ports_per_device[device] = 0; + msynth = client->ports[device]; + client->ports[device] = NULL; + snd_runtime_check(msynth != NULL || ports <= 0, goto __skip); + for (p = 0; p < ports; p++) + snd_seq_midisynth_delete(&msynth[p]); + kfree(msynth); + __skip: + client->num_ports--; + if (client->num_ports <= 0) { + snd_seq_delete_kernel_client(client->seq_client); + synths[card->number] = NULL; + kfree(client); + } + up(®ister_mutex); + return 0; +} + + +static int __init alsa_seq_midi_init(void) +{ + static snd_seq_dev_ops_t ops = { + snd_seq_midisynth_register_port, + snd_seq_midisynth_unregister_port, + }; + memset(&synths, 0, sizeof(synths)); + snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0); + return 0; +} + +static void __exit alsa_seq_midi_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH); +} + +module_init(alsa_seq_midi_init) +module_exit(alsa_seq_midi_exit) diff -Nru a/sound/core/seq/seq_midi_clock.c b/sound/core/seq/seq_midi_clock.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_midi_clock.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,91 @@ +/* + * MIDI clock event converter + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include "seq_queue.h" + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + +typedef struct midi_clock { + unsigned int cur_pos; +} midi_clock_t; + +static int midi_open(queue_sync_t *sync_info, seq_sync_arg_t *retp) +{ + midi_clock_t *arg; + + if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL) + return -ENOMEM; + sync_info->param.tick.ppq = 24; + sync_info->param.tick.ticks = 1; + arg->cur_pos = 0; + *retp = arg; + return 0; +} + +static int midi_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + switch (src->type) { + case SNDRV_SEQ_EVENT_SYNC: + ev->type = SNDRV_SEQ_EVENT_CLOCK; + return 1; + case SNDRV_SEQ_EVENT_SYNC_POS: + ev->type = SNDRV_SEQ_EVENT_SONGPOS; + ev->data.control.value = src->data.queue.param.position / 6; + return 1; + } + return 0; +} + +static int midi_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + midi_clock_t *arg = _arg; + switch (src->type) { + case SNDRV_SEQ_EVENT_CLOCK: + ev->type = SNDRV_SEQ_EVENT_SYNC; + ev->data.queue.param.position = arg->cur_pos; + arg->cur_pos++; + return 1; + case SNDRV_SEQ_EVENT_SONGPOS: + ev->type = SNDRV_SEQ_EVENT_SYNC_POS; + arg->cur_pos = src->data.control.value * 6; + ev->data.queue.param.position = arg->cur_pos; + return 1; + } + return 0; +} + +/* exported */ +seq_sync_parser_t snd_seq_midi_clock_parser = { + format: SNDRV_SEQ_SYNC_FMT_MIDI_CLOCK, + in: { + open: midi_open, + sync: midi_sync_in, + }, + out: { + open: midi_open, + sync: midi_sync_out, + }, +}; + +#endif /* SNDRV_SEQ_SYNC_SUPPORT */ diff -Nru a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_midi_emul.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,739 @@ +/* + * GM/GS/XG midi module. + * + * Copyright (C) 1999 Steve Ratcliffe + * + * Based on awe_wave.c by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* + * This module is used to keep track of the current midi state. + * It can be used for drivers that are required to emulate midi when + * the hardware doesn't. + * + * It was written for a AWE64 driver, but there should be no AWE specific + * code in here. If there is it should be reported as a bug. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai / Steve Ratcliffe"); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +/* Prototypes for static functions */ +static void note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel); +static void do_control(snd_midi_op_t *ops, void *private, + snd_midi_channel_set_t *chset, snd_midi_channel_t *chan, + int control, int value); +static void rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); +static void nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); +static void sysex(snd_midi_op_t *ops, void *private, unsigned char *sysex, int len, snd_midi_channel_set_t *chset); +static void all_sounds_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan); +static void all_notes_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan); +void snd_midi_reset_controllers(snd_midi_channel_t *chan); +static void reset_all_channels(snd_midi_channel_set_t *chset); + + +/* + * Process an event in a driver independant way. This means dealing + * with RPN, NRPN, SysEx etc that are defined for common midi applications + * such as GM, GS and XG. + * There modes that this module will run in are: + * Generic MIDI - no interpretation at all, it will just save current values + * of controlers etc. + * GM - You can use all gm_ prefixed elements of chan. Controls, RPN, NRPN, + * SysEx will be interpreded as defined in General Midi. + * GS - You can use all gs_ prefixed elements of chan. Codes for GS will be + * interpreted. + * XG - You can use all xg_ prefixed elements of chan. Codes for XG will + * be interpreted. + */ +void +snd_midi_process_event(snd_midi_op_t *ops, + snd_seq_event_t *ev, snd_midi_channel_set_t *chanset) +{ + snd_midi_channel_t *chan; + void *drv; + int dest_channel = 0; + + if (ev == NULL || chanset == NULL) { + snd_printd("ev or chanbase NULL (snd_midi_process_event)\n"); + return; + } + if (chanset->channels == NULL) + return; + + if (snd_seq_ev_is_channel_type(ev)) { + dest_channel = ev->data.note.channel; + if (dest_channel >= chanset->max_channels) { + snd_printd("dest channel is %d, max is %d\n", dest_channel, chanset->max_channels); + return; + } + } + + chan = chanset->channels + dest_channel; + drv = chanset->private_data; + + /* EVENT_NOTE should be processed before queued */ + if (ev->type == SNDRV_SEQ_EVENT_NOTE) + return; + + /* Make sure that we don't have a note on that should really be + * a note off */ + if (ev->type == SNDRV_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0) + ev->type = SNDRV_SEQ_EVENT_NOTEOFF; + + /* Make sure the note is within array range */ + if (ev->type == SNDRV_SEQ_EVENT_NOTEON || + ev->type == SNDRV_SEQ_EVENT_NOTEOFF || + ev->type == SNDRV_SEQ_EVENT_KEYPRESS) { + if (ev->data.note.note >= 128) + return; + } + + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEON: + if (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON) { + if (ops->note_off) + ops->note_off(drv, ev->data.note.note, 0, chan); + } + chan->note[ev->data.note.note] = SNDRV_MIDI_NOTE_ON; + if (ops->note_on) + ops->note_on(drv, ev->data.note.note, ev->data.note.velocity, chan); + break; + case SNDRV_SEQ_EVENT_NOTEOFF: + if (! (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON)) + break; + if (ops->note_off) + note_off(ops, drv, chan, ev->data.note.note, ev->data.note.velocity); + break; + case SNDRV_SEQ_EVENT_KEYPRESS: + if (ops->key_press) + ops->key_press(drv, ev->data.note.note, ev->data.note.velocity, chan); + break; + case SNDRV_SEQ_EVENT_CONTROLLER: + do_control(ops, drv, chanset, chan, + ev->data.control.param, ev->data.control.value); + break; + case SNDRV_SEQ_EVENT_PGMCHANGE: + chan->midi_program = ev->data.control.value; + break; + case SNDRV_SEQ_EVENT_PITCHBEND: + chan->midi_pitchbend = ev->data.control.value; + if (ops->control) + ops->control(drv, MIDI_CTL_PITCHBEND, chan); + break; + case SNDRV_SEQ_EVENT_CHANPRESS: + chan->midi_pressure = ev->data.control.value; + if (ops->control) + ops->control(drv, MIDI_CTL_CHAN_PRESSURE, chan); + break; + case SNDRV_SEQ_EVENT_CONTROL14: + /* Best guess is that this is any of the 14 bit controller values */ + if (ev->data.control.param < 32) { + /* set low part first */ + chan->control[ev->data.control.param + 32] = + ev->data.control.value & 0x7f; + do_control(ops, drv, chanset, chan, + ev->data.control.param, + ((ev->data.control.value>>7) & 0x7f)); + } else + do_control(ops, drv, chanset, chan, + ev->data.control.param, + ev->data.control.value); + break; + case SNDRV_SEQ_EVENT_NONREGPARAM: + /* Break it back into its controler values */ + chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED; + chan->control[MIDI_CTL_MSB_DATA_ENTRY] + = (ev->data.control.value >> 7) & 0x7f; + chan->control[MIDI_CTL_LSB_DATA_ENTRY] + = ev->data.control.value & 0x7f; + chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] + = (ev->data.control.param >> 7) & 0x7f; + chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] + = ev->data.control.param & 0x7f; + nrpn(ops, drv, chan, chanset); + break; + case SNDRV_SEQ_EVENT_REGPARAM: + /* Break it back into its controler values */ + chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED; + chan->control[MIDI_CTL_MSB_DATA_ENTRY] + = (ev->data.control.value >> 7) & 0x7f; + chan->control[MIDI_CTL_LSB_DATA_ENTRY] + = ev->data.control.value & 0x7f; + chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] + = (ev->data.control.param >> 7) & 0x7f; + chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB] + = ev->data.control.param & 0x7f; + rpn(ops, drv, chan, chanset); + break; + case SNDRV_SEQ_EVENT_SYSEX: + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { + unsigned char sysexbuf[64]; + int len; + len = snd_seq_expand_var_event(ev, sizeof(sysexbuf), sysexbuf, 1, 0); + if (len > 0) + sysex(ops, drv, sysexbuf, len, chanset); + } + break; + case SNDRV_SEQ_EVENT_SONGPOS: + case SNDRV_SEQ_EVENT_SONGSEL: + case SNDRV_SEQ_EVENT_CLOCK: + case SNDRV_SEQ_EVENT_START: + case SNDRV_SEQ_EVENT_CONTINUE: + case SNDRV_SEQ_EVENT_STOP: + case SNDRV_SEQ_EVENT_QFRAME: + case SNDRV_SEQ_EVENT_TEMPO: + case SNDRV_SEQ_EVENT_TIMESIGN: + case SNDRV_SEQ_EVENT_KEYSIGN: + goto not_yet; + case SNDRV_SEQ_EVENT_SENSING: + break; + case SNDRV_SEQ_EVENT_CLIENT_START: + case SNDRV_SEQ_EVENT_CLIENT_EXIT: + case SNDRV_SEQ_EVENT_CLIENT_CHANGE: + case SNDRV_SEQ_EVENT_PORT_START: + case SNDRV_SEQ_EVENT_PORT_EXIT: + case SNDRV_SEQ_EVENT_PORT_CHANGE: + case SNDRV_SEQ_EVENT_SAMPLE: + case SNDRV_SEQ_EVENT_SAMPLE_START: + case SNDRV_SEQ_EVENT_SAMPLE_STOP: + case SNDRV_SEQ_EVENT_SAMPLE_FREQ: + case SNDRV_SEQ_EVENT_SAMPLE_VOLUME: + case SNDRV_SEQ_EVENT_SAMPLE_LOOP: + case SNDRV_SEQ_EVENT_SAMPLE_POSITION: + case SNDRV_SEQ_EVENT_ECHO: + not_yet: + default: + /*snd_printd("Unimplemented event %d\n", ev->type);*/ + break; + } +} + + +/* + * release note + */ +static void +note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel) +{ + if (chan->gm_hold) { + /* Hold this note until pedal is turned off */ + chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED; + } else if (chan->note[note] & SNDRV_MIDI_NOTE_SUSTENUTO) { + /* Mark this note as release; it will be turned off when sustenuto + * is turned off */ + chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED; + } else { + chan->note[note] = 0; + if (ops->note_off) + ops->note_off(drv, note, vel, chan); + } +} + +/* + * Do all driver independant operations for this controler and pass + * events that need to take place immediately to the driver. + */ +static void +do_control(snd_midi_op_t *ops, void *drv, snd_midi_channel_set_t *chset, + snd_midi_channel_t *chan, int control, int value) +{ + int i; + + /* Switches */ + if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) { + /* These are all switches; either off or on so set to 0 or 127 */ + value = (value >= 64)? 127: 0; + } + chan->control[control] = value; + + switch (control) { + case MIDI_CTL_SUSTAIN: + if (value == 0) { + /* Sustain has been released, turn off held notes */ + for (i = 0; i < 128; i++) { + if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) { + chan->note[i] = SNDRV_MIDI_NOTE_OFF; + if (ops->note_off) + ops->note_off(drv, i, 0, chan); + } + } + } + break; + case MIDI_CTL_PORTAMENTO: + break; + case MIDI_CTL_SUSTENUTO: + if (value) { + /* Mark each note that is currently held down */ + for (i = 0; i < 128; i++) { + if (chan->note[i] & SNDRV_MIDI_NOTE_ON) + chan->note[i] |= SNDRV_MIDI_NOTE_SUSTENUTO; + } + } else { + /* release all notes that were held */ + for (i = 0; i < 128; i++) { + if (chan->note[i] & SNDRV_MIDI_NOTE_SUSTENUTO) { + chan->note[i] &= ~SNDRV_MIDI_NOTE_SUSTENUTO; + if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) { + chan->note[i] = SNDRV_MIDI_NOTE_OFF; + if (ops->note_off) + ops->note_off(drv, i, 0, chan); + } + } + } + } + break; + case MIDI_CTL_MSB_DATA_ENTRY: + chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0; + /* go through here */ + case MIDI_CTL_LSB_DATA_ENTRY: + if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED) + rpn(ops, drv, chan, chset); + else + nrpn(ops, drv, chan, chset); + break; + case MIDI_CTL_REGIST_PARM_NUM_LSB: + case MIDI_CTL_REGIST_PARM_NUM_MSB: + chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED; + break; + case MIDI_CTL_NONREG_PARM_NUM_LSB: + case MIDI_CTL_NONREG_PARM_NUM_MSB: + chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED; + break; + + case MIDI_CTL_ALL_SOUNDS_OFF: + all_sounds_off(ops, drv, chan); + break; + + case MIDI_CTL_ALL_NOTES_OFF: + all_notes_off(ops, drv, chan); + break; + + case MIDI_CTL_MSB_BANK: + if (chset->midi_mode == SNDRV_MIDI_MODE_XG) { + if (value == 127) + chan->drum_channel = 1; + else + chan->drum_channel = 0; + } + break; + case MIDI_CTL_LSB_BANK: + break; + + case MIDI_CTL_RESET_CONTROLLERS: + snd_midi_reset_controllers(chan); + break; + + case MIDI_CTL_SOFT_PEDAL: + case MIDI_CTL_LEGATO_FOOTSWITCH: + case MIDI_CTL_HOLD2: + case MIDI_CTL_SC1_SOUND_VARIATION: + case MIDI_CTL_SC2_TIMBRE: + case MIDI_CTL_SC3_RELEASE_TIME: + case MIDI_CTL_SC4_ATTACK_TIME: + case MIDI_CTL_SC5_BRIGHTNESS: + case MIDI_CTL_E1_REVERB_DEPTH: + case MIDI_CTL_E2_TREMOLO_DEPTH: + case MIDI_CTL_E3_CHORUS_DEPTH: + case MIDI_CTL_E4_DETUNE_DEPTH: + case MIDI_CTL_E5_PHASER_DEPTH: + goto notyet; + notyet: + default: + if (ops->control) + ops->control(drv, control, chan); + break; + } +} + + +/* + * intialize the MIDI status + */ +void +snd_midi_channel_set_clear(snd_midi_channel_set_t *chset) +{ + int i; + + chset->midi_mode = SNDRV_MIDI_MODE_GM; + chset->gs_master_volume = 127; + + for (i = 0; i < chset->max_channels; i++) { + snd_midi_channel_t *chan = chset->channels + i; + memset(chan->note, 0, sizeof(chan->note)); + + chan->midi_aftertouch = 0; + chan->midi_pressure = 0; + chan->midi_program = 0; + chan->midi_pitchbend = 0; + snd_midi_reset_controllers(chan); + chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */ + chan->gm_rpn_fine_tuning = 0; + chan->gm_rpn_coarse_tuning = 0; + + if (i == 9) + chan->drum_channel = 1; + else + chan->drum_channel = 0; + } +} + +/* + * Process a rpn message. + */ +static void +rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, + snd_midi_channel_set_t *chset) +{ + int type; + int val; + + if (chset->midi_mode != SNDRV_MIDI_MODE_NONE) { + type = (chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] << 8) | + chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB]; + val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) | + chan->control[MIDI_CTL_LSB_DATA_ENTRY]; + + switch (type) { + case 0x0000: /* Pitch bend sensitivity */ + /* MSB only / 1 semitone per 128 */ + chan->gm_rpn_pitch_bend_range = val; + break; + + case 0x0001: /* fine tuning: */ + /* MSB/LSB, 8192=center, 100/8192 cent step */ + chan->gm_rpn_fine_tuning = val - 8192; + break; + + case 0x0002: /* coarse tuning */ + /* MSB only / 8192=center, 1 semitone per 128 */ + chan->gm_rpn_coarse_tuning = val - 8192; + break; + + case 0x7F7F: /* "lock-in" RPN */ + /* ignored */ + break; + } + } + /* should call nrpn or rpn callback here.. */ +} + +/* + * Process an nrpn message. + */ +static void +nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, + snd_midi_channel_set_t *chset) +{ + /* parse XG NRPNs here if possible */ + if (ops->nrpn) + ops->nrpn(drv, chan, chset); +} + + +/* + * convert channel parameter in GS sysex + */ +static int +get_channel(unsigned char cmd) +{ + int p = cmd & 0x0f; + if (p == 0) + p = 9; + else if (p < 10) + p--; + return p; +} + + +/* + * Process a sysex message. + */ +static void +sysex(snd_midi_op_t *ops, void *private, unsigned char *buf, int len, snd_midi_channel_set_t *chset) +{ + /* GM on */ + static unsigned char gm_on_macro[] = { + 0x7e,0x7f,0x09,0x01, + }; + /* XG on */ + static unsigned char xg_on_macro[] = { + 0x43,0x10,0x4c,0x00,0x00,0x7e,0x00, + }; + /* GS prefix + * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off + * reverb mode: XX=0x01, YY=0x30, ZZ=0-7 + * chorus mode: XX=0x01, YY=0x38, ZZ=0-7 + * master vol: XX=0x00, YY=0x04, ZZ=0-127 + */ + static unsigned char gs_pfx_macro[] = { + 0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/ + }; + + int parsed = SNDRV_MIDI_SYSEX_NOT_PARSED; + + if (len <= 0 || buf[0] != 0xf0) + return; + /* skip first byte */ + buf++; + len--; + + /* GM on */ + if (len >= sizeof(gm_on_macro) && + memcmp(buf, gm_on_macro, sizeof(gm_on_macro)) == 0) { + if (chset->midi_mode != SNDRV_MIDI_MODE_GS && + chset->midi_mode != SNDRV_MIDI_MODE_XG) { + chset->midi_mode = SNDRV_MIDI_MODE_GM; + reset_all_channels(chset); + parsed = SNDRV_MIDI_SYSEX_GM_ON; + } + } + + /* GS macros */ + else if (len >= 8 && + memcmp(buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) { + if (chset->midi_mode != SNDRV_MIDI_MODE_GS && + chset->midi_mode != SNDRV_MIDI_MODE_XG) + chset->midi_mode = SNDRV_MIDI_MODE_GS; + + if (buf[5] == 0x00 && buf[6] == 0x7f && buf[7] == 0x00) { + /* GS reset */ + parsed = SNDRV_MIDI_SYSEX_GS_RESET; + reset_all_channels(chset); + } + + else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x15) { + /* drum pattern */ + int p = get_channel(buf[5]); + if (p < chset->max_channels) { + parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL; + if (buf[7]) + chset->channels[p].drum_channel = 1; + else + chset->channels[p].drum_channel = 0; + } + + } else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x21) { + /* program */ + int p = get_channel(buf[5]); + if (p < chset->max_channels && + ! chset->channels[p].drum_channel) { + parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL; + chset->channels[p].midi_program = buf[7]; + } + + } else if (buf[5] == 0x01 && buf[6] == 0x30) { + /* reverb mode */ + parsed = SNDRV_MIDI_SYSEX_GS_CHORUS_MODE; + chset->gs_reverb_mode = buf[7]; + + } else if (buf[5] == 0x01 && buf[6] == 0x38) { + /* chorus mode */ + parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE; + chset->gs_chorus_mode = buf[7]; + + } else if (buf[5] == 0x00 && buf[6] == 0x04) { + /* master volume */ + parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE; + chset->gs_master_volume = buf[7]; + + } + } + + /* XG on */ + else if (len >= sizeof(xg_on_macro) && + memcmp(buf, xg_on_macro, sizeof(xg_on_macro)) == 0) { + int i; + chset->midi_mode = SNDRV_MIDI_MODE_XG; + parsed = SNDRV_MIDI_SYSEX_XG_ON; + /* reset CC#0 for drums */ + for (i = 0; i < chset->max_channels; i++) { + if (chset->channels[i].drum_channel) + chset->channels[i].control[MIDI_CTL_MSB_BANK] = 127; + else + chset->channels[i].control[MIDI_CTL_MSB_BANK] = 0; + } + } + + if (ops->sysex) + ops->sysex(private, buf - 1, len + 1, parsed, chset); +} + +/* + * all sound off + */ +static void +all_sounds_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan) +{ + int n; + + if (! ops->note_terminate) + return; + for (n = 0; n < 128; n++) { + if (chan->note[n]) { + ops->note_terminate(drv, n, chan); + chan->note[n] = 0; + } + } +} + +/* + * all notes off + */ +static void +all_notes_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan) +{ + int n; + + if (! ops->note_off) + return; + for (n = 0; n < 128; n++) { + if (chan->note[n] == SNDRV_MIDI_NOTE_ON) + note_off(ops, drv, chan, n, 0); + } +} + +/* + * Initialise a single midi channel control block. + */ +void snd_midi_channel_init(snd_midi_channel_t *p, int n) +{ + if (p == NULL) + return; + + memset(p, 0, sizeof(snd_midi_channel_t)); + p->private = NULL; + p->number = n; + + snd_midi_reset_controllers(p); + p->gm_rpn_pitch_bend_range = 256; /* 2 semitones */ + p->gm_rpn_fine_tuning = 0; + p->gm_rpn_coarse_tuning = 0; + + if (n == 9) + p->drum_channel = 1; /* Default ch 10 as drums */ +} + +/* + * Allocate and initialise a set of midi channel control blocks. + */ +snd_midi_channel_t *snd_midi_channel_init_set(int n) +{ + snd_midi_channel_t *chan; + int i; + + chan = kmalloc(n * sizeof(snd_midi_channel_t), GFP_KERNEL); + if (chan) { + for (i = 0; i < n; i++) + snd_midi_channel_init(chan+i, i); + } + + return chan; +} + +/* + * reset all midi channels + */ +static void +reset_all_channels(snd_midi_channel_set_t *chset) +{ + int ch; + for (ch = 0; ch < chset->max_channels; ch++) { + snd_midi_channel_t *chan = chset->channels + ch; + snd_midi_reset_controllers(chan); + chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */ + chan->gm_rpn_fine_tuning = 0; + chan->gm_rpn_coarse_tuning = 0; + + if (ch == 9) + chan->drum_channel = 1; + else + chan->drum_channel = 0; + } +} + + +/* + * Allocate and initialise a midi channel set. + */ +snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n) +{ + snd_midi_channel_set_t *chset; + + chset = kmalloc(sizeof(*chset), GFP_KERNEL); + if (chset) { + chset->channels = snd_midi_channel_init_set(n); + chset->private_data = NULL; + chset->max_channels = n; + } + return chset; +} + +/* + * Reset the midi controllers on a particular channel to default values. + */ +void snd_midi_reset_controllers(snd_midi_channel_t *chan) +{ + memset(chan->control, 0, sizeof(chan->control)); + chan->gm_volume = 127; + chan->gm_expression = 127; + chan->gm_pan = 64; +} + + +/* + * Free a midi channel set. + */ +void snd_midi_channel_free_set(snd_midi_channel_set_t *chset) +{ + if (chset == NULL) + return; + if (chset->channels != NULL) + kfree(chset->channels); + kfree(chset); +} + +static int __init alsa_seq_midi_emul_init(void) +{ + return 0; +} + +static void __exit alsa_seq_midi_emul_exit(void) +{ +} + +module_init(alsa_seq_midi_emul_init) +module_exit(alsa_seq_midi_emul_exit) + +EXPORT_SYMBOL(snd_midi_process_event); +EXPORT_SYMBOL(snd_midi_channel_set_clear); +EXPORT_SYMBOL(snd_midi_channel_init); +EXPORT_SYMBOL(snd_midi_channel_init_set); +EXPORT_SYMBOL(snd_midi_channel_alloc_set); +EXPORT_SYMBOL(snd_midi_channel_free_set); diff -Nru a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_midi_event.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,468 @@ +/* + * MIDI byte <-> sequencer event coder + * + * Copyright (C) 1998,99 Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai , Jaroslav Kysela "); +MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder"); +MODULE_LICENSE("GPL"); + +/* queue type */ +/* from 0 to 7 are normal commands (note off, on, etc.) */ +#define ST_NOTEOFF 0 +#define ST_NOTEON 1 +#define ST_SPECIAL 8 +#define ST_SYSEX ST_SPECIAL +/* from 8 to 15 are events for 0xf0-0xf7 */ + + +/* status event types */ +typedef void (*event_encode_t)(snd_midi_event_t *dev, snd_seq_event_t *ev); +typedef void (*event_decode_t)(snd_seq_event_t *ev, unsigned char *buf); + +/* + * prototypes + */ +static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void note_decode(snd_seq_event_t *ev, unsigned char *buf); +static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf); +static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf); +static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf); +static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf); + +/* + * event list + */ +static struct status_event_list_t { + int event; + int qlen; + event_encode_t encode; + event_decode_t decode; +} status_event[] = { + /* 0x80 - 0xf0 */ + {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode}, + {SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode}, + {SNDRV_SEQ_EVENT_CHANPRESS, 1, one_param_ctrl_event, one_param_decode}, + {SNDRV_SEQ_EVENT_PITCHBEND, 2, pitchbend_ctrl_event, pitchbend_decode}, + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf0 */ + /* 0xf0 - 0xff */ + {SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */ + {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */ + {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */ + {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf4 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf5 */ + {SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf7 */ + {SNDRV_SEQ_EVENT_CLOCK, 0, NULL, NULL}, /* 0xf8 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf9 */ + {SNDRV_SEQ_EVENT_START, 0, NULL, NULL}, /* 0xfa */ + {SNDRV_SEQ_EVENT_CONTINUE, 0, NULL, NULL}, /* 0xfb */ + {SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xfd */ + {SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */ + {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */ +}; + +static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev); + +static struct extra_event_list_t { + int event; + int (*decode)(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev); +} extra_event[] = { + {SNDRV_SEQ_EVENT_CONTROL14, extra_decode_ctrl14}, + /*{SNDRV_SEQ_EVENT_NONREGPARAM, extra_decode_nrpn},*/ + /*{SNDRV_SEQ_EVENT_REGPARAM, extra_decode_rpn},*/ +}; + +#define numberof(ary) (sizeof(ary)/sizeof(ary[0])) + +/* + * new/delete record + */ + +int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev) +{ + snd_midi_event_t *dev; + + *rdev = NULL; + dev = (snd_midi_event_t *)snd_kcalloc(sizeof(snd_midi_event_t), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + if (bufsize > 0) { + dev->buf = kmalloc(bufsize, GFP_KERNEL); + if (dev->buf == NULL) { + kfree(dev); + return -ENOMEM; + } + } + dev->bufsize = bufsize; + spin_lock_init(&dev->lock); + *rdev = dev; + return 0; +} + +void snd_midi_event_free(snd_midi_event_t *dev) +{ + if (dev != NULL) { + if (dev->buf) + kfree(dev->buf); + kfree(dev); + } +} + +/* + * initialize record + */ +inline static void reset_encode(snd_midi_event_t *dev) +{ + dev->read = 0; + dev->qlen = 0; + dev->type = 0; +} + +void snd_midi_event_reset_encode(snd_midi_event_t *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + reset_encode(dev); + spin_unlock_irqrestore(&dev->lock, flags); +} + +void snd_midi_event_reset_decode(snd_midi_event_t *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + dev->lastcmd = 0xff; + spin_unlock_irqrestore(&dev->lock, flags); +} + +void snd_midi_event_init(snd_midi_event_t *dev) +{ + snd_midi_event_reset_encode(dev); + snd_midi_event_reset_decode(dev); +} + +/* + * resize buffer + */ +int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize) +{ + unsigned char *new_buf, *old_buf; + unsigned long flags; + + if (bufsize == dev->bufsize) + return 0; + new_buf = kmalloc(bufsize, GFP_KERNEL); + if (new_buf == NULL) + return -ENOMEM; + spin_lock_irqsave(&dev->lock, flags); + old_buf = dev->buf; + dev->buf = new_buf; + dev->bufsize = bufsize; + reset_encode(dev); + spin_unlock_irqrestore(&dev->lock, flags); + if (old_buf) + kfree(old_buf); + return 0; +} + +/* + * read bytes and encode to sequencer event if finished + * return the size of encoded bytes + */ +long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev) +{ + long result = 0; + int rc; + + ev->type = SNDRV_SEQ_EVENT_NONE; + + while (count-- > 0) { + rc = snd_midi_event_encode_byte(dev, *buf++, ev); + result++; + if (rc < 0) + return rc; + else if (rc > 0) + return result; + } + + return result; +} + +/* + * read one byte and encode to sequencer event: + * return 1 if MIDI bytes are encoded to an event + * 0 data is not finished + * negative for error + */ +int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev) +{ + int rc = 0; + unsigned long flags; + + c &= 0xff; + + if (c >= MIDI_CMD_COMMON_CLOCK) { + /* real-time event */ + ev->type = status_event[ST_SPECIAL + c - 0xf0].event; + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; + return 1; + } + + spin_lock_irqsave(&dev->lock, flags); + if (dev->qlen > 0) { + /* rest of command */ + dev->buf[dev->read++] = c; + if (dev->type != ST_SYSEX) + dev->qlen--; + } else { + /* new command */ + dev->read = 1; + if (c & 0x80) { + dev->buf[0] = c; + if ((c & 0xf0) == 0xf0) /* special events */ + dev->type = (c & 0x0f) + ST_SPECIAL; + else + dev->type = (c >> 4) & 0x07; + dev->qlen = status_event[dev->type].qlen; + } else { + /* process this byte as argument */ + dev->buf[dev->read++] = c; + dev->qlen = status_event[dev->type].qlen - 1; + } + } + if (dev->qlen == 0) { + ev->type = status_event[dev->type].event; + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; + if (status_event[dev->type].encode) /* set data values */ + status_event[dev->type].encode(dev, ev); + rc = 1; + } else if (dev->type == ST_SYSEX) { + if (c == MIDI_CMD_COMMON_SYSEX_END || + dev->read >= dev->bufsize) { + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + ev->type = SNDRV_SEQ_EVENT_SYSEX; + ev->data.ext.len = dev->read; + ev->data.ext.ptr = dev->buf; + if (c != MIDI_CMD_COMMON_SYSEX_END) + dev->read = 0; /* continue to parse */ + else + reset_encode(dev); /* all parsed */ + rc = 1; + } + } + + spin_unlock_irqrestore(&dev->lock, flags); + return rc; +} + +/* encode note event */ +static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.note.channel = dev->buf[0] & 0x0f; + ev->data.note.note = dev->buf[1]; + ev->data.note.velocity = dev->buf[2]; +} + +/* encode one parameter controls */ +static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.channel = dev->buf[0] & 0x0f; + ev->data.control.value = dev->buf[1]; +} + +/* encode pitch wheel change */ +static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.channel = dev->buf[0] & 0x0f; + ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1] - 8192; +} + +/* encode midi control change */ +static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.channel = dev->buf[0] & 0x0f; + ev->data.control.param = dev->buf[1]; + ev->data.control.value = dev->buf[2]; +} + +/* encode one parameter value*/ +static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.value = dev->buf[1]; +} + +/* encode song position */ +static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1]; +} + +/* + * decode from a sequencer event to midi bytes + * return the size of decoded midi events + */ +long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev) +{ + int cmd, type; + + if (ev->type == SNDRV_SEQ_EVENT_NONE) + return -ENOENT; + + for (type = 0; type < numberof(status_event); type++) { + if (ev->type == status_event[type].event) + goto __found; + } + for (type = 0; type < numberof(extra_event); type++) { + if (ev->type == extra_event[type].event) + return extra_event[type].decode(dev, buf, count, ev); + } + return -ENOENT; + + __found: + if (type >= ST_SPECIAL) + cmd = 0xf0 + (type - ST_SPECIAL); + else + /* data.note.channel and data.control.channel is identical */ + cmd = 0x80 | (type << 4) | (ev->data.note.channel & 0x0f); + + + if (cmd == MIDI_CMD_COMMON_SYSEX) { + snd_midi_event_reset_decode(dev); + return snd_seq_expand_var_event(ev, count, buf, 1, 0); + } else { + int qlen; + unsigned char xbuf[4]; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd) { + dev->lastcmd = cmd; + spin_unlock_irqrestore(&dev->lock, flags); + xbuf[0] = cmd; + if (status_event[type].decode) + status_event[type].decode(ev, xbuf + 1); + qlen = status_event[type].qlen + 1; + } else { + spin_unlock_irqrestore(&dev->lock, flags); + if (status_event[type].decode) + status_event[type].decode(ev, xbuf + 0); + qlen = status_event[type].qlen; + } + if (count < qlen) + return -ENOMEM; + memcpy(buf, xbuf, qlen); + return qlen; + } +} + + +/* decode note event */ +static void note_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.note.note & 0x7f; + buf[1] = ev->data.note.velocity & 0x7f; +} + +/* decode one parameter controls */ +static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.control.value & 0x7f; +} + +/* decode pitch wheel change */ +static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + int value = ev->data.control.value + 8192; + buf[0] = value & 0x7f; + buf[1] = (value >> 7) & 0x7f; +} + +/* decode midi control change */ +static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.control.param & 0x7f; + buf[1] = ev->data.control.value & 0x7f; +} + +/* decode song position */ +static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.control.value & 0x7f; + buf[1] = (ev->data.control.value >> 7) & 0x7f; +} + +/* decode 14bit control */ +static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev) +{ + if (ev->data.control.param < 32) { + if (count < 5) + return -ENOMEM; + buf[0] = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f); + buf[1] = ev->data.control.param; + buf[2] = (ev->data.control.value >> 7) & 0x7f; + buf[3] = ev->data.control.param + 32; + buf[4] = ev->data.control.value & 0x7f; + dev->lastcmd = buf[0]; + return 5; + } else { + if (count < 3) + return -ENOMEM; + buf[0] = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f); + buf[1] = ev->data.control.param & 0x7f; + buf[4] = ev->data.control.value & 0x7f; + dev->lastcmd = buf[0]; + return 3; + } +} + +/* + * exports + */ + +EXPORT_SYMBOL(snd_midi_event_new); +EXPORT_SYMBOL(snd_midi_event_free); +EXPORT_SYMBOL(snd_midi_event_resize_buffer); +EXPORT_SYMBOL(snd_midi_event_init); +EXPORT_SYMBOL(snd_midi_event_reset_encode); +EXPORT_SYMBOL(snd_midi_event_reset_decode); +EXPORT_SYMBOL(snd_midi_event_encode); +EXPORT_SYMBOL(snd_midi_event_encode_byte); +EXPORT_SYMBOL(snd_midi_event_decode); diff -Nru a/sound/core/seq/seq_mtc.c b/sound/core/seq/seq_mtc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_mtc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,252 @@ +/* + * MTC event converter + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include "seq_queue.h" + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + +typedef struct mtc_out { + int out_channel; + unsigned int time_format; + sndrv_seq_time_frame_t cur_time; + unsigned int decode_offset; + unsigned char sysex[10]; +} mtc_out_t; + +typedef struct mtc_in { + unsigned int time_format; + sndrv_seq_time_frame_t cur_time; + unsigned int cur_pos; + int prev_in_offset; +} mtc_in_t; + + +static int mtc_open_out(queue_sync_t *sync_info, seq_sync_arg_t *retp) +{ + mtc_out_t *arg; + + if (sync_info->time_format >= 4) + return -EINVAL; + if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL) + return -ENOMEM; + arg->out_channel = sync_info->opt_info[0]; + if (arg->out_channel == 0) + arg->out_channel = 127; + arg->time_format = sync_info->time_format; + sync_info->param.time.subframes = 4; /* MTC uses quarter frame */ + sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format); + memset(&arg->cur_time, 0, sizeof(arg->cur_time)); + *retp = arg; + return 0; +} + +static int mtc_open_in(queue_sync_t *sync_info, seq_sync_arg_t *retp) +{ + mtc_in_t *arg; + + if (sync_info->time_format >= 4) + return -EINVAL; + if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL) + return -ENOMEM; + arg->time_format = sync_info->time_format; + memset(&arg->cur_time, 0, sizeof(arg->cur_time)); + arg->cur_pos = 0; + arg->prev_in_offset = -1; + sync_info->param.time.subframes = 4; /* MTC uses quarter frame */ + sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format); + *retp = arg; + return 0; +} + + +/* decode sync signal */ +static int sync_out(mtc_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + int val, offset; + + if (arg->time_format != src->data.queue.sync_time_format) + return -EINVAL; + offset = (src->data.queue.param.position + arg->decode_offset) % 8; + if (offset == 0) { + /* convert and remember the current time + for the following 7 MTC quarter frames */ + arg->cur_time = snd_seq_position_to_time_frame(arg->time_format, 4, src->data.queue.param.position); + } + switch (offset) { + case 0: val = arg->cur_time.frame & 0x0f; break; + case 1: val = (arg->cur_time.frame >> 4) & 0x0f; break; + case 2: val = arg->cur_time.sec & 0x0f; break; + case 3: val = (arg->cur_time.sec >> 4) & 0x0f; break; + case 4: val = arg->cur_time.min & 0x0f; break; + case 5: val = (arg->cur_time.min >> 4) & 0x0f; break; + case 6: val = arg->cur_time.hour & 0x0f; break; + case 7: + default: + val = ((arg->cur_time.hour >> 4) & 0x01) | (arg->time_format << 1); + break; + } + val |= (offset << 4); + ev->type = SNDRV_SEQ_EVENT_QFRAME; + ev->data.control.value = val; + return 1; +} + +/* decode sync position */ +static int sync_pos_out(mtc_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + unsigned int pos; + unsigned char *buf = arg->sysex; + + if (arg->time_format != src->data.queue.sync_time_format) + return -EINVAL; + + pos = src->data.queue.param.position; /* quarter frames */ + arg->decode_offset = pos & 4; + pos /= 4; + arg->cur_time = snd_seq_position_to_time_frame(arg->time_format, 4, pos); + + buf[0] = 0xf0; /* SYSEX */ + buf[1] = 0x7f; + buf[2] = arg->out_channel; + buf[3] = 0x01; + buf[4] = 0x01; + buf[5] = arg->cur_time.hour | (arg->time_format << 5); + buf[6] = arg->cur_time.min; + buf[7] = arg->cur_time.sec; + buf[8] = arg->cur_time.frame; + buf[9] = 0xf7; + + ev->type = SNDRV_SEQ_EVENT_SYSEX; + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + ev->data.ext.len = 10; + ev->data.ext.ptr = buf; + + return 1; +} + +static int mtc_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + mtc_out_t *arg = _arg; + switch (src->type) { + case SNDRV_SEQ_EVENT_SYNC: + return sync_out(arg, src, ev); + case SNDRV_SEQ_EVENT_SYNC_POS: + return sync_pos_out(arg, src, ev); + } + return 0; +} + +/* decode sync signal */ +static int sync_in(mtc_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + int val, offset; + unsigned int time_format; + + offset = (src->data.control.value & 0x70) >> 4; + val = src->data.control.value & 0x0f; + if (offset > 0 && offset != arg->prev_in_offset + 1) { + /* bad quarter frame message - something missing.. */ + arg->prev_in_offset = -1; /* wait for next 0 */ + return -EINVAL; + } + switch (offset) { + case 0: arg->cur_time.frame = val; break; + case 1: arg->cur_time.frame |= (val << 4); break; + case 2: arg->cur_time.sec = val; break; + case 3: arg->cur_time.sec |= (val << 4); break; + case 4: arg->cur_time.min = val; break; + case 5: arg->cur_time.min |= (val << 4); break; + case 6: arg->cur_time.hour = val; break; + case 7: + default: + arg->cur_time.hour |= (val & 1) << 4; + time_format = (val >> 1) & 3; + if (time_format != arg->time_format) + return -EINVAL; + arg->cur_pos = snd_seq_time_frame_to_position(time_format, 4, &arg->cur_time); + arg->cur_pos += 7; /* correct the receive time */ + break; + } + + ev->type = SNDRV_SEQ_EVENT_SYNC; + ev->data.queue.sync_time_format = arg->time_format; + ev->data.queue.param.position = arg->cur_pos; + arg->cur_pos++; + + return 1; /* composed */ +} + +/* decode sync position */ +static int sync_pos_in(mtc_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + unsigned time_format; + char buf[10]; + + if (snd_seq_expand_var_event(src, 10, buf, 1, 0) != 10) + return 0; + if (buf[1] != 0x7f || buf[3] != 0x01 || buf[4] != 0x01) + return 0; + time_format = (buf[5] >> 5) & 3; + if (time_format != arg->time_format) + return -EINVAL; + arg->cur_time.hour = buf[5] & 0x1f; + arg->cur_time.min = buf[6]; + arg->cur_time.sec = buf[7]; + arg->cur_time.frame = buf[8]; + arg->cur_pos = snd_seq_time_frame_to_position(time_format, 4, &arg->cur_time); + + ev->type = SNDRV_SEQ_EVENT_SYNC_POS; + ev->data.queue.sync_time_format = time_format; + ev->data.queue.param.position = arg->cur_pos; + + return 1; /* composed */ +} + +static int mtc_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + mtc_in_t *arg = _arg; + switch (src->type) { + case SNDRV_SEQ_EVENT_QFRAME: + return sync_in(arg, src, ev); + case SNDRV_SEQ_EVENT_SYSEX: + return sync_pos_in(arg, src, ev); + } + return 0; +} + +/* exported */ +seq_sync_parser_t snd_seq_mtc_parser = { + format: SNDRV_SEQ_SYNC_FMT_MTC, + in: { + open: mtc_open_in, + sync: mtc_sync_in, + }, + out: { + open: mtc_open_out, + sync: mtc_sync_out, + }, +}; + +#endif /* SNDRV_SEQ_SYNC_SUPPORT */ diff -Nru a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_ports.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,669 @@ +/* + * ALSA sequencer Ports + * Copyright (c) 1998 by Frank van de Pol + * Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include "seq_system.h" +#include "seq_ports.h" +#include "seq_clientmgr.h" + +/* + + registration of client ports + + */ + + +/* + +NOTE: the current implementation of the port structure as a linked list is +not optimal for clients that have many ports. For sending messages to all +subscribers of a port we first need to find the address of the port +structure, which means we have to traverse the list. A direct access table +(array) would be better, but big preallocated arrays waste memory. + +Possible actions: + +1) leave it this way, a client does normaly does not have more than a few +ports + +2) replace the linked list of ports by a array of pointers which is +dynamicly kmalloced. When a port is added or deleted we can simply allocate +a new array, copy the corresponding pointers, and delete the old one. We +then only need a pointer to this array, and an integer that tells us how +much elements are in array. + +*/ + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +/* return pointer to port structure - port is locked if found */ +client_port_t *snd_seq_port_use_ptr(client_t *client, int num) +{ + struct list_head *p; + client_port_t *port; + + if (client == NULL) + return NULL; + read_lock(&client->ports_lock); + list_for_each(p, &client->ports_list_head) { + port = list_entry(p, client_port_t, list); + if (port->addr.port == num) { + if (port->closing) + break; /* deleting now */ + snd_use_lock_use(&port->use_lock); + read_unlock(&client->ports_lock); + return port; + } + } + read_unlock(&client->ports_lock); + return NULL; /* not found */ +} + + +/* search for the next port - port is locked if found */ +client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo) +{ + int num; + struct list_head *p; + client_port_t *port, *found; + + num = pinfo->addr.port; + found = NULL; + read_lock(&client->ports_lock); + list_for_each(p, &client->ports_list_head) { + port = list_entry(p, client_port_t, list); + if (port->addr.port < num) + continue; + if (port->addr.port == num) { + found = port; + break; + } + if (found == NULL || port->addr.port < found->addr.port) + found = port; + } + if (found) { + if (found->closing) + found = NULL; + else + snd_use_lock_use(&found->use_lock); + } + read_unlock(&client->ports_lock); + return found; +} + + +/* initialize port_subs_info_t */ +static void port_subs_info_init(port_subs_info_t *grp) +{ + INIT_LIST_HEAD(&grp->list_head); + grp->count = 0; + grp->exclusive = 0; + rwlock_init(&grp->list_lock); + init_rwsem(&grp->list_mutex); + grp->open = NULL; + grp->close = NULL; +} + + +/* create a port, port number is returned (-1 on failure) */ +client_port_t *snd_seq_create_port(client_t *client, int port) +{ + unsigned long flags; + client_port_t *new_port; + struct list_head *l; + int num = -1; + + /* sanity check */ + snd_assert(client, return NULL); + + if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) { + snd_printk("too many ports for client %d\n", client->number); + return NULL; + } + + /* create a new port */ + new_port = snd_kcalloc(sizeof(client_port_t), GFP_KERNEL); + if (! new_port) { + snd_printd("malloc failed for registering client port\n"); + return NULL; /* failure, out of memory */ + } + /* init port data */ + new_port->addr.client = client->number; + new_port->addr.port = -1; + new_port->owner = THIS_MODULE; + sprintf(new_port->name, "port-%d", num); + snd_use_lock_init(&new_port->use_lock); + port_subs_info_init(&new_port->c_src); + port_subs_info_init(&new_port->c_dest); + + num = port >= 0 ? port : 0; + down(&client->ports_mutex); + write_lock_irqsave(&client->ports_lock, flags); + list_for_each(l, &client->ports_list_head) { + client_port_t *p = list_entry(l, client_port_t, list); + if (p->addr.port > num) + break; + if (port < 0) /* auto-probe mode */ + num = p->addr.port + 1; + } + /* insert the new port */ + list_add_tail(&new_port->list, l); + client->num_ports++; + new_port->addr.port = num; /* store the port number in the port */ + write_unlock_irqrestore(&client->ports_lock, flags); + up(&client->ports_mutex); + sprintf(new_port->name, "port-%d", num); + + return new_port; +} + +/* */ +enum group_type_t { + SRC_LIST, DEST_LIST +}; + +static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack); +static int unsubscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack); + + +static client_port_t *get_client_port(snd_seq_addr_t *addr, client_t **cp) +{ + client_port_t *p; + *cp = snd_seq_client_use_ptr(addr->client); + if (*cp) { + p = snd_seq_port_use_ptr(*cp, addr->port); + if (! p) { + snd_seq_client_unlock(*cp); + *cp = NULL; + } + return p; + } + return NULL; +} + +/* + * remove all subscribers on the list + * this is called from port_delete, for each src and dest list. + */ +static void clear_subscriber_list(client_t *client, client_port_t *port, + port_subs_info_t *grp, int grptype) +{ + struct list_head *p, *n; + + down_write(&grp->list_mutex); + list_for_each_safe(p, n, &grp->list_head) { + subscribers_t *subs; + client_t *c; + client_port_t *aport; + + if (grptype == SRC_LIST) { + subs = list_entry(p, subscribers_t, src_list); + aport = get_client_port(&subs->info.dest, &c); + } else { + subs = list_entry(p, subscribers_t, dest_list); + aport = get_client_port(&subs->info.sender, &c); + } + list_del(p); + unsubscribe_port(client, port, grp, &subs->info, 0); + if (!aport) { + /* looks like the connected port is being deleted. + * we decrease the counter, and when both ports are deleted + * remove the subscriber info + */ + if (atomic_dec_and_test(&subs->ref_count)) + kfree(subs); + } else { + /* ok we got the connected port */ + port_subs_info_t *agrp; + agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src; + down_write(&agrp->list_mutex); + if (grptype == SRC_LIST) + list_del(&subs->dest_list); + else + list_del(&subs->src_list); + unsubscribe_port(c, aport, agrp, &subs->info, 1); + kfree(subs); + up_write(&agrp->list_mutex); + snd_seq_port_unlock(aport); + snd_seq_client_unlock(c); + } + } + up_write(&grp->list_mutex); +} + +/* delete port data */ +static int port_delete(client_t *client, client_port_t *port) +{ + /* set closing flag and wait for all port access are gone */ + port->closing = 1; + snd_use_lock_sync(&port->use_lock); + + /* clear subscribers info */ + clear_subscriber_list(client, port, &port->c_src, SRC_LIST); + clear_subscriber_list(client, port, &port->c_dest, DEST_LIST); + + if (port->private_free) + port->private_free(port->private_data); + + snd_assert(port->c_src.count == 0,); + snd_assert(port->c_dest.count == 0,); + + kfree(port); + return 0; +} + + +/* delete a port with the given port id */ +int snd_seq_delete_port(client_t *client, int port) +{ + unsigned long flags; + struct list_head *l; + client_port_t *found = NULL; + + down(&client->ports_mutex); + write_lock_irqsave(&client->ports_lock, flags); + list_for_each(l, &client->ports_list_head) { + client_port_t *p = list_entry(l, client_port_t, list); + if (p->addr.port == port) { + /* ok found. delete from the list at first */ + list_del(l); + client->num_ports--; + found = p; + break; + } + } + write_unlock_irqrestore(&client->ports_lock, flags); + up(&client->ports_mutex); + if (found) + return port_delete(client, found); + else + return -ENOENT; +} + +/* delete the all ports belonging to the given client */ +int snd_seq_delete_all_ports(client_t *client) +{ + unsigned long flags; + struct list_head deleted_list, *p, *n; + + /* move the port list to deleted_list, and + * clear the port list in the client data. + */ + down(&client->ports_mutex); + write_lock_irqsave(&client->ports_lock, flags); + if (! list_empty(&client->ports_list_head)) { + __list_add(&deleted_list, + client->ports_list_head.prev, + client->ports_list_head.next); + INIT_LIST_HEAD(&client->ports_list_head); + } else { + INIT_LIST_HEAD(&deleted_list); + } + client->num_ports = 0; + write_unlock_irqrestore(&client->ports_lock, flags); + + /* remove each port in deleted_list */ + list_for_each_safe(p, n, &deleted_list) { + client_port_t *port = list_entry(p, client_port_t, list); + list_del(p); + snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port); + port_delete(client, port); + } + up(&client->ports_mutex); + return 0; +} + +/* set port info fields */ +int snd_seq_set_port_info(client_port_t * port, snd_seq_port_info_t * info) +{ + snd_assert(port && info, return -EINVAL); + + /* set port name */ + if (info->name[0]) { + strncpy(port->name, info->name, sizeof(port->name)-1); + port->name[sizeof(port->name)-1] = '\0'; + } + + /* set capabilities */ + port->capability = info->capability; + + /* get port type */ + port->type = info->type; + + /* information about supported channels/voices */ + port->midi_channels = info->midi_channels; + port->synth_voices = info->synth_voices; + + return 0; +} + +/* get port info fields */ +int snd_seq_get_port_info(client_port_t * port, snd_seq_port_info_t * info) +{ + snd_assert(port && info, return -EINVAL); + + /* get port name */ + strncpy(info->name, port->name, sizeof(info->name)); + + /* get capabilities */ + info->capability = port->capability; + + /* get port type */ + info->type = port->type; + + /* information about supported channels/voices */ + info->midi_channels = port->midi_channels; + info->synth_voices = port->synth_voices; + + /* get subscriber counts */ + info->read_use = port->c_src.count; + info->write_use = port->c_dest.count; + + return 0; +} + + + +/* + * call callback functions (if any): + * the callbacks are invoked only when the first (for connection) or + * the last subscription (for disconnection) is done. Second or later + * subscription results in increment of counter, but no callback is + * invoked. + * This feature is useful if these callbacks are associated with + * initialization or termination of devices (see seq_midi.c). + * + * If callback_all option is set, the callback function is invoked + * at each connnection/disconnection. + */ + +static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, + snd_seq_port_subscribe_t *info, int send_ack) +{ + int err = 0; + + if (!try_inc_mod_count(port->owner)) + return -EFAULT; + grp->count++; + if (grp->open && (port->callback_all || grp->count == 1)) { + err = grp->open(port->private_data, info); + if (err < 0) { + dec_mod_count(port->owner); + grp->count--; + } + } + if (err >= 0 && send_ack && client->type == USER_CLIENT) + snd_seq_client_notify_subscription(port->addr.client, port->addr.port, + info, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); + + return err; +} + +static int unsubscribe_port(client_t *client, client_port_t *port, + port_subs_info_t *grp, + snd_seq_port_subscribe_t *info, int send_ack) +{ + int err = 0; + + snd_assert(port->owner, return -EFAULT); + if (! grp->count) + return -EINVAL; + grp->count--; + if (grp->close && (port->callback_all || grp->count == 0)) + err = grp->close(port->private_data, info); + if (send_ack && client->type == USER_CLIENT) + snd_seq_client_notify_subscription(port->addr.client, port->addr.port, + info, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); + dec_mod_count(port->owner); + return err; +} + + + +/* check if both addresses are identical */ +static inline int addr_match(snd_seq_addr_t *r, snd_seq_addr_t *s) +{ + return (r->client == s->client) && (r->port == s->port); +} + +/* check the two subscribe info match */ +/* if flags is zero, checks only sender and destination addresses */ +static int match_subs_info(snd_seq_port_subscribe_t *r, + snd_seq_port_subscribe_t *s) +{ + if (addr_match(&r->sender, &s->sender) && + addr_match(&r->dest, &s->dest)) { + if (r->flags && r->flags == s->flags) + return r->queue == s->queue; + else if (! r->flags) + return 1; + } + return 0; +} + + +/* connect two ports */ +int snd_seq_port_connect(client_t *connector, + client_t *src_client, client_port_t *src_port, + client_t *dest_client, client_port_t *dest_port, + snd_seq_port_subscribe_t *info) +{ + port_subs_info_t *src = &src_port->c_src; + port_subs_info_t *dest = &dest_port->c_dest; + subscribers_t *subs; + struct list_head *p; + int err, src_called = 0; + unsigned long flags; + int exclusive; + + subs = snd_kcalloc(sizeof(*subs), GFP_KERNEL); + if (! subs) + return -ENOMEM; + + subs->info = *info; + atomic_set(&subs->ref_count, 2); + + down_write(&src->list_mutex); + down_write(&dest->list_mutex); + + exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0; + err = -EBUSY; + if (exclusive) { + if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head)) + goto __error; + } else { + if (src->exclusive || dest->exclusive) + goto __error; + /* check whether already exists */ + list_for_each(p, &src->list_head) { + subscribers_t *s = list_entry(p, subscribers_t, src_list); + if (match_subs_info(info, &s->info)) + goto __error; + } + list_for_each(p, &dest->list_head) { + subscribers_t *s = list_entry(p, subscribers_t, dest_list); + if (match_subs_info(info, &s->info)) + goto __error; + } + } + + if ((err = subscribe_port(src_client, src_port, src, info, + connector->number != src_client->number)) < 0) + goto __error; + src_called = 1; + + if ((err = subscribe_port(dest_client, dest_port, dest, info, + connector->number != dest_client->number)) < 0) + goto __error; + + /* add to list */ + write_lock_irqsave(&src->list_lock, flags); + // write_lock(&dest->list_lock); // no other lock yet + list_add_tail(&subs->src_list, &src->list_head); + list_add_tail(&subs->dest_list, &dest->list_head); + // write_unlock(&dest->list_lock); // no other lock yet + write_unlock_irqrestore(&src->list_lock, flags); + + src->exclusive = dest->exclusive = exclusive; + + up_write(&dest->list_mutex); + up_write(&src->list_mutex); + return 0; + + __error: + if (src_called) + unsubscribe_port(src_client, src_port, src, info, + connector->number != src_client->number); + kfree(subs); + up_write(&dest->list_mutex); + up_write(&src->list_mutex); + return err; +} + + +/* remove the connection */ +int snd_seq_port_disconnect(client_t *connector, + client_t *src_client, client_port_t *src_port, + client_t *dest_client, client_port_t *dest_port, + snd_seq_port_subscribe_t *info) +{ + port_subs_info_t *src = &src_port->c_src; + port_subs_info_t *dest = &dest_port->c_dest; + subscribers_t *subs; + struct list_head *p; + int err = -ENOENT; + unsigned long flags; + + down_write(&src->list_mutex); + down_write(&dest->list_mutex); + + /* look for the connection */ + list_for_each(p, &src->list_head) { + subs = list_entry(p, subscribers_t, src_list); + if (match_subs_info(info, &subs->info)) { + write_lock_irqsave(&src->list_lock, flags); + // write_lock(&dest->list_lock); // no lock yet + list_del(&subs->src_list); + list_del(&subs->dest_list); + // write_unlock(&dest->list_lock); + write_unlock_irqrestore(&src->list_lock, flags); + src->exclusive = dest->exclusive = 0; + unsubscribe_port(src_client, src_port, src, info, + connector->number != src_client->number); + unsubscribe_port(dest_client, dest_port, dest, info, + connector->number != dest_client->number); + kfree(subs); + err = 0; + break; + } + } + + up_write(&dest->list_mutex); + up_write(&src->list_mutex); + return err; +} + + +/* get matched subscriber */ +subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, + snd_seq_addr_t *dest_addr) +{ + struct list_head *p; + subscribers_t *s, *found = NULL; + + down_read(&src_grp->list_mutex); + list_for_each(p, &src_grp->list_head) { + s = list_entry(p, subscribers_t, src_list); + if (addr_match(dest_addr, &s->info.dest)) { + found = s; + break; + } + } + up_read(&src_grp->list_mutex); + return found; +} + +/* + * Attach a device driver that wants to receive events from the + * sequencer. Returns the new port number on success. + * A driver that wants to receive the events converted to midi, will + * use snd_seq_midisynth_register_port(). + */ +/* exported */ +int snd_seq_event_port_attach(int client, + snd_seq_port_callback_t *pcbp, + int cap, + int type, + char *portname) +{ + snd_seq_port_info_t portinfo; + int ret; + + /* Set up the port */ + memset(&portinfo, 0, sizeof(portinfo)); + portinfo.addr.client = client; + if (portname) + strncpy(portinfo.name, portname, sizeof(portinfo.name)); + else + sprintf(portinfo.name, "Unamed port"); + + portinfo.capability = cap; + portinfo.type = type; + portinfo.kernel = pcbp; + + /* Create it */ + ret = snd_seq_kernel_client_ctl(client, + SNDRV_SEQ_IOCTL_CREATE_PORT, + &portinfo); + + if (ret >= 0) + ret = portinfo.addr.port; + + return ret; +} + + +/* + * Detach the driver from a port. + */ +/* exported */ +int snd_seq_event_port_detach(int client, int port) +{ + snd_seq_port_info_t portinfo; + int err; + + memset(&portinfo, 0, sizeof(portinfo)); + portinfo.addr.client = client; + portinfo.addr.port = port; + err = snd_seq_kernel_client_ctl(client, + SNDRV_SEQ_IOCTL_DELETE_PORT, + &portinfo); + + return err; +} diff -Nru a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_ports.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,124 @@ +/* + * ALSA sequencer Ports + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_PORTS_H +#define __SND_SEQ_PORTS_H + +#include +#include "seq_lock.h" + +/* list of 'exported' ports */ + +/* Client ports that are not exported are still accessible, but are + anonymous ports. + + If a port supports SUBSCRIPTION, that port can send events to all + subscribersto a special address, with address + (queue==SNDRV_SEQ_ADDRESS_SUBSCRIBERS). The message is then send to all + recipients that are registered in the subscription list. A typical + application for these SUBSCRIPTION events is handling of incoming MIDI + data. The port doesn't 'know' what other clients are interested in this + message. If for instance a MIDI recording application would like to receive + the events from that port, it will first have to subscribe with that port. + +*/ + +typedef struct subscribers_t { + snd_seq_port_subscribe_t info; /* additional info */ + struct list_head src_list; /* link of sources */ + struct list_head dest_list; /* link of destinations */ + atomic_t ref_count; +} subscribers_t; + +typedef struct port_subs_info_t { + struct list_head list_head; /* list of subscribed ports */ + unsigned int count; /* count of subscribers */ + unsigned int exclusive: 1; /* exclusive mode */ + struct rw_semaphore list_mutex; + rwlock_t list_lock; + snd_seq_kernel_port_open_t *open; + snd_seq_kernel_port_close_t *close; +} port_subs_info_t; + +typedef struct client_port_t { + + snd_seq_addr_t addr; /* client/port number */ + struct module *owner; /* owner of this port */ + char name[64]; /* port name */ + struct list_head list; /* port list */ + snd_use_lock_t use_lock; + + /* subscribers */ + port_subs_info_t c_src; /* read (sender) list */ + port_subs_info_t c_dest; /* write (dest) list */ + + snd_seq_kernel_port_input_t *event_input; + snd_seq_kernel_port_private_free_t *private_free; + void *private_data; + unsigned int callback_all : 1; + unsigned int closing : 1; + + /* capability, inport, output, sync */ + unsigned int capability; /* port capability bits */ + unsigned int type; /* port type bits */ + + /* supported channels */ + int midi_channels; + int synth_voices; + +} client_port_t; + +/* return pointer to port structure and lock port */ +client_port_t *snd_seq_port_use_ptr(client_t *client, int num); + +/* search for next port - port is locked if found */ +client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo); + +/* unlock the port */ +#define snd_seq_port_unlock(port) snd_use_lock_free(&(port)->use_lock) + +/* create a port, port number is returned (-1 on failure) */ +client_port_t *snd_seq_create_port(client_t *client, int port_index); + +/* delete a port */ +int snd_seq_delete_port(client_t *client, int port); + +/* delete all ports */ +int snd_seq_delete_all_ports(client_t *client); + +/* set port info fields */ +int snd_seq_set_port_info(client_port_t *port, snd_seq_port_info_t *info); + +/* get port info fields */ +int snd_seq_get_port_info(client_port_t *port, snd_seq_port_info_t *info); + +/* add subscriber to subscription list */ +int snd_seq_port_connect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info); + +/* remove subscriber from subscription list */ +int snd_seq_port_disconnect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info); + +/* subscribe port */ +int snd_seq_port_subscribe(client_port_t *port, snd_seq_port_subscribe_t *info); + +/* get matched subscriber */ +subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, snd_seq_addr_t *dest_addr); + +#endif diff -Nru a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_prioq.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,449 @@ +/* + * ALSA sequencer Priority Queue + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "seq_timer.h" +#include "seq_prioq.h" +#include "seq_timer.h" + + +/* Implementation is a simple linked list for now... + + This priority queue orders the events on timestamp. For events with an + equeal timestamp the queue behaves as a FIFO. + + * + * +-------+ + * Head --> | first | + * +-------+ + * |next + * +-----v-+ + * | | + * +-------+ + * | + * +-----v-+ + * | | + * +-------+ + * | + * +-----v-+ + * Tail --> | last | + * +-------+ + * + + */ + + + +/* create new prioq (constructor) */ +prioq_t *snd_seq_prioq_new(void) +{ + prioq_t *f; + + f = snd_kcalloc(sizeof(prioq_t), GFP_KERNEL); + if (f == NULL) { + snd_printd("oops: malloc failed for snd_seq_prioq_new()\n"); + return NULL; + } + + spin_lock_init(&f->lock); + f->head = NULL; + f->tail = NULL; + f->cells = 0; + + return f; +} + +/* delete prioq (destructor) */ +void snd_seq_prioq_delete(prioq_t **fifo) +{ + prioq_t *f = *fifo; + *fifo = NULL; + + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n"); + return; + } + + /* release resources...*/ + /*....................*/ + + if (f->cells > 0) { + /* drain prioQ */ + while (f->cells > 0) + snd_seq_cell_free(snd_seq_prioq_cell_out(f)); + } + + kfree(f); +} + + + + +/* compare timestamp between events */ +/* return 1 if a >= b; 0 */ +static inline int compare_timestamp(snd_seq_event_t * a, snd_seq_event_t * b) +{ + if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) { + /* compare ticks */ + return (snd_seq_compare_tick_time(&a->time.tick, &b->time.tick)); + } else { + /* compare real time */ + return (snd_seq_compare_real_time(&a->time.time, &b->time.time)); + } +} + +/* compare timestamp between events */ +/* return negative if a < b; + * zero if a = b; + * positive if a > b; + */ +static inline int compare_timestamp_rel(snd_seq_event_t *a, snd_seq_event_t *b) +{ + if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) { + /* compare ticks */ + if (a->time.tick > b->time.tick) + return 1; + else if (a->time.tick == b->time.tick) + return 0; + else + return -1; + } else { + /* compare real time */ + if (a->time.time.tv_sec > b->time.time.tv_sec) + return 1; + else if (a->time.time.tv_sec == b->time.time.tv_sec) { + if (a->time.time.tv_nsec > b->time.time.tv_nsec) + return 1; + else if (a->time.time.tv_nsec == b->time.time.tv_nsec) + return 0; + else + return -1; + } else + return -1; + } +} + +/* enqueue cell to prioq */ +void snd_seq_prioq_cell_in(prioq_t * f, snd_seq_event_cell_t * cell) +{ + snd_seq_event_cell_t *cur, *prev; + unsigned long flags; + int prior; + + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return; + } + if (cell == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL cell\n"); + return; + } + + /* check flags */ + prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK); + + spin_lock_irqsave(&f->lock, flags); + + /* check if this element needs to inserted at the end (ie. ordered + data is inserted) This will be very likeley if a sequencer + application or midi file player is feeding us (sequential) data */ + if (f->tail && !prior) { + if (compare_timestamp(&cell->event, &f->tail->event)) { + /* add new cell to tail of the fifo */ + f->tail->next = cell; + f->tail = cell; + cell->next = NULL; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); + return; + } + } + /* traverse list of elements to find the place where the new cell is + to be inserted... Note that this is a order n process ! */ + + prev = NULL; /* previous cell */ + cur = f->head; /* cursor */ + + while (cur != NULL) { + /* compare timestamps */ + int rel = compare_timestamp_rel(&cell->event, &cur->event); + if (rel < 0) + /* new cell has earlier schedule time, */ + break; + else if (rel == 0 && prior) + /* equal schedule time and prior to others */ + break; + /* new cell has equal or larger schedule time, */ + /* move cursor to next cell */ + prev = cur; + cur = cur->next; + } + + /* insert it before cursor */ + if (prev != NULL) + prev->next = cell; + cell->next = cur; + + if (f->head == cur) /* this is the first cell, set head to it */ + f->head = cell; + if (cur == NULL) /* reached end of the list */ + f->tail = cell; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); +} + +/* dequeue cell from prioq */ +snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t * f) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return NULL; + } + spin_lock_irqsave(&f->lock, flags); + + cell = f->head; + if (cell) { + f->head = cell->next; + + /* reset tail if this was the last element */ + if (f->tail == cell) + f->tail = NULL; + + cell->next = NULL; + f->cells--; + } + + spin_unlock_irqrestore(&f->lock, flags); + return cell; +} + +/* return number of events available in prioq */ +int snd_seq_prioq_avail(prioq_t * f) +{ + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return 0; + } + return f->cells; +} + + +/* peek at cell at the head of the prioq */ +snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t * f) +{ + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return NULL; + } + return f->head; +} + + +static inline int prioq_match(snd_seq_event_cell_t *cell, int client, int timestamp) +{ + if (cell->event.source.client == client || + cell->event.dest.client == client) + return 1; + if (!timestamp) + return 0; + switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + if (cell->event.time.tick) + return 1; + break; + case SNDRV_SEQ_TIME_STAMP_REAL: + if (cell->event.time.time.tv_sec || + cell->event.time.time.tv_nsec) + return 1; + break; + } + return 0; +} + +/* remove cells for left client */ +void snd_seq_prioq_leave(prioq_t * f, int client, int timestamp) +{ + register snd_seq_event_cell_t *cell, *next; + unsigned long flags; + snd_seq_event_cell_t *prev = NULL; + snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext; + + /* collect all removed cells */ + spin_lock_irqsave(&f->lock, flags); + cell = f->head; + while (cell) { + next = cell->next; + if (prioq_match(cell, client, timestamp)) { + /* remove cell from prioq */ + if (cell == f->head) { + f->head = cell->next; + } else { + prev->next = cell->next; + } + if (cell == f->tail) + f->tail = cell->next; + f->cells--; + /* add cell to free list */ + cell->next = NULL; + if (freefirst == NULL) { + freefirst = cell; + } else { + freeprev->next = cell; + } + freeprev = cell; + } else { +#if 0 + printk("type = %i, source = %i, dest = %i, client = %i\n", + cell->event.type, + cell->event.source.client, + cell->event.dest.client, + client); +#endif + prev = cell; + } + cell = next; + } + spin_unlock_irqrestore(&f->lock, flags); + + /* remove selected cells */ + while (freefirst) { + freenext = freefirst->next; + snd_seq_cell_free(freefirst); + freefirst = freenext; + } +} + +static int prioq_remove_match(snd_seq_remove_events_t *info, + snd_seq_event_t *ev) +{ + int res; + + if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) { + if (ev->dest.client != info->dest.client || + ev->dest.port != info->dest.port) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) { + if (! snd_seq_ev_is_channel_type(ev)) + return 0; + /* data.note.channel and data.control.channel are identical */ + if (ev->data.note.channel != info->channel) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) { + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK) + res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick); + else + res = snd_seq_compare_real_time(&ev->time.time, &info->time.time); + if (!res) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) { + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK) + res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick); + else + res = snd_seq_compare_real_time(&ev->time.time, &info->time.time); + if (res) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) { + if (ev->type != info->type) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) { + /* Do not remove off events */ + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEOFF: + /* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */ + return 0; + default: + break; + } + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) { + if (info->tag != ev->tag) + return 0; + } + + return 1; +} + +/* remove cells matching remove criteria */ +void snd_seq_prioq_remove_events(prioq_t * f, int client, + snd_seq_remove_events_t *info) +{ + register snd_seq_event_cell_t *cell, *next; + unsigned long flags; + snd_seq_event_cell_t *prev = NULL; + snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext; + + /* collect all removed cells */ + spin_lock_irqsave(&f->lock, flags); + cell = f->head; + + while (cell) { + next = cell->next; + if (cell->event.source.client == client && + prioq_remove_match(info, &cell->event)) { + + /* remove cell from prioq */ + if (cell == f->head) { + f->head = cell->next; + } else { + prev->next = cell->next; + } + + if (cell == f->tail) + f->tail = cell->next; + f->cells--; + + /* add cell to free list */ + cell->next = NULL; + if (freefirst == NULL) { + freefirst = cell; + } else { + freeprev->next = cell; + } + + freeprev = cell; + } else { + prev = cell; + } + cell = next; + } + spin_unlock_irqrestore(&f->lock, flags); + + /* remove selected cells */ + while (freefirst) { + freenext = freefirst->next; + snd_seq_cell_free(freefirst); + freefirst = freenext; + } +} + + diff -Nru a/sound/core/seq/seq_prioq.h b/sound/core/seq/seq_prioq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_prioq.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,62 @@ +/* + * ALSA sequencer Priority Queue + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_PRIOQ_H +#define __SND_SEQ_PRIOQ_H + +#include "seq_memory.h" + + +/* === PRIOQ === */ + +typedef struct { + snd_seq_event_cell_t* head; /* pointer to head of prioq */ + snd_seq_event_cell_t* tail; /* pointer to tail of prioq */ + int cells; + spinlock_t lock; +} prioq_t; + + +/* create new prioq (constructor) */ +extern prioq_t *snd_seq_prioq_new(void); + +/* delete prioq (destructor) */ +extern void snd_seq_prioq_delete(prioq_t **fifo); + +/* enqueue cell to prioq */ +extern void snd_seq_prioq_cell_in(prioq_t *f, snd_seq_event_cell_t *cell); + +/* dequeue cell from prioq */ +extern snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t *f); + +/* return number of events available in prioq */ +extern int snd_seq_prioq_avail(prioq_t *f); + +/* peek at cell at the head of the prioq */ +extern snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t *f); + +/* client left queue */ +extern void snd_seq_prioq_leave(prioq_t *f, int client, int timestamp); + +/* Remove events */ +void snd_seq_prioq_remove_events(prioq_t * f, int client, + snd_seq_remove_events_t *info); + +#endif diff -Nru a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_queue.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,872 @@ +/* + * ALSA sequencer Timing queue handling + * Copyright (c) 1998-1999 by Frank van de Pol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * MAJOR CHANGES + * Nov. 13, 1999 Takashi Iwai + * - Queues are allocated dynamically via ioctl. + * - When owner client is deleted, all owned queues are deleted, too. + * - Owner of unlocked queue is kept unmodified even if it is + * manipulated by other clients. + * - Owner field in SET_QUEUE_OWNER ioctl must be identical with the + * caller client. i.e. Changing owner to a third client is not + * allowed. + * + * Aug. 30, 2000 Takashi Iwai + * - Queues are managed in static array again, but with better way. + * The API itself is identical. + * - The queue is locked when queue_t pinter is returned via + * queueptr(). This pointer *MUST* be released afterward by + * queuefree(ptr). + * - Addition of experimental sync support. + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_clientmgr.h" +#include "seq_fifo.h" +#include "seq_timer.h" +#include "seq_info.h" + +#ifdef SNDRV_SEQ_SYNC_SUPPORT +/* FIXME: this should be in a header file */ +void snd_seq_sync_info_read(queue_t *q, snd_info_buffer_t *buffer); +#endif + +static void snd_seq_check_queue_in_tasklet(unsigned long private_data); + +/* list of allocated queues */ +static queue_t *queue_list[SNDRV_SEQ_MAX_QUEUES]; +static spinlock_t queue_list_lock = SPIN_LOCK_UNLOCKED; +/* number of queues allocated */ +static int num_queues = 0; + +int snd_seq_queue_get_cur_queues(void) +{ + return num_queues; +} + +/*----------------------------------------------------------------*/ + +/* assign queue id and insert to list */ +static int queue_list_add(queue_t *q) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&queue_list_lock, flags); + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if (! queue_list[i]) { + queue_list[i] = q; + q->queue = i; + num_queues++; + spin_unlock_irqrestore(&queue_list_lock, flags); + return i; + } + } + spin_unlock_irqrestore(&queue_list_lock, flags); + return -1; +} + +static queue_t *queue_list_remove(int id, int client) +{ + queue_t *q; + unsigned long flags; + + spin_lock_irqsave(&queue_list_lock, flags); + q = queue_list[id]; + if (q) { + spin_lock(&q->owner_lock); + if (q->owner == client) { + /* found */ + q->klocked = 1; + spin_unlock(&q->owner_lock); + queue_list[id] = NULL; + num_queues--; + spin_unlock_irqrestore(&queue_list_lock, flags); + return q; + } + spin_unlock(&q->owner_lock); + } + spin_unlock_irqrestore(&queue_list_lock, flags); + return NULL; +} + +/*----------------------------------------------------------------*/ + +/* create new queue (constructor) */ +static queue_t *queue_new(int owner, int locked) +{ + queue_t *q; + + q = snd_kcalloc(sizeof(queue_t), GFP_KERNEL); + if (q == NULL) { + snd_printd("malloc failed for snd_seq_queue_new()\n"); + return NULL; + } + + spin_lock_init(&q->owner_lock); + spin_lock_init(&q->check_lock); + init_MUTEX(&q->timer_mutex); + snd_use_lock_init(&q->use_lock); + tasklet_init(&q->taskq, snd_seq_check_queue_in_tasklet, (unsigned long)q); + q->queue = -1; + + q->tickq = snd_seq_prioq_new(); + q->timeq = snd_seq_prioq_new(); + q->timer = snd_seq_timer_new(); + if (q->tickq == NULL || q->timeq == NULL || q->timer == NULL) { + snd_seq_prioq_delete(&q->tickq); + snd_seq_prioq_delete(&q->timeq); + snd_seq_timer_delete(&q->timer); + kfree(q); + return NULL; + } + + q->owner = owner; + q->locked = locked; + q->klocked = 0; + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + q->master_lock = RW_LOCK_UNLOCKED; + q->slave_lock = RW_LOCK_UNLOCKED; + INIT_LIST_HEAD(&q->master_head); + q->slave.format = 0; +#endif + + return q; +} + +/* delete queue (destructor) */ +static void queue_delete(queue_t *q) +{ +#ifdef SNDRV_SEQ_SYNC_SUPPORT + if (q->info_flags & SNDRV_SEQ_QUEUE_FLG_SYNC) + snd_seq_sync_delete_port(q); +#endif + /* stop and release the timer */ + snd_seq_timer_stop(q->timer); + snd_seq_timer_close(q); + /* wait until access free */ + snd_use_lock_sync(&q->use_lock); + /* release resources... */ + snd_seq_prioq_delete(&q->tickq); + snd_seq_prioq_delete(&q->timeq); + snd_seq_timer_delete(&q->timer); + + kfree(q); +} + + +/*----------------------------------------------------------------*/ + +/* setup queues */ +int __init snd_seq_queues_init(void) +{ + /* + memset(queue_list, 0, sizeof(queue_list)); + num_queues = 0; + */ + return 0; +} + +/* delete all existing queues */ +void __exit snd_seq_queues_delete(void) +{ + int i; + + /* clear list */ + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if (queue_list[i]) + queue_delete(queue_list[i]); + } +} + +/* allocate a new queue - + * return queue index value or negative value for error + */ +int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags) +{ + queue_t *q; + + q = queue_new(client, locked); + if (q == NULL) + return -ENOMEM; + q->info_flags = info_flags; + if (queue_list_add(q) < 0) { + queue_delete(q); + return -ENOMEM; + } + snd_seq_queue_use(q->queue, client, 1); /* use this queue */ +#ifdef SNDRV_SEQ_SYNC_SUPPORT + if (q->info_flags & SNDRV_SEQ_QUEUE_FLG_SYNC) { + if (snd_seq_sync_create_port(q) < 0) + q->info_flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC; + } +#endif + return q->queue; +} + +/* delete a queue - queue must be owned by the client */ +int snd_seq_queue_delete(int client, int queueid) +{ + queue_t *q; + + if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES) + return -EINVAL; + q = queue_list_remove(queueid, client); + if (q == NULL) + return -EINVAL; + queue_delete(q); + + return 0; +} + + +/* return pointer to queue structure for specified id */ +queue_t *queueptr(int queueid) +{ + queue_t *q; + unsigned long flags; + + if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES) + return NULL; + spin_lock_irqsave(&queue_list_lock, flags); + q = queue_list[queueid]; + if (q) + snd_use_lock_use(&q->use_lock); + spin_unlock_irqrestore(&queue_list_lock, flags); + return q; +} + +/* return the (first) queue matching with the specified name */ +queue_t *snd_seq_queue_find_name(char *name) +{ + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) != NULL) { + if (strncpy(q->name, name, sizeof(q->name)) == 0) + return q; + queuefree(q); + } + } + return NULL; +} + + +/* -------------------------------------------------------- */ + +void snd_seq_check_queue(queue_t *q, int atomic, int hop) +{ + unsigned long flags; + int dequeue; + int rc; + snd_seq_event_cell_t *cell; + + if (q == NULL) + return; + + /* make this function non-reentrant */ + spin_lock_irqsave(&q->check_lock, flags); + if (q->check_blocked) { + q->check_again = 1; + spin_unlock_irqrestore(&q->check_lock, flags); + return; /* other thread is already checking queues */ + } + q->check_blocked = 1; + spin_unlock_irqrestore(&q->check_lock, flags); + + if (atomic) + dequeue = SNDRV_SEQ_MAX_DEQUEUE; + else + dequeue = 0x7fffffff; /* XXX */ + + __again: + /* Process tick queue... */ + + /* limit the number of elements dequeued per pass to save the machine from lockups */ + while (dequeue > 0) { + + cell = snd_seq_prioq_cell_peek(q->tickq); + if (cell == NULL) + break; + if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick, &cell->event.time.tick)) { + cell = snd_seq_prioq_cell_out(q->tickq); + if (cell != NULL) { + rc = snd_seq_dispatch_event(cell, atomic, hop); + if (rc > 0) + dequeue -= rc; + } + } else { + /* event remains in the queue */ + break; + } + } + + + /* Process time queue... */ + + /* limit the number of elements dequeued per pass to save the machine from lockups */ + while (dequeue > 0) { + cell = snd_seq_prioq_cell_peek(q->timeq); + if (cell == NULL) + break; + if (snd_seq_compare_real_time(&q->timer->cur_time, &cell->event.time.time)) { + cell = snd_seq_prioq_cell_out(q->timeq); + if (cell != NULL) { + rc = snd_seq_dispatch_event(cell, atomic, hop); + if (rc > 0) + dequeue -= rc; + } + + } else { + /* event remains in the queue */ + break; + } + } + + /* free lock */ + spin_lock_irqsave(&q->check_lock, flags); + if (q->check_again && dequeue > 0) { + q->check_again = 0; + spin_unlock_irqrestore(&q->check_lock, flags); + goto __again; + } + q->check_blocked = 0; + if (dequeue <= 0 && atomic) + tasklet_hi_schedule(&q->taskq); + spin_unlock_irqrestore(&q->check_lock, flags); +} + +/* tasklet */ +static void snd_seq_check_queue_in_tasklet(unsigned long private_data) +{ + queue_t *q = (queue_t *)private_data; + snd_seq_check_queue(q, 0, 0); +} + +/* enqueue a event to singe queue */ +int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop) +{ + int dest; + queue_t *q; + + snd_assert(cell != NULL, return -EINVAL); + dest = cell->event.queue; /* destination queue */ + q = queueptr(dest); + if (q == NULL) + return -EINVAL; + /* handle relative time stamps, convert them into absolute */ + if ((cell->event.flags & SNDRV_SEQ_TIME_MODE_MASK) == SNDRV_SEQ_TIME_MODE_REL) { + switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + cell->event.time.tick += q->timer->tick.cur_tick; + break; + + case SNDRV_SEQ_TIME_STAMP_REAL: + snd_seq_inc_real_time(&cell->event.time.time, &q->timer->cur_time); + break; + } + cell->event.flags &= ~SNDRV_SEQ_TIME_MODE_MASK; + cell->event.flags |= SNDRV_SEQ_TIME_MODE_ABS; + } + /* enqueue event in the real-time or midi queue */ + switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + snd_seq_prioq_cell_in(q->tickq, cell); + break; + + case SNDRV_SEQ_TIME_STAMP_REAL: + snd_seq_prioq_cell_in(q->timeq, cell); + break; + } + + /* trigger dispatching */ + snd_seq_check_queue(q, atomic, hop); + + queuefree(q); /* unlock */ + + return 0; +} + + +/*----------------------------------------------------------------*/ + +static inline int check_access(queue_t *q, int client) +{ + return (q->owner == client) || (!q->locked && !q->klocked); +} + +/* check if the client has permission to modify queue parameters. + * if it does, lock the queue + */ +static int queue_access_lock(queue_t *q, int client) +{ + unsigned long flags; + int access_ok; + + spin_lock_irqsave(&q->owner_lock, flags); + access_ok = check_access(q, client); + if (access_ok) + q->klocked = 1; + spin_unlock_irqrestore(&q->owner_lock, flags); + return access_ok; +} + +/* unlock the queue */ +static inline void queue_access_unlock(queue_t *q) +{ + unsigned long flags; + + spin_lock_irqsave(&q->owner_lock, flags); + q->klocked = 0; + spin_unlock_irqrestore(&q->owner_lock, flags); +} + +/* exported - only checking permission */ +int snd_seq_queue_check_access(int queueid, int client) +{ + queue_t *q = queueptr(queueid); + int access_ok; + unsigned long flags; + + if (! q) + return 0; + spin_lock_irqsave(&q->owner_lock, flags); + access_ok = check_access(q, client); + spin_unlock_irqrestore(&q->owner_lock, flags); + queuefree(q); + return access_ok; +} + +/*----------------------------------------------------------------*/ + +/* + * change queue's owner and permission + */ +int snd_seq_queue_set_owner(int queueid, int client, int locked) +{ + queue_t *q = queueptr(queueid); + + if (q == NULL) + return -EINVAL; + + if (! queue_access_lock(q, client)) { + queuefree(q); + return -EPERM; + } + + q->locked = locked ? 1 : 0; + q->owner = client; + queue_access_unlock(q); + queuefree(q); + + return 0; +} + + +/*----------------------------------------------------------------*/ + +/* open timer - + * q->use mutex should be down before calling this function to avoid + * confliction with snd_seq_queue_use() + */ +int snd_seq_queue_timer_open(int queueid) +{ + int result = 0; + queue_t *queue; + seq_timer_t *tmr; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + tmr = queue->timer; + if ((result = snd_seq_timer_open(queue)) < 0) { + snd_seq_timer_defaults(tmr); + result = snd_seq_timer_open(queue); + } + queuefree(queue); + return result; +} + +/* close timer - + * q->use mutex should be down before calling this function + */ +int snd_seq_queue_timer_close(int queueid) +{ + queue_t *queue; + seq_timer_t *tmr; + int result = 0; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + tmr = queue->timer; + snd_seq_timer_close(queue); + queuefree(queue); + return result; +} + +/* change queue tempo and ppq */ +int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info) +{ + queue_t *q = queueptr(queueid); + int result; + + if (q == NULL) + return -EINVAL; + if (! queue_access_lock(q, client)) { + queuefree(q); + return -EPERM; + } + + result = snd_seq_timer_set_tempo(q->timer, info->tempo); + if (result >= 0) + result = snd_seq_timer_set_ppq(q->timer, info->ppq); + if (result >= 0 && info->skew_base > 0) + result = snd_seq_timer_set_skew(q->timer, info->skew_value, info->skew_base); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_update_tempo(q); +#endif + queue_access_unlock(q); + queuefree(q); + return result; +} + + +/* use or unuse this queue - + * if it is the first client, starts the timer. + * if it is not longer used by any clients, stop the timer. + */ +int snd_seq_queue_use(int queueid, int client, int use) +{ + queue_t *queue; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + down(&queue->timer_mutex); + if (use) { + if (!test_and_set_bit(client, &queue->clients_bitmap)) + queue->clients++; + } else { + if (test_and_clear_bit(client, &queue->clients_bitmap)) + queue->clients--; + } + if (queue->clients) { + if (use && queue->clients == 1) + snd_seq_timer_defaults(queue->timer); + snd_seq_timer_open(queue); + } else { + snd_seq_timer_close(queue); + } + up(&queue->timer_mutex); + queuefree(queue); + return 0; +} + +/* + * check if queue is used by the client + * return negative value if the queue is invalid. + * return 0 if not used, 1 if used. + */ +int snd_seq_queue_is_used(int queueid, int client) +{ + queue_t *q; + int result; + + q = queueptr(queueid); + if (q == NULL) + return -EINVAL; /* invalid queue */ + result = test_bit(client, &q->clients_bitmap) ? 1 : 0; + queuefree(q); + return result; +} + + +/*----------------------------------------------------------------*/ + +/* notification that client has left the system - + * stop the timer on all queues owned by this client + */ +void snd_seq_queue_client_termination(int client) +{ + unsigned long flags; + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + spin_lock_irqsave(&q->owner_lock, flags); + if (q->owner == client) + q->klocked = 1; + spin_unlock_irqrestore(&q->owner_lock, flags); + if (q->owner == client) { + if (q->timer->running) + snd_seq_timer_stop(q->timer); + snd_seq_timer_reset(q->timer); + } + queuefree(q); + } +} + +/* final stage notification - + * remove cells for no longer exist client (for non-owned queue) + * or delete this queue (for owned queue) + */ +void snd_seq_queue_client_leave(int client) +{ + int i; + queue_t *q; + + /* delete own queues from queue list */ + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queue_list_remove(i, client)) != NULL) + queue_delete(q); + } + + /* remove cells from existing queues - + * they are not owned by this client + */ + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + if (test_bit(client, q->clients_bitmap)) { + snd_seq_prioq_leave(q->tickq, client, 0); + snd_seq_prioq_leave(q->timeq, client, 0); + snd_seq_queue_use(q->queue, client, 0); + } + queuefree(q); + } +} + + + +/*----------------------------------------------------------------*/ + +/* remove cells from all queues */ +void snd_seq_queue_client_leave_cells(int client) +{ + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + snd_seq_prioq_leave(q->tickq, client, 0); + snd_seq_prioq_leave(q->timeq, client, 0); + queuefree(q); + } +} + +/* remove cells based on flush criteria */ +void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info) +{ + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + if (test_bit(client, q->clients_bitmap) && + (! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) || + q->queue == info->queue)) { + snd_seq_prioq_remove_events(q->tickq, client, info); + snd_seq_prioq_remove_events(q->timeq, client, info); + } + queuefree(q); + } +} + +/*----------------------------------------------------------------*/ + +/* + * send events to all subscribed ports + */ +static void queue_broadcast_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop) +{ + snd_seq_event_t sev; + + sev = *ev; + + sev.flags = SNDRV_SEQ_TIME_STAMP_TICK|SNDRV_SEQ_TIME_MODE_ABS; + sev.time.tick = q->timer->tick.cur_tick; + sev.queue = q->queue; + sev.data.queue.queue = q->queue; + + if (from_timer_port) { + /* broadcast events from Timer port */ + sev.source.client = SNDRV_SEQ_CLIENT_SYSTEM; + sev.source.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; + sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop); + } +#ifdef SNDRV_SEQ_SYNC_SUPPORT + if (q->info_flags & SNDRV_SEQ_QUEUE_FLG_SYNC) { + /* broadcast events also to slave clients */ + sev.source = q->sync_port; + sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop); + } +#endif +} + +/* + * process a received queue-control event. + * this function is exported for seq_sync.c. + */ +void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop) +{ + switch (ev->type) { + case SNDRV_SEQ_EVENT_START: + snd_seq_prioq_leave(q->tickq, ev->source.client, 1); + snd_seq_prioq_leave(q->timeq, ev->source.client, 1); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_clear(q); +#endif + snd_seq_timer_start(q->timer); + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + snd_seq_sync_check(q, 0, atomic, hop); /* trigger the first signal */ +#endif + break; + + case SNDRV_SEQ_EVENT_CONTINUE: + snd_seq_timer_continue(q->timer); + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + snd_seq_sync_check(q, 0, atomic, hop); +#endif + break; + + case SNDRV_SEQ_EVENT_STOP: + snd_seq_timer_stop(q->timer); + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + break; + + case SNDRV_SEQ_EVENT_TEMPO: + snd_seq_timer_set_tempo(q->timer, ev->data.queue.param.value); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_update_tempo(q); +#endif + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + break; + + case SNDRV_SEQ_EVENT_SETPOS_TICK: + if (snd_seq_timer_set_position_tick(q->timer, ev->data.queue.param.time.tick) == 0) { +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_update_tick(q, 0, atomic, hop); +#endif + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + } + break; + + case SNDRV_SEQ_EVENT_SETPOS_TIME: + if (snd_seq_timer_set_position_time(q->timer, ev->data.queue.param.time.time) == 0) { +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_update_time(q, 0, atomic, hop); +#endif + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + } + break; + case SNDRV_SEQ_EVENT_QUEUE_SKEW: + if (snd_seq_timer_set_skew(q->timer, + ev->data.queue.param.skew.value, + ev->data.queue.param.skew.base) == 0) { + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + } + break; + } +} + + +/* + * Queue control via timer control port: + * this function is exported as a callback of timer port. + */ +int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop) +{ + queue_t *q; + + snd_assert(ev != NULL, return -EINVAL); + q = queueptr(ev->data.queue.queue); + + if (q == NULL) + return -EINVAL; + + if (! queue_access_lock(q, ev->source.client)) { + queuefree(q); + return -EPERM; + } + + snd_seq_queue_process_event(q, ev, 1, atomic, hop); + + queue_access_unlock(q); + queuefree(q); + return 0; +} + + +/*----------------------------------------------------------------*/ + +/* exported to seq_info.c */ +void snd_seq_info_queues_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + int i, bpm; + queue_t *q; + seq_timer_t *tmr; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + + tmr = q->timer; + if (tmr->tempo) + bpm = 60000000 / tmr->tempo; + else + bpm = 0; + + snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name); + snd_iprintf(buffer, "owned by client : %d\n", q->owner); + snd_iprintf(buffer, "lock status : %s\n", q->locked ? "Locked" : "Free"); + snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq)); + snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq)); + snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped"); + snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq); + snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo); + snd_iprintf(buffer, "current BPM : %d\n", bpm); + snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec); + snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick); + snd_iprintf(buffer, "\n"); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_info_read(q, buffer); +#endif + queuefree(q); + } +} diff -Nru a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_queue.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,154 @@ +/* + * ALSA sequencer Queue handling + * Copyright (c) 1998-1999 by Frank van de Pol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_QUEUE_H +#define __SND_SEQ_QUEUE_H + +#include "seq_memory.h" +#include "seq_prioq.h" +#include "seq_timer.h" +#include "seq_lock.h" +#ifdef SNDRV_SEQ_SYNC_SUPPORT +#include "seq_sync.h" +#endif +#include +#include + +#define SEQ_QUEUE_NO_OWNER (-1) + +struct _snd_seq_queue { + int queue; /* queue number */ + + char name[64]; /* name of this queue */ + + prioq_t *tickq; /* midi tick event queue */ + prioq_t *timeq; /* real-time event queue */ + + seq_timer_t *timer; /* time keeper for this queue */ + int owner; /* client that 'owns' the timer */ + unsigned int locked:1, /* timer is only accesibble by owner if set */ + klocked:1, /* kernel lock (after START) */ + check_again:1, + check_blocked:1; + + unsigned int flags; /* status flags */ + unsigned int info_flags; /* info for sync */ + + spinlock_t owner_lock; + spinlock_t check_lock; + + /* clients which uses this queue (bitmap) */ + unsigned int clients_bitmap[SNDRV_SEQ_MAX_CLIENTS/sizeof(unsigned int)]; + unsigned int clients; /* users of this queue */ + struct semaphore timer_mutex; + + snd_use_lock_t use_lock; + + struct tasklet_struct taskq; + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + struct list_head master_head; /* list of masters */ + queue_sync_t slave; /* slave (exclusive) */ + + rwlock_t master_lock; + rwlock_t slave_lock; + + snd_seq_addr_t sync_port; /* address of the attached sync port */ +#endif +}; + + +/* get the number of current queues */ +int snd_seq_queue_get_cur_queues(void); + +/* init queues structure */ +int snd_seq_queues_init(void); + +/* delete queues */ +void snd_seq_queues_delete(void); + + +/* create new queue (constructor) */ +int snd_seq_queue_alloc(int client, int locked, unsigned int flags); + +/* delete queue (destructor) */ +int snd_seq_queue_delete(int client, int queueid); + +/* notification that client has left the system */ +void snd_seq_queue_client_termination(int client); + +/* final stage */ +void snd_seq_queue_client_leave(int client); + +/* enqueue a event received from one the clients */ +int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop); + +/* Remove events */ +void snd_seq_queue_client_leave_cells(int client); +void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info); + +/* return pointer to queue structure for specified id */ +queue_t *queueptr(int queueid); +/* unlock */ +#define queuefree(q) snd_use_lock_free(&(q)->use_lock) + +/* return the (first) queue matching with the specified name */ +queue_t *snd_seq_queue_find_name(char *name); + +/* check single queue and dispatch events */ +void snd_seq_check_queue(queue_t *q, int atomic, int hop); + +/* access to queue's parameters */ +int snd_seq_queue_check_access(int queueid, int client); +int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info); +int snd_seq_queue_set_owner(int queueid, int client, int locked); +int snd_seq_queue_set_locked(int queueid, int client, int locked); +int snd_seq_queue_timer_open(int queueid); +int snd_seq_queue_timer_close(int queueid); +int snd_seq_queue_use(int queueid, int client, int use); +int snd_seq_queue_is_used(int queueid, int client); + +int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop); +void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop); + +/* + * 64bit division - for sync stuff.. + */ +#if defined(i386) || defined(i486) + +#define udiv_qrnnd(q, r, n1, n0, d) \ + __asm__ ("divl %4" \ + : "=a" ((u32)(q)), \ + "=d" ((u32)(r)) \ + : "0" ((u32)(n0)), \ + "1" ((u32)(n1)), \ + "rm" ((u32)(d))) + +#define u64_div(x,y,q) do {u32 __tmp; udiv_qrnnd(q, __tmp, (x)>>32, x, y);} while (0) +#define u64_mod(x,y,r) do {u32 __tmp; udiv_qrnnd(__tmp, q, (x)>>32, x, y);} while (0) +#define u64_divmod(x,y,q,r) udiv_qrnnd(q, r, (x)>>32, x, y) + +#else +#define u64_div(x,y,q) ((q) = (u32)((u64)(x) / (u64)(y))) +#define u64_mod(x,y,r) ((r) = (u32)((u64)(x) % (u64)(y))) +#define u64_divmod(x,y,q,r) (u64_div(x,y,q), u64_mod(x,y,r)) +#endif + + +#endif diff -Nru a/sound/core/seq/seq_sync.c b/sound/core/seq/seq_sync.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_sync.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1006 @@ +/* + * ALSA sequencer queue synchronization routine + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include + +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_clientmgr.h" +#include "seq_fifo.h" +#include "seq_timer.h" +#include "seq_info.h" + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + +#define FOR_EACH_LIST(var,list) \ +for (var = (list)->next; var != list; var = var->next) + +/* + * callbacks + */ +static int event_input_sync(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop); +static int queue_master_add(void *private_data, snd_seq_port_subscribe_t *subs); +static int queue_master_remove(void *private_data, snd_seq_port_subscribe_t *subs); +static void queue_delete_all_masters(queue_t *q); +static int queue_slave_set(void *private_data, snd_seq_port_subscribe_t *subs); +static int queue_slave_reset(void *private_data, snd_seq_port_subscribe_t *subs); +static void queue_sync_close_parser(queue_sync_t *sync, int slave); + +/* + * pre-defined event parsers + */ + +extern seq_sync_parser_t snd_seq_midi_clock_parser; /* seq_midi_clock.c */ +extern seq_sync_parser_t snd_seq_mtc_parser; /* seq_mtc.c */ +extern seq_sync_parser_t snd_seq_dtl_parser; /* seq_dtl.c */ + +static seq_sync_parser_t *event_parsers[] = { + &snd_seq_midi_clock_parser, + &snd_seq_mtc_parser, + &snd_seq_dtl_parser, + NULL +}; + +/* + * create a sync port corresponding to the specified queue + */ +int snd_seq_sync_create_port(queue_t *queue) +{ + snd_seq_port_info_t port; + snd_seq_port_callback_t pcallbacks; + + memset(&pcallbacks, 0, sizeof(pcallbacks)); + memset(&port, 0, sizeof(port)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.event_input = event_input_sync; + pcallbacks.subscribe = queue_master_add; + pcallbacks.unsubscribe = queue_master_remove; + pcallbacks.use = queue_slave_set; + pcallbacks.unuse = queue_slave_reset; + pcallbacks.private_data = queue; + pcallbacks.callback_all = 1; /* call callbacks at each subscription */ + port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ| + SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE| + SNDRV_SEQ_PORT_CAP_DUPLEX| + SNDRV_SEQ_PORT_CAP_SYNC_READ|SNDRV_SEQ_PORT_CAP_SYNC_WRITE; + port.type = 0; + sprintf(port.name, "Sync Queue %d", queue->queue); + port.kernel = &pcallbacks; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + port.port = snd_seq_queue_sync_port(queue->queue); + if (snd_seq_kernel_client_ctl(SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_IOCTL_CREATE_PORT, &port) < 0) + return -ENOMEM; + queue->sync_port.client = SNDRV_SEQ_CLIENT_SYSTEM; + queue->sync_port.port = port.port; + return 0; +} + +/* + * delete attached sync port to the queue + */ +int snd_seq_sync_delete_port(queue_t *queue) +{ + snd_seq_port_info_t port; + + memset(&port, 0, sizeof(port)); + port.client = queue->sync_port.client; + port.port = queue->sync_port.port; + if (snd_seq_kernel_client_ctl(SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_IOCTL_DELETE_PORT, &port) < 0) + return -ENOMEM; + queue_delete_all_masters(queue); + queue_sync_close_parser(&queue->slave, 1); + return 0; +} + + +/* + * send a sync signal to the sync slave client + */ +static void queue_send_sync_event(queue_t *q, queue_sync_t *master, int type, int atomic, int hop) +{ + snd_seq_event_t event; + + memset(&event, 0, sizeof(event)); + + event.flags = SNDRV_SEQ_TIME_MODE_ABS; + /* since we use direct delivery, we have to convert time stamp here.. */ + switch (master->format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + event.flags |= SNDRV_SEQ_TIME_STAMP_TICK; + event.time.tick = q->timer->tick.cur_tick; + break; + case SNDRV_SEQ_SYNC_TIME: + event.flags |= SNDRV_SEQ_TIME_STAMP_REAL; + event.time.time = q->timer->cur_time; + break; + } + event.type = type; + event.data.queue.queue = q->queue; + event.data.queue.sync_format = master->format; + event.data.queue.sync_time_format = master->time_format; + event.data.queue.param.position = master->counter; + event.source = q->sync_port; + event.dest = master->addr; + if (master->parser) { + snd_seq_event_t newev; + newev = event; + if (master->parser->out.sync(master->parser_arg, &event, &newev) > 0) { + snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &newev, atomic, hop); + return; + } + } + snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &event, atomic, hop); +} + +/* + * initialize the sync position + */ +static void queue_sync_clear(queue_sync_t *sync) +{ + memset(&sync->cur_time, 0, sizeof(sync->cur_time)); + sync->counter = 0; + sync->sync_tick.cur_tick = 0; + sync->sync_tick.fraction = 0; +} + +/* + * initialize all sync positions + */ +void snd_seq_sync_clear(queue_t *q) +{ + struct list_head *head; + + /* clear master positions */ + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + queue_sync_clear(master); + } + read_unlock(&q->master_lock); + read_lock(&q->slave_lock); + queue_sync_clear(&q->slave); + read_unlock(&q->slave_lock); +} + + +/* + * change tick resolution of sync master/slave + */ +static void queue_sync_set_tick_resolution(queue_t *q, queue_sync_t *sync) +{ + unsigned int tempo, ppq; + tempo = q->timer->tempo; + if (sync->param.tick.ppq == 0) + ppq = q->timer->ppq; + else + ppq = sync->param.tick.ppq; + snd_seq_timer_set_tick_resolution(&sync->sync_tick, tempo, ppq, sync->param.tick.ticks); +} + + +/* + * update sync-master resolutions + */ +void snd_seq_sync_update_tempo(queue_t *q) +{ + struct list_head *head; + + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + if (master->format & SNDRV_SEQ_SYNC_TICK) + queue_sync_set_tick_resolution(q, master); + } + read_unlock(&q->master_lock); + read_lock(&q->slave_lock); + if (q->slave.format & SNDRV_SEQ_SYNC_TICK) + queue_sync_set_tick_resolution(q, &q->slave); + read_unlock(&q->slave_lock); +} + + +/* + * change the tick position from the current tick of the queue + */ +static void queue_sync_change_tick(queue_t *q, queue_sync_t *sync) +{ + if (sync->param.tick.ppq == 0) + sync->counter = q->timer->tick.cur_tick; + else + sync->counter = (q->timer->tick.cur_tick * sync->param.tick.ppq) / q->timer->ppq; + sync->counter /= sync->param.tick.ticks; + sync->sync_tick.cur_tick = sync->counter; + sync->sync_tick.fraction = 0; +} + +/* + * change the time position from the current time of the queue + */ +static void queue_sync_change_time(queue_t *q, queue_sync_t *sync) +{ + /* we need 64bit calculation here.. */ + u64 nsec; + + nsec = q->timer->cur_time.tv_sec; + nsec *= 1000000000UL; + nsec += q->timer->cur_time.tv_nsec; + u64_div(nsec, sync->param.time.resolution, sync->counter); + sync->counter *= sync->param.time.subframes; + sync->cur_time = q->timer->cur_time; +} + + +/* + * update the tick position of all sync + */ +void snd_seq_sync_update_tick(queue_t *q, int master_only, int atomic, int hop) +{ + struct list_head *head; + + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + if (master->format & SNDRV_SEQ_SYNC_TICK) { + queue_sync_change_tick(q, master); + queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC_POS, atomic, hop); /* broadcast to client */ + } + } + read_unlock(&q->master_lock); + if (master_only) + return; + read_lock(&q->slave_lock); + if (q->slave.format & SNDRV_SEQ_SYNC_TICK) + queue_sync_change_tick(q, &q->slave); + read_unlock(&q->slave_lock); +} + +/* + * update the time position of all sync + */ +void snd_seq_sync_update_time(queue_t *q, int master_only, int atomic, int hop) +{ + struct list_head *head; + + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + if (master->format & SNDRV_SEQ_SYNC_TIME) { + queue_sync_change_time(q, master); + queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC_POS, atomic, hop); + } + } + read_unlock(&q->master_lock); + if (master_only) + return; + read_lock(&q->slave_lock); + if (q->slave.format & SNDRV_SEQ_SYNC_TIME) + queue_sync_change_time(q, &q->slave); + read_unlock(&q->slave_lock); +} + + +/* + * check the current timer value and send sync messages if the sync + * time is elapsed + */ +static void queue_master_check(queue_t *q, unsigned long ticks, int atomic, int hop) +{ + struct list_head *head; + + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + switch (master->format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + snd_seq_timer_update_tick(&master->sync_tick, ticks); + while (master->sync_tick.cur_tick >= master->counter) { + queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC, atomic, hop); + master->counter++; + } + break; + case SNDRV_SEQ_SYNC_TIME: + while (snd_seq_compare_real_time(&q->timer->cur_time, &master->cur_time)) { + queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC, atomic, hop); + snd_seq_inc_time_nsec(&master->cur_time, master->resolution); + master->counter++; + } + break; + } + } + read_unlock(&q->master_lock); +} + + +/* + * slave stuff + */ + +/* + * update tick + */ +static void queue_slave_check(queue_t *q, unsigned long ticks) +{ + switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + snd_seq_timer_update_tick(&q->slave.sync_tick, ticks); + break; + case SNDRV_SEQ_SYNC_TIME: + /* nothing */ + break; + } +} + +/* + * slave synchronization in real-time unit + */ +static int queue_slave_sync_time(queue_t *q, unsigned int position) +{ + struct timeval tm; + long diff_time, new_period; + queue_sync_t *sync = &q->slave; + sndrv_seq_queue_time_sync_t *p = &sync->param.time; + seq_timer_t *tmr = q->timer; + u64 external_counter, tmp; + + do_gettimeofday(&tm); + if (tmr->sync_start) { + /* XXX: we should use 64bit for diff_time, too. */ + diff_time = (tm.tv_sec - tmr->sync_last_tm.tv_sec) * 1000000 + + ((long)tm.tv_usec - (long)tmr->sync_last_tm.tv_usec); + diff_time = (p->x0 * tmr->sync_time_diff + p->x1 * (diff_time * 1000)) / (p->x0 + p->x1); +#define MIN_DIFF_TIME 1000 /* 1ms minimum */ + if (diff_time < MIN_DIFF_TIME) + diff_time = MIN_DIFF_TIME; + tmr->sync_time_diff = diff_time; + tmp = (u64)tmr->base_period * (u64)sync->resolution; + u64_div(tmp, diff_time, new_period); + + /* phase adjustment */ + external_counter = position; + external_counter *= sync->resolution; + + /* calculate current time */ + tmp = snd_seq_timer_get_cur_nsec(tmr, &tm); + + if (external_counter > tmp) { + tmp = external_counter - tmp; + if (tmp < p->max_time_diff) { + /* locked */ + int hz = p->phase_correct_time / tmr->base_period; + diff_time = (u32)tmp; + diff_time /= hz; + new_period += diff_time; + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + } + } else { + tmp = tmp - external_counter; + if (tmp == 0) + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + else if (tmp < p->max_time_diff) { + /* locked */ + int hz = p->phase_correct_time / tmr->base_period; + diff_time = (u32)tmp; + diff_time /= hz; + if (new_period - diff_time > MIN_DIFF_TIME) { + new_period -= diff_time; + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + } else + q->flags |= SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + } + } + tmr->period = new_period; + } else { + tmr->sync_start = 1; + tmr->sync_time_diff = sync->resolution; + } + tmr->sync_last_tm = tm; + sync->counter = position; + + return 0; +} + +/* + * slave synchronization in tick unit + */ +static int queue_slave_sync_tick(queue_t *q, unsigned int position) +{ + struct timeval tm; + long diff_time, tick_diff; + unsigned int tick_time; + queue_sync_t *sync = &q->slave; + seq_timer_t *tmr = q->timer; + sndrv_seq_queue_tick_sync_t *p = &sync->param.tick; + + do_gettimeofday(&tm); + if (tmr->sync_start) { + /* XXX: diff_time should be 64bit for enough long sync period.. */ + diff_time = (tm.tv_sec - tmr->sync_last_tm.tv_sec) * 1000000 + + ((long)tm.tv_usec - (long)tmr->sync_last_tm.tv_usec); + diff_time *= 1000; /* in nsec */ + tick_time = (p->x0 * sync->sync_tick.resolution + + p->x1 * diff_time) / (p->x0 + p->x1); + /* phase adjustment */ + tick_diff = (long)position - (long)sync->sync_tick.cur_tick; + if (tick_diff != 0) { + if (tick_diff >= -p->max_tick_diff && + tick_diff <= p->max_tick_diff) { + /* locked */ + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + tick_time = (tick_time * p->max_tick_diff2) / + (p->max_tick_diff2 + tick_diff); + } else { + /* sync lost.. freewheeling */ + q->flags |= SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + } + } else + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + +#define MIN_TICK_TIME 1000 /* 1ms */ + if (tick_time < MIN_TICK_TIME) + tick_time = MIN_TICK_TIME; + + sync->sync_tick.resolution = tick_time; + snd_seq_timer_update_tick(&sync->sync_tick, 0); + if (p->ppq) + tmr->tick.resolution = (tick_time * p->ppq) / tmr->ppq; + else + tmr->tick.resolution = tick_time; + snd_seq_timer_update_tick(&tmr->tick, 0); + tmr->tempo = (tmr->tick.resolution * tmr->ppq) / 1000; + + } else + tmr->sync_start = 1; + tmr->sync_last_tm = tm; + + sync->counter = position; + + return 0; +} + + +/* + */ +static void queue_slave_jump_to_time(queue_t *q, unsigned int position, int atomic, int hop) +{ + u64 nsec; + queue_sync_t *sync = &q->slave; + + q->slave.counter = position; + nsec = sync->counter; + nsec *= sync->resolution; + u64_divmod(nsec, 1000000000, sync->cur_time.tv_sec, sync->cur_time.tv_nsec); + q->timer->cur_time = sync->cur_time; + + /* update master */ + snd_seq_sync_update_time(q, 1, atomic, hop); +} + +static void queue_slave_jump_to_tick(queue_t *q, unsigned int position, int atomic, int hop) +{ + unsigned int tick; + queue_sync_t *sync = &q->slave; + + sync->counter = position; + sync->sync_tick.cur_tick = sync->counter; + sync->sync_tick.fraction = 0; + + /* update queue timer */ + if (sync->param.tick.ppq == 0) + tick = sync->counter; + else + tick = sync->counter * q->timer->ppq / sync->param.tick.ppq; + q->timer->tick.cur_tick = tick * sync->param.tick.ticks; + q->timer->tick.fraction = 0; + + /* update master */ + snd_seq_sync_update_tick(q, 1, atomic, hop); +} + + +/* + * event input callback + */ +static int event_input_sync(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop) +{ + queue_t *q = private_data; + unsigned long flags; + snd_seq_event_t newev; + + snd_assert(q != NULL, return -ENXIO); + + /* lock the queue owner access.. */ + spin_lock_irqsave(&q->owner_lock, flags); + q->klocked = 1; + spin_unlock_irqrestore(&q->owner_lock, flags); + + read_lock(&q->slave_lock); + if (q->slave.format) { + if (q->slave.parser) { + memset(&newev, 0, sizeof(newev)); + if (q->slave.parser->in.sync(q->slave.parser_arg, ev, &newev) > 0) + ev = &newev; + } + } + if (ev->type == SNDRV_SEQ_EVENT_SYNC) { + /* slave signal received */ + switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + queue_slave_sync_tick(q, ev->data.queue.param.position); + break; + case SNDRV_SEQ_SYNC_TIME: + queue_slave_sync_time(q, ev->data.queue.param.position); + break; + } + } else if (ev->type == SNDRV_SEQ_EVENT_SYNC_POS) { + /* jump to position */ + switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + if (q->timer->running) + queue_slave_sync_tick(q, ev->data.queue.param.position); + else + queue_slave_jump_to_tick(q, ev->data.queue.param.position, atomic, hop); + break; + case SNDRV_SEQ_SYNC_TIME: + if (q->timer->running) + queue_slave_sync_time(q, ev->data.queue.param.position); + else + queue_slave_jump_to_time(q, ev->data.queue.param.position, atomic, hop); + break; + } + } else { + /* control queue */ + snd_seq_queue_process_event(q, ev, 0, atomic, hop); + } + read_unlock(&q->slave_lock); + + /* unlock */ + spin_lock_irqsave(&q->owner_lock, flags); + q->klocked = 0; + spin_unlock_irqrestore(&q->owner_lock, flags); + + return 0; +} + + +/* + * initialize sync parameters + */ +static int queue_param_init(queue_t *q, queue_sync_t *sync, + snd_seq_addr_t *addr, sndrv_seq_queue_sync_t *info, + int slave) +{ + seq_sync_parser_t *parser, **list; + + sync->format = info->format; + sync->time_format = info->time_format; + *sync->opt_info = *info->info; + sync->addr = *addr; + /* copy params */ + if (info->format&SNDRV_SEQ_SYNC_TICK) + sync->param.tick=info->param.tick; + else + sync->param.time=info->param.time; + + sync->parser = NULL; + sync->parser_arg = NULL; + for (list = event_parsers; (parser = *list) != NULL; list++) { + if (parser->format == sync->format) { + int err; + if (slave) + err = parser->in.open(sync, &sync->parser_arg); + else + err = parser->out.open(sync, &sync->parser_arg); + if (err < 0) + return err; + sync->parser = parser; + break; + } + } + + switch (sync->format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + if (sync->param.tick.ppq > 200) + goto __error; + if (sync->param.tick.ticks == 0) + sync->param.tick.ticks = 1; + queue_sync_set_tick_resolution(q, sync); + /* sync slave parameters -- will be configurable */ + sync->param.tick.x0 = 4; + sync->param.tick.x1 = 1; + sync->param.tick.max_tick_diff = 50; + sync->param.tick.max_tick_diff2 = sync->param.tick.max_tick_diff * 2; + break; + case SNDRV_SEQ_SYNC_TIME: + sync->resolution = sync->param.time.resolution; + if (sync->param.time.subframes == 0) + goto __error; + sync->resolution /= sync->param.time.subframes; + if (sync->resolution < 1000000) /* minimum = 1ms */ + goto __error; + /* sync slave parameters -- will be configurable */ + sync->param.time.x0 = 2; + sync->param.time.x1 = 1; + sync->param.time.max_time_diff = 1000000000UL; /* 1s */ + sync->param.time.phase_correct_time = 100000000UL; /* 0.1s */ + break; + default: + snd_printd("seq_sync: invalid format %x\n", sync->format); + goto __error; + } + return 0; + +__error: + queue_sync_close_parser(sync, slave); + return -EINVAL; +} + + +/* + * close event parser if exists + */ +static void queue_sync_close_parser(queue_sync_t *sync, int slave) +{ + if (sync->parser == NULL) + return; + if (slave) { + if (sync->parser->in.close) + sync->parser->in.close(sync->parser_arg); + else if (sync->parser_arg) + kfree(sync->parser_arg); + } else { + if (sync->parser->out.close) + sync->parser->out.close(sync->parser_arg); + else if (sync->parser_arg) + kfree(sync->parser_arg); + } + sync->parser = NULL; + sync->parser_arg = NULL; +} + + +/* + * add to master list + */ +static int queue_master_add(void *private_data, snd_seq_port_subscribe_t *subs) +{ + queue_t *q = private_data; + queue_sync_t *master; + unsigned long flags; + int err; + + snd_assert(q != NULL, return -EINVAL); + if (! subs->sync) + return -EINVAL; + master = snd_kcalloc(sizeof(*master), GFP_KERNEL); + if (master == NULL) + return -ENOMEM; + err = queue_param_init(q, master, &subs->dest, &subs->opt.sync_info, 0); + if (err < 0) { + kfree(master); + return err; + } + write_lock_irqsave(&q->master_lock, flags); + list_add(&master->list, &q->master_head); + write_unlock_irqrestore(&q->master_lock, flags); + + return 0; +} + +/* + * remove master + */ +static int queue_master_remove(void *private_data, snd_seq_port_subscribe_t *subs) +{ + queue_t *q = private_data; + sndrv_seq_queue_sync_t *info; + snd_seq_addr_t *addr; + struct list_head *head; + unsigned long flags; + + snd_assert(q != NULL, return -EINVAL); + if (! subs->sync) + return -EINVAL; + info = &subs->opt.sync_info; + addr = &subs->dest; + + write_lock_irqsave(&q->master_lock, flags); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + if (master->format == info->format && + master->addr.client == addr->client && + master->addr.port == addr->port) { + list_del(&master->list); + write_unlock_irqrestore(&q->master_lock, flags); + queue_sync_close_parser(master, 0); + kfree(master); + return 0; + } + } + write_unlock_irqrestore(&q->master_lock, flags); + snd_printd("seq_queue: can't find master from %d.%d format %0x\n", addr->client, addr->port, info->format); + return -ENXIO; +} + +/* remove all master connections if any exist */ +static void queue_delete_all_masters(queue_t *q) +{ + struct list_head *head; + unsigned long flags; + + write_lock_irqsave(&q->master_lock, flags); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + list_del(&master->list); + queue_sync_close_parser(master, 0); + kfree(master); + } + write_unlock_irqrestore(&q->master_lock, flags); +} + +/* + * set slave mode + */ +static int queue_slave_set(void *private_data, snd_seq_port_subscribe_t *subs) +{ + queue_t *q = private_data; + unsigned long flags; + int err; + + snd_assert(q != NULL, return -EINVAL); + if (! subs->sync) + return -EINVAL; + write_lock_irqsave(&q->slave_lock, flags); + if (q->slave.format) { + write_unlock_irqrestore(&q->slave_lock, flags); + return -EBUSY; + } + err = queue_param_init(q, &q->slave, &subs->sender, + &subs->opt.sync_info, 1); + if (err < 0) { + q->slave.format = 0; + write_unlock_irqrestore(&q->slave_lock, flags); + return err; + } + write_unlock_irqrestore(&q->slave_lock, flags); + return 0; +} + +/* + * remove slave mode + */ +static int queue_slave_reset(void *private_data, snd_seq_port_subscribe_t *subs) +{ + queue_t *q = private_data; + unsigned long flags; + + snd_assert(q != NULL, return -EINVAL); + if (! subs->sync) + return -EINVAL; + write_lock_irqsave(&q->slave_lock, flags); + if (q->slave.addr.client == subs->sender.client && + q->slave.addr.port == subs->sender.port) { + q->slave.format = 0; + queue_sync_close_parser(&q->slave, 1); + write_unlock_irqrestore(&q->slave_lock, flags); + return 0; + } + write_unlock_irqrestore(&q->slave_lock, flags); + snd_printd("seq_queue: can't match slave condition\n"); + return -ENXIO; +} + + +/* + * sync check + * this function is called at each timer interrupt. + */ + +void snd_seq_sync_check(queue_t *q, unsigned long resolution, int atomic, int hop) +{ + queue_master_check(q, resolution, atomic, hop); + queue_slave_check(q, resolution); +} + + +/* + * support functions for SMPTE time frame + */ +static unsigned int linear_time_to_position(sndrv_seq_time_frame_t time, + int nframes, int nsubs) +{ + unsigned int count; + count = time.hour * 60 + time.min; + count = count * 60 + time.sec; + count = count * nframes + time.frame; + count = count * nsubs + time.subframe; + return count; +} + +static sndrv_seq_time_frame_t linear_position_to_time(unsigned int count, + int nframes, int nsubs) +{ + sndrv_seq_time_frame_t time; + time.subframe = count % nsubs; + count /= nsubs; + time.hour = count / (3600 * nframes); + count %= 3600 * nframes; + time.min = count / (60 * nframes); + count %= 60 * nframes; + time.sec = count / nframes; + time.frame = count % nframes; + return time; +} + +/* drop frame - only 30fps */ +#define NFRAMES 30 +#define FRAMES_PER_MIN (NFRAMES * 60 - 2) +#define FRAMES_PER_10MIN (FRAMES_PER_MIN * 10 + 2) +#define FRAMES_PER_HOUR (FRAMES_PER_10MIN * 6) + +static unsigned int drop_time_to_position(sndrv_seq_time_frame_t time, int nsubs) +{ + unsigned int count, min; + + min = time.min % 10; + count = time.frame; + if (min > 0) { + if (time.sec == 0 && time.frame < 2) + count = 2; + } + count += time.sec * NFRAMES; + count += min * FRAMES_PER_MIN; + count += (time.min / 10) * FRAMES_PER_10MIN; + count += time.hour * (FRAMES_PER_HOUR); + count *= nsubs; + count += time.subframe; + + return count; +} + +static sndrv_seq_time_frame_t drop_position_to_time(int count, int nsubs) +{ + unsigned int min10; + sndrv_seq_time_frame_t time; + + time.subframe = count % nsubs; + count /= nsubs; + min10 = count / FRAMES_PER_10MIN; + time.hour = min10 / 6; + min10 %= 6; + count %= FRAMES_PER_10MIN; + if (count < 2) { + time.min = min10 * 10; + time.sec = 0; + } else { + count -= 2; + time.min = count / FRAMES_PER_MIN; + time.min += min10 * 10; + count %= FRAMES_PER_MIN; + count += 2; + time.sec = count / NFRAMES; + count %= NFRAMES; + } + time.frame = count; + + return time; +} + +/* convert from position counter to time frame */ +sndrv_seq_time_frame_t snd_seq_position_to_time_frame(int format, unsigned int nsubs, unsigned int pos) +{ + switch (format) { + case SNDRV_SEQ_SYNC_FPS_24: + return linear_position_to_time(pos, 24, nsubs); + case SNDRV_SEQ_SYNC_FPS_25: + return linear_position_to_time(pos, 25, nsubs); + case SNDRV_SEQ_SYNC_FPS_30_NDP: + return linear_position_to_time(pos, 30, nsubs); + case SNDRV_SEQ_SYNC_FPS_30_DP: + default: + return drop_position_to_time(pos, nsubs); + } +} + +/* convert from position counter to time frame */ +unsigned int snd_seq_time_frame_to_position(int format, unsigned int nsubs, sndrv_seq_time_frame_t *rtime) +{ + switch (format) { + case SNDRV_SEQ_SYNC_FPS_24: + return linear_time_to_position(*rtime, 24, nsubs); + case SNDRV_SEQ_SYNC_FPS_25: + return linear_time_to_position(*rtime, 25, nsubs); + case SNDRV_SEQ_SYNC_FPS_30_NDP: + return linear_time_to_position(*rtime, 30, nsubs); + case SNDRV_SEQ_SYNC_FPS_30_DP: + default: + return drop_time_to_position(*rtime, nsubs); + } +} + +/* resolution in nsec */ +unsigned long snd_seq_get_smpte_resolution(int time_format) +{ + switch (time_format) { + case SNDRV_SEQ_SYNC_FPS_24: + return 1000000000UL / 24; + case SNDRV_SEQ_SYNC_FPS_25: + return 1000000000UL / 25; + case SNDRV_SEQ_SYNC_FPS_30_DP: + case SNDRV_SEQ_SYNC_FPS_30_NDP: + return (unsigned long)(1000000000.0/29.97); + } + return 0; +} + + +/* + * proc interface + */ + +static void print_sync_info(snd_info_buffer_t *buffer, queue_sync_t *sync) +{ + snd_iprintf(buffer, " [%s] ==> %d:%d\n", + (sync->format & SNDRV_SEQ_SYNC_TICK ? "tick" : "time"), + sync->addr.client, sync->addr.port); + snd_iprintf(buffer, " format 0x%0x / time_format %d\n", + sync->format, sync->time_format); + switch (sync->format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + snd_iprintf(buffer, " ppq: %d, ticks: %d\n", + sync->param.tick.ppq, sync->param.tick.ticks); + snd_iprintf(buffer, " resolution: %ld ns, position: %d\n", + sync->sync_tick.resolution, + sync->counter); + break; + case SNDRV_SEQ_SYNC_TIME: + snd_iprintf(buffer, " subframes %d, resolution: %ld ns, position: %d\n", + sync->param.time.subframes, + sync->resolution, + sync->counter); + break; + } +} + +void snd_seq_sync_info_read(queue_t *q, snd_info_buffer_t *buffer) +{ + struct list_head *head; + int count = 0; + + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + snd_iprintf(buffer, "master %d", count); + print_sync_info(buffer, master); + count++; + } + read_unlock(&q->master_lock); + if (q->slave.format) { + snd_iprintf(buffer, "slave"); + print_sync_info(buffer, &q->slave); + count++; + } + if (count) + snd_iprintf(buffer, "\n"); +} + +#endif /* SNDRV_SEQ_SYNC_SUPPORT */ diff -Nru a/sound/core/seq/seq_sync.h b/sound/core/seq/seq_sync.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_sync.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,79 @@ +/* + * Synchronization of ALSA sequencer queues + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_SYNC_H +#define __SND_SEQ_SYNC_H + +typedef struct snd_queue_sync queue_sync_t; +typedef struct snd_seq_sync_parser seq_sync_parser_t; +typedef void *seq_sync_arg_t; + +struct snd_queue_sync { + unsigned char format; + unsigned char time_format; + unsigned char opt_info[6]; /* optional info */ + snd_seq_addr_t addr; /* master/slave address */ + + unsigned int counter; /* current position */ + unsigned long resolution; /* resolution for time */ + snd_seq_real_time_t cur_time; /* current time */ + seq_timer_tick_t sync_tick; /* tick info */ + + union { + struct sndrv_seq_queue_tick_sync tick; + struct sndrv_seq_queue_time_sync time; + } param; + + seq_sync_parser_t *parser; + seq_sync_arg_t parser_arg; + + struct list_head list; +}; + + +struct seq_sync_parser_ops { + int (*open)(queue_sync_t *sync_info, seq_sync_arg_t *retp); + int (*sync)(seq_sync_arg_t arg, const snd_seq_event_t *src, snd_seq_event_t *ev); + int (*close)(seq_sync_arg_t arg); +}; + +struct snd_seq_sync_parser { + unsigned int format; /* supported format */ + struct seq_sync_parser_ops in; /* sync-in (slave) */ + struct seq_sync_parser_ops out; /* sync-out (mastering) */ +}; + +/* + * prototypes + */ +int snd_seq_sync_create_port(queue_t *queue); +int snd_seq_sync_delete_port(queue_t *queue); +void snd_seq_sync_clear(queue_t *q); +void snd_seq_sync_update_tempo(queue_t *q); +void snd_seq_sync_update_tick(queue_t *q, int master_only, int atomic, int hop); +void snd_seq_sync_update_time(queue_t *q, int master_only, int atomic, int hop); +void snd_seq_sync_check(queue_t *q, unsigned long resolution, int atomic, int hop); + +sndrv_seq_time_frame_t snd_seq_position_to_time_frame(int format, unsigned int nsubs, unsigned int pos); +unsigned int snd_seq_time_frame_to_position(int format, unsigned int nsubs, sndrv_seq_time_frame_t *rtime); +unsigned long snd_seq_get_smpte_resolution(int time_format); + + +#endif diff -Nru a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_system.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,183 @@ +/* + * ALSA sequencer System services Client + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include "seq_system.h" +#include "seq_timer.h" +#include "seq_queue.h" + +/* internal client that provide system services, access to timer etc. */ + +/* + * Port "Timer" + * - send tempo /start/stop etc. events to this port to manipulate the + * queue's timer. The queue address is specified in + * data.queue.queue. + * - this port supports subscription. The received timer events are + * broadcasted to all subscribed clients. The modified tempo + * value is stored on data.queue.value. + * The modifier client/port is not send. + * + * Port "Announce" + * - does not receive message + * - supports supscription. For each client or port attaching to or + * detaching from the system an announcement is send to the subscribed + * clients. + * + * Idea: the subscription mechanism might also work handy for distributing + * synchronisation and timing information. In this case we would ideally have + * a list of subscribers for each type of sync (time, tick), for each timing + * queue. + * + * NOTE: the queue to be started, stopped, etc. must be specified + * in data.queue.addr.queue field. queue is used only for + * scheduling, and no longer referred as affected queue. + * They are used only for timer broadcast (see above). + * -- iwai + */ + + +/* client id of our system client */ +static int sysclient = -1; + +/* port id numbers for this client */ +static int announce_port = -1; + + + +/* fill standard header data, source port & channel are filled in */ +static int setheader(snd_seq_event_t * ev, int client, int port) +{ + if (announce_port < 0) + return -ENODEV; + + memset(ev, 0, sizeof(snd_seq_event_t)); + + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; + + ev->source.client = sysclient; + ev->source.port = announce_port; + ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + + /* fill data */ + /*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/ + ev->data.addr.client = client; + ev->data.addr.port = port; + + return 0; +} + + +/* entry points for broadcasting system events */ +void snd_seq_system_broadcast(int client, int port, int type) +{ + snd_seq_event_t ev; + + if (setheader(&ev, client, port) < 0) + return; + ev.type = type; + snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0); +} + +/* entry points for broadcasting system events */ +int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev) +{ + ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; + ev->source.client = sysclient; + ev->source.port = announce_port; + ev->dest.client = client; + ev->dest.port = port; + return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0); +} + +/* call-back handler for timer events */ +static int event_input_timer(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop) +{ + return snd_seq_control_queue(ev, atomic, hop); +} + +/* register our internal client */ +int __init snd_seq_system_client_init(void) +{ + + snd_seq_client_callback_t callbacks; + snd_seq_port_callback_t pcallbacks; + snd_seq_client_info_t inf; + snd_seq_port_info_t port; + + memset(&callbacks, 0, sizeof(callbacks)); + memset(&pcallbacks, 0, sizeof(pcallbacks)); + memset(&inf, 0, sizeof(inf)); + memset(&port, 0, sizeof(port)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.event_input = event_input_timer; + + /* register client */ + callbacks.allow_input = callbacks.allow_output = 1; + sysclient = snd_seq_create_kernel_client(NULL, 0, &callbacks); + + /* set our name */ + inf.client = 0; + inf.type = KERNEL_CLIENT; + strcpy(inf.name, "System"); + snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &inf); + + /* register timer */ + strcpy(port.name, "Timer"); + port.capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */ + port.capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */ + port.kernel = &pcallbacks; + port.type = 0; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + port.addr.client = sysclient; + port.addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; + snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + + /* register announcement port */ + strcpy(port.name, "Announce"); + port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */ + port.kernel = NULL; + port.type = 0; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + port.addr.client = sysclient; + port.addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + announce_port = port.addr.port; + + return 0; +} + + +/* unregister our internal client */ +void __exit snd_seq_system_client_done(void) +{ + int oldsysclient = sysclient; + + if (oldsysclient >= 0) { + sysclient = -1; + announce_port = -1; + snd_seq_delete_kernel_client(oldsysclient); + } +} diff -Nru a/sound/core/seq/seq_system.h b/sound/core/seq/seq_system.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_system.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,46 @@ +/* + * ALSA sequencer System Client + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_SYSTEM_H +#define __SND_SEQ_SYSTEM_H + +#include + + +/* entry points for broadcasting system events */ +void snd_seq_system_broadcast(int client, int port, int type); + +#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START) +#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT) +#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE) +#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START) +#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT) +#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE) + +int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev); + +/* register our internal client */ +int snd_seq_system_client_init(void); + +/* unregister our internal client */ +void snd_seq_system_client_done(void); + + +#endif diff -Nru a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_timer.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,432 @@ +/* + * ALSA sequencer Timer + * Copyright (c) 1998-1999 by Frank van de Pol + * Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include "seq_timer.h" +#include "seq_queue.h" +#include "seq_info.h" + +extern int snd_seq_default_timer_class; +extern int snd_seq_default_timer_sclass; +extern int snd_seq_default_timer_card; +extern int snd_seq_default_timer_device; +extern int snd_seq_default_timer_subdevice; +extern int snd_seq_default_timer_resolution; + +#define SKEW_BASE 0x10000 /* 16bit shift */ + +void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks) +{ + if (tempo < 1000000) + tick->resolution = (tempo * 1000) / ppq; + else { + /* might overflow.. */ + unsigned int s; + s = tempo % ppq; + s = (s * 1000) / ppq; + tick->resolution = (tempo / ppq) * 1000; + tick->resolution += s; + } + if (tick->resolution <= 0) + tick->resolution = 1; + tick->resolution *= nticks; + snd_seq_timer_update_tick(tick, 0); +} + +/* create new timer (constructor) */ +seq_timer_t *snd_seq_timer_new(void) +{ + seq_timer_t *tmr; + + tmr = snd_kcalloc(sizeof(seq_timer_t), GFP_KERNEL); + if (tmr == NULL) { + snd_printd("malloc failed for snd_seq_timer_new() \n"); + return NULL; + } + spin_lock_init(&tmr->lock); + + /* reset setup to defaults */ + snd_seq_timer_defaults(tmr); + + /* reset time */ + snd_seq_timer_reset(tmr); + + return tmr; +} + +/* delete timer (destructor) */ +void snd_seq_timer_delete(seq_timer_t **tmr) +{ + seq_timer_t *t = *tmr; + *tmr = NULL; + + if (t == NULL) { + snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n"); + return; + } + t->running = 0; + + /* reset time */ + snd_seq_timer_stop(t); + snd_seq_timer_reset(t); + + kfree(t); +} + +void snd_seq_timer_defaults(seq_timer_t * tmr) +{ + /* setup defaults */ + tmr->ppq = 96; /* 96 PPQ */ + tmr->tempo = 500000; /* 120 BPM */ + snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1); + tmr->running = 0; + + tmr->type = SNDRV_SEQ_TIMER_ALSA; + tmr->alsa_id.dev_class = snd_seq_default_timer_class; + tmr->alsa_id.dev_sclass = snd_seq_default_timer_sclass; + tmr->alsa_id.card = snd_seq_default_timer_card; + tmr->alsa_id.device = snd_seq_default_timer_device; + tmr->alsa_id.subdevice = snd_seq_default_timer_subdevice; + tmr->preferred_resolution = snd_seq_default_timer_resolution; + + tmr->skew = tmr->skew_base = SKEW_BASE; +} + +void snd_seq_timer_reset(seq_timer_t * tmr) +{ + unsigned long flags; + + spin_lock_irqsave(&tmr->lock, flags); + + /* reset time & songposition */ + tmr->cur_time.tv_sec = 0; + tmr->cur_time.tv_nsec = 0; + + tmr->tick.cur_tick = 0; + tmr->tick.fraction = 0; + + spin_unlock_irqrestore(&tmr->lock, flags); +} + + +/* called by timer interrupt routine. the period time since previous invocation is passed */ +static void snd_seq_timer_interrupt(snd_timer_instance_t *timeri, + unsigned long resolution, + unsigned long ticks, void *data) +{ + unsigned long flags; + queue_t *q = (queue_t *)data; + seq_timer_t *tmr; + + if (q == NULL) + return; + tmr = q->timer; + if (tmr == NULL) + return; + if (!tmr->running) + return; + + resolution *= ticks; + if (tmr->skew != tmr->skew_base) { + /* FIXME: assuming skew_base = 0x10000 */ + resolution = (resolution >> 16) * tmr->skew + + (((resolution & 0xffff) * tmr->skew) >> 16); + } + + spin_lock_irqsave(&tmr->lock, flags); + + /* update timer */ + snd_seq_inc_time_nsec(&tmr->cur_time, resolution); + + /* calculate current tick */ + snd_seq_timer_update_tick(&tmr->tick, resolution); + + /* register actual time of this timer update */ + do_gettimeofday(&tmr->last_update); + + spin_unlock_irqrestore(&tmr->lock, flags); + + /* check queues and dispatch events */ + snd_seq_check_queue(q, 1, 0); +} + +/* set current tempo */ +int snd_seq_timer_set_tempo(seq_timer_t * tmr, int tempo) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + if (tempo <= 0) + return -EINVAL; + spin_lock_irqsave(&tmr->lock, flags); + if (tempo != tmr->tempo) { + tmr->tempo = tempo; + snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1); + } + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set current ppq */ +int snd_seq_timer_set_ppq(seq_timer_t * tmr, int ppq) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + if (ppq <= 0) + return -EINVAL; + spin_lock_irqsave(&tmr->lock, flags); + if (tmr->running && (ppq != tmr->ppq)) { + /* refuse to change ppq on running timers */ + /* because it will upset the song position (ticks) */ + spin_unlock_irqrestore(&tmr->lock, flags); + snd_printd("seq: cannot change ppq of a running timer\n"); + return -EBUSY; + } + + tmr->ppq = ppq; + snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1); + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set current tick position */ +int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + + spin_lock_irqsave(&tmr->lock, flags); + tmr->tick.cur_tick = position; + tmr->tick.fraction = 0; + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set current real-time position */ +int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + + snd_seq_sanity_real_time(&position); + spin_lock_irqsave(&tmr->lock, flags); + tmr->cur_time = position; + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set timer skew */ +int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + + /* FIXME */ + if (base != SKEW_BASE) { + snd_printd("invalid skew base 0x%x\n", base); + return -EINVAL; + } + spin_lock_irqsave(&tmr->lock, flags); + tmr->skew = skew; + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +int snd_seq_timer_open(queue_t *q) +{ + snd_timer_instance_t *t; + seq_timer_t *tmr; + char str[32]; + + tmr = q->timer; + snd_assert(tmr != NULL, return -EINVAL); + if (tmr->timeri) + return -EBUSY; + sprintf(str, "sequencer queue %i", q->queue); + if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { /* standard ALSA timer */ + if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) + tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER; + t = snd_timer_open(str, &tmr->alsa_id, q->queue); + if (t == NULL && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) { + if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL || + tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) { + snd_timer_id_t tid; + memset(&tid, 0, sizeof(tid)); + tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; + tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER; + tid.card = -1; + tid.device = SNDRV_TIMER_GLOBAL_SYSTEM; + t = snd_timer_open(str, &tid, q->queue); + } + if (t == NULL) { + snd_printk("fatal error: cannot create timer\n"); + return -ENODEV; + } + } + } else { + return -EINVAL; + } + t->callback = snd_seq_timer_interrupt; + t->callback_data = q; + t->flags |= SNDRV_TIMER_IFLG_AUTO; + tmr->timeri = t; + return 0; +} + +int snd_seq_timer_close(queue_t *q) +{ + seq_timer_t *tmr; + + tmr = q->timer; + snd_assert(tmr != NULL, return -EINVAL); + if (tmr->timeri) { + snd_timer_stop(tmr->timeri); + snd_timer_close(tmr->timeri); + tmr->timeri = NULL; + } + return 0; +} + +void snd_seq_timer_stop(seq_timer_t * tmr) +{ + if (! tmr->timeri) + return; + if (!tmr->running) + return; + tmr->running = 0; + snd_timer_stop(tmr->timeri); +} + +static int initialize_timer(seq_timer_t *tmr) +{ + snd_timer_t *t; + t = tmr->timeri->timer; + snd_assert(t, return -EINVAL); + + tmr->ticks = 1; + if (tmr->preferred_resolution && + ! (t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { + unsigned long r = t->hw.resolution; + if (! r && t->hw.c_resolution) + r = t->hw.c_resolution(t); + if (r) { + tmr->ticks = (unsigned int)(tmr->preferred_resolution / r); + if (! tmr->ticks) + tmr->ticks = 1; + } + } + tmr->initialized = 1; + return 0; +} + +void snd_seq_timer_start(seq_timer_t * tmr) +{ + if (! tmr->timeri) + return; + if (tmr->running) + snd_seq_timer_stop(tmr); + snd_seq_timer_reset(tmr); + if (initialize_timer(tmr) < 0) + return; + snd_timer_start(tmr->timeri, tmr->ticks); + tmr->running = 1; + do_gettimeofday(&tmr->last_update); +} + +void snd_seq_timer_continue(seq_timer_t * tmr) +{ + if (! tmr->timeri) + return; + if (tmr->running) + return; + if (! tmr->initialized) + if (initialize_timer(tmr) < 0) + return; + snd_timer_start(tmr->timeri, tmr->ticks); + tmr->running = 1; + do_gettimeofday(&tmr->last_update); +} + +/* return current 'real' time. use timeofday() to get better granularity. */ +snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr) +{ + snd_seq_real_time_t cur_time; + + cur_time = tmr->cur_time; + if (tmr->running) { + struct timeval tm; + int usec; + do_gettimeofday(&tm); + usec = (int)(tm.tv_usec - tmr->last_update.tv_usec); + if (usec < 0) { + cur_time.tv_nsec += (1000000 + usec) * 1000; + cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1; + } else { + cur_time.tv_nsec += usec * 1000; + cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec; + } + snd_seq_sanity_real_time(&cur_time); + } + + return cur_time; +} + +/* TODO: use interpolation on tick queue (will only be usefull for very + high PPQ values) */ +snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr) +{ + return tmr->tick.cur_tick; +} + + +/* exported to seq_info.c */ +void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int idx; + queue_t *q; + seq_timer_t *tmr; + snd_timer_instance_t *ti; + unsigned long resolution; + + for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) { + q = queueptr(idx); + if (q == NULL) + continue; + if ((tmr = q->timer) == NULL || + (ti = tmr->timeri) == NULL) { + queuefree(q); + continue; + } + snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name); + resolution = snd_timer_resolution(ti) * tmr->ticks; + snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000); + snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base); + queuefree(q); + } +} diff -Nru a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_timer.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,151 @@ +/* + * ALSA sequencer Timer + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_TIMER_H +#define __SND_SEQ_TIMER_H + +#include +#include + +typedef struct { + snd_seq_tick_time_t cur_tick; /* current tick */ + unsigned long resolution; /* time per tick in nsec */ + unsigned long fraction; /* current time per tick in nsec */ +} seq_timer_tick_t; + +typedef struct { + /* ... tempo / offset / running state */ + + unsigned int running:1, /* running state of queue */ + initialized:1; /* timer is initialized */ + + unsigned int tempo; /* current tempo, us/tick */ + int ppq; /* time resolution, ticks/quarter */ + + snd_seq_real_time_t cur_time; /* current time */ + seq_timer_tick_t tick; /* current tick */ + int tick_updated; + + int type; /* timer type */ + snd_timer_id_t alsa_id; /* ALSA's timer ID */ + snd_timer_instance_t *timeri; /* timer instance */ + unsigned int ticks; + unsigned long preferred_resolution; /* timer resolution */ + + unsigned int skew; + unsigned int skew_base; + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + int sync_start; + struct timeval sync_last_tm; + unsigned int sync_time_diff; +#endif + + struct timeval last_update; /* time of last clock update, used for interpolation */ + + spinlock_t lock; +} seq_timer_t; + + +/* create new timer (constructor) */ +extern seq_timer_t *snd_seq_timer_new(void); + +/* delete timer (destructor) */ +extern void snd_seq_timer_delete(seq_timer_t **tmr); + +void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks); + +/* */ +static inline void snd_seq_timer_update_tick(seq_timer_tick_t *tick, unsigned long resolution) +{ + if (tick->resolution > 0) { + tick->fraction += resolution; + tick->cur_tick += (unsigned int)(tick->fraction / tick->resolution); + tick->fraction %= tick->resolution; + } +} + + +/* compare timestamp between events */ +/* return 1 if a >= b; otherwise return 0 */ +static inline int snd_seq_compare_tick_time(snd_seq_tick_time_t *a, snd_seq_tick_time_t *b) +{ + /* compare ticks */ + return (*a >= *b); +} + +static inline int snd_seq_compare_real_time(snd_seq_real_time_t *a, snd_seq_real_time_t *b) +{ + /* compare real time */ + if (a->tv_sec > b->tv_sec) + return 1; + if ((a->tv_sec == b->tv_sec) && (a->tv_nsec >= b->tv_nsec)) + return 1; + return 0; +} + + +static inline void snd_seq_sanity_real_time(snd_seq_real_time_t *tm) +{ + while (tm->tv_nsec >= 1000000000) { + /* roll-over */ + tm->tv_nsec -= 1000000000; + tm->tv_sec++; + } +} + + +/* increment timestamp */ +static inline void snd_seq_inc_real_time(snd_seq_real_time_t *tm, snd_seq_real_time_t *inc) +{ + tm->tv_sec += inc->tv_sec; + tm->tv_nsec += inc->tv_nsec; + snd_seq_sanity_real_time(tm); +} + +static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long nsec) +{ + tm->tv_nsec += nsec; + snd_seq_sanity_real_time(tm); +} + +/* called by timer isr */ +int snd_seq_timer_open(queue_t *q); +int snd_seq_timer_close(queue_t *q); +int snd_seq_timer_midi_open(queue_t *q); +int snd_seq_timer_midi_close(queue_t *q); +void snd_seq_timer_defaults(seq_timer_t *tmr); +void snd_seq_timer_reset(seq_timer_t *tmr); +void snd_seq_timer_stop(seq_timer_t *tmr); +void snd_seq_timer_start(seq_timer_t *tmr); +void snd_seq_timer_continue(seq_timer_t *tmr); +int snd_seq_timer_set_tempo(seq_timer_t *tmr, int tempo); +int snd_seq_timer_set_ppq(seq_timer_t *tmr, int ppq); +int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position); +int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position); +int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base); +snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr); +snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr); + +#ifdef SNDRV_SEQ_SYNC_SUPPORT +u64 snd_seq_timer_get_cur_nsec(seq_timer_t *tmr, struct timeval *tm); +#endif + +#endif diff -Nru a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/seq/seq_virmidi.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,526 @@ +/* + * Virtual Raw MIDI client on Sequencer + * + * Copyright (c) 2000 by Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Virtual Raw MIDI client + * + * The virtual rawmidi client is a sequencer client which associate + * a rawmidi device file. The created rawmidi device file can be + * accessed as a normal raw midi, but its MIDI source and destination + * are arbitrary. For example, a user-client software synth connected + * to this port can be used as a normal midi device as well. + * + * The virtual rawmidi device accepts also multiple opens. Each file + * has its own input buffer, so that no conflict would occur. The drain + * of input/output buffer acts only to the local buffer. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer"); +MODULE_LICENSE("GPL"); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +/* + * initialize an event record + */ +static void snd_virmidi_init_event(snd_virmidi_t *vmidi, snd_seq_event_t *ev) +{ + memset(ev, 0, sizeof(*ev)); + ev->source.port = vmidi->port; + switch (vmidi->seq_mode) { + case SNDRV_VIRMIDI_SEQ_DISPATCH: + ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + break; + case SNDRV_VIRMIDI_SEQ_ATTACH: + /* FIXME: source and destination are same - not good.. */ + ev->dest.client = vmidi->client; + ev->dest.port = vmidi->port; + break; + } +} + +/* + * decode input event and put to read buffer of each opened file + */ +static int snd_virmidi_dev_receive_event(snd_virmidi_dev_t *rdev, snd_seq_event_t *ev) +{ + snd_virmidi_t *vmidi; + struct list_head *list; + unsigned char msg[4]; + int len; + + snd_assert(rdev != NULL, return -EINVAL); + + if (!(rdev->flags & SNDRV_VIRMIDI_USE)) + return 0; /* ignored */ + + read_lock(&rdev->filelist_lock); + list_for_each(list, &rdev->filelist) { + vmidi = list_entry(list, snd_virmidi_t, list); + if (!vmidi->trigger) + continue; + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) + continue; + snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream); + } else { + len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev); + if (len > 0) + snd_rawmidi_receive(vmidi->substream, msg, len); + } + } + read_unlock(&rdev->filelist_lock); + + return 0; +} + +/* + * event_input callback from sequencer + */ +int snd_virmidi_receive(snd_seq_event_t *ev, int direct, + void *private_data, int atomic, int hop) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + return snd_virmidi_dev_receive_event(rdev, ev); +} + +/* + * trigger rawmidi stream for input + */ +static void snd_virmidi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return); + + if (up) { + vmidi->trigger = 1; + } else { + vmidi->trigger = 0; + } +} + +/* + * trigger rawmidi stream for output + */ +static void snd_virmidi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return); + int count, res; + unsigned char buf[32], *pbuf; + + if (up) { + vmidi->trigger = 1; + if (!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) { + snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail); + return; /* ignored */ + } + if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { + if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) <= 0) + return; + vmidi->event.type = SNDRV_SEQ_EVENT_NONE; + } + while (1) { + count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf)); + if (count <= 0) + break; + pbuf = buf; + while (count > 0) { + res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event); + if (res < 0) { + snd_midi_event_reset_encode(vmidi->parser); + continue; + } + snd_rawmidi_transmit_ack(substream, res); + pbuf += res; + count -= res; + if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { + if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) <= 0) + return; + vmidi->event.type = SNDRV_SEQ_EVENT_NONE; + } + } + } + } else { + vmidi->trigger = 0; + } +} + +/* + * open rawmidi handle for input + */ +static int snd_virmidi_input_open(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, substream->rmidi->private_data, return -EINVAL); + snd_rawmidi_runtime_t *runtime = substream->runtime; + snd_virmidi_t *vmidi; + unsigned long flags; + + vmidi = snd_magic_kcalloc(snd_virmidi_t, 0, GFP_KERNEL); + if (vmidi == NULL) + return -ENOMEM; + vmidi->substream = substream; + if (snd_midi_event_new(0, &vmidi->parser) < 0) { + snd_magic_kfree(vmidi); + return -ENOMEM; + } + vmidi->seq_mode = rdev->seq_mode; + vmidi->client = rdev->client; + vmidi->port = rdev->port; + runtime->private_data = vmidi; + write_lock_irqsave(&rdev->filelist_lock, flags); + list_add_tail(&vmidi->list, &rdev->filelist); + write_unlock_irqrestore(&rdev->filelist_lock, flags); + vmidi->rdev = rdev; + return 0; +} + +/* + * open rawmidi handle for output + */ +static int snd_virmidi_output_open(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, substream->rmidi->private_data, return -EINVAL); + snd_rawmidi_runtime_t *runtime = substream->runtime; + snd_virmidi_t *vmidi; + + vmidi = snd_magic_kcalloc(snd_virmidi_t, 0, GFP_KERNEL); + if (vmidi == NULL) + return -ENOMEM; + vmidi->substream = substream; + if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) { + snd_magic_kfree(vmidi); + return -ENOMEM; + } + vmidi->seq_mode = rdev->seq_mode; + vmidi->client = rdev->client; + vmidi->port = rdev->port; + snd_midi_event_init(vmidi->parser); + snd_virmidi_init_event(vmidi, &vmidi->event); + vmidi->rdev = rdev; + runtime->private_data = vmidi; + return 0; +} + +/* + * close rawmidi handle for input + */ +static int snd_virmidi_input_close(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return -EINVAL); + snd_midi_event_free(vmidi->parser); + list_del(&vmidi->list); + substream->runtime->private_data = NULL; + snd_magic_kfree(vmidi); + return 0; +} + +/* + * close rawmidi handle for output + */ +static int snd_virmidi_output_close(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return -EINVAL); + snd_midi_event_free(vmidi->parser); + substream->runtime->private_data = NULL; + snd_magic_kfree(vmidi); + return 0; +} + +/* + * subscribe callback - allow output to rawmidi device + */ +static int snd_virmidi_subscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + if (!try_inc_mod_count(rdev->card->module)) + return -EFAULT; + rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE; + return 0; +} + +/* + * unsubscribe callback - disallow output to rawmidi device + */ +static int snd_virmidi_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE; + dec_mod_count(rdev->card->module); + return 0; +} + + +/* + * use callback - allow input to rawmidi device + */ +static int snd_virmidi_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + if (!try_inc_mod_count(rdev->card->module)) + return -EFAULT; + rdev->flags |= SNDRV_VIRMIDI_USE; + return 0; +} + +/* + * unuse callback - disallow input to rawmidi device + */ +static int snd_virmidi_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev->flags &= ~SNDRV_VIRMIDI_USE; + dec_mod_count(rdev->card->module); + return 0; +} + + +/* + * Register functions + */ + +static snd_rawmidi_ops_t snd_virmidi_input_ops = { + open: snd_virmidi_input_open, + close: snd_virmidi_input_close, + trigger: snd_virmidi_input_trigger, +}; + +static snd_rawmidi_ops_t snd_virmidi_output_ops = { + open: snd_virmidi_output_open, + close: snd_virmidi_output_close, + trigger: snd_virmidi_output_trigger, +}; + +/* + * create a sequencer client and a port + */ +static int snd_virmidi_dev_attach_seq(snd_virmidi_dev_t *rdev) +{ + int client; + snd_seq_client_callback_t callbacks; + snd_seq_port_callback_t pcallbacks; + snd_seq_client_info_t info; + snd_seq_port_info_t pinfo; + int err; + + if (rdev->client >= 0) + return 0; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = rdev; + callbacks.allow_input = 1; + callbacks.allow_output = 1; + client = snd_seq_create_kernel_client(rdev->card, rdev->device, &callbacks); + if (client < 0) + return client; + rdev->client = client; + + /* set client name */ + memset(&info, 0, sizeof(info)); + info.client = client; + info.type = KERNEL_CLIENT; + sprintf(info.name, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info); + + /* create a port */ + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.addr.client = client; + sprintf(pinfo.name, "VirMIDI %d-%d", rdev->card->number, rdev->device); + /* set all capabilities */ + pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + pinfo.capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; + pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; + pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + pinfo.midi_channels = 16; + memset(&pcallbacks, 0, sizeof(pcallbacks)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.private_data = rdev; + pcallbacks.subscribe = snd_virmidi_subscribe; + pcallbacks.unsubscribe = snd_virmidi_unsubscribe; + pcallbacks.use = snd_virmidi_use; + pcallbacks.unuse = snd_virmidi_unuse; + pcallbacks.event_input = snd_virmidi_receive; + pinfo.kernel = &pcallbacks; + err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo); + if (err < 0) { + snd_seq_delete_kernel_client(client); + rdev->client = -1; + return err; + } + + rdev->port = pinfo.addr.port; + return 0; /* success */ +} + + +/* + * release the sequencer client + */ +static void snd_virmidi_dev_detach_seq(snd_virmidi_dev_t *rdev) +{ + if (rdev->client >= 0) { + snd_seq_delete_kernel_client(rdev->client); + rdev->client = -1; + } +} + +/* + * register the device + */ +static int snd_virmidi_dev_register(snd_rawmidi_t *rmidi) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO); + int err; + + switch (rdev->seq_mode) { + case SNDRV_VIRMIDI_SEQ_DISPATCH: + err = snd_virmidi_dev_attach_seq(rdev); + if (err < 0) + return err; + break; + case SNDRV_VIRMIDI_SEQ_ATTACH: + if (rdev->client == 0) + return -EINVAL; + /* should check presence of port more strictly.. */ + break; + default: + snd_printk("seq_mode is not set: %d\n", rdev->seq_mode); + return -EINVAL; + } + return 0; +} + + +/* + * unregister the device + */ +static int snd_virmidi_dev_unregister(snd_rawmidi_t *rmidi) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO); + + if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH) + snd_virmidi_dev_detach_seq(rdev); + return 0; +} + +/* + * + */ +static snd_rawmidi_global_ops_t snd_virmidi_global_ops = { + dev_register: snd_virmidi_dev_register, + dev_unregister: snd_virmidi_dev_unregister, +}; + +/* + * free device + */ +static void snd_virmidi_free(snd_rawmidi_t *rmidi) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return); + snd_magic_kfree(rdev); +} + +/* + * create a new device + */ +int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi) +{ + snd_rawmidi_t *rmidi; + snd_virmidi_dev_t *rdev; + int err; + + *rrmidi = NULL; + if ((err = snd_rawmidi_new(card, "VirMidi", device, + 16, /* may be configurable */ + 16, /* may be configurable */ + &rmidi)) < 0) + return err; + strcpy(rmidi->name, rmidi->id); + rdev = snd_magic_kcalloc(snd_virmidi_dev_t, 0, GFP_KERNEL); + if (rdev == NULL) { + snd_device_free(card, rmidi); + return -ENOMEM; + } + rdev->card = card; + rdev->rmidi = rmidi; + rdev->device = device; + rdev->client = -1; + rwlock_init(&rdev->filelist_lock); + INIT_LIST_HEAD(&rdev->filelist); + rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; + rmidi->private_data = rdev; + rmidi->private_free = snd_virmidi_free; + rmidi->ops = &snd_virmidi_global_ops; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + *rrmidi = rmidi; + return 0; +} + +/* + * ENTRY functions + */ + +static int __init alsa_virmidi_init(void) +{ + return 0; +} + +static void __exit alsa_virmidi_exit(void) +{ +} + +module_init(alsa_virmidi_init) +module_exit(alsa_virmidi_exit) + +EXPORT_SYMBOL(snd_virmidi_new); diff -Nru a/sound/core/sound.c b/sound/core/sound.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/sound.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,535 @@ +/* + * Advanced Linux Sound Architecture + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DEVFS_FS +#include +#endif + +#define SNDRV_OS_MINORS 256 + +int snd_major = CONFIG_SND_MAJOR; +static int snd_cards_limit = SNDRV_CARDS; +int snd_device_mode = S_IFCHR | S_IRUGO | S_IWUGO; +int snd_device_gid = 0; +int snd_device_uid = 0; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); +MODULE_PARM(snd_major, "i"); +MODULE_PARM_DESC(snd_major, "Major # for sound driver."); +MODULE_PARM_SYNTAX(snd_major, "default:116,skill:devel"); +MODULE_PARM(snd_cards_limit, "i"); +MODULE_PARM_DESC(snd_cards_limit, "Count of soundcards installed in the system."); +MODULE_PARM_SYNTAX(snd_cards_limit, "default:8,skill:advanced"); +MODULE_PARM(snd_device_mode, "i"); +MODULE_PARM_DESC(snd_device_mode, "Device file permission mask for sound dynamic device filesystem."); +MODULE_PARM_SYNTAX(snd_device_mode, "default:0666,base:8"); +MODULE_PARM(snd_device_gid, "i"); +MODULE_PARM_DESC(snd_device_gid, "Device file GID for sound dynamic device filesystem."); +MODULE_PARM_SYNTAX(snd_device_gid, "default:0"); +MODULE_PARM(snd_device_uid, "i"); +MODULE_PARM_DESC(snd_device_uid, "Device file UID for sound dynamic device filesystem."); +MODULE_PARM_SYNTAX(snd_device_uid, "default:0"); + +int snd_ecards_limit; + +static struct list_head snd_minors_hash[SNDRV_CARDS]; + +static DECLARE_MUTEX(sound_mutex); + +#ifdef CONFIG_DEVFS_FS +static devfs_handle_t devfs_handle = NULL; +#endif + +#ifdef CONFIG_KMOD + +void snd_request_card(int card) +{ + char str[32]; + + if (snd_cards[card] != NULL) + return; + if (card < 0 || card >= snd_ecards_limit) + return; + sprintf(str, "snd-card-%i", card); + request_module(str); +} + +static void snd_request_other(int minor) +{ + char *str; + + switch (minor) { + case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break; + case SNDRV_MINOR_TIMER: str = "snd-timer"; break; + default: return; + } + request_module(str); +} + +#endif /* request_module support */ + +static snd_minor_t *snd_minor_search(int minor) +{ + struct list_head *list; + snd_minor_t *mptr; + + list_for_each(list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]) { + mptr = list_entry(list, snd_minor_t, list); + if (mptr->number == minor) + return mptr; + } + return NULL; +} + +static int snd_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + int card = SNDRV_MINOR_CARD(minor); + int dev = SNDRV_MINOR_DEVICE(minor); + snd_minor_t *mptr = NULL; + struct file_operations *old_fops; + int err = 0; + + if (dev != SNDRV_MINOR_SEQUENCER) { + if (snd_cards[card] == NULL) { +#ifdef CONFIG_KMOD + snd_request_card(card); + if (snd_cards[card] == NULL) +#endif + return -ENODEV; + } + } else { +#ifdef CONFIG_KMOD + if ((mptr = snd_minor_search(minor)) == NULL) + snd_request_other(minor); +#endif + } + if (mptr == NULL && (mptr = snd_minor_search(minor)) == NULL) + return -ENODEV; + old_fops = file->f_op; + file->f_op = fops_get(mptr->f_ops); + if (file->f_op->open) + err = file->f_op->open(inode, file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; +} + +struct file_operations snd_fops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + open: snd_open +}; + +static int snd_kernel_minor(int type, snd_card_t * card, int dev) +{ + int minor; + + switch (type) { + case SNDRV_DEVICE_TYPE_SEQUENCER: + case SNDRV_DEVICE_TYPE_TIMER: + minor = type; + break; + case SNDRV_DEVICE_TYPE_CONTROL: + snd_assert(card != NULL, return -EINVAL); + minor = SNDRV_MINOR(card->number, type); + break; + case SNDRV_DEVICE_TYPE_HWDEP: + case SNDRV_DEVICE_TYPE_RAWMIDI: + case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: + case SNDRV_DEVICE_TYPE_PCM_CAPTURE: + snd_assert(card != NULL, return -EINVAL); + minor = SNDRV_MINOR(card->number, type + dev); + break; + default: + return -EINVAL; + } + snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL); + return minor; +} + +int snd_register_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name) +{ + int minor = snd_kernel_minor(type, card, dev); + snd_minor_t *preg; + + if (minor < 0) + return minor; + preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL); + if (preg == NULL) + return -ENOMEM; + *preg = *reg; + preg->number = minor; + preg->device = dev; + preg->dev = NULL; + down(&sound_mutex); + if (snd_minor_search(minor)) { + up(&sound_mutex); + kfree(preg); + return -EBUSY; + } + if (name) + preg->dev = snd_info_create_device(name, minor, 0); + list_add_tail(&preg->list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]); + up(&sound_mutex); + return 0; +} + +int snd_unregister_device(int type, snd_card_t * card, int dev) +{ + int minor = snd_kernel_minor(type, card, dev); + snd_minor_t *mptr; + + if (minor < 0) + return minor; + down(&sound_mutex); + if ((mptr = snd_minor_search(minor)) == NULL) { + up(&sound_mutex); + return -EINVAL; + } + if (mptr->dev) + snd_info_free_device(mptr->dev); + list_del(&mptr->list); + up(&sound_mutex); + kfree(mptr); + return 0; +} + +/* + * INFO PART + */ + +static snd_info_entry_t *snd_minor_info_entry = NULL; + +static void snd_minor_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int card, device; + struct list_head *list; + snd_minor_t *mptr; + + down(&sound_mutex); + for (card = 0; card < SNDRV_CARDS; card++) { + list_for_each(list, &snd_minors_hash[card]) { + mptr = list_entry(list, snd_minor_t, list); + if (SNDRV_MINOR_DEVICE(mptr->number) != SNDRV_MINOR_SEQUENCER) { + if ((device = mptr->device) >= 0) + snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, device, mptr->comment); + else + snd_iprintf(buffer, "%3i: [%i] : %s\n", mptr->number, card, mptr->comment); + } else { + snd_iprintf(buffer, "%3i: : %s\n", mptr->number, mptr->comment); + } + } + } + up(&sound_mutex); +} + +int __init snd_minor_info_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_minor_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_minor_info_entry = entry; + return 0; +} + +int __exit snd_minor_info_done(void) +{ + if (snd_minor_info_entry) + snd_info_unregister(snd_minor_info_entry); + return 0; +} + +/* + * INIT PART + */ + +static int __init alsa_sound_init(void) +{ +#ifdef CONFIG_DEVFS_FS + short controlnum; + char controlname[24]; +#endif +#ifdef CONFIG_SND_OSSEMUL + int err; +#endif + int card; + + snd_ecards_limit = snd_cards_limit; + for (card = 0; card < SNDRV_CARDS; card++) + INIT_LIST_HEAD(&snd_minors_hash[card]); +#ifdef CONFIG_SND_OSSEMUL + if ((err = snd_oss_init_module()) < 0) + return err; +#endif +#ifdef CONFIG_DEVFS_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + devfs_handle = devfs_mk_dir(NULL, "snd", 3, NULL); +#else + devfs_handle = devfs_mk_dir(NULL, "snd", NULL); +#endif + if (devfs_register_chrdev(snd_major, "alsa", &snd_fops)) { +#else + if (register_chrdev(snd_major, "alsa", &snd_fops)) { +#endif + snd_printk("unable to register native major device number %d\n", snd_major); +#ifdef CONFIG_SND_OSSEMUL + snd_oss_cleanup_module(); +#endif + return -EIO; + } +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_init(); +#endif + if (snd_info_init() < 0) { +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_done(); +#endif +#ifdef CONFIG_SND_OSSEMUL + snd_oss_cleanup_module(); +#endif + return -ENOMEM; + } +#ifdef CONFIG_SND_OSSEMUL + snd_info_minor_register(); +#endif +#ifdef CONFIG_DEVFS_FS + for (controlnum = 0; controlnum < snd_cards_limit; controlnum++) { + sprintf(controlname, "snd/controlC%d", controlnum); + devfs_register(NULL, controlname, DEVFS_FL_DEFAULT, + snd_major, controlnum<<5, snd_device_mode | S_IFCHR, + &snd_fops, NULL); + } +#endif +#ifndef MODULE + printk("Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM) + pm_init(); +#endif + return 0; +} + +static void __exit alsa_sound_exit(void) +{ +#ifdef CONFIG_DEVFS_FS + devfs_handle_t master; + char controlname[24]; + short controlnum; + + for (controlnum = 0; controlnum < snd_cards_limit; controlnum++) { + sprintf(controlname, "snd/controlC%d", controlnum); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + master = devfs_find_handle(NULL, controlname, strlen(controlname), 0, 0, DEVFS_SPECIAL_CHR, 0); +#else + master = devfs_find_handle(NULL, controlname, 0, 0, DEVFS_SPECIAL_CHR, 0); +#endif + devfs_unregister(master); + } +#endif + +#ifdef CONFIG_SND_OSSEMUL + snd_info_minor_unregister(); + snd_oss_cleanup_module(); +#endif + snd_info_done(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM) + pm_done(); +#endif +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_done(); +#endif +#ifdef CONFIG_DEVFS_FS + if (devfs_unregister_chrdev(snd_major, "alsa") != 0) +#else + if (unregister_chrdev(snd_major, "alsa") != 0) +#endif + snd_printk("unable to unregister major device number %d\n", snd_major); +#ifdef CONFIG_DEVFS_FS + devfs_unregister(devfs_handle); +#endif +} + +module_init(alsa_sound_init) +module_exit(alsa_sound_exit) + + /* sound.c */ +EXPORT_SYMBOL(snd_ecards_limit); +#if defined(CONFIG_KMOD) +EXPORT_SYMBOL(snd_request_card); +#endif +EXPORT_SYMBOL(snd_register_device); +EXPORT_SYMBOL(snd_unregister_device); +#if defined(CONFIG_SND_OSSEMUL) +EXPORT_SYMBOL(snd_register_oss_device); +EXPORT_SYMBOL(snd_unregister_oss_device); +#endif + /* memory.c */ +#ifdef CONFIG_SND_DEBUG_MEMORY +EXPORT_SYMBOL(snd_hidden_kmalloc); +EXPORT_SYMBOL(snd_hidden_kfree); +EXPORT_SYMBOL(snd_hidden_vmalloc); +EXPORT_SYMBOL(snd_hidden_vfree); +EXPORT_SYMBOL(_snd_magic_kmalloc); +EXPORT_SYMBOL(_snd_magic_kcalloc); +EXPORT_SYMBOL(snd_magic_kfree); +#endif +EXPORT_SYMBOL(snd_kcalloc); +EXPORT_SYMBOL(snd_kmalloc_strdup); +EXPORT_SYMBOL(snd_malloc_pages); +EXPORT_SYMBOL(snd_malloc_pages_fallback); +EXPORT_SYMBOL(snd_free_pages); +#ifdef CONFIG_ISA +EXPORT_SYMBOL(snd_malloc_isa_pages); +EXPORT_SYMBOL(snd_malloc_isa_pages_fallback); +#endif +#ifdef CONFIG_PCI +EXPORT_SYMBOL(snd_malloc_pci_pages); +EXPORT_SYMBOL(snd_malloc_pci_pages_fallback); +EXPORT_SYMBOL(snd_free_pci_pages); +#endif +EXPORT_SYMBOL(copy_to_user_fromio); +EXPORT_SYMBOL(copy_from_user_toio); + /* init.c */ +EXPORT_SYMBOL(snd_cards_count); +EXPORT_SYMBOL(snd_cards); +#ifdef CONFIG_SND_OSSEMUL +EXPORT_SYMBOL(snd_mixer_oss_notify_callback); +#endif +EXPORT_SYMBOL(snd_card_new); +EXPORT_SYMBOL(snd_card_free); +EXPORT_SYMBOL(snd_card_register); +EXPORT_SYMBOL(snd_component_add); +#ifdef CONFIG_PM +EXPORT_SYMBOL(snd_power_wait); +#endif + /* device.c */ +EXPORT_SYMBOL(snd_device_new); +EXPORT_SYMBOL(snd_device_register); +EXPORT_SYMBOL(snd_device_free); +EXPORT_SYMBOL(snd_device_free_all); + /* misc.c */ +#ifdef CONFIG_ISA +EXPORT_SYMBOL(snd_dma_program); +EXPORT_SYMBOL(snd_dma_disable); +EXPORT_SYMBOL(snd_dma_residue); +#endif + /* info.c */ +EXPORT_SYMBOL(snd_seq_root); +EXPORT_SYMBOL(snd_create_proc_entry); +EXPORT_SYMBOL(snd_remove_proc_entry); +EXPORT_SYMBOL(snd_iprintf); +EXPORT_SYMBOL(snd_info_get_line); +EXPORT_SYMBOL(snd_info_get_str); +EXPORT_SYMBOL(snd_info_create_module_entry); +EXPORT_SYMBOL(snd_info_create_card_entry); +EXPORT_SYMBOL(snd_info_free_entry); +EXPORT_SYMBOL(snd_info_create_device); +EXPORT_SYMBOL(snd_info_free_device); +EXPORT_SYMBOL(snd_info_register); +EXPORT_SYMBOL(snd_info_unregister); + /* info_oss.c */ +#ifdef CONFIG_SND_OSSEMUL +EXPORT_SYMBOL(snd_oss_info_register); +#endif + /* control.c */ +EXPORT_SYMBOL(snd_ctl_new); +EXPORT_SYMBOL(snd_ctl_new1); +EXPORT_SYMBOL(snd_ctl_free_one); +EXPORT_SYMBOL(snd_ctl_add); +EXPORT_SYMBOL(snd_ctl_remove); +EXPORT_SYMBOL(snd_ctl_remove_id); +EXPORT_SYMBOL(snd_ctl_rename_id); +EXPORT_SYMBOL(snd_ctl_find_numid); +EXPORT_SYMBOL(snd_ctl_find_id); +EXPORT_SYMBOL(snd_ctl_notify); +EXPORT_SYMBOL(snd_ctl_register_ioctl); +EXPORT_SYMBOL(snd_ctl_unregister_ioctl); + /* misc.c */ +EXPORT_SYMBOL(snd_task_name); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +EXPORT_SYMBOL(try_inc_mod_count); +EXPORT_SYMBOL(snd_compat_mem_region); +EXPORT_SYMBOL(snd_compat_request_region); +EXPORT_SYMBOL(snd_compat_release_resource); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_PCI) +EXPORT_SYMBOL(snd_pci_compat_match_device); +EXPORT_SYMBOL(snd_pci_compat_register_driver); +EXPORT_SYMBOL(snd_pci_compat_unregister_driver); +EXPORT_SYMBOL(snd_pci_compat_get_size); +EXPORT_SYMBOL(snd_pci_compat_get_flags); +EXPORT_SYMBOL(snd_pci_compat_set_power_state); +EXPORT_SYMBOL(snd_pci_compat_enable_device); +EXPORT_SYMBOL(snd_pci_compat_find_capability); +EXPORT_SYMBOL(snd_pci_compat_alloc_consistent); +EXPORT_SYMBOL(snd_pci_compat_free_consistent); +EXPORT_SYMBOL(snd_pci_compat_dma_supported); +EXPORT_SYMBOL(snd_pci_compat_get_dma_mask); +EXPORT_SYMBOL(snd_pci_compat_set_dma_mask); +EXPORT_SYMBOL(snd_pci_compat_get_driver_data); +EXPORT_SYMBOL(snd_pci_compat_set_driver_data); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_PM) +EXPORT_SYMBOL(pm_register); +EXPORT_SYMBOL(pm_unregister); +EXPORT_SYMBOL(pm_send); +#endif + /* wrappers */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +EXPORT_SYMBOL(snd_wrapper_kill_fasync); +#endif +#ifdef CONFIG_SND_DEBUG_MEMORY +EXPORT_SYMBOL(snd_wrapper_kmalloc); +EXPORT_SYMBOL(snd_wrapper_kfree); +#endif +#ifdef HACK_PCI_ALLOC_CONSISTENT +EXPORT_SYMBOL(snd_pci_hack_alloc_consistent); +#endif diff -Nru a/sound/core/sound_oss.c b/sound/core/sound_oss.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/sound_oss.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,249 @@ +/* + * Advanced Linux Sound Architecture + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include + +#ifdef CONFIG_SND_OSSEMUL + +#if !defined(CONFIG_SOUND) && !defined(CONFIG_SOUND_MODULE) +#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel." +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define SNDRV_OS_MINORS 256 + +static struct list_head snd_oss_minors_hash[SNDRV_CARDS]; + +static DECLARE_MUTEX(sound_oss_mutex); + +static snd_minor_t *snd_oss_minor_search(int minor) +{ + struct list_head *list; + snd_minor_t *mptr; + + list_for_each(list, &snd_oss_minors_hash[SNDRV_MINOR_OSS_CARD(minor)]) { + mptr = list_entry(list, snd_minor_t, list); + if (mptr->number == minor) + return mptr; + } + return NULL; +} + +static int snd_oss_kernel_minor(int type, snd_card_t * card, int dev) +{ + int minor; + + switch (type) { + case SNDRV_OSS_DEVICE_TYPE_MIXER: + snd_assert(card != NULL && dev <= 1, return -EINVAL); + minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER)); + break; + case SNDRV_OSS_DEVICE_TYPE_SEQUENCER: + minor = SNDRV_MINOR_OSS_SEQUENCER; + break; + case SNDRV_OSS_DEVICE_TYPE_MUSIC: + minor = SNDRV_MINOR_OSS_MUSIC; + break; + case SNDRV_OSS_DEVICE_TYPE_PCM: + snd_assert(card != NULL && dev <= 1, return -EINVAL); + minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM)); + break; + case SNDRV_OSS_DEVICE_TYPE_MIDI: + snd_assert(card != NULL && dev <= 1, return -EINVAL); + minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI)); + break; + case SNDRV_OSS_DEVICE_TYPE_DMFM: + minor = SNDRV_MINOR_OSS(card->number, SNDRV_MINOR_OSS_DMFM); + break; + case SNDRV_OSS_DEVICE_TYPE_SNDSTAT: + minor = SNDRV_MINOR_OSS_SNDSTAT; + break; + default: + return -EINVAL; + } + snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL); + return minor; +} + +int snd_register_oss_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name) +{ + int minor = snd_oss_kernel_minor(type, card, dev); + int minor_unit; + snd_minor_t *preg; + int cidx = SNDRV_MINOR_OSS_CARD(minor); + int track2 = -1; + int register1 = -1, register2 = -1; + + if (minor < 0) + return minor; + preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL); + if (preg == NULL) + return -ENOMEM; + *preg = *reg; + preg->number = minor; + preg->device = dev; + preg->dev = NULL; + down(&sound_oss_mutex); + list_add_tail(&preg->list, &snd_oss_minors_hash[cidx]); + minor_unit = SNDRV_MINOR_OSS_DEVICE(minor); + switch (minor_unit) { + case SNDRV_MINOR_OSS_PCM: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO); + break; + case SNDRV_MINOR_OSS_MIDI: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI); + break; + case SNDRV_MINOR_OSS_MIDI1: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1); + break; + } + register1 = register_sound_special(reg->f_ops, minor); + if (register1 != minor) + goto __end; + if (track2 >= 0) { + register2 = register_sound_special(reg->f_ops, track2); + if (register2 != track2) + goto __end; + } + up(&sound_oss_mutex); + return 0; + + __end: + if (register2 >= 0) + unregister_sound_special(register2); + if (register1 >= 0) + unregister_sound_special(register1); + list_del(&preg->list); + up(&sound_oss_mutex); + kfree(preg); + return -EBUSY; +} + +int snd_unregister_oss_device(int type, snd_card_t * card, int dev) +{ + int minor = snd_oss_kernel_minor(type, card, dev); + int cidx = SNDRV_MINOR_OSS_CARD(minor); + int track2 = -1; + snd_minor_t *mptr; + + if (minor < 0) + return minor; + down(&sound_oss_mutex); + mptr = snd_oss_minor_search(minor); + if (mptr == NULL) { + up(&sound_oss_mutex); + return -ENOENT; + } + unregister_sound_special(minor); + switch (SNDRV_MINOR_OSS_DEVICE(minor)) { + case SNDRV_MINOR_OSS_PCM: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO); + break; + case SNDRV_MINOR_OSS_MIDI: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI); + break; + case SNDRV_MINOR_OSS_MIDI1: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1); + break; + } + if (track2 >= 0) + unregister_sound_special(track2); + list_del(&mptr->list); + up(&sound_oss_mutex); + kfree(mptr); + return 0; +} + +/* + * INFO PART + */ + +static snd_info_entry_t *snd_minor_info_oss_entry = NULL; + +static void snd_minor_info_oss_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int card, dev; + struct list_head *list; + snd_minor_t *mptr; + + down(&sound_oss_mutex); + for (card = 0; card < SNDRV_CARDS; card++) { + list_for_each(list, &snd_oss_minors_hash[card]) { + mptr = list_entry(list, snd_minor_t, list); + dev = SNDRV_MINOR_OSS_DEVICE(mptr->number); + if (dev != SNDRV_MINOR_OSS_SNDSTAT && + dev != SNDRV_MINOR_OSS_SEQUENCER && + dev != SNDRV_MINOR_OSS_MUSIC) + snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, dev, mptr->comment); + else + snd_iprintf(buffer, "%3i: : %s\n", mptr->number, mptr->comment); + } + } + up(&sound_oss_mutex); +} + +int __init snd_minor_info_oss_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "oss-devices", NULL); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_minor_info_oss_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_minor_info_oss_entry = entry; + return 0; +} + +int __exit snd_minor_info_oss_done(void) +{ + if (snd_minor_info_oss_entry) + snd_info_unregister(snd_minor_info_oss_entry); + return 0; +} + +int __init snd_oss_init_module(void) +{ + int card; + + for (card = 0; card < SNDRV_CARDS; card++) + INIT_LIST_HEAD(&snd_oss_minors_hash[card]); + return 0; +} + +void snd_oss_cleanup_module(void) +{ +} + +#endif /* CONFIG_SND_OSSEMUL */ diff -Nru a/sound/core/timer.c b/sound/core/timer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/timer.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1362 @@ +/* + * Timers abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif + +int snd_timer_limit = 1; +MODULE_AUTHOR("Jaroslav Kysela , Takashi Iwai "); +MODULE_DESCRIPTION("ALSA timer interface"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_PARM(snd_timer_limit, "i"); +MODULE_PARM_DESC(snd_timer_limit, "Maximum global timers in system. (1 by default)"); + +typedef struct { + snd_timer_instance_t *timeri; + unsigned long ticks; + unsigned long overrun; + int qhead; + int qtail; + int qused; + int queue_size; + snd_timer_read_t *queue; + spinlock_t qlock; + wait_queue_head_t qchange_sleep; +} snd_timer_user_t; + +/* list of timers */ +static LIST_HEAD(snd_timer_list); + +/* list of slave instances */ +static LIST_HEAD(snd_timer_slave_list); + +/* lock for slave active lists */ +static spinlock_t slave_active_lock = SPIN_LOCK_UNLOCKED; + +static DECLARE_MUTEX(register_mutex); + +static int snd_timer_free(snd_timer_t *timer); +static int snd_timer_dev_free(snd_device_t *device); +static int snd_timer_dev_register(snd_device_t *device); +static int snd_timer_dev_unregister(snd_device_t *device); + +static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +/* + * create a timer instance with the given owner string. + * when timer is not NULL, increments the module counter + */ +static snd_timer_instance_t *snd_timer_instance_new(char *owner, snd_timer_t *timer) +{ + snd_timer_instance_t *timeri; + timeri = snd_kcalloc(sizeof(snd_timer_instance_t), GFP_KERNEL); + if (timeri == NULL) + return NULL; + timeri->owner = snd_kmalloc_strdup(owner, GFP_KERNEL); + if (! timeri->owner) { + kfree(timeri); + return NULL; + } + INIT_LIST_HEAD(&timeri->open_list); + INIT_LIST_HEAD(&timeri->active_list); + INIT_LIST_HEAD(&timeri->slave_list_head); + INIT_LIST_HEAD(&timeri->slave_active_head); + timeri->in_use = (atomic_t)ATOMIC_INIT(0); + + timeri->timer = timer; + if (timer && timer->card && !try_inc_mod_count(timer->card->module)) { + kfree(timeri->owner); + kfree(timeri); + return NULL; + } + + return timeri; +} + +/* + * find a timer instance from the given timer id + */ +static snd_timer_t *snd_timer_find(snd_timer_id_t *tid) +{ + snd_timer_t *timer = NULL; + struct list_head *p; + + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + + if (timer->tmr_class != tid->dev_class) + continue; + if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || + timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && + (timer->card == NULL || + timer->card->number != tid->card)) + continue; + if (timer->tmr_device != tid->device) + continue; + if (timer->tmr_subdevice != tid->subdevice) + continue; + return timer; + } + return NULL; +} + +#ifdef CONFIG_KMOD + +static void snd_timer_request(snd_timer_id_t *tid) +{ + char str[32]; + + switch (tid->dev_class) { + case SNDRV_TIMER_CLASS_GLOBAL: + sprintf(str, "snd-timer-%i", tid->device); + break; + case SNDRV_TIMER_CLASS_CARD: + case SNDRV_TIMER_CLASS_PCM: + sprintf(str, "snd-card-%i", tid->card); + break; + default: + return; + } + request_module(str); +} + +#endif + +/* + * look for a master instance matching with the slave id of the given slave. + * when found, relink the open_link of the slave. + * + * call this with register_mutex down. + */ +static void snd_timer_check_slave(snd_timer_instance_t *slave) +{ + snd_timer_t *timer; + snd_timer_instance_t *master; + struct list_head *p, *q; + + /* FIXME: it's really dumb to look up all entries.. */ + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + list_for_each(q, &timer->open_list_head) { + master = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); + if (slave->slave_class == master->slave_class && + slave->slave_id == master->slave_id) { + list_del(&slave->open_list); + list_add_tail(&slave->open_list, &master->slave_list_head); + slave->master = master; + return; + } + } + } +} + +/* + * look for slave instances matching with the slave id of the given master. + * when found, relink the open_link of slaves. + * + * call this with register_mutex down. + */ +static void snd_timer_check_master(snd_timer_instance_t *master) +{ + snd_timer_instance_t *slave; + struct list_head *p, *n; + unsigned long flags; + + /* check all pending slaves */ + list_for_each_safe(p, n, &snd_timer_slave_list) { + slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); + if (slave->slave_class == master->slave_class && + slave->slave_id == master->slave_id) { + list_del(p); + list_add_tail(p, &master->slave_list_head); + spin_lock_irqsave(&slave_active_lock, flags); + /* protected here so that timer_start() doesn't start + * this slave yet. + */ + slave->master = master; + spin_unlock_irqrestore(&slave_active_lock, flags); + } + } +} + +/* + * open a timer instance + * when opening a master, the slave id must be here given. + */ +snd_timer_instance_t *snd_timer_open(char *owner, snd_timer_id_t *tid, + unsigned int slave_id) +{ + snd_timer_t *timer; + snd_timer_instance_t *timeri = NULL; + + if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { + /* open a slave instance */ + if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || + tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { + snd_printd("invalid slave class %i\n", tid->dev_sclass); + return NULL; + } + down(®ister_mutex); + timeri = snd_timer_instance_new(owner, NULL); + timeri->slave_class = tid->dev_sclass; + timeri->slave_id = tid->device; + timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; + list_add_tail(&timeri->open_list, &snd_timer_slave_list); + snd_timer_check_slave(timeri); + up(®ister_mutex); + return timeri; + } + + /* open a master instance */ + down(®ister_mutex); + timer = snd_timer_find(tid); +#ifdef CONFIG_KMOD + if (timer == NULL) { + up(®ister_mutex); + snd_timer_request(tid); + down(®ister_mutex); + timer = snd_timer_find(tid); + } +#endif + if (timer) { + timeri = snd_timer_instance_new(owner, timer); + if (timeri) { + timeri->slave_class = tid->dev_sclass; + timeri->slave_id = slave_id; + if (list_empty(&timer->open_list_head) && timer->hw.open) + timer->hw.open(timer); + list_add_tail(&timeri->open_list, &timer->open_list_head); + snd_timer_check_master(timeri); + } + } + up(®ister_mutex); + return timeri; +} + + +/* + * close a timer instance + */ +int snd_timer_close(snd_timer_instance_t * timeri) +{ + snd_timer_t *timer = NULL; + unsigned long flags; + struct list_head *p, *n; + snd_timer_instance_t *slave; + + snd_assert(timeri != NULL, return -ENXIO); + + snd_timer_stop(timeri); /* force to stop the timer */ + + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { + down(®ister_mutex); + list_del(&timeri->open_list); + up(®ister_mutex); + } else { + timer = timeri->timer; + down(®ister_mutex); + list_del(&timeri->open_list); + if (timer && list_empty(&timer->open_list_head) && timer->hw.close) + timer->hw.close(timer); + /* remove slave links */ + list_for_each_safe(p, n, &timeri->slave_list_head) { + slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); + list_del(p); + list_add_tail(p, &snd_timer_slave_list); + spin_lock_irqsave(&slave_active_lock, flags); + slave->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + list_del_init(&slave->active_list); + slave->master = NULL; + spin_unlock_irqrestore(&slave_active_lock, flags); + } + up(®ister_mutex); + } + if (timeri->private_free) + timeri->private_free(timeri); + if (timeri->owner) + kfree(timeri->owner); + kfree(timeri); + if (timer && timer->card) + dec_mod_count(timer->card->module); + return 0; +} + +unsigned long snd_timer_resolution(snd_timer_instance_t * timeri) +{ + snd_timer_t * timer; + + if (timeri == NULL) + return 0; + if ((timer = timeri->timer) != NULL) { + if (timer->hw.c_resolution) + return timer->hw.c_resolution(timer); + return timer->hw.resolution; + } + return 0; +} + +static int snd_timer_start1(snd_timer_t *timer, snd_timer_instance_t *timeri, unsigned long sticks) +{ + list_del(&timeri->active_list); + list_add_tail(&timeri->active_list, &timer->active_list_head); + if (timer->running) { + timer->flags |= SNDRV_TIMER_FLG_RESCHED; + timeri->flags |= SNDRV_TIMER_IFLG_START; + return 1; /* delayed start */ + } else { + timer->sticks = sticks; + timer->hw.start(timer); + timer->running++; + timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; + return 0; + } +} + +static int snd_timer_start_slave(snd_timer_instance_t *timeri) +{ + unsigned long flags; + + spin_lock_irqsave(&slave_active_lock, flags); + timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; + if (timeri->master) + list_add_tail(&timeri->active_list, &timeri->master->slave_active_head); + spin_unlock_irqrestore(&slave_active_lock, flags); + return 1; /* delayed start */ +} + +int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks) +{ + snd_timer_t *timer; + int result = -EINVAL; + unsigned long flags; + + if (timeri == NULL || ticks < 1) + return -EINVAL; + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_start_slave(timeri); + timer = timeri->timer; + if (timer == NULL) + return -EINVAL; + spin_lock_irqsave(&timer->lock, flags); + timeri->ticks = timeri->cticks = ticks; + result = snd_timer_start1(timer, timeri, ticks); + spin_unlock_irqrestore(&timer->lock, flags); + return result; +} + +/* + * stop the timer instance. + * + * FIXME: do not call this from the timer callback! + */ +int snd_timer_stop(snd_timer_instance_t * timeri) +{ + snd_timer_t *timer; + unsigned long flags; + + snd_assert(timeri != NULL, return -ENXIO); + + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { + spin_lock_irqsave(&slave_active_lock, flags); + if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { + timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + list_del_init(&timeri->active_list); + } + spin_unlock_irqrestore(&slave_active_lock, flags); + return 0; + } + + timer = timeri->timer; + if (! timer) + return -EINVAL; + while (atomic_read(&timeri->in_use)) + udelay(10); + spin_lock_irqsave(&timer->lock, flags); + if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { + timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + list_del_init(&timeri->active_list); + if (!(--timer->running)) { + timer->hw.stop(timer); + if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { + timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; + snd_timer_reschedule(timer, 0); + if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { + timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; + timer->hw.start(timer); + } + } + } + } + spin_unlock_irqrestore(&timer->lock, flags); + return 0; +} + +/* + * start again.. the tick is kept. + */ +int snd_timer_continue(snd_timer_instance_t * timeri) +{ + snd_timer_t *timer; + int result = -EINVAL; + unsigned long flags; + + if (timeri == NULL) + return result; + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_start_slave(timeri); + timer = timeri->timer; + if (! timer) + return -EINVAL; + spin_lock_irqsave(&timer->lock, flags); + if (!timeri->cticks) + timeri->cticks = 1; + result = snd_timer_start1(timer, timeri, timer->sticks); + spin_unlock_irqrestore(&timer->lock, flags); + return result; +} + +/* + * reschedule the timer + * + * start pending instances and check the scheduling ticks. + * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. + */ +static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left) +{ + snd_timer_instance_t *ti; + unsigned long ticks = ~0UL; + struct list_head *p; + + list_for_each(p, &timer->active_list_head) { + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); + if (ti->flags & SNDRV_TIMER_IFLG_START) { + ti->flags &= ~SNDRV_TIMER_IFLG_START; + ti->flags |= SNDRV_TIMER_IFLG_RUNNING; + timer->running++; + } + if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { + if (ticks > ti->cticks) + ticks = ti->cticks; + } + } + if (ticks == ~0UL) { + timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; + return; + } + if (ticks > timer->hw.ticks) + ticks = timer->hw.ticks; + if (ticks_left != ticks) + timer->flags |= SNDRV_TIMER_FLG_CHANGE; + timer->sticks = ticks; +} + + +/* + * timer interrupt + * + * ticks_left is usually equal to timer->sticks. + * + */ +void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left) +{ + snd_timer_instance_t *ti, *ts; + unsigned long resolution; + LIST_HEAD(done_list_head); + struct list_head *p, *q, *n; + + if (timer == NULL) + return; + spin_lock(&timer->lock); + /* loop for all active instances + * here we cannot use list_for_each because the active_list of a processed + * instance is relinked to done_list_head before callback is called. + */ + list_for_each_safe(p, n, &timer->active_list_head) { + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); + if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { + if (ti->cticks < ticks_left) + ti->cticks = 0; + else + ti->cticks -= ticks_left; + if (!ti->cticks) { /* expired */ + if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { + ti->cticks = ti->ticks; + } else { + ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + timer->running--; + } + /* relink to done_list */ + list_del(p); + list_add_tail(p, &done_list_head); + atomic_inc(&ti->in_use); + } + } + } + if (timer->flags & SNDRV_TIMER_FLG_RESCHED) + snd_timer_reschedule(timer, ticks_left); + if (timer->running) { + if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { + timer->hw.stop(timer); + timer->flags |= SNDRV_TIMER_FLG_CHANGE; + } + if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || + (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { + /* restart timer */ + timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; + timer->hw.start(timer); + } + } else { + timer->hw.stop(timer); + } + + /* remember the current resolution */ + if (timer->hw.c_resolution) + resolution = timer->hw.c_resolution(timer); + else + resolution = timer->hw.resolution; + + /* now process all callbacks */ + list_for_each_safe(p, n, &done_list_head) { + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); + /* append to active_list */ + list_del(p); + list_add_tail(p, &timer->active_list_head); + spin_unlock(&timer->lock); + if (ti->callback) + ti->callback(ti, resolution, ti->ticks, ti->callback_data); + spin_lock(&slave_active_lock); + /* call callbacks of slaves */ + list_for_each(q, &ti->slave_active_head) { + ts = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, active_list); + if (ts->callback) + ts->callback(ts, resolution, ti->ticks, ts->callback_data); + } + spin_unlock(&slave_active_lock); + atomic_dec(&ti->in_use); + spin_lock(&timer->lock); + } + spin_unlock(&timer->lock); +} + + +/* + + */ + +int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer) +{ + snd_timer_t *timer; + int err; + static snd_device_ops_t ops = { + dev_free: snd_timer_dev_free, + dev_register: snd_timer_dev_register, + dev_unregister: snd_timer_dev_unregister + }; + + snd_assert(tid != NULL, return -EINVAL); + snd_assert(rtimer != NULL, return -EINVAL); + *rtimer = NULL; + timer = snd_magic_kcalloc(snd_timer_t, 0, GFP_KERNEL); + if (timer == NULL) + return -ENOMEM; + timer->tmr_class = tid->dev_class; + timer->card = card; + timer->tmr_device = tid->device; + timer->tmr_subdevice = tid->subdevice; + if (id) + strncpy(timer->id, id, sizeof(timer->id) - 1); + INIT_LIST_HEAD(&timer->device_list); + INIT_LIST_HEAD(&timer->open_list_head); + INIT_LIST_HEAD(&timer->active_list_head); + spin_lock_init(&timer->lock); + if (card != NULL) { + if ((err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops)) < 0) { + snd_timer_free(timer); + return err; + } + } + *rtimer = timer; + return 0; +} + +static int snd_timer_free(snd_timer_t *timer) +{ + snd_assert(timer != NULL, return -ENXIO); + if (timer->private_free) + timer->private_free(timer); + snd_magic_kfree(timer); + return 0; +} + +int snd_timer_dev_free(snd_device_t *device) +{ + snd_timer_t *timer = snd_magic_cast(snd_timer_t, device->device_data, return -ENXIO); + return snd_timer_free(timer); +} + +int snd_timer_dev_register(snd_device_t *dev) +{ + snd_timer_t *timer = snd_magic_cast(snd_timer_t, dev->device_data, return -ENXIO); + snd_timer_t *timer1; + struct list_head *p; + + snd_assert(timer != NULL && timer->hw.start != NULL && timer->hw.stop != NULL, return -ENXIO); + if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && + !timer->hw.resolution && timer->hw.c_resolution == NULL) + return -EINVAL; + + down(®ister_mutex); + list_for_each(p, &snd_timer_list) { + timer1 = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + if (timer1->tmr_class > timer->tmr_class) + break; + if (timer1->tmr_class < timer->tmr_class) + continue; + if (timer1->card && timer->card) { + if (timer1->card->number > timer->card->number) + break; + if (timer1->card->number < timer->card->number) + continue; + } + if (timer1->tmr_device > timer->tmr_device) + break; + if (timer1->tmr_device < timer->tmr_device) + continue; + if (timer1->tmr_subdevice > timer->tmr_subdevice) + break; + if (timer1->tmr_subdevice < timer->tmr_subdevice) + continue; + /* conflicts.. */ + up(®ister_mutex); + return -EBUSY; + } + list_add_tail(&timer->device_list, p); + up(®ister_mutex); + return 0; +} + +int snd_timer_unregister(snd_timer_t *timer) +{ + struct list_head *p, *n; + snd_timer_instance_t *ti; + + snd_assert(timer != NULL, return -ENXIO); + down(®ister_mutex); + if (! list_empty(&timer->open_list_head)) { + snd_printk("timer 0x%lx is busy?\n", (long)timer); + list_for_each_safe(p, n, &timer->open_list_head) { + list_del_init(p); + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); + ti->timer = NULL; + } + } + list_del(&timer->device_list); + up(®ister_mutex); + return snd_timer_free(timer); +} + +static int snd_timer_dev_unregister(snd_device_t *device) +{ + snd_timer_t *timer = snd_magic_cast(snd_timer_t, device->device_data, return -ENXIO); + return snd_timer_unregister(timer); +} + +/* + * exported functions for global timers + */ +int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer) +{ + snd_timer_id_t tid; + + tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = -1; + tid.device = device; + tid.subdevice = 0; + return snd_timer_new(NULL, id, &tid, rtimer); +} + +int snd_timer_global_free(snd_timer_t *timer) +{ + return snd_timer_free(timer); +} + +int snd_timer_global_register(snd_timer_t *timer) +{ + snd_device_t dev; + + memset(&dev, 0, sizeof(dev)); + dev.device_data = timer; + return snd_timer_dev_register(&dev); +} + +int snd_timer_global_unregister(snd_timer_t *timer) +{ + return snd_timer_unregister(timer); +} + +/* + * System timer + */ + +unsigned int snd_timer_system_resolution(void) +{ + return 1000000000L / HZ; +} + +static void snd_timer_s_function(unsigned long data) +{ + snd_timer_t *timer = (snd_timer_t *)data; + snd_timer_interrupt(timer, timer->sticks); +} + +static int snd_timer_s_start(snd_timer_t * timer) +{ + struct timer_list *tlist; + + tlist = (struct timer_list *) timer->private_data; + tlist->expires = jiffies + timer->sticks; + add_timer(tlist); + return 0; +} + +static int snd_timer_s_stop(snd_timer_t * timer) +{ + struct timer_list *tlist; + + tlist = (struct timer_list *) timer->private_data; + del_timer(tlist); + timer->sticks = tlist->expires - jiffies; + return 0; +} + +static struct _snd_timer_hardware snd_timer_system = +{ + flags: SNDRV_TIMER_HW_FIRST, + resolution: 1000000000L / HZ, + ticks: 10000000L, + start: snd_timer_s_start, + stop: snd_timer_s_stop +}; + +static void snd_timer_free_system(snd_timer_t *timer) +{ + if (timer->private_data) + kfree(timer->private_data); +} + +static int snd_timer_register_system(void) +{ + snd_timer_t *timer; + struct timer_list *tlist; + int err; + + if ((err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer)) < 0) + return err; + strcpy(timer->name, "system timer"); + timer->hw = snd_timer_system; + tlist = (struct timer_list *) snd_kcalloc(sizeof(struct timer_list), GFP_KERNEL); + if (tlist == NULL) { + snd_timer_free(timer); + return -ENOMEM; + } + tlist->function = snd_timer_s_function; + tlist->data = (unsigned long) timer; + timer->private_data = tlist; + timer->private_free = snd_timer_free_system; + return snd_timer_global_register(timer); +} + +/* + * Info interface + */ + +static void snd_timer_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + unsigned long flags; + snd_timer_t *timer; + snd_timer_instance_t *ti; + struct list_head *p, *q; + + down(®ister_mutex); + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + switch (timer->tmr_class) { + case SNDRV_TIMER_CLASS_GLOBAL: + snd_iprintf(buffer, "G%i: ", timer->tmr_device); + break; + case SNDRV_TIMER_CLASS_CARD: + snd_iprintf(buffer, "C%i-%i: ", timer->card->number, timer->tmr_device); + break; + case SNDRV_TIMER_CLASS_PCM: + snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, timer->tmr_device, timer->tmr_subdevice); + break; + default: + snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, timer->card ? timer->card->number : -1, timer->tmr_device, timer->tmr_subdevice); + } + snd_iprintf(buffer, "%s :", timer->name); + if (timer->hw.resolution) + snd_iprintf(buffer, " %lu.%luus (%lu ticks)", timer->hw.resolution / 1000, timer->hw.resolution % 1000, timer->hw.ticks); + if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) + snd_iprintf(buffer, " SLAVE"); + snd_iprintf(buffer, "\n"); + spin_lock_irqsave(&timer->lock, flags); + list_for_each(q, &timer->open_list_head) { + ti = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); + snd_iprintf(buffer, " Client %s : %s : lost interrupts %li\n", + ti->owner ? ti->owner : "unknown", + ti->flags & (SNDRV_TIMER_IFLG_START|SNDRV_TIMER_IFLG_RUNNING) ? "running" : "stopped", + ti->lost); + } + spin_unlock_irqrestore(&timer->lock, flags); + } + up(®ister_mutex); +} + +/* + * USER SPACE interface + */ + +static void snd_timer_user_interrupt(snd_timer_instance_t *timeri, + unsigned long resolution, + unsigned long ticks, + void *data) +{ + unsigned long flags; + snd_timer_user_t *tu = snd_magic_cast(snd_timer_user_t, data, return); + snd_timer_read_t *r; + + if (tu->qused >= tu->queue_size) { + tu->overrun++; + } else { + spin_lock_irqsave(&tu->qlock, flags); + r = &tu->queue[tu->qtail++]; + tu->qtail %= tu->queue_size; + r->resolution = resolution; + r->ticks = ticks; + tu->qused++; + spin_unlock_irqrestore(&tu->qlock, flags); + wake_up(&tu->qchange_sleep); + } +} + +static int snd_timer_user_open(struct inode *inode, struct file *file) +{ + snd_timer_user_t *tu; + + tu = snd_magic_kcalloc(snd_timer_user_t, 0, GFP_KERNEL); + if (tu == NULL) + return -ENOMEM; + spin_lock_init(&tu->qlock); + init_waitqueue_head(&tu->qchange_sleep); + tu->ticks = 1; + tu->queue_size = 128; + tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); + if (tu->queue == NULL) { + snd_magic_kfree(tu); + return -ENOMEM; + } + file->private_data = tu; +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static int snd_timer_user_release(struct inode *inode, struct file *file) +{ + snd_timer_user_t *tu; + + if (file->private_data) { + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + file->private_data = NULL; + if (tu->timeri) + snd_timer_close(tu->timeri); + if (tu->queue) + kfree(tu->queue); + snd_magic_kfree(tu); + } +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +static void snd_timer_user_zero_id(snd_timer_id_t *id) +{ + id->dev_class = SNDRV_TIMER_CLASS_NONE; + id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; + id->card = -1; + id->device = -1; + id->subdevice = -1; +} + +static void snd_timer_user_copy_id(snd_timer_id_t *id, snd_timer_t *timer) +{ + id->dev_class = timer->tmr_class; + id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; + id->card = timer->card ? timer->card->number : -1; + id->device = timer->tmr_device; + id->subdevice = timer->tmr_subdevice; +} + +static int snd_timer_user_next_device(snd_timer_id_t *_tid) +{ + snd_timer_id_t id; + snd_timer_t *timer; + struct list_head *p; + + if (copy_from_user(&id, _tid, sizeof(id))) + return -EFAULT; + down(®ister_mutex); + if (id.dev_class < 0) { /* first item */ + if (list_empty(&snd_timer_list)) + snd_timer_user_zero_id(&id); + else { + timer = (snd_timer_t *)list_entry(snd_timer_list.next, snd_timer_t, device_list); + snd_timer_user_copy_id(&id, timer); + } + } else { + switch (id.dev_class) { + case SNDRV_TIMER_CLASS_GLOBAL: + id.device = id.device < 0 ? 0 : id.device + 1; + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_device >= id.device) { + snd_timer_user_copy_id(&id, timer); + break; + } + } + if (p == &snd_timer_list) + snd_timer_user_zero_id(&id); + break; + case SNDRV_TIMER_CLASS_CARD: + case SNDRV_TIMER_CLASS_PCM: + if (id.card < 0) { + id.card = 0; + } else { + if (id.card < 0) { + id.card = 0; + } else { + if (id.device < 0) { + id.device = 0; + } else { + id.subdevice = id.subdevice < 0 ? 0 : id.subdevice + 1; + } + } + } + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + if (timer->tmr_class > id.dev_class) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_class < id.dev_class) + continue; + if (timer->card->number > id.card) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->card->number < id.card) + continue; + if (timer->tmr_device > id.device) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_device < id.device) + continue; + if (timer->tmr_subdevice > id.subdevice) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_subdevice < id.subdevice) + continue; + snd_timer_user_copy_id(&id, timer); + break; + } + if (p == &snd_timer_list) + snd_timer_user_zero_id(&id); + break; + default: + snd_timer_user_zero_id(&id); + } + } + up(®ister_mutex); + if (copy_to_user(_tid, &id, sizeof(*_tid))) + return -EFAULT; + return 0; +} + +static int snd_timer_user_tselect(struct file *file, snd_timer_select_t *_tselect) +{ + snd_timer_user_t *tu; + snd_timer_select_t tselect; + char str[32]; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + if (tu->timeri) + snd_timer_close(tu->timeri); + if (copy_from_user(&tselect, _tselect, sizeof(tselect))) + return -EFAULT; + sprintf(str, "application %i", current->pid); + if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) + tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; + if ((tu->timeri = snd_timer_open(str, &tselect.id, current->pid)) == NULL) + return -ENODEV; + tu->timeri->callback = snd_timer_user_interrupt; + tu->timeri->callback_data = (void *)tu; + return 0; +} + +static int snd_timer_user_info(struct file *file, snd_timer_info_t *_info) +{ + snd_timer_user_t *tu; + snd_timer_info_t info; + snd_timer_t *t; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + t = tu->timeri->timer; + snd_assert(t != NULL, return -ENXIO); + memset(&info, 0, sizeof(info)); + info.card = t->card ? t->card->number : -1; + if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) + info.flags |= SNDRV_TIMER_FLG_SLAVE; + strncpy(info.id, t->id, sizeof(info.id)-1); + strncpy(info.name, t->name, sizeof(info.name)-1); + info.ticks = t->hw.ticks; + info.resolution = t->hw.resolution; + if (copy_to_user(_info, &info, sizeof(*_info))) + return -EFAULT; + return 0; +} + +static int snd_timer_user_params(struct file *file, snd_timer_params_t *_params) +{ + unsigned long flags; + snd_timer_user_t *tu; + snd_timer_params_t params; + snd_timer_t *t; + snd_timer_read_t *tr; + int err; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + t = tu->timeri->timer; + snd_assert(t != NULL, return -ENXIO); + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) { + err = -EINVAL; + goto _end; + } + if (params.queue_size > 0 && (params.queue_size < 32 || params.queue_size > 1024)) { + err = -EINVAL; + goto _end; + } + snd_timer_stop(tu->timeri); + spin_lock_irqsave(&t->lock, flags); + if (params.flags & SNDRV_TIMER_PSFLG_AUTO) { + tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; + } else { + tu->timeri->flags &= ~SNDRV_TIMER_IFLG_AUTO; + } + spin_unlock_irqrestore(&t->lock, flags); + if (params.queue_size > 0 && tu->queue_size != params.queue_size) { + tr = (snd_timer_read_t *)kmalloc(params.queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); + if (tr) { + kfree(tu->queue); + tu->queue_size = params.queue_size; + tu->queue = tr; + } + } + if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) { + tu->ticks = 1; + } else { + tu->ticks = params.ticks; + } + err = 0; + _end: + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +static int snd_timer_user_status(struct file *file, snd_timer_status_t *_status) +{ + unsigned long flags; + snd_timer_user_t *tu; + snd_timer_status_t status; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + memset(&status, 0, sizeof(status)); + status.resolution = snd_timer_resolution(tu->timeri); + status.lost = tu->timeri->lost; + status.overrun = tu->overrun; + spin_lock_irqsave(&tu->qlock, flags); + status.queue = tu->qused; + spin_unlock_irqrestore(&tu->qlock, flags); + if (copy_to_user(_status, &status, sizeof(status))) + return -EFAULT; + return 0; +} + +static int snd_timer_user_start(struct file *file) +{ + int err; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + snd_timer_stop(tu->timeri); + tu->timeri->lost = 0; + return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0; +} + +static int snd_timer_user_stop(struct file *file) +{ + int err; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0; +} + +static int snd_timer_user_continue(struct file *file) +{ + int err; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + tu->timeri->lost = 0; + return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0; +} + +static int snd_timer_user_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + switch (cmd) { + case SNDRV_TIMER_IOCTL_PVERSION: + return put_user(SNDRV_TIMER_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_TIMER_IOCTL_NEXT_DEVICE: + return snd_timer_user_next_device((snd_timer_id_t *)arg); + case SNDRV_TIMER_IOCTL_SELECT: + return snd_timer_user_tselect(file, (snd_timer_select_t *)arg); + case SNDRV_TIMER_IOCTL_INFO: + return snd_timer_user_info(file, (snd_timer_info_t *)arg); + case SNDRV_TIMER_IOCTL_PARAMS: + return snd_timer_user_params(file, (snd_timer_params_t *)arg); + case SNDRV_TIMER_IOCTL_STATUS: + return snd_timer_user_status(file, (snd_timer_status_t *)arg); + case SNDRV_TIMER_IOCTL_START: + return snd_timer_user_start(file); + case SNDRV_TIMER_IOCTL_STOP: + return snd_timer_user_stop(file); + case SNDRV_TIMER_IOCTL_CONTINUE: + return snd_timer_user_continue(file); + } + return -ENOTTY; +} + +static ssize_t snd_timer_user_read(struct file *file, char *buffer, size_t count, loff_t *offset) +{ + snd_timer_user_t *tu; + long result = 0; + int err = 0; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + while (count - result >= sizeof(snd_timer_read_t)) { + spin_lock_irq(&tu->qlock); + while (!tu->qused) { + wait_queue_t wait; + + if (file->f_flags & O_NONBLOCK) { + spin_unlock_irq(&tu->qlock); + err = -EAGAIN; + break; + } + + set_current_state(TASK_INTERRUPTIBLE); + init_waitqueue_entry(&wait, current); + add_wait_queue(&tu->qchange_sleep, &wait); + + spin_unlock(&tu->qlock); + schedule(); + spin_lock_irq(&tu->qlock); + + remove_wait_queue(&tu->qchange_sleep, &wait); + + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + spin_unlock_irq(&tu->qlock); + if (err < 0) + break; + + if (copy_to_user(buffer, &tu->queue[tu->qhead++], sizeof(snd_timer_read_t))) { + err = -EFAULT; + break; + } + + tu->qhead %= tu->queue_size; + spin_lock_irq(&tu->qlock); + tu->qused--; + spin_unlock_irq(&tu->qlock); + result += sizeof(snd_timer_read_t); + buffer += sizeof(snd_timer_read_t); + } + return err? err: result; +} + +static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait) +{ + unsigned int mask; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return 0); + + poll_wait(file, &tu->qchange_sleep, wait); + + mask = 0; + if (tu->qused) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static struct file_operations snd_timer_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_timer_user_read, + open: snd_timer_user_open, + release: snd_timer_user_release, + poll: snd_timer_user_poll, + ioctl: snd_timer_user_ioctl, +}; + +static snd_minor_t snd_timer_reg = +{ + comment: "timer", + f_ops: &snd_timer_f_ops, +}; + +/* + * ENTRY functions + */ + +static snd_info_entry_t *snd_timer_proc_entry = NULL; + +static int __init alsa_timer_init(void) +{ + int err; + snd_info_entry_t *entry; + +#ifdef CONFIG_SND_OSSEMUL + snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, "system timer"); +#endif + if ((entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = SNDRV_TIMER_DEVICES * 128; + entry->c.text.read = snd_timer_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_timer_proc_entry = entry; + if ((err = snd_timer_register_system()) < 0) + snd_printk("unable to register system timer (%i)\n", err); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, + NULL, 0, &snd_timer_reg, "timer"))<0) + snd_printk("unable to register timer device (%i)\n", err); + return 0; +} + +static void __exit alsa_timer_exit(void) +{ + struct list_head *p, *n; + + snd_unregister_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0); + /* unregister the system timer */ + list_for_each_safe(p, n, &snd_timer_list) { + snd_timer_t *timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + snd_timer_unregister(timer); + } + if (snd_timer_proc_entry) { + snd_info_unregister(snd_timer_proc_entry); + snd_timer_proc_entry = NULL; + } +#ifdef CONFIG_SND_OSSEMUL + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); +#endif +} + +module_init(alsa_timer_init) +module_exit(alsa_timer_exit) + +EXPORT_SYMBOL(snd_timer_open); +EXPORT_SYMBOL(snd_timer_close); +EXPORT_SYMBOL(snd_timer_resolution); +EXPORT_SYMBOL(snd_timer_start); +EXPORT_SYMBOL(snd_timer_stop); +EXPORT_SYMBOL(snd_timer_continue); +EXPORT_SYMBOL(snd_timer_new); +EXPORT_SYMBOL(snd_timer_global_new); +EXPORT_SYMBOL(snd_timer_global_free); +EXPORT_SYMBOL(snd_timer_global_register); +EXPORT_SYMBOL(snd_timer_global_unregister); +EXPORT_SYMBOL(snd_timer_interrupt); +EXPORT_SYMBOL(snd_timer_system_resolution); diff -Nru a/sound/core/wrappers.c b/sound/core/wrappers.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/core/wrappers.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,62 @@ +/* + * Various wrappers + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef ALSA_BUILD +#include "config.h" +#endif + +#define __NO_VERSION__ +#include +#include +#ifdef ALSA_BUILD +#if defined(CONFIG_MODVERSIONS) && !defined(__GENKSYMS__) && !defined(__DEPEND__) +#define MODVERSIONS +#include +#include "sndversions.h" +#endif +#endif +#include +#include +#include +#include +#include + +#ifdef CONFIG_SND_DEBUG_MEMORY +void *snd_wrapper_kmalloc(size_t size, int flags) +{ + return kmalloc(size, flags); +} + +void snd_wrapper_kfree(const void *obj) +{ + kfree(obj); +} + +void *snd_wrapper_vmalloc(unsigned long size) +{ + return vmalloc(size); +} + +void snd_wrapper_vfree(void *obj) +{ + vfree(obj); +} +#endif diff -Nru a/sound/drivers/Config.in b/sound/drivers/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/Config.in Tue Feb 19 18:09:00 2002 @@ -0,0 +1,12 @@ +# ALSA generic drivers + +mainmenu_option next_comment +comment 'Generic devices' + +dep_tristate 'Dummy (/dev/null) soundcard' CONFIG_SND_DUMMY $CONFIG_SND +dep_tristate 'Virtual MIDI soundcard' CONFIG_SND_VIRMIDI $CONFIG_SND $CONFIG_SND_SEQUENCER +dep_tristate 'MOTU MidiTimePiece AV multiport MIDI' CONFIG_SND_MTPAV $CONFIG_SND +dep_tristate 'UART16550 - MIDI only driver' CONFIG_SND_SERIAL_U16550 $CONFIG_SND +dep_tristate 'Generic MPU-401 UART driver' CONFIG_SND_MPU401 $CONFIG_SND + +endmenu diff -Nru a/sound/drivers/Makefile b/sound/drivers/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,36 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := drivers.o + +subdir-y := opl3 mpu401 +subdir-m := $(subdir-y) + +list-multi := snd-dummy.o snd-mtpav.o snd-serial-u16550.o snd-virmidi.o + +snd-dummy-objs := dummy.o +snd-mtpav-objs := mtpav.o +snd-serial-u16550-objs := serial-u16550.o +snd-virmidi-objs := virmidi.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_DUMMY) += snd-dummy.o +obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o +obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o +obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o + +include $(TOPDIR)/Rules.make + +snd-dummy.o: $(snd-dummy-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-dummy-objs) + +snd-mtpav.o: $(snd-mtpav-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mtpav-objs) + +snd-serial-u16550.o: $(snd-serial-u16550-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-serial-u16550-objs) + +snd-virmidi.o: $(snd-virmidi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-virmidi-objs) diff -Nru a/sound/drivers/dummy.c b/sound/drivers/dummy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/dummy.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,613 @@ +/* + * Dummy soundcard + * Copyright (c) by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Dummy soundcard (/dev/null)"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALSA,Dummy soundcard}}"); + +#define MAX_PCM_DEVICES 4 +#define MAX_PCM_SUBSTREAMS 16 +#define MAX_MIDI_DEVICES 2 + +#if 0 /* RME9652 emulation */ +#define MAX_BUFFER_SIZE (26 * 64 * 1024) +#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE +#define USE_CHANNELS_MIN 26 +#define USE_CHANNELS_MAX 26 +#define USE_PERIODS_MIN 2 +#define USE_PERIODS_MAX 2 +#endif + +/* defaults */ +#ifndef MAX_BUFFER_SIZE +#define MAX_BUFFER_SIZE (64*1024) +#endif +#ifndef USE_FORMATS +#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE) +#endif +#ifndef USE_CHANNELS_MIN +#define USE_CHANNELS_MIN 1 +#endif +#ifndef USE_CHANNELS_MAX +#define USE_CHANNELS_MAX 2 +#endif +#ifndef USE_PERIODS_MIN +#define USE_PERIODS_MIN 1 +#endif +#ifndef USE_PERIODS_MAX +#define USE_PERIODS_MAX 1024 +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +static int snd_pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; +//static int snd_midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for dummy soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for dummy soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable this dummy soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_pcm_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_devs, "PCM devices # (0-4) for dummy driver."); +MODULE_PARM_SYNTAX(snd_pcm_devs, SNDRV_ENABLED ",allows:{{0,4}},default:1,dialog:list"); +MODULE_PARM(snd_pcm_substreams, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_substreams, "PCM substreams # (1-16) for dummy driver."); +MODULE_PARM_SYNTAX(snd_pcm_substreams, SNDRV_ENABLED ",allows:{{1,16}},default:8,dialog:list"); +//MODULE_PARM(snd_midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +//MODULE_PARM_DESC(snd_midi_devs, "MIDI devices # (0-2) for dummy driver."); +//MODULE_PARM_SYNTAX(snd_midi_devs, SNDRV_ENABLED ",allows:{{0,2}},default:8,dialog:list"); + +#define MIXER_ADDR_MASTER 0 +#define MIXER_ADDR_LINE 1 +#define MIXER_ADDR_MIC 2 +#define MIXER_ADDR_SYNTH 3 +#define MIXER_ADDR_CD 4 +#define MIXER_ADDR_LAST 4 + +typedef struct snd_card_dummy { + snd_card_t *card; + spinlock_t mixer_lock; + int mixer_volume[MIXER_ADDR_LAST+1][2]; + int capture_source[MIXER_ADDR_LAST+1][2]; +} snd_card_dummy_t; + +typedef struct snd_card_dummy_pcm { + snd_card_dummy_t *dummy; + spinlock_t lock; + struct timer_list timer; + unsigned int pcm_size; + unsigned int pcm_count; + unsigned int pcm_bps; /* bytes per second */ + unsigned int pcm_jiffie; /* bytes per one jiffie */ + unsigned int pcm_irq_pos; /* IRQ position */ + unsigned int pcm_buf_pos; /* position in buffer */ + snd_pcm_substream_t *substream; +} snd_card_dummy_pcm_t; + +static snd_card_t *snd_dummy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int snd_card_dummy_playback_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_card_dummy_capture_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static void snd_card_dummy_pcm_timer_start(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + + dpcm->timer.expires = 1 + jiffies; + add_timer(&dpcm->timer); +} + +static void snd_card_dummy_pcm_timer_stop(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + + del_timer(&dpcm->timer); +} + +static int snd_card_dummy_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_card_dummy_pcm_timer_start(substream); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + snd_card_dummy_pcm_timer_stop(substream); + } else { + return -EINVAL; + } + return 0; +} + +static int snd_card_dummy_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_card_dummy_pcm_timer_start(substream); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + snd_card_dummy_pcm_timer_stop(substream); + } else { + return -EINVAL; + } + return 0; +} + +static int snd_card_dummy_pcm_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + unsigned int bps; + + bps = runtime->rate * runtime->channels; + bps *= snd_pcm_format_width(runtime->format); + bps /= 8; + if (bps <= 0) + return -EINVAL; + dpcm->pcm_bps = bps; + dpcm->pcm_jiffie = bps / HZ; + dpcm->pcm_size = snd_pcm_lib_buffer_bytes(substream); + dpcm->pcm_count = snd_pcm_lib_period_bytes(substream); + dpcm->pcm_irq_pos = 0; + dpcm->pcm_buf_pos = 0; + return 0; +} + +static int snd_card_dummy_playback_prepare(snd_pcm_substream_t * substream) +{ + return snd_card_dummy_pcm_prepare(substream); +} + +static int snd_card_dummy_capture_prepare(snd_pcm_substream_t * substream) +{ + return snd_card_dummy_pcm_prepare(substream); +} + +static void snd_card_dummy_pcm_timer_function(unsigned long data) +{ + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, (void *)data, return); + + dpcm->timer.expires = 1 + jiffies; + add_timer(&dpcm->timer); + spin_lock_irq(&dpcm->lock); + dpcm->pcm_irq_pos += dpcm->pcm_jiffie; + dpcm->pcm_buf_pos += dpcm->pcm_jiffie; + dpcm->pcm_buf_pos %= dpcm->pcm_size; + while (dpcm->pcm_irq_pos >= dpcm->pcm_count) { + dpcm->pcm_irq_pos -= dpcm->pcm_count; + snd_pcm_period_elapsed(dpcm->substream); + } + spin_unlock_irq(&dpcm->lock); +} + +static snd_pcm_uframes_t snd_card_dummy_playback_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + + return bytes_to_frames(runtime, dpcm->pcm_buf_pos); +} + +static snd_pcm_uframes_t snd_card_dummy_capture_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + + return bytes_to_frames(runtime, dpcm->pcm_buf_pos); +} + +static snd_pcm_hardware_t snd_card_dummy_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: USE_FORMATS, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: USE_CHANNELS_MIN, + channels_max: USE_CHANNELS_MAX, + buffer_bytes_max: MAX_BUFFER_SIZE, + period_bytes_min: 64, + period_bytes_max: MAX_BUFFER_SIZE, + periods_min: USE_PERIODS_MIN, + periods_max: USE_PERIODS_MAX, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_card_dummy_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: USE_FORMATS, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: USE_CHANNELS_MIN, + channels_max: USE_CHANNELS_MAX, + buffer_bytes_max: MAX_BUFFER_SIZE, + period_bytes_min: 64, + period_bytes_max: MAX_BUFFER_SIZE, + periods_min: USE_PERIODS_MIN, + periods_max: USE_PERIODS_MAX, + fifo_size: 0, +}; + +static void snd_card_dummy_runtime_free(snd_pcm_runtime_t *runtime) +{ + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + snd_magic_kfree(dpcm); +} + +static int snd_card_dummy_playback_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm; + + dpcm = snd_magic_kcalloc(snd_card_dummy_pcm_t, 0, GFP_KERNEL); + if (dpcm == NULL) + return -ENOMEM; + if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) { + snd_magic_kfree(dpcm); + return -ENOMEM; + } + dpcm->timer.data = (unsigned long) dpcm; + dpcm->timer.function = snd_card_dummy_pcm_timer_function; + spin_lock_init(&dpcm->lock); + dpcm->substream = substream; + runtime->private_data = dpcm; + runtime->private_free = snd_card_dummy_runtime_free; + runtime->hw = snd_card_dummy_playback; + if (substream->pcm->device & 1) { + runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; + runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; + } + if (substream->pcm->device & 2) + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); + return 0; +} + +static int snd_card_dummy_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm; + + dpcm = snd_magic_kcalloc(snd_card_dummy_pcm_t, 0, GFP_KERNEL); + if (dpcm == NULL) + return -ENOMEM; + if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) { + snd_magic_kfree(dpcm); + return -ENOMEM; + } + memset(runtime->dma_area, 0, runtime->dma_bytes); + dpcm->timer.data = (unsigned long) dpcm; + dpcm->timer.function = snd_card_dummy_pcm_timer_function; + spin_lock_init(&dpcm->lock); + dpcm->substream = substream; + runtime->private_data = dpcm; + runtime->private_free = snd_card_dummy_runtime_free; + runtime->hw = snd_card_dummy_capture; + if (substream->pcm->device == 1) { + runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; + runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; + } + if (substream->pcm->device & 2) + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); + return 0; +} + +static int snd_card_dummy_playback_close(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return 0; +} + +static int snd_card_dummy_capture_close(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return 0; +} + +static snd_pcm_ops_t snd_card_dummy_playback_ops = { + open: snd_card_dummy_playback_open, + close: snd_card_dummy_playback_close, + ioctl: snd_card_dummy_playback_ioctl, + prepare: snd_card_dummy_playback_prepare, + trigger: snd_card_dummy_playback_trigger, + pointer: snd_card_dummy_playback_pointer, +}; + +static snd_pcm_ops_t snd_card_dummy_capture_ops = { + open: snd_card_dummy_capture_open, + close: snd_card_dummy_capture_close, + ioctl: snd_card_dummy_capture_ioctl, + prepare: snd_card_dummy_capture_prepare, + trigger: snd_card_dummy_capture_trigger, + pointer: snd_card_dummy_capture_pointer, +}; + +static int __init snd_card_dummy_pcm(snd_card_dummy_t *dummy, int device, int substreams) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device, substreams, substreams, &pcm)) < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops); + pcm->private_data = dummy; + pcm->info_flags = 0; + strcpy(pcm->name, "Dummy PCM"); + return 0; +} + +#define DUMMY_VOLUME(xname, xindex, addr) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_dummy_volume_info, \ + get: snd_dummy_volume_get, put: snd_dummy_volume_put, \ + private_value: addr } + +static int snd_dummy_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_dummy_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value; + + spin_lock_irqsave(&dummy->mixer_lock, flags); + ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0]; + ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1]; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return 0; +} + +static int snd_dummy_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0] % 101; + right = ucontrol->value.integer.value[1] % 101; + spin_lock_irqsave(&dummy->mixer_lock, flags); + change = dummy->mixer_volume[addr][0] != left && + dummy->mixer_volume[addr][1] != right; + dummy->mixer_volume[addr][0] = left; + dummy->mixer_volume[addr][1] = right; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return change; +} + +#define DUMMY_CAPSRC(xname, xindex, addr) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_dummy_capsrc_info, \ + get: snd_dummy_capsrc_get, put: snd_dummy_capsrc_put, \ + private_value: addr } + +static int snd_dummy_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_dummy_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value; + + spin_lock_irqsave(&dummy->mixer_lock, flags); + ucontrol->value.integer.value[0] = dummy->capture_source[addr][0]; + ucontrol->value.integer.value[1] = dummy->capture_source[addr][1]; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return 0; +} + +static int snd_dummy_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0] & 1; + right = ucontrol->value.integer.value[1] & 1; + spin_lock_irqsave(&dummy->mixer_lock, flags); + change = dummy->capture_source[addr][0] != left && + dummy->capture_source[addr][1] != right; + dummy->capture_source[addr][0] = left; + dummy->capture_source[addr][1] = right; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return change; +} + +#define DUMMY_CONTROLS (sizeof(snd_dummy_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_dummy_controls[] = { +DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER), +DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH), +DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE), +DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC), +DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD), +DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER) +}; + +int __init snd_card_dummy_new_mixer(snd_card_dummy_t * dummy) +{ + snd_card_t *card = dummy->card; + int idx, err; + + snd_assert(dummy != NULL, return -EINVAL); + spin_lock_init(&dummy->mixer_lock); + strcpy(card->mixername, "Dummy Mixer"); + + for (idx = 0; idx < DUMMY_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0) + return err; + } + return 0; +} + +static int __init snd_card_dummy_probe(int dev) +{ + snd_card_t *card; + struct snd_card_dummy *dummy; + int idx, err; + + if (!snd_enable[dev]) + return -ENODEV; + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_dummy)); + if (card == NULL) + return -ENOMEM; + dummy = (struct snd_card_dummy *)card->private_data; + dummy->card = card; + for (idx = 0; idx < MAX_PCM_DEVICES && idx < snd_pcm_devs[dev]; idx++) { + if (snd_pcm_substreams[dev] < 1) + snd_pcm_substreams[dev] = 1; + if (snd_pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) + snd_pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; + if ((err = snd_card_dummy_pcm(dummy, idx, snd_pcm_substreams[dev])) < 0) + goto __nodev; + } + if ((err = snd_card_dummy_new_mixer(dummy)) < 0) + goto __nodev; + strcpy(card->driver, "Dummy"); + strcpy(card->shortname, "Dummy"); + sprintf(card->longname, "Dummy %i", dev + 1); + if ((err = snd_card_register(card)) == 0) { + snd_dummy_cards[dev] = card; + return 0; + } + __nodev: + snd_card_free(card); + return err; +} + +static int __init alsa_card_dummy_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_card_dummy_probe(dev) < 0) { +#ifdef MODULE + snd_printk("Dummy soundcard #%i not found or device busy\n", dev + 1); +#endif + break; + } + cards++; + } + if (!cards) { +#ifdef MODULE + snd_printk("Dummy soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_dummy_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_dummy_cards[idx]); +} + +module_init(alsa_card_dummy_init) +module_exit(alsa_card_dummy_exit) + +#ifndef MODULE + +/* format is: snd-dummy=snd_enable,snd_index,snd_id, + snd_pcm_devs,snd_pcm_substreams */ + +static int __init alsa_card_dummy_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_pcm_devs[nr_dev]) == 2 && + get_option(&str,&snd_pcm_substreams[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-dummy=", alsa_card_dummy_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/mpu401/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,52 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _mpu401.o + +list-multi := snd-mpu401.o snd-mpu401-uart.o + +export-objs := mpu401_uart.o + +snd-mpu401-objs := mpu401.o +snd-mpu401-uart-objs := mpu401_uart.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_MPU401) += snd-mpu401.o snd-mpu401-uart.o +obj-$(CONFIG_SND_ALS100) += snd-mpu401-uart.o +obj-$(CONFIG_SND_AZT2320) += snd-mpu401-uart.o +obj-$(CONFIG_SND_DT0197H) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES18XX) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPL3SA2) += snd-mpu401-uart.o +obj-$(CONFIG_SND_AD1816A) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CS4231) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CS4232) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CS4236) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES1688) += snd-mpu401-uart.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPTI93X) += snd-mpu401-uart.o +obj-$(CONFIG_SND_SB16) += snd-mpu401-uart.o +obj-$(CONFIG_SND_SBAWE) += snd-mpu401-uart.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ALS4000) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CMIPCI) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES1938) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES1968) += snd-mpu401-uart.o +obj-$(CONFIG_SND_FM801) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o +obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o +obj-$(CONFIG_SND_VIA686) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o +obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o +obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o + +include $(TOPDIR)/Rules.make + +snd-mpu401.o: $(snd-mpu401-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mpu401-objs) + +snd-mpu401-uart.o: $(snd-mpu401-uart-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mpu401-uart-objs) diff -Nru a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/mpu401/mpu401.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,155 @@ +/* + * Driver for generic MPU-401 boards (UART mode only) + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("MPU-401 UART"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* MPU-401 port number */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* MPU-401 IRQ */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for MPU-401 device."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for MPU-401 device."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable MPU-401 device."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for MPU-401 device."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for MPU-401 device."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); + +static snd_card_t *snd_mpu401_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +static int __init snd_card_mpu401_probe(int dev) +{ + snd_card_t *card; + int err; + + if (snd_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_port\n"); + return -EINVAL; + } + if (snd_irq[dev] == SNDRV_AUTO_IRQ) { + snd_printk("specify or disable IRQ port\n"); + return -EINVAL; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + snd_port[dev], 0, + snd_irq[dev], snd_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) { + snd_printk("MPU401 not detected at 0x%lx\n", snd_port[dev]); + snd_card_free(card); + return -ENODEV; + } + strcpy(card->driver, "MPU-401 UART"); + strcpy(card->shortname, card->driver); + sprintf(card->longname, "%s at 0x%lx, ", card->shortname, snd_port[dev]); + if (snd_irq[dev] >= 0) { + sprintf(card->longname + strlen(card->longname), "IRQ %d", snd_irq[dev]); + } else { + strcat(card->longname, "polled"); + } + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_mpu401_cards[dev] = card; + return 0; +} + +static int __init alsa_card_mpu401_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + if (snd_card_mpu401_probe(dev) >= 0) + cards++; + } + if (!cards) { +#ifdef MODULE + snd_printk("MPU-401 device not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_mpu401_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_mpu401_cards[idx]); +} + +module_init(alsa_card_mpu401_init) +module_exit(alsa_card_mpu401_exit) + +#ifndef MODULE + +/* format is: snd-mpu401=snd_enable,snd_index,snd_id,snd_port,snd_irq */ + +static int __init alsa_card_mpu401_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-mpu401=", alsa_card_mpu401_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/mpu401/mpu401_uart.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,444 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of MPU-401 in UART mode + * + * MPU-401 supports UART mode which is not capable generate transmit + * interrupts thus output is done via polling. Also, if irq < 0, then + * input is done also via polling. Do not expect good performance. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode"); +MODULE_LICENSE("GPL"); + +static void snd_mpu401_uart_input_read(mpu401_t * mpu); +static void snd_mpu401_uart_output_write(mpu401_t * mpu); + +/* + + */ + +#define snd_mpu401_input_avail(mpu) (!(inb(MPU401C(mpu)) & 0x80)) +#define snd_mpu401_output_ready(mpu) (!(inb(MPU401C(mpu)) & 0x40)) + +#define MPU401_RESET 0xff +#define MPU401_ENTER_UART 0x3f +#define MPU401_ACK 0xfe + +static void snd_mpu401_uart_clear_rx(mpu401_t *mpu) +{ + int timeout = 100000; + for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--) + inb(MPU401D(mpu)); +#ifdef CONFIG_SND_DEBUG + if (timeout <= 0) + snd_printk("cmd: clear rx timeout (status = 0x%x)\n", inb(MPU401C(mpu))); +#endif +} + +static void _snd_mpu401_uart_interrupt(mpu401_t *mpu) +{ + if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) + snd_mpu401_uart_input_read(mpu); + else + snd_mpu401_uart_clear_rx(mpu); + /* ok. for better Tx performance try do some output when input is done */ + if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) + snd_mpu401_uart_output_write(mpu); +} + +void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + mpu401_t *mpu = snd_magic_cast(mpu401_t, dev_id, return); + + if (mpu == NULL) + return; + _snd_mpu401_uart_interrupt(mpu); +} + +static void snd_mpu401_uart_timer(unsigned long data) +{ + unsigned long flags; + mpu401_t *mpu = snd_magic_cast(mpu401_t, (void *)data, return); + + spin_lock_irqsave(&mpu->timer_lock, flags); + /*mpu->mode |= MPU401_MODE_TIMER;*/ + mpu->timer.expires = 1 + jiffies; + add_timer(&mpu->timer); + spin_unlock_irqrestore(&mpu->timer_lock, flags); + if (mpu->rmidi) + _snd_mpu401_uart_interrupt(mpu); +} + +static void snd_mpu401_uart_add_timer (mpu401_t *mpu, int input) +{ + unsigned long flags; + + spin_lock_irqsave (&mpu->timer_lock, flags); + if (mpu->timer_invoked == 0) { + mpu->timer.data = (unsigned long)mpu; + mpu->timer.function = snd_mpu401_uart_timer; + mpu->timer.expires = 1 + jiffies; + add_timer(&mpu->timer); + } + mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : MPU401_MODE_OUTPUT_TIMER; + spin_unlock_irqrestore (&mpu->timer_lock, flags); +} + +static void snd_mpu401_uart_remove_timer (mpu401_t *mpu, int input) +{ + unsigned long flags; + + spin_lock_irqsave (&mpu->timer_lock, flags); + if (mpu->timer_invoked) { + mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : ~MPU401_MODE_OUTPUT_TIMER; + if (! mpu->timer_invoked) + del_timer(&mpu->timer); + } + spin_unlock_irqrestore (&mpu->timer_lock, flags); +} + +/* + + */ + +static void snd_mpu401_uart_cmd(mpu401_t * mpu, unsigned char cmd, int ack) +{ + unsigned long flags; + int timeout, ok; + + spin_lock_irqsave(&mpu->input_lock, flags); + if (mpu->hardware != MPU401_HW_TRID4DWAVE) { + outb(0x00, MPU401D(mpu)); + /*snd_mpu401_uart_clear_rx(mpu);*/ + } + /* ok. standard MPU-401 initialization */ + if (mpu->hardware != MPU401_HW_SB) { + for (timeout = 1000; timeout > 0 && !snd_mpu401_output_ready(mpu); timeout--) + udelay(10); +#ifdef CONFIG_SND_DEBUG + if (!timeout) + snd_printk("cmd: tx timeout (status = 0x%x)\n", inb(MPU401C(mpu))); +#endif + } + outb(cmd, MPU401C(mpu)); + if (ack) { + ok = 0; + timeout = 10000; + while (!ok && timeout-- > 0) { + if (snd_mpu401_input_avail(mpu)) { + if (inb(MPU401D(mpu)) == MPU401_ACK) + ok = 1; + } + } + if (!ok && inb(MPU401D(mpu)) == MPU401_ACK) + ok = 1; + } else { + ok = 1; + } + spin_unlock_irqrestore(&mpu->input_lock, flags); + if (! ok) + snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, inb(MPU401C(mpu)), inb(MPU401D(mpu))); + // snd_printk("cmd: 0x%x at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, inb(MPU401C(mpu)), inb(MPU401D(mpu))); +} + +/* + * input/output open/close - protected by open_mutex in rawmidi.c + */ +static int snd_mpu401_uart_input_open(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + int err; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + if (mpu->open_input && (err = mpu->open_input(mpu)) < 0) + return err; + if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1); + snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1); + } + mpu->substream_input = substream; + set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); + return 0; +} + +static int snd_mpu401_uart_output_open(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + int err; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + if (mpu->open_output && (err = mpu->open_output(mpu)) < 0) + return err; + if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1); + snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1); + } + mpu->substream_output = substream; + set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); + return 0; +} + +static int snd_mpu401_uart_input_close(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); + mpu->substream_input = NULL; + if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); + if (mpu->close_input) + mpu->close_input(mpu); + return 0; +} + +static int snd_mpu401_uart_output_close(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); + mpu->substream_output = NULL; + if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); + if (mpu->close_output) + mpu->close_output(mpu); + return 0; +} + +/* + * trigger input + */ +static void snd_mpu401_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mpu401_t *mpu; + int max = 64; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return); + spin_lock_irqsave(&mpu->input_lock, flags); + if (up) { + if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { + /* flush FIFO */ + while (max-- > 0) + inb(MPU401D(mpu)); + } + if (mpu->irq < 0) + snd_mpu401_uart_add_timer(mpu, 1); + } else { + if (mpu->irq < 0) + snd_mpu401_uart_remove_timer(mpu, 1); + clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); + } + spin_unlock_irqrestore(&mpu->input_lock, flags); + if (up) + snd_mpu401_uart_input_read(mpu); +} + +static void snd_mpu401_uart_input_read(mpu401_t * mpu) +{ + int max = 128; + unsigned char byte; + + /* prevent double enter via event callback */ + if (test_and_set_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode)) + return; + spin_lock(&mpu->input_lock); + while (max-- > 0) { + if (snd_mpu401_input_avail(mpu)) { + byte = inb(MPU401D(mpu)); + if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { + spin_unlock(&mpu->input_lock); + snd_rawmidi_receive(mpu->substream_input, &byte, 1); + spin_lock(&mpu->input_lock); + } + } else { + break; /* input not available */ + } + } + spin_unlock(&mpu->input_lock); + clear_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode); +} + +/* + * Tx FIFO sizes: + * CS4237B - 16 bytes + * AudioDrive ES1688 - 12 bytes + * S3 SonicVibes - 8 bytes + * SoundBlaster AWE 64 - 2 bytes (ugly hardware) + */ + +static void snd_mpu401_uart_output_write(mpu401_t * mpu) +{ + unsigned char byte; + int max = 256, timeout; + + if (!test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) + return; + /* prevent double enter */ + if (test_and_set_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode)) + return; + do { + spin_lock(&mpu->output_lock); + if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) { + for (timeout = 100; timeout > 0; timeout--) { + if (snd_mpu401_output_ready(mpu)) { + outb(byte, MPU401D(mpu)); + snd_rawmidi_transmit_ack(mpu->substream_output, 1); + break; + } + } + } else { + snd_mpu401_uart_remove_timer (mpu, 0); + max = 1; /* no other data - leave the tx loop */ + } + spin_unlock(&mpu->output_lock); + } while (--max > 0); + clear_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode); +} + +static void snd_mpu401_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mpu401_t *mpu; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return); + spin_lock_irqsave(&mpu->output_lock, flags); + if (up) { + set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); + snd_mpu401_uart_add_timer(mpu, 0); + } else { + snd_mpu401_uart_remove_timer(mpu, 0); + clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); + } + spin_unlock_irqrestore(&mpu->output_lock, flags); + if (up) + snd_mpu401_uart_output_write(mpu); +} + +/* + + */ + +static snd_rawmidi_ops_t snd_mpu401_uart_output = +{ + open: snd_mpu401_uart_output_open, + close: snd_mpu401_uart_output_close, + trigger: snd_mpu401_uart_output_trigger, +}; + +static snd_rawmidi_ops_t snd_mpu401_uart_input = +{ + open: snd_mpu401_uart_input_open, + close: snd_mpu401_uart_input_close, + trigger: snd_mpu401_uart_input_trigger, +}; + +static void snd_mpu401_uart_free(snd_rawmidi_t *rmidi) +{ + mpu401_t *mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return); + if (mpu->irq_flags && mpu->irq >= 0) + free_irq(mpu->irq, (void *) mpu); + if (mpu->res) { + release_resource(mpu->res); + kfree_nocheck(mpu->res); + } + snd_magic_kfree(mpu); +} + +int snd_mpu401_uart_new(snd_card_t * card, int device, + unsigned short hardware, + unsigned long port, int integrated, + int irq, int irq_flags, + snd_rawmidi_t ** rrawmidi) +{ + mpu401_t *mpu; + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(card, "MPU-401U", device, 1, 1, &rmidi)) < 0) + return err; + mpu = snd_magic_kcalloc(mpu401_t, 0, GFP_KERNEL); + if (mpu == NULL) { + snd_device_free(card, rmidi); + return -ENOMEM; + } + rmidi->private_data = mpu; + rmidi->private_free = snd_mpu401_uart_free; + spin_lock_init(&mpu->input_lock); + spin_lock_init(&mpu->output_lock); + spin_lock_init(&mpu->timer_lock); + mpu->hardware = hardware; + if (!integrated) { + if ((mpu->res = request_region(port, 2, "MPU401 UART")) == NULL) { + snd_device_free(card, rmidi); + return -EBUSY; + } + } + mpu->port = port; + if (irq >= 0 && irq_flags) { + if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, "MPU401 UART", (void *) mpu)) { + snd_printk("unable to grab IRQ %d\n", irq); + snd_device_free(card, rmidi); + return -EBUSY; + } + mpu->irq = irq; + mpu->irq_flags = irq_flags; + } + strcpy(rmidi->name, "MPU-401 (UART)"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + mpu->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} + +EXPORT_SYMBOL(snd_mpu401_uart_interrupt); +EXPORT_SYMBOL(snd_mpu401_uart_new); + +/* + * INIT part + */ + +static int __init alsa_mpu401_uart_init(void) +{ + return 0; +} + +static void __exit alsa_mpu401_uart_exit(void) +{ +} + +module_init(alsa_mpu401_uart_init) +module_exit(alsa_mpu401_uart_exit) diff -Nru a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/mtpav.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,812 @@ +/* + * MOTU Midi Timepiece ALSA Main routines + * Copyright by Michael T. Mayers (c) Jan 09, 2000 + * mail: tweakoz@pacbell.net + * Thanks to John Galbraith + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This driver is for the 'Mark Of The Unicorn' (MOTU) + * MidiTimePiece AV multiport MIDI interface + * + * IOPORTS + * ------- + * 8 MIDI Ins and 8 MIDI outs + * Video Sync In (BNC), Word Sync Out (BNC), + * ADAT Sync Out (DB9) + * SMPTE in/out (1/4") + * 2 programmable pedal/footswitch inputs and 4 programmable MIDI controller knobs. + * Macintosh RS422 serial port + * RS422 "network" port for ganging multiple MTP's + * PC Parallel Port ( which this driver currently uses ) + * + * MISC FEATURES + * ------------- + * Hardware MIDI routing, merging, and filtering + * MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources + * 128 'scene' memories, recallable from MIDI program change + * + * + * ChangeLog + * Jun 11 2001 Takashi Iwai + * - Recoded & debugged + * - Added timer interrupt for midi outputs + * - snd_hwports is between 1 and 8, which specifies the number of hardware ports. + * The three global ports, computer, adat and broadcast ports, are created + * always after h/w and remote ports. + * + */ + +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#include +#include + +/* + * globals + */ +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Michael T. Mayers"); +MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{MOTU,MidiTimePiece AV multiport MIDI}}"); + +// io resources +#define MTPAV_IOBASE 0x378 +#define MTPAV_IRQ 7 +#define MTPAV_MAX_PORTS 8 + +static int snd_index = SNDRV_DEFAULT_IDX1; +static char *snd_id = SNDRV_DEFAULT_STR1; +static long snd_port = MTPAV_IOBASE; /* 0x378, 0x278 */ +static int snd_irq = MTPAV_IRQ; /* 7, 5 */ +static int snd_hwports = MTPAV_MAX_PORTS; /* use hardware ports 1-8 */ + +MODULE_PARM(snd_index, "i"); +MODULE_PARM_DESC(snd_index, "Index value for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "s"); +MODULE_PARM_DESC(snd_id, "ID string for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_port, "l"); +MODULE_PARM_DESC(snd_port, "Parallel port # for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x378},{0x278}},dialog:list"); +MODULE_PARM(snd_irq, "i"); +MODULE_PARM_DESC(snd_irq, "Parallel IRQ # for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{7},{5}},dialog:list"); +MODULE_PARM(snd_hwports, "i"); +MODULE_PARM_DESC(snd_hwports, "Hardware ports # for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(snd_hwports, SNDRV_ENABLED ",allows:{{1,8}},dialog:list"); + +/* + * defines + */ +//#define USE_FAKE_MTP // dont actually read/write to MTP device (for debugging without an actual unit) (does not work yet) + +// parallel port usage masks +#define SIGS_BYTE 0x08 +#define SIGS_RFD 0x80 +#define SIGS_IRQ 0x40 +#define SIGS_IN0 0x10 +#define SIGS_IN1 0x20 + +#define SIGC_WRITE 0x04 +#define SIGC_READ 0x08 +#define SIGC_INTEN 0x10 + +#define DREG 0 +#define SREG 1 +#define CREG 2 + +// +#define MTPAV_MODE_INPUT_OPENED 0x01 +#define MTPAV_MODE_OUTPUT_OPENED 0x02 +#define MTPAV_MODE_INPUT_TRIGGERED 0x04 +#define MTPAV_MODE_OUTPUT_TRIGGERED 0x08 + +#define NUMPORTS (0x12+1) + + +/* + */ + +typedef struct mtpav_port { + u8 number; + u8 hwport; + u8 mode; + snd_rawmidi_substream_t *input; + snd_rawmidi_substream_t *output; +} mtpav_port_t; + +typedef struct mtpav { + snd_card_t *card; + unsigned long port; + struct resource *res_port; + int irq; /* interrupt (for inputs) */ + spinlock_t spinlock; + int share_irq; /* number of accesses to input interrupts */ + int istimer; /* number of accesses to timer interrupts */ + struct timer_list timer; /* timer interrupts for outputs */ + snd_rawmidi_t *rmidi; + int num_ports; /* number of hw ports (1-8) */ + mtpav_port_t ports[NUMPORTS]; /* all ports including computer, adat and bc */ + + u32 inmidiport; /* selected input midi port */ + u32 inmidistate; /* during midi command 0xf5 */ + + u32 outmidihwport; /* selected output midi hw port */ +} mtpav_t; + + +/* + * global instance + * hey, we handle at most only one card.. + */ +static mtpav_t *mtp_card; + +/* + * possible hardware ports (selected by 0xf5 port message) + * 0x00 all ports + * 0x01 .. 0x08 this MTP's ports 1..8 + * 0x09 .. 0x10 networked MTP's ports (9..16) + * 0x11 networked MTP's computer port + * 0x63 to ADAT + * + * mappig: + * subdevice 0 - (X-1) ports + * X - (2*X-1) networked ports + * X computer + * X+1 ADAT + * X+2 all ports + * + * where X = chip->num_ports + */ + +#define MTPAV_PIDX_COMPUTER 0 +#define MTPAV_PIDX_ADAT 1 +#define MTPAV_PIDX_BROADCAST 2 + + +static int translate_subdevice_to_hwport(mtpav_t *chip, int subdev) +{ + if (subdev < 0) + return 0x01; /* invalid - use port 0 as default */ + else if (subdev < chip->num_ports) + return subdev + 1; /* single mtp port */ + else if (subdev < chip->num_ports * 2) + return subdev - chip->num_ports + 0x09; /* remote port */ + else if (subdev == chip->num_ports * 2 + MTPAV_PIDX_COMPUTER) + return 0x11; /* computer port */ + else if (subdev == chip->num_ports + MTPAV_PIDX_ADAT) + return 0x63; /* ADAT */ + return 0; /* all ports */ +} + +static int translate_hwport_to_subdevice(mtpav_t *chip, int hwport) +{ + int port; + if (hwport <= 0x00) /* all ports */ + return chip->num_ports + MTPAV_PIDX_BROADCAST; + else if (hwport <= 0x08) { /* single port */ + port = hwport - 1; + if (port >= chip->num_ports) + port = 0; + return port; + } else if (hwport <= 0x10) { /* remote port */ + port = hwport - 0x09 + chip->num_ports; + if (port >= chip->num_ports * 2) + port = chip->num_ports; + return port; + } else if (hwport == 0x11) /* computer port */ + return chip->num_ports + MTPAV_PIDX_COMPUTER; + else /* ADAT */ + return chip->num_ports + MTPAV_PIDX_ADAT; +} + + +/* + */ + +static u8 snd_mtpav_getreg(mtpav_t *chip, u16 reg) +{ + u8 rval = 0; + + if (reg == SREG) { + rval = inb(chip->port + SREG); + rval = (rval & 0xf8); + } else if (reg == CREG) { + rval = inb(chip->port + CREG); + rval = (rval & 0x1c); + } + + return rval; +} + +/* + */ + +static void snd_mtpav_mputreg(mtpav_t *chip, u16 reg, u8 val) +{ + if (reg == DREG) { + outb(val, chip->port + DREG); + } else if (reg == CREG) { + outb(val, chip->port + CREG); + } +} + +/* + */ + +static void snd_mtpav_wait_rfdhi(mtpav_t *chip) +{ + int counts = 10000; + u8 sbyte; + + sbyte = snd_mtpav_getreg(chip, SREG); + while (!(sbyte & SIGS_RFD) && counts--) { + sbyte = snd_mtpav_getreg(chip, SREG); + udelay(10); + } +} + +static void snd_mtpav_send_byte(mtpav_t *chip, u8 byte) +{ + u8 tcbyt; + u8 clrwrite; + u8 setwrite; + + snd_mtpav_wait_rfdhi(chip); + + ///////////////// + + tcbyt = snd_mtpav_getreg(chip, CREG); + clrwrite = tcbyt & (SIGC_WRITE ^ 0xff); + setwrite = tcbyt | SIGC_WRITE; + + snd_mtpav_mputreg(chip, DREG, byte); + snd_mtpav_mputreg(chip, CREG, clrwrite); // clear write bit + + snd_mtpav_mputreg(chip, CREG, setwrite); // set write bit + +} + + +/* + */ + +/* call this with spin lock held */ +static void snd_mtpav_output_port_write(mtpav_port_t *port, + snd_rawmidi_substream_t *substream) +{ + u8 outbyte; + + // send port change command if necessary + + if (port->hwport != mtp_card->outmidihwport) { + mtp_card->outmidihwport = port->hwport; + + snd_mtpav_send_byte(mtp_card, 0xf5); + snd_mtpav_send_byte(mtp_card, port->hwport); + //snd_printk("new outport: 0x%x\n", (unsigned int) port->hwport); + + } + + // send data + + while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1) + snd_mtpav_send_byte(mtp_card, outbyte); +} + +static void snd_mtpav_output_write(snd_rawmidi_substream_t * substream) +{ + mtpav_port_t *port = &mtp_card->ports[substream->number]; + unsigned long flags; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + snd_mtpav_output_port_write(port, substream); + spin_unlock_irqrestore(&mtp_card->spinlock, flags); +} + + +/* + * mtpav control + */ + +static void snd_mtpav_portscan(mtpav_t *chip) // put mtp into smart routing mode +{ + u8 port; + + for (port = 0; port < 8; port++) { + snd_mtpav_send_byte(chip, 0xf5); + snd_mtpav_send_byte(chip, port); + snd_mtpav_send_byte(chip, 0xfe); + } +} + +/* + */ + +static int snd_mtpav_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + //printk("mtpav port: %d opened\n", (int) substream->number); + spin_lock_irqsave(&mtp_card->spinlock, flags); + port->mode |= MTPAV_MODE_INPUT_OPENED; + port->input = substream; + if (mtp_card->share_irq++ == 0) + snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE)); // enable pport interrupts + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +} + +/* + */ + +static int snd_mtpav_input_close(snd_rawmidi_substream_t *substream) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + //printk("mtpav port: %d closed\n", (int) port); + + spin_lock_irqsave(&mtp_card->spinlock, flags); + + port->mode &= (~MTPAV_MODE_INPUT_OPENED); + port->input = NULL; + if (--mtp_card->share_irq == 0) + snd_mtpav_mputreg(mtp_card, CREG, 0); // disable pport interrupts + + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +} + +/* + */ + +static void snd_mtpav_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + if (up) + port->mode |= MTPAV_MODE_INPUT_TRIGGERED; + else + port->mode &= ~MTPAV_MODE_INPUT_TRIGGERED; + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + +} + + +/* + * timer interrupt for outputs + */ + +static void snd_mtpav_output_timer(unsigned long data) +{ + mtpav_t *chip = snd_magic_cast(mtpav_t, (void *)data, return); + int p; + + spin_lock(&chip->spinlock); + /* reprogram timer */ + chip->timer.expires = 1 + jiffies; + add_timer(&chip->timer); + /* process each port */ + for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) { + mtpav_port_t *port = &mtp_card->ports[p]; + if ((port->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && port->output) + snd_mtpav_output_port_write(port, port->output); + } + spin_unlock(&chip->spinlock); +} + +static void snd_mtpav_add_output_timer(mtpav_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->spinlock, flags); + chip->timer.function = snd_mtpav_output_timer; + chip->timer.data = (unsigned long) mtp_card; + chip->timer.expires = 1 + jiffies; + add_timer(&chip->timer); + spin_unlock_irqrestore(&chip->spinlock, flags); +} + +static void snd_mtpav_remove_output_timer(mtpav_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->spinlock, flags); + del_timer(&chip->timer); + spin_unlock_irqrestore(&chip->spinlock, flags); +} + +/* + */ + +static int snd_mtpav_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + port->mode |= MTPAV_MODE_OUTPUT_OPENED; + port->output = substream; + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +}; + +/* + */ + +static int snd_mtpav_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + port->mode &= (~MTPAV_MODE_OUTPUT_OPENED); + port->output = NULL; + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +}; + +/* + */ + +static void snd_mtpav_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + if (up) { + if (! (port->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) { + if (mtp_card->istimer++ == 0) + snd_mtpav_add_output_timer(mtp_card); + port->mode |= MTPAV_MODE_OUTPUT_TRIGGERED; + } + } else { + port->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED; + if (--mtp_card->istimer == 0) + snd_mtpav_remove_output_timer(mtp_card); + } + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + + if (up) + snd_mtpav_output_write(substream); +} + +/* + * midi interrupt for inputs + */ + +static void snd_mtpav_inmidi_process(mtpav_t *mcrd, u8 inbyte) +{ + mtpav_port_t *port; + + if (mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST) + return; + + port = &mcrd->ports[mcrd->inmidiport]; + if (port->mode & MTPAV_MODE_INPUT_TRIGGERED) + snd_rawmidi_receive(port->input, &inbyte, 1); +} + +static void snd_mtpav_inmidi_h(mtpav_t * mcrd, u8 inbyte) +{ + snd_assert(mcrd, return); + + if (inbyte >= 0xf8) { + /* real-time midi code */ + snd_mtpav_inmidi_process(mcrd, inbyte); + return; + } + + if (mcrd->inmidistate == 0) { // awaiting command + if (inbyte == 0xf5) // MTP port # + mcrd->inmidistate = 1; + else + snd_mtpav_inmidi_process(mcrd, inbyte); + } else if (mcrd->inmidistate) { + mcrd->inmidiport = translate_hwport_to_subdevice(mcrd, inbyte); + mcrd->inmidistate = 0; + } +} + +static void snd_mtpav_read_bytes(mtpav_t * mcrd) +{ + u8 clrread, setread; + u8 mtp_read_byte; + u8 sr, cbyt; + int i; + + u8 sbyt = snd_mtpav_getreg(mcrd, SREG); + + //printk("snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt); + + if (!(sbyt & SIGS_BYTE)) + return; + + cbyt = snd_mtpav_getreg(mcrd, CREG); + clrread = cbyt & (SIGC_READ ^ 0xff); + setread = cbyt | SIGC_READ; + + do { + + mtp_read_byte = 0; + for (i = 0; i < 4; i++) { + snd_mtpav_mputreg(mcrd, CREG, setread); + sr = snd_mtpav_getreg(mcrd, SREG); + snd_mtpav_mputreg(mcrd, CREG, clrread); + + sr &= SIGS_IN0 | SIGS_IN1; + sr >>= 4; + mtp_read_byte |= sr << (i * 2); + } + + snd_mtpav_inmidi_h(mcrd, mtp_read_byte); + + sbyt = snd_mtpav_getreg(mcrd, SREG); + + } while (sbyt & SIGS_BYTE); +} + +static void snd_mtpav_irqh(int irq, void *dev_id, struct pt_regs *regs) +{ + mtpav_t *mcard = snd_magic_cast(mtpav_t, dev_id, return); + + //printk("irqh()\n"); + spin_lock(&mcard->spinlock); + snd_mtpav_read_bytes(mcard); + spin_unlock(&mcard->spinlock); +} + +/* + * get ISA resources + */ +static int snd_mtpav_get_ISA(mtpav_t * mcard) +{ + if ((mcard->res_port = request_region(snd_port, 3, "MotuMTPAV MIDI")) == NULL) { + snd_printk("MTVAP port 0x%lx is busy\n", snd_port); + return -EBUSY; + } + mcard->port = snd_port; + if (request_irq(snd_irq, snd_mtpav_irqh, SA_INTERRUPT, "MOTU MTPAV", (void *)mcard)) { + snd_printk("MTVAP IRQ %d busy\n", snd_irq); + return -EBUSY; + } + mcard->irq = snd_irq; + return 0; +} + + +/* + */ + +static snd_rawmidi_ops_t snd_mtpav_output = { + open: snd_mtpav_output_open, + close: snd_mtpav_output_close, + trigger: snd_mtpav_output_trigger, +}; + +static snd_rawmidi_ops_t snd_mtpav_input = { + open: snd_mtpav_input_open, + close: snd_mtpav_input_close, + trigger: snd_mtpav_input_trigger, +}; + + +/* + * get RAWMIDI resources + */ + +static void snd_mtpav_set_name(mtpav_t *chip, snd_rawmidi_substream_t *substream) +{ + if (substream->number >= 0 && substream->number < chip->num_ports) + sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1); + else if (substream->number >= 8 && substream->number < chip->num_ports * 2) + sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1); + else if (substream->number == chip->num_ports * 2) + strcpy(substream->name, "MTP computer"); + else if (substream->number == chip->num_ports * 2 + 1) + strcpy(substream->name, "MTP ADAT"); + else + strcpy(substream->name, "MTP broadcast"); +} + +static int snd_mtpav_get_RAWMIDI(mtpav_t * mcard) +{ + int rval = 0; + snd_rawmidi_t *rawmidi; + snd_rawmidi_substream_t *substream; + struct list_head *list; + + //printk("entering snd_mtpav_get_RAWMIDI\n"); + + if (snd_hwports < 1) + mcard->num_ports = 1; + else if (snd_hwports > 8) + mcard->num_ports = 8; + else + mcard->num_ports = snd_hwports; + + if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0, + mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, + mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, + &mcard->rmidi)) < 0) + return rval; + rawmidi = mcard->rmidi; + + list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_mtpav_set_name(mcard, substream); + substream->ops = &snd_mtpav_input; + } + list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_mtpav_set_name(mcard, substream); + substream->ops = &snd_mtpav_output; + mcard->ports[substream->number].hwport = translate_subdevice_to_hwport(mcard, substream->number); + } + rawmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + sprintf(rawmidi->name, "MTP AV MIDI"); + //printk("exiting snd_mtpav_get_RAWMIDI() \n"); + return 0; +} + +/* + */ + +static mtpav_t *new_mtpav(void) +{ + mtpav_t *ncrd = (mtpav_t *) snd_kcalloc(sizeof(mtpav_t), GFP_KERNEL); + if (ncrd != NULL) { + spin_lock_init(&ncrd->spinlock); + + ncrd->card = NULL; + ncrd->irq = -1; + ncrd->share_irq = 0; + + ncrd->inmidiport = 0xffffffff; + ncrd->inmidistate = 0; + ncrd->outmidihwport = 0xffffffff; + } + return ncrd; +} + +/* + */ + +static void free_mtpav(mtpav_t * crd) +{ + unsigned long flags; + + spin_lock_irqsave(&crd->spinlock, flags); + if (crd->istimer > 0) + snd_mtpav_remove_output_timer(crd); + spin_unlock_irqrestore(&crd->spinlock, flags); + if (crd->irq >= 0) + free_irq(crd->irq, (void *)crd); + if (crd->res_port) { + release_resource(crd->res_port); + kfree_nocheck(crd->res_port); + } + if (crd != NULL) + kfree(crd); +} + +/* + */ + +static int __init alsa_card_mtpav_init(void) +{ + int err = 0; + char longname_buffer[80]; + + mtp_card = new_mtpav(); + if (mtp_card == NULL) + return -ENOMEM; + + mtp_card->card = snd_card_new(snd_index, snd_id, THIS_MODULE, 0); + if (mtp_card->card == NULL) { + free_mtpav(mtp_card); + return -ENOMEM; + } + + err = snd_mtpav_get_ISA(mtp_card); + //printk("snd_mtpav_get_ISA returned: %d\n", err); + if (err < 0) + goto __error; + + strcpy(mtp_card->card->driver, "MTPAV"); + strcpy(mtp_card->card->shortname, "MTPAV on parallel port"); + memset(longname_buffer, 0, sizeof(longname_buffer)); + sprintf(longname_buffer, "MTPAV on parallel port at"); + + err = snd_mtpav_get_RAWMIDI(mtp_card); + //snd_printk("snd_mtapv_get_RAWMIDI returned: %d\n", err); + if (err < 0) + goto __error; + + err = snd_card_register(mtp_card->card); // dont snd_card_register until AFTER all cards reources done! + + //printk("snd_card_register returned %d\n", err); + if (err < 0) + goto __error; + + + snd_mtpav_portscan(mtp_card); + + snd_printk("Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", snd_irq, snd_port); + + return 0; + + __error: + snd_card_free(mtp_card->card); + free_mtpav(mtp_card); + return err; +} + +/* + */ + +static void __exit alsa_card_mtpav_exit(void) +{ + if (mtp_card == NULL) + return; + if (mtp_card->card) + snd_card_free(mtp_card->card); + free_mtpav(mtp_card); +} + +/* + */ + +module_init(alsa_card_mtpav_init) +module_exit(alsa_card_mtpav_exit) + +#ifndef MODULE + +/* format is: snd-mtpav=snd_enable,snd_index,snd_id, + snd_port,snd_irq,snd_hwports */ + +static int __init alsa_card_mtpav_setup(char *str) +{ + int __attribute__ ((__unused__)) enable = 1; + + (void)(get_option(&str,&enable) == 2 && + get_option(&str,&snd_index) == 2 && + get_id(&str,&snd_id) == 2 && + get_option(&str,(int *)&snd_port) == 2 && + get_option(&str,&snd_irq) == 2 && + get_option(&str,&snd_hwports) == 2); + return 1; +} + +__setup("snd-mtpav=", alsa_card_mtpav_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/opl3/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,76 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _opl3.o + +list-multi := snd-opl3-lib.o snd-opl3-synth.o + +export-objs := opl3_lib.o + +snd-opl3-lib-objs := opl3_lib.o opl3_synth.o +snd-opl3-synth-objs := opl3_seq.o opl3_midi.o opl3_drums.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) +snd-opl3-synth-objs += opl3_oss.o +endif + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-opl3-lib.o +obj-$(CONFIG_SND_AZT2320) += snd-opl3-lib.o +obj-$(CONFIG_SND_DT0197H) += snd-opl3-lib.o +obj-$(CONFIG_SND_ES18XX) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPL3SA2) += snd-opl3-lib.o +obj-$(CONFIG_SND_AD1816A) += snd-opl3-lib.o +obj-$(CONFIG_SND_CS4232) += snd-opl3-lib.o +obj-$(CONFIG_SND_CS4236) += snd-opl3-lib.o +obj-$(CONFIG_SND_ES1688) += snd-opl3-lib.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPTI93X) += snd-opl3-lib.o +obj-$(CONFIG_SND_SB) += snd-opl3-lib.o +obj-$(CONFIG_SND_SB16) += snd-opl3-lib.o +obj-$(CONFIG_SND_SBAWE) += snd-opl3-lib.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-lib.o +obj-$(CONFIG_SND_ALS4000) += snd-opl3-lib.o +obj-$(CONFIG_SND_CMIPCI) += snd-opl3-lib.o +obj-$(CONFIG_SND_CS4281) += snd-opl3-lib.o +obj-$(CONFIG_SND_ES1938) += snd-opl3-lib.o +obj-$(CONFIG_SND_FM801) += snd-opl3-lib.o +obj-$(CONFIG_SND_SONICVIBES) += snd-opl3-lib.o +obj-$(CONFIG_SND_YMFPCI) += snd-opl3-lib.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_ALS100) += snd-opl3-synth.o + obj-$(CONFIG_SND_AZT2320) += snd-opl3-synth.o + obj-$(CONFIG_SND_DT0197H) += snd-opl3-synth.o + obj-$(CONFIG_SND_ES18XX) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPL3SA2) += snd-opl3-synth.o + obj-$(CONFIG_SND_AD1816A) += snd-opl3-synth.o + obj-$(CONFIG_SND_CS4232) += snd-opl3-synth.o + obj-$(CONFIG_SND_CS4236) += snd-opl3-synth.o + obj-$(CONFIG_SND_ES1688) += snd-opl3-synth.o + obj-$(CONFIG_SND_GUSEXTREME) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPTI93X) += snd-opl3-synth.o + obj-$(CONFIG_SND_SB) += snd-opl3-synth.o + obj-$(CONFIG_SND_SB16) += snd-opl3-synth.o + obj-$(CONFIG_SND_SBAWE) += snd-opl3-synth.o + obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-synth.o + obj-$(CONFIG_SND_ALS4000) += snd-opl3-synth.o + obj-$(CONFIG_SND_CMIPCI) += snd-opl3-synth.o + obj-$(CONFIG_SND_CS4281) += snd-opl3-synth.o + obj-$(CONFIG_SND_ES1938) += snd-opl3-synth.o + obj-$(CONFIG_SND_FM801) += snd-opl3-synth.o + obj-$(CONFIG_SND_SONICVIBES) += snd-opl3-synth.o + obj-$(CONFIG_SND_YMFPCI) += snd-opl3-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-opl3-lib.o: $(snd-opl3-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3-lib-objs) + +snd-opl3-synth.o: $(snd-opl3-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3-synth-objs) diff -Nru a/sound/drivers/opl3/opl3_drums.c b/sound/drivers/opl3/opl3_drums.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/opl3/opl3_drums.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,224 @@ +/* + * Copyright (c) by Uros Bizjak + * + * OPL2/OPL3/OPL4 FM routines for internal percussion channels + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include "opl3_voice.h" + +extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; + +static char snd_opl3_drum_table[47] = +{ + OPL3_BASSDRUM_ON, OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, /* 35 - 37 */ + OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON, OPL3_SNAREDRUM_ON, /* 38 - 40 */ + OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, OPL3_BASSDRUM_ON, /* 41 - 43 */ + OPL3_HIHAT_ON, OPL3_TOMTOM_ON, OPL3_HIHAT_ON, /* 44 - 46 */ + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_CYMBAL_ON, /* 47 - 49 */ + + OPL3_TOMTOM_ON, OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, /* 50 - 52 */ + OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, /* 53 - 55 */ + OPL3_HIHAT_ON, OPL3_CYMBAL_ON, OPL3_TOMTOM_ON, /* 56 - 58 */ + OPL3_CYMBAL_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 59 - 61 */ + OPL3_HIHAT_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 62 - 64 */ + + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 65 - 67 */ + OPL3_TOMTOM_ON, OPL3_HIHAT_ON, OPL3_HIHAT_ON, /* 68 - 70 */ + OPL3_HIHAT_ON, OPL3_HIHAT_ON, OPL3_TOMTOM_ON, /* 71 - 73 */ + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 74 - 76 */ + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 77 - 79 */ + OPL3_CYMBAL_ON, OPL3_CYMBAL_ON /* 80 - 81 */ +}; + +typedef struct snd_opl3_drum_voice { + int voice; + int op; + unsigned char am_vib; + unsigned char ksl_level; + unsigned char attack_decay; + unsigned char sustain_release; + unsigned char feedback_connection; + unsigned char wave_select; +} snd_opl3_drum_voice_t; + +typedef struct snd_opl3_drum_note { + int voice; + unsigned char fnum; + unsigned char octave_f; + unsigned char feedback_connection; +} snd_opl3_drum_note_t; + +static snd_opl3_drum_voice_t bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00}; +static snd_opl3_drum_voice_t bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00}; +static snd_opl3_drum_note_t bass_note = {6, 0x90, 0x09}; + +static snd_opl3_drum_voice_t hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00}; + +static snd_opl3_drum_voice_t snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02}; +static snd_opl3_drum_note_t snare_note = {7, 0xf4, 0x0d}; + +static snd_opl3_drum_voice_t tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00}; +static snd_opl3_drum_note_t tomtom_note = {8, 0xf4, 0x09}; + +static snd_opl3_drum_voice_t cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00}; + +/* + * set drum voice characteristics + */ +void snd_opl3_drum_voice_set(opl3_t *opl3, snd_opl3_drum_voice_t *data) +{ + unsigned char op_offset = snd_opl3_regmap[data->voice][data->op]; + unsigned char voice_offset = data->voice; + unsigned short opl3_reg; + + /* Set OPL3 AM_VIB register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_AM_VIB + op_offset); + opl3->command(opl3, opl3_reg, data->am_vib); + + /* Set OPL3 KSL_LEVEL register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, data->ksl_level); + + /* Set OPL3 ATTACK_DECAY register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_ATTACK_DECAY + op_offset); + opl3->command(opl3, opl3_reg, data->attack_decay); + + /* Set OPL3 SUSTAIN_RELEASE register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_SUSTAIN_RELEASE + op_offset); + opl3->command(opl3, opl3_reg, data->sustain_release); + + /* Set OPL3 FEEDBACK_CONNECTION register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, data->feedback_connection); + + /* Select waveform */ + opl3_reg = OPL3_LEFT | (OPL3_REG_WAVE_SELECT + op_offset); + opl3->command(opl3, opl3_reg, data->wave_select); +} + +/* + * Set drum voice pitch + */ +void snd_opl3_drum_note_set(opl3_t *opl3, snd_opl3_drum_note_t *data) +{ + unsigned char voice_offset = data->voice; + unsigned short opl3_reg; + + /* Set OPL3 FNUM_LOW register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, data->fnum); + + /* Set OPL3 KEYON_BLOCK register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, data->octave_f); +} + +/* + * Set drum voice volume and position + */ +void snd_opl3_drum_vol_set(opl3_t *opl3, snd_opl3_drum_voice_t *data, int vel, + snd_midi_channel_t *chan) +{ + unsigned char op_offset = snd_opl3_regmap[data->voice][data->op]; + unsigned char voice_offset = data->voice; + unsigned char reg_val; + unsigned short opl3_reg; + + /* Set OPL3 KSL_LEVEL register */ + reg_val = data->ksl_level; + snd_opl3_calc_volume(®_val, vel, chan); + opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 FEEDBACK_CONNECTION register */ + /* Set output voice connection */ + reg_val = data->feedback_connection | OPL3_STEREO_BITS; + if (chan->gm_pan < 43) + reg_val &= ~OPL3_VOICE_TO_RIGHT; + if (chan->gm_pan > 85) + reg_val &= ~OPL3_VOICE_TO_LEFT; + opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); +} + +/* + * Loads drum voices at init time + */ +void snd_opl3_load_drums(opl3_t *opl3) +{ + snd_opl3_drum_voice_set(opl3, &bass_op0); + snd_opl3_drum_voice_set(opl3, &bass_op1); + snd_opl3_drum_note_set(opl3, &bass_note); + + snd_opl3_drum_voice_set(opl3, &hihat); + + snd_opl3_drum_voice_set(opl3, &snare); + snd_opl3_drum_note_set(opl3, &snare_note); + + snd_opl3_drum_voice_set(opl3, &tomtom); + snd_opl3_drum_note_set(opl3, &tomtom_note); + + snd_opl3_drum_voice_set(opl3, &cymbal); +} + +/* + * Switch drum voice on or off + */ +void snd_opl3_drum_switch(opl3_t *opl3, int note, int vel, int on_off, + snd_midi_channel_t *chan) +{ + unsigned char drum_mask; + snd_opl3_drum_voice_t *drum_voice; + + if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE)) + return; + + if ((note < 35) || (note > 81)) + return; + drum_mask = snd_opl3_drum_table[note - 35]; + + if (on_off) { + switch (drum_mask) { + case OPL3_BASSDRUM_ON: + drum_voice = &bass_op1; + break; + case OPL3_HIHAT_ON: + drum_voice = &hihat; + break; + case OPL3_SNAREDRUM_ON: + drum_voice = &snare; + break; + case OPL3_TOMTOM_ON: + drum_voice = &tomtom; + break; + case OPL3_CYMBAL_ON: + drum_voice = &cymbal; + break; + default: + drum_voice = &tomtom; + } + + snd_opl3_drum_vol_set(opl3, drum_voice, vel, chan); + opl3->drum_reg |= drum_mask; + } else { + opl3->drum_reg &= ~drum_mask; + } + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, + opl3->drum_reg); +} diff -Nru a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/opl3/opl3_lib.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,559 @@ +/* + * Copyright (c) by Jaroslav Kysela , + * Hannu Savolainen 1993-1996, + * Rob Hooft + * + * Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips) + * + * Most if code is ported from OSS/Lite. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela , Hannu Savolainen 1993-1996, Rob Hooft"); +MODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)"); +MODULE_LICENSE("GPL"); + +#define chip_t opl3_t + +extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; + +void snd_opl2_command(opl3_t * opl3, unsigned short cmd, unsigned char val) +{ + unsigned long flags; + unsigned long port; + + /* + * The original 2-OP synth requires a quite long delay + * after writing to a register. + */ + + port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; + + spin_lock_irqsave(&opl3->reg_lock, flags); + + outb((unsigned char) cmd, port); + udelay(10); + + outb((unsigned char) val, port + 1); + udelay(30); + + spin_unlock_irqrestore(&opl3->reg_lock, flags); +} + +void snd_opl3_command(opl3_t * opl3, unsigned short cmd, unsigned char val) +{ + unsigned long flags; + unsigned long port; + + /* + * The OPL-3 survives with just two INBs + * after writing to a register. + */ + + port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; + + spin_lock_irqsave(&opl3->reg_lock, flags); + + outb((unsigned char) cmd, port); + inb(opl3->l_port); + inb(opl3->l_port); + + outb((unsigned char) val, port + 1); + inb(opl3->l_port); + inb(opl3->l_port); + + spin_unlock_irqrestore(&opl3->reg_lock, flags); +} + +void snd_opl3_cs4281_command(opl3_t * opl3, unsigned short cmd, unsigned char val) +{ + unsigned long flags; + unsigned long port; + + /* + * CS4281 requires a special access to I/O registers + */ + + port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; + + spin_lock_irqsave(&opl3->reg_lock, flags); + + writel((unsigned int)cmd, port << 2); + + writel((unsigned int)val, (port + 1) << 2); + + spin_unlock_irqrestore(&opl3->reg_lock, flags); +} + +static int snd_opl3_detect(opl3_t * opl3) +{ + /* + * This function returns 1 if the FM chip is present at the given I/O port + * The detection algorithm plays with the timer built in the FM chip and + * looks for a change in the status register. + * + * Note! The timers of the FM chip are not connected to AdLib (and compatible) + * boards. + * + * Note2! The chip is initialized if detected. + */ + + unsigned char stat1, stat2, signature; + + /* Reset timers 1 and 2 */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); + /* Reset the IRQ of the FM chip */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); + signature = stat1 = inb(opl3->l_port); /* Status register */ + if ((stat1 & 0xe0) != 0x00) { /* Should be 0x00 */ + snd_printd("OPL3: stat1 = 0x%x\n", stat1); + return -ENODEV; + } + /* Set timer1 to 0xff */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 0xff); + /* Unmask and start timer 1 */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER2_MASK | OPL3_TIMER1_START); + /* Now we have to delay at least 80us */ + udelay(200); + /* Read status after timers have expired */ + stat2 = inb(opl3->l_port); + /* Stop the timers */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); + /* Reset the IRQ of the FM chip */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); + if ((stat2 & 0xe0) != 0xc0) { /* There is no YM3812 */ + snd_printd("OPL3: stat2 = 0x%x\n", stat2); + return -ENODEV; + } + + /* If the toplevel code knows exactly the type of chip, don't try + to detect it. */ + if (opl3->hardware != OPL3_HW_AUTO) + return 0; + + /* There is a FM chip on this address. Detect the type (OPL2 to OPL4) */ + if (signature == 0x06) { /* OPL2 */ + opl3->hardware = OPL3_HW_OPL2; + } else { + /* + * Detect availability of OPL4 (_experimental_). Works probably + * only after a cold boot. In addition the OPL4 port + * of the chip may not be connected to the PC bus at all. + */ + snd_assert(opl3->r_port != 0, return -ENODEV); + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE); + if (inb(opl3->l_port) == 0x02) { /* Have a OPL4 */ + opl3->hardware = OPL3_HW_OPL4; + } else { + opl3->hardware = OPL3_HW_OPL3; + } + } + return 0; +} + +/* + * AdLib timers + */ + +/* + * Timer 1 - 80us + */ + +static int snd_opl3_timer1_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + ticks = timer->sticks; + tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks); /* timer 1 count */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +static int snd_opl3_timer1_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +/* + * Timer 2 - 320us + */ + +static int snd_opl3_timer2_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + ticks = timer->sticks; + tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks); /* timer 1 count */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +static int snd_opl3_timer2_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +/* + + */ + +static struct _snd_timer_hardware snd_opl3_timer1 = +{ + flags: SNDRV_TIMER_HW_STOP, + resolution: 80000, + ticks: 256, + start: snd_opl3_timer1_start, + stop: snd_opl3_timer1_stop, +}; + +static struct _snd_timer_hardware snd_opl3_timer2 = +{ + flags: SNDRV_TIMER_HW_STOP, + resolution: 320000, + ticks: 256, + start: snd_opl3_timer2_start, + stop: snd_opl3_timer2_stop, +}; + +static int snd_opl3_timer1_init(opl3_t * opl3, int timer_no) +{ + snd_timer_t *timer = NULL; + snd_timer_id_t tid; + int err; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = opl3->card->number; + tid.device = timer_no; + tid.subdevice = 0; + if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) { + strcpy(timer->name, "AdLib timer #1"); + timer->private_data = opl3; + timer->hw = snd_opl3_timer1; + } + opl3->timer1 = timer; + return err; +} + +static int snd_opl3_timer2_init(opl3_t * opl3, int timer_no) +{ + snd_timer_t *timer = NULL; + snd_timer_id_t tid; + int err; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = opl3->card->number; + tid.device = timer_no; + tid.subdevice = 0; + if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) { + strcpy(timer->name, "AdLib timer #2"); + timer->private_data = opl3; + timer->hw = snd_opl3_timer2; + } + opl3->timer2 = timer; + return err; +} + +/* + + */ + +void snd_opl3_interrupt(snd_hwdep_t * hw) +{ + unsigned char status; + opl3_t *opl3; + snd_timer_t *timer; + + if (hw == NULL) + return; + + opl3 = snd_magic_cast(opl3_t, hw->private_data, return); + status = inb(opl3->l_port); +#if 0 + snd_printk("AdLib IRQ status = 0x%x\n", status); +#endif + if (!(status & 0x80)) + return; + + if (status & 0x40) { + timer = opl3->timer1; + snd_timer_interrupt(timer, timer->sticks); + } + if (status & 0x20) { + timer = opl3->timer2; + snd_timer_interrupt(timer, timer->sticks); + } +} + +/* + + */ + +static int snd_opl3_free(opl3_t *opl3) +{ + if (opl3->res_l_port) { + release_resource(opl3->res_l_port); + kfree_nocheck(opl3->res_l_port); + } + if (opl3->res_r_port) { + release_resource(opl3->res_r_port); + kfree_nocheck(opl3->res_r_port); + } + snd_magic_kfree(opl3); + return 0; +} + +static int snd_opl3_dev_free(snd_device_t *device) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, device->device_data, return -ENXIO); + return snd_opl3_free(opl3); +} + +int snd_opl3_create(snd_card_t * card, + unsigned long l_port, + unsigned long r_port, + unsigned short hardware, + int integrated, + opl3_t ** ropl3) +{ + opl3_t *opl3; + int err; + static snd_device_ops_t ops = { + dev_free: snd_opl3_dev_free, + }; + + *ropl3 = NULL; + + opl3 = snd_magic_kcalloc(opl3_t, 0, GFP_KERNEL); + if (opl3 == NULL) + return -ENOMEM; + + if (integrated) + goto __step1; /* ports are already reserved */ + + if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) { + snd_opl3_free(opl3); + return -EBUSY; + } + if (r_port != 0 && + (opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) { + snd_opl3_free(opl3); + return -EBUSY; + } + + __step1: + + opl3->card = card; + opl3->hardware = hardware; + opl3->l_port = l_port; + opl3->r_port = r_port; + + spin_lock_init(&opl3->reg_lock); + spin_lock_init(&opl3->timer_lock); + init_MUTEX(&opl3->access_mutex); + + switch (opl3->hardware) { + /* some hardware doesn't support timers */ + case OPL3_HW_OPL3_SV: + case OPL3_HW_OPL3_CS: + case OPL3_HW_OPL3_FM801: + opl3->command = &snd_opl3_command; + break; + case OPL3_HW_OPL3_CS4281: + opl3->command = &snd_opl3_cs4281_command; + break; + default: + opl3->command = &snd_opl2_command; + if ((err = snd_opl3_detect(opl3)) < 0) { + snd_opl3_free(opl3); + snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n", + opl3->l_port, opl3->r_port); + return err; + } + /* detect routine returns correct hardware type */ + switch (opl3->hardware & OPL3_HW_MASK) { + case OPL3_HW_OPL3: + case OPL3_HW_OPL4: + opl3->command = &snd_opl3_command; + } + } + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); /* Melodic mode */ + + switch (opl3->hardware & OPL3_HW_MASK) { + case OPL3_HW_OPL2: + opl3->max_voices = MAX_OPL2_VOICES; + break; + case OPL3_HW_OPL3: + case OPL3_HW_OPL4: + opl3->max_voices = MAX_OPL3_VOICES; + snd_assert(opl3->r_port != 0, snd_opl3_free(opl3); return -ENODEV); + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); /* Enter OPL2 mode */ + } + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, opl3, &ops)) < 0) { + snd_opl3_free(opl3); + return err; + } + + *ropl3 = opl3; + return 0; +} + +int snd_opl3_timer_new(opl3_t * opl3, int timer1_dev, int timer2_dev) +{ + int err; + + if (timer1_dev >= 0) + if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0) + return err; + if (timer2_dev >= 0) { + if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) { + snd_device_free(opl3->card, opl3->timer1); + opl3->timer1 = NULL; + return err; + } + } + return 0; +} + +int snd_opl3_hwdep_new(opl3_t * opl3, + int device, int seq_device, + snd_hwdep_t ** rhwdep) +{ + snd_hwdep_t *hw; + snd_card_t *card = opl3->card; + int err; + + if (rhwdep) + *rhwdep = NULL; + + /* create hardware dependent device (direct FM) */ + + if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) { + snd_device_free(card, opl3); + return err; + } + hw->private_data = opl3; +#ifdef CONFIG_SND_OSSEMUL + if (device == 0) { + hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM; + sprintf(hw->oss_dev, "dmfm%i", card->number); + } +#endif + strcpy(hw->name, hw->id); + switch (opl3->hardware & OPL3_HW_MASK) { + case OPL3_HW_OPL2: + strcpy(hw->name, "OPL2 FM"); + hw->iface = SNDRV_HWDEP_IFACE_OPL2; + break; + case OPL3_HW_OPL3: + strcpy(hw->name, "OPL3 FM"); + hw->iface = SNDRV_HWDEP_IFACE_OPL3; + break; + case OPL3_HW_OPL4: + strcpy(hw->name, "OPL4 FM"); + hw->iface = SNDRV_HWDEP_IFACE_OPL4; + break; + } + + /* operators - only ioctl */ + hw->ops.open = snd_opl3_open; + hw->ops.ioctl = snd_opl3_ioctl; + hw->ops.release = snd_opl3_release; + + opl3->seq_dev_num = seq_device; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, + sizeof(opl3_t*), &opl3->seq_dev) >= 0) { + strcpy(opl3->seq_dev->name, hw->name); + *(opl3_t**)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3; + } +#endif + if (rhwdep) + *rhwdep = hw; + return 0; +} + +EXPORT_SYMBOL(snd_opl3_interrupt); +EXPORT_SYMBOL(snd_opl3_create); +EXPORT_SYMBOL(snd_opl3_timer_new); +EXPORT_SYMBOL(snd_opl3_hwdep_new); + +/* opl3_synth.c */ +EXPORT_SYMBOL(snd_opl3_regmap); +EXPORT_SYMBOL(snd_opl3_reset); + +/* + * INIT part + */ + +static int __init alsa_opl3_init(void) +{ + return 0; +} + +static void __exit alsa_opl3_exit(void) +{ +} + +module_init(alsa_opl3_init) +module_exit(alsa_opl3_exit) diff -Nru a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/opl3/opl3_midi.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,874 @@ +/* + * Copyright (c) by Uros Bizjak + * + * Midi synth routines for OPL2/OPL3/OPL4 FM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#undef DEBUG_ALLOC +#undef DEBUG_MIDI + +#define __NO_VERSION__ +#include "opl3_voice.h" +#include + +extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; + +extern int use_internal_drums; + +/* + * The next table looks magical, but it certainly is not. Its values have + * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception + * for i=0. This log-table converts a linear volume-scaling (0..127) to a + * logarithmic scaling as present in the FM-synthesizer chips. so : Volume + * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative + * volume -8 it was implemented as a table because it is only 128 bytes and + * it saves a lot of log() calculations. (Rob Hooft ) + */ + +static char opl3_volume_table[128] = +{ + -63, -48, -40, -35, -32, -29, -27, -26, + -24, -23, -21, -20, -19, -18, -18, -17, + -16, -15, -15, -14, -13, -13, -12, -12, + -11, -11, -10, -10, -10, -9, -9, -8, + -8, -8, -7, -7, -7, -6, -6, -6, + -5, -5, -5, -5, -4, -4, -4, -4, + -3, -3, -3, -3, -2, -2, -2, -2, + -2, -1, -1, -1, -1, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8 +}; + +void snd_opl3_calc_volume(unsigned char *volbyte, int vel, + snd_midi_channel_t *chan) +{ + int oldvol, newvol, n; + int volume; + + volume = (vel * chan->gm_volume * chan->gm_expression) / (127*127); + if (volume > 127) + volume = 127; + + oldvol = OPL3_TOTAL_LEVEL_MASK - (*volbyte & OPL3_TOTAL_LEVEL_MASK); + + newvol = opl3_volume_table[volume] + oldvol; + if (newvol > OPL3_TOTAL_LEVEL_MASK) + newvol = OPL3_TOTAL_LEVEL_MASK; + else if (newvol < 0) + newvol = 0; + + n = OPL3_TOTAL_LEVEL_MASK - (newvol & OPL3_TOTAL_LEVEL_MASK); + + *volbyte = (*volbyte & OPL3_KSL_MASK) | (n & OPL3_TOTAL_LEVEL_MASK); +} + +/* + * Converts the note frequency to block and fnum values for the FM chip + */ +static short opl3_note_table[16] = +{ + 305, 323, /* for pitch bending, -2 semitones */ + 343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647, + 686, 726 /* for pitch bending, +2 semitones */ +}; + +static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum, + int note, snd_midi_channel_t *chan) +{ + int block = ((note / 12) & 0x07) - 1; + int idx = (note % 12) + 2; + int freq; + + if (chan->midi_pitchbend) { + int pitchbend = chan->midi_pitchbend; + int segment; + + if (pitchbend > 0x1FFF) + pitchbend = 0x1FFF; + + segment = pitchbend / 0x1000; + freq = opl3_note_table[idx+segment]; + freq += ((opl3_note_table[idx+segment+1] - freq) * + (pitchbend % 0x1000)) / 0x1000; + } else { + freq = opl3_note_table[idx]; + } + + *fnum = (unsigned char) freq; + *blocknum = ((freq >> 8) & OPL3_FNUM_HIGH_MASK) | + ((block << 2) & OPL3_BLOCKNUM_MASK); +} + + +#ifdef DEBUG_ALLOC +static void debug_alloc(opl3_t *opl3, char *s, int voice) { + int i; + char *str = "x.24"; + + printk("time %.5i: %s [%.2i]: ", opl3->use_time, s, voice); + for (i = 0; i < opl3->max_voices; i++) + printk("%c", *(str + opl3->voices[i].state + 1)); + printk("\n"); +} +#endif + +/* + * Get a FM voice (channel) to play a note on. + */ +static int opl3_get_voice(opl3_t *opl3, int instr_4op, + snd_midi_channel_t *chan) { + int chan_4op_1; /* first voice for 4op instrument */ + int chan_4op_2; /* second voice for 4op instrument */ + + snd_opl3_voice_t *vp, *vp2; + unsigned int voice_time; + int i; + +#ifdef DEBUG_ALLOC + char *alloc_type[3] = { "FREE ", "CHEAP ", "EXPENSIVE" }; +#endif + + /* This is our "allocation cost" table */ + enum { + FREE = 0, CHEAP, EXPENSIVE, END + }; + + /* Keeps track of what we are finding */ + struct best { + unsigned int time; + int voice; + } best[END]; + struct best *bp; + + for (i = 0; i < END; i++) { + best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */; + best[i].voice = -1; + } + + /* Look through all the channels for the most suitable. */ + for (i = 0; i < opl3->max_voices; i++) { + vp = &opl3->voices[i]; + + if (vp->state == SNDRV_OPL3_ST_NOT_AVAIL) + /* skip unavailable channels, allocated by + drum voices or by bounded 4op voices) */ + continue; + + voice_time = vp->time; + bp = best; + + chan_4op_1 = ((i < 3) || (i > 8 && i < 12)); + chan_4op_2 = ((i > 2 && i < 6) || (i > 11 && i < 15)); + if (instr_4op) { + /* allocate 4op voice */ + /* skip channels unavailable to 4op instrument */ + if (!chan_4op_1) + continue; + + if (vp->state) + /* kill one voice, CHEAP */ + bp++; + /* get state of bounded 2op channel + to be allocated for 4op instrument */ + vp2 = &opl3->voices[i + 3]; + if (vp2->state == SNDRV_OPL3_ST_ON_2OP) { + /* kill two voices, EXPENSIVE */ + bp++; + voice_time = (voice_time > vp->time) ? + voice_time : vp->time; + } + } else { + /* allocate 2op voice */ + if ((chan_4op_1) || (chan_4op_2)) + /* use bounded channels for 2op, CHEAP */ + bp++; + else if (vp->state) + /* kill one voice on 2op channel, CHEAP */ + bp++; + /* raise kill cost to EXPENSIVE for all channels */ + if (vp->state) + bp++; + } + if (voice_time < bp->time) { + bp->time = voice_time; + bp->voice = i; + } + } + + for (i = 0; i < END; i++) { + if (best[i].voice >= 0) { +#ifdef DEBUG_ALLOC + printk("%s %iop allocation on voice %i\n", + alloc_type[i], instr_4op ? 4 : 2, + best[i].voice); +#endif + return best[i].voice; + } + } + /* not found */ + return -1; +} + +/* ------------------------------ */ + +/* + * System timer interrupt function + */ +void snd_opl3_timer_func(unsigned long data) +{ + + opl3_t *opl3 = (opl3_t *)data; + int again = 0; + int i; + + spin_lock(&opl3->sys_timer_lock); + for (i = 0; i < opl3->max_voices; i++) { + snd_opl3_voice_t *vp = &opl3->voices[i]; + if (vp->state > 0 && vp->note_off_check) { + if (vp->note_off == jiffies) + snd_opl3_note_off(opl3, vp->note, 0, vp->chan); + else + again++; + } + } + if (again) { + opl3->tlist.expires = jiffies + 1; /* invoke again */ + add_timer(&opl3->tlist); + } else { + opl3->sys_timer_status = 0; + } + spin_unlock(&opl3->sys_timer_lock); +} + +/* + * Start system timer + */ +void snd_opl3_start_timer(opl3_t *opl3) +{ + unsigned long flags; + spin_lock_irqsave(&opl3->sys_timer_lock, flags); + if (! opl3->sys_timer_status) { + opl3->tlist.expires = jiffies + 1; + add_timer(&opl3->tlist); + opl3->sys_timer_status = 1; + } + spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); +} + +/* ------------------------------ */ + + +static int snd_opl3_oss_map[MAX_OPL3_VOICES] = { + 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14 +}; + +/* + * Start a note. + */ +void snd_opl3_note_on(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + snd_seq_instr_t wanted; + snd_seq_kinstr_t *kinstr; + int instr_4op; + + int voice; + snd_opl3_voice_t *vp, *vp2; + unsigned short connect_mask; + unsigned char connection; + unsigned char vol_op[4]; + + int extra_prg = 0; + + unsigned short reg_side; + unsigned char op_offset; + unsigned char voice_offset; + unsigned short opl3_reg; + unsigned char reg_val; + + int key = note; + unsigned char fnum, blocknum; + int i; + + fm_instrument_t *fm; + unsigned long flags; + + opl3 = snd_magic_cast(opl3_t, p, return); + +#ifdef DEBUG_MIDI + snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n", + chan->number, chan->midi_program, note, vel); +#endif + wanted.cluster = 0; + wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; + + /* in SYNTH mode, application takes care of voices */ + /* in SEQ mode, drum voice numbers are notes on drum channel */ + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + if (chan->drum_channel) { + /* percussion instruments are located in bank 128 */ + wanted.bank = 128; + wanted.prg = note; + } else { + wanted.bank = chan->gm_bank_select; + wanted.prg = chan->midi_program; + } + } else { + /* Prepare for OSS mode */ + if (chan->number >= MAX_OPL3_VOICES) + return; + + /* OSS instruments are located in bank 127 */ + wanted.bank = 127; + wanted.prg = chan->midi_program; + } + + spin_lock_irqsave(&opl3->voice_lock, flags); + + if (use_internal_drums) { + snd_opl3_drum_switch(opl3, note, vel, 1, chan); + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + + __extra_prg: + kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0); + if (kinstr == NULL) { + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + + fm = KINSTR_DATA(kinstr); + + switch (fm->type) { + case FM_PATCH_OPL2: + instr_4op = 0; + break; + case FM_PATCH_OPL3: + if (opl3->hardware >= OPL3_HW_OPL3) { + instr_4op = 1; + break; + } + default: + snd_seq_instr_free_use(opl3->ilist, kinstr); + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + +#ifdef DEBUG_MIDI + snd_printk(" --> OPL%i instrument: %s\n", + instr_4op ? 3 : 2, kinstr->name); +#endif + /* in SYNTH mode, application takes care of voices */ + /* in SEQ mode, allocate voice on free OPL3 channel */ + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + voice = opl3_get_voice(opl3, instr_4op, chan); + } else { + /* remap OSS voice */ + voice = snd_opl3_oss_map[chan->number]; + } + + if (voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice; + connect_mask = (OPL3_LEFT_4OP_0 << voice_offset) & 0x07; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice - MAX_OPL2_VOICES; + connect_mask = (OPL3_RIGHT_4OP_0 << voice_offset) & 0x38; + } + + /* kill voice on channel */ + vp = &opl3->voices[voice]; + if (vp->state > 0) { + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT; + opl3->command(opl3, opl3_reg, reg_val); + } + if (instr_4op) { + vp2 = &opl3->voices[voice + 3]; + if (vp->state > 0) { + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + + voice_offset + 3); + reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT; + opl3->command(opl3, opl3_reg, reg_val); + } + } + + /* set connection register */ + if (instr_4op) { + if ((opl3->connection_reg ^ connect_mask) & connect_mask) { + opl3->connection_reg |= connect_mask; + /* set connection bit */ + opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT; + opl3->command(opl3, opl3_reg, opl3->connection_reg); + } + } else { + if ((opl3->connection_reg ^ ~connect_mask) & connect_mask) { + opl3->connection_reg &= ~connect_mask; + /* clear connection bit */ + opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT; + opl3->command(opl3, opl3_reg, opl3->connection_reg); + } + } + +#ifdef DEBUG_MIDI + snd_printk(" --> setting OPL3 connection: 0x%x\n", + opl3->connection_reg); +#endif + /* + * calculate volume depending on connection + * between FM operators (see include/opl3.h) + */ + for (i = 0; i < (instr_4op ? 4 : 2); i++) + vol_op[i] = fm->op[i].ksl_level; + + connection = fm->feedback_connection[0] & 0x01; + if (instr_4op) { + connection <<= 1; + connection |= fm->feedback_connection[1] & 0x01; + + snd_opl3_calc_volume(&vol_op[3], vel, chan); + switch (connection) { + case 0x03: + snd_opl3_calc_volume(&vol_op[2], vel, chan); + /* fallthru */ + case 0x02: + snd_opl3_calc_volume(&vol_op[0], vel, chan); + break; + case 0x01: + snd_opl3_calc_volume(&vol_op[1], vel, chan); + } + } else { + snd_opl3_calc_volume(&vol_op[1], vel, chan); + if (connection) + snd_opl3_calc_volume(&vol_op[0], vel, chan); + } + + /* Program the FM voice characteristics */ + for (i = 0; i < (instr_4op ? 4 : 2); i++) { +#ifdef DEBUG_MIDI + snd_printk(" --> programming operator %i\n", i); +#endif + op_offset = snd_opl3_regmap[voice_offset][i]; + + /* Set OPL3 AM_VIB register of requested voice/operator */ + reg_val = fm->op[i].am_vib; + opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 KSL_LEVEL register of requested voice/operator */ + reg_val = vol_op[i]; + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ + reg_val = fm->op[i].attack_decay; + opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ + reg_val = fm->op[i].sustain_release; + opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Select waveform */ + reg_val = fm->op[i].wave_select; + opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + } + + /* Set operator feedback and 2op inter-operator connection */ + reg_val = fm->feedback_connection[0]; + /* Set output voice connection */ + reg_val |= OPL3_STEREO_BITS; + if (chan->gm_pan < 43) + reg_val &= ~OPL3_VOICE_TO_RIGHT; + if (chan->gm_pan > 85) + reg_val &= ~OPL3_VOICE_TO_LEFT; + opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + if (instr_4op) { + /* Set 4op inter-operator connection */ + reg_val = fm->feedback_connection[1] & OPL3_CONNECTION_BIT; + /* Set output voice connection */ + reg_val |= OPL3_STEREO_BITS; + if (chan->gm_pan < 43) + reg_val &= ~OPL3_VOICE_TO_RIGHT; + if (chan->gm_pan > 85) + reg_val &= ~OPL3_VOICE_TO_LEFT; + opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + + voice_offset + 3); + opl3->command(opl3, opl3_reg, reg_val); + } + + /* + * Special treatment of percussion notes for fm: + * Requested pitch is really program, and pitch for + * device is whatever was specified in the patch library. + */ + if (fm->fix_key) + note = fm->fix_key; + /* + * use transpose if defined in patch library + */ + if (fm->trnsps) + note += (fm->trnsps - 64); + + snd_opl3_calc_pitch(&fnum, &blocknum, note, chan); + + /* Set OPL3 FNUM_LOW register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, fnum); + + opl3->voices[voice].keyon_reg = blocknum; + + /* Set output sound flag */ + blocknum |= OPL3_KEYON_BIT; + +#ifdef DEBUG_MIDI + snd_printk(" --> trigger voice %i\n", voice); +#endif + /* Set OPL3 KEYON_BLOCK register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, blocknum); + + /* kill note after fixed duration (in centiseconds) */ + if (fm->fix_dur) { + opl3->voices[voice].note_off = jiffies + + (fm->fix_dur * HZ) / 100; + snd_opl3_start_timer(opl3); + opl3->voices[voice].note_off_check = 1; + } else + opl3->voices[voice].note_off_check = 0; + + /* get extra pgm, but avoid possible loops */ + extra_prg = (extra_prg) ? 0 : fm->modes; + + snd_seq_instr_free_use(opl3->ilist, kinstr); + + /* do the bookkeeping */ + vp->time = opl3->use_time++; + vp->note = key; + vp->chan = chan; + + if (instr_4op) { + vp->state = SNDRV_OPL3_ST_ON_4OP; + + vp2 = &opl3->voices[voice + 3]; + vp2->time = opl3->use_time++; + vp2->note = key; + vp2->chan = chan; + vp2->state = SNDRV_OPL3_ST_NOT_AVAIL; + } else { + if (vp->state == SNDRV_OPL3_ST_ON_4OP) { + /* 4op killed by 2op, release bounded voice */ + vp2 = &opl3->voices[voice + 3]; + vp2->time = opl3->use_time++; + vp2->state = SNDRV_OPL3_ST_OFF; + } + vp->state = SNDRV_OPL3_ST_ON_2OP; + } + +#ifdef DEBUG_ALLOC + debug_alloc(opl3, "note on ", voice); +#endif + + /* allocate extra program if specified in patch library */ + if (extra_prg) { + if (extra_prg > 128) { + wanted.bank = 128; + /* percussions start at 35 */ + wanted.prg = extra_prg - 128 + 35 - 1; + } else { + wanted.bank = 0; + wanted.prg = extra_prg - 1; + } +#ifdef DEBUG_MIDI + snd_printk(" *** allocating extra program\n"); +#endif + goto __extra_prg; + } + spin_unlock_irqrestore(&opl3->voice_lock, flags); +} + +static void snd_opl3_kill_voice(opl3_t *opl3, int voice) +{ + unsigned short reg_side; + unsigned char voice_offset; + unsigned short opl3_reg; + + snd_opl3_voice_t *vp, *vp2; + + snd_assert(voice < MAX_OPL3_VOICES, return); + + vp = &opl3->voices[voice]; + if (voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice - MAX_OPL2_VOICES; + } + + /* kill voice */ +#ifdef DEBUG_MIDI + snd_printk(" --> kill voice %i\n", voice); +#endif + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + /* clear Key ON bit */ + opl3->command(opl3, opl3_reg, vp->keyon_reg); + + /* do the bookkeeping */ + vp->time = opl3->use_time++; + + if (vp->state == SNDRV_OPL3_ST_ON_4OP) { + vp2 = &opl3->voices[voice + 3]; + + vp2->time = opl3->use_time++; + vp2->state = SNDRV_OPL3_ST_OFF; + } + vp->state = SNDRV_OPL3_ST_OFF; +#ifdef DEBUG_ALLOC + debug_alloc(opl3, "note off", voice); +#endif + +} + +/* + * Release a note in response to a midi note off. + */ +void snd_opl3_note_off(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + int voice; + snd_opl3_voice_t *vp; + + unsigned long flags; + + opl3 = snd_magic_cast(opl3_t, p, return); + +#ifdef DEBUG_MIDI + snd_printk("Note off, ch %i, inst %i, note %i\n", + chan->number, chan->midi_program, note); +#endif + + spin_lock_irqsave(&opl3->voice_lock, flags); + + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + if (chan->drum_channel && use_internal_drums) { + snd_opl3_drum_switch(opl3, note, vel, 0, chan); + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + /* this loop will hopefully kill all extra voices, because + they are grouped by the same channel and note values */ + for (voice = 0; voice < opl3->max_voices; voice++) { + vp = &opl3->voices[voice]; + if (vp->state > 0 && vp->chan == chan && vp->note == note) { + snd_opl3_kill_voice(opl3, voice); + } + } + } else { + /* remap OSS voices */ + if (chan->number < MAX_OPL3_VOICES) { + voice = snd_opl3_oss_map[chan->number]; + snd_opl3_kill_voice(opl3, voice); + } + } + spin_unlock_irqrestore(&opl3->voice_lock, flags); +} + +/* + * key pressure change + */ +void snd_opl3_key_press(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("Key pressure, ch#: %i, inst#: %i\n", + chan->number, chan->midi_program); +#endif +} + +/* + * terminate note + */ +void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("Terminate note, ch#: %i, inst#: %i\n", + chan->number, chan->midi_program); +#endif +} + +static void snd_opl3_update_pitch(opl3_t *opl3, int voice) +{ + unsigned short reg_side; + unsigned char voice_offset; + unsigned short opl3_reg; + + unsigned char fnum, blocknum; + + snd_opl3_voice_t *vp; + + snd_assert(voice < MAX_OPL3_VOICES, return); + + vp = &opl3->voices[voice]; + if (vp->chan == NULL) + return; /* not allocated? */ + + if (voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice - MAX_OPL2_VOICES; + } + + snd_opl3_calc_pitch(&fnum, &blocknum, vp->note, vp->chan); + + /* Set OPL3 FNUM_LOW register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, fnum); + + vp->keyon_reg = blocknum; + + /* Set output sound flag */ + blocknum |= OPL3_KEYON_BIT; + + /* Set OPL3 KEYON_BLOCK register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, blocknum); + + vp->time = opl3->use_time++; +} + +/* + * Update voice pitch controller + */ +static void snd_opl3_pitch_ctrl(opl3_t *opl3, snd_midi_channel_t *chan) +{ + int voice; + snd_opl3_voice_t *vp; + + unsigned long flags; + + spin_lock_irqsave(&opl3->voice_lock, flags); + + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + for (voice = 0; voice < opl3->max_voices; voice++) { + vp = &opl3->voices[voice]; + if (vp->state > 0 && vp->chan == chan) { + snd_opl3_update_pitch(opl3, voice); + } + } + } else { + /* remap OSS voices */ + if (chan->number < MAX_OPL3_VOICES) { + voice = snd_opl3_oss_map[chan->number]; + snd_opl3_update_pitch(opl3, voice); + } + } + spin_unlock_irqrestore(&opl3->voice_lock, flags); +} + +/* + * Deal with a controler type event. This includes all types of + * control events, not just the midi controllers + */ +void snd_opl3_control(void *p, int type, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("Controller, TYPE = %i, ch#: %i, inst#: %i\n", + type, chan->number, chan->midi_program); +#endif + + switch (type) { + case MIDI_CTL_MSB_MODWHEEL: + if (chan->control[MIDI_CTL_MSB_MODWHEEL] > 63) + opl3->drum_reg |= OPL3_VIBRATO_DEPTH; + else + opl3->drum_reg &= ~OPL3_VIBRATO_DEPTH; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, + opl3->drum_reg); + break; + case MIDI_CTL_E2_TREMOLO_DEPTH: + if (chan->control[MIDI_CTL_E2_TREMOLO_DEPTH] > 63) + opl3->drum_reg |= OPL3_TREMOLO_DEPTH; + else + opl3->drum_reg &= ~OPL3_TREMOLO_DEPTH; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, + opl3->drum_reg); + break; + case MIDI_CTL_PITCHBEND: + snd_opl3_pitch_ctrl(opl3, chan); + break; + } +} + +/* + * NRPN events + */ +void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, + snd_midi_channel_set_t *chset) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("NRPN, ch#: %i, inst#: %i\n", + chan->number, chan->midi_program); +#endif +} + +/* + * receive sysex + */ +void snd_opl3_sysex(void *p, unsigned char *buf, int len, + int parsed, snd_midi_channel_set_t *chset) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("SYSEX\n"); +#endif +} diff -Nru a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/opl3/opl3_oss.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,357 @@ +/* + * Interface for OSS sequencer emulation + * + * Copyright (C) 2000 Uros Bizjak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "opl3_voice.h" +#include + +static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure); +static int snd_opl3_close_seq_oss(snd_seq_oss_arg_t *arg); +static int snd_opl3_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg); +static int snd_opl3_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char *buf, int offs, int count); +static int snd_opl3_reset_seq_oss(snd_seq_oss_arg_t *arg); + +/* */ + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +/* operators */ + +extern snd_midi_op_t opl3_ops; + +static snd_seq_oss_callback_t oss_callback = { + owner: THIS_MODULE, + open: snd_opl3_open_seq_oss, + close: snd_opl3_close_seq_oss, + ioctl: snd_opl3_ioctl_seq_oss, + load_patch: snd_opl3_load_patch_seq_oss, + reset: snd_opl3_reset_seq_oss, +}; + +static int snd_opl3_oss_event_input(snd_seq_event_t *ev, int direct, + void *private_data, int atomic, int hop) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -EINVAL); + + if (ev->type != SNDRV_SEQ_EVENT_OSS) + snd_midi_process_event(&opl3_ops, ev, opl3->oss_chset); + return 0; +} + +/* ------------------------------ */ + +static void snd_opl3_oss_free_port(void *private_data) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return); + + snd_midi_channel_free_set(opl3->oss_chset); +} + +static int snd_opl3_oss_create_port(opl3_t * opl3) +{ + snd_seq_port_callback_t callbacks; + char name[32]; + int voices, opl_ver; + + voices = (opl3->hardware < OPL3_HW_OPL3) ? + MAX_OPL2_VOICES : MAX_OPL3_VOICES; + opl3->oss_chset = snd_midi_channel_alloc_set(voices); + if (opl3->oss_chset == NULL) + return -ENOMEM; + opl3->oss_chset->private_data = opl3; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.event_input = snd_opl3_oss_event_input; + callbacks.private_free = snd_opl3_oss_free_port; + callbacks.private_data = opl3; + + opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; + sprintf(name, "OPL%i OSS Port", opl_ver); + + opl3->oss_chset->client = opl3->seq_client; + opl3->oss_chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_SYNTH, + name); + if (opl3->oss_chset->port < 0) { + snd_midi_channel_free_set(opl3->oss_chset); + return opl3->oss_chset->port; + } + return 0; +} + +/* ------------------------------ */ + +/* register OSS synth */ +void snd_opl3_init_seq_oss(opl3_t *opl3, char *name) +{ + snd_seq_oss_reg_t *arg; + snd_seq_device_t *dev; + + if (snd_seq_device_new(opl3->card, 0, SNDRV_SEQ_DEV_ID_OSS, + sizeof(snd_seq_oss_reg_t), &dev) < 0) + return; + + opl3->oss_seq_dev = dev; + strncpy(dev->name, name, sizeof(dev->name) - 1); + dev->name[sizeof(dev->name) - 1] = 0; + arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + arg->type = SYNTH_TYPE_FM; + if (opl3->hardware < OPL3_HW_OPL3) { + arg->subtype = FM_TYPE_ADLIB; + arg->nvoices = MAX_OPL2_VOICES; + } else { + arg->subtype = FM_TYPE_OPL3; + arg->nvoices = MAX_OPL3_VOICES; + } + arg->oper = oss_callback; + arg->private_data = opl3; + + snd_opl3_oss_create_port(opl3); + + /* register to OSS synth table */ + snd_device_register(opl3->card, dev); +} + +/* unregister */ +void snd_opl3_free_seq_oss(opl3_t *opl3) +{ + if (opl3->oss_seq_dev) { + snd_device_free(opl3->card, opl3->oss_seq_dev); + opl3->oss_seq_dev = NULL; + } +} + +/* ------------------------------ */ + +/* open OSS sequencer */ +static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, closure, return -EINVAL); + int err; + + snd_assert(arg != NULL, return -ENXIO); + + if ((err = snd_opl3_synth_setup(opl3)) < 0) + return err; + + /* fill the argument data */ + arg->private_data = opl3; + arg->addr.client = opl3->oss_chset->client; + arg->addr.port = opl3->oss_chset->port; + + if ((err = snd_opl3_synth_use_inc(opl3)) < 0) + return err; + + opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH; + return 0; +} + +/* close OSS sequencer */ +static int snd_opl3_close_seq_oss(snd_seq_oss_arg_t *arg) +{ + opl3_t *opl3; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + + snd_opl3_synth_cleanup(opl3); + + snd_opl3_synth_use_dec(opl3); + return 0; +} + +/* load patch */ + +/* offsets for SBI params */ +#define AM_VIB 0 +#define KSL_LEVEL 2 +#define ATTACK_DECAY 4 +#define SUSTAIN_RELEASE 6 +#define WAVE_SELECT 8 + +/* offset for SBI instrument */ +#define CONNECTION 10 +#define OFFSET_4OP 11 + +/* from sound_config.h */ +#define SBFM_MAXINSTR 256 + +static int snd_opl3_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, + const char *buf, int offs, int count) +{ + opl3_t *opl3; + int err = -EINVAL; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + + if ((format == FM_PATCH) || (format == OPL3_PATCH)) { + struct sbi_instrument sbi; + + size_t size; + snd_seq_instr_header_t *put; + snd_seq_instr_data_t *data; + fm_xinstrument_t *xinstr; + + snd_seq_event_t ev; + int i; + + mm_segment_t fs; + + if (count < sizeof(sbi)) { + snd_printk("FM Error: Patch record too short\n"); + return -EINVAL; + } + if (copy_from_user(&sbi, buf, sizeof(sbi))) + return -EFAULT; + + if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { + snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel); + return -EINVAL; + } + + size = sizeof(*put) + sizeof(fm_xinstrument_t); + put = (snd_seq_instr_header_t *)snd_kcalloc(size, GFP_KERNEL); + if (put == NULL) + return -ENOMEM; + /* build header */ + data = &put->data; + data->type = SNDRV_SEQ_INSTR_ATYPE_DATA; + strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3); + /* build data section */ + xinstr = (fm_xinstrument_t *)(data + 1); + xinstr->stype = FM_STRU_INSTR; + + for (i = 0; i < 2; i++) { + xinstr->op[i].am_vib = sbi.operators[AM_VIB + i]; + xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i]; + xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i]; + xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i]; + xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i]; + } + xinstr->feedback_connection[0] = sbi.operators[CONNECTION]; + + if (format == OPL3_PATCH) { + xinstr->type = FM_PATCH_OPL3; + for (i = 0; i < 2; i++) { + xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i]; + xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i]; + xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i]; + xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i]; + xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i]; + } + xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION]; + } else { + xinstr->type = FM_PATCH_OPL2; + } + + put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; + put->id.instr.bank = 127; + put->id.instr.prg = sbi.channel; + put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE; + + memset (&ev, 0, sizeof(ev)); + ev.source.client = SNDRV_SEQ_CLIENT_OSS; + ev.dest = arg->addr; + + ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR; + ev.queue = SNDRV_SEQ_QUEUE_DIRECT; + + fs = snd_enter_user(); + __again: + ev.type = SNDRV_SEQ_EVENT_INSTR_PUT; + ev.data.ext.len = size; + ev.data.ext.ptr = put; + + err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, + opl3->seq_client, 0, 0); + if (err == -EBUSY) { + snd_seq_instr_header_t remove; + + memset (&remove, 0, sizeof(remove)); + remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE; + remove.id.instr = put->id.instr; + + /* remove instrument */ + ev.type = SNDRV_SEQ_EVENT_INSTR_FREE; + ev.data.ext.len = sizeof(remove); + ev.data.ext.ptr = &remove; + + snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, + opl3->seq_client, 0, 0); + goto __again; + } + snd_leave_user(fs); + + kfree(put); + } + return err; +} + +/* ioctl */ +static int snd_opl3_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, + unsigned long ioarg) +{ + opl3_t *opl3; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + switch (cmd) { + case SNDCTL_FM_LOAD_INSTR: + snd_printk("OPL3: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); + return -EINVAL; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + case SNDCTL_FM_4OP_ENABLE: + // handled automatically by OPL instrument type + return 0; + + default: + return -EINVAL; + } + return 0; +} + +/* reset device */ +static int snd_opl3_reset_seq_oss(snd_seq_oss_arg_t *arg) +{ + opl3_t *opl3; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + + return 0; +} diff -Nru a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/opl3/opl3_seq.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,321 @@ +/* + * Copyright (c) by Uros Bizjak + * + * Midi Sequencer interface routines for OPL2/OPL3/OPL4 FM + * + * OPL2/3 FM instrument loader: + * alsa-tools/seq/sbiload/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "opl3_voice.h" +#include +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Uros Bizjak "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth"); +MODULE_CLASSES("{sound}"); + +int use_internal_drums = 0; +MODULE_PARM(use_internal_drums, "i"); +MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums."); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +int snd_opl3_synth_use_inc(opl3_t * opl3) +{ + if (!try_inc_mod_count(opl3->card->module)) + return -EFAULT; + return 0; + +} + +void snd_opl3_synth_use_dec(opl3_t * opl3) +{ + dec_mod_count(opl3->card->module); +} + +int snd_opl3_synth_setup(opl3_t * opl3) +{ + int idx; + + down(&opl3->access_mutex); + if (opl3->used) { + up(&opl3->access_mutex); + return -EBUSY; + } + opl3->used++; + up(&opl3->access_mutex); + + snd_opl3_reset(opl3); + + for (idx = 0; idx < MAX_OPL3_VOICES; idx++) { + opl3->voices[idx].state = SNDRV_OPL3_ST_OFF; + opl3->voices[idx].time = 0; + opl3->voices[idx].keyon_reg = 0x00; + } + opl3->use_time = 0; + opl3->connection_reg = 0x00; + if (opl3->hardware >= OPL3_HW_OPL3) { + /* Enter OPL3 mode */ + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE); + /* Clear 4-op connections */ + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, + opl3->connection_reg); + opl3->max_voices = MAX_OPL3_VOICES; + } + return 0; +} + +void snd_opl3_synth_cleanup(opl3_t * opl3) +{ + unsigned long flags; + + /* Stop system timer */ + spin_lock_irqsave(&opl3->sys_timer_lock, flags); + if (opl3->sys_timer_status) { + del_timer(&opl3->tlist); + opl3->sys_timer_status = 0; + } + spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); + + snd_opl3_reset(opl3); + down(&opl3->access_mutex); + opl3->used--; + up(&opl3->access_mutex); +} + +int snd_opl3_synth_use(void *private_data, snd_seq_port_subscribe_t * info) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -ENXIO); + int err; + + if ((err = snd_opl3_synth_setup(opl3)) < 0) + return err; + + if (use_internal_drums) { + /* Percussion mode */ + opl3->voices[6].state = opl3->voices[7].state = + opl3->voices[8].state = SNDRV_OPL3_ST_NOT_AVAIL; + snd_opl3_load_drums(opl3); + opl3->drum_reg = OPL3_PERCUSSION_ENABLE; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg); + } else { + opl3->drum_reg = 0x00; + } + + if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) { + if ((err = snd_opl3_synth_use_inc(opl3)) < 0) + return err; + } + opl3->synth_mode = SNDRV_OPL3_MODE_SEQ; + return 0; +} + +int snd_opl3_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -ENXIO); + + snd_opl3_synth_cleanup(opl3); + + if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) + snd_opl3_synth_use_dec(opl3); + return 0; +} + +/* + * MIDI emulation operators + */ +snd_midi_op_t opl3_ops = { + snd_opl3_note_on, + snd_opl3_note_off, + snd_opl3_key_press, + snd_opl3_terminate_note, + snd_opl3_control, + snd_opl3_nrpn, + snd_opl3_sysex, +}; + +static int snd_opl3_synth_event_input(snd_seq_event_t * ev, int direct, + void *private_data, int atomic, int hop) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -EINVAL); + + if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN && + ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) { + if (direct) { + snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev, + opl3->seq_client, atomic, hop); + } + } else { + snd_midi_process_event(&opl3_ops, ev, opl3->chset); + } + return 0; +} + +/* ------------------------------ */ + +static void snd_opl3_synth_free_port(void *private_data) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return); + + snd_midi_channel_free_set(opl3->chset); +} + +static int snd_opl3_synth_create_port(opl3_t * opl3) +{ + snd_seq_port_callback_t callbacks; + char name[32]; + int opl_ver; + + opl3->chset = snd_midi_channel_alloc_set(16); + if (opl3->chset == NULL) + return -ENOMEM; + opl3->chset->private_data = opl3; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.use = snd_opl3_synth_use; + callbacks.unuse = snd_opl3_synth_unuse; + callbacks.event_input = snd_opl3_synth_event_input; + callbacks.private_free = snd_opl3_synth_free_port; + callbacks.private_data = opl3; + + opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; + sprintf(name, "OPL%i Port", opl_ver); + + opl3->chset->client = opl3->seq_client; + opl3->chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE | + SNDRV_SEQ_PORT_CAP_SUBS_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_SYNTH, + name); + if (opl3->chset->port < 0) { + snd_midi_channel_free_set(opl3->chset); + return opl3->chset->port; + } + return 0; +} + +/* ------------------------------ */ + +static int snd_opl3_seq_new_device(snd_seq_device_t *dev) +{ + opl3_t *opl3; + int client; + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + int opl_ver; + + opl3 = *(opl3_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (opl3 == NULL) + return -EINVAL; + + spin_lock_init(&opl3->voice_lock); + + opl3->seq_client = -1; + + /* allocate new client */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = opl3; + callbacks.allow_output = callbacks.allow_input = 1; + client = opl3->seq_client = + snd_seq_create_kernel_client(opl3->card, opl3->seq_dev_num, &callbacks); + if (client < 0) + return client; + + /* change name of client */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; + sprintf(cinfo.name, "OPL%i FM synth", opl_ver); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + snd_opl3_synth_create_port(opl3); + + /* initialize instrument list */ + opl3->ilist = snd_seq_instr_list_new(); + if (opl3->ilist == NULL) { + snd_seq_delete_kernel_client(client); + opl3->seq_client = -1; + return -ENOMEM; + } + opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; + snd_seq_fm_init(&opl3->fm_ops, NULL); + + /* setup system timer */ + memset(&opl3->tlist, 0, sizeof(opl3->tlist)); + opl3->tlist.function = snd_opl3_timer_func; + opl3->tlist.data = (unsigned long) opl3; + spin_lock_init(&opl3->sys_timer_lock); + opl3->sys_timer_status = 0; + +#ifdef CONFIG_SND_OSSEMUL + snd_opl3_init_seq_oss(opl3, cinfo.name); +#endif + return 0; +} + +static int snd_opl3_seq_delete_device(snd_seq_device_t *dev) +{ + opl3_t *opl3; + + opl3 = *(opl3_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (opl3 == NULL) + return -EINVAL; + +#ifdef CONFIG_SND_OSSEMUL + snd_opl3_free_seq_oss(opl3); +#endif + if (opl3->seq_client >= 0) { + snd_seq_delete_kernel_client(opl3->seq_client); + opl3->seq_client = -1; + } + if (opl3->ilist) + snd_seq_instr_list_free(&opl3->ilist); + return 0; +} + +static int __init alsa_opl3_seq_init(void) +{ + static snd_seq_dev_ops_t ops = + { + snd_opl3_seq_new_device, + snd_opl3_seq_delete_device + }; + + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops, + sizeof(opl3_t*)); +} + +static void __exit alsa_opl3_seq_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3); +} + +module_init(alsa_opl3_seq_init) +module_exit(alsa_opl3_seq_exit) diff -Nru a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/opl3/opl3_synth.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,456 @@ +/* + * Copyright (c) by Uros Bizjak + * + * Routines for OPL2/OPL3/OPL4 control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#define __SND_OSS_COMPAT__ +#include + +/* + * There is 18 possible 2 OP voices + * (9 in the left and 9 in the right). + * The first OP is the modulator and 2nd is the carrier. + * + * The first three voices in the both sides may be connected + * with another voice to a 4 OP voice. For example voice 0 + * can be connected with voice 3. The operators of voice 3 are + * used as operators 3 and 4 of the new 4 OP voice. + * In this case the 2 OP voice number 0 is the 'first half' and + * voice 3 is the second. + */ + + +/* + * Register offset table for OPL2/3 voices, + * OPL2 / one OPL3 register array side only + */ + +char snd_opl3_regmap[MAX_OPL2_VOICES][4] = +{ +/* OP1 OP2 OP3 OP4 */ +/* ------------------------ */ + { 0x00, 0x03, 0x08, 0x0b }, + { 0x01, 0x04, 0x09, 0x0c }, + { 0x02, 0x05, 0x0a, 0x0d }, + + { 0x08, 0x0b, 0x00, 0x00 }, + { 0x09, 0x0c, 0x00, 0x00 }, + { 0x0a, 0x0d, 0x00, 0x00 }, + + { 0x10, 0x13, 0x00, 0x00 }, /* used by percussive voices */ + { 0x11, 0x14, 0x00, 0x00 }, /* if the percussive mode */ + { 0x12, 0x15, 0x00, 0x00 } /* is selected (only left reg block) */ +}; + +/* + * prototypes + */ +static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note); +static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice); +static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params); +static int snd_opl3_set_mode(opl3_t * opl3, int mode); +static int snd_opl3_set_connection(opl3_t * opl3, int connection); + +/* ------------------------------ */ + +/* + * open the device exclusively + */ +int snd_opl3_open(snd_hwdep_t * hw, struct file *file) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + + down(&opl3->access_mutex); + if (opl3->used) { + up(&opl3->access_mutex); + return -EAGAIN; + } + opl3->used++; + up(&opl3->access_mutex); + + return 0; +} + +/* + * ioctl for hwdep device: + */ +int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + + snd_assert(opl3 != NULL, return -EINVAL); + + switch (cmd) { + /* get information */ + case SNDRV_DM_FM_IOCTL_INFO: + { + snd_dm_fm_info_t info; + + info.fm_mode = opl3->fm_mode; + info.rhythm = opl3->rhythm; + if (copy_to_user((snd_dm_fm_info_t *) arg, &info, sizeof(snd_dm_fm_info_t))) + return -EFAULT; + return 0; + } + + case SNDRV_DM_FM_IOCTL_RESET: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_RESET: +#endif + snd_opl3_reset(opl3); + return 0; + + case SNDRV_DM_FM_IOCTL_PLAY_NOTE: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE: +#endif + { + snd_dm_fm_note_t note; + if (copy_from_user(¬e, (snd_dm_fm_note_t *) arg, sizeof(snd_dm_fm_note_t))) + return -EFAULT; + return snd_opl3_play_note(opl3, ¬e); + } + + case SNDRV_DM_FM_IOCTL_SET_VOICE: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_VOICE: +#endif + { + snd_dm_fm_voice_t voice; + if (copy_from_user(&voice, (snd_dm_fm_voice_t *) arg, sizeof(snd_dm_fm_voice_t))) + return -EFAULT; + return snd_opl3_set_voice(opl3, &voice); + } + + case SNDRV_DM_FM_IOCTL_SET_PARAMS: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS: +#endif + { + snd_dm_fm_params_t params; + if (copy_from_user(¶ms, (snd_dm_fm_params_t *) arg, sizeof(snd_dm_fm_params_t))) + return -EFAULT; + return snd_opl3_set_params(opl3, ¶ms); + } + + case SNDRV_DM_FM_IOCTL_SET_MODE: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_MODE: +#endif + return snd_opl3_set_mode(opl3, (int) arg); + + case SNDRV_DM_FM_IOCTL_SET_CONNECTION: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_OPL: +#endif + return snd_opl3_set_connection(opl3, (int) arg); + +#ifdef CONFIG_SND_DEBUG + default: + snd_printk("unknown IOCTL: 0x%x\n", cmd); +#endif + } + return -ENOTTY; +} + +/* + * close the device + */ +int snd_opl3_release(snd_hwdep_t * hw, struct file *file) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + + snd_opl3_reset(opl3); + down(&opl3->access_mutex); + opl3->used--; + up(&opl3->access_mutex); + + return 0; +} + +/* ------------------------------ */ + +void snd_opl3_reset(opl3_t * opl3) +{ + unsigned short opl3_reg; + + unsigned short reg_side; + unsigned char voice_offset; + + int max_voices, i; + + max_voices = (opl3->hardware < OPL3_HW_OPL3) ? + MAX_OPL2_VOICES : MAX_OPL3_VOICES; + + for (i = 0; i < max_voices; i++) { + /* Get register array side and offset of voice */ + if (i < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = i; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = i - MAX_OPL2_VOICES; + } + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][0]); + opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 1 volume */ + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][1]); + opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 2 volume */ + + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, 0x00); /* Note off */ + } + + if (opl3->hardware >= OPL3_HW_OPL3) + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); /* Enter OPL2 mode */ + + opl3->max_voices = MAX_OPL2_VOICES; + opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2; + + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); /* Melodic mode */ + opl3->rhythm = 0; +} + + +static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note) +{ + unsigned short reg_side; + unsigned char voice_offset; + + unsigned short opl3_reg; + unsigned char reg_val; + + /* Voices 0 - 8 in OPL2 mode */ + /* Voices 0 - 17 in OPL3 mode */ + if (note->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ? + MAX_OPL3_VOICES : MAX_OPL2_VOICES)) + return -EINVAL; + + /* Get register array side and offset of voice */ + if (note->voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = note->voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = note->voice - MAX_OPL2_VOICES; + } + + /* Set lower 8 bits of note frequency */ + reg_val = (unsigned char) note->fnum; + opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + reg_val = 0x00; + /* Set output sound flag */ + if (note->key_on) + reg_val |= OPL3_KEYON_BIT; + /* Set octave */ + reg_val |= (note->octave << 2) & OPL3_BLOCKNUM_MASK; + /* Set higher 2 bits of note frequency */ + reg_val |= (unsigned char) (note->fnum >> 8) & OPL3_FNUM_HIGH_MASK; + + /* Set OPL3 KEYON_BLOCK register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + return 0; +} + + +static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice) +{ + unsigned short reg_side; + unsigned char op_offset; + unsigned char voice_offset; + + unsigned short opl3_reg; + unsigned char reg_val; + + /* Only operators 1 and 2 */ + if (voice->op > 1) + return -EINVAL; + /* Voices 0 - 8 in OPL2 mode */ + /* Voices 0 - 17 in OPL3 mode */ + if (voice->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ? + MAX_OPL3_VOICES : MAX_OPL2_VOICES)) + return -EINVAL; + + /* Get register array side and offset of voice */ + if (voice->voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice->voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice->voice - MAX_OPL2_VOICES; + } + /* Get register offset of operator */ + op_offset = snd_opl3_regmap[voice_offset][voice->op]; + + reg_val = 0x00; + /* Set amplitude modulation (tremolo) effect */ + if (voice->am) + reg_val |= OPL3_TREMOLO_ON; + /* Set vibrato effect */ + if (voice->vibrato) + reg_val |= OPL3_VIBRATO_ON; + /* Set sustaining sound phase */ + if (voice->do_sustain) + reg_val |= OPL3_SUSTAIN_ON; + /* Set keyboard scaling bit */ + if (voice->kbd_scale) + reg_val |= OPL3_KSR; + /* Set harmonic or frequency multiplier */ + reg_val |= voice->harmonic & OPL3_MULTIPLE_MASK; + + /* Set OPL3 AM_VIB register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set decreasing volume of higher notes */ + reg_val = (voice->scale_level << 6) & OPL3_KSL_MASK; + /* Set output volume */ + reg_val |= ~voice->volume & OPL3_TOTAL_LEVEL_MASK; + + /* Set OPL3 KSL_LEVEL register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set attack phase level */ + reg_val = (voice->attack << 4) & OPL3_ATTACK_MASK; + /* Set decay phase level */ + reg_val |= voice->decay & OPL3_DECAY_MASK; + + /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set sustain phase level */ + reg_val = (voice->sustain << 4) & OPL3_SUSTAIN_MASK; + /* Set release phase level */ + reg_val |= voice->release & OPL3_RELEASE_MASK; + + /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set inter-operator feedback */ + reg_val = (voice->feedback << 1) & OPL3_FEEDBACK_MASK; + /* Set inter-operator connection */ + if (voice->connection) + reg_val |= OPL3_CONNECTION_BIT; + /* OPL-3 only */ + if (opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) { + if (voice->left) + reg_val |= OPL3_VOICE_TO_LEFT; + if (voice->right) + reg_val |= OPL3_VOICE_TO_RIGHT; + } + /* Feedback/connection bits are applicable to voice */ + opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Select waveform */ + reg_val = voice->waveform & OPL3_WAVE_SELECT_MASK; + opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + return 0; +} + +static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params) +{ + unsigned char reg_val; + + reg_val = 0x00; + /* Set keyboard split method */ + if (params->kbd_split) + reg_val |= OPL3_KEYBOARD_SPLIT; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_KBD_SPLIT, reg_val); + + reg_val = 0x00; + /* Set amplitude modulation (tremolo) depth */ + if (params->am_depth) + reg_val |= OPL3_TREMOLO_DEPTH; + /* Set vibrato depth */ + if (params->vib_depth) + reg_val |= OPL3_VIBRATO_DEPTH; + /* Set percussion mode */ + if (params->rhythm) { + reg_val |= OPL3_PERCUSSION_ENABLE; + opl3->rhythm = 1; + } else { + opl3->rhythm = 0; + } + /* Play percussion instruments */ + if (params->bass) + reg_val |= OPL3_BASSDRUM_ON; + if (params->snare) + reg_val |= OPL3_SNAREDRUM_ON; + if (params->tomtom) + reg_val |= OPL3_TOMTOM_ON; + if (params->cymbal) + reg_val |= OPL3_CYMBAL_ON; + if (params->hihat) + reg_val |= OPL3_HIHAT_ON; + + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, reg_val); + return 0; +} + +static int snd_opl3_set_mode(opl3_t * opl3, int mode) +{ + if ((mode == SNDRV_DM_FM_MODE_OPL3) && (opl3->hardware < OPL3_HW_OPL3)) + return -EINVAL; + + if (mode == SNDRV_DM_FM_MODE_OPL3) { + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE); /* Enter OPL3 mode */ + opl3->fm_mode = SNDRV_DM_FM_MODE_OPL3; + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, 0x00); /* Clear 4-op connections */ + } else { + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); /* Enter OPL2 mode */ + opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2; + } + + return 0; +} + +static int snd_opl3_set_connection(opl3_t * opl3, int connection) +{ + unsigned char reg_val; + + /* OPL-3 only */ + if (opl3->fm_mode != SNDRV_DM_FM_MODE_OPL3) + return -EINVAL; + + reg_val = connection & (OPL3_RIGHT_4OP_0 | OPL3_RIGHT_4OP_1 | OPL3_RIGHT_4OP_2 | + OPL3_LEFT_4OP_0 | OPL3_LEFT_4OP_1 | OPL3_LEFT_4OP_2); + /* Set 4-op connections */ + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, reg_val); + + return 0; +} diff -Nru a/sound/drivers/opl3/opl3_voice.h b/sound/drivers/opl3/opl3_voice.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/opl3/opl3_voice.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,52 @@ +#ifndef __OPL3_VOICE_H +#define __OPL3_VOICE_H + +/* + * Copyright (c) 2000 Uros Bizjak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/* Prototypes for opl3_seq.c */ +int snd_opl3_synth_use_inc(opl3_t * opl3); +void snd_opl3_synth_use_dec(opl3_t * opl3); +int snd_opl3_synth_setup(opl3_t * opl3); +void snd_opl3_synth_cleanup(opl3_t * opl3); + +/* Prototypes for opl3_midi.c */ +void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan); +void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan); +void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); +void snd_opl3_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); + +void snd_opl3_calc_volume(unsigned char *reg, int vel, snd_midi_channel_t *chan); +void snd_opl3_timer_func(unsigned long data); + +/* Prototypes for opl3_drums.c */ +void snd_opl3_load_drums(opl3_t *opl3); +void snd_opl3_drum_switch(opl3_t *opl3, int note, int on_off, int vel, snd_midi_channel_t *chan); + +/* Prototypes for opl3_oss.c */ +#ifdef CONFIG_SND_OSSEMUL +void snd_opl3_init_seq_oss(opl3_t *opl3, char *name); +void snd_opl3_free_seq_oss(opl3_t *opl3); +#endif + +#endif diff -Nru a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/serial-u16550.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,989 @@ +/* + * serial.c + * Copyright (c) by Jaroslav Kysela , + * Isaku Yamahata , + * George Hansper , + * Hannu Savolainen + * + * This code is based on the code from ALSA 0.5.9, but heavily rewritten. + * + * Sat Mar 31 17:27:57 PST 2001 tim.mann@compaq.com + * Added support for the Midiator MS-124T and for the MS-124W in + * Single Addressed (S/A) or Multiple Burst (M/B) mode, with + * power derived either parasitically from the serial port or + * from a separate power supply. + * + * The new snd_adaptor module parameter allows you to select + * either the default Roland Soundcanvas support (0), which was + * previously included in this driver but was not documented, + * Midiator MS-124T support (1), Midiator MS-124W S/A mode + * support (2), or MS-124W M/B mode support (3). For the + * Midiator MS-124W, you must set the physical M-S and A-B + * switches on the Midiator to match the driver mode you select. + * + * - In Roland Soundcanvas mode, multiple ALSA raw MIDI + * substreams are supported (midiCnD0-midiCnD15). Whenever you + * write to a different substream, the driver sends the + * nonstandard MIDI command sequence F5 NN, where NN is the + * substream number plus 1. Roland modules use this command to + * switch between different "parts", so this feature lets you + * treat each part as a distinct raw MIDI substream. The driver + * provides no way to send F5 00 (no selection) or to not send + * the F5 NN command sequence at all; perhaps it ought to. + * + * - In MS-124T mode, one raw MIDI substream is supported + * (midiCnD0); the snd_outs module parameter is automatically set + * to 1. The driver sends the same data to all four MIDI Out + * connectors. Set the A-B switch and the snd_speed module + * parameter to match (A=19200, B=9600). + * + * Usage example for MS-124T, with A-B switch in A position: + * setserial /dev/ttyS0 uart none + * /sbin/modprobe snd-card-serial snd_port=0x3f8 snd_irq=4 \ + * snd_adaptor=1 snd_speed=19200 + * + * - In MS-124W S/A mode, one raw MIDI substream is supported + * (midiCnD0); the snd_outs module parameter is automatically set + * to 1. The driver sends the same data to all four MIDI Out + * connectors at full MIDI speed. + * + * Usage example for S/A mode: + * setserial /dev/ttyS0 uart none + * /sbin/modprobe snd-card-serial snd_port=0x3f8 snd_irq=4 \ + * snd_adaptor=2 + * + * - In MS-124W M/B mode, the driver supports 16 ALSA raw MIDI + * substreams; the snd_outs module parameter is automatically set + * to 16. The substream number gives a bitmask of which MIDI Out + * connectors the data should be sent to, with midiCnD1 sending + * to Out 1, midiCnD2 to Out 2, midiCnD4 to Out 3, and midiCnD8 + * to Out 4. Thus midiCnD15 sends the data to all 4 ports. As a + * special case, midiCnD0 also sends to all ports, since it is + * not useful to send the data to no ports. M/B mode has extra + * overhead to select the MIDI Out for each byte, so the + * aggregate data rate across all four MIDI Outs is at most one + * byte every 520 us, as compared with the full MIDI data rate of + * one byte every 320 us per port. + * + * Usage example for M/B mode: + * setserial /dev/ttyS0 uart none + * /sbin/insmod snd-card-serial snd_port=0x3f8 snd_irq=4 \ + * snd_adaptor=3 + * + * - The MS-124W hardware's M/A mode is currently not supported. + * This mode allows the MIDI Outs to act independently at double + * the aggregate throughput of M/B, but does not allow sending + * the same byte simultaneously to multiple MIDI Outs. The M/A + * protocol requires the driver to twiddle the modem control + * lines under timing constraints, so it would be a bit more + * complicated to implement than the other modes. + * + * - Midiator models other than MS-124W and MS-124T are currently + * not supported. Note that the suffix letter is significant; + * the MS-124 and MS-124B are not compatible, nor are the other + * known models MS-101, MS-101B, MS-103, and MS-114. I do have + * documentation that partially covers these models, but no units + * to experiment with. The MS-124W support is tested with a real + * unit. The MS-124T support is untested, but should work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +EXPORT_NO_SYMBOLS; +MODULE_DESCRIPTION("MIDI serial"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALSA, MIDI serial}}"); + +#define SNDRV_SERIAL_SOUNDCANVAS 0 /* Roland Soundcanvas; F5 NN selects part */ +#define SNDRV_SERIAL_MS124T 1 /* Midiator MS-124T */ +#define SNDRV_SERIAL_MS124W_SA 2 /* Midiator MS-124W in S/A mode */ +#define SNDRV_SERIAL_MS124W_MB 3 /* Midiator MS-124W in M/B mode */ +#define SNDRV_SERIAL_MAX_ADAPTOR SNDRV_SERIAL_MS124W_MB +static char *adaptor_names[] = { + "Soundcanvas", + "MS-124T", + "MS-124W S/A", + "MS-124W M/B" +}; + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x3f8,0x2f8,0x3e8,0x2e8 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,4,5,7,9,10,11,14,15 */ +static int snd_speed[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 38400}; /* 9600,19200,38400,57600,115200 */ +static int snd_base[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 115200}; /* baud base */ +static int snd_outs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */ +static int snd_adaptor[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = SNDRV_SERIAL_SOUNDCANVAS}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Serial MIDI."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Serial MIDI."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_enable, "Enable UART16550A chip."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for UART16550A chip."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for UART16550A chip."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_speed, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_speed, "Speed in bauds."); +MODULE_PARM_SYNTAX(snd_speed, SNDRV_ENABLED ",allows:{9600,19200,38400,57600,115200},dialog:list"); +MODULE_PARM(snd_base, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_base, "Base for divisor in bauds."); +MODULE_PARM_SYNTAX(snd_base, SNDRV_ENABLED ",allows:{57600,115200,230400,460800},dialog:list"); +MODULE_PARM(snd_outs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_outs, "Number of MIDI outputs."); +MODULE_PARM_SYNTAX(snd_outs, SNDRV_ENABLED ",allows:{{1,16}},dialog:list"); +MODULE_PARM(snd_adaptor, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_adaptor, "Type of adaptor."); +MODULE_PARM_SYNTAX(snd_adaptor, SNDRV_ENABLED ",allows:{{0=Soundcanvas,1=MS-124T,2=MS-124W S/A,3=MS-124W M/B}},dialog:list"); + +/*#define SNDRV_SERIAL_MS124W_MB_NOCOMBO 1*/ /* Address outs as 0-3 instead of bitmap */ + +#define SNDRV_SERIAL_MAX_OUTS 16 /* max 64, min 16 */ + +#define TX_BUFF_SIZE (1<<9) /* Must be 2^n */ +#define TX_BUFF_MASK (TX_BUFF_SIZE - 1) + +#define SERIAL_MODE_NOT_OPENED (0) +#define SERIAL_MODE_INPUT_OPEN (1 << 0) +#define SERIAL_MODE_OUTPUT_OPEN (1 << 1) +#define SERIAL_MODE_INPUT_TRIGGERED (1 << 2) +#define SERIAL_MODE_OUTPUT_TRIGGERED (1 << 3) + +typedef struct _snd_uart16550 { + snd_card_t *card; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_output[SNDRV_SERIAL_MAX_OUTS]; + snd_rawmidi_substream_t *midi_input; + + int filemode; //open status of file + + spinlock_t open_lock; + + int irq; + + unsigned long base; + struct resource *res_base; + + unsigned int speed; + unsigned int speed_base; + unsigned char divisor; + + unsigned char old_divisor_lsb; + unsigned char old_divisor_msb; + unsigned char old_line_ctrl_reg; + + // parameter for using of write loop + short int fifo_limit; //used in uart16550 + short int fifo_count; //used in uart16550 + + // type of adaptor + int adaptor; + + // outputs + int prev_out; + unsigned char prev_status[SNDRV_SERIAL_MAX_OUTS]; + + // write buffer and its writing/reading position + unsigned char tx_buff[TX_BUFF_SIZE]; + int buff_in_count; + int buff_in; + int buff_out; + + // wait timer + unsigned int timer_running:1; + struct timer_list buffer_timer; + +} snd_uart16550_t; + +static snd_card_t *snd_serial_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +inline static void snd_uart16550_add_timer(snd_uart16550_t *uart) +{ + if (! uart->timer_running) { + /* timer 38600bps * 10bit * 16byte */ + uart->buffer_timer.expires = jiffies + (HZ+255)/256; + uart->timer_running = 1; + add_timer(&uart->buffer_timer); + } +} + +inline static void snd_uart16550_del_timer(snd_uart16550_t *uart) +{ + if (uart->timer_running) { + del_timer(&uart->buffer_timer); + uart->timer_running = 0; + } +} + +/* This macro is only used in snd_uart16550_io_loop */ +inline static void snd_uart16550_buffer_output(snd_uart16550_t *uart) +{ + unsigned short buff_out = uart->buff_out; + outb(uart->tx_buff[buff_out], uart->base + UART_TX); + uart->fifo_count++; + buff_out++; + buff_out &= TX_BUFF_MASK; + uart->buff_out = buff_out; + uart->buff_in_count--; +} + +/* This loop should be called with interrupts disabled + * We don't want to interrupt this, + * as we're already handling an interupt + */ +static void snd_uart16550_io_loop(snd_uart16550_t * uart) +{ + unsigned char c, status; + + /* Read Loop */ + while ((status = inb(uart->base + UART_LSR)) & UART_LSR_DR) { + /* while receive data ready */ + c = inb(uart->base + UART_RX); + if (uart->filemode & SERIAL_MODE_INPUT_OPEN) { + snd_rawmidi_receive(uart->midi_input, &c, 1); + } + if (status & UART_LSR_OE) + snd_printk("%s: Overrun on device at 0x%lx\n", + uart->rmidi->name, uart->base); + } + + /* no need of check SERIAL_MODE_OUTPUT_OPEN because if not, + buffer is never filled. */ + /* Check write status */ + if (status & UART_LSR_THRE) { + uart->fifo_count = 0; + } + if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) { + /* Can't use FIFO, must send only when CTS is true */ + status = inb(uart->base + UART_MSR); + if (uart->fifo_count == 0 && (status & UART_MSR_CTS) + && uart->buff_in_count > 0) + snd_uart16550_buffer_output(uart); + } else { + /* Write loop */ + while (uart->fifo_count < uart->fifo_limit /* Can we write ? */ + && uart->buff_in_count > 0) /* Do we want to? */ + snd_uart16550_buffer_output(uart); + } + if (uart->irq < 0 && uart->buff_in_count > 0) + snd_uart16550_add_timer(uart); +} + +/* NOTES ON SERVICING INTERUPTS + * --------------------------- + * After receiving a interrupt, it is important to indicate to the UART that + * this has been done. + * For a Rx interupt, this is done by reading the received byte. + * For a Tx interupt this is done by either: + * a) Writing a byte + * b) Reading the IIR + * It is particularly important to read the IIR if a Tx interupt is received + * when there is no data in tx_buff[], as in this case there no other + * indication that the interupt has been serviced, and it remains outstanding + * indefinitely. This has the curious side effect that and no further interupts + * will be generated from this device AT ALL!!. + * It is also desirable to clear outstanding interupts when the device is + * opened/closed. + * + * + * Note that some devices need OUT2 to be set before they will generate + * interrupts at all. (Possibly tied to an internal pull-up on CTS?) + */ +static void snd_uart16550_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + snd_uart16550_t *uart; + + uart = (snd_uart16550_t *) dev_id; + spin_lock(&uart->open_lock); + if (uart->filemode == SERIAL_MODE_NOT_OPENED) { + spin_unlock(&uart->open_lock); + return; + } + inb(uart->base + UART_IIR); /* indicate to the UART that the interupt has been serviced */ + snd_uart16550_io_loop(uart); + spin_unlock(&uart->open_lock); +} + +/* When the polling mode, this function calls snd_uart16550_io_loop. */ +static void snd_uart16550_buffer_timer(unsigned long data) +{ + snd_uart16550_t *uart; + + uart = (snd_uart16550_t *)data; + spin_lock(&uart->open_lock); + snd_uart16550_del_timer(uart); + snd_uart16550_io_loop(uart); + spin_unlock(&uart->open_lock); +} + +/* + * this method probes, if an uart sits on given port + * return 0 if found + * return negative error if not found + */ +static int __init snd_uart16550_detect(unsigned int io_base) +{ + int ok; + unsigned char c; + + if (check_region(io_base, 8)) + return -EBUSY; + + /* Do some vague tests for the presence of the uart */ + if (io_base == 0) + return -ENODEV; /* Not configured */ + + ok = 1; /* uart detected unless one of the following tests should fail */ + /* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */ + outb(UART_LCR_WLEN8, io_base + UART_LCR); /* Line Control Register */ + c = inb(io_base + UART_IER); + /* The top four bits of the IER should always == 0 */ + if ((c & 0xf0) != 0) + ok = 0; /* failed */ + + outb(0xaa, io_base + UART_SCR); + /* Write arbitrary data into the scratch reg */ + c = inb(io_base + UART_SCR); + /* If it comes back, it's OK */ + if (c != 0xaa) + ok = 0; /* failed */ + + outb(0x55, io_base + UART_SCR); + /* Write arbitrary data into the scratch reg */ + c = inb(io_base + UART_SCR); + /* If it comes back, it's OK */ + if (c != 0x55) + ok = 0; /* failed */ + + return ok; +} + +static void snd_uart16550_do_open(snd_uart16550_t * uart) +{ + char byte; + + /* Initialize basic variables */ + uart->buff_in_count = 0; + uart->buff_in = 0; + uart->buff_out = 0; + uart->fifo_limit = 1; + uart->fifo_count = 0; + uart->timer_running = 0; + + outb(UART_FCR_ENABLE_FIFO /* Enable FIFO's (if available) */ + | UART_FCR_CLEAR_RCVR /* Clear receiver FIFO */ + | UART_FCR_CLEAR_XMIT /* Clear transmitter FIFO */ + | UART_FCR_TRIGGER_4 /* Set FIFO trigger at 4-bytes */ + /* NOTE: interupt generated after T=(time)4-bytes + * if less than UART_FCR_TRIGGER bytes received + */ + ,uart->base + UART_FCR); /* FIFO Control Register */ + + if ((inb(uart->base + UART_IIR) & 0xf0) == 0xc0) + uart->fifo_limit = 16; + if (uart->divisor != 0) { + uart->old_line_ctrl_reg = inb(uart->base + UART_LCR); + outb(UART_LCR_DLAB /* Divisor latch access bit */ + ,uart->base + UART_LCR); /* Line Control Register */ + uart->old_divisor_lsb = inb(uart->base + UART_DLL); + uart->old_divisor_msb = inb(uart->base + UART_DLM); + + outb(uart->divisor + ,uart->base + UART_DLL); /* Divisor Latch Low */ + outb(0 + ,uart->base + UART_DLM); /* Divisor Latch High */ + /* DLAB is reset to 0 in next outb() */ + } + /* Set serial parameters (parity off, etc) */ + outb(UART_LCR_WLEN8 /* 8 data-bits */ + | 0 /* 1 stop-bit */ + | 0 /* parity off */ + | 0 /* DLAB = 0 */ + ,uart->base + UART_LCR); /* Line Control Register */ + + switch (uart->adaptor) { + default: + outb(UART_MCR_RTS /* Set Request-To-Send line active */ + | UART_MCR_DTR /* Set Data-Terminal-Ready line active */ + | UART_MCR_OUT2 /* Set OUT2 - not always required, but when + * it is, it is ESSENTIAL for enabling interrupts + */ + ,uart->base + UART_MCR); /* Modem Control Register */ + break; + case SNDRV_SERIAL_MS124W_SA: + case SNDRV_SERIAL_MS124W_MB: + /* MS-124W can draw power from RTS and DTR if they + are in opposite states. */ + outb(UART_MCR_RTS | (0&UART_MCR_DTR) | UART_MCR_OUT2, + uart->base + UART_MCR); + break; + case SNDRV_SERIAL_MS124T: + /* MS-124T can draw power from RTS and/or DTR (preferably + both) if they are both asserted. */ + outb(UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2, + uart->base + UART_MCR); + break; + } + + if (uart->irq < 0) { + byte = (0 & UART_IER_RDI) /* Disable Receiver data interupt */ + |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interupt */ + ; + } else if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) { + byte = UART_IER_RDI /* Enable Receiver data interrupt */ + | UART_IER_MSI /* Enable Modem status interrupt */ + ; + } else { + byte = UART_IER_RDI /* Enable Receiver data interupt */ + | UART_IER_THRI /* Enable Transmitter holding register empty interupt */ + ; + } + outb(byte, uart->base + UART_IER); /* Interupt enable Register */ + + inb(uart->base + UART_LSR); /* Clear any pre-existing overrun indication */ + inb(uart->base + UART_IIR); /* Clear any pre-existing transmit interrupt */ + inb(uart->base + UART_RX); /* Clear any pre-existing receive interrupt */ +} + +static void snd_uart16550_do_close(snd_uart16550_t * uart) +{ + if (uart->irq < 0) + snd_uart16550_del_timer(uart); + + /* NOTE: may need to disable interrupts before de-registering out handler. + * For now, the consequences are harmless. + */ + + outb((0 & UART_IER_RDI) /* Disable Receiver data interupt */ + |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interupt */ + ,uart->base + UART_IER); /* Interupt enable Register */ + + switch (uart->adaptor) { + default: + outb((0 & UART_MCR_RTS) /* Deactivate Request-To-Send line */ + |(0 & UART_MCR_DTR) /* Deactivate Data-Terminal-Ready line */ + |(0 & UART_MCR_OUT2) /* Deactivate OUT2 */ + ,uart->base + UART_MCR); /* Modem Control Register */ + break; + case SNDRV_SERIAL_MS124W_SA: + case SNDRV_SERIAL_MS124W_MB: + /* MS-124W can draw power from RTS and DTR if they + are in opposite states; leave it powered. */ + outb(UART_MCR_RTS | (0&UART_MCR_DTR) | (0&UART_MCR_OUT2), + uart->base + UART_MCR); + break; + case SNDRV_SERIAL_MS124T: + /* MS-124T can draw power from RTS and/or DTR (preferably + both) if they are both asserted; leave it powered. */ + outb(UART_MCR_RTS | UART_MCR_DTR | (0&UART_MCR_OUT2), + uart->base + UART_MCR); + break; + } + + inb(uart->base + UART_IIR); /* Clear any outstanding interupts */ + + /* Restore old divisor */ + if (uart->divisor != 0) { + outb(UART_LCR_DLAB /* Divisor latch access bit */ + ,uart->base + UART_LCR); /* Line Control Register */ + outb(uart->old_divisor_lsb + ,uart->base + UART_DLL); /* Divisor Latch Low */ + outb(uart->old_divisor_msb + ,uart->base + UART_DLM); /* Divisor Latch High */ + /* Restore old LCR (data bits, stop bits, parity, DLAB) */ + outb(uart->old_line_ctrl_reg + ,uart->base + UART_LCR); /* Line Control Register */ + } +} + +static int snd_uart16550_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_open(uart); + uart->filemode |= SERIAL_MODE_INPUT_OPEN; + uart->midi_input = substream; + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +} + +static int snd_uart16550_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + uart->filemode &= ~SERIAL_MODE_INPUT_OPEN; + uart->midi_input = NULL; + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_close(uart); + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +} + +static void snd_uart16550_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&uart->open_lock, flags); + if (up) { + uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED; + } else { + uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED; + } + spin_unlock_irqrestore(&uart->open_lock, flags); +} + +static int snd_uart16550_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_open(uart); + uart->filemode |= SERIAL_MODE_OUTPUT_OPEN; + uart->midi_output[substream->number] = substream; + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +}; + +static int snd_uart16550_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN; + uart->midi_output[substream->number] = NULL; + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_close(uart); + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +}; + +inline static void snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte) +{ + unsigned short buff_in = uart->buff_in; + uart->tx_buff[buff_in] = byte; + buff_in++; + buff_in &= TX_BUFF_MASK; + uart->buff_in = buff_in; + uart->buff_in_count++; + if (uart->irq < 0) /* polling mode */ + snd_uart16550_add_timer(uart); +} + +static void snd_uart16550_output_byte(snd_uart16550_t *uart, snd_rawmidi_substream_t * substream, unsigned char midi_byte) +{ + if (uart->buff_in_count == 0 /* Buffer empty? */ + && (uart->adaptor != SNDRV_SERIAL_MS124W_SA || + (uart->fifo_count == 0 /* FIFO empty? */ + && (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */ + + /* Tx Buffer Empty - try to write immediately */ + if ((inb(uart->base + UART_LSR) & UART_LSR_THRE) != 0) { + /* Transmitter holding register (and Tx FIFO) empty */ + uart->fifo_count = 1; + outb(midi_byte, uart->base + UART_TX); + } else { + if (uart->fifo_count < uart->fifo_limit) { + uart->fifo_count++; + outb(midi_byte, uart->base + UART_TX); + } else { + /* Cannot write (buffer empty) - put char in buffer */ + snd_uart16550_write_buffer(uart, midi_byte); + } + } + } else { + if (uart->buff_in_count >= TX_BUFF_SIZE) { + snd_printk("%s: Buffer overrun on device at 0x%lx\n", + uart->rmidi->name, uart->base); + return; + } + snd_uart16550_write_buffer(uart, midi_byte); + } +} + +static void snd_uart16550_output_write(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + unsigned char midi_byte, addr_byte; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + char first; + + /* Interupts are disabled during the updating of the tx_buff, + * since it is 'bad' to have two processes updating the same + * variables (ie buff_in & buff_out) + */ + + spin_lock_irqsave(&uart->open_lock, flags); + + if (uart->irq < 0) //polling + snd_uart16550_io_loop(uart); + + if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) { + while (1) { + /* buffer full? */ + /* in this mode we need two bytes of space */ + if (uart->buff_in_count > TX_BUFF_SIZE - 2) + break; + if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1) + break; +#if SNDRV_SERIAL_MS124W_MB_NOCOMBO + /* select exactly one of the four ports */ + addr_byte = (1 << (substream->number + 4)) | 0x08; +#else + /* select any combination of the four ports */ + addr_byte = (substream->number << 4) | 0x08; + /* ...except none */ + if (addr_byte == 0x08) addr_byte = 0xf8; +#endif + snd_uart16550_output_byte(uart, substream, addr_byte); + /* send midi byte */ + snd_uart16550_output_byte(uart, substream, midi_byte); + } + } else { + first = 0; + while (1) { + /* buffer full? */ + if (uart->buff_in_count >= TX_BUFF_SIZE) + break; + if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1) + break; + if (first == 0 && uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS && + uart->prev_out != substream->number) { + /* Roland Soundcanvas part selection */ + /* If this substream of the data is different previous + substream in this uart, send the change part event */ + uart->prev_out = substream->number; + /* change part */ + snd_uart16550_output_byte(uart, substream, 0xf5); + /* data */ + snd_uart16550_output_byte(uart, substream, uart->prev_out + 1); + /* If midi_byte is a data byte, send the previous status byte */ + if (midi_byte < 0x80) + snd_uart16550_output_byte(uart, substream, uart->prev_status[uart->prev_out]); + } + /* send midi byte */ + snd_uart16550_output_byte(uart, substream, midi_byte); + if (midi_byte >= 0x80 && midi_byte < 0xf0) + uart->prev_status[uart->prev_out] = midi_byte; + first = 1; + } + } + spin_unlock_irqrestore(&uart->open_lock, flags); +} + +static void snd_uart16550_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&uart->open_lock, flags); + if (up) { + uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED; + } else { + uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED; + } + spin_unlock_irqrestore(&uart->open_lock, flags); + if (up) + snd_uart16550_output_write(substream); +} + +static snd_rawmidi_ops_t snd_uart16550_output = +{ + open: snd_uart16550_output_open, + close: snd_uart16550_output_close, + trigger: snd_uart16550_output_trigger, +}; + +static snd_rawmidi_ops_t snd_uart16550_input = +{ + open: snd_uart16550_input_open, + close: snd_uart16550_input_close, + trigger: snd_uart16550_input_trigger, +}; + +static int snd_uart16550_free(snd_uart16550_t *uart) +{ + if (uart->irq >= 0) + free_irq(uart->irq, (void *)uart); + if (uart->res_base) { + release_resource(uart->res_base); + kfree_nocheck(uart->res_base); + } + snd_magic_kfree(uart); + return 0; +}; + +static int snd_uart16550_dev_free(snd_device_t *device) +{ + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, device->device_data, return -ENXIO); + return snd_uart16550_free(uart); +} + +static int __init snd_uart16550_create(snd_card_t * card, + unsigned long iobase, + int irq, + unsigned int speed, + unsigned int base, + int adaptor, + snd_uart16550_t **ruart) +{ + static snd_device_ops_t ops = { + dev_free: snd_uart16550_dev_free, + }; + snd_uart16550_t *uart; + int err; + + + if ((uart = snd_magic_kcalloc(snd_uart16550_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + uart->adaptor = adaptor; + uart->card = card; + spin_lock_init(&uart->open_lock); + uart->irq = -1; + if ((uart->res_base = request_region(iobase, 8, "Serial MIDI")) == NULL) { + snd_printk("unable to grab ports 0x%lx-0x%lx\n", iobase, iobase + 8 - 1); + return -EBUSY; + } + uart->base = iobase; + if (irq >= 0) { + if (request_irq(irq, snd_uart16550_interrupt, + SA_INTERRUPT, "Serial MIDI", (void *) uart)) { + uart->irq = -1; + snd_printk("irq %d busy. Using Polling.\n", irq); + } else { + uart->irq = irq; + } + } + uart->divisor = base / speed; + uart->speed = base / (unsigned int)uart->divisor; + uart->speed_base = base; + uart->prev_out = -1; + memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS); + uart->buffer_timer.function = snd_uart16550_buffer_timer; + uart->buffer_timer.data = (unsigned long)uart; + uart->timer_running = 0; + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) { + snd_uart16550_free(uart); + return err; + } + + switch (uart->adaptor) { + case SNDRV_SERIAL_MS124W_SA: + case SNDRV_SERIAL_MS124W_MB: + /* MS-124W can draw power from RTS and DTR if they + are in opposite states. */ + outb(UART_MCR_RTS | (0&UART_MCR_DTR), uart->base + UART_MCR); + break; + case SNDRV_SERIAL_MS124T: + /* MS-124T can draw power from RTS and/or DTR (preferably + both) if they are asserted. */ + outb(UART_MCR_RTS | UART_MCR_DTR, uart->base + UART_MCR); + break; + default: + break; + } + + if (ruart) + *ruart = uart; + + return 0; +} + +static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int outs, snd_rawmidi_t **rmidi) +{ + snd_rawmidi_t *rrawmidi; + int err; + + if ((err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, outs, 1, &rrawmidi)) < 0) + return err; + snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_uart16550_input); + snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output); + sprintf(rrawmidi->name, "uart16550 MIDI #%d", device); + rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rrawmidi->private_data = uart; + if (rmidi) + *rmidi = rrawmidi; + return 0; +} + +static int __init snd_serial_probe(int dev) +{ + snd_card_t *card; + snd_uart16550_t *uart; + int err; + + if (!snd_enable[dev]) + return -ENOENT; + + switch (snd_adaptor[dev]) { + case SNDRV_SERIAL_SOUNDCANVAS: + break; + case SNDRV_SERIAL_MS124T: + case SNDRV_SERIAL_MS124W_SA: + snd_outs[dev] = 1; + break; + case SNDRV_SERIAL_MS124W_MB: + snd_outs[dev] = 16; + break; + default: + snd_printk("Adaptor type is out of range 0-%d (%d)\n", + SNDRV_SERIAL_MAX_ADAPTOR, snd_adaptor[dev]); + return -ENODEV; + } + + if (snd_outs[dev] < 1 || snd_outs[dev] > SNDRV_SERIAL_MAX_OUTS) { + snd_printk("Count of outputs is out of range 1-%d (%d)\n", + SNDRV_SERIAL_MAX_OUTS, snd_outs[dev]); + return -ENODEV; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "Serial"); + strcpy(card->shortname, "Serial midi (uart16550A)"); + + if ((err = snd_uart16550_detect(snd_port[dev])) <= 0) { + snd_card_free(card); + snd_printk("no UART detected at 0x%lx\n", (long)snd_port[dev]); + return err; + } + + if ((err = snd_uart16550_create(card, + snd_port[dev], + snd_irq[dev], + snd_speed[dev], + snd_base[dev], + snd_adaptor[dev], + &uart)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_uart16550_rmidi(uart, 0, snd_outs[dev], &uart->rmidi)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->longname, "%s at 0x%lx, irq %d speed %d div %d outs %d adaptor %s", + card->shortname, + uart->base, + uart->irq, + uart->speed, + (int)uart->divisor, + snd_outs[dev], + adaptor_names[uart->adaptor]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_serial_cards[dev] = card; + return 0; +} + +static int __init alsa_card_serial_init(void) +{ + int dev = 0; + int cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (snd_serial_probe(dev) == 0) + cards++; + } + + if (cards == 0) { +#ifdef MODULE + snd_printk("serial midi soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_serial_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (snd_serial_cards[dev] != NULL) + snd_card_free(snd_serial_cards[dev]); + } +} + +module_init(alsa_card_serial_init) +module_exit(alsa_card_serial_exit) + +#ifndef MODULE + +/* format is: snd-serial=snd_enable,snd_index,snd_id, + snd_port,snd_irq,snd_speed,snd_base,snd_outs */ + +static int __init alsa_card_serial_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_speed[nr_dev]) == 2 && + get_option(&str,&snd_base[nr_dev]) == 2 && + get_option(&str,&snd_outs[nr_dev]) == 2 && + get_option(&str,&snd_adaptor[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-serial=", alsa_card_serial_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/drivers/virmidi.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,185 @@ +/* + * Dummy soundcard for virtual rawmidi devices + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * VIRTUAL RAW MIDI DEVICE CARDS + * + * This dummy card contains up to 4 virtual rawmidi devices. + * They are not real rawmidi devices but just associated with sequencer + * clients, so that any input/output sources can be connected as a raw + * MIDI device arbitrary. + * Also, multiple access is allowed to a single rawmidi device. + * + * Typical usage is like following: + * - Load snd-virmidi module. + * # modprobe snd-virmidi snd_index=2 + * Then, sequencer clients 72:0 to 75:0 will be created, which are + * mapped from /dev/snd/midiC1D0 to /dev/snd/midiC1D3, respectively. + * + * - Connect input/output via aconnect. + * % aconnect 64:0 72:0 # keyboard input redirection 64:0 -> 72:0 + * % aconnect 72:0 65:0 # output device redirection 72:0 -> 65:0 + * + * - Run application using a midi device (eg. /dev/snd/midiC1D0) + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALSA,Virtual rawmidi device}}"); + +#define MAX_MIDI_DEVICES 8 + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for virmidi soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for virmidi soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable this soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_midi_devs, "MIDI devices # (1-8)"); +MODULE_PARM_SYNTAX(snd_midi_devs, SNDRV_ENABLED ",allows:{{1,8}}"); + +typedef struct snd_card_virmidi { + snd_card_t *card; + snd_rawmidi_t *midi[MAX_MIDI_DEVICES]; +} snd_card_virmidi_t; + +static snd_card_t *snd_virmidi_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_card_virmidi_probe(int dev) +{ + snd_card_t *card; + struct snd_card_virmidi *vmidi; + int idx, err; + + if (!snd_enable[dev]) + return -ENODEV; + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_virmidi)); + if (card == NULL) + return -ENOMEM; + vmidi = (struct snd_card_virmidi *)card->private_data; + vmidi->card = card; + + if (snd_midi_devs[dev] > MAX_MIDI_DEVICES) { + snd_printk("too much midi devices for virmidi %d: force to use %d\n", dev, MAX_MIDI_DEVICES); + snd_midi_devs[dev] = MAX_MIDI_DEVICES; + } + for (idx = 0; idx < snd_midi_devs[dev]; idx++) { + snd_rawmidi_t *rmidi; + snd_virmidi_dev_t *rdev; + if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0) + goto __nodev; + rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, continue); + vmidi->midi[idx] = rmidi; + strcpy(rmidi->name, "Virtual Raw MIDI"); + rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; + } + + strcpy(card->driver, "VirMIDI"); + strcpy(card->shortname, "VirMIDI"); + sprintf(card->longname, "Virtual MIDI Card %i", dev + 1); + if ((err = snd_card_register(card)) == 0) { + snd_virmidi_cards[dev] = card; + return 0; + } + __nodev: + snd_card_free(card); + return err; +} + +static int __init alsa_card_virmidi_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_card_virmidi_probe(dev) < 0) { +#ifdef MODULE + snd_printk("Card-VirMIDI #%i not found or device busy\n", dev + 1); +#endif + break; + } + cards++; + } + if (!cards) { +#ifdef MODULE + snd_printk("Card-VirMIDI soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_virmidi_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_virmidi_cards[dev]); +} + +module_init(alsa_card_virmidi_init) +module_exit(alsa_card_virmidi_exit) + +#ifndef MODULE + +/* format is: snd-virmidi=snd_enable,snd_index,snd_id,snd_midi_devs */ + +static int __init alsa_card_virmidi_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_midi_devs[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-virmidi=", alsa_card_virmidi_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/i2c/Makefile b/sound/i2c/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/i2c/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,29 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _i2c.o + +list-multi := snd-i2c.o snd-cs8427.o snd-tea6330t.o + +export-objs := i2c.o cs8427.o tea6330t.o + +snd-i2c-objs := i2c.o +snd-cs8427-objs := cs8427.o +snd-tea6330t-objs := tea6330t.o + +# Module Dependency +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o +obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o + +include $(TOPDIR)/Rules.make + +snd-i2c.o: $(snd-i2c-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-i2c-objs) + +snd-cs8427.o: $(snd-cs8427-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs8427-objs) + +snd-tea6330t.o: $(snd-tea6330t-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-tea6330t-objs) diff -Nru a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/i2c/cs8427.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,470 @@ +/* + * Routines for control of the CS8427 via i2c bus + * IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic"); +MODULE_LICENSE("GPL"); + +#define chip_t snd_i2c_device_t + +#define CS8427_ADDR (0x20>>1) /* fixed address */ + +typedef struct { + snd_pcm_substream_t *substream; + char hw_status[24]; /* hardware status */ + char def_status[24]; /* default status */ + char pcm_status[24]; /* PCM private status */ + char hw_udata[32]; + snd_kcontrol_t *pcm_ctl; +} cs8427_stream_t; + +typedef struct { + unsigned char regmap[0x14]; /* map of first 1 + 13 registers */ + cs8427_stream_t playback; + cs8427_stream_t capture; +} cs8427_t; + +static unsigned char swapbits(unsigned char val) +{ + int bit; + unsigned char res = 0; + for (bit = 0; bit < 8; bit++) { + res |= val & 1; + res <<= 1; + val >>= 1; + } + return res; +} + +int snd_cs8427_detect(snd_i2c_bus_t *bus, unsigned char addr) +{ + int res; + + snd_i2c_lock(bus); + res = snd_i2c_probeaddr(bus, CS8427_ADDR | (addr & 7)); + snd_i2c_unlock(bus); + return res; +} + +static int snd_cs8427_reg_write(snd_i2c_device_t *device, unsigned char reg, unsigned char val) +{ + int err; + unsigned char buf[2]; + + buf[0] = reg & 0x7f; + buf[1] = val; + if ((err = snd_i2c_sendbytes(device, buf, 2)) != 2) { + snd_printk("unable to send bytes 0x%02x:0x%02x to CS8427 (%i)\n", buf[0], buf[1], err); + return err < 0 ? err : -EREMOTE; + } + return 0; +} + +static int snd_cs8427_reg_read(snd_i2c_device_t *device, unsigned char reg) +{ + int err; + unsigned char buf; + + if ((err = snd_i2c_sendbytes(device, ®, 1)) != 1) { + snd_printk("unable to send register 0x%x byte to CS8427\n", reg); + return err < 0 ? err : -EREMOTE; + } + if ((err = snd_i2c_readbytes(device, &buf, 1)) != 1) { + snd_printk("unable to read register 0x%x byte from CS8427\n", reg); + return err < 0 ? err : -EREMOTE; + } + return buf; +} + +static int snd_cs8427_select_corudata(snd_i2c_device_t *device, int udata) +{ + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + int err; + + udata = udata ? CS8427_BSEL : 0; + if (udata != (chip->regmap[CS8427_REG_CSDATABUF] & udata)) { + chip->regmap[CS8427_REG_CSDATABUF] &= ~CS8427_BSEL; + chip->regmap[CS8427_REG_CSDATABUF] |= udata; + err = snd_cs8427_reg_write(device, CS8427_REG_CSDATABUF, chip->regmap[CS8427_REG_CSDATABUF]); + if (err < 0) + return err; + } + return 0; +} + +static int snd_cs8427_send_corudata(snd_i2c_device_t *device, + int udata, + unsigned char *ndata, + int count) +{ + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + char *hw_data = udata ? chip->playback.hw_udata : chip->playback.hw_status; + char data[32]; + int err, idx; + + if (!memcmp(hw_data, ndata, count)) + return 0; + if ((err = snd_cs8427_select_corudata(device, udata)) < 0) + return err; + memcpy(hw_data, data, count); + if (udata) { + memset(data, 0, sizeof(data)); + if (memcmp(hw_data, data, 32) == 0) { + chip->regmap[CS8427_REG_UDATABUF] &= ~CS8427_UBMMASK; + chip->regmap[CS8427_REG_UDATABUF] |= CS8427_UBMZEROS | CS8427_EFTUI; + if ((err = snd_cs8427_reg_write(device, CS8427_REG_UDATABUF, chip->regmap[CS8427_REG_UDATABUF])) < 0) + return err; + return 0; + } + } + data[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF; + for (idx = 0; idx < count; idx++) + data[idx + 1] = swapbits(ndata[idx]); + if (snd_i2c_sendbytes(device, data, count) != count) + return -EREMOTE; + return 1; +} + +static void snd_cs8427_free(snd_i2c_device_t *device) +{ + if (device->private_data) + snd_magic_kfree(device->private_data); +} + +int snd_cs8427_create(snd_i2c_bus_t *bus, + unsigned char addr, + snd_i2c_device_t **r_cs8427) +{ + static unsigned char initvals1[] = { + CS8427_REG_CONTROL1 | CS8427_REG_AUTOINC, + /* CS8427_REG_CLOCKSOURCE: RMCK to OMCK, no validity, disable mutes, TCBL=output */ + CS8427_SWCLK, + /* CS8427_REG_CONTROL2: hold last valid audio sample, RMCK=256*Fs, normal stereo operation */ + 0x00, + /* CS8427_REG_DATAFLOW: output drivers normal operation, Tx<=serial, Rx=>serial */ + CS8427_TXDSERIAL | CS8427_SPDAES3RECEIVER, + /* CS8427_REG_CLOCKSOURCE: Run off, CMCK=256*Fs, output time base = OMCK, input time base = + covered input clock, recovered input clock source is Envy24 */ + CS8427_INC, + /* CS8427_REG_SERIALINPUT: Serial audio input port data format = I2S, 24-bit, 64*Fsi */ + CS8427_SIDEL | CS8427_SILRPOL, + /* CS8427_REG_SERIALOUTPUT: Serial audio output port data format = I2S, 24-bit, 64*Fsi */ + CS8427_SODEL | CS8427_SOLRPOL, + }; + static unsigned char initvals2[] = { + CS8427_REG_RECVERRMASK | CS8427_REG_AUTOINC, + /* CS8427_REG_RECVERRMASK: unmask the input PLL clock, V, confidence, biphase, parity status bits */ + CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR, + /* CS8427_REG_CSDATABUF: + Registers 32-55 window to CS buffer + Inhibit D->E transfers from overwriting first 5 bytes of CS data. + Inhibit D->E transfers (all) of CS data. + Allow E->F transfer of CS data. + One byte mode; both A/B channels get same written CB data. + A channel info is output to chip's EMPH* pin. */ + CS8427_CBMR | CS8427_DETCI, + /* CS8427_REG_UDATABUF: + Use internal buffer to transmit User (U) data. + Chip's U pin is an output. + Transmit all O's for user data. + Inhibit D->E transfers. + Inhibit E->F transfers. */ + CS8427_UD | CS8427_EFTUI | CS8427_DETUI, + }; + int err; + cs8427_t *chip; + snd_i2c_device_t *device; + unsigned char buf[32 + 1]; + + if ((err = snd_i2c_device_create(bus, "CS8427", CS8427_ADDR | (addr & 7), &device)) < 0) + return err; + chip = device->private_data = snd_magic_kcalloc(cs8427_t, 0, GFP_KERNEL); + if (chip == NULL) { + snd_i2c_device_free(device); + return -ENOMEM; + } + device->private_free = snd_cs8427_free; + + snd_i2c_lock(bus); + if ((err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER)) != CS8427_VER8427A) { + snd_i2c_unlock(bus); + snd_printk("unable to find CS8427 signature (expected 0x%x, read 0x%x), initialization is not completed\n", CS8427_VER8427A, err); + return -EFAULT; + } + /* turn off run bit while making changes to configuration */ + if ((err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, 0x00)) < 0) + goto __fail; + /* send initial values */ + memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6); + if ((err = snd_i2c_sendbytes(device, initvals1, 7)) != 7) { + err = err < 0 ? err : -EREMOTE; + goto __fail; + } + /* Turn off CS8427 interrupt stuff that is not used in hardware */ + memset(buf, 0, 8); + /* from address 9 to 16 */ + buf[0] = 9; /* register */ + if ((err = snd_i2c_sendbytes(device, buf, 8)) != 8) + goto __fail; + /* send transfer initialization sequence */ + memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3); + if ((err = snd_i2c_sendbytes(device, initvals2, 4)) != 4) { + err = err < 0 ? err : -EREMOTE; + goto __fail; + } + /* write default channel status bytes */ + buf[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF; + buf[1] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 0)); + buf[2] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 8)); + buf[3] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 16)); + buf[4] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 24)); + memset(buf + 5, 0, sizeof(buf)-5); + memcpy(chip->playback.def_status, buf + 1, 24); + memcpy(chip->playback.pcm_status, buf + 1, 24); + if ((err = snd_i2c_sendbytes(device, buf, 33)) != 33) + goto __fail; + /* turn on run bit and rock'n'roll */ + chip->regmap[CS8427_REG_CLOCKSOURCE] = initvals1[4] | CS8427_RUN; + if ((err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, chip->regmap[CS8427_REG_CLOCKSOURCE])) < 0) + goto __fail; + +#if 0 // it's nice for read tests + { + char buf[128]; + buf[0] = 0x81; + snd_i2c_sendbytes(device, buf, 1); + snd_i2c_readbytes(device, buf, 127); + } +#endif + + snd_i2c_unlock(bus); + if (r_cs8427) + *r_cs8427 = device; + return 0; + + __fail: + snd_i2c_unlock(bus); + snd_i2c_device_free(device); + return err < 0 ? err : -EREMOTE; +} + +static int snd_cs8427_in_status_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_cs8427_in_status_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); + int data; + + snd_i2c_lock(device->bus); + data = snd_cs8427_reg_read(device, 15); + snd_i2c_unlock(device->bus); + if (data < 0) + return data; + ucontrol->value.integer.value[0] = data; + return 0; +} + +static int snd_cs8427_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cs8427_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + + snd_i2c_lock(device->bus); + memcpy(ucontrol->value.iec958.status, chip->playback.def_status, 23); + snd_i2c_unlock(device->bus); + return 0; +} + +static int snd_cs8427_spdif_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + unsigned char *status = kcontrol->private_value ? chip->playback.pcm_status : chip->playback.def_status; + int err, change; + + snd_i2c_lock(device->bus); + change = memcmp(ucontrol->value.iec958.status, status, 23) != 0; + memcpy(status, ucontrol->value.iec958.status, 23); + if (change && (kcontrol->private_value ? chip->playback.substream != NULL : chip->playback.substream == NULL)) { + err = snd_cs8427_send_corudata(device, 0, status, 23); + if (err < 0) + change = err; + } + snd_i2c_unlock(device->bus); + return change; +} + +static int snd_cs8427_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cs8427_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + memset(ucontrol->value.iec958.status, 0xff, 23); + return 0; +} + +#define CONTROLS (sizeof(snd_cs8427_iec958_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs8427_iec958_controls[] = { +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + info: snd_cs8427_in_status_info, + name: "IEC958 CS8427 Input Status", + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + get: snd_cs8427_in_status_get, +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + info: snd_cs8427_spdif_mask_info, + get: snd_cs8427_spdif_mask_get, +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_cs8427_spdif_info, + get: snd_cs8427_spdif_get, + put: snd_cs8427_spdif_put, + private_value: 0 +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_cs8427_spdif_info, + get: snd_cs8427_spdif_get, + put: snd_cs8427_spdif_put, + private_value: 1 +}}; + +int snd_cs8427_iec958_build(snd_i2c_device_t *cs8427, + snd_pcm_substream_t *play_substream, + snd_pcm_substream_t *cap_substream) +{ + cs8427_t *chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + snd_kcontrol_t *kctl; + int idx, err; + + snd_assert(play_substream && cap_substream, return -EINVAL); + for (idx = 0; idx < CONTROLS; idx++) { + kctl = snd_ctl_new1(&snd_cs8427_iec958_controls[idx], cs8427); + if (kctl == NULL) + return -ENOMEM; + kctl->id.device = play_substream->pcm->device; + kctl->id.subdevice = play_substream->number; + err = snd_ctl_add(cs8427->bus->card, kctl); + if (err < 0) + return err; + if (!strcmp(kctl->id.name, SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM))) + chip->playback.pcm_ctl = kctl; + } + + snd_assert(chip->playback.pcm_ctl, return -EIO); + return 0; +} + +int snd_cs8427_iec958_active(snd_i2c_device_t *cs8427, int active) +{ + cs8427_t *chip; + + snd_assert(cs8427, return -ENXIO); + chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + if (active) + memcpy(chip->playback.pcm_status, chip->playback.def_status, 24); + chip->playback.pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(cs8427->bus->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->playback.pcm_ctl->id); + return 0; +} + +int snd_cs8427_iec958_pcm(snd_i2c_device_t *cs8427, unsigned int rate) +{ + cs8427_t *chip; + char *status; + int err; + + snd_assert(cs8427, return -ENXIO); + chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + status = chip->playback.pcm_status; + snd_i2c_lock(cs8427->bus); + if (status[0] & IEC958_AES0_PROFESSIONAL) { + status[0] &= ~IEC958_AES0_PRO_FS; + switch (rate) { + case 32000: status[0] |= IEC958_AES0_PRO_FS_32000; break; + case 44100: status[0] |= IEC958_AES0_PRO_FS_44100; break; + case 48000: status[0] |= IEC958_AES0_PRO_FS_48000; break; + default: status[0] |= IEC958_AES0_PRO_FS_NOTID; break; + } + } else { + status[3] &= ~IEC958_AES3_CON_FS; + switch (rate) { + case 32000: status[3] |= IEC958_AES3_CON_FS_32000; break; + case 44100: status[3] |= IEC958_AES3_CON_FS_44100; break; + case 48000: status[3] |= IEC958_AES3_CON_FS_48000; break; + } + } + err = snd_cs8427_send_corudata(cs8427, 0, status, 23); + if (err > 0) + snd_ctl_notify(cs8427->bus->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->playback.pcm_ctl->id); + snd_i2c_unlock(cs8427->bus); + return err < 0 ? err : 0; +} + +EXPORT_SYMBOL(snd_cs8427_detect); +EXPORT_SYMBOL(snd_cs8427_create); +EXPORT_SYMBOL(snd_cs8427_iec958_build); +EXPORT_SYMBOL(snd_cs8427_iec958_active); +EXPORT_SYMBOL(snd_cs8427_iec958_pcm); diff -Nru a/sound/i2c/i2c.c b/sound/i2c/i2c.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/i2c/i2c.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,324 @@ +/* + * Generic i2c interface for ALSA + * + * (c) 1998 Gerd Knorr + * Modified for the ALSA driver by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Generic i2c interface for ALSA"); +MODULE_LICENSE("GPL"); + +static int snd_i2c_bit_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +static int snd_i2c_bit_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr); + +static snd_i2c_ops_t snd_i2c_bit_ops = { + sendbytes: snd_i2c_bit_sendbytes, + readbytes: snd_i2c_bit_readbytes, + probeaddr: snd_i2c_bit_probeaddr, +}; + +static int snd_i2c_bus_free(snd_i2c_bus_t *bus) +{ + snd_i2c_bus_t *slave; + snd_i2c_device_t *device; + + snd_assert(bus != NULL, return -EINVAL); + while (!list_empty(&bus->devices)) { + device = snd_i2c_device(bus->devices.next); + snd_i2c_device_free(device); + } + if (bus->master) + list_del(&bus->buses); + else { + while (!list_empty(&bus->buses)) { + slave = snd_i2c_slave_bus(bus->buses.next); + snd_device_free(bus->card, slave); + } + } + if (bus->private_free) + bus->private_free(bus); + snd_magic_kfree(bus); + return 0; +} + +static int snd_i2c_bus_dev_free(snd_device_t *device) +{ + snd_i2c_bus_t *bus = snd_magic_cast(snd_i2c_bus_t, device->device_data, return -ENXIO); + return snd_i2c_bus_free(bus); +} + +int snd_i2c_bus_create(snd_card_t *card, const char *name, snd_i2c_bus_t *master, snd_i2c_bus_t **ri2c) +{ + snd_i2c_bus_t *bus; + int err; + static snd_device_ops_t ops = { + dev_free: snd_i2c_bus_dev_free, + }; + + *ri2c = NULL; + bus = (snd_i2c_bus_t *)snd_magic_kcalloc(snd_i2c_bus_t, 0, GFP_KERNEL); + if (bus == NULL) + return -ENOMEM; + spin_lock_init(&bus->lock); + INIT_LIST_HEAD(&bus->devices); + INIT_LIST_HEAD(&bus->buses); + bus->card = card; + bus->ops = &snd_i2c_bit_ops; + if (master) { + list_add_tail(&bus->buses, &master->buses); + bus->master = master; + } + strncpy(bus->name, name, sizeof(bus->name) - 1); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, bus, &ops)) < 0) { + snd_i2c_bus_free(bus); + return err; + } + *ri2c = bus; + return 0; +} + +int snd_i2c_device_create(snd_i2c_bus_t *bus, const char *name, unsigned char addr, snd_i2c_device_t **rdevice) +{ + snd_i2c_device_t *device; + + *rdevice = NULL; + snd_assert(bus != NULL, return -EINVAL); + device = (snd_i2c_device_t *)snd_magic_kcalloc(snd_i2c_device_t, 0, GFP_KERNEL); + if (device == NULL) + return -ENOMEM; + device->addr = addr; + strncpy(device->name, name, sizeof(device->name)-1); + list_add_tail(&device->list, &bus->devices); + device->bus = bus; + *rdevice = device; + return 0; +} + +int snd_i2c_device_free(snd_i2c_device_t *device) +{ + if (device->bus) + list_del(&device->list); + if (device->private_free) + device->private_free(device); + snd_magic_kfree(device); + return 0; +} + +int snd_i2c_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + return device->bus->ops->sendbytes(device, bytes, count); +} + + +int snd_i2c_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + return device->bus->ops->readbytes(device, bytes, count); +} + +int snd_i2c_probeaddr(snd_i2c_bus_t *bus, unsigned short addr) +{ + return bus->ops->probeaddr(bus, addr); +} + +/* + * bit-operations + */ + +static inline void snd_i2c_bit_hw_start(snd_i2c_bus_t *bus) +{ + if (bus->hw_ops.bit->start) + bus->hw_ops.bit->start(bus); +} + +static inline void snd_i2c_bit_hw_stop(snd_i2c_bus_t *bus) +{ + if (bus->hw_ops.bit->stop) + bus->hw_ops.bit->stop(bus); +} + +static void snd_i2c_bit_direction(snd_i2c_bus_t *bus, int clock, int data) +{ + if (bus->hw_ops.bit->direction) + bus->hw_ops.bit->direction(bus, clock, data); +} + +static void snd_i2c_bit_set(snd_i2c_bus_t *bus, int clock, int data) +{ + bus->hw_ops.bit->setlines(bus, clock, data); +} + +#if 0 +static int snd_i2c_bit_clock(snd_i2c_bus_t *bus) +{ + if (bus->hw_ops.bit->getclock) + return bus->hw_ops.bit->getclock(bus); + return -ENXIO; +} +#endif + +static int snd_i2c_bit_data(snd_i2c_bus_t *bus, int ack) +{ + return bus->hw_ops.bit->getdata(bus, ack); +} + +static void snd_i2c_bit_start(snd_i2c_bus_t *bus) +{ + snd_i2c_bit_hw_start(bus); + snd_i2c_bit_direction(bus, 1, 1); /* SCL - wr, SDA - wr */ + snd_i2c_bit_set(bus, 1, 1); + snd_i2c_bit_set(bus, 1, 0); + snd_i2c_bit_set(bus, 0, 0); +} + +static void snd_i2c_bit_stop(snd_i2c_bus_t *bus) +{ + snd_i2c_bit_set(bus, 0, 0); + snd_i2c_bit_set(bus, 1, 0); + snd_i2c_bit_set(bus, 1, 1); + snd_i2c_bit_hw_stop(bus); +} + +static void snd_i2c_bit_send(snd_i2c_bus_t *bus, int data) +{ + snd_i2c_bit_set(bus, 0, data); + snd_i2c_bit_set(bus, 1, data); + snd_i2c_bit_set(bus, 0, data); +} + +static int snd_i2c_bit_ack(snd_i2c_bus_t *bus) +{ + int ack; + + snd_i2c_bit_set(bus, 0, 1); + snd_i2c_bit_set(bus, 1, 1); + snd_i2c_bit_direction(bus, 1, 0); /* SCL - wr, SDA - rd */ + ack = snd_i2c_bit_data(bus, 1); + snd_i2c_bit_direction(bus, 1, 1); /* SCL - wr, SDA - wr */ + snd_i2c_bit_set(bus, 0, 1); + return ack ? -EREMOTEIO : 0; +} + +static int snd_i2c_bit_sendbyte(snd_i2c_bus_t *bus, unsigned char data) +{ + int i, err; + + for (i = 7; i >= 0; i--) + snd_i2c_bit_send(bus, !!(data & (1 << i))); + if ((err = snd_i2c_bit_ack(bus)) < 0) + return err; + return 0; +} + +static int snd_i2c_bit_readbyte(snd_i2c_bus_t *bus, int last) +{ + int i; + unsigned char data = 0; + + snd_i2c_bit_set(bus, 0, 1); + snd_i2c_bit_direction(bus, 1, 0); /* SCL - wr, SDA - rd */ + for (i = 7; i >= 0; i--) { + snd_i2c_bit_set(bus, 1, 1); + if (snd_i2c_bit_data(bus, 0)) + data |= (1 << i); + snd_i2c_bit_set(bus, 0, 1); + } + snd_i2c_bit_direction(bus, 1, 1); /* SCL - wr, SDA - wr */ + snd_i2c_bit_send(bus, !!last); + return data; +} + +static int snd_i2c_bit_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + snd_i2c_bus_t *bus = device->bus; + int err, res = 0; + + if (device->flags & SND_I2C_DEVICE_ADDRTEN) + return -EIO; /* not yet implemented */ + snd_i2c_bit_start(bus); + if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0) + return err; + while (count-- > 0) { + if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0) + return err; + res++; + } + snd_i2c_bit_stop(bus); + return res; +} + +static int snd_i2c_bit_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + snd_i2c_bus_t *bus = device->bus; + int err, res = 0; + + if (device->flags & SND_I2C_DEVICE_ADDRTEN) + return -EIO; /* not yet implemented */ + snd_i2c_bit_start(bus); + if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0) + return err; + while (count-- > 0) { + if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0) + return err; + *bytes++ = (unsigned char)err; + res++; + } + snd_i2c_bit_stop(bus); + return res; +} + +static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr) +{ + int err; + + if (addr & 0x8000) /* 10-bit address */ + return -EIO; /* not yet implemented */ + if (addr & 0x7f80) /* invalid address */ + return -EINVAL; + snd_i2c_bit_start(bus); + if ((err = snd_i2c_bit_sendbyte(bus, addr << 1)) < 0) + return err; + snd_i2c_bit_stop(bus); + return 1; /* present */ +} + +EXPORT_SYMBOL(snd_i2c_bus_create); +EXPORT_SYMBOL(snd_i2c_device_create); +EXPORT_SYMBOL(snd_i2c_device_free); +EXPORT_SYMBOL(snd_i2c_sendbytes); +EXPORT_SYMBOL(snd_i2c_readbytes); +EXPORT_SYMBOL(snd_i2c_probeaddr); + +static int __init alsa_i2c_init(void) +{ + return 0; +} + +static void __exit alsa_i2c_exit(void) +{ +} + +module_init(alsa_i2c_init) +module_exit(alsa_i2c_exit) diff -Nru a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/i2c/tea6330t.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,372 @@ +/* + * Routines for control of the TEA6330T circuit via i2c bus + * Sound fader control circuit for car radios by Philips Semiconductors + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of the TEA6330T circuit via i2c bus"); +MODULE_LICENSE("GPL"); + +#define chip_t tea6330t_t + +#define TEA6330T_ADDR (0x80>>1) /* fixed address */ + +#define TEA6330T_SADDR_VOLUME_LEFT 0x00 /* volume left */ +#define TEA6330T_SADDR_VOLUME_RIGHT 0x01 /* volume right */ +#define TEA6330T_SADDR_BASS 0x02 /* bass control */ +#define TEA6330T_SADDR_TREBLE 0x03 /* treble control */ +#define TEA6330T_SADDR_FADER 0x04 /* fader control */ +#define TEA6330T_MFN 0x20 /* mute control for selected channels */ +#define TEA6330T_FCH 0x10 /* select fader channels - front or rear */ +#define TEA6330T_SADDR_AUDIO_SWITCH 0x05 /* audio switch */ +#define TEA6330T_GMU 0x80 /* mute control, general mute */ +#define TEA6330T_EQN 0x40 /* equalizer switchover (0=equalizer-on) */ + +int snd_tea6330t_detect(snd_i2c_bus_t *bus, int equalizer) +{ + int res; + + snd_i2c_lock(bus); + res = snd_i2c_probeaddr(bus, TEA6330T_ADDR); + snd_i2c_unlock(bus); + return res; +} + +#if 0 +static void snd_tea6330t_set(tea6330t_t *tea, + unsigned char addr, unsigned char value) +{ +#if 0 + printk("set - 0x%x/0x%x\n", addr, value); +#endif + snd_i2c_write(tea->bus, TEA6330T_ADDR, addr, value, 1); +} +#endif + +#define TEA6330T_MASTER_VOLUME(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_tea6330t_info_master_volume, \ + get: snd_tea6330t_get_master_volume, put: snd_tea6330t_put_master_volume } + +static int snd_tea6330t_info_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 43; + return 0; +} + +static int snd_tea6330t_get_master_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + snd_i2c_lock(tea->bus); + ucontrol->value.integer.value[0] = tea->mleft - 0x14; + ucontrol->value.integer.value[1] = tea->mright - 0x14; + snd_i2c_unlock(tea->bus); + return 0; +} + +static int snd_tea6330t_put_master_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, count, err; + unsigned char bytes[3]; + unsigned char val1, val2; + + val1 = (ucontrol->value.integer.value[0] % 44) + 0x14; + val2 = (ucontrol->value.integer.value[1] % 44) + 0x14; + snd_i2c_lock(tea->bus); + change = val1 != tea->mleft || val2 != tea->mright; + tea->mleft = val1; + tea->mright = val2; + count = 0; + if (tea->regs[TEA6330T_SADDR_VOLUME_LEFT] != 0) { + bytes[count++] = TEA6330T_SADDR_VOLUME_LEFT; + bytes[count++] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] = tea->mleft; + } + if (tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] != 0) { + if (count == 0) + bytes[count++] = TEA6330T_SADDR_VOLUME_RIGHT; + bytes[count++] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] = tea->mright; + } + if (count > 0) { + if ((err = snd_i2c_sendbytes(tea->device, bytes, count)) < 0) + change = err; + } + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_MASTER_SWITCH(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_tea6330t_info_master_switch, \ + get: snd_tea6330t_get_master_switch, put: snd_tea6330t_put_master_switch } + +static int snd_tea6330t_info_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_tea6330t_get_master_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + snd_i2c_lock(tea->bus); + ucontrol->value.integer.value[0] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] == 0 ? 0 : 1; + ucontrol->value.integer.value[1] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] == 0 ? 0 : 1; + snd_i2c_unlock(tea->bus); + return 0; +} + +static int snd_tea6330t_put_master_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, err; + unsigned char bytes[3]; + unsigned char oval1, oval2, val1, val2; + + val1 = ucontrol->value.integer.value[0] & 1; + val2 = ucontrol->value.integer.value[1] & 1; + snd_i2c_lock(tea->bus); + oval1 = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] == 0 ? 0 : 1; + oval2 = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] == 0 ? 0 : 1; + change = val1 != oval1 || val2 != oval2; + tea->regs[TEA6330T_SADDR_VOLUME_LEFT] = val1 ? tea->mleft : 0; + tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] = val2 ? tea->mright : 0; + bytes[0] = TEA6330T_SADDR_VOLUME_LEFT; + bytes[1] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT]; + bytes[2] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT]; + if ((err = snd_i2c_sendbytes(tea->device, bytes, 3)) < 0) + change = err; + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_BASS(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_tea6330t_info_bass, \ + get: snd_tea6330t_get_bass, put: snd_tea6330t_put_bass } + +static int snd_tea6330t_info_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tea->max_bass; + return 0; +} + +static int snd_tea6330t_get_bass(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tea->bass; + return 0; +} + +static int snd_tea6330t_put_bass(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, err; + unsigned char bytes[2]; + unsigned char val1; + + val1 = ucontrol->value.integer.value[0] % (tea->max_bass + 1); + snd_i2c_lock(tea->bus); + tea->bass = val1; + val1 += tea->equalizer ? 7 : 3; + change = tea->regs[TEA6330T_SADDR_BASS] != val1; + bytes[0] = TEA6330T_SADDR_BASS; + bytes[1] = tea->regs[TEA6330T_SADDR_BASS] = val1; + if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0) + change = err; + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_TREBLE(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_tea6330t_info_treble, \ + get: snd_tea6330t_get_treble, put: snd_tea6330t_put_treble } + +static int snd_tea6330t_info_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tea->max_treble; + return 0; +} + +static int snd_tea6330t_get_treble(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tea->treble; + return 0; +} + +static int snd_tea6330t_put_treble(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, err; + unsigned char bytes[2]; + unsigned char val1; + + val1 = ucontrol->value.integer.value[0] % (tea->max_treble + 1); + snd_i2c_lock(tea->bus); + tea->treble = val1; + val1 += 3; + change = tea->regs[TEA6330T_SADDR_TREBLE] != val1; + bytes[0] = TEA6330T_SADDR_TREBLE; + bytes[1] = tea->regs[TEA6330T_SADDR_TREBLE] = val1; + if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0) + change = err; + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_CONTROLS (sizeof(snd_tea6330t_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_tea6330t_controls[] = { +TEA6330T_MASTER_SWITCH("Master Playback Switch", 0), +TEA6330T_MASTER_VOLUME("Master Playback Volume", 0), +TEA6330T_BASS("Tone Control - Bass", 0), +TEA6330T_TREBLE("Tone Control - Treble", 0) +}; + +static void snd_tea6330_free(snd_i2c_device_t *device) +{ + tea6330t_t *tea = snd_magic_cast(tea6330t_t, device->private_data, return); + snd_magic_kfree(tea); +} + +int snd_tea6330t_update_mixer(snd_card_t * card, + snd_i2c_bus_t *bus, + int equalizer, int fader) +{ + snd_i2c_device_t *device; + tea6330t_t *tea; + snd_kcontrol_new_t *knew; + int idx, err = -ENOMEM; + u8 default_treble, default_bass; + unsigned char bytes[7]; + + tea = snd_magic_kcalloc(tea6330t_t, 0, GFP_KERNEL); + if (tea == NULL) + return -ENOMEM; + if ((err = snd_i2c_device_create(bus, "TEA6330T", TEA6330T_ADDR, &device)) < 0) { + snd_magic_kfree(tea); + return err; + } + tea->device = device; + tea->bus = bus; + tea->equalizer = equalizer; + tea->fader = fader; + device->private_data = tea; + device->private_free = snd_tea6330_free; + + snd_i2c_lock(bus); + + /* turn fader off and handle equalizer */ + tea->regs[TEA6330T_SADDR_FADER] = 0x3f; + tea->regs[TEA6330T_SADDR_AUDIO_SWITCH] = equalizer ? 0 : TEA6330T_EQN; + /* initialize mixer */ + if (!tea->equalizer) { + tea->max_bass = 9; + tea->max_treble = 8; + default_bass = 3 + 4; + tea->bass = 4; + default_treble = 3 + 4; + tea->treble = 4; + } else { + tea->max_bass = 5; + tea->max_treble = 0; + default_bass = 7 + 4; + tea->bass = 4; + default_treble = 3; + tea->treble = 0; + } + tea->mleft = tea->mright = 0x14; + tea->regs[TEA6330T_SADDR_BASS] = default_bass; + tea->regs[TEA6330T_SADDR_TREBLE] = default_treble; + + /* compose I2C message and put the hardware to initial state */ + bytes[0] = TEA6330T_SADDR_VOLUME_LEFT; + for (idx = 0; idx < 6; idx++) + bytes[idx+1] = tea->regs[idx]; + if ((err = snd_i2c_sendbytes(device, bytes, 7)) < 0) + goto __error; + + strcat(card->mixername, ",TEA6330T"); + if ((err = snd_component_add(card, "TEA6330T")) < 0) + goto __error; + + for (idx = 0; idx < TEA6330T_CONTROLS; idx++) { + knew = &snd_tea6330t_controls[idx]; + if (tea->treble == 0 && !strcmp(knew->name, "Tone Control - Treble")) + continue; + if ((err = snd_ctl_add(card, snd_ctl_new1(knew, tea))) < 0) + goto __error; + } + + snd_i2c_unlock(bus); + return 0; + + __error: + snd_i2c_unlock(bus); + snd_i2c_device_free(device); + return err; +} + +EXPORT_SYMBOL(snd_tea6330t_detect); +EXPORT_SYMBOL(snd_tea6330t_update_mixer); + +/* + * INIT part + */ + +static int __init alsa_tea6330t_init(void) +{ + return 0; +} + +static void __exit alsa_tea6330t_exit(void) +{ +} + +module_init(alsa_tea6330t_init) +module_exit(alsa_tea6330t_exit) diff -Nru a/sound/isa/Config.in b/sound/isa/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/Config.in Tue Feb 19 18:09:00 2002 @@ -0,0 +1,36 @@ +# ALSA ISA drivers + +mainmenu_option next_comment +comment 'ISA devices' + +dep_tristate 'Analog Devices SoundPort AD1816A' CONFIG_SND_AD1816A $CONFIG_SND +dep_tristate 'Generic AD1848/CS4248 driver' CONFIG_SND_AD1848 $CONFIG_SND +dep_tristate 'Generic Cirrus Logic CS4231 driver' CONFIG_SND_CS4231 $CONFIG_SND +dep_tristate 'Generic Cirrus Logic CS4232 driver' CONFIG_SND_CS4232 $CONFIG_SND +dep_tristate 'Generic Cirrus Logic CS4236+ driver' CONFIG_SND_CS4236 $CONFIG_SND +dep_tristate 'Generic ESS ES968 driver' CONFIG_SND_ES968 $CONFIG_SND +dep_tristate 'Generic ESS ES1688 driver' CONFIG_SND_ES1688 $CONFIG_SND +dep_tristate 'Generic ESS ES18xx driver' CONFIG_SND_ES18XX $CONFIG_SND +dep_tristate 'Gravis UltraSound Classic' CONFIG_SND_GUSCLASSIC $CONFIG_SND +dep_tristate 'Gravis UltraSound Extreme' CONFIG_SND_GUSEXTREME $CONFIG_SND +dep_tristate 'Gravis UltraSound MAX' CONFIG_SND_GUSMAX $CONFIG_SND +dep_tristate 'AMD InterWave, Gravis UltraSound PnP' CONFIG_SND_INTERWAVE $CONFIG_SND +dep_tristate 'AMD InterWave + TEA6330T (UltraSound 32-Pro)' CONFIG_SND_INTERWAVE_STB $CONFIG_SND +dep_tristate 'OPTi 82C92x - AD1848' CONFIG_SND_OPTI92X_AD1848 $CONFIG_SND +dep_tristate 'OPTi 82C92x - CS4231' CONFIG_SND_OPTI92X_CS4231 $CONFIG_SND +dep_tristate 'OPTi 82C93x' CONFIG_SND_OPTI93X $CONFIG_SND +dep_tristate 'Sound Blaster 1.0/2.0/Pro (8-bit)' CONFIG_SND_SB8 $CONFIG_SND +dep_tristate 'Sound Blaster 16 (PnP)' CONFIG_SND_SB16 $CONFIG_SND +dep_tristate 'Sound Blaster AWE (32,64) (PnP)' CONFIG_SND_SBAWE $CONFIG_SND +if [ "$CONFIG_SND_SB16" != "n" -o "$CONFIG_SND_SBAWE" != "n" ]; then + bool ' Sound Blaster 16/AWE CSP support' CONFIG_SND_SB16_CSP +fi +dep_tristate 'Turtle Beach Maui,Tropez,Tropez+ (Wavefront)' CONFIG_SND_WAVEFRONT $CONFIG_SND +dep_tristate 'Avance Logic ALS100/ALS120' CONFIG_SND_ALS100 $CONFIG_SND +dep_tristate 'Aztech Systems AZT2320' CONFIG_SND_AZT2320 $CONFIG_SND +dep_tristate 'C-Media CMI8330' CONFIG_SND_CMI8330 $CONFIG_SND +dep_tristate 'Diamond Technologies DT-0197H' CONFIG_SND_DT0197H $CONFIG_SND +dep_tristate 'Yamaha OPL3-SA2/SA3' CONFIG_SND_OPL3SA2 $CONFIG_SND +dep_tristate 'Aztech Sound Galaxy' CONFIG_SND_SGALAXY $CONFIG_SND + +endmenu diff -Nru a/sound/isa/Makefile b/sound/isa/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,52 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := isa.o + +subdir-y := ad1816a ad1848 cs423x es1688 gus opti9xx sb wavefront +subdir-m := $(subdir-y) + +list-multi := snd-als100.o snd-azt2320.o snd-cmi8330.o snd-dt0197h.o \ + snd-es18xx.o snd-opl3sa2.o snd-sgalaxy.o + +snd-als100-objs := als100.o +snd-azt2320-objs := azt2320.o +snd-cmi8330-objs := cmi8330.o +snd-dt0197h-objs := dt0197h.o +snd-es18xx-objs := es18xx.o +snd-opl3sa2-objs := opl3sa2.o +snd-sgalaxy-objs := sgalaxy.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-als100.o +obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o +obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o +obj-$(CONFIG_SND_DT0197H) += snd-dt0197h.o +obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o +obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o +obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o + +include $(TOPDIR)/Rules.make + +snd-als100.o: $(snd-als100-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-als100-objs) + +snd-azt2320.o: $(snd-azt2320-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-azt2320-objs) + +snd-cmi8330.o: $(snd-cmi8330-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cmi8330-objs) + +snd-dt0197h.o: $(snd-dt0197h-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-dt0197h-objs) + +snd-es18xx.o: $(snd-es18xx-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es18xx-objs) + +snd-opl3sa2.o: $(snd-opl3sa2-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3sa2-objs) + +snd-sgalaxy.o: $(snd-sgalaxy-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sgalaxy-objs) diff -Nru a/sound/isa/ad1816a/Makefile b/sound/isa/ad1816a/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/ad1816a/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,24 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ad1816a.o + +list-multi := snd-ad1816a-lib.o snd-ad1816a.o + +export-objs := ad1816a_lib.o + +snd-ad1816a-lib-objs := ad1816a_lib.o +snd-ad1816a-objs := ad1816a.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_AD1816A) += snd-ad1816a.o snd-ad1816a-lib.o + +include $(TOPDIR)/Rules.make + +snd-ad1816a-lib.o: $(snd-ad1816a-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1816a-lib-objs) + +snd-ad1816a.o: $(snd-ad1816a-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1816a-objs) diff -Nru a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/ad1816a/ad1816a.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,397 @@ + +/* + card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards. + Copyright (C) 2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t ad1816a_t + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("AD1816A, AD1815"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Highscreen,Sound-Boostar 16 3D}," + "{Analog Devices,AD1815}," + "{Analog Devices,AD1816A}," + "{TerraTec,Base 64}," + "{TerraTec,AudioSystem EWS64S}," + "{Aztech/Newcom SC-16 3D}," + "{Shark Predator ISA}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ad1816a based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ad1816a based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ad1816a based soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "1st DMA # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "2nd DMA # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); + +struct snd_card_ad1816a { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_ad1816a_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_ad1816a_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_ad1816a_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_AD1816A(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \ + ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \ + } + +static struct isapnp_card_id snd_ad1816a_pnpids[] __devinitdata = { + /* Highscreen Sound-Boostar 16 3D */ + ISAPNP_AD1816A('M','D','K',0x1605,'A','D','S',0x7180,0x7181), + /* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */ + ISAPNP_AD1816A('L','W','C',0x1061,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1815 */ + ISAPNP_AD1816A('A','D','S',0x7150,'A','D','S',0x7150,0x7151), + /* Analog Devices AD1816A - added by Kenneth Platz */ + ISAPNP_AD1816A('A','D','S',0x7181,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1816A - Terratec Base 64 */ + ISAPNP_AD1816A('T','E','R',0x1411,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1816A - Terratec AudioSystem EWS64S */ + ISAPNP_AD1816A('T','E','R',0x1112,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */ + ISAPNP_AD1816A('A','Z','T',0x1022,'A','Z','T',0x1018,0x2002), + /* Shark Predator ISA - added by Ken Arromdee */ + ISAPNP_AD1816A('S','M','M',0x7180,'A','D','S',0x7180,0x7181), + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_ad1816a_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-ad1816a" + + +#ifdef __ISAPNP__ +static int __init snd_card_ad1816a_isapnp(int dev, + struct snd_card_ad1816a *acard) +{ + const struct isapnp_card_id *id = snd_ad1816a_isapnp_id[dev]; + struct isapnp_card *card = snd_ad1816a_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_port[dev], 16); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], + 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], + 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + + if (pdev->activate(pdev) < 0) { + snd_printk("AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + snd_port[dev] = pdev->resource[2].start; + snd_fm_port[dev] = pdev->resource[1].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev == NULL || pdev->prepare(pdev) < 0) { + snd_mpu_port[dev] = -1; + acard->devmpu = NULL; + return 0; + } + + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], + 2); + if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], + 1); + + if (pdev->activate(pdev) < 0) { + /* not fatal error */ + snd_printk("MPU-401 isapnp configure failure\n"); + snd_mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + snd_mpu_port[dev] = pdev->resource[0].start; + snd_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + return 0; +} + +static void snd_card_ad1816a_deactivate(struct snd_card_ad1816a *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } + if (acard->devmpu) { + acard->devmpu->deactivate(acard->devmpu); + acard->devmpu = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_card_ad1816a_free(snd_card_t *card) +{ + struct snd_card_ad1816a *acard = (struct snd_card_ad1816a *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_ad1816a_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_ad1816a_probe(int dev) +{ + int error; + snd_card_t *card; + struct snd_card_ad1816a *acard; + ad1816a_t *chip; + opl3_t *opl3; + + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_ad1816a))) == NULL) + return -ENOMEM; + acard = (struct snd_card_ad1816a *)card->private_data; + card->private_free = snd_card_ad1816a_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_ad1816a_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + snd_printk("you have to enable ISA PnP support.\n"); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_ad1816a_create(card, snd_port[dev], + snd_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_ad1816a_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_ad1816a_mixer(chip)) < 0) { + snd_card_free(card); + return error; + } + + if (snd_mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + snd_mpu_port[dev], 0, snd_mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + snd_printk("no MPU-401 device at 0x%lx.\n", snd_mpu_port[dev]); + } + + if (snd_fm_port[dev] > 0) { + if (snd_opl3_create(card, + snd_fm_port[dev], snd_fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + snd_printk("no OPL device at 0x%lx-0x%lx.\n", snd_fm_port[dev], snd_fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "AD1816A"); + strcpy(card->shortname, "ADI SoundPort AD1816A"); + sprintf(card->longname, "%s soundcard, SS at 0x%lx, irq %d, dma %d&%d", + card->shortname, chip->port, snd_irq[dev], snd_dma1[dev], snd_dma2[dev]); + + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_ad1816a_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_ad1816a_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_ad1816a_isapnp_cards[dev] = card; + snd_ad1816a_isapnp_id[dev] = id; + res = snd_card_ad1816a_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif + +static int __init alsa_card_ad1816a_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_ad1816a_pnpids, snd_ad1816a_isapnp_detect); +#else + snd_printk("you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + snd_printk("no AD1816A based soundcards found.\n"); +#endif /* MODULE */ + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_ad1816a_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_ad1816a_cards[dev]); +} + +module_init(alsa_card_ad1816a_init) +module_exit(alsa_card_ad1816a_exit) + +#ifndef MODULE + +/* format is: snd-ad1816a=snd_enable,snd_index,snd_id,snd_port, + snd_mpu_port,snd_fm_port,snd_irq,snd_mpu_irq, + snd_dma1,snd_dma2 */ + +static int __init alsa_card_ad1816a_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ad1816a=", alsa_card_ad1816a_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/ad1816a/ad1816a_lib.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,964 @@ + +/* + ad1816a.c - lowlevel code for Analog Devices AD1816A chip. + Copyright (C) 1999-2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("lowlevel code for Analog Devices AD1816A chip"); +MODULE_LICENSE("GPL"); + +#define chip_t ad1816a_t + +static inline int snd_ad1816a_busy_wait(ad1816a_t *chip) +{ + int timeout; + + for (timeout = 1000; timeout-- > 0; udelay(10)) + if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY) + return 0; + + snd_printk("chip busy.\n"); + return -EBUSY; +} + +inline unsigned char snd_ad1816a_in(ad1816a_t *chip, unsigned char reg) +{ + snd_ad1816a_busy_wait(chip); + return inb(AD1816A_REG(reg)); +} + +inline void snd_ad1816a_out(ad1816a_t *chip, unsigned char reg, + unsigned char value) +{ + snd_ad1816a_busy_wait(chip); + outb(value, AD1816A_REG(reg)); +} + +inline void snd_ad1816a_out_mask(ad1816a_t *chip, unsigned char reg, + unsigned char mask, unsigned char value) +{ + snd_ad1816a_out(chip, reg, + (value & mask) | (snd_ad1816a_in(chip, reg) & ~mask)); +} + +static unsigned short snd_ad1816a_read(ad1816a_t *chip, unsigned char reg) +{ + snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); + return snd_ad1816a_in(chip, AD1816A_INDIR_DATA_LOW) | + (snd_ad1816a_in(chip, AD1816A_INDIR_DATA_HIGH) << 8); +} + +static void snd_ad1816a_write(ad1816a_t *chip, unsigned char reg, + unsigned short value) +{ + snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); + snd_ad1816a_out(chip, AD1816A_INDIR_DATA_LOW, value & 0xff); + snd_ad1816a_out(chip, AD1816A_INDIR_DATA_HIGH, (value >> 8) & 0xff); +} + +static void snd_ad1816a_write_mask(ad1816a_t *chip, unsigned char reg, + unsigned short mask, unsigned short value) +{ + snd_ad1816a_write(chip, reg, + (value & mask) | (snd_ad1816a_read(chip, reg) & ~mask)); +} + + +static unsigned char snd_ad1816a_get_format(ad1816a_t *chip, + unsigned int format, int channels) +{ + unsigned char retval = AD1816A_FMT_LINEAR_8; + + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: + retval = AD1816A_FMT_ULAW_8; + break; + case SNDRV_PCM_FORMAT_A_LAW: + retval = AD1816A_FMT_ALAW_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + retval = AD1816A_FMT_LINEAR_16_LIT; + break; + case SNDRV_PCM_FORMAT_S16_BE: + retval = AD1816A_FMT_LINEAR_16_BIG; + } + return (channels > 1) ? (retval | AD1816A_FMT_STEREO) : retval; +} + +static int snd_ad1816a_open(ad1816a_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (chip->mode & mode) { + spin_unlock_irqrestore(&chip->lock, flags); + return -EAGAIN; + } + + switch ((mode &= AD1816A_MODE_OPEN)) { + case AD1816A_MODE_PLAYBACK: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_PLAYBACK_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_PLAYBACK_IRQ_ENABLE, 0xffff); + break; + case AD1816A_MODE_CAPTURE: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_CAPTURE_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_CAPTURE_IRQ_ENABLE, 0xffff); + break; + case AD1816A_MODE_TIMER: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_TIMER_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_IRQ_ENABLE, 0xffff); + } + chip->mode |= mode; + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static void snd_ad1816a_close(ad1816a_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + switch ((mode &= AD1816A_MODE_OPEN)) { + case AD1816A_MODE_PLAYBACK: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_PLAYBACK_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_PLAYBACK_IRQ_ENABLE, 0x0000); + break; + case AD1816A_MODE_CAPTURE: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_CAPTURE_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_CAPTURE_IRQ_ENABLE, 0x0000); + break; + case AD1816A_MODE_TIMER: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_TIMER_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_IRQ_ENABLE, 0x0000); + } + if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN)) + chip->mode = 0; + + spin_unlock_irqrestore(&chip->lock, flags); +} + + +static int snd_ad1816a_trigger(ad1816a_t *chip, unsigned char what, + int channel, int cmd) +{ + int error = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + spin_lock(&chip->lock); + cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00; + if (what & AD1816A_PLAYBACK_ENABLE) + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_PLAYBACK_ENABLE, cmd); + if (what & AD1816A_CAPTURE_ENABLE) + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_CAPTURE_ENABLE, cmd); + spin_unlock(&chip->lock); + break; + default: + snd_printk("invalid trigger mode 0x%x.\n", what); + error = -EINVAL; + } + + return error; +} + +static int snd_ad1816a_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1816a_trigger(chip, AD1816A_PLAYBACK_ENABLE, + SNDRV_PCM_STREAM_PLAYBACK, cmd); +} + +static int snd_ad1816a_capture_trigger(snd_pcm_substream_t *substream, int cmd) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1816a_trigger(chip, AD1816A_CAPTURE_ENABLE, + SNDRV_PCM_STREAM_CAPTURE, cmd); +} + +static int snd_ad1816a_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ad1816a_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size; + + spin_lock_irqsave(&chip->lock, flags); + + chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream); + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); + + snd_dma_program(chip->dma1, runtime->dma_addr, size, + DMA_MODE_WRITE | DMA_AUTOINIT); + + snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, runtime->rate); + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_FMT_ALL | AD1816A_FMT_STEREO, + snd_ad1816a_get_format(chip, runtime->format, + runtime->channels)); + + snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT, + snd_pcm_lib_period_bytes(substream) / 4 - 1); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size; + + spin_lock_irqsave(&chip->lock, flags); + + chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream); + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); + + snd_dma_program(chip->dma2, runtime->dma_addr, size, + DMA_MODE_READ | DMA_AUTOINIT); + + snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, runtime->rate); + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_FMT_ALL | AD1816A_FMT_STEREO, + snd_ad1816a_get_format(chip, runtime->format, + runtime->channels)); + + snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT, + snd_pcm_lib_period_bytes(substream) / 4 - 1); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + + +static snd_pcm_uframes_t snd_ad1816a_playback_pointer(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + if (!(chip->mode & AD1816A_MODE_PLAYBACK)) + return 0; + ptr = chip->p_dma_size - snd_dma_residue(chip->dma1); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ad1816a_capture_pointer(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + if (!(chip->mode & AD1816A_MODE_CAPTURE)) + return 0; + ptr = chip->c_dma_size - snd_dma_residue(chip->dma2); + return bytes_to_frames(substream->runtime, ptr); +} + + +static void snd_ad1816a_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, dev_id, return); + unsigned char status; + + spin_lock(&chip->lock); + status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS); + spin_unlock(&chip->lock); + + if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream) + snd_pcm_period_elapsed(chip->playback_substream); + + if ((status & AD1816A_CAPTURE_IRQ_PENDING) && chip->capture_substream) + snd_pcm_period_elapsed(chip->capture_substream); + + if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer) + snd_timer_interrupt(chip->timer, chip->timer->sticks); + + spin_lock(&chip->lock); + snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); + spin_unlock(&chip->lock); +} + + +static snd_pcm_hardware_t snd_ad1816a_playback = { + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 55200, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ad1816a_capture = { + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 55200, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_ad1816a_timer_close(snd_timer_t *timer) +{ + ad1816a_t *chip = snd_timer_chip(timer); + snd_ad1816a_close(chip, AD1816A_MODE_TIMER); + return 0; +} + +static int snd_ad1816a_timer_open(snd_timer_t *timer) +{ + ad1816a_t *chip = snd_timer_chip(timer); + snd_ad1816a_open(chip, AD1816A_MODE_TIMER); + return 0; +} + +static unsigned long snd_ad1816a_timer_resolution(snd_timer_t *timer) +{ + snd_assert(timer != NULL, return 0); + + return 10000; +} + +static int snd_ad1816a_timer_start(snd_timer_t *timer) +{ + unsigned short bits; + unsigned long flags; + ad1816a_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->lock, flags); + bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE); + + if (!(bits & AD1816A_TIMER_ENABLE)) { + snd_ad1816a_write(chip, AD1816A_TIMER_BASE_COUNT, + timer->sticks & 0xffff); + + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_ENABLE, 0xffff); + } + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_ad1816a_timer_stop(snd_timer_t *timer) +{ + unsigned long flags; + ad1816a_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->lock, flags); + + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_ENABLE, 0x0000); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static struct _snd_timer_hardware snd_ad1816a_timer_table = { + flags: SNDRV_TIMER_HW_AUTO, + resolution: 10000, + ticks: 65535, + open: snd_ad1816a_timer_open, + close: snd_ad1816a_timer_close, + c_resolution: snd_ad1816a_timer_resolution, + start: snd_ad1816a_timer_start, + stop: snd_ad1816a_timer_stop, +}; + + +static int snd_ad1816a_playback_open(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int error; + + if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0) + return error; + snd_pcm_set_sync(substream); + runtime->hw = snd_ad1816a_playback; + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); + chip->playback_substream = substream; + return 0; +} + +static int snd_ad1816a_capture_open(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int error; + + if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0) + return error; + snd_pcm_set_sync(substream); + runtime->hw = snd_ad1816a_capture; + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); + chip->capture_substream = substream; + return 0; +} + +static int snd_ad1816a_playback_close(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_ad1816a_close(chip, AD1816A_MODE_PLAYBACK); + return 0; +} + +static int snd_ad1816a_capture_close(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_ad1816a_close(chip, AD1816A_MODE_CAPTURE); + return 0; +} + + +static void snd_ad1816a_init(ad1816a_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); + snd_ad1816a_write(chip, AD1816A_INTERRUPT_ENABLE, 0x0000); + snd_ad1816a_write_mask(chip, AD1816A_CHIP_CONFIG, + AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff); + snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000); + snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000); + + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int snd_ad1816a_probe(ad1816a_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) { + case 0: + chip->hardware = AD1816A_HW_AD1815; + break; + case 1: + chip->hardware = AD1816A_HW_AD18MAX10; + break; + case 3: + chip->hardware = AD1816A_HW_AD1816A; + break; + default: + chip->hardware = AD1816A_HW_AUTO; + } + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_ad1816a_free(ad1816a_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma1 >= 0) { + snd_dma_disable(chip->dma1); + free_dma(chip->dma1); + } + if (chip->dma2 >= 0) { + snd_dma_disable(chip->dma2); + free_dma(chip->dma2); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_ad1816a_dev_free(snd_device_t *device) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, device->device_data, return -ENXIO); + return snd_ad1816a_free(chip); +} + +static const char *snd_ad1816a_chip_id(ad1816a_t *chip) +{ + switch (chip->hardware) { + case AD1816A_HW_AD1816A: return "AD1816A"; + case AD1816A_HW_AD1815: return "AD1815"; + case AD1816A_HW_AD18MAX10: return "AD18max10"; + default: + snd_printk("Unknown chip version %d:%d.\n", + chip->version, chip->hardware); + return "AD1816A - unknown"; + } +} + +int snd_ad1816a_create(snd_card_t *card, + unsigned long port, int irq, int dma1, int dma2, + ad1816a_t **rchip) +{ + static snd_device_ops_t ops = { + dev_free: snd_ad1816a_dev_free, + }; + int error; + ad1816a_t *chip; + + *rchip = NULL; + + chip = snd_magic_kcalloc(ad1816a_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->irq = -1; + chip->dma1 = -1; + chip->dma2 = -1; + + if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) { + snd_ad1816a_free(chip); + return -EBUSY; + } + if (request_irq(irq, snd_ad1816a_interrupt, SA_INTERRUPT, "AD1816A", (void *) chip)) { + snd_ad1816a_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (request_dma(dma1, "AD1816A - 1")) { + snd_ad1816a_free(chip); + return -EBUSY; + } + chip->dma1 = dma1; + if (request_dma(dma2, "AD1816A - 2")) { + snd_ad1816a_free(chip); + return -EBUSY; + } + chip->dma2 = dma2; + + chip->card = card; + chip->port = port; + spin_lock_init(&chip->lock); + + if ((error = snd_ad1816a_probe(chip))) { + snd_ad1816a_free(chip); + return error; + } + + snd_ad1816a_init(chip); + + /* Register device */ + if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_ad1816a_free(chip); + return error; + } + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_ad1816a_playback_ops = { + open: snd_ad1816a_playback_open, + close: snd_ad1816a_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ad1816a_hw_params, + hw_free: snd_ad1816a_hw_free, + prepare: snd_ad1816a_playback_prepare, + trigger: snd_ad1816a_playback_trigger, + pointer: snd_ad1816a_playback_pointer, +}; + +static snd_pcm_ops_t snd_ad1816a_capture_ops = { + open: snd_ad1816a_capture_open, + close: snd_ad1816a_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ad1816a_hw_params, + hw_free: snd_ad1816a_hw_free, + prepare: snd_ad1816a_capture_prepare, + trigger: snd_ad1816a_capture_trigger, + pointer: snd_ad1816a_capture_pointer, +}; + +static void snd_ad1816a_pcm_free(snd_pcm_t *pcm) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm) +{ + int error; + snd_pcm_t *pcm; + + if ((error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm))) + return error; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1816a_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_ad1816a_pcm_free; + pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0; + + strcpy(pcm->name, snd_ad1816a_chip_id(chip)); + snd_ad1816a_init(chip); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + + chip->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +static void snd_ad1816a_timer_free(snd_timer_t *timer) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, timer->private_data, return); + chip->timer = NULL; +} + +int snd_ad1816a_timer(ad1816a_t *chip, int device, snd_timer_t **rtimer) +{ + snd_timer_t *timer; + snd_timer_id_t tid; + int error; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = chip->card->number; + tid.device = device; + tid.subdevice = 0; + if ((error = snd_timer_new(chip->card, "AD1816A", &tid, &timer)) < 0) + return error; + strcpy(timer->name, snd_ad1816a_chip_id(chip)); + timer->private_data = chip; + timer->private_free = snd_ad1816a_timer_free; + chip->timer = timer; + timer->hw = snd_ad1816a_timer_table; + if (rtimer) + *rtimer = timer; + return 0; +} + +/* + * + */ + +static int snd_ad1816a_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Line", "Mix", "CD", "Synth", "Video", + "Mic", "Phone", + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 7; + if (uinfo->value.enumerated.item > 6) + uinfo->value.enumerated.item = 6; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ad1816a_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short val; + + spin_lock_irqsave(&chip->lock, flags); + val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL); + spin_unlock_irqrestore(&chip->lock, flags); + ucontrol->value.enumerated.item[0] = (val >> 12) & 7; + ucontrol->value.enumerated.item[1] = (val >> 4) & 7; + return 0; +} + +static int snd_ad1816a_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short val; + int change; + + if (ucontrol->value.enumerated.item[0] > 6 || + ucontrol->value.enumerated.item[1] > 6) + return -EINVAL; + val = (ucontrol->value.enumerated.item[0] << 12) | + (ucontrol->value.enumerated.item[1] << 4); + spin_lock_irqsave(&chip->lock, flags); + change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val; + snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define AD1816A_SINGLE(xname, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ad1816a_info_single, \ + get: snd_ad1816a_get_single, put: snd_ad1816a_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_ad1816a_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ad1816a_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ad1816a_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short old_val, val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->lock, flags); + old_val = snd_ad1816a_read(chip, reg); + val = (old_val & ~(mask << shift)) | val; + change = val != old_val; + snd_ad1816a_write(chip, reg, val); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ad1816a_info_double, \ + get: snd_ad1816a_get_double, put: snd_ad1816a_put_double, \ + private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } + +static int snd_ad1816a_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ad1816a_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val; + + spin_lock_irqsave(&chip->lock, flags); + val = snd_ad1816a_read(chip, reg); + ucontrol->value.integer.value[0] = (val >> shift_left) & mask; + ucontrol->value.integer.value[1] = (val >> shift_right) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_ad1816a_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short old_val, val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->lock, flags); + old_val = snd_ad1816a_read(chip, reg); + val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != old_val; + snd_ad1816a_write(chip, reg, val1); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define AD1816A_CONTROLS (sizeof(snd_ad1816a_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ad1816a_controls[] = { +AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1), +AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1), +AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1), +AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 63, 1), +AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0), +AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1), +AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1), +AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1), +AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1), +AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_ad1816a_info_mux, + get: snd_ad1816a_get_mux, + put: snd_ad1816a_put_mux, +}, +AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1), +AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0), +AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1), +AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0), +}; + +int snd_ad1816a_mixer(ad1816a_t *chip) +{ + snd_card_t *card; + int err, idx; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, snd_ad1816a_chip_id(chip)); + + for (idx = 0; idx < AD1816A_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +EXPORT_SYMBOL(snd_ad1816a_create); +EXPORT_SYMBOL(snd_ad1816a_pcm); +EXPORT_SYMBOL(snd_ad1816a_mixer); + +static int __init alsa_ad1816a_init(void) +{ + return 0; +} + +static void __exit alsa_ad1816a_exit(void) +{ +} + +module_init(alsa_ad1816a_init) +module_exit(alsa_ad1816a_exit) + diff -Nru a/sound/isa/ad1848/Makefile b/sound/isa/ad1848/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/ad1848/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,27 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ad1848.o + +list-multi := snd-ad1848-lib.o snd-ad1848.o + +export-objs := ad1848_lib.o + +snd-ad1848-lib-objs := ad1848_lib.o +snd-ad1848-objs := ad1848.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_CMI8330) += snd-ad1848-lib.o +obj-$(CONFIG_SND_SGALAXY) += snd-ad1848-lib.o +obj-$(CONFIG_SND_AD1848) += snd-ad1848.o snd-ad1848-lib.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ad1848-lib.o + +include $(TOPDIR)/Rules.make + +snd-ad1848-lib.o: $(snd-ad1848-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1848-lib-objs) + +snd-ad1848.o: $(snd-ad1848-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1848-objs) diff -Nru a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/ad1848/ad1848.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,179 @@ +/* + * Generic driver for AD1848/AD1847/CS4248 chips (0.1 Alpha) + * Copyright (c) by Tugrul Galatali , + * Jaroslav Kysela + * Based on card-4232.c by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t ad1848_t + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Tugrul Galatali , Jaroslav Kysela "); +MODULE_DESCRIPTION("AD1848/AD1847/CS4248"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Analog Devices,AD1848}," + "{Analog Devices,AD1847}," + "{Crystal Semiconductors,CS4248}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for AD1848 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for AD1848 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable AD1848 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for AD1848 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for AD1848 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for AD1848 driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); + +static snd_card_t *snd_ad1848_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_card_ad1848_probe(int dev) +{ + snd_card_t *card; + ad1848_t *chip; + snd_pcm_t *pcm; + int err; + + if (snd_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_port\n"); + return -EINVAL; + } + if (snd_irq[dev] == SNDRV_AUTO_IRQ) { + snd_printk("specify snd_irq\n"); + return -EINVAL; + } + if (snd_dma1[dev] == SNDRV_AUTO_DMA) { + snd_printk("specify snd_dma1\n"); + return -EINVAL; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ad1848_create(card, snd_port[dev], + snd_irq[dev], + snd_dma1[dev], + AD1848_HW_DETECT, + &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ad1848_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ad1848_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + strcpy(card->driver, "AD1848"); + strcpy(card->shortname, pcm->name); + + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + pcm->name, chip->port, snd_irq[dev], snd_dma1[dev]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_ad1848_cards[dev] = card; + return 0; +} + +static int __init alsa_card_ad1848_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) + if (snd_card_ad1848_probe(dev) >= 0) + cards++; + + if (!cards) { +#ifdef MODULE + snd_printk("AD1848 soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_ad1848_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_ad1848_cards[idx]); +} + +module_init(alsa_card_ad1848_init) +module_exit(alsa_card_ad1848_exit) + +#ifndef MODULE + +/* format is: snd-ad1848=snd_enable,snd_index,snd_id,snd_port, + snd_irq,snd_dma1 */ + +static int __init alsa_card_ad1848_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ad1848=", alsa_card_ad1848_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/ad1848/ad1848_lib.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1173 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of AD1848/AD1847/CS4248 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_MAIN_OBJECT_FILE +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248"); +MODULE_LICENSE("GPL"); + +#define chip_t ad1848_t + +#if 0 +#define SNDRV_DEBUG_MCE +#endif + +/* + * Some variables + */ + +static unsigned char freq_bits[14] = { + /* 5510 */ 0x00 | AD1848_XTAL2, + /* 6620 */ 0x0E | AD1848_XTAL2, + /* 8000 */ 0x00 | AD1848_XTAL1, + /* 9600 */ 0x0E | AD1848_XTAL1, + /* 11025 */ 0x02 | AD1848_XTAL2, + /* 16000 */ 0x02 | AD1848_XTAL1, + /* 18900 */ 0x04 | AD1848_XTAL2, + /* 22050 */ 0x06 | AD1848_XTAL2, + /* 27042 */ 0x04 | AD1848_XTAL1, + /* 32000 */ 0x06 | AD1848_XTAL1, + /* 33075 */ 0x0C | AD1848_XTAL2, + /* 37800 */ 0x08 | AD1848_XTAL2, + /* 44100 */ 0x0A | AD1848_XTAL2, + /* 48000 */ 0x0C | AD1848_XTAL1 +}; + +static unsigned int rates[14] = { + 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, + 27042, 32000, 33075, 37800, 44100, 48000 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + count: 14, + list: rates, + mask: 0, +}; + +static unsigned char snd_ad1848_original_image[16] = +{ + 0x00, /* 00 - lic */ + 0x00, /* 01 - ric */ + 0x9f, /* 02 - la1ic */ + 0x9f, /* 03 - ra1ic */ + 0x9f, /* 04 - la2ic */ + 0x9f, /* 05 - ra2ic */ + 0xbf, /* 06 - loc */ + 0xbf, /* 07 - roc */ + 0x20, /* 08 - dfr */ + AD1848_AUTOCALIB, /* 09 - ic */ + 0x00, /* 0a - pc */ + 0x00, /* 0b - ti */ + 0x00, /* 0c - mi */ + 0x00, /* 0d - lbc */ + 0x00, /* 0e - dru */ + 0x00, /* 0f - drl */ +}; + +/* + * Basic I/O functions + */ + +void snd_ad1848_out(ad1848_t *chip, + unsigned char reg, + unsigned char value) +{ + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); + outb(chip->image[reg] = value, AD1848P(chip, REG)); + mb(); +#if 0 + printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value); +#endif +} + +void snd_ad1848_dout(ad1848_t *chip, + unsigned char reg, + unsigned char value) +{ + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); + outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); + outb(value, AD1848P(chip, REG)); + mb(); +} + +unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg) +{ + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("auto calibration time out - reg = 0x%x\n", reg); +#endif + outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); + mb(); + return inb(AD1848P(chip, REG)); +} + +#ifdef CONFIG_SND_DEBUG + +void snd_ad1848_debug(ad1848_t *chip) +{ + printk("AD1848 REGS: INDEX = 0x%02x ", inb(AD1848P(chip, REGSEL))); + printk(" STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS))); + printk(" 0x00: left input = 0x%02x ", snd_ad1848_in(chip, 0x00)); + printk(" 0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08)); + printk(" 0x01: right input = 0x%02x ", snd_ad1848_in(chip, 0x01)); + printk(" 0x09: iface (CFIG 1) = 0x%02x\n", snd_ad1848_in(chip, 0x09)); + printk(" 0x02: AUXA left = 0x%02x ", snd_ad1848_in(chip, 0x02)); + printk(" 0x0a: pin control = 0x%02x\n", snd_ad1848_in(chip, 0x0a)); + printk(" 0x03: AUXA right = 0x%02x ", snd_ad1848_in(chip, 0x03)); + printk(" 0x0b: init & status = 0x%02x\n", snd_ad1848_in(chip, 0x0b)); + printk(" 0x04: AUXB left = 0x%02x ", snd_ad1848_in(chip, 0x04)); + printk(" 0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c)); + printk(" 0x05: AUXB right = 0x%02x ", snd_ad1848_in(chip, 0x05)); + printk(" 0x0d: loopback = 0x%02x\n", snd_ad1848_in(chip, 0x0d)); + printk(" 0x06: left output = 0x%02x ", snd_ad1848_in(chip, 0x06)); + printk(" 0x0e: data upr count = 0x%02x\n", snd_ad1848_in(chip, 0x0e)); + printk(" 0x07: right output = 0x%02x ", snd_ad1848_in(chip, 0x07)); + printk(" 0x0f: data lwr count = 0x%02x\n", snd_ad1848_in(chip, 0x0f)); +} + +#endif + +/* + * AD1848 detection / MCE routines + */ + +void snd_ad1848_mce_up(ad1848_t *chip) +{ + unsigned long flags; + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("mce_up - auto calibration time out (0)\n"); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + chip->mce_bit |= AD1848_MCE; + timeout = inb(AD1848P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); + if (!(timeout & AD1848_MCE)) + outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +void snd_ad1848_mce_down(ad1848_t *chip) +{ + unsigned long flags; + int timeout; + signed long time; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (timeout = 5; timeout > 0; timeout--) + inb(AD1848P(chip, REGSEL)); + /* end of cleanup sequence */ + for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#if 0 + printk("(1) timeout = %i\n", timeout); +#endif +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL)); +#endif + chip->mce_bit &= ~AD1848_MCE; + timeout = inb(AD1848P(chip, REGSEL)); + outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); + if ((timeout & AD1848_MCE) == 0) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + /* calibration process */ + + for (timeout = 500; timeout > 0 && (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0; timeout--); + if ((snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0) { + snd_printd("mce_down - auto calibration time out (1)\n"); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } +#if 0 + printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); +#endif + time = HZ / 4; + while (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (2)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->reg_lock, flags); + } +#if 0 + printk("(3) jiffies = %li\n", jiffies); +#endif + time = HZ / 10; + while (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (3)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->reg_lock, flags); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + printk("(4) jiffies = %li\n", jiffies); + snd_printk("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL))); +#endif +} + +static unsigned int snd_ad1848_get_count(unsigned char format, + unsigned int size) +{ + switch (format & 0xe0) { + case AD1848_LINEAR_16: + size >>= 1; + break; + } + if (format & AD1848_STEREO) + size >>= 1; + return size; +} + +static int snd_ad1848_trigger(ad1848_t *chip, unsigned char what, + int channel, int cmd) +{ + int result = 0; + +#if 0 + printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS))); +#endif + spin_lock(&chip->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (chip->image[AD1848_IFACE_CTRL] & what) { + spin_unlock(&chip->reg_lock); + return 0; + } + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what); + chip->mode |= AD1848_MODE_RUNNING; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (!(chip->image[AD1848_IFACE_CTRL] & what)) { + spin_unlock(&chip->reg_lock); + return 0; + } + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what); + chip->mode &= ~AD1848_MODE_RUNNING; + } else { + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +/* + * CODEC I/O + */ + +static unsigned char snd_ad1848_get_rate(unsigned int rate) +{ + int i; + + for (i = 0; i < 14; i++) + if (rate == rates[i]) + return freq_bits[i]; + snd_BUG(); + return freq_bits[13]; +} + +static int snd_ad1848_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static unsigned char snd_ad1848_get_format(int format, int channels) +{ + unsigned char rformat; + + rformat = AD1848_LINEAR_8; + switch (format) { + case SNDRV_PCM_FORMAT_A_LAW: rformat = AD1848_ALAW_8; break; + case SNDRV_PCM_FORMAT_MU_LAW: rformat = AD1848_ULAW_8; break; + case SNDRV_PCM_FORMAT_S16_LE: rformat = AD1848_LINEAR_16; break; + } + if (channels > 1) + rformat |= AD1848_STEREO; +#if 0 + snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); +#endif + return rformat; +} + +static void snd_ad1848_calibrate_mute(ad1848_t *chip, int mute) +{ + unsigned long flags; + + mute = mute ? 1 : 0; + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->calibrate_mute == mute) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + if (!mute) { + snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]); + snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]); + } + snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]); + snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]); + snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]); + snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]); + snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]); + snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]); + chip->calibrate_mute = mute; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_ad1848_set_data_format(ad1848_t *chip, snd_pcm_hw_params_t *hw_params) +{ + if (hw_params == NULL) { + chip->image[AD1848_DATA_FORMAT] = 0x20; + } else { + chip->image[AD1848_DATA_FORMAT] = + snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) | + snd_ad1848_get_rate(params_rate(hw_params)); + } + // snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]); +} + +static int snd_ad1848_open(ad1848_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + if (chip->mode & AD1848_MODE_OPEN) { + up(&chip->open_mutex); + return -EAGAIN; + } + snd_ad1848_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("open: (1)\n"); +#endif + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO | + AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO | + AD1848_CALIB_MODE); + chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB; + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("open: (2)\n"); +#endif + + snd_ad1848_set_data_format(chip, NULL); + + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("open: (3)\n"); +#endif + + /* ok. now enable and ack CODEC IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE; + snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->mode = mode; + up(&chip->open_mutex); + + return 0; +} + +static void snd_ad1848_close(ad1848_t *chip) +{ + unsigned long flags; + + down(&chip->open_mutex); + if (!chip->mode) { + up(&chip->open_mutex); + return; + } + /* disable IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + chip->image[AD1848_PIN_CTRL] &= ~AD1848_IRQ_ENABLE; + snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* now disable capture & playback */ + + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO | + AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO); + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + + /* clear IRQ again */ + spin_lock_irqsave(&chip->reg_lock, flags); + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->mode = 0; + up(&chip->open_mutex); +} + +/* + * ok.. exported functions.. + */ + +static int snd_ad1848_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd); +} + +static int snd_ad1848_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd); +} + +static int snd_ad1848_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + snd_ad1848_calibrate_mute(chip, 1); + snd_ad1848_set_data_format(chip, hw_params); + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + snd_ad1848_calibrate_mute(chip, 0); + return 0; +} + +static int snd_ad1848_playback_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ad1848_playback_prepare(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO); + snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count); + snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ad1848_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + snd_ad1848_calibrate_mute(chip, 1); + snd_ad1848_set_data_format(chip, hw_params); + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + snd_ad1848_calibrate_mute(chip, 0); + return 0; +} + +static int snd_ad1848_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ad1848_capture_prepare(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO); + snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count); + snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ad1848_t *chip = snd_magic_cast(ad1848_t, dev_id, return); + + if ((chip->mode & AD1848_MODE_PLAY) && chip->playback_substream && + (chip->mode & AD1848_MODE_RUNNING)) + snd_pcm_period_elapsed(chip->playback_substream); + if ((chip->mode & AD1848_MODE_CAPTURE) && chip->capture_substream && + (chip->mode & AD1848_MODE_RUNNING)) + snd_pcm_period_elapsed(chip->capture_substream); + outb(0, AD1848P(chip, STATUS)); /* clear global interrupt bit */ +} + +static snd_pcm_uframes_t snd_ad1848_playback_pointer(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_PLAYBACK_ENABLE)) + return 0; + ptr = chip->dma_size - snd_dma_residue(chip->dma); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ad1848_capture_pointer(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_CAPTURE_ENABLE)) + return 0; + ptr = chip->dma_size - snd_dma_residue(chip->dma); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static int snd_ad1848_probe(ad1848_t * chip) +{ + unsigned long flags; + int i, id, rev, ad1847; + unsigned char *ptr; + +#if 0 + snd_ad1848_debug(chip); +#endif + id = ad1847 = 0; + for (i = 0; i < 1000; i++) { + mb(); + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + udelay(500); + else { + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00); + snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa); + snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45); + rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT); + if (rev == 0x65) { + id = 1; + ad1847 = 1; + break; + } + if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) { + id = 1; + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + } + if (id != 1) + return -ENODEV; /* no valid device found */ + if (chip->hardware == AD1848_HW_DETECT) { + if (ad1847) { + chip->hardware = AD1848_HW_AD1847; + } else { + chip->hardware = AD1848_HW_AD1848; + rev = snd_ad1848_in(chip, AD1848_MISC_INFO); + if (rev & 0x80) { + chip->hardware = AD1848_HW_CS4248; + } else if (rev & 0x0a) { + chip->hardware = AD1848_HW_CMI8330; + } + } + } + spin_lock_irqsave(&chip->reg_lock, flags); + inb(AD1848P(chip, STATUS)); /* clear any pendings IRQ */ + outb(0, AD1848P(chip, STATUS)); + mb(); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->image[AD1848_MISC_INFO] = 0x00; + chip->image[AD1848_IFACE_CTRL] = + (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA; + ptr = (unsigned char *) &chip->image; + snd_ad1848_mce_down(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 16; i++) /* ok.. fill all AD1848 registers */ + snd_ad1848_out(chip, i, *ptr++); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_up(chip); + snd_ad1848_mce_down(chip); + return 0; /* all things are ok.. */ +} + +/* + + */ + +static snd_pcm_hardware_t snd_ad1848_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5510, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ad1848_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5510, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + + */ + +static int snd_ad1848_playback_open(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0) + return err; + chip->playback_substream = substream; + runtime->hw = snd_ad1848_playback; + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return 0; +} + +static int snd_ad1848_capture_open(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0) + return err; + chip->capture_substream = substream; + runtime->hw = snd_ad1848_capture; + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return 0; +} + +static int snd_ad1848_playback_close(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + + chip->mode &= ~AD1848_MODE_PLAY; + chip->playback_substream = NULL; + snd_ad1848_close(chip); + return 0; +} + +static int snd_ad1848_capture_close(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + + chip->mode &= ~AD1848_MODE_CAPTURE; + chip->capture_substream = NULL; + snd_ad1848_close(chip); + return 0; +} + +static int snd_ad1848_free(ad1848_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma >= 0) { + snd_dma_disable(chip->dma); + free_dma(chip->dma); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_ad1848_dev_free(snd_device_t *device) +{ + ad1848_t *chip = snd_magic_cast(ad1848_t, device->device_data, return -ENXIO); + return snd_ad1848_free(chip); +} + +static const char *snd_ad1848_chip_id(ad1848_t *chip) +{ + switch (chip->hardware) { + case AD1848_HW_AD1847: return "AD1847"; + case AD1848_HW_AD1848: return "AD1848"; + case AD1848_HW_CS4248: return "CS4248"; + case AD1848_HW_CMI8330: return "CMI8330/C3D"; + default: return "???"; + } +} + +int snd_ad1848_create(snd_card_t * card, + unsigned long port, + int irq, int dma, + unsigned short hardware, + ad1848_t ** rchip) +{ + static snd_device_ops_t ops = { + dev_free: snd_ad1848_dev_free, + }; + ad1848_t *chip; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(ad1848_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + init_MUTEX(&chip->open_mutex); + chip->card = card; + chip->port = port; + chip->irq = -1; + chip->dma = -1; + chip->hardware = hardware; + memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image)); + + if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) { + snd_ad1848_free(chip); + return -EBUSY; + } + if (request_irq(irq, snd_ad1848_interrupt, SA_INTERRUPT, "AD1848", (void *) chip)) { + snd_ad1848_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (request_dma(dma, "AD1848")) { + snd_ad1848_free(chip); + return -EBUSY; + } + chip->dma = dma; + + if (snd_ad1848_probe(chip) < 0) { + snd_ad1848_free(chip); + return -ENODEV; + } + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_ad1848_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_ad1848_playback_ops = { + open: snd_ad1848_playback_open, + close: snd_ad1848_playback_close, + ioctl: snd_ad1848_ioctl, + hw_params: snd_ad1848_playback_hw_params, + hw_free: snd_ad1848_playback_hw_free, + prepare: snd_ad1848_playback_prepare, + trigger: snd_ad1848_playback_trigger, + pointer: snd_ad1848_playback_pointer, +}; + +static snd_pcm_ops_t snd_ad1848_capture_ops = { + open: snd_ad1848_capture_open, + close: snd_ad1848_capture_close, + ioctl: snd_ad1848_ioctl, + hw_params: snd_ad1848_capture_hw_params, + hw_free: snd_ad1848_capture_hw_free, + prepare: snd_ad1848_capture_prepare, + trigger: snd_ad1848_capture_trigger, + pointer: snd_ad1848_capture_pointer, +}; + +static void snd_ad1848_pcm_free(snd_pcm_t *pcm) +{ + ad1848_t *chip = snd_magic_cast(ad1848_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_ad1848_pcm(ad1848_t *chip, int device, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops); + + pcm->private_free = snd_ad1848_pcm_free; + pcm->private_data = chip; + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + strcpy(pcm->name, snd_ad1848_chip_id(chip)); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma > 3 ? 128*1024 : 64*1024); + + chip->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER part + */ + +static int snd_ad1848_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { + "Line", "Aux", "Mic", "Mix" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ad1848_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.enumerated.item[0] = (chip->image[AD1848_LEFT_INPUT] & AD1848_MIXS_ALL) >> 6; + ucontrol->value.enumerated.item[1] = (chip->image[AD1848_RIGHT_INPUT] & AD1848_MIXS_ALL) >> 6; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ad1848_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right; + int change; + + if (ucontrol->value.enumerated.item[0] > 3 || + ucontrol->value.enumerated.item[1] > 3) + return -EINVAL; + left = ucontrol->value.enumerated.item[0] << 6; + right = ucontrol->value.enumerated.item[1] << 6; + spin_lock_irqsave(&chip->reg_lock, flags); + left = (chip->image[AD1848_LEFT_INPUT] & ~AD1848_MIXS_ALL) | left; + right = (chip->image[AD1848_RIGHT_INPUT] & ~AD1848_MIXS_ALL) | right; + change = left != chip->image[AD1848_LEFT_INPUT] || + right != chip->image[AD1848_RIGHT_INPUT]; + snd_ad1848_out(chip, AD1848_LEFT_INPUT, left); + snd_ad1848_out(chip, AD1848_RIGHT_INPUT, right); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + snd_ad1848_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + snd_ad1848_out(chip, left_reg, val1); + snd_ad1848_out(chip, right_reg, val2); + } else { + val1 = (chip->image[left_reg] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != chip->image[left_reg]; + snd_ad1848_out(chip, left_reg, val1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define AD1848_CONTROLS (sizeof(snd_ad1848_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ad1848_controls[] = { +AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), +AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), +AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_ad1848_info_mux, + get: snd_ad1848_get_mux, + put: snd_ad1848_put_mux, +}, +AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0), +AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0) +}; + +int snd_ad1848_mixer(ad1848_t *chip) +{ + snd_card_t *card; + snd_pcm_t *pcm; + int err, idx; + + snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); + + pcm = chip->pcm; + card = chip->card; + + strcpy(card->mixername, pcm->name); + + for (idx = 0; idx < AD1848_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1848_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +EXPORT_SYMBOL(snd_ad1848_in); +EXPORT_SYMBOL(snd_ad1848_out); +EXPORT_SYMBOL(snd_ad1848_dout); +EXPORT_SYMBOL(snd_ad1848_mce_up); +EXPORT_SYMBOL(snd_ad1848_mce_down); +EXPORT_SYMBOL(snd_ad1848_interrupt); +EXPORT_SYMBOL(snd_ad1848_create); +EXPORT_SYMBOL(snd_ad1848_pcm); +EXPORT_SYMBOL(snd_ad1848_mixer); +EXPORT_SYMBOL(snd_ad1848_info_single); +EXPORT_SYMBOL(snd_ad1848_get_single); +EXPORT_SYMBOL(snd_ad1848_put_single); +EXPORT_SYMBOL(snd_ad1848_info_double); +EXPORT_SYMBOL(snd_ad1848_get_double); +EXPORT_SYMBOL(snd_ad1848_put_double); + +/* + * INIT part + */ + +static int __init alsa_ad1848_init(void) +{ + return 0; +} + +static void __exit alsa_ad1848_exit(void) +{ +} + +module_init(alsa_ad1848_init) +module_exit(alsa_ad1848_exit) diff -Nru a/sound/isa/als100.c b/sound/isa/als100.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/als100.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,421 @@ + +/* + card-als100.c - driver for Avance Logic ALS100 based soundcards. + Copyright (C) 1999-2000 by Massimo Piccioni + + Thanks to Pierfrancesco 'qM2' Passerini. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t sb_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("Avance Logic ALS1X0"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Avance Logic,ALS100 - PRO16PNP}," + "{Avance Logic,ALS110}," + "{Avance Logic,ALS120}," + "{Avance Logic,ALS200}," + "{3D Melody,MF1000}," + "{Digimate,3D Sound}," + "{Avance Logic,ALS120}," + "{RTL,RTL3000}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ +static int snd_dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for als100 based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for als100 based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable als100 based soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for als100 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for als100 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for als100 driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for als100 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for als100 driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for als100 driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); +MODULE_PARM(snd_dma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma16, "16-bit DMA # for als100 driver."); +MODULE_PARM_SYNTAX(snd_dma16, SNDRV_DMA16_DESC); + +struct snd_card_als100 { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; + struct isapnp_dev *devopl; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_als100_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ +static struct isapnp_card *snd_als100_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_als100_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_ALS100(_va, _vb, _vc, _device, _audio, _mpu401, _opl) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID('@', '@', '@', _audio), \ + ISAPNP_DEVICE_ID('@', 'X', '@', _mpu401), \ + ISAPNP_DEVICE_ID('@', 'H', '@', _opl) } \ + } + +static struct isapnp_card_id snd_als100_pnpids[] __devinitdata = { + /* ALS100 - PRO16PNP */ + ISAPNP_ALS100('A','L','S',0x0001,0x0001,0x0001,0x0001), + /* ALS110 - MF1000 - Digimate 3D Sound */ + ISAPNP_ALS100('A','L','S',0x0110,0x1001,0x1001,0x1001), + /* ALS120 */ + ISAPNP_ALS100('A','L','S',0x0120,0x2001,0x2001,0x2001), + /* ALS200 */ + ISAPNP_ALS100('A','L','S',0x0200,0x0020,0x0020,0x0001), + /* RTL3000 */ + ISAPNP_ALS100('R','T','L',0x3000,0x2001,0x2001,0x2001), + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_als100_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-als100" + + +#ifdef __ISAPNP__ +static int __init snd_card_als100_isapnp(int dev, struct snd_card_als100 *acard) +{ + const struct isapnp_card_id *id = snd_als100_isapnp_id[dev]; + struct isapnp_card *card = snd_als100_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + acard->devopl = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->devopl->active) { + acard->dev = acard->devmpu = acard->devopl = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev], + 1); + if (snd_dma16[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma16[dev], + 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + snd_port[dev] = pdev->resource[0].start; + snd_dma8[dev] = pdev->dma_resource[1].start; + snd_dma16[dev] = pdev->dma_resource[0].start; + snd_irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev == NULL || pdev->prepare(pdev)<0) { + snd_mpu_port[dev] = -1; + return 0; + } + + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], + 2); + if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], + 1); + + if (pdev->activate(pdev)<0) { + snd_printk("MPU-401 isapnp configure failure\n"); + snd_mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + snd_mpu_port[dev] = pdev->resource[0].start; + snd_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + pdev = acard->devopl; + if (pdev == NULL || pdev->prepare(pdev)<0) { + snd_fm_port[dev] = -1; + return 0; + } + + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_fm_port[dev], 4); + + if (pdev->activate(pdev)<0) { + snd_printk("OPL isapnp configure failure\n"); + snd_fm_port[dev] = -1; + acard->devopl = NULL; + } else { + snd_fm_port[dev] = pdev->resource[0].start; + } + + return 0; +} + +static void snd_card_als100_deactivate(struct snd_card_als100 *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } + if (acard->devmpu) { + acard->devmpu->deactivate(acard->devmpu); + acard->devmpu = NULL; + } + if (acard->devopl) { + acard->devopl->deactivate(acard->devopl); + acard->devopl = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_card_als100_free(snd_card_t *card) +{ + struct snd_card_als100 *acard = (struct snd_card_als100 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_als100_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_als100_probe(int dev) +{ + int error; + sb_t *chip; + snd_card_t *card; + struct snd_card_als100 *acard; + opl3_t *opl3; + + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_als100))) == NULL) + return -ENOMEM; + acard = (struct snd_card_als100 *)card->private_data; + card->private_free = snd_card_als100_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_als100_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + snd_printk("you have to enable PnP support ...\n"); + snd_card_free(card); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_sbdsp_create(card, snd_port[dev], + snd_irq[dev], + snd_sb16dsp_interrupt, + snd_dma8[dev], + snd_dma16[dev], + SB_HW_ALS100, &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return error; + } + + if (snd_mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_ALS100, + snd_mpu_port[dev], 0, + snd_mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + snd_printk("no MPU-401 device at 0x%lx\n", snd_mpu_port[dev]); + } + + if (snd_fm_port[dev] > 0) { + if (snd_opl3_create(card, + snd_fm_port[dev], snd_fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + snd_printk("no OPL device at 0x%lx-0x%lx\n", + snd_fm_port[dev], snd_fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "ALS100"); + strcpy(card->shortname, "Avance Logic ALS100"); + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d&%d", + card->shortname, chip->name, chip->port, + snd_irq[dev], snd_dma8[dev], snd_dma16[dev]); + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_als100_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_als100_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_als100_isapnp_cards[dev] = card; + snd_als100_isapnp_id[dev] = id; + res = snd_card_als100_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif + +static int __init alsa_card_als100_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_als100_pnpids, snd_als100_isapnp_detect); +#else + snd_printk("you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + snd_printk("no ALS100 based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_als100_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_als100_cards[dev]); +} + +module_init(alsa_card_als100_init) +module_exit(alsa_card_als100_exit) + +#ifndef MODULE + +/* format is: snd-als100=snd_enable,snd_index,snd_id,snd_port, + snd_mpu_port,snd_fm_port,snd_irq,snd_mpu_irq, + snd_dma8,snd_dma16 */ + +static int __init alsa_card_als100_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2 && + get_option(&str,&snd_dma16[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-als100=", alsa_card_als100_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/azt2320.c b/sound/isa/azt2320.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/azt2320.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,444 @@ + +/* + card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards. + Copyright (C) 1999-2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + This driver should provide support for most Aztech AZT2320 based cards. + Several AZT2316 chips are also supported/tested, but autoprobe doesn't + work: all module option have to be set. + + No docs available for us at Aztech headquarters !!! Unbelievable ... + No other help obtained. + + Thanks to Rainer Wiesner for the WSS + activation method (full-duplex audio!). +*/ + +#include +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t cs4231_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("Aztech Systems AZT2320"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Aztech Systems,PRO16V}," + "{Aztech Systems,AZT2320}," + "{Aztech Systems,AZT3300}," + "{Aztech Systems,AZT2320}," + "{Aztech Systems,AZT3000}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for azt2320 based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for azt2320 based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable azt2320 based soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_wss_port, "WSS Port # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_wss_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "1st DMA # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "2nd DMA # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); + +struct snd_card_azt2320 { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_azt2320_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_azt2320_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_azt2320_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_AZT2320(_va, _vb, _vc, _device, _audio, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401), } \ + } + +static struct isapnp_card_id snd_azt2320_pnpids[] __devinitdata = { + /* PRO16V */ + ISAPNP_AZT2320('A','Z','T',0x1008,0x1008,0x2001), + /* --- */ + ISAPNP_AZT2320('A','Z','T',0x2320,0x0001,0x0002), + /* Packard Bell Sound III 336 AM/SP */ + ISAPNP_AZT2320('A','Z','T',0x3000,0x1003,0x2001), + /* AT3300 */ + ISAPNP_AZT2320('A','Z','T',0x3002,0x1004,0x2001), + /* --- */ + ISAPNP_AZT2320('A','Z','T',0x3005,0x1003,0x2001), + /* --- */ + ISAPNP_AZT2320('A','Z','T',0x3011,0x1003,0x2001), + { ISAPNP_CARD_END, } /* end */ +}; + +ISAPNP_CARD_TABLE(snd_azt2320_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-azt2320" + + +#ifdef __ISAPNP__ +static int __init snd_card_azt2320_isapnp(int dev, struct snd_card_azt2320 *acard) +{ + const struct isapnp_card_id *id = snd_azt2320_isapnp_id[dev]; + struct isapnp_card *card = snd_azt2320_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4); + if (snd_wss_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_wss_port[dev], + 4); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], + 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], + 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + + if (pdev->activate(pdev) < 0) { + snd_printk("AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + snd_port[dev] = pdev->resource[0].start; + snd_fm_port[dev] = pdev->resource[1].start; + snd_wss_port[dev] = pdev->resource[2].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev == NULL || pdev->prepare(pdev) < 0) { + snd_mpu_port[dev] = -1; + return 0; + } + + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], + 2); + if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], + 1); + + if (pdev->activate(pdev) < 0) { + /* not fatal error */ + snd_printk("MPU-401 isapnp configure failure\n"); + snd_mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + snd_mpu_port[dev] = pdev->resource[0].start; + snd_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + return 0; +} + +static void snd_card_azt2320_deactivate(struct snd_card_azt2320 *acard) +{ + if (acard->dev) + acard->dev->deactivate(acard->dev); + if (acard->devmpu) + acard->devmpu->deactivate(acard->devmpu); +} +#endif /* __ISAPNP__ */ + +/* same of snd_sbdsp_command by Jaroslav Kysela */ +static int __init snd_card_azt2320_command(unsigned long port, unsigned char val) +{ + int i; + unsigned long limit; + + limit = jiffies + HZ / 10; + for (i = 50000; i && (limit - jiffies) > 0; i--) + if (!(inb(port + 0x0c) & 0x80)) { + outb(val, port + 0x0c); + return 0; + } + return -EBUSY; +} + +static int __init snd_card_azt2320_enable_wss(unsigned long port) +{ + int error; + + if ((error = snd_card_azt2320_command(port, 0x09))) + return error; + if ((error = snd_card_azt2320_command(port, 0x00))) + return error; + + mdelay(5); + return 0; +} + +static void snd_card_azt2320_free(snd_card_t *card) +{ + struct snd_card_azt2320 *acard = (struct snd_card_azt2320 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_azt2320_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_azt2320_probe(int dev) +{ + int error; + snd_card_t *card; + struct snd_card_azt2320 *acard; + cs4231_t *chip; + opl3_t *opl3; + + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_azt2320))) == NULL) + return -ENOMEM; + acard = (struct snd_card_azt2320 *)card->private_data; + card->private_free = snd_card_azt2320_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_azt2320_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#endif /* __ISAPNP__ */ + + if ((error = snd_card_azt2320_enable_wss(snd_port[dev]))) { + snd_card_free(card); + return error; + } + + if ((error = snd_cs4231_create(card, snd_wss_port[dev], -1, + snd_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + CS4231_HW_DETECT, 0, &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_mixer(chip)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if (snd_mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320, + snd_mpu_port[dev], 0, + snd_mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + snd_printk("no MPU-401 device at 0x%lx\n", + snd_mpu_port[dev]); + } + + if (snd_fm_port[dev] > 0) { + if (snd_opl3_create(card, + snd_fm_port[dev], snd_fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + snd_printk("no OPL device at 0x%lx-0x%lx\n", + snd_fm_port[dev], snd_fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "AZT2320"); + strcpy(card->shortname, "Aztech AZT2320"); + sprintf(card->longname, "%s soundcard, WSS at 0x%lx, irq %i, dma %i&%i", + card->shortname, chip->port, snd_irq[dev], snd_dma1[dev], snd_dma2[dev]); + + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_azt2320_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_azt2320_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_azt2320_isapnp_cards[dev] = card; + snd_azt2320_isapnp_id[dev] = id; + res = snd_card_azt2320_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif + +static int __init alsa_card_azt2320_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_azt2320_pnpids, snd_azt2320_isapnp_detect); +#else + snd_printk("you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + snd_printk("no AZT2320 based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_azt2320_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_azt2320_cards[dev]); +} + +module_init(alsa_card_azt2320_init) +module_exit(alsa_card_azt2320_exit) + +#ifndef MODULE + +/* format is: snd-azt2320=snd_enable,snd_index,snd_id,snd_port, + snd_wss_port,snd_mpu_port,snd_fm_port, + snd_irq,snd_mpu_irq,snd_dma1,snd_dma2 */ + +static int __init alsa_card_azt2320_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_wss_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-azt2320=", alsa_card_azt2320_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/cmi8330.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,537 @@ +/* + * Driver for C-Media's CMI8330 soundcards. + * Copyright (c) by George Talusan + * http://www.undergrad.math.uwaterloo.ca/~gstalusa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * NOTES + * + * The extended registers contain mixer settings which are largely + * untapped for the time being. + * + * MPU401 and SPDIF are not supported yet. I don't have the hardware + * to aid in coding and testing, so I won't bother. + * + * To quickly load the module, + * + * modprobe -a snd-card-cmi8330 snd_sbport=0x220 snd_sbirq=5 snd_sbdma8=1 + * snd_sbdma16=5 snd_wssport=0x530 snd_wssirq=11 snd_wssdma=0 + * + * This card has two mixers and two PCM devices. I've cheesed it such + * that recording and playback can be done through the same device. + * The driver "magically" routes the capturing to the AD1848 codec, + * and playback to the SB16 codec. This allows for full-duplex mode + * to some extent. + * The utilities in alsa-utils are aware of both devices, so passing + * the appropriate parameters to amixer and alsactl will give you + * full control over both mixers. + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("George Talusan "); +MODULE_DESCRIPTION("C-Media CMI8330"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int snd_sbirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int snd_sbdma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static int snd_sbdma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static long snd_wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int snd_wssirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int snd_wssdma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for CMI8330 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for CMI8330 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable CMI8330 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +#endif + +MODULE_PARM(snd_sbport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_sbport, "Port # for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(snd_sbport, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list"); +MODULE_PARM(snd_sbirq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_sbirq, "IRQ # for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(snd_sbirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{5},dialog:list"); +MODULE_PARM(snd_sbdma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_sbdma8, "DMA8 for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(snd_sbdma8, SNDRV_DMA8_DESC ",prefers:{1}"); +MODULE_PARM(snd_sbdma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_sbdma16, "DMA16 for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(snd_sbdma16, SNDRV_ENABLED ",allows:{{5},{7}},prefers:{5},dialog:list"); + +MODULE_PARM(snd_wssport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_wssport, "Port # for CMI8330 WSS driver."); +MODULE_PARM_SYNTAX(snd_wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80,0xf40,0xc0}},prefers:{0x530},base:16,dialog:list"); +MODULE_PARM(snd_wssirq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_wssirq, "IRQ # for CMI8330 WSS driver."); +MODULE_PARM_SYNTAX(snd_wssirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{11},dialog:list"); +MODULE_PARM(snd_wssdma, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_wssdma, "DMA for CMI8330 WSS driver."); +MODULE_PARM_SYNTAX(snd_wssdma, SNDRV_DMA8_DESC ",prefers:{0}"); + +#define CMI8330_RMUX3D 16 +#define CMI8330_MUTEMUX 17 +#define CMI8330_OUTPUTVOL 18 +#define CMI8330_MASTVOL 19 +#define CMI8330_LINVOL 20 +#define CMI8330_CDINVOL 21 +#define CMI8330_WAVVOL 22 +#define CMI8330_RECMUX 23 +#define CMI8330_WAVGAIN 24 +#define CMI8330_LINGAIN 25 +#define CMI8330_CDINGAIN 26 + +static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] = +{ + 0x0, /* 16 - recording mux */ + 0x40, /* 17 - mute mux */ + 0x0, /* 18 - vol */ + 0x0, /* 19 - master volume */ + 0x0, /* 20 - line-in volume */ + 0x0, /* 21 - cd-in volume */ + 0x0, /* 22 - wave volume */ + 0x0, /* 23 - mute/rec mux */ + 0x0, /* 24 - wave rec gain */ + 0x0, /* 25 - line-in rec gain */ + 0x0 /* 26 - cd-in rec gain */ +}; + +struct snd_cmi8330 { +#ifdef __ISAPNP__ + struct isapnp_dev *cap; + struct isapnp_dev *play; +#endif +}; + +static snd_card_t *snd_cmi8330_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_cmi8330_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_cmi8330_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_CMI8330(_va, _vb, _vc, _device, _audio1, _audio2) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID('@', '@', '@', _audio1), \ + ISAPNP_DEVICE_ID('@', 'X', '@', _audio2), } \ + } + +static struct isapnp_card_id snd_cmi8330_pnpids[] __devinitdata = +{ + ISAPNP_CMI8330('C','M','I',0x0001,0x0001,0x0001), + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_cmi8330_pnpids); + +#endif + +#define CMI8330_CONTROLS (sizeof(snd_cmi8330_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cmi8330_controls[] __devinitdata = { +AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0), +AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1), +AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), +AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 0), +AD1848_DOUBLE("Line Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0), +AD1848_DOUBLE("Line Playback Volume", 0, CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0), +AD1848_DOUBLE("Line Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0), +AD1848_DOUBLE("Line Capture Volume", 0, CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0), +AD1848_DOUBLE("CD Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0), +AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0), +AD1848_DOUBLE("CD Playback Volume", 0, CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0), +AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0), +AD1848_SINGLE("Mic Playback Switch", 0, CMI8330_MUTEMUX, 0, 1, 0), +AD1848_SINGLE("Mic Playback Volume", 0, CMI8330_OUTPUTVOL, 0, 7, 0), +AD1848_SINGLE("Mic Capture Switch", 0, CMI8330_RMUX3D, 0, 1, 0), +AD1848_SINGLE("Mic Capture Volume", 0, CMI8330_OUTPUTVOL, 5, 7, 0), +AD1848_DOUBLE("Wavetable Playback Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0), +AD1848_DOUBLE("Wavetable Playback Volume", 0, CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0), +AD1848_DOUBLE("Wavetable Capture Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0), +AD1848_DOUBLE("Wavetable Capture Volume", 0, CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0), +AD1848_SINGLE("3D Control - Switch", 0, CMI8330_RMUX3D, 5, 1, 1), +AD1848_SINGLE("PC Speaker Playback Volume", 0, CMI8330_OUTPUTVOL, 3, 3, 0), +AD1848_SINGLE("FM Playback Switch", 0, CMI8330_RECMUX, 3, 1, 1), +AD1848_SINGLE("IEC958 Input Capture Switch", 0, CMI8330_RMUX3D, 7, 1, 1), +AD1848_SINGLE("IEC958 Input Playback Switch", 0, CMI8330_MUTEMUX, 7, 1, 1), +}; + +static int __init snd_cmi8330_mixer(snd_card_t *card, ad1848_t *chip) +{ + int idx, err; + + strcpy(card->mixername, "CMI8330/C3D"); + + for (idx = 0; idx < CMI8330_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmi8330_controls[idx], chip))) < 0) + return err; + + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_cmi8330_isapnp(int dev, struct snd_cmi8330 *acard) +{ + const struct isapnp_card_id *id = snd_cmi8330_isapnp_id[dev]; + struct isapnp_card *card = snd_cmi8330_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->cap = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->cap->active) { + acard->cap = NULL; + return -EBUSY; + } + acard->play = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->play->active) { + acard->cap = acard->play = NULL; + return -EBUSY; + } + + pdev = acard->cap; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + /* allocate AD1848 resources */ + if (snd_wssport[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_wssport[dev], 8); + if (snd_wssdma[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_wssdma[dev], 1); + if (snd_wssirq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_wssirq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("(AD1848) PnP configure failure\n"); + return -EBUSY; + } + snd_wssport[dev] = pdev->resource[0].start; + snd_wssdma[dev] = pdev->dma_resource[0].start; + snd_wssirq[dev] = pdev->irq_resource[0].start; + + /* allocate SB16 resources */ + pdev = acard->play; + if (pdev->prepare(pdev)<0) { + acard->cap->deactivate(acard->cap); + return -EAGAIN; + } + if (snd_sbport[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_sbport[dev], 16); + if (snd_sbdma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_sbdma8[dev], 1); + if (snd_sbdma16[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_sbdma16[dev], 1); + if (snd_sbirq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_sbirq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("CMI8330/C3D (SB16) PnP configure failure\n"); + acard->cap->deactivate(acard->cap); + return -EBUSY; + } + snd_sbport[dev] = pdev->resource[0].start; + snd_sbdma8[dev] = pdev->dma_resource[0].start; + snd_sbdma16[dev] = pdev->dma_resource[1].start; + snd_sbirq[dev] = pdev->irq_resource[0].start; + + return 0; +} + +static void snd_cmi8330_deactivate(struct snd_cmi8330 *acard) +{ + if (acard->cap) { + acard->cap->deactivate(acard->cap); + acard->cap = NULL; + } + if (acard->play) { + acard->play->deactivate(acard->play); + acard->play = NULL; + } +} +#endif + +static void snd_cmi8330_free(snd_card_t *card) +{ + struct snd_cmi8330 *acard = (struct snd_cmi8330 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_cmi8330_deactivate(acard); +#endif + } +} + +static int __init snd_cmi8330_probe(int dev) +{ + snd_card_t *card; + struct snd_cmi8330 *acard; + ad1848_t *chip_wss; + sb_t *chip_sb; + unsigned long flags; + int i, err; + snd_pcm_t *pcm, *wss_pcm, *sb_pcm; + snd_pcm_str_t *pstr; + +#ifdef __ISAPNP__ + if (!snd_isapnp[dev]) { +#endif + if (snd_wssport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_wssport\n"); + return -EINVAL; + } + if (snd_sbport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_sbport\n"); + return -EINVAL; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_cmi8330)); + if (card == NULL) { + snd_printk("could not get a new card\n"); + return -ENOMEM; + } + acard = (struct snd_cmi8330 *)card->private_data; + card->private_free = snd_cmi8330_free; + +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && (err = snd_cmi8330_isapnp(dev, acard)) < 0) { + snd_printk("PnP detection failed\n"); + snd_card_free(card); + return err; + } +#endif + + if ((err = snd_ad1848_create(card, + snd_wssport[dev] + 4, + snd_wssirq[dev], + snd_wssdma[dev], + AD1848_HW_DETECT, + &chip_wss)) < 0) { + snd_printk("(AD1848) device busy??\n"); + snd_card_free(card); + return err; + } + if (chip_wss->hardware != AD1848_HW_CMI8330) { + snd_printk("(AD1848) not found during probe\n"); + snd_card_free(card); + return -ENODEV; + } + if ((err = snd_ad1848_pcm(chip_wss, 0, &wss_pcm)) < 0) { + snd_printk("(AD1848) no enough memory??\n"); + snd_card_free(card); + return err; + } + + if ((err = snd_sbdsp_create(card, snd_sbport[dev], + snd_sbirq[dev], + snd_sb16dsp_interrupt, + snd_sbdma8[dev], + snd_sbdma16[dev], + SB_HW_AUTO, &chip_sb)) < 0) { + snd_printk("(SB16) device busy??\n"); + snd_card_free(card); + return err; + } + if ((err = snd_sb16dsp_pcm(chip_sb, 1, &sb_pcm)) < 0) { + snd_printk("(SB16) no enough memory??\n"); + snd_card_free(card); + return err; + } + + if (chip_sb->hardware != SB_HW_16) { + snd_printk("(SB16) not found during probe\n"); + snd_card_free(card); + return -ENODEV; + } + + memcpy(&chip_wss->image[16], &snd_cmi8330_image, sizeof(snd_cmi8330_image)); + + spin_lock_irqsave(&chip_wss->reg_lock, flags); + snd_ad1848_out(chip_wss, AD1848_MISC_INFO, /* switch on MODE2 */ + chip_wss->image[AD1848_MISC_INFO] |= 0x40); + spin_unlock_irqrestore(&chip_wss->reg_lock, flags); + + if ((err = snd_cmi8330_mixer(card, chip_wss)) < 0) { + snd_printk("failed to create mixers\n"); + snd_card_free(card); + return err; + } + spin_lock_irqsave(&chip_wss->reg_lock, flags); + for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++) + snd_ad1848_out(chip_wss, i, chip_wss->image[i]); + spin_unlock_irqrestore(&chip_wss->reg_lock, flags); + + /* + * KLUDGE ALERT + * disable AD1848 playback + * disable SB16 capture + */ + pcm = wss_pcm; + pstr = &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; + snd_magic_kfree(pstr->substream); + pstr->substream = 0; + pstr->substream_count = 0; + + pcm = sb_pcm; + pstr = &pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; + snd_magic_kfree(pstr->substream); + pstr->substream = 0; + pstr->substream_count = 0; + + strcpy(card->driver, "CMI8330/C3D"); + strcpy(card->shortname, "C-Media CMI8330/C3D"); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + wss_pcm->name, + chip_wss->port, + snd_wssirq[dev], + snd_wssdma[dev]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + snd_cmi8330_cards[dev] = card; + return 0; +} + +static void __exit alsa_card_cmi8330_exit(void) +{ + int i; + + for (i = 0; i < SNDRV_CARDS; i++) + snd_card_free(snd_cmi8330_cards[i]); +} + +#ifdef __ISAPNP__ +static int __init snd_cmi8330_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || !snd_isapnp[dev]) + continue; + snd_cmi8330_isapnp_cards[dev] = card; + snd_cmi8330_isapnp_id[dev] = id; + res = snd_cmi8330_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_cmi8330_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (snd_cmi8330_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_cmi8330_pnpids, snd_cmi8330_isapnp_detect); +#endif + + if (!cards) { +#ifdef MODULE + snd_printk("CMI8330 not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +module_init(alsa_card_cmi8330_init) +module_exit(alsa_card_cmi8330_exit) + +#ifndef MODULE + +/* format is: snd-cmi8330=snd_enable,snd_index,snd_id,snd_isapnp, + snd_sbport,snd_sbirq, + snd_sbdma8,snd_sbdma16, + snd_wssport,snd_wssirq, + snd_wssdma */ + +static int __init alsa_card_cmi8330_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_sbport[nr_dev]) == 2 && + get_option(&str,&snd_sbirq[nr_dev]) == 2 && + get_option(&str,&snd_sbdma8[nr_dev]) == 2 && + get_option(&str,&snd_sbdma16[nr_dev]) == 2 && + get_option(&str,(int *)&snd_wssport[nr_dev]) == 2 && + get_option(&str,&snd_wssirq[nr_dev]) == 2 && + get_option(&str,&snd_wssdma[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +__setup("snd-cmi8330=", alsa_card_cmi8330_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/cs423x/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,46 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _cs423x.o + +list-multi := snd-cs4231-lib.o snd-cs4236-lib.o \ + snd-cs4231.o snd-cs4232.o snd-cs4236.o + +export-objs := cs4231_lib.o cs4236_lib.o + +snd-cs4231-lib-objs := cs4231_lib.o +snd-cs4236-lib-objs := cs4236_lib.o +snd-cs4231-objs := cs4231.o +snd-cs4232-objs := cs4232.o +snd-cs4236-objs := cs4236.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_AZT2320) += snd-cs4231-lib.o +obj-$(CONFIG_SND_OPL3SA2) += snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4231) += snd-cs4231.o snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4232) += snd-cs4232.o snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o snd-cs4231-lib.o +obj-$(CONFIG_SND_GUSMAX) += snd-cs4231-lib.o +obj-$(CONFIG_SND_INTERWAVE) += snd-cs4231-lib.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-cs4231-lib.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-cs4231-lib.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-cs4231-lib.o + +include $(TOPDIR)/Rules.make + +snd-cs4231-lib.o: $(snd-cs4231-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4231-lib-objs) + +snd-cs4236-lib.o: $(snd-cs4236-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4236-lib-objs) + +snd-cs4231.o: $(snd-cs4231-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4231-objs) + +snd-cs4232.o: $(snd-cs4232-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4232-objs) + +snd-cs4236.o: $(snd-cs4236-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4236-objs) diff -Nru a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/cs423x/cs4231.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,211 @@ +/* + * Generic driver for CS4231 chips + * Copyright (c) by Jaroslav Kysela + * Originally the CS4232/CS4232A driver, modified for use on CS4231 by + * Tugrul Galatali + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t cs4231_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Generic CS4231"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Crystal Semiconductors,CS4231}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for CS4231 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for CS4231 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable CS4231 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); + +static snd_card_t *snd_cs4231_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_card_cs4231_probe(int dev) +{ + snd_card_t *card; + struct snd_card_cs4231 *acard; + snd_pcm_t *pcm = NULL; + cs4231_t *chip; + int err; + + if (snd_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_port\n"); + return -EINVAL; + } + if (snd_irq[dev] == SNDRV_AUTO_IRQ) { + snd_printk("specify snd_irq\n"); + return -EINVAL; + } + if (snd_dma1[dev] == SNDRV_AUTO_DMA) { + snd_printk("specify snd_dma1\n"); + return -EINVAL; + } + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_card_cs4231 *)card->private_data; + if (snd_mpu_port[dev] < 0) + snd_mpu_port[dev] = SNDRV_AUTO_PORT; + if ((err = snd_cs4231_create(card, snd_port[dev], -1, + snd_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + CS4231_HW_DETECT, + 0, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_mpu_irq[dev] >= 0 && snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, + snd_mpu_port[dev], 0, + snd_mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + snd_printk("MPU401 not detected\n"); + } + strcpy(card->driver, "CS4231"); + strcpy(card->shortname, pcm->name); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + pcm->name, chip->port, snd_irq[dev], snd_dma1[dev]); + if (snd_dma2[dev] >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_cs4231_cards[dev] = card; + return 0; +} + +static int __init alsa_card_cs4231_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_card_cs4231_probe(dev) >= 0) + cards++; + } + if (!cards) { +#ifdef MODULE + snd_printk("CS4231 soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_cs4231_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_cs4231_cards[idx]); +} + +module_init(alsa_card_cs4231_init) +module_exit(alsa_card_cs4231_exit) + +#ifndef MODULE + +/* format is: snd-cs4231=snd_enable,snd_index,snd_id, + snd_port,snd_mpu_port,snd_irq,snd_mpu_irq, + snd_dma1,snd_dma2 */ + +static int __init alsa_card_cs4231_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cs4231=", alsa_card_cs4231_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/cs423x/cs4231_lib.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1837 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of CS4231(A)/CS4232/InterWave & compatible chips + * + * Bugs: + * - sometimes record brokes playback with WSS portion of + * Yamaha OPL3-SA3 chip + * - CS4231 (GUS MAX) - still trouble with occasional noises + * - broken initialization? + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips"); +MODULE_LICENSE("GPL"); + +#define chip_t cs4231_t + +#if 0 +#define SNDRV_DEBUG_MCE +#endif + +/* + * Some variables + */ + +static unsigned char freq_bits[14] = { + /* 5510 */ 0x00 | CS4231_XTAL2, + /* 6620 */ 0x0E | CS4231_XTAL2, + /* 8000 */ 0x00 | CS4231_XTAL1, + /* 9600 */ 0x0E | CS4231_XTAL1, + /* 11025 */ 0x02 | CS4231_XTAL2, + /* 16000 */ 0x02 | CS4231_XTAL1, + /* 18900 */ 0x04 | CS4231_XTAL2, + /* 22050 */ 0x06 | CS4231_XTAL2, + /* 27042 */ 0x04 | CS4231_XTAL1, + /* 32000 */ 0x06 | CS4231_XTAL1, + /* 33075 */ 0x0C | CS4231_XTAL2, + /* 37800 */ 0x08 | CS4231_XTAL2, + /* 44100 */ 0x0A | CS4231_XTAL2, + /* 48000 */ 0x0C | CS4231_XTAL1 +}; + +static unsigned int rates[14] = { + 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, + 27042, 32000, 33075, 37800, 44100, 48000 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + count: 14, + list: rates, + mask: 0, +}; + +static int snd_cs4231_xrate(snd_pcm_runtime_t *runtime) +{ + return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); +} + +static unsigned char snd_cs4231_original_image[32] = +{ + 0x00, /* 00/00 - lic */ + 0x00, /* 01/01 - ric */ + 0x9f, /* 02/02 - la1ic */ + 0x9f, /* 03/03 - ra1ic */ + 0x9f, /* 04/04 - la2ic */ + 0x9f, /* 05/05 - ra2ic */ + 0xbf, /* 06/06 - loc */ + 0xbf, /* 07/07 - roc */ + 0x20, /* 08/08 - pdfr */ + CS4231_AUTOCALIB, /* 09/09 - ic */ + 0x00, /* 0a/10 - pc */ + 0x00, /* 0b/11 - ti */ + CS4231_MODE2, /* 0c/12 - mi */ + 0xfc, /* 0d/13 - lbc */ + 0x00, /* 0e/14 - pbru */ + 0x00, /* 0f/15 - pbrl */ + 0x80, /* 10/16 - afei */ + 0x01, /* 11/17 - afeii */ + 0x9f, /* 12/18 - llic */ + 0x9f, /* 13/19 - rlic */ + 0x00, /* 14/20 - tlb */ + 0x00, /* 15/21 - thb */ + 0x00, /* 16/22 - la3mic/reserved */ + 0x00, /* 17/23 - ra3mic/reserved */ + 0x00, /* 18/24 - afs */ + 0x00, /* 19/25 - lamoc/version */ + 0xcf, /* 1a/26 - mioc */ + 0x00, /* 1b/27 - ramoc/reserved */ + 0x20, /* 1c/28 - cdfr */ + 0x00, /* 1d/29 - res4 */ + 0x00, /* 1e/30 - cbru */ + 0x00, /* 1f/31 - cbrl */ +}; + +/* + * Basic I/O functions + */ + +void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, + unsigned char mask, unsigned char value) +{ + int timeout; + unsigned char tmp; + + for (timeout = 250; + timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + if (chip->calibrate_mute) { + chip->image[reg] &= mask; + chip->image[reg] |= value; + } else { + outb(chip->mce_bit | reg, CS4231P(chip, REGSEL)); + mb(); + tmp = (chip->image[reg] & mask) | value; + outb(tmp, CS4231P(chip, REG)); + chip->image[reg] = tmp; + mb(); + } +} + +static void snd_cs4231_dout(cs4231_t *chip, unsigned char reg, unsigned char value) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(10); + outb(chip->mce_bit | reg, CS4231P(chip, REGSEL)); + outb(value, CS4231P(chip, REG)); + mb(); +} + +void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char value) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + outb(chip->mce_bit | reg, CS4231P(chip, REGSEL)); + outb(value, CS4231P(chip, REG)); + chip->image[reg] = value; + mb(); +#if 0 + printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value); +#endif +} + +unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("in: auto calibration time out - reg = 0x%x\n", reg); +#endif + outb(chip->mce_bit | reg, CS4231P(chip, REGSEL)); + mb(); + return inb(CS4231P(chip, REG)); +} + +void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val) +{ + outb(chip->mce_bit | 0x17, CS4231P(chip, REGSEL)); + outb(reg | (chip->image[CS4236_EXT_REG] & 0x01), CS4231P(chip, REG)); + outb(val, CS4231P(chip, REG)); + chip->eimage[CS4236_REG(reg)] = val; +#if 0 + printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val); +#endif +} + +unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg) +{ + outb(chip->mce_bit | 0x17, CS4231P(chip, REGSEL)); + outb(reg | (chip->image[CS4236_EXT_REG] & 0x01), CS4231P(chip, REG)); +#if 1 + return inb(CS4231P(chip, REG)); +#else + { + unsigned char res; + res = inb(CS4231P(chip, REG)); + printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res); + return res; + } +#endif +} + +#ifdef CONFIG_SND_DEBUG + +void snd_cs4231_debug(cs4231_t *chip) +{ + printk("CS4231 REGS: INDEX = 0x%02x ", inb(CS4231P(chip, REGSEL))); + printk(" STATUS = 0x%02x\n", inb(CS4231P(chip, STATUS))); + printk(" 0x00: left input = 0x%02x ", snd_cs4231_in(chip, 0x00)); + printk(" 0x10: alt 1 (CFIG 2) = 0x%02x\n", snd_cs4231_in(chip, 0x10)); + printk(" 0x01: right input = 0x%02x ", snd_cs4231_in(chip, 0x01)); + printk(" 0x11: alt 2 (CFIG 3) = 0x%02x\n", snd_cs4231_in(chip, 0x11)); + printk(" 0x02: GF1 left input = 0x%02x ", snd_cs4231_in(chip, 0x02)); + printk(" 0x12: left line in = 0x%02x\n", snd_cs4231_in(chip, 0x12)); + printk(" 0x03: GF1 right input = 0x%02x ", snd_cs4231_in(chip, 0x03)); + printk(" 0x13: right line in = 0x%02x\n", snd_cs4231_in(chip, 0x13)); + printk(" 0x04: CD left input = 0x%02x ", snd_cs4231_in(chip, 0x04)); + printk(" 0x14: timer low = 0x%02x\n", snd_cs4231_in(chip, 0x14)); + printk(" 0x05: CD right input = 0x%02x ", snd_cs4231_in(chip, 0x05)); + printk(" 0x15: timer high = 0x%02x\n", snd_cs4231_in(chip, 0x15)); + printk(" 0x06: left output = 0x%02x ", snd_cs4231_in(chip, 0x06)); + printk(" 0x16: left MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x16)); + printk(" 0x07: right output = 0x%02x ", snd_cs4231_in(chip, 0x07)); + printk(" 0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17)); + printk(" 0x08: playback format = 0x%02x ", snd_cs4231_in(chip, 0x08)); + printk(" 0x18: IRQ status = 0x%02x\n", snd_cs4231_in(chip, 0x18)); + printk(" 0x09: iface (CFIG 1) = 0x%02x ", snd_cs4231_in(chip, 0x09)); + printk(" 0x19: left line out = 0x%02x\n", snd_cs4231_in(chip, 0x19)); + printk(" 0x0a: pin control = 0x%02x ", snd_cs4231_in(chip, 0x0a)); + printk(" 0x1a: mono control = 0x%02x\n", snd_cs4231_in(chip, 0x1a)); + printk(" 0x0b: init & status = 0x%02x ", snd_cs4231_in(chip, 0x0b)); + printk(" 0x1b: right line out = 0x%02x\n", snd_cs4231_in(chip, 0x1b)); + printk(" 0x0c: revision & mode = 0x%02x ", snd_cs4231_in(chip, 0x0c)); + printk(" 0x1c: record format = 0x%02x\n", snd_cs4231_in(chip, 0x1c)); + printk(" 0x0d: loopback = 0x%02x ", snd_cs4231_in(chip, 0x0d)); + printk(" 0x1d: var freq (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x1d)); + printk(" 0x0e: ply upr count = 0x%02x ", snd_cs4231_in(chip, 0x0e)); + printk(" 0x1e: ply lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1e)); + printk(" 0x0f: rec upr count = 0x%02x ", snd_cs4231_in(chip, 0x0f)); + printk(" 0x1f: rec lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1f)); +} + +#endif + +/* + * CS4231 detection / MCE routines + */ + +static void snd_cs4231_busy_wait(cs4231_t *chip) +{ + int timeout; + + /* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */ + for (timeout = 5; timeout > 0; timeout--) + inb(CS4231P(chip, REGSEL)); + /* end of cleanup sequence */ + for (timeout = 250; + timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(10); +} + +void snd_cs4231_mce_up(cs4231_t *chip) +{ + unsigned long flags; + int timeout; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (timeout = 250; timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("mce_up - auto calibration time out (0)\n"); +#endif + chip->mce_bit |= CS4231_MCE; + timeout = inb(CS4231P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); + if (!(timeout & CS4231_MCE)) + outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL)); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +void snd_cs4231_mce_down(cs4231_t *chip) +{ + unsigned long flags; + int timeout; + signed long time; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_busy_wait(chip); +#if 0 + printk("(1) timeout = %i\n", timeout); +#endif +#ifdef CONFIG_SND_DEBUG + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", CS4231P(chip, REGSEL)); +#endif + chip->mce_bit &= ~CS4231_MCE; + timeout = inb(CS4231P(chip, REGSEL)); + outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); + if ((timeout & CS4231_MCE) == 0 || + !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + snd_cs4231_busy_wait(chip); + + /* calibration process */ + + for (timeout = 500; timeout > 0 && (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0; timeout--) + udelay(10); + if ((snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0) { + snd_printd("cs4231_mce_down - auto calibration time out (1)\n"); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } +#if 0 + printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); +#endif + time = HZ / 4; + while (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (2)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->reg_lock, flags); + } +#if 0 + printk("(3) jiffies = %li\n", jiffies); +#endif + time = HZ / 10; + while (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (3)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->reg_lock, flags); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + printk("(4) jiffies = %li\n", jiffies); + snd_printk("mce_down - exit = 0x%x\n", inb(CS4231P(chip, REGSEL))); +#endif +} + +static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size) +{ + switch (format & 0xe0) { + case CS4231_LINEAR_16: + case CS4231_LINEAR_16_BIG: + size >>= 1; + break; + case CS4231_ADPCM_16: + return size >> 2; + } + if (format & CS4231_STEREO) + size >>= 1; + return size; +} + +static int snd_cs4231_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + +#if 0 + printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(CS4231P(card, STATUS))); +#endif + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == chip->playback_substream) { + what |= CS4231_PLAYBACK_ENABLE; + snd_pcm_trigger_done(s, substream); + } else if (s == chip->capture_substream) { + what |= CS4231_RECORD_ENABLE; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&chip->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) + chip->image[CS4231_IFACE_CTRL] |= what; + else + chip->image[CS4231_IFACE_CTRL] &= ~what; + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock(&chip->reg_lock); + break; + } + default: + result = -EINVAL; + break; + } +#if 0 + snd_cs4231_debug(chip); +#endif + return result; +} + +/* + * CODEC I/O + */ + +static unsigned char snd_cs4231_get_rate(unsigned int rate) +{ + int i; + + for (i = 0; i < 14; i++) + if (rate == rates[i]) + return freq_bits[i]; + // snd_BUG(); + return freq_bits[13]; +} + +static unsigned char snd_cs4231_get_format(cs4231_t *chip, + int format, + int channels) +{ + unsigned char rformat; + + rformat = CS4231_LINEAR_8; + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: rformat = CS4231_ULAW_8; break; + case SNDRV_PCM_FORMAT_A_LAW: rformat = CS4231_ALAW_8; break; + case SNDRV_PCM_FORMAT_S16_LE: rformat = CS4231_LINEAR_16; break; + case SNDRV_PCM_FORMAT_S16_BE: rformat = CS4231_LINEAR_16_BIG; break; + case SNDRV_PCM_FORMAT_IMA_ADPCM: rformat = CS4231_ADPCM_16; break; + } + if (channels > 1) + rformat |= CS4231_STEREO; +#if 0 + snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); +#endif + return rformat; +} + +static void snd_cs4231_calibrate_mute(cs4231_t *chip, int mute) +{ + unsigned long flags; + + mute = mute ? 1 : 0; + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->calibrate_mute == mute) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + if (!mute) { + snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]); + } + snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]); + snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]); + snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]); + if (chip->hardware == CS4231_HW_INTERWAVE) { + snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]); + snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]); + } + chip->calibrate_mute = mute; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4231_playback_format(cs4231_t *chip, + snd_pcm_hw_params_t *params, + unsigned char pdfr) +{ + unsigned long flags; + int full_calib = 1; + + down(&chip->mce_mutex); + snd_cs4231_calibrate_mute(chip, 1); + if (chip->hardware == CS4231_HW_CS4231A || + (chip->hardware & CS4231_HW_CS4232_MASK)) { + spin_lock_irqsave(&chip->reg_lock, flags); + if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) { /* rate is same? */ + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10); + full_calib = 0; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + if (full_calib) { + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) { + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, + (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ? + (pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) : + pdfr); + } else { + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + } + snd_cs4231_calibrate_mute(chip, 0); + up(&chip->mce_mutex); +} + +static void snd_cs4231_capture_format(cs4231_t *chip, + snd_pcm_hw_params_t *params, + unsigned char cdfr) +{ + unsigned long flags; + int full_calib = 1; + + down(&chip->mce_mutex); + snd_cs4231_calibrate_mute(chip, 1); + if (chip->hardware == CS4231_HW_CS4231A || + (chip->hardware & CS4231_HW_CS4232_MASK)) { + spin_lock_irqsave(&chip->reg_lock, flags); + if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) || /* rate is same? */ + (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); + snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20); + full_calib = 0; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + if (full_calib) { + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->hardware != CS4231_HW_INTERWAVE) { + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, + ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) | + (cdfr & 0x0f)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + } + } + snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + } + snd_cs4231_calibrate_mute(chip, 0); + up(&chip->mce_mutex); +} + +/* + * Timer interface + */ + +static unsigned long snd_cs4231_timer_resolution(snd_timer_t * timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + if (chip->hardware & CS4231_HW_CS4236B_MASK) + return 14467; + else + return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920; +} + +static int snd_cs4231_timer_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned int ticks; + cs4231_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->reg_lock, flags); + ticks = timer->sticks; + if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 || + (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] || + (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) { + snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8)); + snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4231_timer_stop(snd_timer_t * timer) +{ + unsigned long flags; + cs4231_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static void snd_cs4231_init(cs4231_t *chip) +{ + unsigned long flags; + + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (1)\n"); +#endif + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO | + CS4231_CALIB_MODE); + chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB; + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (2)\n"); +#endif + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]); +#endif + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (4)\n"); +#endif + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (5)\n"); +#endif +} + +static int snd_cs4231_open(cs4231_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + if ((chip->mode & mode) || + ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) { + up(&chip->open_mutex); + return -EAGAIN; + } + if (chip->mode & CS4231_MODE_OPEN) { + chip->mode |= mode; + up(&chip->open_mutex); + return 0; + } + /* ok. now enable and ack CODEC IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | + CS4231_RECORD_IRQ | + CS4231_TIMER_IRQ); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE; + snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | + CS4231_RECORD_IRQ | + CS4231_TIMER_IRQ); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->mode = mode; + up(&chip->open_mutex); + return 0; +} + +static void snd_cs4231_close(cs4231_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + chip->mode &= ~mode; + if (chip->mode & CS4231_MODE_OPEN) { + up(&chip->open_mutex); + return; + } + snd_cs4231_calibrate_mute(chip, 1); + + /* disable IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE; + snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); + + /* now disable record & playback */ + + if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + } + + /* clear IRQ again */ + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_cs4231_calibrate_mute(chip, 0); + + chip->mode = 0; + up(&chip->open_mutex); +} + +/* + * timer open/close + */ + +static int snd_cs4231_timer_open(snd_timer_t * timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + snd_cs4231_open(chip, CS4231_MODE_TIMER); + return 0; +} + +static int snd_cs4231_timer_close(snd_timer_t * timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + snd_cs4231_close(chip, CS4231_MODE_TIMER); + return 0; +} + +static struct _snd_timer_hardware snd_cs4231_timer_table = +{ + flags: SNDRV_TIMER_HW_AUTO, + resolution: 9945, + ticks: 65535, + open: snd_cs4231_timer_open, + close: snd_cs4231_timer_close, + c_resolution: snd_cs4231_timer_resolution, + start: snd_cs4231_timer_start, + stop: snd_cs4231_timer_stop, +}; + +/* + * ok.. exported functions.. + */ + +static int snd_cs4231_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + unsigned char new_pdfr; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | + snd_cs4231_get_rate(params_rate(hw_params)); + chip->set_playback_format(chip, hw_params, new_pdfr); + return 0; +} + +static int snd_cs4231_playback_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_cs4231_playback_prepare(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->p_dma_size = size; + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO); + snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1; + snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); + snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + snd_cs4231_debug(chip); +#endif + return 0; +} + +static int snd_cs4231_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + unsigned char new_cdfr; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | + snd_cs4231_get_rate(params_rate(hw_params)); + chip->set_capture_format(chip, hw_params, new_cdfr); + return 0; +} + +static int snd_cs4231_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_cs4231_capture_prepare(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->c_dma_size = size; + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); + snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1; + if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { + snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); + snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); + } else { + snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count); + snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8)); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static void snd_cs4231_overrange(cs4231_t *chip) +{ + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&chip->reg_lock, flags); + res = snd_cs4231_in(chip, CS4231_TEST_INIT); + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */ + chip->capture_substream->runtime->overrange++; +} + +void snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, dev_id, return); + unsigned char status; + + status = snd_cs4231_in(chip, CS4231_IRQ_STATUS); + if (status & CS4231_TIMER_IRQ) { + if (chip->timer) + snd_timer_interrupt(chip->timer, chip->timer->sticks); + } + if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { + if (status & CS4231_PLAYBACK_IRQ) { + if (chip->mode & CS4231_MODE_PLAY) + snd_pcm_period_elapsed(chip->playback_substream); + if (chip->mode & CS4231_MODE_RECORD) { + snd_cs4231_overrange(chip); + snd_pcm_period_elapsed(chip->capture_substream); + } + } + } else { + if (status & CS4231_PLAYBACK_IRQ) + snd_pcm_period_elapsed(chip->playback_substream); + if (status & CS4231_RECORD_IRQ) { + snd_cs4231_overrange(chip); + snd_pcm_period_elapsed(chip->capture_substream); + } + } + + spin_lock(&chip->reg_lock); + snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0); + spin_unlock(&chip->reg_lock); +} + +static snd_pcm_uframes_t snd_cs4231_playback_pointer(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) + return 0; + ptr = chip->p_dma_size - snd_dma_residue(chip->dma1); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_cs4231_capture_pointer(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)) + return 0; + ptr = chip->c_dma_size - snd_dma_residue(chip->dma2); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static int snd_cs4231_probe(cs4231_t *chip) +{ + unsigned long flags; + int i, id, rev; + unsigned char *ptr; + unsigned int hw; + +#if 0 + snd_cs4231_debug(chip); +#endif + id = 0; + for (i = 0; i < 50; i++) { + mb(); + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + udelay(2000); + else { + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2); + id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (id == 0x0a) + break; /* this is valid value */ + } + } + snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id); + if (id != 0x0a) + return -ENODEV; /* no valid device found */ + + if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { + rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7; + snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev); + if (rev == 0x80) { + chip->hardware = CS4231_HW_CS4231; + } else if (rev == 0xa0) { + chip->hardware = CS4231_HW_CS4231A; + } else if (rev == 0xa2) { + chip->hardware = CS4231_HW_CS4232; + } else if (rev == 0xb2) { + chip->hardware = CS4231_HW_CS4232A; + } else if (rev == 0x83) { + chip->hardware = CS4231_HW_CS4236; + } else if (rev == 0x03) { + chip->hardware = CS4231_HW_CS4236B; + } else { + snd_printk("unknown CS chip with version 0x%x\n", rev); + return -ENODEV; /* unknown CS4231 chip? */ + } + } + spin_lock_irqsave(&chip->reg_lock, flags); + inb(CS4231P(chip, STATUS)); /* clear any pendings IRQ */ + outb(0, CS4231P(chip, STATUS)); + mb(); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->image[CS4231_MISC_INFO] = CS4231_MODE2; + switch (chip->hardware) { + case CS4231_HW_INTERWAVE: + chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3; + break; + case CS4231_HW_CS4235: + case CS4231_HW_CS4236B: + case CS4231_HW_CS4237B: + case CS4231_HW_CS4238B: + case CS4231_HW_CS4239: + if (hw == CS4231_HW_DETECT3) + chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3; + else + chip->hardware = CS4231_HW_CS4236; + break; + } + + chip->image[CS4231_IFACE_CTRL] = + (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) | + (chip->single_dma ? CS4231_SINGLE_DMA : 0); + chip->image[CS4231_ALT_FEATURE_1] = 0x80; + chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01; + ptr = (unsigned char *) &chip->image; + snd_cs4231_mce_down(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 32; i++) /* ok.. fill all CS4231 registers */ + snd_cs4231_out(chip, i, *ptr++); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_up(chip); + snd_cs4231_mce_down(chip); + + mdelay(2); + + /* ok.. try check hardware version for CS4236+ chips */ + if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { + if (chip->hardware == CS4231_HW_CS4236B) { + rev = snd_cs4236_ext_in(chip, CS4236_VERSION); + snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff); + id = snd_cs4236_ext_in(chip, CS4236_VERSION); + snd_cs4236_ext_out(chip, CS4236_VERSION, rev); + snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id); + if ((id & 0x1f) == 0x1d) { /* CS4235 */ + chip->hardware = CS4231_HW_CS4235; + switch (id >> 5) { + case 4: + case 5: + case 6: + break; + default: + snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x0b) { /* CS4236/B */ + switch (id >> 5) { + case 4: + case 5: + case 6: + case 7: + chip->hardware = CS4231_HW_CS4236B; + break; + default: + snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x08) { /* CS4237B */ + chip->hardware = CS4231_HW_CS4237B; + switch (id >> 5) { + case 4: + case 5: + case 6: + case 7: + break; + default: + snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x09) { /* CS4238B */ + chip->hardware = CS4231_HW_CS4238B; + switch (id >> 5) { + case 5: + case 6: + case 7: + break; + default: + snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x1e) { /* CS4239 */ + chip->hardware = CS4231_HW_CS4239; + switch (id >> 5) { + case 4: + case 5: + case 6: + break; + default: + snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id); + } + } else { + snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id); + } + } + } + return 0; /* all things are ok.. */ +} + +/* + + */ + +static snd_pcm_hardware_t snd_cs4231_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5510, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_cs4231_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5510, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + + */ + +static int snd_cs4231_playback_open(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + runtime->hw = snd_cs4231_playback; + + /* hardware bug in InterWave chipset */ + if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3) + runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW; + + /* hardware limitation of cheap chips */ + if (chip->hardware == CS4231_HW_CS4235 || + chip->hardware == CS4231_HW_CS4239) + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; + + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); + + if (chip->claim_dma) { + if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0) + return err; + } + + if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) { + if (chip->release_dma) + chip->release_dma(chip, chip->dma_private_data, chip->dma1); + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return err; + } + chip->playback_substream = substream; + snd_pcm_set_sync(substream); + chip->rate_constraint(runtime); + return 0; +} + +static int snd_cs4231_capture_open(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + runtime->hw = snd_cs4231_capture; + + /* hardware limitation of cheap chips */ + if (chip->hardware == CS4231_HW_CS4235 || + chip->hardware == CS4231_HW_CS4239) + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; + + if (chip->claim_dma) { + if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0) + return err; + } + + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); + + if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) { + if (chip->release_dma) + chip->release_dma(chip, chip->dma_private_data, chip->dma2); + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return err; + } + chip->capture_substream = substream; + snd_pcm_set_sync(substream); + chip->rate_constraint(runtime); + return 0; +} + +static int snd_cs4231_playback_close(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_cs4231_close(chip, CS4231_MODE_PLAY); + return 0; +} + +static int snd_cs4231_capture_close(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_cs4231_close(chip, CS4231_MODE_RECORD); + return 0; +} + +#ifdef CONFIG_PM + +static void snd_cs4231_suspend(cs4231_t *chip) +{ + int reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) + chip->image[reg] = snd_cs4231_in(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4231_resume(cs4231_t *chip) +{ + int reg; + unsigned long flags; + int timeout; + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) { + switch (reg) { + case CS4231_VERSION: + break; + default: + snd_cs4231_out(chip, reg, chip->image[reg]); + break; + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + snd_cs4231_mce_down(chip); +#else + /* The following is a workaround to avoid freeze after resume on TP600E. + This is the first half of copy of snd_cs4231_mce_down(), but doesn't + include rescheduling. -- iwai + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_busy_wait(chip); + chip->mce_bit &= ~CS4231_MCE; + timeout = inb(CS4231P(chip, REGSEL)); + outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port); + if ((timeout & CS4231_MCE) == 0 || + !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + snd_cs4231_busy_wait(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#endif +} + +static int snd_cs4231_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, dev->data, return 0); + + switch (rqst) { + case PM_SUSPEND: + if (chip->suspend) + (*chip->suspend)(chip); + break; + case PM_RESUME: + if (chip->resume) + (*chip->resume)(chip); + break; + } + return 0; +} + +#endif /* CONFIG_PM */ + +static int snd_cs4231_free(cs4231_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_cport) { + release_resource(chip->res_cport); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) { + disable_irq(chip->irq); + if (!(chip->hwshare & CS4231_HWSHARE_IRQ)) + free_irq(chip->irq, (void *) chip); + } + if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) { + snd_dma_disable(chip->dma1); + free_dma(chip->dma1); + } + if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) { + snd_dma_disable(chip->dma2); + free_dma(chip->dma2); + } +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->timer) + snd_device_free(chip->card, chip->timer); + snd_magic_kfree(chip); + return 0; +} + +static int snd_cs4231_dev_free(snd_device_t *device) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, device->device_data, return -ENXIO); + return snd_cs4231_free(chip); +} + +const char *snd_cs4231_chip_id(cs4231_t *chip) +{ + switch (chip->hardware) { + case CS4231_HW_CS4231: return "CS4231"; + case CS4231_HW_CS4231A: return "CS4231A"; + case CS4231_HW_CS4232: return "CS4232"; + case CS4231_HW_CS4232A: return "CS4232A"; + case CS4231_HW_CS4235: return "CS4235"; + case CS4231_HW_CS4236B: return "CS4236B"; + case CS4231_HW_CS4237B: return "CS4237B"; + case CS4231_HW_CS4238B: return "CS4238B"; + case CS4231_HW_CS4239: return "CS4239"; + case CS4231_HW_INTERWAVE: return "AMD InterWave"; + case CS4231_HW_OPL3SA2: return chip->card->shortname; + default: return "???"; + } +} + +int snd_cs4231_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip) +{ + static snd_device_ops_t ops = { + dev_free: snd_cs4231_dev_free, + }; + cs4231_t *chip; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(cs4231_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->irq = -1; + chip->dma1 = -1; + chip->dma2 = -1; + chip->hardware = hardware; + chip->hwshare = hwshare; + + if ((chip->res_port = request_region(port, 4, "CS4231")) == NULL) { + snd_cs4231_free(chip); + return -EBUSY; + } + chip->port = port; + if ((long)cport >= 0 && (chip->res_cport = request_region(cport, 8, "CS4232 Control")) == NULL) { + snd_cs4231_free(chip); + return -ENODEV; + } + chip->cport = cport; + if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, SA_INTERRUPT, "CS4231", (void *) chip)) { + snd_cs4231_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (!(hwshare & CS4231_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) { + snd_cs4231_free(chip); + return -EBUSY; + } + chip->dma1 = dma1; + if (!(hwshare & CS4231_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) { + snd_cs4231_free(chip); + return -EBUSY; + } + if (dma1 == dma2 || dma2 < 0) { + chip->single_dma = 1; + chip->dma2 = chip->dma1; + } else + chip->dma2 = dma2; + + spin_lock_init(&chip->reg_lock); + init_MUTEX(&chip->mce_mutex); + init_MUTEX(&chip->open_mutex); + chip->card = card; + chip->rate_constraint = snd_cs4231_xrate; + chip->set_playback_format = snd_cs4231_playback_format; + chip->set_capture_format = snd_cs4231_capture_format; + memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); + + /* global setup */ + if (snd_cs4231_probe(chip) < 0) { + snd_cs4231_free(chip); + return -ENODEV; + } + snd_cs4231_init(chip); + + if (chip->hardware & CS4231_HW_CS4232_MASK) { + if (chip->res_cport == NULL) + snd_printk("CS4232 control port features are not accessible\n"); + } + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_cs4231_free(chip); + return err; + } + +#ifdef CONFIG_PM + /* Power Management */ + chip->suspend = snd_cs4231_suspend; + chip->resume = snd_cs4231_resume; + chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_cs4231_pm_callback); + if (chip->pm_dev) + chip->pm_dev->data = chip; +#endif + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_cs4231_playback_ops = { + open: snd_cs4231_playback_open, + close: snd_cs4231_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs4231_playback_hw_params, + hw_free: snd_cs4231_playback_hw_free, + prepare: snd_cs4231_playback_prepare, + trigger: snd_cs4231_trigger, + pointer: snd_cs4231_playback_pointer, +}; + +static snd_pcm_ops_t snd_cs4231_capture_ops = { + open: snd_cs4231_capture_open, + close: snd_cs4231_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs4231_capture_hw_params, + hw_free: snd_cs4231_capture_hw_free, + prepare: snd_cs4231_capture_prepare, + trigger: snd_cs4231_trigger, + pointer: snd_cs4231_capture_pointer, +}; + +static void snd_cs4231_pcm_free(snd_pcm_t *pcm) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_cs4231_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "CS4231", device, 1, 1, &pcm)) < 0) + return err; + + spin_lock_init(&chip->reg_lock); + init_MUTEX(&chip->mce_mutex); + init_MUTEX(&chip->open_mutex); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops); + + /* global setup */ + pcm->private_data = chip; + pcm->private_free = snd_cs4231_pcm_free; + pcm->info_flags = 0; + if (chip->single_dma) + pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; + if (chip->hardware != CS4231_HW_INTERWAVE) + pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; + strcpy(pcm->name, snd_cs4231_chip_id(chip)); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + + chip->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +static void snd_cs4231_timer_free(snd_timer_t *timer) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, timer->private_data, return); + chip->timer = NULL; +} + +int snd_cs4231_timer(cs4231_t *chip, int device, snd_timer_t **rtimer) +{ + snd_timer_t *timer; + snd_timer_id_t tid; + int err; + + /* Timer initialization */ + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = chip->card->number; + tid.device = device; + tid.subdevice = 0; + if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0) + return err; + strcpy(timer->name, snd_cs4231_chip_id(chip)); + timer->private_data = chip; + timer->private_free = snd_cs4231_timer_free; + timer->hw = snd_cs4231_timer_table; + chip->timer = timer; + if (rtimer) + *rtimer = timer; + return 0; +} + +/* + * MIXER part + */ + +static int snd_cs4231_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { + "Line", "Aux", "Mic", "Mix" + }; + static char *opl3sa_texts[4] = { + "Line", "CD", "Mic", "Mix" + }; + static char *gusmax_texts[4] = { + "Line", "Synth", "Mic", "Mix" + }; + char **ptexts = texts; + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + + snd_assert(chip->card != NULL, return -EINVAL); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + if (!strcmp(chip->card->driver, "GUS MAX")) + ptexts = gusmax_texts; + switch (chip->hardware) { + case CS4231_HW_INTERWAVE: ptexts = gusmax_texts; break; + case CS4231_HW_OPL3SA2: ptexts = opl3sa_texts; break; + } + strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_cs4231_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6; + ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4231_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right; + int change; + + if (ucontrol->value.enumerated.item[0] > 3 || + ucontrol->value.enumerated.item[1] > 3) + return -EINVAL; + left = ucontrol->value.enumerated.item[0] << 6; + right = ucontrol->value.enumerated.item[1] << 6; + spin_lock_irqsave(&chip->reg_lock, flags); + left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left; + right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right; + change = left != chip->image[CS4231_LEFT_INPUT] || + right != chip->image[CS4231_RIGHT_INPUT]; + snd_cs4231_out(chip, CS4231_LEFT_INPUT, left); + snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + snd_cs4231_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + snd_cs4231_out(chip, left_reg, val1); + snd_cs4231_out(chip, right_reg, val2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4231_CONTROLS (sizeof(snd_cs4231_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4231_controls[] = { +CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), +CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), +CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1), +CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), +CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1), +CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), +CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_cs4231_info_mux, + get: snd_cs4231_get_mux, + put: snd_cs4231_put_mux, +}, +CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0), +CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0), +CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1) +}; + +int snd_cs4231_mixer(cs4231_t *chip) +{ + snd_card_t *card; + int err, idx; + + snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, chip->pcm->name); + + for (idx = 0; idx < CS4231_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +EXPORT_SYMBOL(snd_cs4231_out); +EXPORT_SYMBOL(snd_cs4231_in); +EXPORT_SYMBOL(snd_cs4231_outm); +EXPORT_SYMBOL(snd_cs4236_ext_out); +EXPORT_SYMBOL(snd_cs4236_ext_in); +EXPORT_SYMBOL(snd_cs4231_mce_up); +EXPORT_SYMBOL(snd_cs4231_mce_down); +EXPORT_SYMBOL(snd_cs4231_interrupt); +EXPORT_SYMBOL(snd_cs4231_chip_id); +EXPORT_SYMBOL(snd_cs4231_create); +EXPORT_SYMBOL(snd_cs4231_pcm); +EXPORT_SYMBOL(snd_cs4231_mixer); +EXPORT_SYMBOL(snd_cs4231_timer); +EXPORT_SYMBOL(snd_cs4231_info_single); +EXPORT_SYMBOL(snd_cs4231_get_single); +EXPORT_SYMBOL(snd_cs4231_put_single); +EXPORT_SYMBOL(snd_cs4231_info_double); +EXPORT_SYMBOL(snd_cs4231_get_double); +EXPORT_SYMBOL(snd_cs4231_put_double); + +/* + * INIT part + */ + +static int __init alsa_cs4231_init(void) +{ + return 0; +} + +static void __exit alsa_cs4231_exit(void) +{ +} + +module_init(alsa_cs4231_init) +module_exit(alsa_cs4231_exit) diff -Nru a/sound/isa/cs423x/cs4232.c b/sound/isa/cs423x/cs4232.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/cs423x/cs4232.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,2 @@ +#define CS4232 +#include "cs4236.c" diff -Nru a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/cs423x/cs4236.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,641 @@ +/* + * Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t cs4231_t + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#ifdef CS4232 +MODULE_DESCRIPTION("Cirrus Logic CS4232"); +MODULE_DEVICES("{{Turtle Beach,TBS-2000}," + "{Turtle Beach,Tropez Plus}," + "{SIC CrystalWave 32}," + "{Hewlett Packard,Omnibook 5500}," + "{TerraTec,Maestro 32/96}," + "{Philips,PCA70PS}}"); +#else +MODULE_DESCRIPTION("Cirrus Logic CS4235-9"); +MODULE_DEVICES("{{Crystal Semiconductors,CS4235}," + "{Crystal Semiconductors,CS4236}," + "{Crystal Semiconductors,CS4237}," + "{Crystal Semiconductors,CS4238}," + "{Crystal Semiconductors,CS4239}," + "{Acer,AW37}," + "{Acer,AW35/Pro}," + "{Crystal,3D}," + "{Crystal Computer,TidalWave128}," + "{Dell,Optiplex GX1}," + "{Dell,Workstation 400 sound}," + "{EliteGroup,P5TX-LA sound}," + "{Gallant,SC-70P}," + "{Gateway,E1000 Onboard CS4236B}," + "{Genius,Sound Maker 3DJ}," + "{Hewlett Packard,HP6330 sound}," + "{IBM,PC 300PL sound}," + "{IBM,Aptiva 2137 E24}," + "{IBM,IntelliStation M Pro}," + "{Intel,Marlin Spike Mobo CS4235}," + "{Guillemot,MaxiSound 16 PnP}," + "{NewClear,3D}," + "{TerraTec,AudioSystem EWS64L/XL}," + "{Typhoon Soundsystem,CS4236B}," + "{Turtle Beach,Malibu}," + "{Unknown,Digital PC 5000 Onboard}}"); +#endif + +#ifdef CS4232 +#define IDENT "CS4232" +#else +#define IDENT "CS4236+" +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* PnP setup */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for " IDENT " soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for " IDENT " soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable " IDENT " soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_cport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_cport, "Control port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_cport, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_sb_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_sb_port, "SB port # for " IDENT " driver (optional)."); +MODULE_PARM_SYNTAX(snd_sb_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); + +struct snd_card_cs4236 { + struct resource *res_sb_port; +#ifdef __ISAPNP__ + struct isapnp_dev *wss; + struct isapnp_dev *ctrl; + struct isapnp_dev *mpu; +#endif +}; + +static snd_card_t *snd_cs4236_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_cs4236_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_cs4236_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_CS4232(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401) } \ + } +#define ISAPNP_CS4232_1(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \ + ISAPNP_DEVICE_ID('P', 'N', 'P', _mpu401) } \ + } +#define ISAPNP_CS4232_WOMPU(_va, _vb, _vc, _device, _wss, _ctrl) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl) } \ + } + + +#ifdef CS4232 +static struct isapnp_card_id snd_card_pnpids[] __devinitdata = { + /* Philips PCA70PS */ + ISAPNP_CS4232_1('C','S','C',0x0d32,0x0000,0x0010,0xb006), + /* TerraTec Maestro 32/96 (CS4232) */ + ISAPNP_CS4232('C','S','C',0x1a32,0x0000,0x0010,0x0003), + /* HP Omnibook 5500 onboard */ + ISAPNP_CS4232('C','S','C',0x4232,0x0000,0x0002,0x0003), + /* Turtle Beach TBS-2000 (CS4232) */ + ISAPNP_CS4232('C','S','C',0x7532,0x0000,0x0010,0xb006), + /* Turtle Beach Tropez Plus (CS4232) */ + ISAPNP_CS4232_1('C','S','C',0x7632,0x0000,0x0010,0xb006), + /* SIC CrystalWave 32 (CS4232) */ + ISAPNP_CS4232('C','S','C',0xf032,0x0000,0x0010,0x0003), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; +#else /* CS4236 */ +static struct isapnp_card_id snd_card_pnpids[] __devinitdata = { + /* Intel Marlin Spike Motherboard - CS4235 */ + ISAPNP_CS4232('C','S','C',0x0225,0x0000,0x0010,0x0003), + /* Intel Marlin Spike Motherboard (#2) - CS4235 */ + ISAPNP_CS4232('C','S','C',0x0225,0x0100,0x0110,0x0103), + /* Genius Sound Maker 3DJ - CS4237B */ + ISAPNP_CS4232('C','S','C',0x0437,0x0000,0x0010,0x0003), + /* Digital PC 5000 Onboard - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0x0735,0x0000,0x0010), + /* some uknown CS4236B */ + ISAPNP_CS4232('C','S','C',0x0b35,0x0000,0x0010,0x0003), + /* CS4235 on mainboard without MPU */ + ISAPNP_CS4232_WOMPU('C','S','C',0x1425,0x0100,0x0110), + /* Gateway E1000 Onboard CS4236B */ + ISAPNP_CS4232('C','S','C',0x1335,0x0000,0x0010,0x0003), + /* HP 6330 Onboard sound */ + ISAPNP_CS4232('C','S','C',0x1525,0x0100,0x0110,0x0103), + /* Crystal Computer TidalWave128 */ + ISAPNP_CS4232('C','S','C',0x1e37,0x0000,0x0010,0x0003), + /* ACER AW37 - CS4235 */ + ISAPNP_CS4232('C','S','C',0x4236,0x0000,0x0010,0x0003), + /* build-in soundcard in EliteGroup P5TX-LA motherboard - CS4237B */ + ISAPNP_CS4232('C','S','C',0x4237,0x0000,0x0010,0x0003), + /* Crystal 3D - CS4237B */ + ISAPNP_CS4232('C','S','C',0x4336,0x0000,0x0010,0x0003), + /* Typhoon Soundsystem PnP - CS4236B */ + ISAPNP_CS4232('C','S','C',0x4536,0x0000,0x0010,0x0003), + /* TerraTec AudioSystem EWS64XL - CS4236B */ + ISAPNP_CS4232('C','S','C',0xa836,0xa800,0xa810,0xa803), + /* Crystal Semiconductors CS4237B */ + ISAPNP_CS4232('C','S','C',0x4637,0x0000,0x0010,0x0003), + /* NewClear 3D - CX4237B-XQ3 */ + ISAPNP_CS4232('C','S','C',0x4837,0x0000,0x0010,0x0003), + /* Dell Optiplex GX1 - CS4236B */ + ISAPNP_CS4232('C','S','C',0x6835,0x0000,0x0010,0x0003), + /* Dell P410 motherboard - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0x6835,0x0000,0x0010), + /* Dell Workstation 400 Onboard - CS4236B */ + ISAPNP_CS4232('C','S','C',0x6836,0x0000,0x0010,0x0003), + /* Turtle Beach Malibu - CS4237B */ + ISAPNP_CS4232('C','S','C',0x7537,0x0000,0x0010,0x0003), + /* CS4235 - onboard */ + ISAPNP_CS4232('C','S','C',0x8025,0x0100,0x0110,0x0103), + /* IBM PC 300PL Onboard - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0xe836,0x0000,0x0010), + /* IBM Aptiva 2137 E24 Onboard - CS4237B */ + ISAPNP_CS4232('C','S','C',0x8037,0x0000,0x0010,0x0003), + /* IBM IntelliStation M Pro motherboard */ + ISAPNP_CS4232_WOMPU('C','S','C',0xc835,0x0000,0x0010), + /* Guillemot MaxiSound 16 PnP - CS4236B */ + ISAPNP_CS4232('C','S','C',0x9836,0x0000,0x0010,0x0003), + /* Gallant SC-70P */ + ISAPNP_CS4232('C','S','C',0x9837,0x0000,0x0010,0x0003), + /* ACER AW37/Pro - CS4235 */ + ISAPNP_CS4232('C','S','C',0xd925,0x0000,0x0010,0x0003), + /* ACER AW35/Pro - CS4237B */ + ISAPNP_CS4232('C','S','C',0xd937,0x0000,0x0010,0x0003), + /* CS4235 without MPU401 */ + ISAPNP_CS4232_WOMPU('C','S','C',0xe825,0x0100,0x0110), + /* CS4236B */ + ISAPNP_CS4232('C','S','C',0xf235,0x0000,0x0010,0x0003), + /* CS4236B */ + ISAPNP_CS4232('C','S','C',0xf238,0x0000,0x0010,0x0003), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; +#endif + +ISAPNP_CARD_TABLE(snd_card_pnpids); + +static int __init snd_card_cs4236_isapnp(int dev, struct snd_card_cs4236 *acard) +{ + const struct isapnp_card_id *id = snd_cs4236_isapnp_id[dev]; + struct isapnp_card *card = snd_cs4236_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->wss = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->wss->active) { + acard->wss = NULL; + return -EBUSY; + } + acard->ctrl = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->ctrl->active) { + acard->wss = acard->ctrl = NULL; + return -EBUSY; + } + if (id->devs[2].vendor && id->devs[2].function) { + acard->mpu = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->mpu->active) { + acard->wss = acard->ctrl = acard->mpu = NULL; + return -EBUSY; + } + } + + /* WSS initialization */ + pdev = acard->wss; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 4); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4); + if (snd_sb_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_sb_port[dev], 16); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev] < 0 ? 4 : snd_dma2[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk(IDENT " isapnp configure failed for WSS (out of resources?)\n"); + return -EBUSY; + } + snd_port[dev] = pdev->resource[0].start; + snd_fm_port[dev] = pdev->resource[1].start; + snd_sb_port[dev] = pdev->resource[2].start; + snd_irq[dev] = pdev->irq_resource[0].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start == 4 ? -1 : pdev->dma_resource[1].start; + snd_printdd("isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n", + snd_port[dev], snd_fm_port[dev], snd_sb_port[dev]); + snd_printdd("isapnp WSS: irq=%i, dma1=%i, dma2=%i\n", + snd_irq[dev], snd_dma1[dev], snd_dma2[dev]); + /* CTRL initialization */ + pdev = acard->ctrl; + if (pdev->prepare(pdev) < 0) { + acard->wss->deactivate(acard->wss); + return -EAGAIN; + } + if (snd_cport[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_cport[dev], 8); + if (pdev->activate(pdev)<0) { + snd_printk(IDENT " isapnp configure failed for control (out of resources?)\n"); + acard->wss->deactivate(acard->wss); + return -EBUSY; + } + snd_cport[dev] = pdev->resource[0].start; + snd_printdd("isapnp CTRL: control port=0x%lx\n", snd_cport[dev]); + /* MPU initialization */ + if (acard->mpu) { + pdev = acard->mpu; + if (pdev->prepare(pdev) < 0) { + acard->wss->deactivate(acard->wss); + acard->ctrl->deactivate(acard->ctrl); + return -EAGAIN; + } + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], 2); + if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], 1); + if (pdev->activate(pdev)<0) { + snd_mpu_port[dev] = SNDRV_AUTO_PORT; + snd_mpu_irq[dev] = SNDRV_AUTO_IRQ; + snd_printk(IDENT " isapnp configure failed for MPU (out of resources?)\n"); + } else { + snd_mpu_port[dev] = pdev->resource[0].start; + if (pdev->irq_resource[0].flags & IORESOURCE_IRQ) { + snd_mpu_irq[dev] = pdev->irq_resource[0].start; + } else { + snd_mpu_irq[dev] = -1; /* disable interrupt */ + } + } + snd_printdd("isapnp MPU: port=0x%lx, irq=%i\n", snd_mpu_port[dev], snd_mpu_irq[dev]); + } + return 0; +} + +static void snd_card_cs4236_deactivate(struct snd_card_cs4236 *acard) +{ + if (acard->wss) { + acard->wss->deactivate(acard->wss); + acard->wss = NULL; + } + if (acard->ctrl) { + acard->ctrl->deactivate(acard->ctrl); + acard->ctrl = NULL; + } + if (acard->mpu) { + acard->mpu->deactivate(acard->mpu); + acard->mpu = NULL; + } +} +#endif + +static void snd_card_cs4236_free(snd_card_t *card) +{ + struct snd_card_cs4236 *acard = (struct snd_card_cs4236 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_cs4236_deactivate(acard); +#endif + if (acard->res_sb_port) { + release_resource(acard->res_sb_port); + kfree_nocheck(acard->res_sb_port); + } + } +} + +static int __init snd_card_cs4236_probe(int dev) +{ + snd_card_t *card; + struct snd_card_cs4236 *acard; + snd_pcm_t *pcm = NULL; + cs4231_t *chip; + opl3_t *opl3; + int err; + +#ifdef __ISAPNP__ + if (!snd_isapnp[dev]) { +#endif + if (snd_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_port\n"); + return -EINVAL; + } + if (snd_cport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_cport\n"); + return -EINVAL; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_cs4236)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_card_cs4236 *)card->private_data; + card->private_free = snd_card_cs4236_free; +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && (err = snd_card_cs4236_isapnp(dev, acard))<0) { + snd_printk("isapnp detection failed and probing for " IDENT " is not supported\n"); + snd_card_free(card); + return -ENXIO; + } +#endif + if (snd_mpu_port[dev] < 0) + snd_mpu_port[dev] = SNDRV_AUTO_PORT; + if (snd_fm_port[dev] < 0) + snd_fm_port[dev] = SNDRV_AUTO_PORT; + if (snd_sb_port[dev] < 0) + snd_sb_port[dev] = SNDRV_AUTO_PORT; + if (snd_sb_port[dev] != SNDRV_AUTO_PORT) + if ((acard->res_sb_port = request_region(snd_sb_port[dev], 16, IDENT " SB")) == NULL) { + snd_printk("unable to register SB port at 0x%lx\n", snd_sb_port[dev]); + snd_card_free(card); + return -ENOMEM; + } + +#ifdef CS4232 + if ((err = snd_cs4231_create(card, + snd_port[dev], + snd_cport[dev], + snd_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + CS4231_HW_DETECT, + 0, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + +#else /* CS4236 */ + if ((err = snd_cs4236_create(card, + snd_port[dev], + snd_cport[dev], + snd_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + CS4231_HW_DETECT, + 0, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4236_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4236_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } +#endif + + if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) { + if (snd_opl3_create(card, + snd_fm_port[dev], snd_fm_port[dev] + 2, + OPL3_HW_OPL3_CS, 0, &opl3) < 0) { + snd_printk(IDENT ": OPL3 not detected\n"); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + } + + if (snd_mpu_irq[dev] >= 0 && snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, + snd_mpu_port[dev], 0, + snd_mpu_irq[dev], + snd_mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) + snd_printk(IDENT ": MPU401 not detected\n"); + } + strcpy(card->driver, pcm->name); + strcpy(card->shortname, pcm->name); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", + pcm->name, + chip->port, + snd_irq[dev], + snd_dma1[dev]); + if (snd_dma1[dev] >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_cs4236_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_cs4236_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_cs4236_isapnp_cards[dev] = card; + snd_cs4236_isapnp_id[dev] = id; + res = snd_card_cs4236_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_cs423x_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (snd_card_cs4236_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_card_pnpids, snd_cs4236_isapnp_detect); +#endif + if (!cards) { +#ifdef MODULE + snd_printk(IDENT " soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_cs423x_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_cs4236_cards[idx]); +} + +module_init(alsa_card_cs423x_init) +module_exit(alsa_card_cs423x_exit) + +#ifndef MODULE + +/* format is: snd-cs4232=snd_enable,snd_index,snd_id,snd_isapnp,snd_port, + snd_cport,snd_mpu_port,snd_fm_port,snd_sb_port, + snd_irq,snd_mpu_irq,snd_dma1,snd_dma1_size, + snd_dma2,snd_dma2_size */ +/* format is: snd-cs4236=snd_enable,snd_index,snd_id,snd_isapnp,snd_port, + snd_cport,snd_mpu_port,snd_fm_port,snd_sb_port, + snd_irq,snd_mpu_irq,snd_dma1,snd_dma1_size, + snd_dma2,snd_dma2_size */ + +static int __init alsa_card_cs423x_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_cport[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_sb_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +#ifdef CS4232 +__setup("snd-cs4232=", alsa_card_cs423x_setup); +#else /* CS4236 */ +__setup("snd-cs4236=", alsa_card_cs423x_setup); +#endif + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/cs423x/cs4236_lib.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,980 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of CS4235/4236B/4237B/4238B/4239 chips + * + * Note: + * ----- + * + * Bugs: + * ----- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Indirect control registers (CS4236B+) + * + * C0 + * D8: WSS reset (all chips) + * + * C1 (all chips except CS4236) + * D7-D5: version + * D4-D0: chip id + * 11101 - CS4235 + * 01011 - CS4236B + * 01000 - CS4237B + * 01001 - CS4238B + * 11110 - CS4239 + * + * C2 + * D7-D4: 3D Space (CS4235,CS4237B,CS4238B,CS4239) + * D3-D0: 3D Center (CS4237B); 3D Volume (CS4238B) + * + * C3 + * D7: 3D Enable (CS4237B) + * D6: 3D Mono Enable (CS4237B) + * D5: 3D Serial Output (CS4237B,CS4238B) + * D4: 3D Enable (CS4235,CS4238B,CS4239) + * + * C4 + * D7: consumer serial port enable (CS4237B,CS4238B) + * D6: channels status block reset (CS4237B,CS4238B) + * D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B) + * D4: validity bit bit in sub-frame of digital audio data (CS4237B,CS4238B) + * + * C5 lower channel status (digital serial data description) (CS4237B,CS4238B) + * D7-D6: first two bits of category code + * D5: lock + * D4-D3: pre-emphasis (0 = none, 1 = 50/15us) + * D2: copy/copyright (0 = copy inhibited) + * D1: 0 = digital audio / 1 = non-digital audio + * + * C6 upper channel status (digital serial data description) (CS4237B,CS4238B) + * D7-D6: sample frequency (0 = 44.1kHz) + * D5: generation status (0 = no indication, 1 = original/commercially precaptureed data) + * D4-D0: category code (upper bits) + * + * C7 reserved (must write 0) + * + * C8 wavetable control + * D7: volume control interrupt enable (CS4235,CS4239) + * D6: hardware volume control format (CS4235,CS4239) + * D3: wavetable serial port enable (all chips) + * D2: DSP serial port switch (all chips) + * D1: disable MCLK (all chips) + * D0: force BRESET low (all chips) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of CS4235/4236B/4237B/4238B/4239 chips"); +MODULE_LICENSE("GPL"); + +#define chip_t cs4231_t + +/* + * + */ + +static unsigned char snd_cs4236_ext_map[18] = { + /* CS4236_LEFT_LINE */ 0xff, + /* CS4236_RIGHT_LINE */ 0xff, + /* CS4236_LEFT_MIC */ 0xdf, + /* CS4236_RIGHT_MIC */ 0xdf, + /* CS4236_LEFT_MIX_CTRL */ 0xe0 | 0x18, + /* CS4236_RIGHT_MIX_CTRL */ 0xe0, + /* CS4236_LEFT_FM */ 0xbf, + /* CS4236_RIGHT_FM */ 0xbf, + /* CS4236_LEFT_DSP */ 0xbf, + /* CS4236_RIGHT_DSP */ 0xbf, + /* CS4236_RIGHT_LOOPBACK */ 0xbf, + /* CS4236_DAC_MUTE */ 0xe0, + /* CS4236_ADC_RATE */ 0x01, /* 48kHz */ + /* CS4236_DAC_RATE */ 0x01, /* 48kHz */ + /* CS4236_LEFT_MASTER */ 0xbf, + /* CS4236_RIGHT_MASTER */ 0xbf, + /* CS4236_LEFT_WAVE */ 0xbf, + /* CS4236_RIGHT_WAVE */ 0xbf +}; + +/* + * + */ + +static void snd_cs4236_ctrl_out(cs4231_t *chip, unsigned char reg, unsigned char val) +{ + outb(reg, chip->cport + 3); + outb(chip->cimage[reg] = val, chip->cport + 4); +} + +static unsigned char snd_cs4236_ctrl_in(cs4231_t *chip, unsigned char reg) +{ + outb(reg, chip->cport + 3); + return inb(chip->cport + 4); +} + +/* + * PCM + */ + +#define CLOCKS 8 + +static ratnum_t clocks[CLOCKS] = { + { num: 16934400, den_min: 353, den_max: 353, den_step: 1 }, + { num: 16934400, den_min: 529, den_max: 529, den_step: 1 }, + { num: 16934400, den_min: 617, den_max: 617, den_step: 1 }, + { num: 16934400, den_min: 1058, den_max: 1058, den_step: 1 }, + { num: 16934400, den_min: 1764, den_max: 1764, den_step: 1 }, + { num: 16934400, den_min: 2117, den_max: 2117, den_step: 1 }, + { num: 16934400, den_min: 2558, den_max: 2558, den_step: 1 }, + { num: 16934400/16, den_min: 21, den_max: 192, den_step: 1 } +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + nrats: CLOCKS, + rats: clocks, +}; + +static int snd_cs4236_xrate(snd_pcm_runtime_t *runtime) +{ + return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); +} + +static unsigned char divisor_to_rate_register(unsigned int divisor) +{ + switch (divisor) { + case 353: return 1; + case 529: return 2; + case 617: return 3; + case 1058: return 4; + case 1764: return 5; + case 2117: return 6; + case 2558: return 7; + default: + snd_runtime_check(divisor >= 21 && divisor <= 192, return 192); + return divisor; + } +} + +static void snd_cs4236_playback_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char pdfr) +{ + unsigned long flags; + unsigned char rate = divisor_to_rate_register(params->rate_den); + + spin_lock_irqsave(&chip->reg_lock, flags); + /* set fast playback format change and clean playback FIFO */ + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x10); + snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4236_capture_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char cdfr) +{ + unsigned long flags; + unsigned char rate = divisor_to_rate_register(params->rate_den); + + spin_lock_irqsave(&chip->reg_lock, flags); + /* set fast capture format change and clean capture FIFO */ + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); + snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x20); + snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +#ifdef CONFIG_PM + +static void snd_cs4236_suspend(cs4231_t *chip) +{ + int reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) + chip->image[reg] = snd_cs4231_in(chip, reg); + for (reg = 0; reg < 18; reg++) + chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg)); + for (reg = 2; reg < 9; reg++) + chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4236_resume(cs4231_t *chip) +{ + int reg; + unsigned long flags; + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) { + switch (reg) { + case CS4236_EXT_REG: + case CS4231_VERSION: + case 27: /* why? CS4235 - master left */ + case 29: /* why? CS4235 - master right */ + break; + default: + snd_cs4231_out(chip, reg, chip->image[reg]); + break; + } + } + for (reg = 0; reg < 18; reg++) + snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]); + for (reg = 2; reg < 9; reg++) { + switch (reg) { + case 7: + break; + default: + snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); +} + +#endif /* CONFIG_PM */ + +int snd_cs4236_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip) +{ + cs4231_t *chip; + unsigned char ver1, ver2; + int err, reg; + + *rchip = NULL; + if (hardware == CS4231_HW_DETECT) + hardware = CS4231_HW_DETECT3; + if (cport < 0x100) { + snd_printk("please, specify control port for CS4236+ chips\n"); + return -ENODEV; + } + if ((err = snd_cs4231_create(card, port, cport, irq, dma1, dma2, hardware, hwshare, &chip)) < 0) + return err; + if (!(chip->hardware & CS4231_HW_CS4236B_MASK)) { + snd_device_free(card, chip); + return -ENODEV; + } +#if 0 + { + int idx; + for (idx = 0; idx < 8; idx++) + snd_printk("CD%i = 0x%x\n", idx, inb(chip->cport + idx)); + for (idx = 0; idx < 9; idx++) + snd_printk("C%i = 0x%x\n", idx, snd_cs4236_ctrl_in(chip, idx)); + } +#endif + ver1 = snd_cs4236_ctrl_in(chip, 1); + ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION); + snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2); + if (ver1 != ver2) { + snd_printk("CS4236+ chip detected, but control port 0x%lx is not valid\n", cport); + snd_device_free(card, chip); + return -ENODEV; + } + snd_cs4236_ctrl_out(chip, 0, 0x00); + snd_cs4236_ctrl_out(chip, 2, 0xff); + snd_cs4236_ctrl_out(chip, 3, 0x00); + snd_cs4236_ctrl_out(chip, 4, 0x80); + snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE); + snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2); + snd_cs4236_ctrl_out(chip, 7, 0x00); + /* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */ + /* is working with this setup, other hardware should have */ + /* different signal paths and this value should be selectable */ + /* in the future */ + snd_cs4236_ctrl_out(chip, 8, 0x8c); + chip->rate_constraint = snd_cs4236_xrate; + chip->set_playback_format = snd_cs4236_playback_format; + chip->set_capture_format = snd_cs4236_capture_format; +#ifdef CONFIG_PM + chip->suspend = snd_cs4236_suspend; + chip->resume = snd_cs4236_resume; +#endif + + /* initialize extended registers */ + for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++) + snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]); + + /* initialize compatible but more featured registers */ + snd_cs4231_out(chip, CS4231_LEFT_INPUT, 0x40); + snd_cs4231_out(chip, CS4231_RIGHT_INPUT, 0x40); + snd_cs4231_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff); + snd_cs4231_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff); + snd_cs4231_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf); + snd_cs4231_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf); + snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff); + snd_cs4231_out(chip, CS4231_LEFT_LINE_IN, 0xff); + snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff); + switch (chip->hardware) { + case CS4231_HW_CS4235: + case CS4231_HW_CS4239: + snd_cs4231_out(chip, CS4235_LEFT_MASTER, 0xff); + snd_cs4231_out(chip, CS4235_RIGHT_MASTER, 0xff); + break; + } + + *rchip = chip; + return 0; +} + +int snd_cs4236_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_cs4231_pcm(chip, device, &pcm)) < 0) + return err; + pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX; + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER + */ + +#define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_single, \ + get: snd_cs4236_get_single, put: snd_cs4236_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_cs4236_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_cs4236_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_cs4236_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val; + change = val != chip->eimage[CS4236_REG(reg)]; + snd_cs4236_ext_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_single, \ + get: snd_cs4236_get_singlec, put: snd_cs4236_put_singlec, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_cs4236_get_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_cs4236_put_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->cimage[reg] & ~(mask << shift)) | val; + change = val != chip->cimage[reg]; + snd_cs4236_ctrl_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_double, \ + get: snd_cs4236_get_double, put: snd_cs4236_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_cs4236_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_cs4236_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_cs4236_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1; + val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; + change = val1 != chip->eimage[CS4236_REG(left_reg)] || val2 != chip->eimage[CS4236_REG(right_reg)]; + snd_cs4236_ext_out(chip, left_reg, val1); + snd_cs4236_ext_out(chip, right_reg, val2); + } else { + val1 = (chip->eimage[CS4236_REG(left_reg)] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != chip->eimage[CS4236_REG(left_reg)]; + snd_cs4236_ext_out(chip, left_reg, val1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_double, \ + get: snd_cs4236_get_double1, put: snd_cs4236_put_double1, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_cs4236_get_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_cs4236_put_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)]; + snd_cs4231_out(chip, left_reg, val1); + snd_cs4236_ext_out(chip, right_reg, val2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_MASTER_DIGITAL(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_double, \ + get: snd_cs4236_get_master_digital, put: snd_cs4236_put_master_digital, \ + private_value: 71 << 24 } + +static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol) +{ + return (vol < 64) ? 63 - vol : 64 + (71 - vol); +} + +static int snd_cs4236_get_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f); + ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4236_put_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1, val2; + + val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f); + val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f); + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1; + val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2; + change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)]; + snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1); + snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4235_OUTPUT_ACCU(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_double, \ + get: snd_cs4235_get_output_accu, put: snd_cs4235_put_output_accu, \ + private_value: 3 << 24 } + +static inline int snd_cs4235_mixer_output_accu_get_volume(int vol) +{ + switch ((vol >> 5) & 3) { + case 0: return 1; + case 1: return 3; + case 2: return 2; + case 3: return 0; + } + return 3; +} + +static inline int snd_cs4235_mixer_output_accu_set_volume(int vol) +{ + switch (vol & 3) { + case 0: return 3 << 5; + case 1: return 0 << 5; + case 2: return 2 << 5; + case 3: return 1 << 5; + } + return 1 << 5; +} + +static int snd_cs4235_get_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]); + ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4235_put_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1, val2; + + val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]); + val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]); + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1; + val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2; + change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER]; + snd_cs4231_out(chip, CS4235_LEFT_MASTER, val1); + snd_cs4231_out(chip, CS4235_RIGHT_MASTER, val2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_CONTROLS (sizeof(snd_cs4236_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_controls[] = { + +CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), +CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), +CS4236_MASTER_DIGITAL("Master Digital Volume", 0), + +CS4236_DOUBLE("Capture Boost Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), + +CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), + +CS4236_DOUBLE("DSP Playback Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), +CS4236_DOUBLE("DSP Playback Volume", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1), + +CS4236_DOUBLE("FM Playback Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), +CS4236_DOUBLE("FM Playback Volume", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1), + +CS4236_DOUBLE("Wavetable Playback Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), +CS4236_DOUBLE("Wavetable Playback Volume", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1), + +CS4231_DOUBLE("Synth Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Synth Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), +CS4231_DOUBLE("Synth Capture Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), +CS4231_DOUBLE("Synth Capture Bypass", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1), + +CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), +CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), +CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1), +CS4236_DOUBLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0), + +CS4231_DOUBLE("Line Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Line Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_DOUBLE("Line Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), +CS4231_DOUBLE("Line Capture Bypass", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1), + +CS4231_DOUBLE("CD Playback Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("CD Volume", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_DOUBLE("CD Capture Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), + +CS4236_DOUBLE1("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), +CS4236_DOUBLE1("Mono Playback Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), +CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), +CS4231_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), + +CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +CS4231_DOUBLE("Analog Loopback Capture Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), + +CS4231_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0), +CS4236_DOUBLE1("Digital Loopback Playback Volume", 0, CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1) +}; + +#define CS4235_CONTROLS (sizeof(snd_cs4235_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4235_controls[] = { + +CS4231_DOUBLE("Master Switch", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1), +CS4231_DOUBLE("Master Volume", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1), + +CS4235_OUTPUT_ACCU("Playback Volume", 0), + +CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), +CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), +CS4236_MASTER_DIGITAL("Master Digital Volume", 0), + +CS4231_DOUBLE("Master Digital Playback Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Master Digital Capture Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), +CS4231_DOUBLE("Master Digital Volume", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), + +CS4236_DOUBLE("Capture Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), + +CS4231_DOUBLE("PCM Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), + +CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), + +CS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), + +CS4236_DOUBLE("Wavetable Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), + +CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), +CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), +CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1), +CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0), + +CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), +CS4231_DOUBLE("Aux Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), + +CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Capture Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), +CS4231_DOUBLE("Aux Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), + +CS4236_DOUBLE1("Master Mono Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), + +CS4236_DOUBLE1("Mono Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), +CS4231_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), + +CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), +}; + +#define CS4236_IEC958_ENABLE(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_single, \ + get: snd_cs4236_get_iec958_switch, put: snd_cs4236_put_iec958_switch, \ + private_value: 1 << 16 } + +static int snd_cs4236_get_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0; +#if 0 + printk("get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", + snd_cs4231_in(chip, CS4231_ALT_FEATURE_1), + snd_cs4236_ctrl_in(chip, 3), + snd_cs4236_ctrl_in(chip, 4), + snd_cs4236_ctrl_in(chip, 5), + snd_cs4236_ctrl_in(chip, 6), + snd_cs4236_ctrl_in(chip, 8)); +#endif + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4236_put_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short enable, val; + + enable = ucontrol->value.integer.value[0] & 1; + + down(&chip->mce_mutex); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1); + change = val != chip->image[CS4231_ALT_FEATURE_1]; + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, val); + val = snd_cs4236_ctrl_in(chip, 4) | 0xc0; + snd_cs4236_ctrl_out(chip, 4, val); + udelay(100); + val &= ~0x40; + snd_cs4236_ctrl_out(chip, 4, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + up(&chip->mce_mutex); + +#if 0 + printk("set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", + snd_cs4231_in(chip, CS4231_ALT_FEATURE_1), + snd_cs4236_ctrl_in(chip, 3), + snd_cs4236_ctrl_in(chip, 4), + snd_cs4236_ctrl_in(chip, 5), + snd_cs4236_ctrl_in(chip, 6), + snd_cs4236_ctrl_in(chip, 8)); +#endif + return change; +} + +#define CS4236_IEC958_CONTROLS (sizeof(snd_cs4236_iec958_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_iec958_controls[] = { +CS4236_IEC958_ENABLE("IEC958 Output Enable", 0), +CS4236_SINGLEC("IEC958 Output Validity", 0, 4, 4, 1, 0), +CS4236_SINGLEC("IEC958 Output User", 0, 4, 5, 1, 0), +CS4236_SINGLEC("IEC958 Output CSBR", 0, 4, 6, 1, 0), +CS4236_SINGLEC("IEC958 Output Channel Status Low", 0, 5, 1, 127, 0), +CS4236_SINGLEC("IEC958 Output Channel Status High", 0, 6, 0, 255, 0) +}; + +#define CS4236_3D_CONTROLS_CS4235 (sizeof(snd_cs4236_3d_controls_cs4235)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4235[] = { +CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), +CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1) +}; + +#define CS4236_3D_CONTROLS_CS4237 (sizeof(snd_cs4236_3d_controls_cs4237)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4237[] = { +CS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0), +CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), +CS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1), +CS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0), +CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) +}; + +#define CS4236_3D_CONTROLS_CS4238 (sizeof(snd_cs4236_3d_controls_cs4238)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4238[] = { +CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), +CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), +CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1), +CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) +}; + +int snd_cs4236_mixer(cs4231_t *chip) +{ + snd_card_t *card; + int err, idx, count; + snd_kcontrol_new_t *kcontrol; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + card = chip->card; + strcpy(card->mixername, snd_cs4231_chip_id(chip)); + + if (chip->hardware == CS4231_HW_CS4235 || + chip->hardware == CS4231_HW_CS4239) { + for (idx = 0; idx < CS4235_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0) + return err; + } + } else { + for (idx = 0; idx < CS4236_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0) + return err; + } + } + switch (chip->hardware) { + case CS4231_HW_CS4235: + case CS4231_HW_CS4239: + count = CS4236_3D_CONTROLS_CS4235; + kcontrol = snd_cs4236_3d_controls_cs4235; + break; + case CS4231_HW_CS4237B: + count = CS4236_3D_CONTROLS_CS4237; + kcontrol = snd_cs4236_3d_controls_cs4237; + break; + case CS4231_HW_CS4238B: + count = CS4236_3D_CONTROLS_CS4238; + kcontrol = snd_cs4236_3d_controls_cs4238; + break; + default: + count = -1; + kcontrol = NULL; + } + for (idx = 0; idx < count; idx++, kcontrol++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0) + return err; + } + if (chip->hardware == CS4231_HW_CS4237B || + chip->hardware == CS4231_HW_CS4238B) { + for (idx = 0; idx < CS4236_IEC958_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0) + return err; + } + } + return 0; +} + +EXPORT_SYMBOL(snd_cs4236_create); +EXPORT_SYMBOL(snd_cs4236_pcm); +EXPORT_SYMBOL(snd_cs4236_mixer); + +/* + * INIT part + */ + +static int __init alsa_cs4236_init(void) +{ + return 0; +} + +static void __exit alsa_cs4236_exit(void) +{ +} + +module_init(alsa_cs4236_init) +module_exit(alsa_cs4236_exit) diff -Nru a/sound/isa/dt0197h.c b/sound/isa/dt0197h.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/dt0197h.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,391 @@ + +/* + card-dt0197h.c - driver for Diamond Technologies DT-0197H based soundcards. + Copyright (C) 1999 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t sb_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("Diamond Technologies DT-0197H"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Diamond Technologies,DT-0197H}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for dt0197h based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for dt0197h based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable dt0197h based soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); + +struct snd_card_dt0197h { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; + struct isapnp_dev *devopl; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_dt0197h_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ +static struct isapnp_card *snd_dt0197h_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_dt0197h_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +static struct isapnp_card_id snd_dt0197h_pnpids[] __devinitdata = { + /* DT197A30 */ + { + ISAPNP_CARD_ID('R','W','B',0x1688), + devs: { ISAPNP_DEVICE_ID('@','@','@',0x0001), + ISAPNP_DEVICE_ID('@','X','@',0x0001), + ISAPNP_DEVICE_ID('@','H','@',0x0001) } + }, + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_dt0197h_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-dt0197h" + + +#ifdef __ISAPNP__ +static int __init snd_card_dt0197h_isapnp(int dev, struct snd_card_dt0197h *acard) +{ + const struct isapnp_card_id *id = snd_dt0197h_isapnp_id[dev]; + struct isapnp_card *card = snd_dt0197h_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + acard->devopl = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->devopl->active) { + acard->dev = acard->devmpu = acard->devopl = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev], + 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + snd_port[dev] = pdev->resource[0].start; + snd_dma8[dev] = pdev->dma_resource[0].start; + snd_irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev || pdev->prepare(pdev)<0) + return 0; + + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], + 2); + if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], + 1); + + if (pdev->activate(pdev)<0) { + snd_printk("MPU-401 isapnp configure failure\n"); + snd_mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + snd_mpu_port[dev] = pdev->resource[0].start; + snd_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + pdev = acard->devopl; + if (pdev == NULL || pdev->prepare(pdev)<0) + return 0; + + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_fm_port[dev], 4); + + if (pdev->activate(pdev)<0) { + snd_printk("OPL isapnp configure failure\n"); + snd_fm_port[dev] = -1; + acard->devopl = NULL; + } else { + snd_fm_port[dev] = pdev->resource[0].start; + } + + return 0; +} + +static void snd_card_dt0197h_deactivate(struct snd_card_dt0197h *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } + if (acard->devmpu) { + acard->devmpu->deactivate(acard->devmpu); + acard->devmpu = NULL; + } + if (acard->devopl) { + acard->devopl->deactivate(acard->devopl); + acard->devopl = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_card_dt0197h_free(snd_card_t *card) +{ + struct snd_card_dt0197h *acard = (struct snd_card_dt0197h *)card->private_data; + + if (acard != NULL) { +#ifdef __ISAPNP__ + snd_card_dt0197h_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_dt0197h_probe(int dev) +{ + int error; + sb_t *chip; + snd_card_t *card; + struct snd_card_dt0197h *acard; + opl3_t *opl3; + + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_dt0197h))) == NULL) + return -ENOMEM; + acard = (struct snd_card_dt0197h *)card->private_data; + card->private_free = snd_card_dt0197h_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_dt0197h_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + snd_printk("you have to enable PnP support ...\n"); + snd_card_free(card); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_sbdsp_create(card, snd_port[dev], + snd_irq[dev], + snd_sb16dsp_interrupt, + snd_dma8[dev], + -1, + SB_HW_AUTO, + &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return error; + } + + if (snd_mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, + MPU401_HW_MPU401, + snd_mpu_port[dev], 0, + snd_mpu_irq[dev], + SA_INTERRUPT, + NULL) < 0) + snd_printk("no MPU-401 device at 0x%lx ?\n", + snd_mpu_port[dev]); + } + + if (snd_fm_port[dev] > 0) { + if (snd_opl3_create(card, + snd_fm_port[dev], + snd_fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + snd_printk("no OPL device at 0x%lx-0x%lx ?\n", + snd_fm_port[dev], snd_fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "DT-0197H"); + strcpy(card->shortname, "Diamond Tech. DT-0197H"); + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", + card->shortname, chip->name, chip->port, + snd_irq[dev], snd_dma8[dev]); + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_dt0197h_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_dt0197h_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_dt0197h_isapnp_cards[dev] = card; + snd_dt0197h_isapnp_id[dev] = id; + res = snd_card_dt0197h_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_dt0197h_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_dt0197h_pnpids, snd_dt0197h_isapnp_detect); +#else + snd_printk("you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + snd_printk("no DT-0197H based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_dt0197h_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_dt0197h_cards[dev]); +} + +module_init(alsa_card_dt0197h_init) +module_exit(alsa_card_dt0197h_exit) + +#ifndef MODULE + +/* format is: snd-dt0197h=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port,snd_mpu_port,snd_fm_port, + snd_irq,snd_mpu_irq,snd_dma8,snd_dma8_size */ + +static int __init alsa_card_dt0197h_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-dt0197h=", alsa_card_dt0197h_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/es1688/Makefile b/sound/isa/es1688/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/es1688/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,25 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _es1688.o + +list-multi := snd-es1688-lib.o snd-es1688.o + +export-objs := es1688_lib.o + +snd-es1688-lib-objs := es1688_lib.o +snd-es1688-objs := es1688.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ES1688) += snd-es1688.o snd-es1688-lib.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-es1688-lib.o + +include $(TOPDIR)/Rules.make + +snd-es1688-lib.o: $(snd-es1688-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1688-lib-objs) + +snd-es1688.o: $(snd-es1688-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1688-objs) diff -Nru a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/es1688/es1688.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,242 @@ +/* + * Driver for generic ESS AudioDrive ESx688 soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +#define chip_t es1688_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ESS ESx688 AudioDrive"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100}," + "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102}," + "{ESS,ES688 AudioDrive,pnp:ESS6881}," + "{ESS,ES1688 AudioDrive,pnp:ESS1681}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ESx688 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ESx688 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ESx688 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for ESx688 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ESx688 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for ESx688 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for ESx688 driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for ESx688 driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); + +static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_audiodrive_probe(int dev) +{ + static int possible_irqs[] = {5, 9, 10, 7, -1}; + static int possible_dmas[] = {1, 3, 0, -1}; + int irq, dma, mpu_irq; + snd_card_t *card; + es1688_t *chip; + opl3_t *opl3; + snd_pcm_t *pcm; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + mpu_irq = snd_mpu_irq[dev]; + dma = snd_dma8[dev]; + if (dma == SNDRV_AUTO_DMA) { + if ((dma = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA\n"); + return -EBUSY; + } + } + + if ((err = snd_es1688_create(card, snd_port[dev], snd_mpu_port[dev], + irq, mpu_irq, dma, + ES1688_HW_AUTO, &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1688_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1688_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((snd_opl3_create(card, chip->port, chip->port + 2, OPL3_HW_OPL3, 0, &opl3)) < 0) { + snd_printk("opl3 not detected at 0x%lx\n", chip->port); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if (mpu_irq >= 0) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, + chip->mpu_port, 0, + mpu_irq, + SA_INTERRUPT, + NULL)) < 0) { + snd_card_free(card); + return err; + } + } + strcpy(card->driver, "ES1688"); + strcpy(card->shortname, pcm->name); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port, irq, dma); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_audiodrive_cards[dev] = card; + return 0; + +} + +static int __init snd_audiodrive_legacy_auto_probe(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; + snd_port[dev] = port; + res = snd_audiodrive_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_es1688_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; + int dev, cards = 0; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_audiodrive_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + snd_printk("ESS AudioDrive ES1688 soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_es1688_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_audiodrive_cards[idx]); +} + +module_init(alsa_card_es1688_init) +module_exit(alsa_card_es1688_exit) + +#ifndef MODULE + +/* format is: snd-es1688=snd_enable,snd_index,snd_id, + snd_port,snd_mpu_port, + snd_irq,snd_mpu_irq, + snd_dma8 */ + +static int __init alsa_card_es1688_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es1688=", alsa_card_es1688_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/es1688/es1688_lib.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1059 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of ESS ES1688/688/488 chip + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ESS ESx688 lowlevel module"); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); + +#define chip_t es1688_t + +static int snd_es1688_dsp_command(es1688_t *chip, unsigned char val) +{ + int i; + + for (i = 10000; i; i--) + if ((inb(ES1688P(chip, STATUS)) & 0x80) == 0) { + outb(val, ES1688P(chip, COMMAND)); + return 1; + } +#ifdef CONFIG_SND_DEBUG + printk("snd_es1688_dsp_command: timeout (0x%x)\n", val); +#endif + return 0; +} + +static int snd_es1688_dsp_get_byte(es1688_t *chip) +{ + int i; + + for (i = 1000; i; i--) + if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) + return inb(ES1688P(chip, READ)); + snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL))); + return -ENODEV; +} + +static int snd_es1688_write(es1688_t *chip, + unsigned char reg, unsigned char data) +{ + if (!snd_es1688_dsp_command(chip, reg)) + return 0; + return snd_es1688_dsp_command(chip, data); +} + +int snd_es1688_read(es1688_t *chip, unsigned char reg) +{ + /* Read a byte from an extended mode register of ES1688 */ + if (!snd_es1688_dsp_command(chip, 0xc0)) + return -1; + if (!snd_es1688_dsp_command(chip, reg)) + return -1; + return snd_es1688_dsp_get_byte(chip); +} + +void snd_es1688_mixer_write(es1688_t *chip, + unsigned char reg, unsigned char data) +{ + outb(reg, ES1688P(chip, MIXER_ADDR)); + udelay(10); + outb(data, ES1688P(chip, MIXER_DATA)); + udelay(10); +} + +unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg) +{ + unsigned char result; + + outb(reg, ES1688P(chip, MIXER_ADDR)); + udelay(10); + result = inb(ES1688P(chip, MIXER_DATA)); + udelay(10); + return result; +} + +static int snd_es1688_reset(es1688_t *chip) +{ + int i; + + outb(3, ES1688P(chip, RESET)); /* valid only for ESS chips, SB -> 1 */ + udelay(10); + outb(0, ES1688P(chip, RESET)); + udelay(30); + for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++); + if (inb(ES1688P(chip, READ)) != 0xaa) { + snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port); + return -ENODEV; + } + snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */ + return 0; +} + +static int snd_es1688_probe(es1688_t *chip) +{ + unsigned long flags; + unsigned short major, minor, hw; + int i; + + /* + * initialization sequence + */ + + spin_lock_irqsave(&chip->reg_lock, flags); /* Some ESS1688 cards need this */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */ + + if (snd_es1688_reset(chip) < 0) { + snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ))); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENODEV; + } + snd_es1688_dsp_command(chip, 0xe7); /* return identification */ + + for (i = 1000, major = minor = 0; i; i--) { + if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) { + if (major == 0) { + major = inb(ES1688P(chip, READ)); + } else { + minor = inb(ES1688P(chip, READ)); + } + } + } + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor); + + chip->version = (major << 8) | minor; + if (!chip->version) + return -ENODEV; /* probably SB */ + + hw = ES1688_HW_AUTO; + switch (chip->version & 0xfff0) { + case 0x4880: + snd_printk("[0x%lx] ESS: AudioDrive ES488 detected, but driver is in another place\n", chip->port); + return -ENODEV; + case 0x6880: + hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688; + break; + default: + snd_printk("[0x%lx] ESS: unknown AudioDrive chip with version 0x%x (Jazz16 soundcard?)\n", chip->port, chip->version); + return -ENODEV; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ + snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* enable joystick, but disable OPL3 */ + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_es1688_mixer_write(chip, 0x40, 0x01); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + return 0; +} + +static int snd_es1688_init(es1688_t * chip, int enable) +{ + static int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1}; + unsigned long flags; + int cfg, irq_bits, dma, dma_bits, tmp, tmp1; + + /* ok.. setup MPU-401 port and joystick and OPL3 */ + cfg = 0x01; /* enable joystick, but disable OPL3 */ + if (enable && chip->mpu_port >= 0x300 && chip->mpu_irq > 0 && chip->hardware != ES1688_HW_688) { + tmp = (chip->mpu_port & 0x0f0) >> 4; + if (tmp <= 3) { + switch (chip->mpu_irq) { + case 9: + tmp1 = 4; + break; + case 5: + tmp1 = 5; + break; + case 7: + tmp1 = 6; + break; + case 10: + tmp1 = 7; + break; + default: + tmp1 = 0; + } + if (tmp1) { + cfg |= (tmp << 3) | (tmp1 << 5); + } + } + } +#if 0 + snd_printk("mpu cfg = 0x%x\n", cfg); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_mixer_write(chip, 0x40, cfg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + /* --- */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_read(chip, 0xb1); + snd_es1688_read(chip, 0xb2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (enable) { + cfg = 0xf0; /* enable only DMA counter interrupt */ + irq_bits = irqs[chip->irq & 0x0f]; + if (irq_bits < 0) { + snd_printk("[0x%lx] ESS: bad IRQ %d for ES1688 chip!!\n", chip->port, chip->irq); +#if 0 + irq_bits = 0; + cfg = 0x10; +#endif + return -EINVAL; + } + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + cfg = 0xf0; /* extended mode DMA enable */ + dma = chip->dma8; + if (dma > 3 || dma == 2) { + snd_printk("[0x%lx] ESS: bad DMA channel %d for ES1688 chip!!\n", chip->port, dma); +#if 0 + dma_bits = 0; + cfg = 0x00; /* disable all DMA */ +#endif + return -EINVAL; + } else { + dma_bits = dma; + if (dma != 3) + dma_bits++; + } + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + } else { + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ + snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_read(chip, 0xb1); + snd_es1688_read(chip, 0xb2); + snd_es1688_reset(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +/* + + */ + +static ratnum_t clocks[2] = { + { + num: 795444, + den_min: 1, + den_max: 128, + den_step: 1, + }, + { + num: 397722, + den_min: 1, + den_max: 128, + den_step: 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + nrats: 2, + rats: clocks, +}; + +static void snd_es1688_set_rate(es1688_t *chip, snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int bits, divider; + + if (runtime->rate_num == clocks[0].num) + bits = 256 - runtime->rate_den; + else + bits = 128 - runtime->rate_den; + /* set filter register */ + divider = 256 - 7160000*20/(8*82*runtime->rate); + /* write result to hardware */ + snd_es1688_write(chip, 0xa1, bits); + snd_es1688_write(chip, 0xa2, divider); +} + +static int snd_es1688_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_es1688_trigger(es1688_t *chip, int cmd, unsigned char value) +{ + int val; + + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + value = 0x00; + } else if (cmd != SNDRV_PCM_TRIGGER_START) { + return -EINVAL; + } + spin_lock(&chip->reg_lock); + chip->trigger_value = value; + val = snd_es1688_read(chip, 0xb8); + if ((val < 0) || (val & 0x0f) == value) { + spin_unlock(&chip->reg_lock); + return -EINVAL; /* something is wrong */ + } +#if 0 + printk("trigger: val = 0x%x, value = 0x%x\n", val, value); + printk("trigger: residue = 0x%x\n", get_dma_residue(chip->dma8)); +#endif + snd_es1688_write(chip, 0xb8, (val & 0xf0) | value); + spin_unlock(&chip->reg_lock); + return 0; +} + +static int snd_es1688_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_es1688_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_es1688_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_reset(chip); + snd_es1688_set_rate(chip, substream); + snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */ + snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); + snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ + if (runtime->channels == 1) { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit mono */ + snd_es1688_write(chip, 0xb6, 0x80); + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0xd0); + } else { + /* 16. bit mono */ + snd_es1688_write(chip, 0xb6, 0x00); + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xf4); + } + } else { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit stereo */ + snd_es1688_write(chip, 0xb6, 0x80); + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0x98); + } else { + /* 16. bit stereo */ + snd_es1688_write(chip, 0xb6, 0x00); + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xbc); + } + } + snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); + snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); + snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON); + spin_unlock_irqrestore(&chip->reg_lock, flags); + /* --- */ + count = -count; + snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xa4, (unsigned char) count); + snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_es1688_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + return snd_es1688_trigger(chip, cmd, 0x05); +} + +static int snd_es1688_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_reset(chip); + snd_es1688_set_rate(chip, substream); + snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF); + snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */ + snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); + snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ + if (runtime->channels == 1) { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit mono */ + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0xd0); + } else { + /* 16. bit mono */ + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xf4); + } + } else { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit stereo */ + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0x98); + } else { + /* 16. bit stereo */ + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xbc); + } + } + snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); + snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); + spin_unlock_irqrestore(&chip->reg_lock, flags); + /* --- */ + count = -count; + snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xa4, (unsigned char) count); + snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_es1688_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + return snd_es1688_trigger(chip, cmd, 0x0f); +} + +void snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es1688_t *chip = snd_magic_cast(es1688_t, dev_id, return); + + if (chip->trigger_value == 0x05) /* ok.. playback is active */ + snd_pcm_period_elapsed(chip->playback_substream); + if (chip->trigger_value == 0x0f) /* ok.. capture is active */ + snd_pcm_period_elapsed(chip->capture_substream); + + inb(ES1688P(chip, DATA_AVAIL)); /* ack interrupt */ +} + +static snd_pcm_uframes_t snd_es1688_playback_pointer(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->trigger_value != 0x05) + return 0; + ptr = chip->dma_size - snd_dma_residue(chip->dma8); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_es1688_capture_pointer(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->trigger_value != 0x0f) + return 0; + ptr = chip->dma_size - snd_dma_residue(chip->dma8); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static snd_pcm_hardware_t snd_es1688_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_es1688_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + + */ + +static int snd_es1688_playback_open(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (chip->capture_substream != NULL) + return -EAGAIN; + chip->playback_substream = substream; + runtime->hw = snd_es1688_playback; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + return 0; +} + +static int snd_es1688_capture_open(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (chip->playback_substream != NULL) + return -EAGAIN; + chip->capture_substream = substream; + runtime->hw = snd_es1688_capture; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + return 0; +} + +static int snd_es1688_playback_close(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + return 0; +} + +static int snd_es1688_capture_close(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + return 0; +} + +static int snd_es1688_free(es1688_t *chip) +{ + if (chip->res_port) { + snd_es1688_init(chip, 0); + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma8 >= 0) { + disable_dma(chip->dma8); + free_dma(chip->dma8); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_es1688_dev_free(snd_device_t *device) +{ + es1688_t *chip = snd_magic_cast(es1688_t, device->device_data, return -ENXIO); + return snd_es1688_free(chip); +} + +static const char *snd_es1688_chip_id(es1688_t *chip) +{ + static char tmp[16]; + sprintf(tmp, "ES%s688 rev %i", chip->hardware == ES1688_HW_688 ? "" : "1", chip->version & 0x0f); + return tmp; +} + +int snd_es1688_create(snd_card_t * card, + unsigned long port, + unsigned long mpu_port, + int irq, + int mpu_irq, + int dma8, + unsigned short hardware, + es1688_t **rchip) +{ + static snd_device_ops_t ops = { + dev_free: snd_es1688_dev_free, + }; + + es1688_t *chip; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(es1688_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->irq = -1; + chip->dma8 = -1; + + if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) { + snd_es1688_free(chip); + return -EBUSY; + } + if (request_irq(irq, snd_es1688_interrupt, SA_INTERRUPT, "ES1688", (void *) chip)) { + snd_es1688_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (request_dma(dma8, "ES1688")) { + snd_es1688_free(chip); + return -EBUSY; + } + chip->dma8 = dma8; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->mixer_lock); + chip->card = card; + chip->port = port; + mpu_port &= ~0x000f; + if (mpu_port < 0x300 || mpu_port > 0x330) + mpu_port = 0; + chip->mpu_port = mpu_port; + chip->mpu_irq = mpu_irq; + chip->hardware = hardware; + + if ((err = snd_es1688_probe(chip)) < 0) { + snd_es1688_free(chip); + return err; + } + if ((err = snd_es1688_init(chip, 1)) < 0) { + snd_es1688_free(chip); + return err; + } + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es1688_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_es1688_playback_ops = { + open: snd_es1688_playback_open, + close: snd_es1688_playback_close, + ioctl: snd_es1688_ioctl, + hw_params: snd_es1688_hw_params, + hw_free: snd_es1688_hw_free, + prepare: snd_es1688_playback_prepare, + trigger: snd_es1688_playback_trigger, + pointer: snd_es1688_playback_pointer, +}; + +static snd_pcm_ops_t snd_es1688_capture_ops = { + open: snd_es1688_capture_open, + close: snd_es1688_capture_close, + ioctl: snd_es1688_ioctl, + hw_params: snd_es1688_hw_params, + hw_free: snd_es1688_hw_free, + prepare: snd_es1688_capture_prepare, + trigger: snd_es1688_capture_trigger, + pointer: snd_es1688_capture_pointer, +}; + +static void snd_es1688_pcm_free(snd_pcm_t *pcm) +{ + es1688_t *chip = snd_magic_cast(es1688_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_es1688_pcm(es1688_t * chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "ESx688", device, 1, 1, &pcm)) < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1688_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_es1688_pcm_free; + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + sprintf(pcm->name, snd_es1688_chip_id(chip)); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER part + */ + +static int snd_es1688_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[9] = { + "Mic", "Mic Master", "CD", "AOUT", + "Mic1", "Mix", "Line", "Master" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_es1688_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = snd_es1688_mixer_read(chip, ES1688_REC_DEV) & 7; + return 0; +} + +static int snd_es1688_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char oval, nval; + int change; + + if (ucontrol->value.enumerated.item[0] > 8) + return -EINVAL; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV); + nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15); + change = nval != oval; + if (change) + snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es1688_info_single, \ + get: snd_es1688_get_single, put: snd_es1688_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_es1688_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1688_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_es1688_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned char oval, nval; + + nval = (ucontrol->value.integer.value[0] & mask); + if (invert) + nval = mask - nval; + nval <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_es1688_mixer_read(chip, reg); + nval = (oval & ~(mask << shift)) | nval; + change = nval != oval; + if (change) + snd_es1688_mixer_write(chip, reg, nval); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es1688_info_double, \ + get: snd_es1688_get_double, put: snd_es1688_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_es1688_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1688_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + unsigned char left, right; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg < 0xa0) + left = snd_es1688_mixer_read(chip, left_reg); + else + left = snd_es1688_read(chip, left_reg); + if (left_reg != right_reg) { + if (right_reg < 0xa0) + right = snd_es1688_mixer_read(chip, right_reg); + else + right = snd_es1688_read(chip, right_reg); + } else + right = left; + spin_unlock_irqrestore(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (left >> shift_left) & mask; + ucontrol->value.integer.value[1] = (right >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_es1688_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned char val1, val2, oval1, oval2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + if (left_reg < 0xa0) + oval1 = snd_es1688_mixer_read(chip, left_reg); + else + oval1 = snd_es1688_read(chip, left_reg); + if (right_reg < 0xa0) + oval2 = snd_es1688_mixer_read(chip, right_reg); + else + oval2 = snd_es1688_read(chip, right_reg); + val1 = (oval1 & ~(mask << shift_left)) | val1; + val2 = (oval2 & ~(mask << shift_right)) | val2; + change = val1 != oval1 || val2 != oval2; + if (change) { + if (left_reg < 0xa0) + snd_es1688_mixer_write(chip, left_reg, val1); + else + snd_es1688_write(chip, left_reg, val1); + if (right_reg < 0xa0) + snd_es1688_mixer_write(chip, right_reg, val1); + else + snd_es1688_write(chip, right_reg, val1); + } + } else { + if (left_reg < 0xa0) + oval1 = snd_es1688_mixer_read(chip, left_reg); + else + oval1 = snd_es1688_read(chip, left_reg); + val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != oval1; + if (change) { + if (left_reg < 0xa0) + snd_es1688_mixer_write(chip, left_reg, val1); + else + snd_es1688_write(chip, left_reg, val1); + } + + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define ES1688_CONTROLS (sizeof(snd_es1688_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_es1688_controls[] = { +ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0), +ES1688_SINGLE("PC Speaker Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0), +ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0), +ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_es1688_info_mux, + get: snd_es1688_get_mux, + put: snd_es1688_put_mux, +}, +}; + +#define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2) + +static unsigned char snd_es1688_init_table[][2] = { + { ES1688_MASTER_DEV, 0 }, + { ES1688_PCM_DEV, 0 }, + { ES1688_LINE_DEV, 0 }, + { ES1688_CD_DEV, 0 }, + { ES1688_FM_DEV, 0 }, + { ES1688_MIC_DEV, 0 }, + { ES1688_AUX_DEV, 0 }, + { ES1688_SPEAKER_DEV, 0 }, + { ES1688_RECLEV_DEV, 0 }, + { ES1688_REC_DEV, 0x17 } +}; + +int snd_es1688_mixer(es1688_t *chip) +{ + snd_card_t *card; + int err, idx; + unsigned char reg, val; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, snd_es1688_chip_id(chip)); + + for (idx = 0; idx < ES1688_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0) + return err; + } + for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) { + reg = snd_es1688_init_table[idx][0]; + val = snd_es1688_init_table[idx][1]; + if (reg < 0xa0) + snd_es1688_mixer_write(chip, reg, val); + else + snd_es1688_write(chip, reg, val); + } + return 0; +} + +EXPORT_SYMBOL(snd_es1688_mixer_write); +EXPORT_SYMBOL(snd_es1688_mixer_read); +EXPORT_SYMBOL(snd_es1688_interrupt); +EXPORT_SYMBOL(snd_es1688_create); +EXPORT_SYMBOL(snd_es1688_pcm); +EXPORT_SYMBOL(snd_es1688_mixer); + +/* + * INIT part + */ + +static int __init alsa_es1688_init(void) +{ + return 0; +} + +static void __exit alsa_es1688_exit(void) +{ +} + +module_init(alsa_es1688_init) +module_exit(alsa_es1688_exit) diff -Nru a/sound/isa/es18xx.c b/sound/isa/es18xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/es18xx.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2313 @@ +/* + * Driver for generic ESS AudioDrive ES18xx soundcards + * Copyright (c) by Christian Fischbach + * Copyright (c) by Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* GENERAL NOTES: + * + * BUGS: + * - There are pops (we can't delay in trigger function, cause midlevel + * often need to trigger down and then up very quickly). + * Any ideas? + * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it. + */ + +/* + * ES1868 NOTES: + * - The chip has one half duplex pcm (with very limited full duplex support). + * + * - Duplex stereophonic sound is impossible. + * - Record and playback must share the same frequency rate. + * + * - The driver use dma2 for playback and dma1 for capture. + */ + +/* + * ES1869 NOTES: + * + * - there are a first full duplex pcm and a second playback only pcm + * (incompatible with first pcm capture) + * + * - there is support for the capture volume and ESS Spatializer 3D effect. + * + * - contrarily to some pages in DS_1869.PDF the rates can be set + * independently. + * + * BUGS: + * + * - There is a major trouble I noted: + * + * using both channel for playback stereo 16 bit samples at 44100 Hz + * the second pcm (Audio1) DMA slows down irregularly and sound is garbled. + * + * The same happens using Audio1 for captureing. + * + * The Windows driver does not suffer of this (although it use Audio1 + * only for captureing). I'm unable to discover why. + * + */ + + +#include +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +struct _snd_es18xx { + unsigned long port; /* port of ESS chip */ + unsigned long mpu_port; /* MPU-401 port of ESS chip */ + unsigned long fm_port; /* FM port */ + unsigned long ctrl_port; /* Control port of ESS chip */ + struct resource *res_port; + struct resource *res_mpu_port; + struct resource *res_ctrl_port; + int irq; /* IRQ number of ESS chip */ + int dma1; /* DMA1 */ + int dma2; /* DMA2 */ + unsigned short version; /* version of ESS chip */ + int caps; /* Chip capabilities */ + unsigned short audio2_vol; /* volume level of audio2 */ + + unsigned short active; /* active channel mask */ + unsigned int dma1_size; + unsigned int dma2_size; + unsigned int dma1_shift; + unsigned int dma2_shift; + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_a_substream; + snd_pcm_substream_t *capture_a_substream; + snd_pcm_substream_t *playback_b_substream; + + snd_rawmidi_t *rmidi; + + snd_kcontrol_t *hw_volume; + snd_kcontrol_t *hw_switch; + snd_kcontrol_t *master_volume; + snd_kcontrol_t *master_switch; + + spinlock_t reg_lock; + spinlock_t mixer_lock; + spinlock_t ctrl_lock; +#ifdef CONFIG_PM + struct pm_dev *pm_dev; + unsigned char pm_reg; +#endif +}; + +#define AUDIO1_IRQ 0x01 +#define AUDIO2_IRQ 0x02 +#define HWV_IRQ 0x04 +#define MPU_IRQ 0x08 + +#define ES18XX_PCM2 0x0001 /* Has two useable PCM */ +#define ES18XX_SPATIALIZER 0x0002 /* Has 3D Spatializer */ +#define ES18XX_RECMIX 0x0004 /* Has record mixer */ +#define ES18XX_DUPLEX_MONO 0x0008 /* Has mono duplex only */ +#define ES18XX_DUPLEX_SAME 0x0010 /* Playback and record must share the same rate */ +#define ES18XX_NEW_RATE 0x0020 /* More precise rate setting */ +#define ES18XX_AUXB 0x0040 /* AuxB mixer control */ +#define ES18XX_HWV 0x0080 /* Has hardware volume */ +#define ES18XX_MONO 0x0100 /* Mono_in mixer control */ +#define ES18XX_I2S 0x0200 /* I2S mixer control */ +#define ES18XX_MUTEREC 0x0400 /* Record source can be muted */ +#define ES18XX_CONTROL 0x0800 /* Has control ports */ + +/* Power Management */ +#define ES18XX_PM 0x07 +#define ES18XX_PM_GPO0 0x01 +#define ES18XX_PM_GPO1 0x02 +#define ES18XX_PM_PDR 0x03 +#define ES18XX_PM_ANA 0x04 +#define ES18XX_PM_FM 0x06 +#define ES18XX_PM_SUS 0x08 + +typedef struct _snd_es18xx es18xx_t; + +#define chip_t es18xx_t + +/* Lowlevel */ + +#define DAC1 0x01 +#define ADC1 0x02 +#define DAC2 0x04 +#define MILLISECOND 10000 + +static int snd_es18xx_dsp_command(es18xx_t *chip, unsigned char val) +{ + int i; + + for(i = MILLISECOND; i; i--) + if ((inb(chip->port + 0x0C) & 0x80) == 0) { + outb(val, chip->port + 0x0C); + return 0; + } + snd_printk("dsp_command: timeout (0x%x)\n", val); + return -EINVAL; +} + +static int snd_es18xx_dsp_get_byte(es18xx_t *chip) +{ + int i; + + for(i = MILLISECOND/10; i; i--) + if (inb(chip->port + 0x0C) & 0x40) + return inb(chip->port + 0x0A); + snd_printk("dsp_get_byte failed: 0x%lx = 0x%x!!!\n", chip->port + 0x0A, inb(chip->port + 0x0A)); + return -ENODEV; +} + +#undef REG_DEBUG + +static int snd_es18xx_write(es18xx_t *chip, + unsigned char reg, unsigned char data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chip->reg_lock, flags); + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_command(chip, data); + end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +#ifdef REG_DEBUG + snd_printk("Reg %02x set to %02x\n", reg, data); +#endif + return ret; +} + +static int snd_es18xx_read(es18xx_t *chip, unsigned char reg) +{ + unsigned long flags; + int ret, data; + spin_lock_irqsave(&chip->reg_lock, flags); + ret = snd_es18xx_dsp_command(chip, 0xC0); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + data = snd_es18xx_dsp_get_byte(chip); + ret = data; +#ifdef REG_DEBUG + snd_printk("Reg %02x now is %02x (%d)\n", reg, data, ret); +#endif + end: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return ret; +} + +/* Return old value */ +static int snd_es18xx_bits(es18xx_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + int ret; + unsigned char old, new, oval; + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + ret = snd_es18xx_dsp_command(chip, 0xC0); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_get_byte(chip); + if (ret < 0) { + goto end; + } + old = ret; + oval = old & mask; + if (val != oval) { + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + new = (old & ~mask) | (val & mask); + ret = snd_es18xx_dsp_command(chip, new); + if (ret < 0) + goto end; +#ifdef REG_DEBUG + snd_printk("Reg %02x was %02x, set to %02x (%d)\n", reg, old, new, ret); +#endif + } + ret = oval; + end: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return ret; +} + +inline void snd_es18xx_mixer_write(es18xx_t *chip, + unsigned char reg, unsigned char data) +{ + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + outb(data, chip->port + 0x05); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x set to %02x\n", reg, data); +#endif +} + +inline int snd_es18xx_mixer_read(es18xx_t *chip, unsigned char reg) +{ + unsigned long flags; + int data; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + data = inb(chip->port + 0x05); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x now is %02x\n", reg, data); +#endif + return data; +} + +/* Return old value */ +static inline int snd_es18xx_mixer_bits(es18xx_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + unsigned char old, new, oval; + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + old = inb(chip->port + 0x05); + oval = old & mask; + if (val != oval) { + new = (old & ~mask) | (val & mask); + outb(new, chip->port + 0x05); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new); +#endif + } + spin_unlock_irqrestore(&chip->mixer_lock, flags); + return oval; +} + +static inline int snd_es18xx_mixer_writable(es18xx_t *chip, unsigned char reg, + unsigned char mask) +{ + int old, expected, new; + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + old = inb(chip->port + 0x05); + expected = old ^ mask; + outb(expected, chip->port + 0x05); + new = inb(chip->port + 0x05); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x was %02x, set to %02x, now is %02x\n", reg, old, expected, new); +#endif + return expected == new; +} + + +static int snd_es18xx_reset(es18xx_t *chip) +{ + int i; + outb(0x03, chip->port + 0x06); + inb(chip->port + 0x06); + outb(0x00, chip->port + 0x06); + for(i = 0; i < MILLISECOND && !(inb(chip->port + 0x0E) & 0x80); i++); + if (inb(chip->port + 0x0A) != 0xAA) + return -1; + return 0; +} + +static int snd_es18xx_reset_fifo(es18xx_t *chip) +{ + outb(0x02, chip->port + 0x06); + inb(chip->port + 0x06); + outb(0x00, chip->port + 0x06); + return 0; +} + +static ratnum_t new_clocks[2] = { + { + num: 793800, + den_min: 1, + den_max: 128, + den_step: 1, + }, + { + num: 768000, + den_min: 1, + den_max: 128, + den_step: 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t new_hw_constraints_clocks = { + nrats: 2, + rats: new_clocks, +}; + +static ratnum_t old_clocks[2] = { + { + num: 795444, + den_min: 1, + den_max: 128, + den_step: 1, + }, + { + num: 397722, + den_min: 1, + den_max: 128, + den_step: 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t old_hw_constraints_clocks = { + nrats: 2, + rats: old_clocks, +}; + + +static void snd_es18xx_rate_set(es18xx_t *chip, + snd_pcm_substream_t *substream, + int mode) +{ + unsigned int bits, div0; + snd_pcm_runtime_t *runtime = substream->runtime; + if (chip->caps & ES18XX_NEW_RATE) { + if (runtime->rate_num == new_clocks[0].num) + bits = 128 - runtime->rate_den; + else + bits = 256 - runtime->rate_den; + } else { + if (runtime->rate_num == old_clocks[0].num) + bits = 256 - runtime->rate_den; + else + bits = 128 - runtime->rate_den; + } + + /* set filter register */ + div0 = 256 - 7160000*20/(8*82*runtime->rate); + + if ((chip->caps & ES18XX_PCM2) && mode == DAC2) { + snd_es18xx_mixer_write(chip, 0x70, bits); + snd_es18xx_mixer_write(chip, 0x72, div0); + } else { + snd_es18xx_write(chip, 0xA1, bits); + snd_es18xx_write(chip, 0xA2, div0); + } +} + +static int snd_es18xx_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int shift, err; + + shift = 0; + if (params_channels(hw_params) == 2) + shift++; + if (snd_pcm_format_width(params_format(hw_params)) == 16) + shift++; + + switch (substream->number) { + case 0: + if ((chip->caps & ES18XX_DUPLEX_MONO) && + (chip->capture_a_substream) && + params_channels(hw_params) != 1) { + _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); + return -EBUSY; + } + chip->dma2_shift = shift; + break; + case 1: + chip->dma1_shift = shift; + break; + } + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + return 0; +} + +static int snd_es18xx_playback1_prepare(es18xx_t *chip, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma2_size = size; + + snd_es18xx_rate_set(chip, substream, DAC2); + + /* Transfer Count Reload */ + count = 0x10000 - count; + snd_es18xx_mixer_write(chip, 0x74, count & 0xff); + snd_es18xx_mixer_write(chip, 0x76, count >> 8); + + /* Set format */ + snd_es18xx_mixer_bits(chip, 0x7A, 0x07, + ((runtime->channels == 1) ? 0x00 : 0x02) | + (snd_pcm_format_width(runtime->format) == 16 ? 0x01 : 0x00) | + (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x04)); + + /* Set DMA controller */ + snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + + return 0; +} + +static int snd_es18xx_playback1_trigger(es18xx_t *chip, + snd_pcm_substream_t * substream, + int cmd) +{ + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (chip->active & DAC2) + return 0; + chip->active |= DAC2; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (!(chip->active & DAC2)) + return 0; + chip->active &= ~DAC2; + } else { + return -EINVAL; + } + + if (cmd == SNDRV_PCM_TRIGGER_START) { + /* Start DMA */ + if (chip->dma2 >= 4) + snd_es18xx_mixer_write(chip, 0x78, 0xb3); + else + snd_es18xx_mixer_write(chip, 0x78, 0x93); +#ifdef AVOID_POPS + /* Avoid pops */ + udelay(100000); + if (chip->caps & ES18XX_PCM2) + /* Restore Audio 2 volume */ + snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol); + else + /* Enable PCM output */ + snd_es18xx_dsp_command(chip, 0xD1); +#endif + } + else { + /* Stop DMA */ + snd_es18xx_mixer_write(chip, 0x78, 0x00); +#ifdef AVOID_POPS + udelay(25000); + if (chip->caps & ES18XX_PCM2) + /* Set Audio 2 volume to 0 */ + snd_es18xx_mixer_write(chip, 0x7C, 0); + else + /* Disable PCM output */ + snd_es18xx_dsp_command(chip, 0xD3); +#endif + } + + return 0; +} + +static int snd_es18xx_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int shift, err; + + shift = 0; + if ((chip->caps & ES18XX_DUPLEX_MONO) && + chip->playback_a_substream && + params_channels(hw_params) != 1) { + _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); + return -EBUSY; + } + if (params_channels(hw_params) == 2) + shift++; + if (snd_pcm_format_width(params_format(hw_params)) == 16) + shift++; + chip->dma1_shift = shift; + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + return 0; +} + +static int snd_es18xx_capture_prepare(snd_pcm_substream_t *substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + + snd_es18xx_reset_fifo(chip); + + /* Set stereo/mono */ + snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); + + snd_es18xx_rate_set(chip, substream, ADC1); + + /* Transfer Count Reload */ + count = 0x10000 - count; + snd_es18xx_write(chip, 0xA4, count & 0xff); + snd_es18xx_write(chip, 0xA5, count >> 8); + +#ifdef AVOID_POPS + udelay(100000); +#endif + + /* Set format */ + snd_es18xx_write(chip, 0xB7, + snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); + snd_es18xx_write(chip, 0xB7, 0x90 | + ((runtime->channels == 1) ? 0x40 : 0x08) | + (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | + (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); + + /* Set DMA controler */ + snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + + return 0; +} + +static int snd_es18xx_capture_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (chip->active & ADC1) + return 0; + chip->active |= ADC1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (!(chip->active & ADC1)) + return 0; + chip->active &= ~ADC1; + } else { + return -EINVAL; + } + + if (cmd == SNDRV_PCM_TRIGGER_START) + /* Start DMA */ + snd_es18xx_write(chip, 0xB8, 0x0f); + else + /* Stop DMA */ + snd_es18xx_write(chip, 0xB8, 0x00); + return 0; +} + +static int snd_es18xx_playback2_prepare(es18xx_t *chip, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + + snd_es18xx_reset_fifo(chip); + + /* Set stereo/mono */ + snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); + + snd_es18xx_rate_set(chip, substream, DAC1); + + /* Transfer Count Reload */ + count = 0x10000 - count; + snd_es18xx_write(chip, 0xA4, count & 0xff); + snd_es18xx_write(chip, 0xA5, count >> 8); + + /* Set format */ + snd_es18xx_write(chip, 0xB6, + snd_pcm_format_unsigned(runtime->format) ? 0x80 : 0x00); + snd_es18xx_write(chip, 0xB7, + snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); + snd_es18xx_write(chip, 0xB7, 0x90 | + (runtime->channels == 1 ? 0x40 : 0x08) | + (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | + (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); + + /* Set DMA controler */ + snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + + return 0; +} + +static int snd_es18xx_playback2_trigger(es18xx_t *chip, + snd_pcm_substream_t *substream, + int cmd) +{ + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (chip->active & DAC1) + return 0; + chip->active |= DAC1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (!(chip->active & DAC1)) + return 0; + chip->active &= ~DAC1; + } else { + return -EINVAL; + } + + if (cmd == SNDRV_PCM_TRIGGER_START) { + /* Start DMA */ + snd_es18xx_write(chip, 0xB8, 0x05); +#ifdef AVOID_POPS + /* Avoid pops */ + udelay(100000); + /* Enable Audio 1 */ + snd_es18xx_dsp_command(chip, 0xD1); +#endif + } + else { + /* Stop DMA */ + snd_es18xx_write(chip, 0xB8, 0x00); +#ifdef AVOID_POPS + /* Avoid pops */ + udelay(25000); + /* Disable Audio 1 */ + snd_es18xx_dsp_command(chip, 0xD3); +#endif + } + return 0; +} + +static int snd_es18xx_playback_prepare(snd_pcm_substream_t *substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + switch (substream->number) { + case 0: + return snd_es18xx_playback1_prepare(chip, substream); + case 1: + return snd_es18xx_playback2_prepare(chip, substream); + } + return -EINVAL; +} + +static int snd_es18xx_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + switch (substream->number) { + case 0: + return snd_es18xx_playback1_trigger(chip, substream, cmd); + case 1: + return snd_es18xx_playback2_trigger(chip, substream, cmd); + } + return -EINVAL; +} + +static void snd_es18xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, dev_id, return); + unsigned char status; + + + if (chip->caps & ES18XX_CONTROL) { + /* Read Interrupt status */ + status = inb(chip->ctrl_port + 6); + } else { + /* Read Interrupt status */ + status = snd_es18xx_mixer_read(chip, 0x7f) >> 4; + } +#if 0 + else { + status = 0; + if (inb(chip->port + 0x0C) & 0x01) + status |= AUDIO1_IRQ; + if (snd_es18xx_mixer_read(chip, 0x7A) & 0x80) + status |= AUDIO2_IRQ; + if ((chip->caps & ES18XX_HWV) && + snd_es18xx_mixer_read(chip, 0x64) & 0x10) + status |= HWV_IRQ; + } +#endif + + /* Audio 1 & Audio 2 */ + if (status & AUDIO2_IRQ) { + if (chip->active & DAC2) + snd_pcm_period_elapsed(chip->playback_a_substream); + /* ack interrupt */ + snd_es18xx_mixer_bits(chip, 0x7A, 0x80, 0x00); + } + if (status & AUDIO1_IRQ) { + /* ok.. capture is active */ + if (chip->active & ADC1) + snd_pcm_period_elapsed(chip->capture_a_substream); + /* ok.. playback2 is active */ + else if (chip->active & DAC1) + snd_pcm_period_elapsed(chip->playback_b_substream); + /* ack interrupt */ + inb(chip->port + 0x0E); + } + + /* MPU */ + if ((status & MPU_IRQ) && chip->rmidi) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + + /* Hardware volume */ + if (status & HWV_IRQ) { + int split = snd_es18xx_mixer_read(chip, 0x64) & 0x80; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id); + if (!split) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); + } + /* ack interrupt */ + snd_es18xx_mixer_write(chip, 0x66, 0x00); + } + +} + +static snd_pcm_uframes_t snd_es18xx_playback_pointer(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int pos; + + switch (substream->number) { + case 0: + if (!(chip->active & DAC2)) + return 0; + pos = chip->dma2_size - snd_dma_residue(chip->dma2); + return pos >> chip->dma2_shift; + case 1: + if (!(chip->active & DAC1)) + return 0; + pos = chip->dma1_size - snd_dma_residue(chip->dma1); + return pos >> chip->dma1_shift; + } + return 0; +} + +static snd_pcm_uframes_t snd_es18xx_capture_pointer(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int pos; + + if (!(chip->active & ADC1)) + return 0; + pos = chip->dma1_size - snd_dma_residue(chip->dma1); + return pos >> chip->dma1_shift; +} + +static snd_pcm_hardware_t snd_es18xx_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_es18xx_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_es18xx_playback_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es18xx_t *chip = snd_pcm_substream_chip(substream); + + switch (substream->number) { + case 0: + if ((chip->caps & ES18XX_DUPLEX_MONO) && + chip->capture_a_substream && + chip->capture_a_substream->runtime->channels != 1) + return -EAGAIN; + chip->playback_a_substream = substream; + break; + case 1: + if (chip->capture_a_substream) + return -EAGAIN; + chip->playback_b_substream = substream; + break; + default: + snd_BUG(); + return -EINVAL; + } + substream->runtime->hw = snd_es18xx_playback; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); + return 0; +} + +static int snd_es18xx_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es18xx_t *chip = snd_pcm_substream_chip(substream); + + if (chip->playback_b_substream) + return -EAGAIN; + if ((chip->caps & ES18XX_DUPLEX_MONO) && + chip->playback_a_substream && + chip->playback_a_substream->runtime->channels != 1) + return -EAGAIN; + chip->capture_a_substream = substream; + substream->runtime->hw = snd_es18xx_capture; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); + return 0; +} + +static int snd_es18xx_playback_close(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + + switch (substream->number) { + case 0: + chip->playback_a_substream = NULL; + break; + case 1: + chip->playback_b_substream = NULL; + break; + default: + snd_BUG(); + return -EINVAL; + } + + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_es18xx_capture_close(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_a_substream = NULL; + snd_pcm_lib_free_pages(substream); + return 0; +} + +/* + * MIXER part + */ + +static int snd_es18xx_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Mic", "Mic Master", "CD", "AOUT", + "Mic1", "Mix", "Line", "Master" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_es18xx_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = snd_es18xx_mixer_read(chip, 0x1c) & 0x07; + return 0; +} + +static int snd_es18xx_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = ucontrol->value.enumerated.item[0]; + + if (val > 7) + return -EINVAL; + return snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val; +} + +static int snd_es18xx_info_spatializer_enable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es18xx_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = snd_es18xx_mixer_read(chip, 0x50); + ucontrol->value.integer.value[0] = !!(val & 8); + return 0; +} + +static int snd_es18xx_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char oval, nval; + int change; + nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04; + oval = snd_es18xx_mixer_read(chip, 0x50) & 0x0c; + change = nval != oval; + if (change) { + snd_es18xx_mixer_write(chip, 0x50, nval & ~0x04); + snd_es18xx_mixer_write(chip, 0x50, nval); + } + return change; +} + +static int snd_es18xx_info_hw_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 63; + return 0; +} + +static int snd_es18xx_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = snd_es18xx_mixer_read(chip, 0x61) & 0x3f; + ucontrol->value.integer.value[1] = snd_es18xx_mixer_read(chip, 0x63) & 0x3f; + return 0; +} + +static int snd_es18xx_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es18xx_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = !(snd_es18xx_mixer_read(chip, 0x61) & 0x40); + ucontrol->value.integer.value[1] = !(snd_es18xx_mixer_read(chip, 0x63) & 0x40); + return 0; +} + +static void snd_es18xx_hwv_free(snd_kcontrol_t *kcontrol) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, _snd_kcontrol_chip(kcontrol), return); + chip->master_volume = NULL; + chip->master_switch = NULL; + chip->hw_volume = NULL; + chip->hw_switch = NULL; +} + +static int snd_es18xx_reg_bits(es18xx_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + if (reg < 0xa0) + return snd_es18xx_mixer_bits(chip, reg, mask, val); + else + return snd_es18xx_bits(chip, reg, mask, val); +} + +static int snd_es18xx_reg_read(es18xx_t *chip, unsigned char reg) +{ + if (reg < 0xa0) + return snd_es18xx_mixer_read(chip, reg); + else + return snd_es18xx_read(chip, reg); +} + +#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es18xx_info_single, \ + get: snd_es18xx_get_single, put: snd_es18xx_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_es18xx_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es18xx_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int val; + + val = snd_es18xx_reg_read(chip, reg); + ucontrol->value.integer.value[0] = (val >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_es18xx_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned char val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + mask <<= shift; + val <<= shift; + return snd_es18xx_reg_bits(chip, reg, mask, val) != val; +} + +#define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es18xx_info_double, \ + get: snd_es18xx_get_double, put: snd_es18xx_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_es18xx_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es18xx_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + unsigned char left, right; + + left = snd_es18xx_reg_read(chip, left_reg); + if (left_reg != right_reg) + right = snd_es18xx_reg_read(chip, right_reg); + else + right = left; + ucontrol->value.integer.value[0] = (left >> shift_left) & mask; + ucontrol->value.integer.value[1] = (right >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_es18xx_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned char val1, val2, mask1, mask2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + mask1 = mask << shift_left; + mask2 = mask << shift_right; + if (left_reg != right_reg) { + change = 0; + if (snd_es18xx_reg_bits(chip, left_reg, mask1, val1) != val1) + change = 1; + if (snd_es18xx_reg_bits(chip, right_reg, mask2, val2) != val2) + change = 1; + } else { + change = (snd_es18xx_reg_bits(chip, left_reg, mask1 | mask2, + val1 | val2) != (val1 | val2)); + } + return change; +} + +static snd_kcontrol_new_t snd_es18xx_base_controls[] = { +ES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), +ES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), +ES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), +ES18XX_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), +ES18XX_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), +ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), +ES18XX_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), +ES18XX_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), +ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0), +ES18XX_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), +ES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), +ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_es18xx_info_mux, + get: snd_es18xx_get_mux, + put: snd_es18xx_put_mux, +} +}; + +static snd_kcontrol_new_t snd_es18xx_mono_in_control = +ES18XX_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0); + +static snd_kcontrol_new_t snd_es18xx_recmix_controls[] = { +ES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), +ES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), +ES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), +ES18XX_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), +ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0), +ES18XX_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), +ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0) +}; + +static snd_kcontrol_new_t snd_es18xx_pcm1_controls[] = { +ES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0), +}; + +static snd_kcontrol_new_t snd_es18xx_pcm2_controls[] = { +ES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), +ES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0) +}; + +static snd_kcontrol_new_t snd_es18xx_spatializer_controls[] = { +ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "3D Control - Switch", + info: snd_es18xx_info_spatializer_enable, + get: snd_es18xx_get_spatializer_enable, + put: snd_es18xx_put_spatializer_enable, +} +}; + +static snd_kcontrol_new_t snd_es18xx_micpre1_control = +ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0); + +static snd_kcontrol_new_t snd_es18xx_micpre2_control = +ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0); + +static snd_kcontrol_new_t snd_es18xx_hw_volume_controls[] = { +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Hardware Master Playback Volume", + access: SNDRV_CTL_ELEM_ACCESS_READ, + info: snd_es18xx_info_hw_volume, + get: snd_es18xx_get_hw_volume, +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Hardware Master Playback Switch", + access: SNDRV_CTL_ELEM_ACCESS_READ, + info: snd_es18xx_info_hw_switch, + get: snd_es18xx_get_hw_switch, +}, +ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0), +}; + +#if 0 +static int __init snd_es18xx_config_read(es18xx_t *chip, unsigned char reg) +{ + int data; + unsigned long flags; + spin_lock_irqsave(&chip->ctrl_lock, flags); + outb(reg, chip->ctrl_port); + data = inb(chip->ctrl_port + 1); + spin_unlock_irqrestore(&chip->ctrl_lock, flags); + return data; +} +#endif + +static void __init snd_es18xx_config_write(es18xx_t *chip, + unsigned char reg, unsigned char data) +{ + /* No need for spinlocks, this function is used only in + otherwise protected init code */ + outb(reg, chip->ctrl_port); + outb(data, chip->ctrl_port + 1); +#ifdef REG_DEBUG + snd_printk("Config reg %02x set to %02x\n", reg, data); +#endif +} + +static int __init snd_es18xx_initialize(es18xx_t *chip) +{ + int mask = 0; + + /* enable extended mode */ + snd_es18xx_dsp_command(chip, 0xC6); + /* Reset mixer registers */ + snd_es18xx_mixer_write(chip, 0x00, 0x00); + + /* Audio 1 DMA demand mode (4 bytes/request) */ + snd_es18xx_write(chip, 0xB9, 2); + if (chip->caps & ES18XX_CONTROL) { + /* Hardware volume IRQ */ + snd_es18xx_config_write(chip, 0x27, chip->irq); + if (chip->fm_port > SNDRV_AUTO_PORT) { + /* FM I/O */ + snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8); + snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff); + } + if (chip->mpu_port > SNDRV_AUTO_PORT) { + /* MPU-401 I/O */ + snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8); + snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff); + /* MPU-401 IRQ */ + snd_es18xx_config_write(chip, 0x28, chip->irq); + } + /* Audio1 IRQ */ + snd_es18xx_config_write(chip, 0x70, chip->irq); + /* Audio2 IRQ */ + snd_es18xx_config_write(chip, 0x72, chip->irq); + /* Audio1 DMA */ + snd_es18xx_config_write(chip, 0x74, chip->dma1); + /* Audio2 DMA */ + snd_es18xx_config_write(chip, 0x75, chip->dma2); + + /* Enable Audio 1 IRQ */ + snd_es18xx_write(chip, 0xB1, 0x50); + /* Enable Audio 2 IRQ */ + snd_es18xx_mixer_write(chip, 0x7A, 0x40); + /* Enable Audio 1 DMA */ + snd_es18xx_write(chip, 0xB2, 0x50); + /* Enable MPU and hardware volume interrupt */ + snd_es18xx_mixer_write(chip, 0x64, 0x42); + } + else { + int irqmask, dma1mask, dma2mask; + switch (chip->irq) { + case 2: + case 9: + irqmask = 0; + break; + case 5: + irqmask = 1; + break; + case 7: + irqmask = 2; + break; + case 10: + irqmask = 3; + break; + default: + snd_printk("invalid irq %d\n", chip->irq); + return -ENODEV; + } + switch (chip->dma1) { + case 0: + dma1mask = 1; + break; + case 1: + dma1mask = 2; + break; + case 3: + dma1mask = 3; + break; + default: + snd_printk("invalid dma1 %d\n", chip->dma1); + return -ENODEV; + } + switch (chip->dma2) { + case 0: + dma2mask = 0; + break; + case 1: + dma2mask = 1; + break; + case 3: + dma2mask = 2; + break; + case 5: + dma2mask = 3; + break; + default: + snd_printk("invalid dma2 %d\n", chip->dma2); + return -ENODEV; + } + + /* Enable and set Audio 1 IRQ */ + snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2)); + /* Enable and set Audio 1 DMA */ + snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2)); + /* Set Audio 2 DMA */ + snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask); + /* Enable Audio 2 IRQ and DMA + Set capture mixer input */ + snd_es18xx_mixer_write(chip, 0x7A, 0x68); + /* Enable and set hardware volume interrupt */ + snd_es18xx_mixer_write(chip, 0x64, 0x06); + if (chip->mpu_port > SNDRV_AUTO_PORT) { + /* MPU401 share irq with audio + Joystick enabled + FM enabled */ + snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1); + } + snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01); + } + if (chip->caps & ES18XX_NEW_RATE) { + /* Change behaviour of register A1 + 4x oversampling + 2nd channel DAC asynchronous */ + snd_es18xx_mixer_write(chip, 0x71, 0x32); + } + if (!(chip->caps & ES18XX_PCM2)) { + /* Enable DMA FIFO */ + snd_es18xx_write(chip, 0xB7, 0x80); + } + if (chip->caps & ES18XX_SPATIALIZER) { + /* Set spatializer parameters to recommended values */ + snd_es18xx_mixer_write(chip, 0x54, 0x8f); + snd_es18xx_mixer_write(chip, 0x56, 0x95); + snd_es18xx_mixer_write(chip, 0x58, 0x94); + snd_es18xx_mixer_write(chip, 0x5a, 0x80); + } + /* Mute input source */ + if (chip->caps & ES18XX_MUTEREC) + mask = 0x10; + if (chip->caps & ES18XX_RECMIX) + snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask); + else { + snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask); + snd_es18xx_write(chip, 0xb4, 0x00); + } +#ifndef AVOID_POPS + /* Enable PCM output */ + snd_es18xx_dsp_command(chip, 0xD1); +#endif + + return 0; +} + +static int __init snd_es18xx_identify(es18xx_t *chip) +{ + int hi,lo; + + /* reset */ + if (snd_es18xx_reset(chip) < 0) { + snd_printk("reset at 0x%lx failed!!!\n", chip->port); + return -ENODEV; + } + + snd_es18xx_dsp_command(chip, 0xe7); + hi = snd_es18xx_dsp_get_byte(chip); + if (hi < 0) { + return hi; + } + lo = snd_es18xx_dsp_get_byte(chip); + if ((lo & 0xf0) != 0x80) { + return -ENODEV; + } + if (hi == 0x48) { + chip->version = 0x488; + return 0; + } + if (hi != 0x68) { + return -ENODEV; + } + if ((lo & 0x0f) < 8) { + chip->version = 0x688; + return 0; + } + + outb(0x40, chip->port + 0x04); + hi = inb(chip->port + 0x05); + lo = inb(chip->port + 0x05); + if (hi != lo) { + chip->version = hi << 8 | lo; + chip->ctrl_port = inb(chip->port + 0x05) << 8; + chip->ctrl_port += inb(chip->port + 0x05); + + if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL) + return -EBUSY; + + return 0; + } + + /* If has Hardware volume */ + if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) { + /* If has Audio2 */ + if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) { + /* If has volume count */ + if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) { + chip->version = 0x1887; + } else { + chip->version = 0x1888; + } + } else { + chip->version = 0x1788; + } + } + else + chip->version = 0x1688; + return 0; +} + +static int __init snd_es18xx_probe(es18xx_t *chip) +{ + if (snd_es18xx_identify(chip) < 0) { + snd_printk("[0x%lx] ESS chip not found\n", chip->port); + return -ENODEV; + } + + switch (chip->version) { + case 0x1868: + chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1869: + chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1878: + chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1879: + chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1887: + chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV; + break; + case 0x1888: + chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV; + break; + default: + snd_printk("[0x%lx] unsupported chip ES%x\n", + chip->port, chip->version); + return -ENODEV; + } + + snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version); + + return snd_es18xx_initialize(chip); +} + +static snd_pcm_ops_t snd_es18xx_playback_ops = { + open: snd_es18xx_playback_open, + close: snd_es18xx_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_es18xx_playback_hw_params, + prepare: snd_es18xx_playback_prepare, + trigger: snd_es18xx_playback_trigger, + pointer: snd_es18xx_playback_pointer, +}; + +static snd_pcm_ops_t snd_es18xx_capture_ops = { + open: snd_es18xx_capture_open, + close: snd_es18xx_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_es18xx_capture_hw_params, + prepare: snd_es18xx_capture_prepare, + trigger: snd_es18xx_capture_trigger, + pointer: snd_es18xx_capture_pointer, +}; + +static void snd_es18xx_pcm_free(snd_pcm_t *pcm) +{ + es18xx_t *codec = snd_magic_cast(es18xx_t, pcm->private_data, return); + codec->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __init snd_es18xx_pcm(es18xx_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + char str[16]; + int err; + + if (rpcm) + *rpcm = NULL; + sprintf(str, "ES%x", chip->version); + if (chip->caps & ES18XX_PCM2) { + err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm); + } else { + err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm); + } + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops); + + /* global setup */ + pcm->private_data = chip; + pcm->private_free = snd_es18xx_pcm_free; + pcm->info_flags = 0; + if (chip->caps & ES18XX_DUPLEX_SAME) + pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; + sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* Power Management support functions */ +#ifdef CONFIG_PM +static void snd_es18xx_suspend(es18xx_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + snd_pcm_suspend_all(chip->pcm); + + /* power down */ + chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM); + chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS); + snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg); + snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS); + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void snd_es18xx_resume(es18xx_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + /* restore PM register, we won't wake till (not 0x07) i/o activity though */ + snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +/* callback for control API */ +static int snd_es18xx_set_power_state(snd_card_t *card, unsigned int power_state) +{ + es18xx_t *chip = (es18xx_t *) card->power_state_private_data; + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_es18xx_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_es18xx_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +static int snd_es18xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, dev->data, return 0); + + switch (rqst) { + case PM_SUSPEND: + snd_es18xx_suspend(chip); + break; + case PM_RESUME: + snd_es18xx_resume(chip); + break; + } + return 0; +} +#endif /* CONFIG_PM */ + +static int snd_es18xx_free(es18xx_t *chip) +{ +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_ctrl_port) { + release_resource(chip->res_ctrl_port); + kfree_nocheck(chip->res_ctrl_port); + } + if (chip->res_mpu_port) { + release_resource(chip->res_mpu_port); + kfree_nocheck(chip->res_mpu_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma1 >= 0) { + disable_dma(chip->dma1); + free_dma(chip->dma1); + } + if (chip->dma2 >= 0) { + disable_dma(chip->dma2); + free_dma(chip->dma2); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_es18xx_dev_free(snd_device_t *device) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, device->device_data, return -ENXIO); + return snd_es18xx_free(chip); +} + +static int __init snd_es18xx_new_device(snd_card_t * card, + unsigned long port, + unsigned long mpu_port, + unsigned long fm_port, + int irq, int dma1, int dma2, + es18xx_t ** rchip) +{ + es18xx_t *chip; + static snd_device_ops_t ops = { + dev_free: snd_es18xx_dev_free, + }; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(es18xx_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->mixer_lock); + spin_lock_init(&chip->ctrl_lock); + chip->card = card; + chip->port = port; + chip->mpu_port = mpu_port; + chip->fm_port = fm_port; + chip->irq = -1; + chip->dma1 = -1; + chip->dma2 = -1; + chip->audio2_vol = 0x00; + chip->active = 0; + + if ((chip->res_port = request_region(port, 16, "ES18xx")) == NULL) { + snd_es18xx_free(chip); + snd_printk("unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1); + return -EBUSY; + } + + if (request_irq(irq, snd_es18xx_interrupt, SA_INTERRUPT, "ES18xx", (void *) chip)) { + snd_es18xx_free(chip); + snd_printk("unable to grap IRQ %d\n", irq); + return -EBUSY; + } + chip->irq = irq; + + if (request_dma(dma1, "ES18xx DMA 1")) { + snd_es18xx_free(chip); + snd_printk("unable to grap DMA1 %d\n", dma1); + return -EBUSY; + } + chip->dma1 = dma1; + + if (request_dma(dma2, "ES18xx DMA 2")) { + snd_es18xx_free(chip); + snd_printk("unable to grap DMA2 %d\n", dma2); + return -EBUSY; + } + chip->dma2 = dma2; + + if (snd_es18xx_probe(chip) < 0) { + snd_es18xx_free(chip); + return -ENODEV; + } + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es18xx_free(chip); + return err; + } + *rchip = chip; + return 0; +} + +static int __init snd_es18xx_mixer(es18xx_t *chip) +{ + snd_card_t *card; + int err, idx; + + card = chip->card; + + strcpy(card->mixername, chip->pcm->name); + + for (idx = 0; idx < sizeof(snd_es18xx_base_controls) / + sizeof(snd_es18xx_base_controls[0]); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_es18xx_base_controls[idx], chip); + if (chip->caps & ES18XX_HWV) { + switch (idx) { + case 0: + chip->master_volume = kctl; + kctl->private_free = snd_es18xx_hwv_free; + break; + case 1: + chip->master_switch = kctl; + kctl->private_free = snd_es18xx_hwv_free; + break; + } + } + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + } + if (chip->caps & ES18XX_PCM2) { + for (idx = 0; idx < sizeof(snd_es18xx_pcm2_controls) / + sizeof(snd_es18xx_pcm2_controls[0]); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip))) < 0) + return err; + } + } else { + for (idx = 0; idx < sizeof(snd_es18xx_pcm1_controls) / + sizeof(snd_es18xx_pcm1_controls[0]); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip))) < 0) + return err; + } + } + + if (chip->caps & ES18XX_MONO) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_mono_in_control, chip))) < 0) + return err; + } + if (chip->caps & ES18XX_RECMIX) { + for (idx = 0; idx < sizeof(snd_es18xx_recmix_controls) / + sizeof(snd_es18xx_recmix_controls[0]); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip))) < 0) + return err; + } + } + switch (chip->version) { + default: + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip))) < 0) + return err; + break; + case 0x1869: + case 0x1879: + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip))) < 0) + return err; + break; + } + if (chip->caps & ES18XX_SPATIALIZER) { + for (idx = 0; idx < sizeof(snd_es18xx_spatializer_controls) / + sizeof(snd_es18xx_spatializer_controls[0]); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip))) < 0) + return err; + } + } + if (chip->caps & ES18XX_HWV) { + for (idx = 0; idx < sizeof(snd_es18xx_hw_volume_controls) / + sizeof(snd_es18xx_hw_volume_controls[0]); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_es18xx_hw_volume_controls[idx], chip); + if (idx == 0) + chip->hw_volume = kctl; + else + chip->hw_switch = kctl; + kctl->private_free = snd_es18xx_hwv_free; + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + + } + } + return 0; +} + + +/* Card level */ + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Christian Fischbach , Abramo Bagnara "); +MODULE_DESCRIPTION("ESS ES18xx AudioDrive"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,ES1868 PnP AudioDrive}," + "{ESS,ES1869 PnP AudioDrive}," + "{ESS,ES1878 PnP AudioDrive}," + "{ESS,ES1879 PnP AudioDrive}," + "{ESS,ES1887 PnP AudioDrive}," + "{ESS,ES1888 PnP AudioDrive}," + "{ESS,ES1887 AudioDrive}," + "{ESS,ES1888 AudioDrive}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */ +#ifndef __ISAPNP__ +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +#else +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +#endif +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ES18xx soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ES18xx soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ES18xx soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list"); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x330,0x30},{0x800,0xffe,0x2}},prefers:{0x330,0x300},base:16,dialog:combo"); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x800,0xffc,0x4}},prefers:{0x388},base:16,dialog:combo"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC ",prefers:{5}"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA 1 # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA8_DESC ",prefers:{1}"); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA 2 # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{0},{1},{3},{5}},dialog:list,prefers:{0}"); + +struct snd_audiodrive { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devc; +#endif +}; + +static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_audiodrive_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_audiodrive_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_ES18XX(_va, _vb, _vc, _device, _audio, _control) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _control) } \ + } + +static struct isapnp_card_id snd_audiodrive_pnpids[] __devinitdata = { + /* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */ + ISAPNP_ES18XX('E','S','S',0x1868,0x1868,0x0000), + /* ESS 1868 (integrated on Maxisound Cards) */ + ISAPNP_ES18XX('E','S','S',0x1868,0x8601,0x8600), + /* ESS 1868 (integrated on Maxisound Cards) */ + ISAPNP_ES18XX('E','S','S',0x1868,0x8611,0x8610), + /* ESS ES1869 Plug and Play AudioDrive */ + ISAPNP_ES18XX('E','S','S',0x0003,0x1869,0x0006), + /* ESS 1869 */ + ISAPNP_ES18XX('E','S','S',0x1869,0x1869,0x0006), + /* ESS 1878 */ + ISAPNP_ES18XX('E','S','S',0x1878,0x1878,0x0004), + /* ESS 1879 */ + ISAPNP_ES18XX('E','S','S',0x1879,0x1879,0x0009), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; + +ISAPNP_CARD_TABLE(snd_audiodrive_pnpids); + +static int __init snd_audiodrive_isapnp(int dev, struct snd_audiodrive *acard) +{ + const struct isapnp_card_id *id = snd_audiodrive_isapnp_id[dev]; + struct isapnp_card *card = snd_audiodrive_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devc = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devc->active) { + acard->dev = acard->devc = NULL; + return -EBUSY; + } + /* Control port initialization */ + if (acard->devc->prepare(acard->devc)<0) + return -EAGAIN; + if (acard->devc->activate(acard->devc)<0) { + snd_printk("isapnp control configure failure (out of resources?)\n"); + return -EAGAIN; + } + snd_printdd("isapnp: port=0x%lx\n", acard->devc->resource[0].start); + /* PnP initialization */ + pdev = acard->dev; + if (pdev->prepare(pdev)<0) { + acard->devc->deactivate(acard->devc); + return -EAGAIN; + } + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4); + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_mpu_port[dev], 2); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk("isapnp configure failure (out of resources?)\n"); + acard->devc->deactivate(acard->devc); + return -EBUSY; + } + /* ok. hack using Vendor-Defined Card-Level registers */ + /* skip csn and logdev initialization - already done in isapnp_configure */ + isapnp_cfg_begin(pdev->bus->number, pdev->devfn); + isapnp_write_byte(0x27, pdev->irq_resource[0].start); /* Hardware Volume IRQ Number */ + if (snd_mpu_port[dev] > SNDRV_AUTO_PORT) + isapnp_write_byte(0x28, pdev->irq); /* MPU-401 IRQ Number */ + isapnp_write_byte(0x72, pdev->irq_resource[0].start); /* second IRQ */ + isapnp_cfg_end(); + snd_port[dev] = pdev->resource[0].start; + snd_fm_port[dev] = pdev->resource[1].start; + snd_mpu_port[dev] = pdev->resource[2].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", snd_port[dev], snd_fm_port[dev], snd_mpu_port[dev]); + snd_printdd("isapnp ES18xx: dma1=%i, dma2=%i, irq=%i\n", snd_dma1[dev], snd_dma2[dev], snd_irq[dev]); + return 0; +} + +static void snd_audiodrive_deactivate(struct snd_audiodrive *acard) +{ + if (acard->devc) { + acard->devc->deactivate(acard->devc); + acard->devc = NULL; + } + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_audiodrive_free(snd_card_t *card) +{ + struct snd_audiodrive *acard = (struct snd_audiodrive *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_audiodrive_deactivate(acard); +#endif + } +} + +static int __init snd_audiodrive_probe(int dev) +{ + static int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1}; + static int possible_dmas[] = {1, 0, 3, 5, -1}; + int irq, dma1, dma2; + snd_card_t *card; + struct snd_audiodrive *acard; + snd_rawmidi_t *rmidi = NULL; + es18xx_t *chip; + opl3_t *opl3; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_audiodrive)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_audiodrive *)card->private_data; + card->private_free = snd_audiodrive_free; +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && (err = snd_audiodrive_isapnp(dev, acard)) < 0) { + snd_card_free(card); + return err; + } +#endif + + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + dma1 = snd_dma1[dev]; + if (dma1 == SNDRV_AUTO_DMA) { + if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + dma2 = snd_dma2[dev]; + if (dma2 == SNDRV_AUTO_DMA) { + if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + if ((err = snd_es18xx_new_device(card, + snd_port[dev], + snd_mpu_port[dev], + snd_fm_port[dev], + irq, dma1, dma2, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es18xx_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es18xx_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) { + snd_printk("opl3 not detected at 0x%lx\n", chip->port); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX, + chip->mpu_port, 0, + irq, 0, + &rmidi)) < 0) { + snd_card_free(card); + return err; + } + chip->rmidi = rmidi; + } + +#ifdef CONFIG_PM + /* Power Management */ + chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_es18xx_pm_callback); + if (chip->pm_dev) { + chip->pm_dev->data = chip; + /* set control api callback */ + card->set_power_state = snd_es18xx_set_power_state; + card->power_state_private_data = chip; + } +#endif + sprintf(card->driver, "ES%x", chip->version); + sprintf(card->shortname, "ESS AudioDrive ES%x", chip->version); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma1 %d, dma2 %d", + card->shortname, + chip->port, + irq, dma1, dma2); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_audiodrive_cards[dev] = card; + return 0; +} + +static int __init snd_audiodrive_probe_legacy_port(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + snd_port[dev] = port; + res = snd_audiodrive_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + + +#ifdef __ISAPNP__ +static int __init snd_audiodrive_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || !snd_isapnp[dev]) + continue; + snd_audiodrive_isapnp_cards[dev] = card; + snd_audiodrive_isapnp_id[dev] = id; + res = snd_audiodrive_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_es18xx_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1}; + int dev, cards = 0; + + /* legacy non-auto cards at first */ + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (snd_audiodrive_probe(dev) >= 0) + cards++; + } + /* legacy auto configured cards */ + cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_probe_legacy_port); +#ifdef __ISAPNP__ + /* ISA PnP cards at last */ + cards += isapnp_probe_cards(snd_audiodrive_pnpids, snd_audiodrive_isapnp_detect); +#endif + if(!cards) { +#ifdef MODULE + snd_printk("ESS AudioDrive ES18xx soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_es18xx_exit(void) +{ + int idx; + + for(idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_audiodrive_cards[idx]); +} + +module_init(alsa_card_es18xx_init) +module_exit(alsa_card_es18xx_exit) + +#ifndef MODULE + +/* format is: snd-es18xx=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port,snd_mpu_port,snd_fm_port,snd_irq, + snd_dma1,snd_dma2 */ + +static int __init alsa_card_es18xx_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +__setup("snd-es18xx=", alsa_card_es18xx_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,62 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _gus.o + +list-multi := snd-gus-lib.o snd-gusclassic.o snd-gusextreme.o \ + snd-gusmax.o snd-interwave.o snd-interwave-stb.o + +export-objs := gus_main.o gus_volume.o + +snd-gus-lib-objs := gus_main.o \ + gus_io.o gus_irq.o gus_timer.o \ + gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \ + gus_pcm.o gus_mixer.o \ + gus_uart.o \ + gus_reset.o +snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o + +snd-gusclassic-objs := gusclassic.o +snd-gusextreme-objs := gusextreme.o +snd-gusmax-objs := gusmax.o +snd-interwave-objs := interwave.o +snd-interwave-stb-objs := interwave-stb.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o +obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o +obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_GUSCLASSIC) += snd-gus-synth.o + obj-$(CONFIG_SND_GUSMAX) += snd-gus-synth.o + obj-$(CONFIG_SND_GUSEXTREME) += snd-gus-synth.o + obj-$(CONFIG_SND_INTERWAVE) += snd-gus-synth.o + obj-$(CONFIG_SND_INTERWAVE_STB) += snd-gus-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-gus-lib.o: $(snd-gus-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gus-lib-objs) + +snd-gus-synth.o: $(snd-gus-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gus-synth-objs) + +snd-gusclassic.o: $(snd-gusclassic-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gusclassic-objs) + +snd-gusextreme.o: $(snd-gusextreme-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gusextreme-objs) + +snd-gusmax.o: $(snd-gusmax-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gusmax-objs) + +snd-interwave.o: $(snd-interwave-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-interwave-objs) + +snd-interwave-stb.o: $(snd-interwave-stb-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-interwave-stb-objs) diff -Nru a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_dma.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,245 @@ +/* + * Routines for GF1 DMA control + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +void snd_gf1_dma_ack(snd_gus_card_t * gus) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00); + snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_dma_program(snd_gus_card_t * gus, + unsigned int addr, + unsigned long buf_addr, + unsigned int count, + unsigned int cmd) +{ + unsigned long flags; + unsigned int address; + unsigned char dma_cmd; + unsigned int address_high; + + // snd_printk("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n", addr, (long) buf, count); + + if (gus->gf1.dma1 > 3) { + if (gus->gf1.enh_mode) { + address = addr >> 1; + } else { + if (addr & 0x1f) { + snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr); + return; + } + address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1); + } + } else { + address = addr; + } + + dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd; +#if 0 + dma_cmd |= 0x08; +#endif + if (dma_cmd & SNDRV_GF1_DMA_16BIT) { + count++; + count &= ~1; /* align */ + } + if (gus->gf1.dma1 > 3) { + dma_cmd |= SNDRV_GF1_DMA_WIDTH16; + count++; + count &= ~1; /* align */ + } + snd_gf1_dma_ack(gus); + snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE); +#if 0 + snd_printk("address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n", address << 1, count, dma_cmd); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + if (gus->gf1.enh_mode) { + address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f); + snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4)); + snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high); + } else + snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4)); + snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static snd_gf1_dma_block_t *snd_gf1_dma_next_block(snd_gus_card_t * gus) +{ + snd_gf1_dma_block_t *block; + + /* PCM block have bigger priority than synthesizer one */ + if (gus->gf1.dma_data_pcm) { + block = gus->gf1.dma_data_pcm; + if (gus->gf1.dma_data_pcm_last == block) { + gus->gf1.dma_data_pcm = + gus->gf1.dma_data_pcm_last = NULL; + } else { + gus->gf1.dma_data_pcm = block->next; + } + } else if (gus->gf1.dma_data_synth) { + block = gus->gf1.dma_data_synth; + if (gus->gf1.dma_data_synth_last == block) { + gus->gf1.dma_data_synth = + gus->gf1.dma_data_synth_last = NULL; + } else { + gus->gf1.dma_data_synth = block->next; + } + } else { + block = NULL; + } + if (block) { + gus->gf1.dma_ack = block->ack; + gus->gf1.dma_private_data = block->private_data; + } + return block; +} + + +static void snd_gf1_dma_interrupt(snd_gus_card_t * gus) +{ + snd_gf1_dma_block_t *block; + + snd_gf1_dma_ack(gus); + if (gus->gf1.dma_ack) + gus->gf1.dma_ack(gus, gus->gf1.dma_private_data); + spin_lock(&gus->dma_lock); + if (gus->gf1.dma_data_pcm == NULL && + gus->gf1.dma_data_synth == NULL) { + gus->gf1.dma_ack = NULL; + gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER; + spin_unlock(&gus->dma_lock); + return; + } + block = snd_gf1_dma_next_block(gus); + spin_unlock(&gus->dma_lock); + snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); + kfree(block); +#if 0 + printk("program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", addr, (long) buffer, count, cmd); +#endif +} + +int snd_gf1_dma_init(snd_gus_card_t * gus) +{ + down(&gus->dma_mutex); + gus->gf1.dma_shared++; + if (gus->gf1.dma_shared > 1) { + up(&gus->dma_mutex); + return 0; + } + gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt; + gus->gf1.dma_data_pcm = + gus->gf1.dma_data_pcm_last = + gus->gf1.dma_data_synth = + gus->gf1.dma_data_synth_last = NULL; + up(&gus->dma_mutex); + return 0; +} + +int snd_gf1_dma_done(snd_gus_card_t * gus) +{ + snd_gf1_dma_block_t *block; + + down(&gus->dma_mutex); + gus->gf1.dma_shared--; + if (!gus->gf1.dma_shared) { + snd_dma_disable(gus->gf1.dma1); + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE); + snd_gf1_dma_ack(gus); + while ((block = gus->gf1.dma_data_pcm)) { + gus->gf1.dma_data_pcm = block->next; + kfree(block); + } + while ((block = gus->gf1.dma_data_synth)) { + gus->gf1.dma_data_synth = block->next; + kfree(block); + } + gus->gf1.dma_data_pcm_last = + gus->gf1.dma_data_synth_last = NULL; + } + up(&gus->dma_mutex); + return 0; +} + +int snd_gf1_dma_transfer_block(snd_gus_card_t * gus, + snd_gf1_dma_block_t * __block, + int atomic, + int synth) +{ + unsigned long flags; + snd_gf1_dma_block_t *block; + + block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL); + if (block == NULL) { + snd_printk("gf1: DMA transfer failure; not enough memory\n"); + return -ENOMEM; + } + *block = *__block; + block->next = NULL; +#if 0 + printk("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", block->addr, (long) block->buffer, block->count, block->cmd); +#endif +#if 0 + printk("gus->gf1.dma_data_pcm_last = 0x%lx\n", (long)gus->gf1.dma_data_pcm_last); + printk("gus->gf1.dma_data_pcm = 0x%lx\n", (long)gus->gf1.dma_data_pcm); +#endif + spin_lock_irqsave(&gus->dma_lock, flags); + if (synth) { + if (gus->gf1.dma_data_synth_last) { + gus->gf1.dma_data_synth_last->next = block; + gus->gf1.dma_data_synth_last = block; + } else { + gus->gf1.dma_data_synth = + gus->gf1.dma_data_synth_last = block; + } + } else { + if (gus->gf1.dma_data_pcm_last) { + gus->gf1.dma_data_pcm_last->next = block; + gus->gf1.dma_data_pcm_last = block; + } else { + gus->gf1.dma_data_pcm = + gus->gf1.dma_data_pcm_last = block; + } + } + if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) { + gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER; + block = snd_gf1_dma_next_block(gus); + spin_unlock_irqrestore(&gus->dma_lock, flags); + if (block == NULL) + return 0; + snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); + kfree(block); + return 0; + } + spin_unlock_irqrestore(&gus->dma_lock, flags); + return 0; +} diff -Nru a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_dram.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,104 @@ +/* + * Copyright (c) by Jaroslav Kysela + * DRAM access routines + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + + +static int snd_gus_dram_poke(snd_gus_card_t *gus, char *_buffer, + unsigned int address, unsigned int size) +{ + unsigned long flags; + unsigned int size1, size2; + char buffer[512], *pbuffer; + + while (size > 0) { + if (copy_from_user(buffer, _buffer, 512)) + return -EFAULT; + size1 = size > 512 ? 512 : size; + if (gus->interwave) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + snd_gf1_dram_addr(gus, address); + outsb(GUSP(gus, DRAM), buffer, size1); + spin_unlock_irqrestore(&gus->reg_lock, flags); + address += size1; + } else { + pbuffer = buffer; + size2 = size1; + while (size2--) + snd_gf1_poke(gus, address++, *pbuffer++); + } + size -= size1; + _buffer += size1; + } + return 0; +} + + +int snd_gus_dram_write(snd_gus_card_t *gus, char *buffer, + unsigned int address, unsigned int size) +{ + return snd_gus_dram_poke(gus, buffer, address, size); +} + +static int snd_gus_dram_peek(snd_gus_card_t *gus, char *_buffer, + unsigned int address, unsigned int size, + int rom) +{ + unsigned long flags; + unsigned int size1, size2; + char buffer[512], *pbuffer; + + while (size > 0) { + size1 = size > 512 ? 512 : size; + if (gus->interwave) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01); + snd_gf1_dram_addr(gus, address); + insb(GUSP(gus, DRAM), buffer, size1); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + spin_unlock_irqrestore(&gus->reg_lock, flags); + address += size1; + } else { + pbuffer = buffer; + size2 = size1; + while (size2--) + *pbuffer++ = snd_gf1_peek(gus, address++); + } + if (copy_to_user(_buffer, buffer, size1)) + return -EFAULT; + size -= size1; + _buffer += size1; + } + return 0; +} + +int snd_gus_dram_read(snd_gus_card_t *gus, char *buffer, + unsigned int address, unsigned int size, + int rom) +{ + return snd_gus_dram_peek(gus, buffer, address, size, rom); +} diff -Nru a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_instr.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,174 @@ +/* + * Routines for Gravis UltraSound soundcards - Synthesizer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +/* + * + */ + +int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gf1_mem_block_t *block; + int err; + + if (wave->format & IWFFFF_WAVE_ROM) + return 0; /* it's probably ok - verify the address? */ + if (wave->format & IWFFFF_WAVE_STEREO) + return -EINVAL; /* not supported */ + block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF, + NULL, wave->size, + wave->format & IWFFFF_WAVE_16BIT, 1, + wave->share_id); + if (block == NULL) + return -ENOMEM; + err = snd_gus_dram_write(gus, data, + block->ptr, wave->size); + if (err < 0) { + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); + snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); + return err; + } + wave->address.memory = block->ptr; + return 0; +} + +int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, + wave->format & IWFFFF_WAVE_ROM ? 1 : 0); +} + +int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave, + int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + if (wave->format & IWFFFF_WAVE_ROM) + return 0; /* it's probably ok - verify the address? */ + return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); +} + +/* + * + */ + +int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gf1_mem_block_t *block; + int err; + + if (wave->format & GF1_WAVE_STEREO) + return -EINVAL; /* not supported */ + block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_WAVE_GF1, + NULL, wave->size, + wave->format & GF1_WAVE_16BIT, 1, + wave->share_id); + if (block == NULL) + return -ENOMEM; + err = snd_gus_dram_write(gus, data, + block->ptr, wave->size); + if (err < 0) { + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); + snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); + return err; + } + wave->address.memory = block->ptr; + return 0; +} + +int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0); +} + +int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave, + int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); +} + +/* + * + */ + +int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gf1_mem_block_t *block; + int err; + + if (instr->format & SIMPLE_WAVE_STEREO) + return -EINVAL; /* not supported */ + block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE, + NULL, instr->size, + instr->format & SIMPLE_WAVE_16BIT, 1, + instr->share_id); + if (block == NULL) + return -ENOMEM; + err = snd_gus_dram_write(gus, data, block->ptr, instr->size); + if (err < 0) { + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); + snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); + return err; + } + instr->address.memory = block->ptr; + return 0; +} + +int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0); +} + +int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr, + int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory); +} diff -Nru a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_io.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,532 @@ +/* + * Copyright (c) by Jaroslav Kysela + * I/O routines for GF1/InterWave synthesizer chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +void snd_gf1_delay(snd_gus_card_t * gus) +{ + int i; + + for (i = 0; i < 6; i++) { + mb(); + inb(GUSP(gus, DRAM)); + } +} + +/* + * ======================================================================= + */ + +/* + * ok.. stop of control registers (wave & ramp) need some special things.. + * big UltraClick (tm) elimination... + */ + +static inline void __snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned char value; + + outb(reg | 0x80, gus->gf1.reg_regsel); + mb(); + value = inb(gus->gf1.reg_data8); + mb(); + outb(reg, gus->gf1.reg_regsel); + mb(); + outb((value | 0x03) & ~(0x80 | 0x20), gus->gf1.reg_data8); + mb(); +} + +static inline void __snd_gf1_write8(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + outb(data, gus->gf1.reg_data8); + mb(); +} + +static inline unsigned char __snd_gf1_look8(snd_gus_card_t * gus, + unsigned char reg) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + return inb(gus->gf1.reg_data8); +} + +static inline void __snd_gf1_write16(snd_gus_card_t * gus, + unsigned char reg, unsigned int data) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) data, gus->gf1.reg_data16); + mb(); +} + +static inline unsigned short __snd_gf1_look16(snd_gus_card_t * gus, + unsigned char reg) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + return inw(gus->gf1.reg_data16); +} + +static inline void __snd_gf1_adlib_write(snd_gus_card_t * gus, + unsigned char reg, unsigned char data) +{ + outb(reg, gus->gf1.reg_timerctrl); + inb(gus->gf1.reg_timerctrl); + inb(gus->gf1.reg_timerctrl); + outb(data, gus->gf1.reg_timerdata); + inb(gus->gf1.reg_timerctrl); + inb(gus->gf1.reg_timerctrl); +} + +static inline void __snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, + unsigned int addr, int w_16bit) +{ + if (gus->gf1.enh_mode) { + if (w_16bit) + addr = ((addr >> 1) & ~0x0000000f) | (addr & 0x0000000f); + __snd_gf1_write8(gus, SNDRV_GF1_VB_UPPER_ADDRESS, (unsigned char) ((addr >> 26) & 0x03)); + } else if (w_16bit) + addr = (addr & 0x00c0000f) | ((addr & 0x003ffff0) >> 1); + __snd_gf1_write16(gus, reg, (unsigned short) (addr >> 11)); + __snd_gf1_write16(gus, reg + 1, (unsigned short) (addr << 5)); +} + +static inline unsigned int __snd_gf1_read_addr(snd_gus_card_t * gus, + unsigned char reg, short w_16bit) +{ + unsigned int res; + + res = ((unsigned int) __snd_gf1_look16(gus, reg | 0x80) << 11) & 0xfff800; + res |= ((unsigned int) __snd_gf1_look16(gus, (reg + 1) | 0x80) >> 5) & 0x0007ff; + if (gus->gf1.enh_mode) { + res |= (unsigned int) __snd_gf1_look8(gus, SNDRV_GF1_VB_UPPER_ADDRESS | 0x80) << 26; + if (w_16bit) + res = ((res << 1) & 0xffffffe0) | (res & 0x0000000f); + } else if (w_16bit) + res = ((res & 0x001ffff0) << 1) | (res & 0x00c0000f); + return res; +} + + +/* + * ======================================================================= + */ + +void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) +{ + __snd_gf1_ctrl_stop(gus, reg); +} + +void snd_gf1_write8(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + __snd_gf1_write8(gus, reg, data); +} + +unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg) +{ + return __snd_gf1_look8(gus, reg); +} + +void snd_gf1_write16(snd_gus_card_t * gus, + unsigned char reg, + unsigned int data) +{ + __snd_gf1_write16(gus, reg, data); +} + +unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg) +{ + return __snd_gf1_look16(gus, reg); +} + +void snd_gf1_adlib_write(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + __snd_gf1_adlib_write(gus, reg, data); +} + +void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, + unsigned int addr, short w_16bit) +{ + __snd_gf1_write_addr(gus, reg, addr, w_16bit); +} + +unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, + unsigned char reg, + short w_16bit) +{ + return __snd_gf1_read_addr(gus, reg, w_16bit); +} + +/* + + */ + +void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_ctrl_stop(gus, reg); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_i_write8(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_write8(gus, reg, data); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&gus->reg_lock, flags); + res = __snd_gf1_look8(gus, reg); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_i_write16(snd_gus_card_t * gus, + unsigned char reg, + unsigned int data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_write16(gus, reg, data); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned long flags; + unsigned short res; + + spin_lock_irqsave(&gus->reg_lock, flags); + res = __snd_gf1_look16(gus, reg); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_i_adlib_write(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_adlib_write(gus, reg, data); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, + unsigned int addr, short w_16bit) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_write_addr(gus, reg, addr, w_16bit); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, + unsigned char reg, short w_16bit) +{ + unsigned int res; + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + res = __snd_gf1_read_addr(gus, reg, w_16bit); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +/* + + */ + +void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr) +{ + outb(0x43, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(0x44, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); +} + +void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(data, gus->gf1.reg_dram); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr) +{ + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + res = inb(gus->gf1.reg_dram); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data) +{ + unsigned long flags; + +#ifdef CONFIG_SND_DEBUG + if (!gus->interwave) + snd_printk("snd_gf1_pokew - GF1!!!\n"); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); + mb(); + outw(data, gus->gf1.reg_data16); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr) +{ + unsigned long flags; + unsigned short res; + +#ifdef CONFIG_SND_DEBUG + if (!gus->interwave) + snd_printk("snd_gf1_peekw - GF1!!!\n"); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); + mb(); + res = inw(gus->gf1.reg_data16); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, + unsigned short value, unsigned int count) +{ + unsigned long port; + unsigned long flags; + +#ifdef CONFIG_SND_DEBUG + if (!gus->interwave) + snd_printk("snd_gf1_dram_setmem - GF1!!!\n"); +#endif + addr &= ~1; + count >>= 1; + port = GUSP(gus, GF1DATALOW); + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); + while (count--) + outw(value, port); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +/* + + */ + +void snd_gf1_select_active_voices(snd_gus_card_t * gus) +{ + unsigned short voices; + + static unsigned short voices_tbl[32 - 14 + 1] = + { + 44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843, + 25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293 + }; + + voices = gus->gf1.active_voices; + if (voices > 32) + voices = 32; + if (voices < 14) + voices = 14; + if (gus->gf1.enh_mode) + voices = 32; + gus->gf1.active_voices = voices; + gus->gf1.playback_freq = + gus->gf1.enh_mode ? 44100 : voices_tbl[voices - 14]; + if (!gus->gf1.enh_mode) { + snd_gf1_i_write8(gus, SNDRV_GF1_GB_ACTIVE_VOICES, 0xc0 | (voices - 1)); + udelay(100); + } +} + +#ifdef CONFIG_SND_DEBUG + +void snd_gf1_print_voice_registers(snd_gus_card_t * gus) +{ + unsigned char mode; + int voice, ctrl; + + voice = gus->gf1.active_voice; + printk(" -%i- GF1 voice ctrl, ramp ctrl = 0x%x, 0x%x\n", voice, ctrl = snd_gf1_i_read8(gus, 0), snd_gf1_i_read8(gus, 0x0d)); + printk(" -%i- GF1 frequency = 0x%x\n", voice, snd_gf1_i_read16(gus, 1)); + printk(" -%i- GF1 loop start, end = 0x%x (0x%x), 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 2, ctrl & 4), snd_gf1_i_read_addr(gus, 2, (ctrl & 4) ^ 4), snd_gf1_i_read_addr(gus, 4, ctrl & 4), snd_gf1_i_read_addr(gus, 4, (ctrl & 4) ^ 4)); + printk(" -%i- GF1 ramp start, end, rate = 0x%x, 0x%x, 0x%x\n", voice, snd_gf1_i_read8(gus, 7), snd_gf1_i_read8(gus, 8), snd_gf1_i_read8(gus, 6)); + printk(" -%i- GF1 volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 9)); + printk(" -%i- GF1 position = 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 0x0a, ctrl & 4), snd_gf1_i_read_addr(gus, 0x0a, (ctrl & 4) ^ 4)); + if (gus->interwave && snd_gf1_i_read8(gus, 0x19) & 0x01) { /* enhanced mode */ + mode = snd_gf1_i_read8(gus, 0x15); + printk(" -%i- GFA1 mode = 0x%x\n", voice, mode); + if (mode & 0x01) { /* Effect processor */ + printk(" -%i- GFA1 effect address = 0x%x\n", voice, snd_gf1_i_read_addr(gus, 0x11, ctrl & 4)); + printk(" -%i- GFA1 effect volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x16)); + printk(" -%i- GFA1 effect volume final = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x1d)); + printk(" -%i- GFA1 effect acumulator = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x14)); + } + if (mode & 0x20) { + printk(" -%i- GFA1 left offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x13), snd_gf1_i_read16(gus, 0x13) >> 4); + printk(" -%i- GFA1 left offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1c), snd_gf1_i_read16(gus, 0x1c) >> 4); + printk(" -%i- GFA1 right offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x0c), snd_gf1_i_read16(gus, 0x0c) >> 4); + printk(" -%i- GFA1 right offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1b), snd_gf1_i_read16(gus, 0x1b) >> 4); + } else + printk(" -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c)); + } else + printk(" -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c)); +} + +void snd_gf1_print_global_registers(snd_gus_card_t * gus) +{ + unsigned char global_mode = 0x00; + + printk(" -G- GF1 active voices = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ACTIVE_VOICES)); + if (gus->interwave) { + global_mode = snd_gf1_i_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE); + printk(" -G- GF1 global mode = 0x%x\n", global_mode); + } + if (global_mode & 0x02) /* LFO enabled? */ + printk(" -G- GF1 LFO base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_LFO_BASE)); + printk(" -G- GF1 voices IRQ read = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VOICES_IRQ_READ)); + printk(" -G- GF1 DRAM DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL)); + printk(" -G- GF1 DRAM DMA high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW)); + printk(" -G- GF1 DRAM IO high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_IO_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_IO_LOW)); + if (!gus->interwave) + printk(" -G- GF1 record DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL)); + printk(" -G- GF1 DRAM IO 16 = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_DRAM_IO16)); + if (gus->gf1.enh_mode) { + printk(" -G- GFA1 memory config = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG)); + printk(" -G- GFA1 memory control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MEMORY_CONTROL)); + printk(" -G- GFA1 FIFO record base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR)); + printk(" -G- GFA1 FIFO playback base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR)); + printk(" -G- GFA1 interleave control = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_INTERLEAVE)); + } +} + +void snd_gf1_print_setup_registers(snd_gus_card_t * gus) +{ + printk(" -S- mix control = 0x%x\n", inb(GUSP(gus, MIXCNTRLREG))); + printk(" -S- IRQ status = 0x%x\n", inb(GUSP(gus, IRQSTAT))); + printk(" -S- timer control = 0x%x\n", inb(GUSP(gus, TIMERCNTRL))); + printk(" -S- timer data = 0x%x\n", inb(GUSP(gus, TIMERDATA))); + printk(" -S- status read = 0x%x\n", inb(GUSP(gus, REGCNTRLS))); + printk(" -S- Sound Blaster control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL)); + printk(" -S- AdLib timer 1/2 = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1), snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2)); + printk(" -S- reset = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)); + if (gus->interwave) { + printk(" -S- compatibility = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_COMPATIBILITY)); + printk(" -S- decode control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DECODE_CONTROL)); + printk(" -S- version number = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER)); + printk(" -S- MPU-401 emul. control A/B = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A), snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B)); + printk(" -S- emulation IRQ = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_EMULATION_IRQ)); + } +} + +void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit) +{ + if (!w_16bit) { + while (count-- > 0) + printk(count > 0 ? "%02x:" : "%02x", snd_gf1_peek(gus, addr++)); + } else { + while (count-- > 0) { + printk(count > 0 ? "%04x:" : "%04x", snd_gf1_peek(gus, addr) | (snd_gf1_peek(gus, addr + 1) << 8)); + addr += 2; + } + } +} + +#endif diff -Nru a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_irq.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,158 @@ +/* + * Routine for IRQ handling from GF1/InterWave chip + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +#ifdef CONFIG_SND_DEBUG +#define STAT_ADD(x) ((x)++) +#else +#define STAT_ADD(x) while (0) { ; } +#endif + +void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + snd_gus_card_t * gus = snd_magic_cast(snd_gus_card_t, dev_id, return); + unsigned char status; + int loop = 100; + + __again: + status = inb(gus->gf1.reg_irqstat); + if (status == 0) + return; + // snd_printk("IRQ: status = 0x%x\n", status); + if (status & 0x02) { + STAT_ADD(gus->gf1.interrupt_stat_midi_in); + gus->gf1.interrupt_handler_midi_in(gus); + } + if (status & 0x01) { + STAT_ADD(gus->gf1.interrupt_stat_midi_out); + gus->gf1.interrupt_handler_midi_out(gus); + } + if (status & (0x20 | 0x40)) { + unsigned int already, _current_; + unsigned char voice_status, voice; + snd_gus_voice_t *pvoice; + + already = 0; + while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) { + voice = voice_status & 0x1f; + _current_ = 1 << voice; + if (already & _current_) + continue; /* multi request */ + already |= _current_; /* mark request */ +#if 0 + printk("voice = %i, voice_status = 0x%x, voice_verify = %i\n", voice, voice_status, inb(GUSP(gus, GF1PAGE))); +#endif + pvoice = &gus->gf1.voices[voice]; + if (pvoice->use) { + if (!(voice_status & 0x80)) { /* voice position IRQ */ + STAT_ADD(pvoice->interrupt_stat_wave); + pvoice->handler_wave(gus, pvoice); + } + if (!(voice_status & 0x40)) { /* volume ramp IRQ */ + STAT_ADD(pvoice->interrupt_stat_volume); + pvoice->handler_volume(gus, pvoice); + } + } else { + STAT_ADD(gus->gf1.interrupt_stat_voice_lost); + snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + } + } + } + if (status & 0x04) { + STAT_ADD(gus->gf1.interrupt_stat_timer1); + gus->gf1.interrupt_handler_timer1(gus); + } + if (status & 0x08) { + STAT_ADD(gus->gf1.interrupt_stat_timer2); + gus->gf1.interrupt_handler_timer2(gus); + } + if (status & 0x80) { + if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) { + STAT_ADD(gus->gf1.interrupt_stat_dma_write); + gus->gf1.interrupt_handler_dma_write(gus); + } + if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) { + STAT_ADD(gus->gf1.interrupt_stat_dma_read); + gus->gf1.interrupt_handler_dma_read(gus); + } + } + if (--loop > 0) + goto __again; +} + +#ifdef CONFIG_SND_DEBUG +static void snd_gus_irq_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_gus_card_t *gus; + snd_gus_voice_t *pvoice; + int idx; + + gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return); + snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out); + snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in); + snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1); + snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2); + snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write); + snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read); + snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost); + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n", + idx, + pvoice->interrupt_stat_wave, + pvoice->interrupt_stat_volume); + } +} + +void snd_gus_irq_profile_init(snd_gus_card_t *gus) +{ + snd_info_entry_t *entry; + + gus->irq_entry = NULL; + entry = snd_info_create_card_entry(gus->card, "gusirq", gus->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 512; + entry->c.text.read = snd_gus_irq_info_read; + entry->private_data = gus; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + gus->irq_entry = entry; +} + +void snd_gus_irq_profile_done(snd_gus_card_t *gus) +{ + if (gus->irq_entry) { + snd_info_unregister(gus->irq_entry); + gus->irq_entry = NULL; + } +} +#endif diff -Nru a/sound/isa/gus/gus_lfo.c b/sound/isa/gus/gus_lfo.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_lfo.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,430 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of LFO generators (tremolo & vibrato) for + * GF1/InterWave chips... + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include + +/* + * called by engine routines + */ + +static signed char snd_gf1_lfo_compute_value(snd_gus_card_t * gus, + unsigned char *ptr) +{ + unsigned int twaveinc, depth_delta; + signed int result; + unsigned short control, twave, depth, depth_final; + unsigned char *ptr1; + + control = *(unsigned short *) (ptr + 0x00); + ptr1 = ptr + ((control & 0x4000) >> 12); + /* 1. add TWAVEINC to TWAVE and write the result back */ + /* LFO update rate is 689Hz, effect timer is in ms */ + if (gus->gf1.timer_slave) + twaveinc = (689 * gus->gf1.timer_master_gus->gf1.effect_timer) / 1000; + else + twaveinc = (689 * gus->gf1.effect_timer) / 1000; + if (!twaveinc) + twaveinc++; +#if 0 + printk("twaveinc = 0x%x, effect_timer = %i\n", twaveinc, gus->gf1.effect_timer); +#endif + + depth = *(unsigned short *) (ptr1 + 0x0a); + depth_final = *(unsigned char *) (ptr + 0x02) << 5; + if (depth != depth_final) { + depth_delta = ((twaveinc * *(ptr + 0x03)) + *(unsigned short *) (ptr + 0x04)); + *(unsigned short *) (ptr + 0x04) = depth_delta % 8000; + depth_delta /= 8000; + if (depth < depth_final) { + if (depth + depth_delta > depth_final) + depth = depth_final; + else + depth += depth_delta; + } + if (depth > depth_final) { + if (depth - depth_delta < depth_final) + depth = depth_final; + else + depth -= depth_delta; + } + *(unsigned short *) (ptr1 + 0x0a) = depth; + } + twaveinc *= (unsigned int) control & 0x7ff; + twaveinc += *(unsigned short *) (ptr + 0x06); + *(unsigned short *) (ptr + 0x06) = twaveinc % 1000; + + twave = *(unsigned short *) (ptr1 + 0x08); + twave += (unsigned short) (twaveinc / (unsigned int) 1000); + *(unsigned short *) (ptr1 + 0x08) = twave; + + if (!(control & 0x2000)) { + /* 2. if shift is low */ + if (twave & 0x4000) { /* bit 14 high -> invert TWAVE 13-0 */ + twave ^= 0x3fff; + twave &= ~0x4000; + } + /* TWAVE bit 15 is exclusive or'd with the invert bit (12) */ + twave ^= (control & 0x1000) << 3; + } else { + /* 2. if shift is high */ + if (twave & 0x8000) /* bit 15 high -> invert TWAVE 14-0 */ + twave ^= 0x7fff; + /* the invert bit (12) is used as sign bit */ + if (control & 0x1000) + twave |= 0x8000; + else + twave &= ~0x8000; + } + /* 3. multiply the 14-bit LFO waveform magnitude by 13-bit DEPTH */ +#if 0 + printk("c=0x%x,tw=0x%x,to=0x%x,d=0x%x,df=0x%x,di=0x%x,r=0x%x,r1=%i\n", + control, twave, + *(unsigned short *) (ptr1 + 0x08), + depth, depth_final, *(ptr + 0x03), + (twave & 0x7fff) * depth, ((twave & 0x7fff) * depth) >> 21); +#endif + result = (twave & 0x7fff) * depth; + if (result) { + /* shift */ + result >>= 21; + result &= 0x3f; + } + /* add sign */ + if (twave & 0x8000) + result = -result; +#if 0 + printk("lfo final value = %i\n", result); +#endif + return result; +} + +static void snd_gf1_lfo_register_setup(snd_gus_card_t * gus, + snd_gf1_voice_t * voice, + int lfo_type) +{ + unsigned long flags; + + if (gus->gf1.enh_mode) { + CLI(&flags); + gf1_select_voice(gus, voice->number); + if (lfo_type & 1) { + snd_gf1_write8(gus, GF1_VB_FREQUENCY_LFO, voice->lfo_fc); + voice->lfo_fc = 0; + } + if (lfo_type & 2) { + snd_gf1_write8(gus, GF1_VB_VOLUME_LFO, voice->lfo_volume); + voice->lfo_volume = 0; + } + STI(&flags); + } else { + /* + * ok.. with old GF1 chip can be only vibrato emulated... + * volume register can be in volume ramp state, so tremolo isn't simple.. + */ + if (!(lfo_type & 1)) + return; +#if 0 + if (voice->lfo_fc) + printk("setup - %i = %i\n", voice->number, voice->lfo_fc); +#endif + CLI(&flags); + gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, GF1_VW_FREQUENCY, voice->fc_register + voice->lfo_fc); + STI(&flags); + } +} + +void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice) +{ + unsigned char *ptr; + +#if 0 + if (voice->number != 0) + return; +#endif + ptr = gus->gf1.lfos + ((voice->number) << 5); + /* 1. vibrato */ + if (*(unsigned short *) (ptr + 0x00) & 0x8000) + voice->lfo_fc = snd_gf1_lfo_compute_value(gus, ptr); + /* 2. tremolo */ + ptr += 16; + if (*(unsigned short *) (ptr + 0x00) & 0x8000) + voice->lfo_volume = snd_gf1_lfo_compute_value(gus, ptr); + /* 3. register setup */ + snd_gf1_lfo_register_setup(gus, voice, 3); +} + +/* + + */ + +void snd_gf1_lfo_init(snd_gus_card_t * gus) +{ + if (gus->gf1.hw_lfo) { + snd_gf1_i_write16(gus, GF1_GW_LFO_BASE, 0x0000); + snd_gf1_dram_setmem(gus, 0, 0x0000, 1024); + /* now enable LFO */ + snd_gf1_i_write8(gus, GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, GF1_GB_GLOBAL_MODE) | 0x02); + } + if (gus->gf1.sw_lfo) { +#if 1 + gus->gf1.lfos = snd_calloc(1024); + if (!gus->gf1.lfos) +#endif + gus->gf1.sw_lfo = 0; + } +} + +void snd_gf1_lfo_done(snd_gus_card_t * gus) +{ + if (gus->gf1.sw_lfo) { + if (gus->gf1.lfos) { + snd_gf1_free(gus->gf1.lfos, 1024); + gus->gf1.lfos = NULL; + } + } +} + +void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type, + struct ULTRA_STRU_IW_LFO_PROGRAM *program) +{ + unsigned int lfo_addr, wave_select; + + wave_select = (program->freq_and_control & 0x4000) >> 12; + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) { +#if 0 + printk("LMCI = 0x%x\n", snd_gf1_i_look8(gus, 0x53)); + printk("lfo_program: lfo_addr=0x%x,wave_sel=0x%x,fac=0x%x,df=0x%x,di=0x%x,twave=0x%x,depth=0x%x\n", + lfo_addr, wave_select, + program->freq_and_control, + program->depth_final, + program->depth_inc, + program->twave, + program->depth); +#endif + snd_gf1_poke(gus, lfo_addr + 0x02, program->depth_final); + snd_gf1_poke(gus, lfo_addr + 0x03, program->depth_inc); + snd_gf1_pokew(gus, lfo_addr + 0x08 + wave_select, program->twave); + snd_gf1_pokew(gus, lfo_addr + 0x0a + wave_select, program->depth); + snd_gf1_pokew(gus, lfo_addr + 0x00, program->freq_and_control); +#if 0 + { + int i = 0; + for (i = 0; i < 16; i++) + printk("%02x:", snd_gf1_peek(gus, lfo_addr + i)); + printk("\n"); + } +#endif + } + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + *(ptr + 0x02) = program->depth_final; + *(ptr + 0x03) = program->depth_inc; + *(unsigned short *) (ptr + 0x08 + wave_select) = program->twave; + *(unsigned short *) (ptr + 0x0a + wave_select) = program->depth; + *(unsigned short *) (ptr + 0x00) = program->freq_and_control; + } +} + +void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type) +{ + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) + snd_gf1_pokew(gus, lfo_addr + 0x00, snd_gf1_peekw(gus, lfo_addr + 0x00) | 0x8000); + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + *(unsigned short *) (ptr + 0x00) |= 0x8000; + } +} + +void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type) +{ + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) + snd_gf1_pokew(gus, lfo_addr + 0x00, + snd_gf1_peekw(gus, lfo_addr + 0x00) & ~0x8000); + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + *(unsigned short *) (ptr + 0x00) &= ~0x8000; + } +} + +void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice, + int lfo_type, int freq) +{ + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) + snd_gf1_pokew(gus, lfo_addr + 0x00, + (snd_gf1_peekw(gus, lfo_addr + 0x00) & ~0x7ff) | (freq & 0x7ff)); + if (gus->gf1.sw_lfo) { + unsigned long flags; + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + CLI(&flags); + *(unsigned short *) (ptr + 0x00) &= ~0x7ff; + *(unsigned short *) (ptr + 0x00) |= freq & 0x7ff; + STI(&flags); + } +} + +void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice, + int lfo_type, int depth) +{ + unsigned long flags; + unsigned int lfo_addr; + unsigned short control = 0; + unsigned char *ptr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + ptr = gus->gf1.lfos + lfo_addr; + if (gus->gf1.hw_lfo) + control = snd_gf1_peekw(gus, lfo_addr + 0x00); + if (gus->gf1.sw_lfo) + control = *(unsigned short *) (ptr + 0x00); + if (depth < 0) { + control |= 0x1000; + depth = -depth; + } else + control &= ~0x1000; + if (gus->gf1.hw_lfo) { + CLI(&flags); + snd_gf1_poke(gus, lfo_addr + 0x02, (unsigned char) depth); + snd_gf1_pokew(gus, lfo_addr + 0x0a + ((control & 0x4000) >> 12), depth << 5); + snd_gf1_pokew(gus, lfo_addr + 0x00, control); + STI(&flags); + } + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + CLI(&flags); + *(ptr + 0x02) = (unsigned char) depth; + *(unsigned short *) (ptr + 0x0a + ((control & 0x4000) >> 12)) = depth << 5; + *(unsigned short *) (ptr + 0x00) = control; + STI(&flags); + } +} + +void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type, + int freq, int current_depth, int depth, int sweep, + int shape) +{ + struct ULTRA_STRU_IW_LFO_PROGRAM program; + + program.freq_and_control = 0x8000 | (freq & 0x7ff); + if (shape & ULTRA_STRU_IW_LFO_SHAPE_POSTRIANGLE) + program.freq_and_control |= 0x2000; + if (depth < 0) { + program.freq_and_control |= 0x1000; + depth = -depth; + } + program.twave = 0; + program.depth = current_depth; + program.depth_final = depth; + if (sweep) { + program.depth_inc = (unsigned char) (((int) ((depth << 5) - current_depth) << 9) / (sweep * 4410L)); + if (!program.depth_inc) + program.depth_inc++; + } else + program.depth = (unsigned short) (depth << 5); + snd_gf1_lfo_program(gus, voice, lfo_type, &program); +} + +void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type) +{ + unsigned long flags; + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) { + snd_gf1_pokew(gus, lfo_addr + 0x00, 0x0000); + CLI(&flags); + gf1_select_voice(gus, voice); + snd_gf1_write8(gus, lfo_type == ULTRA_LFO_VIBRATO ? GF1_VB_FREQUENCY_LFO : GF1_VB_VOLUME_LFO, 0); + STI(&flags); + } + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + snd_gf1_voice_t *pvoice; + + *(unsigned short *) (ptr + 0x00) = 0; + *(unsigned short *) (ptr + 0x04) = 0; + *(unsigned short *) (ptr + 0x06) = 0; + if (gus->gf1.syn_voices) { + pvoice = gus->gf1.syn_voices + voice; + if (lfo_type == ULTRA_LFO_VIBRATO) + pvoice->lfo_fc = 0; + else + pvoice->lfo_volume = 0; + snd_gf1_lfo_register_setup(gus, pvoice, lfo_type == ULTRA_LFO_VIBRATO ? 1 : 2); + } else if (gus->gf1.enh_mode) { + CLI(&flags); + gf1_select_voice(gus, voice); + snd_gf1_write8(gus, lfo_type == ULTRA_LFO_VIBRATO ? GF1_VB_FREQUENCY_LFO : GF1_VB_VOLUME_LFO, 0); + STI(&flags); + } + } +} + +void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *data) +{ + int lfo_type; + int lfo_command; + int temp1, temp2; + + lfo_type = *data >> 7; + lfo_command = *data & 0x7f; + switch (lfo_command) { + case ULTRA_LFO_SETUP: /* setup */ + temp1 = snd_gf1_get_word(data, 2); + temp2 = snd_gf1_get_word(data, 4); + snd_gf1_lfo_setup(gus, voice, lfo_type, temp1 & 0x7ff, 0, temp2 > 255 ? 255 : temp2, snd_gf1_get_byte(data, 1), (temp1 & 0x2000) >> 13); + break; + case ULTRA_LFO_FREQ: /* freq */ + snd_gf1_lfo_change_depth(gus, voice, lfo_type, snd_gf1_get_word(data, 2)); + break; + case ULTRA_LFO_DEPTH: /* depth */ + snd_gf1_lfo_change_freq(gus, voice, lfo_type, snd_gf1_get_word(data, 2)); + break; + case ULTRA_LFO_ENABLE: /* enable */ + snd_gf1_lfo_enable(gus, voice, lfo_type); + break; + case ULTRA_LFO_DISABLE: /* disable */ + snd_gf1_lfo_disable(gus, voice, lfo_type); + break; + case ULTRA_LFO_SHUTDOWN: /* shutdown */ + snd_gf1_lfo_shutdown(gus, voice, lfo_type); + break; + } +} diff -Nru a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_main.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,518 @@ +/* + * Routines for Gravis UltraSound soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards"); +MODULE_LICENSE("GPL"); + +#define chip_t snd_gus_card_t + +static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +int snd_gus_use_inc(snd_gus_card_t * gus) +{ + MOD_INC_USE_COUNT; + if (!try_inc_mod_count(gus->card->module)) { + MOD_DEC_USE_COUNT; + return 0; + } + return 1; +} + +void snd_gus_use_dec(snd_gus_card_t * gus) +{ + dec_mod_count(gus->card->module); + MOD_DEC_USE_COUNT; +} + +static int snd_gus_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 31; + return 0; +} + +static int snd_gus_joystick_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = gus->joystick_dac & 31; + return 0; +} + +static int snd_gus_joystick_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval; + + nval = ucontrol->value.integer.value[0] & 31; + spin_lock_irqsave(&gus->reg_lock, flags); + change = gus->joystick_dac != nval; + gus->joystick_dac = nval; + snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_gus_joystick_control = { + iface: SNDRV_CTL_ELEM_IFACE_CARD, + name: "Joystick Speed", + info: snd_gus_joystick_info, + get: snd_gus_joystick_get, + put: snd_gus_joystick_put +}; + +static void snd_gus_init_control(snd_gus_card_t *gus) +{ + if (!gus->ace_flag) + snd_ctl_add(gus->card, snd_ctl_new1(&snd_gus_joystick_control, gus)); +} + +/* + * + */ + +static int snd_gus_free(snd_gus_card_t *gus) +{ + if (gus->gf1.res_port2 == NULL) + goto __hw_end; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (gus->seq_dev) { + snd_device_free(gus->card, gus->seq_dev); + gus->seq_dev = NULL; + } +#endif + snd_gf1_stop(gus); + snd_gus_init_dma_irq(gus, 0); + __hw_end: + if (gus->gf1.res_port1) { + release_resource(gus->gf1.res_port1); + kfree_nocheck(gus->gf1.res_port1); + } + if (gus->gf1.res_port2) { + release_resource(gus->gf1.res_port2); + kfree_nocheck(gus->gf1.res_port2); + } + if (gus->gf1.irq >= 0) + free_irq(gus->gf1.irq, (void *) gus); + if (gus->gf1.dma1 >= 0) { + disable_dma(gus->gf1.dma1); + free_dma(gus->gf1.dma1); + } + if (!gus->equal_dma && gus->gf1.dma2 >= 0) { + disable_dma(gus->gf1.dma2); + free_dma(gus->gf1.dma2); + } + snd_magic_kfree(gus); + return 0; +} + +static int snd_gus_dev_free(snd_device_t *device) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, device->device_data, return -ENXIO); + return snd_gus_free(gus); +} + +int snd_gus_create(snd_card_t * card, + unsigned long port, + int irq, int dma1, int dma2, + int timer_dev, + int voices, + int pcm_channels, + int effect, + snd_gus_card_t **rgus) +{ + snd_gus_card_t *gus; + int err; + static snd_device_ops_t ops = { + dev_free: snd_gus_dev_free, + }; + + *rgus = NULL; + gus = snd_magic_kcalloc(snd_gus_card_t, 0, GFP_KERNEL); + if (gus == NULL) + return -ENOMEM; + gus->gf1.irq = -1; + gus->gf1.dma1 = -1; + gus->gf1.dma2 = -1; + gus->card = card; + gus->gf1.port = port; + /* fill register variables for speedup */ + gus->gf1.reg_page = GUSP(gus, GF1PAGE); + gus->gf1.reg_regsel = GUSP(gus, GF1REGSEL); + gus->gf1.reg_data8 = GUSP(gus, GF1DATAHIGH); + gus->gf1.reg_data16 = GUSP(gus, GF1DATALOW); + gus->gf1.reg_irqstat = GUSP(gus, IRQSTAT); + gus->gf1.reg_dram = GUSP(gus, DRAM); + gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL); + gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA); + /* allocate resources */ + if ((gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)")) == NULL) { + snd_gus_free(gus); + return -EBUSY; + } + if ((gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)")) == NULL) { + snd_gus_free(gus); + return -EBUSY; + } + if (irq >= 0 && request_irq(irq, snd_gus_interrupt, SA_INTERRUPT, "GUS GF1", (void *) gus)) { + snd_gus_free(gus); + return -EBUSY; + } + gus->gf1.irq = irq; + if (request_dma(dma1, "GUS - 1")) { + snd_gus_free(gus); + return -EBUSY; + } + gus->gf1.dma1 = dma1; + if (dma2 >= 0 && dma1 != dma2) { + if (request_dma(dma2, "GUS - 2")) { + snd_gus_free(gus); + return -EBUSY; + } + gus->gf1.dma2 = dma2; + } else { + gus->gf1.dma2 = gus->gf1.dma1; + gus->equal_dma = 1; + } + gus->timer_dev = timer_dev; + if (voices < 14) + voices = 14; + if (voices > 32) + voices = 32; + if (pcm_channels < 0) + pcm_channels = 0; + if (pcm_channels > 8) + pcm_channels = 8; + pcm_channels++; + pcm_channels &= ~1; + gus->gf1.effect = effect ? 1 : 0; + gus->gf1.active_voices = voices; + gus->gf1.pcm_channels = pcm_channels; + gus->gf1.volume_ramp = 25; + gus->gf1.smooth_pan = 1; + spin_lock_init(&gus->reg_lock); + spin_lock_init(&gus->voice_alloc); + spin_lock_init(&gus->active_voice_lock); + spin_lock_init(&gus->event_lock); + spin_lock_init(&gus->dma_lock); + spin_lock_init(&gus->pcm_volume_level_lock); + spin_lock_init(&gus->uart_cmd_lock); + init_MUTEX(&gus->dma_mutex); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops)) < 0) { + snd_gus_free(gus); + return err; + } + *rgus = gus; + return 0; +} + +/* + * Memory detection routine for plain GF1 soundcards + */ + +static int snd_gus_detect_memory(snd_gus_card_t * gus) +{ + int l, idx, local; + unsigned char d; + + snd_gf1_poke(gus, 0L, 0xaa); + snd_gf1_poke(gus, 1L, 0x55); + if (snd_gf1_peek(gus, 0L) != 0xaa || snd_gf1_peek(gus, 1L) != 0x55) { + snd_printk("plain GF1 card at 0x%lx without onboard DRAM?\n", gus->gf1.port); + return -ENOMEM; + } + for (idx = 1, d = 0xab; idx < 4; idx++, d++) { + local = idx << 18; + snd_gf1_poke(gus, local, d); + snd_gf1_poke(gus, local + 1, d + 1); + if (snd_gf1_peek(gus, local) != d || + snd_gf1_peek(gus, local + 1) != d + 1 || + snd_gf1_peek(gus, 0L) != 0xaa) + break; + } +#if 1 + gus->gf1.memory = idx << 18; +#else + gus->gf1.memory = 256 * 1024; +#endif + for (l = 0, local = gus->gf1.memory; l < 4; l++, local -= 256 * 1024) { + gus->gf1.mem_alloc.banks_8[l].address = + gus->gf1.mem_alloc.banks_8[l].size = 0; + gus->gf1.mem_alloc.banks_16[l].address = l << 18; + gus->gf1.mem_alloc.banks_16[l].size = local > 0 ? 256 * 1024 : 0; + } + gus->gf1.mem_alloc.banks_8[0].size = gus->gf1.memory; + return 0; /* some memory were detected */ +} + +static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches) +{ + snd_card_t *card; + unsigned long flags; + int irq, dma1, dma2; + static unsigned char irqs[16] = + {0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7}; + static unsigned char dmas[8] = + {6, 1, 0, 2, 0, 3, 4, 5}; + + snd_assert(gus != NULL, return -EINVAL); + card = gus->card; + snd_assert(card != NULL, return -EINVAL); + + gus->mix_cntrl_reg &= 0xf8; + gus->mix_cntrl_reg |= 0x01; /* disable MIC, LINE IN, enable LINE OUT */ + if (gus->codec_flag || gus->ess_flag) { + gus->mix_cntrl_reg &= ~1; /* enable LINE IN */ + gus->mix_cntrl_reg |= 4; /* enable MIC */ + } + dma1 = gus->gf1.dma1; + dma1 = dma1 < 0 ? -dma1 : dma1; + dma1 = dmas[dma1 & 7]; + dma2 = gus->gf1.dma2; + dma2 = dma2 < 0 ? -dma2 : dma2; + dma2 = dmas[dma2 & 7]; +#if 0 + printk("dma1 = %i, dma2 = %i\n", gus->gf1.dma1, gus->gf1.dma2); +#endif + dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3); + + if ((dma1 & 7) == 0 || (dma2 & 7) == 0) { + snd_printk("Error! DMA isn't defined.\n"); + return -EINVAL; + } + irq = gus->gf1.irq; + irq = irq < 0 ? -irq : irq; + irq = irqs[irq & 0x0f]; + if (irq == 0) { + snd_printk("Error! IRQ isn't defined.\n"); + return -EINVAL; + } + irq |= 0x40; +#if 0 + card->mixer.mix_ctrl_reg |= 0x10; +#endif + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(5, GUSP(gus, REGCNTRLS)); + outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(0x00, GUSP(gus, IRQDMACNTRLREG)); + outb(0, GUSP(gus, REGCNTRLS)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + + udelay(100); + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(dma1, GUSP(gus, IRQDMACNTRLREG)); + if (latches) { + outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(irq, GUSP(gus, IRQDMACNTRLREG)); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + + udelay(100); + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(dma1, GUSP(gus, IRQDMACNTRLREG)); + if (latches) { + outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(irq, GUSP(gus, IRQDMACNTRLREG)); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + + snd_gf1_delay(gus); + + if (latches) + gus->mix_cntrl_reg |= 0x08; /* enable latches */ + else + gus->mix_cntrl_reg &= ~0x08; /* disable latches */ + spin_lock_irqsave(&gus->reg_lock, flags); + outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(0, GUSP(gus, GF1PAGE)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + + return 0; +} + +static int snd_gus_check_version(snd_gus_card_t * gus) +{ + unsigned long flags; + unsigned char val, rev; + snd_card_t *card; + + card = gus->card; + spin_lock_irqsave(&gus->reg_lock, flags); + outb(0x20, GUSP(gus, REGCNTRLS)); + val = inb(GUSP(gus, REGCNTRLS)); + rev = inb(GUSP(gus, BOARDVERSION)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_printdd("GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev); + strcpy(card->driver, "GUS"); + strcpy(card->longname, "Gravis UltraSound Classic (2.4)"); + if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) { + if (rev >= 5 && rev <= 9) { + gus->ics_flag = 1; + if (rev == 5) + gus->ics_flipped = 1; + card->longname[27] = '3'; + card->longname[29] = rev == 5 ? '5' : '7'; + } + if (rev >= 10 && rev != 255) { + if (rev >= 10 && rev <= 11) { + strcpy(card->driver, "GUS MAX"); + strcpy(card->longname, "Gravis UltraSound MAX"); + gus->max_flag = 1; + } else if (rev == 0x30) { + strcpy(card->driver, "GUS ACE"); + strcpy(card->longname, "Gravis UltraSound Ace"); + gus->ace_flag = 1; + } else if (rev == 0x50) { + strcpy(card->driver, "GUS Extreme"); + strcpy(card->longname, "Gravis UltraSound Extreme"); + gus->ess_flag = 1; + } else { + snd_printk("unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n", gus->gf1.port, rev, val); + snd_printk(" please - report to \n"); + } + } + } + strcpy(card->shortname, card->longname); + gus->uart_enable = 1; /* standard GUSes doesn't have midi uart trouble */ + snd_gus_init_control(gus); + return 0; +} + +static void snd_gus_seq_dev_free(snd_seq_device_t *seq_dev) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, seq_dev->private_data, return); + gus->seq_dev = NULL; +} + +int snd_gus_initialize(snd_gus_card_t *gus) +{ + int err; + + if (!gus->interwave) { + if ((err = snd_gus_check_version(gus)) < 0) { + snd_printk("version check failed\n"); + return err; + } + if ((err = snd_gus_detect_memory(gus)) < 0) + return err; + } + if ((err = snd_gus_init_dma_irq(gus, 1)) < 0) + return err; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS, + sizeof(snd_gus_card_t*), &gus->seq_dev) >= 0) { + strcpy(gus->seq_dev->name, "GUS"); + *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus; + gus->seq_dev->private_data = gus; + gus->seq_dev->private_free = snd_gus_seq_dev_free; + } +#endif + snd_gf1_start(gus); + gus->initialized = 1; + return 0; +} + + /* gus_io.c */ +EXPORT_SYMBOL(snd_gf1_delay); +EXPORT_SYMBOL(snd_gf1_write8); +EXPORT_SYMBOL(snd_gf1_look8); +EXPORT_SYMBOL(snd_gf1_write16); +EXPORT_SYMBOL(snd_gf1_look16); +EXPORT_SYMBOL(snd_gf1_i_write8); +EXPORT_SYMBOL(snd_gf1_i_look8); +EXPORT_SYMBOL(snd_gf1_i_write16); +EXPORT_SYMBOL(snd_gf1_i_look16); +EXPORT_SYMBOL(snd_gf1_dram_addr); +EXPORT_SYMBOL(snd_gf1_write_addr); +EXPORT_SYMBOL(snd_gf1_poke); +EXPORT_SYMBOL(snd_gf1_peek); + /* gus_reset.c */ +EXPORT_SYMBOL(snd_gf1_alloc_voice); +EXPORT_SYMBOL(snd_gf1_free_voice); +EXPORT_SYMBOL(snd_gf1_ctrl_stop); +EXPORT_SYMBOL(snd_gf1_stop_voice); +EXPORT_SYMBOL(snd_gf1_start); +EXPORT_SYMBOL(snd_gf1_stop); + /* gus_mixer.c */ +EXPORT_SYMBOL(snd_gf1_new_mixer); + /* gus_pcm.c */ +EXPORT_SYMBOL(snd_gf1_pcm_new); + /* gus.c */ +EXPORT_SYMBOL(snd_gus_use_inc); +EXPORT_SYMBOL(snd_gus_use_dec); +EXPORT_SYMBOL(snd_gus_create); +EXPORT_SYMBOL(snd_gus_initialize); + /* gus_irq.c */ +EXPORT_SYMBOL(snd_gus_interrupt); + /* gus_uart.c */ +EXPORT_SYMBOL(snd_gf1_rawmidi_new); + /* gus_dram.c */ +EXPORT_SYMBOL(snd_gus_dram_write); +EXPORT_SYMBOL(snd_gus_dram_read); + /* gus_volume.c */ +EXPORT_SYMBOL(snd_gf1_lvol_to_gvol_raw); +EXPORT_SYMBOL(snd_gf1_translate_freq); + /* gus_mem.c */ +EXPORT_SYMBOL(snd_gf1_mem_alloc); +EXPORT_SYMBOL(snd_gf1_mem_xfree); +EXPORT_SYMBOL(snd_gf1_mem_free); +EXPORT_SYMBOL(snd_gf1_mem_lock); + +/* + * INIT part + */ + +static int __init alsa_gus_init(void) +{ + return 0; +} + +static void __exit alsa_gus_exit(void) +{ +} + +module_init(alsa_gus_init) +module_exit(alsa_gus_exit) diff -Nru a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_mem.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,368 @@ +/* + * Copyright (c) by Jaroslav Kysela + * GUS's memory allocation routines / bottom layer + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#ifdef CONFIG_SND_DEBUG +static void snd_gf1_mem_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer); +#endif + +void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup) +{ + if (!xup) { + down(&alloc->memory_mutex); + } else { + up(&alloc->memory_mutex); + } +} + +snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc, + snd_gf1_mem_block_t * block) +{ + snd_gf1_mem_block_t *pblock, *nblock; + + nblock = (snd_gf1_mem_block_t *) kmalloc(sizeof(snd_gf1_mem_block_t), GFP_KERNEL); + if (nblock == NULL) + return NULL; + *nblock = *block; + pblock = alloc->first; + while (pblock) { + if (pblock->ptr > nblock->ptr) { + nblock->prev = pblock->prev; + nblock->next = pblock; + pblock->prev = nblock; + if (pblock == alloc->first) + alloc->first = nblock; + else + nblock->prev->next = nblock; + up(&alloc->memory_mutex); + return 0; + } + pblock = pblock->next; + } + nblock->next = NULL; + if (alloc->last == NULL) { + nblock->prev = NULL; + alloc->first = alloc->last = nblock; + } else { + nblock->prev = alloc->last; + alloc->last->next = nblock; + alloc->last = nblock; + } + return nblock; +} + +int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block) +{ + if (block->share) { /* ok.. shared block */ + block->share--; + up(&alloc->memory_mutex); + return 0; + } + if (alloc->first == block) { + alloc->first = block->next; + if (block->next) + block->next->prev = NULL; + } else { + block->prev->next = block->next; + if (block->next) + block->next->prev = block->prev; + } + if (alloc->last == block) { + alloc->last = block->prev; + if (block->prev) + block->prev->next = NULL; + } else { + block->next->prev = block->prev; + if (block->prev) + block->prev->next = block->next; + } + if (block->name) + kfree(block->name); + kfree(block); + return 0; +} + +snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc, + unsigned int address) +{ + snd_gf1_mem_block_t *block; + + for (block = alloc->first; block; block = block->next) { + if (block->ptr == address) { + return block; + } + } + return NULL; +} + +snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc, + unsigned int *share_id) +{ + snd_gf1_mem_block_t *block; + + if (!share_id[0] && !share_id[1] && + !share_id[2] && !share_id[3]) + return NULL; + for (block = alloc->first; block; block = block->next) + if (!memcmp(share_id, block->share_id, sizeof(share_id))) + return block; + return NULL; +} + +static int snd_gf1_mem_find(snd_gf1_mem_t * alloc, + snd_gf1_mem_block_t * block, + unsigned int size, int w_16, int align) +{ + snd_gf1_bank_info_t *info = w_16 ? alloc->banks_16 : alloc->banks_8; + unsigned int idx, boundary; + int size1; + snd_gf1_mem_block_t *pblock; + unsigned int ptr1, ptr2; + + align--; + if (w_16 && align < 1) + align = 1; + block->flags = w_16 ? SNDRV_GF1_MEM_BLOCK_16BIT : 0; + block->owner = SNDRV_GF1_MEM_OWNER_DRIVER; + block->share = 0; + block->share_id[0] = block->share_id[1] = + block->share_id[2] = block->share_id[3] = 0; + block->name = NULL; + block->prev = block->next = NULL; + for (pblock = alloc->first, idx = 0; pblock; pblock = pblock->next) { + while (pblock->ptr >= (boundary = info[idx].address + info[idx].size)) + idx++; + while (pblock->ptr + pblock->size >= (boundary = info[idx].address + info[idx].size)) + idx++; + ptr2 = boundary; + if (pblock->next) { + if (pblock->ptr + pblock->size == pblock->next->ptr) + continue; + if (pblock->next->ptr < boundary) + ptr2 = pblock->next->ptr; + } + ptr1 = (pblock->ptr + pblock->size + align) & ~align; + if (ptr1 >= ptr2) + continue; + size1 = ptr2 - ptr1; + if (size <= size1) { + block->ptr = ptr1; + block->size = size; + return 0; + } + } + while (++idx < 4) { + if (size <= info[idx].size) { + /* I assume that bank address is already aligned.. */ + block->ptr = info[idx].address; + block->size = size; + return 0; + } + } + return -ENOMEM; +} + +snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner, + char *name, int size, int w_16, int align, + unsigned int *share_id) +{ + snd_gf1_mem_block_t block, *nblock; + + snd_gf1_mem_lock(alloc, 0); + if (share_id != NULL) { + nblock = snd_gf1_mem_share(alloc, share_id); + if (nblock != NULL) { + if (size != nblock->size) { + /* TODO: remove in the future */ + snd_printk("snd_gf1_mem_alloc - share: sizes differ\n"); + goto __std; + } + nblock->share++; + snd_gf1_mem_lock(alloc, 1); + return NULL; + } + } + __std: + if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0) { + snd_gf1_mem_lock(alloc, 1); + return NULL; + } + if (share_id != NULL) + memcpy(&block.share_id, share_id, sizeof(block.share_id)); + block.owner = owner; + block.name = snd_kmalloc_strdup(name, GFP_KERNEL); + nblock = snd_gf1_mem_xalloc(alloc, &block); + snd_gf1_mem_lock(alloc, 1); + return nblock; +} + +int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address) +{ + int result; + snd_gf1_mem_block_t *block; + + snd_gf1_mem_lock(alloc, 0); + if ((block = snd_gf1_mem_look(alloc, address)) != NULL) { + result = snd_gf1_mem_xfree(alloc, block); + snd_gf1_mem_lock(alloc, 1); + return result; + } + snd_gf1_mem_lock(alloc, 1); + return -EINVAL; +} + +int snd_gf1_mem_init(snd_gus_card_t * gus) +{ + snd_gf1_mem_t *alloc; + snd_gf1_mem_block_t block; +#ifdef CONFIG_SND_DEBUG + snd_info_entry_t *entry; +#endif + + alloc = &gus->gf1.mem_alloc; + init_MUTEX(&alloc->memory_mutex); + alloc->first = alloc->last = NULL; + if (!gus->gf1.memory) + return 0; + + memset(&block, 0, sizeof(block)); + block.owner = SNDRV_GF1_MEM_OWNER_DRIVER; + if (gus->gf1.enh_mode) { + block.ptr = 0; + block.size = 1024; + block.name = snd_kmalloc_strdup("InterWave LFOs", GFP_KERNEL); + if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + return -ENOMEM; + } + block.ptr = gus->gf1.default_voice_address; + block.size = 4; + block.name = snd_kmalloc_strdup("Voice default (NULL's)", GFP_KERNEL); + if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + return -ENOMEM; +#ifdef CONFIG_SND_DEBUG + alloc->info_entry = NULL; + entry = snd_info_create_card_entry(gus->card, "gusmem", gus->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 256 * 1024; + entry->c.text.read = snd_gf1_mem_info_read; + entry->private_data = gus; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + alloc->info_entry = entry; +#endif + return 0; +} + +int snd_gf1_mem_done(snd_gus_card_t * gus) +{ + snd_gf1_mem_t *alloc; + snd_gf1_mem_block_t *block, *nblock; + + alloc = &gus->gf1.mem_alloc; + block = alloc->first; + while (block) { + nblock = block->next; + snd_gf1_mem_xfree(alloc, block); + block = nblock; + } +#ifdef CONFIG_SND_DEBUG + if (alloc->info_entry) + snd_info_unregister(alloc->info_entry); +#endif + return 0; +} + +#ifdef CONFIG_SND_DEBUG +static void snd_gf1_mem_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_gus_card_t *gus; + snd_gf1_mem_t *alloc; + snd_gf1_mem_block_t *block; + unsigned int total, used; + int i; + + gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return); + alloc = &gus->gf1.mem_alloc; + down(&alloc->memory_mutex); + snd_iprintf(buffer, "8-bit banks : \n "); + for (i = 0; i < 4; i++) + snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : ""); + snd_iprintf(buffer, "\n" + "16-bit banks : \n "); + for (i = total = 0; i < 4; i++) { + snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_16[i].address, alloc->banks_16[i].size >> 10, i + 1 < 4 ? "," : ""); + total += alloc->banks_16[i].size; + } + snd_iprintf(buffer, "\n"); + used = 0; + for (block = alloc->first, i = 0; block; block = block->next, i++) { + used += block->size; + snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size); + if (block->share || + block->share_id[0] || block->share_id[1] || + block->share_id[2] || block->share_id[3]) + snd_iprintf(buffer, " Share : %i [id0 0x%x] [id1 0x%x] [id2 0x%x] [id3 0x%x]\n", + block->share, + block->share_id[0], block->share_id[1], + block->share_id[2], block->share_id[3]); + snd_iprintf(buffer, " Flags :%s\n", + block->flags & SNDRV_GF1_MEM_BLOCK_16BIT ? " 16-bit" : ""); + snd_iprintf(buffer, " Owner : "); + switch (block->owner) { + case SNDRV_GF1_MEM_OWNER_DRIVER: + snd_iprintf(buffer, "driver - %s\n", block->name); + break; + case SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE: + snd_iprintf(buffer, "SIMPLE wave\n"); + break; + case SNDRV_GF1_MEM_OWNER_WAVE_GF1: + snd_iprintf(buffer, "GF1 wave\n"); + break; + case SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF: + snd_iprintf(buffer, "IWFFFF wave\n"); + break; + default: + snd_iprintf(buffer, "unknown\n"); + } + } + snd_iprintf(buffer, " Total: memory = %i, used = %i, free = %i\n", + total, used, total - used); + up(&alloc->memory_mutex); +#if 0 + ultra_iprintf(buffer, " Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n", + ultra_memory_free_size(card, &card->gf1.mem_alloc), + ultra_memory_free_block(card, &card->gf1.mem_alloc, 0), + ultra_memory_free_block(card, &card->gf1.mem_alloc, 1)); +#endif +} +#endif diff -Nru a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_mem_proc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,169 @@ +/* + * Copyright (c) by Jaroslav Kysela + * GUS's memory access via proc filesystem + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +typedef struct gus_proc_private { + int rom; /* data are in ROM */ + unsigned int address; + unsigned int size; + snd_gus_card_t * gus; +} gus_proc_private_t; + +static long snd_gf1_mem_proc_dump(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO); + snd_gus_card_t *gus = priv->gus; + int err; + + size = count; + if (file->f_pos + size > priv->size) + size = (long)priv->size - file->f_pos; + if (size > 0) { + if ((err = snd_gus_dram_read(gus, buf, file->f_pos, size, priv->rom)) < 0) + return err; + file->f_pos += size; + return size; + } + return 0; +} + +static long long snd_gf1_mem_proc_llseek(snd_info_entry_t *entry, + void *private_file_data, + struct file *file, + long long offset, + int orig) +{ + gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO); + + switch (orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + break; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + break; + case 2: /* SEEK_END */ + file->f_pos = priv->size - offset; + break; + default: + return -EINVAL; + } + if (file->f_pos > priv->size) + file->f_pos = priv->size; + return file->f_pos; +} + +static void snd_gf1_mem_proc_free(snd_info_entry_t *entry) +{ + gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return); + snd_magic_kfree(priv); +} + +static struct snd_info_entry_ops snd_gf1_mem_proc_ops = { + read: snd_gf1_mem_proc_dump, + llseek: snd_gf1_mem_proc_llseek, +}; + +int snd_gf1_mem_proc_init(snd_gus_card_t * gus) +{ + int idx; + char name[16]; + gus_proc_private_t *priv; + snd_info_entry_t *entry; + + memset(&gus->gf1.rom_entries, 0, sizeof(gus->gf1.rom_entries)); + memset(&gus->gf1.ram_entries, 0, sizeof(gus->gf1.ram_entries)); + for (idx = 0; idx < 4; idx++) { + if (gus->gf1.mem_alloc.banks_8[idx].size > 0) { + priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); + if (priv == NULL) { + snd_gf1_mem_proc_done(gus); + return -ENOMEM; + } + priv->gus = gus; + sprintf(name, "gus-ram-%i", idx); + entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = priv; + entry->private_free = snd_gf1_mem_proc_free; + entry->c.ops = &snd_gf1_mem_proc_ops; + priv->address = gus->gf1.mem_alloc.banks_8[idx].address; + priv->size = entry->size = gus->gf1.mem_alloc.banks_8[idx].size; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + gus->gf1.ram_entries[idx] = entry; + } + } + for (idx = 0; idx < 4; idx++) { + if (gus->gf1.rom_present & (1 << idx)) { + priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); + if (priv == NULL) { + snd_gf1_mem_proc_done(gus); + return -ENOMEM; + } + priv->rom = 1; + priv->gus = gus; + sprintf(name, "gus-rom-%i", idx); + entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = priv; + entry->private_free = snd_gf1_mem_proc_free; + entry->c.ops = &snd_gf1_mem_proc_ops; + priv->address = idx * 4096 * 1024; + priv->size = entry->size = gus->gf1.rom_memory; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + gus->gf1.rom_entries[idx] = entry; + } + } + return 0; +} + +int snd_gf1_mem_proc_done(snd_gus_card_t * gus) +{ + int idx; + + for (idx = 0; idx < 4; idx++) { + if (gus->gf1.ram_entries[idx]) + snd_info_unregister(gus->gf1.ram_entries[idx]); + } + for (idx = 0; idx < 4; idx++) { + if (gus->gf1.rom_entries[idx]) + snd_info_unregister(gus->gf1.rom_entries[idx]); + } + return 0; +} diff -Nru a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_mixer.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,205 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of ICS 2101 chip and "mixer" in GF1 chip + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +#define chip_t snd_gus_card_t + +/* + * + */ + +#define GF1_SINGLE(xname, xindex, shift, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_gf1_info_single, \ + get: snd_gf1_get_single, put: snd_gf1_put_single, \ + private_value: shift | (invert << 8) } + +static int snd_gf1_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_gf1_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + + ucontrol->value.integer.value[0] = (gus->mix_cntrl_reg >> shift) & 1; + if (invert) + ucontrol->value.integer.value[0] ^= 1; + return 0; +} + +static int snd_gf1_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + int change; + unsigned char oval, nval; + + nval = ucontrol->value.integer.value[0] & 1; + if (invert) + nval ^= 1; + nval <<= shift; + spin_lock_irqsave(&gus->reg_lock, flags); + oval = gus->mix_cntrl_reg; + nval = (oval & ~(1 << shift)) | nval; + change = nval != oval; + outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG)); + outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return change; +} + +#define ICS_DOUBLE(xname, xindex, addr) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ics_info_double, \ + get: snd_ics_get_double, put: snd_ics_put_double, \ + private_value: addr } + +static int snd_ics_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_ics_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value & 0xff; + unsigned char left, right; + + spin_lock_irqsave(&gus->reg_lock, flags); + left = gus->gf1.ics_regs[addr][0]; + right = gus->gf1.ics_regs[addr][1]; + spin_unlock_irqrestore(&gus->reg_lock, flags); + ucontrol->value.integer.value[0] = left & 127; + ucontrol->value.integer.value[1] = right & 127; + return 0; +} + +static int snd_ics_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value & 0xff; + int change; + unsigned char val1, val2, oval1, oval2, tmp; + + val1 = ucontrol->value.integer.value[0] & 127; + val2 = ucontrol->value.integer.value[1] & 127; + spin_lock_irqsave(&gus->reg_lock, flags); + oval1 = gus->gf1.ics_regs[addr][0]; + oval2 = gus->gf1.ics_regs[addr][1]; + change = val1 != oval1 || val2 != oval2; + gus->gf1.ics_regs[addr][0] = val1; + gus->gf1.ics_regs[addr][1] = val2; + if (gus->ics_flag && gus->ics_flipped && + (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) { + tmp = val1; + val1 = val2; + val2 = tmp; + } + addr <<= 3; + outb(addr | 0, GUSP(gus, MIXCNTRLPORT)); + outb(1, GUSP(gus, MIXDATAPORT)); + outb(addr | 2, GUSP(gus, MIXCNTRLPORT)); + outb((unsigned char) val1, GUSP(gus, MIXDATAPORT)); + outb(addr | 1, GUSP(gus, MIXCNTRLPORT)); + outb(2, GUSP(gus, MIXDATAPORT)); + outb(addr | 3, GUSP(gus, MIXCNTRLPORT)); + outb((unsigned char) val2, GUSP(gus, MIXDATAPORT)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return change; +} + +#define GF1_CONTROLS (sizeof(snd_gf1_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_gf1_controls[] = { +GF1_SINGLE("Master Playback Switch", 0, 1, 1), +GF1_SINGLE("Line Switch", 0, 0, 1), +GF1_SINGLE("Mic Switch", 0, 2, 0) +}; + +#define ICS_CONTROLS (sizeof(snd_ics_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ics_controls[] = { +GF1_SINGLE("Master Playback Switch", 0, 1, 1), +ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV), +ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV), +GF1_SINGLE("Line Switch", 0, 0, 1), +ICS_DOUBLE("Line Playback Volume", 0, SNDRV_ICS_LINE_DEV), +GF1_SINGLE("Mic Switch", 0, 2, 0), +ICS_DOUBLE("Mic Playback Volume", 0, SNDRV_ICS_MIC_DEV), +ICS_DOUBLE("CD Playback Volume", 0, SNDRV_ICS_CD_DEV) +}; + +int snd_gf1_new_mixer(snd_gus_card_t * gus) +{ + snd_card_t *card; + int idx, err, max; + + snd_assert(gus != NULL, return -EINVAL); + card = gus->card; + snd_assert(card != NULL, return -EINVAL); + + if (gus->ics_flag) + snd_component_add(card, "ICS2101"); + if (card->mixername[0] == '\0') { + strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); + } else { + if (gus->ics_flag) + strcat(card->mixername, ",ICS2101"); + strcat(card->mixername, ",GF1"); + } + + if (!gus->ics_flag) { + max = gus->ess_flag ? 1 : GF1_CONTROLS; + for (idx = 0; idx < max; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus))) < 0) + return err; + } + } else { + for (idx = 0; idx < ICS_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus))) < 0) + return err; + } + } + return 0; +} diff -Nru a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_pcm.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,890 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of GF1 chip (PCM things) + * + * InterWave chips supports interleaved DMA, but this feature isn't used in + * this code. + * + * This code emulates autoinit DMA transfer for playback, recording by GF1 + * chip doesn't support autoinit DMA. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include "gus_tables.h" + +#define chip_t snd_gus_card_t + +/* maximum rate */ + +#define SNDRV_GF1_PCM_RATE 48000 + +#define SNDRV_GF1_PCM_PFLG_NONE 0 +#define SNDRV_GF1_PCM_PFLG_ACTIVE (1<<0) +#define SNDRV_GF1_PCM_PFLG_NEUTRAL (2<<0) + +typedef struct { + snd_gus_card_t * gus; + snd_pcm_substream_t * substream; + spinlock_t lock; + int voices; + snd_gus_voice_t *pvoices[2]; + unsigned int memory; + unsigned short flags; + unsigned char voice_ctrl, ramp_ctrl; + unsigned int bpos; + unsigned int blocks; + unsigned int block_size; + unsigned int dma_size; + wait_queue_head_t sleep; + atomic_t dma_count; + int final_volume; +} gus_pcm_private_t; + +static int snd_gf1_pcm_use_dma = 1; + +static void snd_gf1_pcm_block_change_ack(snd_gus_card_t * gus, void *private_data) +{ + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, private_data, return); + + if (pcmp) { + atomic_dec(&pcmp->dma_count); + wake_up(&pcmp->sleep); + } +} + +static int snd_gf1_pcm_block_change(snd_pcm_substream_t * substream, + unsigned int offset, + unsigned int addr, + unsigned int count) +{ + snd_gf1_dma_block_t block; + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + + count += offset & 31; + offset &= ~31; + // snd_printk("block change - offset = 0x%x, count = 0x%x\n", offset, count); + memset(&block, 0, sizeof(block)); + block.cmd = SNDRV_GF1_DMA_IRQ; + if (snd_pcm_format_unsigned(runtime->format)) + block.cmd |= SNDRV_GF1_DMA_UNSIGNED; + if (snd_pcm_format_width(runtime->format) == 16) + block.cmd |= SNDRV_GF1_DMA_16BIT; + block.addr = addr & ~31; + block.buffer = runtime->dma_area + offset; + block.buf_addr = runtime->dma_addr + offset; + block.count = count; + block.private_data = pcmp; + block.ack = snd_gf1_pcm_block_change_ack; + if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0)) + atomic_inc(&pcmp->dma_count); + return 0; +} + +static void snd_gf1_pcm_trigger_up(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return); + snd_gus_card_t * gus = pcmp->gus; + unsigned long flags; + unsigned char voice_ctrl, ramp_ctrl; + unsigned short rate; + unsigned int curr, begin, end; + unsigned short vol; + unsigned char pan; + unsigned int voice; + + if (substream == NULL) + return; + spin_lock_irqsave(&pcmp->lock, flags); + if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { + spin_unlock_irqrestore(&pcmp->lock, flags); + return; + } + pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE; + pcmp->final_volume = 0; + spin_unlock_irqrestore(&pcmp->lock, flags); + rate = snd_gf1_translate_freq(gus, runtime->rate << 4); + /* enable WAVE IRQ */ + voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20; + /* enable RAMP IRQ + rollover */ + ramp_ctrl = 0x24; + if (pcmp->blocks == 1) { + voice_ctrl |= 0x08; /* loop enable */ + ramp_ctrl &= ~0x04; /* disable rollover */ + } + for (voice = 0; voice < pcmp->voices; voice++) { + begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels); + curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels; + end = curr + (pcmp->block_size / runtime->channels); + end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1; + // snd_printk("init: curr=0x%x, begin=0x%x, end=0x%x, ctrl=0x%x, ramp=0x%x, rate=0x%x\n", curr, begin, end, voice_ctrl, ramp_ctrl, rate); + pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8; + vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan); + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + } + spin_lock_irqsave(&gus->reg_lock, flags); + for (voice = 0; voice < pcmp->voices; voice++) { + snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00); /* deactivate voice */ + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + voice_ctrl &= ~0x20; + } + voice_ctrl |= 0x20; + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + for (voice = 0; voice < pcmp->voices; voice++) { + snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + voice_ctrl &= ~0x20; /* disable IRQ for next voice */ + } + } + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static void snd_gf1_pcm_interrupt_wave(snd_gus_card_t * gus, snd_gus_voice_t *pvoice) +{ + gus_pcm_private_t * pcmp; + snd_pcm_runtime_t * runtime; + unsigned char voice_ctrl, ramp_ctrl; + int idx; + unsigned int end, step; + + if (!pvoice->private_data) { + snd_printd("snd_gf1_pcm: unknown wave irq?\n"); + snd_gf1_smart_stop_voice(gus, pvoice->number); + return; + } + pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return); + if (pcmp == NULL) { + snd_printd("snd_gf1_pcm: unknown wave irq?\n"); + snd_gf1_smart_stop_voice(gus, pvoice->number); + return; + } + gus = pcmp->gus; + runtime = pcmp->substream->runtime; + + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, pvoice->number); + voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b; + ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03; +#if 0 + snd_gf1_select_voice(gus, pvoice->number); + printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); + snd_gf1_select_voice(gus, pcmp->pvoices[1]->number); + printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); + snd_gf1_select_voice(gus, pvoice->number); +#endif + pcmp->bpos++; + pcmp->bpos %= pcmp->blocks; + if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */ + voice_ctrl |= 0x08; /* enable loop */ + } else { + ramp_ctrl |= 0x04; /* enable rollover */ + } + end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels); + end -= voice_ctrl & 4 ? 2 : 1; + step = pcmp->dma_size / runtime->channels; + voice_ctrl |= 0x20; + if (!pcmp->final_volume) { + ramp_ctrl |= 0x20; + ramp_ctrl &= ~0x03; + } + for (idx = 0; idx < pcmp->voices; idx++, end += step) { + snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + voice_ctrl &= ~0x20; + } + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + voice_ctrl |= 0x20; + for (idx = 0; idx < pcmp->voices; idx++) { + snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + voice_ctrl &= ~0x20; + } + } + spin_unlock(&gus->reg_lock); + + snd_pcm_period_elapsed(pcmp->substream); +#if 0 + if ((runtime->flags & SNDRV_PCM_FLG_MMAP) && + *runtime->state == SNDRV_PCM_STATE_RUNNING) { + end = pcmp->bpos * pcmp->block_size; + if (runtime->channels > 1) { + snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2); + snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2); + } else { + snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size); + } + } +#endif +} + +static void snd_gf1_pcm_interrupt_volume(snd_gus_card_t * gus, snd_gus_voice_t * pvoice) +{ + unsigned short vol; + int cvoice; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return); + + /* stop ramp, but leave rollover bit untouched */ + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, pvoice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + spin_unlock(&gus->reg_lock); + if (pcmp == NULL) + return; + /* are we active? */ + if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) + return; + /* load real volume - better precision */ + cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1; + if (pcmp->substream == NULL) + return; + vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, pvoice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); + pcmp->final_volume = 1; + spin_unlock(&gus->reg_lock); +} + +static void snd_gf1_pcm_volume_change(snd_gus_card_t * gus) +{ +} + +static int snd_gf1_pcm_poke_block(snd_gus_card_t *gus, unsigned char *buf, + unsigned int pos, unsigned int count, + int w16, int invert) +{ + unsigned int len; + unsigned long flags; + + // printk("poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n", (int)buf, pos, count, gus->gf1.port); + while (count > 0) { + len = count; + if (len > 512) /* limit, to allow IRQ */ + len = 512; + count -= len; + if (gus->interwave) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00)); + snd_gf1_dram_addr(gus, pos); + if (w16) { + outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL)); + outsw(GUSP(gus, GF1DATALOW), buf, len >> 1); + } else { + outsb(GUSP(gus, DRAM), buf, len); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + buf += 512; + pos += 512; + } else { + invert = invert ? 0x80 : 0x00; + if (w16) { + len >>= 1; + while (len--) { + snd_gf1_poke(gus, pos++, *buf++); + snd_gf1_poke(gus, pos++, *buf++ ^ invert); + } + } else { + while (len--) + snd_gf1_poke(gus, pos++, *buf++ ^ invert); + } + } + schedule_timeout(1); + if (signal_pending(current)) + return -EAGAIN; + } + return 0; +} + +static int snd_gf1_pcm_playback_copy(snd_pcm_substream_t *substream, + int voice, + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned int bpos, len; + + bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); + len = samples_to_bytes(runtime, count); + snd_assert(bpos <= pcmp->dma_size, return -EIO); + snd_assert(bpos + len <= pcmp->dma_size, return -EIO); + if (copy_from_user(runtime->dma_area + bpos, src, len)) + return -EFAULT; + if (snd_gf1_pcm_use_dma && len > 32) { + return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); + } else { + snd_gus_card_t *gus = pcmp->gus; + int err, w16, invert; + + w16 = (snd_pcm_format_width(runtime->format) == 16); + invert = snd_pcm_format_unsigned(runtime->format); + if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) + return err; + } + return 0; +} + +static int snd_gf1_pcm_playback_silence(snd_pcm_substream_t *substream, + int voice, + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned int bpos, len; + + bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); + len = samples_to_bytes(runtime, count); + snd_assert(bpos <= pcmp->dma_size, return -EIO); + snd_assert(bpos + len <= pcmp->dma_size, return -EIO); + snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count); + if (snd_gf1_pcm_use_dma && len > 32) { + return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); + } else { + snd_gus_card_t *gus = pcmp->gus; + int err, w16, invert; + + w16 = (snd_pcm_format_width(runtime->format) == 16); + invert = snd_pcm_format_unsigned(runtime->format); + if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) + return err; + } + return 0; +} + +static int snd_gf1_pcm_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0) { /* change */ + snd_gf1_mem_block_t *block; + if (pcmp->memory > 0) { + snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory); + pcmp->memory = 0; + } + if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_DRIVER, + "GF1 PCM", + runtime->dma_bytes, 1, 32, + NULL)) == NULL) + return -ENOMEM; + pcmp->memory = block->ptr; + } + pcmp->voices = params_channels(hw_params); + if (pcmp->pvoices[0] == NULL) { + if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) + return -ENOMEM; + pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave; + pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume; + pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change; + pcmp->pvoices[0]->private_data = pcmp; + } + if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) { + if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) + return -ENOMEM; + pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave; + pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume; + pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change; + pcmp->pvoices[1]->private_data = pcmp; + } else if (pcmp->voices == 1) { + if (pcmp->pvoices[1]) { + snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); + pcmp->pvoices[1] = NULL; + } + } + return 0; +} + +static int snd_gf1_pcm_playback_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + + snd_pcm_lib_free_pages(substream); + if (pcmp->pvoices[0]) { + snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]); + pcmp->pvoices[0] = NULL; + } + if (pcmp->pvoices[1]) { + snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); + pcmp->pvoices[1] = NULL; + } + if (pcmp->memory > 0) { + snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory); + pcmp->memory = 0; + } + return 0; +} + +static int snd_gf1_pcm_playback_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + + pcmp->bpos = 0; + pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream); + pcmp->block_size = snd_pcm_lib_period_bytes(substream); + pcmp->blocks = pcmp->dma_size / pcmp->block_size; + return 0; +} + +static int snd_gf1_pcm_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + int voice; + + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_gf1_pcm_trigger_up(substream); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + spin_lock(&pcmp->lock); + pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; + spin_unlock(&pcmp->lock); + voice = pcmp->pvoices[0]->number; + snd_gf1_stop_voices(gus, voice, voice); + if (pcmp->pvoices[1]) { + voice = pcmp->pvoices[1]->number; + snd_gf1_stop_voices(gus, voice, voice); + } + } else { + return -EINVAL; + } + return 0; +} + +static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned int pos; + unsigned char voice_ctrl; + + pos = 0; + spin_lock(&gus->reg_lock); + if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { + snd_gf1_select_voice(gus, pcmp->pvoices[0]->number); + voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory; + if (substream->runtime->channels > 1) + pos <<= 1; + pos = bytes_to_frames(runtime, pos); + } + spin_unlock(&gus->reg_lock); + return pos; +} + +static ratnum_t clock = { + num: 9878400/16, + den_min: 2, + den_max: 257, + den_step: 1, +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + nrats: 1, + rats: &clock, +}; + +static int snd_gf1_pcm_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + + gus->c_dma_size = params_buffer_bytes(hw_params); + gus->c_period_size = params_period_bytes(hw_params); + gus->c_pos = 0; + gus->gf1.pcm_rcntrl_reg = 0x21; /* IRQ at end, enable & start */ + if (params_channels(hw_params) > 1) + gus->gf1.pcm_rcntrl_reg |= 2; + if (gus->gf1.dma2 > 3) + gus->gf1.pcm_rcntrl_reg |= 4; + if (snd_pcm_format_unsigned(params_format(hw_params))) + gus->gf1.pcm_rcntrl_reg |= 0x80; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_gf1_pcm_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_gf1_pcm_capture_prepare(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ + snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ + snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ); + return 0; +} + +static int snd_gf1_pcm_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + int val; + + if (cmd == SNDRV_PCM_TRIGGER_START) { + val = gus->gf1.pcm_rcntrl_reg; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + val = 0; + } else { + return -EINVAL; + } + + spin_lock(&gus->reg_lock); + snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val); + snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); + spin_unlock(&gus->reg_lock); + return 0; +} + +static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + int pos = gus->c_period_size - snd_dma_residue(gus->gf1.dma2); + pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size); + return pos; +} + +static void snd_gf1_pcm_interrupt_dma_read(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ + snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ + if (gus->pcm_cap_substream != NULL) { + snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream); + snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START); + gus->c_pos += gus->c_period_size; + snd_pcm_period_elapsed(gus->pcm_cap_substream); + } +} + +static snd_pcm_hardware_t snd_gf1_pcm_playback = +{ + info: SNDRV_PCM_INFO_NONINTERLEAVED, + formats: (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + rate_min: 5510, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_gf1_pcm_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + rate_min: 5510, + rate_max: 44100, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static void snd_gf1_pcm_playback_free(snd_pcm_runtime_t *runtime) +{ + gus_pcm_private_t * pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return); + snd_magic_kfree(pcmp); +} + +static int snd_gf1_pcm_playback_open(snd_pcm_substream_t *substream) +{ + gus_pcm_private_t *pcmp; + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + pcmp = snd_magic_kcalloc(gus_pcm_private_t, 0, GFP_KERNEL); + if (pcmp == NULL) + return -ENOMEM; + pcmp->gus = gus; + spin_lock_init(&pcmp->lock); + init_waitqueue_head(&pcmp->sleep); + atomic_set(&pcmp->dma_count, 0); + + runtime->private_data = pcmp; + runtime->private_free = snd_gf1_pcm_playback_free; + +#if 0 + printk("playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n", (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer); +#endif + if ((err = snd_gf1_dma_init(gus)) < 0) + return err; + pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE; + pcmp->substream = substream; + runtime->hw = snd_gf1_pcm_playback; + snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); + return 0; +} + +static int snd_gf1_pcm_playback_close(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned long jiffies_old; + + jiffies_old = jiffies; + while (atomic_read(&pcmp->dma_count) > 0) { + interruptible_sleep_on_timeout(&pcmp->sleep, 1); + if ((signed long)(jiffies - jiffies_old) > 2*HZ) { + snd_printk("gf1 pcm - serious DMA problem\n"); + break; + } + } + snd_gf1_dma_done(gus); + return 0; +} + +static int snd_gf1_pcm_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + + gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read; + gus->pcm_cap_substream = substream; + substream->runtime->hw = snd_gf1_pcm_capture; + snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + return 0; +} + +static int snd_gf1_pcm_capture_close(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + + gus->pcm_cap_substream = NULL; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ); + return 0; +} + +static void snd_gf1_pcm_free(snd_pcm_t *pcm) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, pcm->private_data, return); + gus->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int snd_gf1_pcm_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_gf1_pcm_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); + ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1; + ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1; + spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); + return 0; +} + +static int snd_gf1_pcm_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, idx; + unsigned short val1, val2, vol; + gus_pcm_private_t *pcmp; + snd_gus_voice_t *pvoice; + + val1 = ucontrol->value.integer.value[0] & 127; + val2 = ucontrol->value.integer.value[1] & 127; + spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); + change = val1 != gus->gf1.pcm_volume_level_left1 || + val2 != gus->gf1.pcm_volume_level_right1; + gus->gf1.pcm_volume_level_left1 = val1; + gus->gf1.pcm_volume_level_right1 = val2; + gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4; + gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4; + spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); + /* are we active? */ + spin_lock_irqsave(&gus->voice_alloc, flags); + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (!pvoice->pcm) + continue; + pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return -ENXIO); + if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) + continue; + /* load real volume - better precision */ + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, pvoice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); + pcmp->final_volume = 1; + spin_unlock_irqrestore(&gus->reg_lock, flags); + } + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return change; +} + +static snd_kcontrol_new_t snd_gf1_pcm_volume_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Playback Volume", + info: snd_gf1_pcm_volume_info, + get: snd_gf1_pcm_volume_get, + put: snd_gf1_pcm_volume_put +}; + +static snd_pcm_ops_t snd_gf1_pcm_playback_ops = { + open: snd_gf1_pcm_playback_open, + close: snd_gf1_pcm_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_gf1_pcm_playback_hw_params, + hw_free: snd_gf1_pcm_playback_hw_free, + prepare: snd_gf1_pcm_playback_prepare, + trigger: snd_gf1_pcm_playback_trigger, + pointer: snd_gf1_pcm_playback_pointer, + copy: snd_gf1_pcm_playback_copy, + silence: snd_gf1_pcm_playback_silence, +}; + +static snd_pcm_ops_t snd_gf1_pcm_capture_ops = { + open: snd_gf1_pcm_capture_open, + close: snd_gf1_pcm_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_gf1_pcm_capture_hw_params, + hw_free: snd_gf1_pcm_capture_hw_free, + prepare: snd_gf1_pcm_capture_prepare, + trigger: snd_gf1_pcm_capture_trigger, + pointer: snd_gf1_pcm_capture_pointer, +}; + +int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm) +{ + snd_card_t *card; + snd_kcontrol_t *kctl; + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + int capture, err; + + if (rpcm) + *rpcm = NULL; + card = gus->card; + capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0; + err = snd_pcm_new(card, + gus->interwave ? "AMD InterWave" : "GF1", + pcm_dev, + gus->gf1.pcm_channels / 2, + capture, + &pcm); + if (err < 0) + return err; + pcm->private_data = gus; + pcm->private_free = snd_gf1_pcm_free; + /* playback setup */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops); + + for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) + snd_pcm_lib_preallocate_isa_pages(substream, 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + if (capture) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops); + if (gus->gf1.dma2 == gus->gf1.dma1) + pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; + snd_pcm_lib_preallocate_isa_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); + } + strcpy(pcm->name, pcm->id); + if (gus->interwave) { + sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); + } + strcat(pcm->name, " (synth)"); + gus->pcm = pcm; + + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus))) < 0) + return err; + kctl->id.index = control_index; + + if (rpcm) + *rpcm = pcm; + return 0; +} + diff -Nru a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_reset.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,423 @@ +/* + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +extern void snd_gf1_timers_init(snd_gus_card_t * gus); +extern void snd_gf1_timers_done(snd_gus_card_t * gus); +extern int snd_gf1_synth_init(snd_gus_card_t * gus); +extern void snd_gf1_synth_done(snd_gus_card_t * gus); + +/* + * ok.. default interrupt handlers... + */ + +static void snd_gf1_default_interrupt_handler_midi_out(snd_gus_card_t * gus) +{ + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x20); +} + +static void snd_gf1_default_interrupt_handler_midi_in(snd_gus_card_t * gus) +{ + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x80); +} + +static void snd_gf1_default_interrupt_handler_timer1(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~4); +} + +static void snd_gf1_default_interrupt_handler_timer2(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~8); +} + +static void snd_gf1_default_interrupt_handler_wave_and_volume(snd_gus_card_t * gus, snd_gus_voice_t * voice) +{ + snd_gf1_i_ctrl_stop(gus, 0x00); + snd_gf1_i_ctrl_stop(gus, 0x0d); +} + +static void snd_gf1_default_interrupt_handler_dma_write(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, 0x41, 0x00); +} + +static void snd_gf1_default_interrupt_handler_dma_read(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, 0x49, 0x00); +} + +void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what) +{ + if (what & SNDRV_GF1_HANDLER_MIDI_OUT) + gus->gf1.interrupt_handler_midi_out = snd_gf1_default_interrupt_handler_midi_out; + if (what & SNDRV_GF1_HANDLER_MIDI_IN) + gus->gf1.interrupt_handler_midi_in = snd_gf1_default_interrupt_handler_midi_in; + if (what & SNDRV_GF1_HANDLER_TIMER1) + gus->gf1.interrupt_handler_timer1 = snd_gf1_default_interrupt_handler_timer1; + if (what & SNDRV_GF1_HANDLER_TIMER2) + gus->gf1.interrupt_handler_timer2 = snd_gf1_default_interrupt_handler_timer2; + if (what & SNDRV_GF1_HANDLER_VOICE) { + snd_gus_voice_t *voice; + + voice = &gus->gf1.voices[what & 0xffff]; + voice->handler_wave = + voice->handler_volume = snd_gf1_default_interrupt_handler_wave_and_volume; + voice->handler_effect = NULL; + voice->volume_change = NULL; + } + if (what & SNDRV_GF1_HANDLER_DMA_WRITE) + gus->gf1.interrupt_handler_dma_write = snd_gf1_default_interrupt_handler_dma_write; + if (what & SNDRV_GF1_HANDLER_DMA_READ) + gus->gf1.interrupt_handler_dma_read = snd_gf1_default_interrupt_handler_dma_read; +} + +/* + + */ + +static void snd_gf1_clear_regs(snd_gus_card_t * gus) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + inb(GUSP(gus, IRQSTAT)); + snd_gf1_write8(gus, 0x41, 0); /* DRAM DMA Control Register */ + snd_gf1_write8(gus, 0x45, 0); /* Timer Control */ + snd_gf1_write8(gus, 0x49, 0); /* Sampling Control Register */ + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static void snd_gf1_look_regs(snd_gus_card_t * gus) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_look8(gus, 0x41); /* DRAM DMA Control Register */ + snd_gf1_look8(gus, 0x49); /* Sampling Control Register */ + inb(GUSP(gus, IRQSTAT)); + snd_gf1_read8(gus, 0x0f); /* IRQ Source Register */ + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +/* + * put selected GF1 voices to initial stage... + */ + +void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice); +#if 0 + printk(" -%i- smart stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME)); +#endif + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice); +#if 0 + printk(" -%i- stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME)); +#endif + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0); + spin_unlock_irqrestore(&gus->reg_lock, flags); +#if 0 + snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_VIBRATO); + snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_TREMOLO); +#endif +} + +void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max) +{ + unsigned long flags; + unsigned int daddr; + unsigned short i, w_16; + + daddr = gus->gf1.default_voice_address << 4; + for (i = v_min; i <= v_max; i++) { +#if 0 + if (gus->gf1.syn_voices) + gus->gf1.syn_voices[i].flags = ~VFLG_DYNAMIC; +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, i); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); /* Voice Control Register = voice stop */ + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); /* Volume Ramp Control Register = ramp off */ + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, gus->gf1.memory ? 0x02 : 0x82); /* Deactivate voice */ + w_16 = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & 0x04; + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, 0x400); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, daddr, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, daddr, w_16); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, 0); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, 0); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, daddr, w_16); + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, 7); + if (gus->gf1.enh_mode) { + snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, 0); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, 0); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); +#if 0 + snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_VIBRATO); + snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_TREMOLO); +#endif + } +} + +void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max) +{ + unsigned long flags; + short i, ramp_ok; + unsigned short ramp_end; + long time; + + if (!in_interrupt()) { /* this can't be done in interrupt */ + for (i = v_min, ramp_ok = 0; i <= v_max; i++) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, i); + ramp_end = snd_gf1_read16(gus, 9) >> 8; + if (ramp_end > SNDRV_GF1_MIN_OFFSET) { + ramp_ok++; + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 20); /* ramp rate */ + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); /* ramp start */ + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, ramp_end); /* ramp end */ + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); /* ramp down */ + if (gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); + } + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + } + time = HZ / 20; + while (time > 0 && !signal_pending(current)) { + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + } + } + snd_gf1_clear_voices(gus, v_min, v_max); +} + +static void snd_gf1_alloc_voice_use(snd_gus_card_t * gus, + snd_gus_voice_t * pvoice, + int type, int client, int port) +{ + pvoice->use = 1; + switch (type) { + case SNDRV_GF1_VOICE_TYPE_PCM: + gus->gf1.pcm_alloc_voices++; + pvoice->pcm = 1; + break; + case SNDRV_GF1_VOICE_TYPE_SYNTH: + pvoice->synth = 1; + pvoice->client = client; + pvoice->port = port; + break; + case SNDRV_GF1_VOICE_TYPE_MIDI: + pvoice->midi = 1; + pvoice->client = client; + pvoice->port = port; + break; + } +} + +snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port) +{ + snd_gus_voice_t *pvoice; + unsigned long flags; + int idx; + + spin_lock_irqsave(&gus->voice_alloc, flags); + if (type == SNDRV_GF1_VOICE_TYPE_PCM) { + if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels) { + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return NULL; + } + } + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (!pvoice->use) { + snd_gf1_alloc_voice_use(gus, pvoice, type, client, port); + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return pvoice; + } + } + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (pvoice->midi && !pvoice->client) { + snd_gf1_clear_voices(gus, pvoice->number, pvoice->number); + snd_gf1_alloc_voice_use(gus, pvoice, type, client, port); + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return pvoice; + } + } + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return NULL; +} + +void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice) +{ + unsigned long flags; + void (*private_free)(snd_gus_voice_t *voice); + void *private_data; + + if (voice == NULL || !voice->use) + return; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | voice->number); + snd_gf1_clear_voices(gus, voice->number, voice->number); + spin_lock_irqsave(&gus->voice_alloc, flags); + private_free = voice->private_free; + private_data = voice->private_data; + voice->private_free = NULL; + voice->private_data = NULL; + if (voice->pcm) + gus->gf1.pcm_alloc_voices--; + voice->use = voice->pcm = 0; + voice->sample_ops = NULL; + spin_unlock_irqrestore(&gus->voice_alloc, flags); + if (private_free) + private_free(voice); +} + +/* + * call this function only by start of driver + */ + +int snd_gf1_start(snd_gus_card_t * gus) +{ + unsigned long flags; + unsigned int i; + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); + + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); + for (i = 0; i < 32; i++) { + gus->gf1.voices[i].number = i; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); + } + + snd_gf1_uart_cmd(gus, 0x03); /* huh.. this cleanup took me some time... */ + + if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + } + snd_gf1_clear_regs(gus); + snd_gf1_select_active_voices(gus); + snd_gf1_delay(gus); + gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8; + /* initialize LFOs & clear LFOs memory */ + if (gus->gf1.enh_mode && gus->gf1.memory) { + gus->gf1.hw_lfo = 1; + gus->gf1.default_voice_address += 1024; + } else { + gus->gf1.sw_lfo = 1; + } +#if 0 + snd_gf1_lfo_init(gus); +#endif + if (gus->gf1.memory > 0) + for (i = 0; i < 4; i++) + snd_gf1_poke(gus, gus->gf1.default_voice_address + i, 0); + snd_gf1_clear_regs(gus); + snd_gf1_clear_voices(gus, 0, 31); + snd_gf1_look_regs(gus); + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */ + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */ + if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + } + while ((snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ) & 0xc0) != 0xc0); + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); + outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + + snd_gf1_timers_init(gus); + snd_gf1_look_regs(gus); + snd_gf1_mem_init(gus); + snd_gf1_mem_proc_init(gus); +#ifdef CONFIG_SND_DEBUG + snd_gus_irq_profile_init(gus); +#endif + +#if 0 + if (gus->pnp_flag) { + if (gus->chip.playback_fifo_size > 0) + snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR, gus->chip.playback_fifo_block->ptr >> 8); + if (gus->chip.record_fifo_size > 0) + snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR, gus->chip.record_fifo_block->ptr >> 8); + snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_SIZE, gus->chip.interwave_fifo_reg); + } +#endif + + return 0; +} + +/* + * call this function only by shutdown of driver + */ + +int snd_gf1_stop(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); /* stop all timers */ + snd_gf1_stop_voices(gus, 0, 31); /* stop all voices */ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ + snd_gf1_timers_done(gus); +#ifdef CONFIG_SND_DEBUG + snd_gus_irq_profile_done(gus); +#endif + snd_gf1_mem_proc_done(gus); + snd_gf1_mem_done(gus); +#if 0 + snd_gf1_lfo_done(gus); +#endif + return 0; +} diff -Nru a/sound/isa/gus/gus_sample.c b/sound/isa/gus/gus_sample.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_sample.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,156 @@ +/* + * Routines for Gravis UltraSound soundcards - Sample support + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +/* + * + */ + +static void select_instrument(snd_gus_card_t * gus, snd_gus_voice_t * v) +{ + snd_seq_kinstr_t *instr; + +#if 0 + printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n", + v->instr.cluster, + v->instr.std, + v->instr.bank, + v->instr.prg); +#endif + instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1); + if (instr != NULL) { + if (instr->ops) { + if (instr->ops->instr_type == snd_seq_simple_id) + snd_gf1_simple_init(v); + } + snd_seq_instr_free_use(gus->gf1.ilist, instr); + } +} + +/* + * + */ + +static void event_sample(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.std = ev->data.sample.param.sample.std; + if (v->instr.std & 0xff000000) { /* private instrument */ + v->instr.std &= 0x00ffffff; + v->instr.std |= (unsigned int)ev->source.client << 24; + } + v->instr.bank = ev->data.sample.param.sample.bank; + v->instr.prg = ev->data.sample.param.sample.prg; + select_instrument(p->gus, v); +} + +static void event_cluster(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.cluster = ev->data.sample.param.cluster.cluster; + select_instrument(p->gus, v); +} + +static void event_start(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_start) + v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position); +} + +static void event_stop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode); +} + +static void event_freq(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_freq) + v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency); +} + +static void event_volume(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_volume) + v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume); +} + +static void event_loop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_loop) + v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop); +} + +static void event_position(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_pos) + v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position); +} + +static void event_private1(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_private1) + v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8); +} + +typedef void (gus_sample_event_handler_t)(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v); + +static gus_sample_event_handler_t *gus_sample_event_handlers[9] = { + event_sample, + event_cluster, + event_start, + event_stop, + event_freq, + event_volume, + event_loop, + event_position, + event_private1 +}; + +void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p) +{ + int idx, voice; + snd_gus_card_t *gus = p->gus; + snd_gus_voice_t *v; + unsigned long flags; + + idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; + if (idx < 0 || idx > 8) + return; + for (voice = 0; voice < 32; voice++) { + v = &gus->gf1.voices[voice]; + if (v->use && v->client == ev->source.client && + v->port == ev->source.port && + v->index == ev->data.sample.channel) { + spin_lock_irqsave(&gus->event_lock, flags); + gus_sample_event_handlers[idx](ev, p, v); + spin_unlock_irqrestore(&gus->event_lock, flags); + return; + } + } +} diff -Nru a/sound/isa/gus/gus_simple.c b/sound/isa/gus/gus_simple.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_simple.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,635 @@ +/* + * Routines for Gravis UltraSound soundcards - Simple instrument handlers + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "gus_tables.h" + +/* + * + */ + +static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice); + +static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position); +static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode); +static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq); +static void sample_volume(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume); +static void sample_loop(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop); +static void sample_pos(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position); +static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data); + +static snd_gus_sample_ops_t sample_ops = { + sample_start, + sample_stop, + sample_freq, + sample_volume, + sample_loop, + sample_pos, + sample_private1 +}; + +#if 0 + +static void note_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, int wait); +static void note_wait(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void note_off(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void note_volume(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void note_pitchbend(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void note_vibrato(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void note_tremolo(snd_gus_card_t *card, snd_gus_voice_t *voice); + +static struct snd_gus_note_handlers note_commands = { + note_stop, + note_wait, + note_off, + note_volume, + note_pitchbend, + note_vibrato, + note_tremolo +}; + +static void chn_trigger_down(snd_gus_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ); +static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ); +static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ); + +static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = { + chn_trigger_down, + chn_trigger_up, + chn_control +}; + +#endif + +static void do_volume_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void do_pan_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice); + +/* + * + */ + +static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + spin_lock(&gus->event_lock); + snd_gf1_stop_voice(gus, voice->number); + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); + spin_unlock(&gus->reg_lock); + voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + spin_unlock(&gus->event_lock); +} + +static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + spin_lock(&gus->event_lock); + if (voice->flags & SNDRV_GF1_VFLG_RUNNING) + do_volume_envelope(gus, voice); + else + snd_gf1_stop_voice(gus, voice->number); + spin_unlock(&gus->event_lock); +} + +static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + spin_lock(&gus->event_lock); + if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) == + (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) + do_pan_envelope(gus, voice); + spin_unlock(&gus->event_lock); +} + +/* + * + */ + +static void do_volume_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + unsigned short next, rate, old_volume; + int program_next_ramp; + unsigned long flags; + + if (!gus->gf1.volume_ramp) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume); + printk("gf1_volume = 0x%x\n", voice->gf1_volume); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + } + program_next_ramp = 0; + rate = next = 0; + while (1) { + program_next_ramp = 0; + rate = next = 0; + switch (voice->venv_state) { + case VENV_BEFORE: + voice->venv_state = VENV_ATTACK; + voice->venv_value_next = 0; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); + spin_unlock_irqrestore(&gus->reg_lock, flags); + break; + case VENV_ATTACK: + voice->venv_state = VENV_SUSTAIN; + program_next_ramp++; + next = 255; + rate = gus->gf1.volume_ramp; + break; + case VENV_SUSTAIN: + voice->venv_state = VENV_RELEASE; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + case VENV_RELEASE: + voice->venv_state = VENV_DONE; + program_next_ramp++; + next = 0; + rate = gus->gf1.volume_ramp; + break; + case VENV_DONE: + snd_gf1_stop_voice(gus, voice->number); + voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + return; + case VENV_VOLUME: + program_next_ramp++; + next = voice->venv_value_next; + rate = gus->gf1.volume_ramp; + voice->venv_state = voice->venv_state_prev; + break; + } + voice->venv_value_next = next; + if (!program_next_ramp) + continue; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8; + if (!rate) { + spin_unlock_irqrestore(&gus->reg_lock, flags); + continue; + } + next = (((int)voice->gf1_volume * (int)next) / 255) >> 8; + if (old_volume < SNDRV_GF1_MIN_OFFSET) + old_volume = SNDRV_GF1_MIN_OFFSET; + if (next < SNDRV_GF1_MIN_OFFSET) + next = SNDRV_GF1_MIN_OFFSET; + if (next > SNDRV_GF1_MAX_OFFSET) + next = SNDRV_GF1_MAX_OFFSET; + if (old_volume == next) { + spin_unlock_irqrestore(&gus->reg_lock, flags); + continue; + } + voice->volume_control &= ~0xc3; + voice->volume_control |= 0x20; + if (old_volume > next) { + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume); + voice->volume_control |= 0x40; + } else { + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next); + } + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + } +} + +static void do_pan_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + unsigned long flags; + unsigned char old_pan; + +#if 0 + snd_gf1_select_voice(gus, voice->number); + printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", + voice->number, + voice->flags, + voice->gf1_pan, + snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f); +#endif + if (gus->gf1.enh_mode) { + voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); + return; + } + if (!gus->gf1.smooth_pan) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + } + if (!(voice->flags & SNDRV_GF1_VFLG_PAN)) /* before */ + voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f; + if (old_pan > voice->gf1_pan ) + old_pan--; + if (old_pan < voice->gf1_pan) + old_pan++; + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan); + spin_unlock_irqrestore(&gus->reg_lock, flags); + if (old_pan == voice->gf1_pan) /* the goal was reached */ + voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); +#if 0 + snd_gf1_select_voice(gus, voice->number); + printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", + voice->number, + voice->flags, + voice->gf1_pan, + snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f); +#endif +} + +static void set_enhanced_pan(snd_gus_card_t *gus, snd_gus_voice_t *voice, unsigned short pan) +{ + unsigned long flags; + unsigned short vlo, vro; + + vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan); + vro = SNDRV_GF1_ATTEN(pan); + if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) { + vlo >>= 1; + vro >>= 1; + } + vlo <<= 4; + vro <<= 4; +#if 0 + printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n", + vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT), + vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT)); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro); + spin_unlock_irqrestore(&gus->reg_lock, flags); + voice->vlo = vlo; + voice->vro = vro; +} + +/* + * + */ + +static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position) +{ + unsigned long flags; + unsigned int begin, addr, addr_end, addr_start; + int w_16; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + + instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + begin = simple->address.memory << 4; + w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0; + addr_start = simple->loop_start; + if (simple->format & SIMPLE_WAVE_LOOP) { + addr_end = simple->loop_end; + } else { + addr_end = (simple->size << 4) - (w_16 ? 40 : 24); + } + if (simple->format & SIMPLE_WAVE_BACKWARD) { + addr = simple->loop_end; + if (position < simple->loop_end) + addr -= position; + } else { + addr = position; + } + voice->control = 0x00; + voice->mode = 0x20; /* enable offset registers */ + if (simple->format & SIMPLE_WAVE_16BIT) + voice->control |= 0x04; + if (simple->format & SIMPLE_WAVE_BACKWARD) + voice->control |= 0x40; + if (simple->format & SIMPLE_WAVE_LOOP) { + voice->control |= 0x08; + } else { + voice->control |= 0x20; + } + if (simple->format & SIMPLE_WAVE_BIDIR) + voice->control |= 0x10; + if (simple->format & SIMPLE_WAVE_ULAW) + voice->mode |= 0x40; + if (w_16) { + addr = ((addr << 1) & ~0x1f) | (addr & 0x0f); + addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f); + addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f); + } + addr += begin; + addr_start += begin; + addr_end += begin; + snd_gf1_stop_voice(gus, voice->number); + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); + voice->venv_state = VENV_BEFORE; + voice->volume_control = 0x03; + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); + if (!gus->gf1.enh_mode) { + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); + } else { + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro); + snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + do_volume_envelope(gus, voice); + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control); + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control ); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); +#if 0 + snd_gf1_print_voice_registers(gus); +#endif + voice->flags |= SNDRV_GF1_VFLG_RUNNING; + snd_seq_instr_free_use(gus->gf1.ilist, instr); +} + +static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode) +{ + unsigned char control; + unsigned long flags; + + if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING)) + return; + switch (mode) { + default: + if (gus->gf1.volume_ramp > 0) { + if (voice->venv_state < VENV_RELEASE) { + voice->venv_state = VENV_RELEASE; + do_volume_envelope(gus, voice); + } + } + if (mode != SAMPLE_STOP_VENVELOPE) { + snd_gf1_stop_voice(gus, voice->number); + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); + spin_unlock_irqrestore(&gus->reg_lock, flags); + voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + } + break; + case SAMPLE_STOP_LOOP: /* disable loop only */ + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + control &= ~(0x83 | 0x04); + control |= 0x20; + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control); + spin_unlock_irqrestore(&gus->reg_lock, flags); + break; + } +} + +static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + voice->fc_register = snd_gf1_translate_freq(gus, freq); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static void sample_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume) +{ + if (volume->volume >= 0) { + volume->volume &= 0x3fff; + voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4; + voice->venv_state_prev = VENV_SUSTAIN; + voice->venv_state = VENV_VOLUME; + do_volume_envelope(gus, voice); + } + if (volume->lr >= 0) { + volume->lr &= 0x3fff; + if (!gus->gf1.enh_mode) { + voice->gf1_pan = (volume->lr >> 10) & 15; + if (!gus->gf1.full_range_pan) { + if (voice->gf1_pan == 0) + voice->gf1_pan++; + if (voice->gf1_pan == 15) + voice->gf1_pan--; + } + voice->flags &= ~SNDRV_GF1_VFLG_PAN; /* before */ + do_pan_envelope(gus, voice); + } else { + set_enhanced_pan(gus, voice, volume->lr >> 7); + } + } +} + +static void sample_loop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop) +{ + unsigned long flags; + int w_16 = voice->control & 0x04; + unsigned int begin, addr_start, addr_end; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + +#if 0 + printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); +#endif + instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + begin = simple->address.memory; + addr_start = loop->start; + addr_end = loop->end; + addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin; + addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_seq_instr_free_use(gus->gf1.ilist, instr); +} + +static void sample_pos(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position) +{ + unsigned long flags; + int w_16 = voice->control & 0x04; + unsigned int begin, addr; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + +#if 0 + printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); +#endif + instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + begin = simple->address.memory; + addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_seq_instr_free_use(gus->gf1.ilist, instr); +} + +#if 0 + +static unsigned char get_effects_mask( ultra_card_t *card, int value ) +{ + if ( value > 7 ) return 0; + if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE ) + return card -> gf1.effects -> chip.interwave.voice_output[ value ]; + return 0; +} + +#endif + +static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data) +{ +#if 0 + unsigned long flags; + unsigned char uc; + + switch ( *data ) { + case ULTRA_PRIV1_IW_EFFECT: + uc = get_effects_mask( card, ultra_get_byte( data, 4 ) ); + uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 ); + uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) ); + uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 ); + voice -> data.simple.effect_accumulator = uc; + voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4; + if ( !card -> gf1.enh_mode ) return; + if ( voice -> flags & VFLG_WAIT_FOR_START ) return; + if ( voice -> flags & VFLG_RUNNING ) + { + CLI( &flags ); + gf1_select_voice( card, voice -> number ); + ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator ); + ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume ); + STI( &flags ); + } + break; + case ULTRA_PRIV1_IW_LFO: + ultra_lfo_command( card, voice -> number, data ); + } +#endif +} + +#if 0 + +/* + * + */ + +static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait ) +{ +} + +static void note_wait( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_off( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_volume( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +/* + * + */ + +static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ) +{ +} + +static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ) +{ +} + +static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ) +{ +} + +/* + * + */ + +#endif + +void snd_gf1_simple_init(snd_gus_voice_t *voice) +{ + voice->handler_wave = interrupt_wave; + voice->handler_volume = interrupt_volume; + voice->handler_effect = interrupt_effect; + voice->volume_change = NULL; + voice->sample_ops = &sample_ops; +} diff -Nru a/sound/isa/gus/gus_synth.c b/sound/isa/gus/gus_synth.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_synth.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,324 @@ +/* + * Routines for Gravis UltraSound soundcards - Synthesizer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards - Synthesizer"); +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; + +/* + * + */ + +static void snd_gus_synth_free_voices(snd_gus_card_t * gus, int client, int port) +{ + int idx; + snd_gus_voice_t * voice; + + for (idx = 0; idx < 32; idx++) { + voice = &gus->gf1.voices[idx]; + if (voice->use && voice->client == client && voice->port == port) + snd_gf1_free_voice(gus, voice); + } +} + +static int snd_gus_synth_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_gus_port_t * port = (snd_gus_port_t *)private_data; + snd_gus_card_t * gus = port->gus; + snd_gus_voice_t * voice; + int idx; + + if (info->voices > 32) + return -EINVAL; + down(&gus->register_mutex); + if (!snd_gus_use_inc(gus)) { + up(&gus->register_mutex); + return -EFAULT; + } + for (idx = 0; idx < info->voices; idx++) { + voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); + if (voice == NULL) { + snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); + up(&gus->register_mutex); + return -EBUSY; + } + voice->index = idx; + } + up(&gus->register_mutex); + return 0; +} + +static int snd_gus_synth_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_gus_port_t * port = (snd_gus_port_t *)private_data; + snd_gus_card_t * gus = port->gus; + + down(&gus->register_mutex); + snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); + up(&gus->register_mutex); + return 0; +} + +/* + * + */ + +static void snd_gus_synth_free_private_instruments(snd_gus_port_t *p, int client) +{ + snd_seq_instr_header_t ifree; + + memset(&ifree, 0, sizeof(ifree)); + ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; + snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0); +} + +int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop) +{ + snd_gus_port_t * p = (snd_gus_port_t *) private_data; + + snd_assert(p != NULL, return -EINVAL); + if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && + ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { + snd_gus_sample_event(ev, p); + return 0; + } + if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && + ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { + if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { + snd_gus_synth_free_private_instruments(p, ev->data.addr.client); + return 0; + } + } + if (direct) { + if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { + snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops, + p->gus->gf1.ilist, + ev, + p->gus->gf1.seq_client, + atomic, hop); + return 0; + } + } + return 0; +} + +static void snd_gus_synth_instr_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + int idx; + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return); + snd_gus_voice_t *pvoice; + unsigned long flags; + + spin_lock_irqsave(&gus->event_lock, flags); + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { + if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { + pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY); + } else { + snd_gf1_stop_voice(gus, pvoice->number); + pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + } + } + } + spin_unlock_irqrestore(&gus->event_lock, flags); +} + +/* + * + */ + +static void snd_gus_synth_free_port(void *private_data) +{ + snd_gus_port_t * p = (snd_gus_port_t *)private_data; + + if (p) + snd_midi_channel_free_set(p->chset); +} + +static int snd_gus_synth_create_port(snd_gus_card_t * gus, int idx) +{ + snd_gus_port_t * p; + snd_seq_port_callback_t callbacks; + char name[32]; + int result; + + p = &gus->gf1.seq_ports[idx]; + p->chset = snd_midi_channel_alloc_set(16); + if (p->chset == NULL) + return -ENOMEM; + p->chset->private_data = p; + p->gus = gus; + p->client = gus->gf1.seq_client; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.use = snd_gus_synth_use; + callbacks.unuse = snd_gus_synth_unuse; + callbacks.event_input = snd_gus_synth_event_input; + callbacks.private_free = snd_gus_synth_free_port; + callbacks.private_data = p; + + sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx); + p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client, + &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_MIDI_GS | + SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | + SNDRV_SEQ_PORT_TYPE_SYNTH, + name); + if (p->chset->port < 0) { + result = p->chset->port; + snd_gus_synth_free_port(p); + return result; + } + p->port = p->chset->port; + return 0; +} + +/* + * + */ + +static int snd_gus_synth_new_device(snd_seq_device_t *dev) +{ + snd_gus_card_t *gus; + int client, i; + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + snd_seq_port_subscribe_t sub; + snd_iwffff_ops_t *iwops; + snd_gf1_ops_t *gf1ops; + snd_simple_ops_t *simpleops; + + gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (gus == NULL) + return -EINVAL; + + init_MUTEX(&gus->register_mutex); + gus->gf1.seq_client = -1; + + /* allocate new client */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = gus; + callbacks.allow_output = callbacks.allow_input = 1; + client = gus->gf1.seq_client = + snd_seq_create_kernel_client(gus->card, 1, &callbacks); + if (client < 0) + return client; + + /* change name of client */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + sprintf(cinfo.name, gus->interwave ? "AMD InterWave" : "GF1"); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + for (i = 0; i < 4; i++) + snd_gus_synth_create_port(gus, i); + + gus->gf1.ilist = snd_seq_instr_list_new(); + if (gus->gf1.ilist == NULL) { + snd_seq_delete_kernel_client(client); + gus->gf1.seq_client = -1; + return -ENOMEM; + } + gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; + + simpleops = &gus->gf1.simple_ops; + snd_seq_simple_init(simpleops, gus, NULL); + simpleops->put_sample = snd_gus_simple_put_sample; + simpleops->get_sample = snd_gus_simple_get_sample; + simpleops->remove_sample = snd_gus_simple_remove_sample; + simpleops->notify = snd_gus_synth_instr_notify; + + gf1ops = &gus->gf1.gf1_ops; + snd_seq_gf1_init(gf1ops, gus, &simpleops->kops); + gf1ops->put_sample = snd_gus_gf1_put_sample; + gf1ops->get_sample = snd_gus_gf1_get_sample; + gf1ops->remove_sample = snd_gus_gf1_remove_sample; + gf1ops->notify = snd_gus_synth_instr_notify; + + iwops = &gus->gf1.iwffff_ops; + snd_seq_iwffff_init(iwops, gus, &gf1ops->kops); + iwops->put_sample = snd_gus_iwffff_put_sample; + iwops->get_sample = snd_gus_iwffff_get_sample; + iwops->remove_sample = snd_gus_iwffff_remove_sample; + iwops->notify = snd_gus_synth_instr_notify; + + memset(&sub, 0, sizeof(sub)); + sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; + sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + sub.dest.client = client; + sub.dest.port = 0; + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); + + return 0; +} + +static int snd_gus_synth_delete_device(snd_seq_device_t *dev) +{ + snd_gus_card_t *gus; + + gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (gus == NULL) + return -EINVAL; + + if (gus->gf1.seq_client >= 0) { + snd_seq_delete_kernel_client(gus->gf1.seq_client); + gus->gf1.seq_client = -1; + } + if (gus->gf1.ilist) + snd_seq_instr_list_free(&gus->gf1.ilist); + return 0; +} + +static int __init alsa_gus_synth_init(void) +{ + static snd_seq_dev_ops_t ops = { + snd_gus_synth_new_device, + snd_gus_synth_delete_device + }; + + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops, + sizeof(snd_gus_card_t*)); +} + +static void __exit alsa_gus_synth_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS); +} + +module_init(alsa_gus_synth_init) +module_exit(alsa_gus_synth_exit) diff -Nru a/sound/isa/gus/gus_tables.h b/sound/isa/gus/gus_tables.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_tables.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,86 @@ +/* + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_GF1_SCALE_TABLE_SIZE 128 +#define SNDRV_GF1_ATTEN_TABLE_SIZE 128 + +#ifdef __GUS_TABLES_ALLOC__ + +unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] = +{ + 8372, 8870, 9397, 9956, 10548, 11175, + 11840, 12544, 13290, 14080, 14917, 15804, + 16744, 17740, 18795, 19912, 21096, 22351, + 23680, 25088, 26580, 28160, 29834, 31609, + 33488, 35479, 37589, 39824, 42192, 44701, + 47359, 50175, 53159, 56320, 59669, 63217, + 66976, 70959, 75178, 79649, 84385, 89402, + 94719, 100351, 106318, 112640, 119338, 126434, + 133952, 141918, 150356, 159297, 168769, 178805, + 189437, 200702, 212636, 225280, 238676, 252868, + 267905, 283835, 300713, 318594, 337539, 357610, + 378874, 401403, 425272, 450560, 477352, 505737, + 535809, 567670, 601425, 637188, 675077, 715219, + 757749, 802807, 850544, 901120, 954703, 1011473, + 1071618, 1135340, 1202851, 1274376, 1350154, 1430439, + 1515497, 1605613, 1701088, 1802240, 1909407, 2022946, + 2143237, 2270680, 2405702, 2548752, 2700309, 2860878, + 3030994, 3211227, 3402176, 3604480, 3818814, 4045892, + 4286473, 4541360, 4811404, 5097505, 5400618, 5721755, + 6061989, 6422453, 6804352, 7208960, 7637627, 8091784, + 8572947, 9082720, 9622807, 10195009, 10801236, 11443511, + 12123977, 12844906 +}; + +unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE] = { + 4095 /* 0 */,1789 /* 1 */,1533 /* 2 */,1383 /* 3 */,1277 /* 4 */, + 1195 /* 5 */,1127 /* 6 */,1070 /* 7 */,1021 /* 8 */,978 /* 9 */, + 939 /* 10 */,903 /* 11 */,871 /* 12 */,842 /* 13 */,814 /* 14 */, + 789 /* 15 */,765 /* 16 */,743 /* 17 */,722 /* 18 */,702 /* 19 */, + 683 /* 20 */,665 /* 21 */,647 /* 22 */,631 /* 23 */,615 /* 24 */, + 600 /* 25 */,586 /* 26 */,572 /* 27 */,558 /* 28 */,545 /* 29 */, + 533 /* 30 */,521 /* 31 */,509 /* 32 */,498 /* 33 */,487 /* 34 */, + 476 /* 35 */,466 /* 36 */,455 /* 37 */,446 /* 38 */,436 /* 39 */, + 427 /* 40 */,418 /* 41 */,409 /* 42 */,400 /* 43 */,391 /* 44 */, + 383 /* 45 */,375 /* 46 */,367 /* 47 */,359 /* 48 */,352 /* 49 */, + 344 /* 50 */,337 /* 51 */,330 /* 52 */,323 /* 53 */,316 /* 54 */, + 309 /* 55 */,302 /* 56 */,296 /* 57 */,289 /* 58 */,283 /* 59 */, + 277 /* 60 */,271 /* 61 */,265 /* 62 */,259 /* 63 */,253 /* 64 */, + 247 /* 65 */,242 /* 66 */,236 /* 67 */,231 /* 68 */,225 /* 69 */, + 220 /* 70 */,215 /* 71 */,210 /* 72 */,205 /* 73 */,199 /* 74 */, + 195 /* 75 */,190 /* 76 */,185 /* 77 */,180 /* 78 */,175 /* 79 */, + 171 /* 80 */,166 /* 81 */,162 /* 82 */,157 /* 83 */,153 /* 84 */, + 148 /* 85 */,144 /* 86 */,140 /* 87 */,135 /* 88 */,131 /* 89 */, + 127 /* 90 */,123 /* 91 */,119 /* 92 */,115 /* 93 */,111 /* 94 */, + 107 /* 95 */,103 /* 96 */,100 /* 97 */,96 /* 98 */,92 /* 99 */, + 88 /* 100 */,85 /* 101 */,81 /* 102 */,77 /* 103 */,74 /* 104 */, + 70 /* 105 */,67 /* 106 */,63 /* 107 */,60 /* 108 */,56 /* 109 */, + 53 /* 110 */,50 /* 111 */,46 /* 112 */,43 /* 113 */,40 /* 114 */, + 37 /* 115 */,33 /* 116 */,30 /* 117 */,27 /* 118 */,24 /* 119 */, + 21 /* 120 */,18 /* 121 */,15 /* 122 */,12 /* 123 */,9 /* 124 */, + 6 /* 125 */,3 /* 126 */,0 /* 127 */, +}; + +#else + +extern unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE]; +extern unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE]; + +#endif diff -Nru a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_timer.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,207 @@ +/* + * Routines for Gravis UltraSound soundcards - Timers + * Copyright (c) by Jaroslav Kysela + * + * GUS have similar timers as AdLib (OPL2/OPL3 chips). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +#define chip_t snd_gus_card_t + +/* + * Timer 1 - 80us + */ + +static int snd_gf1_timer1_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + ticks = timer->sticks; + tmp = (gus->gf1.timer_enabled |= 4); + snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks); /* timer 1 count */ + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 1 IRQ */ + snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +static int snd_gf1_timer1_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + tmp = (gus->gf1.timer_enabled &= ~4); + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +/* + * Timer 2 - 320us + */ + +static int snd_gf1_timer2_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + ticks = timer->sticks; + tmp = (gus->gf1.timer_enabled |= 8); + snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks); /* timer 2 count */ + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 2 IRQ */ + snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +static int snd_gf1_timer2_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + tmp = (gus->gf1.timer_enabled &= ~8); + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +/* + + */ + +static void snd_gf1_interrupt_timer1(snd_gus_card_t * gus) +{ + snd_timer_t *timer = gus->gf1.timer1; + + if (timer == NULL) + return; + snd_timer_interrupt(timer, timer->sticks); +} + +static void snd_gf1_interrupt_timer2(snd_gus_card_t * gus) +{ + snd_timer_t *timer = gus->gf1.timer2; + + if (timer == NULL) + return; + snd_timer_interrupt(timer, timer->sticks); +} + +/* + + */ + +static struct _snd_timer_hardware snd_gf1_timer1 = +{ + flags: SNDRV_TIMER_HW_STOP, + resolution: 80000, + ticks: 256, + start: snd_gf1_timer1_start, + stop: snd_gf1_timer1_stop, +}; + +static struct _snd_timer_hardware snd_gf1_timer2 = +{ + flags: SNDRV_TIMER_HW_STOP, + resolution: 320000, + ticks: 256, + start: snd_gf1_timer2_start, + stop: snd_gf1_timer2_stop, +}; + +static void snd_gf1_timer1_free(snd_timer_t *timer) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return); + gus->gf1.timer1 = NULL; +} + +static void snd_gf1_timer2_free(snd_timer_t *timer) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return); + gus->gf1.timer2 = NULL; +} + +void snd_gf1_timers_init(snd_gus_card_t * gus) +{ + snd_timer_t *timer; + snd_timer_id_t tid; + + if (gus->gf1.timer1 != NULL || gus->gf1.timer2 != NULL) + return; + + gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1; + gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = gus->card->number; + tid.device = gus->timer_dev; + tid.subdevice = 0; + + if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { + strcpy(timer->name, "GF1 timer #1"); + timer->private_data = gus; + timer->private_free = snd_gf1_timer1_free; + timer->hw = snd_gf1_timer1; + } + gus->gf1.timer1 = timer; + + tid.device++; + + if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { + strcpy(timer->name, "GF1 timer #2"); + timer->private_data = gus; + timer->private_free = snd_gf1_timer2_free; + timer->hw = snd_gf1_timer2; + } + gus->gf1.timer2 = timer; +} + +void snd_gf1_timers_done(snd_gus_card_t * gus) +{ + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_TIMER1 | SNDRV_GF1_HANDLER_TIMER2); + if (gus->gf1.timer1) { + snd_device_free(gus->card, gus->gf1.timer1); + gus->gf1.timer1 = NULL; + } + if (gus->gf1.timer2) { + snd_device_free(gus->card, gus->gf1.timer2); + gus->gf1.timer2 = NULL; + } +} diff -Nru a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_uart.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,260 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for the GF1 MIDI interface - like UART 6850 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus) +{ + int count; + unsigned char stat, data, byte; + unsigned long flags; + + count = 10; + while (count) { + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + stat = snd_gf1_uart_stat(gus); + if (!(stat & 0x01)) { /* data in Rx FIFO? */ + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + count--; + continue; + } + count = 100; /* arm counter to new value */ + data = snd_gf1_uart_get(gus); + if (!(gus->gf1.uart_cmd & 0x80)) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + continue; + } + if (stat & 0x10) { /* framing error */ + gus->gf1.uart_framing++; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + snd_rawmidi_receive_reset(gus->midi_substream_input); + continue; + } + byte = snd_gf1_uart_get(gus); + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + snd_rawmidi_receive(gus->midi_substream_input, &byte, 1); + if (stat & 0x20) { + gus->gf1.uart_overrun++; + snd_rawmidi_receive_reset(gus->midi_substream_input); + } + } +} + +static void snd_gf1_interrupt_midi_out(snd_gus_card_t * gus) +{ + char byte; + unsigned long flags; + + /* try unlock output */ + if (snd_gf1_uart_stat(gus) & 0x01) + snd_gf1_interrupt_midi_in(gus); + + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */ + if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */ + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */ + } else { + snd_gf1_uart_put(gus, byte); + } + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +} + +static void snd_gf1_uart_reset(snd_gus_card_t * gus, int close) +{ + snd_gf1_uart_cmd(gus, 0x03); /* reset */ + if (!close && gus->uart_enable) { + udelay(160); + snd_gf1_uart_cmd(gus, 0x00); /* normal operations */ + } +} + +static int snd_gf1_uart_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */ + snd_gf1_uart_reset(gus, 0); + } + gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out; + gus->midi_substream_output = substream; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +#if 0 + snd_printk("write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); +#endif + return 0; +} + +static int snd_gf1_uart_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + int i; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) { + snd_gf1_uart_reset(gus, 0); + } + gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; + gus->midi_substream_input = substream; + if (gus->uart_enable) { + for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) + snd_gf1_uart_get(gus); /* clean Rx */ + if (i >= 1000) + snd_printk("gus midi uart init read - cleanup error\n"); + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +#if 0 + snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); + snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102)); +#endif + return 0; +} + +static int snd_gf1_uart_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) + snd_gf1_uart_reset(gus, 1); + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT); + gus->midi_substream_output = NULL; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return 0; +} + +static int snd_gf1_uart_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) + snd_gf1_uart_reset(gus, 1); + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN); + gus->midi_substream_input = NULL; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return 0; +} + +static void snd_gf1_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + snd_gus_card_t *gus; + unsigned long flags; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (up) { + if ((gus->gf1.uart_cmd & 0x80) == 0) + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */ + } else { + if (gus->gf1.uart_cmd & 0x80) + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */ + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +} + +static void snd_gf1_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_gus_card_t *gus; + char byte; + int timeout; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (up) { + if ((gus->gf1.uart_cmd & 0x20) == 0) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + /* wait for empty Rx - Tx is probably unlocked */ + timeout = 10000; + while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01); + /* Tx FIFO free? */ + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.uart_cmd & 0x20) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return; + } + if (snd_gf1_uart_stat(gus) & 0x02) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return; + } + snd_gf1_uart_put(gus, byte); + } + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */ + } + } else { + if (gus->gf1.uart_cmd & 0x20) + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +} + +static snd_rawmidi_ops_t snd_gf1_uart_output = +{ + open: snd_gf1_uart_output_open, + close: snd_gf1_uart_output_close, + trigger: snd_gf1_uart_output_trigger, +}; + +static snd_rawmidi_ops_t snd_gf1_uart_input = +{ + open: snd_gf1_uart_input_open, + close: snd_gf1_uart_input_close, + trigger: snd_gf1_uart_input_trigger, +}; + +int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t ** rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = gus; + gus->midi_uart = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return err; +} diff -Nru a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gus_volume.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,211 @@ +/* + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#define __GUS_TABLES_ALLOC__ +#include "gus_tables.h" + +EXPORT_SYMBOL(snd_gf1_atten_table); /* for snd-gus-synth module */ + +unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol) +{ + unsigned short e, m, tmp; + + if (vol > 65535) + vol = 65535; + tmp = vol; + e = 7; + if (tmp < 128) { + while (e > 0 && tmp < (1 << e)) + e--; + } else { + while (tmp > 255) { + tmp >>= 1; + e++; + } + } + m = vol - (1 << e); + if (m > 0) { + if (e > 8) + m >>= e - 8; + else if (e < 8) + m <<= 8 - e; + m &= 255; + } + return (e << 8) | m; +} + +unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol) +{ + unsigned int rvol; + unsigned short e, m; + + if (!gf1_vol) + return 0; + e = gf1_vol >> 8; + m = (unsigned char) gf1_vol; + rvol = 1 << e; + if (e > 8) + return rvol | (m << (e - 8)); + return rvol | (m >> (8 - e)); +} + +unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus, + unsigned short start, + unsigned short end, + unsigned int us) +{ + static unsigned char vol_rates[19] = + { + 23, 24, 26, 28, 29, 31, 32, 34, + 36, 37, 39, 40, 42, 44, 45, 47, + 49, 50, 52 + }; + unsigned short range, increment, value, i; + + start >>= 4; + end >>= 4; + if (start < end) + us /= end - start; + else + us /= start - end; + range = 4; + value = gus->gf1.enh_mode ? + vol_rates[0] : + vol_rates[gus->gf1.active_voices - 14]; + for (i = 0; i < 3; i++) { + if (us < value) { + range = i; + break; + } else + value <<= 3; + } + if (range == 4) { + range = 3; + increment = 1; + } else + increment = (value + (value >> 1)) / us; + return (range << 6) | (increment & 0x3f); +} + +unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq16) +{ + freq16 >>= 3; + if (freq16 < 50) + freq16 = 50; + if (freq16 & 0xf8000000) { + freq16 = ~0xf8000000; + snd_printk("snd_gf1_translate_freq: overflow - freq = 0x%x\n", freq16); + } + return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq; +} + +short snd_gf1_compute_vibrato(short cents, unsigned short fc_register) +{ + static short vibrato_table[] = + { + 0, 0, 32, 592, 61, 1175, 93, 1808, + 124, 2433, 152, 3007, 182, 3632, 213, 4290, + 241, 4834, 255, 5200 + }; + + long depth; + short *vi1, *vi2, pcents, v1; + + pcents = cents < 0 ? -cents : cents; + for (vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2); + v1 = *(vi1 + 1); + /* The FC table above is a list of pairs. The first number in the pair */ + /* is the cents index from 0-255 cents, and the second number in the */ + /* pair is the FC adjustment needed to change the pitch by the indexed */ + /* number of cents. The table was created for an FC of 32768. */ + /* The following expression does a linear interpolation against the */ + /* approximated log curve in the table above, and then scales the number */ + /* by the FC before the LFO. This calculation also adjusts the output */ + /* value to produce the appropriate depth for the hardware. The depth */ + /* is 2 * desired FC + 1. */ + depth = (((int) (*(vi2 + 1) - *vi1) * (pcents - *vi1) / (*vi2 - *vi1)) + v1) * fc_register >> 14; + if (depth) + depth++; + if (depth > 255) + depth = 255; + return cents < 0 ? -(short) depth : (short) depth; +} + +unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens) +{ + static long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933}; + int wheel, sensitivity; + unsigned int mantissa, f1, f2; + unsigned short semitones, f1_index, f2_index, f1_power, f2_power; + char bend_down = 0; + int bend; + + if (!sens) + return 1024; + wheel = (int) pitchbend - 8192; + sensitivity = ((int) sens * wheel) / 128; + if (sensitivity < 0) { + bend_down = 1; + sensitivity = -sensitivity; + } + semitones = (unsigned int) (sensitivity >> 13); + mantissa = sensitivity % 8192; + f1_index = semitones % 12; + f2_index = (semitones + 1) % 12; + f1_power = semitones / 12; + f2_power = (semitones + 1) / 12; + f1 = log_table[f1_index] << f1_power; + f2 = log_table[f2_index] << f2_power; + bend = (int) ((((f2 - f1) * mantissa) >> 13) + f1); + if (bend_down) + bend = 1048576L / bend; + return bend; +} + +unsigned short snd_gf1_compute_freq(unsigned int freq, + unsigned int rate, + unsigned short mix_rate) +{ + unsigned int fc; + int scale = 0; + + while (freq >= 4194304L) { + scale++; + freq >>= 1; + } + fc = (freq << 10) / rate; + if (fc > 97391L) { + fc = 97391; + snd_printk("patch: (1) fc frequency overflow - %u\n", fc); + } + fc = (fc * 44100UL) / mix_rate; + while (scale--) + fc <<= 1; + if (fc > 65535L) { + fc = 65535; + snd_printk("patch: (2) fc frequency overflow - %u\n", fc); + } + return (unsigned short) fc; +} diff -Nru a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gusclassic.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,302 @@ +/* + * Driver for Gravis UltraSound Classic soundcard + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Gravis UltraSound Classic"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Gravis,UltraSound Classic}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,5,9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for GUS Classic soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for GUS Classic soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable GUS Classic soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x10}},dialog:list"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_channels, "GF1 channels for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_channels, SNDRV_ENABLED ",allows:{{14,32}}"); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); + +static snd_card_t *snd_gusclassic_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_gusclassic_detect(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -ENODEV; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -ENODEV; +#endif + + return 0; +} + +static void __init snd_gusclassic_init(int dev, snd_gus_card_t * gus) +{ + gus->equal_irq = 0; + gus->codec_flag = 0; + gus->max_flag = 0; + gus->joystick_dac = snd_joystick_dac[dev]; +} + +static int __init snd_gusclassic_probe(int dev) +{ + static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1}; + static int possible_dmas[] = {5, 6, 7, 1, 3, -1}; + int irq, dma1, dma2; + snd_card_t *card; + struct snd_gusclassic *guscard; + snd_gus_card_t *gus = NULL; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + guscard = (struct snd_gusclassic *)card->private_data; + if (snd_pcm_channels[dev] < 2) + snd_pcm_channels[dev] = 2; + + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + dma1 = snd_dma1[dev]; + if (dma1 == SNDRV_AUTO_DMA) { + if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + dma2 = snd_dma2[dev]; + if (dma2 == SNDRV_AUTO_DMA) { + if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + + if ((err = snd_gus_create(card, + snd_port[dev], + irq, dma1, dma2, + 0, snd_channels[dev], snd_pcm_channels[dev], + 0, &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusclassic_detect(gus)) < 0) { + snd_card_free(card); + return err; + } + snd_gusclassic_init(dev, gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + if (gus->max_flag || gus->ess_flag) { + snd_card_free(card); + snd_printdd("GUS Classic or ACE soundcard was not detected at 0x%lx\n", gus->gf1.port); + return -ENODEV; + } + if ((err = snd_gf1_new_mixer(gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gf1_pcm_new(gus, 0, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (!gus->ace_flag) { + if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %d, dma %d", gus->gf1.port, irq, dma1); + if (dma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", dma2); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_gusclassic_cards[dev] = card; + return 0; +} + +static int __init snd_gusclassic_legacy_auto_probe(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; + snd_port[dev] = port; + res = snd_gusclassic_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_gusclassic_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_gusclassic_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_gusclassic_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + snd_printk("GUS Classic soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_gusclassic_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_gusclassic_cards[idx]); +} + +module_init(alsa_card_gusclassic_init) +module_exit(alsa_card_gusclassic_exit) + +#ifndef MODULE + +/* format is: snd-gusclassic=snd_enable,snd_index,snd_id, + snd_port,snd_irq, + snd_dma1,snd_dma2, + snd_joystick_dac, + snd_channels,snd_pcm_channels */ + +static int __init alsa_card_gusclassic_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2 && + get_option(&str,&snd_joystick_dac[nr_dev]) == 2 && + get_option(&str,&snd_channels[nr_dev]) == 2 && + get_option(&str,&snd_pcm_channels[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-gusclassic=", alsa_card_gusclassic_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gusextreme.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,436 @@ +/* + * Driver for Gravis UltraSound Extreme soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Gravis UltraSound Extreme"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Gravis,UltraSound Extreme}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static long snd_gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */ +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_gf1_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for GUS Extreme soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for GUS Extreme soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable GUS Extreme soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x20}},dialog:list"); +MODULE_PARM(snd_gf1_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_gf1_port, "GF1 port # for GUS Extreme driver (optional)."); +MODULE_PARM_SYNTAX(snd_gf1_port, SNDRV_ENABLED ",allows:{{0x210,0x270,0x10}},dialog:list"); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x320,0x10}},dialog:list"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list"); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list"); +MODULE_PARM(snd_gf1_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_gf1_irq, "GF1 IRQ # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_gf1_irq, SNDRV_ENABLED ",allows:{{2},{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "GF1 DMA # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_channels, "GF1 channels for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_channels, SNDRV_ENABLED ",allows:{{14,32}}"); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); + +static snd_card_t *snd_gusextreme_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_gusextreme_detect(int dev, + snd_card_t * card, + snd_gus_card_t * gus, + es1688_t *es1688) +{ + unsigned long flags; + + /* + * This is main stuff - enable access to GF1 chip... + * I'm not sure, if this will work for card which have + * ES1688 chip in another place than 0x220. + * + * I used reverse-engineering in DOSEMU. [--jk] + * + * ULTRINIT.EXE: + * 0x230 = 0,2,3 + * 0x240 = 2,0,1 + * 0x250 = 2,0,3 + * 0x260 = 2,2,1 + */ + + spin_lock_irqsave(&es1688->mixer_lock, flags); + snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */ + spin_unlock_irqrestore(&es1688->mixer_lock, flags); + spin_lock_irqsave(&es1688->reg_lock, flags); + outb(snd_gf1_port[dev] & 0x040 ? 2 : 0, ES1688P(es1688, INIT1)); + outb(0, 0x201); + outb(snd_gf1_port[dev] & 0x020 ? 2 : 0, ES1688P(es1688, INIT1)); + outb(0, 0x201); + outb(snd_gf1_port[dev] & 0x010 ? 3 : 1, ES1688P(es1688, INIT1)); + spin_unlock_irqrestore(&es1688->reg_lock, flags); + + udelay(100); + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -EIO; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -EIO; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -EIO; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -EIO; +#endif + + return 0; +} + +static void __init snd_gusextreme_init(int dev, snd_gus_card_t * gus) +{ + gus->joystick_dac = snd_joystick_dac[dev]; +} + +static int __init snd_gusextreme_mixer(es1688_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + int err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUX to SYNTHESIZER */ + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Synth Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign Master Playback Switch to Synth Playback Switch */ + strcpy(id1.name, "Master Playback Switch"); + strcpy(id2.name, "Synth Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + return 0; +} + +static int __init snd_gusextreme_probe(int dev) +{ + static int possible_ess_irqs[] = {5, 9, 10, 7, -1}; + static int possible_ess_dmas[] = {1, 3, 0, -1}; + static int possible_gf1_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; + static int possible_gf1_dmas[] = {5, 6, 7, 1, 3, -1}; + int gf1_irq, gf1_dma, ess_irq, mpu_irq, ess_dma; + snd_card_t *card; + struct snd_gusextreme *acard; + snd_gus_card_t *gus; + es1688_t *es1688; + opl3_t *opl3; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_gusextreme *)card->private_data; + + gf1_irq = snd_gf1_irq[dev]; + if (gf1_irq == SNDRV_AUTO_IRQ) { + if ((gf1_irq = snd_legacy_find_free_irq(possible_gf1_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ for GF1\n"); + return -EBUSY; + } + } + ess_irq = snd_irq[dev]; + if (ess_irq == SNDRV_AUTO_IRQ) { + if ((ess_irq = snd_legacy_find_free_irq(possible_ess_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ for ES1688\n"); + return -EBUSY; + } + } + if (snd_mpu_port[dev] == SNDRV_AUTO_PORT) + snd_mpu_port[dev] = 0; + mpu_irq = snd_mpu_irq[dev]; + if (mpu_irq > 15) + mpu_irq = -1; + gf1_dma = snd_dma1[dev]; + if (gf1_dma == SNDRV_AUTO_DMA) { + if ((gf1_dma = snd_legacy_find_free_dma(possible_gf1_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA for GF1\n"); + return -EBUSY; + } + } + ess_dma = snd_dma8[dev]; + if (ess_dma == SNDRV_AUTO_DMA) { + if ((ess_dma = snd_legacy_find_free_dma(possible_ess_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA for ES1688\n"); + return -EBUSY; + } + } + + if ((err = snd_es1688_create(card, snd_port[dev], snd_mpu_port[dev], + ess_irq, mpu_irq, ess_dma, + ES1688_HW_1688, &es1688)) < 0) { + snd_card_free(card); + return err; + } + if (snd_gf1_port[dev] < 0) + snd_gf1_port[dev] = snd_port[dev] + 0x20; + if ((err = snd_gus_create(card, + snd_gf1_port[dev], + gf1_irq, + gf1_dma, + -1, + 0, snd_channels[dev], + snd_pcm_channels[dev], 0, + &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusextreme_detect(dev, card, gus, es1688)) < 0) { + snd_card_free(card); + return err; + } + snd_gusextreme_init(dev, gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + if (!gus->ess_flag) { + snd_card_free(card); + snd_printdd("GUS Extreme soundcard was not detected at 0x%lx\n", gus->gf1.port); + return -ENODEV; + } + if ((err = snd_es1688_pcm(es1688, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1688_mixer(es1688)) < 0) { + snd_card_free(card); + return err; + } + snd_component_add(card, "ES1688"); + if (snd_pcm_channels[dev] > 0) { + if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_gf1_new_mixer(gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusextreme_mixer(es1688)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_opl3_create(card, es1688->port, es1688->port + 2, + OPL3_HW_OPL3, 0, &opl3) < 0) { + snd_printk("opl3 not detected at 0x%lx\n", es1688->port); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if (es1688->mpu_port >= 0x300) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, + es1688->mpu_port, 0, + mpu_irq, + SA_INTERRUPT, + NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, irq %i&%i, dma %i&%i", + es1688->port, gf1_irq, ess_irq, gf1_dma, ess_dma); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_gusextreme_cards[dev] = card; + return 0; +} + +static int __init snd_gusextreme_legacy_auto_probe(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; + snd_port[dev] = port; + res = snd_gusextreme_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_gusextreme_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev] > 0; dev++) { + if (snd_port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_gusextreme_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_gusextreme_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + snd_printk("GUS Extreme soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_gusextreme_exit(void) +{ + int idx; + snd_card_t *card; + struct snd_gusextreme *acard; + + for (idx = 0; idx < SNDRV_CARDS; idx++) { + card = snd_gusextreme_cards[idx]; + if (card == NULL) + continue; + acard = (struct snd_gusextreme *)card->private_data; + snd_card_free(snd_gusextreme_cards[idx]); + } +} + +module_init(alsa_card_gusextreme_init) +module_exit(alsa_card_gusextreme_exit) + +#ifndef MODULE + +/* format is: snd-gusextreme=snd_enable,snd_index,snd_id, + snd_port,snd_gf1_port,snd_mpu_port, + snd_irq,snd_gf1_irq,snd_mpu_irq, + snd_dma8,snd_dma1, + snd_joystick_dac, + snd_channels,snd_pcm_channels */ + +static int __init alsa_card_gusextreme_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_gf1_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_gf1_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-gusextreme=", alsa_card_gusextreme_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/gusmax.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,438 @@ +/* + * Driver for Gravis UltraSound MAX soundcard + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Gravis UltraSound MAX"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Gravis,UltraSound MAX}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for GUS MAX soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for GUS MAX soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable GUS MAX soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220},{0x230},{0x240},{0x250},{0x260}},dialog:list"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); +MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_channels, "Used GF1 channels for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_channels, SNDRV_ENABLED ",allows:{{14,32}}"); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); + +struct snd_gusmax { + int irq; + snd_card_t *card; + snd_gus_card_t *gus; + cs4231_t *cs4231; + unsigned short gus_status_reg; + unsigned short pcm_status_reg; +}; + +static snd_card_t *snd_gusmax_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_gusmax_detect(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -ENODEV; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -ENODEV; +#endif + return 0; +} + +static void snd_gusmax_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct snd_gusmax *maxcard = (struct snd_gusmax *) dev_id; + int loop, max = 5; + + do { + loop = 0; + if (inb(maxcard->gus_status_reg)) { + snd_gus_interrupt(irq, maxcard->gus, regs); + loop++; + } + if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ + snd_cs4231_interrupt(irq, maxcard->cs4231, regs); + loop++; + } + } while (loop && --max > 0); +} + +static void __init snd_gusmax_init(int dev, snd_card_t * card, snd_gus_card_t * gus) +{ + gus->equal_irq = 1; + gus->codec_flag = 1; + gus->joystick_dac = snd_joystick_dac[dev]; + /* init control register */ + gus->max_cntrl_val = (gus->gf1.port >> 4) & 0x0f; + if (gus->gf1.dma1 > 3) + gus->max_cntrl_val |= 0x10; + if (gus->gf1.dma2 > 3) + gus->max_cntrl_val |= 0x20; + gus->max_cntrl_val |= 0x40; + outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT)); +} + +#define CS4231_PRIVATE( left, right, shift, mute ) \ + ((left << 24)|(right << 16)|(shift<<8)|mute) + +static int __init snd_gusmax_mixer(cs4231_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + int err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUXA to SYNTHESIZER */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "Synth Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Synth Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUXB to CD */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "CD Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "CD Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; +#if 0 + /* reassign Mono Input to MIC */ + if (snd_mixer_group_rename(mixer, + SNDRV_MIXER_IN_MONO, 0, + SNDRV_MIXER_IN_MIC, 0) < 0) + goto __error; + if (snd_mixer_elem_rename(mixer, + SNDRV_MIXER_IN_MONO, 0, SNDRV_MIXER_ETYPE_INPUT, + SNDRV_MIXER_IN_MIC, 0) < 0) + goto __error; + if (snd_mixer_elem_rename(mixer, + "Mono Capture Volume", 0, SNDRV_MIXER_ETYPE_VOLUME1, + "Mic Capture Volume", 0) < 0) + goto __error; + if (snd_mixer_elem_rename(mixer, + "Mono Capture Switch", 0, SNDRV_MIXER_ETYPE_SWITCH1, + "Mic Capture Switch", 0) < 0) + goto __error; +#endif + return 0; +} + +static void snd_gusmax_free(snd_card_t *card) +{ + struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data; + + if (maxcard == NULL) + return; + if (maxcard->irq >= 0) + free_irq(maxcard->irq, (void *)maxcard); +} + +static int __init snd_gusmax_probe(int dev) +{ + static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; + static int possible_dmas[] = {5, 6, 7, 1, 3, -1}; + int irq, dma1, dma2, err; + snd_card_t *card; + snd_gus_card_t *gus = NULL; + cs4231_t *cs4231; + struct snd_gusmax *maxcard; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_gusmax)); + if (card == NULL) + return -ENOMEM; + card->private_free = snd_gusmax_free; + maxcard = (struct snd_gusmax *)card->private_data; + maxcard->card = card; + maxcard->irq = -1; + + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + dma1 = snd_dma1[dev]; + if (dma1 == SNDRV_AUTO_DMA) { + if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + dma2 = snd_dma2[dev]; + if (dma2 == SNDRV_AUTO_DMA) { + if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + if ((err = snd_gus_create(card, + snd_port[dev], + -irq, dma1, dma2, + 0, snd_channels[dev], + snd_pcm_channels[dev], + 0, &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusmax_detect(gus)) < 0) { + snd_card_free(card); + return err; + } + maxcard->gus_status_reg = gus->gf1.reg_irqstat; + maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; + snd_gusmax_init(dev, card, gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + if (!gus->max_flag) { + snd_card_free(card); + snd_printk("GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port); + return -ENODEV; + } + + if (request_irq(irq, snd_gusmax_interrupt, SA_INTERRUPT, "GUS MAX", (void *)maxcard)) { + snd_card_free(card); + snd_printk("unable to grab IRQ %d\n", irq); + return -EBUSY; + } + maxcard->irq = irq; + + if ((err = snd_cs4231_create(card, + gus->gf1.port + 0x10c, -1, irq, + dma2 < 0 ? dma1 : dma2, dma1, + CS4231_HW_DETECT, + CS4231_HWSHARE_IRQ | + CS4231_HWSHARE_DMA1 | + CS4231_HWSHARE_DMA2, + &cs4231)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (snd_pcm_channels[dev] > 0) { + if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_gusmax_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, irq, dma1); + if (dma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%i", dma2); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + maxcard->gus = gus; + maxcard->cs4231 = cs4231; + snd_gusmax_cards[dev] = card; + return 0; +} + +static int __init snd_gusmax_legacy_auto_probe(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; + snd_port[dev] = port; + res = snd_gusmax_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_gusmax_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev] > 0; dev++) { + if (snd_port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_gusmax_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_gusmax_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + snd_printk("GUS MAX soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_gusmax_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_gusmax_cards[idx]); +} + +module_init(alsa_card_gusmax_init) +module_exit(alsa_card_gusmax_exit) + +#ifndef MODULE + +/* format is: snd-gusmax=snd_enable,snd_index,snd_id, + snd_port,snd_irq, + snd_dma1,snd_dma2, + snd_joystick_dac, + snd_channels,snd_pcm_channels */ + +static int __init alsa_card_gusmax_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2 && + get_option(&str,&snd_joystick_dac[nr_dev]) == 2 && + get_option(&str,&snd_channels[nr_dev]) == 2 && + get_option(&str,&snd_pcm_channels[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-gusmax=", alsa_card_gusmax_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/gus/interwave-stb.c b/sound/isa/gus/interwave-stb.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/interwave-stb.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2 @@ +#define SNDRV_STB +#include "interwave.c" diff -Nru a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/gus/interwave.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1025 @@ +/* + * Driver for AMD InterWave soundcard + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 1999/07/22 Erik Inge Bolso + * * mixer group handlers + * + */ + +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#ifdef SNDRV_STB +#include +#endif +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); +#ifndef SNDRV_STB +MODULE_DESCRIPTION("AMD InterWave"); +MODULE_DEVICES("{{Gravis,UltraSound Plug & Play}," + "{STB,SoundRage32}," + "{MED,MED3210}," + "{Dynasonix,Dynasonix Pro}," + "{Panasonic,PCA761AW}}"); +#else +MODULE_DESCRIPTION("AMD InterWave STB with TEA6330T"); +MODULE_DEVICES("{{AMD,InterWave STB with TEA6330T}}"); +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x210,0x220,0x230,0x240,0x250,0x260 */ +#ifdef SNDRV_STB +static long snd_port_tc[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x350,0x360,0x370,0x380 */ +#endif +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int snd_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; +static int snd_effect[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for InterWave soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for InterWave soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable InterWave soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for InterWave driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x210,0x260,0x10}},dialog:list"); +#ifdef SNDRV_STB +MODULE_PARM(snd_port_tc, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver."); +MODULE_PARM_SYNTAX(snd_port_tc, SNDRV_ENABLED ",allows:{{0x350,0x380,0x10}},dialog:list"); +#endif +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for InterWave driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for InterWave driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for InterWave driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); +MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver."); +MODULE_PARM_SYNTAX(snd_joystic_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(snd_midi, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_midi, "MIDI UART enable for InterWave driver."); +MODULE_PARM_SYNTAX(snd_midi, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for InterWave driver."); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); +MODULE_PARM(snd_effect, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_effect, "Effects enable for InterWave driver."); +MODULE_PARM_SYNTAX(snd_effect, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); + +struct snd_interwave { + int irq; + snd_card_t *card; + snd_gus_card_t *gus; + cs4231_t *cs4231; +#ifdef SNDRV_STB + struct resource *i2c_res; +#endif + unsigned short gus_status_reg; + unsigned short pcm_status_reg; +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#ifdef SNDRV_STB + struct isapnp_dev *devtc; +#endif +#endif +}; + +static snd_card_t *snd_interwave_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_interwave_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_interwave_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#define ISAPNP_INTERWAVE(_va, _vb, _vc, _device, _audio) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \ + } +#define ISAPNP_INTERWAVE_STB(_va, _vb, _vc, _device, _audio, _tone) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _tone), } \ + } + +static struct isapnp_card_id snd_interwave_pnpids[] __devinitdata = { +#ifndef SNDRV_STB + /* Gravis UltraSound Plug & Play */ + ISAPNP_INTERWAVE('G','R','V',0x0001,0x0000), + /* STB SoundRage32 */ + ISAPNP_INTERWAVE('S','T','B',0x011a,0x0010), + /* MED3210 */ + ISAPNP_INTERWAVE('D','X','P',0x3201,0x0010), + /* Dynasonic Pro */ + /* This device also have CDC1117:DynaSonix Pro Audio Effects Processor */ + ISAPNP_INTERWAVE('C','D','C',0x1111,0x1112), + /* Panasonic PCA761AW Audio Card */ + ISAPNP_INTERWAVE('A','D','V',0x55ff,0x0010), +#else + /* InterWave STB with TEA6330T */ + ISAPNP_INTERWAVE_STB('A','D','V',0x550a,0x0010,0x0015), +#endif + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_interwave_pnpids); + +#endif /* __ISAPNP__ */ + + +#ifdef SNDRV_STB +static void snd_interwave_i2c_setlines(snd_i2c_bus_t *bus, int ctrl, int data) +{ + unsigned long port = bus->private_value; + +#if 0 + printk("i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data); +#endif + outb((data << 1) | ctrl, port); + udelay(10); +} + +static int snd_interwave_i2c_getclockline(snd_i2c_bus_t *bus) +{ + unsigned long port = bus->private_value; + unsigned char res; + + res = inb(port) & 1; +#if 0 + printk("i2c_getclockline - 0x%lx -> %i\n", port, res); +#endif + return res; +} + +static int snd_interwave_i2c_getdataline(snd_i2c_bus_t *bus, int ack) +{ + unsigned long port = bus->private_value; + unsigned char res; + + if (ack) + udelay(10); + res = (inb(port) & 2) >> 1; +#if 0 + printk("i2c_getdataline - 0x%lx -> %i\n", port, res); +#endif + return res; +} + +static snd_i2c_bit_ops_t snd_interwave_i2c_bit_ops = { + setlines: snd_interwave_i2c_setlines, + getclock: snd_interwave_i2c_getclockline, + getdata: snd_interwave_i2c_getdataline, +}; + +static int __init snd_interwave_detect_stb(struct snd_interwave *iwcard, + snd_gus_card_t * gus, int dev, + snd_i2c_bus_t **rbus) +{ + unsigned long port; + snd_i2c_bus_t *bus; + snd_card_t *card = iwcard->card; + char name[32]; + int err; + + *rbus = NULL; + port = snd_port_tc[dev]; + if (port == SNDRV_AUTO_PORT) { + port = 0x350; + if (gus->gf1.port == 0x250) { + port = 0x360; + } + while (port <= 0x380) { + if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL) + break; + port += 0x10; + } + if (port > 0x380) + return -ENODEV; + } else { + if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL) + return -ENODEV; + } + sprintf(name, "InterWave-%i", card->number); + if ((err = snd_i2c_bus_create(card, name, NULL, &bus)) < 0) + return err; + bus->private_value = port; + bus->hw_ops.bit = &snd_interwave_i2c_bit_ops; + if ((err = snd_tea6330t_detect(bus, 0)) < 0) + return err; + *rbus = bus; + return 0; +} +#endif + +static int __init snd_interwave_detect(struct snd_interwave *iwcard, + snd_gus_card_t * gus, + int dev +#ifdef SNDRV_STB + , snd_i2c_bus_t **rbus +#endif + ) +{ + unsigned long flags; + unsigned char rev1, rev2; + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + int d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -ENODEV; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + int d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -ENODEV; +#endif + + spin_lock_irqsave(&gus->reg_lock, flags); + rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1); + rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2); + if ((rev1 & 0xf0) == (rev2 & 0xf0) && + (rev1 & 0x0f) != (rev2 & 0x0f)) { + snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port); + gus->interwave = 1; + strcpy(gus->card->shortname, "AMD InterWave"); + gus->revision = rev1 >> 4; +#ifndef SNDRV_STB + return 0; /* ok.. We have an InterWave board */ +#else + return snd_interwave_detect_stb(iwcard, gus, dev, rbus); +#endif + } + snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port); + return -ENODEV; +} + +static void snd_interwave_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct snd_interwave *iwcard = (struct snd_interwave *) dev_id; + int loop, max = 5; + + do { + loop = 0; + if (inb(iwcard->gus_status_reg)) { + snd_gus_interrupt(irq, iwcard->gus, regs); + loop++; + } + if (inb(iwcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ + snd_cs4231_interrupt(irq, iwcard->cs4231, regs); + loop++; + } + } while (loop && --max > 0); +} + +static void __init snd_interwave_reset(snd_gus_card_t * gus) +{ + snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00); + udelay(160); + snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x01); + udelay(160); +} + +static void __init snd_interwave_bank_sizes(snd_gus_card_t * gus, int *sizes) +{ + unsigned int idx; + unsigned int local; + unsigned char d; + + for (idx = 0; idx < 4; idx++) { + sizes[idx] = 0; + d = 0x55; + for (local = idx << 22; + local < (idx << 22) + 0x400000; + local += 0x40000, d++) { + snd_gf1_poke(gus, local, d); + snd_gf1_poke(gus, local + 1, d + 1); +#if 0 + printk("d = 0x%x, local = 0x%x, local + 1 = 0x%x, idx << 22 = 0x%x\n", + d, + snd_gf1_peek(gus, local), + snd_gf1_peek(gus, local + 1), + snd_gf1_peek(gus, idx << 22)); +#endif + if (snd_gf1_peek(gus, local) != d || + snd_gf1_peek(gus, local + 1) != d + 1 || + snd_gf1_peek(gus, idx << 22) != 0x55) + break; + sizes[idx]++; + } + } +#if 0 + printk("sizes: %i %i %i %i\n", sizes[0], sizes[1], sizes[2], sizes[3]); +#endif +} + +struct rom_hdr { + /* 000 */ unsigned char iwave[8]; + /* 008 */ unsigned char rom_hdr_revision; + /* 009 */ unsigned char series_number; + /* 010 */ unsigned char series_name[16]; + /* 026 */ unsigned char date[10]; + /* 036 */ unsigned short vendor_revision_major; + /* 038 */ unsigned short vendor_revision_minor; + /* 040 */ unsigned int rom_size; + /* 044 */ unsigned char copyright[128]; + /* 172 */ unsigned char vendor_name[64]; + /* 236 */ unsigned char rom_description[128]; + /* 364 */ unsigned char pad[147]; + /* 511 */ unsigned char csum; +}; + +static void __init snd_interwave_detect_memory(snd_gus_card_t * gus) +{ + static unsigned int lmc[13] = + { + 0x00000001, 0x00000101, 0x01010101, 0x00000401, + 0x04040401, 0x00040101, 0x04040101, 0x00000004, + 0x00000404, 0x04040404, 0x00000010, 0x00001010, + 0x10101010 + }; + + int bank_pos, pages; + unsigned int i, lmct; + int psizes[4]; + unsigned char csum; + struct rom_hdr romh; + + snd_interwave_reset(gus); + snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); /* enhanced mode */ + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); /* DRAM I/O cycles selected */ + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff10) | 0x004c); + /* ok.. simple test of memory size */ + pages = 0; + snd_gf1_poke(gus, 0, 0x55); + snd_gf1_poke(gus, 1, 0xaa); +#if 1 + if (snd_gf1_peek(gus, 0) == 0x55 && snd_gf1_peek(gus, 1) == 0xaa) +#else + if (0) /* ok.. for testing of 0k RAM */ +#endif + { + snd_interwave_bank_sizes(gus, psizes); + lmct = (psizes[3] << 24) | (psizes[2] << 16) | + (psizes[1] << 8) | psizes[0]; +#if 0 + printk("lmct = 0x%08x\n", lmct); +#endif + for (i = 0; i < sizeof(lmc) / sizeof(unsigned int); i++) + if (lmct == lmc[i]) { +#if 0 + printk("found !!! %i\n", i); +#endif + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i); + snd_interwave_bank_sizes(gus, psizes); + break; + } + if (i >= sizeof(lmc) / sizeof(unsigned int) && !gus->gf1.enh_mode) + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2); + for (i = 0; i < 4; i++) { + gus->gf1.mem_alloc.banks_8[i].address = + gus->gf1.mem_alloc.banks_16[i].address = i << 22; + gus->gf1.mem_alloc.banks_8[i].size = + gus->gf1.mem_alloc.banks_16[i].size = psizes[i] << 18; + pages += psizes[i]; + } + } + pages <<= 18; + gus->gf1.memory = pages; + + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x03); /* select ROM */ + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff1f) | (4 << 5)); + gus->gf1.rom_banks = 0; + gus->gf1.rom_memory = 0; + for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) { + for (i = 0; i < sizeof(struct rom_hdr); i++) + *(((unsigned char *) &romh) + i) = snd_gf1_peek(gus, i + bank_pos); +#ifdef CONFIG_SND_DEBUG_ROM + printk("ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos, + romh.iwave[0], romh.iwave[1], romh.iwave[2], romh.iwave[3], + romh.iwave[4], romh.iwave[5], romh.iwave[6], romh.iwave[7]); +#endif + if (strncmp(romh.iwave, "INTRWAVE", 8)) + continue; /* first check */ + csum = 0; + for (i = 0; i < sizeof(struct rom_hdr) - 1; i++) + csum += *(((unsigned char *) &romh) + i); +#ifdef CONFIG_SND_DEBUG_ROM + printk("ROM checksum = 0x%x == 0x%x (computed)\n", romh.csum, (unsigned char) (256 - csum)); +#endif + if (256 - csum != romh.csum) + continue; /* not valid rom */ + gus->gf1.rom_banks++; + gus->gf1.rom_present |= 1 << (bank_pos >> 22); +#ifdef SNDRV_LITTLE_ENDIAN + gus->gf1.rom_memory = romh.rom_size; +#else + gus->gf1.rom_memory = ((romh.rom_size >> 24) & 0x000000ff) | + ((romh.rom_size >> 8) & 0x0000ff00) | + ((romh.rom_size << 8) & 0x00ff0000) | + ((romh.rom_size << 24) & 0xff000000); +#endif + } +#if 0 + if (gus->gf1.rom_memory > 0) { + if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) + gus->card->type = SNDRV_CARD_TYPE_IW_DYNASONIC; + } +#endif + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x00); /* select RAM */ + + if (!gus->gf1.enh_mode) + snd_interwave_reset(gus); +} + +static void __init snd_interwave_init(int dev, snd_gus_card_t * gus) +{ + unsigned long flags; + + /* ok.. some InterWave specific initialization */ + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00); + snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f); + snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11); + snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00); + snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30); + snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); + spin_unlock_irqrestore(&gus->reg_lock, flags); + gus->equal_irq = 1; + gus->codec_flag = 1; + gus->interwave = 1; + gus->max_flag = 1; + gus->joystick_dac = snd_joystick_dac[dev]; + +} + +#define INTERWAVE_CONTROLS (sizeof(snd_interwave_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_interwave_controls[] = { +CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1), +CS4231_DOUBLE("Mic Playback Switch", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Mic Playback Volume", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1) +}; + +static int __init snd_interwave_mixer(cs4231_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + int idx, err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; +#if 0 + /* remove mono microphone controls */ + strcpy(id1.name, "Mic Playback Switch"); + if ((err = snd_ctl_remove_id(card, &id1)) < 0) + return err; + strcpy(id1.name, "Mic Playback Volume"); + if ((err = snd_ctl_remove_id(card, &id1)) < 0) + return err; +#endif + /* add new master and mic controls */ + for (idx = 0; idx < INTERWAVE_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0) + return err; + snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f); + snd_cs4231_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f); + snd_cs4231_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f); + snd_cs4231_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f); + /* reassign AUXA to SYNTHESIZER */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "Synth Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Synth Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUXB to CD */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "CD Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "CD Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + return 0; +} + +#ifdef __ISAPNP__ + +static int __init snd_interwave_isapnp(int dev, struct snd_interwave *iwcard) +{ + const struct isapnp_card_id *id = snd_interwave_isapnp_id[dev]; + struct isapnp_card *card = snd_interwave_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + iwcard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (iwcard->dev->active) { + iwcard->dev = NULL; + return -EBUSY; + } +#ifdef SNDRV_STB + iwcard->devtc = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (iwcard->devtc->active) { + iwcard->dev = iwcard->devtc = NULL; + return -EBUSY; + } +#endif + /* Synth & Codec initialization */ + pdev = iwcard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + if (snd_port[dev] != SNDRV_AUTO_PORT) { + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + isapnp_resource_change(&pdev->resource[1], snd_port[dev] + 0x100, 12); + isapnp_resource_change(&pdev->resource[2], snd_port[dev] + 0x10c, 4); + } + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1); + if (snd_dma2[dev] < 0) + isapnp_resource_change(&pdev->dma_resource[1], 4, 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk("isapnp configure failure (out of resources?)\n"); + return -EBUSY; + } + if (pdev->resource[0].start + 0x100 != pdev->resource[1].start || + pdev->resource[0].start + 0x10c != pdev->resource[2].start) { + snd_printk("isapnp configure failure (wrong ports)\n"); + pdev->deactivate(pdev); + return -ENOENT; + } + snd_port[dev] = pdev->resource[0].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + if (snd_dma2[dev] >= 0) + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp IW: sb port=0x%lx, gf1 port=0x%lx, codec port=0x%lx\n", + pdev->resource[0].start, + pdev->resource[1].start, + pdev->resource[2].start); + snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", snd_dma1[dev], snd_dma2[dev], snd_irq[dev]); +#ifdef SNDRV_STB + /* Tone Control initialization */ + pdev = iwcard->devtc; + if (pdev->prepare(pdev)<0) { + iwcard->dev->deactivate(iwcard->dev); + return -EAGAIN; + } + if (snd_port_tc[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port_tc[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk("Tone Control isapnp configure failure (out of resources?)\n"); + iwcard->dev->deactivate(iwcard->dev); + return -EBUSY; + } + snd_port_tc[dev] = pdev->resource[0].start; + snd_printdd("isapnp IW: tone control port=0x%lx\n", snd_port_tc[dev]); +#endif + return 0; +} + +static void snd_interwave_deactivate(struct snd_interwave *iwcard) +{ + if (iwcard->dev) { + iwcard->dev->deactivate(iwcard->dev); + iwcard->dev = NULL; + } +#ifdef SNDRV_STB + if (iwcard->devtc) { + iwcard->devtc->deactivate(iwcard->devtc); + iwcard->devtc = NULL; + } +#endif +} + +#endif /* __ISAPNP__ */ + +static void snd_interwave_free(snd_card_t *card) +{ + struct snd_interwave *iwcard = (struct snd_interwave *)card->private_data; + + if (iwcard == NULL) + return; +#ifdef __ISAPNP__ + snd_interwave_deactivate(iwcard); +#endif +#ifdef SNDRV_STB + if (iwcard->i2c_res) { + release_resource(iwcard->i2c_res); + kfree_nocheck(iwcard->i2c_res); + } +#endif + if (iwcard->irq >= 0) + free_irq(iwcard->irq, (void *)iwcard); +} + +static int __init snd_interwave_probe(int dev) +{ + static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; + static int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1}; + int irq, dma1, dma2; + snd_card_t *card; + struct snd_interwave *iwcard; + cs4231_t *cs4231; + snd_gus_card_t *gus; +#ifdef SNDRV_STB + snd_i2c_bus_t *i2c_bus; +#endif + snd_pcm_t *pcm; + char *str; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_interwave)); + if (card == NULL) + return -ENOMEM; + iwcard = (struct snd_interwave *)card->private_data; + iwcard->card = card; + iwcard->irq = -1; + card->private_free = snd_interwave_free; +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && snd_interwave_isapnp(dev, iwcard)) { + snd_card_free(card); + return -ENODEV; + } +#endif + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + dma1 = snd_dma1[dev]; + if (dma1 == SNDRV_AUTO_DMA) { + if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + dma2 = snd_dma2[dev]; + if (dma2 == SNDRV_AUTO_DMA) { + if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + if ((err = snd_gus_create(card, + snd_port[dev], + -irq, dma1, dma2, + 0, 32, + snd_pcm_channels[dev], snd_effect[dev], &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_interwave_detect(iwcard, gus, dev +#ifdef SNDRV_STB + , &i2c_bus +#endif + )) < 0) { + snd_card_free(card); + return err; + } + iwcard->gus_status_reg = gus->gf1.reg_irqstat; + iwcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; + + snd_interwave_init(dev, gus); + snd_interwave_detect_memory(gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + + if (request_irq(irq, snd_interwave_interrupt, SA_INTERRUPT, "InterWave", (void *)iwcard)) { + snd_card_free(card); + snd_printk("unable to grab IRQ %d\n", irq); + return -EBUSY; + } + iwcard->irq = irq; + + if ((err = snd_cs4231_create(card, + gus->gf1.port + 0x10c, -1, irq, + dma2 < 0 ? dma1 : dma2, dma1, + CS4231_HW_INTERWAVE, + CS4231_HWSHARE_IRQ | + CS4231_HWSHARE_DMA1 | + CS4231_HWSHARE_DMA2, + &cs4231)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_pcm(cs4231, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); + strcat(pcm->name, " (chip)"); + if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } + if (snd_pcm_channels[dev] > 0) { + if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_interwave_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } +#ifdef SNDRV_STB + { + snd_ctl_elem_id_t id1, id2; + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id1.name, "Master Playback Switch"); + strcpy(id2.name, id1.name); + id2.index = 1; + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { + snd_card_free(card); + return err; + } + strcpy(id1.name, "Master Playback Volume"); + strcpy(id2.name, id1.name); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1)) < 0) { + snd_card_free(card); + return err; + } + } +#endif + + gus->uart_enable = snd_midi[dev]; + if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + +#ifndef SNDRV_STB + str = "AMD InterWave"; + if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) + str = "Dynasonic 3-D"; +#else + str = "InterWave STB"; +#endif + strcpy(card->driver, str); + strcpy(card->shortname, str); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d", + str, + gus->gf1.port, + irq, + dma1); + if (dma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", dma2); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + iwcard->cs4231 = cs4231; + iwcard->gus = gus; + snd_interwave_cards[dev++] = card; + return 0; +} + +static int __init snd_interwave_probe_legacy_port(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + snd_port[dev] = port; + res = snd_interwave_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +#ifdef __ISAPNP__ + +static int __init snd_interwave_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || !snd_isapnp[dev]) + continue; + snd_interwave_isapnp_cards[dev] = card; + snd_interwave_isapnp_id[dev] = id; + res = snd_interwave_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} + +#endif /* __ISAPNP__ */ + +static int __init alsa_card_interwave_init(void) +{ + int cards = 0; + static long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260, -1}; + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (!snd_interwave_probe(dev)) { + cards++; + continue; + } +#ifdef MODULE + snd_printk("InterWave soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]); +#endif + } + /* legacy auto configured cards */ + cards += snd_legacy_auto_probe(possible_ports, snd_interwave_probe_legacy_port); +#ifdef __ISAPNP__ + /* ISA PnP cards */ + cards += isapnp_probe_cards(snd_interwave_pnpids, snd_interwave_isapnp_detect); +#endif + + if (!cards) { +#ifdef MODULE + snd_printk("InterWave soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_interwave_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_interwave_cards[dev]); +} + +module_init(alsa_card_interwave_init) +module_exit(alsa_card_interwave_exit) + +#ifndef MODULE + +/* format is: snd-interwave=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port[,snd_port_tc],snd_irq, + snd_dma1,snd_dma2, + snd_joystick_dac,snd_midi, + snd_pcm_channels,snd_effect */ + +static int __init alsa_card_interwave_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && +#ifdef SNDRV_STB + get_option(&str,(int *)&snd_port_tc[nr_dev]) == 2 && +#endif + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2 && + get_option(&str,&snd_joystick_dac[nr_dev]) == 2 && + get_option(&str,&snd_midi[nr_dev]) == 2 && + get_option(&str,&snd_pcm_channels[nr_dev]) == 2 && + get_option(&str,&snd_effect[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +#ifndef SNDRV_STB +__setup("snd-interwave=", alsa_card_interwave_setup); +#else +__setup("snd-interwave-stb=", alsa_card_interwave_setup); +#endif + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/opl3sa2.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,951 @@ +/* + * Driver for Yamaha OPL3-SA[2,3] soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Yamaha OPL3SA2+"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Yamaha,YMF719E-S}," + "{Genius,Sound Maker 3DX}," + "{Yamaha,OPL3SA3}," + "{Intel,AL440LX sound}," + "{NeoMagic,MagicWave 3DX}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0xf86,0x370,0x100 */ +static long snd_sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static long snd_wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x530,0xe80,0xf40,0x604 */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x388 */ +static long snd_midi_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x330,0x300 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 0,1,3,5,9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_opl3sa3_ymode[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* 0,1,2,3 */ /*SL Added*/ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for OPL3-SA soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for OPL3-SA soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable OPL3-SA soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0xf86},{0x370},{0x100}},dialog:list"); +MODULE_PARM(snd_sb_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_sb_port, "SB port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_sb_port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260}},dialog:list"); +MODULE_PARM(snd_wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_wss_port, "WSS port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_wss_port, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list"); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388}},dialog:list"); +MODULE_PARM(snd_midi_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_midi_port, "MIDI port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_midi_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{0},{1},{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(snd_opl3sa3_ymode, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); /* SL Added */ +MODULE_PARM_DESC(snd_opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi."); +MODULE_PARM_SYNTAX(snd_opl3sa3_ymode, SNDRV_ENABLED ",allows:{{0,3}},dialog:list"); /* SL Added */ + +/* control ports */ +#define OPL3SA2_PM_CTRL 0x01 +#define OPL3SA2_SYS_CTRL 0x02 +#define OPL3SA2_IRQ_CONFIG 0x03 +#define OPL3SA2_IRQ_STATUS 0x04 +#define OPL3SA2_DMA_CONFIG 0x06 +#define OPL3SA2_MASTER_LEFT 0x07 +#define OPL3SA2_MASTER_RIGHT 0x08 +#define OPL3SA2_MIC 0x09 +#define OPL3SA2_MISC 0x0A + +/* opl3sa3 only */ +#define OPL3SA3_DGTL_DOWN 0x12 +#define OPL3SA3_ANLG_DOWN 0x13 +#define OPL3SA3_WIDE 0x14 +#define OPL3SA3_BASS 0x15 +#define OPL3SA3_TREBLE 0x16 + +/* power management bits */ +#define OPL3SA2_PM_ADOWN 0x20 +#define OPL3SA2_PM_PSV 0x04 +#define OPL3SA2_PM_PDN 0x02 +#define OPL3SA2_PM_PDX 0x01 + +#define OPL3SA2_PM_D0 0x00 +#define OPL3SA2_PM_D3 (OPL3SA2_PM_ADOWN|OPL3SA2_PM_PSV|OPL3SA2_PM_PDN|OPL3SA2_PM_PDX) + +typedef struct snd_opl3sa2 opl3sa2_t; +#define chip_t opl3sa2_t + +struct snd_opl3sa2 { + snd_card_t *card; + int version; /* 2 or 3 */ + unsigned long port; /* control port */ + struct resource *res_port; /* control port resource */ + int irq; + int single_dma; + spinlock_t reg_lock; + snd_hwdep_t *synth; + snd_rawmidi_t *rmidi; + cs4231_t *cs4231; +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#endif + unsigned char ctlregs[0x20]; + int ymode; /* SL added */ + snd_kcontrol_t *master_switch; + snd_kcontrol_t *master_volume; +#ifdef CONFIG_PM + struct pm_dev *pm_dev; + void (*cs4231_suspend)(cs4231_t *); + void (*cs4231_resume)(cs4231_t *); +#endif +}; + +static snd_card_t *snd_opl3sa2_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_opl3sa2_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_opl3sa2_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_OPL3SA2(_va, _vb, _vc, _device, _function) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _function), } \ + } + +static struct isapnp_card_id snd_opl3sa2_pnpids[] __devinitdata = { + /* Yamaha YMF719E-S (Genius Sound Maker 3DX) */ + ISAPNP_OPL3SA2('Y','M','H',0x0020,0x0021), + /* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */ + ISAPNP_OPL3SA2('Y','M','H',0x0030,0x0021), + /* ??? */ + ISAPNP_OPL3SA2('Y','M','H',0x0800,0x0021), + /* NeoMagic MagicWave 3DX */ + ISAPNP_OPL3SA2('N','M','X',0x2200,0x2210), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; + +ISAPNP_CARD_TABLE(snd_opl3sa2_pnpids); + +#endif /* __ISAPNP__ */ + + +/* read control port (w/o spinlock) */ +static unsigned char __snd_opl3sa2_read(opl3sa2_t *chip, unsigned char reg) +{ + unsigned char result; +#if 0 + outb(0x1d, port); /* password */ + printk("read [0x%lx] = 0x%x\n", port, inb(port)); +#endif + outb(reg, chip->port); /* register */ + result = inb(chip->port + 1); +#if 0 + printk("read [0x%lx] = 0x%x [0x%x]\n", port, result, inb(port)); +#endif + return result; +} + +/* read control port (with spinlock) */ +static unsigned char snd_opl3sa2_read(opl3sa2_t *chip, unsigned char reg) +{ + unsigned long flags; + unsigned char result; + + spin_lock_irqsave(&chip->reg_lock, flags); + result = __snd_opl3sa2_read(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +/* write control port (w/o spinlock) */ +static void __snd_opl3sa2_write(opl3sa2_t *chip, unsigned char reg, unsigned char value) +{ +#if 0 + outb(0x1d, port); /* password */ +#endif + outb(reg, chip->port); /* register */ + outb(value, chip->port + 1); + chip->ctlregs[reg] = value; +} + +/* write control port (with spinlock) */ +static void snd_opl3sa2_write(opl3sa2_t *chip, unsigned char reg, unsigned char value) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + __snd_opl3sa2_write(chip, reg, value); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static int __init snd_opl3sa2_detect(opl3sa2_t *chip) +{ + snd_card_t *card; + unsigned long port; + unsigned char tmp, tmp1; + char str[2]; + + card = chip->card; + port = chip->port; + if ((chip->res_port = request_region(port, 2, "OPL3-SA control")) == NULL) + return -EBUSY; + // snd_printk("REG 0A = 0x%x\n", snd_opl3sa2_read(chip, 0x0a)); + chip->version = 0; + tmp = snd_opl3sa2_read(chip, OPL3SA2_MISC); + if (tmp == 0xff) { + snd_printd("OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp); + return -ENODEV; + } + switch (tmp & 0x07) { + case 0x01: + chip->version = 2; /* YMF711 */ + break; + default: + chip->version = 3; + /* 0x02 - standard */ + /* 0x03 - YM715B */ + /* 0x04 - YM719 - OPL-SA4? */ + /* 0x05 - OPL3-SA3 - Libretto 100 */ + break; + } + str[0] = chip->version + '0'; + str[1] = 0; + strcat(card->shortname, str); + snd_opl3sa2_write(chip, OPL3SA2_MISC, tmp ^ 7); + if ((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC)) != tmp) { + snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1); + return -ENODEV; + } + /* try if the MIC register is accesible */ + tmp = snd_opl3sa2_read(chip, OPL3SA2_MIC); + snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x8a); + if (((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC)) & 0x9f) != 0x8a) { + snd_printd("OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1); + return -ENODEV; + } + snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x9f); + /* initialization */ + /* Power Management - full on */ + snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0); + if (chip->version > 2) { + /* ymode is bits 4&5 (of 0 to 7) on all but opl3sa2 versions */ + snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, (chip->ymode << 4)); + } else { + /* default for opl3sa2 versions */ + snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, 0x00); + } + snd_opl3sa2_write(chip, OPL3SA2_IRQ_CONFIG, 0x0d); /* Interrupt Channel Configuration - IRQ A = OPL3 + MPU + WSS */ + if (chip->single_dma) { + snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x03); /* DMA Configuration - DMA A = WSS-R + WSS-P */ + } else { + snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x21); /* DMA Configuration - DMA B = WSS-R, DMA A = WSS-P */ + } + snd_opl3sa2_write(chip, OPL3SA2_MISC, 0x80 | (tmp & 7)); /* Miscellaneous - default */ + if (chip->version > 2) { + snd_opl3sa2_write(chip, OPL3SA3_DGTL_DOWN, 0x00); /* Digital Block Partial Power Down - default */ + snd_opl3sa2_write(chip, OPL3SA3_ANLG_DOWN, 0x00); /* Analog Block Partial Power Down - default */ + } + return 0; +} + +static void snd_opl3sa2_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned short status; + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, dev_id, return); + + if (chip == NULL || chip->card == NULL) + return; + + status = snd_opl3sa2_read(chip, OPL3SA2_IRQ_STATUS); + + if (status & 0x20) + snd_opl3_interrupt(chip->synth); + + if ((status & 0x10) && chip->rmidi != NULL) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + + if (status & 0x07) /* TI,CI,PI */ + snd_cs4231_interrupt(irq, chip->cs4231, regs); + + if (status & 0x40) { /* hardware volume change */ + /* reading from Master Lch register at 0x07 clears this bit */ + snd_opl3sa2_read(chip, OPL3SA2_MASTER_RIGHT); + snd_opl3sa2_read(chip, OPL3SA2_MASTER_LEFT); + if (chip->master_switch && chip->master_volume) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); + } + } +} + +#define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_opl3sa2_info_single, \ + get: snd_opl3sa2_get_single, put: snd_opl3sa2_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_opl3sa2_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_opl3sa2_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->ctlregs[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +int snd_opl3sa2_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val, oval; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = chip->ctlregs[reg]; + val = (oval & ~(mask << shift)) | val; + change = val != oval; + __snd_opl3sa2_write(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_opl3sa2_info_double, \ + get: snd_opl3sa2_get_double, put: snd_opl3sa2_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +int snd_opl3sa2_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_opl3sa2_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->ctlregs[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->ctlregs[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +int snd_opl3sa2_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2, oval1, oval2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + oval1 = chip->ctlregs[left_reg]; + oval2 = chip->ctlregs[right_reg]; + val1 = (oval1 & ~(mask << shift_left)) | val1; + val2 = (oval2 & ~(mask << shift_right)) | val2; + change = val1 != oval1 || val2 != oval2; + __snd_opl3sa2_write(chip, left_reg, val1); + __snd_opl3sa2_write(chip, right_reg, val2); + } else { + oval1 = chip->ctlregs[left_reg]; + val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != oval1; + __snd_opl3sa2_write(chip, left_reg, val1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define OPL3SA2_CONTROLS (sizeof(snd_opl3sa2_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_opl3sa2_controls[] = { +OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1), +OPL3SA2_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1), +OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1), +OPL3SA2_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1) +}; + +#define OPL3SA2_TONE_CONTROLS (sizeof(snd_opl3sa2_tone_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_opl3sa2_tone_controls[] = { +OPL3SA2_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0), +OPL3SA2_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0), +OPL3SA2_DOUBLE("Tone Control - Treble", 0, 0x16, 0x16, 4, 0, 7, 0) +}; + +static void snd_opl3sa2_master_free(snd_kcontrol_t *kcontrol) +{ + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, _snd_kcontrol_chip(kcontrol), return); + chip->master_switch = NULL; + chip->master_volume = NULL; +} + +static int __init snd_opl3sa2_mixer(opl3sa2_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + snd_kcontrol_t *kctl; + int idx, err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUX0 to CD */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "CD Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "CD Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUX1 to FM */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "FM Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "FM Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* add OPL3SA2 controls */ + for (idx = 0; idx < OPL3SA2_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip))) < 0) + return err; + switch (idx) { + case 0: chip->master_switch = kctl; kctl->private_free = snd_opl3sa2_master_free; break; + case 1: chip->master_volume = kctl; kctl->private_free = snd_opl3sa2_master_free; break; + } + } + if (chip->version > 2) { + for (idx = 0; idx < OPL3SA2_TONE_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +/* Power Management support functions */ +#ifdef CONFIG_PM +static void snd_opl3sa2_suspend(opl3sa2_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + /* FIXME: is this order ok? */ + chip->cs4231_suspend(chip->cs4231); + snd_pcm_suspend_all(chip->cs4231->pcm); + + /* power down */ + snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D3); + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void snd_opl3sa2_resume(opl3sa2_t *chip) +{ + snd_card_t *card = chip->card; + int i; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + /* power up */ + snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0); + + /* restore registers */ + for (i = 2; i <= 0x0a; i++) { + if (i != OPL3SA2_IRQ_STATUS) + snd_opl3sa2_write(chip, i, chip->ctlregs[i]); + } + if (chip->version > 2) { + for (i = 0x12; i <= 0x16; i++) + snd_opl3sa2_write(chip, i, chip->ctlregs[i]); + } + /* restore cs4231 */ + chip->cs4231_resume(chip->cs4231); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +/* callback for control API */ +static int snd_opl3sa2_set_power_state(snd_card_t *card, unsigned int power_state) +{ + opl3sa2_t *chip = (opl3sa2_t *) card->power_state_private_data; + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_opl3sa2_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_opl3sa2_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +static int snd_opl3sa2_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, dev->data, return 0); + + switch (rqst) { + case PM_SUSPEND: + snd_opl3sa2_suspend(chip); + break; + case PM_RESUME: + snd_opl3sa2_resume(chip); + break; + } + return 0; +} + +#endif /* CONFIG_PM */ + +#ifdef __ISAPNP__ +static int __init snd_opl3sa2_isapnp(int dev, opl3sa2_t *chip) +{ + const struct isapnp_card_id *id = snd_opl3sa2_isapnp_id[dev]; + struct isapnp_card *card = snd_opl3sa2_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + chip->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (chip->dev->active) { + chip->dev = NULL; + return -EBUSY; + } + /* PnP initialization */ + pdev = chip->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + if (snd_sb_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_sb_port[dev], 16); + if (snd_wss_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_wss_port[dev], 8); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_fm_port[dev], 4); + if (snd_midi_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[3], snd_midi_port[dev], 2); + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[4], snd_port[dev], 2); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk("isapnp configure failure (out of resources?)\n"); + return -EBUSY; + } + snd_sb_port[dev] = pdev->resource[0].start; + snd_wss_port[dev] = pdev->resource[1].start; + snd_fm_port[dev] = pdev->resource[2].start; + snd_midi_port[dev] = pdev->resource[3].start; + snd_port[dev] = pdev->resource[4].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n", + snd_sb_port[dev], snd_wss_port[dev], snd_fm_port[dev], snd_midi_port[dev]); + snd_printdd("isapnp OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n", + snd_port[dev], snd_dma1[dev], snd_dma2[dev], snd_irq[dev]); + return 0; +} + +static void snd_opl3sa2_deactivate(opl3sa2_t *chip) +{ + if (chip->dev) { + chip->dev->deactivate(chip->dev); + chip->dev = NULL; + } +} +#endif /* __ISAPNP__ */ + +static int snd_opl3sa2_free(opl3sa2_t *chip) +{ +#ifdef __ISAPNP__ + snd_opl3sa2_deactivate(chip); +#endif +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_opl3sa2_dev_free(snd_device_t *device) +{ + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, device->device_data, return -ENXIO); + return snd_opl3sa2_free(chip); +} + +static int __init snd_opl3sa2_probe(int dev) +{ + int irq, dma1, dma2; + snd_card_t *card; + struct snd_opl3sa2 *chip; + cs4231_t *cs4231; + opl3_t *opl3; + static snd_device_ops_t ops = { + dev_free: snd_opl3sa2_dev_free, + }; + int err; + +#ifdef __ISAPNP__ + if (!snd_isapnp[dev]) { +#endif + if (snd_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_port\n"); + return -EINVAL; + } + if (snd_wss_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_wss_port\n"); + return -EINVAL; + } + if (snd_fm_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_fm_port\n"); + return -EINVAL; + } + if (snd_midi_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_midi_port\n"); + return -EINVAL; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + strcpy(card->driver, "OPL3SA2"); + strcpy(card->shortname, "Yamaha OPL3-SA2"); + chip = snd_magic_kcalloc(opl3sa2_t, 0, GFP_KERNEL); + if (chip == NULL) { + err = -ENOMEM; + goto __error; + } + chip->irq = -1; + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __error; +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && (err = snd_opl3sa2_isapnp(dev, chip)) < 0) + goto __error; +#endif + chip->ymode = snd_opl3sa3_ymode[dev] & 0x03 ; /* initialise this card from supplied (or default) parameter*/ + chip->card = card; + chip->port = snd_port[dev]; + irq = snd_irq[dev]; + dma1 = snd_dma1[dev]; + dma2 = snd_dma2[dev]; + if (dma2 < 0) + chip->single_dma = 1; + if ((err = snd_opl3sa2_detect(chip)) < 0) + goto __error; + if (request_irq(irq, snd_opl3sa2_interrupt, SA_INTERRUPT, "OPL3-SA2/3", (void *)chip)) { + err = -ENODEV; + goto __error; + } + chip->irq = irq; + if ((err = snd_cs4231_create(card, + snd_wss_port[dev] + 4, -1, + irq, dma1, dma2, + CS4231_HW_OPL3SA2, + CS4231_HWSHARE_IRQ, + &cs4231)) < 0) { + snd_printd("Oops, WSS not detected at 0x%lx\n", snd_wss_port[dev] + 4); + goto __error; + } + chip->cs4231 = cs4231; + if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) + goto __error; + if ((err = snd_cs4231_mixer(cs4231)) < 0) + goto __error; + if ((err = snd_opl3sa2_mixer(chip)) < 0) + goto __error; + if ((err = snd_cs4231_timer(cs4231, 0, NULL)) < 0) + goto __error; + if (snd_fm_port[dev] >= 0x340 && snd_fm_port[dev] < 0x400) { + if ((err = snd_opl3_create(card, snd_fm_port[dev], + snd_fm_port[dev] + 2, + OPL3_HW_OPL3, 0, &opl3)) < 0) + goto __error; + if ((err = snd_opl3_timer_new(opl3, 1, 2)) < 0) + goto __error; + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth)) < 0) + goto __error; + } + if (snd_midi_port[dev] >= 0x300 && snd_midi_port[dev] < 0x340) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2, + snd_midi_port[dev], 0, + irq, 0, &chip->rmidi)) < 0) + goto __error; + } +#ifdef CONFIG_PM + /* Power Management */ + chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_opl3sa2_pm_callback); + if (chip->pm_dev) { + chip->pm_dev->data = chip; + /* remember callbacks for cs4231 - they are called inside + * opl3sa2 pm callback + */ + chip->cs4231_suspend = chip->cs4231->suspend; + chip->cs4231_resume = chip->cs4231->resume; + /* now clear callbacks for cs4231 */ + chip->cs4231->suspend = NULL; + chip->cs4231->resume = NULL; + /* set control api callback */ + card->set_power_state = snd_opl3sa2_set_power_state; + card->power_state_private_data = chip; + } +#endif + + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + card->shortname, chip->port, irq, dma1); + if (dma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", dma2); + + if ((err = snd_card_register(card)) < 0) + goto __error; + + snd_opl3sa2_cards[dev] = card; + return 0; + + __error: + snd_card_free(card); + return err; +} + +#ifdef __ISAPNP__ +static int __init snd_opl3sa2_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_opl3sa2_isapnp_cards[dev] = card; + snd_opl3sa2_isapnp_id[dev] = id; + res = snd_opl3sa2_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_opl3sa2_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (snd_opl3sa2_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_opl3sa2_pnpids, snd_opl3sa2_isapnp_detect); +#endif + if (!cards) { +#ifdef MODULE + snd_printk("Yamaha OPL3-SA soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_opl3sa2_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_opl3sa2_cards[idx]); +} + +module_init(alsa_card_opl3sa2_init) +module_exit(alsa_card_opl3sa2_exit) + +#ifndef MODULE + +/* format is: snd-opl3sa2=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port,snd_sb_port,snd_wss_port,snd_fm_port, + snd_midi_port,snd_irq,snd_dma1,snd_dma2, + snd_opl3sa3_ymode */ + +static int __init alsa_card_opl3sa2_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_sb_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_wss_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_midi_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2 && + get_option(&str,&snd_opl3sa3_ymode[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +__setup("snd-opl3sa2=", alsa_card_opl3sa2_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/opti9xx/Makefile b/sound/isa/opti9xx/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/opti9xx/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,28 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _opti9xx.o + +list-multi := snd-opti92x-ad1848.o snd-opti92x-cs4231.o snd-opti93x.o + +snd-opti92x-ad1848-objs := opti92x-ad1848.o +snd-opti92x-cs4231-objs := opti92x-cs4231.o +snd-opti93x-objs := opti93x.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opti92x-cs4231.o +obj-$(CONFIG_SND_OPTI93X) += snd-opti93x.o + +include $(TOPDIR)/Rules.make + +snd-opti92x-ad1848.o: $(snd-opti92x-ad1848-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opti92x-ad1848-objs) + +snd-opti92x-cs4231.o: $(snd-opti92x-cs4231-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opti92x-cs4231-objs) + +snd-opti93x.o: $(snd-opti93x-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opti93x-objs) diff -Nru a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/opti9xx/opti92x-ad1848.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2218 @@ +/* + card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards. + Copyright (C) 1998-2000 by Massimo Piccioni + + Part of this code was developed at the Italian Ministry of Air Defence, + Sixth Division (oh, che pace ...), Rome. + + Thanks to Maria Grazia Pollarini, Salvatore Vassallo. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#ifdef CS4231 +#include +#else +#ifndef OPTi93X +#include +#else +#include +#include +#endif /* OPTi93X */ +#endif /* CS4231 */ +#include +#include +#define SNDRV_LEGACY_FIND_FREE_IOPORT +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); +#ifdef OPTi93X +MODULE_DESCRIPTION("OPTi93X"); +MODULE_DEVICES("{{OPTi,82C931/3}}"); +#else /* OPTi93X */ +#ifdef CS4231 +MODULE_DESCRIPTION("OPTi92X - CS4231"); +MODULE_DEVICES("{{OPTi,82C924 (CS4231)}," + "{OPTi,82C925 (CS4231)}}"); +#else /* CS4231 */ +MODULE_DESCRIPTION("OPTi92X - AD1848"); +MODULE_DEVICES("{{OPTi,82C924 (AD1848)}," + "{OPTi,82C925 (AD1848)}," + "{OAK,Mozart}}"); +#endif /* CS4231 */ +#endif /* OPTi93X */ + +static int snd_index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *snd_id = SNDRV_DEFAULT_STR1; /* ID for this card */ +//static int snd_enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ +static int snd_isapnp = 1; /* Enable ISA PnP detection */ +static long snd_port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */ +static long snd_mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */ +static long snd_fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */ +static int snd_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */ +static int snd_mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */ +static int snd_dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ +#if defined(CS4231) || defined(OPTi93X) +static int snd_dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ +#endif /* CS4231 || OPTi93X */ + +MODULE_PARM(snd_index, "i"); +MODULE_PARM_DESC(snd_index, "Index value for opti9xx based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "s"); +MODULE_PARM_DESC(snd_id, "ID string for opti9xx based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +//MODULE_PARM(snd_enable, "i"); +//MODULE_PARM_DESC(snd_enable, "Enable opti9xx soundcard."); +//MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_isapnp, "i"); +MODULE_PARM_DESC(snd_isapnp, "Enable ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +MODULE_PARM(snd_port, "l"); +MODULE_PARM_DESC(snd_port, "WSS port # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT_DESC); +MODULE_PARM(snd_mpu_port, "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC); +MODULE_PARM(snd_fm_port, "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT_DESC); +MODULE_PARM(snd_irq, "i"); +MODULE_PARM_DESC(snd_irq, "WSS irq # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 irq # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "i"); +MODULE_PARM_DESC(snd_dma1, "1st dma # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +#if defined(CS4231) || defined(OPTi93X) +MODULE_PARM(snd_dma2, "i"); +MODULE_PARM_DESC(snd_dma2, "2nd dma # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); +#endif /* CS4231 || OPTi93X */ + +#define OPTi9XX_HW_DETECT 0 +#define OPTi9XX_HW_82C928 1 +#define OPTi9XX_HW_82C929 2 +#define OPTi9XX_HW_82C924 3 +#define OPTi9XX_HW_82C925 4 +#define OPTi9XX_HW_82C930 5 +#define OPTi9XX_HW_82C931 6 +#define OPTi9XX_HW_82C933 7 +#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933 + +#define OPTi9XX_MC_REG(n) n + +typedef struct _snd_opti9xx opti9xx_t; + +#ifdef OPTi93X + +#define OPTi93X_INDEX 0x00 +#define OPTi93X_DATA 0x01 +#define OPTi93X_STATUS 0x02 +#define OPTi93X_DDATA 0x03 +#define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r) + +#define OPTi93X_MIXOUT_LEFT 0x00 +#define OPTi93X_MIXOUT_RIGHT 0x01 +#define OPTi93X_CD_LEFT_INPUT 0x02 +#define OPTi93X_CD_RIGHT_INPUT 0x03 +#define OPTi930_AUX_LEFT_INPUT 0x04 +#define OPTi930_AUX_RIGHT_INPUT 0x05 +#define OPTi931_FM_LEFT_INPUT 0x04 +#define OPTi931_FM_RIGHT_INPUT 0x05 +#define OPTi93X_DAC_LEFT 0x06 +#define OPTi93X_DAC_RIGHT 0x07 +#define OPTi93X_PLAY_FORMAT 0x08 +#define OPTi93X_IFACE_CONF 0x09 +#define OPTi93X_PIN_CTRL 0x0a +#define OPTi93X_ERR_INIT 0x0b +#define OPTi93X_ID 0x0c +#define OPTi93X_PLAY_UPR_CNT 0x0e +#define OPTi93X_PLAY_LWR_CNT 0x0f +#define OPTi931_AUX_LEFT_INPUT 0x10 +#define OPTi931_AUX_RIGHT_INPUT 0x11 +#define OPTi93X_LINE_LEFT_INPUT 0x12 +#define OPTi93X_LINE_RIGHT_INPUT 0x13 +#define OPTi93X_MIC_LEFT_INPUT 0x14 +#define OPTi93X_MIC_RIGHT_INPUT 0x15 +#define OPTi93X_OUT_LEFT 0x16 +#define OPTi93X_OUT_RIGHT 0x17 +#define OPTi93X_CAPT_FORMAT 0x1c +#define OPTi93X_CAPT_UPR_CNT 0x1e +#define OPTi93X_CAPT_LWR_CNT 0x1f + +#define OPTi93X_TRD 0x20 +#define OPTi93X_MCE 0x40 +#define OPTi93X_INIT 0x80 + +#define OPTi93X_MIXOUT_MIC_GAIN 0x20 +#define OPTi93X_MIXOUT_LINE 0x00 +#define OPTi93X_MIXOUT_CD 0x40 +#define OPTi93X_MIXOUT_MIC 0x80 +#define OPTi93X_MIXOUT_MIXER 0xc0 + +#define OPTi93X_STEREO 0x10 +#define OPTi93X_LINEAR_8 0x00 +#define OPTi93X_ULAW_8 0x20 +#define OPTi93X_LINEAR_16_LIT 0x40 +#define OPTi93X_ALAW_8 0x60 +#define OPTi93X_ADPCM_16 0xa0 +#define OPTi93X_LINEAR_16_BIG 0xc0 + +#define OPTi93X_CAPTURE_PIO 0x80 +#define OPTi93X_PLAYBACK_PIO 0x40 +#define OPTi93X_AUTOCALIB 0x08 +#define OPTi93X_SINGLE_DMA 0x04 +#define OPTi93X_CAPTURE_ENABLE 0x02 +#define OPTi93X_PLAYBACK_ENABLE 0x01 + +#define OPTi93X_IRQ_ENABLE 0x02 + +#define OPTi93X_DMA_REQUEST 0x10 +#define OPTi93X_CALIB_IN_PROGRESS 0x20 + +#define OPTi93X_IRQ_PLAYBACK 0x04 +#define OPTi93X_IRQ_CAPTURE 0x08 + + +typedef struct _snd_opti93x opti93x_t; + +struct _snd_opti93x { + unsigned long port; + struct resource *res_port; + int irq; + int dma1; + int dma2; + + opti9xx_t *chip; + unsigned short hardware; + unsigned char image[32]; + + unsigned char mce_bit; + unsigned short mode; + int mute; + + spinlock_t lock; + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p_dma_size; + unsigned int c_dma_size; +}; + +#define OPTi93X_MODE_NONE 0x00 +#define OPTi93X_MODE_PLAY 0x01 +#define OPTi93X_MODE_CAPTURE 0x02 +#define OPTi93X_MODE_OPEN (OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE) + +#endif /* OPTi93X */ + +struct _snd_opti9xx { + unsigned short hardware; + unsigned char password; + char name[7]; + + unsigned long mc_base; + struct resource *res_mc_base; + unsigned long mc_base_size; +#ifdef OPTi93X + unsigned long mc_indir_index; +#endif /* OPTi93X */ + unsigned long pwd_reg; + + spinlock_t lock; + + long wss_base; + int irq; + int dma1; +#if defined(CS4231) || defined(OPTi93X) + int dma2; +#endif /* CS4231 || OPTi93X */ + + long fm_port; + + long mpu_port; + int mpu_irq; + +#if defined(OPTi93X) + opti93x_t *opti93x; +#elif defined(CS4231) + cs4231_t *cs4231; +#else + ad1848_t *ad1848; +#endif /* AD1848 */ + snd_rawmidi_t *rmidi; +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_opti9xx_card = SNDRV_DEFAULT_PTR1; + +#ifdef __ISAPNP__ + +#define ISAPNP_OPTI9XX(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs: { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \ + ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \ + } + +static struct isapnp_card_id snd_card_opti9xx_pnpids[] = { +#ifndef OPTi93X + /* OPTi 82C924 */ + ISAPNP_OPTI9XX('O','P','T',0x0924,'O','P','T',0x0000,0x0002), + /* OPTi 82C925 */ + ISAPNP_OPTI9XX('O','P','T',0x0925,'O','P','T',0x9250,0x0002), +#else + /* OPTi 82C931/3 */ + ISAPNP_OPTI9XX('O','P','T',0x0931,'O','P','T',0x9310,0x0002), +#endif /* OPTi93X */ + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_card_opti9xx_pnpids); + +#endif /* __ISAPNP__ */ + +#ifdef OPTi93X +#define DRIVER_NAME "snd-card-opti93x" +#else +#define DRIVER_NAME "snd-card-opti92x" +#endif /* OPTi93X */ + +static char * snd_opti9xx_names[] = { + "unkown", + "82C928", "82C929", + "82C924", "82C925", + "82C930", "82C931", "82C933" +}; + + +static int __init snd_opti9xx_init(opti9xx_t *chip, unsigned short hardware) +{ + chip->hardware = hardware; + strcpy(chip->name, snd_opti9xx_names[hardware]); + + spin_lock_init(&chip->lock); + + chip->wss_base = -1; + chip->irq = -1; + chip->dma1 = -1; +#if defined(CS4231) || defined (OPTi93X) + chip->dma2 = -1; +#endif /* CS4231 || OPTi93X */ + chip->fm_port = -1; + chip->mpu_port = -1; + chip->mpu_irq = -1; + + switch (hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + chip->mc_base = 0xf8c; + chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3; + chip->pwd_reg = 3; + break; + + case OPTi9XX_HW_82C924: + case OPTi9XX_HW_82C925: + chip->mc_base = 0xf8c; + chip->password = 0xe5; + chip->pwd_reg = 3; + break; +#else /* OPTi93X */ + + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d; + chip->mc_indir_index = 0xe0e; + chip->password = 0xe4; + chip->pwd_reg = 0; + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", hardware); + return -ENODEV; + } + return 0; +} + +static unsigned char snd_opti9xx_read(opti9xx_t *chip, + unsigned char reg) +{ + unsigned long flags; + unsigned char retval = 0xff; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + + switch (chip->hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C924: + case OPTi9XX_HW_82C925: + if (reg > 7) { + outb(reg, chip->mc_base + 8); + outb(chip->password, chip->mc_base + chip->pwd_reg); + retval = inb(chip->mc_base + 9); + break; + } + + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + retval = inb(chip->mc_base + reg); + break; +#else /* OPTi93X */ + + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + outb(reg, chip->mc_indir_index); + outb(chip->password, chip->mc_base + chip->pwd_reg); + retval = inb(chip->mc_indir_index + 1); + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", chip->hardware); + } + + spin_unlock_irqrestore(&chip->lock, flags); + return retval; +} + +static void snd_opti9xx_write(opti9xx_t *chip, unsigned char reg, + unsigned char value) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + + switch (chip->hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C924: + case OPTi9XX_HW_82C925: + if (reg > 7) { + outb(reg, chip->mc_base + 8); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(value, chip->mc_base + 9); + break; + } + + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + outb(value, chip->mc_base + reg); + break; +#else /* OPTi93X */ + + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + outb(reg, chip->mc_indir_index); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(value, chip->mc_indir_index + 1); + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", chip->hardware); + } + + spin_unlock_irqrestore(&chip->lock, flags); +} + + +#define snd_opti9xx_write_mask(chip, reg, value, mask) \ + snd_opti9xx_write(chip, reg, \ + (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask))) + + +static int __init snd_opti9xx_configure(opti9xx_t *chip) +{ + unsigned char wss_base_bits; + unsigned char irq_bits; + unsigned char dma_bits; + unsigned char mpu_port_bits = 0; + unsigned char mpu_irq_bits; + unsigned long flags; + + switch (chip->hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C924: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); + + case OPTi9XX_HW_82C925: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); +#ifdef CS4231 + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); +#else + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); +#endif /* CS4231 */ + break; + + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); + /* + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae); + */ + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); +#ifdef CS4231 + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); +#else + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); +#endif /* CS4231 */ + break; + +#else /* OPTi93X */ + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | + (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04), + 0x34); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf); + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", chip->hardware); + return -EINVAL; + } + + switch (chip->wss_base) { + case 0x530: + wss_base_bits = 0x00; + break; + case 0x604: + wss_base_bits = 0x03; + break; + case 0xe80: + wss_base_bits = 0x01; + break; + case 0xf40: + wss_base_bits = 0x02; + break; + default: + snd_printk("WSS port 0x%lx not valid\n", chip->wss_base); + goto __skip_base; + } + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); + +__skip_base: + switch (chip->irq) { +#ifdef OPTi93X + case 5: + irq_bits = 0x05; + break; +#endif /* OPTi93X */ + case 7: + irq_bits = 0x01; + break; + case 9: + irq_bits = 0x02; + break; + case 10: + irq_bits = 0x03; + break; + case 11: + irq_bits = 0x04; + break; + default: + snd_printk("WSS irq # %d not valid\n", chip->irq); + goto __skip_resources; + } + + switch (chip->dma1) { + case 0: + dma_bits = 0x01; + break; + case 1: + dma_bits = 0x02; + break; + case 3: + dma_bits = 0x03; + break; + default: + snd_printk("WSS dma1 # %d not valid\n", chip->dma1); + goto __skip_resources; + } + +#if defined(CS4231) || defined(OPTi93X) + if (chip->dma1 == chip->dma2) { + snd_printk("don't want to share dmas\n"); + return -EBUSY; + } + + switch (chip->dma2) { + case 0: + case 1: + break; + default: + snd_printk("WSS dma2 # %d not valid\n", chip->dma2); + goto __skip_resources; + } + dma_bits |= 0x04; +#endif /* CS4231 || OPTi93X */ + + spin_lock_irqsave(&chip->lock, flags); + outb(irq_bits << 3 | dma_bits, chip->wss_base); + spin_unlock_irqrestore(&chip->lock, flags); + +__skip_resources: + if (chip->hardware > OPTi9XX_HW_82C928) { + switch (chip->mpu_port) { + case -1: + break; + case 0x300: + mpu_port_bits = 0x03; + break; + case 0x310: + mpu_port_bits = 0x02; + break; + case 0x320: + mpu_port_bits = 0x01; + break; + case 0x330: + mpu_port_bits = 0x00; + break; + default: + snd_printk("MPU-401 port 0x%lx not valid\n", + chip->mpu_port); + goto __skip_mpu; + } + + switch (chip->mpu_irq) { + case 5: + mpu_irq_bits = 0x02; + break; + case 7: + mpu_irq_bits = 0x03; + break; + case 9: + mpu_irq_bits = 0x00; + break; + case 10: + mpu_irq_bits = 0x01; + break; + default: + snd_printk("MPU-401 irq # %d not valid\n", + chip->mpu_irq); + goto __skip_mpu; + } + + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), + (chip->mpu_port == -1) ? 0x00 : + 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, + 0xf8); + } +__skip_mpu: + + return 0; +} + +#ifdef OPTi93X + +#define chip_t opti93x_t + +static unsigned char snd_opti93x_default_image[32] = +{ + 0x00, /* 00/00 - l_mixout_outctrl */ + 0x00, /* 01/01 - r_mixout_outctrl */ + 0x88, /* 02/02 - l_cd_inctrl */ + 0x88, /* 03/03 - r_cd_inctrl */ + 0x88, /* 04/04 - l_a1/fm_inctrl */ + 0x88, /* 05/05 - r_a1/fm_inctrl */ + 0x80, /* 06/06 - l_dac_inctrl */ + 0x80, /* 07/07 - r_dac_inctrl */ + 0x00, /* 08/08 - ply_dataform_reg */ + 0x00, /* 09/09 - if_conf */ + 0x00, /* 0a/10 - pin_ctrl */ + 0x00, /* 0b/11 - err_init_reg */ + 0x0a, /* 0c/12 - id_reg */ + 0x00, /* 0d/13 - reserved */ + 0x00, /* 0e/14 - ply_upcount_reg */ + 0x00, /* 0f/15 - ply_lowcount_reg */ + 0x88, /* 10/16 - reserved/l_a1_inctrl */ + 0x88, /* 11/17 - reserved/r_a1_inctrl */ + 0x88, /* 12/18 - l_line_inctrl */ + 0x88, /* 13/19 - r_line_inctrl */ + 0x88, /* 14/20 - l_mic_inctrl */ + 0x88, /* 15/21 - r_mic_inctrl */ + 0x80, /* 16/22 - l_out_outctrl */ + 0x80, /* 17/23 - r_out_outctrl */ + 0x00, /* 18/24 - reserved */ + 0x00, /* 19/25 - reserved */ + 0x00, /* 1a/26 - reserved */ + 0x00, /* 1b/27 - reserved */ + 0x00, /* 1c/28 - cap_dataform_reg */ + 0x00, /* 1d/29 - reserved */ + 0x00, /* 1e/30 - cap_upcount_reg */ + 0x00 /* 1f/31 - cap_lowcount_reg */ +}; + + +static int snd_opti93x_busy_wait(opti93x_t *chip) +{ + int timeout; + + for (timeout = 250; timeout-- > 0; udelay(10)) + if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT)) + return 0; + + snd_printk("chip still busy.\n"); + return -EBUSY; +} + +static unsigned char snd_opti93x_in(opti93x_t *chip, unsigned char reg) +{ + snd_opti93x_busy_wait(chip); + outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX)); + return inb(OPTi93X_PORT(chip, DATA)); +} + +static void snd_opti93x_out(opti93x_t *chip, unsigned char reg, + unsigned char value) +{ + snd_opti93x_busy_wait(chip); + outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX)); + outb(value, OPTi93X_PORT(chip, DATA)); +} + +static void snd_opti93x_out_image(opti93x_t *chip, unsigned char reg, + unsigned char value) +{ + snd_opti93x_out(chip, reg, chip->image[reg] = value); +} + +static void snd_opti93x_out_mask(opti93x_t *chip, unsigned char reg, + unsigned char mask, unsigned char value) +{ + snd_opti93x_out_image(chip, reg, + (chip->image[reg] & ~mask) | (value & mask)); +} + + +static void snd_opti93x_mce_up(opti93x_t *chip) +{ + snd_opti93x_busy_wait(chip); + + chip->mce_bit = OPTi93X_MCE; + if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)) + outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX)); +} + +static void snd_opti93x_mce_down(opti93x_t *chip) +{ + snd_opti93x_busy_wait(chip); + + chip->mce_bit = 0; + if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE) + outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX)); +} + +#define snd_opti93x_mute_reg(chip, reg, mute) \ + snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]); + +static void snd_opti93x_mute(opti93x_t *chip, int mute) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + mute = mute ? 1 : 0; + if (chip->mute == mute) { + spin_unlock_irqrestore(&chip->lock, flags); + return; + } + chip->mute = mute; + + snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute); + switch (chip->hardware) { + case OPTi9XX_HW_82C930: + snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute); + break; + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute); + } + snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute); + + spin_unlock_irqrestore(&chip->lock, flags); +} + + +static unsigned int snd_opti93x_get_count(unsigned char format, + unsigned int size) +{ + switch (format & 0xe0) { + case OPTi93X_LINEAR_16_LIT: + case OPTi93X_LINEAR_16_BIG: + size >>= 1; + break; + case OPTi93X_ADPCM_16: + return size >> 2; + } + return (format & OPTi93X_STEREO) ? (size >> 1) : size; +} + +unsigned int rates[] = { 5512, 6615, 8000, 9600, 11025, 16000, 18900, + 22050, 27428, 32000, 33075, 37800, 44100, 48000 }; +#define RATES sizeof(rates) / sizeof(rates[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + count: RATES, + list: rates, + mask: 0, +}; + +unsigned char bits[] = { 0x01, 0x0f, 0x00, 0x0e, 0x03, 0x02, 0x05, + 0x07, 0x04, 0x06, 0x0d, 0x09, 0x0b, 0x0c}; + +static unsigned char snd_opti93x_get_freq(unsigned int rate) +{ + int i; + + for (i = 0; i < RATES; i++) { + if (rate == rates[i]) + return bits[i]; + } + snd_BUG(); + return bits[RATES-1]; +} + +static unsigned char snd_opti93x_get_format(opti93x_t *chip, + unsigned int format, int channels) +{ + unsigned char retval = OPTi93X_LINEAR_8; + + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: + retval = OPTi93X_ULAW_8; + break; + case SNDRV_PCM_FORMAT_A_LAW: + retval = OPTi93X_ALAW_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + retval = OPTi93X_LINEAR_16_LIT; + break; + case SNDRV_PCM_FORMAT_S16_BE: + retval = OPTi93X_LINEAR_16_BIG; + break; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + retval = OPTi93X_ADPCM_16; + } + return (channels > 1) ? (retval | OPTi93X_STEREO) : retval; +} + + +static void snd_opti93x_playback_format(opti93x_t *chip, unsigned char fmt) +{ + unsigned long flags; + unsigned char mask; + + spin_lock_irqsave(&chip->lock, flags); + snd_opti93x_mute(chip, 1); + + snd_opti93x_mce_up(chip); + mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff; + snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt); + snd_opti93x_mce_down(chip); + + snd_opti93x_mute(chip, 0); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void snd_opti93x_capture_format(opti93x_t *chip, unsigned char fmt) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + snd_opti93x_mute(chip, 1); + + snd_opti93x_mce_up(chip); + if (!(chip->mode & OPTi93X_MODE_PLAY)) + snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt); + else + fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0; + snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt); + snd_opti93x_mce_down(chip); + + snd_opti93x_mute(chip, 0); + spin_unlock_irqrestore(&chip->lock, flags); +} + + +static int snd_opti93x_open(opti93x_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (chip->mode & mode) { + spin_unlock_irqrestore(&chip->lock, flags); + return -EAGAIN; + } + + if (!(chip->mode & OPTi93X_MODE_OPEN)) { + outb(0x00, OPTi93X_PORT(chip, STATUS)); + snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, + OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE); + chip->mode = mode; + } + else + chip->mode |= mode; + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static void snd_opti93x_close(opti93x_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + chip->mode &= ~mode; + if (chip->mode & OPTi93X_MODE_OPEN) { + spin_unlock_irqrestore(&chip->lock, flags); + return; + } + + snd_opti93x_mute(chip, 1); + + outb(0, OPTi93X_PORT(chip, STATUS)); + snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE, + ~OPTi93X_IRQ_ENABLE); + + snd_opti93x_mce_up(chip); + snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00); + snd_opti93x_mce_down(chip); + chip->mode = 0; + + snd_opti93x_mute(chip, 0); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int snd_opti93x_trigger(snd_pcm_substream_t *substream, + unsigned char what, int cmd) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == chip->playback_substream) { + what |= OPTi93X_PLAYBACK_ENABLE; + snd_pcm_trigger_done(s, substream); + } else if (s == chip->capture_substream) { + what |= OPTi93X_CAPTURE_ENABLE; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&chip->lock); + if (SNDRV_PCM_TRIGGER_START) + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what); + else + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00); + spin_unlock(&chip->lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int snd_opti93x_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + return snd_opti93x_trigger(substream, + OPTi93X_PLAYBACK_ENABLE, cmd); +} + +static int snd_opti93x_capture_trigger(snd_pcm_substream_t * substream, int cmd) +{ + return snd_opti93x_trigger(substream, + OPTi93X_CAPTURE_ENABLE, cmd); +} + +static int snd_opti93x_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + + +static int snd_opti93x_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + + +static int snd_opti93x_playback_prepare(snd_pcm_substream_t * substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned char format; + unsigned int count = snd_pcm_lib_period_bytes(substream); + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + + spin_lock_irqsave(&chip->lock, flags); + + chip->p_dma_size = size; + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, + OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO, + ~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO)); + + snd_dma_program(chip->dma1, runtime->dma_addr, size, + DMA_MODE_WRITE | DMA_AUTOINIT); + + format = snd_opti93x_get_freq(runtime->rate); + format |= snd_opti93x_get_format(chip, runtime->format, + runtime->channels); + snd_opti93x_playback_format(chip, format); + format = chip->image[OPTi93X_PLAY_FORMAT]; + + count = snd_opti93x_get_count(format, count) - 1; + snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count); + snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_opti93x_capture_prepare(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned char format; + unsigned int count = snd_pcm_lib_period_bytes(substream); + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + + spin_lock_irqsave(&chip->lock, flags); + + chip->c_dma_size = size; + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, + OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO, + (unsigned char)~(OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO)); + + snd_dma_program(chip->dma2, runtime->dma_addr, size, + DMA_MODE_READ | DMA_AUTOINIT); + + format = snd_opti93x_get_freq(runtime->rate); + format |= snd_opti93x_get_format(chip, runtime->format, + runtime->channels); + snd_opti93x_capture_format(chip, format); + format = chip->image[OPTi93X_CAPT_FORMAT]; + + count = snd_opti93x_get_count(format, count) - 1; + snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count); + snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_opti93x_playback_pointer(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_PLAYBACK_ENABLE)) + return 0; + + ptr = chip->p_dma_size - snd_dma_residue(chip->dma1); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_opti93x_capture_pointer(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE)) + return 0; + + ptr = chip->c_dma_size - snd_dma_residue(chip->dma2); + return bytes_to_frames(substream->runtime, ptr); +} + + +static void snd_opti93x_overrange(opti93x_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02)) + chip->capture_substream->runtime->overrange++; + + spin_unlock_irqrestore(&chip->lock, flags); +} + +void snd_opti93x_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + opti93x_t *codec = snd_magic_cast(opti93x_t, dev_id, return); + unsigned char status; + + status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11)); + if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream) + snd_pcm_period_elapsed(codec->playback_substream); + if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { + snd_opti93x_overrange(codec); + snd_pcm_period_elapsed(codec->capture_substream); + } + outb(0x00, OPTi93X_PORT(codec, STATUS)); +} + + +static snd_pcm_hardware_t snd_opti93x_playback = { + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5512, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_opti93x_capture = { + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_8000_48000, + rate_min: 5512, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_opti93x_playback_open(snd_pcm_substream_t *substream) +{ + int error; + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0) + return error; + snd_pcm_set_sync(substream); + chip->playback_substream = substream; + runtime->hw = snd_opti93x_playback; + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return error; +} + +static int snd_opti93x_capture_open(snd_pcm_substream_t *substream) +{ + int error; + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0) + return error; + runtime->hw = snd_opti93x_capture; + snd_pcm_set_sync(substream); + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return error; +} + +static int snd_opti93x_playback_close(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_opti93x_close(chip, OPTi93X_MODE_PLAY); + return 0; +} + +static int snd_opti93x_capture_close(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE); + return 0; +} + + +static void snd_opti93x_init(opti93x_t *chip) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&chip->lock, flags); + snd_opti93x_mce_up(chip); + + for (i = 0; i < 32; i++) + snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]); + + snd_opti93x_mce_down(chip); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int snd_opti93x_probe(opti93x_t *chip) +{ + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&chip->lock, flags); + val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f; + spin_unlock_irqrestore(&chip->lock, flags); + + return (val == 0x0a) ? 0 : -ENODEV; +} + +static int snd_opti93x_free(opti93x_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->dma1 >= 0) { + disable_dma(chip->dma1); + free_dma(chip->dma1); + } + if (chip->dma2 >= 0) { + disable_dma(chip->dma2); + free_dma(chip->dma2); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_opti93x_dev_free(snd_device_t *device) +{ + opti93x_t *chip = snd_magic_cast(opti93x_t, device->device_data, return -ENXIO); + return snd_opti93x_free(chip); +} + +static const char *snd_opti93x_chip_id(opti93x_t *codec) +{ + switch (codec->hardware) { + case OPTi9XX_HW_82C930: return "82C930"; + case OPTi9XX_HW_82C931: return "82C931"; + case OPTi9XX_HW_82C933: return "82C933"; + default: return "???"; + } +} + +int snd_opti93x_create(snd_card_t *card, opti9xx_t *chip, + int dma1, int dma2, + opti93x_t **rcodec) +{ + static snd_device_ops_t ops = { + dev_free: snd_opti93x_dev_free, + }; + int error; + opti93x_t *codec; + + *rcodec = NULL; + codec = snd_magic_kcalloc(opti93x_t, 0, GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + codec->irq = -1; + codec->dma1 = -1; + codec->dma2 = -1; + + if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) { + snd_opti93x_free(codec); + return -EBUSY; + } + if (request_dma(dma1, "OPTI93x - 1")) { + snd_opti93x_free(codec); + return -EBUSY; + } + codec->dma1 = chip->dma1; + if (request_dma(dma2, "OPTI93x - 2")) { + snd_opti93x_free(codec); + return -EBUSY; + } + codec->dma2 = chip->dma2; + + codec->card = card; + codec->port = chip->wss_base + 4; + codec->irq = chip->irq; + + spin_lock_init(&codec->lock); + codec->hardware = chip->hardware; + codec->chip = chip; + + if ((error = snd_opti93x_probe(codec))) { + snd_opti93x_free(codec); + return error; + } + + snd_opti93x_init(codec); + + /* Register device */ + if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_opti93x_free(codec); + return error; + } + + *rcodec = codec; + return 0; +} + +static snd_pcm_ops_t snd_opti93x_playback_ops = { + open: snd_opti93x_playback_open, + close: snd_opti93x_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_opti93x_hw_params, + hw_free: snd_opti93x_hw_free, + prepare: snd_opti93x_playback_prepare, + trigger: snd_opti93x_playback_trigger, + pointer: snd_opti93x_playback_pointer, +}; + +static snd_pcm_ops_t snd_opti93x_capture_ops = { + open: snd_opti93x_capture_open, + close: snd_opti93x_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_opti93x_hw_params, + hw_free: snd_opti93x_hw_free, + prepare: snd_opti93x_capture_prepare, + trigger: snd_opti93x_capture_trigger, + pointer: snd_opti93x_capture_pointer, +}; + +static void snd_opti93x_pcm_free(snd_pcm_t *pcm) +{ + opti93x_t *codec = snd_magic_cast(opti93x_t, pcm->private_data, return); + codec->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_opti93x_pcm(opti93x_t *codec, int device, snd_pcm_t **rpcm) +{ + int error; + snd_pcm_t *pcm; + + if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm))) + return error; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops); + + pcm->private_data = codec; + pcm->private_free = snd_opti93x_pcm_free; + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + strcpy(pcm->name, snd_opti93x_chip_id(codec)); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024); + + codec->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER part + */ + +static int snd_opti93x_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { + "Line1", "Aux", "Mic", "Mix" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_opti93x_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6; + ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6; + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_opti93x_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right; + int change; + + if (ucontrol->value.enumerated.item[0] > 3 || + ucontrol->value.enumerated.item[1] > 3) + return -EINVAL; + left = ucontrol->value.enumerated.item[0] << 6; + right = ucontrol->value.enumerated.item[1] << 6; + spin_lock_irqsave(&chip->lock, flags); + left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left; + right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right; + change = left != chip->image[OPTi93X_MIXOUT_LEFT] || + right != chip->image[OPTi93X_MIXOUT_RIGHT]; + snd_opti93x_out(chip, OPTi93X_MIXOUT_LEFT, left); + snd_opti93x_out(chip, OPTi93X_MIXOUT_RIGHT, right); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#if 0 + +#define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_opti93x_info_single, \ + get: snd_opti93x_get_single, put: snd_opti93x_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_opti93x_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_opti93x_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_opti93x_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->lock, flags); + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + snd_opti93x_out(chip, reg, val); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#endif /* single */ + +#define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_opti93x_info_double, \ + get: snd_opti93x_get_double, put: snd_opti93x_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +#define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \ + do { xctl.private_value ^= 22; } while (0) +#define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \ + do { xctl.private_value &= ~0x0000ffff; \ + xctl.private_value |= left_reg | (right_reg << 8); } while (0) + +static int snd_opti93x_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_opti93x_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_opti93x_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->lock, flags); + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + snd_opti93x_out(chip, left_reg, val1); + snd_opti93x_out(chip, right_reg, val1); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define OPTi93X_CONTROLS (sizeof(snd_opti93x_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_opti93x_controls[] = { +OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 0, 0, 31, 1), +OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1), +OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 0), +OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 0, 0, 15, 1), +OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 0, 0, 15, 1), +OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 0, 0, 15, 1), +OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1), +OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 0, 0, 15, 1), +OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 0, 0, 15, 1), +OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_opti93x_info_mux, + get: snd_opti93x_get_mux, + put: snd_opti93x_put_mux, +} +}; + +int snd_opti93x_mixer(opti93x_t *chip) +{ + snd_card_t *card; + snd_kcontrol_new_t knew; + int err, idx; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, snd_opti93x_chip_id(chip)); + + for (idx = 0; idx < OPTi93X_CONTROLS; idx++) { + knew = snd_opti93x_controls[idx]; + if (chip->hardware == OPTi9XX_HW_82C930) { + if (strstr(knew.name, "FM")) /* skip FM controls */ + continue; + else if (strcmp(knew.name, "Mic Playback Volume")) + OPTi93X_DOUBLE_INVERT_INVERT(knew); + else if (strstr(knew.name, "Aux")) + OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT); + else if (strcmp(knew.name, "PCM Playback Volume")) + OPTi93X_DOUBLE_INVERT_INVERT(knew); + else if (strcmp(knew.name, "Master Playback Volume")) + OPTi93X_DOUBLE_INVERT_INVERT(knew); + } + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +#endif /* OPTi93X */ + +static int __init snd_card_opti9xx_detect(snd_card_t *card, opti9xx_t *chip) +{ + int i, err; + static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; + +#ifndef OPTi93X + for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) { + unsigned char value; + + if ((err = snd_opti9xx_init(chip, i)) < 0) + return err; + chip->mc_base_size = opti9xx_mc_size[i]; + if (check_region(chip->mc_base, chip->mc_base_size)) + continue; + + value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); + if ((value != 0xff) && (value != inb(chip->mc_base + 1))) + if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) + return 1; + } +#else /* OPTi93X */ + for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { + unsigned long flags; + unsigned char value; + + if ((err = snd_opti9xx_init(chip, i)) < 0) + return err; + chip->mc_base_size = opti9xx_mc_size[i]; + if (check_region(chip->mc_base, chip->mc_base_size)) + continue; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(((chip->mc_indir_index & (1 << 8)) >> 4) | + ((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base); + spin_unlock_irqrestore(&chip->lock, flags); + + value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); + snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); + if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) + return 1; + } +#endif /* OPTi93X */ + + return -ENODEV; +} + +#ifdef __ISAPNP__ +static int __init snd_card_opti9xx_isapnp(opti9xx_t *chip) +{ + struct isapnp_dev *pdev = NULL; + const struct isapnp_card_id *pid = snd_card_opti9xx_pnpids-1; + static struct isapnp_card *card = NULL; + + __again: + while (1) { + pid++; + if (pid->card_vendor == 0) + return -ENODEV; + if ((card = isapnp_find_card(pid->card_vendor, pid->card_device, card))) + break; + } + if (card == NULL) + return -ENODEV; + + chip->dev = isapnp_find_dev(card, pid->devs[0].vendor, pid->devs[0].function, NULL); + if (chip->dev == NULL) + goto __again; + + chip->devmpu = isapnp_find_dev(card, pid->devs[1].vendor, pid->devs[1].function, NULL); + + pdev = chip->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + +#ifdef OPTi93X + if (snd_port != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port + 4, 4); +#else + if ((pid->card_device != ISAPNP_DEVICE(0x0924)) && (snd_port != SNDRV_AUTO_PORT)) + isapnp_resource_change(&pdev->resource[1], snd_port, 4); +#endif /* OPTi93X */ + if (snd_irq != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq, 1); + if (snd_dma1 != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1, 1); +#if defined(CS4231) || defined(OPTi93X) + if (snd_dma2 != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2, 1); +#endif /* CS4231 || OPTi93X */ + if (snd_fm_port != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port, 4); + + if (pdev->activate(pdev) < 0) { + snd_printk("AUDIO isapnp configure failure\n"); + return -EBUSY; + } + +#ifdef OPTi93X + snd_port = pdev->resource[0].start - 4; + snd_fm_port = pdev->resource[1].start; +#else + if (pid->card_device != ISAPNP_DEVICE(0x0924)) + snd_port = pdev->resource[1].start; + snd_fm_port = pdev->resource[2].start; +#endif /* OPTi93X */ + snd_irq = pdev->irq_resource[0].start; + snd_dma1 = pdev->dma_resource[0].start; +#if defined(CS4231) || defined(OPTi93X) + snd_dma2 = pdev->dma_resource[1].start; +#endif /* CS4231 || OPTi93X */ + + pdev = chip->devmpu; + if (pdev == NULL || pdev->prepare(pdev) < 0) { + snd_mpu_port = -1; + chip->devmpu = NULL; + return pid->card_device; + } + + if (snd_mpu_port != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port, 2); + if (snd_mpu_irq != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq, 1); + + if (pdev->activate(pdev) < 0) { + snd_printk("MPU-401 isapnp configure failure\n"); + snd_mpu_port = -1; + chip->devmpu = NULL; + } else { + snd_mpu_port = pdev->resource[0].start; + snd_mpu_irq = pdev->irq_resource[0].start; + } + return pid->card_device; +} + +static void snd_card_opti9xx_deactivate(opti9xx_t *chip) +{ + if (chip->dev) + chip->dev->deactivate(chip->dev); + if (chip->devmpu) + chip->devmpu->deactivate(chip->devmpu); +} +#endif /* __ISAPNP__ */ + +#if 0 +static int __init snd_card_opti9xx_resources(struct snd_card_opti9xx *chip, + snd_card_t *card) +{ + int error, i, pnp = 0; + +#ifdef __ISAPNP__ + pnp = chip->dev != NULL; +#endif /* __ISAPNP__ */ + +#ifndef OPTi93X + if (chip->chip->hardware == OPTi9XX_HW_82C928) + snd_mpu_port = -1; +#endif /* OPTi93X */ + error = 0; + if (!pnp && (snd_mpu_port == SNDRV_DEFAULT_PORT1)) { + for (i = 0; possible_mpu_ports[i] != -1; i++) + if (!snd_register_ioport(card, possible_mpu_ports[i], 2, + DRIVER_NAME" - MPU-401", NULL)) { + snd_mpu_port = possible_mpu_ports[i]; + break; + } + if (snd_mpu_port == SNDRV_DEFAULT_PORT1) + error = -EBUSY; + } + else + error = (snd_mpu_port == -1) ? -ENODEV : + snd_register_ioport(card, snd_mpu_port, 2, + DRIVER_NAME" - MPU-401", NULL); + if (error) + chip->chip->mpu_port = -1; + else if (pnp && (snd_irq == snd_mpu_irq)) + chip->chip->mpu_irq = snd_mpu_irq; + else if (!snd_register_interrupt(card, + DRIVER_NAME" - MPU-401", + snd_mpu_irq, SNDRV_IRQ_TYPE_ISA, + snd_card_opti9xx_mpu_interrupt, chip, + pnp ? no_alternatives : possible_mpu_irqs, + &chip->mpuirqptr)) { + chip->chip->mpu_port = snd_mpu_port; + chip->chip->mpu_irq = chip->mpuirqptr->irq; + } + else + chip->chip->mpu_port = -1; + + if (!pnp && (snd_port == SNDRV_DEFAULT_PORT1)) { + for (i = 0; possible_ports[i] != -1; i++) + if (!snd_register_ioport(card, possible_ports[i], 8, + DRIVER_NAME" - WSS", NULL)) { + snd_port = possible_ports[i]; + break; + } + if (snd_port == SNDRV_DEFAULT_PORT1) + return -EBUSY; + } + else if ((error = snd_register_ioport(card, snd_port, 8, + DRIVER_NAME" - WSS", NULL)) < 0) + return error; + chip->chip->wss_base = snd_port; + if ((error = snd_register_interrupt(card, DRIVER_NAME" - WSS", + snd_irq, SNDRV_IRQ_TYPE_ISA, + snd_card_opti9xx_interrupt, chip, + pnp ? no_alternatives : possible_irqs, + &chip->irqptr)) < 0) + return error; + chip->chip->irq = chip->irqptr->irq; + if ((error = snd_register_dma_channel(card, +#if defined(CS4231) || defined(OPTi93X) + DRIVER_NAME" - WSS playback", +#else + DRIVER_NAME" - WSS", +#endif /* CS4231 || OPTi93X */ + snd_dma1, SNDRV_DMA_TYPE_ISA, snd_dma1_size, + pnp ? no_alternatives : possible_dma1s, + &chip->dma1ptr)) < 0) + return error; + chip->chip->dma1 = chip->dma1ptr->dma; +#if defined(CS4231) || defined(OPTi93X) + if ((error = snd_register_dma_channel(card, DRIVER_NAME" - WSS capture", + snd_dma2, SNDRV_DMA_TYPE_ISA, snd_dma2_size, + pnp ? no_alternatives : + possible_dma2s[chip->dma1ptr->dma], + &chip->dma2ptr)) < 0) + return error; + chip->chip->dma2 = chip->dma2ptr->dma; +#endif /* CS4231 || OPTi93X */ + + if (snd_register_ioport(card, + pnp ? snd_fm_port : snd_fm_port = 0x388, 4, + DRIVER_NAME" - OPL", NULL) < 0) + snd_fm_port = -1; + chip->chip->fm_port = snd_fm_port; + + return 0; +} +#endif + +static void snd_card_opti9xx_free(snd_card_t *card) +{ + opti9xx_t *chip = (opti9xx_t *)card->private_data; + + if (chip) { +#ifdef __ISAPNP__ + snd_card_opti9xx_deactivate(chip); +#endif /* __ISAPNP__ */ + if (chip->res_mc_base) { + release_resource(chip->res_mc_base); + kfree_nocheck(chip->res_mc_base); + } + } +} + +static int __init snd_card_opti9xx_probe(void) +{ + static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; + static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x310, -1}; +#ifdef OPTi93X + static int possible_irqs[] = {5, 9, 10, 11, 7, -1}; +#else + static int possible_irqs[] = {9, 10, 11, 7, -1}; +#endif /* OPTi93X */ + static int possible_mpu_irqs[] = {5, 9, 10, 7, -1}; + static int possible_dma1s[] = {3, 1, 0, -1}; +#if defined(CS4231) || defined(OPTi93X) + static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}}; +#endif /* CS4231 || OPTi93X */ + int error; + opti9xx_t *chip; +#if defined(OPTi93X) + opti93x_t *codec; +#elif defined(CS4231) + cs4231_t *codec; + snd_timer_t *timer; +#else + ad1848_t *codec; +#endif + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_hwdep_t *synth; +#ifdef __ISAPNP__ + int hw; +#endif /* __ISAPNP__ */ + + if (!(card = snd_card_new(snd_index, snd_id, THIS_MODULE, + sizeof(opti9xx_t)))) + return -ENOMEM; + card->private_free = snd_card_opti9xx_free; + chip = (opti9xx_t *)card->private_data; + +#ifdef __ISAPNP__ + if (snd_isapnp && (hw = snd_card_opti9xx_isapnp(chip)) > 0) { + switch (hw) { + case ISAPNP_DEVICE(0x0924): + hw = OPTi9XX_HW_82C924; + break; + case ISAPNP_DEVICE(0x0925): + hw = OPTi9XX_HW_82C925; + break; + case ISAPNP_DEVICE(0x0931): + hw = OPTi9XX_HW_82C931; + break; + default: + snd_card_free(card); + return -ENODEV; + } + + if ((error = snd_opti9xx_init(chip, hw))) { + snd_card_free(card); + return error; + } + if (hw <= OPTi9XX_HW_82C930) + chip->mc_base -= 0x80; + } else { +#endif /* __ISAPNP__ */ + if ((error = snd_card_opti9xx_detect(card, chip)) < 0) { + snd_card_free(card); + return error; + } +#ifdef __ISAPNP__ + } +#endif /* __ISAPNP__ */ + + if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) { + snd_card_free(card); + return -ENOMEM; + } + + chip->wss_base = snd_port; + chip->fm_port = snd_fm_port; + chip->mpu_port = snd_mpu_port; + chip->irq = snd_irq; + chip->mpu_irq = snd_mpu_irq; + chip->dma1 = snd_dma1; +#if defined(CS4231) || defined(OPTi93X) + chip->dma2 = snd_dma2; +#endif + +#ifdef __ISAPNP__ + if (!snd_isapnp) { +#endif + if (chip->wss_base == SNDRV_AUTO_PORT) { + if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free WSS port\n"); + return -EBUSY; + } + } + if (chip->mpu_port == SNDRV_AUTO_PORT) { + if ((chip->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free MPU401 port\n"); + return -EBUSY; + } + } + if (chip->irq == SNDRV_AUTO_IRQ) { + if ((chip->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + if (chip->mpu_irq == SNDRV_AUTO_IRQ) { + if ((chip->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free MPU401 IRQ\n"); + return -EBUSY; + } + } + if (chip->dma1 == SNDRV_AUTO_DMA) { + if ((chip->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } +#if defined(CS4231) || defined(OPTi93X) + if (chip->dma2 == SNDRV_AUTO_DMA) { + if ((chip->dma2 = snd_legacy_find_free_dma(possible_dma2s[chip->dma1 % 4])) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } +#endif + +#ifdef __ISAPNP__ + } +#endif + + if ((error = snd_opti9xx_configure(chip))) { + snd_card_free(card); + return error; + } + +#if defined(OPTi93X) + if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec))) { + snd_card_free(card); + return error; + } + if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opti93x_mixer(codec)) < 0) { + snd_card_free(card); + return error; + } +#elif defined(CS4231) + if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1, + chip->irq, chip->dma1, chip->dma2, + CS4231_HW_DETECT, + 0, + &codec)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_mixer(codec)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) { + snd_card_free(card); + return error; + } +#else + if ((error = snd_ad1848_create(card, chip->wss_base + 4, + chip->irq, chip->dma1, + AD1848_HW_DETECT, &codec)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_ad1848_mixer(codec)) < 0) { + snd_card_free(card); + return error; + } +#endif + + if (chip->mpu_port <= 0) + rmidi = NULL; + else + if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + chip->mpu_port, 0, chip->mpu_irq, SA_INTERRUPT, + &rmidi))) + snd_printk("no MPU-401 device at 0x%lx?\n", chip->mpu_port); + + if (chip->fm_port > 0) { + opl3_t *opl3; + if (snd_opl3_create(card, + chip->fm_port, + chip->fm_port + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + snd_printk("no OPL device at 0x%lx-0x%lx\n", + chip->fm_port, chip->fm_port + 4 - 1); + } else { + if ((error = snd_opl3_timer_new(opl3, +#ifdef CS4231 + 1, 2)) < 0) { +#else + 0, 1)) < 0) { +#endif /* CS4231 */ + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, &synth)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, chip->name); + sprintf(card->shortname, "OPTi %s", card->driver); +#if defined(CS4231) || defined(OPTi93X) + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d&%d", + card->shortname, pcm->name, chip->wss_base + 4, + chip->irq, chip->dma1, chip->dma2); +#else + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", + card->shortname, pcm->name, chip->wss_base + 4, + chip->irq, chip->dma1); +#endif /* CS4231 || OPTi93X */ + if ((error = snd_card_register(card))) { + snd_card_free(card); + return error; + } + snd_opti9xx_card = card; + return 0; +} + +static int __init alsa_card_opti9xx_init(void) +{ + int error; + + if ((error = snd_card_opti9xx_probe())) { +#ifdef MODULE +#ifdef OPTi93X + snd_printk("no OPTi 82C93x soundcard found\n"); +#else + snd_printk("no OPTi 82C92x soundcard found\n"); +#endif /* OPTi93X */ +#endif + return error; + } + return 0; +} + +static void __exit alsa_card_opti9xx_exit(void) +{ + if (snd_opti9xx_card) + snd_card_free(snd_opti9xx_card); +} + +module_init(alsa_card_opti9xx_init) +module_exit(alsa_card_opti9xx_exit) + +#ifndef MODULE + +/* format is: snd-opti9xx=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port,snd_mpu_port,snd_fm_port, + snd_irq,snd_mpu_irq, + snd_dma1,[snd_dma2] */ + +static int __init alsa_card_opti9xx_setup(char *str) +{ + int __attribute__ ((__unused__)) enable = 1; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + (void)(get_option(&str,&enable) == 2 && + get_option(&str,&snd_index) == 2 && + get_id(&str,&snd_id) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port) == 2 && + get_option(&str,(int *)&snd_mpu_port) == 2 && + get_option(&str,(int *)&snd_fm_port) == 2 && + get_option(&str,&snd_irq) == 2 && + get_option(&str,&snd_mpu_irq) == 2 && + get_option(&str,&snd_dma1) == 2 +#if defined(CS4231) || defined(OPTi93X) + && + get_option(&str,&snd_dma2) == 2 +#endif + ); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp = pnp; +#endif + return 1; +} + +#if defined(OPTi93X) +__setup("snd-opti93x=", alsa_card_opti9xx_setup); +#elif defined(CS4231) +__setup("snd-opti92x-cs4231=", alsa_card_opti9xx_setup); +#else +__setup("snd-opti92x-ad1848=", alsa_card_opti9xx_setup); +#endif + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/opti9xx/opti92x-cs4231.c b/sound/isa/opti9xx/opti92x-cs4231.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/opti9xx/opti92x-cs4231.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,2 @@ +#define CS4231 +#include "opti92x-ad1848.c" diff -Nru a/sound/isa/opti9xx/opti93x.c b/sound/isa/opti9xx/opti93x.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/opti9xx/opti93x.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,3 @@ +#define OPTi93X +#include "opti92x-ad1848.c" + diff -Nru a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,67 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _sb.o + +list-multi := snd-sb-common.o snd-sb8-dsp.o snd-sb16-dsp.o snd-sb16-csp.o \ + snd-sb8.o snd-sb16.o snd-sbawe.o snd-emu8000-synth.o snd-es968.o + +export-objs := emu8000.o emu8000_synth.o sb_common.o sb8_main.o sb16_main.o sb16_csp.o + +snd-sb-common-objs := sb_common.o sb_mixer.o +snd-sb8-dsp-objs := sb8_main.o sb8_midi.o +snd-sb16-dsp-objs := sb16_main.o +snd-sb16-csp-objs := sb16_csp.o +snd-sb8-objs := sb8.o +snd-sb16-objs := sb16.o +snd-sbawe-objs := sbawe.o emu8000.o +snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o +snd-es968-objs := es968.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_DT0197H) += snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o +ifeq ($(CONFIG_SND_SB16_CSP),y) + obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o + obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o +endif +obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_SBAWE) += snd-emu8000-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-sb-common.o: $(snd-sb-common-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb-common-objs) + +snd-sb8-dsp.o: $(snd-sb8-dsp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb8-dsp-objs) + +snd-sb16-dsp.o: $(snd-sb16-dsp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-dsp-objs) + +snd-sb16-csp.o: $(snd-sb16-csp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-csp-objs) + +snd-sb8.o: $(snd-sb8-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb8-objs) + +snd-sb16.o: $(snd-sb16-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-objs) + +snd-sbawe.o: $(snd-sbawe-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sbawe-objs) + +snd-emu8000-synth.o: $(snd-emu8000-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emu8000-synth-objs) + +snd-es968.o: $(snd-es968-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es968-objs) diff -Nru a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/emu8000.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1191 @@ +/* + * Copyright (c) by Jaroslav Kysela + * and (c) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * Routines for control of EMU8000 chip + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe"); +MODULE_DESCRIPTION("Routines for control of EMU8000 chip"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#endif + +/* + * emu8000 register controls + */ + +/* + * The following routines read and write registers on the emu8000. They + * should always be called via the EMU8000*READ/WRITE macros and never + * directly. The macros handle the port number and command word. + */ +/* Write a word */ +void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + outw((unsigned short)val, port); /* Send data */ + spin_unlock_irqrestore(&emu->reg_lock, flags); +} + +/* Read a word */ +unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, unsigned int reg) +{ + unsigned short res; + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + res = inw(port); /* Read data */ + spin_unlock_irqrestore(&emu->reg_lock, flags); + return res; +} + +/* Write a double word */ +void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + outw((unsigned short)val, port); /* Send low word of data */ + outw((unsigned short)(val>>16), port+2); /* Send high word of data */ + spin_unlock_irqrestore(&emu->reg_lock, flags); +} + +/* Read a double word */ +unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, unsigned int reg) +{ + unsigned short low; + unsigned int res; + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + low = inw(port); /* Read low word of data */ + res = low + (inw(port+2) << 16); + spin_unlock_irqrestore(&emu->reg_lock, flags); + return res; +} + +/* + * Set up / close a channel to be used for DMA. + */ +/*exported*/ void +snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode) +{ + if (mode == EMU8000_RAM_CLOSE) { + EMU8000_CCCA_WRITE(emu, ch, 0); + EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F); + return; + } + EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); + EMU8000_VTFT_WRITE(emu, ch, 0); + EMU8000_CVCF_WRITE(emu, ch, 0); + EMU8000_PTRX_WRITE(emu, ch, 0x40000000); + EMU8000_CPF_WRITE(emu, ch, 0x40000000); + EMU8000_PSST_WRITE(emu, ch, 0); + EMU8000_CSL_WRITE(emu, ch, 0); + if (mode == EMU8000_RAM_WRITE) /* DMA write */ + EMU8000_CCCA_WRITE(emu, ch, 0x06000000); + else /* DMA read */ + EMU8000_CCCA_WRITE(emu, ch, 0x04000000); +} + +/* + */ +static void /*__init*/ +snd_emu8000_read_wait(emu8000_t *emu) +{ + while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } +} + +/* + */ +static void /*__init*/ +snd_emu8000_write_wait(emu8000_t *emu) +{ + while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } +} + +/* + * detect a card at the given port + */ +static int /*__init*/ +snd_emu8000_detect(emu8000_t *emu) +{ + /* Initialise */ + EMU8000_HWCF1_WRITE(emu, 0x0059); + EMU8000_HWCF2_WRITE(emu, 0x0020); + EMU8000_HWCF3_WRITE(emu, 0x0000); + /* Check for a recognisable emu8000 */ + /* + if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c) + return -ENODEV; + */ + if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058) + return -ENODEV; + if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003) + return -ENODEV; + + snd_printdd("EMU8000 [0x%lx]: Synth chip found\n", + emu->port1); + return 0; +} + + +/* + * intiailize audio channels + */ +static void /*__init*/ +init_audio(emu8000_t *emu) +{ + int ch; + + /* turn off envelope engines */ + for (ch = 0; ch < EMU8000_CHANNELS; ch++) + EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); + + /* reset all other parameters to zero */ + for (ch = 0; ch < EMU8000_CHANNELS; ch++) { + EMU8000_ENVVOL_WRITE(emu, ch, 0); + EMU8000_ENVVAL_WRITE(emu, ch, 0); + EMU8000_DCYSUS_WRITE(emu, ch, 0); + EMU8000_ATKHLDV_WRITE(emu, ch, 0); + EMU8000_LFO1VAL_WRITE(emu, ch, 0); + EMU8000_ATKHLD_WRITE(emu, ch, 0); + EMU8000_LFO2VAL_WRITE(emu, ch, 0); + EMU8000_IP_WRITE(emu, ch, 0); + EMU8000_IFATN_WRITE(emu, ch, 0); + EMU8000_PEFE_WRITE(emu, ch, 0); + EMU8000_FMMOD_WRITE(emu, ch, 0); + EMU8000_TREMFRQ_WRITE(emu, ch, 0); + EMU8000_FM2FRQ2_WRITE(emu, ch, 0); + EMU8000_PTRX_WRITE(emu, ch, 0); + EMU8000_VTFT_WRITE(emu, ch, 0); + EMU8000_PSST_WRITE(emu, ch, 0); + EMU8000_CSL_WRITE(emu, ch, 0); + EMU8000_CCCA_WRITE(emu, ch, 0); + } + + for (ch = 0; ch < EMU8000_CHANNELS; ch++) { + EMU8000_CPF_WRITE(emu, ch, 0); + EMU8000_CVCF_WRITE(emu, ch, 0); + } +} + + +/* + * initialize DMA address + */ +static void /*__init*/ +init_dma(emu8000_t *emu) +{ + EMU8000_SMALR_WRITE(emu, 0); + EMU8000_SMARR_WRITE(emu, 0); + EMU8000_SMALW_WRITE(emu, 0); + EMU8000_SMARW_WRITE(emu, 0); +} + +/* + * initialization arrays; from ADIP + */ +static unsigned short init1[128] /*__devinitdata*/ = { + 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, + 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, + 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, + 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, + + 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, + 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, + 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, + 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, + + 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, + 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, + 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, + 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, + + 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, + 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, + 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, + 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, +}; + +static unsigned short init2[128] /*__devinitdata*/ = { + 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, + 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, + 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, + 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, + + 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, + 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, + 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, + 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, + + 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, + 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, + 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, + 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, + + 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, + 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, + 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, + 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, +}; + +static unsigned short init3[128] /*__devinitdata*/ = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, + 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, + 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, + 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +static unsigned short init4[128] /*__devinitdata*/ = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, + 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, + 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, + 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +/* send an initialization array + * Taken from the oss driver, not obvious from the doc how this + * is meant to work + */ +static void /*__init*/ +send_array(emu8000_t *emu, unsigned short *data, int size) +{ + int i; + unsigned short *p; + + p = data; + for (i = 0; i < size; i++, p++) + EMU8000_INIT1_WRITE(emu, i, *p); + for (i = 0; i < size; i++, p++) + EMU8000_INIT2_WRITE(emu, i, *p); + for (i = 0; i < size; i++, p++) + EMU8000_INIT3_WRITE(emu, i, *p); + for (i = 0; i < size; i++, p++) + EMU8000_INIT4_WRITE(emu, i, *p); +} + +#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) + + +/* + * Send initialization arrays to start up, this just follows the + * initialisation sequence in the adip. + */ +static void /*__init*/ +init_arrays(emu8000_t *emu) +{ + send_array(emu, init1, NELEM(init1)/4); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((HZ * (44099 + 1024)) / 44100); /* wait for 1024 clocks */ + send_array(emu, init2, NELEM(init2)/4); + send_array(emu, init3, NELEM(init3)/4); + + EMU8000_HWCF4_WRITE(emu, 0); + EMU8000_HWCF5_WRITE(emu, 0x83); + EMU8000_HWCF6_WRITE(emu, 0x8000); + + send_array(emu, init4, NELEM(init4)/4); +} + + +#define UNIQUE_ID1 0xa5b9 +#define UNIQUE_ID2 0x9d53 + +/* + * Size the onboard memory. + * This is written so as not to need arbitary delays after the write. It + * seems that the only way to do this is to use the one channel and keep + * reallocating between read and write. + */ +static void /*__init*/ +size_dram(emu8000_t *emu) +{ + int i, size; + + if (emu->dram_checked) + return; + + size = 0; + + /* write out a magic number */ + snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE); + snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ); + EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET); + EMU8000_SMLD_WRITE(emu, UNIQUE_ID1); + snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */ + + while (size < EMU8000_MAX_DRAM) { + + size += 512 * 1024; /* increment 512kbytes */ + + /* Write a unique data on the test address. + * if the address is out of range, the data is written on + * 0x200000(=EMU8000_DRAM_OFFSET). Then the id word is + * changed by this data. + */ + /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/ + EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); + EMU8000_SMLD_WRITE(emu, UNIQUE_ID2); + snd_emu8000_write_wait(emu); + + /* + * read the data on the just written DRAM address + * if not the same then we have reached the end of ram. + */ + /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/ + EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); + /*snd_emu8000_read_wait(emu);*/ + EMU8000_SMLD_READ(emu); /* discard stale data */ + if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2) + break; /* we must have wrapped around */ + + snd_emu8000_read_wait(emu); + + /* + * If it is the same it could be that the address just + * wraps back to the beginning; so check to see if the + * initial value has been overwritten. + */ + EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET); + EMU8000_SMLD_READ(emu); /* discard stale data */ + if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1) + break; /* we must have wrapped around */ + snd_emu8000_read_wait(emu); + } + + /* wait until FULL bit in SMAxW register is false */ + for (i = 0; i < 10000; i++) { + if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0) + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } + snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE); + snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE); + + snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n", + emu->port1, size/1024); + + emu->mem_size = size; + emu->dram_checked = 1; +} + + +/* + * Initiailise the FM section. You have to do this to use sample RAM + * and therefore lose 2 voices. + */ +/*exported*/ void +snd_emu8000_init_fm(emu8000_t *emu) +{ + unsigned long flags; + + /* Initialize the last two channels for DRAM refresh and producing + the reverb and chorus effects for Yamaha OPL-3 synthesizer */ + + /* 31: FM left channel, 0xffffe0-0xffffe8 */ + EMU8000_DCYSUSV_WRITE(emu, 30, 0x80); + EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */ + EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24)); + EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8)); + EMU8000_CPF_WRITE(emu, 30, 0); + EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3); + + /* 32: FM right channel, 0xfffff0-0xfffff8 */ + EMU8000_DCYSUSV_WRITE(emu, 31, 0x80); + EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */ + EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24)); + EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8)); + EMU8000_CPF_WRITE(emu, 31, 0x8000); + EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3); + + snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0); + + spin_lock_irqsave(&emu->reg_lock, flags); + while (!(inw(EMU8000_PTR(emu)) & 0x1000)) + ; + while ((inw(EMU8000_PTR(emu)) & 0x1000)) + ; + spin_unlock_irqrestore(&emu->reg_lock, flags); + snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828); + /* this is really odd part.. */ + outb(0x3C, EMU8000_PTR(emu)); + outb(0, EMU8000_DATA1(emu)); + + /* skew volume & cutoff */ + EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF); + EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF); +} + + +/* + * The main initialization routine. + */ +static void /*__init*/ +snd_emu8000_init_hw(emu8000_t *emu) +{ + int i; + + emu->last_reg = 0xffff; /* reset the last register index */ + + /* initialize hardware configuration */ + EMU8000_HWCF1_WRITE(emu, 0x0059); + EMU8000_HWCF2_WRITE(emu, 0x0020); + + /* disable audio; this seems to reduce a clicking noise a bit.. */ + EMU8000_HWCF3_WRITE(emu, 0); + + /* initialize audio channels */ + init_audio(emu); + + /* initialize DMA */ + init_dma(emu); + + /* initialize init arrays */ + init_arrays(emu); + + /* + * Initialize the FM section of the AWE32, this is needed + * for DRAM refresh as well + */ + snd_emu8000_init_fm(emu); + + /* terminate all voices */ + for (i = 0; i < EMU8000_DRAM_VOICES; i++) + EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F); + + /* check DRAM memory size */ + size_dram(emu); + + /* enable audio */ + EMU8000_HWCF3_WRITE(emu, 0x4); + + /* set equzlier, chorus and reverb modes */ + snd_emu8000_update_equalizer(emu); + snd_emu8000_update_chorus_mode(emu); + snd_emu8000_update_reverb_mode(emu); +} + + +/*---------------------------------------------------------------- + * Bass/Treble Equalizer + *----------------------------------------------------------------*/ + +static unsigned short bass_parm[12][3] = { + {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ + {0xD25B, 0xD35B, 0x0000}, /* -8 */ + {0xD24C, 0xD34C, 0x0000}, /* -6 */ + {0xD23D, 0xD33D, 0x0000}, /* -4 */ + {0xD21F, 0xD31F, 0x0000}, /* -2 */ + {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ + {0xC219, 0xC319, 0x0001}, /* +2 */ + {0xC22A, 0xC32A, 0x0001}, /* +4 */ + {0xC24C, 0xC34C, 0x0001}, /* +6 */ + {0xC26E, 0xC36E, 0x0001}, /* +8 */ + {0xC248, 0xC384, 0x0002}, /* +10 */ + {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ +}; + +static unsigned short treble_parm[12][9] = { + {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ + {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ + {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, + {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002} /* +12 dB */ +}; + + +/* + * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] + */ +/*exported*/ void +snd_emu8000_update_equalizer(emu8000_t *emu) +{ + unsigned short w; + int bass = emu->bass_level; + int treble = emu->treble_level; + + if (bass < 0 || bass > 11 || treble < 0 || treble > 11) + return; + EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]); + EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]); + EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]); + EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]); + EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]); + EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]); + EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]); + EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]); + EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]); + EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]); + w = bass_parm[bass][2] + treble_parm[treble][8]; + EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262)); + EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362)); +} + + +/*---------------------------------------------------------------- + * Chorus mode control + *----------------------------------------------------------------*/ + +/* + * chorus mode parameters + */ +#define SNDRV_EMU8000_CHORUS_1 0 +#define SNDRV_EMU8000_CHORUS_2 1 +#define SNDRV_EMU8000_CHORUS_3 2 +#define SNDRV_EMU8000_CHORUS_4 3 +#define SNDRV_EMU8000_CHORUS_FEEDBACK 4 +#define SNDRV_EMU8000_CHORUS_FLANGER 5 +#define SNDRV_EMU8000_CHORUS_SHORTDELAY 6 +#define SNDRV_EMU8000_CHORUS_SHORTDELAY2 7 +#define SNDRV_EMU8000_CHORUS_PREDEFINED 8 +/* user can define chorus modes up to 32 */ +#define SNDRV_EMU8000_CHORUS_NUMBERS 32 + +typedef struct soundfont_chorus_fx_t { + unsigned short feedback; /* feedback level (0xE600-0xE6FF) */ + unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */ + unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */ + unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */ + unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */ +} soundfont_chorus_fx_t; + +/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ +static char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS]; +static soundfont_chorus_fx_t chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = { + {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ + {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ + {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ + {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ + {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ + {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ + {0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */ + {0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */ +}; + +/*exported*/ int +snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len) +{ + soundfont_chorus_fx_t rec; + if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) { + snd_printk("illegal chorus mode %d for uploading\n", mode); + return -EINVAL; + } + if (len < sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec))) + return -EFAULT; + chorus_parm[mode] = rec; + chorus_defined[mode] = 1; + return 0; +} + +/*exported*/ void +snd_emu8000_update_chorus_mode(emu8000_t *emu) +{ + int effect = emu->chorus_mode; + if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS || + (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect])) + return; + EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback); + EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset); + EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth); + EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay); + EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq); + EMU8000_HWCF6_WRITE(emu, 0x8000); + EMU8000_HWCF7_WRITE(emu, 0x0000); +} + +/*---------------------------------------------------------------- + * Reverb mode control + *----------------------------------------------------------------*/ + +/* + * reverb mode parameters + */ +#define SNDRV_EMU8000_REVERB_ROOM1 0 +#define SNDRV_EMU8000_REVERB_ROOM2 1 +#define SNDRV_EMU8000_REVERB_ROOM3 2 +#define SNDRV_EMU8000_REVERB_HALL1 3 +#define SNDRV_EMU8000_REVERB_HALL2 4 +#define SNDRV_EMU8000_REVERB_PLATE 5 +#define SNDRV_EMU8000_REVERB_DELAY 6 +#define SNDRV_EMU8000_REVERB_PANNINGDELAY 7 +#define SNDRV_EMU8000_REVERB_PREDEFINED 8 +/* user can define reverb modes up to 32 */ +#define SNDRV_EMU8000_REVERB_NUMBERS 32 + +typedef struct soundfont_reverb_fx_t { + unsigned short parms[28]; +} soundfont_reverb_fx_t; + +/* reverb mode settings; write the following 28 data of 16 bit length + * on the corresponding ports in the reverb_cmds array + */ +static char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS]; +static soundfont_reverb_fx_t reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = { +{{ /* room 1 */ + 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, + 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 2 */ + 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 3 */ + 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, + 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, +}}, +{{ /* hall 1 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, + 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, +}}, +{{ /* hall 2 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, + 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, + 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* plate */ + 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, + 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* delay */ + 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +{{ /* panning delay */ + 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +}; + +enum { DATA1, DATA2 }; +#define AWE_INIT1(c) EMU8000_CMD(2,c), DATA1 +#define AWE_INIT2(c) EMU8000_CMD(2,c), DATA2 +#define AWE_INIT3(c) EMU8000_CMD(3,c), DATA1 +#define AWE_INIT4(c) EMU8000_CMD(3,c), DATA2 + +static struct reverb_cmd_pair { + unsigned short cmd, port; +} reverb_cmds[28] = { + {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)}, + {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)}, + {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)}, + {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)}, + {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)}, + {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)}, + {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, +}; + +/*exported*/ int +snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len) +{ + soundfont_reverb_fx_t rec; + + if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) { + snd_printk("illegal reverb mode %d for uploading\n", mode); + return -EINVAL; + } + if (len < sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec))) + return -EFAULT; + reverb_parm[mode] = rec; + reverb_defined[mode] = 1; + return 0; +} + +/*exported*/ void +snd_emu8000_update_reverb_mode(emu8000_t *emu) +{ + int effect = emu->reverb_mode; + int i; + + if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS || + (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect])) + return; + for (i = 0; i < 28; i++) { + int port; + if (reverb_cmds[i].port == DATA1) + port = EMU8000_DATA1(emu); + else + port = EMU8000_DATA2(emu); + snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]); + } +} + + +/*---------------------------------------------------------------- + * mixer interface + *----------------------------------------------------------------*/ + +#define chip_t emu8000_t + +/* + * bass/treble + */ +static int mixer_bass_treble_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 11; + return 0; +} + +static int mixer_bass_treble_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level; + return 0; +} + +static int mixer_bass_treble_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1; + + val1 = ucontrol->value.integer.value[0] % 12; + spin_lock_irqsave(&emu->control_lock, flags); + if (kcontrol->private_value) { + change = val1 != emu->treble_level; + emu->treble_level = val1; + } else { + change = val1 != emu->bass_level; + emu->bass_level = val1; + } + spin_unlock_irqrestore(&emu->control_lock, flags); + snd_emu8000_update_equalizer(emu); + return change; +} + +static snd_kcontrol_new_t mixer_bass_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Synth Tone Control - Bass", + info: mixer_bass_treble_info, + get: mixer_bass_treble_get, + put: mixer_bass_treble_put, + private_value: 0, +}; + +static snd_kcontrol_new_t mixer_treble_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Synth Tone Control - Treble", + info: mixer_bass_treble_info, + get: mixer_bass_treble_get, + put: mixer_bass_treble_put, + private_value: 1, +}; + +/* + * chorus/reverb mode + */ +static int mixer_chorus_reverb_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1); + return 0; +} + +static int mixer_chorus_reverb_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode; + return 0; +} + +static int mixer_chorus_reverb_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1; + + spin_lock_irqsave(&emu->control_lock, flags); + if (kcontrol->private_value) { + val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS; + change = val1 != emu->chorus_mode; + emu->chorus_mode = val1; + } else { + val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS; + change = val1 != emu->reverb_mode; + emu->reverb_mode = val1; + } + spin_unlock_irqrestore(&emu->control_lock, flags); + if (change) { + if (kcontrol->private_value) + snd_emu8000_update_chorus_mode(emu); + else + snd_emu8000_update_reverb_mode(emu); + } + return change; +} + +static snd_kcontrol_new_t mixer_chorus_mode_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Chorus Mode", + info: mixer_chorus_reverb_info, + get: mixer_chorus_reverb_get, + put: mixer_chorus_reverb_put, + private_value: 1, +}; + +static snd_kcontrol_new_t mixer_reverb_mode_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Reverb Mode", + info: mixer_chorus_reverb_info, + get: mixer_chorus_reverb_get, + put: mixer_chorus_reverb_put, + private_value: 0, +}; + +/* + * FM OPL3 chorus/reverb depth + */ +static int mixer_fm_depth_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int mixer_fm_depth_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth; + return 0; +} + +static int mixer_fm_depth_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1; + + val1 = ucontrol->value.integer.value[0] % 256; + spin_lock_irqsave(&emu->control_lock, flags); + if (kcontrol->private_value) { + change = val1 != emu->fm_chorus_depth; + emu->fm_chorus_depth = val1; + } else { + change = val1 != emu->fm_reverb_depth; + emu->fm_reverb_depth = val1; + } + spin_unlock_irqrestore(&emu->control_lock, flags); + if (change) + snd_emu8000_init_fm(emu); + return change; +} + +static snd_kcontrol_new_t mixer_fm_chorus_depth_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "FM Chorus Depth", + info: mixer_fm_depth_info, + get: mixer_fm_depth_get, + put: mixer_fm_depth_put, + private_value: 1, +}; + +static snd_kcontrol_new_t mixer_fm_reverb_depth_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "FM Reverb Depth", + info: mixer_fm_depth_info, + get: mixer_fm_depth_get, + put: mixer_fm_depth_put, + private_value: 0, +}; + + +static snd_kcontrol_new_t *mixer_defs[EMU8000_NUM_CONTROLS] = { + &mixer_bass_control, + &mixer_treble_control, + &mixer_chorus_mode_control, + &mixer_reverb_mode_control, + &mixer_fm_chorus_depth_control, + &mixer_fm_reverb_depth_control, +}; + +/* + * create and attach mixer elements for WaveTable treble/bass controls + */ +static int /*__init*/ +snd_emu8000_create_mixer(snd_card_t *card, emu8000_t *emu) +{ + int i, err = 0; + + snd_assert(emu != NULL && card != NULL, return -EINVAL); + + spin_lock_init(&emu->control_lock); + + memset(emu->controls, 0, sizeof(emu->controls)); + for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { + if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0) + goto __error; + } + return 0; + +__error: + for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { + if (emu->controls[i]) + snd_ctl_remove(card, emu->controls[i]); + } + return err; +} + + +/* + * free resources + */ +static int snd_emu8000_free(emu8000_t *hw) +{ + if (hw->res_port1) { + release_resource(hw->res_port1); + kfree_nocheck(hw->res_port1); + } + if (hw->res_port2) { + release_resource(hw->res_port2); + kfree_nocheck(hw->res_port2); + } + if (hw->res_port3) { + release_resource(hw->res_port3); + kfree_nocheck(hw->res_port3); + } + snd_magic_kfree(hw); + return 0; +} + +/* + */ +static int snd_emu8000_dev_free(snd_device_t *device) +{ + emu8000_t *hw = snd_magic_cast(emu8000_t, device->device_data, return -ENXIO); + return snd_emu8000_free(hw); +} + +/* + * initialize and register emu8000 synth device. + */ +/*exported*/ int +snd_emu8000_new(snd_card_t *card, int index, long port, int seq_ports, snd_seq_device_t **awe_ret) +{ + snd_seq_device_t *awe; + emu8000_t *hw; + int err; + static snd_device_ops_t ops = { + dev_free: snd_emu8000_dev_free, + }; + + if (awe_ret) + *awe_ret = NULL; + + if (seq_ports <= 0) + return 0; + + hw = snd_magic_kcalloc(emu8000_t, 0, GFP_KERNEL); + if (hw == NULL) + return -ENOMEM; + spin_lock_init(&hw->reg_lock); + hw->index = index; + hw->port1 = port; + hw->port2 = port + 0x400; + hw->port3 = port + 0x800; + if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) || + !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) || + !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) { + snd_emu8000_free(hw); + return -EBUSY; + } + hw->mem_size = 0; + hw->card = card; + hw->seq_ports = seq_ports; + hw->bass_level = 5; + hw->treble_level = 9; + hw->chorus_mode = 2; + hw->reverb_mode = 4; + hw->fm_chorus_depth = 0; + hw->fm_reverb_depth = 0; + + if (snd_emu8000_detect(hw) < 0) { + snd_emu8000_free(hw); + return -ENODEV; + } + + snd_emu8000_init_hw(hw); + if ((err = snd_emu8000_create_mixer(card, hw)) < 0) { + snd_emu8000_free(hw); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hw, &ops)) < 0) { + snd_emu8000_free(hw); + return err; + } + if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000, + sizeof(emu8000_t*), &awe) >= 0) { + strcpy(awe->name, "EMU-8000"); + *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw; + } + if (awe_ret) + *awe_ret = awe; + + return 0; +} + + +/* + * exported stuff + */ + +EXPORT_SYMBOL(snd_emu8000_new); +EXPORT_SYMBOL(snd_emu8000_poke); +EXPORT_SYMBOL(snd_emu8000_peek); +EXPORT_SYMBOL(snd_emu8000_poke_dw); +EXPORT_SYMBOL(snd_emu8000_peek_dw); +EXPORT_SYMBOL(snd_emu8000_dma_chan); +EXPORT_SYMBOL(snd_emu8000_init_fm); +EXPORT_SYMBOL(snd_emu8000_load_chorus_fx); +EXPORT_SYMBOL(snd_emu8000_load_reverb_fx); +EXPORT_SYMBOL(snd_emu8000_update_chorus_mode); +EXPORT_SYMBOL(snd_emu8000_update_reverb_mode); +EXPORT_SYMBOL(snd_emu8000_update_equalizer); + +#if 0 +/* + * INIT part + */ + +static int __init alsa_emu8000_init(void) +{ + return 0; +} + +static void __exit alsa_emu8000_exit(void) +{ +} + +module_init(alsa_emu8000_init) +module_exit(alsa_emu8000_exit) +#endif diff -Nru a/sound/isa/sb/emu8000_callback.c b/sound/isa/sb/emu8000_callback.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/emu8000_callback.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,540 @@ +/* + * synth callback routines for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "emu8000_local.h" +#include + +/* + * prototypes + */ +static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port); +static int start_voice(snd_emux_voice_t *vp); +static void trigger_voice(snd_emux_voice_t *vp); +static void release_voice(snd_emux_voice_t *vp); +static void update_voice(snd_emux_voice_t *vp, int update); +static void reset_voice(snd_emux_t *emu, int ch); +static void terminate_voice(snd_emux_voice_t *vp); +static void sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +#ifdef CONFIG_SND_OSSEMUL +static int oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2); +#endif +static int load_fx(snd_emux_t *emu, int type, int mode, const void *buf, long len); + +static void set_pitch(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_volume(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_pan(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp); +static void snd_emu8000_tweak_voice(emu8000_t *emu, int ch); + +/* + * Ensure a value is between two points + * macro evaluates its args more than once, so changed to upper-case. + */ +#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) +#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) + + +/* + * set up operators + */ +static snd_emux_operators_t emu8000_ops = { + owner: THIS_MODULE, + get_voice: get_voice, + prepare: start_voice, + trigger: trigger_voice, + release: release_voice, + update: update_voice, + terminate: terminate_voice, + reset: reset_voice, + sample_new: snd_emu8000_sample_new, + sample_free: snd_emu8000_sample_free, + sample_reset: snd_emu8000_sample_reset, + load_fx: load_fx, + sysex: sysex, +#ifdef CONFIG_SND_OSSEMUL + oss_ioctl: oss_ioctl, +#endif +}; + +void +snd_emu8000_ops_setup(emu8000_t *hw) +{ + hw->emu->ops = emu8000_ops; +} + + + +/* + * Terminate a voice + */ +static void +release_voice(snd_emux_voice_t *vp) +{ + int dcysusv; + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; + EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease; + EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv); +} + + +/* + */ +static void +terminate_voice(snd_emux_voice_t *vp) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F); +} + + +/* + */ +static void +update_voice(snd_emux_voice_t *vp, int update) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + if (update & SNDRV_EMUX_UPDATE_VOLUME) + set_volume(hw, vp); + if (update & SNDRV_EMUX_UPDATE_PITCH) + set_pitch(hw, vp); + if ((update & SNDRV_EMUX_UPDATE_PAN) && + vp->port->ctrls[EMUX_MD_REALTIME_PAN]) + set_pan(hw, vp); + if (update & SNDRV_EMUX_UPDATE_FMMOD) + set_fmmod(hw, vp); + if (update & SNDRV_EMUX_UPDATE_TREMFREQ) + set_tremfreq(hw, vp); + if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) + set_fm2frq2(hw, vp); + if (update & SNDRV_EMUX_UPDATE_Q) + set_filterQ(hw, vp); +} + + +/* + * Find a channel (voice) within the EMU that is not in use or at least + * less in use than other channels. Always returns a valid pointer + * no matter what. If there is a real shortage of voices then one + * will be cut. Such is life. + * + * The channel index (vp->ch) must be initialized in this routine. + * In Emu8k, it is identical with the array index. + */ +static snd_emux_voice_t * +get_voice(snd_emux_t *emu, snd_emux_port_t *port) +{ + int i; + snd_emux_voice_t *vp; + emu8000_t *hw; + + /* what we are looking for, in order of preference */ + enum { + OFF=0, RELEASED, PLAYING, END + }; + + /* Keeps track of what we are finding */ + struct best { + unsigned int time; + int voice; + } best[END]; + struct best *bp; + + hw = snd_magic_cast(emu8000_t, emu->hw, return NULL); + + for (i = 0; i < END; i++) { + best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */; + best[i].voice = -1; + } + + /* + * Go through them all and get a best one to use. + */ + for (i = 0; i < emu->max_voices; i++) { + int state, val; + + vp = &emu->voices[i]; + state = vp->state; + + if (state == SNDRV_EMUX_ST_OFF) + bp = best + OFF; + else if (state == SNDRV_EMUX_ST_RELEASED || + state == SNDRV_EMUX_ST_PENDING) { + bp = best + RELEASED; + val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff; + if (! val) + bp = best + OFF; + } + else if (state & SNDRV_EMUX_ST_ON) + bp = best + PLAYING; + else + continue; + + /* check if sample is finished playing (non-looping only) */ + if (state != SNDRV_EMUX_ST_OFF && + (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { + val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; + if (val >= vp->reg.loopstart) + bp = best + OFF; + } + + if (vp->time < bp->time) { + bp->time = vp->time; + bp->voice = i; + } + } + + for (i = 0; i < END; i++) { + if (best[i].voice >= 0) { + vp = &emu->voices[best[i].voice]; + vp->ch = best[i].voice; + return vp; + } + } + + /* not found */ + return NULL; +} + +/* + */ +static int +start_voice(snd_emux_voice_t *vp) +{ + unsigned int temp; + int ch; + int addr; + snd_midi_channel_t *chan; + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return -EINVAL); + ch = vp->ch; + chan = vp->chan; + + /* channel to be silent and idle */ + EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080); + EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF); + EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF); + EMU8000_PTRX_WRITE(hw, ch, 0); + EMU8000_CPF_WRITE(hw, ch, 0); + + /* set pitch offset */ + set_pitch(hw, vp); + + /* set envelope parameters */ + EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay); + EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld); + EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus); + EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay); + EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld); + /* decay/sustain parameter for volume envelope is used + for triggerg the voice */ + + /* cutoff and volume */ + set_volume(hw, vp); + + /* modulation envelope heights */ + EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe); + + /* lfo1/2 delay */ + EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay); + EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay); + + /* lfo1 pitch & cutoff shift */ + set_fmmod(hw, vp); + /* lfo1 volume & freq */ + set_tremfreq(hw, vp); + /* lfo2 pitch & freq */ + set_fm2frq2(hw, vp); + /* pan & loop start */ + set_pan(hw, vp); + + /* chorus & loop end (chorus 8bit, MSB) */ + addr = vp->reg.loopend - 1; + temp = vp->reg.parm.chorus; + temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + temp = (temp <<24) | (unsigned int)addr; + EMU8000_CSL_WRITE(hw, ch, temp); + + /* Q & current address (Q 4bit value, MSB) */ + addr = vp->reg.start - 1; + temp = vp->reg.parm.filterQ; + temp = (temp<<28) | (unsigned int)addr; + EMU8000_CCCA_WRITE(hw, ch, temp); + + /* clear unknown registers */ + EMU8000_00A0_WRITE(hw, ch, 0); + EMU8000_0080_WRITE(hw, ch, 0); + + /* reset volume */ + temp = vp->vtarget << 16; + EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget); + EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00); + + return 0; +} + +/* + * Start envelope + */ +static void +trigger_voice(snd_emux_voice_t *vp) +{ + int ch = vp->ch; + unsigned int temp; + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + + /* set reverb and pitch target */ + temp = vp->reg.parm.reverb; + temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux; + EMU8000_PTRX_WRITE(hw, ch, temp); + EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16); + EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus); +} + +/* + * reset voice parameters + */ +static void +reset_voice(snd_emux_t *emu, int ch) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, emu->hw, return); + EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F); + snd_emu8000_tweak_voice(hw, ch); +} + +/* + * Set the pitch of a possibly playing note. + */ +static void +set_pitch(emu8000_t *hw, snd_emux_voice_t *vp) +{ + EMU8000_IP_WRITE(hw, vp->ch, vp->apitch); +} + +/* + * Set the volume of a possibly already playing note + */ +static void +set_volume(emu8000_t *hw, snd_emux_voice_t *vp) +{ + int ifatn; + + ifatn = (unsigned char)vp->acutoff; + ifatn = (ifatn << 8); + ifatn |= (unsigned char)vp->avol; + EMU8000_IFATN_WRITE(hw, vp->ch, ifatn); +} + +/* + * Set pan and loop start address. + */ +static void +set_pan(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned int temp; + + temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1); + EMU8000_PSST_WRITE(hw, vp->ch, temp); +} + +#define MOD_SENSE 18 + +static void +set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fmmod; + short pitch; + unsigned char cutoff; + int modulation; + + pitch = (char)(vp->reg.parm.fmmod>>8); + cutoff = (vp->reg.parm.fmmod & 0xff); + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fmmod = ((unsigned char)pitch<<8) | cutoff; + EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod); +} + +/* set tremolo (lfo1) volume & frequency */ +static void +set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp) +{ + EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq); +} + +/* set lfo2 pitch & frequency */ +static void +set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fm2frq2; + short pitch; + unsigned char freq; + int modulation; + + pitch = (char)(vp->reg.parm.fm2frq2>>8); + freq = vp->reg.parm.fm2frq2 & 0xff; + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fm2frq2 = ((unsigned char)pitch<<8) | freq; + EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2); +} + +/* set filterQ */ +static void +set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned int addr; + addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; + addr |= (vp->reg.parm.filterQ << 28); + EMU8000_CCCA_WRITE(hw, vp->ch, addr); +} + +/* + * set the envelope & LFO parameters to the default values + */ +static void +snd_emu8000_tweak_voice(emu8000_t *emu, int i) +{ + /* set all mod/vol envelope shape to minimum */ + EMU8000_ENVVOL_WRITE(emu, i, 0x8000); + EMU8000_ENVVAL_WRITE(emu, i, 0x8000); + EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F); + EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F); + EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F); + EMU8000_PEFE_WRITE(emu, i, 0); /* mod envelope height to zero */ + EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */ + EMU8000_LFO2VAL_WRITE(emu, i, 0x8000); + EMU8000_IP_WRITE(emu, i, 0xE000); /* no pitch shift */ + EMU8000_IFATN_WRITE(emu, i, 0xFF00); /* volume to minimum */ + EMU8000_FMMOD_WRITE(emu, i, 0); + EMU8000_TREMFRQ_WRITE(emu, i, 0); + EMU8000_FM2FRQ2_WRITE(emu, i, 0); +} + +/* + * sysex callback + */ +static void +sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, emu->hw, return); + + switch (parsed) { + case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE: + hw->chorus_mode = chset->gs_chorus_mode; + snd_emu8000_update_chorus_mode(hw); + break; + + case SNDRV_MIDI_SYSEX_GS_REVERB_MODE: + hw->reverb_mode = chset->gs_reverb_mode; + snd_emu8000_update_reverb_mode(hw); + break; + } +} + + +#ifdef CONFIG_SND_OSSEMUL +/* + * OSS ioctl callback + */ +static int +oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL); + + switch (cmd) { + case _EMUX_OSS_REVERB_MODE: + hw->reverb_mode = p1; + snd_emu8000_update_reverb_mode(hw); + break; + + case _EMUX_OSS_CHORUS_MODE: + hw->chorus_mode = p1; + snd_emu8000_update_chorus_mode(hw); + break; + + case _EMUX_OSS_INITIALIZE_CHIP: + /* snd_emu8000_init(hw); */ /*ignored*/ + break; + + case _EMUX_OSS_EQUALIZER: + hw->bass_level = p1; + hw->treble_level = p2; + snd_emu8000_update_equalizer(hw); + break; + } + return 0; +} +#endif + + +/* + * additional patch keys + */ + +#define SNDRV_EMU8000_LOAD_CHORUS_FX 0x10 /* optarg=mode */ +#define SNDRV_EMU8000_LOAD_REVERB_FX 0x11 /* optarg=mode */ + + +/* + * callback routine + */ + +static int +load_fx(snd_emux_t *emu, int type, int mode, const void *buf, long len) +{ + emu8000_t *hw; + hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL); + + switch (type) { + case SNDRV_EMU8000_LOAD_CHORUS_FX: + return snd_emu8000_load_chorus_fx(hw, mode, buf, len); + case SNDRV_EMU8000_LOAD_REVERB_FX: + return snd_emu8000_load_reverb_fx(hw, mode, buf, len); + } + return -EINVAL; +} + diff -Nru a/sound/isa/sb/emu8000_local.h b/sound/isa/sb/emu8000_local.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/emu8000_local.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,42 @@ +#ifndef __EMU8000_LOCAL_H +#define __EMU8000_LOCAL_H +/* + * Local defininitons for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) + +/* emu8000_patch.c */ +int snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *data, long count); +int snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); +void snd_emu8000_sample_reset(snd_emux_t *rec); + +/* emu8000_callback.c */ +void snd_emu8000_ops_setup(emu8000_t *emu); + +#endif /* __EMU8000_LOCAL_H */ diff -Nru a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/emu8000_patch.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,308 @@ +/* + * Patch routines for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "emu8000_local.h" +#include + +MODULE_PARM(emu8000_reset_addr, "i"); +MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)"); + +int emu8000_reset_addr = 0; + + +/* + * Open up channels. + */ +static int +snd_emu8000_open_dma(emu8000_t *emu, int write) +{ + int i; + + /* reserve all 30 voices for loading */ + for (i = 0; i < EMU8000_DRAM_VOICES; i++) { + snd_emux_lock_voice(emu->emu, i); + snd_emu8000_dma_chan(emu, i, write); + } + + /* assign voice 31 and 32 to ROM */ + EMU8000_VTFT_WRITE(emu, 30, 0); + EMU8000_PSST_WRITE(emu, 30, 0x1d8); + EMU8000_CSL_WRITE(emu, 30, 0x1e0); + EMU8000_CCCA_WRITE(emu, 30, 0x1d8); + EMU8000_VTFT_WRITE(emu, 31, 0); + EMU8000_PSST_WRITE(emu, 31, 0x1d8); + EMU8000_CSL_WRITE(emu, 31, 0x1e0); + EMU8000_CCCA_WRITE(emu, 31, 0x1d8); + + return 0; +} + +/* + * Close all dram channels. + */ +static void +snd_emu8000_close_dma(emu8000_t *emu) +{ + int i; + + for (i = 0; i < EMU8000_DRAM_VOICES; i++) { + snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE); + snd_emux_unlock_voice(emu->emu, i); + } +} + +/* + */ + +#define BLANK_LOOP_START 4 +#define BLANK_LOOP_END 8 +#define BLANK_LOOP_SIZE 12 +#define BLANK_HEAD_SIZE 48 + +/* + * Read a word from userland, taking care of conversions from + * 8bit samples etc. + */ +static unsigned short +read_word(const void *buf, int offset, int mode) +{ + unsigned short c; + if (mode & SNDRV_SFNT_SAMPLE_8BITS) { + unsigned char cc; + get_user(cc, (unsigned char*)buf + offset); + c = cc << 8; /* convert 8bit -> 16bit */ + } else { +#ifdef SNDRV_LITTLE_ENDIAN + get_user(c, (unsigned short*)buf + offset); +#else + unsigned short cc; + get_user(cc, (unsigned short*)buf + offset); + c = swab16(cc); +#endif + } + if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED) + c ^= 0x8000; /* unsigned -> signed */ + return c; +} + +/* + */ +static void +snd_emu8000_write_wait(emu8000_t *emu) +{ + while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } +} + +/* + * write sample word data + * + * You should not have to keep resetting the address each time + * as the chip is supposed to step on the next address automatically. + * It mostly does, but during writes of some samples at random it + * completely loses words (every one in 16 roughly but with no + * obvious pattern). + * + * This is therefore much slower than need be, but is at least + * working. + */ +inline static void +write_word(emu8000_t *emu, int *offset, unsigned short data) +{ + if (emu8000_reset_addr) { + if (emu8000_reset_addr > 1) + snd_emu8000_write_wait(emu); + EMU8000_SMALW_WRITE(emu, *offset); + } + EMU8000_SMLD_WRITE(emu, data); + *offset += 1; +} + +/* + * Write the sample to EMU800 memory. This routine is invoked out of + * the generic soundfont routines as a callback. + */ +int +snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr, const void *data, long count) +{ + int i; + int rc; + int offset; + int truesize; + int dram_offset, dram_start; + emu8000_t *emu; + + emu = snd_magic_cast(emu8000_t, rec->hw, return -EINVAL); + snd_assert(sp != NULL, return -EINVAL); + + if (sp->v.size == 0) + return 0; + + /* be sure loop points start < end */ + if (sp->v.loopstart > sp->v.loopend) { + int tmp = sp->v.loopstart; + sp->v.loopstart = sp->v.loopend; + sp->v.loopend = tmp; + } + + /* compute true data size to be loaded */ + truesize = sp->v.size; + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) + truesize += sp->v.loopend - sp->v.loopstart; + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) + truesize += BLANK_LOOP_SIZE; + + sp->block = snd_util_mem_alloc(hdr, truesize * 2); + if (sp->block == NULL) { + /*snd_printd("EMU8000: out of memory\n");*/ + /* not ENOMEM (for compatibility) */ + return -ENOSPC; + } + + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) { + if (verify_area(VERIFY_READ, data, sp->v.size)) + return -EFAULT; + } else { + if (verify_area(VERIFY_READ, data, sp->v.size * 2)) + return -EFAULT; + } + + /* recalculate address offset */ + sp->v.end -= sp->v.start; + sp->v.loopstart -= sp->v.start; + sp->v.loopend -= sp->v.start; + sp->v.start = 0; + + /* dram position (in word) -- mem_offset is byte */ + dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1); + dram_start = dram_offset; + + /* set the total size (store onto obsolete checksum value) */ + sp->v.truesize = truesize * 2; /* in bytes */ + + snd_emux_terminate_all(emu->emu); + if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0) + return rc; + + /* Set the address to start writing at */ + snd_emu8000_write_wait(emu); + EMU8000_SMALW_WRITE(emu, dram_offset); + + /*snd_emu8000_init_fm(emu);*/ + +#if 0 + /* first block - write 48 samples for silence */ + if (! sp->block->offset) { + for (i = 0; i < BLANK_HEAD_SIZE; i++) { + write_word(emu, &dram_offset, 0); + } + } +#endif + + offset = 0; + for (i = 0; i < sp->v.size; i++) { + unsigned short s; + + s = read_word(data, offset, sp->v.mode_flags); + offset++; + write_word(emu, &dram_offset, s); + + /* we may take too long time in this loop. + * so give controls back to kernel if needed. + */ + if (need_resched()) { + if (current->state != TASK_RUNNING) + set_current_state(TASK_RUNNING); + schedule(); + } + + if (i == sp->v.loopend && + (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))) + { + int looplen = sp->v.loopend - sp->v.loopstart; + int k; + + /* copy reverse loop */ + for (k = 1; k <= looplen; k++) { + s = read_word(data, offset - k, sp->v.mode_flags); + write_word(emu, &dram_offset, s); + } + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { + sp->v.loopend += looplen; + } else { + sp->v.loopstart += looplen; + sp->v.loopend += looplen; + } + sp->v.end += looplen; + } + } + + /* if no blank loop is attached in the sample, add it */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { + for (i = 0; i < BLANK_LOOP_SIZE; i++) { + write_word(emu, &dram_offset, 0); + } + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { + sp->v.loopstart = sp->v.end + BLANK_LOOP_START; + sp->v.loopend = sp->v.end + BLANK_LOOP_END; + } + } + + /* add dram offset */ + sp->v.start += dram_start; + sp->v.end += dram_start; + sp->v.loopstart += dram_start; + sp->v.loopend += dram_start; + + snd_emu8000_close_dma(emu); + snd_emu8000_init_fm(emu); + + return 0; +} + +/* + * free a sample block + */ +int +snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr) +{ + if (sp->block) { + snd_util_mem_free(hdr, sp->block); + sp->block = NULL; + } + return 0; +} + + +/* + * sample_reset callback - terminate voices + */ +void +snd_emu8000_sample_reset(snd_emux_t *rec) +{ + snd_emux_terminate_all(rec); +} diff -Nru a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/emu8000_synth.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,130 @@ +/* + * Copyright (c) by Jaroslav Kysela + * and (c) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * Emu8000 synth plug-in routine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu8000_local.h" +#include +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe"); +MODULE_DESCRIPTION("Emu8000 synth plug-in routine"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +/*----------------------------------------------------------------*/ + +/* + * create a new hardware dependent device for Emu8000 + */ +static int snd_emu8000_new_device(snd_seq_device_t *dev) +{ + emu8000_t *hw; + snd_emux_t *emu; + + hw = *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (hw == NULL) + return -EINVAL; + + if (hw->emu) + return -EBUSY; /* already exists..? */ + + if (snd_emux_new(&emu) < 0) + return -ENOMEM; + + hw->emu = emu; + snd_emu8000_ops_setup(hw); + + emu->hw = hw; + emu->max_voices = EMU8000_DRAM_VOICES; + emu->num_ports = hw->seq_ports; + + if (hw->memhdr) { + snd_printk("memhdr is already initialized!?\n"); + snd_util_memhdr_free(hw->memhdr); + } + hw->memhdr = snd_util_memhdr_new(hw->mem_size); + if (hw->memhdr == NULL) { + snd_emux_free(emu); + hw->emu = NULL; + return -ENOMEM; + } + + emu->memhdr = hw->memhdr; + emu->midi_ports = hw->seq_ports < 2 ? hw->seq_ports : 2; /* number of virmidi ports */ + emu->midi_devidx = 1; + + if (snd_emux_register(emu, dev->card, hw->index, "Emu8000") < 0) { + snd_emux_free(emu); + snd_util_memhdr_free(hw->memhdr); + hw->emu = NULL; + hw->memhdr = NULL; + return -ENOMEM; + } + + dev->driver_data = hw; + + return 0; +} + + +/* + * free all resources + */ +static int snd_emu8000_delete_device(snd_seq_device_t *dev) +{ + emu8000_t *hw; + + if (dev->driver_data == NULL) + return 0; /* no synth was allocated actually */ + + hw = dev->driver_data; + if (hw->emu) + snd_emux_free(hw->emu); + if (hw->memhdr) + snd_util_memhdr_free(hw->memhdr); + hw->emu = NULL; + hw->memhdr = NULL; + return 0; +} + +/* + * INIT part + */ + +static int __init alsa_emu8000_init(void) +{ + + static snd_seq_dev_ops_t ops = { + snd_emu8000_new_device, + snd_emu8000_delete_device, + }; + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, sizeof(emu8000_t*)); +} + +static void __exit alsa_emu8000_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000); +} + +module_init(alsa_emu8000_init) +module_exit(alsa_emu8000_exit) diff -Nru a/sound/isa/sb/es968.c b/sound/isa/sb/es968.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/es968.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,302 @@ + +/* + card-es968.c - driver for ESS AudioDrive ES968 based soundcards. + Copyright (C) 1999 by Massimo Piccioni + + Thanks to Pierfrancesco 'qM2' Passerini. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include + +#define chip_t sb_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("ESS AudioDrive ES968"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,AudioDrive ES968}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for es968 based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for es968 based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable es968 based soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for es968 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for es968 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for es968 driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); + +struct snd_card_es968 { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_es968_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ +static struct isapnp_card *snd_es968_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_es968_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +static struct isapnp_card_id snd_es968_pnpids[] __devinitdata = { + { + ISAPNP_CARD_ID('E','S','S',0x0968), + devs: { ISAPNP_DEVICE_ID('E','S','S',0x0968), } + }, + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_es968_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-es968" + + +static void snd_card_es968_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + + if (chip->open & SB_OPEN_PCM) { + snd_sb8dsp_interrupt(chip); + } else { + snd_sb8dsp_midi_interrupt(chip); + } +} + +#ifdef __ISAPNP__ +static int __init snd_card_es968_isapnp(int dev, struct snd_card_es968 *acard) +{ + const struct isapnp_card_id *id = snd_es968_isapnp_id[dev]; + struct isapnp_card *card = snd_es968_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev], + 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + snd_port[dev] = pdev->resource[0].start; + snd_dma8[dev] = pdev->dma_resource[0].start; + snd_irq[dev] = pdev->irq_resource[0].start; + + return 0; +} + +static void snd_card_es968_deactivate(struct snd_card_es968 *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void __exit snd_card_es968_free(snd_card_t *card) +{ + struct snd_card_es968 *acard = (struct snd_card_es968 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_es968_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_es968_probe(int dev) +{ + int error; + sb_t *chip; + snd_card_t *card; + struct snd_card_es968 *acard; + + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_es968))) == NULL) + return -ENOMEM; + acard = (struct snd_card_es968 *)card->private_data; + card->private_free = snd_card_es968_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_es968_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + snd_printk("you have to enable PnP support ...\n"); + snd_card_free(card); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_sbdsp_create(card, snd_port[dev], + snd_irq[dev], + snd_card_es968_interrupt, + snd_dma8[dev], + -1, + SB_HW_AUTO, &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb8dsp_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + strcpy(card->driver, "ES968"); + strcpy(card->shortname, "ESS ES968"); + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", + card->shortname, chip->name, chip->port, snd_irq[dev], snd_dma8[dev]); + + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_es968_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_es968_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_es968_isapnp_cards[dev] = card; + snd_es968_isapnp_id[dev] = id; + res = snd_card_es968_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_es968_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_es968_pnpids, snd_es968_isapnp_detect); +#else + snd_printk("you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + snd_printk("no ES968 based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_es968_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_es968_cards[dev]); +} + +module_init(alsa_card_es968_init) +module_exit(alsa_card_es968_exit) + +#ifndef MODULE + +/* format is: snd-es968=snd_enable,snd_index,snd_id, + snd_port,snd_irq,snd_dma1 */ + +static int __init alsa_card_es968_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es968=", alsa_card_es968_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/sb16.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,705 @@ +/* + * Driver for SoundBlaster 16/AWE32/AWE64 soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +#define chip_t sb_t + +#ifndef SNDRV_SBAWE +EXPORT_NO_SYMBOLS; +#endif + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#ifndef SNDRV_SBAWE +MODULE_DESCRIPTION("Sound Blaster 16"); +MODULE_DEVICES("{{Creative Labs,SB 16}," + "{Creative Labs,SB Vibra16S}," + "{Creative Labs,SB Vibra16C}," + "{Creative Labs,SB Vibra16CL}," + "{Creative Labs,SB Vibra16X}}"); +#else +MODULE_DESCRIPTION("Sound Blaster AWE"); +MODULE_DEVICES("{{Creative Labs,SB AWE 32}," + "{Creative Labs,SB AWE 64}," + "{Creative Labs,SB AWE 64 Gold}}"); +#endif + +#if 0 +#define SNDRV_DEBUG_IRQ +#endif + +#if defined(SNDRV_SBAWE) && (defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)) +#define SNDRV_SBAWE_EMU8000 +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */ +static long snd_mpu_port[SNDRV_CARDS] = {0x330, 0x300,[2 ... (SNDRV_CARDS - 1)] = -1}; +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +#ifdef SNDRV_SBAWE_EMU8000 +static long snd_awe_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +#endif +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ +static int snd_dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 5,6,7 */ +static int snd_mic_agc[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#ifdef CONFIG_SND_SB16_CSP +static int snd_csp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +#endif +#ifdef SNDRV_SBAWE_EMU8000 +static int snd_seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; +#endif + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for SoundBlaster 16 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for SoundBlaster 16 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable SoundBlaster 16 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for SB16 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260},{0x280}},dialog:list"); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for SB16 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list"); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for SB16 PnP driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x38c},{0x390},{0x394}},dialog:list"); +#ifdef SNDRV_SBAWE_EMU8000 +MODULE_PARM(snd_awe_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_awe_port, "AWE port # for SB16 PnP driver."); +MODULE_PARM_SYNTAX(snd_awe_port, SNDRV_ENABLED ",allows:{{0x620},{0x640},{0x660},{0x680}},dialog:list"); +#endif +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for SB16 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for SB16 driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); +MODULE_PARM(snd_dma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma16, "16-bit DMA # for SB16 driver."); +MODULE_PARM_SYNTAX(snd_dma16, SNDRV_DMA16_DESC); +MODULE_PARM(snd_mic_agc, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mic_agc, "Mic Auto-Gain-Control switch."); +MODULE_PARM_SYNTAX(snd_mic_agc, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); +#ifdef CONFIG_SND_SB16_CSP +MODULE_PARM(snd_csp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_csp, "ASP/CSP chip support."); +MODULE_PARM_SYNTAX(snd_csp, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +#endif +#ifdef SNDRV_SBAWE_EMU8000 +MODULE_PARM(snd_seq_ports, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_seq_ports, "Number of sequencer ports for WaveTable synth."); +MODULE_PARM_SYNTAX(snd_seq_ports, SNDRV_ENABLED ",allows:{{0,8}},skill:advanced"); +#endif + +struct snd_sb16 { + struct resource *fm_res; /* used to block FM i/o region for legacy cards */ +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#ifdef SNDRV_SBAWE_EMU8000 + struct isapnp_dev *devwt; +#endif +#endif +}; + +static snd_card_t *snd_sb16_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_sb16_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_sb16_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#define ISAPNP_SB16(_va, _vb, _vc, _device, _audio) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \ + } +#define ISAPNP_SBAWE(_va, _vb, _vc, _device, _audio, _awe) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _awe), } \ + } + +static struct isapnp_card_id snd_sb16_pnpids[] __devinitdata = { +#ifndef SNDRV_SBAWE + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0024,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0026,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0027,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0028,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0029,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x002a,0x0031), + /* Sound Blaster 16 PnP */ + /* Note: This card has also a CTL0051:StereoEnhance device!!! */ + ISAPNP_SB16('C','T','L',0x002b,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x002c,0x0031), + /* Sound Blaster Vibra16S */ + ISAPNP_SB16('C','T','L',0x0051,0x0001), + /* Sound Blaster Vibra16C */ + ISAPNP_SB16('C','T','L',0x0070,0x0001), + /* Sound Blaster Vibra16CL - added by ctm@ardi.com */ + ISAPNP_SB16('C','T','L',0x0080,0x0041), + /* Sound Blaster Vibra16X */ + ISAPNP_SB16('C','T','L',0x00f0,0x0043), +#else /* SNDRV_SBAWE defined */ + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0035,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0039,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0042,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0043,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + /* Note: This card has also a CTL0051:StereoEnhance device!!! */ + ISAPNP_SBAWE('C','T','L',0x0044,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + /* Note: This card has also a CTL0051:StereoEnhance device!!! */ + ISAPNP_SBAWE('C','T','L',0x0045,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0047,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0048,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0054,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x009a,0x0041,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x009c,0x0041,0x0021), + /* Sound Blaster 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x009f,0x0041,0x0021), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x009d,0x0042,0x0022), + /* Sound Blaster AWE 64 PnP Gold */ + ISAPNP_SBAWE('C','T','L',0x009e,0x0044,0x0023), + /* Sound Blaster AWE 64 PnP Gold */ + ISAPNP_SBAWE('C','T','L',0x00b2,0x0044,0x0023), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c1,0x0042,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c3,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c5,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c7,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00e4,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00e9,0x0045,0x0022), + /* Sound Blaster 16 PnP (AWE) */ + ISAPNP_SBAWE('C','T','L',0x00ed,0x0041,0x0070), + /* Generic entries */ + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0031,0x0021), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0041,0x0021), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0042,0x0022), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0044,0x0023), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0045,0x0022), +#endif /* SNDRV_SBAWE */ + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_sb16_pnpids); + +static int __init snd_sb16_isapnp(int dev, struct snd_sb16 *acard) +{ + const struct isapnp_card_id *id = snd_sb16_isapnp_id[dev]; + struct isapnp_card *card = snd_sb16_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } +#ifdef SNDRV_SBAWE_EMU8000 + acard->devwt = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devwt->active) { + acard->dev = acard->devwt = NULL; + return -EBUSY; + } +#endif + /* Audio initialization */ + pdev = acard->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_mpu_port[dev], 2); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_fm_port[dev], 4); + if (snd_dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev], 1); + if (snd_dma16[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma16[dev], 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + if (pdev->activate(pdev) < 0) { + snd_printk("isapnp configure failure (out of resources?)\n"); + return -EBUSY; + } + snd_port[dev] = pdev->resource[0].start; + snd_mpu_port[dev] = pdev->resource[1].start; + snd_fm_port[dev] = pdev->resource[2].start; + snd_dma8[dev] = pdev->dma_resource[0].start; + snd_dma16[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n", + snd_port[dev], snd_mpu_port[dev], snd_fm_port[dev]); + snd_printdd("isapnp SB16: dma1=%i, dma2=%i, irq=%i\n", + snd_dma8[dev], snd_dma16[dev], snd_irq[dev]); +#ifdef SNDRV_SBAWE_EMU8000 + /* WaveTable initialization */ + pdev = acard->devwt; + if (pdev->prepare(pdev)<0) { + acard->dev->deactivate(acard->dev); + return -EAGAIN; + } + if (snd_awe_port[dev] != SNDRV_AUTO_PORT) { + isapnp_resource_change(&pdev->resource[0], snd_awe_port[dev], 4); + isapnp_resource_change(&pdev->resource[1], snd_awe_port[dev] + 0x400, 4); + isapnp_resource_change(&pdev->resource[2], snd_awe_port[dev] + 0x800, 4); + } + if (pdev->activate(pdev)<0) { + snd_printk("WaveTable isapnp configure failure (out of resources?)\n"); + acard->dev->deactivate(acard->dev); + return -EBUSY; + } + snd_awe_port[dev] = pdev->resource[0].start; + snd_printdd("isapnp SB16: wavetable port=0x%lx\n", pdev->resource[0].start); +#endif + return 0; +} + +static void snd_sb16_deactivate(struct snd_sb16 *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } +#ifdef SNDRV_SBAWE_EMU8000 + if (acard->devwt) { + acard->devwt->deactivate(acard->devwt); + acard->devwt = NULL; + } +#endif +} + +#endif /* __ISAPNP__ */ + +static void snd_sb16_free(snd_card_t *card) +{ + struct snd_sb16 *acard = (struct snd_sb16 *)card->private_data; + + if (acard == NULL) + return; + if (acard->fm_res) { + release_resource(acard->fm_res); + kfree_nocheck(acard->fm_res); + } +#ifdef __ISAPNP__ + snd_sb16_deactivate(acard); +#endif +} + +static int __init snd_sb16_probe(int dev) +{ + static int possible_irqs[] = {5, 9, 10, 7, -1}; + static int possible_dmas8[] = {1, 3, 0, -1}; + static int possible_dmas16[] = {5, 6, 7, -1}; + int irq, dma8, dma16; + sb_t *chip; + snd_card_t *card; + struct snd_sb16 *acard; + opl3_t *opl3; + snd_hwdep_t *synth = NULL; +#ifdef CONFIG_SND_SB16_CSP + snd_hwdep_t *csp = NULL; +#endif + unsigned long flags; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_sb16)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_sb16 *) card->private_data; + card->private_free = snd_sb16_free; +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && snd_sb16_isapnp(dev, acard) < 0) { + snd_card_free(card); + return -EBUSY; + } +#endif + + irq = snd_irq[dev]; + dma8 = snd_dma8[dev]; + dma16 = snd_dma16[dev]; +#ifdef __ISAPNP__ + if (!snd_isapnp[dev]) { +#endif + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + if (dma8 == SNDRV_AUTO_DMA) { + if ((dma8 = snd_legacy_find_free_dma(possible_dmas8)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free 8-bit DMA\n"); + return -EBUSY; + } + } + if (dma16 == SNDRV_AUTO_DMA) { + if ((dma16 = snd_legacy_find_free_dma(possible_dmas16)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free 16-bit DMA\n"); + return -EBUSY; + } + } + /* non-PnP FM port address is hardwired with base port address */ + snd_fm_port[dev] = snd_port[dev]; + /* block the 0x388 port to avoid PnP conflicts */ + acard->fm_res = request_region(0x388, 4, "SoundBlaster FM"); +#ifdef SNDRV_SBAWE_EMU8000 + /* non-PnP AWE port address is hardwired with base port address */ + snd_awe_port[dev] = snd_port[dev] + 0x400; +#endif +#ifdef __ISAPNP__ + } +#endif + + if ((err = snd_sbdsp_create(card, + snd_port[dev], + irq, + snd_sb16dsp_interrupt, + dma8, + dma16, + SB_HW_AUTO, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if (chip->hardware != SB_HW_16) { + snd_card_free(card); + snd_printdd("SB 16 chip was not detected at 0x%lx\n", snd_port[dev]); + return -ENODEV; + } + chip->mpu_port = snd_mpu_port[dev]; +#ifdef __ISAPNP__ + if (!snd_isapnp[dev] && (err = snd_sb16dsp_configure(chip)) < 0) { +#else + if ((err = snd_sb16dsp_configure(chip)) < 0) { +#endif + snd_card_free(card); + return -ENXIO; + } + if ((err = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return -ENXIO; + } + + if (chip->mpu_port) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB, + chip->mpu_port, 0, + irq, 0, &chip->rmidi)) < 0) { + snd_card_free(card); + return -ENXIO; + } + } + + if (snd_fm_port[dev] > 0) { + if (snd_opl3_create(card, snd_fm_port[dev], snd_fm_port[dev] + 2, + OPL3_HW_OPL3, snd_fm_port[dev] == snd_port[dev], + &opl3) < 0) { + snd_printk("no OPL device at 0x%lx-0x%lx\n", + snd_fm_port[dev], snd_fm_port[dev] + 2); + } else { +#ifdef SNDRV_SBAWE_EMU8000 + int seqdev = snd_awe_port[dev] > 0 ? 2 : 1; +#else + int seqdev = 1; +#endif + if ((err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth)) < 0) { + snd_card_free(card); + return -ENXIO; + } + } + } + + if ((err = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return -ENXIO; + } + +#ifdef CONFIG_SND_SB16_CSP + /* CSP chip on SB16ASP/AWE32 */ + if ((chip->hardware == SB_HW_16) && snd_csp[dev]) { + snd_sb_csp_new(chip, synth != NULL ? 1 : 0, &csp); + if (csp) { + chip->csp = csp->private_data; + chip->hardware = SB_HW_16CSP; + } else { + snd_printk("warning - CSP chip not detected on soundcard #%i\n", dev + 1); + } + } +#endif +#ifdef SNDRV_SBAWE_EMU8000 + if (snd_awe_port[dev] > 0) { + if (snd_emu8000_new(card, 1, snd_awe_port[dev], + snd_seq_ports[dev], NULL) < 0) { + snd_printk("fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", snd_awe_port[dev]); + snd_card_free(card); + return -ENXIO; + } + } +#endif + + /* setup Mic AGC */ + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, SB_DSP4_MIC_AGC, + (snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) | + (snd_mic_agc[dev] ? 0x00 : 0x01)); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + strcpy(card->driver, +#ifdef SNDRV_SBAWE_EMU8000 + snd_awe_port[dev] > 0 ? "SB AWE" : +#endif + "SB16"); + strcpy(card->shortname, chip->name); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma ", + chip->name, + chip->port, + irq); + if (dma8 >= 0) + sprintf(card->longname + strlen(card->longname), "%d", dma8); + if (dma16 >= 0) + sprintf(card->longname + strlen(card->longname), "%s%d", + dma8 >= 0 ? "&" : "", dma16); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_sb16_cards[dev] = card; + return 0; +} + +static int __init snd_sb16_probe_legacy_port(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + snd_port[dev] = port; + res = snd_sb16_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +#ifdef __ISAPNP__ + +static int __init snd_sb16_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || !snd_isapnp[dev]) + continue; + snd_sb16_isapnp_cards[dev] = card; + snd_sb16_isapnp_id[dev] = id; + res = snd_sb16_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} + +#endif /* __ISAPNP__ */ + +static int __init alsa_card_sb16_init(void) +{ + int dev, cards = 0; + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1}; + + /* legacy non-auto cards at first */ + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (!snd_sb16_probe(dev)) { + cards++; + continue; + } +#ifdef MODULE + snd_printk("Sound Blaster 16+ soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]); +#endif + } + /* legacy auto configured cards */ + cards += snd_legacy_auto_probe(possible_ports, snd_sb16_probe_legacy_port); +#ifdef __ISAPNP__ + /* ISA PnP cards at last */ + cards += isapnp_probe_cards(snd_sb16_pnpids, snd_sb16_isapnp_detect); +#endif + + if (!cards) { +#ifdef MODULE + snd_printk("Sound Blaster 16 soundcard not found or device busy\n"); +#ifdef SNDRV_SBAWE_EMU8000 + snd_printk("In case, if you have non-AWE card, try snd-card-sb16 module\n"); +#else + snd_printk("In case, if you have AWE card, try snd-card-sbawe module\n"); +#endif +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_sb16_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_sb16_cards[dev]); +} + +module_init(alsa_card_sb16_init) +module_exit(alsa_card_sb16_exit) + +#ifndef MODULE + +/* format is: snd-sb16=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port,snd_mpu_port,snd_fm_port, + snd_irq,snd_dma8,snd_dma16, + snd_mic_agc,snd_csp, + [snd_awe_port,snd_seq_ports] */ + +static int __init alsa_card_sb16_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + int __attribute__ ((__unused__)) csp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2 && + get_option(&str,&snd_dma16[nr_dev]) == 2 && + get_option(&str,&snd_mic_agc[nr_dev]) == 2 +#ifdef CONFIG_SND_SB16_CSP + && + get_option(&str,&snd_csp[nr_dev]) == 2 +#endif +#ifdef SNDRV_SBAWE_EMU8000 + && + get_option(&str,(int *)&snd_awe_port[nr_dev]) == 2 && + get_option(&str,&snd_seq_ports[nr_dev]) == 2 +#endif + ); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif +#ifdef CONFIG_SND_SB16_CSP + if (csp != INT_MAX) + snd_csp[nr_dev] = csp; +#endif + nr_dev++; + return 1; +} + +#ifndef SNDRV_SBAWE_EMU8000 +__setup("snd-sb16=", alsa_card_sb16_setup); +#else +__setup("snd-sbawe=", alsa_card_sb16_setup); +#endif + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/sb16_csp.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1196 @@ +/* + * Copyright (c) 1999 by Uros Bizjak + * Takashi Iwai + * + * SB16ASP/AWE32 CSP control + * + * CSP microcode loader: + * alsa-tools/sb16_csp/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t snd_sb_csp_t + +MODULE_AUTHOR("Uros Bizjak "); +MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +#ifdef SNDRV_LITTLE_ENDIAN +#define CSP_HDR_VALUE(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) +#else +#define CSP_HDR_VALUE(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24)) +#endif +#define LE_SHORT(v) le16_to_cpu(v) +#define LE_INT(v) le32_to_cpu(v) + +#define RIFF_HEADER CSP_HDR_VALUE('R', 'I', 'F', 'F') +#define CSP__HEADER CSP_HDR_VALUE('C', 'S', 'P', ' ') +#define LIST_HEADER CSP_HDR_VALUE('L', 'I', 'S', 'T') +#define FUNC_HEADER CSP_HDR_VALUE('f', 'u', 'n', 'c') +#define CODE_HEADER CSP_HDR_VALUE('c', 'o', 'd', 'e') +#define INIT_HEADER CSP_HDR_VALUE('i', 'n', 'i', 't') +#define MAIN_HEADER CSP_HDR_VALUE('m', 'a', 'i', 'n') + +/* + * RIFF data format + */ +typedef struct riff_header { + __u32 name; + __u32 len; +} riff_header_t; + +typedef struct desc_header { + riff_header_t info; + __u16 func_nr; + __u16 VOC_type; + __u16 flags_play_rec; + __u16 flags_16bit_8bit; + __u16 flags_stereo_mono; + __u16 flags_rates; +} desc_header_t; + +/* + * prototypes + */ +static void snd_sb_csp_free(snd_hwdep_t *hw); +static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file); +static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg); +static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file); + +static int csp_detect(sb_t *chip, int *version); +static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val); +static int set_register(sb_t *chip, unsigned char reg, unsigned char val); +static int read_register(sb_t *chip, unsigned char reg); +static int set_mode_register(sb_t *chip, unsigned char mode); +static int get_version(sb_t *chip); + +static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t * code); +static int snd_sb_csp_unload(snd_sb_csp_t * p); +static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags); +static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode); +static int snd_sb_csp_check_version(snd_sb_csp_t * p); + +static int snd_sb_csp_use(snd_sb_csp_t * p); +static int snd_sb_csp_unuse(snd_sb_csp_t * p); +static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels); +static int snd_sb_csp_stop(snd_sb_csp_t * p); +static int snd_sb_csp_pause(snd_sb_csp_t * p); +static int snd_sb_csp_restart(snd_sb_csp_t * p); + +static int snd_sb_qsound_build(snd_sb_csp_t * p); +static void snd_sb_qsound_destroy(snd_sb_csp_t * p); +static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p); + +static int init_proc_entry(snd_sb_csp_t * p, int device); +static void delete_proc_entry(snd_sb_csp_t * p); +static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); + +/* + * Detect CSP chip and create a new instance + */ +int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep) +{ + snd_sb_csp_t *p; + int version, err; + snd_hwdep_t *hw; + + if (rhwdep) + *rhwdep = NULL; + + if (csp_detect(chip, &version)) + return -ENODEV; + + if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0) + return err; + + if ((p = snd_magic_kcalloc(snd_sb_csp_t, 0, GFP_KERNEL)) == NULL) { + snd_device_free(chip->card, hw); + return -ENOMEM; + } + p->chip = chip; + p->version = version; + + /* CSP operators */ + p->ops.csp_use = snd_sb_csp_use; + p->ops.csp_unuse = snd_sb_csp_unuse; + p->ops.csp_autoload = snd_sb_csp_autoload; + p->ops.csp_start = snd_sb_csp_start; + p->ops.csp_stop = snd_sb_csp_stop; + p->ops.csp_qsound_transfer = snd_sb_csp_qsound_transfer; + + init_MUTEX(&p->access_mutex); + sprintf(hw->name, "CSP v%d.%d", (version >> 4), (version & 0x0f)); + hw->iface = SNDRV_HWDEP_IFACE_SB16CSP; + hw->private_data = p; + hw->private_free = snd_sb_csp_free; + + /* operators - only write/ioctl */ + hw->ops.open = snd_sb_csp_open; + hw->ops.ioctl = snd_sb_csp_ioctl; + hw->ops.release = snd_sb_csp_release; + + /* create a proc entry */ + init_proc_entry(p, device); + if (rhwdep) + *rhwdep = hw; + return 0; +} + +/* + * free_private for hwdep instance + */ +static void snd_sb_csp_free(snd_hwdep_t *hwdep) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hwdep->private_data, return); + if (p) { + if (p->running & SNDRV_SB_CSP_ST_RUNNING) + snd_sb_csp_stop(p); + delete_proc_entry(p); + snd_magic_kfree(p); + } +} + +/* ------------------------------ */ + +/* + * open the device exclusively + */ +static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + return (snd_sb_csp_use(p)); +} + +/* + * ioctl for hwdep device: + */ +static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + snd_sb_csp_info_t info; + snd_sb_csp_start_t start_info; + int err; + + snd_assert(p != NULL, return -EINVAL); + + if (snd_sb_csp_check_version(p)) + return -ENODEV; + + switch (cmd) { + /* get information */ + case SNDRV_SB_CSP_IOCTL_INFO: + *info.codec_name = *p->codec_name; + info.func_nr = p->func_nr; + info.acc_format = p->acc_format; + info.acc_channels = p->acc_channels; + info.acc_width = p->acc_width; + info.acc_rates = p->acc_rates; + info.csp_mode = p->mode; + info.run_channels = p->run_channels; + info.run_width = p->run_width; + info.version = p->version; + info.state = p->running; + err = copy_to_user((void *) arg, &info, sizeof(info)); + break; + + /* load CSP microcode */ + case SNDRV_SB_CSP_IOCTL_LOAD_CODE: + err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? + -EBUSY : snd_sb_csp_riff_load(p, (snd_sb_csp_microcode_t *) arg)); + break; + case SNDRV_SB_CSP_IOCTL_UNLOAD_CODE: + err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? + -EBUSY : snd_sb_csp_unload(p)); + break; + + /* change CSP running state */ + case SNDRV_SB_CSP_IOCTL_START: + if (copy_from_user(&start_info, (void *) arg, sizeof(start_info))) + err = -EFAULT; + else + err = snd_sb_csp_start(p, start_info.sample_width, start_info.channels); + break; + case SNDRV_SB_CSP_IOCTL_STOP: + err = snd_sb_csp_stop(p); + break; + case SNDRV_SB_CSP_IOCTL_PAUSE: + err = snd_sb_csp_pause(p); + break; + case SNDRV_SB_CSP_IOCTL_RESTART: + err = snd_sb_csp_restart(p); + break; + default: + err = -ENOTTY; + break; + } + + return err; +} + +/* + * close the device + */ +static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + return (snd_sb_csp_unuse(p)); +} + +/* ------------------------------ */ + +/* + * acquire device + */ +static int snd_sb_csp_use(snd_sb_csp_t * p) +{ + down(&p->access_mutex); + if (p->used) { + up(&p->access_mutex); + return -EAGAIN; + } + p->used++; + up(&p->access_mutex); + + return 0; + +} + +/* + * release device + */ +static int snd_sb_csp_unuse(snd_sb_csp_t * p) +{ + down(&p->access_mutex); + p->used--; + up(&p->access_mutex); + + return 0; +} + +/* + * load microcode via ioctl: + * code is user-space pointer + */ +static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t * mcode) +{ + snd_sb_csp_mc_header_t info; + + unsigned char *data_ptr, *data_end; + unsigned short func_nr = 0; + + riff_header_t file_h, item_h, code_h; + __u32 item_type; + desc_header_t funcdesc_h; + + unsigned long flags; + int err; + + if (copy_from_user(&info, mcode, sizeof(info))) + return -EFAULT; + data_ptr = mcode->data; + + if (copy_from_user(&file_h, data_ptr, sizeof(file_h))) + return -EFAULT; + if ((file_h.name != RIFF_HEADER) || + (LE_INT(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) { + snd_printd("%s: Invalid RIFF header\n", __FUNCTION__); + return -EINVAL; + } + data_ptr += sizeof(file_h); + data_end = data_ptr + LE_INT(file_h.len); + + if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) + return -EFAULT; + if (item_type != CSP__HEADER) { + snd_printd("%s: Invalid RIFF file type\n", __FUNCTION__); + return -EINVAL; + } + data_ptr += sizeof (item_type); + + for (; data_ptr < data_end; data_ptr += LE_INT(item_h.len)) { + if (copy_from_user(&item_h, data_ptr, sizeof(item_h))) + return -EFAULT; + data_ptr += sizeof(item_h); + if (item_h.name != LIST_HEADER) + continue; + + if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) + return -EFAULT; + switch (item_type) { + case FUNC_HEADER: + if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h))) + return -EFAULT; + func_nr = LE_SHORT(funcdesc_h.func_nr); + break; + case CODE_HEADER: + if (func_nr != info.func_req) + break; /* not required function, try next */ + data_ptr += sizeof(item_type); + + /* destroy QSound mixer element */ + if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { + snd_sb_qsound_destroy(p); + } + /* Clear all flags */ + p->running = 0; + p->mode = 0; + + /* load microcode blocks */ + for (;;) { + if (data_ptr >= data_end) + return -EINVAL; + if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) + return -EFAULT; + + /* init microcode blocks */ + if (code_h.name != INIT_HEADER) + break; + data_ptr += sizeof(code_h); + err = snd_sb_csp_load(p, data_ptr, LE_INT(code_h.len), + SNDRV_SB_CSP_LOAD_INITBLOCK | SNDRV_SB_CSP_LOAD_FROMUSER); + if (err) + return err; + data_ptr += LE_INT(code_h.len); + } + /* main microcode block */ + if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) + return -EFAULT; + + if (code_h.name != MAIN_HEADER) { + snd_printd("%s: Missing 'main' microcode\n", __FUNCTION__); + return -EINVAL; + } + data_ptr += sizeof(code_h); + err = snd_sb_csp_load(p, data_ptr, LE_INT(code_h.len), + SNDRV_SB_CSP_LOAD_FROMUSER); + if (err) + return err; + + /* fill in codec header */ + strncpy(p->codec_name, info.codec_name, sizeof(p->codec_name) - 1); + p->codec_name[sizeof(p->codec_name) - 1] = 0; + p->func_nr = func_nr; + p->mode = LE_SHORT(funcdesc_h.flags_play_rec); + switch (LE_SHORT(funcdesc_h.VOC_type)) { + case 0x0001: /* QSound decoder */ + if (LE_SHORT(funcdesc_h.flags_play_rec) == SNDRV_SB_CSP_MODE_DSP_WRITE) { + if (snd_sb_qsound_build(p) == 0) + /* set QSound flag and clear all other mode flags */ + p->mode = SNDRV_SB_CSP_MODE_QSOUND; + } + p->acc_format = 0; + break; + case 0x0006: /* A Law codec */ + p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; + break; + case 0x0007: /* Mu Law codec */ + p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; + break; + case 0x0011: /* what Creative thinks is IMA ADPCM codec */ + case 0x0200: /* Creative ADPCM codec */ + p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; + break; + case 201: /* Text 2 Speech decoder */ + /* TODO: Text2Speech handling routines */ + p->acc_format = 0; + break; + case 0x0202: /* Fast Speech 8 codec */ + case 0x0203: /* Fast Speech 10 codec */ + p->acc_format = SNDRV_PCM_FMTBIT_SPECIAL; + break; + default: /* other codecs are unsupported */ + p->acc_format = p->acc_width = p->acc_rates = 0; + p->mode = 0; + snd_printd("%s: Unsupported CSP codec type: 0x%04x\n", + __FUNCTION__, + LE_SHORT(funcdesc_h.VOC_type)); + return -EINVAL; + } + p->acc_channels = LE_SHORT(funcdesc_h.flags_stereo_mono); + p->acc_width = LE_SHORT(funcdesc_h.flags_16bit_8bit); + p->acc_rates = LE_SHORT(funcdesc_h.flags_rates); + + /* Decouple CSP from IRQ and DMAREQ lines */ + spin_lock_irqsave(&p->chip->reg_lock, flags); + set_mode_register(p->chip, 0xfc); + set_mode_register(p->chip, 0x00); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + + /* finished loading successfully */ + p->running = SNDRV_SB_CSP_ST_LOADED; /* set LOADED flag */ + return 0; + } + } + snd_printd("%s: Function #%d not found\n", __FUNCTION__, info.func_req); + return -EINVAL; +} + +/* + * unload CSP microcode + */ +static int snd_sb_csp_unload(snd_sb_csp_t * p) +{ + if (p->running & SNDRV_SB_CSP_ST_RUNNING) + return -EBUSY; + if (!(p->running & SNDRV_SB_CSP_ST_LOADED)) + return -ENXIO; + + /* clear supported formats */ + p->acc_format = 0; + p->acc_channels = p->acc_width = p->acc_rates = 0; + /* destroy QSound mixer element */ + if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { + snd_sb_qsound_destroy(p); + } + /* clear all flags */ + p->running = 0; + p->mode = 0; + return 0; +} + +/* + * send command sequence to DSP + */ +static inline int command_seq(sb_t *chip, const unsigned char *seq, int size) +{ + int i; + for (i = 0; i < size; i++) { + if (!snd_sbdsp_command(chip, seq[i])) + return -EIO; + } + return 0; +} + +/* + * set CSP codec parameter + */ +static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val) +{ + unsigned char dsp_cmd[3]; + + dsp_cmd[0] = 0x05; /* CSP set codec parameter */ + dsp_cmd[1] = val; /* Parameter value */ + dsp_cmd[2] = par; /* Parameter */ + command_seq(chip, dsp_cmd, 3); + snd_sbdsp_command(chip, 0x03); /* DSP read? */ + if (snd_sbdsp_get_byte(chip) != par) + return -EIO; + return 0; +} + +/* + * set CSP register + */ +static int set_register(sb_t *chip, unsigned char reg, unsigned char val) +{ + unsigned char dsp_cmd[3]; + + dsp_cmd[0] = 0x0e; /* CSP set register */ + dsp_cmd[1] = reg; /* CSP Register */ + dsp_cmd[2] = val; /* value */ + return command_seq(chip, dsp_cmd, 3); +} + +/* + * read CSP register + * return < 0 -> error + */ +static int read_register(sb_t *chip, unsigned char reg) +{ + unsigned char dsp_cmd[2]; + + dsp_cmd[0] = 0x0f; /* CSP read register */ + dsp_cmd[1] = reg; /* CSP Register */ + command_seq(chip, dsp_cmd, 2); + return snd_sbdsp_get_byte(chip); /* Read DSP value */ +} + +/* + * set CSP mode register + */ +static int set_mode_register(sb_t *chip, unsigned char mode) +{ + unsigned char dsp_cmd[2]; + + dsp_cmd[0] = 0x04; /* CSP set mode register */ + dsp_cmd[1] = mode; /* mode */ + return command_seq(chip, dsp_cmd, 2); +} + +/* + * Detect CSP + * return 0 if CSP exists. + */ +static int csp_detect(sb_t *chip, int *version) +{ + unsigned char csp_test1, csp_test2; + unsigned long flags; + int result = -ENODEV; + + spin_lock_irqsave(&chip->reg_lock, flags); + + set_codec_parameter(chip, 0x00, 0x00); + set_mode_register(chip, 0xfc); /* 0xfc = ?? */ + + csp_test1 = read_register(chip, 0x83); + set_register(chip, 0x83, ~csp_test1); + csp_test2 = read_register(chip, 0x83); + if (csp_test2 != (csp_test1 ^ 0xff)) + goto __fail; + + set_register(chip, 0x83, csp_test1); + csp_test2 = read_register(chip, 0x83); + if (csp_test2 != csp_test1) + goto __fail; + + set_mode_register(chip, 0x00); /* 0x00 = ? */ + + *version = get_version(chip); + snd_sbdsp_reset(chip); /* reset DSP after getversion! */ + if (*version >= 0x10 && *version <= 0x1f) + result = 0; /* valid version id */ + + __fail: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +/* + * get CSP version number + */ +static int get_version(sb_t *chip) +{ + unsigned char dsp_cmd[2]; + + dsp_cmd[0] = 0x08; /* SB_DSP_!something! */ + dsp_cmd[1] = 0x03; /* get chip version id? */ + command_seq(chip, dsp_cmd, 2); + + return (snd_sbdsp_get_byte(chip)); +} + +/* + * check if the CSP version is valid + */ +static int snd_sb_csp_check_version(snd_sb_csp_t * p) +{ + if (p->version < 0x10 || p->version > 0x1f) { + snd_printd("%s: Invalid CSP version: 0x%x\n", __FUNCTION__, p->version); + return 1; + } + return 0; +} + +/* + * download microcode to CSP (microcode should have one "main" block). + */ +static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags) +{ + int status, i; + int err; + int result = -EIO; + unsigned long flags; + + spin_lock_irqsave(&p->chip->reg_lock, flags); + snd_sbdsp_command(p->chip, 0x01); /* CSP download command */ + if (snd_sbdsp_get_byte(p->chip)) { + snd_printd("%s: Download command failed\n", __FUNCTION__); + goto __fail; + } + /* Send CSP low byte (size - 1) */ + snd_sbdsp_command(p->chip, (unsigned char)(size - 1)); + /* Send high byte */ + snd_sbdsp_command(p->chip, (unsigned char)((size - 1) >> 8)); + /* send microcode sequence */ + if (load_flags & SNDRV_SB_CSP_LOAD_FROMUSER) { + /* copy microcode from user space */ + unsigned char *kbuf, *_kbuf; + _kbuf = kbuf = kmalloc (size, GFP_KERNEL); + if (copy_from_user(kbuf, buf, size)) { + result = -EFAULT; + kfree (_kbuf); + goto __fail; + } + while (size--) { + if (!snd_sbdsp_command(p->chip, *kbuf++)) { + kfree (_kbuf); + goto __fail; + } + } + kfree (_kbuf); + } else { + /* load from kernel space */ + while (size--) { + if (!snd_sbdsp_command(p->chip, *buf++)) + goto __fail; + } + } + if (snd_sbdsp_get_byte(p->chip)) + goto __fail; + + if (load_flags & SNDRV_SB_CSP_LOAD_INITBLOCK) { + i = 0; + /* some codecs (FastSpeech) take some time to initialize */ + while (1) { + snd_sbdsp_command(p->chip, 0x03); + status = snd_sbdsp_get_byte(p->chip); + if (status == 0x55 || ++i >= 10) + break; + udelay (10); + } + if (status != 0x55) { + snd_printd("%s: Microcode initialization failed\n", __FUNCTION__); + goto __fail; + } + } else { + /* + * Read mixer register SB_DSP4_DMASETUP after loading 'main' code. + * Start CSP chip if no 16bit DMA channel is set - some kind + * of autorun or perhaps a bugfix? + */ + spin_lock(&p->chip->mixer_lock); + status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP); + spin_unlock(&p->chip->mixer_lock); + if (!(status & (SB_DMASETUP_DMA7 | SB_DMASETUP_DMA6 | SB_DMASETUP_DMA5))) { + err = (set_codec_parameter(p->chip, 0xaa, 0x00) || + set_codec_parameter(p->chip, 0xff, 0x00)); + snd_sbdsp_reset(p->chip); /* really! */ + if (err) + goto __fail; + set_mode_register(p->chip, 0xc0); /* c0 = STOP */ + set_mode_register(p->chip, 0x70); /* 70 = RUN */ + } + } + result = 0; + + __fail: + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + return result; +} + +#include "sb16_csp_codecs.h" + +/* + * autoload hardware codec if necessary + * return 0 if CSP is loaded and ready to run (p->running != 0) + */ +static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode) +{ + unsigned long flags; + int err = 0; + + /* if CSP is running or manually loaded then exit */ + if (p->running & (SNDRV_SB_CSP_ST_RUNNING | SNDRV_SB_CSP_ST_LOADED)) + return -EBUSY; + + /* autoload microcode only if requested hardware codec is not already loaded */ + if (((1 << pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) { + p->running = SNDRV_SB_CSP_ST_AUTO; + } else { + switch (pcm_sfmt) { + case SNDRV_PCM_FORMAT_MU_LAW: + err = snd_sb_csp_load(p, &mulaw_main[0], sizeof(mulaw_main), 0); + p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; + p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; + break; + case SNDRV_PCM_FORMAT_A_LAW: + err = snd_sb_csp_load(p, &alaw_main[0], sizeof(alaw_main), 0); + p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; + p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; + break; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + err = snd_sb_csp_load(p, &ima_adpcm_init[0], sizeof(ima_adpcm_init), + SNDRV_SB_CSP_LOAD_INITBLOCK); + if (err) + break; + if (play_rec_mode == SNDRV_SB_CSP_MODE_DSP_WRITE) { + err = snd_sb_csp_load(p, &ima_adpcm_playback[0], + sizeof(ima_adpcm_playback), 0); + p->mode = SNDRV_SB_CSP_MODE_DSP_WRITE; + } else { + err = snd_sb_csp_load(p, &ima_adpcm_capture[0], + sizeof(ima_adpcm_capture), 0); + p->mode = SNDRV_SB_CSP_MODE_DSP_READ; + } + p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; + break; + default: + /* Decouple CSP from IRQ and DMAREQ lines */ + if (p->running & SNDRV_SB_CSP_ST_AUTO) { + spin_lock_irqsave(&p->chip->reg_lock, flags); + set_mode_register(p->chip, 0xfc); + set_mode_register(p->chip, 0x00); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + p->running = 0; /* clear autoloaded flag */ + } + return -EINVAL; + } + if (err) { + p->acc_format = 0; + p->acc_channels = p->acc_width = p->acc_rates = 0; + + p->running = 0; /* clear autoloaded flag */ + p->mode = 0; + return (err); + } else { + p->running = SNDRV_SB_CSP_ST_AUTO; /* set autoloaded flag */ + p->acc_width = SNDRV_SB_CSP_SAMPLE_16BIT; /* only 16 bit data */ + p->acc_channels = SNDRV_SB_CSP_MONO | SNDRV_SB_CSP_STEREO; + p->acc_rates = SNDRV_SB_CSP_RATE_ALL; /* HW codecs accept all rates */ + } + + } + return (p->running & SNDRV_SB_CSP_ST_AUTO) ? 0 : -ENXIO; +} + +/* + * start CSP + */ +static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels) +{ + unsigned char s_type; /* sample type */ + unsigned char mixL, mixR; + int result = -EIO; + unsigned long flags; + + if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) { + snd_printd("%s: Microcode not loaded\n", __FUNCTION__); + return -ENXIO; + } + if (p->running & SNDRV_SB_CSP_ST_RUNNING) { + snd_printd("%s: CSP already running\n", __FUNCTION__); + return -EBUSY; + } + if (!(sample_width & p->acc_width)) { + snd_printd("%s: Unsupported PCM sample width\n", __FUNCTION__); + return -EINVAL; + } + if (!(channels & p->acc_channels)) { + snd_printd("%s: Invalid number of channels\n", __FUNCTION__); + return -EINVAL; + } + + /* Mute PCM volume */ + spin_lock_irqsave(&p->chip->mixer_lock, flags); + mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV); + mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7); + + spin_lock(&p->chip->reg_lock); + set_mode_register(p->chip, 0xc0); /* c0 = STOP */ + set_mode_register(p->chip, 0x70); /* 70 = RUN */ + + s_type = 0x00; + if (channels == SNDRV_SB_CSP_MONO) + s_type = 0x11; /* 000n 000n (n = 1 if mono) */ + if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT) + s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */ + + if (set_codec_parameter(p->chip, 0x81, s_type)) { + snd_printd("%s: Set sample type command failed\n", __FUNCTION__); + goto __fail; + } + if (set_codec_parameter(p->chip, 0x80, 0x00)) { + snd_printd("%s: Codec start command failed\n", __FUNCTION__); + goto __fail; + } + p->run_width = sample_width; + p->run_channels = channels; + + p->running |= SNDRV_SB_CSP_ST_RUNNING; + + if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) { + set_codec_parameter(p->chip, 0xe0, 0x01); + /* enable QSound decoder */ + set_codec_parameter(p->chip, 0x00, 0xff); + set_codec_parameter(p->chip, 0x01, 0xff); + p->running |= SNDRV_SB_CSP_ST_QSOUND; + /* set QSound startup value */ + snd_sb_csp_qsound_transfer(p); + } + result = 0; + + __fail: + spin_unlock(&p->chip->reg_lock); + + /* restore PCM volume */ + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR); + spin_unlock_irqrestore(&p->chip->mixer_lock, flags); + + return result; +} + +/* + * stop CSP + */ +static int snd_sb_csp_stop(snd_sb_csp_t * p) +{ + int result; + unsigned char mixL, mixR; + unsigned long flags; + + if (!(p->running & SNDRV_SB_CSP_ST_RUNNING)) + return 0; + + /* Mute PCM volume */ + spin_lock_irqsave(&p->chip->mixer_lock, flags); + mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV); + mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7); + + spin_lock(&p->chip->reg_lock); + if (p->running & SNDRV_SB_CSP_ST_QSOUND) { + set_codec_parameter(p->chip, 0xe0, 0x01); + /* disable QSound decoder */ + set_codec_parameter(p->chip, 0x00, 0x00); + set_codec_parameter(p->chip, 0x01, 0x00); + + p->running &= ~SNDRV_SB_CSP_ST_QSOUND; + } + result = set_mode_register(p->chip, 0xc0); /* c0 = STOP */ + spin_unlock(&p->chip->reg_lock); + + /* restore PCM volume */ + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR); + spin_unlock_irqrestore(&p->chip->mixer_lock, flags); + + if (!(result)) + p->running &= ~(SNDRV_SB_CSP_ST_PAUSED | SNDRV_SB_CSP_ST_RUNNING); + return result; +} + +/* + * pause CSP codec and hold DMA transfer + */ +static int snd_sb_csp_pause(snd_sb_csp_t * p) +{ + int result; + unsigned long flags; + + if (!(p->running & SNDRV_SB_CSP_ST_RUNNING)) + return -EBUSY; + + spin_lock_irqsave(&p->chip->reg_lock, flags); + result = set_codec_parameter(p->chip, 0x80, 0xff); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + if (!(result)) + p->running |= SNDRV_SB_CSP_ST_PAUSED; + + return result; +} + +/* + * restart CSP codec and resume DMA transfer + */ +static int snd_sb_csp_restart(snd_sb_csp_t * p) +{ + int result; + unsigned long flags; + + if (!(p->running & SNDRV_SB_CSP_ST_PAUSED)) + return -EBUSY; + + spin_lock_irqsave(&p->chip->reg_lock, flags); + result = set_codec_parameter(p->chip, 0x80, 0x00); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + if (!(result)) + p->running &= ~SNDRV_SB_CSP_ST_PAUSED; + + return result; +} + +/* ------------------------------ */ + +/* + * QSound mixer control for PCM + */ + +static int snd_sb_qsound_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_sb_qsound_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = p->q_enabled ? 1 : 0; + return 0; +} + +static int snd_sb_qsound_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval; + + nval = ucontrol->value.integer.value[0] & 0x01; + spin_lock_irqsave(&p->q_lock, flags); + change = p->q_enabled != nval; + p->q_enabled = nval; + spin_unlock_irqrestore(&p->q_lock, flags); + return change; +} + +static int snd_sb_qsound_space_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; + return 0; +} + +static int snd_sb_qsound_space_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&p->q_lock, flags); + ucontrol->value.integer.value[0] = p->qpos_left; + ucontrol->value.integer.value[1] = p->qpos_right; + spin_unlock_irqrestore(&p->q_lock, flags); + return 0; +} + +static int snd_sb_qsound_space_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval1, nval2; + + nval1 = ucontrol->value.integer.value[0]; + if (nval1 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT) + nval1 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; + nval2 = ucontrol->value.integer.value[1]; + if (nval2 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT) + nval2 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; + spin_lock_irqsave(&p->q_lock, flags); + change = p->qpos_left != nval1 || p->qpos_right != nval2; + p->qpos_left = nval1; + p->qpos_right = nval2; + p->qpos_changed = change; + spin_unlock_irqrestore(&p->q_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_sb_qsound_switch = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "3D Control - Switch", + info: snd_sb_qsound_switch_info, + get: snd_sb_qsound_switch_get, + put: snd_sb_qsound_switch_put +}; + +static snd_kcontrol_new_t snd_sb_qsound_space = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "3D Control - Space", + info: snd_sb_qsound_space_info, + get: snd_sb_qsound_space_get, + put: snd_sb_qsound_space_put +}; + +static int snd_sb_qsound_build(snd_sb_csp_t * p) +{ + snd_card_t * card; + int err; + + snd_assert(p != NULL, return -EINVAL); + + card = p->chip->card; + p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2; + p->qpos_changed = 0; + + spin_lock_init(&p->q_lock); + + if ((err = snd_ctl_add(card, p->qsound_switch = snd_ctl_new1(&snd_sb_qsound_switch, p))) < 0) + goto __error; + if ((err = snd_ctl_add(card, p->qsound_space = snd_ctl_new1(&snd_sb_qsound_space, p))) < 0) + goto __error; + + return 0; + + __error: + snd_sb_qsound_destroy(p); + return err; +} + +static void snd_sb_qsound_destroy(snd_sb_csp_t * p) +{ + snd_card_t * card; + unsigned long flags; + + snd_assert(p != NULL, return); + + card = p->chip->card; + + if (p->qsound_switch) + snd_ctl_remove(card, p->qsound_switch); + if (p->qsound_space) + snd_ctl_remove(card, p->qsound_space); + + /* cancel pending transfer of QSound parameters */ + spin_lock_irqsave (&p->q_lock, flags); + p->qpos_changed = 0; + spin_unlock_irqrestore (&p->q_lock, flags); +} + +/* + * Transfer qsound parameters to CSP, + * function should be called from interrupt routine + */ +static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p) +{ + int err = -ENXIO; + + spin_lock(&p->q_lock); + if (p->running & SNDRV_SB_CSP_ST_QSOUND) { + set_codec_parameter(p->chip, 0xe0, 0x01); + /* left channel */ + set_codec_parameter(p->chip, 0x00, p->qpos_left); + set_codec_parameter(p->chip, 0x02, 0x00); + /* right channel */ + set_codec_parameter(p->chip, 0x00, p->qpos_right); + set_codec_parameter(p->chip, 0x03, 0x00); + err = 0; + } + p->qpos_changed = 0; + spin_unlock(&p->q_lock); + return err; +} + +/* ------------------------------ */ + +/* + * proc interface + */ +static int init_proc_entry(snd_sb_csp_t * p, int device) +{ + char name[16]; + snd_info_entry_t *entry; + sprintf(name, "cspD%d", device); + entry = p->proc = snd_info_create_card_entry(p->chip->card, name, p->chip->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 256; + entry->c.text.read = info_read; + entry->private_data = p; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + p->proc = NULL; + } + } + return 0; +} + +static void delete_proc_entry(snd_sb_csp_t * p) +{ + if (p->proc) { + snd_info_unregister(p->proc); + p->proc = NULL; + } +} + +static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, entry->private_data, return); + + snd_iprintf(buffer, "Creative Signal Processor [v%d.%d]\n", (p->version >> 4), (p->version & 0x0f)); + snd_iprintf(buffer, "State: %cx%c%c%c\n", ((p->running & SNDRV_SB_CSP_ST_QSOUND) ? 'Q' : '-'), + ((p->running & SNDRV_SB_CSP_ST_PAUSED) ? 'P' : '-'), + ((p->running & SNDRV_SB_CSP_ST_RUNNING) ? 'R' : '-'), + ((p->running & SNDRV_SB_CSP_ST_LOADED) ? 'L' : '-')); + if (p->running & SNDRV_SB_CSP_ST_LOADED) { + snd_iprintf(buffer, "Codec: %s [func #%d]\n", p->codec_name, p->func_nr); + snd_iprintf(buffer, "Sample rates: "); + if (p->acc_rates == SNDRV_SB_CSP_RATE_ALL) { + snd_iprintf(buffer, "All\n"); + } else { + snd_iprintf(buffer, "%s%s%s%s\n", + ((p->acc_rates & SNDRV_SB_CSP_RATE_8000) ? "8000Hz " : ""), + ((p->acc_rates & SNDRV_SB_CSP_RATE_11025) ? "11025Hz " : ""), + ((p->acc_rates & SNDRV_SB_CSP_RATE_22050) ? "22050Hz " : ""), + ((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : "")); + } + if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { + snd_iprintf(buffer, "QSound decoder %sabled\n", + p->q_enabled ? "en" : "dis"); + } else { + snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n", + p->acc_format, + ((p->acc_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? "16bit" : "-"), + ((p->acc_width & SNDRV_SB_CSP_SAMPLE_8BIT) ? "8bit" : "-"), + ((p->acc_channels & SNDRV_SB_CSP_MONO) ? "mono" : "-"), + ((p->acc_channels & SNDRV_SB_CSP_STEREO) ? "stereo" : "-"), + ((p->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) ? "playback" : "-"), + ((p->mode & SNDRV_SB_CSP_MODE_DSP_READ) ? "capture" : "-")); + } + } + if (p->running & SNDRV_SB_CSP_ST_AUTO) { + snd_iprintf(buffer, "Autoloaded Mu-Law, A-Law or Ima-ADPCM hardware codec\n"); + } + if (p->running & SNDRV_SB_CSP_ST_RUNNING) { + snd_iprintf(buffer, "Processing %dbit %s PCM samples\n", + ((p->run_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? 16 : 8), + ((p->run_channels & SNDRV_SB_CSP_MONO) ? "mono" : "stereo")); + } + if (p->running & SNDRV_SB_CSP_ST_QSOUND) { + snd_iprintf(buffer, "Qsound position: left = 0x%x, right = 0x%x\n", + p->qpos_left, p->qpos_right); + } +} + +/* */ + +EXPORT_SYMBOL(snd_sb_csp_new); + +/* + * INIT part + */ + +static int __init alsa_sb_csp_init(void) +{ + return 0; +} + +static void __exit alsa_sb_csp_exit(void) +{ +} + +module_init(alsa_sb_csp_init) +module_exit(alsa_sb_csp_exit) diff -Nru a/sound/isa/sb/sb16_csp_codecs.h b/sound/isa/sb/sb16_csp_codecs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/sb16_csp_codecs.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,949 @@ +/* + * Copyright (c) 1994 Creative Technology Ltd. + * Microcode files for SB16 Advanced Signal Processor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +static unsigned char mulaw_main[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44, + 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, + 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b, + 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80, + 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b, + 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80, + 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80, + 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82, + 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19, + 0xa2, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19, + 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24, + 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44, + 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef, + 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1, + 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80, + 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54, + 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0, + 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44, + 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4, + 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4, + 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4, + 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4, + 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4, + 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4, + 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80, + 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b, + 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4, + 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88, + 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39, + 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab, + 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84, + 0x03, 0x03, 0x04, 0x49, 0x08, 0xc2, 0x00, 0x54, + 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84, + 0x3e, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x08, 0x01, 0x00, 0x44, 0x6c, 0x00, 0x51, 0x8b, + 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b, + 0x04, 0x21, 0x00, 0x84, 0xfd, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44, + 0xfe, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, + 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, + 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19, + 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, + 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc7, 0x20, 0x04, 0x19, 0x5e, 0x00, 0x71, 0x8b, + 0xcf, 0x00, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, + 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19, + 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19, + 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19, + 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19, + 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a, + 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, + 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f, + 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e, + 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22, + 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80, + 0x08, 0xa8, 0x00, 0x44, 0x20, 0x31, 0x49, 0x5c, + 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e, + 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0, + 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b, + 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02, + 0xcb, 0x00, 0xa8, 0x58, 0xb0, 0x05, 0xf3, 0x80, + 0x20, 0x04, 0xa8, 0x10, 0x00, 0x00, 0x10, 0x39, + 0xb0, 0x00, 0xe0, 0x8b, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02, + 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0, + 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e, + 0xff, 0xff, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22, + 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80, + 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80, + 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a, + 0x08, 0x83, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80, + 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a, + 0x0c, 0xa3, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82, + 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80, + 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0x73, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0, + 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02, + 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80, + 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80, + 0x00, 0xf3, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0xd3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x00, 0xb3, 0x10, 0xcc, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x71, 0xc0, + 0x00, 0x30, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44, + 0x20, 0x01, 0x00, 0x80, 0xff, 0xff, 0x62, 0x8b, + 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0xe1, 0x09, 0x5c, + 0x82, 0x00, 0x09, 0x2f, 0x80, 0x4a, 0x09, 0x8e, + 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80, + 0x00, 0x00, 0x7a, 0xcb, 0x03, 0x00, 0xa8, 0x18, + 0x00, 0x00, 0x10, 0x39, 0x08, 0x04, 0xea, 0x10, + 0x08, 0x04, 0x7a, 0x10, 0x20, 0x00, 0x00, 0x80, + 0x40, 0x00, 0x21, 0xcb, 0x0c, 0x00, 0xe8, 0x10, + 0x00, 0x00, 0x41, 0x02, 0x0c, 0x00, 0xeb, 0x10, + 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02, + 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19, + 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a, + 0x0c, 0xe8, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a, + 0x03, 0x00, 0x48, 0x0a, 0x00, 0xb8, 0x04, 0x54, + 0xc3, 0x00, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0x44, + 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0xb8, 0x04, 0x54, + 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a, + 0x09, 0x00, 0x48, 0x0a, 0x00, 0x68, 0x04, 0x54, + 0xc9, 0x00, 0x04, 0x19, 0x0c, 0x68, 0x00, 0x44, + 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0x68, 0x04, 0x54, + 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a, + 0x06, 0x00, 0x48, 0x0a, 0x00, 0x78, 0x04, 0x54, + 0xc6, 0x00, 0x04, 0x19, 0x0c, 0x78, 0x00, 0x44, + 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0x78, 0x04, 0x54, + 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, + 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xe8, 0x04, 0x54, + 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xe8, 0x00, 0x44, + 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xe8, 0x04, 0x54, + 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x10, 0x71, 0x8b, 0x09, 0x3f, 0x07, 0x00 +}; + +static unsigned char alaw_main[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44, + 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, + 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b, + 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80, + 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b, + 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80, + 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80, + 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82, + 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19, + 0x21, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19, + 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24, + 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44, + 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef, + 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1, + 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80, + 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54, + 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0, + 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44, + 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4, + 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4, + 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4, + 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4, + 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4, + 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4, + 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80, + 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b, + 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4, + 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88, + 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39, + 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab, + 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84, + 0x03, 0x03, 0x04, 0x49, 0x04, 0xc2, 0x00, 0x54, + 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84, + 0xbe, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x08, 0x01, 0x00, 0x44, 0xec, 0x00, 0x51, 0x8b, + 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b, + 0x04, 0x21, 0x00, 0x84, 0x3f, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44, + 0x3d, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, + 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, + 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19, + 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, + 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc7, 0x20, 0x04, 0x19, 0xde, 0x00, 0x51, 0x8b, + 0xcf, 0x00, 0x00, 0x39, 0x00, 0x01, 0xb1, 0x80, + 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19, + 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19, + 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19, + 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19, + 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a, + 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, + 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f, + 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e, + 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22, + 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80, + 0x08, 0x48, 0x00, 0x44, 0x20, 0xb1, 0x49, 0x5c, + 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e, + 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0, + 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b, + 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02, + 0xc0, 0x00, 0x00, 0x82, 0x0c, 0xc3, 0x08, 0x49, + 0xb0, 0x01, 0xf3, 0x80, 0x00, 0x00, 0x10, 0x39, + 0x20, 0x00, 0x0c, 0x89, 0x0c, 0x88, 0x08, 0x49, + 0x03, 0x00, 0xa8, 0x18, 0x00, 0x00, 0x10, 0x39, + 0xbd, 0xff, 0x62, 0x8b, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02, + 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0, + 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e, + 0xae, 0xae, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22, + 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80, + 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80, + 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a, + 0x08, 0xa3, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80, + 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a, + 0x0c, 0x93, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82, + 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80, + 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0xc3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0, + 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02, + 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80, + 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80, + 0x00, 0x08, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0xf3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x00, 0x71, 0xc0, 0x00, 0x00, 0x93, 0x10, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x08, 0x00, 0x44, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x00, 0xc0, + 0x00, 0x30, 0x71, 0xc0, 0x00, 0x08, 0x00, 0x44, + 0x20, 0x01, 0x00, 0x80, 0xae, 0xae, 0x62, 0x8b, + 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0xa1, 0x49, 0x5c, + 0x82, 0x00, 0x09, 0x6e, 0x80, 0x4a, 0x09, 0x8e, + 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80, + 0x00, 0x00, 0x7a, 0xcb, 0x28, 0x04, 0xea, 0x10, + 0x0c, 0x04, 0x7a, 0x10, 0x70, 0x00, 0xc0, 0x8b, + 0x00, 0x00, 0x10, 0x39, 0x90, 0x03, 0x00, 0x80, + 0x40, 0x00, 0x21, 0x5b, 0x90, 0x00, 0x61, 0x80, + 0x0c, 0x8a, 0x08, 0x49, 0x00, 0x00, 0x1c, 0x19, + 0x40, 0x00, 0x08, 0x5b, 0x08, 0x00, 0x08, 0x49, + 0x20, 0x02, 0x00, 0x80, 0x03, 0x00, 0xa8, 0x18, + 0x00, 0x00, 0x14, 0x19, 0x40, 0x00, 0x21, 0xcb, + 0x00, 0x00, 0x41, 0x02, 0x00, 0x00, 0xeb, 0x80, + 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02, + 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19, + 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a, + 0x0c, 0x0a, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a, + 0x03, 0x00, 0x48, 0x0a, 0x00, 0x58, 0x04, 0x54, + 0xc3, 0x00, 0x04, 0x19, 0x0c, 0x58, 0x00, 0x44, + 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0x58, 0x04, 0x54, + 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a, + 0x09, 0x00, 0x48, 0x0a, 0x00, 0xc8, 0x04, 0x54, + 0xc9, 0x00, 0x04, 0x19, 0x0c, 0xc8, 0x00, 0x44, + 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0xc8, 0x04, 0x54, + 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a, + 0x06, 0x00, 0x48, 0x0a, 0x00, 0xd8, 0x04, 0x54, + 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xd8, 0x00, 0x44, + 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xd8, 0x04, 0x54, + 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, + 0x0c, 0x00, 0x48, 0x0a, 0x00, 0x0a, 0x04, 0x54, + 0xcc, 0x00, 0x04, 0x19, 0x0c, 0x0a, 0x00, 0x44, + 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0x0a, 0x04, 0x54, + 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x10, 0x71, 0x8b, 0x08, 0x42, 0x06, 0x00 +}; + + +static unsigned char ima_adpcm_init[] = { + 0x00, 0x10, 0x00, 0x44, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x40, 0x45, 0xaa, 0xaa, 0x71, 0x8b, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0xff, 0x6e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x05, 0xb1, 0x80, 0x62, 0x00, 0x19, 0x0e, + 0x21, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x40, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x60, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x50, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x70, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xd0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x02, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x22, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x32, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x62, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xf2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x11, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x61, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x13, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x18, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x68, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x0a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x4a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x29, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x79, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9b, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x14, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xf4, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe6, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe5, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xd7, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2e, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9d, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xef, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb2, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x33, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2a, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x3b, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x46, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2c, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdd, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x01, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9a, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x16, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x8e, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc2, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc9, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x3c, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x81, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xd4, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x10, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x34, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x02, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x75, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9a, 0xb0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x12, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x0d, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x3c, 0x60, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe7, 0x50, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x0e, 0x70, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xff, 0xc0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc8, 0xd0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x57, 0xf0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc8, 0x22, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb0, 0x32, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdd, 0x82, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x90, 0xb2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x8a, 0x62, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xce, 0x72, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa5, 0xd2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x97, 0x21, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa2, 0xa1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x5c, 0x41, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xfe, 0xc1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x7a, 0x23, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x78, 0x93, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x67, 0x73, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x17, 0x28, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x88, 0x48, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdb, 0xf8, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2b, 0xba, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xf1, 0x09, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdc, 0x69, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x19, 0x8b, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xff, 0xfb, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x20, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x52, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xff, 0xff, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0x10, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0x80, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0x90, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0x40, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0xff, 0xff, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, + 0x10, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x80, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x90, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x40, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xff, 0xfb, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x00, 0x04, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x4a, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x30, 0x04, 0x19, + 0x10, 0x00, 0x09, 0x4f, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0x00, 0x10, 0x71, 0x8b, 0xc1, 0x30, 0x04, 0x19, + 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09, + 0xcf, 0x30, 0x00, 0x09, 0x00, 0x00, 0x34, 0x49, + 0x00, 0x08, 0x00, 0x44, 0xc8, 0x54, 0x11, 0x00 +}; + +static unsigned char ima_adpcm_playback[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x0c, 0x50, 0x00, 0x44, 0x00, 0x70, 0x00, 0x44, + 0x04, 0x70, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0d, 0xd4, 0x49, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x01, 0xb1, 0x80, 0x00, 0x01, 0xb1, 0x80, + 0xc9, 0x20, 0x04, 0x19, 0x51, 0x00, 0x71, 0x8b, + 0xcd, 0x00, 0x04, 0x19, 0xe4, 0x20, 0x71, 0x8b, + 0xcf, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b, + 0xcb, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc4, 0x20, 0x04, 0x19, 0x65, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, + 0xc2, 0x30, 0x04, 0x19, 0x00, 0x00, 0x63, 0x80, + 0xc1, 0xa0, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, + 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, + 0x04, 0x40, 0x00, 0x14, 0x0c, 0x40, 0x00, 0x14, + 0x00, 0x04, 0x61, 0xa8, 0x02, 0x04, 0x61, 0xa8, + 0x04, 0x60, 0x04, 0x24, 0x00, 0x00, 0x34, 0x49, + 0x00, 0x50, 0x00, 0x44, 0x44, 0x04, 0x04, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45, + 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45, + 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf, + 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80, + 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b, + 0x08, 0xf0, 0x04, 0x54, 0x0c, 0xd0, 0x00, 0xc4, + 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82, + 0x08, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99, + 0x08, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b, + 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b, + 0x00, 0xa2, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b, + 0x0c, 0x92, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b, + 0x04, 0x62, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b, + 0x0c, 0x52, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b, + 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b, + 0x00, 0xc2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b, + 0x00, 0xf2, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b, + 0x00, 0x91, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b, + 0x08, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b, + 0x08, 0xe2, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b, + 0x00, 0x92, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80, + 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x0c, 0x42, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8, + 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02, + 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82, + 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8, + 0x4d, 0xf2, 0x00, 0x39, 0x08, 0x50, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19, + 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10, + 0x4d, 0xf2, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44, + 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19, + 0x08, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a, + 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a, + 0x04, 0xd2, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80, + 0xc1, 0x30, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x00, 0xa1, 0x00, 0x84, + 0xb5, 0x00, 0x51, 0x8b, 0xcf, 0x00, 0x00, 0x39, + 0x00, 0x01, 0xb1, 0x80, 0x88, 0x00, 0x04, 0x19, + 0x8a, 0x00, 0x04, 0x19, 0xc8, 0x20, 0x04, 0x19, + 0xca, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19, + 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, + 0xb0, 0x00, 0x71, 0x8b, 0x8c, 0x00, 0x04, 0x19, + 0x8e, 0x00, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc4, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, + 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, + 0x03, 0x03, 0x04, 0x49, 0x04, 0x81, 0x00, 0x54, + 0x08, 0x50, 0x04, 0x64, 0x08, 0x50, 0x00, 0x44, + 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x08, 0x50, 0x00, 0x44, + 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x00, 0x02, 0xe2, 0x8b, 0x08, 0x41, 0x00, 0x84, + 0x65, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19, + 0x08, 0x61, 0x00, 0x44, 0x2d, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, + 0xc1, 0xa0, 0x04, 0x19, 0x03, 0x00, 0x04, 0x49, + 0x08, 0x50, 0x00, 0x44, 0x02, 0x20, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x02, 0x30, 0x61, 0x0a, + 0x04, 0x03, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, + 0x04, 0x71, 0x00, 0xc4, 0x00, 0x13, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0xca, 0x20, 0x04, 0x19, + 0x4a, 0x04, 0x04, 0x19, 0xff, 0x00, 0xe2, 0x8b, + 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x8e, 0x00, 0x04, 0x19, + 0x03, 0x30, 0x61, 0x0a, 0xc8, 0x20, 0x00, 0x39, + 0x48, 0x04, 0x00, 0x39, 0x0a, 0x30, 0x61, 0x0a, + 0x0c, 0xf9, 0x08, 0x44, 0xcd, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19, + 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19, + 0xca, 0x30, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44, + 0x42, 0x0a, 0x09, 0x0e, 0x00, 0x01, 0x33, 0x11, + 0x8c, 0x01, 0xa3, 0x80, 0x00, 0x01, 0x7a, 0x10, + 0x80, 0x05, 0xb1, 0x80, 0x05, 0xb0, 0xe0, 0x18, + 0x00, 0x93, 0x00, 0x84, 0x00, 0x79, 0x08, 0x44, + 0x00, 0x04, 0x79, 0x80, 0x00, 0x49, 0x00, 0xc4, + 0x0c, 0x1b, 0x08, 0x44, 0x88, 0x00, 0x04, 0x19, + 0x8a, 0x00, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44, + 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b, + 0x00, 0x43, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, + 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x43, 0x00, 0x84, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19, + 0x0c, 0xa8, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a, + 0x00, 0xd3, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, + 0x04, 0x63, 0x00, 0xc4, 0x08, 0xf3, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0x20, 0x00, 0x04, 0x19, + 0xff, 0x00, 0xe2, 0x8b, 0x0c, 0xf9, 0x08, 0x44, + 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19, + 0x8e, 0x00, 0x04, 0x19, 0x03, 0x30, 0x61, 0x0a, + 0xc8, 0x20, 0x00, 0x39, 0xca, 0x20, 0x00, 0x39, + 0x48, 0x04, 0x00, 0x39, 0x4a, 0x04, 0x00, 0x39, + 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19, + 0x0c, 0xd9, 0x08, 0x44, 0x42, 0x0a, 0x09, 0x0e, + 0x05, 0xb0, 0xe0, 0x18, 0x00, 0x18, 0x00, 0x84, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0x0c, 0x1b, 0x08, 0x44, + 0x80, 0x01, 0x00, 0x80, 0x0c, 0xd9, 0x08, 0x44, + 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b, + 0x00, 0x88, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, + 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x88, 0x00, 0x84, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19, + 0x00, 0x01, 0x00, 0x11, 0x00, 0x0f, 0xe2, 0x8b, + 0x00, 0x00, 0x41, 0xcb, 0x8c, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x48, 0xcb, 0x20, 0x00, 0x7a, 0x80, + 0x80, 0x01, 0x00, 0x80, 0x82, 0x0c, 0x09, 0x6e, + 0x03, 0x08, 0x09, 0x0e, 0x80, 0x40, 0x09, 0xcf, + 0x00, 0x01, 0x71, 0xc2, 0x00, 0x08, 0xc2, 0x1b, + 0x04, 0xb8, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, + 0x20, 0x01, 0xf0, 0x80, 0x00, 0x01, 0xc2, 0x1b, + 0x04, 0x48, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, + 0x20, 0x01, 0xf0, 0x80, 0x00, 0x02, 0xc2, 0x1b, + 0x04, 0x68, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, + 0x20, 0x01, 0xf0, 0x80, 0x20, 0x03, 0xa8, 0x80, + 0x00, 0x01, 0x00, 0x11, 0x00, 0x04, 0xc2, 0x8b, + 0x08, 0x78, 0x00, 0xc4, 0x00, 0x00, 0xe9, 0x80, + 0x05, 0xb0, 0xa8, 0x18, 0x00, 0x00, 0x4a, 0xcb, + 0x20, 0x00, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, + 0x40, 0x01, 0x00, 0x80, 0xc4, 0x00, 0x04, 0x19, + 0xb0, 0x00, 0xe2, 0x8b, 0x06, 0x20, 0xa8, 0x0a, + 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x08, 0x09, 0x2e, + 0x00, 0x01, 0xa8, 0x02, 0x0c, 0xf9, 0x08, 0x44, + 0xcd, 0x10, 0x04, 0x19, 0x0c, 0x2b, 0x08, 0x44, + 0x03, 0x08, 0x09, 0x0e, 0x9a, 0x25, 0xb1, 0x60, + 0xa2, 0x0e, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0f, + 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x61, 0xcb, 0x80, 0x01, 0x00, 0x80, + 0x03, 0x00, 0x09, 0x0f, 0x00, 0x01, 0x71, 0xc2, + 0x00, 0x08, 0xc2, 0x1b, 0x0c, 0x2a, 0x00, 0xc4, + 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, + 0x00, 0x01, 0xc2, 0x1b, 0x0c, 0x1a, 0x00, 0xc4, + 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, + 0x00, 0x02, 0xc2, 0x1b, 0x0c, 0x3a, 0x00, 0xc4, + 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, + 0x20, 0x03, 0xa8, 0x80, 0x00, 0x01, 0x00, 0x11, + 0x00, 0x04, 0xc2, 0x8b, 0x04, 0xaa, 0x00, 0xc4, + 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, + 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0xa8, 0x22, + 0xd0, 0x01, 0x00, 0x82, 0x40, 0x01, 0x00, 0x80, + 0xc7, 0x00, 0x04, 0x19, 0xb0, 0x00, 0xe2, 0x8b, + 0x06, 0x20, 0xa8, 0x0a, 0x2f, 0x10, 0x61, 0x0a, + 0xf1, 0x08, 0x09, 0x2e, 0x00, 0x01, 0xa8, 0x02, + 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x9f, 0x35, 0xb1, 0x60, + 0x03, 0x08, 0x09, 0x0e, 0x00, 0x01, 0x71, 0x82, + 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x61, 0xcb, + 0x80, 0x01, 0x00, 0x80, 0xe4, 0x20, 0x71, 0x8b, + 0x00, 0x01, 0x00, 0x45, 0x90, 0x40, 0x09, 0x8f, + 0x00, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x08, 0x19, 0x04, 0xd4, 0x93, 0x00, 0x01, 0x4f, + 0xe7, 0x00, 0x01, 0x6f, 0x0d, 0x30, 0x61, 0x0a, + 0x20, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82, + 0x02, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82, + 0xcd, 0x30, 0x00, 0x09, 0x02, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x02, 0xc0, 0x80, 0x00, 0x09, + 0x20, 0x00, 0x09, 0x49, 0x0f, 0x30, 0x61, 0x0a, + 0x0d, 0x30, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4, + 0x00, 0x80, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4, + 0x00, 0x04, 0xb1, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xc9, 0x20, 0x04, 0x39, 0x00, 0x39, 0x00, 0x44, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x00, 0x04, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x39, + 0x00, 0x39, 0x00, 0x44, 0x09, 0x20, 0x23, 0x0a, + 0x00, 0x00, 0x06, 0x19, 0xc9, 0x20, 0x04, 0x19, + 0x00, 0x00, 0x40, 0x45, 0x02, 0x00, 0x61, 0x0a, + 0x0c, 0xb9, 0x04, 0x14, 0x04, 0x00, 0x61, 0x0a, + 0x06, 0x00, 0x48, 0x0a, 0x00, 0xa9, 0x04, 0x54, + 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xa9, 0x00, 0x44, + 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xa9, 0x04, 0x54, + 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, + 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xb9, 0x04, 0x54, + 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xb9, 0x00, 0x44, + 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xb9, 0x04, 0x54, + 0xce, 0x00, 0x04, 0x19, 0x0c, 0x5a, 0x00, 0x44, + 0x82, 0x0d, 0x09, 0x2e, 0x80, 0x40, 0x09, 0xcf, + 0x00, 0xdf, 0x71, 0x8b, 0x80, 0x01, 0x00, 0x80, + 0x02, 0xc1, 0x00, 0x22, 0x03, 0xc1, 0x00, 0x22, + 0x00, 0x01, 0x65, 0x80, 0xd2, 0x05, 0x65, 0x82, + 0x40, 0x21, 0x00, 0x80, 0xd3, 0x03, 0x00, 0x82, + 0x40, 0x33, 0x00, 0x80, 0x0c, 0x5a, 0x00, 0x44, + 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, + 0x08, 0xd9, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f, + 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a, + 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02, + 0x02, 0x00, 0x00, 0x03, 0xcf, 0x30, 0x00, 0x09, + 0x20, 0x00, 0x09, 0x49, 0x00, 0x04, 0x63, 0x80, + 0x04, 0xd9, 0x00, 0x44, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x00, 0x46, 0x02, 0x30, 0x61, 0x0a, + 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19, + 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0xc8, 0x0a, + 0x0c, 0x0b, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, + 0x04, 0x2b, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, + 0x04, 0x2b, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, + 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0xa8, 0x18, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, + 0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x33, 0x80, + 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80, + 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0x03, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x46, 0x16, 0xce, 0x11, 0x00 +}; + +static unsigned char ima_adpcm_capture[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x00, 0x70, 0x00, 0x44, 0x08, 0xd0, 0x00, 0x44, + 0x00, 0xf0, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0c, 0xd4, 0x49, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x01, 0xb1, 0x80, 0x00, 0x00, 0x71, 0x8b, + 0xc2, 0x30, 0x04, 0x19, 0xc0, 0xa0, 0x04, 0x19, + 0xc2, 0xa0, 0x04, 0x19, 0x89, 0x00, 0x71, 0x8b, + 0xc8, 0x30, 0x04, 0x19, 0x71, 0x00, 0x71, 0x8b, + 0xcd, 0x00, 0x04, 0x19, 0xcf, 0x00, 0x04, 0x19, + 0x80, 0x00, 0x71, 0x8b, 0xcb, 0x20, 0x04, 0x19, + 0x20, 0x00, 0x71, 0x8b, 0xc4, 0x20, 0x04, 0x19, + 0x47, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19, + 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09, + 0xcf, 0x30, 0x00, 0x09, 0x0c, 0x40, 0x00, 0x14, + 0x00, 0x60, 0x00, 0x14, 0x00, 0x04, 0x61, 0xa8, + 0x02, 0x04, 0x61, 0xa8, 0x0c, 0x60, 0x04, 0x24, + 0x00, 0x00, 0x34, 0x49, 0x08, 0x50, 0x00, 0x44, + 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x08, 0x30, 0x61, 0x0a, 0x05, 0xb0, 0xe8, 0x18, + 0x0c, 0xc0, 0x04, 0x54, 0xc8, 0x30, 0x04, 0x19, + 0x09, 0x04, 0x00, 0xa8, 0x0b, 0x04, 0x00, 0xa8, + 0x00, 0x00, 0x40, 0x45, 0x09, 0x04, 0x61, 0xa8, + 0xc1, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8, + 0xca, 0x00, 0x04, 0x19, 0x0d, 0x00, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x0f, 0x00, 0x61, 0x0a, + 0x00, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, + 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf, + 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80, + 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b, + 0x0c, 0x12, 0x04, 0x54, 0x08, 0x12, 0x00, 0xc4, + 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82, + 0x04, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99, + 0x04, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b, + 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b, + 0x04, 0x42, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b, + 0x08, 0x52, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b, + 0x00, 0xe2, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b, + 0x08, 0xd2, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b, + 0x04, 0xf2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b, + 0x04, 0xf2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b, + 0x04, 0x11, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b, + 0x0c, 0x61, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b, + 0x04, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b, + 0x0c, 0x01, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b, + 0x04, 0x62, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80, + 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x08, 0xc2, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8, + 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02, + 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82, + 0x04, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8, + 0x4d, 0xf2, 0x00, 0x39, 0x04, 0x50, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19, + 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10, + 0x4d, 0xf2, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44, + 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19, + 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a, + 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a, + 0x00, 0x11, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80, + 0xc1, 0x30, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x0c, 0x41, 0x00, 0x84, + 0x89, 0x00, 0x71, 0x8b, 0xc8, 0x30, 0x04, 0x19, + 0x97, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, + 0x00, 0x01, 0xb1, 0x80, 0x80, 0x00, 0x04, 0x19, + 0x82, 0x00, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, + 0xc3, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19, + 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, + 0xb0, 0x00, 0x71, 0x8b, 0x84, 0x00, 0x04, 0x19, + 0x86, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b, + 0xcb, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, + 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, + 0x03, 0x02, 0x04, 0x49, 0x08, 0x41, 0x00, 0x14, + 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x63, 0x80, + 0x00, 0x00, 0x06, 0x19, 0x03, 0x00, 0x04, 0x49, + 0x04, 0x50, 0x00, 0x44, 0x20, 0x01, 0x63, 0x80, + 0x00, 0x00, 0x06, 0x19, 0x00, 0x20, 0xe2, 0x8b, + 0x00, 0xc1, 0x00, 0x84, 0x47, 0x00, 0x51, 0x8b, + 0xc0, 0x20, 0x00, 0x39, 0x00, 0x00, 0x63, 0x80, + 0xc1, 0xa0, 0x04, 0x19, 0x00, 0xe1, 0x00, 0x44, + 0xbd, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x00, 0x00, 0xb1, 0x80, 0xc1, 0xa0, 0x04, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x04, 0x50, 0x00, 0x44, + 0x00, 0x20, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45, + 0x02, 0x30, 0x61, 0x0a, 0x0c, 0x83, 0x00, 0xc4, + 0x0c, 0x78, 0x08, 0x44, 0x04, 0x5a, 0x08, 0x44, + 0xb2, 0x00, 0x09, 0x4f, 0x10, 0x42, 0x09, 0x8e, + 0x05, 0xb0, 0xe0, 0x18, 0x04, 0x23, 0x00, 0x84, + 0x0c, 0x01, 0x00, 0x11, 0x08, 0x05, 0x61, 0x10, + 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44, + 0xb2, 0x00, 0x09, 0x4f, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x00, 0x00, 0x82, 0x0c, 0x01, 0x33, 0x10, + 0x28, 0x01, 0xa3, 0x10, 0x00, 0x01, 0x7a, 0x80, + 0x8c, 0x01, 0x00, 0x80, 0x02, 0x30, 0x61, 0x0a, + 0x20, 0x00, 0x04, 0x19, 0x0c, 0x83, 0x00, 0xc4, + 0x05, 0xb0, 0xc8, 0x18, 0x08, 0x43, 0x00, 0xc4, + 0x01, 0x30, 0xc8, 0x0a, 0x0c, 0x38, 0x00, 0xc4, + 0x08, 0x88, 0x00, 0x44, 0x0c, 0x78, 0x08, 0x44, + 0x04, 0x5a, 0x08, 0x44, 0x00, 0x00, 0xa3, 0x18, + 0x80, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8, + 0xc3, 0x20, 0x00, 0x39, 0xc3, 0x30, 0x04, 0x19, + 0x0f, 0x10, 0x61, 0x0a, 0xca, 0x30, 0x04, 0x19, + 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39, + 0xd1, 0x00, 0x09, 0x4f, 0x00, 0x04, 0x61, 0x02, + 0x08, 0x63, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a, + 0x20, 0x00, 0x00, 0x39, 0xa3, 0x00, 0x09, 0x4f, + 0x00, 0x04, 0x61, 0x02, 0x00, 0x48, 0x08, 0x44, + 0x08, 0x88, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a, + 0x00, 0x08, 0x00, 0xc4, 0x0c, 0x78, 0x08, 0x44, + 0x04, 0x5a, 0x08, 0x44, 0xb2, 0x00, 0x09, 0x0f, + 0x10, 0x40, 0x09, 0x8e, 0x00, 0x00, 0x68, 0x5b, + 0x20, 0x04, 0xb1, 0x80, 0x02, 0x00, 0x61, 0x5b, + 0x88, 0x03, 0x7a, 0x80, 0xac, 0x01, 0x00, 0x80, + 0x05, 0xb0, 0xe0, 0x18, 0x00, 0xd3, 0x00, 0x84, + 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44, + 0xb2, 0x00, 0x09, 0x0f, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x00, 0x00, 0x82, 0x02, 0x30, 0x61, 0x0a, + 0x00, 0x08, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, + 0x0c, 0x18, 0x00, 0xc4, 0x01, 0x30, 0xc8, 0x0a, + 0x0c, 0x38, 0x00, 0xc4, 0x08, 0x88, 0x00, 0x44, + 0x0c, 0x78, 0x08, 0x44, 0x00, 0x00, 0x61, 0x18, + 0x20, 0x05, 0xb1, 0x80, 0x00, 0x00, 0x68, 0xcb, + 0x80, 0x00, 0x04, 0x19, 0x0d, 0x10, 0x61, 0x0a, + 0xc3, 0x30, 0x04, 0x19, 0x0b, 0x04, 0x41, 0xa8, + 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39, + 0x08, 0x38, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a, + 0x20, 0x04, 0xb1, 0x80, 0x00, 0x48, 0x08, 0x44, + 0x08, 0x88, 0x00, 0x44, 0x00, 0x00, 0xb1, 0x80, + 0xc2, 0x30, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0xd4, + 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, + 0x0c, 0xb8, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f, + 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a, + 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02, + 0x41, 0x04, 0x04, 0x19, 0x02, 0x04, 0x61, 0x02, + 0x43, 0x04, 0x04, 0x39, 0xcf, 0x30, 0x00, 0x09, + 0x20, 0x00, 0x09, 0x49, 0x00, 0x59, 0x00, 0x44, + 0x93, 0x00, 0x01, 0x4f, 0xe7, 0x00, 0x01, 0x6f, + 0x0d, 0x30, 0x61, 0x0a, 0x20, 0x00, 0x61, 0x88, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x03, 0x00, 0x82, + 0xcd, 0x30, 0x00, 0x09, 0x20, 0x00, 0x09, 0x49, + 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, + 0x0c, 0x58, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, + 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19, + 0x00, 0x00, 0x00, 0x46, 0x90, 0x40, 0x09, 0x8f, + 0x12, 0x04, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0e, + 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x61, 0xcb, 0x80, 0x04, 0xb1, 0x80, + 0x00, 0x01, 0xe0, 0x60, 0x0c, 0xd8, 0x04, 0x14, + 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b, + 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0x0a, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x0c, 0x2a, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0x3a, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82, + 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11, + 0x40, 0x00, 0xc2, 0x8b, 0x00, 0xaa, 0x00, 0xc4, + 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, + 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, + 0xf0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a, + 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x00, 0x09, 0x2e, + 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a, + 0x0c, 0xba, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, + 0x04, 0x4a, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, + 0x04, 0x4a, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, + 0x03, 0x00, 0x09, 0x0e, 0x9a, 0x01, 0x00, 0x60, + 0x32, 0x00, 0x09, 0x2e, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x61, 0xcb, 0x80, 0x24, 0xb1, 0xc0, + 0x00, 0x31, 0xe0, 0x60, 0x0c, 0xca, 0x04, 0x14, + 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b, + 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0xda, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x0c, 0xfa, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0x29, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82, + 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11, + 0x40, 0x00, 0xc2, 0x8b, 0x00, 0x39, 0x00, 0xc4, + 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, + 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, + 0xb0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a, + 0x2f, 0x10, 0x61, 0x0a, 0xf1, 0x00, 0x09, 0x2e, + 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a, + 0x0c, 0xa9, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, + 0x04, 0x99, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, + 0x04, 0x99, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, + 0x9f, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0x07, 0x33, 0x80, + 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80, + 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x46, + 0x02, 0x00, 0x61, 0x0a, 0x04, 0x1b, 0x04, 0x14, + 0x01, 0x00, 0x61, 0x0a, 0x03, 0x00, 0x48, 0x0a, + 0x0c, 0x79, 0x04, 0x54, 0xc3, 0x00, 0x04, 0x19, + 0x04, 0xc9, 0x00, 0x44, 0x08, 0x00, 0xc8, 0x0a, + 0x04, 0xc9, 0x04, 0x54, 0xc8, 0x00, 0x04, 0x19, + 0x0a, 0x00, 0x61, 0x0a, 0x09, 0x00, 0x48, 0x0a, + 0x0c, 0xe9, 0x04, 0x54, 0xc9, 0x00, 0x04, 0x19, + 0x04, 0xd9, 0x00, 0x44, 0x0b, 0x00, 0xc8, 0x0a, + 0x04, 0xd9, 0x04, 0x54, 0xcb, 0x00, 0x04, 0x19, + 0x04, 0x00, 0x61, 0x0a, 0x06, 0x00, 0x48, 0x0a, + 0x0c, 0xf9, 0x04, 0x54, 0xc6, 0x00, 0x04, 0x19, + 0x04, 0x0b, 0x00, 0x44, 0x05, 0x00, 0xc8, 0x0a, + 0x04, 0x0b, 0x04, 0x54, 0xc5, 0x00, 0x04, 0x19, + 0x07, 0x00, 0x61, 0x0a, 0x0c, 0x00, 0x48, 0x0a, + 0x0c, 0x2b, 0x04, 0x54, 0xcc, 0x00, 0x04, 0x19, + 0x04, 0x1b, 0x00, 0x44, 0x0e, 0x00, 0xc8, 0x0a, + 0x04, 0x1b, 0x04, 0x54, 0xce, 0x00, 0x04, 0x19, + 0x00, 0x00, 0x40, 0x45, 0x92, 0x20, 0x71, 0x8b, + 0xa6, 0xc5, 0x11, 0x00 +}; diff -Nru a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/sb16_main.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,899 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of 16-bit SoundBlaster cards and clones + * Note: This is very ugly hardware which uses one 8-bit DMA channel and + * second 16-bit DMA channel. Unfortunately 8-bit DMA channel can't + * transfer 16-bit samples and 16-bit DMA channels can't transfer + * 8-bit samples. This make full duplex more complicated than + * can be... People, don't buy these soundcards for full 16-bit + * duplex!!! + * Note: 16-bit wide is assigned to first direction which made request. + * With full duplex - playback is preferred with abstract layer. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones"); +MODULE_LICENSE("GPL"); + +#define chip_t sb_t + +#ifdef CONFIG_SND_SB16_CSP +static void snd_sb16_csp_playback_prepare(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) && + ((1 << runtime->format) == csp->acc_format)) { + /* Supported runtime PCM format for playback */ + if (csp->ops.csp_use(csp) == 0) { + /* If CSP was successfully acquired */ + goto __start_CSP; + } + } else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) { + /* QSound decoder is loaded and enabled */ + if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) { + /* Only for simple PCM formats */ + if (csp->ops.csp_use(csp) == 0) { + /* If CSP was successfully acquired */ + goto __start_CSP; + } + } + } + } else if (csp->ops.csp_use(csp) == 0) { + /* Acquire CSP and try to autoload hardware codec */ + if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_WRITE)) { + /* Unsupported format, release CSP */ + csp->ops.csp_unuse(csp); + } else { + __start_CSP: + /* Try to start CSP */ + if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_PLAYBACK_16) ? + SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, + (runtime->channels > 1) ? + SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { + /* Failed, release CSP */ + csp->ops.csp_unuse(csp); + } else { + /* Success, CSP acquired and running */ + chip->open = SNDRV_SB_CSP_MODE_DSP_WRITE; + } + } + } + } +} + +static void snd_sb16_csp_capture_prepare(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) && + ((1 << runtime->format) == csp->acc_format)) { + /* Supported runtime PCM format for capture */ + if (csp->ops.csp_use(csp) == 0) { + /* If CSP was successfully acquired */ + goto __start_CSP; + } + } + } else if (csp->ops.csp_use(csp) == 0) { + /* Acquire CSP and try to autoload hardware codec */ + if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_READ)) { + /* Unsupported format, release CSP */ + csp->ops.csp_unuse(csp); + } else { + __start_CSP: + /* Try to start CSP */ + if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_CAPTURE_16) ? + SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, + (runtime->channels > 1) ? + SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { + /* Failed, release CSP */ + csp->ops.csp_unuse(csp); + } else { + /* Success, CSP acquired and running */ + chip->open = SNDRV_SB_CSP_MODE_DSP_READ; + } + } + } + } +} + +static void snd_sb16_csp_update(sb_t *chip) +{ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->qpos_changed) { + spin_lock(&chip->reg_lock); + csp->ops.csp_qsound_transfer (csp); + spin_unlock(&chip->reg_lock); + } + } +} + +static void snd_sb16_csp_playback_open(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + /* CSP decoders (QSound excluded) support only 16bit transfers */ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if (csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) { + runtime->hw.formats |= csp->acc_format; + } + } else { + /* autoloaded codecs */ + runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_IMA_ADPCM; + } + } +} + +static void snd_sb16_csp_playback_close(sb_t *chip) +{ + if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->ops.csp_stop(csp) == 0) { + csp->ops.csp_unuse(csp); + chip->open = 0; + } + } +} + +static void snd_sb16_csp_capture_open(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + /* CSP coders support only 16bit transfers */ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if (csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) { + runtime->hw.formats |= csp->acc_format; + } + } else { + /* autoloaded codecs */ + runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_IMA_ADPCM; + } + } +} + +static void snd_sb16_csp_capture_close(sb_t *chip) +{ + if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->ops.csp_stop(csp) == 0) { + csp->ops.csp_unuse(csp); + chip->open = 0; + } + } +} +#else +#define snd_sb16_csp_playback_prepare(chip, runtime) /*nop*/ +#define snd_sb16_csp_capture_prepare(chip, runtime) /*nop*/ +#define snd_sb16_csp_update(chip) /*nop*/ +#define snd_sb16_csp_playback_open(chip, runtime) /*nop*/ +#define snd_sb16_csp_playback_close(chip) /*nop*/ +#define snd_sb16_csp_capture_open(chip, runtime) /*nop*/ +#define snd_sb16_csp_capture_close(chip) /*nop*/ +#endif + + +static void snd_sb16_setup_rate(sb_t *chip, + unsigned short rate, + int channel) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16)) + snd_sb_ack_16bit(chip); + else + snd_sb_ack_8bit(chip); + if (!(chip->mode & SB_RATE_LOCK)) { + chip->locked_rate = rate; + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_IN); + snd_sbdsp_command(chip, rate >> 8); + snd_sbdsp_command(chip, rate & 0xff); + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT); + snd_sbdsp_command(chip, rate >> 8); + snd_sbdsp_command(chip, rate & 0xff); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static int snd_sb16_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_sb16_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_sb16_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char format; + unsigned int size, count, dma; + + snd_sb16_csp_playback_prepare(chip, runtime); + if (snd_pcm_format_unsigned(runtime->format) > 0) { + format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; + } else { + format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; + } + + snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_PLAYBACK); + size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); + dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; + snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + + count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->mode & SB_MODE_PLAYBACK_16) { + count >>= 1; + count--; + snd_sbdsp_command(chip, SB_DSP4_OUT16_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); + } else { + count--; + snd_sbdsp_command(chip, SB_DSP4_OUT8_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_sb16_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + chip->mode |= SB_RATE_LOCK_PLAYBACK; + snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); + /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ + if (chip->mode & SB_RATE_LOCK_CAPTURE) + snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + chip->mode &= ~SB_RATE_LOCK_PLAYBACK; + break; + default: + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_sb16_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char format; + unsigned int size, count, dma; + + snd_sb16_csp_capture_prepare(chip, runtime); + if (snd_pcm_format_unsigned(runtime->format) > 0) { + format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; + } else { + format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; + } + snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_CAPTURE); + size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); + dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; + snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + + count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->mode & SB_MODE_CAPTURE_16) { + count >>= 1; + count--; + snd_sbdsp_command(chip, SB_DSP4_IN16_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); + } else { + count--; + snd_sbdsp_command(chip, SB_DSP4_IN8_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_sb16_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + chip->mode |= SB_RATE_LOCK_CAPTURE; + snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); + /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ + if (chip->mode & SB_RATE_LOCK_PLAYBACK) + snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + chip->mode &= ~SB_RATE_LOCK_CAPTURE; + break; + default: + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + unsigned char status; + int ok; + + spin_lock(&chip->mixer_lock); + status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); + spin_unlock(&chip->mixer_lock); + if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + if (status & SB_IRQTYPE_8BIT) { + ok = 0; + if (chip->mode & SB_MODE_PLAYBACK_8) { + snd_pcm_period_elapsed(chip->playback_substream); + snd_sb16_csp_update(chip); + ok++; + } + if (chip->mode & SB_MODE_CAPTURE_8) { + snd_pcm_period_elapsed(chip->capture_substream); + ok++; + } + spin_lock(&chip->reg_lock); + if (!ok) + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + snd_sb_ack_8bit(chip); + spin_unlock(&chip->reg_lock); + } + if (status & SB_IRQTYPE_16BIT) { + ok = 0; + if (chip->mode & SB_MODE_PLAYBACK_16) { + snd_pcm_period_elapsed(chip->playback_substream); + snd_sb16_csp_update(chip); + ok++; + } + if (chip->mode & SB_MODE_CAPTURE_16) { + snd_pcm_period_elapsed(chip->capture_substream); + ok++; + } + spin_lock(&chip->reg_lock); + if (!ok) + snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); + snd_sb_ack_16bit(chip); + spin_unlock(&chip->reg_lock); + } +} + +/* + + */ + +static snd_pcm_uframes_t snd_sb16_playback_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int dma; + size_t ptr; + + dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; + ptr = chip->p_dma_size - snd_dma_residue(dma); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_sb16_capture_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int dma; + size_t ptr; + + dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; + ptr = chip->c_dma_size - snd_dma_residue(dma); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static snd_pcm_hardware_t snd_sb16_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: 0, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + rate_min: 4000, + rate_max: 44100, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_sb16_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: 0, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + rate_min: 4000, + rate_max: 44100, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * open/close + */ + +int snd_sb16_playback_open(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->mode & SB_MODE_PLAYBACK) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + runtime->hw = snd_sb16_playback; + + /* skip if 16 bit DMA was reserved for capture */ + if (chip->force_mode16 & SB_MODE_CAPTURE_16) + goto __skip_16bit; + + if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_CAPTURE_16)) { + chip->mode |= SB_MODE_PLAYBACK_16; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + /* Vibra16X hack */ + if (chip->dma16 <= 3) { + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + } else { + snd_sb16_csp_playback_open(chip, runtime); + } + goto __open_ok; + } + + __skip_16bit: + if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_CAPTURE_8)) { + chip->mode |= SB_MODE_PLAYBACK_8; + /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ + if (chip->dma16 < 0) { + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + chip->mode |= SB_MODE_PLAYBACK_16; + } else { + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; + } + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + goto __open_ok; + } + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + + __open_ok: + if (chip->hardware == SB_HW_ALS100) + runtime->hw.rate_max = 48000; + if (chip->mode & SB_RATE_LOCK) + runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; + chip->playback_substream = substream; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +int snd_sb16_playback_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + + snd_sb16_csp_playback_close(chip); + spin_lock_irqsave(&chip->open_lock, flags); + chip->playback_substream = NULL; + chip->mode &= ~SB_MODE_PLAYBACK; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +int snd_sb16_capture_open(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->mode & SB_MODE_CAPTURE) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + runtime->hw = snd_sb16_capture; + + /* skip if 16 bit DMA was reserved for playback */ + if (chip->force_mode16 & SB_MODE_PLAYBACK_16) + goto __skip_16bit; + + if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_16)) { + chip->mode |= SB_MODE_CAPTURE_16; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + /* Vibra16X hack */ + if (chip->dma16 <= 3) { + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + } else { + snd_sb16_csp_capture_open(chip, runtime); + } + goto __open_ok; + } + + __skip_16bit: + if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_8)) { + chip->mode |= SB_MODE_CAPTURE_8; + /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ + if (chip->dma16 < 0) { + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + chip->mode |= SB_MODE_CAPTURE_16; + } else { + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; + } + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + goto __open_ok; + } + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + + __open_ok: + if (chip->hardware == SB_HW_ALS100) + runtime->hw.rate_max = 48000; + if (chip->mode & SB_RATE_LOCK) + runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; + chip->capture_substream = substream; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +int snd_sb16_capture_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + + snd_sb16_csp_capture_close(chip); + spin_lock_irqsave(&chip->open_lock, flags); + chip->capture_substream = NULL; + chip->mode &= ~SB_MODE_CAPTURE; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +/* + * DMA control interface + */ + +static int snd_sb16_set_dma_mode(sb_t *chip, int what) +{ + if (chip->dma8 < 0 || chip->dma16 < 0) { + snd_assert(what == 0, return -EINVAL); + return 0; + } + if (what == 0) { + chip->force_mode16 = 0; + } else if (what == 1) { + chip->force_mode16 = SB_MODE_PLAYBACK_16; + } else if (what == 2) { + chip->force_mode16 = SB_MODE_CAPTURE_16; + } else { + return -EINVAL; + } + return 0; +} + +static int snd_sb16_get_dma_mode(sb_t *chip) +{ + if (chip->dma8 < 0 || chip->dma16 < 0) + return 0; + switch (chip->force_mode16) { + case SB_MODE_PLAYBACK_16: + return 1; + case SB_MODE_CAPTURE_16: + return 2; + default: + return 0; + } +} + +static int snd_sb16_dma_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { + "Auto", "Playback", "Capture" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_sb16_dma_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_sb16_dma_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char nval, oval; + int change; + + if ((nval = ucontrol->value.enumerated.item[0]) > 2) + return -EINVAL; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_sb16_get_dma_mode(chip); + change = nval != oval; + snd_sb16_set_dma_mode(chip, nval); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +snd_kcontrol_new_t snd_sb16_dma_control = { + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "16-bit DMA Allocation", + info: snd_sb16_dma_control_info, + get: snd_sb16_dma_control_get, + put: snd_sb16_dma_control_put +}; + +/* + * Initialization part + */ + +int snd_sb16dsp_configure(sb_t * chip) +{ + unsigned long flags; + unsigned char irqreg = 0, dmareg = 0, mpureg; + unsigned char realirq, realdma, realmpureg; + /* note: mpu register should be present only on SB16 Vibra soundcards */ + + // printk("codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16); + spin_lock_irqsave(&chip->mixer_lock, flags); + mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06; + spin_unlock_irqrestore(&chip->mixer_lock, flags); + switch (chip->irq) { + case 2: + case 9: + irqreg |= SB_IRQSETUP_IRQ9; + break; + case 5: + irqreg |= SB_IRQSETUP_IRQ5; + break; + case 7: + irqreg |= SB_IRQSETUP_IRQ7; + break; + case 10: + irqreg |= SB_IRQSETUP_IRQ10; + break; + default: + return -EINVAL; + } + if (chip->dma8 >= 0) { + switch (chip->dma8) { + case 0: + dmareg |= SB_DMASETUP_DMA0; + break; + case 1: + dmareg |= SB_DMASETUP_DMA1; + break; + case 3: + dmareg |= SB_DMASETUP_DMA3; + break; + default: + return -EINVAL; + } + } + if (chip->dma16 >= 0) { + switch (chip->dma16) { + case 5: + dmareg |= SB_DMASETUP_DMA5; + break; + case 6: + dmareg |= SB_DMASETUP_DMA6; + break; + case 7: + dmareg |= SB_DMASETUP_DMA7; + break; + default: + return -EINVAL; + } + } + switch (chip->mpu_port) { + case 0x300: + mpureg |= 0x04; + break; + case 0x330: + mpureg |= 0x00; + break; + default: + mpureg |= 0x02; /* disable MPU */ + } + spin_lock_irqsave(&chip->mixer_lock, flags); + + snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg); + realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP); + + snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg); + realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP); + + snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg); + realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP); + + spin_unlock_irqrestore(&chip->mixer_lock, flags); + if ((~realirq) & irqreg || (~realdma) & dmareg) { + snd_printk("SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port); + snd_printk("SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg); + snd_printk("SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg); + return -ENODEV; + } + return 0; +} + +static snd_pcm_ops_t snd_sb16_playback_ops = { + open: snd_sb16_playback_open, + close: snd_sb16_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sb16_hw_params, + hw_free: snd_sb16_hw_free, + prepare: snd_sb16_playback_prepare, + trigger: snd_sb16_playback_trigger, + pointer: snd_sb16_playback_pointer, +}; + +static snd_pcm_ops_t snd_sb16_capture_ops = { + open: snd_sb16_capture_open, + close: snd_sb16_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sb16_hw_params, + hw_free: snd_sb16_hw_free, + prepare: snd_sb16_capture_prepare, + trigger: snd_sb16_capture_trigger, + pointer: snd_sb16_capture_pointer, +}; + +static void snd_sb16dsp_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_sb16dsp_pcm(sb_t * chip, int device, snd_pcm_t ** rpcm) +{ + snd_card_t *card = chip->card; + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0) + return err; + sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + pcm->private_data = chip; + pcm->private_free = snd_sb16dsp_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops); + + snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip)); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +EXPORT_SYMBOL(snd_sb16dsp_pcm); +EXPORT_SYMBOL(snd_sb16dsp_configure); +EXPORT_SYMBOL(snd_sb16dsp_interrupt); + +/* + * INIT part + */ + +static int __init alsa_sb16_init(void) +{ + return 0; +} + +static void __exit alsa_sb16_exit(void) +{ +} + +module_init(alsa_sb16_init) +module_exit(alsa_sb16_exit) diff -Nru a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/sb8.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,255 @@ +/* + * Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_GET_ID +#include + +#define chip_t sb_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Sound Blaster soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Sound Blaster soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Sound Blaster soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for SB8 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for SB8 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for SB8 driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); + +struct snd_sb8 { + struct resource *fm_res; /* used to block FM i/o region for legacy cards */ +}; + +static snd_card_t *snd_sb8_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +static void snd_sb8_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + + if (chip->open & SB_OPEN_PCM) { + snd_sb8dsp_interrupt(chip); + } else { + snd_sb8dsp_midi_interrupt(chip); + } +} + +static void snd_sb8_free(snd_card_t *card) +{ + struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data; + + if (acard == NULL) + return; + if (acard->fm_res) { + release_resource(acard->fm_res); + kfree_nocheck(acard->fm_res); + } +} + +static int __init snd_sb8_probe(int dev) +{ + sb_t *chip; + snd_card_t *card; + struct snd_sb8 *acard; + opl3_t *opl3; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_sb8)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_sb8 *)card->private_data; + card->private_free = snd_sb8_free; + + /* block the 0x388 port to avoid PnP conflicts */ + acard->fm_res = request_region(0x388, 4, "SoundBlaster FM"); + + if ((err = snd_sbdsp_create(card, snd_port[dev], snd_irq[dev], + snd_sb8_interrupt, + snd_dma8[dev], + -1, + SB_HW_AUTO, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if (chip->hardware >= SB_HW_16) { + snd_card_free(card); + if (chip->hardware == SB_HW_ALS100) + snd_printdd("ALS100 chip detected at 0x%lx, try snd-card-als100 module\n", + snd_port[dev]); + else + snd_printdd("SB 16 chip detected at 0x%lx, try snd-card-sb16 module\n", + snd_port[dev]); + return -ENODEV; + } + + if ((err = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return err; + } + if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) { + if ((err = snd_opl3_create(card, chip->port + 8, 0, + OPL3_HW_AUTO, 1, + &opl3)) < 0) { + snd_printk("no OPL device at 0x%lx\n", chip->port + 8); + } + } else { + if ((err = snd_opl3_create(card, chip->port, chip->port + 2, + OPL3_HW_AUTO, 1, + &opl3)) < 0) { + snd_printk("no OPL device at 0x%lx-0x%lx\n", + chip->port, chip->port + 2); + } + } + if (err >= 0) { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if ((err = snd_sb8dsp_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8"); + strcpy(card->shortname, chip->name); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + chip->name, + chip->port, + snd_irq[dev], snd_dma8[dev]); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_sb8_cards[dev] = card; + return 0; +} + +static int __init snd_card_sb8_legacy_auto_probe(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; + snd_port[dev] = port; + res = snd_sb8_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_sb8_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_sb8_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_card_sb8_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + snd_printk("Sound Blaster soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_sb8_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_sb8_cards[idx]); +} + +module_init(alsa_card_sb8_init) +module_exit(alsa_card_sb8_exit) + +#ifndef MODULE + +/* format is: snd-sb8=snd_enable,snd_index,snd_id, + snd_port,snd_irq,snd_dma8 */ + +static int __init alsa_card_sb8_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sb8=", alsa_card_sb8_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/sb8_main.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,564 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Uros Bizjak + * + * Routines for control of 8-bit SoundBlaster cards and clones + * Please note: I don't have access to old SB8 soundcards. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -- + * + * Thu Apr 29 20:36:17 BST 1999 George David Morrison + * DSP can't respond to commands whilst in "high speed" mode. Caused + * glitching during playback. Fixed. + * + * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak + * Cleaned up and rewrote lowlevel routines. + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela , Uros Bizjak "); +MODULE_DESCRIPTION("Routines for control of 8-bit SoundBlaster cards and clones"); +MODULE_LICENSE("GPL"); + +#define chip_t sb_t + +#define SB8_CLOCK 1000000 +#define SB8_DEN(v) ((SB8_CLOCK + (v) / 2) / (v)) +#define SB8_RATE(v) (SB8_CLOCK / SB8_DEN(v)) + +static ratnum_t clock = { + num: SB8_CLOCK, + den_min: 1, + den_max: 256, + den_step: 1, +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clock = { + nrats: 1, + rats: &clock, +}; + +static ratnum_t stereo_clocks[] = { + { + num: SB8_CLOCK, + den_min: SB8_DEN(22050), + den_max: SB8_DEN(22050), + den_step: 1, + }, + { + num: SB8_CLOCK, + den_min: SB8_DEN(11025), + den_max: SB8_DEN(11025), + den_step: 1, + } +}; + +static int snd_sb8_hw_constraint_rate_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + if (c->min > 1) { + unsigned int num = 0, den = 0; + int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE), + 2, stereo_clocks, &num, &den); + if (err >= 0 && den) { + params->rate_num = num; + params->rate_den = den; + } + return err; + } + return 0; +} + +static int snd_sb8_hw_constraint_channels_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) { + snd_interval_t t = { min: 1, max: 1 }; + return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t); + } + return 0; +} + +static int snd_sb8_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mixreg, rate, size, count; + + rate = runtime->rate; + switch (chip->hardware) { + case SB_HW_PRO: + if (runtime->channels > 1) { + snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); + chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; + break; + } + /* fallthru */ + case SB_HW_201: + if (rate > 23000) { + chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; + break; + } + /* fallthru */ + case SB_HW_20: + chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; + break; + case SB_HW_10: + chip->playback_format = SB_DSP_OUTPUT; + break; + default: + return -EINVAL; + } + size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); + count = chip->p_period_size = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); + if (runtime->channels > 1) { + /* set playback stereo mode */ + spin_lock(&chip->mixer_lock); + mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW); + snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02); + spin_unlock(&chip->mixer_lock); + + /* Soundblaster hardware programming reference guide, 3-23 */ + snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT); + runtime->dma_area[0] = 0x80; + snd_dma_program(chip->dma8, runtime->dma_addr, 1, DMA_MODE_WRITE); + /* force interrupt */ + chip->mode = SB_MODE_HALT; + snd_sbdsp_command(chip, SB_DSP_OUTPUT); + snd_sbdsp_command(chip, 0); + snd_sbdsp_command(chip, 0); + } + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); + if (runtime->channels > 1) { + snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); + spin_lock(&chip->mixer_lock); + /* save output filter status and turn it off */ + mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT); + snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20); + spin_unlock(&chip->mixer_lock); + /* just use force_mode16 for temporary storate... */ + chip->force_mode16 = mixreg; + } else { + snd_sbdsp_command(chip, 256 - runtime->rate_den); + } + if (chip->playback_format != SB_DSP_OUTPUT) { + count--; + snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_dma_program(chip->dma8, runtime->dma_addr, + size, DMA_MODE_WRITE | DMA_AUTOINIT); + return 0; +} + +static int snd_sb8_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int count; + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_sbdsp_command(chip, chip->playback_format); + if (chip->playback_format == SB_DSP_OUTPUT) { + count = chip->p_period_size - 1; + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) { + snd_pcm_runtime_t *runtime = substream->runtime; + snd_sbdsp_reset(chip); + if (runtime->channels > 1) { + spin_lock(&chip->mixer_lock); + /* restore output filter and set hardware to mono mode */ + snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02); + spin_unlock(&chip->mixer_lock); + } + } else { + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_PLAYBACK_8 : SB_MODE_HALT; + return 0; +} + +static int snd_sb8_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_sb8_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_sb8_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mixreg, rate, size, count; + + rate = runtime->rate; + switch (chip->hardware) { + case SB_HW_PRO: + if (runtime->channels > 1) { + snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); + chip->capture_format = SB_DSP_HI_INPUT_AUTO; + break; + } + chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO; + break; + case SB_HW_201: + if (rate > 13000) { + chip->capture_format = SB_DSP_HI_INPUT_AUTO; + break; + } + /* fallthru */ + case SB_HW_20: + chip->capture_format = SB_DSP_LO_INPUT_AUTO; + break; + case SB_HW_10: + chip->capture_format = SB_DSP_INPUT; + break; + default: + return -EINVAL; + } + size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); + count = chip->c_period_size = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); + if (runtime->channels > 1) + snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT); + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); + if (runtime->channels > 1) { + snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); + spin_lock(&chip->mixer_lock); + /* save input filter status and turn it off */ + mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT); + snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20); + spin_unlock(&chip->mixer_lock); + /* just use force_mode16 for temporary storate... */ + chip->force_mode16 = mixreg; + } else { + snd_sbdsp_command(chip, 256 - runtime->rate_den); + } + if (chip->capture_format != SB_DSP_OUTPUT) { + count--; + snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_dma_program(chip->dma8, runtime->dma_addr, + size, DMA_MODE_READ | DMA_AUTOINIT); + return 0; +} + +static int snd_sb8_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int count; + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_sbdsp_command(chip, chip->capture_format); + if (chip->capture_format == SB_DSP_INPUT) { + count = chip->c_period_size - 1; + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) { + snd_pcm_runtime_t *runtime = substream->runtime; + snd_sbdsp_reset(chip); + if (runtime->channels > 1) { + /* restore input filter status */ + spin_lock(&chip->mixer_lock); + snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16); + spin_unlock(&chip->mixer_lock); + /* set hardware to mono mode */ + snd_sbdsp_command(chip, SB_DSP_MONO_8BIT); + } + } else { + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_CAPTURE_8 : SB_MODE_HALT; + return 0; +} + +void snd_sb8dsp_interrupt(sb_t *chip) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + +#if 0 + snd_printk("sb8: interrupt\n"); +#endif + snd_sb_ack_8bit(chip); + switch (chip->mode) { + case SB_MODE_PLAYBACK_8: /* ok.. playback is active */ + substream = chip->playback_substream; + runtime = substream->runtime; + if (chip->playback_format == SB_DSP_OUTPUT) + snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START); + snd_pcm_period_elapsed(substream); + break; + case SB_MODE_CAPTURE_8: + substream = chip->capture_substream; + runtime = substream->runtime; + if (chip->capture_format == SB_DSP_INPUT) + snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START); + snd_pcm_period_elapsed(substream); + break; + } +} + +static snd_pcm_uframes_t snd_sb8_playback_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->mode != SB_MODE_PLAYBACK_8) + return 0; + ptr = chip->p_dma_size - snd_dma_residue(chip->dma8); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_sb8_capture_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->mode != SB_MODE_CAPTURE_8) + return 0; + ptr = chip->c_dma_size - snd_dma_residue(chip->dma8); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static snd_pcm_hardware_t snd_sb8_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8, + rates: (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050), + rate_min: 4000, + rate_max: 23000, + channels_min: 1, + channels_max: 1, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_sb8_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8, + rates: (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_11025), + rate_min: 4000, + rate_max: 13000, + channels_min: 1, + channels_max: 1, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * + */ + +int snd_sb8_open(snd_pcm_substream_t *substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->open) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + chip->open |= SB_OPEN_PCM; + spin_unlock_irqrestore(&chip->open_lock, flags); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + chip->playback_substream = substream; + runtime->hw = snd_sb8_playback; + } else { + chip->capture_substream = substream; + runtime->hw = snd_sb8_capture; + } + switch (chip->hardware) { + case SB_HW_PRO: + runtime->hw.rate_max = 44100; + runtime->hw.channels_max = 2; + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_sb8_hw_constraint_rate_channels, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_sb8_hw_constraint_channels_rate, 0, + SNDRV_PCM_HW_PARAM_RATE, -1); + break; + case SB_HW_201: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + runtime->hw.rate_max = 44100; + } else { + runtime->hw.rate_max = 15000; + } + default: + break; + } + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clock); + return 0; +} + +int snd_sb8_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + chip->capture_substream = NULL; + spin_lock_irqsave(&chip->open_lock, flags); + chip->open &= ~SB_OPEN_PCM; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +/* + * Initialization part + */ + +static snd_pcm_ops_t snd_sb8_playback_ops = { + open: snd_sb8_open, + close: snd_sb8_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sb8_hw_params, + hw_free: snd_sb8_hw_free, + prepare: snd_sb8_playback_prepare, + trigger: snd_sb8_playback_trigger, + pointer: snd_sb8_playback_pointer, +}; + +static snd_pcm_ops_t snd_sb8_capture_ops = { + open: snd_sb8_open, + close: snd_sb8_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sb8_hw_params, + hw_free: snd_sb8_hw_free, + prepare: snd_sb8_capture_prepare, + trigger: snd_sb8_capture_trigger, + pointer: snd_sb8_capture_pointer, +}; + +static void snd_sb8dsp_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_card_t *card = chip->card; + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0) + return err; + sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + pcm->private_data = chip; + pcm->private_free = snd_sb8dsp_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +EXPORT_SYMBOL(snd_sb8dsp_pcm); +EXPORT_SYMBOL(snd_sb8dsp_interrupt); + /* sb8_midi.c */ +EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt); +EXPORT_SYMBOL(snd_sb8dsp_midi); + +/* + * INIT part + */ + +static int __init alsa_sb8_init(void) +{ + return 0; +} + +static void __exit alsa_sb8_exit(void) +{ +} + +module_init(alsa_sb8_init) +module_exit(alsa_sb8_exit) diff -Nru a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/sb8_midi.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,262 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of SoundBlaster cards - MIDI interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -- + * + * Sun May 9 22:54:38 BST 1999 George David Morrison + * Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from + * working. + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +/* + + */ + +void snd_sb8dsp_midi_interrupt(sb_t * chip) +{ + snd_rawmidi_t *rmidi; + int max = 64; + char byte; + + if (chip == NULL || (rmidi = chip->rmidi) == NULL) { + inb(SBP(chip, READ)); /* ack interrupt */ + return; + } + while (max-- > 0) { + spin_lock(&chip->midi_input_lock); + if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { + byte = inb(SBP(chip, READ)); + spin_unlock(&chip->midi_input_lock); + snd_rawmidi_receive(chip->midi_substream_input, &byte, 1); + } else { + spin_unlock(&chip->midi_input_lock); + } + } +} + +/* + + */ + +static int snd_sb8dsp_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->open) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + chip->open |= SB_OPEN_MIDI_INPUT; + chip->midi_substream_input = substream; + if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static int snd_sb8dsp_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->open) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + chip->open |= SB_OPEN_MIDI_OUTPUT; + chip->midi_substream_output = substream; + if (!(chip->open & SB_OPEN_MIDI_INPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static int snd_sb8dsp_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_TRIGGER); + chip->midi_substream_input = NULL; + if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static int snd_sb8dsp_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + chip->open &= ~SB_OPEN_MIDI_OUTPUT; + chip->midi_substream_output = NULL; + if (!(chip->open & SB_OPEN_MIDI_INPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static void snd_sb8dsp_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + spin_lock_irqsave(&chip->open_lock, flags); + if (up) { + if (!(chip->open & SB_OPEN_MIDI_TRIGGER)) { + snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); + chip->open |= SB_OPEN_MIDI_TRIGGER; + } + } else { + if (chip->open & SB_OPEN_MIDI_TRIGGER) { + snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); + chip->open &= ~SB_OPEN_MIDI_TRIGGER; + } + } + spin_unlock_irqrestore(&chip->open_lock, flags); +} + +static void snd_sb8dsp_midi_output_write(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + char byte; + int max = 32; + + /* how big is Tx FIFO? */ + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + while (max-- > 0) { + spin_lock_irqsave(&chip->open_lock, flags); + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + chip->open &= ~SB_OPEN_MIDI_TRIGGER; + del_timer(&chip->midi_timer); + spin_unlock_irqrestore(&chip->open_lock, flags); + return; + } + snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT); + snd_sbdsp_command(chip, byte); + spin_unlock_irqrestore(&chip->open_lock, flags); + } +} + +static void snd_sb8dsp_midi_output_timer(unsigned long data) +{ + snd_rawmidi_substream_t * substream = (snd_rawmidi_substream_t *) data; + sb_t * chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->open_lock, flags); + chip->midi_timer.expires = 1 + jiffies; + add_timer(&chip->midi_timer); + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sb8dsp_midi_output_write(substream); +} + +static void snd_sb8dsp_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + spin_lock_irqsave(&chip->open_lock, flags); + if (up) { + if (!(chip->open & SB_OPEN_MIDI_TRIGGER)) { + chip->midi_timer.function = snd_sb8dsp_midi_output_timer; + chip->midi_timer.data = (unsigned long) substream; + chip->midi_timer.expires = 1 + jiffies; + add_timer(&chip->midi_timer); + chip->open |= SB_OPEN_MIDI_TRIGGER; + } + } else { + if (chip->open & SB_OPEN_MIDI_TRIGGER) { + chip->open &= ~SB_OPEN_MIDI_TRIGGER; + } + } + spin_unlock_irqrestore(&chip->open_lock, flags); + + if (up) + snd_sb8dsp_midi_output_write(substream); +} + +/* + + */ + +static snd_rawmidi_ops_t snd_sb8dsp_midi_output = +{ + open: snd_sb8dsp_midi_output_open, + close: snd_sb8dsp_midi_output_close, + trigger: snd_sb8dsp_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_sb8dsp_midi_input = +{ + open: snd_sb8dsp_midi_input_open, + close: snd_sb8dsp_midi_input_close, + trigger: snd_sb8dsp_midi_input_trigger, +}; + +int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, "SB8 MIDI"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = chip; + chip->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} diff -Nru a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/sb_common.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,304 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Uros Bizjak + * + * Lowlevel routines for control of Sound Blaster cards + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t sb_t + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ALSA lowlevel driver for Sound Blaster cards"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +#define BUSY_LOOPS 100000 + +#undef IO_DEBUG + +int snd_sbdsp_command(sb_t *chip, unsigned char val) +{ + int i; +#ifdef IO_DEBUG + snd_printk("command 0x%x\n", val); +#endif + for (i = BUSY_LOOPS; i; i--) + if ((inb(SBP(chip, STATUS)) & 0x80) == 0) { + outb(val, SBP(chip, COMMAND)); + return 1; + } + snd_printd("%s [0x%lx]: timeout (0x%x)\n", __FUNCTION__, chip->port, val); + return 0; +} + +int snd_sbdsp_get_byte(sb_t *chip) +{ + int val; + int i; + for (i = BUSY_LOOPS; i; i--) { + if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { + val = inb(SBP(chip, READ)); +#ifdef IO_DEBUG + snd_printk("get_byte 0x%x\n", val); +#endif + return val; + } + } + snd_printd("%s [0x%lx]: timeout\n", __FUNCTION__, chip->port); + return -ENODEV; +} + +int snd_sbdsp_reset(sb_t *chip) +{ + int i; + + outb(1, SBP(chip, RESET)); + udelay(10); + outb(0, SBP(chip, RESET)); + udelay(30); + for (i = BUSY_LOOPS; i; i--) + if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { + if (inb(SBP(chip, READ)) == 0xaa) + return 0; + else + break; + } + snd_printdd("%s [0x%lx] failed...\n", __FUNCTION__, chip->port); + return -ENODEV; +} + +int snd_sbdsp_version(sb_t * chip) +{ + unsigned int result = -ENODEV; + + snd_sbdsp_command(chip, SB_DSP_GET_VERSION); + result = (short) snd_sbdsp_get_byte(chip) << 8; + result |= (short) snd_sbdsp_get_byte(chip); + return result; +} + +static int snd_sbdsp_probe(sb_t * chip) +{ + int version; + int major, minor; + char *str; + unsigned long flags; + + /* + * initialization sequence + */ + + spin_lock_irqsave(&chip->reg_lock, flags); + if (snd_sbdsp_reset(chip) < 0) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENODEV; + } + version = snd_sbdsp_version(chip); + if (version < 0) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENODEV; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + major = version >> 8; + minor = version & 0xff; + snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n", + chip->port, major, minor); + + switch (chip->hardware) { + case SB_HW_AUTO: + switch (major) { + case 1: + chip->hardware = SB_HW_10; + str = "1.0"; + break; + case 2: + if (minor) { + chip->hardware = SB_HW_201; + str = "2.01+"; + } else { + chip->hardware = SB_HW_20; + str = "2.0"; + } + break; + case 3: + chip->hardware = SB_HW_PRO; + str = "Pro"; + break; + case 4: + chip->hardware = SB_HW_16; + str = "16"; + break; + default: + snd_printk("SB [0x%lx]: unknown DSP chip version %i.%i\n", + chip->port, major, minor); + return -ENODEV; + } + break; + case SB_HW_ALS100: + str = "16 (ALS-100)"; + break; + case SB_HW_ALS4000: + str = "16 (ALS-4000)"; + break; + default: + return -ENODEV; + } + sprintf(chip->name, "Sound Blaster %s", str); + chip->version = (major << 8) | minor; + return 0; +} + +static int snd_sbdsp_free(sb_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_alt_port) { + release_resource(chip->res_alt_port); + kfree_nocheck(chip->res_alt_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); +#ifdef CONFIG_ISA + if (chip->dma8 >= 0) { + disable_dma(chip->dma8); + free_dma(chip->dma8); + } + if (chip->dma16 >= 0) { + disable_dma(chip->dma16); + free_dma(chip->dma16); + } +#endif + snd_magic_kfree(chip); + return 0; +} + +static int snd_sbdsp_dev_free(snd_device_t *device) +{ + sb_t *chip = snd_magic_cast(sb_t, device->device_data, return -ENXIO); + return snd_sbdsp_free(chip); +} + +int snd_sbdsp_create(snd_card_t *card, + unsigned long port, + int irq, + void (*irq_handler)(int, void *, struct pt_regs *), + int dma8, + int dma16, + unsigned short hardware, + sb_t **r_chip) +{ + sb_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_sbdsp_dev_free, + }; + + snd_assert(r_chip != NULL, return -EINVAL); + *r_chip = NULL; + chip = snd_magic_kcalloc(sb_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->open_lock); + spin_lock_init(&chip->midi_input_lock); + spin_lock_init(&chip->mixer_lock); + chip->irq = -1; + chip->dma8 = -1; + chip->dma16 = -1; + chip->port = port; + + if (request_irq(irq, irq_handler, hardware == SB_HW_ALS4000 ? + SA_INTERRUPT | SA_SHIRQ : SA_INTERRUPT, + "SoundBlaster", (void *) chip)) { + snd_sbdsp_free(chip); + return -EBUSY; + } + chip->irq = irq; + + if (hardware == SB_HW_ALS4000) + goto __skip_allocation; + + if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) { + snd_sbdsp_free(chip); + return -EBUSY; + } + +#ifdef CONFIG_ISA + if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) { + snd_sbdsp_free(chip); + return -EBUSY; + } + chip->dma8 = dma8; + if (dma16 >= 0 && request_dma(dma16, "SoundBlaster - 16bit")) { + snd_sbdsp_free(chip); + return -EBUSY; + } + chip->dma16 = dma16; +#endif + + __skip_allocation: + chip->card = card; + chip->hardware = hardware; + if ((err = snd_sbdsp_probe(chip)) < 0) { + snd_sbdsp_free(chip); + return err; + } + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_sbdsp_free(chip); + return err; + } + *r_chip = chip; + return 0; +} + +EXPORT_SYMBOL(snd_sbdsp_command); +EXPORT_SYMBOL(snd_sbdsp_get_byte); +EXPORT_SYMBOL(snd_sbdsp_reset); +EXPORT_SYMBOL(snd_sbdsp_create); +/* sb_mixer.c */ +EXPORT_SYMBOL(snd_sbmixer_write); +EXPORT_SYMBOL(snd_sbmixer_read); +EXPORT_SYMBOL(snd_sbmixer_new); + +/* + * INIT part + */ + +static int __init alsa_sb_common_init(void) +{ + return 0; +} + +static void __exit alsa_sb_common_exit(void) +{ +} + +module_init(alsa_sb_common_init) +module_exit(alsa_sb_common_exit) diff -Nru a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/sb_mixer.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,497 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for Sound Blaster mixer control + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +#define chip_t sb_t + +#undef IO_DEBUG + +void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data) +{ + outb(reg, SBP(chip, MIXER_ADDR)); + udelay(10); + outb(data, SBP(chip, MIXER_DATA)); + udelay(10); +#ifdef IO_DEBUG + snd_printk("mixer_write 0x%x 0x%x\n", reg, data); +#endif +} + +unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg) +{ + unsigned char result; + + outb(reg, SBP(chip, MIXER_ADDR)); + udelay(10); + result = inb(SBP(chip, MIXER_DATA)); + udelay(10); +#ifdef IO_DEBUG + snd_printk("mixer_read 0x%x 0x%x\n", reg, result); +#endif + return result; +} + +/* + * Single channel mixer element + */ + +#define SB_SINGLE(xname, reg, shift, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \ + name: xname, \ + info: snd_sbmixer_info_single, \ + get: snd_sbmixer_get_single, put: snd_sbmixer_put_single, \ + private_value: reg | (shift << 16) | (mask << 24) } + +static int snd_sbmixer_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sbmixer_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0xff; + int mask = (kcontrol->private_value >> 24) & 0xff; + unsigned char val; + + spin_lock_irqsave(&sb->mixer_lock, flags); + val = (snd_sbmixer_read(sb, reg) >> shift) & mask; + spin_unlock_irqrestore(&sb->mixer_lock, flags); + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int snd_sbmixer_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned char val, oval; + + val = (ucontrol->value.integer.value[0] & mask) << shift; + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, reg); + val = (oval & ~(mask << shift)) | val; + change = val != oval; + if (change) + snd_sbmixer_write(sb, reg, val); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +/* + * Double channel mixer element + */ + +#define SB_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \ + name: xname, \ + info: snd_sbmixer_info_double, \ + get: snd_sbmixer_get_double, put: snd_sbmixer_put_double, \ + private_value: left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) } + +static int snd_sbmixer_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sbmixer_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + unsigned char left, right; + + spin_lock_irqsave(&sb->mixer_lock, flags); + left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask; + right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask; + spin_unlock_irqrestore(&sb->mixer_lock, flags); + ucontrol->value.integer.value[0] = left; + ucontrol->value.integer.value[1] = right; + return 0; +} + +static int snd_sbmixer_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned char left, right, oleft, oright; + + left = (ucontrol->value.integer.value[0] & mask) << left_shift; + right = (ucontrol->value.integer.value[1] & mask) << right_shift; + spin_lock_irqsave(&sb->mixer_lock, flags); + if (left_reg == right_reg) { + oleft = snd_sbmixer_read(sb, left_reg); + left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right; + change = left != oleft; + if (change) + snd_sbmixer_write(sb, left_reg, left); + } else { + oleft = snd_sbmixer_read(sb, left_reg); + oright = snd_sbmixer_read(sb, right_reg); + left = (oleft & ~(mask << left_shift)) | left; + right = (oright & ~(mask << right_shift)) | right; + change = left != oleft || right != oright; + if (change) { + snd_sbmixer_write(sb, left_reg, left); + snd_sbmixer_write(sb, right_reg, right); + } + } + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +/* + * SBPRO input multiplexer + */ + +static int snd_sb8mixer_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { + "Mic", "CD", "Line" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + + +static int snd_sb8mixer_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char oval; + + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + switch ((oval >> 0x01) & 0x03) { + case SB_DSP_MIXS_CD: + ucontrol->value.enumerated.item[0] = 1; + break; + case SB_DSP_MIXS_LINE: + ucontrol->value.enumerated.item[0] = 2; + break; + default: + ucontrol->value.enumerated.item[0] = 0; + break; + } + return 0; +} + +static int snd_sb8mixer_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval, oval; + + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + switch (ucontrol->value.enumerated.item[0]) { + case 1: + nval = SB_DSP_MIXS_CD; + break; + case 2: + nval = SB_DSP_MIXS_LINE; + break; + default: + nval = SB_DSP_MIXS_MIC; + } + nval <<= 1; + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE); + nval |= oval & ~0x06; + change = nval != oval; + if (change) + snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +/* + * SB16 input switch + */ + +#define SB16_INPUT_SW(xname, reg1, reg2, left_shift, right_shift) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \ + name: xname, \ + info: snd_sb16mixer_info_input_sw, \ + get: snd_sb16mixer_get_input_sw, put: snd_sb16mixer_put_input_sw, \ + private_value: reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) } + +static int snd_sb16mixer_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_sb16mixer_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + unsigned char val1, val2; + + spin_lock_irqsave(&sb->mixer_lock, flags); + val1 = snd_sbmixer_read(sb, reg1); + val2 = snd_sbmixer_read(sb, reg2); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01; + ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01; + ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01; + ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01; + return 0; +} + +static int snd_sb16mixer_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + int change; + unsigned char val1, val2, oval1, oval2; + + spin_lock_irqsave(&sb->mixer_lock, flags); + oval1 = snd_sbmixer_read(sb, reg1); + oval2 = snd_sbmixer_read(sb, reg2); + val1 = oval1 & ~((1 << left_shift) | (1 << right_shift)); + val2 = oval2 & ~((1 << left_shift) | (1 << right_shift)); + val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift; + val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift; + val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift; + val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift; + change = val1 != oval1 || val2 != oval2; + if (change) { + snd_sbmixer_write(sb, reg1, val1); + snd_sbmixer_write(sb, reg2, val2); + } + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +#define SB20_CONTROLS (sizeof(snd_sb20_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sb20_controls[] = { +SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7), +SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3), +SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7), +SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7) +}; + +#define SB20_INIT_VALUES (sizeof(snd_sb20_init_values)/sizeof(unsigned char)/2) + +static unsigned char snd_sb20_init_values[][2] = { + { SB_DSP20_MASTER_DEV, 0 }, + { SB_DSP20_FM_DEV, 0 }, +}; + +#define SBPRO_CONTROLS (sizeof(snd_sbpro_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sbpro_controls[] = { +SB_DOUBLE("Master Playback Volume", SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7), +SB_DOUBLE("PCM Playback Volume", SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7), +SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1), +SB_DOUBLE("Synth Playback Volume", SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7), +SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7), +SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7), +SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_sb8mixer_info_mux, + get: snd_sb8mixer_get_mux, + put: snd_sb8mixer_put_mux, +}, +SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1), +SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1) +}; + +#define SBPRO_INIT_VALUES (sizeof(snd_sbpro_init_values)/sizeof(unsigned char)/2) + +static unsigned char snd_sbpro_init_values[][2] = { + { SB_DSP_MASTER_DEV, 0 }, + { SB_DSP_PCM_DEV, 0 }, + { SB_DSP_FM_DEV, 0 }, +}; + +#define SB16_CONTROLS (sizeof(snd_sb16_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sb16_controls[] = { +SB_DOUBLE("Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31), +SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1), +SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15), +SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15), +SB_DOUBLE("PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31), +SB16_INPUT_SW("Synth Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5), +SB_DOUBLE("Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31), +SB16_INPUT_SW("CD Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1), +SB_DOUBLE("CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1), +SB_DOUBLE("CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31), +SB16_INPUT_SW("Line Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3), +SB_DOUBLE("Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1), +SB_DOUBLE("Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31), +SB_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1), +SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1), +SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), +SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3), +SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3), +SB_DOUBLE("Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3), +SB_SINGLE("Auto Mic Gain", SB_DSP4_MIC_AGC, 0, 1) +}; + +#define SB16_INIT_VALUES (sizeof(snd_sb16_init_values)/sizeof(unsigned char)/2) + +static unsigned char snd_sb16_init_values[][2] = { + { SB_DSP4_MASTER_DEV + 0, 0 }, + { SB_DSP4_MASTER_DEV + 1, 0 }, + { SB_DSP4_PCM_DEV + 0, 0 }, + { SB_DSP4_PCM_DEV + 1, 0 }, + { SB_DSP4_SYNTH_DEV + 0, 0 }, + { SB_DSP4_SYNTH_DEV + 1, 0 }, + { SB_DSP4_INPUT_LEFT, 0 }, + { SB_DSP4_INPUT_RIGHT, 0 }, + { SB_DSP4_OUTPUT_SW, 0 }, + { SB_DSP4_SPEAKER_DEV, 0 }, +}; + +static int snd_sbmixer_init(sb_t *chip, + snd_kcontrol_new_t *controls, + int controls_count, + unsigned char map[][2], + int map_count, + char *name) +{ + unsigned long flags; + snd_card_t *card = chip->card; + int idx, err; + + /* mixer reset */ + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, 0x00, 0x00); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + /* mute and zero volume channels */ + for (idx = 0; idx < map_count; idx++) { + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, map[idx][0], map[idx][1]); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + } + + for (idx = 0; idx < controls_count; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&controls[idx], chip))) < 0) + return err; + } + snd_component_add(card, name); + strcpy(card->mixername, name); + return 0; +} + +int snd_sbmixer_new(sb_t *chip) +{ + snd_card_t * card; + int err; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + switch (chip->hardware) { + case SB_HW_10: + return 0; /* no mixer chip on SB1.x */ + case SB_HW_20: + case SB_HW_201: + if ((err = snd_sbmixer_init(chip, + snd_sb20_controls, SB20_CONTROLS, + snd_sb20_init_values, SB20_INIT_VALUES, + "CTL1335")) < 0) + return err; + break; + case SB_HW_PRO: + if ((err = snd_sbmixer_init(chip, + snd_sbpro_controls, SBPRO_CONTROLS, + snd_sbpro_init_values, SBPRO_INIT_VALUES, + "CTL1345")) < 0) + return err; + break; + case SB_HW_16: + case SB_HW_ALS100: + case SB_HW_ALS4000: + if ((err = snd_sbmixer_init(chip, + snd_sb16_controls, SB16_CONTROLS, + snd_sb16_init_values, SB16_INIT_VALUES, + "CTL1745")) < 0) + return err; + break; + default: + strcpy(card->mixername, "???"); + } + return 0; +} diff -Nru a/sound/isa/sb/sbawe.c b/sound/isa/sb/sbawe.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sb/sbawe.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2 @@ +#define SNDRV_SBAWE +#include "sb16.c" diff -Nru a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/sgalaxy.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,352 @@ +/* + * Driver for Aztech Sound Galaxy cards + * Copyright (c) by Christopher Butler +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Christopher Butler "); +MODULE_DESCRIPTION("Aztech Sound Galaxy"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Aztech Systems,Sound Galaxy}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240 */ +static long snd_wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 7,9,10,11 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Sound Galaxy soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Sound Galaxy soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_sbport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_sbport, "Port # for Sound Galaxy SB driver."); +MODULE_PARM_SYNTAX(snd_sbport, SNDRV_ENABLED ",allows:{{0x220},{0x240}},dialog:list"); +MODULE_PARM(snd_wssport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_wssport, "Port # for Sound Galaxy WSS driver."); +MODULE_PARM_SYNTAX(snd_wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for Sound Galaxy driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{7},{9},{10},{11}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for Sound Galaxy driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA8_DESC); + +#define SGALAXY_AUXC_LEFT 18 +#define SGALAXY_AUXC_RIGHT 19 + +static snd_card_t *snd_sgalaxy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +/* + + */ + +#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x ) + +/* from lowlevel/sb/sb.c - to avoid having to allocate a sb_t for the */ +/* short time we actually need it.. */ + +static int snd_sgalaxy_sbdsp_reset(unsigned long port) +{ + int i; + + outb(1, SBP1(port, RESET)); + udelay(10); + outb(0, SBP1(port, RESET)); + udelay(30); + for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++); + if (inb(SBP1(port, READ)) != 0xaa) { + snd_printd("sb_reset: failed at 0x%lx!!!\n", port); + return -ENODEV; + } + return 0; +} + +static int __init snd_sgalaxy_sbdsp_command(unsigned long port, unsigned char val) +{ + int i; + + for (i = 10000; i; i--) + if ((inb(SBP1(port, STATUS)) & 0x80) == 0) { + outb(val, SBP1(port, COMMAND)); + return 1; + } + + return 0; +} + +static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) +{ + static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, + 0x10, 0x18, 0x20, -1, -1, -1, -1}; + static int dma_bits[] = {1, 2, 0, 3}; + int tmp, tmp1; + + unsigned int flags; + + if ((tmp = inb(port + 3)) == 0xff) + { + snd_printdd("I/O address dead (0x%lx)\n", port); + return 0; + } +#if 0 + snd_printdd("WSS signature = 0x%x\n", tmp); +#endif + + if ((tmp & 0x3f) != 0x04 && + (tmp & 0x3f) != 0x0f && + (tmp & 0x3f) != 0x00) { + snd_printdd("No WSS signature detected on port 0x%lx\n", + port + 3); + return 0; + } + +#if 0 + snd_printdd("sgalaxy - setting up IRQ/DMA for WSS\n"); +#endif + + save_flags(flags); + cli(); + + /* initialize IRQ for WSS codec */ + tmp = interrupt_bits[irq % 16]; + if (tmp < 0) { + restore_flags(flags); + return -EINVAL; + } + outb(tmp | 0x40, port); + tmp1 = dma_bits[dma % 4]; + outb(tmp | tmp1, port); + + restore_flags(flags); + return 0; +} + +static int __init snd_sgalaxy_detect(int dev, int irq, int dma) +{ +#if 0 + snd_printdd("sgalaxy - switching to WSS mode\n"); +#endif + + /* switch to WSS mode */ + snd_sgalaxy_sbdsp_reset(snd_sbport[dev]); + + snd_sgalaxy_sbdsp_command(snd_sbport[dev], 9); + snd_sgalaxy_sbdsp_command(snd_sbport[dev], 0); + + udelay(400); + return snd_sgalaxy_setup_wss(snd_wssport[dev], irq, dma); +} + +#define SGALAXY_CONTROLS 2 + +static snd_kcontrol_new_t snd_sgalaxy_controls[2] = { +AD1848_DOUBLE("Aux Playback Switch", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), +AD1848_DOUBLE("Aux Playback Volume", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0) +}; + +static int __init snd_sgalaxy_mixer(ad1848_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + int idx, err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUX0 to LINE */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "Line Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Line Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUX1 to FM */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "FM Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "FM Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* build AUX2 input */ + for (idx = 0; idx < SGALAXY_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sgalaxy_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +static int __init snd_sgalaxy_probe(int dev) +{ + static int possible_irqs[] = {7, 9, 10, 11, -1}; + static int possible_dmas[] = {1, 3, 0, -1}; + int err, irq, dma1; + snd_card_t *card; + ad1848_t *chip; + + if (snd_sbport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify SB port\n"); + return -EINVAL; + } + if (snd_wssport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify WSS port\n"); + return -EINVAL; + } + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + dma1 = snd_dma1[dev]; + if (dma1 == SNDRV_AUTO_DMA) { + if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA\n"); + return -EBUSY; + } + } + + if ((err = snd_sgalaxy_detect(dev, irq, dma1)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ad1848_create(card, snd_wssport[dev] + 4, + irq, dma1, + AD1848_HW_DETECT, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ad1848_pcm(chip, 0, NULL)) < 0) { + snd_printdd("sgalaxy - error creating new ad1848 PCM device\n"); + snd_card_free(card); + return err; + } + if ((err = snd_ad1848_mixer(chip)) < 0) { + snd_printdd("sgalaxy - error creating new ad1848 mixer\n"); + snd_card_free(card); + return err; + } + if (snd_sgalaxy_mixer(chip) < 0) { + snd_printdd("sgalaxy - the mixer rewrite failed\n"); + snd_card_free(card); + return err; + } + + strcpy(card->driver, "Sound Galaxy"); + strcpy(card->shortname, "Sound Galaxy"); + sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d", + snd_wssport[dev], irq, dma1); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_sgalaxy_cards[dev] = card; + return 0; +} + +static int __init alsa_card_sgalaxy_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_sgalaxy_probe(dev) >= 0) + cards++; + } + if (!cards) { +#ifdef MODULE + snd_printk("Sound Galaxy soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + + return 0; +} + +static void __exit alsa_card_sgalaxy_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_sgalaxy_cards[idx]); +} + +module_init(alsa_card_sgalaxy_init) +module_exit(alsa_card_sgalaxy_exit) + +#ifndef MODULE + +/* format is: snd-sgalaxy=snd_enable,snd_index,snd_id, + snd_sbport,snd_wssport, + snd_irq,snd_dma1 */ + +static int __init alsa_card_sgalaxy_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_sbport[nr_dev]) == 2 && + get_option(&str,(int *)&snd_wssport[nr_dev]) == 2 && + get_option(&str,(int *)&snd_irq[nr_dev]) == 2 && + get_option(&str,(int *)&snd_dma1[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sgalaxy=", alsa_card_sgalaxy_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/wavefront/Makefile b/sound/isa/wavefront/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/wavefront/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,30 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _wavefront.o + +#list-multi := snd-wavefront-fx.o snd-wavefront-synth.o snd-wavefront.o +list-multi := snd-wavefront.o + +#export-objs := wavefront_fx.o wavefront_synth.o + +#snd-wavefront-fx-objs := wavefront_fx.o +#snd-wavefront-synth-objs := wavefront_synth.o wavefront_midi.o +#snd-wavefront-objs := wavefront.o +snd-wavefront-objs := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_WAVEFRONT) += snd-wavefront.o + +include $(TOPDIR)/Rules.make + +snd-wavefront-fx.o: $(snd-wavefront-fx-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-wavefront-fx-objs) + +snd-wavefront-synth.o: $(snd-wavefront-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-wavefront-synth-objs) + +snd-wavefront.o: $(snd-wavefront-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-wavefront-objs) diff -Nru a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/wavefront/wavefront.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,797 @@ +/* + * ALSA card-level driver for Turtle Beach Wavefront cards + * (Maui,Tropez,Tropez+) + * + * Copyright (c) 1997-1999 by Paul Barton-Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include + +#define chip_t cs4231_t + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Paul Barton-Davis "); +MODULE_DESCRIPTION("Turtle Beach Wavefront"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Turtle Beach,Maui/Tropez/Tropez+}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +static long snd_cs4232_pcm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_cs4232_pcm_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static long snd_cs4232_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_cs4232_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ +static long snd_ics2115_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_ics2115_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,9,11,12,15 */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_use_cs4232_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_SYNTAX(snd_index, "Index value for WaveFront soundcard."); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for WaveFront soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable WaveFront soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for WaveFront soundcards."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(snd_cs4232_pcm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_4232_port, "Port # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(snd_cs4232_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_cs4232_pcm_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_cs4232_pcm_irq, "IRQ # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(snd_cs4232_pcm_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); +MODULE_PARM(snd_cs4232_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_cs4232_mpu_port, "port # for CS4232 MPU-401 interface."); +MODULE_PARM_SYNTAX(snd_cs4232_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_cs4232_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface."); +MODULE_PARM_SYNTAX(snd_cs4232_mpu_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_ics2115_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_ics2115_irq, "IRQ # for ICS2115."); +MODULE_PARM_SYNTAX(snd_ics2115_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_ics2115_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_ics2115_port, "Port # for ICS2115."); +MODULE_PARM_SYNTAX(snd_ics2115_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port #."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_use_cs4232_midi, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly located inside your computer)"); +MODULE_PARM_SYNTAX(snd_use_cs4232_midi, SNDRV_ENABLED ",allows use of CS4323 MPU-401 interface"); + +static snd_card_t *snd_wavefront_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_wavefront_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_wavefront_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +static struct isapnp_card_id snd_wavefront_pnpids[] __devinitdata = { + { + ISAPNP_CARD_ID('C','S','C',0x7532), /* Tropez */ + devs: { ISAPNP_DEVICE_ID('C','S','C',0x0000), /* WSS */ + ISAPNP_DEVICE_ID('C','S','C',0x0010), /* CTRL */ + ISAPNP_DEVICE_ID('P','n','P',0xb006), /* MPU */ + ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */ + }, + { + ISAPNP_CARD_ID('C','S','C',0x7632), /* Tropez+ */ + devs: { ISAPNP_DEVICE_ID('C','S','C',0x0000), /* WSS */ + ISAPNP_DEVICE_ID('C','S','C',0x0010), /* CTRL */ + ISAPNP_DEVICE_ID('P','n','P',0xb006), /* MPU */ + ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */ + }, + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_wavefront_pnpids); + +static int __init +snd_wavefront_isapnp (int dev, snd_wavefront_card_t *acard) +{ + const struct isapnp_card_id *id = snd_wavefront_isapnp_id[dev]; + struct isapnp_card *card = snd_wavefront_isapnp_cards[dev]; + struct isapnp_dev *pdev; + int tmp; + + /* Check for each logical device. */ + + /* CS4232 chip (aka "windows sound system") is logical device 0 */ + + acard->wss = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->wss->active) { + acard->wss = NULL; + return -EBUSY; + } + + /* there is a game port at logical device 1, but we ignore it completely */ + + /* the control interface is logical device 2, but we ignore it + completely. in fact, nobody even seems to know what it + does. + */ + + /* Only configure the CS4232 MIDI interface if its been + specifically requested. It is logical device 3. + */ + + if (snd_use_cs4232_midi[dev]) { + acard->mpu = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->mpu->active) { + acard->wss = acard->synth = acard->mpu = NULL; + return -EBUSY; + } + } + + /* The ICS2115 synth is logical device 4 */ + + acard->synth = isapnp_find_dev(card, id->devs[3].vendor, id->devs[3].function, NULL); + if (acard->synth->active) { + acard->wss = acard->synth = NULL; + return -EBUSY; + } + + /* PCM/FM initialization */ + + pdev = acard->wss; + + if ((tmp = pdev->prepare (pdev)) < 0) { + if (tmp == -EBUSY) { + snd_printk ("ISA PnP configuration appears to have " + "been done. Restart the isapnp module.\n"); + return 0; + } + snd_printk ("isapnp WSS preparation failed\n"); + return -EAGAIN; + } + + /* An interesting note from the Tropez+ FAQ: + + Q. [Ports] Why is the base address of the WSS I/O ports off by 4? + + A. WSS I/O requires a block of 8 I/O addresses ("ports"). Of these, the first + 4 are used to identify and configure the board. With the advent of PnP, + these first 4 addresses have become obsolete, and software applications + only use the last 4 addresses to control the codec chip. Therefore, the + base address setting "skips past" the 4 unused addresses. + + */ + + if (snd_cs4232_pcm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_cs4232_pcm_port[dev], 4); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1); + if (snd_cs4232_pcm_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_cs4232_pcm_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk ("isapnp WSS activation failed\n"); + return -EBUSY; + } + + snd_cs4232_pcm_port[dev] = pdev->resource[0].start; + snd_fm_port[dev] = pdev->resource[1].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_cs4232_pcm_irq[dev] = pdev->irq_resource[0].start; + + /* Synth initialization */ + + pdev = acard->synth; + + if ((tmp = pdev->prepare(pdev))<0) { + if (tmp == -EBUSY) { + snd_printk ("ISA PnP configuration appears to have " + "been done. Restart the isapnp module.\n"); + } + acard->wss->deactivate(acard->wss); + snd_printk ("ICS2115 synth preparation failed\n"); + return -EAGAIN; + } + if (snd_ics2115_port[dev] != SNDRV_AUTO_PORT) { + isapnp_resource_change(&pdev->resource[0], snd_ics2115_port[dev], 16); + } + + if (snd_ics2115_port[dev] != SNDRV_AUTO_IRQ) { + isapnp_resource_change(&pdev->irq_resource[0], snd_ics2115_irq[dev], 1); + } + + if (pdev->activate(pdev)<0) { + snd_printk("isapnp activation for ICS2115 failed\n"); + acard->wss->deactivate(acard->wss); + return -EBUSY; + } + + snd_ics2115_port[dev] = pdev->resource[0].start; + snd_ics2115_irq[dev] = pdev->irq_resource[0].start; + + /* CS4232 MPU initialization. Configure this only if + explicitly requested, since its physically inaccessible and + consumes another IRQ. + */ + + if (snd_use_cs4232_midi[dev]) { + + pdev = acard->mpu; + + if (pdev->prepare(pdev)<0) { + acard->wss->deactivate(acard->wss); + if (acard->synth) + acard->synth->deactivate(acard->synth); + snd_printk ("CS4232 MPU preparation failed\n"); + return -EAGAIN; + } + + if (snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_cs4232_mpu_port[dev], 2); + if (snd_cs4232_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->resource[0], snd_cs4232_mpu_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("isapnp CS4232 MPU activation failed\n"); + snd_cs4232_mpu_port[dev] = SNDRV_AUTO_PORT; + } else { + snd_cs4232_mpu_port[dev] = pdev->resource[0].start; + snd_cs4232_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + snd_printk ("CS4232 MPU: port=0x%lx, irq=%i\n", + snd_cs4232_mpu_port[dev], + snd_cs4232_mpu_irq[dev]); + } + + snd_printdd ("CS4232: pcm port=0x%lx, fm port=0x%lx, dma1=%i, dma2=%i, irq=%i\nICS2115: port=0x%lx, irq=%i\n", + snd_cs4232_pcm_port[dev], + snd_fm_port[dev], + snd_dma1[dev], + snd_dma2[dev], + snd_cs4232_pcm_irq[dev], + snd_ics2115_port[dev], + snd_ics2115_irq[dev]); + + return 0; +} + +static void +snd_wavefront_deactivate (snd_wavefront_card_t *acard) +{ + snd_printk ("deactivating PnP devices\n"); + if (acard->wss) { + acard->wss->deactivate(acard->wss); + acard->wss = NULL; + } + if (acard->ctrl) { + acard->ctrl->deactivate(acard->ctrl); + acard->ctrl = NULL; + } + if (acard->mpu) { + acard->mpu->deactivate(acard->mpu); + acard->mpu = NULL; + } + if (acard->synth) { + acard->synth->deactivate(acard->synth); + acard->synth = NULL; + } +} + +#endif /* __ISAPNP__ */ + +static void snd_wavefront_ics2115_interrupt(int irq, + void *dev_id, + struct pt_regs *regs) +{ + snd_wavefront_card_t *acard; + + acard = (snd_wavefront_card_t *) dev_id; + + if (acard == NULL) + return; + + if (acard->wavefront.interrupts_are_midi) { + snd_wavefront_midi_interrupt (acard); + } else { + snd_wavefront_internal_interrupt (acard); + } +} + +snd_hwdep_t * __init +snd_wavefront_new_synth (snd_card_t *card, + int hw_dev, + snd_wavefront_card_t *acard) +{ + snd_hwdep_t *wavefront_synth; + + if (snd_wavefront_detect (acard) < 0) { + return NULL; + } + + if (snd_wavefront_start (&acard->wavefront) < 0) { + return NULL; + } + + if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0) + return NULL; + strcpy (wavefront_synth->name, + "WaveFront (ICS2115) wavetable synthesizer"); + wavefront_synth->ops.open = snd_wavefront_synth_open; + wavefront_synth->ops.release = snd_wavefront_synth_release; + wavefront_synth->ops.ioctl = snd_wavefront_synth_ioctl; + + return wavefront_synth; +} + +snd_hwdep_t * __init +snd_wavefront_new_fx (snd_card_t *card, + int hw_dev, + snd_wavefront_card_t *acard, + unsigned long port) + +{ + snd_hwdep_t *fx_processor; + + if (snd_wavefront_fx_start (&acard->wavefront)) { + snd_printk ("cannot initialize YSS225 FX processor"); + return NULL; + } + + if (snd_hwdep_new (card, "YSS225", hw_dev, &fx_processor) < 0) + return NULL; + sprintf (fx_processor->name, "YSS225 FX Processor at 0x%lx", port); + fx_processor->ops.open = snd_wavefront_fx_open; + fx_processor->ops.release = snd_wavefront_fx_release; + fx_processor->ops.ioctl = snd_wavefront_fx_ioctl; + + return fx_processor; +} + +static snd_wavefront_mpu_id internal_id = internal_mpu; +static snd_wavefront_mpu_id external_id = external_mpu; + +snd_rawmidi_t * __init +snd_wavefront_new_midi (snd_card_t *card, + int midi_dev, + snd_wavefront_card_t *acard, + unsigned long port, + snd_wavefront_mpu_id mpu) + +{ + snd_rawmidi_t *rmidi; + static int first = 1; + + if (first) { + first = 0; + acard->wavefront.midi.base = port; + if (snd_wavefront_midi_start (acard)) { + snd_printk ("cannot initialize MIDI interface\n"); + return NULL; + } + } + + if (snd_rawmidi_new (card, "WaveFront MIDI", midi_dev, 1, 1, &rmidi) < 0) + return NULL; + + if (mpu == internal_mpu) { + strcpy(rmidi->name, "WaveFront MIDI (Internal)"); + rmidi->private_data = &internal_id; + } else { + strcpy(rmidi->name, "WaveFront MIDI (External)"); + rmidi->private_data = &external_id; + } + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_wavefront_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_wavefront_midi_input); + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + + return rmidi; +} + +static void +snd_wavefront_free(snd_card_t *card) +{ + snd_wavefront_card_t *acard = (snd_wavefront_card_t *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_wavefront_deactivate(acard); +#endif + if (acard->wavefront.res_base != NULL) { + release_resource(acard->wavefront.res_base); + kfree_nocheck(acard->wavefront.res_base); + } + if (acard->wavefront.irq > 0) + free_irq(acard->wavefront.irq, (void *)acard); + } +} + +static int __init +snd_wavefront_probe (int dev) +{ + snd_card_t *card; + snd_wavefront_card_t *acard; + cs4231_t *chip; + snd_hwdep_t *wavefront_synth; + snd_rawmidi_t *ics2115_internal_rmidi = NULL; + snd_rawmidi_t *ics2115_external_rmidi = NULL; + snd_hwdep_t *fx_processor; + int hw_dev = 0, midi_dev = 0, err; + + if (snd_cs4232_mpu_port[dev] < 0) + snd_cs4232_mpu_port[dev] = SNDRV_AUTO_PORT; + if (snd_fm_port[dev] < 0) + snd_fm_port[dev] = SNDRV_AUTO_PORT; + if (snd_ics2115_port[dev] < 0) + snd_ics2115_port[dev] = SNDRV_AUTO_PORT; + +#ifdef __ISAPNP__ + if (!snd_isapnp[dev]) { +#endif + if (snd_cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify CS4232 port\n"); + return -EINVAL; + } + if (snd_ics2115_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify ICS2115 port\n"); + return -ENODEV; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new (snd_index[dev], + snd_id[dev], + THIS_MODULE, + sizeof(snd_wavefront_card_t)); + + if (card == NULL) { + return -ENOMEM; + } + acard = (snd_wavefront_card_t *)card->private_data; + acard->wavefront.irq = -1; + init_waitqueue_head(&acard->wavefront.interrupt_sleeper); + spin_lock_init(&acard->wavefront.midi.open); + spin_lock_init(&acard->wavefront.midi.virtual); + card->private_free = snd_wavefront_free; + +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && snd_wavefront_isapnp (dev, acard) < 0) { + if (snd_cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) { + snd_printk ("isapnp detection failed\n"); + snd_card_free (card); + return -ENODEV; + } + } +#endif /* __ISAPNP__ */ + + /* --------- PCM --------------- */ + + if ((err = snd_cs4231_create (card, + snd_cs4232_pcm_port[dev], + -1, + snd_cs4232_pcm_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + CS4231_HW_DETECT, 0, &chip)) < 0) { + snd_card_free(card); + snd_printk ("can't allocate CS4231 device\n"); + return err; + } + + if ((err = snd_cs4231_pcm (chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_timer (chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + /* ---------- OPL3 synth --------- */ + + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) { + opl3_t *opl3; + + if ((err = snd_opl3_create(card, + snd_fm_port[dev], + snd_fm_port[dev] + 2, + OPL3_HW_OPL3_CS, + 0, &opl3)) < 0) { + snd_printk ("can't allocate or detect OPL3 synth\n"); + snd_card_free(card); + return err; + } + + if ((err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + hw_dev++; + } + + /* ------- ICS2115 Wavetable synth ------- */ + + if ((acard->wavefront.res_base = request_region(snd_ics2115_port[dev], 16, "ICS2115")) == NULL) { + snd_printk("unable to grab ICS2115 i/o region 0x%lx-0x%lx\n", snd_ics2115_port[dev], snd_ics2115_port[dev] + 16 - 1); + snd_card_free(card); + return -EBUSY; + } + if (request_irq(snd_ics2115_irq[dev], snd_wavefront_ics2115_interrupt, SA_INTERRUPT, "ICS2115", (void *)acard)) { + snd_printk("unable to use ICS2115 IRQ %d\n", snd_ics2115_irq[dev]); + snd_card_free(card); + return -EBUSY; + } + + acard->wavefront.irq = snd_ics2115_irq[dev]; + acard->wavefront.base = snd_ics2115_port[dev]; + + if ((wavefront_synth = snd_wavefront_new_synth (card, hw_dev, acard)) == NULL) { + snd_printk ("can't create WaveFront synth device\n"); + snd_card_free(card); + return -ENOMEM; + } + + strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer"); + wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115; + hw_dev++; + + /* --------- Mixer ------------ */ + + if ((err = snd_cs4231_mixer(chip)) < 0) { + snd_printk ("can't allocate mixer device\n"); + snd_card_free(card); + return err; + } + + /* -------- CS4232 MPU-401 interface -------- */ + + if (snd_cs4232_mpu_port[dev] > 0 && snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { + if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232, + snd_cs4232_mpu_port[dev], 0, + snd_cs4232_mpu_irq[dev], + SA_INTERRUPT, + NULL)) < 0) { + snd_printk ("can't allocate CS4232 MPU-401 device\n"); + snd_card_free(card); + return err; + } + midi_dev++; + } + + /* ------ ICS2115 internal MIDI ------------ */ + + if (snd_ics2115_port[dev] >= 0 && snd_ics2115_port[dev] != SNDRV_AUTO_PORT) { + ics2115_internal_rmidi = + snd_wavefront_new_midi (card, + midi_dev, + acard, + snd_ics2115_port[dev], + internal_mpu); + if (ics2115_internal_rmidi == NULL) { + snd_printk ("can't setup ICS2115 internal MIDI device\n"); + snd_card_free(card); + return -ENOMEM; + } + midi_dev++; + } + + /* ------ ICS2115 external MIDI ------------ */ + + if (snd_ics2115_port[dev] >= 0 && snd_ics2115_port[dev] != SNDRV_AUTO_PORT) { + ics2115_external_rmidi = + snd_wavefront_new_midi (card, + midi_dev, + acard, + snd_ics2115_port[dev], + external_mpu); + if (ics2115_external_rmidi == NULL) { + snd_printk ("can't setup ICS2115 external MIDI device\n"); + snd_card_free(card); + return -ENOMEM; + } + midi_dev++; + } + + /* FX processor for Tropez+ */ + + if (acard->wavefront.has_fx) { + fx_processor = snd_wavefront_new_fx (card, + hw_dev, + acard, + snd_ics2115_port[dev]); + if (fx_processor == NULL) { + snd_printk ("can't setup FX device\n"); + snd_card_free(card); + return -ENOMEM; + } + + hw_dev++; + + strcpy(card->driver, "Tropez+"); + strcpy(card->shortname, "Turtle Beach Tropez+"); + } else { + /* Need a way to distinguish between Maui and Tropez */ + strcpy(card->driver, "WaveFront"); + strcpy(card->shortname, "Turtle Beach WaveFront"); + } + + /* ----- Register the card --------- */ + + /* Not safe to include "Turtle Beach" in longname, due to + length restrictions + */ + + sprintf(card->longname, "%s PCM 0x%lx irq %d dma %d", + card->driver, + chip->port, + snd_cs4232_pcm_irq[dev], + snd_dma1[dev]); + + if (snd_dma2[dev] >= 0 && snd_dma2[dev] < 8) + sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]); + + if (snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { + sprintf (card->longname + strlen (card->longname), + " MPU-401 0x%lx irq %d", + snd_cs4232_mpu_port[dev], + snd_cs4232_mpu_irq[dev]); + } + + sprintf (card->longname + strlen (card->longname), + " SYNTH 0x%lx irq %d", + snd_ics2115_port[dev], + snd_ics2115_irq[dev]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_wavefront_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ + +static int __init snd_wavefront_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || !snd_isapnp[dev]) + continue; + snd_wavefront_isapnp_cards[dev] = card; + snd_wavefront_isapnp_id[dev] = id; + res = snd_wavefront_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} + +#endif /* __ISAPNP__ */ + +static int __init alsa_card_wavefront_init(void) +{ + int cards = 0; + int dev; + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (snd_wavefront_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_wavefront_pnpids, snd_wavefront_isapnp_detect); +#endif + if (!cards) { +#ifdef MODULE + snd_printk ("No cards found or devices busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_wavefront_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_wavefront_cards[idx]); +} + +module_init(alsa_card_wavefront_init) +module_exit(alsa_card_wavefront_exit) + +#ifndef MODULE + +/* format is: snd-wavefront=snd_enable,snd_index,snd_id,snd_isapnp, + snd_cs4232_pcm_port,snd_cs4232_pcm_irq, + snd_cs4232_mpu_port,snd_cs4232_mpu_irq, + snd_ics2115_port,snd_ics2115_irq, + snd_fm_port, + snd_dma1,snd_dma2, + snd_use_cs4232_midi */ + +static int __init alsa_card_wavefront_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_isapnp[nr_dev]) == 2 && + get_option(&str,(int *)&snd_cs4232_pcm_port[nr_dev]) == 2 && + get_option(&str,&snd_cs4232_pcm_irq[nr_dev]) == 2 && + get_option(&str,(int *)&snd_cs4232_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_cs4232_mpu_irq[nr_dev]) == 2 && + get_option(&str,(int *)&snd_ics2115_port[nr_dev]) == 2 && + get_option(&str,&snd_ics2115_irq[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2 && + get_option(&str,&snd_use_cs4232_midi[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-wavefront=", alsa_card_wavefront_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/wavefront/wavefront_fx.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1052 @@ +/* + * Copyright (c) 1998-2002 by Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +MODULE_AUTHOR("Paul Davis "); +MODULE_DESCRIPTION("ALSA driver for Turtle Beach Tropez+ YSS225 FX Processor"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#endif + +/* Control bits for the Load Control Register + */ + +#define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ +#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ +#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ + +static inline void +dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +static int +wavefront_fx_idle (snd_wavefront_t *dev) + +{ + int i; + unsigned int x = 0x80; + + for (i = 0; i < 1000; i++) { + x = inb (dev->fx_status); + if ((x & 0x80) == 0) { + break; + } + } + + if (x & 0x80) { + snd_printk ("FX device never idle.\n"); + return 0; + } + + return (1); +} + +static void +wavefront_fx_mute (snd_wavefront_t *dev, int onoff) + +{ + if (!wavefront_fx_idle(dev)) { + return; + } + + outb (onoff ? 0x02 : 0x00, dev->fx_op); +} + +static int +wavefront_fx_memset (snd_wavefront_t *dev, + int page, + int addr, + int cnt, + unsigned short *data) +{ + if (page < 0 || page > 7) { + snd_printk ("FX memset: " + "page must be >= 0 and <= 7\n"); + return -(EINVAL); + } + + if (addr < 0 || addr > 0x7f) { + snd_printk ("FX memset: " + "addr must be >= 0 and <= 7f\n"); + return -(EINVAL); + } + + if (cnt == 1) { + + outb (FX_LSB_TRANSFER, dev->fx_lcr); + outb (page, dev->fx_dsp_page); + outb (addr, dev->fx_dsp_addr); + outb ((data[0] >> 8), dev->fx_dsp_msb); + outb ((data[0] & 0xff), dev->fx_dsp_lsb); + + snd_printk ("FX: addr %d:%x set to 0x%x\n", + page, addr, data[0]); + + } else { + int i; + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (page, dev->fx_dsp_page); + outb (addr, dev->fx_dsp_addr); + + for (i = 0; i < cnt; i++) { + outb ((data[i] >> 8), dev->fx_dsp_msb); + outb ((data[i] & 0xff), dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) { + break; + } + } + + if (i != cnt) { + snd_printk ("FX memset " + "(0x%x, 0x%x, 0x%lx, %d) incomplete\n", + page, addr, (unsigned long) data, cnt); + return -(EIO); + } + } + + return 0; +} + +int +snd_wavefront_fx_detect (snd_wavefront_t *dev) + +{ + /* This is a crude check, but its the best one I have for now. + Certainly on the Maui and the Tropez, wavefront_fx_idle() will + report "never idle", which suggests that this test should + work OK. + */ + + if (inb (dev->fx_status) & 0x80) { + snd_printk ("Hmm, probably a Maui or Tropez.\n"); + return -1; + } + + return 0; +} + +int +snd_wavefront_fx_open (snd_hwdep_t *hw, struct file *file) + +{ + MOD_INC_USE_COUNT; + if (!try_inc_mod_count(hw->card->module)) { + MOD_DEC_USE_COUNT; + return -EFAULT; + } + file->private_data = hw; + return 0; +} + +int +snd_wavefront_fx_release (snd_hwdep_t *hw, struct file *file) + +{ + dec_mod_count(hw->card->module); + MOD_DEC_USE_COUNT; + return 0; +} + +int +snd_wavefront_fx_ioctl (snd_hwdep_t *sdev, struct file *file, + unsigned int cmd, unsigned long arg) + +{ + snd_card_t *card; + snd_wavefront_card_t *acard; + snd_wavefront_t *dev; + wavefront_fx_info r; + unsigned short page_data[256]; + unsigned short *pd; + + snd_assert(sdev->card != NULL, return -ENODEV); + + card = sdev->card; + + snd_assert(card->private_data != NULL, return -ENODEV); + + acard = card->private_data; + dev = &acard->wavefront; + + if (copy_from_user (&r, (unsigned char *) arg, sizeof (wavefront_fx_info))) + return -EFAULT; + + switch (r.request) { + case WFFX_MUTE: + wavefront_fx_mute (dev, r.data[0]); + return -EIO; + + case WFFX_MEMSET: + if (r.data[2] <= 0) { + snd_printk ("cannot write " + "<= 0 bytes to FX\n"); + return -EIO; + } else if (r.data[2] == 1) { + pd = (unsigned short *) &r.data[3]; + } else { + if (r.data[2] > sizeof (page_data)) { + snd_printk ("cannot write " + "> 255 bytes to FX\n"); + return -EIO; + } + if (copy_from_user (page_data, + (unsigned char *) r.data[3], + r.data[2])) + return -EFAULT; + pd = page_data; + } + + wavefront_fx_memset (dev, + r.data[0], /* page */ + r.data[1], /* addr */ + r.data[2], /* cnt */ + pd); + break; + + default: + snd_printk ("FX: ioctl %d not yet supported\n", + r.request); + return -ENOTTY; + } + return 0; +} + +/* YSS225 initialization. + + This code was developed using DOSEMU. The Turtle Beach SETUPSND + utility was run with I/O tracing in DOSEMU enabled, and a reconstruction + of the port I/O done, using the Yamaha faxback document as a guide + to add more logic to the code. Its really pretty wierd. + + There was an alternative approach of just dumping the whole I/O + sequence as a series of port/value pairs and a simple loop + that output it. However, I hope that eventually I'll get more + control over what this code does, and so I tried to stick with + a somewhat "algorithmic" approach. +*/ + + +int +snd_wavefront_fx_start (snd_wavefront_t *dev) + +{ + unsigned int i, j; + + /* Set all bits for all channels on the MOD unit to zero */ + /* XXX But why do this twice ? */ + + for (j = 0; j < 2; j++) { + for (i = 0x10; i <= 0xff; i++) { + + if (!wavefront_fx_idle (dev)) { + return (-1); + } + + outb (i, dev->fx_mod_addr); + outb (0x0, dev->fx_mod_data); + } + } + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x02, dev->fx_op); /* mute on */ + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x44, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x42, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x43, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x7c, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x7e, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x46, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x49, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x47, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x4a, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + + /* either because of stupidity by TB's programmers, or because it + actually does something, rezero the MOD page. + */ + for (i = 0x10; i <= 0xff; i++) { + + if (!wavefront_fx_idle (dev)) { + return (-1); + } + + outb (i, dev->fx_mod_addr); + outb (0x0, dev->fx_mod_data); + } + /* load page zero */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x00, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero); i += 2) { + outb (page_zero[i], dev->fx_dsp_msb); + outb (page_zero[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Now load page one */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x01, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one); i += 2) { + outb (page_one[i], dev->fx_dsp_msb); + outb (page_one[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x02, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two); i++) { + outb (page_two[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x03, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three); i++) { + outb (page_three[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x04, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four); i++) { + outb (page_four[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Load memory area (page six) */ + + outb (FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x06, dev->fx_dsp_page); + + for (i = 0; i < sizeof (page_six); i += 3) { + outb (page_six[i], dev->fx_dsp_addr); + outb (page_six[i+1], dev->fx_dsp_msb); + outb (page_six[i+2], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x07, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven); i += 2) { + outb (page_seven[i], dev->fx_dsp_msb); + outb (page_seven[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Now setup the MOD area. We do this algorithmically in order to + save a little data space. It could be done in the same fashion + as the "pages". + */ + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev->fx_mod_addr); + outb (i, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x02, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0xb0; i <= 0xbf; i++) { + outb (i, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0xf0; i <= 0xff; i++) { + outb (i, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x10; i <= 0x1d; i++) { + outb (i, dev->fx_mod_addr); + outb (0xff, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x1e, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x1f; i <= 0x2d; i++) { + outb (i, dev->fx_mod_addr); + outb (0xff, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x2e, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x2f; i <= 0x3e; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x3f, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x40; i <= 0x4d; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x4e, dev->fx_mod_addr); + outb (0x0e, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x4f, dev->fx_mod_addr); + outb (0x0e, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + + for (i = 0x50; i <= 0x6b; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x6c, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (0x6d, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (0x6e, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (0x6f, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x70; i <= 0x7f; i++) { + outb (i, dev->fx_mod_addr); + outb (0xc0, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x80; i <= 0xaf; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0xc0; i <= 0xdd; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0xde, dev->fx_mod_addr); + outb (0x10, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0xdf, dev->fx_mod_addr); + outb (0x10, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0xe0; i <= 0xef; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev->fx_mod_addr); + outb (i, dev->fx_mod_data); + outb (0x02, dev->fx_mod_addr); + outb (0x01, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x02, dev->fx_op); /* mute on */ + + /* Now set the coefficients and so forth for the programs above */ + + for (i = 0; i < sizeof (coefficients); i += 4) { + outb (coefficients[i], dev->fx_dsp_page); + outb (coefficients[i+1], dev->fx_dsp_addr); + outb (coefficients[i+2], dev->fx_dsp_msb); + outb (coefficients[i+3], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Some settings (?) that are too small to bundle into loops */ + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x1e, dev->fx_mod_addr); + outb (0x14, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0xde, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0xdf, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + + /* some more coefficients */ + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x06, dev->fx_dsp_page); + outb (0x78, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x40, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x03, dev->fx_dsp_addr); + outb (0x0f, dev->fx_dsp_msb); + outb (0xff, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x0b, dev->fx_dsp_addr); + outb (0x0f, dev->fx_dsp_msb); + outb (0xff, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x02, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x0a, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x46, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x49, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + + /* Now, for some strange reason, lets reload every page + and all the coefficients over again. I have *NO* idea + why this is done. I do know that no sound is produced + is this phase is omitted. + */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x00, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero_v2); i += 2) { + outb (page_zero_v2[i], dev->fx_dsp_msb); + outb (page_zero_v2[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x01, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one_v2); i += 2) { + outb (page_one_v2[i], dev->fx_dsp_msb); + outb (page_one_v2[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + if (!wavefront_fx_idle (dev)) return (-1); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x02, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two_v2); i++) { + outb (page_two_v2[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x03, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three_v2); i++) { + outb (page_three_v2[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x04, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four_v2); i++) { + outb (page_four_v2[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x06, dev->fx_dsp_page); + + /* Page six v.2 is algorithmic */ + + for (i = 0x10; i <= 0x3e; i += 2) { + outb (i, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x07, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven_v2); i += 2) { + outb (page_seven_v2[i], dev->fx_dsp_msb); + outb (page_seven_v2[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x00; i < sizeof(mod_v2); i += 2) { + outb (mod_v2[i], dev->fx_mod_addr); + outb (mod_v2[i+1], dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0; i < sizeof (coefficients2); i += 4) { + outb (coefficients2[i], dev->fx_dsp_page); + outb (coefficients2[i+1], dev->fx_dsp_addr); + outb (coefficients2[i+2], dev->fx_dsp_msb); + outb (coefficients2[i+3], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0; i < sizeof (coefficients3); i += 2) { + int x; + + outb (0x07, dev->fx_dsp_page); + x = (i % 4) ? 0x4e : 0x4c; + outb (x, dev->fx_dsp_addr); + outb (coefficients3[i], dev->fx_dsp_msb); + outb (coefficients3[i+1], dev->fx_dsp_lsb); + } + + outb (0x00, dev->fx_op); /* mute off */ + if (!wavefront_fx_idle (dev)) return (-1); + + return (0); +} + +/* wierd stuff, derived from port I/O tracing with dosemu */ + +static unsigned char page_zero[] __initdata = { +0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, +0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, +0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, +0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, +0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, +0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, +0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, +0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, +0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, +0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, +0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, +0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, +0x1d, 0x02, 0xdf +}; + +static unsigned char page_one[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, +0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, +0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, +0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, +0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, +0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, +0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, +0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, +0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, +0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, +0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, +0x60, 0x00, 0x1b +}; + +static unsigned char page_two[] __initdata = { +0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, +0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, +0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, +0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, +0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, +0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, +0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, +0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 +}; + +static unsigned char page_three[] __initdata = { +0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, +0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, +0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, +0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, +0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, +0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 +}; + +static unsigned char page_four[] __initdata = { +0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, +0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, +0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, +0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, +0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 +}; + +static unsigned char page_six[] __initdata = { +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, +0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, +0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, +0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, +0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, +0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, +0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, +0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, +0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, +0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, +0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, +0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, +0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, +0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, +0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, +0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, +0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, +0x80, 0x00, 0x7e, 0x80, 0x80 +}; + +static unsigned char page_seven[] __initdata = { +0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, +0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, +0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, +0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, +0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, +0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, +0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, +0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, +0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, +0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, +0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, +0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x02, 0x00 +}; + +static unsigned char page_zero_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char page_one_v2[] __initdata = { +0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char page_two_v2[] __initdata = { +0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +static unsigned char page_three_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +static unsigned char page_four_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char page_seven_v2[] __initdata = { +0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char mod_v2[] __initdata = { +0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, +0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, +0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, +0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, +0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, +0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, +0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, +0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, +0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, +0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, +0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, +0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, +0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, +0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, +0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, +0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, +0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, +0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, +0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, +0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, +0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, +0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, +0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, +0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, +0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, +0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, +0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, +0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 +}; +static unsigned char coefficients[] __initdata = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, +0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, +0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, +0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, +0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, +0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, +0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, +0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, +0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, +0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, +0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, +0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, +0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, +0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, +0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, +0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, +0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, +0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, +0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, +0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, +0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, +0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, +0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, +0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, +0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, +0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, +0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, +0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, +0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, +0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, +0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, +0xba +}; +static unsigned char coefficients2[] __initdata = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, +0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, +0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, +0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, +0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 +}; +static unsigned char coefficients3[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, +0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, +0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, +0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, +0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, +0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, +0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, +0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, +0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, +0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, +0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, +0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, +0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, +0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, +0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, +0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, +0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, +0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, +0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, +0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, +0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, +0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, +0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, +0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, +0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, +0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, +0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, +0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, +0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, +0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, +0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, +0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, +0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, +0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, +0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, +0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, +0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff +}; + +#if 0 +EXPORT_SYMBOL(snd_wavefront_fx_start); +EXPORT_SYMBOL(snd_wavefront_fx_detect); +EXPORT_SYMBOL(snd_wavefront_fx_ioctl); +EXPORT_SYMBOL(snd_wavefront_fx_open); +EXPORT_SYMBOL(snd_wavefront_fx_release); + +static int __init alsa_wavefront_fx_init(void) +{ + return 0; +} + +static void __exit alsa_wavefront_fx_exit(void) +{ +} + +module_init(alsa_wavefront_fx_init) +module_exit(alsa_wavefront_fx_exit) +#endif diff -Nru a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/wavefront/wavefront_midi.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,574 @@ +/* + * Copyright (C) by Paul Barton-Davis 1998-1999 + * + * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this + * software for more info. + */ + +/* The low level driver for the WaveFront ICS2115 MIDI interface(s) + * + * Note that there is also an MPU-401 emulation (actually, a UART-401 + * emulation) on the CS4232 on the Tropez and Tropez Plus. This code + * has nothing to do with that interface at all. + * + * The interface is essentially just a UART-401, but is has the + * interesting property of supporting what Turtle Beach called + * "Virtual MIDI" mode. In this mode, there are effectively *two* + * MIDI buses accessible via the interface, one that is routed + * solely to/from the external WaveFront synthesizer and the other + * corresponding to the pin/socket connector used to link external + * MIDI devices to the board. + * + * This driver fully supports this mode, allowing two distinct MIDI + * busses to be used completely independently, giving 32 channels of + * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI + * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1, + * where `n' is the card number. Note that the device numbers may be + * something other than 0 and 1 if the CS4232 UART/MPU-401 interface + * is enabled. + * + * Switching between the two is accomplished externally by the driver + * using the two otherwise unused MIDI bytes. See the code for more details. + * + * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c) + * + * The main reason to turn off Virtual MIDI mode is when you want to + * tightly couple the WaveFront synth with an external MIDI + * device. You won't be able to distinguish the source of any MIDI + * data except via SysEx ID, but thats probably OK, since for the most + * part, the WaveFront won't be sending any MIDI data at all. + * + * The main reason to turn on Virtual MIDI Mode is to provide two + * completely independent 16-channel MIDI buses, one to the + * WaveFront and one to any external MIDI devices. Given the 32 + * voice nature of the WaveFront, its pretty easy to find a use + * for all 16 channels driving just that synth. + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +static inline int +wf_mpu_status (snd_wavefront_midi_t *midi) + +{ + return inb (midi->mpu_status_port); +} + +static inline int +input_avail (snd_wavefront_midi_t *midi) + +{ + return !(wf_mpu_status(midi) & INPUT_AVAIL); +} + +static inline int +output_ready (snd_wavefront_midi_t *midi) + +{ + return !(wf_mpu_status(midi) & OUTPUT_READY); +} + +static inline int +read_data (snd_wavefront_midi_t *midi) + +{ + return inb (midi->mpu_data_port); +} + +static inline void +write_data (snd_wavefront_midi_t *midi, unsigned char byte) + +{ + outb (byte, midi->mpu_data_port); +} + +static snd_wavefront_midi_t * +get_wavefront_midi (snd_rawmidi_substream_t *substream) + +{ + snd_card_t *card; + snd_wavefront_card_t *acard; + + if (substream == NULL || substream->rmidi == NULL) + return NULL; + + card = substream->rmidi->card; + + if (card == NULL) + return NULL; + + if (card->private_data == NULL) + return NULL; + + acard = card->private_data; + + return &acard->wavefront.midi; +} + +static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card) +{ + snd_wavefront_midi_t *midi = &card->wavefront.midi; + snd_wavefront_mpu_id mpu; + unsigned long flags; + unsigned char midi_byte; + int max = 256, mask = 1; + int timeout; + + /* Its not OK to try to change the status of "virtuality" of + the MIDI interface while we're outputting stuff. See + snd_wavefront_midi_{enable,disable}_virtual () for the + other half of this. + + The first loop attempts to flush any data from the + current output device, and then the second + emits the switch byte (if necessary), and starts + outputting data for the output device currently in use. + */ + + if (midi->substream_output[midi->output_mpu] == NULL) { + goto __second; + } + + while (max > 0) { + + /* XXX fix me - no hard timing loops allowed! */ + + for (timeout = 30000; timeout > 0; timeout--) { + if (output_ready (midi)) + break; + } + + spin_lock_irqsave (&midi->virtual, flags); + if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) { + spin_unlock_irqrestore (&midi->virtual, flags); + goto __second; + } + if (output_ready (midi)) { + if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) { + if (!midi->isvirtual || + (midi_byte != WF_INTERNAL_SWITCH && + midi_byte != WF_EXTERNAL_SWITCH)) + write_data(midi, midi_byte); + max--; + } else { + if (midi->istimer) { + if (--midi->istimer <= 0) + del_timer(&midi->timer); + } + midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + spin_unlock_irqrestore (&midi->virtual, flags); + goto __second; + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + spin_unlock_irqrestore (&midi->virtual, flags); + } + + __second: + + if (midi->substream_output[!midi->output_mpu] == NULL) { + return; + } + + while (max > 0) { + + /* XXX fix me - no hard timing loops allowed! */ + + for (timeout = 30000; timeout > 0; timeout--) { + if (output_ready (midi)) + break; + } + + spin_lock_irqsave (&midi->virtual, flags); + if (!midi->isvirtual) + mask = 0; + mpu = midi->output_mpu ^ mask; + mask = 0; /* don't invert the value from now */ + if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + if (snd_rawmidi_transmit_empty(midi->substream_output[mpu])) + goto __timer; + if (output_ready (midi)) { + if (mpu != midi->output_mpu) { + write_data(midi, mpu == internal_mpu ? + WF_INTERNAL_SWITCH : + WF_EXTERNAL_SWITCH); + midi->output_mpu = mpu; + } else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) { + if (!midi->isvirtual || + (midi_byte != WF_INTERNAL_SWITCH && + midi_byte != WF_EXTERNAL_SWITCH)) + write_data(midi, midi_byte); + max--; + } else { + __timer: + if (midi->istimer) { + if (--midi->istimer <= 0) + del_timer(&midi->timer); + } + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + spin_unlock_irqrestore (&midi->virtual, flags); + } +} + +static int snd_wavefront_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] |= MPU401_MODE_INPUT; + midi->substream_input[mpu] = substream; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] |= MPU401_MODE_OUTPUT; + midi->substream_output[mpu] = substream; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] &= ~MPU401_MODE_INPUT; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT; + spin_unlock_irqrestore (&midi->open, flags); + return 0; +} + +static void snd_wavefront_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (substream == NULL || substream->rmidi == NULL) + return; + + if (substream->rmidi->private_data == NULL) + return; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) { + return; + } + + spin_lock_irqsave (&midi->virtual, flags); + if (up) { + midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER; + } else { + midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER; + } + spin_unlock_irqrestore (&midi->virtual, flags); +} + +static void snd_wavefront_midi_output_timer(unsigned long data) +{ + snd_wavefront_card_t *card = (snd_wavefront_card_t *)data; + snd_wavefront_midi_t *midi = &card->wavefront.midi; + unsigned long flags; + + spin_lock_irqsave (&midi->virtual, flags); + midi->timer.expires = 1 + jiffies; + add_timer(&midi->timer); + spin_unlock_irqrestore (&midi->virtual, flags); + snd_wavefront_midi_output_write(card); +} + +static void snd_wavefront_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (substream == NULL || substream->rmidi == NULL) + return; + + if (substream->rmidi->private_data == NULL) + return; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) { + return; + } + + spin_lock_irqsave (&midi->virtual, flags); + if (up) { + if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) { + if (!midi->istimer) { + midi->timer.function = snd_wavefront_midi_output_timer; + midi->timer.data = (unsigned long) substream->rmidi->card->private_data; + midi->timer.expires = 1 + jiffies; + add_timer(&midi->timer); + } + midi->istimer++; + midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER; + } + } else { + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + } + spin_unlock_irqrestore (&midi->virtual, flags); + + if (up) + snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data); +} + +void +snd_wavefront_midi_interrupt (snd_wavefront_card_t *card) + +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + static snd_rawmidi_substream_t *substream = NULL; + static int mpu = external_mpu; + int max = 128; + unsigned char byte; + + midi = &card->wavefront.midi; + + if (!input_avail (midi)) { /* not for us */ + snd_wavefront_midi_output_write(card); + return; + } + + while (--max) { + spin_lock_irqsave (&midi->virtual, flags); + + if (input_avail (midi)) { + byte = read_data (midi); + + if (midi->isvirtual) { + if (byte == WF_EXTERNAL_SWITCH) { + substream = midi->substream_input[external_mpu]; + mpu = external_mpu; + } else if (byte == WF_INTERNAL_SWITCH) { + substream = midi->substream_output[internal_mpu]; + mpu = internal_mpu; + } /* else just leave it as it is */ + } else { + substream = midi->substream_input[internal_mpu]; + mpu = internal_mpu; + } + + if (substream == NULL) { + spin_unlock_irqrestore (&midi->virtual, flags); + continue; + } + + if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) { + spin_unlock_irqrestore (&midi->virtual, flags); + snd_rawmidi_receive(substream, &byte, 1); + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + break; + } + } + + snd_wavefront_midi_output_write(card); +} + +void +snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card) + +{ + unsigned long flags; + + spin_lock_irqsave (&card->wavefront.midi.virtual, flags); + card->wavefront.midi.isvirtual = 1; + card->wavefront.midi.output_mpu = internal_mpu; + card->wavefront.midi.input_mpu = internal_mpu; + spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); +} + +void +snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card) + +{ + unsigned long flags; + + spin_lock_irqsave (&card->wavefront.midi.virtual, flags); + // snd_wavefront_midi_input_close (card->ics2115_external_rmidi); + // snd_wavefront_midi_output_close (card->ics2115_external_rmidi); + card->wavefront.midi.isvirtual = 0; + spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); +} + +int +snd_wavefront_midi_start (snd_wavefront_card_t *card) + +{ + int ok, i; + unsigned char rbuf[4], wbuf[4]; + snd_wavefront_t *dev; + snd_wavefront_midi_t *midi; + + dev = &card->wavefront; + midi = &dev->midi; + + /* The ICS2115 MPU-401 interface doesn't do anything + until its set into UART mode. + */ + + /* XXX fix me - no hard timing loops allowed! */ + + for (i = 0; i < 30000 && !output_ready (midi); i++); + + if (!output_ready (midi)) { + snd_printk ("MIDI interface not ready for command\n"); + return -1; + } + + /* Any interrupts received from now on + are owned by the MIDI side of things. + */ + + dev->interrupts_are_midi = 1; + + outb (UART_MODE_ON, midi->mpu_command_port); + + for (ok = 0, i = 50000; i > 0 && !ok; i--) { + if (input_avail (midi)) { + if (read_data (midi) == MPU_ACK) { + ok = 1; + break; + } + } + } + + if (!ok) { + snd_printk ("cannot set UART mode for MIDI interface"); + dev->interrupts_are_midi = 0; + return -1; + } + + /* Route external MIDI to WaveFront synth (by default) */ + + if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) { + snd_printk ("can't enable MIDI-IN-2-synth routing.\n"); + /* XXX error ? */ + } + + /* Turn on Virtual MIDI, but first *always* turn it off, + since otherwise consectutive reloads of the driver will + never cause the hardware to generate the initial "internal" or + "external" source bytes in the MIDI data stream. This + is pretty important, since the internal hardware generally will + be used to generate none or very little MIDI output, and + thus the only source of MIDI data is actually external. Without + the switch bytes, the driver will think it all comes from + the internal interface. Duh. + */ + + if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) { + snd_printk ("virtual MIDI mode not disabled\n"); + return 0; /* We're OK, but missing the external MIDI dev */ + } + + snd_wavefront_midi_enable_virtual (card); + + if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) { + snd_printk ("cannot enable virtual MIDI mode.\n"); + snd_wavefront_midi_disable_virtual (card); + } + return 0; +} + +snd_rawmidi_ops_t snd_wavefront_midi_output = +{ + open: snd_wavefront_midi_output_open, + close: snd_wavefront_midi_output_close, + trigger: snd_wavefront_midi_output_trigger, +}; + +snd_rawmidi_ops_t snd_wavefront_midi_input = +{ + open: snd_wavefront_midi_input_open, + close: snd_wavefront_midi_input_close, + trigger: snd_wavefront_midi_input_trigger, +}; + diff -Nru a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/isa/wavefront/wavefront_synth.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2275 @@ +/* Copyright (C) by Paul Barton-Davis 1998-1999 + * + * Some portions of this file are taken from work that is + * copyright (C) by Hannu Savolainen 1993-1996 + * + * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +/* + * An ALSA lowlevel driver for Turtle Beach ICS2115 wavetable synth + * (Maui, Tropez, Tropez Plus) + * + * This driver supports the onboard wavetable synthesizer (an ICS2115), + * including patch, sample and program loading and unloading, conversion + * of GUS patches during loading, and full user-level access to all + * WaveFront commands. It tries to provide semi-intelligent patch and + * sample management as well. + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int wf_raw = 0; /* we normally check for "raw state" to firmware + loading. if non-zero, then during driver loading, the + state of the board is ignored, and we reset the + board and load the firmware anyway. + */ + +int fx_raw = 1; /* if this is zero, we'll leave the FX processor in + whatever state it is when the driver is loaded. + The default is to download the microprogram and + associated coefficients to set it up for "default" + operation, whatever that means. + */ + +int debug_default = 0; /* you can set this to control debugging + during driver loading. it takes any combination + of the WF_DEBUG_* flags defined in + wavefront.h + */ + +/* XXX this needs to be made firmware and hardware version dependent */ + +char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed + version of the WaveFront OS + */ + +int wait_usecs = 150; /* This magic number seems to give pretty optimal + throughput based on my limited experimentation. + If you want to play around with it and find a better + value, be my guest. Remember, the idea is to + get a number that causes us to just busy wait + for as many WaveFront commands as possible, without + coming up with a number so large that we hog the + whole CPU. + + Specifically, with this number, out of about 134,000 + status waits, only about 250 result in a sleep. + */ + +int sleep_interval = 100; /* HZ/sleep_interval seconds per sleep */ +int sleep_tries = 50; /* number of times we'll try to sleep */ + +int reset_time = 2; /* hundreths of a second we wait after a HW + reset for the expected interrupt. + */ + +int ramcheck_time = 20; /* time in seconds to wait while ROM code + checks on-board RAM. + */ + +int osrun_time = 10; /* time in seconds we wait for the OS to + start running. + */ +#if 0 +MODULE_AUTHOR("Paul Barton-Davis "); +MODULE_DESCRIPTION("ALSA driver for Turtle Beach WaveFront ICS2215 Synth"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#endif +MODULE_PARM(wf_raw,"i"); +MODULE_PARM_DESC(wf_raw, "if non-zero, assume that we need to boot the OS"); +MODULE_PARM(fx_raw,"i"); +MODULE_PARM_DESC(fx_raw, "if non-zero, assume that the FX process needs help"); +MODULE_PARM(debug_default,"i"); +MODULE_PARM_DESC(debug_default, "debug parameters for card initialization"); +MODULE_PARM(wait_usecs,"i"); +MODULE_PARM_DESC(wait_usecs, "how long to wait without sleeping, usecs"); +MODULE_PARM(sleep_interval,"i"); +MODULE_PARM_DESC(sleep_interval, "how long to sleep when waiting for reply"); +MODULE_PARM(sleep_tries,"i"); +MODULE_PARM_DESC(sleep_tries, "how many times to try sleeping during a wait"); +MODULE_PARM(ospath,"s"); +MODULE_PARM_DESC(ospath, "full pathname to processed ICS2115 OS firmware"); +MODULE_PARM(reset_time,"i"); +MODULE_PARM_DESC(reset_time, "how long to wait for a reset to take effect"); +MODULE_PARM(ramcheck_time,"i"); +MODULE_PARM_DESC(ramcheck_time, "how many seconds to wait for the RAM test"); +MODULE_PARM(osrun_time,"i"); +MODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS"); + +/* + * This sucks, hopefully it'll get standardised + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,18) && LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define loops_per_sec loops_per_jiffy*HZ +#elif LINUX_VERSION_CODE == KERNEL_VERSION(2,4,0) && defined(I_DIRTY_PAGES) /* linux/fs.h */ +#define loops_per_sec loops_per_jiffy*HZ +#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0) +#define loops_per_sec loops_per_jiffy*HZ +#endif + +#if defined(__alpha__) || defined(__powerpc__) +#ifdef __SMP__ +#define LOOPS_PER_SEC (cpu_data[smp_processor_id()].loops_per_sec) +#else +#define LOOPS_PER_SEC (loops_per_sec) +#endif +#endif + +#if defined(__i386__) +#define LOOPS_PER_SEC (current_cpu_data.loops_per_sec) +#endif + +/* if WF_DEBUG not defined, no run-time debugging messages will + be available via the debug flag setting. Given the current + beta state of the driver, this will remain set until a future + version. +*/ + +#define WF_DEBUG 1 + +#ifdef WF_DEBUG + +#ifdef NEW_MACRO_VARARGS +#define DPRINT(cond, ...) \ + if ((dev->debug & (cond)) == (cond)) { \ + snd_printk (__VA_ARGS__); \ + } +#else +#define DPRINT(cond, args...) \ + if ((dev->debug & (cond)) == (cond)) { \ + snd_printk (##args); \ + } +#endif +#else +#define DPRINT(cond, args...) +#endif /* WF_DEBUG */ + +#define LOGNAME "WaveFront: " + +/* bitmasks for WaveFront status port value */ + +#define STAT_RINTR_ENABLED 0x01 +#define STAT_CAN_READ 0x02 +#define STAT_INTR_READ 0x04 +#define STAT_WINTR_ENABLED 0x10 +#define STAT_CAN_WRITE 0x20 +#define STAT_INTR_WRITE 0x40 + +static int wavefront_delete_sample (snd_wavefront_t *, int sampnum); +static int wavefront_find_free_sample (snd_wavefront_t *); + +typedef struct { + int cmd; + char *action; + unsigned int read_cnt; + unsigned int write_cnt; + int need_ack; +} wavefront_command; + +static struct { + int errno; + const char *errstr; +} wavefront_errors[] = { + { 0x01, "Bad sample number" }, + { 0x02, "Out of sample memory" }, + { 0x03, "Bad patch number" }, + { 0x04, "Error in number of voices" }, + { 0x06, "Sample load already in progress" }, + { 0x0B, "No sample load request pending" }, + { 0x0E, "Bad MIDI channel number" }, + { 0x10, "Download Record Error" }, + { 0x80, "Success" }, + { 0x0, 0x0 } +}; + +#define NEEDS_ACK 1 + +static wavefront_command wavefront_commands[] = { + { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, + { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, + { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, + { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, + { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, + { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, + { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, + { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, + { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, + { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, + { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, + { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, + { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, + { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, + { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, + { WFC_DOWNLOAD_SAMPLE, "download sample", + 0, WF_SAMPLE_BYTES, NEEDS_ACK }, + { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, + { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", + 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, + + /* This command requires a variable number of bytes to be written. + There is a hack in snd_wavefront_cmd() to support this. The actual + count is passed in as the read buffer ptr, cast appropriately. + Ugh. + */ + + { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, + + /* This one is a hack as well. We just read the first byte of the + response, don't fetch an ACK, and leave the rest to the + calling function. Ugly, ugly, ugly. + */ + + { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, + { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", + 0, WF_ALIAS_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, + { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, + { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, + { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, + { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, + { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, + { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, + { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, + { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, + { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, + NEEDS_ACK}, + { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, + { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", + 0, 1, NEEDS_ACK }, + { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, + { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", + 32, 0, 0 }, + { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, + { 0x00 } +}; + +static inline void +dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +static const char * +wavefront_errorstr (int errnum) + +{ + int i; + + for (i = 0; wavefront_errors[i].errstr; i++) { + if (wavefront_errors[i].errno == errnum) { + return wavefront_errors[i].errstr; + } + } + + return "Unknown WaveFront error"; +} + +static wavefront_command * +wavefront_get_command (int cmd) + +{ + int i; + + for (i = 0; wavefront_commands[i].cmd != 0; i++) { + if (cmd == wavefront_commands[i].cmd) { + return &wavefront_commands[i]; + } + } + + return (wavefront_command *) 0; +} + +static inline int +wavefront_status (snd_wavefront_t *dev) + +{ + return inb (dev->status_port); +} + +static int +wavefront_sleep (int limit) + +{ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(limit); + + return signal_pending(current); +} + +static int +wavefront_wait (snd_wavefront_t *dev, int mask) + +{ + int i; + static int short_loop_cnt = 0; + + /* Compute the loop count that lets us sleep for about the + right amount of time, cache issues, bus speeds and all + other issues being unequal but largely irrelevant. + */ + + if (short_loop_cnt == 0) { + short_loop_cnt = wait_usecs * + (LOOPS_PER_SEC / 1000000); + } + + /* Spin for a short period of time, because >99% of all + requests to the WaveFront can be serviced inline like this. + */ + + for (i = 0; i < short_loop_cnt; i++) { + if (wavefront_status (dev) & mask) { + return 1; + } + } + + for (i = 0; i < sleep_tries; i++) { + + if (wavefront_status (dev) & mask) { + return 1; + } + + if (wavefront_sleep (HZ/sleep_interval)) { + return (0); + } + } + + return (0); +} + +static int +wavefront_read (snd_wavefront_t *dev) + +{ + if (wavefront_wait (dev, STAT_CAN_READ)) + return inb (dev->data_port); + + DPRINT (WF_DEBUG_DATA, "read timeout.\n"); + + return -1; +} + +static int +wavefront_write (snd_wavefront_t *dev, unsigned char data) + +{ + if (wavefront_wait (dev, STAT_CAN_WRITE)) { + outb (data, dev->data_port); + return 0; + } + + DPRINT (WF_DEBUG_DATA, "write timeout.\n"); + + return -1; +} + +int +snd_wavefront_cmd (snd_wavefront_t *dev, + int cmd, unsigned char *rbuf, unsigned char *wbuf) + +{ + int ack; + unsigned int i; + int c; + wavefront_command *wfcmd; + + if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { + snd_printk ("command 0x%x not supported.\n", + cmd); + return 1; + } + + /* Hack to handle the one variable-size write command. See + wavefront_send_multisample() for the other half of this + gross and ugly strategy. + */ + + if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { + wfcmd->write_cnt = (unsigned long) rbuf; + rbuf = 0; + } + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + + if (wavefront_write (dev, cmd)) { + DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + } + + if (wfcmd->write_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "writing %d bytes " + "for 0x%x\n", + wfcmd->write_cnt, cmd); + + for (i = 0; i < wfcmd->write_cnt; i++) { + if (wavefront_write (dev, wbuf[i])) { + DPRINT (WF_DEBUG_IO, "bad write for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n", + i, wbuf[i]); + } + } + + if (wfcmd->read_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "reading %d ints " + "for 0x%x\n", + wfcmd->read_cnt, cmd); + + for (i = 0; i < wfcmd->read_cnt; i++) { + + if ((c = wavefront_read (dev)) == -1) { + DPRINT (WF_DEBUG_IO, "bad read for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + /* Now handle errors. Lots of special cases here */ + + if (c == 0xff) { + if ((c = wavefront_read (dev)) == -1) { + DPRINT (WF_DEBUG_IO, "bad read for " + "error byte at " + "read byte %d " + "of 0x%x [%s].\n", + i, cmd, + wfcmd->action); + return 1; + } + + /* Can you believe this madness ? */ + + if (c == 1 && + wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { + rbuf[0] = WF_ST_EMPTY; + return (0); + + } else if (c == 3 && + wfcmd->cmd == WFC_UPLOAD_PATCH) { + + return 3; + + } else if (c == 1 && + wfcmd->cmd == WFC_UPLOAD_PROGRAM) { + + return 1; + + } else { + + DPRINT (WF_DEBUG_IO, "error %d (%s) " + "during " + "read for byte " + "%d of 0x%x " + "[%s].\n", + c, + wavefront_errorstr (c), + i, cmd, + wfcmd->action); + return 1; + + } + + } else { + rbuf[i] = c; + } + + DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]); + } + } + + if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { + + DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd); + + /* Some commands need an ACK, but return zero instead + of the standard value. + */ + + if ((ack = wavefront_read (dev)) == 0) { + ack = WF_ACK; + } + + if (ack != WF_ACK) { + if (ack == -1) { + DPRINT (WF_DEBUG_IO, "cannot read ack for " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + + } else { + int err = -1; /* something unknown */ + + if (ack == 0xff) { /* explicit error */ + + if ((err = wavefront_read (dev)) == -1) { + DPRINT (WF_DEBUG_DATA, + "cannot read err " + "for 0x%x [%s].\n", + cmd, wfcmd->action); + } + } + + DPRINT (WF_DEBUG_IO, "0x%x [%s] " + "failed (0x%x, 0x%x, %s)\n", + cmd, wfcmd->action, ack, err, + wavefront_errorstr (err)); + + return -err; + } + } + + DPRINT (WF_DEBUG_DATA, "ack received " + "for 0x%x [%s]\n", + cmd, wfcmd->action); + } else { + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need " + "ACK (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + } + + return 0; + +} + +/*********************************************************************** +WaveFront data munging + +Things here are wierd. All data written to the board cannot +have its most significant bit set. Any data item with values +potentially > 0x7F (127) must be split across multiple bytes. + +Sometimes, we need to munge numeric values that are represented on +the x86 side as 8-32 bit values. Sometimes, we need to munge data +that is represented on the x86 side as an array of bytes. The most +efficient approach to handling both cases seems to be to use 2 +different functions for munging and 2 for de-munging. This avoids +wierd casting and worrying about bit-level offsets. + +**********************************************************************/ + +static unsigned char * +munge_int32 (unsigned int src, + unsigned char *dst, + unsigned int dst_size) +{ + unsigned int i; + + for (i = 0; i < dst_size; i++) { + *dst = src & 0x7F; /* Mask high bit of LSB */ + src = src >> 7; /* Rotate Right 7 bits */ + /* Note: we leave the upper bits in place */ + + dst++; + }; + return dst; +}; + +static int +demunge_int32 (unsigned char* src, int src_size) + +{ + int i; + int outval = 0; + + for (i = src_size - 1; i >= 0; i--) { + outval=(outval<<7)+src[i]; + } + + return outval; +}; + +static +unsigned char * +munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) + +{ + unsigned int i; + unsigned int last = dst_size / 2; + + for (i = 0; i < last; i++) { + *dst++ = src[i] & 0x7f; + *dst++ = src[i] >> 7; + } + return dst; +} + +static +unsigned char * +demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) + +{ + int i; + unsigned char *end = src + src_bytes; + + end = src + src_bytes; + + /* NOTE: src and dst *CAN* point to the same address */ + + for (i = 0; src != end; i++) { + dst[i] = *src++; + dst[i] |= (*src++)<<7; + } + + return dst; +} + +/*********************************************************************** +WaveFront: sample, patch and program management. +***********************************************************************/ + +static int +wavefront_delete_sample (snd_wavefront_t *dev, int sample_num) + +{ + unsigned char wbuf[2]; + int x; + + wbuf[0] = sample_num & 0x7f; + wbuf[1] = sample_num >> 7; + + if ((x = snd_wavefront_cmd (dev, WFC_DELETE_SAMPLE, 0, wbuf)) == 0) { + dev->sample_status[sample_num] = WF_ST_EMPTY; + } + + return x; +} + +static int +wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom) + +{ + int i; + unsigned char rbuf[32], wbuf[32]; + unsigned int sc_real, sc_alias, sc_multi; + + /* check sample status */ + + if (snd_wavefront_cmd (dev, WFC_GET_NSAMPLES, rbuf, wbuf)) { + snd_printk ("cannot request sample count.\n"); + return -1; + } + + sc_real = sc_alias = sc_multi = dev->samples_used = 0; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + + wbuf[0] = i & 0x7f; + wbuf[1] = i >> 7; + + if (snd_wavefront_cmd (dev, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { + snd_printk("cannot identify sample " + "type of slot %d\n", i); + dev->sample_status[i] = WF_ST_EMPTY; + continue; + } + + dev->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); + + if (assume_rom) { + dev->sample_status[i] |= WF_SLOT_ROM; + } + + switch (rbuf[0] & WF_ST_MASK) { + case WF_ST_SAMPLE: + sc_real++; + break; + case WF_ST_MULTISAMPLE: + sc_multi++; + break; + case WF_ST_ALIAS: + sc_alias++; + break; + case WF_ST_EMPTY: + break; + + default: + snd_printk ("unknown sample type for " + "slot %d (0x%x)\n", + i, rbuf[0]); + } + + if (rbuf[0] != WF_ST_EMPTY) { + dev->samples_used++; + } + } + + snd_printk ("%d samples used (%d real, %d aliases, %d multi), " + "%d empty\n", dev->samples_used, sc_real, sc_alias, sc_multi, + WF_MAX_SAMPLE - dev->samples_used); + + + return (0); + +} + +static int +wavefront_get_patch_status (snd_wavefront_t *dev) + +{ + unsigned char patchbuf[WF_PATCH_BYTES]; + unsigned char patchnum[2]; + wavefront_patch *p; + int i, x, cnt, cnt2; + + for (i = 0; i < WF_MAX_PATCH; i++) { + patchnum[0] = i & 0x7f; + patchnum[1] = i >> 7; + + if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PATCH, patchbuf, + patchnum)) == 0) { + + dev->patch_status[i] |= WF_SLOT_FILLED; + p = (wavefront_patch *) patchbuf; + dev->sample_status + [p->sample_number|(p->sample_msb<<7)] |= + WF_SLOT_USED; + + } else if (x == 3) { /* Bad patch number */ + dev->patch_status[i] = 0; + } else { + snd_printk ("upload patch " + "error 0x%x\n", x); + dev->patch_status[i] = 0; + return 1; + } + } + + /* program status has already filled in slot_used bits */ + + for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { + if (dev->patch_status[i] & WF_SLOT_FILLED) { + cnt++; + } + if (dev->patch_status[i] & WF_SLOT_USED) { + cnt2++; + } + + } + snd_printk ("%d patch slots filled, %d in use\n", cnt, cnt2); + + return (0); +} + +static int +wavefront_get_program_status (snd_wavefront_t *dev) + +{ + unsigned char progbuf[WF_PROGRAM_BYTES]; + wavefront_program prog; + unsigned char prognum; + int i, x, l, cnt; + + for (i = 0; i < WF_MAX_PROGRAM; i++) { + prognum = i; + + if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PROGRAM, progbuf, + &prognum)) == 0) { + + dev->prog_status[i] |= WF_SLOT_USED; + + demunge_buf (progbuf, (unsigned char *) &prog, + WF_PROGRAM_BYTES); + + for (l = 0; l < WF_NUM_LAYERS; l++) { + if (prog.layer[l].mute) { + dev->patch_status + [prog.layer[l].patch_number] |= + WF_SLOT_USED; + } + } + } else if (x == 1) { /* Bad program number */ + dev->prog_status[i] = 0; + } else { + snd_printk ("upload program " + "error 0x%x\n", x); + dev->prog_status[i] = 0; + } + } + + for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { + if (dev->prog_status[i]) { + cnt++; + } + } + + snd_printk ("%d programs slots in use\n", cnt); + + return (0); +} + +static int +wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char buf[WF_PATCH_BYTES+2]; + unsigned char *bptr; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", + header->number); + + dev->patch_status[header->number] |= WF_SLOT_FILLED; + + bptr = buf; + bptr = munge_int32 (header->number, buf, 2); + munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, 0, buf)) { + snd_printk ("download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char buf[WF_PROGRAM_BYTES+1]; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", + header->number); + + dev->prog_status[header->number] = WF_SLOT_USED; + + /* XXX need to zero existing SLOT_USED bit for program_status[i] + where `i' is the program that's being (potentially) overwritten. + */ + + for (i = 0; i < WF_NUM_LAYERS; i++) { + if (header->hdr.pr.layer[i].mute) { + dev->patch_status[header->hdr.pr.layer[i].patch_number] |= + WF_SLOT_USED; + + /* XXX need to mark SLOT_USED for sample used by + patch_number, but this means we have to load it. Ick. + */ + } + } + + buf[0] = header->number; + munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, 0, buf)) { + snd_printk ("download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_freemem (snd_wavefront_t *dev) + +{ + char rbuf[8]; + + if (snd_wavefront_cmd (dev, WFC_REPORT_FREE_MEMORY, rbuf, 0)) { + snd_printk ("can't get memory stats.\n"); + return -1; + } else { + return demunge_int32 (rbuf, 4); + } +} + +static int +wavefront_send_sample (snd_wavefront_t *dev, + wavefront_patch_info *header, + u16 *dataptr, + int data_is_unsigned) + +{ + /* samples are downloaded via a 16-bit wide i/o port + (you could think of it as 2 adjacent 8-bit wide ports + but its less efficient that way). therefore, all + the blocksizes and so forth listed in the documentation, + and used conventionally to refer to sample sizes, + which are given in 8-bit units (bytes), need to be + divided by 2. + */ + + u16 sample_short; + u32 length; + u16 *data_end = 0; + unsigned int i; + const int max_blksize = 4096/2; + unsigned int written; + unsigned int blocksize; + int dma_ack; + int blocknum; + unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; + unsigned char *shptr; + int skip = 0; + int initial_skip = 0; + + DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, " + "type %d, %d bytes from 0x%lx\n", + header->size ? "" : "header ", + header->number, header->subkey, + header->size, + (unsigned long) header->dataptr); + + if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) { + int x; + + if ((x = wavefront_find_free_sample (dev)) < 0) { + return -ENOMEM; + } + snd_printk ("unspecified sample => %d\n", x); + header->number = x; + } + + if (header->size) { + + /* XXX its a debatable point whether or not RDONLY semantics + on the ROM samples should cover just the sample data or + the sample header. For now, it only covers the sample data, + so anyone is free at all times to rewrite sample headers. + + My reason for this is that we have the sample headers + available in the WFB file for General MIDI, and so these + can always be reset if needed. The sample data, however, + cannot be recovered without a complete reset and firmware + reload of the ICS2115, which is a very expensive operation. + + So, doing things this way allows us to honor the notion of + "RESETSAMPLES" reasonably cheaply. Note however, that this + is done purely at user level: there is no WFB parser in + this driver, and so a complete reset (back to General MIDI, + or theoretically some other configuration) is the + responsibility of the user level library. + + To try to do this in the kernel would be a little + crazy: we'd need 158K of kernel space just to hold + a copy of the patch/program/sample header data. + */ + + if (dev->rom_samples_rdonly) { + if (dev->sample_status[header->number] & WF_SLOT_ROM) { + snd_printk ("sample slot %d " + "write protected\n", + header->number); + return -EACCES; + } + } + + wavefront_delete_sample (dev, header->number); + } + + if (header->size) { + dev->freemem = wavefront_freemem (dev); + + if (dev->freemem < header->size) { + snd_printk ("insufficient memory to " + "load %d byte sample.\n", + header->size); + return -ENOMEM; + } + + } + + skip = WF_GET_CHANNEL(&header->hdr.s); + + if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { + snd_printk ("channel selection only " + "possible on 16-bit samples"); + return -(EINVAL); + } + + switch (skip) { + case 0: + initial_skip = 0; + skip = 1; + break; + case 1: + initial_skip = 0; + skip = 2; + break; + case 2: + initial_skip = 1; + skip = 2; + break; + case 3: + initial_skip = 2; + skip = 3; + break; + case 4: + initial_skip = 3; + skip = 4; + break; + case 5: + initial_skip = 4; + skip = 5; + break; + case 6: + initial_skip = 5; + skip = 6; + break; + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => " + "initial skip = %d, skip = %d\n", + WF_GET_CHANNEL (&header->hdr.s), + initial_skip, skip); + + /* Be safe, and zero the "Unused" bits ... */ + + WF_SET_CHANNEL(&header->hdr.s, 0); + + /* adjust size for 16 bit samples by dividing by two. We always + send 16 bits per write, even for 8 bit samples, so the length + is always half the size of the sample data in bytes. + */ + + length = header->size / 2; + + /* the data we're sent has not been munged, and in fact, the + header we have to send isn't just a munged copy either. + so, build the sample header right here. + */ + + shptr = &sample_hdr[0]; + + shptr = munge_int32 (header->number, shptr, 2); + + if (header->size) { + shptr = munge_int32 (length, shptr, 4); + } + + /* Yes, a 4 byte result doesn't contain all of the offset bits, + but the offset only uses 24 bits. + */ + + shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleStartOffset), + shptr, 4); + shptr = munge_int32 (*((u32 *) &header->hdr.s.loopStartOffset), + shptr, 4); + shptr = munge_int32 (*((u32 *) &header->hdr.s.loopEndOffset), + shptr, 4); + shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleEndOffset), + shptr, 4); + + /* This one is truly wierd. What kind of wierdo decided that in + a system dominated by 16 and 32 bit integers, they would use + a just 12 bits ? + */ + + shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); + + /* Why is this nybblified, when the MSB is *always* zero ? + Anyway, we can't take address of bitfield, so make a + good-faith guess at where it starts. + */ + + shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), + shptr, 2); + + if (snd_wavefront_cmd (dev, + header->size ? + WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, + 0, sample_hdr)) { + snd_printk ("sample %sdownload refused.\n", + header->size ? "" : "header "); + return -(EIO); + } + + if (header->size == 0) { + goto sent; /* Sorry. Just had to have one somewhere */ + } + + data_end = dataptr + length; + + /* Do any initial skip over an unused channel's data */ + + dataptr += initial_skip; + + for (written = 0, blocknum = 0; + written < length; written += max_blksize, blocknum++) { + + if ((length - written) > max_blksize) { + blocksize = max_blksize; + } else { + /* round to nearest 16-byte value */ + blocksize = ((length-written+7)&~0x7); + } + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, 0, 0)) { + snd_printk ("download block " + "request refused.\n"); + return -(EIO); + } + + for (i = 0; i < blocksize; i++) { + + if (dataptr < data_end) { + + __get_user (sample_short, dataptr); + dataptr += skip; + + if (data_is_unsigned) { /* GUS ? */ + + if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { + + /* 8 bit sample + resolution, sign + extend both bytes. + */ + + ((unsigned char*) + &sample_short)[0] += 0x7f; + ((unsigned char*) + &sample_short)[1] += 0x7f; + + } else { + + /* 16 bit sample + resolution, sign + extend the MSB. + */ + + sample_short += 0x7fff; + } + } + + } else { + + /* In padding section of final block: + + Don't fetch unsupplied data from + user space, just continue with + whatever the final value was. + */ + } + + if (i < blocksize - 1) { + outw (sample_short, dev->block_port); + } else { + outw (sample_short, dev->last_block_port); + } + } + + /* Get "DMA page acknowledge", even though its really + nothing to do with DMA at all. + */ + + if ((dma_ack = wavefront_read (dev)) != WF_DMA_ACK) { + if (dma_ack == -1) { + snd_printk ("upload sample " + "DMA ack timeout\n"); + return -(EIO); + } else { + snd_printk ("upload sample " + "DMA ack error 0x%x\n", + dma_ack); + return -(EIO); + } + } + } + + dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); + + /* Note, label is here because sending the sample header shouldn't + alter the sample_status info at all. + */ + + sent: + return (0); +} + +static int +wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char alias_hdr[WF_ALIAS_BYTES]; + + DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is " + "alias for %d\n", + header->number, + header->hdr.a.OriginalSample); + + munge_int32 (header->number, &alias_hdr[0], 2); + munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), + &alias_hdr[4], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), + &alias_hdr[8], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), + &alias_hdr[12], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), + &alias_hdr[16], 4); + munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); + munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) { + snd_printk ("download alias failed.\n"); + return -(EIO); + } + + dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); + + return (0); +} + +static int +wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header) +{ + int i; + int num_samples; + unsigned char msample_hdr[WF_MSAMPLE_BYTES]; + + munge_int32 (header->number, &msample_hdr[0], 2); + + /* You'll recall at this point that the "number of samples" value + in a wavefront_multisample struct is actually the log2 of the + real number of samples. + */ + + num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); + msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; + + DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n", + header->number, + header->hdr.ms.NumberOfSamples, + num_samples); + + for (i = 0; i < num_samples; i++) { + DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + munge_int32 (header->hdr.ms.SampleNumber[i], + &msample_hdr[3+(i*2)], 2); + } + + /* Need a hack here to pass in the number of bytes + to be written to the synth. This is ugly, and perhaps + one day, I'll fix it. + */ + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_MULTISAMPLE, + (unsigned char *) (long) ((num_samples*2)+3), + msample_hdr)) { + snd_printk ("download of multisample failed.\n"); + return -(EIO); + } + + dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); + + return (0); +} + +static int +wavefront_fetch_multisample (snd_wavefront_t *dev, + wavefront_patch_info *header) +{ + int i; + unsigned char log_ns[1]; + unsigned char number[2]; + int num_samples; + + munge_int32 (header->number, number, 2); + + if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { + snd_printk ("upload multisample failed.\n"); + return -(EIO); + } + + DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n", + header->number, log_ns[0]); + + header->hdr.ms.NumberOfSamples = log_ns[0]; + + /* get the number of samples ... */ + + num_samples = (1 << log_ns[0]); + + for (i = 0; i < num_samples; i++) { + char d[2]; + + if ((d[0] = wavefront_read (dev)) == -1) { + snd_printk ("upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + + if ((d[1] = wavefront_read (dev)) == -1) { + snd_printk ("upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + + header->hdr.ms.SampleNumber[i] = + demunge_int32 ((unsigned char *) d, 2); + + DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } + + return (0); +} + + +static int +wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char drumbuf[WF_DRUM_BYTES]; + wavefront_drum *drum = &header->hdr.d; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI " + "note %d, patch = %d\n", + header->number, drum->PatchNumber); + + drumbuf[0] = header->number & 0x7f; + + for (i = 0; i < 4; i++) { + munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); + } + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) { + snd_printk ("download drum failed.\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_find_free_sample (snd_wavefront_t *dev) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(dev->sample_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + snd_printk ("no free sample slots!\n"); + return -1; +} + +#if 0 +static int +wavefront_find_free_patch (snd_wavefront_t *dev) + +{ + int i; + + for (i = 0; i < WF_MAX_PATCH; i++) { + if (!(dev->patch_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + snd_printk ("no free patch slots!\n"); + return -1; +} +#endif + +static int +wavefront_load_patch (snd_wavefront_t *dev, const char *addr) + +{ + wavefront_patch_info header; + + if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) - + sizeof(wavefront_any))) { + snd_printk ("bad address for load patch.\n"); + return -(EFAULT); + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "download " + "Sample type: %d " + "Sample number: %d " + "Sample size: %d\n", + header.subkey, + header.number, + header.size); + + switch (header.subkey) { + case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ + + if (copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_sample))) + return -EFAULT; + + return wavefront_send_sample (dev, &header, header.dataptr, 0); + + case WF_ST_MULTISAMPLE: + + if (copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_multisample))) + return -EFAULT; + + return wavefront_send_multisample (dev, &header); + + + case WF_ST_ALIAS: + + if (copy_from_user ((unsigned char *) &header.hdr.a, + (unsigned char *) header.hdrptr, + sizeof (wavefront_alias))) + return -EFAULT; + + return wavefront_send_alias (dev, &header); + + case WF_ST_DRUM: + if (copy_from_user ((unsigned char *) &header.hdr.d, + (unsigned char *) header.hdrptr, + sizeof (wavefront_drum))) + return -EFAULT; + + return wavefront_send_drum (dev, &header); + + case WF_ST_PATCH: + if (copy_from_user ((unsigned char *) &header.hdr.p, + (unsigned char *) header.hdrptr, + sizeof (wavefront_patch))) + return -EFAULT; + + return wavefront_send_patch (dev, &header); + + case WF_ST_PROGRAM: + if (copy_from_user ((unsigned char *) &header.hdr.pr, + (unsigned char *) header.hdrptr, + sizeof (wavefront_program))) + return -EFAULT; + + return wavefront_send_program (dev, &header); + + default: + snd_printk ("unknown patch type %d.\n", + header.subkey); + return -(EINVAL); + } + + return 0; +} + +/*********************************************************************** +WaveFront: hardware-dependent interface +***********************************************************************/ + +static void +process_sample_hdr (u8 *buf) + +{ + wavefront_sample s; + u8 *ptr; + + ptr = buf; + + /* The board doesn't send us an exact copy of a "wavefront_sample" + in response to an Upload Sample Header command. Instead, we + have to convert the data format back into our data structure, + just as in the Download Sample command, where we have to do + something very similar in the reverse direction. + */ + + *((u32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; + + s.SampleResolution = *ptr & 0x3; + s.Loop = *ptr & 0x8; + s.Bidirectional = *ptr & 0x10; + s.Reverse = *ptr & 0x40; + + /* Now copy it back to where it came from */ + + memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); +} + +static int +wavefront_synth_control (snd_wavefront_card_t *acard, + wavefront_control *wc) + +{ + snd_wavefront_t *dev = &acard->wavefront; + unsigned char patchnumbuf[2]; + int i; + + DPRINT (WF_DEBUG_CMD, "synth control with " + "cmd 0x%x\n", wc->cmd); + + /* Pre-handling of or for various commands */ + + switch (wc->cmd) { + + case WFC_DISABLE_INTERRUPTS: + snd_printk ("interrupts disabled.\n"); + outb (0x80|0x20, dev->control_port); + dev->interrupts_are_midi = 1; + return 0; + + case WFC_ENABLE_INTERRUPTS: + snd_printk ("interrupts enabled.\n"); + outb (0x80|0x40|0x20, dev->control_port); + dev->interrupts_are_midi = 1; + return 0; + + case WFC_INTERRUPT_STATUS: + wc->rbuf[0] = dev->interrupts_are_midi; + return 0; + + case WFC_ROMSAMPLES_RDONLY: + dev->rom_samples_rdonly = wc->wbuf[0]; + wc->status = 0; + return 0; + + case WFC_IDENTIFY_SLOT_TYPE: + i = wc->wbuf[0] | (wc->wbuf[1] << 7); + if (i <0 || i >= WF_MAX_SAMPLE) { + snd_printk ("invalid slot ID %d\n", + i); + wc->status = EINVAL; + return -EINVAL; + } + wc->rbuf[0] = dev->sample_status[i]; + wc->status = 0; + return 0; + + case WFC_DEBUG_DRIVER: + dev->debug = wc->wbuf[0]; + snd_printk ("debug = 0x%x\n", dev->debug); + return 0; + + case WFC_UPLOAD_PATCH: + munge_int32 (*((u32 *) wc->wbuf), patchnumbuf, 2); + memcpy (wc->wbuf, patchnumbuf, 2); + break; + + case WFC_UPLOAD_MULTISAMPLE: + /* multisamples have to be handled differently, and + cannot be dealt with properly by snd_wavefront_cmd() alone. + */ + wc->status = wavefront_fetch_multisample + (dev, (wavefront_patch_info *) wc->rbuf); + return 0; + + case WFC_UPLOAD_SAMPLE_ALIAS: + snd_printk ("support for sample alias upload " + "being considered.\n"); + wc->status = EINVAL; + return -EINVAL; + } + + wc->status = snd_wavefront_cmd (dev, wc->cmd, wc->rbuf, wc->wbuf); + + /* Post-handling of certain commands. + + In particular, if the command was an upload, demunge the data + so that the user-level doesn't have to think about it. + */ + + if (wc->status == 0) { + switch (wc->cmd) { + /* intercept any freemem requests so that we know + we are always current with the user-level view + of things. + */ + + case WFC_REPORT_FREE_MEMORY: + dev->freemem = demunge_int32 (wc->rbuf, 4); + break; + + case WFC_UPLOAD_PATCH: + demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES); + break; + + case WFC_UPLOAD_PROGRAM: + demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES); + break; + + case WFC_UPLOAD_EDRUM_PROGRAM: + demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1); + break; + + case WFC_UPLOAD_SAMPLE_HEADER: + process_sample_hdr (wc->rbuf); + break; + + case WFC_UPLOAD_SAMPLE_ALIAS: + snd_printk ("support for " + "sample aliases still " + "being considered.\n"); + break; + + case WFC_VMIDI_OFF: + snd_wavefront_midi_disable_virtual (acard); + break; + + case WFC_VMIDI_ON: + snd_wavefront_midi_enable_virtual (acard); + break; + } + } + + return 0; +} + +int +snd_wavefront_synth_open (snd_hwdep_t *hw, struct file *file) + +{ + MOD_INC_USE_COUNT; + if (!try_inc_mod_count(hw->card->module)) { + MOD_DEC_USE_COUNT; + return -EFAULT; + } + file->private_data = hw; + return 0; +} + +int +snd_wavefront_synth_release (snd_hwdep_t *hw, struct file *file) + +{ + dec_mod_count(hw->card->module); + MOD_DEC_USE_COUNT; + return 0; +} + +int +snd_wavefront_synth_ioctl (snd_hwdep_t *hw, struct file *file, + unsigned int cmd, unsigned long arg) + +{ + snd_card_t *card; + snd_wavefront_t *dev; + snd_wavefront_card_t *acard; + wavefront_control wc; + + card = (snd_card_t *) hw->card; + + snd_assert(card != NULL, return -ENODEV); + + snd_assert(card->private_data != NULL, return -ENODEV); + + acard = card->private_data; + dev = &acard->wavefront; + + switch (cmd) { + case WFCTL_LOAD_SPP: + if (wavefront_load_patch (dev, (char *) arg) != 0) { + return -EIO; + } + break; + + case WFCTL_WFCMD: + if (copy_from_user (&wc, (void *) arg, sizeof (wc))) + return -EFAULT; + if (wavefront_synth_control (acard, &wc) < 0) { + return -EIO; + } + if (copy_to_user ((void *) arg, &wc, sizeof (wc))) + return -EFAULT; + break; + + default: + return -EINVAL; + } + + return 0; +} + + +/***********************************************************************/ +/* WaveFront: interface for card-level wavefront module */ +/***********************************************************************/ + +void +snd_wavefront_internal_interrupt (snd_wavefront_card_t *card) +{ + snd_wavefront_t *dev = &card->wavefront; + + /* + Some comments on interrupts. I attempted a version of this + driver that used interrupts throughout the code instead of + doing busy and/or sleep-waiting. Alas, it appears that once + the Motorola firmware is downloaded, the card *never* + generates an RX interrupt. These are successfully generated + during firmware loading, and after that wavefront_status() + reports that an interrupt is pending on the card from time + to time, but it never seems to be delivered to this + driver. Note also that wavefront_status() continues to + report that RX interrupts are enabled, suggesting that I + didn't goof up and disable them by mistake. + + Thus, I stepped back to a prior version of + wavefront_wait(), the only place where this really + matters. Its sad, but I've looked through the code to check + on things, and I really feel certain that the Motorola + firmware prevents RX-ready interrupts. + */ + + if ((wavefront_status(dev) & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) { + return; + } + + dev->irq_ok = 1; + dev->irq_cnt++; + wake_up_interruptible (&dev->interrupt_sleeper); +} + +/* STATUS REGISTER + +0 Host Rx Interrupt Enable (1=Enabled) +1 Host Rx Register Full (1=Full) +2 Host Rx Interrupt Pending (1=Interrupt) +3 Unused +4 Host Tx Interrupt (1=Enabled) +5 Host Tx Register empty (1=Empty) +6 Host Tx Interrupt Pending (1=Interrupt) +7 Unused +*/ + +int +snd_wavefront_interrupt_bits (int irq) + +{ + int bits; + + switch (irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + snd_printk ("invalid IRQ %d\n", irq); + bits = -1; + } + + return bits; +} + +static void +wavefront_should_cause_interrupt (snd_wavefront_t *dev, + int val, int port, int timeout) + +{ + unsigned long flags; + + save_flags (flags); + cli(); + dev->irq_ok = 0; + outb (val,port); + interruptible_sleep_on_timeout (&dev->interrupt_sleeper, timeout); + restore_flags (flags); +} + +static int +wavefront_reset_to_cleanliness (snd_wavefront_t *dev) + +{ + int bits; + int hwv[2]; + + /* IRQ already checked */ + + bits = snd_wavefront_interrupt_bits (dev->irq); + + /* try reset of port */ + + outb (0x0, dev->control_port); + + /* At this point, the board is in reset, and the H/W initialization + register is accessed at the same address as the data port. + + Bit 7 - Enable IRQ Driver + 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs + 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. + + Bit 6 - MIDI Interface Select + + 0 - Use the MIDI Input from the 26-pin WaveBlaster + compatible header as the serial MIDI source + 1 - Use the MIDI Input from the 9-pin D connector as the + serial MIDI source. + + Bits 5:3 - IRQ Selection + 0 0 0 - IRQ 2/9 + 0 0 1 - IRQ 5 + 0 1 0 - IRQ 12 + 0 1 1 - IRQ 15 + 1 0 0 - Reserved + 1 0 1 - Reserved + 1 1 0 - Reserved + 1 1 1 - Reserved + + Bits 2:1 - Reserved + Bit 0 - Disable Boot ROM + 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM + 1 - memory accesses to 03FC30-03FFFFH are directed to external + storage. + + */ + + /* configure hardware: IRQ, enable interrupts, + plus external 9-pin MIDI interface selected + */ + + outb (0x80 | 0x40 | bits, dev->data_port); + + /* CONTROL REGISTER + + 0 Host Rx Interrupt Enable (1=Enabled) 0x1 + 1 Unused 0x2 + 2 Unused 0x4 + 3 Unused 0x8 + 4 Host Tx Interrupt Enable 0x10 + 5 Mute (0=Mute; 1=Play) 0x20 + 6 Master Interrupt Enable (1=Enabled) 0x40 + 7 Master Reset (0=Reset; 1=Run) 0x80 + + Take us out of reset, mute output, master + TX + RX interrupts on. + + We'll get an interrupt presumably to tell us that the TX + register is clear. + */ + + wavefront_should_cause_interrupt(dev, 0x80|0x40|0x10|0x1, + dev->control_port, + (reset_time*HZ)/100); + + /* Note: data port is now the data port, not the h/w initialization + port. + */ + + if (!dev->irq_ok) { + snd_printk ("intr not received after h/w un-reset.\n"); + goto gone_bad; + } + + /* Note: data port is now the data port, not the h/w initialization + port. + + At this point, only "HW VERSION" or "DOWNLOAD OS" commands + will work. So, issue one of them, and wait for TX + interrupt. This can take a *long* time after a cold boot, + while the ISC ROM does its RAM test. The SDK says up to 4 + seconds - with 12MB of RAM on a Tropez+, it takes a lot + longer than that (~16secs). Note that the card understands + the difference between a warm and a cold boot, so + subsequent ISC2115 reboots (say, caused by module + reloading) will get through this much faster. + + XXX Interesting question: why is no RX interrupt received first ? + */ + + wavefront_should_cause_interrupt(dev, WFC_HARDWARE_VERSION, + dev->data_port, ramcheck_time*HZ); + + if (!dev->irq_ok) { + snd_printk ("post-RAM-check interrupt not received.\n"); + goto gone_bad; + } + + if (!wavefront_wait (dev, STAT_CAN_READ)) { + snd_printk ("no response to HW version cmd.\n"); + goto gone_bad; + } + + if ((hwv[0] = wavefront_read (dev)) == -1) { + snd_printk ("board not responding correctly.\n"); + goto gone_bad; + } + + if (hwv[0] == 0xFF) { /* NAK */ + + /* Board's RAM test failed. Try to read error code, + and tell us about it either way. + */ + + if ((hwv[0] = wavefront_read (dev)) == -1) { + snd_printk ("on-board RAM test failed " + "(bad error code).\n"); + } else { + snd_printk ("on-board RAM test failed " + "(error code: 0x%x).\n", + hwv[0]); + } + goto gone_bad; + } + + /* We're OK, just get the next byte of the HW version response */ + + if ((hwv[1] = wavefront_read (dev)) == -1) { + snd_printk ("incorrect h/w response.\n"); + goto gone_bad; + } + + snd_printk ("hardware version %d.%d\n", + hwv[0], hwv[1]); + + return 0; + + + gone_bad: + return (1); +} + +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include + +static int errno; + +static int +wavefront_download_firmware (snd_wavefront_t *dev, char *path) + +{ + unsigned char section[WF_SECTION_MAX]; + char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ + int section_cnt_downloaded = 0; + int fd; + int c; + int i; + mm_segment_t fs; + + /* This tries to be a bit cleverer than the stuff Alan Cox did for + the generic sound firmware, in that it actually knows + something about the structure of the Motorola firmware. In + particular, it uses a version that has been stripped of the + 20K of useless header information, and had section lengths + added, making it possible to load the entire OS without any + [kv]malloc() activity, since the longest entity we ever read is + 42 bytes (well, WF_SECTION_MAX) long. + */ + + fs = get_fs(); + set_fs (get_ds()); + + if ((fd = open (path, 0, 0)) < 0) { + snd_printk ("Unable to load \"%s\".\n", + path); + return 1; + } + + while (1) { + int x; + + if ((x = read (fd, §ion_length, sizeof (section_length))) != + sizeof (section_length)) { + snd_printk ("firmware read error.\n"); + goto failure; + } + + if (section_length == 0) { + break; + } + + if (read (fd, section, section_length) != section_length) { + snd_printk ("firmware section " + "read error.\n"); + goto failure; + } + + /* Send command */ + + if (wavefront_write (dev, WFC_DOWNLOAD_OS)) { + goto failure; + } + + for (i = 0; i < section_length; i++) { + if (wavefront_write (dev, section[i])) { + goto failure; + } + } + + /* get ACK */ + + if (wavefront_wait (dev, STAT_CAN_READ)) { + + if ((c = inb (dev->data_port)) != WF_ACK) { + + snd_printk ("download " + "of section #%d not " + "acknowledged, ack = 0x%x\n", + section_cnt_downloaded + 1, c); + goto failure; + + } + + } else { + snd_printk ("time out for firmware ACK.\n"); + goto failure; + } + + } + + close (fd); + set_fs (fs); + return 0; + + failure: + close (fd); + set_fs (fs); + snd_printk ("firmware download failed!!!\n"); + return 1; +} + + +static int +wavefront_do_reset (snd_wavefront_t *dev) + +{ + char voices[1]; + + if (wavefront_reset_to_cleanliness (dev)) { + snd_printk ("hw reset failed.\n"); + goto gone_bad; + } + + if (dev->israw) { + if (wavefront_download_firmware (dev, ospath)) { + goto gone_bad; + } + + dev->israw = 0; + + /* Wait for the OS to get running. The protocol for + this is non-obvious, and was determined by + using port-IO tracing in DOSemu and some + experimentation here. + + Rather than using timed waits, use interrupts creatively. + */ + + wavefront_should_cause_interrupt (dev, WFC_NOOP, + dev->data_port, + (osrun_time*HZ)); + + if (!dev->irq_ok) { + snd_printk ("no post-OS interrupt.\n"); + goto gone_bad; + } + + /* Now, do it again ! */ + + wavefront_should_cause_interrupt (dev, WFC_NOOP, + dev->data_port, (10*HZ)); + + if (!dev->irq_ok) { + snd_printk ("no post-OS interrupt(2).\n"); + goto gone_bad; + } + + /* OK, no (RX/TX) interrupts any more, but leave mute + in effect. + */ + + outb (0x80|0x40, dev->control_port); + } + + /* SETUPSND.EXE asks for sample memory config here, but since i + have no idea how to interpret the result, we'll forget + about it. + */ + + if ((dev->freemem = wavefront_freemem (dev)) < 0) { + goto gone_bad; + } + + snd_printk ("available DRAM %dk\n", dev->freemem / 1024); + + if (wavefront_write (dev, 0xf0) || + wavefront_write (dev, 1) || + (wavefront_read (dev) < 0)) { + dev->debug = 0; + snd_printk ("MPU emulation mode not set.\n"); + goto gone_bad; + } + + voices[0] = 32; + + if (snd_wavefront_cmd (dev, WFC_SET_NVOICES, 0, voices)) { + snd_printk ("cannot set number of voices to 32.\n"); + goto gone_bad; + } + + + return 0; + + gone_bad: + /* reset that sucker so that it doesn't bother us. */ + + outb (0x0, dev->control_port); + dev->interrupts_are_midi = 0; + return 1; +} + +int +snd_wavefront_start (snd_wavefront_t *dev) + +{ + int samples_are_from_rom; + + /* IMPORTANT: assumes that snd_wavefront_detect() and/or + wavefront_reset_to_cleanliness() has already been called + */ + + if (dev->israw) { + samples_are_from_rom = 1; + } else { + /* XXX is this always true ? */ + samples_are_from_rom = 0; + } + + if (dev->israw || fx_raw) { + if (wavefront_do_reset (dev)) { + return -1; + } + } + /* Check for FX device, present only on Tropez+ */ + + dev->has_fx = (snd_wavefront_fx_detect (dev) == 0); + + if (dev->has_fx && fx_raw) { + snd_wavefront_fx_start (dev); + } + + wavefront_get_sample_status (dev, samples_are_from_rom); + wavefront_get_program_status (dev); + wavefront_get_patch_status (dev); + + /* Start normal operation: unreset, master interrupt enabled, no mute + */ + + outb (0x80|0x40|0x20, dev->control_port); + + return (0); +} + +int +snd_wavefront_detect (snd_wavefront_card_t *card) + +{ + unsigned char rbuf[4], wbuf[4]; + snd_wavefront_t *dev = &card->wavefront; + + /* returns zero if a WaveFront card is successfully detected. + negative otherwise. + */ + + dev->israw = 0; + dev->has_fx = 0; + dev->debug = debug_default; + dev->interrupts_are_midi = 0; + dev->irq_cnt = 0; + dev->rom_samples_rdonly = 1; + + if (snd_wavefront_cmd (dev, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { + + dev->fw_version[0] = rbuf[0]; + dev->fw_version[1] = rbuf[1]; + + snd_printk ("firmware %d.%d already loaded.\n", + rbuf[0], rbuf[1]); + + /* check that a command actually works */ + + if (snd_wavefront_cmd (dev, WFC_HARDWARE_VERSION, + rbuf, wbuf) == 0) { + dev->hw_version[0] = rbuf[0]; + dev->hw_version[1] = rbuf[1]; + } else { + snd_printk ("not raw, but no " + "hardware version!\n"); + return -1; + } + + if (!wf_raw) { + return 0; + } else { + snd_printk ("reloading firmware as you requested.\n"); + dev->israw = 1; + } + + } else { + + dev->israw = 1; + snd_printk ("no response to firmware probe, assume raw.\n"); + + } + + return 0; +} + +#if 0 +EXPORT_SYMBOL(snd_wavefront_synth_ioctl); +EXPORT_SYMBOL(snd_wavefront_synth_open); +EXPORT_SYMBOL(snd_wavefront_synth_release); +EXPORT_SYMBOL(snd_wavefront_internal_interrupt); +EXPORT_SYMBOL(snd_wavefront_interrupt_bits); +EXPORT_SYMBOL(snd_wavefront_start); +EXPORT_SYMBOL(snd_wavefront_detect); +EXPORT_SYMBOL(snd_wavefront_cmd); + /* wavefront_midi.c */ +EXPORT_SYMBOL(snd_wavefront_midi_interrupt); +EXPORT_SYMBOL(snd_wavefront_midi_enable_virtual); +EXPORT_SYMBOL(snd_wavefront_midi_disable_virtual); +EXPORT_SYMBOL(snd_wavefront_midi_start); +EXPORT_SYMBOL(snd_wavefront_midi_input); +EXPORT_SYMBOL(snd_wavefront_midi_output); + +static int __init alsa_wavefront_init(void) +{ + return 0; +} + +static void __exit alsa_wavefront_exit(void) +{ +} + +module_init(alsa_wavefront_init) +module_exit(alsa_wavefront_exit) +#endif diff -Nru a/sound/last.c b/sound/last.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/last.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,41 @@ +/* + * Advanced Linux Sound Architecture + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_MAIN_OBJECT_FILE +#include +#include + +static int __init alsa_sound_last_init(void) +{ + int idx, ok = 0; + + printk("ALSA device list:\n"); + for (idx = 0; idx < SNDRV_CARDS; idx++) + if (snd_cards[idx] != NULL) { + printk(" #%i: %s\n", idx, snd_cards[idx]->longname); + ok++; + } + if (ok == 0) + printk(" No soundcards found.\n"); + return 0; +} + +__initcall(alsa_sound_last_init); diff -Nru a/sound/oss/724hwmcode.h b/sound/oss/724hwmcode.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/724hwmcode.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,1575 @@ +//============================================================================= +// Copyright (c) 1997-1999 Yamaha Corporation. All Rights Reserved. +// +// Title: +// hwmcode.c +// Desc: +// micro-code for CTRL & DSP +//============================================================================= +#ifndef _HWMCODE_ +#define _HWMCODE_ + +static unsigned long int DspInst[] __initdata = { + 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, + 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, + 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, + 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +static unsigned long int CntrlInst[] __initdata = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x001A82, 0x032D0D, 0x000810, 0x10043A, + 0x02D38D, 0x000810, 0x18043A, 0x00010D, + 0x020015, 0x0000FD, 0x000020, 0x038860, + 0x039060, 0x038060, 0x038040, 0x038040, + 0x038040, 0x018040, 0x000A7D, 0x038040, + 0x038040, 0x018040, 0x200402, 0x000882, + 0x08001A, 0x000904, 0x015986, 0x000007, + 0x260007, 0x000007, 0x000007, 0x018A06, + 0x000007, 0x030C8D, 0x000810, 0x18043A, + 0x260007, 0x00087D, 0x018042, 0x00160A, + 0x04A206, 0x000007, 0x00218D, 0x000810, + 0x08043A, 0x21C206, 0x000007, 0x0007FD, + 0x018042, 0x08000A, 0x000904, 0x029386, + 0x000195, 0x090D04, 0x000007, 0x000820, + 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, + 0x032206, 0x018040, 0x000A7D, 0x038042, + 0x13804A, 0x18000A, 0x001820, 0x059060, + 0x058860, 0x018040, 0x0000FD, 0x018042, + 0x70000A, 0x000115, 0x071144, 0x032386, + 0x030000, 0x007020, 0x034A06, 0x018040, + 0x00348D, 0x000810, 0x08043A, 0x21EA06, + 0x000007, 0x02D38D, 0x000810, 0x18043A, + 0x018206, 0x000007, 0x240007, 0x000F8D, + 0x000810, 0x00163A, 0x002402, 0x005C02, + 0x0028FD, 0x000020, 0x018040, 0x08000D, + 0x000815, 0x510984, 0x000007, 0x00004D, + 0x000E5D, 0x000E02, 0x00418D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00008D, + 0x000924, 0x000F02, 0x00458D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x018386, 0x000007, 0x01AA06, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x218086, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x055A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x034986, 0x000007, 0x002104, 0x034986, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x06C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00688D, 0x000810, 0x08043A, 0x288A06, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x060206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x215886, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x212086, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x07DA86, 0x00057D, 0x002820, + 0x03B060, 0x07F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x07FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x008D8D, 0x000810, + 0x08043A, 0x288A06, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x095186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x007FBD, 0x383DC4, + 0x000007, 0x001A7D, 0x001375, 0x018042, + 0x09004A, 0x10000A, 0x0B8D04, 0x139504, + 0x000007, 0x000820, 0x019060, 0x001104, + 0x212086, 0x010040, 0x0017FD, 0x018042, + 0x08000A, 0x000904, 0x212286, 0x000007, + 0x00197D, 0x038042, 0x09804A, 0x10000A, + 0x000924, 0x001664, 0x0011FD, 0x038042, + 0x2B804A, 0x19804A, 0x00008D, 0x218944, + 0x000007, 0x002244, 0x0AE186, 0x000007, + 0x001A64, 0x002A24, 0x00197D, 0x080102, + 0x100122, 0x000820, 0x039060, 0x018040, + 0x003DFD, 0x00008D, 0x000820, 0x018040, + 0x001375, 0x001A7D, 0x010042, 0x09804A, + 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, + 0x309144, 0x000007, 0x00060D, 0x000A15, + 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, + 0x000464, 0x01B3E4, 0x0232E4, 0x000464, + 0x000464, 0x000464, 0x000464, 0x00040D, + 0x08B1C4, 0x000007, 0x000820, 0x000BF5, + 0x030040, 0x00197D, 0x038042, 0x09804A, + 0x000A24, 0x08000A, 0x080E64, 0x000007, + 0x100122, 0x000820, 0x031060, 0x010040, + 0x0064AC, 0x00027D, 0x000020, 0x018040, + 0x00107D, 0x018042, 0x0011FD, 0x3B804A, + 0x09804A, 0x20000A, 0x000095, 0x1A1144, + 0x00A144, 0x0D2086, 0x00040D, 0x00B984, + 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, + 0x09804A, 0x28000A, 0x000095, 0x010924, + 0x002A64, 0x0D1186, 0x000007, 0x002904, + 0x0D2286, 0x000007, 0x0D2A06, 0x080002, + 0x00008D, 0x00387D, 0x000820, 0x018040, + 0x00127D, 0x018042, 0x10000A, 0x003904, + 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, + 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, + 0x000015, 0x00082D, 0x02C78D, 0x000820, + 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, + 0x0E7186, 0x400025, 0x00008D, 0x110944, + 0x000007, 0x00018D, 0x109504, 0x000007, + 0x009164, 0x000424, 0x000424, 0x000424, + 0x100102, 0x280002, 0x02C68D, 0x000820, + 0x0EC206, 0x00018D, 0x00042D, 0x00008D, + 0x109504, 0x000007, 0x00020D, 0x109184, + 0x000007, 0x02C70D, 0x000820, 0x00008D, + 0x0038FD, 0x018040, 0x003BFD, 0x001020, + 0x03A860, 0x000815, 0x313184, 0x212184, + 0x000007, 0x03B060, 0x03A060, 0x018040, + 0x0022FD, 0x000095, 0x010924, 0x000424, + 0x000424, 0x001264, 0x100102, 0x000820, + 0x039060, 0x018040, 0x001924, 0x00FB8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x000424, + 0x000424, 0x00117D, 0x018042, 0x08000A, + 0x000A24, 0x280502, 0x280C02, 0x09800D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x0022FD, 0x018042, 0x08000A, 0x000095, + 0x280DC4, 0x011924, 0x00197D, 0x018042, + 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, + 0x113144, 0x0A8D04, 0x000007, 0x080A44, + 0x129504, 0x000007, 0x0023FD, 0x001020, + 0x038040, 0x101244, 0x000007, 0x000820, + 0x039060, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x10FA86, 0x000007, + 0x003BFD, 0x000100, 0x000A10, 0x0B807A, + 0x13804A, 0x090984, 0x000007, 0x000095, + 0x013D04, 0x118086, 0x10000A, 0x100002, + 0x090984, 0x000007, 0x038042, 0x11804A, + 0x090D04, 0x000007, 0x10000A, 0x090D84, + 0x000007, 0x00257D, 0x000820, 0x018040, + 0x00010D, 0x000810, 0x28143A, 0x00127D, + 0x018042, 0x20000A, 0x00197D, 0x018042, + 0x00117D, 0x31804A, 0x10000A, 0x003124, + 0x01280D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x300102, 0x003124, 0x000424, 0x000424, + 0x001224, 0x280502, 0x001A4C, 0x130186, + 0x700002, 0x00002D, 0x030000, 0x00387D, + 0x018042, 0x10000A, 0x132A06, 0x002124, + 0x0000AD, 0x100002, 0x00010D, 0x000924, + 0x006B24, 0x01368D, 0x00397D, 0x000820, + 0x058040, 0x038042, 0x09844A, 0x000606, + 0x08040A, 0x003264, 0x00008D, 0x000A24, + 0x001020, 0x00227D, 0x018040, 0x013C0D, + 0x000810, 0x08043A, 0x29D206, 0x000007, + 0x002820, 0x00207D, 0x018040, 0x00117D, + 0x038042, 0x13804A, 0x33800A, 0x00387D, + 0x018042, 0x08000A, 0x000904, 0x163A86, + 0x000007, 0x00008D, 0x030964, 0x01478D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x380102, + 0x000424, 0x000424, 0x001224, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x14A286, + 0x000007, 0x280502, 0x001A4C, 0x163986, + 0x000007, 0x032164, 0x00632C, 0x003DFD, + 0x018042, 0x08000A, 0x000095, 0x090904, + 0x000007, 0x000820, 0x001A4C, 0x156186, + 0x018040, 0x030000, 0x157A06, 0x002124, + 0x00010D, 0x000924, 0x006B24, 0x015B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x003A64, + 0x000095, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x01628D, 0x000810, 0x08043A, 0x29D206, + 0x000007, 0x14D206, 0x000007, 0x007020, + 0x08010A, 0x10012A, 0x0020FD, 0x038860, + 0x039060, 0x018040, 0x00227D, 0x018042, + 0x003DFD, 0x08000A, 0x31844A, 0x000904, + 0x16D886, 0x18008B, 0x00008D, 0x189904, + 0x00312C, 0x17AA06, 0x000007, 0x00324C, + 0x173386, 0x000007, 0x001904, 0x173086, + 0x000007, 0x000095, 0x199144, 0x00222C, + 0x003124, 0x00636C, 0x000E3D, 0x001375, + 0x000BFD, 0x010042, 0x09804A, 0x10000A, + 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, + 0x000007, 0x00008D, 0x189904, 0x00226C, + 0x00322C, 0x30050A, 0x301DAB, 0x002083, + 0x0018FD, 0x018042, 0x08000A, 0x018924, + 0x300502, 0x001083, 0x001875, 0x010042, + 0x10000A, 0x00008D, 0x010924, 0x001375, + 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, + 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, + 0x305C8B, 0x006083, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x187A86, 0x000007, + 0x001E2D, 0x0005FD, 0x018042, 0x08000A, + 0x028924, 0x280502, 0x00060D, 0x000810, + 0x280C3A, 0x00008D, 0x000810, 0x28143A, + 0x0A808D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001275, 0x030042, 0x21004A, + 0x00008D, 0x1A0944, 0x000007, 0x01980D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0001F5, 0x030042, 0x0D004A, 0x10000A, + 0x089144, 0x000007, 0x000820, 0x010040, + 0x0025F5, 0x0A3144, 0x000007, 0x000820, + 0x032860, 0x030040, 0x00217D, 0x038042, + 0x0B804A, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00008D, 0x000124, 0x00012C, + 0x000E64, 0x001A64, 0x00636C, 0x08010A, + 0x10012A, 0x000820, 0x031060, 0x030040, + 0x0020FD, 0x018042, 0x08000A, 0x00227D, + 0x018042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00197D, 0x018042, 0x08000A, + 0x0022FD, 0x038042, 0x10000A, 0x000820, + 0x031060, 0x030040, 0x090D04, 0x000007, + 0x000820, 0x030040, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x038042, 0x13804A, 0x19804A, 0x110D04, + 0x198D04, 0x000007, 0x08000A, 0x001020, + 0x031860, 0x030860, 0x030040, 0x00008D, + 0x0B0944, 0x000007, 0x000820, 0x010040, + 0x0005F5, 0x030042, 0x08000A, 0x000820, + 0x010040, 0x0000F5, 0x010042, 0x08000A, + 0x000904, 0x1C6086, 0x001E75, 0x030042, + 0x01044A, 0x000C0A, 0x1C7206, 0x000007, + 0x000402, 0x000C02, 0x00177D, 0x001AF5, + 0x018042, 0x03144A, 0x031C4A, 0x03244A, + 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, + 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, + 0x030042, 0x0B004A, 0x1B804A, 0x13804A, + 0x20000A, 0x089144, 0x19A144, 0x0389E4, + 0x0399EC, 0x005502, 0x005D0A, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x006502, 0x006D0A, 0x030042, 0x0B004A, + 0x19004A, 0x2B804A, 0x13804A, 0x21804A, + 0x30000A, 0x089144, 0x19A144, 0x2AB144, + 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, + 0x03A9E4, 0x000702, 0x00107D, 0x000415, + 0x018042, 0x08000A, 0x0109E4, 0x000F02, + 0x002AF5, 0x0019FD, 0x010042, 0x09804A, + 0x10000A, 0x000934, 0x001674, 0x0029F5, + 0x010042, 0x10000A, 0x00917C, 0x002075, + 0x010042, 0x08000A, 0x000904, 0x1ED286, + 0x0026F5, 0x0027F5, 0x030042, 0x09004A, + 0x10000A, 0x000A3C, 0x00167C, 0x001A75, + 0x000BFD, 0x010042, 0x51804A, 0x48000A, + 0x160007, 0x001075, 0x010042, 0x282C0A, + 0x281D12, 0x282512, 0x001F32, 0x1E0007, + 0x0E0007, 0x001975, 0x010042, 0x002DF5, + 0x0D004A, 0x10000A, 0x009144, 0x1FB286, + 0x010042, 0x28340A, 0x000E5D, 0x00008D, + 0x000375, 0x000820, 0x010040, 0x05D2F4, + 0x54D104, 0x00735C, 0x205386, 0x000007, + 0x0C0007, 0x080007, 0x0A0007, 0x02040D, + 0x000810, 0x08043A, 0x332206, 0x000007, + 0x205A06, 0x000007, 0x080007, 0x002275, + 0x010042, 0x20000A, 0x002104, 0x212086, + 0x001E2D, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x209286, 0x000007, 0x002010, + 0x30043A, 0x00057D, 0x0180C3, 0x08000A, + 0x028924, 0x280502, 0x280C02, 0x0A810D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x0004FD, 0x018042, 0x70000A, 0x030000, + 0x007020, 0x06FA06, 0x018040, 0x02180D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x218A86, 0x000007, 0x01F206, 0x000007, + 0x000875, 0x0009FD, 0x00010D, 0x220A06, + 0x000295, 0x000B75, 0x00097D, 0x00000D, + 0x000515, 0x010042, 0x18000A, 0x001904, + 0x287886, 0x0006F5, 0x001020, 0x010040, + 0x0004F5, 0x000820, 0x010040, 0x000775, + 0x010042, 0x09804A, 0x10000A, 0x001124, + 0x000904, 0x22BA86, 0x000815, 0x080102, + 0x101204, 0x22DA06, 0x000575, 0x081204, + 0x000007, 0x100102, 0x000575, 0x000425, + 0x021124, 0x100102, 0x000820, 0x031060, + 0x010040, 0x001924, 0x287886, 0x00008D, + 0x000464, 0x009D04, 0x278886, 0x180102, + 0x000575, 0x010042, 0x28040A, 0x00018D, + 0x000924, 0x280D02, 0x00000D, 0x000924, + 0x281502, 0x10000D, 0x000820, 0x0002F5, + 0x010040, 0x200007, 0x001175, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x23C286, + 0x000007, 0x000100, 0x080B20, 0x130B60, + 0x1B0B60, 0x030A60, 0x010040, 0x050042, + 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, + 0x0006F5, 0x010042, 0x28140A, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x24CA86, 0x004015, 0x000095, 0x010D04, + 0x24B886, 0x100022, 0x10002A, 0x24E206, + 0x000007, 0x333104, 0x2AA904, 0x000007, + 0x032124, 0x280502, 0x001124, 0x000424, + 0x000424, 0x003224, 0x00292C, 0x00636C, + 0x25F386, 0x000007, 0x02B164, 0x000464, + 0x000464, 0x00008D, 0x000A64, 0x280D02, + 0x10008D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x00008D, 0x38B904, 0x000007, + 0x03296C, 0x30010A, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x25BA86, 0x000007, + 0x02312C, 0x28050A, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x267A86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x26C086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x26CA86, 0x000007, 0x003124, 0x300502, + 0x003924, 0x300583, 0x000883, 0x0005F5, + 0x010042, 0x28040A, 0x00008D, 0x008124, + 0x280D02, 0x00008D, 0x008124, 0x281502, + 0x10018D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001025, 0x000575, 0x030042, + 0x09004A, 0x10000A, 0x0A0904, 0x121104, + 0x000007, 0x001020, 0x050860, 0x050040, + 0x0006FD, 0x018042, 0x09004A, 0x10000A, + 0x0000A5, 0x0A0904, 0x121104, 0x000007, + 0x000820, 0x019060, 0x010040, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x284286, + 0x000007, 0x230A06, 0x000007, 0x000606, + 0x000007, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x289286, 0x000007, 0x000100, + 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, + 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, + 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, + 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, + 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, + 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, + 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, + 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, + 0x000606, 0x018040, 0x00008D, 0x000A64, + 0x280D02, 0x000A24, 0x00027D, 0x018042, + 0x10000A, 0x001224, 0x0003FD, 0x018042, + 0x08000A, 0x000904, 0x2A8286, 0x000007, + 0x00018D, 0x000A24, 0x000464, 0x000464, + 0x080102, 0x000924, 0x000424, 0x000424, + 0x100102, 0x02000D, 0x009144, 0x2AD986, + 0x000007, 0x0001FD, 0x018042, 0x08000A, + 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x00027D, 0x001020, 0x000606, 0x018040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x2B2A86, 0x000007, 0x00037D, 0x018042, + 0x08000A, 0x000904, 0x2B5A86, 0x000007, + 0x000075, 0x002E7D, 0x010042, 0x0B804A, + 0x000020, 0x000904, 0x000686, 0x010040, + 0x31844A, 0x30048B, 0x000883, 0x00008D, + 0x000810, 0x28143A, 0x00008D, 0x000810, + 0x280C3A, 0x000675, 0x010042, 0x08000A, + 0x003815, 0x010924, 0x280502, 0x0B000D, + 0x000820, 0x0002F5, 0x010040, 0x000606, + 0x220007, 0x000464, 0x000464, 0x000606, + 0x000007, 0x000134, 0x007F8D, 0x00093C, + 0x281D12, 0x282512, 0x001F32, 0x0E0007, + 0x00010D, 0x00037D, 0x000820, 0x018040, + 0x05D2F4, 0x000007, 0x080007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2D0286, + 0x000007, 0x000606, 0x000007, 0x000007, + 0x000012, 0x100007, 0x320007, 0x600007, + 0x100080, 0x48001A, 0x004904, 0x2D6186, + 0x000007, 0x001210, 0x58003A, 0x000145, + 0x5C5D04, 0x000007, 0x000080, 0x48001A, + 0x004904, 0x2DB186, 0x000007, 0x001210, + 0x50003A, 0x005904, 0x2E0886, 0x000045, + 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, + 0x004224, 0x500102, 0x200502, 0x000082, + 0x40001A, 0x004104, 0x2E3986, 0x000007, + 0x003865, 0x40001A, 0x004020, 0x00104D, + 0x04C184, 0x301B86, 0x000040, 0x040007, + 0x000165, 0x000145, 0x004020, 0x000040, + 0x000765, 0x080080, 0x40001A, 0x004104, + 0x2EC986, 0x000007, 0x001210, 0x40003A, + 0x004104, 0x2F2286, 0x00004D, 0x0000CD, + 0x004810, 0x20043A, 0x000882, 0x40001A, + 0x004104, 0x2F3186, 0x000007, 0x004820, + 0x005904, 0x300886, 0x000040, 0x0007E5, + 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, + 0x4216E0, 0x021260, 0x000040, 0x000032, + 0x400075, 0x00007D, 0x07D574, 0x200512, + 0x000082, 0x40001A, 0x004104, 0x2FE186, + 0x000007, 0x037206, 0x640007, 0x060007, + 0x0000E5, 0x000020, 0x000040, 0x000A65, + 0x000020, 0x020040, 0x020040, 0x000040, + 0x000165, 0x000042, 0x70000A, 0x007104, + 0x30A286, 0x000007, 0x018206, 0x640007, + 0x050000, 0x007020, 0x000040, 0x037206, + 0x640007, 0x000007, 0x00306D, 0x028860, + 0x029060, 0x08000A, 0x028860, 0x008040, + 0x100012, 0x00100D, 0x009184, 0x314186, + 0x000E0D, 0x009184, 0x325186, 0x000007, + 0x300007, 0x001020, 0x003B6D, 0x008040, + 0x000080, 0x08001A, 0x000904, 0x316186, + 0x000007, 0x001220, 0x000DED, 0x008040, + 0x008042, 0x10000A, 0x40000D, 0x109544, + 0x000007, 0x001020, 0x000DED, 0x008040, + 0x008042, 0x20040A, 0x000082, 0x08001A, + 0x000904, 0x31F186, 0x000007, 0x003B6D, + 0x008042, 0x08000A, 0x000E15, 0x010984, + 0x329B86, 0x600007, 0x08001A, 0x000C15, + 0x010984, 0x328386, 0x000020, 0x1A0007, + 0x0002ED, 0x008040, 0x620007, 0x00306D, + 0x028042, 0x0A804A, 0x000820, 0x0A804A, + 0x000606, 0x10804A, 0x000007, 0x282512, + 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, + 0x000786, 0x000007, 0x0C0007, 0x0A0007, + 0x1C0007, 0x003465, 0x020040, 0x004820, + 0x025060, 0x40000A, 0x024060, 0x000040, + 0x454944, 0x000007, 0x004020, 0x003AE5, + 0x000040, 0x0028E5, 0x000042, 0x48000A, + 0x004904, 0x386886, 0x002C65, 0x000042, + 0x40000A, 0x0000D5, 0x454104, 0x000007, + 0x000655, 0x054504, 0x34F286, 0x0001D5, + 0x054504, 0x34F086, 0x002B65, 0x000042, + 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, + 0x000007, 0x454504, 0x000007, 0x0000CD, + 0x444944, 0x000007, 0x454504, 0x000007, + 0x00014D, 0x554944, 0x000007, 0x045144, + 0x34E986, 0x002C65, 0x000042, 0x48000A, + 0x4CD104, 0x000007, 0x04C144, 0x34F386, + 0x000007, 0x160007, 0x002CE5, 0x040042, + 0x40000A, 0x004020, 0x000040, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x356086, + 0x000007, 0x002402, 0x36A206, 0x005C02, + 0x0025E5, 0x000042, 0x40000A, 0x004274, + 0x002AE5, 0x000042, 0x40000A, 0x004274, + 0x500112, 0x0029E5, 0x000042, 0x40000A, + 0x004234, 0x454104, 0x000007, 0x004020, + 0x000040, 0x003EE5, 0x000020, 0x000040, + 0x002DE5, 0x400152, 0x50000A, 0x045144, + 0x364A86, 0x0000C5, 0x003EE5, 0x004020, + 0x000040, 0x002BE5, 0x000042, 0x40000A, + 0x404254, 0x000007, 0x002AE5, 0x004020, + 0x000040, 0x500132, 0x040134, 0x005674, + 0x0029E5, 0x020042, 0x42000A, 0x000042, + 0x50000A, 0x05417C, 0x0028E5, 0x000042, + 0x48000A, 0x0000C5, 0x4CC144, 0x371086, + 0x0026E5, 0x0027E5, 0x020042, 0x40004A, + 0x50000A, 0x00423C, 0x00567C, 0x0028E5, + 0x004820, 0x000040, 0x281D12, 0x282512, + 0x001F72, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x37AA86, 0x0E0007, 0x160007, + 0x1E0007, 0x003EE5, 0x000042, 0x40000A, + 0x004104, 0x37E886, 0x002D65, 0x000042, + 0x28340A, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, + 0x54D104, 0x00735C, 0x385186, 0x000007, + 0x000606, 0x080007, 0x0C0007, 0x080007, + 0x0A0007, 0x0001E5, 0x020045, 0x004020, + 0x000060, 0x000365, 0x000040, 0x002E65, + 0x001A20, 0x0A1A60, 0x000040, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x000606, 0x50004A, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +// -------------------------------------------- +// DS-1E Controller InstructionRAM Code +// 1999/06/21 +// Buf441 slot is Enabled. +// -------------------------------------------- +// 04/09?@creat +// 04/12 stop nise fix +// 06/21?@WorkingOff timming +static unsigned long int CntrlInst1E[] __initdata = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x00800D, 0x000810, 0x20043A, 0x001A82, + 0x03460D, 0x000810, 0x10043A, 0x02EC0D, + 0x000810, 0x18043A, 0x00010D, 0x020015, + 0x0000FD, 0x000020, 0x038860, 0x039060, + 0x038060, 0x038040, 0x038040, 0x038040, + 0x018040, 0x000A7D, 0x038040, 0x038040, + 0x018040, 0x200402, 0x000882, 0x08001A, + 0x000904, 0x017186, 0x000007, 0x260007, + 0x400007, 0x000007, 0x03258D, 0x000810, + 0x18043A, 0x260007, 0x284402, 0x00087D, + 0x018042, 0x00160A, 0x05A206, 0x000007, + 0x440007, 0x00230D, 0x000810, 0x08043A, + 0x22FA06, 0x000007, 0x0007FD, 0x018042, + 0x08000A, 0x000904, 0x02AB86, 0x000195, + 0x090D04, 0x000007, 0x000820, 0x0000F5, + 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, + 0x018040, 0x000A7D, 0x038042, 0x13804A, + 0x18000A, 0x001820, 0x059060, 0x058860, + 0x018040, 0x0000FD, 0x018042, 0x70000A, + 0x000115, 0x071144, 0x033B86, 0x030000, + 0x007020, 0x036206, 0x018040, 0x00360D, + 0x000810, 0x08043A, 0x232206, 0x000007, + 0x02EC0D, 0x000810, 0x18043A, 0x019A06, + 0x000007, 0x240007, 0x000F8D, 0x000810, + 0x00163A, 0x002402, 0x005C02, 0x0028FD, + 0x000020, 0x018040, 0x08000D, 0x000815, + 0x510984, 0x000007, 0x00004D, 0x000E5D, + 0x000E02, 0x00430D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x00008D, 0x000924, + 0x000F02, 0x00470D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x480480, 0x001210, + 0x28043A, 0x00778D, 0x000810, 0x280C3A, + 0x00068D, 0x000810, 0x28143A, 0x284402, + 0x03258D, 0x000810, 0x18043A, 0x07FF8D, + 0x000820, 0x0002FD, 0x018040, 0x260007, + 0x200007, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x051286, 0x000007, 0x240007, + 0x02EC0D, 0x000810, 0x18043A, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x019B86, 0x000007, 0x01B206, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x22B886, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x065A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x036186, 0x000007, 0x002104, 0x036186, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x07C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00788D, 0x000810, 0x08043A, 0x2A1206, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x070206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x229086, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x225886, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x08DA86, 0x00057D, 0x002820, + 0x03B060, 0x08F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x08FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x009D8D, 0x000810, + 0x08043A, 0x2A1206, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x0A5186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x00107D, 0x018042, + 0x08000A, 0x000215, 0x010984, 0x3A8186, + 0x000007, 0x007FBD, 0x383DC4, 0x000007, + 0x001A7D, 0x001375, 0x018042, 0x09004A, + 0x10000A, 0x0B8D04, 0x139504, 0x000007, + 0x000820, 0x019060, 0x001104, 0x225886, + 0x010040, 0x0017FD, 0x018042, 0x08000A, + 0x000904, 0x225A86, 0x000007, 0x00197D, + 0x038042, 0x09804A, 0x10000A, 0x000924, + 0x001664, 0x0011FD, 0x038042, 0x2B804A, + 0x19804A, 0x00008D, 0x218944, 0x000007, + 0x002244, 0x0C1986, 0x000007, 0x001A64, + 0x002A24, 0x00197D, 0x080102, 0x100122, + 0x000820, 0x039060, 0x018040, 0x003DFD, + 0x00008D, 0x000820, 0x018040, 0x001375, + 0x001A7D, 0x010042, 0x09804A, 0x10000A, + 0x00021D, 0x0189E4, 0x2992E4, 0x309144, + 0x000007, 0x00060D, 0x000A15, 0x000C1D, + 0x001025, 0x00A9E4, 0x012BE4, 0x000464, + 0x01B3E4, 0x0232E4, 0x000464, 0x000464, + 0x000464, 0x000464, 0x00040D, 0x08B1C4, + 0x000007, 0x000820, 0x000BF5, 0x030040, + 0x00197D, 0x038042, 0x09804A, 0x000A24, + 0x08000A, 0x080E64, 0x000007, 0x100122, + 0x000820, 0x031060, 0x010040, 0x0064AC, + 0x00027D, 0x000020, 0x018040, 0x00107D, + 0x018042, 0x0011FD, 0x3B804A, 0x09804A, + 0x20000A, 0x000095, 0x1A1144, 0x00A144, + 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, + 0x0018FD, 0x018042, 0x0010FD, 0x09804A, + 0x28000A, 0x000095, 0x010924, 0x002A64, + 0x0E4986, 0x000007, 0x002904, 0x0E5A86, + 0x000007, 0x0E6206, 0x080002, 0x00008D, + 0x00387D, 0x000820, 0x018040, 0x00127D, + 0x018042, 0x10000A, 0x003904, 0x0F0986, + 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, + 0x000025, 0x0FB206, 0x00002D, 0x000015, + 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, + 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, + 0x400025, 0x00008D, 0x110944, 0x000007, + 0x00018D, 0x109504, 0x000007, 0x009164, + 0x000424, 0x000424, 0x000424, 0x100102, + 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, + 0x00018D, 0x00042D, 0x00008D, 0x109504, + 0x000007, 0x00020D, 0x109184, 0x000007, + 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, + 0x018040, 0x003BFD, 0x001020, 0x03A860, + 0x000815, 0x313184, 0x212184, 0x000007, + 0x03B060, 0x03A060, 0x018040, 0x0022FD, + 0x000095, 0x010924, 0x000424, 0x000424, + 0x001264, 0x100102, 0x000820, 0x039060, + 0x018040, 0x001924, 0x010F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x000424, 0x000424, + 0x00117D, 0x018042, 0x08000A, 0x000A24, + 0x280502, 0x280C02, 0x09800D, 0x000820, + 0x0002FD, 0x018040, 0x200007, 0x0022FD, + 0x018042, 0x08000A, 0x000095, 0x280DC4, + 0x011924, 0x00197D, 0x018042, 0x0011FD, + 0x09804A, 0x10000A, 0x0000B5, 0x113144, + 0x0A8D04, 0x000007, 0x080A44, 0x129504, + 0x000007, 0x0023FD, 0x001020, 0x038040, + 0x101244, 0x000007, 0x000820, 0x039060, + 0x018040, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x123286, 0x000007, 0x003BFD, + 0x000100, 0x000A10, 0x0B807A, 0x13804A, + 0x090984, 0x000007, 0x000095, 0x013D04, + 0x12B886, 0x10000A, 0x100002, 0x090984, + 0x000007, 0x038042, 0x11804A, 0x090D04, + 0x000007, 0x10000A, 0x090D84, 0x000007, + 0x00257D, 0x000820, 0x018040, 0x00010D, + 0x000810, 0x28143A, 0x00127D, 0x018042, + 0x20000A, 0x00197D, 0x018042, 0x00117D, + 0x31804A, 0x10000A, 0x003124, 0x013B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x300102, + 0x003124, 0x000424, 0x000424, 0x001224, + 0x280502, 0x001A4C, 0x143986, 0x700002, + 0x00002D, 0x030000, 0x00387D, 0x018042, + 0x10000A, 0x146206, 0x002124, 0x0000AD, + 0x100002, 0x00010D, 0x000924, 0x006B24, + 0x014A0D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x003264, 0x00008D, 0x000A24, 0x001020, + 0x00227D, 0x018040, 0x014F8D, 0x000810, + 0x08043A, 0x2B5A06, 0x000007, 0x002820, + 0x00207D, 0x018040, 0x00117D, 0x038042, + 0x13804A, 0x33800A, 0x00387D, 0x018042, + 0x08000A, 0x000904, 0x177286, 0x000007, + 0x00008D, 0x030964, 0x015B0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x380102, 0x000424, + 0x000424, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x280502, 0x001A4C, 0x177186, 0x000007, + 0x032164, 0x00632C, 0x003DFD, 0x018042, + 0x08000A, 0x000095, 0x090904, 0x000007, + 0x000820, 0x001A4C, 0x169986, 0x018040, + 0x030000, 0x16B206, 0x002124, 0x00010D, + 0x000924, 0x006B24, 0x016F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x003A64, 0x000095, + 0x001224, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x171286, 0x000007, 0x01760D, + 0x000810, 0x08043A, 0x2B5A06, 0x000007, + 0x160A06, 0x000007, 0x007020, 0x08010A, + 0x10012A, 0x0020FD, 0x038860, 0x039060, + 0x018040, 0x00227D, 0x018042, 0x003DFD, + 0x08000A, 0x31844A, 0x000904, 0x181086, + 0x18008B, 0x00008D, 0x189904, 0x00312C, + 0x18E206, 0x000007, 0x00324C, 0x186B86, + 0x000007, 0x001904, 0x186886, 0x000007, + 0x000095, 0x199144, 0x00222C, 0x003124, + 0x00636C, 0x000E3D, 0x001375, 0x000BFD, + 0x010042, 0x09804A, 0x10000A, 0x038AEC, + 0x0393EC, 0x00224C, 0x18E186, 0x000007, + 0x00008D, 0x189904, 0x00226C, 0x00322C, + 0x30050A, 0x301DAB, 0x002083, 0x0018FD, + 0x018042, 0x08000A, 0x018924, 0x300502, + 0x001083, 0x001875, 0x010042, 0x10000A, + 0x00008D, 0x010924, 0x001375, 0x330542, + 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, + 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, + 0x006083, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x19B286, 0x000007, 0x001E2D, + 0x0005FD, 0x018042, 0x08000A, 0x028924, + 0x280502, 0x00060D, 0x000810, 0x280C3A, + 0x00008D, 0x000810, 0x28143A, 0x0A808D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x001275, 0x030042, 0x21004A, 0x00008D, + 0x1A0944, 0x000007, 0x01AB8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, + 0x030042, 0x0D004A, 0x10000A, 0x089144, + 0x000007, 0x000820, 0x010040, 0x0025F5, + 0x0A3144, 0x000007, 0x000820, 0x032860, + 0x030040, 0x00217D, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00008D, 0x000124, 0x00012C, 0x000E64, + 0x001A64, 0x00636C, 0x08010A, 0x10012A, + 0x000820, 0x031060, 0x030040, 0x0020FD, + 0x018042, 0x08000A, 0x00227D, 0x018042, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00197D, 0x018042, 0x08000A, 0x0022FD, + 0x038042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x090D04, 0x000007, 0x000820, + 0x030040, 0x038042, 0x0B804A, 0x10000A, + 0x000820, 0x031060, 0x030040, 0x038042, + 0x13804A, 0x19804A, 0x110D04, 0x198D04, + 0x000007, 0x08000A, 0x001020, 0x031860, + 0x030860, 0x030040, 0x00008D, 0x0B0944, + 0x000007, 0x000820, 0x010040, 0x0005F5, + 0x030042, 0x08000A, 0x000820, 0x010040, + 0x0000F5, 0x010042, 0x08000A, 0x000904, + 0x1D9886, 0x001E75, 0x030042, 0x01044A, + 0x000C0A, 0x1DAA06, 0x000007, 0x000402, + 0x000C02, 0x00177D, 0x001AF5, 0x018042, + 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, + 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, + 0x00043D, 0x0013F5, 0x001AFD, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x005502, 0x005D0A, 0x030042, 0x0B004A, + 0x1B804A, 0x13804A, 0x20000A, 0x089144, + 0x19A144, 0x0389E4, 0x0399EC, 0x006502, + 0x006D0A, 0x030042, 0x0B004A, 0x19004A, + 0x2B804A, 0x13804A, 0x21804A, 0x30000A, + 0x089144, 0x19A144, 0x2AB144, 0x0389E4, + 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, + 0x000702, 0x00107D, 0x000415, 0x018042, + 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, + 0x0019FD, 0x010042, 0x09804A, 0x10000A, + 0x000934, 0x001674, 0x0029F5, 0x010042, + 0x10000A, 0x00917C, 0x002075, 0x010042, + 0x08000A, 0x000904, 0x200A86, 0x0026F5, + 0x0027F5, 0x030042, 0x09004A, 0x10000A, + 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, + 0x010042, 0x51804A, 0x48000A, 0x160007, + 0x001075, 0x010042, 0x282C0A, 0x281D12, + 0x282512, 0x001F32, 0x1E0007, 0x0E0007, + 0x001975, 0x010042, 0x002DF5, 0x0D004A, + 0x10000A, 0x009144, 0x20EA86, 0x010042, + 0x28340A, 0x000E5D, 0x00008D, 0x000375, + 0x000820, 0x010040, 0x05D2F4, 0x54D104, + 0x00735C, 0x218B86, 0x000007, 0x0C0007, + 0x080007, 0x0A0007, 0x02178D, 0x000810, + 0x08043A, 0x34B206, 0x000007, 0x219206, + 0x000007, 0x080007, 0x002275, 0x010042, + 0x20000A, 0x002104, 0x225886, 0x001E2D, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x21CA86, 0x000007, 0x002010, 0x30043A, + 0x00057D, 0x0180C3, 0x08000A, 0x028924, + 0x280502, 0x280C02, 0x0A810D, 0x000820, + 0x0002F5, 0x010040, 0x220007, 0x0004FD, + 0x018042, 0x70000A, 0x030000, 0x007020, + 0x07FA06, 0x018040, 0x022B8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x22C286, + 0x000007, 0x020206, 0x000007, 0x000875, + 0x0009FD, 0x00010D, 0x234206, 0x000295, + 0x000B75, 0x00097D, 0x00000D, 0x000515, + 0x010042, 0x18000A, 0x001904, 0x2A0086, + 0x0006F5, 0x001020, 0x010040, 0x0004F5, + 0x000820, 0x010040, 0x000775, 0x010042, + 0x09804A, 0x10000A, 0x001124, 0x000904, + 0x23F286, 0x000815, 0x080102, 0x101204, + 0x241206, 0x000575, 0x081204, 0x000007, + 0x100102, 0x000575, 0x000425, 0x021124, + 0x100102, 0x000820, 0x031060, 0x010040, + 0x001924, 0x2A0086, 0x00008D, 0x000464, + 0x009D04, 0x291086, 0x180102, 0x000575, + 0x010042, 0x28040A, 0x00018D, 0x000924, + 0x280D02, 0x00000D, 0x000924, 0x281502, + 0x10000D, 0x000820, 0x0002F5, 0x010040, + 0x200007, 0x001175, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x24FA86, 0x000007, + 0x000100, 0x080B20, 0x130B60, 0x1B0B60, + 0x030A60, 0x010040, 0x050042, 0x3D004A, + 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, + 0x010042, 0x28140A, 0x0004F5, 0x010042, + 0x08000A, 0x000315, 0x010D04, 0x260286, + 0x004015, 0x000095, 0x010D04, 0x25F086, + 0x100022, 0x10002A, 0x261A06, 0x000007, + 0x333104, 0x2AA904, 0x000007, 0x032124, + 0x280502, 0x284402, 0x001124, 0x400102, + 0x000424, 0x000424, 0x003224, 0x00292C, + 0x00636C, 0x277386, 0x000007, 0x02B164, + 0x000464, 0x000464, 0x00008D, 0x000A64, + 0x280D02, 0x10008D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x00008D, 0x38B904, + 0x000007, 0x03296C, 0x30010A, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x270286, + 0x000007, 0x00212C, 0x28050A, 0x00316C, + 0x00046C, 0x00046C, 0x28450A, 0x001124, + 0x006B64, 0x100102, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x004124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x27FA86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x284086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x284A86, 0x000007, 0x284402, 0x003124, + 0x300502, 0x003924, 0x300583, 0x000883, + 0x0005F5, 0x010042, 0x28040A, 0x00008D, + 0x008124, 0x280D02, 0x00008D, 0x008124, + 0x281502, 0x10018D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001025, 0x000575, + 0x030042, 0x09004A, 0x10000A, 0x0A0904, + 0x121104, 0x000007, 0x001020, 0x050860, + 0x050040, 0x0006FD, 0x018042, 0x09004A, + 0x10000A, 0x0000A5, 0x0A0904, 0x121104, + 0x000007, 0x000820, 0x019060, 0x010040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x29CA86, 0x000007, 0x244206, 0x000007, + 0x000606, 0x000007, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x2A1A86, 0x000007, + 0x000100, 0x080B20, 0x138B60, 0x1B8B60, + 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, + 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, + 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, + 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, + 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, + 0x038A60, 0x000606, 0x018040, 0x00008D, + 0x000A64, 0x280D02, 0x000A24, 0x00027D, + 0x018042, 0x10000A, 0x001224, 0x0003FD, + 0x018042, 0x08000A, 0x000904, 0x2C0A86, + 0x000007, 0x00018D, 0x000A24, 0x000464, + 0x000464, 0x080102, 0x000924, 0x000424, + 0x000424, 0x100102, 0x02000D, 0x009144, + 0x2C6186, 0x000007, 0x0001FD, 0x018042, + 0x08000A, 0x000A44, 0x2C4386, 0x018042, + 0x0A000D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00027D, 0x001020, 0x000606, + 0x018040, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x2CB286, 0x000007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2CE286, + 0x000007, 0x000075, 0x002E7D, 0x010042, + 0x0B804A, 0x000020, 0x000904, 0x000686, + 0x010040, 0x31844A, 0x30048B, 0x000883, + 0x00008D, 0x000810, 0x28143A, 0x00008D, + 0x000810, 0x280C3A, 0x000675, 0x010042, + 0x08000A, 0x003815, 0x010924, 0x280502, + 0x0B000D, 0x000820, 0x0002F5, 0x010040, + 0x000606, 0x220007, 0x000464, 0x000464, + 0x000606, 0x000007, 0x000134, 0x007F8D, + 0x00093C, 0x281D12, 0x282512, 0x001F32, + 0x0E0007, 0x00010D, 0x00037D, 0x000820, + 0x018040, 0x05D2F4, 0x000007, 0x080007, + 0x00037D, 0x018042, 0x08000A, 0x000904, + 0x2E8A86, 0x000007, 0x000606, 0x000007, + 0x000007, 0x000012, 0x100007, 0x320007, + 0x600007, 0x460007, 0x100080, 0x48001A, + 0x004904, 0x2EF186, 0x000007, 0x001210, + 0x58003A, 0x000145, 0x5C5D04, 0x000007, + 0x000080, 0x48001A, 0x004904, 0x2F4186, + 0x000007, 0x001210, 0x50003A, 0x005904, + 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, + 0x7FFF7D, 0x07D524, 0x004224, 0x500102, + 0x200502, 0x000082, 0x40001A, 0x004104, + 0x2FC986, 0x000007, 0x003865, 0x40001A, + 0x004020, 0x00104D, 0x04C184, 0x31AB86, + 0x000040, 0x040007, 0x000165, 0x000145, + 0x004020, 0x000040, 0x000765, 0x080080, + 0x40001A, 0x004104, 0x305986, 0x000007, + 0x001210, 0x40003A, 0x004104, 0x30B286, + 0x00004D, 0x0000CD, 0x004810, 0x20043A, + 0x000882, 0x40001A, 0x004104, 0x30C186, + 0x000007, 0x004820, 0x005904, 0x319886, + 0x000040, 0x0007E5, 0x200480, 0x2816A0, + 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, + 0x000040, 0x000032, 0x400075, 0x00007D, + 0x07D574, 0x200512, 0x000082, 0x40001A, + 0x004104, 0x317186, 0x000007, 0x038A06, + 0x640007, 0x0000E5, 0x000020, 0x000040, + 0x000A65, 0x000020, 0x020040, 0x020040, + 0x000040, 0x000165, 0x000042, 0x70000A, + 0x007104, 0x323286, 0x000007, 0x060007, + 0x019A06, 0x640007, 0x050000, 0x007020, + 0x000040, 0x038A06, 0x640007, 0x000007, + 0x00306D, 0x028860, 0x029060, 0x08000A, + 0x028860, 0x008040, 0x100012, 0x00100D, + 0x009184, 0x32D186, 0x000E0D, 0x009184, + 0x33E186, 0x000007, 0x300007, 0x001020, + 0x003B6D, 0x008040, 0x000080, 0x08001A, + 0x000904, 0x32F186, 0x000007, 0x001220, + 0x000DED, 0x008040, 0x008042, 0x10000A, + 0x40000D, 0x109544, 0x000007, 0x001020, + 0x000DED, 0x008040, 0x008042, 0x20040A, + 0x000082, 0x08001A, 0x000904, 0x338186, + 0x000007, 0x003B6D, 0x008042, 0x08000A, + 0x000E15, 0x010984, 0x342B86, 0x600007, + 0x08001A, 0x000C15, 0x010984, 0x341386, + 0x000020, 0x1A0007, 0x0002ED, 0x008040, + 0x620007, 0x00306D, 0x028042, 0x0A804A, + 0x000820, 0x0A804A, 0x000606, 0x10804A, + 0x000007, 0x282512, 0x001F32, 0x05D2F4, + 0x54D104, 0x00735C, 0x000786, 0x000007, + 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, + 0x020040, 0x004820, 0x025060, 0x40000A, + 0x024060, 0x000040, 0x454944, 0x000007, + 0x004020, 0x003AE5, 0x000040, 0x0028E5, + 0x000042, 0x48000A, 0x004904, 0x39F886, + 0x002C65, 0x000042, 0x40000A, 0x0000D5, + 0x454104, 0x000007, 0x000655, 0x054504, + 0x368286, 0x0001D5, 0x054504, 0x368086, + 0x002B65, 0x000042, 0x003AE5, 0x50004A, + 0x40000A, 0x45C3D4, 0x000007, 0x454504, + 0x000007, 0x0000CD, 0x444944, 0x000007, + 0x454504, 0x000007, 0x00014D, 0x554944, + 0x000007, 0x045144, 0x367986, 0x002C65, + 0x000042, 0x48000A, 0x4CD104, 0x000007, + 0x04C144, 0x368386, 0x000007, 0x160007, + 0x002CE5, 0x040042, 0x40000A, 0x004020, + 0x000040, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x36F086, 0x000007, 0x002402, + 0x383206, 0x005C02, 0x0025E5, 0x000042, + 0x40000A, 0x004274, 0x002AE5, 0x000042, + 0x40000A, 0x004274, 0x500112, 0x0029E5, + 0x000042, 0x40000A, 0x004234, 0x454104, + 0x000007, 0x004020, 0x000040, 0x003EE5, + 0x000020, 0x000040, 0x002DE5, 0x400152, + 0x50000A, 0x045144, 0x37DA86, 0x0000C5, + 0x003EE5, 0x004020, 0x000040, 0x002BE5, + 0x000042, 0x40000A, 0x404254, 0x000007, + 0x002AE5, 0x004020, 0x000040, 0x500132, + 0x040134, 0x005674, 0x0029E5, 0x020042, + 0x42000A, 0x000042, 0x50000A, 0x05417C, + 0x0028E5, 0x000042, 0x48000A, 0x0000C5, + 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, + 0x020042, 0x40004A, 0x50000A, 0x00423C, + 0x00567C, 0x0028E5, 0x004820, 0x000040, + 0x281D12, 0x282512, 0x001F72, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x393A86, + 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, + 0x000042, 0x40000A, 0x004104, 0x397886, + 0x002D65, 0x000042, 0x28340A, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, + 0x39E186, 0x000007, 0x000606, 0x080007, + 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, + 0x020045, 0x004020, 0x000060, 0x000365, + 0x000040, 0x002E65, 0x001A20, 0x0A1A60, + 0x000040, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x000606, 0x50004A, + 0x0017FD, 0x018042, 0x08000A, 0x000904, + 0x225A86, 0x000007, 0x00107D, 0x018042, + 0x0011FD, 0x33804A, 0x19804A, 0x20000A, + 0x000095, 0x2A1144, 0x01A144, 0x3B9086, + 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, + 0x018042, 0x0010FD, 0x09804A, 0x38000A, + 0x000095, 0x010924, 0x003A64, 0x3B8186, + 0x000007, 0x003904, 0x3B9286, 0x000007, + 0x3B9A06, 0x00000D, 0x00008D, 0x000820, + 0x00387D, 0x018040, 0x700002, 0x00117D, + 0x018042, 0x00197D, 0x29804A, 0x30000A, + 0x380002, 0x003124, 0x000424, 0x000424, + 0x002A24, 0x280502, 0x00068D, 0x000810, + 0x28143A, 0x00750D, 0x00B124, 0x002264, + 0x3D0386, 0x284402, 0x000810, 0x280C3A, + 0x0B800D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00758D, 0x00B124, 0x100102, + 0x012144, 0x3E4986, 0x001810, 0x10003A, + 0x00387D, 0x018042, 0x08000A, 0x000904, + 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, + 0x00008D, 0x023164, 0x000A64, 0x280D02, + 0x0B808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00387D, 0x018042, 0x08000A, + 0x000904, 0x3E3286, 0x030000, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x3D8286, + 0x000007, 0x002810, 0x28043A, 0x00750D, + 0x030924, 0x002264, 0x280D02, 0x02316C, + 0x28450A, 0x0B810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x00008D, 0x000A24, + 0x3E4A06, 0x100102, 0x001810, 0x10003A, + 0x0000BD, 0x003810, 0x30043A, 0x00187D, + 0x018042, 0x0018FD, 0x09804A, 0x20000A, + 0x0000AD, 0x028924, 0x07212C, 0x001010, + 0x300583, 0x300D8B, 0x3014BB, 0x301C83, + 0x002083, 0x00137D, 0x038042, 0x33844A, + 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, + 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, + 0x001E0D, 0x0005FD, 0x018042, 0x20000A, + 0x020924, 0x00068D, 0x00A96C, 0x00009D, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x3F6A86, 0x000007, 0x280502, 0x280D0A, + 0x284402, 0x001810, 0x28143A, 0x0C008D, + 0x000820, 0x0002FD, 0x018040, 0x220007, + 0x003904, 0x225886, 0x001E0D, 0x00057D, + 0x018042, 0x20000A, 0x020924, 0x0000A5, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x402A86, 0x000007, 0x280502, 0x280C02, + 0x002010, 0x28143A, 0x0C010D, 0x000820, + 0x0002FD, 0x018040, 0x225A06, 0x220007, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +#endif //_HWMCODE_ + + diff -Nru a/sound/oss/CHANGELOG b/sound/oss/CHANGELOG --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/CHANGELOG Tue Feb 19 18:08:57 2002 @@ -0,0 +1,369 @@ +Note these changes relate to Hannu's code and don't include the changes +made outside of this for modularising the sound + +Changelog for version 3.8o +-------------------------- + +Since 3.8h +- Included support for OPL3-SA1 and SoftOSS + +Since 3.8 +- Fixed SNDCTL_DSP_GETOSPACE +- Compatibility fixes for Linux 2.1.47 + +Since 3.8-beta21 +- Fixed all known bugs (I think). + +Since 3.8-beta8 +- Lot of fixes to audio playback code in dmabuf.c + +Since 3.8-beta6 +- Fixed the famous Quake delay bug. + +Since 3.8-beta5 +- Fixed many bugs in audio playback. + +Since 3.8-beta4 +- Just minor changes. + +Since 3.8-beta1 +- Major rewrite of audio playback handling. +- Added AWE32 support by Takashi Iwai (in ./lowlevel/). + +Since 3.7-beta# +- Passing of ioctl() parameters between soundcard.c and other modules has been +changed so that arg always points to kernel space. +- Some bugfixes. + +Since 3.7-beta5 +- Disabled MIDI input with GUS PnP (Interwave). There seems to be constant +stream of received 0x00 bytes when the MIDI receiver is enabled. + +Since 3.5 +- Changes almost everywhere. +- Support for OPTi 82C924-based sound cards. + +Since 3.5.4-beta8 +- Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode + with GUS. +- Limited minimum fragment size with some audio devices (GUS=512 and + SB=32). These devices require more time to "recover" from processing + of each fragment. + +Since 3.5.4-beta6/7 +- There seems to be problems in the OPTi 82C930 so cards based on this + chip don't necessarily work yet. There are problems in detecting the + MIDI interface. Also mixer volumes may be seriously wrong on some systems. + You can safely use this driver version with C930 if it looks to work. + However please don't complain if you have problems with it. C930 support + should be fixed in future releases. +- Got initialization of GUS PnP to work. With this version GUS PnP should + work in GUS compatible mode after initialization using isapnptools. +- Fixed a bug in handling of full duplex cards in write only mode. This has + been causing "audio device opening" errors with RealAudio player. + +Since 3.5.4.beta5 +- Changes to OPTi 82C930 driver. +- Major changes to the Soundscape driver. The driver requires now just one + DMA channel. The extra audio/dsp device (the "Not functional" one) used + for code download in the earlier versions has been eliminated. There is now + just one /dev/dsp# device which is used both for code download and audio. + +Since 3.5.4.beta4 +- Minor changes. + +Since 3.5.4-beta2 +- Fixed silent playback with ESS 688/1688. +- Got SB16 to work without the 16 bit DMA channel (only the 8 bit one + is required for 8 and 16 bit modes). +- Added the "lowlevel" subdirectory for additional low level drivers that + are not part of USS core. See lowlevel/README for more info. +- Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in + miroPCM sound cards. See lowlevel/aci.readme for more info. +- Support for Aztech Washington chipset (AZT2316 ASIC). + +Since 3.5.4-beta1 +- Reduced clicking with AD1848. +- Support for OPTi 82C930. Only half duplex at this time. 16 bit playback + is sometimes just white noise (occurs randomly). + +Since 3.5.2 +- Major changes to the SB/Jazz16/ESS driver (most parts rewritten). + The most noticeable new feature is support for multiple SB cards at the same + time. +- Renamed sb16_midi.c to uart401.c. Also modified it to work also with + other MPU401 UART compatible cards than SB16/ESS/Jazz. +- Some changes which reduce clicking in audio playback. +- Copying policy is now GPL. + +Since 3.5.1 +- TB Maui initialization support +Since 3.5 +- Improved handling of playback underrun situations. + +Since 3.5-beta10 +- Bug fixing + +Since 3.5-beta9 +- Fixed for compatibility with Linux 1.3.70 and later. +- Changed boot time passing of 16 bit DMA channel number to SB driver. + +Since 3.5-beta8 +- Minor changes + +Since 3.5-beta7 +- enhancements to configure program (by Jeff Tranter): + - prompts are in same format as 1.3.x Linux kernel config program + - on-line help for each question + - fixed some compile warnings detected by gcc/g++ -Wall + - minor grammatical changes to prompts + +Since 3.5-beta6 +- Fixed bugs in mmap() support. +- Minor changes to Maui driver. + +Since 3.5-beta5 +- Fixed crash after recording with ESS688. It's generally a good + idea to stop inbound DMA transfers before freeing the memory + buffer. +- Fixed handling of AD1845 codec (for example Shuttle Sound System). +- Few other fixes. + +Since 3.5-beta4 +- Fixed bug in handling of uninitialized instruments with GUS. + +Since 3.5-beta3 +- Few changes which decrease popping at end/beginning of audio playback. + +Since 3.5-beta2 +- Removed MAD16+CS4231 hack made in previous version since it didn't + help. +- Fixed the above bug in proper way and in proper place. Many thanks + to James Hightower. + +Since 3.5-beta1 +- Bug fixes. +- Full duplex audio with MAD16+CS4231 may work now. The driver configures + SB DMA of MAD16 so that it doesn't conflict with codec's DMA channels. + The side effect is that all 8 bit DMA channels (0,1,3) are populated in + duplex mode. + +Since 3.5-alpha9 +- Bug fixes (mostly in Jazz16 and ESS1688/688 supports). +- Temporarily disabled recording with ESS1688/688 since it causes crash. +- Changed audio buffer partitioning algorithm so that it selects + smaller fragment size than earlier. This improves real time capabilities + of the driver and makes recording to disk to work better. Unfortunately + this change breaks some programs which assume that fragments cannot be + shorter than 4096 bytes. + +Since 3.5-alpha8 +- Bug fixes + +Since 3.5-alpha7 +- Linux kernel compatible configuration (_EXPERIMENTAL_). Enable + using command "cd /linux/drivers/sound;make script" and then + just run kernel's make config normally. +- Minor fixes to the SB support. Hopefully the driver works with + all SB models now. +- Added support for ESS ES1688 "AudioDrive" based cards. + +Since 3.5-alpha6 +- SB Pro and SB16 supports are no longer separately selectable options. + Enabling SB enables them too. +- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified +configure to handle this. +- Removed initialization messages from the +modularized version. They can be enabled by using init_trace=1 in +the insmod command line (insmod sound init_trace=1). +- More AIX stuff. +- Added support for synchronizing dsp/audio devices with /dev/sequencer. +- mmap() support for dsp/audio devices. + +Since 3.5-alpha5 +- AIX port. +- Changed some xxx_PATCH macros in soundcard.h to work with + big endian machines. + +Since 3.5-alpha4 +- Removed the 'setfx' stuff from the version distributed with kernel + sources. Running 'setfx' is required again. + +Since 3.5-alpha3 +- Moved stuff from the 'setfx' program to the AudioTrix Pro driver. + +Since 3.5-alpha2 +- Modifications to makefile and configure.c. Unnecessary sources + are no longer compiled. Newly created local.h is also copied to + /etc/soundconf. "make oldconfig" reads /etc/soundconf and produces + new local.h which is compatible with current version of the driver. +- Some fixes to the SB16 support. +- Fixed random protection fault in gus_wave.c + +Since 3.5-alpha1 +- Modified to work with Linux-1.3.33 and later +- Some minor changes + +Since 3.0.2 +- Support for CS4232 based PnP cards (AcerMagic S23 etc). +- Full duplex support for some CS4231, CS4232 and AD1845 based cards +(GUS MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards +having a codec mentioned above). +- Almost fully rewritten loadable modules support. +- Fixed some bugs. +- Huge amount of testing (more testing is still required). +- mmap() support (works with some cards). Requires much more testing. +- Sample/patch/program loading for TB Maui/Tropez. No initialization +since TB doesn't allow me to release that code. +- Using CS4231 compatible codecs as timer for /dev/music. + +Since 3.0.1 +- Added allocation of I/O ports, DMA channels and interrupts +to the initialization code. This may break modules support since +the driver may not free some resources on unload. Should be fixed soon. + +Since 3.0 +- Some important bug fixes. +- select() for /dev/dsp and /dev/audio (Linux only). +(To use select() with read, you have to call read() to start +the recording. Calling write() kills recording immediately so +use select() carefully when you are writing a half duplex app. +Full duplex mode is not implemented yet.) Select works also with +/dev/sequencer and /dev/music. Maybe with /dev/midi## too. + +Since 3.0-beta2 +- Minor fixes. +- Added Readme.cards + +Since 3.0-beta1 +- Minor fixes to the modules support. +- Eliminated call to sb_free_irq() in ad1848.c +- Rewritten MAD16&Mozart support (not tested with MAD16 Pro). +- Fix to DMA initialization of PSS cards. +- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.) +- Fixed some bugs in the PSS driver which caused I/O errors with + the MSS mode (/dev/dsp). + +Since 3.0-950506 +- Recording with GUS MAX fixed. It works when the driver is configured + to use two DMA channels with GUS MAX (16 bit ones recommended). + +Since 3.0-94xxxx +- Too many changes + +Since 3.0-940818 +- Fixes for Linux 1.1.4x. +- Disables Disney Sound System with SG NX Pro 16 (less noise). + +Since 2.90-2 +- Fixes to soundcard.h +- Non blocking mode to /dev/sequencer +- Experimental detection code for Ensoniq Soundscape. + +Since 2.90 +- Minor and major bug fixes + +Since pre-3.0-940712 +- GUS MAX support +- Partially working MSS/WSS support (could work with some cards). +- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs + (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and + GUS MAX, but it doesn't work yet. +Since pre-3.0-940426 +- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc). +This codec chip is used in various sound cards. This version is developed +for the 16 bit daughtercard of GUS. It should work with other cards also +if the following requirements are met: + - The I/O, IRQ and DMA settings are jumper selectable or + the card is initialized by booting DOS before booting Linux (etc.). + - You add the IO, IRQ and DMA settings manually to the local.h. + (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that + the base address bust be the base address of the codec chip not the + card itself. For the GUS16 these are the same but most MSS compatible + cards have the codec located at card_base+4. +- Some minor changes + +Since 2.5 (******* MAJOR REWRITE ***********) + +This version is based on v2.3. I have tried to maintain two versions +together so that this one should have the same features than v2.5. +Something may still be missing. If you notice such things, please let me +know. + +The Readme.v30 contains more details. + +- /dev/midi## devices. +- /dev/sequencer2 + +Since 2.5-beta2 +- Some fine tuning to the GUS v3.7 mixer code. +- Fixed speed limits for the plain SB (1.0 to 2.0). + +Since 2.5-beta +- Fixed OPL-3 detection with SB. Caused problems with PAS16. +- GUS v3.7 mixer support. + +Since 2.4 +- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h). +- Fixed truncated sound on /dev/dsp when the device is closed. +- Linear volume mode for GUS +- Pitch bends larger than +/- 2 octaves. +- MIDI recording for SB and SB Pro. (Untested). +- Some other fixes. +- SB16 MIDI and DSP drivers only initialized if SB16 actually installed. +- Implemented better detection for OPL-3. This should be useful if you + have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3. +- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested). + +Since 2.3b +- Fixed bug which made it impossible to make long recordings to disk. + Recording was not restarted after a buffer overflow situation. +- Limited mixer support for GUS. +- Numerous improvements to the GUS driver by Andrew Robinson. Including + some click removal etc. + +Since 2.3 +- Fixed some minor bugs in the SB16 driver. + +Since 2.2b +- Full SB16 DSP support. 8/16 bit, mono/stereo +- The SCO and FreeBSD versions should be in sync now. There are some + problems with SB16 and GUS in the FreeBSD versions. + The DMA buffer allocation of the SCO version has been polished but + there could still be some problems. At least it hogs memory. + The DMA channel + configuration method used in the SCO/System is a hack. +- Support for the MPU emulation of the SB16. +- Some big arrays are now allocated boot time. This makes the BSS segment + smaller which makes it possible to use the full driver with + NetBSD. These arrays are not allocated if no suitable sound card is available. +- Fixed a bug in the compute_and_set_volume in gus_wave.c +- Fixed the too fast mono playback problem of SB Pro and PAS16. + +Since 2.2 +- Stereo recording for SB Pro. Somehow it was missing and nobody + had noticed it earlier. +- Minor polishing. +- Interpreting of boot time arguments (sound=) for Linux. +- Breakup of sb_dsp.c. Parts of the code has been moved to + sb_mixer.c and sb_midi.c + +Since 2.1 +- Preliminary support for SB16. + - The SB16 mixer is supported in its native mode. + - Digitized voice capability up to 44.1 kHz/8 bit/mono + (16 bit and stereo support coming in the next release). +- Fixed some bugs in the digitized voice driver for PAS16. +- Proper initialization of the SB emulation of latest PAS16 models. + +- Significantly improved /dev/dsp and /dev/audio support. + - Now supports half duplex mode. It's now possible to record and + playback without closing and reopening the device. + - It's possible to use smaller buffers than earlier. There is a new + ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4. + This call instructs the driver to use smaller buffers. The default + buffer size (0.5 to 1.0 seconds) is divided by n. Should be called + immediately after opening the device. + +Since 2.0 +Just cosmetic changes. diff -Nru a/sound/oss/COPYING b/sound/oss/COPYING --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/COPYING Tue Feb 19 18:08:59 2002 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff -Nru a/sound/oss/Config.help b/sound/oss/Config.help --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/Config.help Tue Feb 19 18:08:57 2002 @@ -0,0 +1,723 @@ +CONFIG_INPUT_GAMEPORT + Gameport support is for the standard 15-pin PC gameport. If you + have a joystick, gamepad, gameport card, a soundcard with a gameport + or anything else that uses the gameport, say Y or M here and also to + at least one of the hardware specific drivers. + Please read the file which + contains more information and the location of the joystick package + that you'll need if you use the gameport with a joystick. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called gameport.o. If you want to compile it as + a module, say M here and read . + +CONFIG_SOUND_OSS + OSS is the Open Sound System suite of sound card drivers. They make + sound programming easier since they provide a common API. Say Y or + M here (the module will be called sound.o) if you haven't found a + driver for your sound card above, then pick your driver from the + list below. + +CONFIG_SOUND_DMAP + Linux can often have problems allocating DMA buffers for ISA sound + cards on machines with more than 16MB of RAM. This is because ISA + DMA buffers must exist below the 16MB boundary and it is quite + possible that a large enough free block in this region cannot be + found after the machine has been running for a while. If you say Y + here the DMA buffers (64Kb) will be allocated at boot time and kept + until the shutdown. This option is only useful if you said Y to + "OSS sound modules", above. If you said M to "OSS sound modules" + then you can get the persistent DMA buffer functionality by passing + the command-line argument "dmabuf=1" to the sound.o module. + + Say Y unless you have 16MB or more RAM or a PCI sound card. + +CONFIG_SOUND_SGALAXY + This module initializes the older non Plug and Play sound galaxy + cards from Aztech. It supports the Waverider Pro 32 - 3D and the + Galaxy Washington 16. + + If you compile the driver into the kernel, you have to add + "sgalaxy=,,,," to the kernel command + line. + +CONFIG_SOUND_AD1816 + Say M here if you have a sound card based on the Analog Devices + AD1816(A) chip. + + If you compile the driver into the kernel, you have to add + "ad1816=,,," to the kernel command line. + +CONFIG_SOUND_OPL3SA1 + Say Y or M if you have a Yamaha OPL3-SA1 sound chip, which is + usually built into motherboards. Read + for details. + + If you compile the driver into the kernel, you have to add + "opl3sa=,,,,," to the kernel + command line. + +CONFIG_SOUND_PAS + Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio + 16 or Logitech SoundMan 16 sound card. Answer N if you have some + other card made by Media Vision or Logitech since those are not + PAS16 compatible. Please read . + It is not necessary to add Sound Blaster support separately; it + is included in PAS support. + + If you compile the driver into the kernel, you have to add + "pas2=,,,,,,, + to the kernel command line. + +CONFIG_PAS_JOYSTICK + Say Y here to enable the Pro Audio Spectrum 16's auxiliary joystick + port. + +CONFIG_SOUND_SB + Answer Y if you have an original Sound Blaster card made by Creative + Labs or a 100% hardware compatible clone (like the Thunderboard or + SM Games). For an unknown card you may answer Y if the card claims + to be Sound Blaster-compatible. + + Please read the file . + + You should also say Y here for cards based on the Avance Logic + ALS-007 and ALS-1X0 chips (read ) and + for cards based on ESS chips (read + and + ). If you have an SB AWE 32 or SB AWE + 64, say Y here and also to "AWE32 synth" below and read + . If you have an IBM Mwave + card, say Y here and read . + + If you compile the driver into the kernel and don't want to use + isapnp, you have to add "sb=,,," to the kernel + command line. + + You can say M here to compile this driver as a module; the module is + called sb.o. + +CONFIG_SOUND_GUS + Say Y here for any type of Gravis Ultrasound card, including the GUS + or GUS MAX. See also for more + information on configuring this card with modules. + + If you compile the driver into the kernel, you have to add + "gus=,,," to the kernel command line. + +CONFIG_SOUND_MPU401 + Be careful with this question. The MPU401 interface is supported by + all sound cards. However, some natively supported cards have their + own driver for MPU401. Enabling this MPU401 option with these cards + will cause a conflict. Also, enabling MPU401 on a system that + doesn't really have a MPU401 could cause some trouble. If your card + was in the list of supported cards, look at the card specific + instructions in the file. It + is safe to answer Y if you have a true MPU401 MIDI interface card. + + If you compile the driver into the kernel, you have to add + "mpu401=," to the kernel command line. + +CONFIG_SOUND_UART6850 + This option enables support for MIDI interfaces based on the 6850 + UART chip. This interface is rarely found on sound cards. It's safe + to answer N to this question. + + If you compile the driver into the kernel, you have to add + "uart6850=," to the kernel command line. + +CONFIG_SOUND_PSS + Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven + ADSP-16 or some other card based on the PSS chipset (AD1848 codec + + ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on + how to compile it into the kernel or as a module see the file + . + + If you compile the driver into the kernel, you have to add + "pss=,,,,," to the kernel + command line. + +CONFIG_PSS_MIXER + Answer Y for Beethoven ADSP-16. You may try to say Y also for other + cards if they have master volume, bass, treble, and you can't + control it under Linux. If you answer N for Beethoven ADSP-16, you + can't control master volume, bass, treble and synth volume. + + If you said M to "PSS support" above, you may enable or disable this + PSS mixer with the module parameter pss_mixer. For more information + see the file . + +CONFIG_PSS_HAVE_BOOT + If you have the DSPxxx.LD file or SYNTH.LD file for you card, say Y + to include this file. Without this file the synth device (OPL) may + not work. + +CONFIG_PSS_BOOT_FILE + Enter the full pathname of your DSPxxx.LD file or SYNTH.LD file, + starting from /. + +CONFIG_SOUND_MSS + Again think carefully before answering Y to this question. It's + safe to answer Y if you have the original Windows Sound System card + made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). Also you may + say Y in case your card is NOT among these: + + ATI Stereo F/X, AdLib, Audio Excell DSP16, Cardinal DSP16, + Ensoniq SoundScape (and compatibles made by Reveal and Spea), + Gravis Ultrasound, Gravis Ultrasound ACE, Gravis Ultrasound Max, + Gravis Ultrasound with 16 bit option, Logitech Sound Man 16, + Logitech SoundMan Games, Logitech SoundMan Wave, MAD16 Pro (OPTi + 82C929), Media Vision Jazz16, MediaTriX AudioTriX Pro, Microsoft + Windows Sound System (MSS/WSS), Mozart (OAK OTI-601), Orchid + SW32, Personal Sound System (PSS), Pro Audio Spectrum 16, Pro + Audio Studio 16, Pro Sonic 16, Roland MPU-401 MIDI interface, + Sound Blaster 1.0, Sound Blaster 16, Sound Blaster 16ASP, Sound + Blaster 2.0, Sound Blaster AWE32, Sound Blaster Pro, TI TM4000M + notebook, ThunderBoard, Turtle Beach Tropez, Yamaha FM + synthesizers (OPL2, OPL3 and OPL4), 6850 UART MIDI Interface. + + For cards having native support in VoxWare, consult the card + specific instructions in . + Some drivers have their own MSS support and saying Y to this option + will cause a conflict. + + If you compile the driver into the kernel, you have to add + "ad1848=,,,[,]" to the kernel command + line. + +CONFIG_SOUND_VWSND + Say Y or M if you have an SGI Visual Workstation and you want to be + able to use its on-board audio. Read + for more info on this driver's + capabilities. + +CONFIG_SOUND_SSCAPE + Answer Y if you have a sound card based on the Ensoniq SoundScape + chipset. Such cards are being manufactured at least by Ensoniq, Spea + and Reveal (Reveal makes also other cards). + + If you compile the driver into the kernel, you have to add + "sscape=,,,," to the kernel command + line. + +CONFIG_SOUND_TRIX + Answer Y if you have the AudioTriX Pro sound card manufactured + by MediaTrix. + +CONFIG_TRIX_HAVE_BOOT + The MediaTrix AudioTrix Pro has an on-board microcontroller which + needs to be initialized by downloading the code from the file + TRXPRO.HEX in the DOS driver directory. If you don't have the + TRXPRO.HEX file handy you may skip this step. However, the SB and + MPU-401 modes of AudioTrix Pro will not work without this file! + +CONFIG_TRIX_BOOT_FILE + Enter the full pathname of your TRXPRO.HEX file, starting from /. + +CONFIG_SOUND_MAD16 + Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi + 82C928 or 82C929 or 82C931) audio interface chip. These chips are + quite common so it's possible that many no-name cards have one of + them. In addition the MAD16 chip is used in some cards made by known + manufacturers such as Turtle Beach (Tropez), Reveal (some models) + and Diamond (latest ones). Note however that the Tropez sound cards + have their own driver; if you have one of those, say N here and Y or + M to "Full support for Turtle Beach WaveFront", below. + + If you compile the driver into the kernel, you have to add + "mad16=,,,,," to the + kernel command line. + + See also and + for more information on setting + these cards up as modules. + +CONFIG_SOUND_WAVEFRONT + Answer Y or M if you have a Tropez Plus, Tropez or Maui sound card + and read the files and + . + +CONFIG_MAD16_OLDCARD + Answer Y (or M) if you have an older card based on the C928 or + Mozart chipset and you want to have MIDI support. If you enable this + option you also need to enable support for Sound Blaster. + +CONFIG_SOUND_CS4232 + Say Y here if you have a card based on the Crystal CS4232 chip set, + which uses its own Plug and Play protocol. + + If you compile the driver into the kernel, you have to add + "cs4232=,,,,," to the kernel + command line. + + See for more information on + configuring this card. + +CONFIG_SOUND_OPL3SA2 + Say Y or M if you have a card based on one of these Yamaha sound + chipsets or the "SAx", which is actually a SA3. Read + for more information on + configuring these cards. + + If you compile the driver into the kernel and do not also + configure in the optional ISA PnP support, you will have to add + "opl3sa2=,,,,," to the kernel + command line. + +CONFIG_SOUND_MAUI + Say Y here if you have a Turtle Beach Wave Front, Maui, or Tropez + sound card. + + If you compile the driver into the kernel, you have to add + "maui=," to the kernel command line. + +CONFIG_MAUI_HAVE_BOOT + Turtle Beach Maui and Tropez sound cards have a microcontroller + which needs to be initialized prior to use. OSWF.MOT is a file + distributed with the card's DOS/Windows drivers. Answer Y if you + have this file. + +CONFIG_MAUI_BOOT_FILE + Enter the full pathname of your OSWF.MOT file, starting from /. + +CONFIG_SOUND_MSNDCLAS + Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or + Monterey (not for the Pinnacle or Fiji). + + See for important information + about this driver. Note that it has been discontinued, but the + Voyetra Turtle Beach knowledge base entry for it is still available + at . + +CONFIG_MSNDCLAS_IO + I/O port address for the MultiSound Classic and related cards. + +CONFIG_MSNDCLAS_IRQ + Interrupt Request line for the MultiSound Classic and related cards. + +CONFIG_MSNDCLAS_MEM + Memory-mapped I/O base address for the MultiSound Classic and + related cards. + +CONFIG_MSNDCLAS_INIT_FILE + The MultiSound cards have two firmware files which are required for + operation, and are not currently included. These files can be + obtained from Turtle Beach. See + for information on how to + obtain this. + +CONFIG_MSNDCLAS_PERM_FILE + The MultiSound cards have two firmware files which are required for + operation, and are not currently included. These files can be + obtained from Turtle Beach. See + for information on how to + obtain this. + +CONFIG_SOUND_MSNDPIN + Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji. + See for important information + about this driver. Note that it has been discontinued, but the + Voyetra Turtle Beach knowledge base entry for it is still available + at . + +CONFIG_MSNDPIN_IDE_IO0 + CD-ROM drive 0 memory-mapped I/O base address for the MultiSound + Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_IDE_IO1 + CD-ROM drive 1 memory-mapped I/O base address for the MultiSound + Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_IDE_IRQ + Interrupt request number for the IDE CD-ROM interface on the + MultiSound Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_IO + Memory-mapped I/O base address for the primary synthesizer on + MultiSound Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_MPU_IO + Memory-mapped I/O base address for the Kurzweil daughterboard + synthesizer on MultiSound Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_MPU_IRQ + Iinterrupt request number for the Kurzweil daughterboard + synthesizer on MultiSound Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_IRQ + Interrupt request line for the primary synthesizer on MultiSound + Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_JOYSTICK_IO + Memory-mapped I/O base address for the joystick port on MultiSound + Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_MEM + Memory-mapped I/O base address for the primary synthesizer on + MultiSound Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_INIT_FILE + The MultiSound cards have two firmware files which are required + for operation, and are not currently included. These files can be + obtained from Turtle Beach. See + for information on how to + obtain this. + +CONFIG_MSNDPIN_PERM_FILE + The MultiSound cards have two firmware files which are required for + operation, and are not currently included. These files can be + obtained from Turtle Beach. See + for information on how to + obtain this. + +CONFIG_MSNDPIN_DIGITAL + If you have the S/PDIF daughter board for the Pinnacle or Fiji, + answer Y here; otherwise, say N. If you have this, you will be able + to play and record from the S/PDIF port (digital signal). See + for information on how to make + use of this capability. + +CONFIG_MSNDPIN_NONPNP + The Pinnacle and Fiji card resources can be configured either with + PnP, or through a configuration port. Say Y here if your card is NOT + in PnP mode. For the Pinnacle, configuration in non-PnP mode allows + use of the IDE and joystick peripherals on the card as well; these + do not show up when the card is in PnP mode. Specifying zero for any + resource of a device will disable the device. If you are running the + card in PnP mode, you must say N here and use isapnptools to + configure the card's resources. + +CONFIG_MSNDPIN_CFG + This is the port which the Pinnacle and Fiji uses to configure the + card's resources when not in PnP mode. If your card is in PnP mode, + then be sure to say N to the previous option, "MSND Pinnacle Non-PnP + Mode". + +CONFIG_MSND_FIFOSIZE + Configures the size of each audio buffer, in kilobytes, for + recording and playing in the MultiSound drivers (both the Classic + and Pinnacle). Larger values reduce the chance of data overruns at + the expense of overall latency. If unsure, use the default. + +CONFIG_SOUND_YM3812 + Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + Answering Y is usually a safe and recommended choice, however some + cards may have software (TSR) FM emulation. Enabling FM support with + these cards may cause trouble (I don't currently know of any such + cards, however). Please read the file + if your card has an OPL3 chip. + + If you compile the driver into the kernel, you have to add + "opl3=" to the kernel command line. + + If unsure, say Y. + +CONFIG_SOUND_ACI_MIXER + ACI (Audio Command Interface) is a protocol used to communicate with + the microcontroller on some sound cards produced by miro and + Cardinal Technologies. The main function of the ACI is to control + the mixer and to get a product identification. + + This VoxWare ACI driver currently supports the ACI functions on the + miroSOUND PCM1-pro, PCM12 and PCM20 radio. On the PCM20 radio, ACI + also controls the radio tuner. This is supported in the video4linux + miropcm20 driver (say M or Y here and go back to "Multimedia + devices" -> "Radio Adapters"). + + This driver is also available as a module and will be called aci.o. + +CONFIG_SOUND_AWE32_SYNTH + Say Y here if you have a Sound Blaster SB32, AWE32-PnP, SB AWE64 or + similar sound card. See , + and the Soundblaster-AWE + mini-HOWTO, available from + for more info. + +CONFIG_SOUND_AEDSP16 + Answer Y if you have a Gallant's Audio Excel DSP 16 card. This + driver supports Audio Excel DSP 16 but not the III nor PnP versions + of this card. + + The Gallant's Audio Excel DSP 16 card can emulate either an SBPro or + a Microsoft Sound System card, so you should have said Y to either + "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support" + or "Microsoft Sound System support", above, and you need to answer + the "MSS emulation" and "SBPro emulation" questions below + accordingly. You should say Y to one and only one of these two + questions. + + Read the file and the head of + as well as + to get more information + about this driver and its configuration. + +CONFIG_AEDSP16_SBPRO + Answer Y if you want your audio card to emulate Sound Blaster Pro. + You should then say Y to "100% Sound Blaster compatibles + (SB16/32/64, ESS, Jazz16) support" and N to "Audio Excel DSP 16 (MSS + emulation)". + + If you compile the driver into the kernel, you have to add + "aedsp16=,,,,," to the kernel + command line. + +CONFIG_AEDSP16_MSS + Answer Y if you want your audio card to emulate Microsoft Sound + System. You should then say Y to "Microsoft Sound System support" + and say N to "Audio Excel DSP 16 (SBPro emulation)". + +CONFIG_SC6600 + The SC6600 is the new version of DSP mounted on the Audio Excel DSP + 16 cards. Find in the manual the FCC ID of your audio card and + answer Y if you have an SC6600 DSP. + +CONFIG_SC6600_JOY + Say Y here in order to use the joystick interface of the Audio Excel + DSP 16 card. + +CONFIG_SC6600_CDROMBASE + Base I/O port address for the CD-ROM interface of the Audio Excel + DSP 16 card. + +CONFIG_AEDSP16_MPU401 + Answer Y if you want your audio card to emulate the MPU-401 midi + interface. You should then also say Y to "MPU-401 support". + + Note that the I/O base for MPU-401 support of aedsp16 is the same + you have selected for "MPU-401 support". If you are using this + driver as a module you have to specify the MPU I/O base address with + the parameter 'mpu_base=0xNNN'. + +CONFIG_SOUND_CMPCI + Say Y or M if you have a PCI sound card using the CMI8338 + or the CMI8378 chipset. Data on these chips are available at + . + + A userspace utility to control some internal registers of these + chips is available at + . + +CONFIG_SOUND_CMPCI_CM8738 + Say Y or M if you have a PCI sound card using the CMI8338 + or the CMI8378 chipset. Data on this chip is available at + . + + A userspace utility to control some internal registers of these + chips is available at + . + +CONFIG_SOUND_CMPCI_JOYSTICK + Say here in order to enable the joystick port on a sound crd using + the CMI8338 or the CMI8738 chipset. Data on these chips are + available at . + +CONFIG_SOUND_CMPCI_SPEAKERS + Specify the number of speaker channels you want the card to drive, + as an integer. + +CONFIG_SOUND_CMPCI_SPDIFLOOP + Enable loopback from SPDIF in to SPDIF out. For discussion, see + "The 8738 Audio SPDIF In/Out Technical Data" on the technical + support page at . + + A userspace utility to control even more internal registers of these + chips is available at + . + This package will among other things help you enable SPDIF + out/in/loop/monitor. + +CONFIG_SOUND_EMU10K1 + Say Y or M if you have a PCI sound card using the EMU10K1 chipset, + such as the Creative SBLive!, SB PCI512 or Emu-APS. + + For more information on this driver and the degree of support for the + different card models please check . + + It is now possible to load dsp microcode patches into the EMU10K1 + chip. These patches are used to implement real time sound + processing effects which include for example: signal routing, + bass/treble control, AC3 passthrough, ... + Userspace tools to create new patches and load/unload them can be + found at . + +CONFIG_MIDI_EMU10K1 + Say Y if you want to be able to use the OSS /dev/sequencer + interface. This code is still experimental. + +CONFIG_SOUND_FUSION + This module drives the Crystal SoundFusion devices (CS4280/46xx + series) when wired as native sound drivers with AC97 codecs. If + this driver does not work try the CS4232 driver. + +CONFIG_SOUND_ES1370 + Say Y or M if you have a PCI sound card utilizing the Ensoniq + ES1370 chipset, such as Ensoniq's AudioPCI (non-97). To find + out if your sound card uses an ES1370 without removing your + computer's cover, use lspci -n and look for the PCI ID + 1274:5000. Since Ensoniq was bought by Creative Labs, + Sound Blaster 64/PCI models are either ES1370 or ES1371 based. + This driver differs slightly from OSS/Free, so PLEASE READ + . + +CONFIG_SOUND_ES1371 + Say Y or M if you have a PCI sound card utilizing the Ensoniq + ES1371 chipset, such as Ensoniq's AudioPCI97. To find out if + your sound card uses an ES1371 without removing your computer's + cover, use lspci -n and look for the PCI ID 1274:1371. Since + Ensoniq was bought by Creative Labs, Sound Blaster 64/PCI + models are either ES1370 or ES1371 based. This driver differs + slightly from OSS/Free, so PLEASE READ + . + +CONFIG_SOUND_ESSSOLO1 + Say Y or M if you have a PCI sound card utilizing the ESS Technology + Solo1 chip. To find out if your sound card uses a + Solo1 chip without removing your computer's cover, use + lspci -n and look for the PCI ID 125D:1969. This driver + differs slightly from OSS/Free, so PLEASE READ + . + +CONFIG_SOUND_SONICVIBES + Say Y or M if you have a PCI sound card utilizing the S3 + SonicVibes chipset. To find out if your sound card uses a + SonicVibes chip without removing your computer's cover, use + lspci -n and look for the PCI ID 5333:CA00. This driver + differs slightly from OSS/Free, so PLEASE READ + . + +CONFIG_SOUND_TRIDENT + Say Y or M if you have a PCI sound card utilizing the Trident + 4DWave-DX/NX chipset or your mother board chipset has SiS 7018 + or ALi 5451 built-in. The SiS 7018 PCI Audio Core is embedded + in SiS960 Super South Bridge and SiS540/630 Single Chipset. + The ALi 5451 PCI Audio Core is embedded in ALi M1535, M1535D, + M1535+ or M1535D+ South Bridge. + + Use lspci -n to find out if your sound card or chipset uses + Trident 4DWave or SiS 7018. PCI ID 1023:2000 or 1023:2001 stands + for Trident 4Dwave. PCI ID 1039:7018 stands for SiS7018. PCI ID + 10B9:5451 stands for ALi5451. + + This driver supports S/PDIF in/out (record/playback) for ALi 5451 + embedded in ALi M1535+ and M1535D+. Note that they aren't all + enabled by default; you can enable them by saying Y to "/proc file + system support" and "Sysctl support", and after the /proc file + system has been mounted, executing the command + + command what is enabled + + echo 0>/proc/ALi5451 pcm out is also set to S/PDIF out. (Default). + + echo 1>/proc/ALi5451 use S/PDIF out to output pcm data. + + echo 2>/proc/ALi5451 use S/PDIF out to output non-pcm data. + (AC3...). + + echo 3>/proc/ALi5451 record from Ac97 in(MIC, Line in...). + (Default). + + echo 4>/proc/ALi5451 no matter Ac97 settings, record from S/PDIF + in. + + + This driver differs slightly from OSS/Free, so PLEASE READ the + comments at the top of . + +CONFIG_SOUND_WAVEARTIST + Say Y here to include support for the Rockwell WaveArtist sound + system. This driver is mainly for the NetWinder. + +CONFIG_SOUND_VIA82CXXX + Say Y here to include support for the audio codec found on VIA + 82Cxxx-based chips. Typically these are built into a motherboard. + + DO NOT select Sound Blaster or Adlib with this driver, unless + you have a Sound Blaster or Adlib card in addition to your VIA + audio chip. + +CONFIG_MIDI_VIA82CXXX + Answer Y to use the MIDI interface of the Via686. You may need to + enable this in the BIOS before it will work. This is for connection + to external MIDI hardware, and is not required for software playback + of MIDI files. + +CONFIG_SOUND_NM256 + Say M here to include audio support for the NeoMagic 256AV/256ZX + chipsets. These are the audio chipsets found in the Sony + Z505S/SX/DX, some Sony F-series, and the Dell Latitude CPi and CPt + laptops. It includes support for an AC97-compatible mixer and an + apparently proprietary sound engine. + + See for further information. + +CONFIG_SOUND_MAESTRO + Say Y or M if you have a sound system driven by ESS's Maestro line + of PCI sound chips. These include the Maestro 1, Maestro 2, and + Maestro 2E. See for more + details. + +CONFIG_SOUND_MAESTRO3 + Say Y or M if you have a sound system driven by ESS's Maestro 3 + PCI sound chip. + +CONFIG_SOUND_ADLIB + Includes ASB 64 4D. Information on programming AdLib cards is + available at . + +CONFIG_SOUND_CS4281 + Picture and feature list at + . + +CONFIG_SOUND_GUS16 + Support for Gravis Ulstrasound (GUS) cards (other than the GUS), + sampling at 16-bit width. + +CONFIG_SOUND_GUSMAX + Support for Gravis Ulstrasound MAX. + +CONFIG_SOUND_ICH + Support for integral audio in Intel's I/O Controller Hub (ICH) + chipset, as used on the 810/820/840 motherboards. + +CONFIG_SOUND_TRACEINIT + Verbose soundcard initialization -- affects the format of autoprobe + and initialization messages at boot time. + +CONFIG_SOUND_TVMIXER + Support for audio mixer facilities on the BT848 TV frame-grabber + card. + +CONFIG_SOUND_VIDC + 16-bit support for the VIDC onboard sound hardware found on Acorn + machines. + +CONFIG_SOUND_VMIDI + Support for MIDI loopback on port 1 or 2. + +CONFIG_SOUND_YMFPCI + Support for Yamaha cards including the YMF711, YMF715, YMF718, + YMF719, YMF724, Waveforce 192XG, and Waveforce 192 Digital. + +CONFIG_SOUND_YMFPCI_LEGACY + Support for YMF7xx PCI cards emulating an MP401. + +CONFIG_SOUND_RME96XX + Say Y or M if you have a Hammerfall, Hammerfall light or Hammerfall + DSP card from RME. + +CONFIG_SOUND_BT878 + Audio DMA support for bt878 based grabber boards. As you might have + already noticed, bt878 is listed with two functions in /proc/pci. + Function 0 does the video stuff (bt848 compatible), function 1 does + the same for audio data. This is a driver for the audio part of + the chip. If you say 'Y' here you get a oss-compatible dsp device + where you can record from. If you want just watch TV you probably + don't need this driver as most TV cards handle sound with a short + cable from the TV card to your sound card's line-in. + + This driver is available as a module called btaudio.o ( = code + which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read . + diff -Nru a/sound/oss/Config.in b/sound/oss/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/Config.in Tue Feb 19 18:08:57 2002 @@ -0,0 +1,213 @@ +# drivers/sound/Config.in +# +# 18 Apr 1998, Michael Elizabeth Chastain, +# More hacking for modularisation. +# + +# Prompt user for primary drivers. + +dep_tristate ' BT878 audio dma' CONFIG_SOUND_BT878 $CONFIG_SOUND +dep_tristate ' C-Media PCI (CMI8338/8738)' CONFIG_SOUND_CMPCI $CONFIG_SOUND $CONFIG_PCI +if [ "$CONFIG_SOUND_CMPCI" = "y" -o "$CONFIG_SOUND_CMPCI" = "m" ]; then + bool ' Enable legacy FM' CONFIG_SOUND_CMPCI_FM + if [ "$CONFIG_SOUND_CMPCI_FM" = "y" ]; then + define_hex CONFIG_SOUND_CMPCI_FMIO 388 + hex ' FM I/O 388, 3C8, 3E0, 3E8' CONFIG_SOUND_CMPCI_FMIO 388 + fi + bool ' Enable legacy MPU-401' CONFIG_SOUND_CMPCI_MIDI + if [ "$CONFIG_SOUND_CMPCI_MIDI" = "y" ]; then + hex ' MPU-401 I/O 330, 320, 310, 300' CONFIG_SOUND_CMPCI_MPUIO 330 + fi + bool ' Enable joystick' CONFIG_SOUND_CMPCI_JOYSTICK + bool ' Support CMI8738 based audio cards' CONFIG_SOUND_CMPCI_CM8738 + if [ "$CONFIG_SOUND_CMPCI_CM8738" = "y" ]; then + bool ' Inverse S/PDIF in for CMI8738' CONFIG_SOUND_CMPCI_SPDIFINVERSE + bool ' Enable S/PDIF loop for CMI8738' CONFIG_SOUND_CMPCI_SPDIFLOOP + int ' Number of speakers 2, 4, 5, 6' CONFIG_SOUND_CMPCI_SPEAKERS 2 + if [ "$CONFIG_SOUND_CMPCI_SPEAKERS" != "2" ]; then + bool ' Use Line-in as Read-out' CONFIG_SOUND_CMPCI_LINE_REAR + bool ' Use Line-in as Bass' CONFIG_SOUND_CMPCI_LINE_BASS + fi + fi +fi +dep_tristate ' Creative SBLive! (EMU10K1)' CONFIG_SOUND_EMU10K1 $CONFIG_SOUND $CONFIG_PCI +dep_mbool ' Creative SBLive! MIDI' CONFIG_MIDI_EMU10K1 $CONFIG_SOUND_EMU10K1 $CONFIG_EXPERIMENTAL +dep_tristate ' Crystal SoundFusion (CS4280/461x)' CONFIG_SOUND_FUSION $CONFIG_SOUND +dep_tristate ' Crystal Sound CS4281' CONFIG_SOUND_CS4281 $CONFIG_SOUND +dep_tristate ' Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND $CONFIG_PCI $CONFIG_SOUND_GAMEPORT +dep_tristate ' Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ES1371 $CONFIG_SOUND $CONFIG_PCI $CONFIG_SOUND_GAMEPORT +dep_tristate ' ESS Technology Solo1' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND $CONFIG_SOUND_GAMEPORT +dep_tristate ' ESS Maestro, Maestro2, Maestro2E driver' CONFIG_SOUND_MAESTRO $CONFIG_SOUND +dep_tristate ' ESS Maestro3/Allegro driver (EXPERIMENTAL)' CONFIG_SOUND_MAESTRO3 $CONFIG_SOUND $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate ' Intel ICH (i8xx) audio support' CONFIG_SOUND_ICH $CONFIG_PCI +dep_tristate ' RME Hammerfall (RME96XX) support' CONFIG_SOUND_RME96XX $CONFIG_SOUND $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate ' S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND $CONFIG_SOUND_GAMEPORT +if [ "$CONFIG_VISWS" = "y" ]; then + dep_tristate ' SGI Visual Workstation Sound' CONFIG_SOUND_VWSND $CONFIG_SOUND +fi +if [ "$CONFIG_DDB5477" = "y" ]; then + dep_tristate ' NEC Vrc5477 AC97 sound' CONFIG_SOUND_VRC5477 $CONFIG_SOUND +fi +dep_tristate ' Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core' CONFIG_SOUND_TRIDENT $CONFIG_SOUND + +dep_tristate ' Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND +if [ "$CONFIG_SOUND_MSNDCLAS" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "m" ]; then + if [ "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then + comment ' Compiled-in MSND Classic support requires firmware during compilation.' + define_bool CONFIG_MSNDCLAS_HAVE_BOOT y + else + define_bool CONFIG_MSNDCLAS_HAVE_BOOT n + fi + string 'Full pathname of MSNDINIT.BIN firmware file' CONFIG_MSNDCLAS_INIT_FILE "/etc/sound/msndinit.bin" + string 'Full pathname of MSNDPERM.BIN firmware file' CONFIG_MSNDCLAS_PERM_FILE "/etc/sound/msndperm.bin" +fi +if [ "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then + int ' MSND Classic IRQ 5, 7, 9, 10, 11, 12' CONFIG_MSNDCLAS_IRQ 5 + hex ' MSND Classic memory B0000, C8000, D0000, D8000, E0000, E8000' CONFIG_MSNDCLAS_MEM D0000 + hex ' MSND Classic I/O 210, 220, 230, 240, 250, 260, 290, 3E0' CONFIG_MSNDCLAS_IO 290 +fi + +dep_tristate ' Support for Turtle Beach MultiSound Pinnacle, Fiji' CONFIG_SOUND_MSNDPIN $CONFIG_SOUND +if [ "$CONFIG_SOUND_MSNDPIN" = "y" -o "$CONFIG_SOUND_MSNDPIN" = "m" ]; then + if [ "$CONFIG_SOUND_MSNDPIN" = "y" ]; then + comment 'Compiled-in MSND Pinnacle support requires firmware during compilation.' + define_bool CONFIG_MSNDPIN_HAVE_BOOT y + else + define_bool CONFIG_MSNDPIN_HAVE_BOOT n + fi + string ' Full pathname of PNDSPINI.BIN firmware file' CONFIG_MSNDPIN_INIT_FILE "/etc/sound/pndspini.bin" + string ' Full pathname of PNDSPERM.BIN firmware file' CONFIG_MSNDPIN_PERM_FILE "/etc/sound/pndsperm.bin" +fi +if [ "$CONFIG_SOUND_MSNDPIN" = "y" ]; then + int ' MSND Pinnacle IRQ 5, 7, 9, 10, 11, 12' CONFIG_MSNDPIN_IRQ 5 + hex ' MSND Pinnacle memory B0000, C8000, D0000, D8000, E0000, E8000' CONFIG_MSNDPIN_MEM D0000 + hex 'MSND Pinnacle I/O 210, 220, 230, 240, 250, 260, 290, 3E0' CONFIG_MSNDPIN_IO 290 + bool ' MSND Pinnacle has S/PDIF I/O' CONFIG_MSNDPIN_DIGITAL + bool ' MSND Pinnacle non-PnP Mode' CONFIG_MSNDPIN_NONPNP + if [ "$CONFIG_MSNDPIN_NONPNP" = "y" ]; then + comment 'MSND Pinnacle DSP section will be configured to above parameters.' + hex 'MSND Pinnacle config port 250,260,270' CONFIG_MSNDPIN_CFG 250 + comment 'Pinnacle-specific Device Configuration (0 disables)' + hex 'MSND Pinnacle MPU I/O (e.g. 330)' CONFIG_MSNDPIN_MPU_IO 0 + int 'MSND Pinnacle MPU IRQ (e.g. 9)' CONFIG_MSNDPIN_MPU_IRQ 0 + hex 'MSND Pinnacle IDE I/O 0 (e.g. 170)' CONFIG_MSNDPIN_IDE_IO0 0 + hex 'MSND Pinnacle IDE I/O 1 (e.g. 376)' CONFIG_MSNDPIN_IDE_IO1 0 + int 'MSND Pinnacle IDE IRQ (e.g. 15)' CONFIG_MSNDPIN_IDE_IRQ 0 + hex 'MSND Pinnacle joystick I/O (e.g. 200)' CONFIG_MSNDPIN_JOYSTICK_IO 0 + fi +fi +if [ "$CONFIG_SOUND_MSNDPIN" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then + int 'MSND buffer size (kB)' CONFIG_MSND_FIFOSIZE 128 +fi + +dep_tristate ' VIA 82C686 Audio Codec' CONFIG_SOUND_VIA82CXXX $CONFIG_PCI +dep_mbool ' VIA 82C686 MIDI' CONFIG_MIDI_VIA82CXXX $CONFIG_SOUND_VIA82CXXX + +dep_tristate ' OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND + +if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then + bool ' Verbose initialisation' CONFIG_SOUND_TRACEINIT + bool ' Persistent DMA buffers' CONFIG_SOUND_DMAP + + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' AD1816(A) based cards (EXPERIMENTAL)' CONFIG_SOUND_AD1816 $CONFIG_SOUND_OSS + fi + dep_tristate ' Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_SGALAXY $CONFIG_SOUND_OSS + dep_tristate ' Adlib Cards' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS + dep_tristate ' ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20)' CONFIG_SOUND_ACI_MIXER $CONFIG_SOUND_OSS + dep_tristate ' Crystal CS4232 based (PnP) cards' CONFIG_SOUND_CS4232 $CONFIG_SOUND_OSS + dep_tristate ' Ensoniq SoundScape support' CONFIG_SOUND_SSCAPE $CONFIG_SOUND_OSS + dep_tristate ' Gravis Ultrasound support' CONFIG_SOUND_GUS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_GUS" != "n" ]; then + bool ' 16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_SOUND_GUS16 + bool ' GUS MAX support' CONFIG_SOUND_GUSMAX + fi + dep_tristate ' Loopback MIDI device support' CONFIG_SOUND_VMIDI $CONFIG_SOUND_OSS + dep_tristate ' MediaTrix AudioTrix Pro support' CONFIG_SOUND_TRIX $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_TRIX" = "y" ]; then + bool ' Have TRXPRO.HEX firmware file' CONFIG_TRIX_HAVE_BOOT + if [ "$CONFIG_TRIX_HAVE_BOOT" = "y" ]; then + string ' Full pathname of TRXPRO.HEX firmware file' CONFIG_TRIX_BOOT_FILE /etc/sound/trxpro.hex + fi + fi + + dep_tristate ' Microsoft Sound System support' CONFIG_SOUND_MSS $CONFIG_SOUND_OSS + dep_tristate ' MPU-401 support (NOT for SB16)' CONFIG_SOUND_MPU401 $CONFIG_SOUND_OSS + dep_tristate ' NM256AV/NM256ZX audio support' CONFIG_SOUND_NM256 $CONFIG_SOUND_OSS + dep_tristate ' OPTi MAD16 and/or Mozart based cards' CONFIG_SOUND_MAD16 $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_MAD16" = "y" -o "$CONFIG_SOUND_MAD16" = "m" ]; then + bool ' Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD + fi + dep_tristate ' ProAudioSpectrum 16 support' CONFIG_SOUND_PAS $CONFIG_SOUND_OSS + dep_bool ' Enable PAS16 joystick port' CONFIG_PAS_JOYSTICK $CONFIG_SOUND_PAS + + dep_tristate ' PSS (AD1848, ADSP-2115, ESC614) support' CONFIG_SOUND_PSS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_PSS" = "y" -o "$CONFIG_SOUND_PSS" = "m" ]; then + bool ' Enable PSS mixer (Beethoven ADSP-16 and other compatibile)' CONFIG_PSS_MIXER + bool ' Have DSPxxx.LD firmware file' CONFIG_PSS_HAVE_BOOT + if [ "$CONFIG_PSS_HAVE_BOOT" = "y" ]; then + string ' Full pathname of DSPxxx.LD firmware file' CONFIG_PSS_BOOT_FILE /etc/sound/dsp001.ld + fi + fi + + dep_tristate ' 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SOUND_SB $CONFIG_SOUND_OSS + dep_tristate ' AWE32 synth' CONFIG_SOUND_AWE32_SYNTH $CONFIG_SOUND_OSS + dep_tristate ' Full support for Turtle Beach WaveFront (Tropez Plus, Tropez, Maui) synth/soundcards' CONFIG_SOUND_WAVEFRONT $CONFIG_SOUND_OSS m + dep_tristate ' Limited support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_SOUND_MAUI $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_MAUI" = "y" ]; then + bool ' Have OSWF.MOT firmware file' CONFIG_MAUI_HAVE_BOOT + if [ "$CONFIG_MAUI_HAVE_BOOT" = "y" ]; then + string ' Full pathname of OSWF.MOT firmware file' CONFIG_MAUI_BOOT_FILE /etc/sound/oswf.mot + fi + fi + + dep_tristate ' Yamaha FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_YM3812 $CONFIG_SOUND_OSS + dep_tristate ' Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_OPL3SA1 $CONFIG_SOUND_OSS + dep_tristate ' Yamaha OPL3-SA2 and SA3 based PnP cards' CONFIG_SOUND_OPL3SA2 $CONFIG_SOUND_OSS + dep_tristate ' Yamaha YMF7xx PCI audio (native mode)' CONFIG_SOUND_YMFPCI $CONFIG_SOUND_OSS $CONFIG_PCI + dep_mbool ' Yamaha PCI legacy ports support' CONFIG_SOUND_YMFPCI_LEGACY $CONFIG_SOUND_YMFPCI + dep_tristate ' 6850 UART support' CONFIG_SOUND_UART6850 $CONFIG_SOUND_OSS + + dep_tristate ' Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_SOUND_AEDSP16 $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_AEDSP16" = "y" -o "$CONFIG_SOUND_AEDSP16" = "m" ]; then + bool ' SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600 + if [ "$CONFIG_SC6600" = "y" ]; then + bool ' Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY + int ' SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' CONFIG_SC6600_CDROM 4 + hex ' SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0 + fi + if [ "$CONFIG_SOUND_SB" = "y" -o "$CONFIG_SOUND_SB" = "m" ]; then + if [ "$CONFIG_AEDSP16_MSS" != "y" ]; then + bool ' Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO + fi + fi + if [ "$CONFIG_SOUND_MSS" = "y" -o "$CONFIG_SOUND_MSS" = "m" ]; then + if [ "$CONFIG_AEDSP16_SBPRO" != "y" ]; then + bool ' Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS + fi + fi + if [ "$CONFIG_SOUND_MPU401" = "y" -o "$CONFIG_SOUND_MPU401" = "m" ]; then + bool ' Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401 + fi + fi + + if [ "$CONFIG_ARM" = "y" ]; then + if [ "$CONFIG_ARCH_ACORN" = "y" -o "$CONFIG_ARCH_CLPS7500" = "y" ]; then + dep_tristate ' VIDC 16-bit sound' CONFIG_SOUND_VIDC $CONFIG_SOUND_OSS + fi + dep_tristate ' Netwinder WaveArtist' CONFIG_SOUND_WAVEARTIST $CONFIG_SOUND_OSS $CONFIG_ARCH_NETWINDER + fi + +fi + +dep_tristate ' TV card (bt848) mixer support' CONFIG_SOUND_TVMIXER $CONFIG_SOUND $CONFIG_I2C + +# A cross directory dependence. The sound modules will need gameport.o compiled in, +# but it resides in the drivers/char/joystick directory. This define_tristate takes +# care of that. --Vojtech + +if [ "$CONFIG_INPUT_GAMEPORT" != "n" ]; then + if [ "$CONFIG_SOUND_ESSSOLO1" = "y" -o "$CONFIG_SOUND_ES1370" = "y" -o "$CONFIG_SOUND_ES1371" = "y" -o "$CONFIG_SOUND_SONICVIBES" = "y" ]; then + define_tristate CONFIG_INPUT_GAMEPORT y + fi +fi diff -Nru a/sound/oss/Hwmcode.h b/sound/oss/Hwmcode.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/Hwmcode.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,804 @@ +//============================================================================= +// Copyright (c) 1997 Yamaha Corporation. All Rights Reserved. +// +// Title: +// hwmcode.c +// Desc: +// micro-code for CTRL & DSP +// HISTORY: +// April 03, 1997: 1st try by M. Mukojima +//============================================================================= +#define YDSXG_DSPLENGTH 0x0080 +#define YDSXG_CTRLLENGTH 0x3000 + + +static unsigned long int gdwDSPCode[YDSXG_DSPLENGTH >> 2] = { + 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, + 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, + 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, + 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + + +// -------------------------------------------- +// DS-1E Controller InstructionRAM Code +// 1999/06/21 +// Buf441 slot is Enabled. +// -------------------------------------------- +// 04/09?@creat +// 04/12 stop nise fix +// 06/21?@WorkingOff timming +static unsigned long gdwCtrl1eCode[YDSXG_CTRLLENGTH >> 2] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x00800D, 0x000810, 0x20043A, 0x001A82, + 0x03460D, 0x000810, 0x10043A, 0x02EC0D, + 0x000810, 0x18043A, 0x00010D, 0x020015, + 0x0000FD, 0x000020, 0x038860, 0x039060, + 0x038060, 0x038040, 0x038040, 0x038040, + 0x018040, 0x000A7D, 0x038040, 0x038040, + 0x018040, 0x200402, 0x000882, 0x08001A, + 0x000904, 0x017186, 0x000007, 0x260007, + 0x400007, 0x000007, 0x03258D, 0x000810, + 0x18043A, 0x260007, 0x284402, 0x00087D, + 0x018042, 0x00160A, 0x05A206, 0x000007, + 0x440007, 0x00230D, 0x000810, 0x08043A, + 0x22FA06, 0x000007, 0x0007FD, 0x018042, + 0x08000A, 0x000904, 0x02AB86, 0x000195, + 0x090D04, 0x000007, 0x000820, 0x0000F5, + 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, + 0x018040, 0x000A7D, 0x038042, 0x13804A, + 0x18000A, 0x001820, 0x059060, 0x058860, + 0x018040, 0x0000FD, 0x018042, 0x70000A, + 0x000115, 0x071144, 0x033B86, 0x030000, + 0x007020, 0x036206, 0x018040, 0x00360D, + 0x000810, 0x08043A, 0x232206, 0x000007, + 0x02EC0D, 0x000810, 0x18043A, 0x019A06, + 0x000007, 0x240007, 0x000F8D, 0x000810, + 0x00163A, 0x002402, 0x005C02, 0x0028FD, + 0x000020, 0x018040, 0x08000D, 0x000815, + 0x510984, 0x000007, 0x00004D, 0x000E5D, + 0x000E02, 0x00430D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x00008D, 0x000924, + 0x000F02, 0x00470D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x480480, 0x001210, + 0x28043A, 0x00778D, 0x000810, 0x280C3A, + 0x00068D, 0x000810, 0x28143A, 0x284402, + 0x03258D, 0x000810, 0x18043A, 0x07FF8D, + 0x000820, 0x0002FD, 0x018040, 0x260007, + 0x200007, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x051286, 0x000007, 0x240007, + 0x02EC0D, 0x000810, 0x18043A, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x019B86, 0x000007, 0x01B206, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x22B886, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x065A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x036186, 0x000007, 0x002104, 0x036186, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x07C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00788D, 0x000810, 0x08043A, 0x2A1206, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x070206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x229086, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x225886, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x08DA86, 0x00057D, 0x002820, + 0x03B060, 0x08F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x08FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x009D8D, 0x000810, + 0x08043A, 0x2A1206, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x0A5186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x00107D, 0x018042, + 0x08000A, 0x000215, 0x010984, 0x3A8186, + 0x000007, 0x007FBD, 0x383DC4, 0x000007, + 0x001A7D, 0x001375, 0x018042, 0x09004A, + 0x10000A, 0x0B8D04, 0x139504, 0x000007, + 0x000820, 0x019060, 0x001104, 0x225886, + 0x010040, 0x0017FD, 0x018042, 0x08000A, + 0x000904, 0x225A86, 0x000007, 0x00197D, + 0x038042, 0x09804A, 0x10000A, 0x000924, + 0x001664, 0x0011FD, 0x038042, 0x2B804A, + 0x19804A, 0x00008D, 0x218944, 0x000007, + 0x002244, 0x0C1986, 0x000007, 0x001A64, + 0x002A24, 0x00197D, 0x080102, 0x100122, + 0x000820, 0x039060, 0x018040, 0x003DFD, + 0x00008D, 0x000820, 0x018040, 0x001375, + 0x001A7D, 0x010042, 0x09804A, 0x10000A, + 0x00021D, 0x0189E4, 0x2992E4, 0x309144, + 0x000007, 0x00060D, 0x000A15, 0x000C1D, + 0x001025, 0x00A9E4, 0x012BE4, 0x000464, + 0x01B3E4, 0x0232E4, 0x000464, 0x000464, + 0x000464, 0x000464, 0x00040D, 0x08B1C4, + 0x000007, 0x000820, 0x000BF5, 0x030040, + 0x00197D, 0x038042, 0x09804A, 0x000A24, + 0x08000A, 0x080E64, 0x000007, 0x100122, + 0x000820, 0x031060, 0x010040, 0x0064AC, + 0x00027D, 0x000020, 0x018040, 0x00107D, + 0x018042, 0x0011FD, 0x3B804A, 0x09804A, + 0x20000A, 0x000095, 0x1A1144, 0x00A144, + 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, + 0x0018FD, 0x018042, 0x0010FD, 0x09804A, + 0x28000A, 0x000095, 0x010924, 0x002A64, + 0x0E4986, 0x000007, 0x002904, 0x0E5A86, + 0x000007, 0x0E6206, 0x080002, 0x00008D, + 0x00387D, 0x000820, 0x018040, 0x00127D, + 0x018042, 0x10000A, 0x003904, 0x0F0986, + 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, + 0x000025, 0x0FB206, 0x00002D, 0x000015, + 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, + 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, + 0x400025, 0x00008D, 0x110944, 0x000007, + 0x00018D, 0x109504, 0x000007, 0x009164, + 0x000424, 0x000424, 0x000424, 0x100102, + 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, + 0x00018D, 0x00042D, 0x00008D, 0x109504, + 0x000007, 0x00020D, 0x109184, 0x000007, + 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, + 0x018040, 0x003BFD, 0x001020, 0x03A860, + 0x000815, 0x313184, 0x212184, 0x000007, + 0x03B060, 0x03A060, 0x018040, 0x0022FD, + 0x000095, 0x010924, 0x000424, 0x000424, + 0x001264, 0x100102, 0x000820, 0x039060, + 0x018040, 0x001924, 0x010F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x000424, 0x000424, + 0x00117D, 0x018042, 0x08000A, 0x000A24, + 0x280502, 0x280C02, 0x09800D, 0x000820, + 0x0002FD, 0x018040, 0x200007, 0x0022FD, + 0x018042, 0x08000A, 0x000095, 0x280DC4, + 0x011924, 0x00197D, 0x018042, 0x0011FD, + 0x09804A, 0x10000A, 0x0000B5, 0x113144, + 0x0A8D04, 0x000007, 0x080A44, 0x129504, + 0x000007, 0x0023FD, 0x001020, 0x038040, + 0x101244, 0x000007, 0x000820, 0x039060, + 0x018040, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x123286, 0x000007, 0x003BFD, + 0x000100, 0x000A10, 0x0B807A, 0x13804A, + 0x090984, 0x000007, 0x000095, 0x013D04, + 0x12B886, 0x10000A, 0x100002, 0x090984, + 0x000007, 0x038042, 0x11804A, 0x090D04, + 0x000007, 0x10000A, 0x090D84, 0x000007, + 0x00257D, 0x000820, 0x018040, 0x00010D, + 0x000810, 0x28143A, 0x00127D, 0x018042, + 0x20000A, 0x00197D, 0x018042, 0x00117D, + 0x31804A, 0x10000A, 0x003124, 0x013B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x300102, + 0x003124, 0x000424, 0x000424, 0x001224, + 0x280502, 0x001A4C, 0x143986, 0x700002, + 0x00002D, 0x030000, 0x00387D, 0x018042, + 0x10000A, 0x146206, 0x002124, 0x0000AD, + 0x100002, 0x00010D, 0x000924, 0x006B24, + 0x014A0D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x003264, 0x00008D, 0x000A24, 0x001020, + 0x00227D, 0x018040, 0x014F8D, 0x000810, + 0x08043A, 0x2B5A06, 0x000007, 0x002820, + 0x00207D, 0x018040, 0x00117D, 0x038042, + 0x13804A, 0x33800A, 0x00387D, 0x018042, + 0x08000A, 0x000904, 0x177286, 0x000007, + 0x00008D, 0x030964, 0x015B0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x380102, 0x000424, + 0x000424, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x280502, 0x001A4C, 0x177186, 0x000007, + 0x032164, 0x00632C, 0x003DFD, 0x018042, + 0x08000A, 0x000095, 0x090904, 0x000007, + 0x000820, 0x001A4C, 0x169986, 0x018040, + 0x030000, 0x16B206, 0x002124, 0x00010D, + 0x000924, 0x006B24, 0x016F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x003A64, 0x000095, + 0x001224, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x171286, 0x000007, 0x01760D, + 0x000810, 0x08043A, 0x2B5A06, 0x000007, + 0x160A06, 0x000007, 0x007020, 0x08010A, + 0x10012A, 0x0020FD, 0x038860, 0x039060, + 0x018040, 0x00227D, 0x018042, 0x003DFD, + 0x08000A, 0x31844A, 0x000904, 0x181086, + 0x18008B, 0x00008D, 0x189904, 0x00312C, + 0x18E206, 0x000007, 0x00324C, 0x186B86, + 0x000007, 0x001904, 0x186886, 0x000007, + 0x000095, 0x199144, 0x00222C, 0x003124, + 0x00636C, 0x000E3D, 0x001375, 0x000BFD, + 0x010042, 0x09804A, 0x10000A, 0x038AEC, + 0x0393EC, 0x00224C, 0x18E186, 0x000007, + 0x00008D, 0x189904, 0x00226C, 0x00322C, + 0x30050A, 0x301DAB, 0x002083, 0x0018FD, + 0x018042, 0x08000A, 0x018924, 0x300502, + 0x001083, 0x001875, 0x010042, 0x10000A, + 0x00008D, 0x010924, 0x001375, 0x330542, + 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, + 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, + 0x006083, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x19B286, 0x000007, 0x001E2D, + 0x0005FD, 0x018042, 0x08000A, 0x028924, + 0x280502, 0x00060D, 0x000810, 0x280C3A, + 0x00008D, 0x000810, 0x28143A, 0x0A808D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x001275, 0x030042, 0x21004A, 0x00008D, + 0x1A0944, 0x000007, 0x01AB8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, + 0x030042, 0x0D004A, 0x10000A, 0x089144, + 0x000007, 0x000820, 0x010040, 0x0025F5, + 0x0A3144, 0x000007, 0x000820, 0x032860, + 0x030040, 0x00217D, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00008D, 0x000124, 0x00012C, 0x000E64, + 0x001A64, 0x00636C, 0x08010A, 0x10012A, + 0x000820, 0x031060, 0x030040, 0x0020FD, + 0x018042, 0x08000A, 0x00227D, 0x018042, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00197D, 0x018042, 0x08000A, 0x0022FD, + 0x038042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x090D04, 0x000007, 0x000820, + 0x030040, 0x038042, 0x0B804A, 0x10000A, + 0x000820, 0x031060, 0x030040, 0x038042, + 0x13804A, 0x19804A, 0x110D04, 0x198D04, + 0x000007, 0x08000A, 0x001020, 0x031860, + 0x030860, 0x030040, 0x00008D, 0x0B0944, + 0x000007, 0x000820, 0x010040, 0x0005F5, + 0x030042, 0x08000A, 0x000820, 0x010040, + 0x0000F5, 0x010042, 0x08000A, 0x000904, + 0x1D9886, 0x001E75, 0x030042, 0x01044A, + 0x000C0A, 0x1DAA06, 0x000007, 0x000402, + 0x000C02, 0x00177D, 0x001AF5, 0x018042, + 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, + 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, + 0x00043D, 0x0013F5, 0x001AFD, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x005502, 0x005D0A, 0x030042, 0x0B004A, + 0x1B804A, 0x13804A, 0x20000A, 0x089144, + 0x19A144, 0x0389E4, 0x0399EC, 0x006502, + 0x006D0A, 0x030042, 0x0B004A, 0x19004A, + 0x2B804A, 0x13804A, 0x21804A, 0x30000A, + 0x089144, 0x19A144, 0x2AB144, 0x0389E4, + 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, + 0x000702, 0x00107D, 0x000415, 0x018042, + 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, + 0x0019FD, 0x010042, 0x09804A, 0x10000A, + 0x000934, 0x001674, 0x0029F5, 0x010042, + 0x10000A, 0x00917C, 0x002075, 0x010042, + 0x08000A, 0x000904, 0x200A86, 0x0026F5, + 0x0027F5, 0x030042, 0x09004A, 0x10000A, + 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, + 0x010042, 0x51804A, 0x48000A, 0x160007, + 0x001075, 0x010042, 0x282C0A, 0x281D12, + 0x282512, 0x001F32, 0x1E0007, 0x0E0007, + 0x001975, 0x010042, 0x002DF5, 0x0D004A, + 0x10000A, 0x009144, 0x20EA86, 0x010042, + 0x28340A, 0x000E5D, 0x00008D, 0x000375, + 0x000820, 0x010040, 0x05D2F4, 0x54D104, + 0x00735C, 0x218B86, 0x000007, 0x0C0007, + 0x080007, 0x0A0007, 0x02178D, 0x000810, + 0x08043A, 0x34B206, 0x000007, 0x219206, + 0x000007, 0x080007, 0x002275, 0x010042, + 0x20000A, 0x002104, 0x225886, 0x001E2D, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x21CA86, 0x000007, 0x002010, 0x30043A, + 0x00057D, 0x0180C3, 0x08000A, 0x028924, + 0x280502, 0x280C02, 0x0A810D, 0x000820, + 0x0002F5, 0x010040, 0x220007, 0x0004FD, + 0x018042, 0x70000A, 0x030000, 0x007020, + 0x07FA06, 0x018040, 0x022B8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x22C286, + 0x000007, 0x020206, 0x000007, 0x000875, + 0x0009FD, 0x00010D, 0x234206, 0x000295, + 0x000B75, 0x00097D, 0x00000D, 0x000515, + 0x010042, 0x18000A, 0x001904, 0x2A0086, + 0x0006F5, 0x001020, 0x010040, 0x0004F5, + 0x000820, 0x010040, 0x000775, 0x010042, + 0x09804A, 0x10000A, 0x001124, 0x000904, + 0x23F286, 0x000815, 0x080102, 0x101204, + 0x241206, 0x000575, 0x081204, 0x000007, + 0x100102, 0x000575, 0x000425, 0x021124, + 0x100102, 0x000820, 0x031060, 0x010040, + 0x001924, 0x2A0086, 0x00008D, 0x000464, + 0x009D04, 0x291086, 0x180102, 0x000575, + 0x010042, 0x28040A, 0x00018D, 0x000924, + 0x280D02, 0x00000D, 0x000924, 0x281502, + 0x10000D, 0x000820, 0x0002F5, 0x010040, + 0x200007, 0x001175, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x24FA86, 0x000007, + 0x000100, 0x080B20, 0x130B60, 0x1B0B60, + 0x030A60, 0x010040, 0x050042, 0x3D004A, + 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, + 0x010042, 0x28140A, 0x0004F5, 0x010042, + 0x08000A, 0x000315, 0x010D04, 0x260286, + 0x004015, 0x000095, 0x010D04, 0x25F086, + 0x100022, 0x10002A, 0x261A06, 0x000007, + 0x333104, 0x2AA904, 0x000007, 0x032124, + 0x280502, 0x284402, 0x001124, 0x400102, + 0x000424, 0x000424, 0x003224, 0x00292C, + 0x00636C, 0x277386, 0x000007, 0x02B164, + 0x000464, 0x000464, 0x00008D, 0x000A64, + 0x280D02, 0x10008D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x00008D, 0x38B904, + 0x000007, 0x03296C, 0x30010A, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x270286, + 0x000007, 0x00212C, 0x28050A, 0x00316C, + 0x00046C, 0x00046C, 0x28450A, 0x001124, + 0x006B64, 0x100102, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x004124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x27FA86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x284086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x284A86, 0x000007, 0x284402, 0x003124, + 0x300502, 0x003924, 0x300583, 0x000883, + 0x0005F5, 0x010042, 0x28040A, 0x00008D, + 0x008124, 0x280D02, 0x00008D, 0x008124, + 0x281502, 0x10018D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001025, 0x000575, + 0x030042, 0x09004A, 0x10000A, 0x0A0904, + 0x121104, 0x000007, 0x001020, 0x050860, + 0x050040, 0x0006FD, 0x018042, 0x09004A, + 0x10000A, 0x0000A5, 0x0A0904, 0x121104, + 0x000007, 0x000820, 0x019060, 0x010040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x29CA86, 0x000007, 0x244206, 0x000007, + 0x000606, 0x000007, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x2A1A86, 0x000007, + 0x000100, 0x080B20, 0x138B60, 0x1B8B60, + 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, + 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, + 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, + 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, + 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, + 0x038A60, 0x000606, 0x018040, 0x00008D, + 0x000A64, 0x280D02, 0x000A24, 0x00027D, + 0x018042, 0x10000A, 0x001224, 0x0003FD, + 0x018042, 0x08000A, 0x000904, 0x2C0A86, + 0x000007, 0x00018D, 0x000A24, 0x000464, + 0x000464, 0x080102, 0x000924, 0x000424, + 0x000424, 0x100102, 0x02000D, 0x009144, + 0x2C6186, 0x000007, 0x0001FD, 0x018042, + 0x08000A, 0x000A44, 0x2C4386, 0x018042, + 0x0A000D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00027D, 0x001020, 0x000606, + 0x018040, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x2CB286, 0x000007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2CE286, + 0x000007, 0x000075, 0x002E7D, 0x010042, + 0x0B804A, 0x000020, 0x000904, 0x000686, + 0x010040, 0x31844A, 0x30048B, 0x000883, + 0x00008D, 0x000810, 0x28143A, 0x00008D, + 0x000810, 0x280C3A, 0x000675, 0x010042, + 0x08000A, 0x003815, 0x010924, 0x280502, + 0x0B000D, 0x000820, 0x0002F5, 0x010040, + 0x000606, 0x220007, 0x000464, 0x000464, + 0x000606, 0x000007, 0x000134, 0x007F8D, + 0x00093C, 0x281D12, 0x282512, 0x001F32, + 0x0E0007, 0x00010D, 0x00037D, 0x000820, + 0x018040, 0x05D2F4, 0x000007, 0x080007, + 0x00037D, 0x018042, 0x08000A, 0x000904, + 0x2E8A86, 0x000007, 0x000606, 0x000007, + 0x000007, 0x000012, 0x100007, 0x320007, + 0x600007, 0x460007, 0x100080, 0x48001A, + 0x004904, 0x2EF186, 0x000007, 0x001210, + 0x58003A, 0x000145, 0x5C5D04, 0x000007, + 0x000080, 0x48001A, 0x004904, 0x2F4186, + 0x000007, 0x001210, 0x50003A, 0x005904, + 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, + 0x7FFF7D, 0x07D524, 0x004224, 0x500102, + 0x200502, 0x000082, 0x40001A, 0x004104, + 0x2FC986, 0x000007, 0x003865, 0x40001A, + 0x004020, 0x00104D, 0x04C184, 0x31AB86, + 0x000040, 0x040007, 0x000165, 0x000145, + 0x004020, 0x000040, 0x000765, 0x080080, + 0x40001A, 0x004104, 0x305986, 0x000007, + 0x001210, 0x40003A, 0x004104, 0x30B286, + 0x00004D, 0x0000CD, 0x004810, 0x20043A, + 0x000882, 0x40001A, 0x004104, 0x30C186, + 0x000007, 0x004820, 0x005904, 0x319886, + 0x000040, 0x0007E5, 0x200480, 0x2816A0, + 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, + 0x000040, 0x000032, 0x400075, 0x00007D, + 0x07D574, 0x200512, 0x000082, 0x40001A, + 0x004104, 0x317186, 0x000007, 0x038A06, + 0x640007, 0x0000E5, 0x000020, 0x000040, + 0x000A65, 0x000020, 0x020040, 0x020040, + 0x000040, 0x000165, 0x000042, 0x70000A, + 0x007104, 0x323286, 0x000007, 0x060007, + 0x019A06, 0x640007, 0x050000, 0x007020, + 0x000040, 0x038A06, 0x640007, 0x000007, + 0x00306D, 0x028860, 0x029060, 0x08000A, + 0x028860, 0x008040, 0x100012, 0x00100D, + 0x009184, 0x32D186, 0x000E0D, 0x009184, + 0x33E186, 0x000007, 0x300007, 0x001020, + 0x003B6D, 0x008040, 0x000080, 0x08001A, + 0x000904, 0x32F186, 0x000007, 0x001220, + 0x000DED, 0x008040, 0x008042, 0x10000A, + 0x40000D, 0x109544, 0x000007, 0x001020, + 0x000DED, 0x008040, 0x008042, 0x20040A, + 0x000082, 0x08001A, 0x000904, 0x338186, + 0x000007, 0x003B6D, 0x008042, 0x08000A, + 0x000E15, 0x010984, 0x342B86, 0x600007, + 0x08001A, 0x000C15, 0x010984, 0x341386, + 0x000020, 0x1A0007, 0x0002ED, 0x008040, + 0x620007, 0x00306D, 0x028042, 0x0A804A, + 0x000820, 0x0A804A, 0x000606, 0x10804A, + 0x000007, 0x282512, 0x001F32, 0x05D2F4, + 0x54D104, 0x00735C, 0x000786, 0x000007, + 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, + 0x020040, 0x004820, 0x025060, 0x40000A, + 0x024060, 0x000040, 0x454944, 0x000007, + 0x004020, 0x003AE5, 0x000040, 0x0028E5, + 0x000042, 0x48000A, 0x004904, 0x39F886, + 0x002C65, 0x000042, 0x40000A, 0x0000D5, + 0x454104, 0x000007, 0x000655, 0x054504, + 0x368286, 0x0001D5, 0x054504, 0x368086, + 0x002B65, 0x000042, 0x003AE5, 0x50004A, + 0x40000A, 0x45C3D4, 0x000007, 0x454504, + 0x000007, 0x0000CD, 0x444944, 0x000007, + 0x454504, 0x000007, 0x00014D, 0x554944, + 0x000007, 0x045144, 0x367986, 0x002C65, + 0x000042, 0x48000A, 0x4CD104, 0x000007, + 0x04C144, 0x368386, 0x000007, 0x160007, + 0x002CE5, 0x040042, 0x40000A, 0x004020, + 0x000040, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x36F086, 0x000007, 0x002402, + 0x383206, 0x005C02, 0x0025E5, 0x000042, + 0x40000A, 0x004274, 0x002AE5, 0x000042, + 0x40000A, 0x004274, 0x500112, 0x0029E5, + 0x000042, 0x40000A, 0x004234, 0x454104, + 0x000007, 0x004020, 0x000040, 0x003EE5, + 0x000020, 0x000040, 0x002DE5, 0x400152, + 0x50000A, 0x045144, 0x37DA86, 0x0000C5, + 0x003EE5, 0x004020, 0x000040, 0x002BE5, + 0x000042, 0x40000A, 0x404254, 0x000007, + 0x002AE5, 0x004020, 0x000040, 0x500132, + 0x040134, 0x005674, 0x0029E5, 0x020042, + 0x42000A, 0x000042, 0x50000A, 0x05417C, + 0x0028E5, 0x000042, 0x48000A, 0x0000C5, + 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, + 0x020042, 0x40004A, 0x50000A, 0x00423C, + 0x00567C, 0x0028E5, 0x004820, 0x000040, + 0x281D12, 0x282512, 0x001F72, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x393A86, + 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, + 0x000042, 0x40000A, 0x004104, 0x397886, + 0x002D65, 0x000042, 0x28340A, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, + 0x39E186, 0x000007, 0x000606, 0x080007, + 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, + 0x020045, 0x004020, 0x000060, 0x000365, + 0x000040, 0x002E65, 0x001A20, 0x0A1A60, + 0x000040, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x000606, 0x50004A, + 0x0017FD, 0x018042, 0x08000A, 0x000904, + 0x225A86, 0x000007, 0x00107D, 0x018042, + 0x0011FD, 0x33804A, 0x19804A, 0x20000A, + 0x000095, 0x2A1144, 0x01A144, 0x3B9086, + 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, + 0x018042, 0x0010FD, 0x09804A, 0x38000A, + 0x000095, 0x010924, 0x003A64, 0x3B8186, + 0x000007, 0x003904, 0x3B9286, 0x000007, + 0x3B9A06, 0x00000D, 0x00008D, 0x000820, + 0x00387D, 0x018040, 0x700002, 0x00117D, + 0x018042, 0x00197D, 0x29804A, 0x30000A, + 0x380002, 0x003124, 0x000424, 0x000424, + 0x002A24, 0x280502, 0x00068D, 0x000810, + 0x28143A, 0x00750D, 0x00B124, 0x002264, + 0x3D0386, 0x284402, 0x000810, 0x280C3A, + 0x0B800D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00758D, 0x00B124, 0x100102, + 0x012144, 0x3E4986, 0x001810, 0x10003A, + 0x00387D, 0x018042, 0x08000A, 0x000904, + 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, + 0x00008D, 0x023164, 0x000A64, 0x280D02, + 0x0B808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00387D, 0x018042, 0x08000A, + 0x000904, 0x3E3286, 0x030000, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x3D8286, + 0x000007, 0x002810, 0x28043A, 0x00750D, + 0x030924, 0x002264, 0x280D02, 0x02316C, + 0x28450A, 0x0B810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x00008D, 0x000A24, + 0x3E4A06, 0x100102, 0x001810, 0x10003A, + 0x0000BD, 0x003810, 0x30043A, 0x00187D, + 0x018042, 0x0018FD, 0x09804A, 0x20000A, + 0x0000AD, 0x028924, 0x07212C, 0x001010, + 0x300583, 0x300D8B, 0x3014BB, 0x301C83, + 0x002083, 0x00137D, 0x038042, 0x33844A, + 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, + 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, + 0x001E0D, 0x0005FD, 0x018042, 0x20000A, + 0x020924, 0x00068D, 0x00A96C, 0x00009D, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x3F6A86, 0x000007, 0x280502, 0x280D0A, + 0x284402, 0x001810, 0x28143A, 0x0C008D, + 0x000820, 0x0002FD, 0x018040, 0x220007, + 0x003904, 0x225886, 0x001E0D, 0x00057D, + 0x018042, 0x20000A, 0x020924, 0x0000A5, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x402A86, 0x000007, 0x280502, 0x280C02, + 0x002010, 0x28143A, 0x0C010D, 0x000820, + 0x0002FD, 0x018040, 0x225A06, 0x220007, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; diff -Nru a/sound/oss/Makefile b/sound/oss/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/Makefile Tue Feb 19 18:08:58 2002 @@ -0,0 +1,288 @@ +# Makefile for the Linux sound card driver +# +# 18 Apr 1998, Michael Elizabeth Chastain, +# Rewritten to use lists instead of if-statements. + + +# All of the (potential) objects that export symbols. +# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. + +export-objs := ad1848.o audio_syms.o midi_syms.o mpu401.o \ + msnd.o opl3.o sb_common.o sequencer_syms.o \ + sound_syms.o uart401.o \ + nm256_audio.o ac97.o ac97_codec.o aci.o + +# Each configuration option enables a list of files. + +obj-$(CONFIG_SOUND_OSS) += sound.o +obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o + +# Please leave it as is, cause the link order is significant ! + +obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o +obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o +obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o +obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb_lib.o uart401.o +obj-$(CONFIG_SOUND_CS4232) += cs4232.o uart401.o +obj-$(CONFIG_SOUND_OPL3SA2) += opl3sa2.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_MSS) += ad1848.o +obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o +obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o +obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o +obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o +obj-$(CONFIG_SOUND_MPU401) += mpu401.o +obj-$(CONFIG_SOUND_UART6850) += uart6850.o +obj-$(CONFIG_SOUND_GUS) += gus.o ad1848.o +obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o +obj-$(CONFIG_SOUND_YM3812) += opl3.o +obj-$(CONFIG_SOUND_VMIDI) += v_midi.o +obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o +obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o +obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o +obj-$(CONFIG_SOUND_AD1816) += ad1816.o +obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o +obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o + +obj-$(CONFIG_SOUND_VIA82CXXX) += via82cxxx_audio.o ac97_codec.o +ifeq ($(CONFIG_MIDI_VIA82CXXX),y) + obj-$(CONFIG_SOUND_VIA82CXXX) += sound.o uart401.o +endif +obj-$(CONFIG_SOUND_YMFPCI) += ymfpci.o ac97_codec.o +ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y) + obj-$(CONFIG_SOUND_YMFPCI) += opl3.o uart401.o +endif +obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o +obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o +obj-$(CONFIG_SOUND_VWSND) += vwsnd.o +obj-$(CONFIG_SOUND_NM256) += nm256_audio.o ac97.o +obj-$(CONFIG_SOUND_ICH) += i810_audio.o ac97_codec.o +obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o +obj-$(CONFIG_SOUND_CMPCI) += cmpci.o +obj-$(CONFIG_SOUND_ES1370) += es1370.o +obj-$(CONFIG_SOUND_ES1371) += es1371.o ac97_codec.o +obj-$(CONFIG_SOUND_VRC5477) += nec_vrc5477.o ac97_codec.o +obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o +obj-$(CONFIG_SOUND_FUSION) += cs46xx.o ac97_codec.o +obj-$(CONFIG_SOUND_MAESTRO) += maestro.o +obj-$(CONFIG_SOUND_MAESTRO3) += maestro3.o ac97_codec.o +obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o +obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o +obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o +obj-$(CONFIG_SOUND_BT878) += btaudio.o +obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o + +ifeq ($(CONFIG_MIDI_EMU10K1),y) + obj-$(CONFIG_SOUND_EMU10K1) += sound.o +endif + +subdir-$(CONFIG_SOUND_EMU10K1) += emu10k1 +subdir-$(CONFIG_SOUND_CS4281) += cs4281 + +ifeq ($(CONFIG_SOUND_EMU10K1),y) + obj-y += emu10k1/emu10k1.o +endif + +ifeq ($(CONFIG_SOUND_CS4281),y) + obj-y += cs4281/cs4281.o +endif + +subdir-$(CONFIG_DMASOUND) += dmasound + +ifeq ($(CONFIG_DMASOUND),y) + obj-y += dmasound/dmasound.o +endif + + +# Declare multi-part drivers. + +list-multi := sound.o gus.o pas2.o sb.o sb_lib.o vidc_mod.o \ + wavefront.o + +sound-objs := \ + dev_table.o soundcard.o sound_syms.o \ + audio.o audio_syms.o dmabuf.o \ + midi_syms.o midi_synth.o midibuf.o \ + sequencer.o sequencer_syms.o sound_timer.o sys_timer.o + +gus-objs := gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o +pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o +sb-objs := sb_card.o +sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o +vidc_mod-objs := vidc.o vidc_fill.o +wavefront-objs := wavfront.o wf_midi.o yss225.o + + +O_TARGET := sounddrivers.o + +include $(TOPDIR)/Rules.make + + + +# Link rules for multi-part drivers. + +sound.o: $(sound-objs) + $(LD) -r -o $@ $(sound-objs) + +gus.o: $(gus-objs) + $(LD) -r -o $@ $(gus-objs) + +pas2.o: $(pas2-objs) + $(LD) -r -o $@ $(pas2-objs) + +sb.o: $(sb-objs) + $(LD) -r -o $@ $(sb-objs) + +sb_lib.o: $(sb_lib-objs) + $(LD) -r -o $@ $(sb_lib-objs) + +vidc_mod.o: $(vidc_mod-objs) + $(LD) -r -o $@ $(vidc_mod-objs) + +wavefront.o: $(wavefront-objs) + $(LD) -r -o $@ $(wavefront-objs) + +# Firmware files that need translation +# +# The translated files are protected by a file that keeps track +# of what name was used to build them. If the name changes, they +# will be forced to be remade. +# +# First make the utilities. + +bin2hex: bin2hex.c + $(HOSTCC) $(HOSTCFLAGS) -o bin2hex bin2hex.c + +hex2hex: hex2hex.c + $(HOSTCC) $(HOSTCFLAGS) -o hex2hex hex2hex.c + + + + +# Turtle Beach Maui / Tropez + +maui.o: maui_boot.h + +ifeq ($(CONFIG_MAUI_HAVE_BOOT),y) + maui_boot.h: $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) bin2hex + ./bin2hex -i maui_os < $(CONFIG_MAUI_BOOT_FILE) > $@ +else + maui_boot.h: + ( \ + echo 'static unsigned char * maui_os = NULL;'; \ + echo 'static int maui_osLen = 0;'; \ + ) > $@ +endif + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MAUI_HAVE_BOOT) $(CONFIG_MAUI_BOOT_FILE)),$$(strip $$(CONFIG_MAUI_HAVE_BOOT) $$(CONFIG_MAUI_BOOT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot + + + +# Turtle Beach MultiSound + +ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y) + msnd_classic.o: msndperm.c msndinit.c + + msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) bin2hex + ./bin2hex msndperm < $(CONFIG_MSNDCLAS_PERM_FILE) > $@ + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MSNDCLAS_HAVE_BOOT) $(CONFIG_MSNDCLAS_PERM_FILE)),$$(strip $$(CONFIG_MSNDCLAS_HAVE_BOOT) $$(CONFIG_MSNDCLAS_PERM_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot + + msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) bin2hex + ./bin2hex msndinit < $(CONFIG_MSNDCLAS_INIT_FILE) > $@ + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MSNDCLAS_HAVE_BOOT) $(CONFIG_MSNDCLAS_INIT_FILE)),$$(strip $$(CONFIG_MSNDCLAS_HAVE_BOOT) $$(CONFIG_MSNDCLAS_INIT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot +endif + +ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y) + msnd_pinnacle.o: pndsperm.c pndspini.c + + pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) bin2hex + ./bin2hex pndsperm < $(CONFIG_MSNDPIN_PERM_FILE) > $@ + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MSNDPIN_HAVE_BOOT) $(CONFIG_MSNDPIN_PERM_FILE)),$$(strip $$(CONFIG_MSNDPIN_HAVE_BOOT) $$(CONFIG_MSNDPIN_PERM_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot + + pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) bin2hex + ./bin2hex pndspini < $(CONFIG_MSNDPIN_INIT_FILE) > $@ + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MSNDPIN_HAVE_BOOT) $(CONFIG_MSNDPIN_INIT_FILE)),$$(strip $$(CONFIG_MSNDPIN_HAVE_BOOT) $$(CONFIG_MSNDPIN_INIT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot +endif + + + +# PSS (ECHO-ADI2111) + +pss.o: pss_boot.h + +ifeq ($(CONFIG_PSS_HAVE_BOOT),y) + pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) bin2hex + ./bin2hex pss_synth < $(CONFIG_PSS_BOOT_FILE) > $@ +else + pss_boot.h: + ( \ + echo 'static unsigned char * pss_synth = NULL;'; \ + echo 'static int pss_synthLen = 0;'; \ + ) > $@ +endif + @ ( \ + echo 'ifeq ($(strip $(CONFIG_PSS_HAVE_BOOT) $(CONFIG_PSS_BOOT_FILE)),$$(strip $$(CONFIG_PSS_HAVE_BOOT) $$(CONFIG_PSS_BOOT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot + + + +# MediaTrix AudioTrix Pro + +trix.o: trix_boot.h + +ifeq ($(CONFIG_TRIX_HAVE_BOOT),y) + trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) hex2hex + ./hex2hex -i trix_boot < $(CONFIG_TRIX_BOOT_FILE) > $@ +else + trix_boot.h: + ( \ + echo 'static unsigned char * trix_boot = NULL;'; \ + echo 'static int trix_boot_len = 0;'; \ + ) > $@ +endif + @ ( \ + echo 'ifeq ($(strip $(CONFIG_TRIX_HAVE_BOOT) $(CONFIG_TRIX_BOOT_FILE)),$$(strip $$(CONFIG_TRIX_HAVE_BOOT) $$(CONFIG_TRIX_BOOT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot + + + +# Find boot files whose source file names have changed and force rebuild. + +FILES_BOOT_UP_TO_DATE := + +FILES_BOOT_EXIST := $(wildcard .*.boot) +ifneq ($(FILES_BOOT_EXIST),) +include $(FILES_BOOT_EXIST) +endif + +FILES_BOOT_CHANGED := $(strip \ + $(filter-out $(FILES_BOOT_UP_TO_DATE), \ + maui_boot.h pss_boot.h trix_boot.h)) + +ifneq ($(FILES_BOOT_CHANGED),) +$(FILES_BOOT_CHANGED): dummy +endif diff -Nru a/sound/oss/README.FIRST b/sound/oss/README.FIRST --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/README.FIRST Tue Feb 19 18:08:59 2002 @@ -0,0 +1,6 @@ +The modular sound driver patches were funded by Red Hat Software +(www.redhat.com). The sound driver here is thus a modified version of +Hannu's code. Please bear that in mind when considering the appropriate +forums for bug reporting. + +Alan Cox diff -Nru a/sound/oss/ac97.c b/sound/oss/ac97.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ac97.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,455 @@ +#include +#include +#include +#include "ac97.h" + +/* Flag for mono controls. */ +#define MO 0 +/* And for stereo. */ +#define ST 1 + +/* Whether or not the bits in the channel are inverted. */ +#define INV 1 +#define NINV 0 + +static struct ac97_chn_desc { + int ac97_regnum; + int oss_channel; + int maxval; + int is_stereo; + int oss_mask; + int recordNum; + u16 regmask; + int is_inverted; +} mixerRegs[] = { + { AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME, 0x3f, ST, SOUND_MASK_VOLUME, 5, 0x0000, INV }, + { AC97_MASTER_VOL_MONO, SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000, INV }, + { AC97_MASTER_TONE, SOUND_MIXER_TREBLE, 0x0f, MO, SOUND_MASK_TREBLE, -1, 0x00ff, INV }, + { AC97_MASTER_TONE, SOUND_MIXER_BASS, 0x0f, MO, SOUND_MASK_BASS, -1, 0xff00, INV }, + { AC97_PCBEEP_VOL, SOUND_MIXER_SPEAKER, 0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e, INV }, + { AC97_PHONE_VOL, SOUND_MIXER_PHONEIN, 0x1f, MO, SOUND_MASK_PHONEIN, 7, 0x0000, INV }, + { AC97_MIC_VOL, SOUND_MIXER_MIC, 0x1f, MO, SOUND_MASK_MIC, 0, 0x0000, INV }, + { AC97_LINEIN_VOL, SOUND_MIXER_LINE, 0x1f, ST, SOUND_MASK_LINE, 4, 0x0000, INV }, + { AC97_CD_VOL, SOUND_MIXER_CD, 0x1f, ST, SOUND_MASK_CD, 1, 0x0000, INV }, + { AC97_VIDEO_VOL, SOUND_MIXER_VIDEO, 0x1f, ST, SOUND_MASK_VIDEO, 2, 0x0000, INV }, + { AC97_AUX_VOL, SOUND_MIXER_LINE1, 0x1f, ST, SOUND_MASK_LINE1, 3, 0x0000, INV }, + { AC97_PCMOUT_VOL, SOUND_MIXER_PCM, 0x1f, ST, SOUND_MASK_PCM, -1, 0x0000, INV }, + { AC97_RECORD_GAIN, SOUND_MIXER_IGAIN, 0x0f, ST, SOUND_MASK_IGAIN, -1, 0x0000, NINV }, + { -1, -1, 0xff, 0, 0, -1, 0x0000, 0 }, +}; + +static struct ac97_chn_desc * +ac97_find_chndesc (struct ac97_hwint *dev, int oss_channel) +{ + int x; + + for (x = 0; mixerRegs[x].oss_channel != -1; x++) { + if (mixerRegs[x].oss_channel == oss_channel) + return mixerRegs + x; + } + + return NULL; +} + +static inline int +ac97_is_valid_channel (struct ac97_hwint *dev, struct ac97_chn_desc *chn) +{ + return (dev->last_written_mixer_values[chn->ac97_regnum / 2] + != AC97_REG_UNSUPPORTED); +} + +int +ac97_init (struct ac97_hwint *dev) +{ + int x; + int reg0; + + /* Clear out the arrays of cached values. */ + for (x = 0; x < AC97_REG_CNT; x++) + dev->last_written_mixer_values[x] = AC97_REGVAL_UNKNOWN; + + for (x = 0; x < SOUND_MIXER_NRDEVICES; x++) + dev->last_written_OSS_values[x] = AC97_REGVAL_UNKNOWN; + + /* Clear the device masks. */ + dev->mixer_devmask = 0; + dev->mixer_stereomask = 0; + dev->mixer_recmask = 0; + + /* ??? Do a "standard reset" via register 0? */ + + /* Hardware-dependent reset. */ + if (dev->reset_device (dev)) + return -1; + + /* Check the mixer device capabilities. */ + reg0 = dev->read_reg (dev, AC97_RESET); + + if (reg0 < 0) + return -1; + + /* Check for support for treble/bass controls. */ + if (! (reg0 & 4)) { + dev->last_written_mixer_values[AC97_MASTER_TONE / 2] + = AC97_REG_UNSUPPORTED; + } + + /* ??? There may be other tests here? */ + + /* Fill in the device masks. */ + for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { + if (ac97_is_valid_channel (dev, mixerRegs + x)) { + dev->mixer_devmask |= mixerRegs[x].oss_mask; + + if (mixerRegs[x].is_stereo) + dev->mixer_stereomask |= mixerRegs[x].oss_mask; + + if (mixerRegs[x].recordNum != -1) + dev->mixer_recmask |= mixerRegs[x].oss_mask; + } + } + + return 0; +} + +/* Reset the mixer to the currently saved settings. */ +int +ac97_reset (struct ac97_hwint *dev) +{ + int x; + + if (dev->reset_device (dev)) + return -1; + + /* Now set the registers back to their last-written values. */ + for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { + int regnum = mixerRegs[x].ac97_regnum; + int value = dev->last_written_mixer_values [regnum / 2]; + if (value >= 0) + ac97_put_register (dev, regnum, value); + } + return 0; +} + +/* Return the contents of register REG; use the cache if the value in it + is valid. Returns a negative error code on failure. */ +int +ac97_get_register (struct ac97_hwint *dev, u8 reg) +{ + if (reg > 127 || (reg & 1)) + return -EINVAL; + + /* See if it's in the cache, or if it's just plain invalid. */ + switch (dev->last_written_mixer_values[reg / 2]) { + case AC97_REG_UNSUPPORTED: + return -EINVAL; + break; + case AC97_REGVAL_UNKNOWN: + dev->last_written_mixer_values[reg / 2] = dev->read_reg (dev, reg); + break; + default: + break; + } + return dev->last_written_mixer_values[reg / 2]; +} + +/* Write VALUE to AC97 register REG, and cache its value in the last-written + cache. Returns a negative error code on failure, or 0 on success. */ +int +ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value) +{ + if (reg > 127 || (reg & 1)) + return -EINVAL; + + if (dev->last_written_mixer_values[reg / 2] == AC97_REG_UNSUPPORTED) + return -EINVAL; + else { + int res = dev->write_reg (dev, reg, value); + if (res >= 0) { + dev->last_written_mixer_values[reg / 2] = value; + return 0; + } + else + return res; + } +} + +/* Scale VALUE (a value fro 0 to MAXVAL) to a value from 0-100. If + IS_STEREO is set, VALUE is a stereo value; the left channel value + is in the lower 8 bits, and the right channel value is in the upper + 8 bits. + + A negative error code is returned on failure, or the unsigned + scaled value on success. */ + +static int +ac97_scale_to_oss_val (int value, int maxval, int is_stereo, int inv) +{ + /* Muted? */ + if (value & AC97_MUTE) + return 0; + + if (is_stereo) + return (ac97_scale_to_oss_val (value & 255, maxval, 0, inv) << 8) + | (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); + else { + int i; + + /* Inverted. */ + if (inv) + value = maxval - value; + + i = (value * 100 + (maxval / 2)) / maxval; + if (i > 100) + i = 100; + if (i < 0) + i = 0; + return i; + } +} + +static int +ac97_scale_from_oss_val (int value, int maxval, int is_stereo, int inv) +{ + if (is_stereo) + return (ac97_scale_from_oss_val (value & 255, maxval, 0, inv) << 8) + | (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); + else { + int i = ((value & 255) * maxval + 50) / 100; + if (inv) + i = maxval - i; + if (i < 0) + i = 0; + if (i > maxval) + i = maxval; + return i; + } +} + +int +ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, u16 oss_value) +{ + int scaled_value; + struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel); + int result; + + if (channel == NULL) + return -ENODEV; + if (! ac97_is_valid_channel (dev, channel)) + return -ENODEV; + scaled_value = ac97_scale_from_oss_val (oss_value, channel->maxval, + channel->is_stereo, + channel->is_inverted); + if (scaled_value < 0) + return scaled_value; + + if (channel->regmask != 0) { + int mv; + + int oldval = ac97_get_register (dev, channel->ac97_regnum); + if (oldval < 0) + return oldval; + + for (mv = channel->regmask; ! (mv & 1); mv >>= 1) + scaled_value <<= 1; + + scaled_value &= channel->regmask; + scaled_value |= (oldval & ~channel->regmask); + } + result = ac97_put_register (dev, channel->ac97_regnum, scaled_value); + if (result == 0) + dev->last_written_OSS_values[oss_channel] = oss_value; + return result; +} + +int +ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel) +{ + struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel); + int regval; + + if (channel == NULL) + return -ENODEV; + + if (! ac97_is_valid_channel (dev, channel)) + return -ENODEV; + + regval = ac97_get_register (dev, channel->ac97_regnum); + + if (regval < 0) + return regval; + + if (channel->regmask != 0) { + int mv; + + regval &= channel->regmask; + + for (mv = channel->regmask; ! (mv & 1); mv >>= 1) + regval >>= 1; + } + return ac97_scale_to_oss_val (regval, channel->maxval, + channel->is_stereo, + channel->is_inverted); +} + +int +ac97_get_recmask (struct ac97_hwint *dev) +{ + int recReg = ac97_get_register (dev, AC97_RECORD_SELECT); + + if (recReg < 0) + return recReg; + else { + int x; + for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { + if (mixerRegs[x].recordNum == (recReg & 7)) + return mixerRegs[x].oss_mask; + } + return -ENODEV; + } +} + +int +ac97_set_recmask (struct ac97_hwint *dev, int oss_recmask) +{ + int x; + + if (oss_recmask == 0) + oss_recmask = SOUND_MIXER_MIC; + + for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { + if ((mixerRegs[x].recordNum >= 0) + && (oss_recmask & mixerRegs[x].oss_mask)) + break; + } + if (mixerRegs[x].ac97_regnum < 0) + return -ENODEV; + else { + int regval = (mixerRegs[x].recordNum << 8) | mixerRegs[x].recordNum; + int res = ac97_put_register (dev, AC97_RECORD_SELECT, regval); + if (res == 0) + return ac97_get_recmask (dev); + else + return res; + } +} + +/* Set the mixer DEV to the list of values in VALUE_LIST. Return 0 on + success, or a negative error code. */ +int +ac97_set_values (struct ac97_hwint *dev, + struct ac97_mixer_value_list *value_list) +{ + int x; + + for (x = 0; value_list[x].oss_channel != -1; x++) { + int chnum = value_list[x].oss_channel; + struct ac97_chn_desc *chent = ac97_find_chndesc (dev, chnum); + if (chent != NULL) { + u16 val; + int res; + + if (chent->is_stereo) + val = (value_list[x].value.stereo.right << 8) + | value_list[x].value.stereo.left; + else { + /* We do this so the returned value looks OK in the + mixer app. It's not necessary otherwise. */ + val = (value_list[x].value.mono << 8) + | value_list[x].value.mono; + } + res = ac97_set_mixer (dev, chnum, val); + if (res < 0) + return res; + } + else + return -ENODEV; + } + return 0; +} + +int +ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, caddr_t arg) +{ + int ret; + + switch (cmd) { + case SOUND_MIXER_READ_RECSRC: + ret = ac97_get_recmask (dev); + break; + + case SOUND_MIXER_WRITE_RECSRC: + { + if (get_user (ret, (int *) arg)) + ret = -EFAULT; + else + ret = ac97_set_recmask (dev, ret); + } + break; + + case SOUND_MIXER_READ_CAPS: + ret = SOUND_CAP_EXCL_INPUT; + break; + + case SOUND_MIXER_READ_DEVMASK: + ret = dev->mixer_devmask; + break; + + case SOUND_MIXER_READ_RECMASK: + ret = dev->mixer_recmask; + break; + + case SOUND_MIXER_READ_STEREODEVS: + ret = dev->mixer_stereomask; + break; + + default: + /* Read or write request. */ + ret = -EINVAL; + if (_IOC_TYPE (cmd) == 'M') { + int dir = _SIOC_DIR (cmd); + int channel = _IOC_NR (cmd); + + if (channel >= 0 && channel < SOUND_MIXER_NRDEVICES) { + ret = 0; + if (dir & _SIOC_WRITE) { + int val; + if (get_user (val, (int *) arg) == 0) + ret = ac97_set_mixer (dev, channel, val); + else + ret = -EFAULT; + } + if (ret >= 0 && (dir & _SIOC_READ)) { + if (dev->last_written_OSS_values[channel] + == AC97_REGVAL_UNKNOWN) + dev->last_written_OSS_values[channel] + = ac97_get_mixer_scaled (dev, channel); + ret = dev->last_written_OSS_values[channel]; + } + } + } + break; + } + + if (ret < 0) + return ret; + else + return put_user(ret, (int *) arg); +} + +EXPORT_SYMBOL(ac97_init); +EXPORT_SYMBOL(ac97_set_values); +EXPORT_SYMBOL(ac97_set_mixer); +EXPORT_SYMBOL(ac97_get_register); +EXPORT_SYMBOL(ac97_put_register); +EXPORT_SYMBOL(ac97_get_mixer_scaled); +EXPORT_SYMBOL(ac97_mixer_ioctl); +EXPORT_SYMBOL(ac97_reset); +MODULE_LICENSE("GPL"); + + +/* + * Local variables: + * c-basic-offset: 4 + * End: + */ diff -Nru a/sound/oss/ac97.h b/sound/oss/ac97.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ac97.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,220 @@ +/* + * ac97.h + * + * definitions for the AC97, Intel's Audio Codec 97 Spec + * also includes support for a generic AC97 interface + */ + +#ifndef _AC97_H_ +#define _AC97_H_ +#include "sound_config.h" +#include "sound_calls.h" + +#define AC97_RESET 0x0000 // +#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out +#define AC97_HEADPHONE_VOL 0x0004 // +#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output +#define AC97_MASTER_TONE 0x0008 // +#define AC97_PCBEEP_VOL 0x000a // none +#define AC97_PHONE_VOL 0x000c // TAD Input (mono) +#define AC97_MIC_VOL 0x000e // MIC Input (mono) +#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo) +#define AC97_CD_VOL 0x0012 // CD Input (stereo) +#define AC97_VIDEO_VOL 0x0014 // none +#define AC97_AUX_VOL 0x0016 // Aux Input (stereo) +#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo) +#define AC97_RECORD_SELECT 0x001a // +#define AC97_RECORD_GAIN 0x001c +#define AC97_RECORD_GAIN_MIC 0x001e +#define AC97_GENERAL_PURPOSE 0x0020 +#define AC97_3D_CONTROL 0x0022 +#define AC97_MODEM_RATE 0x0024 +#define AC97_POWER_CONTROL 0x0026 + +/* registers 0x0028 - 0x0058 are reserved */ + +/* AC'97 2.0 */ +#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ +#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_ADC_RATE 0x0032 /* PCM LR DAC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ +#define AC97_RESERVED_3A 0x003A /* Reserved */ +/* range 0x3c-0x58 - MODEM */ + +/* registers 0x005a - 0x007a are vendor reserved */ + +#define AC97_VENDOR_ID1 0x007c +#define AC97_VENDOR_ID2 0x007e + +/* volume control bit defines */ + +#define AC97_MUTE 0x8000 +#define AC97_MICBOOST 0x0040 +#define AC97_LEFTVOL 0x3f00 +#define AC97_RIGHTVOL 0x003f + +/* record mux defines */ + +#define AC97_RECMUX_MIC 0x0000 +#define AC97_RECMUX_CD 0x0101 +#define AC97_RECMUX_VIDEO 0x0202 /* not used */ +#define AC97_RECMUX_AUX 0x0303 +#define AC97_RECMUX_LINE 0x0404 +#define AC97_RECMUX_STEREO_MIX 0x0505 +#define AC97_RECMUX_MONO_MIX 0x0606 +#define AC97_RECMUX_PHONE 0x0707 + + +/* general purpose register bit defines */ + +#define AC97_GP_LPBK 0x0080 /* Loopback mode */ +#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */ +#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */ +#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */ +#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */ +#define AC97_GP_LD 0x1000 /* Loudness 1=on */ +#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */ +#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */ +#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */ + + +/* powerdown control and status bit defines */ + +/* status */ +#define AC97_PWR_MDM 0x0010 /* Modem section ready */ +#define AC97_PWR_REF 0x0008 /* Vref nominal */ +#define AC97_PWR_ANL 0x0004 /* Analog section ready */ +#define AC97_PWR_DAC 0x0002 /* DAC section ready */ +#define AC97_PWR_ADC 0x0001 /* ADC section ready */ + +/* control */ +#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */ +#define AC97_PWR_PR1 0x0200 /* DAC powerdown */ +#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */ +#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */ +#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */ +#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */ +#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */ +#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */ + +/* useful power states */ +#define AC97_PWR_D0 0x0000 /* everything on */ +#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4 +#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 +#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 +#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */ + +/* Total number of defined registers. */ +#define AC97_REG_CNT 64 + +/* Generic AC97 mixer interface. */ + +/* Structure describing access to the hardware. */ +struct ac97_hwint +{ + /* Perform any hardware-specific reset and initialization. Returns + 0 on success, or a negative error code. */ + int (*reset_device) (struct ac97_hwint *dev); + + /* Returns the contents of the specified register REG. The caller + should check to see if the desired contents are available in + the cache first, if applicable. Returns a positive unsigned value + representing the contents of the register, or a negative error + code. */ + int (*read_reg) (struct ac97_hwint *dev, u8 reg); + + /* Writes VALUE to register REG. Returns 0 on success, or a + negative error code. */ + int (*write_reg) (struct ac97_hwint *dev, u8 reg, u16 value); + + /* Hardware-specific information. */ + void *driver_private; + + /* Three OSS masks. */ + int mixer_devmask; + int mixer_stereomask; + int mixer_recmask; + + /* The mixer cache. The indices correspond to the AC97 hardware register + number / 2, since the register numbers are always an even number. + + Unknown values are set to -1; unsupported registers contain a + -2. */ + int last_written_mixer_values[AC97_REG_CNT]; + + /* A cache of values written via OSS; we need these so we can return + the values originally written by the user. + + Why the original user values? Because the real-world hardware + has less precision, and some existing applications assume that + they will get back the exact value that they wrote (aumix). + + A -1 value indicates that no value has been written to this mixer + channel via OSS. */ + int last_written_OSS_values[SOUND_MIXER_NRDEVICES]; +}; + +/* Values stored in the register cache. */ +#define AC97_REGVAL_UNKNOWN -1 +#define AC97_REG_UNSUPPORTED -2 + +struct ac97_mixer_value_list +{ + /* Mixer channel to set. List is terminated by a value of -1. */ + int oss_channel; + /* The scaled value to set it to; values generally range from 0-100. */ + union { + struct { + u8 left, right; + } stereo; + u8 mono; + } value; +}; + +/* Initialize the ac97 mixer by resetting it. */ +extern int ac97_init (struct ac97_hwint *dev); + +/* Sets the mixer DEV to the values in VALUE_LIST. Returns 0 on success, + or a negative error code. */ +extern int ac97_set_values (struct ac97_hwint *dev, + struct ac97_mixer_value_list *value_list); + +/* Sets one mixer channel OSS_CHANNEL to the scaled value OSS_VALUE. + Returns the resulting (rescaled) value, or a negative value + representing an error code. + + Stereo channels have two values in OSS_VALUE (the left value is in the + lower 8 bits, the right value is in the upper 8 bits). */ +extern int ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, + u16 oss_value); + +/* Return the contents of the specified AC97 register REG; it uses the + last-written value if it is available. */ +extern int ac97_get_register (struct ac97_hwint *dev, u8 reg); + +/* Writes the specified VALUE to the AC97 register REG in the mixer. + Takes care of setting the last-written cache as well. */ +extern int ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value); + +/* Returns the last OSS value written to the OSS_CHANNEL mixer channel. */ +extern int ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel); + +/* Default ioctl. */ +extern int ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, + caddr_t arg); + +/* Do a complete reset on the AC97 mixer, restoring all mixer registers to + the current values. Normally used after an APM resume event. */ +extern int ac97_reset (struct ac97_hwint *dev); +#endif + +/* + * Local variables: + * c-basic-offset: 4 + * End: + */ diff -Nru a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ac97_codec.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,1049 @@ + +/* + * ac97_codec.c: Generic AC97 mixer/modem module + * + * Derived from ac97 mixer in maestro and trident driver. + * + * Copyright 2000 Silicon Integrated System Corporation + * + * 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. + * + ************************************************************************** + * + * The Intel Audio Codec '97 specification is available at the Intel + * audio homepage: http://developer.intel.com/ial/scalableplatforms/audio/ + * + * The specification itself is currently available at: + * ftp://download.intel.com/ial/scalableplatforms/ac97r22.pdf + * + ************************************************************************** + * + * History + * v0.4 Mar 15 2000 Ollie Lho + * dual codecs support verified with 4 channels output + * v0.3 Feb 22 2000 Ollie Lho + * bug fix for record mask setting + * v0.2 Feb 10 2000 Ollie Lho + * add ac97_read_proc for /proc/driver/{vendor}/ac97 + * v0.1 Jan 14 2000 Ollie Lho + * Isolated from trident.c to support multiple ac97 codec + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel); +static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right); +static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ); +static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask); +static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg); + +static int ac97_init_mixer(struct ac97_codec *codec); + +static int wolfson_init(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); +static int sigmatel_9721_init(struct ac97_codec *codec); +static int sigmatel_9744_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); + + +/* + * AC97 operations. + * + * If you are adding a codec then you should be able to use + * eapd_ops - any codec that supports EAPD amp control (most) + * null_ops - any ancient codec that supports nothing + * + * The three functions are + * init - used for non AC97 standard initialisation + * amplifier - used to do amplifier control (1=on 0=off) + * digital - switch to digital modes (0 = analog) + * + * Not all codecs support all features, not all drivers use all the + * operations yet + */ + +static struct ac97_ops null_ops = { NULL, NULL, NULL }; +static struct ac97_ops default_ops = { NULL, eapd_control, NULL }; +static struct ac97_ops wolfson_ops = { wolfson_init, 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 }; +static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL }; +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 }; + +/* sorted by vendor/device id */ +static const struct { + u32 id; + char *name; + struct ac97_ops *ops; +} ac97_codec_ids[] = { + {0x41445303, "Analog Devices AD1819", &null_ops}, + {0x41445340, "Analog Devices AD1881", &null_ops}, + {0x41445348, "Analog Devices AD1881A", &null_ops}, + {0x41445360, "Analog Devices AD1885", &default_ops}, + {0x41445460, "Analog Devices AD1885", &default_ops}, + {0x414B4D00, "Asahi Kasei AK4540", &null_ops}, + {0x414B4D01, "Asahi Kasei AK4542", &null_ops}, + {0x414B4D02, "Asahi Kasei AK4543", &null_ops}, + {0x414C4710, "ALC200/200P", &null_ops}, + {0x43525900, "Cirrus Logic CS4297", &default_ops}, + {0x43525903, "Cirrus Logic CS4297", &default_ops}, + {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops}, + {0x43525914, "Cirrus Logic CS4297A rev B", &default_ops}, + {0x43525923, "Cirrus Logic CS4298", &null_ops}, + {0x4352592B, "Cirrus Logic CS4294", &null_ops}, + {0x4352592D, "Cirrus Logic CS4294", &null_ops}, + {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}, + {0x45838308, "ESS Allegro ES1988", &null_ops}, + {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */ + {0x4e534331, "National Semiconductor LM4549", &null_ops}, + {0x53494c22, "Silicon Laboratory Si3036", &null_ops}, + {0x53494c23, "Silicon Laboratory Si3038", &null_ops}, + {0x545200FF, "TriTech TR?????", &tritech_m_ops}, + {0x54524102, "TriTech TR28022", &null_ops}, + {0x54524103, "TriTech TR28023", &null_ops}, + {0x54524106, "TriTech TR28026", &null_ops}, + {0x54524108, "TriTech TR28028", &tritech_ops}, + {0x54524123, "TriTech TR A5", &null_ops}, + {0x574D4C00, "Wolfson WM9704", &wolfson_ops}, + {0x574D4C03, "Wolfson WM9703/9704", &wolfson_ops}, + {0x574D4C04, "Wolfson WM9704 (quad)", &wolfson_ops}, + {0x83847600, "SigmaTel STAC????", &null_ops}, + {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops}, + {0x83847605, "SigmaTel STAC9704", &null_ops}, + {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops}, + {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops}, + {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops}, + {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops}, + {0x83847684, "SigmaTel STAC9783/84?", &null_ops}, + {0x57454301, "Winbond 83971D", &null_ops}, +}; + +static const char *ac97_stereo_enhancements[] = +{ + /* 0 */ "No 3D Stereo Enhancement", + /* 1 */ "Analog Devices Phat Stereo", + /* 2 */ "Creative Stereo Enhancement", + /* 3 */ "National Semi 3D Stereo Enhancement", + /* 4 */ "YAMAHA Ymersion", + /* 5 */ "BBE 3D Stereo Enhancement", + /* 6 */ "Crystal Semi 3D Stereo Enhancement", + /* 7 */ "Qsound QXpander", + /* 8 */ "Spatializer 3D Stereo Enhancement", + /* 9 */ "SRS 3D Stereo Enhancement", + /* 10 */ "Platform Tech 3D Stereo Enhancement", + /* 11 */ "AKM 3D Audio", + /* 12 */ "Aureal Stereo Enhancement", + /* 13 */ "Aztech 3D Enhancement", + /* 14 */ "Binaura 3D Audio Enhancement", + /* 15 */ "ESS Technology Stereo Enhancement", + /* 16 */ "Harman International VMAx", + /* 17 */ "Nvidea 3D Stereo Enhancement", + /* 18 */ "Philips Incredible Sound", + /* 19 */ "Texas Instruments 3D Stereo Enhancement", + /* 20 */ "VLSI Technology 3D Stereo Enhancement", + /* 21 */ "TriTech 3D Stereo Enhancement", + /* 22 */ "Realtek 3D Stereo Enhancement", + /* 23 */ "Samsung 3D Stereo Enhancement", + /* 24 */ "Wolfson Microelectronics 3D Enhancement", + /* 25 */ "Delta Integration 3D Enhancement", + /* 26 */ "SigmaTel 3D Enhancement", + /* 27 */ "Winbond 3D Stereo Enhancement", + /* 28 */ "Rockwell 3D Stereo Enhancement", + /* 29 */ "Reserved 29", + /* 30 */ "Reserved 30", + /* 31 */ "Reserved 31" +}; + +/* this table has default mixer values for all OSS mixers. */ +static struct mixer_defaults { + int mixer; + unsigned int value; +} mixer_defaults[SOUND_MIXER_NRDEVICES] = { + /* all values 0 -> 100 in bytes */ + {SOUND_MIXER_VOLUME, 0x4343}, + {SOUND_MIXER_BASS, 0x4343}, + {SOUND_MIXER_TREBLE, 0x4343}, + {SOUND_MIXER_PCM, 0x4343}, + {SOUND_MIXER_SPEAKER, 0x4343}, + {SOUND_MIXER_LINE, 0x4343}, + {SOUND_MIXER_MIC, 0x0000}, + {SOUND_MIXER_CD, 0x4343}, + {SOUND_MIXER_ALTPCM, 0x4343}, + {SOUND_MIXER_IGAIN, 0x4343}, + {SOUND_MIXER_LINE1, 0x4343}, + {SOUND_MIXER_PHONEIN, 0x4343}, + {SOUND_MIXER_PHONEOUT, 0x4343}, + {SOUND_MIXER_VIDEO, 0x4343}, + {-1,0} +}; + +/* table to scale scale from OSS mixer value to AC97 mixer register value */ +static struct ac97_mixer_hw { + unsigned char offset; + int scale; +} ac97_hw[SOUND_MIXER_NRDEVICES]= { + [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,64}, + [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 16}, + [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 16}, + [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 32}, + [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 16}, + [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 32}, + [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 32}, + [SOUND_MIXER_CD] = {AC97_CD_VOL, 32}, + [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 64}, + [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 16}, + [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 32}, + [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 32}, + [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 64}, + [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 32}, +}; + +/* the following tables allow us to go from OSS <-> ac97 quickly. */ +enum ac97_recsettings { + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static const unsigned int ac97_rm2oss[] = { + [AC97_REC_MIC] = SOUND_MIXER_MIC, + [AC97_REC_CD] = SOUND_MIXER_CD, + [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO, + [AC97_REC_AUX] = SOUND_MIXER_LINE1, + [AC97_REC_LINE] = SOUND_MIXER_LINE, + [AC97_REC_STEREO]= SOUND_MIXER_IGAIN, + [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN +}; + +/* indexed by bit position */ +static const unsigned int ac97_oss_rm[] = { + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_IGAIN] = AC97_REC_STEREO, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + +/* 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) +{ + u16 val; + int ret = 0; + int scale; + struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; + + val = codec->codec_read(codec , mh->offset); + + if (val & AC97_MUTE) { + ret = 0; + } else if (AC97_STEREO_MASK & (1 << oss_channel)) { + /* nice stereo mixers .. */ + int left,right; + + left = (val >> 8) & 0x7f; + right = val & 0x7f; + + if (oss_channel == SOUND_MIXER_IGAIN) { + right = (right * 100) / mh->scale; + left = (left * 100) / mh->scale; + } else { + /* these may have 5 or 6 bit resolution */ + if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM) + scale = (1 << codec->bit_resolution); + else + scale = mh->scale; + + right = 100 - ((right * 100) / scale); + left = 100 - ((left * 100) / scale); + } + ret = left | (right << 8); + } else if (oss_channel == SOUND_MIXER_SPEAKER) { + ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_PHONEIN) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_PHONEOUT) { + scale = (1 << codec->bit_resolution); + ret = 100 - (((val & 0x1f) * 100) / scale); + } else if (oss_channel == SOUND_MIXER_MIC) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + /* the low bit is optional in the tone sliders and masking + it lets us avoid the 0xf 'bypass'.. */ + } else if (oss_channel == SOUND_MIXER_BASS) { + ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_TREBLE) { + ret = 100 - (((val & 0xe) * 100) / mh->scale); + } + +#ifdef DEBUG + printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), " + "0x%04x -> 0x%04x\n", + oss_channel, codec->id ? "Secondary" : "Primary", + mh->offset, val, ret); +#endif + + return ret; +} + +/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to + make sure all is well in arg land, call with spinlock held */ +static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right) +{ + u16 val = 0; + int scale; + struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; + +#ifdef DEBUG + printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), " + "left vol:%2d, right vol:%2d:", + oss_channel, codec->id ? "Secondary" : "Primary", + mh->offset, left, right); +#endif + + if (AC97_STEREO_MASK & (1 << oss_channel)) { + /* stereo mixers */ + if (left == 0 && right == 0) { + val = AC97_MUTE; + } else { + if (oss_channel == SOUND_MIXER_IGAIN) { + right = (right * mh->scale) / 100; + left = (left * mh->scale) / 100; + if (right >= mh->scale) + right = mh->scale-1; + if (left >= mh->scale) + left = mh->scale-1; + } else { + /* these may have 5 or 6 bit resolution */ + if (oss_channel == SOUND_MIXER_VOLUME || + oss_channel == SOUND_MIXER_ALTPCM) + scale = (1 << codec->bit_resolution); + else + scale = mh->scale; + + right = ((100 - right) * scale) / 100; + left = ((100 - left) * scale) / 100; + if (right >= scale) + right = scale-1; + if (left >= scale) + left = scale-1; + } + val = (left << 8) | right; + } + } else if (oss_channel == SOUND_MIXER_BASS) { + val = codec->codec_read(codec , mh->offset) & ~0x0f00; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= (left << 8) & 0x0e00; + } else if (oss_channel == SOUND_MIXER_TREBLE) { + val = codec->codec_read(codec , mh->offset) & ~0x000f; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= left & 0x000e; + } else if(left == 0) { + val = AC97_MUTE; + } else if (oss_channel == SOUND_MIXER_SPEAKER) { + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val = left << 1; + } else if (oss_channel == SOUND_MIXER_PHONEIN) { + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val = left; + } else if (oss_channel == SOUND_MIXER_PHONEOUT) { + scale = (1 << codec->bit_resolution); + left = ((100 - left) * scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val = left; + } else if (oss_channel == SOUND_MIXER_MIC) { + val = codec->codec_read(codec , mh->offset) & ~0x801f; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= left; + /* the low bit is optional in the tone sliders and masking + it lets us avoid the 0xf 'bypass'.. */ + } +#ifdef DEBUG + printk(" 0x%04x", val); +#endif + + codec->codec_write(codec, mh->offset, val); + +#ifdef DEBUG + val = codec->codec_read(codec, mh->offset); + printk(" -> 0x%04x\n", val); +#endif +} + +/* a thin wrapper for write_mixer */ +static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ) +{ + unsigned int left,right; + + /* cleanse input a little */ + right = ((val >> 8) & 0xff) ; + left = (val & 0xff) ; + + if (right > 100) right = 100; + if (left > 100) left = 100; + + codec->mixer_state[oss_mixer] = (right << 8) | left; + codec->write_mixer(codec, oss_mixer, left, right); +} + +/* read or write the recmask, the ac97 can really have left and right recording + 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) +{ + unsigned int val; + + if (rw) { + /* read it from the card */ + val = codec->codec_read(codec, AC97_RECORD_SELECT); +#ifdef DEBUG + printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val); +#endif + return (1 << ac97_rm2oss[val & 0x07]); + } + + /* else, write the first set in the mask as the + output */ + /* clear out current set value first (AC97 supports only 1 input!) */ + val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]); + if (mask != val) + mask &= ~val; + + val = ffs(mask); + val = ac97_oss_rm[val-1]; + val |= val << 8; /* set both channels */ + +#ifdef DEBUG + printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val); +#endif + + codec->codec_write(codec, AC97_RECORD_SELECT, val); + + return 0; +}; + +static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) +{ + int i, val = 0; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + 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; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + 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; + } + + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* give them the current record source */ + if (!codec->recmask_io) { + val = 0; + } else { + val = codec->recmask_io(codec, 1, 0); + } + break; + + case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ + val = codec->supported_mixers; + break; + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + val = codec->record_sources; + break; + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + val = codec->stereo_mixers; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: /* read a specific mixer */ + i = _IOC_NR(cmd); + + if (!supported_mixer(codec, i)) + return -EINVAL; + + /* do we ever want to touch the hardware? */ + /* val = codec->read_mixer(codec, i); */ + val = codec->mixer_state[i]; + break; + } + return put_user(val, (int *)arg); + } + + if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) { + codec->modcnt++; + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (!codec->recmask_io) return -EINVAL; + if (!val) return 0; + if (!(val &= codec->record_sources)) return -EINVAL; + + codec->recmask_io(codec, 0, val); + + return 0; + default: /* write a specific mixer */ + i = _IOC_NR(cmd); + + if (!supported_mixer(codec, i)) + return -EINVAL; + + ac97_set_mixer(codec, i, val); + + return 0; + } + } + return -EINVAL; +} + +/* entry point for /proc/driver/controller_vendor/ac97/%d */ +int ac97_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0, cap, extid, val, id1, id2; + struct ac97_codec *codec; + int is_ac97_20 = 0; + + if ((codec = data) == NULL) + return -ENODEV; + + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + len += sprintf (page+len, "Vendor name : %s\n", codec->name); + len += sprintf (page+len, "Vendor id : %04X %04X\n", id1, id2); + + extid = codec->codec_read(codec, AC97_EXTENDED_ID); + extid &= ~((1<<2)|(1<<4)|(1<<5)|(1<<10)|(1<<11)|(1<<12)|(1<<13)); + len += sprintf (page+len, "AC97 Version : %s\n", + extid ? "2.0 or later" : "1.0"); + if (extid) is_ac97_20 = 1; + + cap = codec->codec_read(codec, AC97_RESET); + len += sprintf (page+len, "Capabilities :%s%s%s%s%s%s\n", + cap & 0x0001 ? " -dedicated MIC PCM IN channel-" : "", + cap & 0x0002 ? " -reserved1-" : "", + cap & 0x0004 ? " -bass & treble-" : "", + cap & 0x0008 ? " -simulated stereo-" : "", + cap & 0x0010 ? " -headphone out-" : "", + cap & 0x0020 ? " -loudness-" : ""); + val = cap & 0x00c0; + len += sprintf (page+len, "DAC resolutions :%s%s%s\n", + " -16-bit-", + val & 0x0040 ? " -18-bit-" : "", + val & 0x0080 ? " -20-bit-" : ""); + val = cap & 0x0300; + len += sprintf (page+len, "ADC resolutions :%s%s%s\n", + " -16-bit-", + val & 0x0100 ? " -18-bit-" : "", + val & 0x0200 ? " -20-bit-" : ""); + len += sprintf (page+len, "3D enhancement : %s\n", + ac97_stereo_enhancements[(cap >> 10) & 0x1f]); + + val = codec->codec_read(codec, AC97_GENERAL_PURPOSE); + len += sprintf (page+len, "POP path : %s 3D\n" + "Sim. stereo : %s\n" + "3D enhancement : %s\n" + "Loudness : %s\n" + "Mono output : %s\n" + "MIC select : %s\n" + "ADC/DAC loopback : %s\n", + val & 0x8000 ? "post" : "pre", + val & 0x4000 ? "on" : "off", + val & 0x2000 ? "on" : "off", + val & 0x1000 ? "on" : "off", + val & 0x0200 ? "MIC" : "MIX", + val & 0x0100 ? "MIC2" : "MIC1", + val & 0x0080 ? "on" : "off"); + + extid = codec->codec_read(codec, AC97_EXTENDED_ID); + cap = extid; + len += sprintf (page+len, "Ext Capabilities :%s%s%s%s%s%s%s\n", + cap & 0x0001 ? " -var rate PCM audio-" : "", + cap & 0x0002 ? " -2x PCM audio out-" : "", + cap & 0x0008 ? " -var rate MIC in-" : "", + cap & 0x0040 ? " -PCM center DAC-" : "", + cap & 0x0080 ? " -PCM surround DAC-" : "", + cap & 0x0100 ? " -PCM LFE DAC-" : "", + cap & 0x0200 ? " -slot/DAC mappings-" : ""); + if (is_ac97_20) { + len += sprintf (page+len, "Front DAC rate : %d\n", + codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)); + } + + return len; +} + +/** + * ac97_probe_codec - Initialize and setup AC97-compatible codec + * @codec: (in/out) Kernel info for a single AC97 codec + * + * Reset the AC97 codec, then initialize the mixer and + * the rest of the @codec structure. + * + * The codec_read and codec_write fields of @codec are + * required to be setup and working when this function + * is called. All other fields are set by this function. + * + * codec_wait field of @codec can optionally be provided + * when calling this function. If codec_wait is not %NULL, + * this function will call codec_wait any time it is + * necessary to wait for the audio chip to reach the + * codec-ready state. If codec_wait is %NULL, then + * the default behavior is to call schedule_timeout. + * Currently codec_wait is used to wait for AC97 codec + * reset to complete. + * + * Returns 1 (true) on success, or 0 (false) on failure. + */ + +int ac97_probe_codec(struct ac97_codec *codec) +{ + u16 id1, id2; + u16 audio, modem; + int i; + + /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should + * be read zero. + * + * FIXME: is the following comment outdated? -jgarzik + * Probing of AC97 in this way is not reliable, it is not even SAFE !! + */ + codec->codec_write(codec, AC97_RESET, 0L); + + /* also according to spec, we wait for codec-ready state */ + if (codec->codec_wait) + codec->codec_wait(codec); + else + udelay(10); + + if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) { + printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n", + codec->id ? "Secondary" : "Primary"); + return 0; + } + + /* probe for Modem Codec */ + codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); + modem = codec->codec_read(codec, AC97_EXTENDED_MODEM_ID); + + codec->name = NULL; + codec->codec_ops = &null_ops; + + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) { + if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) { + codec->type = ac97_codec_ids[i].id; + codec->name = ac97_codec_ids[i].name; + codec->codec_ops = ac97_codec_ids[i].ops; + break; + } + } + if (codec->name == NULL) + codec->name = "Unknown"; + printk(KERN_INFO "ac97_codec: AC97 %s codec, id: 0x%04x:" + "0x%04x (%s)\n", audio ? "Audio" : (modem ? "Modem" : ""), + id1, id2, codec->name); + + return ac97_init_mixer(codec); +} + +static int ac97_init_mixer(struct ac97_codec *codec) +{ + u16 cap; + int i; + + cap = codec->codec_read(codec, AC97_RESET); + + /* mixer masks */ + codec->supported_mixers = AC97_SUPPORTED_MASK; + codec->stereo_mixers = AC97_STEREO_MASK; + codec->record_sources = AC97_RECORD_MASK; + if (!(cap & 0x04)) + codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + 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) == 0x1f1f) + codec->bit_resolution = 5; + else + codec->bit_resolution = 6; + + /* generic OSS to AC97 wrapper */ + codec->read_mixer = ac97_read_mixer; + codec->write_mixer = ac97_write_mixer; + codec->recmask_io = ac97_recmask_io; + codec->mixer_ioctl = ac97_mixer_ioctl; + + /* codec specific initialization for 4-6 channel output or secondary codec stuff */ + if (codec->codec_ops->init != NULL) { + codec->codec_ops->init(codec); + } + + /* initialize mixer channel volumes */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + struct mixer_defaults *md = &mixer_defaults[i]; + if (md->mixer == -1) + break; + if (!supported_mixer(codec, md->mixer)) + continue; + ac97_set_mixer(codec, md->mixer, md->value); + } + + return 1; +} + +#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */ +#define AC97_SIGMATEL_DAC2INVERT 0x6e +#define AC97_SIGMATEL_BIAS1 0x70 +#define AC97_SIGMATEL_BIAS2 0x72 +#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */ +#define AC97_SIGMATEL_CIC1 0x76 +#define AC97_SIGMATEL_CIC2 0x78 + + +static int sigmatel_9708_init(struct ac97_codec * codec) +{ + u16 codec72, codec6c; + + codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000; + codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG); + + if ((codec72==0) && (codec6c==0)) { + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000); + codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007); + } else if ((codec72==0x8000) && (codec6c==0)) { + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001); + codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008); + } else if ((codec72==0x8000) && (codec6c==0x0080)) { + /* nothing */ + } + codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + + +static int sigmatel_9721_init(struct ac97_codec * codec) +{ + /* Only set up secondary codec */ + if (codec->id == 0) + return 0; + + codec->codec_write(codec, AC97_SURROUND_MASTER, 0L); + + /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link + sloc 3,4 = 0x01, slot 7,8 = 0x00, */ + codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00); + + /* we don't have the crystal when we are on an AMR card, so use + BIT_CLK as our clock source. Write the magic word ABBA and read + back to enable register 0x78 */ + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_read(codec, AC97_SIGMATEL_CIC1); + + /* sync all the clocks*/ + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802); + + return 0; +} + + +static int sigmatel_9744_init(struct ac97_codec * codec) +{ + // patch for SigmaTel + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk + codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002); + codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + + +static int wolfson_init(struct ac97_codec * codec) +{ + 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); + return 0; +} + + +static int tritech_init(struct ac97_codec * codec) +{ + codec->codec_write(codec, 0x26, 0x0300); + codec->codec_write(codec, 0x26, 0x0000); + codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); + codec->codec_write(codec, AC97_RESERVED_3A, 0x0000); + return 0; +} + + +/* copied from drivers/sound/maestro.c */ +static int tritech_maestro_init(struct ac97_codec * codec) +{ + /* no idea what this does */ + codec->codec_write(codec, 0x2A, 0x0001); + codec->codec_write(codec, 0x2C, 0x0000); + codec->codec_write(codec, 0x2C, 0XFFFF); + return 0; +} + + +/* + * This is basically standard AC97. It should work as a default for + * almost all modern codecs. Note that some cards wire EAPD *backwards* + * That side of it is up to the card driver not us to cope with. + * + */ + +static int eapd_control(struct ac97_codec * codec, int on) +{ + if(on) + codec->codec_write(codec, AC97_POWER_CONTROL, + codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000); + else + codec->codec_write(codec, AC97_POWER_CONTROL, + codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000); + return 0; +} + +/* + * Crystal digital audio control (CS4299 + */ + +static int crystal_digital_control(struct ac97_codec *codec, int mode) +{ + u16 cv; + + switch(mode) + { + case 0: cv = 0x0; break; /* SPEN off */ + case 1: cv = 0x8004; break; /* 48KHz digital */ + case 2: cv = 0x8104; break; /* 44.1KHz digital */ + default: + return -1; /* Not supported yet(eg AC3) */ + } + codec->codec_write(codec, 0x68, 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 :) */ +static int pt101_init(struct ac97_codec * codec) +{ + printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); + /* who knows.. */ + codec->codec_write(codec, 0x2A, 0x0001); + codec->codec_write(codec, 0x2C, 0x0000); + codec->codec_write(codec, 0x2C, 0xFFFF); + codec->codec_write(codec, 0x10, 0x9F1F); + codec->codec_write(codec, 0x12, 0x0808); + codec->codec_write(codec, 0x14, 0x9F1F); + codec->codec_write(codec, 0x16, 0x9F1F); + codec->codec_write(codec, 0x18, 0x0404); + codec->codec_write(codec, 0x1A, 0x0000); + codec->codec_write(codec, 0x1C, 0x0000); + codec->codec_write(codec, 0x02, 0x0404); + codec->codec_write(codec, 0x04, 0x0808); + codec->codec_write(codec, 0x0C, 0x801F); + codec->codec_write(codec, 0x0E, 0x801F); + return 0; +} +#endif + + +EXPORT_SYMBOL(ac97_read_proc); +EXPORT_SYMBOL(ac97_probe_codec); + +/* + * AC97 library support routines + */ + +/** + * ac97_set_dac_rate - set codec rate adaption + * @codec: ac97 code + * @rate: rate in hertz + * + * Set the DAC rate. Assumes the codec supports VRA. The caller is + * expected to have checked this little detail. + */ + +unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate) +{ + unsigned int new_rate = rate; + u32 dacp; + u32 mast_vol, phone_vol, mono_vol, pcm_vol; + u32 mute_vol = 0x8000; /* The mute volume? */ + + if(rate != codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)) + { + /* Mute several registers */ + mast_vol = codec->codec_read(codec, AC97_MASTER_VOL_STEREO); + mono_vol = codec->codec_read(codec, AC97_MASTER_VOL_MONO); + phone_vol = codec->codec_read(codec, AC97_HEADPHONE_VOL); + pcm_vol = codec->codec_read(codec, AC97_PCMOUT_VOL); + codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mute_vol); + codec->codec_write(codec, AC97_MASTER_VOL_MONO, mute_vol); + codec->codec_write(codec, AC97_HEADPHONE_VOL, mute_vol); + codec->codec_write(codec, AC97_PCMOUT_VOL, mute_vol); + + /* Power down the DAC */ + dacp=codec->codec_read(codec, AC97_POWER_CONTROL); + codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0200); + /* Load the rate and read the effective rate */ + codec->codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate); + new_rate=codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE); + /* Power it back up */ + codec->codec_write(codec, AC97_POWER_CONTROL, dacp); + + /* Restore volumes */ + codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mast_vol); + codec->codec_write(codec, AC97_MASTER_VOL_MONO, mono_vol); + codec->codec_write(codec, AC97_HEADPHONE_VOL, phone_vol); + codec->codec_write(codec, AC97_PCMOUT_VOL, pcm_vol); + } + return new_rate; +} + +EXPORT_SYMBOL(ac97_set_dac_rate); + +/** + * ac97_set_adc_rate - set codec rate adaption + * @codec: ac97 code + * @rate: rate in hertz + * + * Set the ADC rate. Assumes the codec supports VRA. The caller is + * expected to have checked this little detail. + */ + +unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate) +{ + unsigned int new_rate = rate; + u32 dacp; + + if(rate != codec->codec_read(codec, AC97_PCM_LR_ADC_RATE)) + { + /* Power down the ADC */ + dacp=codec->codec_read(codec, AC97_POWER_CONTROL); + codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0100); + /* Load the rate and read the effective rate */ + codec->codec_write(codec, AC97_PCM_LR_ADC_RATE, rate); + new_rate=codec->codec_read(codec, AC97_PCM_LR_ADC_RATE); + /* Power it back up */ + codec->codec_write(codec, AC97_POWER_CONTROL, dacp); + } + return new_rate; +} + +EXPORT_SYMBOL(ac97_set_adc_rate); + +int ac97_save_state(struct ac97_codec *codec) +{ + return 0; +} + +EXPORT_SYMBOL(ac97_save_state); + +int ac97_restore_state(struct ac97_codec *codec) +{ + int i; + unsigned int left, right, val; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!supported_mixer(codec, i)) + continue; + + val = codec->mixer_state[i]; + right = val >> 8; + left = val & 0xff; + codec->write_mixer(codec, i, left, right); + } + return 0; +} + +EXPORT_SYMBOL(ac97_restore_state); + +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/aci.c b/sound/oss/aci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/aci.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,711 @@ +/* + * Audio Command Interface (ACI) driver (sound/aci.c) + * + * ACI is a protocol used to communicate with the microcontroller on + * some sound cards produced by miro, e.g. the miroSOUND PCM12 and + * PCM20. The ACI has been developed for miro by Norberto Pellicci + * . Special thanks to both him and miro for + * providing the ACI specification. + * + * The main function of the ACI is to control the mixer and to get a + * product identification. On the PCM20, ACI also controls the radio + * tuner on this card, this is supported in the Video for Linux + * miropcm20 driver. + * - + * This is a fullfeatured implementation. Unsupported features + * are bugs... (: + * + * It is not longer necessary to load the mad16 module first. The + * user is currently responsible to set the mad16 mixer correctly. + * + * To toggle the solo mode for full duplex operation just use the OSS + * record switch for the pcm ('wave') controller. Robert + * - + * + * Revision history: + * + * 1995-11-10 Markus Kuhn + * First version written. + * 1995-12-31 Markus Kuhn + * Second revision, general code cleanup. + * 1996-05-16 Hannu Savolainen + * Integrated with other parts of the driver. + * 1996-05-28 Markus Kuhn + * Initialize CS4231A mixer, make ACI first mixer, + * use new private mixer API for solo mode. + * 1998-08-18 Ruurd Reitsma + * Small modification to export ACI functions and + * complete modularisation. + * 2000-06-20 Robert Siemer + * Don't initialize the CS4231A mixer anymore, so the code is + * working again, and other small changes to fit in todays + * kernels. + * 2000-08-26 Robert Siemer + * Clean up and rewrite for 2.4.x. Maybe it's SMP safe now... (: + * ioctl bugfix, and integration of solo-mode into OSS-API, + * added (OSS-limited) equalizer support, return value bugfix, + * changed param aci_reset to reset, new params: ide, wss. + * 2001-04-20 Robert Siemer + * even more cleanups... + * 2001-10-08 Arnaldo Carvalho de Melo + * Get rid of check_region, .bss optimizations, use set_current_state + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" + +int aci_port; /* as determined by bit 4 in the OPTi 929 MC4 register */ +int aci_idcode[2]; /* manufacturer and product ID */ +int aci_version; /* ACI firmware version */ + +EXPORT_SYMBOL(aci_port); +EXPORT_SYMBOL(aci_idcode); +EXPORT_SYMBOL(aci_version); + +#include "aci.h" + + +static int aci_solo; /* status bit of the card that can't be * + * checked with ACI versions prior to 0xb0 */ +static int aci_amp; /* status bit for power-amp/line-out level + but I have no docs about what is what... */ +static int aci_micpreamp=3; /* microphone preamp-level that can't be * + * checked with ACI versions prior to 0xb0 */ + +static int mixer_device; +static struct semaphore aci_sem; + +#ifdef MODULE +static int reset; +MODULE_PARM(reset,"i"); +MODULE_PARM_DESC(reset,"When set to 1, reset aci mixer."); +#else +static int reset = 1; +#endif + +static int ide=-1; +MODULE_PARM(ide,"i"); +MODULE_PARM_DESC(ide,"1 enable, 0 disable ide-port - untested" + " default: do nothing"); +static int wss=-1; +MODULE_PARM(wss,"i"); +MODULE_PARM_DESC(wss,"change between ACI/WSS-mixer; use 0 and 1 - untested" + " default: do nothing; for PCM1-pro only"); + +#if DEBUG +static void print_bits(unsigned char c) +{ + int j; + printk(KERN_DEBUG "aci: "); + + for (j=7; j>=0; j--) { + printk("%d", (c >> j) & 0x1); + } + + printk("\n"); +} +#endif + +/* + * This busy wait code normally requires less than 15 loops and + * practically always less than 100 loops on my i486/DX2 66 MHz. + * + * Warning: Waiting on the general status flag after reseting the MUTE + * function can take a VERY long time, because the PCM12 does some kind + * of fade-in effect. For this reason, access to the MUTE function has + * not been implemented at all. + * + * - The OSS interface has no mute option. It takes about 3 seconds to + * fade-in on my PCM20. busy_wait() handles it great now... Robert + */ + +static int busy_wait(void) +{ + #define MINTIME 500 + long timeout; + unsigned char byte; + + for (timeout = 1; timeout <= MINTIME+30; timeout++) { + if (((byte=inb(BUSY_REGISTER)) & 1) == 0) { + if (timeout >= MINTIME) + printk(KERN_DEBUG "aci: Got READYFLAG in round %ld.\n", timeout-MINTIME); + return byte; + } + if (timeout >= MINTIME) { + long out=10*HZ; + switch (timeout-MINTIME) { + case 0 ... 9: + out /= 10; + case 10 ... 19: + out /= 10; + case 20 ... 30: + out /= 10; + default: + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(out); + break; + } + } + } + printk(KERN_WARNING "aci: busy_wait() time out.\n"); + return -EBUSY; +} + +/* The four ACI command types are fucked up. [-: + * implied is: 1w - special case for INIT + * write is: 2w1r + * read is: x(1w1r) where x is 1 or 2 (1 CHECK_SIG, 1 CHECK_STER, + * 1 VERSION, 2 IDCODE) + * the command is only in the first write, rest is protocol overhead + * + * indexed is technically a write and used for STATUS + * and the special case for TUNE is: 3w1r + * + * Here the new general sheme: TUNE --> aci_rw_cmd(x, y, z) + * indexed and write --> aci_rw_cmd(x, y, -1) + * implied and read (x=1) --> aci_rw_cmd(x, -1, -1) + * + * Read (x>=2) is not implemented (only used during initialization). + * Use aci_idcode[2] and aci_version... Robert + */ + +/* Some notes for error detection: theoretically it is possible. + * But it doubles the I/O-traffic from ww(r) to wwwrw(r) in the normal + * case and doesn't seem to be designed for that... Robert + */ + +static inline int aci_rawwrite(unsigned char byte) +{ + if (busy_wait() >= 0) { +#if DEBUG + printk(KERN_DEBUG "aci_rawwrite(%d)\n", byte); +#endif + outb(byte, COMMAND_REGISTER); + return 0; + } else + return -EBUSY; +} + +static inline int aci_rawread(void) +{ + unsigned char byte; + + if (busy_wait() >= 0) { + byte=inb(STATUS_REGISTER); +#if DEBUG + printk(KERN_DEBUG "%d = aci_rawread()\n", byte); +#endif + return byte; + } else + return -EBUSY; +} + + +int aci_rw_cmd(int write1, int write2, int write3) +{ + int write[] = {write1, write2, write3}; + int read = -EINTR, i; + + if (down_interruptible(&aci_sem)) + goto out; + + for (i=0; i<3; i++) { + if (write[i]< 0 || write[i] > 255) + break; + else { + read = aci_rawwrite(write[i]); + if (read < 0) + goto out_up; + } + + } + + read = aci_rawread(); +out_up: up(&aci_sem); +out: return read; +} + +EXPORT_SYMBOL(aci_rw_cmd); + +static int setvolume(caddr_t arg, + unsigned char left_index, unsigned char right_index) +{ + int vol, ret, uservol, buf; + + __get_user(uservol, (int *)arg); + + /* left channel */ + vol = uservol & 0xff; + if (vol > 100) + vol = 100; + vol = SCALE(100, 0x20, vol); + if ((buf=aci_write_cmd(left_index, 0x20 - vol))<0) + return buf; + ret = SCALE(0x20, 100, vol); + + + /* right channel */ + vol = (uservol >> 8) & 0xff; + if (vol > 100) + vol = 100; + vol = SCALE(100, 0x20, vol); + if ((buf=aci_write_cmd(right_index, 0x20 - vol))<0) + return buf; + ret |= SCALE(0x20, 100, vol) << 8; + + __put_user(ret, (int *)arg); + + return 0; +} + +static int getvolume(caddr_t arg, + unsigned char left_index, unsigned char right_index) +{ + int vol; + int buf; + + /* left channel */ + if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0) + return buf; + vol = SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0); + + /* right channel */ + if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0) + return buf; + vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8; + + __put_user(vol, (int *)arg); + + return 0; +} + + +/* The equalizer is somewhat strange on the ACI. From -12dB to +12dB + * write: 0xff..down.to..0x80==0x00..up.to..0x7f + */ + +static inline unsigned int eq_oss2aci(unsigned int vol) +{ + int boost=0; + unsigned int ret; + + if (vol > 100) + vol = 100; + if (vol > 50) { + vol -= 51; + boost=1; + } + if (boost) + ret=SCALE(49, 0x7e, vol)+1; + else + ret=0xff - SCALE(50, 0x7f, vol); + return ret; +} + +static inline unsigned int eq_aci2oss(unsigned int vol) +{ + if (vol < 0x80) + return SCALE(0x7f, 50, vol) + 50; + else + return SCALE(0x7f, 50, 0xff-vol); +} + + +static int setequalizer(caddr_t arg, + unsigned char left_index, unsigned char right_index) +{ + int buf; + unsigned int vol; + + __get_user(vol, (int *)arg); + + /* left channel */ + if ((buf=aci_write_cmd(left_index, eq_oss2aci(vol & 0xff)))<0) + return buf; + + /* right channel */ + if ((buf=aci_write_cmd(right_index, eq_oss2aci((vol>>8) & 0xff)))<0) + return buf; + + /* the ACI equalizer is more precise */ + return 0; +} + +static int getequalizer(caddr_t arg, + unsigned char left_index, unsigned char right_index) +{ + int buf; + unsigned int vol; + + /* left channel */ + if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0) + return buf; + vol = eq_aci2oss(buf); + + /* right channel */ + if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0) + return buf; + vol |= eq_aci2oss(buf) << 8; + + __put_user(vol, (int *)arg); + + return 0; +} + +static int aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + int vol, buf; + + switch (cmd) { + case SOUND_MIXER_WRITE_VOLUME: + return setvolume(arg, 0x01, 0x00); + case SOUND_MIXER_WRITE_CD: + return setvolume(arg, 0x3c, 0x34); + case SOUND_MIXER_WRITE_MIC: + return setvolume(arg, 0x38, 0x30); + case SOUND_MIXER_WRITE_LINE: + return setvolume(arg, 0x39, 0x31); + case SOUND_MIXER_WRITE_SYNTH: + return setvolume(arg, 0x3b, 0x33); + case SOUND_MIXER_WRITE_PCM: + return setvolume(arg, 0x3a, 0x32); + case MIXER_WRITE(SOUND_MIXER_RADIO): /* fall through */ + case SOUND_MIXER_WRITE_LINE1: /* AUX1 or radio */ + return setvolume(arg, 0x3d, 0x35); + case SOUND_MIXER_WRITE_LINE2: /* AUX2 */ + return setvolume(arg, 0x3e, 0x36); + case SOUND_MIXER_WRITE_BASS: /* set band one and two */ + if (aci_idcode[1]=='C') { + if ((buf=setequalizer(arg, 0x48, 0x40)) || + (buf=setequalizer(arg, 0x49, 0x41))); + return buf; + } + break; + case SOUND_MIXER_WRITE_TREBLE: /* set band six and seven */ + if (aci_idcode[1]=='C') { + if ((buf=setequalizer(arg, 0x4d, 0x45)) || + (buf=setequalizer(arg, 0x4e, 0x46))); + return buf; + } + break; + case SOUND_MIXER_WRITE_IGAIN: /* MIC pre-amp */ + if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { + __get_user(vol, (int *)arg); + vol = vol & 0xff; + if (vol > 100) + vol = 100; + vol = SCALE(100, 3, vol); + if ((buf=aci_write_cmd(ACI_WRITE_IGAIN, vol))<0) + return buf; + aci_micpreamp = vol; + vol = SCALE(3, 100, vol); + vol |= (vol << 8); + __put_user(vol, (int *)arg); + return 0; + } + break; + case SOUND_MIXER_WRITE_OGAIN: /* Power-amp/line-out level */ + if (aci_idcode[1]=='A' || aci_idcode[1]=='B') { + __get_user(buf, (int *)arg); + buf = buf & 0xff; + if (buf > 50) + vol = 1; + else + vol = 0; + if ((buf=aci_write_cmd(ACI_SET_POWERAMP, vol))<0) + return buf; + aci_amp = vol; + if (aci_amp) + buf = (100 || 100<<8); + else + buf = 0; + __put_user(buf, (int *)arg); + return 0; + } + break; + case SOUND_MIXER_WRITE_RECSRC: + /* handle solo mode control */ + __get_user(buf, (int *)arg); + /* unset solo when RECSRC for PCM is requested */ + if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { + vol = !(buf & SOUND_MASK_PCM); + if ((buf=aci_write_cmd(ACI_SET_SOLOMODE, vol))<0) + return buf; + aci_solo = vol; + } + buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE| + SOUND_MASK_SYNTH| SOUND_MASK_LINE2); + if (aci_idcode[1] == 'C') /* PCM20 radio */ + buf |= SOUND_MASK_RADIO; + else + buf |= SOUND_MASK_LINE1; + if (!aci_solo) + buf |= SOUND_MASK_PCM; + __put_user(buf, (int *)arg); + return 0; + case SOUND_MIXER_READ_DEVMASK: + buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD | + SOUND_MASK_MIC | SOUND_MASK_LINE | + SOUND_MASK_SYNTH | SOUND_MASK_PCM | + SOUND_MASK_LINE2); + switch (aci_idcode[1]) { + case 'C': /* PCM20 radio */ + buf |= (SOUND_MASK_RADIO | SOUND_MASK_IGAIN | + SOUND_MASK_BASS | SOUND_MASK_TREBLE); + break; + case 'B': /* PCM12 */ + buf |= (SOUND_MASK_LINE1 | SOUND_MASK_IGAIN | + SOUND_MASK_OGAIN); + break; + case 'A': /* PCM1-pro */ + buf |= (SOUND_MASK_LINE1 | SOUND_MASK_OGAIN); + break; + default: + buf |= SOUND_MASK_LINE1; + } + __put_user(buf, (int *)arg); + return 0; + case SOUND_MIXER_READ_STEREODEVS: + buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD | + SOUND_MASK_MIC | SOUND_MASK_LINE | + SOUND_MASK_SYNTH | SOUND_MASK_PCM | + SOUND_MASK_LINE2); + switch (aci_idcode[1]) { + case 'C': /* PCM20 radio */ + buf |= (SOUND_MASK_RADIO | + SOUND_MASK_BASS | SOUND_MASK_TREBLE); + break; + default: + buf |= SOUND_MASK_LINE1; + } + __put_user(buf, (int *)arg); + return 0; + case SOUND_MIXER_READ_RECMASK: + buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE| + SOUND_MASK_SYNTH| SOUND_MASK_LINE2| SOUND_MASK_PCM); + if (aci_idcode[1] == 'C') /* PCM20 radio */ + buf |= SOUND_MASK_RADIO; + else + buf |= SOUND_MASK_LINE1; + + __put_user(buf, (int *)arg); + return 0; + case SOUND_MIXER_READ_RECSRC: + buf = (SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | + SOUND_MASK_SYNTH | SOUND_MASK_LINE2); + /* do we need aci_solo or can I get it from the ACI? */ + switch (aci_idcode[1]) { + case 'B': /* PCM12 */ + case 'C': /* PCM20 radio */ + if (aci_version >= 0xb0) { + if ((vol=aci_rw_cmd(ACI_STATUS, + ACI_S_GENERAL, -1))<0) + return vol; + if (vol & 0x20) + buf |= SOUND_MASK_PCM; + } + else + if (!aci_solo) + buf |= SOUND_MASK_PCM; + break; + default: + buf |= SOUND_MASK_PCM; + } + if (aci_idcode[1] == 'C') /* PCM20 radio */ + buf |= SOUND_MASK_RADIO; + else + buf |= SOUND_MASK_LINE1; + + __put_user(buf, (int *)arg); + return 0; + case SOUND_MIXER_READ_CAPS: + __put_user(0, (int *)arg); + return 0; + case SOUND_MIXER_READ_VOLUME: + return getvolume(arg, 0x04, 0x03); + case SOUND_MIXER_READ_CD: + return getvolume(arg, 0x0a, 0x09); + case SOUND_MIXER_READ_MIC: + return getvolume(arg, 0x06, 0x05); + case SOUND_MIXER_READ_LINE: + return getvolume(arg, 0x08, 0x07); + case SOUND_MIXER_READ_SYNTH: + return getvolume(arg, 0x0c, 0x0b); + case SOUND_MIXER_READ_PCM: + return getvolume(arg, 0x0e, 0x0d); + case MIXER_READ(SOUND_MIXER_RADIO): /* fall through */ + case SOUND_MIXER_READ_LINE1: /* AUX1 */ + return getvolume(arg, 0x11, 0x10); + case SOUND_MIXER_READ_LINE2: /* AUX2 */ + return getvolume(arg, 0x13, 0x12); + case SOUND_MIXER_READ_BASS: /* get band one */ + if (aci_idcode[1]=='C') { + return getequalizer(arg, 0x23, 0x22); + } + break; + case SOUND_MIXER_READ_TREBLE: /* get band seven */ + if (aci_idcode[1]=='C') { + return getequalizer(arg, 0x2f, 0x2e); + } + break; + case SOUND_MIXER_READ_IGAIN: /* MIC pre-amp */ + if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { + /* aci_micpreamp or ACI? */ + if (aci_version >= 0xb0) { + if ((buf=aci_indexed_cmd(ACI_STATUS, + ACI_S_READ_IGAIN))<0) + return buf; + } + else + buf=aci_micpreamp; + vol = SCALE(3, 100, buf <= 3 ? buf : 3); + vol |= vol << 8; + __put_user(vol, (int *)arg); + return 0; + } + break; + case SOUND_MIXER_READ_OGAIN: + if (aci_amp) + buf = (100 || 100<<8); + else + buf = 0; + __put_user(buf, (int *)arg); + return 0; + } + return -EINVAL; +} + +static struct mixer_operations aci_mixer_operations = +{ + owner: THIS_MODULE, + id: "ACI", + ioctl: aci_mixer_ioctl +}; + +/* + * There is also an internal mixer in the codec (CS4231A or AD1845), + * that deserves no purpose in an ACI based system which uses an + * external ACI controlled stereo mixer. Make sure that this codec + * mixer has the AUX1 input selected as the recording source, that the + * input gain is set near maximum and that the other channels going + * from the inputs to the codec output are muted. + */ + +static int __init attach_aci(void) +{ + char *boardname; + int i, rc = -EBUSY; + + init_MUTEX(&aci_sem); + + outb(0xE3, 0xf8f); /* Write MAD16 password */ + aci_port = (inb(0xf90) & 0x10) ? + 0x344: 0x354; /* Get aci_port from MC4_PORT */ + + if (!request_region(aci_port, 3, "sound mixer (ACI)")) { + printk(KERN_NOTICE + "aci: I/O area 0x%03x-0x%03x already used.\n", + aci_port, aci_port+2); + goto out; + } + + /* force ACI into a known state */ + rc = -EFAULT; + for (i=0; i<3; i++) + if (aci_rw_cmd(ACI_ERROR_OP, -1, -1)<0) + goto out_release_region; + + /* official this is one aci read call: */ + rc = -EFAULT; + if ((aci_idcode[0]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0 || + (aci_idcode[1]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0) { + printk(KERN_ERR "aci: Failed to read idcode on 0x%03x.\n", + aci_port); + goto out_release_region; + } + + if ((aci_version=aci_rw_cmd(ACI_READ_VERSION, -1, -1))<0) { + printk(KERN_ERR "aci: Failed to read version on 0x%03x.\n", + aci_port); + goto out_release_region; + } + + if (aci_idcode[0] == 'm') { + /* It looks like a miro sound card. */ + switch (aci_idcode[1]) { + case 'A': + boardname = "PCM1 pro / early PCM12"; + break; + case 'B': + boardname = "PCM12"; + break; + case 'C': + boardname = "PCM20 radio"; + break; + default: + boardname = "unknown miro"; + } + } else { + printk(KERN_WARNING "aci: Warning: unsupported card! - " + "no hardware, no specs...\n"); + boardname = "unknown Cardinal Technologies"; + } + + printk(KERN_INFO " at 0x%03x\n", + aci_version, + aci_idcode[0], aci_idcode[1], + aci_idcode[0], aci_idcode[1], + boardname, aci_port); + + rc = -EBUSY; + if (reset) { + /* first write()s after reset fail with my PCM20 */ + if (aci_rw_cmd(ACI_INIT, -1, -1)<0 || + aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0 || + aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0) + goto out_release_region; + } + + /* the PCM20 is muted after reset (and reboot) */ + if (aci_rw_cmd(ACI_SET_MUTE, 0x00, -1)<0) + goto out_release_region; + + if (ide>=0) + if (aci_rw_cmd(ACI_SET_IDE, !ide, -1)<0) + goto out_release_region; + + if (wss>=0 && aci_idcode[1]=='A') + if (aci_rw_cmd(ACI_SET_WSS, !!wss, -1)<0) + goto out_release_region; + + mixer_device = sound_install_mixer(MIXER_DRIVER_VERSION, boardname, + &aci_mixer_operations, + sizeof(aci_mixer_operations), NULL); + rc = 0; + if (mixer_device < 0) { + printk(KERN_ERR "aci: Failed to install mixer.\n"); + rc = mixer_device; + goto out_release_region; + } /* else Maybe initialize the CS4231A mixer here... */ +out: return rc; +out_release_region: + release_region(aci_port, 3); + goto out; +} + +static void __exit unload_aci(void) +{ + sound_unload_mixerdev(mixer_device); + release_region(aci_port, 3); +} + +module_init(attach_aci); +module_exit(unload_aci); +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/aci.h b/sound/oss/aci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/aci.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,58 @@ +#ifndef _ACI_H_ +#define _ACI_H_ + +extern int aci_port; +extern int aci_idcode[2]; /* manufacturer and product ID */ +extern int aci_version; /* ACI firmware version */ +extern int aci_rw_cmd(int write1, int write2, int write3); + +#define aci_indexed_cmd(a, b) aci_rw_cmd(a, b, -1) +#define aci_write_cmd(a, b) aci_rw_cmd(a, b, -1) +#define aci_read_cmd(a) aci_rw_cmd(a,-1, -1) + +#define COMMAND_REGISTER (aci_port) /* write register */ +#define STATUS_REGISTER (aci_port + 1) /* read register */ +#define BUSY_REGISTER (aci_port + 2) /* also used for rds */ + +#define RDS_REGISTER BUSY_REGISTER + +#define ACI_SET_MUTE 0x0d +#define ACI_SET_POWERAMP 0x0f +#define ACI_SET_TUNERMUTE 0xa3 +#define ACI_SET_TUNERMONO 0xa4 +#define ACI_SET_IDE 0xd0 +#define ACI_SET_WSS 0xd1 +#define ACI_SET_SOLOMODE 0xd2 +#define ACI_WRITE_IGAIN 0x03 +#define ACI_WRITE_TUNE 0xa7 +#define ACI_READ_TUNERSTEREO 0xa8 +#define ACI_READ_TUNERSTATION 0xa9 +#define ACI_READ_VERSION 0xf1 +#define ACI_READ_IDCODE 0xf2 +#define ACI_INIT 0xff +#define ACI_STATUS 0xf0 +#define ACI_S_GENERAL 0x00 +#define ACI_S_READ_IGAIN 0x21 +#define ACI_ERROR_OP 0xdf + +/* + * The following macro SCALE can be used to scale one integer volume + * value into another one using only integer arithmetic. If the input + * value x is in the range 0 <= x <= xmax, then the result will be in + * the range 0 <= SCALE(xmax,ymax,x) <= ymax. + * + * This macro has for all xmax, ymax > 0 and all 0 <= x <= xmax the + * following nice properties: + * + * - SCALE(xmax,ymax,xmax) = ymax + * - SCALE(xmax,ymax,0) = 0 + * - SCALE(xmax,ymax,SCALE(ymax,xmax,SCALE(xmax,ymax,x))) = SCALE(xmax,ymax,x) + * + * In addition, the rounding error is minimal and nicely distributed. + * The proofs are left as an exercise to the reader. + */ + +#define SCALE(xmax,ymax,x) (((x)*(ymax)+(xmax)/2)/(xmax)) + + +#endif /* _ACI_H_ */ diff -Nru a/sound/oss/ad1816.c b/sound/oss/ad1816.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ad1816.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,1478 @@ +/* + * + * AD1816 lowlevel sound driver for Linux 2.2.0 and above + * + * Copyright (C) 1998 by Thorsten Knabe + * + * Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996 + * + * + * version: 1.3.1 + * status: experimental + * date: 1999/4/18 + * + * Changes: + * Oleg Drokin: Some cleanup of load/unload functions. 1998/11/24 + * + * Thorsten Knabe: attach and unload rewritten, + * some argument checks added 1998/11/30 + * + * Thorsten Knabe: Buggy isa bridge workaround added 1999/01/16 + * + * David Moews/Thorsten Knabe: Introduced options + * parameter. Added slightly modified patch from + * David Moews to disable dsp audio sources by setting + * bit 0 of options parameter. This seems to be + * required by some Aztech/Newcom SC-16 cards. 1999/04/18 + * + * Christoph Hellwig: Adapted to module_init/module_exit. 2000/03/03 + * + * Christoph Hellwig: Added isapnp support 2000/03/15 + * + * Arnaldo Carvalho de Melo: get rid of check_region 2001/10/07 + */ + +#include +#include +#include +#include +#include + +#include "sound_config.h" + +#define DEBUGNOISE(x) +#define DEBUGINFO(x) +#define DEBUGLOG(x) +#define DEBUGWARN(x) + +#define CHECK_FOR_POWER { int timeout=100; \ + while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\ + timeout--; \ + } \ + if (timeout==0) {\ + printk(KERN_WARNING "ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \ + } \ +} + +/* structure to hold device specific information */ +typedef struct +{ + int base; /* set in attach */ + int irq; + int dma_playback; + int dma_capture; + + int speed; /* open */ + int channels; + int audio_format; + unsigned char format_bits; + int audio_mode; + int opened; + + int recmask; /* setup */ + int supported_devices; + int supported_rec_devices; + unsigned short levels[SOUND_MIXER_NRDEVICES]; + int dev_no; /* this is the # in audio_devs and NOT + in ad1816_info */ + int irq_ok; + int *osp; + +} ad1816_info; + +static int nr_ad1816_devs; +static int ad1816_clockfreq = 33000; +static int options; + +/* for backward mapping of irq to sound device */ + +static volatile char irq2dev[17] = {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}; + + +/* supported audio formats */ +static int ad_format_mask = +AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW; + +/* array of device info structures */ +static ad1816_info dev_info[MAX_AUDIO_DEV]; + + +/* ------------------------------------------------------------------- */ + +/* functions for easier access to inderect registers */ + +static int ad_read (ad1816_info * devc, int reg) +{ + unsigned long flags; + int result; + + CHECK_FOR_POWER; + + save_flags (flags); /* make register access atomic */ + cli (); + outb ((unsigned char) (reg & 0x3f), devc->base+0); + result = inb(devc->base+2); + result+= inb(devc->base+3)<<8; + restore_flags (flags); + + return (result); +} + + +static void ad_write (ad1816_info * devc, int reg, int data) +{ + unsigned long flags; + + CHECK_FOR_POWER; + + save_flags (flags); /* make register access atomic */ + cli (); + outb ((unsigned char) (reg & 0xff), devc->base+0); + outb ((unsigned char) (data & 0xff),devc->base+2); + outb ((unsigned char) ((data>>8)&0xff),devc->base+3); + restore_flags (flags); + +} + +/* ------------------------------------------------------------------- */ + +/* function interface required by struct audio_driver */ + +static void ad1816_halt_input (int dev) +{ + unsigned long flags; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + unsigned char buffer; + + DEBUGINFO (printk("ad1816: halt_input called\n")); + + save_flags (flags); + cli (); + + if(!isa_dma_bridge_buggy) { + disable_dma(audio_devs[dev]->dmap_in->dma); + } + + buffer=inb(devc->base+9); + if (buffer & 0x01) { + /* disable capture */ + outb(buffer & ~0x01,devc->base+9); + } + + if(!isa_dma_bridge_buggy) { + enable_dma(audio_devs[dev]->dmap_in->dma); + } + + /* Clear interrupt status */ + outb (~0x40, devc->base+1); + + devc->audio_mode &= ~PCM_ENABLE_INPUT; + restore_flags (flags); +} + +static void ad1816_halt_output (int dev) +{ + unsigned long flags; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + unsigned char buffer; + + DEBUGINFO (printk("ad1816: halt_output called!\n")); + + save_flags (flags); + cli (); + /* Mute pcm output */ + ad_write(devc, 4, ad_read(devc,4)|0x8080); + + if(!isa_dma_bridge_buggy) { + disable_dma(audio_devs[dev]->dmap_out->dma); + } + + buffer=inb(devc->base+8); + if (buffer & 0x01) { + /* disable capture */ + outb(buffer & ~0x01,devc->base+8); + } + + if(!isa_dma_bridge_buggy) { + enable_dma(audio_devs[dev]->dmap_out->dma); + } + + /* Clear interrupt status */ + outb ((unsigned char)~0x80, devc->base+1); + + devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + restore_flags (flags); +} + +static void ad1816_output_block (int dev, unsigned long buf, + int count, int intrflag) +{ + unsigned long flags; + unsigned long cnt; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + DEBUGINFO(printk("ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); + + cnt = count/4 - 1; + + save_flags (flags); + cli (); + + /* set transfer count */ + ad_write (devc, 8, cnt & 0xffff); + + devc->audio_mode |= PCM_ENABLE_OUTPUT; + restore_flags (flags); +} + + +static void ad1816_start_input (int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags; + unsigned long cnt; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + DEBUGINFO(printk("ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); + + cnt = count/4 - 1; + + save_flags (flags); /* make register access atomic */ + cli (); + + /* set transfer count */ + ad_write (devc, 10, cnt & 0xffff); + + devc->audio_mode |= PCM_ENABLE_INPUT; + restore_flags (flags); +} + +static int ad1816_prepare_for_input (int dev, int bsize, int bcount) +{ + unsigned long flags; + unsigned int freq; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + unsigned char fmt_bits; + + DEBUGINFO (printk ("ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount)); + + save_flags (flags); + cli (); + + fmt_bits= (devc->format_bits&0x7)<<3; + + /* set mono/stereo mode */ + if (devc->channels > 1) { + fmt_bits |=0x4; + } + + /* set Mono/Stereo in playback/capture register */ + outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); + outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); + + /* If compiled into kernel, AD1816_CLOCK is defined, so use it */ +#ifdef AD1816_CLOCK + ad1816_clockfreq=AD1816_CLOCK; +#endif + + /* capture/playback frequency correction for soundcards + with clock chips != 33MHz (allowed range 5 - 100 kHz) */ + + if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { + ad1816_clockfreq=33000; + } + + freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; + + /* write playback/capture speeds */ + ad_write (devc, 2, freq & 0xffff); + ad_write (devc, 3, freq & 0xffff); + + restore_flags (flags); + + ad1816_halt_input(dev); + return 0; +} + +static int ad1816_prepare_for_output (int dev, int bsize, int bcount) +{ + unsigned long flags; + unsigned int freq; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + unsigned char fmt_bits; + + DEBUGINFO (printk ("ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount)); + + save_flags (flags); /* make register access atomic */ + cli (); + + fmt_bits= (devc->format_bits&0x7)<<3; + /* set mono/stereo mode */ + if (devc->channels > 1) { + fmt_bits |=0x4; + } + + /* write format bits to playback/capture registers */ + outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); + outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); + +#ifdef AD1816_CLOCK + ad1816_clockfreq=AD1816_CLOCK; +#endif + + /* capture/playback frequency correction for soundcards + with clock chips != 33MHz (allowed range 5 - 100 kHz)*/ + + if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { + ad1816_clockfreq=33000; + } + + freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; + + /* write playback/capture speeds */ + ad_write (devc, 2, freq & 0xffff); + ad_write (devc, 3, freq & 0xffff); + + restore_flags (flags); + + ad1816_halt_output(dev); + return 0; + +} + +static void ad1816_trigger (int dev, int state) +{ + unsigned long flags; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + DEBUGINFO (printk("ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base)); + + /* mode may have changed */ + + save_flags (flags); /* make register access atomic */ + cli (); + + /* mask out modes not specified on open call */ + state &= devc->audio_mode; + + /* setup soundchip to new io-mode */ + if (state & PCM_ENABLE_INPUT) { + /* enable capture */ + outb(inb(devc->base+9)|0x01, devc->base+9); + } else { + /* disable capture */ + outb(inb(devc->base+9)&~0x01, devc->base+9); + } + + if (state & PCM_ENABLE_OUTPUT) { + /* enable playback */ + outb(inb(devc->base+8)|0x01, devc->base+8); + /* unmute pcm output */ + ad_write(devc, 4, ad_read(devc,4)&~0x8080); + } else { + /* mute pcm output */ + ad_write(devc, 4, ad_read(devc,4)|0x8080); + /* disable capture */ + outb(inb(devc->base+8)&~0x01, devc->base+8); + } + restore_flags (flags); +} + + +/* halt input & output */ +static void ad1816_halt (int dev) +{ + ad1816_halt_input(dev); + ad1816_halt_output(dev); +} + +static void ad1816_reset (int dev) +{ + ad1816_halt (dev); +} + +/* set playback speed */ +static int ad1816_set_speed (int dev, int arg) +{ + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + if (arg == 0) { + return devc->speed; + } + /* range checking */ + if (arg < 4000) { + arg = 4000; + } + if (arg > 55000) { + arg = 55000; + } + + devc->speed = arg; + return devc->speed; + +} + +static unsigned int ad1816_set_bits (int dev, unsigned int arg) +{ + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + static struct format_tbl { + int format; + unsigned char bits; + } format2bits[] = { + { 0, 0 }, + { AFMT_MU_LAW, 1 }, + { AFMT_A_LAW, 3 }, + { AFMT_IMA_ADPCM, 0 }, + { AFMT_U8, 0 }, + { AFMT_S16_LE, 2 }, + { AFMT_S16_BE, 6 }, + { AFMT_S8, 0 }, + { AFMT_U16_LE, 0 }, + { AFMT_U16_BE, 0 } + }; + + int i, n = sizeof (format2bits) / sizeof (struct format_tbl); + + /* return current format */ + if (arg == 0) + return devc->audio_format; + + devc->audio_format = arg; + + /* search matching format bits */ + for (i = 0; i < n; i++) + if (format2bits[i].format == arg) { + devc->format_bits = format2bits[i].bits; + devc->audio_format = arg; + return arg; + } + + /* Still hanging here. Something must be terribly wrong */ + devc->format_bits = 0; + return devc->audio_format = AFMT_U8; +} + +static short ad1816_set_channels (int dev, short arg) +{ + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + if (arg != 1 && arg != 2) + return devc->channels; + + devc->channels = arg; + return arg; +} + +/* open device */ +static int ad1816_open (int dev, int mode) +{ + ad1816_info *devc = NULL; + unsigned long flags; + + /* is device number valid ? */ + if (dev < 0 || dev >= num_audiodevs) + return -(ENXIO); + + /* get device info of this dev */ + devc = (ad1816_info *) audio_devs[dev]->devc; + + /* make check if device already open atomic */ + save_flags (flags); + cli (); + + if (devc->opened) { + restore_flags (flags); + return -(EBUSY); + } + + /* mark device as open */ + devc->opened = 1; + + devc->audio_mode = 0; + devc->speed = 8000; + devc->audio_format=AFMT_U8; + devc->channels=1; + + ad1816_reset(devc->dev_no); /* halt all pending output */ + restore_flags (flags); + return 0; +} + +static void ad1816_close (int dev) /* close device */ +{ + unsigned long flags; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + save_flags (flags); + cli (); + + /* halt all pending output */ + ad1816_reset(devc->dev_no); + + devc->opened = 0; + devc->audio_mode = 0; + devc->speed = 8000; + devc->audio_format=AFMT_U8; + devc->format_bits = 0; + + + restore_flags (flags); +} + + +/* ------------------------------------------------------------------- */ + +/* Audio driver structure */ + +static struct audio_driver ad1816_audio_driver = +{ + owner: THIS_MODULE, + open: ad1816_open, + close: ad1816_close, + output_block: ad1816_output_block, + start_input: ad1816_start_input, + prepare_for_input: ad1816_prepare_for_input, + prepare_for_output: ad1816_prepare_for_output, + halt_io: ad1816_halt, + halt_input: ad1816_halt_input, + halt_output: ad1816_halt_output, + trigger: ad1816_trigger, + set_speed: ad1816_set_speed, + set_bits: ad1816_set_bits, + set_channels: ad1816_set_channels, +}; + + +/* ------------------------------------------------------------------- */ + +/* Interrupt handler */ + + +static void ad1816_interrupt (int irq, void *dev_id, struct pt_regs *dummy) +{ + unsigned char status; + ad1816_info *devc; + int dev; + unsigned long flags; + + + if (irq < 0 || irq > 15) { + printk(KERN_WARNING "ad1816: Got bogus interrupt %d\n", irq); + return; + } + + dev = irq2dev[irq]; + + if (dev < 0 || dev >= num_audiodevs) { + printk(KERN_WARNING "ad1816: IRQ2AD1816-mapping failed for " + "irq %d device %d\n", irq,dev); + return; + } + + devc = (ad1816_info *) audio_devs[dev]->devc; + + save_flags(flags); + cli(); + + /* read interrupt register */ + status = inb (devc->base+1); + /* Clear all interrupt */ + outb (~status, devc->base+1); + + DEBUGNOISE (printk("ad1816: Got interrupt subclass %d\n",status)); + + devc->irq_ok=1; + + if (status == 0) + DEBUGWARN(printk ("ad1816: interrupt: Got interrupt, but no reason?\n")); + + if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64)) + DMAbuf_inputintr (dev); + + if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128)) + DMAbuf_outputintr (dev, 1); + + restore_flags(flags); +} + +/* ------------------------------------------------------------------- */ + +/* Mixer stuff */ + +struct mixer_def { + unsigned int regno: 7; + unsigned int polarity:1; /* 0=normal, 1=reversed */ + unsigned int bitpos:4; + unsigned int nbits:4; +}; + +static char mix_cvt[101] = { + 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, + 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, + 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, + 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, + 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, + 100 +}; + +typedef struct mixer_def mixer_ent; + +/* + * Most of the mixer entries work in backwards. Setting the polarity field + * makes them to work correctly. + * + * The channel numbering used by individual soundcards is not fixed. Some + * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs. + * The current version doesn't try to compensate this. + */ + +#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r) \ + {{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}} + + +mixer_ent mix_devices[SOUND_MIXER_NRDEVICES][2] = { +MIX_ENT(SOUND_MIXER_VOLUME, 14, 1, 8, 5, 14, 1, 0, 5), +MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 5, 1, 8, 6, 5, 1, 0, 6), +MIX_ENT(SOUND_MIXER_PCM, 4, 1, 8, 6, 4, 1, 0, 6), +MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 18, 1, 8, 5, 18, 1, 0, 5), +MIX_ENT(SOUND_MIXER_MIC, 19, 1, 8, 5, 19, 1, 0, 5), +MIX_ENT(SOUND_MIXER_CD, 15, 1, 8, 5, 15, 1, 0, 5), +MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 20, 0, 8, 4, 20, 0, 0, 4), +MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 17, 1, 8, 5, 17, 1, 0, 5), +MIX_ENT(SOUND_MIXER_LINE2, 16, 1, 8, 5, 16, 1, 0, 5), +MIX_ENT(SOUND_MIXER_LINE3, 39, 0, 9, 4, 39, 1, 0, 5) +}; + + +static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] = +{ + 0x4343, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x0000, /* FM */ + 0x4343, /* PCM */ + 0x0000, /* PC Speaker */ + 0x0000, /* Ext Line */ + 0x0000, /* Mic */ + 0x0000, /* CD */ + 0x0000, /* Recording monitor */ + 0x0000, /* SB PCM */ + 0x0000, /* Recording level */ + 0x0000, /* Input gain */ + 0x0000, /* Output gain */ + 0x0000, /* Line1 */ + 0x0000, /* Line2 */ + 0x0000 /* Line3 (usually line in)*/ +}; + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + + + +static int +ad1816_set_recmask (ad1816_info * devc, int mask) +{ + unsigned char recdev; + int i, n; + + mask &= devc->supported_rec_devices; + + n = 0; + /* Count selected device bits */ + for (i = 0; i < 32; i++) + if (mask & (1 << i)) + n++; + + if (n == 0) + mask = SOUND_MASK_MIC; + else if (n != 1) { /* Too many devices selected */ + /* Filter out active settings */ + mask &= ~devc->recmask; + + n = 0; + /* Count selected device bits */ + for (i = 0; i < 32; i++) + if (mask & (1 << i)) + n++; + + if (n != 1) + mask = SOUND_MASK_MIC; + } + + switch (mask) { + case SOUND_MASK_MIC: + recdev = 5; + break; + + case SOUND_MASK_LINE: + recdev = 0; + break; + + case SOUND_MASK_CD: + recdev = 2; + break; + + case SOUND_MASK_LINE1: + recdev = 4; + break; + + case SOUND_MASK_LINE2: + recdev = 3; + break; + + case SOUND_MASK_VOLUME: + recdev = 1; + break; + + default: + mask = SOUND_MASK_MIC; + recdev = 5; + } + + recdev <<= 4; + ad_write (devc, 20, + (ad_read (devc, 20) & 0x8f8f) | recdev | (recdev<<8)); + + devc->recmask = mask; + return mask; +} + +static void +change_bits (int *regval, int dev, int chn, int newval) +{ + unsigned char mask; + int shift; + + /* Reverse polarity*/ + + if (mix_devices[dev][chn].polarity == 1) + newval = 100 - newval; + + mask = (1 << mix_devices[dev][chn].nbits) - 1; + shift = mix_devices[dev][chn].bitpos; + /* Scale it */ + newval = (int) ((newval * mask) + 50) / 100; + /* Clear bits */ + *regval &= ~(mask << shift); + /* Set new value */ + *regval |= (newval & mask) << shift; +} + +static int +ad1816_mixer_get (ad1816_info * devc, int dev) +{ + DEBUGINFO(printk("ad1816: mixer_get called!\n")); + + /* range check + supported mixer check */ + if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) + return (-(EINVAL)); + if (!((1 << dev) & devc->supported_devices)) + return -(EINVAL); + + return devc->levels[dev]; +} + +static int +ad1816_mixer_set (ad1816_info * devc, int dev, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int retvol; + + int regoffs; + int val; + int valmute; + + DEBUGINFO(printk("ad1816: mixer_set called!\n")); + + if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) + return -(EINVAL); + + if (left > 100) + left = 100; + if (left < 0) + left = 0; + if (right > 100) + right = 100; + if (right < 0) + right = 0; + + /* Mono control */ + if (mix_devices[dev][RIGHT_CHN].nbits == 0) + right = left; + retvol = left | (right << 8); + + /* Scale it */ + + left = mix_cvt[left]; + right = mix_cvt[right]; + + /* reject all mixers that are not supported */ + if (!(devc->supported_devices & (1 << dev))) + return -(EINVAL); + + /* sanity check */ + if (mix_devices[dev][LEFT_CHN].nbits == 0) + return -(EINVAL); + + /* keep precise volume internal */ + devc->levels[dev] = retvol; + + /* Set the left channel */ + regoffs = mix_devices[dev][LEFT_CHN].regno; + val = ad_read (devc, regoffs); + change_bits (&val, dev, LEFT_CHN, left); + + valmute=val; + + /* Mute bit masking on some registers */ + if ( regoffs==5 || regoffs==14 || regoffs==15 || + regoffs==16 || regoffs==17 || regoffs==18 || + regoffs==19 || regoffs==39) { + if (left==0) + valmute |= 0x8000; + else + valmute &= ~0x8000; + } + ad_write (devc, regoffs, valmute); /* mute */ + + /* + * Set the right channel + */ + + /* Was just a mono channel */ + if (mix_devices[dev][RIGHT_CHN].nbits == 0) + return retvol; + + regoffs = mix_devices[dev][RIGHT_CHN].regno; + val = ad_read (devc, regoffs); + change_bits (&val, dev, RIGHT_CHN, right); + + valmute=val; + if ( regoffs==5 || regoffs==14 || regoffs==15 || + regoffs==16 || regoffs==17 || regoffs==18 || + regoffs==19 || regoffs==39) { + if (right==0) + valmute |= 0x80; + else + valmute &= ~0x80; + } + ad_write (devc, regoffs, valmute); /* mute */ + + return retvol; +} + +#define MIXER_DEVICES ( SOUND_MASK_VOLUME | \ + SOUND_MASK_SYNTH | \ + SOUND_MASK_PCM | \ + SOUND_MASK_LINE | \ + SOUND_MASK_LINE1 | \ + SOUND_MASK_LINE2 | \ + SOUND_MASK_LINE3 | \ + SOUND_MASK_MIC | \ + SOUND_MASK_CD | \ + SOUND_MASK_RECLEV \ + ) +#define REC_DEVICES ( SOUND_MASK_LINE2 |\ + SOUND_MASK_LINE |\ + SOUND_MASK_LINE1 |\ + SOUND_MASK_MIC |\ + SOUND_MASK_CD |\ + SOUND_MASK_VOLUME \ + ) + +static void +ad1816_mixer_reset (ad1816_info * devc) +{ + int i; + + devc->supported_devices = MIXER_DEVICES; + + devc->supported_rec_devices = REC_DEVICES; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (devc->supported_devices & (1 << i)) + ad1816_mixer_set (devc, i, default_mixer_levels[i]); + ad1816_set_recmask (devc, SOUND_MASK_MIC); +} + +static int +ad1816_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + ad1816_info *devc = mixer_devs[dev]->devc; + int val; + + DEBUGINFO(printk("ad1816: mixer_ioctl called!\n")); + + /* Mixer ioctl */ + if (((cmd >> 8) & 0xff) == 'M') { + + /* set ioctl */ + if (_SIOC_DIR (cmd) & _SIOC_WRITE) { + switch (cmd & 0xff){ + case SOUND_MIXER_RECSRC: + + if (get_user(val, (int *)arg)) + return -EFAULT; + val=ad1816_set_recmask (devc, val); + return put_user(val, (int *)arg); + break; + + default: + if (get_user(val, (int *)arg)) + return -EFAULT; + if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0) + return val; + else + return put_user(val, (int *)arg); + } + } else { + /* read ioctl */ + switch (cmd & 0xff) { + + case SOUND_MIXER_RECSRC: + val=devc->recmask; + return put_user(val, (int *)arg); + break; + + case SOUND_MIXER_DEVMASK: + val=devc->supported_devices; + return put_user(val, (int *)arg); + break; + + case SOUND_MIXER_STEREODEVS: + val=devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); + return put_user(val, (int *)arg); + break; + + case SOUND_MIXER_RECMASK: + val=devc->supported_rec_devices; + return put_user(val, (int *)arg); + break; + + case SOUND_MIXER_CAPS: + val=SOUND_CAP_EXCL_INPUT; + return put_user(val, (int *)arg); + break; + + default: + if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0) + return val; + else + return put_user(val, (int *)arg); + } + } + } else + /* not for mixer */ + return -(EINVAL); +} + +/* ------------------------------------------------------------------- */ + +/* Mixer structure */ + +static struct mixer_operations ad1816_mixer_operations = { + owner: THIS_MODULE, + id: "AD1816", + name: "AD1816 Mixer", + ioctl: ad1816_mixer_ioctl +}; + + +/* ------------------------------------------------------------------- */ + +/* stuff for card recognition, init and unloading */ + + +/* replace with probe routine */ +static int __init probe_ad1816 ( struct address_info *hw_config ) +{ + ad1816_info *devc = &dev_info[nr_ad1816_devs]; + int io_base=hw_config->io_base; + int *osp=hw_config->osp; + int tmp; + + printk(KERN_INFO "ad1816: AD1816 sounddriver " + "Copyright (C) 1998 by Thorsten Knabe\n"); + printk(KERN_INFO "ad1816: io=0x%x, irq=%d, dma=%d, dma2=%d, " + "clockfreq=%d, options=%d isadmabug=%d\n", + hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma2, + ad1816_clockfreq, + options, + isa_dma_bridge_buggy); + + if (!request_region(io_base, 16, "AD1816 Sound")) { + printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n", + io_base); + goto err; + } + + DEBUGLOG(printk ("ad1816: detect(%x)\n", io_base)); + + if (nr_ad1816_devs >= MAX_AUDIO_DEV) { + printk(KERN_WARNING "ad1816: detect error - step 0\n"); + goto out_release_region; + } + + devc->base = io_base; + devc->irq_ok = 0; + devc->irq = 0; + devc->opened = 0; + devc->osp = osp; + + /* base+0: bit 1 must be set but not 255 */ + tmp=inb(devc->base); + if ( (tmp&0x80)==0 || tmp==255 ) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 or chip is not active (Test 0)\n")); + goto out_release_region; + } + + + /* writes to ireg 8 are copied to ireg 9 */ + ad_write(devc,8,12345); + if (ad_read(devc,9)!=12345) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 1)\n")); + goto out_release_region; + } + + /* writes to ireg 8 are copied to ireg 9 */ + ad_write(devc,8,54321); + if (ad_read(devc,9)!=54321) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 2)\n")); + goto out_release_region; + } + + + /* writes to ireg 10 are copied to ireg 11 */ + ad_write(devc,10,54321); + if (ad_read(devc,11)!=54321) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 3)\n")); + goto out_release_region; + } + + /* writes to ireg 10 are copied to ireg 11 */ + ad_write(devc,10,12345); + if (ad_read(devc,11)!=12345) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 4)\n")); + goto out_release_region; + } + + /* bit in base +1 cannot be set to 1 */ + tmp=inb(devc->base+1); + outb(0xff,devc->base+1); + if (inb(devc->base+1)!=tmp) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 5)\n")); + goto out_release_region; + } + + + DEBUGLOG (printk ("ad1816: detect() - Detected OK\n")); + DEBUGLOG (printk ("ad1816: AD1816 Version: %d\n",ad_read(devc,45))); + + /* detection was successful */ + return 1; +out_release_region: + release_region(io_base, 16); + /* detection was NOT successful */ +err: return 0; +} + + +/* allocate resources from the kernel. If any allocation fails, free + all allocated resources and exit attach. + + */ + +static void __init attach_ad1816 (struct address_info *hw_config) +{ + int my_dev; + char dev_name[100]; + ad1816_info *devc = &dev_info[nr_ad1816_devs]; + + devc->base = hw_config->io_base; + + /* disable all interrupts */ + ad_write(devc,1,0); + + /* Clear pending interrupts */ + outb (0, devc->base+1); + + /* allocate irq */ + if (hw_config->irq < 0 || hw_config->irq > 15) + goto out_release_region; + if (request_irq(hw_config->irq, ad1816_interrupt,0, + "SoundPort", hw_config->osp) < 0) { + printk(KERN_WARNING "ad1816: IRQ in use\n"); + goto out_release_region; + } + devc->irq=hw_config->irq; + + /* DMA stuff */ + if (sound_alloc_dma (hw_config->dma, "Sound System")) { + printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", + hw_config->dma); + goto out_free_irq; + } + devc->dma_playback=hw_config->dma; + + if ( hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) { + if (sound_alloc_dma(hw_config->dma2, + "Sound System (capture)")) { + printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", + hw_config->dma2); + goto out_free_dma; + } + devc->dma_capture=hw_config->dma2; + devc->audio_mode=DMA_AUTOMODE|DMA_DUPLEX; + } else { + devc->dma_capture=-1; + devc->audio_mode=DMA_AUTOMODE; + } + + sprintf (dev_name,"AD1816 audio driver"); + + conf_printf2 (dev_name, + devc->base, devc->irq, hw_config->dma, hw_config->dma2); + + /* register device */ + if ((my_dev = sound_install_audiodrv (AUDIO_DRIVER_VERSION, + dev_name, + &ad1816_audio_driver, + sizeof (struct audio_driver), + devc->audio_mode, + ad_format_mask, + devc, + hw_config->dma, + hw_config->dma2)) < 0) { + printk(KERN_WARNING "ad1816: Can't install sound driver\n"); + goto out_free_dma_2; + } + + /* fill rest of structure with reasonable default values */ + irq2dev[hw_config->irq] = devc->dev_no = my_dev; + devc->opened = 0; + devc->irq_ok = 0; + devc->osp = hw_config->osp; + nr_ad1816_devs++; + + ad_write(devc,32,0x80f0); /* sound system mode */ + if (options&1) { + ad_write(devc,33,0); /* disable all audiosources for dsp */ + } else { + ad_write(devc,33,0x03f8); /* enable all audiosources for dsp */ + } + ad_write(devc,4,0x8080); /* default values for volumes (muted)*/ + ad_write(devc,5,0x8080); + ad_write(devc,6,0x8080); + ad_write(devc,7,0x8080); + ad_write(devc,15,0x8888); + ad_write(devc,16,0x8888); + ad_write(devc,17,0x8888); + ad_write(devc,18,0x8888); + ad_write(devc,19,0xc888); /* +20db mic active */ + ad_write(devc,14,0x0000); /* Master volume unmuted */ + ad_write(devc,39,0x009f); /* 3D effect on 0% phone out muted */ + ad_write(devc,44,0x0080); /* everything on power, 3d enabled for d/a */ + outb(0x10,devc->base+8); /* set dma mode */ + outb(0x10,devc->base+9); + + /* enable capture + playback interrupt */ + ad_write(devc,1,0xc000); + + /* set mixer defaults */ + ad1816_mixer_reset (devc); + + /* register mixer */ + if ((audio_devs[my_dev]->mixer_dev=sound_install_mixer( + MIXER_DRIVER_VERSION, + dev_name, + &ad1816_mixer_operations, + sizeof (struct mixer_operations), + devc)) >= 0) { + audio_devs[my_dev]->min_fragment = 0; + } +out: return; +out_free_dma_2: + if (devc->dma_capture >= 0) + sound_free_dma(hw_config->dma2); +out_free_dma: + sound_free_dma(hw_config->dma); +out_free_irq: + free_irq(hw_config->irq,hw_config->osp); +out_release_region: + release_region(hw_config->io_base, 16); + goto out; +} + +static void __exit unload_card(ad1816_info *devc) +{ + int mixer, dev = 0; + + if (devc != NULL) { + DEBUGLOG (printk("ad1816: Unloading card at base=%x\n",devc->base)); + + dev = devc->dev_no; + mixer = audio_devs[dev]->mixer_dev; + + /* unreg mixer*/ + if(mixer>=0) { + sound_unload_mixerdev(mixer); + } + sound_unload_audiodev(dev); + + /* free dma channels */ + if (devc->dma_capture>=0) { + sound_free_dma(devc->dma_capture); + } + + /* card wont get added if resources could not be allocated + thus we need not ckeck if allocation was successful */ + sound_free_dma (devc->dma_playback); + free_irq(devc->irq, devc->osp); + release_region (devc->base, 16); + + DEBUGLOG (printk("ad1816: Unloading card at base=%x was successful\n",devc->base)); + + } else + printk(KERN_WARNING "ad1816: no device/card specified\n"); +} + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; + +#ifdef __ISAPNP__ +struct pci_dev *ad1816_dev = NULL; + +static int activated = 1; + +static int isapnp = 1; +static int isapnpjump = 0; + +MODULE_PARM(isapnp, "i"); +MODULE_PARM(isapnpjump, "i"); + +#else +static int isapnp = 0; +#endif + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(ad1816_clockfreq,"i"); +MODULE_PARM(options,"i"); + +#ifdef __ISAPNP__ + +static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) +{ + int err; + + if(dev->active) { + activated = 0; + return(dev); + } + + if((err = dev->activate(dev)) < 0) { + printk(KERN_ERR "ad1816: %s %s config failed (out of resources?)[%d]\n", + devname, resname, err); + dev->deactivate(dev); + return(NULL); + } + + return(dev); +} + +static struct pci_dev *ad1816_init_generic(struct pci_bus *bus, struct pci_dev *card, + struct address_info *hw_config) +{ + if((ad1816_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) { + ad1816_dev->prepare(ad1816_dev); + + if((ad1816_dev = activate_dev("Analog Devices 1816(A)", "ad1816", ad1816_dev))) { + hw_config->io_base = ad1816_dev->resource[2].start; + hw_config->irq = ad1816_dev->irq_resource[0].start; + hw_config->dma = ad1816_dev->dma_resource[0].start; + hw_config->dma2 = ad1816_dev->dma_resource[1].start; + } + } + + return(ad1816_dev); +} + +static struct ad1816_data { + struct pci_dev * (*initfunc)(struct pci_bus*, struct pci_dev *, struct address_info *); + char *name; +} ad1816_pnp_data[] __initdata = { + { &ad1816_init_generic, "Analog Devices 1815" }, + { &ad1816_init_generic, "Analog Devices 1816A" } +}; + +static struct { + unsigned short card_vendor, card_device; + unsigned short vendor; + unsigned short function; + struct ad1816_data *data; +} isapnp_ad1816_list[] __initdata = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150), + &ad1816_pnp_data[0] }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180), + &ad1816_pnp_data[1] }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, isapnp_ad1816_list); + +static int __init ad1816_init_isapnp(struct address_info *hw_config, + struct pci_bus *bus, struct pci_dev *card, int slot) +{ + struct pci_dev *idev = NULL; + + /* You missed the init func? That's bad. */ + if(isapnp_ad1816_list[slot].data->initfunc) { + char *busname = bus->name[0] ? bus->name : isapnp_ad1816_list[slot].data->name; + + printk(KERN_INFO "ad1816: %s detected\n", busname); + + /* Initialize this baby. */ + if((idev = isapnp_ad1816_list[slot].data->initfunc(bus, card, hw_config))) { + /* We got it. */ + + printk(KERN_NOTICE "ad1816: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", + busname, + hw_config->io_base, hw_config->irq, hw_config->dma, + hw_config->dma2); + return 1; + } else + printk(KERN_INFO "ad1816: Failed to initialize %s\n", busname); + } else + printk(KERN_ERR "ad1816: Bad entry in ad1816.c PnP table\n"); + + return 0; +} + +/* + * Actually this routine will detect and configure only the first card with successful + * initialization. isapnpjump could be used to jump to a specific entry. + * Please always add entries at the end of the array. + * Should this be fixed? - azummo + */ + +int __init ad1816_probe_isapnp(struct address_info *hw_config) +{ + int i; + + /* Count entries in isapnp_ad1816_list */ + for (i = 0; isapnp_ad1816_list[i].vendor != 0; i++) + ; + /* Check and adjust isapnpjump */ + if( isapnpjump < 0 || isapnpjump > ( i - 1 ) ) { + printk(KERN_ERR "ad1816: Valid range for isapnpjump is 0-%d. Adjusted to 0.\n", i-1); + isapnpjump = 0; + } + + for (i = isapnpjump; isapnp_ad1816_list[i].vendor != 0; i++) { + struct pci_dev *card = NULL; + + while ((card = isapnp_find_dev(NULL, isapnp_ad1816_list[i].vendor, + isapnp_ad1816_list[i].function, card))) + if(ad1816_init_isapnp(hw_config, card->bus, card, i)) + return 0; + } + + return -ENODEV; +} +#endif + +static int __init init_ad1816(void) +{ + +#ifdef __ISAPNP__ + if(isapnp && (ad1816_probe_isapnp(&cfg) < 0) ) { + printk(KERN_NOTICE "ad1816: No ISAPnP cards found, trying standard ones...\n"); + isapnp = 0; + } +#endif + + if( isapnp == 0) { + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + } + + if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.dma2 == -1) { + printk(KERN_INFO "ad1816: dma, dma2, irq and io must be set.\n"); + return -EINVAL; + } + + if (probe_ad1816(&cfg) == 0) { + return -ENODEV; + } + + attach_ad1816(&cfg); + + return 0; +} + +static void __exit cleanup_ad1816 (void) +{ + int i; + ad1816_info *devc = NULL; + + /* remove any soundcard */ + for (i = 0; i < nr_ad1816_devs; i++) { + devc = &dev_info[i]; + unload_card(devc); + } + nr_ad1816_devs=0; + +#ifdef __ISAPNP__ + if(activated) + if(ad1816_dev) + ad1816_dev->deactivate(ad1816_dev); +#endif +} + +module_init(init_ad1816); +module_exit(cleanup_ad1816); + +#ifndef MODULE +static int __init setup_ad1816(char *str) +{ + /* io, irq, dma, dma2 */ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + + return 1; +} + +__setup("ad1816=", setup_ad1816); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/ad1848.c b/sound/oss/ad1848.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ad1848.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,3177 @@ +/* + * sound/ad1848.c + * + * The low level driver for the AD1848/CS4248 codec chip which + * is used for example in the MS Sound System. + * + * The CS4231 which is used in the GUS MAX and some other cards is + * upwards compatible with AD1848 and this driver is able to drive it. + * + * CS4231A and AD1845 are upward compatible with CS4231. However + * the new features of these chips are different. + * + * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU). + * CS4232A is an improved version of CS4232. + * + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * general sleep/wakeup clean up. + * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free + * of irqs. Use dev_id. + * Christoph Hellwig : adapted to module_init/module_exit + * Aki Laukkanen : added power management support + * Arnaldo C. de Melo : added missing restore_flags in ad1848_resume + * Miguel Freitas : added ISA PnP support + * Alan Cox : Added CS4236->4239 identification + * Daniel T. Cobra : Alernate config/mixer for later chips + * Alan Cox : Merged chip idents and config code + * + * TODO + * APM save restore assist code on IBM thinkpad + * + * Status: + * Tested. Believed fully functional. + */ + +#include +#include +#include +#include +#include +#include + +#define DEB(x) +#define DEB1(x) +#include "sound_config.h" + +#include "ad1848.h" +#include "ad1848_mixer.h" + +typedef struct +{ + int base; + int irq; + int dma1, dma2; + int dual_dma; /* 1, when two DMA channels allocated */ + int subtype; + unsigned char MCE_bit; + unsigned char saved_regs[64]; /* Includes extended register space */ + int debug_flag; + + int audio_flags; + int record_dev, playback_dev; + + int xfer_count; + int audio_mode; + int open_mode; + int intr_active; + char *chip_name, *name; + int model; +#define MD_1848 1 +#define MD_4231 2 +#define MD_4231A 3 +#define MD_1845 4 +#define MD_4232 5 +#define MD_C930 6 +#define MD_IWAVE 7 +#define MD_4235 8 /* Crystal Audio CS4235 */ +#define MD_1845_SSCAPE 9 /* Ensoniq Soundscape PNP*/ +#define MD_4236 10 /* 4236 and higher */ +#define MD_42xB 11 /* CS 42xB */ +#define MD_4239 12 /* CS4239 */ + + /* Mixer parameters */ + int recmask; + int supported_devices, orig_devices; + int supported_rec_devices, orig_rec_devices; + int *levels; + short mixer_reroute[32]; + int dev_no; + volatile unsigned long timer_ticks; + int timer_running; + int irq_ok; + mixer_ents *mix_devices; + int mixer_output_port; + + /* Power management */ + struct pm_dev *pmdev; +} ad1848_info; + +typedef struct ad1848_port_info +{ + int open_mode; + int speed; + unsigned char speed_bits; + int channels; + int audio_format; + unsigned char format_bits; +} +ad1848_port_info; + +static struct address_info cfg; +static int nr_ad1848_devs; + +int deskpro_xl; +int deskpro_m; +int soundpro; + +static volatile signed char irq2dev[17] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +#ifndef EXCLUDE_TIMERS +static int timer_installed = -1; +#endif + +static int loaded; + +static int ad_format_mask[13 /*devc->model */ ] = +{ + 0, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */ + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE /* CS4235 */, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW /* Ensoniq Soundscape*/, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM +}; + +static ad1848_info adev_info[MAX_AUDIO_DEV]; + +#define io_Index_Addr(d) ((d)->base) +#define io_Indexed_Data(d) ((d)->base+1) +#define io_Status(d) ((d)->base+2) +#define io_Polled_IO(d) ((d)->base+3) + +static struct { + unsigned char flags; +#define CAP_F_TIMER 0x01 +} capabilities [10 /*devc->model */ ] = { + {0} + ,{0} /* MD_1848 */ + ,{CAP_F_TIMER} /* MD_4231 */ + ,{CAP_F_TIMER} /* MD_4231A */ + ,{CAP_F_TIMER} /* MD_1845 */ + ,{CAP_F_TIMER} /* MD_4232 */ + ,{0} /* MD_C930 */ + ,{CAP_F_TIMER} /* MD_IWAVE */ + ,{0} /* MD_4235 */ + ,{CAP_F_TIMER} /* MD_1845_SSCAPE */ +}; + +#ifdef __ISAPNP__ +static int isapnp = 1; +static int isapnpjump = 0; +static int reverse = 0; + +static int audio_activated = 0; +#else +static int isapnp = 0; +#endif + + + +static int ad1848_open(int dev, int mode); +static void ad1848_close(int dev); +static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag); +static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag); +static int ad1848_prepare_for_output(int dev, int bsize, int bcount); +static int ad1848_prepare_for_input(int dev, int bsize, int bcount); +static void ad1848_halt(int dev); +static void ad1848_halt_input(int dev); +static void ad1848_halt_output(int dev); +static void ad1848_trigger(int dev, int bits); +static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data); + +#ifndef EXCLUDE_TIMERS +static int ad1848_tmr_install(int dev); +static void ad1848_tmr_reprogram(int dev); +#endif + +static int ad_read(ad1848_info * devc, int reg) +{ + unsigned long flags; + int x; + int timeout = 900000; + + while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ + timeout--; + + save_flags(flags); + cli(); + + if(reg < 32) + { + outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); + x = inb(io_Indexed_Data(devc)); + } + else + { + int xreg, xra; + + xreg = (reg & 0xff) - 32; + xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2); + outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); + outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc)); + x = inb(io_Indexed_Data(devc)); + } + restore_flags(flags); + + return x; +} + +static void ad_write(ad1848_info * devc, int reg, int data) +{ + unsigned long flags; + int timeout = 900000; + + while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */ + timeout--; + + save_flags(flags); + cli(); + + if(reg < 32) + { + outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); + outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc)); + } + else + { + int xreg, xra; + + xreg = (reg & 0xff) - 32; + xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2); + outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); + outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc)); + outb((unsigned char) (data & 0xff), io_Indexed_Data(devc)); + } + restore_flags(flags); +} + +static void wait_for_calibration(ad1848_info * devc) +{ + int timeout = 0; + + /* + * Wait until the auto calibration process has finished. + * + * 1) Wait until the chip becomes ready (reads don't return 0x80). + * 2) Wait until the ACI bit of I11 gets on and then off. + */ + + timeout = 100000; + while (timeout > 0 && inb(devc->base) == 0x80) + timeout--; + if (inb(devc->base) & 0x80) + printk(KERN_WARNING "ad1848: Auto calibration timed out(1).\n"); + + timeout = 100; + while (timeout > 0 && !(ad_read(devc, 11) & 0x20)) + timeout--; + if (!(ad_read(devc, 11) & 0x20)) + return; + + timeout = 80000; + while (timeout > 0 && (ad_read(devc, 11) & 0x20)) + timeout--; + if (ad_read(devc, 11) & 0x20) + if ( (devc->model != MD_1845) || (devc->model != MD_1845_SSCAPE)) + printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n"); +} + +static void ad_mute(ad1848_info * devc) +{ + int i; + unsigned char prev; + + /* + * Save old register settings and mute output channels + */ + + for (i = 6; i < 8; i++) + { + prev = devc->saved_regs[i] = ad_read(devc, i); + } + +} + +static void ad_unmute(ad1848_info * devc) +{ +} + +static void ad_enter_MCE(ad1848_info * devc) +{ + unsigned long flags; + int timeout = 1000; + unsigned short prev; + + while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ + timeout--; + + save_flags(flags); + cli(); + + devc->MCE_bit = 0x40; + prev = inb(io_Index_Addr(devc)); + if (prev & 0x40) + { + restore_flags(flags); + return; + } + outb((devc->MCE_bit), io_Index_Addr(devc)); + restore_flags(flags); +} + +static void ad_leave_MCE(ad1848_info * devc) +{ + unsigned long flags; + unsigned char prev, acal; + int timeout = 1000; + + while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ + timeout--; + + save_flags(flags); + cli(); + + acal = ad_read(devc, 9); + + devc->MCE_bit = 0x00; + prev = inb(io_Index_Addr(devc)); + outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ + + if ((prev & 0x40) == 0) /* Not in MCE mode */ + { + restore_flags(flags); + return; + } + outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ + if (acal & 0x08) /* Auto calibration is enabled */ + wait_for_calibration(devc); + restore_flags(flags); +} + +static int ad1848_set_recmask(ad1848_info * devc, int mask) +{ + unsigned char recdev; + int i, n; + + mask &= devc->supported_rec_devices; + + /* Rename the mixer bits if necessary */ + for (i = 0; i < 32; i++) + { + if (devc->mixer_reroute[i] != i) + { + if (mask & (1 << i)) + { + mask &= ~(1 << i); + mask |= (1 << devc->mixer_reroute[i]); + } + } + } + + n = 0; + for (i = 0; i < 32; i++) /* Count selected device bits */ + if (mask & (1 << i)) + n++; + + if (!soundpro) { + if (n == 0) + mask = SOUND_MASK_MIC; + else if (n != 1) { /* Too many devices selected */ + mask &= ~devc->recmask; /* Filter out active settings */ + + n = 0; + for (i = 0; i < 32; i++) /* Count selected device bits */ + if (mask & (1 << i)) + n++; + + if (n != 1) + mask = SOUND_MASK_MIC; + } + switch (mask) { + case SOUND_MASK_MIC: + recdev = 2; + break; + + case SOUND_MASK_LINE: + case SOUND_MASK_LINE3: + recdev = 0; + break; + + case SOUND_MASK_CD: + case SOUND_MASK_LINE1: + recdev = 1; + break; + + case SOUND_MASK_IMIX: + recdev = 3; + break; + + default: + mask = SOUND_MASK_MIC; + recdev = 2; + } + + recdev <<= 6; + ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev); + ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev); + } else { /* soundpro */ + unsigned char val; + int set_rec_bit; + int j; + + for (i = 0; i < 32; i++) { /* For each bit */ + if ((devc->supported_rec_devices & (1 << i)) == 0) + continue; /* Device not supported */ + + for (j = LEFT_CHN; j <= RIGHT_CHN; j++) { + if (devc->mix_devices[i][j].nbits == 0) /* Inexistent channel */ + continue; + + /* + * This is tricky: + * set_rec_bit becomes 1 if the corresponding bit in mask is set + * then it gets flipped if the polarity is inverse + */ + set_rec_bit = ((mask & (1 << i)) != 0) ^ devc->mix_devices[i][j].recpol; + + val = ad_read(devc, devc->mix_devices[i][j].recreg); + val &= ~(1 << devc->mix_devices[i][j].recpos); + val |= (set_rec_bit << devc->mix_devices[i][j].recpos); + ad_write(devc, devc->mix_devices[i][j].recreg, val); + } + } + } + + /* Rename the mixer bits back if necessary */ + for (i = 0; i < 32; i++) + { + if (devc->mixer_reroute[i] != i) + { + if (mask & (1 << devc->mixer_reroute[i])) + { + mask &= ~(1 << devc->mixer_reroute[i]); + mask |= (1 << i); + } + } + } + devc->recmask = mask; + return mask; +} + +static void change_bits(ad1848_info * devc, unsigned char *regval, + unsigned char *muteval, int dev, int chn, int newval) +{ + unsigned char mask; + int shift; + int mute; + int mutemask; + int set_mute_bit; + + set_mute_bit = (newval == 0) ^ devc->mix_devices[dev][chn].mutepol; + + if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */ + newval = 100 - newval; + + mask = (1 << devc->mix_devices[dev][chn].nbits) - 1; + shift = devc->mix_devices[dev][chn].bitpos; + + if (devc->mix_devices[dev][chn].mutepos == 8) + { /* if there is no mute bit */ + mute = 0; /* No mute bit; do nothing special */ + mutemask = ~0; /* No mute bit; do nothing special */ + } + else + { + mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos); + mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos); + } + + newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ + *regval &= ~(mask << shift); /* Clear bits */ + *regval |= (newval & mask) << shift; /* Set new value */ + + *muteval &= mutemask; + *muteval |= mute; +} + +static int ad1848_mixer_get(ad1848_info * devc, int dev) +{ + if (!((1 << dev) & devc->supported_devices)) + return -EINVAL; + + dev = devc->mixer_reroute[dev]; + + return devc->levels[dev]; +} + +static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int channel) +{ + int regoffs, muteregoffs; + unsigned char val, muteval; + + regoffs = devc->mix_devices[dev][channel].regno; + muteregoffs = devc->mix_devices[dev][channel].mutereg; + val = ad_read(devc, regoffs); + + if (muteregoffs != regoffs) { + muteval = ad_read(devc, muteregoffs); + change_bits(devc, &val, &muteval, dev, channel, value); + } + else + change_bits(devc, &val, &val, dev, channel, value); + + ad_write(devc, regoffs, val); + devc->saved_regs[regoffs] = val; + if (muteregoffs != regoffs) { + ad_write(devc, muteregoffs, muteval); + devc->saved_regs[muteregoffs] = muteval; + } +} + +static int ad1848_mixer_set(ad1848_info * devc, int dev, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int retvol; + + if (dev > 31) + return -EINVAL; + + if (!(devc->supported_devices & (1 << dev))) + return -EINVAL; + + dev = devc->mixer_reroute[dev]; + + if (devc->mix_devices[dev][LEFT_CHN].nbits == 0) + return -EINVAL; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */ + right = left; + + retvol = left | (right << 8); + + /* Scale volumes */ + left = mix_cvt[left]; + right = mix_cvt[right]; + + devc->levels[dev] = retvol; + + /* + * Set the left channel + */ + ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN); + + /* + * Set the right channel + */ + if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) + goto out; + ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN); + + out: + return retvol; +} + +static void ad1848_mixer_reset(ad1848_info * devc) +{ + int i; + char name[32]; + + devc->mix_devices = &(ad1848_mix_devices[0]); + + sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs); + + for (i = 0; i < 32; i++) + devc->mixer_reroute[i] = i; + + devc->supported_rec_devices = MODE1_REC_DEVICES; + + switch (devc->model) + { + case MD_4231: + case MD_4231A: + case MD_1845: + case MD_1845_SSCAPE: + devc->supported_devices = MODE2_MIXER_DEVICES; + break; + + case MD_C930: + devc->supported_devices = C930_MIXER_DEVICES; + devc->mix_devices = &(c930_mix_devices[0]); + break; + + case MD_IWAVE: + devc->supported_devices = MODE3_MIXER_DEVICES; + devc->mix_devices = &(iwave_mix_devices[0]); + break; + + case MD_42xB: + case MD_4239: + devc->mix_devices = &(cs42xb_mix_devices[0]); + devc->supported_devices = MODE3_MIXER_DEVICES; + break; + case MD_4232: + case MD_4236: + devc->supported_devices = MODE3_MIXER_DEVICES; + break; + + case MD_1848: + if (soundpro) { + devc->supported_devices = SPRO_MIXER_DEVICES; + devc->supported_rec_devices = SPRO_REC_DEVICES; + devc->mix_devices = &(spro_mix_devices[0]); + break; + } + + default: + devc->supported_devices = MODE1_MIXER_DEVICES; + } + + devc->orig_devices = devc->supported_devices; + devc->orig_rec_devices = devc->supported_rec_devices; + + devc->levels = load_mixer_volumes(name, default_mixer_levels, 1); + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + { + if (devc->supported_devices & (1 << i)) + ad1848_mixer_set(devc, i, devc->levels[i]); + } + + ad1848_set_recmask(devc, SOUND_MASK_MIC); + + devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT; + + if (!soundpro) { + if (devc->mixer_output_port & AUDIO_SPEAKER) + ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ + else + ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ + } else { + /* + * From the "wouldn't it be nice if the mixer API had (better) + * support for custom stuff" category + */ + /* Enable surround mode and SB16 mixer */ + ad_write(devc, 16, 0x60); + } +} + +static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + ad1848_info *devc = mixer_devs[dev]->devc; + int val; + + if (cmd == SOUND_MIXER_PRIVATE1) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (val != 0xffff) + { + val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT); + devc->mixer_output_port = val; + val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */ + devc->mixer_output_port = val; + if (val & AUDIO_SPEAKER) + ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ + else + ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ + } + val = devc->mixer_output_port; + return put_user(val, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + return(ad1848_control(AD1848_MIXER_REROUTE, val)); + } + if (((cmd >> 8) & 0xff) == 'M') + { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + { + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = ad1848_set_recmask(devc, val); + break; + + default: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = ad1848_mixer_set(devc, cmd & 0xff, val); + break; + } + return put_user(val, (int *)arg); + } + else + { + switch (cmd & 0xff) + { + /* + * Return parameters + */ + + case SOUND_MIXER_RECSRC: + val = devc->recmask; + break; + + case SOUND_MIXER_DEVMASK: + val = devc->supported_devices; + break; + + case SOUND_MIXER_STEREODEVS: + val = devc->supported_devices; + if (devc->model != MD_C930) + val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); + break; + + case SOUND_MIXER_RECMASK: + val = devc->supported_rec_devices; + break; + + case SOUND_MIXER_CAPS: + val=SOUND_CAP_EXCL_INPUT; + break; + + default: + val = ad1848_mixer_get(devc, cmd & 0xff); + break; + } + return put_user(val, (int *)arg); + } + } + else + return -EINVAL; +} + +static int ad1848_set_speed(int dev, int arg) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + /* + * The sampling speed is encoded in the least significant nibble of I8. The + * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other + * three bits select the divisor (indirectly): + * + * The available speeds are in the following table. Keep the speeds in + * the increasing order. + */ + typedef struct + { + int speed; + unsigned char bits; + } + speed_struct; + + static speed_struct speed_table[] = + { + {5510, (0 << 1) | 1}, + {5510, (0 << 1) | 1}, + {6620, (7 << 1) | 1}, + {8000, (0 << 1) | 0}, + {9600, (7 << 1) | 0}, + {11025, (1 << 1) | 1}, + {16000, (1 << 1) | 0}, + {18900, (2 << 1) | 1}, + {22050, (3 << 1) | 1}, + {27420, (2 << 1) | 0}, + {32000, (3 << 1) | 0}, + {33075, (6 << 1) | 1}, + {37800, (4 << 1) | 1}, + {44100, (5 << 1) | 1}, + {48000, (6 << 1) | 0} + }; + + int i, n, selected = -1; + + n = sizeof(speed_table) / sizeof(speed_struct); + + if (arg <= 0) + return portc->speed; + + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* AD1845 has different timer than others */ + { + if (arg < 4000) + arg = 4000; + if (arg > 50000) + arg = 50000; + + portc->speed = arg; + portc->speed_bits = speed_table[3].bits; + return portc->speed; + } + if (arg < speed_table[0].speed) + selected = 0; + if (arg > speed_table[n - 1].speed) + selected = n - 1; + + for (i = 1 /*really */ ; selected == -1 && i < n; i++) + { + if (speed_table[i].speed == arg) + selected = i; + else if (speed_table[i].speed > arg) + { + int diff1, diff2; + + diff1 = arg - speed_table[i - 1].speed; + diff2 = speed_table[i].speed - arg; + + if (diff1 < diff2) + selected = i - 1; + else + selected = i; + } + } + if (selected == -1) + { + printk(KERN_WARNING "ad1848: Can't find speed???\n"); + selected = 3; + } + portc->speed = speed_table[selected].speed; + portc->speed_bits = speed_table[selected].bits; + return portc->speed; +} + +static short ad1848_set_channels(int dev, short arg) +{ + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + if (arg != 1 && arg != 2) + return portc->channels; + + portc->channels = arg; + return arg; +} + +static unsigned int ad1848_set_bits(int dev, unsigned int arg) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + static struct format_tbl + { + int format; + unsigned char bits; + } + format2bits[] = + { + { + 0, 0 + } + , + { + AFMT_MU_LAW, 1 + } + , + { + AFMT_A_LAW, 3 + } + , + { + AFMT_IMA_ADPCM, 5 + } + , + { + AFMT_U8, 0 + } + , + { + AFMT_S16_LE, 2 + } + , + { + AFMT_S16_BE, 6 + } + , + { + AFMT_S8, 0 + } + , + { + AFMT_U16_LE, 0 + } + , + { + AFMT_U16_BE, 0 + } + }; + int i, n = sizeof(format2bits) / sizeof(struct format_tbl); + + if (arg == 0) + return portc->audio_format; + + if (!(arg & ad_format_mask[devc->model])) + arg = AFMT_U8; + + portc->audio_format = arg; + + for (i = 0; i < n; i++) + if (format2bits[i].format == arg) + { + if ((portc->format_bits = format2bits[i].bits) == 0) + return portc->audio_format = AFMT_U8; /* Was not supported */ + + return arg; + } + /* Still hanging here. Something must be terribly wrong */ + portc->format_bits = 0; + return portc->audio_format = AFMT_U8; +} + +static struct audio_driver ad1848_audio_driver = +{ + owner: THIS_MODULE, + open: ad1848_open, + close: ad1848_close, + output_block: ad1848_output_block, + start_input: ad1848_start_input, + prepare_for_input: ad1848_prepare_for_input, + prepare_for_output: ad1848_prepare_for_output, + halt_io: ad1848_halt, + halt_input: ad1848_halt_input, + halt_output: ad1848_halt_output, + trigger: ad1848_trigger, + set_speed: ad1848_set_speed, + set_bits: ad1848_set_bits, + set_channels: ad1848_set_channels +}; + +static struct mixer_operations ad1848_mixer_operations = +{ + owner: THIS_MODULE, + id: "SOUNDPORT", + name: "AD1848/CS4248/CS4231", + ioctl: ad1848_mixer_ioctl +}; + +static int ad1848_open(int dev, int mode) +{ + ad1848_info *devc = NULL; + ad1848_port_info *portc; + unsigned long flags; + + if (dev < 0 || dev >= num_audiodevs) + return -ENXIO; + + devc = (ad1848_info *) audio_devs[dev]->devc; + portc = (ad1848_port_info *) audio_devs[dev]->portc; + + save_flags(flags); + cli(); + if (portc->open_mode || (devc->open_mode & mode)) + { + restore_flags(flags); + return -EBUSY; + } + devc->dual_dma = 0; + + if (audio_devs[dev]->flags & DMA_DUPLEX) + { + devc->dual_dma = 1; + } + devc->intr_active = 0; + devc->audio_mode = 0; + devc->open_mode |= mode; + portc->open_mode = mode; + ad1848_trigger(dev, 0); + + if (mode & OPEN_READ) + devc->record_dev = dev; + if (mode & OPEN_WRITE) + devc->playback_dev = dev; + restore_flags(flags); +/* + * Mute output until the playback really starts. This decreases clicking (hope so). + */ + ad_mute(devc); + + return 0; +} + +static void ad1848_close(int dev) +{ + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + DEB(printk("ad1848_close(void)\n")); + + save_flags(flags); + cli(); + + devc->intr_active = 0; + ad1848_halt(dev); + + devc->audio_mode = 0; + devc->open_mode &= ~portc->open_mode; + portc->open_mode = 0; + + ad_unmute(devc); + restore_flags(flags); +} + +static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag) +{ + unsigned long flags, cnt; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + cnt = count; + + if (portc->audio_format == AFMT_IMA_ADPCM) + { + cnt /= 4; + } + else + { + if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ + cnt >>= 1; + } + if (portc->channels > 1) + cnt >>= 1; + cnt--; + + if ((devc->audio_mode & PCM_ENABLE_OUTPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) && + intrflag && + cnt == devc->xfer_count) + { + devc->audio_mode |= PCM_ENABLE_OUTPUT; + devc->intr_active = 1; + return; /* + * Auto DMA mode on. No need to react + */ + } + save_flags(flags); + cli(); + + ad_write(devc, 15, (unsigned char) (cnt & 0xff)); + ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); + + devc->xfer_count = cnt; + devc->audio_mode |= PCM_ENABLE_OUTPUT; + devc->intr_active = 1; + restore_flags(flags); +} + +static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag) +{ + unsigned long flags, cnt; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + cnt = count; + if (portc->audio_format == AFMT_IMA_ADPCM) + { + cnt /= 4; + } + else + { + if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ + cnt >>= 1; + } + if (portc->channels > 1) + cnt >>= 1; + cnt--; + + if ((devc->audio_mode & PCM_ENABLE_INPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) && + intrflag && + cnt == devc->xfer_count) + { + devc->audio_mode |= PCM_ENABLE_INPUT; + devc->intr_active = 1; + return; /* + * Auto DMA mode on. No need to react + */ + } + save_flags(flags); + cli(); + + if (devc->model == MD_1848) + { + ad_write(devc, 15, (unsigned char) (cnt & 0xff)); + ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); + } + else + { + ad_write(devc, 31, (unsigned char) (cnt & 0xff)); + ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff)); + } + + ad_unmute(devc); + + devc->xfer_count = cnt; + devc->audio_mode |= PCM_ENABLE_INPUT; + devc->intr_active = 1; + restore_flags(flags); +} + +static int ad1848_prepare_for_output(int dev, int bsize, int bcount) +{ + int timeout; + unsigned char fs, old_fs, tmp = 0; + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + ad_mute(devc); + + save_flags(flags); + cli(); + fs = portc->speed_bits | (portc->format_bits << 5); + + if (portc->channels > 1) + fs |= 0x10; + + ad_enter_MCE(devc); /* Enables changes to the format select reg */ + + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* Use alternate speed select registers */ + { + fs &= 0xf0; /* Mask off the rate select bits */ + + ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */ + ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */ + } + old_fs = ad_read(devc, 8); + + if (devc->model == MD_4232 || devc->model >= MD_4236) + { + tmp = ad_read(devc, 16); + ad_write(devc, 16, tmp | 0x30); + } + if (devc->model == MD_IWAVE) + ad_write(devc, 17, 0xc2); /* Disable variable frequency select */ + + ad_write(devc, 8, fs); + + /* + * Write to I8 starts resynchronization. Wait until it completes. + */ + + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + + if (devc->model >= MD_4232) + ad_write(devc, 16, tmp & ~0x30); + + ad_leave_MCE(devc); /* + * Starts the calibration process. + */ + restore_flags(flags); + devc->xfer_count = 0; + +#ifndef EXCLUDE_TIMERS + if (dev == timer_installed && devc->timer_running) + if ((fs & 0x01) != (old_fs & 0x01)) + { + ad1848_tmr_reprogram(dev); + } +#endif + ad1848_halt_output(dev); + return 0; +} + +static int ad1848_prepare_for_input(int dev, int bsize, int bcount) +{ + int timeout; + unsigned char fs, old_fs, tmp = 0; + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + if (devc->audio_mode) + return 0; + + save_flags(flags); + cli(); + fs = portc->speed_bits | (portc->format_bits << 5); + + if (portc->channels > 1) + fs |= 0x10; + + ad_enter_MCE(devc); /* Enables changes to the format select reg */ + + if ((devc->model == MD_1845) || (devc->model == MD_1845_SSCAPE)) /* Use alternate speed select registers */ + { + fs &= 0xf0; /* Mask off the rate select bits */ + + ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */ + ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */ + } + if (devc->model == MD_4232) + { + tmp = ad_read(devc, 16); + ad_write(devc, 16, tmp | 0x30); + } + if (devc->model == MD_IWAVE) + ad_write(devc, 17, 0xc2); /* Disable variable frequency select */ + + /* + * If mode >= 2 (CS4231), set I28. It's the capture format register. + */ + + if (devc->model != MD_1848) + { + old_fs = ad_read(devc, 28); + ad_write(devc, 28, fs); + + /* + * Write to I28 starts resynchronization. Wait until it completes. + */ + + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + + if (devc->model != MD_1848 && devc->model != MD_1845 && devc->model != MD_1845_SSCAPE) + { + /* + * CS4231 compatible devices don't have separate sampling rate selection + * register for recording an playback. The I8 register is shared so we have to + * set the speed encoding bits of it too. + */ + unsigned char tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0); + + ad_write(devc, 8, tmp); + /* + * Write to I8 starts resynchronization. Wait until it completes. + */ + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + } + } + else + { /* For AD1848 set I8. */ + + old_fs = ad_read(devc, 8); + ad_write(devc, 8, fs); + /* + * Write to I8 starts resynchronization. Wait until it completes. + */ + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + } + + if (devc->model == MD_4232) + ad_write(devc, 16, tmp & ~0x30); + + ad_leave_MCE(devc); /* + * Starts the calibration process. + */ + restore_flags(flags); + devc->xfer_count = 0; + +#ifndef EXCLUDE_TIMERS + if (dev == timer_installed && devc->timer_running) + { + if ((fs & 0x01) != (old_fs & 0x01)) + { + ad1848_tmr_reprogram(dev); + } + } +#endif + ad1848_halt_input(dev); + return 0; +} + +static void ad1848_halt(int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + unsigned char bits = ad_read(devc, 9); + + if (bits & 0x01 && (portc->open_mode & OPEN_WRITE)) + ad1848_halt_output(dev); + + if (bits & 0x02 && (portc->open_mode & OPEN_READ)) + ad1848_halt_input(dev); + devc->audio_mode = 0; +} + +static void ad1848_halt_input(int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned long flags; + + if (!(ad_read(devc, 9) & 0x02)) + return; /* Capture not enabled */ + + save_flags(flags); + cli(); + + ad_mute(devc); + + { + int tmout; + + if(!isa_dma_bridge_buggy) + disable_dma(audio_devs[dev]->dmap_in->dma); + + for (tmout = 0; tmout < 100000; tmout++) + if (ad_read(devc, 11) & 0x10) + break; + ad_write(devc, 9, ad_read(devc, 9) & ~0x02); /* Stop capture */ + + if(!isa_dma_bridge_buggy) + enable_dma(audio_devs[dev]->dmap_in->dma); + devc->audio_mode &= ~PCM_ENABLE_INPUT; + } + + outb(0, io_Status(devc)); /* Clear interrupt status */ + outb(0, io_Status(devc)); /* Clear interrupt status */ + + devc->audio_mode &= ~PCM_ENABLE_INPUT; + + restore_flags(flags); +} + +static void ad1848_halt_output(int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned long flags; + + if (!(ad_read(devc, 9) & 0x01)) + return; /* Playback not enabled */ + + save_flags(flags); + cli(); + + ad_mute(devc); + { + int tmout; + + if(!isa_dma_bridge_buggy) + disable_dma(audio_devs[dev]->dmap_out->dma); + + for (tmout = 0; tmout < 100000; tmout++) + if (ad_read(devc, 11) & 0x10) + break; + ad_write(devc, 9, ad_read(devc, 9) & ~0x01); /* Stop playback */ + + if(!isa_dma_bridge_buggy) + enable_dma(audio_devs[dev]->dmap_out->dma); + + devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + } + + outb((0), io_Status(devc)); /* Clear interrupt status */ + outb((0), io_Status(devc)); /* Clear interrupt status */ + + devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + + restore_flags(flags); +} + +static void ad1848_trigger(int dev, int state) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + unsigned long flags; + unsigned char tmp, old; + + save_flags(flags); + cli(); + state &= devc->audio_mode; + + tmp = old = ad_read(devc, 9); + + if (portc->open_mode & OPEN_READ) + { + if (state & PCM_ENABLE_INPUT) + tmp |= 0x02; + else + tmp &= ~0x02; + } + if (portc->open_mode & OPEN_WRITE) + { + if (state & PCM_ENABLE_OUTPUT) + tmp |= 0x01; + else + tmp &= ~0x01; + } + /* ad_mute(devc); */ + if (tmp != old) + { + ad_write(devc, 9, tmp); + ad_unmute(devc); + } + restore_flags(flags); +} + +static void ad1848_init_hw(ad1848_info * devc) +{ + int i; + int *init_values; + + /* + * Initial values for the indirect registers of CS4248/AD1848. + */ + static int init_values_a[] = + { + 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00, + + /* Positions 16 to 31 just for CS4231/2 and ad1845 */ + 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static int init_values_b[] = + { + /* + Values for the newer chips + Some of the register initialization values were changed. In + order to get rid of the click that preceded PCM playback, + calibration was disabled on the 10th byte. On that same byte, + dual DMA was enabled; on the 11th byte, ADC dithering was + enabled, since that is theoretically desirable; on the 13th + byte, Mode 3 was selected, to enable access to extended + registers. + */ + 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0xe0, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + /* + * Select initialisation data + */ + + init_values = init_values_a; + if(devc->model >= MD_4236) + init_values = init_values_b; + + for (i = 0; i < 16; i++) + ad_write(devc, i, init_values[i]); + + + ad_mute(devc); /* Initialize some variables */ + ad_unmute(devc); /* Leave it unmuted now */ + + if (devc->model > MD_1848) + { + if (devc->model == MD_1845_SSCAPE) + ad_write(devc, 12, ad_read(devc, 12) | 0x50); + else + ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ + + if (devc->model == MD_IWAVE) + ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ + + if (devc->model != MD_1845_SSCAPE) + for (i = 16; i < 32; i++) + ad_write(devc, i, init_values[i]); + + if (devc->model == MD_IWAVE) + ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ + } + if (devc->model > MD_1848) + { + if (devc->audio_flags & DMA_DUPLEX) + ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */ + else + ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ + + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) + ad_write(devc, 27, ad_read(devc, 27) | 0x08); /* Alternate freq select enabled */ + + if (devc->model == MD_IWAVE) + { /* Some magic Interwave specific initialization */ + ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ + ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ + ad_write(devc, 17, 0xc2); /* Alternate feature enable */ + } + } + else + { + devc->audio_flags &= ~DMA_DUPLEX; + ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ + if (soundpro) + ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ + } + + outb((0), io_Status(devc)); /* Clear pending interrupts */ + + /* + * Toggle the MCE bit. It completes the initialization phase. + */ + + ad_enter_MCE(devc); /* In case the bit was off */ + ad_leave_MCE(devc); + + ad1848_mixer_reset(devc); +} + +int ad1848_detect(int io_base, int *ad_flags, int *osp) +{ + unsigned char tmp; + ad1848_info *devc = &adev_info[nr_ad1848_devs]; + unsigned char tmp1 = 0xff, tmp2 = 0xff; + int optiC930 = 0; /* OPTi 82C930 flag */ + int interwave = 0; + int ad1847_flag = 0; + int cs4248_flag = 0; + int sscape_flag = 0; + + int i; + + DDB(printk("ad1848_detect(%x)\n", io_base)); + + if (ad_flags) + { + if (*ad_flags == 0x12345678) + { + interwave = 1; + *ad_flags = 0; + } + + if (*ad_flags == 0x87654321) + { + sscape_flag = 1; + *ad_flags = 0; + } + + if (*ad_flags == 0x12345677) + { + cs4248_flag = 1; + *ad_flags = 0; + } + } + if (nr_ad1848_devs >= MAX_AUDIO_DEV) + { + printk(KERN_ERR "ad1848 - Too many audio devices\n"); + return 0; + } + if (check_region(io_base, 4)) + { + printk(KERN_ERR "ad1848.c: Port %x not free.\n", io_base); + return 0; + } + devc->base = io_base; + devc->irq_ok = 0; + devc->timer_running = 0; + devc->MCE_bit = 0x40; + devc->irq = 0; + devc->open_mode = 0; + devc->chip_name = devc->name = "AD1848"; + devc->model = MD_1848; /* AD1848 or CS4248 */ + devc->levels = NULL; + devc->debug_flag = 0; + + /* + * Check that the I/O address is in use. + * + * The bit 0x80 of the base I/O port is known to be 0 after the + * chip has performed its power on initialization. Just assume + * this has happened before the OS is starting. + * + * If the I/O address is unused, it typically returns 0xff. + */ + + if (inb(devc->base) == 0xff) + { + DDB(printk("ad1848_detect: The base I/O address appears to be dead\n")); + } + + /* + * Wait for the device to stop initialization + */ + + DDB(printk("ad1848_detect() - step 0\n")); + + for (i = 0; i < 10000000; i++) + { + unsigned char x = inb(devc->base); + + if (x == 0xff || !(x & 0x80)) + break; + } + + DDB(printk("ad1848_detect() - step A\n")); + + if (inb(devc->base) == 0x80) /* Not ready. Let's wait */ + ad_leave_MCE(devc); + + if ((inb(devc->base) & 0x80) != 0x00) /* Not a AD1848 */ + { + DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base))); + return 0; + } + + /* + * Test if it's possible to change contents of the indirect registers. + * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only + * so try to avoid using it. + */ + + DDB(printk("ad1848_detect() - step B\n")); + ad_write(devc, 0, 0xaa); + ad_write(devc, 1, 0x45); /* 0x55 with bit 0x10 clear */ + + if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45) + { + if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */ + ad1847_flag = 1; + else + { + DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2)); + return 0; + } + } + DDB(printk("ad1848_detect() - step C\n")); + ad_write(devc, 0, 0x45); + ad_write(devc, 1, 0xaa); + + if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa) + { + if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */ + ad1847_flag = 1; + else + { + DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2)); + return 0; + } + } + + /* + * The indirect register I12 has some read only bits. Let's + * try to change them. + */ + + DDB(printk("ad1848_detect() - step D\n")); + tmp = ad_read(devc, 12); + ad_write(devc, 12, (~tmp) & 0x0f); + + if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f)) + { + DDB(printk("ad1848 detect error - step D (%x)\n", tmp1)); + return 0; + } + + /* + * NOTE! Last 4 bits of the reg I12 tell the chip revision. + * 0x01=RevB and 0x0A=RevC. + */ + + /* + * The original AD1848/CS4248 has just 15 indirect registers. This means + * that I0 and I16 should return the same value (etc.). + * However this doesn't work with CS4248. Actually it seems to be impossible + * to detect if the chip is a CS4231 or CS4248. + * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails + * with CS4231. + */ + + /* + * OPTi 82C930 has mode2 control bit in another place. This test will fail + * with it. Accept this situation as a possible indication of this chip. + */ + + DDB(printk("ad1848_detect() - step F\n")); + ad_write(devc, 12, 0); /* Mode2=disabled */ + + for (i = 0; i < 16; i++) + { + if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) + { + DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2)); + if (!ad1847_flag) + optiC930 = 1; + break; + } + } + + /* + * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40). + * The bit 0x80 is always 1 in CS4248 and CS4231. + */ + + DDB(printk("ad1848_detect() - step G\n")); + + if (ad_flags && *ad_flags == 400) + *ad_flags = 0; + else + ad_write(devc, 12, 0x40); /* Set mode2, clear 0x80 */ + + + if (ad_flags) + *ad_flags = 0; + + tmp1 = ad_read(devc, 12); + if (tmp1 & 0x80) + { + if (ad_flags) + *ad_flags |= AD_F_CS4248; + + devc->chip_name = "CS4248"; /* Our best knowledge just now */ + } + if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40)) + { + /* + * CS4231 detected - is it? + * + * Verify that setting I0 doesn't change I16. + */ + + DDB(printk("ad1848_detect() - step H\n")); + ad_write(devc, 16, 0); /* Set I16 to known value */ + + ad_write(devc, 0, 0x45); + if ((tmp1 = ad_read(devc, 16)) != 0x45) /* No change -> CS4231? */ + { + ad_write(devc, 0, 0xaa); + if ((tmp1 = ad_read(devc, 16)) == 0xaa) /* Rotten bits? */ + { + DDB(printk("ad1848 detect error - step H(%x)\n", tmp1)); + return 0; + } + + /* + * Verify that some bits of I25 are read only. + */ + + DDB(printk("ad1848_detect() - step I\n")); + tmp1 = ad_read(devc, 25); /* Original bits */ + ad_write(devc, 25, ~tmp1); /* Invert all bits */ + if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7)) + { + int id; + + /* + * It's at least CS4231 + */ + + devc->chip_name = "CS4231"; + devc->model = MD_4231; + + /* + * It could be an AD1845 or CS4231A as well. + * CS4231 and AD1845 report the same revision info in I25 + * while the CS4231A reports different. + */ + + id = ad_read(devc, 25); + if ((id & 0xe7) == 0x80) /* Device busy??? */ + id = ad_read(devc, 25); + if ((id & 0xe7) == 0x80) /* Device still busy??? */ + id = ad_read(devc, 25); + DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25))); + + if ((id & 0xe7) == 0x80) { + /* + * It must be a CS4231 or AD1845. The register I23 of + * CS4231 is undefined and it appears to be read only. + * AD1845 uses I23 for setting sample rate. Assume + * the chip is AD1845 if I23 is changeable. + */ + + unsigned char tmp = ad_read(devc, 23); + ad_write(devc, 23, ~tmp); + + if (interwave) + { + devc->model = MD_IWAVE; + devc->chip_name = "IWave"; + } + else if (ad_read(devc, 23) != tmp) /* AD1845 ? */ + { + devc->chip_name = "AD1845"; + devc->model = MD_1845; + } + else if (cs4248_flag) + { + if (ad_flags) + *ad_flags |= AD_F_CS4248; + devc->chip_name = "CS4248"; + devc->model = MD_1848; + ad_write(devc, 12, ad_read(devc, 12) & ~0x40); /* Mode2 off */ + } + ad_write(devc, 23, tmp); /* Restore */ + } + else + { + switch (id & 0x1f) { + case 3: /* CS4236/CS4235/CS42xB/CS4239 */ + { + int xid; + ad_write(devc, 12, ad_read(devc, 12) | 0x60); /* switch to mode 3 */ + ad_write(devc, 23, 0x9c); /* select extended register 25 */ + xid = inb(io_Indexed_Data(devc)); + ad_write(devc, 12, ad_read(devc, 12) & ~0x60); /* back to mode 0 */ + switch (xid & 0x1f) + { + case 0x00: + devc->chip_name = "CS4237B(B)"; + devc->model = MD_42xB; + break; + case 0x08: + /* Seems to be a 4238 ?? */ + devc->chip_name = "CS4238"; + devc->model = MD_42xB; + break; + case 0x09: + devc->chip_name = "CS4238B"; + devc->model = MD_42xB; + break; + case 0x0b: + devc->chip_name = "CS4236B"; + devc->model = MD_4236; + break; + case 0x10: + devc->chip_name = "CS4237B"; + devc->model = MD_42xB; + break; + case 0x1d: + devc->chip_name = "CS4235"; + devc->model = MD_4235; + break; + case 0x1e: + devc->chip_name = "CS4239"; + devc->model = MD_4239; + break; + default: + printk("Chip ident is %X.\n", xid&0x1F); + devc->chip_name = "CS42xx"; + devc->model = MD_4232; + break; + } + } + break; + + case 2: /* CS4232/CS4232A */ + devc->chip_name = "CS4232"; + devc->model = MD_4232; + break; + + case 0: + if ((id & 0xe0) == 0xa0) + { + devc->chip_name = "CS4231A"; + devc->model = MD_4231A; + } + else + { + devc->chip_name = "CS4321"; + devc->model = MD_4231; + } + break; + + default: /* maybe */ + DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7)); + if (optiC930) + { + devc->chip_name = "82C930"; + devc->model = MD_C930; + } + else + { + devc->chip_name = "CS4231"; + devc->model = MD_4231; + } + } + } + } + ad_write(devc, 25, tmp1); /* Restore bits */ + + DDB(printk("ad1848_detect() - step K\n")); + } + } else if (tmp1 == 0x0a) { + /* + * Is it perhaps a SoundPro CMI8330? + * If so, then we should be able to change indirect registers + * greater than I15 after activating MODE2, even though reading + * back I12 does not show it. + */ + + /* + * Let's try comparing register values + */ + for (i = 0; i < 16; i++) { + if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) { + DDB(printk("ad1848 detect step H(%d/%x/%x) - SoundPro chip?\n", i, tmp1, tmp2)); + soundpro = 1; + devc->chip_name = "SoundPro CMI 8330"; + break; + } + } + } + + DDB(printk("ad1848_detect() - step L\n")); + if (ad_flags) + { + if (devc->model != MD_1848) + *ad_flags |= AD_F_CS4231; + } + DDB(printk("ad1848_detect() - Detected OK\n")); + + if (devc->model == MD_1848 && ad1847_flag) + devc->chip_name = "AD1847"; + + + if (sscape_flag == 1) + devc->model = MD_1845_SSCAPE; + + return 1; +} + +int ad1848_init (char *name, int io_base, int irq, int dma_playback, + int dma_capture, int share_dma, int *osp, struct module *owner) +{ + /* + * NOTE! If irq < 0, there is another driver which has allocated the IRQ + * so that this driver doesn't need to allocate/deallocate it. + * The actually used IRQ is ABS(irq). + */ + + int my_dev; + char dev_name[100]; + int e; + + ad1848_info *devc = &adev_info[nr_ad1848_devs]; + + ad1848_port_info *portc = NULL; + + devc->irq = (irq > 0) ? irq : 0; + devc->open_mode = 0; + devc->timer_ticks = 0; + devc->dma1 = dma_playback; + devc->dma2 = dma_capture; + devc->subtype = cfg.card_subtype; + devc->audio_flags = DMA_AUTOMODE; + devc->playback_dev = devc->record_dev = 0; + if (name != NULL) + devc->name = name; + + if (name != NULL && name[0] != 0) + sprintf(dev_name, + "%s (%s)", name, devc->chip_name); + else + sprintf(dev_name, + "Generic audio codec (%s)", devc->chip_name); + + request_region(devc->base, 4, devc->name); + + conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture); + + if (devc->model == MD_1848 || devc->model == MD_C930) + devc->audio_flags |= DMA_HARDSTOP; + + if (devc->model > MD_1848) + { + if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1) + devc->audio_flags &= ~DMA_DUPLEX; + else + devc->audio_flags |= DMA_DUPLEX; + } + + portc = (ad1848_port_info *) kmalloc(sizeof(ad1848_port_info), GFP_KERNEL); + if(portc==NULL) + return -1; + + if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + dev_name, + &ad1848_audio_driver, + sizeof(struct audio_driver), + devc->audio_flags, + ad_format_mask[devc->model], + devc, + dma_playback, + dma_capture)) < 0) + { + kfree(portc); + portc=NULL; + return -1; + } + + audio_devs[my_dev]->portc = portc; + audio_devs[my_dev]->mixer_dev = -1; + if (owner) + audio_devs[my_dev]->d->owner = owner; + memset((char *) portc, 0, sizeof(*portc)); + + nr_ad1848_devs++; + + devc->pmdev = pm_register(PM_ISA_DEV, my_dev, ad1848_pm_callback); + if (devc->pmdev) + devc->pmdev->data = devc; + + ad1848_init_hw(devc); + + if (irq > 0) + { + devc->dev_no = my_dev; + if (request_irq(devc->irq, adintr, 0, devc->name, (void *)my_dev) < 0) + { + printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n"); + /* Don't free it either then.. */ + devc->irq = 0; + } + if (capabilities[devc->model].flags & CAP_F_TIMER) + { +#ifndef CONFIG_SMP + int x; + unsigned char tmp = ad_read(devc, 16); +#endif + + devc->timer_ticks = 0; + + ad_write(devc, 21, 0x00); /* Timer MSB */ + ad_write(devc, 20, 0x10); /* Timer LSB */ +#ifndef CONFIG_SMP + ad_write(devc, 16, tmp | 0x40); /* Enable timer */ + for (x = 0; x < 100000 && devc->timer_ticks == 0; x++); + ad_write(devc, 16, tmp & ~0x40); /* Disable timer */ + + if (devc->timer_ticks == 0) + printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", irq); + else + { + DDB(printk("Interrupt test OK\n")); + devc->irq_ok = 1; + } +#else + devc->irq_ok = 1; +#endif + } + else + devc->irq_ok = 1; /* Couldn't test. assume it's OK */ + } else if (irq < 0) + irq2dev[-irq] = devc->dev_no = my_dev; + +#ifndef EXCLUDE_TIMERS + if ((capabilities[devc->model].flags & CAP_F_TIMER) && + devc->irq_ok) + ad1848_tmr_install(my_dev); +#endif + + if (!share_dma) + { + if (sound_alloc_dma(dma_playback, devc->name)) + printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_playback); + + if (dma_capture != dma_playback) + if (sound_alloc_dma(dma_capture, devc->name)) + printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_capture); + } + + if ((e = sound_install_mixer(MIXER_DRIVER_VERSION, + dev_name, + &ad1848_mixer_operations, + sizeof(struct mixer_operations), + devc)) >= 0) + { + audio_devs[my_dev]->mixer_dev = e; + if (owner) + mixer_devs[e]->owner = owner; + } + return my_dev; +} + +int ad1848_control(int cmd, int arg) +{ + ad1848_info *devc; + + if (nr_ad1848_devs < 1) + return -ENODEV; + + devc = &adev_info[nr_ad1848_devs - 1]; + + switch (cmd) + { + case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */ + if (devc->model != MD_1845 || devc->model != MD_1845_SSCAPE) + return -EINVAL; + ad_enter_MCE(devc); + ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5)); + ad_leave_MCE(devc); + break; + + case AD1848_MIXER_REROUTE: + { + int o = (arg >> 8) & 0xff; + int n = arg & 0xff; + + if (o < 0 || o >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + + if (!(devc->supported_devices & (1 << o)) && + !(devc->supported_rec_devices & (1 << o))) + return -EINVAL; + + if (n == SOUND_MIXER_NONE) + { /* Just hide this control */ + ad1848_mixer_set(devc, o, 0); /* Shut up it */ + devc->supported_devices &= ~(1 << o); + devc->supported_rec_devices &= ~(1 << o); + break; + } + + /* Make the mixer control identified by o to appear as n */ + if (n < 0 || n >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + + devc->mixer_reroute[n] = o; /* Rename the control */ + if (devc->supported_devices & (1 << o)) + devc->supported_devices |= (1 << n); + if (devc->supported_rec_devices & (1 << o)) + devc->supported_rec_devices |= (1 << n); + + devc->supported_devices &= ~(1 << o); + devc->supported_rec_devices &= ~(1 << o); + } + break; + } + return 0; +} + +void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma) +{ + int i, mixer, dev = 0; + ad1848_info *devc = NULL; + + for (i = 0; devc == NULL && i < nr_ad1848_devs; i++) + { + if (adev_info[i].base == io_base) + { + devc = &adev_info[i]; + dev = devc->dev_no; + } + } + + if (devc != NULL) + { + if(audio_devs[dev]->portc!=NULL) + kfree(audio_devs[dev]->portc); + release_region(devc->base, 4); + + if (!share_dma) + { + if (devc->irq > 0) /* There is no point in freeing irq, if it wasn't allocated */ + free_irq(devc->irq, (void *)devc->dev_no); + + sound_free_dma(dma_playback); + + if (dma_playback != dma_capture) + sound_free_dma(dma_capture); + + } + mixer = audio_devs[devc->dev_no]->mixer_dev; + if(mixer>=0) + sound_unload_mixerdev(mixer); + + if (devc->pmdev) + pm_unregister(devc->pmdev); + + nr_ad1848_devs--; + for ( ; i < nr_ad1848_devs ; i++) + adev_info[i] = adev_info[i+1]; + } + else + printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base); +} + +void adintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + unsigned char status; + ad1848_info *devc; + int dev; + int alt_stat = 0xff; + unsigned char c930_stat = 0; + int cnt = 0; + + dev = (int)dev_id; + devc = (ad1848_info *) audio_devs[dev]->devc; + +interrupt_again: /* Jump back here if int status doesn't reset */ + + status = inb(io_Status(devc)); + + if (status == 0x80) + printk(KERN_DEBUG "adintr: Why?\n"); + if (devc->model == MD_1848) + outb((0), io_Status(devc)); /* Clear interrupt status */ + + if (status & 0x01) + { + if (devc->model == MD_C930) + { /* 82C930 has interrupt status register in MAD16 register MC11 */ + unsigned long flags; + + save_flags(flags); + cli(); + + /* 0xe0e is C930 address port + * 0xe0f is C930 data port + */ + outb(11, 0xe0e); + c930_stat = inb(0xe0f); + outb((~c930_stat), 0xe0f); + + restore_flags(flags); + + alt_stat = (c930_stat << 2) & 0x30; + } + else if (devc->model != MD_1848) + { + alt_stat = ad_read(devc, 24); + ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */ + } + + if ((devc->open_mode & OPEN_READ) && (devc->audio_mode & PCM_ENABLE_INPUT) && (alt_stat & 0x20)) + { + DMAbuf_inputintr(devc->record_dev); + } + if ((devc->open_mode & OPEN_WRITE) && (devc->audio_mode & PCM_ENABLE_OUTPUT) && + (alt_stat & 0x10)) + { + DMAbuf_outputintr(devc->playback_dev, 1); + } + if (devc->model != MD_1848 && (alt_stat & 0x40)) /* Timer interrupt */ + { + devc->timer_ticks++; +#ifndef EXCLUDE_TIMERS + if (timer_installed == dev && devc->timer_running) + sound_timer_interrupt(); +#endif + } + } +/* + * Sometimes playback or capture interrupts occur while a timer interrupt + * is being handled. The interrupt will not be retriggered if we don't + * handle it now. Check if an interrupt is still pending and restart + * the handler in this case. + */ + if (inb(io_Status(devc)) & 0x01 && cnt++ < 4) + { + goto interrupt_again; + } +} + +/* + * Experimental initialization sequence for the integrated sound system + * of the Compaq Deskpro M. + */ + +static int init_deskpro_m(struct address_info *hw_config) +{ + unsigned char tmp; + + if ((tmp = inb(0xc44)) == 0xff) + { + DDB(printk("init_deskpro_m: Dead port 0xc44\n")); + return 0; + } + + outb(0x10, 0xc44); + outb(0x40, 0xc45); + outb(0x00, 0xc46); + outb(0xe8, 0xc47); + outb(0x14, 0xc44); + outb(0x40, 0xc45); + outb(0x00, 0xc46); + outb(0xe8, 0xc47); + outb(0x10, 0xc44); + + return 1; +} + +/* + * Experimental initialization sequence for the integrated sound system + * of Compaq Deskpro XL. + */ + +static int init_deskpro(struct address_info *hw_config) +{ + unsigned char tmp; + + if ((tmp = inb(0xc44)) == 0xff) + { + DDB(printk("init_deskpro: Dead port 0xc44\n")); + return 0; + } + outb((tmp | 0x04), 0xc44); /* Select bank 1 */ + if (inb(0xc44) != 0x04) + { + DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n")); + return 0; + } + /* + * OK. It looks like a Deskpro so let's proceed. + */ + + /* + * I/O port 0xc44 Audio configuration register. + * + * bits 0xc0: Audio revision bits + * 0x00 = Compaq Business Audio + * 0x40 = MS Sound System Compatible (reset default) + * 0x80 = Reserved + * 0xc0 = Reserved + * bit 0x20: No Wait State Enable + * 0x00 = Disabled (reset default, DMA mode) + * 0x20 = Enabled (programmed I/O mode) + * bit 0x10: MS Sound System Decode Enable + * 0x00 = Decoding disabled (reset default) + * 0x10 = Decoding enabled + * bit 0x08: FM Synthesis Decode Enable + * 0x00 = Decoding Disabled (reset default) + * 0x08 = Decoding enabled + * bit 0x04 Bank select + * 0x00 = Bank 0 + * 0x04 = Bank 1 + * bits 0x03 MSS Base address + * 0x00 = 0x530 (reset default) + * 0x01 = 0x604 + * 0x02 = 0xf40 + * 0x03 = 0xe80 + */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc44 (before): "); + outb((tmp & ~0x04), 0xc44); + printk("%02x ", inb(0xc44)); + outb((tmp | 0x04), 0xc44); + printk("%02x\n", inb(0xc44)); +#endif + + /* Set bank 1 of the register */ + tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */ + + switch (hw_config->io_base) + { + case 0x530: + tmp |= 0x00; + break; + case 0x604: + tmp |= 0x01; + break; + case 0xf40: + tmp |= 0x02; + break; + case 0xe80: + tmp |= 0x03; + break; + default: + DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base)); + return 0; + } + outb((tmp & ~0x04), 0xc44); /* Write to bank=0 */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc44 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc44)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc44)); +#endif + + /* + * I/O port 0xc45 FM Address Decode/MSS ID Register. + * + * bank=0, bits 0xfe: FM synthesis Decode Compare bits 7:1 (default=0x88) + * bank=0, bit 0x01: SBIC Power Control Bit + * 0x00 = Powered up + * 0x01 = Powered down + * bank=1, bits 0xfc: MSS ID (default=0x40) + */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc45 (before): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc45)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc45)); +#endif + + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + outb((0x88), 0xc45); /* FM base 7:0 = 0x88 */ + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + outb((0x10), 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc45 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc45)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc45)); +#endif + + + /* + * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register. + * + * bank=0, bits 0xff: FM synthesis Decode Compare bits 15:8 (default=0x03) + * bank=1, bits 0xff: Audio addressing ASIC id + */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc46 (before): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc46)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc46)); +#endif + + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + outb((0x03), 0xc46); /* FM base 15:8 = 0x03 */ + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + outb((0x11), 0xc46); /* ASIC ID = 0x11 */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc46 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc46)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc46)); +#endif + + /* + * I/O port 0xc47 FM Address Decode Register. + * + * bank=0, bits 0xff: Decode enable selection for various FM address bits + * bank=1, bits 0xff: Reserved + */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc47 (before): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc47)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc47)); +#endif + + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + outb((0x7c), 0xc47); /* FM decode enable bits = 0x7c */ + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + outb((0x00), 0xc47); /* Reserved bank1 = 0x00 */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc47 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc47)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc47)); +#endif + + /* + * I/O port 0xc6f = Audio Disable Function Register + */ + +#ifdef DEBUGXL + printk("Port 0xc6f (before) = %02x\n", inb(0xc6f)); +#endif + + outb((0x80), 0xc6f); + +#ifdef DEBUGXL + printk("Port 0xc6f (after) = %02x\n", inb(0xc6f)); +#endif + + return 1; +} + +int probe_ms_sound(struct address_info *hw_config) +{ + unsigned char tmp; + + DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype)); + + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "MSS: I/O port conflict\n"); + return 0; + } + if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ + { + /* check_opl3(0x388, hw_config); */ + return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); + } + + if (deskpro_xl && hw_config->card_subtype == 2) /* Compaq Deskpro XL */ + { + if (!init_deskpro(hw_config)) + return 0; + } + + if (deskpro_m) /* Compaq Deskpro M */ + { + if (!init_deskpro_m(hw_config)) + return 0; + } + + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (AudioTrix Pro for example) + * return 0x00 or 0x0f. + */ + + if ((tmp = inb(hw_config->io_base + 3)) == 0xff) /* Bus float */ + { + int ret; + + DDB(printk("I/O address is inactive (%x)\n", tmp)); + if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp))) + return 0; + return 1; + } + DDB(printk("MSS signature = %x\n", tmp & 0x3f)); + if ((tmp & 0x3f) != 0x04 && + (tmp & 0x3f) != 0x0f && + (tmp & 0x3f) != 0x00) + { + int ret; + + MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3))); + DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n")); + if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp))) + return 0; + + hw_config->card_subtype = 1; + return 1; + } + if ((hw_config->irq != 5) && + (hw_config->irq != 7) && + (hw_config->irq != 9) && + (hw_config->irq != 10) && + (hw_config->irq != 11) && + (hw_config->irq != 12)) + { + printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) + { + printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma); + return 0; + } + /* + * Check that DMA0 is not in use with a 8 bit board. + */ + + if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "MSS: Can't use DMA0 with a 8 bit card/slot\n"); + return 0; + } + if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); + return 0; + } + return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); +} + +void attach_ms_sound(struct address_info *hw_config, struct module *owner) +{ + static signed char interrupt_bits[12] = + { + -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 + }; + signed char bits; + char dma2_bit = 0; + + static char dma_bits[4] = + { + 1, 2, 0, 3 + }; + + int config_port = hw_config->io_base + 0; + int version_port = hw_config->io_base + 3; + int dma = hw_config->dma; + int dma2 = hw_config->dma2; + + if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ + { + hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0, + hw_config->osp, + owner); + request_region(hw_config->io_base, 4, "WSS config"); + return; + } + /* + * Set the IRQ and DMA addresses. + */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == -1) + { + printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); + return; + } + outb((bits | 0x40), config_port); + if ((inb(version_port) & 0x40) == 0) + printk(KERN_ERR "[MSS: IRQ Conflict?]\n"); + +/* + * Handle the capture DMA channel + */ + + if (dma2 != -1 && dma2 != dma) + { + if (!((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0))) + { /* Unsupported combination. Try to swap channels */ + int tmp = dma; + + dma = dma2; + dma2 = tmp; + } + if ((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0)) + { + dma2_bit = 0x04; /* Enable capture DMA */ + } + else + { + printk(KERN_WARNING "MSS: Invalid capture DMA\n"); + dma2 = dma; + } + } + else + { + dma2 = dma; + } + + hw_config->dma = dma; + hw_config->dma2 = dma2; + + outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ + + hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, + hw_config->irq, + dma, dma2, 0, + hw_config->osp, + THIS_MODULE); + request_region(hw_config->io_base, 4, "WSS config"); +} + +void unload_ms_sound(struct address_info *hw_config) +{ + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0); + sound_unload_audiodev(hw_config->slots[0]); + release_region(hw_config->io_base, 4); +} + +#ifndef EXCLUDE_TIMERS + +/* + * Timer stuff (for /dev/music). + */ + +static unsigned int current_interval = 0; + +static unsigned int ad1848_tmr_start(int dev, unsigned int usecs) +{ + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */ + unsigned long divider; + + save_flags(flags); + cli(); + + /* + * Length of the timer interval (in nanoseconds) depends on the + * selected crystal oscillator. Check this from bit 0x01 of I8. + * + * AD1845 has just one oscillator which has cycle time of 10.050 us + * (when a 24.576 MHz xtal oscillator is used). + * + * Convert requested interval to nanoseconds before computing + * the timer divider. + */ + + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) + xtal_nsecs = 10050; + else if (ad_read(devc, 8) & 0x01) + xtal_nsecs = 9920; + else + xtal_nsecs = 9969; + + divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs; + + if (divider < 100) /* Don't allow shorter intervals than about 1ms */ + divider = 100; + + if (divider > 65535) /* Overflow check */ + divider = 65535; + + ad_write(devc, 21, (divider >> 8) & 0xff); /* Set upper bits */ + ad_write(devc, 20, divider & 0xff); /* Set lower bits */ + ad_write(devc, 16, ad_read(devc, 16) | 0x40); /* Start the timer */ + devc->timer_running = 1; + restore_flags(flags); + + return current_interval = (divider * xtal_nsecs + 500) / 1000; +} + +static void ad1848_tmr_reprogram(int dev) +{ + /* + * Audio driver has changed sampling rate so that a different xtal + * oscillator was selected. We have to reprogram the timer rate. + */ + + ad1848_tmr_start(dev, current_interval); + sound_timer_syncinterval(current_interval); +} + +static void ad1848_tmr_disable(int dev) +{ + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + save_flags(flags); + cli(); + ad_write(devc, 16, ad_read(devc, 16) & ~0x40); + devc->timer_running = 0; + restore_flags(flags); +} + +static void ad1848_tmr_restart(int dev) +{ + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + if (current_interval == 0) + return; + + save_flags(flags); + cli(); + ad_write(devc, 16, ad_read(devc, 16) | 0x40); + devc->timer_running = 1; + restore_flags(flags); +} + +static struct sound_lowlev_timer ad1848_tmr = +{ + 0, + 2, + ad1848_tmr_start, + ad1848_tmr_disable, + ad1848_tmr_restart +}; + +static int ad1848_tmr_install(int dev) +{ + if (timer_installed != -1) + return 0; /* Don't install another timer */ + + timer_installed = ad1848_tmr.dev = dev; + sound_timer_init(&ad1848_tmr, audio_devs[dev]->name); + + return 1; +} +#endif /* EXCLUDE_TIMERS */ + +static int ad1848_suspend(ad1848_info *devc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + ad_mute(devc); + + restore_flags(flags); + return 0; +} + +static int ad1848_resume(ad1848_info *devc) +{ + unsigned long flags; + int mixer_levels[32], i; + + save_flags(flags); + cli(); + + /* Thinkpad is a bit more of PITA than normal. The BIOS tends to + restore it in a different config to the one we use. Need to + fix this somehow */ + + /* store old mixer levels */ + memcpy(mixer_levels, devc->levels, sizeof (mixer_levels)); + ad1848_init_hw(devc); + + /* restore mixer levels */ + for (i = 0; i < 32; i++) + ad1848_mixer_set(devc, devc->dev_no, mixer_levels[i]); + + if (!devc->subtype) { + static signed char interrupt_bits[12] = { -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 }; + static char dma_bits[4] = { 1, 2, 0, 3 }; + + signed char bits; + char dma2_bit = 0; + + int config_port = devc->base + 0; + + bits = interrupt_bits[devc->irq]; + if (bits == -1) { + printk(KERN_ERR "MSS: Bad IRQ %d\n", devc->irq); + restore_flags(flags); + return -1; + } + + outb((bits | 0x40), config_port); + + if (devc->dma2 != -1 && devc->dma2 != devc->dma1) + if ( (devc->dma1 == 0 && devc->dma2 == 1) || + (devc->dma1 == 1 && devc->dma2 == 0) || + (devc->dma1 == 3 && devc->dma2 == 0)) + dma2_bit = 0x04; + + outb((bits | dma_bits[devc->dma1] | dma2_bit), config_port); + } + + restore_flags(flags); + return 0; +} + +static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + ad1848_info *devc = dev->data; + if (devc) { + DEB(printk("ad1848: pm event received: 0x%x\n", rqst)); + + switch (rqst) { + case PM_SUSPEND: + ad1848_suspend(devc); + break; + case PM_RESUME: + ad1848_resume(devc); + break; + } + } + return 0; +} + + +EXPORT_SYMBOL(ad1848_detect); +EXPORT_SYMBOL(ad1848_init); +EXPORT_SYMBOL(ad1848_unload); +EXPORT_SYMBOL(ad1848_control); +EXPORT_SYMBOL(adintr); +EXPORT_SYMBOL(probe_ms_sound); +EXPORT_SYMBOL(attach_ms_sound); +EXPORT_SYMBOL(unload_ms_sound); + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata type = 0; + +MODULE_PARM(io, "i"); /* I/O for a raw AD1848 card */ +MODULE_PARM(irq, "i"); /* IRQ to use */ +MODULE_PARM(dma, "i"); /* First DMA channel */ +MODULE_PARM(dma2, "i"); /* Second DMA channel */ +MODULE_PARM(type, "i"); /* Card type */ +MODULE_PARM(deskpro_xl, "i"); /* Special magic for Deskpro XL boxen */ +MODULE_PARM(deskpro_m, "i"); /* Special magic for Deskpro M box */ +MODULE_PARM(soundpro, "i"); /* More special magic for SoundPro chips */ + +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "i"); +MODULE_PARM(isapnpjump, "i"); +MODULE_PARM(reverse, "i"); +MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled"); +MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke."); +MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order"); + +struct pci_dev *ad1848_dev = NULL; + +/* Please add new entries at the end of the table */ +static struct { + char *name; + unsigned short card_vendor, card_device, + vendor, function; + short mss_io, irq, dma, dma2; /* index into isapnp table */ + int type; +} ad1848_isapnp_list[] __initdata = { + {"CMI 8330 SoundPRO", + ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + 0, 0, 0,-1, 0}, + {"CS4232 based card", + ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), + 0, 0, 0, 1, 0}, + {"CS4232 based card", + ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), + 0, 0, 0, 1, 0}, + {"OPL3-SA2 WSS mode", + ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), + 1, 0, 0, 1, 1}, + {"Advanced Gravis InterWave Audio", + ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), + 0, 0, 0, 1, 0}, + {0} +}; + +static struct isapnp_device_id id_table[] __devinitdata = { + { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), 0 }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), 0 }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), 0 }, + { ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), 0 }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, id_table); + +static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) +{ + int err; + + /* Device already active? Let's use it */ + if(dev->active) + return(dev); + + if((err = dev->activate(dev)) < 0) { + printk(KERN_ERR "ad1848: %s %s config failed (out of resources?)[%d]\n", devname, resname, err); + + dev->deactivate(dev); + + return(NULL); + } + return(dev); +} + +static struct pci_dev *ad1848_init_generic(struct pci_bus *bus, struct address_info *hw_config, int slot) +{ + + /* Configure Audio device */ + if((ad1848_dev = isapnp_find_dev(bus, ad1848_isapnp_list[slot].vendor, ad1848_isapnp_list[slot].function, NULL))) + { + int ret; + ret = ad1848_dev->prepare(ad1848_dev); + /* If device is active, assume configured with /proc/isapnp + * and use anyway. Some other way to check this? */ + if(ret && ret != -EBUSY) { + printk(KERN_ERR "ad1848: ISAPnP found device that could not be autoconfigured.\n"); + return(NULL); + } + if(ret == -EBUSY) + audio_activated = 1; + + if((ad1848_dev = activate_dev(ad1848_isapnp_list[slot].name, "ad1848", ad1848_dev))) + { + hw_config->io_base = ad1848_dev->resource[ad1848_isapnp_list[slot].mss_io].start; + hw_config->irq = ad1848_dev->irq_resource[ad1848_isapnp_list[slot].irq].start; + hw_config->dma = ad1848_dev->dma_resource[ad1848_isapnp_list[slot].dma].start; + if(ad1848_isapnp_list[slot].dma2 != -1) + hw_config->dma2 = ad1848_dev->dma_resource[ad1848_isapnp_list[slot].dma2].start; + else + hw_config->dma2 = -1; + hw_config->card_subtype = ad1848_isapnp_list[slot].type; + } else + return(NULL); + } else + return(NULL); + + return(ad1848_dev); +} + +static int __init ad1848_isapnp_init(struct address_info *hw_config, struct pci_bus *bus, int slot) +{ + char *busname = bus->name[0] ? bus->name : ad1848_isapnp_list[slot].name; + + printk(KERN_INFO "ad1848: %s detected\n", busname); + + /* Initialize this baby. */ + + if(ad1848_init_generic(bus, hw_config, slot)) { + /* We got it. */ + + printk(KERN_NOTICE "ad1848: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", + busname, + hw_config->io_base, hw_config->irq, hw_config->dma, + hw_config->dma2); + return 1; + } + else + printk(KERN_INFO "ad1848: Failed to initialize %s\n", busname); + + return 0; +} + +static int __init ad1848_isapnp_probe(struct address_info *hw_config) +{ + static int first = 1; + int i; + + /* Count entries in sb_isapnp_list */ + for (i = 0; ad1848_isapnp_list[i].card_vendor != 0; i++); + i--; + + /* Check and adjust isapnpjump */ + if( isapnpjump < 0 || isapnpjump > i) { + isapnpjump = reverse ? i : 0; + printk(KERN_ERR "ad1848: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump); + } + + if(!first || !reverse) + i = isapnpjump; + first = 0; + while(ad1848_isapnp_list[i].card_vendor != 0) { + static struct pci_bus *bus = NULL; + + while ((bus = isapnp_find_card( + ad1848_isapnp_list[i].card_vendor, + ad1848_isapnp_list[i].card_device, + bus))) { + + if(ad1848_isapnp_init(hw_config, bus, i)) { + isapnpjump = i; /* start next search from here */ + return 0; + } + } + i += reverse ? -1 : 1; + } + + return -ENODEV; +} +#endif + + +static int __init init_ad1848(void) +{ + printk(KERN_INFO "ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + +#ifdef __ISAPNP__ + if(isapnp && (ad1848_isapnp_probe(&cfg) < 0) ) { + printk(KERN_NOTICE "ad1848: No ISAPnP cards found, trying standard ones...\n"); + isapnp = 0; + } +#endif + + if(io != -1) { + if( isapnp == 0 ) + { + if(irq == -1 || dma == -1) { + printk(KERN_WARNING "ad1848: must give I/O , IRQ and DMA.\n"); + return -EINVAL; + } + + cfg.irq = irq; + cfg.io_base = io; + cfg.dma = dma; + cfg.dma2 = dma2; + cfg.card_subtype = type; + } + + if(!probe_ms_sound(&cfg)) + return -ENODEV; + attach_ms_sound(&cfg, THIS_MODULE); + loaded = 1; + } + return 0; +} + +static void __exit cleanup_ad1848(void) +{ + if(loaded) + unload_ms_sound(&cfg); + +#ifdef __ISAPNP__ + if(audio_activated) + if(ad1848_dev) + ad1848_dev->deactivate(ad1848_dev); +#endif +} + +module_init(init_ad1848); +module_exit(cleanup_ad1848); + +#ifndef MODULE +static int __init setup_ad1848(char *str) +{ + /* io, irq, dma, dma2, type */ + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + type = ints[5]; + + return 1; +} + +__setup("ad1848=", setup_ad1848); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/ad1848.h b/sound/oss/ad1848.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ad1848.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,28 @@ +/* + * ad1848.c + * + * Copyright: Christoph Hellwig + */ + +#define AD_F_CS4231 0x0001 /* Returned if a CS4232 (or compatible) detected */ +#define AD_F_CS4248 0x0001 /* Returned if a CS4248 (or compatible) detected */ + +#define AD1848_SET_XTAL 1 +#define AD1848_MIXER_REROUTE 2 + +#define AD1848_REROUTE(oldctl, newctl) \ + ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl)) + + +int ad1848_init(char *name, int io_base, int irq, int dma_playback, + int dma_capture, int share_dma, int *osp, struct module *owner); +void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma); + +int ad1848_detect (int io_base, int *flags, int *osp); +int ad1848_control(int cmd, int arg); + +void adintr(int irq, void *dev_id, struct pt_regs * dummy); +void attach_ms_sound(struct address_info * hw_config, struct module * owner); + +int probe_ms_sound(struct address_info *hw_config); +void unload_ms_sound(struct address_info *hw_info); diff -Nru a/sound/oss/ad1848_mixer.h b/sound/oss/ad1848_mixer.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ad1848_mixer.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,253 @@ +/* + * sound/ad1848_mixer.h + * + * Definitions for the mixer of AD1848 and compatible codecs. + */ + +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + + +/* + * The AD1848 codec has generic input lines called Line, Aux1 and Aux2. + * Sound card manufacturers have connected actual inputs (CD, synth, line, + * etc) to these inputs in different order. Therefore it's difficult + * to assign mixer channels to these inputs correctly. The following + * contains two alternative mappings. The first one is for GUS MAX and + * the second is just a generic one (line1, line2 and line3). + * (Actually this is not a mapping but rather some kind of interleaving + * solution). + */ +#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \ + SOUND_MASK_LINE1 | SOUND_MASK_IMIX) + +#define SPRO_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_LINE1) + +#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \ + SOUND_MASK_LINE2 | \ + SOUND_MASK_IGAIN | \ + SOUND_MASK_PCM | SOUND_MASK_IMIX) + +#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \ + SOUND_MASK_MIC | \ + SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \ + SOUND_MASK_IGAIN | \ + SOUND_MASK_PCM | SOUND_MASK_IMIX) + +#define MODE3_MIXER_DEVICES (MODE2_MIXER_DEVICES | SOUND_MASK_VOLUME) + +/* OPTi 82C930 has no IMIX level control, but it can still be selected as an + * input + */ +#define C930_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \ + SOUND_MASK_MIC | SOUND_MASK_VOLUME | \ + SOUND_MASK_LINE3 | \ + SOUND_MASK_IGAIN | SOUND_MASK_PCM) + +#define SPRO_MIXER_DEVICES (SOUND_MASK_VOLUME | SOUND_MASK_PCM | \ + SOUND_MASK_LINE | SOUND_MASK_SYNTH | \ + SOUND_MASK_CD | SOUND_MASK_MIC | \ + SOUND_MASK_SPEAKER | SOUND_MASK_LINE1 | \ + SOUND_MASK_OGAIN) + +struct mixer_def { + unsigned int regno:6; /* register number for volume */ + unsigned int polarity:1; /* volume polarity: 0=normal, 1=reversed */ + unsigned int bitpos:3; /* position of bits in register for volume */ + unsigned int nbits:3; /* number of bits in register for volume */ + unsigned int mutereg:6; /* register number for mute bit */ + unsigned int mutepol:1; /* mute polarity: 0=normal, 1=reversed */ + unsigned int mutepos:4; /* position of mute bit in register */ + unsigned int recreg:6; /* register number for recording bit */ + unsigned int recpol:1; /* recording polarity: 0=normal, 1=reversed */ + unsigned int recpos:4; /* position of recording bit in register */ +}; + +static char mix_cvt[101] = { + 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, + 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, + 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, + 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, + 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, + 100 +}; + +typedef struct mixer_def mixer_ent; +typedef mixer_ent mixer_ents[2]; + +/* + * Most of the mixer entries work in backwards. Setting the polarity field + * makes them to work correctly. + * + * The channel numbering used by individual sound cards is not fixed. Some + * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs. + * The current version doesn't try to compensate this. + */ + +#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit) \ + [name] = {{reg_l, pola_l, pos_l, len_l, reg_l, 0, mute_bit, 0, 0, 8}, \ + {reg_r, pola_r, pos_r, len_r, reg_r, 0, mute_bit, 0, 0, 8}} + +#define MIX_ENT2(name, reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \ + rec_reg_l, rec_pola_l, rec_pos_l, \ + reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \ + rec_reg_r, rec_pola_r, rec_pos_r) \ + [name] = {{reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \ + rec_reg_l, rec_pola_l, rec_pos_l}, \ + {reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \ + rec_reg_r, rec_pola_r, rec_pos_r}} + +static mixer_ents ad1848_mix_devices[32] = { + MIX_ENT(SOUND_MIXER_VOLUME, 27, 1, 0, 4, 29, 1, 0, 4, 8), + MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), + MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8), + MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), + MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7) +}; + +static mixer_ents iwave_mix_devices[32] = { + MIX_ENT(SOUND_MIXER_VOLUME, 25, 1, 0, 5, 27, 1, 0, 5, 8), + MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), + MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8), + MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_IMIX, 16, 1, 0, 5, 17, 1, 0, 5, 8), + MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), + MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7) +}; + +static mixer_ents cs42xb_mix_devices[32] = { + /* Digital master volume actually has seven bits, but we only use + six to avoid the discontinuity when the analog gain kicks in. */ + MIX_ENT(SOUND_MIXER_VOLUME, 46, 1, 0, 6, 47, 1, 0, 6, 7), + MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), + MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_MIC, 34, 1, 0, 5, 35, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), + /* For the IMIX entry, it was not possible to use the MIX_ENT macro + because the mute bit is in different positions for the two + channels and requires reverse polarity. */ + [SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8}, + {42, 1, 0, 6, 42, 1, 7, 0, 0, 8}}, + MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), + MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE3, 38, 1, 0, 6, 39, 1, 0, 6, 7) +}; + +/* OPTi 82C930 has somewhat different port addresses. + * Note: VOLUME == SPEAKER, SYNTH == LINE2, LINE == LINE3, CD == LINE1 + * VOLUME, SYNTH, LINE, CD are not enabled above. + * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc. + */ +static mixer_ents c930_mix_devices[32] = { + MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5, 7), + MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_SPEAKER, 22, 1, 1, 5, 23, 1, 1, 5, 7), + MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_MIC, 20, 1, 1, 4, 21, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), + MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 1, 4, 3, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 1, 4, 5, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 1, 4, 19, 1, 1, 4, 7) +}; + +static mixer_ents spro_mix_devices[32] = { + MIX_ENT (SOUND_MIXER_VOLUME, 19, 0, 4, 4, 19, 0, 0, 4, 8), + MIX_ENT (SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT (SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT2(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 23, 0, 3, 0, 0, 8, + 5, 1, 1, 4, 23, 0, 3, 0, 0, 8), + MIX_ENT (SOUND_MIXER_PCM, 6, 1, 1, 4, 7, 1, 1, 4, 8), + MIX_ENT (SOUND_MIXER_SPEAKER, 18, 0, 3, 2, 0, 0, 0, 0, 8), + MIX_ENT2(SOUND_MIXER_LINE, 20, 0, 4, 4, 17, 1, 4, 16, 0, 2, + 20, 0, 0, 4, 17, 1, 3, 16, 0, 1), + MIX_ENT2(SOUND_MIXER_MIC, 18, 0, 0, 3, 17, 1, 0, 16, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + MIX_ENT2(SOUND_MIXER_CD, 21, 0, 4, 4, 17, 1, 2, 16, 0, 4, + 21, 0, 0, 4, 17, 1, 1, 16, 0, 3), + MIX_ENT (SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT (SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT (SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT (SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT (SOUND_MIXER_OGAIN, 17, 1, 6, 1, 0, 0, 0, 0, 8), + /* This is external wavetable */ + MIX_ENT2(SOUND_MIXER_LINE1, 22, 0, 4, 4, 23, 1, 1, 23, 0, 4, + 22, 0, 0, 4, 23, 1, 0, 23, 0, 5), +}; + +static int default_mixer_levels[32] = +{ + 0x3232, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x4b4b, /* FM */ + 0x3232, /* PCM */ + 0x1515, /* PC Speaker */ + 0x2020, /* Ext Line */ + 0x1010, /* Mic */ + 0x4b4b, /* CD */ + 0x0000, /* Recording monitor */ + 0x4b4b, /* Second PCM */ + 0x4b4b, /* Recording level */ + 0x4b4b, /* Input gain */ + 0x4b4b, /* Output gain */ + 0x2020, /* Line1 */ + 0x2020, /* Line2 */ + 0x1515 /* Line3 (usually line in)*/ +}; + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + +/* + * Channel enable bits for ioctl(SOUND_MIXER_PRIVATE1) + */ + +#ifndef AUDIO_SPEAKER +#define AUDIO_SPEAKER 0x01 /* Enable mono output */ +#define AUDIO_HEADPHONE 0x02 /* Sparc only */ +#define AUDIO_LINE_OUT 0x04 /* Sparc only */ +#endif diff -Nru a/sound/oss/adlib_card.c b/sound/oss/adlib_card.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/adlib_card.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,73 @@ +/* + * sound/adlib_card.c + * + * Detection routine for the AdLib card. + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +#include +#include + +#include "sound_config.h" + +#include "opl3.h" + +static void __init attach_adlib_card(struct address_info *hw_config) +{ + hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp, THIS_MODULE); +} + +static int __init probe_adlib(struct address_info *hw_config) +{ + return opl3_detect(hw_config->io_base, hw_config->osp); +} + +static struct address_info cfg; + +static int __initdata io = -1; + +MODULE_PARM(io, "i"); + +static int __init init_adlib(void) +{ + cfg.io_base = io; + + if (cfg.io_base == -1) { + printk(KERN_ERR "adlib: must specify I/O address.\n"); + return -EINVAL; + } + if (probe_adlib(&cfg) == 0) + return -ENODEV; + attach_adlib_card(&cfg); + + return 0; +} + +static void __exit cleanup_adlib(void) +{ + sound_unload_synthdev(cfg.slots[0]); + +} + +module_init(init_adlib); +module_exit(cleanup_adlib); + +#ifndef MODULE +static int __init setup_adlib(char *str) +{ + /* io */ + int ints[2]; + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + + return 1; +} +__setup("adlib=", setup_adlib); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/aedsp16.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1381 @@ +/* + drivers/sound/lowlevel/aedsp16.c + + Audio Excel DSP 16 software configuration routines + Copyright (C) 1995,1996,1997,1998 Riccardo Facchetti (fizban@tin.it) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ +/* + * Include the main OSS Lite header file. It include all the os, OSS Lite, etc + * headers needed by this source. + */ +#include +#include +#include +#include +#include "sound_config.h" + +/* + * Sanity checks + */ + +#if defined(CONFIG_SOUND_AEDSP16_SBPRO) && defined(CONFIG_SOUND_AEDSP16_MSS) +#error You have to enable only one of the MSS and SBPRO emulations. +#endif + +/* + + READ THIS + + This module started to configure the Audio Excel DSP 16 Sound Card. + Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards. + + NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this + audio card and want to see the kernel support for it, please contact me. + + Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401 + compatible card. + It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq), + so before this module, the only way to configure the DSP under linux was + boot the MS-DOS loading the sound.sys device driver (this driver soft- + configure the sound board hardware by massaging someone of its registers), + and then ctrl-alt-del to boot linux with the DSP configured by the DOS + driver. + + This module works configuring your Audio Excel DSP 16's irq, dma and + mpu-401-irq. The OSS Lite routines rely on the fact that if the + hardware is there, they can detect it. The problem with AEDSP16 is + that no hardware can be found by the probe routines if the sound card + is not configured properly. Sometimes the kernel probe routines can find + an SBPRO even when the card is not configured (this is the standard setup + of the card), but the SBPRO emulation don't work well if the card is not + properly initialized. For this reason + + aedsp16_init_board() + + routine is called before the OSS Lite probe routines try to detect the + hardware. + + NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS) + + NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards + have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They + have to be configured by software. + + NOTE: The driver is merged with the new OSS Lite sound driver. It works + as a lowlevel driver. + + The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS; + the OSS Lite sound driver can be configured for SBPRO and MSS cards + at the same time, but the aedsp16 can't be two cards!! + When we configure it, we have to choose the SBPRO or the MSS emulation + for AEDSP16. We also can install a *REAL* card of the other type (see [1]). + + NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO + please let me know if it works. + + The MPU-401 support can be compiled in together with one of the other + two operating modes. + + NOTE: This is something like plug-and-play: we have only to plug + the AEDSP16 board in the socket, and then configure and compile + a kernel that uses the AEDSP16 software configuration capability. + No jumper setting is needed! + + For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3 + you have just to make config the OSS Lite package, configuring + the AEDSP16 sound card, then activating the SBPro emulation mode + and at last configuring IRQ and DMA. + Compile the kernel and run it. + + NOTE: This means for SC-6000 cards that you can choose irq and dma, + but not the I/O addresses. To change I/O addresses you have to set + them with jumpers. For SC-6600 cards you have no jumpers so you have + to set up your full card configuration in the make config. + + You can change the irq/dma/mirq settings WITHOUT THE NEED to open + your computer and massage the jumpers (there are no irq/dma/mirq + jumpers to be configured anyway, only I/O BASE values have to be + configured with jumpers) + + For some ununderstandable reason, the card default of irq 7, dma 1, + don't work for me. Seems to be an IRQ or DMA conflict. Under heavy + HDD work, the kernel start to erupt out a lot of messages like: + + 'Sound: DMA timed out - IRQ/DRQ config error?' + + For what I can say, I have NOT any conflict at irq 7 (under linux I'm + using the lp polling driver), and dma line 1 is unused as stated by + /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so + I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows! + Anyway a setting of irq 10, dma 3 works really fine. + + NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know + the emulation mode, all the installed hardware and the hardware + configuration (irq and dma settings of all the hardware). + + This init module should work with SBPRO+MSS, when one of the two is + the AEDSP16 emulation and the other the real card. (see [1]) + For example: + + AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other + AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other + + MPU401 should work. (see [2]) + + [1] + --- + Date: Mon, 29 Jul 1997 08:35:40 +0100 + From: Mr S J Greenaway + + [...] + Just to let you know got my Audio Excel (emulating a MSS) working + with my original SB16, thanks for the driver! + [...] + --- + + [2] Not tested by me for lack of hardware. + + TODO, WISHES AND TECH + + - About I/O ports allocation - + + Request the 2x0h region (port base) in any case if we are using this card. + + NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16 + port base region (see code) does not mean necessarily that we are emulating + sbpro. Even if this region is the sbpro I/O ports region, we use this + region to access the control registers of the card, and if emulating + sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro + registers are not used, in no way, to emulate an sbpro: they are + used only for configuration purposes. + + Started Fri Mar 17 16:13:18 MET 1995 + + v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c) + - Initial code. + v0.2 (ALPHA) + - Cleanups. + - Integrated with Linux voxware v 2.90-2 kernel sound driver. + - SoundBlaster Pro mode configuration. + - Microsoft Sound System mode configuration. + - MPU-401 mode configuration. + v0.3 (ALPHA) + - Cleanups. + - Rearranged the code to let aedsp16_init_board be more general. + - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h + inclusion too. We rely on os.h + - Used the to get a variable + len string (we are not sure about the len of Copyright string). + This works with any SB and compatible. + - Added the code to request_region at device init (should go in + the main body of voxware). + v0.4 (BETA) + - Better configure.c patch for aedsp16 configuration (better + logic of inclusion of AEDSP16 support) + - Modified the conditional compilation to better support more than + one sound card of the emulated type (read the NOTES above) + - Moved the sb init routine from the attach to the very first + probe in sb_card.c + - Rearrangements and cleanups + - Wiped out some unnecessary code and variables: this is kernel + code so it is better save some TEXT and DATA + - Fixed the request_region code. We must allocate the aedsp16 (sbpro) + I/O ports in any case because they are used to access the DSP + configuration registers and we can not allow anyone to get them. + v0.5 + - cleanups on comments + - prep for diffs against v3.0-proto-950402 + v0.6 + - removed the request_region()s when compiling the MODULE sound.o + because we are not allowed (by the actual voxware structure) to + release_region() + v0.7 (pre ALPHA, not distributed) + - started porting this module to kernel 1.3.84. Dummy probe/attach + routines. + v0.8 (ALPHA) + - attached all the init routines. + v0.9 (BETA) + - Integrated with linux-pre2.0.7 + - Integrated with configuration scripts. + - Cleaned up and beautyfied the code. + v0.9.9 (BETA) + - Thanks to Piercarlo Grandi: corrected the conditonal compilation code. + Now only the code configured is compiled in, with some memory saving. + v0.9.10 + - Integration into the sound/lowlevel/ section of the sound driver. + - Re-organized the code. + v0.9.11 (not distributed) + - Rewritten the init interface-routines to initialize the AEDSP16 in + one shot. + - More cosmetics. + - SC-6600 support. + - More soft/hard configuration. + v0.9.12 + - Refined the v0.9.11 code with conditional compilation to distinguish + between SC-6000 and SC-6600 code. + v1.0.0 + - Prep for merging with OSS Lite and Linux kernel 2.1.13 + - Corrected a bug in request/check/release region calls (thanks to the + new kernel exception handling). + v1.1 + - Revamped for integration with new modularized sound drivers: to enhance + the flexibility of modular version, I have removed all the conditional + compilation for SBPRO, MPU and MSS code. Now it is all managed with + the ae_config structure. + v1.2 + - Module informations added. + - Removed aedsp16_delay_10msec(), now using mdelay(10) + - All data and funcs moved to .*.init section. + v1.3 + Arnaldo Carvalho de Melo - 2000/09/27 + - got rid of check_region + + Known Problems: + - Audio Excel DSP 16 III don't work with this driver. + + Credits: + Many thanks to Gerald Britton . He helped me a + lot in testing the 0.9.11 and 0.9.12 versions of this driver. + + */ + + +#define VERSION "1.3" /* Version of Audio Excel DSP 16 driver */ + +#undef AEDSP16_DEBUG /* Define this to 1 to enable debug code */ +#undef AEDSP16_DEBUG_MORE /* Define this to 1 to enable more debug */ +#undef AEDSP16_INFO /* Define this to 1 to enable info code */ + +#if defined(AEDSP16_DEBUG) +# define DBG(x) printk x +# if defined(AEDSP16_DEBUG_MORE) +# define DBG1(x) printk x +# else +# define DBG1(x) +# endif +#else +# define DBG(x) +# define DBG1(x) +#endif + +/* + * Misc definitions + */ +#define TRUE 1 +#define FALSE 0 + +/* + * Region Size for request/check/release region. + */ +#define IOBASE_REGION_SIZE 0x10 + +/* + * Hardware related defaults + */ +#define DEF_AEDSP16_IOB 0x220 /* 0x220(default) 0x240 */ +#define DEF_AEDSP16_IRQ 7 /* 5 7(default) 9 10 11 */ +#define DEF_AEDSP16_MRQ 0 /* 5 7 9 10 0(default), 0 means disable */ +#define DEF_AEDSP16_DMA 1 /* 0 1(default) 3 */ + +/* + * Commands of AEDSP16's DSP (SBPRO+special). + * Some of them are COMMAND_xx, in the future they may change. + */ +#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */ +#define COMMAND_52 0x52 /* */ +#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */ +#define COMMAND_5C 0x5c /* */ +#define COMMAND_60 0x60 /* */ +#define COMMAND_66 0x66 /* */ +#define COMMAND_6C 0x6c /* */ +#define COMMAND_6E 0x6e /* */ +#define COMMAND_88 0x88 /* */ +#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */ +#define COMMAND_C5 0xc5 /* */ +#define GET_DSP_VERSION 0xe1 /* Get DSP Version */ +#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */ + +/* + * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port + * to have the actual I/O port. + * Register permissions are: + * (wo) == Write Only + * (ro) == Read Only + * (w-) == Write + * (r-) == Read + */ +#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */ +#define DSP_READ 0x0a /* offset of DSP READ (ro) */ +#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */ +#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */ +#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */ +#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */ + + +#define RETRY 10 /* Various retry values on I/O opera- */ +#define STATUSRETRY 1000 /* tions. Sometimes we have to */ +#define HARDRETRY 500000 /* wait for previous cmd to complete */ + +/* + * Size of character arrays that store name and version of sound card + */ +#define CARDNAMELEN 15 /* Size of the card's name in chars */ +#define CARDVERLEN 2 /* Size of the card's version in chars */ + +#if defined(CONFIG_SC6600) +/* + * Bitmapped flags of hard configuration + */ +/* + * Decode macros (xl == low byte, xh = high byte) + */ +#define IOBASE(xl) ((xl & 0x01)?0x240:0x220) +#define JOY(xl) (xl & 0x02) +#define MPUADDR(xl) ( \ + (xl & 0x0C)?0x330: \ + (xl & 0x08)?0x320: \ + (xl & 0x04)?0x310: \ + 0x300) +#define WSSADDR(xl) ((xl & 0x10)?0xE80:0x530) +#define CDROM(xh) (xh & 0x20) +#define CDROMADDR(xh) (((xh & 0x1F) << 4) + 0x200) +/* + * Encode macros + */ +#define BLDIOBASE(xl, val) { \ + xl &= ~0x01; \ + if (val == 0x240) \ + xl |= 0x01; \ + } +#define BLDJOY(xl, val) { \ + xl &= ~0x02; \ + if (val == 1) \ + xl |= 0x02; \ + } +#define BLDMPUADDR(xl, val) { \ + xl &= ~0x0C; \ + switch (val) { \ + case 0x330: \ + xl |= 0x0C; \ + break; \ + case 0x320: \ + xl |= 0x08; \ + break; \ + case 0x310: \ + xl |= 0x04; \ + break; \ + case 0x300: \ + xl |= 0x00; \ + break; \ + default: \ + xl |= 0x00; \ + break; \ + } \ + } +#define BLDWSSADDR(xl, val) { \ + xl &= ~0x10; \ + if (val == 0xE80) \ + xl |= 0x10; \ + } +#define BLDCDROM(xh, val) { \ + xh &= ~0x20; \ + if (val == 1) \ + xh |= 0x20; \ + } +#define BLDCDROMADDR(xh, val) { \ + int tmp = val; \ + tmp -= 0x200; \ + tmp >>= 4; \ + tmp &= 0x1F; \ + xh |= tmp; \ + xh &= 0x7F; \ + xh |= 0x40; \ + } +#endif /* CONFIG_SC6600 */ + +/* + * Bit mapped flags for calling aedsp16_init_board(), and saving the current + * emulation mode. + */ +#define INIT_NONE (0 ) +#define INIT_SBPRO (1<<0) +#define INIT_MSS (1<<1) +#define INIT_MPU401 (1<<2) + +static int soft_cfg __initdata = 0; /* bitmapped config */ +static int soft_cfg_mss __initdata = 0; /* bitmapped mss config */ +static int ver[CARDVERLEN] __initdata = {0, 0}; /* DSP Ver: + hi->ver[0] lo->ver[1] */ + +#if defined(CONFIG_SC6600) +static int hard_cfg[2] /* lo<-hard_cfg[0] hi<-hard_cfg[1] */ + __initdata = { 0, 0}; +#endif /* CONFIG_SC6600 */ + +#if defined(CONFIG_SC6600) +/* Decoded hard configuration */ +struct d_hcfg { + int iobase; + int joystick; + int mpubase; + int wssbase; + int cdrom; + int cdrombase; +}; + +struct d_hcfg decoded_hcfg __initdata = {0, }; + +#endif /* CONFIG_SC6600 */ + +/* orVals contain the values to be or'ed */ +struct orVals { + int val; /* irq|mirq|dma */ + int or; /* soft_cfg |= TheStruct.or */ +}; + +/* aedsp16_info contain the audio card configuration */ +struct aedsp16_info { + int base_io; /* base I/O address for accessing card */ + int irq; /* irq value for DSP I/O */ + int mpu_irq; /* irq for mpu401 interface I/O */ + int dma; /* dma value for DSP I/O */ + int mss_base; /* base I/O for Microsoft Sound System */ + int mpu_base; /* base I/O for MPU-401 emulation */ + int init; /* Initialization status of the card */ +}; + +/* + * Magic values that the DSP will eat when configuring irq/mirq/dma + */ +/* DSP IRQ conversion array */ +static struct orVals orIRQ[] __initdata = { + {0x05, 0x28}, + {0x07, 0x08}, + {0x09, 0x10}, + {0x0a, 0x18}, + {0x0b, 0x20}, + {0x00, 0x00} +}; + +/* MPU-401 IRQ conversion array */ +static struct orVals orMIRQ[] __initdata = { + {0x05, 0x04}, + {0x07, 0x44}, + {0x09, 0x84}, + {0x0a, 0xc4}, + {0x00, 0x00} +}; + +/* DMA Channels conversion array */ +static struct orVals orDMA[] __initdata = { + {0x00, 0x01}, + {0x01, 0x02}, + {0x03, 0x03}, + {0x00, 0x00} +}; + +static struct aedsp16_info ae_config __initdata = { + DEF_AEDSP16_IOB, + DEF_AEDSP16_IRQ, + DEF_AEDSP16_MRQ, + DEF_AEDSP16_DMA, + -1, + -1, + INIT_NONE +}; + +/* + * Buffers to store audio card informations + */ +static char DSPCopyright[CARDNAMELEN + 1] __initdata = {0, }; +static char DSPVersion[CARDVERLEN + 1] __initdata = {0, }; + +static int __init aedsp16_wait_data(int port) +{ + int loop = STATUSRETRY; + unsigned char ret = 0; + + DBG1(("aedsp16_wait_data (0x%x): ", port)); + + do { + ret = inb(port + DSP_DATAVAIL); + /* + * Wait for data available (bit 7 of ret == 1) + */ + } while (!(ret & 0x80) && loop--); + + if (ret & 0x80) { + DBG1(("success.\n")); + return TRUE; + } + + DBG1(("failure.\n")); + return FALSE; +} + +static int __init aedsp16_read(int port) +{ + int inbyte; + + DBG((" Read DSP Byte (0x%x): ", port)); + + if (aedsp16_wait_data(port) == FALSE) { + DBG(("failure.\n")); + return -1; + } + + inbyte = inb(port + DSP_READ); + + DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte)); + + return inbyte; +} + +static int __init aedsp16_test_dsp(int port) +{ + return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE); +} + +static int __init aedsp16_dsp_reset(int port) +{ + /* + * Reset DSP + */ + + DBG(("Reset DSP:\n")); + + outb(1, (port + DSP_RESET)); + udelay(10); + outb(0, (port + DSP_RESET)); + udelay(10); + udelay(10); + if (aedsp16_test_dsp(port) == TRUE) { + DBG(("success.\n")); + return TRUE; + } else + DBG(("failure.\n")); + return FALSE; +} + +static int __init aedsp16_write(int port, int cmd) +{ + unsigned char ret; + int loop = HARDRETRY; + + DBG((" Write DSP Byte (0x%x) [0x%x]: ", port, cmd)); + + do { + ret = inb(port + DSP_STATUS); + /* + * DSP ready to receive data if bit 7 of ret == 0 + */ + if (!(ret & 0x80)) { + outb(cmd, port + DSP_COMMAND); + DBG(("success.\n")); + return 0; + } + } while (loop--); + + DBG(("timeout.\n")); + printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd); + + return -1; +} + +#if defined(CONFIG_SC6600) + +#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) +void __init aedsp16_pinfo(void) { + DBG(("\n Base address: %x\n", decoded_hcfg.iobase)); + DBG((" Joystick : %s present\n", decoded_hcfg.joystick?"":" not")); + DBG((" WSS addr : %x\n", decoded_hcfg.wssbase)); + DBG((" MPU-401 addr: %x\n", decoded_hcfg.mpubase)); + DBG((" CDROM : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not")); + DBG((" CDROMADDR : %x\n\n", decoded_hcfg.cdrombase)); +} +#endif + +void __init aedsp16_hard_decode(void) { + + DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); + +/* + * Decode Cfg Bytes. + */ + decoded_hcfg.iobase = IOBASE(hard_cfg[0]); + decoded_hcfg.joystick = JOY(hard_cfg[0]); + decoded_hcfg.wssbase = WSSADDR(hard_cfg[0]); + decoded_hcfg.mpubase = MPUADDR(hard_cfg[0]); + decoded_hcfg.cdrom = CDROM(hard_cfg[1]); + decoded_hcfg.cdrombase = CDROMADDR(hard_cfg[1]); + +#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) + printk(" Original sound card configuration:\n"); + aedsp16_pinfo(); +#endif + +/* + * Now set up the real kernel configuration. + */ + decoded_hcfg.iobase = ae_config.base_io; + decoded_hcfg.wssbase = ae_config.mss_base; + decoded_hcfg.mpubase = ae_config.mpu_base; + +#if defined(CONFIG_SC6600_JOY) + decoded_hcfg.joystick = CONFIG_SC6600_JOY; /* Enable */ +#endif +#if defined(CONFIG_SC6600_CDROM) + decoded_hcfg.cdrom = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */ +#endif +#if defined(CONFIG_SC6600_CDROMBASE) + decoded_hcfg.cdrombase = CONFIG_SC6600_CDROMBASE; /* 0 Disable */ +#endif + +#if defined(AEDSP16_DEBUG) + DBG((" New Values:\n")); + aedsp16_pinfo(); +#endif + + DBG(("success.\n")); +} + +void __init aedsp16_hard_encode(void) { + + DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); + + hard_cfg[0] = 0; + hard_cfg[1] = 0; + + hard_cfg[0] |= 0x20; + + BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase); + BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase); + BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase); + BLDJOY(hard_cfg[0], decoded_hcfg.joystick); + BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom); + BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase); + +#if defined(AEDSP16_DEBUG) + aedsp16_pinfo(); +#endif + + DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); + DBG(("success.\n")); + +} + +static int __init aedsp16_hard_write(int port) { + + DBG(("aedsp16_hard_write:\n")); + + if (aedsp16_write(port, COMMAND_6C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, COMMAND_5C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, hard_cfg[0])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, hard_cfg[1])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, COMMAND_C5)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5); + DBG(("failure.\n")); + return FALSE; + } + + DBG(("success.\n")); + + return TRUE; +} + +static int __init aedsp16_hard_read(int port) { + + DBG(("aedsp16_hard_read:\n")); + + if (aedsp16_write(port, READ_HARD_CFG)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + + if ((hard_cfg[0] = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + if ((hard_cfg[1] = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_read(port) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + + DBG(("success.\n")); + + return TRUE; +} + +static int __init aedsp16_ext_cfg_write(int port) { + + int extcfg, val; + + if (aedsp16_write(port, COMMAND_66)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66); + return FALSE; + } + + extcfg = 7; + if (decoded_hcfg.cdrom != 2) + extcfg = 0x0F; + if ((decoded_hcfg.cdrom == 4) || + (decoded_hcfg.cdrom == 3)) + extcfg &= ~2; + if (decoded_hcfg.cdrombase == 0) + extcfg &= ~2; + if (decoded_hcfg.mpubase == 0) + extcfg &= ~1; + + if (aedsp16_write(port, extcfg)) { + printk("[AEDSP16] Write extcfg: failed!\n"); + return FALSE; + } + if (aedsp16_write(port, 0)) { + printk("[AEDSP16] Write extcfg: failed!\n"); + return FALSE; + } + if (decoded_hcfg.cdrom == 3) { + if (aedsp16_write(port, COMMAND_52)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52); + return FALSE; + } + if ((val = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n" + , COMMAND_52); + return FALSE; + } + val &= 0x7F; + if (aedsp16_write(port, COMMAND_60)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60); + return FALSE; + } + if (aedsp16_write(port, val)) { + printk("[AEDSP16] Write val: failed!\n"); + return FALSE; + } + } + + return TRUE; +} + +#endif /* CONFIG_SC6600 */ + +static int __init aedsp16_cfg_write(int port) { + if (aedsp16_write(port, WRITE_MDIRQ_CFG)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); + return FALSE; + } + if (aedsp16_write(port, soft_cfg)) { + printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n"); + return FALSE; + } + return TRUE; +} + +static int __init aedsp16_init_mss(int port) +{ + DBG(("aedsp16_init_mss:\n")); + + mdelay(10); + + if (aedsp16_write(port, DSP_INIT_MSS)) { + printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n", + DSP_INIT_MSS); + DBG(("failure.\n")); + return FALSE; + } + + mdelay(10); + + if (aedsp16_cfg_write(port) == FALSE) + return FALSE; + + outb(soft_cfg_mss, ae_config.mss_base); + + DBG(("success.\n")); + + return TRUE; +} + +static int __init aedsp16_setup_board(int port) { + int loop = RETRY; + +#if defined(CONFIG_SC6600) + int val = 0; + + if (aedsp16_hard_read(port) == FALSE) { + printk("[AEDSP16] aedsp16_hard_read: failed!\n"); + return FALSE; + } + + if (aedsp16_write(port, COMMAND_52)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52); + return FALSE; + } + + if ((val = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + COMMAND_52); + return FALSE; + } +#endif + + do { + if (aedsp16_write(port, COMMAND_88)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88); + return FALSE; + } + mdelay(10); + } while ((aedsp16_wait_data(port) == FALSE) && loop--); + + if (aedsp16_read(port) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + COMMAND_88); + return FALSE; + } + +#if !defined(CONFIG_SC6600) + if (aedsp16_write(port, COMMAND_5C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); + return FALSE; + } +#endif + + if (aedsp16_cfg_write(port) == FALSE) + return FALSE; + +#if defined(CONFIG_SC6600) + if (aedsp16_write(port, COMMAND_60)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60); + return FALSE; + } + if (aedsp16_write(port, val)) { + printk("[AEDSP16] DATA 0x%x: failed!\n", val); + return FALSE; + } + if (aedsp16_write(port, COMMAND_6E)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E); + return FALSE; + } + if (aedsp16_write(port, ver[0])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]); + return FALSE; + } + if (aedsp16_write(port, ver[1])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]); + return FALSE; + } + + if (aedsp16_hard_write(port) == FALSE) { + printk("[AEDSP16] aedsp16_hard_write: failed!\n"); + return FALSE; + } + + if (aedsp16_write(port, COMMAND_5C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); + return FALSE; + } + +#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET) + if (aedsp16_cfg_write(port) == FALSE) + return FALSE; +#endif + +#endif + + return TRUE; +} + +static int __init aedsp16_stdcfg(int port) { + if (aedsp16_write(port, WRITE_MDIRQ_CFG)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); + return FALSE; + } + /* + * 0x0A == (IRQ 7, DMA 1, MIRQ 0) + */ + if (aedsp16_write(port, 0x0A)) { + printk("[AEDSP16] aedsp16_stdcfg: failed!\n"); + return FALSE; + } + return TRUE; +} + +static int __init aedsp16_dsp_version(int port) +{ + int len = 0; + int ret; + + DBG(("Get DSP Version:\n")); + + if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION); + DBG(("failed.\n")); + return FALSE; + } + + do { + if ((ret = aedsp16_read(port)) == -1) { + DBG(("failed.\n")); + return FALSE; + } + /* + * We already know how many int are stored (2), so we know when the + * string is finished. + */ + ver[len++] = ret; + } while (len < CARDVERLEN); + sprintf(DSPVersion, "%d.%d", ver[0], ver[1]); + + DBG(("success.\n")); + + return TRUE; +} + +static int __init aedsp16_dsp_copyright(int port) +{ + int len = 0; + int ret; + + DBG(("Get DSP Copyright:\n")); + + if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT); + DBG(("failed.\n")); + return FALSE; + } + + do { + if ((ret = aedsp16_read(port)) == -1) { + /* + * If no more data available, return to the caller, no error if len>0. + * We have no other way to know when the string is finished. + */ + if (len) + break; + else { + DBG(("failed.\n")); + return FALSE; + } + } + + DSPCopyright[len++] = ret; + + } while (len < CARDNAMELEN); + + DBG(("success.\n")); + + return TRUE; +} + +static void __init aedsp16_init_tables(void) +{ + int i = 0; + + memset(DSPCopyright, 0, CARDNAMELEN + 1); + memset(DSPVersion, 0, CARDVERLEN + 1); + + for (i = 0; orIRQ[i].or; i++) + if (orIRQ[i].val == ae_config.irq) { + soft_cfg |= orIRQ[i].or; + soft_cfg_mss |= orIRQ[i].or; + } + + for (i = 0; orMIRQ[i].or; i++) + if (orMIRQ[i].or == ae_config.mpu_irq) + soft_cfg |= orMIRQ[i].or; + + for (i = 0; orDMA[i].or; i++) + if (orDMA[i].val == ae_config.dma) { + soft_cfg |= orDMA[i].or; + soft_cfg_mss |= orDMA[i].or; + } +} + +static int __init aedsp16_init_board(void) +{ + aedsp16_init_tables(); + + if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_dsp_reset: failed!\n"); + return FALSE; + } + if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n"); + return FALSE; + } + + /* + * My AEDSP16 card return SC-6000 in DSPCopyright, so + * if we have something different, we have to be warned. + */ + if (strcmp("SC-6000", DSPCopyright)) + printk("[AEDSP16] Warning: non SC-6000 audio card!\n"); + + if (aedsp16_dsp_version(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_dsp_version: failed!\n"); + return FALSE; + } + + if (aedsp16_stdcfg(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_stdcfg: failed!\n"); + return FALSE; + } + +#if defined(CONFIG_SC6600) + if (aedsp16_hard_read(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_hard_read: failed!\n"); + return FALSE; + } + + aedsp16_hard_decode(); + + aedsp16_hard_encode(); + + if (aedsp16_hard_write(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_hard_write: failed!\n"); + return FALSE; + } + + if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n"); + return FALSE; + } +#endif /* CONFIG_SC6600 */ + + if (aedsp16_setup_board(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_setup_board: failed!\n"); + return FALSE; + } + + if (ae_config.mss_base != -1) { + if (ae_config.init & INIT_MSS) { + if (aedsp16_init_mss(ae_config.base_io) == FALSE) { + printk("[AEDSP16] Can not initialize" + "Microsoft Sound System mode.\n"); + return FALSE; + } + } + } + +#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) + + printk("Audio Excel DSP 16 init v%s (%s %s) [", + VERSION, DSPCopyright, + DSPVersion); + + if (ae_config.mpu_base != -1) { + if (ae_config.init & INIT_MPU401) { + printk("MPU401"); + if ((ae_config.init & INIT_MSS) || + (ae_config.init & INIT_SBPRO)) + printk(" "); + } + } + + if (ae_config.mss_base == -1) { + if (ae_config.init & INIT_SBPRO) { + printk("SBPro"); + if (ae_config.init & INIT_MSS) + printk(" "); + } + } + + if (ae_config.mss_base != -1) + if (ae_config.init & INIT_MSS) + printk("MSS"); + + printk("]\n"); +#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */ + + mdelay(10); + + return TRUE; +} + +static int __init init_aedsp16_sb(void) +{ + DBG(("init_aedsp16_sb: ")); + +/* + * If the card is already init'ed MSS, we can not init it to SBPRO too + * because the board can not emulate simultaneously MSS and SBPRO. + */ + if (ae_config.init & INIT_MSS) + return FALSE; + if (ae_config.init & INIT_SBPRO) + return FALSE; + + ae_config.init |= INIT_SBPRO; + + DBG(("done.\n")); + + return TRUE; +} + +static void __init uninit_aedsp16_sb(void) +{ + DBG(("uninit_aedsp16_sb: ")); + + ae_config.init &= ~INIT_SBPRO; + + DBG(("done.\n")); +} + +static int __init init_aedsp16_mss(void) +{ + DBG(("init_aedsp16_mss: ")); + +/* + * If the card is already init'ed SBPRO, we can not init it to MSS too + * because the board can not emulate simultaneously MSS and SBPRO. + */ + if (ae_config.init & INIT_SBPRO) + return FALSE; + if (ae_config.init & INIT_MSS) + return FALSE; +/* + * We must allocate the CONFIG_AEDSP16_BASE region too because these are the + * I/O ports to access card's control registers. + */ + if (!(ae_config.init & INIT_MPU401)) { + if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE, + "aedsp16 (base)")) { + printk( + "AEDSP16 BASE I/O port region is already in use.\n"); + return FALSE; + } + } + + ae_config.init |= INIT_MSS; + + DBG(("done.\n")); + + return TRUE; +} + +static void __init uninit_aedsp16_mss(void) +{ + DBG(("uninit_aedsp16_mss: ")); + + if ((!(ae_config.init & INIT_MPU401)) && + (ae_config.init & INIT_MSS)) { + release_region(ae_config.base_io, IOBASE_REGION_SIZE); + DBG(("AEDSP16 base region released.\n")); + } + + ae_config.init &= ~INIT_MSS; + DBG(("done.\n")); +} + +static int __init init_aedsp16_mpu(void) +{ + DBG(("init_aedsp16_mpu: ")); + + if (ae_config.init & INIT_MPU401) + return FALSE; + +/* + * We must request the CONFIG_AEDSP16_BASE region too because these are the I/O + * ports to access card's control registers. + */ + if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) { + if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE, + "aedsp16 (base)")) { + printk( + "AEDSP16 BASE I/O port region is already in use.\n"); + return FALSE; + } + } + + ae_config.init |= INIT_MPU401; + + DBG(("done.\n")); + + return TRUE; +} + +static void __init uninit_aedsp16_mpu(void) +{ + DBG(("uninit_aedsp16_mpu: ")); + + if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) && + (ae_config.init & INIT_MPU401)) { + release_region(ae_config.base_io, IOBASE_REGION_SIZE); + DBG(("AEDSP16 base region released.\n")); + } + + ae_config.init &= ~INIT_MPU401; + + DBG(("done.\n")); +} + +int __init init_aedsp16(void) +{ + int initialized = FALSE; + + DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n", + ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq)); + + if (ae_config.mss_base == -1) { + if (init_aedsp16_sb() == FALSE) { + uninit_aedsp16_sb(); + } else { + initialized = TRUE; + } + } + + if (ae_config.mpu_base != -1) { + if (init_aedsp16_mpu() == FALSE) { + uninit_aedsp16_mpu(); + } else { + initialized = TRUE; + } + } + +/* + * In the sequence of init routines, the MSS init MUST be the last! + * This because of the special register programming the MSS mode needs. + * A board reset would disable the MSS mode restoring the default SBPRO + * mode. + */ + if (ae_config.mss_base != -1) { + if (init_aedsp16_mss() == FALSE) { + uninit_aedsp16_mss(); + } else { + initialized = TRUE; + } + } + + if (initialized) + initialized = aedsp16_init_board(); + return initialized; +} + +void __init uninit_aedsp16(void) +{ + if (ae_config.mss_base != -1) + uninit_aedsp16_mss(); + else + uninit_aedsp16_sb(); + if (ae_config.mpu_base != -1) + uninit_aedsp16_mpu(); +} + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata mpu_irq = -1; +static int __initdata mss_base = -1; +static int __initdata mpu_base = -1; + +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)"); +MODULE_PARM(irq, "i"); +MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)"); +MODULE_PARM(dma, "i"); +MODULE_PARM_DESC(dma, "dma line (0 1 3)"); +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)"); +MODULE_PARM(mss_base, "i"); +MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)"); +MODULE_PARM(mpu_base, "i"); +MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)"); +MODULE_AUTHOR("Riccardo Facchetti "); +MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION); +MODULE_LICENSE("GPL"); + +static int __init do_init_aedsp16(void) { + printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n"); + if (io == -1 || dma == -1 || irq == -1) { + printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n"); + return -EINVAL; + } + + ae_config.base_io = io; + ae_config.irq = irq; + ae_config.dma = dma; + + ae_config.mss_base = mss_base; + ae_config.mpu_base = mpu_base; + ae_config.mpu_irq = mpu_irq; + + if (init_aedsp16() == FALSE) { + printk(KERN_ERR "aedsp16: initialization failed\n"); + /* + * XXX + * What error should we return here ? + */ + return -EINVAL; + } + return 0; +} + +static void __exit cleanup_aedsp16(void) { + uninit_aedsp16(); +} + +module_init(do_init_aedsp16); +module_exit(cleanup_aedsp16); + +#ifndef MODULE +static int __init setup_aedsp16(char *str) +{ + /* io, irq, dma, mss_io, mpu_io, mpu_irq */ + int ints[7]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + mss_base = ints[4]; + mpu_base = ints[5]; + mpu_irq = ints[6]; + return 1; +} + +__setup("aedsp16=", setup_aedsp16); +#endif diff -Nru a/sound/oss/audio.c b/sound/oss/audio.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/audio.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,987 @@ +/* + * sound/audio.c + * + * Device file manager for /dev/audio + */ + +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Thomas Sailer : moved several static variables into struct audio_operations + * (which is grossly misnamed btw.) because they have the same + * lifetime as the rest in there and dynamic allocation saves + * 12k or so + * Thomas Sailer : use more logical O_NONBLOCK semantics + * Daniel Rodriksson: reworked the use of the device specific copy_user + * still generic + * Horst von Brand: Add missing #include + * Chris Rankin : Update the module-usage counter for the coprocessor, + * and decrement the counters again if we cannot open + * the audio device. + */ + +#include +#include +#include + +#include "sound_config.h" +#include "ulaw.h" +#include "coproc.h" + +#define NEUTRAL8 0x80 +#define NEUTRAL16 0x00 + + +int dma_ioctl(int dev, unsigned int cmd, caddr_t arg); + +static int set_format(int dev, int fmt) +{ + if (fmt != AFMT_QUERY) + { + audio_devs[dev]->local_conversion = 0; + + if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ + { + if (fmt == AFMT_MU_LAW) + { + fmt = AFMT_U8; + audio_devs[dev]->local_conversion = CNV_MU_LAW; + } + else + fmt = AFMT_U8; /* This is always supported */ + } + audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt); + audio_devs[dev]->local_format = fmt; + } + else + return audio_devs[dev]->local_format; + + if (audio_devs[dev]->local_conversion) + return audio_devs[dev]->local_conversion; + else + return audio_devs[dev]->local_format; +} + +int audio_open(int dev, struct file *file) +{ + int ret; + int bits; + int dev_type = dev & 0x0f; + int mode = translate_mode(file); + const struct audio_driver *driver; + const struct coproc_operations *coprocessor; + + dev = dev >> 4; + + if (dev_type == SND_DEV_DSP16) + bits = 16; + else + bits = 8; + + if (dev < 0 || dev >= num_audiodevs) + return -ENXIO; + + driver = audio_devs[dev]->d; + if (driver->owner) + __MOD_INC_USE_COUNT(driver->owner); + + if ((ret = DMAbuf_open(dev, mode)) < 0) + goto error_1; + + if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) { + if (coprocessor->owner) + __MOD_INC_USE_COUNT(coprocessor->owner); + + if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) { + printk(KERN_WARNING "Sound: Can't access coprocessor device\n"); + goto error_2; + } + } + + audio_devs[dev]->local_conversion = 0; + + if (dev_type == SND_DEV_AUDIO) + set_format(dev, AFMT_MU_LAW); + else + set_format(dev, bits); + + audio_devs[dev]->audio_mode = AM_NONE; + + return 0; + + /* + * Clean-up stack: this is what needs (un)doing if + * we can't open the audio device ... + */ + error_2: + if (coprocessor->owner) + __MOD_DEC_USE_COUNT(coprocessor->owner); + DMAbuf_release(dev, mode); + + error_1: + if (driver->owner) + __MOD_DEC_USE_COUNT(driver->owner); + + return ret; +} + +static void sync_output(int dev) +{ + int p, i; + int l; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + if (dmap->fragment_size <= 0) + return; + dmap->flags |= DMA_POST; + + /* Align the write pointer with fragment boundaries */ + + if ((l = dmap->user_counter % dmap->fragment_size) > 0) + { + int len; + unsigned long offs = dmap->user_counter % dmap->bytes_in_use; + + len = dmap->fragment_size - l; + memset(dmap->raw_buf + offs, dmap->neutral_byte, len); + DMAbuf_move_wrpointer(dev, len); + } + + /* + * Clean all unused buffer fragments. + */ + + p = dmap->qtail; + dmap->flags |= DMA_POST; + + for (i = dmap->qlen + 1; i < dmap->nbufs; i++) + { + p = (p + 1) % dmap->nbufs; + if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) > + (dmap->raw_buf + dmap->buffsize)) + printk(KERN_ERR "audio: Buffer error 2\n"); + + memset(dmap->raw_buf + p * dmap->fragment_size, + dmap->neutral_byte, + dmap->fragment_size); + } + + dmap->flags |= DMA_DIRTY; +} + +void audio_release(int dev, struct file *file) +{ + const struct coproc_operations *coprocessor; + int mode = translate_mode(file); + + dev = dev >> 4; + + /* + * We do this in DMAbuf_release(). Why are we doing it + * here? Why don't we test the file mode before setting + * both flags? DMAbuf_release() does. + * ...pester...pester...pester... + */ + audio_devs[dev]->dmap_out->closing = 1; + audio_devs[dev]->dmap_in->closing = 1; + + /* + * We need to make sure we allocated the dmap_out buffer + * before we go mucking around with it in sync_output(). + */ + if (mode & OPEN_WRITE) + sync_output(dev); + + if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) { + coprocessor->close(coprocessor->devc, COPR_PCM); + + if (coprocessor->owner) + __MOD_DEC_USE_COUNT(coprocessor->owner); + } + DMAbuf_release(dev, mode); + + if (audio_devs[dev]->d->owner) + __MOD_DEC_USE_COUNT (audio_devs[dev]->d->owner); +} + +static void translate_bytes(const unsigned char *table, unsigned char *buff, int n) +{ + unsigned long i; + + if (n <= 0) + return; + + for (i = 0; i < n; ++i) + buff[i] = table[buff[i]]; +} + +int audio_write(int dev, struct file *file, const char *buf, int count) +{ + int c, p, l, buf_size, used, returned; + int err; + char *dma_buf; + + dev = dev >> 4; + + p = 0; + c = count; + + if(count < 0) + return -EINVAL; + + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EPERM; + + if (audio_devs[dev]->flags & DMA_DUPLEX) + audio_devs[dev]->audio_mode |= AM_WRITE; + else + audio_devs[dev]->audio_mode = AM_WRITE; + + if (!count) /* Flush output */ + { + sync_output(dev); + return 0; + } + + while (c) + { + if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0) + { + /* Handle nonblocking mode */ + if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN) + return p? p : -EAGAIN; /* No more space. Return # of accepted bytes */ + return err; + } + l = c; + + if (l > buf_size) + l = buf_size; + + returned = l; + used = l; + if (!audio_devs[dev]->d->copy_user) + { + if ((dma_buf + l) > + (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize)) + { + printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize); + return -EDOM; + } + if (dma_buf < audio_devs[dev]->dmap_out->raw_buf) + { + printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf); + return -EDOM; + } + if(copy_from_user(dma_buf, &(buf)[p], l)) + return -EFAULT; + } + else audio_devs[dev]->d->copy_user (dev, + dma_buf, 0, + buf, p, + c, buf_size, + &used, &returned, + l); + l = returned; + + if (audio_devs[dev]->local_conversion & CNV_MU_LAW) + { + /* + * This just allows interrupts while the conversion is running + */ + sti(); + translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l); + } + c -= used; + p += used; + DMAbuf_move_wrpointer(dev, l); + + } + + return count; +} + +int audio_read(int dev, struct file *file, char *buf, int count) +{ + int c, p, l; + char *dmabuf; + int buf_no; + + dev = dev >> 4; + p = 0; + c = count; + + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return -EPERM; + + if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) + sync_output(dev); + + if (audio_devs[dev]->flags & DMA_DUPLEX) + audio_devs[dev]->audio_mode |= AM_READ; + else + audio_devs[dev]->audio_mode = AM_READ; + + while(c) + { + if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0) + { + /* + * Nonblocking mode handling. Return current # of bytes + */ + + if (p > 0) /* Avoid throwing away data */ + return p; /* Return it instead */ + + if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN) + return -EAGAIN; + + return buf_no; + } + if (l > c) + l = c; + + /* + * Insert any local processing here. + */ + + if (audio_devs[dev]->local_conversion & CNV_MU_LAW) + { + /* + * This just allows interrupts while the conversion is running + */ + sti(); + + translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l); + } + + { + char *fixit = dmabuf; + + if(copy_to_user(&(buf)[p], fixit, l)) + return -EFAULT; + }; + + DMAbuf_rmchars(dev, buf_no, l); + + p += l; + c -= l; + } + + return count - c; +} + +int audio_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg) +{ + int val, count; + unsigned long flags; + struct dma_buffparms *dmap; + + dev = dev >> 4; + + if (_IOC_TYPE(cmd) == 'C') { + if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ + return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0); + /* else + printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */ + return -ENXIO; + } + else switch (cmd) + { + case SNDCTL_DSP_SYNC: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return 0; + if (audio_devs[dev]->dmap_out->fragment_size == 0) + return 0; + sync_output(dev); + DMAbuf_sync(dev); + DMAbuf_reset(dev); + return 0; + + case SNDCTL_DSP_POST: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return 0; + if (audio_devs[dev]->dmap_out->fragment_size == 0) + return 0; + audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY; + sync_output(dev); + dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0); + return 0; + + case SNDCTL_DSP_RESET: + audio_devs[dev]->audio_mode = AM_NONE; + DMAbuf_reset(dev); + return 0; + + case SNDCTL_DSP_GETFMTS: + val = audio_devs[dev]->format_mask | AFMT_MU_LAW; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = set_format(dev, val); + break; + + case SNDCTL_DSP_GETISPACE: + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return 0; + if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) + return -EBUSY; + return dma_ioctl(dev, cmd, arg); + + case SNDCTL_DSP_GETOSPACE: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EPERM; + if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) + return -EBUSY; + return dma_ioctl(dev, cmd, arg); + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + val = 1 | DSP_CAP_MMAP; /* Revision level of this ioctl() */ + if (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode == OPEN_READWRITE) + val |= DSP_CAP_DUPLEX; + if (audio_devs[dev]->coproc) + val |= DSP_CAP_COPROC; + if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ + val |= DSP_CAP_BATCH; + if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */ + val |= DSP_CAP_TRIGGER; + break; + + case SOUND_PCM_WRITE_RATE: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = audio_devs[dev]->d->set_speed(dev, val); + break; + + case SOUND_PCM_READ_RATE: + val = audio_devs[dev]->d->set_speed(dev, 0); + break; + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val > 1 || val < 0) + return -EINVAL; + val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = audio_devs[dev]->d->set_channels(dev, val); + break; + + case SOUND_PCM_READ_CHANNELS: + val = audio_devs[dev]->d->set_channels(dev, 0); + break; + + case SOUND_PCM_READ_BITS: + val = audio_devs[dev]->d->set_bits(dev, 0); + break; + + case SNDCTL_DSP_SETDUPLEX: + if (audio_devs[dev]->open_mode != OPEN_READWRITE) + return -EPERM; + return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO; + + case SNDCTL_DSP_PROFILE: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + audio_devs[dev]->dmap_out->applic_profile = val; + if (audio_devs[dev]->open_mode & OPEN_READ) + audio_devs[dev]->dmap_in->applic_profile = val; + return 0; + + case SNDCTL_DSP_GETODELAY: + dmap = audio_devs[dev]->dmap_out; + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + if (!(dmap->flags & DMA_ALLOC_DONE)) + { + val=0; + break; + } + + save_flags (flags); + cli(); + /* Compute number of bytes that have been played */ + count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT); + if (count < dmap->fragment_size && dmap->qhead != 0) + count += dmap->bytes_in_use; /* Pointer wrap not handled yet */ + count += dmap->byte_counter; + + /* Substract current count from the number of bytes written by app */ + count = dmap->user_counter - count; + if (count < 0) + count = 0; + restore_flags (flags); + val = count; + break; + + default: + return dma_ioctl(dev, cmd, arg); + } + return put_user(val, (int *)arg); +} + +void audio_init_devices(void) +{ + /* + * NOTE! This routine could be called several times during boot. + */ +} + +void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording) +{ + /* + * This routine breaks the physical device buffers to logical ones. + */ + + struct audio_operations *dsp_dev = audio_devs[dev]; + + unsigned i, n; + unsigned sr, nc, sz, bsz; + + sr = dsp_dev->d->set_speed(dev, 0); + nc = dsp_dev->d->set_channels(dev, 0); + sz = dsp_dev->d->set_bits(dev, 0); + + if (sz == 8) + dmap->neutral_byte = NEUTRAL8; + else + dmap->neutral_byte = NEUTRAL16; + + if (sr < 1 || nc < 1 || sz < 1) + { +/* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/ + sr = DSP_DEFAULT_SPEED; + nc = 1; + sz = 8; + } + + sz = sr * nc * sz; + + sz /= 8; /* #bits -> #bytes */ + dmap->data_rate = sz; + + if (!dmap->needs_reorg) + return; + dmap->needs_reorg = 0; + + if (dmap->fragment_size == 0) + { + /* Compute the fragment size using the default algorithm */ + + /* + * Compute a buffer size for time not exceeding 1 second. + * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds + * of sound (using the current speed, sample size and #channels). + */ + + bsz = dmap->buffsize; + while (bsz > sz) + bsz /= 2; + + if (bsz == dmap->buffsize) + bsz /= 2; /* Needs at least 2 buffers */ + + /* + * Split the computed fragment to smaller parts. After 3.5a9 + * the default subdivision is 4 which should give better + * results when recording. + */ + + if (dmap->subdivision == 0) /* Not already set */ + { + dmap->subdivision = 4; /* Init to the default value */ + + if ((bsz / dmap->subdivision) > 4096) + dmap->subdivision *= 2; + if ((bsz / dmap->subdivision) < 4096) + dmap->subdivision = 1; + } + bsz /= dmap->subdivision; + + if (bsz < 16) + bsz = 16; /* Just a sanity check */ + + dmap->fragment_size = bsz; + } + else + { + /* + * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or + * the buffer size computation has already been done. + */ + if (dmap->fragment_size > (dmap->buffsize / 2)) + dmap->fragment_size = (dmap->buffsize / 2); + bsz = dmap->fragment_size; + } + + if (audio_devs[dev]->min_fragment) + if (bsz < (1 << audio_devs[dev]->min_fragment)) + bsz = 1 << audio_devs[dev]->min_fragment; + if (audio_devs[dev]->max_fragment) + if (bsz > (1 << audio_devs[dev]->max_fragment)) + bsz = 1 << audio_devs[dev]->max_fragment; + bsz &= ~0x07; /* Force size which is multiple of 8 bytes */ +#ifdef OS_DMA_ALIGN_CHECK + OS_DMA_ALIGN_CHECK(bsz); +#endif + + n = dmap->buffsize / bsz; + if (n > MAX_SUB_BUFFERS) + n = MAX_SUB_BUFFERS; + if (n > dmap->max_fragments) + n = dmap->max_fragments; + + if (n < 2) + { + n = 2; + bsz /= 2; + } + dmap->nbufs = n; + dmap->bytes_in_use = n * bsz; + dmap->fragment_size = bsz; + dmap->max_byte_counter = (dmap->data_rate * 60 * 60) + + dmap->bytes_in_use; /* Approximately one hour */ + + if (dmap->raw_buf) + { + memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use); + } + + for (i = 0; i < dmap->nbufs; i++) + { + dmap->counts[i] = 0; + } + + dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY; +} + +static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact) +{ + if (fact == 0) + { + fact = dmap->subdivision; + if (fact == 0) + fact = 1; + return fact; + } + if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */ + return -EINVAL; + + if (fact > MAX_REALTIME_FACTOR) + return -EINVAL; + + if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) + return -EINVAL; + + dmap->subdivision = fact; + return fact; +} + +static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact) +{ + int bytes, count; + + if (fact == 0) + return -EIO; + + if (dmap->subdivision != 0 || + dmap->fragment_size) /* Too late to change */ + return -EINVAL; + + bytes = fact & 0xffff; + count = (fact >> 16) & 0x7fff; + + if (count == 0) + count = MAX_SUB_BUFFERS; + else if (count < MAX_SUB_BUFFERS) + count++; + + if (bytes < 4 || bytes > 17) /* <16 || > 512k */ + return -EINVAL; + + if (count < 2) + return -EINVAL; + + if (audio_devs[dev]->min_fragment > 0) + if (bytes < audio_devs[dev]->min_fragment) + bytes = audio_devs[dev]->min_fragment; + + if (audio_devs[dev]->max_fragment > 0) + if (bytes > audio_devs[dev]->max_fragment) + bytes = audio_devs[dev]->max_fragment; + +#ifdef OS_DMA_MINBITS + if (bytes < OS_DMA_MINBITS) + bytes = OS_DMA_MINBITS; +#endif + + dmap->fragment_size = (1 << bytes); + dmap->max_fragments = count; + + if (dmap->fragment_size > dmap->buffsize) + dmap->fragment_size = dmap->buffsize; + + if (dmap->fragment_size == dmap->buffsize && + audio_devs[dev]->flags & DMA_AUTOMODE) + dmap->fragment_size /= 2; /* Needs at least 2 buffers */ + + dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ + return bytes | ((count - 1) << 16); +} + +int dma_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out; + struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in; + struct dma_buffparms *dmap; + audio_buf_info info; + count_info cinfo; + int fact, ret, changed, bits, count, err; + unsigned long flags; + + switch (cmd) + { + case SNDCTL_DSP_SUBDIVIDE: + ret = 0; + if (get_user(fact, (int *)arg)) + return -EFAULT; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + ret = dma_subdivide(dev, dmap_out, fact); + if (ret < 0) + return ret; + if (audio_devs[dev]->open_mode != OPEN_WRITE || + (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode & OPEN_READ)) + ret = dma_subdivide(dev, dmap_in, fact); + if (ret < 0) + return ret; + break; + + case SNDCTL_DSP_GETISPACE: + case SNDCTL_DSP_GETOSPACE: + dmap = dmap_out; + if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ)) + return -EINVAL; + if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX) + dmap = dmap_in; + if (dmap->mapping_flags & DMA_MAP_MAPPED) + return -EINVAL; + if (!(dmap->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE)); + info.fragstotal = dmap->nbufs; + if (cmd == SNDCTL_DSP_GETISPACE) + info.fragments = dmap->qlen; + else + { + if (!DMAbuf_space_in_queue(dev)) + info.fragments = 0; + else + { + info.fragments = DMAbuf_space_in_queue(dev); + if (audio_devs[dev]->d->local_qlen) + { + int tmp = audio_devs[dev]->d->local_qlen(dev); + if (tmp && info.fragments) + tmp--; /* + * This buffer has been counted twice + */ + info.fragments -= tmp; + } + } + } + if (info.fragments < 0) + info.fragments = 0; + else if (info.fragments > dmap->nbufs) + info.fragments = dmap->nbufs; + + info.fragsize = dmap->fragment_size; + info.bytes = info.fragments * dmap->fragment_size; + + if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen) + info.bytes -= dmap->counts[dmap->qhead]; + else + { + info.fragments = info.bytes / dmap->fragment_size; + info.bytes -= dmap->user_counter % dmap->fragment_size; + } + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(bits, (int *)arg)) + return -EFAULT; + bits &= audio_devs[dev]->open_mode; + if (audio_devs[dev]->d->trigger == NULL) + return -EINVAL; + if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) && + (bits & PCM_ENABLE_OUTPUT)) + return -EINVAL; + save_flags(flags); + cli(); + changed = audio_devs[dev]->enable_bits ^ bits; + if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) + { + reorganize_buffers(dev, dmap_in, 1); + if ((err = audio_devs[dev]->d->prepare_for_input(dev, + dmap_in->fragment_size, dmap_in->nbufs)) < 0) { + restore_flags(flags); + return -err; + } + dmap_in->dma_mode = DMODE_INPUT; + audio_devs[dev]->enable_bits = bits; + DMAbuf_activate_recording(dev, dmap_in); + } + if ((changed & bits) & PCM_ENABLE_OUTPUT && + (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) && + audio_devs[dev]->go) + { + if (!(dmap_out->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap_out, 0); + dmap_out->dma_mode = DMODE_OUTPUT; + audio_devs[dev]->enable_bits = bits; + dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; + DMAbuf_launch_output(dev, dmap_out); + } + audio_devs[dev]->enable_bits = bits; +#if 0 + if (changed && audio_devs[dev]->d->trigger) + audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go); +#endif + restore_flags(flags); + /* Falls through... */ + + case SNDCTL_DSP_GETTRIGGER: + ret = audio_devs[dev]->enable_bits; + break; + + case SNDCTL_DSP_SETSYNCRO: + if (!audio_devs[dev]->d->trigger) + return -EINVAL; + audio_devs[dev]->d->trigger(dev, 0); + audio_devs[dev]->go = 0; + return 0; + + case SNDCTL_DSP_GETIPTR: + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return -EINVAL; + save_flags(flags); + cli(); + cinfo.bytes = dmap_in->byte_counter; + cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3; + if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0) + cinfo.bytes += dmap_in->bytes_in_use; /* Pointer wrap not handled yet */ + cinfo.blocks = dmap_in->qlen; + cinfo.bytes += cinfo.ptr; + if (dmap_in->mapping_flags & DMA_MAP_MAPPED) + dmap_in->qlen = 0; /* Reset interrupt counter */ + restore_flags(flags); + if (copy_to_user(arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETOPTR: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + + save_flags(flags); + cli(); + cinfo.bytes = dmap_out->byte_counter; + cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3; + if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0) + cinfo.bytes += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */ + cinfo.blocks = dmap_out->qlen; + cinfo.bytes += cinfo.ptr; + if (dmap_out->mapping_flags & DMA_MAP_MAPPED) + dmap_out->qlen = 0; /* Reset interrupt counter */ + restore_flags(flags); + if (copy_to_user(arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + if (!(dmap_out->flags & DMA_ALLOC_DONE)) + { + ret=0; + break; + } + save_flags(flags); + cli(); + /* Compute number of bytes that have been played */ + count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT); + if (count < dmap_out->fragment_size && dmap_out->qhead != 0) + count += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */ + count += dmap_out->byte_counter; + /* Substract current count from the number of bytes written by app */ + count = dmap_out->user_counter - count; + if (count < 0) + count = 0; + restore_flags (flags); + ret = count; + break; + + case SNDCTL_DSP_POST: + if (audio_devs[dev]->dmap_out->qlen > 0) + if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE)) + DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out); + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + dmap = dmap_out; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ)); + if (audio_devs[dev]->open_mode == OPEN_READ || + (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode & OPEN_READ)) + reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ)); + if (audio_devs[dev]->open_mode == OPEN_READ) + dmap = dmap_in; + ret = dmap->fragment_size; + break; + + case SNDCTL_DSP_SETFRAGMENT: + ret = 0; + if (get_user(fact, (int *)arg)) + return -EFAULT; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + ret = dma_set_fragment(dev, dmap_out, fact); + if (ret < 0) + return ret; + if (audio_devs[dev]->open_mode == OPEN_READ || + (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode & OPEN_READ)) + ret = dma_set_fragment(dev, dmap_in, fact); + if (ret < 0) + return ret; + if (!arg) /* don't know what this is good for, but preserve old semantics */ + return 0; + break; + + default: + if (!audio_devs[dev]->d->ioctl) + return -EINVAL; + return audio_devs[dev]->d->ioctl(dev, cmd, arg); + } + return put_user(ret, (int *)arg); +} diff -Nru a/sound/oss/audio_syms.c b/sound/oss/audio_syms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/audio_syms.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,21 @@ +/* + * Exported symbols for audio driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include + +char audio_syms_symbol; + +#include "sound_config.h" +#include "sound_calls.h" + +EXPORT_SYMBOL(DMAbuf_start_dma); +EXPORT_SYMBOL(DMAbuf_open_dma); +EXPORT_SYMBOL(DMAbuf_close_dma); +EXPORT_SYMBOL(DMAbuf_inputintr); +EXPORT_SYMBOL(DMAbuf_outputintr); +EXPORT_SYMBOL(dma_ioctl); +EXPORT_SYMBOL(audio_open); +EXPORT_SYMBOL(audio_release); diff -Nru a/sound/oss/awe_hw.h b/sound/oss/awe_hw.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/awe_hw.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,99 @@ +/* + * sound/awe_hw.h + * + * Access routines and definitions for the low level driver for the + * Creative AWE32/SB32/AWE64 wave table synth. + * version 0.4.4; Jan. 4, 2000 + * + * Copyright (C) 1996-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef AWE_HW_H_DEF +#define AWE_HW_H_DEF + +/* + * Emu-8000 control registers + * name(channel) reg, port + */ + +#define awe_cmd_idx(reg,ch) (((reg)<< 5) | (ch)) + +#define Data0 0 /* 0x620: doubleword r/w */ +#define Data1 1 /* 0xA20: doubleword r/w */ +#define Data2 2 /* 0xA22: word r/w */ +#define Data3 3 /* 0xE20: word r/w */ +#define Pointer 4 /* 0xE22 register pointer r/w */ + +#define AWE_CPF(ch) awe_cmd_idx(0,ch), Data0 /* DW: current pitch and fractional address */ +#define AWE_PTRX(ch) awe_cmd_idx(1,ch), Data0 /* DW: pitch target and reverb send */ +#define AWE_CVCF(ch) awe_cmd_idx(2,ch), Data0 /* DW: current volume and filter cutoff */ +#define AWE_VTFT(ch) awe_cmd_idx(3,ch), Data0 /* DW: volume and filter cutoff targets */ +#define AWE_0080(ch) awe_cmd_idx(4,ch), Data0 /* DW: ?? */ +#define AWE_00A0(ch) awe_cmd_idx(5,ch), Data0 /* DW: ?? */ +#define AWE_PSST(ch) awe_cmd_idx(6,ch), Data0 /* DW: pan send and loop start address */ +#define AWE_CSL(ch) awe_cmd_idx(7,ch), Data0 /* DW: chorus send and loop end address */ +#define AWE_CCCA(ch) awe_cmd_idx(0,ch), Data1 /* DW: Q, control bits, and current address */ +#define AWE_HWCF4 awe_cmd_idx(1,9), Data1 /* DW: config dw 4 */ +#define AWE_HWCF5 awe_cmd_idx(1,10), Data1 /* DW: config dw 5 */ +#define AWE_HWCF6 awe_cmd_idx(1,13), Data1 /* DW: config dw 6 */ +#define AWE_HWCF7 awe_cmd_idx(1,14), Data1 /* DW: config dw 7? (not documented) */ +#define AWE_SMALR awe_cmd_idx(1,20), Data1 /* DW: sound memory address for left read */ +#define AWE_SMARR awe_cmd_idx(1,21), Data1 /* DW: for right read */ +#define AWE_SMALW awe_cmd_idx(1,22), Data1 /* DW: sound memory address for left write */ +#define AWE_SMARW awe_cmd_idx(1,23), Data1 /* DW: for right write */ +#define AWE_SMLD awe_cmd_idx(1,26), Data1 /* W: sound memory left data */ +#define AWE_SMRD awe_cmd_idx(1,26), Data2 /* W: right data */ +#define AWE_WC awe_cmd_idx(1,27), Data2 /* W: sample counter */ +#define AWE_WC_Cmd awe_cmd_idx(1,27) +#define AWE_WC_Port Data2 +#define AWE_HWCF1 awe_cmd_idx(1,29), Data1 /* W: config w 1 */ +#define AWE_HWCF2 awe_cmd_idx(1,30), Data1 /* W: config w 2 */ +#define AWE_HWCF3 awe_cmd_idx(1,31), Data1 /* W: config w 3 */ +#define AWE_INIT1(ch) awe_cmd_idx(2,ch), Data1 /* W: init array 1 */ +#define AWE_INIT2(ch) awe_cmd_idx(2,ch), Data2 /* W: init array 2 */ +#define AWE_INIT3(ch) awe_cmd_idx(3,ch), Data1 /* W: init array 3 */ +#define AWE_INIT4(ch) awe_cmd_idx(3,ch), Data2 /* W: init array 4 */ +#define AWE_ENVVOL(ch) awe_cmd_idx(4,ch), Data1 /* W: volume envelope delay */ +#define AWE_DCYSUSV(ch) awe_cmd_idx(5,ch), Data1 /* W: volume envelope sustain and decay */ +#define AWE_ENVVAL(ch) awe_cmd_idx(6,ch), Data1 /* W: modulation envelope delay */ +#define AWE_DCYSUS(ch) awe_cmd_idx(7,ch), Data1 /* W: modulation envelope sustain and decay */ +#define AWE_ATKHLDV(ch) awe_cmd_idx(4,ch), Data2 /* W: volume envelope attack and hold */ +#define AWE_LFO1VAL(ch) awe_cmd_idx(5,ch), Data2 /* W: LFO#1 Delay */ +#define AWE_ATKHLD(ch) awe_cmd_idx(6,ch), Data2 /* W: modulation envelope attack and hold */ +#define AWE_LFO2VAL(ch) awe_cmd_idx(7,ch), Data2 /* W: LFO#2 Delay */ +#define AWE_IP(ch) awe_cmd_idx(0,ch), Data3 /* W: initial pitch */ +#define AWE_IFATN(ch) awe_cmd_idx(1,ch), Data3 /* W: initial filter cutoff and attenuation */ +#define AWE_PEFE(ch) awe_cmd_idx(2,ch), Data3 /* W: pitch and filter envelope heights */ +#define AWE_FMMOD(ch) awe_cmd_idx(3,ch), Data3 /* W: vibrato and filter modulation freq */ +#define AWE_TREMFRQ(ch) awe_cmd_idx(4,ch), Data3 /* W: LFO#1 tremolo amount and freq */ +#define AWE_FM2FRQ2(ch) awe_cmd_idx(5,ch), Data3 /* W: LFO#2 vibrato amount and freq */ + +/* used during detection (returns ROM version?; not documented in ADIP) */ +#define AWE_U1 0xE0, Data3 /* (R)(W) used in initialization */ +#define AWE_U2(ch) 0xC0+(ch), Data3 /* (W)(W) used in init envelope */ + + +#define AWE_MAX_VOICES 32 +#define AWE_NORMAL_VOICES 30 /*30&31 are reserved for DRAM refresh*/ + +#define AWE_MAX_CHANNELS 32 /* max midi channels (must >= voices) */ +#define AWE_MAX_LAYERS AWE_MAX_VOICES /* maximum number of multiple layers */ + +#define AWE_DRAM_OFFSET 0x200000 +#define AWE_MAX_DRAM_SIZE (28 * 1024) /* 28 MB is max onboard memory */ + +#endif diff -Nru a/sound/oss/awe_wave.c b/sound/oss/awe_wave.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/awe_wave.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,6159 @@ +/* + * sound/awe_wave.c + * + * The low level driver for the AWE32/SB32/AWE64 wave table synth. + * version 0.4.4; Jan. 4, 2000 + * + * Copyright (C) 1996-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "sound_config.h" + +#include "awe_wave.h" +#include "awe_hw.h" + +#ifdef AWE_HAS_GUS_COMPATIBILITY +#include "tuning.h" +#include +#endif + +/* + * debug message + */ + +#ifdef AWE_DEBUG_ON +#define DEBUG(LVL,XXX) {if (ctrls[AWE_MD_DEBUG_MODE] > LVL) { XXX; }} +#define ERRMSG(XXX) {if (ctrls[AWE_MD_DEBUG_MODE]) { XXX; }} +#define FATALERR(XXX) XXX +#else +#define DEBUG(LVL,XXX) /**/ +#define ERRMSG(XXX) XXX +#define FATALERR(XXX) XXX +#endif + +/* + * bank and voice record + */ + +typedef struct _sf_list sf_list; +typedef struct _awe_voice_list awe_voice_list; +typedef struct _awe_sample_list awe_sample_list; + +/* soundfont record */ +struct _sf_list { + unsigned short sf_id; /* id number */ + unsigned short type; /* lock & shared flags */ + int num_info; /* current info table index */ + int num_sample; /* current sample table index */ + int mem_ptr; /* current word byte pointer */ + awe_voice_list *infos, *last_infos; /* instruments */ + awe_sample_list *samples, *last_samples; /* samples */ +#ifdef AWE_ALLOW_SAMPLE_SHARING + sf_list *shared; /* shared list */ + unsigned char name[AWE_PATCH_NAME_LEN]; /* sharing id */ +#endif + sf_list *next, *prev; +}; + +/* instrument list */ +struct _awe_voice_list { + awe_voice_info v; /* instrument information */ + sf_list *holder; /* parent sf_list of this record */ + unsigned char bank, instr; /* preset number information */ + char type, disabled; /* type=normal/mapped, disabled=boolean */ + awe_voice_list *next; /* linked list with same sf_id */ + awe_voice_list *next_instr; /* instrument list */ + awe_voice_list *next_bank; /* hash table list */ +}; + +/* voice list type */ +#define V_ST_NORMAL 0 +#define V_ST_MAPPED 1 + +/* sample list */ +struct _awe_sample_list { + awe_sample_info v; /* sample information */ + sf_list *holder; /* parent sf_list of this record */ + awe_sample_list *next; /* linked list with same sf_id */ +}; + +/* sample and information table */ +static int current_sf_id = 0; /* current number of fonts */ +static int locked_sf_id = 0; /* locked position */ +static sf_list *sfhead = NULL, *sftail = NULL; /* linked-lists */ + +#define awe_free_mem_ptr() (sftail ? sftail->mem_ptr : 0) +#define awe_free_info() (sftail ? sftail->num_info : 0) +#define awe_free_sample() (sftail ? sftail->num_sample : 0) + +#define AWE_MAX_PRESETS 256 +#define AWE_DEFAULT_PRESET 0 +#define AWE_DEFAULT_BANK 0 +#define AWE_DEFAULT_DRUM 0 +#define AWE_DRUM_BANK 128 + +#define MAX_LAYERS AWE_MAX_VOICES + +/* preset table index */ +static awe_voice_list *preset_table[AWE_MAX_PRESETS]; + +/* + * voice table + */ + +/* effects table */ +typedef struct FX_Rec { /* channel effects */ + unsigned char flags[AWE_FX_END]; + short val[AWE_FX_END]; +} FX_Rec; + + +/* channel parameters */ +typedef struct _awe_chan_info { + int channel; /* channel number */ + int bank; /* current tone bank */ + int instr; /* current program */ + int bender; /* midi pitchbend (-8192 - 8192) */ + int bender_range; /* midi bender range (x100) */ + int panning; /* panning (0-127) */ + int main_vol; /* channel volume (0-127) */ + int expression_vol; /* midi expression (0-127) */ + int chan_press; /* channel pressure */ + int sustained; /* sustain status in MIDI */ + FX_Rec fx; /* effects */ + FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */ +} awe_chan_info; + +/* voice parameters */ +typedef struct _voice_info { + int state; +#define AWE_ST_OFF (1<<0) /* no sound */ +#define AWE_ST_ON (1<<1) /* playing */ +#define AWE_ST_STANDBY (1<<2) /* stand by for playing */ +#define AWE_ST_SUSTAINED (1<<3) /* sustained */ +#define AWE_ST_MARK (1<<4) /* marked for allocation */ +#define AWE_ST_DRAM (1<<5) /* DRAM read/write */ +#define AWE_ST_FM (1<<6) /* reserved for FM */ +#define AWE_ST_RELEASED (1<<7) /* released */ + + int ch; /* midi channel */ + int key; /* internal key for search */ + int layer; /* layer number (for channel mode only) */ + int time; /* allocated time */ + awe_chan_info *cinfo; /* channel info */ + + int note; /* midi key (0-127) */ + int velocity; /* midi velocity (0-127) */ + int sostenuto; /* sostenuto on/off */ + awe_voice_info *sample; /* assigned voice */ + + /* EMU8000 parameters */ + int apitch; /* pitch parameter */ + int avol; /* volume parameter */ + int apan; /* panning parameter */ + int acutoff; /* cutoff parameter */ + short aaux; /* aux word */ +} voice_info; + +/* voice information */ +static voice_info voices[AWE_MAX_VOICES]; + +#define IS_NO_SOUND(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED)) +#define IS_NO_EFFECT(v) (voices[v].state != AWE_ST_ON) +#define IS_PLAYING(v) (voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED)) +#define IS_EMPTY(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_MARK|AWE_ST_DRAM|AWE_ST_FM)) + + +/* MIDI channel effects information (for hw control) */ +static awe_chan_info channels[AWE_MAX_CHANNELS]; + + +/* + * global variables + */ + +#ifndef AWE_DEFAULT_BASE_ADDR +#define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */ +#endif + +#ifndef AWE_DEFAULT_MEM_SIZE +#define AWE_DEFAULT_MEM_SIZE -1 /* autodetect */ +#endif + +int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */ +int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */ +#ifdef __ISAPNP__ +static int isapnp = -1; +#else +static int isapnp = 0; +#endif + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("SB AWE32/64 WaveTable driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "base i/o port of Emu8000"); +MODULE_PARM(memsize, "i"); +MODULE_PARM_DESC(memsize, "onboard DRAM size in Kbytes"); +MODULE_PARM(isapnp, "i"); +MODULE_PARM_DESC(isapnp, "use ISAPnP detection"); +EXPORT_NO_SYMBOLS; + +/* DRAM start offset */ +static int awe_mem_start = AWE_DRAM_OFFSET; + +/* maximum channels for playing */ +static int awe_max_voices = AWE_MAX_VOICES; + +static int patch_opened = 0; /* sample already loaded? */ + +static char atten_relative = FALSE; +static short atten_offset = 0; + +static int awe_present = FALSE; /* awe device present? */ +static int awe_busy = FALSE; /* awe device opened? */ + +static int my_dev = -1; + +#define DEFAULT_DRUM_FLAGS ((1 << 9) | (1 << 25)) +#define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c))) +#define DRUM_CHANNEL_ON(c) (drum_flags |= (1 << (c))) +#define DRUM_CHANNEL_OFF(c) (drum_flags &= ~(1 << (c))) +static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */ + +static int playing_mode = AWE_PLAY_INDIRECT; +#define SINGLE_LAYER_MODE() (playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT) +#define MULTI_LAYER_MODE() (playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2) + +static int current_alloc_time = 0; /* voice allocation index for channel mode */ + +static struct synth_info awe_info = { + "AWE32 Synth", /* name */ + 0, /* device */ + SYNTH_TYPE_SAMPLE, /* synth_type */ + SAMPLE_TYPE_AWE32, /* synth_subtype */ + 0, /* perc_mode (obsolete) */ + AWE_MAX_VOICES, /* nr_voices */ + 0, /* nr_drums (obsolete) */ + 400 /* instr_bank_size */ +}; + + +static struct voice_alloc_info *voice_alloc; /* set at initialization */ + + +/* + * function prototypes + */ + +static int awe_check_port(void); +static void awe_request_region(void); +static void awe_release_region(void); + +static void awe_reset_samples(void); +/* emu8000 chip i/o access */ +static void setup_ports(int p1, int p2, int p3); +static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data); +static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data); +static unsigned short awe_peek(unsigned short cmd, unsigned short port); +static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port); +static void awe_wait(unsigned short delay); + +/* initialize emu8000 chip */ +static int _attach_awe(void); +static void _unload_awe(void); +static void awe_initialize(void); + +/* set voice parameters */ +static void awe_init_ctrl_parms(int init_all); +static void awe_init_voice_info(awe_voice_info *vp); +static void awe_init_voice_parm(awe_voice_parm *pp); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static int freq_to_note(int freq); +static int calc_rate_offset(int Hz); +/*static int calc_parm_delay(int msec);*/ +static int calc_parm_hold(int msec); +static int calc_parm_attack(int msec); +static int calc_parm_decay(int msec); +static int calc_parm_search(int msec, short *table); +#endif /* gus compat */ + +/* turn on/off note */ +static void awe_note_on(int voice); +static void awe_note_off(int voice); +static void awe_terminate(int voice); +static void awe_exclusive_off(int voice); +static void awe_note_off_all(int do_sustain); + +/* calculate voice parameters */ +typedef void (*fx_affect_func)(int voice, int forced); +static void awe_set_pitch(int voice, int forced); +static void awe_set_voice_pitch(int voice, int forced); +static void awe_set_volume(int voice, int forced); +static void awe_set_voice_vol(int voice, int forced); +static void awe_set_pan(int voice, int forced); +static void awe_fx_fmmod(int voice, int forced); +static void awe_fx_tremfrq(int voice, int forced); +static void awe_fx_fm2frq2(int voice, int forced); +static void awe_fx_filterQ(int voice, int forced); +static void awe_calc_pitch(int voice); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static void awe_calc_pitch_from_freq(int voice, int freq); +#endif +static void awe_calc_volume(int voice); +static void awe_update_volume(void); +static void awe_change_master_volume(short val); +static void awe_voice_init(int voice, int init_all); +static void awe_channel_init(int ch, int init_all); +static void awe_fx_init(int ch); +static void awe_send_effect(int voice, int layer, int type, int val); +static void awe_modwheel_change(int voice, int value); + +/* sequencer interface */ +static int awe_open(int dev, int mode); +static void awe_close(int dev); +static int awe_ioctl(int dev, unsigned int cmd, caddr_t arg); +static int awe_kill_note(int dev, int voice, int note, int velocity); +static int awe_start_note(int dev, int v, int note_num, int volume); +static int awe_set_instr(int dev, int voice, int instr_no); +static int awe_set_instr_2(int dev, int voice, int instr_no); +static void awe_reset(int dev); +static void awe_hw_control(int dev, unsigned char *event); +static int awe_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag); +static void awe_aftertouch(int dev, int voice, int pressure); +static void awe_controller(int dev, int voice, int ctrl_num, int value); +static void awe_panning(int dev, int voice, int value); +static void awe_volume_method(int dev, int mode); +static void awe_bender(int dev, int voice, int value); +static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc); +static void awe_setup_voice(int dev, int voice, int chn); + +#define awe_key_pressure(dev,voice,key,press) awe_start_note(dev,voice,(key)+128,press) + +/* hardware controls */ +#ifdef AWE_HAS_GUS_COMPATIBILITY +static void awe_hw_gus_control(int dev, int cmd, unsigned char *event); +#endif +static void awe_hw_awe_control(int dev, int cmd, unsigned char *event); +static void awe_voice_change(int voice, fx_affect_func func); +static void awe_sostenuto_on(int voice, int forced); +static void awe_sustain_off(int voice, int forced); +static void awe_terminate_and_init(int voice, int forced); + +/* voice search */ +static int awe_search_key(int bank, int preset, int note); +static awe_voice_list *awe_search_instr(int bank, int preset, int note); +static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist); +static void awe_alloc_multi_voices(int ch, int note, int velocity, int key); +static void awe_alloc_one_voice(int voice, int note, int velocity); +static int awe_clear_voice(void); + +/* load / remove patches */ +static int awe_open_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_close_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_load_info(awe_patch_info *patch, const char *addr, int count); +static int awe_remove_info(awe_patch_info *patch, const char *addr, int count); +static int awe_load_data(awe_patch_info *patch, const char *addr, int count); +static int awe_replace_data(awe_patch_info *patch, const char *addr, int count); +static int awe_load_map(awe_patch_info *patch, const char *addr, int count); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag); +#endif +/*static int awe_probe_info(awe_patch_info *patch, const char *addr, int count);*/ +static int awe_probe_data(awe_patch_info *patch, const char *addr, int count); +static sf_list *check_patch_opened(int type, char *name); +static int awe_write_wave_data(const char *addr, int offset, awe_sample_list *sp, int channels); +static int awe_create_sf(int type, char *name); +static void awe_free_sf(sf_list *sf); +static void add_sf_info(sf_list *sf, awe_voice_list *rec); +static void add_sf_sample(sf_list *sf, awe_sample_list *smp); +static void purge_old_list(awe_voice_list *rec, awe_voice_list *next); +static void add_info_list(awe_voice_list *rec); +static void awe_remove_samples(int sf_id); +static void rebuild_preset_list(void); +static short awe_set_sample(awe_voice_list *rec); +static awe_sample_list *search_sample_index(sf_list *sf, int sample); + +static int is_identical_holder(sf_list *sf1, sf_list *sf2); +#ifdef AWE_ALLOW_SAMPLE_SHARING +static int is_identical_name(unsigned char *name, sf_list *p); +static int is_shared_sf(unsigned char *name); +static int info_duplicated(sf_list *sf, awe_voice_list *rec); +#endif /* allow sharing */ + +/* lowlevel functions */ +static void awe_init_audio(void); +static void awe_init_dma(void); +static void awe_init_array(void); +static void awe_send_array(unsigned short *data); +static void awe_tweak_voice(int voice); +static void awe_tweak(void); +static void awe_init_fm(void); +static int awe_open_dram_for_write(int offset, int channels); +static void awe_open_dram_for_check(void); +static void awe_close_dram(void); +/*static void awe_write_dram(unsigned short c);*/ +static int awe_detect_base(int addr); +static int awe_detect(void); +static void awe_check_dram(void); +static int awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count); +static void awe_set_chorus_mode(int mode); +static void awe_update_chorus_mode(void); +static int awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count); +static void awe_set_reverb_mode(int mode); +static void awe_update_reverb_mode(void); +static void awe_equalizer(int bass, int treble); +static void awe_update_equalizer(void); + +#ifdef CONFIG_AWE32_MIXER +static void attach_mixer(void); +static void unload_mixer(void); +#endif + +#ifdef CONFIG_AWE32_MIDIEMU +static void attach_midiemu(void); +static void unload_midiemu(void); +#endif + +#define limitvalue(x, a, b) if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b) + +/* + * control parameters + */ + + +#ifdef AWE_USE_NEW_VOLUME_CALC +#define DEF_VOLUME_CALC TRUE +#else +#define DEF_VOLUME_CALC FALSE +#endif /* new volume */ + +#define DEF_ZERO_ATTEN 32 /* 12dB below */ +#define DEF_MOD_SENSE 18 +#define DEF_CHORUS_MODE 2 +#define DEF_REVERB_MODE 4 +#define DEF_BASS_LEVEL 5 +#define DEF_TREBLE_LEVEL 9 + +static struct CtrlParmsDef { + int value; + int init_each_time; + void (*update)(void); +} ctrl_parms[AWE_MD_END] = { + {0,0, NULL}, {0,0, NULL}, /* <-- not used */ + {AWE_VERSION_NUMBER, FALSE, NULL}, + {TRUE, FALSE, NULL}, /* exclusive */ + {TRUE, FALSE, NULL}, /* realpan */ + {AWE_DEFAULT_BANK, FALSE, NULL}, /* gusbank */ + {FALSE, TRUE, NULL}, /* keep effect */ + {DEF_ZERO_ATTEN, FALSE, awe_update_volume}, /* zero_atten */ + {FALSE, FALSE, NULL}, /* chn_prior */ + {DEF_MOD_SENSE, FALSE, NULL}, /* modwheel sense */ + {AWE_DEFAULT_PRESET, FALSE, NULL}, /* def_preset */ + {AWE_DEFAULT_BANK, FALSE, NULL}, /* def_bank */ + {AWE_DEFAULT_DRUM, FALSE, NULL}, /* def_drum */ + {FALSE, FALSE, NULL}, /* toggle_drum_bank */ + {DEF_VOLUME_CALC, FALSE, awe_update_volume}, /* new_volume_calc */ + {DEF_CHORUS_MODE, FALSE, awe_update_chorus_mode}, /* chorus mode */ + {DEF_REVERB_MODE, FALSE, awe_update_reverb_mode}, /* reverb mode */ + {DEF_BASS_LEVEL, FALSE, awe_update_equalizer}, /* bass level */ + {DEF_TREBLE_LEVEL, FALSE, awe_update_equalizer}, /* treble level */ + {0, FALSE, NULL}, /* debug mode */ + {FALSE, FALSE, NULL}, /* pan exchange */ +}; + +static int ctrls[AWE_MD_END]; + + +/* + * synth operation table + */ + +static struct synth_operations awe_operations = +{ + owner: THIS_MODULE, + id: "EMU8K", + info: &awe_info, + midi_dev: 0, + synth_type: SYNTH_TYPE_SAMPLE, + synth_subtype: SAMPLE_TYPE_AWE32, + open: awe_open, + close: awe_close, + ioctl: awe_ioctl, + kill_note: awe_kill_note, + start_note: awe_start_note, + set_instr: awe_set_instr_2, + reset: awe_reset, + hw_control: awe_hw_control, + load_patch: awe_load_patch, + aftertouch: awe_aftertouch, + controller: awe_controller, + panning: awe_panning, + volume_method: awe_volume_method, + bender: awe_bender, + alloc_voice: awe_alloc, + setup_voice: awe_setup_voice +}; + + +/* + * General attach / unload interface + */ + +static int __init _attach_awe(void) +{ + if (awe_present) return 0; /* for OSS38.. called twice? */ + + /* check presence of AWE32 card */ + if (! awe_detect()) { + printk(KERN_ERR "AWE32: not detected\n"); + return 0; + } + + /* check AWE32 ports are available */ + if (awe_check_port()) { + printk(KERN_ERR "AWE32: I/O area already used.\n"); + return 0; + } + + /* set buffers to NULL */ + sfhead = sftail = NULL; + + my_dev = sound_alloc_synthdev(); + if (my_dev == -1) { + printk(KERN_ERR "AWE32 Error: too many synthesizers\n"); + return 0; + } + + voice_alloc = &awe_operations.alloc; + voice_alloc->max_voice = awe_max_voices; + synth_devs[my_dev] = &awe_operations; + +#ifdef CONFIG_AWE32_MIXER + attach_mixer(); +#endif +#ifdef CONFIG_AWE32_MIDIEMU + attach_midiemu(); +#endif + + /* reserve I/O ports for awedrv */ + awe_request_region(); + + /* clear all samples */ + awe_reset_samples(); + + /* intialize AWE32 hardware */ + awe_initialize(); + + sprintf(awe_info.name, "AWE32-%s (RAM%dk)", + AWEDRV_VERSION, memsize/1024); + printk(KERN_INFO "\n", memsize/1024); + + awe_present = TRUE; + + return 1; +} + + +static void free_tables(void) +{ + if (sftail) { + sf_list *p, *prev; + for (p = sftail; p; p = prev) { + prev = p->prev; + awe_free_sf(p); + } + } + sfhead = sftail = NULL; +} + + +static void __exit _unload_awe(void) +{ + if (awe_present) { + awe_reset_samples(); + awe_release_region(); + free_tables(); +#ifdef CONFIG_AWE32_MIXER + unload_mixer(); +#endif +#ifdef CONFIG_AWE32_MIDIEMU + unload_midiemu(); +#endif + sound_unload_synthdev(my_dev); + awe_present = FALSE; + } +} + + +/* + * clear sample tables + */ + +static void +awe_reset_samples(void) +{ + /* free all bank tables */ + memset(preset_table, 0, sizeof(preset_table)); + free_tables(); + + current_sf_id = 0; + locked_sf_id = 0; + patch_opened = 0; +} + + +/* + * EMU register access + */ + +/* select a given AWE32 pointer */ +static int awe_ports[5]; +static int port_setuped = FALSE; +static int awe_cur_cmd = -1; +#define awe_set_cmd(cmd) \ +if (awe_cur_cmd != cmd) { outw(cmd, awe_ports[Pointer]); awe_cur_cmd = cmd; } + +/* store values to i/o port array */ +static void setup_ports(int port1, int port2, int port3) +{ + awe_ports[0] = port1; + if (port2 == 0) + port2 = port1 + 0x400; + awe_ports[1] = port2; + awe_ports[2] = port2 + 2; + if (port3 == 0) + port3 = port1 + 0x800; + awe_ports[3] = port3; + awe_ports[4] = port3 + 2; + + port_setuped = TRUE; +} + +/* write 16bit data */ +static void +awe_poke(unsigned short cmd, unsigned short port, unsigned short data) +{ + awe_set_cmd(cmd); + outw(data, awe_ports[port]); +} + +/* write 32bit data */ +static void +awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data) +{ + unsigned short addr = awe_ports[port]; + awe_set_cmd(cmd); + outw(data, addr); /* write lower 16 bits */ + outw(data >> 16, addr + 2); /* write higher 16 bits */ +} + +/* read 16bit data */ +static unsigned short +awe_peek(unsigned short cmd, unsigned short port) +{ + unsigned short k; + awe_set_cmd(cmd); + k = inw(awe_ports[port]); + return k; +} + +/* read 32bit data */ +static unsigned int +awe_peek_dw(unsigned short cmd, unsigned short port) +{ + unsigned int k1, k2; + unsigned short addr = awe_ports[port]; + awe_set_cmd(cmd); + k1 = inw(addr); + k2 = inw(addr + 2); + k1 |= k2 << 16; + return k1; +} + +/* wait delay number of AWE32 44100Hz clocks */ +#ifdef WAIT_BY_LOOP /* wait by loop -- that's not good.. */ +static void +awe_wait(unsigned short delay) +{ + unsigned short clock, target; + unsigned short port = awe_ports[AWE_WC_Port]; + int counter; + + /* sample counter */ + awe_set_cmd(AWE_WC_Cmd); + clock = (unsigned short)inw(port); + target = clock + delay; + counter = 0; + if (target < clock) { + for (; (unsigned short)inw(port) > target; counter++) + if (counter > 65536) + break; + } + for (; (unsigned short)inw(port) < target; counter++) + if (counter > 65536) + break; +} +#else + +static void awe_wait(unsigned short delay) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((HZ*(unsigned long)delay + 44099)/44100); +} +/* +static void awe_wait(unsigned short delay) +{ + udelay(((unsigned long)delay * 1000000L + 44099) / 44100); +} +*/ +#endif /* wait by loop */ + +/* write a word data */ +#define awe_write_dram(c) awe_poke(AWE_SMLD, c) + + +/* + * port check / request + * 0x620-623, 0xA20-A23, 0xE20-E23 + */ + +static int __init +awe_check_port(void) +{ + if (! port_setuped) return 0; + return (check_region(awe_ports[0], 4) || + check_region(awe_ports[1], 4) || + check_region(awe_ports[3], 4)); +} + +static void __init +awe_request_region(void) +{ + if (! port_setuped) return; + request_region(awe_ports[0], 4, "sound driver (AWE32)"); + request_region(awe_ports[1], 4, "sound driver (AWE32)"); + request_region(awe_ports[3], 4, "sound driver (AWE32)"); +} + +static void __exit +awe_release_region(void) +{ + if (! port_setuped) return; + release_region(awe_ports[0], 4); + release_region(awe_ports[1], 4); + release_region(awe_ports[3], 4); +} + + +/* + * initialization of AWE driver + */ + +static void +awe_initialize(void) +{ + DEBUG(0,printk("AWE32: initializing..\n")); + + /* initialize hardware configuration */ + awe_poke(AWE_HWCF1, 0x0059); + awe_poke(AWE_HWCF2, 0x0020); + + /* disable audio; this seems to reduce a clicking noise a bit.. */ + awe_poke(AWE_HWCF3, 0); + + /* initialize audio channels */ + awe_init_audio(); + + /* initialize DMA */ + awe_init_dma(); + + /* initialize init array */ + awe_init_array(); + + /* check DRAM memory size */ + awe_check_dram(); + + /* initialize the FM section of the AWE32 */ + awe_init_fm(); + + /* set up voice envelopes */ + awe_tweak(); + + /* enable audio */ + awe_poke(AWE_HWCF3, 0x0004); + + /* set default values */ + awe_init_ctrl_parms(TRUE); + + /* set equalizer */ + awe_update_equalizer(); + + /* set reverb & chorus modes */ + awe_update_reverb_mode(); + awe_update_chorus_mode(); +} + + +/* + * AWE32 voice parameters + */ + +/* initialize voice_info record */ +static void +awe_init_voice_info(awe_voice_info *vp) +{ + vp->sample = 0; + vp->rate_offset = 0; + + vp->start = 0; + vp->end = 0; + vp->loopstart = 0; + vp->loopend = 0; + vp->mode = 0; + vp->root = 60; + vp->tune = 0; + vp->low = 0; + vp->high = 127; + vp->vellow = 0; + vp->velhigh = 127; + + vp->fixkey = -1; + vp->fixvel = -1; + vp->fixpan = -1; + vp->pan = -1; + + vp->exclusiveClass = 0; + vp->amplitude = 127; + vp->attenuation = 0; + vp->scaleTuning = 100; + + awe_init_voice_parm(&vp->parm); +} + +/* initialize voice_parm record: + * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. + * Vibrato and Tremolo effects are zero. + * Cutoff is maximum. + * Chorus and Reverb effects are zero. + */ +static void +awe_init_voice_parm(awe_voice_parm *pp) +{ + pp->moddelay = 0x8000; + pp->modatkhld = 0x7f7f; + pp->moddcysus = 0x7f7f; + pp->modrelease = 0x807f; + pp->modkeyhold = 0; + pp->modkeydecay = 0; + + pp->voldelay = 0x8000; + pp->volatkhld = 0x7f7f; + pp->voldcysus = 0x7f7f; + pp->volrelease = 0x807f; + pp->volkeyhold = 0; + pp->volkeydecay = 0; + + pp->lfo1delay = 0x8000; + pp->lfo2delay = 0x8000; + pp->pefe = 0; + + pp->fmmod = 0; + pp->tremfrq = 0; + pp->fm2frq2 = 0; + + pp->cutoff = 0xff; + pp->filterQ = 0; + + pp->chorus = 0; + pp->reverb = 0; +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* convert frequency mHz to abstract cents (= midi key * 100) */ +static int +freq_to_note(int mHz) +{ + /* abscents = log(mHz/8176) / log(2) * 1200 */ + unsigned int max_val = (unsigned int)0xffffffff / 10000; + int i, times; + unsigned int base; + unsigned int freq; + int note, tune; + + if (mHz == 0) + return 0; + if (mHz < 0) + return 12799; /* maximum */ + + freq = mHz; + note = 0; + for (base = 8176 * 2; freq >= base; base *= 2) { + note += 12; + if (note >= 128) /* over maximum */ + return 12799; + } + base /= 2; + + /* to avoid overflow... */ + times = 10000; + while (freq > max_val) { + max_val *= 10; + times /= 10; + base /= 10; + } + + freq = freq * times / base; + for (i = 0; i < 12; i++) { + if (freq < semitone_tuning[i+1]) + break; + note++; + } + + tune = 0; + freq = freq * 10000 / semitone_tuning[i]; + for (i = 0; i < 100; i++) { + if (freq < cent_tuning[i+1]) + break; + tune++; + } + + return note * 100 + tune; +} + + +/* convert Hz to AWE32 rate offset: + * sample pitch offset for the specified sample rate + * rate=44100 is no offset, each 4096 is 1 octave (twice). + * eg, when rate is 22050, this offset becomes -4096. + */ +static int +calc_rate_offset(int Hz) +{ + /* offset = log(Hz / 44100) / log(2) * 4096 */ + int freq, base, i; + + /* maybe smaller than max (44100Hz) */ + if (Hz <= 0 || Hz >= 44100) return 0; + + base = 0; + for (freq = Hz * 2; freq < 44100; freq *= 2) + base++; + base *= 1200; + + freq = 44100 * 10000 / (freq/2); + for (i = 0; i < 12; i++) { + if (freq < semitone_tuning[i+1]) + break; + base += 100; + } + freq = freq * 10000 / semitone_tuning[i]; + for (i = 0; i < 100; i++) { + if (freq < cent_tuning[i+1]) + break; + base++; + } + return -base * 4096 / 1200; +} + + +/* + * convert envelope time parameter to AWE32 raw parameter + */ + +/* attack & decay/release time table (msec) */ +static short attack_time_tbl[128] = { +32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816, +707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, +361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, +180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, +90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, +45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, +22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, +11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0, +}; + +static short decay_time_tbl[128] = { +32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082, +2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507, +1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722, +691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361, +345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180, +172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90, +86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45, +43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22, +}; + +#define calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725); + +/* delay time = 0x8000 - msec/92 */ +static int +calc_parm_hold(int msec) +{ + int val = (0x7f * 92 - msec) / 92; + if (val < 1) val = 1; + if (val > 127) val = 127; + return val; +} + +/* attack time: search from time table */ +static int +calc_parm_attack(int msec) +{ + return calc_parm_search(msec, attack_time_tbl); +} + +/* decay/release time: search from time table */ +static int +calc_parm_decay(int msec) +{ + return calc_parm_search(msec, decay_time_tbl); +} + +/* search an index for specified time from given time table */ +static int +calc_parm_search(int msec, short *table) +{ + int left = 1, right = 127, mid; + while (left < right) { + mid = (left + right) / 2; + if (msec < (int)table[mid]) + left = mid + 1; + else + right = mid; + } + return left; +} +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + + +/* + * effects table + */ + +/* set an effect value */ +#define FX_FLAG_OFF 0 +#define FX_FLAG_SET 1 +#define FX_FLAG_ADD 2 + +#define FX_SET(rec,type,value) \ + ((rec)->flags[type] = FX_FLAG_SET, (rec)->val[type] = (value)) +#define FX_ADD(rec,type,value) \ + ((rec)->flags[type] = FX_FLAG_ADD, (rec)->val[type] = (value)) +#define FX_UNSET(rec,type) \ + ((rec)->flags[type] = FX_FLAG_OFF, (rec)->val[type] = 0) + +/* check the effect value is set */ +#define FX_ON(rec,type) ((rec)->flags[type]) + +#define PARM_BYTE 0 +#define PARM_WORD 1 +#define PARM_SIGN 2 + +static struct PARM_DEFS { + int type; /* byte or word */ + int low, high; /* value range */ + fx_affect_func realtime; /* realtime paramater change */ +} parm_defs[] = { + {PARM_WORD, 0, 0x8000, NULL}, /* env1 delay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 attack */ + {PARM_BYTE, 0, 0x7e, NULL}, /* env1 hold */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 decay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 release */ + {PARM_BYTE, 0, 0x7f, NULL}, /* env1 sustain */ + {PARM_BYTE, 0, 0xff, NULL}, /* env1 pitch */ + {PARM_BYTE, 0, 0xff, NULL}, /* env1 cutoff */ + + {PARM_WORD, 0, 0x8000, NULL}, /* env2 delay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 attack */ + {PARM_BYTE, 0, 0x7e, NULL}, /* env2 hold */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 decay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 release */ + {PARM_BYTE, 0, 0x7f, NULL}, /* env2 sustain */ + + {PARM_WORD, 0, 0x8000, NULL}, /* lfo1 delay */ + {PARM_BYTE, 0, 0xff, awe_fx_tremfrq}, /* lfo1 freq */ + {PARM_SIGN, -128, 127, awe_fx_tremfrq}, /* lfo1 volume */ + {PARM_SIGN, -128, 127, awe_fx_fmmod}, /* lfo1 pitch */ + {PARM_BYTE, 0, 0xff, awe_fx_fmmod}, /* lfo1 cutoff */ + + {PARM_WORD, 0, 0x8000, NULL}, /* lfo2 delay */ + {PARM_BYTE, 0, 0xff, awe_fx_fm2frq2}, /* lfo2 freq */ + {PARM_SIGN, -128, 127, awe_fx_fm2frq2}, /* lfo2 pitch */ + + {PARM_WORD, 0, 0xffff, awe_set_voice_pitch}, /* initial pitch */ + {PARM_BYTE, 0, 0xff, NULL}, /* chorus */ + {PARM_BYTE, 0, 0xff, NULL}, /* reverb */ + {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial cutoff */ + {PARM_BYTE, 0, 15, awe_fx_filterQ}, /* initial resonance */ + + {PARM_WORD, 0, 0xffff, NULL}, /* sample start */ + {PARM_WORD, 0, 0xffff, NULL}, /* loop start */ + {PARM_WORD, 0, 0xffff, NULL}, /* loop end */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse sample start */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop start */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop end */ + {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial attenuation */ +}; + + +static unsigned char +FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value) +{ + int effect = 0; + int on = 0; + if (lay && (on = FX_ON(lay, type)) != 0) + effect = lay->val[type]; + if (!on && (on = FX_ON(rec, type)) != 0) + effect = rec->val[type]; + if (on == FX_FLAG_ADD) { + if (parm_defs[type].type == PARM_SIGN) { + if (value > 0x7f) + effect += (int)value - 0x100; + else + effect += (int)value; + } else { + effect += (int)value; + } + } + if (on) { + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + return (unsigned char)effect; + } + return value; +} + +/* get word effect value */ +static unsigned short +FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value) +{ + int effect = 0; + int on = 0; + if (lay && (on = FX_ON(lay, type)) != 0) + effect = lay->val[type]; + if (!on && (on = FX_ON(rec, type)) != 0) + effect = rec->val[type]; + if (on == FX_FLAG_ADD) + effect += (int)value; + if (on) { + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + return (unsigned short)effect; + } + return value; +} + +/* get word (upper=type1/lower=type2) effect value */ +static unsigned short +FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value) +{ + unsigned short tmp; + tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8)); + tmp <<= 8; + tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff)); + return tmp; +} + +/* address offset */ +static int +FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode) +{ + int addr = 0; + if (lay && FX_ON(lay, hi)) + addr = (short)lay->val[hi]; + else if (FX_ON(rec, hi)) + addr = (short)rec->val[hi]; + addr = addr << 15; + if (lay && FX_ON(lay, lo)) + addr += (short)lay->val[lo]; + else if (FX_ON(rec, lo)) + addr += (short)rec->val[lo]; + if (!(mode & AWE_SAMPLE_8BITS)) + addr /= 2; + return addr; +} + + +/* + * turn on/off sample + */ + +/* table for volume target calculation */ +static unsigned short voltarget[16] = { + 0xEAC0, 0XE0C8, 0XD740, 0XCE20, 0XC560, 0XBD08, 0XB500, 0XAD58, + 0XA5F8, 0X9EF0, 0X9830, 0X91C0, 0X8B90, 0X85A8, 0X8000, 0X7A90 +}; + +static void +awe_note_on(int voice) +{ + unsigned int temp; + int addr; + int vtarget, ftarget, ptarget, pitch; + awe_voice_info *vp; + awe_voice_parm_block *parm; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + /* A voice sample must assigned before calling */ + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + parm = (awe_voice_parm_block*)&vp->parm; + + /* channel to be silent and idle */ + awe_poke(AWE_DCYSUSV(voice), 0x0080); + awe_poke(AWE_VTFT(voice), 0x0000FFFF); + awe_poke(AWE_CVCF(voice), 0x0000FFFF); + awe_poke(AWE_PTRX(voice), 0); + awe_poke(AWE_CPF(voice), 0); + + /* set pitch offset */ + awe_set_pitch(voice, TRUE); + + /* modulation & volume envelope */ + if (parm->modatk >= 0x80 && parm->moddelay >= 0x8000) { + awe_poke(AWE_ENVVAL(voice), 0xBFFF); + pitch = (parm->env1pit<<4) + voices[voice].apitch; + if (pitch > 0xffff) pitch = 0xffff; + /* calculate filter target */ + ftarget = parm->cutoff + parm->env1fc; + limitvalue(ftarget, 0, 255); + ftarget <<= 8; + } else { + awe_poke(AWE_ENVVAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, parm->moddelay)); + ftarget = parm->cutoff; + ftarget <<= 8; + pitch = voices[voice].apitch; + } + + /* calcualte pitch target */ + if (pitch != 0xffff) { + ptarget = 1 << (pitch >> 12); + if (pitch & 0x800) ptarget += (ptarget*0x102e)/0x2710; + if (pitch & 0x400) ptarget += (ptarget*0x764)/0x2710; + if (pitch & 0x200) ptarget += (ptarget*0x389)/0x2710; + ptarget += (ptarget>>1); + if (ptarget > 0xffff) ptarget = 0xffff; + + } else ptarget = 0xffff; + if (parm->modatk >= 0x80) + awe_poke(AWE_ATKHLD(voice), + FX_BYTE(fx, fx_lay, AWE_FX_ENV1_HOLD, parm->modhld) << 8 | 0x7f); + else + awe_poke(AWE_ATKHLD(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK, + vp->parm.modatkhld)); + awe_poke(AWE_DCYSUS(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY, + vp->parm.moddcysus)); + + if (parm->volatk >= 0x80 && parm->voldelay >= 0x8000) { + awe_poke(AWE_ENVVOL(voice), 0xBFFF); + vtarget = voltarget[voices[voice].avol%0x10]>>(voices[voice].avol>>4); + } else { + awe_poke(AWE_ENVVOL(voice), + FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay)); + vtarget = 0; + } + if (parm->volatk >= 0x80) + awe_poke(AWE_ATKHLDV(voice), + FX_BYTE(fx, fx_lay, AWE_FX_ENV2_HOLD, parm->volhld) << 8 | 0x7f); + else + awe_poke(AWE_ATKHLDV(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK, + vp->parm.volatkhld)); + /* decay/sustain parameter for volume envelope must be set at last */ + + /* cutoff and volume */ + awe_set_volume(voice, TRUE); + + /* modulation envelope heights */ + awe_poke(AWE_PEFE(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF, + vp->parm.pefe)); + + /* lfo1/2 delay */ + awe_poke(AWE_LFO1VAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay)); + awe_poke(AWE_LFO2VAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay)); + + /* lfo1 pitch & cutoff shift */ + awe_fx_fmmod(voice, TRUE); + /* lfo1 volume & freq */ + awe_fx_tremfrq(voice, TRUE); + /* lfo2 pitch & freq */ + awe_fx_fm2frq2(voice, TRUE); + /* pan & loop start */ + awe_set_pan(voice, TRUE); + + /* chorus & loop end (chorus 8bit, MSB) */ + addr = vp->loopend - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END, + AWE_FX_COARSE_LOOP_END, vp->mode); + temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus); + temp = (temp <<24) | (unsigned int)addr; + awe_poke_dw(AWE_CSL(voice), temp); + DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr)); + + /* Q & current address (Q 4bit value, MSB) */ + addr = vp->start - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START, + AWE_FX_COARSE_SAMPLE_START, vp->mode); + temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ); + temp = (temp<<28) | (unsigned int)addr; + awe_poke_dw(AWE_CCCA(voice), temp); + DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr)); + + /* clear unknown registers */ + awe_poke_dw(AWE_00A0(voice), 0); + awe_poke_dw(AWE_0080(voice), 0); + + /* reset volume */ + awe_poke_dw(AWE_VTFT(voice), (vtarget<<16)|ftarget); + awe_poke_dw(AWE_CVCF(voice), (vtarget<<16)|ftarget); + + /* set reverb */ + temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb); + temp = (temp << 8) | (ptarget << 16) | voices[voice].aaux; + awe_poke_dw(AWE_PTRX(voice), temp); + awe_poke_dw(AWE_CPF(voice), ptarget << 16); + /* turn on envelope */ + awe_poke(AWE_DCYSUSV(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY, + vp->parm.voldcysus)); + + voices[voice].state = AWE_ST_ON; + + /* clear voice position for the next note on this channel */ + if (SINGLE_LAYER_MODE()) { + FX_UNSET(fx, AWE_FX_SAMPLE_START); + FX_UNSET(fx, AWE_FX_COARSE_SAMPLE_START); + } +} + + +/* turn off the voice */ +static void +awe_note_off(int voice) +{ + awe_voice_info *vp; + unsigned short tmp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if ((vp = voices[voice].sample) == NULL) { + voices[voice].state = AWE_ST_OFF; + return; + } + + tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE, + (unsigned char)vp->parm.modrelease); + awe_poke(AWE_DCYSUS(voice), tmp); + tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE, + (unsigned char)vp->parm.volrelease); + awe_poke(AWE_DCYSUSV(voice), tmp); + voices[voice].state = AWE_ST_RELEASED; +} + +/* force to terminate the voice (no releasing echo) */ +static void +awe_terminate(int voice) +{ + awe_poke(AWE_DCYSUSV(voice), 0x807F); + awe_tweak_voice(voice); + voices[voice].state = AWE_ST_OFF; +} + +/* turn off other voices with the same exclusive class (for drums) */ +static void +awe_exclusive_off(int voice) +{ + int i, exclass; + + if (voices[voice].sample == NULL) + return; + if ((exclass = voices[voice].sample->exclusiveClass) == 0) + return; /* not exclusive */ + + /* turn off voices with the same class */ + for (i = 0; i < awe_max_voices; i++) { + if (i != voice && IS_PLAYING(i) && + voices[i].sample && voices[i].ch == voices[voice].ch && + voices[i].sample->exclusiveClass == exclass) { + DEBUG(4,printk("AWE32: [exoff(%d)]\n", i)); + awe_terminate(i); + awe_voice_init(i, TRUE); + } + } +} + + +/* + * change the parameters of an audible voice + */ + +/* change pitch */ +static void +awe_set_pitch(int voice, int forced) +{ + if (IS_NO_EFFECT(voice) && !forced) return; + awe_poke(AWE_IP(voice), voices[voice].apitch); + DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch)); +} + +/* calculate & change pitch */ +static void +awe_set_voice_pitch(int voice, int forced) +{ + awe_calc_pitch(voice); + awe_set_pitch(voice, forced); +} + +/* change volume & cutoff */ +static void +awe_set_volume(int voice, int forced) +{ + awe_voice_info *vp; + unsigned short tmp2; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (!IS_PLAYING(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF, + (unsigned char)voices[voice].acutoff); + tmp2 = (tmp2 << 8); + tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN, + (unsigned char)voices[voice].avol); + awe_poke(AWE_IFATN(voice), tmp2); +} + +/* calculate & change volume */ +static void +awe_set_voice_vol(int voice, int forced) +{ + if (IS_EMPTY(voice)) + return; + awe_calc_volume(voice); + awe_set_volume(voice, forced); +} + + +/* change pan; this could make a click noise.. */ +static void +awe_set_pan(int voice, int forced) +{ + unsigned int temp; + int addr; + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */ + if (vp->fixpan > 0) /* 0-127 */ + temp = 255 - (int)vp->fixpan * 2; + else { + int pos = 0; + if (vp->pan >= 0) /* 0-127 */ + pos = (int)vp->pan * 2 - 128; + pos += voices[voice].cinfo->panning; /* -128 - 127 */ + temp = 127 - pos; + } + limitvalue(temp, 0, 255); + if (ctrls[AWE_MD_PAN_EXCHANGE]) { + temp = 255 - temp; + } + if (forced || temp != voices[voice].apan) { + voices[voice].apan = temp; + if (temp == 0) + voices[voice].aaux = 0xff; + else + voices[voice].aaux = (-temp) & 0xff; + addr = vp->loopstart - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START, + AWE_FX_COARSE_LOOP_START, vp->mode); + temp = (temp<<24) | (unsigned int)addr; + awe_poke_dw(AWE_PSST(voice), temp); + DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr)); + } +} + +/* effects change during playing */ +static void +awe_fx_fmmod(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + awe_poke(AWE_FMMOD(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF, + vp->parm.fmmod)); +} + +/* set tremolo (lfo1) volume & frequency */ +static void +awe_fx_tremfrq(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + awe_poke(AWE_TREMFRQ(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ, + vp->parm.tremfrq)); +} + +/* set lfo2 pitch & frequency */ +static void +awe_fx_fm2frq2(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + awe_poke(AWE_FM2FRQ2(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ, + vp->parm.fm2frq2)); +} + + +/* Q & current address (Q 4bit value, MSB) */ +static void +awe_fx_filterQ(int voice, int forced) +{ + unsigned int addr; + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + addr = awe_peek_dw(AWE_CCCA(voice)) & 0xffffff; + addr |= (FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ) << 28); + awe_poke_dw(AWE_CCCA(voice), addr); +} + +/* + * calculate pitch offset + * + * 0xE000 is no pitch offset at 44100Hz sample. + * Every 4096 is one octave. + */ + +static void +awe_calc_pitch(int voice) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + awe_chan_info *cp = voices[voice].cinfo; + int offset; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + if (ap->index == 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample((awe_voice_list*)ap) == 0) + return; + } + + /* calculate offset */ + if (ap->fixkey >= 0) { + DEBUG(3,printk("AWE32: p-> fixkey(%d) tune(%d)\n", ap->fixkey, ap->tune)); + offset = (ap->fixkey - ap->root) * 4096 / 12; + } else { + DEBUG(3,printk("AWE32: p(%d)-> root(%d) tune(%d)\n", vp->note, ap->root, ap->tune)); + offset = (vp->note - ap->root) * 4096 / 12; + DEBUG(4,printk("AWE32: p-> ofs=%d\n", offset)); + } + offset = (offset * ap->scaleTuning) / 100; + DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset)); + offset += ap->tune * 4096 / 1200; + DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset)); + if (cp->bender != 0) { + DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender)); + /* (819200: 1 semitone) ==> (4096: 12 semitones) */ + offset += cp->bender * cp->bender_range / 2400; + } + + /* add initial pitch correction */ + if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH)) + offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH]; + else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) + offset += cp->fx.val[AWE_FX_INIT_PITCH]; + + /* 0xe000: root pitch */ + vp->apitch = 0xe000 + ap->rate_offset + offset; + DEBUG(4,printk("AWE32: p-> sum aofs=%x, rate_ofs=%d\n", vp->apitch, ap->rate_offset)); + if (vp->apitch > 0xffff) + vp->apitch = 0xffff; + if (vp->apitch < 0) + vp->apitch = 0; +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY +/* calculate MIDI key and semitone from the specified frequency */ +static void +awe_calc_pitch_from_freq(int voice, int freq) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + int offset; + int note; + + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + if (ap->index == 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample((awe_voice_list*)ap) == 0) + return; + } + note = freq_to_note(freq); + offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200; + offset = (offset * ap->scaleTuning) / 100; + if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH)) + offset += fx_lay->val[AWE_FX_INIT_PITCH]; + else if (FX_ON(fx, AWE_FX_INIT_PITCH)) + offset += fx->val[AWE_FX_INIT_PITCH]; + vp->apitch = 0xe000 + ap->rate_offset + offset; + if (vp->apitch > 0xffff) + vp->apitch = 0xffff; + if (vp->apitch < 0) + vp->apitch = 0; +} +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + + +/* + * calculate volume attenuation + * + * Voice volume is controlled by volume attenuation parameter. + * So volume becomes maximum when avol is 0 (no attenuation), and + * minimum when 255 (-96dB or silence). + */ + +static int vol_table[128] = { + 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, + 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, + 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, + 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, + 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, + 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, + 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, + 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, +}; + +/* tables for volume->attenuation calculation */ +static unsigned char voltab1[128] = { + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, + 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, + 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, + 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char voltab2[128] = { + 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a, + 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, + 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15, + 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, + 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, + 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char expressiontab[128] = { + 0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42, + 0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30, + 0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, + 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e, + 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, + 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, + 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, + 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static void +awe_calc_volume(int voice) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + awe_chan_info *cp = voices[voice].cinfo; + int vol; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + + ap = vp->sample; + if (ap->index == 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample((awe_voice_list*)ap) == 0) + return; + } + + if (ctrls[AWE_MD_NEW_VOLUME_CALC]) { + int main_vol = cp->main_vol * ap->amplitude / 127; + limitvalue(vp->velocity, 0, 127); + limitvalue(main_vol, 0, 127); + limitvalue(cp->expression_vol, 0, 127); + + vol = voltab1[main_vol] + voltab2[vp->velocity]; + vol = (vol * 8) / 3; + vol += ap->attenuation; + if (cp->expression_vol < 127) + vol += ((0x100 - vol) * expressiontab[cp->expression_vol])/128; + vol += atten_offset; + if (atten_relative) + vol += ctrls[AWE_MD_ZERO_ATTEN]; + limitvalue(vol, 0, 255); + vp->avol = vol; + + } else { + /* 0 - 127 */ + vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127); + vol = vol * ap->amplitude / 127; + + if (vol < 0) vol = 0; + if (vol > 127) vol = 127; + + /* calc to attenuation */ + vol = vol_table[vol]; + vol += (int)ap->attenuation; + vol += atten_offset; + if (atten_relative) + vol += ctrls[AWE_MD_ZERO_ATTEN]; + if (vol > 255) vol = 255; + + vp->avol = vol; + } + if (cp->bank != AWE_DRUM_BANK && ((awe_voice_parm_block*)(&ap->parm))->volatk < 0x7d) { + int atten; + if (vp->velocity < 70) atten = 70; + else atten = vp->velocity; + vp->acutoff = (atten * ap->parm.cutoff + 0xa0) >> 7; + } else { + vp->acutoff = ap->parm.cutoff; + } + DEBUG(3,printk("AWE32: [-- voice(%d) vol=%x]\n", voice, vol)); +} + +/* change master volume */ +static void +awe_change_master_volume(short val) +{ + limitvalue(val, 0, 127); + atten_offset = vol_table[val]; + atten_relative = TRUE; + awe_update_volume(); +} + +/* update volumes of all available channels */ +static void awe_update_volume(void) +{ + int i; + for (i = 0; i < awe_max_voices; i++) + awe_set_voice_vol(i, TRUE); +} + +/* set sostenuto on */ +static void awe_sostenuto_on(int voice, int forced) +{ + if (IS_NO_EFFECT(voice) && !forced) return; + voices[voice].sostenuto = 127; +} + + +/* drop sustain */ +static void awe_sustain_off(int voice, int forced) +{ + if (voices[voice].state == AWE_ST_SUSTAINED) { + awe_note_off(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, FALSE); + } +} + + +/* terminate and initialize voice */ +static void awe_terminate_and_init(int voice, int forced) +{ + awe_terminate(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, TRUE); +} + + +/* + * synth operation routines + */ + +#define AWE_VOICE_KEY(v) (0x8000 | (v)) +#define AWE_CHAN_KEY(c,n) (((c) << 8) | ((n) + 1)) +#define KEY_CHAN_MATCH(key,c) (((key) >> 8) == (c)) + +/* initialize the voice */ +static void +awe_voice_init(int voice, int init_all) +{ + voice_info *vp = &voices[voice]; + + /* reset voice search key */ + if (playing_mode == AWE_PLAY_DIRECT) + vp->key = AWE_VOICE_KEY(voice); + else + vp->key = 0; + + /* clear voice mapping */ + voice_alloc->map[voice] = 0; + + /* touch the timing flag */ + vp->time = current_alloc_time; + + /* initialize other parameters if necessary */ + if (init_all) { + vp->note = -1; + vp->velocity = 0; + vp->sostenuto = 0; + + vp->sample = NULL; + vp->cinfo = &channels[voice]; + vp->ch = voice; + vp->state = AWE_ST_OFF; + + /* emu8000 parameters */ + vp->apitch = 0; + vp->avol = 255; + vp->apan = -1; + } +} + +/* clear effects */ +static void awe_fx_init(int ch) +{ + if (SINGLE_LAYER_MODE() && !ctrls[AWE_MD_KEEP_EFFECT]) { + memset(&channels[ch].fx, 0, sizeof(channels[ch].fx)); + memset(&channels[ch].fx_layer, 0, sizeof(&channels[ch].fx_layer)); + } +} + +/* initialize channel info */ +static void awe_channel_init(int ch, int init_all) +{ + awe_chan_info *cp = &channels[ch]; + cp->channel = ch; + if (init_all) { + cp->panning = 0; /* zero center */ + cp->bender_range = 200; /* sense * 100 */ + cp->main_vol = 127; + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) { + cp->instr = ctrls[AWE_MD_DEF_DRUM]; + cp->bank = AWE_DRUM_BANK; + } else { + cp->instr = ctrls[AWE_MD_DEF_PRESET]; + cp->bank = ctrls[AWE_MD_DEF_BANK]; + } + } + + cp->bender = 0; /* zero tune skew */ + cp->expression_vol = 127; + cp->chan_press = 0; + cp->sustained = 0; + + if (! ctrls[AWE_MD_KEEP_EFFECT]) { + memset(&cp->fx, 0, sizeof(cp->fx)); + memset(&cp->fx_layer, 0, sizeof(cp->fx_layer)); + } +} + + +/* change the voice parameters; voice = channel */ +static void awe_voice_change(int voice, fx_affect_func func) +{ + int i; + switch (playing_mode) { + case AWE_PLAY_DIRECT: + func(voice, FALSE); + break; + case AWE_PLAY_INDIRECT: + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == AWE_VOICE_KEY(voice)) + func(i, FALSE); + break; + default: + for (i = 0; i < awe_max_voices; i++) + if (KEY_CHAN_MATCH(voices[i].key, voice)) + func(i, FALSE); + break; + } +} + + +/* + * device open / close + */ + +/* open device: + * reset status of all voices, and clear sample position flag + */ +static int +awe_open(int dev, int mode) +{ + if (awe_busy) + return -EBUSY; + + awe_busy = TRUE; + + /* set default mode */ + awe_init_ctrl_parms(FALSE); + atten_relative = TRUE; + atten_offset = 0; + drum_flags = DEFAULT_DRUM_FLAGS; + playing_mode = AWE_PLAY_INDIRECT; + + /* reset voices & channels */ + awe_reset(dev); + + patch_opened = 0; + + return 0; +} + + +/* close device: + * reset all voices again (terminate sounds) + */ +static void +awe_close(int dev) +{ + awe_reset(dev); + awe_busy = FALSE; +} + + +/* set miscellaneous mode parameters + */ +static void +awe_init_ctrl_parms(int init_all) +{ + int i; + for (i = 0; i < AWE_MD_END; i++) { + if (init_all || ctrl_parms[i].init_each_time) + ctrls[i] = ctrl_parms[i].value; + } +} + + +/* sequencer I/O control: + */ +static int +awe_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + switch (cmd) { + case SNDCTL_SYNTH_INFO: + if (playing_mode == AWE_PLAY_DIRECT) + awe_info.nr_voices = awe_max_voices; + else + awe_info.nr_voices = AWE_MAX_CHANNELS; + memcpy((char*)arg, &awe_info, sizeof(awe_info)); + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + awe_reset(dev); + awe_reset_samples(); + return 0; + break; + + case SNDCTL_SEQ_PERCMODE: + /* what's this? */ + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return memsize - awe_free_mem_ptr() * 2; + + default: + printk(KERN_WARNING "AWE32: unsupported ioctl %d\n", cmd); + return -EINVAL; + } +} + + +static int voice_in_range(int voice) +{ + if (playing_mode == AWE_PLAY_DIRECT) { + if (voice < 0 || voice >= awe_max_voices) + return FALSE; + } else { + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return FALSE; + } + return TRUE; +} + +static void release_voice(int voice, int do_sustain) +{ + if (IS_NO_SOUND(voice)) + return; + if (do_sustain && (voices[voice].cinfo->sustained == 127 || + voices[voice].sostenuto == 127)) + voices[voice].state = AWE_ST_SUSTAINED; + else { + awe_note_off(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, FALSE); + } +} + +/* release all notes */ +static void awe_note_off_all(int do_sustain) +{ + int i; + for (i = 0; i < awe_max_voices; i++) + release_voice(i, do_sustain); +} + +/* kill a voice: + * not terminate, just release the voice. + */ +static int +awe_kill_note(int dev, int voice, int note, int velocity) +{ + int i, v2, key; + + DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity)); + if (! voice_in_range(voice)) + return -EINVAL; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + key = AWE_VOICE_KEY(voice); + break; + + case AWE_PLAY_MULTI2: + v2 = voice_alloc->map[voice] >> 8; + voice_alloc->map[voice] = 0; + voice = v2; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return -EINVAL; + /* continue to below */ + default: + key = AWE_CHAN_KEY(voice, note); + break; + } + + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) + release_voice(i, TRUE); + } + return 0; +} + + +static void start_or_volume_change(int voice, int velocity) +{ + voices[voice].velocity = velocity; + awe_calc_volume(voice); + if (voices[voice].state == AWE_ST_STANDBY) + awe_note_on(voice); + else if (voices[voice].state == AWE_ST_ON) + awe_set_volume(voice, FALSE); +} + +static void set_and_start_voice(int voice, int state) +{ + /* calculate pitch & volume parameters */ + voices[voice].state = state; + awe_calc_pitch(voice); + awe_calc_volume(voice); + if (state == AWE_ST_ON) + awe_note_on(voice); +} + +/* start a voice: + * if note is 255, identical with aftertouch function. + * Otherwise, start a voice with specified not and volume. + */ +static int +awe_start_note(int dev, int voice, int note, int velocity) +{ + int i, key, state, volonly; + + DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity)); + if (! voice_in_range(voice)) + return -EINVAL; + + if (velocity == 0) + state = AWE_ST_STANDBY; /* stand by for playing */ + else + state = AWE_ST_ON; /* really play */ + volonly = FALSE; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + key = AWE_VOICE_KEY(voice); + if (note == 255) + volonly = TRUE; + break; + + case AWE_PLAY_MULTI2: + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return -EINVAL; + /* continue to below */ + default: + if (note >= 128) { /* key volume mode */ + note -= 128; + volonly = TRUE; + } + key = AWE_CHAN_KEY(voice, note); + break; + } + + /* dynamic volume change */ + if (volonly) { + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) + start_or_volume_change(i, velocity); + } + return 0; + } + + /* if the same note still playing, stop it */ + if (playing_mode != AWE_PLAY_DIRECT || ctrls[AWE_MD_EXCLUSIVE_SOUND]) { + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == key) { + if (voices[i].state == AWE_ST_ON) { + awe_note_off(i); + awe_voice_init(i, FALSE); + } else if (voices[i].state == AWE_ST_STANDBY) + awe_voice_init(i, TRUE); + } + } + + /* allocate voices */ + if (playing_mode == AWE_PLAY_DIRECT) + awe_alloc_one_voice(voice, note, velocity); + else + awe_alloc_multi_voices(voice, note, velocity, key); + + /* turn off other voices exlusively (for drums) */ + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == key) + awe_exclusive_off(i); + + /* set up pitch and volume parameters */ + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key && voices[i].state == AWE_ST_OFF) + set_and_start_voice(i, state); + } + + return 0; +} + + +/* calculate hash key */ +static int +awe_search_key(int bank, int preset, int note) +{ + unsigned int key; + +#if 1 /* new hash table */ + if (bank == AWE_DRUM_BANK) + key = preset + note + 128; + else + key = bank + preset; +#else + key = preset; +#endif + key %= AWE_MAX_PRESETS; + + return (int)key; +} + + +/* search instrument from hash table */ +static awe_voice_list * +awe_search_instr(int bank, int preset, int note) +{ + awe_voice_list *p; + int key, key2; + + key = awe_search_key(bank, preset, note); + for (p = preset_table[key]; p; p = p->next_bank) { + if (p->instr == preset && p->bank == bank) + return p; + } + key2 = awe_search_key(bank, preset, 0); /* search default */ + if (key == key2) + return NULL; + for (p = preset_table[key2]; p; p = p->next_bank) { + if (p->instr == preset && p->bank == bank) + return p; + } + return NULL; +} + + +/* assign the instrument to a voice */ +static int +awe_set_instr_2(int dev, int voice, int instr_no) +{ + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return -EINVAL; + } + return awe_set_instr(dev, voice, instr_no); +} + +/* assign the instrument to a channel; voice is the channel number */ +static int +awe_set_instr(int dev, int voice, int instr_no) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return -EINVAL; + + if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS) + return -EINVAL; + + cinfo = &channels[voice]; + cinfo->instr = instr_no; + DEBUG(2,printk("AWE32: [program(%d) %d]\n", voice, instr_no)); + + return 0; +} + + +/* reset all voices; terminate sounds and initialize parameters */ +static void +awe_reset(int dev) +{ + int i; + current_alloc_time = 0; + /* don't turn off voice 31 and 32. they are used also for FM voices */ + for (i = 0; i < awe_max_voices; i++) { + awe_terminate(i); + awe_voice_init(i, TRUE); + } + for (i = 0; i < AWE_MAX_CHANNELS; i++) + awe_channel_init(i, TRUE); + for (i = 0; i < 16; i++) { + awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127; + awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127; + } + awe_init_fm(); + awe_tweak(); +} + + +/* hardware specific control: + * GUS specific and AWE32 specific controls are available. + */ +static void +awe_hw_control(int dev, unsigned char *event) +{ + int cmd = event[2]; + if (cmd & _AWE_MODE_FLAG) + awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event); +#ifdef AWE_HAS_GUS_COMPATIBILITY + else + awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event); +#endif +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* GUS compatible controls */ +static void +awe_hw_gus_control(int dev, int cmd, unsigned char *event) +{ + int voice, i, key; + unsigned short p1; + short p2; + int plong; + + if (MULTI_LAYER_MODE()) + return; + if (cmd == _GUS_NUMVOICES) + return; + + voice = event[3]; + if (! voice_in_range(voice)) + return; + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + plong = *(int*) &event[4]; + + switch (cmd) { + case _GUS_VOICESAMPLE: + awe_set_instr(dev, voice, p1); + return; + + case _GUS_VOICEBALA: + /* 0 to 15 --> -128 to 127 */ + awe_panning(dev, voice, ((int)p1 << 4) - 128); + return; + + case _GUS_VOICEVOL: + case _GUS_VOICEVOL2: + /* not supported yet */ + return; + + case _GUS_RAMPRANGE: + case _GUS_RAMPRATE: + case _GUS_RAMPMODE: + case _GUS_RAMPON: + case _GUS_RAMPOFF: + /* volume ramping not supported */ + return; + + case _GUS_VOLUME_SCALE: + return; + + case _GUS_VOICE_POS: + FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START, + (short)(plong & 0x7fff)); + FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START, + (plong >> 15) & 0xffff); + return; + } + + key = AWE_VOICE_KEY(voice); + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) { + switch (cmd) { + case _GUS_VOICEON: + awe_note_on(i); + break; + + case _GUS_VOICEOFF: + awe_terminate(i); + awe_fx_init(voices[i].ch); + awe_voice_init(i, TRUE); + break; + + case _GUS_VOICEFADE: + awe_note_off(i); + awe_fx_init(voices[i].ch); + awe_voice_init(i, FALSE); + break; + + case _GUS_VOICEFREQ: + awe_calc_pitch_from_freq(i, plong); + break; + } + } + } +} + +#endif /* gus_compat */ + + +/* AWE32 specific controls */ +static void +awe_hw_awe_control(int dev, int cmd, unsigned char *event) +{ + int voice; + unsigned short p1; + short p2; + int i; + + voice = event[3]; + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + + switch (cmd) { + case _AWE_DEBUG_MODE: + ctrls[AWE_MD_DEBUG_MODE] = p1; + printk(KERN_DEBUG "AWE32: debug mode = %d\n", ctrls[AWE_MD_DEBUG_MODE]); + break; + case _AWE_REVERB_MODE: + ctrls[AWE_MD_REVERB_MODE] = p1; + awe_update_reverb_mode(); + break; + + case _AWE_CHORUS_MODE: + ctrls[AWE_MD_CHORUS_MODE] = p1; + awe_update_chorus_mode(); + break; + + case _AWE_REMOVE_LAST_SAMPLES: + DEBUG(0,printk("AWE32: remove last samples\n")); + awe_reset(0); + if (locked_sf_id > 0) + awe_remove_samples(locked_sf_id); + break; + + case _AWE_INITIALIZE_CHIP: + awe_initialize(); + break; + + case _AWE_SEND_EFFECT: + i = -1; + if (p1 >= 0x100) { + i = (p1 >> 8); + if (i < 0 || i >= MAX_LAYERS) + break; + } + awe_send_effect(voice, i, p1, p2); + break; + + case _AWE_RESET_CHANNEL: + awe_channel_init(voice, !p1); + break; + + case _AWE_TERMINATE_ALL: + awe_reset(0); + break; + + case _AWE_TERMINATE_CHANNEL: + awe_voice_change(voice, awe_terminate_and_init); + break; + + case _AWE_RELEASE_ALL: + awe_note_off_all(FALSE); + break; + case _AWE_NOTEOFF_ALL: + awe_note_off_all(TRUE); + break; + + case _AWE_INITIAL_VOLUME: + DEBUG(0,printk("AWE32: init attenuation %d\n", p1)); + atten_relative = (char)p2; + atten_offset = (short)p1; + awe_update_volume(); + break; + + case _AWE_CHN_PRESSURE: + channels[voice].chan_press = p1; + awe_modwheel_change(voice, p1); + break; + + case _AWE_CHANNEL_MODE: + DEBUG(0,printk("AWE32: channel mode = %d\n", p1)); + playing_mode = p1; + awe_reset(0); + break; + + case _AWE_DRUM_CHANNELS: + DEBUG(0,printk("AWE32: drum flags = %x\n", p1)); + drum_flags = *(unsigned int*)&event[4]; + break; + + case _AWE_MISC_MODE: + DEBUG(0,printk("AWE32: ctrl parms = %d %d\n", p1, p2)); + if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END) { + ctrls[p1] = p2; + if (ctrl_parms[p1].update) + ctrl_parms[p1].update(); + } + break; + + case _AWE_EQUALIZER: + ctrls[AWE_MD_BASS_LEVEL] = p1; + ctrls[AWE_MD_TREBLE_LEVEL] = p2; + awe_update_equalizer(); + break; + + default: + DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice)); + break; + } +} + + +/* change effects */ +static void +awe_send_effect(int voice, int layer, int type, int val) +{ + awe_chan_info *cinfo; + FX_Rec *fx; + int mode; + + cinfo = &channels[voice]; + if (layer >= 0 && layer < MAX_LAYERS) + fx = &cinfo->fx_layer[layer]; + else + fx = &cinfo->fx; + + if (type & 0x40) + mode = FX_FLAG_OFF; + else if (type & 0x80) + mode = FX_FLAG_ADD; + else + mode = FX_FLAG_SET; + type &= 0x3f; + + if (type >= 0 && type < AWE_FX_END) { + DEBUG(2,printk("AWE32: effects (%d) %d %d\n", voice, type, val)); + if (mode == FX_FLAG_SET) + FX_SET(fx, type, val); + else if (mode == FX_FLAG_ADD) + FX_ADD(fx, type, val); + else + FX_UNSET(fx, type); + if (mode != FX_FLAG_OFF && parm_defs[type].realtime) { + DEBUG(2,printk("AWE32: fx_realtime (%d)\n", voice)); + awe_voice_change(voice, parm_defs[type].realtime); + } + } +} + + +/* change modulation wheel; voice is already mapped on multi2 mode */ +static void +awe_modwheel_change(int voice, int value) +{ + int i; + awe_chan_info *cinfo; + + cinfo = &channels[voice]; + i = value * ctrls[AWE_MD_MOD_SENSE] / 1200; + FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i); + awe_voice_change(voice, awe_fx_fmmod); + FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i); + awe_voice_change(voice, awe_fx_fm2frq2); +} + + +/* voice pressure change */ +static void +awe_aftertouch(int dev, int voice, int pressure) +{ + int note; + + DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure)); + if (! voice_in_range(voice)) + return; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + awe_start_note(dev, voice, 255, pressure); + break; + case AWE_PLAY_MULTI2: + note = (voice_alloc->map[voice] & 0xff) - 1; + awe_key_pressure(dev, voice, note + 0x80, pressure); + break; + } +} + + +/* voice control change */ +static void +awe_controller(int dev, int voice, int ctrl_num, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + cinfo = &channels[voice]; + + switch (ctrl_num) { + case CTL_BANK_SELECT: /* MIDI control #0 */ + DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value)); + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice) && + !ctrls[AWE_MD_TOGGLE_DRUM_BANK]) + break; + if (value < 0 || value > 255) + break; + cinfo->bank = value; + if (cinfo->bank == AWE_DRUM_BANK) + DRUM_CHANNEL_ON(cinfo->channel); + else + DRUM_CHANNEL_OFF(cinfo->channel); + awe_set_instr(dev, voice, cinfo->instr); + break; + + case CTL_MODWHEEL: /* MIDI control #1 */ + DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value)); + awe_modwheel_change(voice, value); + break; + + case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */ + DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value)); + /* zero centered */ + cinfo->bender = value; + awe_voice_change(voice, awe_set_voice_pitch); + break; + + case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value)); + /* value = sense x 100 */ + cinfo->bender_range = value; + /* no audible pitch change yet.. */ + break; + + case CTL_EXPRESSION: /* MIDI control #11 */ + if (SINGLE_LAYER_MODE()) + value /= 128; + case CTRL_EXPRESSION: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value)); + /* 0 - 127 */ + cinfo->expression_vol = value; + awe_voice_change(voice, awe_set_voice_vol); + break; + + case CTL_PAN: /* MIDI control #10 */ + DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value)); + /* (0-127) -> signed 8bit */ + cinfo->panning = value * 2 - 128; + if (ctrls[AWE_MD_REALTIME_PAN]) + awe_voice_change(voice, awe_set_pan); + break; + + case CTL_MAIN_VOLUME: /* MIDI control #7 */ + if (SINGLE_LAYER_MODE()) + value = (value * 100) / 16383; + case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value)); + /* 0 - 127 */ + cinfo->main_vol = value; + awe_voice_change(voice, awe_set_voice_vol); + break; + + case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */ + DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value)); + FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2); + break; + + case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */ + DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value)); + FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2); + break; + + case 120: /* all sounds off */ + awe_note_off_all(FALSE); + break; + case 123: /* all notes off */ + awe_note_off_all(TRUE); + break; + + case CTL_SUSTAIN: /* MIDI control #64 */ + cinfo->sustained = value; + if (value != 127) + awe_voice_change(voice, awe_sustain_off); + break; + + case CTL_SOSTENUTO: /* MIDI control #66 */ + if (value == 127) + awe_voice_change(voice, awe_sostenuto_on); + else + awe_voice_change(voice, awe_sustain_off); + break; + + default: + DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n", + voice, ctrl_num, value)); + break; + } +} + + +/* voice pan change (value = -128 - 127) */ +static void +awe_panning(int dev, int voice, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + cinfo = &channels[voice]; + cinfo->panning = value; + DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning)); + if (ctrls[AWE_MD_REALTIME_PAN]) + awe_voice_change(voice, awe_set_pan); +} + + +/* volume mode change */ +static void +awe_volume_method(int dev, int mode) +{ + /* not impremented */ + DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode)); +} + + +/* pitch wheel change: 0-16384 */ +static void +awe_bender(int dev, int voice, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + /* convert to zero centered value */ + cinfo = &channels[voice]; + cinfo->bender = value - 8192; + DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender)); + awe_voice_change(voice, awe_set_voice_pitch); +} + + +/* + * load a sound patch: + * three types of patches are accepted: AWE, GUS, and SYSEX. + */ + +static int +awe_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + awe_patch_info patch; + int rc = 0; + +#ifdef AWE_HAS_GUS_COMPATIBILITY + if (format == GUS_PATCH) { + return awe_load_guspatch(addr, offs, count, pmgr_flag); + } else +#endif + if (format == SYSEX_PATCH) { + /* no system exclusive message supported yet */ + return 0; + } else if (format != AWE_PATCH) { + printk(KERN_WARNING "AWE32 Error: Invalid patch format (key) 0x%x\n", format); + return -EINVAL; + } + + if (count < AWE_PATCH_INFO_SIZE) { + printk(KERN_WARNING "AWE32 Error: Patch header too short\n"); + return -EINVAL; + } + if (copy_from_user(((char*)&patch) + offs, addr + offs, + AWE_PATCH_INFO_SIZE - offs)) + return -EFAULT; + + count -= AWE_PATCH_INFO_SIZE; + if (count < patch.len) { + printk(KERN_WARNING "AWE32: sample: Patch record too short (%d<%d)\n", + count, patch.len); + return -EINVAL; + } + + switch (patch.type) { + case AWE_LOAD_INFO: + rc = awe_load_info(&patch, addr, count); + break; + case AWE_LOAD_DATA: + rc = awe_load_data(&patch, addr, count); + break; + case AWE_OPEN_PATCH: + rc = awe_open_patch(&patch, addr, count); + break; + case AWE_CLOSE_PATCH: + rc = awe_close_patch(&patch, addr, count); + break; + case AWE_UNLOAD_PATCH: + rc = awe_unload_patch(&patch, addr, count); + break; + case AWE_REPLACE_DATA: + rc = awe_replace_data(&patch, addr, count); + break; + case AWE_MAP_PRESET: + rc = awe_load_map(&patch, addr, count); + break; + /* case AWE_PROBE_INFO: + rc = awe_probe_info(&patch, addr, count); + break;*/ + case AWE_PROBE_DATA: + rc = awe_probe_data(&patch, addr, count); + break; + case AWE_REMOVE_INFO: + rc = awe_remove_info(&patch, addr, count); + break; + case AWE_LOAD_CHORUS_FX: + rc = awe_load_chorus_fx(&patch, addr, count); + break; + case AWE_LOAD_REVERB_FX: + rc = awe_load_reverb_fx(&patch, addr, count); + break; + + default: + printk(KERN_WARNING "AWE32 Error: unknown patch format type %d\n", + patch.type); + rc = -EINVAL; + } + + return rc; +} + + +/* create an sf list record */ +static int +awe_create_sf(int type, char *name) +{ + sf_list *rec; + + /* terminate sounds */ + awe_reset(0); + rec = (sf_list *)kmalloc(sizeof(*rec), GFP_KERNEL); + if (rec == NULL) + return 1; /* no memory */ + rec->sf_id = current_sf_id + 1; + rec->type = type; + if (/*current_sf_id == 0 ||*/ (type & AWE_PAT_LOCKED) != 0) + locked_sf_id = current_sf_id + 1; + rec->num_info = awe_free_info(); + rec->num_sample = awe_free_sample(); + rec->mem_ptr = awe_free_mem_ptr(); + rec->infos = rec->last_infos = NULL; + rec->samples = rec->last_samples = NULL; + + /* add to linked-list */ + rec->next = NULL; + rec->prev = sftail; + if (sftail) + sftail->next = rec; + else + sfhead = rec; + sftail = rec; + current_sf_id++; + +#ifdef AWE_ALLOW_SAMPLE_SHARING + rec->shared = NULL; + if (name) + memcpy(rec->name, name, AWE_PATCH_NAME_LEN); + else + strcpy(rec->name, "*TEMPORARY*"); + if (current_sf_id > 1 && name && (type & AWE_PAT_SHARED) != 0) { + /* is the current font really a shared font? */ + if (is_shared_sf(rec->name)) { + /* check if the shared font is already installed */ + sf_list *p; + for (p = rec->prev; p; p = p->prev) { + if (is_identical_name(rec->name, p)) { + rec->shared = p; + break; + } + } + } + } +#endif /* allow sharing */ + + return 0; +} + + +#ifdef AWE_ALLOW_SAMPLE_SHARING + +/* check if the given name is a valid shared name */ +#define ASC_TO_KEY(c) ((c) - 'A' + 1) +static int is_shared_sf(unsigned char *name) +{ + static unsigned char id_head[4] = { + ASC_TO_KEY('A'), ASC_TO_KEY('W'), ASC_TO_KEY('E'), + AWE_MAJOR_VERSION, + }; + if (memcmp(name, id_head, 4) == 0) + return TRUE; + return FALSE; +} + +/* check if the given name matches to the existing list */ +static int is_identical_name(unsigned char *name, sf_list *p) +{ + char *id = p->name; + if (is_shared_sf(id) && memcmp(id, name, AWE_PATCH_NAME_LEN) == 0) + return TRUE; + return FALSE; +} + +/* check if the given voice info exists */ +static int info_duplicated(sf_list *sf, awe_voice_list *rec) +{ + /* search for all sharing lists */ + for (; sf; sf = sf->shared) { + awe_voice_list *p; + for (p = sf->infos; p; p = p->next) { + if (p->type == V_ST_NORMAL && + p->bank == rec->bank && + p->instr == rec->instr && + p->v.low == rec->v.low && + p->v.high == rec->v.high && + p->v.sample == rec->v.sample) + return TRUE; + } + } + return FALSE; +} + +#endif /* AWE_ALLOW_SAMPLE_SHARING */ + + +/* free sf_list record */ +/* linked-list in this function is not cared */ +static void +awe_free_sf(sf_list *sf) +{ + if (sf->infos) { + awe_voice_list *p, *next; + for (p = sf->infos; p; p = next) { + next = p->next; + kfree(p); + } + } + if (sf->samples) { + awe_sample_list *p, *next; + for (p = sf->samples; p; p = next) { + next = p->next; + kfree(p); + } + } + kfree(sf); +} + + +/* open patch; create sf list and set opened flag */ +static int +awe_open_patch(awe_patch_info *patch, const char *addr, int count) +{ + awe_open_parm parm; + int shared; + + if (copy_from_user(&parm, addr + AWE_PATCH_INFO_SIZE, sizeof(parm))) + return -EFAULT; + shared = FALSE; + +#ifdef AWE_ALLOW_SAMPLE_SHARING + if (sftail && (parm.type & AWE_PAT_SHARED) != 0) { + /* is the previous font the same font? */ + if (is_identical_name(parm.name, sftail)) { + /* then append to the previous */ + shared = TRUE; + awe_reset(0); + if (parm.type & AWE_PAT_LOCKED) + locked_sf_id = current_sf_id; + } + } +#endif /* allow sharing */ + if (! shared) { + if (awe_create_sf(parm.type, parm.name)) { + printk(KERN_ERR "AWE32: can't open: failed to alloc new list\n"); + return -ENOMEM; + } + } + patch_opened = TRUE; + return current_sf_id; +} + +/* check if the patch is already opened */ +static sf_list * +check_patch_opened(int type, char *name) +{ + if (! patch_opened) { + if (awe_create_sf(type, name)) { + printk(KERN_ERR "AWE32: failed to alloc new list\n"); + return NULL; + } + patch_opened = TRUE; + return sftail; + } + return sftail; +} + +/* close the patch; if no voice is loaded, remove the patch */ +static int +awe_close_patch(awe_patch_info *patch, const char *addr, int count) +{ + if (patch_opened && sftail) { + /* if no voice is loaded, release the current patch */ + if (sftail->infos == NULL) { + awe_reset(0); + awe_remove_samples(current_sf_id - 1); + } + } + patch_opened = 0; + return 0; +} + + +/* remove the latest patch */ +static int +awe_unload_patch(awe_patch_info *patch, const char *addr, int count) +{ + if (current_sf_id > 0 && current_sf_id > locked_sf_id) { + awe_reset(0); + awe_remove_samples(current_sf_id - 1); + } + return 0; +} + +/* allocate voice info list records */ +static awe_voice_list * +alloc_new_info(void) +{ + awe_voice_list *newlist; + + newlist = (awe_voice_list *)kmalloc(sizeof(*newlist), GFP_KERNEL); + if (newlist == NULL) { + printk(KERN_ERR "AWE32: can't alloc info table\n"); + return NULL; + } + return newlist; +} + +/* allocate sample info list records */ +static awe_sample_list * +alloc_new_sample(void) +{ + awe_sample_list *newlist; + + newlist = (awe_sample_list *)kmalloc(sizeof(*newlist), GFP_KERNEL); + if (newlist == NULL) { + printk(KERN_ERR "AWE32: can't alloc sample table\n"); + return NULL; + } + return newlist; +} + +/* load voice map */ +static int +awe_load_map(awe_patch_info *patch, const char *addr, int count) +{ + awe_voice_map map; + awe_voice_list *rec, *p; + sf_list *sf; + + /* get the link info */ + if (count < sizeof(map)) { + printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); + return -EINVAL; + } + if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map))) + return -EFAULT; + + /* check if the identical mapping already exists */ + p = awe_search_instr(map.map_bank, map.map_instr, map.map_key); + for (; p; p = p->next_instr) { + if (p->type == V_ST_MAPPED && + p->v.start == map.src_instr && + p->v.end == map.src_bank && + p->v.fixkey == map.src_key) + return 0; /* already present! */ + } + + if ((sf = check_patch_opened(AWE_PAT_TYPE_MAP, NULL)) == NULL) + return -ENOMEM; + + if ((rec = alloc_new_info()) == NULL) + return -ENOMEM; + + rec->bank = map.map_bank; + rec->instr = map.map_instr; + rec->type = V_ST_MAPPED; + rec->disabled = FALSE; + awe_init_voice_info(&rec->v); + if (map.map_key >= 0) { + rec->v.low = map.map_key; + rec->v.high = map.map_key; + } + rec->v.start = map.src_instr; + rec->v.end = map.src_bank; + rec->v.fixkey = map.src_key; + add_sf_info(sf, rec); + add_info_list(rec); + + return 0; +} + +#if 0 +/* probe preset in the current list -- nothing to be loaded */ +static int +awe_probe_info(awe_patch_info *patch, const char *addr, int count) +{ +#ifdef AWE_ALLOW_SAMPLE_SHARING + awe_voice_map map; + awe_voice_list *p; + + if (! patch_opened) + return -EINVAL; + + /* get the link info */ + if (count < sizeof(map)) { + printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); + return -EINVAL; + } + if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map))) + return -EFAULT; + + /* check if the identical mapping already exists */ + if (sftail == NULL) + return -EINVAL; + p = awe_search_instr(map.src_bank, map.src_instr, map.src_key); + for (; p; p = p->next_instr) { + if (p->type == V_ST_NORMAL && + is_identical_holder(p->holder, sftail) && + p->v.low <= map.src_key && + p->v.high >= map.src_key) + return 0; /* already present! */ + } +#endif /* allow sharing */ + return -EINVAL; +} +#endif + +/* probe sample in the current list -- nothing to be loaded */ +static int +awe_probe_data(awe_patch_info *patch, const char *addr, int count) +{ +#ifdef AWE_ALLOW_SAMPLE_SHARING + if (! patch_opened) + return -EINVAL; + + /* search the specified sample by optarg */ + if (search_sample_index(sftail, patch->optarg) != NULL) + return 0; +#endif /* allow sharing */ + return -EINVAL; +} + + +/* remove the present instrument layers */ +static int +remove_info(sf_list *sf, int bank, int instr) +{ + awe_voice_list *prev, *next, *p; + int removed = 0; + + prev = NULL; + for (p = sf->infos; p; p = next) { + next = p->next; + if (p->type == V_ST_NORMAL && + p->bank == bank && p->instr == instr) { + /* remove this layer */ + if (prev) + prev->next = next; + else + sf->infos = next; + if (p == sf->last_infos) + sf->last_infos = prev; + sf->num_info--; + removed++; + kfree(p); + } else + prev = p; + } + if (removed) + rebuild_preset_list(); + return removed; +} + +/* load voice information data */ +static int +awe_load_info(awe_patch_info *patch, const char *addr, int count) +{ + int offset; + awe_voice_rec_hdr hdr; + int i; + int total_size; + sf_list *sf; + awe_voice_list *rec; + + if (count < AWE_VOICE_REC_SIZE) { + printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); + return -EINVAL; + } + + offset = AWE_PATCH_INFO_SIZE; + if (copy_from_user((char*)&hdr, addr + offset, AWE_VOICE_REC_SIZE)) + return -EFAULT; + offset += AWE_VOICE_REC_SIZE; + + if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { + printk(KERN_WARNING "AWE32 Error: Invalid voice number %d\n", hdr.nvoices); + return -EINVAL; + } + total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices; + if (count < total_size) { + printk(KERN_WARNING "AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n", + count, hdr.nvoices); + return -EINVAL; + } + + if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL) + return -ENOMEM; + + switch (hdr.write_mode) { + case AWE_WR_EXCLUSIVE: + /* exclusive mode - if the instrument already exists, + return error */ + for (rec = sf->infos; rec; rec = rec->next) { + if (rec->type == V_ST_NORMAL && + rec->bank == hdr.bank && + rec->instr == hdr.instr) + return -EINVAL; + } + break; + case AWE_WR_REPLACE: + /* replace mode - remove the instrument if it already exists */ + remove_info(sf, hdr.bank, hdr.instr); + break; + } + + /* append new layers */ + for (i = 0; i < hdr.nvoices; i++) { + rec = alloc_new_info(); + if (rec == NULL) + return -ENOMEM; + + rec->bank = hdr.bank; + rec->instr = hdr.instr; + rec->type = V_ST_NORMAL; + rec->disabled = FALSE; + + /* copy awe_voice_info parameters */ + if (copy_from_user(&rec->v, addr + offset, AWE_VOICE_INFO_SIZE)) { + kfree(rec); + return -EFAULT; + } + offset += AWE_VOICE_INFO_SIZE; +#ifdef AWE_ALLOW_SAMPLE_SHARING + if (sf && sf->shared) { + if (info_duplicated(sf, rec)) { + kfree(rec); + continue; + } + } +#endif /* allow sharing */ + if (rec->v.mode & AWE_MODE_INIT_PARM) + awe_init_voice_parm(&rec->v.parm); + add_sf_info(sf, rec); + awe_set_sample(rec); + add_info_list(rec); + } + + return 0; +} + + +/* remove instrument layers */ +static int +awe_remove_info(awe_patch_info *patch, const char *addr, int count) +{ + unsigned char bank, instr; + sf_list *sf; + + if (! patch_opened || (sf = sftail) == NULL) { + printk(KERN_WARNING "AWE32: remove_info: patch not opened\n"); + return -EINVAL; + } + + bank = ((unsigned short)patch->optarg >> 8) & 0xff; + instr = (unsigned short)patch->optarg & 0xff; + if (! remove_info(sf, bank, instr)) + return -EINVAL; + return 0; +} + + +/* load wave sample data */ +static int +awe_load_data(awe_patch_info *patch, const char *addr, int count) +{ + int offset, size; + int rc; + awe_sample_info tmprec; + awe_sample_list *rec; + sf_list *sf; + + if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL) + return -ENOMEM; + + size = (count - AWE_SAMPLE_INFO_SIZE) / 2; + offset = AWE_PATCH_INFO_SIZE; + if (copy_from_user(&tmprec, addr + offset, AWE_SAMPLE_INFO_SIZE)) + return -EFAULT; + offset += AWE_SAMPLE_INFO_SIZE; + if (size != tmprec.size) { + printk(KERN_WARNING "AWE32: load: sample size differed (%d != %d)\n", + tmprec.size, size); + return -EINVAL; + } + + if (search_sample_index(sf, tmprec.sample) != NULL) { +#ifdef AWE_ALLOW_SAMPLE_SHARING + /* if shared sample, skip this data */ + if (sf->type & AWE_PAT_SHARED) + return 0; +#endif /* allow sharing */ + DEBUG(1,printk("AWE32: sample data %d already present\n", tmprec.sample)); + return -EINVAL; + } + + if ((rec = alloc_new_sample()) == NULL) + return -ENOMEM; + + memcpy(&rec->v, &tmprec, sizeof(tmprec)); + + if (rec->v.size > 0) { + if ((rc = awe_write_wave_data(addr, offset, rec, -1)) < 0) { + kfree(rec); + return rc; + } + sf->mem_ptr += rc; + } + + add_sf_sample(sf, rec); + return 0; +} + + +/* replace wave sample data */ +static int +awe_replace_data(awe_patch_info *patch, const char *addr, int count) +{ + int offset; + int size; + int rc; + int channels; + awe_sample_info cursmp; + int save_mem_ptr; + sf_list *sf; + awe_sample_list *rec; + + if (! patch_opened || (sf = sftail) == NULL) { + printk(KERN_WARNING "AWE32: replace: patch not opened\n"); + return -EINVAL; + } + + size = (count - AWE_SAMPLE_INFO_SIZE) / 2; + offset = AWE_PATCH_INFO_SIZE; + if (copy_from_user(&cursmp, addr + offset, AWE_SAMPLE_INFO_SIZE)) + return -EFAULT; + offset += AWE_SAMPLE_INFO_SIZE; + if (cursmp.size == 0 || size != cursmp.size) { + printk(KERN_WARNING "AWE32: replace: invalid sample size (%d!=%d)\n", + cursmp.size, size); + return -EINVAL; + } + channels = patch->optarg; + if (channels <= 0 || channels > AWE_NORMAL_VOICES) { + printk(KERN_WARNING "AWE32: replace: invalid channels %d\n", channels); + return -EINVAL; + } + + for (rec = sf->samples; rec; rec = rec->next) { + if (rec->v.sample == cursmp.sample) + break; + } + if (rec == NULL) { + printk(KERN_WARNING "AWE32: replace: cannot find existing sample data %d\n", + cursmp.sample); + return -EINVAL; + } + + if (rec->v.size != cursmp.size) { + printk(KERN_WARNING "AWE32: replace: exiting size differed (%d!=%d)\n", + rec->v.size, cursmp.size); + return -EINVAL; + } + + save_mem_ptr = awe_free_mem_ptr(); + sftail->mem_ptr = rec->v.start - awe_mem_start; + memcpy(&rec->v, &cursmp, sizeof(cursmp)); + rec->v.sf_id = current_sf_id; + if ((rc = awe_write_wave_data(addr, offset, rec, channels)) < 0) + return rc; + sftail->mem_ptr = save_mem_ptr; + + return 0; +} + + +/*----------------------------------------------------------------*/ + +static const char *readbuf_addr; +static int readbuf_offs; +static int readbuf_flags; + +/* initialize read buffer */ +static int +readbuf_init(const char *addr, int offset, awe_sample_info *sp) +{ + readbuf_addr = addr; + readbuf_offs = offset; + readbuf_flags = sp->mode_flags; + return 0; +} + +/* read directly from user buffer */ +static unsigned short +readbuf_word(int pos) +{ + unsigned short c; + /* read from user buffer */ + if (readbuf_flags & AWE_SAMPLE_8BITS) { + unsigned char cc; + get_user(cc, (unsigned char*)(readbuf_addr + readbuf_offs + pos)); + c = (unsigned short)cc << 8; /* convert 8bit -> 16bit */ + } else { + get_user(c, (unsigned short*)(readbuf_addr + readbuf_offs + pos * 2)); + } + if (readbuf_flags & AWE_SAMPLE_UNSIGNED) + c ^= 0x8000; /* unsigned -> signed */ + return c; +} + +#define readbuf_word_cache readbuf_word +#define readbuf_end() /**/ + +/*----------------------------------------------------------------*/ + +#define BLANK_LOOP_START 8 +#define BLANK_LOOP_END 40 +#define BLANK_LOOP_SIZE 48 + +/* loading onto memory - return the actual written size */ +static int +awe_write_wave_data(const char *addr, int offset, awe_sample_list *list, int channels) +{ + int i, truesize, dram_offset; + awe_sample_info *sp = &list->v; + int rc; + + /* be sure loop points start < end */ + if (sp->loopstart > sp->loopend) { + int tmp = sp->loopstart; + sp->loopstart = sp->loopend; + sp->loopend = tmp; + } + + /* compute true data size to be loaded */ + truesize = sp->size; + if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) + truesize += sp->loopend - sp->loopstart; + if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) + truesize += BLANK_LOOP_SIZE; + if (awe_free_mem_ptr() + truesize >= memsize/2) { + DEBUG(-1,printk("AWE32 Error: Sample memory full\n")); + return -ENOSPC; + } + + /* recalculate address offset */ + sp->end -= sp->start; + sp->loopstart -= sp->start; + sp->loopend -= sp->start; + + dram_offset = awe_free_mem_ptr() + awe_mem_start; + sp->start = dram_offset; + sp->end += dram_offset; + sp->loopstart += dram_offset; + sp->loopend += dram_offset; + + /* set the total size (store onto obsolete checksum value) */ + if (sp->size == 0) + sp->checksum = 0; + else + sp->checksum = truesize; + + if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0) + return rc; + + if (readbuf_init(addr, offset, sp) < 0) + return -ENOSPC; + + for (i = 0; i < sp->size; i++) { + unsigned short c; + c = readbuf_word(i); + awe_write_dram(c); + if (i == sp->loopend && + (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) { + int looplen = sp->loopend - sp->loopstart; + /* copy reverse loop */ + int k; + for (k = 1; k <= looplen; k++) { + c = readbuf_word_cache(i - k); + awe_write_dram(c); + } + if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) { + sp->end += looplen; + } else { + sp->start += looplen; + sp->end += looplen; + } + } + } + readbuf_end(); + + /* if no blank loop is attached in the sample, add it */ + if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) { + for (i = 0; i < BLANK_LOOP_SIZE; i++) + awe_write_dram(0); + if (sp->mode_flags & AWE_SAMPLE_SINGLESHOT) { + sp->loopstart = sp->end + BLANK_LOOP_START; + sp->loopend = sp->end + BLANK_LOOP_END; + } + } + + awe_close_dram(); + + /* initialize FM */ + awe_init_fm(); + + return truesize; +} + + +/*----------------------------------------------------------------*/ + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* calculate GUS envelope time: + * is this correct? i have no idea.. + */ +static int +calc_gus_envelope_time(int rate, int start, int end) +{ + int r, p, t; + r = (3 - ((rate >> 6) & 3)) * 3; + p = rate & 0x3f; + t = end - start; + if (t < 0) t = -t; + if (13 > r) + t = t << (13 - r); + else + t = t >> (r - 13); + return (t * 10) / (p * 441); +} + +#define calc_gus_sustain(val) (0x7f - vol_table[(val)/2]) +#define calc_gus_attenuation(val) vol_table[(val)/2] + +/* load GUS patch */ +static int +awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag) +{ + struct patch_info patch; + awe_voice_info *rec; + awe_sample_info *smp; + awe_voice_list *vrec; + awe_sample_list *smprec; + int sizeof_patch; + int note, rc; + sf_list *sf; + + sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */ + if (size < sizeof_patch) { + printk(KERN_WARNING "AWE32 Error: Patch header too short\n"); + return -EINVAL; + } + if (copy_from_user(((char*)&patch) + offs, addr + offs, sizeof_patch - offs)) + return -EFAULT; + size -= sizeof_patch; + if (size < patch.len) { + printk(KERN_WARNING "AWE32 Error: Patch record too short (%d<%d)\n", + size, patch.len); + return -EINVAL; + } + if ((sf = check_patch_opened(AWE_PAT_TYPE_GUS, NULL)) == NULL) + return -ENOMEM; + if ((smprec = alloc_new_sample()) == NULL) + return -ENOMEM; + if ((vrec = alloc_new_info()) == NULL) { + kfree(smprec); + return -ENOMEM; + } + + smp = &smprec->v; + smp->sample = sf->num_sample; + smp->start = 0; + smp->end = patch.len; + smp->loopstart = patch.loop_start; + smp->loopend = patch.loop_end; + smp->size = patch.len; + + /* set up mode flags */ + smp->mode_flags = 0; + if (!(patch.mode & WAVE_16_BITS)) + smp->mode_flags |= AWE_SAMPLE_8BITS; + if (patch.mode & WAVE_UNSIGNED) + smp->mode_flags |= AWE_SAMPLE_UNSIGNED; + smp->mode_flags |= AWE_SAMPLE_NO_BLANK; + if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) + smp->mode_flags |= AWE_SAMPLE_SINGLESHOT; + if (patch.mode & WAVE_BIDIR_LOOP) + smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP; + if (patch.mode & WAVE_LOOP_BACK) + smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP; + + DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags)); + if (patch.mode & WAVE_16_BITS) { + /* convert to word offsets */ + smp->size /= 2; + smp->end /= 2; + smp->loopstart /= 2; + smp->loopend /= 2; + } + smp->checksum_flag = 0; + smp->checksum = 0; + + if ((rc = awe_write_wave_data(addr, sizeof_patch, smprec, -1)) < 0) + return rc; + sf->mem_ptr += rc; + add_sf_sample(sf, smprec); + + /* set up voice info */ + rec = &vrec->v; + awe_init_voice_info(rec); + rec->sample = sf->num_info; /* the last sample */ + rec->rate_offset = calc_rate_offset(patch.base_freq); + note = freq_to_note(patch.base_note); + rec->root = note / 100; + rec->tune = -(note % 100); + rec->low = freq_to_note(patch.low_note) / 100; + rec->high = freq_to_note(patch.high_note) / 100; + DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n", + rec->rate_offset, note, + rec->low, rec->high, + patch.low_note, patch.high_note)); + /* panning position; -128 - 127 => 0-127 */ + rec->pan = (patch.panning + 128) / 2; + + /* detuning is ignored */ + /* 6points volume envelope */ + if (patch.mode & WAVE_ENVELOPES) { + int attack, hold, decay, release; + attack = calc_gus_envelope_time + (patch.env_rate[0], 0, patch.env_offset[0]); + hold = calc_gus_envelope_time + (patch.env_rate[1], patch.env_offset[0], + patch.env_offset[1]); + decay = calc_gus_envelope_time + (patch.env_rate[2], patch.env_offset[1], + patch.env_offset[2]); + release = calc_gus_envelope_time + (patch.env_rate[3], patch.env_offset[1], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[4], patch.env_offset[3], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[5], patch.env_offset[4], + patch.env_offset[5]); + rec->parm.volatkhld = (calc_parm_hold(hold) << 8) | + calc_parm_attack(attack); + rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | + calc_parm_decay(decay); + rec->parm.volrelease = 0x8000 | calc_parm_decay(release); + DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release)); + rec->attenuation = calc_gus_attenuation(patch.env_offset[0]); + } + + /* tremolo effect */ + if (patch.mode & WAVE_TREMOLO) { + int rate = (patch.tremolo_rate * 1000 / 38) / 42; + rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; + DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n", + patch.tremolo_rate, patch.tremolo_depth, + rec->parm.tremfrq)); + } + /* vibrato effect */ + if (patch.mode & WAVE_VIBRATO) { + int rate = (patch.vibrato_rate * 1000 / 38) / 42; + rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; + DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n", + patch.tremolo_rate, patch.tremolo_depth, + rec->parm.tremfrq)); + } + + /* scale_freq, scale_factor, volume, and fractions not implemented */ + + /* append to the tail of the list */ + vrec->bank = ctrls[AWE_MD_GUS_BANK]; + vrec->instr = patch.instr_no; + vrec->disabled = FALSE; + vrec->type = V_ST_NORMAL; + + add_sf_info(sf, vrec); + add_info_list(vrec); + + /* set the voice index */ + awe_set_sample(vrec); + + return 0; +} + +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + +/* + * sample and voice list handlers + */ + +/* append this to the current sf list */ +static void add_sf_info(sf_list *sf, awe_voice_list *rec) +{ + if (sf == NULL) + return; + rec->holder = sf; + rec->v.sf_id = sf->sf_id; + if (sf->last_infos) + sf->last_infos->next = rec; + else + sf->infos = rec; + sf->last_infos = rec; + rec->next = NULL; + sf->num_info++; +} + +/* prepend this sample to sf list */ +static void add_sf_sample(sf_list *sf, awe_sample_list *rec) +{ + if (sf == NULL) + return; + rec->holder = sf; + rec->v.sf_id = sf->sf_id; + if (sf->last_samples) + sf->last_samples->next = rec; + else + sf->samples = rec; + sf->last_samples = rec; + rec->next = NULL; + sf->num_sample++; +} + +/* purge the old records which don't belong with the same file id */ +static void purge_old_list(awe_voice_list *rec, awe_voice_list *next) +{ + rec->next_instr = next; + if (rec->bank == AWE_DRUM_BANK) { + /* remove samples with the same note range */ + awe_voice_list *cur, *prev = rec; + int low = rec->v.low; + int high = rec->v.high; + for (cur = next; cur; cur = cur->next_instr) { + if (cur->v.low == low && + cur->v.high == high && + ! is_identical_holder(cur->holder, rec->holder)) + prev->next_instr = cur->next_instr; + else + prev = cur; + } + } else { + if (! is_identical_holder(next->holder, rec->holder)) + /* remove all samples */ + rec->next_instr = NULL; + } +} + +/* prepend to top of the preset table */ +static void add_info_list(awe_voice_list *rec) +{ + awe_voice_list *prev, *cur; + int key; + + if (rec->disabled) + return; + + key = awe_search_key(rec->bank, rec->instr, rec->v.low); + prev = NULL; + for (cur = preset_table[key]; cur; cur = cur->next_bank) { + /* search the first record with the same bank number */ + if (cur->instr == rec->instr && cur->bank == rec->bank) { + /* replace the list with the new record */ + rec->next_bank = cur->next_bank; + if (prev) + prev->next_bank = rec; + else + preset_table[key] = rec; + purge_old_list(rec, cur); + return; + } + prev = cur; + } + + /* this is the first bank record.. just add this */ + rec->next_instr = NULL; + rec->next_bank = preset_table[key]; + preset_table[key] = rec; +} + +/* remove samples later than the specified sf_id */ +static void +awe_remove_samples(int sf_id) +{ + sf_list *p, *prev; + + if (sf_id <= 0) { + awe_reset_samples(); + return; + } + /* already removed? */ + if (current_sf_id <= sf_id) + return; + + for (p = sftail; p; p = prev) { + if (p->sf_id <= sf_id) + break; + prev = p->prev; + awe_free_sf(p); + } + sftail = p; + if (sftail) { + sf_id = sftail->sf_id; + sftail->next = NULL; + } else { + sf_id = 0; + sfhead = NULL; + } + current_sf_id = sf_id; + if (locked_sf_id > sf_id) + locked_sf_id = sf_id; + + rebuild_preset_list(); +} + +/* rebuild preset search list */ +static void rebuild_preset_list(void) +{ + sf_list *p; + awe_voice_list *rec; + + memset(preset_table, 0, sizeof(preset_table)); + + for (p = sfhead; p; p = p->next) { + for (rec = p->infos; rec; rec = rec->next) + add_info_list(rec); + } +} + +/* compare the given sf_id pair */ +static int is_identical_holder(sf_list *sf1, sf_list *sf2) +{ + if (sf1 == NULL || sf2 == NULL) + return FALSE; + if (sf1 == sf2) + return TRUE; +#ifdef AWE_ALLOW_SAMPLE_SHARING + { + /* compare with the sharing id */ + sf_list *p; + int counter = 0; + if (sf1->sf_id < sf2->sf_id) { /* make sure id1 > id2 */ + sf_list *tmp; tmp = sf1; sf1 = sf2; sf2 = tmp; + } + for (p = sf1->shared; p; p = p->shared) { + if (counter++ > current_sf_id) + break; /* strange sharing loop.. quit */ + if (p == sf2) + return TRUE; + } + } +#endif /* allow sharing */ + return FALSE; +} + +/* search the sample index matching with the given sample id */ +static awe_sample_list * +search_sample_index(sf_list *sf, int sample) +{ + awe_sample_list *p; +#ifdef AWE_ALLOW_SAMPLE_SHARING + int counter = 0; + while (sf) { + for (p = sf->samples; p; p = p->next) { + if (p->v.sample == sample) + return p; + } + sf = sf->shared; + if (counter++ > current_sf_id) + break; /* strange sharing loop.. quit */ + } +#else + if (sf) { + for (p = sf->samples; p; p = p->next) { + if (p->v.sample == sample) + return p; + } + } +#endif + return NULL; +} + +/* search the specified sample */ +/* non-zero = found */ +static short +awe_set_sample(awe_voice_list *rec) +{ + awe_sample_list *smp; + awe_voice_info *vp = &rec->v; + + vp->index = 0; + if ((smp = search_sample_index(rec->holder, vp->sample)) == NULL) + return 0; + + /* set the actual sample offsets */ + vp->start += smp->v.start; + vp->end += smp->v.end; + vp->loopstart += smp->v.loopstart; + vp->loopend += smp->v.loopend; + /* copy mode flags */ + vp->mode = smp->v.mode_flags; + /* set flag */ + vp->index = 1; + + return 1; +} + + +/* + * voice allocation + */ + +/* look for all voices associated with the specified note & velocity */ +static int +awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, + awe_voice_info **vlist) +{ + int nvoices; + + nvoices = 0; + for (; rec; rec = rec->next_instr) { + if (note >= rec->v.low && + note <= rec->v.high && + velocity >= rec->v.vellow && + velocity <= rec->v.velhigh) { + if (rec->type == V_ST_MAPPED) { + /* mapper */ + vlist[0] = &rec->v; + return -1; + } + vlist[nvoices++] = &rec->v; + if (nvoices >= AWE_MAX_VOICES) + break; + } + } + return nvoices; +} + +/* store the voice list from the specified note and velocity. + if the preset is mapped, seek for the destination preset, and rewrite + the note number if necessary. + */ +static int +really_alloc_voices(int bank, int instr, int *note, int velocity, awe_voice_info **vlist) +{ + int nvoices; + awe_voice_list *vrec; + int level = 0; + + for (;;) { + vrec = awe_search_instr(bank, instr, *note); + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + if (nvoices == 0) { + if (bank == AWE_DRUM_BANK) + /* search default drumset */ + vrec = awe_search_instr(bank, ctrls[AWE_MD_DEF_DRUM], *note); + else + /* search default preset */ + vrec = awe_search_instr(ctrls[AWE_MD_DEF_BANK], instr, *note); + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + } + if (nvoices == 0) { + if (bank == AWE_DRUM_BANK && ctrls[AWE_MD_DEF_DRUM] != 0) + /* search default drumset */ + vrec = awe_search_instr(bank, 0, *note); + else if (bank != AWE_DRUM_BANK && ctrls[AWE_MD_DEF_BANK] != 0) + /* search default preset */ + vrec = awe_search_instr(0, instr, *note); + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + } + if (nvoices < 0) { /* mapping */ + int key = vlist[0]->fixkey; + instr = vlist[0]->start; + bank = vlist[0]->end; + if (level++ > 5) { + printk(KERN_ERR "AWE32: too deep mapping level\n"); + return 0; + } + if (key >= 0) + *note = key; + } else + break; + } + + return nvoices; +} + +/* allocate voices corresponding note and velocity; supports multiple insts. */ +static void +awe_alloc_multi_voices(int ch, int note, int velocity, int key) +{ + int i, v, nvoices, bank; + awe_voice_info *vlist[AWE_MAX_VOICES]; + + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) + bank = AWE_DRUM_BANK; /* always search drumset */ + else + bank = channels[ch].bank; + + /* check the possible voices; note may be changeable if mapped */ + nvoices = really_alloc_voices(bank, channels[ch].instr, + ¬e, velocity, vlist); + + /* set the voices */ + current_alloc_time++; + for (i = 0; i < nvoices; i++) { + v = awe_clear_voice(); + voices[v].key = key; + voices[v].ch = ch; + voices[v].note = note; + voices[v].velocity = velocity; + voices[v].time = current_alloc_time; + voices[v].cinfo = &channels[ch]; + voices[v].sample = vlist[i]; + voices[v].state = AWE_ST_MARK; + voices[v].layer = nvoices - i - 1; /* in reverse order */ + } + + /* clear the mark in allocated voices */ + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].state == AWE_ST_MARK) + voices[i].state = AWE_ST_OFF; + + } +} + + +/* search an empty voice. + if no empty voice is found, at least terminate a voice + */ +static int +awe_clear_voice(void) +{ + enum { + OFF=0, RELEASED, SUSTAINED, PLAYING, END + }; + struct voice_candidate_t { + int best; + int time; + int vtarget; + } candidate[END]; + int i, type, vtarget; + + vtarget = 0xffff; + for (type = OFF; type < END; type++) { + candidate[type].best = -1; + candidate[type].time = current_alloc_time + 1; + candidate[type].vtarget = vtarget; + } + + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].state & AWE_ST_OFF) + type = OFF; + else if (voices[i].state & AWE_ST_RELEASED) + type = RELEASED; + else if (voices[i].state & AWE_ST_SUSTAINED) + type = SUSTAINED; + else if (voices[i].state & ~AWE_ST_MARK) + type = PLAYING; + else + continue; +#ifdef AWE_CHECK_VTARGET + /* get current volume */ + vtarget = (awe_peek_dw(AWE_VTFT(i)) >> 16) & 0xffff; +#endif + if (candidate[type].best < 0 || + vtarget < candidate[type].vtarget || + (vtarget == candidate[type].vtarget && + voices[i].time < candidate[type].time)) { + candidate[type].best = i; + candidate[type].time = voices[i].time; + candidate[type].vtarget = vtarget; + } + } + + for (type = OFF; type < END; type++) { + if ((i = candidate[type].best) >= 0) { + if (voices[i].state != AWE_ST_OFF) + awe_terminate(i); + awe_voice_init(i, TRUE); + return i; + } + } + return 0; +} + + +/* search sample for the specified note & velocity and set it on the voice; + * note that voice is the voice index (not channel index) + */ +static void +awe_alloc_one_voice(int voice, int note, int velocity) +{ + int ch, nvoices, bank; + awe_voice_info *vlist[AWE_MAX_VOICES]; + + ch = voices[voice].ch; + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice)) + bank = AWE_DRUM_BANK; /* always search drumset */ + else + bank = voices[voice].cinfo->bank; + + nvoices = really_alloc_voices(bank, voices[voice].cinfo->instr, + ¬e, velocity, vlist); + if (nvoices > 0) { + voices[voice].time = ++current_alloc_time; + voices[voice].sample = vlist[0]; /* use the first one */ + voices[voice].layer = 0; + voices[voice].note = note; + voices[voice].velocity = velocity; + } +} + + +/* + * sequencer2 functions + */ + +/* search an empty voice; used by sequencer2 */ +static int +awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + playing_mode = AWE_PLAY_MULTI2; + awe_info.nr_voices = AWE_MAX_CHANNELS; + return awe_clear_voice(); +} + + +/* set up voice; used by sequencer2 */ +static void +awe_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info; + if (synth_devs[dev] == NULL || + (info = &synth_devs[dev]->chn_info[chn]) == NULL) + return; + + if (voice < 0 || voice >= awe_max_voices) + return; + + DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn)); + channels[chn].expression_vol = info->controllers[CTL_EXPRESSION]; + channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME]; + channels[chn].panning = + info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */ + channels[chn].bender = info->bender_value; /* zero center */ + channels[chn].bank = info->controllers[CTL_BANK_SELECT]; + channels[chn].sustained = info->controllers[CTL_SUSTAIN]; + if (info->controllers[CTL_EXT_EFF_DEPTH]) { + FX_SET(&channels[chn].fx, AWE_FX_REVERB, + info->controllers[CTL_EXT_EFF_DEPTH] * 2); + } + if (info->controllers[CTL_CHORUS_DEPTH]) { + FX_SET(&channels[chn].fx, AWE_FX_CHORUS, + info->controllers[CTL_CHORUS_DEPTH] * 2); + } + awe_set_instr(dev, chn, info->pgm_num); +} + + +#ifdef CONFIG_AWE32_MIXER +/* + * AWE32 mixer device control + */ + +static int awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg); + +static int my_mixerdev = -1; + +static struct mixer_operations awe_mixer_operations = { + owner: THIS_MODULE, + id: "AWE", + name: "AWE32 Equalizer", + ioctl: awe_mixer_ioctl, +}; + +static void __init attach_mixer(void) +{ + if ((my_mixerdev = sound_alloc_mixerdev()) >= 0) { + mixer_devs[my_mixerdev] = &awe_mixer_operations; + } +} + +static void __exit unload_mixer(void) +{ + if (my_mixerdev >= 0) + sound_unload_mixerdev(my_mixerdev); +} + +static int +awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int i, level, value; + + if (((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + level = *(int*)arg; + level = ((level & 0xff) + (level >> 8)) / 2; + DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level)); + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + switch (cmd & 0xff) { + case SOUND_MIXER_BASS: + value = level * 12 / 100; + if (value >= 12) + value = 11; + ctrls[AWE_MD_BASS_LEVEL] = value; + awe_update_equalizer(); + break; + case SOUND_MIXER_TREBLE: + value = level * 12 / 100; + if (value >= 12) + value = 11; + ctrls[AWE_MD_TREBLE_LEVEL] = value; + awe_update_equalizer(); + break; + case SOUND_MIXER_VOLUME: + level = level * 127 / 100; + if (level >= 128) level = 127; + atten_relative = FALSE; + atten_offset = vol_table[level]; + awe_update_volume(); + break; + } + } + switch (cmd & 0xff) { + case SOUND_MIXER_BASS: + level = ctrls[AWE_MD_BASS_LEVEL] * 100 / 24; + level = (level << 8) | level; + break; + case SOUND_MIXER_TREBLE: + level = ctrls[AWE_MD_TREBLE_LEVEL] * 100 / 24; + level = (level << 8) | level; + break; + case SOUND_MIXER_VOLUME: + value = atten_offset; + if (atten_relative) + value += ctrls[AWE_MD_ZERO_ATTEN]; + for (i = 127; i > 0; i--) { + if (value <= vol_table[i]) + break; + } + level = i * 100 / 127; + level = (level << 8) | level; + break; + case SOUND_MIXER_DEVMASK: + level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME; + break; + default: + level = 0; + break; + } + return *(int*)arg = level; +} +#endif /* CONFIG_AWE32_MIXER */ + + +/* + * initialization of Emu8000 + */ + +/* intiailize audio channels */ +static void +awe_init_audio(void) +{ + int ch; + + /* turn off envelope engines */ + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke(AWE_DCYSUSV(ch), 0x80); + } + + /* reset all other parameters to zero */ + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke(AWE_ENVVOL(ch), 0); + awe_poke(AWE_ENVVAL(ch), 0); + awe_poke(AWE_DCYSUS(ch), 0); + awe_poke(AWE_ATKHLDV(ch), 0); + awe_poke(AWE_LFO1VAL(ch), 0); + awe_poke(AWE_ATKHLD(ch), 0); + awe_poke(AWE_LFO2VAL(ch), 0); + awe_poke(AWE_IP(ch), 0); + awe_poke(AWE_IFATN(ch), 0); + awe_poke(AWE_PEFE(ch), 0); + awe_poke(AWE_FMMOD(ch), 0); + awe_poke(AWE_TREMFRQ(ch), 0); + awe_poke(AWE_FM2FRQ2(ch), 0); + awe_poke_dw(AWE_PTRX(ch), 0); + awe_poke_dw(AWE_VTFT(ch), 0); + awe_poke_dw(AWE_PSST(ch), 0); + awe_poke_dw(AWE_CSL(ch), 0); + awe_poke_dw(AWE_CCCA(ch), 0); + } + + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke_dw(AWE_CPF(ch), 0); + awe_poke_dw(AWE_CVCF(ch), 0); + } +} + + +/* initialize DMA address */ +static void +awe_init_dma(void) +{ + awe_poke_dw(AWE_SMALR, 0); + awe_poke_dw(AWE_SMARR, 0); + awe_poke_dw(AWE_SMALW, 0); + awe_poke_dw(AWE_SMARW, 0); +} + + +/* initialization arrays; from ADIP */ + +static unsigned short init1[128] = { + 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, + 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, + 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, + 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, + + 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, + 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, + 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, + 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, + + 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, + 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, + 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, + 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, + + 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, + 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, + 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, + 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, +}; + +static unsigned short init2[128] = { + 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, + 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, + 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, + 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, + + 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, + 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, + 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, + 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, + + 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, + 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, + 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, + 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, + + 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, + 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, + 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, + 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, +}; + +static unsigned short init3[128] = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, + 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, + 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, + 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +static unsigned short init4[128] = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, + 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, + 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, + 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + + +/* send initialization arrays to start up */ +static void +awe_init_array(void) +{ + awe_send_array(init1); + awe_wait(1024); + awe_send_array(init2); + awe_send_array(init3); + awe_poke_dw(AWE_HWCF4, 0); + awe_poke_dw(AWE_HWCF5, 0x83); + awe_poke_dw(AWE_HWCF6, 0x8000); + awe_send_array(init4); +} + +/* send an initialization array */ +static void +awe_send_array(unsigned short *data) +{ + int i; + unsigned short *p; + + p = data; + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT1(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT2(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT3(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT4(i), *p); +} + + +/* + * set up awe32 channels to some known state. + */ + +/* set the envelope & LFO parameters to the default values; see ADIP */ +static void +awe_tweak_voice(int i) +{ + /* set all mod/vol envelope shape to minimum */ + awe_poke(AWE_ENVVOL(i), 0x8000); + awe_poke(AWE_ENVVAL(i), 0x8000); + awe_poke(AWE_DCYSUS(i), 0x7F7F); + awe_poke(AWE_ATKHLDV(i), 0x7F7F); + awe_poke(AWE_ATKHLD(i), 0x7F7F); + awe_poke(AWE_PEFE(i), 0); /* mod envelope height to zero */ + awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */ + awe_poke(AWE_LFO2VAL(i), 0x8000); + awe_poke(AWE_IP(i), 0xE000); /* no pitch shift */ + awe_poke(AWE_IFATN(i), 0xFF00); /* volume to minimum */ + awe_poke(AWE_FMMOD(i), 0); + awe_poke(AWE_TREMFRQ(i), 0); + awe_poke(AWE_FM2FRQ2(i), 0); +} + +static void +awe_tweak(void) +{ + int i; + /* reset all channels */ + for (i = 0; i < awe_max_voices; i++) + awe_tweak_voice(i); +} + + +/* + * initializes the FM section of AWE32; + * see Vince Vu's unofficial AWE32 programming guide + */ + +static void +awe_init_fm(void) +{ +#ifndef AWE_ALWAYS_INIT_FM + /* if no extended memory is on board.. */ + if (memsize <= 0) + return; +#endif + DEBUG(3,printk("AWE32: initializing FM\n")); + + /* Initialize the last two channels for DRAM refresh and producing + the reverb and chorus effects for Yamaha OPL-3 synthesizer */ + + /* 31: FM left channel, 0xffffe0-0xffffe8 */ + awe_poke(AWE_DCYSUSV(30), 0x80); + awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */ + awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 | + (DEF_FM_CHORUS_DEPTH << 24)); + awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8)); + awe_poke_dw(AWE_CPF(30), 0); + awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3); + + /* 32: FM right channel, 0xfffff0-0xfffff8 */ + awe_poke(AWE_DCYSUSV(31), 0x80); + awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */ + awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 | + (DEF_FM_CHORUS_DEPTH << 24)); + awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8)); + awe_poke_dw(AWE_CPF(31), 0x8000); + awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3); + + /* skew volume & cutoff */ + awe_poke_dw(AWE_VTFT(30), 0x8000FFFF); + awe_poke_dw(AWE_VTFT(31), 0x8000FFFF); + + voices[30].state = AWE_ST_FM; + voices[31].state = AWE_ST_FM; + + /* change maximum channels to 30 */ + awe_max_voices = AWE_NORMAL_VOICES; + if (playing_mode == AWE_PLAY_DIRECT) + awe_info.nr_voices = awe_max_voices; + else + awe_info.nr_voices = AWE_MAX_CHANNELS; + voice_alloc->max_voice = awe_max_voices; +} + +/* + * AWE32 DRAM access routines + */ + +/* open DRAM write accessing mode */ +static int +awe_open_dram_for_write(int offset, int channels) +{ + int vidx[AWE_NORMAL_VOICES]; + int i; + + if (channels < 0 || channels >= AWE_NORMAL_VOICES) { + channels = AWE_NORMAL_VOICES; + for (i = 0; i < AWE_NORMAL_VOICES; i++) + vidx[i] = i; + } else { + for (i = 0; i < channels; i++) { + vidx[i] = awe_clear_voice(); + voices[vidx[i]].state = AWE_ST_MARK; + } + } + + /* use all channels for DMA transfer */ + for (i = 0; i < channels; i++) { + if (vidx[i] < 0) continue; + awe_poke(AWE_DCYSUSV(vidx[i]), 0x80); + awe_poke_dw(AWE_VTFT(vidx[i]), 0); + awe_poke_dw(AWE_CVCF(vidx[i]), 0); + awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000); + awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000); + awe_poke_dw(AWE_PSST(vidx[i]), 0); + awe_poke_dw(AWE_CSL(vidx[i]), 0); + awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000); + voices[vidx[i]].state = AWE_ST_DRAM; + } + /* point channels 31 & 32 to ROM samples for DRAM refresh */ + awe_poke_dw(AWE_VTFT(30), 0); + awe_poke_dw(AWE_PSST(30), 0x1d8); + awe_poke_dw(AWE_CSL(30), 0x1e0); + awe_poke_dw(AWE_CCCA(30), 0x1d8); + awe_poke_dw(AWE_VTFT(31), 0); + awe_poke_dw(AWE_PSST(31), 0x1d8); + awe_poke_dw(AWE_CSL(31), 0x1e0); + awe_poke_dw(AWE_CCCA(31), 0x1d8); + voices[30].state = AWE_ST_FM; + voices[31].state = AWE_ST_FM; + + /* if full bit is on, not ready to write on */ + if (awe_peek_dw(AWE_SMALW) & 0x80000000) { + for (i = 0; i < channels; i++) { + awe_poke_dw(AWE_CCCA(vidx[i]), 0); + voices[vidx[i]].state = AWE_ST_OFF; + } + printk("awe: not ready to write..\n"); + return -EPERM; + } + + /* set address to write */ + awe_poke_dw(AWE_SMALW, offset); + + return 0; +} + +/* open DRAM for RAM size detection */ +static void +awe_open_dram_for_check(void) +{ + int i; + for (i = 0; i < AWE_NORMAL_VOICES; i++) { + awe_poke(AWE_DCYSUSV(i), 0x80); + awe_poke_dw(AWE_VTFT(i), 0); + awe_poke_dw(AWE_CVCF(i), 0); + awe_poke_dw(AWE_PTRX(i), 0x40000000); + awe_poke_dw(AWE_CPF(i), 0x40000000); + awe_poke_dw(AWE_PSST(i), 0); + awe_poke_dw(AWE_CSL(i), 0); + if (i & 1) /* DMA write */ + awe_poke_dw(AWE_CCCA(i), 0x06000000); + else /* DMA read */ + awe_poke_dw(AWE_CCCA(i), 0x04000000); + voices[i].state = AWE_ST_DRAM; + } +} + + +/* close dram access */ +static void +awe_close_dram(void) +{ + int i; + /* wait until FULL bit in SMAxW register be false */ + for (i = 0; i < 10000; i++) { + if (!(awe_peek_dw(AWE_SMALW) & 0x80000000)) + break; + awe_wait(10); + } + + for (i = 0; i < AWE_NORMAL_VOICES; i++) { + if (voices[i].state == AWE_ST_DRAM) { + awe_poke_dw(AWE_CCCA(i), 0); + awe_poke(AWE_DCYSUSV(i), 0x807F); + voices[i].state = AWE_ST_OFF; + } + } +} + + +/* + * detect presence of AWE32 and check memory size + */ + +/* detect emu8000 chip on the specified address; from VV's guide */ + +static int __init +awe_detect_base(int addr) +{ + setup_ports(addr, 0, 0); + if ((awe_peek(AWE_U1) & 0x000F) != 0x000C) + return 0; + if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058) + return 0; + if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003) + return 0; + DEBUG(0,printk("AWE32 found at %x\n", addr)); + return 1; +} + +#ifdef __ISAPNP__ +static struct { + unsigned short card_vendor, card_device; + unsigned short vendor; + unsigned short function; + char *name; +} isapnp_awe_list[] __initdata = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0021), + "AWE32 WaveTable" }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0022), + "AWE64 WaveTable" }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0023), + "AWE64 Gold WaveTable" }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, isapnp_awe_list); + +static struct pci_dev *idev = NULL; + +static int __init awe_probe_isapnp(int *port) +{ + int i; + + for (i = 0; isapnp_awe_list[i].vendor != 0; i++) { + while ((idev = isapnp_find_dev(NULL, + isapnp_awe_list[i].vendor, + isapnp_awe_list[i].function, + idev))) { + if (idev->prepare(idev) < 0) + continue; + if (idev->activate(idev) < 0 || + !idev->resource[0].start) { + idev->deactivate(idev); + idev->deactivate(idev); + continue; + } + *port = idev->resource[0].start; + break; + } + if (!idev) + continue; + printk(KERN_INFO "ISAPnP reports %s at i/o %#x\n", + isapnp_awe_list[i].name, *port); + return 0; + } + return -ENODEV; +} + +static void __exit awe_deactivate_isapnp(void) +{ +#if 1 + if (idev) { + idev->deactivate(idev); + idev = NULL; + } +#endif +} + +#endif + +static int __init +awe_detect(void) +{ + int base; + +#ifdef __ISAPNP__ + if (isapnp) { + if (awe_probe_isapnp(&io) < 0) { + printk(KERN_ERR "AWE32: No ISAPnP cards found\n"); + if (isapnp != -1) + return 0; + } else { + setup_ports(io, 0, 0); + return 1; + } + } +#endif /* isapnp */ + + if (io) /* use default i/o port value */ + setup_ports(io, 0, 0); + else { /* probe it */ + for (base = 0x620; base <= 0x680; base += 0x20) + if (awe_detect_base(base)) + return 1; + DEBUG(0,printk("AWE32 not found\n")); + return 0; + } + + return 1; +} + + +/* + * check dram size on AWE board + */ + +/* any three numbers you like */ +#define UNIQUE_ID1 0x1234 +#define UNIQUE_ID2 0x4321 +#define UNIQUE_ID3 0xABCD + +static void __init +awe_check_dram(void) +{ + if (awe_present) /* already initialized */ + return; + + if (memsize >= 0) { /* given by config file or module option */ + memsize *= 1024; /* convert to Kbytes */ + return; + } + + awe_open_dram_for_check(); + + memsize = 0; + + /* set up unique two id numbers */ + awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET); + awe_poke(AWE_SMLD, UNIQUE_ID1); + awe_poke(AWE_SMLD, UNIQUE_ID2); + + while (memsize < AWE_MAX_DRAM_SIZE) { + awe_wait(5); + /* read a data on the DRAM start address */ + awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET); + awe_peek(AWE_SMLD); /* discard stale data */ + if (awe_peek(AWE_SMLD) != UNIQUE_ID1) + break; + if (awe_peek(AWE_SMLD) != UNIQUE_ID2) + break; + memsize += 512; /* increment 512kbytes */ + /* Write a unique data on the test address; + * if the address is out of range, the data is written on + * 0x200000(=AWE_DRAM_OFFSET). Then the two id words are + * broken by this data. + */ + awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + memsize*512L); + awe_poke(AWE_SMLD, UNIQUE_ID3); + awe_wait(5); + /* read a data on the just written DRAM address */ + awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + memsize*512L); + awe_peek(AWE_SMLD); /* discard stale data */ + if (awe_peek(AWE_SMLD) != UNIQUE_ID3) + break; + } + awe_close_dram(); + + DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", memsize)); + + /* convert to Kbytes */ + memsize *= 1024; +} + + +/*----------------------------------------------------------------*/ + +/* + * chorus and reverb controls; from VV's guide + */ + +/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ +static char chorus_defined[AWE_CHORUS_NUMBERS]; +static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = { + {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ + {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ + {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ + {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ + {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ + {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ + {0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */ + {0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */ +}; + +static int +awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count) +{ + if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) { + printk(KERN_WARNING "AWE32 Error: invalid chorus mode %d for uploading\n", patch->optarg); + return -EINVAL; + } + if (count < sizeof(awe_chorus_fx_rec)) { + printk(KERN_WARNING "AWE32 Error: too short chorus fx parameters\n"); + return -EINVAL; + } + if (copy_from_user(&chorus_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE, + sizeof(awe_chorus_fx_rec))) + return -EFAULT; + chorus_defined[patch->optarg] = TRUE; + return 0; +} + +static void +awe_set_chorus_mode(int effect) +{ + if (effect < 0 || effect >= AWE_CHORUS_NUMBERS || + (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect])) + return; + awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback); + awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset); + awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth); + awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay); + awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq); + awe_poke_dw(AWE_HWCF6, 0x8000); + awe_poke_dw(AWE_HWCF7, 0x0000); +} + +static void +awe_update_chorus_mode(void) +{ + awe_set_chorus_mode(ctrls[AWE_MD_CHORUS_MODE]); +} + +/*----------------------------------------------------------------*/ + +/* reverb mode settings; write the following 28 data of 16 bit length + * on the corresponding ports in the reverb_cmds array + */ +static char reverb_defined[AWE_CHORUS_NUMBERS]; +static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = { +{{ /* room 1 */ + 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, + 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 2 */ + 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 3 */ + 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, + 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, +}}, +{{ /* hall 1 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, + 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, +}}, +{{ /* hall 2 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, + 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, + 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* plate */ + 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, + 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* delay */ + 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +{{ /* panning delay */ + 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +}; + +static struct ReverbCmdPair { + unsigned short cmd, port; +} reverb_cmds[28] = { + {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)}, + {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)}, + {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)}, + {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)}, + {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)}, + {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)}, + {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, +}; + +static int +awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count) +{ + if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) { + printk(KERN_WARNING "AWE32 Error: invalid reverb mode %d for uploading\n", patch->optarg); + return -EINVAL; + } + if (count < sizeof(awe_reverb_fx_rec)) { + printk(KERN_WARNING "AWE32 Error: too short reverb fx parameters\n"); + return -EINVAL; + } + if (copy_from_user(&reverb_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE, + sizeof(awe_reverb_fx_rec))) + return -EFAULT; + reverb_defined[patch->optarg] = TRUE; + return 0; +} + +static void +awe_set_reverb_mode(int effect) +{ + int i; + if (effect < 0 || effect >= AWE_REVERB_NUMBERS || + (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect])) + return; + for (i = 0; i < 28; i++) + awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port, + reverb_parm[effect].parms[i]); +} + +static void +awe_update_reverb_mode(void) +{ + awe_set_reverb_mode(ctrls[AWE_MD_REVERB_MODE]); +} + +/* + * treble/bass equalizer control + */ + +static unsigned short bass_parm[12][3] = { + {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ + {0xD25B, 0xD35B, 0x0000}, /* -8 */ + {0xD24C, 0xD34C, 0x0000}, /* -6 */ + {0xD23D, 0xD33D, 0x0000}, /* -4 */ + {0xD21F, 0xD31F, 0x0000}, /* -2 */ + {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ + {0xC219, 0xC319, 0x0001}, /* +2 */ + {0xC22A, 0xC32A, 0x0001}, /* +4 */ + {0xC24C, 0xC34C, 0x0001}, /* +6 */ + {0xC26E, 0xC36E, 0x0001}, /* +8 */ + {0xC248, 0xC348, 0x0002}, /* +10 */ + {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ +}; + +static unsigned short treble_parm[12][9] = { + {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ + {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ + {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, + {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */ +}; + + +/* + * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] + */ +static void +awe_equalizer(int bass, int treble) +{ + unsigned short w; + + if (bass < 0 || bass > 11 || treble < 0 || treble > 11) + return; + awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]); + awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]); + awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]); + awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]); + awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]); + awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]); + awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]); + awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]); + awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]); + awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]); + w = bass_parm[bass][2] + treble_parm[treble][8]; + awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262)); + awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362)); +} + +static void awe_update_equalizer(void) +{ + awe_equalizer(ctrls[AWE_MD_BASS_LEVEL], ctrls[AWE_MD_TREBLE_LEVEL]); +} + + +/*----------------------------------------------------------------*/ + +#ifdef CONFIG_AWE32_MIDIEMU + +/* + * Emu8000 MIDI Emulation + */ + +/* + * midi queue record + */ + +/* queue type */ +enum { Q_NONE, Q_VARLEN, Q_READ, Q_SYSEX, }; + +#define MAX_MIDIBUF 64 + +/* midi status */ +typedef struct MidiStatus { + int queue; /* queue type */ + int qlen; /* queue length */ + int read; /* chars read */ + int status; /* current status */ + int chan; /* current channel */ + unsigned char buf[MAX_MIDIBUF]; +} MidiStatus; + +/* MIDI mode type */ +enum { MODE_GM, MODE_GS, MODE_XG, }; + +/* NRPN / CC -> Emu8000 parameter converter */ +typedef struct { + int control; + int awe_effect; + unsigned short (*convert)(int val); +} ConvTable; + + +/* + * prototypes + */ + +static int awe_midi_open(int dev, int mode, void (*input)(int,unsigned char), void (*output)(int)); +static void awe_midi_close(int dev); +static int awe_midi_ioctl(int dev, unsigned cmd, caddr_t arg); +static int awe_midi_outputc(int dev, unsigned char midi_byte); + +static void init_midi_status(MidiStatus *st); +static void clear_rpn(void); +static void get_midi_char(MidiStatus *st, int c); +/*static void queue_varlen(MidiStatus *st, int c);*/ +static void special_event(MidiStatus *st, int c); +static void queue_read(MidiStatus *st, int c); +static void midi_note_on(MidiStatus *st); +static void midi_note_off(MidiStatus *st); +static void midi_key_pressure(MidiStatus *st); +static void midi_channel_pressure(MidiStatus *st); +static void midi_pitch_wheel(MidiStatus *st); +static void midi_program_change(MidiStatus *st); +static void midi_control_change(MidiStatus *st); +static void midi_select_bank(MidiStatus *st, int val); +static void midi_nrpn_event(MidiStatus *st); +static void midi_rpn_event(MidiStatus *st); +static void midi_detune(int chan, int coarse, int fine); +static void midi_system_exclusive(MidiStatus *st); +static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val); +static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val); +static int xg_control_change(MidiStatus *st, int cmd, int val); + +#define numberof(ary) (sizeof(ary)/sizeof(ary[0])) + + +/* + * OSS Midi device record + */ + +static struct midi_operations awe_midi_operations = +{ + owner: THIS_MODULE, + info: {"AWE Midi Emu", 0, 0, SNDCARD_SB}, + in_info: {0}, + open: awe_midi_open, /*open*/ + close: awe_midi_close, /*close*/ + ioctl: awe_midi_ioctl, /*ioctl*/ + outputc: awe_midi_outputc, /*outputc*/ +}; + +static int my_mididev = -1; + +static void __init attach_midiemu(void) +{ + if ((my_mididev = sound_alloc_mididev()) < 0) + printk ("Sound: Too many midi devices detected\n"); + else + midi_devs[my_mididev] = &awe_midi_operations; +} + +static void __exit unload_midiemu(void) +{ + if (my_mididev >= 0) + sound_unload_mididev(my_mididev); +} + + +/* + * open/close midi device + */ + +static int midi_opened = FALSE; + +static int midi_mode; +static int coarsetune = 0, finetune = 0; + +static int xg_mapping = TRUE; +static int xg_bankmode = 0; + +/* effect sensitivity */ + +#define FX_CUTOFF 0 +#define FX_RESONANCE 1 +#define FX_ATTACK 2 +#define FX_RELEASE 3 +#define FX_VIBRATE 4 +#define FX_VIBDEPTH 5 +#define FX_VIBDELAY 6 +#define FX_NUMS 7 + +#define DEF_FX_CUTOFF 170 +#define DEF_FX_RESONANCE 6 +#define DEF_FX_ATTACK 50 +#define DEF_FX_RELEASE 50 +#define DEF_FX_VIBRATE 30 +#define DEF_FX_VIBDEPTH 4 +#define DEF_FX_VIBDELAY 1500 + +/* effect sense: */ +static int gs_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; +static int xg_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; + + +/* current status */ +static MidiStatus curst; + + +static int +awe_midi_open (int dev, int mode, + void (*input)(int,unsigned char), + void (*output)(int)) +{ + if (midi_opened) + return -EBUSY; + + midi_opened = TRUE; + + midi_mode = MODE_GM; + + curst.queue = Q_NONE; + curst.qlen = 0; + curst.read = 0; + curst.status = 0; + curst.chan = 0; + memset(curst.buf, 0, sizeof(curst.buf)); + + init_midi_status(&curst); + + return 0; +} + +static void +awe_midi_close (int dev) +{ + midi_opened = FALSE; +} + + +static int +awe_midi_ioctl (int dev, unsigned cmd, caddr_t arg) +{ + return -EPERM; +} + +static int +awe_midi_outputc (int dev, unsigned char midi_byte) +{ + if (! midi_opened) + return 1; + + /* force to change playing mode */ + playing_mode = AWE_PLAY_MULTI; + + get_midi_char(&curst, midi_byte); + return 1; +} + + +/* + * initialize + */ + +static void init_midi_status(MidiStatus *st) +{ + clear_rpn(); + coarsetune = 0; + finetune = 0; +} + + +/* + * RPN & NRPN + */ + +#define MAX_MIDI_CHANNELS 16 + +/* RPN & NRPN */ +static unsigned char nrpn[MAX_MIDI_CHANNELS]; /* current event is NRPN? */ +static int msb_bit; /* current event is msb for RPN/NRPN */ +/* RPN & NRPN indeces */ +static unsigned char rpn_msb[MAX_MIDI_CHANNELS], rpn_lsb[MAX_MIDI_CHANNELS]; +/* RPN & NRPN values */ +static int rpn_val[MAX_MIDI_CHANNELS]; + +static void clear_rpn(void) +{ + int i; + for (i = 0; i < MAX_MIDI_CHANNELS; i++) { + nrpn[i] = 0; + rpn_msb[i] = 127; + rpn_lsb[i] = 127; + rpn_val[i] = 0; + } + msb_bit = 0; +} + + +/* + * process midi queue + */ + +/* status event types */ +typedef void (*StatusEvent)(MidiStatus *st); +static struct StatusEventList { + StatusEvent process; + int qlen; +} status_event[8] = { + {midi_note_off, 2}, + {midi_note_on, 2}, + {midi_key_pressure, 2}, + {midi_control_change, 2}, + {midi_program_change, 1}, + {midi_channel_pressure, 1}, + {midi_pitch_wheel, 2}, + {NULL, 0}, +}; + + +/* read a char from fifo and process it */ +static void get_midi_char(MidiStatus *st, int c) +{ + if (c == 0xfe) { + /* ignore active sense */ + st->queue = Q_NONE; + return; + } + + switch (st->queue) { + /* case Q_VARLEN: queue_varlen(st, c); break;*/ + case Q_READ: + case Q_SYSEX: + queue_read(st, c); + break; + case Q_NONE: + st->read = 0; + if ((c & 0xf0) == 0xf0) { + special_event(st, c); + } else if (c & 0x80) { /* status change */ + st->status = (c >> 4) & 0x07; + st->chan = c & 0x0f; + st->queue = Q_READ; + st->qlen = status_event[st->status].qlen; + if (st->qlen == 0) + st->queue = Q_NONE; + } + break; + } +} + +/* 0xfx events */ +static void special_event(MidiStatus *st, int c) +{ + switch (c) { + case 0xf0: /* system exclusive */ + st->queue = Q_SYSEX; + st->qlen = 0; + break; + case 0xf1: /* MTC quarter frame */ + case 0xf3: /* song select */ + st->queue = Q_READ; + st->qlen = 1; + break; + case 0xf2: /* song position */ + st->queue = Q_READ; + st->qlen = 2; + break; + } +} + +#if 0 +/* read variable length value */ +static void queue_varlen(MidiStatus *st, int c) +{ + st->qlen += (c & 0x7f); + if (c & 0x80) { + st->qlen <<= 7; + return; + } + if (st->qlen <= 0) { + st->qlen = 0; + st->queue = Q_NONE; + } + st->queue = Q_READ; + st->read = 0; +} +#endif + + +/* read a char */ +static void queue_read(MidiStatus *st, int c) +{ + if (st->read < MAX_MIDIBUF) { + if (st->queue != Q_SYSEX) + c &= 0x7f; + st->buf[st->read] = (unsigned char)c; + } + st->read++; + if (st->queue == Q_SYSEX && c == 0xf7) { + midi_system_exclusive(st); + st->queue = Q_NONE; + } else if (st->queue == Q_READ && st->read >= st->qlen) { + if (status_event[st->status].process) + status_event[st->status].process(st); + st->queue = Q_NONE; + } +} + + +/* + * status events + */ + +/* note on */ +static void midi_note_on(MidiStatus *st) +{ + DEBUG(2,printk("midi: note_on (%d) %d %d\n", st->chan, st->buf[0], st->buf[1])); + if (st->buf[1] == 0) + midi_note_off(st); + else + awe_start_note(0, st->chan, st->buf[0], st->buf[1]); +} + +/* note off */ +static void midi_note_off(MidiStatus *st) +{ + DEBUG(2,printk("midi: note_off (%d) %d %d\n", st->chan, st->buf[0], st->buf[1])); + awe_kill_note(0, st->chan, st->buf[0], st->buf[1]); +} + +/* key pressure change */ +static void midi_key_pressure(MidiStatus *st) +{ + awe_key_pressure(0, st->chan, st->buf[0], st->buf[1]); +} + +/* channel pressure change */ +static void midi_channel_pressure(MidiStatus *st) +{ + channels[st->chan].chan_press = st->buf[0]; + awe_modwheel_change(st->chan, st->buf[0]); +} + +/* pitch wheel change */ +static void midi_pitch_wheel(MidiStatus *st) +{ + int val = (int)st->buf[1] * 128 + st->buf[0]; + awe_bender(0, st->chan, val); +} + +/* program change */ +static void midi_program_change(MidiStatus *st) +{ + int preset; + preset = st->buf[0]; + if (midi_mode == MODE_GS && IS_DRUM_CHANNEL(st->chan) && preset == 127) + preset = 0; + else if (midi_mode == MODE_XG && xg_mapping && IS_DRUM_CHANNEL(st->chan)) + preset += 64; + + awe_set_instr(0, st->chan, preset); +} + +#define send_effect(chan,type,val) awe_send_effect(chan,-1,type,val) +#define add_effect(chan,type,val) awe_send_effect(chan,-1,(type)|0x80,val) +#define unset_effect(chan,type) awe_send_effect(chan,-1,(type)|0x40,0) + +/* midi control change */ +static void midi_control_change(MidiStatus *st) +{ + int cmd = st->buf[0]; + int val = st->buf[1]; + + DEBUG(2,printk("midi: control (%d) %d %d\n", st->chan, cmd, val)); + if (midi_mode == MODE_XG) { + if (xg_control_change(st, cmd, val)) + return; + } + + /* controls #31 - #64 are LSB of #0 - #31 */ + msb_bit = 1; + if (cmd >= 0x20 && cmd < 0x40) { + msb_bit = 0; + cmd -= 0x20; + } + + switch (cmd) { + case CTL_SOFT_PEDAL: + if (val == 127) + add_effect(st->chan, AWE_FX_CUTOFF, -160); + else + unset_effect(st->chan, AWE_FX_CUTOFF); + break; + + case CTL_BANK_SELECT: + midi_select_bank(st, val); + break; + + /* set RPN/NRPN parameter */ + case CTL_REGIST_PARM_NUM_MSB: + nrpn[st->chan]=0; rpn_msb[st->chan]=val; + break; + case CTL_REGIST_PARM_NUM_LSB: + nrpn[st->chan]=0; rpn_lsb[st->chan]=val; + break; + case CTL_NONREG_PARM_NUM_MSB: + nrpn[st->chan]=1; rpn_msb[st->chan]=val; + break; + case CTL_NONREG_PARM_NUM_LSB: + nrpn[st->chan]=1; rpn_lsb[st->chan]=val; + break; + + /* send RPN/NRPN entry */ + case CTL_DATA_ENTRY: + if (msb_bit) + rpn_val[st->chan] = val * 128; + else + rpn_val[st->chan] |= val; + if (nrpn[st->chan]) + midi_nrpn_event(st); + else + midi_rpn_event(st); + break; + + /* increase/decrease data entry */ + case CTL_DATA_INCREMENT: + rpn_val[st->chan]++; + midi_rpn_event(st); + break; + case CTL_DATA_DECREMENT: + rpn_val[st->chan]--; + midi_rpn_event(st); + break; + + /* default */ + default: + awe_controller(0, st->chan, cmd, val); + break; + } +} + +/* tone bank change */ +static void midi_select_bank(MidiStatus *st, int val) +{ + if (midi_mode == MODE_XG && msb_bit) { + xg_bankmode = val; + /* XG MSB value; not normal bank selection */ + switch (val) { + case 127: /* remap to drum channel */ + awe_controller(0, st->chan, CTL_BANK_SELECT, 128); + break; + default: /* remap to normal channel */ + awe_controller(0, st->chan, CTL_BANK_SELECT, val); + break; + } + return; + } else if (midi_mode == MODE_GS && !msb_bit) + /* ignore LSB bank in GS mode (used for mapping) */ + return; + + /* normal bank controls; accept both MSB and LSB */ + if (! IS_DRUM_CHANNEL(st->chan)) { + if (midi_mode == MODE_XG) { + if (xg_bankmode) return; + if (val == 64 || val == 126) + val = 0; + } else if (midi_mode == MODE_GS && val == 127) + val = 0; + awe_controller(0, st->chan, CTL_BANK_SELECT, val); + } +} + + +/* + * RPN events + */ + +static void midi_rpn_event(MidiStatus *st) +{ + int type; + type = (rpn_msb[st->chan]<<8) | rpn_lsb[st->chan]; + switch (type) { + case 0x0000: /* Pitch bend sensitivity */ + /* MSB only / 1 semitone per 128 */ + if (msb_bit) { + channels[st->chan].bender_range = + rpn_val[st->chan] * 100 / 128; + } + break; + + case 0x0001: /* fine tuning: */ + /* MSB/LSB, 8192=center, 100/8192 cent step */ + finetune = rpn_val[st->chan] - 8192; + midi_detune(st->chan, coarsetune, finetune); + break; + + case 0x0002: /* coarse tuning */ + /* MSB only / 8192=center, 1 semitone per 128 */ + if (msb_bit) { + coarsetune = rpn_val[st->chan] - 8192; + midi_detune(st->chan, coarsetune, finetune); + } + break; + + case 0x7F7F: /* "lock-in" RPN */ + break; + } +} + + +/* tuning: + * coarse = -8192 to 8192 (100 cent per 128) + * fine = -8192 to 8192 (max=100cent) + */ +static void midi_detune(int chan, int coarse, int fine) +{ + /* 4096 = 1200 cents in AWE parameter */ + int val; + val = coarse * 4096 / (12 * 128); + val += fine / 24; + if (val) + send_effect(chan, AWE_FX_INIT_PITCH, val); + else + unset_effect(chan, AWE_FX_INIT_PITCH); +} + + +/* + * system exclusive message + * GM/GS/XG macros are accepted + */ + +static void midi_system_exclusive(MidiStatus *st) +{ + /* GM on */ + static unsigned char gm_on_macro[] = { + 0x7e,0x7f,0x09,0x01, + }; + /* XG on */ + static unsigned char xg_on_macro[] = { + 0x43,0x10,0x4c,0x00,0x00,0x7e,0x00, + }; + /* GS prefix + * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off + * reverb mode: XX=0x01, YY=0x30, ZZ=0-7 + * chorus mode: XX=0x01, YY=0x38, ZZ=0-7 + */ + static unsigned char gs_pfx_macro[] = { + 0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/ + }; + +#if 0 + /* SC88 system mode set + * single module mode: XX=1 + * double module mode: XX=0 + */ + static unsigned char gs_mode_macro[] = { + 0x41,0x10,0x42,0x12,0x00,0x00,0x7F,/*ZZ*/ + }; + /* SC88 display macro: XX=01:bitmap, 00:text + */ + static unsigned char gs_disp_macro[] = { + 0x41,0x10,0x45,0x12,0x10,/*XX,00*/ + }; +#endif + + /* GM on */ + if (memcmp(st->buf, gm_on_macro, sizeof(gm_on_macro)) == 0) { + if (midi_mode != MODE_GS && midi_mode != MODE_XG) + midi_mode = MODE_GM; + init_midi_status(st); + } + + /* GS macros */ + else if (memcmp(st->buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) { + if (midi_mode != MODE_GS && midi_mode != MODE_XG) + midi_mode = MODE_GS; + + if (st->buf[5] == 0x00 && st->buf[6] == 0x7f && st->buf[7] == 0x00) { + /* GS reset */ + init_midi_status(st); + } + + else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x15) { + /* drum pattern */ + int p = st->buf[5] & 0x0f; + if (p == 0) p = 9; + else if (p < 10) p--; + if (st->buf[7] == 0) + DRUM_CHANNEL_OFF(p); + else + DRUM_CHANNEL_ON(p); + + } else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x21) { + /* program */ + int p = st->buf[5] & 0x0f; + if (p == 0) p = 9; + else if (p < 10) p--; + if (! IS_DRUM_CHANNEL(p)) + awe_set_instr(0, p, st->buf[7]); + + } else if (st->buf[5] == 0x01 && st->buf[6] == 0x30) { + /* reverb mode */ + awe_set_reverb_mode(st->buf[7]); + + } else if (st->buf[5] == 0x01 && st->buf[6] == 0x38) { + /* chorus mode */ + awe_set_chorus_mode(st->buf[7]); + + } else if (st->buf[5] == 0x00 && st->buf[6] == 0x04) { + /* master volume */ + awe_change_master_volume(st->buf[7]); + + } + } + + /* XG on */ + else if (memcmp(st->buf, xg_on_macro, sizeof(xg_on_macro)) == 0) { + midi_mode = MODE_XG; + xg_mapping = TRUE; + xg_bankmode = 0; + } +} + + +/*----------------------------------------------------------------*/ + +/* + * convert NRPN/control values + */ + +static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val) +{ + int i, cval; + for (i = 0; i < num_tables; i++) { + if (table[i].control == type) { + cval = table[i].convert(val); + send_effect(st->chan, table[i].awe_effect, cval); + return TRUE; + } + } + return FALSE; +} + +static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val) +{ + int i, cval; + for (i = 0; i < num_tables; i++) { + if (table[i].control == type) { + cval = table[i].convert(val); + add_effect(st->chan, table[i].awe_effect|0x80, cval); + return TRUE; + } + } + return FALSE; +} + + +/* + * AWE32 NRPN effects + */ + +static unsigned short fx_delay(int val); +static unsigned short fx_attack(int val); +static unsigned short fx_hold(int val); +static unsigned short fx_decay(int val); +static unsigned short fx_the_value(int val); +static unsigned short fx_twice_value(int val); +static unsigned short fx_conv_pitch(int val); +static unsigned short fx_conv_Q(int val); + +/* function for each NRPN */ /* [range] units */ +#define fx_env1_delay fx_delay /* [0,5900] 4msec */ +#define fx_env1_attack fx_attack /* [0,5940] 1msec */ +#define fx_env1_hold fx_hold /* [0,8191] 1msec */ +#define fx_env1_decay fx_decay /* [0,5940] 4msec */ +#define fx_env1_release fx_decay /* [0,5940] 4msec */ +#define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */ +#define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */ + +#define fx_env2_delay fx_delay /* [0,5900] 4msec */ +#define fx_env2_attack fx_attack /* [0,5940] 1msec */ +#define fx_env2_hold fx_hold /* [0,8191] 1msec */ +#define fx_env2_decay fx_decay /* [0,5940] 4msec */ +#define fx_env2_release fx_decay /* [0,5940] 4msec */ +#define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */ + +#define fx_lfo1_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */ +#define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */ + +#define fx_lfo2_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */ + +#define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */ +#define fx_chorus fx_the_value /* [0,255] -- */ +#define fx_reverb fx_the_value /* [0,255] -- */ +#define fx_cutoff fx_twice_value /* [0,127] 62Hz */ +#define fx_filterQ fx_conv_Q /* [0,127] -- */ + +static unsigned short fx_delay(int val) +{ + return (unsigned short)calc_parm_delay(val); +} + +static unsigned short fx_attack(int val) +{ + return (unsigned short)calc_parm_attack(val); +} + +static unsigned short fx_hold(int val) +{ + return (unsigned short)calc_parm_hold(val); +} + +static unsigned short fx_decay(int val) +{ + return (unsigned short)calc_parm_decay(val); +} + +static unsigned short fx_the_value(int val) +{ + return (unsigned short)(val & 0xff); +} + +static unsigned short fx_twice_value(int val) +{ + return (unsigned short)((val * 2) & 0xff); +} + +static unsigned short fx_conv_pitch(int val) +{ + return (short)(val * 4096 / 1200); +} + +static unsigned short fx_conv_Q(int val) +{ + return (unsigned short)((val / 8) & 0xff); +} + + +static ConvTable awe_effects[] = +{ + { 0, AWE_FX_LFO1_DELAY, fx_lfo1_delay}, + { 1, AWE_FX_LFO1_FREQ, fx_lfo1_freq}, + { 2, AWE_FX_LFO2_DELAY, fx_lfo2_delay}, + { 3, AWE_FX_LFO2_FREQ, fx_lfo2_freq}, + + { 4, AWE_FX_ENV1_DELAY, fx_env1_delay}, + { 5, AWE_FX_ENV1_ATTACK,fx_env1_attack}, + { 6, AWE_FX_ENV1_HOLD, fx_env1_hold}, + { 7, AWE_FX_ENV1_DECAY, fx_env1_decay}, + { 8, AWE_FX_ENV1_SUSTAIN, fx_env1_sustain}, + { 9, AWE_FX_ENV1_RELEASE, fx_env1_release}, + + {10, AWE_FX_ENV2_DELAY, fx_env2_delay}, + {11, AWE_FX_ENV2_ATTACK, fx_env2_attack}, + {12, AWE_FX_ENV2_HOLD, fx_env2_hold}, + {13, AWE_FX_ENV2_DECAY, fx_env2_decay}, + {14, AWE_FX_ENV2_SUSTAIN, fx_env2_sustain}, + {15, AWE_FX_ENV2_RELEASE, fx_env2_release}, + + {16, AWE_FX_INIT_PITCH, fx_init_pitch}, + {17, AWE_FX_LFO1_PITCH, fx_lfo1_pitch}, + {18, AWE_FX_LFO2_PITCH, fx_lfo2_pitch}, + {19, AWE_FX_ENV1_PITCH, fx_env1_pitch}, + {20, AWE_FX_LFO1_VOLUME, fx_lfo1_volume}, + {21, AWE_FX_CUTOFF, fx_cutoff}, + {22, AWE_FX_FILTERQ, fx_filterQ}, + {23, AWE_FX_LFO1_CUTOFF, fx_lfo1_cutoff}, + {24, AWE_FX_ENV1_CUTOFF, fx_env1_cutoff}, + {25, AWE_FX_CHORUS, fx_chorus}, + {26, AWE_FX_REVERB, fx_reverb}, +}; + +static int num_awe_effects = numberof(awe_effects); + + +/* + * GS(SC88) NRPN effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static unsigned short gs_cutoff(int val) +{ + return (val - 64) * gs_sense[FX_CUTOFF] / 50; +} + +/* resonance: 0 to 15(max) */ +static unsigned short gs_filterQ(int val) +{ + return (val - 64) * gs_sense[FX_RESONANCE] / 50; +} + +/* attack: */ +static unsigned short gs_attack(int val) +{ + return -(val - 64) * gs_sense[FX_ATTACK] / 50; +} + +/* decay: */ +static unsigned short gs_decay(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* release: */ +static unsigned short gs_release(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* vibrato freq: 0.042Hz step, max=255 */ +static unsigned short gs_vib_rate(int val) +{ + return (val - 64) * gs_sense[FX_VIBRATE] / 50; +} + +/* vibrato depth: max=127, 1 octave */ +static unsigned short gs_vib_depth(int val) +{ + return (val - 64) * gs_sense[FX_VIBDEPTH] / 50; +} + +/* vibrato delay: -0.725msec step */ +static unsigned short gs_vib_delay(int val) +{ + return -(val - 64) * gs_sense[FX_VIBDELAY] / 50; +} + +static ConvTable gs_effects[] = +{ + {32, AWE_FX_CUTOFF, gs_cutoff}, + {33, AWE_FX_FILTERQ, gs_filterQ}, + {99, AWE_FX_ENV2_ATTACK, gs_attack}, + {100, AWE_FX_ENV2_DECAY, gs_decay}, + {102, AWE_FX_ENV2_RELEASE, gs_release}, + {8, AWE_FX_LFO1_FREQ, gs_vib_rate}, + {9, AWE_FX_LFO1_VOLUME, gs_vib_depth}, + {10, AWE_FX_LFO1_DELAY, gs_vib_delay}, +}; + +static int num_gs_effects = numberof(gs_effects); + + +/* + * NRPN events: accept as AWE32/SC88 specific controls + */ + +static void midi_nrpn_event(MidiStatus *st) +{ + if (rpn_msb[st->chan] == 127 && rpn_lsb[st->chan] <= 26) { + if (! msb_bit) /* both MSB/LSB necessary */ + send_converted_effect(awe_effects, num_awe_effects, + st, rpn_lsb[st->chan], + rpn_val[st->chan] - 8192); + } else if (rpn_msb[st->chan] == 1) { + if (msb_bit) /* only MSB is valid */ + add_converted_effect(gs_effects, num_gs_effects, + st, rpn_lsb[st->chan], + rpn_val[st->chan] / 128); + } +} + + +/* + * XG control effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static unsigned short xg_cutoff(int val) +{ + return (val - 64) * xg_sense[FX_CUTOFF] / 64; +} + +/* resonance: 0(open) to 15(most nasal) */ +static unsigned short xg_filterQ(int val) +{ + return (val - 64) * xg_sense[FX_RESONANCE] / 64; +} + +/* attack: */ +static unsigned short xg_attack(int val) +{ + return -(val - 64) * xg_sense[FX_ATTACK] / 64; +} + +/* release: */ +static unsigned short xg_release(int val) +{ + return -(val - 64) * xg_sense[FX_RELEASE] / 64; +} + +static ConvTable xg_effects[] = +{ + {71, AWE_FX_CUTOFF, xg_cutoff}, + {74, AWE_FX_FILTERQ, xg_filterQ}, + {72, AWE_FX_ENV2_RELEASE, xg_release}, + {73, AWE_FX_ENV2_ATTACK, xg_attack}, +}; + +static int num_xg_effects = numberof(xg_effects); + +static int xg_control_change(MidiStatus *st, int cmd, int val) +{ + return add_converted_effect(xg_effects, num_xg_effects, st, cmd, val); +} + +#endif /* CONFIG_AWE32_MIDIEMU */ + + +/*----------------------------------------------------------------*/ + +/* + * device / lowlevel (module) interface + */ + +int __init attach_awe(void) +{ + return _attach_awe() ? 0 : -ENODEV; +} + +void __exit unload_awe(void) +{ + _unload_awe(); +#ifdef __ISAPNP__ + if (isapnp) + awe_deactivate_isapnp(); +#endif /* isapnp */ +} + + +module_init(attach_awe); +module_exit(unload_awe); + +#ifndef MODULE +static int __init setup_awe(char *str) +{ + /* io, memsize, isapnp */ + int ints[4]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + memsize = ints[2]; + isapnp = ints[3]; + + return 1; +} + +__setup("awe=", setup_awe); +#endif diff -Nru a/sound/oss/awe_wave.h b/sound/oss/awe_wave.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/awe_wave.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,77 @@ +/* + * sound/awe_config.h + * + * Configuration of AWE32/SB32/AWE64 wave table synth driver. + * version 0.4.4; Jan. 4, 2000 + * + * Copyright (C) 1996-1998 Takashi Iwai + * + * 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. + */ + +/* + * chorus & reverb effects send for FM chip: from 0 to 0xff + * larger numbers often cause weird sounds. + */ + +#define DEF_FM_CHORUS_DEPTH 0x10 +#define DEF_FM_REVERB_DEPTH 0x10 + + +/* + * other compile conditions + */ + +/* initialize FM passthrough even without extended RAM */ +#undef AWE_ALWAYS_INIT_FM + +/* debug on */ +#define AWE_DEBUG_ON + +/* GUS compatible mode */ +#define AWE_HAS_GUS_COMPATIBILITY + +/* add MIDI emulation by wavetable */ +#define CONFIG_AWE32_MIDIEMU + +/* add mixer control of emu8000 equalizer */ +#undef CONFIG_AWE32_MIXER + +/* use new volume calculation method as default */ +#define AWE_USE_NEW_VOLUME_CALC + +/* check current volume target for searching empty voices */ +#define AWE_CHECK_VTARGET + +/* allow sample sharing */ +#define AWE_ALLOW_SAMPLE_SHARING + +/* + * AWE32 card configuration: + * uncomment the following lines *ONLY* when auto detection doesn't + * work properly on your machine. + */ + +/*#define AWE_DEFAULT_BASE_ADDR 0x620*/ /* base port address */ +/*#define AWE_DEFAULT_MEM_SIZE 512*/ /* kbytes */ + +/* + * AWE driver version number + */ +#define AWE_MAJOR_VERSION 0 +#define AWE_MINOR_VERSION 4 +#define AWE_TINY_VERSION 4 +#define AWE_VERSION_NUMBER ((AWE_MAJOR_VERSION<<16)|(AWE_MINOR_VERSION<<8)|AWE_TINY_VERSION) +#define AWEDRV_VERSION "0.4.4" diff -Nru a/sound/oss/bin2hex.c b/sound/oss/bin2hex.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/bin2hex.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,38 @@ +#include +#include + +int main( int argc, const char * argv [] ) +{ + const char * varname; + int i = 0; + int c; + int id = 0; + + if(argv[1] && strcmp(argv[1],"-i")==0) + { + argv++; + argc--; + id=1; + } + + if(argc==1) + { + fprintf(stderr, "bin2hex: [-i] firmware\n"); + exit(1); + } + + varname = argv[1]; + printf( "/* automatically generated by bin2hex */\n" ); + printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":""); + + while ( ( c = getchar( ) ) != EOF ) + { + if ( i != 0 && i % 10 == 0 ) + printf( "\n" ); + printf( "0x%02lx,", c & 0xFFl ); + i++; + } + + printf( "};\nstatic int %sLen = %d;\n", varname, i ); + return 0; +} diff -Nru a/sound/oss/btaudio.c b/sound/oss/btaudio.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/btaudio.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,1072 @@ +/* + btaudio - bt878 audio dma driver for linux 2.4.x + + (c) 2000 Gerd Knorr + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mmio access */ +#define btwrite(dat,adr) writel((dat), (bta->mmio+(adr))) +#define btread(adr) readl(bta->mmio+(adr)) + +#define btand(dat,adr) btwrite((dat) & btread(adr), adr) +#define btor(dat,adr) btwrite((dat) | btread(adr), adr) +#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) + +/* registers (shifted because bta->mmio is long) */ +#define REG_INT_STAT (0x100 >> 2) +#define REG_INT_MASK (0x104 >> 2) +#define REG_GPIO_DMA_CTL (0x10c >> 2) +#define REG_PACKET_LEN (0x110 >> 2) +#define REG_RISC_STRT_ADD (0x114 >> 2) +#define REG_RISC_COUNT (0x120 >> 2) + +/* IRQ bits - REG_INT_(STAT|MASK) */ +#define IRQ_SCERR (1 << 19) +#define IRQ_OCERR (1 << 18) +#define IRQ_PABORT (1 << 17) +#define IRQ_RIPERR (1 << 16) +#define IRQ_PPERR (1 << 15) +#define IRQ_FDSR (1 << 14) +#define IRQ_FTRGT (1 << 13) +#define IRQ_FBUS (1 << 12) +#define IRQ_RISCI (1 << 11) +#define IRQ_OFLOW (1 << 3) + +#define IRQ_BTAUDIO (IRQ_SCERR | IRQ_OCERR | IRQ_PABORT | IRQ_RIPERR |\ + IRQ_PPERR | IRQ_FDSR | IRQ_FTRGT | IRQ_FBUS |\ + IRQ_RISCI) + +/* REG_GPIO_DMA_CTL bits */ +#define DMA_CTL_A_PWRDN (1 << 26) +#define DMA_CTL_DA_SBR (1 << 14) +#define DMA_CTL_DA_ES2 (1 << 13) +#define DMA_CTL_ACAP_EN (1 << 4) +#define DMA_CTL_RISC_EN (1 << 1) +#define DMA_CTL_FIFO_EN (1 << 0) + +/* RISC instructions */ +#define RISC_WRITE (0x01 << 28) +#define RISC_JUMP (0x07 << 28) +#define RISC_SYNC (0x08 << 28) + +/* RISC bits */ +#define RISC_WR_SOL (1 << 27) +#define RISC_WR_EOL (1 << 26) +#define RISC_IRQ (1 << 24) +#define RISC_SYNC_RESYNC (1 << 15) +#define RISC_SYNC_FM1 0x06 +#define RISC_SYNC_VRO 0x0c + +#define HWBASE_AD (448000) + +/* -------------------------------------------------------------- */ + +struct btaudio { + /* linked list */ + struct btaudio *next; + + /* device info */ + int dsp_digital; + int dsp_analog; + int mixer_dev; + struct pci_dev *pci; + unsigned int irq; + unsigned long mem; + unsigned long *mmio; + + /* locking */ + int users; + struct semaphore lock; + + /* risc instructions */ + unsigned int risc_size; + unsigned long *risc_cpu; + dma_addr_t risc_dma; + + /* audio data */ + unsigned int buf_size; + unsigned char *buf_cpu; + dma_addr_t buf_dma; + + /* buffer setup */ + int line_bytes; + int line_count; + int block_bytes; + int block_count; + + /* read fifo management */ + int recording; + int dma_block; + int read_offset; + int read_count; + wait_queue_head_t readq; + + /* settings */ + int gain[3]; + int source; + int bits; + int decimation; + int mixcount; + int sampleshift; + int channels; + int analog; +}; + +static struct btaudio *btaudios = NULL; +static unsigned int dsp1 = -1; +static unsigned int dsp2 = -1; +static unsigned int mixer = -1; +static unsigned int debug = 0; +static unsigned int irq_debug = 0; +static int digital = 1; +static int analog = 1; +static int rate = 32000; + +/* -------------------------------------------------------------- */ + +#define BUF_DEFAULT 128*1024 +#define BUF_MIN 8192 + +static int alloc_buffer(struct btaudio *bta) +{ + if (NULL == bta->buf_cpu) { + for (bta->buf_size = BUF_DEFAULT; bta->buf_size >= BUF_MIN; + bta->buf_size = bta->buf_size >> 1) { + bta->buf_cpu = pci_alloc_consistent + (bta->pci, bta->buf_size, &bta->buf_dma); + if (NULL != bta->buf_cpu) + break; + } + if (NULL == bta->buf_cpu) + return -ENOMEM; + memset(bta->buf_cpu,0,bta->buf_size); + } + if (NULL == bta->risc_cpu) { + bta->risc_size = PAGE_SIZE; + bta->risc_cpu = pci_alloc_consistent + (bta->pci, bta->risc_size, &bta->risc_dma); + if (NULL == bta->risc_cpu) + return -ENOMEM; + } + return 0; +} + +static void free_buffer(struct btaudio *bta) +{ + if (NULL != bta->buf_cpu) { + pci_free_consistent(bta->pci, bta->buf_size, + bta->buf_cpu, bta->buf_dma); + bta->buf_cpu = NULL; + } + if (NULL != bta->risc_cpu) { + pci_free_consistent(bta->pci, bta->risc_size, + bta->risc_cpu, bta->risc_dma); + bta->risc_cpu = NULL; + } +} + +static int make_risc(struct btaudio *bta) +{ + int rp, bp, line, block; + unsigned long risc; + + bta->block_bytes = bta->buf_size >> 4; + bta->block_count = 1 << 4; + bta->line_bytes = bta->block_bytes; + bta->line_count = bta->block_count; + while (bta->line_bytes > 4095) { + bta->line_bytes >>= 1; + bta->line_count <<= 1; + } + if (bta->line_count > 255) + return -EINVAL; + if (debug) + printk(KERN_DEBUG + "btaudio: bufsize=%d - bs=%d bc=%d - ls=%d, lc=%d\n", + bta->buf_size,bta->block_bytes,bta->block_count, + bta->line_bytes,bta->line_count); + rp = 0; bp = 0; + block = 0; + bta->risc_cpu[rp++] = cpu_to_le32(RISC_SYNC|RISC_SYNC_FM1); + bta->risc_cpu[rp++] = cpu_to_le32(0); + for (line = 0; line < bta->line_count; line++) { + risc = RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL; + risc |= bta->line_bytes; + if (0 == (bp & (bta->block_bytes-1))) { + risc |= RISC_IRQ; + risc |= (block & 0x0f) << 16; + risc |= (~block & 0x0f) << 20; + block++; + } + bta->risc_cpu[rp++] = cpu_to_le32(risc); + bta->risc_cpu[rp++] = cpu_to_le32(bta->buf_dma + bp); + bp += bta->line_bytes; + } + bta->risc_cpu[rp++] = cpu_to_le32(RISC_SYNC|RISC_SYNC_VRO); + bta->risc_cpu[rp++] = cpu_to_le32(0); + bta->risc_cpu[rp++] = cpu_to_le32(RISC_JUMP); + bta->risc_cpu[rp++] = cpu_to_le32(bta->risc_dma); + return 0; +} + +static int start_recording(struct btaudio *bta) +{ + int ret; + + if (0 != (ret = alloc_buffer(bta))) + return ret; + if (0 != (ret = make_risc(bta))) + return ret; + + btwrite(bta->risc_dma, REG_RISC_STRT_ADD); + btwrite((bta->line_count << 16) | bta->line_bytes, + REG_PACKET_LEN); + btwrite(IRQ_BTAUDIO, REG_INT_MASK); + if (bta->analog) { + btwrite(DMA_CTL_ACAP_EN | + DMA_CTL_RISC_EN | + DMA_CTL_FIFO_EN | + DMA_CTL_DA_ES2 | + ((bta->bits == 8) ? DMA_CTL_DA_SBR : 0) | + (bta->gain[bta->source] << 28) | + (bta->source << 24) | + (bta->decimation << 8), + REG_GPIO_DMA_CTL); + } else { + btwrite(DMA_CTL_ACAP_EN | + DMA_CTL_RISC_EN | + DMA_CTL_FIFO_EN | + DMA_CTL_DA_ES2 | + DMA_CTL_A_PWRDN | + (1 << 6) | + ((bta->bits == 8) ? DMA_CTL_DA_SBR : 0) | + (bta->gain[bta->source] << 28) | + (bta->source << 24) | + (bta->decimation << 8), + REG_GPIO_DMA_CTL); + } + bta->dma_block = 0; + bta->read_offset = 0; + bta->read_count = 0; + bta->recording = 1; + if (debug) + printk(KERN_DEBUG "btaudio: recording started\n"); + return 0; +} + +static void stop_recording(struct btaudio *bta) +{ + btand(~15, REG_GPIO_DMA_CTL); + bta->recording = 0; + if (debug) + printk(KERN_DEBUG "btaudio: recording stopped\n"); +} + + +/* -------------------------------------------------------------- */ + +static int btaudio_mixer_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct btaudio *bta; + + for (bta = btaudios; bta != NULL; bta = bta->next) + if (bta->mixer_dev == minor) + break; + if (NULL == bta) + return -ENODEV; + + if (debug) + printk("btaudio: open mixer [%d]\n",minor); + file->private_data = bta; + return 0; +} + +static int btaudio_mixer_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int btaudio_mixer_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct btaudio *bta = file->private_data; + int ret,val=0,i=0; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + memset(&info,0,sizeof(info)); + strncpy(info.id,"bt878",sizeof(info.id)-1); + strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)-1); + info.modify_counter = bta->mixcount; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + memset(&info,0,sizeof(info)); + strncpy(info.id,"bt878",sizeof(info.id)-1); + strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)-1); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + /* read */ + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (cmd) { + case MIXER_READ(SOUND_MIXER_CAPS): + ret = SOUND_CAP_EXCL_INPUT; + break; + case MIXER_READ(SOUND_MIXER_STEREODEVS): + ret = 0; + break; + case MIXER_READ(SOUND_MIXER_RECMASK): + case MIXER_READ(SOUND_MIXER_DEVMASK): + ret = SOUND_MASK_LINE1|SOUND_MASK_LINE2|SOUND_MASK_LINE3; + break; + + case MIXER_WRITE(SOUND_MIXER_RECSRC): + if (val & SOUND_MASK_LINE1 && bta->source != 0) + bta->source = 0; + else if (val & SOUND_MASK_LINE2 && bta->source != 1) + bta->source = 1; + else if (val & SOUND_MASK_LINE3 && bta->source != 2) + bta->source = 2; + btaor((bta->gain[bta->source] << 28) | + (bta->source << 24), + 0x0cffffff, REG_GPIO_DMA_CTL); + case MIXER_READ(SOUND_MIXER_RECSRC): + switch (bta->source) { + case 0: ret = SOUND_MASK_LINE1; break; + case 1: ret = SOUND_MASK_LINE2; break; + case 2: ret = SOUND_MASK_LINE3; break; + default: ret = 0; + } + break; + + case MIXER_WRITE(SOUND_MIXER_LINE1): + case MIXER_WRITE(SOUND_MIXER_LINE2): + case MIXER_WRITE(SOUND_MIXER_LINE3): + if (MIXER_WRITE(SOUND_MIXER_LINE1) == cmd) + i = 0; + if (MIXER_WRITE(SOUND_MIXER_LINE2) == cmd) + i = 1; + if (MIXER_WRITE(SOUND_MIXER_LINE3) == cmd) + i = 2; + bta->gain[i] = (val & 0xff) * 15 / 100; + if (bta->gain[i] > 15) bta->gain[i] = 15; + if (bta->gain[i] < 0) bta->gain[i] = 0; + if (i == bta->source) + btaor((bta->gain[bta->source]<<28), + 0x0fffffff, REG_GPIO_DMA_CTL); + ret = bta->gain[i] * 100 / 15; + ret |= ret << 8; + break; + + case MIXER_READ(SOUND_MIXER_LINE1): + case MIXER_READ(SOUND_MIXER_LINE2): + case MIXER_READ(SOUND_MIXER_LINE3): + if (MIXER_READ(SOUND_MIXER_LINE1) == cmd) + i = 0; + if (MIXER_READ(SOUND_MIXER_LINE2) == cmd) + i = 1; + if (MIXER_READ(SOUND_MIXER_LINE3) == cmd) + i = 2; + ret = bta->gain[i] * 100 / 15; + ret |= ret << 8; + break; + + default: + return -EINVAL; + } + if (put_user(ret, (int *)arg)) + return -EFAULT; + return 0; +} + +static struct file_operations btaudio_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + open: btaudio_mixer_open, + release: btaudio_mixer_release, + ioctl: btaudio_mixer_ioctl, +}; + +/* -------------------------------------------------------------- */ + +static int btaudio_dsp_open(struct inode *inode, struct file *file, + struct btaudio *bta, int analog) +{ + down(&bta->lock); + if (bta->users) + goto busy; + bta->users++; + file->private_data = bta; + + bta->analog = analog; + bta->dma_block = 0; + bta->read_offset = 0; + bta->read_count = 0; + bta->sampleshift = 0; + + up(&bta->lock); + return 0; + + busy: + up(&bta->lock); + return -EBUSY; +} + +static int btaudio_dsp_open_digital(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct btaudio *bta; + + for (bta = btaudios; bta != NULL; bta = bta->next) + if (bta->dsp_digital == minor) + break; + if (NULL == bta) + return -ENODEV; + + if (debug) + printk("btaudio: open digital dsp [%d]\n",minor); + return btaudio_dsp_open(inode,file,bta,0); +} + +static int btaudio_dsp_open_analog(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct btaudio *bta; + + for (bta = btaudios; bta != NULL; bta = bta->next) + if (bta->dsp_analog == minor) + break; + if (NULL == bta) + return -ENODEV; + + if (debug) + printk("btaudio: open analog dsp [%d]\n",minor); + return btaudio_dsp_open(inode,file,bta,1); +} + +static int btaudio_dsp_release(struct inode *inode, struct file *file) +{ + struct btaudio *bta = file->private_data; + + down(&bta->lock); + if (bta->recording) + stop_recording(bta); + bta->users--; + up(&bta->lock); + return 0; +} + +static ssize_t btaudio_dsp_read(struct file *file, char *buffer, + size_t swcount, loff_t *ppos) +{ + struct btaudio *bta = file->private_data; + int hwcount = swcount << bta->sampleshift; + int nsrc, ndst, err, ret = 0; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&bta->readq, &wait); + down(&bta->lock); + while (swcount > 0) { + if (0 == bta->read_count) { + if (!bta->recording) { + if (0 != (err = start_recording(bta))) { + if (0 == ret) + ret = err; + break; + } + } + if (file->f_flags & O_NONBLOCK) { + if (0 == ret) + ret = -EAGAIN; + break; + } + up(&bta->lock); + current->state = TASK_INTERRUPTIBLE; + schedule(); + down(&bta->lock); + if(signal_pending(current)) { + if (0 == ret) + ret = -EINTR; + break; + } + } + nsrc = (bta->read_count < hwcount) ? bta->read_count : hwcount; + if (nsrc > bta->buf_size - bta->read_offset) + nsrc = bta->buf_size - bta->read_offset; + ndst = nsrc >> bta->sampleshift; + + if ((bta->analog && 0 == bta->sampleshift) || + (!bta->analog && 2 == bta->channels)) { + /* just copy */ + if (copy_to_user(buffer + ret, bta->buf_cpu + bta->read_offset, nsrc)) { + if (0 == ret) + ret = -EFAULT; + break; + } + + } else if (!bta->analog) { + /* stereo => mono (digital audio) */ + __s16 *src = (__s16*)(bta->buf_cpu + bta->read_offset); + __s16 *dst = (__s16*)(buffer + ret); + __s16 avg; + int n = ndst>>1; + if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { + if (0 == ret) + ret = -EFAULT; + break; + } + for (; n; n--, dst++) { + avg = (__s16)le16_to_cpu(*src) / 2; src++; + avg += (__s16)le16_to_cpu(*src) / 2; src++; + __put_user(cpu_to_le16(avg),(__u16*)(dst)); + } + + } else if (8 == bta->bits) { + /* copy + byte downsampling (audio A/D) */ + __u8 *src = bta->buf_cpu + bta->read_offset; + __u8 *dst = buffer + ret; + int n = ndst; + if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { + if (0 == ret) + ret = -EFAULT; + break; + } + for (; n; n--, src += (1 << bta->sampleshift), dst++) + __put_user(*src,(__u8*)(dst)); + + } else { + /* copy + word downsampling (audio A/D) */ + __u16 *src = (__u16*)(bta->buf_cpu + bta->read_offset); + __u16 *dst = (__u16*)(buffer + ret); + int n = ndst>>1; + if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { + if (0 == ret) + ret = -EFAULT; + break; + } + for (; n; n--, src += (1 << bta->sampleshift), dst++) + __put_user(*src,(__u16*)(dst)); + } + + ret += ndst; + swcount -= ndst; + hwcount -= nsrc; + bta->read_count -= nsrc; + bta->read_offset += nsrc; + if (bta->read_offset == bta->buf_size) + bta->read_offset = 0; + } + up(&bta->lock); + remove_wait_queue(&bta->readq, &wait); + current->state = TASK_RUNNING; + return ret; +} + +static ssize_t btaudio_dsp_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static int btaudio_dsp_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct btaudio *bta = file->private_data; + int s, i, ret, val = 0; + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + case SNDCTL_DSP_GETCAPS: + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int*)arg)) + return -EFAULT; + if (bta->analog) { + for (s = 0; s < 16; s++) + if (val << s >= HWBASE_AD*4/15) + break; + for (i = 15; i >= 5; i--) + if (val << s <= HWBASE_AD*4/i) + break; + bta->sampleshift = s; + bta->decimation = i; + if (debug) + printk(KERN_DEBUG "btaudio: rate: req=%d " + "dec=%d shift=%d hwrate=%d swrate=%d\n", + val,i,s,(HWBASE_AD*4/i),(HWBASE_AD*4/i)>>s); + } else { + bta->sampleshift = (bta->channels == 2) ? 0 : 1; + bta->decimation = 0; + } + if (bta->recording) { + down(&bta->lock); + stop_recording(bta); + start_recording(bta); + up(&bta->lock); + } + /* fall through */ + case SOUND_PCM_READ_RATE: + if (bta->analog) { + return put_user(HWBASE_AD*4/bta->decimation>>bta->sampleshift, (int*)arg); + } else { + return put_user(rate, (int*)arg); + } + + case SNDCTL_DSP_STEREO: + if (!bta->analog) { + if (get_user(val, (int*)arg)) + return -EFAULT; + bta->channels = (val > 0) ? 2 : 1; + bta->sampleshift = (bta->channels == 2) ? 0 : 1; + if (debug) + printk(KERN_INFO + "btaudio: stereo=%d channels=%d\n", + val,bta->channels); + } else { + if (val == 1) + return -EFAULT; + else { + bta->channels = 1; + if (debug) + printk(KERN_INFO + "btaudio: stereo=0 channels=1\n"); + } + } + return put_user((bta->channels)-1, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + if (!analog) { + if (get_user(val, (int*)arg)) + return -EFAULT; + bta->channels = (val > 1) ? 2 : 1; + bta->sampleshift = (bta->channels == 2) ? 0 : 1; + if (debug) + printk(KERN_DEBUG + "btaudio: val=%d channels=%d\n", + val,bta->channels); + } + /* fall through */ + case SOUND_PCM_READ_CHANNELS: + return put_user(bta->channels, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + if (analog) + return put_user(AFMT_S16_LE|AFMT_S8, (int*)arg); + else + return put_user(AFMT_S16_LE, (int*)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int*)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (analog) + bta->bits = (val == AFMT_S8) ? 8 : 16; + else + bta->bits = 16; + if (bta->recording) { + down(&bta->lock); + stop_recording(bta); + start_recording(bta); + up(&bta->lock); + } + } + if (debug) + printk(KERN_DEBUG "btaudio: fmt: bits=%d\n",bta->bits); + return put_user((bta->bits==16) ? AFMT_S16_LE : AFMT_S8, + (int*)arg); + break; + case SOUND_PCM_READ_BITS: + return put_user(bta->bits, (int*)arg); + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: + if (bta->recording) { + down(&bta->lock); + stop_recording(bta); + up(&bta->lock); + } + return 0; + case SNDCTL_DSP_GETBLKSIZE: + if (!bta->recording) { + if (0 != (ret = alloc_buffer(bta))) + return ret; + if (0 != (ret = make_risc(bta))) + return ret; + } + return put_user(bta->block_bytes>>bta->sampleshift,(int*)arg); + + case SNDCTL_DSP_SYNC: + /* NOP */ + return 0; + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info info; + if (!bta->recording) + return -EINVAL; + info.fragsize = bta->block_bytes>>bta->sampleshift; + info.fragstotal = bta->block_count; + info.bytes = bta->read_count; + info.fragments = info.bytes / info.fragsize; + if (debug) + printk(KERN_DEBUG "btaudio: SNDCTL_DSP_GETISPACE " + "returns %d/%d/%d/%d\n", + info.fragsize, info.fragstotal, + info.bytes, info.fragments); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } +#if 0 /* TODO */ + case SNDCTL_DSP_GETTRIGGER: + case SNDCTL_DSP_SETTRIGGER: + case SNDCTL_DSP_SETFRAGMENT: +#endif + default: + return -EINVAL; + } +} + +static unsigned int btaudio_dsp_poll(struct file *file, struct poll_table_struct *wait) +{ + struct btaudio *bta = file->private_data; + unsigned int mask = 0; + + poll_wait(file, &bta->readq, wait); + + if (0 == bta->read_count) + mask |= (POLLIN | POLLRDNORM); + + return mask; +} + +static struct file_operations btaudio_digital_dsp_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + open: btaudio_dsp_open_digital, + release: btaudio_dsp_release, + read: btaudio_dsp_read, + write: btaudio_dsp_write, + ioctl: btaudio_dsp_ioctl, + poll: btaudio_dsp_poll, +}; + +static struct file_operations btaudio_analog_dsp_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + open: btaudio_dsp_open_analog, + release: btaudio_dsp_release, + read: btaudio_dsp_read, + write: btaudio_dsp_write, + ioctl: btaudio_dsp_ioctl, + poll: btaudio_dsp_poll, +}; + +/* -------------------------------------------------------------- */ + +static char *irq_name[] = { "", "", "", "OFLOW", "", "", "", "", "", "", "", + "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR", + "RIPERR", "PABORT", "OCERR", "SCERR" }; + +static void btaudio_irq(int irq, void *dev_id, struct pt_regs * regs) +{ + int count = 0; + u32 stat,astat; + struct btaudio *bta = dev_id; + + for (;;) { + count++; + stat = btread(REG_INT_STAT); + astat = stat & btread(REG_INT_MASK); + if (!astat) + return; + btwrite(astat,REG_INT_STAT); + + if (irq_debug) { + int i; + printk(KERN_DEBUG "btaudio: irq loop=%d risc=%x, bits:", + count, stat>>28); + for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) { + if (stat & (1 << i)) + printk(" %s",irq_name[i]); + if (astat & (1 << i)) + printk("*"); + } + printk("\n"); + } + if (stat & IRQ_RISCI) { + int blocks; + blocks = (stat >> 28) - bta->dma_block; + if (blocks < 0) + blocks += bta->block_count; + bta->dma_block = stat >> 28; + if (bta->read_count + 2*bta->block_bytes > bta->buf_size) { + stop_recording(bta); + printk(KERN_INFO "btaudio: buffer overrun\n"); + } + if (blocks > 0) { + bta->read_count += blocks * bta->block_bytes; + wake_up_interruptible(&bta->readq); + } + } + if (count > 10) { + printk(KERN_WARNING + "btaudio: Oops - irq mask cleared\n"); + btwrite(0, REG_INT_MASK); + } + } + return; +} + +/* -------------------------------------------------------------- */ + +static int __devinit btaudio_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct btaudio *bta; + unsigned char revision,latency; + int rc = -EBUSY; + + if (pci_enable_device(pci_dev)) + return -EIO; + if (!request_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0), + "btaudio")) { + return -EBUSY; + } + + bta = kmalloc(sizeof(*bta),GFP_ATOMIC); + memset(bta,0,sizeof(*bta)); + + bta->pci = pci_dev; + bta->irq = pci_dev->irq; + bta->mem = pci_resource_start(pci_dev,0); + bta->mmio = ioremap(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0)); + + bta->source = 1; + bta->bits = 8; + bta->channels = 1; + if (bta->analog) { + bta->decimation = 15; + } else { + bta->decimation = 0; + bta->sampleshift = 1; + } + + init_MUTEX(&bta->lock); + init_waitqueue_head(&bta->readq); + + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &latency); + printk(KERN_INFO "btaudio: Bt%x (rev %d) at %02x:%02x.%x, ", + pci_dev->device,revision,pci_dev->bus->number, + PCI_SLOT(pci_dev->devfn),PCI_FUNC(pci_dev->devfn)); + printk("irq: %d, latency: %d, memory: 0x%lx\n", + bta->irq, latency, bta->mem); + + /* init hw */ + btwrite(0, REG_GPIO_DMA_CTL); + btwrite(0, REG_INT_MASK); + btwrite(~0x0UL, REG_INT_STAT); + pci_set_master(pci_dev); + + if ((rc = request_irq(bta->irq, btaudio_irq, SA_SHIRQ|SA_INTERRUPT, + "btaudio",(void *)bta)) < 0) { + printk(KERN_WARNING + "btaudio: can't request irq (rc=%d)\n",rc); + goto fail1; + } + + /* register devices */ + if (digital) { + rc = bta->dsp_digital = + register_sound_dsp(&btaudio_digital_dsp_fops,dsp1); + if (rc < 0) { + printk(KERN_WARNING + "btaudio: can't register digital dsp (rc=%d)\n",rc); + goto fail2; + } + } + if (analog) { + rc = bta->dsp_analog = + register_sound_dsp(&btaudio_analog_dsp_fops,dsp2); + if (rc < 0) { + printk(KERN_WARNING + "btaudio: can't register analog dsp (rc=%d)\n",rc); + goto fail3; + } + rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops,mixer); + if (rc < 0) { + printk(KERN_WARNING + "btaudio: can't register mixer (rc=%d)\n",rc); + goto fail4; + } + } + if (debug) + printk(KERN_DEBUG "btaudio: minors: digital=%d, analog=%d, mixer=%d\n", + bta->dsp_digital, bta->dsp_analog, bta->mixer_dev); + + /* hook into linked list */ + bta->next = btaudios; + btaudios = bta; + + pci_set_drvdata(pci_dev,bta); + return 0; + + fail4: + unregister_sound_dsp(bta->dsp_analog); + fail3: + if (digital) + unregister_sound_dsp(bta->dsp_digital); + fail2: + free_irq(bta->irq,bta); + fail1: + release_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0)); + kfree(bta); + return rc; +} + +static void __devexit btaudio_remove(struct pci_dev *pci_dev) +{ + struct btaudio *bta = pci_get_drvdata(pci_dev); + struct btaudio *walk; + + /* turn off all DMA / IRQs */ + btand(~15, REG_GPIO_DMA_CTL); + btwrite(0, REG_INT_MASK); + btwrite(~0x0UL, REG_INT_STAT); + + /* unregister devices */ + if (digital) { + unregister_sound_dsp(bta->dsp_digital); + } + if (analog) { + unregister_sound_dsp(bta->dsp_analog); + unregister_sound_mixer(bta->mixer_dev); + } + + /* free resources */ + free_buffer(bta); + free_irq(bta->irq,bta); + release_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0)); + + /* remove from linked list */ + if (bta == btaudios) { + btaudios = NULL; + } else { + for (walk = btaudios; walk->next != bta; walk = walk->next) + ; /* if (NULL == walk->next) BUG(); */ + walk->next = bta->next; + } + + pci_set_drvdata(pci_dev, NULL); + kfree(bta); + return; +} + +/* -------------------------------------------------------------- */ + +static struct pci_device_id btaudio_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_BROOKTREE, 0x0878, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_BROOKTREE, 0x0879, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +static struct pci_driver btaudio_pci_driver = { + name: "btaudio", + id_table: btaudio_pci_tbl, + probe: btaudio_probe, + remove: btaudio_remove, +}; + +int btaudio_init_module(void) +{ + printk(KERN_INFO "btaudio: driver version 0.6 loaded [%s%s%s]\n", + analog ? "analog" : "", + analog && digital ? "+" : "", + digital ? "digital" : ""); + return pci_module_init(&btaudio_pci_driver); +} + +void btaudio_cleanup_module(void) +{ + pci_unregister_driver(&btaudio_pci_driver); + return; +} + +module_init(btaudio_init_module); +module_exit(btaudio_cleanup_module); + +MODULE_PARM(dsp1,"i"); +MODULE_PARM(dsp2,"i"); +MODULE_PARM(mixer,"i"); +MODULE_PARM(debug,"i"); +MODULE_PARM(irq_debug,"i"); +MODULE_PARM(digital,"i"); +MODULE_PARM(analog,"i"); +MODULE_PARM(rate,"i"); + +MODULE_DEVICE_TABLE(pci, btaudio_pci_tbl); +MODULE_DESCRIPTION("bt878 audio dma driver"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nru a/sound/oss/cmpci.c b/sound/oss/cmpci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cmpci.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,3195 @@ +/*****************************************************************************/ +/* + * cmpci.c -- C-Media PCI audio driver. + * + * Copyright (C) 1999 ChenLi Tien (cltien@cmedia.com.tw) + * C-media support (support@cmedia.com.tw) + * + * Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * For update, visit: + * http://members.home.net/puresoft/cmedia.html + * http://www.cmedia.com.tw + * + * 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. + * + * Special thanks to David C. Niemi, Jan Pfeifer + * + * + * Module command line parameters: + * none so far + * + * + * 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 + * + * The card has both an FM and a Wavetable synth, but I have to figure + * out first how to drive them... + * + * Revision history + * 06.05.98 0.1 Initial release + * 10.05.98 0.2 Fixed many bugs, esp. ADC rate calculation + * First stab at a simple midi interface (no bells&whistles) + * 13.05.98 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of + * set_dac_rate in the FMODE_WRITE case in cm_open + * Fix hwptr out of bounds (now mpg123 works) + * 14.05.98 0.4 Don't allow excessive interrupt rates + * 08.06.98 0.5 First release using Alan Cox' soundcore instead of miscdevice + * 03.08.98 0.6 Do not include modversions.h + * Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * 31.08.98 0.7 Fix realplayer problems - dac.count issues + * 10.12.98 0.8 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.98 0.9 Fix a few f_file & FMODE_ bugs + * 06.01.99 0.10 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.99 0.11 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 18.08.99 1.5 Only deallocate DMA buffer when unloading. + * 02.09.99 1.6 Enable SPDIF LOOP + * Change the mixer read back + * 21.09.99 2.33 Use RCS version as driver version. + * Add support for modem, S/PDIF loop and 4 channels. + * (8738 only) + * Fix bug cause x11amp cannot play. + * + * Fixes: + * Arnaldo Carvalho de Melo + * 18/05/2001 - .bss nitpicks, fix a bug in set_dac_channels where it + * was calling prog_dmabuf with s->lock held, call missing + * unlock_kernel in cm_midi_release + * 08/10/2001 - use set_current_state in some more places + * + * Carlos Eduardo Gorges + * Fri May 25 2001 + * - SMP support ( spin[un]lock* revision ) + * - speaker mixer support + * Mon Aug 13 2001 + * - optimizations and cleanups + * + */ +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" + +/* --------------------------------------------------------------------- */ +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#undef DMABYTEIO +/* --------------------------------------------------------------------- */ + +#define CM_MAGIC ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A) + +/* CM8338 registers definition ****************/ + +#define CODEC_CMI_FUNCTRL0 (0x00) +#define CODEC_CMI_FUNCTRL1 (0x04) +#define CODEC_CMI_CHFORMAT (0x08) +#define CODEC_CMI_INT_HLDCLR (0x0C) +#define CODEC_CMI_INT_STATUS (0x10) +#define CODEC_CMI_LEGACY_CTRL (0x14) +#define CODEC_CMI_MISC_CTRL (0x18) +#define CODEC_CMI_TDMA_POS (0x1C) +#define CODEC_CMI_MIXER (0x20) +#define CODEC_SB16_DATA (0x22) +#define CODEC_SB16_ADDR (0x23) +#define CODEC_CMI_MIXER1 (0x24) +#define CODEC_CMI_MIXER2 (0x25) +#define CODEC_CMI_AUX_VOL (0x26) +#define CODEC_CMI_MISC (0x27) +#define CODEC_CMI_AC97 (0x28) + +#define CODEC_CMI_CH0_FRAME1 (0x80) +#define CODEC_CMI_CH0_FRAME2 (0x84) +#define CODEC_CMI_CH1_FRAME1 (0x88) +#define CODEC_CMI_CH1_FRAME2 (0x8C) + +#define CODEC_CMI_EXT_REG (0xF0) + +/* Mixer registers for SB16 ******************/ + +#define DSP_MIX_DATARESETIDX ((unsigned char)(0x00)) + +#define DSP_MIX_MASTERVOLIDX_L ((unsigned char)(0x30)) +#define DSP_MIX_MASTERVOLIDX_R ((unsigned char)(0x31)) +#define DSP_MIX_VOICEVOLIDX_L ((unsigned char)(0x32)) +#define DSP_MIX_VOICEVOLIDX_R ((unsigned char)(0x33)) +#define DSP_MIX_FMVOLIDX_L ((unsigned char)(0x34)) +#define DSP_MIX_FMVOLIDX_R ((unsigned char)(0x35)) +#define DSP_MIX_CDVOLIDX_L ((unsigned char)(0x36)) +#define DSP_MIX_CDVOLIDX_R ((unsigned char)(0x37)) +#define DSP_MIX_LINEVOLIDX_L ((unsigned char)(0x38)) +#define DSP_MIX_LINEVOLIDX_R ((unsigned char)(0x39)) + +#define DSP_MIX_MICVOLIDX ((unsigned char)(0x3A)) +#define DSP_MIX_SPKRVOLIDX ((unsigned char)(0x3B)) + +#define DSP_MIX_OUTMIXIDX ((unsigned char)(0x3C)) + +#define DSP_MIX_ADCMIXIDX_L ((unsigned char)(0x3D)) +#define DSP_MIX_ADCMIXIDX_R ((unsigned char)(0x3E)) + +#define DSP_MIX_INGAINIDX_L ((unsigned char)(0x3F)) +#define DSP_MIX_INGAINIDX_R ((unsigned char)(0x40)) +#define DSP_MIX_OUTGAINIDX_L ((unsigned char)(0x41)) +#define DSP_MIX_OUTGAINIDX_R ((unsigned char)(0x42)) + +#define DSP_MIX_AGCIDX ((unsigned char)(0x43)) + +#define DSP_MIX_TREBLEIDX_L ((unsigned char)(0x44)) +#define DSP_MIX_TREBLEIDX_R ((unsigned char)(0x45)) +#define DSP_MIX_BASSIDX_L ((unsigned char)(0x46)) +#define DSP_MIX_BASSIDX_R ((unsigned char)(0x47)) + +#define CM_CH0_RESET 0x04 +#define CM_CH1_RESET 0x08 +#define CM_EXTENT_CODEC 0x100 +#define CM_EXTENT_MIDI 0x2 +#define CM_EXTENT_SYNTH 0x4 +#define CM_INT_CH0 1 +#define CM_INT_CH1 2 + +#define CM_CFMT_STEREO 0x01 +#define CM_CFMT_16BIT 0x02 +#define CM_CFMT_MASK 0x03 +#define CM_CFMT_DACSHIFT 2 +#define CM_CFMT_ADCSHIFT 0 + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define CM_ENABLE_CH1 0x2 +#define CM_ENABLE_CH0 0x1 + +/* MIDI buffer sizes **************************/ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 2 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +#define SND_DEV_DSP16 5 + +#define NR_DEVICE 3 /* maximum number of devices */ + +/*********************************************/ + +struct cm_state { + unsigned int magic; /* magic */ + struct cm_state *next; /* we keep cm cards in a linked list */ + + int dev_audio; /* soundcore stuff */ + int dev_mixer; + int dev_midi; + int dev_dmfm; + + unsigned int iosb, iobase, iosynth, + iomidi, iogame, irq; /* hardware resources */ + unsigned short deviceid; /* pci_id */ + + struct { /* mixer stuff */ + unsigned int modcnt; + unsigned short vol[13]; + } mix; + + unsigned int rateadc, ratedac; /* wave stuff */ + unsigned char fmt, enable; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + unsigned rawphys; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + + unsigned fragsize; /* redundant, but makes calculations easier */ + unsigned dmasize; + unsigned fragsamples; + unsigned dmasamples; + + unsigned mapped:1; /* OSS stuff */ + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + struct { /* midi stuff */ + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + int chip_version; + int max_channels; + int curr_channels; + int speakers; /* number of speakers */ + int capability; /* HW capability, various for chip versions */ + + int status; /* HW or SW state */ + + int spdif_counter; /* spdif frame counter */ +}; + +/* flags used for capability */ +#define CAN_AC3_HW 0x00000001 /* 037 or later */ +#define CAN_AC3_SW 0x00000002 /* 033 or later */ +#define CAN_AC3 (CAN_AC3_HW | CAN_AC3_SW) +#define CAN_DUAL_DAC 0x00000004 /* 033 or later */ +#define CAN_MULTI_CH_HW 0x00000008 /* 039 or later */ +#define CAN_MULTI_CH (CAN_MULTI_CH_HW | CAN_DUAL_DAC) +#define CAN_LINE_AS_REAR 0x00000010 /* 033 or later */ +#define CAN_LINE_AS_BASS 0x00000020 /* 039 or later */ +#define CAN_MIC_AS_BASS 0x00000040 /* 039 or later */ + +/* flags used for status */ +#define DO_AC3_HW 0x00000001 +#define DO_AC3_SW 0x00000002 +#define DO_AC3 (DO_AC3_HW | DO_AC3_SW) +#define DO_DUAL_DAC 0x00000004 +#define DO_MULTI_CH_HW 0x00000008 +#define DO_MULTI_CH (DO_MULTI_CH_HW | DO_DUAL_DAC) +#define DO_LINE_AS_REAR 0x00000010 /* 033 or later */ +#define DO_LINE_AS_BASS 0x00000020 /* 039 or later */ +#define DO_MIC_AS_BASS 0x00000040 /* 039 or later */ +#define DO_SPDIF_OUT 0x00000100 +#define DO_SPDIF_IN 0x00000200 +#define DO_SPDIF_LOOP 0x00000400 + +static struct cm_state *devs; +static unsigned long wavetable_mem; + +/* --------------------------------------------------------------------- */ + +static inline unsigned ld2(unsigned int x) +{ + unsigned exp=16,l=5,r=0; + static const unsigned num[]={0x2,0x4,0x10,0x100,0x10000}; + + /* num: 2, 4, 16, 256, 65536 */ + /* exp: 1, 2, 4, 8, 16 */ + + while(l--) { + if( x >= num[l] ) { + if(num[l]>2) x >>= exp; + r+=exp; + } + exp>>=1; + } + + return r; +} + +/* --------------------------------------------------------------------- */ + +static void maskb(unsigned int addr, unsigned int mask, unsigned int value) +{ + outb((inb(addr) & mask) | value, addr); +} + +static void maskw(unsigned int addr, unsigned int mask, unsigned int value) +{ + outw((inw(addr) & mask) | value, addr); +} + +static void maskl(unsigned int addr, unsigned int mask, unsigned int value) +{ + outl((inl(addr) & mask) | value, addr); +} + +static void set_dmadac1(struct cm_state *s, unsigned int addr, unsigned int count) +{ + if (addr) + outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~1, 0); +} + +static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count) +{ + outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 1); +} + +static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count) +{ + outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 0); + if (s->status & DO_DUAL_DAC) + set_dmadac1(s, 0, count); +} + +static void set_countadc(struct cm_state *s, unsigned count) +{ + outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2); +} + +static void set_countdac(struct cm_state *s, unsigned count) +{ + outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2); + if (s->status & DO_DUAL_DAC) + set_countadc(s, count); +} + +static inline unsigned get_dmadac(struct cm_state *s) +{ + unsigned int curr_addr; + + curr_addr = inw(s->iobase + CODEC_CMI_CH1_FRAME2) + 1; + curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; + curr_addr = s->dma_dac.dmasize - curr_addr; + + return curr_addr; +} + +static inline unsigned get_dmaadc(struct cm_state *s) +{ + unsigned int curr_addr; + + curr_addr = inw(s->iobase + CODEC_CMI_CH0_FRAME2) + 1; + curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]; + curr_addr = s->dma_adc.dmasize - curr_addr; + + return curr_addr; +} + +static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data) +{ + outb(idx, s->iobase + CODEC_SB16_ADDR); + udelay(10); + outb(data, s->iobase + CODEC_SB16_DATA); + udelay(10); +} + +static unsigned char rdmixer(struct cm_state *s, unsigned char idx) +{ + unsigned char v; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + outb(idx, s->iobase + CODEC_SB16_ADDR); + udelay(10); + v = inb(s->iobase + CODEC_SB16_DATA); + udelay(10); + spin_unlock_irqrestore(&s->lock, flags); + return v; +} + +static void set_fmt_unlocked(struct cm_state *s, unsigned char mask, unsigned char data) +{ + if (mask) + { + s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT); + udelay(10); + } + s->fmt = (s->fmt & mask) | data; + outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT); + udelay(10); +} + +static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + set_fmt_unlocked(s,mask,data); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data) +{ + outb(idx, s->iobase + CODEC_SB16_ADDR); + udelay(10); + outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA); + udelay(10); +} + +static struct { + unsigned rate; + unsigned lower; + unsigned upper; + unsigned char freq; +} rate_lookup[] = +{ + { 5512, (0 + 5512) / 2, (5512 + 8000) / 2, 0 }, + { 8000, (5512 + 8000) / 2, (8000 + 11025) / 2, 4 }, + { 11025, (8000 + 11025) / 2, (11025 + 16000) / 2, 1 }, + { 16000, (11025 + 16000) / 2, (16000 + 22050) / 2, 5 }, + { 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2 }, + { 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6 }, + { 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3 }, + { 48000, (44100 + 48000) / 2, 48000, 7 } +}; + +static void set_spdifout_unlocked(struct cm_state *s, unsigned rate) +{ + if (rate == 48000 || rate == 44100) { + // SPDIFI48K SPDF_ACc97 + maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~0x01008000, rate == 48000 ? 0x01008000 : 0); + // ENSPDOUT + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0, 0x80); + // SPDF_1 SPD2DAC + maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x240); + // CDPLAY + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1); + s->status |= DO_SPDIF_OUT; + } else { + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0x80, 0); + maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0x240, 0); + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0); + s->status &= ~DO_SPDIF_OUT; + } +} + +static void set_spdifout(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + set_spdifout_unlocked(s,rate); + spin_unlock_irqrestore(&s->lock, flags); +} + +/* find parity for bit 4~30 */ +static unsigned parity(unsigned data) +{ + unsigned parity = 0; + int counter = 4; + + data >>= 4; // start from bit 4 + while (counter <= 30) { + if (data & 1) + parity++; + data >>= 1; + counter++; + } + return parity & 1; +} + +static void set_ac3_unlocked(struct cm_state *s, unsigned rate) +{ + /* enable AC3 */ + if (rate == 48000 || rate == 44100) { + // mute DAC + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x40); + // AC3EN for 037, 0x10 + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x10); + // AC3EN for 039, 0x04 + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x04); + if (s->capability & CAN_AC3_HW) { + // SPD24SEL for 037, 0x02 + // SPD24SEL for 039, 0x20, but cannot be set + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x02); + s->status |= DO_AC3_HW; + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0); + } else { + // SPD32SEL for 037 & 039, 0x20 + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x20); + // set 176K sample rate to fix 033 HW bug + if (s->chip_version == 33) { + if (rate == 48000) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0, 0x08); + else + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + } + s->status |= DO_AC3_SW; + } + } else { + maskb(s->iobase + CODEC_CMI_MIXER1, ~0x40, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0x32, 0); + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x24, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + if (s->chip_version == 33) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1); + s->status &= ~DO_AC3; + } + s->spdif_counter = 0; + +} + +static void set_ac3(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + set_spdifout_unlocked(s, rate); + set_ac3_unlocked(s,rate); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void trans_ac3(struct cm_state *s, void *dest, const char *source, int size) +{ + int i = size / 2; + unsigned long data; + unsigned long *dst = (unsigned long *) dest; + unsigned short *src = (unsigned short *)source; + + do { + data = (unsigned long) *src++; + data <<= 12; // ok for 16-bit data + if (s->spdif_counter == 2 || s->spdif_counter == 3) + data |= 0x40000000; // indicate AC-3 raw data + if (parity(data)) + data |= 0x80000000; // parity + if (s->spdif_counter == 0) + data |= 3; // preamble 'M' + else if (s->spdif_counter & 1) + data |= 5; // odd, 'W' + else + data |= 9; // even, 'M' + *dst++ = data; + s->spdif_counter++; + if (s->spdif_counter == 384) + s->spdif_counter = 0; + } while (--i); +} + +static void set_adc_rate_unlocked(struct cm_state *s, unsigned rate) +{ + unsigned char freq = 4; + int i; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { + rate = rate_lookup[i].rate; + freq = rate_lookup[i].freq; + break; + } + } + s->rateadc = rate; + freq <<= 2; + + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq); +} + +static void set_adc_rate(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + unsigned char freq = 4; + int i; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { + rate = rate_lookup[i].rate; + freq = rate_lookup[i].freq; + break; + } + } + s->rateadc = rate; + freq <<= 2; + + spin_lock_irqsave(&s->lock, flags); + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void set_dac_rate(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + unsigned char freq = 4; + int i; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { + rate = rate_lookup[i].rate; + freq = rate_lookup[i].freq; + break; + } + } + s->ratedac = rate; + freq <<= 5; + + spin_lock_irqsave(&s->lock, flags); + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0xe0, freq); + + + if (s->curr_channels <= 2) + set_spdifout_unlocked(s, rate); + if (s->status & DO_DUAL_DAC) + set_adc_rate_unlocked(s, rate); + + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ +static inline void reset_adc(struct cm_state *s) +{ + /* reset bus master */ + outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + udelay(10); + outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); +} + +static inline void reset_dac(struct cm_state *s) +{ + /* reset bus master */ + outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + if (s->status & DO_DUAL_DAC) + reset_adc(s); +} + +static inline void pause_adc(struct cm_state *s) +{ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 4); +} + +static inline void pause_dac(struct cm_state *s) +{ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 8); + if (s->status & DO_DUAL_DAC) + pause_adc(s); +} + +static inline void disable_adc(struct cm_state *s) +{ + /* disable channel */ + s->enable &= ~CM_ENABLE_CH0; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + reset_adc(s); +} + +static inline void disable_dac(struct cm_state *s) +{ + /* disable channel */ + s->enable &= ~CM_ENABLE_CH1; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + reset_dac(s); + if (s->status & DO_DUAL_DAC) + disable_adc(s); +} + +static inline void enable_adc(struct cm_state *s) +{ + if (!(s->enable & CM_ENABLE_CH0)) { + /* enable channel */ + s->enable |= CM_ENABLE_CH0; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + } + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~4, 0); +} + +static inline void enable_dac_unlocked(struct cm_state *s) +{ + if (!(s->enable & CM_ENABLE_CH1)) { + /* enable channel */ + s->enable |= CM_ENABLE_CH1; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + } + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~8, 0); + + if (s->status & DO_DUAL_DAC) + enable_adc(s); +} + +static inline void enable_dac(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + enable_dac_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_adc_unlocked(struct cm_state *s) +{ + if (s->enable & CM_ENABLE_CH0) { + /* disable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~1, 0); + disable_adc(s); + } +} + +static inline void stop_adc(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + stop_adc_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); + +} + +static inline void stop_dac_unlocked(struct cm_state *s) +{ + if (s->enable & CM_ENABLE_CH1) { + /* disable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~2, 0); + disable_dac(s); + } + if (s->status & DO_DUAL_DAC) + stop_adc_unlocked(s); +} + +static inline void stop_dac(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + stop_dac_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc_unlocked(struct cm_state *s) +{ + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + /* enable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1); + enable_adc(s); + } +} + +static void start_adc(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + start_adc_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1_unlocked(struct cm_state *s) +{ + if ((s->dma_adc.mapped || s->dma_adc.count > 0) && s->dma_adc.ready) { + /* enable interrupt */ +// maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1); + enable_dac_unlocked(s); + } +} + +static void start_dac_unlocked(struct cm_state *s) +{ + if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + /* enable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 2); + enable_dac_unlocked(s); + } + if (s->status & DO_DUAL_DAC) + start_dac1_unlocked(s); +} + +static void start_dac(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + start_dac_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static int prog_dmabuf(struct cm_state *s, unsigned rec); + +static int set_dac_channels(struct cm_state *s, int channels) +{ + unsigned long flags; + spin_lock_irqsave(&s->lock, flags); + + if ((channels > 2) && (channels <= s->max_channels) + && (((s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK) == (CM_CFMT_STEREO | CM_CFMT_16BIT))) { + set_spdifout_unlocked(s, 0); + if (s->capability & CAN_MULTI_CH_HW) { + // NXCHG + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0, 0x80); + // CHB3D or CHB3D5C + maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, channels > 4 ? 0x80 : 0x20); + // CHB3D6C + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, channels == 6 ? 0x80 : 0); + // ENCENTER + maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~0x80, channels == 6 ? 0x80 : 0); + s->status |= DO_MULTI_CH_HW; + } else if (s->capability & CAN_DUAL_DAC) { + unsigned char fmtm = ~0, fmts = 0; + ssize_t ret; + + // ENDBDAC, turn on double DAC mode + // XCHGDAC, CH0 -> back, CH1->front + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0xC0); + s->status |= DO_DUAL_DAC; + // prepare secondary buffer + + spin_unlock_irqrestore(&s->lock, flags); + ret = prog_dmabuf(s, 1); + if (ret) return ret; + spin_lock_irqsave(&s->lock, flags); + + // copy the hw state + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); + // the HW only support 16-bit stereo + fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; + fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; + fmts |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + fmts |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + + set_fmt_unlocked(s, fmtm, fmts); + set_adc_rate_unlocked(s, s->ratedac); + + } + + if (s->speakers > 2) + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0x04, 0); + s->curr_channels = channels; + } else { + if (s->status & DO_MULTI_CH_HW) { + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x80, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, 0); + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, 0); + } else if (s->status & DO_DUAL_DAC) { + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x80, 0); + } + // N4SPK3D, enable 4 speaker mode (analog duplicate) + if (s->speakers > 2) + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0, 0x04); + s->status &= ~DO_MULTI_CH; + s->curr_channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; + } + + spin_unlock_irqrestore(&s->lock, flags); + return s->curr_channels; +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static void dealloc_dmabuf(struct dmabuf *db) +{ + struct page *pstart, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) + mem_map_unreserve(pstart); + free_pages((unsigned long)db->rawbuf, db->buforder); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +/* Ch1 is used for playback, Ch0 is used for recording */ + +static int prog_dmabuf(struct cm_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + int order; + unsigned bytepersec; + unsigned bufs; + struct page *pstart, *pend; + unsigned char fmt; + unsigned long flags; + + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= CM_CFMT_ADCSHIFT; + } else { + stop_dac(s); + fmt >>= CM_CFMT_DACSHIFT; + } + + fmt &= CM_CFMT_MASK; + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + db->rawphys = virt_to_bus(db->rawbuf); + if ((db->rawphys ^ (db->rawphys + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) + printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", + (long) db->rawphys, PAGE_SIZE << db->buforder); + if ((db->rawphys + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) + printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", + (long) db->rawphys, PAGE_SIZE << db->buforder); + /* 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 (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) + mem_map_reserve(pstart); + } + 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--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + /* to make fragsize >= 4096 */ + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + db->dmasamples = db->dmasize >> sample_shift[fmt]; + memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize); + spin_lock_irqsave(&s->lock, flags); + if (rec) { + if (s->status & DO_DUAL_DAC) + set_dmadac1(s, db->rawphys, db->dmasize >> sample_shift[fmt]); + else + set_dmaadc(s, db->rawphys, db->dmasize >> sample_shift[fmt]); + /* program sample counts */ + set_countdac(s, db->fragsamples); + } else { + set_dmadac(s, db->rawphys, db->dmasize >> sample_shift[fmt]); + /* program sample counts */ + set_countdac(s, db->fragsamples); + } + spin_unlock_irqrestore(&s->lock, flags); + db->ready = 1; + return 0; +} + +static inline void clear_advance(struct cm_state *s) +{ + unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80; + unsigned char *buf = s->dma_dac.rawbuf; + unsigned char *buf1 = s->dma_adc.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + if (s->status & DO_DUAL_DAC) + memset(buf1 + bptr, c, x); + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); + if (s->status & DO_DUAL_DAC) + memset(buf1 + bptr, c, len); +} + +/* call with spinlock held! */ +static void cm_update_ptr(struct cm_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + if (s->status & DO_DUAL_DAC) { + hwptr = get_dmaadc(s) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + if (s->dma_adc.mapped) { + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + s->dma_adc.count -= diff; + if (s->dma_adc.count <= 0) { + pause_adc(s); + s->dma_adc.error++; + } else if (s->dma_adc.count <= (signed)s->dma_adc.fragsize && !s->dma_adc.endcleared) { + clear_advance(s); + s->dma_adc.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) + wake_up(&s->dma_adc.wait); + } + } else { + hwptr = get_dmaadc(s) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + pause_adc(s); + s->dma_adc.error++; + } + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = get_dmadac(s) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + pause_dac(s); + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +#ifdef CONFIG_SOUND_CMPCI_MIDI +/* hold spinlock for the following! */ +static void cm_handle_midi(struct cm_state *s) +{ + unsigned char ch; + int wake; + + wake = 0; + while (!(inb(s->iomidi+1) & 0x80)) { + ch = inb(s->iomidi); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->iomidi); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} +#endif + +static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cm_state *s = (struct cm_state *)dev_id; + unsigned int intsrc, intstat; + unsigned char mask = 0; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS); + if (!(intsrc & 0x80000000)) + return; + spin_lock(&s->lock); + intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2); + /* acknowledge interrupt */ + if (intsrc & CM_INT_CH0) + mask |= 1; + if (intsrc & CM_INT_CH1) + mask |= 2; + outb(intstat & ~mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + outb(intstat | mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + cm_update_ptr(s); +#ifdef CONFIG_SOUND_CMPCI_MIDI + cm_handle_midi(s); +#endif + spin_unlock(&s->lock); +} + +#ifdef CONFIG_SOUND_CMPCI_MIDI +static void cm_midi_timer(unsigned long data) +{ + struct cm_state *s = (struct cm_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + cm_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} +#endif + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n"; + +#ifdef CONFIG_SOUND_CMPCI /* support multiple chips */ +#define VALIDATE_STATE(s) +#else +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != CM_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) +#endif + +/* --------------------------------------------------------------------- */ + +#define MT_4 1 +#define MT_5MUTE 2 +#define MT_4MUTEMONO 3 +#define MT_6MUTE 4 +#define MT_5MUTEMONO 5 + +static const struct { + unsigned left; + unsigned right; + unsigned type; + unsigned rec; + unsigned play; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_CD] = { DSP_MIX_CDVOLIDX_L, DSP_MIX_CDVOLIDX_R, MT_5MUTE, 0x04, 0x02 }, + [SOUND_MIXER_LINE] = { DSP_MIX_LINEVOLIDX_L, DSP_MIX_LINEVOLIDX_R, MT_5MUTE, 0x10, 0x08 }, + [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, DSP_MIX_MICVOLIDX, MT_5MUTEMONO, 0x01, 0x01 }, + [SOUND_MIXER_SYNTH] = { DSP_MIX_FMVOLIDX_L, DSP_MIX_FMVOLIDX_R, MT_5MUTE, 0x40, 0x00 }, + [SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, + [SOUND_MIXER_PCM] = { DSP_MIX_VOICEVOLIDX_L, DSP_MIX_VOICEVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, + [SOUND_MIXER_SPEAKER]= { DSP_MIX_SPKRVOLIDX, DSP_MIX_SPKRVOLIDX, MT_5MUTEMONO, 0x01, 0x01 } +}; + +static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = +{ + [SOUND_MIXER_CD] = 1, + [SOUND_MIXER_LINE] = 2, + [SOUND_MIXER_MIC] = 3, + [SOUND_MIXER_SYNTH] = 4, + [SOUND_MIXER_VOLUME] = 5, + [SOUND_MIXER_PCM] = 6, + [SOUND_MIXER_SPEAKER]= 7 +}; + +static unsigned mixer_recmask(struct cm_state *s) +{ + int i, j, k; + + j = rdmixer(s, DSP_MIX_ADCMIXIDX_L); + j &= 0x7f; + for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (j & mixtable[i].rec) + k |= 1 << i; + return k; +} + +static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val, j; + unsigned char l, r, rl, rr; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + 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; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + 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; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_recmask(s), (int *)arg); + + case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_recmask(s), (int *)arg);//need fix + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].rec) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_OUTMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].play) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(0, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + if (!volidx[i]) + return -EINVAL; + return put_user(s->mix.vol[volidx[i]-1], (int *)arg); + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + 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 = generic_hweight32(val); + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].rec) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].rec; + } + spin_lock_irqsave(&s->lock, flags); + wrmixer(s, DSP_MIX_ADCMIXIDX_L, j); + wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | (j>>1)); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ + if (get_user(val, (int *)arg)) + return -EFAULT; + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].play) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].play; + } + spin_lock_irqsave(&s->lock, flags); + frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, j); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + l = val & 0xff; + r = (val >> 8) & 0xff; + if (l > 100) + l = 100; + if (r > 100) + r = 100; + spin_lock_irqsave(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + if (l >= 10) + l -= 10; + if (r >= 10) + r -= 10; + frobindir(s, mixtable[i].left, 0xf0, l / 6); + frobindir(s, mixtable[i].right, 0xf0, l / 6); + break; + + case MT_4MUTEMONO: + rl = (l < 4 ? 0 : (l - 5) / 3) & 31; + rr = (rl >> 2) & 7; + wrmixer(s, mixtable[i].left, rl<<3); + maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); + break; + + case MT_5MUTEMONO: + r = l; + rl = l < 4 ? 0 : (l - 5) / 3; + rr = rl >> 2; + wrmixer(s, mixtable[i].left, rl<<3); + maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); + break; + + case MT_5MUTE: + rl = l < 4 ? 0 : (l - 5) / 3; + rr = r < 4 ? 0 : (r - 5) / 3; + wrmixer(s, mixtable[i].left, rl<<3); + wrmixer(s, mixtable[i].right, rr<<3); + break; + + case MT_6MUTE: + if (l < 6) + rl = 0x00; + else + rl = l * 2 / 3; + if (r < 6) + rr = 0x00; + else + rr = r * 2 / 3; + wrmixer(s, mixtable[i].left, rl); + wrmixer(s, mixtable[i].right, rr); + break; + } + spin_unlock_irqrestore(&s->lock, flags); + + if (!volidx[i]) + return -EINVAL; + s->mix.vol[volidx[i]-1] = val; + return put_user(s->mix.vol[volidx[i]-1], (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +static int cm_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct cm_state *s = devs; + + while (s && s->dev_mixer != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + return 0; +} + +static int cm_release_mixdev(struct inode *inode, struct file *file) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int cm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct cm_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations cm_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: cm_ioctl_mixdev, + open: cm_open_mixdev, + release: cm_release_mixdev, +}; + + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct cm_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&s->dma_dac.wait, &wait); + 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) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; + tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cmpci: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t cm_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + 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(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { + printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + spin_lock_irqsave(&s->lock, flags); + stop_adc_unlocked(s); + set_dmaadc(s, s->dma_adc.rawphys, s->dma_adc.dmasamples); + /* program sample counts */ + set_countadc(s, s->dma_adc.fragsamples); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + +static ssize_t cm_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + 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(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (s->status & DO_DUAL_DAC) { + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + } + ret = 0; + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + if (s->status & DO_DUAL_DAC) { + s->dma_adc.swptr = s->dma_dac.swptr; + s->dma_adc.count = s->dma_dac.count; + s->dma_adc.endcleared = s->dma_dac.endcleared; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->status & DO_AC3_SW) { + if (s->dma_dac.count + 2 * cnt > s->dma_dac.dmasize) + cnt = (s->dma_dac.dmasize - s->dma_dac.count) / 2; + } else { + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if ((s->status & DO_DUAL_DAC) && (cnt > count / 2)) + cnt = count / 2; + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { + printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + spin_lock_irqsave(&s->lock, flags); + stop_dac_unlocked(s); + set_dmadac(s, s->dma_dac.rawphys, s->dma_dac.dmasamples); + /* program sample counts */ + set_countdac(s, s->dma_dac.fragsamples); + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + if (s->status & DO_DUAL_DAC) { + set_dmadac1(s, s->dma_adc.rawphys, s->dma_adc.dmasamples); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (s->status & DO_AC3_SW) { + // clip exceeded data, caught by 033 and 037 + if (swptr + 2 * cnt > s->dma_dac.dmasize) + cnt = (s->dma_dac.dmasize - swptr) / 2; + trans_ac3(s, s->dma_dac.rawbuf + swptr, buffer, cnt); + swptr = (swptr + 2 * cnt) % s->dma_dac.dmasize; + } else if (s->status & DO_DUAL_DAC) { + int i; + unsigned long *src, *dst0, *dst1; + + src = (unsigned long *) buffer; + dst0 = (unsigned long *) (s->dma_dac.rawbuf + swptr); + dst1 = (unsigned long *) (s->dma_adc.rawbuf + swptr); + // copy left/right sample at one time + for (i = 0; i <= cnt / 4; i++) { + *dst0++ = *src++; + *dst1++ = *src++; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } else { + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + if (s->status & DO_AC3_SW) + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->status & DO_DUAL_DAC) { + count -= cnt; + buffer += cnt; + ret += cnt; + } + start_dac(s); + } + return ret; +} + +static unsigned int cm_poll(struct file *file, struct poll_table_struct *wait) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + 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); + cm_update_ptr(s); + 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 cm_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 0)) != 0) + goto out; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 1)) != 0) + goto out; + db = &s->dma_adc; + } else + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EINVAL; + if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int cm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + + 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: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + 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 | DSP_CAP_BIND, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + if (s->status & DO_DUAL_DAC) + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&s->lock, flags); + stop_adc_unlocked(s); + s->dma_adc.ready = 0; + set_adc_rate_unlocked(s, val); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (s->status & DO_DUAL_DAC) + s->dma_adc.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + } + set_fmt(s, fmtm, fmtd); + if ((s->capability & CAN_MULTI_CH) + && (file->f_mode & FMODE_WRITE)) { + val = set_dac_channels(s, val); + return put_user(val, (int *)arg); + } + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) + : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8|AFMT_AC3, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_ADCSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE || val == AFMT_AC3) + fmtd |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; + else + fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_DACSHIFT); + if (val == AFMT_AC3) { + fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + set_ac3(s, s->ratedac); + } else + set_ac3(s, 0); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + } + set_fmt(s, fmtm, fmtd); + } + if (s->status & DO_AC3) return put_user(AFMT_AC3, (int *)arg); + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) + : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (s->status & DO_DUAL_DAC) { + if (file->f_mode & FMODE_WRITE && + (s->enable & CM_ENABLE_CH1) && + (s->enable & CM_ENABLE_CH0)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + } + if (file->f_mode & FMODE_READ && s->enable & CM_ENABLE_CH0) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->enable & CM_ENABLE_CH1) + 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(s, 1))) + 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(s, 0))) + return ret; + if (s->status & DO_DUAL_DAC) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + 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->enable & CM_ENABLE_CH1) && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + 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->enable & CM_ENABLE_CH0) && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + 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; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + 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; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + 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; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + 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; + if (s->status & DO_DUAL_DAC) { + 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_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + if (s->status & DO_DUAL_DAC) { + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(2 * s->dma_dac.fragsize, (int *)arg); + } + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + 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) { + 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 (file->f_mode & FMODE_WRITE) { + 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 (s->status & DO_DUAL_DAC) { + s->dma_adc.ossfragshift = s->dma_dac.ossfragshift; + s->dma_adc.ossmaxfrags = s->dma_dac.ossmaxfrags; + } + } + 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) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.subdivision = val; + if (s->status & DO_DUAL_DAC) + s->dma_adc.subdivision = val; + } + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? 16 : 8, (int *)arg); + + case SOUND_PCM_READ_FILTER: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_GETCHANNELMASK: + return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE|DSP_BIND_SPDIF, (int *)arg); + + case SNDCTL_DSP_BIND_CHANNEL: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val == DSP_BIND_QUERY) { + val = DSP_BIND_FRONT; + if (s->status & DO_SPDIF_OUT) + val |= DSP_BIND_SPDIF; + else { + if (s->curr_channels == 4) + val |= DSP_BIND_SURR; + if (s->curr_channels > 4) + val |= DSP_BIND_CENTER_LFE; + } + } else { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val & DSP_BIND_SPDIF) { + set_spdifout(s, s->ratedac); + set_dac_channels(s, s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1); + if (!(s->status & DO_SPDIF_OUT)) + val &= ~DSP_BIND_SPDIF; + } else { + int channels; + int mask; + + 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: + channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; + break; + } + set_dac_channels(s, channels); + } + } + } + return put_user(val, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int cm_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct cm_state *s = devs; + unsigned char fmtm = ~0, fmts = 0; + + while (s && ((s->dev_audio ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + 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; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + if (file->f_mode & FMODE_READ) { + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + // clear previous multichannel, spdif, ac3 state + set_spdifout(s, 0); + if (s->deviceid == PCI_DEVICE_ID_CMEDIA_CM8738) { + set_ac3(s, 0); + set_dac_channels(s, 1); + } + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + return 0; +} + +static int cm_release(struct inode *inode, struct file *file) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + + VALIDATE_STATE(s); + 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->dma_dac); + if (s->status & DO_DUAL_DAC) + dealloc_dmabuf(&s->dma_adc); + + if (s->status & DO_MULTI_CH) + set_dac_channels(s, 0); + if (s->status & DO_AC3) + set_ac3(s, 0); + if (s->status & DO_SPDIF_OUT) + set_spdifout(s, 0); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(&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 cm_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: cm_read, + write: cm_write, + poll: cm_poll, + ioctl: cm_ioctl, + mmap: cm_mmap, + open: cm_open, + release: cm_release, +}; + +#ifdef CONFIG_SOUND_CMPCI_MIDI +/* --------------------------------------------------------------------- */ + +static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + { + if (!ret) + ret = -EAGAIN; + break; + } + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) + { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + cm_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + { + if (!ret) + ret = -EAGAIN; + break; + } + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + cm_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +static unsigned int cm_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int cm_midi_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct cm_state *s = devs; + unsigned long flags; + + while (s && s->dev_midi != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + /* enable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 4); + outb(0xff, s->iomidi+1); /* reset command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + outb(0x3f, s->iomidi+1); /* uart command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = cm_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + return 0; +} + +static int cm_midi_release(struct inode *inode, struct file *file) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + lock_kernel(); + + if (file->f_mode & FMODE_WRITE) { + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + 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; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "cmpci: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + del_timer(&s->midi.timer); + outb(0xff, s->iomidi+1); /* reset command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + /* disable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~4, 0); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations cm_midi_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: cm_midi_read, + write: cm_midi_write, + poll: cm_midi_poll, + open: cm_midi_open, + release: cm_midi_release, +}; +#endif + +/* --------------------------------------------------------------------- */ + +#ifdef CONFIG_SOUND_CMPCI_FM +static int cm_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct cm_state *s = (struct cm_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->iosynth + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void *)arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->iosynth); + outb((p.kbd_split & 1) << 6, s->iosynth+1); + outb(0xbd, s->iosynth); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->iosynth+2); + outb(arg, s->iosynth+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->iosynth+2); + outb(arg & 1, s->iosynth+3); + return 0; + } + return -EINVAL; +} + +static int cm_dmfm_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct cm_state *s = devs; + + while (s && s->dev_dmfm != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DMFM) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + /* init the stuff */ + outb(1, s->iosynth); + outb(0x20, s->iosynth+1); /* enable waveforms */ + outb(4, s->iosynth+2); + outb(0, s->iosynth+3); /* no 4op enabled */ + outb(5, s->iosynth+2); + outb(1, s->iosynth+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + up(&s->open_sem); + return 0; +} + +static int cm_dmfm_release(struct inode *inode, struct file *file) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + lock_kernel(); + down(&s->open_sem); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations cm_dmfm_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: cm_dmfm_ioctl, + open: cm_dmfm_open, + release: cm_dmfm_release, +}; +#endif /* CONFIG_SOUND_CMPCI_FM */ + + + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_CD, 0x4f4f }, + { SOUND_MIXER_WRITE_LINE, 0x4f4f }, + { SOUND_MIXER_WRITE_MIC, 0x4f4f }, + { SOUND_MIXER_WRITE_SYNTH, 0x4f4f }, + { SOUND_MIXER_WRITE_VOLUME, 0x4f4f }, + { SOUND_MIXER_WRITE_PCM, 0x4f4f } +}; + +/* check chip version and capability */ +static int query_chip(struct cm_state *s) +{ + int ChipVersion = -1; + unsigned char RegValue; + + // check reg 0Ch, bit 24-31 + RegValue = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 3); + if (RegValue == 0) { + // check reg 08h, bit 24-28 + RegValue = inb(s->iobase + CODEC_CMI_CHFORMAT + 3); + RegValue &= 0x1f; + if (RegValue == 0) { + ChipVersion = 33; + s->max_channels = 4; + s->capability |= CAN_AC3_SW; + s->capability |= CAN_DUAL_DAC; + } else { + ChipVersion = 37; + s->max_channels = 4; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + } + } else { + // check reg 0Ch, bit 26 + if (RegValue & (1 << (26-24))) { + ChipVersion = 39; + if (RegValue & (1 << (24-24))) + s->max_channels = 6; + else + s->max_channels = 4; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + s->capability |= CAN_MULTI_CH_HW; + } else { + ChipVersion = 55; // 4 or 6 channels + s->max_channels = 6; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + s->capability |= CAN_MULTI_CH_HW; + } + } + // still limited to number of speakers + if (s->max_channels > s->speakers) + s->max_channels = s->speakers; + return ChipVersion; +} + +#ifdef CONFIG_SOUND_CMPCI_MIDI +static int mpuio = CONFIG_SOUND_CMPCI_MPUIO; +#else +static int mpuio; +#endif +#ifdef CONFIG_SOUND_CMPCI_FM +static int fmio = CONFIG_SOUND_CMPCI_FMIO; +#else +static int fmio; +#endif +#ifdef CONFIG_SOUND_CMPCI_SPDIFINVERSE +static int spdif_inverse = 1; +#else +static int spdif_inverse; +#endif +#ifdef CONFIG_SOUND_CMPCI_SPDIFLOOP +static int spdif_loop = 1; +#else +static int spdif_loop; +#endif +#ifdef CONFIG_SOUND_CMPCI_SPEAKERS +static int speakers = CONFIG_SOUND_CMPCI_SPEAKERS; +#else +static int speakers = 2; +#endif +#ifdef CONFIG_SOUND_CMPCI_LINE_REAR +static int use_line_as_rear = 1; +#else +static int use_line_as_rear; +#endif +#ifdef CONFIG_SOUND_CMPCI_LINE_BASS +static int use_line_as_bass = 1; +#else +static int use_line_as_bass; +#endif +#ifdef CONFIG_SOUND_CMPCI_JOYSTICK +static int joystick = 1; +#else +static int joystick; +#endif +MODULE_PARM(mpuio, "i"); +MODULE_PARM(fmio, "i"); +MODULE_PARM(spdif_inverse, "i"); +MODULE_PARM(spdif_loop, "i"); +MODULE_PARM(speakers, "i"); +MODULE_PARM(use_line_as_rear, "i"); +MODULE_PARM(use_line_as_bass, "i"); +MODULE_PARM(joystick, "i"); +MODULE_PARM_DESC(mpuio, "(0x330, 0x320, 0x310, 0x300) Base of MPU-401, 0 to disable"); +MODULE_PARM_DESC(fmio, "(0x388, 0x3C8, 0x3E0) Base of OPL3, 0 to disable"); +MODULE_PARM_DESC(spdif_inverse, "(1/0) Invert S/PDIF-in signal"); +MODULE_PARM_DESC(spdif_loop, "(1/0) Route S/PDIF-in to S/PDIF-out directly"); +MODULE_PARM_DESC(speakers, "(2-6) Number of speakers you connect"); +MODULE_PARM_DESC(use_line_as_rear, "(1/0) Use line-in jack as rear-out"); +MODULE_PARM_DESC(use_line_as_bass, "(1/0) Use line-in jack as bass/center"); +MODULE_PARM_DESC(joystick, "(1/0) Enable joystick interface, still need joystick driver"); + +static struct pci_device_id cmpci_pci_tbl[] = { + { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, cmpci_pci_tbl); + +void initialize_chip(struct pci_dev *pcidev) +{ + struct cm_state *s; + mm_segment_t fs; + int i, val; +#if defined(CONFIG_SOUND_CMPCI_MIDI) || defined(CONFIG_SOUND_CMPCI_FM) + unsigned char reg_mask = 0; +#endif + struct { + unsigned short deviceid; + char *devicename; + } devicetable[] = + { + { PCI_DEVICE_ID_CMEDIA_CM8338A, "CM8338A" }, + { PCI_DEVICE_ID_CMEDIA_CM8338B, "CM8338B" }, + { PCI_DEVICE_ID_CMEDIA_CM8738, "CM8738" }, + { PCI_DEVICE_ID_CMEDIA_CM8738B, "CM8738B" }, + }; + char *devicename = "unknown"; + { + if (pci_enable_device(pcidev)) + return; + if (pcidev->irq == 0) + return; + s = kmalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + printk(KERN_WARNING "cmpci: out of memory\n"); + return; + } + /* search device name */ + for (i = 0; i < sizeof(devicetable) / sizeof(devicetable[0]); i++) { + if (devicetable[i].deviceid == pcidev->device) + { + devicename = devicetable[i].devicename; + break; + } + } + memset(s, 0, sizeof(struct cm_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = CM_MAGIC; + s->iobase = pci_resource_start(pcidev, 0); + s->iosynth = fmio; + s->iomidi = mpuio; + s->status = 0; + /* range check */ + if (speakers < 2) + speakers = 2; + else if (speakers > 6) + speakers = 6; + s->speakers = speakers; + if (s->iobase == 0) + return; + s->irq = pcidev->irq; + + if (!request_region(s->iobase, CM_EXTENT_CODEC, "cmpci")) { + printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1); + goto err_region5; + } +#ifdef CONFIG_SOUND_CMPCI_MIDI + /* disable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0); + if (s->iomidi) { + if (!request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi")) { + printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1); + s->iomidi = 0; + } else { + /* set IO based at 0x330 */ + switch (s->iomidi) { + case 0x330: + reg_mask = 0; + break; + case 0x320: + reg_mask = 0x20; + break; + case 0x310: + reg_mask = 0x40; + break; + case 0x300: + reg_mask = 0x60; + break; + default: + s->iomidi = 0; + break; + } + outb((inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60) | reg_mask, s->iobase + CODEC_CMI_LEGACY_CTRL + 3); + /* enable MPU-401 */ + if (s->iomidi) { + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); + } + } + } +#endif +#ifdef CONFIG_SOUND_CMPCI_FM + /* disable FM */ + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0); + if (s->iosynth) { + if (!request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM")) { + printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1); + s->iosynth = 0; + } else { + /* set IO based at 0x388 */ + switch (s->iosynth) { + case 0x388: + reg_mask = 0; + break; + case 0x3C8: + reg_mask = 0x01; + break; + case 0x3E0: + reg_mask = 0x02; + break; + case 0x3E8: + reg_mask = 0x03; + break; + default: + s->iosynth = 0; + break; + } + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x03, reg_mask); + /* enable FM */ + if (s->iosynth) { + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 8); + } + } + } +#endif + /* enable joystick */ + if (joystick) + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x02); + else + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x02, 0); + /* initialize codec registers */ + outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ + outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ + /* reset mixer */ + wrmixer(s, DSP_MIX_DATARESETIDX, 0); + + /* request irq */ + if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) { + printk(KERN_ERR "cmpci: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n", + devicename, s->iobase, s->irq); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0) + goto err_dev2; +#ifdef CONFIG_SOUND_CMPCI_MIDI + if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0) + goto err_dev3; +#endif +#ifdef CONFIG_SOUND_CMPCI_FM + if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0) + goto err_dev4; +#endif + pci_set_master(pcidev); /* enable bus mastering */ + /* initialize the chips */ + fs = get_fs(); + set_fs(KERNEL_DS); + /* set mixer output */ + frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, 0x1f); + /* set mixer input */ + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD|SOUND_MASK_MIC; + 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); + } + /* use channel 0 for record, channel 1 for play */ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 1); + s->deviceid = pcidev->device; + + if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738) { + + /* chip version and hw capability check */ + s->chip_version = query_chip(s); + printk(KERN_INFO "cmpci: chip version = 0%d\n", s->chip_version); + + /* seet SPDIF-in inverse before enable SPDIF loop */ + if (spdif_inverse) { + /* turn on spdif-in inverse */ + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 1); + printk(KERN_INFO "cmpci: Inverse SPDIF-in\n"); + } else { + /* turn off spdif-ininverse */ + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~1, 0); + } + + /* enable SPDIF loop */ + if (spdif_loop) { + s->status |= DO_SPDIF_LOOP; + /* turn on spdif-in to spdif-out */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x80); + printk(KERN_INFO "cmpci: Enable SPDIF loop\n"); + } else { + s->status &= ~DO_SPDIF_LOOP; + /* turn off spdif-in to spdif-out */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x80, 0); + } + if (use_line_as_rear) { + s->capability |= CAN_LINE_AS_REAR; + s->status |= DO_LINE_AS_REAR; + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x20); + } else + maskb(s->iobase + CODEC_CMI_MIXER1, ~0x20, 0); + if (s->chip_version >= 39) { + if (use_line_as_bass) { + s->capability |= CAN_LINE_AS_BASS; + s->status |= DO_LINE_AS_BASS; + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0, 0x60); + } else + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x60, 0); + } + } else { + /* 8338 will fall here */ + s->max_channels = 2; + } + /* queue it for later freeing */ + s->next = devs; + devs = s; + return; + +#ifdef CONFIG_SOUND_CMPCI_FM + unregister_sound_special(s->dev_dmfm); + err_dev4: +#endif +#ifdef CONFIG_SOUND_CMPCI_MIDI + unregister_sound_midi(s->dev_midi); + err_dev3: +#endif + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "cmpci: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: +#ifdef CONFIG_SOUND_CMPCI_FM + if (s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH); +#endif +#ifdef CONFIG_SOUND_CMPCI_MIDI + if (s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI); +#endif + release_region(s->iobase, CM_EXTENT_CODEC); + err_region5: + kfree(s); + } + if (!devs) { + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); + return; + } + return; +} + +static int __init init_cmpci(void) +{ + struct pci_dev *pcidev = NULL; + int index = 0; + +#ifdef CONFIG_PCI + if (!pci_present()) /* No PCI bus in this machine! */ +#endif + return -ENODEV; + printk(KERN_INFO "cmpci: version $Revision: 5.64 $ time " __TIME__ " " __DATE__ "\n"); + + while (index < NR_DEVICE && ( + (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, pcidev)))) { + initialize_chip(pcidev); + index++; + } + while (index < NR_DEVICE && ( + (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)))) { + initialize_chip(pcidev); + index++; + } + while (index < NR_DEVICE && ( + (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, pcidev)))) { + initialize_chip(pcidev); + index++; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("ChenLi Tien, cltien@cmedia.com.tw"); +MODULE_DESCRIPTION("CM8x38 Audio Driver"); +MODULE_LICENSE("GPL"); + + +static void __exit cleanup_cmpci(void) +{ + struct cm_state *s; + + while ((s = devs)) { + devs = devs->next; + outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ + synchronize_irq(); + outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ + free_irq(s->irq, s); + + /* reset mixer */ + wrmixer(s, DSP_MIX_DATARESETIDX, 0); + + release_region(s->iobase, CM_EXTENT_CODEC); +#ifdef CONFIG_SOUND_CMPCI_MIDI + if (s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI); +#endif +#ifdef CONFIG_SOUND_CMPCI_FM + if (s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH); +#endif + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); +#ifdef CONFIG_SOUND_CMPCI_MIDI + unregister_sound_midi(s->dev_midi); +#endif +#ifdef CONFIG_SOUND_CMPCI_FM + unregister_sound_special(s->dev_dmfm); +#endif + kfree(s); + } + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); + printk(KERN_INFO "cmpci: unloading\n"); +} + +module_init(init_cmpci); +module_exit(cleanup_cmpci); diff -Nru a/sound/oss/coproc.h b/sound/oss/coproc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/coproc.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,12 @@ +/* + * Definitions for various on board processors on the sound cards. For + * example DSP processors. + */ + +/* + * Coprocessor access types + */ +#define COPR_CUSTOM 0x0001 /* Custom applications */ +#define COPR_MIDI 0x0002 /* MIDI (MPU-401) emulation */ +#define COPR_PCM 0x0004 /* Digitized voice applications */ +#define COPR_SYNTH 0x0008 /* Music synthesis */ diff -Nru a/sound/oss/cs4232.c b/sound/oss/cs4232.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs4232.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,505 @@ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * cs4232.c + * + * The low level driver for Crystal CS4232 based cards. The CS4232 is + * a PnP compatible chip which contains a CS4231A codec, SB emulation, + * a MPU401 compatible MIDI port, joystick and synthesizer and IDE CD-ROM + * interfaces. This is just a temporary driver until full PnP support + * gets implemented. Just the WSS codec, FM synth and the MIDI ports are + * supported. Other interfaces are left uninitialized. + * + * ifdef ...WAVEFRONT... + * + * Support is provided for initializing the WaveFront synth + * interface as well, which is logical device #4. Note that if + * you have a Tropez+ card, you probably don't need to setup + * the CS4232-supported MIDI interface, since it corresponds to + * the internal 26-pin header that's hard to access. Using this + * requires an additional IRQ, a resource none too plentiful in + * this environment. Just don't set module parameters mpuio and + * mpuirq, and the MIDI port will be left uninitialized. You can + * still use the ICS2115 hosted MIDI interface which corresponds + * to the 9-pin D connector on the back of the card. + * + * endif ...WAVEFRONT... + * + * Supported chips are: + * CS4232 + * CS4236 + * CS4236B + * + * Note: You will need a PnP config setup to initialise some CS4232 boards + * anyway. + * + * Changes + * Alan Cox Modularisation, Basic cleanups. + * Paul Barton-Davis Separated MPU configuration, added + * Tropez+ (WaveFront) support + * Christoph Hellwig Adapted to module_init/module_exit, + * simple cleanups + * Arnaldo C. de Melo got rid of attach_uart401 + * Bartlomiej Zolnierkiewicz + * Added some __init/__initdata/__exit + * Marcus Meissner Added ISA PnP support. + */ + +#include +#include +#include +#include + +#include "sound_config.h" + +#include "cs4232.h" +#include "ad1848.h" +#include "mpu401.h" + +#define KEY_PORT 0x279 /* Same as LPT1 status port */ +#define CSN_NUM 0x99 /* Just a random number */ + +static void CS_OUT(unsigned char a) +{ + outb(a, KEY_PORT); +} + +#define CS_OUT2(a, b) {CS_OUT(a);CS_OUT(b);} +#define CS_OUT3(a, b, c) {CS_OUT(a);CS_OUT(b);CS_OUT(c);} + +static int mpu_base = 0, mpu_irq = 0; +static int synth_base = 0, synth_irq = 0; +static int mpu_detected = 0; + +int __init probe_cs4232_mpu(struct address_info *hw_config) +{ + /* + * Just write down the config values. + */ + + mpu_base = hw_config->io_base; + mpu_irq = hw_config->irq; + + return 1; +} + +static unsigned char crystal_key[] __initdata = /* A 32 byte magic key sequence */ +{ + 0x96, 0x35, 0x9a, 0xcd, 0xe6, 0xf3, 0x79, 0xbc, + 0x5e, 0xaf, 0x57, 0x2b, 0x15, 0x8a, 0xc5, 0xe2, + 0xf1, 0xf8, 0x7c, 0x3e, 0x9f, 0x4f, 0x27, 0x13, + 0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a +}; + +static void sleep(unsigned howlong) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(howlong); +} + +int __init probe_cs4232(struct address_info *hw_config, int isapnp_configured) +{ + int i, n; + int base = hw_config->io_base, irq = hw_config->irq; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + + /* + * Verify that the I/O port range is free. + */ + + if (check_region(base, 4)) + { + printk(KERN_ERR "cs4232.c: I/O port 0x%03x not free\n", base); + return 0; + } + if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) { + return 1; /* The card is already active */ + } + if (isapnp_configured) { + printk(KERN_ERR "cs4232.c: ISA PnP configured, but not detected?\n"); + return 0; + } + + /* + * This version of the driver doesn't use the PnP method when configuring + * the card but a simplified method defined by Crystal. This means that + * just one CS4232 compatible device can exist on the system. Also this + * method conflicts with possible PnP support in the OS. For this reason + * driver is just a temporary kludge. + * + * Also the Cirrus/Crystal method doesnt always work. Try ISA PnP first ;) + */ + + /* + * Repeat initialization few times since it doesn't always succeed in + * first time. + */ + + for (n = 0; n < 4; n++) + { + /* + * Wake up the card by sending a 32 byte Crystal key to the key port. + */ + + for (i = 0; i < 32; i++) + CS_OUT(crystal_key[i]); + + sleep(HZ / 10); + + /* + * Now set the CSN (Card Select Number). + */ + + CS_OUT2(0x06, CSN_NUM); + + /* + * Then set some config bytes. First logical device 0 + */ + + CS_OUT2(0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */ + CS_OUT3(0x47, (base >> 8) & 0xff, base & 0xff); /* WSS base */ + + if (check_region(0x388, 4)) /* Not free */ + CS_OUT3(0x48, 0x00, 0x00) /* FM base off */ + else + CS_OUT3(0x48, 0x03, 0x88); /* FM base 0x388 */ + + CS_OUT3(0x42, 0x00, 0x00); /* SB base off */ + CS_OUT2(0x22, irq); /* SB+WSS IRQ */ + CS_OUT2(0x2a, dma1); /* SB+WSS DMA */ + + if (dma2 != -1) + CS_OUT2(0x25, dma2) /* WSS DMA2 */ + else + CS_OUT2(0x25, 4); /* No WSS DMA2 */ + + CS_OUT2(0x33, 0x01); /* Activate logical dev 0 */ + + sleep(HZ / 10); + + /* + * Initialize logical device 3 (MPU) + */ + + if (mpu_base != 0 && mpu_irq != 0) + { + CS_OUT2(0x15, 0x03); /* Select logical device 3 (MPU) */ + CS_OUT3(0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPU base */ + CS_OUT2(0x22, mpu_irq); /* MPU IRQ */ + CS_OUT2(0x33, 0x01); /* Activate logical dev 3 */ + } + + if(synth_base != 0) + { + CS_OUT2 (0x15, 0x04); /* logical device 4 (WaveFront) */ + CS_OUT3 (0x47, (synth_base >> 8) & 0xff, + synth_base & 0xff); /* base */ + CS_OUT2 (0x22, synth_irq); /* IRQ */ + CS_OUT2 (0x33, 0x01); /* Activate logical dev 4 */ + } + + /* + * Finally activate the chip + */ + + CS_OUT(0x79); + + sleep(HZ / 5); + + /* + * Then try to detect the codec part of the chip + */ + + if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) + return 1; + + sleep(HZ); + } + return 0; +} + +void __init attach_cs4232(struct address_info *hw_config) +{ + int base = hw_config->io_base, + irq = hw_config->irq, + dma1 = hw_config->dma, + dma2 = hw_config->dma2; + + if (base == -1 || irq == -1 || dma1 == -1) { + printk(KERN_ERR "cs4232: dma, irq and io must be set.\n"); + return; + } + + if (dma2 == -1) + dma2 = dma1; + + hw_config->slots[0] = ad1848_init("Crystal audio controller", base, + irq, + dma1, /* Playback DMA */ + dma2, /* Capture DMA */ + 0, + hw_config->osp, + THIS_MODULE); + + if (hw_config->slots[0] != -1 && + audio_devs[hw_config->slots[0]]->mixer_dev!=-1) + { + /* Assume the mixer map is as suggested in the CS4232 databook */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM synth */ + } + if (mpu_base != 0 && mpu_irq != 0) + { + static struct address_info hw_config2 = { + 0 + }; /* Ensure it's initialized */ + + hw_config2.io_base = mpu_base; + hw_config2.irq = mpu_irq; + hw_config2.dma = -1; + hw_config2.dma2 = -1; + hw_config2.always_detect = 0; + hw_config2.name = NULL; + hw_config2.driver_use_1 = 0; + hw_config2.driver_use_2 = 0; + hw_config2.card_subtype = 0; + + if (probe_uart401(&hw_config2, THIS_MODULE)) + { + mpu_detected = 1; + } + else + { + mpu_base = mpu_irq = 0; + } + hw_config->slots[1] = hw_config2.slots[1]; + } +} + +void __exit unload_cs4232(struct address_info *hw_config) +{ + int base = hw_config->io_base, irq = hw_config->irq; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = dma1; + + ad1848_unload(base, + irq, + dma1, /* Playback DMA */ + dma2, /* Capture DMA */ + 0); + + sound_unload_audiodev(hw_config->slots[0]); + if (mpu_base != 0 && mpu_irq != 0 && mpu_detected) + { + static struct address_info hw_config2 = + { + 0 + }; /* Ensure it's initialized */ + + hw_config2.io_base = mpu_base; + hw_config2.irq = mpu_irq; + hw_config2.dma = -1; + hw_config2.dma2 = -1; + hw_config2.always_detect = 0; + hw_config2.name = NULL; + hw_config2.driver_use_1 = 0; + hw_config2.driver_use_2 = 0; + hw_config2.card_subtype = 0; + hw_config2.slots[1] = hw_config->slots[1]; + + unload_uart401(&hw_config2); + } +} + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata mpuio = -1; +static int __initdata mpuirq = -1; +static int __initdata synthio = -1; +static int __initdata synthirq = -1; +static int __initdata isapnp = 1; + +MODULE_DESCRIPTION("CS4232 based soundcard driver"); +MODULE_AUTHOR("Hannu Savolainen, Paul Barton-Davis"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(io,"i"); +MODULE_PARM_DESC(io,"base I/O port for AD1848"); +MODULE_PARM(irq,"i"); +MODULE_PARM_DESC(irq,"IRQ for AD1848 chip"); +MODULE_PARM(dma,"i"); +MODULE_PARM_DESC(dma,"8 bit DMA for AD1848 chip"); +MODULE_PARM(dma2,"i"); +MODULE_PARM_DESC(dma2,"16 bit DMA for AD1848 chip"); +MODULE_PARM(mpuio,"i"); +MODULE_PARM_DESC(mpuio,"MPU 401 base address"); +MODULE_PARM(mpuirq,"i"); +MODULE_PARM_DESC(mpuirq,"MPU 401 IRQ"); +MODULE_PARM(synthio,"i"); +MODULE_PARM_DESC(synthio,"Maui WaveTable base I/O port"); +MODULE_PARM(synthirq,"i"); +MODULE_PARM_DESC(synthirq,"Maui WaveTable IRQ"); +MODULE_PARM(isapnp,"i"); +MODULE_PARM_DESC(isapnp,"Enable ISAPnP probing (default 1)"); + +/* + * Install a CS4232 based card. Need to have ad1848 and mpu401 + * loaded ready. + */ + +/* All cs4232 based cards have the main ad1848 card either as CSC0000 or + * CSC0100. */ +struct isapnp_device_id isapnp_cs4232_list[] __initdata = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), + 0 }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), + 0 }, + /* Guillemot Turtlebeach something appears to be cs4232 compatible + * (untested) */ + { ISAPNP_VENDOR('C','S','C'), ISAPNP_ANY_ID, + ISAPNP_VENDOR('G','I','M'), ISAPNP_FUNCTION(0x0100), + 0 }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, isapnp_cs4232_list); + +int cs4232_isapnp_probe(struct pci_dev *dev, const struct isapnp_device_id *id) +{ + int ret; + struct address_info *isapnpcfg; + + isapnpcfg=(struct address_info*)kmalloc(sizeof(*isapnpcfg),GFP_KERNEL); + if (!isapnpcfg) + return -ENOMEM; + /* + * If device is active, assume configured with /proc/isapnp + * and use anyway. Any other way to check this? + */ + ret = dev->prepare(dev); + if(ret && ret != -EBUSY) { + printk(KERN_ERR "cs4232: ISA PnP found device that could not be autoconfigured.\n"); + kfree(isapnpcfg); + return -ENODEV; + } + if(ret != -EBUSY) { + if(dev->activate(dev) < 0) { + printk(KERN_WARNING "cs4232: ISA PnP activate failed\n"); + kfree(isapnpcfg); + return -ENODEV; + } + } /* else subfunction is already activated */ + + isapnpcfg->irq = dev->irq_resource[0].start; + isapnpcfg->dma = dev->dma_resource[0].start; + isapnpcfg->dma2 = dev->dma_resource[1].start; + isapnpcfg->io_base = dev->resource[0].start; + if (probe_cs4232(isapnpcfg,TRUE) == 0) { + printk(KERN_ERR "cs4232: ISA PnP card found, but not detected?\n"); + return -ENODEV; + } + attach_cs4232(isapnpcfg); + pci_set_drvdata(dev,isapnpcfg); + return 0; +} + +static int __init init_cs4232(void) +{ +#ifdef CONFIG_SOUND_WAVEFRONT_MODULE + if(synthio == -1) + printk(KERN_INFO "cs4232: set synthio and synthirq to use the wavefront facilities.\n"); + else { + synth_base = synthio; + synth_irq = synthirq; + } +#else + if(synthio != -1) + printk(KERN_WARNING "cs4232: wavefront support not enabled in this driver.\n"); +#endif + cfg.irq = -1; + + if (isapnp && + (isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_probe) > 0) + ) + return 0; + + if(io==-1||irq==-1||dma==-1) + { + printk(KERN_ERR "cs4232: Must set io, irq and dma.\n"); + return -ENODEV; + } + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + cfg_mpu.io_base = -1; + cfg_mpu.irq = -1; + + if (mpuio != -1 && mpuirq != -1) { + cfg_mpu.io_base = mpuio; + cfg_mpu.irq = mpuirq; + probe_cs4232_mpu(&cfg_mpu); /* Bug always returns 0 not OK -- AC */ + } + + if (probe_cs4232(&cfg,FALSE) == 0) + return -ENODEV; + attach_cs4232(&cfg); + + return 0; +} + +int cs4232_isapnp_remove(struct pci_dev *dev, const struct isapnp_device_id *id) +{ + struct address_info *cfg = (struct address_info*)pci_get_drvdata(dev); + if (cfg) unload_cs4232(cfg); + pci_set_drvdata(dev,NULL); + dev->deactivate(dev); + return 0; +} + +static void __exit cleanup_cs4232(void) +{ + isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_remove); + if (cfg.irq != -1) + unload_cs4232(&cfg); /* Unloads global MPU as well, if needed */ +} + +module_init(init_cs4232); +module_exit(cleanup_cs4232); + +#ifndef MODULE +static int __init setup_cs4232(char *str) +{ + /* io, irq, dma, dma2 mpuio, mpuirq*/ + int ints[7]; + + /* If we have isapnp cards, no need for options */ + if (isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_probe) > 0) + return 1; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + mpuio = ints[5]; + mpuirq = ints[6]; + + return 1; +} + +__setup("cs4232=", setup_cs4232); +#endif diff -Nru a/sound/oss/cs4232.h b/sound/oss/cs4232.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs4232.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,11 @@ +/* + * cs4232.h + * + * Copyright: Christoph Hellwig + * + */ + +int probe_cs4232 (struct address_info *hw_config,int useisapnp); +void attach_cs4232 (struct address_info *hw_config); +int probe_cs4232_mpu (struct address_info *hw_config); +void attach_cs4232_mpu (struct address_info *hw_config); diff -Nru a/sound/oss/cs4281/Makefile b/sound/oss/cs4281/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs4281/Makefile Tue Feb 19 18:08:57 2002 @@ -0,0 +1,12 @@ +# Makefile for Cirrus Logic-Crystal CS4281 +# + +O_TARGET := cs4281.o + +obj-y := cs4281m.o +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s diff -Nru a/sound/oss/cs4281/cs4281_hwdefs.h b/sound/oss/cs4281/cs4281_hwdefs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs4281/cs4281_hwdefs.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,1234 @@ +//**************************************************************************** +// +// HWDEFS.H - Definitions of the registers and data structures used by the +// CS4281 +// +// Copyright (c) 1999,2000,2001 Crystal Semiconductor Corp. +// +//**************************************************************************** + +#ifndef _H_HWDEFS +#define _H_HWDEFS + +//**************************************************************************** +// +// The following define the offsets of the registers located in the PCI +// configuration space of the CS4281 part. +// +//**************************************************************************** +#define PCICONFIG_DEVID_VENID 0x00000000L +#define PCICONFIG_STATUS_COMMAND 0x00000004L +#define PCICONFIG_CLASS_REVISION 0x00000008L +#define PCICONFIG_LATENCY_TIMER 0x0000000CL +#define PCICONFIG_BA0 0x00000010L +#define PCICONFIG_BA1 0x00000014L +#define PCICONFIG_SUBSYSID_SUBSYSVENID 0x0000002CL +#define PCICONFIG_INTERRUPT 0x0000003CL + +//**************************************************************************** +// +// The following define the offsets of the registers accessed via base address +// register zero on the CS4281 part. +// +//**************************************************************************** +#define BA0_HISR 0x00000000L +#define BA0_HICR 0x00000008L +#define BA0_HIMR 0x0000000CL +#define BA0_IIER 0x00000010L +#define BA0_HDSR0 0x000000F0L +#define BA0_HDSR1 0x000000F4L +#define BA0_HDSR2 0x000000F8L +#define BA0_HDSR3 0x000000FCL +#define BA0_DCA0 0x00000110L +#define BA0_DCC0 0x00000114L +#define BA0_DBA0 0x00000118L +#define BA0_DBC0 0x0000011CL +#define BA0_DCA1 0x00000120L +#define BA0_DCC1 0x00000124L +#define BA0_DBA1 0x00000128L +#define BA0_DBC1 0x0000012CL +#define BA0_DCA2 0x00000130L +#define BA0_DCC2 0x00000134L +#define BA0_DBA2 0x00000138L +#define BA0_DBC2 0x0000013CL +#define BA0_DCA3 0x00000140L +#define BA0_DCC3 0x00000144L +#define BA0_DBA3 0x00000148L +#define BA0_DBC3 0x0000014CL +#define BA0_DMR0 0x00000150L +#define BA0_DCR0 0x00000154L +#define BA0_DMR1 0x00000158L +#define BA0_DCR1 0x0000015CL +#define BA0_DMR2 0x00000160L +#define BA0_DCR2 0x00000164L +#define BA0_DMR3 0x00000168L +#define BA0_DCR3 0x0000016CL +#define BA0_DLMR 0x00000170L +#define BA0_DLSR 0x00000174L +#define BA0_FCR0 0x00000180L +#define BA0_FCR1 0x00000184L +#define BA0_FCR2 0x00000188L +#define BA0_FCR3 0x0000018CL +#define BA0_FPDR0 0x00000190L +#define BA0_FPDR1 0x00000194L +#define BA0_FPDR2 0x00000198L +#define BA0_FPDR3 0x0000019CL +#define BA0_FCHS 0x0000020CL +#define BA0_FSIC0 0x00000210L +#define BA0_FSIC1 0x00000214L +#define BA0_FSIC2 0x00000218L +#define BA0_FSIC3 0x0000021CL +#define BA0_PCICFG00 0x00000300L +#define BA0_PCICFG04 0x00000304L +#define BA0_PCICFG08 0x00000308L +#define BA0_PCICFG0C 0x0000030CL +#define BA0_PCICFG10 0x00000310L +#define BA0_PCICFG14 0x00000314L +#define BA0_PCICFG18 0x00000318L +#define BA0_PCICFG1C 0x0000031CL +#define BA0_PCICFG20 0x00000320L +#define BA0_PCICFG24 0x00000324L +#define BA0_PCICFG28 0x00000328L +#define BA0_PCICFG2C 0x0000032CL +#define BA0_PCICFG30 0x00000330L +#define BA0_PCICFG34 0x00000334L +#define BA0_PCICFG38 0x00000338L +#define BA0_PCICFG3C 0x0000033CL +#define BA0_PCICFG40 0x00000340L +#define BA0_PMCS 0x00000344L +#define BA0_CWPR 0x000003E0L +#define BA0_EPPMC 0x000003E4L +#define BA0_GPIOR 0x000003E8L +#define BA0_SPMC 0x000003ECL +#define BA0_CFLR 0x000003F0L +#define BA0_IISR 0x000003F4L +#define BA0_TMS 0x000003F8L +#define BA0_SSVID 0x000003FCL +#define BA0_CLKCR1 0x00000400L +#define BA0_FRR 0x00000410L +#define BA0_SLT12O 0x0000041CL +#define BA0_SERMC 0x00000420L +#define BA0_SERC1 0x00000428L +#define BA0_SERC2 0x0000042CL +#define BA0_SLT12M 0x0000045CL +#define BA0_ACCTL 0x00000460L +#define BA0_ACSTS 0x00000464L +#define BA0_ACOSV 0x00000468L +#define BA0_ACCAD 0x0000046CL +#define BA0_ACCDA 0x00000470L +#define BA0_ACISV 0x00000474L +#define BA0_ACSAD 0x00000478L +#define BA0_ACSDA 0x0000047CL +#define BA0_JSPT 0x00000480L +#define BA0_JSCTL 0x00000484L +#define BA0_MIDCR 0x00000490L +#define BA0_MIDCMD 0x00000494L +#define BA0_MIDSR 0x00000494L +#define BA0_MIDWP 0x00000498L +#define BA0_MIDRP 0x0000049CL +#define BA0_AODSD1 0x000004A8L +#define BA0_AODSD2 0x000004ACL +#define BA0_CFGI 0x000004B0L +#define BA0_SLT12M2 0x000004DCL +#define BA0_ACSTS2 0x000004E4L +#define BA0_ACISV2 0x000004F4L +#define BA0_ACSAD2 0x000004F8L +#define BA0_ACSDA2 0x000004FCL +#define BA0_IOTGP 0x00000500L +#define BA0_IOTSB 0x00000504L +#define BA0_IOTFM 0x00000508L +#define BA0_IOTDMA 0x0000050CL +#define BA0_IOTAC0 0x00000500L +#define BA0_IOTAC1 0x00000504L +#define BA0_IOTAC2 0x00000508L +#define BA0_IOTAC3 0x0000050CL +#define BA0_IOTPCP 0x0000052CL +#define BA0_IOTCC 0x00000530L +#define BA0_IOTCR 0x0000058CL +#define BA0_PCPRR 0x00000600L +#define BA0_PCPGR 0x00000604L +#define BA0_PCPCR 0x00000608L +#define BA0_PCPCIEN 0x00000608L +#define BA0_SBMAR 0x00000700L +#define BA0_SBMDR 0x00000704L +#define BA0_SBRR 0x00000708L +#define BA0_SBRDP 0x0000070CL +#define BA0_SBWDP 0x00000710L +#define BA0_SBWBS 0x00000710L +#define BA0_SBRBS 0x00000714L +#define BA0_FMSR 0x00000730L +#define BA0_B0AP 0x00000730L +#define BA0_FMDP 0x00000734L +#define BA0_B1AP 0x00000738L +#define BA0_B1DP 0x0000073CL +#define BA0_SSPM 0x00000740L +#define BA0_DACSR 0x00000744L +#define BA0_ADCSR 0x00000748L +#define BA0_SSCR 0x0000074CL +#define BA0_FMLVC 0x00000754L +#define BA0_FMRVC 0x00000758L +#define BA0_SRCSA 0x0000075CL +#define BA0_PPLVC 0x00000760L +#define BA0_PPRVC 0x00000764L +#define BA0_PASR 0x00000768L +#define BA0_CASR 0x0000076CL + +//**************************************************************************** +// +// The following define the offsets of the AC97 shadow registers, which appear +// as a virtual extension to the base address register zero memory range. +// +//**************************************************************************** +#define AC97_REG_OFFSET_MASK 0x0000007EL +#define AC97_CODEC_NUMBER_MASK 0x00003000L + +#define BA0_AC97_RESET 0x00001000L +#define BA0_AC97_MASTER_VOLUME 0x00001002L +#define BA0_AC97_HEADPHONE_VOLUME 0x00001004L +#define BA0_AC97_MASTER_VOLUME_MONO 0x00001006L +#define BA0_AC97_MASTER_TONE 0x00001008L +#define BA0_AC97_PC_BEEP_VOLUME 0x0000100AL +#define BA0_AC97_PHONE_VOLUME 0x0000100CL +#define BA0_AC97_MIC_VOLUME 0x0000100EL +#define BA0_AC97_LINE_IN_VOLUME 0x00001010L +#define BA0_AC97_CD_VOLUME 0x00001012L +#define BA0_AC97_VIDEO_VOLUME 0x00001014L +#define BA0_AC97_AUX_VOLUME 0x00001016L +#define BA0_AC97_PCM_OUT_VOLUME 0x00001018L +#define BA0_AC97_RECORD_SELECT 0x0000101AL +#define BA0_AC97_RECORD_GAIN 0x0000101CL +#define BA0_AC97_RECORD_GAIN_MIC 0x0000101EL +#define BA0_AC97_GENERAL_PURPOSE 0x00001020L +#define BA0_AC97_3D_CONTROL 0x00001022L +#define BA0_AC97_MODEM_RATE 0x00001024L +#define BA0_AC97_POWERDOWN 0x00001026L +#define BA0_AC97_EXT_AUDIO_ID 0x00001028L +#define BA0_AC97_EXT_AUDIO_POWER 0x0000102AL +#define BA0_AC97_PCM_FRONT_DAC_RATE 0x0000102CL +#define BA0_AC97_PCM_SURR_DAC_RATE 0x0000102EL +#define BA0_AC97_PCM_LFE_DAC_RATE 0x00001030L +#define BA0_AC97_PCM_LR_ADC_RATE 0x00001032L +#define BA0_AC97_MIC_ADC_RATE 0x00001034L +#define BA0_AC97_6CH_VOL_C_LFE 0x00001036L +#define BA0_AC97_6CH_VOL_SURROUND 0x00001038L +#define BA0_AC97_RESERVED_3A 0x0000103AL +#define BA0_AC97_EXT_MODEM_ID 0x0000103CL +#define BA0_AC97_EXT_MODEM_POWER 0x0000103EL +#define BA0_AC97_LINE1_CODEC_RATE 0x00001040L +#define BA0_AC97_LINE2_CODEC_RATE 0x00001042L +#define BA0_AC97_HANDSET_CODEC_RATE 0x00001044L +#define BA0_AC97_LINE1_CODEC_LEVEL 0x00001046L +#define BA0_AC97_LINE2_CODEC_LEVEL 0x00001048L +#define BA0_AC97_HANDSET_CODEC_LEVEL 0x0000104AL +#define BA0_AC97_GPIO_PIN_CONFIG 0x0000104CL +#define BA0_AC97_GPIO_PIN_TYPE 0x0000104EL +#define BA0_AC97_GPIO_PIN_STICKY 0x00001050L +#define BA0_AC97_GPIO_PIN_WAKEUP 0x00001052L +#define BA0_AC97_GPIO_PIN_STATUS 0x00001054L +#define BA0_AC97_MISC_MODEM_AFE_STAT 0x00001056L +#define BA0_AC97_RESERVED_58 0x00001058L +#define BA0_AC97_CRYSTAL_REV_N_FAB_ID 0x0000105AL +#define BA0_AC97_TEST_AND_MISC_CTRL 0x0000105CL +#define BA0_AC97_AC_MODE 0x0000105EL +#define BA0_AC97_MISC_CRYSTAL_CONTROL 0x00001060L +#define BA0_AC97_LINE1_HYPRID_CTRL 0x00001062L +#define BA0_AC97_VENDOR_RESERVED_64 0x00001064L +#define BA0_AC97_VENDOR_RESERVED_66 0x00001066L +#define BA0_AC97_SPDIF_CONTROL 0x00001068L +#define BA0_AC97_VENDOR_RESERVED_6A 0x0000106AL +#define BA0_AC97_VENDOR_RESERVED_6C 0x0000106CL +#define BA0_AC97_VENDOR_RESERVED_6E 0x0000106EL +#define BA0_AC97_VENDOR_RESERVED_70 0x00001070L +#define BA0_AC97_VENDOR_RESERVED_72 0x00001072L +#define BA0_AC97_VENDOR_RESERVED_74 0x00001074L +#define BA0_AC97_CAL_ADDRESS 0x00001076L +#define BA0_AC97_CAL_DATA 0x00001078L +#define BA0_AC97_VENDOR_RESERVED_7A 0x0000107AL +#define BA0_AC97_VENDOR_ID1 0x0000107CL +#define BA0_AC97_VENDOR_ID2 0x0000107EL + +//**************************************************************************** +// +// The following define the offsets of the registers and memories accessed via +// base address register one on the CS4281 part. +// +//**************************************************************************** + +//**************************************************************************** +// +// The following defines are for the flags in the PCI device ID/vendor ID +// register. +// +//**************************************************************************** +#define PDV_VENID_MASK 0x0000FFFFL +#define PDV_DEVID_MASK 0xFFFF0000L +#define PDV_VENID_SHIFT 0L +#define PDV_DEVID_SHIFT 16L +#define VENID_CIRRUS_LOGIC 0x1013L +#define DEVID_CS4281 0x6005L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI status and command +// register. +// +//**************************************************************************** +#define PSC_IO_SPACE_ENABLE 0x00000001L +#define PSC_MEMORY_SPACE_ENABLE 0x00000002L +#define PSC_BUS_MASTER_ENABLE 0x00000004L +#define PSC_SPECIAL_CYCLES 0x00000008L +#define PSC_MWI_ENABLE 0x00000010L +#define PSC_VGA_PALETTE_SNOOP 0x00000020L +#define PSC_PARITY_RESPONSE 0x00000040L +#define PSC_WAIT_CONTROL 0x00000080L +#define PSC_SERR_ENABLE 0x00000100L +#define PSC_FAST_B2B_ENABLE 0x00000200L +#define PSC_UDF_MASK 0x007F0000L +#define PSC_FAST_B2B_CAPABLE 0x00800000L +#define PSC_PARITY_ERROR_DETECTED 0x01000000L +#define PSC_DEVSEL_TIMING_MASK 0x06000000L +#define PSC_TARGET_ABORT_SIGNALLED 0x08000000L +#define PSC_RECEIVED_TARGET_ABORT 0x10000000L +#define PSC_RECEIVED_MASTER_ABORT 0x20000000L +#define PSC_SIGNALLED_SERR 0x40000000L +#define PSC_DETECTED_PARITY_ERROR 0x80000000L +#define PSC_UDF_SHIFT 16L +#define PSC_DEVSEL_TIMING_SHIFT 25L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI class/revision ID +// register. +// +//**************************************************************************** +#define PCR_REVID_MASK 0x000000FFL +#define PCR_INTERFACE_MASK 0x0000FF00L +#define PCR_SUBCLASS_MASK 0x00FF0000L +#define PCR_CLASS_MASK 0xFF000000L +#define PCR_REVID_SHIFT 0L +#define PCR_INTERFACE_SHIFT 8L +#define PCR_SUBCLASS_SHIFT 16L +#define PCR_CLASS_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI latency timer register. +// +//**************************************************************************** +#define PLT_CACHE_LINE_SIZE_MASK 0x000000FFL +#define PLT_LATENCY_TIMER_MASK 0x0000FF00L +#define PLT_HEADER_TYPE_MASK 0x00FF0000L +#define PLT_BIST_MASK 0xFF000000L +#define PLT_CACHE_LINE_SIZE_SHIFT 0L +#define PLT_LATENCY_TIMER_SHIFT 8L +#define PLT_HEADER_TYPE_SHIFT 16L +#define PLT_BIST_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI base address registers. +// +//**************************************************************************** +#define PBAR_MEMORY_SPACE_INDICATOR 0x00000001L +#define PBAR_LOCATION_TYPE_MASK 0x00000006L +#define PBAR_NOT_PREFETCHABLE 0x00000008L +#define PBAR_ADDRESS_MASK 0xFFFFFFF0L +#define PBAR_LOCATION_TYPE_SHIFT 1L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI subsystem ID/subsystem +// vendor ID register. +// +//**************************************************************************** +#define PSS_SUBSYSTEM_VENDOR_ID_MASK 0x0000FFFFL +#define PSS_SUBSYSTEM_ID_MASK 0xFFFF0000L +#define PSS_SUBSYSTEM_VENDOR_ID_SHIFT 0L +#define PSS_SUBSYSTEM_ID_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI interrupt register. +// +//**************************************************************************** +#define PI_LINE_MASK 0x000000FFL +#define PI_PIN_MASK 0x0000FF00L +#define PI_MIN_GRANT_MASK 0x00FF0000L +#define PI_MAX_LATENCY_MASK 0xFF000000L +#define PI_LINE_SHIFT 0L +#define PI_PIN_SHIFT 8L +#define PI_MIN_GRANT_SHIFT 16L +#define PI_MAX_LATENCY_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the host interrupt status +// register. +// +//**************************************************************************** +#define HISR_HVOLMASK 0x00000003L +#define HISR_VDNI 0x00000001L +#define HISR_VUPI 0x00000002L +#define HISR_GP1I 0x00000004L +#define HISR_GP3I 0x00000008L +#define HISR_GPSI 0x00000010L +#define HISR_GPPI 0x00000020L +#define HISR_DMAI 0x00040000L +#define HISR_FIFOI 0x00100000L +#define HISR_HVOL 0x00200000L +#define HISR_MIDI 0x00400000L +#define HISR_SBINT 0x00800000L +#define HISR_INTENA 0x80000000L +#define HISR_DMA_MASK 0x00000F00L +#define HISR_FIFO_MASK 0x0000F000L +#define HISR_DMA_SHIFT 8L +#define HISR_FIFO_SHIFT 12L +#define HISR_FIFO0 0x00001000L +#define HISR_FIFO1 0x00002000L +#define HISR_FIFO2 0x00004000L +#define HISR_FIFO3 0x00008000L +#define HISR_DMA0 0x00000100L +#define HISR_DMA1 0x00000200L +#define HISR_DMA2 0x00000400L +#define HISR_DMA3 0x00000800L +#define HISR_RESERVED 0x40000000L + +//**************************************************************************** +// +// The following defines are for the flags in the host interrupt control +// register. +// +//**************************************************************************** +#define HICR_IEV 0x00000001L +#define HICR_CHGM 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the DMA Mode Register n +// (DMRn) +// +//**************************************************************************** +#define DMRn_TR_MASK 0x0000000CL +#define DMRn_TR_SHIFT 2L +#define DMRn_AUTO 0x00000010L +#define DMRn_TR_READ 0x00000008L +#define DMRn_TR_WRITE 0x00000004L +#define DMRn_TYPE_MASK 0x000000C0L +#define DMRn_TYPE_SHIFT 6L +#define DMRn_SIZE8 0x00010000L +#define DMRn_MONO 0x00020000L +#define DMRn_BEND 0x00040000L +#define DMRn_USIGN 0x00080000L +#define DMRn_SIZE20 0x00100000L +#define DMRn_SWAPC 0x00400000L +#define DMRn_CBC 0x01000000L +#define DMRn_TBC 0x02000000L +#define DMRn_POLL 0x10000000L +#define DMRn_DMA 0x20000000L +#define DMRn_FSEL_MASK 0xC0000000L +#define DMRn_FSEL_SHIFT 30L +#define DMRn_FSEL0 0x00000000L +#define DMRn_FSEL1 0x40000000L +#define DMRn_FSEL2 0x80000000L +#define DMRn_FSEL3 0xC0000000L + +//**************************************************************************** +// +// The following defines are for the flags in the DMA Command Register n +// (DCRn) +// +//**************************************************************************** +#define DCRn_HTCIE 0x00020000L +#define DCRn_TCIE 0x00010000L +#define DCRn_MSK 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the FIFO Control +// register n.(FCRn) +// +//**************************************************************************** +#define FCRn_OF_MASK 0x0000007FL +#define FCRn_OF_SHIFT 0L +#define FCRn_SZ_MASK 0x00007F00L +#define FCRn_SZ_SHIFT 8L +#define FCRn_LS_MASK 0x001F0000L +#define FCRn_LS_SHIFT 16L +#define FCRn_RS_MASK 0x1F000000L +#define FCRn_RS_SHIFT 24L +#define FCRn_FEN 0x80000000L +#define FCRn_PSH 0x20000000L +#define FCRn_DACZ 0x40000000L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port Power Management +// control register.(SPMC) +// +//**************************************************************************** +#define SPMC_RSTN 0x00000001L +#define SPMC_ASYN 0x00000002L +#define SPMC_WUP1 0x00000004L +#define SPMC_WUP2 0x00000008L +#define SPMC_ASDI2E 0x00000100L +#define SPMC_ESSPD 0x00000200L +#define SPMC_GISPEN 0x00004000L +#define SPMC_GIPPEN 0x00008000L + +//**************************************************************************** +// +// The following defines are for the flags in the Configuration Load register. +// (CFLR) +// +//**************************************************************************** +#define CFLR_CLOCK_SOURCE_MASK 0x00000003L +#define CFLR_CLOCK_SOURCE_AC97 0x00000001L + +#define CFLR_CB0_MASK 0x000000FFL +#define CFLR_CB1_MASK 0x0000FF00L +#define CFLR_CB2_MASK 0x00FF0000L +#define CFLR_CB3_MASK 0xFF000000L +#define CFLR_CB0_SHIFT 0L +#define CFLR_CB1_SHIFT 8L +#define CFLR_CB2_SHIFT 16L +#define CFLR_CB3_SHIFT 24L + +#define IOTCR_DMA0 0x00000000L +#define IOTCR_DMA1 0x00000400L +#define IOTCR_DMA2 0x00000800L +#define IOTCR_DMA3 0x00000C00L +#define IOTCR_CCLS 0x00000100L +#define IOTCR_PCPCI 0x00000200L +#define IOTCR_DDMA 0x00000300L + +#define SBWBS_WBB 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the SRC Slot Assignment Register +// (SRCSA) +// +//**************************************************************************** +#define SRCSA_PLSS_MASK 0x0000001FL +#define SRCSA_PLSS_SHIFT 0L +#define SRCSA_PRSS_MASK 0x00001F00L +#define SRCSA_PRSS_SHIFT 8L +#define SRCSA_CLSS_MASK 0x001F0000L +#define SRCSA_CLSS_SHIFT 16L +#define SRCSA_CRSS_MASK 0x1F000000L +#define SRCSA_CRSS_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound System Power Management +// register.(SSPM) +// +//**************************************************************************** +#define SSPM_FPDN 0x00000080L +#define SSPM_MIXEN 0x00000040L +#define SSPM_CSRCEN 0x00000020L +#define SSPM_PSRCEN 0x00000010L +#define SSPM_JSEN 0x00000008L +#define SSPM_ACLEN 0x00000004L +#define SSPM_FMEN 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound System Control +// Register. (SSCR) +// +//**************************************************************************** +#define SSCR_SB 0x00000004L +#define SSCR_HVC 0x00000008L +#define SSCR_LPFIFO 0x00000040L +#define SSCR_LPSRC 0x00000080L +#define SSCR_XLPSRC 0x00000100L +#define SSCR_MVMD 0x00010000L +#define SSCR_MVAD 0x00020000L +#define SSCR_MVLD 0x00040000L +#define SSCR_MVCS 0x00080000L + +//**************************************************************************** +// +// The following defines are for the flags in the Clock Control Register 1. +// (CLKCR1) +// +//**************************************************************************** +#define CLKCR1_DLLSS_MASK 0x0000000CL +#define CLKCR1_DLLSS_SHIFT 2L +#define CLKCR1_DLLP 0x00000010L +#define CLKCR1_SWCE 0x00000020L +#define CLKCR1_DLLOS 0x00000040L +#define CLKCR1_CKRA 0x00010000L +#define CLKCR1_CKRN 0x00020000L +#define CLKCR1_DLLRDY 0x01000000L +#define CLKCR1_CLKON 0x02000000L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound Blaster Read Buffer +// Status.(SBRBS) +// +//**************************************************************************** +#define SBRBS_RD_MASK 0x0000007FL +#define SBRBS_RD_SHIFT 0L +#define SBRBS_RBF 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port master control +// register.(SERMC) +// +//**************************************************************************** +#define SERMC_MSPE 0x00000001L +#define SERMC_PTC_MASK 0x0000000EL +#define SERMC_PTC_SHIFT 1L +#define SERMC_PTC_AC97 0x00000002L +#define SERMC_PLB 0x00000010L +#define SERMC_PXLB 0x00000020L +#define SERMC_LOFV 0x00080000L +#define SERMC_SLB 0x00100000L +#define SERMC_SXLB 0x00200000L +#define SERMC_ODSEN1 0x01000000L +#define SERMC_ODSEN2 0x02000000L + +//**************************************************************************** +// +// The following defines are for the flags in the General Purpose I/O Register. +// (GPIOR) +// +//**************************************************************************** +#define GPIOR_VDNS 0x00000001L +#define GPIOR_VUPS 0x00000002L +#define GPIOR_GP1S 0x00000004L +#define GPIOR_GP3S 0x00000008L +#define GPIOR_GPSS 0x00000010L +#define GPIOR_GPPS 0x00000020L +#define GPIOR_GP1D 0x00000400L +#define GPIOR_GP3D 0x00000800L +#define GPIOR_VDNLT 0x00010000L +#define GPIOR_VDNPO 0x00020000L +#define GPIOR_VDNST 0x00040000L +#define GPIOR_VDNW 0x00080000L +#define GPIOR_VUPLT 0x00100000L +#define GPIOR_VUPPO 0x00200000L +#define GPIOR_VUPST 0x00400000L +#define GPIOR_VUPW 0x00800000L +#define GPIOR_GP1OE 0x01000000L +#define GPIOR_GP1PT 0x02000000L +#define GPIOR_GP1ST 0x04000000L +#define GPIOR_GP1W 0x08000000L +#define GPIOR_GP3OE 0x10000000L +#define GPIOR_GP3PT 0x20000000L +#define GPIOR_GP3ST 0x40000000L +#define GPIOR_GP3W 0x80000000L + +//**************************************************************************** +// +// The following defines are for the flags in the clock control register 1. +// +//**************************************************************************** +#define CLKCR1_PLLSS_MASK 0x0000000CL +#define CLKCR1_PLLSS_SERIAL 0x00000000L +#define CLKCR1_PLLSS_CRYSTAL 0x00000004L +#define CLKCR1_PLLSS_PCI 0x00000008L +#define CLKCR1_PLLSS_RESERVED 0x0000000CL +#define CLKCR1_PLLP 0x00000010L +#define CLKCR1_SWCE 0x00000020L +#define CLKCR1_PLLOS 0x00000040L + +//**************************************************************************** +// +// The following defines are for the flags in the feature reporting register. +// +//**************************************************************************** +#define FRR_FAB_MASK 0x00000003L +#define FRR_MASK_MASK 0x0000001CL +#define FRR_ID_MASK 0x00003000L +#define FRR_FAB_SHIFT 0L +#define FRR_MASK_SHIFT 2L +#define FRR_ID_SHIFT 12L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port 1 configuration +// register. +// +//**************************************************************************** +#define SERC1_VALUE 0x00000003L +#define SERC1_SO1EN 0x00000001L +#define SERC1_SO1F_MASK 0x0000000EL +#define SERC1_SO1F_CS423X 0x00000000L +#define SERC1_SO1F_AC97 0x00000002L +#define SERC1_SO1F_DAC 0x00000004L +#define SERC1_SO1F_SPDIF 0x00000006L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port 2 configuration +// register. +// +//**************************************************************************** +#define SERC2_VALUE 0x00000003L +#define SERC2_SI1EN 0x00000001L +#define SERC2_SI1F_MASK 0x0000000EL +#define SERC2_SI1F_CS423X 0x00000000L +#define SERC2_SI1F_AC97 0x00000002L +#define SERC2_SI1F_ADC 0x00000004L +#define SERC2_SI1F_SPDIF 0x00000006L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 control register. +// +//**************************************************************************** +#define ACCTL_ESYN 0x00000002L +#define ACCTL_VFRM 0x00000004L +#define ACCTL_DCV 0x00000008L +#define ACCTL_CRW 0x00000010L +#define ACCTL_TC 0x00000040L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status register. +// +//**************************************************************************** +#define ACSTS_CRDY 0x00000001L +#define ACSTS_VSTS 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 output slot valid +// register. +// +//**************************************************************************** +#define ACOSV_SLV3 0x00000001L +#define ACOSV_SLV4 0x00000002L +#define ACOSV_SLV5 0x00000004L +#define ACOSV_SLV6 0x00000008L +#define ACOSV_SLV7 0x00000010L +#define ACOSV_SLV8 0x00000020L +#define ACOSV_SLV9 0x00000040L +#define ACOSV_SLV10 0x00000080L +#define ACOSV_SLV11 0x00000100L +#define ACOSV_SLV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 command address +// register. +// +//**************************************************************************** +#define ACCAD_CI_MASK 0x0000007FL +#define ACCAD_CI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 command data register. +// +//**************************************************************************** +#define ACCDA_CD_MASK 0x0000FFFFL +#define ACCDA_CD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 input slot valid +// register. +// +//**************************************************************************** +#define ACISV_ISV3 0x00000001L +#define ACISV_ISV4 0x00000002L +#define ACISV_ISV5 0x00000004L +#define ACISV_ISV6 0x00000008L +#define ACISV_ISV7 0x00000010L +#define ACISV_ISV8 0x00000020L +#define ACISV_ISV9 0x00000040L +#define ACISV_ISV10 0x00000080L +#define ACISV_ISV11 0x00000100L +#define ACISV_ISV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status address +// register. +// +//**************************************************************************** +#define ACSAD_SI_MASK 0x0000007FL +#define ACSAD_SI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status data register. +// +//**************************************************************************** +#define ACSDA_SD_MASK 0x0000FFFFL +#define ACSDA_SD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers (all 12). +// +//**************************************************************************** +#define IOTAC_SA_MASK 0x0000FFFFL +#define IOTAC_MSK_MASK 0x000F0000L +#define IOTAC_IODC_MASK 0x06000000L +#define IOTAC_IODC_16_BIT 0x00000000L +#define IOTAC_IODC_10_BIT 0x02000000L +#define IOTAC_IODC_12_BIT 0x04000000L +#define IOTAC_WSPI 0x08000000L +#define IOTAC_RSPI 0x10000000L +#define IOTAC_WSE 0x20000000L +#define IOTAC_WE 0x40000000L +#define IOTAC_RE 0x80000000L +#define IOTAC_SA_SHIFT 0L +#define IOTAC_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI master enable +// register. +// +//**************************************************************************** +#define PCPCIEN_EN 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the joystick poll/trigger +// register. +// +//**************************************************************************** +#define JSPT_CAX 0x00000001L +#define JSPT_CAY 0x00000002L +#define JSPT_CBX 0x00000004L +#define JSPT_CBY 0x00000008L +#define JSPT_BA1 0x00000010L +#define JSPT_BA2 0x00000020L +#define JSPT_BB1 0x00000040L +#define JSPT_BB2 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the joystick control register. +// The TBF bit has been moved from MIDSR register to JSCTL register bit 8. +// +//**************************************************************************** +#define JSCTL_SP_MASK 0x00000003L +#define JSCTL_SP_SLOW 0x00000000L +#define JSCTL_SP_MEDIUM_SLOW 0x00000001L +#define JSCTL_SP_MEDIUM_FAST 0x00000002L +#define JSCTL_SP_FAST 0x00000003L +#define JSCTL_ARE 0x00000004L +#define JSCTL_TBF 0x00000100L + + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI control register. +// +//**************************************************************************** +#define MIDCR_TXE 0x00000001L +#define MIDCR_RXE 0x00000002L +#define MIDCR_RIE 0x00000004L +#define MIDCR_TIE 0x00000008L +#define MIDCR_MLB 0x00000010L +#define MIDCR_MRST 0x00000020L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI status register. +// +//**************************************************************************** +#define MIDSR_RBE 0x00000080L +#define MIDSR_RDA 0x00008000L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI write port register. +// +//**************************************************************************** +#define MIDWP_MWD_MASK 0x000000FFL +#define MIDWP_MWD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI read port register. +// +//**************************************************************************** +#define MIDRP_MRD_MASK 0x000000FFL +#define MIDRP_MRD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the configuration interface +// register. +// +//**************************************************************************** +#define CFGI_CLK 0x00000001L +#define CFGI_DOUT 0x00000002L +#define CFGI_DIN_EEN 0x00000004L +#define CFGI_EELD 0x00000008L + +//**************************************************************************** +// +// The following defines are for the flags in the subsystem ID and vendor ID +// register. +// +//**************************************************************************** +#define SSVID_VID_MASK 0x0000FFFFL +#define SSVID_SID_MASK 0xFFFF0000L +#define SSVID_VID_SHIFT 0L +#define SSVID_SID_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the GPIO pin interface register. +// +//**************************************************************************** +#define GPIOR_VOLDN 0x00000001L +#define GPIOR_VOLUP 0x00000002L +#define GPIOR_SI2D 0x00000004L +#define GPIOR_SI2OE 0x00000008L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status register 2. +// +//**************************************************************************** +#define ACSTS2_CRDY 0x00000001L +#define ACSTS2_VSTS 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 input slot valid +// register 2. +// +//**************************************************************************** +#define ACISV2_ISV3 0x00000001L +#define ACISV2_ISV4 0x00000002L +#define ACISV2_ISV5 0x00000004L +#define ACISV2_ISV6 0x00000008L +#define ACISV2_ISV7 0x00000010L +#define ACISV2_ISV8 0x00000020L +#define ACISV2_ISV9 0x00000040L +#define ACISV2_ISV10 0x00000080L +#define ACISV2_ISV11 0x00000100L +#define ACISV2_ISV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status address +// register 2. +// +//**************************************************************************** +#define ACSAD2_SI_MASK 0x0000007FL +#define ACSAD2_SI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status data register 2. +// +//**************************************************************************** +#define ACSDA2_SD_MASK 0x0000FFFFL +#define ACSDA2_SD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap control register. +// +//**************************************************************************** +#define IOTCR_ITD 0x00000001L +#define IOTCR_HRV 0x00000002L +#define IOTCR_SRV 0x00000004L +#define IOTCR_DTI 0x00000008L +#define IOTCR_DFI 0x00000010L +#define IOTCR_DDP 0x00000020L +#define IOTCR_JTE 0x00000040L +#define IOTCR_PPE 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for Hardware Master Volume. +// +//**************************************************************************** +#define IOTGP_SA_MASK 0x0000FFFFL +#define IOTGP_MSK_MASK 0x000F0000L +#define IOTGP_IODC_MASK 0x06000000L +#define IOTGP_IODC_16_BIT 0x00000000L +#define IOTGP_IODC_10_BIT 0x02000000L +#define IOTGP_IODC_12_BIT 0x04000000L +#define IOTGP_WSPI 0x08000000L +#define IOTGP_RSPI 0x10000000L +#define IOTGP_WSE 0x20000000L +#define IOTGP_WE 0x40000000L +#define IOTGP_RE 0x80000000L +#define IOTGP_SA_SHIFT 0L +#define IOTGP_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for Sound Blaster +// +//**************************************************************************** +#define IOTSB_SA_MASK 0x0000FFFFL +#define IOTSB_MSK_MASK 0x000F0000L +#define IOTSB_IODC_MASK 0x06000000L +#define IOTSB_IODC_16_BIT 0x00000000L +#define IOTSB_IODC_10_BIT 0x02000000L +#define IOTSB_IODC_12_BIT 0x04000000L +#define IOTSB_WSPI 0x08000000L +#define IOTSB_RSPI 0x10000000L +#define IOTSB_WSE 0x20000000L +#define IOTSB_WE 0x40000000L +#define IOTSB_RE 0x80000000L +#define IOTSB_SA_SHIFT 0L +#define IOTSB_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for FM. +// +//**************************************************************************** +#define IOTFM_SA_MASK 0x0000FFFFL +#define IOTFM_MSK_MASK 0x000F0000L +#define IOTFM_IODC_MASK 0x06000000L +#define IOTFM_IODC_16_BIT 0x00000000L +#define IOTFM_IODC_10_BIT 0x02000000L +#define IOTFM_IODC_12_BIT 0x04000000L +#define IOTFM_WSPI 0x08000000L +#define IOTFM_RSPI 0x10000000L +#define IOTFM_WSE 0x20000000L +#define IOTFM_WE 0x40000000L +#define IOTFM_RE 0x80000000L +#define IOTFM_SA_SHIFT 0L +#define IOTFM_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI request register. +// +//**************************************************************************** +#define PCPRR_RDC_MASK 0x00000007L +#define PCPRR_REQ 0x00008000L +#define PCPRR_RDC_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI grant register. +// +//**************************************************************************** +#define PCPGR_GDC_MASK 0x00000007L +#define PCPGR_VL 0x00008000L +#define PCPGR_GDC_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI Control Register. +// +//**************************************************************************** +#define PCPCR_EN 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the debug index register. +// +//**************************************************************************** +#define DREG_REGID_MASK 0x0000007FL +#define DREG_DEBUG 0x00000080L +#define DREG_RGBK_MASK 0x00000700L +#define DREG_TRAP 0x00000800L +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000L +#endif +#endif +#define DREG_REGID_SHIFT 0L +#define DREG_RGBK_SHIFT 8L +#define DREG_RGBK_REGID_MASK 0x0000077FL +#define DREG_REGID_R0 0x00000010L +#define DREG_REGID_R1 0x00000011L +#define DREG_REGID_R2 0x00000012L +#define DREG_REGID_R3 0x00000013L +#define DREG_REGID_R4 0x00000014L +#define DREG_REGID_R5 0x00000015L +#define DREG_REGID_R6 0x00000016L +#define DREG_REGID_R7 0x00000017L +#define DREG_REGID_R8 0x00000018L +#define DREG_REGID_R9 0x00000019L +#define DREG_REGID_RA 0x0000001AL +#define DREG_REGID_RB 0x0000001BL +#define DREG_REGID_RC 0x0000001CL +#define DREG_REGID_RD 0x0000001DL +#define DREG_REGID_RE 0x0000001EL +#define DREG_REGID_RF 0x0000001FL +#define DREG_REGID_RA_BUS_LOW 0x00000020L +#define DREG_REGID_RA_BUS_HIGH 0x00000038L +#define DREG_REGID_YBUS_LOW 0x00000050L +#define DREG_REGID_YBUS_HIGH 0x00000058L +#define DREG_REGID_TRAP_0 0x00000100L +#define DREG_REGID_TRAP_1 0x00000101L +#define DREG_REGID_TRAP_2 0x00000102L +#define DREG_REGID_TRAP_3 0x00000103L +#define DREG_REGID_TRAP_4 0x00000104L +#define DREG_REGID_TRAP_5 0x00000105L +#define DREG_REGID_TRAP_6 0x00000106L +#define DREG_REGID_TRAP_7 0x00000107L +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010EL +#define DREG_REGID_TOP_OF_STACK 0x0000010FL +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110L +#define DREG_REGID_TRAP_9 0x00000111L +#define DREG_REGID_TRAP_10 0x00000112L +#define DREG_REGID_TRAP_11 0x00000113L +#define DREG_REGID_TRAP_12 0x00000114L +#define DREG_REGID_TRAP_13 0x00000115L +#define DREG_REGID_TRAP_14 0x00000116L +#define DREG_REGID_TRAP_15 0x00000117L +#define DREG_REGID_TRAP_16 0x00000118L +#define DREG_REGID_TRAP_17 0x00000119L +#define DREG_REGID_TRAP_18 0x0000011AL +#define DREG_REGID_TRAP_19 0x0000011BL +#define DREG_REGID_TRAP_20 0x0000011CL +#define DREG_REGID_TRAP_21 0x0000011DL +#define DREG_REGID_TRAP_22 0x0000011EL +#define DREG_REGID_TRAP_23 0x0000011FL +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200L +#define DREG_REGID_RSA0_HIGH 0x00000201L +#define DREG_REGID_RSA1_LOW 0x00000202L +#define DREG_REGID_RSA1_HIGH 0x00000203L +#define DREG_REGID_RSA2 0x00000204L +#define DREG_REGID_RSA3 0x00000205L +#define DREG_REGID_RSI0_LOW 0x00000206L +#define DREG_REGID_RSI0_HIGH 0x00000207L +#define DREG_REGID_RSI1 0x00000208L +#define DREG_REGID_RSI2 0x00000209L +#define DREG_REGID_SAGUSTATUS 0x0000020AL +#define DREG_REGID_RSCONFIG01_LOW 0x0000020BL +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020CL +#define DREG_REGID_RSCONFIG23_LOW 0x0000020DL +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020EL +#define DREG_REGID_RSDMA01E 0x0000020FL +#define DREG_REGID_RSDMA23E 0x00000210L +#define DREG_REGID_RSD0_LOW 0x00000211L +#define DREG_REGID_RSD0_HIGH 0x00000212L +#define DREG_REGID_RSD1_LOW 0x00000213L +#define DREG_REGID_RSD1_HIGH 0x00000214L +#define DREG_REGID_RSD2_LOW 0x00000215L +#define DREG_REGID_RSD2_HIGH 0x00000216L +#define DREG_REGID_RSD3_LOW 0x00000217L +#define DREG_REGID_RSD3_HIGH 0x00000218L +#define DREG_REGID_SRAR_HIGH 0x0000021AL +#define DREG_REGID_SRAR_LOW 0x0000021BL +#define DREG_REGID_DMA_STATE 0x0000021CL +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021DL +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021EL +#define DREG_REGID_CPU_STATUS 0x00000300L +#define DREG_REGID_MAC_MODE 0x00000301L +#define DREG_REGID_STACK_AND_REPEAT 0x00000302L +#define DREG_REGID_INDEX0 0x00000304L +#define DREG_REGID_INDEX1 0x00000305L +#define DREG_REGID_DMA_STATE_0_3 0x00000400L +#define DREG_REGID_DMA_STATE_4_7 0x00000404L +#define DREG_REGID_DMA_STATE_8_11 0x00000408L +#define DREG_REGID_DMA_STATE_12_15 0x0000040CL +#define DREG_REGID_DMA_STATE_16_19 0x00000410L +#define DREG_REGID_DMA_STATE_20_23 0x00000414L +#define DREG_REGID_DMA_STATE_24_27 0x00000418L +#define DREG_REGID_DMA_STATE_28_31 0x0000041CL +#define DREG_REGID_DMA_STATE_32_35 0x00000420L +#define DREG_REGID_DMA_STATE_36_39 0x00000424L +#define DREG_REGID_DMA_STATE_40_43 0x00000428L +#define DREG_REGID_DMA_STATE_44_47 0x0000042CL +#define DREG_REGID_DMA_STATE_48_51 0x00000430L +#define DREG_REGID_DMA_STATE_52_55 0x00000434L +#define DREG_REGID_DMA_STATE_56_59 0x00000438L +#define DREG_REGID_DMA_STATE_60_63 0x0000043CL +#define DREG_REGID_DMA_STATE_64_67 0x00000440L +#define DREG_REGID_DMA_STATE_68_71 0x00000444L +#define DREG_REGID_DMA_STATE_72_75 0x00000448L +#define DREG_REGID_DMA_STATE_76_79 0x0000044CL +#define DREG_REGID_DMA_STATE_80_83 0x00000450L +#define DREG_REGID_DMA_STATE_84_87 0x00000454L +#define DREG_REGID_DMA_STATE_88_91 0x00000458L +#define DREG_REGID_DMA_STATE_92_95 0x0000045CL +#define DREG_REGID_TRAP_SELECT 0x00000500L +#define DREG_REGID_TRAP_WRITE_0 0x00000500L +#define DREG_REGID_TRAP_WRITE_1 0x00000501L +#define DREG_REGID_TRAP_WRITE_2 0x00000502L +#define DREG_REGID_TRAP_WRITE_3 0x00000503L +#define DREG_REGID_TRAP_WRITE_4 0x00000504L +#define DREG_REGID_TRAP_WRITE_5 0x00000505L +#define DREG_REGID_TRAP_WRITE_6 0x00000506L +#define DREG_REGID_TRAP_WRITE_7 0x00000507L +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510L +#define DREG_REGID_TRAP_WRITE_9 0x00000511L +#define DREG_REGID_TRAP_WRITE_10 0x00000512L +#define DREG_REGID_TRAP_WRITE_11 0x00000513L +#define DREG_REGID_TRAP_WRITE_12 0x00000514L +#define DREG_REGID_TRAP_WRITE_13 0x00000515L +#define DREG_REGID_TRAP_WRITE_14 0x00000516L +#define DREG_REGID_TRAP_WRITE_15 0x00000517L +#define DREG_REGID_TRAP_WRITE_16 0x00000518L +#define DREG_REGID_TRAP_WRITE_17 0x00000519L +#define DREG_REGID_TRAP_WRITE_18 0x0000051AL +#define DREG_REGID_TRAP_WRITE_19 0x0000051BL +#define DREG_REGID_TRAP_WRITE_20 0x0000051CL +#define DREG_REGID_TRAP_WRITE_21 0x0000051DL +#define DREG_REGID_TRAP_WRITE_22 0x0000051EL +#define DREG_REGID_TRAP_WRITE_23 0x0000051FL +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600L +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601L +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602L +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603L +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604L +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605L +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606L +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607L +#define DREG_REGID_MAC0_ACC0_MID 0x00000608L +#define DREG_REGID_MAC0_ACC1_MID 0x00000609L +#define DREG_REGID_MAC0_ACC2_MID 0x0000060AL +#define DREG_REGID_MAC0_ACC3_MID 0x0000060BL +#define DREG_REGID_MAC1_ACC0_MID 0x0000060CL +#define DREG_REGID_MAC1_ACC1_MID 0x0000060DL +#define DREG_REGID_MAC1_ACC2_MID 0x0000060EL +#define DREG_REGID_MAC1_ACC3_MID 0x0000060FL +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610L +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611L +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612L +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613L +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614L +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615L +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616L +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617L +#define DREG_REGID_RSHOUT_LOW 0x00000620L +#define DREG_REGID_RSHOUT_MID 0x00000628L +#define DREG_REGID_RSHOUT_HIGH 0x00000630L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 S/PDIF Control register. +// +//**************************************************************************** +#define SPDIF_CONTROL_SPDIF_EN 0x00008000L +#define SPDIF_CONTROL_VAL 0x00004000L +#define SPDIF_CONTROL_COPY 0x00000004L +#define SPDIF_CONTROL_CC0 0x00000010L +#define SPDIF_CONTROL_CC1 0x00000020L +#define SPDIF_CONTROL_CC2 0x00000040L +#define SPDIF_CONTROL_CC3 0x00000080L +#define SPDIF_CONTROL_CC4 0x00000100L +#define SPDIF_CONTROL_CC5 0x00000200L +#define SPDIF_CONTROL_CC6 0x00000400L +#define SPDIF_CONTROL_L 0x00000800L + +#endif // _H_HWDEFS diff -Nru a/sound/oss/cs4281/cs4281_wrapper-24.c b/sound/oss/cs4281/cs4281_wrapper-24.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs4281/cs4281_wrapper-24.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,42 @@ +/******************************************************************************* +* +* "cs4281_wrapper.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.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. +* +* 12/20/00 trw - new file. +* +*******************************************************************************/ + +#include + +void cs4281_null(struct pci_dev *pcidev) { return; } +#define cs4x_mem_map_reserve(page) mem_map_reserve(page) +#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page) + +#define free_dmabuf(state, dmabuf) \ + pci_free_consistent(state->pcidev, \ + PAGE_SIZE << (dmabuf)->buforder, \ + (dmabuf)->rawbuf, (dmabuf)->dmaaddr); +#define free_dmabuf2(state, dmabuf) \ + pci_free_consistent((state)->pcidev, \ + PAGE_SIZE << (state)->buforder_tmpbuff, \ + (state)->tmpbuff, (state)->dmaaddr_tmpbuff); +#define cs4x_pgoff(vma) ((vma)->vm_pgoff) + diff -Nru a/sound/oss/cs4281/cs4281m.c b/sound/oss/cs4281/cs4281m.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs4281/cs4281m.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,4528 @@ +/******************************************************************************* +* +* "cs4281.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* 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). +* +* 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 cs4281_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. +* +*******************************************************************************/ + +/* uncomment the following line to disable building PM support into the driver */ +//#define NOT_CS4281_PM 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include "cs_dm.h" +#include "cs4281_hwdefs.h" +#include "cs4281pm.h" + +struct cs4281_state; +EXPORT_NO_SYMBOLS; + +static void stop_dac(struct cs4281_state *s); +static void stop_adc(struct cs4281_state *s); +static void start_dac(struct cs4281_state *s); +static void start_adc(struct cs4281_state *s); +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +// --------------------------------------------------------------------- + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CRYSTAL_CS4281 +#define PCI_DEVICE_ID_CRYSTAL_CS4281 0x6005 +#endif + +#define CS4281_MAGIC ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS) +#define CS4281_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */ + +// 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 1 +#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_MIDI_WRITE 0x00000040 // write information for midi +#define CS_MIDI_READ 0x00000080 // read information for midi +#define CS_MPU401_WRITE 0x00000100 // write information for mpu401 +#define CS_MPU401_READ 0x00000200 // read information for mpu401 +#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_PM 0x00004000 // power management +#define CS_TMP 0x10000000 // tmp debug mask bit + +#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend +#define CS_IOCTL_CMD_RESUME 0x2 // resume +// +// 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 = 1; // levels range from 1-9 +static unsigned long cs_debugmask = CS_INIT | CS_ERROR; // use CS_DBGOUT with various mask values +MODULE_PARM(cs_debuglevel, "i"); +MODULE_PARM(cs_debugmask, "i"); +#endif +#define CS_TRUE 1 +#define CS_FALSE 0 + +// MIDI buffer sizes +#define MIDIINBUF 500 +#define MIDIOUTBUF 500 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define CS4281_MAJOR_VERSION 1 +#define CS4281_MINOR_VERSION 13 +#ifdef __ia64__ +#define CS4281_ARCH 64 //architecture key +#else +#define CS4281_ARCH 32 //architecture key +#endif + +#define CS_TYPE_ADC 0 +#define CS_TYPE_DAC 1 + + +static const char invalid_magic[] = + KERN_CRIT "cs4281: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != CS4281_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +//LIST_HEAD(cs4281_devs); +struct list_head cs4281_devs = { &cs4281_devs, &cs4281_devs }; + +struct cs4281_state; + +#include "cs4281_wrapper-24.c" + +struct cs4281_state { + // magic + unsigned int magic; + + // we keep the cards in a linked list + struct cs4281_state *next; + + // pcidev is needed to turn off the DDMA controller at driver shutdown + struct pci_dev *pcidev; + struct list_head list; + + // soundcore stuff + int dev_audio; + int dev_mixer; + int dev_midi; + + // hardware resources + unsigned int pBA0phys, pBA1phys; + char *pBA0, *pBA1; + unsigned int irq; + + // 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; + unsigned char clkdiv; + } prop_dac, prop_adc; + unsigned conversion:1; // conversion from 16 to 8 bit in progress + void *tmpbuff; // tmp buffer for sample conversions + 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_tmpbuff; + unsigned buforder_tmpbuff; // Log base 2 of 'rawbuf' size in bytes.. + struct dmabuf { + void *rawbuf; // Physical address of + dma_addr_t dmaaddr; + unsigned buforder; // Log base 2 of 'rawbuf' 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; + // redundant, but makes calculations easier + unsigned fragsize; // 2**fragshift.. + unsigned dmasize; // 2**buforder. + unsigned fragsamples; + // OSS stuff + unsigned mapped:1; // Buffer mapped in cs4281_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; + } dma_dac, dma_adc; + + // midi stuff + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct cs4281_pm pm; + struct cs4281_pipeline pl[CS4281_NUMBER_OF_PIPELINES]; +}; + +#include "cs4281pm-24.c" + +#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) + +#define SOUND_MIXER_CS_APM _SIOWR('M',124, 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 prog_dmabuf_adc(struct cs4281_state *s); +static void prog_codec(struct cs4281_state *s, unsigned type); + +// --------------------------------------------------------------------- +// +// Hardware Interfaces For the CS4281 +// + + +//****************************************************************************** +// "delayus()-- Delay for the specified # of microseconds. +//****************************************************************************** +static void delayus(struct cs4281_state *s, u32 delay) +{ + u32 j; + if ((delay > 9999) && (s->pm.flags & CS4281_PM_IDLE)) { + j = (delay * HZ) / 1000000; /* calculate delay in jiffies */ + if (j < 1) + j = 1; /* minimum one jiffy. */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(j); + } else + udelay(delay); + return; +} + + +//****************************************************************************** +// "cs4281_read_ac97" -- Reads a word from the specified location in the +// CS4281's address space(based on the BA0 register). +// +// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address +// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 register, +// 0h for reads. +// 3. Write ACCTL = Control Register = 460h for initiating the write +// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h +// 5. if DCV not cleared, break and return error +// 6. Read ACSTS = Status Register = 464h, check VSTS bit +//**************************************************************************** +static int cs4281_read_ac97(struct cs4281_state *card, u32 offset, + u32 * value) +{ + u32 count, status; + + // Make sure that there is not data sitting + // around from a previous uncompleted access. + // ACSDA = Status Data Register = 47Ch + status = readl(card->pBA0 + BA0_ACSDA); + + // Setup the AC97 control registers on the CS4281 to send the + // appropriate command to the AC97 to perform the read. + // ACCAD = Command Address Register = 46Ch + // ACCDA = Command Data Register = 470h + // ACCTL = Control Register = 460h + // bit DCV - will clear when process completed + // bit CRW - Read command + // bit VFRM - valid frame enabled + // bit ESYN - ASYNC generation enabled + + // Get the actual AC97 register from the offset + writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); + writel(0, card->pBA0 + BA0_ACCDA); + writel(ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN, + card->pBA0 + BA0_ACCTL); + + // Wait for the read to occur. + for (count = 0; count < 10; count++) { + // First, we want to wait for a short time. + udelay(25); + + // Now, check to see if the read has completed. + // ACCTL = 460h, DCV should be reset by now and 460h = 17h + if (!(readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV)) + break; + } + + // Make sure the read completed. + if (readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV) + return 1; + + // Wait for the valid status bit to go active. + for (count = 0; count < 10; count++) { + // Read the AC97 status register. + // ACSTS = Status Register = 464h + status = readl(card->pBA0 + BA0_ACSTS); + + // See if we have valid status. + // VSTS - Valid Status + if (status & ACSTS_VSTS) + break; + // Wait for a short while. + udelay(25); + } + + // Make sure we got valid status. + if (!(status & ACSTS_VSTS)) + return 1; + + // Read the data returned from the AC97 register. + // ACSDA = Status Data Register = 474h + *value = readl(card->pBA0 + BA0_ACSDA); + + // Success. + return (0); +} + + +//**************************************************************************** +// +// "cs4281_write_ac97()"-- writes a word to the specified location in the +// CS461x's address space (based on the part's base address zero register). +// +// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address +// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 reg. +// 3. Write ACCTL = Control Register = 460h for initiating the write +// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h +// 5. if DCV not cleared, break and return error +// +//**************************************************************************** +static int cs4281_write_ac97(struct cs4281_state *card, u32 offset, + u32 value) +{ + u32 count, status=0; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs_4281_write_ac97()+ \n")); + + // Setup the AC97 control registers on the CS4281 to send the + // appropriate command to the AC97 to perform the read. + // ACCAD = Command Address Register = 46Ch + // ACCDA = Command Data Register = 470h + // ACCTL = Control Register = 460h + // set DCV - will clear when process completed + // reset CRW - Write command + // set VFRM - valid frame enabled + // set ESYN - ASYNC generation enabled + // set RSTN - ARST# inactive, AC97 codec not reset + + // Get the actual AC97 register from the offset + + writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); + writel(value, card->pBA0 + BA0_ACCDA); + writel(ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN, + card->pBA0 + BA0_ACCTL); + + // Wait for the write to occur. + for (count = 0; count < 100; count++) { + // First, we want to wait for a short time. + udelay(25); + // Now, check to see if the write has completed. + // ACCTL = 460h, DCV should be reset by now and 460h = 07h + status = readl(card->pBA0 + BA0_ACCTL); + if (!(status & ACCTL_DCV)) + break; + } + + // Make sure the write completed. + if (status & ACCTL_DCV) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs_4281_write_ac97()- unable to write. ACCTL_DCV active\n")); + return 1; + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs_4281_write_ac97()- 0\n")); + // Success. + return 0; +} + + +//****************************************************************************** +// "Init4281()" -- Bring up the part. +//****************************************************************************** +static __devinit int cs4281_hw_init(struct cs4281_state *card) +{ + u32 ac97_slotid; + u32 temp1, temp2; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_hw_init()+ \n")); +#ifndef NOT_CS4281_PM + if(!card) + return 1; +#endif + temp2 = readl(card->pBA0 + BA0_CFLR); + CS_DBGOUT(CS_INIT | CS_ERROR | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_hw_init() CFLR 0x%x\n", temp2)); + if(temp2 != CS4281_CFLR_DEFAULT) + { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs4281_hw_init() CFLR invalid - resetting from 0x%x to 0x%x\n", + temp2,CS4281_CFLR_DEFAULT)); + writel(CS4281_CFLR_DEFAULT, card->pBA0 + BA0_CFLR); + temp2 = readl(card->pBA0 + BA0_CFLR); + if(temp2 != CS4281_CFLR_DEFAULT) + { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs4281_hw_init() Invalid hardware - unable to configure CFLR\n")); + return 1; + } + } + + //***************************************7 + // Set up the Sound System Configuration + //*************************************** + + // Set the 'Configuration Write Protect' register + // to 4281h. Allows vendor-defined configuration + // space between 0e4h and 0ffh to be written. + + writel(0x4281, card->pBA0 + BA0_CWPR); // (3e0h) + + // (0), Blast the clock control register to zero so that the + // PLL starts out in a known state, and blast the master serial + // port control register to zero so that the serial ports also + // start out in a known state. + + writel(0, card->pBA0 + BA0_CLKCR1); // (400h) + writel(0, card->pBA0 + BA0_SERMC); // (420h) + + + // (1), Make ESYN go to zero to turn off + // the Sync pulse on the AC97 link. + + writel(0, card->pBA0 + BA0_ACCTL); + udelay(50); + + + // (2) Drive the ARST# pin low for a minimum of 1uS (as defined in + // the AC97 spec) and then drive it high. This is done for non + // AC97 modes since there might be logic external to the CS461x + // that uses the ARST# line for a reset. + + writel(0, card->pBA0 + BA0_SPMC); // (3ech) + udelay(100); + writel(SPMC_RSTN, card->pBA0 + BA0_SPMC); + delayus(card,50000); // Wait 50 ms for ABITCLK to become stable. + + // (3) Turn on the Sound System Clocks. + writel(CLKCR1_PLLP, card->pBA0 + BA0_CLKCR1); // (400h) + delayus(card,50000); // Wait for the PLL to stabilize. + // Turn on clocking of the core (CLKCR1(400h) = 0x00000030) + writel(CLKCR1_PLLP | CLKCR1_SWCE, card->pBA0 + BA0_CLKCR1); + + // (4) Power on everything for now.. + writel(0x7E, card->pBA0 + BA0_SSPM); // (740h) + + // (5) Wait for clock stabilization. + for (temp1 = 0; temp1 < 1000; temp1++) { + udelay(1000); + if (readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY) + break; + } + if (!(readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: DLLRDY failed!\n")); + return -EIO; + } + // (6) Enable ASYNC generation. + writel(ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) + + // Now wait 'for a short while' to allow the AC97 + // part to start generating bit clock. (so we don't + // Try to start the PLL without an input clock.) + delayus(card,50000); + + // Set the serial port timing configuration, so that the + // clock control circuit gets its clock from the right place. + writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. + + // (7) Wait for the codec ready signal from the AC97 codec. + + for (temp1 = 0; temp1 < 1000; temp1++) { + // Delay a mil to let things settle out and + // to prevent retrying the read too quickly. + udelay(1000); + if (readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY) // If ready, (464h) + break; // exit the 'for' loop. + } + if (!(readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY)) // If never came ready, + { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR + "cs4281: ACSTS never came ready!\n")); + return -EIO; // exit initialization. + } + // (8) Assert the 'valid frame' signal so we can + // begin sending commands to the AC97 codec. + writel(ACCTL_VFRM | ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) + + // (9), Wait until CODEC calibration is finished. + // Print an error message if it doesn't. + for (temp1 = 0; temp1 < 1000; temp1++) { + delayus(card,10000); + // Read the AC97 Powerdown Control/Status Register. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp2); + if ((temp2 & 0x0000000F) == 0x0000000F) + break; + } + if ((temp2 & 0x0000000F) != 0x0000000F) { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR + "cs4281: Codec failed to calibrate. Status = %.8x.\n", + temp2)); + return -EIO; + } + // (10), Set the serial port timing configuration, so that the + // clock control circuit gets its clock from the right place. + writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. + + + // (11) Wait until we've sampled input slots 3 & 4 as valid, meaning + // that the codec is pumping ADC data across the AC link. + for (temp1 = 0; temp1 < 1000; temp1++) { + // Delay a mil to let things settle out and + // to prevent retrying the read too quickly. + delayus(card,1000); //(test) + + // Read the input slot valid register; See + // if input slots 3 and 4 are valid yet. + if ( + (readl(card->pBA0 + BA0_ACISV) & + (ACISV_ISV3 | ACISV_ISV4)) == + (ACISV_ISV3 | ACISV_ISV4)) break; // Exit the 'for' if slots are valid. + } + // If we never got valid data, exit initialization. + if ((readl(card->pBA0 + BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) + != (ACISV_ISV3 | ACISV_ISV4)) { + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_ERR + "cs4281: Never got valid data!\n")); + return -EIO; // If no valid data, exit initialization. + } + // (12), Start digital data transfer of audio data to the codec. + writel(ACOSV_SLV3 | ACOSV_SLV4, card->pBA0 + BA0_ACOSV); // (468h) + + + //************************************** + // Unmute the Master and Alternate + // (headphone) volumes. Set to max. + //************************************** + cs4281_write_ac97(card, BA0_AC97_HEADPHONE_VOLUME, 0); + cs4281_write_ac97(card, BA0_AC97_MASTER_VOLUME, 0); + + //****************************************** + // Power on the DAC(AddDACUser()from main()) + //****************************************** + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfdff); + + // Wait until we sample a DAC ready state. + for (temp2 = 0; temp2 < 32; temp2++) { + // Let's wait a mil to let things settle. + delayus(card,1000); + // Read the current state of the power control reg. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + // If the DAC ready state bit is set, stop waiting. + if (temp1 & 0x2) + break; + } + + //****************************************** + // Power on the ADC(AddADCUser()from main()) + //****************************************** + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfeff); + + // Wait until we sample ADC ready state. + for (temp2 = 0; temp2 < 32; temp2++) { + // Let's wait a mil to let things settle. + delayus(card,1000); + // Read the current state of the power control reg. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + // If the ADC ready state bit is set, stop waiting. + if (temp1 & 0x1) + break; + } + // Set up 4281 Register contents that + // don't change for boot duration. + + // For playback, we map AC97 slot 3 and 4(Left + // & Right PCM playback) to DMA Channel 0. + // Set the fifo to be 15 bytes at offset zero. + + ac97_slotid = 0x01000f00; // FCR0.RS[4:0]=1(=>slot4, right PCM playback). + // FCR0.LS[4:0]=0(=>slot3, left PCM playback). + // FCR0.SZ[6-0]=15; FCR0.OF[6-0]=0. + writel(ac97_slotid, card->pBA0 + BA0_FCR0); // (180h) + writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR0); // Turn on FIFO Enable. + + // For capture, we map AC97 slot 10 and 11(Left + // and Right PCM Record) to DMA Channel 1. + // Set the fifo to be 15 bytes at offset sixteen. + ac97_slotid = 0x0B0A0f10; // FCR1.RS[4:0]=11(=>slot11, right PCM record). + // FCR1.LS[4:0]=10(=>slot10, left PCM record). + // FCR1.SZ[6-0]=15; FCR1.OF[6-0]=16. + writel(ac97_slotid | FCRn_PSH, card->pBA0 + BA0_FCR1); // (184h) + writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR1); // Turn on FIFO Enable. + + // Map the Playback SRC to the same AC97 slots(3 & 4-- + // --Playback left & right)as DMA channel 0. + // Map the record SRC to the same AC97 slots(10 & 11-- + // -- Record left & right) as DMA channel 1. + + ac97_slotid = 0x0b0a0100; // SCRSA.PRSS[4:0]=1(=>slot4, right PCM playback). + // SCRSA.PLSS[4:0]=0(=>slot3, left PCM playback). + // SCRSA.CRSS[4:0]=11(=>slot11, right PCM record) + // SCRSA.CLSS[4:0]=10(=>slot10, left PCM record). + writel(ac97_slotid, card->pBA0 + BA0_SRCSA); // (75ch) + + // Set 'Half Terminal Count Interrupt Enable' and 'Terminal + // Count Interrupt Enable' in DMA Control Registers 0 & 1. + // Set 'MSK' flag to 1 to keep the DMA engines paused. + temp1 = (DCRn_HTCIE | DCRn_TCIE | DCRn_MSK); // (00030001h) + writel(temp1, card->pBA0 + BA0_DCR0); // (154h + writel(temp1, card->pBA0 + BA0_DCR1); // (15ch) + + // Set 'Auto-Initialize Control' to 'enabled'; For playback, + // set 'Transfer Type Control'(TR[1:0]) to 'read transfer', + // for record, set Transfer Type Control to 'write transfer'. + // All other bits set to zero; Some will be changed @ transfer start. + temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_READ); // (20000018h) + writel(temp1, card->pBA0 + BA0_DMR0); // (150h) + temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE); // (20000014h) + writel(temp1, card->pBA0 + BA0_DMR1); // (158h) + + // Enable DMA interrupts generally, and + // DMA0 & DMA1 interrupts specifically. + temp1 = readl(card->pBA0 + BA0_HIMR) & 0xfffbfcff; + writel(temp1, card->pBA0 + BA0_HIMR); + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_hw_init()- 0\n")); + return 0; +} + +#ifndef NOT_CS4281_PM +static void printpm(struct cs4281_state *s) +{ + CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); + CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n", + (unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue)); + CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n", + s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n", + s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n", + s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n", + s->pm.u32SSCR,s->pm.u32SRCSA)); + CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n", + s->pm.u32DacASR,s->pm.u32AdcASR)); + CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n", + s->pm.u32DacSR,s->pm.u32AdcSR)); + CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n", + s->pm.u32MIDCR_Save)); + +} +static void printpipe(struct cs4281_pipeline *pl) +{ + + CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); + CS_DBGOUT(CS_PM, 9, printk("flags:0x%x number: 0%x\n", + (unsigned)pl->flags,pl->number)); + CS_DBGOUT(CS_PM, 9, printk("u32DBAnValue: 0%x u32DBCnValue: 0x%x\n", + pl->u32DBAnValue,pl->u32DBCnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32DMRnValue: 0x%x u32DCRnValue: 0x%x\n", + pl->u32DMRnValue,pl->u32DCRnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32DBAnAddress: 0x%x u32DBCnAddress: 0x%x\n", + pl->u32DBAnAddress,pl->u32DBCnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32DCAnAddress: 0x%x u32DCCnAddress: 0x%x\n", + pl->u32DCCnAddress,pl->u32DCCnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32DMRnAddress: 0x%x u32DCRnAddress: 0x%x\n", + pl->u32DMRnAddress,pl->u32DCRnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32HDSRnAddress: 0x%x u32DBAn_Save: 0x%x\n", + pl->u32HDSRnAddress,pl->u32DBAn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DBCn_Save: 0x%x u32DMRn_Save: 0x%x\n", + pl->u32DBCn_Save,pl->u32DMRn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DCRn_Save: 0x%x u32DCCn_Save: 0x%x\n", + pl->u32DCRn_Save,pl->u32DCCn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DCAn_Save: 0x%x\n", + pl->u32DCAn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRn_Save: 0x%x u32FSICn_Save: 0x%x\n", + pl->u32FCRn_Save,pl->u32FSICn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRnValue: 0x%x u32FSICnValue: 0x%x\n", + pl->u32FCRnValue,pl->u32FSICnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRnAddress: 0x%x u32FSICnAddress: 0x%x\n", + pl->u32FCRnAddress,pl->u32FSICnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32FPDRnValue: 0x%x u32FPDRnAddress: 0x%x\n", + pl->u32FPDRnValue,pl->u32FPDRnAddress)); +} +static void printpipelines(struct cs4281_state *s) +{ + int i; + for(i=0;ipl[i].flags & CS4281_PIPELINE_VALID) + { + printpipe(&s->pl[i]); + } + } +} +/**************************************************************************** +* +* Suspend - save the ac97 regs, mute the outputs and power down the part. +* +****************************************************************************/ +void cs4281_ac97_suspend(struct cs4281_state *s) +{ + int Count,i; + + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()+\n")); +/* +* change the state, save the current hwptr, then stop the dac/adc +*/ + s->pm.flags &= ~CS4281_PM_IDLE; + s->pm.flags |= CS4281_PM_SUSPENDING; + s->pm.u32hwptr_playback = readl(s->pBA0 + BA0_DCA0); + s->pm.u32hwptr_capture = readl(s->pBA0 + BA0_DCA1); + stop_dac(s); + stop_adc(s); + + for(Count = 0x2, i=0; (Count <= CS4281_AC97_HIGHESTREGTORESTORE) + && (i < CS4281_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + cs4281_read_ac97(s, BA0_AC97_RESET + Count, &s->pm.ac97[i]); + } +/* +* Save the ac97 volume registers as well as the current powerdown state. +* Now, mute the all the outputs (master, headphone, and mono), as well +* as the PCM volume, in preparation for powering down the entire part. +*/ + cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME, &s->pm.u32AC97_master_volume); + cs4281_read_ac97(s, BA0_AC97_HEADPHONE_VOLUME, &s->pm.u32AC97_headphone_volume); + cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, &s->pm.u32AC97_master_volume_mono); + cs4281_read_ac97(s, BA0_AC97_PCM_OUT_VOLUME, &s->pm.u32AC97_pcm_out_volume); + + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, 0x8000); + cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, 0x8000); + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, 0x8000); + cs4281_write_ac97(s, BA0_AC97_PCM_OUT_VOLUME, 0x8000); + + cs4281_read_ac97(s, BA0_AC97_POWERDOWN, &s->pm.u32AC97_powerdown); + cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, &s->pm.u32AC97_general_purpose); + +/* +* And power down everything on the AC97 codec. +*/ + cs4281_write_ac97(s, BA0_AC97_POWERDOWN, 0xff00); + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()-\n")); +} + +/**************************************************************************** +* +* Resume - power up the part and restore its registers.. +* +****************************************************************************/ +void cs4281_ac97_resume(struct cs4281_state *s) +{ + int Count,i; + + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()+\n")); + +/* do not save the power state registers at this time + // + // If we saved away the power control registers, write them into the + // shadows so those saved values get restored instead of the current + // shadowed value. + // + if( bPowerStateSaved ) + { + PokeShadow( 0x26, ulSaveReg0x26 ); + bPowerStateSaved = FALSE; + } +*/ + +// +// First, we restore the state of the general purpose register. This +// contains the mic select (mic1 or mic2) and if we restore this after +// we restore the mic volume/boost state and mic2 was selected at +// suspend time, we will end up with a brief period of time where mic1 +// is selected with the volume/boost settings for mic2, causing +// acoustic feedback. So we restore the general purpose register +// first, thereby getting the correct mic selected before we restore +// the mic volume/boost. +// + cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, s->pm.u32AC97_general_purpose); + +// +// Now, while the outputs are still muted, restore the state of power +// on the AC97 part. +// + cs4281_write_ac97(s, BA0_AC97_POWERDOWN, s->pm.u32AC97_powerdown); + +/* +* Restore just the first set of registers, from register number +* 0x02 to the register number that ulHighestRegToRestore specifies. +*/ + for( Count = 0x2, i=0; + (Count <= CS4281_AC97_HIGHESTREGTORESTORE) + && (i < CS4281_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + cs4281_write_ac97(s, BA0_AC97_RESET + Count, s->pm.ac97[i]); + } + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()-\n")); +} + +/* do not save the power state registers at this time +**************************************************************************** +* +* SavePowerState - Save the power registers away. +* +**************************************************************************** +void +HWAC97codec::SavePowerState(void) +{ + ENTRY(TM_OBJECTCALLS, "HWAC97codec::SavePowerState()\r\n"); + + ulSaveReg0x26 = PeekShadow(0x26); + + // + // Note that we have saved registers that need to be restored during a + // resume instead of ulAC97Regs[]. + // + bPowerStateSaved = TRUE; + +} // SavePowerState +*/ + +void cs4281_SuspendFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + /* + * We need to save the contents of the BASIC FIFO Registers. + */ + pl->u32FCRn_Save = readl(s->pBA0 + pl->u32FCRnAddress); + pl->u32FSICn_Save = readl(s->pBA0 + pl->u32FSICnAddress); +} +void cs4281_ResumeFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + /* + * We need to restore the contents of the BASIC FIFO Registers. + */ + writel(pl->u32FCRn_Save,s->pBA0 + pl->u32FCRnAddress); + writel(pl->u32FSICn_Save,s->pBA0 + pl->u32FSICnAddress); +} +void cs4281_SuspendDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + // + // We need to save the contents of the BASIC DMA Registers. + // + pl->u32DBAn_Save = readl(s->pBA0 + pl->u32DBAnAddress); + pl->u32DBCn_Save = readl(s->pBA0 + pl->u32DBCnAddress); + pl->u32DMRn_Save = readl(s->pBA0 + pl->u32DMRnAddress); + pl->u32DCRn_Save = readl(s->pBA0 + pl->u32DCRnAddress); + pl->u32DCCn_Save = readl(s->pBA0 + pl->u32DCCnAddress); + pl->u32DCAn_Save = readl(s->pBA0 + pl->u32DCAnAddress); +} +void cs4281_ResumeDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + // + // We need to save the contents of the BASIC DMA Registers. + // + writel( pl->u32DBAn_Save, s->pBA0 + pl->u32DBAnAddress); + writel( pl->u32DBCn_Save, s->pBA0 + pl->u32DBCnAddress); + writel( pl->u32DMRn_Save, s->pBA0 + pl->u32DMRnAddress); + writel( pl->u32DCRn_Save, s->pBA0 + pl->u32DCRnAddress); + writel( pl->u32DCCn_Save, s->pBA0 + pl->u32DCCnAddress); + writel( pl->u32DCAn_Save, s->pBA0 + pl->u32DCAnAddress); +} + +int cs4281_suspend(struct cs4281_state *s) +{ + int i; + u32 u32CLKCR1; + struct cs4281_pm *pm = &s->pm; + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, + printk("cs4281: cs4281_suspend()+ flags=%d\n", + (unsigned)s->pm.flags)); +/* +* check the current state, only suspend if IDLE +*/ + if(!(s->pm.flags & CS4281_PM_IDLE)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs4281: cs4281_suspend() unable to suspend, not IDLE\n")); + return 1; + } + s->pm.flags &= ~CS4281_PM_IDLE; + s->pm.flags |= CS4281_PM_SUSPENDING; + +// +// Gershwin CLKRUN - Set CKRA +// + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + + pm->u32CLKCR1_SAVE = u32CLKCR1; + if(!(u32CLKCR1 & 0x00010000 ) ) + writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); + +// +// First, turn on the clocks (yikes) to the devices, so that they will +// respond when we try to save their state. +// + if(!(u32CLKCR1 & CLKCR1_SWCE)) + { + writel(u32CLKCR1 | CLKCR1_SWCE , s->pBA0 + BA0_CLKCR1); + } + + // + // Save the power state + // + pm->u32SSPMValue = readl(s->pBA0 + BA0_SSPM); + + // + // Disable interrupts. + // + writel(HICR_CHGM, s->pBA0 + BA0_HICR); + + // + // Save the PCM Playback Left and Right Volume Control. + // + pm->u32PPLVCvalue = readl(s->pBA0 + BA0_PPLVC); + pm->u32PPRVCvalue = readl(s->pBA0 + BA0_PPRVC); + + // + // Save the FM Synthesis Left and Right Volume Control. + // + pm->u32FMLVCvalue = readl(s->pBA0 + BA0_FMLVC); + pm->u32FMRVCvalue = readl(s->pBA0 + BA0_FMRVC); + + // + // Save the GPIOR value. + // + pm->u32GPIORvalue = readl(s->pBA0 + BA0_GPIOR); + + // + // Save the JSCTL value. + // + pm->u32JSCTLvalue = readl(s->pBA0 + BA0_GPIOR); + + // + // Save Sound System Control Register + // + pm->u32SSCR = readl(s->pBA0 + BA0_SSCR); + + // + // Save SRC Slot Assinment register + // + pm->u32SRCSA = readl(s->pBA0 + BA0_SRCSA); + + // + // Save sample rate + // + pm->u32DacASR = readl(s->pBA0 + BA0_PASR); + pm->u32AdcASR = readl(s->pBA0 + BA0_CASR); + pm->u32DacSR = readl(s->pBA0 + BA0_DACSR); + pm->u32AdcSR = readl(s->pBA0 + BA0_ADCSR); + + // + // Loop through all of the PipeLines + // + for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) + { + if(s->pl[i].flags & CS4281_PIPELINE_VALID) + { + // + // Ask the DMAengines and FIFOs to Suspend. + // + cs4281_SuspendDMAengine(s,&s->pl[i]); + cs4281_SuspendFIFO(s,&s->pl[i]); + } + } + // + // We need to save the contents of the Midi Control Register. + // + pm->u32MIDCR_Save = readl(s->pBA0 + BA0_MIDCR); +/* +* save off the AC97 part information +*/ + cs4281_ac97_suspend(s); + + // + // Turn off the serial ports. + // + writel(0, s->pBA0 + BA0_SERMC); + + // + // Power off FM, Joystick, AC link, + // + writel(0, s->pBA0 + BA0_SSPM); + + // + // DLL off. + // + writel(0, s->pBA0 + BA0_CLKCR1); + + // + // AC link off. + // + writel(0, s->pBA0 + BA0_SPMC); + + // + // Put the chip into D3(hot) state. + // + // PokeBA0(BA0_PMCS, 0x00000003); + + // + // Gershwin CLKRUN - Clear CKRA + // + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + writel(u32CLKCR1 & 0xFFFEFFFF, s->pBA0 + BA0_CLKCR1); + +#ifdef CSDEBUG + printpm(s); + printpipelines(s); +#endif + + s->pm.flags &= ~CS4281_PM_SUSPENDING; + s->pm.flags |= CS4281_PM_SUSPENDED; + + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, + printk("cs4281: cs4281_suspend()- flags=%d\n", + (unsigned)s->pm.flags)); + return 0; +} + +int cs4281_resume(struct cs4281_state *s) +{ + int i; + unsigned temp1; + u32 u32CLKCR1; + struct cs4281_pm *pm = &s->pm; + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk( "cs4281: cs4281_resume()+ flags=%d\n", + (unsigned)s->pm.flags)); + if(!(s->pm.flags & CS4281_PM_SUSPENDED)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs4281: cs4281_resume() unable to resume, not SUSPENDED\n")); + return 1; + } + s->pm.flags &= ~CS4281_PM_SUSPENDED; + s->pm.flags |= CS4281_PM_RESUMING; + +// +// Gershwin CLKRUN - Set CKRA +// + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); + + // + // set the power state. + // + //old PokeBA0(BA0_PMCS, 0); + + // + // Program the clock circuit and serial ports. + // + temp1 = cs4281_hw_init(s); + if (temp1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, + printk(KERN_ERR + "cs4281: resume cs4281_hw_init() error.\n")); + return -1; + } + + // + // restore the Power state + // + writel(pm->u32SSPMValue, s->pBA0 + BA0_SSPM); + + // + // Set post SRC mix setting (FM or ALT48K) + // + writel(pm->u32SSPM_BITS, s->pBA0 + BA0_SSPM); + + // + // Loop through all of the PipeLines + // + for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) + { + if(s->pl[i].flags & CS4281_PIPELINE_VALID) + { + // + // Ask the DMAengines and FIFOs to Resume. + // + cs4281_ResumeDMAengine(s,&s->pl[i]); + cs4281_ResumeFIFO(s,&s->pl[i]); + } + } + // + // We need to restore the contents of the Midi Control Register. + // + writel(pm->u32MIDCR_Save, s->pBA0 + BA0_MIDCR); + + cs4281_ac97_resume(s); + // + // Restore the PCM Playback Left and Right Volume Control. + // + writel(pm->u32PPLVCvalue, s->pBA0 + BA0_PPLVC); + writel(pm->u32PPRVCvalue, s->pBA0 + BA0_PPRVC); + + // + // Restore the FM Synthesis Left and Right Volume Control. + // + writel(pm->u32FMLVCvalue, s->pBA0 + BA0_FMLVC); + writel(pm->u32FMRVCvalue, s->pBA0 + BA0_FMRVC); + + // + // Restore the JSCTL value. + // + writel(pm->u32JSCTLvalue, s->pBA0 + BA0_JSCTL); + + // + // Restore the GPIOR register value. + // + writel(pm->u32GPIORvalue, s->pBA0 + BA0_GPIOR); + + // + // Restore Sound System Control Register + // + writel(pm->u32SSCR, s->pBA0 + BA0_SSCR); + + // + // Restore SRC Slot Assignment register + // + writel(pm->u32SRCSA, s->pBA0 + BA0_SRCSA); + + // + // Restore sample rate + // + writel(pm->u32DacASR, s->pBA0 + BA0_PASR); + writel(pm->u32AdcASR, s->pBA0 + BA0_CASR); + writel(pm->u32DacSR, s->pBA0 + BA0_DACSR); + writel(pm->u32AdcSR, s->pBA0 + BA0_ADCSR); + + // + // Restore CFL1/2 registers we saved to compensate for OEM bugs. + // + // PokeBA0(BA0_CFLR, ulConfig); + + // + // Gershwin CLKRUN - Clear CKRA + // + writel(pm->u32CLKCR1_SAVE, s->pBA0 + BA0_CLKCR1); + + // + // Enable interrupts on the part. + // + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); + +#ifdef CSDEBUG + printpm(s); + printpipelines(s); +#endif +/* +* change the state, restore the current hwptrs, then stop the dac/adc +*/ + s->pm.flags |= CS4281_PM_IDLE; + s->pm.flags &= ~(CS4281_PM_SUSPENDING | CS4281_PM_SUSPENDED + | CS4281_PM_RESUMING | CS4281_PM_RESUMED); + + writel(s->pm.u32hwptr_playback, s->pBA0 + BA0_DCA0); + writel(s->pm.u32hwptr_capture, s->pBA0 + BA0_DCA1); + start_dac(s); + start_adc(s); + + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, printk("cs4281: cs4281_resume()- flags=%d\n", + (unsigned)s->pm.flags)); + return 0; +} + +#endif + +//****************************************************************************** +// "cs4281_play_rate()" -- +//****************************************************************************** +static void cs4281_play_rate(struct cs4281_state *card, u32 playrate) +{ + u32 DACSRvalue = 1; + + // Based on the sample rate, program the DACSR register. + if (playrate == 8000) + DACSRvalue = 5; + if (playrate == 11025) + DACSRvalue = 4; + else if (playrate == 22050) + DACSRvalue = 2; + else if (playrate == 44100) + DACSRvalue = 1; + else if ((playrate <= 48000) && (playrate >= 6023)) + DACSRvalue = 24576000 / (playrate * 16); + else if (playrate < 6023) + // Not allowed by open. + return; + else if (playrate > 48000) + // Not allowed by open. + return; + CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 2, printk(KERN_INFO + "cs4281: cs4281_play_rate(): DACSRvalue=0x%.8x playrate=%d\n", + DACSRvalue, playrate)); + // Write the 'sample rate select code' + // to the 'DAC Sample Rate' register. + writel(DACSRvalue, card->pBA0 + BA0_DACSR); // (744h) +} + +//****************************************************************************** +// "cs4281_record_rate()" -- Initialize the record sample rate converter. +//****************************************************************************** +static void cs4281_record_rate(struct cs4281_state *card, u32 outrate) +{ + u32 ADCSRvalue = 1; + + // + // Based on the sample rate, program the ADCSR register + // + if (outrate == 8000) + ADCSRvalue = 5; + if (outrate == 11025) + ADCSRvalue = 4; + else if (outrate == 22050) + ADCSRvalue = 2; + else if (outrate == 44100) + ADCSRvalue = 1; + else if ((outrate <= 48000) && (outrate >= 6023)) + ADCSRvalue = 24576000 / (outrate * 16); + else if (outrate < 6023) { + // Not allowed by open. + return; + } else if (outrate > 48000) { + // Not allowed by open. + return; + } + CS_DBGOUT(CS_WAVE_READ | CS_PARMS, 2, printk(KERN_INFO + "cs4281: cs4281_record_rate(): ADCSRvalue=0x%.8x outrate=%d\n", + ADCSRvalue, outrate)); + // Write the 'sample rate select code + // to the 'ADC Sample Rate' register. + writel(ADCSRvalue, card->pBA0 + BA0_ADCSR); // (748h) +} + + + +static void stop_dac(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4281: stop_dac():\n")); + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_WRITE; + temp1 = readl(s->pBA0 + BA0_DCR0) | DCRn_MSK; + writel(temp1, s->pBA0 + BA0_DCR0); + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void start_dac(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4281: 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)) +#ifndef NOT_CS4281_PM + && (s->pm.flags & CS4281_PM_IDLE)) +#else +) +#endif + { + s->ena |= FMODE_WRITE; + temp1 = readl(s->pBA0 + BA0_DCR0) & ~DCRn_MSK; // Clear DMA0 channel mask. + writel(temp1, s->pBA0 + BA0_DCR0); // Start DMA'ing. + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. + + writel(7, s->pBA0 + BA0_PPRVC); + writel(7, s->pBA0 + BA0_PPLVC); + CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO + "cs4281: start_dac(): writel 0x%x start dma\n", temp1)); + + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: start_dac()-\n")); +} + + +static void stop_adc(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: 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; + } + temp1 = readl(s->pBA0 + BA0_DCR1) | DCRn_MSK; + writel(temp1, s->pBA0 + BA0_DCR1); + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: stop_adc()-\n")); +} + + +static void start_adc(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: start_adc()+\n")); + + if (!(s->ena & FMODE_READ) && + (s->dma_adc.mapped || s->dma_adc.count <= + (signed) (s->dma_adc.dmasize - 2 * s->dma_adc.fragsize)) + && s->dma_adc.ready +#ifndef NOT_CS4281_PM + && (s->pm.flags & CS4281_PM_IDLE)) +#else +) +#endif + { + 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); + + if (prog_dmabuf_adc(s) != 0) { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: start_adc(): error in prog_dmabuf_adc\n")); + } + s->conversion = 1; + } + spin_lock_irqsave(&s->lock, flags); + s->ena |= FMODE_READ; + temp1 = readl(s->pBA0 + BA0_DCR1) & ~DCRn_MSK; // Clear DMA1 channel mask bit. + writel(temp1, s->pBA0 + BA0_DCR1); // Start recording + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. + spin_unlock_irqrestore(&s->lock, flags); + + CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO + "cs4281: start_adc(): writel 0x%x \n", temp1)); + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: start_adc()-\n")); + +} + + +// --------------------------------------------------------------------- + +#define DMABUF_MINORDER 1 // ==> min buffer size = 8K. + + +extern void dealloc_dmabuf(struct cs4281_state *s, struct dmabuf *db) +{ + struct page *map, *mapend; + + if (db->rawbuf) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = + virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - + 1); + for (map = virt_to_page(db->rawbuf); map <= mapend; map++) + cs4x_mem_map_unreserve(map); + free_dmabuf(s, db); + } + if (s->tmpbuff && (db->type == CS_TYPE_ADC)) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = + virt_to_page(s->tmpbuff + + (PAGE_SIZE << s->buforder_tmpbuff) - 1); + for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) + cs4x_mem_map_unreserve(map); + free_dmabuf2(s, db); + } + s->tmpbuff = NULL; + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct cs4281_state *s, struct dmabuf *db) +{ + int order; + unsigned bytespersec, temp1; + unsigned bufs, sample_shift = 0; + struct page *map, *mapend; + unsigned long df; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_dmabuf()+\n")); + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = + db->endcleared = db->blocks = db->wakeup = db->underrun = 0; +/* +* check for order within limits, but do not overwrite value, check +* later for a fractional defaultorder (i.e. 100+). +*/ + if((defaultorder > 0) && (defaultorder < 12)) + df = defaultorder; + else + df = 1; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = df; order >= DMABUF_MINORDER; order--) + if ( (db->rawbuf = (void *) pci_alloc_consistent( + s->pcidev, PAGE_SIZE << order, &db-> dmaaddr))) + break; + if (!db->rawbuf) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: prog_dmabuf(): unable to allocate rawbuf\n")); + return -ENOMEM; + } + db->buforder = order; + // Now mark the pages as reserved; otherwise the + // remap_page_range() in cs4281_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(db->rawbuf); map <= mapend; map++) + cs4x_mem_map_reserve(map); + } + if (!s->tmpbuff && (db->type == CS_TYPE_ADC)) { + for (order = df; order >= DMABUF_MINORDER; + order--) + if ( (s->tmpbuff = (void *) pci_alloc_consistent( + s->pcidev, PAGE_SIZE << order, + &s->dmaaddr_tmpbuff))) + break; + if (!s->tmpbuff) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: prog_dmabuf(): unable to allocate tmpbuff\n")); + return -ENOMEM; + } + s->buforder_tmpbuff = order; + // Now mark the pages as reserved; otherwise the + // remap_page_range() in cs4281_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(s->tmpbuff + + (PAGE_SIZE << s->buforder_tmpbuff) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) + cs4x_mem_map_reserve(map); + } + if (db->type == CS_TYPE_DAC) { + if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->prop_dac.channels > 1) + sample_shift++; + bytespersec = s->prop_dac.rate << sample_shift; + } else // CS_TYPE_ADC + { + if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->prop_adc.channels > 1) + sample_shift++; + bytespersec = s->prop_adc.rate << sample_shift; + } + bufs = PAGE_SIZE << db->buforder; + +/* +* 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. +*/ + if(defaultorder >= 100) + { + bufs = 1 << (defaultorder-100); + } + +#define INTERRUPT_RATE_MS 100 // Interrupt rate in milliseconds. + db->numfrag = 2; +/* +* Nominal frag size(bytes/interrupt) +*/ + temp1 = bytespersec / (1000 / INTERRUPT_RATE_MS); + db->fragshift = 8; // Min 256 bytes. + while (1 << db->fragshift < temp1) // Calc power of 2 frag size. + db->fragshift += 1; + db->fragsize = 1 << db->fragshift; + db->dmasize = db->fragsize * 2; + db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. + +// If the calculated size is larger than the allocated +// buffer, divide the allocated buffer into 2 fragments. + if (db->dmasize > bufs) { + + db->numfrag = 2; // Two fragments. + db->fragsize = bufs >> 1; // Each 1/2 the alloc'ed buffer. + db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. + db->dmasize = bufs; // Use all the alloc'ed buffer. + + db->fragshift = 0; // Calculate 'fragshift'. + temp1 = db->fragsize; // update_ptr() uses it + while ((temp1 >>= 1) > 1) // to calc 'total-bytes' + db->fragshift += 1; // returned in DSP_GETI/OPTR. + } + CS_DBGOUT(CS_PARMS, 3, printk(KERN_INFO + "cs4281: prog_dmabuf(): numfrag=%d fragsize=%d fragsamples=%d fragshift=%d bufs=%d fmt=0x%x ch=%d\n", + db->numfrag, db->fragsize, db->fragsamples, + db->fragshift, bufs, + (db->type == CS_TYPE_DAC) ? s->prop_dac.fmt : + s->prop_adc.fmt, + (db->type == CS_TYPE_DAC) ? s->prop_dac.channels : + s->prop_adc.channels)); + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_dmabuf()-\n")); + return 0; +} + + +static int prog_dmabuf_adc(struct cs4281_state *s) +{ + unsigned long va; + unsigned count; + int c; + stop_adc(s); + s->dma_adc.type = CS_TYPE_ADC; + if ((c = prog_dmabuf(s, &s->dma_adc))) + return c; + + if (s->dma_adc.rawbuf) { + memset(s->dma_adc.rawbuf, + (s->prop_adc. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + s->dma_adc.dmasize); + } + if (s->tmpbuff) { + memset(s->tmpbuff, + (s->prop_adc. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + PAGE_SIZE << s->buforder_tmpbuff); + } + + va = virt_to_bus(s->dma_adc.rawbuf); + + count = s->dma_adc.dmasize; + + if (s->prop_adc. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) + count /= 2; // 16-bit. + + if (s->prop_adc.channels > 1) + count /= 2; // Assume stereo. + + CS_DBGOUT(CS_WAVE_READ, 3, printk(KERN_INFO + "cs4281: prog_dmabuf_adc(): count=%d va=0x%.8x\n", + count, (unsigned) va)); + + writel(va, s->pBA0 + BA0_DBA1); // Set buffer start address. + writel(count - 1, s->pBA0 + BA0_DBC1); // Set count. + s->dma_adc.ready = 1; + return 0; +} + + +static int prog_dmabuf_dac(struct cs4281_state *s) +{ + unsigned long va; + unsigned count; + int c; + stop_dac(s); + s->dma_dac.type = CS_TYPE_DAC; + if ((c = prog_dmabuf(s, &s->dma_dac))) + return c; + memset(s->dma_dac.rawbuf, + (s->prop_dac.fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + s->dma_dac.dmasize); + + va = virt_to_bus(s->dma_dac.rawbuf); + + count = s->dma_dac.dmasize; + if (s->prop_dac. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) + count /= 2; // 16-bit. + + if (s->prop_dac.channels > 1) + count /= 2; // Assume stereo. + + writel(va, s->pBA0 + BA0_DBA0); // Set buffer start address. + writel(count - 1, s->pBA0 + BA0_DBC0); // Set count. + + CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO + "cs4281: prog_dmabuf_dac(): count=%d va=0x%.8x\n", + count, (unsigned) va)); + + 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 + "cs4281: clear_advance(): memset %d at 0x%.8x for %d size \n", + (unsigned)c, (unsigned)((char *) buf) + bptr, len)); + memset(((char *) buf) + bptr, c, len); +} + + + +// call with spinlock held! +static void cs4281_update_ptr(struct cs4281_state *s, int intflag) +{ + int diff; + unsigned hwptr, va; + + // update ADC pointer + if (s->ena & FMODE_READ) { + hwptr = readl(s->pBA0 + BA0_DCA1); // Read capture DMA address. + va = virt_to_bus(s->dma_adc.rawbuf); + hwptr -= (unsigned) va; + diff = + (s->dma_adc.dmasize + hwptr - + s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count > s->dma_adc.dmasize) + s->dma_adc.count = s->dma_adc.dmasize; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= + (signed) s->dma_adc.fragsize) wake_up(&s-> + dma_adc. + wait); + } else { + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", + (unsigned)s, s->dma_adc.hwptr, + s->dma_adc.total_bytes, s->dma_adc.count)); + } + // 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) { + hwptr = readl(s->pBA0 + BA0_DCA0); // Read play DMA address. + va = virt_to_bus(s->dma_dac.rawbuf); + hwptr -= (unsigned) va; + diff = (s->dma_dac.dmasize + hwptr - + s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= s->dma_dac.fragsize) { + s->dma_dac.wakeup = 1; + wake_up(&s->dma_dac.wait); + if (s->dma_dac.count > s->dma_dac.dmasize) + s->dma_dac.count &= + s->dma_dac.dmasize - 1; + } + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.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 + "cs4281: cs4281_update_ptr(): memset %d at 0x%.8x for %d size \n", + (unsigned)(s->prop_dac.fmt & + (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + (unsigned)s->dma_dac.rawbuf, + s->dma_dac.dmasize)); + memset(s->dma_dac.rawbuf, + (s->prop_dac. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? + 0x80 : 0, s->dma_dac.dmasize); + if (s->dma_dac.count < 0) { + s->dma_dac.underrun = 1; + s->dma_dac.count = 0; + CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): underrun\n")); + } + } else if (s->dma_dac.count <= + (signed) s->dma_dac.fragsize + && !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, + s->dma_dac.dmasize, + s->dma_dac.swptr, + s->dma_dac.fragsize, + (s->prop_dac. + fmt & (AFMT_U8 | + AFMT_U16_LE)) ? 0x80 + : 0); + s->dma_dac.endcleared = 1; + } + if ( (s->dma_dac.count <= (signed) s->dma_dac.dmasize/2) || + intflag) + { + wake_up(&s->dma_dac.wait); + } + } + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", + (unsigned) s, s->dma_dac.hwptr, + s->dma_dac.total_bytes, s->dma_dac.count)); + } +} + + +// --------------------------------------------------------------------- + +static void prog_codec(struct cs4281_state *s, unsigned type) +{ + unsigned long flags; + unsigned temp1, format; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_codec()+ \n")); + + spin_lock_irqsave(&s->lock, flags); + if (type == CS_TYPE_ADC) { + temp1 = readl(s->pBA0 + BA0_DCR1); + writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR1); // Stop capture DMA, if active. + + // program sampling rates + // Note, for CS4281, capture & play rates can be set independently. + cs4281_record_rate(s, s->prop_adc.rate); + + // program ADC parameters + format = DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE; + if (s->prop_adc. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit + if (s->prop_adc.fmt & (AFMT_S16_BE | AFMT_U16_BE)) // Big-endian? + format |= DMRn_BEND; + if (s->prop_adc.fmt & (AFMT_U16_LE | AFMT_U16_BE)) + format |= DMRn_USIGN; // Unsigned. + } else + format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned + if (s->prop_adc.channels < 2) + format |= DMRn_MONO; + + writel(format, s->pBA0 + BA0_DMR1); + + CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO + "cs4281: prog_codec(): adc %s %s %s rate=%d DMR0 format=0x%.8x\n", + (format & DMRn_SIZE8) ? "8" : "16", + (format & DMRn_USIGN) ? "Unsigned" : "Signed", + (format & DMRn_MONO) ? "Mono" : "Stereo", + s->prop_adc.rate, format)); + + s->ena &= ~FMODE_READ; // not capturing data yet + } + + + if (type == CS_TYPE_DAC) { + temp1 = readl(s->pBA0 + BA0_DCR0); + writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR0); // Stop play DMA, if active. + + // program sampling rates + // Note, for CS4281, capture & play rates can be set independently. + cs4281_play_rate(s, s->prop_dac.rate); + + // program DAC parameters + format = DMRn_DMA | DMRn_AUTO | DMRn_TR_READ; + if (s->prop_dac. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit + if (s->prop_dac.fmt & (AFMT_S16_BE | AFMT_U16_BE)) + format |= DMRn_BEND; // Big Endian. + if (s->prop_dac.fmt & (AFMT_U16_LE | AFMT_U16_BE)) + format |= DMRn_USIGN; // Unsigned. + } else + format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned + + if (s->prop_dac.channels < 2) + format |= DMRn_MONO; + + writel(format, s->pBA0 + BA0_DMR0); + + + CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO + "cs4281: prog_codec(): dac %s %s %s rate=%d DMR0 format=0x%.8x\n", + (format & DMRn_SIZE8) ? "8" : "16", + (format & DMRn_USIGN) ? "Unsigned" : "Signed", + (format & DMRn_MONO) ? "Mono" : "Stereo", + s->prop_dac.rate, format)); + + s->ena &= ~FMODE_WRITE; // not capturing data yet + + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_codec()- \n")); +} + + +static int mixer_ioctl(struct cs4281_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[] = { + BA0_AC97_PCM_OUT_VOLUME, + BA0_AC97_AUX_VOLUME, + BA0_AC97_CD_VOLUME, + BA0_AC97_LINE_IN_VOLUME + }; + 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 + "cs4281: 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) || + (cmd == SOUND_MIXER_CS_APM)) + { + 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; +#ifndef NOT_CS4281_PM + case SOUND_MIXER_CS_APM: + if (get_user(val, (unsigned long *) arg)) + return -EFAULT; + if(val == CS_IOCTL_CMD_SUSPEND) + cs4281_suspend(s); + else if(val == CS_IOCTL_CMD_RESUME) + cs4281_resume(s); + else + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: mixer_ioctl(): invalid APM cmd (%d)\n", + val)); + } + return 0; +#endif + default: + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: mixer_ioctl(): ERROR unknown debug cmd\n")); + return 0; + } + } +#endif + + if (cmd == SOUND_MIXER_PRIVATE1) { + // enable/disable/query mixer preamp + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != -1) { + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + temp1 = val ? (temp1 | 0x40) : (temp1 & 0xffbf); + cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); + } + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + val = (temp1 & 0x40) ? 1 : 0; + return put_user(val, (int *) arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + // enable/disable/query spatializer + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != -1) { + temp1 = (val & 0x3f) >> 2; + cs4281_write_ac97(s, BA0_AC97_3D_CONTROL, temp1); + cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, + &temp1); + cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, + temp1 | 0x2000); + } + cs4281_read_ac97(s, BA0_AC97_3D_CONTROL, &temp1); + return put_user((temp1 << 2) | 3, (int *) arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "CS4281", sizeof(info.id)); + strncpy(info.name, "Crystal CS4281", 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, "CS4281", sizeof(info.id)); + strncpy(info.name, "Crystal CS4281", 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 + cs4281_read_ac97(s, BA0_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_SYNTH | + SOUND_MASK_CD | SOUND_MASK_LINE | + SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | + SOUND_MASK_RECLEV | + SOUND_MASK_SPEAKER, (int *) arg); + + case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source + return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_CD | SOUND_MASK_VOLUME | + SOUND_MASK_LINE1, (int *) arg); + + case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | + SOUND_MASK_CD | SOUND_MASK_LINE | + SOUND_MASK_LINE1 | SOUND_MASK_MIC | + 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; + cs4281_write_ac97(s, + BA0_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; + + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, temp1); + cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, 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; + cs4281_write_ac97(s, BA0_AC97_PC_BEEP_VOLUME, 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; + cs4281_write_ac97(s, BA0_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; + } + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + temp1 &= 0x40; // Isolate 20db gain bit. + if (rl < 3) { + temp1 |= 0x8000; + rl = 0; + } + rl = 31 - rl; // Convert volume to attenuation. + temp1 |= rl; + cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, 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 + BA0_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 + BA0_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 + "cs4281: 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; + cs4281_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 +#ifndef NOT_CS4281_PM + CS_DBGOUT(CS_PM, 9, printk(KERN_INFO + "write ac97 mixreg[%d]=0x%x mix.vol[]=0x%x\n", + vidx-1,temp1,s->mix.vol[vidx-1])); +#endif + return put_user(s->mix.vol[vidx - 1], (int *) arg); + } +} + + +// --------------------------------------------------------------------- + +static int cs4281_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct cs4281_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_open_mixdev()+\n")); + + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + if(s->dev_mixer == minor) + break; + } + if (!s) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, + printk(KERN_INFO "cs4281: cs4281_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 "cs4281: cs4281_open_mixdev()- 0\n")); + + return 0; +} + + +static int cs4281_release_mixdev(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + + +static int cs4281_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct cs4281_state *) file->private_data, cmd, + arg); +} + + +// ****************************************************************************************** +// Mixer file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_mixer_fops = { + llseek:no_llseek, + ioctl:cs4281_ioctl_mixdev, + open:cs4281_open_mixdev, + release:cs4281_release_mixdev, +}; + +// --------------------------------------------------------------------- + + +static int drain_adc(struct cs4281_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_adc.mapped) + return 0; + add_wait_queue(&s->dma_adc.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: drain_adc() %d\n", count)); + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: drain_adc() count<0\n")); + break; + } + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_adc.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = + 3 * HZ * (count + + s->dma_adc.fragsize) / 2 / s->prop_adc.rate; + if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->prop_adc.channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cs4281: dma timed out??\n"); + } + remove_wait_queue(&s->dma_adc.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac(struct cs4281_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_dac.mapped) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + 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) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = + 3 * HZ * (count + + s->dma_dac.fragsize) / 2 / s->prop_dac.rate; + if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->prop_dac.channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cs4281: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +//**************************************************************************** +// +// CopySamples copies 16-bit stereo samples from the source to the +// destination, possibly converting down to either 8-bit or mono or both. +// count specifies the number of output bytes to write. +// +// Arguments: +// +// dst - Pointer to a destination buffer. +// src - Pointer to a source buffer +// count - The number of bytes to copy into the destination buffer. +// iChannels - Stereo - 2 +// Mono - 1 +// fmt - AFMT_xxx (soundcard.h formats) +// +// NOTES: only call this routine for conversion to 8bit from 16bit +// +//**************************************************************************** +static void CopySamples(char *dst, char *src, int count, int iChannels, + unsigned fmt) +{ + + unsigned short *psSrc; + long lAudioSample; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: CopySamples()+ ")); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " dst=0x%x src=0x%x count=%d iChannels=%d fmt=0x%x\n", + (unsigned) dst, (unsigned) src, (unsigned) count, + (unsigned) iChannels, (unsigned) fmt)); + + // Gershwin does format conversion in hardware so normally + // we don't do any host based coversion. The data formatter + // truncates 16 bit data to 8 bit and that causes some hiss. + // We have already forced the HW to do 16 bit sampling and + // 2 channel so that we can use software to round instead + // of truncate + + // + // See if the data should be output as 8-bit unsigned stereo. + // or if the data should be output at 8-bit unsigned mono. + // + if ( ((iChannels == 2) && (fmt & AFMT_U8)) || + ((iChannels == 1) && (fmt & AFMT_U8)) ) { + // + // Convert each 16-bit unsigned stereo sample to 8-bit unsigned + // stereo using rounding. + // + psSrc = (unsigned short *) src; + count = count / 2; + while (count--) { + lAudioSample = (long) psSrc[count] + (long) 0x80; + if (lAudioSample > 0xffff) { + lAudioSample = 0xffff; + } + dst[count] = (char) (lAudioSample >> 8); + } + } + // + // check for 8-bit signed stereo. + // + else if ((iChannels == 2) && (fmt & AFMT_S8)) { + // + // Convert each 16-bit stereo sample to 8-bit stereo using rounding. + // + psSrc = (short *) src; + while (count--) { + lAudioSample = + (((long) psSrc[0] + (long) psSrc[1]) / 2); + psSrc += 2; + *dst++ = (char) ((short) lAudioSample >> 8); + } + } + // + // Otherwise, the data should be output as 8-bit signed mono. + // + else if ((iChannels == 1) && (fmt & AFMT_S8)) { + // + // Convert each 16-bit signed mono sample to 8-bit signed mono + // using rounding. + // + psSrc = (short *) src; + count = count / 2; + while (count--) { + lAudioSample = + (((long) psSrc[0] + (long) psSrc[1]) / 2); + if (lAudioSample > 0x7fff) { + lAudioSample = 0x7fff; + } + psSrc += 2; + *dst++ = (char) ((short) lAudioSample >> 8); + } + } +} + +// +// cs_copy_to_user() +// replacement for the standard copy_to_user, to allow for a conversion from +// 16 bit to 8 bit if the record conversion is active. the cs4281 has some +// issues with 8 bit capture, so the driver always captures data in 16 bit +// and then if the user requested 8 bit, converts from 16 to 8 bit. +// +static unsigned cs_copy_to_user(struct cs4281_state *s, void *dest, + unsigned *hwsrc, unsigned cnt, + unsigned *copied) +{ + void *src = hwsrc; //default to the standard destination buffer addr + + CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO + "cs_copy_to_user()+ fmt=0x%x fmt_o=0x%x cnt=%d dest=0x%.8x\n", + s->prop_adc.fmt, s->prop_adc.fmt_original, + (unsigned) cnt, (unsigned) dest)); + + if (cnt > s->dma_adc.dmasize) { + cnt = s->dma_adc.dmasize; + } + if (!cnt) { + *copied = 0; + return 0; + } + if (s->conversion) { + if (!s->tmpbuff) { + *copied = cnt / 2; + return 0; + } + CopySamples(s->tmpbuff, (void *) hwsrc, cnt, + (unsigned) s->prop_adc.channels, + s->prop_adc.fmt_original); + src = s->tmpbuff; + cnt = cnt / 2; + } + + if (copy_to_user(dest, src, cnt)) { + *copied = 0; + return -EFAULT; + } + *copied = cnt; + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs_copy_to_user()- copied bytes is %d \n", cnt)); + return 0; +} + +// --------------------------------------------------------------------- + +static ssize_t cs4281_read(struct file *file, char *buffer, size_t count, + loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned copied = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4281: cs4281_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); + + // get the current copy point of the sw buffer + swptr = s->dma_adc.swptr; + + // cnt is the amount of unread bytes from the end of the + // hw buffer to the current sw pointer + cnt = s->dma_adc.dmasize - swptr; + + // 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 (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + 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 > (count * 2)) + cnt = (count * 2); + } else { + if (cnt > count) + cnt = 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 rawbuf+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, count)); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " .dmasize=%d .count=%d buffer=0x%.8x ret=%d\n", + s->dma_adc.dmasize, s->dma_adc.count, + (unsigned) buffer, ret)); + + if (cs_copy_to_user + (s, buffer, s->dma_adc.rawbuf + swptr, cnt, &copied)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + 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 "cs4281: cs4281_read()- %d\n", ret)); + return ret; +} + + +static ssize_t cs4281_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr, hwptr, busaddr; + int cnt; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4281: cs4281_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) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + if (s->dma_dac.underrun) { + s->dma_dac.underrun = 0; + hwptr = readl(s->pBA0 + BA0_DCA0); + busaddr = virt_to_bus(s->dma_dac.rawbuf); + hwptr -= (unsigned) busaddr; + s->dma_dac.swptr = s->dma_dac.hwptr = hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize - swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.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(&s->dma_dac.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.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 "cs4281: cs4281_write()- %d\n", ret)); + return ret; +} + + +static unsigned int cs4281_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cs4281_state *s = + (struct cs4281_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 "cs4281: cs4281_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 + "cs4281: cs4281_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 + "cs4281: cs4281_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); + cs4281_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.dmasize/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 "cs4281: cs4281_poll()- 0x%.8x\n", + mask)); + return mask; +} + + +static int cs4281_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_mmap()+\n")); + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac(s)) != 0) + return ret; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; +// +// only support PLAYBACK for now +// + db = &s->dma_dac; + + if (cs4x_pgoff(vma) != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range + (vma, vma->vm_start, virt_to_phys(db->rawbuf), size, + vma->vm_page_prot)) return -EAGAIN; + db->mapped = 1; + + CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_mmap()- 0 size=%d\n", + (unsigned) size)); + + return 0; +} + + +static int cs4281_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs4281: cs4281_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 + "cs4281: cs4281_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 + "cs4281: cs4281_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 + "cs4281: cs4281_ioctl(): DSP_RESET\n")); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = + s->dma_dac.count = s->dma_dac.total_bytes = + s->dma_dac.blocks = s->dma_dac.wakeup = 0; + prog_codec(s, CS_TYPE_DAC); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = + s->dma_adc.count = s->dma_adc.total_bytes = + s->dma_adc.blocks = s->dma_dac.wakeup = 0; + prog_codec(s, CS_TYPE_ADC); + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SPEED val=%d\n", val)); + // + // support independent capture and playback channels + // assume that the file mode bit determines the + // direction of the data flow. + // + if (file->f_mode & FMODE_READ) { + if (val >= 0) { + stop_adc(s); + s->dma_adc.ready = 0; + // program sampling rates + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + s->prop_adc.rate = val; + prog_codec(s, CS_TYPE_ADC); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val >= 0) { + stop_dac(s); + s->dma_dac.ready = 0; + // program sampling rates + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + s->prop_dac.rate = val; + prog_codec(s, CS_TYPE_DAC); + } + } + + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.rate; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.rate; + + 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 + "cs4281: cs4281_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; + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + s->prop_dac.channels = val ? 2 : 1; + prog_codec(s, CS_TYPE_DAC); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_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; + prog_codec(s, CS_TYPE_ADC); + } + 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; + prog_codec(s, CS_TYPE_DAC); + } + } + + 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 + "cs4281: cs4281_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 + "cs4281: cs4281_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; + prog_codec(s, CS_TYPE_ADC); + } + 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; + prog_codec(s, CS_TYPE_DAC); + } + } 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 + "cs4281: cs4281_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 + "cs4281: cs4281_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); + cs4281_update_ptr(s,CS_FALSE); + abinfo.fragsize = s->dma_dac.fragsize; + if (s->dma_dac.mapped) + abinfo.bytes = s->dma_dac.dmasize; + else + abinfo.bytes = + s->dma_dac.dmasize - 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 + "cs4281: cs4281_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); + cs4281_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); + cs4281_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); + cs4281_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); + cs4281_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 cs4281_release(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO + "cs4281: cs4281_release(): inode=0x%.8x file=0x%.8x f_mode=%d\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 cs4281_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct cs4281_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n", + (unsigned) inode, (unsigned) file, file->f_mode)); + + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + if (entry == &cs4281_devs) + return -ENODEV; + if (!s) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_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 + "cs4281: cs4281_open(): Error - must open READ and/or WRITE\n")); + return -ENODEV; + } + if (file->f_mode & FMODE_WRITE) { + 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)) + 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)) + 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_U8; + s->prop_adc.fmt_original = s->prop_adc.fmt; + s->prop_adc.channels = 1; + s->prop_adc.rate = 8000; + s->prop_adc.clkdiv = 96 | 0x80; + 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 + "cs4281: adc Program dmabufs failed.\n")); + cs4281_release(inode, file); + return -ENOMEM; + } + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + s->prop_dac.fmt = AFMT_U8; + s->prop_dac.fmt_original = s->prop_dac.fmt; + s->prop_dac.channels = 1; + s->prop_dac.rate = 8000; + s->prop_dac.clkdiv = 96 | 0x80; + 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 + "cs4281: dac Program dmabufs failed.\n")); + cs4281_release(inode, file); + return -ENOMEM; + } + prog_codec(s, CS_TYPE_DAC); + } + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, + printk(KERN_INFO "cs4281: cs4281_open()- 0\n")); + return 0; +} + + +// ****************************************************************************************** +// Wave (audio) file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_audio_fops = { + llseek:no_llseek, + read:cs4281_read, + write:cs4281_write, + poll:cs4281_poll, + ioctl:cs4281_ioctl, + mmap:cs4281_mmap, + open:cs4281_open, + release:cs4281_release, +}; + +// --------------------------------------------------------------------- + +// hold spinlock for the following! +static void cs4281_handle_midi(struct cs4281_state *s) +{ + unsigned char ch; + int wake; + unsigned temp1; + + wake = 0; + while (!(readl(s->pBA0 + BA0_MIDSR) & 0x80)) { + ch = readl(s->pBA0 + BA0_MIDRP); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(readl(s->pBA0 + BA0_MIDSR) & 0x40) && s->midi.ocnt > 0) { + temp1 = (s->midi.obuf[s->midi.ord]) & 0x000000ff; + writel(temp1, s->pBA0 + BA0_MIDWP); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF - 16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + + + +static void cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cs4281_state *s = (struct cs4281_state *) dev_id; + unsigned int temp1; + + // fastpath out, to ease interrupt sharing + temp1 = readl(s->pBA0 + BA0_HISR); // Get Int Status reg. + + CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO + "cs4281: cs4281_interrupt() BA0_HISR=0x%.8x\n", temp1)); +/* +* If not DMA or MIDI interrupt, then just return. +*/ + if (!(temp1 & (HISR_DMA0 | HISR_DMA1 | HISR_MIDI))) { + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); + CS_DBGOUT(CS_INTERRUPT, 9, printk(KERN_INFO + "cs4281: cs4281_interrupt(): returning not cs4281 interrupt.\n")); + return; + } + + if (temp1 & HISR_DMA0) // If play interrupt, + readl(s->pBA0 + BA0_HDSR0); // clear the source. + + if (temp1 & HISR_DMA1) // Same for play. + readl(s->pBA0 + BA0_HDSR1); + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Local EOI + + spin_lock(&s->lock); + cs4281_update_ptr(s,CS_TRUE); + cs4281_handle_midi(s); + spin_unlock(&s->lock); +} + +// ************************************************************************** + +static void cs4281_midi_timer(unsigned long data) +{ + struct cs4281_state *s = (struct cs4281_state *) data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies + 1; + add_timer(&s->midi.timer); +} + + +// --------------------------------------------------------------------- + +static ssize_t cs4281_midi_read(struct file *file, char *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + + +static ssize_t cs4281_midi_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + + +static unsigned int cs4281_midi_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + + +static int cs4281_midi_open(struct inode *inode, struct file *file) +{ + unsigned long flags, temp1; + unsigned int minor = minor(inode->i_rdev); + struct cs4281_state *s=NULL; + struct list_head *entry; + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + + if (s->dev_midi == minor) + break; + } + + if (entry == &cs4281_devs) + return -ENODEV; + if (!s) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): Error - unable to find audio state struct\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + // wait for device to become free + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + writel(1, s->pBA0 + BA0_MIDCR); // Reset the interface. + writel(0, s->pBA0 + BA0_MIDCR); // Return to normal mode. + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + writel(0x0000000f, s->pBA0 + BA0_MIDCR); // Enable transmit, record, ints. + temp1 = readl(s->pBA0 + BA0_HIMR); + writel(temp1 & 0xffbfffff, s->pBA0 + BA0_HIMR); // Enable midi int. recognition. + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies + 1; + s->midi.timer.data = (unsigned long) s; + s->midi.timer.function = cs4281_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= + (file-> + f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | + FMODE_MIDI_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + + +static int cs4281_midi_release(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG + "cs4281: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&s->open_sem); + s->open_mode &= + (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ | + FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + writel(0, s->pBA0 + BA0_MIDCR); // Disable Midi interrupts. + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +// ****************************************************************************************** +// Midi file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_midi_fops = { + llseek:no_llseek, + read:cs4281_midi_read, + write:cs4281_midi_write, + poll:cs4281_midi_poll, + open:cs4281_midi_open, + release:cs4281_midi_release, +}; + + +// --------------------------------------------------------------------- + +// maximum number of devices +#define NR_DEVICE 8 // Only eight devices supported currently. + +// --------------------------------------------------------------------- + +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} +}; + + +#ifndef NOT_CS4281_PM +void __devinit cs4281_BuildFIFO( + struct cs4281_pipeline *p, + struct cs4281_state *s) +{ + switch(p->number) + { + case 0: /* playback */ + { + p->u32FCRnAddress = BA0_FCR0; + p->u32FSICnAddress = BA0_FSIC0; + p->u32FPDRnAddress = BA0_FPDR0; + break; + } + case 1: /* capture */ + { + p->u32FCRnAddress = BA0_FCR1; + p->u32FSICnAddress = BA0_FSIC1; + p->u32FPDRnAddress = BA0_FPDR1; + break; + } + + case 2: + { + p->u32FCRnAddress = BA0_FCR2; + p->u32FSICnAddress = BA0_FSIC2; + p->u32FPDRnAddress = BA0_FPDR2; + break; + } + case 3: + { + p->u32FCRnAddress = BA0_FCR3; + p->u32FSICnAddress = BA0_FSIC3; + p->u32FPDRnAddress = BA0_FPDR3; + break; + } + default: + break; + } + // + // first read the hardware to initialize the member variables + // + p->u32FCRnValue = readl(s->pBA0 + p->u32FCRnAddress); + p->u32FSICnValue = readl(s->pBA0 + p->u32FSICnAddress); + p->u32FPDRnValue = readl(s->pBA0 + p->u32FPDRnAddress); + +} + +void __devinit cs4281_BuildDMAengine( + struct cs4281_pipeline *p, + struct cs4281_state *s) +{ +/* +* initialize all the addresses of this pipeline dma info. +*/ + switch(p->number) + { + case 0: /* playback */ + { + p->u32DBAnAddress = BA0_DBA0; + p->u32DCAnAddress = BA0_DCA0; + p->u32DBCnAddress = BA0_DBC0; + p->u32DCCnAddress = BA0_DCC0; + p->u32DMRnAddress = BA0_DMR0; + p->u32DCRnAddress = BA0_DCR0; + p->u32HDSRnAddress = BA0_HDSR0; + break; + } + + case 1: /* capture */ + { + p->u32DBAnAddress = BA0_DBA1; + p->u32DCAnAddress = BA0_DCA1; + p->u32DBCnAddress = BA0_DBC1; + p->u32DCCnAddress = BA0_DCC1; + p->u32DMRnAddress = BA0_DMR1; + p->u32DCRnAddress = BA0_DCR1; + p->u32HDSRnAddress = BA0_HDSR1; + break; + } + + case 2: + { + p->u32DBAnAddress = BA0_DBA2; + p->u32DCAnAddress = BA0_DCA2; + p->u32DBCnAddress = BA0_DBC2; + p->u32DCCnAddress = BA0_DCC2; + p->u32DMRnAddress = BA0_DMR2; + p->u32DCRnAddress = BA0_DCR2; + p->u32HDSRnAddress = BA0_HDSR2; + break; + } + + case 3: + { + p->u32DBAnAddress = BA0_DBA3; + p->u32DCAnAddress = BA0_DCA3; + p->u32DBCnAddress = BA0_DBC3; + p->u32DCCnAddress = BA0_DCC3; + p->u32DMRnAddress = BA0_DMR3; + p->u32DCRnAddress = BA0_DCR3; + p->u32HDSRnAddress = BA0_HDSR3; + break; + } + default: + break; + } + +// +// Initialize the dma values for this pipeline +// + p->u32DBAnValue = readl(s->pBA0 + p->u32DBAnAddress); + p->u32DBCnValue = readl(s->pBA0 + p->u32DBCnAddress); + p->u32DMRnValue = readl(s->pBA0 + p->u32DMRnAddress); + p->u32DCRnValue = readl(s->pBA0 + p->u32DCRnAddress); + +} + +void __devinit cs4281_InitPM(struct cs4281_state *s) +{ + int i; + struct cs4281_pipeline *p; + + for(i=0;ipl[i]; + p->number = i; + cs4281_BuildDMAengine(p,s); + cs4281_BuildFIFO(p,s); + /* + * currently only 2 pipelines are used + * so, only set the valid bit on the playback and capture. + */ + if( (i == CS4281_PLAYBACK_PIPELINE_NUMBER) || + (i == CS4281_CAPTURE_PIPELINE_NUMBER)) + p->flags |= CS4281_PIPELINE_VALID; + } + s->pm.u32SSPM_BITS = 0x7e; /* rev c, use 0x7c for rev a or b */ +} +#endif + +static int __devinit cs4281_probe(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ +#ifndef NOT_CS4281_PM + struct pm_dev *pmdev; +#endif + struct cs4281_state *s; + dma_addr_t dma_mask; + mm_segment_t fs; + int i, val; + unsigned int temp1, temp2; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, + printk(KERN_INFO "cs4281: probe()+\n")); + + if (pci_enable_device(pcidev)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: pci_enable_device() failed\n")); + return -1; + } + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM) || + !(pci_resource_flags(pcidev, 1) & IORESOURCE_MEM)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe()- Memory region not assigned\n")); + return -ENODEV; + } + if (pcidev->irq == 0) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() IRQ not assigned\n")); + return -ENODEV; + } + dma_mask = 0xffffffff; /* this enables playback and recording */ + i = pci_set_dma_mask(pcidev, dma_mask); + if (i) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() architecture does not support 32bit PCI busmaster DMA\n")); + return i; + } + if (!(s = kmalloc(sizeof(struct cs4281_state), GFP_KERNEL))) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() no memory for state struct.\n")); + return -1; + } + memset(s, 0, sizeof(struct cs4281_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->open_wait_adc); + init_waitqueue_head(&s->open_wait_dac); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + init_MUTEX(&s->open_sem_adc); + init_MUTEX(&s->open_sem_dac); + spin_lock_init(&s->lock); + s->pBA0phys = pci_resource_start(pcidev, 0); + s->pBA1phys = pci_resource_start(pcidev, 1); + + /* Convert phys to linear. */ + s->pBA0 = ioremap_nocache(s->pBA0phys, 4096); + if (!s->pBA0) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: BA0 I/O mapping failed. Skipping part.\n")); + goto err_free; + } + s->pBA1 = ioremap_nocache(s->pBA1phys, 65536); + if (!s->pBA1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: BA1 I/O mapping failed. Skipping part.\n")); + goto err_unmap; + } + + temp1 = readl(s->pBA0 + BA0_PCICFG00); + temp2 = readl(s->pBA0 + BA0_PCICFG04); + + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO + "cs4281: probe() BA0=0x%.8x BA1=0x%.8x pBA0=0x%.8x pBA1=0x%.8x \n", + (unsigned) temp1, (unsigned) temp2, + (unsigned) s->pBA0, (unsigned) s->pBA1)); + + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO + "cs4281: probe() pBA0phys=0x%.8x pBA1phys=0x%.8x\n", + (unsigned) s->pBA0phys, (unsigned) s->pBA1phys)); + +#ifndef NOT_CS4281_PM + s->pm.flags = CS4281_PM_IDLE; +#endif + temp1 = cs4281_hw_init(s); + if (temp1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: cs4281_hw_init() failed. Skipping part.\n")); + goto err_irq; + } + s->magic = CS4281_MAGIC; + s->pcidev = pcidev; + s->irq = pcidev->irq; + if (request_irq + (s->irq, cs4281_interrupt, SA_SHIRQ, "Crystal CS4281", s)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, + printk(KERN_ERR "cs4281: irq %u in use\n", s->irq)); + goto err_irq; + } + if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_dsp() failed.\n")); + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_mixer() failed.\n")); + goto err_dev2; + } + if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1)) < 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_midi() failed.\n")); + goto err_dev3; + } +#ifndef NOT_CS4281_PM + cs4281_InitPM(s); + pmdev = cs_pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), cs4281_pm_callback); + if (pmdev) + { + CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO + "cs4281: probe() pm_register() succeeded (0x%x).\n", + (unsigned)pmdev)); + pmdev->data = s; + } + else + { + CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 0, printk(KERN_INFO + "cs4281: probe() pm_register() failed (0x%x).\n", + (unsigned)pmdev)); + s->pm.flags |= CS4281_PM_NOT_REGISTERED; + } +#endif + + pci_set_master(pcidev); // enable bus mastering + + fs = get_fs(); + set_fs(KERNEL_DS); + 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); + } + val = 1; // enable mic preamp + mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long) &val); + set_fs(fs); + + pci_set_drvdata(pcidev, s); + list_add(&s->list, &cs4281_devs); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: probe()- device allocated successfully\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: + iounmap(s->pBA1); + err_unmap: + iounmap(s->pBA0); + err_free: + kfree(s); + + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: probe()- no device allocated\n")); + return -ENODEV; +} // probe_cs4281 + + +// --------------------------------------------------------------------- + +static void __devinit cs4281_remove(struct pci_dev *pci_dev) +{ + struct cs4281_state *s = pci_get_drvdata(pci_dev); + // stop DMA controller + synchronize_irq(); + free_irq(s->irq, s); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + iounmap(s->pBA1); + iounmap(s->pBA0); + pci_set_drvdata(pci_dev,NULL); + list_del(&s->list); + kfree(s); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs4281_remove()-: remove successful\n")); +} + +static struct pci_device_id cs4281_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CRYSTAL_CS4281, + PCI_ANY_ID, PCI_ANY_ID, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, cs4281_pci_tbl); + +struct pci_driver cs4281_pci_driver = { + name:"cs4281", + id_table:cs4281_pci_tbl, + probe:cs4281_probe, + remove:cs4281_remove, + suspend:CS4281_SUSPEND_TBL, + resume:CS4281_RESUME_TBL, +}; + +int __init cs4281_init_module(void) +{ + int rtn = 0; + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs4281_init_module()+ \n")); + if (!pci_present()) { /* No PCI bus in this machine! */ + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs4281_init_module()- no pci bus found\n")); + return -ENODEV; + } + printk(KERN_INFO "cs4281: version v%d.%02d.%d time " __TIME__ " " + __DATE__ "\n", CS4281_MAJOR_VERSION, CS4281_MINOR_VERSION, + CS4281_ARCH); + rtn = pci_module_init(&cs4281_pci_driver); + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_init_module()- (%d)\n",rtn)); + return rtn; +} + +void __exit cs4281_cleanup_module(void) +{ + pci_unregister_driver(&cs4281_pci_driver); +#ifndef NOT_CS4281_PM + cs_pm_unregister_all(cs4281_pm_callback); +#endif + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cleanup_cs4281() finished\n")); +} +// --------------------------------------------------------------------- + +MODULE_AUTHOR("gw boynton, audio@crystal.cirrus.com"); +MODULE_DESCRIPTION("Cirrus Logic CS4281 Driver"); +MODULE_LICENSE("GPL"); + +// --------------------------------------------------------------------- + +module_init(cs4281_init_module); +module_exit(cs4281_cleanup_module); + +#ifndef MODULE +int __init init_cs4281(void) +{ + return cs4281_init_module(); +} +#endif diff -Nru a/sound/oss/cs4281/cs4281pm-24.c b/sound/oss/cs4281/cs4281pm-24.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs4281/cs4281pm-24.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,84 @@ +/******************************************************************************* +* +* "cs4281pm.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.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. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ + +#ifndef NOT_CS4281_PM +#include + +#define cs_pm_register(a, b, c) pm_register((a), (b), (c)); +#define cs_pm_unregister_all(a) pm_unregister_all((a)); + +int cs4281_suspend(struct cs4281_state *s); +int cs4281_resume(struct cs4281_state *s); +/* +* for now (12/22/00) only enable the pm_register PM support. +* allow these table entries to be null. +#define CS4281_SUSPEND_TBL cs4281_suspend_tbl +#define CS4281_RESUME_TBL cs4281_resume_tbl +*/ +#define CS4281_SUSPEND_TBL cs4281_null +#define CS4281_RESUME_TBL cs4281_null + +int cs4281_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct cs4281_state *state; + + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs4281: cs4281_pm_callback dev=0x%x rqst=0x%x state=%d\n", + (unsigned)dev,(unsigned)rqst,(unsigned)data)); + state = (struct cs4281_state *) dev->data; + if (state) { + switch(rqst) { + case PM_SUSPEND: + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs4281: PM suspend request\n")); + if(cs4281_suspend(state)) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: PM suspend request refused\n")); + return 1; + } + break; + case PM_RESUME: + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs4281: PM resume request\n")); + if(cs4281_resume(state)) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: PM resume request refused\n")); + return 1; + } + break; + } + } + + return 0; +} + +#else /* CS4281_PM */ +#define CS4281_SUSPEND_TBL cs4281_null +#define CS4281_RESUME_TBL cs4281_null +#endif /* CS4281_PM */ + diff -Nru a/sound/oss/cs4281/cs4281pm.h b/sound/oss/cs4281/cs4281pm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs4281/cs4281pm.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,74 @@ +#ifndef NOT_CS4281_PM +/******************************************************************************* +* +* "cs4281pm.h" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.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. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ +/* general pm definitions */ +#define CS4281_AC97_HIGHESTREGTORESTORE 0x26 +#define CS4281_AC97_NUMBER_RESTORE_REGS (CS4281_AC97_HIGHESTREGTORESTORE/2-1) + +/* pipeline definitions */ +#define CS4281_NUMBER_OF_PIPELINES 4 +#define CS4281_PIPELINE_VALID 0x0001 +#define CS4281_PLAYBACK_PIPELINE_NUMBER 0x0000 +#define CS4281_CAPTURE_PIPELINE_NUMBER 0x0001 + +/* PM state defintions */ +#define CS4281_PM_NOT_REGISTERED 0x1000 +#define CS4281_PM_IDLE 0x0001 +#define CS4281_PM_SUSPENDING 0x0002 +#define CS4281_PM_SUSPENDED 0x0004 +#define CS4281_PM_RESUMING 0x0008 +#define CS4281_PM_RESUMED 0x0010 + +struct cs4281_pm { + unsigned long flags; + u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue; + u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR; + u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save; + u32 u32SSPM_BITS; + u32 ac97[CS4281_AC97_NUMBER_RESTORE_REGS]; + u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono; + u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose; + u32 u32hwptr_playback,u32hwptr_capture; +}; + +struct cs4281_pipeline { + unsigned flags; + unsigned number; + u32 u32DBAnValue,u32DBCnValue,u32DMRnValue,u32DCRnValue; + u32 u32DBAnAddress,u32DCAnAddress,u32DBCnAddress,u32DCCnAddress; + u32 u32DMRnAddress,u32DCRnAddress,u32HDSRnAddress; + u32 u32DBAn_Save,u32DBCn_Save,u32DMRn_Save,u32DCRn_Save; + u32 u32DCCn_Save,u32DCAn_Save; +/* +* technically, these are fifo variables, but just map the +* first fifo with the first pipeline and then use the fifo +* variables inside of the pipeline struct. +*/ + u32 u32FCRn_Save,u32FSICn_Save; + u32 u32FCRnValue,u32FCRnAddress,u32FSICnValue,u32FSICnAddress; + u32 u32FPDRnValue,u32FPDRnAddress; +}; +#endif diff -Nru a/sound/oss/cs461x.h b/sound/oss/cs461x.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs461x.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,1691 @@ +#ifndef __CS461X_H +#define __CS461X_H + +/* + * Copyright (c) by Cirrus Logic Corporation + * Copyright (c) by Jaroslav Kysela + * Definitions for Cirrus Logic CS461x chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4610 +#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4612 +#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4615 +#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 +#endif + +/* + * Direct registers + */ + +/* + * The following define the offsets of the registers accessed via base address + * register zero on the CS461x part. + */ +#define BA0_HISR 0x00000000 +#define BA0_HSR0 0x00000004 +#define BA0_HICR 0x00000008 +#define BA0_DMSR 0x00000100 +#define BA0_HSAR 0x00000110 +#define BA0_HDAR 0x00000114 +#define BA0_HDMR 0x00000118 +#define BA0_HDCR 0x0000011C +#define BA0_PFMC 0x00000200 +#define BA0_PFCV1 0x00000204 +#define BA0_PFCV2 0x00000208 +#define BA0_PCICFG00 0x00000300 +#define BA0_PCICFG04 0x00000304 +#define BA0_PCICFG08 0x00000308 +#define BA0_PCICFG0C 0x0000030C +#define BA0_PCICFG10 0x00000310 +#define BA0_PCICFG14 0x00000314 +#define BA0_PCICFG18 0x00000318 +#define BA0_PCICFG1C 0x0000031C +#define BA0_PCICFG20 0x00000320 +#define BA0_PCICFG24 0x00000324 +#define BA0_PCICFG28 0x00000328 +#define BA0_PCICFG2C 0x0000032C +#define BA0_PCICFG30 0x00000330 +#define BA0_PCICFG34 0x00000334 +#define BA0_PCICFG38 0x00000338 +#define BA0_PCICFG3C 0x0000033C +#define BA0_CLKCR1 0x00000400 +#define BA0_CLKCR2 0x00000404 +#define BA0_PLLM 0x00000408 +#define BA0_PLLCC 0x0000040C +#define BA0_FRR 0x00000410 +#define BA0_CFL1 0x00000414 +#define BA0_CFL2 0x00000418 +#define BA0_SERMC1 0x00000420 +#define BA0_SERMC2 0x00000424 +#define BA0_SERC1 0x00000428 +#define BA0_SERC2 0x0000042C +#define BA0_SERC3 0x00000430 +#define BA0_SERC4 0x00000434 +#define BA0_SERC5 0x00000438 +#define BA0_SERBSP 0x0000043C +#define BA0_SERBST 0x00000440 +#define BA0_SERBCM 0x00000444 +#define BA0_SERBAD 0x00000448 +#define BA0_SERBCF 0x0000044C +#define BA0_SERBWP 0x00000450 +#define BA0_SERBRP 0x00000454 +#ifndef NO_CS4612 +#define BA0_ASER_FADDR 0x00000458 +#endif +#define BA0_ACCTL 0x00000460 +#define BA0_ACSTS 0x00000464 +#define BA0_ACOSV 0x00000468 +#define BA0_ACCAD 0x0000046C +#define BA0_ACCDA 0x00000470 +#define BA0_ACISV 0x00000474 +#define BA0_ACSAD 0x00000478 +#define BA0_ACSDA 0x0000047C +#define BA0_JSPT 0x00000480 +#define BA0_JSCTL 0x00000484 +#define BA0_JSC1 0x00000488 +#define BA0_JSC2 0x0000048C +#define BA0_MIDCR 0x00000490 +#define BA0_MIDSR 0x00000494 +#define BA0_MIDWP 0x00000498 +#define BA0_MIDRP 0x0000049C +#define BA0_JSIO 0x000004A0 +#ifndef NO_CS4612 +#define BA0_ASER_MASTER 0x000004A4 +#endif +#define BA0_CFGI 0x000004B0 +#define BA0_SSVID 0x000004B4 +#define BA0_GPIOR 0x000004B8 +#ifndef NO_CS4612 +#define BA0_EGPIODR 0x000004BC +#define BA0_EGPIOPTR 0x000004C0 +#define BA0_EGPIOTR 0x000004C4 +#define BA0_EGPIOWR 0x000004C8 +#define BA0_EGPIOSR 0x000004CC +#define BA0_SERC6 0x000004D0 +#define BA0_SERC7 0x000004D4 +#define BA0_SERACC 0x000004D8 +#define BA0_ACCTL2 0x000004E0 +#define BA0_ACSTS2 0x000004E4 +#define BA0_ACOSV2 0x000004E8 +#define BA0_ACCAD2 0x000004EC +#define BA0_ACCDA2 0x000004F0 +#define BA0_ACISV2 0x000004F4 +#define BA0_ACSAD2 0x000004F8 +#define BA0_ACSDA2 0x000004FC +#define BA0_IOTAC0 0x00000500 +#define BA0_IOTAC1 0x00000504 +#define BA0_IOTAC2 0x00000508 +#define BA0_IOTAC3 0x0000050C +#define BA0_IOTAC4 0x00000510 +#define BA0_IOTAC5 0x00000514 +#define BA0_IOTAC6 0x00000518 +#define BA0_IOTAC7 0x0000051C +#define BA0_IOTAC8 0x00000520 +#define BA0_IOTAC9 0x00000524 +#define BA0_IOTAC10 0x00000528 +#define BA0_IOTAC11 0x0000052C +#define BA0_IOTFR0 0x00000540 +#define BA0_IOTFR1 0x00000544 +#define BA0_IOTFR2 0x00000548 +#define BA0_IOTFR3 0x0000054C +#define BA0_IOTFR4 0x00000550 +#define BA0_IOTFR5 0x00000554 +#define BA0_IOTFR6 0x00000558 +#define BA0_IOTFR7 0x0000055C +#define BA0_IOTFIFO 0x00000580 +#define BA0_IOTRRD 0x00000584 +#define BA0_IOTFP 0x00000588 +#define BA0_IOTCR 0x0000058C +#define BA0_DPCID 0x00000590 +#define BA0_DPCIA 0x00000594 +#define BA0_DPCIC 0x00000598 +#define BA0_PCPCIR 0x00000600 +#define BA0_PCPCIG 0x00000604 +#define BA0_PCPCIEN 0x00000608 +#define BA0_EPCIPMC 0x00000610 +#endif + +/* + * The following define the offsets of the registers and memories accessed via + * base address register one on the CS461x part. + */ +#define BA1_SP_DMEM0 0x00000000 +#define BA1_SP_DMEM1 0x00010000 +#define BA1_SP_PMEM 0x00020000 +#define BA1_SP_REG 0x00030000 +#define BA1_SPCR 0x00030000 +#define BA1_DREG 0x00030004 +#define BA1_DSRWP 0x00030008 +#define BA1_TWPR 0x0003000C +#define BA1_SPWR 0x00030010 +#define BA1_SPIR 0x00030014 +#define BA1_FGR1 0x00030020 +#define BA1_SPCS 0x00030028 +#define BA1_SDSR 0x0003002C +#define BA1_FRMT 0x00030030 +#define BA1_FRCC 0x00030034 +#define BA1_FRSC 0x00030038 +#define BA1_OMNI_MEM 0x000E0000 + +/* + * The following defines are for the flags in the host interrupt status + * register. + */ +#define HISR_VC_MASK 0x0000FFFF +#define HISR_VC0 0x00000001 +#define HISR_VC1 0x00000002 +#define HISR_VC2 0x00000004 +#define HISR_VC3 0x00000008 +#define HISR_VC4 0x00000010 +#define HISR_VC5 0x00000020 +#define HISR_VC6 0x00000040 +#define HISR_VC7 0x00000080 +#define HISR_VC8 0x00000100 +#define HISR_VC9 0x00000200 +#define HISR_VC10 0x00000400 +#define HISR_VC11 0x00000800 +#define HISR_VC12 0x00001000 +#define HISR_VC13 0x00002000 +#define HISR_VC14 0x00004000 +#define HISR_VC15 0x00008000 +#define HISR_INT0 0x00010000 +#define HISR_INT1 0x00020000 +#define HISR_DMAI 0x00040000 +#define HISR_FROVR 0x00080000 +#define HISR_MIDI 0x00100000 +#ifdef NO_CS4612 +#define HISR_RESERVED 0x0FE00000 +#else +#define HISR_SBINT 0x00200000 +#define HISR_RESERVED 0x0FC00000 +#endif +#define HISR_H0P 0x40000000 +#define HISR_INTENA 0x80000000 + +/* + * The following defines are for the flags in the host signal register 0. + */ +#define HSR0_VC_MASK 0xFFFFFFFF +#define HSR0_VC16 0x00000001 +#define HSR0_VC17 0x00000002 +#define HSR0_VC18 0x00000004 +#define HSR0_VC19 0x00000008 +#define HSR0_VC20 0x00000010 +#define HSR0_VC21 0x00000020 +#define HSR0_VC22 0x00000040 +#define HSR0_VC23 0x00000080 +#define HSR0_VC24 0x00000100 +#define HSR0_VC25 0x00000200 +#define HSR0_VC26 0x00000400 +#define HSR0_VC27 0x00000800 +#define HSR0_VC28 0x00001000 +#define HSR0_VC29 0x00002000 +#define HSR0_VC30 0x00004000 +#define HSR0_VC31 0x00008000 +#define HSR0_VC32 0x00010000 +#define HSR0_VC33 0x00020000 +#define HSR0_VC34 0x00040000 +#define HSR0_VC35 0x00080000 +#define HSR0_VC36 0x00100000 +#define HSR0_VC37 0x00200000 +#define HSR0_VC38 0x00400000 +#define HSR0_VC39 0x00800000 +#define HSR0_VC40 0x01000000 +#define HSR0_VC41 0x02000000 +#define HSR0_VC42 0x04000000 +#define HSR0_VC43 0x08000000 +#define HSR0_VC44 0x10000000 +#define HSR0_VC45 0x20000000 +#define HSR0_VC46 0x40000000 +#define HSR0_VC47 0x80000000 + +/* + * The following defines are for the flags in the host interrupt control + * register. + */ +#define HICR_IEV 0x00000001 +#define HICR_CHGM 0x00000002 + +/* + * The following defines are for the flags in the DMA status register. + */ +#define DMSR_HP 0x00000001 +#define DMSR_HR 0x00000002 +#define DMSR_SP 0x00000004 +#define DMSR_SR 0x00000008 + +/* + * The following defines are for the flags in the host DMA source address + * register. + */ +#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HSAR_DSP_ADDR_MASK 0x0000FFFF +#define HSAR_MEMID_MASK 0x000F0000 +#define HSAR_MEMID_SP_DMEM0 0x00000000 +#define HSAR_MEMID_SP_DMEM1 0x00010000 +#define HSAR_MEMID_SP_PMEM 0x00020000 +#define HSAR_MEMID_SP_DEBUG 0x00030000 +#define HSAR_MEMID_OMNI_MEM 0x000E0000 +#define HSAR_END 0x40000000 +#define HSAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA destination address + * register. + */ +#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HDAR_DSP_ADDR_MASK 0x0000FFFF +#define HDAR_MEMID_MASK 0x000F0000 +#define HDAR_MEMID_SP_DMEM0 0x00000000 +#define HDAR_MEMID_SP_DMEM1 0x00010000 +#define HDAR_MEMID_SP_PMEM 0x00020000 +#define HDAR_MEMID_SP_DEBUG 0x00030000 +#define HDAR_MEMID_OMNI_MEM 0x000E0000 +#define HDAR_END 0x40000000 +#define HDAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDMR_AC_MASK 0x0000F000 +#define HDMR_AC_8_16 0x00001000 +#define HDMR_AC_M_S 0x00002000 +#define HDMR_AC_B_L 0x00004000 +#define HDMR_AC_S_U 0x00008000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDCR_COUNT_MASK 0x000003FF +#define HDCR_DONE 0x00004000 +#define HDCR_OPT 0x00008000 +#define HDCR_WBD 0x00400000 +#define HDCR_WBS 0x00800000 +#define HDCR_DMS_MASK 0x07000000 +#define HDCR_DMS_LINEAR 0x00000000 +#define HDCR_DMS_16_DWORDS 0x01000000 +#define HDCR_DMS_32_DWORDS 0x02000000 +#define HDCR_DMS_64_DWORDS 0x03000000 +#define HDCR_DMS_128_DWORDS 0x04000000 +#define HDCR_DMS_256_DWORDS 0x05000000 +#define HDCR_DMS_512_DWORDS 0x06000000 +#define HDCR_DMS_1024_DWORDS 0x07000000 +#define HDCR_DH 0x08000000 +#define HDCR_SMS_MASK 0x70000000 +#define HDCR_SMS_LINEAR 0x00000000 +#define HDCR_SMS_16_DWORDS 0x10000000 +#define HDCR_SMS_32_DWORDS 0x20000000 +#define HDCR_SMS_64_DWORDS 0x30000000 +#define HDCR_SMS_128_DWORDS 0x40000000 +#define HDCR_SMS_256_DWORDS 0x50000000 +#define HDCR_SMS_512_DWORDS 0x60000000 +#define HDCR_SMS_1024_DWORDS 0x70000000 +#define HDCR_SH 0x80000000 +#define HDCR_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the performance monitor control + * register. + */ +#define PFMC_C1SS_MASK 0x0000001F +#define PFMC_C1EV 0x00000020 +#define PFMC_C1RS 0x00008000 +#define PFMC_C2SS_MASK 0x001F0000 +#define PFMC_C2EV 0x00200000 +#define PFMC_C2RS 0x80000000 +#define PFMC_C1SS_SHIFT 0 +#define PFMC_C2SS_SHIFT 16 +#define PFMC_BUS_GRANT 0 +#define PFMC_GRANT_AFTER_REQ 1 +#define PFMC_TRANSACTION 2 +#define PFMC_DWORD_TRANSFER 3 +#define PFMC_SLAVE_READ 4 +#define PFMC_SLAVE_WRITE 5 +#define PFMC_PREEMPTION 6 +#define PFMC_DISCONNECT_RETRY 7 +#define PFMC_INTERRUPT 8 +#define PFMC_BUS_OWNERSHIP 9 +#define PFMC_TRANSACTION_LAG 10 +#define PFMC_PCI_CLOCK 11 +#define PFMC_SERIAL_CLOCK 12 +#define PFMC_SP_CLOCK 13 + +/* + * The following defines are for the flags in the performance counter value 1 + * register. + */ +#define PFCV1_PC1V_MASK 0xFFFFFFFF +#define PFCV1_PC1V_SHIFT 0 + +/* + * The following defines are for the flags in the performance counter value 2 + * register. + */ +#define PFCV2_PC2V_MASK 0xFFFFFFFF +#define PFCV2_PC2V_SHIFT 0 + +/* + * The following defines are for the flags in the clock control register 1. + */ +#define CLKCR1_OSCS 0x00000001 +#define CLKCR1_OSCP 0x00000002 +#define CLKCR1_PLLSS_MASK 0x0000000C +#define CLKCR1_PLLSS_SERIAL 0x00000000 +#define CLKCR1_PLLSS_CRYSTAL 0x00000004 +#define CLKCR1_PLLSS_PCI 0x00000008 +#define CLKCR1_PLLSS_RESERVED 0x0000000C +#define CLKCR1_PLLP 0x00000010 +#define CLKCR1_SWCE 0x00000020 +#define CLKCR1_PLLOS 0x00000040 + +/* + * The following defines are for the flags in the clock control register 2. + */ +#define CLKCR2_PDIVS_MASK 0x0000000F +#define CLKCR2_PDIVS_1 0x00000001 +#define CLKCR2_PDIVS_2 0x00000002 +#define CLKCR2_PDIVS_4 0x00000004 +#define CLKCR2_PDIVS_7 0x00000007 +#define CLKCR2_PDIVS_8 0x00000008 +#define CLKCR2_PDIVS_16 0x00000000 + +/* + * The following defines are for the flags in the PLL multiplier register. + */ +#define PLLM_MASK 0x000000FF +#define PLLM_SHIFT 0 + +/* + * The following defines are for the flags in the PLL capacitor coefficient + * register. + */ +#define PLLCC_CDR_MASK 0x00000007 +#ifndef NO_CS4610 +#define PLLCC_CDR_240_350_MHZ 0x00000000 +#define PLLCC_CDR_184_265_MHZ 0x00000001 +#define PLLCC_CDR_144_205_MHZ 0x00000002 +#define PLLCC_CDR_111_160_MHZ 0x00000003 +#define PLLCC_CDR_87_123_MHZ 0x00000004 +#define PLLCC_CDR_67_96_MHZ 0x00000005 +#define PLLCC_CDR_52_74_MHZ 0x00000006 +#define PLLCC_CDR_45_58_MHZ 0x00000007 +#endif +#ifndef NO_CS4612 +#define PLLCC_CDR_271_398_MHZ 0x00000000 +#define PLLCC_CDR_227_330_MHZ 0x00000001 +#define PLLCC_CDR_167_239_MHZ 0x00000002 +#define PLLCC_CDR_150_215_MHZ 0x00000003 +#define PLLCC_CDR_107_154_MHZ 0x00000004 +#define PLLCC_CDR_98_140_MHZ 0x00000005 +#define PLLCC_CDR_73_104_MHZ 0x00000006 +#define PLLCC_CDR_63_90_MHZ 0x00000007 +#endif +#define PLLCC_LPF_MASK 0x000000F8 +#ifndef NO_CS4610 +#define PLLCC_LPF_23850_60000_KHZ 0x00000000 +#define PLLCC_LPF_7960_26290_KHZ 0x00000008 +#define PLLCC_LPF_4160_10980_KHZ 0x00000018 +#define PLLCC_LPF_1740_4580_KHZ 0x00000038 +#define PLLCC_LPF_724_1910_KHZ 0x00000078 +#define PLLCC_LPF_317_798_KHZ 0x000000F8 +#endif +#ifndef NO_CS4612 +#define PLLCC_LPF_25580_64530_KHZ 0x00000000 +#define PLLCC_LPF_14360_37270_KHZ 0x00000008 +#define PLLCC_LPF_6100_16020_KHZ 0x00000018 +#define PLLCC_LPF_2540_6690_KHZ 0x00000038 +#define PLLCC_LPF_1050_2780_KHZ 0x00000078 +#define PLLCC_LPF_450_1160_KHZ 0x000000F8 +#endif + +/* + * The following defines are for the flags in the feature reporting register. + */ +#define FRR_FAB_MASK 0x00000003 +#define FRR_MASK_MASK 0x0000001C +#ifdef NO_CS4612 +#define FRR_CFOP_MASK 0x000000E0 +#else +#define FRR_CFOP_MASK 0x00000FE0 +#endif +#define FRR_CFOP_NOT_DVD 0x00000020 +#define FRR_CFOP_A3D 0x00000040 +#define FRR_CFOP_128_PIN 0x00000080 +#ifndef NO_CS4612 +#define FRR_CFOP_CS4280 0x00000800 +#endif +#define FRR_FAB_SHIFT 0 +#define FRR_MASK_SHIFT 2 +#define FRR_CFOP_SHIFT 5 + +/* + * The following defines are for the flags in the configuration load 1 + * register. + */ +#define CFL1_CLOCK_SOURCE_MASK 0x00000003 +#define CFL1_CLOCK_SOURCE_CS423X 0x00000000 +#define CFL1_CLOCK_SOURCE_AC97 0x00000001 +#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002 +#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003 +#define CFL1_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the configuration load 2 + * register. + */ +#define CFL2_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the serial port master control + * register 1. + */ +#define SERMC1_MSPE 0x00000001 +#define SERMC1_PTC_MASK 0x0000000E +#define SERMC1_PTC_CS423X 0x00000000 +#define SERMC1_PTC_AC97 0x00000002 +#define SERMC1_PTC_DAC 0x00000004 +#define SERMC1_PLB 0x00000010 +#define SERMC1_XLB 0x00000020 + +/* + * The following defines are for the flags in the serial port master control + * register 2. + */ +#define SERMC2_LROE 0x00000001 +#define SERMC2_MCOE 0x00000002 +#define SERMC2_MCDIV 0x00000004 + +/* + * The following defines are for the flags in the serial port 1 configuration + * register. + */ +#define SERC1_SO1EN 0x00000001 +#define SERC1_SO1F_MASK 0x0000000E +#define SERC1_SO1F_CS423X 0x00000000 +#define SERC1_SO1F_AC97 0x00000002 +#define SERC1_SO1F_DAC 0x00000004 +#define SERC1_SO1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 2 configuration + * register. + */ +#define SERC2_SI1EN 0x00000001 +#define SERC2_SI1F_MASK 0x0000000E +#define SERC2_SI1F_CS423X 0x00000000 +#define SERC2_SI1F_AC97 0x00000002 +#define SERC2_SI1F_ADC 0x00000004 +#define SERC2_SI1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 3 configuration + * register. + */ +#define SERC3_SO2EN 0x00000001 +#define SERC3_SO2F_MASK 0x00000006 +#define SERC3_SO2F_DAC 0x00000000 +#define SERC3_SO2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 4 configuration + * register. + */ +#define SERC4_SO3EN 0x00000001 +#define SERC4_SO3F_MASK 0x00000006 +#define SERC4_SO3F_DAC 0x00000000 +#define SERC4_SO3F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 5 configuration + * register. + */ +#define SERC5_SI2EN 0x00000001 +#define SERC5_SI2F_MASK 0x00000006 +#define SERC5_SI2F_ADC 0x00000000 +#define SERC5_SI2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor sample + * pointer register. + */ +#define SERBSP_FSP_MASK 0x0000000F +#define SERBSP_FSP_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor status + * register. + */ +#define SERBST_RRDY 0x00000001 +#define SERBST_WBSY 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor command + * register. + */ +#define SERBCM_RDC 0x00000001 +#define SERBCM_WRC 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor address + * register. + */ +#ifdef NO_CS4612 +#define SERBAD_FAD_MASK 0x000000FF +#else +#define SERBAD_FAD_MASK 0x000001FF +#endif +#define SERBAD_FAD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor + * configuration register. + */ +#define SERBCF_HBP 0x00000001 + +/* + * The following defines are for the flags in the serial port backdoor write + * port register. + */ +#define SERBWP_FWD_MASK 0x000FFFFF +#define SERBWP_FWD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor read + * port register. + */ +#define SERBRP_FRD_MASK 0x000FFFFF +#define SERBRP_FRD_SHIFT 0 + +/* + * The following defines are for the flags in the async FIFO address register. + */ +#ifndef NO_CS4612 +#define ASER_FADDR_A1_MASK 0x000001FF +#define ASER_FADDR_EN1 0x00008000 +#define ASER_FADDR_A2_MASK 0x01FF0000 +#define ASER_FADDR_EN2 0x80000000 +#define ASER_FADDR_A1_SHIFT 0 +#define ASER_FADDR_A2_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the AC97 control register. + */ +#define ACCTL_RSTN 0x00000001 +#define ACCTL_ESYN 0x00000002 +#define ACCTL_VFRM 0x00000004 +#define ACCTL_DCV 0x00000008 +#define ACCTL_CRW 0x00000010 +#define ACCTL_ASYN 0x00000020 +#ifndef NO_CS4612 +#define ACCTL_TC 0x00000040 +#endif + +/* + * The following defines are for the flags in the AC97 status register. + */ +#define ACSTS_CRDY 0x00000001 +#define ACSTS_VSTS 0x00000002 +#ifndef NO_CS4612 +#define ACSTS_WKUP 0x00000004 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register. + */ +#define ACOSV_SLV3 0x00000001 +#define ACOSV_SLV4 0x00000002 +#define ACOSV_SLV5 0x00000004 +#define ACOSV_SLV6 0x00000008 +#define ACOSV_SLV7 0x00000010 +#define ACOSV_SLV8 0x00000020 +#define ACOSV_SLV9 0x00000040 +#define ACOSV_SLV10 0x00000080 +#define ACOSV_SLV11 0x00000100 +#define ACOSV_SLV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 command address + * register. + */ +#define ACCAD_CI_MASK 0x0000007F +#define ACCAD_CI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 command data register. + */ +#define ACCDA_CD_MASK 0x0000FFFF +#define ACCDA_CD_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 input slot valid + * register. + */ +#define ACISV_ISV3 0x00000001 +#define ACISV_ISV4 0x00000002 +#define ACISV_ISV5 0x00000004 +#define ACISV_ISV6 0x00000008 +#define ACISV_ISV7 0x00000010 +#define ACISV_ISV8 0x00000020 +#define ACISV_ISV9 0x00000040 +#define ACISV_ISV10 0x00000080 +#define ACISV_ISV11 0x00000100 +#define ACISV_ISV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 status address + * register. + */ +#define ACSAD_SI_MASK 0x0000007F +#define ACSAD_SI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 status data register. + */ +#define ACSDA_SD_MASK 0x0000FFFF +#define ACSDA_SD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick poll/trigger + * register. + */ +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* + * The following defines are for the flags in the joystick control register. + */ +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* + * The following defines are for the flags in the joystick coordinate pair 1 + * readback register. + */ +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 + +/* + * The following defines are for the flags in the joystick coordinate pair 2 + * readback register. + */ +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* + * The following defines are for the flags in the MIDI control register. + */ +#define MIDCR_TXE 0x00000001 /* Enable transmitting. */ +#define MIDCR_RXE 0x00000002 /* Enable receiving. */ +#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */ +#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */ +#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */ +#define MIDCR_MRST 0x00000020 /* Reset interface. */ + +/* + * The following defines are for the flags in the MIDI status register. + */ +#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */ +#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */ + +/* + * The following defines are for the flags in the MIDI write port register. + */ +#define MIDWP_MWD_MASK 0x000000FF +#define MIDWP_MWD_SHIFT 0 + +/* + * The following defines are for the flags in the MIDI read port register. + */ +#define MIDRP_MRD_MASK 0x000000FF +#define MIDRP_MRD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick GPIO register. + */ +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + * The following defines are for the flags in the master async/sync serial + * port enable register. + */ +#ifndef NO_CS4612 +#define ASER_MASTER_ME 0x00000001 +#endif + +/* + * The following defines are for the flags in the configuration interface + * register. + */ +#define CFGI_CLK 0x00000001 +#define CFGI_DOUT 0x00000002 +#define CFGI_DIN_EEN 0x00000004 +#define CFGI_EELD 0x00000008 + +/* + * The following defines are for the flags in the subsystem ID and vendor ID + * register. + */ +#define SSVID_VID_MASK 0x0000FFFF +#define SSVID_SID_MASK 0xFFFF0000 +#define SSVID_VID_SHIFT 0 +#define SSVID_SID_SHIFT 16 + +/* + * The following defines are for the flags in the GPIO pin interface register. + */ +#define GPIOR_VOLDN 0x00000001 +#define GPIOR_VOLUP 0x00000002 +#define GPIOR_SI2D 0x00000004 +#define GPIOR_SI2OE 0x00000008 + +/* + * The following defines are for the flags in the extended GPIO pin direction + * register. + */ +#ifndef NO_CS4612 +#define EGPIODR_GPOE0 0x00000001 +#define EGPIODR_GPOE1 0x00000002 +#define EGPIODR_GPOE2 0x00000004 +#define EGPIODR_GPOE3 0x00000008 +#define EGPIODR_GPOE4 0x00000010 +#define EGPIODR_GPOE5 0x00000020 +#define EGPIODR_GPOE6 0x00000040 +#define EGPIODR_GPOE7 0x00000080 +#define EGPIODR_GPOE8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin polarity/ + * type register. + */ +#ifndef NO_CS4612 +#define EGPIOPTR_GPPT0 0x00000001 +#define EGPIOPTR_GPPT1 0x00000002 +#define EGPIOPTR_GPPT2 0x00000004 +#define EGPIOPTR_GPPT3 0x00000008 +#define EGPIOPTR_GPPT4 0x00000010 +#define EGPIOPTR_GPPT5 0x00000020 +#define EGPIOPTR_GPPT6 0x00000040 +#define EGPIOPTR_GPPT7 0x00000080 +#define EGPIOPTR_GPPT8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin sticky + * register. + */ +#ifndef NO_CS4612 +#define EGPIOTR_GPS0 0x00000001 +#define EGPIOTR_GPS1 0x00000002 +#define EGPIOTR_GPS2 0x00000004 +#define EGPIOTR_GPS3 0x00000008 +#define EGPIOTR_GPS4 0x00000010 +#define EGPIOTR_GPS5 0x00000020 +#define EGPIOTR_GPS6 0x00000040 +#define EGPIOTR_GPS7 0x00000080 +#define EGPIOTR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO ping wakeup + * register. + */ +#ifndef NO_CS4612 +#define EGPIOWR_GPW0 0x00000001 +#define EGPIOWR_GPW1 0x00000002 +#define EGPIOWR_GPW2 0x00000004 +#define EGPIOWR_GPW3 0x00000008 +#define EGPIOWR_GPW4 0x00000010 +#define EGPIOWR_GPW5 0x00000020 +#define EGPIOWR_GPW6 0x00000040 +#define EGPIOWR_GPW7 0x00000080 +#define EGPIOWR_GPW8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin status + * register. + */ +#ifndef NO_CS4612 +#define EGPIOSR_GPS0 0x00000001 +#define EGPIOSR_GPS1 0x00000002 +#define EGPIOSR_GPS2 0x00000004 +#define EGPIOSR_GPS3 0x00000008 +#define EGPIOSR_GPS4 0x00000010 +#define EGPIOSR_GPS5 0x00000020 +#define EGPIOSR_GPS6 0x00000040 +#define EGPIOSR_GPS7 0x00000080 +#define EGPIOSR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the serial port 6 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC6_ASDO2EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the serial port 7 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC7_ASDI2EN 0x00000001 +#define SERC7_POSILB 0x00000002 +#define SERC7_SIPOLB 0x00000004 +#define SERC7_SOSILB 0x00000008 +#define SERC7_SISOLB 0x00000010 +#endif + +/* + * The following defines are for the flags in the serial port AC link + * configuration register. + */ +#ifndef NO_CS4612 +#define SERACC_CODEC_TYPE_MASK 0x00000001 +#define SERACC_CODEC_TYPE_1_03 0x00000000 +#define SERACC_CODEC_TYPE_2_0 0x00000001 +#define SERACC_TWO_CODECS 0x00000002 +#define SERACC_MDM 0x00000004 +#define SERACC_HSP 0x00000008 +#endif + +/* + * The following defines are for the flags in the AC97 control register 2. + */ +#ifndef NO_CS4612 +#define ACCTL2_RSTN 0x00000001 +#define ACCTL2_ESYN 0x00000002 +#define ACCTL2_VFRM 0x00000004 +#define ACCTL2_DCV 0x00000008 +#define ACCTL2_CRW 0x00000010 +#define ACCTL2_ASYN 0x00000020 +#endif + +/* + * The following defines are for the flags in the AC97 status register 2. + */ +#ifndef NO_CS4612 +#define ACSTS2_CRDY 0x00000001 +#define ACSTS2_VSTS 0x00000002 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACOSV2_SLV3 0x00000001 +#define ACOSV2_SLV4 0x00000002 +#define ACOSV2_SLV5 0x00000004 +#define ACOSV2_SLV6 0x00000008 +#define ACOSV2_SLV7 0x00000010 +#define ACOSV2_SLV8 0x00000020 +#define ACOSV2_SLV9 0x00000040 +#define ACOSV2_SLV10 0x00000080 +#define ACOSV2_SLV11 0x00000100 +#define ACOSV2_SLV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 command address + * register 2. + */ +#ifndef NO_CS4612 +#define ACCAD2_CI_MASK 0x0000007F +#define ACCAD2_CI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 command data register + * 2. + */ +#ifndef NO_CS4612 +#define ACCDA2_CD_MASK 0x0000FFFF +#define ACCDA2_CD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 input slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACISV2_ISV3 0x00000001 +#define ACISV2_ISV4 0x00000002 +#define ACISV2_ISV5 0x00000004 +#define ACISV2_ISV6 0x00000008 +#define ACISV2_ISV7 0x00000010 +#define ACISV2_ISV8 0x00000020 +#define ACISV2_ISV9 0x00000040 +#define ACISV2_ISV10 0x00000080 +#define ACISV2_ISV11 0x00000100 +#define ACISV2_ISV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 status address + * register 2. + */ +#ifndef NO_CS4612 +#define ACSAD2_SI_MASK 0x0000007F +#define ACSAD2_SI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 status data register 2. + */ +#ifndef NO_CS4612 +#define ACSDA2_SD_MASK 0x0000FFFF +#define ACSDA2_SD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap address and control + * registers (all 12). + */ +#ifndef NO_CS4612 +#define IOTAC_SA_MASK 0x0000FFFF +#define IOTAC_MSK_MASK 0x000F0000 +#define IOTAC_IODC_MASK 0x06000000 +#define IOTAC_IODC_16_BIT 0x00000000 +#define IOTAC_IODC_10_BIT 0x02000000 +#define IOTAC_IODC_12_BIT 0x04000000 +#define IOTAC_WSPI 0x08000000 +#define IOTAC_RSPI 0x10000000 +#define IOTAC_WSE 0x20000000 +#define IOTAC_WE 0x40000000 +#define IOTAC_RE 0x80000000 +#define IOTAC_SA_SHIFT 0 +#define IOTAC_MSK_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap fast read registers + * (all 8). + */ +#ifndef NO_CS4612 +#define IOTFR_D_MASK 0x0000FFFF +#define IOTFR_A_MASK 0x000F0000 +#define IOTFR_R_MASK 0x0F000000 +#define IOTFR_ALL 0x40000000 +#define IOTFR_VL 0x80000000 +#define IOTFR_D_SHIFT 0 +#define IOTFR_A_SHIFT 16 +#define IOTFR_R_SHIFT 24 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO register. + */ +#ifndef NO_CS4612 +#define IOTFIFO_BA_MASK 0x00003FFF +#define IOTFIFO_S_MASK 0x00FF0000 +#define IOTFIFO_OF 0x40000000 +#define IOTFIFO_SPIOF 0x80000000 +#define IOTFIFO_BA_SHIFT 0 +#define IOTFIFO_S_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap retry read data + * register. + */ +#ifndef NO_CS4612 +#define IOTRRD_D_MASK 0x0000FFFF +#define IOTRRD_RDV 0x80000000 +#define IOTRRD_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO pointer + * register. + */ +#ifndef NO_CS4612 +#define IOTFP_CA_MASK 0x00003FFF +#define IOTFP_PA_MASK 0x3FFF0000 +#define IOTFP_CA_SHIFT 0 +#define IOTFP_PA_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap control register. + */ +#ifndef NO_CS4612 +#define IOTCR_ITD 0x00000001 +#define IOTCR_HRV 0x00000002 +#define IOTCR_SRV 0x00000004 +#define IOTCR_DTI 0x00000008 +#define IOTCR_DFI 0x00000010 +#define IOTCR_DDP 0x00000020 +#define IOTCR_JTE 0x00000040 +#define IOTCR_PPE 0x00000080 +#endif + +/* + * The following defines are for the flags in the direct PCI data register. + */ +#ifndef NO_CS4612 +#define DPCID_D_MASK 0xFFFFFFFF +#define DPCID_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI address register. + */ +#ifndef NO_CS4612 +#define DPCIA_A_MASK 0xFFFFFFFF +#define DPCIA_A_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI command register. + */ +#ifndef NO_CS4612 +#define DPCIC_C_MASK 0x0000000F +#define DPCIC_C_IOREAD 0x00000002 +#define DPCIC_C_IOWRITE 0x00000003 +#define DPCIC_BE_MASK 0x000000F0 +#endif + +/* + * The following defines are for the flags in the PC/PCI request register. + */ +#ifndef NO_CS4612 +#define PCPCIR_RDC_MASK 0x00000007 +#define PCPCIR_C_MASK 0x00007000 +#define PCPCIR_REQ 0x00008000 +#define PCPCIR_RDC_SHIFT 0 +#define PCPCIR_C_SHIFT 12 +#endif + +/* + * The following defines are for the flags in the PC/PCI grant register. + */ +#ifndef NO_CS4612 +#define PCPCIG_GDC_MASK 0x00000007 +#define PCPCIG_VL 0x00008000 +#define PCPCIG_GDC_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the PC/PCI master enable + * register. + */ +#ifndef NO_CS4612 +#define PCPCIEN_EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the extended PCI power + * management control register. + */ +#ifndef NO_CS4612 +#define EPCIPMC_GWU 0x00000001 +#define EPCIPMC_FSPC 0x00000002 +#endif + +/* + * The following defines are for the flags in the SP control register. + */ +#define SPCR_RUN 0x00000001 +#define SPCR_STPFR 0x00000002 +#define SPCR_RUNFR 0x00000004 +#define SPCR_TICK 0x00000008 +#define SPCR_DRQEN 0x00000020 +#define SPCR_RSTSP 0x00000040 +#define SPCR_OREN 0x00000080 +#ifndef NO_CS4612 +#define SPCR_PCIINT 0x00000100 +#define SPCR_OINTD 0x00000200 +#define SPCR_CRE 0x00008000 +#endif + +/* + * The following defines are for the flags in the debug index register. + */ +#define DREG_REGID_MASK 0x0000007F +#define DREG_DEBUG 0x00000080 +#define DREG_RGBK_MASK 0x00000700 +#define DREG_TRAP 0x00000800 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000 +#endif +#endif +#define DREG_REGID_SHIFT 0 +#define DREG_RGBK_SHIFT 8 +#define DREG_RGBK_REGID_MASK 0x0000077F +#define DREG_REGID_R0 0x00000010 +#define DREG_REGID_R1 0x00000011 +#define DREG_REGID_R2 0x00000012 +#define DREG_REGID_R3 0x00000013 +#define DREG_REGID_R4 0x00000014 +#define DREG_REGID_R5 0x00000015 +#define DREG_REGID_R6 0x00000016 +#define DREG_REGID_R7 0x00000017 +#define DREG_REGID_R8 0x00000018 +#define DREG_REGID_R9 0x00000019 +#define DREG_REGID_RA 0x0000001A +#define DREG_REGID_RB 0x0000001B +#define DREG_REGID_RC 0x0000001C +#define DREG_REGID_RD 0x0000001D +#define DREG_REGID_RE 0x0000001E +#define DREG_REGID_RF 0x0000001F +#define DREG_REGID_RA_BUS_LOW 0x00000020 +#define DREG_REGID_RA_BUS_HIGH 0x00000038 +#define DREG_REGID_YBUS_LOW 0x00000050 +#define DREG_REGID_YBUS_HIGH 0x00000058 +#define DREG_REGID_TRAP_0 0x00000100 +#define DREG_REGID_TRAP_1 0x00000101 +#define DREG_REGID_TRAP_2 0x00000102 +#define DREG_REGID_TRAP_3 0x00000103 +#define DREG_REGID_TRAP_4 0x00000104 +#define DREG_REGID_TRAP_5 0x00000105 +#define DREG_REGID_TRAP_6 0x00000106 +#define DREG_REGID_TRAP_7 0x00000107 +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E +#define DREG_REGID_TOP_OF_STACK 0x0000010F +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110 +#define DREG_REGID_TRAP_9 0x00000111 +#define DREG_REGID_TRAP_10 0x00000112 +#define DREG_REGID_TRAP_11 0x00000113 +#define DREG_REGID_TRAP_12 0x00000114 +#define DREG_REGID_TRAP_13 0x00000115 +#define DREG_REGID_TRAP_14 0x00000116 +#define DREG_REGID_TRAP_15 0x00000117 +#define DREG_REGID_TRAP_16 0x00000118 +#define DREG_REGID_TRAP_17 0x00000119 +#define DREG_REGID_TRAP_18 0x0000011A +#define DREG_REGID_TRAP_19 0x0000011B +#define DREG_REGID_TRAP_20 0x0000011C +#define DREG_REGID_TRAP_21 0x0000011D +#define DREG_REGID_TRAP_22 0x0000011E +#define DREG_REGID_TRAP_23 0x0000011F +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200 +#define DREG_REGID_RSA0_HIGH 0x00000201 +#define DREG_REGID_RSA1_LOW 0x00000202 +#define DREG_REGID_RSA1_HIGH 0x00000203 +#define DREG_REGID_RSA2 0x00000204 +#define DREG_REGID_RSA3 0x00000205 +#define DREG_REGID_RSI0_LOW 0x00000206 +#define DREG_REGID_RSI0_HIGH 0x00000207 +#define DREG_REGID_RSI1 0x00000208 +#define DREG_REGID_RSI2 0x00000209 +#define DREG_REGID_SAGUSTATUS 0x0000020A +#define DREG_REGID_RSCONFIG01_LOW 0x0000020B +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C +#define DREG_REGID_RSCONFIG23_LOW 0x0000020D +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E +#define DREG_REGID_RSDMA01E 0x0000020F +#define DREG_REGID_RSDMA23E 0x00000210 +#define DREG_REGID_RSD0_LOW 0x00000211 +#define DREG_REGID_RSD0_HIGH 0x00000212 +#define DREG_REGID_RSD1_LOW 0x00000213 +#define DREG_REGID_RSD1_HIGH 0x00000214 +#define DREG_REGID_RSD2_LOW 0x00000215 +#define DREG_REGID_RSD2_HIGH 0x00000216 +#define DREG_REGID_RSD3_LOW 0x00000217 +#define DREG_REGID_RSD3_HIGH 0x00000218 +#define DREG_REGID_SRAR_HIGH 0x0000021A +#define DREG_REGID_SRAR_LOW 0x0000021B +#define DREG_REGID_DMA_STATE 0x0000021C +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E +#define DREG_REGID_CPU_STATUS 0x00000300 +#define DREG_REGID_MAC_MODE 0x00000301 +#define DREG_REGID_STACK_AND_REPEAT 0x00000302 +#define DREG_REGID_INDEX0 0x00000304 +#define DREG_REGID_INDEX1 0x00000305 +#define DREG_REGID_DMA_STATE_0_3 0x00000400 +#define DREG_REGID_DMA_STATE_4_7 0x00000404 +#define DREG_REGID_DMA_STATE_8_11 0x00000408 +#define DREG_REGID_DMA_STATE_12_15 0x0000040C +#define DREG_REGID_DMA_STATE_16_19 0x00000410 +#define DREG_REGID_DMA_STATE_20_23 0x00000414 +#define DREG_REGID_DMA_STATE_24_27 0x00000418 +#define DREG_REGID_DMA_STATE_28_31 0x0000041C +#define DREG_REGID_DMA_STATE_32_35 0x00000420 +#define DREG_REGID_DMA_STATE_36_39 0x00000424 +#define DREG_REGID_DMA_STATE_40_43 0x00000428 +#define DREG_REGID_DMA_STATE_44_47 0x0000042C +#define DREG_REGID_DMA_STATE_48_51 0x00000430 +#define DREG_REGID_DMA_STATE_52_55 0x00000434 +#define DREG_REGID_DMA_STATE_56_59 0x00000438 +#define DREG_REGID_DMA_STATE_60_63 0x0000043C +#define DREG_REGID_DMA_STATE_64_67 0x00000440 +#define DREG_REGID_DMA_STATE_68_71 0x00000444 +#define DREG_REGID_DMA_STATE_72_75 0x00000448 +#define DREG_REGID_DMA_STATE_76_79 0x0000044C +#define DREG_REGID_DMA_STATE_80_83 0x00000450 +#define DREG_REGID_DMA_STATE_84_87 0x00000454 +#define DREG_REGID_DMA_STATE_88_91 0x00000458 +#define DREG_REGID_DMA_STATE_92_95 0x0000045C +#define DREG_REGID_TRAP_SELECT 0x00000500 +#define DREG_REGID_TRAP_WRITE_0 0x00000500 +#define DREG_REGID_TRAP_WRITE_1 0x00000501 +#define DREG_REGID_TRAP_WRITE_2 0x00000502 +#define DREG_REGID_TRAP_WRITE_3 0x00000503 +#define DREG_REGID_TRAP_WRITE_4 0x00000504 +#define DREG_REGID_TRAP_WRITE_5 0x00000505 +#define DREG_REGID_TRAP_WRITE_6 0x00000506 +#define DREG_REGID_TRAP_WRITE_7 0x00000507 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510 +#define DREG_REGID_TRAP_WRITE_9 0x00000511 +#define DREG_REGID_TRAP_WRITE_10 0x00000512 +#define DREG_REGID_TRAP_WRITE_11 0x00000513 +#define DREG_REGID_TRAP_WRITE_12 0x00000514 +#define DREG_REGID_TRAP_WRITE_13 0x00000515 +#define DREG_REGID_TRAP_WRITE_14 0x00000516 +#define DREG_REGID_TRAP_WRITE_15 0x00000517 +#define DREG_REGID_TRAP_WRITE_16 0x00000518 +#define DREG_REGID_TRAP_WRITE_17 0x00000519 +#define DREG_REGID_TRAP_WRITE_18 0x0000051A +#define DREG_REGID_TRAP_WRITE_19 0x0000051B +#define DREG_REGID_TRAP_WRITE_20 0x0000051C +#define DREG_REGID_TRAP_WRITE_21 0x0000051D +#define DREG_REGID_TRAP_WRITE_22 0x0000051E +#define DREG_REGID_TRAP_WRITE_23 0x0000051F +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600 +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601 +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602 +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603 +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604 +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605 +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606 +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607 +#define DREG_REGID_MAC0_ACC0_MID 0x00000608 +#define DREG_REGID_MAC0_ACC1_MID 0x00000609 +#define DREG_REGID_MAC0_ACC2_MID 0x0000060A +#define DREG_REGID_MAC0_ACC3_MID 0x0000060B +#define DREG_REGID_MAC1_ACC0_MID 0x0000060C +#define DREG_REGID_MAC1_ACC1_MID 0x0000060D +#define DREG_REGID_MAC1_ACC2_MID 0x0000060E +#define DREG_REGID_MAC1_ACC3_MID 0x0000060F +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610 +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611 +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612 +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613 +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614 +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615 +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616 +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617 +#define DREG_REGID_RSHOUT_LOW 0x00000620 +#define DREG_REGID_RSHOUT_MID 0x00000628 +#define DREG_REGID_RSHOUT_HIGH 0x00000630 + +/* + * The following defines are for the flags in the DMA stream requestor write + */ +#define DSRWP_DSR_MASK 0x0000000F +#define DSRWP_DSR_BG_RQ 0x00000001 +#define DSRWP_DSR_PRIORITY_MASK 0x00000006 +#define DSRWP_DSR_PRIORITY_0 0x00000000 +#define DSRWP_DSR_PRIORITY_1 0x00000002 +#define DSRWP_DSR_PRIORITY_2 0x00000004 +#define DSRWP_DSR_PRIORITY_3 0x00000006 +#define DSRWP_DSR_RQ_PENDING 0x00000008 + +/* + * The following defines are for the flags in the trap write port register. + */ +#define TWPR_TW_MASK 0x0000FFFF +#define TWPR_TW_SHIFT 0 + +/* + * The following defines are for the flags in the stack pointer write + * register. + */ +#define SPWR_STKP_MASK 0x0000000F +#define SPWR_STKP_SHIFT 0 + +/* + * The following defines are for the flags in the SP interrupt register. + */ +#define SPIR_FRI 0x00000001 +#define SPIR_DOI 0x00000002 +#define SPIR_GPI2 0x00000004 +#define SPIR_GPI3 0x00000008 +#define SPIR_IP0 0x00000010 +#define SPIR_IP1 0x00000020 +#define SPIR_IP2 0x00000040 +#define SPIR_IP3 0x00000080 + +/* + * The following defines are for the flags in the functional group 1 register. + */ +#define FGR1_F1S_MASK 0x0000FFFF +#define FGR1_F1S_SHIFT 0 + +/* + * The following defines are for the flags in the SP clock status register. + */ +#define SPCS_FRI 0x00000001 +#define SPCS_DOI 0x00000002 +#define SPCS_GPI2 0x00000004 +#define SPCS_GPI3 0x00000008 +#define SPCS_IP0 0x00000010 +#define SPCS_IP1 0x00000020 +#define SPCS_IP2 0x00000040 +#define SPCS_IP3 0x00000080 +#define SPCS_SPRUN 0x00000100 +#define SPCS_SLEEP 0x00000200 +#define SPCS_FG 0x00000400 +#define SPCS_ORUN 0x00000800 +#define SPCS_IRQ 0x00001000 +#define SPCS_FGN_MASK 0x0000E000 +#define SPCS_FGN_SHIFT 13 + +/* + * The following defines are for the flags in the SP DMA requestor status + * register. + */ +#define SDSR_DCS_MASK 0x000000FF +#define SDSR_DCS_SHIFT 0 +#define SDSR_DCS_NONE 0x00000007 + +/* + * The following defines are for the flags in the frame timer register. + */ +#define FRMT_FTV_MASK 0x0000FFFF +#define FRMT_FTV_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer current count + * register. + */ +#define FRCC_FCC_MASK 0x0000FFFF +#define FRCC_FCC_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer save count + * register. + */ +#define FRSC_FCS_MASK 0x0000FFFF +#define FRSC_FCS_SHIFT 0 + +/* + * The following define the various flags stored in the scatter/gather + * descriptors. + */ +#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8 +#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000 +#define DMA_SG_SAMPLE_END_FLAG 0x10000000 +#define DMA_SG_LOOP_END_FLAG 0x20000000 +#define DMA_SG_SIGNAL_END_FLAG 0x40000000 +#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000 +#define DMA_SG_NEXT_ENTRY_SHIFT 3 +#define DMA_SG_SAMPLE_END_SHIFT 16 + +/* + * The following define the offsets of the fields within the on-chip generic + * DMA requestor. + */ +#define DMA_RQ_CONTROL1 0x00000000 +#define DMA_RQ_CONTROL2 0x00000004 +#define DMA_RQ_SOURCE_ADDR 0x00000008 +#define DMA_RQ_DESTINATION_ADDR 0x0000000C +#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010 +#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014 +#define DMA_RQ_LOOP_START_ADDR 0x00000018 +#define DMA_RQ_POST_LOOP_ADDR 0x0000001C +#define DMA_RQ_PAGE_MAP_ADDR 0x00000020 + +/* + * The following defines are for the flags in the first control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C1_COUNT_MASK 0x000003FF +#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000 +#define DMA_RQ_C1_SOURCE_GATHER 0x00002000 +#define DMA_RQ_C1_DONE_FLAG 0x00004000 +#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000 +#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000 +#define DMA_RQ_C1_FULL_PAGE 0x00000000 +#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000 +#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000 +#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000 +#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000 +#define DMA_RQ_C1_NOT_LOOP_END 0x00000000 +#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000 +#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000 +#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000 +#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000 +#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000 +#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000 +#define DMA_RQ_C1_PM_RESERVED 0x00200000 +#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000 +#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000 +#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000 +#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000 +#define DMA_RQ_C1_DEST_LINEAR 0x00000000 +#define DMA_RQ_C1_DEST_MOD16 0x01000000 +#define DMA_RQ_C1_DEST_MOD32 0x02000000 +#define DMA_RQ_C1_DEST_MOD64 0x03000000 +#define DMA_RQ_C1_DEST_MOD128 0x04000000 +#define DMA_RQ_C1_DEST_MOD256 0x05000000 +#define DMA_RQ_C1_DEST_MOD512 0x06000000 +#define DMA_RQ_C1_DEST_MOD1024 0x07000000 +#define DMA_RQ_C1_DEST_ON_HOST 0x08000000 +#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000 +#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000 +#define DMA_RQ_C1_SOURCE_MOD16 0x10000000 +#define DMA_RQ_C1_SOURCE_MOD32 0x20000000 +#define DMA_RQ_C1_SOURCE_MOD64 0x30000000 +#define DMA_RQ_C1_SOURCE_MOD128 0x40000000 +#define DMA_RQ_C1_SOURCE_MOD256 0x50000000 +#define DMA_RQ_C1_SOURCE_MOD512 0x60000000 +#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000 +#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000 +#define DMA_RQ_C1_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the second control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F +#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300 +#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000 +#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100 +#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200 +#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300 +#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000 +#define DMA_RQ_C2_AC_NONE 0x00000000 +#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000 +#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000 +#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000 +#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000 +#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000 +#define DMA_RQ_C2_LOOP_MASK 0x30000000 +#define DMA_RQ_C2_NO_LOOP 0x00000000 +#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000 +#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000 +#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000 +#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000 +#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000 +#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0 +#define DMA_RQ_C2_LOOP_END_SHIFT 16 + +/* + * The following defines are for the flags in the source and destination words + * of the on-chip generic DMA requestor. + */ +#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF +#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000 +#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000 +#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000 +#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000 +#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000 +#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000 +#define DMA_RQ_SD_END_FLAG 0x40000000 +#define DMA_RQ_SD_ERROR_FLAG 0x80000000 +#define DMA_RQ_SD_ADDRESS_SHIFT 0 + +/* + * The following defines are for the flags in the page map address word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8 +#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000 +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3 +#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12 + +#define BA1_VARIDEC_BUF_1 0x000 + +#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */ +#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */ +#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */ +#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */ +#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */ +#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */ + +#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */ +#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */ +#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */ +#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */ +#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */ +#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */ +#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */ + +#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */ +#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */ +#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ +#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ + +/* + * + */ + +#define CS461X_MODE_OUTPUT (1<<0) /* MIDI UART - output */ +#define CS461X_MODE_INPUT (1<<1) /* MIDI UART - input */ + +//**************************************************************************** +// +// The following define the offsets of the AC97 shadow registers, which appear +// as a virtual extension to the base address register zero memory range. +// +//**************************************************************************** +#define AC97_REG_OFFSET_MASK 0x0000007EL +#define AC97_CODEC_NUMBER_MASK 0x00003000L + +#define BA0_AC97_RESET 0x00001000L +#define BA0_AC97_MASTER_VOLUME 0x00001002L +#define BA0_AC97_HEADPHONE_VOLUME 0x00001004L +#define BA0_AC97_MASTER_VOLUME_MONO 0x00001006L +#define BA0_AC97_MASTER_TONE 0x00001008L +#define BA0_AC97_PC_BEEP_VOLUME 0x0000100AL +#define BA0_AC97_PHONE_VOLUME 0x0000100CL +#define BA0_AC97_MIC_VOLUME 0x0000100EL +#define BA0_AC97_LINE_IN_VOLUME 0x00001010L +#define BA0_AC97_CD_VOLUME 0x00001012L +#define BA0_AC97_VIDEO_VOLUME 0x00001014L +#define BA0_AC97_AUX_VOLUME 0x00001016L +#define BA0_AC97_PCM_OUT_VOLUME 0x00001018L +#define BA0_AC97_RECORD_SELECT 0x0000101AL +#define BA0_AC97_RECORD_GAIN 0x0000101CL +#define BA0_AC97_RECORD_GAIN_MIC 0x0000101EL +#define BA0_AC97_GENERAL_PURPOSE 0x00001020L +#define BA0_AC97_3D_CONTROL 0x00001022L +#define BA0_AC97_MODEM_RATE 0x00001024L +#define BA0_AC97_POWERDOWN 0x00001026L +#define BA0_AC97_EXT_AUDIO_ID 0x00001028L +#define BA0_AC97_EXT_AUDIO_POWER 0x0000102AL +#define BA0_AC97_PCM_FRONT_DAC_RATE 0x0000102CL +#define BA0_AC97_PCM_SURR_DAC_RATE 0x0000102EL +#define BA0_AC97_PCM_LFE_DAC_RATE 0x00001030L +#define BA0_AC97_PCM_LR_ADC_RATE 0x00001032L +#define BA0_AC97_MIC_ADC_RATE 0x00001034L +#define BA0_AC97_6CH_VOL_C_LFE 0x00001036L +#define BA0_AC97_6CH_VOL_SURROUND 0x00001038L +#define BA0_AC97_RESERVED_3A 0x0000103AL +#define BA0_AC97_EXT_MODEM_ID 0x0000103CL +#define BA0_AC97_EXT_MODEM_POWER 0x0000103EL +#define BA0_AC97_LINE1_CODEC_RATE 0x00001040L +#define BA0_AC97_LINE2_CODEC_RATE 0x00001042L +#define BA0_AC97_HANDSET_CODEC_RATE 0x00001044L +#define BA0_AC97_LINE1_CODEC_LEVEL 0x00001046L +#define BA0_AC97_LINE2_CODEC_LEVEL 0x00001048L +#define BA0_AC97_HANDSET_CODEC_LEVEL 0x0000104AL +#define BA0_AC97_GPIO_PIN_CONFIG 0x0000104CL +#define BA0_AC97_GPIO_PIN_TYPE 0x0000104EL +#define BA0_AC97_GPIO_PIN_STICKY 0x00001050L +#define BA0_AC97_GPIO_PIN_WAKEUP 0x00001052L +#define BA0_AC97_GPIO_PIN_STATUS 0x00001054L +#define BA0_AC97_MISC_MODEM_AFE_STAT 0x00001056L +#define BA0_AC97_RESERVED_58 0x00001058L +#define BA0_AC97_CRYSTAL_REV_N_FAB_ID 0x0000105AL +#define BA0_AC97_TEST_AND_MISC_CTRL 0x0000105CL +#define BA0_AC97_AC_MODE 0x0000105EL +#define BA0_AC97_MISC_CRYSTAL_CONTROL 0x00001060L +#define BA0_AC97_LINE1_HYPRID_CTRL 0x00001062L +#define BA0_AC97_VENDOR_RESERVED_64 0x00001064L +#define BA0_AC97_VENDOR_RESERVED_66 0x00001066L +#define BA0_AC97_SPDIF_CONTROL 0x00001068L +#define BA0_AC97_VENDOR_RESERVED_6A 0x0000106AL +#define BA0_AC97_VENDOR_RESERVED_6C 0x0000106CL +#define BA0_AC97_VENDOR_RESERVED_6E 0x0000106EL +#define BA0_AC97_VENDOR_RESERVED_70 0x00001070L +#define BA0_AC97_VENDOR_RESERVED_72 0x00001072L +#define BA0_AC97_VENDOR_RESERVED_74 0x00001074L +#define BA0_AC97_CAL_ADDRESS 0x00001076L +#define BA0_AC97_CAL_DATA 0x00001078L +#define BA0_AC97_VENDOR_RESERVED_7A 0x0000107AL +#define BA0_AC97_VENDOR_ID1 0x0000107CL +#define BA0_AC97_VENDOR_ID2 0x0000107EL +#endif /* __CS461X_H */ diff -Nru a/sound/oss/cs461x_image.h b/sound/oss/cs461x_image.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs461x_image.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,322 @@ +/**************************************************************************** + * "CWCIMAGE.H"-- For CS46XX. Ver 1.04 + * Copyright 1998-2001 (c) Cirrus Logic Corp. + * Version 1.04 + **************************************************************************** + */ +#ifndef __CS_IMAGE_H +#define __CS_IMAGE_H + +#define CLEAR__COUNT 3 +#define FILL__COUNT 4 +#define BA1__DWORD_SIZE 13*1024+512 + +static struct +{ + unsigned BA1__DestByteOffset; + unsigned BA1__SourceSize; +} ClrStat[CLEAR__COUNT] ={ {0x00000000, 0x00003000 }, + {0x00010000, 0x00003800 }, + {0x00020000, 0x00007000 } }; + +static u32 FillArray1[]={ +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000163,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00200040,0x00008010,0x00000000, +0x00000000,0x80000001,0x00000001,0x00060000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00900080,0x00000173,0x00000000, +0x00000000,0x00000010,0x00800000,0x00900000, +0xf2c0000f,0x00000200,0x00000000,0x00010600, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000163,0x330300c2, +0x06000000,0x00000000,0x80008000,0x80008000, +0x3fc0000f,0x00000301,0x00010400,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00b00000,0x00d0806d,0x330480c3, +0x04800000,0x00000001,0x00800001,0x0000ffff, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x066a0600,0x06350070,0x0000929d,0x929d929d, +0x00000000,0x0000735a,0x00000600,0x00000000, +0x929d735a,0x00000000,0x00010000,0x735a735a, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000804f,0x000000c3, +0x05000000,0x00a00010,0x00000000,0x80008000, +0x00000000,0x00000000,0x00000700,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000080,0x00a00000,0x0000809a,0x000000c2, +0x07400000,0x00000000,0x80008000,0xffffffff, +0x00c80028,0x00005555,0x00000000,0x000107a0, +0x00c80028,0x000000c2,0x06800000,0x00000000, +0x06e00080,0x00300000,0x000080bb,0x000000c9, +0x07a00000,0x04000000,0x80008000,0xffffffff, +0x00c80028,0x00005555,0x00000000,0x00000780, +0x00c80028,0x000000c5,0xff800000,0x00000000, +0x00640080,0x00c00000,0x00008197,0x000000c9, +0x07800000,0x04000000,0x80008000,0xffffffff, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000805e,0x000000c1, +0x00000000,0x00800000,0x80008000,0x80008000, +0x00020000,0x0000ffff,0x00000000,0x00000000}; + +static u32 FillArray2[]={ +0x929d0600,0x929d929d,0x929d929d,0x929d0000, +0x929d929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x00100635,0x060b013f,0x00000004, +0x00000001,0x007a0002,0x00000000,0x066e0610, +0x0105929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0xa431ac75,0x0001735a,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051, +0x00000000,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0x00000000,0x06400136, +0x0000270f,0x00010000,0x007a0000,0x00000000, +0x068e0645,0x0105929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0xa431ac75,0x0001735a, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0x735a0100,0x00000000,0x00000000,0x00000000}; + +static u32 FillArray3[]={ +0x00000000,0x00000000,0x00000000,0x00010004}; + +static u32 FillArray4[]={ +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00001705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00009705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00011705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00019705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00021705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00029705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00031705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00039705,0x00001400,0x000a411e,0x00001003, +0x000fe19e,0x00001003,0x0009c730,0x00001003, +0x0008e19c,0x00001003,0x000083c1,0x00093040, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00009705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00011705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00019705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00021705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00029705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00031705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00039705,0x00001400,0x000a211e,0x00001003, +0x0000a730,0x00001008,0x000e2730,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x00000000,0x00000000,0x000f619c,0x00001003, +0x0007f801,0x000c0000,0x00000037,0x00001000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x000c0000,0x00000000,0x00000000, +0x0000373c,0x00001000,0x00000000,0x00000000, +0x000ee19c,0x00001003,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000273c,0x00001000, +0x00000033,0x00001000,0x000e679e,0x00001003, +0x00007705,0x00001400,0x000ac71e,0x00001003, +0x00087fc1,0x000c3be0,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000a730,0x00001003, +0x00000033,0x00001000,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x000c0000, +0x00000032,0x00001000,0x0000273d,0x00001000, +0x0004a730,0x00001003,0x00000f41,0x00097140, +0x0000a841,0x0009b240,0x0000a0c1,0x0009f040, +0x0001c641,0x00093540,0x0001cec1,0x0009b5c0, +0x00000000,0x00000000,0x0001bf05,0x0003fc40, +0x00002725,0x000aa400,0x00013705,0x00093a00, +0x0000002e,0x0009d6c0,0x00038630,0x00001004, +0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000, +0x00000000,0x000c70e0,0x0007d182,0x0002c640, +0x00000630,0x00001004,0x000799b8,0x0002c6c0, +0x00031705,0x00092240,0x00039f05,0x000932c0, +0x0003520a,0x00000000,0x00040731,0x0000100b, +0x00010705,0x000b20c0,0x00000000,0x000eba44, +0x00032108,0x000c60c4,0x00065208,0x000c2917, +0x000406b0,0x00001007,0x00012f05,0x00036880, +0x0002818e,0x000c0000,0x0004410a,0x00000000, +0x00040630,0x00001007,0x00029705,0x000c0000, +0x00000000,0x00000000,0x00003fc1,0x0003fc40, +0x000037c1,0x00091b40,0x00003fc1,0x000911c0, +0x000037c1,0x000957c0,0x00003fc1,0x000951c0, +0x000037c1,0x00000000,0x00003fc1,0x000991c0, +0x000037c1,0x00000000,0x00003fc1,0x0009d1c0, +0x000037c1,0x00000000,0x0001ccc1,0x000915c0, +0x0001c441,0x0009d800,0x0009cdc1,0x00091240, +0x0001c541,0x00091d00,0x0009cfc1,0x00095240, +0x0001c741,0x00095c80,0x000e8ca9,0x00099240, +0x000e85ad,0x00095640,0x00069ca9,0x00099d80, +0x000e952d,0x00099640,0x000eaca9,0x0009d6c0, +0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80, +0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0, +0x000ec5ad,0x0009da40,0x000edca9,0x0009d300, +0x000a6e0a,0x00001000,0x000ed52d,0x00091e40, +0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40, +0x0006fca9,0x00002500,0x000fb208,0x000c59a0, +0x000ef52d,0x0009de40,0x00068ca9,0x000912c1, +0x000683ad,0x00095241,0x00020f05,0x000991c1, +0x00000000,0x00000000,0x00086f88,0x00001000, +0x0009cf81,0x000b5340,0x0009c701,0x000b92c0, +0x0009de81,0x000bd300,0x0009d601,0x000b1700, +0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0, +0x000a0f81,0x000bd740,0x00020701,0x000b5c80, +0x000a1681,0x000b97c0,0x00021601,0x00002500, +0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0, +0x00021681,0x00002d00,0x00020f81,0x000bd800, +0x000a0701,0x000b5bc0,0x00021601,0x00003500, +0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0, +0x00021681,0x00003d00,0x00020f81,0x000b1d00, +0x000a0701,0x000b1fc0,0x00021601,0x00020500, +0x00020f81,0x000b1341,0x000a0701,0x000b9fc0, +0x00021681,0x00020d00,0x00020f81,0x000bde80, +0x000a0701,0x000bdfc0,0x00021601,0x00021500, +0x00020f81,0x000b9341,0x00020701,0x000b53c1, +0x00021681,0x00021d00,0x000a0f81,0x000d0380, +0x0000b601,0x000b15c0,0x00007b01,0x00000000, +0x00007b81,0x000bd1c0,0x00007b01,0x00000000, +0x00007b81,0x000b91c0,0x00007b01,0x000b57c0, +0x00007b81,0x000b51c0,0x00007b01,0x000b1b40, +0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0, +0x0007e488,0x000d7e45,0x00000000,0x000d7a44, +0x0007e48a,0x00000000,0x00011f05,0x00084080, +0x00000000,0x00000000,0x00001705,0x000b3540, +0x00008a01,0x000bf040,0x00007081,0x000bb5c0, +0x00055488,0x00000000,0x0000d482,0x0003fc40, +0x0003fc88,0x00000000,0x0001e401,0x000b3a00, +0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784, +0x000c86b0,0x00001007,0x00008281,0x000bb240, +0x0000b801,0x000b7140,0x00007888,0x00000000, +0x0000073c,0x00001000,0x0007f188,0x000c0000, +0x00000000,0x00000000,0x00055288,0x000c555c, +0x0005528a,0x000c0000,0x0009fa88,0x000c5d00, +0x0000fa88,0x00000000,0x00000032,0x00001000, +0x0000073d,0x00001000,0x0007f188,0x000c0000, +0x00000000,0x00000000,0x0008c01c,0x00001003, +0x00002705,0x00001008,0x0008b201,0x000c1392, +0x0000ba01,0x00000000,0x00008731,0x00001400, +0x0004c108,0x000fe0c4,0x00057488,0x00000000, +0x000a6388,0x00001001,0x0008b334,0x000bc141, +0x0003020e,0x00000000,0x000886b0,0x00001008, +0x00003625,0x000c5dfa,0x000a638a,0x00001001, +0x0008020e,0x00001002,0x0008a6b0,0x00001008, +0x0007f301,0x00000000,0x00000000,0x00000000, +0x00002725,0x000a8c40,0x000000ae,0x00000000, +0x000d8630,0x00001008,0x00000000,0x000c74e0, +0x0007d182,0x0002d640,0x000a8630,0x00001008, +0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5, +0x0007420a,0x000c0000,0x00062208,0x000c4117, +0x00070630,0x00001009,0x00000000,0x000c0000, +0x0001022e,0x00000000,0x0003a630,0x00001009, +0x00000000,0x000c0000,0x00000036,0x00001000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x0002a730,0x00001008,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0002a730,0x00001008, +0x00000033,0x00001000,0x0002a705,0x00001008, +0x00007a01,0x000c0000,0x000e6288,0x000d550a, +0x0006428a,0x00000000,0x00060730,0x0000100a, +0x00000000,0x000c0000,0x00000000,0x00000000, +0x0007aab0,0x00034880,0x00078fb0,0x0000100b, +0x00057488,0x00000000,0x00033b94,0x00081140, +0x000183ae,0x00000000,0x000786b0,0x0000100b, +0x00022f05,0x000c3545,0x0000eb8a,0x00000000, +0x00042731,0x00001003,0x0007aab0,0x00034880, +0x00048fb0,0x0000100a,0x00057488,0x00000000, +0x00033b94,0x00081140,0x000183ae,0x00000000, +0x000806b0,0x0000100b,0x00022f05,0x00000000, +0x00007401,0x00091140,0x00048f05,0x000951c0, +0x00042731,0x00001003,0x0000473d,0x00001000, +0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, +0x000fe19e,0x00001003,0x00000000,0x00000000, +0x0008e19c,0x00001003,0x000083c1,0x00093040, +0x00000f41,0x00097140,0x0000a841,0x0009b240, +0x0000a0c1,0x0009f040,0x0001c641,0x00093540, +0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44, +0x00055208,0x00000000,0x00010705,0x000a2880, +0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5, +0x0004ef0a,0x000c0000,0x00012f05,0x00036880, +0x00065308,0x000c2997,0x000d86b0,0x0000100a, +0x0004410a,0x000d40c7,0x00000000,0x00000000, +0x00080730,0x00001004,0x00056f0a,0x000ea105, +0x00000000,0x00000000,0x0000473d,0x00001000, +0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, +0x0000273d,0x00001000,0x00000000,0x000eba44, +0x00048f05,0x0000f440,0x00007401,0x0000f7c0, +0x00000734,0x00001000,0x00010705,0x000a6880, +0x00006a88,0x000c75c4,0x00000000,0x000e5084, +0x00000000,0x000eba44,0x00087401,0x000e4782, +0x00000734,0x00001000,0x00010705,0x000a6880, +0x00006a88,0x000c75c4,0x0007c108,0x000c0000, +0x0007e721,0x000bed40,0x00005f25,0x000badc0, +0x0003ba97,0x000beb80,0x00065590,0x000b2e00, +0x00033217,0x00003ec0,0x00065590,0x000b8e40, +0x0003ed80,0x000491c0,0x00073fb0,0x00074c80, +0x000283a0,0x0000100c,0x000ee388,0x00042970, +0x00008301,0x00021ef2,0x000b8f14,0x0000000f, +0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6, +0x000032ac,0x000c3916,0x0004edc2,0x00074c80, +0x00078898,0x00001000,0x00038894,0x00000032, +0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6, +0x0004edc2,0x000c1956,0x0000722c,0x00034a00, +0x00041705,0x0009ed40,0x00058730,0x00001400, +0x000d7488,0x000c3a00,0x00048f05,0x00000000}; + +static struct +{ u32 Offset; + u32 Size; + u32 *pFill; +} FillStat[FILL__COUNT] = { + {0x00000000, sizeof(FillArray1), FillArray1}, + {0x00001800, sizeof(FillArray2), FillArray2}, + {0x000137f0, sizeof(FillArray3), FillArray3}, + {0x00020000, sizeof(FillArray4), FillArray4} + }; + + +#endif diff -Nru a/sound/oss/cs46xx.c b/sound/oss/cs46xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs46xx.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,5727 @@ +/* + * Crystal SoundFusion CS46xx driver + * + * Copyright 1998-2001 Cirrus Logic Corporation + * + * Copyright 1999-2000 Jaroslav Kysela + * Copyright 2000 Alan Cox + * + * The core of this code is taken from the ALSA project driver by + * Jaroslav. Please send Jaroslav the credit for the driver and + * report bugs in this port to + * + * 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. + * Current maintainers: + * Cirrus Logic Corporation, Thomas Woller (tw) + * + * Nils Faerber (nf) + * + * Thanks to David Pollard for testing. + * + * Changes: + * 20000909-nf Changed cs_read, cs_write and drain_dac + * 20001025-tw Separate Playback/Capture structs and buffers. + * Added Scatter/Gather support for Playback. + * Added Capture. + * 20001027-nf Port to kernel 2.4.0-test9, some clean-ups + * Start of powermanagement support (CS46XX_PM). + * 20001128-tw Add module parm for default buffer order. + * added DMA_GFP flag to kmalloc dma buffer allocs. + * backfill silence to eliminate stuttering on + * underruns. + * 20001201-tw add resyncing of swptr on underruns. + * 20001205-tw-nf fixed GETOSPACE ioctl() after open() + * 20010113-tw patch from Hans Grobler general cleanup. + * 20010117-tw 2.4.0 pci cleanup, wrapper code for 2.2.16-2.4.0 + * 20010118-tw basic PM support for 2.2.16+ and 2.4.0/2.4.2. + * 20010228-dh patch from David Huggins - cs_update_ptr recursion. + * 20010409-tw add hercules game theatre XP amp code. + * 20010420-tw cleanup powerdown/up code. + * 20010521-tw eliminate pops, and fixes for powerdown. + * 20010525-tw added fixes for thinkpads with powerdown logic. + * 20010723-sh patch from Horms (Simon Horman) - + * SOUND_PCM_READ_BITS returns bits as set in driver + * rather than a logical or of the possible values. + * Various ioctls handle the case where the device + * is open for reading or writing but not both better. + * + * Status: + * Playback/Capture supported from 8k-48k. + * 16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported. + * + * APM/PM - 2.2.x APM is enabled and functioning fine. APM can also + * be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro + * definition. + * + * Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp, + * so, use the drain/polarity to enable. + * hercules_egpio_disable set to 1, will force a 0 to EGPIODR. + * + * VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control + * the external amplifier for the "back" speakers, since we do not + * support the secondary codec then this external amp is also not + * turned on. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs46xxpm-24.h" +#include "cs46xx_wrapper-24.h" + +#include "cs461x.h" + +/* MIDI buffer sizes */ +#define CS_MIDIINBUF 500 +#define CS_MIDIOUTBUF 500 + +#define ADC_RUNNING 1 +#define DAC_RUNNING 2 + +#define CS_FMT_16BIT 1 /* These are fixed in fact */ +#define CS_FMT_STEREO 2 +#define CS_FMT_MASK 3 + +#define CS_TYPE_ADC 1 +#define CS_TYPE_DAC 2 + +#define CS_TRUE 1 +#define CS_FALSE 0 + +#define CS_INC_USE_COUNT(m) (atomic_inc(m)) +#define CS_DEC_USE_COUNT(m) (atomic_dec(m)) +#define CS_DEC_AND_TEST(m) (atomic_dec_and_test(m)) +#define CS_IN_USE(m) (atomic_read(m) != 0) + +#define CS_DBGBREAKPOINT {__asm__("INT $3");} +/* + * CS461x definitions + */ + +#define CS461X_BA0_SIZE 0x2000 +#define CS461X_BA1_DATA0_SIZE 0x3000 +#define CS461X_BA1_DATA1_SIZE 0x3800 +#define CS461X_BA1_PRG_SIZE 0x7000 +#define CS461X_BA1_REG_SIZE 0x0100 + +#define GOF_PER_SEC 200 + +#define CSDEBUG_INTERFACE 1 +#define CSDEBUG 1 +/* + * Turn on/off debugging compilation by using 1/0 respectively for CSDEBUG + * + * + * 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 +/* + * 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_MIDI_WRITE 0x00000040 /* write information for midi */ +#define CS_MIDI_READ 0x00000080 /* read information for midi */ +#define CS_MPU401_WRITE 0x00000100 /* write information for mpu401 */ +#define CS_MPU401_READ 0x00000200 /* read information for mpu401 */ +#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_PM 0x00004000 /* PM */ +#define CS_TMP 0x10000000 /* tmp debug mask bit */ + +#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend +#define CS_IOCTL_CMD_RESUME 0x2 // resume + +#if CSDEBUG +static unsigned long cs_debuglevel=1; /* levels range from 1-9 */ +MODULE_PARM(cs_debuglevel, "i"); +static unsigned long cs_debugmask=CS_INIT | CS_ERROR; /* use CS_DBGOUT with various mask values */ +MODULE_PARM(cs_debugmask, "i"); +#endif +static unsigned long hercules_egpio_disable=0; /* if non-zero set all EGPIO to 0 */ +MODULE_PARM(hercules_egpio_disable, "i"); +static unsigned long initdelay=700; /* PM delay in millisecs */ +MODULE_PARM(initdelay, "i"); +static unsigned long powerdown=-1; /* turn on/off powerdown processing in driver */ +MODULE_PARM(powerdown, "i"); +#define DMABUF_DEFAULTORDER 3 +static unsigned long defaultorder=DMABUF_DEFAULTORDER; +MODULE_PARM(defaultorder, "i"); + +static int external_amp; +MODULE_PARM(external_amp, "i"); +static int thinkpad; +MODULE_PARM(thinkpad, "i"); + +/* +* set the powerdown module parm to 0 to disable all +* powerdown. also set thinkpad to 1 to disable powerdown, +* but also to enable the clkrun functionality. +*/ +static unsigned cs_powerdown=1; +static unsigned cs_laptop_wait=1; + +/* An instance of the 4610 channel */ +struct cs_channel +{ + int used; + int num; + void *state; +}; + +#define CS46XX_MAJOR_VERSION "1" +#define CS46XX_MINOR_VERSION "28" + +#ifdef __ia64__ +#define CS46XX_ARCH "64" //architecture key +#else +#define CS46XX_ARCH "32" //architecture key +#endif + +struct list_head cs46xx_devs = { &cs46xx_devs, &cs46xx_devs }; + +/* magic numbers to protect our data structures */ +#define CS_CARD_MAGIC 0x43525553 /* "CRUS" */ +#define CS_STATE_MAGIC 0x4c4f4749 /* "LOGI" */ +#define NR_HW_CH 3 + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +/* "software" or virtual channel, an instance of opened /dev/dsp */ +struct cs_state { + unsigned int magic; + struct cs_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; + + struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable; + + /* hardware channel */ + struct cs_channel *channel; + int pringbuf; /* Software ring slot */ + void *pbuf; /* 4K hardware DMA buffer */ + + /* OSS buffer management stuff */ + void *rawbuf; + dma_addr_t dma_handle; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned divisor; + unsigned type; + void *tmpbuff; /* tmp buffer for sample conversions */ + dma_addr_t dmaaddr; + dma_addr_t dmaaddr_tmpbuff; + unsigned buforder_tmpbuff; /* Log base 2 of size in bytes.. */ + + /* 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 comsumed or been generated by dma machine */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + unsigned blocks; /* total blocks */ + + unsigned error; /* number of over/underruns */ + unsigned underrun; /* underrun pending before next write has occurred */ + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned SGok:1; + unsigned update_flag; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dmabuf; + /* Guard against mmap/write/read races */ + struct semaphore sem; +}; + +struct cs_card { + struct cs_channel channel[2]; + unsigned int magic; + + /* We keep cs461x cards in a linked list */ + struct cs_card *next; + + /* The cs461x has a certain amount of cross channel interaction + so we use a single per card lock */ + spinlock_t lock; + + /* mixer use count */ + atomic_t mixer_use_cnt; + + /* PCI device stuff */ + struct pci_dev * pci_dev; + struct list_head list; + + unsigned int pctl, cctl; /* Hardware DMA flag sets */ + + /* soundcore stuff */ + int dev_audio; + int dev_midi; + + /* structures for abstraction of hardware facilities, codecs, banks and channels*/ + struct ac97_codec *ac97_codec[NR_AC97]; + struct cs_state *states[2]; + + u16 ac97_features; + + int amplifier; /* Amplifier control */ + void (*amplifier_ctrl)(struct cs_card *, int); + void (*amp_init)(struct cs_card *); + + int active; /* Active clocking */ + void (*active_ctrl)(struct cs_card *, int); + + /* hardware resources */ + unsigned long ba0_addr; + unsigned long ba1_addr; + u32 irq; + + /* mappings */ + void *ba0; + union + { + struct + { + u8 *data0; + u8 *data1; + u8 *pmem; + u8 *reg; + } name; + u8 *idx[4]; + } ba1; + + /* Function support */ + struct cs_channel *(*alloc_pcm_channel)(struct cs_card *); + struct cs_channel *(*alloc_rec_pcm_channel)(struct cs_card *); + void (*free_pcm_channel)(struct cs_card *, int chan); + + /* /dev/midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t open_wait; + wait_queue_head_t iwait; + wait_queue_head_t owait; + spinlock_t lock; + unsigned char ibuf[CS_MIDIINBUF]; + unsigned char obuf[CS_MIDIOUTBUF]; + mode_t open_mode; + struct semaphore open_sem; + } midi; + struct cs46xx_pm pm; +}; + +static int cs_open_mixdev(struct inode *inode, struct file *file); +static int cs_release_mixdev(struct inode *inode, struct file *file); +static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg); +static int cs_hardware_init(struct cs_card *card); +static int cs46xx_powerup(struct cs_card *card, unsigned int type); +static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag); +static void cs461x_clear_serial_FIFOs(struct cs_card *card, int type); +static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state); +static int cs46xx_resume_tbl(struct pci_dev *pcidev); + +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; +} + +#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) +#define SOUND_MIXER_CS_APM _SIOWR('M',124, int) + +void printioctl(unsigned int x) +{ + unsigned int i; + unsigned char vidx; + /* these values are incorrect for the ac97 driver, fix. + * 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: ") ); + break; + case SOUND_MIXER_CS_GETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGLEVEL: ") ); + break; + case SOUND_MIXER_CS_SETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGMASK: ") ); + break; + case SOUND_MIXER_CS_SETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGLEVEL: ") ); + break; + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION: ") ); + break; + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC: ") ); + break; + case SNDCTL_DSP_SETDUPLEX: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX: ") ); + break; + case SNDCTL_DSP_GETCAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS: ") ); + break; + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET: ") ); + break; + case SNDCTL_DSP_SPEED: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED: ") ); + break; + case SNDCTL_DSP_STEREO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO: ") ); + break; + case SNDCTL_DSP_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS: ") ); + break; + case SNDCTL_DSP_GETFMTS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS: ") ); + break; + case SNDCTL_DSP_SETFMT: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT: ") ); + break; + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST: ") ); + break; + case SNDCTL_DSP_GETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER: ") ); + break; + case SNDCTL_DSP_SETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER: ") ); + break; + case SNDCTL_DSP_GETOSPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE: ") ); + break; + case SNDCTL_DSP_GETISPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE: ") ); + break; + case SNDCTL_DSP_NONBLOCK: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK: ") ); + break; + case SNDCTL_DSP_GETODELAY: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY: ") ); + break; + case SNDCTL_DSP_GETIPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR: ") ); + break; + case SNDCTL_DSP_GETOPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR: ") ); + break; + case SNDCTL_DSP_GETBLKSIZE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE: ") ); + break; + case SNDCTL_DSP_SETFRAGMENT: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFRAGMENT: ") ); + break; + case SNDCTL_DSP_SUBDIVIDE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE: ") ); + break; + case SOUND_PCM_READ_RATE: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE: ") ); + break; + case SOUND_PCM_READ_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_CHANNELS: ") ); + break; + case SOUND_PCM_READ_BITS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS: ") ); + break; + case SOUND_PCM_WRITE_FILTER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_WRITE_FILTER: ") ); + break; + case SNDCTL_DSP_SETSYNCRO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO: ") ); + break; + case SOUND_PCM_READ_FILTER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER: ") ); + break; + + case SOUND_MIXER_PRIVATE1: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1: ") ); + break; + case SOUND_MIXER_PRIVATE2: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2: ") ); + break; + case SOUND_MIXER_PRIVATE3: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3: ") ); + break; + case SOUND_MIXER_PRIVATE4: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4: ") ); + break; + case SOUND_MIXER_PRIVATE5: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5: ") ); + break; + case SOUND_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO: ") ); + break; + case SOUND_OLD_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO: ") ); + break; + + default: + switch (_IOC_NR(x)) + { + case SOUND_MIXER_VOLUME: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_VOLUME: ") ); + break; + case SOUND_MIXER_SPEAKER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SPEAKER: ") ); + break; + case SOUND_MIXER_RECLEV: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECLEV: ") ); + break; + case SOUND_MIXER_MIC: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_MIC: ") ); + break; + case SOUND_MIXER_SYNTH: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SYNTH: ") ); + break; + case SOUND_MIXER_RECSRC: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECSRC: ") ); + break; + case SOUND_MIXER_DEVMASK: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_DEVMASK: ") ); + break; + case SOUND_MIXER_RECMASK: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECMASK: ") ); + break; + case SOUND_MIXER_STEREODEVS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_STEREODEVS: ") ); + break; + case SOUND_MIXER_CAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:") ); + 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 ",x,i) ); + } + else + { + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d ", + x,i) ); + } + break; + } + } + CS_DBGOUT(CS_IOCTL, 4, printk("command = 0x%x IOC_NR=%d\n",x, _IOC_NR(x)) ); +} +#endif + +/* + * common I/O routines + */ + +static void cs461x_poke(struct cs_card *codec, unsigned long reg, unsigned int val) +{ + writel(val, codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff)); +} + +static unsigned int cs461x_peek(struct cs_card *codec, unsigned long reg) +{ + return readl(codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff)); +} + +static void cs461x_pokeBA0(struct cs_card *codec, unsigned long reg, unsigned int val) +{ + writel(val, codec->ba0+reg); +} + +static unsigned int cs461x_peekBA0(struct cs_card *codec, unsigned long reg) +{ + return readl(codec->ba0+reg); +} + + +static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg); +static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); + +static struct cs_channel *cs_alloc_pcm_channel(struct cs_card *card) +{ + if(card->channel[1].used==1) + return NULL; + card->channel[1].used=1; + card->channel[1].num=1; + return &card->channel[1]; +} + +static struct cs_channel *cs_alloc_rec_pcm_channel(struct cs_card *card) +{ + if(card->channel[0].used==1) + return NULL; + card->channel[0].used=1; + card->channel[0].num=0; + return &card->channel[0]; +} + +static void cs_free_pcm_channel(struct cs_card *card, int channel) +{ + card->channel[channel].state = NULL; + card->channel[channel].used=0; +} + +/* + * setup a divisor value to help with conversion from + * 16bit Stereo, down to 8bit stereo/mono or 16bit mono. + * assign a divisor of 1 if using 16bit Stereo as that is + * the only format that the static image will capture. + */ +static void cs_set_divisor(struct dmabuf *dmabuf) +{ + if(dmabuf->type == CS_TYPE_DAC) + dmabuf->divisor = 1; + else if( !(dmabuf->fmt & CS_FMT_STEREO) && + (dmabuf->fmt & CS_FMT_16BIT)) + dmabuf->divisor = 2; + else if( (dmabuf->fmt & CS_FMT_STEREO) && + !(dmabuf->fmt & CS_FMT_16BIT)) + dmabuf->divisor = 2; + else if( !(dmabuf->fmt & CS_FMT_STEREO) && + !(dmabuf->fmt & CS_FMT_16BIT)) + dmabuf->divisor = 4; + else + dmabuf->divisor = 1; + + CS_DBGOUT(CS_PARMS | CS_FUNCTION, 8, printk( + "cs46xx: cs_set_divisor()- %s %d\n", + (dmabuf->type == CS_TYPE_ADC) ? "ADC" : "DAC", + dmabuf->divisor) ); +} + +/* +* mute some of the more prevalent registers to avoid popping. +*/ +static void cs_mute(struct cs_card *card, int state) +{ + struct ac97_codec *dev=card->ac97_codec[0]; + + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()+ %s\n", + (state == CS_TRUE) ? "Muting" : "UnMuting") ); + + if(state == CS_TRUE) + { + /* + * fix pops when powering up on thinkpads + */ + card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev, + (u8)BA0_AC97_MASTER_VOLUME); + card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_HEADPHONE_VOLUME); + card->pm.u32AC97_master_volume_mono = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_MASTER_VOLUME_MONO); + card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_PCM_OUT_VOLUME); + + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000); + } + else + { + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, card->pm.u32AC97_master_volume); + cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, card->pm.u32AC97_headphone_volume); + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, card->pm.u32AC97_master_volume_mono); + cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, card->pm.u32AC97_pcm_out_volume); + } + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()-\n")); +} + +/* set playback sample rate */ +static unsigned int cs_set_dac_rate(struct cs_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int tmp1, tmp2; + unsigned int phiIncr; + unsigned int correctionPerGOF, correctionPerSec; + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()+ %d\n",rate) ); + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M + * GOF_PER_SEC * correctionPerGOF + * + * i.e. + * + * phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + */ + tmp1 = rate << 16; + phiIncr = tmp1 / 48000; + tmp1 -= phiIncr * 48000; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / 48000; + phiIncr += tmp2; + tmp1 -= tmp2 * 48000; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + + /* + * Fill in the SampleRateConverter control block. + */ + + spin_lock_irqsave(&state->card->lock, flags); + cs461x_poke(state->card, BA1_PSRC, + ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); + cs461x_poke(state->card, BA1_PPI, phiIncr); + spin_unlock_irqrestore(&state->card->lock, flags); + dmabuf->rate = rate; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()- %d\n",rate) ); + return rate; +} + +/* set recording sample rate */ +static unsigned int cs_set_adc_rate(struct cs_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned int phiIncr, coeffIncr, tmp1, tmp2; + unsigned int correctionPerGOF, correctionPerSec, initialDelay; + unsigned int frameGroupLength, cnt; + unsigned long flags; + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()+ %d\n",rate) ); + + /* + * We can only decimate by up to a factor of 1/9th the hardware rate. + * Correct the value if an attempt is made to stray outside that limit. + */ + if ((rate * 9) < 48000) + rate = 48000 / 9; + + /* + * We can not capture at at rate greater than the Input Rate (48000). + * Return an error if an attempt is made to stray outside that limit. + */ + if (rate > 48000) + rate = 48000; + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * coeffIncr = -floor((Fs,out * 2^23) / Fs,in) + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - + * GOF_PER_SEC * correctionPerGOF + * initialDelay = ceil((24 * Fs,in) / Fs,out) + * + * i.e. + * + * coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in)) + * phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + * initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out) + */ + + tmp1 = rate << 16; + coeffIncr = tmp1 / 48000; + tmp1 -= coeffIncr * 48000; + tmp1 <<= 7; + coeffIncr <<= 7; + coeffIncr += tmp1 / 48000; + coeffIncr ^= 0xFFFFFFFF; + coeffIncr++; + tmp1 = 48000 << 16; + phiIncr = tmp1 / rate; + tmp1 -= phiIncr * rate; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / rate; + phiIncr += tmp2; + tmp1 -= tmp2 * rate; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + initialDelay = ((48000 * 24) + rate - 1) / rate; + + /* + * Fill in the VariDecimate control block. + */ + spin_lock_irqsave(&card->lock, flags); + cs461x_poke(card, BA1_CSRC, + ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); + cs461x_poke(card, BA1_CCI, coeffIncr); + cs461x_poke(card, BA1_CD, + (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80); + cs461x_poke(card, BA1_CPI, phiIncr); + spin_unlock_irqrestore(&card->lock, flags); + + /* + * Figure out the frame group length for the write back task. Basically, + * this is just the factors of 24000 (2^6*3*5^3) that are not present in + * the output sample rate. + */ + frameGroupLength = 1; + for (cnt = 2; cnt <= 64; cnt *= 2) { + if (((rate / cnt) * cnt) != rate) + frameGroupLength *= 2; + } + if (((rate / 3) * 3) != rate) { + frameGroupLength *= 3; + } + for (cnt = 5; cnt <= 125; cnt *= 5) { + if (((rate / cnt) * cnt) != rate) + frameGroupLength *= 5; + } + + /* + * Fill in the WriteBack control block. + */ + spin_lock_irqsave(&card->lock, flags); + cs461x_poke(card, BA1_CFG1, frameGroupLength); + cs461x_poke(card, BA1_CFG2, (0x00800000 | frameGroupLength)); + cs461x_poke(card, BA1_CCST, 0x0000FFFF); + cs461x_poke(card, BA1_CSPB, ((65536 * rate) / 24000)); + cs461x_poke(card, (BA1_CSPB + 4), 0x0000FFFF); + spin_unlock_irqrestore(&card->lock, flags); + dmabuf->rate = rate; + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()- %d\n",rate) ); + return rate; +} + +/* prepare channel attributes for playback */ +static void cs_play_setup(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned int tmp, Count, playFormat; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()+\n") ); + cs461x_poke(card, BA1_PVOL, 0x80008000); + if(!dmabuf->SGok) + cs461x_poke(card, BA1_PBA, virt_to_bus(dmabuf->pbuf)); + + Count = 4; + playFormat=cs461x_peek(card, BA1_PFIE); + if ((dmabuf->fmt & CS_FMT_STEREO)) { + playFormat &= ~DMA_RQ_C2_AC_MONO_TO_STEREO; + Count *= 2; + } + else + playFormat |= DMA_RQ_C2_AC_MONO_TO_STEREO; + + if ((dmabuf->fmt & CS_FMT_16BIT)) { + playFormat &= ~(DMA_RQ_C2_AC_8_TO_16_BIT + | DMA_RQ_C2_AC_SIGNED_CONVERT); + Count *= 2; + } + else + playFormat |= (DMA_RQ_C2_AC_8_TO_16_BIT + | DMA_RQ_C2_AC_SIGNED_CONVERT); + + cs461x_poke(card, BA1_PFIE, playFormat); + + tmp = cs461x_peek(card, BA1_PDTC); + tmp &= 0xfffffe00; + cs461x_poke(card, BA1_PDTC, tmp | --Count); + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()-\n") ); + +} + +struct InitStruct +{ + u32 long off; + u32 long val; +} InitArray[] = { {0x00000040, 0x3fc0000f}, + {0x0000004c, 0x04800000}, + + {0x000000b3, 0x00000780}, + {0x000000b7, 0x00000000}, + {0x000000bc, 0x07800000}, + + {0x000000cd, 0x00800000}, + }; + +/* + * "SetCaptureSPValues()" -- Initialize record task values before each + * capture startup. + */ +void SetCaptureSPValues(struct cs_card *card) +{ + unsigned i, offset; + CS_DBGOUT(CS_FUNCTION, 8, printk("cs46xx: SetCaptureSPValues()+\n") ); + for(i=0; icard; + struct dmabuf *dmabuf = &state->dmabuf; + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()+\n") ); + + SetCaptureSPValues(card); + + /* + * set the attenuation to 0dB + */ + cs461x_poke(card, BA1_CVOL, 0x80008000); + + /* + * set the physical address of the capture buffer into the SP + */ + cs461x_poke(card, BA1_CBA, virt_to_bus(dmabuf->rawbuf)); + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()-\n") ); +} + + +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ + +static inline unsigned cs_get_dma_addr(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 offset; + + if ( (!(dmabuf->enable & DAC_RUNNING)) && + (!(dmabuf->enable & ADC_RUNNING) ) ) + { + CS_DBGOUT(CS_ERROR, 2, printk( + "cs46xx: ERROR cs_get_dma_addr(): not enabled \n") ); + return 0; + } + + /* + * ganularity is byte boundry, good part. + */ + if(dmabuf->enable & DAC_RUNNING) + { + offset = cs461x_peek(state->card, BA1_PBA); + } + else /* ADC_RUNNING must be set */ + { + offset = cs461x_peek(state->card, BA1_CBA); + } + CS_DBGOUT(CS_PARMS | CS_FUNCTION, 9, + printk("cs46xx: cs_get_dma_addr() %d\n",offset) ); + offset = (u32)bus_to_virt((unsigned long)offset) - (u32)dmabuf->rawbuf; + CS_DBGOUT(CS_PARMS | CS_FUNCTION, 8, + printk("cs46xx: cs_get_dma_addr()- %d\n",offset) ); + return offset; +} + +static void resync_dma_ptrs(struct cs_state *state) +{ + struct dmabuf *dmabuf; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()+ \n") ); + if(state) + { + dmabuf = &state->dmabuf; + dmabuf->hwptr=dmabuf->swptr = 0; + dmabuf->pringbuf = 0; + } + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()- \n") ); +} + +/* Stop recording (lock held) */ +static inline void __stop_adc(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned int tmp; + + dmabuf->enable &= ~ADC_RUNNING; + + tmp = cs461x_peek(card, BA1_CCTL); + tmp &= 0xFFFF0000; + cs461x_poke(card, BA1_CCTL, tmp ); +} + +static void stop_adc(struct cs_state *state) +{ + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_adc()+ \n") ); + spin_lock_irqsave(&state->card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&state->card->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_adc()- \n") ); +} + +static void start_adc(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned long flags; + unsigned int tmp; + + spin_lock_irqsave(&card->lock, flags); + if (!(dmabuf->enable & ADC_RUNNING) && + ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) + && dmabuf->ready) && + ((card->pm.flags & CS46XX_PM_IDLE) || + (card->pm.flags & CS46XX_PM_RESUMED)) ) + { + dmabuf->enable |= ADC_RUNNING; + cs_set_divisor(dmabuf); + tmp = cs461x_peek(card, BA1_CCTL); + tmp &= 0xFFFF0000; + tmp |= card->cctl; + CS_DBGOUT(CS_FUNCTION, 2, printk( + "cs46xx: start_adc() poke 0x%x \n",tmp) ); + cs461x_poke(card, BA1_CCTL, tmp); + } + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop playback (lock held) */ +static inline void __stop_dac(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned int tmp; + + dmabuf->enable &= ~DAC_RUNNING; + + tmp=cs461x_peek(card, BA1_PCTL); + tmp&=0xFFFF; + cs461x_poke(card, BA1_PCTL, tmp); +} + +static void stop_dac(struct cs_state *state) +{ + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_dac()+ \n") ); + spin_lock_irqsave(&state->card->lock, flags); + __stop_dac(state); + spin_unlock_irqrestore(&state->card->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_dac()- \n") ); +} + +static void start_dac(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned long flags; + int tmp; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()+ \n") ); + spin_lock_irqsave(&card->lock, flags); + if (!(dmabuf->enable & DAC_RUNNING) && + ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) && + ((card->pm.flags & CS46XX_PM_IDLE) || + (card->pm.flags & CS46XX_PM_RESUMED)) ) + { + dmabuf->enable |= DAC_RUNNING; + tmp = cs461x_peek(card, BA1_PCTL); + tmp &= 0xFFFF; + tmp |= card->pctl; + CS_DBGOUT(CS_PARMS, 6, printk( + "cs46xx: start_dac() poke card=0x%.08x tmp=0x%.08x addr=0x%.08x \n", + (unsigned)card, (unsigned)tmp, + (unsigned)card->ba1.idx[(BA1_PCTL >> 16) & 3]+(BA1_PCTL&0xffff) ) ); + cs461x_poke(card, BA1_PCTL, tmp); + } + spin_unlock_irqrestore(&card->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()- \n") ); +} + +#define DMABUF_MINORDER 1 + +/* + * allocate DMA buffer, playback and recording buffers are separate. + */ +static int alloc_dmabuf(struct cs_state *state) +{ + + struct cs_card *card=state->card; + struct dmabuf *dmabuf = &state->dmabuf; + void *rawbuf = NULL; + void *tmpbuff = NULL; + int order; + struct page *map, *mapend; + unsigned long df; + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->SGok = 0; +/* +* check for order within limits, but do not overwrite value. +*/ + if((defaultorder > 1) && (defaultorder < 12)) + df = defaultorder; + else + df = 2; + + for (order = df; order >= DMABUF_MINORDER; order--) + if ( (rawbuf = (void *) pci_alloc_consistent( + card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr))) + break; + if (!rawbuf) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs46xx: alloc_dmabuf(): unable to allocate rawbuf\n")); + return -ENOMEM; + } + dmabuf->buforder = order; + dmabuf->rawbuf = rawbuf; + // Now mark the pages as reserved; otherwise the + // remap_page_range() in cs46xx_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(dmabuf->rawbuf + + (PAGE_SIZE << dmabuf->buforder) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; 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) ); + +/* +* only allocate the conversion buffer for the ADC +*/ + if(dmabuf->type == CS_TYPE_DAC) + { + dmabuf->tmpbuff = NULL; + dmabuf->buforder_tmpbuff = 0; + return 0; + } +/* + * now the temp buffer for 16/8 conversions + */ + + tmpbuff = (void *) pci_alloc_consistent( + card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr_tmpbuff); + + if (!tmpbuff) + return -ENOMEM; + CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, tmpbuff) ); + + dmabuf->tmpbuff = tmpbuff; + dmabuf->buforder_tmpbuff = order; + + // Now mark the pages as reserved; otherwise the + // remap_page_range() in cs46xx_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(dmabuf->tmpbuff + + (PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++) + cs4x_mem_map_reserve(map); + return 0; +} + +/* free DMA buffer */ +static void dealloc_dmabuf(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct page *map, *mapend; + + if (dmabuf->rawbuf) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = virt_to_page(dmabuf->rawbuf + + (PAGE_SIZE << dmabuf->buforder) - 1); + for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) + cs4x_mem_map_unreserve(map); + free_dmabuf(state->card, dmabuf); + } + + if (dmabuf->tmpbuff) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = virt_to_page(dmabuf->tmpbuff + + (PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1); + for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++) + cs4x_mem_map_unreserve(map); + free_dmabuf2(state->card, dmabuf); + } + + dmabuf->rawbuf = NULL; + dmabuf->tmpbuff = NULL; + dmabuf->mapped = dmabuf->ready = 0; + dmabuf->SGok = 0; +} + +static int __prog_dmabuf(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long allocated_pages, allocated_bytes; + unsigned long tmp1, tmp2, fmt=0; + unsigned long *ptmp = (unsigned long *) dmabuf->pbuf; + unsigned long SGarray[9], nSGpages=0; + int ret; + + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()+ \n")); +/* + * check for CAPTURE and use only non-sg for initial release + */ + if(dmabuf->type == CS_TYPE_ADC) + { + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() ADC\n")); + /* + * add in non-sg support for capture. + */ + spin_lock_irqsave(&state->card->lock, flags); + /* add code to reset the rawbuf memory. TRW */ + resync_dma_ptrs(state); + dmabuf->total_bytes = dmabuf->blocks = 0; + dmabuf->count = dmabuf->error = dmabuf->underrun = 0; + + dmabuf->SGok = 0; + + spin_unlock_irqrestore(&state->card->lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf || !dmabuf->tmpbuff) + if ((ret = alloc_dmabuf(state))) + return ret; + /* + * static image only supports 16Bit signed, stereo - hard code fmt + */ + fmt = CS_FMT_16BIT | CS_FMT_STEREO; + + dmabuf->numfrag = 2; + dmabuf->fragsize = 2048; + dmabuf->fragsamples = 2048 >> sample_shift[fmt]; + dmabuf->dmasize = 4096; + dmabuf->fragshift = 11; + + memset(dmabuf->rawbuf, (fmt & CS_FMT_16BIT) ? 0 : 0x80, + dmabuf->dmasize); + memset(dmabuf->tmpbuff, (fmt & CS_FMT_16BIT) ? 0 : 0x80, + PAGE_SIZE<buforder_tmpbuff); + + /* + * Now set up the ring + */ + + spin_lock_irqsave(&state->card->lock, flags); + cs_rec_setup(state); + spin_unlock_irqrestore(&state->card->lock, flags); + + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + + CS_DBGOUT(CS_PARMS, 4, printk( + "cs46xx: prog_dmabuf(): CAPTURE rate=%d fmt=0x%x numfrag=%d " + "fragsize=%d dmasize=%d\n", + dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize) ); + + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- 0 \n")); + return 0; + } + else if (dmabuf->type == CS_TYPE_DAC) + { + /* + * Must be DAC + */ + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() DAC\n")); + spin_lock_irqsave(&state->card->lock, flags); + resync_dma_ptrs(state); + dmabuf->total_bytes = dmabuf->blocks = 0; + dmabuf->count = dmabuf->error = dmabuf->underrun = 0; + + dmabuf->SGok = 0; + + spin_unlock_irqrestore(&state->card->lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf) + if ((ret = alloc_dmabuf(state))) + return ret; + + allocated_pages = 1 << dmabuf->buforder; + allocated_bytes = allocated_pages*PAGE_SIZE; + + if(allocated_pages < 2) + { + CS_DBGOUT(CS_FUNCTION, 4, printk( + "cs46xx: prog_dmabuf() Error: allocated_pages too small (%d)\n", + (unsigned)allocated_pages)); + return -ENOMEM; + } + + /* Use all the pages allocated, fragsize 4k. */ + /* Use 'pbuf' for S/G page map table. */ + dmabuf->SGok = 1; /* Use S/G. */ + + nSGpages = allocated_bytes/4096; /* S/G pages always 4k. */ + + /* Set up S/G variables. */ + *ptmp = virt_to_bus(dmabuf->rawbuf); + *(ptmp+1) = 0x00000008; + for(tmp1= 1; tmp1 < nSGpages; tmp1++) { + *(ptmp+2*tmp1) = virt_to_bus( (dmabuf->rawbuf)+4096*tmp1); + if( tmp1 == nSGpages-1) + tmp2 = 0xbfff0000; + else + tmp2 = 0x80000000+8*(tmp1+1); + *(ptmp+2*tmp1+1) = tmp2; + } + SGarray[0] = 0x82c0200d; + SGarray[1] = 0xffff0000; + SGarray[2] = *ptmp; + SGarray[3] = 0x00010600; + SGarray[4] = *(ptmp+2); + SGarray[5] = 0x80000010; + SGarray[6] = *ptmp; + SGarray[7] = *(ptmp+2); + SGarray[8] = (virt_to_bus(dmabuf->pbuf) & 0xffff000) | 0x10; + + if (dmabuf->SGok) { + dmabuf->numfrag = nSGpages; + dmabuf->fragsize = 4096; + dmabuf->fragsamples = 4096 >> sample_shift[dmabuf->fmt]; + dmabuf->fragshift = 12; + dmabuf->dmasize = dmabuf->numfrag*4096; + } + else { + SGarray[0] = 0xf2c0000f; + SGarray[1] = 0x00000200; + SGarray[2] = 0; + SGarray[3] = 0x00010600; + SGarray[4]=SGarray[5]=SGarray[6]=SGarray[7]=SGarray[8] = 0; + dmabuf->numfrag = 2; + dmabuf->fragsize = 2048; + dmabuf->fragsamples = 2048 >> sample_shift[dmabuf->fmt]; + dmabuf->dmasize = 4096; + dmabuf->fragshift = 11; + } + for(tmp1 = 0; tmp1 < sizeof(SGarray)/4; tmp1++) + cs461x_poke( state->card, BA1_PDTC+tmp1*4, SGarray[tmp1]); + + memset(dmabuf->rawbuf, (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, + dmabuf->dmasize); + + /* + * Now set up the ring + */ + + spin_lock_irqsave(&state->card->lock, flags); + cs_play_setup(state); + spin_unlock_irqrestore(&state->card->lock, flags); + + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + + CS_DBGOUT(CS_PARMS, 4, printk( + "cs46xx: prog_dmabuf(): PLAYBACK rate=%d fmt=0x%x numfrag=%d " + "fragsize=%d dmasize=%d\n", + dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize) ); + + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- \n")); + return 0; + } + else + { + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- Invalid Type %d\n", + dmabuf->type)); + } + return 1; +} + +static int prog_dmabuf(struct cs_state *state) +{ + int ret; + + down(&state->sem); + ret = __prog_dmabuf(state); + up(&state->sem); + + return ret; +} + +static void cs_clear_tail(struct cs_state *state) +{ +} + +static int drain_dac(struct cs_state *state, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card=state->card; + unsigned long flags; + unsigned long tmo; + int count; + + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()+ \n")); + if (dmabuf->mapped || !dmabuf->ready) + { + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- 0, not ready\n")); + return 0; + } + + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + /* It seems that we have to set the current state to TASK_INTERRUPTIBLE + every time to make the process really go to sleep */ + current->state = TASK_INTERRUPTIBLE; + + spin_lock_irqsave(&state->card->lock, flags); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (count <= 0) + break; + + if (signal_pending(current)) + break; + + if (nonblock) { + remove_wait_queue(&dmabuf->wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + + tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; + tmo >>= sample_shift[dmabuf->fmt]; + tmo += (2048*HZ)/dmabuf->rate; + + if (!schedule_timeout(tmo ? tmo : 1) && tmo){ + printk(KERN_ERR "cs46xx: drain_dac, dma timeout? %d\n", count); + break; + } + } + remove_wait_queue(&dmabuf->wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + { + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- -ERESTARTSYS\n")); + /* + * set to silence and let that clear the fifos. + */ + cs461x_clear_serial_FIFOs(card, CS_TYPE_DAC); + return -ERESTARTSYS; + } + + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- 0\n")); + return 0; +} + + +/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ +static void cs_update_ptr(struct cs_card *card, int wake) +{ + struct cs_state *state; + struct dmabuf *dmabuf; + unsigned hwptr; + int diff; + + /* error handling and process wake up for ADC */ + state = card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + if (dmabuf->enable & ADC_RUNNING) { + /* update hardware pointer */ + hwptr = cs_get_dma_addr(state); + + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + CS_DBGOUT(CS_PARMS, 9, printk( + "cs46xx: cs_update_ptr()+ ADC hwptr=%d diff=%d\n", + hwptr,diff) ); + 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 >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else + { + if (wake && dmabuf->count > 0) + wake_up(&dmabuf->wait); + } + } + } + +/* + * Now the DAC + */ + state = card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + /* error handling and process wake up for DAC */ + if (dmabuf->enable & DAC_RUNNING) { + /* update hardware pointer */ + hwptr = cs_get_dma_addr(state); + + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + CS_DBGOUT(CS_PARMS, 9, printk( + "cs46xx: cs_update_ptr()+ DAC hwptr=%d diff=%d\n", + hwptr,diff) ); + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + if (dmabuf->mapped) { + dmabuf->count += diff; + if (wake && dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + /* + * other drivers use fragsize, but don't see any sense + * in that, since dmasize is the buffer asked for + * via mmap. + */ + if( dmabuf->count > dmabuf->dmasize) + dmabuf->count &= dmabuf->dmasize-1; + } else { + dmabuf->count -= diff; + /* + * backfill with silence and clear out the last + * "diff" number of bytes. + */ + if(hwptr >= diff) + { + memset(dmabuf->rawbuf + hwptr - diff, + (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, diff); + } + else + { + memset(dmabuf->rawbuf, + (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, + (unsigned)hwptr); + memset((void *)((unsigned)dmabuf->rawbuf + + dmabuf->dmasize + hwptr - diff), + (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, + diff - hwptr); + } + + if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs46xx: ERROR DAC count<0 or count > dmasize (%d)\n", + dmabuf->count)); + /* + * buffer underrun or buffer overrun, reset the + * count of bytes written back to 0. + */ + if(dmabuf->count < 0) + dmabuf->underrun=1; + dmabuf->count = 0; + dmabuf->error++; + } + if (wake && dmabuf->count < (signed)dmabuf->dmasize/2) + wake_up(&dmabuf->wait); + } + } + } +} + + +/* hold spinlock for the following! */ +static void cs_handle_midi(struct cs_card *card) +{ + unsigned char ch; + int wake; + unsigned temp1; + + wake = 0; + while (!(cs461x_peekBA0(card, BA0_MIDSR) & MIDSR_RBE)) { + ch = cs461x_peekBA0(card, BA0_MIDRP); + if (card->midi.icnt < CS_MIDIINBUF) { + card->midi.ibuf[card->midi.iwr] = ch; + card->midi.iwr = (card->midi.iwr + 1) % CS_MIDIINBUF; + card->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&card->midi.iwait); + wake = 0; + while (!(cs461x_peekBA0(card, BA0_MIDSR) & MIDSR_TBF) && card->midi.ocnt > 0) { + temp1 = ( card->midi.obuf[card->midi.ord] ) & 0x000000ff; + cs461x_pokeBA0(card, BA0_MIDWP,temp1); + card->midi.ord = (card->midi.ord + 1) % CS_MIDIOUTBUF; + card->midi.ocnt--; + if (card->midi.ocnt < CS_MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&card->midi.owait); +} + +static void cs_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cs_card *card = (struct cs_card *)dev_id; + /* Single channel card */ + struct cs_state *recstate = card->channel[0].state; + struct cs_state *playstate = card->channel[1].state; + u32 status; + + CS_DBGOUT(CS_INTERRUPT, 9, printk("cs46xx: cs_interrupt()+ \n")); + + spin_lock(&card->lock); + + status = cs461x_peekBA0(card, BA0_HISR); + + if ((status & 0x7fffffff) == 0) + { + cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV); + spin_unlock(&card->lock); + return; + } + + /* + * check for playback or capture interrupt only + */ + if( ((status & HISR_VC0) && playstate && playstate->dmabuf.ready) || + (((status & HISR_VC1) && recstate && recstate->dmabuf.ready)) ) + { + CS_DBGOUT(CS_INTERRUPT, 8, printk( + "cs46xx: cs_interrupt() interrupt bit(s) set (0x%x)\n",status)); + cs_update_ptr(card, CS_TRUE); + } + + if( status & HISR_MIDI ) + cs_handle_midi(card); + + /* clear 'em */ + cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV); + spin_unlock(&card->lock); + CS_DBGOUT(CS_INTERRUPT, 9, printk("cs46xx: cs_interrupt()- \n")); +} + + +/**********************************************************************/ + +static ssize_t cs_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&card->lock, flags); + ptr = card->midi.ird; + cnt = CS_MIDIINBUF - ptr; + if (card->midi.icnt < cnt) + cnt = card->midi.icnt; + spin_unlock_irqrestore(&card->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&card->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, card->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % CS_MIDIINBUF; + spin_lock_irqsave(&card->lock, flags); + card->midi.ird = ptr; + card->midi.icnt -= cnt; + spin_unlock_irqrestore(&card->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + + +static ssize_t cs_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&card->lock, flags); + ptr = card->midi.owr; + cnt = CS_MIDIOUTBUF - ptr; + if (card->midi.ocnt + cnt > CS_MIDIOUTBUF) + cnt = CS_MIDIOUTBUF - card->midi.ocnt; + if (cnt <= 0) + cs_handle_midi(card); + spin_unlock_irqrestore(&card->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&card->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(card->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % CS_MIDIOUTBUF; + spin_lock_irqsave(&card->lock, flags); + card->midi.owr = ptr; + card->midi.ocnt += cnt; + spin_unlock_irqrestore(&card->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&card->lock, flags); + cs_handle_midi(card); + spin_unlock_irqrestore(&card->lock, flags); + } + return ret; +} + + +static unsigned int cs_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &card->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &card->midi.iwait, wait); + spin_lock_irqsave(&card->lock, flags); + if (file->f_flags & FMODE_READ) { + if (card->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (card->midi.ocnt < CS_MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&card->lock, flags); + return mask; +} + + +static int cs_midi_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct cs_card *card=NULL; + unsigned long flags; + struct list_head *entry; + + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + if (card->dev_midi == minor) + break; + } + + if (entry == &cs46xx_devs) + return -ENODEV; + if (!card) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs46xx: cs46xx_midi_open(): Error - unable to find card struct\n")); + return -ENODEV; + } + + file->private_data = card; + /* wait for device to become free */ + down(&card->midi.open_sem); + while (card->midi.open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&card->midi.open_sem); + return -EBUSY; + } + up(&card->midi.open_sem); + interruptible_sleep_on(&card->midi.open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&card->midi.open_sem); + } + spin_lock_irqsave(&card->midi.lock, flags); + if (!(card->midi.open_mode & (FMODE_READ | FMODE_WRITE))) { + card->midi.ird = card->midi.iwr = card->midi.icnt = 0; + card->midi.ord = card->midi.owr = card->midi.ocnt = 0; + card->midi.ird = card->midi.iwr = card->midi.icnt = 0; + cs461x_pokeBA0(card, BA0_MIDCR, 0x0000000f); /* Enable xmit, rcv. */ + cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM); /* Enable interrupts */ + } + if (file->f_mode & FMODE_READ) { + card->midi.ird = card->midi.iwr = card->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + card->midi.ord = card->midi.owr = card->midi.ocnt = 0; + } + spin_unlock_irqrestore(&card->midi.lock, flags); + card->midi.open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE)); + up(&card->midi.open_sem); + MOD_INC_USE_COUNT; /* for 2.2 */ + return 0; +} + + +static int cs_midi_release(struct inode *inode, struct file *file) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + if (file->f_mode & FMODE_WRITE) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&card->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&card->midi.lock, flags); + count = card->midi.ocnt; + spin_unlock_irqrestore(&card->midi.lock, flags); + if (count <= 0) + 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; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "cs46xx: midi timed out??\n"); + } + remove_wait_queue(&card->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&card->midi.open_sem); + card->midi.open_mode &= (~(file->f_mode & (FMODE_READ | FMODE_WRITE))); + up(&card->midi.open_sem); + wake_up(&card->midi.open_wait); + MOD_DEC_USE_COUNT; /* for 2.2 */ + return 0; +} + +/* + * Midi file operations struct. + */ +static /*const*/ struct file_operations cs_midi_fops = { + CS_OWNER CS_THIS_MODULE + llseek: no_llseek, + read: cs_midi_read, + write: cs_midi_write, + poll: cs_midi_poll, + open: cs_midi_open, + release: cs_midi_release, +}; + +/* + * + * CopySamples copies 16-bit stereo signed samples from the source to the + * destination, possibly converting down to unsigned 8-bit and/or mono. + * count specifies the number of output bytes to write. + * + * Arguments: + * + * dst - Pointer to a destination buffer. + * src - Pointer to a source buffer + * count - The number of bytes to copy into the destination buffer. + * fmt - CS_FMT_16BIT and/or CS_FMT_STEREO bits + * dmabuf - pointer to the dma buffer structure + * + * NOTES: only call this routine if the output desired is not 16 Signed Stereo + * + * + */ +static void CopySamples(char *dst, char *src, int count, unsigned fmt, + struct dmabuf *dmabuf) +{ + + s32 s32AudioSample; + s16 *psSrc=(s16 *)src; + s16 *psDst=(s16 *)dst; + u8 *pucDst=(u8 *)dst; + + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: CopySamples()+ ") ); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " dst=0x%x src=0x%x count=%d fmt=0x%x\n", + (unsigned)dst,(unsigned)src,(unsigned)count,(unsigned)fmt) ); + + /* + * See if the data should be output as 8-bit unsigned stereo. + */ + if((fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) + { + /* + * Convert each 16-bit signed stereo sample to 8-bit unsigned + * stereo using rounding. + */ + psSrc = (s16 *)src; + count = count/2; + while(count--) + { + *(pucDst++) = (u8)(((s16)(*psSrc++) + (s16)0x8000) >> 8); + } + } + /* + * See if the data should be output at 8-bit unsigned mono. + */ + else if(!(fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) + { + /* + * Convert each 16-bit signed stereo sample to 8-bit unsigned + * mono using averaging and rounding. + */ + psSrc = (s16 *)src; + count = count/2; + while(count--) + { + s32AudioSample = ((*psSrc)+(*(psSrc + 1)))/2 + (s32)0x80; + if(s32AudioSample > 0x7fff) + s32AudioSample = 0x7fff; + *(pucDst++) = (u8)(((s16)s32AudioSample + (s16)0x8000) >> 8); + psSrc += 2; + } + } + /* + * See if the data should be output at 16-bit signed mono. + */ + else if(!(fmt & CS_FMT_STEREO) && (fmt & CS_FMT_16BIT)) + { + /* + * Convert each 16-bit signed stereo sample to 16-bit signed + * mono using averaging. + */ + psSrc = (s16 *)src; + count = count/2; + while(count--) + { + *(psDst++) = (s16)((*psSrc)+(*(psSrc + 1)))/2; + psSrc += 2; + } + } +} + +/* + * cs_copy_to_user() + * replacement for the standard copy_to_user, to allow for a conversion from + * 16 bit to 8 bit and from stereo to mono, if the record conversion is active. + * The current CS46xx/CS4280 static image only records in 16bit unsigned Stereo, + * so we convert from any of the other format combinations. + */ +static unsigned cs_copy_to_user( + struct cs_state *s, + void *dest, + void *hwsrc, + unsigned cnt, + unsigned *copied) +{ + struct dmabuf *dmabuf = &s->dmabuf; + void *src = hwsrc; /* default to the standard destination buffer addr */ + + CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO + "cs_copy_to_user()+ fmt=0x%x cnt=%d dest=0x%.8x\n", + dmabuf->fmt,(unsigned)cnt,(unsigned)dest) ); + + if(cnt > dmabuf->dmasize) + { + cnt = dmabuf->dmasize; + } + if(!cnt) + { + *copied = 0; + return 0; + } + if(dmabuf->divisor != 1) + { + if(!dmabuf->tmpbuff) + { + *copied = cnt/dmabuf->divisor; + return 0; + } + + CopySamples((char *)dmabuf->tmpbuff, (char *)hwsrc, cnt, + dmabuf->fmt, dmabuf); + src = dmabuf->tmpbuff; + cnt = cnt/dmabuf->divisor; + } + if (copy_to_user(dest, src, cnt)) + { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR + "cs46xx: cs_copy_to_user()- fault dest=0x%x src=0x%x cnt=%d\n", + (unsigned)dest,(unsigned)src,cnt) ); + *copied = 0; + return -EFAULT; + } + *copied = cnt; + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: cs_copy_to_user()- copied bytes is %d \n",cnt) ); + return 0; +} + +/* 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 cs_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct cs_card *card = (struct cs_card *) file->private_data; + struct cs_state *state; + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf; + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned copied=0; + + CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, + printk("cs46xx: cs_read()+ %d\n",count) ); + state = (struct cs_state *)card->states[0]; + if(!state) + return -ENODEV; + dmabuf = &state->dmabuf; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + down(&state->sem); + if (!dmabuf->ready && (ret = __prog_dmabuf(state))) + goto out; + + add_wait_queue(&state->dmabuf.wait, &wait); + while (count > 0) { + while(!(card->pm.flags & CS46XX_PM_IDLE)) + { + schedule(); + if (signal_pending(current)) { + if(!ret) ret = -ERESTARTSYS; + goto out; + } + } + spin_lock_irqsave(&state->card->lock, flags); + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count < cnt) + cnt = dmabuf->count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&state->card->lock, flags); + + if (cnt > (count * dmabuf->divisor)) + cnt = count * dmabuf->divisor; + if (cnt <= 0) { + /* buffer is empty, start the dma machine and wait for data to be + recorded */ + start_adc(state); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + goto out; + } + up(&state->sem); + schedule(); + if (signal_pending(current)) { + if(!ret) ret = -ERESTARTSYS; + goto out; + } + down(&state->sem); + if (dmabuf->mapped) + { + if(!ret) + ret = -ENXIO; + goto out; + } + continue; + } + + CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO + "_read() copy_to cnt=%d count=%d ", cnt,count) ); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " .dmasize=%d .count=%d buffer=0x%.8x ret=%d\n", + dmabuf->dmasize,dmabuf->count,(unsigned)buffer,ret) ); + + if (cs_copy_to_user(state, buffer, + (void *)((unsigned)dmabuf->rawbuf + swptr), cnt, &copied)) + { + if (!ret) ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % dmabuf->dmasize; + spin_lock_irqsave(&card->lock, flags); + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + spin_unlock_irqrestore(&card->lock, flags); + count -= copied; + buffer += copied; + ret += copied; + start_adc(state); + } +out: + up(&state->sem); + remove_wait_queue(&state->dmabuf.wait, &wait); + set_current_state(TASK_RUNNING); + CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, + printk("cs46xx: cs_read()- %d\n",ret) ); + 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 cs_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct cs_card *card = (struct cs_card *) file->private_data; + struct cs_state *state; + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 4, + printk("cs46xx: cs_write called, count = %d\n", count) ); + state = (struct cs_state *)card->states[1]; + if(!state) + return -ENODEV; + dmabuf = &state->dmabuf; + + if (ppos != &file->f_pos) + return -ESPIPE; + + down(&state->sem); + if (dmabuf->mapped) + { + ret = -ENXIO; + goto out; + } + + 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; +/* +* Start the loop to read from the user's buffer and write to the dma buffer. +* check for PM events and underrun/overrun in the loop. +*/ + while (count > 0) { + while(!(card->pm.flags & CS46XX_PM_IDLE)) + { + schedule(); + if (signal_pending(current)) { + if(!ret) ret = -ERESTARTSYS; + goto out; + } + } + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->count < 0) { + /* buffer underrun, we are recovering from sleep_on_timeout, + resync hwptr and swptr */ + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + } + if (dmabuf->underrun) + { + dmabuf->underrun = 0; + dmabuf->hwptr = cs_get_dma_addr(state); + dmabuf->swptr = dmabuf->hwptr; + } + + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count + cnt > dmabuf->dmasize) + cnt = dmabuf->dmasize - dmabuf->count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&state->card->lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + /* buffer is full, start the dma machine and wait for data to be + played */ + start_dac(state); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + goto out; + } + up(&state->sem); + schedule(); + if (signal_pending(current)) { + if(!ret) ret = -ERESTARTSYS; + goto out; + } + down(&state->sem); + if (dmabuf->mapped) + { + if(!ret) + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + goto out; + } + spin_lock_irqsave(&state->card->lock, flags); + swptr = (swptr + cnt) % dmabuf->dmasize; + dmabuf->swptr = swptr; + dmabuf->count += cnt; + if(dmabuf->count > dmabuf->dmasize) + { + CS_DBGOUT(CS_WAVE_WRITE | CS_ERROR, 2, printk( + "cs46xx: cs_write() d->count > dmasize - resetting\n")); + dmabuf->count = dmabuf->dmasize; + } + dmabuf->endcleared = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(state); + } +out: + up(&state->sem); + remove_wait_queue(&state->dmabuf.wait, &wait); + set_current_state(TASK_RUNNING); + + CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 2, + printk("cs46xx: cs_write()- ret=0x%x\n", ret) ); + return ret; +} + +static unsigned int cs_poll(struct file *file, struct poll_table_struct *wait) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + struct dmabuf *dmabuf; + struct cs_state *state; + + unsigned long flags; + unsigned int mask = 0; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()+ \n")); + if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) + { + return -EINVAL; + } + if (file->f_mode & FMODE_WRITE) + { + state = card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + poll_wait(file, &dmabuf->wait, wait); + } + } + if (file->f_mode & FMODE_READ) + { + state = card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + poll_wait(file, &dmabuf->wait, wait); + } + } + + spin_lock_irqsave(&card->lock, flags); + cs_update_ptr(card, CS_FALSE); + if (file->f_mode & FMODE_READ) { + state = card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_mode & FMODE_WRITE) { + state = card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + if (dmabuf->mapped) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)dmabuf->dmasize >= dmabuf->count + + (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + } + spin_unlock_irqrestore(&card->lock, flags); + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()- (0x%x) \n", + mask)); + return mask; +} + +/* + * We let users mmap the ring buffer. Its not the real DMA buffer but + * that side of the code is hidden in the IRQ handling. We do a software + * emulation of DMA from a 64K or so buffer into a 2K FIFO. + * (the hardware probably deserves a moan here but Crystal send me nice + * toys ;)). + */ + +static int cs_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_state *state; + struct dmabuf *dmabuf; + int ret = 0; + unsigned long size; + + CS_DBGOUT(CS_FUNCTION | CS_PARMS, 2, printk("cs46xx: cs_mmap()+ file=0x%x %s %s\n", + (unsigned)file, vma->vm_flags & VM_WRITE ? "VM_WRITE" : "", + vma->vm_flags & VM_READ ? "VM_READ" : "") ); + + if (vma->vm_flags & VM_WRITE) { + state = card->states[1]; + if(state) + { + CS_DBGOUT(CS_OPEN, 2, printk( + "cs46xx: cs_mmap() VM_WRITE - state TRUE prog_dmabuf DAC\n") ); + if ((ret = prog_dmabuf(state)) != 0) + return ret; + } + } else if (vma->vm_flags & VM_READ) { + state = card->states[0]; + if(state) + { + CS_DBGOUT(CS_OPEN, 2, printk( + "cs46xx: cs_mmap() VM_READ - state TRUE prog_dmabuf ADC\n") ); + if ((ret = prog_dmabuf(state)) != 0) + return ret; + } + } else { + CS_DBGOUT(CS_ERROR, 2, printk( + "cs46xx: cs_mmap() return -EINVAL\n") ); + return -EINVAL; + } + +/* + * For now ONLY support playback, but seems like the only way to use + * mmap() is to open an FD with RDWR, just read or just write access + * does not function, get an error back from the kernel. + * Also, QuakeIII opens with RDWR! So, there must be something + * to needing read/write access mapping. So, allow read/write but + * use the DAC only. + */ + state = card->states[1]; + if(!(unsigned)state) + { + ret = -EINVAL; + goto out; + } + + down(&state->sem); + dmabuf = &state->dmabuf; + if (cs4x_pgoff(vma) != 0) + { + ret = -EINVAL; + goto out; + } + size = vma->vm_end - vma->vm_start; + + CS_DBGOUT(CS_PARMS, 2, printk("cs46xx: cs_mmap(): size=%d\n",(unsigned)size) ); + + if (size > (PAGE_SIZE << dmabuf->buforder)) + { + ret = -EINVAL; + goto out; + } + if (remap_page_range(vma, vma->vm_start, virt_to_phys(dmabuf->rawbuf), + size, vma->vm_page_prot)) + { + ret = -EAGAIN; + goto out; + } + dmabuf->mapped = 1; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_mmap()-\n") ); +out: + up(&state->sem); + return ret; +} + +static int cs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_state *state; + struct dmabuf *dmabuf=0; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, valsave, mapped, ret; + + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + mapped = (file->f_mode & FMODE_READ) && dmabuf->mapped; + } + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + mapped |= (file->f_mode & FMODE_WRITE) && dmabuf->mapped; + } + +#if CSDEBUG + printioctl(cmd); +#endif + + switch (cmd) + { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_RESET: + /* FIXME: spin_lock ? */ + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + stop_dac(state); + synchronize_irq(); + dmabuf->ready = 0; + resync_dma_ptrs(state); + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + dmabuf->blocks = 0; + dmabuf->SGok = 0; + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + stop_adc(state); + synchronize_irq(); + resync_dma_ptrs(state); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + dmabuf->blocks = 0; + dmabuf->SGok = 0; + } + } + CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_RESET()-\n") ); + return 0; + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(state, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SPEED: /* set sample rate */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + stop_adc(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + cs_set_adc_rate(state, val); + cs_set_divisor(dmabuf); + } + } + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + stop_dac(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + cs_set_dac_rate(state, val); + cs_set_divisor(dmabuf); + } + } + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( + "cs46xx: cs_ioctl() DSP_SPEED %s %s %d\n", + file->f_mode & FMODE_WRITE ? "DAC" : "", + file->f_mode & FMODE_READ ? "ADC" : "", + dmabuf->rate ) ); + return put_user(dmabuf->rate, (int *)arg); + } + return put_user(0, (int *)arg); + + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + stop_dac(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val) + dmabuf->fmt |= CS_FMT_STEREO; + else + dmabuf->fmt &= ~CS_FMT_STEREO; + cs_set_divisor(dmabuf); + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( + "cs46xx: DSP_STEREO() DAC %s\n", + (dmabuf->fmt & CS_FMT_STEREO) ? + "STEREO":"MONO") ); + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + stop_adc(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val) + dmabuf->fmt |= CS_FMT_STEREO; + else + dmabuf->fmt &= ~CS_FMT_STEREO; + cs_set_divisor(dmabuf); + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( + "cs46xx: DSP_STEREO() ADC %s\n", + (dmabuf->fmt & CS_FMT_STEREO) ? + "STEREO":"MONO") ); + } + } + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + if ((val = prog_dmabuf(state))) + return val; + return put_user(dmabuf->fragsize, (int *)arg); + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + if ((val = prog_dmabuf(state))) + return val; + return put_user(dmabuf->fragsize/dmabuf->divisor, + (int *)arg); + } + } + return put_user(0, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + return put_user(AFMT_S16_LE | AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Select sample format */ + if (get_user(val, (int *)arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( + "cs46xx: cs_ioctl() DSP_SETFMT %s %s %s %s\n", + file->f_mode & FMODE_WRITE ? "DAC" : "", + file->f_mode & FMODE_READ ? "ADC" : "", + val == AFMT_S16_LE ? "16Bit Signed" : "", + val == AFMT_U8 ? "8Bit Unsigned" : "") ); + valsave = val; + if (val != AFMT_QUERY) { + if(val==AFMT_S16_LE || val==AFMT_U8) + { + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + stop_dac(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val==AFMT_S16_LE) + dmabuf->fmt |= CS_FMT_16BIT; + else + dmabuf->fmt &= ~CS_FMT_16BIT; + cs_set_divisor(dmabuf); + if((ret = prog_dmabuf(state))) + return ret; + } + } + if (file->f_mode & FMODE_READ) { + val = valsave; + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + stop_adc(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val==AFMT_S16_LE) + dmabuf->fmt |= CS_FMT_16BIT; + else + dmabuf->fmt &= ~CS_FMT_16BIT; + cs_set_divisor(dmabuf); + if((ret = prog_dmabuf(state))) + return ret; + } + } + } + else + { + CS_DBGOUT(CS_IOCTL | CS_ERROR, 2, printk( + "cs46xx: DSP_SETFMT() Unsupported format (0x%x)\n", + valsave) ); + } + } + else + { + if(file->f_mode & FMODE_WRITE) + { + state = (struct cs_state *)card->states[1]; + if(state) + dmabuf = &state->dmabuf; + } + else if(file->f_mode & FMODE_READ) + { + state = (struct cs_state *)card->states[0]; + if(state) + dmabuf = &state->dmabuf; + } + } + if(dmabuf) + { + if(dmabuf->fmt & CS_FMT_16BIT) + return put_user(AFMT_S16_LE, (int *)arg); + else + return put_user(AFMT_U8, (int *)arg); + } + return put_user(0, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + stop_dac(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val>1) + dmabuf->fmt |= CS_FMT_STEREO; + else + dmabuf->fmt &= ~CS_FMT_STEREO; + cs_set_divisor(dmabuf); + if (prog_dmabuf(state)) + return 0; + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + stop_adc(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val>1) + dmabuf->fmt |= CS_FMT_STEREO; + else + dmabuf->fmt &= ~CS_FMT_STEREO; + cs_set_divisor(dmabuf); + if (prog_dmabuf(state)) + return 0; + } + } + } + return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1, + (int *)arg); + + case SNDCTL_DSP_POST: + /* + * There will be a longer than normal pause in the data. + * so... do nothing, because there is nothing that we can do. + */ + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + if (dmabuf->subdivision) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2) + return -EINVAL; + dmabuf->subdivision = val; + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + if (dmabuf->subdivision) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2) + return -EINVAL; + dmabuf->subdivision = val; + } + } + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + dmabuf->ossfragshift = val & 0xffff; + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + dmabuf->ossfragshift = val & 0xffff; + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + spin_lock_irqsave(&state->card->lock, flags); + cs_update_ptr(card, CS_TRUE); + abinfo.fragsize = dmabuf->fragsize; + abinfo.fragstotal = dmabuf->numfrag; + /* + * for mmap we always have total space available + */ + if (dmabuf->mapped) + abinfo.bytes = dmabuf->dmasize; + else + abinfo.bytes = dmabuf->dmasize - dmabuf->count; + + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + } + return -ENODEV; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + spin_lock_irqsave(&state->card->lock, flags); + cs_update_ptr(card, CS_TRUE); + abinfo.fragsize = dmabuf->fragsize/dmabuf->divisor; + abinfo.bytes = dmabuf->count/dmabuf->divisor; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + } + return -ENODEV; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, + (int *)arg); + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()+\n") ); + if (file->f_mode & FMODE_WRITE) + { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + if(dmabuf->enable & DAC_RUNNING) + val |= PCM_ENABLE_INPUT; + } + } + if (file->f_mode & FMODE_READ) + { + if(state) + { + state = (struct cs_state *)card->states[0]; + dmabuf = &state->dmabuf; + if(dmabuf->enable & ADC_RUNNING) + val |= PCM_ENABLE_OUTPUT; + } + } + CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()- val=0x%x\n",val) ); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + if (val & PCM_ENABLE_INPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state))) + return ret; + start_adc(state); + } else + stop_adc(state); + } + } + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + if (val & PCM_ENABLE_OUTPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state))) + return ret; + start_dac(state); + } else + stop_dac(state); + } + } + return 0; + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + spin_lock_irqsave(&state->card->lock, flags); + cs_update_ptr(card, CS_TRUE); + cinfo.bytes = dmabuf->total_bytes/dmabuf->divisor; + cinfo.blocks = dmabuf->count/dmabuf->divisor >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr/dmabuf->divisor; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + } + return -ENODEV; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + spin_lock_irqsave(&state->card->lock, flags); + cs_update_ptr(card, CS_TRUE); + cinfo.bytes = dmabuf->total_bytes; + if (dmabuf->mapped) + { + cinfo.blocks = (cinfo.bytes >> dmabuf->fragshift) + - dmabuf->blocks; + CS_DBGOUT(CS_PARMS, 8, + printk("total_bytes=%d blocks=%d dmabuf->blocks=%d\n", + cinfo.bytes,cinfo.blocks,dmabuf->blocks) ); + dmabuf->blocks = cinfo.bytes >> dmabuf->fragshift; + } + else + { + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + } + cinfo.ptr = dmabuf->hwptr; + + CS_DBGOUT(CS_PARMS, 4, printk( + "cs46xx: GETOPTR bytes=%d blocks=%d ptr=%d\n", + cinfo.bytes,cinfo.blocks,cinfo.ptr) ); + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + } + return -ENODEV; + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + spin_lock_irqsave(&state->card->lock, flags); + cs_update_ptr(card, CS_TRUE); + val = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + } + else + val = 0; + return put_user(val, (int *)arg); + + case SOUND_PCM_READ_RATE: + if(file->f_mode & FMODE_READ) + state = (struct cs_state *)card->states[0]; + else + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + return put_user(dmabuf->rate, (int *)arg); + } + return put_user(0, (int *)arg); + + + case SOUND_PCM_READ_CHANNELS: + if(file->f_mode & FMODE_READ) + state = (struct cs_state *)card->states[0]; + else + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1, + (int *)arg); + } + return put_user(0, (int *)arg); + + case SOUND_PCM_READ_BITS: + if(file->f_mode & FMODE_READ) + state = (struct cs_state *)card->states[0]; + else + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + return put_user((dmabuf->fmt & CS_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + + } + return put_user(0, (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; +} + + +/* + * AMP control - null AMP + */ + +static void amp_none(struct cs_card *card, int change) +{ +} + +/* + * Crystal EAPD mode + */ + +static void amp_voyetra(struct cs_card *card, int change) +{ + /* Manage the EAPD bit on the Crystal 4297 + and the Analog AD1885 */ + + int old=card->amplifier; + + card->amplifier+=change; + if(card->amplifier && !old) + { + /* Turn the EAPD amp on */ + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, + cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) | + 0x8000); + } + else if(old && !card->amplifier) + { + /* Turn the EAPD amp off */ + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, + cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + ~0x8000); + } +} + + +/* + * Game Theatre XP card - EGPIO[2] is used to enable the external amp. + */ + +static void amp_hercules(struct cs_card *card, int change) +{ + int old=card->amplifier; + if(!card) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs46xx: amp_hercules() called before initialized.\n")); + return; + } + card->amplifier+=change; + if( (card->amplifier && !old) && !(hercules_egpio_disable)) + { + CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO + "cs46xx: amp_hercules() external amp enabled\n")); + cs461x_pokeBA0(card, BA0_EGPIODR, + EGPIODR_GPOE2); /* enable EGPIO2 output */ + cs461x_pokeBA0(card, BA0_EGPIOPTR, + EGPIOPTR_GPPT2); /* open-drain on output */ + } + else if(old && !card->amplifier) + { + CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO + "cs46xx: amp_hercules() external amp disabled\n")); + cs461x_pokeBA0(card, BA0_EGPIODR, 0); /* disable */ + cs461x_pokeBA0(card, BA0_EGPIOPTR, 0); /* disable */ + } +} + +/* + * Handle the CLKRUN on a thinkpad. We must disable CLKRUN support + * whenever we need to beat on the chip. + * + * The original idea and code for this hack comes from David Kaiser at + * Linuxcare. Perhaps one day Crystal will document their chips well + * enough to make them useful. + */ + +static void clkrun_hack(struct cs_card *card, int change) +{ + struct pci_dev *acpi_dev; + u16 control; + u8 pp; + unsigned long port; + int old=card->active; + + card->active+=change; + + acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + if(acpi_dev == NULL) + return; /* Not a thinkpad thats for sure */ + + /* Find the control port */ + pci_read_config_byte(acpi_dev, 0x41, &pp); + port=pp<<8; + + /* Read ACPI port */ + control=inw(port+0x10); + + /* Flip CLKRUN off while running */ + if(!card->active && old) + { + CS_DBGOUT(CS_PARMS , 9, printk( KERN_INFO + "cs46xx: clkrun() enable clkrun - change=%d active=%d\n", + change,card->active)); + outw(control|0x2000, port+0x10); + } + else + { + /* + * sometimes on a resume the bit is set, so always reset the bit. + */ + CS_DBGOUT(CS_PARMS , 9, printk( KERN_INFO + "cs46xx: clkrun() disable clkrun - change=%d active=%d\n", + change,card->active)); + outw(control&~0x2000, port+0x10); + } +} + + +static int cs_open(struct inode *inode, struct file *file) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_state *state = NULL; + struct dmabuf *dmabuf = NULL; + struct list_head *entry; + unsigned int minor = minor(inode->i_rdev); + int ret=0; + unsigned int tmp; + + CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()+ file=0x%x %s %s\n", + (unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "", + file->f_mode & FMODE_READ ? "FMODE_READ" : "") ); + + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + + if (!((card->dev_audio ^ minor) & ~0xf)) + break; + } + if (entry == &cs46xx_devs) + return -ENODEV; + if (!card) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs46xx: cs_open(): Error - unable to find audio card struct\n")); + return -ENODEV; + } + + /* + * hardcode state[0] for capture, [1] for playback + */ + if(file->f_mode & FMODE_READ) + { + CS_DBGOUT(CS_WAVE_READ, 2, printk("cs46xx: cs_open() FMODE_READ\n") ); + if (card->states[0] == NULL) { + state = card->states[0] = (struct cs_state *) + kmalloc(sizeof(struct cs_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + memset(state, 0, sizeof(struct cs_state)); + init_MUTEX(&state->sem); + dmabuf = &state->dmabuf; + dmabuf->pbuf = (void *)get_free_page(GFP_KERNEL | GFP_DMA); + if(dmabuf->pbuf==NULL) + { + kfree(state); + card->states[0]=NULL; + return -ENOMEM; + } + } + else + { + state = card->states[0]; + if(state->open_mode & FMODE_READ) + return -EBUSY; + } + dmabuf->channel = card->alloc_rec_pcm_channel(card); + + if (dmabuf->channel == NULL) { + kfree (card->states[0]); + card->states[0] = NULL;; + return -ENODEV; + } + + /* Now turn on external AMP if needed */ + state->card = card; + state->card->active_ctrl(state->card,1); + state->card->amplifier_ctrl(state->card,1); + + if( (tmp = cs46xx_powerup(card, CS_POWER_ADC)) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs46xx_powerup of ADC failed (0x%x)\n",tmp) ); + return -EIO; + } + + dmabuf->channel->state = state; + /* initialize the virtual channel */ + state->virt = 0; + state->magic = CS_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + file->private_data = card; + + down(&state->open_sem); + + /* 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 */ + + /* Default input is 8bit mono */ + dmabuf->fmt &= ~CS_FMT_MASK; + dmabuf->type = CS_TYPE_ADC; + dmabuf->ossfragshift = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + cs_set_adc_rate(state, 8000); + cs_set_divisor(dmabuf); + + state->open_mode |= FMODE_READ; + up(&state->open_sem); + } + if(file->f_mode & FMODE_WRITE) + { + CS_DBGOUT(CS_OPEN, 2, printk("cs46xx: cs_open() FMODE_WRITE\n") ); + if (card->states[1] == NULL) { + state = card->states[1] = (struct cs_state *) + kmalloc(sizeof(struct cs_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + memset(state, 0, sizeof(struct cs_state)); + init_MUTEX(&state->sem); + dmabuf = &state->dmabuf; + dmabuf->pbuf = (void *)get_free_page(GFP_KERNEL | GFP_DMA); + if(dmabuf->pbuf==NULL) + { + kfree(state); + card->states[1]=NULL; + return -ENOMEM; + } + } + else + { + state = card->states[1]; + if(state->open_mode & FMODE_WRITE) + return -EBUSY; + } + dmabuf->channel = card->alloc_pcm_channel(card); + + if (dmabuf->channel == NULL) { + kfree (card->states[1]); + card->states[1] = NULL;; + return -ENODEV; + } + + /* Now turn on external AMP if needed */ + state->card = card; + state->card->active_ctrl(state->card,1); + state->card->amplifier_ctrl(state->card,1); + + if( (tmp = cs46xx_powerup(card, CS_POWER_DAC)) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs46xx_powerup of DAC failed (0x%x)\n",tmp) ); + return -EIO; + } + + dmabuf->channel->state = state; + /* initialize the virtual channel */ + state->virt = 1; + state->magic = CS_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + file->private_data = card; + + down(&state->open_sem); + + /* 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 */ + + /* Default output is 8bit mono. */ + dmabuf->fmt &= ~CS_FMT_MASK; + dmabuf->type = CS_TYPE_DAC; + dmabuf->ossfragshift = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + cs_set_dac_rate(state, 8000); + cs_set_divisor(dmabuf); + + state->open_mode |= FMODE_WRITE; + up(&state->open_sem); + if((ret = prog_dmabuf(state))) + return ret; + } + MOD_INC_USE_COUNT; /* for 2.2 */ + CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()- 0\n") ); + return 0; +} + +static int cs_release(struct inode *inode, struct file *file) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + struct dmabuf *dmabuf; + struct cs_state *state; + unsigned int tmp; + CS_DBGOUT(CS_RELEASE | CS_FUNCTION, 2, printk("cs46xx: cs_release()+ file=0x%x %s %s\n", + (unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "", + file->f_mode & FMODE_READ ? "FMODE_READ" : "") ); + + if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) + { + return -EINVAL; + } + state = card->states[1]; + if(state) + { + if ( (state->open_mode & FMODE_WRITE) & (file->f_mode & FMODE_WRITE) ) + { + CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_WRITE\n") ); + dmabuf = &state->dmabuf; + cs_clear_tail(state); + drain_dac(state, file->f_flags & O_NONBLOCK); + /* stop DMA state machine and free DMA buffers/channels */ + down(&state->open_sem); + stop_dac(state); + dealloc_dmabuf(state); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); + free_page((unsigned long)state->dmabuf.pbuf); + + /* we're covered by the open_sem */ + up(&state->open_sem); + state->card->states[state->virt] = NULL; + state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + if( (tmp = cs461x_powerdown(card, CS_POWER_DAC, CS_FALSE )) ) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs46xx: cs_release_mixdev() powerdown DAC failure (0x%x)\n",tmp) ); + } + + /* Now turn off external AMP if needed */ + state->card->amplifier_ctrl(state->card, -1); + state->card->active_ctrl(state->card, -1); + + kfree(state); + } + } + + state = card->states[0]; + if(state) + { + if ( (state->open_mode & FMODE_READ) & (file->f_mode & FMODE_READ) ) + { + CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_READ\n") ); + dmabuf = &state->dmabuf; + down(&state->open_sem); + stop_adc(state); + dealloc_dmabuf(state); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); + free_page((unsigned long)state->dmabuf.pbuf); + + /* we're covered by the open_sem */ + up(&state->open_sem); + state->card->states[state->virt] = NULL; + state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + if( (tmp = cs461x_powerdown(card, CS_POWER_ADC, CS_FALSE )) ) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs46xx: cs_release_mixdev() powerdown ADC failure (0x%x)\n",tmp) ); + } + + /* Now turn off external AMP if needed */ + state->card->amplifier_ctrl(state->card, -1); + state->card->active_ctrl(state->card, -1); + + kfree(state); + } + } + + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk("cs46xx: cs_release()- 0\n") ); + MOD_DEC_USE_COUNT; /* For 2.2 */ + return 0; +} + +static void printpm(struct cs_card *s) +{ + CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); + CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n", + (unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue)); + CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n", + s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n", + s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n", + s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n", + s->pm.u32SSCR,s->pm.u32SRCSA)); + CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n", + s->pm.u32DacASR,s->pm.u32AdcASR)); + CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n", + s->pm.u32DacSR,s->pm.u32AdcSR)); + CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n", + s->pm.u32MIDCR_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32AC97_powerdown: 0x%x _general_purpose 0x%x\n", + s->pm.u32AC97_powerdown,s->pm.u32AC97_general_purpose)); + CS_DBGOUT(CS_PM, 9, printk("u32AC97_master_volume: 0x%x\n", + s->pm.u32AC97_master_volume)); + CS_DBGOUT(CS_PM, 9, printk("u32AC97_headphone_volume: 0x%x\n", + s->pm.u32AC97_headphone_volume)); + CS_DBGOUT(CS_PM, 9, printk("u32AC97_master_volume_mono: 0x%x\n", + s->pm.u32AC97_master_volume_mono)); + CS_DBGOUT(CS_PM, 9, printk("u32AC97_pcm_out_volume: 0x%x\n", + s->pm.u32AC97_pcm_out_volume)); + CS_DBGOUT(CS_PM, 9, printk("dmabuf_swptr_play: 0x%x dmabuf_count_play: %d\n", + s->pm.dmabuf_swptr_play,s->pm.dmabuf_count_play)); + CS_DBGOUT(CS_PM, 9, printk("dmabuf_swptr_capture: 0x%x dmabuf_count_capture: %d\n", + s->pm.dmabuf_swptr_capture,s->pm.dmabuf_count_capture)); + +} + +/**************************************************************************** +* +* Suspend - save the ac97 regs, mute the outputs and power down the part. +* +****************************************************************************/ +void cs46xx_ac97_suspend(struct cs_card *card) +{ + int Count,i; + struct ac97_codec *dev=card->ac97_codec[0]; + unsigned int tmp; + + CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()+\n")); + + if(card->states[1]) + { + stop_dac(card->states[1]); + resync_dma_ptrs(card->states[1]); + } + if(card->states[0]) + { + stop_adc(card->states[0]); + resync_dma_ptrs(card->states[0]); + } + + for(Count = 0x2, i=0; (Count <= CS46XX_AC97_HIGHESTREGTORESTORE) + && (i < CS46XX_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + card->pm.ac97[i] = cs_ac97_get(dev, BA0_AC97_RESET + Count); + } +/* +* Save the ac97 volume registers as well as the current powerdown state. +* Now, mute the all the outputs (master, headphone, and mono), as well +* as the PCM volume, in preparation for powering down the entire part. + card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev, + (u8)BA0_AC97_MASTER_VOLUME); + card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_HEADPHONE_VOLUME); + card->pm.u32AC97_master_volume_mono = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_MASTER_VOLUME_MONO); + card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_PCM_OUT_VOLUME); +*/ +/* +* mute the outputs +*/ + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000); + +/* +* save the registers that cause pops +*/ + card->pm.u32AC97_powerdown = (u32)cs_ac97_get(dev, (u8)AC97_POWER_CONTROL); + card->pm.u32AC97_general_purpose = (u32)cs_ac97_get(dev, (u8)BA0_AC97_GENERAL_PURPOSE); +/* +* And power down everything on the AC97 codec. +* well, for now, only power down the DAC/ADC and MIXER VREFON components. +* trouble with removing VREF. +*/ + if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON, CS_TRUE )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs46xx_ac97_suspend() failure (0x%x)\n",tmp) ); + } + + CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()-\n")); +} + +/**************************************************************************** +* +* Resume - power up the part and restore its registers.. +* +****************************************************************************/ +void cs46xx_ac97_resume(struct cs_card *card) +{ + int Count,i; + struct ac97_codec *dev=card->ac97_codec[0]; + + CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_resume()+\n")); + +/* +* First, we restore the state of the general purpose register. This +* contains the mic select (mic1 or mic2) and if we restore this after +* we restore the mic volume/boost state and mic2 was selected at +* suspend time, we will end up with a brief period of time where mic1 +* is selected with the volume/boost settings for mic2, causing +* acoustic feedback. So we restore the general purpose register +* first, thereby getting the correct mic selected before we restore +* the mic volume/boost. +*/ + cs_ac97_set(dev, (u8)BA0_AC97_GENERAL_PURPOSE, + (u16)card->pm.u32AC97_general_purpose); +/* +* Now, while the outputs are still muted, restore the state of power +* on the AC97 part. +*/ + cs_ac97_set(dev, (u8)BA0_AC97_POWERDOWN, (u16)card->pm.u32AC97_powerdown); + mdelay(5 * cs_laptop_wait); +/* +* Restore just the first set of registers, from register number +* 0x02 to the register number that ulHighestRegToRestore specifies. +*/ + for( Count = 0x2, i=0; + (Count <= CS46XX_AC97_HIGHESTREGTORESTORE) + && (i < CS46XX_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + cs_ac97_set(dev, (u8)(BA0_AC97_RESET + Count), (u16)card->pm.ac97[i]); + } + + /* Check if we have to init the amplifier */ + if(card->amp_init) + card->amp_init(card); + + CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_resume()-\n")); +} + + +static int cs46xx_restart_part(struct cs_card *card) +{ + struct dmabuf *dmabuf; + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk( "cs46xx: cs46xx_restart_part()+\n")); + if(card->states[1]) + { + dmabuf = &card->states[1]->dmabuf; + dmabuf->ready = 0; + resync_dma_ptrs(card->states[1]); + cs_set_divisor(dmabuf); + if(__prog_dmabuf(card->states[1])) + { + CS_DBGOUT(CS_PM | CS_ERROR, 1, + printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() dac error\n")); + return -1; + } + cs_set_dac_rate(card->states[1], dmabuf->rate); + } + if(card->states[0]) + { + dmabuf = &card->states[0]->dmabuf; + dmabuf->ready = 0; + resync_dma_ptrs(card->states[0]); + cs_set_divisor(dmabuf); + if(__prog_dmabuf(card->states[0])) + { + CS_DBGOUT(CS_PM | CS_ERROR, 1, + printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() adc error\n")); + return -1; + } + cs_set_adc_rate(card->states[0], dmabuf->rate); + } + card->pm.flags |= CS46XX_PM_RESUMED; + if(card->states[0]) + start_adc(card->states[0]); + if(card->states[1]) + start_dac(card->states[1]); + + card->pm.flags |= CS46XX_PM_IDLE; + card->pm.flags &= ~(CS46XX_PM_SUSPENDING | CS46XX_PM_SUSPENDED + | CS46XX_PM_RESUMING | CS46XX_PM_RESUMED); + if(card->states[0]) + wake_up(&card->states[0]->dmabuf.wait); + if(card->states[1]) + wake_up(&card->states[1]->dmabuf.wait); + + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk( "cs46xx: cs46xx_restart_part()-\n")); + return 0; +} + + +static void cs461x_reset(struct cs_card *card); +static void cs461x_proc_stop(struct cs_card *card); +static int cs46xx_suspend(struct cs_card *card, u32 state) +{ + unsigned int tmp; + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk("cs46xx: cs46xx_suspend()+ flags=0x%x s=0x%x\n", + (unsigned)card->pm.flags,(unsigned)card)); +/* +* check the current state, only suspend if IDLE +*/ + if(!(card->pm.flags & CS46XX_PM_IDLE)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs46xx: cs46xx_suspend() unable to suspend, not IDLE\n")); + return 1; + } + card->pm.flags &= ~CS46XX_PM_IDLE; + card->pm.flags |= CS46XX_PM_SUSPENDING; + + card->active_ctrl(card,1); + + tmp = cs461x_peek(card, BA1_PFIE); + tmp &= ~0x0000f03f; + tmp |= 0x00000010; + cs461x_poke(card, BA1_PFIE, tmp); /* playback interrupt disable */ + + tmp = cs461x_peek(card, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000011; + cs461x_poke(card, BA1_CIE, tmp); /* capture interrupt disable */ + + /* + * Stop playback DMA. + */ + tmp = cs461x_peek(card, BA1_PCTL); + cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = cs461x_peek(card, BA1_CCTL); + cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); + + if(card->states[1]) + { + card->pm.dmabuf_swptr_play = card->states[1]->dmabuf.swptr; + card->pm.dmabuf_count_play = card->states[1]->dmabuf.count; + } + if(card->states[0]) + { + card->pm.dmabuf_swptr_capture = card->states[0]->dmabuf.swptr; + card->pm.dmabuf_count_capture = card->states[0]->dmabuf.count; + } + + cs46xx_ac97_suspend(card); + + /* + * Reset the processor. + */ + cs461x_reset(card); + + cs461x_proc_stop(card); + + /* + * Power down the DAC and ADC. For now leave the other areas on. + */ + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, 0x0300); + + /* + * Power down the PLL. + */ + cs461x_pokeBA0(card, BA0_CLKCR1, 0); + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE; + cs461x_pokeBA0(card, BA0_CLKCR1, tmp); + + card->active_ctrl(card,-1); + + card->pm.flags &= ~CS46XX_PM_SUSPENDING; + card->pm.flags |= CS46XX_PM_SUSPENDED; + + printpm(card); + + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk("cs46xx: cs46xx_suspend()- flags=0x%x\n", + (unsigned)card->pm.flags)); + return 0; +} + +static int cs46xx_resume(struct cs_card *card) +{ + int i; + + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk( "cs46xx: cs46xx_resume()+ flags=0x%x\n", + (unsigned)card->pm.flags)); + if(!(card->pm.flags & CS46XX_PM_SUSPENDED)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs46xx: cs46xx_resume() unable to resume, not SUSPENDED\n")); + return 1; + } + card->pm.flags |= CS46XX_PM_RESUMING; + card->pm.flags &= ~CS46XX_PM_SUSPENDED; + printpm(card); + card->active_ctrl(card, 1); + + for(i=0;i<5;i++) + { + if (cs_hardware_init(card) != 0) + { + CS_DBGOUT(CS_PM | CS_ERROR, 4, printk( + "cs46xx: cs46xx_resume()- ERROR in cs_hardware_init()\n")); + mdelay(10 * cs_laptop_wait); + cs461x_reset(card); + continue; + } + break; + } + if(i>=4) + { + CS_DBGOUT(CS_PM | CS_ERROR, 1, printk( + "cs46xx: cs46xx_resume()- cs_hardware_init() failed, retried %d times.\n",i)); + return 0; + } + + if(cs46xx_restart_part(card)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 4, printk( + "cs46xx: cs46xx_resume(): cs46xx_restart_part() returned error\n")); + } + + card->active_ctrl(card, -1); + + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, printk("cs46xx: cs46xx_resume()- flags=0x%x\n", + (unsigned)card->pm.flags)); + return 0; +} + +static /*const*/ struct file_operations cs461x_fops = { + CS_OWNER CS_THIS_MODULE + llseek: no_llseek, + read: cs_read, + write: cs_write, + poll: cs_poll, + ioctl: cs_ioctl, + mmap: cs_mmap, + open: cs_open, + release: cs_release, +}; + +/* Write AC97 codec registers */ + + +static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg) +{ + struct cs_card *card = dev->private_data; + int count,loopcnt; + unsigned int tmp; + + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h + * 5. if DCV not cleared, break and return error + * 6. Read ACSTS = Status Register = 464h, check VSTS bit + */ + + + cs461x_peekBA0(card, BA0_ACSDA); + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * set CRW - Read command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + + cs461x_pokeBA0(card, BA0_ACCAD, reg); + cs461x_pokeBA0(card, BA0_ACCDA, 0); + cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW | + ACCTL_VFRM | ACCTL_ESYN | + ACCTL_RSTN); + + + /* + * Wait for the read to occur. + */ + if(!(card->pm.flags & CS46XX_PM_IDLE)) + loopcnt = 2000; + else + loopcnt = 500 * cs_laptop_wait; + loopcnt *= cs_laptop_wait; + for (count = 0; count < loopcnt; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10 * cs_laptop_wait); + /* + * Now, check to see if the read has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 17h + */ + if (!(cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV)) + break; + } + + /* + * Make sure the read completed. + */ + if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: AC'97 read problem (ACCTL_DCV), reg = 0x%x returning 0xffff\n", reg)); + return 0xffff; + } + + /* + * Wait for the valid status bit to go active. + */ + + if(!(card->pm.flags & CS46XX_PM_IDLE)) + loopcnt = 2000; + else + loopcnt = 1000; + loopcnt *= cs_laptop_wait; + for (count = 0; count < loopcnt; count++) { + /* + * Read the AC97 status register. + * ACSTS = Status Register = 464h + * VSTS - Valid Status + */ + if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_VSTS) + break; + udelay(10 * cs_laptop_wait); + } + + /* + * Make sure we got valid status. + */ + if (!( (tmp=cs461x_peekBA0(card, BA0_ACSTS)) & ACSTS_VSTS)) { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_WARNING + "cs46xx: AC'97 read problem (ACSTS_VSTS), reg = 0x%x val=0x%x 0xffff \n", + reg, tmp)); + return 0xffff; + } + + /* + * Read the data returned from the AC97 register. + * ACSDA = Status Data Register = 474h + */ + CS_DBGOUT(CS_FUNCTION, 9, printk(KERN_INFO + "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)); +} + +static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val) +{ + struct cs_card *card = dev->private_data; + int count; + int val2 = 0; + + if(reg == 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 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h + * 5. if DCV not cleared, break and return error + */ + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * reset CRW - Write command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + cs461x_pokeBA0(card, BA0_ACCAD, reg); + cs461x_pokeBA0(card, BA0_ACCDA, val); + cs461x_peekBA0(card, BA0_ACCTL); + cs461x_pokeBA0(card, BA0_ACCTL, 0 | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); + cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM | + ACCTL_ESYN | ACCTL_RSTN); + for (count = 0; count < 1000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10 * cs_laptop_wait); + /* + * Now, check to see if the write has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 07h + */ + if (!(cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV)) + break; + } + /* + * Make sure the write completed. + */ + if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val)); + } + + /* + * Adjust power if the mixer is selected/deselected according + * to the CD. + * + * IF the CD is a valid input source (mixer or direct) AND + * the CD is not muted THEN power is needed + * + * We do two things. When record select changes the input to + * add/remove the CD we adjust the power count if the CD is + * unmuted. + * + * When the CD mute changes we adjust the power level if the + * CD was a valid input. + * + * We also check for CD volume != 0, as the CD mute isn't + * normally tweaked from userspace. + */ + + /* CD mute change ? */ + + if(reg==AC97_CD_VOL) + { + /* Mute bit change ? */ + if((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) + { + /* This is a hack but its cleaner than the alternatives. + Right now card->ac97_codec[0] might be NULL as we are + still doing codec setup. This does an early assignment + to avoid the problem if it occurs */ + + if(card->ac97_codec[0]==NULL) + card->ac97_codec[0]=dev; + + /* Mute on */ + if(val&0x8000 || val == 0x1f1f) + card->amplifier_ctrl(card, -1); + else /* Mute off power on */ + { + if(card->amp_init) + card->amp_init(card); + card->amplifier_ctrl(card, 1); + } + } + } +} + + +/* OSS /dev/mixer file operation methods */ + +static int cs_open_mixdev(struct inode *inode, struct file *file) +{ + int i=0; + unsigned int minor = minor(inode->i_rdev); + struct cs_card *card=NULL; + struct list_head *entry; + unsigned int tmp; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs46xx: cs_open_mixdev()+\n")); + + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL && + card->ac97_codec[i]->dev_mixer == minor) + goto match; + } + if (!card) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, + printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n")); + return -ENODEV; + } + match: + if(!card->ac97_codec[i]) + return -ENODEV; + file->private_data = card->ac97_codec[i]; + + card->active_ctrl(card,1); + if(!CS_IN_USE(&card->mixer_use_cnt)) + { + if( (tmp = cs46xx_powerup(card, CS_POWER_MIXVON )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs_open_mixdev() powerup failure (0x%x)\n",tmp) ); + return -EIO; + } + } + card->amplifier_ctrl(card, 1); + CS_INC_USE_COUNT(&card->mixer_use_cnt); + MOD_INC_USE_COUNT; /* for 2.2 */ + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs46xx: cs_open_mixdev()- 0\n")); + return 0; +} + +static int cs_release_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct cs_card *card=NULL; + struct list_head *entry; + int i; + unsigned int tmp; + + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4, + printk(KERN_INFO "cs46xx: cs_release_mixdev()+\n")); + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL && + card->ac97_codec[i]->dev_mixer == minor) + goto match; + } + if (!card) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, + printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n")); + return -ENODEV; + } +match: + MOD_DEC_USE_COUNT; /* for 2.2 */ + if(!CS_DEC_AND_TEST(&card->mixer_use_cnt)) + { + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4, + printk(KERN_INFO "cs46xx: cs_release_mixdev()- no powerdown, usecnt>0\n")); + card->active_ctrl(card, -1); + card->amplifier_ctrl(card, -1); + return 0; + } +/* +* ok, no outstanding mixer opens, so powerdown. +*/ + if( (tmp = cs461x_powerdown(card, CS_POWER_MIXVON, CS_FALSE )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs_release_mixdev() powerdown MIXVON failure (0x%x)\n",tmp) ); + card->active_ctrl(card, -1); + card->amplifier_ctrl(card, -1); + return -EIO; + } + card->active_ctrl(card, -1); + card->amplifier_ctrl(card, -1); + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4, + printk(KERN_INFO "cs46xx: cs_release_mixdev()- 0\n")); + return 0; +} + +void __exit cs46xx_cleanup_module(void); +static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + struct cs_card *card=NULL; + struct list_head *entry; + +#if CSDEBUG_INTERFACE + int val; + + if( (cmd == SOUND_MIXER_CS_GETDBGMASK) || + (cmd == SOUND_MIXER_CS_SETDBGMASK) || + (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_SETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_APM)) + { + 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; + + case SOUND_MIXER_CS_APM: + if (get_user(val, (unsigned long *) arg)) + return -EFAULT; + if(val == CS_IOCTL_CMD_SUSPEND) + { + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + cs46xx_suspend(card, 0); + } + + } + else if(val == CS_IOCTL_CMD_RESUME) + { + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + cs46xx_resume(card); + } + } + else + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs46xx: mixer_ioctl(): invalid APM cmd (%d)\n", + val)); + } + return 0; + + default: + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs46xx: mixer_ioctl(): ERROR unknown debug cmd\n") ); + return 0; + } + } +#endif + return codec->mixer_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations cs_mixer_fops = { + CS_OWNER CS_THIS_MODULE + llseek: no_llseek, + ioctl: cs_ioctl_mixdev, + open: cs_open_mixdev, + release: cs_release_mixdev, +}; + +/* AC97 codec initialisation. */ +static int __init cs_ac97_init(struct cs_card *card) +{ + int num_ac97 = 0; + int ready_2nd = 0; + struct ac97_codec *codec; + u16 eid; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "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) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* 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 = cs_ac97_get; + codec->codec_write = cs_ac97_set; + + if (ac97_probe_codec(codec) == 0) + { + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_ac97_init()- codec number %d not found\n", + num_ac97) ); + card->ac97_codec[num_ac97] = 0; + break; + } + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_ac97_init() found codec %d\n",num_ac97) ); + + eid = cs_ac97_get(codec, AC97_EXTENDED_ID); + + if(eid==0xFFFFFF) + { + printk(KERN_WARNING "cs46xx: codec %d not present\n",num_ac97); + kfree(codec); + break; + } + + card->ac97_features = eid; + + if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) { + printk(KERN_ERR "cs46xx: couldn't register mixer!\n"); + kfree(codec); + break; + } + card->ac97_codec[num_ac97] = codec; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_ac97_init() ac97_codec[%d] set to 0x%x\n", + (unsigned int)num_ac97, + (unsigned int)codec)); + /* if there is no secondary codec at all, don't probe any more */ + if (!ready_2nd) + { + num_ac97 += 1; + break; + } + } + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_ac97_init()- %d\n", (unsigned int)num_ac97)); + return num_ac97; +} + +/* + * load the static image into the DSP + */ +#include "cs461x_image.h" +static void cs461x_download_image(struct cs_card *card) +{ + unsigned i, j, temp1, temp2, offset, count; + unsigned char *pBA1 = ioremap(card->ba1_addr, 0x40000); + for( i=0; i < CLEAR__COUNT; i++) + { + offset = ClrStat[i].BA1__DestByteOffset; + count = ClrStat[i].BA1__SourceSize; + for( temp1 = offset; temp1<(offset+count); temp1+=4 ); + writel(0, pBA1+temp1); + } + + for(i=0; iac97_codec[0], AC97_POWER_CONTROL); + CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO + "cs46xx: cs461x_powerdown() powerdown reg=0x%x\n",tmp)); +/* +* if powering down only the VREF, and not powering down the DAC/ADC, +* then do not power down the VREF, UNLESS both the DAC and ADC are not +* currently powered down. If powering down DAC and ADC, then +* it is possible to power down the VREF (ON). +*/ + if ( ((type & CS_POWER_MIXVON) && + (!(type & CS_POWER_ADC) || (!(type & CS_POWER_DAC))) ) + && + ((tmp & CS_AC97_POWER_CONTROL_ADC_ON) || + (tmp & CS_AC97_POWER_CONTROL_DAC_ON) ) ) + { + CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO + "cs46xx: cs461x_powerdown()- 0 unable to powerdown. tmp=0x%x\n",tmp)); + return 0; + } +/* +* for now, always keep power to the mixer block. +* not sure why it's a problem but it seems to be if we power off. +*/ + type &= ~CS_POWER_MIXVON; + type &= ~CS_POWER_MIXVOFF; + + /* + * Power down indicated areas. + */ + if(type & CS_POWER_MIXVOFF) + { + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVOFF\n")); + /* + * Power down the MIXER (VREF ON) on the AC97 card. + */ + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp |= CS_AC97_POWER_CONTROL_MIXVOFF; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVOFF_ON)) + break; + } + + /* + * Check the status.. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVOFF_ON) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerdown MIXVOFF failed\n")); + return 1; + } + } + } + if(type & CS_POWER_MIXVON) + { + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVON\n")); + /* + * Power down the MIXER (VREF ON) on the AC97 card. + */ + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (tmp & CS_AC97_POWER_CONTROL_MIXVON_ON) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp |= CS_AC97_POWER_CONTROL_MIXVON; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVON_ON)) + break; + } + + /* + * Check the status.. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVON_ON) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerdown MIXVON failed\n")); + return 1; + } + } + } + if(type & CS_POWER_ADC) + { + /* + * Power down the ADC on the AC97 card. + */ + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs461x_powerdown()+ ADC\n")); + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (tmp & CS_AC97_POWER_CONTROL_ADC_ON) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp |= CS_AC97_POWER_CONTROL_ADC; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_ADC_ON)) + break; + } + + /* + * Check the status.. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_ADC_ON) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerdown ADC failed\n")); + return 1; + } + } + } + if(type & CS_POWER_DAC) + { + /* + * Power down the DAC on the AC97 card. + */ + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs461x_powerdown()+ DAC\n")); + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (tmp & CS_AC97_POWER_CONTROL_DAC_ON) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp |= CS_AC97_POWER_CONTROL_DAC; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_DAC_ON)) + break; + } + + /* + * Check the status.. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_DAC_ON) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerdown DAC failed\n")); + return 1; + } + } + } + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if(muted) + cs_mute(card, CS_FALSE); + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs46xx: cs461x_powerdown()- 0 tmp=0x%x\n",tmp)); + return 0; +} + +static int cs46xx_powerup(struct cs_card *card, unsigned int type) +{ + int count; + unsigned int tmp=0,muted=0; + + CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO + "cs46xx: cs46xx_powerup()+ type=0x%x\n",type)); + /* + * check for VREF and powerup if need to. + */ + if(type & CS_POWER_MIXVON) + type |= CS_POWER_MIXVOFF; + if(type & (CS_POWER_DAC | CS_POWER_ADC)) + type |= CS_POWER_MIXVON | CS_POWER_MIXVOFF; + + /* + * Power up indicated areas. + */ + if(type & CS_POWER_MIXVOFF) + { + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVOFF\n")); + /* + * Power up the MIXER (VREF ON) on the AC97 card. + */ + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (!(tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON)) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp &= ~CS_AC97_POWER_CONTROL_MIXVOFF; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVOFF_ON) + break; + } + + /* + * Check the status.. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVOFF_ON)) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerup MIXVOFF failed\n")); + return 1; + } + } + } + if(type & CS_POWER_MIXVON) + { + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVON\n")); + /* + * Power up the MIXER (VREF ON) on the AC97 card. + */ + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (!(tmp & CS_AC97_POWER_CONTROL_MIXVON_ON)) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp &= ~CS_AC97_POWER_CONTROL_MIXVON; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVON_ON) + break; + } + + /* + * Check the status.. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVON_ON)) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerup MIXVON failed\n")); + return 1; + } + } + } + if(type & CS_POWER_ADC) + { + /* + * Power up the ADC on the AC97 card. + */ + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs46xx_powerup()+ ADC\n")); + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (!(tmp & CS_AC97_POWER_CONTROL_ADC_ON)) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp &= ~CS_AC97_POWER_CONTROL_ADC; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_ADC_ON) + break; + } + + /* + * Check the status.. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_ADC_ON)) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerup ADC failed\n")); + return 1; + } + } + } + if(type & CS_POWER_DAC) + { + /* + * Power up the DAC on the AC97 card. + */ + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs46xx_powerup()+ DAC\n")); + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (!(tmp & CS_AC97_POWER_CONTROL_DAC_ON)) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp &= ~CS_AC97_POWER_CONTROL_DAC; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_DAC_ON) + break; + } + + /* + * Check the status.. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_DAC_ON)) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerup DAC failed\n")); + return 1; + } + } + } + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if(muted) + cs_mute(card, CS_FALSE); + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs46xx: cs46xx_powerup()- 0 tmp=0x%x\n",tmp)); + return 0; +} + + +static void cs461x_proc_start(struct cs_card *card) +{ + int cnt; + + /* + * Set the frame timer to reflect the number of cycles per frame. + */ + cs461x_poke(card, BA1_FRMT, 0xadf); + /* + * Turn on the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + cs461x_poke(card, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN); + /* + * Wait until the run at frame bit resets itself in the SP control + * register. + */ + for (cnt = 0; cnt < 25; cnt++) { + udelay(50); + if (!(cs461x_peek(card, BA1_SPCR) & SPCR_RUNFR)) + break; + } + + if (cs461x_peek(card, BA1_SPCR) & SPCR_RUNFR) + printk(KERN_WARNING "cs46xx: SPCR_RUNFR never reset\n"); +} + +static void cs461x_proc_stop(struct cs_card *card) +{ + /* + * Turn off the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + cs461x_poke(card, BA1_SPCR, 0); +} + +static int cs_hardware_init(struct cs_card *card) +{ + unsigned long end_time; + unsigned int tmp,count; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_hardware_init()+\n") ); + /* + * First, blast the clock control register to zero so that the PLL starts + * out in a known state, and blast the master serial port control register + * to zero so that the serial ports also start out in a known state. + */ + cs461x_pokeBA0(card, BA0_CLKCR1, 0); + cs461x_pokeBA0(card, BA0_SERMC1, 0); + + /* + * If we are in AC97 mode, then we must set the part to a host controlled + * AC-link. Otherwise, we won't be able to bring up the link. + */ + cs461x_pokeBA0(card, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_1_03); /* 1.03 card */ + /* cs461x_pokeBA0(card, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_2_0); */ /* 2.00 card */ + + /* + * Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 + * spec) and then drive it high. This is done for non AC97 modes since + * there might be logic external to the CS461x that uses the ARST# line + * for a reset. + */ + cs461x_pokeBA0(card, BA0_ACCTL, 1); + udelay(50); + cs461x_pokeBA0(card, BA0_ACCTL, 0); + udelay(50); + cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_RSTN); + + /* + * The first thing we do here is to enable sync generation. As soon + * as we start receiving bit clock, we'll start producing the SYNC + * signal. + */ + cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN); + + /* + * Now wait for a short while to allow the AC97 part to start + * generating bit clock (so we don't try to start the PLL without an + * input clock). + */ + mdelay(5 * cs_laptop_wait); /* 1 should be enough ?? (and pigs might fly) */ + + /* + * Set the serial port timing configuration, so that + * the clock control circuit gets its clock from the correct place. + */ + cs461x_pokeBA0(card, BA0_SERMC1, SERMC1_PTC_AC97); + + /* + * The part seems to not be ready for a while after a resume. + * so, if we are resuming, then wait for 700 mils. Note that 600 mils + * is not enough for some platforms! tested on an IBM Thinkpads and + * reference cards. + */ + if(!(card->pm.flags & CS46XX_PM_IDLE)) + mdelay(initdelay); + /* + * Write the selected clock control setup to the hardware. Do not turn on + * SWCE yet (if requested), so that the devices clocked by the output of + * PLL are not clocked until the PLL is stable. + */ + cs461x_pokeBA0(card, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ); + cs461x_pokeBA0(card, BA0_PLLM, 0x3a); + cs461x_pokeBA0(card, BA0_CLKCR2, CLKCR2_PDIVS_8); + + /* + * Power up the PLL. + */ + cs461x_pokeBA0(card, BA0_CLKCR1, CLKCR1_PLLP); + + /* + * Wait until the PLL has stabilized. + */ + mdelay(5 * cs_laptop_wait); /* Again 1 should be enough ?? */ + + /* + * Turn on clocking of the core so that we can setup the serial ports. + */ + tmp = cs461x_peekBA0(card, BA0_CLKCR1) | CLKCR1_SWCE; + cs461x_pokeBA0(card, BA0_CLKCR1, tmp); + + /* + * Fill the serial port FIFOs with silence. + */ + cs461x_clear_serial_FIFOs(card,CS_TYPE_DAC | CS_TYPE_ADC); + + /* + * Set the serial port FIFO pointer to the first sample in the FIFO. + */ + /* cs461x_pokeBA0(card, BA0_SERBSP, 0); */ + + /* + * Write the serial port configuration to the part. The master + * enable bit is not set until all other values have been written. + */ + cs461x_pokeBA0(card, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN); + cs461x_pokeBA0(card, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN); + cs461x_pokeBA0(card, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE); + + + mdelay(5 * cs_laptop_wait); /* Shouldnt be needed ?? */ + +/* +* If we are resuming under 2.2.x then we can not schedule a timeout. +* so, just spin the CPU. +*/ + if(card->pm.flags & CS46XX_PM_IDLE) + { + /* + * Wait for the card ready signal from the AC97 card. + */ + end_time = jiffies + 3 * (HZ >> 2); + do { + /* + * Read the AC97 status register to see if we've seen a CODEC READY + * signal from the AC97 card. + */ + if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY) + break; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1); + } while (time_before(jiffies, end_time)); + } + else + { + for (count = 0; count < 100; count++) { + // First, we want to wait for a short time. + udelay(25 * cs_laptop_wait); + + if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY) + break; + } + } + + /* + * Make sure CODEC is READY. + */ + if (!(cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING + "cs46xx: create - never read card ready from AC'97\n")); + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING + "cs46xx: probably not a bug, try using the CS4232 driver,\n")); + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING + "cs46xx: or turn off any automatic Power Management support in the BIOS.\n")); + return -EIO; + } + + /* + * Assert the vaid frame signal so that we can start sending commands + * to the AC97 card. + */ + cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); + + if(card->pm.flags & CS46XX_PM_IDLE) + { + /* + * Wait until we've sampled input slots 3 and 4 as valid, meaning that + * the card is pumping ADC data across the AC-link. + */ + end_time = jiffies + 3 * (HZ >> 2); + do { + /* + * Read the input slot valid register and see if input slots 3 and + * 4 are valid yet. + */ + if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) + break; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1); + } while (time_before(jiffies, end_time)); + } + else + { + for (count = 0; count < 100; count++) { + // First, we want to wait for a short time. + udelay(25 * cs_laptop_wait); + + if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) + break; + } + } + /* + * Make sure input slots 3 and 4 are valid. If not, then return + * an error. + */ + if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) != (ACISV_ISV3 | ACISV_ISV4)) { + printk(KERN_WARNING "cs46xx: create - never read ISV3 & ISV4 from AC'97\n"); + return -EIO; + } + + /* + * Now, assert valid frame and the slot 3 and 4 valid bits. This will + * commense the transfer of digital audio data to the AC97 card. + */ + cs461x_pokeBA0(card, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4); + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + /* tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE; */ + /* cs461x_pokeBA0(card, BA0_CLKCR1, tmp); */ + + /* + * Reset the processor. + */ + cs461x_reset(card); + + /* + * Download the image to the processor. + */ + + cs461x_download_image(card); + + /* + * Stop playback DMA. + */ + tmp = cs461x_peek(card, BA1_PCTL); + card->pctl = tmp & 0xffff0000; + cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = cs461x_peek(card, BA1_CCTL); + card->cctl = tmp & 0x0000ffff; + cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); + + /* initialize AC97 codec and register /dev/mixer */ + if(card->pm.flags & CS46XX_PM_IDLE) + { + if (cs_ac97_init(card) <= 0) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs_ac97_init() failure\n") ); + return -EIO; + } + } + else + { + cs46xx_ac97_resume(card); + } + + cs461x_proc_start(card); + + /* + * Enable interrupts on the part. + */ + cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM); + + tmp = cs461x_peek(card, BA1_PFIE); + tmp &= ~0x0000f03f; + cs461x_poke(card, BA1_PFIE, tmp); /* playback interrupt enable */ + + tmp = cs461x_peek(card, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000001; + cs461x_poke(card, BA1_CIE, tmp); /* capture interrupt enable */ + + /* + * If IDLE then Power down the part. We will power components up + * when we need them. + */ + if(card->pm.flags & CS46XX_PM_IDLE) + { + if(!cs_powerdown) + { + if( (tmp = cs46xx_powerup(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs461x_powerup() failure (0x%x)\n",tmp) ); + return -EIO; + } + } + else + { + if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON, CS_FALSE )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) ); + return -EIO; + } + } + } + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_hardware_init()- 0\n")); + return 0; +} + +/* 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) */ + +/* + * Card subid table + */ + +struct cs_card_type +{ + u16 vendor; + u16 id; + char *name; + void (*amp)(struct cs_card *, int); + void (*amp_init)(struct cs_card *); + void (*active)(struct cs_card *, int); +}; + +static struct cs_card_type cards[]={ + {0x1489, 0x7001, "Genius Soundmaker 128 value", amp_none, NULL, NULL}, + {0x5053, 0x3357, "Voyetra", amp_voyetra, NULL, NULL}, + {0x1071, 0x6003, "Mitac MI6020/21", amp_voyetra, NULL, NULL}, + {0x14AF, 0x0050, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + {0x1681, 0x0050, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + {0x1681, 0x0051, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + {0x1681, 0x0052, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + {0x1681, 0x0053, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + {0x1681, 0x0054, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + /* Not sure if the 570 needs the clkrun hack */ + {PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", amp_none, NULL, clkrun_hack}, + {PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, NULL, clkrun_hack}, + {PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL}, + {0, 0, "Card without SSID set", NULL, NULL, NULL }, + {0, 0, NULL, NULL, NULL} +}; + +MODULE_AUTHOR("Alan Cox , Jaroslav Kysela, "); +MODULE_DESCRIPTION("Crystal SoundFusion Audio Support"); +MODULE_LICENSE("GPL"); + + +static const char cs46xx_banner[] = KERN_INFO "Crystal 4280/46xx + AC97 Audio, version " CS46XX_MAJOR_VERSION "." CS46XX_MINOR_VERSION "." CS46XX_ARCH ", " __TIME__ " " __DATE__ "\n"; +static const char fndmsg[] = KERN_INFO "cs46xx: Found %d audio device(s).\n"; + +static int __devinit cs46xx_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pciid) +{ + struct pm_dev *pmdev; + int i,j; + u16 ss_card, ss_vendor; + struct cs_card *card; + dma_addr_t dma_mask; + struct cs_card_type *cp = &cards[0]; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, + printk(KERN_INFO "cs46xx: probe()+\n")); + + dma_mask = 0xffffffff; /* this enables playback and recording */ + if (pci_enable_device(pci_dev)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs46xx: pci_enable_device() failed\n")); + return -1; + } + if (!RSRCISMEMORYREGION(pci_dev, 0) || + !RSRCISMEMORYREGION(pci_dev, 1)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs46xx: probe()- Memory region not assigned\n")); + return -1; + } + if (pci_dev->irq == 0) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs46xx: probe() IRQ not assigned\n")); + return -1; + } + if (!pci_dma_supported(pci_dev, 0xffffffff)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs46xx: probe() architecture does not support 32bit PCI busmaster DMA\n")); + return -1; + } + pci_read_config_word(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor); + pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &ss_card); + + if ((card = kmalloc(sizeof(struct cs_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "cs46xx: out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(*card)); + card->ba0_addr = RSRCADDRESS(pci_dev, 0); + card->ba1_addr = RSRCADDRESS(pci_dev, 1); + card->pci_dev = pci_dev; + card->irq = pci_dev->irq; + card->magic = CS_CARD_MAGIC; + spin_lock_init(&card->lock); + + pci_set_master(pci_dev); + + printk(cs46xx_banner); + printk(KERN_INFO "cs46xx: Card found at 0x%08lx and 0x%08lx, IRQ %d\n", + card->ba0_addr, card->ba1_addr, card->irq); + + card->alloc_pcm_channel = cs_alloc_pcm_channel; + card->alloc_rec_pcm_channel = cs_alloc_rec_pcm_channel; + card->free_pcm_channel = cs_free_pcm_channel; + card->amplifier_ctrl = amp_none; + card->active_ctrl = amp_none; + + while (cp->name) + { + if(cp->vendor == ss_vendor && cp->id == ss_card) + { + card->amplifier_ctrl = cp->amp; + if(cp->active) + card->active_ctrl = cp->active; + if(cp->amp_init) + card->amp_init = cp->amp_init; + break; + } + cp++; + } + if (cp->name==NULL) + { + printk(KERN_INFO "cs46xx: Unknown card (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n", + ss_vendor, ss_card, card->ba0_addr, card->ba1_addr, card->irq); + } + else + { + printk(KERN_INFO "cs46xx: %s (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n", + cp->name, ss_vendor, ss_card, card->ba0_addr, card->ba1_addr, card->irq); + } + + if (card->amplifier_ctrl==NULL) + { + card->amplifier_ctrl = amp_none; + card->active_ctrl = clkrun_hack; + } + + if (external_amp == 1) + { + printk(KERN_INFO "cs46xx: Crystal EAPD support forced on.\n"); + card->amplifier_ctrl = amp_voyetra; + } + + if (thinkpad == 1) + { + printk(KERN_INFO "cs46xx: Activating CLKRUN hack for Thinkpad.\n"); + card->active_ctrl = clkrun_hack; + } +/* +* The thinkpads don't work well without runtime updating on their kernel +* delay values (or any laptop with variable CPU speeds really). +* so, just to be safe set the init delay to 2100. Eliminates +* failures on T21 Thinkpads. remove this code when the udelay +* and mdelay kernel code is replaced by a pm timer, or the delays +* work well for battery and/or AC power both. +*/ + if(card->active_ctrl == clkrun_hack) + { + initdelay = 2100; + cs_laptop_wait = 5; + } + if((card->active_ctrl == clkrun_hack) && !(powerdown == 1)) + { +/* +* for some currently unknown reason, powering down the DAC and ADC component +* blocks on thinkpads causes some funky behavior... distoorrrtion and ac97 +* codec access problems. probably the serial clock becomes unsynced. +* added code to sync the chips back up, but only helped about 70% the time. +*/ + cs_powerdown = 0; + } + if(powerdown == 0) + cs_powerdown = 0; + card->active_ctrl(card, 1); + + /* claim our iospace and irq */ + + card->ba0 = ioremap_nocache(card->ba0_addr, CS461X_BA0_SIZE); + card->ba1.name.data0 = ioremap_nocache(card->ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE); + card->ba1.name.data1 = ioremap_nocache(card->ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE); + card->ba1.name.pmem = ioremap_nocache(card->ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE); + card->ba1.name.reg = ioremap_nocache(card->ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE); + + CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO + "cs46xx: card=0x%x card->ba0=0x%.08x\n",(unsigned)card,(unsigned)card->ba0) ); + CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO + "cs46xx: card->ba1=0x%.08x 0x%.08x 0x%.08x 0x%.08x\n", + (unsigned)card->ba1.name.data0, + (unsigned)card->ba1.name.data1, + (unsigned)card->ba1.name.pmem, + (unsigned)card->ba1.name.reg) ); + + if(card->ba0 == 0 || card->ba1.name.data0 == 0 || + card->ba1.name.data1 == 0 || card->ba1.name.pmem == 0 || + card->ba1.name.reg == 0) + goto fail2; + + if (request_irq(card->irq, &cs_interrupt, SA_SHIRQ, "cs46xx", card)) { + printk(KERN_ERR "cs46xx: unable to allocate irq %d\n", card->irq); + goto fail2; + } + /* register /dev/dsp */ + if ((card->dev_audio = register_sound_dsp(&cs461x_fops, -1)) < 0) { + printk(KERN_ERR "cs46xx: unable to register dsp\n"); + goto fail; + } + + /* register /dev/midi */ + if((card->dev_midi = register_sound_midi(&cs_midi_fops, -1)) < 0) + printk(KERN_ERR "cs46xx: unable to register midi\n"); + + card->pm.flags |= CS46XX_PM_IDLE; + for(i=0;i<5;i++) + { + if (cs_hardware_init(card) != 0) + { + CS_DBGOUT(CS_ERROR, 4, printk( + "cs46xx: ERROR in cs_hardware_init()... retrying\n")); + 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]); + } + mdelay(10 * cs_laptop_wait); + continue; + } + break; + } + if(i>=4) + { + CS_DBGOUT(CS_PM | CS_ERROR, 1, printk( + "cs46xx: cs46xx_probe()- cs_hardware_init() failed, retried %d times.\n",i)); + unregister_sound_dsp(card->dev_audio); + if(card->dev_midi) + unregister_sound_midi(card->dev_midi); + goto fail; + } + + init_waitqueue_head(&card->midi.open_wait); + init_MUTEX(&card->midi.open_sem); + init_waitqueue_head(&card->midi.iwait); + init_waitqueue_head(&card->midi.owait); + cs461x_pokeBA0(card, BA0_MIDCR, MIDCR_MRST); + cs461x_pokeBA0(card, BA0_MIDCR, 0); + + /* + * Check if we have to init the amplifier, but probably already done + * since the CD logic in the ac97 init code will turn on the ext amp. + */ + if(cp->amp_init) + cp->amp_init(card); + card->active_ctrl(card, -1); + + PCI_SET_DRIVER_DATA(pci_dev, card); + PCI_SET_DMA_MASK(pci_dev, dma_mask); + list_add(&card->list, &cs46xx_devs); + + pmdev = cs_pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev), cs46xx_pm_callback); + if (pmdev) + { + CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO + "cs46xx: probe() pm_register() succeeded (0x%x).\n", + (unsigned)pmdev)); + pmdev->data = card; + } + else + { + CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 2, printk(KERN_INFO + "cs46xx: probe() pm_register() failed (0x%x).\n", + (unsigned)pmdev)); + card->pm.flags |= CS46XX_PM_NOT_REGISTERED; + } + + CS_DBGOUT(CS_PM, 9, printk(KERN_INFO "cs46xx: pm.flags=0x%x card=0x%x\n", + (unsigned)card->pm.flags,(unsigned)card)); + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: probe()- device allocated successfully\n")); + return 0; + +fail: + free_irq(card->irq, card); +fail2: + if(card->ba0) + iounmap(card->ba0); + if(card->ba1.name.data0) + iounmap(card->ba1.name.data0); + if(card->ba1.name.data1) + iounmap(card->ba1.name.data1); + if(card->ba1.name.pmem) + iounmap(card->ba1.name.pmem); + if(card->ba1.name.reg) + iounmap(card->ba1.name.reg); + kfree(card); + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs46xx: probe()- no device allocated\n")); + return -ENODEV; +} // probe_cs46xx + +// --------------------------------------------------------------------- + +static void __devinit cs46xx_remove(struct pci_dev *pci_dev) +{ + struct cs_card *card = PCI_GET_DRIVER_DATA(pci_dev); + int i; + unsigned int tmp; + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: cs46xx_remove()+\n")); + + card->active_ctrl(card,1); + + tmp = cs461x_peek(card, BA1_PFIE); + tmp &= ~0x0000f03f; + tmp |= 0x00000010; + cs461x_poke(card, BA1_PFIE, tmp); /* playback interrupt disable */ + + tmp = cs461x_peek(card, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000011; + cs461x_poke(card, BA1_CIE, tmp); /* capture interrupt disable */ + + /* + * Stop playback DMA. + */ + tmp = cs461x_peek(card, BA1_PCTL); + cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = cs461x_peek(card, BA1_CCTL); + cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); + + /* + * Reset the processor. + */ + cs461x_reset(card); + + cs461x_proc_stop(card); + + /* + * Power down the DAC and ADC. We will power them up (if) when we need + * them. + */ + if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON, CS_TRUE )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) ); + } + + /* + * Power down the PLL. + */ + cs461x_pokeBA0(card, BA0_CLKCR1, 0); + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE; + cs461x_pokeBA0(card, BA0_CLKCR1, tmp); + + card->active_ctrl(card,-1); + + /* free hardware resources */ + free_irq(card->irq, card); + iounmap(card->ba0); + iounmap(card->ba1.name.data0); + iounmap(card->ba1.name.data1); + iounmap(card->ba1.name.pmem); + iounmap(card->ba1.name.reg); + + /* 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); + kfree (card->ac97_codec[i]); + } + unregister_sound_dsp(card->dev_audio); + if(card->dev_midi) + unregister_sound_midi(card->dev_midi); + list_del(&card->list); + kfree(card); + PCI_SET_DRIVER_DATA(pci_dev,NULL); + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: cs46xx_remove()-: remove successful\n")); +} + +enum { + CS46XX_4610 = 0, + CS46XX_4612, /* same as 4630 */ + CS46XX_4615, /* same as 4624 */ +}; + +static struct pci_device_id cs46xx_pci_tbl[] __devinitdata = { + + {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4610}, + {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4612}, + {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4615}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, cs46xx_pci_tbl); + +struct pci_driver cs46xx_pci_driver = { + name:"cs46xx", + id_table:cs46xx_pci_tbl, + probe:cs46xx_probe, + remove:cs46xx_remove, + suspend:CS46XX_SUSPEND_TBL, + resume:CS46XX_RESUME_TBL, +}; + +int __init cs46xx_init_module(void) +{ + int rtn = 0; + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: cs46xx_init_module()+ \n")); + if (!pci_present()) { /* No PCI bus in this machine! */ + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: cs46xx_init_module()- no pci bus found\n")); + return -ENODEV; + } + rtn = pci_module_init(&cs46xx_pci_driver); + + if(rtn == -ENODEV) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk( + "cs46xx: Unable to detect valid cs46xx device\n")); + } + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs46xx: cs46xx_init_module()- (%d)\n",rtn)); + return rtn; +} + +void __exit cs46xx_cleanup_module(void) +{ + pci_unregister_driver(&cs46xx_pci_driver); + cs_pm_unregister_all(cs46xx_pm_callback); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs46xx: cleanup_cs46xx() finished\n")); +} + +module_init(cs46xx_init_module); +module_exit(cs46xx_cleanup_module); + +int cs46xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct cs_card *card; + + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs46xx: cs46xx_pm_callback dev=0x%x rqst=0x%x card=%d\n", + (unsigned)dev,(unsigned)rqst,(unsigned)data)); + card = (struct cs_card *) dev->data; + if (card) { + switch(rqst) { + case PM_SUSPEND: + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs46xx: PM suspend request\n")); + if(cs46xx_suspend(card, 0)) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs46xx: PM suspend request refused\n")); + return 1; + } + break; + case PM_RESUME: + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs46xx: PM resume request\n")); + if(cs46xx_resume(card)) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs46xx: PM resume request refused\n")); + return 1; + } + break; + } + } + + return 0; +} + +#if CS46XX_ACPI_SUPPORT +static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state) +{ + struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev); + CS_DBGOUT(CS_PM | CS_FUNCTION, 2, + printk(KERN_INFO "cs46xx: cs46xx_suspend_tbl request\n")); + cs46xx_suspend(s, 0); + return 0; +} + +static int cs46xx_resume_tbl(struct pci_dev *pcidev) +{ + struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev); + CS_DBGOUT(CS_PM | CS_FUNCTION, 2, + printk(KERN_INFO "cs46xx: cs46xx_resume_tbl request\n")); + cs46xx_resume(s); + return 0; +} +#endif diff -Nru a/sound/oss/cs46xx_wrapper-24.h b/sound/oss/cs46xx_wrapper-24.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs46xx_wrapper-24.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,56 @@ +/******************************************************************************* +* +* "cs46xx_wrapper.c" -- Cirrus Logic-Crystal CS46XX linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (pcaudio@crystal.cirrus.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. +* +* 01/11/2001 trw - new file from cs4281 wrapper code. +* +*******************************************************************************/ +#ifndef __CS46XX_WRAPPER24_H +#define __CS46XX_WRAPPER24_H + +#include + +#define CS_OWNER owner: +#define CS_THIS_MODULE THIS_MODULE, +void cs46xx_null(struct pci_dev *pcidev) { return; } +#define cs4x_mem_map_reserve(page) mem_map_reserve(page) +#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page) + +#define free_dmabuf(card, dmabuf) \ + pci_free_consistent((card)->pci_dev, \ + PAGE_SIZE << (dmabuf)->buforder, \ + (dmabuf)->rawbuf, (dmabuf)->dmaaddr); +#define free_dmabuf2(card, dmabuf) \ + pci_free_consistent((card)->pci_dev, \ + PAGE_SIZE << (dmabuf)->buforder_tmpbuff, \ + (dmabuf)->tmpbuff, (dmabuf)->dmaaddr_tmpbuff); +#define cs4x_pgoff(vma) ((vma)->vm_pgoff) + +#define RSRCISIOREGION(dev,num) ((dev)->resource[(num)].start != 0 && \ + ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) +#define RSRCISMEMORYREGION(dev,num) ((dev)->resource[(num)].start != 0 && \ + ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) +#define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start) +#define PCI_GET_DRIVER_DATA pci_get_drvdata +#define PCI_SET_DRIVER_DATA pci_set_drvdata +#define PCI_SET_DMA_MASK(pcidev,mask) pcidev->dma_mask = mask + +#endif diff -Nru a/sound/oss/cs46xxpm-24.h b/sound/oss/cs46xxpm-24.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs46xxpm-24.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,53 @@ +/******************************************************************************* +* +* "cs46xxpm-24.h" -- Cirrus Logic-Crystal CS46XX linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (pcaudio@crystal.cirrus.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. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ +#ifndef __CS46XXPM24_H +#define __CS46XXPM24_H + +#include +#include "cs46xxpm.h" + + +#define CS46XX_ACPI_SUPPORT 1 +#ifdef CS46XX_ACPI_SUPPORT +/* +* for now (12/22/00) only enable the pm_register PM support. +* allow these table entries to be null. +*/ +static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state); +static int cs46xx_resume_tbl(struct pci_dev *pcidev); +#define cs_pm_register(a, b, c) 0 +#define cs_pm_unregister_all(a) +#define CS46XX_SUSPEND_TBL cs46xx_suspend_tbl +#define CS46XX_RESUME_TBL cs46xx_resume_tbl +#else +#define cs_pm_register(a, b, c) pm_register((a), (b), (c)); +#define cs_pm_unregister_all(a) pm_unregister_all((a)); +#define CS46XX_SUSPEND_TBL cs46xx_null +#define CS46XX_RESUME_TBL cs46xx_null +#endif +int cs46xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data); + +#endif diff -Nru a/sound/oss/cs46xxpm.h b/sound/oss/cs46xxpm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/cs46xxpm.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,70 @@ +/******************************************************************************* +* +* "cs46xxpm.h" -- Cirrus Logic-Crystal CS46XX linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (pcaudio@crystal.cirrus.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. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ +#ifndef __CS46XXPM_H +#define __CS46XXPM_H + +#define CS46XX_AC97_HIGHESTREGTORESTORE 0x26 +#define CS46XX_AC97_NUMBER_RESTORE_REGS (CS46XX_AC97_HIGHESTREGTORESTORE/2-1) + +/* PM state defintions */ +#define CS46XX_PM_NOT_REGISTERED 0x1000 +#define CS46XX_PM_IDLE 0x0001 +#define CS46XX_PM_SUSPENDING 0x0002 +#define CS46XX_PM_SUSPENDED 0x0004 +#define CS46XX_PM_RESUMING 0x0008 +#define CS46XX_PM_RESUMED 0x0010 + +#define CS_POWER_DAC 0x0001 +#define CS_POWER_ADC 0x0002 +#define CS_POWER_MIXVON 0x0004 +#define CS_POWER_MIXVOFF 0x0008 +#define CS_AC97_POWER_CONTROL_ON 0xf000 /* always on bits (inverted) */ +#define CS_AC97_POWER_CONTROL_ADC 0x0100 +#define CS_AC97_POWER_CONTROL_DAC 0x0200 +#define CS_AC97_POWER_CONTROL_MIXVON 0x0400 +#define CS_AC97_POWER_CONTROL_MIXVOFF 0x0800 +#define CS_AC97_POWER_CONTROL_ADC_ON 0x0001 +#define CS_AC97_POWER_CONTROL_DAC_ON 0x0002 +#define CS_AC97_POWER_CONTROL_MIXVON_ON 0x0004 +#define CS_AC97_POWER_CONTROL_MIXVOFF_ON 0x0008 + +struct cs46xx_pm { + unsigned long flags; + u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue; + u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR; + u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save; + u32 u32SSPM_BITS; + u32 ac97[CS46XX_AC97_NUMBER_RESTORE_REGS]; + u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono; + u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose; + u32 u32hwptr_playback,u32hwptr_capture; + unsigned dmabuf_swptr_play; + int dmabuf_count_play; + unsigned dmabuf_swptr_capture; + int dmabuf_count_capture; +}; + +#endif diff -Nru a/sound/oss/dev_table.c b/sound/oss/dev_table.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dev_table.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,221 @@ +/* + * sound/dev_table.c + * + * Device call tables. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +#include + +#define _DEV_TABLE_C_ +#include "sound_config.h" + +int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, + int driver_size, int flags, unsigned int format_mask, + void *devc, int dma1, int dma2) +{ + struct audio_driver *d; + struct audio_operations *op; + int l, num; + + if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) { + printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name); + return -(EINVAL); + } + num = sound_alloc_audiodev(); + + if (num == -1) { + printk(KERN_ERR "sound: Too many audio drivers\n"); + return -(EBUSY); + } + d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver))); + + if (sound_nblocks < 1024) + sound_nblocks++; + + op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_operations))); + + if (sound_nblocks < 1024) + sound_nblocks++; + if (d == NULL || op == NULL) { + printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name); + sound_unload_audiodev(num); + return -(ENOMEM); + } + memset((char *) op, 0, sizeof(struct audio_operations)); + init_waitqueue_head(&op->in_sleeper); + init_waitqueue_head(&op->out_sleeper); + init_waitqueue_head(&op->poll_sleeper); + if (driver_size < sizeof(struct audio_driver)) + memset((char *) d, 0, sizeof(struct audio_driver)); + + memcpy((char *) d, (char *) driver, driver_size); + + op->d = d; + l = strlen(name) + 1; + if (l > sizeof(op->name)) + l = sizeof(op->name); + strncpy(op->name, name, l); + op->name[l - 1] = 0; + op->flags = flags; + op->format_mask = format_mask; + op->devc = devc; + + /* + * Hardcoded defaults + */ + audio_devs[num] = op; + + DMAbuf_init(num, dma1, dma2); + + audio_init_devices(); + return num; +} + +int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, + int driver_size, void *devc) +{ + struct mixer_operations *op; + int l; + + int n = sound_alloc_mixerdev(); + + if (n == -1) { + printk(KERN_ERR "Sound: Too many mixer drivers\n"); + return -EBUSY; + } + if (vers != MIXER_DRIVER_VERSION || + driver_size > sizeof(struct mixer_operations)) { + printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name); + return -EINVAL; + } + + /* FIXME: This leaks a mixer_operations struct every time its called + until you unload sound! */ + + op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct mixer_operations))); + + if (sound_nblocks < 1024) + sound_nblocks++; + if (op == NULL) { + printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name); + return -ENOMEM; + } + memset((char *) op, 0, sizeof(struct mixer_operations)); + memcpy((char *) op, (char *) driver, driver_size); + + l = strlen(name) + 1; + if (l > sizeof(op->name)) + l = sizeof(op->name); + strncpy(op->name, name, l); + op->name[l - 1] = 0; + op->devc = devc; + + mixer_devs[n] = op; + return n; +} + +void sound_unload_audiodev(int dev) +{ + if (dev != -1) { + DMAbuf_deinit(dev); + audio_devs[dev] = NULL; + unregister_sound_dsp((dev<<4)+3); + } +} + +int sound_alloc_audiodev(void) +{ + int i = register_sound_dsp(&oss_sound_fops, -1); + if(i==-1) + return i; + i>>=4; + if(i>=num_audiodevs) + num_audiodevs = i + 1; + return i; +} + +int sound_alloc_mididev(void) +{ + int i = register_sound_midi(&oss_sound_fops, -1); + if(i==-1) + return i; + i>>=4; + if(i>=num_midis) + num_midis = i + 1; + return i; +} + +int sound_alloc_synthdev(void) +{ + int i; + + for (i = 0; i < MAX_SYNTH_DEV; i++) { + if (synth_devs[i] == NULL) { + if (i >= num_synths) + num_synths++; + return i; + } + } + return -1; +} + +int sound_alloc_mixerdev(void) +{ + int i = register_sound_mixer(&oss_sound_fops, -1); + if(i==-1) + return -1; + i>>=4; + if(i>=num_mixers) + num_mixers = i + 1; + return i; +} + +int sound_alloc_timerdev(void) +{ + int i; + + for (i = 0; i < MAX_TIMER_DEV; i++) { + if (sound_timer_devs[i] == NULL) { + if (i >= num_sound_timers) + num_sound_timers++; + return i; + } + } + return -1; +} + +void sound_unload_mixerdev(int dev) +{ + if (dev != -1) { + mixer_devs[dev] = NULL; + unregister_sound_mixer(dev<<4); + num_mixers--; + } +} + +void sound_unload_mididev(int dev) +{ + if (dev != -1) { + midi_devs[dev] = NULL; + unregister_sound_midi((dev<<4)+2); + } +} + +void sound_unload_synthdev(int dev) +{ + if (dev != -1) + synth_devs[dev] = NULL; +} + +void sound_unload_timerdev(int dev) +{ + if (dev != -1) + sound_timer_devs[dev] = NULL; +} diff -Nru a/sound/oss/dev_table.h b/sound/oss/dev_table.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dev_table.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,403 @@ +/* + * dev_table.h + * + * Global definitions for device call tables + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + + +#ifndef _DEV_TABLE_H_ +#define _DEV_TABLE_H_ + +/* + * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h) + * Numbers 1000 to N are reserved for driver's internal use. + */ + +#define SNDCARD_DESKPROXL 27 /* Compaq Deskpro XL */ +#define SNDCARD_VIDC 28 /* ARMs VIDC */ +#define SNDCARD_SBPNP 29 +#define SNDCARD_SOFTOSS 36 +#define SNDCARD_VMIDI 37 +#define SNDCARD_OPL3SA1 38 /* Note: clash in msnd.h */ +#define SNDCARD_OPL3SA1_SB 39 +#define SNDCARD_OPL3SA1_MPU 40 +#define SNDCARD_WAVEFRONT 41 +#define SNDCARD_OPL3SA2 42 +#define SNDCARD_OPL3SA2_MPU 43 +#define SNDCARD_WAVEARTIST 44 /* Waveartist */ +#define SNDCARD_OPL3SA2_MSS 45 /* Originally missed */ +#define SNDCARD_AD1816 88 + +/* + * NOTE! NOTE! NOTE! NOTE! + * + * If you modify this file, please check the dev_table.c also. + * + * NOTE! NOTE! NOTE! NOTE! + */ + +struct driver_info +{ + char *driver_id; + int card_subtype; /* Driver specific. Usually 0 */ + int card_type; /* From soundcard.h */ + char *name; + void (*attach) (struct address_info *hw_config); + int (*probe) (struct address_info *hw_config); + void (*unload) (struct address_info *hw_config); +}; + +struct card_info +{ + int card_type; /* Link (search key) to the driver list */ + struct address_info config; + int enabled; + void *for_driver_use; +}; + + +/* + * Device specific parameters (used only by dmabuf.c) + */ +#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR) + +#define DMODE_NONE 0 +#define DMODE_OUTPUT PCM_ENABLE_OUTPUT +#define DMODE_INPUT PCM_ENABLE_INPUT + +struct dma_buffparms +{ + int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */ + int closing; + + /* + * Pointers to raw buffers + */ + + char *raw_buf; + unsigned long raw_buf_phys; + int buffsize; + + /* + * Device state tables + */ + + unsigned long flags; +#define DMA_BUSY 0x00000001 +#define DMA_RESTART 0x00000002 +#define DMA_ACTIVE 0x00000004 +#define DMA_STARTED 0x00000008 +#define DMA_EMPTY 0x00000010 +#define DMA_ALLOC_DONE 0x00000020 +#define DMA_SYNCING 0x00000040 +#define DMA_DIRTY 0x00000080 +#define DMA_POST 0x00000100 +#define DMA_NODMA 0x00000200 +#define DMA_NOTIMEOUT 0x00000400 + + int open_mode; + + /* + * Queue parameters. + */ + int qlen; + int qhead; + int qtail; + int cfrag; /* Current incomplete fragment (write) */ + + int nbufs; + int counts[MAX_SUB_BUFFERS]; + int subdivision; + + int fragment_size; + int needs_reorg; + int max_fragments; + + int bytes_in_use; + + int underrun_count; + unsigned long byte_counter; + unsigned long user_counter; + unsigned long max_byte_counter; + int data_rate; /* Bytes/second */ + + int mapping_flags; +#define DMA_MAP_MAPPED 0x00000001 + char neutral_byte; + int dma; /* DMA channel */ + + int applic_profile; /* Application profile (APF_*) */ + /* Interrupt callback stuff */ + void (*audio_callback) (int dev, int parm); + int callback_parm; + + int buf_flags[MAX_SUB_BUFFERS]; +#define BUFF_EOF 0x00000001 /* Increment eof count */ +#define BUFF_DIRTY 0x00000002 /* Buffer written */ +}; + +/* + * Structure for use with various microcontrollers and DSP processors + * in the recent sound cards. + */ +typedef struct coproc_operations +{ + char name[64]; + struct module *owner; + int (*open) (void *devc, int sub_device); + void (*close) (void *devc, int sub_device); + int (*ioctl) (void *devc, unsigned int cmd, caddr_t arg, int local); + void (*reset) (void *devc); + + void *devc; /* Driver specific info */ +} coproc_operations; + +struct audio_driver +{ + struct module *owner; + int (*open) (int dev, int mode); + void (*close) (int dev); + void (*output_block) (int dev, unsigned long buf, + int count, int intrflag); + void (*start_input) (int dev, unsigned long buf, + int count, int intrflag); + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); + int (*prepare_for_input) (int dev, int bufsize, int nbufs); + int (*prepare_for_output) (int dev, int bufsize, int nbufs); + void (*halt_io) (int dev); + int (*local_qlen)(int dev); + void (*copy_user) (int dev, + char *localbuf, int localoffs, + const char *userbuf, int useroffs, + int max_in, int max_out, + int *used, int *returned, + int len); + void (*halt_input) (int dev); + void (*halt_output) (int dev); + void (*trigger) (int dev, int bits); + int (*set_speed)(int dev, int speed); + unsigned int (*set_bits)(int dev, unsigned int bits); + short (*set_channels)(int dev, short channels); + void (*postprocess_write)(int dev); /* Device spesific postprocessing for written data */ + void (*preprocess_read)(int dev); /* Device spesific preprocessing for read data */ + void (*mmap)(int dev); +}; + +struct audio_operations +{ + char name[128]; + int flags; +#define NOTHING_SPECIAL 0x00 +#define NEEDS_RESTART 0x01 +#define DMA_AUTOMODE 0x02 +#define DMA_DUPLEX 0x04 +#define DMA_PSEUDO_AUTOMODE 0x08 +#define DMA_HARDSTOP 0x10 +#define DMA_EXACT 0x40 +#define DMA_NORESET 0x80 + int format_mask; /* Bitmask for supported audio formats */ + void *devc; /* Driver specific info */ + struct audio_driver *d; + void *portc; /* Driver spesific info */ + struct dma_buffparms *dmap_in, *dmap_out; + struct coproc_operations *coproc; + int mixer_dev; + int enable_bits; + int open_mode; + int go; + int min_fragment; /* 0 == unlimited */ + int max_fragment; /* 0 == unlimited */ + int parent_dev; /* 0 -> no parent, 1 to n -> parent=parent_dev+1 */ + + /* fields formerly in dmabuf.c */ + wait_queue_head_t in_sleeper; + wait_queue_head_t out_sleeper; + wait_queue_head_t poll_sleeper; + + /* fields formerly in audio.c */ + int audio_mode; + +#define AM_NONE 0 +#define AM_WRITE OPEN_WRITE +#define AM_READ OPEN_READ + + int local_format; + int audio_format; + int local_conversion; +#define CNV_MU_LAW 0x00000001 + + /* large structures at the end to keep offsets small */ + struct dma_buffparms dmaps[2]; +}; + +int *load_mixer_volumes(char *name, int *levels, int present); + +struct mixer_operations +{ + struct module *owner; + char id[16]; + char name[64]; + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); + + void *devc; + int modify_counter; +}; + +struct synth_operations +{ + struct module *owner; + char *id; /* Unique identifier (ASCII) max 29 char */ + struct synth_info *info; + int midi_dev; + int synth_type; + int synth_subtype; + + int (*open) (int dev, int mode); + void (*close) (int dev); + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); + int (*kill_note) (int dev, int voice, int note, int velocity); + int (*start_note) (int dev, int voice, int note, int velocity); + int (*set_instr) (int dev, int voice, int instr); + void (*reset) (int dev); + void (*hw_control) (int dev, unsigned char *event); + int (*load_patch) (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag); + void (*aftertouch) (int dev, int voice, int pressure); + void (*controller) (int dev, int voice, int ctrl_num, int value); + void (*panning) (int dev, int voice, int value); + void (*volume_method) (int dev, int mode); + void (*bender) (int dev, int chn, int value); + int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc); + void (*setup_voice) (int dev, int voice, int chn); + int (*send_sysex)(int dev, unsigned char *bytes, int len); + + struct voice_alloc_info alloc; + struct channel_info chn_info[16]; + int emulation; +#define EMU_GM 1 /* General MIDI */ +#define EMU_XG 2 /* Yamaha XG */ +#define MAX_SYSEX_BUF 64 + unsigned char sysex_buf[MAX_SYSEX_BUF]; + int sysex_ptr; +}; + +struct midi_input_info +{ + /* MIDI input scanner variables */ +#define MI_MAX 10 + int m_busy; + unsigned char m_buf[MI_MAX]; + unsigned char m_prev_status; /* For running status */ + int m_ptr; +#define MST_INIT 0 +#define MST_DATA 1 +#define MST_SYSEX 2 + int m_state; + int m_left; +}; + +struct midi_operations +{ + struct module *owner; + struct midi_info info; + struct synth_operations *converter; + struct midi_input_info in_info; + int (*open) (int dev, int mode, + void (*inputintr)(int dev, unsigned char data), + void (*outputintr)(int dev) + ); + void (*close) (int dev); + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); + int (*outputc) (int dev, unsigned char data); + int (*start_read) (int dev); + int (*end_read) (int dev); + void (*kick)(int dev); + int (*command) (int dev, unsigned char *data); + int (*buffer_status) (int dev); + int (*prefix_cmd) (int dev, unsigned char status); + struct coproc_operations *coproc; + void *devc; +}; + +struct sound_lowlev_timer +{ + int dev; + int priority; + unsigned int (*tmr_start)(int dev, unsigned int usecs); + void (*tmr_disable)(int dev); + void (*tmr_restart)(int dev); +}; + +struct sound_timer_operations +{ + struct module *owner; + struct sound_timer_info info; + int priority; + int devlink; + int (*open)(int dev, int mode); + void (*close)(int dev); + int (*event)(int dev, unsigned char *ev); + unsigned long (*get_time)(int dev); + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); + void (*arm_timer)(int dev, long time); +}; + +#ifdef _DEV_TABLE_C_ +struct audio_operations *audio_devs[MAX_AUDIO_DEV]; +int num_audiodevs; +struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; +int num_mixers; +struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; +int num_synths; +struct midi_operations *midi_devs[MAX_MIDI_DEV]; +int num_midis; + +extern struct sound_timer_operations default_sound_timer; +struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { + &default_sound_timer, NULL +}; +int num_sound_timers = 1; +#else +extern struct audio_operations *audio_devs[MAX_AUDIO_DEV]; +extern int num_audiodevs; +extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; +extern int num_mixers; +extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; +extern int num_synths; +extern struct midi_operations *midi_devs[MAX_MIDI_DEV]; +extern int num_midis; +extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV]; +extern int num_sound_timers; +#endif /* _DEV_TABLE_C_ */ + +extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info); +void sound_timer_init (struct sound_lowlev_timer *t, char *name); +void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan); + +#define AUDIO_DRIVER_VERSION 2 +#define MIXER_DRIVER_VERSION 2 +int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, + int driver_size, int flags, unsigned int format_mask, + void *devc, int dma1, int dma2); +int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, + int driver_size, void *devc); + +void sound_unload_audiodev(int dev); +void sound_unload_mixerdev(int dev); +void sound_unload_mididev(int dev); +void sound_unload_synthdev(int dev); +void sound_unload_timerdev(int dev); +int sound_alloc_audiodev(void); +int sound_alloc_mixerdev(void); +int sound_alloc_timerdev(void); +int sound_alloc_synthdev(void); +int sound_alloc_mididev(void); +#endif /* _DEV_TABLE_H_ */ + diff -Nru a/sound/oss/dm.h b/sound/oss/dm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dm.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,79 @@ +#ifndef _DRIVERS_SOUND_DM_H +#define _DRIVERS_SOUND_DM_H + +/* + * Definitions of the 'direct midi sound' interface used + * by the newer commercial OSS package. We should export + * this to userland somewhere in glibc later. + */ + +/* + * Data structure composing an FM "note" or sound event. + */ + +struct dm_fm_voice +{ + u8 op; + u8 voice; + u8 am; + u8 vibrato; + u8 do_sustain; + u8 kbd_scale; + u8 harmonic; + u8 scale_level; + u8 volume; + u8 attack; + u8 decay; + u8 sustain; + u8 release; + u8 feedback; + u8 connection; + u8 left; + u8 right; + u8 waveform; +}; + +/* + * This describes an FM note by its voice, octave, frequency number (10bit) + * and key on/off. + */ + +struct dm_fm_note +{ + u8 voice; + u8 octave; + u32 fnum; + u8 key_on; +}; + +/* + * FM parameters that apply globally to all voices, and thus are not "notes" + */ + +struct dm_fm_params +{ + u8 am_depth; + u8 vib_depth; + u8 kbd_split; + u8 rhythm; + + /* This block is the percussion instrument data */ + u8 bass; + u8 snare; + u8 tomtom; + u8 cymbal; + u8 hihat; +}; + +/* + * FM mode ioctl settings + */ + +#define FM_IOCTL_RESET 0x20 +#define FM_IOCTL_PLAY_NOTE 0x21 +#define FM_IOCTL_SET_VOICE 0x22 +#define FM_IOCTL_SET_PARAMS 0x23 +#define FM_IOCTL_SET_MODE 0x24 +#define FM_IOCTL_SET_OPL 0x25 + +#endif diff -Nru a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmabuf.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,1296 @@ +/* + * sound/dmabuf.c + * + * The DMA buffer manager for digitized voice applications + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Thomas Sailer : moved several static variables into struct audio_operations + * (which is grossly misnamed btw.) because they have the same + * lifetime as the rest in there and dynamic allocation saves + * 12k or so + * Thomas Sailer : remove {in,out}_sleep_flag. It was used for the sleeper to + * determine if it was woken up by the expiring timeout or by + * an explicit wake_up. The return value from schedule_timeout + * can be used instead; if 0, the wakeup was due to the timeout. + * + * Rob Riggs Added persistent DMA buffers (1998/10/17) + */ + +#define BE_CONSERVATIVE +#define SAMPLE_ROUNDUP 0 + +#include "sound_config.h" +#include + +#define DMAP_FREE_ON_CLOSE 0 +#define DMAP_KEEP_ON_CLOSE 1 +extern int sound_dmap_flag; + +static void dma_reset_output(int dev); +static void dma_reset_input(int dev); +static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode); + + + +static int debugmem = 0; /* switched off by default */ +static int dma_buffsize = DSP_BUFFSIZE; + +static long dmabuf_timeout(struct dma_buffparms *dmap) +{ + long tmout; + + tmout = (dmap->fragment_size * HZ) / dmap->data_rate; + tmout += HZ / 5; /* Some safety distance */ + if (tmout < (HZ / 2)) + tmout = HZ / 2; + if (tmout > 20 * HZ) + tmout = 20 * HZ; + return tmout; +} + +static int sound_alloc_dmap(struct dma_buffparms *dmap) +{ + char *start_addr, *end_addr; + int dma_pagesize; + int sz, size; + struct page *page; + + dmap->mapping_flags &= ~DMA_MAP_MAPPED; + + if (dmap->raw_buf != NULL) + return 0; /* Already done */ + if (dma_buffsize < 4096) + dma_buffsize = 4096; + dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024); + + /* + * Now check for the Cyrix problem. + */ + + if(isa_dma_bridge_buggy==2) + dma_pagesize=32768; + + dmap->raw_buf = NULL; + dmap->buffsize = dma_buffsize; + if (dmap->buffsize > dma_pagesize) + dmap->buffsize = dma_pagesize; + start_addr = NULL; + /* + * Now loop until we get a free buffer. Try to get smaller buffer if + * it fails. Don't accept smaller than 8k buffer for performance + * reasons. + */ + while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) { + for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); + dmap->buffsize = PAGE_SIZE * (1 << sz); + start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz); + if (start_addr == NULL) + dmap->buffsize /= 2; + } + + if (start_addr == NULL) { + printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n"); + return -ENOMEM; + } else { + /* make some checks */ + end_addr = start_addr + dmap->buffsize - 1; + + if (debugmem) + printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr); + + /* now check if it fits into the same dma-pagesize */ + + if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1)) + || end_addr >= (char *) (MAX_DMA_ADDRESS)) { + printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize); + return -EFAULT; + } + } + dmap->raw_buf = start_addr; + dmap->raw_buf_phys = virt_to_bus(start_addr); + + for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) + mem_map_reserve(page); + return 0; +} + +static void sound_free_dmap(struct dma_buffparms *dmap) +{ + int sz, size; + struct page *page; + unsigned long start_addr, end_addr; + + if (dmap->raw_buf == NULL) + return; + if (dmap->mapping_flags & DMA_MAP_MAPPED) + return; /* Don't free mmapped buffer. Will use it next time */ + for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); + + start_addr = (unsigned long) dmap->raw_buf; + end_addr = start_addr + dmap->buffsize; + + for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) + mem_map_unreserve(page); + + free_pages((unsigned long) dmap->raw_buf, sz); + dmap->raw_buf = NULL; +} + + +/* Intel version !!!!!!!!! */ + +static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode) +{ + unsigned long flags; + int chan = dmap->dma; + + /* printk( "Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */ + + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + set_dma_mode(chan, dma_mode); + set_dma_addr(chan, physaddr); + set_dma_count(chan, count); + enable_dma(chan); + release_dma_lock(flags); + + return 0; +} + +static void dma_init_buffers(struct dma_buffparms *dmap) +{ + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; + dmap->byte_counter = 0; + dmap->max_byte_counter = 8000 * 60 * 60; + dmap->bytes_in_use = dmap->buffsize; + + dmap->dma_mode = DMODE_NONE; + dmap->mapping_flags = 0; + dmap->neutral_byte = 0x80; + dmap->data_rate = 8000; + dmap->cfrag = -1; + dmap->closing = 0; + dmap->nbufs = 1; + dmap->flags = DMA_BUSY; /* Other flags off */ +} + +static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap) +{ + int err; + + if (dmap->flags & DMA_BUSY) + return -EBUSY; + if ((err = sound_alloc_dmap(dmap)) < 0) + return err; + + if (dmap->raw_buf == NULL) { + printk(KERN_WARNING "Sound: DMA buffers not available\n"); + return -ENOSPC; /* Memory allocation failed during boot */ + } + if (dmap->dma >= 0 && sound_open_dma(dmap->dma, adev->name)) { + printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma); + return -EBUSY; + } + dma_init_buffers(dmap); + dmap->open_mode = mode; + dmap->subdivision = dmap->underrun_count = 0; + dmap->fragment_size = 0; + dmap->max_fragments = 65536; /* Just a large value */ + dmap->byte_counter = 0; + dmap->max_byte_counter = 8000 * 60 * 60; + dmap->applic_profile = APF_NORMAL; + dmap->needs_reorg = 1; + dmap->audio_callback = NULL; + dmap->callback_parm = 0; + return 0; +} + +static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap) +{ + unsigned long flags; + + if (dmap->dma >= 0) { + sound_close_dma(dmap->dma); + flags=claim_dma_lock(); + disable_dma(dmap->dma); + release_dma_lock(flags); + } + if (dmap->flags & DMA_BUSY) + dmap->dma_mode = DMODE_NONE; + dmap->flags &= ~DMA_BUSY; + + if (sound_dmap_flag == DMAP_FREE_ON_CLOSE) + sound_free_dmap(dmap); +} + + +static unsigned int default_set_bits(int dev, unsigned int bits) +{ + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (caddr_t)&bits); + set_fs(fs); + return bits; +} + +static int default_set_speed(int dev, int speed) +{ + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (caddr_t)&speed); + set_fs(fs); + return speed; +} + +static short default_set_channels(int dev, short channels) +{ + int c = channels; + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (caddr_t)&c); + set_fs(fs); + return c; +} + +static void check_driver(struct audio_driver *d) +{ + if (d->set_speed == NULL) + d->set_speed = default_set_speed; + if (d->set_bits == NULL) + d->set_bits = default_set_bits; + if (d->set_channels == NULL) + d->set_channels = default_set_channels; +} + +int DMAbuf_open(int dev, int mode) +{ + struct audio_operations *adev = audio_devs[dev]; + int retval; + struct dma_buffparms *dmap_in = NULL; + struct dma_buffparms *dmap_out = NULL; + + if (!adev) + return -ENXIO; + if (!(adev->flags & DMA_DUPLEX)) + adev->dmap_in = adev->dmap_out; + check_driver(adev->d); + + if ((retval = adev->d->open(dev, mode)) < 0) + return retval; + dmap_out = adev->dmap_out; + dmap_in = adev->dmap_in; + if (dmap_in == dmap_out) + adev->flags &= ~DMA_DUPLEX; + + if (mode & OPEN_WRITE) { + if ((retval = open_dmap(adev, mode, dmap_out)) < 0) { + adev->d->close(dev); + return retval; + } + } + adev->enable_bits = mode; + + if (mode == OPEN_READ || (mode != OPEN_WRITE && (adev->flags & DMA_DUPLEX))) { + if ((retval = open_dmap(adev, mode, dmap_in)) < 0) { + adev->d->close(dev); + if (mode & OPEN_WRITE) + close_dmap(adev, dmap_out); + return retval; + } + } + adev->open_mode = mode; + adev->go = 1; + + adev->d->set_bits(dev, 8); + adev->d->set_channels(dev, 1); + adev->d->set_speed(dev, DSP_DEFAULT_SPEED); + if (adev->dmap_out->dma_mode == DMODE_OUTPUT) + memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, + adev->dmap_out->bytes_in_use); + return 0; +} + +void DMAbuf_reset(int dev) +{ + if (audio_devs[dev]->open_mode & OPEN_WRITE) + dma_reset_output(dev); + + if (audio_devs[dev]->open_mode & OPEN_READ) + dma_reset_input(dev); +} + +static void dma_reset_output(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags,f ; + struct dma_buffparms *dmap = adev->dmap_out; + + if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */ + return; + + /* + * First wait until the current fragment has been played completely + */ + save_flags(flags); + cli(); + adev->dmap_out->flags |= DMA_SYNCING; + + adev->dmap_out->underrun_count = 0; + if (!signal_pending(current) && adev->dmap_out->qlen && + adev->dmap_out->underrun_count == 0) + interruptible_sleep_on_timeout(&adev->out_sleeper, + dmabuf_timeout(dmap)); + adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); + + /* + * Finally shut the device off + */ + if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output) + adev->d->halt_io(dev); + else + adev->d->halt_output(dev); + adev->dmap_out->flags &= ~DMA_STARTED; + + f=claim_dma_lock(); + clear_dma_ff(dmap->dma); + disable_dma(dmap->dma); + release_dma_lock(f); + + restore_flags(flags); + dmap->byte_counter = 0; + reorganize_buffers(dev, adev->dmap_out, 0); + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; +} + +static void dma_reset_input(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + struct dma_buffparms *dmap = adev->dmap_in; + + save_flags(flags); + cli(); + if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input) + adev->d->halt_io(dev); + else + adev->d->halt_input(dev); + adev->dmap_in->flags &= ~DMA_STARTED; + restore_flags(flags); + + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; + dmap->byte_counter = 0; + reorganize_buffers(dev, adev->dmap_in, 1); +} + +void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap) +{ + struct audio_operations *adev = audio_devs[dev]; + + if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT)) + return; /* Don't start DMA yet */ + dmap->dma_mode = DMODE_OUTPUT; + + if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { + if (!(dmap->flags & DMA_STARTED)) { + reorganize_buffers(dev, dmap, 0); + if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs)) + return; + if (!(dmap->flags & DMA_NODMA)) + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE); + dmap->flags |= DMA_STARTED; + } + if (dmap->counts[dmap->qhead] == 0) + dmap->counts[dmap->qhead] = dmap->fragment_size; + dmap->dma_mode = DMODE_OUTPUT; + adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size, + dmap->counts[dmap->qhead], 1); + if (adev->d->trigger) + adev->d->trigger(dev,adev->enable_bits * adev->go); + } + dmap->flags |= DMA_ACTIVE; +} + +int DMAbuf_sync(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + int n = 0; + struct dma_buffparms *dmap; + + if (!adev->go && !(adev->enable_bits & PCM_ENABLE_OUTPUT)) + return 0; + + if (adev->dmap_out->dma_mode == DMODE_OUTPUT) { + dmap = adev->dmap_out; + save_flags(flags); + cli(); + if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE)) + DMAbuf_launch_output(dev, dmap); + adev->dmap_out->flags |= DMA_SYNCING; + adev->dmap_out->underrun_count = 0; + while (!signal_pending(current) && n++ <= adev->dmap_out->nbufs && + adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) { + long t = dmabuf_timeout(dmap); + t = interruptible_sleep_on_timeout(&adev->out_sleeper, + t); + if (!t) { + adev->dmap_out->flags &= ~DMA_SYNCING; + restore_flags(flags); + return adev->dmap_out->qlen; + } + } + adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); + restore_flags(flags); + + /* + * Some devices such as GUS have huge amount of on board RAM for the + * audio data. We have to wait until the device has finished playing. + */ + + save_flags(flags); + cli(); + if (adev->d->local_qlen) { /* Device has hidden buffers */ + while (!signal_pending(current) && + adev->d->local_qlen(dev)) + interruptible_sleep_on_timeout(&adev->out_sleeper, + dmabuf_timeout(dmap)); + } + restore_flags(flags); + } + adev->dmap_out->dma_mode = DMODE_NONE; + return adev->dmap_out->qlen; +} + +int DMAbuf_release(int dev, int mode) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + + if (adev->open_mode & OPEN_WRITE) + adev->dmap_out->closing = 1; + if (adev->open_mode & OPEN_READ) + adev->dmap_in->closing = 1; + + if (adev->open_mode & OPEN_WRITE) + if (!(adev->dmap_out->mapping_flags & DMA_MAP_MAPPED)) + if (!signal_pending(current) && (adev->dmap_out->dma_mode == DMODE_OUTPUT)) + DMAbuf_sync(dev); + if (adev->dmap_out->dma_mode == DMODE_OUTPUT) + memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use); + save_flags(flags); + cli(); + + DMAbuf_reset(dev); + adev->d->close(dev); + + if (adev->open_mode & OPEN_WRITE) + close_dmap(adev, adev->dmap_out); + + if (adev->open_mode == OPEN_READ || + (adev->open_mode != OPEN_WRITE && + (adev->flags & DMA_DUPLEX))) + close_dmap(adev, adev->dmap_in); + adev->open_mode = 0; + restore_flags(flags); + return 0; +} + +int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap) +{ + struct audio_operations *adev = audio_devs[dev]; + int err; + + if (!(adev->open_mode & OPEN_READ)) + return 0; + if (!(adev->enable_bits & PCM_ENABLE_INPUT)) + return 0; + if (dmap->dma_mode == DMODE_OUTPUT) { /* Direction change */ + DMAbuf_sync(dev); + DMAbuf_reset(dev); + dmap->dma_mode = DMODE_NONE; + } + if (!dmap->dma_mode) { + reorganize_buffers(dev, dmap, 1); + if ((err = adev->d->prepare_for_input(dev, + dmap->fragment_size, dmap->nbufs)) < 0) + return err; + dmap->dma_mode = DMODE_INPUT; + } + if (!(dmap->flags & DMA_ACTIVE)) { + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); + adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, + dmap->fragment_size, 0); + dmap->flags |= DMA_ACTIVE; + if (adev->d->trigger) + adev->d->trigger(dev, adev->enable_bits * adev->go); + } + return 0; +} + +int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + int err = 0, n = 0; + struct dma_buffparms *dmap = adev->dmap_in; + int go; + + if (!(adev->open_mode & OPEN_READ)) + return -EIO; + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + save_flags(flags); + cli(); + if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) { +/* printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/ + restore_flags(flags); + return -EINVAL; + } else while (dmap->qlen <= 0 && n++ < 10) { + long timeout = MAX_SCHEDULE_TIMEOUT; + if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) { + restore_flags(flags); + return -EAGAIN; + } + if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) { + restore_flags(flags); + return err; + } + /* Wait for the next block */ + + if (dontblock) { + restore_flags(flags); + return -EAGAIN; + } + if ((go = adev->go)) + timeout = dmabuf_timeout(dmap); + timeout = interruptible_sleep_on_timeout(&adev->in_sleeper, + timeout); + if (!timeout) { + /* FIXME: include device name */ + err = -EIO; + printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); + dma_reset_input(dev); + } else + err = -EINTR; + } + restore_flags(flags); + + if (dmap->qlen <= 0) + return err ? err : -EINTR; + *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]]; + *len = dmap->fragment_size - dmap->counts[dmap->qhead]; + + return dmap->qhead; +} + +int DMAbuf_rmchars(int dev, int buff_no, int c) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; + int p = dmap->counts[dmap->qhead] + c; + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + { +/* printk("Sound: Can't read from mmapped device (2)\n");*/ + return -EINVAL; + } + else if (dmap->qlen <= 0) + return -EIO; + else if (p >= dmap->fragment_size) { /* This buffer is completely empty */ + dmap->counts[dmap->qhead] = 0; + dmap->qlen--; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + } + else dmap->counts[dmap->qhead] = p; + + return 0; +} + +int DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction) +{ + /* + * Try to approximate the active byte position of the DMA pointer within the + * buffer area as well as possible. + */ + + int pos; + unsigned long flags; + unsigned long f; + + save_flags(flags); + cli(); + if (!(dmap->flags & DMA_ACTIVE)) + pos = 0; + else { + int chan = dmap->dma; + + f=claim_dma_lock(); + clear_dma_ff(chan); + + if(!isa_dma_bridge_buggy) + disable_dma(dmap->dma); + + pos = get_dma_residue(chan); + + pos = dmap->bytes_in_use - pos; + + if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) { + if (direction == DMODE_OUTPUT) { + if (dmap->qhead == 0) + if (pos > dmap->fragment_size) + pos = 0; + } else { + if (dmap->qtail == 0) + if (pos > dmap->fragment_size) + pos = 0; + } + } + if (pos < 0) + pos = 0; + if (pos >= dmap->bytes_in_use) + pos = 0; + + if(!isa_dma_bridge_buggy) + enable_dma(dmap->dma); + + release_dma_lock(f); + } + restore_flags(flags); + /* printk( "%04x ", pos); */ + + return pos; +} + +/* + * DMAbuf_start_devices() is called by the /dev/music driver to start + * one or more audio devices at desired moment. + */ + +void DMAbuf_start_devices(unsigned int devmask) +{ + struct audio_operations *adev; + int dev; + + for (dev = 0; dev < num_audiodevs; dev++) { + if (!(devmask & (1 << dev))) + continue; + if (!(adev = audio_devs[dev])) + continue; + if (adev->open_mode == 0) + continue; + if (adev->go) + continue; + /* OK to start the device */ + adev->go = 1; + if (adev->d->trigger) + adev->d->trigger(dev,adev->enable_bits * adev->go); + } +} + +int DMAbuf_space_in_queue(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + int len, max, tmp; + struct dma_buffparms *dmap = adev->dmap_out; + int lim = dmap->nbufs; + + if (lim < 2) + lim = 2; + + if (dmap->qlen >= lim) /* No space at all */ + return 0; + + /* + * Verify that there are no more pending buffers than the limit + * defined by the process. + */ + + max = dmap->max_fragments; + if (max > lim) + max = lim; + len = dmap->qlen; + + if (adev->d->local_qlen) { + tmp = adev->d->local_qlen(dev); + if (tmp && len) + tmp--; /* This buffer has been counted twice */ + len += tmp; + } + if (dmap->byte_counter % dmap->fragment_size) /* There is a partial fragment */ + len = len + 1; + + if (len >= max) + return 0; + return max - len; +} + +static int output_sleep(int dev, int dontblock) +{ + struct audio_operations *adev = audio_devs[dev]; + int err = 0; + struct dma_buffparms *dmap = adev->dmap_out; + long timeout; + long timeout_value; + + if (dontblock) + return -EAGAIN; + if (!(adev->enable_bits & PCM_ENABLE_OUTPUT)) + return -EAGAIN; + + /* + * Wait for free space + */ + if (signal_pending(current)) + return -EINTR; + timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT)); + if (timeout) + timeout_value = dmabuf_timeout(dmap); + else + timeout_value = MAX_SCHEDULE_TIMEOUT; + timeout_value = interruptible_sleep_on_timeout(&adev->out_sleeper, + timeout_value); + if (timeout != MAX_SCHEDULE_TIMEOUT && !timeout_value) { + printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); + dma_reset_output(dev); + } else { + if (signal_pending(current)) + err = -EINTR; + } + return err; +} + +static int find_output_space(int dev, char **buf, int *size) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + unsigned long flags; + unsigned long active_offs; + long len, offs; + int maxfrags; + int occupied_bytes = (dmap->user_counter % dmap->fragment_size); + + *buf = dmap->raw_buf; + if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes) + return 0; + save_flags(flags); + cli(); + +#ifdef BE_CONSERVATIVE + active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size; +#else + active_offs = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT); + /* Check for pointer wrapping situation */ + if (active_offs < 0 || active_offs >= dmap->bytes_in_use) + active_offs = 0; + active_offs += dmap->byte_counter; +#endif + + offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP; + if (offs < 0 || offs >= dmap->bytes_in_use) { + restore_flags(flags); + printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs); + printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use); + return 0; + } + *buf = dmap->raw_buf + offs; + + len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */ + + if ((offs + len) > dmap->bytes_in_use) + len = dmap->bytes_in_use - offs; + if (len < 0) { + restore_flags(flags); + return 0; + } + if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes)) + len = (maxfrags * dmap->fragment_size) - occupied_bytes; + *size = len & ~SAMPLE_ROUNDUP; + restore_flags(flags); + return (*size > 0); +} + +int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + int err = -EIO; + struct dma_buffparms *dmap = adev->dmap_out; + + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + + if (dmap->mapping_flags & DMA_MAP_MAPPED) { +/* printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/ + return -EINVAL; + } + if (dmap->dma_mode == DMODE_INPUT) { /* Direction change */ + DMAbuf_reset(dev); + dmap->dma_mode = DMODE_NONE; + } + dmap->dma_mode = DMODE_OUTPUT; + + save_flags(flags); + cli(); + while (find_output_space(dev, buf, size) <= 0) { + if ((err = output_sleep(dev, dontblock)) < 0) { + restore_flags(flags); + return err; + } + } + restore_flags(flags); + + return 0; +} + +int DMAbuf_move_wrpointer(int dev, int l) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + unsigned long ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; + unsigned long end_ptr, p; + int post = (dmap->flags & DMA_POST); + + dmap->flags &= ~DMA_POST; + dmap->cfrag = -1; + dmap->user_counter += l; + dmap->flags |= DMA_DIRTY; + + if (dmap->byte_counter >= dmap->max_byte_counter) { + /* Wrap the byte counters */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; + + p = (dmap->user_counter - 1) % dmap->bytes_in_use; + dmap->neutral_byte = dmap->raw_buf[p]; + + /* Update the fragment based bookkeeping too */ + while (ptr < end_ptr) { + dmap->counts[dmap->qtail] = dmap->fragment_size; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + dmap->qlen++; + ptr += dmap->fragment_size; + } + + dmap->counts[dmap->qtail] = dmap->user_counter - ptr; + + /* + * Let the low level driver to perform some postprocessing to + * the written data. + */ + if (adev->d->postprocess_write) + adev->d->postprocess_write(dev); + + if (!(dmap->flags & DMA_ACTIVE)) + if (dmap->qlen > 1 || (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1))) + DMAbuf_launch_output(dev, dmap); + return 0; +} + +int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in; + + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "sound: DMA buffer(1) == NULL\n"); + printk("Device %d, chn=%s\n", dev, (dmap == adev->dmap_out) ? "out" : "in"); + return 0; + } + if (dmap->dma < 0) + return 0; + sound_start_dma(dmap, physaddr, count, dma_mode); + return count; +} + +static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode) +{ + struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in; + + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "sound: DMA buffer(2) == NULL\n"); + printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in"); + return 0; + } + if (dmap->flags & DMA_NODMA) + return 1; + if (dmap->dma < 0) + return 0; + sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT); + dmap->flags |= DMA_STARTED; + return count; +} + +static void finish_output_interrupt(int dev, struct dma_buffparms *dmap) +{ + struct audio_operations *adev = audio_devs[dev]; + + if (dmap->audio_callback != NULL) + dmap->audio_callback(dev, dmap->callback_parm); + wake_up(&adev->out_sleeper); + wake_up(&adev->poll_sleeper); +} + +static void do_outputintr(int dev, int dummy) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + struct dma_buffparms *dmap = adev->dmap_out; + int this_fragment; + + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev); + return; + } + if (dmap->mapping_flags & DMA_MAP_MAPPED) { /* Virtual memory mapped access */ + /* mmapped access */ + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + if (dmap->qhead == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + dmap->qlen++; /* Yes increment it (don't decrement) */ + if (!(adev->flags & DMA_AUTOMODE)) + dmap->flags &= ~DMA_ACTIVE; + dmap->counts[dmap->qhead] = dmap->fragment_size; + DMAbuf_launch_output(dev, dmap); + finish_output_interrupt(dev, dmap); + return; + } + save_flags(flags); + cli(); + + dmap->qlen--; + this_fragment = dmap->qhead; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + + if (dmap->qhead == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + if (!(adev->flags & DMA_AUTOMODE)) + dmap->flags &= ~DMA_ACTIVE; + + /* + * This is dmap->qlen <= 0 except when closing when + * dmap->qlen < 0 + */ + + while (dmap->qlen <= -dmap->closing) { + dmap->underrun_count++; + dmap->qlen++; + if ((dmap->flags & DMA_DIRTY) && dmap->applic_profile != APF_CPUINTENS) { + dmap->flags &= ~DMA_DIRTY; + memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, + adev->dmap_out->buffsize); + } + dmap->user_counter += dmap->fragment_size; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + } + if (dmap->qlen > 0) + DMAbuf_launch_output(dev, dmap); + restore_flags(flags); + finish_output_interrupt(dev, dmap); +} + +void DMAbuf_outputintr(int dev, int notify_only) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + struct dma_buffparms *dmap = adev->dmap_out; + + save_flags(flags); + cli(); + if (!(dmap->flags & DMA_NODMA)) { + int chan = dmap->dma, pos, n; + unsigned long f; + + f=claim_dma_lock(); + + if(!isa_dma_bridge_buggy) + disable_dma(dmap->dma); + clear_dma_ff(chan); + pos = dmap->bytes_in_use - get_dma_residue(chan); + if(!isa_dma_bridge_buggy) + enable_dma(dmap->dma); + release_dma_lock(f); + + pos = pos / dmap->fragment_size; /* Actual qhead */ + if (pos < 0 || pos >= dmap->nbufs) + pos = 0; + n = 0; + while (dmap->qhead != pos && n++ < dmap->nbufs) + do_outputintr(dev, notify_only); + } + else + do_outputintr(dev, notify_only); + restore_flags(flags); +} + +static void do_inputintr(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; + + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n"); + return; + } + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + if (dmap->qtail == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + dmap->qlen++; + + if (!(adev->flags & DMA_AUTOMODE)) { + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ); + adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, + dmap->fragment_size, 1); + if (adev->d->trigger) + adev->d->trigger(dev, adev->enable_bits * adev->go); + } + dmap->flags |= DMA_ACTIVE; + } else if (dmap->qlen >= (dmap->nbufs - 1)) { + printk(KERN_WARNING "Sound: Recording overrun\n"); + dmap->underrun_count++; + + /* Just throw away the oldest fragment but keep the engine running */ + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + } else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) { + dmap->qlen++; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + if (dmap->qtail == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + } + if (!(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); + adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1); + if (adev->d->trigger) + adev->d->trigger(dev,adev->enable_bits * adev->go); + } + dmap->flags |= DMA_ACTIVE; + if (dmap->qlen > 0) + { + wake_up(&adev->in_sleeper); + wake_up(&adev->poll_sleeper); + } +} + +void DMAbuf_inputintr(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; + unsigned long flags; + + save_flags(flags); + cli(); + + if (!(dmap->flags & DMA_NODMA)) { + int chan = dmap->dma, pos, n; + unsigned long f; + + f=claim_dma_lock(); + if(!isa_dma_bridge_buggy) + disable_dma(dmap->dma); + clear_dma_ff(chan); + pos = dmap->bytes_in_use - get_dma_residue(chan); + if(!isa_dma_bridge_buggy) + enable_dma(dmap->dma); + release_dma_lock(f); + + pos = pos / dmap->fragment_size; /* Actual qhead */ + if (pos < 0 || pos >= dmap->nbufs) + pos = 0; + + n = 0; + while (dmap->qtail != pos && ++n < dmap->nbufs) + do_inputintr(dev); + } else + do_inputintr(dev); + restore_flags(flags); +} + +int DMAbuf_open_dma(int dev) +{ + /* + * NOTE! This routine opens only the primary DMA channel (output). + */ + struct audio_operations *adev = audio_devs[dev]; + int err; + + if ((err = open_dmap(adev, OPEN_READWRITE, adev->dmap_out)) < 0) + return -EBUSY; + dma_init_buffers(adev->dmap_out); + adev->dmap_out->flags |= DMA_ALLOC_DONE; + adev->dmap_out->fragment_size = adev->dmap_out->buffsize; + + if (adev->dmap_out->dma >= 0) { + unsigned long flags; + + flags=claim_dma_lock(); + clear_dma_ff(adev->dmap_out->dma); + disable_dma(adev->dmap_out->dma); + release_dma_lock(flags); + } + return 0; +} + +void DMAbuf_close_dma(int dev) +{ + close_dmap(audio_devs[dev], audio_devs[dev]->dmap_out); +} + +void DMAbuf_init(int dev, int dma1, int dma2) +{ + struct audio_operations *adev = audio_devs[dev]; + /* + * NOTE! This routine could be called several times. + */ + + /* drag in audio_syms.o */ + { + extern char audio_syms_symbol; + audio_syms_symbol = 0; + } + + if (adev && adev->dmap_out == NULL) { + if (adev->d == NULL) + panic("OSS: audio_devs[%d]->d == NULL\n", dev); + + if (adev->parent_dev) { /* Use DMA map of the parent dev */ + int parent = adev->parent_dev - 1; + adev->dmap_out = audio_devs[parent]->dmap_out; + adev->dmap_in = audio_devs[parent]->dmap_in; + } else { + adev->dmap_out = adev->dmap_in = &adev->dmaps[0]; + adev->dmap_out->dma = dma1; + if (adev->flags & DMA_DUPLEX) { + adev->dmap_in = &adev->dmaps[1]; + adev->dmap_in->dma = dma2; + } + } + /* Persistent DMA buffers allocated here */ + if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) { + if (adev->dmap_in->raw_buf == NULL) + sound_alloc_dmap(adev->dmap_in); + if (adev->dmap_out->raw_buf == NULL) + sound_alloc_dmap(adev->dmap_out); + } + } +} + +/* No kernel lock - DMAbuf_activate_recording protected by global cli/sti */ +static unsigned int poll_input(struct file * file, int dev, poll_table *wait) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; + + if (!(adev->open_mode & OPEN_READ)) + return 0; + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + if (dmap->qlen) + return POLLIN | POLLRDNORM; + return 0; + } + if (dmap->dma_mode != DMODE_INPUT) { + if (dmap->dma_mode == DMODE_NONE && + adev->enable_bits & PCM_ENABLE_INPUT && + !dmap->qlen && adev->go) { + unsigned long flags; + + save_flags(flags); + cli(); + DMAbuf_activate_recording(dev, dmap); + restore_flags(flags); + } + return 0; + } + if (!dmap->qlen) + return 0; + return POLLIN | POLLRDNORM; +} + +static unsigned int poll_output(struct file * file, int dev, poll_table *wait) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + + if (!(adev->open_mode & OPEN_WRITE)) + return 0; + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + if (dmap->qlen) + return POLLOUT | POLLWRNORM; + return 0; + } + if (dmap->dma_mode == DMODE_INPUT) + return 0; + if (dmap->dma_mode == DMODE_NONE) + return POLLOUT | POLLWRNORM; + if (!DMAbuf_space_in_queue(dev)) + return 0; + return POLLOUT | POLLWRNORM; +} + +unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait) +{ + struct audio_operations *adev = audio_devs[dev]; + poll_wait(file, &adev->poll_sleeper, wait); + return poll_input(file, dev, wait) | poll_output(file, dev, wait); +} + +void DMAbuf_deinit(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + /* This routine is called when driver is being unloaded */ + if (!adev) + return; + + /* Persistent DMA buffers deallocated here */ + if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) { + sound_free_dmap(adev->dmap_out); + if (adev->flags & DMA_DUPLEX) + sound_free_dmap(adev->dmap_in); + } +} diff -Nru a/sound/oss/dmasound/Config.help b/sound/oss/dmasound/Config.help --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmasound/Config.help Tue Feb 19 18:08:57 2002 @@ -0,0 +1,47 @@ +CONFIG_DMASOUND + Support built-in audio chips accessible by DMA on various machines + that have them. Note that this symbol does not affect the kernel + directly; rather, it controls whether configuration questions + enabling DMA sound drivers for various specific machine + architectures will be used. + +CONFIG_DMASOUND_ATARI + If you want to use the internal audio of your Atari in Linux, answer + Y to this question. This will provide a Sun-like /dev/audio, + compatible with the Linux/i386 sound system. Otherwise, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + . + +CONFIG_DMASOUND_AWACS + If you want to use the internal audio of your PowerMac in Linux, + answer Y to this question. This will provide a Sun-like /dev/audio, + compatible with the Linux/i386 sound system. Otherwise, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + . + +CONFIG_DMASOUND_PAULA + If you want to use the internal audio of your Amiga in Linux, answer + Y to this question. This will provide a Sun-like /dev/audio, + compatible with the Linux/i386 sound system. Otherwise, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + . + +CONFIG_DMASOUND_Q40 + If you want to use the internal audio of your Q40 in Linux, answer + Y to this question. This will provide a Sun-like /dev/audio, + compatible with the Linux/i386 sound system. Otherwise, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + . + diff -Nru a/sound/oss/dmasound/Config.in b/sound/oss/dmasound/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmasound/Config.in Tue Feb 19 18:08:57 2002 @@ -0,0 +1,27 @@ +# drivers/sound/dmasound/Config.in + +if [ "$CONFIG_ATARI" = "y" ]; then + dep_tristate ' Atari DMA sound support' CONFIG_DMASOUND_ATARI $CONFIG_SOUND +fi +if [ "$CONFIG_ALL_PPC" = "y" ]; then + dep_tristate ' PowerMac DMA sound support' CONFIG_DMASOUND_AWACS $CONFIG_SOUND +fi +if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_APUS" = "y" ]; then + dep_tristate ' Amiga DMA sound support' CONFIG_DMASOUND_PAULA $CONFIG_SOUND +fi +if [ "$CONFIG_Q40" = "y" ]; then + dep_tristate ' Q40 sound support' CONFIG_DMASOUND_Q40 $CONFIG_SOUND +fi +if [ "$CONFIG_DMASOUND_ATARI" = "y" -o \ + "$CONFIG_DMASOUND_AWACS" = "y" -o \ + "$CONFIG_DMASOUND_PAULA" = "y" -o \ + "$CONFIG_DMASOUND_Q40" = "y" ]; then + define_tristate CONFIG_DMASOUND y +else + if [ "$CONFIG_DMASOUND_ATARI" = "m" -o \ + "$CONFIG_DMASOUND_AWACS" = "m" -o \ + "$CONFIG_DMASOUND_PAULA" = "m" -o \ + "$CONFIG_DMASOUND_Q40" = "m" ]; then + define_tristate CONFIG_DMASOUND m + fi +fi diff -Nru a/sound/oss/dmasound/Makefile b/sound/oss/dmasound/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmasound/Makefile Tue Feb 19 18:08:58 2002 @@ -0,0 +1,21 @@ +# +# Makefile for the DMA sound driver +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +export-objs := dmasound_core.o + +obj-$(CONFIG_DMASOUND_ATARI) += dmasound_core.o dmasound_atari.o +obj-$(CONFIG_DMASOUND_AWACS) += dmasound_core.o dmasound_awacs.o +obj-$(CONFIG_DMASOUND_PAULA) += dmasound_core.o dmasound_paula.o +obj-$(CONFIG_DMASOUND_Q40) += dmasound_core.o dmasound_q40.o + +ifeq ($(CONFIG_DMASOUND),y) + O_TARGET = dmasound.o +endif + +include $(TOPDIR)/Rules.make diff -Nru a/sound/oss/dmasound/awacs_defs.h b/sound/oss/dmasound/awacs_defs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmasound/awacs_defs.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,229 @@ +/*********************************************************/ +/* This file was written by someone, somewhere, sometime */ +/* And is released into the Public Domain */ +/*********************************************************/ + +#ifndef _AWACS_DEFS_H_ +#define _AWACS_DEFS_H_ + +/*******************************/ +/* AWACs Audio Register Layout */ +/*******************************/ + +struct awacs_regs { + unsigned control; /* Audio control register */ + unsigned pad0[3]; + unsigned codec_ctrl; /* Codec control register */ + unsigned pad1[3]; + unsigned codec_stat; /* Codec status register */ + unsigned pad2[3]; + unsigned clip_count; /* Clipping count register */ + unsigned pad3[3]; + unsigned byteswap; /* Data is little-endian if 1 */ +}; + +/*******************/ +/* Audio Bit Masks */ +/*******************/ + +/* Audio Control Reg Bit Masks */ +/* ----- ------- --- --- ----- */ +#define MASK_ISFSEL (0xf) /* Input SubFrame Select */ +#define MASK_OSFSEL (0xf << 4) /* Output SubFrame Select */ +#define MASK_RATE (0x7 << 8) /* Sound Rate */ +#define MASK_CNTLERR (0x1 << 11) /* Error */ +#define MASK_PORTCHG (0x1 << 12) /* Port Change */ +#define MASK_IEE (0x1 << 13) /* Enable Interrupt on Error */ +#define MASK_IEPC (0x1 << 14) /* Enable Interrupt on Port Change */ +#define MASK_SSFSEL (0x3 << 15) /* Status SubFrame Select */ + +/* Audio Codec Control Reg Bit Masks */ +/* ----- ----- ------- --- --- ----- */ +#define MASK_NEWECMD (0x1 << 24) /* Lock: don't write to reg when 1 */ +#define MASK_EMODESEL (0x3 << 22) /* Send info out on which frame? */ +#define MASK_EXMODEADDR (0x3ff << 12) /* Extended Mode Address -- 10 bits */ +#define MASK_EXMODEDATA (0xfff) /* Extended Mode Data -- 12 bits */ + +/* Audio Codec Control Address Values / Masks */ +/* ----- ----- ------- ------- ------ - ----- */ +#define MASK_ADDR0 (0x0 << 12) /* Expanded Data Mode Address 0 */ +#define MASK_ADDR_MUX MASK_ADDR0 /* Mux Control */ +#define MASK_ADDR_GAIN MASK_ADDR0 + +#define MASK_ADDR1 (0x1 << 12) /* Expanded Data Mode Address 1 */ +#define MASK_ADDR_MUTE MASK_ADDR1 +#define MASK_ADDR_RATE MASK_ADDR1 + +#define MASK_ADDR2 (0x2 << 12) /* Expanded Data Mode Address 2 */ +#define MASK_ADDR_VOLA MASK_ADDR2 /* Volume Control A -- Headphones */ +#define MASK_ADDR_VOLHD MASK_ADDR2 + +#define MASK_ADDR4 (0x4 << 12) /* Expanded Data Mode Address 4 */ +#define MASK_ADDR_VOLC MASK_ADDR4 /* Volume Control C -- Speaker */ +#define MASK_ADDR_VOLSPK MASK_ADDR4 + +/* additional registers of screamer */ +#define MASK_ADDR5 (0x5 << 12) /* Expanded Data Mode Address 5 */ +#define MASK_ADDR6 (0x6 << 12) /* Expanded Data Mode Address 6 */ +#define MASK_ADDR7 (0x7 << 12) /* Expanded Data Mode Address 7 */ + +/* Address 0 Bit Masks & Macros */ +/* ------- - --- ----- - ------ */ +#define MASK_GAINRIGHT (0xf) /* Gain Right Mask */ +#define MASK_GAINLEFT (0xf << 4) /* Gain Left Mask */ +#define MASK_GAINLINE (0x1 << 8) /* Change Gain for Line??? */ +#define MASK_GAINMIC (0x0 << 8) /* Change Gain for Mic??? */ + +#define MASK_MUX_CD (0x1 << 9) /* Select CD in MUX */ +#define MASK_MUX_AUDIN (0x1 << 10) /* Select Audio In in MUX */ +#define MASK_MUX_MIC (0x1 << 11) /* Select Mic in MUX */ +#define MASK_MUX_LINE MASK_MUX_AUDIN + +#define GAINRIGHT(x) ((x) & MASK_GAINRIGHT) +#define GAINLEFT(x) (((x) << 4) & MASK_GAINLEFT) + +/* Address 1 Bit Masks */ +/* ------- - --- ----- */ +#define MASK_ADDR1RES1 (0x3) /* Reserved */ +#define MASK_RECALIBRATE (0x1 << 2) /* Recalibrate */ +#define MASK_SAMPLERATE (0x7 << 3) /* Sample Rate: */ +#define MASK_LOOPTHRU (0x1 << 6) /* Loopthrough Enable */ +#define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */ +#define MASK_SPKMUTE MASK_CMUTE +#define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */ +#define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */ +#define MASK_HDMUTE MASK_AMUTE +#define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */ + +#define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */ +#define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */ +#define SAMPLERATE_24000 (0x2 << 3) /* 24 or 22.05 kHz */ +#define SAMPLERATE_19200 (0x3 << 3) /* 19.2 or 17.64 kHz */ +#define SAMPLERATE_16000 (0x4 << 3) /* 16 or 14.7 kHz */ +#define SAMPLERATE_12000 (0x5 << 3) /* 12 or 11.025 kHz */ +#define SAMPLERATE_9600 (0x6 << 3) /* 9.6 or 8.82 kHz */ +#define SAMPLERATE_8000 (0x7 << 3) /* 8 or 7.35 kHz */ + +/* Address 2 & 4 Bit Masks & Macros */ +/* ------- - - - --- ----- - ------ */ +#define MASK_OUTVOLRIGHT (0xf) /* Output Right Volume */ +#define MASK_ADDR2RES1 (0x2 << 4) /* Reserved */ +#define MASK_ADDR4RES1 MASK_ADDR2RES1 +#define MASK_OUTVOLLEFT (0xf << 6) /* Output Left Volume */ +#define MASK_ADDR2RES2 (0x2 << 10) /* Reserved */ +#define MASK_ADDR4RES2 MASK_ADDR2RES2 + +#define VOLRIGHT(x) (((~(x)) & MASK_OUTVOLRIGHT)) +#define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT) + +/* Audio Codec Status Reg Bit Masks */ +/* ----- ----- ------ --- --- ----- */ +#define MASK_EXTEND (0x1 << 23) /* Extend */ +#define MASK_VALID (0x1 << 22) /* Valid Data? */ +#define MASK_OFLEFT (0x1 << 21) /* Overflow Left */ +#define MASK_OFRIGHT (0x1 << 20) /* Overflow Right */ +#define MASK_ERRCODE (0xf << 16) /* Error Code */ +#define MASK_REVISION (0xf << 12) /* Revision Number */ +#define MASK_MFGID (0xf << 8) /* Mfg. ID */ +#define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */ +#define MASK_INPPORT (0xf) /* Input Port */ +#define MASK_HDPCONN 8 /* headphone plugged in */ + +/* Clipping Count Reg Bit Masks */ +/* -------- ----- --- --- ----- */ +#define MASK_CLIPLEFT (0xff << 7) /* Clipping Count, Left Channel */ +#define MASK_CLIPRIGHT (0xff) /* Clipping Count, Right Channel */ + +/* DBDMA ChannelStatus Bit Masks */ +/* ----- ------------- --- ----- */ +#define MASK_CSERR (0x1 << 7) /* Error */ +#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */ +#define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */ +#define MASK_WAIT (0x1) /* Wait */ + +/* Various Rates */ +/* ------- ----- */ +#define RATE_48000 (0x0 << 8) /* 48 kHz */ +#define RATE_44100 (0x0 << 8) /* 44.1 kHz */ +#define RATE_32000 (0x1 << 8) /* 32 kHz */ +#define RATE_29400 (0x1 << 8) /* 29.4 kHz */ +#define RATE_24000 (0x2 << 8) /* 24 kHz */ +#define RATE_22050 (0x2 << 8) /* 22.05 kHz */ +#define RATE_19200 (0x3 << 8) /* 19.2 kHz */ +#define RATE_17640 (0x3 << 8) /* 17.64 kHz */ +#define RATE_16000 (0x4 << 8) /* 16 kHz */ +#define RATE_14700 (0x4 << 8) /* 14.7 kHz */ +#define RATE_12000 (0x5 << 8) /* 12 kHz */ +#define RATE_11025 (0x5 << 8) /* 11.025 kHz */ +#define RATE_9600 (0x6 << 8) /* 9.6 kHz */ +#define RATE_8820 (0x6 << 8) /* 8.82 kHz */ +#define RATE_8000 (0x7 << 8) /* 8 kHz */ +#define RATE_7350 (0x7 << 8) /* 7.35 kHz */ + +#define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */ + + +/* Burgundy values */ + +#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12) +#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12) + +#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12) + +#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12) +#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12) + +#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12) + +#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12) + +#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12) +#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12) +#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1) +#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2) +#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3) +#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + +#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1) +#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2) +#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3) +#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + + +/* These are all default values for the burgundy */ +#define DEF_BURGUNDY_INPSEL21 (0xAA) +#define DEF_BURGUNDY_INPSEL3 (0x0A) + +#define DEF_BURGUNDY_GAINCD (0x33) +#define DEF_BURGUNDY_GAINLINE (0x44) +#define DEF_BURGUNDY_GAINMIC (0x44) +#define DEF_BURGUNDY_GAINMODEM (0x06) + +/* Remember: lowest volume here is 0x9b */ +#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC) +#define DEF_BURGUNDY_VOLLINE (0x00000000) +#define DEF_BURGUNDY_VOLMIC (0x00000000) +#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC) + +#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f) +#define DEF_BURGUNDY_OUTPUTENABLES (0x0A) + +#define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) + +#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E) + +#define DEF_BURGUNDY_ATTENSPEAKER (0x44) +#define DEF_BURGUNDY_ATTENLINEOUT (0xCC) +#define DEF_BURGUNDY_ATTENHP (0xCC) + +#endif /* _AWACS_DEFS_H_ */ diff -Nru a/sound/oss/dmasound/dmasound.h b/sound/oss/dmasound/dmasound.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmasound/dmasound.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,248 @@ + +/* + * linux/drivers/sound/dmasound/dmasound.h + * + * + * Minor numbers for the sound driver. + * + * Unfortunately Creative called the codec chip of SB as a DSP. For this + * reason the /dev/dsp is reserved for digitized audio use. There is a + * device for true DSP processors but it will be called something else. + * In v3.0 it's /dev/sndproc but this could be a temporary solution. + */ + + +#include + + +#define SND_NDEVS 256 /* Number of supported devices */ +#define SND_DEV_CTL 0 /* Control port /dev/mixer */ +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + synthesizer and MIDI output) */ +#define SND_DEV_MIDIN 2 /* Raw midi access */ +#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ +#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ +#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ +#define SND_DEV_STATUS 6 /* /dev/sndstat */ +/* #7 not in use now. Was in 2.4. Free for use after v3.0. */ +#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ +#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ +#define SND_DEV_PSS SND_DEV_SNDPROC + +#define DSP_DEFAULT_SPEED 8000 + +#define ON 1 +#define OFF 0 + +#define MAX_AUDIO_DEV 5 +#define MAX_MIXER_DEV 2 +#define MAX_SYNTH_DEV 3 +#define MAX_MIDI_DEV 6 +#define MAX_TIMER_DEV 3 + + +#define MAX_CATCH_RADIUS 10 +#define MIN_BUFFERS 4 +#define MIN_BUFSIZE 4 /* in KB */ +#define MAX_BUFSIZE 128 /* Limit for Amiga in KB */ + + +#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) +#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) + +#define IOCTL_IN(arg, ret) \ + do { int error = get_user(ret, (int *)(arg)); \ + if (error) return error; \ + } while (0) +#define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) + +static inline int ioctl_return(int *addr, int value) +{ + return value < 0 ? value : put_user(value, addr); +} + + + /* + * Configuration + */ + +#undef HAS_8BIT_TABLES +#undef HAS_14BIT_TABLES +#undef HAS_16BIT_TABLES +#undef HAS_RECORD + +#if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\ + defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\ + defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE) +#define HAS_8BIT_TABLES +#endif +#if defined(CONFIG_DMASOUND_AWACS) || defined(CONFIG_DMASOUND_AWACS_MODULE) +#define HAS_16BIT_TABLES +#define HAS_RECORD +#endif + + + /* + * Initialization + */ + +extern int dmasound_init(void); +#ifdef MODULE +extern void dmasound_deinit(void); +#else +#define dmasound_deinit() do { } while (0) +#endif + + + /* + * Machine definitions + */ + +typedef struct { + const char *name; + const char *name2; + void (*open)(void); + void (*release)(void); + void *(*dma_alloc)(unsigned int, int); + void (*dma_free)(void *, unsigned int); + int (*irqinit)(void); +#ifdef MODULE + void (*irqcleanup)(void); +#endif + void (*init)(void); + void (*silence)(void); + int (*setFormat)(int); + int (*setVolume)(int); + int (*setBass)(int); + int (*setTreble)(int); + int (*setGain)(int); + void (*play)(void); + void (*record)(void); /* optional */ + void (*mixer_init)(void); /* optional */ + int (*mixer_ioctl)(u_int, u_long); /* optional */ + void (*write_sq_setup)(void); /* optional */ + void (*read_sq_setup)(void); /* optional */ + void (*sq_open)(void); /* optional */ + int (*state_info)(char *); /* optional */ + void (*abort_read)(void); /* optional */ + int min_dsp_speed; +} MACHINE; + + + /* + * Low level stuff + */ + +typedef struct { + int format; /* AFMT_* */ + int stereo; /* 0 = mono, 1 = stereo */ + int size; /* 8/16 bit*/ + int speed; /* speed */ +} SETTINGS; + +typedef struct { + ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); +} TRANS; + +struct sound_settings { + MACHINE mach; /* machine dependent things */ + SETTINGS hard; /* hardware settings */ + SETTINGS soft; /* software settings */ + SETTINGS dsp; /* /dev/dsp default settings */ + TRANS *trans_write; /* supported translations */ +#ifdef HAS_RECORD + TRANS *trans_read; /* supported translations */ +#endif + int volume_left; /* volume (range is machine dependent) */ + int volume_right; + int bass; /* tone (range is machine dependent) */ + int treble; + int gain; + int minDev; /* minor device number currently open */ +}; + +extern struct sound_settings dmasound; + +extern char dmasound_ulaw2dma8[]; +extern char dmasound_alaw2dma8[]; +extern short dmasound_ulaw2dma16[]; +extern short dmasound_alaw2dma16[]; + + + /* + * Mid level stuff + */ + +static inline int dmasound_set_volume(int volume) +{ + return dmasound.mach.setVolume(volume); +} + +static inline int dmasound_set_bass(int bass) +{ + return dmasound.mach.setBass ? dmasound.mach.setBass(bass) : 50; +} + +static inline int dmasound_set_treble(int treble) +{ + return dmasound.mach.setTreble ? dmasound.mach.setTreble(treble) : 50; +} + +static inline int dmasound_set_gain(int gain) +{ + return dmasound.mach.setGain ? dmasound.mach.setGain(gain) : 100; +} + + + /* + * Sound queue stuff, the heart of the driver + */ + +struct sound_queue { + /* buffers allocated for this queue */ + int numBufs; + int bufSize; /* in bytes */ + char **buffers; + + /* current parameters */ + int max_count; + int block_size; /* in bytes */ + int max_active; + + /* it shouldn't be necessary to declare any of these volatile */ + int front, rear, count; + int rear_size; + /* + * The use of the playing field depends on the hardware + * + * Atari, PMac: The number of frames that are loaded/playing + * + * Amiga: Bit 0 is set: a frame is loaded + * Bit 1 is set: a frame is playing + */ + int active; + wait_queue_head_t action_queue, open_queue, sync_queue; + int open_mode; + int busy, syncing; +}; + +#define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ) +#define WAKE_UP(queue) (wake_up_interruptible(&queue)) + +extern struct sound_queue dmasound_write_sq; +extern struct sound_queue dmasound_read_sq; + +#define write_sq dmasound_write_sq +#define read_sq dmasound_read_sq + +extern int dmasound_catchRadius; + +#define catchRadius dmasound_catchRadius + diff -Nru a/sound/oss/dmasound/dmasound_atari.c b/sound/oss/dmasound/dmasound_atari.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmasound/dmasound_atari.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,1562 @@ + +/* + * linux/drivers/sound/dmasound/dmasound_atari.c + * + * Atari TT and Falcon DMA Sound Driver + * + * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dmasound.h" + + +extern void atari_microwire_cmd(int cmd); + + +static int is_falcon; +static int write_sq_ignore_int = 0; /* ++TeSche: used for Falcon */ + +static int expand_bal; /* Balance factor for expanding (not volume!) */ +static int expand_data; /* Data for expanding */ + + +/*** Translations ************************************************************/ + + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + +static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); + + +/*** Low level stuff *********************************************************/ + + +static void AtaOpen(void); +static void AtaRelease(void); +static void *AtaAlloc(unsigned int size, int flags); +static void AtaFree(void *, unsigned int size); +static int AtaIrqInit(void); +#ifdef MODULE +static void AtaIrqCleanUp(void); +#endif /* MODULE */ +static int AtaSetBass(int bass); +static int AtaSetTreble(int treble); +static void TTSilence(void); +static void TTInit(void); +static int TTSetFormat(int format); +static int TTSetVolume(int volume); +static int TTSetGain(int gain); +static void FalconSilence(void); +static void FalconInit(void); +static int FalconSetFormat(int format); +static int FalconSetVolume(int volume); +static void AtaPlayNextFrame(int index); +static void AtaPlay(void); +static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp); + +/*** Mid level stuff *********************************************************/ + +static void TTMixerInit(void); +static void FalconMixerInit(void); +static int AtaMixerIoctl(u_int cmd, u_long arg); +static int TTMixerIoctl(u_int cmd, u_long arg); +static int FalconMixerIoctl(u_int cmd, u_long arg); +static void AtaWriteSqSetup(void); +static void AtaSqOpen(void); +static int TTStateInfo(char *buffer); +static int FalconStateInfo(char *buffer); + + +/*** Translations ************************************************************/ + + +static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8 + : dmasound_alaw2dma8; + ssize_t count, used; + u_char *p = &frame[*frameUsed]; + + count = min_t(unsigned long, userCount, frameLeft); + if (dmasound.soft.stereo) + count &= ~1; + used = count; + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + *p++ = table[data]; + count--; + } + *frameUsed += used; + return used; +} + + +static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + void *p = &frame[*frameUsed]; + + count = min_t(unsigned long, userCount, frameLeft); + if (dmasound.soft.stereo) + count &= ~1; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + *frameUsed += used; + return used; +} + + +static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft); + used = count; + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + *p++ = data ^ 0x80; + count--; + } + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *p++ = data ^ 0x8080; + count--; + } + } + *frameUsed += used; + return used; +} + + +static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + void *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft) & ~3; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>2; + used = count*4; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + *p++ = data ^ 0x80008000; + count--; + } + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + count = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>2; + used = count*4; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data); + *p++ = data; + count--; + } + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + count = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + *p++ = data; + *p++ = data; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>2; + used = count; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data) ^ 0x80008000; + *p++ = data; + count--; + } + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8 + : dmasound_alaw2dma8; + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = expand_data; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (!userCount) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 2) { + u_char c; + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c] << 8; + if (get_user(c, userPtr++)) + return -EFAULT; + data |= table[c]; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = expand_data; + while (frameLeft) { + if (bal < 0) { + if (!userCount) + break; + if (get_user(data, userPtr++)) + return -EFAULT; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 2) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = expand_data; + while (frameLeft) { + if (bal < 0) { + if (!userCount) + break; + if (get_user(data, userPtr++)) + return -EFAULT; + data ^= 0x80; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 2) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8080; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data ^= 0x80008000; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data); + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data) ^ 0x80008000; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static TRANS transTTNormal = { + ct_ulaw: ata_ct_law, + ct_alaw: ata_ct_law, + ct_s8: ata_ct_s8, + ct_u8: ata_ct_u8, +}; + +static TRANS transTTExpanding = { + ct_ulaw: ata_ctx_law, + ct_alaw: ata_ctx_law, + ct_s8: ata_ctx_s8, + ct_u8: ata_ctx_u8, +}; + +static TRANS transFalconNormal = { + ct_ulaw: ata_ct_law, + ct_alaw: ata_ct_law, + ct_s8: ata_ct_s8, + ct_u8: ata_ct_u8, + ct_s16be: ata_ct_s16be, + ct_u16be: ata_ct_u16be, + ct_s16le: ata_ct_s16le, + ct_u16le: ata_ct_u16le +}; + +static TRANS transFalconExpanding = { + ct_ulaw: ata_ctx_law, + ct_alaw: ata_ctx_law, + ct_s8: ata_ctx_s8, + ct_u8: ata_ctx_u8, + ct_s16be: ata_ctx_s16be, + ct_u16be: ata_ctx_u16be, + ct_s16le: ata_ctx_s16le, + ct_u16le: ata_ctx_u16le, +}; + + +/*** Low level stuff *********************************************************/ + + + +/* + * Atari (TT/Falcon) + */ + +static void AtaOpen(void) +{ + MOD_INC_USE_COUNT; +} + +static void AtaRelease(void) +{ + MOD_DEC_USE_COUNT; +} + +static void *AtaAlloc(unsigned int size, int flags) +{ + return atari_stram_alloc(size, "dmasound"); +} + +static void AtaFree(void *obj, unsigned int size) +{ + atari_stram_free( obj ); +} + +static int __init AtaIrqInit(void) +{ + /* Set up timer A. Timer A + will receive a signal upon end of playing from the sound + hardware. Furthermore Timer A is able to count events + and will cause an interrupt after a programmed number + of events. So all we need to keep the music playing is + to provide the sound hardware with new data upon + an interrupt from timer A. */ + mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */ + mfp.tim_dt_a = 1; /* Cause interrupt after first event. */ + mfp.tim_ct_a = 8; /* Turn on event counting. */ + /* Register interrupt handler. */ + request_irq(IRQ_MFP_TIMA, AtaInterrupt, IRQ_TYPE_SLOW, "DMA sound", + AtaInterrupt); + mfp.int_en_a |= 0x20; /* Turn interrupt on. */ + mfp.int_mk_a |= 0x20; + return 1; +} + +#ifdef MODULE +static void AtaIrqCleanUp(void) +{ + mfp.tim_ct_a = 0; /* stop timer */ + mfp.int_en_a &= ~0x20; /* turn interrupt off */ + free_irq(IRQ_MFP_TIMA, AtaInterrupt); +} +#endif /* MODULE */ + + +#define TONE_VOXWARE_TO_DB(v) \ + (((v) < 0) ? -12 : ((v) > 100) ? 12 : ((v) - 50) * 6 / 25) +#define TONE_DB_TO_VOXWARE(v) (((v) * 25 + ((v) > 0 ? 5 : -5)) / 6 + 50) + + +static int AtaSetBass(int bass) +{ + dmasound.bass = TONE_VOXWARE_TO_DB(bass); + atari_microwire_cmd(MW_LM1992_BASS(dmasound.bass)); + return TONE_DB_TO_VOXWARE(dmasound.bass); +} + + +static int AtaSetTreble(int treble) +{ + dmasound.treble = TONE_VOXWARE_TO_DB(treble); + atari_microwire_cmd(MW_LM1992_TREBLE(dmasound.treble)); + return TONE_DB_TO_VOXWARE(dmasound.treble); +} + + + +/* + * TT + */ + + +static void TTSilence(void) +{ + tt_dmasnd.ctrl = DMASND_CTRL_OFF; + atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */ +} + + +static void TTInit(void) +{ + int mode, i, idx; + const int freq[4] = {50066, 25033, 12517, 6258}; + + /* search a frequency that fits into the allowed error range */ + + idx = -1; + for (i = 0; i < ARRAY_SIZE(freq); i++) + /* this isn't as much useful for a TT than for a Falcon, but + * then it doesn't hurt very much to implement it for a TT too. + */ + if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius) + idx = i; + if (idx > -1) { + dmasound.soft.speed = freq[idx]; + dmasound.trans_write = &transTTNormal; + } else + dmasound.trans_write = &transTTExpanding; + + TTSilence(); + dmasound.hard = dmasound.soft; + + if (dmasound.hard.speed > 50066) { + /* we would need to squeeze the sound, but we won't do that */ + dmasound.hard.speed = 50066; + mode = DMASND_MODE_50KHZ; + dmasound.trans_write = &transTTNormal; + } else if (dmasound.hard.speed > 25033) { + dmasound.hard.speed = 50066; + mode = DMASND_MODE_50KHZ; + } else if (dmasound.hard.speed > 12517) { + dmasound.hard.speed = 25033; + mode = DMASND_MODE_25KHZ; + } else if (dmasound.hard.speed > 6258) { + dmasound.hard.speed = 12517; + mode = DMASND_MODE_12KHZ; + } else { + dmasound.hard.speed = 6258; + mode = DMASND_MODE_6KHZ; + } + + tt_dmasnd.mode = (dmasound.hard.stereo ? + DMASND_MODE_STEREO : DMASND_MODE_MONO) | + DMASND_MODE_8BIT | mode; + + expand_bal = -dmasound.soft.speed; +} + + +static int TTSetFormat(int format) +{ + /* TT sound DMA supports only 8bit modes */ + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_S8: + case AFMT_U8: + break; + default: + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = 8; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = 8; + } + TTInit(); + + return format; +} + + +#define VOLUME_VOXWARE_TO_DB(v) \ + (((v) < 0) ? -40 : ((v) > 100) ? 0 : ((v) * 2) / 5 - 40) +#define VOLUME_DB_TO_VOXWARE(v) ((((v) + 40) * 5 + 1) / 2) + + +static int TTSetVolume(int volume) +{ + dmasound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff); + atari_microwire_cmd(MW_LM1992_BALLEFT(dmasound.volume_left)); + dmasound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8); + atari_microwire_cmd(MW_LM1992_BALRIGHT(dmasound.volume_right)); + return VOLUME_DB_TO_VOXWARE(dmasound.volume_left) | + (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8); +} + + +#define GAIN_VOXWARE_TO_DB(v) \ + (((v) < 0) ? -80 : ((v) > 100) ? 0 : ((v) * 4) / 5 - 80) +#define GAIN_DB_TO_VOXWARE(v) ((((v) + 80) * 5 + 1) / 4) + +static int TTSetGain(int gain) +{ + dmasound.gain = GAIN_VOXWARE_TO_DB(gain); + atari_microwire_cmd(MW_LM1992_VOLUME(dmasound.gain)); + return GAIN_DB_TO_VOXWARE(dmasound.gain); +} + + + +/* + * Falcon + */ + + +static void FalconSilence(void) +{ + /* stop playback, set sample rate 50kHz for PSG sound */ + tt_dmasnd.ctrl = DMASND_CTRL_OFF; + tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT; + tt_dmasnd.int_div = 0; /* STE compatible divider */ + tt_dmasnd.int_ctrl = 0x0; + tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */ + tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */ + tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */ + tt_dmasnd.adc_src = 3; /* ADC Input = PSG */ +} + + +static void FalconInit(void) +{ + int divider, i, idx; + const int freq[8] = {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195}; + + /* search a frequency that fits into the allowed error range */ + + idx = -1; + for (i = 0; i < ARRAY_SIZE(freq); i++) + /* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would + * be playable without expanding, but that now a kernel runtime + * option + */ + if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius) + idx = i; + if (idx > -1) { + dmasound.soft.speed = freq[idx]; + dmasound.trans_write = &transFalconNormal; + } else + dmasound.trans_write = &transFalconExpanding; + + FalconSilence(); + dmasound.hard = dmasound.soft; + + if (dmasound.hard.size == 16) { + /* the Falcon can play 16bit samples only in stereo */ + dmasound.hard.stereo = 1; + } + + if (dmasound.hard.speed > 49170) { + /* we would need to squeeze the sound, but we won't do that */ + dmasound.hard.speed = 49170; + divider = 1; + dmasound.trans_write = &transFalconNormal; + } else if (dmasound.hard.speed > 32780) { + dmasound.hard.speed = 49170; + divider = 1; + } else if (dmasound.hard.speed > 24585) { + dmasound.hard.speed = 32780; + divider = 2; + } else if (dmasound.hard.speed > 19668) { + dmasound.hard.speed = 24585; + divider = 3; + } else if (dmasound.hard.speed > 16390) { + dmasound.hard.speed = 19668; + divider = 4; + } else if (dmasound.hard.speed > 12292) { + dmasound.hard.speed = 16390; + divider = 5; + } else if (dmasound.hard.speed > 9834) { + dmasound.hard.speed = 12292; + divider = 7; + } else if (dmasound.hard.speed > 8195) { + dmasound.hard.speed = 9834; + divider = 9; + } else { + dmasound.hard.speed = 8195; + divider = 11; + } + tt_dmasnd.int_div = divider; + + /* Setup Falcon sound DMA for playback */ + tt_dmasnd.int_ctrl = 0x4; /* Timer A int at play end */ + tt_dmasnd.track_select = 0x0; /* play 1 track, track 1 */ + tt_dmasnd.cbar_src = 0x0001; /* DMA(25MHz) --> DAC */ + tt_dmasnd.cbar_dst = 0x0000; + tt_dmasnd.rec_track_select = 0; + tt_dmasnd.dac_src = 2; /* connect matrix to DAC */ + tt_dmasnd.adc_src = 0; /* ADC Input = Mic */ + + tt_dmasnd.mode = (dmasound.hard.stereo ? + DMASND_MODE_STEREO : DMASND_MODE_MONO) | + ((dmasound.hard.size == 8) ? + DMASND_MODE_8BIT : DMASND_MODE_16BIT) | + DMASND_MODE_6KHZ; + + expand_bal = -dmasound.soft.speed; +} + + +static int FalconSetFormat(int format) +{ + int size; + /* Falcon sound DMA supports 8bit and 16bit modes */ + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + size = 8; + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = size; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = dmasound.soft.size; + } + + FalconInit(); + + return format; +} + + +/* This is for the Falcon output *attenuation* in 1.5dB steps, + * i.e. output level from 0 to -22.5dB in -1.5dB steps. + */ +#define VOLUME_VOXWARE_TO_ATT(v) \ + ((v) < 0 ? 15 : (v) > 100 ? 0 : 15 - (v) * 3 / 20) +#define VOLUME_ATT_TO_VOXWARE(v) (100 - (v) * 20 / 3) + + +static int FalconSetVolume(int volume) +{ + dmasound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff); + dmasound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8); + tt_dmasnd.output_atten = dmasound.volume_left << 8 | dmasound.volume_right << 4; + return VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) | + VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8; +} + + +static void AtaPlayNextFrame(int index) +{ + char *start, *end; + + /* used by AtaPlay() if all doubts whether there really is something + * to be played are already wiped out. + */ + start = write_sq.buffers[write_sq.front]; + end = start+((write_sq.count == index) ? write_sq.rear_size + : write_sq.block_size); + /* end might not be a legal virtual address. */ + DMASNDSetEnd(virt_to_phys(end - 1) + 1); + DMASNDSetBase(virt_to_phys(start)); + /* Since only an even number of samples per frame can + be played, we might lose one byte here. (TO DO) */ + write_sq.front = (write_sq.front+1) % write_sq.max_count; + write_sq.active++; + tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT; +} + + +static void AtaPlay(void) +{ + /* ++TeSche: Note that write_sq.active is no longer just a flag but + * holds the number of frames the DMA is currently programmed for + * instead, may be 0, 1 (currently being played) or 2 (pre-programmed). + * + * Changes done to write_sq.count and write_sq.active are a bit more + * subtle again so now I must admit I also prefer disabling the irq + * here rather than considering all possible situations. But the point + * is that disabling the irq doesn't have any bad influence on this + * version of the driver as we benefit from having pre-programmed the + * DMA wherever possible: There's no need to reload the DMA at the + * exact time of an interrupt but only at some time while the + * pre-programmed frame is playing! + */ + atari_disable_irq(IRQ_MFP_TIMA); + + if (write_sq.active == 2 || /* DMA is 'full' */ + write_sq.count <= 0) { /* nothing to do */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + + if (write_sq.active == 0) { + /* looks like there's nothing 'in' the DMA yet, so try + * to put two frames into it (at least one is available). + */ + if (write_sq.count == 1 && + write_sq.rear_size < write_sq.block_size && + !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + AtaPlayNextFrame(1); + if (write_sq.count == 1) { + /* no more frames */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + if (write_sq.count == 2 && + write_sq.rear_size < write_sq.block_size && + !write_sq.syncing) { + /* hmmm, there were two frames, but the second + * one is not yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + AtaPlayNextFrame(2); + } else { + /* there's already a frame being played so we may only stuff + * one new into the DMA, but even if this may be the last + * frame existing the previous one is still on write_sq.count. + */ + if (write_sq.count == 2 && + write_sq.rear_size < write_sq.block_size && + !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + AtaPlayNextFrame(2); + } + atari_enable_irq(IRQ_MFP_TIMA); +} + + +static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ +#if 0 + /* ++TeSche: if you should want to test this... */ + static int cnt = 0; + if (write_sq.active == 2) + if (++cnt == 10) { + /* simulate losing an interrupt */ + cnt = 0; + return; + } +#endif + + if (write_sq_ignore_int && is_falcon) { + /* ++TeSche: Falcon only: ignore first irq because it comes + * immediately after starting a frame. after that, irqs come + * (almost) like on the TT. + */ + write_sq_ignore_int = 0; + return; + } + + if (!write_sq.active) { + /* playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(write_sq.sync_queue); + return; + } + + /* Probably ;) one frame is finished. Well, in fact it may be that a + * pre-programmed one is also finished because there has been a long + * delay in interrupt delivery and we've completely lost one, but + * there's no way to detect such a situation. In such a case the last + * frame will be played more than once and the situation will recover + * as soon as the irq gets through. + */ + write_sq.count--; + write_sq.active--; + + if (!write_sq.active) { + tt_dmasnd.ctrl = DMASND_CTRL_OFF; + write_sq_ignore_int = 1; + } + + WAKE_UP(write_sq.action_queue); + /* At least one block of the queue is free now + so wake up a writing process blocked because + of a full queue. */ + + if ((write_sq.active != 1) || (write_sq.count != 1)) + /* We must be a bit carefully here: write_sq.count indicates the + * number of buffers used and not the number of frames to be + * played. If write_sq.count==1 and write_sq.active==1 that + * means the only remaining frame was already programmed + * earlier (and is currently running) so we mustn't call + * AtaPlay() here, otherwise we'll play one frame too much. + */ + AtaPlay(); + + if (!write_sq.active) WAKE_UP(write_sq.sync_queue); + /* We are not playing after AtaPlay(), so there + is nothing to play any more. Wake up a process + waiting for audio output to drain. */ +} + + +/*** Mid level stuff *********************************************************/ + + +/* + * /dev/mixer abstraction + */ + +#define RECLEVEL_VOXWARE_TO_GAIN(v) \ + ((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20) +#define RECLEVEL_GAIN_TO_VOXWARE(v) (((v) * 20 + 2) / 3) + + +static void __init TTMixerInit(void) +{ + atari_microwire_cmd(MW_LM1992_VOLUME(0)); + dmasound.volume_left = 0; + atari_microwire_cmd(MW_LM1992_BALLEFT(0)); + dmasound.volume_right = 0; + atari_microwire_cmd(MW_LM1992_BALRIGHT(0)); + atari_microwire_cmd(MW_LM1992_TREBLE(0)); + atari_microwire_cmd(MW_LM1992_BASS(0)); +} + +static void __init FalconMixerInit(void) +{ + dmasound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8; + dmasound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4; +} + +static int AtaMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_SPEAKER: + if (is_falcon || MACH_IS_TT) { + int porta; + cli(); + sound_ym.rd_data_reg_sel = 14; + porta = sound_ym.rd_data_reg_sel; + sti(); + return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); + } + break; + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_volume(data)); + case SOUND_MIXER_WRITE_SPEAKER: + if (is_falcon || MACH_IS_TT) { + int porta; + IOCTL_IN(arg, data); + cli(); + sound_ym.rd_data_reg_sel = 14; + porta = (sound_ym.rd_data_reg_sel & ~0x40) | + (data < 50 ? 0x40 : 0); + sound_ym.wd_data = porta; + sti(); + return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); + } + } + return -EINVAL; +} + + +static int TTMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_RECMASK: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_DEVMASK: + return IOCTL_OUT(arg, + SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS | + (MACH_IS_TT ? SOUND_MASK_SPEAKER : 0)); + case SOUND_MIXER_READ_STEREODEVS: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME); + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, + VOLUME_DB_TO_VOXWARE(dmasound.volume_left) | + (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8)); + case SOUND_MIXER_READ_BASS: + return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.bass)); + case SOUND_MIXER_READ_TREBLE: + return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.treble)); + case SOUND_MIXER_READ_OGAIN: + return IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(dmasound.gain)); + case SOUND_MIXER_WRITE_BASS: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_bass(data)); + case SOUND_MIXER_WRITE_TREBLE: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_treble(data)); + case SOUND_MIXER_WRITE_OGAIN: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_gain(data)); + } + return AtaMixerIoctl(cmd, arg); +} + +static int FalconMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_RECMASK: + return IOCTL_OUT(arg, SOUND_MASK_MIC); + case SOUND_MIXER_READ_DEVMASK: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER); + case SOUND_MIXER_READ_STEREODEVS: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC); + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, + VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) | + VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + tt_dmasnd.input_gain = + RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 | + RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff); + /* fall thru, return set value */ + case SOUND_MIXER_READ_MIC: + return IOCTL_OUT(arg, + RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) | + RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8); + } + return AtaMixerIoctl(cmd, arg); +} + +static void AtaWriteSqSetup(void) +{ + write_sq_ignore_int = 0; +} + +static void AtaSqOpen(void) +{ + write_sq_ignore_int = 1; +} + +static int TTStateInfo(char *buffer) +{ + int len = 0; + len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n", + dmasound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n", + dmasound.volume_right); + len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n", + dmasound.bass); + len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n", + dmasound.treble); + return len; +} + +static int FalconStateInfo(char *buffer) +{ + int len = 0; + len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n", + dmasound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n", + dmasound.volume_right); + return len; +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machTT = { + name: "Atari", + name2: "TT", + open: AtaOpen, + release: AtaRelease, + dma_alloc: AtaAlloc, + dma_free: AtaFree, + irqinit: AtaIrqInit, +#ifdef MODULE + irqcleanup: AtaIrqCleanUp, +#endif /* MODULE */ + init: TTInit, + silence: TTSilence, + setFormat: TTSetFormat, + setVolume: TTSetVolume, + setBass: AtaSetBass, + setTreble: AtaSetTreble, + setGain: TTSetGain, + play: AtaPlay, + mixer_init: TTMixerInit, + mixer_ioctl: TTMixerIoctl, + write_sq_setup: AtaWriteSqSetup, + sq_open: AtaSqOpen, + state_info: TTStateInfo, + min_dsp_speed: 6258, +}; + +static MACHINE machFalcon = { + name: "Atari", + name2: "FALCON", + dma_alloc: AtaAlloc, + dma_free: AtaFree, + irqinit: AtaIrqInit, +#ifdef MODULE + irqcleanup: AtaIrqCleanUp, +#endif /* MODULE */ + init: FalconInit, + silence: FalconSilence, + setFormat: FalconSetFormat, + setVolume: FalconSetVolume, + setBass: AtaSetBass, + setTreble: AtaSetTreble, + play: AtaPlay, + mixer_init: FalconMixerInit, + mixer_ioctl: FalconMixerIoctl, + write_sq_setup: AtaWriteSqSetup, + sq_open: AtaSqOpen, + state_info: FalconStateInfo, + min_dsp_speed: 8195, +}; + + +/*** Config & Setup **********************************************************/ + + +static int __init dmasound_atari_init(void) +{ + if (MACH_IS_ATARI && ATARIHW_PRESENT(PCM_8BIT)) { + if (ATARIHW_PRESENT(CODEC)) { + dmasound.mach = machFalcon; + is_falcon = 1; + } else if (ATARIHW_PRESENT(MICROWIRE)) { + dmasound.mach = machTT; + is_falcon = 0; + } else + return -ENODEV; + if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0) + return dmasound_init(); + else { + printk("DMA sound driver: Timer A interrupt already in use\n"); + return -EBUSY; + } + } + return -ENODEV; +} + +static void __exit dmasound_atari_cleanup(void) +{ + dmasound_deinit(); +} + +module_init(dmasound_atari_init); +module_exit(dmasound_atari_cleanup); +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/dmasound/dmasound_awacs.c b/sound/oss/dmasound/dmasound_awacs.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmasound/dmasound_awacs.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,2183 @@ + +/* + * linux/drivers/sound/dmasound/dmasound_awacs.c + * + * PowerMac `AWACS' and `Burgundy' DMA Sound Driver + * + * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ADB_CUDA +#include +#endif +#ifdef CONFIG_ADB_PMU +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "awacs_defs.h" +#include "dmasound.h" + + +/* + * Interrupt numbers and addresses, obtained from the device tree. + */ +static int awacs_irq, awacs_tx_irq, awacs_rx_irq; +static volatile struct awacs_regs *awacs; +static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma; +static int awacs_rate_index; +static int awacs_subframe; +static int awacs_spkr_vol; +static struct device_node* awacs_node; + +static char awacs_name[64]; +static int awacs_revision; +int awacs_is_screamer = 0; +int awacs_device_id = 0; +int awacs_has_iic = 0; +#define AWACS_BURGUNDY 100 /* fake revision # for burgundy */ + +/* + * Space for the DBDMA command blocks. + */ +static void *awacs_tx_cmd_space; +static volatile struct dbdma_cmd *awacs_tx_cmds; + +static void *awacs_rx_cmd_space; +static volatile struct dbdma_cmd *awacs_rx_cmds; + +/* + * Cached values of AWACS registers (we can't read them). + * Except on the burgundy. XXX + */ +int awacs_reg[8]; + +#define HAS_16BIT_TABLES +#undef HAS_8BIT_TABLES + +/* + * Stuff for outputting a beep. The values range from -327 to +327 + * so we can multiply by an amplitude in the range 0..100 to get a + * signed short value to put in the output buffer. + */ +static short beep_wform[256] = { + 0, 40, 79, 117, 153, 187, 218, 245, + 269, 288, 304, 316, 323, 327, 327, 324, + 318, 310, 299, 288, 275, 262, 249, 236, + 224, 213, 204, 196, 190, 186, 183, 182, + 182, 183, 186, 189, 192, 196, 200, 203, + 206, 208, 209, 209, 209, 207, 204, 201, + 197, 193, 188, 183, 179, 174, 170, 166, + 163, 161, 160, 159, 159, 160, 161, 162, + 164, 166, 168, 169, 171, 171, 171, 170, + 169, 167, 163, 159, 155, 150, 144, 139, + 133, 128, 122, 117, 113, 110, 107, 105, + 103, 103, 103, 103, 104, 104, 105, 105, + 105, 103, 101, 97, 92, 86, 78, 68, + 58, 45, 32, 18, 3, -11, -26, -41, + -55, -68, -79, -88, -95, -100, -102, -102, + -99, -93, -85, -75, -62, -48, -33, -16, + 0, 16, 33, 48, 62, 75, 85, 93, + 99, 102, 102, 100, 95, 88, 79, 68, + 55, 41, 26, 11, -3, -18, -32, -45, + -58, -68, -78, -86, -92, -97, -101, -103, + -105, -105, -105, -104, -104, -103, -103, -103, + -103, -105, -107, -110, -113, -117, -122, -128, + -133, -139, -144, -150, -155, -159, -163, -167, + -169, -170, -171, -171, -171, -169, -168, -166, + -164, -162, -161, -160, -159, -159, -160, -161, + -163, -166, -170, -174, -179, -183, -188, -193, + -197, -201, -204, -207, -209, -209, -209, -208, + -206, -203, -200, -196, -192, -189, -186, -183, + -182, -182, -183, -186, -190, -196, -204, -213, + -224, -236, -249, -262, -275, -288, -299, -310, + -318, -324, -327, -327, -323, -316, -304, -288, + -269, -245, -218, -187, -153, -117, -79, -40, +}; + +#define BEEP_SRATE 22050 /* 22050 Hz sample rate */ +#define BEEP_BUFLEN 512 +#define BEEP_VOLUME 15 /* 0 - 100 */ + +static int beep_volume = BEEP_VOLUME; +static int beep_playing = 0; +static int awacs_beep_state = 0; +static short *beep_buf; +static volatile struct dbdma_cmd *beep_dbdma_cmd; +static void (*orig_mksound)(unsigned int, unsigned int); +static int is_pbook_3400; +static unsigned char *latch_base; +static int is_pbook_G3; +static unsigned char *macio_base; + +/* Burgundy functions */ +static void awacs_burgundy_wcw(unsigned addr,unsigned newval); +static unsigned awacs_burgundy_rcw(unsigned addr); +static void awacs_burgundy_write_volume(unsigned address, int volume); +static int awacs_burgundy_read_volume(unsigned address); +static void awacs_burgundy_write_mvolume(unsigned address, int volume); +static int awacs_burgundy_read_mvolume(unsigned address); + +#ifdef CONFIG_PMAC_PBOOK +/* + * Stuff for restoring after a sleep. + */ +static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when); +struct pmu_sleep_notifier awacs_sleep_notifier = { + awacs_sleep_notify, SLEEP_LEVEL_SOUND, +}; +#endif /* CONFIG_PMAC_PBOOK */ + +static int expand_bal; /* Balance factor for expanding (not volume!) */ +static int expand_data; /* Data for expanding */ + + +/*** Translations ************************************************************/ + + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + +static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); + + +/*** Low level stuff *********************************************************/ + + +static void PMacOpen(void); +static void PMacRelease(void); +static void *PMacAlloc(unsigned int size, int flags); +static void PMacFree(void *ptr, unsigned int size); +static int PMacIrqInit(void); +#ifdef MODULE +static void PMacIrqCleanup(void); +#endif +static void PMacSilence(void); +static void PMacInit(void); +static int PMacSetFormat(int format); +static int PMacSetVolume(int volume); +static void PMacPlay(void); +static void PMacRecord(void); +static void pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs); +static void pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs); +static void pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs); +static void awacs_write(int val); +static int awacs_get_volume(int reg, int lshift); +static int awacs_volume_setter(int volume, int n, int mute, int lshift); +static void awacs_mksound(unsigned int hz, unsigned int ticks); +static void awacs_nosound(unsigned long xx); + + +/*** Mid level stuff **********************************************************/ + + +static int PMacMixerIoctl(u_int cmd, u_long arg); +static void PMacWriteSqSetup(void); +static void PMacReadSqSetup(void); +static void PMacAbortRead(void); + + +/*** Translations ************************************************************/ + + +static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + short *table = dmasound.soft.format == AFMT_MU_LAW + ? dmasound_ulaw2dma16 : dmasound_alaw2dma16; + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min_t(unsigned long, userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + if (get_user(data, up++)) + return -EFAULT; + *fp++ = data; + *fp++ = data; + count--; + } + } else { + if (copy_from_user(fp, userPtr, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + int data; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + *fp++ = data; + if (stereo) { + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + } + *fp++ = data; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + + +static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned short *table = (unsigned short *) + (dmasound.soft.format == AFMT_MU_LAW + ? dmasound_ulaw2dma16 : dmasound_alaw2dma16); + unsigned int data = expand_data; + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + int stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + table[c]; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + (c << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = (c ^ 0x80) << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + ((c ^ 0x80) << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + unsigned short *up = (unsigned short *) userPtr; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + c; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + + +static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + unsigned short *up = (unsigned short *) userPtr; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + (c ^ mask); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + +static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + u_char data; + + val = *p++; + data = val >> 8; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + if (stereo) { + val = *p; + data = val >> 8; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + } + p++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + u_char data; + + val = *p++; + data = (val >> 8) ^ 0x80; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + if (stereo) { + val = *p; + data = (val >> 8) ^ 0x80; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + } + p++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min_t(unsigned long, userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + data = *fp; + if (put_user(data, up++)) + return -EFAULT; + fp+=2; + count--; + } + } else { + if (copy_to_user((u_char *)userPtr, fp, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + int data; + + data = *fp++; + data ^= mask; + if (put_user(data, up++)) + return -EFAULT; + if (stereo) { + data = *fp; + data ^= mask; + if (put_user(data, up++)) + return -EFAULT; + } + fp++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + + +static TRANS transAwacsNormal = { + ct_ulaw: pmac_ct_law, + ct_alaw: pmac_ct_law, + ct_s8: pmac_ct_s8, + ct_u8: pmac_ct_u8, + ct_s16be: pmac_ct_s16, + ct_u16be: pmac_ct_u16, + ct_s16le: pmac_ct_s16, + ct_u16le: pmac_ct_u16, +}; + +static TRANS transAwacsExpand = { + ct_ulaw: pmac_ctx_law, + ct_alaw: pmac_ctx_law, + ct_s8: pmac_ctx_s8, + ct_u8: pmac_ctx_u8, + ct_s16be: pmac_ctx_s16, + ct_u16be: pmac_ctx_u16, + ct_s16le: pmac_ctx_s16, + ct_u16le: pmac_ctx_u16, +}; + +static TRANS transAwacsNormalRead = { + ct_s8: pmac_ct_s8_read, + ct_u8: pmac_ct_u8_read, + ct_s16be: pmac_ct_s16_read, + ct_u16be: pmac_ct_u16_read, + ct_s16le: pmac_ct_s16_read, + ct_u16le: pmac_ct_u16_read, +}; + +/*** Low level stuff *********************************************************/ + + + +/* + * PCI PowerMac, with AWACS and DBDMA. + */ + +static void PMacOpen(void) +{ + MOD_INC_USE_COUNT; +} + +static void PMacRelease(void) +{ + MOD_DEC_USE_COUNT; +} + +static void *PMacAlloc(unsigned int size, int flags) +{ + return kmalloc(size, flags); +} + +static void PMacFree(void *ptr, unsigned int size) +{ + kfree(ptr); +} + +static int __init PMacIrqInit(void) +{ + if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0) + || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0) + || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0)) + return 0; + return 1; +} + +#ifdef MODULE +static void PMacIrqCleanup(void) +{ + /* turn off output dma */ + out_le32(&awacs_txdma->control, RUN<<16); + /* disable interrupts from awacs interface */ + out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff); +#ifdef CONFIG_PMAC_PBOOK + if (is_pbook_G3) { + feature_clear(awacs_node, FEATURE_Sound_power); + feature_clear(awacs_node, FEATURE_Sound_CLK_enable); + } +#endif + free_irq(awacs_irq, 0); + free_irq(awacs_tx_irq, 0); + free_irq(awacs_rx_irq, 0); + kfree(awacs_tx_cmd_space); + if (awacs_rx_cmd_space) + kfree(awacs_rx_cmd_space); + if (beep_buf) + kfree(beep_buf); + kd_mksound = orig_mksound; +#ifdef CONFIG_PMAC_PBOOK + pmu_unregister_sleep_notifier(&awacs_sleep_notifier); +#endif +} +#endif /* MODULE */ + +static void PMacSilence(void) +{ + /* turn off output dma */ + out_le32(&awacs_txdma->control, RUN<<16); +} + +static int awacs_freqs[8] = { + 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 +}; +static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + +static void PMacInit(void) +{ + int i, tolerance; + + switch (dmasound.soft.format) { + case AFMT_S16_LE: + case AFMT_U16_LE: + dmasound.hard.format = AFMT_S16_LE; + break; + default: + dmasound.hard.format = AFMT_S16_BE; + break; + } + dmasound.hard.stereo = 1; + dmasound.hard.size = 16; + + /* + * If we have a sample rate which is within catchRadius percent + * of the requested value, we don't have to expand the samples. + * Otherwise choose the next higher rate. + * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz. + */ + i = 8; + do { + tolerance = catchRadius * awacs_freqs[--i] / 100; + if (awacs_freqs_ok[i] + && dmasound.soft.speed <= awacs_freqs[i] + tolerance) + break; + } while (i > 0); + if (dmasound.soft.speed >= awacs_freqs[i] - tolerance) + dmasound.trans_write = &transAwacsNormal; + else + dmasound.trans_write = &transAwacsExpand; + dmasound.trans_read = &transAwacsNormalRead; + dmasound.hard.speed = awacs_freqs[i]; + awacs_rate_index = i; + + /* XXX disable error interrupt on burgundy for now */ + out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11 + | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); + awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3); + awacs_write(awacs_reg[1] | MASK_ADDR1); + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + + /* We really want to execute a DMA stop command, after the AWACS + * is initialized. + * For reasons I don't understand, it stops the hissing noise + * common to many PowerBook G3 systems (like mine :-). + */ + out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); + out_le32(&awacs_txdma->control, RUN | (RUN << 16)); + + expand_bal = -dmasound.soft.speed; +} + +static int PMacSetFormat(int format) +{ + int size; + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", + format); + size = 8; + format = AFMT_U8; + } + + dmasound.soft.format = format; + dmasound.soft.size = size; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = size; + } + + PMacInit(); + + return format; +} + +#define AWACS_VOLUME_TO_MASK(x) (15 - ((((x) - 1) * 15) / 99)) +#define AWACS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 15)) + +static int awacs_get_volume(int reg, int lshift) +{ + int volume; + + volume = AWACS_MASK_TO_VOLUME((reg >> lshift) & 0xf); + volume |= AWACS_MASK_TO_VOLUME(reg & 0xf) << 8; + return volume; +} + +static int awacs_volume_setter(int volume, int n, int mute, int lshift) +{ + int r1, rn; + + if (mute && volume == 0) { + r1 = awacs_reg[1] | mute; + } else { + r1 = awacs_reg[1] & ~mute; + rn = awacs_reg[n] & ~(0xf | (0xf << lshift)); + rn |= ((AWACS_VOLUME_TO_MASK(volume & 0xff) & 0xf) << lshift); + rn |= AWACS_VOLUME_TO_MASK((volume >> 8) & 0xff) & 0xf; + awacs_reg[n] = rn; + awacs_write((n << 12) | rn); + volume = awacs_get_volume(rn, lshift); + } + if (r1 != awacs_reg[1]) { + awacs_reg[1] = r1; + awacs_write(r1 | MASK_ADDR1); + } + return volume; +} + +static int PMacSetVolume(int volume) +{ + return awacs_volume_setter(volume, 2, MASK_AMUTE, 6); +} + +static void PMacPlay(void) +{ + volatile struct dbdma_cmd *cp; + int i, count; + unsigned long flags; + + save_flags(flags); cli(); + if (awacs_beep_state) { + /* sound takes precedence over beeps */ + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (awacs_rate_index << 8)); + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(write_sq.front+write_sq.active) % write_sq.max_count]))); + + beep_playing = 0; + awacs_beep_state = 0; + } + i = write_sq.front + write_sq.active; + if (i >= write_sq.max_count) + i -= write_sq.max_count; + while (write_sq.active < 2 && write_sq.active < write_sq.count) { + count = (write_sq.count == write_sq.active + 1)?write_sq.rear_size:write_sq.block_size; + if (count < write_sq.block_size && !write_sq.syncing) + /* last block not yet filled, and we're not syncing. */ + break; + cp = &awacs_tx_cmds[i]; + st_le16(&cp->req_count, count); + st_le16(&cp->xfer_status, 0); + if (++i >= write_sq.max_count) + i = 0; + out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP); + out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS); + if (write_sq.active == 0) + out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp)); + out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); + ++write_sq.active; + } + restore_flags(flags); +} + + +static void PMacRecord(void) +{ + unsigned long flags; + + if (read_sq.active) + return; + + save_flags(flags); cli(); + + /* This is all we have to do......Just start it up. + */ + out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); + read_sq.active = 1; + + restore_flags(flags); +} + + +static void +pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs) +{ + int i = write_sq.front; + int stat; + volatile struct dbdma_cmd *cp; + + while (write_sq.active > 0) { + cp = &awacs_tx_cmds[i]; + stat = ld_le16(&cp->xfer_status); + if ((stat & ACTIVE) == 0) + break; /* this frame is still going */ + --write_sq.count; + --write_sq.active; + if (++i >= write_sq.max_count) + i = 0; + } + if (i != write_sq.front) + WAKE_UP(write_sq.action_queue); + write_sq.front = i; + + PMacPlay(); + + if (!write_sq.active) + WAKE_UP(write_sq.sync_queue); +} + + +static void +pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs) +{ + + /* For some reason on my PowerBook G3, I get one interrupt + * when the interrupt vector is installed (like something is + * pending). This happens before the dbdma is initialize by + * us, so I just check the command pointer and if it is zero, + * just blow it off. + */ + if (in_le32(&awacs_rxdma->cmdptr) == 0) + return; + + /* We also want to blow 'em off when shutting down. + */ + if (read_sq.active == 0) + return; + + /* Check multiple buffers in case we were held off from + * interrupt processing for a long time. Geeze, I really hope + * this doesn't happen. + */ + while (awacs_rx_cmds[read_sq.rear].xfer_status) { + + /* Clear status and move on to next buffer. + */ + awacs_rx_cmds[read_sq.rear].xfer_status = 0; + read_sq.rear++; + + /* Wrap the buffer ring. + */ + if (read_sq.rear >= read_sq.max_active) + read_sq.rear = 0; + + /* If we have caught up to the front buffer, bump it. + * This will cause weird (but not fatal) results if the + * read loop is currently using this buffer. The user is + * behind in this case anyway, so weird things are going + * to happen. + */ + if (read_sq.rear == read_sq.front) { + read_sq.front++; + if (read_sq.front >= read_sq.max_active) + read_sq.front = 0; + } + } + + WAKE_UP(read_sq.action_queue); +} + + +static void +pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs) +{ + int ctrl = in_le32(&awacs->control); + + if (ctrl & MASK_PORTCHG) { + /* do something when headphone is plugged/unplugged? */ + } + if (ctrl & MASK_CNTLERR) { + int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16; + if (err != 0 && awacs_revision < AWACS_BURGUNDY) + printk(KERN_ERR "AWACS: error %x\n", err); + } + /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ + out_le32(&awacs->control, ctrl); +} + +static void +awacs_write(int val) +{ + if (awacs_revision >= AWACS_BURGUNDY) + return; + while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) + ; /* XXX should have timeout */ + out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22)); +} + +static void awacs_nosound(unsigned long xx) +{ + unsigned long flags; + + save_flags(flags); cli(); + if (beep_playing) { + st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (awacs_rate_index << 8)); + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + beep_playing = 0; + } + restore_flags(flags); +} + +static struct timer_list beep_timer = { + function: awacs_nosound +}; + +static void awacs_mksound(unsigned int hz, unsigned int ticks) +{ + unsigned long flags; + int beep_speed = 0; + int srate; + int period, ncycles, nsamples; + int i, j, f; + short *p; + static int beep_hz_cache; + static int beep_nsamples_cache; + static int beep_volume_cache; + + for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i) + if (awacs_freqs_ok[i]) + beep_speed = i; + srate = awacs_freqs[beep_speed]; + + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { +#if 1 + /* this is a hack for broken X server code */ + hz = 750; + ticks = 12; +#else + /* cancel beep currently playing */ + awacs_nosound(0); + return; +#endif + } + save_flags(flags); cli(); + del_timer(&beep_timer); + if (ticks) { + beep_timer.expires = jiffies + ticks; + add_timer(&beep_timer); + } + if (beep_playing || write_sq.active || beep_buf == NULL) { + restore_flags(flags); + return; /* too hard, sorry :-( */ + } + beep_playing = 1; + st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); + restore_flags(flags); + + if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { + nsamples = beep_nsamples_cache; + } else { + period = srate * 256 / hz; /* fixed point */ + ncycles = BEEP_BUFLEN * 256 / period; + nsamples = (period * ncycles) >> 8; + f = ncycles * 65536 / nsamples; + j = 0; + p = beep_buf; + for (i = 0; i < nsamples; ++i, p += 2) { + p[0] = p[1] = beep_wform[j >> 8] * beep_volume; + j = (j + f) & 0xffff; + } + beep_hz_cache = hz; + beep_volume_cache = beep_volume; + beep_nsamples_cache = nsamples; + } + + st_le16(&beep_dbdma_cmd->req_count, nsamples*4); + st_le16(&beep_dbdma_cmd->xfer_status, 0); + st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); + st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); + awacs_beep_state = 1; + + save_flags(flags); cli(); + if (beep_playing) { /* i.e. haven't been terminated already */ + out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (beep_speed << 8)); + out_le32(&awacs->byteswap, 0); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); + out_le32(&awacs_txdma->control, RUN | (RUN << 16)); + } + restore_flags(flags); +} + +#ifdef CONFIG_PMAC_PBOOK +/* + * Save state when going to sleep, restore it afterwards. + */ +static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when) +{ + switch (when) { + case PBOOK_SLEEP_NOW: + /* XXX we should stop any dma in progress when going to sleep + and restart it when we wake. */ + PMacSilence(); + disable_irq(awacs_irq); + disable_irq(awacs_tx_irq); + if (is_pbook_G3) { + feature_clear(awacs_node, FEATURE_Sound_CLK_enable); + feature_clear(awacs_node, FEATURE_Sound_power); + } + break; + case PBOOK_WAKE: + /* There is still a problem on wake. Sound seems to work fine + if I launch mpg123 and resumes fine if mpg123 was playing, + but the console beep is dead until I do something with the + mixer. Probably yet another timing issue */ + if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable) + || !feature_test(awacs_node, FEATURE_Sound_power)) { + /* these aren't present on the 3400 AFAIK -- paulus */ + feature_set(awacs_node, FEATURE_Sound_CLK_enable); + feature_set(awacs_node, FEATURE_Sound_power); + mdelay(1000); + } + out_le32(&awacs->control, MASK_IEPC + | (awacs_rate_index << 8) | 0x11 + | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); + awacs_write(awacs_reg[0] | MASK_ADDR0); + awacs_write(awacs_reg[1] | MASK_ADDR1); + awacs_write(awacs_reg[2] | MASK_ADDR2); + awacs_write(awacs_reg[4] | MASK_ADDR4); + if (awacs_is_screamer) { + awacs_write(awacs_reg[5] + MASK_ADDR5); + awacs_write(awacs_reg[6] + MASK_ADDR6); + awacs_write(awacs_reg[7] + MASK_ADDR7); + } + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + enable_irq(awacs_irq); + enable_irq(awacs_tx_irq); + if (awacs_revision == 3) { + mdelay(100); + awacs_write(0x6000); + mdelay(2); + awacs_write(awacs_reg[1] | MASK_ADDR1); + } + /* enable CD sound input */ + if (macio_base && is_pbook_G3) { + out_8(macio_base + 0x37, 3); + } else if (is_pbook_3400) { + feature_set(awacs_node, FEATURE_IOBUS_enable); + udelay(10); + in_8(latch_base + 0x190); + } + /* Resume pending sounds. */ + PMacPlay(); + } + return PBOOK_SLEEP_OK; +} +#endif /* CONFIG_PMAC_PBOOK */ + + +/* All the burgundy functions: */ + +/* Waits for busy flag to clear */ +inline static void +awacs_burgundy_busy_wait(void) +{ + while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) + ; +} + +inline static void +awacs_burgundy_extend_wait(void) +{ + while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) + ; + while (in_le32(&awacs->codec_stat) & MASK_EXTEND) + ; +} + +static void +awacs_burgundy_wcw(unsigned addr, unsigned val) +{ + out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); + awacs_burgundy_busy_wait(); +} + +static unsigned +awacs_burgundy_rcw(unsigned addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + save_flags(flags); cli(); + + out_le32(&awacs->codec_ctrl, addr + 0x100000); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; + + out_le32(&awacs->codec_ctrl, addr + 0x100100); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8; + + out_le32(&awacs->codec_ctrl, addr + 0x100200); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16; + + out_le32(&awacs->codec_ctrl, addr + 0x100300); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24; + + restore_flags(flags); + + return val; +} + + +static void +awacs_burgundy_wcb(unsigned addr, unsigned val) +{ + out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); + awacs_burgundy_busy_wait(); +} + +static unsigned +awacs_burgundy_rcb(unsigned addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + save_flags(flags); cli(); + + out_le32(&awacs->codec_ctrl, addr + 0x100000); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; + + restore_flags(flags); + + return val; +} + +static int +awacs_burgundy_check(void) +{ + /* Checks to see the chip is alive and kicking */ + int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE; + + return error == 0xf0000; +} + +static int +awacs_burgundy_init(void) +{ + if (awacs_burgundy_check()) { + printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n"); + return 1; + } + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES, + DEF_BURGUNDY_OUTPUTENABLES); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + DEF_BURGUNDY_MORE_OUTPUTENABLES); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS, + DEF_BURGUNDY_OUTPUTSELECTS); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21, + DEF_BURGUNDY_INPSEL21); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3, + DEF_BURGUNDY_INPSEL3); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD, + DEF_BURGUNDY_GAINCD); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE, + DEF_BURGUNDY_GAINLINE); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC, + DEF_BURGUNDY_GAINMIC); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM, + DEF_BURGUNDY_GAINMODEM); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, + DEF_BURGUNDY_ATTENSPEAKER); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT, + DEF_BURGUNDY_ATTENLINEOUT); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP, + DEF_BURGUNDY_ATTENHP); + + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME, + DEF_BURGUNDY_MASTER_VOLUME); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD, + DEF_BURGUNDY_VOLCD); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE, + DEF_BURGUNDY_VOLLINE); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC, + DEF_BURGUNDY_VOLMIC); + return 0; +} + +static void +awacs_burgundy_write_volume(unsigned address, int volume) +{ + int hardvolume,lvolume,rvolume; + + lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0; + rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0; + + hardvolume = lvolume + (rvolume << 16); + + awacs_burgundy_wcw(address, hardvolume); +} + +static int +awacs_burgundy_read_volume(unsigned address) +{ + int softvolume,wvolume; + + wvolume = awacs_burgundy_rcw(address); + + softvolume = (wvolume & 0xff) - 155; + softvolume += (((wvolume >> 16) & 0xff) - 155)<<8; + + return softvolume > 0 ? softvolume : 0; +} + + + + +static int +awacs_burgundy_read_mvolume(unsigned address) +{ + int lvolume,rvolume,wvolume; + + wvolume = awacs_burgundy_rcw(address); + + wvolume &= 0xffff; + + rvolume = (wvolume & 0xff) - 155; + lvolume = ((wvolume & 0xff00)>>8) - 155; + + return lvolume + (rvolume << 8); +} + + +static void +awacs_burgundy_write_mvolume(unsigned address, int volume) +{ + int lvolume,rvolume,hardvolume; + + lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0; + rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0; + + hardvolume = lvolume + (rvolume << 8); + hardvolume += (hardvolume << 16); + + awacs_burgundy_wcw(address, hardvolume); +} + +/* End burgundy functions */ + + + + + +/* Turn on sound output, needed on G3 desktop powermacs */ +static void +awacs_enable_amp(int spkr_vol) +{ + struct adb_request req; + + awacs_spkr_vol = spkr_vol; + if (sys_ctrler != SYS_CTRLER_CUDA) + return; + +#ifdef CONFIG_ADB_CUDA + /* turn on headphones */ + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 4, 0); + while (!req.complete) cuda_poll(); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 6, 0); + while (!req.complete) cuda_poll(); + + /* turn on speaker */ + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 3, (100 - (spkr_vol & 0xff)) * 32 / 100); + while (!req.complete) cuda_poll(); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 5, (100 - ((spkr_vol >> 8) & 0xff)) * 32 / 100); + while (!req.complete) cuda_poll(); + + cuda_request(&req, NULL, 5, CUDA_PACKET, + CUDA_GET_SET_IIC, 0x8a, 1, 0x29); + while (!req.complete) cuda_poll(); +#endif /* CONFIG_ADB_CUDA */ +} + + +/*** Mid level stuff *********************************************************/ + + +/* + * /dev/mixer abstraction + */ + +static int awacs_mixer_ioctl(u_int cmd, u_long arg) +{ + int data; + + switch (cmd) { + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD | SOUND_MASK_RECLEV + | SOUND_MASK_ALTPCM + | SOUND_MASK_MONITOR; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + data = 0; + if (awacs_reg[0] & MASK_MUX_AUDIN) + data |= SOUND_MASK_LINE; + if (awacs_reg[0] & MASK_MUX_MIC) + data |= SOUND_MASK_MIC; + if (awacs_reg[0] & MASK_MUX_CD) + data |= SOUND_MASK_CD; + if (awacs_reg[1] & MASK_LOOPTHRU) + data |= SOUND_MASK_MONITOR; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_CD + | SOUND_MASK_MONITOR); + awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC + | MASK_MUX_AUDIN); + awacs_reg[1] &= ~MASK_LOOPTHRU; + if (data & SOUND_MASK_LINE) + awacs_reg[0] |= MASK_MUX_AUDIN; + if (data & SOUND_MASK_MIC) + awacs_reg[0] |= MASK_MUX_MIC; + if (data & SOUND_MASK_CD) + awacs_reg[0] |= MASK_MUX_CD; + if (data & SOUND_MASK_MONITOR) + awacs_reg[1] |= MASK_LOOPTHRU; + awacs_write(awacs_reg[0] | MASK_ADDR0); + awacs_write(awacs_reg[1] | MASK_ADDR1); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_RECLEV; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_VOLUME: + data = (awacs_reg[1] & MASK_AMUTE)? 0: + awacs_get_volume(awacs_reg[2], 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, PMacSetVolume(data)); + case SOUND_MIXER_READ_SPEAKER: + if (awacs_revision == 3 + && sys_ctrler == SYS_CTRLER_CUDA) + data = awacs_spkr_vol; + else + data = (awacs_reg[1] & MASK_CMUTE)? 0: + awacs_get_volume(awacs_reg[4], 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_SPEAKER: + IOCTL_IN(arg, data); + if (awacs_revision == 3 + && sys_ctrler == SYS_CTRLER_CUDA) + awacs_enable_amp(data); + else + data = awacs_volume_setter(data, 4, MASK_CMUTE, 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_LINE: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_AUDIN; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_AUDIN; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + data &= 0xff; + awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); + if (data >= 25) { + awacs_reg[0] |= MASK_MUX_MIC; + if (data >= 75) + awacs_reg[0] |= MASK_GAINLINE; + } + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = (awacs_reg[0] & MASK_MUX_MIC)? + (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_CD; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); + case MIXER_WRITE(SOUND_MIXER_MONITOR): + IOCTL_IN(arg, data); + awacs_reg[1] &= ~MASK_LOOPTHRU; + if ((data & 0xff) >= 50) + awacs_reg[1] |= MASK_LOOPTHRU; + awacs_write(MASK_ADDR1 | awacs_reg[1]); + /* fall through */ + case MIXER_READ(SOUND_MIXER_MONITOR): + data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0; + return IOCTL_OUT(arg, data); + } + return -EINVAL; +} + +static int burgundy_mixer_ioctl(u_int cmd, u_long arg) +{ + int data; + + /* We are, we are, we are... Burgundy or better */ + switch(cmd) { + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + data = 0; + if (awacs_reg[0] & MASK_MUX_AUDIN) + data |= SOUND_MASK_LINE; + if (awacs_reg[0] & MASK_MUX_MIC) + data |= SOUND_MASK_MIC; + if (awacs_reg[0] & MASK_MUX_CD) + data |= SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_CD); + awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC + | MASK_MUX_AUDIN); + if (data & SOUND_MASK_LINE) + awacs_reg[0] |= MASK_MUX_AUDIN; + if (data & SOUND_MASK_MIC) + awacs_reg[0] |= MASK_MUX_MIC; + if (data & SOUND_MASK_CD) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(awacs_reg[0] | MASK_ADDR0); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_RECLEV | SOUND_MASK_CD + | SOUND_MASK_LINE; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data); + /* Fall through */ + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME)); + case SOUND_MIXER_WRITE_SPEAKER: + IOCTL_IN(arg, data); + + if (!(data & 0xff)) { + /* Mute the left speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2); + } else { + /* Unmute the left speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2); + } + if (!(data & 0xff00)) { + /* Mute the right speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4); + } else { + /* Unmute the right speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4); + } + + data = (((data&0xff)*16)/100 > 0xf ? 0xf : + (((data&0xff)*16)/100)) + + ((((data>>8)*16)/100 > 0xf ? 0xf : + ((((data>>8)*16)/100)))<<4); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data); + /* Fall through */ + case SOUND_MIXER_READ_SPEAKER: + data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER); + data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8); + return IOCTL_OUT(arg, ~data); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_LINE: + IOCTL_IN(arg, data); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data); + + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + /* Mic is mono device */ + data = (data << 8) + (data << 24); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC); + data <<= 24; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_OUTMASK: + break; + case SOUND_MIXER_OUTSRC: + break; + } + return -EINVAL; +} + +static int PMacMixerIoctl(u_int cmd, u_long arg) +{ + /* Different IOCTLS for burgundy*/ + if (awacs_revision >= AWACS_BURGUNDY) + return burgundy_mixer_ioctl(cmd, arg); + return awacs_mixer_ioctl(cmd, arg); +} + + +static void PMacWriteSqSetup(void) +{ + int i; + volatile struct dbdma_cmd *cp; + + cp = awacs_tx_cmds; + memset((void *)cp, 0, (write_sq.numBufs+1) * sizeof(struct dbdma_cmd)); + for (i = 0; i < write_sq.numBufs; ++i, ++cp) { + st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i])); + } + st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); + st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds)); + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds)); +} + +static void PMacReadSqSetup(void) +{ + int i; + volatile struct dbdma_cmd *cp; + + cp = awacs_rx_cmds; + memset((void *)cp, 0, (read_sq.numBufs+1) * sizeof(struct dbdma_cmd)); + + /* Set dma buffers up in a loop */ + for (i = 0; i < read_sq.numBufs; i++,cp++) { + st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i])); + st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS); + st_le16(&cp->req_count, read_sq.block_size); + st_le16(&cp->xfer_status, 0); + } + + /* The next two lines make the thing loop around. + */ + st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); + st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds)); + + /* Don't start until the first read is done. + * This will also abort any operations in progress if the DMA + * happens to be running (and it shouldn't). + */ + out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds)); + +} + +static void PMacAbortRead(void) +{ + int i; + volatile struct dbdma_cmd *cp; + + cp = awacs_rx_cmds; + for (i = 0; i < read_sq.numBufs; i++,cp++) + st_le16(&cp->command, DBDMA_STOP); + /* + * We should probably wait for the thing to stop before we + * release the memory + */ +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machPMac = { + name: awacs_name, + name2: "AWACS", + open: PMacOpen, + release: PMacRelease, + dma_alloc: PMacAlloc, + dma_free: PMacFree, + irqinit: PMacIrqInit, +#ifdef MODULE + irqcleanup: PMacIrqCleanup, +#endif /* MODULE */ + init: PMacInit, + silence: PMacSilence, + setFormat: PMacSetFormat, + setVolume: PMacSetVolume, + play: PMacPlay, + record: PMacRecord, + mixer_ioctl: PMacMixerIoctl, + write_sq_setup: PMacWriteSqSetup, + read_sq_setup: PMacReadSqSetup, + abort_read: PMacAbortRead, + min_dsp_speed: 8000 +}; + + +/*** Config & Setup **********************************************************/ + + +int __init dmasound_awacs_init(void) +{ + struct device_node *np; + + if (_machine != _MACH_Pmac) + return -ENODEV; + + awacs_subframe = 0; + awacs_revision = 0; + np = find_devices("awacs"); + if (np == 0) { + /* + * powermac G3 models have a node called "davbus" + * with a child called "sound". + */ + struct device_node *sound; + np = find_devices("davbus"); + sound = find_devices("sound"); + if (sound != 0 && sound->parent == np) { + unsigned int *prop, l, i; + prop = (unsigned int *) + get_property(sound, "sub-frame", 0); + if (prop != 0 && *prop >= 0 && *prop < 16) + awacs_subframe = *prop; + if (device_is_compatible(sound, "burgundy")) + awacs_revision = AWACS_BURGUNDY; + /* This should be verified on older screamers */ + if (device_is_compatible(sound, "screamer")) + awacs_is_screamer = 1; + prop = (unsigned int *)get_property(sound, "device-id", 0); + if (prop != 0) + awacs_device_id = *prop; + awacs_has_iic = (find_devices("perch") != NULL); + + /* look for a property saying what sample rates + are available */ + for (i = 0; i < 8; ++i) + awacs_freqs_ok[i] = 0; + prop = (unsigned int *) get_property + (sound, "sample-rates", &l); + if (prop == 0) + prop = (unsigned int *) get_property + (sound, "output-frame-rates", &l); + if (prop != 0) { + for (l /= sizeof(int); l > 0; --l) { + /* sometimes the rate is in the + high-order 16 bits (?) */ + unsigned int r = *prop++; + if (r >= 0x10000) + r >>= 16; + for (i = 0; i < 8; ++i) { + if (r == awacs_freqs[i]) { + awacs_freqs_ok[i] = 1; + break; + } + } + } + } else { + /* assume just 44.1k is OK */ + awacs_freqs_ok[0] = 1; + } + } + } + if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) { + int vol; + dmasound.mach = machPMac; + + awacs = (volatile struct awacs_regs *) + ioremap(np->addrs[0].address, 0x80); + awacs_txdma = (volatile struct dbdma_regs *) + ioremap(np->addrs[1].address, 0x100); + awacs_rxdma = (volatile struct dbdma_regs *) + ioremap(np->addrs[2].address, 0x100); + + awacs_irq = np->intrs[0].line; + awacs_tx_irq = np->intrs[1].line; + awacs_rx_irq = np->intrs[2].line; + + awacs_tx_cmd_space = kmalloc((write_sq.numBufs + 4) * sizeof(struct dbdma_cmd), + GFP_KERNEL); + if (awacs_tx_cmd_space == NULL) { + printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n"); + return -ENOMEM; + } + awacs_node = np; +#ifdef CONFIG_PMAC_PBOOK + if (machine_is_compatible("PowerBook1,1") + || machine_is_compatible("AAPL,PowerBook1998")) { + pmu_suspend(); + feature_set(np, FEATURE_Sound_CLK_enable); + feature_set(np, FEATURE_Sound_power); + /* Shorter delay will not work */ + mdelay(1000); + pmu_resume(); + } +#endif + awacs_tx_cmds = (volatile struct dbdma_cmd *) + DBDMA_ALIGN(awacs_tx_cmd_space); + + + awacs_rx_cmd_space = kmalloc((read_sq.numBufs + 4) * sizeof(struct dbdma_cmd), + GFP_KERNEL); + if (awacs_rx_cmd_space == NULL) { + printk("DMA sound driver: No memory for input"); + } + awacs_rx_cmds = (volatile struct dbdma_cmd *) + DBDMA_ALIGN(awacs_rx_cmd_space); + + + + awacs_reg[0] = MASK_MUX_CD; + /* FIXME: Only machines with external SRS module need MASK_PAROUT */ + awacs_reg[1] = MASK_LOOPTHRU; + if (awacs_has_iic || awacs_device_id == 0x5 || /*awacs_device_id == 0x8 + || */awacs_device_id == 0xb) + awacs_reg[1] |= MASK_PAROUT; + /* get default volume from nvram */ + vol = (~nvram_read_byte(0x1308) & 7) << 1; + awacs_reg[2] = vol + (vol << 6); + awacs_reg[4] = vol + (vol << 6); + awacs_reg[5] = 0; + awacs_reg[6] = 0; + awacs_reg[7] = 0; + out_le32(&awacs->control, 0x11); + awacs_write(awacs_reg[0] + MASK_ADDR0); + awacs_write(awacs_reg[1] + MASK_ADDR1); + awacs_write(awacs_reg[2] + MASK_ADDR2); + awacs_write(awacs_reg[4] + MASK_ADDR4); + if (awacs_is_screamer) { + awacs_write(awacs_reg[5] + MASK_ADDR5); + awacs_write(awacs_reg[6] + MASK_ADDR6); + awacs_write(awacs_reg[7] + MASK_ADDR7); + } + + /* Initialize recent versions of the awacs */ + if (awacs_revision == 0) { + awacs_revision = + (in_le32(&awacs->codec_stat) >> 12) & 0xf; + if (awacs_revision == 3) { + mdelay(100); + awacs_write(0x6000); + mdelay(2); + awacs_write(awacs_reg[1] + MASK_ADDR1); + awacs_enable_amp(100 * 0x101); + } + } + if (awacs_revision >= AWACS_BURGUNDY) + awacs_burgundy_init(); + + /* Initialize beep stuff */ + beep_dbdma_cmd = awacs_tx_cmds + (write_sq.numBufs + 1); + orig_mksound = kd_mksound; + kd_mksound = awacs_mksound; + beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); + if (beep_buf == NULL) + printk(KERN_WARNING "dmasound: no memory for " + "beep buffer\n"); +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier(&awacs_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ + + /* Powerbooks have odd ways of enabling inputs such as + an expansion-bay CD or sound from an internal modem + or a PC-card modem. */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) { + is_pbook_3400 = 1; + /* + * Enable CD and PC-card sound inputs. + * This is done by reading from address + * f301a000, + 0x10 to enable the expansion-bay + * CD sound input, + 0x80 to enable the PC-card + * sound input. The 0x100 enables the SCSI bus + * terminator power. + */ + latch_base = (unsigned char *) ioremap + (0xf301a000, 0x1000); + in_8(latch_base + 0x190); + } else if (machine_is_compatible("PowerBook1,1") + || machine_is_compatible("AAPL,PowerBook1998")) { + struct device_node* mio; + macio_base = 0; + is_pbook_G3 = 1; + for (mio = np->parent; mio; mio = mio->parent) { + if (strcmp(mio->name, "mac-io") == 0 + && mio->n_addrs > 0) { + macio_base = (unsigned char *) ioremap + (mio->addrs[0].address, 0x40); + break; + } + } + /* + * Enable CD sound input. + * The relevant bits for writing to this byte are 0x8f. + * I haven't found out what the 0x80 bit does. + * For the 0xf bits, writing 3 or 7 enables the CD + * input, any other value disables it. Values + * 1, 3, 5, 7 enable the microphone. Values 0, 2, + * 4, 6, 8 - f enable the input from the modem. + */ + if (macio_base) + out_8(macio_base + 0x37, 3); + } + sprintf(awacs_name, "PowerMac (AWACS rev %d) ", + awacs_revision); + return dmasound_init(); + } + return -ENODEV; +} + +static void __exit dmasound_awacs_cleanup(void) +{ + dmasound_deinit(); +} + +module_init(dmasound_awacs_init); +module_exit(dmasound_awacs_cleanup); +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmasound/dmasound_core.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,1313 @@ + +/* + * linux/drivers/sound/dmasound/dmasound_core.c + * + * + * OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for + * Linux/m68k + * Extended to support Power Macintosh for Linux/ppc by Paul Mackerras + * + * (c) 1995 by Michael Schlueter & Michael Marte + * + * Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS + * interface and the u-law to signed byte conversion. + * + * Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue, + * /dev/mixer, /dev/sndstat and complemented the VFS interface. He would like + * to thank: + * - Michael Schlueter for initial ideas and documentation on the MFP and + * the DMA sound hardware. + * - Therapy? for their CD 'Troublegum' which really made me rock. + * + * /dev/sndstat is based on code by Hannu Savolainen, the author of the + * VoxWare family of drivers. + * + * 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 archive + * for more details. + * + * History: + * + * 1995/8/25 First release + * + * 1995/9/02 Roman Hodek: + * - Fixed atari_stram_alloc() call, the timer + * programming and several race conditions + * 1995/9/14 Roman Hodek: + * - After some discussion with Michael Schlueter, + * revised the interrupt disabling + * - Slightly speeded up U8->S8 translation by using + * long operations where possible + * - Added 4:3 interpolation for /dev/audio + * + * 1995/9/20 Torsten Scherer: + * - Fixed a bug in sq_write and changed /dev/audio + * converting to play at 12517Hz instead of 6258Hz. + * + * 1995/9/23 Torsten Scherer: + * - Changed sq_interrupt() and sq_play() to pre-program + * the DMA for another frame while there's still one + * running. This allows the IRQ response to be + * arbitrarily delayed and playing will still continue. + * + * 1995/10/14 Guenther Kelleter, Torsten Scherer: + * - Better support for Falcon audio (the Falcon doesn't + * raise an IRQ at the end of a frame, but at the + * beginning instead!). uses 'if (codec_dma)' in lots + * of places to simply switch between Falcon and TT + * code. + * + * 1995/11/06 Torsten Scherer: + * - Started introducing a hardware abstraction scheme + * (may perhaps also serve for Amigas?) + * - Can now play samples at almost all frequencies by + * means of a more generalized expand routine + * - Takes a good deal of care to cut data only at + * sample sizes + * - Buffer size is now a kernel runtime option + * - Implemented fsync() & several minor improvements + * Guenther Kelleter: + * - Useful hints and bug fixes + * - Cross-checked it for Falcons + * + * 1996/3/9 Geert Uytterhoeven: + * - Support added for Amiga, A-law, 16-bit little + * endian. + * - Unification to drivers/sound/dmasound.c. + * + * 1996/4/6 Martin Mitchell: + * - Updated to 1.3 kernel. + * + * 1996/6/13 Topi Kanerva: + * - Fixed things that were broken (mainly the amiga + * 14-bit routines) + * - /dev/sndstat shows now the real hardware frequency + * - The lowpass filter is disabled by default now + * + * 1996/9/25 Geert Uytterhoeven: + * - Modularization + * + * 1998/6/10 Andreas Schwab: + * - Converted to use sound_core + * + * 1999/12/28 Richard Zidlicky: + * - Added support for Q40 + * + * 2000/2/27 Geert Uytterhoeven: + * - Clean up and split the code into 4 parts: + * o dmasound_core: machine-independent code + * o dmasound_atari: Atari TT and Falcon support + * o dmasound_awacs: Apple PowerMac support + * o dmasound_paula: Amiga support + * + * 2000/3/25 Geert Uytterhoeven: + * - Integration of dmasound_q40 + * - Small clean ups + */ + + +#include +#include +#include +#include +#include +#include + +#include + +#include "dmasound.h" + + + /* + * Declarations + */ + +int dmasound_catchRadius = 0; +static unsigned int numWriteBufs = 4; +static unsigned int writeBufSize = 32; /* in KB! */ +#ifdef HAS_RECORD +static unsigned int numReadBufs = 4; +static unsigned int readBufSize = 32; /* in KB! */ +#endif + +MODULE_PARM(dmasound_catchRadius, "i"); +MODULE_PARM(numWriteBufs, "i"); +MODULE_PARM(writeBufSize, "i"); +MODULE_PARM(numReadBufs, "i"); +MODULE_PARM(readBufSize, "i"); +MODULE_LICENSE("GPL"); + +#ifdef MODULE +static int sq_unit = -1; +static int mixer_unit = -1; +static int state_unit = -1; +static int irq_installed = 0; +#endif /* MODULE */ + + + /* + * Conversion tables + */ + +#ifdef HAS_8BIT_TABLES +/* 8 bit mu-law */ + +char dmasound_ulaw2dma8[] = { + -126, -122, -118, -114, -110, -106, -102, -98, + -94, -90, -86, -82, -78, -74, -70, -66, + -63, -61, -59, -57, -55, -53, -51, -49, + -47, -45, -43, -41, -39, -37, -35, -33, + -31, -30, -29, -28, -27, -26, -25, -24, + -23, -22, -21, -20, -19, -18, -17, -16, + -16, -15, -15, -14, -14, -13, -13, -12, + -12, -11, -11, -10, -10, -9, -9, -8, + -8, -8, -7, -7, -7, -7, -6, -6, + -6, -6, -5, -5, -5, -5, -4, -4, + -4, -4, -4, -4, -3, -3, -3, -3, + -3, -3, -3, -3, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 0, + 125, 121, 117, 113, 109, 105, 101, 97, + 93, 89, 85, 81, 77, 73, 69, 65, + 62, 60, 58, 56, 54, 52, 50, 48, + 46, 44, 42, 40, 38, 36, 34, 32, + 30, 29, 28, 27, 26, 25, 24, 23, + 22, 21, 20, 19, 18, 17, 16, 15, + 15, 14, 14, 13, 13, 12, 12, 11, + 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 6, 6, 5, 5, + 5, 5, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 2, 2, 2, 2, + 2, 2, 2, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* 8 bit A-law */ + +char dmasound_alaw2dma8[] = { + -22, -21, -24, -23, -18, -17, -20, -19, + -30, -29, -32, -31, -26, -25, -28, -27, + -11, -11, -12, -12, -9, -9, -10, -10, + -15, -15, -16, -16, -13, -13, -14, -14, + -86, -82, -94, -90, -70, -66, -78, -74, + -118, -114, -126, -122, -102, -98, -110, -106, + -43, -41, -47, -45, -35, -33, -39, -37, + -59, -57, -63, -61, -51, -49, -55, -53, + -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -6, -6, -6, -6, -5, -5, -5, -5, + -8, -8, -8, -8, -7, -7, -7, -7, + -3, -3, -3, -3, -3, -3, -3, -3, + -4, -4, -4, -4, -4, -4, -4, -4, + 21, 20, 23, 22, 17, 16, 19, 18, + 29, 28, 31, 30, 25, 24, 27, 26, + 10, 10, 11, 11, 8, 8, 9, 9, + 14, 14, 15, 15, 12, 12, 13, 13, + 86, 82, 94, 90, 70, 66, 78, 74, + 118, 114, 126, 122, 102, 98, 110, 106, + 43, 41, 47, 45, 35, 33, 39, 37, + 59, 57, 63, 61, 51, 49, 55, 53, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 4, 4, 4, 4, + 7, 7, 7, 7, 6, 6, 6, 6, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3 +}; +#endif /* HAS_8BIT_TABLES */ + +#ifdef HAS_16BIT_TABLES + +/* 16 bit mu-law */ + +short dmasound_ulaw2dma16[] = { + -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, + -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, + -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, + -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0, +}; + +/* 16 bit A-law */ + +short dmasound_alaw2dma16[] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, + -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, + -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, + -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848, +}; +#endif /* HAS_16BIT_TABLES */ + + +#ifdef HAS_14BIT_TABLES + + /* + * Unused for now. Where are the MSB parts anyway?? + */ + +/* 14 bit mu-law (LSB) */ + +char dmasound_ulaw2dma14l[] = { + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 49, 17, 49, 17, 49, 17, 49, 17, + 49, 17, 49, 17, 49, 17, 49, 17, + 41, 57, 9, 25, 41, 57, 9, 25, + 41, 57, 9, 25, 41, 57, 9, 25, + 37, 45, 53, 61, 5, 13, 21, 29, + 37, 45, 53, 61, 5, 13, 21, 29, + 35, 39, 43, 47, 51, 55, 59, 63, + 3, 7, 11, 15, 19, 23, 27, 31, + 34, 36, 38, 40, 42, 44, 46, 48, + 50, 52, 54, 56, 58, 60, 62, 0, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, + 15, 47, 15, 47, 15, 47, 15, 47, + 15, 47, 15, 47, 15, 47, 15, 47, + 23, 7, 55, 39, 23, 7, 55, 39, + 23, 7, 55, 39, 23, 7, 55, 39, + 27, 19, 11, 3, 59, 51, 43, 35, + 27, 19, 11, 3, 59, 51, 43, 35, + 29, 25, 21, 17, 13, 9, 5, 1, + 61, 57, 53, 49, 45, 41, 37, 33, + 30, 28, 26, 24, 22, 20, 18, 16, + 14, 12, 10, 8, 6, 4, 2, 0 +}; + +/* 14 bit A-law (LSB) */ + +char dmasound_alaw2dma14l[] = { + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 16, 48, 16, 48, 16, 48, 16, 48, + 16, 48, 16, 48, 16, 48, 16, 48, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 42, 46, 34, 38, 58, 62, 50, 54, + 10, 14, 2, 6, 26, 30, 18, 22, + 42, 46, 34, 38, 58, 62, 50, 54, + 10, 14, 2, 6, 26, 30, 18, 22, + 40, 56, 8, 24, 40, 56, 8, 24, + 40, 56, 8, 24, 40, 56, 8, 24, + 20, 28, 4, 12, 52, 60, 36, 44, + 20, 28, 4, 12, 52, 60, 36, 44, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 48, 16, 48, 16, 48, 16, 48, 16, + 48, 16, 48, 16, 48, 16, 48, 16, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 22, 18, 30, 26, 6, 2, 14, 10, + 54, 50, 62, 58, 38, 34, 46, 42, + 22, 18, 30, 26, 6, 2, 14, 10, + 54, 50, 62, 58, 38, 34, 46, 42, + 24, 8, 56, 40, 24, 8, 56, 40, + 24, 8, 56, 40, 24, 8, 56, 40, + 44, 36, 60, 52, 12, 4, 28, 20, + 44, 36, 60, 52, 12, 4, 28, 20 +}; +#endif /* HAS_14BIT_TABLES */ + + + /* + * Mid level stuff + */ + +struct sound_settings dmasound; + +static inline void sound_silence(void) +{ + /* update hardware settings one more */ + dmasound.mach.init(); + + dmasound.mach.silence(); +} + +static inline void sound_init(void) +{ + dmasound.mach.init(); +} + +static inline int sound_set_format(int format) +{ + return dmasound.mach.setFormat(format); +} + +static int sound_set_speed(int speed) +{ + if (speed < 0) + return dmasound.soft.speed; + + dmasound.soft.speed = speed; + dmasound.mach.init(); + if (dmasound.minDev == SND_DEV_DSP) + dmasound.dsp.speed = dmasound.soft.speed; + + return dmasound.soft.speed; +} + +static int sound_set_stereo(int stereo) +{ + if (stereo < 0) + return dmasound.soft.stereo; + + stereo = !!stereo; /* should be 0 or 1 now */ + + dmasound.soft.stereo = stereo; + if (dmasound.minDev == SND_DEV_DSP) + dmasound.dsp.stereo = stereo; + dmasound.mach.init(); + + return stereo; +} + +static ssize_t sound_copy_translate(TRANS *trans, const u_char *userPtr, + size_t userCount, u_char frame[], + ssize_t *frameUsed, ssize_t frameLeft) +{ + ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + + switch (dmasound.soft.format) { + case AFMT_MU_LAW: + ct_func = trans->ct_ulaw; + break; + case AFMT_A_LAW: + ct_func = trans->ct_alaw; + break; + case AFMT_S8: + ct_func = trans->ct_s8; + break; + case AFMT_U8: + ct_func = trans->ct_u8; + break; + case AFMT_S16_BE: + ct_func = trans->ct_s16be; + break; + case AFMT_U16_BE: + ct_func = trans->ct_u16be; + break; + case AFMT_S16_LE: + ct_func = trans->ct_s16le; + break; + case AFMT_U16_LE: + ct_func = trans->ct_u16le; + break; + default: + return 0; + } + return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); +} + + + /* + * /dev/mixer abstraction + */ + +static struct { + int busy; + int modify_counter; +} mixer; + +static int mixer_open(struct inode *inode, struct file *file) +{ + dmasound.mach.open(); + mixer.busy = 1; + return 0; +} + +static int mixer_release(struct inode *inode, struct file *file) +{ + lock_kernel(); + mixer.busy = 0; + dmasound.mach.release(); + unlock_kernel(); + return 0; +} +static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) +{ + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + mixer.modify_counter++; + switch (cmd) { + case OSS_GETVERSION: + return IOCTL_OUT(arg, SOUND_VERSION); + case SOUND_MIXER_INFO: + { + mixer_info info; + strncpy(info.id, dmasound.mach.name2, sizeof(info.id)); + strncpy(info.name, dmasound.mach.name2, sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + info.modify_counter = mixer.modify_counter; + if (copy_to_user((int *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + } + if (dmasound.mach.mixer_ioctl) + return dmasound.mach.mixer_ioctl(cmd, arg); + return -EINVAL; +} + +static struct file_operations mixer_fops = +{ + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: mixer_ioctl, + open: mixer_open, + release: mixer_release, +}; + +static void __init mixer_init(void) +{ +#ifndef MODULE + int mixer_unit; +#endif + mixer_unit = register_sound_mixer(&mixer_fops, -1); + if (mixer_unit < 0) + return; + + mixer.busy = 0; + dmasound.treble = 0; + dmasound.bass = 0; + if (dmasound.mach.mixer_init) + dmasound.mach.mixer_init(); +} + + + /* + * Sound queue stuff, the heart of the driver + */ + +struct sound_queue dmasound_write_sq; +#ifdef HAS_RECORD +struct sound_queue dmasound_read_sq; +#endif + +static int sq_allocate_buffers(struct sound_queue *sq, int num, int size) +{ + int i; + + if (sq->buffers) + return 0; + sq->numBufs = num; + sq->bufSize = size; + sq->buffers = kmalloc (num * sizeof(char *), GFP_KERNEL); + if (!sq->buffers) + return -ENOMEM; + for (i = 0; i < num; i++) { + sq->buffers[i] = dmasound.mach.dma_alloc(size, GFP_KERNEL); + if (!sq->buffers[i]) { + while (i--) + dmasound.mach.dma_free(sq->buffers[i], size); + kfree(sq->buffers); + sq->buffers = 0; + return -ENOMEM; + } + } + return 0; +} + +static void sq_release_buffers(struct sound_queue *sq) +{ + int i; + + if (sq->buffers) { + if (sq != &write_sq && dmasound.mach.abort_read) + dmasound.mach.abort_read(); + for (i = 0; i < sq->numBufs; i++) + dmasound.mach.dma_free(sq->buffers[i], sq->bufSize); + kfree(sq->buffers); + sq->buffers = NULL; + } +} + +static void sq_setup(struct sound_queue *sq, int max_count, int max_active, + int block_size) +{ + void (*setup_func)(void); + + sq->max_count = max_count; + sq->max_active = max_active; + sq->block_size = block_size; + + sq->front = sq->count = sq->rear_size = 0; + sq->syncing = 0; + sq->active = 0; + + if (sq == &write_sq) { + sq->rear = -1; + setup_func = dmasound.mach.write_sq_setup; + } else { + sq->rear = 0; + setup_func = dmasound.mach.read_sq_setup; + } + if (setup_func) + setup_func(); +} + +static inline void sq_play(void) +{ + dmasound.mach.play(); +} + +static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, + loff_t *ppos) +{ + ssize_t uWritten = 0; + u_char *dest; + ssize_t uUsed, bUsed, bLeft; + + /* ++TeSche: Is something like this necessary? + * Hey, that's an honest question! Or does any other part of the + * filesystem already checks this situation? I really don't know. + */ + if (uLeft == 0) + return 0; + + /* The interrupt doesn't start to play the last, incomplete frame. + * Thus we can append to it without disabling the interrupts! (Note + * also that write_sq.rear isn't affected by the interrupt.) + */ + + if (write_sq.count > 0 && + (bLeft = write_sq.block_size-write_sq.rear_size) > 0) { + dest = write_sq.buffers[write_sq.rear]; + bUsed = write_sq.rear_size; + uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, + dest, &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + write_sq.rear_size = bUsed; + } + + do { + while (write_sq.count == write_sq.max_active) { + sq_play(); + if (write_sq.open_mode & O_NONBLOCK) + return uWritten > 0 ? uWritten : -EAGAIN; + SLEEP(write_sq.action_queue); + if (signal_pending(current)) + return uWritten > 0 ? uWritten : -EINTR; + } + + /* Here, we can avoid disabling the interrupt by first + * copying and translating the data, and then updating + * the write_sq variables. Until this is done, the interrupt + * won't see the new frame and we can work on it + * undisturbed. + */ + + dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count]; + bUsed = 0; + bLeft = write_sq.block_size; + uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, + dest, &bUsed, bLeft); + if (uUsed <= 0) + break; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + if (bUsed) { + write_sq.rear = (write_sq.rear+1) % write_sq.max_count; + write_sq.rear_size = bUsed; + write_sq.count++; + } + } while (bUsed); /* uUsed may have been 0 */ + + sq_play(); + + return uUsed < 0? uUsed: uWritten; +} + +#ifdef HAS_RECORD + /* + * Here is how the values are used for reading. + * The value 'active' simply indicates the DMA is running. This is done + * so the driver semantics are DMA starts when the first read is posted. + * The value 'front' indicates the buffer we should next send to the user. + * The value 'rear' indicates the buffer the DMA is currently filling. + * When 'front' == 'rear' the buffer "ring" is empty (we always have an + * empty available). The 'rear_size' is used to track partial offsets + * into the current buffer. Right now, I just keep the DMA running. If + * the reader can't keep up, the interrupt tosses the oldest buffer. We + * could also shut down the DMA in this case. + */ + +static ssize_t sq_read(struct file *file, char *dst, size_t uLeft, + loff_t *ppos) +{ + + ssize_t uRead, bLeft, bUsed, uUsed; + + if (uLeft == 0) + return 0; + + if (!read_sq.active && dmasound.mach.record) + dmasound.mach.record(); /* Kick off the record process. */ + + uRead = 0; + + /* Move what the user requests, depending upon other options. + */ + while (uLeft > 0) { + + /* When front == rear, the DMA is not done yet. + */ + while (read_sq.front == read_sq.rear) { + if (read_sq.open_mode & O_NONBLOCK) { + return uRead > 0 ? uRead : -EAGAIN; + } + SLEEP(read_sq.action_queue); + if (signal_pending(current)) + return uRead > 0 ? uRead : -EINTR; + } + + /* The amount we move is either what is left in the + * current buffer or what the user wants. + */ + bLeft = read_sq.block_size - read_sq.rear_size; + bUsed = read_sq.rear_size; + uUsed = sound_copy_translate(dmasound.trans_read, dst, uLeft, + read_sq.buffers[read_sq.front], + &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + dst += uUsed; + uRead += uUsed; + uLeft -= uUsed; + read_sq.rear_size += bUsed; + if (read_sq.rear_size >= read_sq.block_size) { + read_sq.rear_size = 0; + read_sq.front++; + if (read_sq.front >= read_sq.max_active) + read_sq.front = 0; + } + } + return uRead; +} +#endif /* HAS_RECORD */ + +static inline void sq_init_waitqueue(struct sound_queue *sq) +{ + init_waitqueue_head(&sq->action_queue); + init_waitqueue_head(&sq->open_queue); + init_waitqueue_head(&sq->sync_queue); + sq->busy = 0; +} + +static inline void sq_wake_up(struct sound_queue *sq, struct file *file, + mode_t mode) +{ + if (file->f_mode & mode) { + sq->busy = 0; + WAKE_UP(sq->open_queue); + } +} + +static int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode, + int numbufs, int bufsize) +{ + int rc = 0; + + if (file->f_mode & mode) { + if (sq->busy) { + rc = -EBUSY; + if (file->f_flags & O_NONBLOCK) + return rc; + rc = -EINTR; + while (sq->busy) { + SLEEP(sq->open_queue); + if (signal_pending(current)) + return rc; + } + rc = 0; + } + sq->busy = 1; /* Let's play spot-the-race-condition */ + + if (sq_allocate_buffers(sq, numbufs, bufsize)) { + sq_wake_up(sq, file, mode); + return rc; + } + + sq_setup(sq, numbufs, numbufs, bufsize); + sq->open_mode = file->f_mode; + } + return rc; +} + +#define write_sq_init_waitqueue() sq_init_waitqueue(&write_sq) +#define write_sq_wake_up(file) sq_wake_up(&write_sq, file, FMODE_WRITE) +#define write_sq_release_buffers() sq_release_buffers(&write_sq) +#define write_sq_open(file) \ + sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize << 10) + +#ifdef HAS_RECORD +#define read_sq_init_waitqueue() sq_init_waitqueue(&read_sq) +#define read_sq_wake_up(file) sq_wake_up(&read_sq, file, FMODE_READ) +#define read_sq_release_buffers() sq_release_buffers(&read_sq) +#define read_sq_open(file) \ + sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize << 10) +#else /* !HAS_RECORD */ +#define read_sq_init_waitqueue() do {} while (0) +#define read_sq_wake_up(file) do {} while (0) +#define read_sq_release_buffers() do {} while (0) +#define read_sq_open(file) (0) +#endif /* !HAS_RECORD */ + +static int sq_open(struct inode *inode, struct file *file) +{ + int rc; + + dmasound.mach.open(); + if ((rc = write_sq_open(file)) || (rc = read_sq_open(file))) { + dmasound.mach.release(); + return rc; + } + + if (dmasound.mach.sq_open) + dmasound.mach.sq_open(); + dmasound.minDev = MINOR(inode->i_rdev) & 0x0f; + dmasound.soft = dmasound.dsp; + dmasound.hard = dmasound.dsp; + sound_init(); + if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) { + sound_set_speed(8000); + sound_set_stereo(0); + sound_set_format(AFMT_MU_LAW); + } + +#if 0 + if (file->f_mode == FMODE_READ && dmasound.mach.record) { + /* Start dma'ing straight away */ + dmasound.mach.record(); + } +#endif + + return 0; +} + +static void sq_reset(void) +{ + sound_silence(); + write_sq.active = 0; + write_sq.count = 0; + write_sq.front = (write_sq.rear+1) % write_sq.max_count; +} + +static int sq_fsync(struct file *filp, struct dentry *dentry) +{ + int rc = 0; + + write_sq.syncing = 1; + sq_play(); /* there may be an incomplete frame waiting */ + + while (write_sq.active) { + SLEEP(write_sq.sync_queue); + if (signal_pending(current)) { + /* While waiting for audio output to drain, an + * interrupt occurred. Stop audio output immediately + * and clear the queue. */ + sq_reset(); + rc = -EINTR; + break; + } + } + + write_sq.syncing = 0; + return rc; +} + +static int sq_release(struct inode *inode, struct file *file) +{ + int rc = 0; + + lock_kernel(); + if (write_sq.busy) + rc = sq_fsync(file, file->f_dentry); + dmasound.soft = dmasound.dsp; + dmasound.hard = dmasound.dsp; + sound_silence(); + + write_sq_release_buffers(); + read_sq_release_buffers(); + dmasound.mach.release(); + + /* There is probably a DOS atack here. They change the mode flag. */ + /* XXX add check here */ + read_sq_wake_up(file); + write_sq_wake_up(file); + + /* Wake up a process waiting for the queue being released. + * Note: There may be several processes waiting for a call + * to open() returning. */ + unlock_kernel(); + + return rc; +} + +static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) +{ + int val; + u_long fmt; + int data; + int size, nbufs; + audio_buf_info info; + + switch (cmd) { + case SNDCTL_DSP_RESET: + sq_reset(); + return 0; + case SNDCTL_DSP_POST: + case SNDCTL_DSP_SYNC: + return sq_fsync(file, file->f_dentry); + + /* ++TeSche: before changing any of these it's + * probably wise to wait until sound playing has + * settled down. */ + case SNDCTL_DSP_SPEED: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_speed(data)); + case SNDCTL_DSP_STEREO: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data)); + case SOUND_PCM_WRITE_CHANNELS: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); + case SNDCTL_DSP_SETFMT: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_format(data)); + case SNDCTL_DSP_GETFMTS: + fmt = 0; + if (dmasound.trans_write) { + if (dmasound.trans_write->ct_ulaw) + fmt |= AFMT_MU_LAW; + if (dmasound.trans_write->ct_alaw) + fmt |= AFMT_A_LAW; + if (dmasound.trans_write->ct_s8) + fmt |= AFMT_S8; + if (dmasound.trans_write->ct_u8) + fmt |= AFMT_U8; + if (dmasound.trans_write->ct_s16be) + fmt |= AFMT_S16_BE; + if (dmasound.trans_write->ct_u16be) + fmt |= AFMT_U16_BE; + if (dmasound.trans_write->ct_s16le) + fmt |= AFMT_S16_LE; + if (dmasound.trans_write->ct_u16le) + fmt |= AFMT_U16_LE; + } + return IOCTL_OUT(arg, fmt); + case SNDCTL_DSP_GETBLKSIZE: + size = write_sq.block_size + * dmasound.soft.size * (dmasound.soft.stereo + 1) + / (dmasound.hard.size * (dmasound.hard.stereo + 1)); + return IOCTL_OUT(arg, size); + case SNDCTL_DSP_SUBDIVIDE: + break; + case SNDCTL_DSP_SETFRAGMENT: + if (write_sq.count || write_sq.active || write_sq.syncing) + return -EINVAL; + IOCTL_IN(arg, size); + nbufs = size >> 16; + if (nbufs < 2 || nbufs > write_sq.numBufs) + nbufs = write_sq.numBufs; + size &= 0xffff; + if (size >= 8 && size <= 29) { + size = 1 << size; + size *= dmasound.hard.size * (dmasound.hard.stereo + 1); + size /= dmasound.soft.size * (dmasound.soft.stereo + 1); + if (size > write_sq.bufSize) + size = write_sq.bufSize; + } else + size = write_sq.bufSize; + sq_setup(&write_sq, write_sq.numBufs, nbufs, size); + return IOCTL_OUT(arg,write_sq.bufSize | write_sq.numBufs << 16); + case SNDCTL_DSP_GETOSPACE: + info.fragments = write_sq.max_active - write_sq.count; + info.fragstotal = write_sq.max_active; + info.fragsize = write_sq.block_size; + info.bytes = info.fragments * info.fragsize; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + case SNDCTL_DSP_GETCAPS: + val = 1; /* Revision level of this ioctl() */ + return IOCTL_OUT(arg,val); + + default: + return mixer_ioctl(inode, file, cmd, arg); + } + return -EINVAL; +} + +static struct file_operations sq_fops = +{ + owner: THIS_MODULE, + llseek: no_llseek, + write: sq_write, + ioctl: sq_ioctl, + open: sq_open, + release: sq_release, +#ifdef HAS_RECORD + read: sq_read, +#endif +}; + +static void __init sq_init(void) +{ +#ifndef MODULE + int sq_unit; +#endif + sq_unit = register_sound_dsp(&sq_fops, -1); + if (sq_unit < 0) + return; + + write_sq_init_waitqueue(); + read_sq_init_waitqueue(); + + /* whatever you like as startup mode for /dev/dsp, + * (/dev/audio hasn't got a startup mode). note that + * once changed a new open() will *not* restore these! + */ + dmasound.dsp.format = AFMT_U8; + dmasound.dsp.stereo = 0; + dmasound.dsp.size = 8; + + /* set minimum rate possible without expanding */ + dmasound.dsp.speed = dmasound.mach.min_dsp_speed; + + /* before the first open to /dev/dsp this wouldn't be set */ + dmasound.soft = dmasound.dsp; + dmasound.hard = dmasound.dsp; + + sound_silence(); +} + + + /* + * /dev/sndstat + */ + +static struct { + int busy; + char buf[512]; /* state.buf should not overflow! */ + int len, ptr; +} state; + +static int state_open(struct inode *inode, struct file *file) +{ + char *buffer = state.buf; + int len = 0; + + if (state.busy) + return -EBUSY; + + dmasound.mach.open(); + state.ptr = 0; + state.busy = 1; + + len += sprintf(buffer+len, "%sDMA sound driver:\n", dmasound.mach.name); + + len += sprintf(buffer+len, "\tsound.format = 0x%x", + dmasound.soft.format); + switch (dmasound.soft.format) { + case AFMT_MU_LAW: + len += sprintf(buffer+len, " (mu-law)"); + break; + case AFMT_A_LAW: + len += sprintf(buffer+len, " (A-law)"); + break; + case AFMT_U8: + len += sprintf(buffer+len, " (unsigned 8 bit)"); + break; + case AFMT_S8: + len += sprintf(buffer+len, " (signed 8 bit)"); + break; + case AFMT_S16_BE: + len += sprintf(buffer+len, " (signed 16 bit big)"); + break; + case AFMT_U16_BE: + len += sprintf(buffer+len, " (unsigned 16 bit big)"); + break; + case AFMT_S16_LE: + len += sprintf(buffer+len, " (signed 16 bit little)"); + break; + case AFMT_U16_LE: + len += sprintf(buffer+len, " (unsigned 16 bit little)"); + break; + } + len += sprintf(buffer+len, "\n"); + len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n", + dmasound.soft.speed, dmasound.hard.speed); + len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", + dmasound.soft.stereo, + dmasound.soft.stereo ? "stereo" : "mono"); + if (dmasound.mach.state_info) + len += dmasound.mach.state_info(buffer); + len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d" + " sq.max_active = %d\n", + write_sq.block_size, write_sq.max_count, + write_sq.max_active); + len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", + write_sq.count, write_sq.rear_size); + len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n", + write_sq.active, write_sq.syncing); + state.len = len; + return 0; +} + +static int state_release(struct inode *inode, struct file *file) +{ + lock_kernel(); + state.busy = 0; + dmasound.mach.release(); + unlock_kernel(); + return 0; +} + +static ssize_t state_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int n = state.len - state.ptr; + if (n > count) + n = count; + if (n <= 0) + return 0; + if (copy_to_user(buf, &state.buf[state.ptr], n)) + return -EFAULT; + state.ptr += n; + return n; +} + +static struct file_operations state_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: state_read, + open: state_open, + release: state_release, +}; + +static void __init state_init(void) +{ +#ifndef MODULE + int state_unit; +#endif + state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); + if (state_unit < 0) + return; + state.busy = 0; +} + + + /* + * Config & Setup + * + * This function is called by _one_ chipset-specific driver + */ + +int __init dmasound_init(void) +{ +#ifdef MODULE + if (irq_installed) + return -EBUSY; +#endif + + /* Set up sound queue, /dev/audio and /dev/dsp. */ + + /* Set default settings. */ + sq_init(); + + /* Set up /dev/sndstat. */ + state_init(); + + /* Set up /dev/mixer. */ + mixer_init(); + + if (!dmasound.mach.irqinit()) { + printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n"); + return -ENODEV; + } +#ifdef MODULE + irq_installed = 1; +#endif + + printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", + numWriteBufs, writeBufSize); + + return 0; +} + +#ifdef MODULE + +void dmasound_deinit(void) +{ + if (irq_installed) { + sound_silence(); + dmasound.mach.irqcleanup(); + } + + write_sq_release_buffers(); + read_sq_release_buffers(); + + if (mixer_unit >= 0) + unregister_sound_mixer(mixer_unit); + if (state_unit >= 0) + unregister_sound_special(state_unit); + if (sq_unit >= 0) + unregister_sound_dsp(sq_unit); +} + +#else /* !MODULE */ + +static int __init dmasound_setup(char *str) +{ + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + /* check the bootstrap parameter for "dmasound=" */ + + switch (ints[0]) { + case 3: + if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS)) + printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius); + else + catchRadius = ints[3]; + /* fall through */ + case 2: + if (ints[1] < MIN_BUFFERS) + printk("dmasound_setup: illegal number of buffers, using default = %d\n", numWriteBufs); + else + numWriteBufs = ints[1]; + if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE) + printk("dmasound_setup: illegal buffer size, using default = %dK\n", writeBufSize); + else + writeBufSize = ints[2]; + break; + case 0: + break; + default: + printk("dmasound_setup: illegal number of arguments\n"); + return 0; + } + return 1; +} + +__setup("dmasound=", dmasound_setup); + +#endif /* !MODULE */ + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(dmasound); +EXPORT_SYMBOL(dmasound_init); +#ifdef MODULE +EXPORT_SYMBOL(dmasound_deinit); +#endif +EXPORT_SYMBOL(dmasound_write_sq); +#ifdef HAS_RECORD +EXPORT_SYMBOL(dmasound_read_sq); +#endif +EXPORT_SYMBOL(dmasound_catchRadius); +#ifdef HAS_8BIT_TABLES +EXPORT_SYMBOL(dmasound_ulaw2dma8); +EXPORT_SYMBOL(dmasound_alaw2dma8); +#endif +#ifdef HAS_16BIT_TABLES +EXPORT_SYMBOL(dmasound_ulaw2dma16); +EXPORT_SYMBOL(dmasound_alaw2dma16); +#endif +#ifdef HAS_14BIT_TABLES +EXPORT_SYMBOL(dmasound_ulaw2dma14l); +EXPORT_SYMBOL(dmasound_ulaw2dma14h); +EXPORT_SYMBOL(dmasound_alaw2dma14l); +EXPORT_SYMBOL(dmasound_alaw2dma14h); +#endif + diff -Nru a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmasound/dmasound_paula.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,723 @@ + +/* + * linux/drivers/sound/dmasound/dmasound_paula.c + * + * Amiga `Paula' DMA Sound Driver + * + * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "dmasound.h" + + + /* + * The minimum period for audio depends on htotal (for OCS/ECS/AGA) + * (Imported from arch/m68k/amiga/amisound.c) + */ + +extern volatile u_short amiga_audio_min_period; + + + /* + * amiga_mksound() should be able to restore the period after beeping + * (Imported from arch/m68k/amiga/amisound.c) + */ + +extern u_short amiga_audio_period; + + + /* + * Audio DMA masks + */ + +#define AMI_AUDIO_OFF (DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3) +#define AMI_AUDIO_8 (DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1) +#define AMI_AUDIO_14 (AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3) + + + /* + * Helper pointers for 16(14)-bit sound + */ + +static int write_sq_block_size_half, write_sq_block_size_quarter; + + +/*** Low level stuff *********************************************************/ + + +static void AmiOpen(void); +static void AmiRelease(void); +static void *AmiAlloc(unsigned int size, int flags); +static void AmiFree(void *obj, unsigned int size); +static int AmiIrqInit(void); +#ifdef MODULE +static void AmiIrqCleanUp(void); +#endif +static void AmiSilence(void); +static void AmiInit(void); +static int AmiSetFormat(int format); +static int AmiSetVolume(int volume); +static int AmiSetTreble(int treble); +static void AmiPlayNextFrame(int index); +static void AmiPlay(void); +static void AmiInterrupt(int irq, void *dummy, struct pt_regs *fp); + +#ifdef CONFIG_HEARTBEAT + + /* + * Heartbeat interferes with sound since the 7 kHz low-pass filter and the + * power LED are controlled by the same line. + */ + +#ifdef CONFIG_APUS +#define mach_heartbeat ppc_md.heartbeat +#endif + +static void (*saved_heartbeat)(int) = NULL; + +static inline void disable_heartbeat(void) +{ + if (mach_heartbeat) { + saved_heartbeat = mach_heartbeat; + mach_heartbeat = NULL; + } + AmiSetTreble(dmasound.treble); +} + +static inline void enable_heartbeat(void) +{ + if (saved_heartbeat) + mach_heartbeat = saved_heartbeat; +} +#else /* !CONFIG_HEARTBEAT */ +#define disable_heartbeat() do { } while (0) +#define enable_heartbeat() do { } while (0) +#endif /* !CONFIG_HEARTBEAT */ + + +/*** Mid level stuff *********************************************************/ + +static void AmiMixerInit(void); +static int AmiMixerIoctl(u_int cmd, u_long arg); +static void AmiWriteSqSetup(void); +static int AmiStateInfo(char *buffer); + + +/*** Translations ************************************************************/ + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + + + /* + * Native format + */ + +static ssize_t ami_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + void *p = &frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft) & ~1; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + } else { + u_char *left = &frame[*frameUsed>>1]; + u_char *right = left+write_sq_block_size_half; + count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1; + used = count*2; + while (count > 0) { + if (get_user(*left++, userPtr++) + || get_user(*right++, userPtr++)) + return -EFAULT; + count--; + } + } + *frameUsed += used; + return used; +} + + + /* + * Copy and convert 8 bit data + */ + +#define GENERATE_AMI_CT8(funcname, convsample) \ +static ssize_t funcname(const u_char *userPtr, size_t userCount, \ + u_char frame[], ssize_t *frameUsed, \ + ssize_t frameLeft) \ +{ \ + ssize_t count, used; \ + \ + if (!dmasound.soft.stereo) { \ + u_char *p = &frame[*frameUsed]; \ + count = min_t(size_t, userCount, frameLeft) & ~1; \ + used = count; \ + while (count > 0) { \ + u_char data; \ + if (get_user(data, userPtr++)) \ + return -EFAULT; \ + *p++ = convsample(data); \ + count--; \ + } \ + } else { \ + u_char *left = &frame[*frameUsed>>1]; \ + u_char *right = left+write_sq_block_size_half; \ + count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \ + used = count*2; \ + while (count > 0) { \ + u_char data; \ + if (get_user(data, userPtr++)) \ + return -EFAULT; \ + *left++ = convsample(data); \ + if (get_user(data, userPtr++)) \ + return -EFAULT; \ + *right++ = convsample(data); \ + count--; \ + } \ + } \ + *frameUsed += used; \ + return used; \ +} + +#define AMI_CT_ULAW(x) (dmasound_ulaw2dma8[(x)]) +#define AMI_CT_ALAW(x) (dmasound_alaw2dma8[(x)]) +#define AMI_CT_U8(x) ((x) ^ 0x80) + +GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW) +GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW) +GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8) + + + /* + * Copy and convert 16 bit data + */ + +#define GENERATE_AMI_CT_16(funcname, convsample) \ +static ssize_t funcname(const u_char *userPtr, size_t userCount, \ + u_char frame[], ssize_t *frameUsed, \ + ssize_t frameLeft) \ +{ \ + ssize_t count, used; \ + u_short data; \ + \ + if (!dmasound.soft.stereo) { \ + u_char *high = &frame[*frameUsed>>1]; \ + u_char *low = high+write_sq_block_size_half; \ + count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \ + used = count*2; \ + while (count > 0) { \ + if (get_user(data, ((u_short *)userPtr)++)) \ + return -EFAULT; \ + data = convsample(data); \ + *high++ = data>>8; \ + *low++ = (data>>2) & 0x3f; \ + count--; \ + } \ + } else { \ + u_char *lefth = &frame[*frameUsed>>2]; \ + u_char *leftl = lefth+write_sq_block_size_quarter; \ + u_char *righth = lefth+write_sq_block_size_half; \ + u_char *rightl = righth+write_sq_block_size_quarter; \ + count = min_t(size_t, userCount, frameLeft)>>2 & ~1; \ + used = count*4; \ + while (count > 0) { \ + if (get_user(data, ((u_short *)userPtr)++)) \ + return -EFAULT; \ + data = convsample(data); \ + *lefth++ = data>>8; \ + *leftl++ = (data>>2) & 0x3f; \ + if (get_user(data, ((u_short *)userPtr)++)) \ + return -EFAULT; \ + data = convsample(data); \ + *righth++ = data>>8; \ + *rightl++ = (data>>2) & 0x3f; \ + count--; \ + } \ + } \ + *frameUsed += used; \ + return used; \ +} + +#define AMI_CT_S16BE(x) (x) +#define AMI_CT_U16BE(x) ((x) ^ 0x8000) +#define AMI_CT_S16LE(x) (le2be16((x))) +#define AMI_CT_U16LE(x) (le2be16((x)) ^ 0x8000) + +GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE) +GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE) +GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE) +GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE) + + +static TRANS transAmiga = { + ct_ulaw: ami_ct_ulaw, + ct_alaw: ami_ct_alaw, + ct_s8: ami_ct_s8, + ct_u8: ami_ct_u8, + ct_s16be: ami_ct_s16be, + ct_u16be: ami_ct_u16be, + ct_s16le: ami_ct_s16le, + ct_u16le: ami_ct_u16le, +}; + +/*** Low level stuff *********************************************************/ + + +static void AmiOpen(void) +{ + MOD_INC_USE_COUNT; +} + +static void AmiRelease(void) +{ + MOD_DEC_USE_COUNT; +} + +static inline void StopDMA(void) +{ + custom.aud[0].audvol = custom.aud[1].audvol = 0; + custom.aud[2].audvol = custom.aud[3].audvol = 0; + custom.dmacon = AMI_AUDIO_OFF; + enable_heartbeat(); +} + +static void *AmiAlloc(unsigned int size, int flags) +{ + return amiga_chip_alloc((long)size, "dmasound [Paula]"); +} + +static void AmiFree(void *obj, unsigned int size) +{ + amiga_chip_free (obj); +} + +static int __init AmiIrqInit(void) +{ + /* turn off DMA for audio channels */ + StopDMA(); + + /* Register interrupt handler. */ + if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound", + AmiInterrupt)) + return 0; + return 1; +} + +#ifdef MODULE +static void AmiIrqCleanUp(void) +{ + /* turn off DMA for audio channels */ + StopDMA(); + /* release the interrupt */ + free_irq(IRQ_AMIGA_AUD0, AmiInterrupt); +} +#endif /* MODULE */ + +static void AmiSilence(void) +{ + /* turn off DMA for audio channels */ + StopDMA(); +} + + +static void AmiInit(void) +{ + int period, i; + + AmiSilence(); + + if (dmasound.soft.speed) + period = amiga_colorclock/dmasound.soft.speed-1; + else + period = amiga_audio_min_period; + dmasound.hard = dmasound.soft; + dmasound.trans_write = &transAmiga; + + if (period < amiga_audio_min_period) { + /* we would need to squeeze the sound, but we won't do that */ + period = amiga_audio_min_period; + } else if (period > 65535) { + period = 65535; + } + dmasound.hard.speed = amiga_colorclock/(period+1); + + for (i = 0; i < 4; i++) + custom.aud[i].audper = period; + amiga_audio_period = period; +} + + +static int AmiSetFormat(int format) +{ + int size; + + /* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */ + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + size = 8; + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = size; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = dmasound.soft.size; + } + AmiInit(); + + return format; +} + + +#define VOLUME_VOXWARE_TO_AMI(v) \ + (((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100) +#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64) + +static int AmiSetVolume(int volume) +{ + dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff); + custom.aud[0].audvol = dmasound.volume_left; + dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8); + custom.aud[1].audvol = dmasound.volume_right; + if (dmasound.hard.size == 16) { + if (dmasound.volume_left == 64 && dmasound.volume_right == 64) { + custom.aud[2].audvol = 1; + custom.aud[3].audvol = 1; + } else { + custom.aud[2].audvol = 0; + custom.aud[3].audvol = 0; + } + } + return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) | + (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8); +} + +static int AmiSetTreble(int treble) +{ + dmasound.treble = treble; + if (treble < 50) + ciaa.pra &= ~0x02; + else + ciaa.pra |= 0x02; + return treble; +} + + +#define AMI_PLAY_LOADED 1 +#define AMI_PLAY_PLAYING 2 +#define AMI_PLAY_MASK 3 + + +static void AmiPlayNextFrame(int index) +{ + u_char *start, *ch0, *ch1, *ch2, *ch3; + u_long size; + + /* used by AmiPlay() if all doubts whether there really is something + * to be played are already wiped out. + */ + start = write_sq.buffers[write_sq.front]; + size = (write_sq.count == index ? write_sq.rear_size + : write_sq.block_size)>>1; + + if (dmasound.hard.stereo) { + ch0 = start; + ch1 = start+write_sq_block_size_half; + size >>= 1; + } else { + ch0 = start; + ch1 = start; + } + + disable_heartbeat(); + custom.aud[0].audvol = dmasound.volume_left; + custom.aud[1].audvol = dmasound.volume_right; + if (dmasound.hard.size == 8) { + custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0); + custom.aud[0].audlen = size; + custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1); + custom.aud[1].audlen = size; + custom.dmacon = AMI_AUDIO_8; + } else { + size >>= 1; + custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0); + custom.aud[0].audlen = size; + custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1); + custom.aud[1].audlen = size; + if (dmasound.volume_left == 64 && dmasound.volume_right == 64) { + /* We can play pseudo 14-bit only with the maximum volume */ + ch3 = ch0+write_sq_block_size_quarter; + ch2 = ch1+write_sq_block_size_quarter; + custom.aud[2].audvol = 1; /* we are being affected by the beeps */ + custom.aud[3].audvol = 1; /* restoring volume here helps a bit */ + custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2); + custom.aud[2].audlen = size; + custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3); + custom.aud[3].audlen = size; + custom.dmacon = AMI_AUDIO_14; + } else { + custom.aud[2].audvol = 0; + custom.aud[3].audvol = 0; + custom.dmacon = AMI_AUDIO_8; + } + } + write_sq.front = (write_sq.front+1) % write_sq.max_count; + write_sq.active |= AMI_PLAY_LOADED; +} + + +static void AmiPlay(void) +{ + int minframes = 1; + + custom.intena = IF_AUD0; + + if (write_sq.active & AMI_PLAY_LOADED) { + /* There's already a frame loaded */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } + + if (write_sq.active & AMI_PLAY_PLAYING) + /* Increase threshold: frame 1 is already being played */ + minframes = 2; + + if (write_sq.count < minframes) { + /* Nothing to do */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } + + if (write_sq.count <= minframes && + write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } + + AmiPlayNextFrame(minframes); + + custom.intena = IF_SETCLR | IF_AUD0; +} + + +static void AmiInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ + int minframes = 1; + + custom.intena = IF_AUD0; + + if (!write_sq.active) { + /* Playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(write_sq.sync_queue); + return; + } + + if (write_sq.active & AMI_PLAY_PLAYING) { + /* We've just finished a frame */ + write_sq.count--; + WAKE_UP(write_sq.action_queue); + } + + if (write_sq.active & AMI_PLAY_LOADED) + /* Increase threshold: frame 1 is already being played */ + minframes = 2; + + /* Shift the flags */ + write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK; + + if (!write_sq.active) + /* No frame is playing, disable audio DMA */ + StopDMA(); + + custom.intena = IF_SETCLR | IF_AUD0; + + if (write_sq.count >= minframes) + /* Try to play the next frame */ + AmiPlay(); + + if (!write_sq.active) + /* Nothing to play anymore. + Wake up a process waiting for audio output to drain. */ + WAKE_UP(write_sq.sync_queue); +} + +/*** Mid level stuff *********************************************************/ + + +/* + * /dev/mixer abstraction + */ + +static void __init AmiMixerInit(void) +{ + dmasound.volume_left = 64; + dmasound.volume_right = 64; + custom.aud[0].audvol = dmasound.volume_left; + custom.aud[3].audvol = 1; /* For pseudo 14bit */ + custom.aud[1].audvol = dmasound.volume_right; + custom.aud[2].audvol = 1; /* For pseudo 14bit */ + dmasound.treble = 50; +} + +static int AmiMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_DEVMASK: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE); + case SOUND_MIXER_READ_RECMASK: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_STEREODEVS: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME); + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, + VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) | + VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_volume(data)); + case SOUND_MIXER_READ_TREBLE: + return IOCTL_OUT(arg, dmasound.treble); + case SOUND_MIXER_WRITE_TREBLE: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_treble(data)); + } + return -EINVAL; +} + + +static void AmiWriteSqSetup(void) +{ + write_sq_block_size_half = write_sq.block_size>>1; + write_sq_block_size_quarter = write_sq_block_size_half>>1; +} + + +static int AmiStateInfo(char *buffer) +{ + int len = 0; + len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n", + dmasound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n", + dmasound.volume_right); + return len; +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machAmiga = { + name: "Amiga", + name2: "AMIGA", + open: AmiOpen, + release: AmiRelease, + dma_alloc: AmiAlloc, + dma_free: AmiFree, + irqinit: AmiIrqInit, +#ifdef MODULE + irqcleanup: AmiIrqCleanUp, +#endif /* MODULE */ + init: AmiInit, + silence: AmiSilence, + setFormat: AmiSetFormat, + setVolume: AmiSetVolume, + setTreble: AmiSetTreble, + play: AmiPlay, + mixer_init: AmiMixerInit, + mixer_ioctl: AmiMixerIoctl, + write_sq_setup: AmiWriteSqSetup, + state_info: AmiStateInfo, + min_dsp_speed: 8000 +}; + + +/*** Config & Setup **********************************************************/ + + +int __init dmasound_paula_init(void) +{ + int err; + + if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_AUDIO)) { + if (!request_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40, + "dmasound [Paula]")) + return -EBUSY; + dmasound.mach = machAmiga; + err = dmasound_init(); + if (err) + release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40); + return err; + } else + return -ENODEV; +} + +static void __exit dmasound_paula_cleanup(void) +{ + dmasound_deinit(); + release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40); +} + +module_init(dmasound_paula_init); +module_exit(dmasound_paula_cleanup); +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/dmasound/dmasound_q40.c b/sound/oss/dmasound/dmasound_q40.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/dmasound/dmasound_q40.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,588 @@ + +/* + * linux/drivers/sound/dmasound/dmasound_q40.c + * + * Q40 DMA Sound Driver + * + * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits + */ + + +#include +#include +#include +#include + +#include +#include + +#include "dmasound.h" + + +static int expand_bal; /* Balance factor for expanding (not volume!) */ +static int expand_data; /* Data for expanding */ + + +/*** Low level stuff *********************************************************/ + + +static void Q40Open(void); +static void Q40Release(void); +static void *Q40Alloc(unsigned int size, int flags); +static void Q40Free(void *, unsigned int); +static int Q40IrqInit(void); +#ifdef MODULE +static void Q40IrqCleanUp(void); +#endif +static void Q40Silence(void); +static void Q40Init(void); +static int Q40SetFormat(int format); +static int Q40SetVolume(int volume); +static void Q40PlayNextFrame(int index); +static void Q40Play(void); +static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp); +static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp); +static void Q40Interrupt(void); + + +/*** Mid level stuff *********************************************************/ + + +#if 1 +/* userCount, frameUsed, frameLeft == byte counts */ +static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8; + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + + used = count = min_t(size_t, userCount, frameLeft); + if (copy_from_user(p,userPtr,count)) + return -EFAULT; + while (count > 0) { + *p = table[*p]+128; + p++; + count--; + } + *frameUsed += used ; + return used; +} +#else +static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8; + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + u_char val; + int stereo = sound.soft.stereo; + + + frameLeft >>= 1; + if (stereo) + userCount >>= 1; + used = count = min_t(size_t, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]+128; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]+128; + } + *p++ = val; + count--; + } + *frameUsed += used * 2; + return stereo? used * 2: used; +} +#endif + +#if 1 +static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + + used = count = min_t(size_t, userCount, frameLeft); + if (copy_from_user(p,userPtr,count)) + return -EFAULT; + while (count > 0) { + *p = *p + 128; + p++; + count--; + } + *frameUsed += used; + return used; +} +#else +static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + u_char val; + int stereo = dmasound.soft.stereo; + + frameLeft >>= 1; + if (stereo) + userCount >>= 1; + used = count = min_t(size_t, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data + 128; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data + 128; + } + *p++ = val; + count--; + } + *frameUsed += used * 2; + return stereo? used * 2: used; +} +#endif + +#if 1 +static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + + used = count = min_t(size_t, userCount, frameLeft); + if (copy_from_user(p,userPtr,count)) + return -EFAULT; + *frameUsed += used; + return used; +} +#else +static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + u_char val; + int stereo = dmasound.soft.stereo; + + + frameLeft >>= 1; + if (stereo) + userCount >>= 1; + used = count = min_t(size_t, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data; + } + *p++ = val; + count--; + } + *frameUsed += used * 2; + return stereo? used * 2: used; +} +#endif + +/* a bit too complicated to optimise right now ..*/ +static ssize_t q40_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned char *table = (unsigned char *) + (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8); + unsigned int data = expand_data; + u_char *p = (u_char *) &frame[*frameUsed]; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + data += 0x80; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft); + utotal -= userCount; + return utotal; +} + + +static ssize_t q40_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + u_char *p = (u_char *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + + + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c ; + data += 0x80; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft); + utotal -= userCount; + return utotal; +} + + +static ssize_t q40_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + u_char *p = (u_char *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c ; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) ; + utotal -= userCount; + return utotal; +} + + +static TRANS transQ40Normal = { + q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL +}; + +static TRANS transQ40Expanding = { + q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL +}; + + +/*** Low level stuff *********************************************************/ + + +static void Q40Open(void) +{ + MOD_INC_USE_COUNT; +} + +static void Q40Release(void) +{ + MOD_DEC_USE_COUNT; +} + + +static void *Q40Alloc(unsigned int size, int flags) +{ + return kmalloc(size, flags); /* change to vmalloc */ +} + +static void Q40Free(void *ptr, unsigned int size) +{ + kfree(ptr); +} + +static int __init Q40IrqInit(void) +{ + /* Register interrupt handler. */ + request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, + "DMA sound", Q40Interrupt); + + return(1); +} + + +#ifdef MODULE +static void Q40IrqCleanUp(void) +{ + master_outb(0,SAMPLE_ENABLE_REG); + free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); +} +#endif /* MODULE */ + + +static void Q40Silence(void) +{ + master_outb(0,SAMPLE_ENABLE_REG); + *DAC_LEFT=*DAC_RIGHT=0; +} + +static char *q40_pp=NULL; +static unsigned int q40_sc=0; + +static void Q40PlayNextFrame(int index) +{ + u_char *start; + u_long size; + u_char speed; + + /* used by Q40Play() if all doubts whether there really is something + * to be played are already wiped out. + */ + start = write_sq.buffers[write_sq.front]; + size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size); + + q40_pp=start; + q40_sc=size; + + write_sq.front = (write_sq.front+1) % write_sq.max_count; + write_sq.active++; + + speed=(dmasound.hard.speed==10000 ? 0 : 1); + + master_outb( 0,SAMPLE_ENABLE_REG); + free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); + if (dmasound.soft.stereo) + request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, + "Q40 sound", Q40Interrupt); + else + request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0, + "Q40 sound", Q40Interrupt); + + master_outb( speed, SAMPLE_RATE_REG); + master_outb( 1,SAMPLE_CLEAR_REG); + master_outb( 1,SAMPLE_ENABLE_REG); +} + +static void Q40Play(void) +{ + unsigned long flags; + + if (write_sq.active || write_sq.count<=0 ) { + /* There's already a frame loaded */ + return; + } + + /* nothing in the queue */ + if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + return; + } + save_flags(flags); cli(); + Q40PlayNextFrame(1); + restore_flags(flags); +} + +static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ + if (q40_sc>1){ + *DAC_LEFT=*q40_pp++; + *DAC_RIGHT=*q40_pp++; + q40_sc -=2; + master_outb(1,SAMPLE_CLEAR_REG); + }else Q40Interrupt(); +} +static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ + if (q40_sc>0){ + *DAC_LEFT=*q40_pp; + *DAC_RIGHT=*q40_pp++; + q40_sc --; + master_outb(1,SAMPLE_CLEAR_REG); + }else Q40Interrupt(); +} +static void Q40Interrupt(void) +{ + if (!write_sq.active) { + /* playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(write_sq.sync_queue); + master_outb(0,SAMPLE_ENABLE_REG); /* better safe */ + goto exit; + } else write_sq.active=0; + write_sq.count--; + Q40Play(); + + if (q40_sc<2) + { /* there was nothing to play, disable irq */ + master_outb(0,SAMPLE_ENABLE_REG); + *DAC_LEFT=*DAC_RIGHT=0; + } + WAKE_UP(write_sq.action_queue); + + exit: + master_outb(1,SAMPLE_CLEAR_REG); +} + + +static void Q40Init(void) +{ + int i, idx; + const int freq[] = {10000, 20000}; + + /* search a frequency that fits into the allowed error range */ + + idx = -1; + for (i = 0; i < 2; i++) + if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius) + idx = i; + + dmasound.hard = dmasound.soft; + /*sound.hard.stereo=1;*/ /* no longer true */ + dmasound.hard.size=8; + + if (idx > -1) { + dmasound.soft.speed = freq[idx]; + dmasound.trans_write = &transQ40Normal; + } else + dmasound.trans_write = &transQ40Expanding; + + Q40Silence(); + + if (dmasound.hard.speed > 20000) { + /* we would need to squeeze the sound, but we won't do that */ + dmasound.hard.speed = 20000; + dmasound.trans_write = &transQ40Normal; + } else if (dmasound.hard.speed > 10000) { + dmasound.hard.speed = 20000; + } else { + dmasound.hard.speed = 10000; + } + expand_bal = -dmasound.soft.speed; +} + + +static int Q40SetFormat(int format) +{ + /* Q40 sound supports only 8bit modes */ + + switch (format) { + case AFMT_QUERY: + return(dmasound.soft.format); + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_S8: + case AFMT_U8: + break; + default: + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = 8; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = 8; + } + Q40Init(); + + return(format); +} + +static int Q40SetVolume(int volume) +{ + return 0; +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machQ40 = { + name: "Q40", + name2: "Q40", + open: Q40Open, + release: Q40Release, + dma_alloc: Q40Alloc, + dma_free: Q40Free, + irqinit: Q40IrqInit, +#ifdef MODULE + irqcleanup: Q40IrqCleanUp, +#endif /* MODULE */ + init: Q40Init, + silence: Q40Silence, + setFormat: Q40SetFormat, + setVolume: Q40SetVolume, + play: Q40Play +}; + + +/*** Config & Setup **********************************************************/ + + +int __init dmasound_q40_init(void) +{ + if (MACH_IS_Q40) { + dmasound.mach = machQ40; + return dmasound_init(); + } else + return -ENODEV; +} + +static void __exit dmasound_q40_cleanup(void) +{ + dmasound_deinit(); +} + +module_init(dmasound_q40_init); +module_exit(dmasound_q40_cleanup); +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/emu10k1/8010.h b/sound/oss/emu10k1/8010.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/8010.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,624 @@ +/* + ********************************************************************** + * 8010.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox Cleaned of 8bit chars, DOS + * line endings + * December 8, 1999 Jon Taylor Added lots of new register info + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * + ********************************************************************** + */ + + +#ifndef _8010_H +#define _8010_H + +#include + +/************************************************************************************************/ +/* PCI function 0 registers, address = + PCIBASE0 */ +/************************************************************************************************/ + +#define PTR 0x00 /* Indexed register set pointer register */ + /* NOTE: The CHANNELNUM and ADDRESS words can */ + /* be modified independently of each other. */ +#define PTR_CHANNELNUM_MASK 0x0000003f /* For each per-channel register, indicates the */ + /* channel number of the register to be */ + /* accessed. For non per-channel registers the */ + /* value should be set to zero. */ +#define PTR_ADDRESS_MASK 0x07ff0000 /* Register index */ + +#define DATA 0x04 /* Indexed register set data register */ + +#define IPR 0x08 /* Global interrupt pending register */ + /* Clear pending interrupts by writing a 1 to */ + /* the relevant bits and zero to the other bits */ +#define IPR_SAMPLERATETRACKER 0x01000000 /* Sample rate tracker lock status change */ +#define IPR_FXDSP 0x00800000 /* Enable FX DSP interrupts */ +#define IPR_FORCEINT 0x00400000 /* Force Sound Blaster interrupt */ +#define IPR_PCIERROR 0x00200000 /* PCI bus error */ +#define IPR_VOLINCR 0x00100000 /* Volume increment button pressed */ +#define IPR_VOLDECR 0x00080000 /* Volume decrement button pressed */ +#define IPR_MUTE 0x00040000 /* Mute button pressed */ +#define IPR_MICBUFFULL 0x00020000 /* Microphone buffer full */ +#define IPR_MICBUFHALFFULL 0x00010000 /* Microphone buffer half full */ +#define IPR_ADCBUFFULL 0x00008000 /* ADC buffer full */ +#define IPR_ADCBUFHALFFULL 0x00004000 /* ADC buffer half full */ +#define IPR_EFXBUFFULL 0x00002000 /* Effects buffer full */ +#define IPR_EFXBUFHALFFULL 0x00001000 /* Effects buffer half full */ +#define IPR_GPSPDIFSTATUSCHANGE 0x00000800 /* GPSPDIF channel status change */ +#define IPR_CDROMSTATUSCHANGE 0x00000400 /* CD-ROM channel status change */ +#define IPR_INTERVALTIMER 0x00000200 /* Interval timer terminal count */ +#define IPR_MIDITRANSBUFEMPTY 0x00000100 /* MIDI UART transmit buffer empty */ +#define IPR_MIDIRECVBUFEMPTY 0x00000080 /* MIDI UART receive buffer empty */ +#define IPR_CHANNELLOOP 0x00000040 /* One or more channel loop interrupts pending */ +#define IPR_CHANNELNUMBERMASK 0x0000003f /* When IPR_CHANNELLOOP is set, indicates the */ + /* Highest set channel in CLIPL or CLIPH. When */ + /* IP is written with CL set, the bit in CLIPL */ + /* or CLIPH corresponding to the CIN value */ + /* written will be cleared. */ + +#define INTE 0x0c /* Interrupt enable register */ +#define INTE_VIRTUALSB_MASK 0xc0000000 /* Virtual Soundblaster I/O port capture */ +#define INTE_VIRTUALSB_220 0x00000000 /* Capture at I/O base address 0x220-0x22f */ +#define INTE_VIRTUALSB_240 0x40000000 /* Capture at I/O base address 0x240 */ +#define INTE_VIRTUALSB_260 0x80000000 /* Capture at I/O base address 0x260 */ +#define INTE_VIRTUALSB_280 0xc0000000 /* Capture at I/O base address 0x280 */ +#define INTE_VIRTUALMPU_MASK 0x30000000 /* Virtual MPU I/O port capture */ +#define INTE_VIRTUALMPU_300 0x00000000 /* Capture at I/O base address 0x300-0x301 */ +#define INTE_VIRTUALMPU_310 0x10000000 /* Capture at I/O base address 0x310 */ +#define INTE_VIRTUALMPU_320 0x20000000 /* Capture at I/O base address 0x320 */ +#define INTE_VIRTUALMPU_330 0x30000000 /* Capture at I/O base address 0x330 */ +#define INTE_MASTERDMAENABLE 0x08000000 /* Master DMA emulation at 0x000-0x00f */ +#define INTE_SLAVEDMAENABLE 0x04000000 /* Slave DMA emulation at 0x0c0-0x0df */ +#define INTE_MASTERPICENABLE 0x02000000 /* Master PIC emulation at 0x020-0x021 */ +#define INTE_SLAVEPICENABLE 0x01000000 /* Slave PIC emulation at 0x0a0-0x0a1 */ +#define INTE_VSBENABLE 0x00800000 /* Enable virtual Soundblaster */ +#define INTE_ADLIBENABLE 0x00400000 /* Enable AdLib emulation at 0x388-0x38b */ +#define INTE_MPUENABLE 0x00200000 /* Enable virtual MPU */ +#define INTE_FORCEINT 0x00100000 /* Continuously assert INTAN */ + +#define INTE_MRHANDENABLE 0x00080000 /* Enable the "Mr. Hand" logic */ + /* NOTE: There is no reason to use this under */ + /* Linux, and it will cause odd hardware */ + /* behavior and possibly random segfaults and */ + /* lockups if enabled. */ + +#define INTE_SAMPLERATETRACKER 0x00002000 /* Enable sample rate tracker interrupts */ + /* NOTE: This bit must always be enabled */ +#define INTE_FXDSPENABLE 0x00001000 /* Enable FX DSP interrupts */ +#define INTE_PCIERRORENABLE 0x00000800 /* Enable PCI bus error interrupts */ +#define INTE_VOLINCRENABLE 0x00000400 /* Enable volume increment button interrupts */ +#define INTE_VOLDECRENABLE 0x00000200 /* Enable volume decrement button interrupts */ +#define INTE_MUTEENABLE 0x00000100 /* Enable mute button interrupts */ +#define INTE_MICBUFENABLE 0x00000080 /* Enable microphone buffer interrupts */ +#define INTE_ADCBUFENABLE 0x00000040 /* Enable ADC buffer interrupts */ +#define INTE_EFXBUFENABLE 0x00000020 /* Enable Effects buffer interrupts */ +#define INTE_GPSPDIFENABLE 0x00000010 /* Enable GPSPDIF status interrupts */ +#define INTE_CDSPDIFENABLE 0x00000008 /* Enable CDSPDIF status interrupts */ +#define INTE_INTERVALTIMERENB 0x00000004 /* Enable interval timer interrupts */ +#define INTE_MIDITXENABLE 0x00000002 /* Enable MIDI transmit-buffer-empty interrupts */ +#define INTE_MIDIRXENABLE 0x00000001 /* Enable MIDI receive-buffer-empty interrupts */ + +#define WC 0x10 /* Wall Clock register */ +#define WC_SAMPLECOUNTER_MASK 0x03FFFFC0 /* Sample periods elapsed since reset */ +#define WC_SAMPLECOUNTER 0x14060010 +#define WC_CURRENTCHANNEL 0x0000003F /* Channel [0..63] currently being serviced */ + /* NOTE: Each channel takes 1/64th of a sample */ + /* period to be serviced. */ + +#define HCFG 0x14 /* Hardware config register */ + /* NOTE: There is no reason to use the legacy */ + /* SoundBlaster emulation stuff described below */ + /* under Linux, and all kinds of weird hardware */ + /* behavior can result if you try. Don't. */ +#define HCFG_LEGACYFUNC_MASK 0xe0000000 /* Legacy function number */ +#define HCFG_LEGACYFUNC_MPU 0x00000000 /* Legacy MPU */ +#define HCFG_LEGACYFUNC_SB 0x40000000 /* Legacy SB */ +#define HCFG_LEGACYFUNC_AD 0x60000000 /* Legacy AD */ +#define HCFG_LEGACYFUNC_MPIC 0x80000000 /* Legacy MPIC */ +#define HCFG_LEGACYFUNC_MDMA 0xa0000000 /* Legacy MDMA */ +#define HCFG_LEGACYFUNC_SPCI 0xc0000000 /* Legacy SPCI */ +#define HCFG_LEGACYFUNC_SDMA 0xe0000000 /* Legacy SDMA */ +#define HCFG_IOCAPTUREADDR 0x1f000000 /* The 4 LSBs of the captured I/O address. */ +#define HCFG_LEGACYWRITE 0x00800000 /* 1 = write, 0 = read */ +#define HCFG_LEGACYWORD 0x00400000 /* 1 = word, 0 = byte */ +#define HCFG_LEGACYINT 0x00200000 /* 1 = legacy event captured. Write 1 to clear. */ + /* NOTE: The rest of the bits in this register */ + /* _are_ relevant under Linux. */ +#define HCFG_CODECFORMAT_MASK 0x00070000 /* CODEC format */ +#define HCFG_CODECFORMAT_AC97 0x00000000 /* AC97 CODEC format -- Primary Output */ +#define HCFG_CODECFORMAT_I2S 0x00010000 /* I2S CODEC format -- Secondary (Rear) Output */ +#define HCFG_GPINPUT0 0x00004000 /* External pin112 */ +#define HCFG_GPINPUT1 0x00002000 /* External pin110 */ + +#define HCFG_GPOUTPUT_MASK 0x00001c00 /* External pins which may be controlled */ +#define HCFG_GPOUT0 0x00001000 /* set to enable digital out on 5.1 cards */ + +#define HCFG_JOYENABLE 0x00000200 /* Internal joystick enable */ +#define HCFG_PHASETRACKENABLE 0x00000100 /* Phase tracking enable */ + /* 1 = Force all 3 async digital inputs to use */ + /* the same async sample rate tracker (ZVIDEO) */ +#define HCFG_AC3ENABLE_MASK 0x0x0000e0 /* AC3 async input control - Not implemented */ +#define HCFG_AC3ENABLE_ZVIDEO 0x00000080 /* Channels 0 and 1 replace ZVIDEO */ +#define HCFG_AC3ENABLE_CDSPDIF 0x00000040 /* Channels 0 and 1 replace CDSPDIF */ +#define HCFG_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */ +#define HCFG_AUTOMUTE 0x00000010 /* When set, the async sample rate convertors */ + /* will automatically mute their output when */ + /* they are not rate-locked to the external */ + /* async audio source */ +#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE_MASK 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE 0x01020014 +#define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Master mute button sets AUDIOENABLE = 0. */ + /* NOTE: This is a 'cheap' way to implement a */ + /* master mute function on the mute button, and */ + /* in general should not be used unless a more */ + /* sophisticated master mute function has not */ + /* been written. */ +#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ + /* Should be set to 1 when the EMU10K1 is */ + /* completely initialized. */ + +#define MUDATA 0x18 /* MPU401 data register (8 bits) */ + +#define MUCMD 0x19 /* MPU401 command register (8 bits) */ +#define MUCMD_RESET 0xff /* RESET command */ +#define MUCMD_ENTERUARTMODE 0x3f /* Enter_UART_mode command */ + /* NOTE: All other commands are ignored */ + +#define MUSTAT MUCMD /* MPU401 status register (8 bits) */ +#define MUSTAT_IRDYN 0x80 /* 0 = MIDI data or command ACK */ +#define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ + +#define TIMER 0x1a /* Timer terminal count register */ + /* NOTE: After the rate is changed, a maximum */ + /* of 1024 sample periods should be allowed */ + /* before the new rate is guaranteed accurate. */ +#define TIMER_RATE_MASK 0x000003ff /* Timer interrupt rate in sample periods */ + /* 0 == 1024 periods, [1..4] are not useful */ +#define TIMER_RATE 0x0a00001a + +#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ + +#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ +#define AC97ADDRESS_READY 0x80 /* Read-only bit, reflects CODEC READY signal */ +#define AC97ADDRESS_ADDRESS 0x7f /* Address of indexed AC97 register */ + +/********************************************************************************************************/ +/* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers */ +/********************************************************************************************************/ + +#define CPF 0x00 /* Current pitch and fraction register */ +#define CPF_CURRENTPITCH_MASK 0xffff0000 /* Current pitch (linear, 0x4000 == unity pitch shift) */ +#define CPF_CURRENTPITCH 0x10100000 +#define CPF_STEREO_MASK 0x00008000 /* 1 = Even channel interleave, odd channel locked */ +#define CPF_STOP_MASK 0x00004000 /* 1 = Current pitch forced to 0 */ +#define CPF_FRACADDRESS_MASK 0x00003fff /* Linear fractional address of the current channel */ + +#define PTRX 0x01 /* Pitch target and send A/B amounts register */ +#define PTRX_PITCHTARGET_MASK 0xffff0000 /* Pitch target of specified channel */ +#define PTRX_PITCHTARGET 0x10100001 +#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00 /* Linear level of channel output sent to FX send bus A */ +#define PTRX_FXSENDAMOUNT_A 0x08080001 +#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff /* Linear level of channel output sent to FX send bus B */ +#define PTRX_FXSENDAMOUNT_B 0x08000001 + +#define CVCF 0x02 /* Current volume and filter cutoff register */ +#define CVCF_CURRENTVOL_MASK 0xffff0000 /* Current linear volume of specified channel */ +#define CVCF_CURRENTVOL 0x10100002 +#define CVCF_CURRENTFILTER_MASK 0x0000ffff /* Current filter cutoff frequency of specified channel */ +#define CVCF_CURRENTFILTER 0x10000002 + +#define VTFT 0x03 /* Volume target and filter cutoff target register */ +#define VTFT_VOLUMETARGET_MASK 0xffff0000 /* Volume target of specified channel */ +#define VTFT_FILTERTARGET_MASK 0x0000ffff /* Filter cutoff target of specified channel */ + +#define Z1 0x05 /* Filter delay memory 1 register */ + +#define Z2 0x04 /* Filter delay memory 2 register */ + +#define PSST 0x06 /* Send C amount and loop start address register */ +#define PSST_FXSENDAMOUNT_C_MASK 0xff000000 /* Linear level of channel output sent to FX send bus C */ + +#define PSST_FXSENDAMOUNT_C 0x08180006 + +#define PSST_LOOPSTARTADDR_MASK 0x00ffffff /* Loop start address of the specified channel */ +#define PSST_LOOPSTARTADDR 0x18000006 + +#define DSL 0x07 /* Send D amount and loop start address register */ +#define DSL_FXSENDAMOUNT_D_MASK 0xff000000 /* Linear level of channel output sent to FX send bus D */ + +#define DSL_FXSENDAMOUNT_D 0x08180007 + +#define DSL_LOOPENDADDR_MASK 0x00ffffff /* Loop end address of the specified channel */ +#define DSL_LOOPENDADDR 0x18000007 + +#define CCCA 0x08 /* Filter Q, interp. ROM, byte size, cur. addr register */ +#define CCCA_RESONANCE 0xf0000000 /* Lowpass filter resonance (Q) height */ +#define CCCA_INTERPROMMASK 0x0e000000 /* Selects passband of interpolation ROM */ + /* 1 == full band, 7 == lowpass */ + /* ROM 0 is used when pitch shifting downward or less */ + /* then 3 semitones upward. Increasingly higher ROM */ + /* numbers are used, typically in steps of 3 semitones, */ + /* as upward pitch shifting is performed. */ +#define CCCA_INTERPROM_0 0x00000000 /* Select interpolation ROM 0 */ +#define CCCA_INTERPROM_1 0x02000000 /* Select interpolation ROM 1 */ +#define CCCA_INTERPROM_2 0x04000000 /* Select interpolation ROM 2 */ +#define CCCA_INTERPROM_3 0x06000000 /* Select interpolation ROM 3 */ +#define CCCA_INTERPROM_4 0x08000000 /* Select interpolation ROM 4 */ +#define CCCA_INTERPROM_5 0x0a000000 /* Select interpolation ROM 5 */ +#define CCCA_INTERPROM_6 0x0c000000 /* Select interpolation ROM 6 */ +#define CCCA_INTERPROM_7 0x0e000000 /* Select interpolation ROM 7 */ +#define CCCA_8BITSELECT 0x01000000 /* 1 = Sound memory for this channel uses 8-bit samples */ +#define CCCA_CURRADDR_MASK 0x00ffffff /* Current address of the selected channel */ +#define CCCA_CURRADDR 0x18000008 + +#define CCR 0x09 /* Cache control register */ +#define CCR_CACHEINVALIDSIZE 0x07190009 +#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000 /* Number of invalid samples cache for this channel */ +#define CCR_CACHELOOPFLAG 0x01000000 /* 1 = Cache has a loop service pending */ +#define CCR_INTERLEAVEDSAMPLES 0x00800000 /* 1 = A cache service will fetch interleaved samples */ +#define CCR_WORDSIZEDSAMPLES 0x00400000 /* 1 = A cache service will fetch word sized samples */ +#define CCR_READADDRESS 0x06100009 +#define CCR_READADDRESS_MASK 0x003f0000 /* Location of cache just beyond current cache service */ +#define CCR_LOOPINVALSIZE 0x0000fe00 /* Number of invalid samples in cache prior to loop */ + /* NOTE: This is valid only if CACHELOOPFLAG is set */ +#define CCR_LOOPFLAG 0x00000100 /* Set for a single sample period when a loop occurs */ +#define CCR_CACHELOOPADDRHI 0x000000ff /* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */ + +#define CLP 0x0a /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */ + /* NOTE: This register is normally not used */ +#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address (DSL_LOOPSTARTADDR [0..15]) */ + +#define FXRT 0x0b /* Effects send routing register */ + /* NOTE: It is illegal to assign the same routing to */ + /* two effects sends. */ +#define FXRT_CHANNELA 0x000f0000 /* Effects send bus number for channel's effects send A */ +#define FXRT_CHANNELB 0x00f00000 /* Effects send bus number for channel's effects send B */ +#define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */ +#define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */ + +#define MAPA 0x0c /* Cache map A */ + +#define MAPB 0x0d /* Cache map B */ + +#define MAP_PTE_MASK 0xffffe000 /* The 19 MSBs of the PTE indexed by the PTI */ +#define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */ + +#define ENVVOL 0x10 /* Volume envelope register */ +#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ATKHLDV 0x11 /* Volume envelope hold and attack register */ +#define ATKHLDV_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDV_HOLDTIME_MASK 0x00007f00 /* Envelope hold time (127-n == n*88.2msec) */ +#define ATKHLDV_ATTACKTIME_MASK 0x0000007f /* Envelope attack time, log encoded */ + /* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec */ + +#define DCYSUSV 0x12 /* Volume envelope sustain and decay register */ +#define DCYSUSV_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSV_CHANNELENABLE_MASK 0x00000080 /* 1 = Inhibit envelope engine from writing values in */ + /* this channel and from writing to pitch, filter and */ + /* volume targets. */ +#define DCYSUSV_DECAYTIME_MASK 0x0000007f /* Volume envelope decay time, log encoded */ + /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL1 0x13 /* Modulation LFO value */ +#define LFOVAL_MASK 0x0000ffff /* Current value of modulation LFO state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ENVVAL 0x14 /* Modulation envelope register */ +#define ENVVAL_MASK 0x0000ffff /* Current value of modulation envelope state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ATKHLDM 0x15 /* Modulation envelope hold and attack register */ +#define ATKHLDM_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDM_HOLDTIME 0x00007f00 /* Envelope hold time (127-n == n*42msec) */ +#define ATKHLDM_ATTACKTIME 0x0000007f /* Envelope attack time, log encoded */ + /* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec */ + +#define DCYSUSM 0x16 /* Modulation envelope decay and sustain register */ +#define DCYSUSM_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSM_DECAYTIME_MASK 0x0000007f /* Envelope decay time, log encoded */ + /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL2 0x17 /* Vibrato LFO register */ +#define LFOVAL2_MASK 0x0000ffff /* Current value of vibrato LFO state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define IP 0x18 /* Initial pitch register */ +#define IP_MASK 0x0000ffff /* Exponential initial pitch shift */ + /* 4 bits of octave, 12 bits of fractional octave */ +#define IP_UNITY 0x0000e000 /* Unity pitch shift */ + +#define IFATN 0x19 /* Initial filter cutoff and attenuation register */ +#define IFATN_FILTERCUTOFF_MASK 0x0000ff00 /* Initial filter cutoff frequency in exponential units */ + /* 6 most significant bits are semitones */ + /* 2 least significant bits are fractions */ +#define IFATN_FILTERCUTOFF 0x08080019 +#define IFATN_ATTENUATION_MASK 0x000000ff /* Initial attenuation in 0.375dB steps */ +#define IFATN_ATTENUATION 0x08000019 + + +#define PEFE 0x1a /* Pitch envelope and filter envelope amount register */ +#define PEFE_PITCHAMOUNT_MASK 0x0000ff00 /* Pitch envlope amount */ + /* Signed 2's complement, +/- one octave peak extremes */ +#define PEFE_PITCHAMOUNT 0x0808001a +#define PEFE_FILTERAMOUNT_MASK 0x000000ff /* Filter envlope amount */ + /* Signed 2's complement, +/- six octaves peak extremes */ +#define PEFE_FILTERAMOUNT 0x0800001a +#define FMMOD 0x1b /* Vibrato/filter modulation from LFO register */ +#define FMMOD_MODVIBRATO 0x0000ff00 /* Vibrato LFO modulation depth */ + /* Signed 2's complement, +/- one octave extremes */ +#define FMMOD_MOFILTER 0x000000ff /* Filter LFO modulation depth */ + /* Signed 2's complement, +/- three octave extremes */ + + +#define TREMFRQ 0x1c /* Tremolo amount and modulation LFO frequency register */ +#define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */ + /* Signed 2's complement, with +/- 12dB extremes */ + +#define FM2FRQ2 0x1d /* Vibrato amount and vibrato LFO frequency register */ +#define FM2FRQ2_DEPTH 0x0000ff00 /* Vibrato LFO vibrato depth */ + /* Signed 2's complement, +/- one octave extremes */ +#define FM2FRQ2_FREQUENCY 0x000000ff /* Vibrato LFO frequency */ + /* 0.039Hz steps, maximum of 9.85 Hz. */ + +#define TEMPENV 0x1e /* Tempory envelope register */ +#define TEMPENV_MASK 0x0000ffff /* 16-bit value */ + /* NOTE: All channels contain internal variables; do */ + /* not write to these locations. */ + +#define CD0 0x20 /* Cache data 0 register */ +#define CD1 0x21 /* Cache data 1 register */ +#define CD2 0x22 /* Cache data 2 register */ +#define CD3 0x23 /* Cache data 3 register */ +#define CD4 0x24 /* Cache data 4 register */ +#define CD5 0x25 /* Cache data 5 register */ +#define CD6 0x26 /* Cache data 6 register */ +#define CD7 0x27 /* Cache data 7 register */ +#define CD8 0x28 /* Cache data 8 register */ +#define CD9 0x29 /* Cache data 9 register */ +#define CDA 0x2a /* Cache data A register */ +#define CDB 0x2b /* Cache data B register */ +#define CDC 0x2c /* Cache data C register */ +#define CDD 0x2d /* Cache data D register */ +#define CDE 0x2e /* Cache data E register */ +#define CDF 0x2f /* Cache data F register */ + +#define PTB 0x40 /* Page table base register */ +#define PTB_MASK 0xfffff000 /* Physical address of the page table in host memory */ + +#define TCB 0x41 /* Tank cache base register */ +#define TCB_MASK 0xfffff000 /* Physical address of the bottom of host based TRAM */ + +#define ADCCR 0x42 /* ADC sample rate/stereo control register */ +#define ADCCR_RCHANENABLE 0x00000010 /* Enables right channel for writing to the host */ +#define ADCCR_LCHANENABLE 0x00000008 /* Enables left channel for writing to the host */ + /* NOTE: To guarantee phase coherency, both channels */ + /* must be disabled prior to enabling both channels. */ +#define ADCCR_SAMPLERATE_MASK 0x00000007 /* Sample rate convertor output rate */ +#define ADCCR_SAMPLERATE_48 0x00000000 /* 48kHz sample rate */ +#define ADCCR_SAMPLERATE_44 0x00000001 /* 44.1kHz sample rate */ +#define ADCCR_SAMPLERATE_32 0x00000002 /* 32kHz sample rate */ +#define ADCCR_SAMPLERATE_24 0x00000003 /* 24kHz sample rate */ +#define ADCCR_SAMPLERATE_22 0x00000004 /* 22.05kHz sample rate */ +#define ADCCR_SAMPLERATE_16 0x00000005 /* 16kHz sample rate */ +#define ADCCR_SAMPLERATE_11 0x00000006 /* 11.025kHz sample rate */ +#define ADCCR_SAMPLERATE_8 0x00000007 /* 8kHz sample rate */ + +#define FXWC 0x43 /* FX output write channels register */ + /* When set, each bit enables the writing of the */ + /* corresponding FX output channel into host memory */ + +#define TCBS 0x44 /* Tank cache buffer size register */ +#define TCBS_MASK 0x00000007 /* Tank cache buffer size field */ +#define TCBS_BUFFSIZE_16K 0x00000000 +#define TCBS_BUFFSIZE_32K 0x00000001 +#define TCBS_BUFFSIZE_64K 0x00000002 +#define TCBS_BUFFSIZE_128K 0x00000003 +#define TCBS_BUFFSIZE_256K 0x00000004 +#define TCBS_BUFFSIZE_512K 0x00000005 +#define TCBS_BUFFSIZE_1024K 0x00000006 +#define TCBS_BUFFSIZE_2048K 0x00000007 + +#define MICBA 0x45 /* AC97 microphone buffer address register */ +#define MICBA_MASK 0xfffff000 /* 20 bit base address */ + +#define ADCBA 0x46 /* ADC buffer address register */ +#define ADCBA_MASK 0xfffff000 /* 20 bit base address */ + +#define FXBA 0x47 /* FX Buffer Address */ +#define FXBA_MASK 0xfffff000 /* 20 bit base address */ + +#define MICBS 0x49 /* Microphone buffer size register */ + +#define ADCBS 0x4a /* ADC buffer size register */ + +#define FXBS 0x4b /* FX buffer size register */ + +/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */ +#define ADCBS_BUFSIZE_NONE 0x00000000 +#define ADCBS_BUFSIZE_384 0x00000001 +#define ADCBS_BUFSIZE_448 0x00000002 +#define ADCBS_BUFSIZE_512 0x00000003 +#define ADCBS_BUFSIZE_640 0x00000004 +#define ADCBS_BUFSIZE_768 0x00000005 +#define ADCBS_BUFSIZE_896 0x00000006 +#define ADCBS_BUFSIZE_1024 0x00000007 +#define ADCBS_BUFSIZE_1280 0x00000008 +#define ADCBS_BUFSIZE_1536 0x00000009 +#define ADCBS_BUFSIZE_1792 0x0000000a +#define ADCBS_BUFSIZE_2048 0x0000000b +#define ADCBS_BUFSIZE_2560 0x0000000c +#define ADCBS_BUFSIZE_3072 0x0000000d +#define ADCBS_BUFSIZE_3584 0x0000000e +#define ADCBS_BUFSIZE_4096 0x0000000f +#define ADCBS_BUFSIZE_5120 0x00000010 +#define ADCBS_BUFSIZE_6144 0x00000011 +#define ADCBS_BUFSIZE_7168 0x00000012 +#define ADCBS_BUFSIZE_8192 0x00000013 +#define ADCBS_BUFSIZE_10240 0x00000014 +#define ADCBS_BUFSIZE_12288 0x00000015 +#define ADCBS_BUFSIZE_14366 0x00000016 +#define ADCBS_BUFSIZE_16384 0x00000017 +#define ADCBS_BUFSIZE_20480 0x00000018 +#define ADCBS_BUFSIZE_24576 0x00000019 +#define ADCBS_BUFSIZE_28672 0x0000001a +#define ADCBS_BUFSIZE_32768 0x0000001b +#define ADCBS_BUFSIZE_40960 0x0000001c +#define ADCBS_BUFSIZE_49152 0x0000001d +#define ADCBS_BUFSIZE_57344 0x0000001e +#define ADCBS_BUFSIZE_65536 0x0000001f + + +#define CDCS 0x50 /* CD-ROM digital channel status register */ + +#define GPSCS 0x51 /* General Purpose SPDIF channel status register*/ + +#define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ + +/* definitions for debug register - taken from the alsa drivers */ +#define DBG_ZC 0x80000000 /* zero tram counter */ +#define DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ +#define DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ +#define DBG_SINGLE_STEP 0x00008000 /* single step mode */ +#define DBG_STEP 0x00004000 /* start single step */ +#define DBG_CONDITION_CODE 0x00003e00 /* condition code */ +#define DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ + + +#define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ + +#define SPCS0 0x54 /* SPDIF output Channel Status 0 register */ + +#define SPCS1 0x55 /* SPDIF output Channel Status 1 register */ + +#define SPCS2 0x56 /* SPDIF output Channel Status 2 register */ + +#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */ +#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */ +#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */ +#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */ +#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */ +#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */ +#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */ +#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */ +#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */ +#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */ +#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */ +#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */ +#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */ +#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */ +#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */ +#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */ +#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */ +#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */ +#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */ +#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */ +#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */ +#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */ +#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */ + +/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status */ +#define CLIEL 0x58 /* Channel loop interrupt enable low register */ + +#define CLIEH 0x59 /* Channel loop interrupt enable high register */ + +#define CLIPL 0x5a /* Channel loop interrupt pending low register */ + +#define CLIPH 0x5b /* Channel loop interrupt pending high register */ + +#define SOLEL 0x5c /* Stop on loop enable low register */ + +#define SOLEH 0x5d /* Stop on loop enable high register */ + +#define SPBYPASS 0x5e /* SPDIF BYPASS mode register */ +#define SPBYPASS_ENABLE 0x00000001 /* Enable SPDIF bypass mode */ + +#define AC97SLOT 0x5f /* additional AC97 slots enable bits */ +#define AC97SLOT_CNTR 0x10 /* Center enable */ +#define AC97SLOT_LFE 0x20 /* LFE enable */ + +#define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */ + +#define GPSRCS 0x61 /* General Purpose SPDIF sample rate cvt status */ + +#define ZVSRCS 0x62 /* ZVideo sample rate converter status */ + /* NOTE: This one has no SPDIFLOCKED field */ + /* Assumes sample lock */ + +/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS. */ +#define SRCS_SPDIFLOCKED 0x02000000 /* SPDIF stream locked */ +#define SRCS_RATELOCKED 0x01000000 /* Sample rate locked */ +#define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */ + +#define MICIDX 0x63 /* Microphone recording buffer index register */ +#define MICIDX_MASK 0x0000ffff /* 16-bit value */ +#define MICIDX_IDX 0x10000063 + +#define ADCIDX 0x64 /* ADC recording buffer index register */ +#define ADCIDX_MASK 0x0000ffff /* 16 bit index field */ +#define ADCIDX_IDX 0x10000064 + +#define FXIDX 0x65 /* FX recording buffer index register */ +#define FXIDX_MASK 0x0000ffff /* 16-bit value */ +#define FXIDX_IDX 0x10000065 + +/* Each FX general purpose register is 32 bits in length, all bits are used */ +#define FXGPREGBASE 0x100 /* FX general purpose registers base */ + +/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */ +/* decompressed back to 20 bits on a read. There are a total of 160 locations, the last 32 */ +/* locations are for external TRAM. */ +#define TANKMEMDATAREGBASE 0x200 /* Tank memory data registers base */ +#define TANKMEMDATAREG_MASK 0x000fffff /* 20 bit tank audio data field */ + +/* Combined address field and memory opcode or flag field. 160 locations, last 32 are external */ +#define TANKMEMADDRREGBASE 0x300 /* Tank memory address registers base */ +#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ +#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ +#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ +#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ +#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ + +#define MICROCODEBASE 0x400 /* Microcode data base address */ + +/* Each DSP microcode instruction is mapped into 2 doublewords */ +/* NOTE: When writing, always write the LO doubleword first. Reads can be in either order. */ +#define LOWORD_OPX_MASK 0x000ffc00 /* Instruction operand X */ +#define LOWORD_OPY_MASK 0x000003ff /* Instruction operand Y */ +#define HIWORD_OPCODE_MASK 0x00f00000 /* Instruction opcode */ +#define HIWORD_RESULT_MASK 0x000ffc00 /* Instruction result */ +#define HIWORD_OPA_MASK 0x000003ff /* Instruction operand A */ + +#endif /* _8010_H */ diff -Nru a/sound/oss/emu10k1/Makefile b/sound/oss/emu10k1/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/Makefile Tue Feb 19 18:08:59 2002 @@ -0,0 +1,23 @@ +# Makefile for Creative Labs EMU10K1 +# +# 12 Apr 2000 Rui Sousa + +O_TARGET := emu10k1.o + +obj-y := audio.o cardmi.o cardmo.o cardwi.o cardwo.o ecard.o \ + efxmgr.o emuadxmg.o hwaccess.o irqmgr.o main.o midi.o \ + mixer.o passthrough.o recmgr.o timer.o voicemgr.o +obj-m := $(O_TARGET) + +ifdef DEBUG + EXTRA_CFLAGS += -DEMU10K1_DEBUG +endif + +ifdef CONFIG_MIDI_EMU10K1 + EXTRA_CFLAGS += -DEMU10K1_SEQUENCER +endif + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s diff -Nru a/sound/oss/emu10k1/audio.c b/sound/oss/emu10k1/audio.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/audio.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,1570 @@ +/* + ********************************************************************** + * audio.c -- /dev/dsp interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up types/leaks + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "hwaccess.h" +#include "cardwo.h" +#include "cardwi.h" +#include "recmgr.h" +#include "irqmgr.h" +#include "audio.h" +#include "8010.h" + +static void calculate_ofrag(struct woinst *); +static void calculate_ifrag(struct wiinst *); + +/* Audio file operations */ +static ssize_t emu10k1_audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct wiinst *wiinst = wave_dev->wiinst; + ssize_t ret = 0; + unsigned long flags; + + DPD(3, "emu10k1_audio_read(), buffer=%p, count=%d\n", buffer, (u32) count); + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->mmapped) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -ENXIO; + } + + if (wiinst->state == WAVE_STATE_CLOSED) { + calculate_ifrag(wiinst); + + while (emu10k1_wavein_open(wave_dev) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + interruptible_sleep_on(&wave_dev->card->open_wait); + + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&wiinst->lock, flags); + } + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + + while (count > 0) { + u32 bytestocopy; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (!(wiinst->state & WAVE_STATE_STARTED) + && (wave_dev->enablebits & PCM_ENABLE_INPUT)) + emu10k1_wavein_start(wave_dev); + + emu10k1_wavein_update(wave_dev->card, wiinst); + emu10k1_wavein_getxfersize(wiinst, &bytestocopy); + + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPD(3, "bytestocopy --> %d\n", bytestocopy); + + if ((bytestocopy >= wiinst->buffer.fragment_size) + || (bytestocopy >= count)) { + bytestocopy = min_t(u32, bytestocopy, count); + + emu10k1_wavein_xferdata(wiinst, (u8 *) buffer, &bytestocopy); + + count -= bytestocopy; + buffer += bytestocopy; + ret += bytestocopy; + } + + if (count > 0) { + if ((file->f_flags & O_NONBLOCK) + || (!(wave_dev->enablebits & PCM_ENABLE_INPUT))) + return (ret ? ret : -EAGAIN); + + interruptible_sleep_on(&wiinst->wait_queue); + + if (signal_pending(current)) + return (ret ? ret : -ERESTARTSYS); + + } + } + + DPD(3, "bytes copied -> %d\n", (u32) ret); + + return ret; +} + +static ssize_t emu10k1_audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct woinst *woinst = wave_dev->woinst; + ssize_t ret; + unsigned long flags; + + DPD(3, "emu10k1_audio_write(), buffer=%p, count=%d\n", buffer, (u32) count); + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->mmapped) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -ENXIO; + } + + if (woinst->format.passthrough) { + int r; + + woinst->buffer.ossfragshift = PT_BLOCKSIZE_LOG2; + woinst->buffer.numfrags = PT_BLOCKCOUNT; + calculate_ofrag(woinst); + + r = emu10k1_pt_write(file, buffer, count); + spin_unlock_irqrestore(&woinst->lock, flags); + return r; + } + + if (woinst->state == WAVE_STATE_CLOSED) { + calculate_ofrag(woinst); + + while (emu10k1_waveout_open(wave_dev) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + interruptible_sleep_on(&wave_dev->card->open_wait); + + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&woinst->lock, flags); + } + } + + spin_unlock_irqrestore(&woinst->lock, flags); + + ret = 0; + if (count % woinst->format.bytespersample) + return -EINVAL; + + count /= woinst->num_voices; + + while (count > 0) { + u32 bytestocopy; + + spin_lock_irqsave(&woinst->lock, flags); + emu10k1_waveout_update(woinst); + emu10k1_waveout_getxfersize(woinst, &bytestocopy); + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(3, "bytestocopy --> %d\n", bytestocopy); + + if ((bytestocopy >= woinst->buffer.fragment_size) + || (bytestocopy >= count)) { + + bytestocopy = min_t(u32, bytestocopy, count); + + emu10k1_waveout_xferdata(woinst, (u8 *) buffer, &bytestocopy); + + count -= bytestocopy; + buffer += bytestocopy * woinst->num_voices; + ret += bytestocopy * woinst->num_voices; + + spin_lock_irqsave(&woinst->lock, flags); + woinst->total_copied += bytestocopy; + + if (!(woinst->state & WAVE_STATE_STARTED) + && (wave_dev->enablebits & PCM_ENABLE_OUTPUT) + && (woinst->total_copied >= woinst->buffer.fragment_size)) + emu10k1_waveout_start(wave_dev); + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (count > 0) { + if ((file->f_flags & O_NONBLOCK) + || (!(wave_dev->enablebits & PCM_ENABLE_OUTPUT))) + return (ret ? ret : -EAGAIN); + + interruptible_sleep_on(&woinst->wait_queue); + + if (signal_pending(current)) + return (ret ? ret : -ERESTARTSYS); + } + } + + DPD(3, "bytes copied -> %d\n", (u32) ret); + + return ret; +} + +static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct woinst *woinst = NULL; + struct wiinst *wiinst = NULL; + int val = 0; + u32 bytestocopy; + unsigned long flags; + + DPF(4, "emu10k1_audio_ioctl()\n"); + + if (file->f_mode & FMODE_WRITE) + woinst = wave_dev->woinst; + + if (file->f_mode & FMODE_READ) + wiinst = wave_dev->wiinst; + + switch (cmd) { + case OSS_GETVERSION: + DPF(2, "OSS_GETVERSION:\n"); + return put_user(SOUND_VERSION, (int *) arg); + + case SNDCTL_DSP_RESET: + DPF(2, "SNDCTL_DSP_RESET:\n"); + wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT; + + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->state & WAVE_STATE_OPEN) { + emu10k1_waveout_close(wave_dev); + } + + woinst->mmapped = 0; + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->blocks = 0; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->state & WAVE_STATE_OPEN) { + emu10k1_wavein_close(wave_dev); + } + + wiinst->mmapped = 0; + wiinst->total_recorded = 0; + wiinst->blocks = 0; + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + break; + + case SNDCTL_DSP_SYNC: + DPF(2, "SNDCTL_DSP_SYNC:\n"); + + if (file->f_mode & FMODE_WRITE) { + + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->state & WAVE_STATE_OPEN) { + + if (woinst->state & WAVE_STATE_STARTED) + while ((woinst->total_played < woinst->total_copied) + && !signal_pending(current)) { + spin_unlock_irqrestore(&woinst->lock, flags); + interruptible_sleep_on(&woinst->wait_queue); + spin_lock_irqsave(&woinst->lock, flags); + } + emu10k1_waveout_close(wave_dev); + } + + woinst->mmapped = 0; + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->blocks = 0; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->state & WAVE_STATE_OPEN) { + emu10k1_wavein_close(wave_dev); + } + + wiinst->mmapped = 0; + wiinst->total_recorded = 0; + wiinst->blocks = 0; + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + break; + + case SNDCTL_DSP_SETDUPLEX: + DPF(2, "SNDCTL_DSP_SETDUPLEX:\n"); + break; + + case SNDCTL_DSP_GETCAPS: + DPF(2, "SNDCTL_DSP_GETCAPS:\n"); + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_COPROC, (int *) arg); + + case SNDCTL_DSP_SPEED: + DPF(2, "SNDCTL_DSP_SPEED:\n"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + DPD(2, "val is %d\n", val); + + if (val > 0) { + if (file->f_mode & FMODE_READ) { + struct wave_format format; + + spin_lock_irqsave(&wiinst->lock, flags); + + format = wiinst->format; + format.samplingrate = val; + + if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -EINVAL; + } + + val = wiinst->format.samplingrate; + + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPD(2, "set recording sampling rate -> %d\n", val); + } + + if (file->f_mode & FMODE_WRITE) { + struct wave_format format; + + spin_lock_irqsave(&woinst->lock, flags); + + format = woinst->format; + format.samplingrate = val; + + if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + val = woinst->format.samplingrate; + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(2, "set playback sampling rate -> %d\n", val); + } + + return put_user(val, (int *) arg); + } else { + if (file->f_mode & FMODE_READ) + val = wiinst->format.samplingrate; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.samplingrate; + + return put_user(val, (int *) arg); + } + break; + + case SNDCTL_DSP_STEREO: + DPF(2, "SNDCTL_DSP_STEREO:\n"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + DPD(2, " val is %d\n", val); + + if (file->f_mode & FMODE_READ) { + struct wave_format format; + + spin_lock_irqsave(&wiinst->lock, flags); + + format = wiinst->format; + format.channels = val ? 2 : 1; + + if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -EINVAL; + } + + val = wiinst->format.channels - 1; + + spin_unlock_irqrestore(&wiinst->lock, flags); + DPD(2, "set recording stereo -> %d\n", val); + } + + if (file->f_mode & FMODE_WRITE) { + struct wave_format format; + + spin_lock_irqsave(&woinst->lock, flags); + + format = woinst->format; + format.channels = val ? 2 : 1; + + if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + val = woinst->format.channels - 1; + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(2, "set playback stereo -> %d\n", val); + } + + return put_user(val, (int *) arg); + + break; + + case SNDCTL_DSP_CHANNELS: + DPF(2, "SNDCTL_DSP_CHANNELS:\n"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + DPD(2, " val is %d\n", val); + + if (val > 0) { + if (file->f_mode & FMODE_READ) { + struct wave_format format; + + spin_lock_irqsave(&wiinst->lock, flags); + + format = wiinst->format; + format.channels = val; + + if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -EINVAL; + } + val = wiinst->format.channels; + + spin_unlock_irqrestore(&wiinst->lock, flags); + DPD(2, "set recording number of channels -> %d\n", val); + } + + if (file->f_mode & FMODE_WRITE) { + struct wave_format format; + + spin_lock_irqsave(&woinst->lock, flags); + + format = woinst->format; + format.channels = val; + + if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + val = woinst->format.channels; + + spin_unlock_irqrestore(&woinst->lock, flags); + DPD(2, "set playback number of channels -> %d\n", val); + } + + return put_user(val, (int *) arg); + } else { + if (file->f_mode & FMODE_READ) + val = wiinst->format.channels; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.channels; + + return put_user(val, (int *) arg); + } + break; + + case SNDCTL_DSP_GETFMTS: + DPF(2, "SNDCTL_DSP_GETFMTS:\n"); + + if (file->f_mode & FMODE_READ) + val = AFMT_S16_LE; + else if (file->f_mode & FMODE_WRITE) { + val = AFMT_S16_LE | AFMT_U8; + if (emu10k1_find_control_gpr(&wave_dev->card->mgr, + wave_dev->card->pt.patch_name, + wave_dev->card->pt.enable_gpr_name) >= 0) + val |= AFMT_AC3; + } + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETFMT: /* Same as SNDCTL_DSP_SAMPLESIZE */ + DPF(2, "SNDCTL_DSP_SETFMT:\n"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + DPD(2, " val is %d\n", val); + + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + struct wave_format format; + + spin_lock_irqsave(&wiinst->lock, flags); + + format = wiinst->format; + format.id = val; + + if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -EINVAL; + } + + val = wiinst->format.id; + + spin_unlock_irqrestore(&wiinst->lock, flags); + DPD(2, "set recording format -> %d\n", val); + } + + if (file->f_mode & FMODE_WRITE) { + struct wave_format format; + + spin_lock_irqsave(&woinst->lock, flags); + + format = woinst->format; + format.id = val; + + if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + val = woinst->format.id; + + spin_unlock_irqrestore(&woinst->lock, flags); + DPD(2, "set playback format -> %d\n", val); + } + + return put_user(val, (int *) arg); + } else { + if (file->f_mode & FMODE_READ) + val = wiinst->format.id; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.id; + + return put_user(val, (int *) arg); + } + break; + + case SOUND_PCM_READ_BITS: + + if (file->f_mode & FMODE_READ) + val = wiinst->format.bitsperchannel; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.bitsperchannel; + + return put_user(val, (int *) arg); + + case SOUND_PCM_READ_RATE: + + if (file->f_mode & FMODE_READ) + val = wiinst->format.samplingrate; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.samplingrate; + + return put_user(val, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: + + if (file->f_mode & FMODE_READ) + val = wiinst->format.channels; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.channels; + + return put_user(val, (int *) arg); + + case SOUND_PCM_WRITE_FILTER: + DPF(2, "SOUND_PCM_WRITE_FILTER: not implemented\n"); + break; + + case SOUND_PCM_READ_FILTER: + DPF(2, "SOUND_PCM_READ_FILTER: not implemented\n"); + break; + + case SNDCTL_DSP_SETSYNCRO: + DPF(2, "SNDCTL_DSP_SETSYNCRO: not implemented\n"); + break; + + case SNDCTL_DSP_GETTRIGGER: + DPF(2, "SNDCTL_DSP_GETTRIGGER:\n"); + + if (file->f_mode & FMODE_WRITE && (wave_dev->enablebits & PCM_ENABLE_OUTPUT)) + val |= PCM_ENABLE_OUTPUT; + + if (file->f_mode & FMODE_READ && (wave_dev->enablebits & PCM_ENABLE_INPUT)) + val |= PCM_ENABLE_INPUT; + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + DPF(2, "SNDCTL_DSP_SETTRIGGER:\n"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + if (val & PCM_ENABLE_OUTPUT) { + wave_dev->enablebits |= PCM_ENABLE_OUTPUT; + if (woinst->state & WAVE_STATE_OPEN) + emu10k1_waveout_start(wave_dev); + } else { + wave_dev->enablebits &= ~PCM_ENABLE_OUTPUT; + if (woinst->state & WAVE_STATE_STARTED) + emu10k1_waveout_stop(wave_dev); + } + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + if (val & PCM_ENABLE_INPUT) { + wave_dev->enablebits |= PCM_ENABLE_INPUT; + if (wiinst->state & WAVE_STATE_OPEN) + emu10k1_wavein_start(wave_dev); + } else { + wave_dev->enablebits &= ~PCM_ENABLE_INPUT; + if (wiinst->state & WAVE_STATE_STARTED) + emu10k1_wavein_stop(wave_dev); + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + } + break; + + case SNDCTL_DSP_GETOSPACE: + { + audio_buf_info info; + + DPF(4, "SNDCTL_DSP_GETOSPACE:\n"); + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->state & WAVE_STATE_OPEN) { + emu10k1_waveout_update(woinst); + emu10k1_waveout_getxfersize(woinst, &bytestocopy); + info.bytes = bytestocopy; + } else { + calculate_ofrag(woinst); + info.bytes = woinst->buffer.size; + } + spin_unlock_irqrestore(&woinst->lock, flags); + + info.bytes *= woinst->num_voices; + info.fragsize = woinst->buffer.fragment_size * woinst->num_voices; + info.fragstotal = woinst->buffer.numfrags * woinst->num_voices; + info.fragments = info.bytes / info.fragsize; + + if (copy_to_user((int *) arg, &info, sizeof(info))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info info; + + DPF(4, "SNDCTL_DSP_GETISPACE:\n"); + + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&wiinst->lock, flags); + if (wiinst->state & WAVE_STATE_OPEN) { + emu10k1_wavein_update(wave_dev->card, wiinst); + emu10k1_wavein_getxfersize(wiinst, &bytestocopy); + info.bytes = bytestocopy; + } else { + calculate_ifrag(wiinst); + info.bytes = 0; + } + spin_unlock_irqrestore(&wiinst->lock, flags); + + info.fragstotal = wiinst->buffer.numfrags; + info.fragments = info.bytes / wiinst->buffer.fragment_size; + info.fragsize = wiinst->buffer.fragment_size; + + if (copy_to_user((int *) arg, &info, sizeof(info))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_NONBLOCK: + DPF(2, "SNDCTL_DSP_NONBLOCK:\n"); + + file->f_flags |= O_NONBLOCK; + break; + + case SNDCTL_DSP_GETODELAY: + DPF(4, "SNDCTL_DSP_GETODELAY:\n"); + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&woinst->lock, flags); + if (woinst->state & WAVE_STATE_OPEN) { + emu10k1_waveout_update(woinst); + emu10k1_waveout_getxfersize(woinst, &bytestocopy); + val = woinst->buffer.size - bytestocopy; + } else + val = 0; + + val *= woinst->num_voices; + spin_unlock_irqrestore(&woinst->lock, flags); + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETIPTR: + { + count_info cinfo; + + DPF(4, "SNDCTL_DSP_GETIPTR: \n"); + + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->state & WAVE_STATE_OPEN) { + emu10k1_wavein_update(wave_dev->card, wiinst); + cinfo.ptr = wiinst->buffer.hw_pos; + cinfo.bytes = cinfo.ptr + wiinst->total_recorded - wiinst->total_recorded % wiinst->buffer.size; + cinfo.blocks = cinfo.bytes / wiinst->buffer.fragment_size - wiinst->blocks; + wiinst->blocks = cinfo.bytes / wiinst->buffer.fragment_size; + } else { + cinfo.ptr = 0; + cinfo.bytes = 0; + cinfo.blocks = 0; + } + + if(wiinst->mmapped) + wiinst->buffer.bytestocopy %= wiinst->buffer.fragment_size; + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_GETOPTR: + { + count_info cinfo; + + DPF(4, "SNDCTL_DSP_GETOPTR:\n"); + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->state & WAVE_STATE_OPEN || + (woinst->format.passthrough && wave_dev->card->pt.state)) { + int num_fragments; + if (woinst->format.passthrough) { + emu10k1_pt_waveout_update(wave_dev); + cinfo.bytes = woinst->total_played; + } else { + emu10k1_waveout_update(woinst); + cinfo.bytes = woinst->total_played; + } + cinfo.ptr = woinst->buffer.hw_pos; + num_fragments = cinfo.bytes / woinst->buffer.fragment_size; + cinfo.blocks = num_fragments - woinst->blocks; + woinst->blocks = num_fragments; + + cinfo.bytes *= woinst->num_voices; + cinfo.ptr *= woinst->num_voices; + } else { + cinfo.ptr = 0; + cinfo.bytes = 0; + cinfo.blocks = 0; + } + + if (woinst->mmapped) + woinst->buffer.free_bytes %= woinst->buffer.fragment_size; + + spin_unlock_irqrestore(&woinst->lock, flags); + + if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_GETBLKSIZE: + DPF(2, "SNDCTL_DSP_GETBLKSIZE:\n"); + + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + calculate_ofrag(woinst); + val = woinst->buffer.fragment_size * woinst->num_voices; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + calculate_ifrag(wiinst); + val = wiinst->buffer.fragment_size; + + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + return put_user(val, (int *) arg); + + break; + + case SNDCTL_DSP_POST: + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + if (!(woinst->state & WAVE_STATE_STARTED) + && (wave_dev->enablebits & PCM_ENABLE_OUTPUT) + && (woinst->total_copied > 0)) + emu10k1_waveout_start(wave_dev); + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + break; + + case SNDCTL_DSP_SUBDIVIDE: + DPF(2, "SNDCTL_DSP_SUBDIVIDE: not implemented\n"); + break; + + case SNDCTL_DSP_SETFRAGMENT: + DPF(2, "SNDCTL_DSP_SETFRAGMENT:\n"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + DPD(2, "val is %#x\n", val); + + if (val == 0) + return -EIO; + + if (file->f_mode & FMODE_WRITE) { + /* digital pass-through fragment count and size are fixed values */ + if (woinst->state & WAVE_STATE_OPEN || woinst->format.passthrough) + return -EINVAL; /* too late to change */ + + woinst->buffer.ossfragshift = val & 0xffff; + woinst->buffer.numfrags = (val >> 16) & 0xffff; + } + + if (file->f_mode & FMODE_READ) { + if (wiinst->state & WAVE_STATE_OPEN) + return -EINVAL; /* too late to change */ + + wiinst->buffer.ossfragshift = val & 0xffff; + wiinst->buffer.numfrags = (val >> 16) & 0xffff; + } + + break; + + case SNDCTL_COPR_LOAD: + { + copr_buffer *buf; + u32 i; + + DPF(4, "SNDCTL_COPR_LOAD:\n"); + + buf = kmalloc(sizeof(copr_buffer), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, (copr_buffer *) arg, sizeof(copr_buffer))) { + kfree (buf); + return -EFAULT; + } + + if ((buf->command != CMD_READ) && (buf->command != CMD_WRITE)) { + kfree (buf); + return -EINVAL; + } +#ifdef DBGEMU + if ( (buf->offs < 0) || (buf->offs + buf->len > 0x800) || (buf->len > 1000)) { +#else + if ( ((buf->offs < 0x100 ) || (buf->offs + buf->len > 0x800) || (buf->len > 1000)) + && !( ( buf->offs == DBG) && (buf->len ==1) )){ +#endif + kfree(buf); + return -EINVAL; + } + + if (buf->command == CMD_READ) { + for (i = 0; i < buf->len; i++) + ((u32 *) buf->data)[i] = sblive_readptr(wave_dev->card, buf->offs + i, 0); + + if (copy_to_user((copr_buffer *) arg, buf, sizeof(copr_buffer))) { + kfree(buf); + return -EFAULT; + } + } else { + for (i = 0; i < buf->len; i++) + sblive_writeptr(wave_dev->card, buf->offs + i, 0, ((u32 *) buf->data)[i]); + } + + kfree (buf); + break; + } + + default: /* Default is unrecognized command */ + DPD(2, "default: %#x\n", cmd); + return -EINVAL; + } + return 0; +} + +static struct page *emu10k1_mm_nopage (struct vm_area_struct * vma, unsigned long address, int write_access) +{ + struct emu10k1_wavedevice *wave_dev = vma->vm_private_data; + struct woinst *woinst = wave_dev->woinst; + struct wiinst *wiinst = wave_dev->wiinst; + struct page *dmapage; + unsigned long pgoff; + int rd, wr; + + DPF(4, "emu10k1_mm_nopage()\n"); + DPD(4, "addr: %#lx\n", address); + + if (address > vma->vm_end) { + DPF(2, "EXIT, returning NOPAGE_SIGBUS\n"); + return NOPAGE_SIGBUS; /* Disallow mremap */ + } + + pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT); + if (woinst != NULL) + wr = woinst->mmapped; + else + wr = 0; + + if (wiinst != NULL) + rd = wiinst->mmapped; + else + rd = 0; + + /* if full-duplex (read+write) and we have two sets of bufs, + * then the playback buffers come first, sez soundcard.c */ + if (wr) { + if (pgoff >= woinst->buffer.pages) { + pgoff -= woinst->buffer.pages; + dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE); + } else + dmapage = virt_to_page (woinst->buffer.mem[0].addr[pgoff]); + } else { + dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE); + } + + get_page (dmapage); + + DPD(4, "page: %#lx\n", dmapage); + return dmapage; +} + +struct vm_operations_struct emu10k1_mm_ops = { + nopage: emu10k1_mm_nopage, +}; + +static int emu10k1_audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + unsigned long max_pages, n_pages, pgoffset; + struct woinst *woinst = NULL; + struct wiinst *wiinst = NULL; + unsigned long flags; + + DPF(2, "emu10k1_audio_mmap()\n"); + + max_pages = 0; + if (vma->vm_flags & VM_WRITE) { + woinst = wave_dev->woinst; + + spin_lock_irqsave(&woinst->lock, flags); + + /* No m'mapping possible for multichannel */ + if (woinst->num_voices > 1) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + if (woinst->state == WAVE_STATE_CLOSED) { + calculate_ofrag(woinst); + + if (emu10k1_waveout_open(wave_dev) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + ERROR(); + return -EINVAL; + } + } + + woinst->mmapped = 1; + max_pages += woinst->buffer.pages; + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (vma->vm_flags & VM_READ) { + wiinst = wave_dev->wiinst; + + spin_lock_irqsave(&wiinst->lock, flags); + if (wiinst->state == WAVE_STATE_CLOSED) { + calculate_ifrag(wiinst); + + if (emu10k1_wavein_open(wave_dev) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + ERROR(); + return -EINVAL; + } + } + + wiinst->mmapped = 1; + max_pages += wiinst->buffer.pages; + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + n_pages = ((vma->vm_end - vma->vm_start) + PAGE_SIZE - 1) >> PAGE_SHIFT; + pgoffset = vma->vm_pgoff; + + DPD(3, "vma_start: %#lx, vma_end: %#lx, vma_offset: %d\n", vma->vm_start, vma->vm_end, pgoffset); + DPD(3, "n_pages: %d, max_pages: %d\n", n_pages, max_pages); + + if (pgoffset + n_pages > max_pages) + return -EINVAL; + + vma->vm_flags |= VM_RESERVED; + vma->vm_ops = &emu10k1_mm_ops; + vma->vm_private_data = wave_dev; + + return 0; +} + +static int emu10k1_audio_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct emu10k1_card *card = NULL; + struct list_head *entry; + struct emu10k1_wavedevice *wave_dev; + + DPF(2, "emu10k1_audio_open()\n"); + + /* Check for correct device to open */ + + list_for_each(entry, &emu10k1_devs) { + card = list_entry(entry, struct emu10k1_card, list); + + if (!((card->audio_dev ^ minor) & ~0xf) || !((card->audio_dev1 ^ minor) & ~0xf)) + goto match; + } + + return -ENODEV; + +match: + + wave_dev = (struct emu10k1_wavedevice *) kmalloc(sizeof(struct emu10k1_wavedevice), GFP_KERNEL); + + if (wave_dev == NULL) { + ERROR(); + return -ENOMEM; + } + + wave_dev->card = card; + wave_dev->wiinst = NULL; + wave_dev->woinst = NULL; + wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT; /* Default */ + + if (file->f_mode & FMODE_READ) { + /* Recording */ + struct wiinst *wiinst; + + if ((wiinst = (struct wiinst *) kmalloc(sizeof(struct wiinst), GFP_KERNEL)) == NULL) { + ERROR(); + return -ENODEV; + } + + wiinst->recsrc = card->wavein.recsrc; + wiinst->fxwc = card->wavein.fxwc; + + switch (wiinst->recsrc) { + case WAVERECORD_AC97: + wiinst->format.id = AFMT_S16_LE; + wiinst->format.samplingrate = 8000; + wiinst->format.bitsperchannel = 16; + wiinst->format.channels = 1; + break; + case WAVERECORD_MIC: + wiinst->format.id = AFMT_S16_LE; + wiinst->format.samplingrate = 8000; + wiinst->format.bitsperchannel = 16; + wiinst->format.channels = 1; + break; + case WAVERECORD_FX: + wiinst->format.id = AFMT_S16_LE; + wiinst->format.samplingrate = 48000; + wiinst->format.bitsperchannel = 16; + wiinst->format.channels = hweight32(wiinst->fxwc); + break; + default: + BUG(); + break; + } + + wiinst->state = WAVE_STATE_CLOSED; + + wiinst->buffer.ossfragshift = 0; + wiinst->buffer.fragment_size = 0; + wiinst->buffer.numfrags = 0; + + init_waitqueue_head(&wiinst->wait_queue); + + wiinst->mmapped = 0; + wiinst->total_recorded = 0; + wiinst->blocks = 0; + wiinst->lock = SPIN_LOCK_UNLOCKED; + tasklet_init(&wiinst->timer.tasklet, emu10k1_wavein_bh, (unsigned long) wave_dev); + wave_dev->wiinst = wiinst; + emu10k1_wavein_setformat(wave_dev, &wiinst->format); + } + + if (file->f_mode & FMODE_WRITE) { + struct woinst *woinst; + int i; + + if ((woinst = (struct woinst *) kmalloc(sizeof(struct woinst), GFP_KERNEL)) == NULL) { + ERROR(); + return -ENODEV; + } + + if (wave_dev->wiinst != NULL) { + woinst->format = wave_dev->wiinst->format; + } else { + woinst->format.id = AFMT_U8; + woinst->format.samplingrate = 8000; + woinst->format.bitsperchannel = 8; + woinst->format.channels = 1; + } + + woinst->state = WAVE_STATE_CLOSED; + + woinst->buffer.fragment_size = 0; + woinst->buffer.ossfragshift = 0; + woinst->buffer.numfrags = 0; + woinst->device = (card->audio_dev1 == minor); + woinst->timer.state = TIMER_STATE_UNINSTALLED; + woinst->num_voices = 1; + for (i = 0; i < WAVEOUT_MAXVOICES; i++) { + woinst->voice[i].usage = VOICE_USAGE_FREE; + woinst->buffer.mem[i].emupageindex = -1; + } + + init_waitqueue_head(&woinst->wait_queue); + + woinst->mmapped = 0; + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->blocks = 0; + woinst->lock = SPIN_LOCK_UNLOCKED; + tasklet_init(&woinst->timer.tasklet, emu10k1_waveout_bh, (unsigned long) wave_dev); + wave_dev->woinst = woinst; + emu10k1_waveout_setformat(wave_dev, &woinst->format); + } + + file->private_data = (void *) wave_dev; + + return 0; +} + +static int emu10k1_audio_release(struct inode *inode, struct file *file) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct emu10k1_card *card; + unsigned long flags; + + card = wave_dev->card; + + DPF(2, "emu10k1_audio_release()\n"); + + if (file->f_mode & FMODE_WRITE) { + struct woinst *woinst = wave_dev->woinst; + + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->format.passthrough && card->pt.state != PT_STATE_INACTIVE) { + spin_lock(&card->pt.lock); + emu10k1_pt_stop(card); + spin_unlock(&card->pt.lock); + } + if (woinst->state & WAVE_STATE_OPEN) { + if (woinst->state & WAVE_STATE_STARTED) { + if (!(file->f_flags & O_NONBLOCK)) { + while (!signal_pending(current) + && (woinst->total_played < woinst->total_copied)) { + DPF(4, "Buffer hasn't been totally played, sleep....\n"); + spin_unlock_irqrestore(&woinst->lock, flags); + interruptible_sleep_on(&woinst->wait_queue); + spin_lock_irqsave(&woinst->lock, flags); + } + } + } + emu10k1_waveout_close(wave_dev); + } + + spin_unlock_irqrestore(&woinst->lock, flags); + /* remove the tasklet */ + tasklet_kill(&woinst->timer.tasklet); + kfree(wave_dev->woinst); + } + + if (file->f_mode & FMODE_READ) { + struct wiinst *wiinst = wave_dev->wiinst; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->state & WAVE_STATE_OPEN) { + emu10k1_wavein_close(wave_dev); + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + tasklet_kill(&wiinst->timer.tasklet); + kfree(wave_dev->wiinst); + } + + kfree(wave_dev); + + if (waitqueue_active(&card->open_wait)) + wake_up_interruptible(&card->open_wait); + + return 0; +} + +/* FIXME sort out poll() + mmap() */ +static unsigned int emu10k1_audio_poll(struct file *file, struct poll_table_struct *wait) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct woinst *woinst = wave_dev->woinst; + struct wiinst *wiinst = wave_dev->wiinst; + unsigned int mask = 0; + u32 bytestocopy; + unsigned long flags; + + DPF(4, "emu10k1_audio_poll()\n"); + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &woinst->wait_queue, wait); + + if (file->f_mode & FMODE_READ) + poll_wait(file, &wiinst->wait_queue, wait); + + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->state & WAVE_STATE_OPEN) { + emu10k1_waveout_update(woinst); + emu10k1_waveout_getxfersize(woinst, &bytestocopy); + + if (bytestocopy >= woinst->buffer.fragment_size) + mask |= POLLOUT | POLLWRNORM; + } else + mask |= POLLOUT | POLLWRNORM; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->state == WAVE_STATE_CLOSED) { + calculate_ifrag(wiinst); + if (emu10k1_wavein_open(wave_dev) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return (mask |= POLLERR); + } + } + + if (!(wiinst->state & WAVE_STATE_STARTED)) { + wave_dev->enablebits |= PCM_ENABLE_INPUT; + emu10k1_wavein_start(wave_dev); + } + emu10k1_wavein_update(wave_dev->card, wiinst); + emu10k1_wavein_getxfersize(wiinst, &bytestocopy); + + if (bytestocopy >= wiinst->buffer.fragment_size) + mask |= POLLIN | POLLRDNORM; + + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + return mask; +} + +static void calculate_ofrag(struct woinst *woinst) +{ + struct waveout_buffer *buffer = &woinst->buffer; + u32 fragsize; + + if (buffer->fragment_size) + return; + + if (!buffer->ossfragshift) { + fragsize = (woinst->format.bytespervoicesample * woinst->format.samplingrate * WAVEOUT_DEFAULTFRAGLEN) / 1000 - 1; + + while (fragsize) { + fragsize >>= 1; + buffer->ossfragshift++; + } + } + + if (buffer->ossfragshift < WAVEOUT_MINFRAGSHIFT) + buffer->ossfragshift = WAVEOUT_MINFRAGSHIFT; + + buffer->fragment_size = 1 << buffer->ossfragshift; + + if (!buffer->numfrags) { + u32 numfrags; + + numfrags = (woinst->format.bytespervoicesample * woinst->format.samplingrate * WAVEOUT_DEFAULTBUFLEN) / + (buffer->fragment_size * 1000) - 1; + + buffer->numfrags = 1; + + while (numfrags) { + numfrags >>= 1; + buffer->numfrags <<= 1; + } + } + + if (buffer->numfrags < MINFRAGS) + buffer->numfrags = MINFRAGS; + + if (buffer->numfrags * buffer->fragment_size > WAVEOUT_MAXBUFSIZE) { + buffer->numfrags = WAVEOUT_MAXBUFSIZE / buffer->fragment_size; + + if (buffer->numfrags < MINFRAGS) { + buffer->numfrags = MINFRAGS; + buffer->fragment_size = WAVEOUT_MAXBUFSIZE / MINFRAGS; + } + + } else if (buffer->numfrags * buffer->fragment_size < WAVEOUT_MINBUFSIZE) + buffer->numfrags = WAVEOUT_MINBUFSIZE / buffer->fragment_size; + + buffer->size = buffer->fragment_size * buffer->numfrags; + buffer->pages = buffer->size / PAGE_SIZE + ((buffer->size % PAGE_SIZE) ? 1 : 0); + + DPD(2, " calculated playback fragment_size -> %d\n", buffer->fragment_size); + DPD(2, " calculated playback numfrags -> %d\n", buffer->numfrags); + + return; +} + +static void calculate_ifrag(struct wiinst *wiinst) +{ + struct wavein_buffer *buffer = &wiinst->buffer; + u32 fragsize, bufsize, size[4]; + int i, j; + + if (buffer->fragment_size) + return; + + if (!buffer->ossfragshift) { + fragsize = (wiinst->format.bytespersec * WAVEIN_DEFAULTFRAGLEN) / 1000 - 1; + + while (fragsize) { + fragsize >>= 1; + buffer->ossfragshift++; + } + } + + if (buffer->ossfragshift < WAVEIN_MINFRAGSHIFT) + buffer->ossfragshift = WAVEIN_MINFRAGSHIFT; + + buffer->fragment_size = 1 << buffer->ossfragshift; + + if (!buffer->numfrags) + buffer->numfrags = (wiinst->format.bytespersec * WAVEIN_DEFAULTBUFLEN) / (buffer->fragment_size * 1000) - 1; + + if (buffer->numfrags < MINFRAGS) + buffer->numfrags = MINFRAGS; + + if (buffer->numfrags * buffer->fragment_size > WAVEIN_MAXBUFSIZE) { + buffer->numfrags = WAVEIN_MAXBUFSIZE / buffer->fragment_size; + + if (buffer->numfrags < MINFRAGS) { + buffer->numfrags = MINFRAGS; + buffer->fragment_size = WAVEIN_MAXBUFSIZE / MINFRAGS; + } + } else if (buffer->numfrags * buffer->fragment_size < WAVEIN_MINBUFSIZE) + buffer->numfrags = WAVEIN_MINBUFSIZE / buffer->fragment_size; + + bufsize = buffer->fragment_size * buffer->numfrags; + + if (bufsize >= 0x10000) { + buffer->size = 0x10000; + buffer->sizeregval = 0x1f; + } else { + buffer->size = 0; + size[0] = 384; + size[1] = 448; + size[2] = 512; + size[3] = 640; + + for (i = 0; i < 8; i++) + for (j = 0; j < 4; j++) + if (bufsize >= size[j]) { + buffer->size = size[j]; + size[j] *= 2; + buffer->sizeregval = i * 4 + j + 1; + } else + goto exitloop; + exitloop: + if (buffer->size == 0) { + buffer->size = 384; + buffer->sizeregval = 0x01; + } + } + + buffer->numfrags = buffer->size / buffer->fragment_size; + buffer->pages = buffer->size / PAGE_SIZE + ((buffer->size % PAGE_SIZE) ? 1 : 0); + if (buffer->size % buffer->fragment_size) + BUG(); + + DPD(2, " calculated recording fragment_size -> %d\n", buffer->fragment_size); + DPD(2, " calculated recording numfrags -> %d\n", buffer->numfrags); + DPD(2, " buffer size register -> %#04x\n", buffer->sizeregval); + + return; +} + +void emu10k1_wavein_bh(unsigned long refdata) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) refdata; + struct wiinst *wiinst = wave_dev->wiinst; + u32 bytestocopy; + unsigned long flags; + + if (!wiinst) + return; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (!(wiinst->state & WAVE_STATE_STARTED)) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return; + } + + emu10k1_wavein_update(wave_dev->card, wiinst); + emu10k1_wavein_getxfersize(wiinst, &bytestocopy); + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (bytestocopy >= wiinst->buffer.fragment_size) { + if (waitqueue_active(&wiinst->wait_queue)) + wake_up_interruptible(&wiinst->wait_queue); + } else + DPD(3, "Not enough transfer size, %d\n", bytestocopy); + + return; +} + +void emu10k1_waveout_bh(unsigned long refdata) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) refdata; + struct woinst *woinst = wave_dev->woinst; + u32 bytestocopy; + unsigned long flags; + + if (!woinst) + return; + + spin_lock_irqsave(&woinst->lock, flags); + + if (!(woinst->state & WAVE_STATE_STARTED)) { + spin_unlock_irqrestore(&woinst->lock, flags); + return; + } + + emu10k1_waveout_update(woinst); + emu10k1_waveout_getxfersize(woinst, &bytestocopy); + + if (woinst->buffer.fill_silence) { + spin_unlock_irqrestore(&woinst->lock, flags); + emu10k1_waveout_fillsilence(woinst); + } else + spin_unlock_irqrestore(&woinst->lock, flags); + + if (bytestocopy >= woinst->buffer.fragment_size) { + if (waitqueue_active(&woinst->wait_queue)) + wake_up_interruptible(&woinst->wait_queue); + } else + DPD(3, "Not enough transfer size -> %d\n", bytestocopy); + + return; +} + +struct file_operations emu10k1_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: emu10k1_audio_read, + write: emu10k1_audio_write, + poll: emu10k1_audio_poll, + ioctl: emu10k1_audio_ioctl, + mmap: emu10k1_audio_mmap, + open: emu10k1_audio_open, + release: emu10k1_audio_release, +}; diff -Nru a/sound/oss/emu10k1/audio.h b/sound/oss/emu10k1/audio.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/audio.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,49 @@ +/* + ********************************************************************** + * audio.c -- /dev/dsp interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up types/leaks + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _AUDIO_H +#define _AUDIO_H + +#define MINFRAGS 2 /* _don't_ got bellow 2 */ + +struct emu10k1_wavedevice +{ + struct emu10k1_card *card; + struct wiinst *wiinst; + struct woinst *woinst; + u16 enablebits; +}; + +void emu10k1_waveout_bh(unsigned long); +void emu10k1_wavein_bh(unsigned long); + +#endif /* _AUDIO_H */ diff -Nru a/sound/oss/emu10k1/cardmi.c b/sound/oss/emu10k1/cardmi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/cardmi.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,817 @@ +/* + ********************************************************************** + * sblive_mi.c - MIDI UART input HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox clean up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include +#include + +#include "hwaccess.h" +#include "8010.h" +#include "cardmi.h" +#include "irqmgr.h" + +static struct { + int (*Fn) (struct emu10k1_mpuin *, u8); +} midistatefn[] = { + + { + sblive_miStateParse}, { + sblive_miState3Byte}, /* 0x8n, 0x9n, 0xAn, 0xBn, 0xEn */ + { + sblive_miState3ByteKey}, /* Byte 1 */ + { + sblive_miState3ByteVel}, /* Byte 2 */ + { + sblive_miState2Byte}, /* 0xCn, 0xDn */ + { + sblive_miState2ByteKey}, /* Byte 1 */ + { + sblive_miStateSysCommon2}, /* 0xF1 , 0xF3 */ + { + sblive_miStateSysCommon2Key}, /* 0xF1 , 0xF3, Byte 1 */ + { + sblive_miStateSysCommon3}, /* 0xF2 */ + { + sblive_miStateSysCommon3Key}, /* 0xF2 , Byte 1 */ + { + sblive_miStateSysCommon3Vel}, /* 0xF2 , Byte 2 */ + { + sblive_miStateSysExNorm}, /* 0xF0, 0xF7, Normal mode */ + { + sblive_miStateSysReal} /* 0xF4 - 0xF6 ,0xF8 - 0xFF */ +}; + +/* Installs the IRQ handler for the MPU in port */ + +/* and initialize parameters */ + +int emu10k1_mpuin_open(struct emu10k1_card *card, struct midi_openinfo *openinfo) +{ + struct emu10k1_mpuin *card_mpuin = card->mpuin; + + DPF(2, "emu10k1_mpuin_open\n"); + + if (!(card_mpuin->status & FLAGS_AVAILABLE)) + return -1; + + /* Copy open info and mark channel as in use */ + card_mpuin->openinfo = *openinfo; + card_mpuin->status &= ~FLAGS_AVAILABLE; /* clear */ + card_mpuin->status |= FLAGS_READY; /* set */ + card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ + card_mpuin->firstmidiq = NULL; + card_mpuin->lastmidiq = NULL; + card_mpuin->qhead = 0; + card_mpuin->qtail = 0; + + sblive_miStateInit(card_mpuin); + + emu10k1_mpu_reset(card); + emu10k1_mpu_acquire(card); + + return 0; +} + +int emu10k1_mpuin_close(struct emu10k1_card *card) +{ + struct emu10k1_mpuin *card_mpuin = card->mpuin; + + DPF(2, "emu10k1_mpuin_close()\n"); + + /* Check if there are pending input SysEx buffers */ + if (card_mpuin->firstmidiq != NULL) { + ERROR(); + return -1; + } + + /* Disable RX interrupt */ + emu10k1_irq_disable(card, INTE_MIDIRXENABLE); + + emu10k1_mpu_release(card); + + card_mpuin->status |= FLAGS_AVAILABLE; /* set */ + card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ + + return 0; +} + +/* Adds MIDI buffer to local queue list */ + +int emu10k1_mpuin_add_buffer(struct emu10k1_mpuin *card_mpuin, struct midi_hdr *midihdr) +{ + struct midi_queue *midiq; + unsigned long flags; + + DPF(2, "emu10k1_mpuin_add_buffer()\n"); + + /* Update MIDI buffer flags */ + midihdr->flags |= MIDIBUF_INQUEUE; /* set */ + midihdr->flags &= ~MIDIBUF_DONE; /* clear */ + + if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_ATOMIC)) == NULL) { + /* Message lost */ + return -1; + } + + midiq->next = NULL; + midiq->qtype = 1; + midiq->length = midihdr->bufferlength; + midiq->sizeLeft = midihdr->bufferlength; + midiq->midibyte = midihdr->data; + midiq->refdata = (unsigned long) midihdr; + + spin_lock_irqsave(&card_mpuin->lock, flags); + + if (card_mpuin->firstmidiq == NULL) { + card_mpuin->firstmidiq = midiq; + card_mpuin->lastmidiq = midiq; + } else { + (card_mpuin->lastmidiq)->next = midiq; + card_mpuin->lastmidiq = midiq; + } + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + return 0; +} + +/* First set the Time Stamp if MIDI IN has not started. */ + +/* Then enable RX Irq. */ + +int emu10k1_mpuin_start(struct emu10k1_card *card) +{ + struct emu10k1_mpuin *card_mpuin = card->mpuin; + u8 dummy; + + DPF(2, "emu10k1_mpuin_start()\n"); + + /* Set timestamp if not set */ + if (card_mpuin->status & FLAGS_MIDM_STARTED) { + DPF(2, "Time Stamp not changed\n"); + } else { + while (!emu10k1_mpu_read_data(card, &dummy)); + + card_mpuin->status |= FLAGS_MIDM_STARTED; /* set */ + + /* Set new time stamp */ + card_mpuin->timestart = (jiffies * 1000) / HZ; + DPD(2, "New Time Stamp = %d\n", card_mpuin->timestart); + + card_mpuin->qhead = 0; + card_mpuin->qtail = 0; + + emu10k1_irq_enable(card, INTE_MIDIRXENABLE); + } + + return 0; +} + +/* Disable the RX Irq. If a partial recorded buffer */ + +/* exist, send it up to IMIDI level. */ + +int emu10k1_mpuin_stop(struct emu10k1_card *card) +{ + struct emu10k1_mpuin *card_mpuin = card->mpuin; + struct midi_queue *midiq; + unsigned long flags; + + DPF(2, "emu10k1_mpuin_stop()\n"); + + emu10k1_irq_disable(card, INTE_MIDIRXENABLE); + + card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ + + if (card_mpuin->firstmidiq) { + spin_lock_irqsave(&card_mpuin->lock, flags); + + midiq = card_mpuin->firstmidiq; + if (midiq != NULL) { + if (midiq->sizeLeft == midiq->length) + midiq = NULL; + else { + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + } + } + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + if (midiq) { + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0); + kfree(midiq); + } + } + + return 0; +} + +/* Disable the RX Irq. If any buffer */ + +/* exist, send it up to IMIDI level. */ +int emu10k1_mpuin_reset(struct emu10k1_card *card) +{ + struct emu10k1_mpuin *card_mpuin = card->mpuin; + struct midi_queue *midiq; + + DPF(2, "emu10k1_mpuin_reset()\n"); + + emu10k1_irq_disable(card, INTE_MIDIRXENABLE); + + while (card_mpuin->firstmidiq) { + midiq = card_mpuin->firstmidiq; + card_mpuin->firstmidiq = midiq->next; + + if (midiq->sizeLeft == midiq->length) + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0); + else + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0); + + kfree(midiq); + } + + card_mpuin->lastmidiq = NULL; + card_mpuin->status &= ~FLAGS_MIDM_STARTED; + + return 0; +} + +/* Passes the message with the data back to the client */ + +/* via IRQ & DPC callbacks to Ring 3 */ +int emu10k1_mpuin_callback(struct emu10k1_mpuin *card_mpuin, u32 msg, unsigned long data, u32 bytesvalid) +{ + unsigned long timein; + struct midi_queue *midiq; + unsigned long callback_msg[3]; + struct midi_hdr *midihdr; + + /* Called during ISR. The data & code touched are: + * 1. card_mpuin + * 2. The function to be called + */ + + timein = card_mpuin->timein; + if (card_mpuin->timestart <= timein) + callback_msg[0] = timein - card_mpuin->timestart; + else + callback_msg[0] = (~0x0L - card_mpuin->timestart) + timein; + + if (msg == ICARDMIDI_INDATA || msg == ICARDMIDI_INDATAERROR) { + callback_msg[1] = data; + callback_msg[2] = bytesvalid; + DPD(2, "emu10k1_mpuin_callback: midimsg = %#lx\n", data); + } else { + midiq = (struct midi_queue *) data; + midihdr = (struct midi_hdr *) midiq->refdata; + + callback_msg[1] = midiq->length - midiq->sizeLeft; + callback_msg[2] = midiq->refdata; + midihdr->flags &= ~MIDIBUF_INQUEUE; + midihdr->flags |= MIDIBUF_DONE; + + midihdr->bytesrecorded = midiq->length - midiq->sizeLeft; + } + + /* Notify client that Sysex buffer has been sent */ + emu10k1_midi_callback(msg, card_mpuin->openinfo.refdata, callback_msg); + + return 0; +} + +void emu10k1_mpuin_bh(unsigned long refdata) +{ + u8 data; + unsigned idx; + struct emu10k1_mpuin *card_mpuin = (struct emu10k1_mpuin *) refdata; + unsigned long flags; + + while (card_mpuin->qhead != card_mpuin->qtail) { + spin_lock_irqsave(&card_mpuin->lock, flags); + idx = card_mpuin->qhead; + data = card_mpuin->midiq[idx].data; + card_mpuin->timein = card_mpuin->midiq[idx].timein; + idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE; + card_mpuin->qhead = idx; + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + sblive_miStateEntry(card_mpuin, data); + } + + return; +} + +/* IRQ callback handler routine for the MPU in port */ + +int emu10k1_mpuin_irqhandler(struct emu10k1_card *card) +{ + unsigned idx; + unsigned count; + u8 MPUIvalue; + struct emu10k1_mpuin *card_mpuin = card->mpuin; + + /* IRQ service routine. The data and code touched are: + * 1. card_mpuin + */ + + count = 0; + idx = card_mpuin->qtail; + + while (1) { + if (emu10k1_mpu_read_data(card, &MPUIvalue) < 0) { + break; + } else { + ++count; + card_mpuin->midiq[idx].data = MPUIvalue; + card_mpuin->midiq[idx].timein = (jiffies * 1000) / HZ; + idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE; + } + } + + if (count) { + card_mpuin->qtail = idx; + + tasklet_hi_schedule(&card_mpuin->tasklet); + } + + return 0; +} + +/*****************************************************************************/ + +/* Supporting functions for Midi-In Interpretation State Machine */ + +/*****************************************************************************/ + +/* FIXME: This should be a macro */ +int sblive_miStateInit(struct emu10k1_mpuin *card_mpuin) +{ + card_mpuin->status = 0; /* For MIDI running status */ + card_mpuin->fstatus = 0; /* For 0xFn status only */ + card_mpuin->curstate = STIN_PARSE; + card_mpuin->laststate = STIN_PARSE; + card_mpuin->data = 0; + card_mpuin->timestart = 0; + card_mpuin->timein = 0; + + return 0; +} + +/* FIXME: This should be a macro */ +int sblive_miStateEntry(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data); +} + +int sblive_miStateParse(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + switch (data & 0xf0) { + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + case 0xE0: + card_mpuin->curstate = STIN_3BYTE; + break; + + case 0xC0: + case 0xD0: + card_mpuin->curstate = STIN_2BYTE; + break; + + case 0xF0: + /* System messages do not affect the previous running status! */ + switch (data & 0x0f) { + case 0x0: + card_mpuin->laststate = card_mpuin->curstate; + card_mpuin->curstate = STIN_SYS_EX_NORM; + + if (card_mpuin->firstmidiq) { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + *midiq->midibyte = data; + --midiq->sizeLeft; + ++midiq->midibyte; + } + + return CTSTATUS_NEXT_BYTE; + + case 0x7: + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, 0xf7, 0); + return -1; + + case 0x2: + card_mpuin->laststate = card_mpuin->curstate; + card_mpuin->curstate = STIN_SYS_COMMON_3; + break; + + case 0x1: + case 0x3: + card_mpuin->laststate = card_mpuin->curstate; + card_mpuin->curstate = STIN_SYS_COMMON_2; + break; + + default: + /* includes 0xF4 - 0xF6, 0xF8 - 0xFF */ + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + } + + break; + + default: + DPF(2, "BUG: default case hit\n"); + return -1; + } + + return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data); +} + +int sblive_miState3Byte(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + u8 temp = data & 0xf0; + + if (temp < 0x80) { + return midistatefn[STIN_3BYTE_KEY].Fn(card_mpuin, data); + } else if (temp <= 0xe0 && temp != 0xc0 && temp != 0xd0) { + card_mpuin->status = data; + card_mpuin->curstate = STIN_3BYTE_KEY; + + return CTSTATUS_NEXT_BYTE; + } + + return midistatefn[STIN_PARSE].Fn(card_mpuin, data); +} + +int sblive_miState3ByteKey(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 1 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = STIN_PARSE; + tmp = ((unsigned long) data) << 8; + tmp |= (unsigned long) card_mpuin->status; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return -1; + } + + card_mpuin->data = data; + card_mpuin->curstate = STIN_3BYTE_VEL; + + return CTSTATUS_NEXT_BYTE; +} + +int sblive_miState3ByteVel(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 2 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = STIN_PARSE; + tmp = ((unsigned long) data) << 8; + tmp |= card_mpuin->data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->status; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return -1; + } + + card_mpuin->curstate = STIN_3BYTE; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->status; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3); + + return 0; +} + +int sblive_miState2Byte(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + u8 temp = data & 0xf0; + + if ((temp == 0xc0) || (temp == 0xd0)) { + card_mpuin->status = data; + card_mpuin->curstate = STIN_2BYTE_KEY; + + return CTSTATUS_NEXT_BYTE; + } + + if (temp < 0x80) + return midistatefn[STIN_2BYTE_KEY].Fn(card_mpuin, data); + + return midistatefn[STIN_PARSE].Fn(card_mpuin, data); +} + +int sblive_miState2ByteKey(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 1 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = STIN_PARSE; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->status; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return -1; + } + + card_mpuin->curstate = STIN_2BYTE; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->status; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2); + + return 0; +} + +int sblive_miStateSysCommon2(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + card_mpuin->fstatus = data; + card_mpuin->curstate = STIN_SYS_COMMON_2_KEY; + + return CTSTATUS_NEXT_BYTE; +} + +int sblive_miStateSysCommon2Key(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 1 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->fstatus; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return -1; + } + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->fstatus; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2); + + return 0; +} + +int sblive_miStateSysCommon3(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + card_mpuin->fstatus = data; + card_mpuin->curstate = STIN_SYS_COMMON_3_KEY; + + return CTSTATUS_NEXT_BYTE; +} + +int sblive_miStateSysCommon3Key(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 1 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->fstatus; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return -1; + } + + card_mpuin->data = data; + card_mpuin->curstate = STIN_SYS_COMMON_3_VEL; + + return CTSTATUS_NEXT_BYTE; +} + +int sblive_miStateSysCommon3Vel(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 2 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->fstatus; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return -1; + } + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->fstatus; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3); + + return 0; +} + +int sblive_miStateSysExNorm(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + unsigned long flags; + + if ((data > 0x7f) && (data != 0xf7)) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid Data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + + if (card_mpuin->firstmidiq) { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + *midiq->midibyte = data; + --midiq->sizeLeft; + ++midiq->midibyte; + + spin_lock_irqsave(&card_mpuin->lock, flags); + + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0); + + kfree(midiq); + } + + return -1; + } + + if (card_mpuin->firstmidiq) { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + *midiq->midibyte = data; + --midiq->sizeLeft; + ++midiq->midibyte; + } + + if (data == 0xf7) { + /* End of Sysex buffer */ + /* Send down the buffer */ + + card_mpuin->curstate = card_mpuin->laststate; + + if (card_mpuin->firstmidiq) { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + + spin_lock_irqsave(&card_mpuin->lock, flags); + + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0); + + kfree(midiq); + } + + return 0; + } + + if (card_mpuin->firstmidiq) { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + + if (midiq->sizeLeft == 0) { + /* Special case */ + + spin_lock_irqsave(&card_mpuin->lock, flags); + + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0); + + kfree(midiq); + + return CTSTATUS_NEXT_BYTE; + } + } + + return CTSTATUS_NEXT_BYTE; +} + +int sblive_miStateSysReal(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, data, 1); + + return CTSTATUS_NEXT_BYTE; +} diff -Nru a/sound/oss/emu10k1/cardmi.h b/sound/oss/emu10k1/cardmi.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/cardmi.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,114 @@ +/* + ********************************************************************** + * sblive_mi.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _CARDMI_H +#define _CARDMI_H + +#include "icardmid.h" +#include + +typedef enum +{ + STIN_PARSE = 0, + STIN_3BYTE, /* 0x80, 0x90, 0xA0, 0xB0, 0xE0 */ + STIN_3BYTE_KEY, /* Byte 1 */ + STIN_3BYTE_VEL, /* Byte 1 */ + STIN_2BYTE, /* 0xC0, 0xD0 */ + STIN_2BYTE_KEY, /* Byte 1 */ + STIN_SYS_COMMON_2, /* 0xF1, 0xF3 */ + STIN_SYS_COMMON_2_KEY, + STIN_SYS_COMMON_3, /* 0xF2 */ + STIN_SYS_COMMON_3_KEY, + STIN_SYS_COMMON_3_VEL, + STIN_SYS_EX_NORM, /* 0xF0, Normal mode */ + STIN_SYS_REAL +} midi_in_state; + + +/* flags for card MIDI in object */ +#define FLAGS_MIDM_STARTED 0x00001000 // Data has started to come in after Midm Start +#define MIDIIN_MAX_BUFFER_SIZE 200 // Definition for struct emu10k1_mpuin + +struct midi_data +{ + u8 data; + u32 timein; +}; + +struct emu10k1_mpuin +{ + spinlock_t lock; + struct midi_queue *firstmidiq; + struct midi_queue *lastmidiq; + unsigned qhead, qtail; + struct midi_data midiq[MIDIIN_MAX_BUFFER_SIZE]; + struct tasklet_struct tasklet; + struct midi_openinfo openinfo; + + /* For MIDI state machine */ + u8 status; /* For MIDI running status */ + u8 fstatus; /* For 0xFn status only */ + midi_in_state curstate; + midi_in_state laststate; + u32 timestart; + u32 timein; + u8 data; +}; + +int emu10k1_mpuin_open(struct emu10k1_card *, struct midi_openinfo *); +int emu10k1_mpuin_close(struct emu10k1_card *); +int emu10k1_mpuin_add_buffer(struct emu10k1_mpuin *, struct midi_hdr *); +int emu10k1_mpuin_start(struct emu10k1_card *); +int emu10k1_mpuin_stop(struct emu10k1_card *); +int emu10k1_mpuin_reset(struct emu10k1_card *); + +int sblive_miStateInit(struct emu10k1_mpuin *); +int sblive_miStateEntry(struct emu10k1_mpuin *, u8); +int sblive_miStateParse(struct emu10k1_mpuin *, u8); +int sblive_miState3Byte(struct emu10k1_mpuin *, u8); +int sblive_miState3ByteKey(struct emu10k1_mpuin *, u8); +int sblive_miState3ByteVel(struct emu10k1_mpuin *, u8); +int sblive_miState2Byte(struct emu10k1_mpuin *, u8); +int sblive_miState2ByteKey(struct emu10k1_mpuin *, u8); +int sblive_miStateSysCommon2(struct emu10k1_mpuin *, u8); +int sblive_miStateSysCommon2Key(struct emu10k1_mpuin *, u8); +int sblive_miStateSysCommon3(struct emu10k1_mpuin *, u8); +int sblive_miStateSysCommon3Key(struct emu10k1_mpuin *, u8); +int sblive_miStateSysCommon3Vel(struct emu10k1_mpuin *, u8); +int sblive_miStateSysExNorm(struct emu10k1_mpuin *, u8); +int sblive_miStateSysReal(struct emu10k1_mpuin *, u8); + +int emu10k1_mpuin_irqhandler(struct emu10k1_card *); +void emu10k1_mpuin_bh(unsigned long); +int emu10k1_mpuin_callback(struct emu10k1_mpuin *card_mpuin, u32 msg, unsigned long data, u32 bytesvalid); + +#endif /* _CARDMI_H */ diff -Nru a/sound/oss/emu10k1/cardmo.c b/sound/oss/emu10k1/cardmo.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/cardmo.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,229 @@ +/* + ********************************************************************** + * cardmo.c - MIDI UART output HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include + +#include "hwaccess.h" +#include "8010.h" +#include "cardmo.h" +#include "irqmgr.h" + +/* Installs the IRQ handler for the MPU out port * + * and initialize parameters */ + +int emu10k1_mpuout_open(struct emu10k1_card *card, struct midi_openinfo *openinfo) +{ + struct emu10k1_mpuout *card_mpuout = card->mpuout; + + DPF(2, "emu10k1_mpuout_open()\n"); + + if (!(card_mpuout->status & FLAGS_AVAILABLE)) + return -1; + + /* Copy open info and mark channel as in use */ + card_mpuout->intr = 0; + card_mpuout->openinfo = *openinfo; + card_mpuout->status &= ~FLAGS_AVAILABLE; + card_mpuout->laststatus = 0x80; + card_mpuout->firstmidiq = NULL; + card_mpuout->lastmidiq = NULL; + + emu10k1_mpu_reset(card); + emu10k1_mpu_acquire(card); + + return 0; +} + +int emu10k1_mpuout_close(struct emu10k1_card *card) +{ + struct emu10k1_mpuout *card_mpuout = card->mpuout; + struct midi_queue *midiq; + struct midi_hdr *midihdr; + unsigned long flags; + + DPF(2, "emu10k1_mpuout_close()\n"); + + emu10k1_irq_disable(card, INTE_MIDITXENABLE); + + spin_lock_irqsave(&card_mpuout->lock, flags); + + while (card_mpuout->firstmidiq != NULL) { + midiq = card_mpuout->firstmidiq; + midihdr = (struct midi_hdr *) midiq->refdata; + + card_mpuout->firstmidiq = midiq->next; + + kfree(midihdr->data); + kfree(midihdr); + kfree(midiq); + } + + card_mpuout->lastmidiq = NULL; + + emu10k1_mpu_release(card); + + card_mpuout->status |= FLAGS_AVAILABLE; + + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return 0; +} + +/* If there isn't enough buffer space, reject Midi Buffer. * +* Otherwise, disable TX, create object to hold Midi * +* uffer, update buffer flags and other parameters * +* before enabling TX again. */ + +int emu10k1_mpuout_add_buffer(struct emu10k1_card *card, struct midi_hdr *midihdr) +{ + struct emu10k1_mpuout *card_mpuout = card->mpuout; + struct midi_queue *midiq; + unsigned long flags; + + DPF(2, "emu10k1_mpuout_add_buffer()\n"); + + if (card_mpuout->state == CARDMIDIOUT_STATE_SUSPEND) + return 0; + + midihdr->flags |= MIDIBUF_INQUEUE; + midihdr->flags &= ~MIDIBUF_DONE; + + if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_KERNEL)) == NULL) { + /* Message lost */ + return -1; + } + + midiq->next = NULL; + midiq->qtype = 1; + midiq->length = midihdr->bufferlength; + midiq->sizeLeft = midihdr->bufferlength; + midiq->midibyte = midihdr->data; + + midiq->refdata = (unsigned long) midihdr; + + spin_lock_irqsave(&card_mpuout->lock, flags); + + if (card_mpuout->firstmidiq == NULL) { + card_mpuout->firstmidiq = midiq; + card_mpuout->lastmidiq = midiq; + } else { + (card_mpuout->lastmidiq)->next = midiq; + card_mpuout->lastmidiq = midiq; + } + + card_mpuout->intr = 0; + + emu10k1_irq_enable(card, INTE_MIDITXENABLE); + + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return 0; +} + +void emu10k1_mpuout_bh(unsigned long refdata) +{ + struct emu10k1_card *card = (struct emu10k1_card *) refdata; + struct emu10k1_mpuout *card_mpuout = card->mpuout; + int cByteSent = 0; + struct midi_queue *midiq; + struct midi_queue *doneq = NULL; + unsigned long flags; + + spin_lock_irqsave(&card_mpuout->lock, flags); + + while (card_mpuout->firstmidiq != NULL) { + midiq = card_mpuout->firstmidiq; + + while (cByteSent < 4 && midiq->sizeLeft) { + if (emu10k1_mpu_write_data(card, *midiq->midibyte) < 0) { + DPF(2, "emu10k1_mpuoutDpcCallback error!!\n"); + } else { + ++cByteSent; + --midiq->sizeLeft; + ++midiq->midibyte; + } + } + + if (midiq->sizeLeft == 0) { + if (doneq == NULL) + doneq = midiq; + card_mpuout->firstmidiq = midiq->next; + } else + break; + } + + if (card_mpuout->firstmidiq == NULL) + card_mpuout->lastmidiq = NULL; + + if (doneq != NULL) { + while (doneq != card_mpuout->firstmidiq) { + unsigned long callback_msg[3]; + + midiq = doneq; + doneq = midiq->next; + + if (midiq->qtype) { + callback_msg[0] = 0; + callback_msg[1] = midiq->length; + callback_msg[2] = midiq->refdata; + + emu10k1_midi_callback(ICARDMIDI_OUTLONGDATA, card_mpuout->openinfo.refdata, callback_msg); + } else if (((u8) midiq->refdata) < 0xF0 && ((u8) midiq->refdata) > 0x7F) + card_mpuout->laststatus = (u8) midiq->refdata; + + kfree(midiq); + } + } + + if ((card_mpuout->firstmidiq != NULL) || cByteSent) { + card_mpuout->intr = 0; + emu10k1_irq_enable(card, INTE_MIDITXENABLE); + } + + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return; +} + +int emu10k1_mpuout_irqhandler(struct emu10k1_card *card) +{ + struct emu10k1_mpuout *card_mpuout = card->mpuout; + + DPF(4, "emu10k1_mpuout_irqhandler\n"); + + card_mpuout->intr = 1; + emu10k1_irq_disable(card, INTE_MIDITXENABLE); + + tasklet_hi_schedule(&card_mpuout->tasklet); + + return 0; +} diff -Nru a/sound/oss/emu10k1/cardmo.h b/sound/oss/emu10k1/cardmo.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/cardmo.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,62 @@ +/* + ********************************************************************** + * cardmo.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _CARDMO_H +#define _CARDMO_H + +#include "icardmid.h" +#include + +#define CARDMIDIOUT_STATE_DEFAULT 0x00000000 +#define CARDMIDIOUT_STATE_SUSPEND 0x00000001 + +struct emu10k1_mpuout +{ + u32 status; + u32 state; + volatile int intr; + struct midi_queue *firstmidiq; + struct midi_queue *lastmidiq; + u8 laststatus; + struct tasklet_struct tasklet; + spinlock_t lock; + struct midi_openinfo openinfo; +}; + +int emu10k1_mpuout_open(struct emu10k1_card *, struct midi_openinfo *); +int emu10k1_mpuout_close(struct emu10k1_card *); +int emu10k1_mpuout_add_buffer(struct emu10k1_card *, struct midi_hdr *); + +int emu10k1_mpuout_irqhandler(struct emu10k1_card *); +void emu10k1_mpuout_bh(unsigned long); + +#endif /* _CARDMO_H */ diff -Nru a/sound/oss/emu10k1/cardwi.c b/sound/oss/emu10k1/cardwi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/cardwi.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,371 @@ +/* + ********************************************************************** + * cardwi.c - PCM input HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include +#include "hwaccess.h" +#include "timer.h" +#include "recmgr.h" +#include "audio.h" +#include "cardwi.h" + +/** + * query_format - returns a valid sound format + * + * This function will return a valid sound format as close + * to the requested one as possible. + */ +void query_format(int recsrc, struct wave_format *wave_fmt) +{ + + switch (recsrc) { + case WAVERECORD_AC97: + + if ((wave_fmt->channels != 1) && (wave_fmt->channels != 2)) + wave_fmt->channels = 2; + + if (wave_fmt->samplingrate >= (0xBB80 + 0xAC44) / 2) + wave_fmt->samplingrate = 0xBB80; + else if (wave_fmt->samplingrate >= (0xAC44 + 0x7D00) / 2) + wave_fmt->samplingrate = 0xAC44; + else if (wave_fmt->samplingrate >= (0x7D00 + 0x5DC0) / 2) + wave_fmt->samplingrate = 0x7D00; + else if (wave_fmt->samplingrate >= (0x5DC0 + 0x5622) / 2) + wave_fmt->samplingrate = 0x5DC0; + else if (wave_fmt->samplingrate >= (0x5622 + 0x3E80) / 2) + wave_fmt->samplingrate = 0x5622; + else if (wave_fmt->samplingrate >= (0x3E80 + 0x2B11) / 2) + wave_fmt->samplingrate = 0x3E80; + else if (wave_fmt->samplingrate >= (0x2B11 + 0x1F40) / 2) + wave_fmt->samplingrate = 0x2B11; + else + wave_fmt->samplingrate = 0x1F40; + + switch (wave_fmt->id) { + case AFMT_S16_LE: + wave_fmt->bitsperchannel = 16; + break; + case AFMT_U8: + wave_fmt->bitsperchannel = 8; + break; + default: + wave_fmt->id = AFMT_S16_LE; + wave_fmt->bitsperchannel = 16; + break; + } + + break; + + /* these can't be changed from the original values */ + case WAVERECORD_MIC: + case WAVERECORD_FX: + break; + + default: + BUG(); + break; + } + + wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3; + wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel; + wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate; +} + +static int alloc_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer) +{ + buffer->addr = pci_alloc_consistent(card->pci_dev, buffer->size * buffer->cov, + &buffer->dma_handle); + if (buffer->addr == NULL) + return -1; + + return 0; +} + +static void free_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer) +{ + if (buffer->addr != NULL) + pci_free_consistent(card->pci_dev, buffer->size * buffer->cov, + buffer->addr, buffer->dma_handle); +} + +int emu10k1_wavein_open(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + struct wiinst **wiinst_tmp = NULL; + u32 delay; + unsigned long flags; + + DPF(2, "emu10k1_wavein_open()\n"); + + switch (wiinst->recsrc) { + case WAVERECORD_AC97: + wiinst_tmp = &card->wavein.ac97; + break; + case WAVERECORD_MIC: + wiinst_tmp = &card->wavein.mic; + break; + case WAVERECORD_FX: + wiinst_tmp = &card->wavein.fx; + break; + default: + BUG(); + break; + } + + spin_lock_irqsave(&card->lock, flags); + if (*wiinst_tmp != NULL) { + spin_unlock_irqrestore(&card->lock, flags); + return -1; + } + + *wiinst_tmp = wiinst; + spin_unlock_irqrestore(&card->lock, flags); + + /* handle 8 bit recording */ + if (wiinst->format.bytesperchannel == 1) { + if (wiinst->buffer.size > 0x8000) { + wiinst->buffer.size = 0x8000; + wiinst->buffer.sizeregval = 0x1f; + } else + wiinst->buffer.sizeregval += 4; + + wiinst->buffer.cov = 2; + } else + wiinst->buffer.cov = 1; + + if (alloc_buffer(card, &wiinst->buffer) < 0) { + ERROR(); + emu10k1_wavein_close(wave_dev); + return -1; + } + + emu10k1_set_record_src(card, wiinst); + + delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec; + + emu10k1_timer_install(card, &wiinst->timer, delay / 2); + + wiinst->state = WAVE_STATE_OPEN; + + return 0; +} + +void emu10k1_wavein_close(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + unsigned long flags; + + DPF(2, "emu10k1_wavein_close()\n"); + + emu10k1_wavein_stop(wave_dev); + + emu10k1_timer_uninstall(card, &wiinst->timer); + + free_buffer(card, &wiinst->buffer); + + spin_lock_irqsave(&card->lock, flags); + switch (wave_dev->wiinst->recsrc) { + case WAVERECORD_AC97: + card->wavein.ac97 = NULL; + break; + case WAVERECORD_MIC: + card->wavein.mic = NULL; + break; + case WAVERECORD_FX: + card->wavein.fx = NULL; + break; + default: + BUG(); + break; + } + spin_unlock_irqrestore(&card->lock, flags); + + wiinst->state = WAVE_STATE_CLOSED; +} + +void emu10k1_wavein_start(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + + DPF(2, "emu10k1_wavein_start()\n"); + + emu10k1_start_record(card, &wiinst->buffer); + emu10k1_timer_enable(wave_dev->card, &wiinst->timer); + + wiinst->buffer.hw_pos = 0; + wiinst->buffer.pos = 0; + wiinst->buffer.bytestocopy = 0; + + wiinst->state |= WAVE_STATE_STARTED; +} + +void emu10k1_wavein_stop(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + + DPF(2, "emu10k1_wavein_stop()\n"); + + if (!(wiinst->state & WAVE_STATE_STARTED)) + return; + + emu10k1_timer_disable(card, &wiinst->timer); + emu10k1_stop_record(card, &wiinst->buffer); + + wiinst->state &= ~WAVE_STATE_STARTED; +} + +int emu10k1_wavein_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + u32 delay; + + DPF(2, "emu10k1_wavein_setformat()\n"); + + if (wiinst->state & WAVE_STATE_STARTED) + return -1; + + query_format(wiinst->recsrc, format); + + if ((wiinst->format.samplingrate != format->samplingrate) + || (wiinst->format.bitsperchannel != format->bitsperchannel) + || (wiinst->format.channels != format->channels)) { + + wiinst->format = *format; + + if (wiinst->state == WAVE_STATE_CLOSED) + return 0; + + wiinst->buffer.size *= wiinst->buffer.cov; + + if (wiinst->format.bytesperchannel == 1) { + wiinst->buffer.cov = 2; + wiinst->buffer.size /= wiinst->buffer.cov; + } else + wiinst->buffer.cov = 1; + + emu10k1_timer_uninstall(card, &wiinst->timer); + + delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec; + + emu10k1_timer_install(card, &wiinst->timer, delay / 2); + } + + return 0; +} + +void emu10k1_wavein_getxfersize(struct wiinst *wiinst, u32 * size) +{ + struct wavein_buffer *buffer = &wiinst->buffer; + + *size = buffer->bytestocopy; + + if (wiinst->mmapped) + return; + + if (*size > buffer->size) { + *size = buffer->size; + buffer->pos = buffer->hw_pos; + buffer->bytestocopy = buffer->size; + DPF(1, "buffer overrun\n"); + } +} + +static void copy_block(u8 *dst, u8 * src, u32 str, u32 len, u8 cov) +{ + if (cov == 1) + __copy_to_user(dst, src + str, len); + else { + u8 byte; + u32 i; + + src += 1 + 2 * str; + + for (i = 0; i < len; i++) { + byte = src[2 * i] ^ 0x80; + __copy_to_user(dst + i, &byte, 1); + } + } +} + +void emu10k1_wavein_xferdata(struct wiinst *wiinst, u8 * data, u32 * size) +{ + struct wavein_buffer *buffer = &wiinst->buffer; + u32 sizetocopy, sizetocopy_now, start; + unsigned long flags; + + sizetocopy = min_t(u32, buffer->size, *size); + *size = sizetocopy; + + if (!sizetocopy) + return; + + spin_lock_irqsave(&wiinst->lock, flags); + start = buffer->pos; + buffer->pos += sizetocopy; + buffer->pos %= buffer->size; + buffer->bytestocopy -= sizetocopy; + sizetocopy_now = buffer->size - start; + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (sizetocopy > sizetocopy_now) { + sizetocopy -= sizetocopy_now; + + copy_block(data, buffer->addr, start, sizetocopy_now, buffer->cov); + copy_block(data + sizetocopy_now, buffer->addr, 0, sizetocopy, buffer->cov); + } else { + copy_block(data, buffer->addr, start, sizetocopy, buffer->cov); + } +} + +void emu10k1_wavein_update(struct emu10k1_card *card, struct wiinst *wiinst) +{ + u32 hw_pos; + u32 diff; + + /* There is no actual start yet */ + if (!(wiinst->state & WAVE_STATE_STARTED)) { + hw_pos = wiinst->buffer.hw_pos; + } else { + /* hw_pos in byte units */ + hw_pos = sblive_readptr(card, wiinst->buffer.idxreg, 0) / wiinst->buffer.cov; + } + + diff = (wiinst->buffer.size + hw_pos - wiinst->buffer.hw_pos) % wiinst->buffer.size; + wiinst->total_recorded += diff; + wiinst->buffer.bytestocopy += diff; + + wiinst->buffer.hw_pos = hw_pos; +} diff -Nru a/sound/oss/emu10k1/cardwi.h b/sound/oss/emu10k1/cardwi.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/cardwi.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,90 @@ +/* + ********************************************************************** + * cardwi.h -- header file for card wave input functions + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ +#ifndef _CARDWI_H +#define _CARDWI_H + +#include "icardwav.h" +#include "audio.h" +#include "timer.h" + +struct wavein_buffer { + u16 ossfragshift; + u32 fragment_size; + u32 numfrags; + u32 hw_pos; /* hardware cursor position */ + u32 pos; /* software cursor position */ + u32 bytestocopy; /* bytes of recorded data available */ + u32 size; + u32 pages; + u32 sizereg; + u32 sizeregval; + u32 addrreg; + u32 idxreg; + u32 adcctl; + void *addr; + u8 cov; + dma_addr_t dma_handle; +}; + +struct wiinst +{ + u8 state; + struct emu_timer timer; + struct wave_format format; + struct wavein_buffer buffer; + wait_queue_head_t wait_queue; + u8 mmapped; + u32 total_recorded; /* total bytes read() from device */ + u32 blocks; + spinlock_t lock; + u8 recsrc; + u16 fxwc; +}; + +#define WAVEIN_MAXBUFSIZE 65536 +#define WAVEIN_MINBUFSIZE 368 + +#define WAVEIN_DEFAULTFRAGLEN 100 +#define WAVEIN_DEFAULTBUFLEN 1000 + +#define WAVEIN_MINFRAGSHIFT 8 + +int emu10k1_wavein_open(struct emu10k1_wavedevice *); +void emu10k1_wavein_close(struct emu10k1_wavedevice *); +void emu10k1_wavein_start(struct emu10k1_wavedevice *); +void emu10k1_wavein_stop(struct emu10k1_wavedevice *); +void emu10k1_wavein_getxfersize(struct wiinst *, u32 *); +void emu10k1_wavein_xferdata(struct wiinst *, u8 *, u32 *); +int emu10k1_wavein_setformat(struct emu10k1_wavedevice *, struct wave_format *); +void emu10k1_wavein_update(struct emu10k1_card *, struct wiinst *); + + +#endif /* _CARDWI_H */ diff -Nru a/sound/oss/emu10k1/cardwo.c b/sound/oss/emu10k1/cardwo.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/cardwo.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,693 @@ +/* + ********************************************************************** + * cardwo.c - PCM output HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include +#include "hwaccess.h" +#include "8010.h" +#include "voicemgr.h" +#include "cardwo.h" +#include "audio.h" + +static u32 samplerate_to_linearpitch(u32 samplingrate) +{ + samplingrate = (samplingrate << 8) / 375; + return (samplingrate >> 1) + (samplingrate & 1); +} + +static void query_format(struct emu10k1_wavedevice *wave_dev, struct wave_format *wave_fmt) +{ + int i, j, do_passthrough = 0, is_ac3 = 0; + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + + if ((wave_fmt->channels > 2) && (wave_fmt->id != AFMT_S16_LE) && (wave_fmt->id != AFMT_U8)) + wave_fmt->channels = 2; + + if ((wave_fmt->channels < 1) || (wave_fmt->channels > WAVEOUT_MAXVOICES)) + wave_fmt->channels = 2; + + if (wave_fmt->channels == 2) + woinst->num_voices = 1; + else + woinst->num_voices = wave_fmt->channels; + + if (wave_fmt->samplingrate >= 0x2ee00) + wave_fmt->samplingrate = 0x2ee00; + + wave_fmt->passthrough = 0; + do_passthrough = is_ac3 = 0; + + if (card->pt.selected) + do_passthrough = 1; + + switch (wave_fmt->id) { + case AFMT_S16_LE: + wave_fmt->bitsperchannel = 16; + break; + case AFMT_U8: + wave_fmt->bitsperchannel = 8; + break; + case AFMT_AC3: + do_passthrough = 1; + is_ac3 = 1; + break; + default: + wave_fmt->id = AFMT_S16_LE; + wave_fmt->bitsperchannel = 16; + break; + } + if (do_passthrough) { + i = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.intr_gpr_name); + j = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.enable_gpr_name); + /* currently only one waveout instance may use pass-through */ + if (i < 0 || j < 0 || woinst->state != WAVE_STATE_CLOSED || + card->pt.state != PT_STATE_INACTIVE || + (wave_fmt->samplingrate != 48000 && !is_ac3) || + (wave_fmt->samplingrate != 48000 && !is_ac3)) { + DPF(2, "unable to set pass-through mode\n"); + } else { + wave_fmt->samplingrate = 48000; + wave_fmt->channels = 2; + wave_fmt->passthrough = 1; + card->pt.intr_gpr = i; + card->pt.enable_gpr = j; + card->pt.state = PT_STATE_INACTIVE; + card->pt.pos_gpr = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.pos_gpr_name); + DPD(2, "is_ac3 is %d\n", is_ac3); + card->pt.ac3data = is_ac3; + wave_fmt->bitsperchannel = 16; + } + } + + wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3; + + if (wave_fmt->channels == 2) + wave_fmt->bytespervoicesample = wave_fmt->channels * wave_fmt->bytesperchannel; + else + wave_fmt->bytespervoicesample = wave_fmt->bytesperchannel; + + wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel; + wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate; +} + +/** + * alloc_buffer - + * + * allocates the memory buffer for a voice. Two page tables are kept for each buffer. + * One (dma_handle) keeps track of the host memory pages used and the other (virtualpagetable) + * is passed to the device so that it can do DMA to host memory. + * + */ +static int alloc_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer, unsigned int voicenum) +{ + u32 pageindex, pagecount; + unsigned long busaddx; + int i; + + DPD(2, "requested pages is: %d\n", buffer->pages); + + if ((buffer->mem[voicenum].emupageindex = + emu10k1_addxmgr_alloc(buffer->pages * PAGE_SIZE, card)) < 0) + return -1; + + /* Fill in virtual memory table */ + for (pagecount = 0; pagecount < buffer->pages; pagecount++) { + if ((buffer->mem[voicenum].addr[pagecount] = + pci_alloc_consistent(card->pci_dev, PAGE_SIZE, + &buffer->mem[voicenum].dma_handle[pagecount])) == NULL) { + buffer->pages = pagecount; + return -1; + } + + DPD(2, "Virtual Addx: %p\n", buffer->mem[voicenum].addr[pagecount]); + + for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) { + busaddx = buffer->mem[voicenum].dma_handle[pagecount] + i * EMUPAGESIZE; + + DPD(3, "Bus Addx: %#lx\n", busaddx); + + pageindex = buffer->mem[voicenum].emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i; + + ((u32 *) card->virtualpagetable.addr)[pageindex] = cpu_to_le32((busaddx * 2) | pageindex); + } + } + + return 0; +} + +/** + * free_buffer - + * + * frees the memory buffer for a voice. + */ +static void free_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer, unsigned int voicenum) +{ + u32 pagecount, pageindex; + int i; + + if (buffer->mem[voicenum].emupageindex < 0) + return; + + for (pagecount = 0; pagecount < buffer->pages; pagecount++) { + pci_free_consistent(card->pci_dev, PAGE_SIZE, + buffer->mem[voicenum].addr[pagecount], + buffer->mem[voicenum].dma_handle[pagecount]); + + for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) { + pageindex = buffer->mem[voicenum].emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i; + ((u32 *) card->virtualpagetable.addr)[pageindex] = + cpu_to_le32((card->silentpage.dma_handle * 2) | pageindex); + } + } + + emu10k1_addxmgr_free(card, buffer->mem[voicenum].emupageindex); + buffer->mem[voicenum].emupageindex = -1; +} + +static int get_voice(struct emu10k1_card *card, struct woinst *woinst, unsigned int voicenum) +{ + struct emu_voice *voice = &woinst->voice[voicenum]; + /* Allocate voices here, if no voices available, return error. + * Init voice_allocdesc first.*/ + + voice->usage = VOICE_USAGE_PLAYBACK; + + voice->flags = 0; + + if (woinst->format.channels == 2) + voice->flags |= VOICE_FLAGS_STEREO; + + if (woinst->format.bitsperchannel == 16) + voice->flags |= VOICE_FLAGS_16BIT; + + if (emu10k1_voice_alloc(card, voice) < 0) { + voice->usage = VOICE_USAGE_FREE; + return -1; + } + + /* Calculate pitch */ + voice->initial_pitch = (u16) (srToPitch(woinst->format.samplingrate) >> 8); + voice->pitch_target = samplerate_to_linearpitch(woinst->format.samplingrate); + + DPD(2, "Initial pitch --> %#x\n", voice->initial_pitch); + + voice->startloop = (woinst->buffer.mem[voicenum].emupageindex << 12) / + woinst->format.bytespervoicesample; + voice->endloop = voice->startloop + woinst->buffer.size / woinst->format.bytespervoicesample; + voice->start = voice->startloop; + + if (voice->flags & VOICE_FLAGS_STEREO) { + voice->params[0].send_a = card->waveout.send_a[1]; + voice->params[0].send_b = card->waveout.send_b[1]; + voice->params[0].send_c = card->waveout.send_c[1]; + voice->params[0].send_d = card->waveout.send_d[1]; + + if (woinst->device) + voice->params[0].send_routing = 0x7654; + else + voice->params[0].send_routing = card->waveout.send_routing[1]; + + voice->params[0].volume_target = 0xffff; + voice->params[0].initial_fc = 0xff; + voice->params[0].initial_attn = 0x00; + voice->params[0].byampl_env_sustain = 0x7f; + voice->params[0].byampl_env_decay = 0x7f; + + voice->params[1].send_a = card->waveout.send_a[2]; + voice->params[1].send_b = card->waveout.send_b[2]; + voice->params[1].send_c = card->waveout.send_c[2]; + voice->params[1].send_d = card->waveout.send_d[2]; + + if (woinst->device) + voice->params[1].send_routing = 0x7654; + else + voice->params[1].send_routing = card->waveout.send_routing[2]; + + voice->params[1].volume_target = 0xffff; + voice->params[1].initial_fc = 0xff; + voice->params[1].initial_attn = 0x00; + voice->params[1].byampl_env_sustain = 0x7f; + voice->params[1].byampl_env_decay = 0x7f; + } else { + if (woinst->num_voices > 1) { + voice->params[0].send_a = 0xff; + voice->params[0].send_b = 0; + voice->params[0].send_c = 0; + voice->params[0].send_d = 0; + + voice->params[0].send_routing = + 0xfff0 + card->mchannel_fx + voicenum; + } else { + voice->params[0].send_a = card->waveout.send_a[0]; + voice->params[0].send_b = card->waveout.send_b[0]; + voice->params[0].send_c = card->waveout.send_c[0]; + voice->params[0].send_d = card->waveout.send_d[0]; + + if (woinst->device) + voice->params[0].send_routing = 0x7654; + else + voice->params[0].send_routing = card->waveout.send_routing[0]; + } + + voice->params[0].volume_target = 0xffff; + voice->params[0].initial_fc = 0xff; + voice->params[0].initial_attn = 0x00; + voice->params[0].byampl_env_sustain = 0x7f; + voice->params[0].byampl_env_decay = 0x7f; + } + + DPD(2, "voice: startloop=%#x, endloop=%#x\n", voice->startloop, voice->endloop); + + emu10k1_voice_playback_setup(voice); + + return 0; +} + +int emu10k1_waveout_open(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + struct waveout_buffer *buffer = &woinst->buffer; + unsigned int voicenum; + u32 delay; + + DPF(2, "emu10k1_waveout_open()\n"); + + for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) { + if (alloc_buffer(card, buffer, voicenum) < 0) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return -1; + } + + if (get_voice(card, woinst, voicenum) < 0) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return -1; + } + } + + buffer->fill_silence = 0; + buffer->silence_bytes = 0; + buffer->silence_pos = 0; + buffer->hw_pos = 0; + buffer->free_bytes = woinst->buffer.size; + + delay = (48000 * woinst->buffer.fragment_size) / + (woinst->format.samplingrate * woinst->format.bytespervoicesample); + + emu10k1_timer_install(card, &woinst->timer, delay / 2); + + woinst->state = WAVE_STATE_OPEN; + + return 0; +} + +void emu10k1_waveout_close(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + unsigned int voicenum; + + DPF(2, "emu10k1_waveout_close()\n"); + + emu10k1_waveout_stop(wave_dev); + + emu10k1_timer_uninstall(card, &woinst->timer); + + for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) { + emu10k1_voice_free(&woinst->voice[voicenum]); + free_buffer(card, &woinst->buffer, voicenum); + } + + woinst->state = WAVE_STATE_CLOSED; +} + +void emu10k1_waveout_start(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + + DPF(2, "emu10k1_waveout_start()\n"); + + /* Actual start */ + emu10k1_voices_start(woinst->voice, woinst->num_voices, woinst->total_played); + + emu10k1_timer_enable(card, &woinst->timer); + + woinst->state |= WAVE_STATE_STARTED; +} + +int emu10k1_waveout_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + unsigned int voicenum; + u32 delay; + + DPF(2, "emu10k1_waveout_setformat()\n"); + + if (woinst->state & WAVE_STATE_STARTED) + return -1; + + query_format(wave_dev, format); + + if (woinst->format.samplingrate != format->samplingrate || + woinst->format.channels != format->channels || + woinst->format.bitsperchannel != format->bitsperchannel) { + + woinst->format = *format; + + if (woinst->state == WAVE_STATE_CLOSED) + return 0; + + emu10k1_timer_uninstall(card, &woinst->timer); + + for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) { + emu10k1_voice_free(&woinst->voice[voicenum]); + + if (get_voice(card, woinst, voicenum) < 0) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return -1; + } + } + + delay = (48000 * woinst->buffer.fragment_size) / + (woinst->format.samplingrate * woinst->format.bytespervoicesample); + + emu10k1_timer_install(card, &woinst->timer, delay / 2); + } + + return 0; +} + +void emu10k1_waveout_stop(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + + DPF(2, "emu10k1_waveout_stop()\n"); + + if (!(woinst->state & WAVE_STATE_STARTED)) + return; + + emu10k1_timer_disable(card, &woinst->timer); + + /* Stop actual voices */ + emu10k1_voices_stop(woinst->voice, woinst->num_voices); + + emu10k1_waveout_update(woinst); + + woinst->state &= ~WAVE_STATE_STARTED; +} + +/** + * emu10k1_waveout_getxfersize - + * + * gives the total free bytes on the voice buffer, including silence bytes + * (basically: total_free_bytes = free_bytes + silence_bytes). + * + */ +void emu10k1_waveout_getxfersize(struct woinst *woinst, u32 *total_free_bytes) +{ + struct waveout_buffer *buffer = &woinst->buffer; + int pending_bytes; + + if (woinst->mmapped) { + *total_free_bytes = buffer->free_bytes; + return; + } + + pending_bytes = buffer->size - buffer->free_bytes; + + buffer->fill_silence = (pending_bytes < (signed) buffer->fragment_size) ? 1 : 0; + + if (pending_bytes > (signed) buffer->silence_bytes) { + *total_free_bytes = (buffer->free_bytes + buffer->silence_bytes); + } else { + *total_free_bytes = buffer->size; + buffer->silence_bytes = pending_bytes; + if (pending_bytes < 0) { + buffer->silence_pos = buffer->hw_pos; + buffer->silence_bytes = 0; + buffer->free_bytes = buffer->size; + DPF(1, "buffer underrun\n"); + } + } +} + +/** + * copy_block - + * + * copies a block of pcm data to a voice buffer. + * Notice that the voice buffer is actually a set of disjointed memory pages. + * + */ +static void copy_block(void **dst, u32 str, u8 *src, u32 len) +{ + unsigned int pg; + unsigned int pgoff; + unsigned int k; + + pg = str / PAGE_SIZE; + pgoff = str % PAGE_SIZE; + + if (len > PAGE_SIZE - pgoff) { + k = PAGE_SIZE - pgoff; + __copy_from_user((u8 *)dst[pg] + pgoff, src, k); + len -= k; + while (len > PAGE_SIZE) { + __copy_from_user(dst[++pg], src + k, PAGE_SIZE); + k += PAGE_SIZE; + len -= PAGE_SIZE; + } + __copy_from_user(dst[++pg], src + k, len); + + } else + __copy_from_user((u8 *)dst[pg] + pgoff, src, len); +} + +/** + * copy_ilv_block - + * + * copies a block of pcm data containing n interleaved channels to n mono voice buffers. + * Notice that the voice buffer is actually a set of disjointed memory pages. + * + */ +static void copy_ilv_block(struct woinst *woinst, u32 str, u8 *src, u32 len) +{ + unsigned int pg; + unsigned int pgoff; + unsigned int voice_num; + struct waveout_mem *mem = woinst->buffer.mem; + + pg = str / PAGE_SIZE; + pgoff = str % PAGE_SIZE; + + while (len) { + for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) { + __copy_from_user((u8 *)(mem[voice_num].addr[pg]) + pgoff, src, woinst->format.bytespervoicesample); + src += woinst->format.bytespervoicesample; + } + + len -= woinst->format.bytespervoicesample; + + pgoff += woinst->format.bytespervoicesample; + if (pgoff >= PAGE_SIZE) { + pgoff = 0; + pg++; + } + } +} + +/** + * fill_block - + * + * fills a set voice buffers with a block of a given sample. + * + */ +static void fill_block(struct woinst *woinst, u32 str, u8 data, u32 len) +{ + unsigned int pg; + unsigned int pgoff; + unsigned int voice_num; + struct waveout_mem *mem = woinst->buffer.mem; + unsigned int k; + + pg = str / PAGE_SIZE; + pgoff = str % PAGE_SIZE; + + if (len > PAGE_SIZE - pgoff) { + k = PAGE_SIZE - pgoff; + for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) + memset((u8 *)mem[voice_num].addr[pg] + pgoff, data, k); + len -= k; + while (len > PAGE_SIZE) { + pg++; + for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) + memset(mem[voice_num].addr[pg], data, PAGE_SIZE); + + len -= PAGE_SIZE; + } + pg++; + for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) + memset(mem[voice_num].addr[pg], data, len); + + } else { + for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) + memset((u8 *)mem[voice_num].addr[pg] + pgoff, data, len); + } +} + +/** + * emu10k1_waveout_xferdata - + * + * copies pcm data to the voice buffer. Silence samples + * previously added to the buffer are overwritten. + * + */ +void emu10k1_waveout_xferdata(struct woinst *woinst, u8 *data, u32 *size) +{ + struct waveout_buffer *buffer = &woinst->buffer; + u32 sizetocopy, sizetocopy_now, start; + unsigned long flags; + + sizetocopy = min_t(u32, buffer->size, *size); + *size = sizetocopy; + + if (!sizetocopy) + return; + + spin_lock_irqsave(&woinst->lock, flags); + start = (buffer->size + buffer->silence_pos - buffer->silence_bytes) % buffer->size; + + if (sizetocopy > buffer->silence_bytes) { + buffer->silence_pos += sizetocopy - buffer->silence_bytes; + buffer->free_bytes -= sizetocopy - buffer->silence_bytes; + buffer->silence_bytes = 0; + } else + buffer->silence_bytes -= sizetocopy; + + spin_unlock_irqrestore(&woinst->lock, flags); + + sizetocopy_now = buffer->size - start; + if (sizetocopy > sizetocopy_now) { + sizetocopy -= sizetocopy_now; + if (woinst->num_voices > 1) { + copy_ilv_block(woinst, start, data, sizetocopy_now); + copy_ilv_block(woinst, 0, data + sizetocopy_now * woinst->num_voices, sizetocopy); + } else { + copy_block(buffer->mem[0].addr, start, data, sizetocopy_now); + copy_block(buffer->mem[0].addr, 0, data + sizetocopy_now, sizetocopy); + } + } else { + if (woinst->num_voices > 1) + copy_ilv_block(woinst, start, data, sizetocopy); + else + copy_block(buffer->mem[0].addr, start, data, sizetocopy); + } +} + +/** + * emu10k1_waveout_fillsilence - + * + * adds samples of silence to the voice buffer so that we + * don't loop over stale pcm data. + * + */ +void emu10k1_waveout_fillsilence(struct woinst *woinst) +{ + struct waveout_buffer *buffer = &woinst->buffer; + u32 sizetocopy, sizetocopy_now, start; + u8 filldata; + unsigned long flags; + + sizetocopy = buffer->fragment_size; + + if (woinst->format.bitsperchannel == 16) + filldata = 0x00; + else + filldata = 0x80; + + spin_lock_irqsave(&woinst->lock, flags); + buffer->silence_bytes += sizetocopy; + buffer->free_bytes -= sizetocopy; + buffer->silence_pos %= buffer->size; + start = buffer->silence_pos; + buffer->silence_pos += sizetocopy; + spin_unlock_irqrestore(&woinst->lock, flags); + + sizetocopy_now = buffer->size - start; + + if (sizetocopy > sizetocopy_now) { + sizetocopy -= sizetocopy_now; + fill_block(woinst, start, filldata, sizetocopy_now); + fill_block(woinst, 0, filldata, sizetocopy); + } else { + fill_block(woinst, start, filldata, sizetocopy); + } +} + +/** + * emu10k1_waveout_update - + * + * updates the position of the voice buffer hardware pointer (hw_pos) + * and the number of free bytes on the buffer (free_bytes). + * The free bytes _don't_ include silence bytes that may have been + * added to the buffer. + * + */ +void emu10k1_waveout_update(struct woinst *woinst) +{ + u32 hw_pos; + u32 diff; + + /* There is no actual start yet */ + if (!(woinst->state & WAVE_STATE_STARTED)) { + hw_pos = woinst->buffer.hw_pos; + } else { + /* hw_pos in sample units */ + hw_pos = sblive_readptr(woinst->voice[0].card, CCCA_CURRADDR, woinst->voice[0].num); + + if(hw_pos < woinst->voice[0].start) + hw_pos += woinst->buffer.size / woinst->format.bytespervoicesample - woinst->voice[0].start; + else + hw_pos -= woinst->voice[0].start; + + hw_pos *= woinst->format.bytespervoicesample; + } + + diff = (woinst->buffer.size + hw_pos - woinst->buffer.hw_pos) % woinst->buffer.size; + woinst->total_played += diff; + woinst->buffer.free_bytes += diff; + woinst->buffer.hw_pos = hw_pos; +} diff -Nru a/sound/oss/emu10k1/cardwo.h b/sound/oss/emu10k1/cardwo.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/cardwo.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,98 @@ +/* + ********************************************************************** + * cardwo.h -- header file for card wave out functions + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _CARDWO_H +#define _CARDWO_H + +#include "icardwav.h" +#include "audio.h" +#include "voicemgr.h" +#include "timer.h" + +/* setting this to other than a power of two may break some applications */ +#define WAVEOUT_MAXBUFSIZE MAXBUFSIZE +#define WAVEOUT_MINBUFSIZE 64 + +#define WAVEOUT_DEFAULTFRAGLEN 20 /* Time to play a fragment in ms (latency) */ +#define WAVEOUT_DEFAULTBUFLEN 500 /* Time to play the entire buffer in ms */ + +#define WAVEOUT_MINFRAGSHIFT 6 +#define WAVEOUT_MAXVOICES 6 + +/* waveout_mem is cardwo internal */ +struct waveout_mem { + int emupageindex; + void *addr[BUFMAXPAGES]; + dma_addr_t dma_handle[BUFMAXPAGES]; +}; + +struct waveout_buffer { + u16 ossfragshift; + u32 numfrags; + u32 fragment_size; /* in bytes units */ + u32 size; /* in bytes units */ + u32 pages; /* buffer size in page units*/ + struct waveout_mem mem[WAVEOUT_MAXVOICES]; + u32 silence_pos; /* software cursor position (including silence bytes) */ + u32 hw_pos; /* hardware cursor position */ + u32 free_bytes; /* free bytes available on the buffer (not including silence bytes) */ + u8 fill_silence; + u32 silence_bytes; /* silence bytes on the buffer */ +}; + +struct woinst +{ + u8 state; + u8 num_voices; + struct emu_voice voice[WAVEOUT_MAXVOICES]; + struct emu_timer timer; + struct wave_format format; + struct waveout_buffer buffer; + wait_queue_head_t wait_queue; + u8 mmapped; + u32 total_copied; /* total number of bytes written() to the buffer (excluding silence) */ + u32 total_played; /* total number of bytes played including silence */ + u32 blocks; + u8 device; + spinlock_t lock; +}; + +int emu10k1_waveout_open(struct emu10k1_wavedevice *); +void emu10k1_waveout_close(struct emu10k1_wavedevice *); +void emu10k1_waveout_start(struct emu10k1_wavedevice *); +void emu10k1_waveout_stop(struct emu10k1_wavedevice *); +void emu10k1_waveout_getxfersize(struct woinst*, u32 *); +void emu10k1_waveout_xferdata(struct woinst*, u8*, u32 *); +void emu10k1_waveout_fillsilence(struct woinst*); +int emu10k1_waveout_setformat(struct emu10k1_wavedevice*, struct wave_format*); +void emu10k1_waveout_update(struct woinst*); + +#endif /* _CARDWO_H */ diff -Nru a/sound/oss/emu10k1/ecard.c b/sound/oss/emu10k1/ecard.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/ecard.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,157 @@ +/* + ********************************************************************** + * ecard.c - E-card initialization code + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "ecard.h" +#include "hwaccess.h" + +/* Private routines */ +static void ecard_setadcgain(struct emu10k1_card *, struct ecard_state *, u16); +static void ecard_write(struct emu10k1_card *, u32); + +/************************************************************************** + * @func Set the gain of the ECARD's CS3310 Trim/gain controller. The + * trim value consists of a 16bit value which is composed of two + * 8 bit gain/trim values, one for the left channel and one for the + * right channel. The following table maps from the Gain/Attenuation + * value in decibels into the corresponding bit pattern for a single + * channel. + */ + +static void ecard_setadcgain(struct emu10k1_card *card, struct ecard_state *ecard, u16 gain) +{ + u32 currbit; + ecard->adc_gain = gain; + + /* Enable writing to the TRIM registers */ + ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN); + + /* Do it again to insure that we meet hold time requirements */ + ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN); + + for (currbit = (1L << 15); currbit; currbit >>= 1) { + + u32 value = ecard->control_bits & ~(EC_TRIM_CSN|EC_TRIM_SDATA); + + if (gain & currbit) + value |= EC_TRIM_SDATA; + + /* Clock the bit */ + ecard_write(card, value); + ecard_write(card, value | EC_TRIM_SCLK); + ecard_write(card, value); + } + + ecard_write(card, ecard->control_bits); +} + +/************************************************************************** + * @func Clock bits into the Ecard's control latch. The Ecard uses a + * control latch will is loaded bit-serially by toggling the Modem control + * lines from function 2 on the E8010. This function hides these details + * and presents the illusion that we are actually writing to a distinct + * register. + */ +static void ecard_write(struct emu10k1_card *card, u32 value) +{ + u16 count; + u32 data, hcvalue; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + hcvalue = inl(card->iobase + HCFG) & ~(HOOKN_BIT|HANDN_BIT|PULSEN_BIT); + + outl(card->iobase + HCFG, hcvalue); + + for (count = 0 ; count < EC_NUM_CONTROL_BITS; count++) { + + /* Set up the value */ + data = ((value & 0x1) ? PULSEN_BIT : 0); + value >>= 1; + + outl(card->iobase + HCFG, hcvalue | data); + + /* Clock the shift register */ + outl(card->iobase + HCFG, hcvalue | data | HANDN_BIT); + outl(card->iobase + HCFG, hcvalue | data); + } + + /* Latch the bits */ + outl(card->iobase + HCFG, hcvalue | HOOKN_BIT); + outl(card->iobase + HCFG, hcvalue); + + spin_unlock_irqrestore(&card->lock, flags); +} + +void __devinit emu10k1_ecard_init(struct emu10k1_card *card) +{ + u32 hcvalue; + struct ecard_state ecard; + + /* Set up the initial settings */ + ecard.mux0_setting = EC_DEFAULT_SPDIF0_SEL; + ecard.mux1_setting = EC_DEFAULT_SPDIF1_SEL; + ecard.mux2_setting = 0; + ecard.adc_gain = EC_DEFAULT_ADC_GAIN; + ecard.control_bits = EC_RAW_RUN_MODE | + EC_SPDIF0_SELECT(ecard.mux0_setting) | + EC_SPDIF1_SELECT(ecard.mux1_setting); + + + /* Step 0: Set the codec type in the hardware control register + * and enable audio output */ + hcvalue = emu10k1_readfn0(card, HCFG); + emu10k1_writefn0(card, HCFG, hcvalue | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S); + + /* Step 1: Turn off the led and deassert TRIM_CS */ + ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 2: Calibrate the ADC and DAC */ + ecard_write(card, EC_DACCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 3: Wait for awhile; FIXME: Is this correct? */ + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ); + + /* Step 4: Switch off the DAC and ADC calibration. Note + * That ADC_CAL is actually an inverted signal, so we assert + * it here to stop calibration. */ + ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 4: Switch into run mode */ + ecard_write(card, ecard.control_bits); + + /* Step 5: Set the analog input gain */ + ecard_setadcgain(card, &ecard, ecard.adc_gain); +} + + diff -Nru a/sound/oss/emu10k1/ecard.h b/sound/oss/emu10k1/ecard.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/ecard.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,113 @@ +/* + ********************************************************************** + * ecard.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _ECARD_H +#define _ECARD_H + +#include "8010.h" +#include "hwaccess.h" +#include + +/* In A1 Silicon, these bits are in the HC register */ +#define HOOKN_BIT (1L << 12) +#define HANDN_BIT (1L << 11) +#define PULSEN_BIT (1L << 10) + +#define EC_GDI1 (1 << 13) +#define EC_GDI0 (1 << 14) + +#define EC_NUM_CONTROL_BITS 20 + +#define EC_AC3_DATA_SELN 0x0001L +#define EC_EE_DATA_SEL 0x0002L +#define EC_EE_CNTRL_SELN 0x0004L +#define EC_EECLK 0x0008L +#define EC_EECS 0x0010L +#define EC_EESDO 0x0020L +#define EC_TRIM_CSN 0x0040L +#define EC_TRIM_SCLK 0x0080L +#define EC_TRIM_SDATA 0x0100L +#define EC_TRIM_MUTEN 0x0200L +#define EC_ADCCAL 0x0400L +#define EC_ADCRSTN 0x0800L +#define EC_DACCAL 0x1000L +#define EC_DACMUTEN 0x2000L +#define EC_LEDN 0x4000L + +#define EC_SPDIF0_SEL_SHIFT 15 +#define EC_SPDIF1_SEL_SHIFT 17 +#define EC_SPDIF0_SEL_MASK (0x3L << EC_SPDIF0_SEL_SHIFT) +#define EC_SPDIF1_SEL_MASK (0x7L << EC_SPDIF1_SEL_SHIFT) +#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK) +#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK) +#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory. This should + * be incremented any time the EEPROM's + * format is changed. */ + +#define EC_EEPROM_SIZE 0x40 /* ECARD EEPROM has 64 16-bit words */ + +/* Addresses for special values stored in to EEPROM */ +#define EC_PROM_VERSION_ADDR 0x20 /* Address of the current prom version */ +#define EC_BOARDREV0_ADDR 0x21 /* LSW of board rev */ +#define EC_BOARDREV1_ADDR 0x22 /* MSW of board rev */ + +#define EC_LAST_PROMFILE_ADDR 0x2f + +#define EC_SERIALNUM_ADD 0x30 /* First word of serial number. The number + * can be up to 30 characters in length + * and is stored as a NULL-terminated + * ASCII string. Any unused bytes must be + * filled with zeros */ +#define EC_CHECKSUM_ADDR 0x3f /* Location at which checksum is stored */ + + + +/* Most of this stuff is pretty self-evident. According to the hardware + * dudes, we need to leave the ADCCAL bit low in order to avoid a DC + * offset problem. Weird. + */ +#define EC_RAW_RUN_MODE (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | EC_TRIM_CSN) + + +#define EC_DEFAULT_ADC_GAIN 0xC4C4 +#define EC_DEFAULT_SPDIF0_SEL 0x0 +#define EC_DEFAULT_SPDIF1_SEL 0x4 + +#define HC_EA 0x01L + +/* ECARD state structure. This structure maintains the state + * for various portions of the ECARD's onboard hardware. + */ +struct ecard_state { + u32 control_bits; + u16 adc_gain; + u16 mux0_setting; + u16 mux1_setting; + u16 mux2_setting; +}; + +void emu10k1_ecard_init(struct emu10k1_card *) __devinit; + +#endif /* _ECARD_H */ diff -Nru a/sound/oss/emu10k1/efxmgr.c b/sound/oss/emu10k1/efxmgr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/efxmgr.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,218 @@ +/* + ********************************************************************** + * efxmgr.c + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include +#include "hwaccess.h" +#include "efxmgr.h" + +int emu10k1_find_control_gpr(struct patch_manager *mgr, const char *patch_name, const char *gpr_name) +{ + struct dsp_patch *patch; + struct dsp_rpatch *rpatch; + char s[PATCH_NAME_SIZE + 4]; + u32 *gpr_used; + int i; + + DPD(2, "emu10k1_find_control_gpr(): %s %s\n", patch_name, gpr_name); + + rpatch = &mgr->rpatch; + if (!strcmp(rpatch->name, patch_name)) { + gpr_used = rpatch->gpr_used; + goto match; + } + + for (i = 0; i < mgr->current_pages * PATCHES_PER_PAGE; i++) { + patch = PATCH(mgr, i); + sprintf(s,"%s", patch->name); + + if (!strcmp(s, patch_name)) { + gpr_used = patch->gpr_used; + goto match; + } + } + + return -1; + + match: + for (i = 0; i < NUM_GPRS; i++) + if (mgr->gpr[i].type == GPR_TYPE_CONTROL && + test_bit(i, gpr_used) && + !strcmp(mgr->gpr[i].name, gpr_name)) + return i; + + return -1; +} + +void emu10k1_set_control_gpr(struct emu10k1_card *card, int addr, s32 val, int flag) +{ + struct patch_manager *mgr = &card->mgr; + + DPD(2, "emu10k1_set_control_gpr(): %d %x\n", addr, val); + + if (addr < 0 || addr >= NUM_GPRS) + return; + + if (flag) + val += sblive_readptr(card, GPR_BASE + addr, 0); + + if (val > mgr->gpr[addr].max) + val = mgr->gpr[addr].max; + else if (val < mgr->gpr[addr].min) + val = mgr->gpr[addr].min; + + sblive_writeptr(card, GPR_BASE + addr, 0, val); +} + +//TODO: make this configurable: +#define VOLCTRL_CHANNEL SOUND_MIXER_VOLUME +#define VOLCTRL_STEP_SIZE 5 + +//An internal function for setting OSS mixer controls. +void emu10k1_set_oss_vol(struct emu10k1_card *card, int oss_mixer, + unsigned int left, unsigned int right) +{ + extern char volume_params[SOUND_MIXER_NRDEVICES]; + + card->ac97.mixer_state[oss_mixer] = (right << 8) | left; + + if (!card->isaps) + 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]); + + emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, + volume_params[oss_mixer]); +} + +//FIXME: mute should unmute when pressed a second time +void emu10k1_mute_irqhandler(struct emu10k1_card *card) +{ + int oss_channel = VOLCTRL_CHANNEL; + int left, right; + static int val = 0; + + if (val) { + left = val & 0xff; + right = (val >> 8) & 0xff; + val = 0; + } else { + val = card->ac97.mixer_state[oss_channel]; + left = 0; + right = 0; + } + + emu10k1_set_oss_vol(card, oss_channel, left, right); +} + +void emu10k1_volincr_irqhandler(struct emu10k1_card *card) +{ + 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; + + if ((left += VOLCTRL_STEP_SIZE) > 100) + left = 100; + + if ((right += VOLCTRL_STEP_SIZE) > 100) + right = 100; + + emu10k1_set_oss_vol(card, oss_channel, left, right); +} + +void emu10k1_voldecr_irqhandler(struct emu10k1_card *card) +{ + 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; + + if ((left -= VOLCTRL_STEP_SIZE) < 0) + left = 0; + + if ((right -= VOLCTRL_STEP_SIZE) < 0) + right = 0; + + emu10k1_set_oss_vol(card, oss_channel, left, right); +} + +void emu10k1_set_volume_gpr(struct emu10k1_card *card, int addr, s32 vol, int scale) +{ + struct patch_manager *mgr = &card->mgr; + unsigned long flags; + int muting; + + const s32 log2lin[5] ={ // attenuation (dB) + 0x7fffffff, // 0.0 + 0x7fffffff * 0.840896415253715 , // 1.5 + 0x7fffffff * 0.707106781186548, // 3.0 + 0x7fffffff * 0.594603557501361 , // 4.5 + }; + + if (addr < 0) + return; + + muting = (scale == 0x10) ? 0x7f: scale; + + vol = (100 - vol ) * scale / 100; + + // Thanks to the comp.dsp newsgroup for this neat trick: + vol = (vol >= muting) ? 0 : (log2lin[vol & 3] >> (vol >> 2)); + + spin_lock_irqsave(&mgr->lock, flags); + emu10k1_set_control_gpr(card, addr, vol, 0); + spin_unlock_irqrestore(&mgr->lock, flags); +} + +void emu10k1_dsp_irqhandler(struct emu10k1_card *card) +{ + unsigned long flags; + + if (card->pt.state != PT_STATE_INACTIVE) { + u32 bc; + bc = sblive_readptr(card, GPR_BASE + card->pt.intr_gpr, 0); + if (bc != 0) { + DPD(3, "pt interrupt, bc = %d\n", bc); + spin_lock_irqsave(&card->pt.lock, flags); + card->pt.blocks_played = bc; + if (card->pt.blocks_played >= card->pt.blocks_copied) { + DPF(1, "buffer underrun in passthrough playback\n"); + emu10k1_pt_stop(card); + } + wake_up_interruptible(&card->pt.wait); + spin_unlock_irqrestore(&card->pt.lock, flags); + } + } +} + diff -Nru a/sound/oss/emu10k1/efxmgr.h b/sound/oss/emu10k1/efxmgr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/efxmgr.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,254 @@ +/* + ********************************************************************** + * sblive_fx.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _EFXMGR_H +#define _EFXMGR_H + +#define WRITE_EFX(a, b, c) sblive_writeptr((a), MICROCODEBASE + (b), 0, (c)) + +#define OP(op, z, w, x, y) \ + do { WRITE_EFX(card, (pc) * 2, ((x) << 10) | (y)); \ + WRITE_EFX(card, (pc) * 2 + 1, ((op) << 20) | ((z) << 10) | (w)); \ + ++pc; } while (0) + +#define NUM_INPUTS 0x20 +#define NUM_OUTPUTS 0x20 +#define NUM_GPRS 0x100 +#define GPR_NAME_SIZE 32 +#define PATCH_NAME_SIZE 32 + +struct dsp_rpatch { + char name[PATCH_NAME_SIZE]; + u16 code_start; + u16 code_size; + + u32 gpr_used[NUM_GPRS / 32]; + u32 gpr_input[NUM_GPRS / 32]; + u32 route[NUM_OUTPUTS]; + u32 route_v[NUM_OUTPUTS]; +}; + +struct dsp_patch { + char name[PATCH_NAME_SIZE]; + u8 id; + u32 input; /* bitmap of the lines used as inputs */ + u32 output; /* bitmap of the lines used as outputs */ + u16 code_start; + u16 code_size; + + u32 gpr_used[NUM_GPRS / 32]; /* bitmap of used gprs */ + u32 gpr_input[NUM_GPRS / 32]; + u8 traml_istart; /* starting address of the internal tram lines used */ + u8 traml_isize; /* number of internal tram lines used */ + + u8 traml_estart; + u8 traml_esize; + + u16 tramb_istart; /* starting address of the internal tram memory used */ + u16 tramb_isize; /* amount of internal memory used */ + u32 tramb_estart; + u32 tramb_esize; +}; + +struct dsp_gpr { + u8 type; /* gpr type, STATIC, DYNAMIC, INPUT, OUTPUT, CONTROL */ + char name[GPR_NAME_SIZE]; /* gpr value, only valid for control gprs */ + s32 min, max; /* value range for this gpr, only valid for control gprs */ + u8 line; /* which input/output line is the gpr attached, only valid for input/output gprs */ + u8 usage; +}; + +enum { + GPR_TYPE_NULL = 0, + GPR_TYPE_IO, + GPR_TYPE_STATIC, + GPR_TYPE_DYNAMIC, + GPR_TYPE_CONTROL, + GPR_TYPE_CONSTANT +}; + +#define GPR_BASE 0x100 +#define OUTPUT_BASE 0x20 + +#define MAX_PATCHES_PAGES 32 + +struct patch_manager { + void *patch[MAX_PATCHES_PAGES]; + int current_pages; + struct dsp_rpatch rpatch; + struct dsp_gpr gpr[NUM_GPRS]; /* gpr usage table */ + spinlock_t lock; + s16 ctrl_gpr[SOUND_MIXER_NRDEVICES][2]; +}; + + +#define PATCHES_PER_PAGE (PAGE_SIZE / sizeof(struct dsp_patch)) + +#define PATCH(mgr, i) ((struct dsp_patch *) (mgr)->patch[(i) / PATCHES_PER_PAGE] + (i) % PATCHES_PER_PAGE) + +/* PCM volume control */ +#define TMP_PCM_L 0x100 //temp PCM L (after the vol control) +#define TMP_PCM_R 0x101 +#define VOL_PCM_L 0x102 //vol PCM +#define VOL_PCM_R 0x103 + +/* Routing patch */ +#define TMP_AC_L 0x104 //tmp ac97 out +#define TMP_AC_R 0x105 +#define TMP_REAR_L 0x106 //output - Temp Rear +#define TMP_REAR_R 0x107 +#define TMP_DIGI_L 0x108 //output - Temp digital +#define TMP_DIGI_R 0x109 +#define DSP_VOL_L 0x10a // main dsp volume +#define DSP_VOL_R 0x10b + +/* hw inputs */ +#define PCM_IN_L 0x00 +#define PCM_IN_R 0x01 + +#define PCM1_IN_L 0x04 +#define PCM1_IN_R 0x05 +//mutilchannel playback stream appear here: + +#define MULTI_FRONT_L 0x08 +#define MULTI_FRONT_R 0x09 +#define MULTI_REAR_L 0x0a +#define MULTI_REAR_R 0x0b +#define MULTI_CENTER 0x0c +#define MULTI_LFE 0x0d + +#define AC97_IN_L 0x10 +#define AC97_IN_R 0x11 +#define SPDIF_CD_L 0x12 +#define SPDIF_CD_R 0x13 + +/* hw outputs */ +#define AC97_FRONT_L 0x20 +#define AC97_FRONT_R 0x21 +#define DIGITAL_OUT_L 0x22 +#define DIGITAL_OUT_R 0x23 +#define DIGITAL_CENTER 0x24 +#define DIGITAL_LFE 0x25 + +#define ANALOG_REAR_L 0x28 +#define ANALOG_REAR_R 0x29 +#define ADC_REC_L 0x2a +#define ADC_REC_R 0x2b + +#define ANALOG_CENTER 0x31 +#define ANALOG_LFE 0x32 + + +#define INPUT_PATCH_START(patch, nm, ln, i) \ +do { \ + patch = PATCH(mgr, patch_n); \ + strcpy(patch->name, nm); \ + patch->code_start = pc * 2; \ + patch->input = (1<<(0x1f&ln)); \ + patch->output= (1<<(0x1f&ln)); \ + patch->id = i; \ +} while(0) + +#define INPUT_PATCH_END(patch) \ +do { \ + patch->code_size = pc * 2 - patch->code_start; \ + patch_n++; \ +} while(0) + + +#define ROUTING_PATCH_START(patch, nm) \ +do { \ + patch = &mgr->rpatch; \ + strcpy(patch->name, nm); \ + patch->code_start = pc * 2; \ +} while(0) + +#define ROUTING_PATCH_END(patch) \ +do { \ + patch->code_size = pc * 2 - patch->code_start; \ +} while(0) + +#define CONNECT(input, output) set_bit(input, &rpatch->route[(output) - OUTPUT_BASE]); + +#define CONNECT_V(input, output) set_bit(input, &rpatch->route_v[(output) - OUTPUT_BASE]); + +#define OUTPUT_PATCH_START(patch, nm, ln, i) \ +do { \ + patch = PATCH(mgr, patch_n); \ + strcpy(patch->name, nm); \ + patch->code_start = pc * 2; \ + patch->input = (1<<(0x1f&ln)); \ + patch->output= (1<<(0x1f&ln)); \ + patch->id = i; \ +} while(0) + +#define OUTPUT_PATCH_END(patch) \ +do { \ + patch->code_size = pc * 2 - patch->code_start; \ + patch_n++; \ +} while(0) + +#define GET_OUTPUT_GPR(patch, g, ln) \ +do { \ + mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_IO; \ + mgr->gpr[(g) - GPR_BASE].usage++; \ + mgr->gpr[(g) - GPR_BASE].line = ln; \ + set_bit((g) - GPR_BASE, patch->gpr_used); \ +} while(0) + +#define GET_INPUT_GPR(patch, g, ln) \ +do { \ + mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_IO; \ + mgr->gpr[(g) - GPR_BASE].usage++; \ + mgr->gpr[(g) - GPR_BASE].line = ln; \ + set_bit((g) - GPR_BASE, patch->gpr_used); \ + set_bit((g) - GPR_BASE, patch->gpr_input); \ +} while(0) + +#define GET_DYNAMIC_GPR(patch, g) \ +do { \ + mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_DYNAMIC; \ + mgr->gpr[(g) - GPR_BASE].usage++; \ + set_bit((g) - GPR_BASE, patch->gpr_used); \ +} while(0) + +#define GET_CONTROL_GPR(patch, g, nm, a, b) \ +do { \ + strcpy(mgr->gpr[(g) - GPR_BASE].name, nm); \ + mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_CONTROL; \ + mgr->gpr[(g) - GPR_BASE].usage++; \ + mgr->gpr[(g) - GPR_BASE].min = a; \ + mgr->gpr[(g) - GPR_BASE].max = b; \ + sblive_writeptr(card, g, 0, b); \ + set_bit((g) - GPR_BASE, patch->gpr_used); \ +} while(0) + +#endif /* _EFXMGR_H */ diff -Nru a/sound/oss/emu10k1/emuadxmg.c b/sound/oss/emu10k1/emuadxmg.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/emuadxmg.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,104 @@ + +/* + ********************************************************************** + * emuadxmg.c - Address space manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" + +/* Allocates emu address space */ + +int emu10k1_addxmgr_alloc(u32 size, struct emu10k1_card *card) +{ + u16 *pagetable = card->emupagetable; + u16 index = 0; + u16 numpages; + unsigned long flags; + + /* Convert bytes to pages */ + numpages = (size / EMUPAGESIZE) + ((size % EMUPAGESIZE) ? 1 : 0); + + spin_lock_irqsave(&card->lock, flags); + + while (index < (MAXPAGES - 1)) { + if (pagetable[index] & 0x8000) { + /* This block of pages is in use, jump to the start of the next block. */ + index += (pagetable[index] & 0x7fff); + } else { + /* Found free block */ + if (pagetable[index] >= numpages) { + + /* Block is large enough */ + + /* If free block is larger than the block requested + * then adjust the size of the block remaining */ + if (pagetable[index] > numpages) + pagetable[index + numpages] = pagetable[index] - numpages; + + pagetable[index] = (numpages | 0x8000); /* Mark block as used */ + + spin_unlock_irqrestore(&card->lock, flags); + + return index; + } else { + /* Block too small, jump to the start of the next block */ + index += pagetable[index]; + } + } + } + + spin_unlock_irqrestore(&card->lock, flags); + + return -1; +} + +/* Frees a previously allocated emu address space. */ + +void emu10k1_addxmgr_free(struct emu10k1_card *card, int index) +{ + u16 *pagetable = card->emupagetable; + u16 origsize = 0; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + if (pagetable[index] & 0x8000) { + /* Block is allocated - mark block as free */ + origsize = pagetable[index] & 0x7fff; + pagetable[index] = origsize; + + /* If next block is free, we concat both blocks */ + if (!(pagetable[index + origsize] & 0x8000)) + pagetable[index] += pagetable[index + origsize] & 0x7fff; + } + + spin_unlock_irqrestore(&card->lock, flags); + + return; +} diff -Nru a/sound/oss/emu10k1/hwaccess.c b/sound/oss/emu10k1/hwaccess.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/hwaccess.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,480 @@ +/* + ********************************************************************** + * hwaccess.c -- Hardware access layer + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * December 9, 1999 Jon Taylor rewrote the I/O subsystem + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include + +#include "hwaccess.h" +#include "8010.h" +#include "icardmid.h" + +/************************************************************************* +* Function : srToPitch * +* Input : sampleRate - sampling rate * +* Return : pitch value * +* About : convert sampling rate to pitch * +* Note : for 8010, sampling rate is at 48kHz, this function should * +* be changed. * +*************************************************************************/ +u32 srToPitch(u32 sampleRate) +{ + int i; + + /* FIXME: These tables should be defined in a headerfile */ + static u32 logMagTable[128] = { + 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, + 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, + 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, + 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, + 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, + 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, + 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, + 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, + 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, + 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, + 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, + 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, + 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, + 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, + 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, + 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df + }; + + static char logSlopeTable[128] = { + 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, + 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, + 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, + 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, + 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, + 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, + 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, + 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, + 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, + 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, + 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, + 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f + }; + + if (sampleRate == 0) + return 0; /* Bail out if no leading "1" */ + + sampleRate *= 11185; /* Scale 48000 to 0x20002380 */ + + for (i = 31; i > 0; i--) { + if (sampleRate & 0x80000000) { /* Detect leading "1" */ + return (u32) (((s32) (i - 15) << 20) + + logMagTable[0x7f & (sampleRate >> 24)] + + (0x7f & (sampleRate >> 17)) * logSlopeTable[0x7f & (sampleRate >> 24)]); + } + sampleRate = sampleRate << 1; + } + + DPF(2, "srToPitch: BUG!\n"); + return 0; /* Should never reach this point */ +} + +/* Returns an attenuation based upon a cumulative volume value */ + +/* Algorithm calculates 0x200 - 0x10 log2 (input) */ +u8 sumVolumeToAttenuation(u32 value) +{ + u16 count = 16; + s16 ans; + + if (value == 0) + return 0xFF; + + /* Find first SET bit. This is the integer part of the value */ + while ((value & 0x10000) == 0) { + value <<= 1; + count--; + } + + /* The REST of the data is the fractional part. */ + ans = (s16) (0x110 - ((count << 4) + ((value & 0x0FFFFL) >> 12))); + if (ans > 0xFF) + ans = 0xFF; + + return (u8) ans; +} + +/******************************************* +* write/read PCI function 0 registers * +********************************************/ +void emu10k1_writefn0(struct emu10k1_card *card, u32 reg, u32 data) +{ + unsigned long flags; + + if (reg & 0xff000000) { + u32 mask; + u8 size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + data = (data << offset) & mask; + reg &= 0x7f; + + spin_lock_irqsave(&card->lock, flags); + data |= inl(card->iobase + reg) & ~mask; + outl(data, card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + } else { + spin_lock_irqsave(&card->lock, flags); + outl(data, card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + } + + return; +} + +u32 emu10k1_readfn0(struct emu10k1_card * card, u32 reg) +{ + u32 val; + unsigned long flags; + + if (reg & 0xff000000) { + u32 mask; + u8 size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + reg &= 0x7f; + + spin_lock_irqsave(&card->lock, flags); + val = inl(card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + + return (val & mask) >> offset; + } else { + spin_lock_irqsave(&card->lock, flags); + val = inl(card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + return val; + } +} + +/************************************************************************ +* write/read Emu10k1 pointer-offset register set, accessed through * +* the PTR and DATA registers * +*************************************************************************/ +void sblive_writeptr(struct emu10k1_card *card, u32 reg, u32 channel, u32 data) +{ + u32 regptr; + unsigned long flags; + + regptr = ((reg << 16) & PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) { + u32 mask; + u8 size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + data = (data << offset) & mask; + + spin_lock_irqsave(&card->lock, flags); + outl(regptr, card->iobase + PTR); + data |= inl(card->iobase + DATA) & ~mask; + outl(data, card->iobase + DATA); + spin_unlock_irqrestore(&card->lock, flags); + } else { + spin_lock_irqsave(&card->lock, flags); + outl(regptr, card->iobase + PTR); + outl(data, card->iobase + DATA); + spin_unlock_irqrestore(&card->lock, flags); + } +} + +/* ... : data, reg, ... , TAGLIST_END */ +void sblive_writeptr_tag(struct emu10k1_card *card, u32 channel, ...) +{ + va_list args; + + unsigned long flags; + u32 reg; + + va_start(args, channel); + + spin_lock_irqsave(&card->lock, flags); + while ((reg = va_arg(args, u32)) != TAGLIST_END) { + u32 data = va_arg(args, u32); + u32 regptr = (((reg << 16) & PTR_ADDRESS_MASK) + | (channel & PTR_CHANNELNUM_MASK)); + outl(regptr, card->iobase + PTR); + if (reg & 0xff000000) { + int size = (reg >> 24) & 0x3f; + int offset = (reg >> 16) & 0x1f; + u32 mask = ((1 << size) - 1) << offset; + data = (data << offset) & mask; + + data |= inl(card->iobase + DATA) & ~mask; + } + outl(data, card->iobase + DATA); + } + spin_unlock_irqrestore(&card->lock, flags); + + va_end(args); + + return; +} + +u32 sblive_readptr(struct emu10k1_card * card, u32 reg, u32 channel) +{ + u32 regptr, val; + unsigned long flags; + + regptr = ((reg << 16) & PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) { + u32 mask; + u8 size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + + spin_lock_irqsave(&card->lock, flags); + outl(regptr, card->iobase + PTR); + val = inl(card->iobase + DATA); + spin_unlock_irqrestore(&card->lock, flags); + + return (val & mask) >> offset; + } else { + spin_lock_irqsave(&card->lock, flags); + outl(regptr, card->iobase + PTR); + val = inl(card->iobase + DATA); + spin_unlock_irqrestore(&card->lock, flags); + + return val; + } +} + +void emu10k1_irq_enable(struct emu10k1_card *card, u32 irq_mask) +{ + u32 val; + unsigned long flags; + + DPF(2,"emu10k1_irq_enable()\n"); + + spin_lock_irqsave(&card->lock, flags); + val = inl(card->iobase + INTE) | irq_mask; + outl(val, card->iobase + INTE); + spin_unlock_irqrestore(&card->lock, flags); + return; +} + +void emu10k1_irq_disable(struct emu10k1_card *card, u32 irq_mask) +{ + u32 val; + unsigned long flags; + + DPF(2,"emu10k1_irq_disable()\n"); + + spin_lock_irqsave(&card->lock, flags); + val = inl(card->iobase + INTE) & ~irq_mask; + outl(val, card->iobase + INTE); + spin_unlock_irqrestore(&card->lock, flags); + return; +} + +void emu10k1_set_stop_on_loop(struct emu10k1_card *card, u32 voicenum) +{ + /* Voice interrupt */ + if (voicenum >= 32) + sblive_writeptr(card, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 1); + else + sblive_writeptr(card, SOLEL | ((0x0100 | voicenum) << 16), 0, 1); + + return; +} + +void emu10k1_clear_stop_on_loop(struct emu10k1_card *card, u32 voicenum) +{ + /* Voice interrupt */ + if (voicenum >= 32) + sblive_writeptr(card, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 0); + else + sblive_writeptr(card, SOLEL | ((0x0100 | voicenum) << 16), 0, 0); + + return; +} + +static void sblive_wcwait(struct emu10k1_card *card, u32 wait) +{ + volatile unsigned uCount; + u32 newtime = 0, curtime; + + curtime = emu10k1_readfn0(card, WC_SAMPLECOUNTER); + while (wait--) { + uCount = 0; + while (uCount++ < TIMEOUT) { + newtime = emu10k1_readfn0(card, WC_SAMPLECOUNTER); + if (newtime != curtime) + break; + } + + if (uCount >= TIMEOUT) + break; + + curtime = newtime; + } +} + +u16 emu10k1_ac97_read(struct ac97_codec *codec, u8 reg) +{ + struct emu10k1_card *card = codec->private_data; + u16 data; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + outb(reg, card->iobase + AC97ADDRESS); + data = inw(card->iobase + AC97DATA); + + spin_unlock_irqrestore(&card->lock, flags); + + return data; +} + +void emu10k1_ac97_write(struct ac97_codec *codec, u8 reg, u16 value) +{ + struct emu10k1_card *card = codec->private_data; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + outb(reg, card->iobase + AC97ADDRESS); + outw(value, card->iobase + AC97DATA); + + spin_unlock_irqrestore(&card->lock, flags); +} + +/********************************************************* +* MPU access functions * +**********************************************************/ + +int emu10k1_mpu_write_data(struct emu10k1_card *card, u8 data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&card->lock, flags); + + if ((inb(card->iobase + MUSTAT) & MUSTAT_ORDYN) == 0) { + outb(data, card->iobase + MUDATA); + ret = 0; + } else + ret = -1; + + spin_unlock_irqrestore(&card->lock, flags); + + return ret; +} + +int emu10k1_mpu_read_data(struct emu10k1_card *card, u8 * data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&card->lock, flags); + + if ((inb(card->iobase + MUSTAT) & MUSTAT_IRDYN) == 0) { + *data = inb(card->iobase + MUDATA); + ret = 0; + } else + ret = -1; + + spin_unlock_irqrestore(&card->lock, flags); + + return ret; +} + +int emu10k1_mpu_reset(struct emu10k1_card *card) +{ + u8 status; + unsigned long flags; + + DPF(2, "emu10k1_mpu_reset()\n"); + + if (card->mpuacqcount == 0) { + spin_lock_irqsave(&card->lock, flags); + outb(MUCMD_RESET, card->iobase + MUCMD); + spin_unlock_irqrestore(&card->lock, flags); + + sblive_wcwait(card, 8); + + spin_lock_irqsave(&card->lock, flags); + outb(MUCMD_RESET, card->iobase + MUCMD); + spin_unlock_irqrestore(&card->lock, flags); + + sblive_wcwait(card, 8); + + spin_lock_irqsave(&card->lock, flags); + outb(MUCMD_ENTERUARTMODE, card->iobase + MUCMD); + spin_unlock_irqrestore(&card->lock, flags); + + sblive_wcwait(card, 8); + + spin_lock_irqsave(&card->lock, flags); + status = inb(card->iobase + MUDATA); + spin_unlock_irqrestore(&card->lock, flags); + + if (status == 0xfe) + return 0; + else + return -1; + } + + return 0; +} + +int emu10k1_mpu_acquire(struct emu10k1_card *card) +{ + /* FIXME: This should be a macro */ + ++card->mpuacqcount; + + return 0; +} + +int emu10k1_mpu_release(struct emu10k1_card *card) +{ + /* FIXME: this should be a macro */ + --card->mpuacqcount; + + return 0; +} diff -Nru a/sound/oss/emu10k1/hwaccess.h b/sound/oss/emu10k1/hwaccess.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/hwaccess.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,239 @@ +/* + ********************************************************************** + * hwaccess.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _HWACCESS_H +#define _HWACCESS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "efxmgr.h" +#include "passthrough.h" +#include "midi.h" + +#define EMUPAGESIZE 4096 /* don't change */ +#define NUM_G 64 /* use all channels */ +#define NUM_FXSENDS 4 /* don't change */ +/* setting this to other than a power of two may break some applications */ +#define MAXBUFSIZE 65536 +#define MAXPAGES 8192 +#define BUFMAXPAGES (MAXBUFSIZE / PAGE_SIZE) + +#define FLAGS_AVAILABLE 0x0001 +#define FLAGS_READY 0x0002 + +struct memhandle +{ + dma_addr_t dma_handle; + void *addr; + u32 size; +}; + +#define DEBUG_LEVEL 2 + +#ifdef EMU10K1_DEBUG +# define DPD(level,x,y...) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ , y );} while(0) +# define DPF(level,x) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ );} while(0) +#else +# define DPD(level,x,y...) do { } while (0) /* not debugging: nothing */ +# define DPF(level,x) do { } while (0) +#endif /* EMU10K1_DEBUG */ + +#define ERROR() DPF(1,"error\n") + +/* DATA STRUCTURES */ + +struct emu10k1_waveout +{ + u16 send_routing[3]; + + u8 send_a[3]; + u8 send_b[3]; + u8 send_c[3]; + u8 send_d[3]; +}; + +struct emu10k1_wavein +{ + struct wiinst *ac97; + struct wiinst *mic; + struct wiinst *fx; + + u8 recsrc; + u32 fxwc; +}; + +#define CMD_READ 1 +#define CMD_WRITE 2 + +struct mixer_private_ioctl { + u32 cmd; + u32 val[90]; +}; + +/* bogus ioctls numbers to escape from OSS mixer limitations */ +#define CMD_WRITEFN0 _IOW('D', 0, struct mixer_private_ioctl) +#define CMD_READFN0 _IOR('D', 1, struct mixer_private_ioctl) +#define CMD_WRITEPTR _IOW('D', 2, struct mixer_private_ioctl) +#define CMD_READPTR _IOR('D', 3, struct mixer_private_ioctl) +#define CMD_SETRECSRC _IOW('D', 4, struct mixer_private_ioctl) +#define CMD_GETRECSRC _IOR('D', 5, struct mixer_private_ioctl) +#define CMD_GETVOICEPARAM _IOR('D', 6, struct mixer_private_ioctl) +#define CMD_SETVOICEPARAM _IOW('D', 7, struct mixer_private_ioctl) +#define CMD_GETPATCH _IOR('D', 8, struct mixer_private_ioctl) +#define CMD_GETGPR _IOR('D', 9, struct mixer_private_ioctl) +#define CMD_GETCTLGPR _IOR('D', 10, struct mixer_private_ioctl) +#define CMD_SETPATCH _IOW('D', 11, struct mixer_private_ioctl) +#define CMD_SETGPR _IOW('D', 12, struct mixer_private_ioctl) +#define CMD_SETCTLGPR _IOW('D', 13, struct mixer_private_ioctl) +#define CMD_SETGPOUT _IOW('D', 14, struct mixer_private_ioctl) +#define CMD_GETGPR2OSS _IOR('D', 15, struct mixer_private_ioctl) +#define CMD_SETGPR2OSS _IOW('D', 16, struct mixer_private_ioctl) +#define CMD_SETMCH_FX _IOW('D', 17, struct mixer_private_ioctl) +#define CMD_SETPASSTHROUGH _IOW('D', 18, struct mixer_private_ioctl) +#define CMD_PRIVATE3_VERSION _IOW('D', 19, struct mixer_private_ioctl) + +//up this number when breaking compatibility +#define PRIVATE3_VERSION 1 + +struct emu10k1_card +{ + struct list_head list; + + struct memhandle virtualpagetable; + struct memhandle tankmem; + struct memhandle silentpage; + + spinlock_t lock; + + u8 voicetable[NUM_G]; + u16 emupagetable[MAXPAGES]; + + struct list_head timers; + unsigned timer_delay; + spinlock_t timer_lock; + + struct pci_dev *pci_dev; + unsigned long iobase; + unsigned long length; + unsigned short model; + unsigned int irq; + + int audio_dev; + int audio_dev1; + int midi_dev; +#ifdef EMU10K1_SEQUENCER + int seq_dev; + struct emu10k1_mididevice *seq_mididev; +#endif + + struct ac97_codec ac97; + int ac97_supported_mixers; + int ac97_stereo_mixers; + + /* Number of first fx voice for multichannel output */ + u8 mchannel_fx; + struct emu10k1_waveout waveout; + struct emu10k1_wavein wavein; + struct emu10k1_mpuout *mpuout; + struct emu10k1_mpuin *mpuin; + + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + u32 mpuacqcount; // Mpu acquire count + u32 has_toslink; // TOSLink detection + + u8 chiprev; /* Chip revision */ + + int isaps; + + struct patch_manager mgr; + struct pt_data pt; +}; + +int emu10k1_addxmgr_alloc(u32, struct emu10k1_card *); +void emu10k1_addxmgr_free(struct emu10k1_card *, int); + + + +int emu10k1_find_control_gpr(struct patch_manager *, const char *, const char *); +void emu10k1_set_control_gpr(struct emu10k1_card *, int , s32, int ); + +void emu10k1_set_volume_gpr(struct emu10k1_card *, int, s32, int); + + +#define VOL_6BIT 0x40 +#define VOL_5BIT 0x20 +#define VOL_4BIT 0x10 + +#define TIMEOUT 16384 + +u32 srToPitch(u32); +u8 sumVolumeToAttenuation(u32); + +extern struct list_head emu10k1_devs; + +/* Hardware Abstraction Layer access functions */ + +void emu10k1_writefn0(struct emu10k1_card *, u32 , u32 ); +u32 emu10k1_readfn0(struct emu10k1_card *, u32 ); + +void sblive_writeptr(struct emu10k1_card *, u32 , u32 , u32 ); +void sblive_writeptr_tag(struct emu10k1_card *card, u32 channel, ...); +#define TAGLIST_END 0 + +u32 sblive_readptr(struct emu10k1_card *, u32 , u32 ); + +void emu10k1_irq_enable(struct emu10k1_card *, u32); +void emu10k1_irq_disable(struct emu10k1_card *, u32); +void emu10k1_set_stop_on_loop(struct emu10k1_card *, u32); +void emu10k1_clear_stop_on_loop(struct emu10k1_card *, u32); + +/* AC97 Codec register access function */ +u16 emu10k1_ac97_read(struct ac97_codec *, u8); +void emu10k1_ac97_write(struct ac97_codec *, u8, u16); + +/* MPU access function*/ +int emu10k1_mpu_write_data(struct emu10k1_card *, u8); +int emu10k1_mpu_read_data(struct emu10k1_card *, u8 *); +int emu10k1_mpu_reset(struct emu10k1_card *); +int emu10k1_mpu_acquire(struct emu10k1_card *); +int emu10k1_mpu_release(struct emu10k1_card *); + +#endif /* _HWACCESS_H */ diff -Nru a/sound/oss/emu10k1/icardmid.h b/sound/oss/emu10k1/icardmid.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/icardmid.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,163 @@ +/* + ********************************************************************** + * isblive_mid.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _ICARDMIDI_H +#define _ICARDMIDI_H + +/* MIDI defines */ +#define MIDI_DATA_FIRST 0x00 +#define MIDI_DATA_LAST 0x7F +#define MIDI_STATUS_FIRST 0x80 +#define MIDI_STATUS_LAST 0xFF + +/* Channel status bytes */ +#define MIDI_STATUS_CHANNEL_FIRST 0x80 +#define MIDI_STATUS_CHANNEL_LAST 0xE0 +#define MIDI_STATUS_CHANNEL_MASK 0xF0 + +/* Channel voice messages */ +#define MIDI_VOICE_NOTE_OFF 0x80 +#define MIDI_VOICE_NOTE_ON 0x90 +#define MIDI_VOICE_POLY_PRESSURE 0xA0 +#define MIDI_VOICE_CONTROL_CHANGE 0xB0 +#define MIDI_VOICE_PROGRAM_CHANGE 0xC0 +#define MIDI_VOICE_CHANNEL_PRESSURE 0xD0 +#define MIDI_VOICE_PITCH_BEND 0xE0 + +/* Channel mode messages */ +#define MIDI_MODE_CHANNEL MIDI_VOICE_CONTROL_CHANGE + +/* System status bytes */ +#define MIDI_STATUS_SYSTEM_FIRST 0xF0 +#define MIDI_STATUS_SYSTEM_LAST 0xFF + +/* System exclusive messages */ +#define MIDI_SYSEX_BEGIN 0xF0 +#define MIDI_SYSEX_EOX 0xF7 + +/* System common messages */ +#define MIDI_COMMON_TCQF 0xF1 /* Time code quarter frame */ +#define MIDI_COMMON_SONG_POSITION 0xF2 +#define MIDI_COMMON_SONG_SELECT 0xF3 +#define MIDI_COMMON_UNDEFINED_F4 0xF4 +#define MIDI_COMMON_UNDEFINED_F5 0xF5 +#define MIDI_COMMON_TUNE_REQUEST 0xF6 + +/* System real-time messages */ +#define MIDI_RTIME_TIMING_CLOCK 0xF8 +#define MIDI_RTIME_UNDEFINED_F9 0xF9 +#define MIDI_RTIME_START 0xFA +#define MIDI_RTIME_CONTINUE 0xFB +#define MIDI_RTIME_STOP 0xFC +#define MIDI_RTIME_UNDEFINED_FD 0xFD +#define MIDI_RTIME_ACTIVE_SENSING 0xFE +#define MIDI_RTIME_SYSTEM_RESET 0xFF + +/* Flags for flags parm of midiOutCachePatches(), midiOutCacheDrumPatches() */ +#define MIDI_CACHE_ALL 1 +#define MIDI_CACHE_BESTFIT 2 +#define MIDI_CACHE_QUERY 3 +#define MIDI_UNCACHE 4 + +/* Event declarations for MPU IRQ Callbacks */ +#define ICARDMIDI_INLONGDATA 0x00000001 /* MIM_LONGDATA */ +#define ICARDMIDI_INLONGERROR 0x00000002 /* MIM_LONGERROR */ +#define ICARDMIDI_OUTLONGDATA 0x00000004 /* MOM_DONE for MPU OUT buffer */ +#define ICARDMIDI_INDATA 0x00000010 /* MIM_DATA */ +#define ICARDMIDI_INDATAERROR 0x00000020 /* MIM_ERROR */ + +/* Declaration for flags in CARDMIDIBUFFERHDR */ +/* Make it the same as MHDR_DONE, MHDR_INQUEUE in mmsystem.h */ +#define MIDIBUF_DONE 0x00000001 +#define MIDIBUF_INQUEUE 0x00000004 + +/* Declaration for msg parameter in midiCallbackFn */ +#define ICARDMIDI_OUTBUFFEROK 0x00000001 +#define ICARDMIDI_INMIDIOK 0x00000002 + +/* Declaration for technology in struct midi_caps */ +#define MT_MIDIPORT 0x00000001 /* In original MIDIOUTCAPS structure */ +#define MT_FMSYNTH 0x00000004 /* In original MIDIOUTCAPS structure */ +#define MT_AWESYNTH 0x00001000 +#define MT_PCISYNTH 0x00002000 +#define MT_PCISYNTH64 0x00004000 +#define CARDMIDI_AWEMASK 0x0000F000 + +enum LocalErrorCode +{ + CTSTATUS_NOTENABLED = 0x7000, + CTSTATUS_READY, + CTSTATUS_BUSY, + CTSTATUS_DATAAVAIL, + CTSTATUS_NODATA, + CTSTATUS_NEXT_BYTE +}; + +/* MIDI data block header */ +struct midi_hdr +{ + u8 *reserved; /* Pointer to original locked data block */ + u32 bufferlength; /* Length of data in data block */ + u32 bytesrecorded; /* Used for input only */ + u32 user; /* For client's use */ + u32 flags; /* Assorted flags (see defines) */ + struct list_head list; /* Reserved for driver */ + u8 *data; /* Second copy of first pointer */ +}; + +/* Enumeration for SetControl */ +enum +{ + MIDIOBJVOLUME = 0x1, + MIDIQUERYACTIVEINST +}; + +struct midi_queue +{ + struct midi_queue *next; + u32 qtype; /* 0 = short message, 1 = long data */ + u32 length; + u32 sizeLeft; + u8 *midibyte; + unsigned long refdata; +}; + +struct midi_openinfo +{ + u32 cbsize; + u32 flags; + unsigned long refdata; + u32 streamid; +}; + +int emu10k1_midi_callback(unsigned long , unsigned long, unsigned long *); + +#endif /* _ICARDMIDI_H */ diff -Nru a/sound/oss/emu10k1/icardwav.h b/sound/oss/emu10k1/icardwav.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/icardwav.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,53 @@ +/* + ********************************************************************** + * icardwav.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _ICARDWAV_H +#define _ICARDWAV_H + +struct wave_format +{ + int id; + int samplingrate; + u8 bitsperchannel; + u8 channels; /* 1 = Mono, 2 = Stereo, 3, ... = Multichannel */ + u8 bytesperchannel; + u8 bytespervoicesample; + u8 bytespersample; + int bytespersec; + u8 passthrough; +}; + +/* emu10k1_wave states */ +#define WAVE_STATE_OPEN 0x01 +#define WAVE_STATE_STARTED 0x02 +#define WAVE_STATE_CLOSED 0x04 + +#endif /* _ICARDWAV_H */ diff -Nru a/sound/oss/emu10k1/irqmgr.c b/sound/oss/emu10k1/irqmgr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/irqmgr.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,104 @@ + +/* + ********************************************************************** + * irqmgr.c - IRQ manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "8010.h" +#include "cardmi.h" +#include "cardmo.h" +#include "irqmgr.h" + +/* Interrupt handler */ + +void emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct emu10k1_card *card = (struct emu10k1_card *) dev_id; + u32 irqstatus; + + DPD(4, "emu10k1_interrupt called, irq = %u\n", irq); + + /* + ** NOTE : + ** We do a 'while loop' here cos on certain machines, with both + ** playback and recording going on at the same time, IRQs will + ** stop coming in after a while. Checking IPND indeed shows that + ** there are interrupts pending but the PIC says no IRQs pending. + ** I suspect that some boards need edge-triggered IRQs but are not + ** getting that condition if we don't completely clear the IPND + ** (make sure no more interrupts are pending). + ** - Eric + */ + + while ((irqstatus = inl(card->iobase + IPR))) { + DPD(4, "irq status %#x\n", irqstatus); + + /* acknowledge interrupt */ + outl(irqstatus, card->iobase + IPR); + + if (irqstatus & IRQTYPE_TIMER) { + emu10k1_timer_irqhandler(card); + irqstatus &= ~IRQTYPE_TIMER; + } + + if (irqstatus & IRQTYPE_DSP) { + emu10k1_dsp_irqhandler(card); + irqstatus &= ~IRQTYPE_DSP; + } + + if (irqstatus & IRQTYPE_MPUIN) { + emu10k1_mpuin_irqhandler(card); + irqstatus &= ~IRQTYPE_MPUIN; + } + + if (irqstatus & IRQTYPE_MPUOUT) { + emu10k1_mpuout_irqhandler(card); + irqstatus &= ~IRQTYPE_MPUOUT; + } + + if (irqstatus & IPR_MUTE) { + emu10k1_mute_irqhandler(card); + irqstatus &=~IPR_MUTE; + } + + if (irqstatus & IPR_VOLINCR) { + emu10k1_volincr_irqhandler(card); + irqstatus &=~IPR_VOLINCR; + } + + if (irqstatus & IPR_VOLDECR) { + emu10k1_voldecr_irqhandler(card); + irqstatus &=~IPR_VOLDECR; + } + + if (irqstatus) + emu10k1_irq_disable(card, irqstatus); + } +} diff -Nru a/sound/oss/emu10k1/irqmgr.h b/sound/oss/emu10k1/irqmgr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/irqmgr.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,52 @@ +/* + ********************************************************************** + * irq.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _IRQ_H +#define _IRQ_H + +/* EMU Irq Types */ +#define IRQTYPE_PCIBUSERROR IPR_PCIERROR +#define IRQTYPE_MIXERBUTTON (IPR_VOLINCR | IPR_VOLDECR | IPR_MUTE) +#define IRQTYPE_VOICE (IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK) +#define IRQTYPE_RECORD (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL | IPR_MICBUFFULL | IPR_MICBUFHALFFULL | IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL) +#define IRQTYPE_MPUOUT IPR_MIDITRANSBUFEMPTY +#define IRQTYPE_MPUIN IPR_MIDIRECVBUFEMPTY +#define IRQTYPE_TIMER IPR_INTERVALTIMER +#define IRQTYPE_SPDIF (IPR_GPSPDIFSTATUSCHANGE | IPR_CDROMSTATUSCHANGE) +#define IRQTYPE_DSP IPR_FXDSP + +void emu10k1_timer_irqhandler(struct emu10k1_card *); +void emu10k1_dsp_irqhandler(struct emu10k1_card *); +void emu10k1_mute_irqhandler(struct emu10k1_card *); +void emu10k1_volincr_irqhandler(struct emu10k1_card *); +void emu10k1_voldecr_irqhandler(struct emu10k1_card *); + +#endif /* _IRQ_H */ diff -Nru a/sound/oss/emu10k1/main.c b/sound/oss/emu10k1/main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/main.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,1181 @@ +/* + ********************************************************************** + * main.c - Creative EMU10K1 audio driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up stuff + * + ********************************************************************** + * + * 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. + * + ********************************************************************** + * + * Supported devices: + * /dev/dsp: Standard /dev/dsp device, OSS-compatible + * /dev/dsp1: Routes to rear speakers only + * /dev/mixer: Standard /dev/mixer device, OSS-compatible + * /dev/midi: Raw MIDI UART device, mostly OSS-compatible + * /dev/sequencer: Sequencer Interface (requires sound.o) + * + * Revision history: + * 0.1 beta Initial release + * 0.2 Lowered initial mixer vol. Improved on stuttering wave playback. Added MIDI UART support. + * 0.3 Fixed mixer routing bug, added APS, joystick support. + * 0.4 Added rear-channel, SPDIF support. + * 0.5 Source cleanup, SMP fixes, multiopen support, 64 bit arch fixes, + * moved bh's to tasklets, moved to the new PCI driver initialization style. + * 0.6 Make use of pci_alloc_consistent, improve compatibility layer for 2.2 kernels, + * code reorganization and cleanup. + * 0.7 Support for the Emu-APS. Bug fixes for voice cache setup, mmaped sound + poll(). + * Support for setting external TRAM size. + * 0.8 Make use of the kernel ac97 interface. Support for a dsp patch manager. + * 0.9 Re-enables rear speakers volume controls + * 0.10 Initializes rear speaker volume. + * Dynamic patch storage allocation. + * New private ioctls to change control gpr values. + * Enable volume control interrupts. + * By default enable dsp routes to digital out. + * 0.11 Fixed fx / 4 problem. + * 0.12 Implemented mmaped for recording. + * Fixed bug: not unreserving mmaped buffer pages. + * IRQ handler cleanup. + * 0.13 Fixed problem with dsp1 + * Simplified dsp patch writing (inside the driver) + * Fixed several bugs found by the Stanford tools + * 0.14 New control gpr to oss mixer mapping feature (Chris Purnell) + * Added AC3 Passthrough Support (Juha Yrjola) + * Added Support for 5.1 cards (digital out and the third analog out) + * 0.15 Added Sequencer Support (Daniel Mack) + * Support for multichannel pcm playback (Eduard Hasenleithner) + * 0.16 Mixer improvements, added old treble/bass support (Daniel Bertrand) + * Small code format cleanup. + * Deadlock bug fix for emu10k1_volxxx_irqhandler(). + * + *********************************************************************/ + +/* These are only included once per module */ +#include +#include +#include +#include +#include +#include + +#include "hwaccess.h" +#include "8010.h" +#include "efxmgr.h" +#include "cardwo.h" +#include "cardwi.h" +#include "cardmo.h" +#include "cardmi.h" +#include "recmgr.h" +#include "ecard.h" + + +#ifdef EMU10K1_SEQUENCER +#define MIDI_SYNTH_NAME "EMU10K1 MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT + +#include "../sound_config.h" +#include "../midi_synth.h" + +/* this should be in dev_table.h */ +#define SNDCARD_EMU10K1 46 +#endif + +#define DRIVER_VERSION "0.16" + +/* FIXME: is this right? */ +/* does the card support 32 bit bus master?*/ +#define EMU10K1_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ + +#ifndef PCI_VENDOR_ID_CREATIVE +#define PCI_VENDOR_ID_CREATIVE 0x1102 +#endif + +#ifndef PCI_DEVICE_ID_CREATIVE_EMU10K1 +#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 +#endif + +#define EMU_APS_SUBID 0x40011102 + +enum { + EMU10K1 = 0, +}; + +static char *card_names[] __devinitdata = { + "EMU10K1", +}; + +static struct pci_device_id emu10k1_pci_tbl[] = { + {PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_EMU10K1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, EMU10K1}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, emu10k1_pci_tbl); + +/* Global var instantiation */ + +LIST_HEAD(emu10k1_devs); + +extern struct file_operations emu10k1_audio_fops; +extern struct file_operations emu10k1_mixer_fops; +extern struct file_operations emu10k1_midi_fops; + +#ifdef EMU10K1_SEQUENCER +static struct midi_operations emu10k1_midi_operations; +#endif + +extern void emu10k1_interrupt(int, void *, struct pt_regs *s); + +static int __devinit emu10k1_audio_init(struct emu10k1_card *card) +{ + card->audio_dev = register_sound_dsp(&emu10k1_audio_fops, -1); + if (card->audio_dev < 0) { + printk(KERN_ERR "emu10k1: cannot register first audio device!\n"); + goto err_dev; + } + + card->audio_dev1 = register_sound_dsp(&emu10k1_audio_fops, -1); + if (card->audio_dev1 < 0) { + printk(KERN_ERR "emu10k1: cannot register second audio device!\n"); + goto err_dev1; + } + + /* Assign default playback voice parameters */ + card->mchannel_fx = 8; + /* mono voice */ + card->waveout.send_a[0] = 0xff; + card->waveout.send_b[0] = 0xff; + card->waveout.send_c[0] = 0x00; + card->waveout.send_d[0] = 0x00; + card->waveout.send_routing[0] = 0x3210; + + /* stereo voice */ + /* left */ + card->waveout.send_a[1] = 0xff; + card->waveout.send_b[1] = 0x00; + card->waveout.send_c[1] = 0x00; + card->waveout.send_d[1] = 0x00; + card->waveout.send_routing[1] = 0x3210; + + /* right */ + card->waveout.send_a[2] = 0x00; + card->waveout.send_b[2] = 0xff; + card->waveout.send_c[2] = 0x00; + card->waveout.send_d[2] = 0x00; + card->waveout.send_routing[2] = 0x3210; + + /* Assign default recording parameters */ + /* FIXME */ + if(card->isaps) + card->wavein.recsrc = WAVERECORD_FX; + else + card->wavein.recsrc = WAVERECORD_AC97; + + card->wavein.fxwc = 0x0003; + return 0; + +err_dev1: + unregister_sound_dsp(card->audio_dev); +err_dev: + return -ENODEV; +} + +static void __devinit emu10k1_audio_cleanup(struct emu10k1_card *card) +{ + unregister_sound_dsp(card->audio_dev1); + unregister_sound_dsp(card->audio_dev); +} + +static int __devinit emu10k1_mixer_init(struct emu10k1_card *card) +{ + char s[32]; + 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"); + return -EIO; + } + + card->ac97.private_data = card; + + if (!card->isaps) { + 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) { + printk(KERN_ERR "emu10k1: unable to probe AC97 codec\n"); + goto err_out; + } + /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version + does not support this, it shouldn't do any harm */ + sblive_writeptr(card, AC97SLOT, 0, AC97SLOT_CNTR | AC97SLOT_LFE); + + // Force 5bit + //card->ac97.bit_resolution=5; + + if (!proc_mkdir ("driver/emu10k1", 0)) { + printk(KERN_ERR "emu10k1: unable to create proc directory driver/emu10k1\n"); + goto err_out; + } + + sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name); + if (!proc_mkdir (s, 0)) { + printk(KERN_ERR "emu10k1: unable to create proc directory %s\n", s); + goto err_emu10k1_proc; + } + + sprintf(s, "driver/emu10k1/%s/ac97", card->pci_dev->slot_name); + 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; + } + + return 0; + + err_ac97_proc: + sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name); + remove_proc_entry(s, NULL); + + err_emu10k1_proc: + remove_proc_entry("driver/emu10k1", NULL); + err_out: + unregister_sound_mixer (card->ac97.dev_mixer); + return -EIO; +} + +static void __devinit emu10k1_mixer_cleanup(struct emu10k1_card *card) +{ + char s[32]; + + if (!card->isaps) { + sprintf(s, "driver/emu10k1/%s/ac97", card->pci_dev->slot_name); + remove_proc_entry(s, NULL); + + sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name); + remove_proc_entry(s, NULL); + + remove_proc_entry("driver/emu10k1", NULL); + } + + unregister_sound_mixer (card->ac97.dev_mixer); +} + +static int __devinit emu10k1_midi_init(struct emu10k1_card *card) +{ + int ret; + + card->midi_dev = register_sound_midi(&emu10k1_midi_fops, -1); + if (card->midi_dev < 0) { + printk(KERN_ERR "emu10k1: cannot register midi device!\n"); + return -ENODEV; + } + + + card->mpuout = kmalloc(sizeof(struct emu10k1_mpuout), GFP_KERNEL); + if (card->mpuout == NULL) { + printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuout: out of memory\n"); + ret = -ENOMEM; + goto err_out1; + } + + memset(card->mpuout, 0, sizeof(struct emu10k1_mpuout)); + + card->mpuout->intr = 1; + card->mpuout->status = FLAGS_AVAILABLE; + card->mpuout->state = CARDMIDIOUT_STATE_DEFAULT; + + tasklet_init(&card->mpuout->tasklet, emu10k1_mpuout_bh, (unsigned long) card); + + spin_lock_init(&card->mpuout->lock); + + card->mpuin = kmalloc(sizeof(struct emu10k1_mpuin), GFP_KERNEL); + if (card->mpuin == NULL) { + printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuin: out of memory\n"); + ret = -ENOMEM; + goto err_out2; + } + + memset(card->mpuin, 0, sizeof(struct emu10k1_mpuin)); + + card->mpuin->status = FLAGS_AVAILABLE; + + tasklet_init(&card->mpuin->tasklet, emu10k1_mpuin_bh, (unsigned long) card->mpuin); + + spin_lock_init(&card->mpuin->lock); + + /* Reset the MPU port */ + if (emu10k1_mpu_reset(card) < 0) { + ERROR(); + ret = -EIO; + goto err_out3; + } + +#ifdef EMU10K1_SEQUENCER + card->seq_dev = sound_alloc_mididev(); + if (card->seq_dev == -1) + printk(KERN_WARNING "emu10k1: unable to register sequencer device!"); + else { + std_midi_synth.midi_dev = card->seq_dev; + midi_devs[card->seq_dev] = + (struct midi_operations *) + kmalloc(sizeof(struct midi_operations), GFP_KERNEL); + + if (midi_devs[card->seq_dev] == NULL) { + printk(KERN_ERR "emu10k1: unable to allocate memory!"); + sound_unload_mididev(card->seq_dev); + card->seq_dev = -1; + return 0; + } else { + memcpy((char *)midi_devs[card->seq_dev], + (char *)&emu10k1_midi_operations, + sizeof(struct midi_operations)); + midi_devs[card->seq_dev]->devc = card; + sequencer_init(); + } + } + card->seq_mididev = 0; +#endif + return 0; + +err_out3: + kfree(card->mpuin); +err_out2: + kfree(card->mpuout); +err_out1: + unregister_sound_midi(card->midi_dev); + return ret; +} + +static void __devinit emu10k1_midi_cleanup(struct emu10k1_card *card) +{ + tasklet_kill(&card->mpuout->tasklet); + kfree(card->mpuout); + + tasklet_kill(&card->mpuin->tasklet); + kfree(card->mpuin); + +#ifdef EMU10K1_SEQUENCER + if (card->seq_dev > -1) { + kfree(midi_devs[card->seq_dev]); + midi_devs[card->seq_dev] = NULL; + sound_unload_mididev(card->seq_dev); + card->seq_dev = -1; + } +#endif + + unregister_sound_midi(card->midi_dev); +} + +static void __devinit voice_init(struct emu10k1_card *card) +{ + int i; + + for (i = 0; i < NUM_G; i++) + card->voicetable[i] = VOICE_USAGE_FREE; +} + +static void __devinit timer_init(struct emu10k1_card *card) +{ + INIT_LIST_HEAD(&card->timers); + card->timer_delay = TIMER_STOPPED; + card->timer_lock = SPIN_LOCK_UNLOCKED; +} + +static void __devinit addxmgr_init(struct emu10k1_card *card) +{ + u32 count; + + for (count = 0; count < MAXPAGES; count++) + card->emupagetable[count] = 0; + + /* Mark first page as used */ + /* This page is reserved by the driver */ + card->emupagetable[0] = 0x8001; + card->emupagetable[1] = MAXPAGES - 1; +} + +static void __devinit fx_cleanup(struct patch_manager *mgr) +{ + int i; + for(i = 0; i < mgr->current_pages; i++) + free_page((unsigned long) mgr->patch[i]); +} + +static int __devinit fx_init(struct emu10k1_card *card) +{ + struct patch_manager *mgr = &card->mgr; + struct dsp_patch *patch; + struct dsp_rpatch *rpatch; + s32 left, right; + int i; + u32 pc = 0; + u32 patch_n; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + mgr->ctrl_gpr[i][0] = -1; + mgr->ctrl_gpr[i][1] = -1; + } + + for (i = 0; i < 512; i++) + OP(6, 0x40, 0x40, 0x40, 0x40); + + for (i = 0; i < 256; i++) + sblive_writeptr_tag(card, 0, + FXGPREGBASE + i, 0, + TANKMEMADDRREGBASE + i, 0, + TAGLIST_END); + + /* !! The number bellow must equal the number of patches, currently 11 !! */ + mgr->current_pages = (11 + PATCHES_PER_PAGE - 1) / PATCHES_PER_PAGE; + for (i = 0; i < mgr->current_pages; i++) { + mgr->patch[i] = (void *)__get_free_page(GFP_KERNEL); + if (mgr->patch[i] == NULL) { + mgr->current_pages = i; + fx_cleanup(mgr); + return -ENOMEM; + } + memset(mgr->patch[i], 0, PAGE_SIZE); + } + + pc = 0; + patch_n = 0; + //first free GPR = 0x11b + + /* FX volume correction and Volume control*/ + INPUT_PATCH_START(patch, "Pcm L vol", 0x0, 0); + GET_OUTPUT_GPR(patch, 0x100, 0x0); + GET_CONTROL_GPR(patch, 0x106, "Vol", 0, 0x7fffffff); + GET_DYNAMIC_GPR(patch, 0x112); + + OP(4, 0x112, 0x40, PCM_IN_L, 0x44); //*4 + OP(0, 0x100, 0x040, 0x112, 0x106); //*vol + INPUT_PATCH_END(patch); + + + INPUT_PATCH_START(patch, "Pcm R vol", 0x1, 0); + GET_OUTPUT_GPR(patch, 0x101, 0x1); + GET_CONTROL_GPR(patch, 0x107, "Vol", 0, 0x7fffffff); + GET_DYNAMIC_GPR(patch, 0x112); + + OP(4, 0x112, 0x40, PCM_IN_R, 0x44); + OP(0, 0x101, 0x040, 0x112, 0x107); + + INPUT_PATCH_END(patch); + + + // CD-Digital In Volume control + INPUT_PATCH_START(patch, "CD-Digital Vol L", 0x12, 0); + GET_OUTPUT_GPR(patch, 0x10c, 0x12); + GET_CONTROL_GPR(patch, 0x10d, "Vol", 0, 0x7fffffff); + + OP(0, 0x10c, 0x040, SPDIF_CD_L, 0x10d); + INPUT_PATCH_END(patch); + + INPUT_PATCH_START(patch, "CD-Digital Vol R", 0x13, 0); + GET_OUTPUT_GPR(patch, 0x10e, 0x13); + GET_CONTROL_GPR(patch, 0x10f, "Vol", 0, 0x7fffffff); + + OP(0, 0x10e, 0x040, SPDIF_CD_R, 0x10f); + INPUT_PATCH_END(patch); + + //Volume Correction for Multi-channel Inputs + INPUT_PATCH_START(patch, "Multi-Channel Gain", 0x08, 0); + patch->input=patch->output=0x3F00; + + GET_OUTPUT_GPR(patch, 0x113, MULTI_FRONT_L); + GET_OUTPUT_GPR(patch, 0x114, MULTI_FRONT_R); + GET_OUTPUT_GPR(patch, 0x115, MULTI_REAR_L); + GET_OUTPUT_GPR(patch, 0x116, MULTI_REAR_R); + GET_OUTPUT_GPR(patch, 0x117, MULTI_CENTER); + GET_OUTPUT_GPR(patch, 0x118, MULTI_LFE); + + OP(4, 0x113, 0x40, MULTI_FRONT_L, 0x44); + OP(4, 0x114, 0x40, MULTI_FRONT_R, 0x44); + OP(4, 0x115, 0x40, MULTI_REAR_L, 0x44); + OP(4, 0x116, 0x40, MULTI_REAR_R, 0x44); + OP(4, 0x117, 0x40, MULTI_CENTER, 0x44); + OP(4, 0x118, 0x40, MULTI_LFE, 0x44); + + INPUT_PATCH_END(patch); + + + //Routing patch start + ROUTING_PATCH_START(rpatch, "Routing"); + GET_INPUT_GPR(rpatch, 0x100, 0x0); + GET_INPUT_GPR(rpatch, 0x101, 0x1); + GET_INPUT_GPR(rpatch, 0x10c, 0x12); + GET_INPUT_GPR(rpatch, 0x10e, 0x13); + GET_INPUT_GPR(rpatch, 0x113, MULTI_FRONT_L); + GET_INPUT_GPR(rpatch, 0x114, MULTI_FRONT_R); + GET_INPUT_GPR(rpatch, 0x115, MULTI_REAR_L); + GET_INPUT_GPR(rpatch, 0x116, MULTI_REAR_R); + GET_INPUT_GPR(rpatch, 0x117, MULTI_CENTER); + GET_INPUT_GPR(rpatch, 0x118, MULTI_LFE); + + GET_DYNAMIC_GPR(rpatch, 0x102); + GET_DYNAMIC_GPR(rpatch, 0x103); + + GET_OUTPUT_GPR(rpatch, 0x104, 0x8); + GET_OUTPUT_GPR(rpatch, 0x105, 0x9); + GET_OUTPUT_GPR(rpatch, 0x10a, 0x2); + GET_OUTPUT_GPR(rpatch, 0x10b, 0x3); + + + /* input buffer */ + OP(6, 0x102, AC97_IN_L, 0x40, 0x40); + OP(6, 0x103, AC97_IN_R, 0x40, 0x40); + + + /* Digital In + PCM + MULTI_FRONT-> AC97 out (front speakers)*/ + OP(6, AC97_FRONT_L, 0x100, 0x10c, 0x113); + + CONNECT(MULTI_FRONT_L, AC97_FRONT_L); + CONNECT(PCM_IN_L, AC97_FRONT_L); + CONNECT(SPDIF_CD_L, AC97_FRONT_L); + + OP(6, AC97_FRONT_R, 0x101, 0x10e, 0x114); + + CONNECT(MULTI_FRONT_R, AC97_FRONT_R); + CONNECT(PCM_IN_R, AC97_FRONT_R); + CONNECT(SPDIF_CD_R, AC97_FRONT_R); + + /* Digital In + PCM + AC97 In + PCM1 + MULTI_REAR --> Rear Channel */ + OP(6, 0x104, PCM1_IN_L, 0x100, 0x115); + OP(6, 0x104, 0x104, 0x10c, 0x102); + + CONNECT(MULTI_REAR_L, ANALOG_REAR_L); + CONNECT(AC97_IN_L, ANALOG_REAR_L); + CONNECT(PCM_IN_L, ANALOG_REAR_L); + CONNECT(SPDIF_CD_L, ANALOG_REAR_L); + CONNECT(PCM1_IN_L, ANALOG_REAR_L); + + OP(6, 0x105, PCM1_IN_R, 0x101, 0x116); + OP(6, 0x105, 0x105, 0x10e, 0x103); + + CONNECT(MULTI_REAR_R, ANALOG_REAR_R); + CONNECT(AC97_IN_R, ANALOG_REAR_R); + CONNECT(PCM_IN_R, ANALOG_REAR_R); + CONNECT(SPDIF_CD_R, ANALOG_REAR_R); + CONNECT(PCM1_IN_R, ANALOG_REAR_R); + + /* Digital In + PCM + AC97 In + MULTI_FRONT --> Digital out */ + OP(6, 0x10b, 0x100, 0x102, 0x10c); + OP(6, 0x10b, 0x10b, 0x113, 0x40); + + CONNECT(MULTI_FRONT_L, DIGITAL_OUT_L); + CONNECT(PCM_IN_L, DIGITAL_OUT_L); + CONNECT(AC97_IN_L, DIGITAL_OUT_L); + CONNECT(SPDIF_CD_L, DIGITAL_OUT_L); + + OP(6, 0x10a, 0x101, 0x103, 0x10e); + OP(6, 0x10b, 0x10b, 0x114, 0x40); + + CONNECT(MULTI_FRONT_R, DIGITAL_OUT_R); + CONNECT(PCM_IN_R, DIGITAL_OUT_R); + CONNECT(AC97_IN_R, DIGITAL_OUT_R); + CONNECT(SPDIF_CD_R, DIGITAL_OUT_R); + + /* AC97 In --> ADC Recording Buffer */ + OP(6, ADC_REC_L, 0x102, 0x40, 0x40); + + CONNECT(AC97_IN_L, ADC_REC_L); + + OP(6, ADC_REC_R, 0x103, 0x40, 0x40); + + CONNECT(AC97_IN_R, ADC_REC_R); + + + /* fx12:Analog-Center */ + OP(6, ANALOG_CENTER, 0x117, 0x40, 0x40); + CONNECT(MULTI_CENTER, ANALOG_CENTER); + + /* fx11:Analog-LFE */ + OP(6, ANALOG_LFE, 0x118, 0x40, 0x40); + CONNECT(MULTI_LFE, ANALOG_LFE); + + /* fx12:Digital-Center */ + OP(6, DIGITAL_CENTER, 0x117, 0x40, 0x40); + CONNECT(MULTI_CENTER, DIGITAL_CENTER); + + /* fx11:Analog-LFE */ + OP(6, DIGITAL_LFE, 0x118, 0x40, 0x40); + CONNECT(MULTI_LFE, DIGITAL_LFE); + + ROUTING_PATCH_END(rpatch); + + + // Rear volume control + OUTPUT_PATCH_START(patch, "Vol Rear L", 0x8, 0); + GET_INPUT_GPR(patch, 0x104, 0x8); + GET_CONTROL_GPR(patch, 0x119, "Vol", 0, 0x7fffffff); + + OP(0, ANALOG_REAR_L, 0x040, 0x104, 0x119); + OUTPUT_PATCH_END(patch); + + + OUTPUT_PATCH_START(patch, "Vol Rear R", 0x9, 0); + GET_INPUT_GPR(patch, 0x105, 0x9); + GET_CONTROL_GPR(patch, 0x11a, "Vol", 0, 0x7fffffff); + + OP(0, ANALOG_REAR_R, 0x040, 0x105, 0x11a); + OUTPUT_PATCH_END(patch); + + + //Master volume control on front-digital + OUTPUT_PATCH_START(patch, "Vol Master L", 0x2, 1); + GET_INPUT_GPR(patch, 0x10a, 0x2); + GET_CONTROL_GPR(patch, 0x108, "Vol", 0, 0x7fffffff); + + OP(0, DIGITAL_OUT_L, 0x040, 0x10a, 0x108); + OUTPUT_PATCH_END(patch); + + + OUTPUT_PATCH_START(patch, "Vol Master R", 0x3, 1); + GET_INPUT_GPR(patch, 0x10b, 0x3); + GET_CONTROL_GPR(patch, 0x109, "Vol", 0, 0x7fffffff); + + OP(0, DIGITAL_OUT_R, 0x040, 0x10b, 0x109); + OUTPUT_PATCH_END(patch); + + + /* delimiter patch */ + patch = PATCH(mgr, patch_n); + patch->code_size = 0; + + sblive_writeptr(card, DBG, 0, 0); + + mgr->lock = SPIN_LOCK_UNLOCKED; + + + //Master volume + 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; + + 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.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); + + //PCM Volume + 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; + + emu10k1_set_volume_gpr(card, 6, left, VOL_5BIT); + emu10k1_set_volume_gpr(card, 7, right, VOL_5BIT); + + //CD-Digital Volume + mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][0] = 0xd; + mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][1] = 0xf; + + left = right = 67; + card->ac97.mixer_state[SOUND_MIXER_DIGITAL1] = (right << 8) | left; + + 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); + 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; + + return 0; +} + +static int __devinit hw_init(struct emu10k1_card *card) +{ + int nCh; + u32 pagecount; /* tmp */ + int ret; + + /* Disable audio and lock cache */ + emu10k1_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE); + + /* Reset recording buffers */ + sblive_writeptr_tag(card, 0, + MICBS, ADCBS_BUFSIZE_NONE, + MICBA, 0, + FXBS, ADCBS_BUFSIZE_NONE, + FXBA, 0, + ADCBS, ADCBS_BUFSIZE_NONE, + ADCBA, 0, + TAGLIST_END); + + /* Disable channel interrupt */ + emu10k1_writefn0(card, INTE, 0); + sblive_writeptr_tag(card, 0, + CLIEL, 0, + CLIEH, 0, + SOLEL, 0, + SOLEH, 0, + TAGLIST_END); + + /* Init envelope engine */ + for (nCh = 0; nCh < NUM_G; nCh++) { + sblive_writeptr_tag(card, nCh, + DCYSUSV, 0, + IP, 0, + VTFT, 0xffff, + CVCF, 0xffff, + PTRX, 0, + CPF, 0, + CCR, 0, + + PSST, 0, + DSL, 0x10, + CCCA, 0, + Z1, 0, + Z2, 0, + FXRT, 0xd01c0000, + + ATKHLDM, 0, + DCYSUSM, 0, + IFATN, 0xffff, + PEFE, 0, + FMMOD, 0, + TREMFRQ, 24, /* 1 Hz */ + FM2FRQ2, 24, /* 1 Hz */ + TEMPENV, 0, + + /*** These are last so OFF prevents writing ***/ + LFOVAL2, 0, + LFOVAL1, 0, + ATKHLDV, 0, + ENVVOL, 0, + ENVVAL, 0, + TAGLIST_END); + } + + /* + ** Init to 0x02109204 : + ** Clock accuracy = 0 (1000ppm) + ** Sample Rate = 2 (48kHz) + ** Audio Channel = 1 (Left of 2) + ** Source Number = 0 (Unspecified) + ** Generation Status = 1 (Original for Cat Code 12) + ** Cat Code = 12 (Digital Signal Mixer) + ** Mode = 0 (Mode 0) + ** Emphasis = 0 (None) + ** CP = 1 (Copyright unasserted) + ** AN = 0 (Digital audio) + ** P = 0 (Consumer) + */ + + sblive_writeptr_tag(card, 0, + + /* SPDIF0 */ + SPCS0, (SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT), + + /* SPDIF1 */ + SPCS1, (SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT), + + /* SPDIF2 & SPDIF3 */ + SPCS2, (SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT), + + TAGLIST_END); + + ret = fx_init(card); /* initialize effects engine */ + if (ret < 0) + return ret; + + card->tankmem.size = 0; + + card->virtualpagetable.size = MAXPAGES * sizeof(u32); + + card->virtualpagetable.addr = pci_alloc_consistent(card->pci_dev, card->virtualpagetable.size, &card->virtualpagetable.dma_handle); + if (card->virtualpagetable.addr == NULL) { + ERROR(); + ret = -ENOMEM; + goto err0; + } + + card->silentpage.size = EMUPAGESIZE; + + card->silentpage.addr = pci_alloc_consistent(card->pci_dev, card->silentpage.size, &card->silentpage.dma_handle); + if (card->silentpage.addr == NULL) { + ERROR(); + ret = -ENOMEM; + goto err1; + } + + for (pagecount = 0; pagecount < MAXPAGES; pagecount++) + ((u32 *) card->virtualpagetable.addr)[pagecount] = cpu_to_le32((card->silentpage.dma_handle * 2) | pagecount); + + /* Init page table & tank memory base register */ + sblive_writeptr_tag(card, 0, + PTB, card->virtualpagetable.dma_handle, + TCB, 0, + TCBS, 0, + TAGLIST_END); + + for (nCh = 0; nCh < NUM_G; nCh++) { + sblive_writeptr_tag(card, nCh, + MAPA, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), + MAPB, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), + TAGLIST_END); + } + + /* Hokay, now enable the AUD bit */ + /* Enable Audio = 1 */ + /* Mute Disable Audio = 0 */ + /* Lock Tank Memory = 1 */ + /* Lock Sound Memory = 0 */ + /* Auto Mute = 1 */ + + if (card->model == 0x20 || card->model == 0xc400 || + (card->model == 0x21 && card->chiprev < 6)) + emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE); + else + emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE); + + /* Enable Vol_Ctrl irqs */ + emu10k1_irq_enable(card, INTE_VOLINCRENABLE | INTE_VOLDECRENABLE | INTE_MUTEENABLE | INTE_FXDSPENABLE); + + /* FIXME: TOSLink detection */ + card->has_toslink = 0; + + /* Initialize digital passthrough variables */ + card->pt.pos_gpr = card->pt.intr_gpr = card->pt.enable_gpr = -1; + card->pt.selected = 0; + card->pt.state = PT_STATE_INACTIVE; + card->pt.spcs_to_use = 0x01; + card->pt.patch_name = "AC3pass"; + card->pt.intr_gpr_name = "count"; + card->pt.enable_gpr_name = "enable"; + card->pt.pos_gpr_name = "ptr"; + spin_lock_init(&card->pt.lock); + init_waitqueue_head(&card->pt.wait); + +/* tmp = sblive_readfn0(card, HCFG); + if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { + sblive_writefn0(card, HCFG, tmp | 0x800); + + udelay(512); + + if (tmp != (sblive_readfn0(card, HCFG) & ~0x800)) { + card->has_toslink = 1; + sblive_writefn0(card, HCFG, tmp); + } + } +*/ + return 0; + + err1: + pci_free_consistent(card->pci_dev, card->virtualpagetable.size, card->virtualpagetable.addr, card->virtualpagetable.dma_handle); + err0: + fx_cleanup(&card->mgr); + + return ret; +} + +static int __devinit emu10k1_init(struct emu10k1_card *card) +{ + /* Init Card */ + if (hw_init(card) < 0) + return -1; + + voice_init(card); + timer_init(card); + addxmgr_init(card); + + DPD(2, " hw control register -> %#x\n", emu10k1_readfn0(card, HCFG)); + + return 0; +} + +static void __devinit emu10k1_cleanup(struct emu10k1_card *card) +{ + int ch; + + emu10k1_writefn0(card, INTE, 0); + + /** Shutdown the chip **/ + for (ch = 0; ch < NUM_G; ch++) + sblive_writeptr(card, DCYSUSV, ch, 0); + + for (ch = 0; ch < NUM_G; ch++) { + sblive_writeptr_tag(card, ch, + VTFT, 0, + CVCF, 0, + PTRX, 0, + CPF, 0, + TAGLIST_END); + } + + /* Disable audio and lock cache */ + emu10k1_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE); + + sblive_writeptr_tag(card, 0, + PTB, 0, + + /* Reset recording buffers */ + MICBS, ADCBS_BUFSIZE_NONE, + MICBA, 0, + FXBS, ADCBS_BUFSIZE_NONE, + FXBA, 0, + FXWC, 0, + ADCBS, ADCBS_BUFSIZE_NONE, + ADCBA, 0, + TCBS, 0, + TCB, 0, + DBG, 0x8000, + + /* Disable channel interrupt */ + CLIEL, 0, + CLIEH, 0, + SOLEL, 0, + SOLEH, 0, + TAGLIST_END); + + + pci_free_consistent(card->pci_dev, card->virtualpagetable.size, card->virtualpagetable.addr, card->virtualpagetable.dma_handle); + pci_free_consistent(card->pci_dev, card->silentpage.size, card->silentpage.addr, card->silentpage.dma_handle); + + if(card->tankmem.size != 0) + pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle); + + /* release patch storage memory */ + fx_cleanup(&card->mgr); +} + +/* Driver initialization routine */ +static int __devinit emu10k1_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + struct emu10k1_card *card; + u32 subsysvid; + int ret; + + if (pci_set_dma_mask(pci_dev, EMU10K1_DMA_MASK)) { + printk(KERN_ERR "emu10k1: architecture does not support 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if (pci_enable_device(pci_dev)) + return -EIO; + + pci_set_master(pci_dev); + + if ((card = kmalloc(sizeof(struct emu10k1_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "emu10k1: out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(struct emu10k1_card)); + + card->iobase = pci_resource_start(pci_dev, 0); + card->length = pci_resource_len(pci_dev, 0); + + if (request_region(card->iobase, card->length, card_names[pci_id->driver_data]) == NULL) { + printk(KERN_ERR "emu10k1: IO space in use\n"); + ret = -EBUSY; + goto err_region; + } + + pci_set_drvdata(pci_dev, card); + + card->irq = pci_dev->irq; + card->pci_dev = pci_dev; + + /* Reserve IRQ Line */ + if (request_irq(card->irq, emu10k1_interrupt, SA_SHIRQ, card_names[pci_id->driver_data], card)) { + printk(KERN_ERR "emu10k1: IRQ in use\n"); + ret = -EBUSY; + goto err_irq; + } + + pci_read_config_byte(pci_dev, PCI_REVISION_ID, &card->chiprev); + pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &card->model); + + printk(KERN_INFO "emu10k1: %s rev %d model 0x%x found, IO at 0x%04lx-0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], card->chiprev, card->model, card->iobase, + card->iobase + card->length - 1, card->irq); + + pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &subsysvid); + card->isaps = (subsysvid == EMU_APS_SUBID); + + spin_lock_init(&card->lock); + init_MUTEX(&card->open_sem); + card->open_mode = 0; + init_waitqueue_head(&card->open_wait); + + ret = emu10k1_audio_init(card); + if(ret < 0) { + printk(KERN_ERR "emu10k1: cannot initialize audio devices\n"); + goto err_audio; + } + + ret = emu10k1_mixer_init(card); + if(ret < 0) { + printk(KERN_ERR "emu10k1: cannot initialize AC97 codec\n"); + goto err_mixer; + } + + ret = emu10k1_midi_init(card); + if (ret < 0) { + printk(KERN_ERR "emu10k1: cannot register midi device\n"); + goto err_midi; + } + + ret = emu10k1_init(card); + if (ret < 0) { + printk(KERN_ERR "emu10k1: cannot initialize device\n"); + goto err_emu10k1_init; + } + + if (card->isaps) + emu10k1_ecard_init(card); + + list_add(&card->list, &emu10k1_devs); + + return 0; + +err_emu10k1_init: + emu10k1_midi_cleanup(card); + +err_midi: + emu10k1_mixer_cleanup(card); + +err_mixer: + emu10k1_audio_cleanup(card); + +err_audio: + free_irq(card->irq, card); + +err_irq: + release_region(card->iobase, card->length); + pci_set_drvdata(pci_dev, NULL); + +err_region: + kfree(card); + + return ret; +} + +static void __devexit emu10k1_remove(struct pci_dev *pci_dev) +{ + struct emu10k1_card *card = pci_get_drvdata(pci_dev); + + list_del(&card->list); + + emu10k1_cleanup(card); + emu10k1_midi_cleanup(card); + emu10k1_mixer_cleanup(card); + emu10k1_audio_cleanup(card); + free_irq(card->irq, card); + release_region(card->iobase, card->length); + kfree(card); + pci_set_drvdata(pci_dev, NULL); +} + +MODULE_AUTHOR("Bertrand Lee, Cai Ying. (Email to: emu10k1-devel@opensource.creative.com)"); +MODULE_DESCRIPTION("Creative EMU10K1 PCI Audio Driver v" DRIVER_VERSION "\nCopyright (C) 1999 Creative Technology Ltd."); +MODULE_LICENSE("GPL"); + +static struct pci_driver emu10k1_pci_driver = { + name: "emu10k1", + id_table: emu10k1_pci_tbl, + probe: emu10k1_probe, + remove: emu10k1_remove, +}; + +static int __init emu10k1_init_module(void) +{ + printk(KERN_INFO "Creative EMU10K1 PCI Audio Driver, version " DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + return pci_module_init(&emu10k1_pci_driver); +} + +static void __exit emu10k1_cleanup_module(void) +{ + pci_unregister_driver(&emu10k1_pci_driver); + + return; +} + +module_init(emu10k1_init_module); +module_exit(emu10k1_cleanup_module); + +#ifdef EMU10K1_SEQUENCER + +/* in midi.c */ +extern int emu10k1_seq_midi_open(int dev, int mode, + void (*input)(int dev, unsigned char midi_byte), + void (*output)(int dev)); +extern void emu10k1_seq_midi_close(int dev); +extern int emu10k1_seq_midi_out(int dev, unsigned char midi_byte); +extern int emu10k1_seq_midi_start_read(int dev); +extern int emu10k1_seq_midi_end_read(int dev); +extern void emu10k1_seq_midi_kick(int dev); +extern int emu10k1_seq_midi_buffer_status(int dev); + +static struct midi_operations emu10k1_midi_operations = +{ + THIS_MODULE, + {"EMU10K1 MIDI", 0, 0, SNDCARD_EMU10K1}, + &std_midi_synth, + {0}, + emu10k1_seq_midi_open, + emu10k1_seq_midi_close, + NULL, + emu10k1_seq_midi_out, + emu10k1_seq_midi_start_read, + emu10k1_seq_midi_end_read, + emu10k1_seq_midi_kick, + NULL, + emu10k1_seq_midi_buffer_status, + NULL +}; + +#endif diff -Nru a/sound/oss/emu10k1/midi.c b/sound/oss/emu10k1/midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/midi.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,597 @@ +/* + ********************************************************************** + * midi.c - /dev/midi interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +#include "hwaccess.h" +#include "cardmo.h" +#include "cardmi.h" +#include "midi.h" + +#ifdef EMU10K1_SEQUENCER +#include "../sound_config.h" +#endif + +static spinlock_t midi_spinlock __attribute((unused)) = SPIN_LOCK_UNLOCKED; + +static void init_midi_hdr(struct midi_hdr *midihdr) +{ + midihdr->bufferlength = MIDIIN_BUFLEN; + midihdr->bytesrecorded = 0; + midihdr->flags = 0; +} + +static int midiin_add_buffer(struct emu10k1_mididevice *midi_dev, struct midi_hdr **midihdrptr) +{ + struct midi_hdr *midihdr; + + if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL) { + ERROR(); + return -EINVAL; + } + + init_midi_hdr(midihdr); + + if ((midihdr->data = (u8 *) kmalloc(MIDIIN_BUFLEN, GFP_KERNEL)) == NULL) { + ERROR(); + kfree(midihdr); + return -1; + } + + if (emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr) < 0) { + ERROR(); + kfree(midihdr->data); + kfree(midihdr); + return -1; + } + + *midihdrptr = midihdr; + list_add_tail(&midihdr->list, &midi_dev->mid_hdrs); + + return 0; +} + +static int emu10k1_midi_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct emu10k1_card *card = NULL; + struct emu10k1_mididevice *midi_dev; + struct list_head *entry; + + DPF(2, "emu10k1_midi_open()\n"); + + /* Check for correct device to open */ + list_for_each(entry, &emu10k1_devs) { + card = list_entry(entry, struct emu10k1_card, list); + + if (card->midi_dev == minor) + goto match; + } + + return -ENODEV; + +match: +#ifdef EMU10K1_SEQUENCER + if (card->seq_mididev) /* card is opened by sequencer */ + return -EBUSY; +#endif + + /* Wait for device to become free */ + down(&card->open_sem); + while (card->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&card->open_sem); + return -EBUSY; + } + + up(&card->open_sem); + interruptible_sleep_on(&card->open_wait); + + if (signal_pending(current)) { + return -ERESTARTSYS; + } + + down(&card->open_sem); + } + + if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL) + return -EINVAL; + + midi_dev->card = card; + midi_dev->mistate = MIDIIN_STATE_STOPPED; + init_waitqueue_head(&midi_dev->oWait); + init_waitqueue_head(&midi_dev->iWait); + midi_dev->ird = 0; + midi_dev->iwr = 0; + midi_dev->icnt = 0; + INIT_LIST_HEAD(&midi_dev->mid_hdrs); + + if (file->f_mode & FMODE_READ) { + struct midi_openinfo dsCardMidiOpenInfo; + struct midi_hdr *midihdr1; + struct midi_hdr *midihdr2; + + dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; + + if (emu10k1_mpuin_open(card, &dsCardMidiOpenInfo) < 0) { + ERROR(); + kfree(midi_dev); + return -ENODEV; + } + + /* Add two buffers to receive sysex buffer */ + if (midiin_add_buffer(midi_dev, &midihdr1) < 0) { + kfree(midi_dev); + return -ENODEV; + } + + if (midiin_add_buffer(midi_dev, &midihdr2) < 0) { + list_del(&midihdr1->list); + kfree(midihdr1->data); + kfree(midihdr1); + kfree(midi_dev); + return -ENODEV; + } + } + + if (file->f_mode & FMODE_WRITE) { + struct midi_openinfo dsCardMidiOpenInfo; + + dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; + + if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) { + ERROR(); + kfree(midi_dev); + return -ENODEV; + } + } + + file->private_data = (void *) midi_dev; + + card->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + + up(&card->open_sem); + + return 0; +} + +static int emu10k1_midi_release(struct inode *inode, struct file *file) +{ + struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data; + struct emu10k1_card *card; + + lock_kernel(); + + card = midi_dev->card; + DPF(2, "emu10k1_midi_release()\n"); + + if (file->f_mode & FMODE_WRITE) { + if (!(file->f_flags & O_NONBLOCK)) { + + while (!signal_pending(current) && (card->mpuout->firstmidiq != NULL)) { + DPF(4, "Cannot close - buffers not empty\n"); + + interruptible_sleep_on(&midi_dev->oWait); + + } + } + + emu10k1_mpuout_close(card); + } + + if (file->f_mode & FMODE_READ) { + struct midi_hdr *midihdr; + + if (midi_dev->mistate == MIDIIN_STATE_STARTED) { + emu10k1_mpuin_stop(card); + midi_dev->mistate = MIDIIN_STATE_STOPPED; + } + + emu10k1_mpuin_reset(card); + emu10k1_mpuin_close(card); + + while (!list_empty(&midi_dev->mid_hdrs)) { + midihdr = list_entry(midi_dev->mid_hdrs.next, struct midi_hdr, list); + + list_del(midi_dev->mid_hdrs.next); + kfree(midihdr->data); + kfree(midihdr); + } + } + + kfree(midi_dev); + + down(&card->open_sem); + card->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE)); + up(&card->open_sem); + wake_up_interruptible(&card->open_wait); + + unlock_kernel(); + + return 0; +} + +static ssize_t emu10k1_midi_read(struct file *file, char *buffer, size_t count, loff_t * pos) +{ + struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data; + ssize_t ret = 0; + u16 cnt; + unsigned long flags; + + DPD(4, "emu10k1_midi_read(), count %#x\n", (u32) count); + + if (pos != &file->f_pos) + return -ESPIPE; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + if (midi_dev->mistate == MIDIIN_STATE_STOPPED) { + if (emu10k1_mpuin_start(midi_dev->card) < 0) { + ERROR(); + return -EINVAL; + } + + midi_dev->mistate = MIDIIN_STATE_STARTED; + } + + while (count > 0) { + cnt = MIDIIN_BUFLEN - midi_dev->ird; + + spin_lock_irqsave(&midi_spinlock, flags); + + if (midi_dev->icnt < cnt) + cnt = midi_dev->icnt; + + spin_unlock_irqrestore(&midi_spinlock, flags); + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + DPF(2, " Go to sleep...\n"); + + interruptible_sleep_on(&midi_dev->iWait); + + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + + continue; + } + + if (copy_to_user(buffer, midi_dev->iBuf + midi_dev->ird, cnt)) { + ERROR(); + return ret ? ret : -EFAULT; + } + + midi_dev->ird += cnt; + midi_dev->ird %= MIDIIN_BUFLEN; + + spin_lock_irqsave(&midi_spinlock, flags); + + midi_dev->icnt -= cnt; + + spin_unlock_irqrestore(&midi_spinlock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + + if (midi_dev->icnt == 0) + break; + } + + return ret; +} + +static ssize_t emu10k1_midi_write(struct file *file, const char *buffer, size_t count, loff_t * pos) +{ + struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data; + struct midi_hdr *midihdr; + ssize_t ret = 0; + unsigned long flags; + + DPD(4, "emu10k1_midi_write(), count=%#x\n", (u32) count); + + if (pos != &file->f_pos) + return -ESPIPE; + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL) + return -EINVAL; + + midihdr->bufferlength = count; + midihdr->bytesrecorded = 0; + midihdr->flags = 0; + + if ((midihdr->data = (u8 *) kmalloc(count, GFP_KERNEL)) == NULL) { + ERROR(); + kfree(midihdr); + return -EINVAL; + } + + if (copy_from_user(midihdr->data, buffer, count)) { + kfree(midihdr->data); + kfree(midihdr); + return ret ? ret : -EFAULT; + } + + spin_lock_irqsave(&midi_spinlock, flags); + + if (emu10k1_mpuout_add_buffer(midi_dev->card, midihdr) < 0) { + ERROR(); + kfree(midihdr->data); + kfree(midihdr); + spin_unlock_irqrestore(&midi_spinlock, flags); + return -EINVAL; + } + + spin_unlock_irqrestore(&midi_spinlock, flags); + + return count; +} + +static unsigned int emu10k1_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + DPF(4, "emu10k1_midi_poll() called\n"); + return 0; +} + +int emu10k1_midi_callback(unsigned long msg, unsigned long refdata, unsigned long *pmsg) +{ + struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) refdata; + struct midi_hdr *midihdr = NULL; + unsigned long flags; + int i; + + DPF(4, "emu10k1_midi_callback()\n"); + + spin_lock_irqsave(&midi_spinlock, flags); + + switch (msg) { + case ICARDMIDI_OUTLONGDATA: + midihdr = (struct midi_hdr *) pmsg[2]; + + kfree(midihdr->data); + kfree(midihdr); + wake_up_interruptible(&midi_dev->oWait); + + break; + + case ICARDMIDI_INLONGDATA: + midihdr = (struct midi_hdr *) pmsg[2]; + + for (i = 0; i < midihdr->bytesrecorded; i++) { + midi_dev->iBuf[midi_dev->iwr++] = midihdr->data[i]; + midi_dev->iwr %= MIDIIN_BUFLEN; + } + + midi_dev->icnt += midihdr->bytesrecorded; + + if (midi_dev->mistate == MIDIIN_STATE_STARTED) { + init_midi_hdr(midihdr); + emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr); + wake_up_interruptible(&midi_dev->iWait); + } + break; + + case ICARDMIDI_INDATA: + { + u8 *pBuf = (u8 *) & pmsg[1]; + u16 bytesvalid = pmsg[2]; + + for (i = 0; i < bytesvalid; i++) { + midi_dev->iBuf[midi_dev->iwr++] = pBuf[i]; + midi_dev->iwr %= MIDIIN_BUFLEN; + } + + midi_dev->icnt += bytesvalid; + } + + wake_up_interruptible(&midi_dev->iWait); + break; + + default: /* Unknown message */ + spin_unlock_irqrestore(&midi_spinlock, flags); + return -1; + } + + spin_unlock_irqrestore(&midi_spinlock, flags); + + return 0; +} + +/* MIDI file operations */ +struct file_operations emu10k1_midi_fops = { + owner: THIS_MODULE, + read: emu10k1_midi_read, + write: emu10k1_midi_write, + poll: emu10k1_midi_poll, + open: emu10k1_midi_open, + release: emu10k1_midi_release, +}; + + +#ifdef EMU10K1_SEQUENCER + +/* functions used for sequencer access */ + +int emu10k1_seq_midi_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev)) +{ + struct emu10k1_card *card; + struct midi_openinfo dsCardMidiOpenInfo; + struct emu10k1_mididevice *midi_dev; + + if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) + return -EINVAL; + + card = midi_devs[dev]->devc; + + if (card->open_mode) /* card is opened native */ + return -EBUSY; + + DPF(2, "emu10k1_seq_midi_open()\n"); + + if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL) + return -EINVAL; + + midi_dev->card = card; + midi_dev->mistate = MIDIIN_STATE_STOPPED; + init_waitqueue_head(&midi_dev->oWait); + init_waitqueue_head(&midi_dev->iWait); + midi_dev->ird = 0; + midi_dev->iwr = 0; + midi_dev->icnt = 0; + INIT_LIST_HEAD(&midi_dev->mid_hdrs); + + dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; + + if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) { + ERROR(); + return -ENODEV; + } + + card->seq_mididev = midi_dev; + + return 0; +} + +void emu10k1_seq_midi_close(int dev) +{ + struct emu10k1_card *card; + + DPF(2, "emu10k1_seq_midi_close()\n"); + if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) + return; + + card = midi_devs[dev]->devc; + emu10k1_mpuout_close(card); + + if (card->seq_mididev) { + kfree(card->seq_mididev); + card->seq_mididev = 0; + } +} + +int emu10k1_seq_midi_out(int dev, unsigned char midi_byte) +{ + struct emu10k1_card *card; + struct midi_hdr *midihdr; + unsigned long flags; + + if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) + return -EINVAL; + + card = midi_devs[dev]->devc; + + if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL) + return -EINVAL; + + midihdr->bufferlength = 1; + midihdr->bytesrecorded = 0; + midihdr->flags = 0; + + if ((midihdr->data = (u8 *) kmalloc(1, GFP_KERNEL)) == NULL) { + ERROR(); + kfree(midihdr); + return -EINVAL; + } + + *(midihdr->data) = midi_byte; + + spin_lock_irqsave(&midi_spinlock, flags); + + if (emu10k1_mpuout_add_buffer(card, midihdr) < 0) { + ERROR(); + kfree(midihdr->data); + kfree(midihdr); + spin_unlock_irqrestore(&midi_spinlock, flags); + return -EINVAL; + } + + spin_unlock_irqrestore(&midi_spinlock, flags); + + return 1; +} + +int emu10k1_seq_midi_start_read(int dev) +{ + return 0; +} + +int emu10k1_seq_midi_end_read(int dev) +{ + return 0; +} + +void emu10k1_seq_midi_kick(int dev) +{ +} + +int emu10k1_seq_midi_buffer_status(int dev) +{ + int count; + struct midi_queue *queue; + struct emu10k1_card *card; + + if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) + return -EINVAL; + + count = 0; + + card = midi_devs[dev]->devc; + queue = card->mpuout->firstmidiq; + + while (queue != NULL) { + count++; + if (queue == card->mpuout->lastmidiq) + break; + + queue = queue->next; + } + + return count; +} + +#endif + diff -Nru a/sound/oss/emu10k1/midi.h b/sound/oss/emu10k1/midi.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/midi.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,55 @@ +/* + ********************************************************************** + * midi.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _MIDI_H +#define _MIDI_H + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define MIDIIN_STATE_STARTED 0x00000001 +#define MIDIIN_STATE_STOPPED 0x00000002 + +#define MIDIIN_BUFLEN 1024 + +struct emu10k1_mididevice +{ + struct emu10k1_card *card; + u32 mistate; + wait_queue_head_t oWait; + wait_queue_head_t iWait; + s8 iBuf[MIDIIN_BUFLEN]; + u16 ird, iwr, icnt; + struct list_head mid_hdrs; +}; + +#endif /* _MIDI_H */ diff -Nru a/sound/oss/emu10k1/mixer.c b/sound/oss/emu10k1/mixer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/mixer.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,675 @@ +/* + ********************************************************************** + * mixer.c - /dev/mixer interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up stuff + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#define __NO_VERSION__ /* Kernel version only defined once */ +#include +#include +#include +#include + +#include "hwaccess.h" +#include "8010.h" +#include "recmgr.h" + + +static const u32 bass_table[41][5] = { + { 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 }, + { 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d }, + { 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee }, + { 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c }, + { 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b }, + { 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 }, + { 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f }, + { 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 }, + { 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 }, + { 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 }, + { 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 }, + { 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be }, + { 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b }, + { 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c }, + { 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b }, + { 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 }, + { 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a }, + { 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 }, + { 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 }, + { 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e }, + { 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee }, + { 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 }, + { 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 }, + { 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 }, + { 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 }, + { 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e }, + { 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 }, + { 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 }, + { 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 }, + { 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 }, + { 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 }, + { 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca }, + { 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 }, + { 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 }, + { 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 }, + { 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 }, + { 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a }, + { 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f }, + { 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 }, + { 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 }, + { 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 } +}; + +static const u32 treble_table[41][5] = { + { 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 }, + { 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 }, + { 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 }, + { 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca }, + { 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 }, + { 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 }, + { 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 }, + { 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 }, + { 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 }, + { 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df }, + { 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff }, + { 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 }, + { 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c }, + { 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 }, + { 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 }, + { 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 }, + { 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 }, + { 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d }, + { 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 }, + { 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 }, + { 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f }, + { 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb }, + { 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 }, + { 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 }, + { 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd }, + { 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 }, + { 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad }, + { 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 }, + { 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 }, + { 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c }, + { 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 }, + { 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 }, + { 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 }, + { 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 }, + { 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 }, + { 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 }, + { 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d }, + { 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b }, + { 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 }, + { 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd }, + { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 } +}; + + +static void set_bass(struct emu10k1_card *card, int l, int r) +{ + int i; + + l = (l * 40 + 50) / 100; + r = (r * 40 + 50) / 100; + + for (i = 0; i < 5; i++) + sblive_writeptr(card, GPR_BASE + card->mgr.ctrl_gpr[SOUND_MIXER_BASS][0] + i, 0, bass_table[l][i]); +} + +static void set_treble(struct emu10k1_card *card, int l, int r) +{ + int i; + + l = (l * 40 + 50) / 100; + r = (r * 40 + 50) / 100; + + for (i = 0; i < 5; i++) + sblive_writeptr(card, GPR_BASE + card->mgr.ctrl_gpr[SOUND_MIXER_TREBLE][0] + i , 0, treble_table[l][i]); +} + +const char volume_params[SOUND_MIXER_NRDEVICES]= { +/* Used by the ac97 driver */ + [SOUND_MIXER_VOLUME] = VOL_6BIT, + [SOUND_MIXER_BASS] = VOL_4BIT, + [SOUND_MIXER_TREBLE] = VOL_4BIT, + [SOUND_MIXER_PCM] = VOL_5BIT, + [SOUND_MIXER_SPEAKER] = VOL_4BIT, + [SOUND_MIXER_LINE] = VOL_5BIT, + [SOUND_MIXER_MIC] = VOL_5BIT, + [SOUND_MIXER_CD] = VOL_5BIT, + [SOUND_MIXER_ALTPCM] = VOL_6BIT, + [SOUND_MIXER_IGAIN] = VOL_4BIT, + [SOUND_MIXER_LINE1] = VOL_5BIT, + [SOUND_MIXER_PHONEIN] = VOL_5BIT, + [SOUND_MIXER_PHONEOUT] = VOL_6BIT, + [SOUND_MIXER_VIDEO] = VOL_5BIT, +/* Not used by the ac97 driver */ + [SOUND_MIXER_SYNTH] = VOL_5BIT, + [SOUND_MIXER_IMIX] = VOL_5BIT, + [SOUND_MIXER_RECLEV] = VOL_5BIT, + [SOUND_MIXER_OGAIN] = VOL_5BIT, + [SOUND_MIXER_LINE2] = VOL_5BIT, + [SOUND_MIXER_LINE3] = VOL_5BIT, + [SOUND_MIXER_DIGITAL1] = VOL_5BIT, + [SOUND_MIXER_DIGITAL2] = VOL_5BIT, + [SOUND_MIXER_DIGITAL3] = VOL_5BIT, + [SOUND_MIXER_RADIO] = VOL_5BIT, + [SOUND_MIXER_MONITOR] = VOL_5BIT +}; + +/* Mixer file operations */ +static int emu10k1_private_mixer(struct emu10k1_card *card, unsigned int cmd, unsigned long arg) +{ + struct mixer_private_ioctl *ctl; + struct dsp_patch *patch; + u32 size, page; + int addr, size_reg, i, ret; + unsigned int id, ch; + + switch (cmd) { + + case SOUND_MIXER_PRIVATE3: + + ctl = (struct mixer_private_ioctl *) kmalloc(sizeof(struct mixer_private_ioctl), GFP_KERNEL); + if (ctl == NULL) + return -ENOMEM; + + if (copy_from_user(ctl, (void *) arg, sizeof(struct mixer_private_ioctl))) { + kfree(ctl); + return -EFAULT; + } + + ret = 0; + switch (ctl->cmd) { +#ifdef DBGEMU + case CMD_WRITEFN0: + emu10k1_writefn0(card, ctl->val[0], ctl->val[1]); + break; + + case CMD_WRITEPTR: + if (ctl->val[1] >= 0x40 || ctl->val[0] > 0xff) { + ret = -EINVAL; + break; + } + + if ((ctl->val[0] & 0x7ff) > 0x3f) + ctl->val[1] = 0x00; + + sblive_writeptr(card, ctl->val[0], ctl->val[1], ctl->val[2]); + + break; +#endif + case CMD_READFN0: + ctl->val[2] = emu10k1_readfn0(card, ctl->val[0]); + + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_READPTR: + if (ctl->val[1] >= 0x40 || (ctl->val[0] & 0x7ff) > 0xff) { + ret = -EINVAL; + break; + } + + if ((ctl->val[0] & 0x7ff) > 0x3f) + ctl->val[1] = 0x00; + + ctl->val[2] = sblive_readptr(card, ctl->val[0], ctl->val[1]); + + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_SETRECSRC: + switch (ctl->val[0]) { + case WAVERECORD_AC97: + if (card->isaps) { + ret = -EINVAL; + break; + } + + card->wavein.recsrc = WAVERECORD_AC97; + break; + + case WAVERECORD_MIC: + card->wavein.recsrc = WAVERECORD_MIC; + break; + + case WAVERECORD_FX: + card->wavein.recsrc = WAVERECORD_FX; + card->wavein.fxwc = ctl->val[1] & 0xffff; + + if (!card->wavein.fxwc) + ret = -EINVAL; + + break; + + default: + ret = -EINVAL; + break; + } + break; + + case CMD_GETRECSRC: + ctl->val[0] = card->wavein.recsrc; + ctl->val[1] = card->wavein.fxwc; + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_GETVOICEPARAM: + ctl->val[0] = card->waveout.send_routing[0]; + ctl->val[1] = card->waveout.send_a[0] | card->waveout.send_b[0] << 8 | + card->waveout.send_c[0] << 16 | card->waveout.send_d[0] << 24; + + ctl->val[2] = card->waveout.send_routing[1]; + ctl->val[3] = card->waveout.send_a[1] | card->waveout.send_b[1] << 8 | + card->waveout.send_c[1] << 16 | card->waveout.send_d[1] << 24; + + ctl->val[4] = card->waveout.send_routing[2]; + ctl->val[5] = card->waveout.send_a[2] | card->waveout.send_b[2] << 8 | + card->waveout.send_c[2] << 16 | card->waveout.send_d[2] << 24; + + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_SETVOICEPARAM: + card->waveout.send_routing[0] = ctl->val[0] & 0xffff; + card->waveout.send_a[0] = ctl->val[1] & 0xff; + card->waveout.send_b[0] = (ctl->val[1] >> 8) & 0xff; + card->waveout.send_c[0] = (ctl->val[1] >> 16) & 0xff; + card->waveout.send_d[0] = (ctl->val[1] >> 24) & 0xff; + + card->waveout.send_routing[1] = ctl->val[2] & 0xffff; + card->waveout.send_a[1] = ctl->val[3] & 0xff; + card->waveout.send_b[1] = (ctl->val[3] >> 8) & 0xff; + card->waveout.send_c[1] = (ctl->val[3] >> 16) & 0xff; + card->waveout.send_d[1] = (ctl->val[3] >> 24) & 0xff; + + card->waveout.send_routing[2] = ctl->val[4] & 0xffff; + card->waveout.send_a[2] = ctl->val[5] & 0xff; + card->waveout.send_b[2] = (ctl->val[5] >> 8) & 0xff; + card->waveout.send_c[2] = (ctl->val[5] >> 16) & 0xff; + card->waveout.send_d[2] = (ctl->val[5] >> 24) & 0xff; + + break; + + case CMD_SETMCH_FX: + card->mchannel_fx = ctl->val[0] & 0x000f; + break; + + case CMD_GETPATCH: + if (ctl->val[0] == 0) { + if (copy_to_user((void *) arg, &card->mgr.rpatch, sizeof(struct dsp_rpatch))) + ret = -EFAULT; + } else { + if ((ctl->val[0] - 1) / PATCHES_PER_PAGE >= card->mgr.current_pages) { + ret = -EINVAL; + break; + } + + if (copy_to_user((void *) arg, PATCH(&card->mgr, ctl->val[0] - 1), sizeof(struct dsp_patch))) + ret = -EFAULT; + } + + break; + + case CMD_GETGPR: + id = ctl->val[0]; + + if (id > NUM_GPRS) { + ret = -EINVAL; + break; + } + + if (copy_to_user((void *) arg, &card->mgr.gpr[id], sizeof(struct dsp_gpr))) + ret = -EFAULT; + + break; + + case CMD_GETCTLGPR: + addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, &((char *) ctl->val)[PATCH_NAME_SIZE]); + ctl->val[0] = sblive_readptr(card, addr, 0); + + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_SETPATCH: + if (ctl->val[0] == 0) + memcpy(&card->mgr.rpatch, &ctl->val[1], sizeof(struct dsp_rpatch)); + else { + page = (ctl->val[0] - 1) / PATCHES_PER_PAGE; + if (page > MAX_PATCHES_PAGES) { + ret = -EINVAL; + break; + } + + if (page >= card->mgr.current_pages) { + for (i = card->mgr.current_pages; i < page + 1; i++) { + card->mgr.patch[i] = (void *)__get_free_page(GFP_KERNEL); + if(card->mgr.patch[i] == NULL) { + card->mgr.current_pages = i; + ret = -ENOMEM; + break; + } + memset(card->mgr.patch[i], 0, PAGE_SIZE); + } + card->mgr.current_pages = page + 1; + } + + patch = PATCH(&card->mgr, ctl->val[0] - 1); + + memcpy(patch, &ctl->val[1], sizeof(struct dsp_patch)); + + if (patch->code_size == 0) { + for(i = page + 1; i < card->mgr.current_pages; i++) + free_page((unsigned long) card->mgr.patch[i]); + + card->mgr.current_pages = page + 1; + } + } + break; + + case CMD_SETGPR: + if (ctl->val[0] > NUM_GPRS) { + ret = -EINVAL; + break; + } + + memcpy(&card->mgr.gpr[ctl->val[0]], &ctl->val[1], sizeof(struct dsp_gpr)); + break; + + case CMD_SETCTLGPR: + addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, (char *) ctl->val + PATCH_NAME_SIZE); + emu10k1_set_control_gpr(card, addr, *((s32 *)((char *) ctl->val + 2 * PATCH_NAME_SIZE)), 0); + break; + + case CMD_SETGPOUT: + if (ctl->val[0] > 2 || ctl->val[1] > 1) { + ret= -EINVAL; + break; + } + + emu10k1_writefn0(card, (1 << 24) | (((ctl->val[0]) + 10) << 16) | HCFG, ctl->val[1]); + break; + + case CMD_GETGPR2OSS: + id = ctl->val[0]; + ch = ctl->val[1]; + + if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) { + ret = -EINVAL; + break; + } + + ctl->val[2] = card->mgr.ctrl_gpr[id][ch]; + + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_SETGPR2OSS: + id = ctl->val[0]; + ch = ctl->val[1]; + addr = ctl->val[2]; + + if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) { + ret = -EINVAL; + break; + } + + card->mgr.ctrl_gpr[id][ch] = addr; + + if (card->isaps) + break; + + if (addr >= 0) { + unsigned int state = card->ac97.mixer_state[id]; + + if (ch) { + state >>= 8; + card->ac97.stereo_mixers |= (1 << id); + } else { + 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); + } else if (id == SOUND_MIXER_BASS) { + 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 { + if (ch) { + card->ac97.stereo_mixers &= ~(1 << id); + card->ac97.stereo_mixers |= card->ac97_stereo_mixers; + } else { + card->ac97.supported_mixers &= ~(1 << id); + card->ac97.supported_mixers |= card->ac97_supported_mixers; + } + } + break; + + case CMD_SETPASSTHROUGH: + card->pt.selected = ctl->val[0] ? 1 : 0; + if (card->pt.state != PT_STATE_INACTIVE) + break; + + card->pt.spcs_to_use = ctl->val[0] & 0x07; + break; + + case CMD_PRIVATE3_VERSION: + ctl->val[0]=PRIVATE3_VERSION; + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + break; + + default: + ret = -EINVAL; + break; + } + + kfree(ctl); + return ret; + break; + + case SOUND_MIXER_PRIVATE4: + + if (copy_from_user(&size, (void *) arg, sizeof(size))) + return -EFAULT; + + DPD(2, "External tram size %#x\n", size); + + if (size > 0x1fffff) + return -EINVAL; + + size_reg = 0; + + if (size != 0) { + size = (size - 1) >> 14; + + while (size) { + size >>= 1; + size_reg++; + } + + size = 0x4000 << size_reg; + } + + DPD(2, "External tram size %#x %#x\n", size, size_reg); + + if (size != card->tankmem.size) { + if (card->tankmem.size > 0) { + emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 1); + + sblive_writeptr_tag(card, 0, TCB, 0, TCBS, 0, TAGLIST_END); + + pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle); + + card->tankmem.size = 0; + } + + if (size != 0) { + card->tankmem.addr = pci_alloc_consistent(card->pci_dev, size, &card->tankmem.dma_handle); + if (card->tankmem.addr == NULL) + return -ENOMEM; + + card->tankmem.size = size; + + sblive_writeptr_tag(card, 0, TCB, card->tankmem.dma_handle, TCBS, size_reg, TAGLIST_END); + + emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 0); + } + } + return 0; + break; + + default: + break; + } + + return -EINVAL; +} + +static int emu10k1_dsp_mixer(struct emu10k1_card *card, unsigned int oss_mixer, unsigned long arg) +{ + unsigned int left, right; + int val; + int scale; + + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* cleanse input a little */ + right = ((val >> 8) & 0xff); + left = (val & 0xff); + + if (right > 100) right = 100; + if (left > 100) left = 100; + + card->ac97.mixer_state[oss_mixer] = (right << 8) | left; + if (oss_mixer == SOUND_MIXER_TREBLE) { + set_treble(card, left, right); + return 0; + } if (oss_mixer == SOUND_MIXER_BASS) { + set_bass(card, left, right); + return 0; + } + + if (oss_mixer == SOUND_MIXER_VOLUME) + scale = 1 << card->ac97.bit_resolution; + else + scale = volume_params[oss_mixer]; + + emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, scale); + 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); + + return 0; +} + +static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + struct emu10k1_card *card = file->private_data; + unsigned int oss_mixer = _IOC_NR(cmd); + + ret = -EINVAL; + if (!card->isaps) { + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + + 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; + + return 0; + } + + if ((_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) && oss_mixer <= SOUND_MIXER_NRDEVICES) + ret = emu10k1_dsp_mixer(card, oss_mixer, arg); + else + ret = card->ac97.mixer_ioctl(&card->ac97, cmd, arg); + } + + if (ret < 0) + ret = emu10k1_private_mixer(card, cmd, arg); + + return ret; +} + +static int emu10k1_mixer_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct emu10k1_card *card = NULL; + struct list_head *entry; + + DPF(4, "emu10k1_mixer_open()\n"); + + list_for_each(entry, &emu10k1_devs) { + card = list_entry(entry, struct emu10k1_card, list); + + if (card->ac97.dev_mixer == minor) + goto match; + } + + return -ENODEV; + + match: + file->private_data = card; + return 0; +} + +static int emu10k1_mixer_release(struct inode *inode, struct file *file) +{ + DPF(4, "emu10k1_mixer_release()\n"); + return 0; +} + +struct file_operations emu10k1_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: emu10k1_mixer_ioctl, + open: emu10k1_mixer_open, + release: emu10k1_mixer_release, +}; diff -Nru a/sound/oss/emu10k1/passthrough.c b/sound/oss/emu10k1/passthrough.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/passthrough.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,239 @@ +/* + ********************************************************************** + * passthrough.c -- Emu10k1 digital passthrough + * Copyright (C) 2001 Juha Yrjölä + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * May 15, 2001 Juha Yrjölä base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hwaccess.h" +#include "cardwo.h" +#include "cardwi.h" +#include "recmgr.h" +#include "irqmgr.h" +#include "audio.h" +#include "8010.h" + +static void pt_putsamples(struct pt_data *pt, u16 *ptr, u16 left, u16 right) +{ + unsigned int idx; + + ptr[pt->copyptr] = left; + idx = pt->copyptr + PT_SAMPLES/2; + idx %= PT_SAMPLES; + ptr[idx] = right; +} + +static inline int pt_can_write(struct pt_data *pt) +{ + return pt->blocks_copied < pt->blocks_played + 8; +} + +static int pt_wait_for_write(struct emu10k1_wavedevice *wavedev, int nonblock) +{ + struct emu10k1_card *card = wavedev->card; + struct pt_data *pt = &card->pt; + + if (nonblock && !pt_can_write(pt)) + return -EAGAIN; + while (!pt_can_write(pt) && pt->state != PT_STATE_INACTIVE) { + interruptible_sleep_on(&pt->wait); + if (signal_pending(current)) + return -ERESTARTSYS; + } + if (pt->state == PT_STATE_INACTIVE) + return -EAGAIN; + + return 0; +} + +static int pt_putblock(struct emu10k1_wavedevice *wave_dev, u16 *block, int nonblock) +{ + struct woinst *woinst = wave_dev->woinst; + struct emu10k1_card *card = wave_dev->card; + struct pt_data *pt = &card->pt; + u16 *ptr = (u16 *) card->tankmem.addr; + int i = 0, r; + unsigned long flags; + + r = pt_wait_for_write(wave_dev, nonblock); + if (r < 0) + return r; + spin_lock_irqsave(&card->pt.lock, flags); + while (i < PT_BLOCKSAMPLES) { + pt_putsamples(pt, ptr, block[2*i], block[2*i+1]); + if (pt->copyptr == 0) + pt->copyptr = PT_SAMPLES; + pt->copyptr--; + i++; + } + woinst->total_copied += PT_BLOCKSIZE; + pt->blocks_copied++; + if (pt->blocks_copied >= 4 && pt->state != PT_STATE_PLAYING) { + DPF(2, "activating digital pass-through playback\n"); + sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 1); + pt->state = PT_STATE_PLAYING; + } + spin_unlock_irqrestore(&card->pt.lock, flags); + return 0; +} + +static int pt_setup(struct emu10k1_wavedevice *wave_dev) +{ + u32 bits; + struct emu10k1_card *card = wave_dev->card; + struct pt_data *pt = &card->pt; + int i; + + for (i = 0; i < 3; i++) { + pt->old_spcs[i] = sblive_readptr(card, SPCS0 + i, 0); + if (pt->spcs_to_use & (1 << i)) { + DPD(2, "using S/PDIF port %d\n", i); + bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | + 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT; + if (pt->ac3data) + bits |= SPCS_NOTAUDIODATA; + sblive_writeptr(card, SPCS0 + i, 0, bits); + } + } + return 0; +} + +ssize_t emu10k1_pt_write(struct file *file, const char *buffer, size_t count) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct emu10k1_card *card = wave_dev->card; + struct pt_data *pt = &card->pt; + int nonblock, i, r, blocks, blocks_copied, bytes_copied = 0; + + DPD(3, "emu10k1_pt_write(): %d bytes\n", count); + + nonblock = file->f_flags & O_NONBLOCK; + + if (card->tankmem.size < PT_SAMPLES*2) + return -EFAULT; + if (pt->state == PT_STATE_INACTIVE) { + DPF(2, "bufptr init\n"); + pt->playptr = PT_SAMPLES-1; + pt->copyptr = PT_INITPTR; + pt->blocks_played = pt->blocks_copied = 0; + memset(card->tankmem.addr, 0, card->tankmem.size); + pt->state = PT_STATE_ACTIVATED; + pt->buf = kmalloc(PT_BLOCKSIZE, GFP_KERNEL); + pt->prepend_size = 0; + if (pt->buf == NULL) + return -ENOMEM; + pt_setup(wave_dev); + } + if (pt->prepend_size) { + int needed = PT_BLOCKSIZE - pt->prepend_size; + + DPD(3, "prepend size %d, prepending %d bytes\n", pt->prepend_size, needed); + if (count < needed) { + copy_from_user(pt->buf + pt->prepend_size, buffer, count); + pt->prepend_size += count; + DPD(3, "prepend size now %d\n", pt->prepend_size); + return count; + } + copy_from_user(pt->buf + pt->prepend_size, buffer, needed); + r = pt_putblock(wave_dev, (u16 *) pt->buf, nonblock); + if (r) + return r; + bytes_copied += needed; + pt->prepend_size = 0; + } + blocks = (count-bytes_copied)/PT_BLOCKSIZE; + blocks_copied = 0; + while (blocks > 0) { + u16 *bufptr = (u16 *) buffer + (bytes_copied/2); + copy_from_user(pt->buf, bufptr, PT_BLOCKSIZE); + bufptr = (u16 *) pt->buf; + r = pt_putblock(wave_dev, bufptr, nonblock); + if (r) { + if (bytes_copied) + return bytes_copied; + else + return r; + } + bytes_copied += PT_BLOCKSIZE; + blocks--; + blocks_copied++; + } + i = count - bytes_copied; + if (i) { + pt->prepend_size = i; + copy_from_user(pt->buf, buffer + bytes_copied, i); + bytes_copied += i; + DPD(3, "filling prepend buffer with %d bytes", i); + } + return bytes_copied; +} + +void emu10k1_pt_stop(struct emu10k1_card *card) +{ + struct pt_data *pt = &card->pt; + int i; + + if (pt->state != PT_STATE_INACTIVE) { + DPF(2, "digital pass-through stopped\n"); + sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 0); + for (i = 0; i < 3; i++) { + if (pt->spcs_to_use & (1 << i)) + sblive_writeptr(card, SPCS0 + i, 0, pt->old_spcs[i]); + } + pt->state = PT_STATE_INACTIVE; + kfree(pt->buf); + } +} + +void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev) +{ + struct woinst *woinst = wave_dev->woinst; + struct pt_data *pt = &wave_dev->card->pt; + u32 pos; + + if (pt->state == PT_STATE_PLAYING && pt->pos_gpr >= 0) { + pos = sblive_readptr(wave_dev->card, GPR_BASE + pt->pos_gpr, 0); + if (pos > PT_BLOCKSAMPLES) + pos = PT_BLOCKSAMPLES; + pos = 4 * (PT_BLOCKSAMPLES - pos); + } else + pos = 0; + woinst->total_played = pt->blocks_played * woinst->buffer.fragment_size + pos; + woinst->buffer.hw_pos = pos; +} diff -Nru a/sound/oss/emu10k1/passthrough.h b/sound/oss/emu10k1/passthrough.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/passthrough.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,70 @@ +/* + ********************************************************************** + * passthrough.h -- Emu10k1 digital passthrough header file + * Copyright (C) 2001 Juha Yrjölä + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * May 15, 2001 Juha Yrjölä base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _PASSTHROUGH_H +#define _PASSTHROUGH_H + +#include "audio.h" + +/* number of 16-bit stereo samples in XTRAM buffer */ +#define PT_SAMPLES 0x8000 +#define PT_BLOCKSAMPLES 0x400 +#define PT_BLOCKSIZE (PT_BLOCKSAMPLES*4) +#define PT_BLOCKSIZE_LOG2 12 +#define PT_BLOCKCOUNT (PT_SAMPLES/PT_BLOCKSAMPLES) +#define PT_INITPTR (PT_SAMPLES/2-1) + +#define PT_STATE_INACTIVE 0 +#define PT_STATE_ACTIVATED 1 +#define PT_STATE_PLAYING 2 + +/* passthrough struct */ +struct pt_data +{ + u8 selected, state, spcs_to_use; + int intr_gpr, enable_gpr, pos_gpr; + u32 blocks_played, blocks_copied, old_spcs[3]; + u32 playptr, copyptr; + u32 prepend_size; + u8 *buf; + u8 ac3data; + + char *patch_name, *intr_gpr_name, *enable_gpr_name, *pos_gpr_name; + + wait_queue_head_t wait; + spinlock_t lock; +}; + +ssize_t emu10k1_pt_write(struct file *file, const char *buf, size_t count); +void emu10k1_pt_stop(struct emu10k1_card *card); +void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev); + +#endif /* _PASSTHROUGH_H */ diff -Nru a/sound/oss/emu10k1/recmgr.c b/sound/oss/emu10k1/recmgr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/recmgr.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,138 @@ +/* + ********************************************************************** + * recmgr.c -- Recording manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "8010.h" +#include "recmgr.h" + +void emu10k1_start_record(struct emu10k1_card *card, struct wavein_buffer *buffer) +{ + DPF(2, "emu10k1_start_record()\n"); + + sblive_writeptr(card, buffer->sizereg, 0, buffer->sizeregval); + + if (buffer->adcctl) + sblive_writeptr(card, ADCCR, 0, buffer->adcctl); + + return; +} + +void emu10k1_stop_record(struct emu10k1_card *card, struct wavein_buffer *buffer) +{ + DPF(2, "emu10k1_stop_record()\n"); + + /* Disable record transfer */ + if (buffer->adcctl) + sblive_writeptr(card, ADCCR, 0, 0); + + sblive_writeptr(card, buffer->sizereg, 0, ADCBS_BUFSIZE_NONE); + + return; +} + +void emu10k1_set_record_src(struct emu10k1_card *card, struct wiinst *wiinst) +{ + struct wavein_buffer *buffer = &wiinst->buffer; + + DPF(2, "emu10k1_set_record_src()\n"); + + switch (wiinst->recsrc) { + + case WAVERECORD_AC97: + DPF(2, "recording source: AC97\n"); + buffer->sizereg = ADCBS; + buffer->addrreg = ADCBA; + buffer->idxreg = ADCIDX_IDX; + + switch (wiinst->format.samplingrate) { + case 0xBB80: + buffer->adcctl = ADCCR_SAMPLERATE_48; + break; + case 0xAC44: + buffer->adcctl = ADCCR_SAMPLERATE_44; + break; + case 0x7D00: + buffer->adcctl = ADCCR_SAMPLERATE_32; + break; + case 0x5DC0: + buffer->adcctl = ADCCR_SAMPLERATE_24; + break; + case 0x5622: + buffer->adcctl = ADCCR_SAMPLERATE_22; + break; + case 0x3E80: + buffer->adcctl = ADCCR_SAMPLERATE_16; + break; + case 0x2B11: + buffer->adcctl = ADCCR_SAMPLERATE_11; + break; + case 0x1F40: + buffer->adcctl = ADCCR_SAMPLERATE_8; + break; + default: + BUG(); + break; + } + + buffer->adcctl |= ADCCR_LCHANENABLE; + + if (wiinst->format.channels == 2) + buffer->adcctl |= ADCCR_RCHANENABLE; + + break; + + case WAVERECORD_MIC: + DPF(2, "recording source: MIC\n"); + buffer->sizereg = MICBS; + buffer->addrreg = MICBA; + buffer->idxreg = MICIDX_IDX; + buffer->adcctl = 0; + break; + + case WAVERECORD_FX: + DPF(2, "recording source: FX\n"); + buffer->sizereg = FXBS; + buffer->addrreg = FXBA; + buffer->idxreg = FXIDX_IDX; + buffer->adcctl = 0; + + sblive_writeptr(card, FXWC, 0, wiinst->fxwc); + break; + default: + BUG(); + break; + } + + DPD(2, "bus addx: %#x\n", buffer->dma_handle); + + sblive_writeptr(card, buffer->addrreg, 0, buffer->dma_handle); + + return; +} diff -Nru a/sound/oss/emu10k1/recmgr.h b/sound/oss/emu10k1/recmgr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/recmgr.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,48 @@ +/* + ********************************************************************** + * recmgr.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _RECORDMGR_H +#define _RECORDMGR_H + +#include "hwaccess.h" +#include "cardwi.h" + +/* Recording resources */ +#define WAVERECORD_AC97 0x01 +#define WAVERECORD_MIC 0x02 +#define WAVERECORD_FX 0x03 + +void emu10k1_start_record(struct emu10k1_card *, struct wavein_buffer *); +void emu10k1_stop_record(struct emu10k1_card *, struct wavein_buffer *); +void emu10k1_set_record_src(struct emu10k1_card *, struct wiinst *wiinst); + + +#endif /* _RECORDMGR_H */ diff -Nru a/sound/oss/emu10k1/timer.c b/sound/oss/emu10k1/timer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/timer.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,176 @@ + +/* + ********************************************************************** + * timer.c + * Copyright (C) 1999, 2000 Creative Labs, inc. + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +/* 3/6/2000 Improved support for different timer delays Rui Sousa */ + +/* 4/3/2000 Implemented timer list using list.h Rui Sousa */ + +#include "hwaccess.h" +#include "8010.h" +#include "irqmgr.h" +#include "timer.h" + +/* Try to schedule only once per fragment */ + +void emu10k1_timer_irqhandler(struct emu10k1_card *card) +{ + struct emu_timer *t; + struct list_head *entry; + + spin_lock(&card->timer_lock); + + list_for_each(entry, &card->timers) { + t = list_entry(entry, struct emu_timer, list); + + if (t->state & TIMER_STATE_ACTIVE) { + t->count++; + if (t->count == t->count_max) { + t->count = 0; + tasklet_hi_schedule(&t->tasklet); + } + } + } + + spin_unlock(&card->timer_lock); + + return; +} + +void emu10k1_timer_install(struct emu10k1_card *card, struct emu_timer *timer, u32 delay) +{ + struct emu_timer *t; + struct list_head *entry; + unsigned long flags; + + if (delay < 5) + delay = 5; + + timer->delay = delay; + timer->state = TIMER_STATE_INSTALLED; + + spin_lock_irqsave(&card->timer_lock, flags); + + timer->count_max = timer->delay / (card->timer_delay < 1024 ? card->timer_delay : 1024); + timer->count = timer->count_max - 1; + + list_add(&timer->list, &card->timers); + + if (card->timer_delay > delay) { + if (card->timer_delay == TIMER_STOPPED) + emu10k1_irq_enable(card, INTE_INTERVALTIMERENB); + + card->timer_delay = delay; + delay = (delay < 1024 ? delay : 1024); + + emu10k1_writefn0(card, TIMER_RATE, delay); + + list_for_each(entry, &card->timers) { + t = list_entry(entry, struct emu_timer, list); + + t->count_max = t->delay / delay; + /* don't want to think much, just force scheduling + on the next interrupt */ + t->count = t->count_max - 1; + } + + DPD(2, "timer rate --> %u\n", delay); + } + + spin_unlock_irqrestore(&card->timer_lock, flags); + + return; +} + +void emu10k1_timer_uninstall(struct emu10k1_card *card, struct emu_timer *timer) +{ + struct emu_timer *t; + struct list_head *entry; + u32 delay = TIMER_STOPPED; + unsigned long flags; + + if (timer->state == TIMER_STATE_UNINSTALLED) + return; + + spin_lock_irqsave(&card->timer_lock, flags); + + list_del(&timer->list); + + list_for_each(entry, &card->timers) { + t = list_entry(entry, struct emu_timer, list); + + if (t->delay < delay) + delay = t->delay; + } + + if (card->timer_delay != delay) { + card->timer_delay = delay; + + if (delay == TIMER_STOPPED) + emu10k1_irq_disable(card, INTE_INTERVALTIMERENB); + else { + delay = (delay < 1024 ? delay : 1024); + + emu10k1_writefn0(card, TIMER_RATE, delay); + + list_for_each(entry, &card->timers) { + t = list_entry(entry, struct emu_timer, list); + + t->count_max = t->delay / delay; + t->count = t->count_max - 1; + } + } + + DPD(2, "timer rate --> %u\n", delay); + } + + spin_unlock_irqrestore(&card->timer_lock, flags); + + timer->state = TIMER_STATE_UNINSTALLED; + + return; +} + +void emu10k1_timer_enable(struct emu10k1_card *card, struct emu_timer *timer) +{ + unsigned long flags; + + spin_lock_irqsave(&card->timer_lock, flags); + timer->state |= TIMER_STATE_ACTIVE; + spin_unlock_irqrestore(&card->timer_lock, flags); + + return; +} + +void emu10k1_timer_disable(struct emu10k1_card *card, struct emu_timer *timer) +{ + unsigned long flags; + + spin_lock_irqsave(&card->timer_lock, flags); + timer->state &= ~TIMER_STATE_ACTIVE; + spin_unlock_irqrestore(&card->timer_lock, flags); + + return; +} diff -Nru a/sound/oss/emu10k1/timer.h b/sound/oss/emu10k1/timer.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/timer.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,54 @@ +/* + ********************************************************************** + * timer.h + * Copyright (C) 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + + +#ifndef _TIMER_H +#define _TIMER_H + +#include +#include +#include "hwaccess.h" + +struct emu_timer +{ + struct list_head list; + struct tasklet_struct tasklet; + u8 state; + u32 count; /* current number of interrupts */ + u32 count_max; /* number of interrupts needed to schedule the bh */ + u32 delay; /* timer delay */ +}; + +void emu10k1_timer_install(struct emu10k1_card *, struct emu_timer *, u32); +void emu10k1_timer_uninstall(struct emu10k1_card *, struct emu_timer *); +void emu10k1_timer_enable(struct emu10k1_card *, struct emu_timer *); +void emu10k1_timer_disable(struct emu10k1_card *, struct emu_timer *); + +#define TIMER_STOPPED 0xffffffff +#define TIMER_STATE_INSTALLED 0x01 +#define TIMER_STATE_ACTIVE 0x02 +#define TIMER_STATE_UNINSTALLED 0x04 + +#endif /* _TIMER_H */ diff -Nru a/sound/oss/emu10k1/voicemgr.c b/sound/oss/emu10k1/voicemgr.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/voicemgr.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,281 @@ +/* + ********************************************************************** + * voicemgr.c - Voice manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "voicemgr.h" +#include "8010.h" + +int emu10k1_voice_alloc(struct emu10k1_card *card, struct emu_voice *voice) +{ + u8 *voicetable = card->voicetable; + int i; + unsigned long flags; + + DPF(2, "emu10k1_voice_alloc()\n"); + + spin_lock_irqsave(&card->lock, flags); + + if (voice->flags & VOICE_FLAGS_STEREO) { + for (i = 0; i < NUM_G; i += 2) + if ((voicetable[i] == VOICE_USAGE_FREE) && (voicetable[i + 1] == VOICE_USAGE_FREE)) { + voicetable[i] = voice->usage; + voicetable[i + 1] = voice->usage; + break; + } + } else { + for (i = 0; i < NUM_G; i++) + if (voicetable[i] == VOICE_USAGE_FREE) { + voicetable[i] = voice->usage; + break; + } + } + + spin_unlock_irqrestore(&card->lock, flags); + + if (i >= NUM_G) + return -1; + + voice->card = card; + voice->num = i; + + for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) { + DPD(2, " voice allocated -> %d\n", voice->num + i); + + sblive_writeptr_tag(card, voice->num + i, IFATN, 0xffff, + DCYSUSV, 0, + VTFT, 0x0000ffff, + PTRX, 0, + TAGLIST_END); + } + + return 0; +} + +void emu10k1_voice_free(struct emu_voice *voice) +{ + struct emu10k1_card *card = voice->card; + int i; + unsigned long flags; + + DPF(2, "emu10k1_voice_free()\n"); + + if (voice->usage == VOICE_USAGE_FREE) + return; + + for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) { + DPD(2, " voice released -> %d\n", voice->num + i); + + sblive_writeptr_tag(card, voice->num + i, DCYSUSV, 0, + VTFT, 0x0000ffff, + PTRX_PITCHTARGET, 0, + CVCF, 0x0000ffff, + CPF, 0, + TAGLIST_END); + } + + voice->usage = VOICE_USAGE_FREE; + + spin_lock_irqsave(&card->lock, flags); + + card->voicetable[voice->num] = VOICE_USAGE_FREE; + + if (voice->flags & VOICE_FLAGS_STEREO) + card->voicetable[voice->num + 1] = VOICE_USAGE_FREE; + + spin_unlock_irqrestore(&card->lock, flags); +} + +void emu10k1_voice_playback_setup(struct emu_voice *voice) +{ + struct emu10k1_card *card = voice->card; + u32 start; + int i; + + DPF(2, "emu10k1_voice_playback_setup()\n"); + + if (voice->flags & VOICE_FLAGS_STEREO) { + /* Set stereo bit */ + start = 28; + sblive_writeptr(card, CPF, voice->num, CPF_STEREO_MASK); + sblive_writeptr(card, CPF, voice->num + 1, CPF_STEREO_MASK); + } else { + start = 30; + sblive_writeptr(card, CPF, voice->num, 0); + } + + if(!(voice->flags & VOICE_FLAGS_16BIT)) + start *= 2; + + voice->start += start; + + for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) { + sblive_writeptr(card, FXRT, voice->num + i, voice->params[i].send_routing << 16); + + /* Stop CA */ + /* Assumption that PT is already 0 so no harm overwriting */ + sblive_writeptr(card, PTRX, voice->num + i, (voice->params[i].send_a << 8) | voice->params[i].send_b); + + sblive_writeptr_tag(card, voice->num + i, + /* CSL, ST, CA */ + DSL, voice->endloop | (voice->params[i].send_d << 24), + PSST, voice->startloop | (voice->params[i].send_c << 24), + CCCA, (voice->start) | CCCA_INTERPROM_0 | ((voice->flags & VOICE_FLAGS_16BIT) ? 0 : CCCA_8BITSELECT), + /* Clear filter delay memory */ + Z1, 0, + Z2, 0, + /* Invalidate maps */ + MAPA, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), + MAPB, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), + /* modulation envelope */ + CVCF, 0x0000ffff, + VTFT, 0x0000ffff, + ATKHLDM, 0, + DCYSUSM, 0x007f, + LFOVAL1, 0x8000, + LFOVAL2, 0x8000, + FMMOD, 0, + TREMFRQ, 0, + FM2FRQ2, 0, + ENVVAL, 0x8000, + /* volume envelope */ + ATKHLDV, 0x7f7f, + ENVVOL, 0x8000, + /* filter envelope */ + PEFE_FILTERAMOUNT, 0x7f, + /* pitch envelope */ + PEFE_PITCHAMOUNT, 0, TAGLIST_END); + + voice->params[i].fc_target = 0xffff; + } +} + +void emu10k1_voices_start(struct emu_voice *first_voice, unsigned int num_voices, int set) +{ + struct emu10k1_card *card = first_voice->card; + struct emu_voice *voice; + unsigned int voicenum; + int j; + + DPF(2, "emu10k1_voices_start()\n"); + + for (voicenum = 0; voicenum < num_voices; voicenum++) + { + voice = first_voice + voicenum; + + if (!set) { + u32 cra, ccis, cs, sample; + if (voice->flags & VOICE_FLAGS_STEREO) { + cra = 64; + ccis = 28; + cs = 4; + } else { + cra = 64; + ccis = 30; + cs = 2; + } + + if(voice->flags & VOICE_FLAGS_16BIT) { + sample = 0x00000000; + } else { + sample = 0x80808080; + ccis *= 2; + } + + for(j = 0; j < cs; j++) + sblive_writeptr(card, CD0 + j, voice->num, sample); + + /* Reset cache */ + sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num, 0); + if (voice->flags & VOICE_FLAGS_STEREO) + sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num + 1, 0); + + sblive_writeptr(card, CCR_READADDRESS, voice->num, cra); + + if (voice->flags & VOICE_FLAGS_STEREO) + sblive_writeptr(card, CCR_READADDRESS, voice->num + 1, cra); + + /* Fill cache */ + sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num, ccis); + } + + for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) { + sblive_writeptr_tag(card, voice->num + j, + IFATN, (voice->params[j].initial_fc << 8) | voice->params[j].initial_attn, + VTFT, (voice->params[j].volume_target << 16) | voice->params[j].fc_target, + CVCF, (voice->params[j].volume_target << 16) | voice->params[j].fc_target, + DCYSUSV, (voice->params[j].byampl_env_sustain << 8) | voice->params[j].byampl_env_decay, + TAGLIST_END); + + emu10k1_clear_stop_on_loop(card, voice->num + j); + } + } + + + for (voicenum = 0; voicenum < num_voices; voicenum++) + { + voice = first_voice + voicenum; + + for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) { + sblive_writeptr(card, PTRX_PITCHTARGET, voice->num + j, voice->pitch_target); + + if (j == 0) + sblive_writeptr(card, CPF_CURRENTPITCH, voice->num, voice->pitch_target); + + sblive_writeptr(card, IP, voice->num + j, voice->initial_pitch); + } + } +} + +void emu10k1_voices_stop(struct emu_voice *first_voice, int num_voices) +{ + struct emu10k1_card *card = first_voice->card; + struct emu_voice *voice; + unsigned int voice_num; + int j; + + DPF(2, "emu10k1_voice_stop()\n"); + + for (voice_num = 0; voice_num < num_voices; voice_num++) + { + voice = first_voice + voice_num; + + for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) { + sblive_writeptr_tag(card, voice->num + j, + PTRX_PITCHTARGET, 0, + CPF_CURRENTPITCH, 0, + IFATN, 0xffff, + VTFT, 0x0000ffff, + CVCF, 0x0000ffff, + IP, 0, + TAGLIST_END); + } + } +} + diff -Nru a/sound/oss/emu10k1/voicemgr.h b/sound/oss/emu10k1/voicemgr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/emu10k1/voicemgr.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,91 @@ +/* + ********************************************************************** + * sblive_voice.h -- EMU Voice Resource Manager header file + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _VOICEMGR_H +#define _VOICEMGR_H + +#include "hwaccess.h" + +/* struct emu_voice.usage flags */ +#define VOICE_USAGE_FREE 0x01 +#define VOICE_USAGE_MIDI 0x02 +#define VOICE_USAGE_PLAYBACK 0x04 + +/* struct emu_voice.flags flags */ +#define VOICE_FLAGS_STEREO 0x02 +#define VOICE_FLAGS_16BIT 0x04 + +struct voice_param +{ + /* FX bus amount send */ + + u32 send_routing; + + u32 send_a; + u32 send_b; + u32 send_c; + u32 send_d; + + u32 initial_fc; + u32 fc_target; + + u32 initial_attn; + u32 volume_target; + + u32 byampl_env_sustain; + u32 byampl_env_decay; +}; + + +struct emu_voice +{ + struct emu10k1_card *card; + u8 usage; /* Free, MIDI, playback */ + u8 num; /* Voice ID */ + u8 flags; /* Stereo/mono, 8/16 bit */ + + u32 startloop; + u32 endloop; + u32 start; + + u32 initial_pitch; + u32 pitch_target; + + struct voice_param params[2]; +}; + +int emu10k1_voice_alloc(struct emu10k1_card *, struct emu_voice *); +void emu10k1_voice_free(struct emu_voice *); +void emu10k1_voice_playback_setup(struct emu_voice *); +void emu10k1_voices_start(struct emu_voice *, unsigned int, int); +void emu10k1_voices_stop(struct emu_voice *, int); + +#endif /* _VOICEMGR_H */ diff -Nru a/sound/oss/es1370.c b/sound/oss/es1370.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/es1370.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2784 @@ +/*****************************************************************************/ + +/* + * es1370.c -- Ensoniq ES1370/Asahi Kasei AK4531 audio driver. + * + * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * 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. + * + * Special thanks to David C. Niemi + * + * + * Module command line parameters: + * joystick if 1 enables the joystick interface on the card; but it still + * needs a driver for joysticks connected to a standard IBM-PC + * joyport. It is tested with the joy-analog driver. This + * module must be loaded before the joystick driver. Kmod will + * not ensure that. + * lineout if 1 the LINE jack is used as an output instead of an input. + * LINE then contains the unmixed dsp output. This can be used + * to make the card a four channel one: use dsp to output two + * channels to LINE and dac to output the other two channels to + * SPKR. Set the mixer to only output synth to SPKR. + * micbias sets the +5V bias to the mic if using an electretmic. + * + * + * Note: sync mode is not yet supported (i.e. running dsp and dac from the same + * clock source) + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/dsp1 additional DAC, like /dev/dsp, but output only, + * only 5512, 11025, 22050 and 44100 samples/s, + * outputs to mixer "SYNTH" setting + * /dev/midi simple MIDI UART interface, no ioctl + * + * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed + * to be done in software. That is what /dev/dac is for. By now (Q2 1998) + * there are several MIDI to PCM (WAV) packages, one of them is timidity. + * + * Revision history + * 26.03.1998 0.1 Initial release + * 31.03.1998 0.2 Fix bug in GETOSPACE + * 04.04.1998 0.3 Make it work (again) under 2.0.33 + * Fix mixer write operation not returning the actual + * settings + * 05.04.1998 0.4 First attempt at using the new PCI stuff + * 29.04.1998 0.5 Fix hang when ^C is pressed on amp + * 07.05.1998 0.6 Don't double lock around stop_*() in *_release() + * 10.05.1998 0.7 First stab at a simple midi interface (no bells&whistles) + * 14.05.1998 0.8 Don't allow excessive interrupt rates + * 08.06.1998 0.9 First release using Alan Cox' soundcore instead of + * miscdevice + * 05.07.1998 0.10 Fixed the driver to correctly maintin OSS style volume + * settings (not sure if this should be standard) + * Fixed many references: f_flags should be f_mode + * -- Gerald Britton + * 03.08.1998 0.11 Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * Fixed mixer table thanks to Hakan.Lennestal@lu.erisoft.se + * On module startup, set DAC2 to 11kSPS instead of 5.5kSPS, + * as it produces an annoying ssssh in the lower sampling rate + * Do not include modversions.h + * 22.08.1998 0.12 Mixer registers actually have 5 instead of 4 bits + * pointed out by Itai Nahshon + * 31.08.1998 0.13 Fix realplayer problems - dac.count issues + * 08.10.1998 0.14 Joystick support fixed + * -- Oliver Neukum + * 10.12.1998 0.15 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.1998 0.16 Don't wake up app until there are fragsize bytes to read/write + * 06.01.1999 0.17 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.18 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.19 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.20 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Note: joystick address handling might still be wrong on archs + * other than i386 + * 10.05.1999 0.21 Added support for an electret mic for SB PCI64 + * to the Linux kernel sound driver. This mod also straighten + * out the question marks around the mic impedance setting + * (micz). From Kim.Berts@fisub.mail.abb.com + * 11.05.1999 0.22 Implemented the IMIX call to mute recording monitor. + * Guenter Geiger + * 15.06.1999 0.23 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.24 Add pci_set_master + * 02.08.1999 0.25 Added workaround for the "phantom write" bug first + * documented by Dave Sharpless from Anchor Games + * 03.08.1999 0.26 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1370=joystick[,lineout[,micbias]]" + * removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge + * 12.08.1999 0.27 module_init/__setup fixes + * 19.08.1999 0.28 SOUND_MIXER_IMIX fixes, reported by Gianluca + * 31.08.1999 0.29 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.30 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 28.10.1999 0.31 More waitqueue races fixed + * 08.01.2000 0.32 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * 07.02.2000 0.33 Use pci_alloc_consistent and pci_register_driver + * 21.11.2000 0.34 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.35 More dma buffer initializations, patch from + * Tjeerd Mulder + * 07.01.2001 0.36 Timeout change in wrcodec as requested by Frank Klemm + * 31.01.2001 0.37 Register/Unregister gameport + * Fix SETTRIGGER non OSS API conformity + * + * some important things missing in Ensoniq documentation: + * + * Experimental PCLKDIV results: play the same waveforms on both DAC1 and DAC2 + * and vary PCLKDIV to obtain zero beat. + * 5512sps: 254 + * 44100sps: 30 + * seems to be fs = 1411200/(PCLKDIV+2) + * + * should find out when curr_sample_ct is cleared and + * where exactly the CCB fetches data + * + * The card uses a 22.5792 MHz crystal. + * The LINEIN jack may be converted to an AOUT jack by + * setting pin 47 (XCTL0) of the ES1370 to high. + * Pin 48 (XCTL1) of the ES1370 sets the +5V bias for an electretmic + * + * + */ + +/*****************************************************************************/ + +#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 DBG(x) {} +/*#define DBG(x) {x}*/ + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ENSONIQ +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#endif + +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1370 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 +#endif + +#define ES1370_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1370) + +#define ES1370_EXTENT 0x40 +#define JOY_EXTENT 8 + +#define ES1370_REG_CONTROL 0x00 +#define ES1370_REG_STATUS 0x04 +#define ES1370_REG_UART_DATA 0x08 +#define ES1370_REG_UART_STATUS 0x09 +#define ES1370_REG_UART_CONTROL 0x09 +#define ES1370_REG_UART_TEST 0x0a +#define ES1370_REG_MEMPAGE 0x0c +#define ES1370_REG_CODEC 0x10 +#define ES1370_REG_SERIAL_CONTROL 0x20 +#define ES1370_REG_DAC1_SCOUNT 0x24 +#define ES1370_REG_DAC2_SCOUNT 0x28 +#define ES1370_REG_ADC_SCOUNT 0x2c + +#define ES1370_REG_DAC1_FRAMEADR 0xc30 +#define ES1370_REG_DAC1_FRAMECNT 0xc34 +#define ES1370_REG_DAC2_FRAMEADR 0xc38 +#define ES1370_REG_DAC2_FRAMECNT 0xc3c +#define ES1370_REG_ADC_FRAMEADR 0xd30 +#define ES1370_REG_ADC_FRAMECNT 0xd34 +#define ES1370_REG_PHANTOM_FRAMEADR 0xd38 +#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c + +#define ES1370_FMT_U8_MONO 0 +#define ES1370_FMT_U8_STEREO 1 +#define ES1370_FMT_S16_MONO 2 +#define ES1370_FMT_S16_STEREO 3 +#define ES1370_FMT_STEREO 1 +#define ES1370_FMT_S16 2 +#define ES1370_FMT_MASK 3 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; + +#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2) +#define DAC2_DIVTOSR(x) (1411200/((x)+2)) + +#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ +#define CTRL_XCTL1 0x40000000 /* electret mic bias */ +#define CTRL_OPEN 0x20000000 /* no function, can be read and written */ +#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ +#define CTRL_SH_PCLKDIV 16 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ +#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */ +#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */ +#define CTRL_SH_WTSRSEL 12 +#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */ +#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */ +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */ +#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */ +#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */ + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */ +#define STAT_CBUSY 0x00000200 /* 1 = codec busy */ +#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */ +#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ +#define STAT_SH_VC 5 +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define USTAT_RXINT 0x80 /* UART rx int pending */ +#define USTAT_TXINT 0x04 /* UART tx int pending */ +#define USTAT_TXRDY 0x02 /* UART tx ready */ +#define USTAT_RXRDY 0x01 /* UART rx ready */ + +#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ +#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ +#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ +#define UCTRL_CNTRL 0x03 /* control field */ +#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ + +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + +/* misc stuff */ + +#define FMODE_DAC 4 /* slight misuse of mode_t */ + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +/* --------------------------------------------------------------------- */ + +struct es1370_state { + /* magic */ + unsigned int magic; + + /* list of es1370 devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_dac; + int dev_midi; + + /* hardware resources */ + unsigned long io; /* long for SPARC */ + unsigned int irq; + + /* mixer registers; there is no HW readback */ + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + unsigned int imix; + } mix; + + /* wave stuff */ + unsigned ctrl; + unsigned sctrl; + + 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; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + 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 endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac1, dma_dac2, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct gameport gameport; + struct semaphore sem; +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); + +/* + * The following buffer is used to point the phantom write channel to, + * so that it cannot wreak havoc. The attribute makes sure it doesn't + * cross a page boundary and ensures dword alignment for the DMA engine + */ +static unsigned char bugbuf[16] __attribute__ ((aligned (16))); + +/* --------------------------------------------------------------------- */ + +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; +} + +/* --------------------------------------------------------------------- */ + +static void wrcodec(struct es1370_state *s, unsigned char idx, unsigned char data) +{ + unsigned long tmo = jiffies + HZ/10, j; + + do { + j = jiffies; + if (!(inl(s->io+ES1370_REG_STATUS) & STAT_CSTAT)) { + outw((((unsigned short)idx)<<8)|data, s->io+ES1370_REG_CODEC); + return; + } + schedule(); + } while ((signed)(tmo-j) > 0); + printk(KERN_ERR "es1370: write to codec register timeout\n"); +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_adc(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac1(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac2(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) + && s->dma_dac1.ready) { + s->ctrl |= CTRL_DAC1_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac1.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac2(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) + && s->dma_dac2.ready) { + s->ctrl |= CTRL_DAC2_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | + SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | + (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | + (0 << SCTRL_SH_P2STINC); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac2.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->ctrl |= CTRL_ADC_EN; + s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_adc.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static inline void dealloc_dmabuf(struct es1370_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); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct es1370_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + 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); + } + fmt &= ES1370_FMT_MASK; + 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--; + 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 & ES1370_FMT_S16) ? 0 : 0x80, db->dmasize); + outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + outl(db->dmaaddr, s->io+(reg & 0xff)); + outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); + db->enabled = 1; + db->ready = 1; + return 0; +} + +static inline int prog_dmabuf_adc(struct es1370_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + (s->sctrl >> SCTRL_SH_R1FMT) & ES1370_FMT_MASK, ES1370_REG_ADC_FRAMEADR); +} + +static inline int prog_dmabuf_dac2(struct es1370_state *s) +{ + stop_dac2(s); + return prog_dmabuf(s, &s->dma_dac2, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + (s->sctrl >> SCTRL_SH_P2FMT) & ES1370_FMT_MASK, ES1370_REG_DAC2_FRAMEADR); +} + +static inline int prog_dmabuf_dac1(struct es1370_state *s) +{ + stop_dac1(s); + return prog_dmabuf(s, &s->dma_dac1, dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], + (s->sctrl >> SCTRL_SH_P1FMT) & ES1370_FMT_MASK, ES1370_REG_DAC1_FRAMEADR); +} + +static inline unsigned get_hwptr(struct es1370_state *s, struct dmabuf *db, unsigned reg) +{ + unsigned hwptr, diff; + + outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; + diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; + db->hwptr = hwptr; + return diff; +} + +static inline 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; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ +static void es1370_update_ptr(struct es1370_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->ctrl & CTRL_ADC_EN) { + diff = get_hwptr(s, &s->dma_adc, ES1370_REG_ADC_FRAMECNT); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_adc.error++; + } + } + } + /* update DAC1 pointer */ + if (s->ctrl & CTRL_DAC1_EN) { + diff = get_hwptr(s, &s->dma_dac1, ES1370_REG_DAC1_FRAMECNT); + s->dma_dac1.total_bytes += diff; + if (s->dma_dac1.mapped) { + s->dma_dac1.count += diff; + if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) + wake_up(&s->dma_dac1.wait); + } else { + s->dma_dac1.count -= diff; + if (s->dma_dac1.count <= 0) { + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_dac1.error++; + } else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { + clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, + s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); + s->dma_dac1.endcleared = 1; + } + if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize) + wake_up(&s->dma_dac1.wait); + } + } + /* update DAC2 pointer */ + if (s->ctrl & CTRL_DAC2_EN) { + diff = get_hwptr(s, &s->dma_dac2, ES1370_REG_DAC2_FRAMECNT); + s->dma_dac2.total_bytes += diff; + if (s->dma_dac2.mapped) { + s->dma_dac2.count += diff; + if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) + wake_up(&s->dma_dac2.wait); + } else { + s->dma_dac2.count -= diff; + if (s->dma_dac2.count <= 0) { + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_dac2.error++; + } else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { + clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, + s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); + s->dma_dac2.endcleared = 1; + } + if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize) + wake_up(&s->dma_dac2.wait); + } + } +} + +/* hold spinlock for the following! */ +static void es1370_handle_midi(struct es1370_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->ctrl & CTRL_UART_EN)) + return; + wake = 0; + while (inb(s->io+ES1370_REG_UART_STATUS) & USTAT_RXRDY) { + ch = inb(s->io+ES1370_REG_UART_DATA); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while ((inb(s->io+ES1370_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->io+ES1370_REG_UART_DATA); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); + outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1370_REG_UART_CONTROL); +} + +static void es1370_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct es1370_state *s = (struct es1370_state *)dev_id; + unsigned int intsrc, sctl; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->io+ES1370_REG_STATUS); + if (!(intsrc & 0x80000000)) + return; + spin_lock(&s->lock); + /* clear audio interrupts first */ + sctl = s->sctrl; + if (intsrc & STAT_ADC) + sctl &= ~SCTRL_R1INTEN; + if (intsrc & STAT_DAC1) + sctl &= ~SCTRL_P1INTEN; + if (intsrc & STAT_DAC2) + sctl &= ~SCTRL_P2INTEN; + outl(sctl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + es1370_update_ptr(s); + es1370_handle_midi(s); + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "es1370: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != ES1370_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +static const struct { + unsigned volidx:4; + unsigned left:4; + unsigned right:4; + unsigned stereo:1; + unsigned recmask:13; + unsigned avail:1; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 }, /* master */ + [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 }, /* voice */ + [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 }, /* FM */ + [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 }, /* CD */ + [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 }, /* Line */ + [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 }, /* AUX */ + [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 }, /* Mono1 */ + [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 }, /* Mono2 */ + [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 }, /* Mic */ + [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } /* mono out */ +}; + +static void set_recsrc(struct es1370_state *s, unsigned int val) +{ + unsigned int i, j; + + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].recmask) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].recmask; + } + s->mix.recsrc = val; + wrcodec(s, 0x12, j & 0xd5); + wrcodec(s, 0x13, j & 0xaa); + wrcodec(s, 0x14, (j >> 8) & 0x17); + wrcodec(s, 0x15, (j >> 8) & 0x0f); + i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60; + if (!s->mix.imix) { + i &= 0xff60; /* mute record and line monitor */ + } + wrcodec(s, 0x10, i); + wrcodec(s, 0x11, i >> 8); +} + +static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val; + unsigned char l, r, rl, rr; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_PRIVATE1) { + /* enable/disable/query mixer preamp */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != -1) { + s->mix.micpreamp = !!val; + wrcodec(s, 0x19, s->mix.micpreamp); + } + return put_user(s->mix.micpreamp, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + /* enable/disable/query use of linein as second lineout */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != -1) { + spin_lock_irqsave(&s->lock, flags); + if (val) + s->ctrl |= CTRL_XCTL0; + else + s->ctrl &= ~CTRL_XCTL0; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->ctrl & CTRL_XCTL0) ? 1 : 0, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE3) { + /* enable/disable/query microphone impedance setting */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != -1) { + spin_lock_irqsave(&s->lock, flags); + if (val) + s->ctrl |= CTRL_XCTL1; + else + s->ctrl &= ~CTRL_XCTL1; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->ctrl & CTRL_XCTL1) ? 1 : 0, (int *)arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + 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; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + 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; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(s->mix.recsrc, (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + val = SOUND_MASK_IMIX; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].avail) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].recmask) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].stereo) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(0, (int *)arg); + + case SOUND_MIXER_IMIX: + return put_user(s->mix.imix, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg); + } + } + if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + + case SOUND_MIXER_IMIX: + if (get_user(s->mix.imix, (int *)arg)) + return -EFAULT; + set_recsrc(s, s->mix.recsrc); + return 0; + + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (get_user(val, (int *)arg)) + return -EFAULT; + set_recsrc(s, val); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (mixtable[i].stereo) { + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (l < 7) { + rl = 0x80; + l = 0; + } else { + rl = 31 - ((l - 7) / 3); + l = (31 - rl) * 3 + 7; + } + if (r < 7) { + rr = 0x80; + r = 0; + } else { + rr = 31 - ((r - 7) / 3); + r = (31 - rr) * 3 + 7; + } + wrcodec(s, mixtable[i].right, rr); + } else { + if (mixtable[i].left == 15) { + if (l < 2) { + rr = rl = 0x80; + r = l = 0; + } else { + rl = 7 - ((l - 2) / 14); + r = l = (7 - rl) * 14 + 2; + } + } else { + if (l < 7) { + rl = 0x80; + r = l = 0; + } else { + rl = 31 - ((l - 7) / 3); + r = l = (31 - rl) * 3 + 7; + } + } + } + wrcodec(s, mixtable[i].left, rl); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[mixtable[i].volidx] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[mixtable[i].volidx] = val; +#endif + return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +static int es1370_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (s->dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + return 0; +} + +static int es1370_release_mixdev(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int es1370_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct es1370_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations es1370_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: es1370_ioctl_mixdev, + open: es1370_open_mixdev, + release: es1370_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac1(struct es1370_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac1.mapped || !s->dma_dac1.ready) + return 0; + add_wait_queue(&s->dma_dac1.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 + / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; + tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac2(struct es1370_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac2.mapped || !s->dma_dac2.ready) + return 0; + add_wait_queue(&s->dma_dac2.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 + / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + down(&s->sem); + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + goto out; + + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); + 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 out; + } + down(&s->sem); + if (s->dma_adc.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); + } +out: + up(&s->sem); + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac2.mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + down(&s->sem); + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + goto out; + ret = 0; + add_wait_queue(&s->dma_dac2.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac2.count < 0) { + s->dma_dac2.count = 0; + s->dma_dac2.swptr = s->dma_dac2.hwptr; + } + swptr = s->dma_dac2.swptr; + cnt = s->dma_dac2.dmasize-swptr; + if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) + cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac2.enabled) + start_dac2(s); + 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 out; + } + down(&s->sem); + if (s->dma_dac2.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_dac2.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac2.swptr = swptr; + s->dma_dac2.count += cnt; + s->dma_dac2.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac2.enabled) + start_dac2(s); + } +out: + up(&s->sem); + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1370_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac2.ready && prog_dmabuf_dac2(s)) + return 0; + poll_wait(file, &s->dma_dac2.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + 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_dac2.mapped) { + if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + struct dmabuf *db; + int ret = 0; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + down(&s->sem); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac2(s)) != 0) { + goto out; + } + db = &s->dma_dac2; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) { + goto out; + } + 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, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) { + ret = -EAGAIN; + goto out; + } + db->mapped = 1; +out: + up(&s->sem); + unlock_kernel(); + return ret; +} + +static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac2(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: + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(); + s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (s->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) + return -EINVAL; + if (val < 4000) + val = 4000; + if (val > 50000) + val = 50000; + stop_adc(s); + stop_dac2(s); + s->dma_adc.ready = s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (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.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + 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); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (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); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_R1SEB; + else + s->sctrl &= ~SCTRL_R1SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P2SEB; + else + s->sctrl &= ~SCTRL_P2SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + 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; + s->dma_adc.enabled = 1; + start_adc(s); + } else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + s->dma_dac2.enabled = 1; + start_dac2(s); + } else { + s->dma_dac2.enabled = 0; + stop_dac2(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_dac2.fragsize; + count = s->dma_dac2.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac2.dmasize - count; + abinfo.fragstotal = s->dma_dac2.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; + 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)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + count = s->dma_adc.count; + if (count < 0) + count = 0; + abinfo.bytes = 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_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + count = s->dma_dac2.count; + 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; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + 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_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_dac2.total_bytes; + count = s->dma_dac2.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac2.fragshift; + cinfo.ptr = s->dma_dac2.hwptr; + if (s->dma_dac2.mapped) + s->dma_dac2.count &= s->dma_dac2.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_dac2(s))) + return val; + return put_user(s->dma_dac2.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + 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) { + 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 (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = val & 0xffff; + s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac2.ossfragshift < 4) + s->dma_dac2.ossfragshift = 4; + if (s->dma_dac2.ossfragshift > 15) + s->dma_dac2.ossfragshift = 15; + if (s->dma_dac2.ossmaxfrags < 4) + s->dma_dac2.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.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; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? + 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + 16 : 8, (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 es1370_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + 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); + if (!(s->open_mode & (FMODE_READ|FMODE_WRITE))) + s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + s->sctrl &= ~SCTRL_R1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_R1FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_R1FMT; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; + s->dma_dac2.enabled = 1; + s->sctrl &= ~SCTRL_P2FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P2FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P2FMT; + } + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + init_MUTEX(&s->sem); + return 0; +} + +static int es1370_release(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac2(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(); + dealloc_dmabuf(s, &s->dma_dac2); + } + 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); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1370_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: es1370_read, + write: es1370_write, + poll: es1370_poll, + ioctl: es1370_ioctl, + mmap: es1370_mmap, + open: es1370_open, + release: es1370_release, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac1.mapped) + return -ENXIO; + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + add_wait_queue(&s->dma_dac1.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac1.count < 0) { + s->dma_dac1.count = 0; + s->dma_dac1.swptr = s->dma_dac1.hwptr; + } + swptr = s->dma_dac1.swptr; + cnt = s->dma_dac1.dmasize-swptr; + if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) + cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac1.enabled) + start_dac1(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac1.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac1.swptr = swptr; + s->dma_dac1.count += cnt; + s->dma_dac1.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac1.enabled) + start_dac1(s); + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1370_poll_dac(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (!s->dma_dac1.ready && prog_dmabuf_dac1(s)) + return 0; + poll_wait(file, &s->dma_dac1.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_mmap_dac(struct file *file, struct vm_area_struct *vma) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (!(vma->vm_flags & VM_WRITE)) + return -EINVAL; + lock_kernel(); + if ((ret = prog_dmabuf_dac1(s)) != 0) + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << s->dma_dac1.buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma, vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot)) + goto out; + s->dma_dac1.mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + unsigned ctrl; + int val, ret; + + VALIDATE_STATE(s); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + stop_dac1(s); + synchronize_irq(); + s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + for (ctrl = 0; ctrl <= 2; ctrl++) + if (val < (dac1_samplerate[ctrl] + dac1_samplerate[ctrl+1]) / 2) + break; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (ctrl << CTRL_SH_WTSRSEL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (s->dma_dac1.mapped) + return -EINVAL; + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (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) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P1SEB; + else + s->sctrl &= ~SCTRL_P1SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + s->dma_dac1.enabled = 1; + start_dac1(s); + } else { + s->dma_dac1.enabled = 0; + stop_dac1(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_dac1.fragsize; + count = s->dma_dac1.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac1.dmasize - count; + abinfo.fragstotal = s->dma_dac1.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac1.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 (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETOPTR: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_dac1.total_bytes; + count = s->dma_dac1.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac1.fragshift; + cinfo.ptr = s->dma_dac1.hwptr; + if (s->dma_dac1.mapped) + s->dma_dac1.count &= s->dma_dac1.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if ((val = prog_dmabuf_dac1(s))) + return val; + return put_user(s->dma_dac1.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + s->dma_dac1.ossfragshift = val & 0xffff; + s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac1.ossfragshift < 4) + s->dma_dac1.ossfragshift = 4; + if (s->dma_dac1.ossfragshift > 15) + s->dma_dac1.ossfragshift = 15; + if (s->dma_dac1.ossmaxfrags < 4) + s->dma_dac1.ossmaxfrags = 4; + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (s->dma_dac1.subdivision) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + s->dma_dac1.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, (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 es1370_open_dac(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (!((s->dev_dac ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + /* we allow opening with O_RDWR, most programs do it although they will only write */ +#if 0 + if (file->f_mode & FMODE_READ) + return -EPERM; +#endif + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DAC) { + 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); + } + s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; + s->dma_dac1.enabled = 1; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (1 << CTRL_SH_WTSRSEL); + s->sctrl &= ~SCTRL_P1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P1FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P1FMT; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= FMODE_DAC; + up(&s->open_sem); + return 0; +} + +static int es1370_release_dac(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + drain_dac1(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + stop_dac1(s); + dealloc_dmabuf(s, &s->dma_dac1); + s->open_mode &= ~FMODE_DAC; + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1370_dac_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + write: es1370_write_dac, + poll: es1370_poll_dac, + ioctl: es1370_ioctl_dac, + mmap: es1370_mmap_dac, + open: es1370_open_dac, + release: es1370_release_dac, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + es1370_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1370_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_midi_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (s->dev_midi == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + 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); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(UCTRL_CNTRL_SWR, s->io+ES1370_REG_UART_CONTROL); + outb(0, s->io+ES1370_REG_UART_CONTROL); + outb(0, s->io+ES1370_REG_UART_TEST); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + s->ctrl |= CTRL_UART_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + return 0; +} + +static int es1370_midi_release(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + 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; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");) + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->ctrl &= ~CTRL_UART_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + } + spin_unlock_irqrestore(&s->lock, flags); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1370_midi_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: es1370_midi_read, + write: es1370_midi_write, + poll: es1370_midi_poll, + open: es1370_midi_open, + release: es1370_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int joystick[NR_DEVICE] = { 0, }; +static int lineout[NR_DEVICE] = { 0, }; +static int micbias[NR_DEVICE] = { 0, }; + +static unsigned int devindex = 0; + +MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)"); +MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); +MODULE_PARM(micbias, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1370 AudioPCI Driver"); +MODULE_LICENSE("GPL"); + + +/* --------------------------------------------------------------------- */ + +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_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_LINE3, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_OGAIN, 0x4040 } +}; + +static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + struct es1370_state *s; + mm_segment_t fs; + int i, val, ret; + + if ((ret=pci_enable_device(pcidev))) + return ret; + + if ( !(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) || + !pci_resource_start(pcidev, 0) + ) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + i = pci_set_dma_mask(pcidev, 0xffffffff); + if (i) { + printk(KERN_WARNING "es1370: architecture does not support 32bit PCI busmaster DMA\n"); + return i; + } + if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) { + printk(KERN_WARNING "es1370: out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct es1370_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac1.wait); + init_waitqueue_head(&s->dma_dac2.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = ES1370_MAGIC; + s->dev = pcidev; + s->io = pci_resource_start(pcidev, 0); + s->irq = pcidev->irq; + if (!request_region(s->io, ES1370_EXTENT, "es1370")) { + printk(KERN_ERR "es1370: io ports %#lx-%#lx in use\n", s->io, s->io+ES1370_EXTENT-1); + ret = -EBUSY; + goto err_region; + } + if ((ret=request_irq(s->irq, es1370_interrupt, SA_SHIRQ, "es1370",s))) { + printk(KERN_ERR "es1370: irq %u in use\n", s->irq); + goto err_irq; + } + + /* initialize codec registers */ + /* note: setting CTRL_SERR_DIS is reported to break + * mic bias setting (by Kim.Berts@fisub.mail.abb.com) */ + s->ctrl = CTRL_CDC_EN | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL); + s->gameport.io = 0; + if (joystick[devindex]) { + if (!request_region(0x200, JOY_EXTENT, "es1370")) + printk(KERN_ERR "es1370: joystick io port 0x200 in use\n"); + else { + s->ctrl |= CTRL_JYSTK_EN; + s->gameport.io = 0x200; + } + } + if (lineout[devindex]) + s->ctrl |= CTRL_XCTL0; + if (micbias[devindex]) + s->ctrl |= CTRL_XCTL1; + s->sctrl = 0; + printk(KERN_INFO "es1370: found adapter at io %#lx irq %u\n" + KERN_INFO "es1370: features: joystick %s, line %s, mic impedance %s\n", + s->io, s->irq, (s->ctrl & CTRL_JYSTK_EN) ? "on" : "off", + (s->ctrl & CTRL_XCTL0) ? "out" : "in", + (s->ctrl & CTRL_XCTL1) ? "1" : "0"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1)) < 0) { + ret = s->dev_audio; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1)) < 0) { + ret = s->dev_dac; + goto err_dev3; + } + if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1)) < 0) { + ret = s->dev_midi; + goto err_dev4; + } + /* initialize the chips */ + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + /* point phantom write channel to "bugbuf" */ + outl((ES1370_REG_PHANTOM_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + outl(virt_to_bus(bugbuf), s->io+(ES1370_REG_PHANTOM_FRAMEADR & 0xff)); + outl(0, s->io+(ES1370_REG_PHANTOM_FRAMECNT & 0xff)); + pci_set_master(pcidev); /* enable bus mastering */ + wrcodec(s, 0x16, 3); /* no RST, PD */ + wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ + wrcodec(s, 0x18, 0); /* recording source is mixer */ + wrcodec(s, 0x19, s->mix.micpreamp = 1); /* turn on MIC preamp */ + s->mix.imix = 1; + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD; + 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); + } + set_fs(fs); + /* register gameport */ + gameport_register_port(&s->gameport); + + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "es1370: cannot register misc device\n"); + free_irq(s->irq, s); + if (s->gameport.io) + release_region(s->gameport.io, JOY_EXTENT); + err_irq: + release_region(s->io, ES1370_EXTENT); + err_region: + kfree(s); + return ret; +} + +static void __devinit es1370_remove(struct pci_dev *dev) +{ + struct es1370_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); + outl(CTRL_SERR_DIS | (1 << CTRL_SH_WTSRSEL), s->io+ES1370_REG_CONTROL); /* switch everything off */ + outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */ + synchronize_irq(); + free_irq(s->irq, s); + if (s->gameport.io) { + gameport_unregister_port(&s->gameport); + release_region(s->gameport.io, JOY_EXTENT); + } + release_region(s->io, ES1370_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_dsp(s->dev_dac); + unregister_sound_midi(s->dev_midi); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver es1370_driver = { + name: "es1370", + id_table: id_table, + probe: es1370_probe, + remove: es1370_remove +}; + +static int __init init_es1370(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "es1370: version v0.37 time " __TIME__ " " __DATE__ "\n"); + return pci_module_init(&es1370_driver); +} + +static void __exit cleanup_es1370(void) +{ + printk(KERN_INFO "es1370: unloading\n"); + pci_unregister_driver(&es1370_driver); +} + +module_init(init_es1370); +module_exit(cleanup_es1370); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +/* format is: es1370=[joystick[,lineout[,micbias]]] */ + +static int __init es1370_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; + + (void) + ( (get_option(&str,&joystick[nr_dev]) == 2) + && (get_option(&str,&lineout [nr_dev]) == 2) + && get_option(&str,&micbias [nr_dev]) + ); + + nr_dev++; + return 1; +} + +__setup("es1370=", es1370_setup); + +#endif /* MODULE */ diff -Nru a/sound/oss/es1371.c b/sound/oss/es1371.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/es1371.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,3084 @@ +/*****************************************************************************/ + +/* + * es1371.c -- Creative Ensoniq ES1371. + * + * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * 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. + * + * Special thanks to Ensoniq + * + * + * Module command line parameters: + * joystick must be set to the base I/O-Port to be used for + * the gameport. Legal values are 0x200, 0x208, 0x210 and 0x218. + * The gameport is mirrored eight times. + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/dsp1 additional DAC, like /dev/dsp, but outputs to mixer "SYNTH" setting + * /dev/midi simple MIDI UART interface, no ioctl + * + * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed + * to be done in software. That is what /dev/dac is for. By now (Q2 1998) + * there are several MIDI to PCM (WAV) packages, one of them is timidity. + * + * Revision history + * 04.06.1998 0.1 Initial release + * Mixer stuff should be overhauled; especially optional AC97 mixer bits + * should be detected. This results in strange behaviour of some mixer + * settings, like master volume and mic. + * 08.06.1998 0.2 First release using Alan Cox' soundcore instead of miscdevice + * 03.08.1998 0.3 Do not include modversions.h + * Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * 31.08.1998 0.4 Fix realplayer problems - dac.count issues + * 27.10.1998 0.5 Fix joystick support + * -- Oliver Neukum (c188@org.chemie.uni-muenchen.de) + * 10.12.1998 0.6 Fix drain_dac trying to wait on not yet initialized DMA + * 23.12.1998 0.7 Fix a few f_file & FMODE_ bugs + * Don't wake up app until there are fragsize bytes to read/write + * 06.01.1999 0.8 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.9 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.10 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.11 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Another Alpha fix (wait_src_ready in init routine) + * reported by "Ivan N. Kokshaysky" + * Note: joystick address handling might still be wrong on archs + * other than i386 + * 15.06.1999 0.12 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.13 Add pci_set_master + * 03.08.1999 0.14 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1371=joystickaddr" + * removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge + * 10.08.1999 0.15 (Re)added S/PDIF module option for cards revision >= 4. + * Initial version by Dave Platt . + * module_init/__setup fixes + * 08.16.1999 0.16 Joe Cotellese + * Added detection for ES1371 revision ID so that we can + * detect the ES1373 and later parts. + * added AC97 #defines for readability + * added a /proc file system for dumping hardware state + * updated SRC and CODEC w/r functions to accomodate bugs + * in some versions of the ES137x chips. + * 31.08.1999 0.17 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.18 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 21.10.1999 0.19 Round sampling rates, requested by + * Kasamatsu Kenichi + * 27.10.1999 0.20 Added SigmaTel 3D enhancement string + * Codec ID printing changes + * 28.10.1999 0.21 More waitqueue races fixed + * Joe Cotellese + * Changed PCI detection routine so we can more easily + * detect ES137x chip and derivatives. + * 05.01.2000 0.22 Should now work with rev7 boards; patch by + * Eric Lemar, elemar@cs.washington.edu + * 08.01.2000 0.23 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * 07.02.2000 0.24 Use pci_alloc_consistent and pci_register_driver + * 07.02.2000 0.25 Use ac97_codec + * 01.03.2000 0.26 SPDIF patch by Mikael Bouillot + * Use pci_module_init + * 21.11.2000 0.27 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.28 More dma buffer initializations, patch from + * Tjeerd Mulder + * 05.01.2001 0.29 Hopefully updates will not be required anymore when Creative bumps + * the CT5880 revision. + * suggested by Stephan Müller + * 31.01.2001 0.30 Register/Unregister gameport + * Fix SETTRIGGER non OSS API conformity + * 14.07.2001 0.31 Add list of laptops needing amplifier control + */ + +/*****************************************************************************/ + +#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 ES1371_DEBUG +#define DBG(x) {} +/*#define DBG(x) {x}*/ + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ENSONIQ +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#endif + +#ifndef PCI_VENDOR_ID_ECTIVA +#define PCI_VENDOR_ID_ECTIVA 0x1102 +#endif + +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 +#endif + +#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880 +#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 +#endif + +#ifndef PCI_DEVICE_ID_ECTIVA_EV1938 +#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938 +#endif + +/* ES1371 chip ID */ +/* This is a little confusing because all ES1371 compatible chips have the + same DEVICE_ID, the only thing differentiating them is the REV_ID field. + This is only significant if you want to enable features on the later parts. + Yes, I know it's stupid and why didn't we use the sub IDs? +*/ +#define ES1371REV_ES1373_A 0x04 +#define ES1371REV_ES1373_B 0x06 +#define ES1371REV_CT5880_A 0x07 +#define CT5880REV_CT5880_C 0x02 +#define CT5880REV_CT5880_D 0x03 +#define ES1371REV_ES1371_B 0x09 +#define EV1938REV_EV1938_A 0x00 +#define ES1371REV_ES1373_8 0x08 + +#define ES1371_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371) + +#define ES1371_EXTENT 0x40 +#define JOY_EXTENT 8 + +#define ES1371_REG_CONTROL 0x00 +#define ES1371_REG_STATUS 0x04 /* on the 5880 it is control/status */ +#define ES1371_REG_UART_DATA 0x08 +#define ES1371_REG_UART_STATUS 0x09 +#define ES1371_REG_UART_CONTROL 0x09 +#define ES1371_REG_UART_TEST 0x0a +#define ES1371_REG_MEMPAGE 0x0c +#define ES1371_REG_SRCONV 0x10 +#define ES1371_REG_CODEC 0x14 +#define ES1371_REG_LEGACY 0x18 +#define ES1371_REG_SERIAL_CONTROL 0x20 +#define ES1371_REG_DAC1_SCOUNT 0x24 +#define ES1371_REG_DAC2_SCOUNT 0x28 +#define ES1371_REG_ADC_SCOUNT 0x2c + +#define ES1371_REG_DAC1_FRAMEADR 0xc30 +#define ES1371_REG_DAC1_FRAMECNT 0xc34 +#define ES1371_REG_DAC2_FRAMEADR 0xc38 +#define ES1371_REG_DAC2_FRAMECNT 0xc3c +#define ES1371_REG_ADC_FRAMEADR 0xd30 +#define ES1371_REG_ADC_FRAMECNT 0xd34 + +#define ES1371_FMT_U8_MONO 0 +#define ES1371_FMT_U8_STEREO 1 +#define ES1371_FMT_S16_MONO 2 +#define ES1371_FMT_S16_STEREO 3 +#define ES1371_FMT_STEREO 1 +#define ES1371_FMT_S16 2 +#define ES1371_FMT_MASK 3 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define CTRL_RECEN_B 0x08000000 /* 1 = don't mix analog in to digital out */ +#define CTRL_SPDIFEN_B 0x04000000 +#define CTRL_JOY_SHIFT 24 +#define CTRL_JOY_MASK 3 +#define CTRL_JOY_200 0x00000000 /* joystick base address */ +#define CTRL_JOY_208 0x01000000 +#define CTRL_JOY_210 0x02000000 +#define CTRL_JOY_218 0x03000000 +#define CTRL_GPIO_IN0 0x00100000 /* general purpose inputs/outputs */ +#define CTRL_GPIO_IN1 0x00200000 +#define CTRL_GPIO_IN2 0x00400000 +#define CTRL_GPIO_IN3 0x00800000 +#define CTRL_GPIO_OUT0 0x00010000 +#define CTRL_GPIO_OUT1 0x00020000 +#define CTRL_GPIO_OUT2 0x00040000 +#define CTRL_GPIO_OUT3 0x00080000 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ +#define CTRL_SYNCRES 0x00004000 /* AC97 warm reset */ +#define CTRL_ADCSTOP 0x00002000 /* stop ADC transfers */ +#define CTRL_PWR_INTRM 0x00001000 /* 1 = power level ints enabled */ +#define CTRL_M_CB 0x00000800 /* recording source: 0 = ADC, 1 = MPEG */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_PDLEV0 0x00000000 /* power down level */ +#define CTRL_PDLEV1 0x00000100 +#define CTRL_PDLEV2 0x00000200 +#define CTRL_PDLEV3 0x00000300 +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port */ +#define CTRL_XTALCLKDIS 0x00000002 /* 1 = disable crystal clock input */ +#define CTRL_PCICLKDIS 0x00000001 /* 1 = disable PCI clock distribution */ + + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define CSTAT_5880_AC97_RST 0x20000000 /* CT5880 Reset bit */ +#define STAT_EN_SPDIF 0x00040000 /* enable S/PDIF circuitry */ +#define STAT_TS_SPDIF 0x00020000 /* test S/PDIF circuitry */ +#define STAT_TESTMODE 0x00010000 /* test ASIC */ +#define STAT_SYNC_ERR 0x00000100 /* 1 = codec sync error */ +#define STAT_VC 0x000000c0 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ +#define STAT_SH_VC 6 +#define STAT_MPWR 0x00000020 /* power level interrupt */ +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define USTAT_RXINT 0x80 /* UART rx int pending */ +#define USTAT_TXINT 0x04 /* UART tx int pending */ +#define USTAT_TXRDY 0x02 /* UART tx ready */ +#define USTAT_RXRDY 0x01 /* UART rx ready */ + +#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ +#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ +#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ +#define UCTRL_CNTRL 0x03 /* control field */ +#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ + +/* sample rate converter */ +#define SRC_OKSTATE 1 + +#define SRC_RAMADDR_MASK 0xfe000000 +#define SRC_RAMADDR_SHIFT 25 +#define SRC_DAC1FREEZE (1UL << 21) +#define SRC_DAC2FREEZE (1UL << 20) +#define SRC_ADCFREEZE (1UL << 19) + + +#define SRC_WE 0x01000000 /* read/write control for SRC RAM */ +#define SRC_BUSY 0x00800000 /* SRC busy */ +#define SRC_DIS 0x00400000 /* 1 = disable SRC */ +#define SRC_DDAC1 0x00200000 /* 1 = disable accum update for DAC1 */ +#define SRC_DDAC2 0x00100000 /* 1 = disable accum update for DAC2 */ +#define SRC_DADC 0x00080000 /* 1 = disable accum update for ADC2 */ +#define SRC_CTLMASK 0x00780000 +#define SRC_RAMDATA_MASK 0x0000ffff +#define SRC_RAMDATA_SHIFT 0 + +#define SRCREG_ADC 0x78 +#define SRCREG_DAC1 0x70 +#define SRCREG_DAC2 0x74 +#define SRCREG_VOL_ADC 0x6c +#define SRCREG_VOL_DAC1 0x7c +#define SRCREG_VOL_DAC2 0x7e + +#define SRCREG_TRUNC_N 0x00 +#define SRCREG_INT_REGS 0x01 +#define SRCREG_ACCUM_FRAC 0x02 +#define SRCREG_VFREQ_FRAC 0x03 + +#define CODEC_PIRD 0x00800000 /* 0 = write AC97 register */ +#define CODEC_PIADD_MASK 0x007f0000 +#define CODEC_PIADD_SHIFT 16 +#define CODEC_PIDAT_MASK 0x0000ffff +#define CODEC_PIDAT_SHIFT 0 + +#define CODEC_RDY 0x80000000 /* AC97 read data valid */ +#define CODEC_WIP 0x40000000 /* AC97 write in progress */ +#define CODEC_PORD 0x00800000 /* 0 = write AC97 register */ +#define CODEC_POADD_MASK 0x007f0000 +#define CODEC_POADD_SHIFT 16 +#define CODEC_PODAT_MASK 0x0000ffff +#define CODEC_PODAT_SHIFT 0 + + +#define LEGACY_JFAST 0x80000000 /* fast joystick timing */ +#define LEGACY_FIRQ 0x01000000 /* force IRQ */ + +#define SCTRL_DACTEST 0x00400000 /* 1 = DAC test, test vector generation purposes */ +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + + +/* misc stuff */ +#define POLL_COUNT 0x1000 +#define FMODE_DAC 4 /* slight misuse of mode_t */ + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define ES1371_MODULE_NAME "es1371" +#define PFX ES1371_MODULE_NAME ": " + +/* --------------------------------------------------------------------- */ + +struct es1371_state { + /* magic */ + unsigned int magic; + + /* list of es1371 devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_dac; + int dev_midi; + + /* hardware resources */ + unsigned long io; /* long for SPARC */ + 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 */ + +#ifdef ES1371_DEBUG + /* debug /proc entry */ + struct proc_dir_entry *ps; +#endif /* ES1371_DEBUG */ + + struct ac97_codec codec; + + /* wave stuff */ + unsigned ctrl; + unsigned sctrl; + unsigned dac1rate, dac2rate, 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; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + 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 endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac1, dma_dac2, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct gameport gameport; + struct semaphore sem; +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); + +/* --------------------------------------------------------------------- */ + +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; +} + +/* --------------------------------------------------------------------- */ + +static unsigned wait_src_ready(struct es1371_state *s) +{ + unsigned int t, r; + + for (t = 0; t < POLL_COUNT; t++) { + if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY)) + return r; + udelay(1); + } + printk(KERN_DEBUG PFX "sample rate converter timeout r = 0x%08x\n", r); + return r; +} + +static unsigned src_read(struct es1371_state *s, unsigned reg) +{ + unsigned int temp,i,orig; + + /* wait for ready */ + temp = wait_src_ready (s); + + /* we can only access the SRC at certain times, make sure + we're allowed to before we read */ + + orig = temp; + /* expose the SRC state bits */ + outl ( (temp & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT) | 0x10000UL, + s->io + ES1371_REG_SRCONV); + + /* now, wait for busy and the correct time to read */ + temp = wait_src_ready (s); + + if ( (temp & 0x00870000UL ) != ( SRC_OKSTATE << 16 )){ + /* wait for the right state */ + for (i=0; iio + ES1371_REG_SRCONV); + if ( (temp & 0x00870000UL ) == ( SRC_OKSTATE << 16 )) + break; + } + } + + /* hide the state bits */ + outl ((orig & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT), s->io + ES1371_REG_SRCONV); + return temp; + + +} + +static void src_write(struct es1371_state *s, unsigned reg, unsigned data) +{ + + unsigned int r; + + r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); + r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; + r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK; + outl(r | SRC_WE, s->io + ES1371_REG_SRCONV); + +} + +/* --------------------------------------------------------------------- */ + +/* most of the following here is black magic */ +static void set_adc_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int n, truncm, freq; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + n = rate / 3000; + if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) + n--; + truncm = (21 * n - 1) | 1; + freq = ((48000UL << 15) / rate) * n; + s->adcrate = (48000UL << 15) / (freq / n); + spin_lock_irqsave(&s->lock, flags); + if (rate >= 24000) { + if (truncm > 239) + truncm = 239; + src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, + (((239 - truncm) >> 1) << 9) | (n << 4)); + } else { + if (truncm > 119) + truncm = 119; + src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, + 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); + } + src_write(s, SRCREG_ADC+SRCREG_INT_REGS, + (src_read(s, SRCREG_ADC+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_ADC+SRCREG_VFREQ_FRAC, freq & 0x7fff); + src_write(s, SRCREG_VOL_ADC, n << 8); + src_write(s, SRCREG_VOL_ADC+1, n << 8); + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void set_dac1_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int freq, r; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + freq = ((rate << 15) + 1500) / 3000; + s->dac1rate = (freq * 3000 + 16384) >> 15; + spin_lock_irqsave(&s->lock, flags); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1; + outl(r, s->io + ES1371_REG_SRCONV); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, + (src_read(s, SRCREG_DAC1+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_DAC1+SRCREG_VFREQ_FRAC, freq & 0x7fff); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)); + outl(r, s->io + ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void set_dac2_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int freq, r; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + freq = ((rate << 15) + 1500) / 3000; + s->dac2rate = (freq * 3000 + 16384) >> 15; + spin_lock_irqsave(&s->lock, flags); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2; + outl(r, s->io + ES1371_REG_SRCONV); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, + (src_read(s, SRCREG_DAC2+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_DAC2+SRCREG_VFREQ_FRAC, freq & 0x7fff); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)); + outl(r, s->io + ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static void __init src_init(struct es1371_state *s) +{ + unsigned int i; + + /* before we enable or disable the SRC we need + to wait for it to become ready */ + wait_src_ready(s); + + outl(SRC_DIS, s->io + ES1371_REG_SRCONV); + + for (i = 0; i < 0x80; i++) + src_write(s, i, 0); + + src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_VOL_ADC, 1 << 12); + src_write(s, SRCREG_VOL_ADC+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC2, 1 << 12); + src_write(s, SRCREG_VOL_DAC2+1, 1 << 12); + set_adc_rate(s, 22050); + set_dac1_rate(s, 22050); + set_dac2_rate(s, 22050); + + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) + */ + wait_src_ready(s); + outl(0, s->io+ES1371_REG_SRCONV); +} + +/* --------------------------------------------------------------------- */ + +static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) +{ + struct es1371_state *s = (struct es1371_state *)codec->private_data; + unsigned long flags; + unsigned t, x; + + 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); + + /* enable SRC state data in SRC mux */ + outl((x & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, + s->io+ES1371_REG_SRCONV); + + /* wait for not busy (state 0) first to avoid + transition states */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0 ) + break; + udelay(1); + } + + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000) + break; + udelay(1); + } + + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | + ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC); + + /* restore SRC reg */ + wait_src_ready(s); + outl(x, s->io+ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +static u16 rdcodec(struct ac97_codec *codec, u8 addr) +{ + struct es1371_state *s = (struct es1371_state *)codec->private_data; + unsigned long flags; + unsigned t, x; + + /* 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)); + + /* enable SRC state data in SRC mux */ + outl( x | 0x00010000, + s->io+ES1371_REG_SRCONV); + + /* wait for not busy (state 0) first to avoid + transition states */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0 ) + break; + udelay(1); + } + + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000) + break; + udelay(1); + } + + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC); + /* 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++) + if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) + break; + + /* now wait for the stinkin' data (RDY) */ + for (t = 0; t < POLL_COUNT; t++) + if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY) + break; + + return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_adc(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac1(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac2(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) + && s->dma_dac1.ready) { + s->ctrl |= CTRL_DAC1_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac1.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac2(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) + && s->dma_dac2.ready) { + s->ctrl |= CTRL_DAC2_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | + SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | + (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | + (0 << SCTRL_SH_P2STINC); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac2.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->ctrl |= CTRL_ADC_EN; + s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_adc.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + + +static inline void dealloc_dmabuf(struct es1371_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); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct es1371_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + 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); + } + fmt &= ES1371_FMT_MASK; + 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--; + 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 & ES1371_FMT_S16) ? 0 : 0x80, db->dmasize); + outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE); + outl(db->dmaaddr, s->io+(reg & 0xff)); + outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); + db->enabled = 1; + db->ready = 1; + return 0; +} + +static inline int prog_dmabuf_adc(struct es1371_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, s->adcrate, (s->sctrl >> SCTRL_SH_R1FMT) & ES1371_FMT_MASK, + ES1371_REG_ADC_FRAMEADR); +} + +static inline int prog_dmabuf_dac2(struct es1371_state *s) +{ + stop_dac2(s); + return prog_dmabuf(s, &s->dma_dac2, s->dac2rate, (s->sctrl >> SCTRL_SH_P2FMT) & ES1371_FMT_MASK, + ES1371_REG_DAC2_FRAMEADR); +} + +static inline int prog_dmabuf_dac1(struct es1371_state *s) +{ + stop_dac1(s); + return prog_dmabuf(s, &s->dma_dac1, s->dac1rate, (s->sctrl >> SCTRL_SH_P1FMT) & ES1371_FMT_MASK, + ES1371_REG_DAC1_FRAMEADR); +} + +static inline unsigned get_hwptr(struct es1371_state *s, struct dmabuf *db, unsigned reg) +{ + unsigned hwptr, diff; + + outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE); + hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; + diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; + db->hwptr = hwptr; + return diff; +} + +static inline 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; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ +static void es1371_update_ptr(struct es1371_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->ctrl & CTRL_ADC_EN) { + diff = get_hwptr(s, &s->dma_adc, ES1371_REG_ADC_FRAMECNT); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_adc.error++; + } + } + } + /* update DAC1 pointer */ + if (s->ctrl & CTRL_DAC1_EN) { + diff = get_hwptr(s, &s->dma_dac1, ES1371_REG_DAC1_FRAMECNT); + s->dma_dac1.total_bytes += diff; + if (s->dma_dac1.mapped) { + s->dma_dac1.count += diff; + if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) + wake_up(&s->dma_dac1.wait); + } else { + s->dma_dac1.count -= diff; + if (s->dma_dac1.count <= 0) { + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_dac1.error++; + } else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { + clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, + s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); + s->dma_dac1.endcleared = 1; + } + if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize) + wake_up(&s->dma_dac1.wait); + } + } + /* update DAC2 pointer */ + if (s->ctrl & CTRL_DAC2_EN) { + diff = get_hwptr(s, &s->dma_dac2, ES1371_REG_DAC2_FRAMECNT); + s->dma_dac2.total_bytes += diff; + if (s->dma_dac2.mapped) { + s->dma_dac2.count += diff; + if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) + wake_up(&s->dma_dac2.wait); + } else { + s->dma_dac2.count -= diff; + if (s->dma_dac2.count <= 0) { + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_dac2.error++; + } else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { + clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, + s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); + s->dma_dac2.endcleared = 1; + } + if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize) + wake_up(&s->dma_dac2.wait); + } + } +} + +/* hold spinlock for the following! */ +static void es1371_handle_midi(struct es1371_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->ctrl & CTRL_UART_EN)) + return; + wake = 0; + while (inb(s->io+ES1371_REG_UART_STATUS) & USTAT_RXRDY) { + ch = inb(s->io+ES1371_REG_UART_DATA); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while ((inb(s->io+ES1371_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->io+ES1371_REG_UART_DATA); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); + outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1371_REG_UART_CONTROL); +} + +static void es1371_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct es1371_state *s = (struct es1371_state *)dev_id; + unsigned int intsrc, sctl; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->io+ES1371_REG_STATUS); + if (!(intsrc & 0x80000000)) + return; + spin_lock(&s->lock); + /* clear audio interrupts first */ + sctl = s->sctrl; + if (intsrc & STAT_ADC) + sctl &= ~SCTRL_R1INTEN; + if (intsrc & STAT_DAC1) + sctl &= ~SCTRL_P1INTEN; + if (intsrc & STAT_DAC2) + sctl &= ~SCTRL_P2INTEN; + outl(sctl, s->io+ES1371_REG_SERIAL_CONTROL); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + es1371_update_ptr(s); + es1371_handle_midi(s); + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != ES1371_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +/* Conversion table for S/PDIF PCM volume emulation through the SRC */ +/* dB-linear table of DAC vol values; -0dB to -46.5dB with mute */ +static const unsigned short DACVolTable[101] = +{ + 0x1000, 0x0f2a, 0x0e60, 0x0da0, 0x0cea, 0x0c3e, 0x0b9a, 0x0aff, + 0x0a6d, 0x09e1, 0x095e, 0x08e1, 0x086a, 0x07fa, 0x078f, 0x072a, + 0x06cb, 0x0670, 0x061a, 0x05c9, 0x057b, 0x0532, 0x04ed, 0x04ab, + 0x046d, 0x0432, 0x03fa, 0x03c5, 0x0392, 0x0363, 0x0335, 0x030b, + 0x02e2, 0x02bc, 0x0297, 0x0275, 0x0254, 0x0235, 0x0217, 0x01fb, + 0x01e1, 0x01c8, 0x01b0, 0x0199, 0x0184, 0x0170, 0x015d, 0x014b, + 0x0139, 0x0129, 0x0119, 0x010b, 0x00fd, 0x00f0, 0x00e3, 0x00d7, + 0x00cc, 0x00c1, 0x00b7, 0x00ae, 0x00a5, 0x009c, 0x0094, 0x008c, + 0x0085, 0x007e, 0x0077, 0x0071, 0x006b, 0x0066, 0x0060, 0x005b, + 0x0057, 0x0052, 0x004e, 0x004a, 0x0046, 0x0042, 0x003f, 0x003c, + 0x0038, 0x0036, 0x0033, 0x0030, 0x002e, 0x002b, 0x0029, 0x0027, + 0x0025, 0x0023, 0x0021, 0x001f, 0x001e, 0x001c, 0x001b, 0x0019, + 0x0018, 0x0017, 0x0016, 0x0014, 0x0000 +}; + +/* + * when we are in S/PDIF mode, we want to disable any analog output so + * we filter the mixer ioctls + */ +static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)codec->private_data; + int val; + unsigned long flags; + unsigned int left, right; + + VALIDATE_STATE(s); + /* filter mixer ioctls to catch PCM and MASTER volume when in S/PDIF mode */ + if (s->spdif_volume == -1) + return codec->mixer_ioctl(codec, cmd, arg); + switch (cmd) { + case SOUND_MIXER_WRITE_VOLUME: + return 0; + + case SOUND_MIXER_WRITE_PCM: /* use SRC for PCM volume */ + 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; + spin_lock_irqsave(&s->lock, flags); + src_write(s, SRCREG_VOL_DAC2, DACVolTable[100 - left]); + src_write(s, SRCREG_VOL_DAC2+1, DACVolTable[100 - right]); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SOUND_MIXER_READ_PCM: + return put_user(s->spdif_volume, (int *)arg); + } + return codec->mixer_ioctl(codec, cmd, arg); +} + +/* --------------------------------------------------------------------- */ + +/* + * AC97 Mixer Register to Connections mapping of the Concert 97 board + * + * AC97_MASTER_VOL_STEREO Line Out + * AC97_MASTER_VOL_MONO TAD Output + * AC97_PCBEEP_VOL none + * AC97_PHONE_VOL TAD Input (mono) + * AC97_MIC_VOL MIC Input (mono) + * AC97_LINEIN_VOL Line Input (stereo) + * AC97_CD_VOL CD Input (stereo) + * AC97_VIDEO_VOL none + * AC97_AUX_VOL Aux Input (stereo) + * AC97_PCMOUT_VOL Wave Output (stereo) + */ + +static int es1371_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct list_head *list; + struct es1371_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1371_state, devs); + if (s->codec.dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + return 0; +} + +static int es1371_release_mixdev(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +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; + + return mixdev_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations es1371_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: es1371_ioctl_mixdev, + open: es1371_open_mixdev, + release: es1371_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac1(struct es1371_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac1.mapped || !s->dma_dac1.ready) + return 0; + add_wait_queue(&s->dma_dac1.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate; + tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG PFX "dac1 dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac2(struct es1371_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac2.mapped || !s->dma_dac2.ready) + return 0; + add_wait_queue(&s->dma_dac2.wait, &wait); + for (;;) { + __set_current_state(TASK_UNINTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate; + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG PFX "dac2 dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + down(&s->sem); + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + goto out2; + + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); + 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); + if (s->dma_adc.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); + } +out: + up(&s->sem); +out2: + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac2.mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + down(&s->sem); + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + goto out3; + ret = 0; + add_wait_queue(&s->dma_dac2.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac2.count < 0) { + s->dma_dac2.count = 0; + s->dma_dac2.swptr = s->dma_dac2.hwptr; + } + swptr = s->dma_dac2.swptr; + cnt = s->dma_dac2.dmasize-swptr; + if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) + cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac2.enabled) + start_dac2(s); + 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); + if (s->dma_dac2.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_dac2.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac2.swptr = swptr; + s->dma_dac2.count += cnt; + s->dma_dac2.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac2.enabled) + start_dac2(s); + } +out: + up(&s->sem); +out2: + remove_wait_queue(&s->dma_dac2.wait, &wait); +out3: + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1371_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac2.ready && prog_dmabuf_dac2(s)) + return 0; + poll_wait(file, &s->dma_dac2.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + 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_dac2.mapped) { + if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1371_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + struct dmabuf *db; + int ret = 0; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + down(&s->sem); + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac2(s)) != 0) { + goto out; + } + db = &s->dma_dac2; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) { + goto out; + } + 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, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) { + ret = -EAGAIN; + goto out; + } + db->mapped = 1; +out: + up(&s->sem); + unlock_kernel(); + return ret; +} + +static int es1371_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac2(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: + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(); + s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + 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); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + set_dac2_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (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.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + 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); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (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); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_R1SEB; + else + s->sctrl &= ~SCTRL_R1SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P2SEB; + else + s->sctrl &= ~SCTRL_P2SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + 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; + s->dma_adc.enabled = 1; + start_adc(s); + } else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + s->dma_dac2.enabled = 1; + start_dac2(s); + } else { + s->dma_dac2.enabled = 0; + stop_dac2(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_dac2.fragsize; + count = s->dma_dac2.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac2.dmasize - count; + abinfo.fragstotal = s->dma_dac2.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; + 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)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + count = s->dma_adc.count; + if (count < 0) + count = 0; + abinfo.bytes = 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_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + count = s->dma_dac2.count; + 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; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + 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_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_dac2.total_bytes; + count = s->dma_dac2.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac2.fragshift; + cinfo.ptr = s->dma_dac2.hwptr; + if (s->dma_dac2.mapped) + s->dma_dac2.count &= s->dma_dac2.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_dac2(s))) + return val; + return put_user(s->dma_dac2.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + 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) { + 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 (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = val & 0xffff; + s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac2.ossfragshift < 4) + s->dma_dac2.ossfragshift = 4; + if (s->dma_dac2.ossfragshift > 15) + s->dma_dac2.ossfragshift = 15; + if (s->dma_dac2.ossmaxfrags < 4) + s->dma_dac2.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.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; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? 16 : 8, (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 es1371_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 es1371_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1371_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + 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); + } + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; + s->dma_dac2.enabled = 1; + set_dac2_rate(s, 8000); + } + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + s->sctrl &= ~SCTRL_R1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_R1FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_R1FMT; + } + if (file->f_mode & FMODE_WRITE) { + s->sctrl &= ~SCTRL_P2FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P2FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P2FMT; + } + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + init_MUTEX(&s->sem); + return 0; +} + +static int es1371_release(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac2(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + dealloc_dmabuf(s, &s->dma_dac2); + } + 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 es1371_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: es1371_read, + write: es1371_write, + poll: es1371_poll, + ioctl: es1371_ioctl, + mmap: es1371_mmap, + open: es1371_open, + release: es1371_release, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac1.mapped) + return -ENXIO; + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + add_wait_queue(&s->dma_dac1.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac1.count < 0) { + s->dma_dac1.count = 0; + s->dma_dac1.swptr = s->dma_dac1.hwptr; + } + swptr = s->dma_dac1.swptr; + cnt = s->dma_dac1.dmasize-swptr; + if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) + cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac1.enabled) + start_dac1(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac1.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac1.swptr = swptr; + s->dma_dac1.count += cnt; + s->dma_dac1.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac1.enabled) + start_dac1(s); + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1371_poll_dac(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (!s->dma_dac1.ready && prog_dmabuf_dac1(s)) + return 0; + poll_wait(file, &s->dma_dac1.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1371_mmap_dac(struct file *file, struct vm_area_struct *vma) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (!(vma->vm_flags & VM_WRITE)) + return -EINVAL; + lock_kernel(); + if ((ret = prog_dmabuf_dac1(s)) != 0) + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << s->dma_dac1.buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma, vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot)) + goto out; + s->dma_dac1.mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int es1371_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, ret; + + VALIDATE_STATE(s); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + stop_dac1(s); + synchronize_irq(); + s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + set_dac1_rate(s, val); + } + return put_user(s->dac1rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (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) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P1SEB; + else + s->sctrl &= ~SCTRL_P1SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + s->dma_dac1.enabled = 1; + start_dac1(s); + } else { + s->dma_dac1.enabled = 0; + stop_dac1(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_dac1.fragsize; + count = s->dma_dac1.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac1.dmasize - count; + abinfo.fragstotal = s->dma_dac1.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac1.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 (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETOPTR: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_dac1.total_bytes; + count = s->dma_dac1.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac1.fragshift; + cinfo.ptr = s->dma_dac1.hwptr; + if (s->dma_dac1.mapped) + s->dma_dac1.count &= s->dma_dac1.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if ((val = prog_dmabuf_dac1(s))) + return val; + return put_user(s->dma_dac1.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + s->dma_dac1.ossfragshift = val & 0xffff; + s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac1.ossfragshift < 4) + s->dma_dac1.ossfragshift = 4; + if (s->dma_dac1.ossfragshift > 15) + s->dma_dac1.ossfragshift = 15; + if (s->dma_dac1.ossmaxfrags < 4) + s->dma_dac1.ossmaxfrags = 4; + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (s->dma_dac1.subdivision) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + s->dma_dac1.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(s->dac1rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, (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 es1371_open_dac(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1371_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1371_state, devs); + if (!((s->dev_dac ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + /* we allow opening with O_RDWR, most programs do it although they will only write */ +#if 0 + if (file->f_mode & FMODE_READ) + return -EPERM; +#endif + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DAC) { + 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); + } + s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; + s->dma_dac1.enabled = 1; + set_dac1_rate(s, 8000); + spin_lock_irqsave(&s->lock, flags); + s->sctrl &= ~SCTRL_P1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P1FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P1FMT; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= FMODE_DAC; + up(&s->open_sem); + return 0; +} + +static int es1371_release_dac(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + drain_dac1(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + stop_dac1(s); + dealloc_dmabuf(s, &s->dma_dac1); + s->open_mode &= ~FMODE_DAC; + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1371_dac_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + write: es1371_write_dac, + poll: es1371_poll_dac, + ioctl: es1371_ioctl_dac, + mmap: es1371_mmap_dac, + open: es1371_open_dac, + release: es1371_release_dac, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + es1371_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + es1371_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1371_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1371_midi_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 es1371_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1371_state, devs); + if (s->dev_midi == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + 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); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(UCTRL_CNTRL_SWR, s->io+ES1371_REG_UART_CONTROL); + outb(0, s->io+ES1371_REG_UART_CONTROL); + outb(0, s->io+ES1371_REG_UART_TEST); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + s->ctrl |= CTRL_UART_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + es1371_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + return 0; +} + +static int es1371_midi_release(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + 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; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG PFX "midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->ctrl &= ~CTRL_UART_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1371_midi_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: es1371_midi_read, + write: es1371_midi_write, + poll: es1371_midi_poll, + open: es1371_midi_open, + release: es1371_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef ES1371_DEBUG +static int proc_es1371_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data) +{ + struct es1371_state *s; + int cnt, len = 0; + + if (list_empty(&devs)) + return 0; + s = list_entry(devs.next, struct es1371_state, devs); + /* print out header */ + len += sprintf(buf + len, "\t\tCreative ES137x Debug Dump-o-matic\n"); + + /* 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)); + + if (fpos >=len){ + *start = buf; + *eof =1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof =1; + return len; + +} +#endif /* ES1371_DEBUG */ + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int joystick[NR_DEVICE] = { 0, }; +static int spdif[NR_DEVICE] = { 0, }; +static int nomix[NR_DEVICE] = { 0, }; + +static unsigned int devindex = 0; +static int amplifier = 0; + +MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(joystick, "sets address and enables joystick interface (still need separate driver)"); +MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(spdif, "if 1 the output is in S/PDIF digital mode"); +MODULE_PARM(nomix, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(nomix, "if 1 no analog audio is mixed to the digital output"); +MODULE_PARM(amplifier, "i"); +MODULE_PARM_DESC(amplifier, "Set to 1 if the machine needs the amp control enabling (many laptops)"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1371 AudioPCI97 Driver"); +MODULE_LICENSE("GPL"); + + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_VIDEO), 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 }, + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_PHONEOUT), 0x4040 }, + { SOUND_MIXER_WRITE_OGAIN, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_PHONEIN), 0x4040 }, + { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_IGAIN, 0x4040 } +}; + +static struct +{ + short svid, sdid; +} amplifier_needed[] = +{ + { 0x107B, 0x2150 }, /* Gateway Solo 2150 */ + { 0x13BD, 0x100C }, /* Mebius PC-MJ100V */ + { 0x1102, 0x5938 }, /* Targa Xtender 300 */ + { 0x1102, 0x8938 }, /* IPC notebook */ + { PCI_ANY_ID, PCI_ANY_ID } +}; + + +static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + struct es1371_state *s; + mm_segment_t fs; + int i, val, res = -1; + int idx; + unsigned long tmo; + signed long tmo2; + unsigned int cssr; + + if ((res=pci_enable_device(pcidev))) + return res; + + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO)) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + i = pci_set_dma_mask(pcidev, 0xffffffff); + if (i) { + printk(KERN_WARNING "es1371: architecture does not support 32bit PCI busmaster DMA\n"); + return i; + } + if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) { + printk(KERN_WARNING PFX "out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct es1371_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac1.wait); + init_waitqueue_head(&s->dma_dac2.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = ES1371_MAGIC; + 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; + 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")) { + printk(KERN_ERR PFX "io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1); + res = -EBUSY; + goto err_region; + } + if ((res=request_irq(s->irq, es1371_interrupt, SA_SHIRQ, "es1371",s))) { + printk(KERN_ERR PFX "irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO PFX "found es1371 rev %d at io %#lx irq %u\n" + KERN_INFO PFX "features: joystick 0x%x\n", s->rev, s->io, s->irq, joystick[devindex]); + /* 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)) + goto err_dev2; + if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1)) < 0)) + goto err_dev3; + if ((res=(s->dev_midi = register_sound_midi(&es1371_midi_fops, -1))<0 )) + goto err_dev4; +#ifdef ES1371_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_read_entry("es1371",0,NULL,proc_es1371_dump,NULL); +#endif /* ES1371_DEBUG */ + + /* initialize codec registers */ + s->ctrl = 0; + + /* Check amplifier requirements */ + + if(amplifier) + s->ctrl |= CTRL_GPIO_OUT0; + else for(idx = 0; amplifier_needed[idx].svid != PCI_ANY_ID; idx++) + { + if(pcidev->subsystem_vendor == amplifier_needed[idx].svid && + pcidev->subsystem_device == amplifier_needed[idx].sdid) + { + s->ctrl |= CTRL_GPIO_OUT0; /* turn internal amplifier on */ + printk(KERN_INFO PFX "Enabling internal amplifier.\n"); + } + } + s->gameport.io = 0; + if ((joystick[devindex] & ~0x18) == 0x200) { + if (!request_region(joystick[devindex], JOY_EXTENT, "es1371")) + printk(KERN_ERR PFX "joystick address 0x%x already in use\n", joystick[devindex]); + else { + s->ctrl |= CTRL_JYSTK_EN | (((joystick[devindex] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); + s->gameport.io = joystick[devindex]; + } + } else if (joystick[devindex] == 1) { + for (i = 0x218; i >= 0x200; i -= 0x08) { + if (request_region(i, JOY_EXTENT, "es1371")) { + s->ctrl |= CTRL_JYSTK_EN | (((i >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); + s->gameport.io = i; + break; + } + } + if (!s->gameport.io) + printk(KERN_ERR PFX "no free joystick address found\n"); + } + s->sctrl = 0; + cssr = 0; + s->spdif_volume = -1; + /* check to see if s/pdif mode is being requested */ + if (spdif[devindex]) { + if (s->rev >= 4) { + printk(KERN_INFO PFX "enabling S/PDIF output\n"); + s->spdif_volume = 0; + cssr |= STAT_EN_SPDIF; + s->ctrl |= CTRL_SPDIFEN_B; + if (nomix[devindex]) /* don't mix analog inputs to s/pdif output */ + s->ctrl |= CTRL_RECEN_B; + } else { + printk(KERN_ERR PFX "revision %d does not support S/PDIF\n", s->rev); + } + } + /* initialize the chips */ + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + outl(0, s->io+ES1371_REG_LEGACY); + pci_set_master(pcidev); /* enable bus mastering */ + /* if we are a 5880 turn on the AC97 */ + if (s->vendor == PCI_VENDOR_ID_ENSONIQ && + ((s->device == PCI_DEVICE_ID_ENSONIQ_CT5880 && s->rev >= CT5880REV_CT5880_C) || + (s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_CT5880_A) || + (s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_ES1373_8))) { + cssr |= CSTAT_5880_AC97_RST; + outl(cssr, s->io+ES1371_REG_STATUS); + /* need to delay around 20ms(bleech) to give + some CODECs enough time to wakeup */ + tmo = jiffies + (HZ / 50) + 1; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } + } + /* AC97 warm reset to start the bitclk */ + outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL); + udelay(2); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + /* init the sample rate converter */ + src_init(s); + /* codec init */ + if (!ac97_probe_codec(&s->codec)) { + res = -ENODEV; + goto err_gp; + } + /* set default values */ + + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + 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); + } + /* 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); + } + set_fs(fs); + /* turn on S/PDIF output driver if requested */ + outl(cssr, s->io+ES1371_REG_STATUS); + /* register gameport */ + gameport_register_port(&s->gameport); + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_gp: + if (s->gameport.io) + release_region(s->gameport.io, JOY_EXTENT); + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->codec.dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR PFX "cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, ES1371_EXTENT); + err_region: + kfree(s); + return res; +} + +static void __devinit es1371_remove(struct pci_dev *dev) +{ + struct es1371_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); +#ifdef ES1371_DEBUG + if (s->ps) + remove_proc_entry("es1371", NULL); +#endif /* ES1371_DEBUG */ + outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */ + outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */ + synchronize_irq(); + free_irq(s->irq, s); + if (s->gameport.io) { + gameport_unregister_port(&s->gameport); + release_region(s->gameport.io, JOY_EXTENT); + } + release_region(s->io, ES1371_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_dsp(s->dev_dac); + unregister_sound_midi(s->dev_midi); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_CT5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { PCI_VENDOR_ID_ECTIVA, PCI_DEVICE_ID_ECTIVA_EV1938, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver es1371_driver = { + name: "es1371", + id_table: id_table, + probe: es1371_probe, + remove: es1371_remove +}; + +static int __init init_es1371(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO PFX "version v0.30 time " __TIME__ " " __DATE__ "\n"); + return pci_module_init(&es1371_driver); +} + +static void __exit cleanup_es1371(void) +{ + printk(KERN_INFO PFX "unloading\n"); + pci_unregister_driver(&es1371_driver); +} + +module_init(init_es1371); +module_exit(cleanup_es1371); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +/* format is: es1371=[joystick] */ + +static int __init es1371_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; + if (get_option(&str, &joystick[nr_dev]) == 2) + (void)get_option(&str, &spdif[nr_dev]); + nr_dev++; + return 1; +} + +__setup("es1371=", es1371_setup); + +#endif /* MODULE */ diff -Nru a/sound/oss/esssolo1.c b/sound/oss/esssolo1.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/esssolo1.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,2484 @@ +/****************************************************************************/ + +/* + * esssolo1.c -- ESS Technology Solo1 (ES1946) audio driver. + * + * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * 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 so far + * + * 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 + * + * Revision history + * 10.11.1998 0.1 Initial release (without any hardware) + * 22.03.1999 0.2 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.3 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * 15.06.1999 0.4 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.5 Add pci_set_master + * 12.08.1999 0.6 Fix MIDI UART crashing the driver + * Changed mixer semantics from OSS documented + * behaviour to OSS "code behaviour". + * Recording might actually work now. + * The real DDMA controller address register is at PCI config + * 0x60, while the register at 0x18 is used as a placeholder + * register for BIOS address allocation. This register + * is supposed to be copied into 0x60, according + * to the Solo1 datasheet. When I do that, I can access + * the DDMA registers except the mask bit, which + * is stuck at 1. When I copy the contents of 0x18 +0x10 + * to the DDMA base register, everything seems to work. + * The fun part is that the Windows Solo1 driver doesn't + * seem to do these tricks. + * Bugs remaining: plops and clicks when starting/stopping playback + * 31.08.1999 0.7 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.8 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 07.10.1999 0.9 Fix initialization; complain if sequencer writes time out + * Revised resource grabbing for the FM synthesizer + * 28.10.1999 0.10 More waitqueue races fixed + * 09.12.1999 0.11 Work around stupid Alpha port issue (virt_to_bus(kmalloc(GFP_DMA)) > 16M) + * Disabling recording on Alpha + * 12.01.2000 0.12 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * Integrated (aka redid 8-)) APM support patch by Zach Brown + * 07.02.2000 0.13 Use pci_alloc_consistent and pci_register_driver + * 19.02.2000 0.14 Use pci_dma_supported to determine if recording should be disabled + * 13.03.2000 0.15 Reintroduce initialization of a couple of PCI config space registers + * 21.11.2000 0.16 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.17 More dma buffer initializations, patch from + * Tjeerd Mulder + * 31.01.2001 0.18 Register/Unregister gameport, original patch from + * Nathaniel Daw + * Fix SETTRIGGER non OSS API conformity + * 10.03.2001 provide abs function, prevent picking up a bogus kernel macro + * for abs. Bug report by Andrew Morton + * 15.05.2001 pci_enable_device moved, return values in probe cleaned + * up. Marcus Meissner + * 22.05.2001 0.19 more cleanups, changed PM to PCI 2.4 style, got rid + * of global list of devices, using pci device data. + * Marcus Meissner + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125d +#endif +#ifndef PCI_DEVICE_ID_ESS_SOLO1 +#define PCI_DEVICE_ID_ESS_SOLO1 0x1969 +#endif + +#define SOLO1_MAGIC ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1) + +#define DDMABASE_OFFSET 0 /* chip bug workaround kludge */ +#define DDMABASE_EXTENT 16 + +#define IOBASE_EXTENT 16 +#define SBBASE_EXTENT 16 +#define VCBASE_EXTENT (DDMABASE_EXTENT+DDMABASE_OFFSET) +#define MPUBASE_EXTENT 4 +#define GPBASE_EXTENT 4 +#define GAMEPORT_EXTENT 4 + +#define FMSYNTH_EXTENT 4 + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +static struct pci_driver solo1_driver; + +/* --------------------------------------------------------------------- */ + +struct solo1_state { + /* magic */ + unsigned int magic; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_midi; + int dev_dmfm; + + /* hardware resources */ + unsigned long iobase, sbbase, vcbase, ddmabase, mpubase; /* long for SPARC */ + unsigned int irq; + + /* mixer registers */ + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + /* wave stuff */ + unsigned fmt; + unsigned channels; + unsigned rate; + unsigned char clkdiv; + unsigned ena; + + 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; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + 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 endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct gameport gameport; +}; + +/* --------------------------------------------------------------------- */ + +static inline void write_seq(struct solo1_state *s, unsigned char data) +{ + int i; + unsigned long flags; + + /* the __cli stunt is to send the data within the command window */ + for (i = 0; i < 0xffff; i++) { + __save_flags(flags); + __cli(); + if (!(inb(s->sbbase+0xc) & 0x80)) { + outb(data, s->sbbase+0xc); + __restore_flags(flags); + return; + } + __restore_flags(flags); + } + printk(KERN_ERR "esssolo1: write_seq timeout\n"); + outb(data, s->sbbase+0xc); +} + +static inline int read_seq(struct solo1_state *s, unsigned char *data) +{ + int i; + + if (!data) + return 0; + for (i = 0; i < 0xffff; i++) + if (inb(s->sbbase+0xe) & 0x80) { + *data = inb(s->sbbase+0xa); + return 1; + } + printk(KERN_ERR "esssolo1: read_seq timeout\n"); + return 0; +} + +static int inline reset_ctrl(struct solo1_state *s) +{ + int i; + + outb(3, s->sbbase+6); /* clear sequencer and FIFO */ + udelay(10); + outb(0, s->sbbase+6); + for (i = 0; i < 0xffff; i++) + if (inb(s->sbbase+0xe) & 0x80) + if (inb(s->sbbase+0xa) == 0xaa) { + write_seq(s, 0xc6); /* enter enhanced mode */ + return 1; + } + return 0; +} + +static void write_ctrl(struct solo1_state *s, unsigned char reg, unsigned char data) +{ + write_seq(s, reg); + write_seq(s, data); +} + +#if 0 /* unused */ +static unsigned char read_ctrl(struct solo1_state *s, unsigned char reg) +{ + unsigned char r; + + write_seq(s, 0xc0); + write_seq(s, reg); + read_seq(s, &r); + return r; +} +#endif /* unused */ + +static void write_mixer(struct solo1_state *s, unsigned char reg, unsigned char data) +{ + outb(reg, s->sbbase+4); + outb(data, s->sbbase+5); +} + +static unsigned char read_mixer(struct solo1_state *s, unsigned char reg) +{ + outb(reg, s->sbbase+4); + return inb(s->sbbase+5); +} + +/* --------------------------------------------------------------------- */ + +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; +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_dac(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_WRITE; + write_mixer(s, 0x78, 0x10); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct solo1_state *s) +{ + unsigned long flags; + + 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; + write_mixer(s, 0x78, 0x12); + udelay(10); + write_mixer(s, 0x78, 0x13); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_adc(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_READ; + write_ctrl(s, 0xb8, 0xe); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_READ) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->ena |= FMODE_READ; + write_ctrl(s, 0xb8, 0xf); +#if 0 + printk(KERN_DEBUG "solo1: DMAbuffer: 0x%08lx\n", (long)s->dma_adc.rawbuf); + printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", + inb(s->ddmabase+0xf), inw(s->ddmabase+4), inl(s->ddmabase), inb(s->ddmabase+8)); +#endif + outb(0, s->ddmabase+0xd); /* master reset */ + outb(1, s->ddmabase+0xf); /* mask */ + outb(0x54/*0x14*/, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ + outl(virt_to_bus(s->dma_adc.rawbuf), s->ddmabase); + outw(s->dma_adc.dmasize-1, s->ddmabase+4); + outb(0, s->ddmabase+0xf); + } + spin_unlock_irqrestore(&s->lock, flags); +#if 0 + printk(KERN_DEBUG "solo1: start DMA: reg B8: 0x%02x SBstat: 0x%02x\n" + KERN_DEBUG "solo1: DMA: stat: 0x%02x cnt: 0x%04x mask: 0x%02x\n", + read_ctrl(s, 0xb8), inb(s->sbbase+0xc), + inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->ddmabase+0xf)); + printk(KERN_DEBUG "solo1: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1: B1: 0x%02x B2: 0x%02x B4: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb4), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), + read_ctrl(s, 0xb9)); +#endif +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static inline void dealloc_dmabuf(struct solo1_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); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db) +{ + int order; + unsigned bytespersec; + unsigned bufs, sample_shift = 0; + struct page *page, *pend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + 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); + } + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->channels > 1) + sample_shift++; + bytespersec = s->rate << sample_shift; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytespersec) + db->fragshift = ld2(bytespersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytespersec/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--; + 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; + db->dmasize = db->numfrag << db->fragshift; + db->enabled = 1; + return 0; +} + +static inline int prog_dmabuf_adc(struct solo1_state *s) +{ + unsigned long va; + int c; + + stop_adc(s); + /* check if PCI implementation supports 24bit busmaster DMA */ + if (s->dev->dma_mask > 0xffffff) + return -EIO; + if ((c = prog_dmabuf(s, &s->dma_adc))) + return c; + va = s->dma_adc.dmaaddr; + if ((va & ~((1<<24)-1))) + panic("solo1: buffer above 16M boundary"); + outb(0, s->ddmabase+0xd); /* clear */ + outb(1, s->ddmabase+0xf); /* mask */ + /*outb(0, s->ddmabase+8);*/ /* enable (enable is active low!) */ + outb(0x54, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ + outl(va, s->ddmabase); + outw(s->dma_adc.dmasize-1, s->ddmabase+4); + c = - s->dma_adc.fragsamples; + write_ctrl(s, 0xa4, c); + write_ctrl(s, 0xa5, c >> 8); + outb(0, s->ddmabase+0xf); + s->dma_adc.ready = 1; + return 0; +} + +static inline int prog_dmabuf_dac(struct solo1_state *s) +{ + unsigned long va; + int c; + + stop_dac(s); + if ((c = prog_dmabuf(s, &s->dma_dac))) + return c; + memset(s->dma_dac.rawbuf, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80, s->dma_dac.dmasize); /* almost correct for U16 */ + va = s->dma_dac.dmaaddr; + if ((va ^ (va + s->dma_dac.dmasize - 1)) & ~((1<<20)-1)) + panic("solo1: buffer crosses 1M boundary"); + outl(va, s->iobase); + /* warning: s->dma_dac.dmasize & 0xffff must not be zero! i.e. this limits us to a 32k buffer */ + outw(s->dma_dac.dmasize, s->iobase+4); + c = - s->dma_dac.fragsamples; + write_mixer(s, 0x74, c); + write_mixer(s, 0x76, c >> 8); + outb(0xa, s->iobase+6); + s->dma_dac.ready = 1; + return 0; +} + +static inline 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; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ + +static void solo1_update_ptr(struct solo1_state *s) +{ + int diff; + unsigned hwptr; + + /* update ADC pointer */ + if (s->ena & FMODE_READ) { + hwptr = (s->dma_adc.dmasize - 1 - inw(s->ddmabase+4)) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; +#if 0 + printk(KERN_DEBUG "solo1: rd: hwptr %u swptr %u dmasize %u count %u\n", + s->dma_adc.hwptr, s->dma_adc.swptr, s->dma_adc.dmasize, s->dma_adc.count); +#endif + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->ena &= ~FMODE_READ; + write_ctrl(s, 0xb8, 0xe); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + /* update DAC pointer */ + if (s->ena & FMODE_WRITE) { + hwptr = (s->dma_dac.dmasize - inw(s->iobase+4)) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; +#if 0 + printk(KERN_DEBUG "solo1: wr: hwptr %u swptr %u dmasize %u count %u\n", + s->dma_dac.hwptr, s->dma_dac.swptr, s->dma_dac.dmasize, s->dma_dac.count); +#endif + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + s->ena &= ~FMODE_WRITE; + write_mixer(s, 0x78, 0x12); + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, s->dma_dac.dmasize, s->dma_dac.swptr, + s->dma_dac.fragsize, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count < (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +/* --------------------------------------------------------------------- */ + +static void prog_codec(struct solo1_state *s) +{ + unsigned long flags; + int fdiv, filter; + unsigned char c; + + reset_ctrl(s); + write_seq(s, 0xd3); + /* program sampling rates */ + filter = s->rate * 9 / 20; /* Set filter roll-off to 90% of rate/2 */ + fdiv = 256 - 7160000 / (filter * 82); + spin_lock_irqsave(&s->lock, flags); + write_ctrl(s, 0xa1, s->clkdiv); + write_ctrl(s, 0xa2, fdiv); + write_mixer(s, 0x70, s->clkdiv); + write_mixer(s, 0x72, fdiv); + /* program ADC parameters */ + write_ctrl(s, 0xb8, 0xe); + write_ctrl(s, 0xb9, /*0x1*/0); + write_ctrl(s, 0xa8, (s->channels > 1) ? 0x11 : 0x12); + c = 0xd0; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + c |= 0x04; + if (s->fmt & (AFMT_S16_LE | AFMT_S8)) + c |= 0x20; + if (s->channels > 1) + c ^= 0x48; + write_ctrl(s, 0xb7, (c & 0x70) | 1); + write_ctrl(s, 0xb7, c); + write_ctrl(s, 0xb1, 0x50); + write_ctrl(s, 0xb2, 0x50); + /* program DAC parameters */ + c = 0x40; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + c |= 1; + if (s->fmt & (AFMT_S16_LE | AFMT_S8)) + c |= 4; + if (s->channels > 1) + c |= 2; + write_mixer(s, 0x7a, c); + write_mixer(s, 0x78, 0x10); + s->ena = 0; + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "solo1: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != SOLO1_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long arg) +{ + static const unsigned int mixer_src[8] = { + SOUND_MASK_MIC, SOUND_MASK_MIC, SOUND_MASK_CD, SOUND_MASK_VOLUME, + SOUND_MASK_MIC, 0, SOUND_MASK_LINE, 0 + }; + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, /* voice */ + [SOUND_MIXER_SYNTH] = 2, /* FM */ + [SOUND_MIXER_CD] = 3, /* CD */ + [SOUND_MIXER_LINE] = 4, /* Line */ + [SOUND_MIXER_LINE1] = 5, /* AUX */ + [SOUND_MIXER_MIC] = 6, /* Mic */ + [SOUND_MIXER_LINE2] = 7, /* Mono in */ + [SOUND_MIXER_SPEAKER] = 8, /* Speaker */ + [SOUND_MIXER_RECLEV] = 9, /* Recording level */ + [SOUND_MIXER_VOLUME] = 10 /* Master Volume */ + }; + static const unsigned char mixreg[] = { + 0x7c, /* voice */ + 0x36, /* FM */ + 0x38, /* CD */ + 0x3e, /* Line */ + 0x3a, /* AUX */ + 0x1a, /* Mic */ + 0x6d /* Mono in */ + }; + unsigned char l, r, rl, rr, vidx; + int i, val; + + VALIDATE_STATE(s); + + if (cmd == SOUND_MIXER_PRIVATE1) { + /* enable/disable/query mixer preamp */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != -1) { + val = val ? 0xff : 0xf7; + write_mixer(s, 0x7d, (read_mixer(s, 0x7d) | 0x08) & val); + } + val = (read_mixer(s, 0x7d) & 0x08) ? 1 : 0; + return put_user(val, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + /* enable/disable/query spatializer */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != -1) { + val &= 0x3f; + write_mixer(s, 0x52, val); + write_mixer(s, 0x50, val ? 0x08 : 0); + } + return put_user(read_mixer(s, 0x52), (int *)arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + 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; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + 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; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_src[read_mixer(s, 0x1c) & 7], (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV | + SOUND_MASK_SPEAKER, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | 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 (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ +#if 0 + { + static const unsigned char regs[] = { + 0x1c, 0x1a, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x60, 0x62, 0x6d, 0x7c + }; + int i; + + for (i = 0; i < sizeof(regs); i++) + printk(KERN_DEBUG "solo1: mixer reg 0x%02x: 0x%02x\n", + regs[i], read_mixer(s, regs[i])); + printk(KERN_DEBUG "solo1: ctrl reg 0x%02x: 0x%02x\n", + 0xb4, read_ctrl(s, 0xb4)); + } +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + i = hweight32(val); + if (i == 0) + return 0; + else if (i > 1) + val &= ~mixer_src[read_mixer(s, 0x1c) & 7]; + for (i = 0; i < 8; i++) { + if (mixer_src[i] & val) + break; + } + if (i > 7) + return 0; + write_mixer(s, 0x1c, i); + return 0; + + case SOUND_MIXER_VOLUME: + 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; + if (l < 6) { + rl = 0x40; + l = 0; + } else { + rl = (l * 2 - 11) / 3; + l = (rl * 3 + 11) / 2; + } + if (r < 6) { + rr = 0x40; + r = 0; + } else { + rr = (r * 2 - 11) / 3; + r = (rr * 3 + 11) / 2; + } + write_mixer(s, 0x60, rl); + write_mixer(s, 0x62, rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[9] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[9] = val; +#endif + return put_user(s->mix.vol[9], (int *)arg); + + case SOUND_MIXER_SPEAKER: + if (get_user(val, (int *)arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + else if (l < 2) + l = 2; + rl = (l - 2) / 14; + l = rl * 14 + 2; + write_mixer(s, 0x3c, rl); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[7] = l * 0x101; +#else + s->mix.vol[7] = val; +#endif + return put_user(s->mix.vol[7], (int *)arg); + + case SOUND_MIXER_RECLEV: + if (get_user(val, (int *)arg)) + return -EFAULT; + l = (val << 1) & 0x1fe; + if (l > 200) + l = 200; + else if (l < 5) + l = 5; + r = (val >> 7) & 0x1fe; + if (r > 200) + r = 200; + else if (r < 5) + r = 5; + rl = (l - 5) / 13; + rr = (r - 5) / 13; + r = (rl * 13 + 5) / 2; + l = (rr * 13 + 5) / 2; + write_ctrl(s, 0xb4, (rl << 4) | rr); +#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); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + l = (val << 1) & 0x1fe; + if (l > 200) + l = 200; + else if (l < 5) + l = 5; + r = (val >> 7) & 0x1fe; + if (r > 200) + r = 200; + else if (r < 5) + r = 5; + rl = (l - 5) / 13; + rr = (r - 5) / 13; + r = (rl * 13 + 5) / 2; + l = (rr * 13 + 5) / 2; + write_mixer(s, mixreg[vidx-1], (rl << 4) | rr); +#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 int solo1_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; + + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; + drvr = pci_dev_driver (pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (s->dev_mixer == minor) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + return 0; +} + +static int solo1_release_mixdev(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int solo1_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct solo1_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations solo1_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: solo1_ioctl_mixdev, + open: solo1_open_mixdev, + release: solo1_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct solo1_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_dac.mapped) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + 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) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "solo1: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + 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; + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc), cnt); +#endif + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" + KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" + KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), + inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); +#endif + if (inb(s->ddmabase+15) & 1) + printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" + KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" + KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), + inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); +#endif + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x\n", + read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc)); +#endif + } + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t solo1_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + 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; +#if 0 + printk(KERN_DEBUG "solo1_write: reg 70: 0x%02x 71: 0x%02x 72: 0x%02x 74: 0x%02x 76: 0x%02x 78: 0x%02x 7A: 0x%02x\n" + KERN_DEBUG "solo1_write: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x SBstat: 0x%02x\n", + read_mixer(s, 0x70), read_mixer(s, 0x71), read_mixer(s, 0x72), read_mixer(s, 0x74), read_mixer(s, 0x76), + read_mixer(s, 0x78), read_mixer(s, 0x7a), inl(s->iobase), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); + printk(KERN_DEBUG "solo1_write: reg 78: 0x%02x reg 7A: 0x%02x DMAcnt: 0x%04x DMAstat: 0x%02x SBstat: 0x%02x\n", + read_mixer(s, 0x78), read_mixer(s, 0x7a), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); +#endif + ret = 0; + add_wait_queue(&s->dma_dac.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac.enabled) + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac.enabled) + start_dac(s); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int solo1_poll(struct file *file, struct poll_table_struct *wait) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + 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) { + if (!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + 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; + } + } + 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) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + + +static int solo1_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac(s)) != 0) + goto out; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + goto out; + db = &s->dma_adc; + } else + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret, count; + int div1, div2; + unsigned rate1, rate2; + + 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: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + 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: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + prog_codec(s); + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program sampling rates */ + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + div1 = (768000 + val / 2) / val; + rate1 = (768000 + div1 / 2) / div1; + div1 = -div1; + div2 = (793800 + val / 2) / val; + rate2 = (793800 + div2 / 2) / div2; + div2 = (-div2) & 0x7f; + if (abs(val - rate2) < abs(val - rate1)) { + rate1 = rate2; + div1 = div2; + } + s->rate = rate1; + s->clkdiv = div1; + prog_codec(s); + } + return put_user(s->rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program channels */ + s->channels = val ? 2 : 1; + prog_codec(s); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program channels */ + s->channels = (val >= 2) ? 2 : 1; + prog_codec(s); + } + return put_user(s->channels, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program format */ + if (val != AFMT_S16_LE && val != AFMT_U16_LE && + val != AFMT_S8 && val != AFMT_U8) + val = AFMT_U8; + s->fmt = val; + prog_codec(s); + } + return put_user(s->fmt, (int *)arg); + + case SNDCTL_DSP_POST: + 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; + s->dma_dac.enabled = 1; + start_adc(s); + if (inb(s->ddmabase+15) & 1) + printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); + } else { + s->dma_dac.enabled = 0; + 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; + s->dma_dac.enabled = 1; + start_dac(s); + } else { + s->dma_dac.enabled = 0; + 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)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + count = s->dma_dac.count; + 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; + 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)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + 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 && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + count = s->dma_dac.count; + 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; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + 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 && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (count < 0) + count = 0; + cinfo.blocks = 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); +#if 0 + printk(KERN_DEBUG "esssolo1: GETOPTR: bytes %u blocks %u ptr %u, buforder %u numfrag %u fragshift %u\n" + KERN_DEBUG "esssolo1: swptr %u count %u fragsize %u dmasize %u fragsamples %u\n", + cinfo.bytes, cinfo.blocks, cinfo.ptr, s->dma_dac.buforder, s->dma_dac.numfrag, s->dma_dac.fragshift, + s->dma_dac.swptr, s->dma_dac.count, s->dma_dac.fragsize, s->dma_dac.dmasize, s->dma_dac.fragsamples); +#endif + 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; + 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) { + 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 (file->f_mode & FMODE_WRITE) { + 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; + } + 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) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(s->rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user(s->channels, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->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 solo1_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + + VALIDATE_STATE(s); + 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); + outb(0, s->iobase+6); /* disable DMA */ + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + outb(1, s->ddmabase+0xf); /* mask DMA channel */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ~(FMODE_READ | FMODE_WRITE); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static int solo1_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; + + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; + + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (FMODE_READ | FMODE_WRITE)) { + 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); + } + s->fmt = AFMT_U8; + s->channels = 1; + s->rate = 8000; + s->clkdiv = 96 | 0x80; + s->ena = 0; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + s->dma_dac.enabled = 1; + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + prog_codec(s); + return 0; +} + +static /*const*/ struct file_operations solo1_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: solo1_read, + write: solo1_write, + poll: solo1_poll, + ioctl: solo1_ioctl, + mmap: solo1_mmap, + open: solo1_open, + release: solo1_release, +}; + +/* --------------------------------------------------------------------- */ + +/* hold spinlock for the following! */ +static void solo1_handle_midi(struct solo1_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->mpubase)) + return; + wake = 0; + while (!(inb(s->mpubase+1) & 0x80)) { + ch = inb(s->mpubase); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->mpubase+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->mpubase); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + +static void solo1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct solo1_state *s = (struct solo1_state *)dev_id; + unsigned int intsrc; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inb(s->iobase+7); /* get interrupt source(s) */ + if (!intsrc) + return; + (void)inb(s->sbbase+0xe); /* clear interrupt */ + spin_lock(&s->lock); + /* clear audio interrupts first */ + if (intsrc & 0x20) + write_mixer(s, 0x7a, read_mixer(s, 0x7a) & 0x7f); + solo1_update_ptr(s); + solo1_handle_midi(s); + spin_unlock(&s->lock); +} + +static void solo1_midi_timer(unsigned long data) +{ + struct solo1_state *s = (struct solo1_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + solo1_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} + +/* --------------------------------------------------------------------- */ + +static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + solo1_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + solo1_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int solo1_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int solo1_midi_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; + + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; + + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (s->dev_midi == minor) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + 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); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(0xff, s->mpubase+1); /* reset command */ + outb(0x3f, s->mpubase+1); /* uart command */ + if (!(inb(s->mpubase+1) & 0x80)) + inb(s->mpubase); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + outb(0xb0, s->iobase + 7); /* enable A1, A2, MPU irq's */ + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = solo1_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + return 0; +} + +static int solo1_midi_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + 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; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "solo1: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + outb(0x30, s->iobase + 7); /* enable A1, A2 irq's */ + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations solo1_midi_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: solo1_midi_read, + write: solo1_midi_write, + poll: solo1_midi_poll, + open: solo1_midi_open, + release: solo1_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +static int solo1_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct solo1_state *s = (struct solo1_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->sbbase); + outb(0, s->sbbase+1); + outb(regb, s->sbbase+2); + outb(0, s->sbbase+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->sbbase+2; + } else { + regb = n.voice; + io = s->sbbase; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->sbbase + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->sbbase+2; + } else { + regb = n.voice; + io = s->sbbase; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void *)arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->sbbase); + outb((p.kbd_split & 1) << 6, s->sbbase+1); + outb(0xbd, s->sbbase); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->sbbase+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->sbbase+2); + outb(arg, s->sbbase+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->sbbase+2); + outb(arg & 1, s->sbbase+3); + return 0; + + default: + return -EINVAL; + } +} + +static int solo1_dmfm_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; + + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; + + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (s->dev_dmfm == minor) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DMFM) { + 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); + } + if (!request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1")) { + up(&s->open_sem); + printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n"); + return -EBUSY; + } + /* init the stuff */ + outb(1, s->sbbase); + outb(0x20, s->sbbase+1); /* enable waveforms */ + outb(4, s->sbbase+2); + outb(0, s->sbbase+3); /* no 4op enabled */ + outb(5, s->sbbase+2); + outb(1, s->sbbase+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + up(&s->open_sem); + return 0; +} + +static int solo1_dmfm_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + lock_kernel(); + down(&s->open_sem); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->sbbase); + outb(0, s->sbbase+1); + outb(regb, s->sbbase+2); + outb(0, s->sbbase+3); + } + release_region(s->sbbase, FMSYNTH_EXTENT); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations solo1_dmfm_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: solo1_dmfm_ioctl, + open: solo1_dmfm_open, + release: solo1_dmfm_release, +}; + +/* --------------------------------------------------------------------- */ + +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_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 } +}; + +static int setup_solo1(struct solo1_state *s) +{ + struct pci_dev *pcidev = s->dev; + mm_segment_t fs; + int i, val; + + /* initialize DDMA base address */ + printk(KERN_DEBUG "solo1: ddma base address: 0x%lx\n", s->ddmabase); + pci_write_config_word(pcidev, 0x60, (s->ddmabase & (~0xf)) | 1); + /* set DMA policy to DDMA, IRQ emulation off (CLKRUN disabled for now) */ + pci_write_config_dword(pcidev, 0x50, 0); + /* disable legacy audio address decode */ + pci_write_config_word(pcidev, 0x40, 0x907f); + + /* initialize the chips */ + if (!reset_ctrl(s)) { + printk(KERN_ERR "esssolo1: cannot reset controller\n"); + return -1; + } + outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */ + + /* initialize mixer regs */ + write_mixer(s, 0x7f, 0); /* disable music digital recording */ + write_mixer(s, 0x7d, 0x0c); /* enable mic preamp, MONO_OUT is 2nd DAC right channel */ + write_mixer(s, 0x64, 0x45); /* volume control */ + write_mixer(s, 0x48, 0x10); /* enable music DAC/ES6xx interface */ + write_mixer(s, 0x50, 0); /* disable spatializer */ + write_mixer(s, 0x52, 0); + write_mixer(s, 0x14, 0); /* DAC1 minimum volume */ + write_mixer(s, 0x71, 0x20); /* enable new 0xA1 reg format */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ + outb(1, s->ddmabase+0xf); /* mask channel */ + /*outb(0, s->ddmabase+0x8);*/ /* enable controller (enable is low active!!) */ + + pci_set_master(pcidev); /* enable bus mastering */ + + fs = get_fs(); + set_fs(KERNEL_DS); + 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); + } + val = 1; /* enable mic preamp */ + mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long)&val); + set_fs(fs); + return 0; +} + +static int +solo1_suspend(struct pci_dev *pci_dev, u32 state) { + struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + return 1; + outb(0, s->iobase+6); + /* DMA master clear */ + outb(0, s->ddmabase+0xd); + /* reset sequencer and FIFO */ + outb(3, s->sbbase+6); + /* turn off DDMA controller address space */ + pci_write_config_word(s->dev, 0x60, 0); + return 0; +} + +static int +solo1_resume(struct pci_dev *pci_dev) { + struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + return 1; + setup_solo1(s); + return 0; +} + +static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + struct solo1_state *s; + int ret; + + if ((ret=pci_enable_device(pcidev))) + return ret; + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) || + !(pci_resource_flags(pcidev, 1) & IORESOURCE_IO) || + !(pci_resource_flags(pcidev, 2) & IORESOURCE_IO) || + !(pci_resource_flags(pcidev, 3) & IORESOURCE_IO)) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + + /* Recording requires 24-bit DMA, so attempt to set dma mask + * to 24 bits first, then 32 bits (playback only) if that fails. + */ + if (pci_set_dma_mask(pcidev, 0x00ffffff) && + pci_set_dma_mask(pcidev, 0xffffffff)) { + printk(KERN_WARNING "solo1: architecture does not support 24bit or 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if (!(s = kmalloc(sizeof(struct solo1_state), GFP_KERNEL))) { + printk(KERN_WARNING "solo1: out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct solo1_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = SOLO1_MAGIC; + s->dev = pcidev; + s->iobase = pci_resource_start(pcidev, 0); + s->sbbase = pci_resource_start(pcidev, 1); + s->vcbase = pci_resource_start(pcidev, 2); + s->ddmabase = s->vcbase + DDMABASE_OFFSET; + s->mpubase = pci_resource_start(pcidev, 3); + s->gameport.io = pci_resource_start(pcidev, 4); + s->irq = pcidev->irq; + ret = -EBUSY; + if (!request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region1; + } + if (!request_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region2; + } + if (!request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region3; + } + if (!request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region4; + } + if (s->gameport.io && !request_region(s->gameport.io, GAMEPORT_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: gameport io ports in use\n"); + s->gameport.io = 0; + } + if ((ret=request_irq(s->irq,solo1_interrupt,SA_SHIRQ,"ESS Solo1",s))) { + printk(KERN_ERR "solo1: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "solo1: joystick port at %#x\n", s->gameport.io+1); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1)) < 0) { + ret = s->dev_audio; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1)) < 0) { + ret = s->dev_midi; + goto err_dev3; + } + if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) { + ret = s->dev_dmfm; + goto err_dev4; + } + if (setup_solo1(s)) { + ret = -EIO; + goto err; + } + /* register gameport */ + gameport_register_port(&s->gameport); + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + return 0; + + err: + unregister_sound_dsp(s->dev_dmfm); + err_dev4: + unregister_sound_dsp(s->dev_midi); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "solo1: initialisation error\n"); + free_irq(s->irq, s); + err_irq: + if (s->gameport.io) + release_region(s->gameport.io, GAMEPORT_EXTENT); + release_region(s->iobase, IOBASE_EXTENT); + err_region4: + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); + err_region3: + release_region(s->ddmabase, DDMABASE_EXTENT); + err_region2: + release_region(s->mpubase, MPUBASE_EXTENT); + err_region1: + kfree(s); + return ret; +} + +static void __devinit solo1_remove(struct pci_dev *dev) +{ + struct solo1_state *s = pci_get_drvdata(dev); + + if (!s) + return; + /* stop DMA controller */ + outb(0, s->iobase+6); + outb(0, s->ddmabase+0xd); /* DMA master clear */ + outb(3, s->sbbase+6); /* reset sequencer and FIFO */ + synchronize_irq(); + pci_write_config_word(s->dev, 0x60, 0); /* turn off DDMA controller address space */ + free_irq(s->irq, s); + if (s->gameport.io) { + gameport_unregister_port(&s->gameport); + release_region(s->gameport.io, GAMEPORT_EXTENT); + } + release_region(s->iobase, IOBASE_EXTENT); + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); + release_region(s->ddmabase, DDMABASE_EXTENT); + release_region(s->mpubase, MPUBASE_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + unregister_sound_special(s->dev_dmfm); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver solo1_driver = { + name: "ESS Solo1", + id_table: id_table, + probe: solo1_probe, + remove: solo1_remove, + suspend: solo1_suspend, + resume: solo1_resume +}; + + +static int __init init_solo1(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "solo1: version v0.19 time " __TIME__ " " __DATE__ "\n"); + if (!pci_register_driver(&solo1_driver)) { + pci_unregister_driver(&solo1_driver); + return -ENODEV; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ESS Solo1 Driver"); +MODULE_LICENSE("GPL"); + + +static void __exit cleanup_solo1(void) +{ + printk(KERN_INFO "solo1: unloading\n"); + pci_unregister_driver(&solo1_driver); +} + +/* --------------------------------------------------------------------- */ + +module_init(init_solo1); +module_exit(cleanup_solo1); + diff -Nru a/sound/oss/gus.h b/sound/oss/gus.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/gus.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,30 @@ +/* + * gus.h + * + * Copyright: Christoph Hellwig + * + */ + +#include "ad1848.h" + +/* From gus_card.c */ +int gus_set_midi_irq(int num); +void gusintr(int irq, void *dev_id, struct pt_regs * dummy); + +/* From gus_wave.c */ +int gus_wave_detect(int baseaddr); +void gus_wave_init(struct address_info *hw_config); +void gus_wave_unload (struct address_info *hw_config); +void gus_voice_irq(void); +void gus_write8(int reg, unsigned int data); +void guswave_dma_irq(void); +void gus_delay(void); +int gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg); +void gus_timer_command (unsigned int addr, unsigned int val); + +/* From gus_midi.c */ +void gus_midi_init(struct address_info *hw_config); +void gus_midi_interrupt(int dummy); + +/* From ics2101.c */ +int ics2101_mixer_init(void); diff -Nru a/sound/oss/gus_card.c b/sound/oss/gus_card.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/gus_card.c Tue Feb 19 18:08:56 2002 @@ -0,0 +1,298 @@ +/* + * sound/gus_card.c + * + * Detection routine for the Gravis Ultrasound. + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * + * Frank van de Pol : Fixed GUS MAX interrupt handling, enabled simultanious + * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. + * Christoph Hellwig: Adapted to module_init/module_exit, simple cleanups. + * + * Status: + * Tested... + */ + + +#include +#include +#include + +#include "sound_config.h" + +#include "gus.h" +#include "gus_hw.h" + +void gusintr(int irq, void *dev_id, struct pt_regs *dummy); + +int gus_base = 0, gus_irq = 0, gus_dma = 0; +int gus_no_wave_dma = 0; +extern int gus_wave_volume; +extern int gus_pcm_volume; +extern int have_gus_max; +int gus_pnp_flag = 0; +#ifdef CONFIG_SOUND_GUS16 +static int db16 = 0; /* Has a Gus16 AD1848 on it */ +#endif + +static void __init attach_gus(struct address_info *hw_config) +{ + gus_wave_init(hw_config); + + request_region(hw_config->io_base, 16, "GUS"); + request_region(hw_config->io_base + 0x100, 12, "GUS"); /* 0x10c-> is MAX */ + + if (sound_alloc_dma(hw_config->dma, "GUS")) + printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma); + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + if (sound_alloc_dma(hw_config->dma2, "GUS(2)")) + printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma2); + gus_midi_init(hw_config); + if(request_irq(hw_config->irq, gusintr, 0, "Gravis Ultrasound", hw_config)<0) + printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq); + + return; +} + +static int __init probe_gus(struct address_info *hw_config) +{ + int irq; + int io_addr; + + if (hw_config->card_subtype == 1) + gus_pnp_flag = 1; + + irq = hw_config->irq; + + if (hw_config->card_subtype == 0) /* GUS/MAX/ACE */ + if (irq != 3 && irq != 5 && irq != 7 && irq != 9 && + irq != 11 && irq != 12 && irq != 15) + { + printk(KERN_ERR "GUS: Unsupported IRQ %d\n", irq); + return 0; + } + if (check_region(hw_config->io_base, 16)) + printk(KERN_ERR "GUS: I/O range conflict (1)\n"); + else if (check_region(hw_config->io_base + 0x100, 16)) + printk(KERN_ERR "GUS: I/O range conflict (2)\n"); + else if (gus_wave_detect(hw_config->io_base)) + return 1; + +#ifndef EXCLUDE_GUS_IODETECT + + /* + * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) + */ + + for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) + if (io_addr != hw_config->io_base) /* + * Already tested + */ + if (!check_region(io_addr, 16)) + if (!check_region(io_addr + 0x100, 16)) + if (gus_wave_detect(io_addr)) + { + hw_config->io_base = io_addr; + return 1; + } +#endif + + printk("NO GUS card found !\n"); + return 0; +} + +static void __exit unload_gus(struct address_info *hw_config) +{ + DDB(printk("unload_gus(%x)\n", hw_config->io_base)); + + gus_wave_unload(hw_config); + + release_region(hw_config->io_base, 16); + release_region(hw_config->io_base + 0x100, 12); /* 0x10c-> is MAX */ + free_irq(hw_config->irq, hw_config); + + sound_free_dma(hw_config->dma); + + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + sound_free_dma(hw_config->dma2); +} + +void gusintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + unsigned char src; + extern int gus_timer_enabled; + + sti(); + +#ifdef CONFIG_SOUND_GUSMAX + if (have_gus_max) { + struct address_info *hw_config = dev_id; + adintr(irq, (void *)hw_config->slots[1], NULL); + } +#endif +#ifdef CONFIG_SOUND_GUS16 + if (db16) { + struct address_info *hw_config = dev_id; + adintr(irq, (void *)hw_config->slots[3], NULL); + } +#endif + + while (1) + { + if (!(src = inb(u_IrqStatus))) + return; + + if (src & DMA_TC_IRQ) + { + guswave_dma_irq(); + } + if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) + { + gus_midi_interrupt(0); + } + if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) + { + if (gus_timer_enabled) + sound_timer_interrupt(); + gus_write8(0x45, 0); /* Ack IRQ */ + gus_timer_command(4, 0x80); /* Reset IRQ flags */ + } + if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) + gus_voice_irq(); + } +} + +/* + * Some extra code for the 16 bit sampling option + */ + +#ifdef CONFIG_SOUND_GUS16 + +static int __init probe_gus_db16(struct address_info *hw_config) +{ + return ad1848_detect(hw_config->io_base, NULL, hw_config->osp); +} + +static void __init attach_gus_db16(struct address_info *hw_config) +{ + gus_pcm_volume = 100; + gus_wave_volume = 90; + + hw_config->slots[3] = ad1848_init("GUS 16 bit sampling", hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0, + hw_config->osp, + THIS_MODULE); +} + +static void __exit unload_gus_db16(struct address_info *hw_config) +{ + + ad1848_unload(hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0); + sound_unload_audiodev(hw_config->slots[3]); +} +#endif + +#ifdef CONFIG_SOUND_GUS16 +static int gus16 = 0; +#endif +#ifdef CONFIG_SOUND_GUSMAX +static int no_wave_dma = 0;/* Set if no dma is to be used for the + wave table (GF1 chip) */ +#endif + + +/* + * Note DMA2 of -1 has the right meaning in the GUS driver as well + * as here. + */ + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ +static int __initdata type = 0; /* 1 for PnP */ + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(dma, "i"); +MODULE_PARM(dma16, "i"); +MODULE_PARM(type, "i"); +#ifdef CONFIG_SOUND_GUSMAX +MODULE_PARM(no_wave_dma, "i"); +#endif +#ifdef CONFIG_SOUND_GUS16 +MODULE_PARM(db16, "i"); +MODULE_PARM(gus16, "i"); +#endif +MODULE_LICENSE("GPL"); + +static int __init init_gus(void) +{ + printk(KERN_INFO "Gravis Ultrasound audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma16; + cfg.card_subtype = type; +#ifdef CONFIG_SOUND_GUSMAX + gus_no_wave_dma = no_wave_dma; +#endif + + if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { + printk(KERN_ERR "I/O, IRQ, and DMA are mandatory\n"); + return -EINVAL; + } + +#ifdef CONFIG_SOUND_GUS16 + if (probe_gus_db16(&cfg) && gus16) { + /* FIXME: This can't work, can it ? -- Christoph */ + attach_gus_db16(&cfg); + db16 = 1; + } +#endif + if (!probe_gus(&cfg)) + return -ENODEV; + attach_gus(&cfg); + + return 0; +} + +static void __exit cleanup_gus(void) +{ +#ifdef CONFIG_SOUND_GUS16 + if (db16) + unload_gus_db16(&cfg); +#endif + unload_gus(&cfg); +} + +module_init(init_gus); +module_exit(cleanup_gus); + +#ifndef MODULE +static int __init setup_gus(char *str) +{ + /* io, irq, dma, dma2 */ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma16 = ints[4]; + + return 1; +} + +__setup("gus=", setup_gus); +#endif diff -Nru a/sound/oss/gus_hw.h b/sound/oss/gus_hw.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/gus_hw.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,50 @@ + +/* + * I/O addresses + */ + +#define u_Base (gus_base + 0x000) +#define u_Mixer u_Base +#define u_Status (gus_base + 0x006) +#define u_TimerControl (gus_base + 0x008) +#define u_TimerData (gus_base + 0x009) +#define u_IRQDMAControl (gus_base + 0x00b) +#define u_MidiControl (gus_base + 0x100) +#define MIDI_RESET 0x03 +#define MIDI_ENABLE_XMIT 0x20 +#define MIDI_ENABLE_RCV 0x80 +#define u_MidiStatus u_MidiControl +#define MIDI_RCV_FULL 0x01 +#define MIDI_XMIT_EMPTY 0x02 +#define MIDI_FRAME_ERR 0x10 +#define MIDI_OVERRUN 0x20 +#define MIDI_IRQ_PEND 0x80 +#define u_MidiData (gus_base + 0x101) +#define u_Voice (gus_base + 0x102) +#define u_Command (gus_base + 0x103) +#define u_DataLo (gus_base + 0x104) +#define u_DataHi (gus_base + 0x105) +#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */ +#define u_MixSelect (gus_base + 0x506) /* registers. */ +#define u_IrqStatus u_Status +# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ +# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ +# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ +# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ +# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ +# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ +# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ + +#define ICS2101 1 +# define ICS_MIXDEVS 6 +# define DEV_MIC 0 +# define DEV_LINE 1 +# define DEV_CD 2 +# define DEV_GF1 3 +# define DEV_UNUSED 4 +# define DEV_VOL 5 + +# define CHN_LEFT 0 +# define CHN_RIGHT 1 +#define CS4231 2 +#define u_DRAMIO (gus_base + 0x107) diff -Nru a/sound/oss/gus_linearvol.h b/sound/oss/gus_linearvol.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/gus_linearvol.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,18 @@ +static unsigned short gus_linearvol[128] = { + 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0, + 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0, + 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70, + 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0, + 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38, + 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78, + 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8, + 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8, + 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c, + 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c, + 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c, + 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c, + 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c, + 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc, + 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc, + 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc +}; diff -Nru a/sound/oss/gus_midi.c b/sound/oss/gus_midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/gus_midi.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,262 @@ +/* + * sound/gus2_midi.c + * + * The low level driver for the GUS Midi Interface. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added __init to gus_midi_init() + */ + +#include +#include "sound_config.h" + +#include "gus.h" +#include "gus_hw.h" + +static int midi_busy = 0, input_opened = 0; +static int my_dev; +static int output_used = 0; +static volatile unsigned char gus_midi_control; + +static void (*midi_input_intr) (int dev, unsigned char data); + +static unsigned char tmp_queue[256]; +extern int gus_pnp_flag; +static volatile int qlen; +static volatile unsigned char qhead, qtail; +extern int gus_base, gus_irq, gus_dma; +extern int *gus_osp; + +static int GUS_MIDI_STATUS(void) +{ + return inb(u_MidiStatus); +} + +static int gus_midi_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev)) +{ + if (midi_busy) + { +/* printk("GUS: Midi busy\n");*/ + return -EBUSY; + } + outb((MIDI_RESET), u_MidiControl); + gus_delay(); + + gus_midi_control = 0; + input_opened = 0; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) + if (!gus_pnp_flag) + { + gus_midi_control |= MIDI_ENABLE_RCV; + input_opened = 1; + } + outb((gus_midi_control), u_MidiControl); /* Enable */ + + midi_busy = 1; + qlen = qhead = qtail = output_used = 0; + midi_input_intr = input; + + return 0; +} + +static int dump_to_midi(unsigned char midi_byte) +{ + unsigned long flags; + int ok = 0; + + output_used = 1; + + save_flags(flags); + cli(); + + if (GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY) + { + ok = 1; + outb((midi_byte), u_MidiData); + } + else + { + /* + * Enable Midi xmit interrupts (again) + */ + gus_midi_control |= MIDI_ENABLE_XMIT; + outb((gus_midi_control), u_MidiControl); + } + + restore_flags(flags); + return ok; +} + +static void gus_midi_close(int dev) +{ + /* + * Reset FIFO pointers, disable intrs + */ + + outb((MIDI_RESET), u_MidiControl); + midi_busy = 0; +} + +static int gus_midi_out(int dev, unsigned char midi_byte) +{ + unsigned long flags; + + /* + * Drain the local queue first + */ + + save_flags(flags); + cli(); + + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + restore_flags(flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi(midi_byte)) + return 1; /* + * OK + */ + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* + * Local queue full + */ + save_flags(flags); + cli(); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + restore_flags(flags); + return 1; +} + +static int gus_midi_start_read(int dev) +{ + return 0; +} + +static int gus_midi_end_read(int dev) +{ + return 0; +} + +static void gus_midi_kick(int dev) +{ +} + +static int gus_midi_buffer_status(int dev) +{ + unsigned long flags; + + if (!output_used) + return 0; + + save_flags(flags); + cli(); + + if (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + restore_flags(flags); + return (qlen > 0) | !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY); +} + +#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations gus_midi_operations = +{ + owner: THIS_MODULE, + info: {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS}, + converter: &std_midi_synth, + in_info: {0}, + open: gus_midi_open, + close: gus_midi_close, + outputc: gus_midi_out, + start_read: gus_midi_start_read, + end_read: gus_midi_end_read, + kick: gus_midi_kick, + buffer_status: gus_midi_buffer_status, +}; + +void __init gus_midi_init(struct address_info *hw_config) +{ + int dev = sound_alloc_mididev(); + + if (dev == -1) + { + printk(KERN_INFO "gus_midi: Too many midi devices detected\n"); + return; + } + outb((MIDI_RESET), u_MidiControl); + + std_midi_synth.midi_dev = my_dev = dev; + hw_config->slots[2] = dev; + midi_devs[dev] = &gus_midi_operations; + sequencer_init(); + return; +} + +void gus_midi_interrupt(int dummy) +{ + volatile unsigned char stat, data; + unsigned long flags; + int timeout = 10; + + save_flags(flags); + cli(); + + while (timeout-- > 0 && (stat = GUS_MIDI_STATUS()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY)) + { + if (stat & MIDI_RCV_FULL) + { + data = inb(u_MidiData); + if (input_opened) + midi_input_intr(my_dev, data); + } + if (stat & MIDI_XMIT_EMPTY) + { + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + if (!qlen) + { + /* + * Disable Midi output interrupts, since no data in the buffer + */ + gus_midi_control &= ~MIDI_ENABLE_XMIT; + outb((gus_midi_control), u_MidiControl); + outb((gus_midi_control), u_MidiControl); + } + } + } + restore_flags(flags); +} diff -Nru a/sound/oss/gus_vol.c b/sound/oss/gus_vol.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/gus_vol.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,153 @@ + +/* + * gus_vol.c - Compute volume for GUS. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +#include "sound_config.h" + +#include "gus.h" +#include "gus_linearvol.h" + +#define GUS_VOLUME gus_wave_volume + + +extern int gus_wave_volume; + +/* + * Calculate gus volume from note velocity, main volume, expression, and + * intrinsic patch volume given in patch library. Expression is multiplied + * in, so it emphasizes differences in note velocity, while main volume is + * added in -- I don't know whether this is right, but it seems reasonable to + * me. (In the previous stage, main volume controller messages were changed + * to expression controller messages, if they were found to be used for + * dynamic volume adjustments, so here, main volume can be assumed to be + * constant throughout a song.) + * + * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so + * we can give a big boost to very weak voices like nylon guitar and the + * basses. The normal value is 64. Strings are assigned lower values. + */ + +unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev) +{ + int i, m, n, x; + + + /* + * A voice volume of 64 is considered neutral, so adjust the main volume if + * something other than this neutral value was assigned in the patch + * library. + */ + x = 256 + 6 * (voicev - 64); + + /* + * Boost expression by voice volume above neutral. + */ + + if (voicev > 65) + xpn += voicev - 64; + xpn += (voicev - 64) / 2; + + /* + * Combine multiplicative and level components. + */ + x = vel * xpn * 6 + (voicev / 4) * x; + +#ifdef GUS_VOLUME + /* + * Further adjustment by installation-specific master volume control + * (default 60). + */ + x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + /* + * Experimental support for the channel main volume + */ + + mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */ + x = (x * mainv * mainv) / 16384; +#endif + + if (x < 2) + return (0); + else if (x >= 65535) + return ((15 << 8) | 255); + + /* + * Convert to GUS's logarithmic form with 4 bit exponent i and 8 bit + * mantissa m. + */ + + n = x; + i = 7; + if (n < 128) + { + while (i > 0 && n < (1 << i)) + i--; + } + else + { + while (n > 255) + { + n >>= 1; + i++; + } + } + /* + * Mantissa is part of linear volume not expressed in exponent. (This is + * not quite like real logs -- I wonder if it's right.) + */ + m = x - (1 << i); + + /* + * Adjust mantissa to 8 bits. + */ + if (m > 0) + { + if (i > 8) + m >>= i - 8; + else if (i < 8) + m <<= 8 - i; + } + return ((i << 8) + m); +} + +/* + * Volume-values are interpreted as linear values. Volume is based on the + * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in) + * and the volume set by the mixer-device (default 60%). + */ + +unsigned short gus_linear_vol(int vol, int mainvol) +{ + int mixer_mainvol; + + if (vol <= 0) + vol = 0; + else if (vol >= 127) + vol = 127; + +#ifdef GUS_VOLUME + mixer_mainvol = GUS_VOLUME; +#else + mixer_mainvol = 100; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + if (mainvol <= 0) + mainvol = 0; + else if (mainvol >= 127) + mainvol = 127; +#else + mainvol = 127; +#endif + return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100]; +} diff -Nru a/sound/oss/gus_wave.c b/sound/oss/gus_wave.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/gus_wave.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,3540 @@ +/* + * sound/gus_wave.c + * + * Driver for the Gravis UltraSound wave table synth. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Frank van de Pol : Fixed GUS MAX interrupt handling. Enabled simultanious + * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. + * Bartlomiej Zolnierkiewicz : added some __init/__exit + */ + +#include +#include + +#define GUSPNP_AUTODETECT + +#include "sound_config.h" +#include + +#include "gus.h" +#include "gus_hw.h" + +#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024)) + +#define MAX_SAMPLE 150 +#define MAX_PATCH 256 + +#define NOT_SAMPLE 0xffff + +struct voice_info +{ + unsigned long orig_freq; + unsigned long current_freq; + unsigned long mode; + int fixed_pitch; + int bender; + int bender_range; + int panning; + int midi_volume; + unsigned int initial_volume; + unsigned int current_volume; + int loop_irq_mode, loop_irq_parm; +#define LMODE_FINISH 1 +#define LMODE_PCM 2 +#define LMODE_PCM_STOP 3 + int volume_irq_mode, volume_irq_parm; +#define VMODE_HALT 1 +#define VMODE_ENVELOPE 2 +#define VMODE_START_NOTE 3 + + int env_phase; + unsigned char env_rate[6]; + unsigned char env_offset[6]; + + /* + * Volume computation parameters for gus_adagio_vol() + */ + int main_vol, expression_vol, patch_vol; + + /* Variables for "Ultraclick" removal */ + int dev_pending, note_pending, volume_pending, + sample_pending; + char kill_pending; + long offset_pending; + +}; + +static struct voice_alloc_info *voice_alloc; +static struct address_info *gus_hw_config; +extern int gus_base; +extern int gus_irq, gus_dma; +extern int gus_pnp_flag; +extern int gus_no_wave_dma; +static int gus_dma2 = -1; +static int dual_dma_mode = 0; +static long gus_mem_size = 0; +static long free_mem_ptr = 0; +static int gus_busy = 0; +static int gus_no_dma = 0; +static int nr_voices = 0; +static int gus_devnum = 0; +static int volume_base, volume_scale, volume_method; +static int gus_recmask = SOUND_MASK_MIC; +static int recording_active = 0; +static int only_read_access = 0; +static int only_8_bits = 0; + +int iw_mode = 0; +int gus_wave_volume = 60; +int gus_pcm_volume = 80; +int have_gus_max = 0; +static int gus_line_vol = 100, gus_mic_vol = 0; +static unsigned char mix_image = 0x00; + +int gus_timer_enabled = 0; + +/* + * Current version of this driver doesn't allow synth and PCM functions + * at the same time. The active_device specifies the active driver + */ + +static int active_device = 0; + +#define GUS_DEV_WAVE 1 /* Wave table synth */ +#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */ +#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */ + +static int gus_audio_speed; +static int gus_audio_channels; +static int gus_audio_bits; +static int gus_audio_bsize; +static char bounce_buf[8 * 1024]; /* Must match value set to max_fragment */ + +static DECLARE_WAIT_QUEUE_HEAD(dram_sleeper); + +/* + * Variables and buffers for PCM output + */ + +#define MAX_PCM_BUFFERS (128*MAX_REALTIME_FACTOR) /* Don't change */ + +static int pcm_bsize, pcm_nblk, pcm_banksize; +static int pcm_datasize[MAX_PCM_BUFFERS]; +static volatile int pcm_head, pcm_tail, pcm_qlen; +static volatile int pcm_active; +static volatile int dma_active; +static int pcm_opened = 0; +static int pcm_current_dev; +static int pcm_current_block; +static unsigned long pcm_current_buf; +static int pcm_current_count; +static int pcm_current_intrflag; + +extern int *gus_osp; + +static struct voice_info voices[32]; + +static int freq_div_table[] = +{ + 44100, /* 14 */ + 41160, /* 15 */ + 38587, /* 16 */ + 36317, /* 17 */ + 34300, /* 18 */ + 32494, /* 19 */ + 30870, /* 20 */ + 29400, /* 21 */ + 28063, /* 22 */ + 26843, /* 23 */ + 25725, /* 24 */ + 24696, /* 25 */ + 23746, /* 26 */ + 22866, /* 27 */ + 22050, /* 28 */ + 21289, /* 29 */ + 20580, /* 30 */ + 19916, /* 31 */ + 19293 /* 32 */ +}; + +static struct patch_info *samples = NULL; +static long sample_ptrs[MAX_SAMPLE + 1]; +static int sample_map[32]; +static int free_sample; +static int mixer_type = 0; + + +static int patch_table[MAX_PATCH]; +static int patch_map[32]; + +static struct synth_info gus_info = { + "Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, + 0, 16, 0, MAX_PATCH +}; + +static void gus_poke(long addr, unsigned char data); +static void compute_and_set_volume(int voice, int volume, int ramp_time); +extern unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev); +extern unsigned short gus_linear_vol(int vol, int mainvol); +static void compute_volume(int voice, int volume); +static void do_volume_irq(int voice); +static void set_input_volumes(void); +static void gus_tmr_install(int io_base); + +#define INSTANT_RAMP -1 /* Instant change. No ramping */ +#define FAST_RAMP 0 /* Fastest possible ramp */ + +static void reset_sample_memory(void) +{ + int i; + + for (i = 0; i <= MAX_SAMPLE; i++) + sample_ptrs[i] = -1; + for (i = 0; i < 32; i++) + sample_map[i] = -1; + for (i = 0; i < 32; i++) + patch_map[i] = -1; + + gus_poke(0, 0); /* Put a silent sample to the beginning */ + gus_poke(1, 0); + free_mem_ptr = 2; + + free_sample = 0; + + for (i = 0; i < MAX_PATCH; i++) + patch_table[i] = NOT_SAMPLE; +} + +void gus_delay(void) +{ + int i; + + for (i = 0; i < 7; i++) + inb(u_DRAMIO); +} + +static void gus_poke(long addr, unsigned char data) +{ /* Writes a byte to the DRAM */ + unsigned long flags; + + save_flags(flags); + cli(); + outb((0x43), u_Command); + outb((addr & 0xff), u_DataLo); + outb(((addr >> 8) & 0xff), u_DataHi); + + outb((0x44), u_Command); + outb(((addr >> 16) & 0xff), u_DataHi); + outb((data), u_DRAMIO); + restore_flags(flags); +} + +static unsigned char gus_peek(long addr) +{ /* Reads a byte from the DRAM */ + unsigned long flags; + unsigned char tmp; + + save_flags(flags); + cli(); + outb((0x43), u_Command); + outb((addr & 0xff), u_DataLo); + outb(((addr >> 8) & 0xff), u_DataHi); + + outb((0x44), u_Command); + outb(((addr >> 16) & 0xff), u_DataHi); + tmp = inb(u_DRAMIO); + restore_flags(flags); + + return tmp; +} + +void gus_write8(int reg, unsigned int data) +{ /* Writes to an indirect register (8 bit) */ + unsigned long flags; + + save_flags(flags); + cli(); + + outb((reg), u_Command); + outb(((unsigned char) (data & 0xff)), u_DataHi); + + restore_flags(flags); +} + +static unsigned char gus_read8(int reg) +{ + /* Reads from an indirect register (8 bit). Offset 0x80. */ + unsigned long flags; + unsigned char val; + + save_flags(flags); + cli(); + outb((reg | 0x80), u_Command); + val = inb(u_DataHi); + restore_flags(flags); + + return val; +} + +static unsigned char gus_look8(int reg) +{ + /* Reads from an indirect register (8 bit). No additional offset. */ + unsigned long flags; + unsigned char val; + + save_flags(flags); + cli(); + outb((reg), u_Command); + val = inb(u_DataHi); + restore_flags(flags); + + return val; +} + +static void gus_write16(int reg, unsigned int data) +{ + /* Writes to an indirect register (16 bit) */ + unsigned long flags; + + save_flags(flags); + cli(); + + outb((reg), u_Command); + + outb(((unsigned char) (data & 0xff)), u_DataLo); + outb(((unsigned char) ((data >> 8) & 0xff)), u_DataHi); + + restore_flags(flags); +} + +static unsigned short gus_read16(int reg) +{ + /* Reads from an indirect register (16 bit). Offset 0x80. */ + unsigned long flags; + unsigned char hi, lo; + + save_flags(flags); + cli(); + + outb((reg | 0x80), u_Command); + + lo = inb(u_DataLo); + hi = inb(u_DataHi); + + restore_flags(flags); + + return ((hi << 8) & 0xff00) | lo; +} + +static unsigned short gus_look16(int reg) +{ + /* Reads from an indirect register (16 bit). No additional offset. */ + unsigned long flags; + unsigned char hi, lo; + + save_flags(flags); + cli(); + + outb((reg), u_Command); + + lo = inb(u_DataLo); + hi = inb(u_DataHi); + + restore_flags(flags); + + return ((hi << 8) & 0xff00) | lo; +} + +static void gus_write_addr(int reg, unsigned long address, int frac, int is16bit) +{ + /* Writes an 24 bit memory address */ + unsigned long hold_address; + unsigned long flags; + + save_flags(flags); + cli(); + if (is16bit) + { + if (iw_mode) + { + /* Interwave spesific address translations */ + address >>= 1; + } + else + { + /* + * Special processing required for 16 bit patches + */ + + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + } + gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) + + (frac << 5)); + /* Could writing twice fix problems with GUS_VOICE_POS()? Let's try. */ + gus_delay(); + gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) + + (frac << 5)); + restore_flags(flags); +} + +static void gus_select_voice(int voice) +{ + if (voice < 0 || voice > 31) + return; + outb((voice), u_Voice); +} + +static void gus_select_max_voices(int nvoices) +{ + if (iw_mode) + nvoices = 32; + if (nvoices < 14) + nvoices = 14; + if (nvoices > 32) + nvoices = 32; + + voice_alloc->max_voice = nr_voices = nvoices; + gus_write8(0x0e, (nvoices - 1) | 0xc0); +} + +static void gus_voice_on(unsigned int mode) +{ + gus_write8(0x00, (unsigned char) (mode & 0xfc)); + gus_delay(); + gus_write8(0x00, (unsigned char) (mode & 0xfc)); +} + +static void gus_voice_off(void) +{ + gus_write8(0x00, gus_read8(0x00) | 0x03); +} + +static void gus_voice_mode(unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8(0x00, (gus_read8(0x00) & 0x03) | + (mode & 0xfc)); /* Don't touch last two bits */ + gus_delay(); + gus_write8(0x00, (gus_read8(0x00) & 0x03) | (mode & 0xfc)); +} + +static void gus_voice_freq(unsigned long freq) +{ + unsigned long divisor = freq_div_table[nr_voices - 14]; + unsigned short fc; + + /* Interwave plays at 44100 Hz with any number of voices */ + if (iw_mode) + fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100); + else + fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); + fc = fc << 1; + + gus_write16(0x01, fc); +} + +static void gus_voice_volume(unsigned int vol) +{ + gus_write8(0x0d, 0x03); /* Stop ramp before setting volume */ + gus_write16(0x09, (unsigned short) (vol << 4)); +} + +static void gus_voice_balance(unsigned int balance) +{ + gus_write8(0x0c, (unsigned char) (balance & 0xff)); +} + +static void gus_ramp_range(unsigned int low, unsigned int high) +{ + gus_write8(0x07, (unsigned char) ((low >> 4) & 0xff)); + gus_write8(0x08, (unsigned char) ((high >> 4) & 0xff)); +} + +static void gus_ramp_rate(unsigned int scale, unsigned int rate) +{ + gus_write8(0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); +} + +static void gus_rampon(unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8(0x0d, mode & 0xfc); + gus_delay(); + gus_write8(0x0d, mode & 0xfc); +} + +static void gus_ramp_mode(unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | + (mode & 0xfc)); /* Leave the last 2 bits alone */ + gus_delay(); + gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | (mode & 0xfc)); +} + +static void gus_rampoff(void) +{ + gus_write8(0x0d, 0x03); +} + +static void gus_set_voice_pos(int voice, long position) +{ + int sample_no; + + if ((sample_no = sample_map[voice]) != -1) { + if (position < samples[sample_no].len) { + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + voices[voice].offset_pending = position; + else + gus_write_addr(0x0a, sample_ptrs[sample_no] + position, 0, + samples[sample_no].mode & WAVE_16_BITS); + } + } +} + +static void gus_voice_init(int voice) +{ + unsigned long flags; + + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_volume(0); + gus_voice_off(); + gus_write_addr(0x0a, 0, 0, 0); /* Set current position to 0 */ + gus_write8(0x00, 0x03); /* Voice off */ + gus_write8(0x0d, 0x03); /* Ramping off */ + voice_alloc->map[voice] = 0; + voice_alloc->alloc_times[voice] = 0; + restore_flags(flags); + +} + +static void gus_voice_init2(int voice) +{ + voices[voice].panning = 0; + voices[voice].mode = 0; + voices[voice].orig_freq = 20000; + voices[voice].current_freq = 20000; + voices[voice].bender = 0; + voices[voice].bender_range = 200; + voices[voice].initial_volume = 0; + voices[voice].current_volume = 0; + voices[voice].loop_irq_mode = 0; + voices[voice].loop_irq_parm = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].volume_irq_parm = 0; + voices[voice].env_phase = 0; + voices[voice].main_vol = 127; + voices[voice].patch_vol = 127; + voices[voice].expression_vol = 127; + voices[voice].sample_pending = -1; + voices[voice].fixed_pitch = 0; +} + +static void step_envelope(int voice) +{ + unsigned vol, prev_vol, phase; + unsigned char rate; + long int flags; + + if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) + { + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_rampoff(); + restore_flags(flags); + return; + /* + * Sustain phase begins. Continue envelope after receiving note off. + */ + } + if (voices[voice].env_phase >= 5) + { + /* Envelope finished. Shoot the voice down */ + gus_voice_init(voice); + return; + } + prev_vol = voices[voice].current_volume; + phase = ++voices[voice].env_phase; + compute_volume(voice, voices[voice].midi_volume); + vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; + rate = voices[voice].env_rate[phase]; + + save_flags(flags); + cli(); + gus_select_voice(voice); + + gus_voice_volume(prev_vol); + + + gus_write8(0x06, rate); /* Ramping rate */ + + voices[voice].volume_irq_mode = VMODE_ENVELOPE; + + if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ + { + restore_flags(flags); + step_envelope(voice); /* Continue the envelope on the next step */ + return; + } + if (vol > prev_vol) + { + if (vol >= (4096 - 64)) + vol = 4096 - 65; + gus_ramp_range(0, vol); + gus_rampon(0x20); /* Increasing volume, with IRQ */ + } + else + { + if (vol <= 64) + vol = 65; + gus_ramp_range(vol, 4030); + gus_rampon(0x60); /* Decreasing volume, with IRQ */ + } + voices[voice].current_volume = vol; + restore_flags(flags); +} + +static void init_envelope(int voice) +{ + voices[voice].env_phase = -1; + voices[voice].current_volume = 64; + + step_envelope(voice); +} + +static void start_release(int voice, long int flags) +{ + if (gus_read8(0x00) & 0x03) + return; /* Voice already stopped */ + + voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ + + voices[voice].current_volume = voices[voice].initial_volume = + gus_read16(0x09) >> 4; /* Get current volume */ + + voices[voice].mode &= ~WAVE_SUSTAIN_ON; + gus_rampoff(); + restore_flags(flags); + step_envelope(voice); +} + +static void gus_voice_fade(int voice) +{ + int instr_no = sample_map[voice], is16bits; + long int flags; + + save_flags(flags); + cli(); + gus_select_voice(voice); + + if (instr_no < 0 || instr_no > MAX_SAMPLE) + { + gus_write8(0x00, 0x03); /* Hard stop */ + voice_alloc->map[voice] = 0; + restore_flags(flags); + return; + } + is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */ + + if (voices[voice].mode & WAVE_ENVELOPES) + { + start_release(voice, flags); + restore_flags(flags); + return; + } + /* + * Ramp the volume down but not too quickly. + */ + if ((int) (gus_read16(0x09) >> 4) < 100) /* Get current volume */ + { + gus_voice_off(); + gus_rampoff(); + gus_voice_init(voice); + restore_flags(flags); + return; + } + gus_ramp_range(65, 4030); + gus_ramp_rate(2, 4); + gus_rampon(0x40 | 0x20); /* Down, once, with IRQ */ + voices[voice].volume_irq_mode = VMODE_HALT; + restore_flags(flags); +} + +static void gus_reset(void) +{ + int i; + + gus_select_max_voices(24); + volume_base = 3071; + volume_scale = 4; + volume_method = VOL_METHOD_ADAGIO; + + for (i = 0; i < 32; i++) + { + gus_voice_init(i); /* Turn voice off */ + gus_voice_init2(i); + } +} + +static void gus_initialize(void) +{ + unsigned long flags; + unsigned char dma_image, irq_image, tmp; + + static unsigned char gus_irq_map[16] = { + 0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7 + }; + + static unsigned char gus_dma_map[8] = { + 0, 1, 0, 2, 0, 3, 4, 5 + }; + + save_flags(flags); + cli(); + gus_write8(0x4c, 0); /* Reset GF1 */ + gus_delay(); + gus_delay(); + + gus_write8(0x4c, 1); /* Release Reset */ + gus_delay(); + gus_delay(); + + /* + * Clear all interrupts + */ + + gus_write8(0x41, 0); /* DMA control */ + gus_write8(0x45, 0); /* Timer control */ + gus_write8(0x49, 0); /* Sample control */ + + gus_select_max_voices(24); + + inb(u_Status); /* Touch the status register */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ + + gus_reset(); /* Resets all voices */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ + + gus_write8(0x4c, 7); /* Master reset | DAC enable | IRQ enable */ + + /* + * Set up for Digital ASIC + */ + + outb((0x05), gus_base + 0x0f); + + mix_image |= 0x02; /* Disable line out (for a moment) */ + outb((mix_image), u_Mixer); + + outb((0x00), u_IRQDMAControl); + + outb((0x00), gus_base + 0x0f); + + /* + * Now set up the DMA and IRQ interface + * + * The GUS supports two IRQs and two DMAs. + * + * Just one DMA channel is used. This prevents simultaneous ADC and DAC. + * Adding this support requires significant changes to the dmabuf.c, dsp.c + * and audio.c also. + */ + + irq_image = 0; + tmp = gus_irq_map[gus_irq]; + if (!gus_pnp_flag && !tmp) + printk(KERN_WARNING "Warning! GUS IRQ not selected\n"); + irq_image |= tmp; + irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ + + dual_dma_mode = 1; + if (gus_dma2 == gus_dma || gus_dma2 == -1) + { + dual_dma_mode = 0; + dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */ + + tmp = gus_dma_map[gus_dma]; + if (!tmp) + printk(KERN_WARNING "Warning! GUS DMA not selected\n"); + + dma_image |= tmp; + } + else + { + /* Setup dual DMA channel mode for GUS MAX */ + + dma_image = gus_dma_map[gus_dma]; + if (!dma_image) + printk(KERN_WARNING "Warning! GUS DMA not selected\n"); + + tmp = gus_dma_map[gus_dma2] << 3; + if (!tmp) + { + printk(KERN_WARNING "Warning! Invalid GUS MAX DMA\n"); + tmp = 0x40; /* Combine DMA channels */ + dual_dma_mode = 0; + } + dma_image |= tmp; + } + + /* + * For some reason the IRQ and DMA addresses must be written twice + */ + + /* + * Doing it first time + */ + + outb((mix_image), u_Mixer); /* Select DMA control */ + outb((dma_image | 0x80), u_IRQDMAControl); /* Set DMA address */ + + outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ + outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ + + /* + * Doing it second time + */ + + outb((mix_image), u_Mixer); /* Select DMA control */ + outb((dma_image), u_IRQDMAControl); /* Set DMA address */ + + outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ + outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ + + gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ + + mix_image &= ~0x02; /* Enable line out */ + mix_image |= 0x08; /* Enable IRQ */ + outb((mix_image), u_Mixer); /* + * Turn mixer channels on + * Note! Mic in is left off. + */ + + gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ + + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ + + inb(u_Status); /* Touch the status register */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + + gus_read8(0x0f); /* Clear pending IRQs */ + + if (iw_mode) + gus_write8(0x19, gus_read8(0x19) | 0x01); + restore_flags(flags); +} + + +static void __init pnp_mem_init(void) +{ +#include "iwmem.h" +#define CHUNK_SIZE (256*1024) +#define BANK_SIZE (4*1024*1024) +#define CHUNKS_PER_BANK (BANK_SIZE/CHUNK_SIZE) + + int bank, chunk, addr, total = 0; + int bank_sizes[4]; + int i, j, bits = -1, testbits = -1, nbanks = 0; + + /* + * This routine determines what kind of RAM is installed in each of the four + * SIMM banks and configures the DRAM address decode logic accordingly. + */ + + /* + * Place the chip into enhanced mode + */ + gus_write8(0x19, gus_read8(0x19) | 0x01); + gus_write8(0x53, gus_look8(0x53) & ~0x02); /* Select DRAM I/O access */ + + /* + * Set memory configuration to 4 DRAM banks of 4M in each (16M total). + */ + + gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | 0x000c); + + /* + * Perform the DRAM size detection for each bank individually. + */ + for (bank = 0; bank < 4; bank++) + { + int size = 0; + + addr = bank * BANK_SIZE; + + /* Clean check points of each chunk */ + for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) + { + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); + } + + /* Write a value to each chunk point and verify the result */ + for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) + { + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x55); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0xAA); + + if (gus_peek(addr + chunk * CHUNK_SIZE + 0L) == 0x55 && + gus_peek(addr + chunk * CHUNK_SIZE + 1L) == 0xAA) + { + /* OK. There is RAM. Now check for possible shadows */ + int ok = 1, chunk2; + + for (chunk2 = 0; ok && chunk2 < chunk; chunk2++) + if (gus_peek(addr + chunk2 * CHUNK_SIZE + 0L) || + gus_peek(addr + chunk2 * CHUNK_SIZE + 1L)) + ok = 0; /* Addressing wraps */ + + if (ok) + size = (chunk + 1) * CHUNK_SIZE; + } + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); + } + bank_sizes[bank] = size; + if (size) + nbanks = bank + 1; + DDB(printk("Interwave: Bank %d, size=%dk\n", bank, size / 1024)); + } + + if (nbanks == 0) /* No RAM - Give up */ + { + printk(KERN_ERR "Sound: An Interwave audio chip detected but no DRAM\n"); + printk(KERN_ERR "Sound: Unable to work with this card.\n"); + gus_write8(0x19, gus_read8(0x19) & ~0x01); + gus_mem_size = 0; + return; + } + + /* + * Now we know how much DRAM there is in each bank. The next step is + * to find a DRAM size encoding (0 to 12) which is best for the combination + * we have. + * + * First try if any of the possible alternatives matches exactly the amount + * of memory we have. + */ + + for (i = 0; bits == -1 && i < 13; i++) + { + bits = i; + + for (j = 0; bits != -1 && j < 4; j++) + if (mem_decode[i][j] != bank_sizes[j]) + bits = -1; /* No hit */ + } + + /* + * If necessary, try to find a combination where other than the last + * bank matches our configuration and the last bank is left oversized. + * In this way we don't leave holes in the middle of memory. + */ + + if (bits == -1) /* No luck yet */ + { + for (i = 0; bits == -1 && i < 13; i++) + { + bits = i; + + for (j = 0; bits != -1 && j < nbanks - 1; j++) + if (mem_decode[i][j] != bank_sizes[j]) + bits = -1; /* No hit */ + if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1]) + bits = -1; /* The last bank is too small */ + } + } + /* + * The last resort is to search for a combination where the banks are + * smaller than the actual SIMMs. This leaves some memory in the banks + * unused but doesn't leave holes in the DRAM address space. + */ + if (bits == -1) /* No luck yet */ + { + for (i = 0; i < 13; i++) + { + testbits = i; + for (j = 0; testbits != -1 && j < nbanks - 1; j++) + if (mem_decode[i][j] > bank_sizes[j]) { + testbits = -1; + } + if(testbits > bits) bits = testbits; + } + if (bits != -1) + { + printk(KERN_INFO "Interwave: Can't use all installed RAM.\n"); + printk(KERN_INFO "Interwave: Try reordering SIMMS.\n"); + } + printk(KERN_INFO "Interwave: Can't find working DRAM encoding.\n"); + printk(KERN_INFO "Interwave: Defaulting to 256k. Try reordering SIMMS.\n"); + bits = 0; + } + DDB(printk("Interwave: Selecting DRAM addressing mode %d\n", bits)); + + for (bank = 0; bank < 4; bank++) + { + DDB(printk(" Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024)); + + if (bank_sizes[bank] > mem_decode[bits][bank]) + total += mem_decode[bits][bank]; + else + total += bank_sizes[bank]; + } + + DDB(printk("Total %dk of DRAM (enhanced mode)\n", total / 1024)); + + /* + * Set the memory addressing mode. + */ + gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | bits); + +/* Leave the chip into enhanced mode. Disable LFO */ + gus_mem_size = total; + iw_mode = 1; + gus_write8(0x19, (gus_read8(0x19) | 0x01) & ~0x02); +} + +int __init gus_wave_detect(int baseaddr) +{ + unsigned long i, max_mem = 1024L; + unsigned long loc; + unsigned char val; + + gus_base = baseaddr; + + gus_write8(0x4c, 0); /* Reset GF1 */ + gus_delay(); + gus_delay(); + + gus_write8(0x4c, 1); /* Release Reset */ + gus_delay(); + gus_delay(); + +#ifdef GUSPNP_AUTODETECT + val = gus_look8(0x5b); /* Version number register */ + gus_write8(0x5b, ~val); /* Invert all bits */ + + if ((gus_look8(0x5b) & 0xf0) == (val & 0xf0)) /* No change */ + { + if ((gus_look8(0x5b) & 0x0f) == ((~val) & 0x0f)) /* Change */ + { + DDB(printk("Interwave chip version %d detected\n", (val & 0xf0) >> 4)); + gus_pnp_flag = 1; + } + else + { + DDB(printk("Not an Interwave chip (%x)\n", gus_look8(0x5b))); + gus_pnp_flag = 0; + } + } + gus_write8(0x5b, val); /* Restore all bits */ +#endif + + if (gus_pnp_flag) + pnp_mem_init(); + if (iw_mode) + return 1; + + /* See if there is first block there.... */ + gus_poke(0L, 0xaa); + if (gus_peek(0L) != 0xaa) + return (0); + + /* Now zero it out so that I can check for mirroring .. */ + gus_poke(0L, 0x00); + for (i = 1L; i < max_mem; i++) + { + int n, failed; + + /* check for mirroring ... */ + if (gus_peek(0L) != 0) + break; + loc = i << 10; + + for (n = loc - 1, failed = 0; n <= loc; n++) + { + gus_poke(loc, 0xaa); + if (gus_peek(loc) != 0xaa) + failed = 1; + gus_poke(loc, 0x55); + if (gus_peek(loc) != 0x55) + failed = 1; + } + if (failed) + break; + } + gus_mem_size = i << 10; + return 1; +} + +static int guswave_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + + switch (cmd) + { + case SNDCTL_SYNTH_INFO: + gus_info.nr_voices = nr_voices; + if (copy_to_user(arg, &gus_info, sizeof(gus_info))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_RESETSAMPLES: + reset_sample_memory(); + return 0; + + case SNDCTL_SEQ_PERCMODE: + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32; + + default: + return -EINVAL; + } +} + +static int guswave_set_instr(int dev, int voice, int instr_no) +{ + int sample_no; + + if (instr_no < 0 || instr_no > MAX_PATCH) + instr_no = 0; /* Default to acoustic piano */ + + if (voice < 0 || voice > 31) + return -EINVAL; + + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].sample_pending = instr_no; + return 0; + } + sample_no = patch_table[instr_no]; + patch_map[voice] = -1; + + if (sample_no == NOT_SAMPLE) + { +/* printk("GUS: Undefined patch %d for voice %d\n", instr_no, voice);*/ + return -EINVAL; /* Patch not defined */ + } + if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ + { +/* printk("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);*/ + return -EINVAL; + } + sample_map[voice] = sample_no; + patch_map[voice] = instr_no; + return 0; +} + +static int guswave_kill_note(int dev, int voice, int note, int velocity) +{ + unsigned long flags; + + save_flags(flags); + cli(); + /* voice_alloc->map[voice] = 0xffff; */ + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].kill_pending = 1; + restore_flags(flags); + } + else + { + restore_flags(flags); + gus_voice_fade(voice); + } + + return 0; +} + +static void guswave_aftertouch(int dev, int voice, int pressure) +{ +} + +static void guswave_panning(int dev, int voice, int value) +{ + if (voice >= 0 || voice < 32) + voices[voice].panning = value; +} + +static void guswave_volume_method(int dev, int mode) +{ + if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) + volume_method = mode; +} + +static void compute_volume(int voice, int volume) +{ + if (volume < 128) + voices[voice].midi_volume = volume; + + switch (volume_method) + { + case VOL_METHOD_ADAGIO: + voices[voice].initial_volume = + gus_adagio_vol(voices[voice].midi_volume, voices[voice].main_vol, + voices[voice].expression_vol, + voices[voice].patch_vol); + break; + + case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ + voices[voice].initial_volume = gus_linear_vol(volume, voices[voice].main_vol); + break; + + default: + voices[voice].initial_volume = volume_base + + (voices[voice].midi_volume * volume_scale); + } + + if (voices[voice].initial_volume > 4030) + voices[voice].initial_volume = 4030; +} + +static void compute_and_set_volume(int voice, int volume, int ramp_time) +{ + int curr, target, rate; + unsigned long flags; + + compute_volume(voice, volume); + voices[voice].current_volume = voices[voice].initial_volume; + + save_flags(flags); + cli(); + /* + * CAUTION! Interrupts disabled. Enable them before returning + */ + + gus_select_voice(voice); + + curr = gus_read16(0x09) >> 4; + target = voices[voice].initial_volume; + + if (ramp_time == INSTANT_RAMP) + { + gus_rampoff(); + gus_voice_volume(target); + restore_flags(flags); + return; + } + if (ramp_time == FAST_RAMP) + rate = 63; + else + rate = 16; + gus_ramp_rate(0, rate); + + if ((target - curr) / 64 == 0) /* Close enough to target. */ + { + gus_rampoff(); + gus_voice_volume(target); + restore_flags(flags); + return; + } + if (target > curr) + { + if (target > (4095 - 65)) + target = 4095 - 65; + gus_ramp_range(curr, target); + gus_rampon(0x00); /* Ramp up, once, no IRQ */ + } + else + { + if (target < 65) + target = 65; + + gus_ramp_range(target, curr); + gus_rampon(0x40); /* Ramp down, once, no irq */ + } + restore_flags(flags); +} + +static void dynamic_volume_change(int voice) +{ + unsigned char status; + unsigned long flags; + + save_flags(flags); + cli(); + gus_select_voice(voice); + status = gus_read8(0x00); /* Get voice status */ + restore_flags(flags); + + if (status & 0x03) + return; /* Voice was not running */ + + if (!(voices[voice].mode & WAVE_ENVELOPES)) + { + compute_and_set_volume(voice, voices[voice].midi_volume, 1); + return; + } + + /* + * Voice is running and has envelopes. + */ + + save_flags(flags); + cli(); + gus_select_voice(voice); + status = gus_read8(0x0d); /* Ramping status */ + restore_flags(flags); + + if (status & 0x03) /* Sustain phase? */ + { + compute_and_set_volume(voice, voices[voice].midi_volume, 1); + return; + } + if (voices[voice].env_phase < 0) + return; + + compute_volume(voice, voices[voice].midi_volume); + +} + +static void guswave_controller(int dev, int voice, int ctrl_num, int value) +{ + unsigned long flags; + unsigned long freq; + + if (voice < 0 || voice > 31) + return; + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + voices[voice].bender = value; + + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + { + freq = compute_finetune(voices[voice].orig_freq, value, voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_freq(freq); + restore_flags(flags); + } + break; + + case CTRL_PITCH_BENDER_RANGE: + voices[voice].bender_range = value; + break; + case CTL_EXPRESSION: + value /= 128; + case CTRL_EXPRESSION: + if (volume_method == VOL_METHOD_ADAGIO) + { + voices[voice].expression_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change(voice); + } + break; + + case CTL_PAN: + voices[voice].panning = (value * 2) - 128; + break; + + case CTL_MAIN_VOLUME: + value = (value * 100) / 16383; + + case CTRL_MAIN_VOLUME: + voices[voice].main_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change(voice); + break; + + default: + break; + } +} + +static int guswave_start_note2(int dev, int voice, int note_num, int volume) +{ + int sample, best_sample, best_delta, delta_freq; + int is16bits, samplep, patch, pan; + unsigned long note_freq, base_note, freq, flags; + unsigned char mode = 0; + + if (voice < 0 || voice > 31) + { +/* printk("GUS: Invalid voice\n");*/ + return -EINVAL; + } + if (note_num == 255) + { + if (voices[voice].mode & WAVE_ENVELOPES) + { + voices[voice].midi_volume = volume; + dynamic_volume_change(voice); + return 0; + } + compute_and_set_volume(voice, volume, 1); + return 0; + } + if ((patch = patch_map[voice]) == -1) + return -EINVAL; + if ((samplep = patch_table[patch]) == NOT_SAMPLE) + { + return -EINVAL; + } + note_freq = note_to_freq(note_num); + + /* + * Find a sample within a patch so that the note_freq is between low_note + * and high_note. + */ + sample = -1; + + best_sample = samplep; + best_delta = 1000000; + while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1) + { + delta_freq = note_freq - samples[samplep].base_note; + if (delta_freq < 0) + delta_freq = -delta_freq; + if (delta_freq < best_delta) + { + best_sample = samplep; + best_delta = delta_freq; + } + if (samples[samplep].low_note <= note_freq && + note_freq <= samples[samplep].high_note) + { + sample = samplep; + } + else + samplep = samples[samplep].key; /* Link to next sample */ + } + if (sample == -1) + sample = best_sample; + + if (sample == -1) + { +/* printk("GUS: Patch %d not defined for note %d\n", patch, note_num);*/ + return 0; /* Should play default patch ??? */ + } + is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; + voices[voice].mode = samples[sample].mode; + voices[voice].patch_vol = samples[sample].volume; + + if (iw_mode) + gus_write8(0x15, 0x00); /* RAM, Reset voice deactivate bit of SMSI */ + + if (voices[voice].mode & WAVE_ENVELOPES) + { + int i; + + for (i = 0; i < 6; i++) + { + voices[voice].env_rate[i] = samples[sample].env_rate[i]; + voices[voice].env_offset[i] = samples[sample].env_offset[i]; + } + } + sample_map[voice] = sample; + + if (voices[voice].fixed_pitch) /* Fixed pitch */ + { + freq = samples[sample].base_freq; + } + else + { + base_note = samples[sample].base_note / 100; + note_freq /= 100; + + freq = samples[sample].base_freq * note_freq / base_note; + } + + voices[voice].orig_freq = freq; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + freq = compute_finetune(voices[voice].orig_freq, voices[voice].bender, + voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + pan = (samples[sample].panning + voices[voice].panning) / 32; + pan += 7; + if (pan < 0) + pan = 0; + if (pan > 15) + pan = 15; + + if (samples[sample].mode & WAVE_16_BITS) + { + mode |= 0x04; /* 16 bits */ + if ((sample_ptrs[sample] / GUS_BANK_SIZE) != + ((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE)) + printk(KERN_ERR "GUS: Sample address error\n"); + } + /************************************************************************* + * CAUTION! Interrupts disabled. Don't return before enabling + *************************************************************************/ + + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_off(); + gus_rampoff(); + + restore_flags(flags); + + if (voices[voice].mode & WAVE_ENVELOPES) + { + compute_volume(voice, volume); + init_envelope(voice); + } + else + { + compute_and_set_volume(voice, volume, 0); + } + + save_flags(flags); + cli(); + gus_select_voice(voice); + + if (samples[sample].mode & WAVE_LOOP_BACK) + gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].len - + voices[voice].offset_pending, 0, is16bits); /* start=end */ + else + gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending, 0, is16bits); /* Sample start=begin */ + + if (samples[sample].mode & WAVE_LOOPING) + { + mode |= 0x08; + + if (samples[sample].mode & WAVE_BIDIR_LOOP) + mode |= 0x10; + + if (samples[sample].mode & WAVE_LOOP_BACK) + { + gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].loop_end - + voices[voice].offset_pending, + (samples[sample].fractions >> 4) & 0x0f, is16bits); + mode |= 0x40; + } + gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start, + samples[sample].fractions & 0x0f, is16bits); /* Loop start location */ + gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end, + (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ + } + else + { + mode |= 0x20; /* Loop IRQ at the end */ + voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */ + voices[voice].loop_irq_parm = 1; + gus_write_addr(0x02, sample_ptrs[sample], 0, is16bits); /* Loop start location */ + gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1, + (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ + } + gus_voice_freq(freq); + gus_voice_balance(pan); + gus_voice_on(mode); + restore_flags(flags); + + return 0; +} + +/* + * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking + * when the note playing on the voice is changed. It uses volume + * ramping. + */ + +static int guswave_start_note(int dev, int voice, int note_num, int volume) +{ + long int flags; + int mode; + int ret_val = 0; + + save_flags(flags); + cli(); + if (note_num == 255) + { + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].volume_pending = volume; + } + else + { + ret_val = guswave_start_note2(dev, voice, note_num, volume); + } + } + else + { + gus_select_voice(voice); + mode = gus_read8(0x00); + if (mode & 0x20) + gus_write8(0x00, mode & 0xdf); /* No interrupt! */ + + voices[voice].offset_pending = 0; + voices[voice].kill_pending = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].loop_irq_mode = 0; + + if (voices[voice].sample_pending >= 0) + { + restore_flags(flags); /* Run temporarily with interrupts enabled */ + guswave_set_instr(voices[voice].dev_pending, voice, voices[voice].sample_pending); + voices[voice].sample_pending = -1; + save_flags(flags); + cli(); + gus_select_voice(voice); /* Reselect the voice (just to be sure) */ + } + if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < (unsigned) 2065)) + { + ret_val = guswave_start_note2(dev, voice, note_num, volume); + } + else + { + voices[voice].dev_pending = dev; + voices[voice].note_pending = note_num; + voices[voice].volume_pending = volume; + voices[voice].volume_irq_mode = VMODE_START_NOTE; + + gus_rampoff(); + gus_ramp_range(2000, 4065); + gus_ramp_rate(0, 63); /* Fastest possible rate */ + gus_rampon(0x20 | 0x40); /* Ramp down, once, irq */ + } + } + restore_flags(flags); + return ret_val; +} + +static void guswave_reset(int dev) +{ + int i; + + for (i = 0; i < 32; i++) + { + gus_voice_init(i); + gus_voice_init2(i); + } +} + +static int guswave_open(int dev, int mode) +{ + int err; + + if (gus_busy) + return -EBUSY; + + voice_alloc->timestamp = 0; + + if (gus_no_wave_dma) { + gus_no_dma = 1; + } else { + if ((err = DMAbuf_open_dma(gus_devnum)) < 0) + { + /* printk( "GUS: Loading samples without DMA\n"); */ + gus_no_dma = 1; /* Upload samples using PIO */ + } + else + gus_no_dma = 0; + } + + init_waitqueue_head(&dram_sleeper); + gus_busy = 1; + active_device = GUS_DEV_WAVE; + + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ + gus_initialize(); + gus_reset(); + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ + + return 0; +} + +static void guswave_close(int dev) +{ + gus_busy = 0; + active_device = 0; + gus_reset(); + + if (!gus_no_dma) + DMAbuf_close_dma(gus_devnum); +} + +static int guswave_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info patch; + int instr; + long sizeof_patch; + + unsigned long blk_sz, blk_end, left, src_offs, target; + + sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */ + + if (format != GUS_PATCH) + { +/* printk("GUS Error: Invalid patch format (key) 0x%x\n", format);*/ + return -EINVAL; + } + if (count < sizeof_patch) + { +/* printk("GUS Error: Patch header too short\n");*/ + return -EINVAL; + } + count -= sizeof_patch; + + if (free_sample >= MAX_SAMPLE) + { +/* printk("GUS: Sample table full\n");*/ + return -ENOSPC; + } + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + copy_from_user(&((char *) &patch)[offs], &(addr)[offs], sizeof_patch - offs); + + if (patch.mode & WAVE_ROM) + return -EINVAL; + if (gus_mem_size == 0) + return -ENOSPC; + + instr = patch.instr_no; + + if (instr < 0 || instr > MAX_PATCH) + { +/* printk(KERN_ERR "GUS: Invalid patch number %d\n", instr);*/ + return -EINVAL; + } + if (count < patch.len) + { +/* printk(KERN_ERR "GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len);*/ + patch.len = count; + } + if (patch.len <= 0 || patch.len > gus_mem_size) + { +/* printk(KERN_ERR "GUS: Invalid sample length %d\n", (int) patch.len);*/ + return -EINVAL; + } + if (patch.mode & WAVE_LOOPING) + { + if (patch.loop_start < 0 || patch.loop_start >= patch.len) + { +/* printk(KERN_ERR "GUS: Invalid loop start\n");*/ + return -EINVAL; + } + if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) + { +/* printk(KERN_ERR "GUS: Invalid loop end\n");*/ + return -EINVAL; + } + } + free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */ + + if (patch.mode & WAVE_16_BITS) + { + /* + * 16 bit samples must fit one 256k bank. + */ + if (patch.len >= GUS_BANK_SIZE) + { +/* printk("GUS: Sample (16 bit) too long %d\n", (int) patch.len);*/ + return -ENOSPC; + } + if ((free_mem_ptr / GUS_BANK_SIZE) != + ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) + { + unsigned long tmp_mem = + /* Align to 256K */ + ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; + + if ((tmp_mem + patch.len) > gus_mem_size) + return -ENOSPC; + + free_mem_ptr = tmp_mem; /* This leaves unusable memory */ + } + } + if ((free_mem_ptr + patch.len) > gus_mem_size) + return -ENOSPC; + + sample_ptrs[free_sample] = free_mem_ptr; + + /* + * Tremolo is not possible with envelopes + */ + + if (patch.mode & WAVE_ENVELOPES) + patch.mode &= ~WAVE_TREMOLO; + + if (!(patch.mode & WAVE_FRACTIONS)) + { + patch.fractions = 0; + } + memcpy((char *) &samples[free_sample], &patch, sizeof_patch); + + /* + * Link this_one sample to the list of samples for patch 'instr'. + */ + + samples[free_sample].key = patch_table[instr]; + patch_table[instr] = free_sample; + + /* + * Use DMA to transfer the wave data to the DRAM + */ + + left = patch.len; + src_offs = 0; + target = free_mem_ptr; + + while (left) /* Not completely transferred yet */ + { + blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use; + if (blk_sz > left) + blk_sz = left; + + /* + * DMA cannot cross bank (256k) boundaries. Check for that. + */ + + blk_end = target + blk_sz; + + if ((target / GUS_BANK_SIZE) != (blk_end / GUS_BANK_SIZE)) + { + /* Split the block */ + blk_end &= ~(GUS_BANK_SIZE - 1); + blk_sz = blk_end - target; + } + if (gus_no_dma) + { + /* + * For some reason the DMA is not possible. We have to use PIO. + */ + long i; + unsigned char data; + + for (i = 0; i < blk_sz; i++) + { + get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[sizeof_patch + i])); + if (patch.mode & WAVE_UNSIGNED) + if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) + data ^= 0x80; /* Convert to signed */ + gus_poke(target + i, data); + } + } + else + { + unsigned long address, hold_address; + unsigned char dma_command; + unsigned long flags; + + if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL) + { + printk(KERN_ERR "GUS: DMA buffer == NULL\n"); + return -ENOSPC; + } + /* + * OK, move now. First in and then out. + */ + + copy_from_user(audio_devs[gus_devnum]->dmap_out->raw_buf, &(addr)[sizeof_patch + src_offs], blk_sz); + + save_flags(flags); + cli(); + /******** INTERRUPTS DISABLED NOW ********/ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma(gus_devnum, audio_devs[gus_devnum]->dmap_out->raw_buf_phys, + blk_sz, DMA_MODE_WRITE); + + /* + * Set the DRAM address for the wave data + */ + + if (iw_mode) + { + /* Different address translation in enhanced mode */ + + unsigned char hi; + + if (gus_dma > 4) + address = target >> 1; /* Convert to 16 bit word address */ + else + address = target; + + hi = (unsigned char) ((address >> 16) & 0xf0); + hi += (unsigned char) (address & 0x0f); + + gus_write16(0x42, (address >> 4) & 0xffff); /* DMA address (low) */ + gus_write8(0x50, hi); + } + else + { + address = target; + if (audio_devs[gus_devnum]->dmap_out->dma > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + } + + /* + * Start the DMA transfer + */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + if (patch.mode & WAVE_UNSIGNED) + dma_command |= 0x80; /* Invert MSB */ + if (patch.mode & WAVE_16_BITS) + dma_command |= 0x40; /* 16 bit _DATA_ */ + if (audio_devs[gus_devnum]->dmap_out->dma > 3) + dma_command |= 0x04; /* 16 bit DMA _channel_ */ + + gus_write8(0x41, dma_command); /* Lets go luteet (=bugs) */ + + /* + * Sleep here until the DRAM DMA done interrupt is served + */ + active_device = GUS_DEV_WAVE; + + if (!interruptible_sleep_on_timeout(&dram_sleeper, HZ)) + printk("GUS: DMA Transfer timed out\n"); + restore_flags(flags); + } + + /* + * Now the next part + */ + + left -= blk_sz; + src_offs += blk_sz; + target += blk_sz; + + gus_write8(0x41, 0); /* Stop DMA */ + } + + free_mem_ptr += patch.len; + free_sample++; + return 0; +} + +static void guswave_hw_control(int dev, unsigned char *event_rec) +{ + int voice, cmd; + unsigned short p1, p2; + unsigned int plong; + unsigned long flags; + + cmd = event_rec[2]; + voice = event_rec[3]; + p1 = *(unsigned short *) &event_rec[4]; + p2 = *(unsigned short *) &event_rec[6]; + plong = *(unsigned int *) &event_rec[4]; + + if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && + (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) + do_volume_irq(voice); + + switch (cmd) + { + case _GUS_NUMVOICES: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_select_max_voices(p1); + restore_flags(flags); + break; + + case _GUS_VOICESAMPLE: + guswave_set_instr(dev, voice, p1); + break; + + case _GUS_VOICEON: + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_on(p1); + restore_flags(flags); + break; + + case _GUS_VOICEOFF: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_off(); + restore_flags(flags); + break; + + case _GUS_VOICEFADE: + gus_voice_fade(voice); + break; + + case _GUS_VOICEMODE: + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_mode(p1); + restore_flags(flags); + break; + + case _GUS_VOICEBALA: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_balance(p1); + restore_flags(flags); + break; + + case _GUS_VOICEFREQ: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_freq(plong); + restore_flags(flags); + break; + + case _GUS_VOICEVOL: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_volume(p1); + restore_flags(flags); + break; + + case _GUS_VOICEVOL2: /* Just update the software voice level */ + voices[voice].initial_volume = voices[voice].current_volume = p1; + break; + + case _GUS_RAMPRANGE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_ramp_range(p1, p2); + restore_flags(flags); + break; + + case _GUS_RAMPRATE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NJET-NJET */ + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_ramp_rate(p1, p2); + restore_flags(flags); + break; + + case _GUS_RAMPMODE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_ramp_mode(p1); + restore_flags(flags); + break; + + case _GUS_RAMPON: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* EI-EI */ + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_rampon(p1); + restore_flags(flags); + break; + + case _GUS_RAMPOFF: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NEJ-NEJ */ + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_rampoff(); + restore_flags(flags); + break; + + case _GUS_VOLUME_SCALE: + volume_base = p1; + volume_scale = p2; + break; + + case _GUS_VOICE_POS: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_set_voice_pos(voice, plong); + restore_flags(flags); + break; + + default: + break; + } +} + +static int gus_audio_set_speed(int speed) +{ + if (speed <= 0) + speed = gus_audio_speed; + + if (speed < 4000) + speed = 4000; + + if (speed > 44100) + speed = 44100; + + gus_audio_speed = speed; + + if (only_read_access) + { + /* Compute nearest valid recording speed and return it */ + + /* speed = (9878400 / (gus_audio_speed + 2)) / 16; */ + speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; + speed = (9878400 / (speed * 16)) - 2; + } + return speed; +} + +static int gus_audio_set_channels(int channels) +{ + if (!channels) + return gus_audio_channels; + if (channels > 2) + channels = 2; + if (channels < 1) + channels = 1; + gus_audio_channels = channels; + return channels; +} + +static int gus_audio_set_bits(int bits) +{ + if (!bits) + return gus_audio_bits; + + if (bits != 8 && bits != 16) + bits = 8; + + if (only_8_bits) + bits = 8; + + gus_audio_bits = bits; + return bits; +} + +static int gus_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val; + + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_speed(val); + break; + + case SOUND_PCM_READ_RATE: + val = gus_audio_speed; + break; + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_channels(val + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_channels(val); + break; + + case SOUND_PCM_READ_CHANNELS: + val = gus_audio_channels; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_bits(val); + break; + + case SOUND_PCM_READ_BITS: + val = gus_audio_bits; + break; + + case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */ + case SOUND_PCM_READ_FILTER: + val = -EINVAL; + break; + default: + return -EINVAL; + } + return put_user(val, (int *)arg); +} + +static void gus_audio_reset(int dev) +{ + if (recording_active) + { + gus_write8(0x49, 0x00); /* Halt recording */ + set_input_volumes(); + } +} + +static int saved_iw_mode; /* A hack hack hack */ + +static int gus_audio_open(int dev, int mode) +{ + if (gus_busy) + return -EBUSY; + + if (gus_pnp_flag && mode & OPEN_READ) + { +/* printk(KERN_ERR "GUS: Audio device #%d is playback only.\n", dev);*/ + return -EIO; + } + gus_initialize(); + + gus_busy = 1; + active_device = 0; + + saved_iw_mode = iw_mode; + if (iw_mode) + { + /* There are some problems with audio in enhanced mode so disable it */ + gus_write8(0x19, gus_read8(0x19) & ~0x01); /* Disable enhanced mode */ + iw_mode = 0; + } + + gus_reset(); + reset_sample_memory(); + gus_select_max_voices(14); + + pcm_active = 0; + dma_active = 0; + pcm_opened = 1; + if (mode & OPEN_READ) + { + recording_active = 1; + set_input_volumes(); + } + only_read_access = !(mode & OPEN_WRITE); + only_8_bits = mode & OPEN_READ; + if (only_8_bits) + audio_devs[dev]->format_mask = AFMT_U8; + else + audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE; + + return 0; +} + +static void gus_audio_close(int dev) +{ + iw_mode = saved_iw_mode; + gus_reset(); + gus_busy = 0; + pcm_opened = 0; + active_device = 0; + + if (recording_active) + { + gus_write8(0x49, 0x00); /* Halt recording */ + set_input_volumes(); + } + recording_active = 0; +} + +static void gus_audio_update_volume(void) +{ + unsigned long flags; + int voice; + + if (pcm_active && pcm_opened) + for (voice = 0; voice < gus_audio_channels; voice++) + { + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_rampoff(); + gus_voice_volume(1530 + (25 * gus_pcm_volume)); + gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); + restore_flags(flags); + } +} + +static void play_next_pcm_block(void) +{ + unsigned long flags; + int speed = gus_audio_speed; + int this_one, is16bits, chn; + unsigned long dram_loc; + unsigned char mode[2], ramp_mode[2]; + + if (!pcm_qlen) + return; + + this_one = pcm_head; + + for (chn = 0; chn < gus_audio_channels; chn++) + { + mode[chn] = 0x00; + ramp_mode[chn] = 0x03; /* Ramping and rollover off */ + + if (chn == 0) + { + mode[chn] |= 0x20; /* Loop IRQ */ + voices[chn].loop_irq_mode = LMODE_PCM; + } + if (gus_audio_bits != 8) + { + is16bits = 1; + mode[chn] |= 0x04; /* 16 bit data */ + } + else + is16bits = 0; + + dram_loc = this_one * pcm_bsize; + dram_loc += chn * pcm_banksize; + + if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */ + { + mode[chn] |= 0x08; /* Enable loop */ + ramp_mode[chn] = 0x03; /* Disable rollover bit */ + } + else + { + if (chn == 0) + ramp_mode[chn] = 0x04; /* Enable rollover bit */ + } + save_flags(flags); + cli(); + gus_select_voice(chn); + gus_voice_freq(speed); + + if (gus_audio_channels == 1) + gus_voice_balance(7); /* mono */ + else if (chn == 0) + gus_voice_balance(0); /* left */ + else + gus_voice_balance(15); /* right */ + + if (!pcm_active) /* Playback not already active */ + { + /* + * The playback was not started yet (or there has been a pause). + * Start the voice (again) and ask for a rollover irq at the end of + * this_one block. If this_one one is last of the buffers, use just + * the normal loop with irq. + */ + + gus_voice_off(); + gus_rampoff(); + gus_voice_volume(1530 + (25 * gus_pcm_volume)); + gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); + + gus_write_addr(0x0a, chn * pcm_banksize, 0, is16bits); /* Starting position */ + gus_write_addr(0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */ + + if (chn != 0) + gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1, + 0, is16bits); /* Loop end location */ + } + if (chn == 0) + gus_write_addr(0x04, dram_loc + pcm_bsize - 1, + 0, is16bits); /* Loop end location */ + else + mode[chn] |= 0x08; /* Enable looping */ + restore_flags(flags); + } + for (chn = 0; chn < gus_audio_channels; chn++) + { + save_flags(flags); + cli(); + gus_select_voice(chn); + gus_write8(0x0d, ramp_mode[chn]); + if (iw_mode) + gus_write8(0x15, 0x00); /* Reset voice deactivate bit of SMSI */ + gus_voice_on(mode[chn]); + restore_flags(flags); + } + pcm_active = 1; +} + +static void gus_transfer_output_block(int dev, unsigned long buf, + int total_count, int intrflag, int chn) +{ + /* + * This routine transfers one block of audio data to the DRAM. In mono mode + * it's called just once. When in stereo mode, this_one routine is called + * once for both channels. + * + * The left/mono channel data is transferred to the beginning of dram and the + * right data to the area pointed by gus_page_size. + */ + + int this_one, count; + unsigned long flags; + unsigned char dma_command; + unsigned long address, hold_address; + + save_flags(flags); + cli(); + + count = total_count / gus_audio_channels; + + if (chn == 0) + { + if (pcm_qlen >= pcm_nblk) + printk(KERN_WARNING "GUS Warning: PCM buffers out of sync\n"); + + this_one = pcm_current_block = pcm_tail; + pcm_qlen++; + pcm_tail = (pcm_tail + 1) % pcm_nblk; + pcm_datasize[this_one] = count; + } + else + this_one = pcm_current_block; + + gus_write8(0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma(dev, buf + (chn * count), count, DMA_MODE_WRITE); + + address = this_one * pcm_bsize; + address += chn * pcm_banksize; + + if (audio_devs[dev]->dmap_out->dma > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + + if (gus_audio_bits != 8) + dma_command |= 0x40; /* 16 bit _DATA_ */ + else + dma_command |= 0x80; /* Invert MSB */ + + if (audio_devs[dev]->dmap_out->dma > 3) + dma_command |= 0x04; /* 16 bit DMA channel */ + + gus_write8(0x41, dma_command); /* Kick start */ + + if (chn == (gus_audio_channels - 1)) /* Last channel */ + { + /* + * Last (right or mono) channel data + */ + dma_active = 1; /* DMA started. There is a unacknowledged buffer */ + active_device = GUS_DEV_PCM_DONE; + if (!pcm_active && (pcm_qlen > 1 || count < pcm_bsize)) + { + play_next_pcm_block(); + } + } + else + { + /* + * Left channel data. The right channel + * is transferred after DMA interrupt + */ + active_device = GUS_DEV_PCM_CONTINUE; + } + + restore_flags(flags); +} + +static void gus_uninterleave8(char *buf, int l) +{ +/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */ + int i, p = 0, halfsize = l / 2; + char *buf2 = buf + halfsize, *src = bounce_buf; + + memcpy(bounce_buf, buf, l); + + for (i = 0; i < halfsize; i++) + { + buf[i] = src[p++]; /* Left channel */ + buf2[i] = src[p++]; /* Right channel */ + } +} + +static void gus_uninterleave16(short *buf, int l) +{ +/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */ + int i, p = 0, halfsize = l / 2; + short *buf2 = buf + halfsize, *src = (short *) bounce_buf; + + memcpy(bounce_buf, (char *) buf, l * 2); + + for (i = 0; i < halfsize; i++) + { + buf[i] = src[p++]; /* Left channel */ + buf2[i] = src[p++]; /* Right channel */ + } +} + +static void gus_audio_output_block(int dev, unsigned long buf, int total_count, + int intrflag) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT; + + pcm_current_buf = buf; + pcm_current_count = total_count; + pcm_current_intrflag = intrflag; + pcm_current_dev = dev; + if (gus_audio_channels == 2) + { + char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys); + + if (gus_audio_bits == 8) + gus_uninterleave8(b, total_count); + else + gus_uninterleave16((short *) b, total_count / 2); + } + gus_transfer_output_block(dev, buf, total_count, intrflag, 0); +} + +static void gus_audio_start_input(int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags; + unsigned char mode; + + save_flags(flags); + cli(); + + DMAbuf_start_dma(dev, buf, count, DMA_MODE_READ); + mode = 0xa0; /* DMA IRQ enabled, invert MSB */ + + if (audio_devs[dev]->dmap_in->dma > 3) + mode |= 0x04; /* 16 bit DMA channel */ + if (gus_audio_channels > 1) + mode |= 0x02; /* Stereo */ + mode |= 0x01; /* DMA enable */ + + gus_write8(0x49, mode); + restore_flags(flags); +} + +static int gus_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + unsigned int rate; + + gus_audio_bsize = bsize; + audio_devs[dev]->dmap_in->flags |= DMA_NODMA; + rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; + + gus_write8(0x48, rate & 0xff); /* Set sampling rate */ + + if (gus_audio_bits != 8) + { +/* printk("GUS Error: 16 bit recording not supported\n");*/ + return -EINVAL; + } + return 0; +} + +static int gus_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + int i; + + long mem_ptr, mem_size; + + audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT; + mem_ptr = 0; + mem_size = gus_mem_size / gus_audio_channels; + + if (mem_size > (256 * 1024)) + mem_size = 256 * 1024; + + pcm_bsize = bsize / gus_audio_channels; + pcm_head = pcm_tail = pcm_qlen = 0; + + pcm_nblk = 2; /* MAX_PCM_BUFFERS; */ + if ((pcm_bsize * pcm_nblk) > mem_size) + pcm_nblk = mem_size / pcm_bsize; + + for (i = 0; i < pcm_nblk; i++) + pcm_datasize[i] = 0; + + pcm_banksize = pcm_nblk * pcm_bsize; + + if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024)) + pcm_nblk--; + gus_write8(0x41, 0); /* Disable GF1 DMA */ + return 0; +} + +static int gus_local_qlen(int dev) +{ + return pcm_qlen; +} + + +static struct audio_driver gus_audio_driver = +{ + owner: THIS_MODULE, + open: gus_audio_open, + close: gus_audio_close, + output_block: gus_audio_output_block, + start_input: gus_audio_start_input, + ioctl: gus_audio_ioctl, + prepare_for_input: gus_audio_prepare_for_input, + prepare_for_output: gus_audio_prepare_for_output, + halt_io: gus_audio_reset, + local_qlen: gus_local_qlen, +}; + +static void guswave_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info = &synth_devs[dev]->chn_info[chn]; + + guswave_set_instr(dev, voice, info->pgm_num); + voices[voice].expression_vol = info->controllers[CTL_EXPRESSION]; /* Just MSB */ + voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128; + voices[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; + voices[voice].bender = 0; + voices[voice].bender_range = info->bender_range; + + if (chn == 9) + voices[voice].fixed_pitch = 1; +} + +static void guswave_bender(int dev, int voice, int value) +{ + int freq; + unsigned long flags; + + voices[voice].bender = value - 8192; + freq = compute_finetune(voices[voice].orig_freq, value - 8192, voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_freq(freq); + restore_flags(flags); +} + +static int guswave_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + int i, p, best = -1, best_time = 0x7fffffff; + + p = alloc->ptr; + /* + * First look for a completely stopped voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0) + { + alloc->ptr = p; + return p; + } + if (alloc->alloc_times[p] < best_time) + { + best = p; + best_time = alloc->alloc_times[p]; + } + p = (p + 1) % alloc->max_voice; + } + + /* + * Then look for a releasing voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0xffff) + { + alloc->ptr = p; + return p; + } + p = (p + 1) % alloc->max_voice; + } + if (best >= 0) + p = best; + + alloc->ptr = p; + return p; +} + +static struct synth_operations guswave_operations = +{ + owner: THIS_MODULE, + id: "GUS", + info: &gus_info, + midi_dev: 0, + synth_type: SYNTH_TYPE_SAMPLE, + synth_subtype: SAMPLE_TYPE_GUS, + open: guswave_open, + close: guswave_close, + ioctl: guswave_ioctl, + kill_note: guswave_kill_note, + start_note: guswave_start_note, + set_instr: guswave_set_instr, + reset: guswave_reset, + hw_control: guswave_hw_control, + load_patch: guswave_load_patch, + aftertouch: guswave_aftertouch, + controller: guswave_controller, + panning: guswave_panning, + volume_method: guswave_volume_method, + bender: guswave_bender, + alloc_voice: guswave_alloc, + setup_voice: guswave_setup_voice +}; + +static void set_input_volumes(void) +{ + unsigned long flags; + unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ + + if (have_gus_max) /* Don't disturb GUS MAX */ + return; + + save_flags(flags); + cli(); + + /* + * Enable channels having vol > 10% + * Note! bit 0x01 means the line in DISABLED while 0x04 means + * the mic in ENABLED. + */ + if (gus_line_vol > 10) + mask &= ~0x01; + if (gus_mic_vol > 10) + mask |= 0x04; + + if (recording_active) + { + /* + * Disable channel, if not selected for recording + */ + if (!(gus_recmask & SOUND_MASK_LINE)) + mask |= 0x01; + if (!(gus_recmask & SOUND_MASK_MIC)) + mask &= ~0x04; + } + mix_image &= ~0x07; + mix_image |= mask & 0x07; + outb((mix_image), u_Mixer); + + restore_flags(flags); +} + +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH|SOUND_MASK_PCM) + +int gus_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int vol, val; + + if (((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if (!access_ok(VERIFY_WRITE, (int *)arg, sizeof(int))) + return -EFAULT; + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + { + if (__get_user(val, (int *) arg)) + return -EFAULT; + + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + gus_recmask = val & MIX_DEVS; + if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) + gus_recmask = SOUND_MASK_MIC; + /* Note! Input volumes are updated during next open for recording */ + val = gus_recmask; + break; + + case SOUND_MIXER_MIC: + vol = val & 0xff; + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_mic_vol = vol; + set_input_volumes(); + val = vol | (vol << 8); + break; + + case SOUND_MIXER_LINE: + vol = val & 0xff; + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_line_vol = vol; + set_input_volumes(); + val = vol | (vol << 8); + break; + + case SOUND_MIXER_PCM: + gus_pcm_volume = val & 0xff; + if (gus_pcm_volume < 0) + gus_pcm_volume = 0; + if (gus_pcm_volume > 100) + gus_pcm_volume = 100; + gus_audio_update_volume(); + val = gus_pcm_volume | (gus_pcm_volume << 8); + break; + + case SOUND_MIXER_SYNTH: + gus_wave_volume = val & 0xff; + if (gus_wave_volume < 0) + gus_wave_volume = 0; + if (gus_wave_volume > 100) + gus_wave_volume = 100; + if (active_device == GUS_DEV_WAVE) + { + int voice; + for (voice = 0; voice < nr_voices; voice++) + dynamic_volume_change(voice); /* Apply the new vol */ + } + val = gus_wave_volume | (gus_wave_volume << 8); + break; + + default: + return -EINVAL; + } + } + else + { + switch (cmd & 0xff) + { + /* + * Return parameters + */ + case SOUND_MIXER_RECSRC: + val = gus_recmask; + break; + + case SOUND_MIXER_DEVMASK: + val = MIX_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + val = 0; + break; + + case SOUND_MIXER_RECMASK: + val = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + + case SOUND_MIXER_CAPS: + val = 0; + break; + + case SOUND_MIXER_MIC: + val = gus_mic_vol | (gus_mic_vol << 8); + break; + + case SOUND_MIXER_LINE: + val = gus_line_vol | (gus_line_vol << 8); + break; + + case SOUND_MIXER_PCM: + val = gus_pcm_volume | (gus_pcm_volume << 8); + break; + + case SOUND_MIXER_SYNTH: + val = gus_wave_volume | (gus_wave_volume << 8); + break; + + default: + return -EINVAL; + } + } + return __put_user(val, (int *)arg); +} + +static struct mixer_operations gus_mixer_operations = +{ + owner: THIS_MODULE, + id: "GUS", + name: "Gravis Ultrasound", + ioctl: gus_default_mixer_ioctl +}; + +static int __init gus_default_mixer_init(void) +{ + int n; + + if ((n = sound_alloc_mixerdev()) != -1) + { + /* + * Don't install if there is another + * mixer + */ + mixer_devs[n] = &gus_mixer_operations; + } + if (have_gus_max) + { + /* + * Enable all mixer channels on the GF1 side. Otherwise recording will + * not be possible using GUS MAX. + */ + mix_image &= ~0x07; + mix_image |= 0x04; /* All channels enabled */ + outb((mix_image), u_Mixer); + } + return n; +} + +void __init gus_wave_init(struct address_info *hw_config) +{ + unsigned long flags; + unsigned char val; + char *model_num = "2.4"; + char tmp[64], tmp2[64]; + int gus_type = 0x24; /* 2.4 */ + + int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2; + int sdev; + + hw_config->slots[0] = -1; /* No wave */ + hw_config->slots[1] = -1; /* No ad1848 */ + hw_config->slots[4] = -1; /* No audio */ + hw_config->slots[5] = -1; /* No mixer */ + + if (!gus_pnp_flag) + { + if (irq < 0 || irq > 15) + { + printk(KERN_ERR "ERROR! Invalid IRQ#%d. GUS Disabled", irq); + return; + } + } + + if (dma < 0 || dma > 7 || dma == 4) + { + printk(KERN_ERR "ERROR! Invalid DMA#%d. GUS Disabled", dma); + return; + } + gus_irq = irq; + gus_dma = dma; + gus_dma2 = dma2; + gus_hw_config = hw_config; + + if (gus_dma2 == -1) + gus_dma2 = dma; + + /* + * Try to identify the GUS model. + * + * Versions < 3.6 don't have the digital ASIC. Try to probe it first. + */ + + save_flags(flags); + cli(); + outb((0x20), gus_base + 0x0f); + val = inb(gus_base + 0x0f); + restore_flags(flags); + + if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */ + { + int ad_flags = 0; + + if (gus_pnp_flag) + ad_flags = 0x12345678; /* Interwave "magic" */ + /* + * It has the digital ASIC so the card is at least v3.4. + * Next try to detect the true model. + */ + + if (gus_pnp_flag) /* Hack hack hack */ + val = 10; + else + val = inb(u_MixSelect); + + /* + * Value 255 means pre-3.7 which don't have mixer. + * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. + * 10 and above is GUS MAX which has the CS4231 codec/mixer. + * + */ + + if (val == 255 || val < 5) + { + model_num = "3.4"; + gus_type = 0x34; + } + else if (val < 10) + { + model_num = "3.7"; + gus_type = 0x37; + mixer_type = ICS2101; + request_region(u_MixSelect, 1, "GUS mixer"); + } + else + { + model_num = "MAX"; + gus_type = 0x40; + mixer_type = CS4231; +#ifdef CONFIG_SOUND_GUSMAX + { + unsigned char max_config = 0x40; /* Codec enable */ + + if (gus_dma2 == -1) + gus_dma2 = gus_dma; + + if (gus_dma > 3) + max_config |= 0x10; /* 16 bit capture DMA */ + + if (gus_dma2 > 3) + max_config |= 0x20; /* 16 bit playback DMA */ + + max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ + + outb((max_config), gus_base + 0x106); /* UltraMax control */ + } + + if (ad1848_detect(gus_base + 0x10c, &ad_flags, hw_config->osp)) + { + char *name = "GUS MAX"; + int old_num_mixers = num_mixers; + + if (gus_pnp_flag) + name = "GUS PnP"; + + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + have_gus_max = 1; + if (hw_config->name) + name = hw_config->name; + + hw_config->slots[1] = ad1848_init(name, gus_base + 0x10c, + -irq, gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1, /* Share DMA channels with GF1 */ + hw_config->osp, + THIS_MODULE); + + if (num_mixers > old_num_mixers) + { + /* GUS has it's own mixer map */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } + } + else + printk(KERN_WARNING "GUS: No CS4231 ??"); +#else + printk(KERN_ERR "GUS MAX found, but not compiled in\n"); +#endif + } + } + else + { + /* + * ASIC not detected so the card must be 2.2 or 2.4. + * There could still be the 16-bit/mixer daughter card. + */ + } + + if (hw_config->name) + { + strncpy(tmp, hw_config->name, 45); + tmp[45] = 0; + sprintf(tmp2, "%s (%dk)", tmp, (int) gus_mem_size / 1024); + tmp2[sizeof(tmp2) - 1] = 0; + } + else if (gus_pnp_flag) + { + sprintf(tmp2, "Gravis UltraSound PnP (%dk)", + (int) gus_mem_size / 1024); + } + else + sprintf(tmp2, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); + + + samples = (struct patch_info *)vmalloc((MAX_SAMPLE + 1) * sizeof(*samples)); + if (samples == NULL) + { + printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n"); + return; + } + conf_printf(tmp2, hw_config); + tmp2[sizeof(gus_info.name) - 1] = 0; + strcpy(gus_info.name, tmp2); + + if ((sdev = sound_alloc_synthdev()) == -1) + printk(KERN_WARNING "gus_init: Too many synthesizers\n"); + else + { + voice_alloc = &guswave_operations.alloc; + if (iw_mode) + guswave_operations.id = "IWAVE"; + hw_config->slots[0] = sdev; + synth_devs[sdev] = &guswave_operations; + sequencer_init(); + gus_tmr_install(gus_base + 8); + } + + reset_sample_memory(); + + gus_initialize(); + + if ((gus_mem_size > 0) & !gus_no_wave_dma) + { + hw_config->slots[4] = -1; + if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + "Ultrasound", + &gus_audio_driver, + sizeof(struct audio_driver), + NEEDS_RESTART | + ((!iw_mode && dma2 != dma && dma2 != -1) ? + DMA_DUPLEX : 0), + AFMT_U8 | AFMT_S16_LE, + NULL, dma, dma2)) < 0) + { + return; + } + + hw_config->slots[4] = gus_devnum; + audio_devs[gus_devnum]->min_fragment = 9; /* 512k */ + audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */ + audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */ + audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; + } + + /* + * Mixer dependent initialization. + */ + + switch (mixer_type) + { + case ICS2101: + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + request_region(u_MixSelect, 1, "GUS mixer"); + hw_config->slots[5] = ics2101_mixer_init(); + audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ + return; + + case CS4231: + /* Initialized elsewhere (ad1848.c) */ + default: + hw_config->slots[5] = gus_default_mixer_init(); + audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ + return; + } +} + +void __exit gus_wave_unload(struct address_info *hw_config) +{ +#ifdef CONFIG_SOUND_GUSMAX + if (have_gus_max) + { + ad1848_unload(gus_base + 0x10c, + -gus_irq, + gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1); /* Share DMA channels with GF1 */ + } +#endif + + if (mixer_type == ICS2101) + { + release_region(u_MixSelect, 1); + } + if (hw_config->slots[0] != -1) + sound_unload_synthdev(hw_config->slots[0]); + if (hw_config->slots[1] != -1) + sound_unload_audiodev(hw_config->slots[1]); + if (hw_config->slots[2] != -1) + sound_unload_mididev(hw_config->slots[2]); + if (hw_config->slots[4] != -1) + sound_unload_audiodev(hw_config->slots[4]); + if (hw_config->slots[5] != -1) + sound_unload_mixerdev(hw_config->slots[5]); + + if(samples) + vfree(samples); + samples=NULL; +} + +static void do_loop_irq(int voice) +{ + unsigned char tmp; + int mode, parm; + unsigned long flags; + + save_flags(flags); + cli(); + gus_select_voice(voice); + + tmp = gus_read8(0x00); + tmp &= ~0x20; /* + * Disable wave IRQ for this_one voice + */ + gus_write8(0x00, tmp); + + if (tmp & 0x03) /* Voice stopped */ + voice_alloc->map[voice] = 0; + + mode = voices[voice].loop_irq_mode; + voices[voice].loop_irq_mode = 0; + parm = voices[voice].loop_irq_parm; + + switch (mode) + { + case LMODE_FINISH: /* + * Final loop finished, shoot volume down + */ + + if ((int) (gus_read16(0x09) >> 4) < 100) /* + * Get current volume + */ + { + gus_voice_off(); + gus_rampoff(); + gus_voice_init(voice); + break; + } + gus_ramp_range(65, 4065); + gus_ramp_rate(0, 63); /* + * Fastest possible rate + */ + gus_rampon(0x20 | 0x40); /* + * Ramp down, once, irq + */ + voices[voice].volume_irq_mode = VMODE_HALT; + break; + + case LMODE_PCM_STOP: + pcm_active = 0; /* Signal to the play_next_pcm_block routine */ + case LMODE_PCM: + { + pcm_qlen--; + pcm_head = (pcm_head + 1) % pcm_nblk; + if (pcm_qlen && pcm_active) + { + play_next_pcm_block(); + } + else + { + /* Underrun. Just stop the voice */ + gus_select_voice(0); /* Left channel */ + gus_voice_off(); + gus_rampoff(); + gus_select_voice(1); /* Right channel */ + gus_voice_off(); + gus_rampoff(); + pcm_active = 0; + } + + /* + * If the queue was full before this interrupt, the DMA transfer was + * suspended. Let it continue now. + */ + + if (audio_devs[gus_devnum]->dmap_out->qlen > 0) + DMAbuf_outputintr(gus_devnum, 0); + } + break; + + default: + break; + } + restore_flags(flags); +} + +static void do_volume_irq(int voice) +{ + unsigned char tmp; + int mode, parm; + unsigned long flags; + + save_flags(flags); + cli(); + + gus_select_voice(voice); + tmp = gus_read8(0x0d); + tmp &= ~0x20; /* + * Disable volume ramp IRQ + */ + gus_write8(0x0d, tmp); + + mode = voices[voice].volume_irq_mode; + voices[voice].volume_irq_mode = 0; + parm = voices[voice].volume_irq_parm; + + switch (mode) + { + case VMODE_HALT: /* Decay phase finished */ + if (iw_mode) + gus_write8(0x15, 0x02); /* Set voice deactivate bit of SMSI */ + restore_flags(flags); + gus_voice_init(voice); + break; + + case VMODE_ENVELOPE: + gus_rampoff(); + restore_flags(flags); + step_envelope(voice); + break; + + case VMODE_START_NOTE: + restore_flags(flags); + guswave_start_note2(voices[voice].dev_pending, voice, + voices[voice].note_pending, voices[voice].volume_pending); + if (voices[voice].kill_pending) + guswave_kill_note(voices[voice].dev_pending, voice, + voices[voice].note_pending, 0); + + if (voices[voice].sample_pending >= 0) + { + guswave_set_instr(voices[voice].dev_pending, voice, + voices[voice].sample_pending); + voices[voice].sample_pending = -1; + } + break; + + default: + restore_flags(flags); + } + restore_flags(flags); +} + +void gus_voice_irq(void) +{ + unsigned long wave_ignore = 0, volume_ignore = 0; + unsigned long voice_bit; + + unsigned char src, voice; + + while (1) + { + src = gus_read8(0x0f); /* + * Get source info + */ + voice = src & 0x1f; + src &= 0xc0; + + if (src == (0x80 | 0x40)) + return; /* + * No interrupt + */ + + voice_bit = 1 << voice; + + if (!(src & 0x80)) /* + * Wave IRQ pending + */ + if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /* + * Not done + * yet + */ + { + wave_ignore |= voice_bit; + do_loop_irq(voice); + } + if (!(src & 0x40)) /* + * Volume IRQ pending + */ + if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /* + * Not done + * yet + */ + { + volume_ignore |= voice_bit; + do_volume_irq(voice); + } + } +} + +void guswave_dma_irq(void) +{ + unsigned char status; + + status = gus_look8(0x41); /* Get DMA IRQ Status */ + if (status & 0x40) /* DMA interrupt pending */ + switch (active_device) + { + case GUS_DEV_WAVE: + wake_up(&dram_sleeper); + break; + + case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + gus_transfer_output_block(pcm_current_dev, pcm_current_buf, + pcm_current_count, + pcm_current_intrflag, 1); + break; + + case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + if (pcm_qlen < pcm_nblk) + { + dma_active = 0; + if (gus_busy) + { + if (audio_devs[gus_devnum]->dmap_out->qlen > 0) + DMAbuf_outputintr(gus_devnum, 0); + } + } + break; + + default: + break; + } + status = gus_look8(0x49); /* + * Get Sampling IRQ Status + */ + if (status & 0x40) /* + * Sampling Irq pending + */ + { + DMAbuf_inputintr(gus_devnum); + } +} + +/* + * Timer stuff + */ + +static volatile int select_addr, data_addr; +static volatile int curr_timer = 0; + +void gus_timer_command(unsigned int addr, unsigned int val) +{ + int i; + + outb(((unsigned char) (addr & 0xff)), select_addr); + + for (i = 0; i < 2; i++) + inb(select_addr); + + outb(((unsigned char) (val & 0xff)), data_addr); + + for (i = 0; i < 2; i++) + inb(select_addr); +} + +static void arm_timer(int timer, unsigned int interval) +{ + curr_timer = timer; + + if (timer == 1) + { + gus_write8(0x46, 256 - interval); /* Set counter for timer 1 */ + gus_write8(0x45, 0x04); /* Enable timer 1 IRQ */ + gus_timer_command(0x04, 0x01); /* Start timer 1 */ + } + else + { + gus_write8(0x47, 256 - interval); /* Set counter for timer 2 */ + gus_write8(0x45, 0x08); /* Enable timer 2 IRQ */ + gus_timer_command(0x04, 0x02); /* Start timer 2 */ + } + + gus_timer_enabled = 1; +} + +static unsigned int gus_tmr_start(int dev, unsigned int usecs_per_tick) +{ + int timer_no, resolution; + int divisor; + + if (usecs_per_tick > (256 * 80)) + { + timer_no = 2; + resolution = 320; /* usec */ + } + else + { + timer_no = 1; + resolution = 80; /* usec */ + } + divisor = (usecs_per_tick + (resolution / 2)) / resolution; + arm_timer(timer_no, divisor); + + return divisor * resolution; +} + +static void gus_tmr_disable(int dev) +{ + gus_write8(0x45, 0); /* Disable both timers */ + gus_timer_enabled = 0; +} + +static void gus_tmr_restart(int dev) +{ + if (curr_timer == 1) + gus_write8(0x45, 0x04); /* Start timer 1 again */ + else + gus_write8(0x45, 0x08); /* Start timer 2 again */ + gus_timer_enabled = 1; +} + +static struct sound_lowlev_timer gus_tmr = +{ + 0, + 1, + gus_tmr_start, + gus_tmr_disable, + gus_tmr_restart +}; + +static void gus_tmr_install(int io_base) +{ + struct sound_lowlev_timer *tmr; + + select_addr = io_base; + data_addr = io_base + 1; + + tmr = &gus_tmr; + +#ifdef THIS_GETS_FIXED + sound_timer_init(&gus_tmr, "GUS"); +#endif +} diff -Nru a/sound/oss/hex2hex.c b/sound/oss/hex2hex.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/hex2hex.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,101 @@ +/* + * hex2hex reads stdin in Intel HEX format and produces an + * (unsigned char) array which contains the bytes and writes it + * to stdout using C syntax + */ + +#include +#include +#include + +#define ABANDON(why) { fprintf(stderr, "%s\n", why); exit(1); } +#define MAX_SIZE (256*1024) +unsigned char buf[MAX_SIZE]; + +int loadhex(FILE *inf, unsigned char *buf) +{ + int l=0, c, i; + + while ((c=getc(inf))!=EOF) + { + if (c == ':') /* Sync with beginning of line */ + { + int n, check; + unsigned char sum; + int addr; + int linetype; + + if (fscanf(inf, "%02x", &n) != 1) + ABANDON("File format error"); + sum = n; + + if (fscanf(inf, "%04x", &addr) != 1) + ABANDON("File format error"); + sum += addr/256; + sum += addr%256; + + if (fscanf(inf, "%02x", &linetype) != 1) + ABANDON("File format error"); + sum += linetype; + + if (linetype != 0) + continue; + + for (i=0;i= MAX_SIZE) + ABANDON("File too large"); + buf[addr++] = c; + if (addr > l) + l = addr; + sum += c; + } + + if (fscanf(inf, "%02x", &check) != 1) + ABANDON("File format error"); + + sum = ~sum + 1; + if (check != sum) + ABANDON("Line checksum error"); + } + } + + return l; +} + +int main( int argc, const char * argv [] ) +{ + const char * varline; + int i,l; + int id=0; + + if(argv[1] && strcmp(argv[1], "-i")==0) + { + argv++; + argc--; + id=1; + } + if(argv[1]==NULL) + { + fprintf(stderr,"hex2hex: [-i] filename\n"); + exit(1); + } + varline = argv[1]; + l = loadhex(stdin, buf); + + printf("/*\n *\t Computer generated file. Do not edit.\n */\n"); + printf("static int %s_len = %d;\n", varline, l); + printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":""); + + for (i=0;i + * + * Built from: + * Low level code: Zach Brown (original nonworking i810 OSS driver) + * Jaroslav Kysela (working ALSA driver) + * + * Framework: Thomas Sailer + * Extended by: Zach Brown + * and others.. + * + * Hardware Provided By: + * Analog Devices (A major AC97 codec maker) + * Intel Corp (you've probably heard of them already) + * + * AC97 clues and assistance provided by + * Analog Devices + * Zach 'Fufu' Brown + * Jeff Garzik + * + * 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. + * + * + * Intel 810 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. + * + * Fix The Sound On Dell + * + * Not everyone uses 48KHz. We know of no way to detect this reliably + * and certainly not to get the right data. If your i810 audio sounds + * stupid you may need to investigate other speeds. According to Analog + * they tend to use a 14.318MHz clock which gives you a base rate of + * 41194Hz. + * + * This is available via the 'ftsodell=1' option. + * + * If you need to force a specific rate set the clocking= option + * + * This driver is cursed. (Ben LaHaise) + */ + +#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_INTEL_82801 +#define PCI_DEVICE_ID_INTEL_82801 0x2415 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82901 +#define PCI_DEVICE_ID_INTEL_82901 0x2425 +#endif +#ifndef PCI_DEVICE_ID_INTEL_ICH2 +#define PCI_DEVICE_ID_INTEL_ICH2 0x2445 +#endif +#ifndef PCI_DEVICE_ID_INTEL_ICH3 +#define PCI_DEVICE_ID_INTEL_ICH3 0x2485 +#endif +#ifndef PCI_DEVICE_ID_INTEL_440MX +#define PCI_DEVICE_ID_INTEL_440MX 0x7195 +#endif +#ifndef PCI_DEVICE_ID_SI_7012 +#define PCI_DEVICE_ID_SI_7012 0x7012 +#endif +#ifndef PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO +#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1 +#endif + +static int ftsodell=0; +static int strict_clocking=0; +static unsigned int clocking=0; +static int spdif_locked=0; + +//#define DEBUG +//#define DEBUG2 +//#define DEBUG_INTERRUPTS +//#define DEBUG_MMAP + +#define ADC_RUNNING 1 +#define DAC_RUNNING 2 + +#define I810_FMT_16BIT 1 +#define I810_FMT_STEREO 2 +#define I810_FMT_MASK 3 + +#define SPDIF_ON 0x0004 +#define SURR_ON 0x0010 +#define CENTER_LFE_ON 0x0020 +#define VOL_MUTED 0x8000 + +/* 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 i810 channel */ +#define SG_LEN 32 +struct i810_channel +{ + /* these sg guys should probably be allocated + seperately 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 seperate 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##_PIV = 0x##DIG##a, /* Prefetched Index Value */ \ + PRE##_CR = 0x##DIG##b /* Control Register */ \ +} + +ENUM_ENGINE(OFF,0); /* Offsets */ +ENUM_ENGINE(PI,0); /* PCM In */ +ENUM_ENGINE(PO,1); /* PCM Out */ +ENUM_ENGINE(MC,2); /* Mic In */ + +enum { + GLOB_CNT = 0x2c, /* Global Control */ + GLOB_STA = 0x30, /* Global Status */ + CAS = 0x34 /* Codec Write Semaphore Register */ +}; + +/* 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) */ +#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI) + +/* interrupts for the whole chip */ +#define INT_SEC (1<<11) +#define INT_PRI (1<<10) +#define INT_MC (1<<7) +#define INT_PO (1<<6) +#define INT_PI (1<<5) +#define INT_MO (1<<2) +#define INT_NI (1<<1) +#define INT_GPI (1<<0) +#define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI) + + +#define DRIVER_VERSION "0.21" + +/* magic numbers to protect our data structures */ +#define I810_CARD_MAGIC 0x5072696E /* "Prin" */ +#define I810_STATE_MAGIC 0x63657373 /* "cess" */ +#define I810_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ +#define NR_HW_CH 3 + +/* 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 }; + +enum { + ICH82801AA = 0, + ICH82901AB, + INTEL440MX, + INTELICH2, + INTELICH3, + SI7012, + NVIDIA_NFORCE +}; + +static char * card_names[] = { + "Intel ICH 82801AA", + "Intel ICH 82901AB", + "Intel 440MX", + "Intel ICH2", + "Intel ICH3", + "SiS 7012", + "NVIDIA nForce Audio" +}; + +static struct pci_device_id i810_pci_tbl [] __initdata = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82801AA}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82901, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82901AB}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_440MX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTEL440MX}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH2}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH3}, + {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, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, NVIDIA_NFORCE}, + {0,} +}; + +MODULE_DEVICE_TABLE (pci, i810_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 i810_state { + unsigned int magic; + struct i810_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 i810_channel *read_channel; + struct i810_channel *write_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 i810_card { + unsigned int magic; + + /* We keep i810 cards in a linked list */ + struct i810_card *next; + + /* The i810 has a certain amount of cross channel interaction + so we use a single per card lock */ + spinlock_t 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 i810_state *states[NR_HW_CH]; + struct i810_channel *channel; /* 1:1 to states[] but diff. lifetime */ + dma_addr_t chandma; + + u16 ac97_features; + u16 ac97_status; + u16 channels; + + /* hardware resources */ + unsigned long iobase; + unsigned long ac97base; + u32 irq; + + /* Function support */ + struct i810_channel *(*alloc_pcm_channel)(struct i810_card *); + struct i810_channel *(*alloc_rec_pcm_channel)(struct i810_card *); + struct i810_channel *(*alloc_rec_mic_channel)(struct i810_card *); + void (*free_pcm_channel)(struct i810_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 i810_card *devs = NULL; + +static int i810_open_mixdev(struct inode *inode, struct file *file); +static int i810_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg); +static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); + +static struct i810_channel *i810_alloc_pcm_channel(struct i810_card *card) +{ + if(card->channel[1].used==1) + return NULL; + card->channel[1].used=1; + return &card->channel[1]; +} + +static struct i810_channel *i810_alloc_rec_pcm_channel(struct i810_card *card) +{ + if(card->channel[0].used==1) + return NULL; + card->channel[0].used=1; + return &card->channel[0]; +} + +static struct i810_channel *i810_alloc_rec_mic_channel(struct i810_card *card) +{ + if(card->channel[2].used==1) + return NULL; + card->channel[2].used=1; + return &card->channel[2]; +} + +static void i810_free_pcm_channel(struct i810_card *card, int channel) +{ + card->channel[channel].used=0; +} + +static int i810_valid_spdif_rate ( struct ac97_codec *codec, int rate ) +{ + unsigned long id = 0L; + + id = (i810_ac97_get(codec, AC97_VENDOR_ID1) << 16); + id |= i810_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff; +#ifdef DEBUG + printk ( "i810_audio: codec = %s, codec_id = 0x%08lx\n", codec->name, id); +#endif + switch ( id ) { + case 0x41445361: /* AD1886 */ + 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); +} + +/* i810_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 i810_set_spdif_output(struct i810_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)) { +#ifdef DEBUG + printk(KERN_WARNING "i810_audio: S/PDIF transmitter not available.\n"); +#endif + 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)); + + /* 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; + } + + 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 + + /* turn off S/PDIF */ + i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + 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)); + } +} + +/* i810_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 i810_set_dac_channels(struct i810_state *state, int channel) +{ + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + aud_reg = i810_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; + } + i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + +} + + +/* set playback sample rate */ +static unsigned int i810_set_dac_rate(struct i810_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; +#ifdef DEBUG + printk("Asked for %d Hz, but ac97_features says we only do %dHz. Sorry!\n", + rate,clocking); +#endif + 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; + } +#ifdef DEBUG + printk("i810_audio: called i810_set_dac_rate : asked for %d, got %d\n", rate, dmabuf->rate); +#endif + rate = new_rate; + return dmabuf->rate; +} + +/* set recording sample rate */ +static unsigned int i810_set_adc_rate(struct i810_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; + } +#ifdef DEBUG + printk("i810_audio: called i810_set_adc_rate : rate = %d/%d\n", dmabuf->rate, rate); +#endif + return dmabuf->rate; +} + +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ + +static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int civ, offset, port, port_picb, bytes = 2; + + if (!dmabuf->enable) + return 0; + + if (rec) + port = state->card->iobase + dmabuf->read_channel->port; + else + port = state->card->iobase + dmabuf->write_channel->port; + + if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) { + port_picb = port + OFF_SR; + bytes = 1; + } else + 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 the right place, and we will suddenly think + * we just went forward dmasize - fragsize bytes, causing + * totally stupid *huge* dma overrun messages. We are + * assuming that the 1us delay 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)); + + return (((civ + 1) * dmabuf->fragsize - (bytes * offset)) + % dmabuf->dmasize); +} + +/* Stop recording (lock held) */ +static inline void __stop_adc(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_card *card = state->card; + + dmabuf->enable &= ~ADC_RUNNING; + outb(0, card->iobase + PI_CR); + // wait for the card to acknowledge shutdown + while( inb(card->iobase + PI_CR) != 0 ) ; + // now clear any latent interrupt bits (like the halt bit) + if(card->pci_id == PCI_DEVICE_ID_SI_7012) + outb( inb(card->iobase + PI_PICB), card->iobase + PI_PICB ); + else + outb( inb(card->iobase + PI_SR), card->iobase + PI_SR ); + outl( inl(card->iobase + GLOB_STA) & INT_PI, card->iobase + GLOB_STA); +} + +static void stop_adc(struct i810_state *state) +{ + struct i810_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 i810_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) | 1, state->card->iobase + PI_CR); + } +} + +static void start_adc(struct i810_state *state) +{ + struct i810_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 i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_card *card = state->card; + + dmabuf->enable &= ~DAC_RUNNING; + outb(0, card->iobase + PO_CR); + // wait for the card to acknowledge shutdown + while( inb(card->iobase + PO_CR) != 0 ) ; + // now clear any latent interrupt bits (like the halt bit) + if(card->pci_id == PCI_DEVICE_ID_SI_7012) + outb( inb(card->iobase + PO_PICB), card->iobase + PO_PICB ); + else + outb( inb(card->iobase + PO_SR), card->iobase + PO_SR ); + outl( inl(card->iobase + GLOB_STA) & INT_PO, card->iobase + GLOB_STA); +} + +static void stop_dac(struct i810_state *state) +{ + struct i810_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 i810_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) | 1, state->card->iobase + PO_CR); + } +} +static void start_dac(struct i810_state *state) +{ + struct i810_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __start_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* allocate DMA buffer, playback and recording buffer should be allocated seperately */ +static int alloc_dmabuf(struct i810_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<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< size ) + continue; + if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, + PAGE_SIZE << order, + &dmabuf->dma_handle))) + break; + } + if (!rawbuf) + return -ENOMEM; + + +#ifdef DEBUG + printk("i810_audio: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); +#endif + + 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++) + mem_map_reserve(page); + + return 0; +} + +/* free DMA buffer */ +static void dealloc_dmabuf(struct i810_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++) + mem_map_unreserve(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 i810_state *state, unsigned rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_channel *c; + 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); + 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(dmabuf->read_channel) + c = dmabuf->read_channel; + else + c = dmabuf->write_channel; + while(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;inumfrag;i++) + { + sg->busaddr=(u32)dmabuf->dma_handle+dmabuf->fragsize*i; + // the card will always be doing 16bit stereo + sg->control=dmabuf->fragsamples; + if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) + sg->control <<= 1; + sg->control|=CON_BUFPAD; + // 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((u32)state->card->chandma + + c->num*sizeof(struct i810_channel), + 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); + + if(c != dmabuf->write_channel) + c = dmabuf->write_channel; + else + c = NULL; + } + + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + +#ifdef DEBUG + printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, " + "fragsize = %d dmasize = %d\n", + dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); +#endif + + return 0; +} + +static void __i810_update_lvi(struct i810_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int x, port; + + port = state->card->iobase; + if(rec) + port += dmabuf->read_channel->port; + else + 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))) ) ; + } 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))) ) ; + } + } + + /* 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 i810_update_lvi(struct i810_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + + if(!dmabuf->ready) + return; + spin_lock_irqsave(&state->card->lock, flags); + __i810_update_lvi(state, rec); + spin_unlock_irqrestore(&state->card->lock, flags); +} + +/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ +static void i810_update_ptr(struct i810_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 = i810_get_dma_addr(state, 1); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; +#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) + printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); +#endif + 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 "i810_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 = i810_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 "i810_audio: DMA overrun on write\n"); + printk("i810_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); + } +} + +static inline int i810_get_free_write_space(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int free; + + i810_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 i810_get_available_read_data(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int avail; + + i810_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 i810_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); + i810_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; + i810_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 "i810_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 void i810_channel_interrupt(struct i810_card *card) +{ + int i, count; + +#ifdef DEBUG_INTERRUPTS + printk("CHANNEL "); +#endif + for(i=0;istates[i]; + struct i810_channel *c; + struct dmabuf *dmabuf; + unsigned long port = card->iobase; + u16 status; + + if(!state) + continue; + if(!state->dmabuf.ready) + continue; + dmabuf = &state->dmabuf; + if(dmabuf->enable & DAC_RUNNING) { + c=dmabuf->write_channel; + } else if(dmabuf->enable & ADC_RUNNING) { + c=dmabuf->read_channel; + } else /* This can occur going from R/W to close */ + continue; + + port+=c->port; + + if(card->pci_id == PCI_DEVICE_ID_SI_7012) + status = inw(port + OFF_PICB); + else + status = inw(port + OFF_SR); + +#ifdef DEBUG_INTERRUPTS + printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status); +#endif + if(status & DMA_INT_COMPLETE) + { + /* only wake_up() waiters if this interrupt signals + * us being beyond a userfragsize of data open or + * available, and i810_update_ptr() does that for + * us + */ + i810_update_ptr(state); +#ifdef DEBUG_INTERRUPTS + printk("COMP %d ", dmabuf->hwptr / + dmabuf->fragsize); +#endif + } + if(status & (DMA_INT_LVI | DMA_INT_DCH)) + { + /* wake_up() unconditionally on LVI and DCH */ + i810_update_ptr(state); + wake_up(&dmabuf->wait); +#ifdef DEBUG_INTERRUPTS + if(status & DMA_INT_LVI) + printk("LVI "); + if(status & DMA_INT_DCH) + printk("DCH -"); +#endif + if(dmabuf->enable & DAC_RUNNING) + count = dmabuf->count; + else + count = dmabuf->dmasize - dmabuf->count; + if(count > 0) { + outb(inb(port+OFF_CR) | 1, port+OFF_CR); +#ifdef DEBUG_INTERRUPTS + printk(" CONTINUE "); +#endif + } else { + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + dmabuf->enable = 0; + wake_up(&dmabuf->wait); +#ifdef DEBUG_INTERRUPTS + printk(" STOP "); +#endif + } + } + if(card->pci_id == PCI_DEVICE_ID_SI_7012) + outw(status & DMA_INT_MASK, port + OFF_PICB); + else + outw(status & DMA_INT_MASK, port + OFF_SR); + } +#ifdef DEBUG_INTERRUPTS + printk(")\n"); +#endif +} + +static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct i810_card *card = (struct i810_card *)dev_id; + u32 status; + + spin_lock(&card->lock); + + status = inl(card->iobase + GLOB_STA); + + if(!(status & INT_MASK)) + { + spin_unlock(&card->lock); + return; /* not for us */ + } + + if(status & (INT_PO|INT_PI|INT_MC)) + i810_channel_interrupt(card); + + /* clear 'em */ + outl(status & INT_MASK, card->iobase + GLOB_STA); + spin_unlock(&card->lock); +} + +/* 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 i810_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct i810_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("i810_audio: i810_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 = i810_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. + */ + i810_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)) { +#ifdef DEBUG + printk(KERN_ERR "i810_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); +#endif + /* 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: + i810_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 i810_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct i810_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("i810_audio: i810_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 (!dmabuf->write_channel) { + dmabuf->ready = 0; + dmabuf->write_channel = card->alloc_pcm_channel(card); + if(!dmabuf->write_channel) + return -EBUSY; + } + 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 = i810_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 "i810_audio: i810_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 + */ + dmabuf->trigger = PCM_ENABLE_OUTPUT; + i810_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. */ + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { +#ifdef DEBUG + printk(KERN_ERR "i810_audio: playback schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); +#endif + /* a buffer underrun, we delay the recovery until next time the + while loop begin and we REALLY have data to play */ + //return ret; + } + 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: + i810_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 i810_poll(struct file *file, struct poll_table_struct *wait) +{ + struct i810_state *state = (struct i810_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); + if (dmabuf->enable & ADC_RUNNING || + dmabuf->trigger & PCM_ENABLE_INPUT) { + if (i810_get_available_read_data(state) >= + (signed)dmabuf->userfragsize) + mask |= POLLIN | POLLRDNORM; + } + if (dmabuf->enable & DAC_RUNNING || + dmabuf->trigger & PCM_ENABLE_OUTPUT) { + if (i810_get_free_write_space(state) >= + (signed)dmabuf->userfragsize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&state->card->lock, flags); + return mask; +} + +static int i810_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct i810_state *state = (struct i810_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; +#ifdef DEBUG_MMAP + printk("i810_audio: mmap'ed %ld bytes of data space\n", size); +#endif +out: + unlock_kernel(); + return ret; +} + +static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct i810_channel *c = NULL; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + unsigned int i_glob_cnt; + int val = 0, ret; + struct ac97_codec *codec = state->card->ac97_codec[0]; + +#ifdef DEBUG + printk("i810_audio: i810_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 (c != NULL) { + outb(2, state->card->iobase+c->port+OFF_CR); /* reset DMA machine */ + outl((u32)state->card->chandma + + c->num*sizeof(struct i810_channel), + 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(); + 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 (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 */ + /* AD1886 only supports 48000, need to check that */ + if ( i810_valid_spdif_rate ( codec, val ) ) { + /* Set DAC rate */ + i810_set_spdif_output ( state, -1, 0 ); + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + i810_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + /* Set S/PDIF transmitter rate. */ + i810_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); + i810_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); + i810_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); + } + return put_user(1, (int *)arg); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + 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: +#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 & ADC_RUNNING) { + stop_adc(state); + } + } else { + return put_user(state->card->channels, (int *)arg); + } + + /* ICH and ICH0 only support 2 channels */ + if ( state->card->pci_id == 0x2415 || state->card->pci_id == 0x2425 ) + return put_user(2, (int *)arg); + + /* Multi-channel support was added with ICH2. Bits in */ + /* Global Status and Global Control register are now */ + /* used to indicate this. */ + + i_glob_cnt = inl(state->card->iobase + GLOB_CNT); + + /* Current # of channels enabled */ + if ( i_glob_cnt & 0x0100000 ) + ret = 4; + else if ( i_glob_cnt & 0x0200000 ) + ret = 6; + else + ret = 2; + + switch ( val ) { + case 2: /* 2 channels is always supported */ + outl(state->card->iobase + GLOB_CNT, (i_glob_cnt & 0xcfffff)); + /* Do we need to change mixer settings???? */ + break; + case 4: /* Supported on some chipsets, better check first */ + if ( state->card->channels >= 4 ) { + outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0100000)); + /* Do we need to change mixer settings??? */ + } else { + val = ret; + } + break; + case 6: /* Supported on some chipsets, better check first */ + if ( state->card->channels >= 6 ) { + outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0200000)); + /* Do we need to change mixer settings??? */ + } else { + val = ret; + } + 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(!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 (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + abinfo.fragsize = dmabuf->userfragsize; + abinfo.fragstotal = dmabuf->userfrags; + if (dmabuf->mapped) + abinfo.bytes = dmabuf->dmasize; + else + abinfo.bytes = i810_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 (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + val = i810_get_free_write_space(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.ptr = dmabuf->hwptr; + cinfo.blocks = val/dmabuf->userfragsize; + if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __i810_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 = i810_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 = i810_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; + __i810_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); + } + 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); + i810_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = i810_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __i810_update_lvi(state, 0); + spin_unlock_irqrestore(&state->card->lock, flags); + } else + start_dac(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); + i810_update_ptr(state); + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + } + i810_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); + i810_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 = i810_ac97_get(codec, AC97_SPDIF_CONTROL); + val |= (ret & 0x3000); + + i810_ac97_set(codec, AC97_SPDIF_CONTROL, val); + if(i810_ac97_get(codec, AC97_SPDIF_CONTROL) != val ) { + printk(KERN_ERR "i810_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val); + return -EFAULT; + } + } +#ifdef DEBUG + else + printk(KERN_WARNING "i810_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 "i810_audio: S/PDIF transmitter not avalible.\n"); +#endif + val = 0; + } else { + val = i810_ac97_get(codec, AC97_SPDIF_CONTROL); + } + //return put_user((val & 0xcfff), (int *)arg); + return put_user(val, (int *)arg); + + 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. + */ + + i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, dmabuf->rate ); + 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 ) + i810_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; + } + i810_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: +#ifdef DEBUG + printk("SNDCTL_* -EINVAL\n"); +#endif + return -EINVAL; + } + return -EINVAL; +} + +static int i810_open(struct inode *inode, struct file *file) +{ + int i = 0; + struct i810_card *card = devs; + struct i810_state *state = NULL; + struct dmabuf *dmabuf = NULL; + + /* find an avaiable 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 i810_state *) + kmalloc(sizeof(struct i810_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + memset(state, 0, sizeof(struct i810_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 = I810_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; + i810_set_adc_rate(state, 8000); + } + if(file->f_mode & FMODE_WRITE) { + 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 ( spdif_locked ) { + i810_set_dac_rate(state, spdif_locked); + i810_set_spdif_output(state, AC97_EA_SPSA_3_4, spdif_locked); + } else { + i810_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 |= I810_FMT_16BIT | I810_FMT_STEREO; + dmabuf->ossfragsize = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + + state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + return 0; +} + +static int i810_release(struct inode *inode, struct file *file) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct i810_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 & PCM_ENABLE_INPUT) { + stop_adc(state); + } + spin_lock_irqsave(&card->lock, flags); + dealloc_dmabuf(state); + if (file->f_mode & FMODE_WRITE) { + 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 i810_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: i810_read, + write: i810_write, + poll: i810_poll, + ioctl: i810_ioctl, + mmap: i810_mmap, + open: i810_open, + release: i810_release, +}; + +/* Write AC97 codec registers */ + +static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg) +{ + struct i810_card *card = dev->private_data; + int count = 100; + u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); + + while(count-- && (inb(card->iobase + CAS) & 1)) + udelay(1); + + return inw(card->ac97base + reg_set); +} + +static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) +{ + struct i810_card *card = dev->private_data; + int count = 100; + u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); + + while(count-- && (inb(card->iobase + CAS) & 1)) + udelay(1); + outw(data, card->ac97base + reg_set); +} + + +/* OSS /dev/mixer file operation methods */ + +static int i810_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + unsigned int minor = minor(inode->i_rdev); + struct i810_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 i810_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 i810_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: i810_ioctl_mixdev, + open: i810_open_mixdev, +}; + +/* AC97 codec initialisation. These small functions exist so we don't + duplicate code between module init and apm resume */ + +static inline int i810_ac97_exists(struct i810_card *card,int ac97_number) +{ + u32 reg = inl(card->iobase + GLOB_STA); + return (reg & (0x100 << ac97_number)); +} + +static inline int i810_ac97_enable_variable_rate(struct ac97_codec *codec) +{ + i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9); + i810_ac97_set(codec,AC97_EXTENDED_STATUS, + i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800); + + return (i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1); +} + + +static int i810_ac97_probe_and_powerup(struct i810_card *card,struct ac97_codec *codec) +{ + /* Returns 0 on failure */ + int i; + + if (ac97_probe_codec(codec) == 0) return 0; + + /* power it all up */ + i810_ac97_set(codec, AC97_POWER_CONTROL, + i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); + /* wait for analog ready */ + for (i=10; + i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); + i--) + { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + } + return i; +} + +/* if I knew what this did, I'd give it a better name */ +static int i810_ac97_random_init_stuff(struct i810_card *card) +{ + u32 reg = inl(card->iobase + GLOB_CNT); + int i; + + if((reg&2)==0) /* Cold required */ + reg|=2; + else + reg|=4; /* Warm */ + + reg&=~8; /* ACLink on */ + outl(reg , card->iobase + GLOB_CNT); + + for(i=0;i<10;i++) + { + if((inl(card->iobase+GLOB_CNT)&4)==0) + break; + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + } + if(i==10) + { + printk(KERN_ERR "i810_audio: AC'97 reset failed.\n"); + return 0; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/2); + reg = inl(card->iobase + GLOB_STA); + inw(card->ac97base); + return 1; +} + +static int __init i810_ac97_init(struct i810_card *card) +{ + int num_ac97 = 0; + int total_channels = 0; + struct ac97_codec *codec; + u16 eid; + u32 reg; + + if(!i810_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.... */ + + card->channels = 2; + reg = inl(card->iobase + GLOB_STA); + if ( reg & 0x0200000 ) + card->channels = 6; + else if ( reg & 0x0100000 ) + card->channels = 4; + printk("i810_audio: Audio Controller supports %d channels.\n", card->channels); + + inw(card->ac97base); + + 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 (!i810_ac97_exists(card,num_ac97)) { + if(num_ac97 == 0) + printk(KERN_ERR "i810_audio: Primary codec not ready.\n"); + break; /* I think this works, if not ready stop */ + } + + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* 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 = i810_ac97_get; + codec->codec_write = i810_ac97_set; + + if(!i810_ac97_probe_and_powerup(card,codec)) { + printk("i810_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 = i810_ac97_get(codec, AC97_EXTENDED_ID); + + if(eid==0xFFFFFF) + { + printk(KERN_WARNING "i810_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 810 hardware engine */ + + if(!(eid&0x0001)) + printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n"); + else + { + if(!i810_ac97_enable_variable_rate(codec)) { + printk(KERN_WARNING "i810_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(eid & 0x200) { /* GOOD, AMAP support */ + if (eid & 0x0080) /* L/R Surround channels */ + total_channels += 2; + if (eid & 0x0140) /* LFE and Center channels */ + total_channels += 2; + printk("i810_audio: AC'97 codec %d supports AMAP, total channels = %d\n", num_ac97, total_channels); + } else if (eid & 0x0400) { /* this only works on 2.2 compliant codecs */ + eid &= 0xffcf; + if((eid & 0xc000) != 0) { + switch ( total_channels ) { + case 2: + /* Set dsa1, dsa0 to 01 */ + eid |= 0x0010; + break; + case 4: + /* Set dsa1, dsa0 to 10 */ + eid |= 0x0020; + break; + case 6: + /* Set dsa1, dsa0 to 11 */ + eid |= 0x0030; + break; + } + total_channels += 2; + } + i810_ac97_set(codec, AC97_EXTENDED_ID, eid); + eid = i810_ac97_get(codec, AC97_EXTENDED_ID); + printk("i810_audio: AC'97 codec %d, new EID value = 0x%04x\n", num_ac97, eid); + if (eid & 0x0080) /* L/R Surround channels */ + total_channels += 2; + if (eid & 0x0140) /* LFE and Center channels */ + total_channels += 2; + printk("i810_audio: AC'97 codec %d, DAC map configured, total channels = %d\n", num_ac97, total_channels); + } else { + printk("i810_audio: AC'97 codec %d Unable to map surround DAC's (or DAC's not present), total channels = %d\n", num_ac97, total_channels); + } + + if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) { + printk(KERN_ERR "i810_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 i810_configure_clocking (void) +{ + struct i810_card *card; + struct i810_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 i810 in a machine? Besides, clocking is global, so unless + * someone actually thinks more than one i810 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 i810_state *) + kmalloc(sizeof(struct i810_state), GFP_KERNEL); + if (state == NULL) + return; + memset(state, 0, sizeof(struct i810_state)); + dmabuf = &state->dmabuf; + + dmabuf->write_channel = card->alloc_pcm_channel(card); + state->virt = 0; + state->card = card; + state->magic = I810_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + dmabuf->fmt = I810_FMT_STEREO | I810_FMT_16BIT; + dmabuf->trigger = PCM_ENABLE_OUTPUT; + i810_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); + save_flags(flags); + cli(); + start_dac(state); + offset = i810_get_dma_addr(state, 0); + mdelay(50); + new_offset = i810_get_dma_addr(state, 0); + stop_dac(state); + outb(2,card->iobase+dmabuf->write_channel->port+OFF_CR); + restore_flags(flags); + i = new_offset - offset; +#ifdef DEBUG + printk("i810_audio: %d bytes in 50 milliseconds\n", i); +#endif + if(i == 0) + goto config_out; + i = i / 4 * 20; + if (i > 48500 || i < 47500) { + clocking = clocking * clocking / i; + printk("i810_audio: setting clocking to %d\n", clocking); + } +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 i810_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + struct i810_card *card; + + if (pci_enable_device(pci_dev)) + return -EIO; + + if (pci_set_dma_mask(pci_dev, I810_DMA_MASK)) { + printk(KERN_ERR "intel810: architecture does not support" + " 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if ((card = kmalloc(sizeof(struct i810_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "i810_audio: out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(*card)); + + card->initializing = 1; + card->iobase = pci_resource_start (pci_dev, 1); + card->ac97base = 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 = I810_CARD_MAGIC; +#ifdef CONFIG_PM + card->pm_suspended=0; +#endif + spin_lock_init(&card->lock); + devs = card; + + pci_set_master(pci_dev); + + printk(KERN_INFO "i810: %s found at IO 0x%04lx and 0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], card->iobase, card->ac97base, + card->irq); + + card->alloc_pcm_channel = i810_alloc_pcm_channel; + card->alloc_rec_pcm_channel = i810_alloc_rec_pcm_channel; + card->alloc_rec_mic_channel = i810_alloc_rec_mic_channel; + card->free_pcm_channel = i810_free_pcm_channel; + + if ((card->channel = pci_alloc_consistent(pci_dev, + sizeof(struct i810_channel)*NR_HW_CH, &card->chandma)) == NULL) { + printk(KERN_ERR "i810: cannot allocate channel DMA memory\n"); + goto out_mem; + } + + { /* We may dispose of this altogether some time soon, so... */ + struct i810_channel *cp = card->channel; + + cp[0].offset = 0; + cp[0].port = 0x00; + cp[0].num = 0; + cp[1].offset = 0; + cp[1].port = 0x10; + cp[1].num = 1; + cp[2].offset = 0; + cp[2].port = 0x20; + cp[2].num = 2; + } + + /* claim our iospace and irq */ + request_region(card->iobase, 64, card_names[pci_id->driver_data]); + request_region(card->ac97base, 256, card_names[pci_id->driver_data]); + + if (request_irq(card->irq, &i810_interrupt, SA_SHIRQ, + card_names[pci_id->driver_data], card)) { + printk(KERN_ERR "i810_audio: unable to allocate irq %d\n", card->irq); + release_region(card->iobase, 64); + release_region(card->ac97base, 256); + goto out_chan; + } + + /* initialize AC97 codec and register /dev/mixer */ + if (i810_ac97_init(card) <= 0) { + release_region(card->iobase, 64); + release_region(card->ac97base, 256); + free_irq(card->irq, card); + goto out_chan; + } + pci_set_drvdata(pci_dev, card); + + if(clocking == 0) { + clocking = 48000; + i810_configure_clocking(); + } + + /* register /dev/dsp */ + if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) { + int i; + printk(KERN_ERR "i810_audio: couldn't register DSP device!\n"); + release_region(card->iobase, 64); + release_region(card->ac97base, 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]); + } + goto out_chan; + } + card->initializing = 0; + return 0; + + out_chan: + pci_free_consistent(pci_dev, sizeof(struct i810_channel)*NR_HW_CH, + card->channel, card->chandma); + out_mem: + kfree(card); + return -ENODEV; +} + +static void __exit i810_remove(struct pci_dev *pci_dev) +{ + int i; + struct i810_card *card = pci_get_drvdata(pci_dev); + /* free hardware resources */ + free_irq(card->irq, devs); + release_region(card->iobase, 64); + release_region(card->ac97base, 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); + kfree (card->ac97_codec[i]); + card->ac97_codec[i] = NULL; + } + unregister_sound_dsp(card->dev_audio); + kfree(card); +} + +#ifdef CONFIG_PM +static int i810_pm_suspend(struct pci_dev *dev, u32 pm_state) +{ + struct i810_card *card = pci_get_drvdata(dev); + struct i810_state *state; + unsigned long flags; + struct dmabuf *dmabuf; + int i,num_ac97; +#ifdef DEBUG + printk("i810_audio: i810_pm_suspend called\n"); +#endif + if(!card) return 0; + spin_lock_irqsave(&card->lock, flags); + card->pm_suspended=1; + for(i=0;istates[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 i810_pm_resume(struct pci_dev *dev) +{ + int num_ac97,i=0; + struct i810_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 */ + + i810_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 || !i810_ac97_exists(card,num_ac97)) { + if(num_ac97) continue; + else BUG(); + } + if(!i810_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(!i810_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;istates[i]; + if(state) { + if(state->pm_saved_adc_rate) + i810_set_adc_rate(state,state->pm_saved_adc_rate); + if(state->pm_saved_dac_rate) + i810_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;istates[i]; + if(state) wake_up(&state->dmabuf.wait); + } + + return 0; +} +#endif /* CONFIG_PM */ + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("Intel 810 audio support"); +MODULE_LICENSE("GPL"); +MODULE_PARM(ftsodell, "i"); +MODULE_PARM(clocking, "i"); +MODULE_PARM(strict_clocking, "i"); +MODULE_PARM(spdif_locked, "i"); + +#define I810_MODULE_NAME "intel810_audio" + +static struct pci_driver i810_pci_driver = { + name: I810_MODULE_NAME, + id_table: i810_pci_tbl, + probe: i810_probe, + remove: i810_remove, +#ifdef CONFIG_PM + suspend: i810_pm_suspend, + resume: i810_pm_resume, +#endif /* CONFIG_PM */ +}; + + +static int __init i810_init_module (void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + + printk(KERN_INFO "Intel 810 + AC97 Audio, version " + DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + if (!pci_register_driver(&i810_pci_driver)) { + pci_unregister_driver(&i810_pci_driver); + return -ENODEV; + } + if(ftsodell != 0) { + printk("i810_audio: ftsodell is now a deprecated option.\n"); + } + if(spdif_locked > 0 ) { + if(spdif_locked == 32000 || spdif_locked == 44100 || spdif_locked == 48000) { + printk("i810_audio: Enabling S/PDIF at sample rate %dHz.\n", spdif_locked); + } else { + printk("i810_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + spdif_locked = 0; + } + } + + return 0; +} + +static void __exit i810_cleanup_module (void) +{ + pci_unregister_driver(&i810_pci_driver); +} + +module_init(i810_init_module); +module_exit(i810_cleanup_module); + +/* +Local Variables: +c-basic-offset: 8 +End: +*/ diff -Nru a/sound/oss/ics2101.c b/sound/oss/ics2101.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ics2101.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,246 @@ +/* + * sound/ics2101.c + * + * Driver for the ICS2101 mixer of GUS v3.7. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Bartlomiej Zolnierkiewicz : added __init to ics2101_mixer_init() + */ +#include +#include "sound_config.h" + +#include + +#include "gus.h" +#include "gus_hw.h" + +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH| \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +extern int *gus_osp; +extern int gus_base; +static int volumes[ICS_MIXDEVS]; +static int left_fix[ICS_MIXDEVS] = +{1, 1, 1, 2, 1, 2}; +static int right_fix[ICS_MIXDEVS] = +{2, 2, 2, 1, 2, 1}; + +static int scale_vol(int vol) +{ + /* + * Experimental volume scaling by Risto Kankkunen. + * This should give smoother volume response than just + * a plain multiplication. + */ + + int e; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + vol = (31 * vol + 50) / 100; + e = 0; + if (vol) + { + while (vol < 16) + { + vol <<= 1; + e--; + } + vol -= 16; + e += 7; + } + return ((e << 4) + vol); +} + +static void write_mix(int dev, int chn, int vol) +{ + int *selector; + unsigned long flags; + int ctrl_addr = dev << 3; + int attn_addr = dev << 3; + + vol = scale_vol(vol); + + if (chn == CHN_LEFT) + { + selector = left_fix; + ctrl_addr |= 0x00; + attn_addr |= 0x02; + } + else + { + selector = right_fix; + ctrl_addr |= 0x01; + attn_addr |= 0x03; + } + + save_flags(flags); + cli(); + outb((ctrl_addr), u_MixSelect); + outb((selector[dev]), u_MixData); + outb((attn_addr), u_MixSelect); + outb(((unsigned char) vol), u_MixData); + restore_flags(flags); +} + +static int set_volumes(int dev, int vol) +{ + int left = vol & 0x00ff; + int right = (vol >> 8) & 0x00ff; + + if (left < 0) + left = 0; + if (left > 100) + left = 100; + if (right < 0) + right = 0; + if (right > 100) + right = 100; + + write_mix(dev, CHN_LEFT, left); + write_mix(dev, CHN_RIGHT, right); + + vol = left + (right << 8); + volumes[dev] = vol; + return vol; +} + +static int ics2101_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val; + + if (((cmd >> 8) & 0xff) == 'M') { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + + if (get_user(val, (int *)arg)) + return -EFAULT; + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl(dev, cmd, arg); + + case SOUND_MIXER_MIC: + val = set_volumes(DEV_MIC, val); + break; + + case SOUND_MIXER_CD: + val = set_volumes(DEV_CD, val); + break; + + case SOUND_MIXER_LINE: + val = set_volumes(DEV_LINE, val); + break; + + case SOUND_MIXER_SYNTH: + val = set_volumes(DEV_GF1, val); + break; + + case SOUND_MIXER_VOLUME: + val = set_volumes(DEV_VOL, val); + break; + + default: + return -EINVAL; + } + return put_user(val, (int *)arg); + } else { + switch (cmd & 0xff) { + /* + * Return parameters + */ + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl(dev, cmd, arg); + + case SOUND_MIXER_DEVMASK: + val = MIX_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; + break; + + case SOUND_MIXER_RECMASK: + val = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + + case SOUND_MIXER_CAPS: + val = 0; + break; + + case SOUND_MIXER_MIC: + val = volumes[DEV_MIC]; + break; + + case SOUND_MIXER_LINE: + val = volumes[DEV_LINE]; + break; + + case SOUND_MIXER_CD: + val = volumes[DEV_CD]; + break; + + case SOUND_MIXER_VOLUME: + val = volumes[DEV_VOL]; + break; + + case SOUND_MIXER_SYNTH: + val = volumes[DEV_GF1]; + break; + + default: + return -EINVAL; + } + return put_user(val, (int *)arg); + } + } + return -EINVAL; +} + +static struct mixer_operations ics2101_mixer_operations = +{ + owner: THIS_MODULE, + id: "ICS2101", + name: "ICS2101 Multimedia Mixer", + ioctl: ics2101_mixer_ioctl +}; + +int __init ics2101_mixer_init(void) +{ + int i; + int n; + + if ((n = sound_alloc_mixerdev()) != -1) + { + mixer_devs[n] = &ics2101_mixer_operations; + + /* + * Some GUS v3.7 cards had some channels flipped. Disable + * the flipping feature if the model id is other than 5. + */ + + if (inb(u_MixSelect) != 5) + { + for (i = 0; i < ICS_MIXDEVS; i++) + left_fix[i] = 1; + for (i = 0; i < ICS_MIXDEVS; i++) + right_fix[i] = 2; + } + set_volumes(DEV_GF1, 0x5a5a); + set_volumes(DEV_CD, 0x5a5a); + set_volumes(DEV_MIC, 0x0000); + set_volumes(DEV_LINE, 0x5a5a); + set_volumes(DEV_VOL, 0x5a5a); + set_volumes(DEV_UNUSED, 0x0000); + } + return n; +} diff -Nru a/sound/oss/ite8172.c b/sound/oss/ite8172.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ite8172.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,1952 @@ +/* + * ite8172.c -- ITE IT8172G Sound Driver. + * + * 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. + * 2. The following support is untested: + * * Memory mapping the audio buffers, and the ioctl controls that go + * with it. + * * S/PDIF output. + * 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 + */ +#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 IT8172_DEBUG +#undef IT8172_VERBOSE_DEBUG +#define DBG(x) {} + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + + +/* + * Audio Controller register bit definitions follow. See + * include/asm/it8172/it8172.h for register offsets. + */ + +/* PCM Out Volume Reg */ +#define PCMOV_PCMOM (1<<15) /* PCM Out Mute default 1: mute */ +#define PCMOV_PCMRCG_BIT 8 /* PCM Right channel Gain */ +#define PCMOV_PCMRCG_MASK (0x1f<= 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; + + if (in_interrupt()) + return; + + 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; + + 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; +} + +static void set_adc_rate(struct it8172_state *s, unsigned rate) +{ + unsigned long flags; + unsigned short sr; + + 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); + + s->adcrate = rate; +} + + +static void set_dac_rate(struct it8172_state *s, unsigned rate) +{ + unsigned long flags; + unsigned short sr; + + 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); + + s->dacrate = rate; +} + + +/* --------------------------------------------------------------------- */ + +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"); + + 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 + + outw(data, s->io+IT_AC_CIRDP); // send data first + outw(circp, s->io+IT_AC_CIRCP); + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void waitcodec(struct ac97_codec *codec) +{ + unsigned short temp; + + /* 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); + } + + // 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; + } +} + + +/* --------------------------------------------------------------------- */ + +extern inline void stop_adc(struct it8172_state *s) +{ + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + unsigned char imc; + + if (db->stopped) + return; + + 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); + + // disable capture interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc | IMC_CCIM, s->io+IT_AC_IMC); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac(struct it8172_state *s) +{ + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + unsigned char imc; + + if (db->stopped) + return; + + 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); + + // disable playback interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc | IMC_PCIM, s->io+IT_AC_IMC); + + db->stopped = 1; + + 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; + + if (!db->stopped) + return; + + 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); + + 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; + + 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; + + 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; + + // 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); + + 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 it8172_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); + 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++) + 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; + 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, db->dmasize); + + // set data length register + outw(db->fragsize, s->io+reg+2); + db->ready = 1; + + return 0; +} + +extern 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); +} + +extern 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); +} + + +/* hold spinlock for the following! */ + +static void 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))) + return; + + /* 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; + + 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->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; + + 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->count -= dac->fragsize; + dac->total_bytes += dac->fragsize; + + /* wake up anybody listening */ + if (waitqueue_active(&dac->wait)) + wake_up_interruptible(&dac->wait); + + if (dac->count <= 0) + stop_dac(s); + } + + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +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; +} + +static int it8172_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 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; + + 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, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct it8172_state *s, int nonblock) +{ + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + 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->dacrate; + tmo >>= sample_shift[(s->pcc & CC_FMT_MASK) >> CC_FMT_BIT]; + it8172_delay(tmo); + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +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; + + 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; + } + + 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 -= bufcnt; + buffer += bufcnt; + ret += bufcnt; + } // while (count > 0) + + 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); + + 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; + } + + 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; + + count -= bufcnt; + buffer += bufcnt; + ret += bufcnt; + } // while (count > 0) + + 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; +} + +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)) { + unlock_kernel(); + return -EAGAIN; + } + db->mapped = 1; + unlock_kernel(); + return 0; +} + + +#ifdef IT8172_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 + +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; + + 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; + + 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 ((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_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_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; + } + 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; + 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; + 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_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_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; + } + + 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; + } + 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); + + 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; +} + +static int it8172_release(struct inode *inode, struct file *file) +{ + 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; +} + +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, +}; + + +/* --------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------- */ + +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef IT8172_DEBUG +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)); + + if (fpos >=len){ + *start = buf; + *eof =1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof =1; + return len; + +} +#endif /* IT8172_DEBUG */ + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int spdif[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_AUTHOR("Monta Vista Software, stevel@mvista.com"); +MODULE_DESCRIPTION("IT8172 AudioPCI97 Driver"); +MODULE_LICENSE("GPL"); + +/* --------------------------------------------------------------------- */ + +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; + } + + 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; + +#ifdef IT8172_DEBUG + /* 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; + 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, + (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); +#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; + + err_dev3: + unregister_sound_mixer(s->codec.dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR PFX "cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, pci_resource_len(pcidev,0)); + err_region: + kfree(s); + return -1; +} + +static void __devinit it8172_remove(struct pci_dev *dev) +{ + struct it8172_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); +#ifdef IT8172_DEBUG + if (s->ps) + remove_proc_entry(IT8172_MODULE_NAME, NULL); +#endif /* IT8172_DEBUG */ + 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); + 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, } +}; + +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 +}; + +static int __init init_it8172(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk("version v0.26 time " __TIME__ " " __DATE__ "\n"); + return pci_module_init(&it8172_driver); +} + +static void __exit cleanup_it8172(void) +{ + printk(KERN_INFO PFX "unloading\n"); + pci_unregister_driver(&it8172_driver); +} + +module_init(init_it8172); +module_exit(cleanup_it8172); + diff -Nru a/sound/oss/iwmem.h b/sound/oss/iwmem.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/iwmem.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,36 @@ +/* + * sound/iwmem.h + * + * DRAM size encoding table for AMD Interwave chip. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Bartlomiej Zolnierkiewicz : added __initdata to mem_decode + */ + + +#define K 1024 +#define M (1024*K) +static int mem_decode[][4] __initdata = +{ +/* Bank0 Bank1 Bank2 Bank3 Encoding bits */ + {256*K, 0, 0, 0}, /* 0 */ + {256*K, 256*K, 0, 0}, /* 1 */ + {256*K, 256*K, 256*K, 256*K}, /* 2 */ + {256*K, 1*M, 0, 0}, /* 3 */ + {256*K, 1*M, 1*M, 1*M}, /* 4 */ + {256*K, 256*K, 1*M, 0}, /* 5 */ + {256*K, 256*K, 1*M, 1*M}, /* 6 */ + {1*M, 0, 0, 0}, /* 7 */ + {1*M, 1*M, 0, 0}, /* 8 */ + {1*M, 1*M, 1*M, 1*M}, /* 9 */ + {4*M, 0, 0, 0}, /* 10 */ + {4*M, 4*M, 0, 0}, /* 11 */ + {4*M, 4*M, 4*M, 4*M} /* 12 */ +}; diff -Nru a/sound/oss/mad16.c b/sound/oss/mad16.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/mad16.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,1074 @@ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * mad16.c + * + * Initialization code for OPTi MAD16 compatible audio chips. Including + * + * OPTi 82C928 MAD16 (replaced by C929) + * OAK OTI-601D Mozart + * OAK OTI-605 Mozart (later version with MPU401 Midi) + * OPTi 82C929 MAD16 Pro + * OPTi 82C930 + * OPTi 82C924 + * + * These audio interface chips don't produce sound themselves. They just + * connect some other components (OPL-[234] and a WSS compatible codec) + * to the PC bus and perform I/O, DMA and IRQ address decoding. There is + * also a UART for the MPU-401 mode (not 82C928/Mozart). + * The Mozart chip appears to be compatible with the 82C928, although later + * issues of the card, using the OTI-605 chip, have an MPU-401 compatable Midi + * port. This port is configured differently to that of the OPTi audio chips. + * + * Changes + * + * Alan Cox Clean up, added module selections. + * + * A. Wik Added support for Opti924 PnP. + * Improved debugging support. 16-May-1998 + * Fixed bug. 16-Jun-1998 + * + * Torsten Duwe Made Opti924 PnP support non-destructive + * 23-Dec-1998 + * + * Paul Grayson Added support for Midi on later Mozart cards. + * 25-Nov-1999 + * Christoph Hellwig Adapted to module_init/module_exit. + * Arnaldo C. de Melo got rid of attach_uart401 21-Sep-2000 + * + * Pavel Rabel Clean up Nov-2000 + */ + +#include +#include +#include + +#include "sound_config.h" + +#include "ad1848.h" +#include "sb.h" +#include "mpu401.h" + +static int mad16_conf; +static int mad16_cdsel; + +static int already_initialized = 0; + +#define C928 1 +#define MOZART 2 +#define C929 3 +#define C930 4 +#define C924 5 + +/* + * Registers + * + * The MAD16 occupies I/O ports 0xf8d to 0xf93 (fixed locations). + * All ports are inactive by default. They can be activated by + * writing 0xE2 or 0xE3 to the password register. The password is valid + * only until the next I/O read or write. + * + * 82C930 uses 0xE4 as the password and indirect addressing to access + * the config registers. + */ + +#define MC0_PORT 0xf8c /* Dummy port */ +#define MC1_PORT 0xf8d /* SB address, CD-ROM interface type, joystick */ +#define MC2_PORT 0xf8e /* CD-ROM address, IRQ, DMA, plus OPL4 bit */ +#define MC3_PORT 0xf8f +#define PASSWD_REG 0xf8f +#define MC4_PORT 0xf90 +#define MC5_PORT 0xf91 +#define MC6_PORT 0xf92 +#define MC7_PORT 0xf93 +#define MC8_PORT 0xf94 +#define MC9_PORT 0xf95 +#define MC10_PORT 0xf96 +#define MC11_PORT 0xf97 +#define MC12_PORT 0xf98 + +static int board_type = C928; + +static int *mad16_osp; +static int c931_detected; /* minor differences from C930 */ +static char c924pnp = 0; /* " " " C924 */ +static int debug = 0; /* debugging output */ + +#ifdef DDB +#undef DDB +#endif +#define DDB(x) {if (debug) x;} + +static unsigned char mad_read(int port) +{ + unsigned long flags; + unsigned char tmp; + + save_flags(flags); + cli(); + + switch (board_type) /* Output password */ + { + case C928: + case MOZART: + outb((0xE2), PASSWD_REG); + break; + + case C929: + outb((0xE3), PASSWD_REG); + break; + + case C930: + /* outb(( 0xE4), PASSWD_REG); */ + break; + + case C924: + /* the c924 has its ports relocated by -128 if + PnP is enabled -aw */ + if (!c924pnp) + outb((0xE5), PASSWD_REG); else + outb((0xE5), PASSWD_REG - 0x80); + break; + } + + if (board_type == C930) + { + outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ + tmp = inb(0xe0f); /* Read from data reg */ + } + else + if (!c924pnp) + tmp = inb(port); else + tmp = inb(port-0x80); + restore_flags(flags); + + return tmp; +} + +static void mad_write(int port, int value) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + switch (board_type) /* Output password */ + { + case C928: + case MOZART: + outb((0xE2), PASSWD_REG); + break; + + case C929: + outb((0xE3), PASSWD_REG); + break; + + case C930: + /* outb(( 0xE4), PASSWD_REG); */ + break; + + case C924: + if (!c924pnp) + outb((0xE5), PASSWD_REG); else + outb((0xE5), PASSWD_REG - 0x80); + break; + } + + if (board_type == C930) + { + outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ + outb(((unsigned char) (value & 0xff)), 0xe0f); + } + else + if (!c924pnp) + outb(((unsigned char) (value & 0xff)), port); else + outb(((unsigned char) (value & 0xff)), port-0x80); + restore_flags(flags); +} + +static int __init detect_c930(void) +{ + unsigned char tmp = mad_read(MC1_PORT); + + if ((tmp & 0x06) != 0x06) + { + DDB(printk("Wrong C930 signature (%x)\n", tmp)); + /* return 0; */ + } + mad_write(MC1_PORT, 0); + + if (mad_read(MC1_PORT) != 0x06) + { + DDB(printk("Wrong C930 signature2 (%x)\n", tmp)); + /* return 0; */ + } + mad_write(MC1_PORT, tmp); /* Restore bits */ + + mad_write(MC7_PORT, 0); + if ((tmp = mad_read(MC7_PORT)) != 0) + { + DDB(printk("MC7 not writable (%x)\n", tmp)); + return 0; + } + mad_write(MC7_PORT, 0xcb); + if ((tmp = mad_read(MC7_PORT)) != 0xcb) + { + DDB(printk("MC7 not writable2 (%x)\n", tmp)); + return 0; + } + + tmp = mad_read(MC0_PORT+18); + if (tmp == 0xff || tmp == 0x00) + return 1; + /* We probably have a C931 */ + DDB(printk("Detected C931 config=0x%02x\n", tmp)); + c931_detected = 1; + + /* + * We cannot configure the chip if it is in PnP mode. + * If we have a CSN assigned (bit 8 in MC13) we first try + * a software reset, then a software power off, finally + * Clearing PnP mode. The last option is not + * Bit 8 in MC13 + */ + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Software reset */ + mad_write(MC9_PORT, 0x02); + mad_write(MC9_PORT, 0x00); + + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Power off, and on again */ + mad_write(MC9_PORT, 0xc2); + mad_write(MC9_PORT, 0xc0); + + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + +#if 0 + /* Force off PnP mode. This is not recommended because + * the PnP bios will not recognize the chip on the next + * warm boot and may assignd different resources to other + * PnP/PCI cards. + */ + mad_write(MC0_PORT+17, 0x04); +#endif + return 1; +} + +static int __init detect_mad16(void) +{ + unsigned char tmp, tmp2, bit; + int i, port; + + /* + * Check that reading a register doesn't return bus float (0xff) + * when the card is accessed using password. This may fail in case + * the card is in low power mode. Normally at least the power saving + * mode bit should be 0. + */ + + if ((tmp = mad_read(MC1_PORT)) == 0xff) + { + DDB(printk("MC1_PORT returned 0xff\n")); + return 0; + } + for (i = 0xf8d; i <= 0xf98; i++) + if (!c924pnp) + DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i))) else + DDB(printk("Port %0x (init value) = %0x\n", i-0x80, mad_read(i))); + + if (board_type == C930) + return detect_c930(); + + /* + * Now check that the gate is closed on first I/O after writing + * the password. (This is how a MAD16 compatible card works). + */ + + if ((tmp2 = inb(MC1_PORT)) == tmp) /* It didn't close */ + { + DDB(printk("MC1_PORT didn't close after read (0x%02x)\n", tmp2)); + return 0; + } + + bit = (c924pnp) ? 0x20 : 0x80; + port = (c924pnp) ? MC2_PORT : MC1_PORT; + + tmp = mad_read(port); + mad_write(port, tmp ^ bit); /* Toggle a bit */ + if ((tmp2 = mad_read(port)) != (tmp ^ bit)) /* Compare the bit */ + { + mad_write(port, tmp); /* Restore */ + DDB(printk("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2)); + return 0; + } + mad_write(port, tmp); /* Restore */ + return 1; /* Bingo */ +} + +static int __init wss_init(struct address_info *hw_config) +{ + int ad_flags = 0; + + /* + * Verify the WSS parameters + */ + + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "MSS: I/O port conflict\n"); + return 0; + } + if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) + return 0; + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (AudioTrix Pro for example) + * return 0x00. + */ + + if ((inb(hw_config->io_base + 3) & 0x3f) != 0x04 && + (inb(hw_config->io_base + 3) & 0x3f) != 0x00) + { + DDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb(hw_config->io_base + 3))); + return 0; + } + if (hw_config->irq > 11) + { + printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) + { + printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma); + return 0; + } + /* + * Check that DMA0 is not in use with a 8 bit board. + */ + + if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + { + printk("MSS: Can't use DMA0 with a 8 bit card/slot\n"); + return 0; + } + if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); + return 1; +} + +static int __init init_c930(struct address_info *hw_config) +{ + unsigned char cfg = 0; + + if(c931_detected) + { + /* Bit 0 has reversd meaning. Bits 1 and 2 sese + reversed on write. + Support only IDE cdrom. IDE port programmed + somewhere else. */ + cfg = (cfg & 0x09) ^ 0x07; + } + + switch (hw_config->io_base) + { + case 0x530: + cfg |= 0x00; + break; + case 0xe80: + cfg |= 0x10; + break; + case 0xf40: + cfg |= 0x20; + break; + case 0x604: + cfg |= 0x30; + break; + default: + printk(KERN_ERR "MAD16: Invalid codec port %x\n", hw_config->io_base); + return 0; + } + mad_write(MC1_PORT, cfg); + + /* MC2 is CD configuration. Don't touch it. */ + + mad_write(MC3_PORT, 0); /* Disable SB mode IRQ and DMA */ + + /* bit 2 of MC4 reverses it's meaning between the C930 + and the C931. */ + cfg = c931_detected ? 0x04 : 0x00; + + mad_write(MC4_PORT, 0x52|cfg); + + mad_write(MC5_PORT, 0x3C); /* Init it into mode2 */ + mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ + mad_write(MC7_PORT, 0xCB); + mad_write(MC10_PORT, 0x11); + + return wss_init(hw_config); +} + +static int __init chip_detect(void) +{ + int i; + + /* + * Then try to detect with the old password + */ + board_type = C924; + + DDB(printk("Detect using password = 0xE5\n")); + + if (detect_mad16()) { + return 1; + } + + board_type = C928; + + DDB(printk("Detect using password = 0xE2\n")); + + if (detect_mad16()) + { + unsigned char model; + + if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) { + DDB(printk("mad16.c: Mozart detected\n")); + board_type = MOZART; + } else { + DDB(printk("mad16.c: 82C928 detected???\n")); + board_type = C928; + } + return 1; + } + + board_type = C929; + + DDB(printk("Detect using password = 0xE3\n")); + + if (detect_mad16()) + { + DDB(printk("mad16.c: 82C929 detected\n")); + return 1; + } + + if (inb(PASSWD_REG) != 0xff) + return 0; + + /* + * First relocate MC# registers to 0xe0e/0xe0f, disable password + */ + + outb((0xE4), PASSWD_REG); + outb((0x80), PASSWD_REG); + + board_type = C930; + + DDB(printk("Detect using password = 0xE4\n")); + + for (i = 0xf8d; i <= 0xf93; i++) + DDB(printk("port %03x = %02x\n", i, mad_read(i))); + + if(detect_mad16()) { + DDB(printk("mad16.c: 82C930 detected\n")); + return 1; + } + + /* The C931 has the password reg at F8D */ + outb((0xE4), 0xF8D); + outb((0x80), 0xF8D); + DDB(printk("Detect using password = 0xE4 for C931\n")); + + if (detect_mad16()) { + return 1; + } + + board_type = C924; + c924pnp++; + DDB(printk("Detect using password = 0xE5 (again), port offset -0x80\n")); + if (detect_mad16()) { + DDB(printk("mad16.c: 82C924 PnP detected\n")); + return 1; + } + + c924pnp=0; + + return 0; +} + +static int __init probe_mad16(struct address_info *hw_config) +{ + int i; + static int valid_ports[] = + { + 0x530, 0xe80, 0xf40, 0x604 + }; + unsigned char tmp; + unsigned char cs4231_mode = 0; + + int ad_flags = 0; + + if (already_initialized) + return 0; + + mad16_osp = hw_config->osp; + + /* + * Check that all ports return 0xff (bus float) when no password + * is written to the password register. + */ + + DDB(printk("--- Detecting MAD16 / Mozart ---\n")); + if (!chip_detect()) + return 0; + + if (board_type == C930) + return init_c930(hw_config); + + + for (i = 0xf8d; i <= 0xf93; i++) + if (!c924pnp) + DDB(printk("port %03x = %02x\n", i, mad_read(i))) else + DDB(printk("port %03x = %02x\n", i-0x80, mad_read(i))); + +/* + * Set the WSS address + */ + + tmp = (mad_read(MC1_PORT) & 0x0f) | 0x80; /* Enable WSS, Disable SB */ + + for (i = 0; i < 5; i++) + { + if (i > 3) /* Not a valid port */ + { + printk(KERN_ERR "MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base); + return 0; + } + if (valid_ports[i] == hw_config->io_base) + { + tmp |= i << 4; /* WSS port select bits */ + break; + } + } + + /* + * Set optional CD-ROM and joystick settings. + */ + + tmp &= ~0x0f; + mad_write(MC1_PORT, tmp); + + tmp = mad_read(MC2_PORT); + + mad_write(MC2_PORT, tmp); + mad_write(MC3_PORT, 0xf0); /* Disable SB */ + + if (board_type == C924) /* Specific C924 init values */ + { + mad_write(MC4_PORT, 0xA0); + mad_write(MC5_PORT, 0x05); + mad_write(MC6_PORT, 0x03); + } + if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) + return 0; + + if (ad_flags & (AD_F_CS4231 | AD_F_CS4248)) + cs4231_mode = 0x02; /* CS4248/CS4231 sync delay switch */ + + if (board_type == C929) + { + mad_write(MC4_PORT, 0xa2); + mad_write(MC5_PORT, 0xA5 | cs4231_mode); + mad_write(MC6_PORT, 0x03); /* Disable MPU401 */ + } + else + { + mad_write(MC4_PORT, 0x02); + mad_write(MC5_PORT, 0x30 | cs4231_mode); + } + + for (i = 0xf8d; i <= 0xf93; i++) if (!c924pnp) + DDB(printk("port %03x after init = %02x\n", i, mad_read(i))) else + DDB(printk("port %03x after init = %02x\n", i-0x80, mad_read(i))); + wss_init(hw_config); + + return 1; +} + +static void __init attach_mad16(struct address_info *hw_config) +{ + + static signed char interrupt_bits[12] = { + -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 + }; + signed char bits; + + static char dma_bits[4] = { + 1, 2, 0, 3 + }; + + int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; + int ad_flags = 0, dma = hw_config->dma, dma2 = hw_config->dma2; + unsigned char dma2_bit = 0; + + already_initialized = 1; + + if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) + return; + + /* + * Set the IRQ and DMA addresses. + */ + + if (board_type == C930 || c924pnp) + interrupt_bits[5] = 0x28; /* Also IRQ5 is possible on C930 */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == -1) + return; + + outb((bits | 0x40), config_port); + if ((inb(version_port) & 0x40) == 0) + printk(KERN_ERR "[IRQ Conflict?]\n"); + + /* + * Handle the capture DMA channel + */ + + if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma) + { + if (!((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0))) + { /* Unsupported combination. Try to swap channels */ + int tmp = dma; + + dma = dma2; + dma2 = tmp; + } + if ((dma == 0 && dma2 == 1) || (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0)) + { + dma2_bit = 0x04; /* Enable capture DMA */ + } + else + { + printk("MAD16: Invalid capture DMA\n"); + dma2 = dma; + } + } + else dma2 = dma; + + outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ + + hw_config->slots[0] = ad1848_init("MAD16 WSS", hw_config->io_base + 4, + hw_config->irq, + dma, + dma2, 0, + hw_config->osp, + THIS_MODULE); + request_region(hw_config->io_base, 4, "MAD16 WSS config"); +} + +static int __init probe_mad16_mpu(struct address_info *hw_config) +{ + static int mpu_attached = 0; + unsigned char tmp; + + if (!already_initialized) /* The MSS port must be initialized first */ + return 0; + + if (mpu_attached) /* Don't let them call this twice */ + return 0; + mpu_attached = 1; + + if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ + { + +#ifdef CONFIG_MAD16_OLDCARD + + tmp = mad_read(MC3_PORT); + + /* + * MAD16 SB base is defined by the WSS base. It cannot be changed + * alone. + * Ignore configured I/O base. Use the active setting. + */ + + if (mad_read(MC1_PORT) & 0x20) + hw_config->io_base = 0x240; + else + hw_config->io_base = 0x220; + + switch (hw_config->irq) + { + case 5: + tmp = (tmp & 0x3f) | 0x80; + break; + case 7: + tmp = (tmp & 0x3f); + break; + case 11: + tmp = (tmp & 0x3f) | 0x40; + break; + default: + printk(KERN_ERR "mad16/Mozart: Invalid MIDI IRQ\n"); + return 0; + } + + mad_write(MC3_PORT, tmp | 0x04); + hw_config->driver_use_1 = SB_MIDI_ONLY; + if (!sb_dsp_detect(hw_config, 0, 0, NULL)) + return 0; + + if (mad_read(MC1_PORT) & 0x20) + hw_config->io_base = 0x240; + else + hw_config->io_base = 0x220; + + hw_config->name = "Mad16/Mozart"; + sb_dsp_init(hw_config, THIS_MODULE); + return 1; +#else + /* assuming all later Mozart cards are identified as + * either 82C928 or Mozart. If so, following code attempts + * to set MPU register. TODO - add probing + */ + + tmp = mad_read(MC8_PORT); + + switch (hw_config->irq) + { + case 5: + tmp |= 0x08; + break; + case 7: + tmp |= 0x10; + break; + case 9: + tmp |= 0x18; + break; + case 10: + tmp |= 0x20; + break; + case 11: + tmp |= 0x28; + break; + default: + printk(KERN_ERR "mad16/MOZART: invalid mpu_irq\n"); + return 0; + } + + switch (hw_config->io_base) + { + case 0x300: + tmp |= 0x01; + break; + case 0x310: + tmp |= 0x03; + break; + case 0x320: + tmp |= 0x05; + break; + case 0x330: + tmp |= 0x07; + break; + default: + printk(KERN_ERR "mad16/MOZART: invalid mpu_io\n"); + return 0; + } + + mad_write(MC8_PORT, tmp); /* write MPU port parameters */ + goto probe_401; +#endif + } + tmp = mad_read(MC6_PORT) & 0x83; + tmp |= 0x80; /* MPU-401 enable */ + + /* Set the MPU base bits */ + + switch (hw_config->io_base) + { + case 0x300: + tmp |= 0x60; + break; + case 0x310: + tmp |= 0x40; + break; + case 0x320: + tmp |= 0x20; + break; + case 0x330: + tmp |= 0x00; + break; + default: + printk(KERN_ERR "MAD16: Invalid MIDI port 0x%x\n", hw_config->io_base); + return 0; + } + + /* Set the MPU IRQ bits */ + + switch (hw_config->irq) + { + case 5: + tmp |= 0x10; + break; + case 7: + tmp |= 0x18; + break; + case 9: + tmp |= 0x00; + break; + case 10: + tmp |= 0x08; + break; + default: + printk(KERN_ERR "MAD16: Invalid MIDI IRQ %d\n", hw_config->irq); + break; + } + + mad_write(MC6_PORT, tmp); /* Write MPU401 config */ + +#ifndef CONFIG_MAD16_OLDCARD +probe_401: +#endif + hw_config->driver_use_1 = SB_MIDI_ONLY; + hw_config->name = "Mad16/Mozart"; + return probe_uart401(hw_config, THIS_MODULE); +} + +static void __exit unload_mad16(struct address_info *hw_config) +{ + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0); + release_region(hw_config->io_base, 4); + sound_unload_audiodev(hw_config->slots[0]); +} + +static void __exit unload_mad16_mpu(struct address_info *hw_config) +{ +#ifdef CONFIG_MAD16_OLDCARD + if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ + { + sb_dsp_unload(hw_config, 0); + return; + } +#endif + + unload_uart401(hw_config); +} + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int found_mpu; + +static int __initdata mpu_io = 0; +static int __initdata mpu_irq = 0; +static int __initdata io = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ +static int __initdata irq = -1; +static int __initdata cdtype = 0; +static int __initdata cdirq = 0; +static int __initdata cdport = 0x340; +static int __initdata cddma = -1; +static int __initdata opl4 = 0; +static int __initdata joystick = 0; + +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM(io,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma16,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(cdtype,"i"); +MODULE_PARM(cdirq,"i"); +MODULE_PARM(cdport,"i"); +MODULE_PARM(cddma,"i"); +MODULE_PARM(opl4,"i"); +MODULE_PARM(joystick,"i"); +MODULE_PARM(debug,"i"); + +static int __initdata dma_map[2][8] = +{ + {0x03, -1, -1, -1, -1, 0x00, 0x01, 0x02}, + {0x03, -1, 0x01, 0x00, -1, -1, -1, -1} +}; + +static int __initdata irq_map[16] = +{ + 0x00, -1, -1, 0x0A, + -1, 0x04, -1, 0x08, + -1, 0x10, 0x14, 0x18, + -1, -1, -1, -1 +}; + +static int __init init_mad16(void) +{ + int dmatype = 0; + + printk(KERN_INFO "MAD16 audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + printk(KERN_INFO "CDROM "); + switch (cdtype) + { + case 0x00: + printk("Disabled"); + cdirq = 0; + break; + case 0x02: + printk("Sony CDU31A"); + dmatype = 1; + if(cddma == -1) cddma = 3; + break; + case 0x04: + printk("Mitsumi"); + dmatype = 0; + if(cddma == -1) cddma = 5; + break; + case 0x06: + printk("Panasonic Lasermate"); + dmatype = 1; + if(cddma == -1) cddma = 3; + break; + case 0x08: + printk("Secondary IDE"); + dmatype = 0; + if(cddma == -1) cddma = 5; + break; + case 0x0A: + printk("Primary IDE"); + dmatype = 0; + if(cddma == -1) cddma = 5; + break; + default: + printk("\n"); + printk(KERN_ERR "Invalid CDROM type\n"); + return -EINVAL; + } + + /* + * Build the config words + */ + + mad16_conf = (joystick ^ 1) | cdtype; + mad16_cdsel = 0; + if (opl4) + mad16_cdsel |= 0x20; + + if(cdtype){ + if (cddma > 7 || cddma < 0 || dma_map[dmatype][cddma] == -1) + { + printk("\n"); + printk(KERN_ERR "Invalid CDROM DMA\n"); + return -EINVAL; + } + if (cddma) + printk(", DMA %d", cddma); + else + printk(", no DMA"); + + if (!cdirq) + printk(", no IRQ"); + else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1) + { + printk(", invalid IRQ (disabling)"); + cdirq = 0; + } + else printk(", IRQ %d", cdirq); + + mad16_cdsel |= dma_map[dmatype][cddma]; + + if (cdtype < 0x08) + { + switch (cdport) + { + case 0x340: + mad16_cdsel |= 0x00; + break; + case 0x330: + mad16_cdsel |= 0x40; + break; + case 0x360: + mad16_cdsel |= 0x80; + break; + case 0x320: + mad16_cdsel |= 0xC0; + break; + default: + printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport); + return -EINVAL; + } + } + mad16_cdsel |= irq_map[cdirq]; + } + + printk(".\n"); + printk(KERN_INFO "Joystick port "); + if (joystick == 1) + printk("enabled.\n"); + else + { + joystick = 0; + printk("disabled.\n"); + } + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma16; + + if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { + printk(KERN_ERR "I/O, DMA and irq are mandatory\n"); + return -EINVAL; + } + + if (!probe_mad16(&cfg)) + return -ENODEV; + + cfg_mpu.io_base = mpu_io; + cfg_mpu.irq = mpu_irq; + + attach_mad16(&cfg); + + found_mpu = probe_mad16_mpu(&cfg_mpu); + return 0; +} + +static void __exit cleanup_mad16(void) +{ + if (found_mpu) + unload_mad16_mpu(&cfg_mpu); + unload_mad16(&cfg); +} + +module_init(init_mad16); +module_exit(cleanup_mad16); + +#ifndef MODULE +static int __init setup_mad16(char *str) +{ + /* io, irq */ + int ints[7]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma16 = ints[4]; + mpu_io = ints[5]; + mpu_irq = ints[6]; + + return 1; +} + +__setup("mad16=", setup_mad16); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/maestro.c b/sound/oss/maestro.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/maestro.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,3807 @@ +/***************************************************************************** + * + * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x + * + * 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. + * + * (c) Copyright 1999 Alan Cox + * + * Based heavily on SonicVibes.c: + * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * Heavily modified by Zach Brown based on lunch + * with ESS engineers. Many thanks to Howard Kim for providing + * contacts and hardware. Honorable mention goes to Eric + * Brombaugh for all sorts of things. Best regards to the + * proprietors of Hack Central for fine lodging. + * + * Supported devices: + * /dev/dsp0-3 standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * + * Hardware Description + * + * A working Maestro setup contains the Maestro chip wired to a + * codec or 2. In the Maestro we have the APUs, the ASSP, and the + * Wavecache. The APUs can be though of as virtual audio routing + * channels. They can take data from a number of sources and perform + * basic encodings of the data. The wavecache is a storehouse for + * PCM data. Typically it deals with PCI and interracts with the + * APUs. The ASSP is a wacky DSP like device that ESS is loth + * to release docs on. Thankfully it isn't required on the Maestro + * until you start doing insane things like FM emulation and surround + * encoding. The codecs are almost always AC-97 compliant codecs, + * but it appears that early Maestros may have had PT101 (an ESS + * part?) wired to them. The only real difference in the Maestro + * families is external goop like docking capability, memory for + * the ASSP, and initialization differences. + * + * Driver Operation + * + * We only drive the APU/Wavecache as typical DACs and drive the + * mixers in the codecs. There are 64 APUs. We assign 6 to each + * /dev/dsp? device. 2 channels for output, and 4 channels for + * input. + * + * Each APU can do a number of things, but we only really use + * 3 basic functions. For playback we use them to convert PCM + * data fetched over PCI by the wavecahche into analog data that + * is handed to the codec. One APU for mono, and a pair for stereo. + * When in stereo, the combination of smarts in the APU and Wavecache + * decide which wavecache gets the left or right channel. + * + * For record we still use the old overly mono system. For each in + * coming channel the data comes in from the codec, through a 'input' + * APU, through another rate converter APU, and then into memory via + * the wavecache and PCI. If its stereo, we mash it back into LRLR in + * software. The pass between the 2 APUs is supposedly what requires us + * to have a 512 byte buffer sitting around in wavecache/memory. + * + * The wavecache makes our life even more fun. First off, it can + * only address the first 28 bits of PCI address space, making it + * useless on quite a few architectures. Secondly, its insane. + * It claims to fetch from 4 regions of PCI space, each 4 meg in length. + * But that doesn't really work. You can only use 1 region. So all our + * allocations have to be in 4meg of each other. Booo. Hiss. + * So we have a module parameter, dsps_order, that is the order of + * the number of dsps to provide. All their buffer space is allocated + * on open time. The sonicvibes OSS routines we inherited really want + * power of 2 buffers, so we have all those next to each other, then + * 512 byte regions for the recording wavecaches. This ends up + * wasting quite a bit of memory. The only fixes I can see would be + * getting a kernel allocator that could work in zones, or figuring out + * just how to coerce the WP into doing what we want. + * + * The indirection of the various registers means we have to spinlock + * nearly all register accesses. We have the main register indirection + * like the wave cache, maestro registers, etc. Then we have beasts + * like the APU interface that is indirect registers gotten at through + * the main maestro indirection. Ouch. We spinlock around the actual + * ports on a per card basis. This means spinlock activity at each IO + * operation, but the only IO operation clusters are in non critical + * paths and it makes the code far easier to follow. Interrupts are + * blocked while holding the locks because the int handler has to + * get at some of them :(. The mixer interface doesn't, however. + * We also have an OSS state lock that is thrown around in a few + * places. + * + * This driver has brute force APM suspend support. We catch suspend + * notifications and stop all work being done on the chip. Any people + * that try between this shutdown and the real suspend operation will + * be put to sleep. When we resume we restore our software state on + * the chip and wake up the people that were using it. The code thats + * being used now is quite dirty and assumes we're on a uni-processor + * machine. Much of it will need to be cleaned up for SMP ACPI or + * similar. + * + * We also pay attention to PCI power management now. The driver + * will power down units of the chip that it knows aren't needed. + * The WaveProcessor and company are only powered on when people + * have /dev/dsp*s open. On removal the driver will + * power down the maestro entirely. There could still be + * trouble with BIOSen that magically change power states + * themselves, but we'll see. + * + * History + * v0.15 - May 21 2001 - Marcus Meissner + * Ported to Linux 2.4 PCI API. Some clean ups, global devs list + * removed (now using pci device driver data). + * PM needs to be polished still. Bumped version. + * (still kind of v0.14) May 13 2001 - Ben Pfaff + * Add support for 978 docking and basic hardware volume control + * (still kind of v0.14) Nov 23 - Alan Cox + * Add clocking= for people with seriously warped hardware + * (still v0.14) Nov 10 2000 - Bartlomiej Zolnierkiewicz + * add __init to maestro_ac97_init() and maestro_install() + * (still based on v0.14) Mar 29 2000 - Zach Brown + * move to 2.3 power management interface, which + * required hacking some suspend/resume/check paths + * make static compilation work + * v0.14 - Jan 28 2000 - Zach Brown + * add PCI power management through ACPI regs. + * we now shut down on machine reboot/halt + * leave scary PCI config items alone (isa stuff, mostly) + * enable 1921s, it seems only mine was broke. + * fix swapped left/right pcm dac. har har. + * up bob freq, increase buffers, fix pointers at underflow + * silly compilation problems + * v0.13 - Nov 18 1999 - Zach Brown + * fix nec Versas? man would that be cool. + * v0.12 - Nov 12 1999 - Zach Brown + * brown bag volume max fix.. + * v0.11 - Nov 11 1999 - Zach Brown + * use proper stereo apu decoding, mmap/write should work. + * make volume sliders more useful, tweak rate calculation. + * fix lame 8bit format reporting bug. duh. apm apu saving buglet also + * fix maestro 1 clock freq "bug", remove pt101 support + * v0.10 - Oct 28 1999 - Zach Brown + * aha, so, sometimes the WP writes a status word to offset 0 + * from one of the PCMBARs. rearrange allocation accordingly.. + * cheers again to Eric for being a good hacker in investigating this. + * Jeroen Hoogervorst submits 7500 fix out of nowhere. yay. :) + * v0.09 - Oct 23 1999 - Zach Brown + * added APM support. + * re-order something such that some 2Es now work. Magic! + * new codec reset routine. made some codecs come to life. + * fix clear_advance, sync some control with ESS. + * now write to all base regs to be paranoid. + * v0.08 - Oct 20 1999 - Zach Brown + * Fix initial buflen bug. I am so smart. also smp compiling.. + * I owe Eric yet another beer: fixed recmask, igain, + * muting, and adc sync consistency. Go Team. + * v0.07 - Oct 4 1999 - Zach Brown + * tweak adc/dac, formating, and stuff to allow full duplex + * allocate dsps memory at open() so we can fit in the wavecache window + * fix wavecache braindamage. again. no more scribbling? + * fix ess 1921 codec bug on some laptops. + * fix dumb pci scanning bug + * started 2.3 cleanup, redid spinlocks, little cleanups + * v0.06 - Sep 20 1999 - Zach Brown + * fix wavecache thinkos. limit to 1 /dev/dsp. + * eric is wearing his thinking toque this week. + * spotted apu mode bugs and gain ramping problem + * don't touch weird mixer regs, make recmask optional + * fixed igain inversion, defaults for mixers, clean up rec_start + * make mono recording work. + * report subsystem stuff, please send reports. + * littles: parallel out, amp now + * v0.05 - Sep 17 1999 - Zach Brown + * merged and fixed up Eric's initial recording code + * munged format handling to catch misuse, needs rewrite. + * revert ring bus init, fixup shared int, add pci busmaster setting + * fix mixer oss interface, fix mic mute and recmask + * mask off unsupported mixers, reset with all 1s, modularize defaults + * make sure bob is running while we need it + * got rid of device limit, initial minimal apm hooks + * pull out dead code/includes, only allow multimedia/audio maestros + * v0.04 - Sep 01 1999 - Zach Brown + * copied memory leak fix from sonicvibes driver + * different ac97 reset, play with 2.0 ac97, simplify ring bus setup + * bob freq code, region sanity, jitter sync fix; all from Eric + * + * TODO + * fix bob frequency + * endianness + * do smart things with ac97 2.0 bits. + * dual codecs + * leave 54->61 open + * + * it also would be fun to have a mode that would not use pci dma at all + * but would copy into the wavecache on board memory and use that + * on architectures that don't like the maestro's pci dma ickiness. + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +static int maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *d); + +#include "maestro.h" + +static struct pci_driver maestro_pci_driver; + +/* --------------------------------------------------------------------- */ + +#define M_DEBUG 1 + +#ifdef M_DEBUG +static int debug=0; +#define M_printk(args...) {if (debug) printk(args);} +#else +#define M_printk(x) +#endif + +/* we try to setup 2^(dsps_order) /dev/dsp devices */ +static int dsps_order=0; +/* wether or not we mess around with power management */ +static int use_pm=2; /* set to 1 for force */ +/* clocking for broken hardware - a few laptops seem to use a 50Khz clock + ie insmod with clocking=50000 or so */ + +static int clocking=48000; + +MODULE_AUTHOR("Zach Brown , Alan Cox "); +MODULE_DESCRIPTION("ESS Maestro Driver"); +MODULE_LICENSE("GPL"); + +#ifdef M_DEBUG +MODULE_PARM(debug,"i"); +#endif +MODULE_PARM(dsps_order,"i"); +MODULE_PARM(use_pm,"i"); +MODULE_PARM(clocking, "i"); + +/* --------------------------------------------------------------------- */ +#define DRIVER_VERSION "0.15" + +#ifndef PCI_VENDOR_ESS +#define PCI_VENDOR_ESS 0x125D +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 /* Maestro 2 */ +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 /* Maestro 2E */ + +#define PCI_VENDOR_ESS_OLD 0x1285 /* Platform Tech, + the people the maestro + was bought from */ +#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 /* maestro 1 */ +#endif /* PCI_VENDOR_ESS */ + +#define ESS_CHAN_HARD 0x100 + +/* NEC Versas ? */ +#define NEC_VERSA_SUBID1 0x80581033 +#define NEC_VERSA_SUBID2 0x803c1033 + + +/* changed so that I could actually find all the + references and fix them up. its a little more readable now. */ +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 +#define ESS_FMT_MASK 0x03 +#define ESS_DAC_SHIFT 0 +#define ESS_ADC_SHIFT 4 + +#define ESS_STATE_MAGIC 0x125D1968 +#define ESS_CARD_MAGIC 0x19283746 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define MAX_DSP_ORDER 2 +#define MAX_DSPS (1<src buffer page */ + void *mixbuf; + +}; + +struct ess_card { + unsigned int magic; + + /* We keep maestro cards in a linked list */ + struct ess_card *next; + + int dev_mixer; + + int card_type; + + /* as most of this is static, + perhaps it should be a pointer to a global struct */ + struct mixer_goo { + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + /* the caller must guarantee arg sanity before calling these */ +/* int (*read_mixer)(struct ess_card *card, int index);*/ + void (*write_mixer)(struct ess_card *card,int mixer, unsigned int left,unsigned int right); + int (*recmask_io)(struct ess_card *card,int rw,int mask); + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; + } mix; + + int power_regs; + + int in_suspend; + wait_queue_head_t suspend_queue; + + struct ess_state channels[MAX_DSPS]; + u16 maestro_map[NR_IDRS]; /* Register map */ + /* we have to store this junk so that we can come back from a + suspend */ + u16 apu_map[NR_APUS][NR_APU_REGS]; /* contents of apu regs */ + + /* this locks around the physical registers on the card */ + spinlock_t lock; + + /* memory for this card.. wavecache limited :(*/ + void *dmapages; + int dmaorder; + + /* hardware resources */ + struct pci_dev *pcidev; + u32 iobase; + u32 irq; + + int bob_freq; + char dsps_open; + + int dock_mute_vol; +}; + +static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ); + +static 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; +} + + +/* --------------------------------------------------------------------- */ + +static void check_suspend(struct ess_card *card); + +/* --------------------------------------------------------------------- */ + + +/* + * ESS Maestro AC97 codec programming interface. + */ + +static void maestro_ac97_set(struct ess_card *card, u8 cmd, u16 val) +{ + int io = card->iobase; + int i; + /* + * Wait for the codec bus to be free + */ + + check_suspend(card); + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + /* + * Write the bus + */ + outw(val, io+ESS_AC97_DATA); + mdelay(1); + outb(cmd, io+ESS_AC97_INDEX); + mdelay(1); +} + +static u16 maestro_ac97_get(struct ess_card *card, u8 cmd) +{ + int io = card->iobase; + int sanity=10000; + u16 data; + int i; + + check_suspend(card); + /* + * Wait for the codec bus to be free + */ + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + + outb(cmd|0x80, io+ESS_AC97_INDEX); + mdelay(1); + + while(inb(io+ESS_AC97_INDEX)&1) + { + sanity--; + if(!sanity) + { + printk(KERN_ERR "maestro: ac97 codec timeout reading 0x%x.\n",cmd); + return 0; + } + } + data=inw(io+ESS_AC97_DATA); + mdelay(1); + return data; +} + +/* OSS interface to the ac97s.. */ + +#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\ + SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\ + SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN) + +#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ + SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\ + SOUND_MASK_SPEAKER) + +#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ + SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ + SOUND_MASK_PHONEIN) + +#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<offset); + + if(AC97_STEREO_MASK & (1<> 8) & 0x7f; + right = val & 0x7f; + + if (mixer == SOUND_MIXER_IGAIN) { + right = (right * 100) / mh->scale; + left = (left * 100) / mh->scale; + else { + right = 100 - ((right * 100) / mh->scale); + left = 100 - ((left * 100) / mh->scale); + } + + ret = left | (right << 8); + } else if (mixer == SOUND_MIXER_SPEAKER) { + ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); + } else if (mixer == SOUND_MIXER_MIC) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); + } else if (mixer == SOUND_MIXER_TREBLE) { + ret = 100 - (((val & 0xe) * 100) / mh->scale); + } + + M_printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret); + + return ret; +} +#endif + +/* write the OSS encoded volume to the given OSS encoded mixer, + again caller's job to make sure all is well in arg land, + call with spinlock held */ + +/* linear scale -> log */ +static unsigned char lin2log[101] = +{ +0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 , +50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 , +63 , 65 , 66 , 67 , 68 , 69 , 69 , 70 , 71 , +72 , 73 , 73 , 74 , 75 , 75 , 76 , 77 , 77 , +78 , 78 , 79 , 80 , 80 , 81 , 81 , 82 , 82 , +83 , 83 , 84 , 84 , 84 , 85 , 85 , 86 , 86 , +87 , 87 , 87 , 88 , 88 , 88 , 89 , 89 , 89 , +90 , 90 , 90 , 91 , 91 , 91 , 92 , 92 , 92 , +93 , 93 , 93 , 94 , 94 , 94 , 94 , 95 , 95 , +95 , 95 , 96 , 96 , 96 , 96 , 97 , 97 , 97 , +97 , 98 , 98 , 98 , 98 , 99 , 99 , 99 , 99 , 99 +}; + +static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, unsigned int right) +{ + u16 val=0; + struct ac97_mixer_hw *mh = &ac97_hw[mixer]; + + M_printk("wrote mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right); + + if(AC97_STEREO_MASK & (1<scale) / 100; + left = (left * mh->scale) / 100; + if ((left == 0) && (right == 0)) + val |= 0x8000; + } else { + /* log conversion for the stereo controls */ + if((left == 0) && (right == 0)) + val = 0x8000; + right = ((100 - lin2log[right]) * mh->scale) / 100; + left = ((100 - lin2log[left]) * mh->scale) / 100; + } + + val |= (left << 8) | right; + + } else if (mixer == SOUND_MIXER_SPEAKER) { + val = (((100 - left) * mh->scale) / 100) << 1; + } else if (mixer == SOUND_MIXER_MIC) { + val = maestro_ac97_get(card, mh->offset) & ~0x801f; + val |= (((100 - left) * mh->scale) / 100); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + val = maestro_ac97_get(card , mh->offset) & ~0x0f00; + val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; + } else if (mixer == SOUND_MIXER_TREBLE) { + val = maestro_ac97_get(card , mh->offset) & ~0x000f; + val |= (((100 - left) * mh->scale) / 100) & 0x000e; + } + + maestro_ac97_set(card , mh->offset, val); + + M_printk(" -> %x\n",val); +} + +/* the following tables allow us to go from + OSS <-> ac97 quickly. */ + +enum ac97_recsettings { + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static unsigned int ac97_oss_mask[] = { + [AC97_REC_MIC] = SOUND_MASK_MIC, + [AC97_REC_CD] = SOUND_MASK_CD, + [AC97_REC_VIDEO] = SOUND_MASK_VIDEO, + [AC97_REC_AUX] = SOUND_MASK_LINE1, + [AC97_REC_LINE] = SOUND_MASK_LINE, + [AC97_REC_PHONE] = SOUND_MASK_PHONEIN +}; + +/* indexed by bit position */ +static unsigned int ac97_oss_rm[] = { + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + +/* read or write the recmask + the ac97 can really have left and right recording + 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 ess_card *card, int read, int mask) +{ + unsigned int val = ac97_oss_mask[ maestro_ac97_get(card, 0x1a) & 0x7 ]; + + if (read) return val; + + /* oss can have many inputs, maestro cant. try + to pick the 'new' one */ + + if (mask != val) mask &= ~val; + + val = ffs(mask) - 1; + val = ac97_oss_rm[val]; + val |= val << 8; /* set both channels */ + + M_printk("maestro: setting ac97 recmask to 0x%x\n",val); + + maestro_ac97_set(card,0x1a,val); + + return 0; +}; + +/* + * The Maestro can be wired to a standard AC97 compliant codec + * (see www.intel.com for the pdf's on this), or to a PT101 codec + * which appears to be the ES1918 (data sheet on the esstech.com.tw site) + * + * The PT101 setup is untested. + */ + +static u16 __init maestro_ac97_init(struct ess_card *card) +{ + u16 vend1, vend2, caps; + + card->mix.supported_mixers = AC97_SUPPORTED_MASK; + card->mix.stereo_mixers = AC97_STEREO_MASK; + card->mix.record_sources = AC97_RECORD_MASK; +/* card->mix.read_mixer = ac97_read_mixer;*/ + card->mix.write_mixer = ac97_write_mixer; + card->mix.recmask_io = ac97_recmask_io; + + vend1 = maestro_ac97_get(card, 0x7c); + vend2 = maestro_ac97_get(card, 0x7e); + + caps = maestro_ac97_get(card, 0x00); + + printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n", + vend1,vend2,caps,maestro_ac97_get(card,0x26) & 0xf); + + if (! (caps & 0x4) ) { + /* no bass/treble nobs */ + card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + } + + /* XXX endianness, dork head. */ + /* vendor specifc bits.. */ + switch ((long)(vend1 << 16) | vend2) { + case 0x545200ff: /* TriTech */ + /* no idea what this does */ + maestro_ac97_set(card,0x2a,0x0001); + maestro_ac97_set(card,0x2c,0x0000); + maestro_ac97_set(card,0x2c,0xffff); + break; +#if 0 /* i thought the problems I was seeing were with + the 1921, but apparently they were with the pci board + it was on, so this code is commented out. + lets see if this holds true. */ + case 0x83847609: /* ESS 1921 */ + /* writing to 0xe (mic) or 0x1a (recmask) seems + to hang this codec */ + card->mix.supported_mixers &= ~(SOUND_MASK_MIC); + card->mix.record_sources = 0; + card->mix.recmask_io = NULL; +#if 0 /* don't ask. I have yet to see what these actually do. */ + maestro_ac97_set(card,0x76,0xABBA); /* o/~ Take a chance on me o/~ */ + udelay(20); + maestro_ac97_set(card,0x78,0x3002); + udelay(20); + maestro_ac97_set(card,0x78,0x3802); + udelay(20); +#endif + break; +#endif + default: break; + } + + maestro_ac97_set(card, 0x1E, 0x0404); + /* null misc stuff */ + maestro_ac97_set(card, 0x20, 0x0000); + + return 0; +} + +#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 :) */ +static u16 maestro_pt101_init(struct ess_card *card,int iobase) +{ + printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); + /* who knows.. */ + maestro_ac97_set(iobase, 0x2A, 0x0001); + maestro_ac97_set(iobase, 0x2C, 0x0000); + maestro_ac97_set(iobase, 0x2C, 0xFFFF); + maestro_ac97_set(iobase, 0x10, 0x9F1F); + maestro_ac97_set(iobase, 0x12, 0x0808); + maestro_ac97_set(iobase, 0x14, 0x9F1F); + maestro_ac97_set(iobase, 0x16, 0x9F1F); + maestro_ac97_set(iobase, 0x18, 0x0404); + maestro_ac97_set(iobase, 0x1A, 0x0000); + maestro_ac97_set(iobase, 0x1C, 0x0000); + maestro_ac97_set(iobase, 0x02, 0x0404); + maestro_ac97_set(iobase, 0x04, 0x0808); + maestro_ac97_set(iobase, 0x0C, 0x801F); + maestro_ac97_set(iobase, 0x0E, 0x801F); + return 0; +} +#endif + +/* this is very magic, and very slow.. */ +static void +maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev) +{ + u16 save_68; + u16 w; + u32 vend; + + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + + /* reset the first codec */ + outw(0x0000, ioaddr+0x36); + save_68 = inw(ioaddr+0x68); + pci_read_config_word(pcidev, 0x58, &w); /* something magical with gpio and bus arb. */ + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &vend); + if( w & 0x1) + save_68 |= 0x10; + outw(0xfffe, ioaddr + 0x64); /* tickly gpio 0.. */ + outw(0x0001, ioaddr + 0x68); + outw(0x0000, ioaddr + 0x60); + udelay(20); + outw(0x0001, ioaddr + 0x60); + mdelay(20); + + outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ + outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38); + outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a); + outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c); + + /* now the second codec */ + outw(0x0000, ioaddr+0x36); + outw(0xfff7, ioaddr + 0x64); + save_68 = inw(ioaddr+0x68); + outw(0x0009, ioaddr + 0x68); + outw(0x0001, ioaddr + 0x60); + udelay(20); + outw(0x0009, ioaddr + 0x60); + mdelay(500); /* .. ouch.. */ + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + +#if 0 /* the loop here needs to be much better if we want it.. */ + M_printk("trying software reset\n"); + /* try and do a software reset */ + outb(0x80|0x7c, ioaddr + 0x30); + for (w=0; ; w++) { + if ((inw(ioaddr+ 0x30) & 1) == 0) { + if(inb(ioaddr + 0x32) !=0) break; + + outb(0x80|0x7d, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + outb(0x80|0x7f, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + } + + if( w > 10000) { + outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ + mdelay(500); /* oh my.. */ + outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); + udelay(1); + outw( 0x80, ioaddr+0x30); + for(w = 0 ; w < 10000; w++) { + if((inw(ioaddr + 0x30) & 1) ==0) break; + } + } + } +#endif + if ( vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { + /* turn on external amp? */ + outw(0xf9ff, ioaddr + 0x64); + outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68); + outw(0x0209, ioaddr + 0x60); + } + + /* Turn on the 978 docking chip. + First frob the "master output enable" bit, + then set most of the playback volume control registers to max. */ + outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); + outb(0xff, ioaddr+0xc3); + outb(0xff, ioaddr+0xc4); + outb(0xff, ioaddr+0xc6); + outb(0xff, ioaddr+0xc8); + outb(0x3f, ioaddr+0xcf); + outb(0x3f, ioaddr+0xd0); +} +/* + * Indirect register access. Not all registers are readable so we + * need to keep register state ourselves + */ + +#define WRITEABLE_MAP 0xEFFFFF +#define READABLE_MAP 0x64003F + +/* + * The Maestro engineers were a little indirection happy. These indirected + * registers themselves include indirect registers at another layer + */ + +static void __maestro_write(struct ess_card *card, u16 reg, u16 data) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + outw(data, ioaddr+0x00); + if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg); + else card->maestro_map[reg]=data; + +} + +static void maestro_write(struct ess_state *s, u16 reg, u16 data) +{ + unsigned long flags; + + check_suspend(s->card); + spin_lock_irqsave(&s->card->lock,flags); + + __maestro_write(s->card,reg,data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 __maestro_read(struct ess_card *card, u16 reg) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + return card->maestro_map[reg]=inw(ioaddr+0x00); +} + +static u16 maestro_read(struct ess_state *s, u16 reg) +{ + if(READABLE_MAP & (1<card); + spin_lock_irqsave(&s->card->lock,flags); + + __maestro_read(s->card,reg); + + spin_unlock_irqrestore(&s->card->lock,flags); + } + return s->card->maestro_map[reg]; +} + +/* + * These routines handle accessing the second level indirections to the + * wave ram. + */ + +/* + * The register names are the ones ESS uses (see 104T31.ZIP) + */ + +#define IDR0_DATA_PORT 0x00 +#define IDR1_CRAM_POINTER 0x01 +#define IDR2_CRAM_DATA 0x02 +#define IDR3_WAVE_DATA 0x03 +#define IDR4_WAVE_PTR_LOW 0x04 +#define IDR5_WAVE_PTR_HI 0x05 +#define IDR6_TIMER_CTRL 0x06 +#define IDR7_WAVE_ROMRAM 0x07 + +static void apu_index_set(struct ess_card *card, u16 index) +{ + int i; + __maestro_write(card, IDR1_CRAM_POINTER, index); + for(i=0;i<1000;i++) + if(__maestro_read(card, IDR1_CRAM_POINTER)==index) + return; + printk(KERN_WARNING "maestro: APU register select failed.\n"); +} + +static void apu_data_set(struct ess_card *card, u16 data) +{ + int i; + for(i=0;i<1000;i++) + { + if(__maestro_read(card, IDR0_DATA_PORT)==data) + return; + __maestro_write(card, IDR0_DATA_PORT, data); + } +} + +/* + * This is the public interface for APU manipulation. It handles the + * interlock to avoid two APU writes in parallel etc. Don't diddle + * directly with the stuff above. + */ + +static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data) +{ + unsigned long flags; + + check_suspend(s->card); + + if(channel&ESS_CHAN_HARD) + channel&=~ESS_CHAN_HARD; + else + { + if(channel>5) + printk("BAD CHANNEL %d.\n",channel); + else + channel = s->apu[channel]; + /* store based on real hardware apu/reg */ + s->card->apu_map[channel][reg]=data; + } + reg|=(channel<<4); + + /* hooray for double indirection!! */ + spin_lock_irqsave(&s->card->lock,flags); + + apu_index_set(s->card, reg); + apu_data_set(s->card, data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg) +{ + unsigned long flags; + u16 v; + + check_suspend(s->card); + + if(channel&ESS_CHAN_HARD) + channel&=~ESS_CHAN_HARD; + else + channel = s->apu[channel]; + + reg|=(channel<<4); + + spin_lock_irqsave(&s->card->lock,flags); + + apu_index_set(s->card, reg); + v=__maestro_read(s->card, IDR0_DATA_PORT); + + spin_unlock_irqrestore(&s->card->lock,flags); + return v; +} + + +/* + * The wavecache buffers between the APUs and + * pci bus mastering + */ + +static void wave_set_register(struct ess_state *s, u16 reg, u16 value) +{ + long ioaddr = s->card->iobase; + unsigned long flags; + check_suspend(s->card); + + spin_lock_irqsave(&s->card->lock,flags); + + outw(reg, ioaddr+0x10); + outw(value, ioaddr+0x12); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 wave_get_register(struct ess_state *s, u16 reg) +{ + long ioaddr = s->card->iobase; + unsigned long flags; + u16 value; + check_suspend(s->card); + + spin_lock_irqsave(&s->card->lock,flags); + outw(reg, ioaddr+0x10); + value=inw(ioaddr+0x12); + spin_unlock_irqrestore(&s->card->lock,flags); + + return value; +} + +static void sound_reset(int ioaddr) +{ + outw(0x2000, 0x18+ioaddr); + udelay(1); + outw(0x0000, 0x18+ioaddr); + udelay(1); +} + +/* sets the play formats of these apus, should be passed the already shifted format */ +static void set_apu_fmt(struct ess_state *s, int apu, int mode) +{ + int apu_fmt = 0x10; + + if(!(mode&ESS_FMT_16BIT)) apu_fmt+=0x20; + if((mode&ESS_FMT_STEREO)) apu_fmt+=0x10; + s->apu_mode[apu] = apu_fmt; + s->apu_mode[apu+1] = apu_fmt; +} + +/* this only fixes the output apu mode to be later set by start_dac and + company. output apu modes are set in ess_rec_setup */ +static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data) +{ + s->fmt = (s->fmt & mask) | data; + set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK); +} + +/* this is off by a little bit.. */ +static u32 compute_rate(struct ess_state *s, u32 freq) +{ + u32 clock = clock_freq[s->card->card_type]; + + freq = (freq * clocking)/48000; + + if (freq == 48000) + return 0x10000; + + return ((freq / clock) <<16 )+ + (((freq % clock) << 16) / clock); +} + +static void set_dac_rate(struct ess_state *s, unsigned int rate) +{ + u32 freq; + int fmt = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + s->ratedac = rate; + + if(! (fmt & ESS_FMT_16BIT) && !(fmt & ESS_FMT_STEREO)) + rate >>= 1; + +/* M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/ + + freq = compute_rate(s, rate); + + /* Load the frequency, turn on 6dB */ + apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 0, 3, freq>>8); + apu_set_register(s, 1, 2,(apu_get_register(s, 1, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 1, 3, freq>>8); +} + +static void set_adc_rate(struct ess_state *s, unsigned rate) +{ + u32 freq; + + /* Sample Rate conversion APUs don't like 0x10000 for their rate */ + if (rate > 47999) + rate = 47999; + if (rate < 4000) + rate = 4000; + + s->rateadc = rate; + + freq = compute_rate(s, rate); + + /* Load the frequency, turn on 6dB */ + apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 2, 3, freq>>8); + apu_set_register(s, 3, 2,(apu_get_register(s, 3, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 3, 3, freq>>8); + + /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ + freq = 0x10000; + + apu_set_register(s, 4, 2,(apu_get_register(s, 4, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 4, 3, freq>>8); + apu_set_register(s, 5, 2,(apu_get_register(s, 5, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 5, 3, freq>>8); +} + +/* Stop our host of recording apus */ +static inline void stop_adc(struct ess_state *s) +{ + /* XXX lets hope we don't have to lock around this */ + if (! (s->enable & ADC_RUNNING)) return; + + s->enable &= ~ADC_RUNNING; + apu_set_register(s, 2, 0, apu_get_register(s, 2, 0)&0xFF0F); + apu_set_register(s, 3, 0, apu_get_register(s, 3, 0)&0xFF0F); + apu_set_register(s, 4, 0, apu_get_register(s, 2, 0)&0xFF0F); + apu_set_register(s, 5, 0, apu_get_register(s, 3, 0)&0xFF0F); +} + +/* stop output apus */ +static void stop_dac(struct ess_state *s) +{ + /* XXX have to lock around this? */ + if (! (s->enable & DAC_RUNNING)) return; + + s->enable &= ~DAC_RUNNING; + apu_set_register(s, 0, 0, apu_get_register(s, 0, 0)&0xFF0F); + apu_set_register(s, 1, 0, apu_get_register(s, 1, 0)&0xFF0F); +} + +static void start_dac(struct ess_state *s) +{ + /* XXX locks? */ + if ( (s->dma_dac.mapped || s->dma_dac.count > 0) && + s->dma_dac.ready && + (! (s->enable & DAC_RUNNING)) ) { + + s->enable |= DAC_RUNNING; + + apu_set_register(s, 0, 0, + (apu_get_register(s, 0, 0)&0xFF0F)|s->apu_mode[0]); + + if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) + apu_set_register(s, 1, 0, + (apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]); + } +} + +static void start_adc(struct ess_state *s) +{ + /* XXX locks? */ + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready && (! (s->enable & ADC_RUNNING)) ) { + + s->enable |= ADC_RUNNING; + apu_set_register(s, 2, 0, + (apu_get_register(s, 2, 0)&0xFF0F)|s->apu_mode[2]); + apu_set_register(s, 4, 0, + (apu_get_register(s, 4, 0)&0xFF0F)|s->apu_mode[4]); + + if( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + apu_set_register(s, 3, 0, + (apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]); + apu_set_register(s, 5, 0, + (apu_get_register(s, 5, 0)&0xFF0F)|s->apu_mode[5]); + } + + } +} + + +/* + * Native play back driver + */ + +/* the mode passed should be already shifted and masked */ +static void +ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) +{ + u32 pa; + u32 tmpval; + int high_apu = 0; + int channel; + + M_printk("mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + + /* all maestro sizes are in 16bit words */ + size >>=1; + + if(mode&ESS_FMT_STEREO) { + high_apu++; + /* only 16/stereo gets size divided */ + if(mode&ESS_FMT_16BIT) + size>>=1; + } + + for(channel=0; channel <= high_apu; channel++) + { + pa = virt_to_bus(buffer); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + if(!(mode & ESS_FMT_16BIT)) tmpval |= 4; + if(mode & ESS_FMT_STEREO) tmpval |= 2; + ess->apu_base[channel]=tmpval; + wave_set_register(ess, ess->apu[channel]<<3, tmpval); + + pa -= virt_to_bus(ess->card->dmapages); + pa>>=1; /* words */ + + /* base offset of dma calcs when reading the pointer + on the left one */ + if(!channel) ess->dma_dac.base = pa&0xFFFF; + + pa|=0x00400000; /* System RAM */ + + /* XXX the 16bit here might not be needed.. */ + if((mode & ESS_FMT_STEREO) && (mode & ESS_FMT_16BIT)) { + if(channel) + pa|=0x00800000; /* Stereo */ + pa>>=1; + } + +/* XXX think about endianess when writing these registers */ + M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa); + /* start of sample */ + apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); + apu_set_register(ess, channel, 5, pa&0xFFFF); + /* sample end */ + apu_set_register(ess, channel, 6, (pa+size)&0xFFFF); + /* setting loop len == sample len */ + apu_set_register(ess, channel, 7, size); + + /* clear effects/env.. */ + apu_set_register(ess, channel, 8, 0x0000); + /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ + apu_set_register(ess, channel, 9, 0xD000); + + /* clear routing stuff */ + apu_set_register(ess, channel, 11, 0x0000); + /* dma on, no envelopes, filter to all 1s) */ + apu_set_register(ess, channel, 0, 0x400F); + + if(mode&ESS_FMT_16BIT) + ess->apu_mode[channel]=0x10; + else + ess->apu_mode[channel]=0x30; + + if(mode&ESS_FMT_STEREO) { + /* set panning: left or right */ + apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0 : 0x10)); + ess->apu_mode[channel] += 0x10; + } else + apu_set_register(ess, channel, 10, 0x8F08); + } + + /* clear WP interrupts */ + outw(1, ess->card->iobase+0x04); + /* enable WP ints */ + outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); + + /* go team! */ + set_dac_rate(ess,rate); + start_dac(ess); +} + +/* + * Native record driver + */ + +/* again, passed mode is alrady shifted/masked */ +static void +ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) +{ + int apu_step = 2; + int channel; + + M_printk("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n", + mode, rate, buffer, size); + + /* all maestro sizes are in 16bit words */ + size >>=1; + + /* we're given the full size of the buffer, but + in stereo each channel will only use its half */ + if(mode&ESS_FMT_STEREO) { + size >>=1; + apu_step = 1; + } + + /* APU assignments: 2 = mono/left SRC + 3 = right SRC + 4 = mono/left Input Mixer + 5 = right Input Mixer */ + for(channel=2;channel<6;channel+=apu_step) + { + int i; + int bsize, route; + u32 pa; + u32 tmpval; + + /* data seems to flow from the codec, through an apu into + the 'mixbuf' bit of page, then through the SRC apu + and out to the real 'buffer'. ok. sure. */ + + if(channel & 0x04) { + /* ok, we're an input mixer going from adc + through the mixbuf to the other apus */ + + if(!(channel & 0x01)) { + pa = virt_to_bus(ess->mixbuf); + } else { + pa = virt_to_bus(ess->mixbuf + (PAGE_SIZE >> 4)); + } + + /* we source from a 'magic' apu */ + bsize = PAGE_SIZE >> 5; /* half of this channels alloc, in words */ + route = 0x14 + (channel - 4); /* parallel in crap, see maestro reg 0xC [8-11] */ + ess->apu_mode[channel] = 0x90; /* Input Mixer */ + + } else { + /* we're a rate converter taking + input from the input apus and outputing it to + system memory */ + if(!(channel & 0x01)) { + pa = virt_to_bus(buffer); + } else { + /* right channel records its split half. + *2 accomodates for rampant shifting earlier */ + pa = virt_to_bus(buffer + size*2); + } + + ess->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ + + bsize = size; + /* get input from inputing apu */ + route = channel + 2; + } + + M_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n",pa,channel); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + ess->apu_base[channel]=tmpval; + wave_set_register(ess, ess->apu[channel]<<3, tmpval); + + pa -= virt_to_bus(ess->card->dmapages); + pa>>=1; /* words */ + + /* base offset of dma calcs when reading the pointer + on this left one */ + if(channel==2) ess->dma_adc.base = pa&0xFFFF; + + pa|=0x00400000; /* bit 22 -> System RAM */ + + M_printk("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n", + ess->apu[channel], pa, bsize, route); + + /* Begin loading the APU */ + for(i=0;i<15;i++) /* clear all PBRs */ + apu_set_register(ess, channel, i, 0x0000); + + apu_set_register(ess, channel, 0, 0x400F); + + /* need to enable subgroups.. and we should probably + have different groups for different /dev/dsps.. */ + apu_set_register(ess, channel, 2, 0x8); + + /* Load the buffer into the wave engine */ + apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); + /* XXX reg is little endian.. */ + apu_set_register(ess, channel, 5, pa&0xFFFF); + apu_set_register(ess, channel, 6, (pa+bsize)&0xFFFF); + apu_set_register(ess, channel, 7, bsize); + + /* clear effects/env.. */ + apu_set_register(ess, channel, 8, 0x00F0); + + /* amplitude now? sure. why not. */ + apu_set_register(ess, channel, 9, 0x0000); + + /* set filter tune, radius, polar pan */ + apu_set_register(ess, channel, 10, 0x8F08); + + /* route input */ + apu_set_register(ess, channel, 11, route); + } + + /* clear WP interrupts */ + outw(1, ess->card->iobase+0x04); + /* enable WP ints */ + outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); + + /* let 'er rip */ + set_adc_rate(ess,rate); + start_adc(ess); +} +/* --------------------------------------------------------------------- */ + +static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count) +{ + M_printk("set_dmaa??\n"); +} + +static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count) +{ + M_printk("set_dmac??\n"); +} + +/* Playback pointer */ +static inline unsigned get_dmaa(struct ess_state *s) +{ + int offset; + + offset = apu_get_register(s,0,5); + +/* M_printk("dmaa: offset: %d, base: %d\n",offset,s->dma_dac.base); */ + + offset-=s->dma_dac.base; + + return (offset&0xFFFE)<<1; /* hardware is in words */ +} + +/* Record pointer */ +static inline unsigned get_dmac(struct ess_state *s) +{ + int offset; + + offset = apu_get_register(s,2,5); + +/* M_printk("dmac: offset: %d, base: %d\n",offset,s->dma_adc.base); */ + + /* The offset is an address not a position relative to base */ + offset-=s->dma_adc.base; + + return (offset&0xFFFE)<<1; /* hardware is in words */ +} + +/* + * Meet Bob, the timer... + */ + +static void ess_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static void stop_bob(struct ess_state *s) +{ + /* Mask IDR 11,17 */ + maestro_write(s, 0x11, maestro_read(s, 0x11)&~1); + maestro_write(s, 0x17, maestro_read(s, 0x17)&~1); +} + +/* eventually we could be clever and limit bob ints + to the frequency at which our smallest duration + chunks may expire */ +#define ESS_SYSCLK 50000000 +static void start_bob(struct ess_state *s) +{ + int prescale; + int divide; + + /* XXX make freq selector much smarter, see calc_bob_rate */ + int freq = 200; + + /* compute ideal interrupt frequency for buffer size & play rate */ + /* first, find best prescaler value to match freq */ + for(prescale=5;prescale<12;prescale++) + if(freq > (ESS_SYSCLK>>(prescale+9))) + break; + + /* next, back off prescaler whilst getting divider into optimum range */ + divide=1; + while((prescale > 5) && (divide<32)) + { + prescale--; + divide <<=1; + } + divide>>=1; + + /* now fine-tune the divider for best match */ + for(;divide<31;divide++) + if(freq >= ((ESS_SYSCLK>>(prescale+9))/(divide+1))) + break; + + /* divide = 0 is illegal, but don't let prescale = 4! */ + if(divide == 0) + { + divide++; + if(prescale>5) + prescale--; + } + + maestro_write(s, 6, 0x9000 | (prescale<<5) | divide); /* set reg */ + + /* Now set IDR 11/17 */ + maestro_write(s, 0x11, maestro_read(s, 0x11)|1); + maestro_write(s, 0x17, maestro_read(s, 0x17)|1); +} +/* --------------------------------------------------------------------- */ + +/* this quickly calculates the frequency needed for bob + and sets it if its different than what bob is + currently running at. its called often so + needs to be fairly quick. */ +#define BOB_MIN 50 +#define BOB_MAX 400 +static void calc_bob_rate(struct ess_state *s) { +#if 0 /* this thing tries to set the frequency of bob such that + there are 2 interrupts / buffer walked by the dac/adc. That + is probably very wrong for people who actually care about + mid buffer positioning. it should be calculated as bytes/interrupt + and that needs to be decided :) so for now just use the static 150 + in start_bob.*/ + + unsigned int dac_rate=2,adc_rate=1,newrate; + static int israte=-1; + + if (s->dma_dac.fragsize == 0) dac_rate = BOB_MIN; + else { + dac_rate = (2 * s->ratedac * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / + (s->dma_dac.fragsize) ; + } + + if (s->dma_adc.fragsize == 0) adc_rate = BOB_MIN; + else { + adc_rate = (2 * s->rateadc * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / + (s->dma_adc.fragsize) ; + } + + if(dac_rate > adc_rate) newrate = adc_rate; + else newrate=dac_rate; + + if(newrate > BOB_MAX) newrate = BOB_MAX; + else { + if(newrate < BOB_MIN) + newrate = BOB_MIN; + } + + if( israte != newrate) { + printk("dac: %d adc: %d rate: %d\n",dac_rate,adc_rate,israte); + israte=newrate; + } +#endif + +} + +static int +prog_dmabuf(struct ess_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + unsigned bytepersec; + unsigned bufs; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= ESS_ADC_SHIFT; + } else { + stop_dac(s); + fmt >>= ESS_DAC_SHIFT; + } + spin_unlock_irqrestore(&s->lock, flags); + fmt &= ESS_FMT_MASK; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + + /* this algorithm is a little nuts.. where did /1000 come from? */ + 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--; + 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; + + M_printk("maestro: setup oss: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); + + memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); + + spin_lock_irqsave(&s->lock, flags); + if (rec) + ess_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); + else + ess_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); + + spin_unlock_irqrestore(&s->lock, flags); + db->ready = 1; + + return 0; +} + +static __inline__ void +clear_advance(struct ess_state *s) +{ + unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; + + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + /* account for wrapping? */ + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void +ess_update_ptr(struct ess_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + /* oh boy should this all be re-written. everything in the current code paths think + that the various counters/pointers are expressed in bytes to the user but we have + two apus doing stereo stuff so we fix it up here.. it propogates to all the various + counters from here. */ + if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize; + } else { + hwptr = get_dmac(s) % s->dma_adc.dmasize; + } + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + /* FILL ME + wrindir(s, SV_CIENABLE, s->enable); */ + stop_adc(s); + /* brute force everyone back in sync, sigh */ + s->dma_adc.count = 0; + s->dma_adc.swptr = 0; + s->dma_adc.hwptr = 0; + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = get_dmaa(s) % s->dma_dac.dmasize; + /* the apu only reports the length it has seen, not the + length of the memory that has been used (the WP + knows that) */ + if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT)) + hwptr<<=1; + + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; +/* M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/ + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { + wake_up(&s->dma_dac.wait); + } + } else { + s->dma_dac.count -= diff; +/* M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */ + if (s->dma_dac.count <= 0) { + M_printk("underflow! diff: %d count: %d hw: %d sw: %d\n", diff, s->dma_dac.count, + hwptr, s->dma_dac.swptr); + /* FILL ME + wrindir(s, SV_CIENABLE, s->enable); */ + /* XXX how on earth can calling this with the lock held work.. */ + stop_dac(s); + /* brute force everyone back in sync, sigh */ + s->dma_dac.count = 0; + s->dma_dac.swptr = hwptr; + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { + wake_up(&s->dma_dac.wait); +/* printk("waking up DAC count: %d sw: %d hw: %d\n",s->dma_dac.count, s->dma_dac.swptr, + hwptr);*/ + } + } + } +} + +static void +ess_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ess_state *s; + struct ess_card *c = (struct ess_card *)dev_id; + int i; + u32 event; + + if ( ! (event = inb(c->iobase+0x1A)) ) return; + + outw(inw(c->iobase+4)&1, c->iobase+4); + +/* M_printk("maestro int: %x\n",event);*/ + if(event&(1<<6)) + { + int x; + enum {UP_EVT, DOWN_EVT, MUTE_EVT} vol_evt; + int volume; + + /* Figure out which volume control button was pushed, + based on differences from the default register + values. */ + x = inb(c->iobase+0x1c); + if (x&1) vol_evt = MUTE_EVT; + else if (((x>>1)&7) > 4) vol_evt = UP_EVT; + else vol_evt = DOWN_EVT; + + /* Reset the volume control registers. */ + outb(0x88, c->iobase+0x1c); + outb(0x88, c->iobase+0x1d); + outb(0x88, c->iobase+0x1e); + outb(0x88, c->iobase+0x1f); + + /* Deal with the button press in a hammer-handed + manner by adjusting the master mixer volume. */ + volume = c->mix.mixer_state[0] & 0xff; + if (vol_evt == UP_EVT) { + volume += 10; + if (volume > 100) + volume = 100; + } + else if (vol_evt == DOWN_EVT) { + volume -= 10; + if (volume < 0) + volume = 0; + } else { + /* vol_evt == MUTE_EVT */ + if (volume == 0) + volume = c->dock_mute_vol; + else { + c->dock_mute_vol = volume; + volume = 0; + } + } + set_mixer (c, 0, (volume << 8) | volume); + } + + /* Ack all the interrupts. */ + outb(0xFF, c->iobase+0x1A); + + /* + * Update the pointers for all APU's we are running. + */ + for(i=0;ichannels[i]; + if(s->dev_audio == -1) + break; + spin_lock(&s->lock); + ess_update_ptr(s); + spin_unlock(&s->lock); + } +} + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "maestro: invalid magic value in %s\n"; + +#define VALIDATE_MAGIC(FOO,MAG) \ +({ \ + if (!(FOO) || (FOO)->magic != MAG) { \ + printk(invalid_magic,__FUNCTION__); \ + return -ENXIO; \ + } \ +}) + +#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,ESS_STATE_MAGIC) +#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,ESS_CARD_MAGIC) + +static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ) +{ + unsigned int left,right; + /* cleanse input a little */ + right = ((val >> 8) & 0xff) ; + left = (val & 0xff) ; + + if(right > 100) right = 100; + if(left > 100) left = 100; + + card->mix.mixer_state[mixer]=(right << 8) | left; + card->mix.write_mixer(card,mixer,left,right); +} + +static void +mixer_push_state(struct ess_card *card) +{ + int i; + for(i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) { + if( ! supported_mixer(card,i)) continue; + + set_mixer(card,i,card->mix.mixer_state[i]); + } +} + +static int mixer_ioctl(struct ess_card *card, unsigned int cmd, unsigned long arg) +{ + int i, val=0; + unsigned long flags; + + VALIDATE_CARD(card); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + 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; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + 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; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* give them the current record source */ + + if(!card->mix.recmask_io) { + val = 0; + } else { + spin_lock_irqsave(&card->lock, flags); + val = card->mix.recmask_io(card,1,0); + spin_unlock_irqrestore(&card->lock, flags); + } + break; + + case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ + val = card->mix.supported_mixers; + break; + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + val = card->mix.record_sources; + break; + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + val = card->mix.stereo_mixers; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: /* read a specific mixer */ + i = _IOC_NR(cmd); + + if ( ! supported_mixer(card,i)) + return -EINVAL; + + /* do we ever want to touch the hardware? */ +/* spin_lock_irqsave(&card->lock, flags); + val = card->mix.read_mixer(card,i); + spin_unlock_irqrestore(&card->lock, flags);*/ + + val = card->mix.mixer_state[i]; +/* M_printk("returned 0x%x for mixer %d\n",val,i);*/ + + break; + } + return put_user(val,(int *)arg); + } + + if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ)) + return -EINVAL; + + card->mix.modcnt++; + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + + if (!card->mix.recmask_io) return -EINVAL; + if(!val) return 0; + if(! (val &= card->mix.record_sources)) return -EINVAL; + + spin_lock_irqsave(&card->lock, flags); + card->mix.recmask_io(card,0,val); + spin_unlock_irqrestore(&card->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + + if ( ! supported_mixer(card,i)) + return -EINVAL; + + spin_lock_irqsave(&card->lock, flags); + set_mixer(card,i,val); + spin_unlock_irqrestore(&card->lock, flags); + + return 0; + } +} + +/* --------------------------------------------------------------------- */ +static int ess_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct ess_card *card = NULL; + struct pci_dev *pdev; + struct pci_driver *drvr; + + pci_for_each_dev(pdev) { + drvr = pci_dev_driver (pdev); + if (drvr == &maestro_pci_driver) { + card = (struct ess_card*)pci_get_drvdata (pdev); + if (!card) + continue; + if (card->dev_mixer == minor) + break; + } + } + if (!card) + return -ENODEV; + file->private_data = card; + return 0; +} + +static int ess_release_mixdev(struct inode *inode, struct file *file) +{ + struct ess_card *card = (struct ess_card *)file->private_data; + + VALIDATE_CARD(card); + + return 0; +} + +static int ess_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ess_card *card = (struct ess_card *)file->private_data; + + VALIDATE_CARD(card); + + return mixer_ioctl(card, cmd, arg); +} + +static /*const*/ struct file_operations ess_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: ess_ioctl_mixdev, + open: ess_open_mixdev, + release: ess_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct ess_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait,current); + unsigned long flags; + int count; + signed long tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + /* XXX uhm.. questionable locking*/ + 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) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->ratedac; + tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; + /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. + or something. who cares. - zach */ + if (!schedule_timeout(tmo ? tmo : 1) && tmo) + M_printk(KERN_DEBUG "maestro: dma timed out?? %ld\n",jiffies); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ +/* Zach sez: "god this is gross.." */ +static int +comb_stereo(unsigned char *real_buffer,unsigned char *tmp_buffer, int offset, + int count, int bufsize) +{ + /* No such thing as stereo recording, so we + use dual input mixers. which means we have to + combine mono to stereo buffer. yuck. + + but we don't have to be able to work a byte at a time..*/ + + unsigned char *so,*left,*right; + int i; + + so = tmp_buffer; + left = real_buffer + offset; + right = real_buffer + bufsize/2 + offset; + +/* M_printk("comb_stereo writing %d to %p from %p and %p, offset: %d size: %d\n",count/2, tmp_buffer,left,right,offset,bufsize);*/ + + for(i=count/4; i ; i--) { + (*(so+2)) = *(right++); + (*(so+3)) = *(right++); + (*so) = *(left++); + (*(so+1)) = *(left++); + so+=4; + } + + return 0; +} + +/* in this loop, dma_adc.count signifies the amount of data thats waiting + to be copied to the user's buffer. it is filled by the interrupt + handler and drained by this loop. */ +static ssize_t +ess_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned char *combbuf = NULL; + + 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(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if(!(combbuf = kmalloc(count,GFP_KERNEL))) + return -ENOMEM; + ret = 0; + + calc_bob_rate(s); + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + /* remember, all these things are expressed in bytes to be + sent to the user.. hence the evil / 2 down below */ + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt > count) + cnt = count; + + if ( cnt > 0 ) cnt &= ~3; + + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + { + ret = ret ? ret : -EAGAIN; + goto rec_return_free; + } + if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { + if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + spin_lock_irqsave(&s->lock, flags); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + /* program enhanced mode registers */ + /* FILL ME */ +/* wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); */ + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) + { + ret = ret ? ret : -ERESTARTSYS; + goto rec_return_free; + } + continue; + } + + if(s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + /* swptr/2 so that we know the real offset in each apu's buffer */ + comb_stereo(s->dma_adc.rawbuf,combbuf,swptr/2,cnt,s->dma_adc.dmasize); + if (copy_to_user(buffer, combbuf, cnt)) { + ret = ret ? ret : -EFAULT; + goto rec_return_free; + } + } else { + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + ret = ret ? ret : -EFAULT; + goto rec_return_free; + } + } + + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + +rec_return_free: + if(combbuf) kfree(combbuf); + return ret; +} + +static ssize_t +ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + 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(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + calc_bob_rate(s); + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + + cnt = s->dma_dac.dmasize-swptr; + + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if(!ret) ret = -EAGAIN; + goto return_free; + } + if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { + if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + spin_lock_irqsave(&s->lock, flags); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + /* program enhanced mode registers */ +/* wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); */ + /* FILL ME */ + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto return_free; + } + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + goto return_free; + } +/* printk("wrote %d bytes at sw: %d cnt: %d while hw: %d\n",cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr);*/ + + swptr = (swptr + cnt) % s->dma_dac.dmasize; + + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } +return_free: + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + +/* In 0.14 prog_dmabuf always returns success anyway ... */ + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf(s, 0)) + return 0; + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf(s, 1)) + return 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); + ess_update_ptr(s); + 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 ess_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 1)) != 0) + goto out; + db = &s->dma_dac; + } else +#if 0 + /* if we can have the wp/wc do the combining + we can turn this back on. */ + if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + goto out; + db = &s->dma_adc; + } else +#endif + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + +/* printk("maestro: ess_ioctl: cmd %d\n", cmd);*/ + + 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: + 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: + /* XXX fix */ + 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.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + 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); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + /* fixed at 16bit for now */ + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; +#if 0 + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); +#endif + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? + (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? + AFMT_S16_LE : + AFMT_U8, + (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) + 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(s, 1))) + 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(s, 0))) + 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 && (ret = prog_dmabuf(s, 0))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + 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 && (ret = prog_dmabuf(s, 1))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + 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 && (ret = prog_dmabuf(s, 0))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + 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 && (ret = prog_dmabuf(s, 1))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + 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 && (ret = prog_dmabuf(s, 0))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + 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(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + M_printk("maestro: SETFRAGMENT: %0x\n",val); + if (file->f_mode & FMODE_READ) { + 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 (file->f_mode & FMODE_WRITE) { + 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; + } + 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) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return -EINVAL; +} + +static void +set_base_registers(struct ess_state *s,void *vaddr) +{ + unsigned long packed_phys = virt_to_bus(vaddr)>>12; + wave_set_register(s, 0x01FC , packed_phys); + wave_set_register(s, 0x01FD , packed_phys); + wave_set_register(s, 0x01FE , packed_phys); + wave_set_register(s, 0x01FF , packed_phys); +} + +/* + * this guy makes sure we're in the right power + * state for what we want to be doing + */ +static void maestro_power(struct ess_card *card, int tostate) +{ + u16 active_mask = acpi_state_mask[tostate]; + u8 state; + + if(!use_pm) return; + + pci_read_config_byte(card->pcidev, card->power_regs+0x4, &state); + state&=3; + + /* make sure we're in the right state */ + if(state != tostate) { + M_printk(KERN_WARNING "maestro: dev %02x:%02x.%x switching from D%d to D%d\n", + card->pcidev->bus->number, + PCI_SLOT(card->pcidev->devfn), + PCI_FUNC(card->pcidev->devfn), + state,tostate); + pci_write_config_byte(card->pcidev, card->power_regs+0x4, tostate); + } + + /* and make sure the units we care about are on + XXX we might want to do this before state flipping? */ + pci_write_config_word(card->pcidev, 0x54, ~ active_mask); + pci_write_config_word(card->pcidev, 0x56, ~ active_mask); +} + +/* we allocate a large power of two for all our memory. + this is cut up into (not to scale :): + |silly fifo word | 512byte mixbuf per adc | dac/adc * channels | +*/ +static int +allocate_buffers(struct ess_state *s) +{ + void *rawbuf=NULL; + int order,i; + struct page *page, *pend; + + /* alloc as big a chunk as we can */ + for (order = (dsps_order + (16-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--) + if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order))) + break; + + if (!rawbuf) + return 1; + + M_printk("maestro: allocated %ld (%d) bytes at %p\n",PAGE_SIZE<card->dmapages = rawbuf; + s->card->dmaorder = order; + + for(i=0;icard->channels[i]; + + if(ess->dev_audio == -1) + continue; + + ess->dma_dac.ready = s->dma_dac.mapped = 0; + ess->dma_adc.ready = s->dma_adc.mapped = 0; + ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - dsps_order - 1; + + /* offset dac and adc buffers starting half way through and then at each [da][ad]c's + order's intervals.. */ + ess->dma_dac.rawbuf = rawbuf + (PAGE_SIZE<<(order-1)) + (i * ( PAGE_SIZE << (ess->dma_dac.buforder + 1 ))); + ess->dma_adc.rawbuf = ess->dma_dac.rawbuf + ( PAGE_SIZE << ess->dma_dac.buforder); + /* offset mixbuf by a mixbuf so that the lame status fifo can + happily scribble away.. */ + ess->mixbuf = rawbuf + (512 * (i+1)); + + M_printk("maestro: setup apu %d: dac: %p adc: %p mix: %p\n",i,ess->dma_dac.rawbuf, + ess->dma_adc.rawbuf, ess->mixbuf); + + } + + /* 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++) + mem_map_reserve(page); + + return 0; +} +static void +free_buffers(struct ess_state *s) +{ + struct page *page, *pend; + + s->dma_dac.rawbuf = s->dma_adc.rawbuf = NULL; + s->dma_dac.mapped = s->dma_adc.mapped = 0; + s->dma_dac.ready = s->dma_adc.ready = 0; + + M_printk("maestro: freeing %p\n",s->card->dmapages); + /* undo marking the pages as reserved */ + + pend = virt_to_page(s->card->dmapages + (PAGE_SIZE << s->card->dmaorder) - 1); + for (page = virt_to_page(s->card->dmapages); page <= pend; page++) + mem_map_unreserve(page); + + free_pages((unsigned long)s->card->dmapages,s->card->dmaorder); + s->card->dmapages = NULL; +} + +static int +ess_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct ess_state *s = NULL; + unsigned char fmtm = ~0, fmts = 0; + struct pci_dev *pdev; + /* + * Scan the cards and find the channel. We only + * do this at open time so it is ok + */ + + pci_for_each_dev(pdev) { + struct ess_card *c; + struct pci_driver *drvr; + + drvr = pci_dev_driver (pdev); + if (drvr == &maestro_pci_driver) { + int i; + struct ess_state *sp; + + c = (struct ess_card*)pci_get_drvdata (pdev); + if (!c) + continue; + for(i=0;ichannels[i]; + if(sp->dev_audio < 0) + continue; + if((sp->dev_audio ^ minor) & ~0xf) + continue; + s=sp; + } + } + } + if (!s) + return -ENODEV; + + VALIDATE_STATE(s); + 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 -EWOULDBLOCK; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + /* under semaphore.. */ + if ((s->card->dmapages==NULL) && allocate_buffers(s)) { + up(&s->open_sem); + return -ENOMEM; + } + + /* we're covered by the open_sem */ + if( ! s->card->dsps_open ) { + maestro_power(s->card,ACPI_D0); + start_bob(s); + } + s->card->dsps_open++; + M_printk("maestro: open, %d bobs now\n",s->card->dsps_open); + + /* ok, lets write WC base regs now that we've + powered up the chip */ + M_printk("maestro: writing 0x%lx (bus 0x%lx) to the wp\n",virt_to_bus(s->card->dmapages), + ((virt_to_bus(s->card->dmapages))&0xFFE00000)>>12); + set_base_registers(s,s->card->dmapages); + + if (file->f_mode & FMODE_READ) { +/* + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; */ + + fmtm &= ~((ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT); + fmts = (ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT; + + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + up(&s->open_sem); + return 0; +} + +static int +ess_release(struct inode *inode, struct file *file) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + + VALIDATE_STATE(s); + 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); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + } + + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + /* we're covered by the open_sem */ + M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1); + if( --s->card->dsps_open <= 0) { + s->card->dsps_open = 0; + stop_bob(s); + free_buffers(s); + maestro_power(s->card,ACPI_D2); + } + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static struct file_operations ess_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: ess_read, + write: ess_write, + poll: ess_poll, + ioctl: ess_ioctl, + mmap: ess_mmap, + open: ess_open, + release: ess_release, +}; + +static int +maestro_config(struct ess_card *card) +{ + struct pci_dev *pcidev = card->pcidev; + struct ess_state *ess = &card->channels[0]; + int apu,iobase = card->iobase; + u16 w; + u32 n; + + /* We used to muck around with pci config space that + * we had no business messing with. We don't know enough + * about the machine to know which DMA mode is appropriate, + * etc. We were guessing wrong on some machines and making + * them unhappy. We now trust in the BIOS to do things right, + * which almost certainly means a new host of problems will + * arise with broken BIOS implementations. screw 'em. + * We're already intolerant of machines that don't assign + * IRQs. + */ + + /* do config work at full power */ + maestro_power(card,ACPI_D0); + + pci_read_config_word(pcidev, 0x50, &w); + + w&=~(1<<5); /* Don't swap left/right (undoc)*/ + + pci_write_config_word(pcidev, 0x50, w); + + pci_read_config_word(pcidev, 0x52, &w); + w&=~(1<<15); /* Turn off internal clock multiplier */ + /* XXX how do we know which to use? */ + w&=~(1<<14); /* External clock */ + + w|= (1<<7); /* Hardware volume control on */ + w|= (1<<6); /* Debounce off: easier to push the HWV buttons. */ + w&=~(1<<5); /* GPIO 4:5 */ + w|= (1<<4); /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ + w&=~(1<<2); /* MIDI fix off (undoc) */ + w&=~(1<<1); /* reserved, always write 0 */ + pci_write_config_word(pcidev, 0x52, w); + + /* + * Legacy mode + */ + + pci_read_config_word(pcidev, 0x40, &w); + w|=(1<<15); /* legacy decode off */ + w&=~(1<<14); /* Disable SIRQ */ + w&=~(0x1f); /* disable mpu irq/io, game port, fm, SB */ + + pci_write_config_word(pcidev, 0x40, w); + + /* Set up 978 docking control chip. */ + pci_read_config_word(pcidev, 0x58, &w); + w|=1<<2; /* Enable 978. */ + w|=1<<3; /* Turn on 978 hardware volume control. */ + w&=~(1<<11); /* Turn on 978 mixer volume control. */ + pci_write_config_word(pcidev, 0x58, w); + + sound_reset(iobase); + + /* + * Ring Bus Setup + */ + + /* setup usual 0x34 stuff.. 0x36 may be chip specific */ + outw(0xC090, iobase+0x34); /* direct sound, stereo */ + udelay(20); + outw(0x3000, iobase+0x36); /* direct sound, stereo */ + udelay(20); + + + /* + * Reset the CODEC + */ + + maestro_ac97_reset(iobase,pcidev); + + /* + * Ring Bus Setup + */ + + n=inl(iobase+0x34); + n&=~0xF000; + n|=12<<12; /* Direct Sound, Stereo */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x0F00; /* Modem off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x00F0; + n|=9<<4; /* DAC, Stereo */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x000F; /* ASSP off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n|=(1<<29); /* Enable ring bus */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n|=(1<<28); /* Enable serial bus */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x00F00000; /* MIC off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x000F0000; /* I2S off */ + outl(n, iobase+0x34); + + + w=inw(iobase+0x18); + w&=~(1<<7); /* ClkRun off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<6); /* Hardware volume control interrupt off... for now. */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<4); /* ASSP irq off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<3); /* ISDN irq off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w|=(1<<2); /* Direct Sound IRQ on */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<1); /* MPU401 IRQ off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w|=(1<<0); /* SB IRQ on */ + outw(w, iobase+0x18); + + /* Set hardware volume control registers to midpoints. + We can tell which button was pushed based on how they change. */ + outb(0x88, iobase+0x1c); + outb(0x88, iobase+0x1d); + outb(0x88, iobase+0x1e); + outb(0x88, iobase+0x1f); + + /* it appears some maestros (dell 7500) only work if these are set, + regardless of wether we use the assp or not. */ + + outb(0, iobase+0xA4); + outb(3, iobase+0xA2); + outb(0, iobase+0xA6); + + for(apu=0;apu<16;apu++) + { + /* Write 0 into the buffer area 0x1E0->1EF */ + outw(0x01E0+apu, 0x10+iobase); + outw(0x0000, 0x12+iobase); + + /* + * The 1.10 test program seem to write 0 into the buffer area + * 0x1D0-0x1DF too. + */ + outw(0x01D0+apu, 0x10+iobase); + outw(0x0000, 0x12+iobase); + } + +#if 1 + wave_set_register(ess, IDR7_WAVE_ROMRAM, + (wave_get_register(ess, IDR7_WAVE_ROMRAM)&0xFF00)); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)|0x100); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)&~0x200); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)|~0x400); +#else + maestro_write(ess, IDR7_WAVE_ROMRAM, + (maestro_read(ess, IDR7_WAVE_ROMRAM)&0xFF00)); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)|0x100); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)&~0x200); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)|0x400); +#endif + + maestro_write(ess, IDR2_CRAM_DATA, 0x0000); + maestro_write(ess, 0x08, 0xB004); + /* Now back to the DirectSound stuff */ + maestro_write(ess, 0x09, 0x001B); + maestro_write(ess, 0x0A, 0x8000); + maestro_write(ess, 0x0B, 0x3F37); + maestro_write(ess, 0x0C, 0x0098); + + /* parallel out ?? */ + maestro_write(ess, 0x0C, + (maestro_read(ess, 0x0C)&~0xF000)|0x8000); + /* parallel in, has something to do with recording :) */ + maestro_write(ess, 0x0C, + (maestro_read(ess, 0x0C)&~0x0F00)|0x0500); + + maestro_write(ess, 0x0D, 0x7632); + + /* Wave cache control on - test off, sg off, + enable, enable extra chans 1Mb */ + + outw(inw(0x14+iobase)|(1<<8),0x14+iobase); + outw(inw(0x14+iobase)&0xFE03,0x14+iobase); + outw((inw(0x14+iobase)&0xFFFC), 0x14+iobase); + outw(inw(0x14+iobase)|(1<<7),0x14+iobase); + + outw(0xA1A0, 0x14+iobase); /* 0300 ? */ + + /* Now clear the APU control ram */ + for(apu=0;apupower_regs = 0; + + /* check to see if we have a capabilities list in + the config register */ + pci_read_config_word(pcidev, PCI_STATUS, &w); + if(! w & PCI_STATUS_CAP_LIST) return 0; + + /* walk the list, starting at the head. */ + pci_read_config_byte(pcidev,PCI_CAPABILITY_LIST,&next); + + while(next && max--) { + pci_read_config_dword(pcidev, next & ~3, &n); + if((n & 0xff) == PCI_CAP_ID_PM) { + card->power_regs = next; + break; + } + next = ((n>>8) & 0xff); + } + + return card->power_regs ? 1 : 0; +} + +static int __init +maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid) +{ + int card_type = pdid->driver_data; + u32 n; + int iobase; + int i, ret; + struct ess_card *card; + struct ess_state *ess; + struct pm_dev *pmdev; + int num = 0; + +/* when built into the kernel, we only print version if device is found */ +#ifndef MODULE + static int printed_version; + if (!printed_version++) + printk(version); +#endif + + /* don't pick up weird modem maestros */ + if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) + return -ENODEV; + + + if ((ret=pci_enable_device(pcidev))) + return ret; + + iobase = pci_resource_start(pcidev,0); + if (!iobase || !(pci_resource_flags(pcidev, 0 ) & IORESOURCE_IO)) + return -ENODEV; + + if(pcidev->irq == 0) + return -ENODEV; + + /* stake our claim on the iospace */ + if( request_region(iobase, 256, card_names[card_type]) == NULL ) + { + printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase); + return -EBUSY; + } + + /* just to be sure */ + pci_set_master(pcidev); + + card = kmalloc(sizeof(struct ess_card), GFP_KERNEL); + if(card == NULL) + { + printk(KERN_WARNING "maestro: out of memory\n"); + release_region(iobase, 256); + return -ENOMEM; + } + + memset(card, 0, sizeof(*card)); + card->pcidev = pcidev; + + pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), + maestro_pm_callback); + if (pmdev) + pmdev->data = card; + + card->iobase = iobase; + card->card_type = card_type; + card->irq = pcidev->irq; + card->magic = ESS_CARD_MAGIC; + spin_lock_init(&card->lock); + init_waitqueue_head(&card->suspend_queue); + + card->dock_mute_vol = 50; + + /* init our groups of 6 apus */ + for(i=0;ichannels[i]; + + s->index = i; + + s->card = card; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + spin_lock_init(&s->lock); + init_MUTEX(&s->open_sem); + s->magic = ESS_STATE_MAGIC; + + s->apu[0] = 6*i; + s->apu[1] = (6*i)+1; + s->apu[2] = (6*i)+2; + s->apu[3] = (6*i)+3; + s->apu[4] = (6*i)+4; + s->apu[5] = (6*i)+5; + + if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) + printk("maestro: BOTCH!\n"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0) + break; + } + + num = i; + + /* clear the rest if we ran out of slots to register */ + for(;ichannels[i]; + s->dev_audio = -1; + } + + ess = &card->channels[0]; + + /* + * Ok card ready. Begin setup proper + */ + + printk(KERN_INFO "maestro: Configuring %s found at IO 0x%04X IRQ %d\n", + card_names[card_type],iobase,card->irq); + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n); + printk(KERN_INFO "maestro: subvendor id: 0x%08x\n",n); + + /* turn off power management unless: + * - the user explicitly asks for it + * or + * - we're not a 2e, lesser chipps seem to have problems. + * - we're not on our _very_ small whitelist. some implemenetations + * really dont' like the pm code, others require it. + * feel free to expand this as required. + */ +#define SUBSYSTEM_VENDOR(x) (x&0xffff) + if( (use_pm != 1) && + ((card_type != TYPE_MAESTRO2E) || (SUBSYSTEM_VENDOR(n) != 0x1028))) + use_pm = 0; + + if(!use_pm) + printk(KERN_INFO "maestro: not attempting power management.\n"); + else { + if(!parse_power(card,pcidev)) + printk(KERN_INFO "maestro: no PCI power management interface found.\n"); + else { + pci_read_config_dword(pcidev, card->power_regs, &n); + printk(KERN_INFO "maestro: PCI power management capability: 0x%x\n",n>>16); + } + } + + maestro_config(card); + + if(maestro_ac97_get(card, 0x00)==0x0080) { + printk(KERN_ERR "maestro: my goodness! you seem to have a pt101 codec, which is quite rare.\n" + "\tyou should tell someone about this.\n"); + } else { + maestro_ac97_init(card); + } + + if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) { + printk("maestro: couldn't register mixer!\n"); + } else { + memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state)); + mixer_push_state(card); + } + + if((ret=request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card))) + { + printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq); + unregister_sound_mixer(card->dev_mixer); + for(i=0;ichannels[i]; + if(s->dev_audio != -1) + unregister_sound_dsp(s->dev_audio); + } + release_region(card->iobase, 256); + unregister_reboot_notifier(&maestro_nb); + kfree(card); + return ret; + } + + /* Turn on hardware volume control interrupt. + This has to come after we grab the IRQ above, + or a crash will result on installation if a button has been pressed, + because in that case we'll get an immediate interrupt. */ + n = inw(iobase+0x18); + n|=(1<<6); + outw(n, iobase+0x18); + + pci_set_drvdata(pcidev,card); + /* now go to sleep 'till something interesting happens */ + maestro_power(card,ACPI_D2); + + printk(KERN_INFO "maestro: %d channels configured.\n", num); + return 0; +} + +static void maestro_remove(struct pci_dev *pcidev) { + struct ess_card *card = pci_get_drvdata(pcidev); + int i; + + /* XXX maybe should force stop bob, but should be all + stopped by _release by now */ + free_irq(card->irq, card); + unregister_sound_mixer(card->dev_mixer); + for(i=0;ichannels[i]; + if(ess->dev_audio != -1) + unregister_sound_dsp(ess->dev_audio); + } + /* Goodbye, Mr. Bond. */ + maestro_power(card,ACPI_D3); + release_region(card->iobase, 256); + kfree(card); + pci_set_drvdata(pcidev,NULL); +} + +static struct pci_device_id maestro_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2}, + {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2E}, + {PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, maestro_pci_tbl); + +static struct pci_driver maestro_pci_driver = { + name:"maestro", + id_table:maestro_pci_tbl, + probe:maestro_probe, + remove:maestro_remove, +}; + +int __init init_maestro(void) +{ + int rc; + + rc = pci_module_init(&maestro_pci_driver); + if (rc < 0) + return rc; + + if (register_reboot_notifier(&maestro_nb)) + printk(KERN_WARNING "maestro: reboot notifier registration failed; may not reboot properly.\n"); +#ifdef MODULE + printk(version); +#endif + if (dsps_order < 0) { + dsps_order = 1; + printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); + } + else if (dsps_order > MAX_DSP_ORDER) { + dsps_order = MAX_DSP_ORDER; + printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); + } + return 0; +} + +static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf) +{ + /* this notifier is called when the kernel is really shut down. */ + M_printk("maestro: shutting down\n"); + /* this will remove all card instances too */ + pci_unregister_driver(&maestro_pci_driver); + /* XXX dunno about power management */ + return NOTIFY_OK; +} + +/* --------------------------------------------------------------------- */ + + +void cleanup_maestro(void) { + M_printk("maestro: unloading\n"); + pci_unregister_driver(&maestro_pci_driver); + pm_unregister_all(maestro_pm_callback); + unregister_reboot_notifier(&maestro_nb); +} + +/* --------------------------------------------------------------------- */ + +void +check_suspend(struct ess_card *card) +{ + DECLARE_WAITQUEUE(wait, current); + + if(!card->in_suspend) return; + + card->in_suspend++; + add_wait_queue(&(card->suspend_queue), &wait); + current->state = TASK_UNINTERRUPTIBLE; + schedule(); + remove_wait_queue(&(card->suspend_queue), &wait); + current->state = TASK_RUNNING; +} + +static int +maestro_suspend(struct ess_card *card) +{ + unsigned long flags; + int i,j; + + save_flags(flags); + cli(); /* over-kill */ + + M_printk("maestro: apm in dev %p\n",card); + + /* we have to read from the apu regs, need + to power it up */ + maestro_power(card,ACPI_D0); + + for(i=0;ichannels[i]; + + if(s->dev_audio == -1) + continue; + + M_printk("maestro: stopping apus for device %d\n",i); + stop_dac(s); + stop_adc(s); + for(j=0;j<6;j++) + card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5); + + } + + /* get rid of interrupts? */ + if( card->dsps_open > 0) + stop_bob(&card->channels[0]); + + card->in_suspend++; + + restore_flags(flags); + + /* we trust in the bios to power down the chip on suspend. + * XXX I'm also not sure that in_suspend will protect + * against all reg accesses from here on out. + */ + return 0; +} +static int +maestro_resume(struct ess_card *card) +{ + unsigned long flags; + int i; + + save_flags(flags); + cli(); /* over-kill */ + + card->in_suspend = 0; + + M_printk("maestro: resuming card at %p\n",card); + + /* restore all our config */ + maestro_config(card); + /* need to restore the base pointers.. */ + if(card->dmapages) + set_base_registers(&card->channels[0],card->dmapages); + + mixer_push_state(card); + + /* set each channels' apu control registers before + * restoring audio + */ + for(i=0;ichannels[i]; + int chan,reg; + + if(s->dev_audio == -1) + continue; + + for(chan = 0 ; chan < 6 ; chan++) { + wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]); + for(reg = 1 ; reg < NR_APU_REGS ; reg++) + apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]); + } + for(chan = 0 ; chan < 6 ; chan++) + apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F); + } + + /* now we flip on the music */ + + if( card->dsps_open <= 0) { + /* this card's idle */ + maestro_power(card,ACPI_D2); + } else { + /* ok, we're actually playing things on + this card */ + maestro_power(card,ACPI_D0); + start_bob(&card->channels[0]); + for(i=0;ichannels[i]; + + /* these use the apu_mode, and can handle + spurious calls */ + start_dac(s); + start_adc(s); + } + } + + restore_flags(flags); + + /* all right, we think things are ready, + wake up people who were using the device + when we suspended */ + wake_up(&(card->suspend_queue)); + + return 0; +} + +int +maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct ess_card *card = (struct ess_card*) dev->data; + + if ( ! card ) goto out; + + M_printk("maestro: pm event 0x%x received for card %p\n", rqst, card); + + switch (rqst) { + case PM_SUSPEND: + maestro_suspend(card); + break; + case PM_RESUME: + maestro_resume(card); + break; + /* + * we'd also like to find out about + * power level changes because some biosen + * do mean things to the maestro when they + * change their power state. + */ + } +out: + return 0; +} + +module_init(init_maestro); +module_exit(cleanup_maestro); diff -Nru a/sound/oss/maestro.h b/sound/oss/maestro.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/maestro.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,60 @@ +/* + * Registers for the ESS PCI cards + */ + +/* + * Memory access + */ + +#define ESS_MEM_DATA 0x00 +#define ESS_MEM_INDEX 0x02 + +/* + * AC-97 Codec port. Delay 1uS after each write. This is used to + * talk AC-97 (see intel.com). Write data then register. + */ + +#define ESS_AC97_INDEX 0x30 /* byte wide */ +#define ESS_AC97_DATA 0x32 + +/* + * Reading is a bit different. You write register|0x80 to ubdex + * delay 1uS poll the low bit of index, when it clears read the + * data value. + */ + +/* + * Control port. Not yet fully understood + * The value 0xC090 gets loaded to it then 0x0000 and 0x2800 + * to the data port. Then after 4uS the value 0x300 is written + */ + +#define RING_BUS_CTRL_L 0x34 +#define RING_BUS_CTRL_H 0x36 + +/* + * This is also used during setup. The value 0x17 is written to it + */ + +#define ESS_SETUP_18 0x18 + +/* + * And this one gets 0x000b + */ + +#define ESS_SETUP_A2 0xA2 + +/* + * And this 0x0000 + */ + +#define ESS_SETUP_A4 0xA4 +#define ESS_SETUP_A6 0xA6 + +/* + * Stuff to do with Harpo - the wave stuff + */ + +#define ESS_WAVETABLE_SIZE 0x14 +#define ESS_WAVETABLE_2M 0xA180 + diff -Nru a/sound/oss/maestro3.c b/sound/oss/maestro3.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/maestro3.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2962 @@ +/***************************************************************************** + * + * ESS Maestro3/Allegro driver for Linux 2.4.x + * + * 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. + * + * (c) Copyright 2000 Zach Brown + * + * I need to thank many people for helping make this driver happen. + * As always, Eric Brombaugh was a hacking machine and killed many bugs + * that I was too dumb to notice. Howard Kim at ESS provided reference boards + * and as much docs as he could. Todd and Mick at Dell tested snapshots on + * an army of laptops. msw and deviant at Red Hat also humoured me by hanging + * their laptops every few hours in the name of science. + * + * Shouts go out to Mike "DJ XPCom" Ang. + * + * History + * v1.22 - Feb 28 2001 - Zach Brown + * allocate mem at insmod/setup, rather than open + * limit pci dma addresses to 28bit, thanks guys. + * v1.21 - Feb 04 2001 - Zach Brown + * fix up really dumb notifier -> suspend oops + * v1.20 - Jan 30 2001 - Zach Brown + * get rid of pm callback and use pci_dev suspend/resume instead + * m3_probe cleanups, including pm oops think-o + * v1.10 - Jan 6 2001 - Zach Brown + * revert to lame remap_page_range mmap() just to make it work + * record mmap fixed. + * fix up incredibly broken open/release resource management + * duh. fix record format setting. + * add SMP locking and cleanup formatting here and there + * v1.00 - Dec 16 2000 - Zach Brown + * port to sexy 2.4 interfaces + * properly align instance allocations so recording works + * clean up function namespace a little :/ + * update PCI IDs based on mail from ESS + * arbitrarily bump version number to show its 2.4 now, + * 2.2 will stay 0., oss_audio port gets 2. + * v0.03 - Nov 05 2000 - Zach Brown + * disable recording but allow dsp to be opened read + * pull out most silly compat defines + * v0.02 - Nov 04 2000 - Zach Brown + * changed clocking setup for m3, slowdown fixed. + * codec reset is hopefully reliable now + * rudimentary apm/power management makes suspend/resume work + * v0.01 - Oct 31 2000 - Zach Brown + * first release + * v0.00 - Sep 09 2000 - Zach Brown + * first pass derivation from maestro.c + * + * TODO + * in/out allocated contiguously so fullduplex mmap will work? + * no beep on init (mute) + * resetup msrc data memory if freq changes? + * + * -- + * + * Allow me to ramble a bit about the m3 architecture. The core of the + * chip is the 'assp', the custom ESS dsp that runs the show. It has + * a small amount of code and data ram. ESS drops binary dsp code images + * on our heads, but we don't get to see specs on the dsp. + * + * The constant piece of code on the dsp is the 'kernel'. It also has a + * chunk of the dsp memory that is statically set aside for its control + * info. This is the KDATA defines in maestro3.h. Part of its core + * data is a list of code addresses that point to the pieces of DSP code + * that it should walk through in its loop. These other pieces of code + * do the real work. The kernel presumably jumps into each of them in turn. + * These code images tend to have their own data area, and one can have + * multiple data areas representing different states for each of the 'client + * instance' code portions. There is generally a list in the kernel data + * that points to the data instances for a given piece of code. + * + * We've only been given the binary image for the 'minisrc', mini sample + * rate converter. This is rather annoying because it limits the work + * we can do on the dsp, but it also greatly simplifies the job of managing + * dsp data memory for the code and data for our playing streams :). We + * statically allocate the minisrc code into a region we 'know' to be free + * based on the map of the binary kernel image we're loading. We also + * statically allocate the data areas for the maximum number of pcm streams + * we can be dealing with. This max is set by the length of the static list + * in the kernel data that records the number of minisrc data regions we + * can have. Thats right, all software dsp mixing with static code list + * limits. Rock. + * + * How sound goes in and out is still a relative mystery. It appears + * that the dsp has the ability to get input and output through various + * 'connections'. To do IO from or to a connection, you put the address + * of the minisrc client area in the static kernel data lists for that + * input or output. so for pcm -> dsp -> mixer, we put the minisrc data + * instance in the DMA list and also in the list for the mixer. I guess + * it Just Knows which is in/out, and we give some dma control info that + * helps. There are all sorts of cool inputs/outputs that it seems we can't + * use without dsp code images that know how to use them. + * + * So at init time we preload all the memory allocation stuff and set some + * system wide parameters. When we really get a sound to play we build + * up its minisrc header (stream parameters, buffer addresses, input/output + * settings). Then we throw its header on the various lists. We also + * tickle some KDATA settings that ask the assp to raise clock interrupts + * and do some amount of software mixing before handing data to the ac97. + * + * Sorry for the vague details. Feel free to ask Eric or myself if you + * happen to be trying to use this driver elsewhere. Please accept my + * apologies for the quality of the OSS support code, its passed through + * too many hands now and desperately wants to be rethought. + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + /* + * for crizappy mmap() + */ +#include + +#include "maestro3.h" + +#define M_DEBUG 1 + +#define DRIVER_VERSION "1.22" +#define M3_MODULE_NAME "maestro3" +#define PFX M3_MODULE_NAME ": " + +#define M3_STATE_MAGIC 0x734d724d +#define M3_CARD_MAGIC 0x646e6f50 + +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 +#define ESS_FMT_MASK 0x03 +#define ESS_DAC_SHIFT 0 +#define ESS_ADC_SHIFT 4 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define SND_DEV_DSP16 5 + +#ifdef M_DEBUG +static int debug; +#define DPMOD 1 /* per module load */ +#define DPSTR 2 /* per 'stream' */ +#define DPSYS 3 /* per syscall */ +#define DPCRAP 4 /* stuff the user shouldn't see unless they're really debuggin */ +#define DPINT 5 /* per interrupt, LOTS */ +#define DPRINTK(DP, args...) {if (debug >= (DP)) printk(KERN_DEBUG PFX args);} +#else +#define DPRINTK(x) +#endif + +struct m3_list { + int curlen; + u16 mem_addr; + int max; +}; + +int external_amp = 1; + +struct m3_state { + unsigned int magic; + struct m3_card *card; + unsigned char fmt, enable; + + int index; + + /* this locks around the oss state in the driver */ + spinlock_t lock; + + struct semaphore open_sem; + wait_queue_head_t open_wait; + mode_t open_mode; + + int dev_audio; + + struct assp_instance { + u16 code, data; + } dac_inst, adc_inst; + + /* should be in dmabuf */ + unsigned int rateadc, ratedac; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + 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 endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + /* new in m3 */ + int mixer_index, dma_index, msrc_index, adc1_index; + int in_lists; + /* 2.4.. */ + dma_addr_t handle; + + } dma_dac, dma_adc; +}; + +struct m3_card { + unsigned int magic; + + struct m3_card *next; + + struct ac97_codec *ac97; + spinlock_t ac97_lock; + + int card_type; + +#define NR_DSPS 1 +#define MAX_DSPS NR_DSPS + struct m3_state channels[MAX_DSPS]; + + /* this locks around the physical registers on the card */ + spinlock_t lock; + + /* hardware resources */ + struct pci_dev *pcidev; + u32 iobase; + u32 irq; + + int dacs_active; + + int timer_users; + + struct m3_list msrc_list, + mixer_list, + adc1_list, + dma_list; + + /* for storing reset state..*/ + u8 reset_state; + + u16 *suspend_mem; + int in_suspend; + wait_queue_head_t suspend_queue; +}; + +/* + * an arbitrary volume we set the internal + * volume settings to so that the ac97 volume + * range is a little less insane. 0x7fff is + * max. + */ +#define ARB_VOLUME ( 0x6800 ) + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +enum { + ESS_ALLEGRO, + ESS_MAESTRO3, + /* + * a maestro3 with 'hardware strapping', only + * found inside ESS? + */ + ESS_MAESTRO3HW, +}; + +static char *card_names[] = { + [ESS_ALLEGRO] = "Allegro", + [ESS_MAESTRO3] = "Maestro3(i)", + [ESS_MAESTRO3HW] = "Maestro3(i)hw" +}; + +#ifndef PCI_VENDOR_ESS +#define PCI_VENDOR_ESS 0x125D +#endif + +#define M3_DEVICE(DEV, TYPE) \ +{ \ +vendor: PCI_VENDOR_ESS, \ +device: DEV, \ +subvendor: PCI_ANY_ID, \ +subdevice: PCI_ANY_ID, \ +class: PCI_CLASS_MULTIMEDIA_AUDIO << 8, \ +class_mask: 0xffff << 8, \ +driver_data: TYPE, \ +} + +static struct pci_device_id m3_id_table[] __initdata = { + M3_DEVICE(0x1988, ESS_ALLEGRO), + M3_DEVICE(0x1998, ESS_MAESTRO3), + M3_DEVICE(0x199a, ESS_MAESTRO3HW), + {0,} +}; + +MODULE_DEVICE_TABLE (pci, m3_id_table); + +/* + * reports seem to indicate that the m3 is limited + * to 28bit bus addresses. aaaargggh... + */ +#define M3_PCI_DMA_MASK 0x0fffffff + +static 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; +} + +static struct m3_card *devs; + +/* + * I'm not very good at laying out functions in a file :) + */ +static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf); +static int m3_suspend(struct pci_dev *pci_dev, u32 state); +static void check_suspend(struct m3_card *card); + +struct notifier_block m3_reboot_nb = {m3_notifier, NULL, 0}; + +static void m3_outw(struct m3_card *card, + u16 value, unsigned long reg) +{ + check_suspend(card); + outw(value, card->iobase + reg); +} + +static u16 m3_inw(struct m3_card *card, unsigned long reg) +{ + check_suspend(card); + return inw(card->iobase + reg); +} +static void m3_outb(struct m3_card *card, + u8 value, unsigned long reg) +{ + check_suspend(card); + outb(value, card->iobase + reg); +} +static u8 m3_inb(struct m3_card *card, unsigned long reg) +{ + check_suspend(card); + return inb(card->iobase + reg); +} + +/* + * access 16bit words to the code or data regions of the dsp's memory. + * index addresses 16bit words. + */ +static u16 __m3_assp_read(struct m3_card *card, u16 region, u16 index) +{ + m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + m3_outw(card, index, DSP_PORT_MEMORY_INDEX); + return m3_inw(card, DSP_PORT_MEMORY_DATA); +} +static u16 m3_assp_read(struct m3_card *card, u16 region, u16 index) +{ + unsigned long flags; + u16 ret; + + spin_lock_irqsave(&(card->lock), flags); + ret = __m3_assp_read(card, region, index); + spin_unlock_irqrestore(&(card->lock), flags); + + return ret; +} + +static void __m3_assp_write(struct m3_card *card, + u16 region, u16 index, u16 data) +{ + m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + m3_outw(card, index, DSP_PORT_MEMORY_INDEX); + m3_outw(card, data, DSP_PORT_MEMORY_DATA); +} +static void m3_assp_write(struct m3_card *card, + u16 region, u16 index, u16 data) +{ + unsigned long flags; + + spin_lock_irqsave(&(card->lock), flags); + __m3_assp_write(card, region, index, data); + spin_unlock_irqrestore(&(card->lock), flags); +} + +static void m3_assp_halt(struct m3_card *card) +{ + card->reset_state = m3_inb(card, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK; + mdelay(10); + m3_outb(card, card->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + +static void m3_assp_continue(struct m3_card *card) +{ + m3_outb(card, card->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + +/* + * This makes me sad. the maestro3 has lists + * internally that must be packed.. 0 terminates, + * apparently, or maybe all unused entries have + * to be 0, the lists have static lengths set + * by the binary code images. + */ + +static int m3_add_list(struct m3_card *card, + struct m3_list *list, u16 val) +{ + DPRINTK(DPSTR, "adding val 0x%x to list 0x%p at pos %d\n", + val, list, list->curlen); + + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + list->curlen, + val); + + return list->curlen++; + +} + +static void m3_remove_list(struct m3_card *card, + struct m3_list *list, int index) +{ + u16 val; + int lastindex = list->curlen - 1; + + DPRINTK(DPSTR, "removing ind %d from list 0x%p\n", + index, list); + + if(index != lastindex) { + val = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex); + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + index, + val); + } + + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex, + 0); + + list->curlen--; +} + +static void set_fmt(struct m3_state *s, unsigned char mask, unsigned char data) +{ + int tmp; + + s->fmt = (s->fmt & mask) | data; + + tmp = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; + + /* write to 'mono' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 1, + (tmp & ESS_FMT_STEREO) ? 0 : 1); + /* write to '8bit' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 2, + (tmp & ESS_FMT_16BIT) ? 0 : 1); + + tmp = (s->fmt >> ESS_ADC_SHIFT) & ESS_FMT_MASK; + + /* write to 'mono' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + SRC3_DIRECTION_OFFSET + 1, + (tmp & ESS_FMT_STEREO) ? 0 : 1); + /* write to '8bit' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + SRC3_DIRECTION_OFFSET + 2, + (tmp & ESS_FMT_16BIT) ? 0 : 1); +} + +static void set_dac_rate(struct m3_state *s, unsigned int rate) +{ + u32 freq; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + + s->ratedac = rate; + + freq = ((rate << 15) + 24000 ) / 48000; + if(freq) + freq--; + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_FREQUENCY, + freq); +} + +static void set_adc_rate(struct m3_state *s, unsigned int rate) +{ + u32 freq; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + + s->rateadc = rate; + + freq = ((rate << 15) + 24000 ) / 48000; + if(freq) + freq--; + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_FREQUENCY, + freq); +} + +static void inc_timer_users(struct m3_card *card) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + card->timer_users++; + DPRINTK(DPSYS, "inc timer users now %d\n", + card->timer_users); + if(card->timer_users != 1) + goto out; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 240 ) ; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 240 ) ; + + m3_outw(card, + m3_inw(card, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +out: + spin_unlock_irqrestore(&card->lock, flags); +} + +static void dec_timer_users(struct m3_card *card) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + card->timer_users--; + DPRINTK(DPSYS, "dec timer users now %d\n", + card->timer_users); + if(card->timer_users > 0 ) + goto out; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 0 ) ; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 0 ) ; + + m3_outw(card, m3_inw(card, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +out: + spin_unlock_irqrestore(&card->lock, flags); +} + +/* + * {start,stop}_{adc,dac} should be called + * while holding the 'state' lock and they + * will try to grab the 'card' lock.. + */ +static void stop_adc(struct m3_state *s) +{ + if (! (s->enable & ADC_RUNNING)) + return; + + s->enable &= ~ADC_RUNNING; + dec_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_INSTANCE_READY, 0); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 0); +} + +static void stop_dac(struct m3_state *s) +{ + if (! (s->enable & DAC_RUNNING)) + return; + + DPRINTK(DPSYS, "stop_dac()\n"); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_INSTANCE_READY, 0); + + s->enable &= ~DAC_RUNNING; + s->card->dacs_active--; + dec_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + s->card->dacs_active ) ; +} + +static void start_dac(struct m3_state *s) +{ + if( (!s->dma_dac.mapped && s->dma_dac.count < 1) || + !s->dma_dac.ready || + (s->enable & DAC_RUNNING)) + return; + + DPRINTK(DPSYS, "start_dac()\n"); + + s->enable |= DAC_RUNNING; + s->card->dacs_active++; + inc_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_INSTANCE_READY, 1); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + s->card->dacs_active ) ; +} + +static void start_adc(struct m3_state *s) +{ + if ((! s->dma_adc.mapped && + s->dma_adc.count >= (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + || !s->dma_adc.ready + || (s->enable & ADC_RUNNING) ) + return; + + DPRINTK(DPSYS, "start_adc()\n"); + + s->enable |= ADC_RUNNING; + inc_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 1); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_INSTANCE_READY, 1); +} + +static struct play_vals { + u16 addr, val; +} pv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 0} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */ + {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */ +}; + + +/* the mode passed should be already shifted and masked */ +static void m3_play_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size) +{ + int dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2); + int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2); + int dsp_in_buffer = s->dac_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); + int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; + struct dmabuf *db = &s->dma_dac; + int i; + + DPRINTK(DPSTR, "mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + +#define LO(x) ((x) & 0xffff) +#define HI(x) LO((x) >> 16) + + /* host dma buffer pointers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_ADDRL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_ADDRH, + HI(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1L, + LO(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1H, + HI(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_CURRENTL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_CURRENTH, + HI(virt_to_bus(buffer))); +#undef LO +#undef HI + + /* dsp buffers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_BEGIN, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_END_PLUS_1, + dsp_in_buffer + (dsp_in_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_HEAD, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_TAIL, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_BEGIN, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_END_PLUS_1, + dsp_out_buffer + (dsp_out_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_HEAD, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_TAIL, + dsp_out_buffer); + + /* + * some per client initializers + */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 12, + s->dac_inst.data + 40 + 8); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 19, + s->dac_inst.code + MINISRC_COEF_LOC); + + /* enable or disable low pass filter? */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 22, + s->ratedac > 45000 ? 0xff : 0 ); + + /* tell it which way dma is going? */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_DMA_CONTROL, + DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for(i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + pv[i].addr, pv[i].val); + + /* + * put us in the lists if we're not already there + */ + + if(db->in_lists == 0) { + + db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, + s->dac_inst.data >> DP_SHIFT_COUNT); + + db->dma_index = m3_add_list(s->card, &s->card->dma_list, + s->dac_inst.data >> DP_SHIFT_COUNT); + + db->mixer_index = m3_add_list(s->card, &s->card->mixer_list, + s->dac_inst.data >> DP_SHIFT_COUNT); + + db->in_lists = 1; + } + + set_dac_rate(s,rate); + start_dac(s); +} + +/* + * Native record driver + */ +static struct rec_vals { + u16 addr, val; +} rv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 1} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */ + {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */ + {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */ +}; + +/* again, passed mode is alrady shifted/masked */ +static void m3_rec_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size) +{ + int dsp_in_size = MINISRC_IN_BUFFER_SIZE + (0x10 * 2); + int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2); + int dsp_in_buffer = s->adc_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); + int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; + struct dmabuf *db = &s->dma_adc; + int i; + + DPRINTK(DPSTR, "rec_setup mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + +#define LO(x) ((x) & 0xffff) +#define HI(x) LO((x) >> 16) + + /* host dma buffer pointers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_ADDRL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_ADDRH, + HI(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1L, + LO(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1H, + HI(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_CURRENTL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_CURRENTH, + HI(virt_to_bus(buffer))); +#undef LO +#undef HI + + /* dsp buffers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_BEGIN, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_END_PLUS_1, + dsp_in_buffer + (dsp_in_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_HEAD, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_TAIL, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_BEGIN, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_END_PLUS_1, + dsp_out_buffer + (dsp_out_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_HEAD, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_TAIL, + dsp_out_buffer); + + /* + * some per client initializers + */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + SRC3_DIRECTION_OFFSET + 12, + s->adc_inst.data + 40 + 8); + + /* tell it which way dma is going? */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_DMA_CONTROL, + DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for(i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + rv[i].addr, rv[i].val); + + /* + * put us in the lists if we're not already there + */ + + if(db->in_lists == 0) { + + db->adc1_index = m3_add_list(s->card, &s->card->adc1_list, + s->adc_inst.data >> DP_SHIFT_COUNT); + + db->dma_index = m3_add_list(s->card, &s->card->dma_list, + s->adc_inst.data >> DP_SHIFT_COUNT); + + db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, + s->adc_inst.data >> DP_SHIFT_COUNT); + + db->in_lists = 1; + } + + set_adc_rate(s,rate); + start_adc(s); +} +/* --------------------------------------------------------------------- */ + +static void set_dmaa(struct m3_state *s, unsigned int addr, unsigned int count) +{ + DPRINTK(DPINT,"set_dmaa??\n"); +} + +static void set_dmac(struct m3_state *s, unsigned int addr, unsigned int count) +{ + DPRINTK(DPINT,"set_dmac??\n"); +} + +u32 get_dma_pos(struct m3_card *card, + int instance_addr) +{ + u16 hi = 0, lo = 0; + int retry = 10; + + /* + * try and get a valid answer + */ + while(retry--) { + hi = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + instance_addr + CDATA_HOST_SRC_CURRENTH); + + lo = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + instance_addr + CDATA_HOST_SRC_CURRENTL); + + if(hi == m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + instance_addr + CDATA_HOST_SRC_CURRENTH)) + break; + } + return lo | (hi<<16); +} + +u32 get_dmaa(struct m3_state *s) +{ + u32 offset; + + offset = get_dma_pos(s->card, s->dac_inst.data) - + virt_to_bus(s->dma_dac.rawbuf); + + DPRINTK(DPINT,"get_dmaa: 0x%08x\n",offset); + + return offset; +} + +u32 get_dmac(struct m3_state *s) +{ + u32 offset; + + offset = get_dma_pos(s->card, s->adc_inst.data) - + virt_to_bus(s->dma_adc.rawbuf); + + DPRINTK(DPINT,"get_dmac: 0x%08x\n",offset); + + return offset; + +} + +static void m3_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static int +prog_dmabuf(struct m3_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + unsigned bytepersec; + unsigned bufs; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= ESS_ADC_SHIFT; + } else { + stop_dac(s); + fmt >>= ESS_DAC_SHIFT; + } + fmt &= ESS_FMT_MASK; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + + 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--; + 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; + + DPRINTK(DPSTR,"prog_dmabuf: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); + + memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); + + if (rec) + m3_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); + else + m3_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); + + db->ready = 1; + + spin_unlock_irqrestore(&s->lock, flags); + + return 0; +} + +static void clear_advance(struct m3_state *s) +{ + unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; + + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + /* account for wrapping? */ + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void m3_update_ptr(struct m3_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + hwptr = get_dmac(s) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + stop_adc(s); + /* brute force everyone back in sync, sigh */ + s->dma_adc.count = 0; + s->dma_adc.swptr = 0; + s->dma_adc.hwptr = 0; + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = get_dmaa(s) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + + DPRINTK(DPINT,"updating dac: hwptr: %6d diff: %6d count: %6d\n", + hwptr,diff,s->dma_dac.count); + + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + + if (s->dma_dac.mapped) { + + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { + wake_up(&s->dma_dac.wait); + } + } else { + + s->dma_dac.count -= diff; + + if (s->dma_dac.count <= 0) { + DPRINTK(DPCRAP,"underflow! diff: %d (0x%x) count: %d (0x%x) hw: %d (0x%x) sw: %d (0x%x)\n", + diff, diff, + s->dma_dac.count, + s->dma_dac.count, + hwptr, hwptr, + s->dma_dac.swptr, + s->dma_dac.swptr); + stop_dac(s); + /* brute force everyone back in sync, sigh */ + s->dma_dac.count = 0; + s->dma_dac.swptr = hwptr; + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { + wake_up(&s->dma_dac.wait); + DPRINTK(DPINT,"waking up DAC count: %d sw: %d hw: %d\n", + s->dma_dac.count, s->dma_dac.swptr, hwptr); + } + } + } +} + +static void m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct m3_card *c = (struct m3_card *)dev_id; + struct m3_state *s = &c->channels[0]; + u8 status; + + status = inb(c->iobase+0x1A); + + if(status == 0xff) return; + + /* presumably acking the ints? */ + outw(status, c->iobase+0x1A); + + if(c->in_suspend) + return; + + /* + * ack an assp int if its running + * and has an int pending + */ + if( status & ASSP_INT_PENDING) { + u8 ctl = inb(c->iobase + ASSP_CONTROL_B); + if( !(ctl & STOP_ASSP_CLOCK)) { + ctl = inb(c->iobase + ASSP_HOST_INT_STATUS ); + if(ctl & DSP2HOST_REQ_TIMER) { + outb( DSP2HOST_REQ_TIMER, c->iobase + ASSP_HOST_INT_STATUS); + /* update adc/dac info if it was a timer int */ + spin_lock(&s->lock); + m3_update_ptr(s); + spin_unlock(&s->lock); + } + } + } + + /* XXX is this needed? */ + if(status & 0x40) + outb(0x40, c->iobase+0x1A); +} + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value in %s\n"; + +#define VALIDATE_MAGIC(FOO,MAG) \ +({ \ + if (!(FOO) || (FOO)->magic != MAG) { \ + printk(invalid_magic,__FUNCTION__); \ + return -ENXIO; \ + } \ +}) + +#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,M3_STATE_MAGIC) +#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,M3_CARD_MAGIC) + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct m3_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait,current); + unsigned long flags; + int count; + signed long tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&s->dma_dac.wait, &wait); + 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) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = (count * HZ) / s->ratedac; + tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; + /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. + or something. who cares. - zach */ + if (!schedule_timeout(tmo ? tmo : 1) && tmo) + DPRINTK(DPCRAP,"dma timed out?? %ld\n",jiffies); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static ssize_t m3_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + 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(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + spin_lock_irqsave(&s->lock, flags); + + while (count > 0) { + int timed_out; + + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + { + ret = ret ? ret : -EAGAIN; + goto out; + } + + spin_unlock_irqrestore(&s->lock, flags); + timed_out = interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ) == 0; + spin_lock_irqsave(&s->lock, flags); + + if(timed_out) { + printk("read: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + } + if (signal_pending(current)) + { + ret = ret ? ret : -ERESTARTSYS; + goto out; + } + continue; + } + + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + ret = ret ? ret : -EFAULT; + return ret; + } + spin_lock_irqsave(&s->lock, flags); + + swptr = (swptr + cnt) % s->dma_adc.dmasize; + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + +out: + spin_unlock_irqrestore(&s->lock, flags); + return ret; +} + +static ssize_t m3_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + 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(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + spin_lock_irqsave(&s->lock, flags); + + while (count > 0) { + int timed_out; + + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + + cnt = s->dma_dac.dmasize-swptr; + + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if(!ret) ret = -EAGAIN; + goto out; + } + spin_unlock_irqrestore(&s->lock, flags); + timed_out = interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ) == 0; + spin_lock_irqsave(&s->lock, flags); + if(timed_out) { + DPRINTK(DPCRAP,"write: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto out; + } + continue; + } + spin_unlock_irqrestore(&s->lock, flags); + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + return ret; + } + spin_lock_irqsave(&s->lock, flags); + + DPRINTK(DPSYS,"wrote %6d bytes at sw: %6d cnt: %6d while hw: %6d\n", + cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr); + + swptr = (swptr + cnt) % s->dma_dac.dmasize; + + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } +out: + spin_unlock_irqrestore(&s->lock, flags); + return ret; +} + +static unsigned int m3_poll(struct file *file, struct poll_table_struct *wait) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + 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); + m3_update_ptr(s); + + 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 m3_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + unsigned long max_size, size, start, offset; + struct dmabuf *db; + int ret = -EINVAL; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 0)) != 0) + return ret; + db = &s->dma_dac; + } else + if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 1)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + + max_size = db->dmasize; + + start = vma->vm_start; + offset = (vma->vm_pgoff << PAGE_SHIFT); + size = vma->vm_end - vma->vm_start; + + if(size > max_size) + goto out; + if(offset > max_size - size) + goto out; + + /* + * this will be ->nopage() once I can + * ask Jeff what the hell I'm doing wrong. + */ + ret = -EAGAIN; + if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + goto out; + + db->mapped = 1; + ret = 0; + +out: + return ret; +} + +/* + * this function is a disaster.. + */ +#define get_user_ret(x, ptr, ret) ({ if(get_user(x, ptr)) return ret; }) +static int m3_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + + VALIDATE_STATE(s); + + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + + DPRINTK(DPSYS,"m3_ioctl: cmd %d\n", cmd); + + 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: + /* XXX fix */ + 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: + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + spin_unlock_irqrestore(&s->lock, flags); + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + spin_unlock_irqrestore(&s->lock, flags); + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + spin_unlock_irqrestore(&s->lock, flags); + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? + (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? + AFMT_S16_LE : + AFMT_U8, + (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + 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(s, 0))) + 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->enable & DAC_RUNNING) && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + 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->enable & ADC_RUNNING) && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + 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; + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + 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; + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + 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; + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + 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(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + 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 (file->f_mode & FMODE_WRITE) { + 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; + } + spin_unlock_irqrestore(&s->lock, flags); + 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; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return -EINVAL; +} + +static int +allocate_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db) +{ + int order; + + DPRINTK(DPSTR,"allocating for dmabuf %p\n", db); + + /* + * alloc as big a chunk as we can, start with + * 64k 'cause we're insane. based on order cause + * the amazingly complicated prog_dmabuf wants it. + * + * pci_alloc_sonsistent guarantees that it won't cross a natural + * boundry; the m3 hardware can't have dma cross a 64k bus + * address boundry. + */ + for (order = 16-PAGE_SHIFT; order >= 1; order--) { + db->rawbuf = pci_alloc_consistent(pci_dev, PAGE_SIZE << order, + &(db->handle)); + if(db->rawbuf) + break; + } + + if (!db->rawbuf) + return 1; + + DPRINTK(DPSTR,"allocated %ld (%d) bytes at %p\n", + PAGE_SIZE<rawbuf); + + { + struct page *page, *pend; + + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + + + db->buforder = order; + db->ready = 0; + db->mapped = 0; + + return 0; +} + +static void +nuke_lists(struct m3_card *card, struct dmabuf *db) +{ + m3_remove_list(card, &(card->dma_list), db->dma_index); + m3_remove_list(card, &(card->msrc_list), db->msrc_index); + db->in_lists = 0; +} + +static void +free_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db) +{ + if(db->rawbuf == NULL) + return; + + DPRINTK(DPSTR,"freeing %p from dmabuf %p\n",db->rawbuf, db); + + { + struct page *page, *pend; + 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(pci_dev, PAGE_SIZE << db->buforder, + db->rawbuf, db->handle); + + db->rawbuf = NULL; + db->buforder = 0; + db->mapped = 0; + db->ready = 0; +} + +static int m3_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct m3_card *c; + struct m3_state *s = NULL; + int i; + unsigned char fmtm = ~0, fmts = 0; + unsigned long flags; + + /* + * Scan the cards and find the channel. We only + * do this at open time so it is ok + */ + for(c = devs ; c != NULL ; c = c->next) { + + for(i=0;ichannels[i].dev_audio < 0) + continue; + if((c->channels[i].dev_audio ^ minor) & ~0xf) + continue; + + s = &c->channels[i]; + break; + } + } + + if (!s) + return -ENODEV; + + VALIDATE_STATE(s); + + 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 -EWOULDBLOCK; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + spin_lock_irqsave(&s->lock, flags); + + if (file->f_mode & FMODE_READ) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + up(&s->open_sem); + spin_unlock_irqrestore(&s->lock, flags); + return 0; +} + +static int m3_release(struct inode *inode, struct file *file) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + unsigned long flags; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + + down(&s->open_sem); + spin_lock_irqsave(&s->lock, flags); + + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if(s->dma_dac.in_lists) { + m3_remove_list(s->card, &(s->card->mixer_list), s->dma_dac.mixer_index); + nuke_lists(s->card, &(s->dma_dac)); + } + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if(s->dma_adc.in_lists) { + m3_remove_list(s->card, &(s->card->adc1_list), s->dma_adc.adc1_index); + nuke_lists(s->card, &(s->dma_adc)); + } + } + + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + + return 0; +} + +/* + * Wait for the ac97 serial bus to be free. + * return nonzero if the bus is still busy. + */ +static int m3_ac97_wait(struct m3_card *card) +{ + int i = 10000; + + while( (m3_inb(card, 0x30) & 1) && i--) ; + + return i == 0; +} + +u16 m3_ac97_read(struct ac97_codec *codec, u8 reg) +{ + u16 ret = 0; + struct m3_card *card = codec->private_data; + + spin_lock(&card->ac97_lock); + + if(m3_ac97_wait(card)) { + printk(KERN_ERR PFX "serial bus busy reading reg 0x%x\n",reg); + goto out; + } + + m3_outb(card, 0x80 | (reg & 0x7f), 0x30); + + if(m3_ac97_wait(card)) { + printk(KERN_ERR PFX "serial bus busy finishing read reg 0x%x\n",reg); + goto out; + } + + ret = m3_inw(card, 0x32); + DPRINTK(DPCRAP,"reading 0x%04x from 0x%02x\n",ret, reg); + +out: + spin_unlock(&card->ac97_lock); + return ret; +} + +void m3_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) +{ + struct m3_card *card = codec->private_data; + + spin_lock(&card->ac97_lock); + + if(m3_ac97_wait(card)) { + printk(KERN_ERR PFX "serial bus busy writing 0x%x to 0x%x\n",val, reg); + goto out; + } + DPRINTK(DPCRAP,"writing 0x%04x to 0x%02x\n", val, reg); + + m3_outw(card, val, 0x32); + m3_outb(card, reg & 0x7f, 0x30); +out: + spin_unlock(&card->ac97_lock); +} +/* OSS /dev/mixer file operation methods */ +static int m3_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct m3_card *card = devs; + + for (card = devs; card != NULL; card = card->next) { + if((card->ac97 != NULL) && (card->ac97->dev_mixer == minor)) + break; + } + + if (!card) { + return -ENODEV; + } + + file->private_data = card->ac97; + + return 0; +} + +static int m3_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + +static int m3_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 struct file_operations m3_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: m3_ioctl_mixdev, + open: m3_open_mixdev, + release: m3_release_mixdev, +}; + +void remote_codec_config(int io, int isremote) +{ + isremote = isremote ? 1 : 0; + + outw( (inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote, + io + RING_BUS_CTRL_B); + outw( (inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote, + io + SDO_OUT_DEST_CTRL); + outw( (inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote, + io + SDO_IN_DEST_CTRL); +} + +/* + * hack, returns non zero on err + */ +static int try_read_vendor(struct m3_card *card) +{ + u16 ret; + + if(m3_ac97_wait(card)) + return 1; + + m3_outb(card, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30); + + if(m3_ac97_wait(card)) + return 1; + + ret = m3_inw(card, 0x32); + + return (ret == 0) || (ret == 0xffff); +} + +static void m3_codec_reset(struct m3_card *card, int busywait) +{ + u16 dir; + int delay1 = 0, delay2 = 0, i; + int io = card->iobase; + + switch (card->card_type) { + /* + * the onboard codec on the allegro seems + * to want to wait a very long time before + * coming back to life + */ + case ESS_ALLEGRO: + delay1 = 50; + delay2 = 800; + break; + case ESS_MAESTRO3: + case ESS_MAESTRO3HW: + delay1 = 20; + delay2 = 500; + break; + } + + for(i = 0; i < 5; i ++) { + dir = inw(io + GPIO_DIRECTION); + dir |= 0x10; /* assuming pci bus master? */ + + remote_codec_config(io, 0); + + outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A); + udelay(20); + + outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION); + outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK); + outw(0, io + GPIO_DATA); + outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION); + + if(busywait) { + mdelay(delay1); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay1 * HZ) / 1000); + } + + outw(GPO_PRIMARY_AC97, io + GPIO_DATA); + udelay(5); + /* ok, bring back the ac-link */ + outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A); + outw(~0, io + GPIO_MASK); + + if(busywait) { + mdelay(delay2); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay2 * HZ) / 1000); + } + if(! try_read_vendor(card)) + break; + + delay1 += 10; + delay2 += 100; + + DPRINTK(DPMOD, "retrying codec reset with delays of %d and %d ms\n", + delay1, delay2); + } + +#if 0 + /* more gung-ho reset that doesn't + * seem to work anywhere :) + */ + tmp = inw(io + RING_BUS_CTRL_A); + outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A); + mdelay(20); + outw(tmp, io + RING_BUS_CTRL_A); + mdelay(50); +#endif +} + +static int __init m3_codec_install(struct m3_card *card) +{ + struct ac97_codec *codec; + + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + codec->private_data = card; + codec->codec_read = m3_ac97_read; + codec->codec_write = m3_ac97_write; + /* someday we should support secondary codecs.. */ + codec->id = 0; + + if (ac97_probe_codec(codec) == 0) { + printk(KERN_ERR PFX "codec probe failed\n"); + kfree(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); + return -1; + } + + card->ac97 = codec; + + return 0; +} + + +#define MINISRC_LPF_LEN 10 +static u16 minisrc_lpf[MINISRC_LPF_LEN] = { + 0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C, + 0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F +}; +static void m3_assp_init(struct m3_card *card) +{ + int i; + + /* zero kernel data */ + for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR + i, 0); + + /* zero mixer data? */ + for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR2 + i, 0); + + /* init dma pointer */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_CURRENT_DMA, + KDATA_DMA_XFER0); + + /* write kernel into code memory.. */ + for(i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) { + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + REV_B_CODE_MEMORY_BEGIN + i, + assp_kernel_image[i]); + } + + /* + * We only have this one client and we know that 0x400 + * is free in our kernel's mem map, so lets just + * drop it there. It seems that the minisrc doesn't + * need vectors, so we won't bother with them.. + */ + for(i = 0 ; i < sizeof(assp_minisrc_image) / 2; i++) { + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + 0x400 + i, + assp_minisrc_image[i]); + } + + /* + * write the coefficients for the low pass filter? + */ + for(i = 0; i < MINISRC_LPF_LEN ; i++) { + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + i, + minisrc_lpf[i]); + } + + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN, + 0x8000); + + /* + * the minisrc is the only thing on + * our task list.. + */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TASK0, + 0x400); + + /* + * init the mixer number.. + */ + + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER,0); + + /* + * EXTREME KERNEL MASTER VOLUME + */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_LEFT_VOLUME, ARB_VOLUME); + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME); + + card->mixer_list.mem_addr = KDATA_MIXER_XFER0; + card->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS; + card->adc1_list.mem_addr = KDATA_ADC1_XFER0; + card->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS; + card->dma_list.mem_addr = KDATA_DMA_XFER0; + card->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS; + card->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC; + card->msrc_list.max = MAX_INSTANCE_MINISRC; +} + +static int setup_msrc(struct m3_card *card, + struct assp_instance *inst, int index) +{ + int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + + MINISRC_IN_BUFFER_SIZE / 2 + + 1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 ); + int address, i; + + /* + * the revb memory map has 0x1100 through 0x1c00 + * free. + */ + + /* + * align instance address to 256 bytes so that it's + * shifted list address is aligned. + * list address = (mem address >> 1) >> 7; + */ + data_bytes = (data_bytes + 255) & ~255; + address = 0x1100 + ((data_bytes/2) * index); + + if((address + (data_bytes/2)) >= 0x1c00) { + printk(KERN_ERR PFX "no memory for %d bytes at ind %d (addr 0x%x)\n", + data_bytes, index, address); + return -1; + } + + for(i = 0; i < data_bytes/2 ; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + address + i, 0); + + inst->code = 0x400; + inst->data = address; + + return 0; +} + +static int m3_assp_client_init(struct m3_state *s) +{ + setup_msrc(s->card, &(s->dac_inst), s->index * 2); + setup_msrc(s->card, &(s->adc_inst), (s->index * 2) + 1); + + return 0; +} + +static void m3_amp_enable(struct m3_card *card, int enable) +{ + /* + * this works for the reference board, have to find + * out about others + * + * this needs more magic for 4 speaker, but.. + */ + int io = card->iobase; + u16 gpo, polarity_port, polarity; + + if(!external_amp) + return; + + switch (card->card_type) { + case ESS_ALLEGRO: + polarity_port = 0x1800; + break; + default: + /* presumably this is for all 'maestro3's.. */ + polarity_port = 0x1100; + break; + } + + gpo = (polarity_port >> 8) & 0x0F; + polarity = polarity_port >> 12; + if ( enable ) + polarity = !polarity; + polarity = polarity << gpo; + gpo = 1 << gpo; + + outw(~gpo , io + GPIO_MASK); + + outw( inw(io + GPIO_DIRECTION) | gpo , + io + GPIO_DIRECTION); + + outw( (GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity) , + io + GPIO_DATA); + + outw(0xffff , io + GPIO_MASK); +} + +static int +maestro_config(struct m3_card *card) +{ + struct pci_dev *pcidev = card->pcidev; + u32 n; + u8 t; /* makes as much sense as 'n', no? */ + + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= REDUCED_DEBOUNCE; + n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + outb(RESET_ASSP, card->iobase + ASSP_CONTROL_B); + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= ~INT_CLK_SELECT; + if(card->card_type >= ESS_MAESTRO3) { + n &= ~INT_CLK_MULT_ENABLE; + n |= INT_CLK_SRC_NOT_PCI; + } + n &= ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 ); + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + if(card->card_type <= ESS_ALLEGRO) { + pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n); + n |= IN_CLK_12MHZ_SELECT; + pci_write_config_dword(pcidev, PCI_USER_CONFIG, n); + } + + t = inb(card->iobase + ASSP_CONTROL_A); + t &= ~( DSP_CLK_36MHZ_SELECT | ASSP_CLK_49MHZ_SELECT); + t |= ASSP_CLK_49MHZ_SELECT; + t |= ASSP_0_WS_ENABLE; + outb(t, card->iobase + ASSP_CONTROL_A); + + outb(RUN_ASSP, card->iobase + ASSP_CONTROL_B); + + return 0; +} + +static void m3_enable_ints(struct m3_card *card) +{ + unsigned long io = card->iobase; + + outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL); + outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, + io + ASSP_CONTROL_C); +} + +static struct file_operations m3_audio_fops = { + owner: THIS_MODULE, + llseek: &no_llseek, + read: &m3_read, + write: &m3_write, + poll: &m3_poll, + ioctl: &m3_ioctl, + mmap: &m3_mmap, + open: &m3_open, + release: &m3_release, +}; + +#ifdef CONFIG_PM +int alloc_dsp_suspendmem(struct m3_card *card) +{ + int len = sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH); + + if( (card->suspend_mem = vmalloc(len)) == NULL) + return 1; + + return 0; +} +void free_dsp_suspendmem(struct m3_card *card) +{ + if(card->suspend_mem) + vfree(card->suspend_mem); +} + +#else +#define alloc_dsp_suspendmem(args...) 0 +#define free_dsp_suspendmem(args...) +#endif + +/* + * great day! this function is ugly as hell. + */ +static int __init m3_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + u32 n; + int i; + struct m3_card *card = NULL; + int ret = 0; + int card_type = pci_id->driver_data; + + DPRINTK(DPMOD, "in maestro_install\n"); + + if (pci_enable_device(pci_dev)) + return -EIO; + + if (pci_set_dma_mask(pci_dev, M3_PCI_DMA_MASK)) { + printk(KERN_ERR PFX "architecture does not support limiting to 28bit PCI bus addresses\n"); + return -ENODEV; + } + + pci_set_master(pci_dev); + + if( (card = kmalloc(sizeof(struct m3_card), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING PFX "out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(struct m3_card)); + card->pcidev = pci_dev; + init_waitqueue_head(&card->suspend_queue); + + if ( ! request_region(pci_resource_start(pci_dev, 0), + pci_resource_len (pci_dev, 0), M3_MODULE_NAME)) { + + printk(KERN_WARNING PFX "unable to reserve I/O space.\n"); + ret = -EBUSY; + goto out; + } + + card->iobase = pci_resource_start(pci_dev, 0); + + if(alloc_dsp_suspendmem(card)) { + printk(KERN_WARNING PFX "couldn't alloc %d bytes for saving dsp state on suspend\n", + REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH); + ret = -ENOMEM; + goto out; + } + + card->card_type = card_type; + card->irq = pci_dev->irq; + card->next = devs; + card->magic = M3_CARD_MAGIC; + spin_lock_init(&card->lock); + spin_lock_init(&card->ac97_lock); + devs = card; + for(i = 0; ichannels[i]); + s->dev_audio = -1; + } + + printk(KERN_INFO PFX "Configuring ESS %s found at IO 0x%04X IRQ %d\n", + card_names[card->card_type], card->iobase, card->irq); + + pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &n); + printk(KERN_INFO PFX " subvendor id: 0x%08x\n",n); + + maestro_config(card); + m3_assp_halt(card); + + m3_codec_reset(card, 0); + + if(m3_codec_install(card)) { + ret = -EIO; + goto out; + } + + m3_assp_init(card); + m3_amp_enable(card, 1); + + for(i=0;ichannels[i]; + + s->index = i; + + s->card = card; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + spin_lock_init(&s->lock); + init_MUTEX(&(s->open_sem)); + s->magic = M3_STATE_MAGIC; + + m3_assp_client_init(s); + + if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) + printk(KERN_WARNING PFX "initing a dsp device that is already in use?\n"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1)) < 0) { + break; + } + + if( allocate_dmabuf(card->pcidev, &(s->dma_adc)) || + allocate_dmabuf(card->pcidev, &(s->dma_dac))) { + ret = -ENOMEM; + goto out; + } + } + + if(request_irq(card->irq, m3_interrupt, SA_SHIRQ, card_names[card->card_type], card)) { + + printk(KERN_ERR PFX "unable to allocate irq %d,\n", card->irq); + + ret = -EIO; + goto out; + } + + pci_set_drvdata(pci_dev, card); + + m3_enable_ints(card); + m3_assp_continue(card); + +out: + if(ret) { + if(card->iobase) + release_region(pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); + free_dsp_suspendmem(card); + if(card->ac97) { + unregister_sound_mixer(card->ac97->dev_mixer); + kfree(card->ac97); + } + for(i=0;ichannels[i]; + if(s->dev_audio != -1) + unregister_sound_dsp(s->dev_audio); + } + kfree(card); + } + + return ret; +} + +static void m3_remove(struct pci_dev *pci_dev) +{ + struct m3_card *card; + + unregister_reboot_notifier(&m3_reboot_nb); + + while ((card = devs)) { + int i; + devs = devs->next; + + free_irq(card->irq, card); + unregister_sound_mixer(card->ac97->dev_mixer); + kfree(card->ac97); + + for(i=0;ichannels[i]; + if(s->dev_audio < 0) + continue; + + unregister_sound_dsp(s->dev_audio); + free_dmabuf(card->pcidev, &s->dma_adc); + free_dmabuf(card->pcidev, &s->dma_dac); + } + + release_region(card->iobase, 256); + free_dsp_suspendmem(card); + kfree(card); + } + devs = NULL; +} + +/* + * some bioses like the sound chip to be powered down + * at shutdown. We're just calling _suspend to + * achieve that.. + */ +static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf) +{ + struct m3_card *card; + + DPRINTK(DPMOD, "notifier suspending all cards\n"); + + for(card = devs; card != NULL; card = card->next) { + if(!card->in_suspend) + m3_suspend(card->pcidev, 3); /* XXX legal? */ + } + return 0; +} + +static int m3_suspend(struct pci_dev *pci_dev, u32 state) +{ + unsigned long flags; + int i; + struct m3_card *card = pci_get_drvdata(pci_dev); + + /* must be a better way.. */ + save_flags(flags); + cli(); + + DPRINTK(DPMOD, "pm in dev %p\n",card); + + for(i=0;ichannels[i]; + + if(s->dev_audio == -1) + continue; + + DPRINTK(DPMOD, "stop_adc/dac() device %d\n",i); + stop_dac(s); + stop_adc(s); + } + + mdelay(10); /* give the assp a chance to idle.. */ + + m3_assp_halt(card); + + if(card->suspend_mem) { + int index = 0; + + DPRINTK(DPMOD, "saving code\n"); + for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++) + card->suspend_mem[index++] = + m3_assp_read(card, MEMTYPE_INTERNAL_CODE, i); + DPRINTK(DPMOD, "saving data\n"); + for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + card->suspend_mem[index++] = + m3_assp_read(card, MEMTYPE_INTERNAL_DATA, i); + } + + DPRINTK(DPMOD, "powering down apci regs\n"); + m3_outw(card, 0xffff, 0x54); + m3_outw(card, 0xffff, 0x56); + + card->in_suspend = 1; + + restore_flags(flags); + + return 0; +} + +static int m3_resume(struct pci_dev *pci_dev) +{ + unsigned long flags; + int index; + int i; + struct m3_card *card = pci_get_drvdata(pci_dev); + + save_flags(flags); /* paranoia */ + cli(); + card->in_suspend = 0; + + DPRINTK(DPMOD, "resuming\n"); + + /* first lets just bring everything back. .*/ + + DPRINTK(DPMOD, "bringing power back on card 0x%p\n",card); + m3_outw(card, 0, 0x54); + m3_outw(card, 0, 0x56); + + DPRINTK(DPMOD, "restoring pci configs and reseting codec\n"); + maestro_config(card); + m3_assp_halt(card); + m3_codec_reset(card, 1); + + DPRINTK(DPMOD, "restoring dsp code card\n"); + index = 0; + for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, i, + card->suspend_mem[index++]); + for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, i, + card->suspend_mem[index++]); + + /* tell the dma engine to restart itself */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_DMA_ACTIVE, 0); + + DPRINTK(DPMOD, "resuming dsp\n"); + m3_assp_continue(card); + + DPRINTK(DPMOD, "enabling ints\n"); + m3_enable_ints(card); + + /* bring back the old school flavor */ + for(i = 0; i < SOUND_MIXER_NRDEVICES ; i++) { + int state = card->ac97->mixer_state[i]; + if (!supported_mixer(card->ac97, i)) + continue; + + card->ac97->write_mixer(card->ac97, i, + state & 0xff, (state >> 8) & 0xff); + } + + m3_amp_enable(card, 1); + + /* + * now we flip on the music + */ + for(i=0;ichannels[i]; + if(s->dev_audio == -1) + continue; + /* + * db->ready makes it so these guys can be + * called unconditionally.. + */ + DPRINTK(DPMOD, "turning on dacs ind %d\n",i); + start_dac(s); + start_adc(s); + } + + restore_flags(flags); + + /* + * all right, we think things are ready, + * wake up people who were using the device + * when we suspended + */ + wake_up(&card->suspend_queue); + + return 0; +} + +MODULE_AUTHOR("Zach Brown "); +MODULE_DESCRIPTION("ESS Maestro3/Allegro Driver"); +MODULE_LICENSE("GPL"); + +#ifdef M_DEBUG +MODULE_PARM(debug,"i"); +#endif +MODULE_PARM(external_amp,"i"); + +static struct pci_driver m3_pci_driver = { + name: "ess_m3_audio", + id_table: m3_id_table, + probe: m3_probe, + remove: m3_remove, + suspend: m3_suspend, + resume: m3_resume, +}; + +static int __init m3_init_module(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + + printk(KERN_INFO PFX "version " DRIVER_VERSION " built at " __TIME__ " " __DATE__ "\n"); + + if (register_reboot_notifier(&m3_reboot_nb)) { + printk(KERN_WARNING PFX "reboot notifier registration failed\n"); + return -ENODEV; /* ? */ + } + + if (!pci_register_driver(&m3_pci_driver)) { + pci_unregister_driver(&m3_pci_driver); + unregister_reboot_notifier(&m3_reboot_nb); + return -ENODEV; + } + return 0; +} + +static void __exit m3_cleanup_module(void) +{ + pci_unregister_driver(&m3_pci_driver); +} + +module_init(m3_init_module); +module_exit(m3_cleanup_module); + +void check_suspend(struct m3_card *card) +{ + DECLARE_WAITQUEUE(wait, current); + + if(!card->in_suspend) + return; + + card->in_suspend++; + add_wait_queue(&card->suspend_queue, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule(); + remove_wait_queue(&card->suspend_queue, &wait); + set_current_state(TASK_RUNNING); +} diff -Nru a/sound/oss/maestro3.h b/sound/oss/maestro3.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/maestro3.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,821 @@ +/* + * ESS Technology allegro audio driver. + * + * Copyright (C) 1992-2000 Don Kim (don.kim@esstech.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. + * + * Hacked for the maestro3 driver by zab + */ + +// Allegro PCI configuration registers +#define PCI_LEGACY_AUDIO_CTRL 0x40 +#define SOUND_BLASTER_ENABLE 0x00000001 +#define FM_SYNTHESIS_ENABLE 0x00000002 +#define GAME_PORT_ENABLE 0x00000004 +#define MPU401_IO_ENABLE 0x00000008 +#define MPU401_IRQ_ENABLE 0x00000010 +#define ALIAS_10BIT_IO 0x00000020 +#define SB_DMA_MASK 0x000000C0 +#define SB_DMA_0 0x00000040 +#define SB_DMA_1 0x00000040 +#define SB_DMA_R 0x00000080 +#define SB_DMA_3 0x000000C0 +#define SB_IRQ_MASK 0x00000700 +#define SB_IRQ_5 0x00000000 +#define SB_IRQ_7 0x00000100 +#define SB_IRQ_9 0x00000200 +#define SB_IRQ_10 0x00000300 +#define MIDI_IRQ_MASK 0x00003800 +#define SERIAL_IRQ_ENABLE 0x00004000 +#define DISABLE_LEGACY 0x00008000 + +#define PCI_ALLEGRO_CONFIG 0x50 +#define SB_ADDR_240 0x00000004 +#define MPU_ADDR_MASK 0x00000018 +#define MPU_ADDR_330 0x00000000 +#define MPU_ADDR_300 0x00000008 +#define MPU_ADDR_320 0x00000010 +#define MPU_ADDR_340 0x00000018 +#define USE_PCI_TIMING 0x00000040 +#define POSTED_WRITE_ENABLE 0x00000080 +#define DMA_POLICY_MASK 0x00000700 +#define DMA_DDMA 0x00000000 +#define DMA_TDMA 0x00000100 +#define DMA_PCPCI 0x00000200 +#define DMA_WBDMA16 0x00000400 +#define DMA_WBDMA4 0x00000500 +#define DMA_WBDMA2 0x00000600 +#define DMA_WBDMA1 0x00000700 +#define DMA_SAFE_GUARD 0x00000800 +#define HI_PERF_GP_ENABLE 0x00001000 +#define PIC_SNOOP_MODE_0 0x00002000 +#define PIC_SNOOP_MODE_1 0x00004000 +#define SOUNDBLASTER_IRQ_MASK 0x00008000 +#define RING_IN_ENABLE 0x00010000 +#define SPDIF_TEST_MODE 0x00020000 +#define CLK_MULT_MODE_SELECT_2 0x00040000 +#define EEPROM_WRITE_ENABLE 0x00080000 +#define CODEC_DIR_IN 0x00100000 +#define HV_BUTTON_FROM_GD 0x00200000 +#define REDUCED_DEBOUNCE 0x00400000 +#define HV_CTRL_ENABLE 0x00800000 +#define SPDIF_ENABLE 0x01000000 +#define CLK_DIV_SELECT 0x06000000 +#define CLK_DIV_BY_48 0x00000000 +#define CLK_DIV_BY_49 0x02000000 +#define CLK_DIV_BY_50 0x04000000 +#define CLK_DIV_RESERVED 0x06000000 +#define PM_CTRL_ENABLE 0x08000000 +#define CLK_MULT_MODE_SELECT 0x30000000 +#define CLK_MULT_MODE_SHIFT 28 +#define CLK_MULT_MODE_0 0x00000000 +#define CLK_MULT_MODE_1 0x10000000 +#define CLK_MULT_MODE_2 0x20000000 +#define CLK_MULT_MODE_3 0x30000000 +#define INT_CLK_SELECT 0x40000000 +#define INT_CLK_MULT_RESET 0x80000000 + +// M3 +#define INT_CLK_SRC_NOT_PCI 0x00100000 +#define INT_CLK_MULT_ENABLE 0x80000000 + +#define PCI_ACPI_CONTROL 0x54 +#define PCI_ACPI_D0 0x00000000 +#define PCI_ACPI_D1 0xB4F70000 +#define PCI_ACPI_D2 0xB4F7B4F7 + +#define PCI_USER_CONFIG 0x58 +#define EXT_PCI_MASTER_ENABLE 0x00000001 +#define SPDIF_OUT_SELECT 0x00000002 +#define TEST_PIN_DIR_CTRL 0x00000004 +#define AC97_CODEC_TEST 0x00000020 +#define TRI_STATE_BUFFER 0x00000080 +#define IN_CLK_12MHZ_SELECT 0x00000100 +#define MULTI_FUNC_DISABLE 0x00000200 +#define EXT_MASTER_PAIR_SEL 0x00000400 +#define PCI_MASTER_SUPPORT 0x00000800 +#define STOP_CLOCK_ENABLE 0x00001000 +#define EAPD_DRIVE_ENABLE 0x00002000 +#define REQ_TRI_STATE_ENABLE 0x00004000 +#define REQ_LOW_ENABLE 0x00008000 +#define MIDI_1_ENABLE 0x00010000 +#define MIDI_2_ENABLE 0x00020000 +#define SB_AUDIO_SYNC 0x00040000 +#define HV_CTRL_TEST 0x00100000 +#define SOUNDBLASTER_TEST 0x00400000 + +#define PCI_USER_CONFIG_C 0x5C + +#define PCI_DDMA_CTRL 0x60 +#define DDMA_ENABLE 0x00000001 + + +// Allegro registers +#define HOST_INT_CTRL 0x18 +#define SB_INT_ENABLE 0x0001 +#define MPU401_INT_ENABLE 0x0002 +#define ASSP_INT_ENABLE 0x0010 +#define RING_INT_ENABLE 0x0020 +#define HV_INT_ENABLE 0x0040 +#define CLKRUN_GEN_ENABLE 0x0100 +#define HV_CTRL_TO_PME 0x0400 +#define SOFTWARE_RESET_ENABLE 0x8000 + +/* + * should be using the above defines, probably. + */ +#define REGB_ENABLE_RESET 0x01 +#define REGB_STOP_CLOCK 0x10 + +#define HOST_INT_STATUS 0x1A +#define SB_INT_PENDING 0x01 +#define MPU401_INT_PENDING 0x02 +#define ASSP_INT_PENDING 0x10 +#define RING_INT_PENDING 0x20 +#define HV_INT_PENDING 0x40 + +#define HARDWARE_VOL_CTRL 0x1B +#define SHADOW_MIX_REG_VOICE 0x1C +#define HW_VOL_COUNTER_VOICE 0x1D +#define SHADOW_MIX_REG_MASTER 0x1E +#define HW_VOL_COUNTER_MASTER 0x1F + +#define CODEC_COMMAND 0x30 +#define CODEC_READ_B 0x80 + +#define CODEC_STATUS 0x30 +#define CODEC_BUSY_B 0x01 + +#define CODEC_DATA 0x32 + +#define RING_BUS_CTRL_A 0x36 +#define RAC_PME_ENABLE 0x0100 +#define RAC_SDFS_ENABLE 0x0200 +#define LAC_PME_ENABLE 0x0400 +#define LAC_SDFS_ENABLE 0x0800 +#define SERIAL_AC_LINK_ENABLE 0x1000 +#define IO_SRAM_ENABLE 0x2000 +#define IIS_INPUT_ENABLE 0x8000 + +#define RING_BUS_CTRL_B 0x38 +#define SECOND_CODEC_ID_MASK 0x0003 +#define SPDIF_FUNC_ENABLE 0x0010 +#define SECOND_AC_ENABLE 0x0020 +#define SB_MODULE_INTF_ENABLE 0x0040 +#define SSPE_ENABLE 0x0040 +#define M3I_DOCK_ENABLE 0x0080 + +#define SDO_OUT_DEST_CTRL 0x3A +#define COMMAND_ADDR_OUT 0x0003 +#define PCM_LR_OUT_LOCAL 0x0000 +#define PCM_LR_OUT_REMOTE 0x0004 +#define PCM_LR_OUT_MUTE 0x0008 +#define PCM_LR_OUT_BOTH 0x000C +#define LINE1_DAC_OUT_LOCAL 0x0000 +#define LINE1_DAC_OUT_REMOTE 0x0010 +#define LINE1_DAC_OUT_MUTE 0x0020 +#define LINE1_DAC_OUT_BOTH 0x0030 +#define PCM_CLS_OUT_LOCAL 0x0000 +#define PCM_CLS_OUT_REMOTE 0x0040 +#define PCM_CLS_OUT_MUTE 0x0080 +#define PCM_CLS_OUT_BOTH 0x00C0 +#define PCM_RLF_OUT_LOCAL 0x0000 +#define PCM_RLF_OUT_REMOTE 0x0100 +#define PCM_RLF_OUT_MUTE 0x0200 +#define PCM_RLF_OUT_BOTH 0x0300 +#define LINE2_DAC_OUT_LOCAL 0x0000 +#define LINE2_DAC_OUT_REMOTE 0x0400 +#define LINE2_DAC_OUT_MUTE 0x0800 +#define LINE2_DAC_OUT_BOTH 0x0C00 +#define HANDSET_OUT_LOCAL 0x0000 +#define HANDSET_OUT_REMOTE 0x1000 +#define HANDSET_OUT_MUTE 0x2000 +#define HANDSET_OUT_BOTH 0x3000 +#define IO_CTRL_OUT_LOCAL 0x0000 +#define IO_CTRL_OUT_REMOTE 0x4000 +#define IO_CTRL_OUT_MUTE 0x8000 +#define IO_CTRL_OUT_BOTH 0xC000 + +#define SDO_IN_DEST_CTRL 0x3C +#define STATUS_ADDR_IN 0x0003 +#define PCM_LR_IN_LOCAL 0x0000 +#define PCM_LR_IN_REMOTE 0x0004 +#define PCM_LR_RESERVED 0x0008 +#define PCM_LR_IN_BOTH 0x000C +#define LINE1_ADC_IN_LOCAL 0x0000 +#define LINE1_ADC_IN_REMOTE 0x0010 +#define LINE1_ADC_IN_MUTE 0x0020 +#define MIC_ADC_IN_LOCAL 0x0000 +#define MIC_ADC_IN_REMOTE 0x0040 +#define MIC_ADC_IN_MUTE 0x0080 +#define LINE2_DAC_IN_LOCAL 0x0000 +#define LINE2_DAC_IN_REMOTE 0x0400 +#define LINE2_DAC_IN_MUTE 0x0800 +#define HANDSET_IN_LOCAL 0x0000 +#define HANDSET_IN_REMOTE 0x1000 +#define HANDSET_IN_MUTE 0x2000 +#define IO_STATUS_IN_LOCAL 0x0000 +#define IO_STATUS_IN_REMOTE 0x4000 + +#define SPDIF_IN_CTRL 0x3E +#define SPDIF_IN_ENABLE 0x0001 + +#define GPIO_DATA 0x60 +#define GPIO_DATA_MASK 0x0FFF +#define GPIO_HV_STATUS 0x3000 +#define GPIO_PME_STATUS 0x4000 + +#define GPIO_MASK 0x64 +#define GPIO_DIRECTION 0x68 +#define GPO_PRIMARY_AC97 0x0001 +#define GPI_LINEOUT_SENSE 0x0004 +#define GPO_SECONDARY_AC97 0x0008 +#define GPI_VOL_DOWN 0x0010 +#define GPI_VOL_UP 0x0020 +#define GPI_IIS_CLK 0x0040 +#define GPI_IIS_LRCLK 0x0080 +#define GPI_IIS_DATA 0x0100 +#define GPI_DOCKING_STATUS 0x0100 +#define GPI_HEADPHONE_SENSE 0x0200 +#define GPO_EXT_AMP_SHUTDOWN 0x1000 + +// M3 +#define GPO_M3_EXT_AMP_SHUTDN 0x0002 + +#define ASSP_INDEX_PORT 0x80 +#define ASSP_MEMORY_PORT 0x82 +#define ASSP_DATA_PORT 0x84 + +#define MPU401_DATA_PORT 0x98 +#define MPU401_STATUS_PORT 0x99 + +#define CLK_MULT_DATA_PORT 0x9C + +#define ASSP_CONTROL_A 0xA2 +#define ASSP_0_WS_ENABLE 0x01 +#define ASSP_CTRL_A_RESERVED1 0x02 +#define ASSP_CTRL_A_RESERVED2 0x04 +#define ASSP_CLK_49MHZ_SELECT 0x08 +#define FAST_PLU_ENABLE 0x10 +#define ASSP_CTRL_A_RESERVED3 0x20 +#define DSP_CLK_36MHZ_SELECT 0x40 + +#define ASSP_CONTROL_B 0xA4 +#define RESET_ASSP 0x00 +#define RUN_ASSP 0x01 +#define ENABLE_ASSP_CLOCK 0x00 +#define STOP_ASSP_CLOCK 0x10 +#define RESET_TOGGLE 0x40 + +#define ASSP_CONTROL_C 0xA6 +#define ASSP_HOST_INT_ENABLE 0x01 +#define FM_ADDR_REMAP_DISABLE 0x02 +#define HOST_WRITE_PORT_ENABLE 0x08 + +#define ASSP_HOST_INT_STATUS 0xAC +#define DSP2HOST_REQ_PIORECORD 0x01 +#define DSP2HOST_REQ_I2SRATE 0x02 +#define DSP2HOST_REQ_TIMER 0x04 + +// AC97 registers +// XXX fix this crap up +/*#define AC97_RESET 0x00*/ + +#define AC97_VOL_MUTE_B 0x8000 +#define AC97_VOL_M 0x1F +#define AC97_LEFT_VOL_S 8 + +#define AC97_MASTER_VOL 0x02 +#define AC97_LINE_LEVEL_VOL 0x04 +#define AC97_MASTER_MONO_VOL 0x06 +#define AC97_PC_BEEP_VOL 0x0A +#define AC97_PC_BEEP_VOL_M 0x0F +#define AC97_SROUND_MASTER_VOL 0x38 +#define AC97_PC_BEEP_VOL_S 1 + +/*#define AC97_PHONE_VOL 0x0C +#define AC97_MIC_VOL 0x0E*/ +#define AC97_MIC_20DB_ENABLE 0x40 + +/*#define AC97_LINEIN_VOL 0x10 +#define AC97_CD_VOL 0x12 +#define AC97_VIDEO_VOL 0x14 +#define AC97_AUX_VOL 0x16*/ +#define AC97_PCM_OUT_VOL 0x18 +/*#define AC97_RECORD_SELECT 0x1A*/ +#define AC97_RECORD_MIC 0x00 +#define AC97_RECORD_CD 0x01 +#define AC97_RECORD_VIDEO 0x02 +#define AC97_RECORD_AUX 0x03 +#define AC97_RECORD_MONO_MUX 0x02 +#define AC97_RECORD_DIGITAL 0x03 +#define AC97_RECORD_LINE 0x04 +#define AC97_RECORD_STEREO 0x05 +#define AC97_RECORD_MONO 0x06 +#define AC97_RECORD_PHONE 0x07 + +/*#define AC97_RECORD_GAIN 0x1C*/ +#define AC97_RECORD_VOL_M 0x0F + +/*#define AC97_GENERAL_PURPOSE 0x20*/ +#define AC97_POWER_DOWN_CTRL 0x26 +#define AC97_ADC_READY 0x0001 +#define AC97_DAC_READY 0x0002 +#define AC97_ANALOG_READY 0x0004 +#define AC97_VREF_ON 0x0008 +#define AC97_PR0 0x0100 +#define AC97_PR1 0x0200 +#define AC97_PR2 0x0400 +#define AC97_PR3 0x0800 +#define AC97_PR4 0x1000 + +#define AC97_RESERVED1 0x28 + +#define AC97_VENDOR_TEST 0x5A + +#define AC97_CLOCK_DELAY 0x5C +#define AC97_LINEOUT_MUX_SEL 0x0001 +#define AC97_MONO_MUX_SEL 0x0002 +#define AC97_CLOCK_DELAY_SEL 0x1F +#define AC97_DAC_CDS_SHIFT 6 +#define AC97_ADC_CDS_SHIFT 11 + +#define AC97_MULTI_CHANNEL_SEL 0x74 + +/*#define AC97_VENDOR_ID1 0x7C +#define AC97_VENDOR_ID2 0x7E*/ + +/* + * ASSP control regs + */ +#define DSP_PORT_TIMER_COUNT 0x06 + +#define DSP_PORT_MEMORY_INDEX 0x80 + +#define DSP_PORT_MEMORY_TYPE 0x82 +#define MEMTYPE_INTERNAL_CODE 0x0002 +#define MEMTYPE_INTERNAL_DATA 0x0003 +#define MEMTYPE_MASK 0x0003 + +#define DSP_PORT_MEMORY_DATA 0x84 + +#define DSP_PORT_CONTROL_REG_A 0xA2 +#define DSP_PORT_CONTROL_REG_B 0xA4 +#define DSP_PORT_CONTROL_REG_C 0xA6 + +#define REV_A_CODE_MEMORY_BEGIN 0x0000 +#define REV_A_CODE_MEMORY_END 0x0FFF +#define REV_A_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_A_CODE_MEMORY_LENGTH (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1) + +#define REV_B_CODE_MEMORY_BEGIN 0x0000 +#define REV_B_CODE_MEMORY_END 0x0BFF +#define REV_B_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_B_CODE_MEMORY_LENGTH (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1) + +#define REV_A_DATA_MEMORY_BEGIN 0x1000 +#define REV_A_DATA_MEMORY_END 0x2FFF +#define REV_A_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_A_DATA_MEMORY_LENGTH (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1) + +#define REV_B_DATA_MEMORY_BEGIN 0x1000 +#define REV_B_DATA_MEMORY_END 0x2BFF +#define REV_B_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_B_DATA_MEMORY_LENGTH (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1) + + +#define NUM_UNITS_KERNEL_CODE 16 +#define NUM_UNITS_KERNEL_DATA 2 + +#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16 +#define NUM_UNITS_KERNEL_DATA_WITH_HSP 5 + +/* + * Kernel data layout + */ + +#define DP_SHIFT_COUNT 7 + +#define KDATA_BASE_ADDR 0x1000 +#define KDATA_BASE_ADDR2 0x1080 + +#define KDATA_TASK0 (KDATA_BASE_ADDR + 0x0000) +#define KDATA_TASK1 (KDATA_BASE_ADDR + 0x0001) +#define KDATA_TASK2 (KDATA_BASE_ADDR + 0x0002) +#define KDATA_TASK3 (KDATA_BASE_ADDR + 0x0003) +#define KDATA_TASK4 (KDATA_BASE_ADDR + 0x0004) +#define KDATA_TASK5 (KDATA_BASE_ADDR + 0x0005) +#define KDATA_TASK6 (KDATA_BASE_ADDR + 0x0006) +#define KDATA_TASK7 (KDATA_BASE_ADDR + 0x0007) +#define KDATA_TASK_ENDMARK (KDATA_BASE_ADDR + 0x0008) + +#define KDATA_CURRENT_TASK (KDATA_BASE_ADDR + 0x0009) +#define KDATA_TASK_SWITCH (KDATA_BASE_ADDR + 0x000A) + +#define KDATA_INSTANCE0_POS3D (KDATA_BASE_ADDR + 0x000B) +#define KDATA_INSTANCE1_POS3D (KDATA_BASE_ADDR + 0x000C) +#define KDATA_INSTANCE2_POS3D (KDATA_BASE_ADDR + 0x000D) +#define KDATA_INSTANCE3_POS3D (KDATA_BASE_ADDR + 0x000E) +#define KDATA_INSTANCE4_POS3D (KDATA_BASE_ADDR + 0x000F) +#define KDATA_INSTANCE5_POS3D (KDATA_BASE_ADDR + 0x0010) +#define KDATA_INSTANCE6_POS3D (KDATA_BASE_ADDR + 0x0011) +#define KDATA_INSTANCE7_POS3D (KDATA_BASE_ADDR + 0x0012) +#define KDATA_INSTANCE8_POS3D (KDATA_BASE_ADDR + 0x0013) +#define KDATA_INSTANCE_POS3D_ENDMARK (KDATA_BASE_ADDR + 0x0014) + +#define KDATA_INSTANCE0_SPKVIRT (KDATA_BASE_ADDR + 0x0015) +#define KDATA_INSTANCE_SPKVIRT_ENDMARK (KDATA_BASE_ADDR + 0x0016) + +#define KDATA_INSTANCE0_SPDIF (KDATA_BASE_ADDR + 0x0017) +#define KDATA_INSTANCE_SPDIF_ENDMARK (KDATA_BASE_ADDR + 0x0018) + +#define KDATA_INSTANCE0_MODEM (KDATA_BASE_ADDR + 0x0019) +#define KDATA_INSTANCE_MODEM_ENDMARK (KDATA_BASE_ADDR + 0x001A) + +#define KDATA_INSTANCE0_SRC (KDATA_BASE_ADDR + 0x001B) +#define KDATA_INSTANCE1_SRC (KDATA_BASE_ADDR + 0x001C) +#define KDATA_INSTANCE_SRC_ENDMARK (KDATA_BASE_ADDR + 0x001D) + +#define KDATA_INSTANCE0_MINISRC (KDATA_BASE_ADDR + 0x001E) +#define KDATA_INSTANCE1_MINISRC (KDATA_BASE_ADDR + 0x001F) +#define KDATA_INSTANCE2_MINISRC (KDATA_BASE_ADDR + 0x0020) +#define KDATA_INSTANCE3_MINISRC (KDATA_BASE_ADDR + 0x0021) +#define KDATA_INSTANCE_MINISRC_ENDMARK (KDATA_BASE_ADDR + 0x0022) + +#define KDATA_INSTANCE0_CPYTHRU (KDATA_BASE_ADDR + 0x0023) +#define KDATA_INSTANCE1_CPYTHRU (KDATA_BASE_ADDR + 0x0024) +#define KDATA_INSTANCE_CPYTHRU_ENDMARK (KDATA_BASE_ADDR + 0x0025) + +#define KDATA_CURRENT_DMA (KDATA_BASE_ADDR + 0x0026) +#define KDATA_DMA_SWITCH (KDATA_BASE_ADDR + 0x0027) +#define KDATA_DMA_ACTIVE (KDATA_BASE_ADDR + 0x0028) + +#define KDATA_DMA_XFER0 (KDATA_BASE_ADDR + 0x0029) +#define KDATA_DMA_XFER1 (KDATA_BASE_ADDR + 0x002A) +#define KDATA_DMA_XFER2 (KDATA_BASE_ADDR + 0x002B) +#define KDATA_DMA_XFER3 (KDATA_BASE_ADDR + 0x002C) +#define KDATA_DMA_XFER4 (KDATA_BASE_ADDR + 0x002D) +#define KDATA_DMA_XFER5 (KDATA_BASE_ADDR + 0x002E) +#define KDATA_DMA_XFER6 (KDATA_BASE_ADDR + 0x002F) +#define KDATA_DMA_XFER7 (KDATA_BASE_ADDR + 0x0030) +#define KDATA_DMA_XFER8 (KDATA_BASE_ADDR + 0x0031) +#define KDATA_DMA_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0032) + +#define KDATA_I2S_SAMPLE_COUNT (KDATA_BASE_ADDR + 0x0033) +#define KDATA_I2S_INT_METER (KDATA_BASE_ADDR + 0x0034) +#define KDATA_I2S_ACTIVE (KDATA_BASE_ADDR + 0x0035) + +#define KDATA_TIMER_COUNT_RELOAD (KDATA_BASE_ADDR + 0x0036) +#define KDATA_TIMER_COUNT_CURRENT (KDATA_BASE_ADDR + 0x0037) + +#define KDATA_HALT_SYNCH_CLIENT (KDATA_BASE_ADDR + 0x0038) +#define KDATA_HALT_SYNCH_DMA (KDATA_BASE_ADDR + 0x0039) +#define KDATA_HALT_ACKNOWLEDGE (KDATA_BASE_ADDR + 0x003A) + +#define KDATA_ADC1_XFER0 (KDATA_BASE_ADDR + 0x003B) +#define KDATA_ADC1_XFER_ENDMARK (KDATA_BASE_ADDR + 0x003C) +#define KDATA_ADC1_LEFT_VOLUME (KDATA_BASE_ADDR + 0x003D) +#define KDATA_ADC1_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x003E) +#define KDATA_ADC1_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x003F) +#define KDATA_ADC1_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0040) + +#define KDATA_ADC2_XFER0 (KDATA_BASE_ADDR + 0x0041) +#define KDATA_ADC2_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0042) +#define KDATA_ADC2_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0043) +#define KDATA_ADC2_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x0044) +#define KDATA_ADC2_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x0045) +#define KDATA_ADC2_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0046) + +#define KDATA_CD_XFER0 (KDATA_BASE_ADDR + 0x0047) +#define KDATA_CD_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0048) +#define KDATA_CD_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0049) +#define KDATA_CD_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x004A) +#define KDATA_CD_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x004B) +#define KDATA_CD_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x004C) + +#define KDATA_MIC_XFER0 (KDATA_BASE_ADDR + 0x004D) +#define KDATA_MIC_XFER_ENDMARK (KDATA_BASE_ADDR + 0x004E) +#define KDATA_MIC_VOLUME (KDATA_BASE_ADDR + 0x004F) +#define KDATA_MIC_SUR_VOL (KDATA_BASE_ADDR + 0x0050) + +#define KDATA_I2S_XFER0 (KDATA_BASE_ADDR + 0x0051) +#define KDATA_I2S_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0052) + +#define KDATA_CHI_XFER0 (KDATA_BASE_ADDR + 0x0053) +#define KDATA_CHI_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0054) + +#define KDATA_SPDIF_XFER (KDATA_BASE_ADDR + 0x0055) +#define KDATA_SPDIF_CURRENT_FRAME (KDATA_BASE_ADDR + 0x0056) +#define KDATA_SPDIF_FRAME0 (KDATA_BASE_ADDR + 0x0057) +#define KDATA_SPDIF_FRAME1 (KDATA_BASE_ADDR + 0x0058) +#define KDATA_SPDIF_FRAME2 (KDATA_BASE_ADDR + 0x0059) + +#define KDATA_SPDIF_REQUEST (KDATA_BASE_ADDR + 0x005A) +#define KDATA_SPDIF_TEMP (KDATA_BASE_ADDR + 0x005B) + +#define KDATA_SPDIFIN_XFER0 (KDATA_BASE_ADDR + 0x005C) +#define KDATA_SPDIFIN_XFER_ENDMARK (KDATA_BASE_ADDR + 0x005D) +#define KDATA_SPDIFIN_INT_METER (KDATA_BASE_ADDR + 0x005E) + +#define KDATA_DSP_RESET_COUNT (KDATA_BASE_ADDR + 0x005F) +#define KDATA_DEBUG_OUTPUT (KDATA_BASE_ADDR + 0x0060) + +#define KDATA_KERNEL_ISR_LIST (KDATA_BASE_ADDR + 0x0061) + +#define KDATA_KERNEL_ISR_CBSR1 (KDATA_BASE_ADDR + 0x0062) +#define KDATA_KERNEL_ISR_CBER1 (KDATA_BASE_ADDR + 0x0063) +#define KDATA_KERNEL_ISR_CBCR (KDATA_BASE_ADDR + 0x0064) +#define KDATA_KERNEL_ISR_AR0 (KDATA_BASE_ADDR + 0x0065) +#define KDATA_KERNEL_ISR_AR1 (KDATA_BASE_ADDR + 0x0066) +#define KDATA_KERNEL_ISR_AR2 (KDATA_BASE_ADDR + 0x0067) +#define KDATA_KERNEL_ISR_AR3 (KDATA_BASE_ADDR + 0x0068) +#define KDATA_KERNEL_ISR_AR4 (KDATA_BASE_ADDR + 0x0069) +#define KDATA_KERNEL_ISR_AR5 (KDATA_BASE_ADDR + 0x006A) +#define KDATA_KERNEL_ISR_BRCR (KDATA_BASE_ADDR + 0x006B) +#define KDATA_KERNEL_ISR_PASR (KDATA_BASE_ADDR + 0x006C) +#define KDATA_KERNEL_ISR_PAER (KDATA_BASE_ADDR + 0x006D) + +#define KDATA_CLIENT_SCRATCH0 (KDATA_BASE_ADDR + 0x006E) +#define KDATA_CLIENT_SCRATCH1 (KDATA_BASE_ADDR + 0x006F) +#define KDATA_KERNEL_SCRATCH (KDATA_BASE_ADDR + 0x0070) +#define KDATA_KERNEL_ISR_SCRATCH (KDATA_BASE_ADDR + 0x0071) + +#define KDATA_OUEUE_LEFT (KDATA_BASE_ADDR + 0x0072) +#define KDATA_QUEUE_RIGHT (KDATA_BASE_ADDR + 0x0073) + +#define KDATA_ADC1_REQUEST (KDATA_BASE_ADDR + 0x0074) +#define KDATA_ADC2_REQUEST (KDATA_BASE_ADDR + 0x0075) +#define KDATA_CD_REQUEST (KDATA_BASE_ADDR + 0x0076) +#define KDATA_MIC_REQUEST (KDATA_BASE_ADDR + 0x0077) + +#define KDATA_ADC1_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0078) +#define KDATA_ADC2_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0079) +#define KDATA_CD_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007A) +#define KDATA_MIC_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007B) +#define KDATA_MIC_SYNC_COUNTER (KDATA_BASE_ADDR + 0x007C) + +/* + * second 'segment' (?) reserved for mixer + * buffers.. + */ + +#define KDATA_MIXER_WORD0 (KDATA_BASE_ADDR2 + 0x0000) +#define KDATA_MIXER_WORD1 (KDATA_BASE_ADDR2 + 0x0001) +#define KDATA_MIXER_WORD2 (KDATA_BASE_ADDR2 + 0x0002) +#define KDATA_MIXER_WORD3 (KDATA_BASE_ADDR2 + 0x0003) +#define KDATA_MIXER_WORD4 (KDATA_BASE_ADDR2 + 0x0004) +#define KDATA_MIXER_WORD5 (KDATA_BASE_ADDR2 + 0x0005) +#define KDATA_MIXER_WORD6 (KDATA_BASE_ADDR2 + 0x0006) +#define KDATA_MIXER_WORD7 (KDATA_BASE_ADDR2 + 0x0007) +#define KDATA_MIXER_WORD8 (KDATA_BASE_ADDR2 + 0x0008) +#define KDATA_MIXER_WORD9 (KDATA_BASE_ADDR2 + 0x0009) +#define KDATA_MIXER_WORDA (KDATA_BASE_ADDR2 + 0x000A) +#define KDATA_MIXER_WORDB (KDATA_BASE_ADDR2 + 0x000B) +#define KDATA_MIXER_WORDC (KDATA_BASE_ADDR2 + 0x000C) +#define KDATA_MIXER_WORDD (KDATA_BASE_ADDR2 + 0x000D) +#define KDATA_MIXER_WORDE (KDATA_BASE_ADDR2 + 0x000E) +#define KDATA_MIXER_WORDF (KDATA_BASE_ADDR2 + 0x000F) + +#define KDATA_MIXER_XFER0 (KDATA_BASE_ADDR2 + 0x0010) +#define KDATA_MIXER_XFER1 (KDATA_BASE_ADDR2 + 0x0011) +#define KDATA_MIXER_XFER2 (KDATA_BASE_ADDR2 + 0x0012) +#define KDATA_MIXER_XFER3 (KDATA_BASE_ADDR2 + 0x0013) +#define KDATA_MIXER_XFER4 (KDATA_BASE_ADDR2 + 0x0014) +#define KDATA_MIXER_XFER5 (KDATA_BASE_ADDR2 + 0x0015) +#define KDATA_MIXER_XFER6 (KDATA_BASE_ADDR2 + 0x0016) +#define KDATA_MIXER_XFER7 (KDATA_BASE_ADDR2 + 0x0017) +#define KDATA_MIXER_XFER8 (KDATA_BASE_ADDR2 + 0x0018) +#define KDATA_MIXER_XFER9 (KDATA_BASE_ADDR2 + 0x0019) +#define KDATA_MIXER_XFER_ENDMARK (KDATA_BASE_ADDR2 + 0x001A) + +#define KDATA_MIXER_TASK_NUMBER (KDATA_BASE_ADDR2 + 0x001B) +#define KDATA_CURRENT_MIXER (KDATA_BASE_ADDR2 + 0x001C) +#define KDATA_MIXER_ACTIVE (KDATA_BASE_ADDR2 + 0x001D) +#define KDATA_MIXER_BANK_STATUS (KDATA_BASE_ADDR2 + 0x001E) +#define KDATA_DAC_LEFT_VOLUME (KDATA_BASE_ADDR2 + 0x001F) +#define KDATA_DAC_RIGHT_VOLUME (KDATA_BASE_ADDR2 + 0x0020) + +#define MAX_INSTANCE_MINISRC (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC) +#define MAX_VIRTUAL_DMA_CHANNELS (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0) +#define MAX_VIRTUAL_MIXER_CHANNELS (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0) +#define MAX_VIRTUAL_ADC1_CHANNELS (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0) + +/* + * client data area offsets + */ +#define CDATA_INSTANCE_READY 0x00 + +#define CDATA_HOST_SRC_ADDRL 0x01 +#define CDATA_HOST_SRC_ADDRH 0x02 +#define CDATA_HOST_SRC_END_PLUS_1L 0x03 +#define CDATA_HOST_SRC_END_PLUS_1H 0x04 +#define CDATA_HOST_SRC_CURRENTL 0x05 +#define CDATA_HOST_SRC_CURRENTH 0x06 + +#define CDATA_IN_BUF_CONNECT 0x07 +#define CDATA_OUT_BUF_CONNECT 0x08 + +#define CDATA_IN_BUF_BEGIN 0x09 +#define CDATA_IN_BUF_END_PLUS_1 0x0A +#define CDATA_IN_BUF_HEAD 0x0B +#define CDATA_IN_BUF_TAIL 0x0C +#define CDATA_OUT_BUF_BEGIN 0x0D +#define CDATA_OUT_BUF_END_PLUS_1 0x0E +#define CDATA_OUT_BUF_HEAD 0x0F +#define CDATA_OUT_BUF_TAIL 0x10 + +#define CDATA_DMA_CONTROL 0x11 +#define CDATA_RESERVED 0x12 + +#define CDATA_FREQUENCY 0x13 +#define CDATA_LEFT_VOLUME 0x14 +#define CDATA_RIGHT_VOLUME 0x15 +#define CDATA_LEFT_SUR_VOL 0x16 +#define CDATA_RIGHT_SUR_VOL 0x17 + +#define CDATA_HEADER_LEN 0x18 + +#define SRC3_DIRECTION_OFFSET CDATA_HEADER_LEN +#define SRC3_MODE_OFFSET (CDATA_HEADER_LEN + 1) +#define SRC3_WORD_LENGTH_OFFSET (CDATA_HEADER_LEN + 2) +#define SRC3_PARAMETER_OFFSET (CDATA_HEADER_LEN + 3) +#define SRC3_COEFF_ADDR_OFFSET (CDATA_HEADER_LEN + 8) +#define SRC3_FILTAP_ADDR_OFFSET (CDATA_HEADER_LEN + 10) +#define SRC3_TEMP_INBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 16) +#define SRC3_TEMP_OUTBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 17) + +#define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 ) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 ) +#define MINISRC_BIQUAD_STAGE 2 +#define MINISRC_COEF_LOC 0X175 + +#define DMACONTROL_BLOCK_MASK 0x000F +#define DMAC_BLOCK0_SELECTOR 0x0000 +#define DMAC_BLOCK1_SELECTOR 0x0001 +#define DMAC_BLOCK2_SELECTOR 0x0002 +#define DMAC_BLOCK3_SELECTOR 0x0003 +#define DMAC_BLOCK4_SELECTOR 0x0004 +#define DMAC_BLOCK5_SELECTOR 0x0005 +#define DMAC_BLOCK6_SELECTOR 0x0006 +#define DMAC_BLOCK7_SELECTOR 0x0007 +#define DMAC_BLOCK8_SELECTOR 0x0008 +#define DMAC_BLOCK9_SELECTOR 0x0009 +#define DMAC_BLOCKA_SELECTOR 0x000A +#define DMAC_BLOCKB_SELECTOR 0x000B +#define DMAC_BLOCKC_SELECTOR 0x000C +#define DMAC_BLOCKD_SELECTOR 0x000D +#define DMAC_BLOCKE_SELECTOR 0x000E +#define DMAC_BLOCKF_SELECTOR 0x000F +#define DMACONTROL_PAGE_MASK 0x00F0 +#define DMAC_PAGE0_SELECTOR 0x0030 +#define DMAC_PAGE1_SELECTOR 0x0020 +#define DMAC_PAGE2_SELECTOR 0x0010 +#define DMAC_PAGE3_SELECTOR 0x0000 +#define DMACONTROL_AUTOREPEAT 0x1000 +#define DMACONTROL_STOPPED 0x2000 +#define DMACONTROL_DIRECTION 0x0100 + + +/* + * DSP Code images + */ + +u16 assp_kernel_image[] = { + 0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, + 0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, + 0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, + 0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, + 0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, + 0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, + 0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, + 0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, + 0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, + 0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, + 0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, + 0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, + 0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, + 0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, + 0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, + 0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, + 0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, + 0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, + 0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, + 0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, + 0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, + 0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, + 0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, + 0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, + 0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, + 0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, + 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, + 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, + 0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, + 0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, + 0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, + 0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, + 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, + 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, + 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, + 0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, + 0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, + 0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, + 0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, + 0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, + 0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, + 0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, + 0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, + 0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, + 0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, + 0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, + 0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, + 0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, + 0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, + 0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, + 0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, + 0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, + 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, + 0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, + 0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, + 0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, + 0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, + 0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, + 0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, + 0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, + 0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, + 0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, + 0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, + 0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, + 0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, + 0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, + 0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, + 0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, + 0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, + 0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, + 0xBE3A, +}; + +/* + * Mini sample rate converter code image + * that is to be loaded at 0x400 on the DSP. + */ +u16 assp_minisrc_image[] = { + + 0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, + 0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, + 0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, + 0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, + 0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, + 0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, + 0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, + 0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, + 0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, + 0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, + 0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, + 0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, + 0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, + 0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, + 0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, + 0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, + 0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, + 0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, + 0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, + 0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, + 0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, + 0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, + 0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, + 0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, + 0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, + 0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, + 0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, + 0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, + 0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, + 0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, + 0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, + 0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + diff -Nru a/sound/oss/maestro_tables.h b/sound/oss/maestro_tables.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/maestro_tables.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,333 @@ +/* + * Set up data block for the ESS soundblaster emulation. This is like + * the example code from ftp.esstech.com.tw (104T31.ZIP) + */ + +u16 asp_block_0[]= { + 0x7980, 0x003a, 0x7980, 0x007d, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, + 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, + 0x7980, 0x0be6, 0x7980, 0x0069, 0xbe3a, 0x8b00, 0x8048, 0x6945, + 0xb801, 0x9045, 0x6941, 0xe388, 0x0031, 0x6944, 0xba50, 0xe38c, + 0x0031, 0x8b88, 0x6942, 0x304a, 0xe308, 0x0028, 0x6949, 0x9042, + 0x0042, 0xafa0, 0x8000, 0xafa0, 0x8000, 0x8042, 0x6944, 0xb801, + 0x9044, 0x0048, 0xbe3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xbe41, 0xbf80, 0x0101, 0x8806, 0x8804, 0xb912, + 0x8807, 0xbc26, 0xbe40, 0xb92f, 0x9001, 0x9002, 0x003b, 0x0122, + 0x7a88, 0x0055, 0x003a, 0x013c, 0x7a88, 0x0055, 0x4902, 0xe100, + 0x0800, 0x0c02, 0xa000, 0x7980, 0x004e, 0xb908, 0x8809, 0xbec6, + 0x0067, 0x4a80, 0xe200, 0x0065, 0x4880, 0xe200, 0x0063, 0xb97f, + 0x6e80, 0x7980, 0x0066, 0x1089, 0x9088, 0xb900, 0x90a9, 0x8ba8, + 0xef00, 0x803d, 0x0003, 0x1007, 0x881f, 0x8b88, 0xbb0f, 0xada0, + 0x0810, 0x9003, 0x4903, 0xe200, 0x007b, 0x9002, 0x6a05, 0x6d04, + 0x9803, 0x9804, 0x9005, 0x003d, 0xbe3a, 0xaf06, 0x0000, 0x803d, + 0x8b88, 0x6906, 0x8810, 0xaf06, 0x0001, 0xbfb0, 0x00ff, 0xbaf5, + 0xe3cc, 0x009a, 0x5e06, 0x003f, 0xbf80, 0x0080, 0x4a06, 0xe200, + 0x0095, 0x6e80, 0x6d06, 0x7980, 0x009b, 0x9080, 0x0810, 0xba46, + 0x8810, 0x8b00, 0x1006, 0x9080, 0x003d, 0xbe3a, 0x1024, 0x2009, + 0x8810, 0x8b88, 0x8b00, 0x1089, 0xbfb0, 0x000e, 0xbe0a, 0x880d, + 0x903e, 0x1038, 0x2008, 0x8810, 0x1037, 0x2008, 0x8811, 0x5f3e, + 0x0000, 0x8b00, 0xf500, 0x5e80, 0xfffe, 0x1039, 0x2008, 0x8815, + 0x8b8d, 0x0231, 0x0333, 0x108a, 0x90ad, 0x1025, 0x200a, 0x8815, + 0x0605, 0xb91f, 0x8809, 0x4f16, 0x1080, 0xf600, 0xbfb0, + 0x0003, 0xbfb0, 0x0007, 0x9089, 0xbe47, 0xbe43, 0xbec6, + 0x00f6, 0x4f8b, 0x14a8, 0xe500, 0x6380, 0x8b89, 0x4e8a, + 0xbfe3, 0xe500, 0x2480, 0xbe46, 0x8b9d, 0xbfe7, 0xbfb0, + 0x00ff, 0x288c, 0x2026, 0x8814, 0xbe47, 0x8b00, 0x738f, + 0x54aa, 0xbe03, 0xbe0a, 0x2fa8, 0x988a, 0x8da9, 0xbe03, + 0x4d8e, 0xbfe1, 0xf500, 0x6180, 0x9880, 0x8ba9, 0xbe03, + 0x4c8e, 0xbfe1, 0xf500, 0x6180, 0x9880, 0x8ba9, 0xbe42, + 0x1039, 0x2008, 0x8815, 0x8b8d, 0x8b00, 0x8d8a, 0x7980, + 0x0bcf}; + +u16 asp_block_1[]={ + 0x0005, 0xb900, 0x9008, 0x9009, 0x900a, 0xbb3f, 0x90a0, + 0x001a, 0x011b, 0x021c, 0x0323, 0x1089, 0x9015, 0x108a, + 0x9016, 0x108b, 0x9017, 0x1088, 0x9018, 0x0137, 0x0524, + 0x8b8d, 0x4f16, 0xe200, 0x0827, 0x4f15, 0xe200, 0x0827, + 0x7a80, 0x08e6, 0x102c, 0xb802, 0x8813, 0x8b8b, 0xb900, + 0x90a0, 0x908d, 0x7980, 0x0833, 0x7a80, 0x0913, 0x7803, + 0x7a80, 0x0913, 0x102c, 0xb802, 0x8813, 0x8b8b, 0xb903, + 0x90a0, 0x908d, 0x7c02, 0x4f16, 0xe200, 0x0845, 0x4e15, + 0xe200, 0x0845, 0x7a80, 0x08e6, 0x102c, 0xb806, 0x8813, + 0x8b8b, 0xb901, 0x90a0, 0x908d, 0x7980, 0x0851, 0x7a80, + 0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, 0xb806, 0x8813, + 0x8b8b, 0xb904, 0x90a0, 0x908d, 0x7c02, 0x4f16, 0xe200, + 0x0863, 0x4d15, 0xe200, 0x0863, 0x7a80, 0x08e6, 0x102c, + 0xb80a, 0x8813, 0x8b8b, 0xb902, 0x90a0, 0x908d, 0x7980, + 0x086f, 0x7a80, 0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, + 0xb80a, 0x8813, 0x8b8b, 0xb905, 0x90a0, 0x908d, 0x7801, + 0x7a80, 0x0913, 0x7801, 0x7a80, 0x0913, 0x7801, 0x7a80, + 0x0913, 0x1024, 0xbf90, 0x0100, 0x8815, 0x4f16, 0xe200, + 0x088e, 0x4c15, 0xe200, 0x088e, 0x7a80, 0x08e6, 0x102c, + 0xb814, 0x8813, 0x8b8b, 0xbf80, 0x0100, 0x90a0, 0x908d, + 0x7980, 0x089b, 0x7a80, 0x0913, 0x7803, 0x7a80, 0x0913, + 0x102c, 0xb814, 0x8813, 0x8b8b, 0xbf80, 0x0103, 0x90a0, + 0x908d, 0x7c02, 0x4f16, 0xe200, 0x08ae, 0x4b15, 0xe200, + 0x08ae, 0x7a80, 0x08e6, 0x102c, 0xb818, 0x8813, 0x8b8b, + 0xbf80, 0x0101, 0x90a0, 0x908d, 0x7980, 0x08bb, 0x7a80, + 0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, 0xb818, 0x8813, + 0x8b8b, 0xbf80, 0x0104, 0x90a0, 0x908d, 0x7c02, 0x4f16, + 0xe200, 0x08ce, 0x4a15, 0xe200, 0x08ce, 0x7a80, 0x08e6, + 0x102c, 0xb81c, 0x8813, 0x8b8b, 0xbf80, 0x0102, 0x90a0, + 0x908d, 0x7980, 0x08db, 0x7a80, 0x0913, 0x7803, 0x7a80, + 0x0913, 0x102c, 0xb81c, 0x8813, 0x8b8b, 0xbf80, 0x0105, + 0x90a0, 0x908d, 0x7801, 0x7a80, 0x0913, 0x7801, 0x7a80, + 0x0913, 0x7801, 0x7a80, 0x0913, 0x7980, 0x0920, 0x4f80, + 0x7803, 0xe100, 0x08fe, 0x4f89, 0xe100, 0x08f5, 0xb901, + 0x90a0, 0xb902, 0x90a0, 0x90a0, 0xb906, 0x90ad, 0xef00, + 0xb901, 0x90a0, 0xb906, 0x90a0, 0xb900, 0x90a0, 0xb906, + 0x90ad, 0xef00, 0x4f89, 0xe100, 0x090a, 0xb905, 0x90a0, + 0xb900, 0x90a0, 0xb902, 0x90a0, 0xb906, 0x90ad, 0xef00, + 0xb905, 0x90a0, 0xb900, 0x90a0, 0xb906, 0x90a0, 0xb904, + 0x90ad, 0xef00, 0x4f89, 0xe100, 0x091b, 0xb901, 0x90a0, + 0xb906, 0x90ad, 0xef00, 0xb905, 0x90a0, 0xb904, 0x90ad, + 0xef00, 0xb91f, 0x8809, 0x0034, 0x8b88, 0xbec6, 0x0932, + 0x1313, 0xbe1e, 0x1014, 0xbe1a, 0xbe01, 0xbfe8, 0xbe17, + 0x6a13, 0x6214, 0xbe14, 0x9813, 0x9014, 0x98a0, 0xbe47, + 0x5f0f, 0x002e, 0xe200, 0x093d, 0xbf80, 0xffd2, 0x900f, + 0x7980, 0x0940, 0x100f, 0xb801, 0x900f, 0x400f, 0x8b00, + 0xe500, 0xbe01, 0xbe09, 0x9010, 0xbe46, 0x5f11, 0x003f, + 0xe200, 0x094f, 0xb900, 0x9011, 0x7980, 0x0952, 0x1011, + 0xb801, 0x9011, 0x1001, 0xe388, 0x0bcf, 0x1021, 0x2009, + 0x8813, 0x8b8b, 0x8b00, 0x1080, 0xbfe6, 0x7810, 0x8b00, + 0x8b00, 0x2180, 0xbfb0, 0x0007, 0x4c11, 0x8b00, 0xe100, + 0x096c, 0x4b11, 0x8b00, 0xe600, 0xb900, 0x7980, 0x096d, + 0xbe0a, 0x4a11, 0x8b00, 0xe500, 0xbe01, 0x9012, 0x1037, + 0x2008, 0x8811, 0x102e, 0x2008, 0x8812, 0x4a89, 0xb901, + 0xe500, 0x9019, 0xe100, 0x09c9, 0xb900, 0x9019, 0x4a18, + 0xe200, 0x09c9, 0x5f0a, 0x0010, 0xe200, 0x098d, 0x4b18, + 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, 0x0013, + 0xe200, 0x0997, 0x4b18, 0xb901, 0xe500, 0x9019, 0x7980, + 0x09c9, 0x5f0a, 0x0011, 0xe200, 0x09a4, 0x4f18, 0xb905, + 0xe500, 0x9080, 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, + 0x5f0a, 0x0014, 0xe200, 0x09b1, 0x4c18, 0xb904, 0xe500, + 0x9080, 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, + 0x0012, 0xe200, 0x09be, 0x4d18, 0xb905, 0xe500, 0x9080, + 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, 0x0015, + 0xe200, 0x09c9, 0x4e18, 0xb904, 0xe500, 0x9080, 0xb901, + 0xe500, 0x9019, 0x8b8a, 0x4f19, 0xe200, 0x09d4, 0x4b80, + 0xe200, 0x09d6, 0x5d80, 0x0020, 0x7980, 0x09d9, 0x5d80, + 0x0010, 0x4a80, 0xe200, 0x0bca, 0x1001, 0xba01, 0x9001, + 0x1024, 0x2009, 0x8815, 0x8b89, 0x4d8d, 0xe200, 0x09f8, + 0x4f16, 0xe100, 0x09ed, 0x8b89, 0x1080, 0xbfc0, 0x000c, + 0x908c, 0x7980, 0x09f8, 0x4b89, 0x108d, 0xf600, 0xbfb0, + 0x0003, 0x4a89, 0x8b00, 0xf500, 0xbfc0, 0x0008, 0x908c, + 0x1022, 0x2009, 0x8811, 0x102e, 0x2008, 0x8812, 0x102f, + 0x2008, 0x8813, 0x1020, 0x200a, 0x8814, 0x101d, 0x200a, + 0x8815, 0x8b8a, 0x4f19, 0xe100, 0x0a11, 0x5e80, 0x0020, + 0x5d80, 0x0018, 0x7980, 0x0a3e, 0x4f80, 0xe200, 0x0a1e, + 0x8b8b, 0x108a, 0xbfa0, 0x7fc0, 0x8b00, 0xf704, 0x5c80, + 0x0003, 0x7980, 0x0a3e, 0x4e80, 0xe200, 0x0a36, 0x8b8c, + 0x178b, 0xbe01, 0xbfb7, 0x00f0, 0x308a, 0xe344, 0x0a3e, + 0x8b8d, 0x4a8a, 0x8b00, 0xe200, 0x0a32, 0x5c80, 0x0006, + 0x7980, 0x0a3e, 0x5c80, 0x000a, 0x7980, 0x0a3e, 0x4c80, + 0xe200, 0x0a3e, 0x4b80, 0x8b00, 0xf500, 0x5c80, 0x0019, + 0x4f80, 0xe200, 0x0a4d, 0x101f, 0x200a, 0x8816, 0x8b00, + 0x8b00, 0x8b8e, 0x1780, 0xbfb7, 0x00f0, 0x900b, 0x7980, + 0x0a64, 0x4e80, 0xe200, 0x0a5c, 0x101f, 0x200a, 0x8816, + 0x8b00, 0x8b00, 0x8b8e, 0x1b80, 0xbfbb, 0x000f, 0x900b, + 0x7980, 0x0a64, 0x4c80, 0xe200, 0x0a64, 0x8b8c, 0x1b80, + 0xbfbb, 0x000f, 0x900b, 0x8b89, 0x1880, 0xbfb8, 0x001c, + 0x4917, 0xe100, 0x0a6e, 0x4f80, 0x7980, 0x0a70, 0x4e80, + 0x8b00, 0xf500, 0xbf98, 0x0002, 0x8b8d, 0x4b89, 0x8b00, + 0xe600, 0xbfe1, 0x903e, 0xbe43, 0x6a0b, 0x613e, 0xbfe8, + 0x980c, 0xbe42, 0x7c10, 0x1080, 0xbfe5, 0xbe1e, 0x7810, + 0x1280, 0xbfb2, 0x001f, 0xbe11, 0x2027, 0x8811, 0x101e, + 0x200a, 0x8816, 0x8b00, 0x128e, 0x4980, 0xe100, 0x0a9b, + 0x4880, 0xe100, 0x0a98, 0xb900, 0x7980, 0x0a9f, 0xbe0a, + 0x7980, 0x0a9f, 0x4880, 0xe200, 0x0a9f, 0xbe09, 0xbe1e, + 0x158d, 0xbfb5, 0x003f, 0xbe11, 0x4889, 0xe200, 0x0aae, + 0xbe1e, 0x1010, 0x4818, 0x8b00, 0xe600, 0xbfe1, 0xbe11, + 0xbfe1, 0x8811, 0xbe1e, 0xb9ff, 0x8819, 0x8b00, 0x8b00, + 0xbf46, 0x8b00, 0xe600, 0xbe1f, 0xbe01, 0xbfb0, 0x00ff, + 0x202a, 0x8811, 0x8b00, 0x8b00, 0x108a, 0x900d, 0xb91f, + 0x8809, 0x0732, 0x730d, 0x4f80, 0xe200, 0x0ad8, 0x1028, + 0x200c, 0x8815, 0xbe43, 0x8b8b, 0x6a8d, 0xbec6, 0x0ad4, + 0x618b, 0x9880, 0x548f, 0x8dad, 0xbe42, 0x7980, 0x0b24, + 0x4e80, 0xe200, 0x0af9, 0x8b8c, 0x178b, 0xbe01, 0xbfb7, + 0x00f0, 0x903e, 0x1029, 0x200c, 0x8815, 0x108d, 0xbec6, + 0x0af6, 0x308b, 0xbe1e, 0x303e, 0x903f, 0x403f, 0xbe1f, + 0xe500, 0x103e, 0x9080, 0xbfe6, 0x202a, 0x8811, 0x8b00, + 0x1089, 0x548f, 0x8dad, 0x7980, 0x0b24, 0x4c8b, 0xe200, + 0x0b1b, 0x1029, 0x200c, 0x8815, 0xbf80, 0x007f, 0x903e, + 0x108d, 0xbec6, 0x0b14, 0x308b, 0xbe1e, 0x303e, 0x903f, + 0x403f, 0xbe1f, 0xe500, 0xb900, 0x9080, 0xbfe6, 0x202a, + 0x8811, 0x8b00, 0x1089, 0x548f, 0x8dad, 0x8b8a, 0xf500, + 0x5e80, 0xffdf, 0x7980, 0x0b24, 0x1089, 0xbfe6, 0x202a, + 0x8811, 0x8b00, 0x8b00, 0x548f, 0xbb1f, 0x8da0, 0x8b8f, + 0x0732, 0x1021, 0x2009, 0x8811, 0x8b89, 0x101d, 0x200a, + 0x8812, 0x1080, 0x7810, 0x8b00, 0xbe47, 0x288a, 0xbfb0, + 0x03ff, 0x4989, 0xe200, 0x0b3e, 0xbe1e, 0x1012, 0x4918, + 0x8b00, 0xe600, 0xbe0a, 0xbe11, 0x900e, 0xbe46, 0x108a, + 0xbfb0, 0x001c, 0xbfe1, 0x880d, 0x8b00, 0x6b0e, 0xbe0a, + 0x880c, 0x108c, 0xbfb0, 0x000f, 0x202d, 0x8814, 0x8b00, + 0x8b00, 0x5589, 0xbf03, 0x8c0e, 0xbf00, 0x1030, 0x2008, + 0x8811, 0xb91f, 0x8809, 0x108b, 0x0333, 0xbec6, 0x0b5f, + 0x200e, 0x90a0, 0x8b00, 0x8b89, 0x9080, 0x4a18, 0xe200, + 0x0b7a, 0x5f0a, 0x0011, 0xe200, 0x0b6c, 0x4f18, 0xe900, + 0x0b7c, 0x5f0a, 0x0014, 0xe200, 0x0b73, 0x4c18, 0xe900, + 0x0b9b, 0x5f0a, 0x0015, 0xe200, 0x0b7a, 0x4e18, 0xe900, + 0x0bb2, 0x7980, 0x009e, 0x0034, 0xb91f, 0x8809, 0x0333, + 0x8b8b, 0xbec6, 0x0b99, 0x1280, 0x6c80, 0xbfea, 0xbe1e, + 0x1580, 0x6c88, 0xbfec, 0xbe13, 0x903e, 0x6cab, 0x903f, + 0x4f3e, 0xb900, 0xf500, 0xbf80, 0x8000, 0x4f3f, 0xbf90, + 0x0d00, 0xf500, 0xbf90, 0x2700, 0x90a0, 0xef00, 0x0034, + 0xb91f, 0x8809, 0x0333, 0x8b88, 0xbec6, 0x0bb0, 0x1eab, + 0x6c80, 0xbfed, 0x903e, 0x4180, 0xb900, 0xf500, 0xbf80, + 0x8000, 0x4f3e, 0x8b00, 0xf500, 0xbf90, 0x4000, 0x90a8, + 0xef00, 0x0034, 0xb91f, 0x8809, 0x0333, 0x8b8b, 0xbec6, + 0x0bc8, 0x1280, 0x6c8b, 0xbfea, 0xbe1e, 0x1580, 0x6c80, + 0xbfec, 0xbe13, 0x903e, 0x4f3e, 0xbf80, 0x4000, 0xf500, + 0xbf90, 0x8000, 0x90a0, 0xef00, 0x0231, 0x8b8a, 0xb900, + 0xbb20, 0x90a0, 0x5f08, 0x0023, 0xe100, 0x0043, 0x1008, + 0xb801, 0x9008, 0x102b, 0x2008, 0x8812, 0x8b00, 0x8b00, + 0x1080, 0x900a, 0x102c, 0x2008, 0x8812, 0x8b00, 0x8b00, + 0x1080, 0x9009, 0x7980, 0x0952, 0x8148, 0x6946, 0xb801, + 0x9046, 0xb901, 0x9041, 0x6944, 0xba08, 0xe344, 0x0bfe, + 0x9044, 0x8b89, 0x0143, 0x694b, 0x881f, 0xbb0f, 0xada0, + 0x8143, 0x0811, 0x304a, 0xe308, 0x0bfe, 0x6949, 0x9043, + 0x0148, 0xbe3a +}; + +u16 asp_block_2[]={ + 0x0000, 0x0000, 0x0003, 0x0003, 0x0001, 0x0001, + 0x0004, 0x0004, 0x0002, 0x0002, 0x0005, 0x0005, + 0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, + 0x0100, 0x0100, 0x0103, 0x0103, 0x0101, 0x0101, + 0x0104, 0x0104, 0x0102, 0x0102, 0x0105, 0x0105, + 0x0106, 0x0106, 0x0107, 0x0107, 0x0108, 0x0108 +}; + +u16 asp_block_3[]={ + 0x0000, 0x0000, 0x0000, 0x1200, 0x1200, 0x1280, 0x0000, 0x05d0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x1104, 0x1105, 0x1008, 0x1020, 0x1040, 0x1060, + 0x1080, 0x10a0, 0x10b0, 0x100d, 0x1010, 0x10e0, 0x2000, 0x2980, + 0x2b00, 0x2b40, 0x2a00, 0x2b90, 0x13dc, 0x2b80, 0x11bc, 0x134c, + 0x1370, 0x12e0, 0x1240, 0x1260, 0x12c0, 0x009e, 0x0045, 0x10bc, + 0x1394, 0x13b8, 0x11f6, 0x10f6, 0x11b0, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0815, 0x0956, 0x09df, 0x0be5, 0x0a19, 0x0a48, 0x0b37, + 0x0b5d, 0x0a8b, 0x0aae, 0x0ad2 }; + + +u16 asp_block_4[] = { + 0x0192, 0x04b6, 0x07d9, 0x0afb, 0x0e1c, 0x113a, 0x1455, 0x176e, + 0x1a83, 0x1d93, 0x209f, 0x23a7, 0x26a8, 0x29a4, 0x2c99, 0x2f87, + 0x326e, 0x354e, 0x3825, 0x3af3, 0x3db8, 0x4074, 0x4326, 0x45cd, + 0x486a, 0x4afb, 0x4d81, 0x4ffb, 0x5269, 0x54ca, 0x571e, 0x5964, + 0x5b9d, 0x5dc8, 0x5fe4, 0x61f1, 0x63ef, 0x65de, 0x67bd, 0x698c, + 0x6b4b, 0x6cf9, 0x6e97, 0x7023, 0x719e, 0x7308, 0x7460, 0x75a6, + 0x76d9, 0x77fb, 0x790a, 0x7a06, 0x7aef, 0x7bc6, 0x7c89, 0x7d3a, + 0x7dd6, 0x7e60, 0x7ed6, 0x7f38, 0x7f87, 0x7fc2, 0x7fea, 0x7ffe, + 0x7ffe, 0x7fea, 0x7fc2, 0x7f87, 0x7f38, 0x7ed6, 0x7e60, 0x7dd6, + 0x7d3a, 0x7c89, 0x7bc6, 0x7aef, 0x7a06, 0x790a, 0x77fb, 0x76d9, + 0x75a6, 0x7460, 0x7308, 0x719e, 0x7023, 0x6e97, 0x6cf9, 0x6b4b, + 0x698c, 0x67bd, 0x65de, 0x63ef, 0x61f1, 0x5fe4, 0x5dc8, 0x5b9d, + 0x5964, 0x571e, 0x54ca, 0x5269, 0x4ffb, 0x4d81, 0x4afb, 0x486a, + 0x45cd, 0x4326, 0x4074, 0x3db8, 0x3af3, 0x3825, 0x354e, 0x326e, + 0x2f87, 0x2c99, 0x29a4, 0x26a8, 0x23a7, 0x209f, 0x1d93, 0x1a83, + 0x176e, 0x1455, 0x113a, 0x0e1c, 0x0afb, 0x07d9, 0x04b6, 0x0192, + 0xfe6f, 0xfb4b, 0xf828, 0xf506, 0xf1e5, 0xeec7, 0xebac, 0xe893, + 0xe57e, 0xe26e, 0xdf62, 0xdc5a, 0xd959, 0xd65d, 0xd368, 0xd07a, + 0xcd93, 0xcab3, 0xc7dc, 0xc50e, 0xc249, 0xbf8d, 0xbcdb, 0xba34, + 0xb797, 0xb506, 0xb280, 0xb006, 0xad98, 0xab37, 0xa8e3, 0xa69d, + 0xa464, 0xa239, 0xa01d, 0x9e10, 0x9c12, 0x9a23, 0x9844, 0x9675, + 0x94b6, 0x9308, 0x916a, 0x8fde, 0x8e63, 0x8cf9, 0x8ba1, 0x8a5b, + 0x8928, 0x8806, 0x86f7, 0x85fb, 0x8512, 0x843b, 0x8378, 0x82c7, + 0x822b, 0x81a1, 0x812b, 0x80c9, 0x807a, 0x803f, 0x8017, 0x8003, + 0x8003, 0x8017, 0x803f, 0x807a, 0x80c9, 0x812b, 0x81a1, 0x822b, + 0x82c7, 0x8378, 0x843b, 0x8512, 0x85fb, 0x86f7, 0x8806, 0x8928, + 0x8a5b, 0x8ba1, 0x8cf9, 0x8e63, 0x8fde, 0x916a, 0x9308, 0x94b6, + 0x9675, 0x9844, 0x9a23, 0x9c12, 0x9e10, 0xa01d, 0xa239, 0xa464, + 0xa69d, 0xa8e3, 0xab37, 0xad98, 0xb006, 0xb280, 0xb506, 0xb797, + 0xba34, 0xbcdb, 0xbf8d, 0xc249, 0xc50e, 0xc7dc, 0xcab3, 0xcd93, + 0xd07a, 0xd368, 0xd65d, 0xd959, 0xdc5a, 0xdf62, 0xe26e, 0xe57e, + 0xe893, 0xebac, 0xeec7, 0xf1e5, 0xf506, 0xf828, 0xfb4b, 0xfe6f, + 0x7cc3, 0x725e, 0x68d5, 0x6017, 0x5813, 0x50b9, 0x49fb, 0x43cd, + 0x3e22, 0x38ef, 0x342b, 0x2fcc, 0x2bc9, 0x281c, 0x24be, 0x21a6, + 0x1ed1, 0x1c37, 0x19d5, 0x17a6, 0x15a5, 0x13ce, 0x121f, 0x1093, + 0x0f28, 0x0ddc, 0x0cab, 0x0b93, 0x0a92, 0x09a7, 0x08cf, 0x080a, + 0x0754, 0x06ae, 0x0615, 0x0589, 0x0509, 0x0494, 0x0428, 0x03c5, + 0x036a, 0x0317, 0x02cb, 0x0285, 0x0245, 0x020a, 0x01d4, 0x01a2, + 0x0175, 0x014b, 0x0125, 0x0102, 0x00e2, 0x00c5, 0x00aa, 0x0091, + 0x007b, 0x0066, 0x0053, 0x0041, 0x0031, 0x0022, 0x0015, 0x0009, + 0xfffe, 0xfff2, 0xffe5, 0xffd7, 0xffc8, 0xffb7, 0xffa5, 0xff91, + 0xff7b, 0xff64, 0xff4a, 0xff2e, 0xff0f, 0xfeee, 0xfec9, 0xfea1, + 0xfe76, 0xfe46, 0xfe13, 0xfdda, 0xfd9d, 0xfd5a, 0xfd11, 0xfcc1, + 0xfc6b, 0xfc0c, 0xfba5, 0xfb34, 0xfab9, 0xfa33, 0xf9a1, 0xf902, + 0xf854, 0xf797, 0xf6c8, 0xf5e7, 0xf4f1, 0xf3e5, 0xf2c1, 0xf183, + 0xf027, 0xeeac, 0xed0f, 0xeb4d, 0xe961, 0xe74a, 0xe501, 0xe284, + 0xdfcd, 0xdcd8, 0xd99d, 0xd618, 0xd242, 0xce12, 0xc982, 0xc487, + 0xbf1a, 0xb92e, 0xb2ba, 0xabaf, 0xa402, 0x9ba3, 0x9282, 0x888d, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e, 0x0010, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x000a, 0x000e, + 0x0010, 0x0014, 0x0016, 0x0018, 0x001a, 0x001c, 0x001e, 0x0020, + 0x0000, 0x0000, 0x0000, 0x000a, 0x0010, 0x0016, 0x001a, 0x001e, + 0x0020, 0x0024, 0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, + 0x0000, 0x0000, 0x0010, 0x001a, 0x0020, 0x0026, 0x002a, 0x002e, + 0x0030, 0x0034, 0x0036, 0x0038, 0x003a, 0x003c, 0x003e, 0x0040, + 0x0000, 0x0010, 0x0020, 0x002a, 0x0030, 0x0036, 0x003a, 0x003e, + 0x0040, 0x0044, 0x0046, 0x0048, 0x004a, 0x004c, 0x004e, 0x0050, + 0x0000, 0x0020, 0x0030, 0x003a, 0x0040, 0x0046, 0x004a, 0x004e, + 0x0050, 0x0054, 0x0056, 0x0058, 0x005a, 0x005c, 0x005e, 0x0060, + 0x0000, 0x0030, 0x0040, 0x004a, 0x0050, 0x0056, 0x005a, 0x005e, + 0x0060, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, 0x0070, + 0x0008, 0x0021, 0x0042, 0x0064, 0x0086, 0x00a8, 0x00cb, 0x00ee, + 0x0111, 0x0135, 0x0158, 0x017d, 0x01a1, 0x01c6, 0x01eb, 0x0211, + 0x0236, 0x025d, 0x0283, 0x02aa, 0x02d1, 0x02f9, 0x0321, 0x0349, + 0x0372, 0x039b, 0x03c4, 0x03ee, 0x0418, 0x0442, 0x046d, 0x0499, + 0x04c4, 0x04f0, 0x051d, 0x054a, 0x0577, 0x05a5, 0x05d3, 0x0601, + 0x0630, 0x0660, 0x0690, 0x06c0, 0x06f1, 0x0722, 0x0753, 0x0785, + 0x07b8, 0x07eb, 0x081e, 0x0852, 0x0886, 0x08bb, 0x08f0, 0x0926, + 0x095c, 0x0993, 0x09ca, 0x0a02, 0x0a3a, 0x0a73, 0x0aac, 0x0ae6, + 0x0b20, 0x0b5b, 0x0b96, 0x0bd2, 0x0c0e, 0x0c4b, 0x0c89, 0x0cc7, + 0x0d05, 0x0d44, 0x0d84, 0x0dc5, 0x0e05, 0x0e47, 0x0e89, 0x0ecc, + 0x0f0f, 0x0f53, 0x0f97, 0x0fdd, 0x1022, 0x1069, 0x10b0, 0x10f7, + 0x1140, 0x1189, 0x11d2, 0x121c, 0x1267, 0x12b3, 0x12ff, 0x134c, + 0x139a, 0x13e8, 0x1438, 0x1487, 0x14d8, 0x1529, 0x157b, 0x15ce, + 0x1621, 0x1676, 0x16cb, 0x1720, 0x1777, 0x17ce, 0x1826, 0x187f, + 0x18d9, 0x1934, 0x198f, 0x19eb, 0x1a48, 0x1aa6, 0x1b05, 0x1b64, + 0x1bc5, 0x1c26, 0x1c88, 0x1ceb, 0x1d4f, 0x1db4, 0x1e1a, 0x1e80, + 0x1ee8, 0x1f51, 0x1fba, 0x2025, 0x2090, 0x20fc, 0x216a, 0x21d8, + 0x2247, 0x22b8, 0x2329, 0x239b, 0x240f, 0x2483, 0x24f9, 0x256f, + 0x25e7, 0x2660, 0x26da, 0x2755, 0x27d1, 0x284e, 0x28cc, 0x294b, + 0x29cc, 0x2a4e, 0x2ad1, 0x2b55, 0x2bda, 0x2c61, 0x2ce8, 0x2d71, + 0x2dfb, 0x2e87, 0x2f13, 0x2fa1, 0x3031, 0x30c1, 0x3153, 0x31e6, + 0x327b, 0x3310, 0x33a8, 0x3440, 0x34da, 0x3575, 0x3612, 0x36b0, + 0x3750, 0x37f1, 0x3893, 0x3937, 0x39dc, 0x3a83, 0x3b2c, 0x3bd6, + 0x3c81, 0x3d2e, 0x3ddd, 0x3e8d, 0x3f3f, 0x3ff2, 0x40a7, 0x415d, + 0x4216, 0x42d0, 0x438b, 0x4448, 0x4507, 0x45c8, 0x468b, 0x474f, + 0x4815, 0x48dd, 0x49a6, 0x4a72, 0x4b3f, 0x4c0e, 0x4cdf, 0x4db2, + 0x4e87, 0x4f5d, 0x5036, 0x5111, 0x51ed, 0x52cc, 0x53ac, 0x548f, + 0x5573, 0x565a, 0x5743, 0x582e, 0x591b, 0x5a0a, 0x5afb, 0x5bef, + 0x5ce4, 0x5ddc, 0x5ed7, 0x5fd3, 0x60d2, 0x61d3, 0x62d6, 0x63dc, + 0x64e4, 0x65ee, 0x66fb, 0x680a, 0x691c, 0x6a30, 0x6b47, 0x6c60, + 0x6d7c, 0x6e9a, 0x6fbb, 0x70de, 0x7204, 0x732d, 0x7459, 0x7587, + 0x76b8, 0x77eb, 0x7922, 0x7a5b, 0x7b97, 0x7cd6, 0x7e18, 0x7f5d, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, + 0x0003, 0x0004, 0x0004, 0x0005, 0x0006, 0x0008, 0x0009, 0x000a, + 0x000c, 0x0010, 0x0012, 0x0015, 0x0019, 0x0022, 0x0024, 0x002a, + 0x0031, 0x003e, 0x0049, 0x0055, 0x0062, 0x007c, 0x0092, 0x00a9, + 0x00c4, 0x00fc, 0x0125, 0x0152, 0x0187, 0x01f2, 0x024a, 0x02a4, + 0x030d, 0x03e3, 0x0492, 0x0547, 0x061b, 0x07c7, 0x0923, 0x0a8d, + 0x0c19, 0x0eb3, 0x1228, 0x14c1, 0x17fb, 0x1d17, 0x22f2, 0x2835, + 0x2dd4, 0x3cd0, 0x4cf5, 0x51cc, 0x7fff, 0x7fff, 0x7fff, 0x7fff, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, + 0x0002, 0x0003, 0x0004, 0x0005, 0x0005, 0x0007, 0x0008, 0x000a, + 0x000b, 0x000e, 0x0011, 0x0014, 0x0017, 0x001c, 0x0022, 0x0028, + 0x002e, 0x0039, 0x0045, 0x0050, 0x005c, 0x0073, 0x008a, 0x00a0, + 0x00b9, 0x00e7, 0x0114, 0x0141, 0x0172, 0x01ce, 0x0228, 0x0283, + 0x02e3, 0x039b, 0x0454, 0x0501, 0x05bf, 0x072a, 0x0899, 0x0a18, + 0x0b7e, 0x0e55, 0x10d3, 0x1404, 0x16c3, 0x16c3, 0x16c3, 0x16c3, + 0x0012, 0x0024, 0x0048, 0x006c, 0x0090, 0x00b4, 0x00d8, 0x00fc, + 0x0120, 0x0144, 0x0168, 0x0168, 0x01b0, 0x01b0, 0x021c, 0x021c, + 0x0000, 0x0003, 0x0008, 0x000b, 0x0001, 0x0004, 0x0009, 0x000c, + 0x0002, 0x0005, 0x000a, 0x000d, 0x0010, 0x0013, 0x0011, 0x0014, + 0x0012, 0x0015, 0x0100, 0x0103, 0x0108, 0x010b, 0x0101, 0x0104, + 0x0109, 0x010c, 0x0102, 0x0105, 0x010a, 0x010d, 0x0110, 0x0113, + 0x0111, 0x0114, 0x0112, 0x0115 }; diff -Nru a/sound/oss/maui.c b/sound/oss/maui.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/maui.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,477 @@ +/* + * sound/maui.c + * + * The low level driver for Turtle Beach Maui and Tropez. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Alan Cox General clean up, use kernel IRQ + * system + * Christoph Hellwig Adapted to module_init/module_exit + * Bartlomiej Zolnierkiewicz + * Added __init to download_code() + * + * Status: + * Andrew J. Kroll Tested 06/01/1999 with: + * * OSWF.MOT File Version: 1.15 + * * OSWF.MOT File Dated: 09/12/94 + * * Older versions will cause problems. + */ + +#include +#include +#include + +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include "sound_config.h" +#include "sound_firmware.h" + +#include "mpu401.h" + +static int maui_base = 0x330; + +static volatile int irq_ok = 0; +static int *maui_osp; + +#define HOST_DATA_PORT (maui_base + 2) +#define HOST_STAT_PORT (maui_base + 3) +#define HOST_CTRL_PORT (maui_base + 3) + +#define STAT_TX_INTR 0x40 +#define STAT_TX_AVAIL 0x20 +#define STAT_TX_IENA 0x10 +#define STAT_RX_INTR 0x04 +#define STAT_RX_AVAIL 0x02 +#define STAT_RX_IENA 0x01 + +static int (*orig_load_patch) (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) = NULL; + +#include "maui_boot.h" + +static int maui_wait(int mask) +{ + int i; + + /* + * Perform a short initial wait without sleeping + */ + + for (i = 0; i < 100; i++) + if (inb(HOST_STAT_PORT) & mask) + return 1; + + /* + * Wait up to 15 seconds with sleeping + */ + + for (i = 0; i < 150; i++) { + if (inb(HOST_STAT_PORT) & mask) + return 1; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 10); + if (signal_pending(current)) + return 0; + } + return 0; +} + +static int maui_read(void) +{ + if (maui_wait(STAT_RX_AVAIL)) + return inb(HOST_DATA_PORT); + return -1; +} + +static int maui_write(unsigned char data) +{ + if (maui_wait(STAT_TX_AVAIL)) { + outb((data), HOST_DATA_PORT); + return 1; + } + printk(KERN_WARNING "Maui: Write timeout\n"); + return 0; +} + +static void mauiintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + irq_ok = 1; +} + +static int __init download_code(void) +{ + int i, lines = 0; + int eol_seen = 0, done = 0; + int skip = 1; + + printk(KERN_INFO "Code download (%d bytes): ", maui_osLen); + + for (i = 0; i < maui_osLen; i++) { + if (maui_os[i] != '\r') { + if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n'))) { + skip = 0; + + if (maui_os[i] == '\n') + eol_seen = skip = 1; + else if (maui_os[i] == 'S') { + if (maui_os[i + 1] == '8') + done = 1; + if (!maui_write(0xF1)) + goto failure; + if (!maui_write('S')) + goto failure; + } else { + if (!maui_write(maui_os[i])) + goto failure; + } + + if (eol_seen) { + int c = 0; + int n; + + eol_seen = 0; + + for (n = 0; n < 2; n++) { + if (maui_wait(STAT_RX_AVAIL)) { + c = inb(HOST_DATA_PORT); + break; + } + } + if (c != 0x80) { + printk("Download not acknowledged\n"); + return 0; + } + else if (!(lines++ % 10)) + printk("."); + + if (done) { + printk("\n"); + printk(KERN_INFO "Download complete\n"); + return 1; + } + } + } + } + } + +failure: + printk("\n"); + printk(KERN_ERR "Download failed!!!\n"); + return 0; +} + +static int __init maui_init(int irq) +{ + unsigned char bits; + + switch (irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + printk(KERN_ERR "Maui: Invalid IRQ %d\n", irq); + return 0; + } + outb((0x00), HOST_CTRL_PORT); /* Reset */ + outb((bits), HOST_DATA_PORT); /* Set the IRQ bits */ + outb((bits | 0x80), HOST_DATA_PORT); /* Set the IRQ bits again? */ + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + outb((0xD0), HOST_CTRL_PORT); /* Cause interrupt */ + +#ifdef CONFIG_SMP + { + int i; + for (i = 0; i < 1000000 && !irq_ok; i++) + ; + if (!irq_ok) + return 0; + } +#endif + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + + printk(KERN_INFO "Turtle Beach Maui initialization\n"); + + if (!download_code()) + return 0; + + outb((0xE0), HOST_CTRL_PORT); /* Normal operation */ + + /* Select mpu401 mode */ + + maui_write(0xf0); + maui_write(1); + if (maui_read() != 0x80) { + maui_write(0xf0); + maui_write(1); + if (maui_read() != 0x80) + printk(KERN_ERR "Maui didn't acknowledge set HW mode command\n"); + } + printk(KERN_INFO "Maui initialized OK\n"); + return 1; +} + +static int maui_short_wait(int mask) { + int i; + + for (i = 0; i < 1000; i++) { + if (inb(HOST_STAT_PORT) & mask) { + return 1; + } + } + return 0; +} + +static int maui_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + + struct sysex_info header; + unsigned long left, src_offs; + int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header; + int i; + + if (format == SYSEX_PATCH) /* Handled by midi_synth.c */ + return orig_load_patch(dev, format, addr, offs, count, pmgr_flag); + + if (format != MAUI_PATCH) + { + printk(KERN_WARNING "Maui: Unknown patch format\n"); + } + if (count < hdr_size) { +/* printk("Maui error: Patch header too short\n");*/ + return -EINVAL; + } + count -= hdr_size; + + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + if(copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs)) + return -EFAULT; + + if (count < header.len) { + printk(KERN_ERR "Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len); + header.len = count; + } + left = header.len; + src_offs = 0; + + for (i = 0; i < left; i++) { + unsigned char data; + + if(get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]))) + return -EFAULT; + if (i == 0 && !(data & 0x80)) + return -EINVAL; + + if (maui_write(data) == -1) + return -EIO; + } + + if ((i = maui_read()) != 0x80) { + if (i != -1) + printk("Maui: Error status %02x\n", i); + return -EIO; + } + return 0; +} + +static int __init probe_maui(struct address_info *hw_config) +{ + int i; + int tmp1, tmp2, ret; + + if (check_region(hw_config->io_base, 8)) + return 0; + + maui_base = hw_config->io_base; + maui_osp = hw_config->osp; + + if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0) + return 0; + + /* + * Initialize the processor if necessary + */ + + if (maui_osLen > 0) { + if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) || + !maui_write(0x9F) || /* Report firmware version */ + !maui_short_wait(STAT_RX_AVAIL) || + maui_read() == -1 || maui_read() == -1) + if (!maui_init(hw_config->irq)) { + free_irq(hw_config->irq, NULL); + return 0; + } + } + if (!maui_write(0xCF)) /* Report hardware version */ { + printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); + free_irq(hw_config->irq, NULL); + return 0; + } + if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) { + printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); + free_irq(hw_config->irq, NULL); + return 0; + } + if (tmp1 == 0xff || tmp2 == 0xff) { + free_irq(hw_config->irq, NULL); + return 0; + } + printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2); + + if (!maui_write(0x9F)) /* Report firmware version */ + return 0; + if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) + return 0; + + printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2); + + if (!maui_write(0x85)) /* Report free DRAM */ + return 0; + tmp1 = 0; + for (i = 0; i < 4; i++) { + tmp1 |= maui_read() << (7 * i); + } + printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024); + + for (i = 0; i < 1000; i++) + if (probe_mpu401(hw_config)) + break; + + ret = probe_mpu401(hw_config); + + if (ret) + request_region(hw_config->io_base + 2, 6, "Maui"); + + return ret; +} + +static void __init attach_maui(struct address_info *hw_config) +{ + int this_dev; + + conf_printf("Maui", hw_config); + + hw_config->irq *= -1; + hw_config->name = "Maui"; + attach_mpu401(hw_config, THIS_MODULE); + + if (hw_config->slots[1] != -1) /* The MPU401 driver installed itself */ { + struct synth_operations *synth; + + this_dev = hw_config->slots[1]; + + /* + * Intercept patch loading calls so that they can be handled + * by the Maui driver. + */ + + synth = midi_devs[this_dev]->converter; + synth->id = "MAUI"; + + if (synth != NULL) { + orig_load_patch = synth->load_patch; + synth->load_patch = &maui_load_patch; + } else + printk(KERN_ERR "Maui: Can't install patch loader\n"); + } +} + +static void __exit unload_maui(struct address_info *hw_config) +{ + int irq = hw_config->irq; + release_region(hw_config->io_base + 2, 6); + unload_mpu401(hw_config); + + if (irq < 0) + irq = -irq; + if (irq > 0) + free_irq(irq, NULL); +} + +static int fw_load = 0; + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); + +/* + * Install a Maui card. Needs mpu401 loaded already. + */ + +static int __init init_maui(void) +{ + printk(KERN_INFO "Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.io_base = io; + cfg.irq = irq; + + if (cfg.io_base == -1 || cfg.irq == -1) { + printk(KERN_INFO "maui: irq and io must be set.\n"); + return -EINVAL; + } + + if (maui_os == NULL) { + fw_load = 1; + maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os); + } + if (probe_maui(&cfg) == 0) + return -ENODEV; + attach_maui(&cfg); + + return 0; +} + +static void __exit cleanup_maui(void) +{ + if (fw_load && maui_os) + vfree(maui_os); + unload_maui(&cfg); +} + +module_init(init_maui); +module_exit(cleanup_maui); + +#ifndef MODULE +static int __init setup_maui(char *str) +{ + /* io, irq */ + int ints[3]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + + return 1; +} + +__setup("maui=", setup_maui); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/midi_ctrl.h b/sound/oss/midi_ctrl.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/midi_ctrl.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,22 @@ +static unsigned char ctrl_def_values[128] = +{ + 0x40,0x00,0x40,0x40, 0x40,0x40,0x40,0x7f, /* 0 to 7 */ + 0x40,0x40,0x40,0x7f, 0x40,0x40,0x40,0x40, /* 8 to 15 */ + 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 16 to 23 */ + 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 24 to 31 */ + + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */ + + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */ + + 0x00,0x00,0x7f,0x7f, 0x7f,0x7f,0x00,0x00, /* 96 to 103 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */ +}; diff -Nru a/sound/oss/midi_syms.c b/sound/oss/midi_syms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/midi_syms.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,31 @@ +/* + * Exported symbols for midi driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include + +char midi_syms_symbol; + +#include "sound_config.h" +#define _MIDI_SYNTH_C_ +#include "midi_synth.h" + +EXPORT_SYMBOL(do_midi_msg); +EXPORT_SYMBOL(midi_synth_open); +EXPORT_SYMBOL(midi_synth_close); +EXPORT_SYMBOL(midi_synth_ioctl); +EXPORT_SYMBOL(midi_synth_kill_note); +EXPORT_SYMBOL(midi_synth_start_note); +EXPORT_SYMBOL(midi_synth_set_instr); +EXPORT_SYMBOL(midi_synth_reset); +EXPORT_SYMBOL(midi_synth_hw_control); +EXPORT_SYMBOL(midi_synth_aftertouch); +EXPORT_SYMBOL(midi_synth_controller); +EXPORT_SYMBOL(midi_synth_panning); +EXPORT_SYMBOL(midi_synth_setup_voice); +EXPORT_SYMBOL(midi_synth_send_sysex); +EXPORT_SYMBOL(midi_synth_bender); +EXPORT_SYMBOL(midi_synth_load_patch); +EXPORT_SYMBOL(MIDIbuf_avail); diff -Nru a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/midi_synth.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,697 @@ +/* + * sound/midi_synth.c + * + * High level midi sequencer manager for dumb MIDI interfaces. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Andrew Veliath : fixed running status in MIDI input state machine + */ +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include "sound_config.h" + +#define _MIDI_SYNTH_C_ + +#include "midi_synth.h" + +static int midi2synth[MAX_MIDI_DEV]; +static int sysex_state[MAX_MIDI_DEV] = +{0}; +static unsigned char prev_out_status[MAX_MIDI_DEV]; + +#define STORE(cmd) \ +{ \ + int len; \ + unsigned char obuf[8]; \ + cmd; \ + seq_input_event(obuf, len); \ +} + +#define _seqbuf obuf +#define _seqbufptr 0 +#define _SEQ_ADVBUF(x) len=x + +void +do_midi_msg(int synthno, unsigned char *msg, int mlen) +{ + switch (msg[0] & 0xf0) + { + case 0x90: + if (msg[2] != 0) + { + STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + } + msg[2] = 64; + + case 0x80: + STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + + case 0xA0: + STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + + case 0xB0: + STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f, + msg[1], msg[2])); + break; + + case 0xC0: + STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1])); + break; + + case 0xD0: + STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1])); + break; + + case 0xE0: + STORE(SEQ_BENDER(synthno, msg[0] & 0x0f, + (msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7))); + break; + + default: + /* printk( "MPU: Unknown midi channel message %02x\n", msg[0]); */ + ; + } +} + +static void +midi_outc(int midi_dev, int data) +{ + int timeout; + + for (timeout = 0; timeout < 3200; timeout++) + if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff))) + { + if (data & 0x80) /* + * Status byte + */ + prev_out_status[midi_dev] = + (unsigned char) (data & 0xff); /* + * Store for running status + */ + return; /* + * Mission complete + */ + } + /* + * Sorry! No space on buffers. + */ + printk("Midi send timed out\n"); +} + +static int +prefix_cmd(int midi_dev, unsigned char status) +{ + if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL) + return 1; + + return midi_devs[midi_dev]->prefix_cmd(midi_dev, status); +} + +static void +midi_synth_input(int orig_dev, unsigned char data) +{ + int dev; + struct midi_input_info *inc; + + static unsigned char len_tab[] = /* # of data bytes following a status + */ + { + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ + }; + + if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL) + return; + + if (data == 0xfe) /* Ignore active sensing */ + return; + + dev = midi2synth[orig_dev]; + inc = &midi_devs[orig_dev]->in_info; + + switch (inc->m_state) + { + case MST_INIT: + if (data & 0x80) /* MIDI status byte */ + { + if ((data & 0xf0) == 0xf0) /* Common message */ + { + switch (data) + { + case 0xf0: /* Sysex */ + inc->m_state = MST_SYSEX; + break; /* Sysex */ + + case 0xf1: /* MTC quarter frame */ + case 0xf3: /* Song select */ + inc->m_state = MST_DATA; + inc->m_ptr = 1; + inc->m_left = 1; + inc->m_buf[0] = data; + break; + + case 0xf2: /* Song position pointer */ + inc->m_state = MST_DATA; + inc->m_ptr = 1; + inc->m_left = 2; + inc->m_buf[0] = data; + break; + + default: + inc->m_buf[0] = data; + inc->m_ptr = 1; + do_midi_msg(dev, inc->m_buf, inc->m_ptr); + inc->m_ptr = 0; + inc->m_left = 0; + } + } else + { + inc->m_state = MST_DATA; + inc->m_ptr = 1; + inc->m_left = len_tab[(data >> 4) - 8]; + inc->m_buf[0] = inc->m_prev_status = data; + } + } else if (inc->m_prev_status & 0x80) { + /* Data byte (use running status) */ + inc->m_ptr = 2; + inc->m_buf[1] = data; + inc->m_buf[0] = inc->m_prev_status; + inc->m_left = len_tab[(inc->m_buf[0] >> 4) - 8] - 1; + if (inc->m_left > 0) + inc->m_state = MST_DATA; /* Not done yet */ + else { + inc->m_state = MST_INIT; + do_midi_msg(dev, inc->m_buf, inc->m_ptr); + inc->m_ptr = 0; + } + } + break; /* MST_INIT */ + + case MST_DATA: + inc->m_buf[inc->m_ptr++] = data; + if (--inc->m_left <= 0) + { + inc->m_state = MST_INIT; + do_midi_msg(dev, inc->m_buf, inc->m_ptr); + inc->m_ptr = 0; + } + break; /* MST_DATA */ + + case MST_SYSEX: + if (data == 0xf7) /* Sysex end */ + { + inc->m_state = MST_INIT; + inc->m_left = 0; + inc->m_ptr = 0; + } + break; /* MST_SYSEX */ + + default: + printk("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data); + inc->m_state = MST_INIT; + } +} + +static void +leave_sysex(int dev) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int timeout = 0; + + if (!sysex_state[dev]) + return; + + sysex_state[dev] = 0; + + while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) && + timeout < 1000) + timeout++; + + sysex_state[dev] = 0; +} + +static void +midi_synth_output(int dev) +{ + /* + * Currently NOP + */ +} + +int midi_synth_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + /* + * int orig_dev = synth_devs[dev]->midi_dev; + */ + + switch (cmd) { + + case SNDCTL_SYNTH_INFO: + if (__copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info))) + return -EFAULT; + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + default: + return -EINVAL; + } +} + +int +midi_synth_kill_note(int dev, int channel, int note, int velocity) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, chn; + + if (note < 0 || note > 127) + return 0; + if (channel < 0 || channel > 15) + return 0; + if (velocity < 0) + velocity = 0; + if (velocity > 127) + velocity = 127; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80)) + { /* + * Use running status + */ + if (!prefix_cmd(orig_dev, note)) + return 0; + + midi_outc(orig_dev, note); + + if (msg == 0x90) /* + * Running status = Note on + */ + midi_outc(orig_dev, 0); /* + * Note on with velocity 0 == note + * off + */ + else + midi_outc(orig_dev, velocity); + } else + { + if (velocity == 64) + { + if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /* + * Note on + */ + midi_outc(orig_dev, note); + midi_outc(orig_dev, 0); /* + * Zero G + */ + } else + { + if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0x80 | (channel & 0x0f)); /* + * Note off + */ + midi_outc(orig_dev, note); + midi_outc(orig_dev, velocity); + } + } + + return 0; +} + +int +midi_synth_set_instr(int dev, int channel, int instr_no) +{ + int orig_dev = synth_devs[dev]->midi_dev; + + if (instr_no < 0 || instr_no > 127) + instr_no = 0; + if (channel < 0 || channel > 15) + return 0; + + leave_sysex(dev); + + if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0xc0 | (channel & 0x0f)); /* + * Program change + */ + midi_outc(orig_dev, instr_no); + + return 0; +} + +int +midi_synth_start_note(int dev, int channel, int note, int velocity) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, chn; + + if (note < 0 || note > 127) + return 0; + if (channel < 0 || channel > 15) + return 0; + if (velocity < 0) + velocity = 0; + if (velocity > 127) + velocity = 127; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (chn == channel && msg == 0x90) + { /* + * Use running status + */ + if (!prefix_cmd(orig_dev, note)) + return 0; + midi_outc(orig_dev, note); + midi_outc(orig_dev, velocity); + } else + { + if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /* + * Note on + */ + midi_outc(orig_dev, note); + midi_outc(orig_dev, velocity); + } + return 0; +} + +void +midi_synth_reset(int dev) +{ + + leave_sysex(dev); +} + +int +midi_synth_open(int dev, int mode) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int err; + unsigned long flags; + struct midi_input_info *inc; + + if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL) + return -ENXIO; + + midi2synth[orig_dev] = dev; + sysex_state[dev] = 0; + prev_out_status[orig_dev] = 0; + + if ((err = midi_devs[orig_dev]->open(orig_dev, mode, + midi_synth_input, midi_synth_output)) < 0) + return err; + inc = &midi_devs[orig_dev]->in_info; + + save_flags(flags); + cli(); + inc->m_busy = 0; + inc->m_state = MST_INIT; + inc->m_ptr = 0; + inc->m_left = 0; + inc->m_prev_status = 0x00; + restore_flags(flags); + + return 1; +} + +void +midi_synth_close(int dev) +{ + int orig_dev = synth_devs[dev]->midi_dev; + + leave_sysex(dev); + + /* + * Shut up the synths by sending just single active sensing message. + */ + midi_devs[orig_dev]->outputc(orig_dev, 0xfe); + + midi_devs[orig_dev]->close(orig_dev); +} + +void +midi_synth_hw_control(int dev, unsigned char *event) +{ +} + +int +midi_synth_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + int orig_dev = synth_devs[dev]->midi_dev; + + struct sysex_info sysex; + int i; + unsigned long left, src_offs, eox_seen = 0; + int first_byte = 1; + int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex; + + leave_sysex(dev); + + if (!prefix_cmd(orig_dev, 0xf0)) + return 0; + + if (format != SYSEX_PATCH) + { +/* printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);*/ + return -EINVAL; + } + if (count < hdr_size) + { +/* printk("MIDI Error: Patch header too short\n");*/ + return -EINVAL; + } + count -= hdr_size; + + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + if(copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs)) + return -EFAULT; + + if (count < sysex.len) + { +/* printk(KERN_WARNING "MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);*/ + sysex.len = count; + } + left = sysex.len; + src_offs = 0; + + for (i = 0; i < left && !signal_pending(current); i++) + { + unsigned char data; + + get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i])); + + eox_seen = (i > 0 && data & 0x80); /* End of sysex */ + + if (eox_seen && data != 0xf7) + data = 0xf7; + + if (i == 0) + { + if (data != 0xf0) + { + printk(KERN_WARNING "midi_synth: Sysex start missing\n"); + return -EINVAL; + } + } + while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) && + !signal_pending(current)) + schedule(); + + if (!first_byte && data & 0x80) + return 0; + first_byte = 0; + } + + if (!eox_seen) + midi_outc(orig_dev, 0xf7); + return 0; +} + +void midi_synth_panning(int dev, int channel, int pressure) +{ +} + +void midi_synth_aftertouch(int dev, int channel, int pressure) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, chn; + + if (pressure < 0 || pressure > 127) + return; + if (channel < 0 || channel > 15) + return; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (msg != 0xd0 || chn != channel) /* + * Test for running status + */ + { + if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f))) + return; + midi_outc(orig_dev, 0xd0 | (channel & 0x0f)); /* + * Channel pressure + */ + } else if (!prefix_cmd(orig_dev, pressure)) + return; + + midi_outc(orig_dev, pressure); +} + +void +midi_synth_controller(int dev, int channel, int ctrl_num, int value) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int chn, msg; + + if (ctrl_num < 0 || ctrl_num > 127) + return; + if (channel < 0 || channel > 15) + return; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (msg != 0xb0 || chn != channel) + { + if (!prefix_cmd(orig_dev, 0xb0 | (channel & 0x0f))) + return; + midi_outc(orig_dev, 0xb0 | (channel & 0x0f)); + } else if (!prefix_cmd(orig_dev, ctrl_num)) + return; + + midi_outc(orig_dev, ctrl_num); + midi_outc(orig_dev, value & 0x7f); +} + +void +midi_synth_bender(int dev, int channel, int value) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, prev_chn; + + if (channel < 0 || channel > 15) + return; + + if (value < 0 || value > 16383) + return; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + prev_chn = prev_out_status[orig_dev] & 0x0f; + + if (msg != 0xd0 || prev_chn != channel) /* + * Test for running status + */ + { + if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f))) + return; + midi_outc(orig_dev, 0xe0 | (channel & 0x0f)); + } else if (!prefix_cmd(orig_dev, value & 0x7f)) + return; + + midi_outc(orig_dev, value & 0x7f); + midi_outc(orig_dev, (value >> 7) & 0x7f); +} + +void +midi_synth_setup_voice(int dev, int voice, int channel) +{ +} + +int +midi_synth_send_sysex(int dev, unsigned char *bytes, int len) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int i; + + for (i = 0; i < len; i++) + { + switch (bytes[i]) + { + case 0xf0: /* Start sysex */ + if (!prefix_cmd(orig_dev, 0xf0)) + return 0; + sysex_state[dev] = 1; + break; + + case 0xf7: /* End sysex */ + if (!sysex_state[dev]) /* Orphan sysex end */ + return 0; + sysex_state[dev] = 0; + break; + + default: + if (!sysex_state[dev]) + return 0; + + if (bytes[i] & 0x80) /* Error. Another message before sysex end */ + { + bytes[i] = 0xf7; /* Sysex end */ + sysex_state[dev] = 0; + } + } + + if (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i])) + { +/* + * Hardware level buffer is full. Abort the sysex message. + */ + + int timeout = 0; + + bytes[i] = 0xf7; + sysex_state[dev] = 0; + + while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) && + timeout < 1000) + timeout++; + } + if (!sysex_state[dev]) + return 0; + } + + return 0; +} diff -Nru a/sound/oss/midi_synth.h b/sound/oss/midi_synth.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/midi_synth.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,47 @@ +int midi_synth_ioctl (int dev, + unsigned int cmd, caddr_t arg); +int midi_synth_kill_note (int dev, int channel, int note, int velocity); +int midi_synth_set_instr (int dev, int channel, int instr_no); +int midi_synth_start_note (int dev, int channel, int note, int volume); +void midi_synth_reset (int dev); +int midi_synth_open (int dev, int mode); +void midi_synth_close (int dev); +void midi_synth_hw_control (int dev, unsigned char *event); +int midi_synth_load_patch (int dev, int format, const char * addr, + int offs, int count, int pmgr_flag); +void midi_synth_panning (int dev, int channel, int pressure); +void midi_synth_aftertouch (int dev, int channel, int pressure); +void midi_synth_controller (int dev, int channel, int ctrl_num, int value); +void midi_synth_bender (int dev, int chn, int value); +void midi_synth_setup_voice (int dev, int voice, int chn); +int midi_synth_send_sysex(int dev, unsigned char *bytes,int len); + +#ifndef _MIDI_SYNTH_C_ +static struct synth_info std_synth_info = +{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS}; + +static struct synth_operations std_midi_synth = +{ + owner: THIS_MODULE, + id: "MIDI", + info: &std_synth_info, + midi_dev: 0, + synth_type: SYNTH_TYPE_MIDI, + synth_subtype: 0, + open: midi_synth_open, + close: midi_synth_close, + ioctl: midi_synth_ioctl, + kill_note: midi_synth_kill_note, + start_note: midi_synth_start_note, + set_instr: midi_synth_set_instr, + reset: midi_synth_reset, + hw_control: midi_synth_hw_control, + load_patch: midi_synth_load_patch, + aftertouch: midi_synth_aftertouch, + controller: midi_synth_controller, + panning: midi_synth_panning, + bender: midi_synth_bender, + setup_voice: midi_synth_setup_voice, + send_sysex: midi_synth_send_sysex +}; +#endif diff -Nru a/sound/oss/midibuf.c b/sound/oss/midibuf.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/midibuf.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,438 @@ +/* + * sound/midibuf.c + * + * Device file manager for /dev/midi# + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + */ +#include +#include + +#define MIDIBUF_C + +#include "sound_config.h" + + +/* + * Don't make MAX_QUEUE_SIZE larger than 4000 + */ + +#define MAX_QUEUE_SIZE 4000 + +static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV]; +static wait_queue_head_t input_sleeper[MAX_MIDI_DEV]; + +struct midi_buf +{ + int len, head, tail; + unsigned char queue[MAX_QUEUE_SIZE]; +}; + +struct midi_parms +{ + long prech_timeout; /* + * Timeout before the first ch + */ +}; + +static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL}; +static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL}; +static struct midi_parms parms[MAX_MIDI_DEV]; + +static void midi_poll(unsigned long dummy); + + +static struct timer_list poll_timer = { + function: midi_poll +}; + +static volatile int open_devs = 0; + +#define DATA_AVAIL(q) (q->len) +#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len) + +#define QUEUE_BYTE(q, data) \ + if (SPACE_AVAIL(q)) \ + { \ + unsigned long flags; \ + save_flags( flags);cli(); \ + q->queue[q->tail] = (data); \ + q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \ + restore_flags(flags); \ + } + +#define REMOVE_BYTE(q, data) \ + if (DATA_AVAIL(q)) \ + { \ + unsigned long flags; \ + save_flags( flags);cli(); \ + data = q->queue[q->head]; \ + q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \ + restore_flags(flags); \ + } + +static void drain_midi_queue(int dev) +{ + + /* + * Give the Midi driver time to drain its output queues + */ + + if (midi_devs[dev]->buffer_status != NULL) + while (!signal_pending(current) && midi_devs[dev]->buffer_status(dev)) + interruptible_sleep_on_timeout(&midi_sleeper[dev], + HZ/10); +} + +static void midi_input_intr(int dev, unsigned char data) +{ + if (midi_in_buf[dev] == NULL) + return; + + if (data == 0xfe) /* + * Active sensing + */ + return; /* + * Ignore + */ + + if (SPACE_AVAIL(midi_in_buf[dev])) { + QUEUE_BYTE(midi_in_buf[dev], data); + wake_up(&input_sleeper[dev]); + } +} + +static void midi_output_intr(int dev) +{ + /* + * Currently NOP + */ +} + +static void midi_poll(unsigned long dummy) +{ + unsigned long flags; + int dev; + + save_flags(flags); + cli(); + if (open_devs) + { + for (dev = 0; dev < num_midis; dev++) + if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL) + { + int ok = 1; + + while (DATA_AVAIL(midi_out_buf[dev]) && ok) + { + int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head]; + + restore_flags(flags); /* Give some time to others */ + ok = midi_devs[dev]->outputc(dev, c); + cli(); + midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE; + midi_out_buf[dev]->len--; + } + + if (DATA_AVAIL(midi_out_buf[dev]) < 100) + wake_up(&midi_sleeper[dev]); + } + poll_timer.expires = (1) + jiffies; + add_timer(&poll_timer); + /* + * Come back later + */ + } + restore_flags(flags); +} + +int MIDIbuf_open(int dev, struct file *file) +{ + int mode, err; + + dev = dev >> 4; + mode = translate_mode(file); + + if (num_midis > MAX_MIDI_DEV) + { + printk(KERN_ERR "midi: Too many midi interfaces\n"); + num_midis = MAX_MIDI_DEV; + } + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + return -ENXIO; + /* + * Interrupts disabled. Be careful + */ + + if (midi_devs[dev]->owner) + __MOD_INC_USE_COUNT (midi_devs[dev]->owner); + + if ((err = midi_devs[dev]->open(dev, mode, + midi_input_intr, midi_output_intr)) < 0) + return err; + + parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT; + midi_in_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf)); + + if (midi_in_buf[dev] == NULL) + { + printk(KERN_WARNING "midi: Can't allocate buffer\n"); + midi_devs[dev]->close(dev); + return -EIO; + } + midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0; + + midi_out_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf)); + + if (midi_out_buf[dev] == NULL) + { + printk(KERN_WARNING "midi: Can't allocate buffer\n"); + midi_devs[dev]->close(dev); + vfree(midi_in_buf[dev]); + midi_in_buf[dev] = NULL; + return -EIO; + } + midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0; + open_devs++; + + init_waitqueue_head(&midi_sleeper[dev]); + init_waitqueue_head(&input_sleeper[dev]); + + if (open_devs < 2) /* This was first open */ + { + poll_timer.expires = 1 + jiffies; + add_timer(&poll_timer); /* Start polling */ + } + return err; +} + +void MIDIbuf_release(int dev, struct file *file) +{ + int mode; + unsigned long flags; + + dev = dev >> 4; + mode = translate_mode(file); + + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + return; + + save_flags(flags); + cli(); + + /* + * Wait until the queue is empty + */ + + if (mode != OPEN_READ) + { + midi_devs[dev]->outputc(dev, 0xfe); /* + * Active sensing to shut the + * devices + */ + + while (!signal_pending(current) && DATA_AVAIL(midi_out_buf[dev])) + interruptible_sleep_on(&midi_sleeper[dev]); + /* + * Sync + */ + + drain_midi_queue(dev); /* + * Ensure the output queues are empty + */ + } + restore_flags(flags); + + midi_devs[dev]->close(dev); + + open_devs--; + if (open_devs == 0) + del_timer_sync(&poll_timer); + vfree(midi_in_buf[dev]); + vfree(midi_out_buf[dev]); + midi_in_buf[dev] = NULL; + midi_out_buf[dev] = NULL; + + if (midi_devs[dev]->owner) + __MOD_DEC_USE_COUNT (midi_devs[dev]->owner); +} + +int MIDIbuf_write(int dev, struct file *file, const char *buf, int count) +{ + unsigned long flags; + int c, n, i; + unsigned char tmp_data; + + dev = dev >> 4; + + if (!count) + return 0; + + save_flags(flags); + cli(); + + c = 0; + + while (c < count) + { + n = SPACE_AVAIL(midi_out_buf[dev]); + + if (n == 0) { /* + * No space just now. + */ + + if (file->f_flags & O_NONBLOCK) { + restore_flags(flags); + return -EAGAIN; + } + + interruptible_sleep_on(&midi_sleeper[dev]); + if (signal_pending(current)) + { + restore_flags(flags); + return -EINTR; + } + n = SPACE_AVAIL(midi_out_buf[dev]); + } + if (n > (count - c)) + n = count - c; + + for (i = 0; i < n; i++) + { + /* BROKE BROKE BROKE - CANT DO THIS WITH CLI !! */ + copy_from_user((char *) &tmp_data, &(buf)[c], 1); + QUEUE_BYTE(midi_out_buf[dev], tmp_data); + c++; + } + } + restore_flags(flags); + return c; +} + + +int MIDIbuf_read(int dev, struct file *file, char *buf, int count) +{ + int n, c = 0; + unsigned long flags; + unsigned char tmp_data; + + dev = dev >> 4; + + save_flags(flags); + cli(); + + if (!DATA_AVAIL(midi_in_buf[dev])) { /* + * No data yet, wait + */ + if (file->f_flags & O_NONBLOCK) { + restore_flags(flags); + return -EAGAIN; + } + interruptible_sleep_on_timeout(&input_sleeper[dev], + parms[dev].prech_timeout); + + if (signal_pending(current)) + c = -EINTR; /* The user is getting restless */ + } + if (c == 0 && DATA_AVAIL(midi_in_buf[dev])) /* + * Got some bytes + */ + { + n = DATA_AVAIL(midi_in_buf[dev]); + if (n > count) + n = count; + c = 0; + + while (c < n) + { + char *fixit; + REMOVE_BYTE(midi_in_buf[dev], tmp_data); + fixit = (char *) &tmp_data; + /* BROKE BROKE BROKE */ + copy_to_user(&(buf)[c], fixit, 1); + c++; + } + } + restore_flags(flags); + return c; +} + +int MIDIbuf_ioctl(int dev, struct file *file, + unsigned int cmd, caddr_t arg) +{ + int val; + + dev = dev >> 4; + + if (((cmd >> 8) & 0xff) == 'C') + { + if (midi_devs[dev]->coproc) /* Coprocessor ioctl */ + return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0); +/* printk("/dev/midi%d: No coprocessor for this device\n", dev);*/ + return -ENXIO; + } + else + { + switch (cmd) + { + case SNDCTL_MIDI_PRETIME: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 0) + val = 0; + val = (HZ * val) / 10; + parms[dev].prech_timeout = val; + return put_user(val, (int *)arg); + + default: + if (!midi_devs[dev]->ioctl) + return -EINVAL; + return midi_devs[dev]->ioctl(dev, cmd, arg); + } + } +} + +/* No kernel lock - fine */ +unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + + dev = dev >> 4; + + /* input */ + poll_wait(file, &input_sleeper[dev], wait); + if (DATA_AVAIL(midi_in_buf[dev])) + mask |= POLLIN | POLLRDNORM; + + /* output */ + poll_wait(file, &midi_sleeper[dev], wait); + if (!SPACE_AVAIL(midi_out_buf[dev])) + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + + +void MIDIbuf_init(void) +{ + /* drag in midi_syms.o */ + { + extern char midi_syms_symbol; + midi_syms_symbol = 0; + } +} + +int MIDIbuf_avail(int dev) +{ + if (midi_in_buf[dev]) + return DATA_AVAIL (midi_in_buf[dev]); + return 0; +} diff -Nru a/sound/oss/mpu401.c b/sound/oss/mpu401.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/mpu401.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,1797 @@ +/* + * sound/mpu401.c + * + * The low level driver for Roland MPU-401 compatible Midi cards. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) + * Alan Cox modularisation, use normal request_irq, use dev_id + * Bartlomiej Zolnierkiewicz removed some __init to allow using many drivers + * Chris Rankin Update the module-usage counter for the coprocessor + */ + +#include +#include + +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include "sound_config.h" + +#include "coproc.h" +#include "mpu401.h" + +static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL; + +struct mpu_config +{ + int base; /* + * I/O base + */ + int irq; + int opened; /* + * Open mode + */ + int devno; + int synthno; + int uart_mode; + int initialized; + int mode; +#define MODE_MIDI 1 +#define MODE_SYNTH 2 + unsigned char version, revision; + unsigned int capabilities; +#define MPU_CAP_INTLG 0x10000000 +#define MPU_CAP_SYNC 0x00000010 +#define MPU_CAP_FSK 0x00000020 +#define MPU_CAP_CLS 0x00000040 +#define MPU_CAP_SMPTE 0x00000080 +#define MPU_CAP_2PORT 0x00000001 + int timer_flag; + +#define MBUF_MAX 10 +#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ + {printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} + int m_busy; + unsigned char m_buf[MBUF_MAX]; + int m_ptr; + int m_state; + int m_left; + unsigned char last_status; + void (*inputintr) (int dev, unsigned char data); + int shared_irq; + int *osp; + }; + +#define DATAPORT(base) (base) +#define COMDPORT(base) (base+1) +#define STATPORT(base) (base+1) + + +static void mpu401_close(int dev); + +static int mpu401_status(struct mpu_config *devc) +{ + return inb(STATPORT(devc->base)); +} + +#define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL)) +#define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY)) + +static void write_command(struct mpu_config *devc, unsigned char cmd) +{ + outb(cmd, COMDPORT(devc->base)); +} + +static int read_data(struct mpu_config *devc) +{ + return inb(DATAPORT(devc->base)); +} + +static void write_data(struct mpu_config *devc, unsigned char byte) +{ + outb(byte, DATAPORT(devc->base)); +} + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define MPU_RESET 0xFF +#define UART_MODE_ON 0x3F + +static struct mpu_config dev_conf[MAX_MIDI_DEV] = +{ + {0} +}; + +static int n_mpu_devs = 0; + +static int reset_mpu401(struct mpu_config *devc); +static void set_uart_mode(int dev, struct mpu_config *devc, int arg); + +static int mpu_timer_init(int midi_dev); +static void mpu_timer_interrupt(void); +static void timer_ext_event(struct mpu_config *devc, int event, int parm); + +static struct synth_info mpu_synth_info_proto = { + "MPU-401 MIDI interface", + 0, + SYNTH_TYPE_MIDI, + MIDI_TYPE_MPU401, + 0, 128, + 0, 128, + SYNTH_CAP_INPUT +}; + +static struct synth_info mpu_synth_info[MAX_MIDI_DEV]; + +/* + * States for the input scanner + */ + +#define ST_INIT 0 /* Ready for timing byte or msg */ +#define ST_TIMED 1 /* Leading timing byte rcvd */ +#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */ + +#define ST_SYSMSG 100 /* System message (sysx etc). */ +#define ST_SYSEX 101 /* System exclusive msg */ +#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */ +#define ST_SONGSEL 103 /* Song select */ +#define ST_SONGPOS 104 /* Song position pointer */ + +static unsigned char len_tab[] = /* # of data bytes following a status + */ +{ + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ +}; + +#define STORE(cmd) \ +{ \ + int len; \ + unsigned char obuf[8]; \ + cmd; \ + seq_input_event(obuf, len); \ +} + +#define _seqbuf obuf +#define _seqbufptr 0 +#define _SEQ_ADVBUF(x) len=x + +static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic) +{ + + switch (devc->m_state) + { + case ST_INIT: + switch (midic) + { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + printk(""); + break; + + case 0xfd: + if (devc->timer_flag) + mpu_timer_interrupt(); + break; + + case 0xfe: + return MPU_ACK; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + printk("", midic & 0x0f); + break; + + case 0xf9: + printk(""); + break; + + case 0xff: + devc->m_state = ST_SYSMSG; + break; + + default: + if (midic <= 0xef) + { + /* printk( "mpu time: %d ", midic); */ + devc->m_state = ST_TIMED; + } + else + printk(" ", midic); + } + break; + + case ST_TIMED: + { + int msg = ((int) (midic & 0xf0) >> 4); + + devc->m_state = ST_DATABYTE; + + if (msg < 8) /* Data byte */ + { + /* printk( "midi msg (running status) "); */ + msg = ((int) (devc->last_status & 0xf0) >> 4); + msg -= 8; + devc->m_left = len_tab[msg] - 1; + + devc->m_ptr = 2; + devc->m_buf[0] = devc->last_status; + devc->m_buf[1] = midic; + + if (devc->m_left <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + } + else if (msg == 0xf) /* MPU MARK */ + { + devc->m_state = ST_INIT; + + switch (midic) + { + case 0xf8: + /* printk( "NOP "); */ + break; + + case 0xf9: + /* printk( "meas end "); */ + break; + + case 0xfc: + /* printk( "data end "); */ + break; + + default: + printk("Unknown MPU mark %02x\n", midic); + } + } + else + { + devc->last_status = midic; + /* printk( "midi msg "); */ + msg -= 8; + devc->m_left = len_tab[msg]; + + devc->m_ptr = 1; + devc->m_buf[0] = midic; + + if (devc->m_left <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + } + } + break; + + case ST_SYSMSG: + switch (midic) + { + case 0xf0: + printk(""); + devc->m_state = ST_SYSEX; + break; + + case 0xf1: + devc->m_state = ST_MTC; + break; + + case 0xf2: + devc->m_state = ST_SONGPOS; + devc->m_ptr = 0; + break; + + case 0xf3: + devc->m_state = ST_SONGSEL; + break; + + case 0xf6: + /* printk( "tune_request\n"); */ + devc->m_state = ST_INIT; + + /* + * Real time messages + */ + case 0xf8: + /* midi clock */ + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_CLOCK, 0); + break; + + case 0xfA: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_START, 0); + break; + + case 0xFB: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_CONTINUE, 0); + break; + + case 0xFC: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_STOP, 0); + break; + + case 0xFE: + /* active sensing */ + devc->m_state = ST_INIT; + break; + + case 0xff: + /* printk( "midi hard reset"); */ + devc->m_state = ST_INIT; + break; + + default: + printk("unknown MIDI sysmsg %0x\n", midic); + devc->m_state = ST_INIT; + } + break; + + case ST_MTC: + devc->m_state = ST_INIT; + printk("MTC frame %x02\n", midic); + break; + + case ST_SYSEX: + if (midic == 0xf7) + { + printk(""); + devc->m_state = ST_INIT; + } + else + printk("%02x ", midic); + break; + + case ST_SONGPOS: + BUFTEST(devc); + devc->m_buf[devc->m_ptr++] = midic; + if (devc->m_ptr == 2) + { + devc->m_state = ST_INIT; + devc->m_ptr = 0; + timer_ext_event(devc, TMR_SPP, + ((devc->m_buf[1] & 0x7f) << 7) | + (devc->m_buf[0] & 0x7f)); + } + break; + + case ST_DATABYTE: + BUFTEST(devc); + devc->m_buf[devc->m_ptr++] = midic; + if ((--devc->m_left) <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + break; + + default: + printk("Bad state %d ", devc->m_state); + devc->m_state = ST_INIT; + } + return 1; +} + +static void mpu401_input_loop(struct mpu_config *devc) +{ + unsigned long flags; + int busy; + int n; + + save_flags(flags); + cli(); + busy = devc->m_busy; + devc->m_busy = 1; + restore_flags(flags); + + if (busy) /* Already inside the scanner */ + return; + + n = 50; + + while (input_avail(devc) && n-- > 0) + { + unsigned char c = read_data(devc); + + if (devc->mode == MODE_SYNTH) + { + mpu_input_scanner(devc, c); + } + else if (devc->opened & OPEN_READ && devc->inputintr != NULL) + devc->inputintr(devc->devno, c); + } + devc->m_busy = 0; +} + +int intchk_mpu401(void *dev_id) +{ + struct mpu_config *devc; + int dev = (int) dev_id; + + devc = &dev_conf[dev]; + return input_avail(devc); +} + +void mpuintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + struct mpu_config *devc; + int dev = (int) dev_id; + + sti(); + devc = &dev_conf[dev]; + + if (input_avail(devc)) + { + if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH)) + mpu401_input_loop(devc); + else + { + /* Dummy read (just to acknowledge the interrupt) */ + read_data(devc); + } + } +} + +static int mpu401_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + int err; + struct mpu_config *devc; + struct coproc_operations *coprocessor; + + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + return -ENXIO; + + devc = &dev_conf[dev]; + + if (devc->opened) + return -EBUSY; + /* + * Verify that the device is really running. + * Some devices (such as Ensoniq SoundScape don't + * work before the on board processor (OBP) is initialized + * by downloading its microcode. + */ + + if (!devc->initialized) + { + if (mpu401_status(devc) == 0xff) /* Bus float */ + { + printk(KERN_ERR "mpu401: Device not initialized properly\n"); + return -EIO; + } + reset_mpu401(devc); + } + + if ( (coprocessor = midi_devs[dev]->coproc) != NULL ) + { + if (coprocessor->owner) + __MOD_INC_USE_COUNT(coprocessor->owner); + + if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0) + { + printk(KERN_WARNING "MPU-401: Can't access coprocessor device\n"); + mpu401_close(dev); + return err; + } + } + + set_uart_mode(dev, devc, 1); + devc->mode = MODE_MIDI; + devc->synthno = 0; + + mpu401_input_loop(devc); + + devc->inputintr = input; + devc->opened = mode; + + return 0; +} + +static void mpu401_close(int dev) +{ + struct mpu_config *devc; + struct coproc_operations *coprocessor; + + devc = &dev_conf[dev]; + if (devc->uart_mode) + reset_mpu401(devc); /* + * This disables the UART mode + */ + devc->mode = 0; + devc->inputintr = NULL; + + coprocessor = midi_devs[dev]->coproc; + if (coprocessor) { + coprocessor->close(coprocessor->devc, COPR_MIDI); + + if (coprocessor->owner) + __MOD_DEC_USE_COUNT(coprocessor->owner); + } + devc->opened = 0; +} + +static int mpu401_out(int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + + struct mpu_config *devc; + + devc = &dev_conf[dev]; + + /* + * Sometimes it takes about 30000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); + + save_flags(flags); + cli(); + if (!output_ready(devc)) + { + printk(KERN_WARNING "mpu401: Send data timeout\n"); + restore_flags(flags); + return 0; + } + write_data(devc, midi_byte); + restore_flags(flags); + return 1; +} + +static int mpu401_command(int dev, mpu_command_rec * cmd) +{ + int i, timeout, ok; + int ret = 0; + unsigned long flags; + struct mpu_config *devc; + + devc = &dev_conf[dev]; + + if (devc->uart_mode) /* + * Not possible in UART mode + */ + { + printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n"); + return -EINVAL; + } + /* + * Test for input since pending input seems to block the output. + */ + if (input_avail(devc)) + mpu401_input_loop(devc); + + /* + * Sometimes it takes about 50000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + timeout = 50000; +retry: + if (timeout-- <= 0) + { + printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd); + return -EIO; + } + save_flags(flags); + cli(); + + if (!output_ready(devc)) + { + restore_flags(flags); + goto retry; + } + write_command(devc, cmd->cmd); + + ok = 0; + for (timeout = 50000; timeout > 0 && !ok; timeout--) + { + if (input_avail(devc)) + { + if (devc->opened && devc->mode == MODE_SYNTH) + { + if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK) + ok = 1; + } + else + { + /* Device is not currently open. Use simpler method */ + if (read_data(devc) == MPU_ACK) + ok = 1; + } + } + } + if (!ok) + { + restore_flags(flags); + return -EIO; + } + if (cmd->nr_args) + { + for (i = 0; i < cmd->nr_args; i++) + { + for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--); + + if (!mpu401_out(dev, cmd->data[i])) + { + restore_flags(flags); + printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd); + return -EIO; + } + } + } + ret = 0; + cmd->data[0] = 0; + + if (cmd->nr_returns) + { + for (i = 0; i < cmd->nr_returns; i++) + { + ok = 0; + for (timeout = 5000; timeout > 0 && !ok; timeout--) + if (input_avail(devc)) + { + cmd->data[i] = read_data(devc); + ok = 1; + } + if (!ok) + { + restore_flags(flags); + return -EIO; + } + } + } + restore_flags(flags); + return ret; +} + +static int mpu_cmd(int dev, int cmd, int data) +{ + int ret; + + static mpu_command_rec rec; + + rec.cmd = cmd & 0xff; + rec.nr_args = ((cmd & 0xf0) == 0xE0); + rec.nr_returns = ((cmd & 0xf0) == 0xA0); + rec.data[0] = data & 0xff; + + if ((ret = mpu401_command(dev, &rec)) < 0) + return ret; + return (unsigned char) rec.data[0]; +} + +static int mpu401_prefix_cmd(int dev, unsigned char status) +{ + struct mpu_config *devc = &dev_conf[dev]; + + if (devc->uart_mode) + return 1; + + if (status < 0xf0) + { + if (mpu_cmd(dev, 0xD0, 0) < 0) + return 0; + return 1; + } + switch (status) + { + case 0xF0: + if (mpu_cmd(dev, 0xDF, 0) < 0) + return 0; + return 1; + + default: + return 0; + } +} + +static int mpu401_start_read(int dev) +{ + return 0; +} + +static int mpu401_end_read(int dev) +{ + return 0; +} + +static int mpu401_ioctl(int dev, unsigned cmd, caddr_t arg) +{ + struct mpu_config *devc; + mpu_command_rec rec; + int val, ret; + + devc = &dev_conf[dev]; + switch (cmd) + { + case SNDCTL_MIDI_MPUMODE: + if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */ + printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n"); + return -EINVAL; + } + if (get_user(val, (int *)arg)) + return -EFAULT; + set_uart_mode(dev, devc, !val); + return 0; + + case SNDCTL_MIDI_MPUCMD: + if (copy_from_user(&rec, arg, sizeof(rec))) + return -EFAULT; + if ((ret = mpu401_command(dev, &rec)) < 0) + return ret; + if (copy_to_user(arg, &rec, sizeof(rec))) + return -EFAULT; + return 0; + + default: + return -EINVAL; + } +} + +static void mpu401_kick(int dev) +{ +} + +static int mpu401_buffer_status(int dev) +{ + return 0; /* + * No data in buffers + */ +} + +static int mpu_synth_ioctl(int dev, + unsigned int cmd, caddr_t arg) +{ + int midi_dev; + struct mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL) + return -ENXIO; + + devc = &dev_conf[midi_dev]; + + switch (cmd) + { + + case SNDCTL_SYNTH_INFO: + memcpy((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof(struct synth_info)); + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + default: + return -EINVAL; + } +} + +static int mpu_synth_open(int dev, int mode) +{ + int midi_dev, err; + struct mpu_config *devc; + struct coproc_operations *coprocessor; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL) + return -ENXIO; + + devc = &dev_conf[midi_dev]; + + /* + * Verify that the device is really running. + * Some devices (such as Ensoniq SoundScape don't + * work before the on board processor (OBP) is initialized + * by downloading its microcode. + */ + + if (!devc->initialized) + { + if (mpu401_status(devc) == 0xff) /* Bus float */ + { + printk(KERN_ERR "mpu401: Device not initialized properly\n"); + return -EIO; + } + reset_mpu401(devc); + } + if (devc->opened) + return -EBUSY; + devc->mode = MODE_SYNTH; + devc->synthno = dev; + + devc->inputintr = NULL; + + coprocessor = midi_devs[midi_dev]->coproc; + if (coprocessor) { + if (coprocessor->owner) + __MOD_INC_USE_COUNT(coprocessor->owner); + + if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0) + { + printk(KERN_WARNING "mpu401: Can't access coprocessor device\n"); + return err; + } + } + devc->opened = mode; + reset_mpu401(devc); + + if (mode & OPEN_READ) + { + mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */ + mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ + mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */ + } + return 0; +} + +static void mpu_synth_close(int dev) +{ + int midi_dev; + struct mpu_config *devc; + struct coproc_operations *coprocessor; + + midi_dev = synth_devs[dev]->midi_dev; + + devc = &dev_conf[midi_dev]; + mpu_cmd(midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */ + mpu_cmd(midi_dev, 0x8a, 0); /* Disable data in stopped mode */ + + devc->inputintr = NULL; + + coprocessor = midi_devs[midi_dev]->coproc; + if (coprocessor) { + coprocessor->close(coprocessor->devc, COPR_MIDI); + + if (coprocessor->owner) + __MOD_DEC_USE_COUNT(coprocessor->owner); + } + devc->opened = 0; + devc->mode = 0; +} + +#define MIDI_SYNTH_NAME "MPU-401 UART Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct synth_operations mpu401_synth_proto = +{ + owner: THIS_MODULE, + id: "MPU401", + info: NULL, + midi_dev: 0, + synth_type: SYNTH_TYPE_MIDI, + synth_subtype: 0, + open: mpu_synth_open, + close: mpu_synth_close, + ioctl: mpu_synth_ioctl, + kill_note: midi_synth_kill_note, + start_note: midi_synth_start_note, + set_instr: midi_synth_set_instr, + reset: midi_synth_reset, + hw_control: midi_synth_hw_control, + load_patch: midi_synth_load_patch, + aftertouch: midi_synth_aftertouch, + controller: midi_synth_controller, + panning: midi_synth_panning, + bender: midi_synth_bender, + setup_voice: midi_synth_setup_voice, + send_sysex: midi_synth_send_sysex +}; + +static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV]; + +static struct midi_operations mpu401_midi_proto = +{ + owner: THIS_MODULE, + info: {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, + in_info: {0}, + open: mpu401_open, + close: mpu401_close, + ioctl: mpu401_ioctl, + outputc: mpu401_out, + start_read: mpu401_start_read, + end_read: mpu401_end_read, + kick: mpu401_kick, + buffer_status: mpu401_buffer_status, + prefix_cmd: mpu401_prefix_cmd +}; + +static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV]; + +static void mpu401_chk_version(int n, struct mpu_config *devc) +{ + int tmp; + unsigned long flags; + + devc->version = devc->revision = 0; + + save_flags(flags); + cli(); + if ((tmp = mpu_cmd(n, 0xAC, 0)) < 0) + { + restore_flags(flags); + return; + } + if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */ + { + restore_flags(flags); + return; + } + devc->version = tmp; + + if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0) + { + devc->version = 0; + restore_flags(flags); + return; + } + devc->revision = tmp; + restore_flags(flags); +} + +void attach_mpu401(struct address_info *hw_config, struct module *owner) +{ + unsigned long flags; + char revision_char; + + int m; + struct mpu_config *devc; + + hw_config->slots[1] = -1; + m = sound_alloc_mididev(); + if (m == -1) + { + printk(KERN_WARNING "MPU-401: Too many midi devices detected\n"); + return; + } + devc = &dev_conf[m]; + devc->base = hw_config->io_base; + devc->osp = hw_config->osp; + devc->irq = hw_config->irq; + devc->opened = 0; + devc->uart_mode = 0; + devc->initialized = 0; + devc->version = 0; + devc->revision = 0; + devc->capabilities = 0; + devc->timer_flag = 0; + devc->m_busy = 0; + devc->m_state = ST_INIT; + devc->shared_irq = hw_config->always_detect; + devc->irq = hw_config->irq; + + if (devc->irq < 0) + { + devc->irq *= -1; + devc->shared_irq = 1; + } + + if (!hw_config->always_detect) + { + /* Verify the hardware again */ + if (!reset_mpu401(devc)) + { + printk(KERN_WARNING "mpu401: Device didn't respond\n"); + sound_unload_mididev(m); + return; + } + if (!devc->shared_irq) + { + if (request_irq(devc->irq, mpuintr, 0, "mpu401", (void *)m) < 0) + { + printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq); + sound_unload_mididev(m); + return; + } + } + save_flags(flags); + cli(); + mpu401_chk_version(m, devc); + if (devc->version == 0) + mpu401_chk_version(m, devc); + restore_flags(flags); + } + request_region(hw_config->io_base, 2, "mpu401"); + + if (devc->version != 0) + if (mpu_cmd(m, 0xC5, 0) >= 0) /* Set timebase OK */ + if (mpu_cmd(m, 0xE0, 120) >= 0) /* Set tempo OK */ + devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */ + + + mpu401_synth_operations[m] = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); + + if (mpu401_synth_operations[m] == NULL) + { + sound_unload_mididev(m); + printk(KERN_ERR "mpu401: Can't allocate memory\n"); + return; + } + if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */ + { + memcpy((char *) mpu401_synth_operations[m], + (char *) &std_midi_synth, + sizeof(struct synth_operations)); + } + else + { + memcpy((char *) mpu401_synth_operations[m], + (char *) &mpu401_synth_proto, + sizeof(struct synth_operations)); + } + if (owner) + mpu401_synth_operations[m]->owner = owner; + + memcpy((char *) &mpu401_midi_operations[m], + (char *) &mpu401_midi_proto, + sizeof(struct midi_operations)); + + mpu401_midi_operations[m].converter = mpu401_synth_operations[m]; + + memcpy((char *) &mpu_synth_info[m], + (char *) &mpu_synth_info_proto, + sizeof(struct synth_info)); + + n_mpu_devs++; + + if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */ + { + int ports = (devc->revision & 0x08) ? 32 : 16; + + devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | + MPU_CAP_CLS | MPU_CAP_2PORT; + + revision_char = (devc->revision == 0x7f) ? 'M' : ' '; + sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d", + ports, + revision_char, + n_mpu_devs); + } + else + { + revision_char = devc->revision ? devc->revision + '@' : ' '; + if ((int) devc->revision > ('Z' - '@')) + revision_char = '+'; + + devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; + + if (hw_config->name) + sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name); + else + sprintf(mpu_synth_info[m].name, + "MPU-401 %d.%d%c Midi interface #%d", + (int) (devc->version & 0xf0) >> 4, + devc->version & 0x0f, + revision_char, + n_mpu_devs); + } + + strcpy(mpu401_midi_operations[m].info.name, + mpu_synth_info[m].name); + + conf_printf(mpu_synth_info[m].name, hw_config); + + mpu401_synth_operations[m]->midi_dev = devc->devno = m; + mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno]; + + if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */ + hw_config->slots[2] = mpu_timer_init(m); + + midi_devs[m] = &mpu401_midi_operations[devc->devno]; + + if (owner) + midi_devs[m]->owner = owner; + + hw_config->slots[1] = m; + sequencer_init(); +} + +static int reset_mpu401(struct mpu_config *devc) +{ + unsigned long flags; + int ok, timeout, n; + int timeout_limit; + + /* + * Send the RESET command. Try again if no success at the first time. + * (If the device is in the UART mode, it will not ack the reset cmd). + */ + + ok = 0; + + timeout_limit = devc->initialized ? 30000 : 100000; + devc->initialized = 1; + + for (n = 0; n < 2 && !ok; n++) + { + for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) + ok = output_ready(devc); + + write_command(devc, MPU_RESET); /* + * Send MPU-401 RESET Command + */ + + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ + + for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) + { + save_flags(flags); + cli(); + if (input_avail(devc)) + if (read_data(devc) == MPU_ACK) + ok = 1; + restore_flags(flags); + } + + } + + devc->m_state = ST_INIT; + devc->m_ptr = 0; + devc->m_left = 0; + devc->last_status = 0; + devc->uart_mode = 0; + + return ok; +} + +static void set_uart_mode(int dev, struct mpu_config *devc, int arg) +{ + if (!arg && (devc->capabilities & MPU_CAP_INTLG)) + return; + if ((devc->uart_mode == 0) == (arg == 0)) + return; /* Already set */ + reset_mpu401(devc); /* This exits the uart mode */ + + if (arg) + { + if (mpu_cmd(dev, UART_MODE_ON, 0) < 0) + { + printk(KERN_ERR "mpu401: Can't enter UART mode\n"); + devc->uart_mode = 0; + return; + } + } + devc->uart_mode = arg; + +} + +int probe_mpu401(struct address_info *hw_config) +{ + int ok = 0; + struct mpu_config tmp_devc; + + if (check_region(hw_config->io_base, 2)) + { + printk(KERN_ERR "mpu401: I/O port %x already in use\n\n", hw_config->io_base); + return 0; + } + tmp_devc.base = hw_config->io_base; + tmp_devc.irq = hw_config->irq; + tmp_devc.initialized = 0; + tmp_devc.opened = 0; + tmp_devc.osp = hw_config->osp; + + if (hw_config->always_detect) + return 1; + + if (inb(hw_config->io_base + 1) == 0xff) + { + DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base)); + return 0; /* Just bus float? */ + } + ok = reset_mpu401(&tmp_devc); + + if (!ok) + { + DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base)); + } + return ok; +} + +void __exit unload_mpu401(struct address_info *hw_config) +{ + void *p; + int n=hw_config->slots[1]; + + release_region(hw_config->io_base, 2); + if (hw_config->always_detect == 0 && hw_config->irq > 0) + free_irq(hw_config->irq, (void *)n); + p=mpu401_synth_operations[n]; + sound_unload_mididev(n); + sound_unload_timerdev(hw_config->slots[2]); + if(p) + kfree(p); +} + +/***************************************************** + * Timer stuff + ****************************************************/ + +static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0; +static volatile int curr_tempo, curr_timebase, hw_timebase; +static int max_timebase = 8; /* 8*24=192 ppqn */ +static volatile unsigned long next_event_time; +static volatile unsigned long curr_ticks, curr_clocks; +static unsigned long prev_event_time; +static int metronome_mode; + +static unsigned long clocks2ticks(unsigned long clocks) +{ + /* + * The MPU-401 supports just a limited set of possible timebase values. + * Since the applications require more choices, the driver has to + * program the HW to do its best and to convert between the HW and + * actual timebases. + */ + return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase; +} + +static void set_timebase(int midi_dev, int val) +{ + int hw_val; + + if (val < 48) + val = 48; + if (val > 1000) + val = 1000; + + hw_val = val; + hw_val = (hw_val + 12) / 24; + if (hw_val > max_timebase) + hw_val = max_timebase; + + if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0) + { + printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24); + return; + } + hw_timebase = hw_val * 24; + curr_timebase = val; + +} + +static void tmr_reset(void) +{ + unsigned long flags; + + save_flags(flags); + cli(); + next_event_time = (unsigned long) -1; + prev_event_time = 0; + curr_ticks = curr_clocks = 0; + restore_flags(flags); +} + +static void set_timer_mode(int midi_dev) +{ + if (timer_mode & TMR_MODE_CLS) + mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ + else if (timer_mode & TMR_MODE_SMPTE) + mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ + + if (timer_mode & TMR_INTERNAL) + { + mpu_cmd(midi_dev, 0x80, 0); /* Use MIDI sync */ + } + else + { + if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) + { + mpu_cmd(midi_dev, 0x82, 0); /* Use MIDI sync */ + mpu_cmd(midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ + } + else if (timer_mode & TMR_MODE_FSK) + mpu_cmd(midi_dev, 0x81, 0); /* Use FSK sync */ + } +} + +static void stop_metronome(int midi_dev) +{ + mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ +} + +static void setup_metronome(int midi_dev) +{ + int numerator, denominator; + int clks_per_click, num_32nds_per_beat; + int beats_per_measure; + + numerator = ((unsigned) metronome_mode >> 24) & 0xff; + denominator = ((unsigned) metronome_mode >> 16) & 0xff; + clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff; + num_32nds_per_beat = (unsigned) metronome_mode & 0xff; + beats_per_measure = (numerator * 4) >> denominator; + + if (!metronome_mode) + mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ + else + { + mpu_cmd(midi_dev, 0xE4, clks_per_click); + mpu_cmd(midi_dev, 0xE6, beats_per_measure); + mpu_cmd(midi_dev, 0x83, 0); /* Enable metronome without accents */ + } +} + +static int mpu_start_timer(int midi_dev) +{ + tmr_reset(); + set_timer_mode(midi_dev); + + if (tmr_running) + return TIMER_NOT_ARMED; /* Already running */ + + if (timer_mode & TMR_INTERNAL) + { + mpu_cmd(midi_dev, 0x02, 0); /* Send MIDI start */ + tmr_running = 1; + return TIMER_NOT_ARMED; + } + else + { + mpu_cmd(midi_dev, 0x35, 0); /* Enable mode messages to PC */ + mpu_cmd(midi_dev, 0x38, 0); /* Enable sys common messages to PC */ + mpu_cmd(midi_dev, 0x39, 0); /* Enable real time messages to PC */ + mpu_cmd(midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ + } + return TIMER_ARMED; +} + +static int mpu_timer_open(int dev, int mode) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + if (timer_open) + return -EBUSY; + + tmr_reset(); + curr_tempo = 50; + mpu_cmd(midi_dev, 0xE0, 50); + curr_timebase = hw_timebase = 120; + set_timebase(midi_dev, 120); + timer_open = 1; + metronome_mode = 0; + set_timer_mode(midi_dev); + + mpu_cmd(midi_dev, 0xe7, 0x04); /* Send all clocks to host */ + mpu_cmd(midi_dev, 0x95, 0); /* Enable clock to host */ + + return 0; +} + +static void mpu_timer_close(int dev) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + timer_open = tmr_running = 0; + mpu_cmd(midi_dev, 0x15, 0); /* Stop all */ + mpu_cmd(midi_dev, 0x94, 0); /* Disable clock to host */ + mpu_cmd(midi_dev, 0x8c, 0); /* Disable measure end messages to host */ + stop_metronome(midi_dev); +} + +static int mpu_timer_event(int dev, unsigned char *event) +{ + unsigned char command = event[1]; + unsigned long parm = *(unsigned int *) &event[4]; + int midi_dev = sound_timer_devs[dev]->devlink; + + switch (command) + { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + time = parm; + next_event_time = prev_event_time = time; + + return TIMER_ARMED; + } + break; + + case TMR_START: + if (tmr_running) + break; + return mpu_start_timer(midi_dev); + + case TMR_STOP: + mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ + stop_metronome(midi_dev); + tmr_running = 0; + break; + + case TMR_CONTINUE: + if (tmr_running) + break; + mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ + setup_metronome(midi_dev); + tmr_running = 1; + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 250) + parm = 250; + if (mpu_cmd(midi_dev, 0xE0, parm) < 0) + printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm); + curr_tempo = parm; + } + break; + + case TMR_ECHO: + seq_copy_to_input(event, 8); + break; + + case TMR_TIMESIG: + if (metronome_mode) /* Metronome enabled */ + { + metronome_mode = parm; + setup_metronome(midi_dev); + } + break; + + default:; + } + return TIMER_NOT_ARMED; +} + +static unsigned long mpu_timer_get_time(int dev) +{ + if (!timer_open) + return 0; + + return curr_ticks; +} + +static int mpu_timer_ioctl(int dev, unsigned int command, caddr_t arg) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + switch (command) + { + case SNDCTL_TMR_SOURCE: + { + int parm; + + parm = *(int *) arg; + parm &= timer_caps; + + if (parm != 0) + { + timer_mode = parm; + + if (timer_mode & TMR_MODE_CLS) + mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ + else if (timer_mode & TMR_MODE_SMPTE) + mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ + } + return (*(int *) arg = timer_mode); + } + break; + + case SNDCTL_TMR_START: + mpu_start_timer(midi_dev); + return 0; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ + stop_metronome(midi_dev); + return 0; + + case SNDCTL_TMR_CONTINUE: + if (tmr_running) + return 0; + tmr_running = 1; + mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ + return 0; + + case SNDCTL_TMR_TIMEBASE: + { + int val; + + val = *(int *) arg; + if (val) + set_timebase(midi_dev, val); + return (*(int *) arg = curr_timebase); + } + break; + + case SNDCTL_TMR_TEMPO: + { + int val; + int ret; + + val = *(int *) arg; + + if (val) + { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0) + { + printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val); + return ret; + } + curr_tempo = val; + } + return (*(int *) arg = curr_tempo); + } + break; + + case SNDCTL_SEQ_CTRLRATE: + { + int val; + + val = *(int *) arg; + if (val != 0) /* Can't change */ + return -EINVAL; + return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60); + } + break; + + case SNDCTL_SEQ_GETTIME: + return (*(int *) arg = curr_ticks); + + case SNDCTL_TMR_METRONOME: + metronome_mode = *(int *) arg; + setup_metronome(midi_dev); + return 0; + + default:; + } + return -EINVAL; +} + +static void mpu_timer_arm(int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + next_event_time = prev_event_time = time; + return; +} + +static struct sound_timer_operations mpu_timer = +{ + owner: THIS_MODULE, + info: {"MPU-401 Timer", 0}, + priority: 10, /* Priority */ + devlink: 0, /* Local device link */ + open: mpu_timer_open, + close: mpu_timer_close, + event: mpu_timer_event, + get_time: mpu_timer_get_time, + ioctl: mpu_timer_ioctl, + arm_timer: mpu_timer_arm +}; + +static void mpu_timer_interrupt(void) +{ + if (!timer_open) + return; + + if (!tmr_running) + return; + + curr_clocks++; + curr_ticks = clocks2ticks(curr_clocks); + + if (curr_ticks >= next_event_time) + { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } +} + +static void timer_ext_event(struct mpu_config *devc, int event, int parm) +{ + int midi_dev = devc->devno; + + if (!devc->timer_flag) + return; + + switch (event) + { + case TMR_CLOCK: + printk(""); + break; + + case TMR_START: + printk("Ext MIDI start\n"); + if (!tmr_running) + { + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 1; + setup_metronome(midi_dev); + next_event_time = 0; + STORE(SEQ_START_TIMER()); + } + } + break; + + case TMR_STOP: + printk("Ext MIDI stop\n"); + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 0; + stop_metronome(midi_dev); + STORE(SEQ_STOP_TIMER()); + } + break; + + case TMR_CONTINUE: + printk("Ext MIDI continue\n"); + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 1; + setup_metronome(midi_dev); + STORE(SEQ_CONTINUE_TIMER()); + } + break; + + case TMR_SPP: + printk("Songpos: %d\n", parm); + if (timer_mode & TMR_EXTERNAL) + { + STORE(SEQ_SONGPOS(parm)); + } + break; + } +} + +static int mpu_timer_init(int midi_dev) +{ + struct mpu_config *devc; + int n; + + devc = &dev_conf[midi_dev]; + + if (timer_initialized) + return -1; /* There is already a similar timer */ + + timer_initialized = 1; + + mpu_timer.devlink = midi_dev; + dev_conf[midi_dev].timer_flag = 1; + + n = sound_alloc_timerdev(); + if (n == -1) + n = 0; + sound_timer_devs[n] = &mpu_timer; + + if (devc->version < 0x20) /* Original MPU-401 */ + timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI; + else + { + /* + * The version number 2.0 is used (at least) by the + * MusicQuest cards and the Roland Super-MPU. + * + * MusicQuest has given a special meaning to the bits of the + * revision number. The Super-MPU returns 0. + */ + + if (devc->revision) + timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI; + + if (devc->revision & 0x02) + timer_caps |= TMR_MODE_CLS; + + + if (devc->revision & 0x40) + max_timebase = 10; /* Has the 216 and 240 ppqn modes */ + } + + timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps; + return n; + +} + +EXPORT_SYMBOL(probe_mpu401); +EXPORT_SYMBOL(attach_mpu401); +EXPORT_SYMBOL(unload_mpu401); +EXPORT_SYMBOL(intchk_mpu401); +EXPORT_SYMBOL(mpuintr); + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; + +MODULE_PARM(irq, "i"); +MODULE_PARM(io, "i"); + +int __init init_mpu401(void) +{ + /* Can be loaded either for module use or to provide functions + to others */ + if (io != -1 && irq != -1) { + cfg.irq = irq; + cfg.io_base = io; + if (probe_mpu401(&cfg) == 0) + return -ENODEV; + attach_mpu401(&cfg, THIS_MODULE); + } + + return 0; +} + +void __exit cleanup_mpu401(void) +{ + if (io != -1 && irq != -1) { + /* Check for use by, for example, sscape driver */ + unload_mpu401(&cfg); + } +} + +module_init(init_mpu401); +module_exit(cleanup_mpu401); + +#ifndef MODULE +static int __init setup_mpu401(char *str) +{ + /* io, irq */ + int ints[3]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + + return 1; +} + +__setup("mpu401=", setup_mpu401); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/mpu401.h b/sound/oss/mpu401.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/mpu401.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,20 @@ +/* + * uart401.h + * + * Copyright: Christoph Hellwig + * + */ + +/* From uart401.c */ +int probe_uart401 (struct address_info *hw_config, struct module *owner); +void unload_uart401 (struct address_info *hw_config); + +void uart401intr (int irq, void *dev_id, struct pt_regs * dummy); + +/* From mpu401.c */ +int probe_mpu401(struct address_info *hw_config); +void attach_mpu401(struct address_info * hw_config, struct module *owner); +void unload_mpu401(struct address_info *hw_info); + +int intchk_mpu401(void *dev_id); +void mpuintr(int irq, void *dev_id, struct pt_regs * dummy); diff -Nru a/sound/oss/msnd.c b/sound/oss/msnd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/msnd.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,406 @@ +/********************************************************************* + * + * msnd.c - Driver Base + * + * Turtle Beach MultiSound Sound Card Driver for Linux + * + * Copyright (C) 1998 Andrew Veliath + * + * 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. + * + * $Id: msnd.c,v 1.17 1999/03/21 16:50:09 andrewtv Exp $ + * + ********************************************************************/ + +#include +#if LINUX_VERSION_CODE < 0x020101 +# define LINUX20 +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef LINUX20 +# include +# include +# include +# include +# include "sound_config.h" +#else +# include +# include +# include +# include +#endif +#include +#include "msnd.h" + +#define LOGNAME "msnd" + +#define MSND_MAX_DEVS 4 + +static multisound_dev_t *devs[MSND_MAX_DEVS]; +static int num_devs; + +int __init msnd_register(multisound_dev_t *dev) +{ + int i; + + for (i = 0; i < MSND_MAX_DEVS; ++i) + if (devs[i] == NULL) + break; + + if (i == MSND_MAX_DEVS) + return -ENOMEM; + + devs[i] = dev; + ++num_devs; + + MOD_INC_USE_COUNT; + + return 0; +} + +void msnd_unregister(multisound_dev_t *dev) +{ + int i; + + for (i = 0; i < MSND_MAX_DEVS; ++i) + if (devs[i] == dev) + break; + + if (i == MSND_MAX_DEVS) { + printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n"); + return; + } + + devs[i] = NULL; + --num_devs; + + MOD_DEC_USE_COUNT; +} + +int msnd_get_num_devs(void) +{ + return num_devs; +} + +multisound_dev_t *msnd_get_dev(int j) +{ + int i; + + for (i = 0; i < MSND_MAX_DEVS && j; ++i) + if (devs[i] != NULL) + --j; + + if (i == MSND_MAX_DEVS || j != 0) + return NULL; + + return devs[i]; +} + +void msnd_init_queue(unsigned long base, int start, int size) +{ + isa_writew(PCTODSP_BASED(start), base + JQS_wStart); + isa_writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize); + isa_writew(0, base + JQS_wHead); + isa_writew(0, base + JQS_wTail); +} + +void msnd_fifo_init(msnd_fifo *f) +{ + f->data = NULL; +} + +void msnd_fifo_free(msnd_fifo *f) +{ + if (f->data) { + vfree(f->data); + f->data = NULL; + } +} + +int msnd_fifo_alloc(msnd_fifo *f, size_t n) +{ + msnd_fifo_free(f); + f->data = (char *)vmalloc(n); + f->n = n; + f->tail = 0; + f->head = 0; + f->len = 0; + + if (!f->data) + return -ENOMEM; + + return 0; +} + +void msnd_fifo_make_empty(msnd_fifo *f) +{ + f->len = f->tail = f->head = 0; +} + +int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user) +{ + int count = 0; + + if (f->len == f->n) + return 0; + + while ((count < len) && (f->len != f->n)) { + + int nwritten; + + if (f->head <= f->tail) { + nwritten = len - count; + if (nwritten > f->n - f->tail) + nwritten = f->n - f->tail; + } + else { + nwritten = f->head - f->tail; + if (nwritten > len - count) + nwritten = len - count; + } + + if (user) { + if (copy_from_user(f->data + f->tail, buf, nwritten)) + return -EFAULT; + } else + isa_memcpy_fromio(f->data + f->tail, (unsigned long) buf, nwritten); + + count += nwritten; + buf += nwritten; + f->len += nwritten; + f->tail += nwritten; + f->tail %= f->n; + } + + return count; +} + +int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user) +{ + int count = 0; + + if (f->len == 0) + return f->len; + + while ((count < len) && (f->len > 0)) { + + int nread; + + if (f->tail <= f->head) { + nread = len - count; + if (nread > f->n - f->head) + nread = f->n - f->head; + } + else { + nread = f->tail - f->head; + if (nread > len - count) + nread = len - count; + } + + if (user) { + if (copy_to_user(buf, f->data + f->head, nread)) + return -EFAULT; + } else + isa_memcpy_toio((unsigned long) buf, f->data + f->head, nread); + + count += nread; + buf += nread; + f->len -= nread; + f->head += nread; + f->head %= f->n; + } + + return count; +} + +int msnd_wait_TXDE(multisound_dev_t *dev) +{ + register unsigned int io = dev->io; + register int timeout = 1000; + + while(timeout-- > 0) + if (inb(io + HP_ISR) & HPISR_TXDE) + return 0; + + return -EIO; +} + +int msnd_wait_HC0(multisound_dev_t *dev) +{ + register unsigned int io = dev->io; + register int timeout = 1000; + + while(timeout-- > 0) + if (!(inb(io + HP_CVR) & HPCVR_HC)) + return 0; + + return -EIO; +} + +int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (msnd_wait_HC0(dev) == 0) { + outb(cmd, dev->io + HP_CVR); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; + } + spin_unlock_irqrestore(&dev->lock, flags); + + printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n"); + + return -EIO; +} + +int msnd_send_word(multisound_dev_t *dev, unsigned char high, + unsigned char mid, unsigned char low) +{ + register unsigned int io = dev->io; + + if (msnd_wait_TXDE(dev) == 0) { + outb(high, io + HP_TXH); + outb(mid, io + HP_TXM); + outb(low, io + HP_TXL); + return 0; + } + + printk(KERN_DEBUG LOGNAME ": Send host word timeout\n"); + + return -EIO; +} + +int msnd_upload_host(multisound_dev_t *dev, char *bin, int len) +{ + int i; + + if (len % 3 != 0) { + printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n"); + return -EINVAL; + } + + for (i = 0; i < len; i += 3) + if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0) + return -EIO; + + inb(dev->io + HP_RXL); + inb(dev->io + HP_CVR); + + return 0; +} + +int msnd_enable_irq(multisound_dev_t *dev) +{ + unsigned long flags; + + if (dev->irq_ref++) + return 0; + + printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n"); + + spin_lock_irqsave(&dev->lock, flags); + if (msnd_wait_TXDE(dev) == 0) { + outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR); + if (dev->type == msndClassic) + outb(dev->irqid, dev->io + HP_IRQM); + outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR); + outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR); + enable_irq(dev->irq); + msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; + } + spin_unlock_irqrestore(&dev->lock, flags); + + printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n"); + + return -EIO; +} + +int msnd_disable_irq(multisound_dev_t *dev) +{ + unsigned long flags; + + if (--dev->irq_ref > 0) + return 0; + + if (dev->irq_ref < 0) + printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref); + + printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n"); + + spin_lock_irqsave(&dev->lock, flags); + if (msnd_wait_TXDE(dev) == 0) { + outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR); + if (dev->type == msndClassic) + outb(HPIRQ_NONE, dev->io + HP_IRQM); + disable_irq(dev->irq); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; + } + spin_unlock_irqrestore(&dev->lock, flags); + + printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n"); + + return -EIO; +} + +#ifndef LINUX20 +EXPORT_SYMBOL(msnd_register); +EXPORT_SYMBOL(msnd_unregister); +EXPORT_SYMBOL(msnd_get_num_devs); +EXPORT_SYMBOL(msnd_get_dev); + +EXPORT_SYMBOL(msnd_init_queue); + +EXPORT_SYMBOL(msnd_fifo_init); +EXPORT_SYMBOL(msnd_fifo_free); +EXPORT_SYMBOL(msnd_fifo_alloc); +EXPORT_SYMBOL(msnd_fifo_make_empty); +EXPORT_SYMBOL(msnd_fifo_write); +EXPORT_SYMBOL(msnd_fifo_read); + +EXPORT_SYMBOL(msnd_wait_TXDE); +EXPORT_SYMBOL(msnd_wait_HC0); +EXPORT_SYMBOL(msnd_send_dsp_cmd); +EXPORT_SYMBOL(msnd_send_word); +EXPORT_SYMBOL(msnd_upload_host); + +EXPORT_SYMBOL(msnd_enable_irq); +EXPORT_SYMBOL(msnd_disable_irq); +#endif + +#ifdef MODULE +MODULE_AUTHOR ("Andrew Veliath "); +MODULE_DESCRIPTION ("Turtle Beach MultiSound Driver Base"); +MODULE_LICENSE("GPL"); + + +int init_module(void) +{ + return 0; +} + +void cleanup_module(void) +{ +} +#endif diff -Nru a/sound/oss/msnd.h b/sound/oss/msnd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/msnd.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,281 @@ +/********************************************************************* + * + * msnd.h + * + * Turtle Beach MultiSound Sound Card Driver for Linux + * + * Some parts of this header file were derived from the Turtle Beach + * MultiSound Driver Development Kit. + * + * Copyright (C) 1998 Andrew Veliath + * Copyright (C) 1993 Turtle Beach Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * $Id: msnd.h,v 1.36 1999/03/21 17:05:42 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_H +#define __MSND_H + +#define VERSION "0.8.3.1" + +#define DEFSAMPLERATE DSP_DEFAULT_SPEED +#define DEFSAMPLESIZE AFMT_U8 +#define DEFCHANNELS 1 + +#define DEFFIFOSIZE 128 + +#define SNDCARD_MSND 38 + +#define SRAM_BANK_SIZE 0x8000 +#define SRAM_CNTL_START 0x7F00 + +#define DSP_BASE_ADDR 0x4000 +#define DSP_BANK_BASE 0x4000 + +#define HP_ICR 0x00 +#define HP_CVR 0x01 +#define HP_ISR 0x02 +#define HP_IVR 0x03 +#define HP_NU 0x04 +#define HP_INFO 0x04 +#define HP_TXH 0x05 +#define HP_RXH 0x05 +#define HP_TXM 0x06 +#define HP_RXM 0x06 +#define HP_TXL 0x07 +#define HP_RXL 0x07 + +#define HP_ICR_DEF 0x00 +#define HP_CVR_DEF 0x12 +#define HP_ISR_DEF 0x06 +#define HP_IVR_DEF 0x0f +#define HP_NU_DEF 0x00 + +#define HP_IRQM 0x09 + +#define HPR_BLRC 0x08 +#define HPR_SPR1 0x09 +#define HPR_SPR2 0x0A +#define HPR_TCL0 0x0B +#define HPR_TCL1 0x0C +#define HPR_TCL2 0x0D +#define HPR_TCL3 0x0E +#define HPR_TCL4 0x0F + +#define HPICR_INIT 0x80 +#define HPICR_HM1 0x40 +#define HPICR_HM0 0x20 +#define HPICR_HF1 0x10 +#define HPICR_HF0 0x08 +#define HPICR_TREQ 0x02 +#define HPICR_RREQ 0x01 + +#define HPCVR_HC 0x80 + +#define HPISR_HREQ 0x80 +#define HPISR_DMA 0x40 +#define HPISR_HF3 0x10 +#define HPISR_HF2 0x08 +#define HPISR_TRDY 0x04 +#define HPISR_TXDE 0x02 +#define HPISR_RXDF 0x01 + +#define HPIO_290 0 +#define HPIO_260 1 +#define HPIO_250 2 +#define HPIO_240 3 +#define HPIO_230 4 +#define HPIO_220 5 +#define HPIO_210 6 +#define HPIO_3E0 7 + +#define HPMEM_NONE 0 +#define HPMEM_B000 1 +#define HPMEM_C800 2 +#define HPMEM_D000 3 +#define HPMEM_D400 4 +#define HPMEM_D800 5 +#define HPMEM_E000 6 +#define HPMEM_E800 7 + +#define HPIRQ_NONE 0 +#define HPIRQ_5 1 +#define HPIRQ_7 2 +#define HPIRQ_9 3 +#define HPIRQ_10 4 +#define HPIRQ_11 5 +#define HPIRQ_12 6 +#define HPIRQ_15 7 + +#define HIMT_PLAY_DONE 0x00 +#define HIMT_RECORD_DONE 0x01 +#define HIMT_MIDI_EOS 0x02 +#define HIMT_MIDI_OUT 0x03 + +#define HIMT_MIDI_IN_UCHAR 0x0E +#define HIMT_DSP 0x0F + +#define HDEX_BASE 0x92 +#define HDEX_PLAY_START (0 + HDEX_BASE) +#define HDEX_PLAY_STOP (1 + HDEX_BASE) +#define HDEX_PLAY_PAUSE (2 + HDEX_BASE) +#define HDEX_PLAY_RESUME (3 + HDEX_BASE) +#define HDEX_RECORD_START (4 + HDEX_BASE) +#define HDEX_RECORD_STOP (5 + HDEX_BASE) +#define HDEX_MIDI_IN_START (6 + HDEX_BASE) +#define HDEX_MIDI_IN_STOP (7 + HDEX_BASE) +#define HDEX_MIDI_OUT_START (8 + HDEX_BASE) +#define HDEX_MIDI_OUT_STOP (9 + HDEX_BASE) +#define HDEX_AUX_REQ (10 + HDEX_BASE) + +#define HIWORD(l) ((WORD)((((DWORD)(l)) >> 16) & 0xFFFF)) +#define LOWORD(l) ((WORD)(DWORD)(l)) +#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) +#define LOBYTE(w) ((BYTE)(w)) +#define MAKELONG(low,hi) ((long)(((WORD)(low))|(((DWORD)((WORD)(hi)))<<16))) +#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8))) + +#define PCTODSP_OFFSET(w) (USHORT)((w)/2) +#define PCTODSP_BASED(w) (USHORT)(((w)/2) + DSP_BASE_ADDR) +#define DSPTOPC_BASED(w) (((w) - DSP_BASE_ADDR) * 2) + +#ifdef SLOWIO +# undef outb +# undef inb +# define outb outb_p +# define inb inb_p +#endif + +/* JobQueueStruct */ +#define JQS_wStart 0x00 +#define JQS_wSize 0x02 +#define JQS_wHead 0x04 +#define JQS_wTail 0x06 +#define JQS__size 0x08 + +/* DAQueueDataStruct */ +#define DAQDS_wStart 0x00 +#define DAQDS_wSize 0x02 +#define DAQDS_wFormat 0x04 +#define DAQDS_wSampleSize 0x06 +#define DAQDS_wChannels 0x08 +#define DAQDS_wSampleRate 0x0A +#define DAQDS_wIntMsg 0x0C +#define DAQDS_wFlags 0x0E +#define DAQDS__size 0x10 + +typedef u8 BYTE; +typedef u16 USHORT; +typedef u16 WORD; +typedef u32 DWORD; +typedef unsigned long LPDAQD; + +/* Generic FIFO */ +typedef struct { + size_t n, len; + char *data; + int head, tail; +} msnd_fifo; + +typedef struct multisound_dev { + /* Linux device info */ + char *name; + int dsp_minor, mixer_minor; + int ext_midi_dev, hdr_midi_dev; + + /* Hardware resources */ + int io, numio; + int memid, irqid; + int irq, irq_ref; + unsigned char info; + unsigned long base; + + /* Motorola 56k DSP SMA */ + unsigned long SMA; + unsigned long DAPQ, DARQ, MODQ, MIDQ, DSPQ; + unsigned long pwDSPQData, pwMIDQData, pwMODQData; + int dspq_data_buff, dspq_buff_size; + + /* State variables */ + enum { msndClassic, msndPinnacle } type; + mode_t mode; + unsigned long flags; +#define F_RESETTING 0 +#define F_HAVEDIGITAL 1 +#define F_AUDIO_WRITE_INUSE 2 +#define F_WRITING 3 +#define F_WRITEBLOCK 4 +#define F_WRITEFLUSH 5 +#define F_AUDIO_READ_INUSE 6 +#define F_READING 7 +#define F_READBLOCK 8 +#define F_EXT_MIDI_INUSE 9 +#define F_HDR_MIDI_INUSE 10 +#define F_DISABLE_WRITE_NDELAY 11 + wait_queue_head_t writeblock; + wait_queue_head_t readblock; + wait_queue_head_t writeflush; + spinlock_t lock; + int nresets; + unsigned long recsrc; + int left_levels[16]; + int right_levels[16]; + int mixer_mod_count; + int calibrate_signal; + int play_sample_size, play_sample_rate, play_channels; + int play_ndelay; + int rec_sample_size, rec_sample_rate, rec_channels; + int rec_ndelay; + BYTE bCurrentMidiPatch; + + /* Digital audio FIFOs */ + msnd_fifo DAPF, DARF; + int fifosize; + int last_playbank, last_recbank; + + /* MIDI in callback */ + void (*midi_in_interrupt)(struct multisound_dev *); +} multisound_dev_t; + +#ifndef mdelay +# define mdelay(a) udelay((a) * 1000) +#endif + +int msnd_register(multisound_dev_t *dev); +void msnd_unregister(multisound_dev_t *dev); +int msnd_get_num_devs(void); +multisound_dev_t * msnd_get_dev(int i); + +void msnd_init_queue(unsigned long, int start, int size); + +void msnd_fifo_init(msnd_fifo *f); +void msnd_fifo_free(msnd_fifo *f); +int msnd_fifo_alloc(msnd_fifo *f, size_t n); +void msnd_fifo_make_empty(msnd_fifo *f); +int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user); +int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user); + +int msnd_wait_TXDE(multisound_dev_t *dev); +int msnd_wait_HC0(multisound_dev_t *dev); +int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd); +int msnd_send_word(multisound_dev_t *dev, unsigned char high, + unsigned char mid, unsigned char low); +int msnd_upload_host(multisound_dev_t *dev, char *bin, int len); +int msnd_enable_irq(multisound_dev_t *dev); +int msnd_disable_irq(multisound_dev_t *dev); + +#endif /* __MSND_H */ diff -Nru a/sound/oss/msnd_classic.c b/sound/oss/msnd_classic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/msnd_classic.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,3 @@ +/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */ +#define MSND_CLASSIC +#include "msnd_pinnacle.c" diff -Nru a/sound/oss/msnd_classic.h b/sound/oss/msnd_classic.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/msnd_classic.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,188 @@ +/********************************************************************* + * + * msnd_classic.h + * + * Turtle Beach MultiSound Sound Card Driver for Linux + * + * Some parts of this header file were derived from the Turtle Beach + * MultiSound Driver Development Kit. + * + * Copyright (C) 1998 Andrew Veliath + * Copyright (C) 1993 Turtle Beach Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * $Id: msnd_classic.h,v 1.10 1999/03/21 17:36:09 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_CLASSIC_H +#define __MSND_CLASSIC_H + +#include + +#define DSP_NUMIO 0x10 + +#define HP_MEMM 0x08 + +#define HP_BITM 0x0E +#define HP_WAIT 0x0D +#define HP_DSPR 0x0A +#define HP_PROR 0x0B +#define HP_BLKS 0x0C + +#define HPPRORESET_OFF 0 +#define HPPRORESET_ON 1 + +#define HPDSPRESET_OFF 0 +#define HPDSPRESET_ON 1 + +#define HPBLKSEL_0 0 +#define HPBLKSEL_1 1 + +#define HPWAITSTATE_0 0 +#define HPWAITSTATE_1 1 + +#define HPBITMODE_16 0 +#define HPBITMODE_8 1 + +#define HIDSP_INT_PLAY_UNDER 0x00 +#define HIDSP_INT_RECORD_OVER 0x01 +#define HIDSP_INPUT_CLIPPING 0x02 +#define HIDSP_MIDI_IN_OVER 0x10 +#define HIDSP_MIDI_OVERRUN_ERR 0x13 + +#define HDEXAR_CLEAR_PEAKS 1 +#define HDEXAR_IN_SET_POTS 2 +#define HDEXAR_AUX_SET_POTS 3 +#define HDEXAR_CAL_A_TO_D 4 +#define HDEXAR_RD_EXT_DSP_BITS 5 + +#define TIME_PRO_RESET_DONE 0x028A +#define TIME_PRO_SYSEX 0x0040 +#define TIME_PRO_RESET 0x0032 + +#define AGND 0x01 +#define SIGNAL 0x02 + +#define EXT_DSP_BIT_DCAL 0x0001 +#define EXT_DSP_BIT_MIDI_CON 0x0002 + +#define BUFFSIZE 0x8000 +#define HOSTQ_SIZE 0x40 + +#define SRAM_CNTL_START 0x7F00 +#define SMA_STRUCT_START 0x7F40 + +#define DAP_BUFF_SIZE 0x2400 +#define DAR_BUFF_SIZE 0x2000 + +#define DAPQ_STRUCT_SIZE 0x10 +#define DARQ_STRUCT_SIZE 0x10 +#define DAPQ_BUFF_SIZE (3 * 0x10) +#define DARQ_BUFF_SIZE (3 * 0x10) +#define MODQ_BUFF_SIZE 0x400 +#define MIDQ_BUFF_SIZE 0x200 +#define DSPQ_BUFF_SIZE 0x40 + +#define DAPQ_DATA_BUFF 0x6C00 +#define DARQ_DATA_BUFF 0x6C30 +#define MODQ_DATA_BUFF 0x6C60 +#define MIDQ_DATA_BUFF 0x7060 +#define DSPQ_DATA_BUFF 0x7260 + +#define DAPQ_OFFSET SRAM_CNTL_START +#define DARQ_OFFSET (SRAM_CNTL_START + 0x08) +#define MODQ_OFFSET (SRAM_CNTL_START + 0x10) +#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18) +#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20) + +#define MOP_SYNTH 0x10 +#define MOP_EXTOUT 0x32 +#define MOP_EXTTHRU 0x02 +#define MOP_OUTMASK 0x01 + +#define MIP_EXTIN 0x01 +#define MIP_SYNTH 0x00 +#define MIP_INMASK 0x32 + +/* Classic SMA Common Data */ +#define SMA_wCurrPlayBytes 0x0000 +#define SMA_wCurrRecordBytes 0x0002 +#define SMA_wCurrPlayVolLeft 0x0004 +#define SMA_wCurrPlayVolRight 0x0006 +#define SMA_wCurrInVolLeft 0x0008 +#define SMA_wCurrInVolRight 0x000a +#define SMA_wUser_3 0x000c +#define SMA_wUser_4 0x000e +#define SMA_dwUser_5 0x0010 +#define SMA_dwUser_6 0x0014 +#define SMA_wUser_7 0x0018 +#define SMA_wReserved_A 0x001a +#define SMA_wReserved_B 0x001c +#define SMA_wReserved_C 0x001e +#define SMA_wReserved_D 0x0020 +#define SMA_wReserved_E 0x0022 +#define SMA_wReserved_F 0x0024 +#define SMA_wReserved_G 0x0026 +#define SMA_wReserved_H 0x0028 +#define SMA_wCurrDSPStatusFlags 0x002a +#define SMA_wCurrHostStatusFlags 0x002c +#define SMA_wCurrInputTagBits 0x002e +#define SMA_wCurrLeftPeak 0x0030 +#define SMA_wCurrRightPeak 0x0032 +#define SMA_wExtDSPbits 0x0034 +#define SMA_bExtHostbits 0x0036 +#define SMA_bBoardLevel 0x0037 +#define SMA_bInPotPosRight 0x0038 +#define SMA_bInPotPosLeft 0x0039 +#define SMA_bAuxPotPosRight 0x003a +#define SMA_bAuxPotPosLeft 0x003b +#define SMA_wCurrMastVolLeft 0x003c +#define SMA_wCurrMastVolRight 0x003e +#define SMA_bUser_12 0x0040 +#define SMA_bUser_13 0x0041 +#define SMA_wUser_14 0x0042 +#define SMA_wUser_15 0x0044 +#define SMA_wCalFreqAtoD 0x0046 +#define SMA_wUser_16 0x0048 +#define SMA_wUser_17 0x004a +#define SMA__size 0x004c + +#ifdef HAVE_DSPCODEH +# include "msndperm.c" +# include "msndinit.c" +# define PERMCODE msndperm +# define INITCODE msndinit +# define PERMCODESIZE sizeof(msndperm) +# define INITCODESIZE sizeof(msndinit) +#else +# ifndef CONFIG_MSNDCLAS_INIT_FILE +# define CONFIG_MSNDCLAS_INIT_FILE \ + "/etc/sound/msndinit.bin" +# endif +# ifndef CONFIG_MSNDCLAS_PERM_FILE +# define CONFIG_MSNDCLAS_PERM_FILE \ + "/etc/sound/msndperm.bin" +# endif +# define PERMCODEFILE CONFIG_MSNDCLAS_PERM_FILE +# define INITCODEFILE CONFIG_MSNDCLAS_INIT_FILE +# define PERMCODE dspini +# define INITCODE permini +# define PERMCODESIZE sizeof_dspini +# define INITCODESIZE sizeof_permini +#endif +#define LONGNAME "MultiSound (Classic/Monterey/Tahiti)" + +#endif /* __MSND_CLASSIC_H */ diff -Nru a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/msnd_pinnacle.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,1894 @@ +/********************************************************************* + * + * Turtle Beach MultiSound Sound Card Driver for Linux + * Linux 2.0/2.2 Version + * + * msnd_pinnacle.c / msnd_classic.c + * + * -- If MSND_CLASSIC is defined: + * + * -> driver for Turtle Beach Classic/Monterey/Tahiti + * + * -- Else + * + * -> driver for Turtle Beach Pinnacle/Fiji + * + * Copyright (C) 1998 Andrew Veliath + * + * 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. + * + * $Id: msnd_pinnacle.c,v 1.8 2000/12/30 00:33:21 sycamore Exp $ + * + * 12-3-2000 Modified IO port validation Steve Sycamore + * + * + * $$$: msnd_pinnacle.c,v 1.75 1999/03/21 16:50:09 andrewtv $$$ $ + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" +#include "sound_firmware.h" +#ifdef MSND_CLASSIC +# ifndef __alpha__ +# define SLOWIO +# endif +#endif +#include "msnd.h" +#ifdef MSND_CLASSIC +# ifdef CONFIG_MSNDCLAS_HAVE_BOOT +# define HAVE_DSPCODEH +# endif +# include "msnd_classic.h" +# define LOGNAME "msnd_classic" +#else +# ifdef CONFIG_MSNDPIN_HAVE_BOOT +# define HAVE_DSPCODEH +# endif +# include "msnd_pinnacle.h" +# define LOGNAME "msnd_pinnacle" +#endif + +#ifndef CONFIG_MSND_WRITE_NDELAY +# define CONFIG_MSND_WRITE_NDELAY 1 +#endif + +#define get_play_delay_jiffies(size) ((size) * HZ * \ + dev.play_sample_size / 8 / \ + dev.play_sample_rate / \ + dev.play_channels) + +#define get_rec_delay_jiffies(size) ((size) * HZ * \ + dev.rec_sample_size / 8 / \ + dev.rec_sample_rate / \ + dev.rec_channels) + +static multisound_dev_t dev; + +#ifndef HAVE_DSPCODEH +static char *dspini, *permini; +static int sizeof_dspini, sizeof_permini; +#endif + +static int dsp_full_reset(void); +static void dsp_write_flush(void); + +static __inline__ int chk_send_dsp_cmd(multisound_dev_t *dev, register BYTE cmd) +{ + if (msnd_send_dsp_cmd(dev, cmd) == 0) + return 0; + dsp_full_reset(); + return msnd_send_dsp_cmd(dev, cmd); +} + +static void reset_play_queue(void) +{ + int n; + LPDAQD lpDAQ; + + dev.last_playbank = -1; + isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wHead); + isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wTail); + + for (n = 0, lpDAQ = dev.base + DAPQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { + isa_writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart); + isa_writew(0, lpDAQ + DAQDS_wSize); + isa_writew(1, lpDAQ + DAQDS_wFormat); + isa_writew(dev.play_sample_size, lpDAQ + DAQDS_wSampleSize); + isa_writew(dev.play_channels, lpDAQ + DAQDS_wChannels); + isa_writew(dev.play_sample_rate, lpDAQ + DAQDS_wSampleRate); + isa_writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg); + isa_writew(n, lpDAQ + DAQDS_wFlags); + } +} + +static void reset_record_queue(void) +{ + int n; + LPDAQD lpDAQ; + unsigned long flags; + + dev.last_recbank = 2; + isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DARQ + JQS_wHead); + isa_writew(PCTODSP_OFFSET(dev.last_recbank * DAQDS__size), dev.DARQ + JQS_wTail); + + /* Critical section: bank 1 access */ + spin_lock_irqsave(&dev.lock, flags); + outb(HPBLKSEL_1, dev.io + HP_BLKS); + isa_memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); + outb(HPBLKSEL_0, dev.io + HP_BLKS); + spin_unlock_irqrestore(&dev.lock, flags); + + for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { + isa_writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart); + isa_writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize); + isa_writew(1, lpDAQ + DAQDS_wFormat); + isa_writew(dev.rec_sample_size, lpDAQ + DAQDS_wSampleSize); + isa_writew(dev.rec_channels, lpDAQ + DAQDS_wChannels); + isa_writew(dev.rec_sample_rate, lpDAQ + DAQDS_wSampleRate); + isa_writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg); + isa_writew(n, lpDAQ + DAQDS_wFlags); + } +} + +static void reset_queues(void) +{ + if (dev.mode & FMODE_WRITE) { + msnd_fifo_make_empty(&dev.DAPF); + reset_play_queue(); + } + if (dev.mode & FMODE_READ) { + msnd_fifo_make_empty(&dev.DARF); + reset_record_queue(); + } +} + +static int dsp_set_format(struct file *file, int val) +{ + int data, i; + LPDAQD lpDAQ, lpDARQ; + + lpDAQ = dev.base + DAPQ_DATA_BUFF; + lpDARQ = dev.base + DARQ_DATA_BUFF; + + switch (val) { + case AFMT_U8: + case AFMT_S16_LE: + data = val; + break; + default: + data = DEFSAMPLESIZE; + break; + } + + for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { + if (file->f_mode & FMODE_WRITE) + isa_writew(data, lpDAQ + DAQDS_wSampleSize); + if (file->f_mode & FMODE_READ) + isa_writew(data, lpDARQ + DAQDS_wSampleSize); + } + if (file->f_mode & FMODE_WRITE) + dev.play_sample_size = data; + if (file->f_mode & FMODE_READ) + dev.rec_sample_size = data; + + return data; +} + +static int dsp_get_frag_size(void) +{ + int size; + size = dev.fifosize / 4; + if (size > 32 * 1024) + size = 32 * 1024; + return size; +} + +static int dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int val, i, data, tmp; + LPDAQD lpDAQ, lpDARQ; + audio_buf_info abinfo; + unsigned long flags; + + lpDAQ = dev.base + DAPQ_DATA_BUFF; + lpDARQ = dev.base + DARQ_DATA_BUFF; + + switch (cmd) { + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_SETFRAGMENT: + case SNDCTL_DSP_SETDUPLEX: + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + return -EINVAL; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&dev.lock, flags); + abinfo.fragsize = dsp_get_frag_size(); + abinfo.bytes = dev.DAPF.n - dev.DAPF.len; + abinfo.fragstotal = dev.DAPF.n / abinfo.fragsize; + abinfo.fragments = abinfo.bytes / abinfo.fragsize; + spin_unlock_irqrestore(&dev.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; + spin_lock_irqsave(&dev.lock, flags); + abinfo.fragsize = dsp_get_frag_size(); + abinfo.bytes = dev.DARF.n - dev.DARF.len; + abinfo.fragstotal = dev.DARF.n / abinfo.fragsize; + abinfo.fragments = abinfo.bytes / abinfo.fragsize; + spin_unlock_irqrestore(&dev.lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_RESET: + dev.nresets = 0; + reset_queues(); + return 0; + + case SNDCTL_DSP_SYNC: + dsp_write_flush(); + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + tmp = dsp_get_frag_size(); + if (put_user(tmp, (int *)arg)) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETFMTS: + val = AFMT_S16_LE | AFMT_U8; + if (put_user(val, (int *)arg)) + return -EFAULT; + return 0; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (file->f_mode & FMODE_WRITE) + data = val == AFMT_QUERY + ? dev.play_sample_size + : dsp_set_format(file, val); + else + data = val == AFMT_QUERY + ? dev.rec_sample_size + : dsp_set_format(file, val); + + if (put_user(data, (int *)arg)) + return -EFAULT; + return 0; + + case SNDCTL_DSP_NONBLOCK: + if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags) && + file->f_mode & FMODE_WRITE) + dev.play_ndelay = 1; + if (file->f_mode & FMODE_READ) + dev.rec_ndelay = 1; + return 0; + + case SNDCTL_DSP_GETCAPS: + val = DSP_CAP_DUPLEX | DSP_CAP_BATCH; + if (put_user(val, (int *)arg)) + return -EFAULT; + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (val < 8000) + val = 8000; + + if (val > 48000) + val = 48000; + + data = val; + + for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { + if (file->f_mode & FMODE_WRITE) + isa_writew(data, lpDAQ + DAQDS_wSampleRate); + if (file->f_mode & FMODE_READ) + isa_writew(data, lpDARQ + DAQDS_wSampleRate); + } + if (file->f_mode & FMODE_WRITE) + dev.play_sample_rate = data; + if (file->f_mode & FMODE_READ) + dev.rec_sample_rate = data; + + if (put_user(data, (int *)arg)) + return -EFAULT; + return 0; + + case SNDCTL_DSP_CHANNELS: + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (cmd == SNDCTL_DSP_CHANNELS) { + switch (val) { + case 1: + case 2: + data = val; + break; + default: + val = data = 2; + break; + } + } else { + switch (val) { + case 0: + data = 1; + break; + default: + val = 1; + case 1: + data = 2; + break; + } + } + + for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { + if (file->f_mode & FMODE_WRITE) + isa_writew(data, lpDAQ + DAQDS_wChannels); + if (file->f_mode & FMODE_READ) + isa_writew(data, lpDARQ + DAQDS_wChannels); + } + if (file->f_mode & FMODE_WRITE) + dev.play_channels = data; + if (file->f_mode & FMODE_READ) + dev.rec_channels = data; + + if (put_user(val, (int *)arg)) + return -EFAULT; + return 0; + } + + return -EINVAL; +} + +static int mixer_get(int d) +{ + if (d > 31) + return -EINVAL; + + switch (d) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: + case SOUND_MIXER_IMIX: + case SOUND_MIXER_LINE1: +#ifndef MSND_CLASSIC + case SOUND_MIXER_MIC: + case SOUND_MIXER_SYNTH: +#endif + return (dev.left_levels[d] >> 8) * 100 / 0xff | + (((dev.right_levels[d] >> 8) * 100 / 0xff) << 8); + default: + return 0; + } +} + +#define update_volm(a,b) \ + isa_writew((dev.left_levels[a] >> 1) * \ + isa_readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \ + dev.SMA + SMA_##b##Left); \ + isa_writew((dev.right_levels[a] >> 1) * \ + isa_readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \ + dev.SMA + SMA_##b##Right); + +#define update_potm(d,s,ar) \ + isa_writeb((dev.left_levels[d] >> 8) * \ + isa_readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \ + dev.SMA + SMA_##s##Left); \ + isa_writeb((dev.right_levels[d] >> 8) * \ + isa_readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \ + dev.SMA + SMA_##s##Right); \ + if (msnd_send_word(&dev, 0, 0, ar) == 0) \ + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + +#define update_pot(d,s,ar) \ + isa_writeb(dev.left_levels[d] >> 8, \ + dev.SMA + SMA_##s##Left); \ + isa_writeb(dev.right_levels[d] >> 8, \ + dev.SMA + SMA_##s##Right); \ + if (msnd_send_word(&dev, 0, 0, ar) == 0) \ + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + +static int mixer_set(int d, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int bLeft, bRight; + int wLeft, wRight; + int updatemaster = 0; + + if (d > 31) + return -EINVAL; + + bLeft = left * 0xff / 100; + wLeft = left * 0xffff / 100; + + bRight = right * 0xff / 100; + wRight = right * 0xffff / 100; + + dev.left_levels[d] = wLeft; + dev.right_levels[d] = wRight; + + switch (d) { + /* master volume unscaled controls */ + case SOUND_MIXER_LINE: /* line pot control */ + /* scaled by IMIX in digital mix */ + isa_writeb(bLeft, dev.SMA + SMA_bInPotPosLeft); + isa_writeb(bRight, dev.SMA + SMA_bInPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; +#ifndef MSND_CLASSIC + case SOUND_MIXER_MIC: /* mic pot control */ + /* scaled by IMIX in digital mix */ + isa_writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft); + isa_writeb(bRight, dev.SMA + SMA_bMicPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; +#endif + case SOUND_MIXER_VOLUME: /* master volume */ + isa_writew(wLeft, dev.SMA + SMA_wCurrMastVolLeft); + isa_writew(wRight, dev.SMA + SMA_wCurrMastVolRight); + /* fall through */ + + case SOUND_MIXER_LINE1: /* aux pot control */ + /* scaled by master volume */ + /* fall through */ + + /* digital controls */ + case SOUND_MIXER_SYNTH: /* synth vol (dsp mix) */ + case SOUND_MIXER_PCM: /* pcm vol (dsp mix) */ + case SOUND_MIXER_IMIX: /* input monitor (dsp mix) */ + /* scaled by master volume */ + updatemaster = 1; + break; + + default: + return 0; + } + + if (updatemaster) { + /* update master volume scaled controls */ + update_volm(SOUND_MIXER_PCM, wCurrPlayVol); + update_volm(SOUND_MIXER_IMIX, wCurrInVol); +#ifndef MSND_CLASSIC + update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol); +#endif + update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS); + } + + return mixer_get(d); +} + +static void mixer_setup(void) +{ + update_pot(SOUND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); + update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS); + update_volm(SOUND_MIXER_PCM, wCurrPlayVol); + update_volm(SOUND_MIXER_IMIX, wCurrInVol); +#ifndef MSND_CLASSIC + update_pot(SOUND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS); + update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol); +#endif +} + +static unsigned long set_recsrc(unsigned long recsrc) +{ + if (dev.recsrc == recsrc) + return dev.recsrc; +#ifdef HAVE_NORECSRC + else if (recsrc == 0) + dev.recsrc = 0; +#endif + else + dev.recsrc ^= recsrc; + +#ifndef MSND_CLASSIC + if (dev.recsrc & SOUND_MASK_IMIX) { + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + } + else if (dev.recsrc & SOUND_MASK_SYNTH) { + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + } + else if ((dev.recsrc & SOUND_MASK_DIGITAL1) && test_bit(F_HAVEDIGITAL, &dev.flags)) { + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + } + else { +#ifdef HAVE_NORECSRC + /* Select no input (?) */ + dev.recsrc = 0; +#else + dev.recsrc = SOUND_MASK_IMIX; + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); +#endif + } +#endif /* MSND_CLASSIC */ + + return dev.recsrc; +} + +static unsigned long force_recsrc(unsigned long recsrc) +{ + dev.recsrc = 0; + return set_recsrc(recsrc); +} + +#define set_mixer_info() \ + 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) +{ + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + set_mixer_info(); + info.modify_counter = dev.mixer_mod_count; + return copy_to_user((void *)arg, &info, sizeof(info)); + } else if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + set_mixer_info(); + return copy_to_user((void *)arg, &info, sizeof(info)); + } else if (cmd == SOUND_MIXER_PRIVATE1) { + dev.nresets = 0; + dsp_full_reset(); + return 0; + } else if (((cmd >> 8) & 0xff) == 'M') { + int val = 0; + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = set_recsrc(val); + break; + + default: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = mixer_set(cmd & 0xff, val); + break; + } + ++dev.mixer_mod_count; + return put_user(val, (int *)arg); + } else { + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + val = dev.recsrc; + break; + + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_PCM | + SOUND_MASK_LINE | + SOUND_MASK_IMIX | + SOUND_MASK_LINE1 | +#ifndef MSND_CLASSIC + SOUND_MASK_MIC | + SOUND_MASK_SYNTH | +#endif + SOUND_MASK_VOLUME; + break; + + case SOUND_MIXER_RECMASK: +#ifdef MSND_CLASSIC + val = 0; +#else + val = SOUND_MASK_IMIX | + SOUND_MASK_SYNTH; + if (test_bit(F_HAVEDIGITAL, &dev.flags)) + val |= SOUND_MASK_DIGITAL1; +#endif + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: + if ((val = mixer_get(cmd & 0xff)) < 0) + return -EINVAL; + break; + } + } + + return put_user(val, (int *)arg); + } + + return -EINVAL; +} + +static int dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int minor = MINOR(inode->i_rdev); + + if (cmd == OSS_GETVERSION) { + int sound_version = SOUND_VERSION; + return put_user(sound_version, (int *)arg); + } + + if (minor == dev.dsp_minor) + return dsp_ioctl(file, cmd, arg); + else if (minor == dev.mixer_minor) + return mixer_ioctl(cmd, arg); + + return -EINVAL; +} + +static void dsp_write_flush(void) +{ + if (!(dev.mode & FMODE_WRITE) || !test_bit(F_WRITING, &dev.flags)) + return; + set_bit(F_WRITEFLUSH, &dev.flags); + interruptible_sleep_on_timeout( + &dev.writeflush, + get_play_delay_jiffies(dev.DAPF.len)); + clear_bit(F_WRITEFLUSH, &dev.flags); + if (!signal_pending(current)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(get_play_delay_jiffies(DAP_BUFF_SIZE)); + } + clear_bit(F_WRITING, &dev.flags); +} + +static void dsp_halt(struct file *file) +{ + if ((file ? file->f_mode : dev.mode) & FMODE_READ) { + clear_bit(F_READING, &dev.flags); + chk_send_dsp_cmd(&dev, HDEX_RECORD_STOP); + msnd_disable_irq(&dev); + if (file) { + printk(KERN_DEBUG LOGNAME ": Stopping read for %p\n", file); + dev.mode &= ~FMODE_READ; + } + clear_bit(F_AUDIO_READ_INUSE, &dev.flags); + } + if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) { + if (test_bit(F_WRITING, &dev.flags)) { + dsp_write_flush(); + chk_send_dsp_cmd(&dev, HDEX_PLAY_STOP); + } + msnd_disable_irq(&dev); + if (file) { + printk(KERN_DEBUG LOGNAME ": Stopping write for %p\n", file); + dev.mode &= ~FMODE_WRITE; + } + clear_bit(F_AUDIO_WRITE_INUSE, &dev.flags); + } +} + +static int dsp_release(struct file *file) +{ + dsp_halt(file); + return 0; +} + +static int dsp_open(struct file *file) +{ + if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) { + set_bit(F_AUDIO_WRITE_INUSE, &dev.flags); + clear_bit(F_WRITING, &dev.flags); + msnd_fifo_make_empty(&dev.DAPF); + reset_play_queue(); + if (file) { + printk(KERN_DEBUG LOGNAME ": Starting write for %p\n", file); + dev.mode |= FMODE_WRITE; + } + msnd_enable_irq(&dev); + } + if ((file ? file->f_mode : dev.mode) & FMODE_READ) { + set_bit(F_AUDIO_READ_INUSE, &dev.flags); + clear_bit(F_READING, &dev.flags); + msnd_fifo_make_empty(&dev.DARF); + reset_record_queue(); + if (file) { + printk(KERN_DEBUG LOGNAME ": Starting read for %p\n", file); + dev.mode |= FMODE_READ; + } + msnd_enable_irq(&dev); + } + return 0; +} + +static void set_default_play_audio_parameters(void) +{ + dev.play_sample_size = DEFSAMPLESIZE; + dev.play_sample_rate = DEFSAMPLERATE; + dev.play_channels = DEFCHANNELS; +} + +static void set_default_rec_audio_parameters(void) +{ + dev.rec_sample_size = DEFSAMPLESIZE; + dev.rec_sample_rate = DEFSAMPLERATE; + dev.rec_channels = DEFCHANNELS; +} + +static void set_default_audio_parameters(void) +{ + set_default_play_audio_parameters(); + set_default_rec_audio_parameters(); +} + +static int dev_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + int err = 0; + + if (minor == dev.dsp_minor) { + if ((file->f_mode & FMODE_WRITE && + test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) || + (file->f_mode & FMODE_READ && + test_bit(F_AUDIO_READ_INUSE, &dev.flags))) + return -EBUSY; + + if ((err = dsp_open(file)) >= 0) { + dev.nresets = 0; + if (file->f_mode & FMODE_WRITE) { + set_default_play_audio_parameters(); + if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags)) + dev.play_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0; + else + dev.play_ndelay = 0; + } + if (file->f_mode & FMODE_READ) { + set_default_rec_audio_parameters(); + dev.rec_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0; + } + } + } + else if (minor == dev.mixer_minor) { + /* nothing */ + } else + err = -EINVAL; + + return err; +} + +static int dev_release(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + int err = 0; + + lock_kernel(); + if (minor == dev.dsp_minor) + err = dsp_release(file); + else if (minor == dev.mixer_minor) { + /* nothing */ + } else + err = -EINVAL; + unlock_kernel(); + return err; +} + +static __inline__ int pack_DARQ_to_DARF(register int bank) +{ + register int size, n, timeout = 3; + register WORD wTmp; + LPDAQD DAQD; + + /* Increment the tail and check for queue wrap */ + wTmp = isa_readw(dev.DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size); + if (wTmp > isa_readw(dev.DARQ + JQS_wSize)) + wTmp = 0; + while (wTmp == isa_readw(dev.DARQ + JQS_wHead) && timeout--) + udelay(1); + isa_writew(wTmp, dev.DARQ + JQS_wTail); + + /* Get our digital audio queue struct */ + DAQD = bank * DAQDS__size + dev.base + DARQ_DATA_BUFF; + + /* Get length of data */ + size = isa_readw(DAQD + DAQDS_wSize); + + /* Read data from the head (unprotected bank 1 access okay + since this is only called inside an interrupt) */ + outb(HPBLKSEL_1, dev.io + HP_BLKS); + if ((n = msnd_fifo_write( + &dev.DARF, + (char *)(dev.base + bank * DAR_BUFF_SIZE), + size, 0)) <= 0) { + outb(HPBLKSEL_0, dev.io + HP_BLKS); + return n; + } + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + return 1; +} + +static __inline__ int pack_DAPF_to_DAPQ(register int start) +{ + register WORD DAPQ_tail; + register int protect = start, nbanks = 0; + LPDAQD DAQD; + + DAPQ_tail = isa_readw(dev.DAPQ + JQS_wTail); + while (DAPQ_tail != isa_readw(dev.DAPQ + JQS_wHead) || start) { + register int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size); + register int n; + unsigned long flags; + + /* Write the data to the new tail */ + if (protect) { + /* Critical section: protect fifo in non-interrupt */ + spin_lock_irqsave(&dev.lock, flags); + if ((n = msnd_fifo_read( + &dev.DAPF, + (char *)(dev.base + bank_num * DAP_BUFF_SIZE), + DAP_BUFF_SIZE, 0)) < 0) { + spin_unlock_irqrestore(&dev.lock, flags); + return n; + } + spin_unlock_irqrestore(&dev.lock, flags); + } else { + if ((n = msnd_fifo_read( + &dev.DAPF, + (char *)(dev.base + bank_num * DAP_BUFF_SIZE), + DAP_BUFF_SIZE, 0)) < 0) { + return n; + } + } + if (!n) + break; + + if (start) + start = 0; + + /* Get our digital audio queue struct */ + DAQD = bank_num * DAQDS__size + dev.base + DAPQ_DATA_BUFF; + + /* Write size of this bank */ + isa_writew(n, DAQD + DAQDS_wSize); + ++nbanks; + + /* Then advance the tail */ + DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size); + isa_writew(DAPQ_tail, dev.DAPQ + JQS_wTail); + /* Tell the DSP to play the bank */ + msnd_send_dsp_cmd(&dev, HDEX_PLAY_START); + } + return nbanks; +} + +static int dsp_read(char *buf, size_t len) +{ + int count = len; + + while (count > 0) { + int n; + unsigned long flags; + + /* Critical section: protect fifo in non-interrupt */ + spin_lock_irqsave(&dev.lock, flags); + if ((n = msnd_fifo_read(&dev.DARF, buf, count, 1)) < 0) { + printk(KERN_WARNING LOGNAME ": FIFO read error\n"); + spin_unlock_irqrestore(&dev.lock, flags); + return n; + } + spin_unlock_irqrestore(&dev.lock, flags); + buf += n; + count -= n; + + if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) { + dev.last_recbank = -1; + if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0) + set_bit(F_READING, &dev.flags); + } + + if (dev.rec_ndelay) + return count == len ? -EAGAIN : len - count; + + if (count > 0) { + set_bit(F_READBLOCK, &dev.flags); + if (!interruptible_sleep_on_timeout( + &dev.readblock, + get_rec_delay_jiffies(DAR_BUFF_SIZE))) + clear_bit(F_READING, &dev.flags); + clear_bit(F_READBLOCK, &dev.flags); + if (signal_pending(current)) + return -EINTR; + } + } + + return len - count; +} + +static int dsp_write(const char *buf, size_t len) +{ + int count = len; + + while (count > 0) { + int n; + unsigned long flags; + + /* Critical section: protect fifo in non-interrupt */ + spin_lock_irqsave(&dev.lock, flags); + if ((n = msnd_fifo_write(&dev.DAPF, buf, count, 1)) < 0) { + printk(KERN_WARNING LOGNAME ": FIFO write error\n"); + spin_unlock_irqrestore(&dev.lock, flags); + return n; + } + spin_unlock_irqrestore(&dev.lock, flags); + buf += n; + count -= n; + + if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) { + dev.last_playbank = -1; + if (pack_DAPF_to_DAPQ(1) > 0) + set_bit(F_WRITING, &dev.flags); + } + + if (dev.play_ndelay) + return count == len ? -EAGAIN : len - count; + + if (count > 0) { + set_bit(F_WRITEBLOCK, &dev.flags); + interruptible_sleep_on_timeout( + &dev.writeblock, + get_play_delay_jiffies(DAP_BUFF_SIZE)); + clear_bit(F_WRITEBLOCK, &dev.flags); + if (signal_pending(current)) + return -EINTR; + } + } + + return len - count; +} + +static ssize_t dev_read(struct file *file, char *buf, size_t count, loff_t *off) +{ + int minor = MINOR(file->f_dentry->d_inode->i_rdev); + if (minor == dev.dsp_minor) + return dsp_read(buf, count); + else + return -EINVAL; +} + +static ssize_t dev_write(struct file *file, const char *buf, size_t count, loff_t *off) +{ + int minor = MINOR(file->f_dentry->d_inode->i_rdev); + if (minor == dev.dsp_minor) + return dsp_write(buf, count); + else + return -EINVAL; +} + +static __inline__ void eval_dsp_msg(register WORD wMessage) +{ + switch (HIBYTE(wMessage)) { + case HIMT_PLAY_DONE: + if (dev.last_playbank == LOBYTE(wMessage) || !test_bit(F_WRITING, &dev.flags)) + break; + dev.last_playbank = LOBYTE(wMessage); + + if (pack_DAPF_to_DAPQ(0) <= 0) { + if (!test_bit(F_WRITEBLOCK, &dev.flags)) { + if (test_and_clear_bit(F_WRITEFLUSH, &dev.flags)) + wake_up_interruptible(&dev.writeflush); + } + clear_bit(F_WRITING, &dev.flags); + } + + if (test_bit(F_WRITEBLOCK, &dev.flags)) + wake_up_interruptible(&dev.writeblock); + break; + + case HIMT_RECORD_DONE: + if (dev.last_recbank == LOBYTE(wMessage)) + break; + dev.last_recbank = LOBYTE(wMessage); + + pack_DARQ_to_DARF(dev.last_recbank); + + if (test_bit(F_READBLOCK, &dev.flags)) + wake_up_interruptible(&dev.readblock); + break; + + case HIMT_DSP: + switch (LOBYTE(wMessage)) { +#ifndef MSND_CLASSIC + case HIDSP_PLAY_UNDER: +#endif + case HIDSP_INT_PLAY_UNDER: +/* printk(KERN_DEBUG LOGNAME ": Play underflow\n"); */ + clear_bit(F_WRITING, &dev.flags); + break; + + case HIDSP_INT_RECORD_OVER: +/* printk(KERN_DEBUG LOGNAME ": Record overflow\n"); */ + clear_bit(F_READING, &dev.flags); + break; + + default: +/* printk(KERN_DEBUG LOGNAME ": DSP message %d 0x%02x\n", + LOBYTE(wMessage), LOBYTE(wMessage)); */ + break; + } + break; + + case HIMT_MIDI_IN_UCHAR: + if (dev.midi_in_interrupt) + (*dev.midi_in_interrupt)(&dev); + break; + + default: +/* printk(KERN_DEBUG LOGNAME ": HIMT message %d 0x%02x\n", HIBYTE(wMessage), HIBYTE(wMessage)); */ + break; + } +} + +static void intr(int irq, void *dev_id, struct pt_regs *regs) +{ + /* Send ack to DSP */ + inb(dev.io + HP_RXL); + + /* Evaluate queued DSP messages */ + while (isa_readw(dev.DSPQ + JQS_wTail) != isa_readw(dev.DSPQ + JQS_wHead)) { + register WORD wTmp; + + eval_dsp_msg(isa_readw(dev.pwDSPQData + 2*isa_readw(dev.DSPQ + JQS_wHead))); + + if ((wTmp = isa_readw(dev.DSPQ + JQS_wHead) + 1) > isa_readw(dev.DSPQ + JQS_wSize)) + isa_writew(0, dev.DSPQ + JQS_wHead); + else + isa_writew(wTmp, dev.DSPQ + JQS_wHead); + } +} + +static struct file_operations dev_fileops = { + owner: THIS_MODULE, + read: dev_read, + write: dev_write, + ioctl: dev_ioctl, + open: dev_open, + release: dev_release, +}; + +static int reset_dsp(void) +{ + int timeout = 100; + + outb(HPDSPRESET_ON, dev.io + HP_DSPR); + mdelay(1); +#ifndef MSND_CLASSIC + dev.info = inb(dev.io + HP_INFO); +#endif + outb(HPDSPRESET_OFF, dev.io + HP_DSPR); + mdelay(1); + while (timeout-- > 0) { + if (inb(dev.io + HP_CVR) == HP_CVR_DEF) + return 0; + mdelay(1); + } + printk(KERN_ERR LOGNAME ": Cannot reset DSP\n"); + + return -EIO; +} + +static int __init probe_multisound(void) +{ +#ifndef MSND_CLASSIC + char *xv, *rev = NULL; + char *pin = "Pinnacle", *fiji = "Fiji"; + char *pinfiji = "Pinnacle/Fiji"; +#endif + + if (check_region(dev.io, dev.numio)) { + printk(KERN_ERR LOGNAME ": I/O port conflict\n"); + return -ENODEV; + } + request_region(dev.io, dev.numio, "probing"); + + if (reset_dsp() < 0) { + release_region(dev.io, dev.numio); + return -ENODEV; + } + +#ifdef MSND_CLASSIC + dev.name = "Classic/Tahiti/Monterey"; + printk(KERN_INFO LOGNAME ": %s, " +#else + switch (dev.info >> 4) { + case 0xf: xv = "<= 1.15"; break; + case 0x1: xv = "1.18/1.2"; break; + case 0x2: xv = "1.3"; break; + case 0x3: xv = "1.4"; break; + default: xv = "unknown"; break; + } + + switch (dev.info & 0x7) { + case 0x0: rev = "I"; dev.name = pin; break; + case 0x1: rev = "F"; dev.name = pin; break; + case 0x2: rev = "G"; dev.name = pin; break; + case 0x3: rev = "H"; dev.name = pin; break; + case 0x4: rev = "E"; dev.name = fiji; break; + case 0x5: rev = "C"; dev.name = fiji; break; + case 0x6: rev = "D"; dev.name = fiji; break; + case 0x7: + rev = "A-B (Fiji) or A-E (Pinnacle)"; + dev.name = pinfiji; + break; + } + printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, " +#endif /* MSND_CLASSIC */ + "I/O 0x%x-0x%x, IRQ %d, memory mapped to 0x%lX-0x%lX\n", + dev.name, +#ifndef MSND_CLASSIC + rev, xv, +#endif + dev.io, dev.io + dev.numio - 1, + dev.irq, + dev.base, dev.base + 0x7fff); + + release_region(dev.io, dev.numio); + return 0; +} + +static int init_sma(void) +{ + static int initted; + WORD mastVolLeft, mastVolRight; + unsigned long flags; + +#ifdef MSND_CLASSIC + outb(dev.memid, dev.io + HP_MEMM); +#endif + outb(HPBLKSEL_0, dev.io + HP_BLKS); + if (initted) { + mastVolLeft = isa_readw(dev.SMA + SMA_wCurrMastVolLeft); + mastVolRight = isa_readw(dev.SMA + SMA_wCurrMastVolRight); + } else + mastVolLeft = mastVolRight = 0; + isa_memset_io(dev.base, 0, 0x8000); + + /* Critical section: bank 1 access */ + spin_lock_irqsave(&dev.lock, flags); + outb(HPBLKSEL_1, dev.io + HP_BLKS); + isa_memset_io(dev.base, 0, 0x8000); + outb(HPBLKSEL_0, dev.io + HP_BLKS); + spin_unlock_irqrestore(&dev.lock, flags); + + dev.pwDSPQData = (dev.base + DSPQ_DATA_BUFF); + dev.pwMODQData = (dev.base + MODQ_DATA_BUFF); + dev.pwMIDQData = (dev.base + MIDQ_DATA_BUFF); + + /* Motorola 56k shared memory base */ + dev.SMA = dev.base + SMA_STRUCT_START; + + /* Digital audio play queue */ + dev.DAPQ = dev.base + DAPQ_OFFSET; + msnd_init_queue(dev.DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE); + + /* Digital audio record queue */ + dev.DARQ = dev.base + DARQ_OFFSET; + msnd_init_queue(dev.DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); + + /* MIDI out queue */ + dev.MODQ = dev.base + MODQ_OFFSET; + msnd_init_queue(dev.MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE); + + /* MIDI in queue */ + dev.MIDQ = dev.base + MIDQ_OFFSET; + msnd_init_queue(dev.MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE); + + /* DSP -> host message queue */ + dev.DSPQ = dev.base + DSPQ_OFFSET; + msnd_init_queue(dev.DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE); + + /* Setup some DSP values */ +#ifndef MSND_CLASSIC + isa_writew(1, dev.SMA + SMA_wCurrPlayFormat); + isa_writew(dev.play_sample_size, dev.SMA + SMA_wCurrPlaySampleSize); + isa_writew(dev.play_channels, dev.SMA + SMA_wCurrPlayChannels); + isa_writew(dev.play_sample_rate, dev.SMA + SMA_wCurrPlaySampleRate); +#endif + isa_writew(dev.play_sample_rate, dev.SMA + SMA_wCalFreqAtoD); + isa_writew(mastVolLeft, dev.SMA + SMA_wCurrMastVolLeft); + isa_writew(mastVolRight, dev.SMA + SMA_wCurrMastVolRight); +#ifndef MSND_CLASSIC + isa_writel(0x00010000, dev.SMA + SMA_dwCurrPlayPitch); + isa_writel(0x00000001, dev.SMA + SMA_dwCurrPlayRate); +#endif + isa_writew(0x303, dev.SMA + SMA_wCurrInputTagBits); + + initted = 1; + + return 0; +} + +static int __init calibrate_adc(WORD srate) +{ + isa_writew(srate, dev.SMA + SMA_wCalFreqAtoD); + if (dev.calibrate_signal == 0) + isa_writew(isa_readw(dev.SMA + SMA_wCurrHostStatusFlags) + | 0x0001, dev.SMA + SMA_wCurrHostStatusFlags); + else + isa_writew(isa_readw(dev.SMA + SMA_wCurrHostStatusFlags) + & ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags); + if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 && + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 3); + return 0; + } + printk(KERN_WARNING LOGNAME ": ADC calibration failed\n"); + + return -EIO; +} + +static int upload_dsp_code(void) +{ + outb(HPBLKSEL_0, dev.io + HP_BLKS); +#ifndef HAVE_DSPCODEH + INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE); + if (!INITCODE) { + printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE); + return -EBUSY; + } + + PERMCODESIZE = mod_firmware_load(PERMCODEFILE, &PERMCODE); + if (!PERMCODE) { + printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE); + vfree(INITCODE); + return -EBUSY; + } +#endif + isa_memcpy_toio(dev.base, PERMCODE, PERMCODESIZE); + if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) { + printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n"); + return -ENODEV; + } +#ifdef HAVE_DSPCODEH + printk(KERN_INFO LOGNAME ": DSP firmware uploaded (resident)\n"); +#else + printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n"); +#endif + +#ifndef HAVE_DSPCODEH + vfree(INITCODE); + vfree(PERMCODE); +#endif + + return 0; +} + +#ifdef MSND_CLASSIC +static void reset_proteus(void) +{ + outb(HPPRORESET_ON, dev.io + HP_PROR); + mdelay(TIME_PRO_RESET); + outb(HPPRORESET_OFF, dev.io + HP_PROR); + mdelay(TIME_PRO_RESET_DONE); +} +#endif + +static int initialize(void) +{ + int err, timeout; + +#ifdef MSND_CLASSIC + outb(HPWAITSTATE_0, dev.io + HP_WAIT); + outb(HPBITMODE_16, dev.io + HP_BITM); + + reset_proteus(); +#endif + if ((err = init_sma()) < 0) { + printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n"); + return err; + } + + if ((err = reset_dsp()) < 0) + return err; + + if ((err = upload_dsp_code()) < 0) { + printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n"); + return err; + } + + timeout = 200; + while (isa_readw(dev.base)) { + mdelay(1); + if (!timeout--) { + printk(KERN_DEBUG LOGNAME ": DSP reset timeout\n"); + return -EIO; + } + } + + mixer_setup(); + + return 0; +} + +static int dsp_full_reset(void) +{ + int rv; + + if (test_bit(F_RESETTING, &dev.flags) || ++dev.nresets > 10) + return 0; + + set_bit(F_RESETTING, &dev.flags); + printk(KERN_INFO LOGNAME ": DSP reset\n"); + dsp_halt(NULL); /* Unconditionally halt */ + if ((rv = initialize())) + printk(KERN_WARNING LOGNAME ": DSP reset failed\n"); + force_recsrc(dev.recsrc); + dsp_open(NULL); + clear_bit(F_RESETTING, &dev.flags); + + return rv; +} + +static int __init attach_multisound(void) +{ + int err; + + if ((err = request_irq(dev.irq, intr, 0, dev.name, &dev)) < 0) { + printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq); + return err; + } + request_region(dev.io, dev.numio, dev.name); + + if ((err = dsp_full_reset()) < 0) { + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return err; + } + + if ((err = msnd_register(&dev)) < 0) { + printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n"); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return err; + } + + if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) { + printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n"); + msnd_unregister(&dev); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return dev.dsp_minor; + } + + if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) { + printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n"); + unregister_sound_mixer(dev.mixer_minor); + msnd_unregister(&dev); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return dev.mixer_minor; + } + + dev.ext_midi_dev = dev.hdr_midi_dev = -1; + + disable_irq(dev.irq); + calibrate_adc(dev.play_sample_rate); +#ifndef MSND_CLASSIC + force_recsrc(SOUND_MASK_IMIX); +#endif + + return 0; +} + +static void __exit unload_multisound(void) +{ + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + unregister_sound_mixer(dev.mixer_minor); + unregister_sound_dsp(dev.dsp_minor); + msnd_unregister(&dev); +} + +#ifndef MSND_CLASSIC + +/* Pinnacle/Fiji Logical Device Configuration */ + +static int __init msnd_write_cfg(int cfg, int reg, int value) +{ + outb(reg, cfg); + outb(value, cfg + 1); + if (value != inb(cfg + 1)) { + printk(KERN_ERR LOGNAME ": msnd_write_cfg: I/O error\n"); + return -EIO; + } + return 0; +} + +static int __init msnd_write_cfg_io0(int cfg, int num, WORD io) +{ + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io))) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io))) + return -EIO; + return 0; +} + +static int __init msnd_write_cfg_io1(int cfg, int num, WORD io) +{ + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io))) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io))) + return -EIO; + return 0; +} + +static int __init msnd_write_cfg_irq(int cfg, int num, WORD irq) +{ + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq))) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE)) + return -EIO; + return 0; +} + +static int __init msnd_write_cfg_mem(int cfg, int num, int mem) +{ + WORD wmem; + + mem >>= 8; + mem &= 0xfff; + wmem = (WORD)mem; + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem))) + return -EIO; + if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem))) + return -EIO; + if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT))) + return -EIO; + return 0; +} + +static int __init msnd_activate_logical(int cfg, int num) +{ + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE)) + return -EIO; + return 0; +} + +static int __init msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem) +{ + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg_io0(cfg, num, io0)) + return -EIO; + if (msnd_write_cfg_io1(cfg, num, io1)) + return -EIO; + if (msnd_write_cfg_irq(cfg, num, irq)) + return -EIO; + if (msnd_write_cfg_mem(cfg, num, mem)) + return -EIO; + if (msnd_activate_logical(cfg, num)) + return -EIO; + return 0; +} + +typedef struct msnd_pinnacle_cfg_device { + WORD io0, io1, irq; + int mem; +} msnd_pinnacle_cfg_t[4]; + +static int __init msnd_pinnacle_cfg_devices(int cfg, int reset, msnd_pinnacle_cfg_t device) +{ + int i; + + /* Reset devices if told to */ + if (reset) { + printk(KERN_INFO LOGNAME ": Resetting all devices\n"); + for (i = 0; i < 4; ++i) + if (msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0)) + return -EIO; + } + + /* Configure specified devices */ + for (i = 0; i < 4; ++i) { + + switch (i) { + case 0: /* DSP */ + if (!(device[i].io0 && device[i].irq && device[i].mem)) + continue; + break; + case 1: /* MPU */ + if (!(device[i].io0 && device[i].irq)) + continue; + printk(KERN_INFO LOGNAME + ": Configuring MPU to I/O 0x%x IRQ %d\n", + device[i].io0, device[i].irq); + break; + case 2: /* IDE */ + if (!(device[i].io0 && device[i].io1 && device[i].irq)) + continue; + printk(KERN_INFO LOGNAME + ": Configuring IDE to I/O 0x%x, 0x%x IRQ %d\n", + device[i].io0, device[i].io1, device[i].irq); + break; + case 3: /* Joystick */ + if (!(device[i].io0)) + continue; + printk(KERN_INFO LOGNAME + ": Configuring joystick to I/O 0x%x\n", + device[i].io0); + break; + } + + /* Configure the device */ + if (msnd_write_cfg_logical(cfg, i, device[i].io0, device[i].io1, device[i].irq, device[i].mem)) + return -EIO; + } + + return 0; +} +#endif + +#ifdef MODULE +MODULE_AUTHOR ("Andrew Veliath "); +MODULE_DESCRIPTION ("Turtle Beach " LONGNAME " Linux Driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM (io, "i"); +MODULE_PARM (irq, "i"); +MODULE_PARM (mem, "i"); +MODULE_PARM (write_ndelay, "i"); +MODULE_PARM (fifosize, "i"); +MODULE_PARM (calibrate_signal, "i"); +#ifndef MSND_CLASSIC +MODULE_PARM (digital, "i"); +MODULE_PARM (cfg, "i"); +MODULE_PARM (reset, "i"); +MODULE_PARM (mpu_io, "i"); +MODULE_PARM (mpu_irq, "i"); +MODULE_PARM (ide_io0, "i"); +MODULE_PARM (ide_io1, "i"); +MODULE_PARM (ide_irq, "i"); +MODULE_PARM (joystick_io, "i"); +#endif + +static int io __initdata = -1; +static int irq __initdata = -1; +static int mem __initdata = -1; +static int write_ndelay __initdata = -1; + +#ifndef MSND_CLASSIC +/* Pinnacle/Fiji non-PnP Config Port */ +static int cfg __initdata = -1; + +/* Extra Peripheral Configuration */ +static int reset __initdata = 0; +static int mpu_io __initdata = 0; +static int mpu_irq __initdata = 0; +static int ide_io0 __initdata = 0; +static int ide_io1 __initdata = 0; +static int ide_irq __initdata = 0; +static int joystick_io __initdata = 0; + +/* If we have the digital daugherboard... */ +static int digital __initdata = 0; +#endif + +static int fifosize __initdata = DEFFIFOSIZE; +static int calibrate_signal __initdata = 0; + +#else /* not a module */ + +static int write_ndelay __initdata = -1; + +#ifdef MSND_CLASSIC +static int io __initdata = CONFIG_MSNDCLAS_IO; +static int irq __initdata = CONFIG_MSNDCLAS_IRQ; +static int mem __initdata = CONFIG_MSNDCLAS_MEM; +#else /* Pinnacle/Fiji */ + +static int io __initdata = CONFIG_MSNDPIN_IO; +static int irq __initdata = CONFIG_MSNDPIN_IRQ; +static int mem __initdata = CONFIG_MSNDPIN_MEM; + +/* Pinnacle/Fiji non-PnP Config Port */ +#ifdef CONFIG_MSNDPIN_NONPNP +# ifndef CONFIG_MSNDPIN_CFG +# define CONFIG_MSNDPIN_CFG 0x250 +# endif +#else +# ifdef CONFIG_MSNDPIN_CFG +# undef CONFIG_MSNDPIN_CFG +# endif +# define CONFIG_MSNDPIN_CFG -1 +#endif +static int cfg __initdata = CONFIG_MSNDPIN_CFG; +/* If not a module, we don't need to bother with reset=1 */ +static int reset; + +/* Extra Peripheral Configuration (Default: Disable) */ +#ifndef CONFIG_MSNDPIN_MPU_IO +# define CONFIG_MSNDPIN_MPU_IO 0 +#endif +static int mpu_io __initdata = CONFIG_MSNDPIN_MPU_IO; + +#ifndef CONFIG_MSNDPIN_MPU_IRQ +# define CONFIG_MSNDPIN_MPU_IRQ 0 +#endif +static int mpu_irq __initdata = CONFIG_MSNDPIN_MPU_IRQ; + +#ifndef CONFIG_MSNDPIN_IDE_IO0 +# define CONFIG_MSNDPIN_IDE_IO0 0 +#endif +static int ide_io0 __initdata = CONFIG_MSNDPIN_IDE_IO0; + +#ifndef CONFIG_MSNDPIN_IDE_IO1 +# define CONFIG_MSNDPIN_IDE_IO1 0 +#endif +static int ide_io1 __initdata = CONFIG_MSNDPIN_IDE_IO1; + +#ifndef CONFIG_MSNDPIN_IDE_IRQ +# define CONFIG_MSNDPIN_IDE_IRQ 0 +#endif +static int ide_irq __initdata = CONFIG_MSNDPIN_IDE_IRQ; + +#ifndef CONFIG_MSNDPIN_JOYSTICK_IO +# define CONFIG_MSNDPIN_JOYSTICK_IO 0 +#endif +static int joystick_io __initdata = CONFIG_MSNDPIN_JOYSTICK_IO; + +/* Have SPDIF (Digital) Daughterboard */ +#ifndef CONFIG_MSNDPIN_DIGITAL +# define CONFIG_MSNDPIN_DIGITAL 0 +#endif +static int digital __initdata = CONFIG_MSNDPIN_DIGITAL; + +#endif /* MSND_CLASSIC */ + +#ifndef CONFIG_MSND_FIFOSIZE +# define CONFIG_MSND_FIFOSIZE DEFFIFOSIZE +#endif +static int fifosize __initdata = CONFIG_MSND_FIFOSIZE; + +#ifndef CONFIG_MSND_CALSIGNAL +# define CONFIG_MSND_CALSIGNAL 0 +#endif +static int +calibrate_signal __initdata = CONFIG_MSND_CALSIGNAL; +#endif /* MODULE */ + + +static int __init msnd_init(void) +{ + int err; +#ifndef MSND_CLASSIC + static msnd_pinnacle_cfg_t pinnacle_devs; +#endif /* MSND_CLASSIC */ + + printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version " + VERSION ", Copyright (C) 1998 Andrew Veliath\n"); + + if (io == -1 || irq == -1 || mem == -1) + printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n"); + +#ifdef MSND_CLASSIC + if (io == -1 || + !(io == 0x290 || + io == 0x260 || + io == 0x250 || + io == 0x240 || + io == 0x230 || + io == 0x220 || + io == 0x210 || + io == 0x3e0)) { + printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, or 0x3E0\n"); + return -EINVAL; + } +#else + if (io == -1 || + io < 0x100 || + io > 0x3e0 || + (io % 0x10) != 0) { + printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must within the range 0x100 to 0x3E0 and must be evenly divisible by 0x10\n"); + return -EINVAL; + } +#endif /* MSND_CLASSIC */ + + if (irq == -1 || + !(irq == 5 || + irq == 7 || + irq == 9 || + irq == 10 || + irq == 11 || + irq == 12)) { + printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n"); + return -EINVAL; + } + + if (mem == -1 || + !(mem == 0xb0000 || + mem == 0xc8000 || + mem == 0xd0000 || + mem == 0xd8000 || + mem == 0xe0000 || + mem == 0xe8000)) { + printk(KERN_ERR LOGNAME ": \"mem\" - must be set to " + "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); + return -EINVAL; + } + +#ifdef MSND_CLASSIC + switch (irq) { + case 5: dev.irqid = HPIRQ_5; break; + case 7: dev.irqid = HPIRQ_7; break; + case 9: dev.irqid = HPIRQ_9; break; + case 10: dev.irqid = HPIRQ_10; break; + case 11: dev.irqid = HPIRQ_11; break; + case 12: dev.irqid = HPIRQ_12; break; + } + + switch (mem) { + case 0xb0000: dev.memid = HPMEM_B000; break; + case 0xc8000: dev.memid = HPMEM_C800; break; + case 0xd0000: dev.memid = HPMEM_D000; break; + case 0xd8000: dev.memid = HPMEM_D800; break; + case 0xe0000: dev.memid = HPMEM_E000; break; + case 0xe8000: dev.memid = HPMEM_E800; break; + } +#else + if (cfg == -1) { + printk(KERN_INFO LOGNAME ": Assuming PnP mode\n"); + } else if (cfg != 0x250 && cfg != 0x260 && cfg != 0x270) { + printk(KERN_INFO LOGNAME ": Config port must be 0x250, 0x260 or 0x270 (or unspecified for PnP mode)\n"); + return -EINVAL; + } else { + printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%x\n", cfg); + + /* DSP */ + pinnacle_devs[0].io0 = io; + pinnacle_devs[0].irq = irq; + pinnacle_devs[0].mem = mem; + + /* The following are Pinnacle specific */ + + /* MPU */ + pinnacle_devs[1].io0 = mpu_io; + pinnacle_devs[1].irq = mpu_irq; + + /* IDE */ + pinnacle_devs[2].io0 = ide_io0; + pinnacle_devs[2].io1 = ide_io1; + pinnacle_devs[2].irq = ide_irq; + + /* Joystick */ + pinnacle_devs[3].io0 = joystick_io; + + if (check_region(cfg, 2)) { + printk(KERN_ERR LOGNAME ": Config port 0x%x conflict\n", cfg); + return -EIO; + } + + request_region(cfg, 2, "Pinnacle/Fiji Config"); + if (msnd_pinnacle_cfg_devices(cfg, reset, pinnacle_devs)) { + printk(KERN_ERR LOGNAME ": Device configuration error\n"); + release_region(cfg, 2); + return -EIO; + } + release_region(cfg, 2); + } +#endif /* MSND_CLASSIC */ + + if (fifosize < 16) + fifosize = 16; + + if (fifosize > 1024) + fifosize = 1024; + + set_default_audio_parameters(); +#ifdef MSND_CLASSIC + dev.type = msndClassic; +#else + dev.type = msndPinnacle; +#endif + dev.io = io; + dev.numio = DSP_NUMIO; + dev.irq = irq; + dev.base = mem; + dev.fifosize = fifosize * 1024; + dev.calibrate_signal = calibrate_signal ? 1 : 0; + dev.recsrc = 0; + dev.dspq_data_buff = DSPQ_DATA_BUFF; + dev.dspq_buff_size = DSPQ_BUFF_SIZE; + if (write_ndelay == -1) + write_ndelay = CONFIG_MSND_WRITE_NDELAY; + if (write_ndelay) + clear_bit(F_DISABLE_WRITE_NDELAY, &dev.flags); + else + set_bit(F_DISABLE_WRITE_NDELAY, &dev.flags); +#ifndef MSND_CLASSIC + if (digital) + set_bit(F_HAVEDIGITAL, &dev.flags); +#endif + init_waitqueue_head(&dev.writeblock); + init_waitqueue_head(&dev.readblock); + init_waitqueue_head(&dev.writeflush); + msnd_fifo_init(&dev.DAPF); + msnd_fifo_init(&dev.DARF); + spin_lock_init(&dev.lock); + printk(KERN_INFO LOGNAME ": %u byte audio FIFOs (x2)\n", dev.fifosize); + if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) { + printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n"); + return err; + } + + if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) { + printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n"); + msnd_fifo_free(&dev.DAPF); + return err; + } + + if ((err = probe_multisound()) < 0) { + printk(KERN_ERR LOGNAME ": Probe failed\n"); + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + return err; + } + + if ((err = attach_multisound()) < 0) { + printk(KERN_ERR LOGNAME ": Attach failed\n"); + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + return err; + } + + return 0; +} + +static void __exit msdn_cleanup(void) +{ + unload_multisound(); + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); +} + +module_init(msnd_init); +module_exit(msdn_cleanup); diff -Nru a/sound/oss/msnd_pinnacle.h b/sound/oss/msnd_pinnacle.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/msnd_pinnacle.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,249 @@ +/********************************************************************* + * + * msnd_pinnacle.h + * + * Turtle Beach MultiSound Sound Card Driver for Linux + * + * Some parts of this header file were derived from the Turtle Beach + * MultiSound Driver Development Kit. + * + * Copyright (C) 1998 Andrew Veliath + * Copyright (C) 1993 Turtle Beach Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * $Id: msnd_pinnacle.h,v 1.11 1999/03/21 17:36:09 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_PINNACLE_H +#define __MSND_PINNACLE_H + +#include + +#define DSP_NUMIO 0x08 + +#define IREG_LOGDEVICE 0x07 +#define IREG_ACTIVATE 0x30 +#define LD_ACTIVATE 0x01 +#define LD_DISACTIVATE 0x00 +#define IREG_EECONTROL 0x3F +#define IREG_MEMBASEHI 0x40 +#define IREG_MEMBASELO 0x41 +#define IREG_MEMCONTROL 0x42 +#define IREG_MEMRANGEHI 0x43 +#define IREG_MEMRANGELO 0x44 +#define MEMTYPE_8BIT 0x00 +#define MEMTYPE_16BIT 0x02 +#define MEMTYPE_RANGE 0x00 +#define MEMTYPE_HIADDR 0x01 +#define IREG_IO0_BASEHI 0x60 +#define IREG_IO0_BASELO 0x61 +#define IREG_IO1_BASEHI 0x62 +#define IREG_IO1_BASELO 0x63 +#define IREG_IRQ_NUMBER 0x70 +#define IREG_IRQ_TYPE 0x71 +#define IRQTYPE_HIGH 0x02 +#define IRQTYPE_LOW 0x00 +#define IRQTYPE_LEVEL 0x01 +#define IRQTYPE_EDGE 0x00 + +#define HP_DSPR 0x04 +#define HP_BLKS 0x04 + +#define HPDSPRESET_OFF 2 +#define HPDSPRESET_ON 0 + +#define HPBLKSEL_0 2 +#define HPBLKSEL_1 3 + +#define HIMT_DAT_OFF 0x03 + +#define HIDSP_PLAY_UNDER 0x00 +#define HIDSP_INT_PLAY_UNDER 0x01 +#define HIDSP_SSI_TX_UNDER 0x02 +#define HIDSP_RECQ_OVERFLOW 0x08 +#define HIDSP_INT_RECORD_OVER 0x09 +#define HIDSP_SSI_RX_OVERFLOW 0x0a + +#define HIDSP_MIDI_IN_OVER 0x10 + +#define HIDSP_MIDI_FRAME_ERR 0x11 +#define HIDSP_MIDI_PARITY_ERR 0x12 +#define HIDSP_MIDI_OVERRUN_ERR 0x13 + +#define HIDSP_INPUT_CLIPPING 0x20 +#define HIDSP_MIX_CLIPPING 0x30 +#define HIDSP_DAT_IN_OFF 0x21 + +#define HDEXAR_SET_ANA_IN 0 +#define HDEXAR_CLEAR_PEAKS 1 +#define HDEXAR_IN_SET_POTS 2 +#define HDEXAR_AUX_SET_POTS 3 +#define HDEXAR_CAL_A_TO_D 4 +#define HDEXAR_RD_EXT_DSP_BITS 5 + +#define HDEXAR_SET_SYNTH_IN 4 +#define HDEXAR_READ_DAT_IN 5 +#define HDEXAR_MIC_SET_POTS 6 +#define HDEXAR_SET_DAT_IN 7 + +#define HDEXAR_SET_SYNTH_48 8 +#define HDEXAR_SET_SYNTH_44 9 + +#define TIME_PRO_RESET_DONE 0x028A +#define TIME_PRO_SYSEX 0x001E +#define TIME_PRO_RESET 0x0032 + +#define AGND 0x01 +#define SIGNAL 0x02 + +#define EXT_DSP_BIT_DCAL 0x0001 +#define EXT_DSP_BIT_MIDI_CON 0x0002 + +#define BUFFSIZE 0x8000 +#define HOSTQ_SIZE 0x40 + +#define SRAM_CNTL_START 0x7F00 +#define SMA_STRUCT_START 0x7F40 + +#define DAP_BUFF_SIZE 0x2400 +#define DAR_BUFF_SIZE 0x2000 + +#define DAPQ_STRUCT_SIZE 0x10 +#define DARQ_STRUCT_SIZE 0x10 +#define DAPQ_BUFF_SIZE (3 * 0x10) +#define DARQ_BUFF_SIZE (3 * 0x10) +#define MODQ_BUFF_SIZE 0x400 +#define MIDQ_BUFF_SIZE 0x800 +#define DSPQ_BUFF_SIZE 0x5A0 + +#define DAPQ_DATA_BUFF 0x6C00 +#define DARQ_DATA_BUFF 0x6C30 +#define MODQ_DATA_BUFF 0x6C60 +#define MIDQ_DATA_BUFF 0x7060 +#define DSPQ_DATA_BUFF 0x7860 + +#define DAPQ_OFFSET SRAM_CNTL_START +#define DARQ_OFFSET (SRAM_CNTL_START + 0x08) +#define MODQ_OFFSET (SRAM_CNTL_START + 0x10) +#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18) +#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20) + +#define MOP_WAVEHDR 0 +#define MOP_EXTOUT 1 +#define MOP_HWINIT 0xfe +#define MOP_NONE 0xff +#define MOP_MAX 1 + +#define MIP_EXTIN 0 +#define MIP_WAVEHDR 1 +#define MIP_HWINIT 0xfe +#define MIP_MAX 1 + +/* Pinnacle/Fiji SMA Common Data */ +#define SMA_wCurrPlayBytes 0x0000 +#define SMA_wCurrRecordBytes 0x0002 +#define SMA_wCurrPlayVolLeft 0x0004 +#define SMA_wCurrPlayVolRight 0x0006 +#define SMA_wCurrInVolLeft 0x0008 +#define SMA_wCurrInVolRight 0x000a +#define SMA_wCurrMHdrVolLeft 0x000c +#define SMA_wCurrMHdrVolRight 0x000e +#define SMA_dwCurrPlayPitch 0x0010 +#define SMA_dwCurrPlayRate 0x0014 +#define SMA_wCurrMIDIIOPatch 0x0018 +#define SMA_wCurrPlayFormat 0x001a +#define SMA_wCurrPlaySampleSize 0x001c +#define SMA_wCurrPlayChannels 0x001e +#define SMA_wCurrPlaySampleRate 0x0020 +#define SMA_wCurrRecordFormat 0x0022 +#define SMA_wCurrRecordSampleSize 0x0024 +#define SMA_wCurrRecordChannels 0x0026 +#define SMA_wCurrRecordSampleRate 0x0028 +#define SMA_wCurrDSPStatusFlags 0x002a +#define SMA_wCurrHostStatusFlags 0x002c +#define SMA_wCurrInputTagBits 0x002e +#define SMA_wCurrLeftPeak 0x0030 +#define SMA_wCurrRightPeak 0x0032 +#define SMA_bMicPotPosLeft 0x0034 +#define SMA_bMicPotPosRight 0x0035 +#define SMA_bMicPotMaxLeft 0x0036 +#define SMA_bMicPotMaxRight 0x0037 +#define SMA_bInPotPosLeft 0x0038 +#define SMA_bInPotPosRight 0x0039 +#define SMA_bAuxPotPosLeft 0x003a +#define SMA_bAuxPotPosRight 0x003b +#define SMA_bInPotMaxLeft 0x003c +#define SMA_bInPotMaxRight 0x003d +#define SMA_bAuxPotMaxLeft 0x003e +#define SMA_bAuxPotMaxRight 0x003f +#define SMA_bInPotMaxMethod 0x0040 +#define SMA_bAuxPotMaxMethod 0x0041 +#define SMA_wCurrMastVolLeft 0x0042 +#define SMA_wCurrMastVolRight 0x0044 +#define SMA_wCalFreqAtoD 0x0046 +#define SMA_wCurrAuxVolLeft 0x0048 +#define SMA_wCurrAuxVolRight 0x004a +#define SMA_wCurrPlay1VolLeft 0x004c +#define SMA_wCurrPlay1VolRight 0x004e +#define SMA_wCurrPlay2VolLeft 0x0050 +#define SMA_wCurrPlay2VolRight 0x0052 +#define SMA_wCurrPlay3VolLeft 0x0054 +#define SMA_wCurrPlay3VolRight 0x0056 +#define SMA_wCurrPlay4VolLeft 0x0058 +#define SMA_wCurrPlay4VolRight 0x005a +#define SMA_wCurrPlay1PeakLeft 0x005c +#define SMA_wCurrPlay1PeakRight 0x005e +#define SMA_wCurrPlay2PeakLeft 0x0060 +#define SMA_wCurrPlay2PeakRight 0x0062 +#define SMA_wCurrPlay3PeakLeft 0x0064 +#define SMA_wCurrPlay3PeakRight 0x0066 +#define SMA_wCurrPlay4PeakLeft 0x0068 +#define SMA_wCurrPlay4PeakRight 0x006a +#define SMA_wCurrPlayPeakLeft 0x006c +#define SMA_wCurrPlayPeakRight 0x006e +#define SMA_wCurrDATSR 0x0070 +#define SMA_wCurrDATRXCHNL 0x0072 +#define SMA_wCurrDATTXCHNL 0x0074 +#define SMA_wCurrDATRXRate 0x0076 +#define SMA_dwDSPPlayCount 0x0078 +#define SMA__size 0x007c + +#ifdef HAVE_DSPCODEH +# include "pndsperm.c" +# include "pndspini.c" +# define PERMCODE pndsperm +# define INITCODE pndspini +# define PERMCODESIZE sizeof(pndsperm) +# define INITCODESIZE sizeof(pndspini) +#else +# ifndef CONFIG_MSNDPIN_INIT_FILE +# define CONFIG_MSNDPIN_INIT_FILE \ + "/etc/sound/pndspini.bin" +# endif +# ifndef CONFIG_MSNDPIN_PERM_FILE +# define CONFIG_MSNDPIN_PERM_FILE \ + "/etc/sound/pndsperm.bin" +# endif +# define PERMCODEFILE CONFIG_MSNDPIN_PERM_FILE +# define INITCODEFILE CONFIG_MSNDPIN_INIT_FILE +# define PERMCODE dspini +# define INITCODE permini +# define PERMCODESIZE sizeof_dspini +# define INITCODESIZE sizeof_permini +#endif +#define LONGNAME "MultiSound (Pinnacle/Fiji)" + +#endif /* __MSND_PINNACLE_H */ diff -Nru a/sound/oss/nec_vrc5477.c b/sound/oss/nec_vrc5477.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/nec_vrc5477.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,2039 @@ +/*********************************************************************** + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * drivers/sound/nec_vrc5477.c + * AC97 sound dirver for NEC Vrc5477 chip (an integrated, + * multi-function controller chip for MIPS CPUs) + * + * 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 code is derived from ite8172.c, which is written by Steve Longerbeam. + * + * Features: + * Currently we only support the following capabilities: + * . mono output to PCM L/R (line out). + * . stereo output to PCM L/R (line out). + * . mono input from PCM L (line in). + * . stereo output from PCM (line in). + * . sampling rate at 48k or variable sampling rate + * . support /dev/dsp, /dev/mixer devices, standard OSS devices. + * . only support 16-bit PCM format (hardware limit, no software + * translation) + * . support duplex, but no trigger or realtime. + * + * Specifically the following are not supported: + * . app-set frag size. + * . mmap'ed buffer access + */ + +/* + * Original comments from ite8172.c file. + */ + +/* + * + * 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. + * 2. The following support is untested: + * * Memory mapping the audio buffers, and the ioctl controls that go + * with it. + * * S/PDIF output. + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#undef VRC5477_AC97_VERBOSE_DEBUG + +/* one must turn on CONFIG_LL_DEBUG before VERBOSE_DEBUG is turned */ +#if defined(VRC5477_AC97_VERBOSE_DEBUG) +#if !defined(CONFIG_LL_DEBUG) +#error "You must turn CONFIG_LL_DEBUG" +#endif +#endif + +#if defined(VRC5477_AC97_VERBOSE_DEBUG) +static u16 inTicket=0; /* check sync between intr & write */ +static u16 outTicket=0; +#endif + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define VRC5477_INT_CLR 0x0 +#define VRC5477_INT_STATUS 0x0 +#define VRC5477_CODEC_WR 0x4 +#define VRC5477_CODEC_RD 0x8 +#define VRC5477_CTRL 0x18 +#define VRC5477_ACLINK_CTRL 0x1c +#define VRC5477_INT_MASK 0x24 + +#define VRC5477_DAC1_CTRL 0x30 +#define VRC5477_DAC1L 0x34 +#define VRC5477_DAC1_BADDR 0x38 +#define VRC5477_DAC2_CTRL 0x3c +#define VRC5477_DAC2L 0x40 +#define VRC5477_DAC2_BADDR 0x44 +#define VRC5477_DAC3_CTRL 0x48 +#define VRC5477_DAC3L 0x4c +#define VRC5477_DAC3_BADDR 0x50 + +#define VRC5477_ADC1_CTRL 0x54 +#define VRC5477_ADC1L 0x58 +#define VRC5477_ADC1_BADDR 0x5c +#define VRC5477_ADC2_CTRL 0x60 +#define VRC5477_ADC2L 0x64 +#define VRC5477_ADC2_BADDR 0x68 +#define VRC5477_ADC3_CTRL 0x6c +#define VRC5477_ADC3L 0x70 +#define VRC5477_ADC3_BADDR 0x74 + +#define VRC5477_CODEC_WR_RWC (1 << 23) + +#define VRC5477_CODEC_RD_RRDYA (1 << 31) +#define VRC5477_CODEC_RD_RRDYD (1 << 30) + +#define VRC5477_ACLINK_CTRL_RST_ON (1 << 15) +#define VRC5477_ACLINK_CTRL_RST_TIME 0x7f +#define VRC5477_ACLINK_CTRL_SYNC_ON (1 << 30) +#define VRC5477_ACLINK_CTRL_CK_STOP_ON (1 << 31) + +#define VRC5477_CTRL_DAC2ENB (1 << 15) +#define VRC5477_CTRL_ADC2ENB (1 << 14) +#define VRC5477_CTRL_DAC1ENB (1 << 13) +#define VRC5477_CTRL_ADC1ENB (1 << 12) + +#define VRC5477_INT_MASK_NMASK (1 << 31) +#define VRC5477_INT_MASK_DAC1END (1 << 5) +#define VRC5477_INT_MASK_DAC2END (1 << 4) +#define VRC5477_INT_MASK_DAC3END (1 << 3) +#define VRC5477_INT_MASK_ADC1END (1 << 2) +#define VRC5477_INT_MASK_ADC2END (1 << 1) +#define VRC5477_INT_MASK_ADC3END (1 << 0) + +#define VRC5477_DMA_ACTIVATION (1 << 31) +#define VRC5477_DMA_WIP (1 << 30) + + +#define VRC5477_AC97_MODULE_NAME "NEC_Vrc5477_audio" +#define PFX VRC5477_AC97_MODULE_NAME ": " + +/* --------------------------------------------------------------------- */ + +struct vrc5477_ac97_state { + /* list of vrc5477_ac97 devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + + /* hardware resources */ + unsigned long io; + unsigned int irq; + +#ifdef CONFIG_LL_DEBUG + /* debug /proc entry */ + struct proc_dir_entry *ps; + struct proc_dir_entry *ac97_ps; +#endif /* CONFIG_LL_DEBUG */ + + struct ac97_codec codec; + + unsigned dacChannels, adcChannels; + unsigned short dacRate, adcRate; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *lbuf, *rbuf; + dma_addr_t lbufDma, rbufDma; + unsigned bufOrder; + unsigned numFrag; + unsigned fragShift; + unsigned fragSize; /* redundant */ + unsigned fragTotalSize; /* = numFrag * fragSize(real) */ + unsigned nextIn; + unsigned nextOut; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* OSS stuff */ + unsigned stopped:1; + unsigned ready:1; + } dma_dac, dma_adc; + + #define WORK_BUF_SIZE 2048 + struct { + u16 lchannel; + u16 rchannel; + } workBuf[WORK_BUF_SIZE/4]; +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); + +/* --------------------------------------------------------------------- */ + +extern 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; +} + +/* --------------------------------------------------------------------- */ + +static u16 rdcodec(struct ac97_codec *codec, u8 addr) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)codec->private_data; + unsigned long flags; + u32 result; + + spin_lock_irqsave(&s->lock, flags); + + /* wait until we can access codec registers */ + while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); + + /* write the address and "read" command to codec */ + addr = addr & 0x7f; + outl((addr << 16) | VRC5477_CODEC_WR_RWC, s->io + VRC5477_CODEC_WR); + + /* get the return result */ + udelay(100); /* workaround hardware bug */ + while ( (result = inl(s->io + VRC5477_CODEC_RD)) & + (VRC5477_CODEC_RD_RRDYA | VRC5477_CODEC_RD_RRDYD) ) { + /* we get either addr or data, or both */ + if (result & VRC5477_CODEC_RD_RRDYA) { + MIPS_ASSERT(addr == ((result >> 16) & 0x7f) ); + } + if (result & VRC5477_CODEC_RD_RRDYD) { + break; + } + } + + spin_unlock_irqrestore(&s->lock, flags); + + return result & 0xffff;; +} + + +static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)codec->private_data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + + /* wait until we can access codec registers */ + while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); + + /* write the address and value to codec */ + outl((addr << 16) | data, s->io + VRC5477_CODEC_WR); + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void waitcodec(struct ac97_codec *codec) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)codec->private_data; + + /* wait until we can access codec registers */ + while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); +} + + +/* --------------------------------------------------------------------- */ + +static void vrc5477_ac97_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 void set_adc_rate(struct vrc5477_ac97_state *s, unsigned rate) +{ + wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, rate); + s->adcRate = rate; +} + + +static void set_dac_rate(struct vrc5477_ac97_state *s, unsigned rate) +{ + wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, rate); + s->dacRate = rate; +} + + +/* --------------------------------------------------------------------- */ + +extern inline void +stop_dac(struct vrc5477_ac97_state *s) +{ + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + u32 temp; + + spin_lock_irqsave(&s->lock, flags); + + if (db->stopped) { + spin_unlock_irqrestore(&s->lock, flags); + return; + } + + /* deactivate the dma */ + outl(0, s->io + VRC5477_DAC1_CTRL); + outl(0, s->io + VRC5477_DAC2_CTRL); + + /* wait for DAM completely stop */ + while (inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); + while (inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); + + /* disable dac slots in aclink */ + temp = inl(s->io + VRC5477_CTRL); + temp &= ~ (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB); + outl (temp, s->io + VRC5477_CTRL); + + /* disable interrupts */ + temp = inl(s->io + VRC5477_INT_MASK); + temp &= ~ (VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END); + outl (temp, s->io + VRC5477_INT_MASK); + + /* clear pending ones */ + outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, + s->io + VRC5477_INT_CLR); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct vrc5477_ac97_state *s) +{ + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + u32 dmaLength; + u32 temp; + + spin_lock_irqsave(&s->lock, flags); + + if (!db->stopped) { + spin_unlock_irqrestore(&s->lock, flags); + return; + } + + /* we should have some data to do the DMA trasnfer */ + MIPS_ASSERT(db->count >= db->fragSize); + + /* clear pending fales interrupts */ + outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, + s->io + VRC5477_INT_CLR); + + /* enable interrupts */ + temp = inl(s->io + VRC5477_INT_MASK); + temp |= VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END; + outl(temp, s->io + VRC5477_INT_MASK); + + /* setup dma base addr */ + outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC1_BADDR); + if (s->dacChannels == 1) { + outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR); + } else { + outl(db->rbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR); + } + + /* set dma length, in the unit of 0x10 bytes */ + dmaLength = db->fragSize >> 4; + outl(dmaLength, s->io + VRC5477_DAC1L); + outl(dmaLength, s->io + VRC5477_DAC2L); + + /* activate dma */ + outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC1_CTRL); + outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC2_CTRL); + + /* enable dac slots - we should hear the music now! */ + temp = inl(s->io + VRC5477_CTRL); + temp |= (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB); + outl (temp, s->io + VRC5477_CTRL); + + /* it is time to setup next dma transfer */ + MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); + MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); + + temp = db->nextOut + db->fragSize; + if (temp >= db->fragTotalSize) { + MIPS_ASSERT(temp == db->fragTotalSize); + temp = 0; + } + + outl(db->lbufDma + temp, s->io + VRC5477_DAC1_BADDR); + if (s->dacChannels == 1) { + outl(db->lbufDma + temp, s->io + VRC5477_DAC2_BADDR); + } else { + outl(db->rbufDma + temp, s->io + VRC5477_DAC2_BADDR); + } + + db->stopped = 0; + +#if defined(VRC5477_AC97_VERBOSE_DEBUG) + outTicket = *(u16*)(db->lbuf+db->nextOut); + if (db->count > db->fragSize) { + MIPS_ASSERT((u16)(outTicket+1) == *(u16*)(db->lbuf+temp)); + } +#endif + + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_adc(struct vrc5477_ac97_state *s) +{ + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + u32 temp; + + spin_lock_irqsave(&s->lock, flags); + + if (db->stopped) { + spin_unlock_irqrestore(&s->lock, flags); + return; + } + + /* deactivate the dma */ + outl(0, s->io + VRC5477_ADC1_CTRL); + outl(0, s->io + VRC5477_ADC2_CTRL); + + /* disable adc slots in aclink */ + temp = inl(s->io + VRC5477_CTRL); + temp &= ~ (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB); + outl (temp, s->io + VRC5477_CTRL); + + /* disable interrupts */ + temp = inl(s->io + VRC5477_INT_MASK); + temp &= ~ (VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END); + outl (temp, s->io + VRC5477_INT_MASK); + + /* clear pending ones */ + outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, + s->io + VRC5477_INT_CLR); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct vrc5477_ac97_state *s) +{ + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + u32 dmaLength; + u32 temp; + + spin_lock_irqsave(&s->lock, flags); + + if (!db->stopped) { + spin_unlock_irqrestore(&s->lock, flags); + return; + } + + /* we should at least have some free space in the buffer */ + MIPS_ASSERT(db->count < db->fragTotalSize - db->fragSize * 2); + + /* clear pending ones */ + outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, + s->io + VRC5477_INT_CLR); + + /* enable interrupts */ + temp = inl(s->io + VRC5477_INT_MASK); + temp |= VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END; + outl(temp, s->io + VRC5477_INT_MASK); + + /* setup dma base addr */ + outl(db->lbufDma + db->nextIn, s->io + VRC5477_ADC1_BADDR); + outl(db->rbufDma + db->nextIn, s->io + VRC5477_ADC2_BADDR); + + /* setup dma length */ + dmaLength = db->fragSize >> 4; + outl(dmaLength, s->io + VRC5477_ADC1L); + outl(dmaLength, s->io + VRC5477_ADC2L); + + /* activate dma */ + outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_ADC1_CTRL); + outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_ADC2_CTRL); + + /* enable adc slots */ + temp = inl(s->io + VRC5477_CTRL); + temp |= (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB); + outl (temp, s->io + VRC5477_CTRL); + + /* it is time to setup next dma transfer */ + temp = db->nextIn + db->fragSize; + if (temp >= db->fragTotalSize) { + MIPS_ASSERT(temp == db->fragTotalSize); + temp = 0; + } + outl(db->lbufDma + temp, s->io + VRC5477_ADC1_BADDR); + outl(db->rbufDma + temp, s->io + VRC5477_ADC2_BADDR); + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +extern inline void dealloc_dmabuf(struct vrc5477_ac97_state *s, + struct dmabuf *db) +{ + if (db->lbuf) { + MIPS_ASSERT(db->rbuf); + pci_free_consistent(s->dev, PAGE_SIZE << db->bufOrder, + db->lbuf, db->lbufDma); + pci_free_consistent(s->dev, PAGE_SIZE << db->bufOrder, + db->rbuf, db->rbufDma); + db->lbuf = db->rbuf = NULL; + } + db->nextIn = db->nextOut = 0; + db->ready = 0; +} + +static int prog_dmabuf(struct vrc5477_ac97_state *s, + struct dmabuf *db, + unsigned rate) +{ + int order; + unsigned bufsize; + + if (!db->lbuf) { + MIPS_ASSERT(!db->rbuf); + + db->ready = 0; + for (order = DMABUF_DEFAULTORDER; + order >= DMABUF_MINORDER; + order--) { + db->lbuf = pci_alloc_consistent(s->dev, + PAGE_SIZE << order, + &db->lbufDma); + db->rbuf = pci_alloc_consistent(s->dev, + PAGE_SIZE << order, + &db->rbufDma); + if (db->lbuf && db->rbuf) break; + if (db->lbuf) { + MIPS_ASSERT(!db->rbuf); + pci_free_consistent(s->dev, + PAGE_SIZE << order, + db->lbuf, + db->lbufDma); + } + } + if (!db->lbuf) { + MIPS_ASSERT(!db->rbuf); + return -ENOMEM; + } + + db->bufOrder = order; + } + + db->count = 0; + db->nextIn = db->nextOut = 0; + + bufsize = PAGE_SIZE << db->bufOrder; + db->fragShift = ld2(rate * 2 / 100); + if (db->fragShift < 4) db->fragShift = 4; + + db->numFrag = bufsize >> db->fragShift; + while (db->numFrag < 4 && db->fragShift > 4) { + db->fragShift--; + db->numFrag = bufsize >> db->fragShift; + } + db->fragSize = 1 << db->fragShift; + db->fragTotalSize = db->numFrag << db->fragShift; + memset(db->lbuf, 0, db->fragTotalSize); + memset(db->rbuf, 0, db->fragTotalSize); + + db->ready = 1; + + return 0; +} + +extern inline int prog_dmabuf_adc(struct vrc5477_ac97_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, s->adcRate); +} + +extern inline int prog_dmabuf_dac(struct vrc5477_ac97_state *s) +{ + stop_dac(s); + return prog_dmabuf(s, &s->dma_dac, s->dacRate); +} + + +/* --------------------------------------------------------------------- */ +/* hold spinlock for the following! */ + +static inline void vrc5477_ac97_adc_interrupt(struct vrc5477_ac97_state *s) +{ + struct dmabuf* adc = &s->dma_adc; + unsigned temp; + + /* we need two frags avaiable because one is already being used + * and the other will be used when next interrupt happens. + */ + if (adc->count >= adc->fragTotalSize - adc->fragSize) { + stop_adc(s); + adc->error++; + printk(KERN_INFO PFX "adc overrun\n"); + return; + } + + /* set the base addr for next DMA transfer */ + temp = adc->nextIn + 2*adc->fragSize; + if (temp >= adc->fragTotalSize) { + MIPS_ASSERT( (temp == adc->fragTotalSize) || + (temp == adc->fragTotalSize + adc->fragSize) ); + temp -= adc->fragTotalSize; + } + outl(adc->lbufDma + temp, s->io + VRC5477_ADC1_BADDR); + outl(adc->rbufDma + temp, s->io + VRC5477_ADC2_BADDR); + + /* adjust nextIn */ + adc->nextIn += adc->fragSize; + if (adc->nextIn >= adc->fragTotalSize) { + MIPS_ASSERT(adc->nextIn == adc->fragTotalSize); + adc->nextIn = 0; + } + + /* adjust count */ + adc->count += adc->fragSize; + + /* wake up anybody listening */ + if (waitqueue_active(&adc->wait)) { + wake_up_interruptible(&adc->wait); + } +} + +static inline void vrc5477_ac97_dac_interrupt(struct vrc5477_ac97_state *s) +{ + struct dmabuf* dac = &s->dma_dac; + unsigned temp; + + /* next DMA transfer should already started */ + MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); + MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); + + /* let us set for next next DMA transfer */ + temp = dac->nextOut + dac->fragSize*2; + if (temp >= dac->fragTotalSize) { + MIPS_ASSERT( (temp == dac->fragTotalSize) || + (temp == dac->fragTotalSize + dac->fragSize) ); + temp -= dac->fragTotalSize; + } + outl(dac->lbufDma + temp, s->io + VRC5477_DAC1_BADDR); + if (s->dacChannels == 1) { + outl(dac->lbufDma + temp, s->io + VRC5477_DAC2_BADDR); + } else { + outl(dac->rbufDma + temp, s->io + VRC5477_DAC2_BADDR); + } + +#if defined(VRC5477_AC97_VERBOSE_DEBUG) + if (*(u16*)(dac->lbuf + dac->nextOut) != outTicket) { + printk("assert fail: - %d vs %d\n", + *(u16*)(dac->lbuf + dac->nextOut), + outTicket); + MIPS_ASSERT(1 == 0); + } +#endif + + /* adjust nextOut pointer */ + dac->nextOut += dac->fragSize; + if (dac->nextOut >= dac->fragTotalSize) { + MIPS_ASSERT(dac->nextOut == dac->fragTotalSize); + dac->nextOut = 0; + } + + /* adjust count */ + dac->count -= dac->fragSize; + if (dac->count <=0 ) { + MIPS_ASSERT(dac->count == 0); + MIPS_ASSERT(dac->nextIn == dac->nextOut); + /* buffer under run */ + stop_dac(s); + } + +#if defined(VRC5477_AC97_VERBOSE_DEBUG) + if (dac->count) { + outTicket ++; + MIPS_ASSERT(*(u16*)(dac->lbuf + dac->nextOut) == outTicket); + } +#endif + + /* we cannot have both under run and someone is waiting on us */ + MIPS_ASSERT(! (waitqueue_active(&dac->wait) && (dac->count <= 0)) ); + + /* wake up anybody listening */ + if (waitqueue_active(&dac->wait)) + wake_up_interruptible(&dac->wait); +} + +static void vrc5477_ac97_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)dev_id; + u32 irqStatus; + u32 adcInterrupts, dacInterrupts; + + spin_lock(&s->lock); + + /* get irqStatus and clear the detected ones */ + irqStatus = inl(s->io + VRC5477_INT_STATUS); + outl(irqStatus, s->io + VRC5477_INT_CLR); + + /* let us see what we get */ + dacInterrupts = VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END; + adcInterrupts = VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END; + if (irqStatus & dacInterrupts) { + /* we should get both interrupts, but just in case ... */ + if (irqStatus & VRC5477_INT_MASK_DAC1END) { + vrc5477_ac97_dac_interrupt(s); + } + if ( (irqStatus & dacInterrupts) != dacInterrupts ) { + printk(KERN_WARNING "vrc5477_ac97 : dac interrupts not in sync!!!\n"); + stop_dac(s); + start_dac(s); + } + } else if (irqStatus & adcInterrupts) { + /* we should get both interrupts, but just in case ... */ + if(irqStatus & VRC5477_INT_MASK_ADC1END) { + vrc5477_ac97_adc_interrupt(s); + } + if ( (irqStatus & adcInterrupts) != adcInterrupts ) { + printk(KERN_WARNING "vrc5477_ac97 : adc interrupts not in sync!!!\n"); + stop_adc(s); + start_adc(s); + } + } + + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static int vrc5477_ac97_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *list; + struct vrc5477_ac97_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct vrc5477_ac97_state, devs); + if (s->codec.dev_mixer == minor) + break; + } + file->private_data = s; + return 0; +} + +static int vrc5477_ac97_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 vrc5477_ac97_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)file->private_data; + struct ac97_codec *codec = &s->codec; + + return mixdev_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations vrc5477_ac97_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: vrc5477_ac97_ioctl_mixdev, + open: vrc5477_ac97_open_mixdev, + release: vrc5477_ac97_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct vrc5477_ac97_state *s, int nonblock) +{ + unsigned long flags; + int count, tmo; + + if (!s->dma_dac.ready) + 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->dacRate / 2; + vrc5477_ac97_delay(tmo); + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int inline +copy_two_channel_adc_to_user(struct vrc5477_ac97_state *s, + char *buffer, + int copyCount) +{ + struct dmabuf *db = &s->dma_adc; + int bufStart = db->nextOut; + for (; copyCount > 0; ) { + int i; + int count = copyCount; + if (count > WORK_BUF_SIZE/2) count = WORK_BUF_SIZE/2; + for (i=0; i< count/2; i++) { + s->workBuf[i].lchannel = + *(u16*)(db->lbuf + bufStart + i*2); + s->workBuf[i].rchannel = + *(u16*)(db->rbuf + bufStart + i*2); + } + if (copy_to_user(buffer, s->workBuf, count*2)) { + return -1; + } + + copyCount -= count; + bufStart += count; + MIPS_ASSERT(bufStart <= db->fragTotalSize); + buffer += count *2; + } + return 0; +} + +/* return the total bytes that is copied */ +static int inline +copy_adc_to_user(struct vrc5477_ac97_state *s, + char * buffer, + size_t count, + int avail) +{ + struct dmabuf *db = &s->dma_adc; + int copyCount=0; + int copyFragCount=0; + int totalCopyCount = 0; + int totalCopyFragCount = 0; + unsigned long flags; + + /* adjust count to signel channel byte count */ + count >>= s->adcChannels - 1; + + /* we may have to "copy" twice as ring buffer wraps around */ + for (; (avail > 0) && (count > 0); ) { + /* determine max possible copy count for single channel */ + copyCount = count; + if (copyCount > avail) { + copyCount = avail; + } + if (copyCount + db->nextOut > db->fragTotalSize) { + copyCount = db->fragTotalSize - db->nextOut; + MIPS_ASSERT((copyCount % db->fragSize) == 0); + } + + copyFragCount = (copyCount-1) >> db->fragShift; + copyFragCount = (copyFragCount+1) << db->fragShift; + MIPS_ASSERT(copyFragCount >= copyCount); + + /* we copy differently based on adc channels */ + if (s->adcChannels == 1) { + if (copy_to_user(buffer, + db->lbuf + db->nextOut, + copyCount)) + return -1; + } else { + /* *sigh* we have to mix two streams into one */ + if (copy_two_channel_adc_to_user(s, buffer, copyCount)) + return -1; + } + + count -= copyCount; + totalCopyCount += copyCount; + avail -= copyFragCount; + totalCopyFragCount += copyFragCount; + + buffer += copyCount << (s->adcChannels-1); + + db->nextOut += copyFragCount; + if (db->nextOut >= db->fragTotalSize) { + MIPS_ASSERT(db->nextOut == db->fragTotalSize); + db->nextOut = 0; + } + + MIPS_ASSERT((copyFragCount % db->fragSize) == 0); + MIPS_ASSERT( (count == 0) || (copyCount == copyFragCount)); + } + + spin_lock_irqsave(&s->lock, flags); + db->count -= totalCopyFragCount; + spin_unlock_irqrestore(&s->lock, flags); + + return totalCopyCount << (s->adcChannels-1); +} + +static ssize_t +vrc5477_ac97_read(struct file *file, + char *buffer, + size_t count, + loff_t *ppos) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)file->private_data; + struct dmabuf *db = &s->dma_adc; + ssize_t ret = 0; + unsigned long flags; + int copyCount; + size_t avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + MIPS_ASSERT(db->ready); + + 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); + + MIPS_ASSERT( (avail % db->fragSize) == 0); + copyCount = copy_adc_to_user(s, buffer, count, avail); + if (copyCount <=0 ) { + if (!ret) ret = -EFAULT; + return ret; + } + + count -= copyCount; + buffer += copyCount; + ret += copyCount; + } // while (count > 0) + + return ret; +} + +static int inline +copy_two_channel_dac_from_user(struct vrc5477_ac97_state *s, + const char *buffer, + int copyCount) +{ + struct dmabuf *db = &s->dma_dac; + int bufStart = db->nextIn; + + MIPS_ASSERT(db->ready); + + for (; copyCount > 0; ) { + int i; + int count = copyCount; + if (count > WORK_BUF_SIZE/2) count = WORK_BUF_SIZE/2; + if (copy_from_user(s->workBuf, buffer, count*2)) { + return -1; + } + for (i=0; i< count/2; i++) { + *(u16*)(db->lbuf + bufStart + i*2) = + s->workBuf[i].lchannel; + *(u16*)(db->rbuf + bufStart + i*2) = + s->workBuf[i].rchannel; + } + + copyCount -= count; + bufStart += count; + MIPS_ASSERT(bufStart <= db->fragTotalSize); + buffer += count *2; + } + return 0; + +} + +/* return the total bytes that is copied */ +static int inline +copy_dac_from_user(struct vrc5477_ac97_state *s, + const char *buffer, + size_t count, + int avail) +{ + struct dmabuf *db = &s->dma_dac; + int copyCount=0; + int copyFragCount=0; + int totalCopyCount = 0; + int totalCopyFragCount = 0; + unsigned long flags; +#if defined(VRC5477_AC97_VERBOSE_DEBUG) + int i; +#endif + + /* adjust count to signel channel byte count */ + count >>= s->dacChannels - 1; + + /* we may have to "copy" twice as ring buffer wraps around */ + for (; (avail > 0) && (count > 0); ) { + /* determine max possible copy count for single channel */ + copyCount = count; + if (copyCount > avail) { + copyCount = avail; + } + if (copyCount + db->nextIn > db->fragTotalSize) { + copyCount = db->fragTotalSize - db->nextIn; + MIPS_ASSERT((copyCount % db->fragSize) == 0); + MIPS_ASSERT(copyCount > 0); + } + + copyFragCount = (copyCount-1) >> db->fragShift; + copyFragCount = (copyFragCount+1) << db->fragShift; + MIPS_ASSERT(copyFragCount >= copyCount); + + /* we copy differently based on the number channels */ + if (s->dacChannels == 1) { + if (copy_from_user(db->lbuf + db->nextIn, + buffer, + copyCount)) + return -1; + /* fill gaps with 0 */ + memset(db->lbuf + db->nextIn + copyCount, + 0, + copyFragCount - copyCount); + } else { + /* we have demux the stream into two separate ones */ + if (copy_two_channel_dac_from_user(s, buffer, copyCount)) + return -1; + /* fill gaps with 0 */ + memset(db->lbuf + db->nextIn + copyCount, + 0, + copyFragCount - copyCount); + memset(db->rbuf + db->nextIn + copyCount, + 0, + copyFragCount - copyCount); + } + +#if defined(VRC5477_AC97_VERBOSE_DEBUG) + for (i=0; i< copyFragCount; i+= db->fragSize) { + *(u16*)(db->lbuf + db->nextIn + i) = inTicket ++; + } +#endif + + count -= copyCount; + totalCopyCount =+ copyCount; + avail -= copyFragCount; + totalCopyFragCount += copyFragCount; + + buffer += copyCount << (s->dacChannels - 1); + + db->nextIn += copyFragCount; + if (db->nextIn >= db->fragTotalSize) { + MIPS_ASSERT(db->nextIn == db->fragTotalSize); + db->nextIn = 0; + } + + MIPS_ASSERT((copyFragCount % db->fragSize) == 0); + MIPS_ASSERT( (count == 0) || (copyCount == copyFragCount)); + } + + spin_lock_irqsave(&s->lock, flags); + db->count += totalCopyFragCount; + if (db->stopped) { + start_dac(s); + } + + /* nextIn should not be equal to nextOut unless we are full */ + MIPS_ASSERT( ( (db->count == db->fragTotalSize) && + (db->nextIn == db->nextOut) ) || + ( (db->count < db->fragTotalSize) && + (db->nextIn != db->nextOut) ) ); + + spin_unlock_irqrestore(&s->lock, flags); + + return totalCopyCount << (s->dacChannels-1); + +} + +static ssize_t vrc5477_ac97_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)file->private_data; + struct dmabuf *db = &s->dma_dac; + ssize_t ret; + unsigned long flags; + int copyCount, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + 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->fragTotalSize - 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); + + MIPS_ASSERT( (avail % db->fragSize) == 0); + copyCount = copy_dac_from_user(s, buffer, count, avail); + if (copyCount < 0) { + if (!ret) ret = -EFAULT; + return ret; + } + + count -= copyCount; + buffer += copyCount; + ret += copyCount; + } // while (count > 0) + + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int vrc5477_ac97_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct vrc5477_ac97_state *s = (struct vrc5477_ac97_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 ((signed)s->dma_dac.fragTotalSize >= + s->dma_dac.count + (signed)s->dma_dac.fragSize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +#ifdef CONFIG_LL_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_SETFMT, "SNDCTL_DSP_SETFMT"}, + {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_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 vrc5477_ac97_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + int count; + int val, ret; + +#ifdef CONFIG_LL_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, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = 0; + s->dma_dac.nextIn = s->dma_dac.nextOut = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = 0; + s->dma_adc.nextIn = s->dma_adc.nextOut = 0; + } + 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->adcChannels = 2; + else + s->adcChannels = 1; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val) + s->dacChannels = 2; + else + s->dacChannels = 1; + 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 ( (val != 1) && (val != 2)) val = 2; + + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dacChannels = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dacChannels = 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, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (val != AFMT_S16_LE) return -EINVAL; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } else { + val = AFMT_S16_LE; + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + case SNDCTL_DSP_SETTRIGGER: + /* NO trigger */ + return -EINVAL; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + abinfo.fragsize = s->dma_dac.fragSize << (s->dacChannels-1); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + abinfo.bytes = (s->dma_dac.fragTotalSize - count) << + (s->dacChannels-1); + abinfo.fragstotal = s->dma_dac.numFrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragShift >> + (s->dacChannels-1); + 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 << (s->adcChannels-1); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = count << (s->adcChannels-1); + abinfo.fragstotal = s->dma_adc.numFrag; + abinfo.fragments = (abinfo.bytes >> s->dma_adc.fragShift) >> + (s->adcChannels-1); + 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; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + /* we cannot get DMA ptr */ + return -EINVAL; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(s->dma_dac.fragSize << (s->dacChannels-1), (int *)arg); + else + return put_user(s->dma_adc.fragSize << (s->adcChannels-1), (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + /* we ignore fragment size request */ + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + /* what is this for? [jsun] */ + 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->adcChannels, (int *)arg); + else + return put_user(s->dacChannels ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user(16, (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 vrc5477_ac97_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 vrc5477_ac97_state *s; + int ret=0; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct vrc5477_ac97_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; + } + 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); + + if (file->f_mode & FMODE_READ) { + /* set default settings */ + set_adc_rate(s, 48000); + s->adcChannels = 2; + + ret = prog_dmabuf_adc(s); + if (ret) goto bailout; + } + if (file->f_mode & FMODE_WRITE) { + /* set default settings */ + set_dac_rate(s, 48000); + s->dacChannels = 2; + + ret = prog_dmabuf_dac(s); + if (ret) goto bailout; + } + + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + bailout: + spin_unlock_irqrestore(&s->lock, flags); + + up(&s->open_sem); + return ret; +} + +static int vrc5477_ac97_release(struct inode *inode, struct file *file) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_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; +} + +static /*const*/ struct file_operations vrc5477_ac97_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: vrc5477_ac97_read, + write: vrc5477_ac97_write, + poll: vrc5477_ac97_poll, + ioctl: vrc5477_ac97_ioctl, + // mmap: vrc5477_ac97_mmap, + open: vrc5477_ac97_open, + release: vrc5477_ac97_release, +}; + + +/* --------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------- */ + +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef CONFIG_LL_DEBUG + +struct { + const char *regname; + unsigned regaddr; +} vrc5477_ac97_regs[] = { + {"VRC5477_INT_STATUS", VRC5477_INT_STATUS}, + {"VRC5477_CODEC_WR", VRC5477_CODEC_WR}, + {"VRC5477_CODEC_RD", VRC5477_CODEC_RD}, + {"VRC5477_CTRL", VRC5477_CTRL}, + {"VRC5477_ACLINK_CTRL", VRC5477_ACLINK_CTRL}, + {"VRC5477_INT_MASK", VRC5477_INT_MASK}, + {"VRC5477_DAC1_CTRL", VRC5477_DAC1_CTRL}, + {"VRC5477_DAC1L", VRC5477_DAC1L}, + {"VRC5477_DAC1_BADDR", VRC5477_DAC1_BADDR}, + {"VRC5477_DAC2_CTRL", VRC5477_DAC2_CTRL}, + {"VRC5477_DAC2L", VRC5477_DAC2L}, + {"VRC5477_DAC2_BADDR", VRC5477_DAC2_BADDR}, + {"VRC5477_DAC3_CTRL", VRC5477_DAC3_CTRL}, + {"VRC5477_DAC3L", VRC5477_DAC3L}, + {"VRC5477_DAC3_BADDR", VRC5477_DAC3_BADDR}, + {"VRC5477_ADC1_CTRL", VRC5477_ADC1_CTRL}, + {"VRC5477_ADC1L", VRC5477_ADC1L}, + {"VRC5477_ADC1_BADDR", VRC5477_ADC1_BADDR}, + {"VRC5477_ADC2_CTRL", VRC5477_ADC2_CTRL}, + {"VRC5477_ADC2L", VRC5477_ADC2L}, + {"VRC5477_ADC2_BADDR", VRC5477_ADC2_BADDR}, + {"VRC5477_ADC3_CTRL", VRC5477_ADC3_CTRL}, + {"VRC5477_ADC3L", VRC5477_ADC3L}, + {"VRC5477_ADC3_BADDR", VRC5477_ADC3_BADDR}, + {NULL, 0x0} +}; + +static int proc_vrc5477_ac97_dump (char *buf, char **start, off_t fpos, + int length, int *eof, void *data) +{ + struct vrc5477_ac97_state *s; + int cnt, len = 0; + + if (list_empty(&devs)) + return 0; + s = list_entry(devs.next, struct vrc5477_ac97_state, devs); + + /* print out header */ + len += sprintf(buf + len, "\n\t\tVrc5477 Audio Debug\n\n"); + + // print out digital controller state + len += sprintf (buf + len, "NEC Vrc5477 Audio Controller registers\n"); + len += sprintf (buf + len, "---------------------------------\n"); + for (cnt=0; vrc5477_ac97_regs[cnt].regname != NULL; cnt++) { + len+= sprintf (buf + len, "%-20s = %08x\n", + vrc5477_ac97_regs[cnt].regname, + inl(s->io + vrc5477_ac97_regs[cnt].regaddr)); + } + + /* print out driver state */ + len += sprintf (buf + len, "NEC Vrc5477 Audio driver states\n"); + len += sprintf (buf + len, "---------------------------------\n"); + len += sprintf (buf + len, "dacChannels = %d\n", s->dacChannels); + len += sprintf (buf + len, "adcChannels = %d\n", s->adcChannels); + len += sprintf (buf + len, "dacRate = %d\n", s->dacRate); + len += sprintf (buf + len, "adcRate = %d\n", s->adcRate); + + len += sprintf (buf + len, "dma_dac is %s ready\n", + s->dma_dac.ready? "" : "not"); + if (s->dma_dac.ready) { + len += sprintf (buf + len, "dma_dac is %s stopped.\n", + s->dma_dac.stopped? "" : "not"); + len += sprintf (buf + len, "dma_dac.fragSize = %x\n", + s->dma_dac.fragSize); + len += sprintf (buf + len, "dma_dac.fragShift = %x\n", + s->dma_dac.fragShift); + len += sprintf (buf + len, "dma_dac.numFrag = %x\n", + s->dma_dac.numFrag); + len += sprintf (buf + len, "dma_dac.fragTotalSize = %x\n", + s->dma_dac.fragTotalSize); + len += sprintf (buf + len, "dma_dac.nextIn = %x\n", + s->dma_dac.nextIn); + len += sprintf (buf + len, "dma_dac.nextOut = %x\n", + s->dma_dac.nextOut); + len += sprintf (buf + len, "dma_dac.count = %x\n", + s->dma_dac.count); + } + + len += sprintf (buf + len, "dma_adc is %s ready\n", + s->dma_adc.ready? "" : "not"); + if (s->dma_adc.ready) { + len += sprintf (buf + len, "dma_adc is %s stopped.\n", + s->dma_adc.stopped? "" : "not"); + len += sprintf (buf + len, "dma_adc.fragSize = %x\n", + s->dma_adc.fragSize); + len += sprintf (buf + len, "dma_adc.fragShift = %x\n", + s->dma_adc.fragShift); + len += sprintf (buf + len, "dma_adc.numFrag = %x\n", + s->dma_adc.numFrag); + len += sprintf (buf + len, "dma_adc.fragTotalSize = %x\n", + s->dma_adc.fragTotalSize); + len += sprintf (buf + len, "dma_adc.nextIn = %x\n", + s->dma_adc.nextIn); + len += sprintf (buf + len, "dma_adc.nextOut = %x\n", + s->dma_adc.nextOut); + len += sprintf (buf + len, "dma_adc.count = %x\n", + s->dma_adc.count); + } + + /* 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 len; + +} +#endif /* CONFIG_LL_DEBUG */ + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static unsigned int devindex = 0; + +MODULE_AUTHOR("Monta Vista Software, jsun@mvista.com or jsun@junsun.net"); +MODULE_DESCRIPTION("NEC Vrc5477 audio (AC97) Driver"); +MODULE_LICENSE("GPL"); + +/* --------------------------------------------------------------------- */ +extern void jsun_scan_pci_bus(void); +extern void vrc5477_show_pci_regs(void); +extern void vrc5477_show_pdar_regs(void); + +/* -------------------------------------------------------- */ +#define AC97_BASE 0xbb000000 +#define myinl(x) *(volatile u32*)(AC97_BASE + (x)) +#define myoutl(x,y) *(volatile u32*)(AC97_BASE + (y)) = (x) + +u16 myrdcodec(u8 addr) +{ + u32 result; + + /* wait until we can access codec registers */ + // while (inl(VRC5477_CODEC_WR) & 0x80000000); + + /* write the address and "read" command to codec */ + addr = addr & 0x7f; + myoutl((addr << 16) | VRC5477_CODEC_WR_RWC, VRC5477_CODEC_WR); + + /* get the return result */ + udelay(100); /* workaround hardware bug */ + // dump_memory(0xbb000000, 48); + while ( ((result=myinl(VRC5477_CODEC_RD)) & 0xc0000000) != 0xc0000000); + MIPS_ASSERT(addr == ((result >> 16) & 0x7f) ); + return result & 0xffff; +} + +void mywrcodec(u8 addr, u16 data) +{ + /* wait until we can access codec registers */ + while (myinl(VRC5477_CODEC_WR) & 0x80000000); + + /* write the address and value to codec */ + myoutl((addr << 16) | data, VRC5477_CODEC_WR); + +} + + +void jsun_ac97_test(struct vrc5477_ac97_state *s) +{ + int i; + + /* reset codec */ + /* + wrcodec(&s->codec, 0, 0); + while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); + */ + mywrcodec(0, 0); + while (myinl(VRC5477_CODEC_WR) & 0x80000000); + + for (i=0; i< 0x40; i+=4) { + MIPS_ASSERT(inl(s->io+i) == myinl(i)); + } + + printk("codec registers : "); + for (i=0; i<= 0x3a; i+=2) { + if ( (i%0x10) == 0) { + printk("\n%02x\t", i); + } + // printk("%04x\t", rdcodec(&s->codec, i)); + printk("%04x\t", myrdcodec(i)); + } + printk("\n\n"); + printk("codec registers : "); + for (i=0; i<= 0x3a; i+=2) { + if ( (i%0x10) == 0) { + printk("\n%02x\t", i); + } + printk("%04x\t", rdcodec(&s->codec, i)); + } + printk("\n\n"); +} + +static int __devinit vrc5477_ac97_probe(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ + struct vrc5477_ac97_state *s; + char proc_str[80]; + + MIPS_DEBUG(printk("vrc5477_ac97_probe() invoked\n")); + + if (pcidev->irq == 0) + return -1; + + if (!(s = kmalloc(sizeof(struct vrc5477_ac97_state), GFP_KERNEL))) { + printk(KERN_ERR PFX "alloc of device struct failed\n"); + return -1; + } + memset(s, 0, sizeof(struct vrc5477_ac97_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->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 + * no persistent state across file opens. + */ + + if (!request_region(s->io, pci_resource_len(pcidev,0), + VRC5477_AC97_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, vrc5477_ac97_interrupt, SA_INTERRUPT, + VRC5477_AC97_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(&vrc5477_ac97_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->codec.dev_mixer = + register_sound_mixer(&vrc5477_ac97_mixer_fops, -1)) < 0) + goto err_dev2; + +#ifdef CONFIG_LL_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_read_entry(VRC5477_AC97_MODULE_NAME, 0, NULL, + proc_vrc5477_ac97_dump, NULL); +#endif /* CONFIG_LL_DEBUG */ + + /* enable pci io and bus mastering */ + if (pci_enable_device(pcidev)) + goto err_dev3; + pci_set_master(pcidev); + +/* +jsun_scan_pci_bus(); +vrc5477_show_pci_regs(); +vrc5477_show_pdar_regs(); +*/ + + /* cold reset the AC97 */ + outl(VRC5477_ACLINK_CTRL_RST_ON | VRC5477_ACLINK_CTRL_RST_TIME, + s->io + VRC5477_ACLINK_CTRL); + while (inl(s->io + VRC5477_ACLINK_CTRL) & VRC5477_ACLINK_CTRL_RST_ON); + +/* +jsun_ac97_test(s); +*/ + + /* codec init */ + if (!ac97_probe_codec(&s->codec)) + goto err_dev3; + +#ifdef CONFIG_LL_DEBUG + sprintf(proc_str, "driver/%s/%d/ac97", + VRC5477_AC97_MODULE_NAME, s->codec.id); + s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, + ac97_read_proc, &s->codec); + /* TODO : why this proc file does not show up? */ +#endif + + /* let us get the default volumne louder */ + wrcodec(&s->codec, 0x2, 0); + wrcodec(&s->codec, 0x18, 0x0707); + /* mute line in loopback to line out */ + wrcodec(&s->codec, 0x10, 0x8000); + + /* by default we select line in the input */ + wrcodec(&s->codec, 0x1a, 0x0404); + /* pick middle value for record gain */ + // wrcodec(&s->codec, 0x1c, 0x0707); + 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); + + /* 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); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR PFX "cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, pci_resource_len(pcidev,0)); + err_region: + kfree(s); + return -1; +} + +static void __devinit vrc5477_ac97_remove(struct pci_dev *dev) +{ + struct vrc5477_ac97_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); +#ifdef CONFIG_LL_DEBUG + if (s->ps) + remove_proc_entry(VRC5477_AC97_MODULE_NAME, NULL); +#endif /* CONFIG_LL_DEBUG */ + 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); + kfree(s); + pci_set_drvdata(dev, NULL); +} + + +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00A6 +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_VRC5477_AC97, + PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver vrc5477_ac97_driver = { + name: VRC5477_AC97_MODULE_NAME, + id_table: id_table, + probe: vrc5477_ac97_probe, + remove: vrc5477_ac97_remove +}; + +static int __init init_vrc5477_ac97(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk("Vrc5477 AC97 driver: version v0.1 time " __TIME__ " " __DATE__ " by Jun Sun\n"); + return pci_module_init(&vrc5477_ac97_driver); +} + +static void __exit cleanup_vrc5477_ac97(void) +{ + printk(KERN_INFO PFX "unloading\n"); + pci_unregister_driver(&vrc5477_ac97_driver); +} + +module_init(init_vrc5477_ac97); +module_exit(cleanup_vrc5477_ac97); + diff -Nru a/sound/oss/nm256.h b/sound/oss/nm256.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/nm256.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,290 @@ +#ifndef _NM256_H_ +#define _NM256_H_ + +#include "ac97.h" + +/* The revisions that we currently handle. */ +enum nm256rev { + REV_NM256AV, REV_NM256ZX +}; + +/* Per-card structure. */ +struct nm256_info +{ + /* Magic number used to verify that this struct is valid. */ +#define NM_MAGIC_SIG 0x55aa00ff + int magsig; + + /* Revision number */ + enum nm256rev rev; + + struct ac97_hwint mdev; + + /* Our audio device numbers. */ + int dev[2]; + + /* The # of times each device has been opened. (Should only be + 0 or 1). */ + int opencnt[2]; + + /* We use two devices, because we can do simultaneous play and record. + This keeps track of which device is being used for what purpose; + these are the actual device numbers. */ + int dev_for_play; + int dev_for_record; + + /* The mixer device. */ + int mixer_oss_dev; + + /* + * Can only be opened once for each operation. These aren't set + * until an actual I/O operation is performed; this allows one + * device to be open for read/write without inhibiting I/O to + * the other device. + */ + int is_open_play; + int is_open_record; + + /* Non-zero if we're currently playing a sample. */ + int playing; + /* Ditto for recording a sample. */ + int recording; + + /* The two memory ports. */ + struct nm256_ports { + /* Physical address of the port. */ + u32 physaddr; + /* Our mapped-in pointer. */ + char *ptr; + /* PTR's offset within the physical port. */ + u32 start_offset; + /* And the offset of the end of the buffer. */ + u32 end_offset; + } port[2]; + + /* The following are offsets within memory port 1. */ + u32 coeffBuf; + u32 allCoeffBuf; + + /* Record and playback buffers. */ + u32 abuf1, abuf2; + + /* Offset of the AC97 mixer in memory port 2. */ + u32 mixer; + + /* Offset of the mixer status register in memory port 2. */ + u32 mixer_status_offset; + + /* Non-zero if we have written initial values to the mixer. */ + u8 mixer_values_init; + + /* + * Status mask bit; (*mixer_status_loc & mixer_status_mask) == 0 means + * it's ready. + */ + u16 mixer_status_mask; + + /* The sizes of the playback and record ring buffers. */ + u32 playbackBufferSize; + u32 recordBufferSize; + + /* Are the coefficient values in the memory cache current? */ + u8 coeffsCurrent; + + /* For writes, the amount we last wrote. */ + u32 requested_amt; + /* The start of the block currently playing. */ + u32 curPlayPos; + + /* The amount of data we were requested to record. */ + u32 requestedRecAmt; + /* The offset of the currently-recording block. */ + u32 curRecPos; + /* The destination buffer. */ + char *recBuf; + + /* Our IRQ number. */ + int irq; + + /* A flag indicating how many times we've grabbed the IRQ. */ + int has_irq; + + /* The card interrupt service routine. */ + void (*introutine) (int, void *, struct pt_regs *); + + /* Current audio config, cached. */ + struct sinfo { + u32 samplerate; + u8 bits; + u8 stereo; + } sinfo[2]; /* goes with each device */ + + /* The cards are stored in a chain; this is the next card. */ + struct nm256_info *next_card; +}; + +/* Debug flag--bigger numbers mean more output. */ +extern int nm256_debug; + +/* The BIOS signature. */ +#define NM_SIGNATURE 0x4e4d0000 +/* Signature mask. */ +#define NM_SIG_MASK 0xffff0000 + +/* Size of the second memory area. */ +#define NM_PORT2_SIZE 4096 + +/* The base offset of the mixer in the second memory area. */ +#define NM_MIXER_OFFSET 0x600 + +/* The maximum size of a coefficient entry. */ +#define NM_MAX_COEFFICIENT 0x5000 + +/* The interrupt register. */ +#define NM_INT_REG 0xa04 +/* And its bits. */ +#define NM_PLAYBACK_INT 0x40 +#define NM_RECORD_INT 0x100 +#define NM_MISC_INT_1 0x4000 +#define NM_MISC_INT_2 0x1 +#define NM_ACK_INT(CARD, X) nm256_writePort16((CARD), 2, NM_INT_REG, (X) << 1) + +/* The AV's "mixer ready" status bit and location. */ +#define NM_MIXER_STATUS_OFFSET 0xa04 +#define NM_MIXER_READY_MASK 0x0800 +#define NM_MIXER_PRESENCE 0xa06 +#define NM_PRESENCE_MASK 0x0050 +#define NM_PRESENCE_VALUE 0x0040 + +/* + * For the ZX. It uses the same interrupt register, but it holds 32 + * bits instead of 16. + */ +#define NM2_PLAYBACK_INT 0x10000 +#define NM2_RECORD_INT 0x80000 +#define NM2_MISC_INT_1 0x8 +#define NM2_MISC_INT_2 0x2 +#define NM2_ACK_INT(CARD, X) nm256_writePort32((CARD), 2, NM_INT_REG, (X)) + +/* The ZX's "mixer ready" status bit and location. */ +#define NM2_MIXER_STATUS_OFFSET 0xa06 +#define NM2_MIXER_READY_MASK 0x0800 + +/* The playback registers start from here. */ +#define NM_PLAYBACK_REG_OFFSET 0x0 +/* The record registers start from here. */ +#define NM_RECORD_REG_OFFSET 0x200 + +/* The rate register is located 2 bytes from the start of the register area. */ +#define NM_RATE_REG_OFFSET 2 + +/* Mono/stereo flag, number of bits on playback, and rate mask. */ +#define NM_RATE_STEREO 1 +#define NM_RATE_BITS_16 2 +#define NM_RATE_MASK 0xf0 + +/* Playback enable register. */ +#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1) +#define NM_PLAYBACK_ENABLE_FLAG 1 +#define NM_PLAYBACK_ONESHOT 2 +#define NM_PLAYBACK_FREERUN 4 + +/* Mutes the audio output. */ +#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18) +#define NM_AUDIO_MUTE_LEFT 0x8000 +#define NM_AUDIO_MUTE_RIGHT 0x0080 + +/* Recording enable register. */ +#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0) +#define NM_RECORD_ENABLE_FLAG 1 +#define NM_RECORD_FREERUN 2 + +#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4) +#define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10) +#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc) +#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8) + +#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4) +#define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14) +#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc) +#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8) + +/* A few trivial routines to make it easier to work with the registers + on the chip. */ + +/* This is a common code portion used to fix up the port offsets. */ +#define NM_FIX_PORT \ + if (port < 1 || port > 2 || card == NULL) \ + return -1; \ +\ + if (offset < card->port[port - 1].start_offset \ + || offset >= card->port[port - 1].end_offset) { \ + printk (KERN_ERR "Bad access: port %d, offset 0x%x\n", port, offset); \ + return -1; \ + } \ + offset -= card->port[port - 1].start_offset; + +#define DEFwritePortX(X, func) \ +static inline int nm256_writePort##X (struct nm256_info *card,\ + int port, int offset, int value)\ +{\ + u##X *addr;\ +\ + if (nm256_debug > 1)\ + printk (KERN_DEBUG "Writing 0x%x to %d:0x%x\n", value, port, offset);\ +\ + NM_FIX_PORT;\ +\ + addr = (u##X *)(card->port[port - 1].ptr + offset);\ + func (value, addr);\ + return 0;\ +} + +DEFwritePortX (8, writeb) +DEFwritePortX (16, writew) +DEFwritePortX (32, writel) + +#define DEFreadPortX(X, func) \ +static inline u##X nm256_readPort##X (struct nm256_info *card,\ + int port, int offset)\ +{\ + u##X *addr;\ +\ + NM_FIX_PORT\ +\ + addr = (u##X *)(card->port[port - 1].ptr + offset);\ + return func(addr);\ +} + +DEFreadPortX (8, readb) +DEFreadPortX (16, readw) +DEFreadPortX (32, readl) + +static inline int +nm256_writeBuffer8 (struct nm256_info *card, u8 *src, int port, int offset, + int amt) +{ + NM_FIX_PORT; + memcpy_toio (card->port[port - 1].ptr + offset, src, amt); + return 0; +} + +static inline int +nm256_readBuffer8 (struct nm256_info *card, u8 *dst, int port, int offset, + int amt) +{ + NM_FIX_PORT; + memcpy_fromio (dst, card->port[port - 1].ptr + offset, amt); + return 0; +} + +/* Returns a non-zero value if we should use the coefficient cache. */ +extern int nm256_cachedCoefficients (struct nm256_info *card); + +#endif + +/* + * Local variables: + * c-basic-offset: 4 + * End: + */ diff -Nru a/sound/oss/nm256_audio.c b/sound/oss/nm256_audio.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/nm256_audio.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,1690 @@ +/* + * Audio driver for the NeoMagic 256AV and 256ZX chipsets in native + * mode, with AC97 mixer support. + * + * Overall design and parts of this code stolen from vidc_*.c and + * skeleton.c. + * + * Yeah, there are a lot of magic constants in here. You tell ME what + * they are. I just get this stuff psychically, remember? + * + * This driver was written by someone who wishes to remain anonymous. + * It is in the public domain, so share and enjoy. Try to make a profit + * off of it; go on, I dare you. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added some __init + * 19-04-2001 Marcus Meissner + * Ported to 2.4 PCI API. + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "sound_config.h" +#include "nm256.h" +#include "nm256_coeff.h" + +int nm256_debug; +static int force_load; + +/* + * The size of the playback reserve. When the playback buffer has less + * than NM256_PLAY_WMARK_SIZE bytes to output, we request a new + * buffer. + */ +#define NM256_PLAY_WMARK_SIZE 512 + +static struct audio_driver nm256_audio_driver; + +static int nm256_grabInterrupt (struct nm256_info *card); +static int nm256_releaseInterrupt (struct nm256_info *card); +static void nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy); +static void nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy); +static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data); + +/* These belong in linux/pci.h. */ +#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 +#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 + +/* List of cards. */ +static struct nm256_info *nmcard_list; + +/* Release the mapped-in memory for CARD. */ +static void +nm256_release_ports (struct nm256_info *card) +{ + int x; + + for (x = 0; x < 2; x++) { + if (card->port[x].ptr != NULL) { + iounmap (card->port[x].ptr); + card->port[x].ptr = NULL; + } + } +} + +/* + * Map in the memory ports for CARD, if they aren't already mapped in + * and have been configured. If successful, a zero value is returned; + * otherwise any previously mapped-in areas are released and a non-zero + * value is returned. + * + * This is invoked twice, once for each port. Ideally it would only be + * called once, but we now need to map in the second port in order to + * check how much memory the card has on the 256ZX. + */ +static int +nm256_remap_ports (struct nm256_info *card) +{ + int x; + + for (x = 0; x < 2; x++) { + if (card->port[x].ptr == NULL && card->port[x].end_offset > 0) { + u32 physaddr + = card->port[x].physaddr + card->port[x].start_offset; + u32 size + = card->port[x].end_offset - card->port[x].start_offset; + + card->port[x].ptr = ioremap_nocache (physaddr, size); + + if (card->port[x].ptr == NULL) { + printk (KERN_ERR "NM256: Unable to remap port %d\n", x + 1); + nm256_release_ports (card); + return -1; + } + } + } + return 0; +} + +/* Locate the card in our list. */ +static struct nm256_info * +nm256_find_card (int dev) +{ + struct nm256_info *card; + + for (card = nmcard_list; card != NULL; card = card->next_card) + if (card->dev[0] == dev || card->dev[1] == dev) + return card; + + return NULL; +} + +/* + * Ditto, but find the card struct corresponding to the mixer device DEV + * instead. + */ +static struct nm256_info * +nm256_find_card_for_mixer (int dev) +{ + struct nm256_info *card; + + for (card = nmcard_list; card != NULL; card = card->next_card) + if (card->mixer_oss_dev == dev) + return card; + + return NULL; +} + +static int usecache; +static int buffertop; + +/* Check to see if we're using the bank of cached coefficients. */ +int +nm256_cachedCoefficients (struct nm256_info *card) +{ + return usecache; +} + +/* The actual rates supported by the card. */ +static int samplerates[9] = { + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 +}; + +/* + * Set the card samplerate, word size and stereo mode to correspond to + * the settings in the CARD struct for the specified device in DEV. + * We keep two separate sets of information, one for each device; the + * hardware is not actually configured until a read or write is + * attempted. + */ + +int +nm256_setInfo (int dev, struct nm256_info *card) +{ + int x; + int w; + int targetrate; + + if (card->dev[0] == dev) + w = 0; + else if (card->dev[1] == dev) + w = 1; + else + return -ENODEV; + + targetrate = card->sinfo[w].samplerate; + + if ((card->sinfo[w].bits != 8 && card->sinfo[w].bits != 16) + || targetrate < samplerates[0] + || targetrate > samplerates[7]) + return -EINVAL; + + for (x = 0; x < 8; x++) + if (targetrate < ((samplerates[x] + samplerates[x + 1]) / 2)) + break; + + if (x < 8) { + u8 ratebits = ((x << 4) & NM_RATE_MASK); + if (card->sinfo[w].bits == 16) + ratebits |= NM_RATE_BITS_16; + if (card->sinfo[w].stereo) + ratebits |= NM_RATE_STEREO; + + card->sinfo[w].samplerate = samplerates[x]; + + + if (card->dev_for_play == dev && card->playing) { + if (nm256_debug) + printk (KERN_DEBUG "Setting play ratebits to 0x%x\n", + ratebits); + nm256_loadCoefficient (card, 0, x); + nm256_writePort8 (card, 2, + NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); + } + + if (card->dev_for_record == dev && card->recording) { + if (nm256_debug) + printk (KERN_DEBUG "Setting record ratebits to 0x%x\n", + ratebits); + nm256_loadCoefficient (card, 1, x); + nm256_writePort8 (card, 2, + NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); + } + return 0; + } + else + return -EINVAL; +} + +/* Start the play process going. */ +static void +startPlay (struct nm256_info *card) +{ + if (! card->playing) { + card->playing = 1; + if (nm256_grabInterrupt (card) == 0) { + nm256_setInfo (card->dev_for_play, card); + + /* Enable playback engine and interrupts. */ + nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG, + NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); + + /* Enable both channels. */ + nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG, 0x0); + } + } +} + +/* + * Request one chunk of AMT bytes from the recording device. When the + * operation is complete, the data will be copied into BUFFER and the + * function DMAbuf_inputintr will be invoked. + */ + +static void +nm256_startRecording (struct nm256_info *card, char *buffer, u32 amt) +{ + u32 endpos; + int enableEngine = 0; + u32 ringsize = card->recordBufferSize; + unsigned long flags; + + if (amt > (ringsize / 2)) { + /* + * Of course this won't actually work right, because the + * caller is going to assume we will give what we got asked + * for. + */ + printk (KERN_ERR "NM256: Read request too large: %d\n", amt); + amt = ringsize / 2; + } + + if (amt < 8) { + printk (KERN_ERR "NM256: Read request too small; %d\n", amt); + return; + } + + save_flags (flags); + cli (); + /* + * If we're not currently recording, set up the start and end registers + * for the recording engine. + */ + if (! card->recording) { + card->recording = 1; + if (nm256_grabInterrupt (card) == 0) { + card->curRecPos = 0; + nm256_setInfo (card->dev_for_record, card); + nm256_writePort32 (card, 2, NM_RBUFFER_START, card->abuf2); + nm256_writePort32 (card, 2, NM_RBUFFER_END, + card->abuf2 + ringsize); + + nm256_writePort32 (card, 2, NM_RBUFFER_CURRP, + card->abuf2 + card->curRecPos); + enableEngine = 1; + } + else { + /* Not sure what else to do here. */ + restore_flags (flags); + return; + } + } + + /* + * If we happen to go past the end of the buffer a bit (due to a + * delayed interrupt) it's OK. So might as well set the watermark + * right at the end of the data we want. + */ + endpos = card->abuf2 + ((card->curRecPos + amt) % ringsize); + + card->recBuf = buffer; + card->requestedRecAmt = amt; + nm256_writePort32 (card, 2, NM_RBUFFER_WMARK, endpos); + /* Enable recording engine and interrupts. */ + if (enableEngine) + nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG, + NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN); + + restore_flags (flags); +} + +/* Stop the play engine. */ +static void +stopPlay (struct nm256_info *card) +{ + /* Shut off sound from both channels. */ + nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG, + NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); + /* Disable play engine. */ + nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG, 0); + if (card->playing) { + nm256_releaseInterrupt (card); + + /* Reset the relevant state bits. */ + card->playing = 0; + card->curPlayPos = 0; + } +} + +/* Stop recording. */ +static void +stopRecord (struct nm256_info *card) +{ + /* Disable recording engine. */ + nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG, 0); + + if (card->recording) { + nm256_releaseInterrupt (card); + + card->recording = 0; + card->curRecPos = 0; + } +} + +/* + * Ring buffers, man. That's where the hip-hop, wild-n-wooly action's at. + * 1972? (Well, I suppose it was cheep-n-easy to implement.) + * + * Write AMT bytes of BUFFER to the playback ring buffer, and start the + * playback engine running. It will only accept up to 1/2 of the total + * size of the ring buffer. No check is made that we're about to overwrite + * the currently-playing sample. + */ + +static void +nm256_write_block (struct nm256_info *card, char *buffer, u32 amt) +{ + u32 ringsize = card->playbackBufferSize; + u32 endstop; + unsigned long flags; + + if (amt > (ringsize / 2)) { + printk (KERN_ERR "NM256: Write request too large: %d\n", amt); + amt = (ringsize / 2); + } + + if (amt < NM256_PLAY_WMARK_SIZE) { + printk (KERN_ERR "NM256: Write request too small: %d\n", amt); + return; + } + + card->curPlayPos %= ringsize; + + card->requested_amt = amt; + + save_flags (flags); + cli (); + + if ((card->curPlayPos + amt) >= ringsize) { + u32 rem = ringsize - card->curPlayPos; + + nm256_writeBuffer8 (card, buffer, 1, + card->abuf1 + card->curPlayPos, + rem); + if (amt > rem) + nm256_writeBuffer8 (card, buffer + rem, 1, card->abuf1, + amt - rem); + } + else + nm256_writeBuffer8 (card, buffer, 1, + card->abuf1 + card->curPlayPos, + amt); + + /* + * Setup the start-n-stop-n-limit registers, and start that engine + * goin'. + * + * Normally we just let it wrap around to avoid the click-click + * action scene. + */ + if (! card->playing) { + /* The PBUFFER_END register in this case points to one sample + before the end of the buffer. */ + int w = (card->dev_for_play == card->dev[0] ? 0 : 1); + int sampsize = (card->sinfo[w].bits == 16 ? 2 : 1); + + if (card->sinfo[w].stereo) + sampsize *= 2; + + /* Need to set the not-normally-changing-registers up. */ + nm256_writePort32 (card, 2, NM_PBUFFER_START, + card->abuf1 + card->curPlayPos); + nm256_writePort32 (card, 2, NM_PBUFFER_END, + card->abuf1 + ringsize - sampsize); + nm256_writePort32 (card, 2, NM_PBUFFER_CURRP, + card->abuf1 + card->curPlayPos); + } + endstop = (card->curPlayPos + amt - NM256_PLAY_WMARK_SIZE) % ringsize; + nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop); + + if (! card->playing) + startPlay (card); + + restore_flags (flags); +} + +/* We just got a card playback interrupt; process it. */ +static void +nm256_get_new_block (struct nm256_info *card) +{ + /* Check to see how much got played so far. */ + u32 amt = nm256_readPort32 (card, 2, NM_PBUFFER_CURRP) - card->abuf1; + + if (amt >= card->playbackBufferSize) { + printk (KERN_ERR "NM256: Sound playback pointer invalid!\n"); + amt = 0; + } + + if (amt < card->curPlayPos) + amt = (card->playbackBufferSize - card->curPlayPos) + amt; + else + amt -= card->curPlayPos; + + if (card->requested_amt > (amt + NM256_PLAY_WMARK_SIZE)) { + u32 endstop = + card->curPlayPos + card->requested_amt - NM256_PLAY_WMARK_SIZE; + nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop); + } + else { + card->curPlayPos += card->requested_amt; + /* Get a new block to write. This will eventually invoke + nm256_write_block () or stopPlay (). */ + DMAbuf_outputintr (card->dev_for_play, 1); + } +} + +/* Ultra cheez-whiz. But I'm too lazy to grep headers. */ +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) + +/* + * Read the last-recorded block from the ring buffer, copy it into the + * saved buffer pointer, and invoke DMAuf_inputintr() with the recording + * device. + */ + +static void +nm256_read_block (struct nm256_info *card) +{ + /* Grab the current position of the recording pointer. */ + u32 currptr = nm256_readPort32 (card, 2, NM_RBUFFER_CURRP) - card->abuf2; + u32 amtToRead = card->requestedRecAmt; + u32 ringsize = card->recordBufferSize; + + if (currptr >= card->recordBufferSize) { + printk (KERN_ERR "NM256: Sound buffer record pointer invalid!\n"); + currptr = 0; + } + + /* + * This test is probably redundant; we shouldn't be here unless + * it's true. + */ + if (card->recording) { + /* If we wrapped around, copy everything from the start of our + recording buffer to the end of the buffer. */ + if (currptr < card->curRecPos) { + u32 amt = MIN (ringsize - card->curRecPos, amtToRead); + + nm256_readBuffer8 (card, card->recBuf, 1, + card->abuf2 + card->curRecPos, + amt); + amtToRead -= amt; + card->curRecPos += amt; + card->recBuf += amt; + if (card->curRecPos == ringsize) + card->curRecPos = 0; + } + + if ((card->curRecPos < currptr) && (amtToRead > 0)) { + u32 amt = MIN (currptr - card->curRecPos, amtToRead); + nm256_readBuffer8 (card, card->recBuf, 1, + card->abuf2 + card->curRecPos, amt); + card->curRecPos = ((card->curRecPos + amt) % ringsize); + } + card->recBuf = NULL; + card->requestedRecAmt = 0; + DMAbuf_inputintr (card->dev_for_record); + } +} +#undef MIN + +/* + * Initialize the hardware. + */ +static void +nm256_initHw (struct nm256_info *card) +{ + /* Reset everything. */ + nm256_writePort8 (card, 2, 0x0, 0x11); + nm256_writePort16 (card, 2, 0x214, 0); + + stopRecord (card); + stopPlay (card); +} + +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * + * I don't like the cut-n-paste job here either between the two routines, + * but there are sufficient differences between the two interrupt handlers + * that parameterizing it isn't all that great either. (Could use a macro, + * I suppose...yucky bleah.) + */ + +static void +nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy) +{ + struct nm256_info *card = (struct nm256_info *)dev_id; + u16 status; + static int badintrcount = 0; + + if ((card == NULL) || (card->magsig != NM_MAGIC_SIG)) { + printk (KERN_ERR "NM256: Bad card pointer\n"); + return; + } + + status = nm256_readPort16 (card, 2, NM_INT_REG); + + /* Not ours. */ + if (status == 0) { + if (badintrcount++ > 1000) { + /* + * I'm not sure if the best thing is to stop the card from + * playing or just release the interrupt (after all, we're in + * a bad situation, so doing fancy stuff may not be such a good + * idea). + * + * I worry about the card engine continuing to play noise + * over and over, however--that could become a very + * obnoxious problem. And we know that when this usually + * happens things are fairly safe, it just means the user's + * inserted a PCMCIA card and someone's spamming us with IRQ 9s. + */ + + if (card->playing) + stopPlay (card); + if (card->recording) + stopRecord (card); + badintrcount = 0; + } + return; + } + + badintrcount = 0; + + /* Rather boring; check for individual interrupts and process them. */ + + if (status & NM_PLAYBACK_INT) { + status &= ~NM_PLAYBACK_INT; + NM_ACK_INT (card, NM_PLAYBACK_INT); + + if (card->playing) + nm256_get_new_block (card); + } + + if (status & NM_RECORD_INT) { + status &= ~NM_RECORD_INT; + NM_ACK_INT (card, NM_RECORD_INT); + + if (card->recording) + nm256_read_block (card); + } + + if (status & NM_MISC_INT_1) { + u8 cbyte; + + status &= ~NM_MISC_INT_1; + printk (KERN_ERR "NM256: Got misc interrupt #1\n"); + NM_ACK_INT (card, NM_MISC_INT_1); + nm256_writePort16 (card, 2, NM_INT_REG, 0x8000); + cbyte = nm256_readPort8 (card, 2, 0x400); + nm256_writePort8 (card, 2, 0x400, cbyte | 2); + } + + if (status & NM_MISC_INT_2) { + u8 cbyte; + + status &= ~NM_MISC_INT_2; + printk (KERN_ERR "NM256: Got misc interrupt #2\n"); + NM_ACK_INT (card, NM_MISC_INT_2); + cbyte = nm256_readPort8 (card, 2, 0x400); + nm256_writePort8 (card, 2, 0x400, cbyte & ~2); + } + + /* Unknown interrupt. */ + if (status) { + printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n", + status); + /* Pray. */ + NM_ACK_INT (card, status); + } +} + +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * This handler is for the 256ZX, and is very similar to the non-ZX + * routine. + */ + +static void +nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy) +{ + struct nm256_info *card = (struct nm256_info *)dev_id; + u32 status; + static int badintrcount = 0; + + if ((card == NULL) || (card->magsig != NM_MAGIC_SIG)) { + printk (KERN_ERR "NM256: Bad card pointer\n"); + return; + } + + status = nm256_readPort32 (card, 2, NM_INT_REG); + + /* Not ours. */ + if (status == 0) { + if (badintrcount++ > 1000) { + printk (KERN_ERR "NM256: Releasing interrupt, over 1000 invalid interrupts\n"); + /* + * I'm not sure if the best thing is to stop the card from + * playing or just release the interrupt (after all, we're in + * a bad situation, so doing fancy stuff may not be such a good + * idea). + * + * I worry about the card engine continuing to play noise + * over and over, however--that could become a very + * obnoxious problem. And we know that when this usually + * happens things are fairly safe, it just means the user's + * inserted a PCMCIA card and someone's spamming us with + * IRQ 9s. + */ + + if (card->playing) + stopPlay (card); + if (card->recording) + stopRecord (card); + badintrcount = 0; + } + return; + } + + badintrcount = 0; + + /* Rather boring; check for individual interrupts and process them. */ + + if (status & NM2_PLAYBACK_INT) { + status &= ~NM2_PLAYBACK_INT; + NM2_ACK_INT (card, NM2_PLAYBACK_INT); + + if (card->playing) + nm256_get_new_block (card); + } + + if (status & NM2_RECORD_INT) { + status &= ~NM2_RECORD_INT; + NM2_ACK_INT (card, NM2_RECORD_INT); + + if (card->recording) + nm256_read_block (card); + } + + if (status & NM2_MISC_INT_1) { + u8 cbyte; + + status &= ~NM2_MISC_INT_1; + printk (KERN_ERR "NM256: Got misc interrupt #1\n"); + NM2_ACK_INT (card, NM2_MISC_INT_1); + cbyte = nm256_readPort8 (card, 2, 0x400); + nm256_writePort8 (card, 2, 0x400, cbyte | 2); + } + + if (status & NM2_MISC_INT_2) { + u8 cbyte; + + status &= ~NM2_MISC_INT_2; + printk (KERN_ERR "NM256: Got misc interrupt #2\n"); + NM2_ACK_INT (card, NM2_MISC_INT_2); + cbyte = nm256_readPort8 (card, 2, 0x400); + nm256_writePort8 (card, 2, 0x400, cbyte & ~2); + } + + /* Unknown interrupt. */ + if (status) { + printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n", + status); + /* Pray. */ + NM2_ACK_INT (card, status); + } +} + +/* + * Request our interrupt. + */ +static int +nm256_grabInterrupt (struct nm256_info *card) +{ + if (card->has_irq++ == 0) { + if (request_irq (card->irq, card->introutine, SA_SHIRQ, + "NM256_audio", card) < 0) { + printk (KERN_ERR "NM256: can't obtain IRQ %d\n", card->irq); + return -1; + } + } + return 0; +} + +/* + * Release our interrupt. + */ +static int +nm256_releaseInterrupt (struct nm256_info *card) +{ + if (card->has_irq <= 0) { + printk (KERN_ERR "nm256: too many calls to releaseInterrupt\n"); + return -1; + } + card->has_irq--; + if (card->has_irq == 0) { + free_irq (card->irq, card); + } + return 0; +} + +/* + * Waits for the mixer to become ready to be written; returns a zero value + * if it timed out. + */ + +static int +nm256_isReady (struct ac97_hwint *dev) +{ + struct nm256_info *card = (struct nm256_info *)dev->driver_private; + int t2 = 10; + u32 testaddr; + u16 testb; + int done = 0; + + if (card->magsig != NM_MAGIC_SIG) { + printk (KERN_ERR "NM256: Bad magic signature in isReady!\n"); + return 0; + } + + testaddr = card->mixer_status_offset; + testb = card->mixer_status_mask; + + /* + * Loop around waiting for the mixer to become ready. + */ + while (! done && t2-- > 0) { + if ((nm256_readPort16 (card, 2, testaddr) & testb) == 0) + done = 1; + else + udelay (100); + } + return done; +} + +/* + * Return the contents of the AC97 mixer register REG. Returns a positive + * value if successful, or a negative error code. + */ +static int +nm256_readAC97Reg (struct ac97_hwint *dev, u8 reg) +{ + struct nm256_info *card = (struct nm256_info *)dev->driver_private; + + if (card->magsig != NM_MAGIC_SIG) { + printk (KERN_ERR "NM256: Bad magic signature in readAC97Reg!\n"); + return -EINVAL; + } + + if (reg < 128) { + int res; + + nm256_isReady (dev); + res = nm256_readPort16 (card, 2, card->mixer + reg); + /* Magic delay. Bleah yucky. */ + udelay (1000); + return res; + } + else + return -EINVAL; +} + +/* + * Writes VALUE to AC97 mixer register REG. Returns 0 if successful, or + * a negative error code. + */ +static int +nm256_writeAC97Reg (struct ac97_hwint *dev, u8 reg, u16 value) +{ + unsigned long flags; + int tries = 2; + int done = 0; + u32 base; + + struct nm256_info *card = (struct nm256_info *)dev->driver_private; + + if (card->magsig != NM_MAGIC_SIG) { + printk (KERN_ERR "NM256: Bad magic signature in writeAC97Reg!\n"); + return -EINVAL; + } + + base = card->mixer; + + save_flags (flags); + cli (); + + nm256_isReady (dev); + + /* Wait for the write to take, too. */ + while ((tries-- > 0) && !done) { + nm256_writePort16 (card, 2, base + reg, value); + if (nm256_isReady (dev)) { + done = 1; + break; + } + + } + + restore_flags (flags); + udelay (1000); + + return ! done; +} + +/* + * Initial register values to be written to the AC97 mixer. + * While most of these are identical to the reset values, we do this + * so that we have most of the register contents cached--this avoids + * reading from the mixer directly (which seems to be problematic, + * probably due to ignorance). + */ +struct initialValues +{ + unsigned short port; + unsigned short value; +}; + +static struct initialValues nm256_ac97_initial_values[] = +{ + { AC97_MASTER_VOL_STEREO, 0x8000 }, + { AC97_HEADPHONE_VOL, 0x8000 }, + { AC97_MASTER_VOL_MONO, 0x0000 }, + { AC97_PCBEEP_VOL, 0x0000 }, + { AC97_PHONE_VOL, 0x0008 }, + { AC97_MIC_VOL, 0x8000 }, + { AC97_LINEIN_VOL, 0x8808 }, + { AC97_CD_VOL, 0x8808 }, + { AC97_VIDEO_VOL, 0x8808 }, + { AC97_AUX_VOL, 0x8808 }, + { AC97_PCMOUT_VOL, 0x0808 }, + { AC97_RECORD_SELECT, 0x0000 }, + { AC97_RECORD_GAIN, 0x0B0B }, + { AC97_GENERAL_PURPOSE, 0x0000 }, + { 0xffff, 0xffff } +}; + +/* Initialize the AC97 into a known state. */ +static int +nm256_resetAC97 (struct ac97_hwint *dev) +{ + struct nm256_info *card = (struct nm256_info *)dev->driver_private; + int x; + + if (card->magsig != NM_MAGIC_SIG) { + printk (KERN_ERR "NM256: Bad magic signature in resetAC97!\n"); + return -EINVAL; + } + + /* Reset the mixer. 'Tis magic! */ + nm256_writePort8 (card, 2, 0x6c0, 1); + nm256_writePort8 (card, 2, 0x6cc, 0x87); + nm256_writePort8 (card, 2, 0x6cc, 0x80); + nm256_writePort8 (card, 2, 0x6cc, 0x0); + + if (! card->mixer_values_init) { + for (x = 0; nm256_ac97_initial_values[x].port != 0xffff; x++) { + ac97_put_register (dev, + nm256_ac97_initial_values[x].port, + nm256_ac97_initial_values[x].value); + card->mixer_values_init = 1; + } + } + + return 0; +} + +/* + * We don't do anything particularly special here; it just passes the + * mixer ioctl to the AC97 driver. + */ +static int +nm256_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + struct nm256_info *card = nm256_find_card_for_mixer (dev); + if (card != NULL) + return ac97_mixer_ioctl (&(card->mdev), cmd, arg); + else + return -ENODEV; +} + +static struct mixer_operations nm256_mixer_operations = { + owner: THIS_MODULE, + id: "NeoMagic", + name: "NM256AC97Mixer", + ioctl: nm256_default_mixer_ioctl +}; + +/* + * Default settings for the OSS mixer. These are set last, after the + * mixer is initialized. + * + * I "love" C sometimes. Got braces? + */ +static struct ac97_mixer_value_list mixer_defaults[] = { + { SOUND_MIXER_VOLUME, { { 85, 85 } } }, + { SOUND_MIXER_SPEAKER, { { 100 } } }, + { SOUND_MIXER_PCM, { { 65, 65 } } }, + { SOUND_MIXER_CD, { { 65, 65 } } }, + { -1, { { 0, 0 } } } +}; + + +/* Installs the AC97 mixer into CARD. */ +static int __init +nm256_install_mixer (struct nm256_info *card) +{ + int mixer; + + card->mdev.reset_device = nm256_resetAC97; + card->mdev.read_reg = nm256_readAC97Reg; + card->mdev.write_reg = nm256_writeAC97Reg; + card->mdev.driver_private = (void *)card; + + if (ac97_init (&(card->mdev))) + return -1; + + mixer = sound_alloc_mixerdev(); + if (num_mixers >= MAX_MIXER_DEV) { + printk ("NM256 mixer: Unable to alloc mixerdev\n"); + return -1; + } + + mixer_devs[mixer] = &nm256_mixer_operations; + card->mixer_oss_dev = mixer; + + /* Some reasonable default values. */ + ac97_set_values (&(card->mdev), mixer_defaults); + + printk(KERN_INFO "Initialized AC97 mixer\n"); + return 0; +} + +/* Perform a full reset on the hardware; this is invoked when an APM + resume event occurs. */ +static void +nm256_full_reset (struct nm256_info *card) +{ + nm256_initHw (card); + ac97_reset (&(card->mdev)); +} + +/* + * See if the signature left by the NM256 BIOS is intact; if so, we use + * the associated address as the end of our audio buffer in the video + * RAM. + */ + +static void __init +nm256_peek_for_sig (struct nm256_info *card) +{ + u32 port1offset + = card->port[0].physaddr + card->port[0].end_offset - 0x0400; + /* The signature is located 1K below the end of video RAM. */ + char *temp = ioremap_nocache (port1offset, 16); + /* Default buffer end is 5120 bytes below the top of RAM. */ + u32 default_value = card->port[0].end_offset - 0x1400; + u32 sig; + + /* Install the default value first, so we don't have to repeatedly + do it if there is a problem. */ + card->port[0].end_offset = default_value; + + if (temp == NULL) { + printk (KERN_ERR "NM256: Unable to scan for card signature in video RAM\n"); + return; + } + sig = readl (temp); + if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { + u32 pointer = readl (temp + 4); + + /* + * If it's obviously invalid, don't use it (the port already has a + * suitable default value set). + */ + if (pointer != 0xffffffff) + card->port[0].end_offset = pointer; + + printk (KERN_INFO "NM256: Found card signature in video RAM: 0x%x\n", + pointer); + } + + iounmap (temp); +} + +/* + * Install a driver for the PCI device referenced by PCIDEV. + * VERSTR is a human-readable version string. + */ + +static int __init +nm256_install(struct pci_dev *pcidev, enum nm256rev rev, char *verstr) +{ + struct nm256_info *card; + struct pm_dev *pmdev; + int x; + + if (pci_enable_device(pcidev)) + return 0; + + card = kmalloc (sizeof (struct nm256_info), GFP_KERNEL); + if (card == NULL) { + printk (KERN_ERR "NM256: out of memory!\n"); + return 0; + } + + card->magsig = NM_MAGIC_SIG; + card->playing = 0; + card->recording = 0; + card->rev = rev; + + /* Init the memory port info. */ + for (x = 0; x < 2; x++) { + card->port[x].physaddr = pci_resource_start (pcidev, x); + card->port[x].ptr = NULL; + card->port[x].start_offset = 0; + card->port[x].end_offset = 0; + } + + /* Port 2 is easy. */ + card->port[1].start_offset = 0; + card->port[1].end_offset = NM_PORT2_SIZE; + + /* Yuck. But we have to map in port 2 so we can check how much RAM the + card has. */ + if (nm256_remap_ports (card)) { + kfree (card); + return 0; + } + + /* + * The NM256 has two memory ports. The first port is nothing + * more than a chunk of video RAM, which is used as the I/O ring + * buffer. The second port has the actual juicy stuff (like the + * mixer and the playback engine control registers). + */ + + if (card->rev == REV_NM256AV) { + /* Ok, try to see if this is a non-AC97 version of the hardware. */ + int pval = nm256_readPort16 (card, 2, NM_MIXER_PRESENCE); + if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { + if (! force_load) { + 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 "\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"); + printk (KERN_ERR " IRQ and/or DMA for the sound card, this is *not* the correct\n"); + printk (KERN_ERR " driver to use.)\n"); + nm256_release_ports (card); + kfree (card); + return 0; + } + else { + printk (KERN_INFO "NM256: Forcing driver load as per user request.\n"); + } + } + else { + /* printk (KERN_INFO "NM256: Congratulations. You're not running Eunice.\n")*/; + } + card->port[0].end_offset = 2560 * 1024; + card->introutine = nm256_interrupt; + card->mixer_status_offset = NM_MIXER_STATUS_OFFSET; + card->mixer_status_mask = NM_MIXER_READY_MASK; + } + else { + /* Not sure if there is any relevant detect for the ZX or not. */ + if (nm256_readPort8 (card, 2, 0xa0b) != 0) + card->port[0].end_offset = 6144 * 1024; + else + card->port[0].end_offset = 4096 * 1024; + + card->introutine = nm256_interrupt_zx; + card->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; + card->mixer_status_mask = NM2_MIXER_READY_MASK; + } + + if (buffertop >= 98304 && buffertop < card->port[0].end_offset) + card->port[0].end_offset = buffertop; + else + nm256_peek_for_sig (card); + + card->port[0].start_offset = card->port[0].end_offset - 98304; + + printk (KERN_INFO "NM256: Mapping port 1 from 0x%x - 0x%x\n", + card->port[0].start_offset, card->port[0].end_offset); + + if (nm256_remap_ports (card)) { + kfree (card); + return 0; + } + + /* See if we can get the interrupt. */ + + card->irq = pcidev->irq; + card->has_irq = 0; + + if (nm256_grabInterrupt (card) != 0) { + nm256_release_ports (card); + kfree (card); + return 0; + } + + nm256_releaseInterrupt (card); + + /* + * Init the board. + */ + + card->playbackBufferSize = 16384; + card->recordBufferSize = 16384; + + card->coeffBuf = card->port[0].end_offset - NM_MAX_COEFFICIENT; + card->abuf2 = card->coeffBuf - card->recordBufferSize; + card->abuf1 = card->abuf2 - card->playbackBufferSize; + card->allCoeffBuf = card->abuf2 - (NM_TOTAL_COEFF_COUNT * 4); + + /* Fixed setting. */ + card->mixer = NM_MIXER_OFFSET; + card->mixer_values_init = 0; + + card->is_open_play = 0; + card->is_open_record = 0; + + card->coeffsCurrent = 0; + + card->opencnt[0] = 0; card->opencnt[1] = 0; + + /* Reasonable default settings, but largely unnecessary. */ + for (x = 0; x < 2; x++) { + card->sinfo[x].bits = 8; + card->sinfo[x].stereo = 0; + card->sinfo[x].samplerate = 8000; + } + + nm256_initHw (card); + + for (x = 0; x < 2; x++) { + if ((card->dev[x] = + sound_install_audiodrv(AUDIO_DRIVER_VERSION, + "NM256", &nm256_audio_driver, + sizeof(struct audio_driver), + DMA_NODMA, AFMT_U8 | AFMT_S16_LE, + NULL, -1, -1)) >= 0) { + /* 1K minimum buffer size. */ + audio_devs[card->dev[x]]->min_fragment = 10; + /* Maximum of 8K buffer size. */ + audio_devs[card->dev[x]]->max_fragment = 13; + } + else { + printk(KERN_ERR "NM256: Too many PCM devices available\n"); + nm256_release_ports (card); + kfree (card); + return 0; + } + } + + pci_set_drvdata(pcidev,card); + + /* Insert the card in the list. */ + card->next_card = nmcard_list; + nmcard_list = card; + + printk(KERN_INFO "Initialized NeoMagic %s audio in PCI native mode\n", + verstr); + + /* + * And our mixer. (We should allow support for other mixers, maybe.) + */ + + nm256_install_mixer (card); + + pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), handle_pm_event); + if (pmdev) + pmdev->data = card; + + return 1; +} + + +/* + * PM event handler, so the card is properly reinitialized after a power + * event. + */ +static int +handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct nm256_info *crd = (struct nm256_info*) dev->data; + if (crd) { + switch (rqst) { + case PM_SUSPEND: + break; + case PM_RESUME: + { + int playing = crd->playing; + nm256_full_reset (crd); + /* + * A little ugly, but that's ok; pretend the + * block we were playing is done. + */ + if (playing) + DMAbuf_outputintr (crd->dev_for_play, 1); + } + break; + } + } + return 0; +} + +static int __devinit +nm256_probe(struct pci_dev *pcidev,const struct pci_device_id *pciid) +{ + if (pcidev->device == PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO) + return nm256_install(pcidev, REV_NM256AV, "256AV"); + if (pcidev->device == PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO) + return nm256_install(pcidev, REV_NM256ZX, "256ZX"); + return -1; /* should not come here ... */ +} + +static void __devinit +nm256_remove(struct pci_dev *pcidev) { + struct nm256_info *xcard = pci_get_drvdata(pcidev); + struct nm256_info *card,*next_card = NULL; + + for (card = nmcard_list; card != NULL; card = next_card) { + next_card = card->next_card; + if (card == xcard) { + stopPlay (card); + stopRecord (card); + if (card->has_irq) + free_irq (card->irq, card); + nm256_release_ports (card); + sound_unload_mixerdev (card->mixer_oss_dev); + sound_unload_audiodev (card->dev[0]); + sound_unload_audiodev (card->dev[1]); + kfree (card); + break; + } + } + if (nmcard_list == card) + nmcard_list = next_card; +} + +/* + * Open the device + * + * DEV - device + * MODE - mode to open device (logical OR of OPEN_READ and OPEN_WRITE) + * + * Called when opening the DMAbuf (dmabuf.c:259) + */ +static int +nm256_audio_open(int dev, int mode) +{ + struct nm256_info *card = nm256_find_card (dev); + int w; + + if (card == NULL) + return -ENODEV; + + if (card->dev[0] == dev) + w = 0; + else if (card->dev[1] == dev) + w = 1; + else + return -ENODEV; + + if (card->opencnt[w] > 0) + return -EBUSY; + + /* No bits set? Huh? */ + if (! ((mode & OPEN_READ) || (mode & OPEN_WRITE))) + return -EIO; + + /* + * If it's open for both read and write, and the card's currently + * being read or written to, then do the opposite of what has + * already been done. Otherwise, don't specify any mode until the + * user actually tries to do I/O. (Some programs open the device + * for both read and write, but only actually do reading or writing.) + */ + + if ((mode & OPEN_WRITE) && (mode & OPEN_READ)) { + if (card->is_open_play) + mode = OPEN_WRITE; + else if (card->is_open_record) + mode = OPEN_READ; + else mode = 0; + } + + if (mode & OPEN_WRITE) { + if (card->is_open_play == 0) { + card->dev_for_play = dev; + card->is_open_play = 1; + } + else + return -EBUSY; + } + + if (mode & OPEN_READ) { + if (card->is_open_record == 0) { + card->dev_for_record = dev; + card->is_open_record = 1; + } + else + return -EBUSY; + } + + card->opencnt[w]++; + return 0; +} + +/* + * Close the device + * + * DEV - device + * + * Called when closing the DMAbuf (dmabuf.c:477) + * after halt_xfer + */ +static void +nm256_audio_close(int dev) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card != NULL) { + int w; + + if (card->dev[0] == dev) + w = 0; + else if (card->dev[1] == dev) + w = 1; + else + return; + + card->opencnt[w]--; + if (card->opencnt[w] <= 0) { + card->opencnt[w] = 0; + + if (card->dev_for_play == dev) { + stopPlay (card); + card->is_open_play = 0; + card->dev_for_play = -1; + } + + if (card->dev_for_record == dev) { + stopRecord (card); + card->is_open_record = 0; + card->dev_for_record = -1; + } + } + } +} + +/* Standard ioctl handler. */ +static int +nm256_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int ret; + u32 oldinfo; + int w; + + struct nm256_info *card = nm256_find_card (dev); + + if (card == NULL) + return -ENODEV; + + if (dev == card->dev[0]) + w = 0; + else + w = 1; + + /* + * The code here is messy. There are probably better ways to do + * it. (It should be possible to handle it the same way the AC97 mixer + * is done.) + */ + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (get_user(ret, (int *) arg)) + return -EFAULT; + + if (ret != 0) { + oldinfo = card->sinfo[w].samplerate; + card->sinfo[w].samplerate = ret; + ret = nm256_setInfo(dev, card); + if (ret != 0) + card->sinfo[w].samplerate = oldinfo; + } + if (ret == 0) + ret = card->sinfo[w].samplerate; + break; + + case SOUND_PCM_READ_RATE: + ret = card->sinfo[w].samplerate; + break; + + case SNDCTL_DSP_STEREO: + if (get_user(ret, (int *) arg)) + return -EFAULT; + + card->sinfo[w].stereo = ret ? 1 : 0; + ret = nm256_setInfo (dev, card); + if (ret == 0) + ret = card->sinfo[w].stereo; + + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(ret, (int *) arg)) + return -EFAULT; + + if (ret < 1 || ret > 3) + ret = card->sinfo[w].stereo + 1; + else { + card->sinfo[w].stereo = ret - 1; + ret = nm256_setInfo (dev, card); + if (ret == 0) + ret = card->sinfo[w].stereo + 1; + } + break; + + case SOUND_PCM_READ_CHANNELS: + ret = card->sinfo[w].stereo + 1; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(ret, (int *) arg)) + return -EFAULT; + + if (ret != 0) { + oldinfo = card->sinfo[w].bits; + card->sinfo[w].bits = ret; + ret = nm256_setInfo (dev, card); + if (ret != 0) + card->sinfo[w].bits = oldinfo; + } + if (ret == 0) + ret = card->sinfo[w].bits; + break; + + case SOUND_PCM_READ_BITS: + ret = card->sinfo[w].bits; + break; + + default: + return -EINVAL; + } + return put_user(ret, (int *) arg); +} + +/* + * Given the sound device DEV and an associated physical buffer PHYSBUF, + * return a pointer to the actual buffer in kernel space. + * + * This routine should exist as part of the soundcore routines. + */ + +static char * +nm256_getDMAbuffer (int dev, unsigned long physbuf) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + char *dma_start = + (char *)(physbuf - (unsigned long)dmap->raw_buf_phys + + (unsigned long)dmap->raw_buf); + + return dma_start; +} + + +/* + * Output a block to sound device + * + * dev - device number + * buf - physical address of buffer + * total_count - total byte count in buffer + * intrflag - set if this has been called from an interrupt + * (via DMAbuf_outputintr) + * restart_dma - set if engine needs to be re-initialised + * + * Called when: + * 1. Starting output (dmabuf.c:1327) + * 2. (dmabuf.c:1504) + * 3. A new buffer needs to be sent to the device (dmabuf.c:1579) + */ +static void +nm256_audio_output_block(int dev, unsigned long physbuf, + int total_count, int intrflag) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card != NULL) { + char *dma_buf = nm256_getDMAbuffer (dev, physbuf); + card->is_open_play = 1; + card->dev_for_play = dev; + nm256_write_block (card, dma_buf, total_count); + } +} + +/* Ditto, but do recording instead. */ +static void +nm256_audio_start_input(int dev, unsigned long physbuf, int count, + int intrflag) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card != NULL) { + char *dma_buf = nm256_getDMAbuffer (dev, physbuf); + card->is_open_record = 1; + card->dev_for_record = dev; + nm256_startRecording (card, dma_buf, count); + } +} + +/* + * Prepare for inputting samples to DEV. + * Each requested buffer will be BSIZE byes long, with a total of + * BCOUNT buffers. + */ + +static int +nm256_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card == NULL) + return -ENODEV; + + if (card->is_open_record && card->dev_for_record != dev) + return -EBUSY; + + audio_devs[dev]->dmap_in->flags |= DMA_NODMA; + return 0; +} + +/* + * Prepare for outputting samples to `dev' + * + * Each buffer that will be passed will be `bsize' bytes long, + * with a total of `bcount' buffers. + * + * Called when: + * 1. A trigger enables audio output (dmabuf.c:978) + * 2. We get a write buffer without dma_mode setup (dmabuf.c:1152) + * 3. We restart a transfer (dmabuf.c:1324) + */ + +static int +nm256_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card == NULL) + return -ENODEV; + + if (card->is_open_play && card->dev_for_play != dev) + return -EBUSY; + + audio_devs[dev]->dmap_out->flags |= DMA_NODMA; + return 0; +} + +/* Stop the current operations associated with DEV. */ +static void +nm256_audio_reset(int dev) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card != NULL) { + if (card->dev_for_play == dev) + stopPlay (card); + if (card->dev_for_record == dev) + stopRecord (card); + } +} + +static int +nm256_audio_local_qlen(int dev) +{ + return 0; +} + +static struct audio_driver nm256_audio_driver = +{ + owner: THIS_MODULE, + open: nm256_audio_open, + close: nm256_audio_close, + output_block: nm256_audio_output_block, + start_input: nm256_audio_start_input, + ioctl: nm256_audio_ioctl, + prepare_for_input: nm256_audio_prepare_for_input, + prepare_for_output:nm256_audio_prepare_for_output, + halt_io: nm256_audio_reset, + local_qlen: nm256_audio_local_qlen, +}; + +static struct pci_device_id nm256_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0}, + {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, nm256_pci_tbl); +MODULE_LICENSE("GPL"); + + +struct pci_driver nm256_pci_driver = { + name:"nm256_audio", + id_table:nm256_pci_tbl, + probe:nm256_probe, + remove:nm256_remove, +}; + +MODULE_PARM (usecache, "i"); +MODULE_PARM (buffertop, "i"); +MODULE_PARM (nm256_debug, "i"); +MODULE_PARM (force_load, "i"); + +static int __init do_init_nm256(void) +{ + printk (KERN_INFO "NeoMagic 256AV/256ZX audio driver, version 1.1p\n"); + return pci_module_init(&nm256_pci_driver); +} + +static void __exit cleanup_nm256 (void) +{ + pci_unregister_driver(&nm256_pci_driver); + pm_unregister_all (&handle_pm_event); +} + +module_init(do_init_nm256); +module_exit(cleanup_nm256); + +/* + * Local variables: + * c-basic-offset: 4 + * End: + */ diff -Nru a/sound/oss/nm256_coeff.h b/sound/oss/nm256_coeff.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/nm256_coeff.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,4697 @@ +#ifndef NM256_COEFF_H +#define NM256_COEFF_H + +#define NM_TOTAL_COEFF_COUNT 0x3158 + +static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { + 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21, + 0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01, + 0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, + 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, + 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFD, 0xFF, + 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, + 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, + 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x02, 0x00, 0x05, + 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, + 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, + 0x60, 0x00, 0xA4, 0xFF, 0xDE, 0xFF, 0xB5, 0x01, 0x5E, 0xF9, 0xE9, + 0x45, 0x62, 0x11, 0x87, 0xF7, 0x21, 0x05, 0xEF, 0xFC, 0xA5, 0x01, + 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, + 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, + 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, + 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xCA, 0x01, 0x95, 0xFC, 0xEA, 0x05, 0xBB, 0xF5, 0x25, 0x17, 0x3C, + 0x43, 0x8D, 0xF6, 0x43, 0x03, 0xF5, 0xFE, 0x26, 0x00, 0x20, 0x00, + 0xE2, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, + 0x01, 0x4C, 0xFC, 0x26, 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, + 0x8F, 0xF1, 0xCA, 0x06, 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD5, 0xFF, 0xBC, 0x00, + 0xF0, 0xFD, 0xEC, 0x04, 0xD9, 0xF3, 0xB1, 0x3E, 0xCD, 0x1E, 0xC1, + 0xF3, 0xAF, 0x06, 0x49, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, + 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, + 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0x98, 0x01, 0x0D, 0xFD, + 0xE0, 0x04, 0x14, 0xF8, 0xC3, 0x0F, 0x89, 0x46, 0x4C, 0xFA, 0x38, + 0x01, 0x25, 0x00, 0x7D, 0xFF, 0x73, 0x00, 0xC2, 0xFF, 0x0F, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, + 0x07, 0x84, 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, + 0x41, 0xFD, 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x15, 0x00, 0x97, 0xFF, 0x37, 0x01, 0x22, 0xFD, 0x23, 0x06, + 0x2F, 0xF2, 0x11, 0x39, 0x7B, 0x26, 0x50, 0xF2, 0x1B, 0x07, 0x32, + 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC8, 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, + 0xF9, 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, + 0xA2, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, + 0x00, 0x6A, 0xFF, 0x53, 0x01, 0xA6, 0xFD, 0xA6, 0x03, 0xA1, 0xFA, + 0xDE, 0x08, 0x76, 0x48, 0x0C, 0xFF, 0xDE, 0xFE, 0x73, 0x01, 0xC9, + 0xFE, 0xCA, 0x00, 0xA0, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, + 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, + 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x68, + 0xFF, 0x93, 0x01, 0x92, 0xFC, 0xE2, 0x06, 0x83, 0xF1, 0x8C, 0x32, + 0xED, 0x2D, 0x90, 0xF1, 0x1E, 0x07, 0x57, 0xFC, 0xBD, 0x01, 0x51, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, + 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, + 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, 0x03, 0x01, 0x53, + 0xFE, 0x53, 0x02, 0x39, 0xFD, 0xA9, 0x02, 0xF2, 0x48, 0xB9, 0x04, + 0x54, 0xFC, 0xCA, 0x02, 0x16, 0xFE, 0x20, 0x01, 0x7F, 0xFF, 0x20, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC3, 0x01, + 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, 0x43, 0x20, + 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, 0xDD, 0xFF, + 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCD, 0x01, 0x43, + 0xFC, 0x2A, 0x07, 0xBC, 0xF1, 0x64, 0x2B, 0xE3, 0x34, 0xA3, 0xF1, + 0xAE, 0x06, 0xBD, 0xFC, 0x77, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, 0xFD, + 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, 0xC8, + 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x14, 0x00, 0xAC, 0xFF, 0xAC, 0x00, 0x08, 0xFF, 0xFD, 0x00, 0xB5, + 0xFF, 0x4B, 0xFD, 0xF4, 0x47, 0x30, 0x0B, 0xBC, 0xF9, 0x17, 0x04, + 0x6E, 0xFD, 0x6D, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, 0x26, 0xFD, 0xAD, 0x04, + 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, 0xFB, 0xD4, 0x00, 0x5D, + 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, 0x10, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0x01, 0x07, 0xBE, + 0xF2, 0xD6, 0x23, 0x1F, 0x3B, 0xA5, 0xF2, 0xC5, 0x05, 0x62, 0xFD, + 0x10, 0x01, 0xAB, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, + 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, 0x4D, 0x06, 0x00, 0xF2, + 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, 0x07, 0x34, 0xFC, 0xDD, + 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, + 0x56, 0x00, 0xB9, 0xFF, 0xB8, 0xFF, 0xF7, 0x01, 0xE2, 0xF8, 0x8D, + 0x45, 0x46, 0x12, 0x3C, 0xF7, 0x43, 0x05, 0xDF, 0xFC, 0xAC, 0x01, + 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, + 0xFF, 0x46, 0x01, 0xC3, 0xFD, 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, + 0xA6, 0x48, 0xF8, 0xFF, 0x70, 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, + 0x00, 0x9A, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xDE, 0x01, 0x5D, 0xFC, 0x74, 0x06, 0x63, 0xF4, 0x23, 0x1C, 0x66, + 0x40, 0xAA, 0xF4, 0x65, 0x04, 0x44, 0xFE, 0x8B, 0x00, 0xEE, 0xFF, + 0xF5, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, + 0x01, 0x80, 0xFC, 0xF7, 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, + 0x83, 0xF1, 0x13, 0x07, 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, + 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xED, 0xFF, 0x05, 0x00, 0x5D, 0x00, + 0x95, 0xFE, 0xE2, 0x03, 0x7F, 0xF5, 0xCC, 0x41, 0xC7, 0x19, 0xFF, + 0xF4, 0x37, 0x06, 0x75, 0xFC, 0xD6, 0x01, 0x39, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1B, 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, + 0x02, 0xAA, 0xFD, 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, + 0x05, 0x03, 0xF7, 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBB, 0x01, 0xBA, 0xFC, + 0x95, 0x05, 0x83, 0xF6, 0x8C, 0x14, 0x87, 0x44, 0xBB, 0xF7, 0x98, + 0x02, 0x5A, 0xFF, 0xEE, 0xFF, 0x3C, 0x00, 0xD8, 0xFF, 0x0A, 0x00, + 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, + 0x07, 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, + 0xD5, 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, + 0x00, 0x07, 0x00, 0xBE, 0xFF, 0xEA, 0x00, 0xA2, 0xFD, 0x65, 0x05, + 0x28, 0xF3, 0xDB, 0x3C, 0x78, 0x21, 0x30, 0xF3, 0xDF, 0x06, 0x3A, + 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, + 0xB2, 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, + 0xFC, 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, + 0x79, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x58, 0xFF, 0x82, 0x01, 0x3F, 0xFD, 0x78, 0x04, 0xF2, 0xF8, + 0x50, 0x0D, 0x5E, 0x47, 0xD5, 0xFB, 0x6F, 0x00, 0x96, 0x00, 0x40, + 0xFF, 0x91, 0x00, 0xB7, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, + 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, + 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x85, + 0xFF, 0x5B, 0x01, 0xE9, 0xFC, 0x73, 0x06, 0xD8, 0xF1, 0xE5, 0x36, + 0x19, 0x29, 0xF8, 0xF1, 0x29, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x42, + 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, 0xFF, 0x47, 0x00, + 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, 0x8D, + 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, 0xFF, + 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x75, 0xFF, 0x39, + 0x01, 0xE0, 0xFD, 0x33, 0x03, 0x87, 0xFB, 0xA2, 0x06, 0xCB, 0x48, + 0xEA, 0x00, 0x01, 0xFE, 0xE9, 0x01, 0x8A, 0xFE, 0xE8, 0x00, 0x95, + 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, 0xDA, 0x01, + 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, 0x41, 0x1F, + 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, 0xF0, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5B, 0xFF, 0xAB, 0x01, 0x6F, + 0xFC, 0x08, 0x07, 0x7E, 0xF1, 0x21, 0x30, 0x67, 0x30, 0x7D, 0xF1, + 0x05, 0x07, 0x73, 0xFC, 0xA8, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, + 0xFF, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0x67, 0xFE, + 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, 0xA6, 0xF4, 0x5A, + 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x96, 0xFF, 0xE5, 0x00, 0x91, 0xFE, 0xDC, 0x01, 0x1A, + 0xFE, 0xB3, 0x00, 0xC3, 0x48, 0xE1, 0x06, 0x6E, 0xFB, 0x40, 0x03, + 0xDA, 0xFD, 0x3C, 0x01, 0x74, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, + 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, + 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x41, 0xFF, 0xD9, 0x01, 0x36, 0xFC, 0x28, 0x07, 0x01, + 0xF2, 0xCE, 0x28, 0x23, 0x37, 0xE0, 0xF1, 0x6B, 0x06, 0xEF, 0xFC, + 0x57, 0x01, 0x87, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, + 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, 0xFD, 0x9C, 0x05, 0xDC, 0xF2, + 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, 0xF3, 0x06, 0x35, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xB8, 0xFF, + 0x8E, 0x00, 0x46, 0xFF, 0x8A, 0x00, 0x86, 0x00, 0xA7, 0xFB, 0x48, + 0x47, 0x95, 0x0D, 0xD9, 0xF8, 0x84, 0x04, 0x39, 0xFD, 0x85, 0x01, + 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, + 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, + 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, + 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE6, 0x01, 0x3B, 0xFC, 0xDA, 0x06, 0x3F, 0xF3, 0x2C, 0x21, 0x11, + 0x3D, 0x3A, 0xF3, 0x58, 0x05, 0xAA, 0xFD, 0xE5, 0x00, 0xC1, 0xFF, + 0x06, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, + 0x01, 0xCF, 0xFC, 0x96, 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, + 0xD4, 0xF1, 0x2B, 0x07, 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, + 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD9, 0xFF, 0x39, 0x00, 0xF4, 0xFF, + 0x4E, 0xFF, 0xAC, 0x02, 0x98, 0xF7, 0x65, 0x44, 0xD6, 0x14, 0x6C, + 0xF6, 0x9F, 0x05, 0xB6, 0xFC, 0xBD, 0x01, 0x42, 0xFF, 0x32, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, + 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, + 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD5, 0x01, 0x78, 0xFC, + 0x2F, 0x06, 0x13, 0xF5, 0x7C, 0x19, 0xF7, 0x41, 0x9B, 0xF5, 0xD1, + 0x03, 0x9F, 0xFE, 0x57, 0x00, 0x08, 0x00, 0xEC, 0xFF, 0x06, 0x00, + 0xFD, 0xFF, 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, + 0x07, 0x85, 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, + 0x84, 0xFC, 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, + 0x00, 0xF6, 0xFF, 0xEB, 0xFF, 0x91, 0x00, 0x3B, 0xFE, 0x75, 0x04, + 0x92, 0xF4, 0x36, 0x40, 0x6E, 0x1C, 0x50, 0xF4, 0x7B, 0x06, 0x5B, + 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, + 0x9C, 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, + 0xFF, 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, + 0x49, 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x49, 0xFF, 0xAA, 0x01, 0xE4, 0xFC, 0x38, 0x05, 0x54, 0xF7, + 0xFE, 0x11, 0xAA, 0x45, 0x09, 0xF9, 0xE2, 0x01, 0xC4, 0xFF, 0xB3, + 0xFF, 0x59, 0x00, 0xCD, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, + 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, + 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA9, + 0xFF, 0x15, 0x01, 0x5B, 0xFD, 0xD0, 0x05, 0x97, 0xF2, 0xE6, 0x3A, + 0x21, 0x24, 0xB1, 0xF2, 0x04, 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x39, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, + 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, + 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, + 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x6A, + 0x01, 0x74, 0xFD, 0x0A, 0x04, 0xD5, 0xF9, 0xED, 0x0A, 0x03, 0x48, + 0x7C, 0xFD, 0x9E, 0xFF, 0x0A, 0x01, 0x01, 0xFF, 0xAF, 0x00, 0xAB, + 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, + 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, 0x3D, 0x91, + 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, 0x02, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x75, 0xFF, 0x7A, 0x01, 0xB8, + 0xFC, 0xB4, 0x06, 0x9E, 0xF1, 0xA2, 0x34, 0xAD, 0x2B, 0xB6, 0xF1, + 0x29, 0x07, 0x45, 0xFC, 0xCB, 0x01, 0x49, 0xFF, 0x31, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, 0xFF, + 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, 0xCA, + 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0x1C, 0x01, 0x1C, 0xFE, 0xBD, + 0x02, 0x6E, 0xFC, 0x7D, 0x04, 0xF3, 0x48, 0xE2, 0x02, 0x1F, 0xFD, + 0x60, 0x02, 0x4C, 0xFE, 0x06, 0x01, 0x89, 0xFF, 0x1D, 0x00, 0xFE, + 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, 0x88, 0xFC, 0x09, 0x06, + 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, 0xF6, 0x83, 0x03, 0xCF, + 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, + 0x2E, 0x00, 0x50, 0xFF, 0xBF, 0x01, 0x54, 0xFC, 0x20, 0x07, 0x94, + 0xF1, 0xA6, 0x2D, 0xD0, 0x32, 0x85, 0xF1, 0xDD, 0x06, 0x96, 0xFC, + 0x90, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, + 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, 0xB9, 0x04, 0x27, 0xF4, + 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, 0x06, 0x50, 0xFC, 0xE2, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA2, 0xFF, + 0xC7, 0x00, 0xD0, 0xFE, 0x65, 0x01, 0xF6, 0xFE, 0xD9, 0xFE, 0x6A, + 0x48, 0x1F, 0x09, 0x87, 0xFA, 0xB3, 0x03, 0xA0, 0xFD, 0x56, 0x01, + 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, + 0xFF, 0xA0, 0x01, 0xFB, 0xFC, 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, + 0x2B, 0x46, 0xBB, 0xF9, 0x83, 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, + 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, + 0xE1, 0x01, 0x31, 0xFC, 0x19, 0x07, 0x5B, 0xF2, 0x30, 0x26, 0x4B, + 0x39, 0x3B, 0xF2, 0x1A, 0x06, 0x29, 0xFD, 0x33, 0x01, 0x99, 0xFF, + 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, + 0x01, 0x3A, 0xFD, 0x00, 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, + 0x79, 0xF2, 0x12, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC4, 0xFF, 0x70, 0x00, 0x84, 0xFF, + 0x19, 0x00, 0x4D, 0x01, 0x22, 0xFA, 0x70, 0x46, 0x0A, 0x10, 0xFC, + 0xF7, 0xEB, 0x04, 0x08, 0xFD, 0x9A, 0x01, 0x4F, 0xFF, 0x2E, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, + 0xFD, 0xD2, 0x03, 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, + 0x33, 0xFF, 0x45, 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, + 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4B, 0xFC, + 0xA9, 0x06, 0xD2, 0xF3, 0x81, 0x1E, 0xE4, 0x3E, 0xEF, 0xF3, 0xDE, + 0x04, 0xF9, 0xFD, 0xB7, 0x00, 0xD8, 0xFF, 0xFD, 0xFF, 0x03, 0x00, + 0xFD, 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, + 0x06, 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, + 0x4E, 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE4, 0xFF, 0x1D, 0x00, 0x2D, 0x00, 0xEA, 0xFE, 0x56, 0x03, + 0x6D, 0xF6, 0x17, 0x43, 0x70, 0x17, 0xA6, 0xF5, 0xF3, 0x05, 0x91, + 0xFC, 0xCC, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x86, 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, + 0x03, 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, + 0x14, 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, + 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x99, 0xFC, 0xE1, 0x05, 0xD1, 0xF5, + 0xDC, 0x16, 0x65, 0x43, 0xAD, 0xF6, 0x31, 0x03, 0x00, 0xFF, 0x20, + 0x00, 0x23, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, + 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, + 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, + 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xD3, + 0xFF, 0xC1, 0x00, 0xE7, 0xFD, 0xFA, 0x04, 0xC4, 0xF3, 0x7E, 0x3E, + 0x19, 0x1F, 0xB0, 0xF3, 0xB5, 0x06, 0x47, 0xFC, 0xE4, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, 0x00, + 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, 0x47, + 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x96, + 0x01, 0x13, 0xFD, 0xD5, 0x04, 0x2C, 0xF8, 0x7D, 0x0F, 0xA3, 0x46, + 0x76, 0xFA, 0x22, 0x01, 0x32, 0x00, 0x76, 0xFF, 0x76, 0x00, 0xC1, + 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, 0xE4, 0x01, + 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, 0x3A, 0x74, + 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, 0x11, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x95, 0xFF, 0x3B, 0x01, 0x1B, + 0xFD, 0x2D, 0x06, 0x24, 0xF2, 0xD3, 0x38, 0xC6, 0x26, 0x45, 0xF2, + 0x1D, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, 0xFF, 0xE2, 0xFF, + 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, 0x8F, 0xF7, 0x1D, + 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x51, 0x01, 0xAC, 0xFD, 0x9A, + 0x03, 0xBA, 0xFA, 0x9E, 0x08, 0x81, 0x48, 0x40, 0xFF, 0xC6, 0xFE, + 0x80, 0x01, 0xC2, 0xFE, 0xCE, 0x00, 0x9F, 0xFF, 0x17, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, + 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, + 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFD, 0xFF, + 0x27, 0x00, 0x66, 0xFF, 0x96, 0x01, 0x8E, 0xFC, 0xE7, 0x06, 0x81, + 0xF1, 0x48, 0x32, 0x34, 0x2E, 0x8D, 0xF1, 0x1C, 0x07, 0x5A, 0xFC, + 0xBB, 0x01, 0x53, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, + 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, 0xFE, 0xA6, 0x03, 0xE4, 0xF5, + 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, 0x1A, 0x06, 0x81, 0xFC, 0xD2, + 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8B, 0xFF, + 0xFF, 0x00, 0x5A, 0xFE, 0x46, 0x02, 0x52, 0xFD, 0x70, 0x02, 0xED, + 0x48, 0xF5, 0x04, 0x3B, 0xFC, 0xD7, 0x02, 0x0F, 0xFE, 0x23, 0x01, + 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, + 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, + 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, + 0xCE, 0x01, 0x41, 0xFC, 0x2A, 0x07, 0xC2, 0xF1, 0x1B, 0x2B, 0x25, + 0x35, 0xA8, 0xF1, 0xA7, 0x06, 0xC2, 0xFC, 0x74, 0x01, 0x78, 0xFF, + 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, + 0x00, 0xBF, 0xFD, 0x38, 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, + 0x66, 0xF3, 0xCE, 0x06, 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAE, 0xFF, 0xA9, 0x00, 0x0F, 0xFF, + 0xF0, 0x00, 0xCD, 0xFF, 0x1B, 0xFD, 0xE4, 0x47, 0x73, 0x0B, 0xA2, + 0xF9, 0x23, 0x04, 0x68, 0xFD, 0x70, 0x01, 0x5F, 0xFF, 0x29, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, + 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, + 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, + 0xFD, 0x06, 0xCB, 0xF2, 0x8A, 0x23, 0x58, 0x3B, 0xB4, 0xF2, 0xBA, + 0x05, 0x6A, 0xFD, 0x0B, 0x01, 0xAE, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, + 0x06, 0xF7, 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, + 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, + 0x00, 0xCF, 0xFF, 0x52, 0x00, 0xC0, 0xFF, 0xAC, 0xFF, 0x0C, 0x02, + 0xBC, 0xF8, 0x6D, 0x45, 0x8E, 0x12, 0x24, 0xF7, 0x4D, 0x05, 0xDB, + 0xFC, 0xAE, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x24, 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, + 0xFB, 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, + 0xA3, 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xDD, 0x01, 0x60, 0xFC, 0x6D, 0x06, 0x76, 0xF4, + 0xD8, 0x1B, 0x95, 0x40, 0xC3, 0xF4, 0x56, 0x04, 0x4E, 0xFE, 0x85, + 0x00, 0xF1, 0xFF, 0xF4, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, + 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, + 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, + 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x02, + 0x00, 0x63, 0x00, 0x8A, 0xFE, 0xF3, 0x03, 0x63, 0xF5, 0xA1, 0x41, + 0x12, 0x1A, 0xEB, 0xF4, 0x3F, 0x06, 0x72, 0xFC, 0xD7, 0x01, 0x39, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, + 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, + 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, + 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBA, + 0x01, 0xBF, 0xFC, 0x8B, 0x05, 0x99, 0xF6, 0x43, 0x14, 0xA9, 0x44, + 0xDE, 0xF7, 0x85, 0x02, 0x65, 0xFF, 0xE7, 0xFF, 0x3F, 0x00, 0xD6, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD5, 0x01, + 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, 0x36, 0xC5, + 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, 0x1E, 0x00, + 0xFE, 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBC, 0xFF, 0xEF, 0x00, 0x9A, + 0xFD, 0x72, 0x05, 0x16, 0xF3, 0xA5, 0x3C, 0xC4, 0x21, 0x21, 0xF3, + 0xE4, 0x06, 0x39, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, 0x00, + 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, 0x5A, + 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x80, 0x01, 0x45, 0xFD, 0x6C, + 0x04, 0x0B, 0xF9, 0x0B, 0x0D, 0x73, 0x47, 0x02, 0xFC, 0x58, 0x00, + 0xA3, 0x00, 0x39, 0xFF, 0x94, 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, 0xFC, 0xEB, 0x06, + 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, 0xF2, 0x84, 0x05, 0x8D, + 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, + 0x1D, 0x00, 0x83, 0xFF, 0x5E, 0x01, 0xE3, 0xFC, 0x7B, 0x06, 0xD0, + 0xF1, 0xA5, 0x36, 0x62, 0x29, 0xEF, 0xF1, 0x29, 0x07, 0x39, 0xFC, + 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, + 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, 0x67, 0x02, 0x14, 0xF8, + 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, 0x05, 0xC5, 0xFC, 0xB7, + 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, + 0x76, 0xFF, 0x35, 0x01, 0xE7, 0xFD, 0x26, 0x03, 0xA1, 0xFB, 0x64, + 0x06, 0xD2, 0x48, 0x21, 0x01, 0xE8, 0xFD, 0xF7, 0x01, 0x83, 0xFE, + 0xEC, 0x00, 0x93, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, + 0xFF, 0xD9, 0x01, 0x6D, 0xFC, 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, + 0x5F, 0x41, 0x3A, 0xF5, 0x0C, 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, + 0xFF, 0xEF, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, + 0xAD, 0x01, 0x6C, 0xFC, 0x0C, 0x07, 0x7F, 0xF1, 0xDC, 0x2F, 0xAD, + 0x30, 0x7D, 0xF1, 0x01, 0x07, 0x76, 0xFC, 0xA6, 0x01, 0x5E, 0xFF, + 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, + 0x00, 0x5D, 0xFE, 0x3E, 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, + 0x93, 0xF4, 0x62, 0x06, 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x97, 0xFF, 0xE2, 0x00, 0x98, 0xFE, + 0xCF, 0x01, 0x33, 0xFE, 0x7D, 0x00, 0xBB, 0x48, 0x1F, 0x07, 0x54, + 0xFB, 0x4C, 0x03, 0xD3, 0xFD, 0x3F, 0x01, 0x73, 0xFF, 0x23, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, + 0xFC, 0x5D, 0x05, 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, + 0x2A, 0x02, 0x9A, 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, + 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDA, 0x01, 0x35, 0xFC, + 0x27, 0x07, 0x09, 0xF2, 0x85, 0x28, 0x63, 0x37, 0xE9, 0xF1, 0x63, + 0x06, 0xF5, 0xFC, 0x53, 0x01, 0x89, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x00, 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, + 0x05, 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, + 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, + 0x00, 0xB9, 0xFF, 0x8A, 0x00, 0x4D, 0xFF, 0x7D, 0x00, 0x9C, 0x00, + 0x7B, 0xFB, 0x31, 0x47, 0xD9, 0x0D, 0xC0, 0xF8, 0x8F, 0x04, 0x34, + 0xFD, 0x87, 0x01, 0x56, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, + 0xF9, 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, + 0x19, 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD5, 0x06, 0x4F, 0xF3, + 0xE0, 0x20, 0x45, 0x3D, 0x4D, 0xF3, 0x4B, 0x05, 0xB3, 0xFD, 0xE0, + 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, + 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, + 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, + 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xDA, 0xFF, 0x36, + 0x00, 0xFA, 0xFF, 0x43, 0xFF, 0xBF, 0x02, 0x75, 0xF7, 0x42, 0x44, + 0x20, 0x15, 0x55, 0xF6, 0xA9, 0x05, 0xB2, 0xFC, 0xBF, 0x01, 0x41, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, + 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, 0xEA, + 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, 0x00, + 0x8D, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD4, + 0x01, 0x7C, 0xFC, 0x27, 0x06, 0x28, 0xF5, 0x31, 0x19, 0x21, 0x42, + 0xB8, 0xF5, 0xC0, 0x03, 0xAA, 0xFE, 0x51, 0x00, 0x0B, 0x00, 0xEA, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, 0xB7, 0x01, + 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, 0x31, 0x7E, + 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, 0x28, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xE8, 0xFF, 0x96, 0x00, 0x31, + 0xFE, 0x84, 0x04, 0x79, 0xF4, 0x07, 0x40, 0xBA, 0x1C, 0x3E, 0xF4, + 0x82, 0x06, 0x58, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, 0xFE, 0x93, 0x01, + 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, 0xE1, 0xFA, 0x86, + 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA8, 0x01, 0xE9, 0xFC, 0x2D, + 0x05, 0x6B, 0xF7, 0xB6, 0x11, 0xC8, 0x45, 0x30, 0xF9, 0xCD, 0x01, + 0xD0, 0xFF, 0xAC, 0xFF, 0x5C, 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, + 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, + 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x10, 0x00, 0xA7, 0xFF, 0x19, 0x01, 0x53, 0xFD, 0xDB, 0x05, 0x88, + 0xF2, 0xAD, 0x3A, 0x6D, 0x24, 0xA4, 0xF2, 0x08, 0x07, 0x32, 0xFC, + 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBF, + 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, 0x00, 0x01, 0x01, 0xB6, 0xFA, + 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, 0xC4, 0x04, 0x1B, 0xFD, 0x92, + 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x63, 0xFF, 0x67, 0x01, 0x7A, 0xFD, 0xFE, 0x03, 0xEE, 0xF9, 0xAA, + 0x0A, 0x16, 0x48, 0xAC, 0xFD, 0x86, 0xFF, 0x17, 0x01, 0xFA, 0xFE, + 0xB3, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, + 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, + 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x73, 0xFF, + 0x7D, 0x01, 0xB3, 0xFC, 0xBB, 0x06, 0x9A, 0xF1, 0x60, 0x34, 0xF5, + 0x2B, 0xB0, 0xF1, 0x28, 0x07, 0x47, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, + 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, + 0x00, 0x10, 0xFF, 0x15, 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, + 0xF1, 0xF5, 0xD3, 0x05, 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x19, 0x01, + 0x23, 0xFE, 0xB0, 0x02, 0x87, 0xFC, 0x41, 0x04, 0xF4, 0x48, 0x1C, + 0x03, 0x06, 0xFD, 0x6E, 0x02, 0x45, 0xFE, 0x09, 0x01, 0x88, 0xFF, + 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, + 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, + 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4F, 0xFF, 0xC1, 0x01, 0x52, 0xFC, + 0x22, 0x07, 0x98, 0xF1, 0x5E, 0x2D, 0x13, 0x33, 0x87, 0xF1, 0xD8, + 0x06, 0x9B, 0xFC, 0x8D, 0x01, 0x6B, 0xFF, 0x25, 0x00, 0xFD, 0xFF, + 0x03, 0x00, 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, + 0x04, 0x10, 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, + 0x4E, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, + 0x00, 0xA3, 0xFF, 0xC3, 0x00, 0xD7, 0xFE, 0x58, 0x01, 0x0F, 0xFF, + 0xA6, 0xFE, 0x5D, 0x48, 0x61, 0x09, 0x6E, 0xFA, 0xC0, 0x03, 0x99, + 0xFD, 0x59, 0x01, 0x68, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2E, 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, + 0xF7, 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, + 0x8E, 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x16, 0x07, 0x67, 0xF2, + 0xE5, 0x25, 0x87, 0x39, 0x47, 0xF2, 0x10, 0x06, 0x30, 0xFD, 0x2F, + 0x01, 0x9C, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x00, + 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, + 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC5, 0xFF, 0x6D, + 0x00, 0x8B, 0xFF, 0x0D, 0x00, 0x63, 0x01, 0xF9, 0xF9, 0x55, 0x46, + 0x51, 0x10, 0xE3, 0xF7, 0xF7, 0x04, 0x03, 0xFD, 0x9D, 0x01, 0x4E, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, + 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, + 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, + 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, + 0x01, 0x4D, 0xFC, 0xA3, 0x06, 0xE4, 0xF3, 0x36, 0x1E, 0x16, 0x3F, + 0x05, 0xF4, 0xCF, 0x04, 0x02, 0xFE, 0xB2, 0x00, 0xDB, 0xFF, 0xFC, + 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, 0x8B, 0x01, + 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, 0x2D, 0x9A, + 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, 0x2F, 0x00, + 0xFD, 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x1A, 0x00, 0x33, 0x00, 0xDF, + 0xFE, 0x68, 0x03, 0x4E, 0xF6, 0xEE, 0x42, 0xBB, 0x17, 0x90, 0xF5, + 0xFC, 0x05, 0x8E, 0xFC, 0xCD, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, 0x02, + 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, 0xA9, + 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC7, 0x01, 0x9D, 0xFC, 0xD8, + 0x05, 0xE7, 0xF5, 0x91, 0x16, 0x89, 0x43, 0xCD, 0xF6, 0x1E, 0x03, + 0x0B, 0xFF, 0x1A, 0x00, 0x26, 0x00, 0xE0, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, 0x48, 0xFC, 0x28, 0x07, + 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, 0xF1, 0xBE, 0x06, 0xB0, + 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, + 0x00, 0x00, 0xD0, 0xFF, 0xC7, 0x00, 0xDE, 0xFD, 0x08, 0x05, 0xB0, + 0xF3, 0x4A, 0x3E, 0x64, 0x1F, 0xA0, 0xF3, 0xBB, 0x06, 0x45, 0xFC, + 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA9, + 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, 0x7A, 0xFF, 0xC5, 0xFD, + 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, 0x03, 0x7D, 0xFD, 0x66, + 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, + 0x52, 0xFF, 0x93, 0x01, 0x18, 0xFD, 0xC9, 0x04, 0x45, 0xF8, 0x36, + 0x0F, 0xBB, 0x46, 0xA1, 0xFA, 0x0C, 0x01, 0x3E, 0x00, 0x70, 0xFF, + 0x7A, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, + 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, + 0x8F, 0x3A, 0x82, 0xF2, 0xE1, 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, + 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x17, 0x00, 0x93, 0xFF, + 0x3F, 0x01, 0x15, 0xFD, 0x36, 0x06, 0x19, 0xF2, 0x97, 0x38, 0x11, + 0x27, 0x3B, 0xF2, 0x1F, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, + 0xFF, 0xD6, 0xFF, 0xC3, 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, + 0x77, 0xF7, 0x28, 0x05, 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6D, 0xFF, 0x4E, 0x01, + 0xB3, 0xFD, 0x8D, 0x03, 0xD4, 0xFA, 0x5D, 0x08, 0x8D, 0x48, 0x74, + 0xFF, 0xAE, 0xFE, 0x8D, 0x01, 0xBB, 0xFE, 0xD1, 0x00, 0x9E, 0xFF, + 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, + 0xFC, 0x85, 0x06, 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, + 0x8C, 0x04, 0x2C, 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, + 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x65, 0xFF, 0x98, 0x01, 0x8A, 0xFC, + 0xEC, 0x06, 0x7F, 0xF1, 0x04, 0x32, 0x7B, 0x2E, 0x8A, 0xF1, 0x1A, + 0x07, 0x5D, 0xFC, 0xB8, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, + 0x06, 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, + 0x03, 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, + 0x7D, 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, + 0x00, 0x8D, 0xFF, 0xFC, 0x00, 0x61, 0xFE, 0x39, 0x02, 0x6B, 0xFD, + 0x37, 0x02, 0xEB, 0x48, 0x31, 0x05, 0x21, 0xFC, 0xE4, 0x02, 0x08, + 0xFE, 0x26, 0x01, 0x7C, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x32, 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, + 0xF6, 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, + 0xFE, 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x32, + 0x00, 0x47, 0xFF, 0xD0, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xCA, 0xF1, + 0xD1, 0x2A, 0x65, 0x35, 0xAE, 0xF1, 0xA0, 0x06, 0xC7, 0xFC, 0x70, + 0x01, 0x7A, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, + 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, + 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA5, + 0x00, 0x16, 0xFF, 0xE3, 0x00, 0xE4, 0xFF, 0xEB, 0xFC, 0xD2, 0x47, + 0xB6, 0x0B, 0x89, 0xF9, 0x2F, 0x04, 0x62, 0xFD, 0x72, 0x01, 0x5E, + 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x56, 0xFF, + 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, 0x26, + 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, 0x00, + 0xBA, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, + 0x01, 0x34, 0xFC, 0xF9, 0x06, 0xD9, 0xF2, 0x3F, 0x23, 0x90, 0x3B, + 0xC4, 0xF2, 0xAE, 0x05, 0x72, 0xFD, 0x07, 0x01, 0xB0, 0xFF, 0x0C, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, 0x51, 0x01, + 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, 0x28, 0x0E, + 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x4F, 0x00, 0xC7, 0xFF, 0xA0, + 0xFF, 0x20, 0x02, 0x96, 0xF8, 0x4E, 0x45, 0xD7, 0x12, 0x0D, 0xF7, + 0x58, 0x05, 0xD6, 0xFC, 0xB0, 0x01, 0x47, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, 0x01, 0xD0, 0xFD, + 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, 0x62, 0x00, 0x3F, + 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, 0xFF, 0x19, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x63, 0xFC, 0x66, + 0x06, 0x89, 0xF4, 0x8C, 0x1B, 0xC3, 0x40, 0xDD, 0xF4, 0x46, 0x04, + 0x58, 0xFE, 0x80, 0x00, 0xF4, 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, + 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, + 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, + 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, + 0xEF, 0xFF, 0xFF, 0xFF, 0x69, 0x00, 0x80, 0xFE, 0x04, 0x04, 0x48, + 0xF5, 0x74, 0x41, 0x5D, 0x1A, 0xD7, 0xF4, 0x47, 0x06, 0x6F, 0xFC, + 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x93, + 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, 0x01, 0xDC, 0xFD, 0x3C, 0x01, + 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, 0x1F, 0x03, 0xEA, 0xFD, 0x34, + 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, + 0x44, 0xFF, 0xB8, 0x01, 0xC3, 0xFC, 0x81, 0x05, 0xB0, 0xF6, 0xFA, + 0x13, 0xCC, 0x44, 0x02, 0xF8, 0x71, 0x02, 0x71, 0xFF, 0xE1, 0xFF, + 0x42, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, + 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, + 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, + 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x09, 0x00, 0xBA, 0xFF, + 0xF4, 0x00, 0x91, 0xFD, 0x7E, 0x05, 0x05, 0xF3, 0x6E, 0x3C, 0x10, + 0x22, 0x12, 0xF3, 0xE9, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, + 0xFF, 0xA9, 0x00, 0x4D, 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, + 0x18, 0xF9, 0x66, 0x04, 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5A, 0xFF, 0x7D, 0x01, + 0x4B, 0xFD, 0x60, 0x04, 0x24, 0xF9, 0xC6, 0x0C, 0x86, 0x47, 0x30, + 0xFC, 0x41, 0x00, 0xB0, 0x00, 0x32, 0xFF, 0x98, 0x00, 0xB4, 0xFF, + 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, + 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, + 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x81, 0xFF, 0x62, 0x01, 0xDD, 0xFC, + 0x83, 0x06, 0xC9, 0xF1, 0x66, 0x36, 0xAC, 0x29, 0xE7, 0xF1, 0x2A, + 0x07, 0x3A, 0xFC, 0xD5, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, + 0x02, 0xF0, 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, + 0xC1, 0xFC, 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x77, 0xFF, 0x32, 0x01, 0xED, 0xFD, 0x19, 0x03, + 0xBB, 0xFB, 0x26, 0x06, 0xD7, 0x48, 0x58, 0x01, 0xCF, 0xFD, 0x04, + 0x02, 0x7D, 0xFE, 0xEF, 0x00, 0x92, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, + 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, + 0xF4, 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, + 0x66, 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2B, + 0x00, 0x59, 0xFF, 0xB0, 0x01, 0x69, 0xFC, 0x0F, 0x07, 0x80, 0xF1, + 0x96, 0x2F, 0xF2, 0x30, 0x7C, 0xF1, 0xFD, 0x06, 0x7A, 0xFC, 0xA3, + 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF4, 0xFF, + 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, + 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x98, 0xFF, 0xDE, + 0x00, 0x9F, 0xFE, 0xC2, 0x01, 0x4B, 0xFE, 0x48, 0x00, 0xB3, 0x48, + 0x5E, 0x07, 0x3B, 0xFB, 0x59, 0x03, 0xCD, 0xFD, 0x42, 0x01, 0x71, + 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, + 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, + 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, + 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDB, + 0x01, 0x35, 0xFC, 0x25, 0x07, 0x13, 0xF2, 0x3A, 0x28, 0xA0, 0x37, + 0xF2, 0xF1, 0x5A, 0x06, 0xFB, 0xFC, 0x4F, 0x01, 0x8B, 0xFF, 0x1A, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, 0x09, 0x01, + 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, 0x23, 0xD2, + 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x11, 0x00, 0xBB, 0xFF, 0x87, 0x00, 0x54, 0xFF, 0x70, + 0x00, 0xB3, 0x00, 0x4E, 0xFB, 0x1A, 0x47, 0x1F, 0x0E, 0xA8, 0xF8, + 0x9B, 0x04, 0x2E, 0xFD, 0x8A, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, 0xFD, + 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, 0xD9, + 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD0, + 0x06, 0x5E, 0xF3, 0x94, 0x20, 0x7B, 0x3D, 0x60, 0xF3, 0x3E, 0x05, + 0xBB, 0xFD, 0xDB, 0x00, 0xC6, 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, + 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, 0xC4, 0xFC, 0xA4, 0x06, + 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, 0xF1, 0x2A, 0x07, 0x40, + 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, + 0xDB, 0xFF, 0x33, 0x00, 0x01, 0x00, 0x38, 0xFF, 0xD3, 0x02, 0x53, + 0xF7, 0x1F, 0x44, 0x69, 0x15, 0x3F, 0xF6, 0xB2, 0x05, 0xAD, 0xFC, + 0xC1, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, + 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, 0xDE, 0x02, 0x2E, 0xFC, + 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, 0xFD, 0x3F, 0x02, 0x5D, + 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3B, 0xFF, 0xD3, 0x01, 0x7F, 0xFC, 0x1F, 0x06, 0x3C, 0xF5, 0xE6, + 0x18, 0x4D, 0x42, 0xD5, 0xF5, 0xAF, 0x03, 0xB4, 0xFE, 0x4B, 0x00, + 0x0E, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, + 0xFF, 0xBA, 0x01, 0x5B, 0xFC, 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, + 0x26, 0x32, 0x80, 0xF1, 0xEA, 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, + 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, 0xFF, 0xE6, 0xFF, + 0x9C, 0x00, 0x27, 0xFE, 0x94, 0x04, 0x61, 0xF4, 0xD7, 0x3F, 0x06, + 0x1D, 0x2B, 0xF4, 0x89, 0x06, 0x56, 0xFC, 0xE0, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, + 0xFE, 0x86, 0x01, 0xBA, 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, + 0xC7, 0xFA, 0x93, 0x03, 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA6, 0x01, + 0xEE, 0xFC, 0x23, 0x05, 0x83, 0xF7, 0x6E, 0x11, 0xE5, 0x45, 0x57, + 0xF9, 0xB8, 0x01, 0xDC, 0xFF, 0xA5, 0xFF, 0x5F, 0x00, 0xCA, 0xFF, + 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, + 0xFC, 0x1E, 0x07, 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, + 0x32, 0x06, 0x18, 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA4, 0xFF, 0x1D, 0x01, 0x4C, 0xFD, + 0xE6, 0x05, 0x7B, 0xF2, 0x71, 0x3A, 0xB8, 0x24, 0x97, 0xF2, 0x0B, + 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x0F, 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, + 0x01, 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, + 0x15, 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x64, 0xFF, 0x65, 0x01, 0x81, 0xFD, 0xF2, 0x03, + 0x08, 0xFA, 0x68, 0x0A, 0x25, 0x48, 0xDE, 0xFD, 0x6E, 0xFF, 0x24, + 0x01, 0xF3, 0xFE, 0xB6, 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, + 0xF3, 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, + 0xC4, 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x23, + 0x00, 0x71, 0xFF, 0x81, 0x01, 0xAE, 0xFC, 0xC1, 0x06, 0x95, 0xF1, + 0x1E, 0x34, 0x3E, 0x2C, 0xAB, 0xF1, 0x27, 0x07, 0x49, 0xFC, 0xC8, + 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, + 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, + 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, + 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, + 0xFF, 0x16, 0x01, 0x2A, 0xFE, 0xA3, 0x02, 0xA1, 0xFC, 0x06, 0x04, + 0xF5, 0x48, 0x56, 0x03, 0xED, 0xFC, 0x7B, 0x02, 0x3E, 0xFE, 0x0C, + 0x01, 0x86, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, 0x02, + 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, 0x00, + 0xE4, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, + 0x01, 0x4F, 0xFC, 0x24, 0x07, 0x9C, 0xF1, 0x17, 0x2D, 0x57, 0x33, + 0x8A, 0xF1, 0xD3, 0x06, 0x9F, 0xFC, 0x8A, 0x01, 0x6D, 0xFF, 0x25, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, 0xB4, 0x00, + 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, + 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC0, 0x00, 0xDE, 0xFE, 0x4B, + 0x01, 0x27, 0xFF, 0x73, 0xFE, 0x4F, 0x48, 0xA2, 0x09, 0x54, 0xFA, + 0xCC, 0x03, 0x93, 0xFD, 0x5C, 0x01, 0x67, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, 0x01, 0x05, 0xFD, + 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, 0x0D, 0xFA, 0x58, + 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x14, + 0x07, 0x73, 0xF2, 0x99, 0x25, 0xC2, 0x39, 0x54, 0xF2, 0x05, 0x06, + 0x37, 0xFD, 0x2B, 0x01, 0x9E, 0xFF, 0x13, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, + 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, + 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC6, 0xFF, 0x69, 0x00, 0x91, 0xFF, 0x00, 0x00, 0x78, 0x01, 0xD0, + 0xF9, 0x39, 0x46, 0x98, 0x10, 0xCB, 0xF7, 0x02, 0x05, 0xFE, 0xFC, + 0x9F, 0x01, 0x4D, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, + 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, 0xFD, 0xB9, 0x03, 0x7B, 0xFA, + 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, 0x03, 0xFF, 0x5F, 0x01, 0xD4, + 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE2, 0x01, 0x4F, 0xFC, 0x9C, 0x06, 0xF5, 0xF3, 0xEA, + 0x1D, 0x47, 0x3F, 0x1B, 0xF4, 0xC1, 0x04, 0x0B, 0xFE, 0xAC, 0x00, + 0xDE, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, + 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, + 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x17, 0x00, + 0x39, 0x00, 0xD4, 0xFE, 0x7A, 0x03, 0x2F, 0xF6, 0xC7, 0x42, 0x06, + 0x18, 0x7B, 0xF5, 0x05, 0x06, 0x8A, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, + 0xFE, 0x67, 0x02, 0x13, 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, + 0x7A, 0xFC, 0xB6, 0x02, 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, + 0xA1, 0xFC, 0xCF, 0x05, 0xFC, 0xF5, 0x47, 0x16, 0xB0, 0x43, 0xEE, + 0xF6, 0x0C, 0x03, 0x16, 0xFF, 0x14, 0x00, 0x29, 0x00, 0xDF, 0xFF, + 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, + 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, + 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCE, 0xFF, 0xCC, 0x00, 0xD5, 0xFD, + 0x16, 0x05, 0x9B, 0xF3, 0x18, 0x3E, 0xB1, 0x1F, 0x8F, 0xF3, 0xC0, + 0x06, 0x43, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x15, 0x00, 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, + 0xFF, 0x94, 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, + 0x77, 0xFD, 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x91, 0x01, 0x1E, 0xFD, 0xBE, 0x04, + 0x5E, 0xF8, 0xF0, 0x0E, 0xD3, 0x46, 0xCB, 0xFA, 0xF6, 0x00, 0x4B, + 0x00, 0x69, 0xFF, 0x7D, 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, + 0xF2, 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, + 0x17, 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, + 0x00, 0x91, 0xFF, 0x43, 0x01, 0x0E, 0xFD, 0x40, 0x06, 0x0F, 0xF2, + 0x5B, 0x38, 0x5C, 0x27, 0x30, 0xF2, 0x21, 0x07, 0x33, 0xFC, 0xDE, + 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCC, 0xFF, + 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, + 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, + 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6E, + 0xFF, 0x4B, 0x01, 0xB9, 0xFD, 0x80, 0x03, 0xEE, 0xFA, 0x1D, 0x08, + 0x98, 0x48, 0xA8, 0xFF, 0x95, 0xFE, 0x9A, 0x01, 0xB4, 0xFE, 0xD4, + 0x00, 0x9C, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, + 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, + 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9B, + 0x01, 0x86, 0xFC, 0xF1, 0x06, 0x7E, 0xF1, 0xC0, 0x31, 0xC2, 0x2E, + 0x87, 0xF1, 0x17, 0x07, 0x5F, 0xFC, 0xB6, 0x01, 0x55, 0xFF, 0x2D, + 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, 0x54, 0x00, + 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, 0x19, 0x1E, + 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1C, 0x00, 0x8E, 0xFF, 0xF9, 0x00, 0x68, 0xFE, 0x2C, + 0x02, 0x84, 0xFD, 0xFF, 0x01, 0xE6, 0x48, 0x6E, 0x05, 0x07, 0xFC, + 0xF1, 0x02, 0x01, 0xFE, 0x29, 0x01, 0x7B, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, 0xFC, + 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, 0xB6, + 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, 0x00, + 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3E, 0xFC, 0x2B, + 0x07, 0xD0, 0xF1, 0x89, 0x2A, 0xA6, 0x35, 0xB4, 0xF1, 0x99, 0x06, + 0xCD, 0xFC, 0x6D, 0x01, 0x7C, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, + 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, 0xAE, 0xFD, 0x52, 0x05, + 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, 0xF3, 0xD8, 0x06, 0x3C, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, + 0xB0, 0xFF, 0xA2, 0x00, 0x1D, 0xFF, 0xD6, 0x00, 0xFC, 0xFF, 0xBC, + 0xFC, 0xC0, 0x47, 0xFA, 0x0B, 0x70, 0xF9, 0x3C, 0x04, 0x5C, 0xFD, + 0x75, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, 0x89, 0x04, 0xCD, 0xF8, + 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, 0x00, 0x83, 0x00, 0x4A, + 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF5, 0x06, 0xE7, 0xF2, 0xF2, + 0x22, 0xC7, 0x3B, 0xD4, 0xF2, 0xA2, 0x05, 0x7A, 0xFD, 0x02, 0x01, + 0xB2, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, + 0xFF, 0x55, 0x01, 0xF2, 0xFC, 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, + 0xAA, 0x28, 0x05, 0xF2, 0x27, 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, + 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD2, 0xFF, 0x4C, 0x00, + 0xCD, 0xFF, 0x94, 0xFF, 0x34, 0x02, 0x70, 0xF8, 0x2E, 0x45, 0x20, + 0x13, 0xF6, 0xF6, 0x62, 0x05, 0xD1, 0xFC, 0xB2, 0x01, 0x46, 0xFF, + 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, + 0x01, 0xD6, 0xFD, 0x46, 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, + 0x98, 0x00, 0x26, 0xFE, 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, + 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, + 0x66, 0xFC, 0x5E, 0x06, 0x9C, 0xF4, 0x40, 0x1B, 0xEF, 0x40, 0xF7, + 0xF4, 0x35, 0x04, 0x62, 0xFE, 0x7A, 0x00, 0xF7, 0xFF, 0xF2, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, + 0xFC, 0x03, 0x07, 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, + 0x0A, 0x07, 0x6E, 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, + 0xFF, 0x05, 0x00, 0xF0, 0xFF, 0xFC, 0xFF, 0x6E, 0x00, 0x76, 0xFE, + 0x15, 0x04, 0x2C, 0xF5, 0x49, 0x41, 0xA9, 0x1A, 0xC3, 0xF4, 0x4F, + 0x06, 0x6C, 0xFC, 0xD9, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, + 0xFD, 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, + 0xE4, 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB6, 0x01, 0xC8, 0xFC, 0x77, 0x05, + 0xC7, 0xF6, 0xB1, 0x13, 0xED, 0x44, 0x26, 0xF8, 0x5D, 0x02, 0x7D, + 0xFF, 0xDA, 0xFF, 0x46, 0x00, 0xD4, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, + 0xF1, 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, + 0x5C, 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, + 0x00, 0xB7, 0xFF, 0xF9, 0x00, 0x89, 0xFD, 0x8A, 0x05, 0xF4, 0xF2, + 0x37, 0x3C, 0x5B, 0x22, 0x03, 0xF3, 0xED, 0x06, 0x37, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB6, 0xFF, + 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, + 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, + 0xFF, 0x7A, 0x01, 0x50, 0xFD, 0x54, 0x04, 0x3D, 0xF9, 0x82, 0x0C, + 0x9A, 0x47, 0x5E, 0xFC, 0x2A, 0x00, 0xBD, 0x00, 0x2B, 0xFF, 0x9B, + 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, 0xC0, + 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, 0xFF, + 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x80, 0xFF, 0x66, + 0x01, 0xD8, 0xFC, 0x8B, 0x06, 0xC1, 0xF1, 0x27, 0x36, 0xF6, 0x29, + 0xDF, 0xF1, 0x2A, 0x07, 0x3B, 0xFC, 0xD4, 0x01, 0x44, 0xFF, 0x32, + 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, 0xEA, 0xFF, + 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, 0x14, 0x8E, + 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, 0x32, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x79, 0xFF, 0x2F, 0x01, 0xF4, + 0xFD, 0x0C, 0x03, 0xD4, 0xFB, 0xE9, 0x05, 0xDE, 0x48, 0x8F, 0x01, + 0xB6, 0xFD, 0x11, 0x02, 0x76, 0xFE, 0xF2, 0x00, 0x91, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, 0x01, 0x73, 0xFC, + 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, 0x71, 0xF5, 0xEB, + 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, 0xFF, 0x06, 0x00, + 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB2, 0x01, 0x65, 0xFC, 0x12, + 0x07, 0x82, 0xF1, 0x50, 0x2F, 0x38, 0x31, 0x7C, 0xF1, 0xF9, 0x06, + 0x7E, 0xFC, 0xA1, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, + 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, + 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, + 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, + 0x9A, 0xFF, 0xDB, 0x00, 0xA6, 0xFE, 0xB4, 0x01, 0x64, 0xFE, 0x12, + 0x00, 0xAA, 0x48, 0x9E, 0x07, 0x21, 0xFB, 0x66, 0x03, 0xC6, 0xFD, + 0x45, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, 0xFC, 0x48, 0x05, 0x30, 0xF7, + 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, 0x01, 0x02, 0xB2, 0xFF, 0xBD, + 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x24, 0x07, 0x1C, 0xF2, 0xF0, + 0x27, 0xDF, 0x37, 0xFB, 0xF1, 0x51, 0x06, 0x01, 0xFD, 0x4B, 0x01, + 0x8D, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, + 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, + 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBC, 0xFF, 0x84, 0x00, + 0x5B, 0xFF, 0x64, 0x00, 0xC9, 0x00, 0x22, 0xFB, 0x02, 0x47, 0x64, + 0x0E, 0x8F, 0xF8, 0xA7, 0x04, 0x29, 0xFD, 0x8C, 0x01, 0x54, 0xFF, + 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, + 0x01, 0x6B, 0xFD, 0x1D, 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, + 0x33, 0xFD, 0xC1, 0xFF, 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, + 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x40, 0xFC, 0xCB, 0x06, 0x6E, 0xF3, 0x49, 0x20, 0xB0, 0x3D, 0x73, + 0xF3, 0x31, 0x05, 0xC4, 0xFD, 0xD6, 0x00, 0xC8, 0xFF, 0x03, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, + 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, + 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x2F, 0x00, 0x07, 0x00, 0x2C, 0xFF, + 0xE6, 0x02, 0x31, 0xF7, 0xFA, 0x43, 0xB3, 0x15, 0x29, 0xF6, 0xBC, + 0x05, 0xA9, 0xFC, 0xC2, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, + 0x02, 0x47, 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, + 0x4D, 0x02, 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFE, + 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, 0x83, 0xFC, 0x16, 0x06, + 0x51, 0xF5, 0x9B, 0x18, 0x75, 0x42, 0xF3, 0xF5, 0x9D, 0x03, 0xBF, + 0xFE, 0x45, 0x00, 0x11, 0x00, 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, + 0x2E, 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, + 0xF1, 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, + 0x94, 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF9, + 0xFF, 0xE3, 0xFF, 0xA1, 0x00, 0x1E, 0xFE, 0xA3, 0x04, 0x49, 0xF4, + 0xA8, 0x3F, 0x52, 0x1D, 0x19, 0xF4, 0x90, 0x06, 0x53, 0xFC, 0xE1, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, + 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, + 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, + 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, + 0xFF, 0xA3, 0x01, 0xF3, 0xFC, 0x18, 0x05, 0x9B, 0xF7, 0x27, 0x11, + 0x02, 0x46, 0x7F, 0xF9, 0xA3, 0x01, 0xE8, 0xFF, 0x9F, 0xFF, 0x63, + 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, + 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, + 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, + 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA2, 0xFF, 0x22, + 0x01, 0x45, 0xFD, 0xF1, 0x05, 0x6D, 0xF2, 0x38, 0x3A, 0x03, 0x25, + 0x8B, 0xF2, 0x0E, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x3A, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, 0x7A, 0xFF, + 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, 0x0F, 0x20, + 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, 0x2E, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x62, 0x01, 0x87, + 0xFD, 0xE5, 0x03, 0x21, 0xFA, 0x25, 0x0A, 0x33, 0x48, 0x0F, 0xFE, + 0x57, 0xFF, 0x31, 0x01, 0xEC, 0xFE, 0xB9, 0x00, 0xA7, 0xFF, 0x15, + 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, 0xFC, + 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, 0xF3, + 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, 0x00, + 0xFE, 0xFF, 0x23, 0x00, 0x70, 0xFF, 0x84, 0x01, 0xA9, 0xFC, 0xC7, + 0x06, 0x91, 0xF1, 0xDC, 0x33, 0x87, 0x2C, 0xA5, 0xF1, 0x26, 0x07, + 0x4B, 0xFC, 0xC6, 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, 0xFA, 0xFE, 0x3A, 0x03, + 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, 0xF5, 0xE6, 0x05, 0x97, + 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, + 0x1E, 0x00, 0x84, 0xFF, 0x13, 0x01, 0x31, 0xFE, 0x95, 0x02, 0xBA, + 0xFC, 0xCB, 0x03, 0xF7, 0x48, 0x91, 0x03, 0xD3, 0xFC, 0x88, 0x02, + 0x38, 0xFE, 0x10, 0x01, 0x85, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, + 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, 0xEF, 0x05, 0xB0, 0xF5, + 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, 0x03, 0xEF, 0xFE, 0x2A, + 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4D, 0xFF, 0xC4, 0x01, 0x4D, 0xFC, 0x25, 0x07, 0xA1, 0xF1, 0xCE, + 0x2C, 0x99, 0x33, 0x8E, 0xF1, 0xCD, 0x06, 0xA4, 0xFC, 0x87, 0x01, + 0x6E, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD7, + 0xFF, 0xBA, 0x00, 0xF4, 0xFD, 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, + 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBD, 0x00, + 0xE5, 0xFE, 0x3E, 0x01, 0x3F, 0xFF, 0x41, 0xFE, 0x41, 0x48, 0xE4, + 0x09, 0x3B, 0xFA, 0xD9, 0x03, 0x8D, 0xFD, 0x5F, 0x01, 0x66, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, + 0x01, 0x0B, 0xFD, 0xE6, 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, + 0x37, 0xFA, 0x42, 0x01, 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, + 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, + 0x31, 0xFC, 0x11, 0x07, 0x7F, 0xF2, 0x4E, 0x25, 0xFD, 0x39, 0x60, + 0xF2, 0xFB, 0x05, 0x3E, 0xFD, 0x26, 0x01, 0xA0, 0xFF, 0x12, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, + 0xFD, 0x1E, 0x06, 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, + 0x1A, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x66, 0x00, 0x98, 0xFF, 0xF4, 0xFF, + 0x8E, 0x01, 0xA7, 0xF9, 0x1D, 0x46, 0xDF, 0x10, 0xB3, 0xF7, 0x0D, + 0x05, 0xF8, 0xFC, 0xA1, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, + 0x03, 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, + 0x6C, 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x51, 0xFC, 0x96, 0x06, + 0x07, 0xF4, 0x9E, 0x1D, 0x77, 0x3F, 0x32, 0xF4, 0xB2, 0x04, 0x15, + 0xFE, 0xA7, 0x00, 0xE0, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, + 0x26, 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, + 0xF1, 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, + 0xBE, 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE7, + 0xFF, 0x14, 0x00, 0x3F, 0x00, 0xC9, 0xFE, 0x8C, 0x03, 0x11, 0xF6, + 0x9E, 0x42, 0x50, 0x18, 0x66, 0xF5, 0x0D, 0x06, 0x86, 0xFC, 0xD0, + 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, + 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, + 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, + 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC4, 0x01, 0xA5, 0xFC, 0xC5, 0x05, 0x13, 0xF6, 0xFD, 0x15, + 0xD4, 0x43, 0x0F, 0xF7, 0xF9, 0x02, 0x21, 0xFF, 0x0D, 0x00, 0x2C, + 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, 0xFF, + 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, 0xC3, + 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, 0xFF, + 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, 0xCB, 0xFF, 0xD1, + 0x00, 0xCC, 0xFD, 0x24, 0x05, 0x87, 0xF3, 0xE4, 0x3D, 0xFD, 0x1F, + 0x7F, 0xF3, 0xC6, 0x06, 0x41, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, 0x05, 0xFF, + 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, 0x0B, 0xC8, + 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, 0x28, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x8F, 0x01, 0x23, + 0xFD, 0xB2, 0x04, 0x76, 0xF8, 0xAA, 0x0E, 0xED, 0x46, 0xF7, 0xFA, + 0xDF, 0x00, 0x57, 0x00, 0x62, 0xFF, 0x80, 0x00, 0xBD, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x33, 0xFC, + 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, 0x9E, 0xF2, 0xCB, + 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x18, 0x00, 0x8F, 0xFF, 0x47, 0x01, 0x08, 0xFD, 0x49, + 0x06, 0x05, 0xF2, 0x1D, 0x38, 0xA6, 0x27, 0x26, 0xF2, 0x23, 0x07, + 0x33, 0xFC, 0xDD, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, + 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, + 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, + 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x24, 0x00, 0x6F, 0xFF, 0x48, 0x01, 0xC0, 0xFD, 0x73, 0x03, 0x07, + 0xFB, 0xDD, 0x07, 0xA1, 0x48, 0xDD, 0xFF, 0x7D, 0xFE, 0xA7, 0x01, + 0xAD, 0xFE, 0xD8, 0x00, 0x9B, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, 0xFC, 0x78, 0x06, 0x5A, 0xF4, + 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, 0x6D, 0x04, 0x3F, 0xFE, 0x8E, + 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x62, 0xFF, 0x9E, 0x01, 0x82, 0xFC, 0xF5, 0x06, 0x7D, 0xF1, 0x7B, + 0x31, 0x09, 0x2F, 0x84, 0xF1, 0x15, 0x07, 0x62, 0xFC, 0xB4, 0x01, + 0x56, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, + 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, + 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x8F, 0xFF, 0xF5, 0x00, + 0x6F, 0xFE, 0x1E, 0x02, 0x9D, 0xFD, 0xC7, 0x01, 0xE1, 0x48, 0xAB, + 0x05, 0xEE, 0xFB, 0xFE, 0x02, 0xFB, 0xFD, 0x2C, 0x01, 0x7A, 0xFF, + 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, + 0x01, 0xB8, 0xFC, 0x9A, 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, + 0xA9, 0xF7, 0xA2, 0x02, 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD3, 0x01, + 0x3C, 0xFC, 0x2A, 0x07, 0xD8, 0xF1, 0x3F, 0x2A, 0xE6, 0x35, 0xBB, + 0xF1, 0x92, 0x06, 0xD2, 0xFC, 0x69, 0x01, 0x7E, 0xFF, 0x1F, 0x00, + 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, + 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, + 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0x9F, 0x00, 0x24, 0xFF, 0xC9, 0x00, + 0x13, 0x00, 0x8D, 0xFC, 0xAE, 0x47, 0x3E, 0x0C, 0x56, 0xF9, 0x48, + 0x04, 0x56, 0xFD, 0x78, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, + 0x04, 0xE6, 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, + 0x90, 0x00, 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xF1, 0x06, + 0xF5, 0xF2, 0xA7, 0x22, 0xFF, 0x3B, 0xE4, 0xF2, 0x96, 0x05, 0x81, + 0xFD, 0xFD, 0x00, 0xB5, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, + 0x1C, 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, + 0xF1, 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, + 0xD8, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, + 0xFF, 0x49, 0x00, 0xD4, 0xFF, 0x88, 0xFF, 0x49, 0x02, 0x4B, 0xF8, + 0x0D, 0x45, 0x68, 0x13, 0xDF, 0xF6, 0x6C, 0x05, 0xCC, 0xFC, 0xB4, + 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, + 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, + 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDA, 0x01, 0x69, 0xFC, 0x57, 0x06, 0xAF, 0xF4, 0xF5, 0x1A, + 0x1D, 0x41, 0x11, 0xF5, 0x25, 0x04, 0x6C, 0xFE, 0x74, 0x00, 0xF9, + 0xFF, 0xF1, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, + 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, + 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, + 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF1, 0xFF, 0xF9, 0xFF, 0x74, + 0x00, 0x6C, 0xFE, 0x25, 0x04, 0x11, 0xF5, 0x1D, 0x41, 0xF5, 0x1A, + 0xAF, 0xF4, 0x57, 0x06, 0x69, 0xFC, 0xDA, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, 0x8E, 0xFE, + 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, 0x06, 0x7B, + 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, 0x23, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB4, 0x01, 0xCC, + 0xFC, 0x6C, 0x05, 0xDF, 0xF6, 0x68, 0x13, 0x0D, 0x45, 0x4B, 0xF8, + 0x49, 0x02, 0x88, 0xFF, 0xD4, 0xFF, 0x49, 0x00, 0xD3, 0xFF, 0x0B, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, 0xFC, + 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, 0x6F, + 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, + 0x01, 0x00, 0x0B, 0x00, 0xB5, 0xFF, 0xFD, 0x00, 0x81, 0xFD, 0x96, + 0x05, 0xE4, 0xF2, 0xFF, 0x3B, 0xA7, 0x22, 0xF5, 0xF2, 0xF1, 0x06, + 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, + 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, 0x90, 0x00, 0x7A, 0x00, + 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, 0xF8, 0x7E, 0x04, 0x3C, + 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x5C, 0xFF, 0x78, 0x01, 0x56, 0xFD, 0x48, 0x04, 0x56, + 0xF9, 0x3E, 0x0C, 0xAE, 0x47, 0x8D, 0xFC, 0x13, 0x00, 0xC9, 0x00, + 0x24, 0xFF, 0x9F, 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, 0xDD, 0x06, 0x37, 0xF3, + 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, 0x05, 0xA6, 0xFD, 0xE8, + 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, + 0x7E, 0xFF, 0x69, 0x01, 0xD2, 0xFC, 0x92, 0x06, 0xBB, 0xF1, 0xE6, + 0x35, 0x3F, 0x2A, 0xD8, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, + 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, + 0x00, 0xF1, 0xFF, 0x54, 0xFF, 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, + 0xB1, 0x14, 0x77, 0xF6, 0x9A, 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, + 0x2C, 0x01, 0xFB, 0xFD, 0xFE, 0x02, 0xEE, 0xFB, 0xAB, 0x05, 0xE1, + 0x48, 0xC7, 0x01, 0x9D, 0xFD, 0x1E, 0x02, 0x6F, 0xFE, 0xF5, 0x00, + 0x8F, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, + 0x01, 0x77, 0xFC, 0x33, 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, + 0x8D, 0xF5, 0xDA, 0x03, 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x56, 0xFF, 0xB4, 0x01, + 0x62, 0xFC, 0x15, 0x07, 0x84, 0xF1, 0x09, 0x2F, 0x7B, 0x31, 0x7D, + 0xF1, 0xF5, 0x06, 0x82, 0xFC, 0x9E, 0x01, 0x62, 0xFF, 0x28, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, + 0xFE, 0x6D, 0x04, 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, + 0x78, 0x06, 0x5C, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x18, 0x00, 0x9B, 0xFF, 0xD8, 0x00, 0xAD, 0xFE, 0xA7, 0x01, + 0x7D, 0xFE, 0xDD, 0xFF, 0xA1, 0x48, 0xDD, 0x07, 0x07, 0xFB, 0x73, + 0x03, 0xC0, 0xFD, 0x48, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, + 0x05, 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, + 0xBE, 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDD, 0x01, 0x33, 0xFC, 0x23, 0x07, + 0x26, 0xF2, 0xA6, 0x27, 0x1D, 0x38, 0x05, 0xF2, 0x49, 0x06, 0x08, + 0xFD, 0x47, 0x01, 0x8F, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x0E, 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, + 0xF2, 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, + 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBD, + 0xFF, 0x80, 0x00, 0x62, 0xFF, 0x57, 0x00, 0xDF, 0x00, 0xF7, 0xFA, + 0xED, 0x46, 0xAA, 0x0E, 0x76, 0xF8, 0xB2, 0x04, 0x23, 0xFD, 0x8F, + 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, + 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, + 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x41, 0xFC, 0xC6, 0x06, 0x7F, 0xF3, 0xFD, 0x1F, + 0xE4, 0x3D, 0x87, 0xF3, 0x24, 0x05, 0xCC, 0xFD, 0xD1, 0x00, 0xCB, + 0xFF, 0x02, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, 0xFF, + 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, 0x89, + 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2C, 0x00, 0x0D, + 0x00, 0x21, 0xFF, 0xF9, 0x02, 0x0F, 0xF7, 0xD4, 0x43, 0xFD, 0x15, + 0x13, 0xF6, 0xC5, 0x05, 0xA5, 0xFC, 0xC4, 0x01, 0x40, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0x1E, 0x01, + 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, 0x48, 0xC6, + 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, 0x8A, 0xFF, + 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD0, 0x01, 0x86, + 0xFC, 0x0D, 0x06, 0x66, 0xF5, 0x50, 0x18, 0x9E, 0x42, 0x11, 0xF6, + 0x8C, 0x03, 0xC9, 0xFE, 0x3F, 0x00, 0x14, 0x00, 0xE7, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, 0x01, 0x56, 0xFC, + 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, 0x84, 0xF1, 0xE0, + 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, + 0x03, 0x00, 0xFA, 0xFF, 0xE0, 0xFF, 0xA7, 0x00, 0x15, 0xFE, 0xB2, + 0x04, 0x32, 0xF4, 0x77, 0x3F, 0x9E, 0x1D, 0x07, 0xF4, 0x96, 0x06, + 0x51, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, + 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, + 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, + 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2F, 0x00, 0x4C, 0xFF, 0xA1, 0x01, 0xF8, 0xFC, 0x0D, 0x05, 0xB3, + 0xF7, 0xDF, 0x10, 0x1D, 0x46, 0xA7, 0xF9, 0x8E, 0x01, 0xF4, 0xFF, + 0x98, 0xFF, 0x66, 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, 0xFC, 0x1A, 0x07, 0x56, 0xF2, + 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, 0x1E, 0x06, 0x25, 0xFD, 0x35, + 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x12, 0x00, + 0xA0, 0xFF, 0x26, 0x01, 0x3E, 0xFD, 0xFB, 0x05, 0x60, 0xF2, 0xFD, + 0x39, 0x4E, 0x25, 0x7F, 0xF2, 0x11, 0x07, 0x31, 0xFC, 0xE3, 0x01, + 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, + 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, + 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, + 0x5F, 0x01, 0x8D, 0xFD, 0xD9, 0x03, 0x3B, 0xFA, 0xE4, 0x09, 0x41, + 0x48, 0x41, 0xFE, 0x3F, 0xFF, 0x3E, 0x01, 0xE5, 0xFE, 0xBD, 0x00, + 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x4A, 0xFC, 0xAC, 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, + 0xE4, 0xF3, 0xE5, 0x04, 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, + 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6E, 0xFF, 0x87, 0x01, + 0xA4, 0xFC, 0xCD, 0x06, 0x8E, 0xF1, 0x99, 0x33, 0xCE, 0x2C, 0xA1, + 0xF1, 0x25, 0x07, 0x4D, 0xFC, 0xC4, 0x01, 0x4D, 0xFF, 0x2F, 0x00, + 0xFD, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, + 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, + 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x85, 0xFF, 0x10, 0x01, 0x38, 0xFE, 0x88, 0x02, + 0xD3, 0xFC, 0x91, 0x03, 0xF7, 0x48, 0xCB, 0x03, 0xBA, 0xFC, 0x95, + 0x02, 0x31, 0xFE, 0x13, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, + 0x05, 0xC6, 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, + 0xFA, 0xFE, 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4C, 0xFF, 0xC6, 0x01, 0x4B, 0xFC, 0x26, 0x07, + 0xA5, 0xF1, 0x87, 0x2C, 0xDC, 0x33, 0x91, 0xF1, 0xC7, 0x06, 0xA9, + 0xFC, 0x84, 0x01, 0x70, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x03, 0x00, + 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, + 0xF3, 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, + 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA7, + 0xFF, 0xB9, 0x00, 0xEC, 0xFE, 0x31, 0x01, 0x57, 0xFF, 0x0F, 0xFE, + 0x33, 0x48, 0x25, 0x0A, 0x21, 0xFA, 0xE5, 0x03, 0x87, 0xFD, 0x62, + 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, + 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, + 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, + 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, + 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0E, 0x07, 0x8B, 0xF2, 0x03, 0x25, + 0x38, 0x3A, 0x6D, 0xF2, 0xF1, 0x05, 0x45, 0xFD, 0x22, 0x01, 0xA2, + 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, + 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, + 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, + 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x63, 0x00, 0x9F, + 0xFF, 0xE8, 0xFF, 0xA3, 0x01, 0x7F, 0xF9, 0x02, 0x46, 0x27, 0x11, + 0x9B, 0xF7, 0x18, 0x05, 0xF3, 0xFC, 0xA3, 0x01, 0x4C, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, 0x52, 0x01, + 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, 0x48, 0x26, + 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, 0xA0, 0xFF, + 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE1, 0x01, 0x53, + 0xFC, 0x90, 0x06, 0x19, 0xF4, 0x52, 0x1D, 0xA8, 0x3F, 0x49, 0xF4, + 0xA3, 0x04, 0x1E, 0xFE, 0xA1, 0x00, 0xE3, 0xFF, 0xF9, 0xFF, 0x04, + 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, 0xFC, + 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, 0x1D, + 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x07, 0x00, 0xE8, 0xFF, 0x11, 0x00, 0x45, 0x00, 0xBF, 0xFE, 0x9D, + 0x03, 0xF3, 0xF5, 0x75, 0x42, 0x9B, 0x18, 0x51, 0xF5, 0x16, 0x06, + 0x83, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, + 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, 0x4D, 0x02, 0x45, 0xFD, + 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, 0xFC, 0xD1, 0x02, 0x12, + 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x33, 0x00, 0x40, 0xFF, 0xC2, 0x01, 0xA9, 0xFC, 0xBC, 0x05, 0x29, + 0xF6, 0xB3, 0x15, 0xFA, 0x43, 0x31, 0xF7, 0xE6, 0x02, 0x2C, 0xFF, + 0x07, 0x00, 0x2F, 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, + 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, 0x2A, 0x07, 0xBF, 0xF1, + 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, 0x06, 0xBF, 0xFC, 0x75, + 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, + 0xC8, 0xFF, 0xD6, 0x00, 0xC4, 0xFD, 0x31, 0x05, 0x73, 0xF3, 0xB0, + 0x3D, 0x49, 0x20, 0x6E, 0xF3, 0xCB, 0x06, 0x40, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, + 0x00, 0x0C, 0xFF, 0xF7, 0x00, 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, + 0x51, 0x0B, 0xAF, 0xF9, 0x1D, 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, + 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, + 0x8C, 0x01, 0x29, 0xFD, 0xA7, 0x04, 0x8F, 0xF8, 0x64, 0x0E, 0x02, + 0x47, 0x22, 0xFB, 0xC9, 0x00, 0x64, 0x00, 0x5B, 0xFF, 0x84, 0x00, + 0xBC, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0xFF, 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, + 0xAD, 0xF2, 0xBF, 0x05, 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8D, 0xFF, 0x4B, 0x01, + 0x01, 0xFD, 0x51, 0x06, 0xFB, 0xF1, 0xDF, 0x37, 0xF0, 0x27, 0x1C, + 0xF2, 0x24, 0x07, 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, + 0xFF, 0x01, 0x02, 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, + 0x48, 0x05, 0xDD, 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x45, 0x01, 0xC6, 0xFD, + 0x66, 0x03, 0x21, 0xFB, 0x9E, 0x07, 0xAA, 0x48, 0x12, 0x00, 0x64, + 0xFE, 0xB4, 0x01, 0xA6, 0xFE, 0xDB, 0x00, 0x9A, 0xFF, 0x19, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, + 0x06, 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, + 0x49, 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFD, + 0xFF, 0x29, 0x00, 0x61, 0xFF, 0xA1, 0x01, 0x7E, 0xFC, 0xF9, 0x06, + 0x7C, 0xF1, 0x38, 0x31, 0x50, 0x2F, 0x82, 0xF1, 0x12, 0x07, 0x65, + 0xFC, 0xB2, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, + 0xED, 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, + 0xF5, 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, + 0xD7, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, + 0xFF, 0xF2, 0x00, 0x76, 0xFE, 0x11, 0x02, 0xB6, 0xFD, 0x8F, 0x01, + 0xDE, 0x48, 0xE9, 0x05, 0xD4, 0xFB, 0x0C, 0x03, 0xF4, 0xFD, 0x2F, + 0x01, 0x79, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, + 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, + 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, + 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, + 0xFF, 0xD4, 0x01, 0x3B, 0xFC, 0x2A, 0x07, 0xDF, 0xF1, 0xF6, 0x29, + 0x27, 0x36, 0xC1, 0xF1, 0x8B, 0x06, 0xD8, 0xFC, 0x66, 0x01, 0x80, + 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xBD, 0xFF, + 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, 0x9E, + 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x9B, 0x00, 0x2B, + 0xFF, 0xBD, 0x00, 0x2A, 0x00, 0x5E, 0xFC, 0x9A, 0x47, 0x82, 0x0C, + 0x3D, 0xF9, 0x54, 0x04, 0x50, 0xFD, 0x7A, 0x01, 0x5B, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x81, 0x01, + 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, 0x47, 0xEB, + 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, 0xB6, 0xFF, + 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, + 0xFC, 0xED, 0x06, 0x03, 0xF3, 0x5B, 0x22, 0x37, 0x3C, 0xF4, 0xF2, + 0x8A, 0x05, 0x89, 0xFD, 0xF9, 0x00, 0xB7, 0xFF, 0x0A, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, 0x01, 0xE6, 0xFC, + 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, 0xF3, 0xF1, 0x29, + 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD4, 0xFF, 0x46, 0x00, 0xDA, 0xFF, 0x7D, 0xFF, 0x5D, + 0x02, 0x26, 0xF8, 0xED, 0x44, 0xB1, 0x13, 0xC7, 0xF6, 0x77, 0x05, + 0xC8, 0xFC, 0xB6, 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, + 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, + 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x35, 0x00, 0x38, 0xFF, 0xD9, 0x01, 0x6C, 0xFC, 0x4F, 0x06, 0xC3, + 0xF4, 0xA9, 0x1A, 0x49, 0x41, 0x2C, 0xF5, 0x15, 0x04, 0x76, 0xFE, + 0x6E, 0x00, 0xFC, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, + 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, 0xFC, 0x0A, 0x07, 0x7E, 0xF1, + 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, 0x03, 0x07, 0x75, 0xFC, 0xA7, + 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF2, 0xFF, + 0xF7, 0xFF, 0x7A, 0x00, 0x62, 0xFE, 0x35, 0x04, 0xF7, 0xF4, 0xEF, + 0x40, 0x40, 0x1B, 0x9C, 0xF4, 0x5E, 0x06, 0x66, 0xFC, 0xDB, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, + 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, + 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, + 0xB2, 0x01, 0xD1, 0xFC, 0x62, 0x05, 0xF6, 0xF6, 0x20, 0x13, 0x2E, + 0x45, 0x70, 0xF8, 0x34, 0x02, 0x94, 0xFF, 0xCD, 0xFF, 0x4C, 0x00, + 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, + 0x01, 0x36, 0xFC, 0x27, 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, + 0xE4, 0xF1, 0x67, 0x06, 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, 0x00, 0xB2, 0xFF, 0x02, 0x01, + 0x7A, 0xFD, 0xA2, 0x05, 0xD4, 0xF2, 0xC7, 0x3B, 0xF2, 0x22, 0xE7, + 0xF2, 0xF5, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, + 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, + 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x75, 0x01, 0x5C, 0xFD, + 0x3C, 0x04, 0x70, 0xF9, 0xFA, 0x0B, 0xC0, 0x47, 0xBC, 0xFC, 0xFC, + 0xFF, 0xD6, 0x00, 0x1D, 0xFF, 0xA2, 0x00, 0xB0, 0xFF, 0x13, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, + 0x06, 0x47, 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, + 0xAE, 0xFD, 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0xFE, + 0xFF, 0x1F, 0x00, 0x7C, 0xFF, 0x6D, 0x01, 0xCD, 0xFC, 0x99, 0x06, + 0xB4, 0xF1, 0xA6, 0x35, 0x89, 0x2A, 0xD0, 0xF1, 0x2B, 0x07, 0x3E, + 0xFC, 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, + 0xD9, 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, + 0xF7, 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, + 0xBE, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, + 0x00, 0x7B, 0xFF, 0x29, 0x01, 0x01, 0xFE, 0xF1, 0x02, 0x07, 0xFC, + 0x6E, 0x05, 0xE6, 0x48, 0xFF, 0x01, 0x84, 0xFD, 0x2C, 0x02, 0x68, + 0xFE, 0xF9, 0x00, 0x8E, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, + 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, + 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x55, + 0xFF, 0xB6, 0x01, 0x5F, 0xFC, 0x17, 0x07, 0x87, 0xF1, 0xC2, 0x2E, + 0xC0, 0x31, 0x7E, 0xF1, 0xF1, 0x06, 0x86, 0xFC, 0x9B, 0x01, 0x63, + 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, + 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, + 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9C, 0xFF, 0xD4, 0x00, 0xB4, + 0xFE, 0x9A, 0x01, 0x95, 0xFE, 0xA8, 0xFF, 0x98, 0x48, 0x1D, 0x08, + 0xEE, 0xFA, 0x80, 0x03, 0xB9, 0xFD, 0x4B, 0x01, 0x6E, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA9, 0x01, + 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, 0x45, 0x1C, + 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, 0xCC, 0xFF, + 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDE, 0x01, 0x33, + 0xFC, 0x21, 0x07, 0x30, 0xF2, 0x5C, 0x27, 0x5B, 0x38, 0x0F, 0xF2, + 0x40, 0x06, 0x0E, 0xFD, 0x43, 0x01, 0x91, 0xFF, 0x18, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, 0xFD, + 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, 0x06, + 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x10, 0x00, 0xBE, 0xFF, 0x7D, 0x00, 0x69, 0xFF, 0x4B, 0x00, 0xF6, + 0x00, 0xCB, 0xFA, 0xD3, 0x46, 0xF0, 0x0E, 0x5E, 0xF8, 0xBE, 0x04, + 0x1E, 0xFD, 0x91, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, 0x77, 0xFD, 0x04, 0x04, + 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, 0xFD, 0x92, 0xFF, 0x10, + 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x43, 0xFC, 0xC0, 0x06, 0x8F, + 0xF3, 0xB1, 0x1F, 0x18, 0x3E, 0x9B, 0xF3, 0x16, 0x05, 0xD5, 0xFD, + 0xCC, 0x00, 0xCE, 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, + 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, 0xB8, 0x06, 0x9C, 0xF1, + 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, 0x07, 0x46, 0xFC, 0xCA, + 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, + 0x29, 0x00, 0x14, 0x00, 0x16, 0xFF, 0x0C, 0x03, 0xEE, 0xF6, 0xB0, + 0x43, 0x47, 0x16, 0xFC, 0xF5, 0xCF, 0x05, 0xA1, 0xFC, 0xC6, 0x01, + 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, + 0xFF, 0x1B, 0x01, 0x20, 0xFE, 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, + 0xF4, 0x48, 0xFF, 0x02, 0x13, 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, + 0x01, 0x88, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, + 0xCF, 0x01, 0x8A, 0xFC, 0x05, 0x06, 0x7B, 0xF5, 0x06, 0x18, 0xC7, + 0x42, 0x2F, 0xF6, 0x7A, 0x03, 0xD4, 0xFE, 0x39, 0x00, 0x17, 0x00, + 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, + 0x01, 0x53, 0xFC, 0x21, 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, + 0x86, 0xF1, 0xDB, 0x06, 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDE, 0xFF, 0xAC, 0x00, + 0x0B, 0xFE, 0xC1, 0x04, 0x1B, 0xF4, 0x47, 0x3F, 0xEA, 0x1D, 0xF5, + 0xF3, 0x9C, 0x06, 0x4F, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, + 0x01, 0x03, 0xFF, 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, + 0xB9, 0x03, 0x9D, 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4D, 0xFF, 0x9F, 0x01, 0xFE, 0xFC, + 0x02, 0x05, 0xCB, 0xF7, 0x98, 0x10, 0x39, 0x46, 0xD0, 0xF9, 0x78, + 0x01, 0x00, 0x00, 0x91, 0xFF, 0x69, 0x00, 0xC6, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, + 0x07, 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, + 0x2C, 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x13, 0x00, 0x9E, 0xFF, 0x2B, 0x01, 0x37, 0xFD, 0x05, 0x06, + 0x54, 0xF2, 0xC2, 0x39, 0x99, 0x25, 0x73, 0xF2, 0x14, 0x07, 0x31, + 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC4, 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, + 0xFA, 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, + 0x9C, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, + 0x00, 0x67, 0xFF, 0x5C, 0x01, 0x93, 0xFD, 0xCC, 0x03, 0x54, 0xFA, + 0xA2, 0x09, 0x4F, 0x48, 0x73, 0xFE, 0x27, 0xFF, 0x4B, 0x01, 0xDE, + 0xFE, 0xC0, 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, + 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, + 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6D, + 0xFF, 0x8A, 0x01, 0x9F, 0xFC, 0xD3, 0x06, 0x8A, 0xF1, 0x57, 0x33, + 0x17, 0x2D, 0x9C, 0xF1, 0x24, 0x07, 0x4F, 0xFC, 0xC3, 0x01, 0x4E, + 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE4, 0xFF, 0x1B, 0x00, + 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, 0x96, + 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x86, 0xFF, 0x0C, 0x01, 0x3E, + 0xFE, 0x7B, 0x02, 0xED, 0xFC, 0x56, 0x03, 0xF5, 0x48, 0x06, 0x04, + 0xA1, 0xFC, 0xA3, 0x02, 0x2A, 0xFE, 0x16, 0x01, 0x83, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC8, 0x01, + 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, 0x43, 0xBD, + 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, 0xE1, 0xFF, + 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC8, 0x01, 0x49, + 0xFC, 0x27, 0x07, 0xAB, 0xF1, 0x3E, 0x2C, 0x1E, 0x34, 0x95, 0xF1, + 0xC1, 0x06, 0xAE, 0xFC, 0x81, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, 0x00, 0xE2, 0xFD, + 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, 0xA8, 0xF3, 0xB8, + 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x15, 0x00, 0xA8, 0xFF, 0xB6, 0x00, 0xF3, 0xFE, 0x24, 0x01, 0x6E, + 0xFF, 0xDE, 0xFD, 0x25, 0x48, 0x68, 0x0A, 0x08, 0xFA, 0xF2, 0x03, + 0x81, 0xFD, 0x65, 0x01, 0x64, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, + 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, + 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0B, 0x07, 0x97, + 0xF2, 0xB8, 0x24, 0x71, 0x3A, 0x7B, 0xF2, 0xE6, 0x05, 0x4C, 0xFD, + 0x1D, 0x01, 0xA4, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, + 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, 0xFD, 0x32, 0x06, 0x1F, 0xF2, + 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, 0x1E, 0x07, 0x32, 0xFC, 0xDF, + 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, + 0x5F, 0x00, 0xA5, 0xFF, 0xDC, 0xFF, 0xB8, 0x01, 0x57, 0xF9, 0xE5, + 0x45, 0x6E, 0x11, 0x83, 0xF7, 0x23, 0x05, 0xEE, 0xFC, 0xA6, 0x01, + 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, + 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, + 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, + 0x00, 0x9E, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE0, 0x01, 0x56, 0xFC, 0x89, 0x06, 0x2B, 0xF4, 0x06, 0x1D, 0xD7, + 0x3F, 0x61, 0xF4, 0x94, 0x04, 0x27, 0xFE, 0x9C, 0x00, 0xE6, 0xFF, + 0xF8, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, + 0x01, 0x8C, 0xFC, 0xEA, 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, + 0x8B, 0xF1, 0x1B, 0x07, 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, + 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, 0xFF, 0x0E, 0x00, 0x4B, 0x00, + 0xB4, 0xFE, 0xAF, 0x03, 0xD5, 0xF5, 0x4D, 0x42, 0xE6, 0x18, 0x3C, + 0xF5, 0x1F, 0x06, 0x7F, 0xFC, 0xD3, 0x01, 0x3B, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, + 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, + 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xC1, 0x01, 0xAD, 0xFC, + 0xB2, 0x05, 0x3F, 0xF6, 0x69, 0x15, 0x1F, 0x44, 0x53, 0xF7, 0xD3, + 0x02, 0x38, 0xFF, 0x01, 0x00, 0x33, 0x00, 0xDB, 0xFF, 0x09, 0x00, + 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, + 0x07, 0xC6, 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, + 0xC4, 0xFC, 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, + 0x00, 0x04, 0x00, 0xC6, 0xFF, 0xDB, 0x00, 0xBB, 0xFD, 0x3E, 0x05, + 0x60, 0xF3, 0x7B, 0x3D, 0x94, 0x20, 0x5E, 0xF3, 0xD0, 0x06, 0x3E, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, + 0xAE, 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, + 0xFD, 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, + 0x71, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, + 0x00, 0x55, 0xFF, 0x8A, 0x01, 0x2E, 0xFD, 0x9B, 0x04, 0xA8, 0xF8, + 0x1F, 0x0E, 0x1A, 0x47, 0x4E, 0xFB, 0xB3, 0x00, 0x70, 0x00, 0x54, + 0xFF, 0x87, 0x00, 0xBB, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, + 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, + 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8B, + 0xFF, 0x4F, 0x01, 0xFB, 0xFC, 0x5A, 0x06, 0xF2, 0xF1, 0xA0, 0x37, + 0x3A, 0x28, 0x13, 0xF2, 0x25, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, + 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, + 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, + 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, + 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x42, + 0x01, 0xCD, 0xFD, 0x59, 0x03, 0x3B, 0xFB, 0x5E, 0x07, 0xB3, 0x48, + 0x48, 0x00, 0x4B, 0xFE, 0xC2, 0x01, 0x9F, 0xFE, 0xDE, 0x00, 0x98, + 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDD, 0x01, + 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, 0x40, 0xD0, + 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, 0xF4, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA3, 0x01, 0x7A, + 0xFC, 0xFD, 0x06, 0x7C, 0xF1, 0xF2, 0x30, 0x96, 0x2F, 0x80, 0xF1, + 0x0F, 0x07, 0x69, 0xFC, 0xB0, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, + 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, 0xFE, + 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, 0x43, + 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0x1B, 0x00, 0x92, 0xFF, 0xEF, 0x00, 0x7D, 0xFE, 0x04, 0x02, 0xCF, + 0xFD, 0x58, 0x01, 0xD7, 0x48, 0x26, 0x06, 0xBB, 0xFB, 0x19, 0x03, + 0xED, 0xFD, 0x32, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, 0xC1, 0xFC, 0x86, 0x05, + 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, 0xF7, 0x7B, 0x02, 0x6B, + 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x43, 0xFF, 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE7, + 0xF1, 0xAC, 0x29, 0x66, 0x36, 0xC9, 0xF1, 0x83, 0x06, 0xDD, 0xFC, + 0x62, 0x01, 0x81, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x08, + 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, 0x78, 0x05, 0x0E, 0xF3, + 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, 0x06, 0x38, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB4, 0xFF, + 0x98, 0x00, 0x32, 0xFF, 0xB0, 0x00, 0x41, 0x00, 0x30, 0xFC, 0x86, + 0x47, 0xC6, 0x0C, 0x24, 0xF9, 0x60, 0x04, 0x4B, 0xFD, 0x7D, 0x01, + 0x5A, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x5A, + 0xFF, 0x7E, 0x01, 0x48, 0xFD, 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, + 0x7C, 0x47, 0x19, 0xFC, 0x4D, 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, + 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x38, 0xFC, 0xE9, 0x06, 0x12, 0xF3, 0x10, 0x22, 0x6E, + 0x3C, 0x05, 0xF3, 0x7E, 0x05, 0x91, 0xFD, 0xF4, 0x00, 0xBA, 0xFF, + 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, + 0x01, 0xE0, 0xFC, 0x7F, 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, + 0xEB, 0xF1, 0x2A, 0x07, 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, 0xFF, 0x42, 0x00, 0xE1, 0xFF, + 0x71, 0xFF, 0x71, 0x02, 0x02, 0xF8, 0xCC, 0x44, 0xFA, 0x13, 0xB0, + 0xF6, 0x81, 0x05, 0xC3, 0xFC, 0xB8, 0x01, 0x44, 0xFF, 0x31, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, + 0xFD, 0x1F, 0x03, 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, + 0xDC, 0xFD, 0xFD, 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x6F, 0xFC, + 0x47, 0x06, 0xD7, 0xF4, 0x5D, 0x1A, 0x74, 0x41, 0x48, 0xF5, 0x04, + 0x04, 0x80, 0xFE, 0x69, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0x05, 0x00, + 0xFD, 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, + 0x07, 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, + 0x78, 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, + 0x00, 0xF3, 0xFF, 0xF4, 0xFF, 0x80, 0x00, 0x58, 0xFE, 0x46, 0x04, + 0xDD, 0xF4, 0xC3, 0x40, 0x8C, 0x1B, 0x89, 0xF4, 0x66, 0x06, 0x63, + 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, + 0x98, 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, + 0x00, 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, + 0x40, 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x47, 0xFF, 0xB0, 0x01, 0xD6, 0xFC, 0x58, 0x05, 0x0D, 0xF7, + 0xD7, 0x12, 0x4E, 0x45, 0x96, 0xF8, 0x20, 0x02, 0xA0, 0xFF, 0xC7, + 0xFF, 0x4F, 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, + 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, + 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0xB0, + 0xFF, 0x07, 0x01, 0x72, 0xFD, 0xAE, 0x05, 0xC4, 0xF2, 0x90, 0x3B, + 0x3F, 0x23, 0xD9, 0xF2, 0xF9, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, 0x00, + 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, 0xFC, + 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, 0xFF, + 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5E, 0xFF, 0x72, + 0x01, 0x62, 0xFD, 0x2F, 0x04, 0x89, 0xF9, 0xB6, 0x0B, 0xD2, 0x47, + 0xEB, 0xFC, 0xE4, 0xFF, 0xE3, 0x00, 0x16, 0xFF, 0xA5, 0x00, 0xAF, + 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, 0x3D, 0x56, + 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, 0x05, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x7A, 0xFF, 0x70, 0x01, 0xC7, + 0xFC, 0xA0, 0x06, 0xAE, 0xF1, 0x65, 0x35, 0xD1, 0x2A, 0xCA, 0xF1, + 0x2A, 0x07, 0x40, 0xFC, 0xD0, 0x01, 0x47, 0xFF, 0x32, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x3D, 0xFF, + 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, 0x4A, 0xF6, 0xAD, + 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, 0x26, 0x01, 0x08, 0xFE, 0xE4, + 0x02, 0x21, 0xFC, 0x31, 0x05, 0xEB, 0x48, 0x37, 0x02, 0x6B, 0xFD, + 0x39, 0x02, 0x61, 0xFE, 0xFC, 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFE, + 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, + 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, + 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFD, 0xFF, + 0x2D, 0x00, 0x54, 0xFF, 0xB8, 0x01, 0x5D, 0xFC, 0x1A, 0x07, 0x8A, + 0xF1, 0x7B, 0x2E, 0x04, 0x32, 0x7F, 0xF1, 0xEC, 0x06, 0x8A, 0xFC, + 0x98, 0x01, 0x65, 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, + 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, 0xFE, 0x8C, 0x04, 0x6D, 0xF4, + 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, 0x85, 0x06, 0x57, 0xFC, 0xE0, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9E, 0xFF, + 0xD1, 0x00, 0xBB, 0xFE, 0x8D, 0x01, 0xAE, 0xFE, 0x74, 0xFF, 0x8D, + 0x48, 0x5D, 0x08, 0xD4, 0xFA, 0x8D, 0x03, 0xB3, 0xFD, 0x4E, 0x01, + 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, + 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, + 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, + 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xDF, 0x01, 0x32, 0xFC, 0x1F, 0x07, 0x3B, 0xF2, 0x11, 0x27, 0x97, + 0x38, 0x19, 0xF2, 0x36, 0x06, 0x15, 0xFD, 0x3F, 0x01, 0x93, 0xFF, + 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, + 0x01, 0x50, 0xFD, 0xE1, 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, + 0x9D, 0xF2, 0x09, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0x7A, 0x00, 0x70, 0xFF, + 0x3E, 0x00, 0x0C, 0x01, 0xA1, 0xFA, 0xBB, 0x46, 0x36, 0x0F, 0x45, + 0xF8, 0xC9, 0x04, 0x18, 0xFD, 0x93, 0x01, 0x52, 0xFF, 0x2D, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, + 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, + 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x45, 0xFC, + 0xBB, 0x06, 0xA0, 0xF3, 0x64, 0x1F, 0x4A, 0x3E, 0xB0, 0xF3, 0x08, + 0x05, 0xDE, 0xFD, 0xC7, 0x00, 0xD0, 0xFF, 0x00, 0x00, 0x02, 0x00, + 0xFE, 0xFF, 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, + 0x06, 0x97, 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, + 0x48, 0xFC, 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE0, 0xFF, 0x26, 0x00, 0x1A, 0x00, 0x0B, 0xFF, 0x1E, 0x03, + 0xCD, 0xF6, 0x89, 0x43, 0x91, 0x16, 0xE7, 0xF5, 0xD8, 0x05, 0x9D, + 0xFC, 0xC7, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x1F, 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, + 0xFC, 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, + 0x42, 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, + 0x00, 0x3C, 0xFF, 0xCD, 0x01, 0x8E, 0xFC, 0xFC, 0x05, 0x90, 0xF5, + 0xBB, 0x17, 0xEE, 0x42, 0x4E, 0xF6, 0x68, 0x03, 0xDF, 0xFE, 0x33, + 0x00, 0x1A, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, + 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, + 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0xDB, + 0xFF, 0xB2, 0x00, 0x02, 0xFE, 0xCF, 0x04, 0x05, 0xF4, 0x16, 0x3F, + 0x36, 0x1E, 0xE4, 0xF3, 0xA3, 0x06, 0x4D, 0xFC, 0xE3, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, + 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, + 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, + 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9D, + 0x01, 0x03, 0xFD, 0xF7, 0x04, 0xE3, 0xF7, 0x51, 0x10, 0x55, 0x46, + 0xF9, 0xF9, 0x63, 0x01, 0x0D, 0x00, 0x8B, 0xFF, 0x6D, 0x00, 0xC5, + 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, + 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, 0x39, 0x4D, + 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, 0x13, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x9C, 0xFF, 0x2F, 0x01, 0x30, + 0xFD, 0x10, 0x06, 0x47, 0xF2, 0x87, 0x39, 0xE5, 0x25, 0x67, 0xF2, + 0x16, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, 0x00, + 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, 0xFC, + 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x59, 0x01, 0x99, 0xFD, 0xC0, + 0x03, 0x6E, 0xFA, 0x61, 0x09, 0x5D, 0x48, 0xA6, 0xFE, 0x0F, 0xFF, + 0x58, 0x01, 0xD7, 0xFE, 0xC3, 0x00, 0xA3, 0xFF, 0x16, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4E, 0xFC, 0xA0, 0x06, + 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, 0xF4, 0xC8, 0x04, 0x07, + 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, 0x03, 0x00, 0xFD, 0xFF, + 0x25, 0x00, 0x6B, 0xFF, 0x8D, 0x01, 0x9B, 0xFC, 0xD8, 0x06, 0x87, + 0xF1, 0x13, 0x33, 0x5E, 0x2D, 0x98, 0xF1, 0x22, 0x07, 0x52, 0xFC, + 0xC1, 0x01, 0x4F, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE5, + 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, 0x71, 0x03, 0x3F, 0xF6, + 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, 0x06, 0x8C, 0xFC, 0xCE, + 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, + 0x09, 0x01, 0x45, 0xFE, 0x6E, 0x02, 0x06, 0xFD, 0x1C, 0x03, 0xF4, + 0x48, 0x41, 0x04, 0x87, 0xFC, 0xB0, 0x02, 0x23, 0xFE, 0x19, 0x01, + 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, + 0xFF, 0xC6, 0x01, 0x9F, 0xFC, 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, + 0x9E, 0x43, 0xDD, 0xF6, 0x15, 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, + 0x00, 0xDF, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xCA, 0x01, 0x47, 0xFC, 0x28, 0x07, 0xB0, 0xF1, 0xF5, 0x2B, 0x60, + 0x34, 0x9A, 0xF1, 0xBB, 0x06, 0xB3, 0xFC, 0x7D, 0x01, 0x73, 0xFF, + 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, + 0x00, 0xDA, 0xFD, 0x0F, 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, + 0x97, 0xF3, 0xBD, 0x06, 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xAA, 0xFF, 0xB3, 0x00, 0xFA, 0xFE, + 0x17, 0x01, 0x86, 0xFF, 0xAC, 0xFD, 0x16, 0x48, 0xAA, 0x0A, 0xEE, + 0xF9, 0xFE, 0x03, 0x7A, 0xFD, 0x67, 0x01, 0x63, 0xFF, 0x28, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, + 0xFD, 0xC4, 0x04, 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, + 0x01, 0x01, 0x44, 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, + 0x08, 0x07, 0xA4, 0xF2, 0x6D, 0x24, 0xAD, 0x3A, 0x88, 0xF2, 0xDB, + 0x05, 0x53, 0xFD, 0x19, 0x01, 0xA7, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, + 0x06, 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, + 0x33, 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, + 0x00, 0xCB, 0xFF, 0x5C, 0x00, 0xAC, 0xFF, 0xD0, 0xFF, 0xCD, 0x01, + 0x30, 0xF9, 0xC8, 0x45, 0xB6, 0x11, 0x6B, 0xF7, 0x2D, 0x05, 0xE9, + 0xFC, 0xA8, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x25, 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, + 0xFA, 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, + 0xB8, 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x58, 0xFC, 0x82, 0x06, 0x3E, 0xF4, + 0xBA, 0x1C, 0x07, 0x40, 0x79, 0xF4, 0x84, 0x04, 0x31, 0xFE, 0x96, + 0x00, 0xE8, 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, + 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, + 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEA, 0xFF, 0x0B, + 0x00, 0x51, 0x00, 0xAA, 0xFE, 0xC0, 0x03, 0xB8, 0xF5, 0x21, 0x42, + 0x31, 0x19, 0x28, 0xF5, 0x27, 0x06, 0x7C, 0xFC, 0xD4, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, 0x00, + 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, 0x50, + 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, 0xFF, + 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xBF, + 0x01, 0xB2, 0xFC, 0xA9, 0x05, 0x55, 0xF6, 0x20, 0x15, 0x42, 0x44, + 0x75, 0xF7, 0xBF, 0x02, 0x43, 0xFF, 0xFA, 0xFF, 0x36, 0x00, 0xDA, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, + 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, 0x35, 0xB1, + 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, 0x20, 0x00, + 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE0, 0x00, 0xB3, + 0xFD, 0x4B, 0x05, 0x4D, 0xF3, 0x45, 0x3D, 0xE0, 0x20, 0x4F, 0xF3, + 0xD5, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, 0xFF, 0xDD, 0x00, + 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, 0x7C, 0xF9, 0x35, + 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2C, 0x00, 0x56, 0xFF, 0x87, 0x01, 0x34, 0xFD, 0x8F, + 0x04, 0xC0, 0xF8, 0xD9, 0x0D, 0x31, 0x47, 0x7B, 0xFB, 0x9C, 0x00, + 0x7D, 0x00, 0x4D, 0xFF, 0x8A, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, + 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, + 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x89, 0xFF, 0x53, 0x01, 0xF5, 0xFC, 0x63, 0x06, 0xE9, + 0xF1, 0x63, 0x37, 0x85, 0x28, 0x09, 0xF2, 0x27, 0x07, 0x35, 0xFC, + 0xDA, 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD1, + 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, 0xFF, 0x2A, 0x02, 0x83, 0xF8, + 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, 0x5D, 0x05, 0xD3, 0xFC, 0xB1, + 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x73, 0xFF, 0x3F, 0x01, 0xD3, 0xFD, 0x4C, 0x03, 0x54, 0xFB, 0x1F, + 0x07, 0xBB, 0x48, 0x7D, 0x00, 0x33, 0xFE, 0xCF, 0x01, 0x98, 0xFE, + 0xE2, 0x00, 0x97, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, + 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, + 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5E, 0xFF, + 0xA6, 0x01, 0x76, 0xFC, 0x01, 0x07, 0x7D, 0xF1, 0xAD, 0x30, 0xDC, + 0x2F, 0x7F, 0xF1, 0x0C, 0x07, 0x6C, 0xFC, 0xAD, 0x01, 0x5A, 0xFF, + 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, + 0x00, 0x7B, 0xFE, 0x0C, 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, + 0xCD, 0xF4, 0x4B, 0x06, 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, + 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x93, 0xFF, 0xEC, 0x00, 0x83, 0xFE, + 0xF7, 0x01, 0xE8, 0xFD, 0x21, 0x01, 0xD2, 0x48, 0x64, 0x06, 0xA1, + 0xFB, 0x26, 0x03, 0xE7, 0xFD, 0x35, 0x01, 0x76, 0xFF, 0x22, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, + 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, + 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x39, 0xFC, + 0x29, 0x07, 0xEF, 0xF1, 0x62, 0x29, 0xA5, 0x36, 0xD0, 0xF1, 0x7B, + 0x06, 0xE3, 0xFC, 0x5E, 0x01, 0x83, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, + 0x01, 0x00, 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, + 0x05, 0xFD, 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, + 0x37, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, + 0x00, 0xB5, 0xFF, 0x94, 0x00, 0x39, 0xFF, 0xA3, 0x00, 0x58, 0x00, + 0x02, 0xFC, 0x73, 0x47, 0x0B, 0x0D, 0x0B, 0xF9, 0x6C, 0x04, 0x45, + 0xFD, 0x80, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, + 0xF9, 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, + 0x2E, 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x39, 0xFC, 0xE4, 0x06, 0x21, 0xF3, + 0xC4, 0x21, 0xA5, 0x3C, 0x16, 0xF3, 0x72, 0x05, 0x9A, 0xFD, 0xEF, + 0x00, 0xBC, 0xFF, 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, + 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD6, 0xFF, 0x3F, + 0x00, 0xE7, 0xFF, 0x65, 0xFF, 0x85, 0x02, 0xDE, 0xF7, 0xA9, 0x44, + 0x43, 0x14, 0x99, 0xF6, 0x8B, 0x05, 0xBF, 0xFC, 0xBA, 0x01, 0x43, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, + 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, + 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, + 0x91, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, + 0x01, 0x72, 0xFC, 0x3F, 0x06, 0xEB, 0xF4, 0x12, 0x1A, 0xA1, 0x41, + 0x63, 0xF5, 0xF3, 0x03, 0x8A, 0xFE, 0x63, 0x00, 0x02, 0x00, 0xEE, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, 0xB1, 0x01, + 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, 0x31, 0x7C, + 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, 0x29, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF4, 0xFF, 0xF1, 0xFF, 0x85, 0x00, 0x4E, + 0xFE, 0x56, 0x04, 0xC3, 0xF4, 0x95, 0x40, 0xD8, 0x1B, 0x76, 0xF4, + 0x6D, 0x06, 0x60, 0xFC, 0xDD, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, 0x01, + 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, 0x60, + 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAE, 0x01, 0xDB, 0xFC, 0x4D, + 0x05, 0x24, 0xF7, 0x8E, 0x12, 0x6D, 0x45, 0xBC, 0xF8, 0x0C, 0x02, + 0xAC, 0xFF, 0xC0, 0xFF, 0x52, 0x00, 0xCF, 0xFF, 0x0C, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x25, 0x07, + 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, 0xF1, 0x56, 0x06, 0xFE, + 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x0D, 0x00, 0xAE, 0xFF, 0x0B, 0x01, 0x6A, 0xFD, 0xBA, 0x05, 0xB4, + 0xF2, 0x58, 0x3B, 0x8A, 0x23, 0xCB, 0xF2, 0xFD, 0x06, 0x34, 0xFC, + 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBB, + 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, 0xBE, 0x00, 0x38, 0xFB, + 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, 0x04, 0x2B, 0xFD, 0x8B, + 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, + 0x5F, 0xFF, 0x70, 0x01, 0x68, 0xFD, 0x23, 0x04, 0xA2, 0xF9, 0x73, + 0x0B, 0xE4, 0x47, 0x1B, 0xFD, 0xCD, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, + 0xA9, 0x00, 0xAE, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE6, 0x01, 0x3F, 0xFC, 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, + 0x96, 0x3D, 0x69, 0xF3, 0x38, 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, + 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x78, 0xFF, + 0x74, 0x01, 0xC2, 0xFC, 0xA7, 0x06, 0xA8, 0xF1, 0x25, 0x35, 0x1B, + 0x2B, 0xC2, 0xF1, 0x2A, 0x07, 0x41, 0xFC, 0xCE, 0x01, 0x47, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, + 0x00, 0x32, 0xFF, 0xDC, 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, + 0x34, 0xF6, 0xB7, 0x05, 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x23, 0x01, + 0x0F, 0xFE, 0xD7, 0x02, 0x3B, 0xFC, 0xF5, 0x04, 0xED, 0x48, 0x70, + 0x02, 0x52, 0xFD, 0x46, 0x02, 0x5A, 0xFE, 0xFF, 0x00, 0x8B, 0xFF, + 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, + 0xFC, 0x1A, 0x06, 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, + 0xA6, 0x03, 0xB9, 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x53, 0xFF, 0xBB, 0x01, 0x5A, 0xFC, + 0x1C, 0x07, 0x8D, 0xF1, 0x34, 0x2E, 0x48, 0x32, 0x81, 0xF1, 0xE7, + 0x06, 0x8E, 0xFC, 0x96, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, + 0x04, 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, + 0x04, 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, + 0x55, 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, + 0x00, 0x9F, 0xFF, 0xCE, 0x00, 0xC2, 0xFE, 0x80, 0x01, 0xC6, 0xFE, + 0x40, 0xFF, 0x81, 0x48, 0x9E, 0x08, 0xBA, 0xFA, 0x9A, 0x03, 0xAC, + 0xFD, 0x51, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2F, 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, + 0xF7, 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, + 0xA2, 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3D, 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1D, 0x07, 0x45, 0xF2, + 0xC6, 0x26, 0xD3, 0x38, 0x24, 0xF2, 0x2D, 0x06, 0x1B, 0xFD, 0x3B, + 0x01, 0x95, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, + 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, + 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, + 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC1, 0xFF, 0x76, + 0x00, 0x76, 0xFF, 0x32, 0x00, 0x22, 0x01, 0x76, 0xFA, 0xA3, 0x46, + 0x7D, 0x0F, 0x2C, 0xF8, 0xD5, 0x04, 0x13, 0xFD, 0x96, 0x01, 0x51, + 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, 0xFF, + 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, 0x2C, + 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, 0x00, + 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x47, 0xFC, 0xB5, 0x06, 0xB0, 0xF3, 0x19, 0x1F, 0x7E, 0x3E, + 0xC4, 0xF3, 0xFA, 0x04, 0xE7, 0xFD, 0xC1, 0x00, 0xD3, 0xFF, 0xFF, + 0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, 0x82, 0x01, + 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, 0x2C, 0xA8, + 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, 0x30, 0x00, + 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, 0x23, 0x00, 0x20, 0x00, 0x00, + 0xFF, 0x31, 0x03, 0xAD, 0xF6, 0x65, 0x43, 0xDC, 0x16, 0xD1, 0xF5, + 0xE1, 0x05, 0x99, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, 0x01, 0x2D, 0xFE, + 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, 0x73, 0x03, 0xE0, + 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, 0xFF, 0x1E, 0x00, + 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCC, 0x01, 0x91, 0xFC, 0xF3, + 0x05, 0xA6, 0xF5, 0x70, 0x17, 0x17, 0x43, 0x6D, 0xF6, 0x56, 0x03, + 0xEA, 0xFE, 0x2D, 0x00, 0x1D, 0x00, 0xE4, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, + 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, + 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, + 0xFD, 0xFF, 0xD8, 0xFF, 0xB7, 0x00, 0xF9, 0xFD, 0xDE, 0x04, 0xEF, + 0xF3, 0xE4, 0x3E, 0x81, 0x1E, 0xD2, 0xF3, 0xA9, 0x06, 0x4B, 0xFC, + 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA5, + 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, 0x01, 0x33, 0xFF, 0x5A, 0xFE, + 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, 0xD2, 0x03, 0x90, 0xFD, 0x5E, + 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, + 0x4F, 0xFF, 0x9A, 0x01, 0x08, 0xFD, 0xEB, 0x04, 0xFC, 0xF7, 0x0A, + 0x10, 0x70, 0x46, 0x22, 0xFA, 0x4D, 0x01, 0x19, 0x00, 0x84, 0xFF, + 0x70, 0x00, 0xC4, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, + 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, + 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, + 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x99, 0xFF, + 0x33, 0x01, 0x29, 0xFD, 0x1A, 0x06, 0x3B, 0xF2, 0x4B, 0x39, 0x30, + 0x26, 0x5B, 0xF2, 0x19, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, + 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, + 0xFF, 0xFA, 0xFF, 0x83, 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, + 0xBF, 0xF7, 0x07, 0x05, 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x56, 0x01, + 0xA0, 0xFD, 0xB3, 0x03, 0x87, 0xFA, 0x1F, 0x09, 0x6A, 0x48, 0xD9, + 0xFE, 0xF6, 0xFE, 0x65, 0x01, 0xD0, 0xFE, 0xC7, 0x00, 0xA2, 0xFF, + 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, + 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, + 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, + 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x69, 0xFF, 0x90, 0x01, 0x96, 0xFC, + 0xDD, 0x06, 0x85, 0xF1, 0xD0, 0x32, 0xA6, 0x2D, 0x94, 0xF1, 0x20, + 0x07, 0x54, 0xFC, 0xBF, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x07, 0x00, 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, + 0x03, 0x20, 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, + 0x88, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, + 0x00, 0x89, 0xFF, 0x06, 0x01, 0x4C, 0xFE, 0x60, 0x02, 0x1F, 0xFD, + 0xE2, 0x02, 0xF3, 0x48, 0x7D, 0x04, 0x6E, 0xFC, 0xBD, 0x02, 0x1C, + 0xFE, 0x1C, 0x01, 0x80, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x33, 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, + 0xF6, 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, + 0x11, 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, + 0x00, 0x49, 0xFF, 0xCB, 0x01, 0x45, 0xFC, 0x29, 0x07, 0xB6, 0xF1, + 0xAD, 0x2B, 0xA2, 0x34, 0x9E, 0xF1, 0xB4, 0x06, 0xB8, 0xFC, 0x7A, + 0x01, 0x75, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, + 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, + 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAB, 0xFF, 0xAF, + 0x00, 0x01, 0xFF, 0x0A, 0x01, 0x9E, 0xFF, 0x7C, 0xFD, 0x03, 0x48, + 0xED, 0x0A, 0xD5, 0xF9, 0x0A, 0x04, 0x74, 0xFD, 0x6A, 0x01, 0x62, + 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, + 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, + 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, + 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0x04, 0x07, 0xB1, 0xF2, 0x21, 0x24, 0xE6, 0x3A, + 0x97, 0xF2, 0xD0, 0x05, 0x5B, 0xFD, 0x15, 0x01, 0xA9, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, 0x45, 0x01, + 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, 0x27, 0x2B, + 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0D, 0x00, 0xCD, 0xFF, 0x59, 0x00, 0xB3, 0xFF, 0xC4, + 0xFF, 0xE2, 0x01, 0x09, 0xF9, 0xAA, 0x45, 0xFE, 0x11, 0x54, 0xF7, + 0x38, 0x05, 0xE4, 0xFC, 0xAA, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, 0xFD, + 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, 0x89, + 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5B, 0xFC, 0x7B, + 0x06, 0x50, 0xF4, 0x6E, 0x1C, 0x36, 0x40, 0x92, 0xF4, 0x75, 0x04, + 0x3B, 0xFE, 0x91, 0x00, 0xEB, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, + 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, 0x84, 0xFC, 0xF3, 0x06, + 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, 0xF1, 0x16, 0x07, 0x61, + 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, + 0xEC, 0xFF, 0x08, 0x00, 0x57, 0x00, 0x9F, 0xFE, 0xD1, 0x03, 0x9B, + 0xF5, 0xF7, 0x41, 0x7C, 0x19, 0x13, 0xF5, 0x2F, 0x06, 0x78, 0xFC, + 0xD5, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8F, + 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, 0x91, 0xFD, 0xE3, 0x01, + 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, 0x02, 0xFE, 0xFD, 0x2B, + 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, + 0x42, 0xFF, 0xBD, 0x01, 0xB6, 0xFC, 0x9F, 0x05, 0x6C, 0xF6, 0xD6, + 0x14, 0x65, 0x44, 0x98, 0xF7, 0xAC, 0x02, 0x4E, 0xFF, 0xF4, 0xFF, + 0x39, 0x00, 0xD9, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, + 0xFF, 0xD2, 0x01, 0x3D, 0xFC, 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, + 0xC6, 0x35, 0xB7, 0xF1, 0x96, 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, + 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x06, 0x00, 0xC1, 0xFF, + 0xE5, 0x00, 0xAA, 0xFD, 0x58, 0x05, 0x3A, 0xF3, 0x11, 0x3D, 0x2C, + 0x21, 0x3F, 0xF3, 0xDA, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, + 0xFF, 0xD0, 0x00, 0x07, 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, + 0x63, 0xF9, 0x42, 0x04, 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x85, 0x01, + 0x39, 0xFD, 0x84, 0x04, 0xD9, 0xF8, 0x95, 0x0D, 0x48, 0x47, 0xA7, + 0xFB, 0x86, 0x00, 0x8A, 0x00, 0x46, 0xFF, 0x8E, 0x00, 0xB8, 0xFF, + 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, + 0xFC, 0xF3, 0x06, 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, + 0x9C, 0x05, 0x7E, 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x87, 0xFF, 0x57, 0x01, 0xEF, 0xFC, + 0x6B, 0x06, 0xE0, 0xF1, 0x23, 0x37, 0xCE, 0x28, 0x01, 0xF2, 0x28, + 0x07, 0x36, 0xFC, 0xD9, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, + 0x02, 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, + 0xCF, 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x23, 0x00, 0x74, 0xFF, 0x3C, 0x01, 0xDA, 0xFD, 0x40, 0x03, + 0x6E, 0xFB, 0xE1, 0x06, 0xC3, 0x48, 0xB3, 0x00, 0x1A, 0xFE, 0xDC, + 0x01, 0x91, 0xFE, 0xE5, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, + 0xF4, 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, + 0x77, 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, + 0x00, 0x5C, 0xFF, 0xA8, 0x01, 0x73, 0xFC, 0x05, 0x07, 0x7D, 0xF1, + 0x67, 0x30, 0x21, 0x30, 0x7E, 0xF1, 0x08, 0x07, 0x6F, 0xFC, 0xAB, + 0x01, 0x5B, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF0, 0xFF, + 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, + 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, + 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE8, + 0x00, 0x8A, 0xFE, 0xE9, 0x01, 0x01, 0xFE, 0xEA, 0x00, 0xCB, 0x48, + 0xA2, 0x06, 0x87, 0xFB, 0x33, 0x03, 0xE0, 0xFD, 0x39, 0x01, 0x75, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, + 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, 0xFD, + 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, 0x00, + 0xD3, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD8, + 0x01, 0x37, 0xFC, 0x29, 0x07, 0xF8, 0xF1, 0x19, 0x29, 0xE5, 0x36, + 0xD8, 0xF1, 0x73, 0x06, 0xE9, 0xFC, 0x5B, 0x01, 0x85, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, 0xFB, 0x00, + 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, 0x22, 0xFC, + 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x12, 0x00, 0xB7, 0xFF, 0x91, 0x00, 0x40, 0xFF, 0x96, + 0x00, 0x6F, 0x00, 0xD5, 0xFB, 0x5E, 0x47, 0x50, 0x0D, 0xF2, 0xF8, + 0x78, 0x04, 0x3F, 0xFD, 0x82, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, 0x01, 0x53, 0xFD, + 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, 0x76, 0xFC, 0x1F, + 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, 0xFF, 0x13, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xDF, + 0x06, 0x30, 0xF3, 0x78, 0x21, 0xDB, 0x3C, 0x28, 0xF3, 0x65, 0x05, + 0xA2, 0xFD, 0xEA, 0x00, 0xBE, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, + 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, + 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, + 0xD8, 0xFF, 0x3C, 0x00, 0xEE, 0xFF, 0x5A, 0xFF, 0x98, 0x02, 0xBB, + 0xF7, 0x87, 0x44, 0x8C, 0x14, 0x83, 0xF6, 0x95, 0x05, 0xBA, 0xFC, + 0xBB, 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, + 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, 0xFD, 0x05, 0x03, 0xE1, 0xFB, + 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, 0xAA, 0xFD, 0x18, 0x02, 0x72, + 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x39, 0xFF, 0xD6, 0x01, 0x75, 0xFC, 0x37, 0x06, 0xFF, 0xF4, 0xC7, + 0x19, 0xCC, 0x41, 0x7F, 0xF5, 0xE2, 0x03, 0x95, 0xFE, 0x5D, 0x00, + 0x05, 0x00, 0xED, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, + 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, + 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, + 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF5, 0xFF, 0xEE, 0xFF, + 0x8B, 0x00, 0x44, 0xFE, 0x65, 0x04, 0xAA, 0xF4, 0x66, 0x40, 0x23, + 0x1C, 0x63, 0xF4, 0x74, 0x06, 0x5D, 0xFC, 0xDE, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, + 0xFE, 0xAE, 0x01, 0x70, 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, + 0x14, 0xFB, 0x6D, 0x03, 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAC, 0x01, + 0xDF, 0xFC, 0x43, 0x05, 0x3C, 0xF7, 0x46, 0x12, 0x8D, 0x45, 0xE2, + 0xF8, 0xF7, 0x01, 0xB8, 0xFF, 0xB9, 0xFF, 0x56, 0x00, 0xCE, 0xFF, + 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, + 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, + 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAB, 0xFF, 0x10, 0x01, 0x62, 0xFD, + 0xC5, 0x05, 0xA5, 0xF2, 0x1F, 0x3B, 0xD6, 0x23, 0xBE, 0xF2, 0x01, + 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x10, 0x00, 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, + 0x00, 0x0C, 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, + 0x26, 0xFD, 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6D, 0x01, 0x6E, 0xFD, 0x17, 0x04, + 0xBC, 0xF9, 0x30, 0x0B, 0xF4, 0x47, 0x4B, 0xFD, 0xB5, 0xFF, 0xFD, + 0x00, 0x08, 0xFF, 0xAC, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, + 0xF3, 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, + 0xD4, 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, + 0x00, 0x77, 0xFF, 0x77, 0x01, 0xBD, 0xFC, 0xAE, 0x06, 0xA3, 0xF1, + 0xE3, 0x34, 0x64, 0x2B, 0xBC, 0xF1, 0x2A, 0x07, 0x43, 0xFC, 0xCD, + 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDD, 0xFF, + 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, + 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, + 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, + 0xFF, 0x20, 0x01, 0x16, 0xFE, 0xCA, 0x02, 0x54, 0xFC, 0xB9, 0x04, + 0xF2, 0x48, 0xA9, 0x02, 0x39, 0xFD, 0x53, 0x02, 0x53, 0xFE, 0x03, + 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, + 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, + 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, + 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBD, + 0x01, 0x57, 0xFC, 0x1E, 0x07, 0x90, 0xF1, 0xED, 0x2D, 0x8C, 0x32, + 0x83, 0xF1, 0xE2, 0x06, 0x92, 0xFC, 0x93, 0x01, 0x68, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, 0xA4, 0x00, + 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, 0x1D, 0x10, + 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, 0xCA, 0x00, 0xC9, 0xFE, 0x73, + 0x01, 0xDE, 0xFE, 0x0C, 0xFF, 0x76, 0x48, 0xDE, 0x08, 0xA1, 0xFA, + 0xA6, 0x03, 0xA6, 0xFD, 0x53, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, 0xFC, + 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, 0x98, + 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x32, 0xFC, 0x1B, + 0x07, 0x50, 0xF2, 0x7B, 0x26, 0x11, 0x39, 0x2F, 0xF2, 0x23, 0x06, + 0x22, 0xFD, 0x37, 0x01, 0x97, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, 0x41, 0xFD, 0xF6, 0x05, + 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, 0xF2, 0x0F, 0x07, 0x31, + 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, + 0xC2, 0xFF, 0x73, 0x00, 0x7D, 0xFF, 0x25, 0x00, 0x38, 0x01, 0x4C, + 0xFA, 0x89, 0x46, 0xC3, 0x0F, 0x14, 0xF8, 0xE0, 0x04, 0x0D, 0xFD, + 0x98, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, + 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, 0xDF, 0x03, 0x2E, 0xFA, + 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, 0xFF, 0x38, 0x01, 0xE9, + 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE4, 0x01, 0x49, 0xFC, 0xAF, 0x06, 0xC1, 0xF3, 0xCD, + 0x1E, 0xB1, 0x3E, 0xD9, 0xF3, 0xEC, 0x04, 0xF0, 0xFD, 0xBC, 0x00, + 0xD5, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, + 0xFF, 0x85, 0x01, 0xA6, 0xFC, 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, + 0xAB, 0x2C, 0xA3, 0xF1, 0x26, 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, + 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE2, 0xFF, 0x20, 0x00, + 0x26, 0x00, 0xF5, 0xFE, 0x43, 0x03, 0x8D, 0xF6, 0x3C, 0x43, 0x25, + 0x17, 0xBB, 0xF5, 0xEA, 0x05, 0x95, 0xFC, 0xCA, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, + 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, + 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, + 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, + 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, + 0xFD, 0x4E, 0x05, 0x4A, 0xF3, 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, + 0xD6, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, + 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, + 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, + 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, 0x01, 0x4C, 0xFC, 0x26, + 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, 0x8F, 0xF1, 0xCA, 0x06, + 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x16, + 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, + 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, + 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, 0x07, 0x84, + 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, 0x41, 0xFD, + 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, + 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, + 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, + 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, + 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, + 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0x07, 0x00, 0xE8, 0xFF, 0x12, + 0x00, 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, + 0x76, 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, + 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, + 0xC3, 0x01, 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, + 0x43, 0x20, 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, + 0xDD, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, + 0x00, 0xC8, 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, + 0x76, 0xF3, 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, + 0x26, 0xFD, 0xAD, 0x04, 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, + 0xFB, 0xD4, 0x00, 0x5D, 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, + 0x10, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, + 0xFD, 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, + 0x23, 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x46, 0x01, 0xC3, 0xFD, + 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, 0xA6, 0x48, 0xF8, 0xFF, 0x70, + 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, 0x00, 0x9A, 0xFF, 0x19, 0x00, + 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, + 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, + 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, + 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, 0x02, 0xAA, 0xFD, + 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, 0x05, 0x03, 0xF7, + 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, 0xDC, + 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, 0xFC, + 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xB2, + 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, 0xFC, + 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, 0x79, + 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, + 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, + 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xD3, 0xFF, 0x47, + 0x00, 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, + 0x8D, 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, + 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, + 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, + 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, + 0xF0, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, + 0x00, 0x67, 0xFE, 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, + 0xA6, 0xF4, 0x5A, 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, + 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, + 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, + 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, + 0xFD, 0x9C, 0x05, 0xDC, 0xF2, 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, + 0xF3, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x76, 0x01, 0x59, 0xFD, + 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, 0xB6, 0x47, 0xA4, 0xFC, 0x07, + 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, 0x00, 0xB1, 0xFF, 0x13, 0x00, + 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, 0x01, 0xCF, 0xFC, 0x96, + 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, 0xD4, 0xF1, 0x2B, 0x07, + 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, + 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, + 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, + 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, 0x07, 0x85, + 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, 0x84, 0xFC, + 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9C, + 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, + 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, + 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, + 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, + 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, + 0x00, 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, + 0xCD, 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, + 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE5, 0x01, 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, + 0x3D, 0x91, 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, + 0x02, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, + 0x00, 0x1B, 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, + 0x07, 0xF6, 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, + 0x88, 0xFC, 0x09, 0x06, 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, + 0xF6, 0x83, 0x03, 0xCF, 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, + 0x07, 0x00, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, + 0xFE, 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, + 0x99, 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, 0xFF, 0xA0, 0x01, 0xFB, 0xFC, + 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, 0x2B, 0x46, 0xBB, 0xF9, 0x83, + 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, 0x00, 0xC7, 0xFF, 0x0E, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, + 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, + 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, 0xFD, 0xD2, 0x03, + 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, 0x33, 0xFF, 0x45, + 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, 0x00, 0xFD, 0xFF, + 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, 0x8C, + 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, 0xFC, + 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x86, + 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, 0x03, + 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, 0x14, + 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, + 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, + 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, + 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, + 0x00, 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, + 0x47, 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, + 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, + 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, + 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, + 0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, + 0xFF, 0xE2, 0xFF, 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, + 0x8F, 0xF7, 0x1D, 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, + 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, + 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, + 0x04, 0x00, 0x07, 0x00, 0xE9, 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, + 0xFE, 0xA6, 0x03, 0xE4, 0xF5, 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, + 0x1A, 0x06, 0x81, 0xFC, 0xD2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC1, 0x01, 0xAB, 0xFC, + 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, 0x0B, 0x44, 0x42, 0xF7, 0xDC, + 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, 0x00, 0xDC, 0xFF, 0x09, 0x00, + 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, 0x00, 0xBF, 0xFD, 0x38, + 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, 0x66, 0xF3, 0xCE, 0x06, + 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, + 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, + 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, 0x00, 0xFF, 0xFF, + 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, 0x06, 0xF7, + 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, 0x34, 0xFC, + 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, + 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, + 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, + 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, + 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, + 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, + 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, + 0x00, 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, + 0x07, 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, + 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, + 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, + 0x36, 0xC5, 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, + 0x1E, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, + 0xFF, 0xB6, 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, + 0x31, 0xF9, 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, + 0x37, 0xFC, 0xEB, 0x06, 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, + 0xF2, 0x84, 0x05, 0x8D, 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, + 0x01, 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, + 0xFF, 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, + 0x7C, 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, + 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD9, 0x01, 0x6D, 0xFC, + 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, 0x5F, 0x41, 0x3A, 0xF5, 0x0C, + 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, 0xFF, 0xEF, 0xFF, 0x05, 0x00, + 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, + 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, + 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, + 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, 0xFC, 0x5D, 0x05, + 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, 0x2A, 0x02, 0x9A, + 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, 0xCC, + 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, 0xFC, + 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, + 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, 0xF9, + 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, 0x19, + 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x20, 0x00, + 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, + 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, + 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, + 0xFF, 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, + 0xEA, 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, + 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, + 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, + 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, + 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, + 0xFE, 0x93, 0x01, 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, + 0xE1, 0xFA, 0x86, 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, + 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, + 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, + 0xFF, 0xFF, 0x10, 0x00, 0xBF, 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, + 0x00, 0x01, 0x01, 0xB6, 0xFA, 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, + 0xC4, 0x04, 0x1B, 0xFD, 0x92, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x44, 0xFC, + 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, 0x31, 0x3E, 0xA5, 0xF3, 0x0F, + 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, 0xFF, 0x01, 0x00, 0x02, 0x00, + 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, 0x00, 0x10, 0xFF, 0x15, + 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, 0xF1, 0xF5, 0xD3, 0x05, + 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, + 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, + 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0x03, 0x00, + 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, 0x04, 0x10, + 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, 0x4E, 0xFC, + 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, + 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, + 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, + 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x13, 0x00, + 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, + 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, + 0xFF, 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, + 0x57, 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, + 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, + 0x8B, 0x01, 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, + 0x2D, 0x9A, 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, + 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, + 0xFE, 0x74, 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, + 0x94, 0xFC, 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, + 0x48, 0xFC, 0x28, 0x07, 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, + 0xF1, 0xBE, 0x06, 0xB0, 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, + 0xFE, 0xFF, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, + 0x01, 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, + 0xF8, 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, + 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, 0x8F, 0x3A, 0x82, 0xF2, 0xE1, + 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, + 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, + 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, 0xFC, 0x85, 0x06, + 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, 0x8C, 0x04, 0x2C, + 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, 0x00, 0x06, 0x00, + 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, 0xC7, + 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, 0xFC, + 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, + 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, 0xF6, + 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, 0xFE, + 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x05, 0x00, + 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, + 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x56, + 0xFF, 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, + 0x26, 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, + 0x00, 0xBA, 0xFF, 0x11, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, + 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, + 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, + 0x01, 0xD0, 0xFD, 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, + 0x62, 0x00, 0x3F, 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, + 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, + 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, + 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, + 0xFD, 0xFF, 0x1B, 0x00, 0x93, 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, + 0x01, 0xDC, 0xFD, 0x3C, 0x01, 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, + 0x1F, 0x03, 0xEA, 0xFD, 0x34, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, 0xFF, 0xD6, 0x01, 0x39, 0xFC, + 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, 0x85, 0x36, 0xCC, 0xF1, 0x7F, + 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, + 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, 0xFF, 0xA9, 0x00, 0x4D, + 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, 0x18, 0xF9, 0x66, 0x04, + 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, + 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, + 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, 0x00, 0x0B, 0x00, + 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, 0x02, 0xF0, + 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, 0xC1, 0xFC, + 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, + 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, + 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, + 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0x05, 0x00, 0xF4, 0xFF, + 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, + 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x47, + 0xFF, 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, + 0x5C, 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, + 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, + 0x09, 0x01, 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, + 0x23, 0xD2, 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, + 0x01, 0x65, 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, + 0x03, 0xFD, 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, + 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, + 0xC4, 0xFC, 0xA4, 0x06, 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, + 0xF1, 0x2A, 0x07, 0x40, 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, + 0xFE, 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, + 0x5E, 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, + 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0xBA, 0x01, 0x5B, 0xFC, + 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, 0x26, 0x32, 0x80, 0xF1, 0xEA, + 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, + 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, + 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, + 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, 0xFC, 0x1E, 0x07, + 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, 0x32, 0x06, 0x18, + 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, + 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, 0x8B, + 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, 0xFD, + 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, 0xF3, + 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, 0xC4, + 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xE1, 0xFF, + 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, + 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, + 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, + 0xFF, 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, + 0x02, 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, + 0x00, 0xE4, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, + 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, + 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, + 0x01, 0x05, 0xFD, 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, + 0x0D, 0xFA, 0x58, 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, + 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, + 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, + 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, + 0xFD, 0xB9, 0x03, 0x7B, 0xFA, 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, + 0x03, 0xFF, 0x5F, 0x01, 0xD4, 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, + 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, 0xFF, 0x8E, 0x01, 0x99, 0xFC, + 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, 0x82, 0x2D, 0x96, 0xF1, 0x21, + 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, 0xFE, 0x67, 0x02, 0x13, + 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, 0x7A, 0xFC, 0xB6, 0x02, + 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, + 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, + 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x15, 0x00, + 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, 0xFF, 0x94, + 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, 0x77, 0xFD, + 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, + 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, + 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xCC, 0xFF, + 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, + 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, + 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, + 0xFF, 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, + 0x1F, 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, + 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, + 0x54, 0x00, 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, + 0x19, 0x1E, 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, + 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, + 0x01, 0xB4, 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, + 0x86, 0xF7, 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, + 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, + 0xAE, 0xFD, 0x52, 0x05, 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, + 0xF3, 0xD8, 0x06, 0x3C, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, + 0xFD, 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, + 0x91, 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, + 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, 0xFF, 0x55, 0x01, 0xF2, 0xFC, + 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, 0xAA, 0x28, 0x05, 0xF2, 0x27, + 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, + 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, + 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFD, + 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, 0xFC, 0x03, 0x07, + 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, 0x0A, 0x07, 0x6E, + 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x1A, 0x00, + 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, 0x05, + 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, 0xFD, + 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, + 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, 0xF1, + 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, 0x5C, + 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB6, 0xFF, + 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, + 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, + 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, + 0xC0, 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, + 0xFF, 0x07, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, + 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, + 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, + 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, + 0x01, 0x73, 0xFC, 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, + 0x71, 0xF5, 0xEB, 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, + 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, + 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, + 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, + 0xFC, 0x48, 0x05, 0x30, 0xF7, 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, + 0x01, 0x02, 0xB2, 0xFF, 0xBD, 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, + 0x00, 0x00, 0x00, 0x0E, 0x00, 0xAC, 0xFF, 0x0E, 0x01, 0x66, 0xFD, + 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, 0xB0, 0x23, 0xC4, 0xF2, 0xFF, + 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, 0x01, 0x6B, 0xFD, 0x1D, + 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, 0x33, 0xFD, 0xC1, 0xFF, + 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, 0xFF, 0x14, 0x00, 0xFE, + 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, + 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, + 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, 0x02, 0x47, + 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, 0x4D, 0x02, + 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, + 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, + 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, + 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA0, 0xFF, + 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, + 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, + 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, + 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, + 0xF2, 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, + 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, + 0x7A, 0xFF, 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, + 0x0F, 0x20, 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, + 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x48, 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, + 0xCF, 0xF3, 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, + 0xFA, 0xFE, 0x3A, 0x03, 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, + 0xF5, 0xE6, 0x05, 0x97, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, + 0xFE, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, + 0xFC, 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, + 0x4D, 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, + 0x00, 0x03, 0x00, 0xFE, 0xFF, 0xD7, 0xFF, 0xBA, 0x00, 0xF4, 0xFD, + 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, + 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, + 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, + 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0xFF, + 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, 0xFD, 0x1E, 0x06, + 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, 0x1A, 0x07, 0x31, + 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, 0x94, + 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, 0x01, + 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, + 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, 0xF1, + 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, 0xBE, + 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, + 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, + 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, + 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, + 0xFF, 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, + 0xC3, 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, + 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, + 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, + 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, + 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, + 0x9E, 0xF2, 0xCB, 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, + 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, + 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, + 0xFC, 0x78, 0x06, 0x5A, 0xF4, 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, + 0x6D, 0x04, 0x3F, 0xFE, 0x8E, 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, + 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x5A, 0x00, 0x9A, 0xFE, + 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, 0xA1, 0x19, 0x09, 0xF5, 0x33, + 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, 0x01, 0xB8, 0xFC, 0x9A, + 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, 0xA9, 0xF7, 0xA2, 0x02, + 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, 0xFF, 0x0A, 0x00, 0x01, + 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, + 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, 0x04, 0xE6, + 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, 0x90, 0x00, + 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFE, 0xFF, 0x1C, + 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, + 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, + 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, + 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, + 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, + 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, + 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, + 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, + 0x8E, 0xFE, 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, + 0x06, 0x7B, 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, + 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, + 0x01, 0x37, 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, + 0xDC, 0xF1, 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x11, 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, + 0x90, 0x00, 0x7A, 0x00, 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, + 0xF8, 0x7E, 0x04, 0x3C, 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, + 0xFC, 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, + 0x5F, 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, + 0x00, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, 0x00, 0xF1, 0xFF, 0x54, 0xFF, + 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, 0xB1, 0x14, 0x77, 0xF6, 0x9A, + 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, + 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, + 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x04, + 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, 0xFE, 0x6D, 0x04, + 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, 0x78, 0x06, 0x5C, + 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, + 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, 0x48, + 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, 0xFF, + 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0E, + 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, 0xF2, + 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, 0xE5, + 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, + 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, + 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, + 0xFF, 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, + 0x89, 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, + 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, + 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, + 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, + 0x8A, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, + 0x01, 0x56, 0xFC, 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, + 0x84, 0xF1, 0xE0, 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, + 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, + 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, + 0xFC, 0x1A, 0x07, 0x56, 0xF2, 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, + 0x1E, 0x06, 0x25, 0xFD, 0x35, 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, + 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, 0x00, 0x81, 0xFF, 0x1F, 0x00, + 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, 0xE7, 0x0F, 0x08, 0xF8, 0xE6, + 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x4A, 0xFC, 0xAC, + 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, 0xE4, 0xF3, 0xE5, 0x04, + 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0x08, + 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, + 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, + 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFE, 0xFF, + 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, 0x05, 0xC6, + 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, 0xFA, 0xFE, + 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFF, + 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, + 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, + 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, + 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, + 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, + 0xFF, 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, + 0xA0, 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, + 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, + 0x52, 0x01, 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, + 0x48, 0x26, 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, + 0xA0, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, + 0x01, 0x90, 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, + 0x8E, 0xF1, 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, + 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, + 0x4D, 0x02, 0x45, 0xFD, 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, + 0xFC, 0xD1, 0x02, 0x12, 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, + 0xFC, 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, + 0xAB, 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, + 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, 0x00, 0x0C, 0xFF, 0xF7, 0x00, + 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, 0x51, 0x0B, 0xAF, 0xF9, 0x1D, + 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, + 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, + 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, 0xFF, 0x01, 0x02, + 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, 0x48, 0x05, 0xDD, + 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, + 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, 0x6C, + 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, 0xFE, + 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xED, + 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, 0xF5, + 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, 0xD7, + 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, + 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, + 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, + 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x07, 0x00, 0xBD, + 0xFF, 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, + 0x9E, 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, + 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, + 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, + 0xB6, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, + 0x01, 0xE6, 0xFC, 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, + 0xF3, 0xF1, 0x29, 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, + 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, + 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, + 0x1A, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, + 0xFC, 0x0A, 0x07, 0x7E, 0xF1, 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, + 0x03, 0x07, 0x75, 0xFC, 0xA7, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, + 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, 0x00, 0x95, 0xFE, 0xD5, 0x01, + 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, 0x00, 0x07, 0x61, 0xFB, 0x46, + 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, 0xFF, 0x23, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, 0x01, 0x36, 0xFC, 0x27, + 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, 0xE4, 0xF1, 0x67, 0x06, + 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x11, + 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, + 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, + 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, 0x06, 0x47, + 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, 0xAE, 0xFD, + 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD9, + 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, + 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, + 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, + 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, + 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF7, 0xFF, 0xEA, + 0xFF, 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, + 0x94, 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xA9, 0x01, 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, + 0x45, 0x1C, 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, + 0xCC, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, + 0x01, 0x57, 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, + 0xAA, 0xF2, 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, + 0x77, 0xFD, 0x04, 0x04, 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, + 0xFD, 0x92, 0xFF, 0x10, 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, + 0x15, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, + 0xFC, 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, + 0x29, 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x1B, 0x01, 0x20, 0xFE, + 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, 0xF4, 0x48, 0xFF, 0x02, 0x13, + 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, 0x01, 0x88, 0xFF, 0x1D, 0x00, + 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, + 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, + 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, + 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, 0x01, 0x03, 0xFF, + 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, 0xB9, 0x03, 0x9D, + 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, 0x61, + 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, 0xFD, + 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC4, + 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, 0xFA, + 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, 0x9C, + 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, + 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, + 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE4, 0xFF, 0x1B, + 0x00, 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, + 0x96, 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, + 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, + 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, + 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, + 0xE1, 0xFF, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, + 0x00, 0xE2, 0xFD, 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, + 0xA8, 0xF3, 0xB8, 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, + 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, + 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, + 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, + 0xFD, 0x32, 0x06, 0x1F, 0xF2, 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, + 0x1E, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x4F, 0x01, 0xB0, 0xFD, + 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, 0x86, 0x48, 0x5A, 0xFF, 0xBA, + 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, 0x00, 0x9E, 0xFF, 0x17, 0x00, + 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, 0x01, 0x8C, 0xFC, 0xEA, + 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, 0x8B, 0xF1, 0x1B, 0x07, + 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, + 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, + 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, + 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xC6, + 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, 0xC4, 0xFC, + 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAE, + 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, + 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, + 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, + 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, + 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, + 0x00, 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, + 0xB2, 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, + 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, + 0xDD, 0x01, 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, + 0x40, 0xD0, 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, + 0xF4, 0xFF, 0x05, 0x00, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, + 0x00, 0x85, 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, + 0xE1, 0xF4, 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, + 0xC1, 0xFC, 0x86, 0x05, 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, + 0xF7, 0x7B, 0x02, 0x6B, 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, + 0x0B, 0x00, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, + 0xFD, 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, + 0xE6, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x5A, 0xFF, 0x7E, 0x01, 0x48, 0xFD, + 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, 0x7C, 0x47, 0x19, 0xFC, 0x4D, + 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, 0x00, 0xB5, 0xFF, 0x12, 0x00, + 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, + 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, + 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, 0xFD, 0x1F, 0x03, + 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, 0xDC, 0xFD, 0xFD, + 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, + 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, 0x80, + 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, 0xFC, + 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x98, + 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, 0x00, + 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, 0x40, + 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, + 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, + 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, + 0x00, 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, + 0xFC, 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, + 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, + 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, + 0x05, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x3D, 0xFF, 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, + 0x4A, 0xF6, 0xAD, 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, + 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, + 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, + 0x06, 0x00, 0x04, 0x00, 0xF8, 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, + 0xFE, 0x8C, 0x04, 0x6D, 0xF4, 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, + 0x85, 0x06, 0x57, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, 0xFF, 0xA7, 0x01, 0xEC, 0xFC, + 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, 0xD7, 0x45, 0x43, 0xF9, 0xC3, + 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, 0x00, 0xCB, 0xFF, 0x0D, 0x00, + 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, 0x01, 0x50, 0xFD, 0xE1, + 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, 0x9D, 0xF2, 0x09, 0x07, + 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, + 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, + 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, 0x00, 0xFE, 0xFF, + 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, 0x06, 0x97, + 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, 0x48, 0xFC, + 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, + 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, + 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, + 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, + 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, + 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, + 0x00, 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, + 0x81, 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, + 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, + 0xE2, 0x01, 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, + 0x39, 0x4D, 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, + 0x13, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, + 0xFF, 0x06, 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, + 0xD7, 0xF7, 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, + 0x4E, 0xFC, 0xA0, 0x06, 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, + 0xF4, 0xC8, 0x04, 0x07, 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, + 0x03, 0x00, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, + 0xFE, 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, + 0x00, 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, 0x9F, 0xFC, + 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, 0x9E, 0x43, 0xDD, 0xF6, 0x15, + 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, 0x00, 0xDF, 0xFF, 0x09, 0x00, + 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, + 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, + 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, 0xFD, 0xC4, 0x04, + 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, 0x01, 0x01, 0x44, + 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, 0x00, 0xFF, 0xFF, + 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, 0x14, + 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, 0xFC, + 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, + 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, 0xFA, + 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, 0xB8, + 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, + 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, + 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, + 0x00, 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, + 0x50, 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, + 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, + 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, + 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, + 0x20, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, + 0xFF, 0xDD, 0x00, 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, + 0x7C, 0xF9, 0x35, 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, + 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, + 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0xD1, 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, + 0xFF, 0x2A, 0x02, 0x83, 0xF8, 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, + 0x5D, 0x05, 0xD3, 0xFC, 0xB1, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, + 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x64, 0xFC, + 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, 0xD9, 0x40, 0xEA, 0xF4, 0x3E, + 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, 0xFF, 0xF3, 0xFF, 0x05, 0x00, + 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x7B, 0xFE, 0x0C, + 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, 0xCD, 0xF4, 0x4B, 0x06, + 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, + 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, + 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, + 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0x01, 0x00, + 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, 0x05, 0xFD, + 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, 0x37, 0xFC, + 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, + 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, + 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, + 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, + 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, + 0xFF, 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, + 0xDB, 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, + 0x00, 0x91, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, + 0xB1, 0x01, 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, + 0x31, 0x7C, 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, + 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, + 0xFE, 0xBB, 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, + 0x2E, 0xFB, 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, + 0x34, 0xFC, 0x25, 0x07, 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, + 0xF1, 0x56, 0x06, 0xFE, 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, + 0xFF, 0xFF, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, + 0x00, 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, + 0xA1, 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3F, 0xFC, + 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, 0x96, 0x3D, 0x69, 0xF3, 0x38, + 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, 0xFF, 0x04, 0x00, 0x02, 0x00, + 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, + 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, + 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, 0xFC, 0x1A, 0x06, + 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, 0xA6, 0x03, 0xB9, + 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0x04, 0x00, + 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, 0x55, + 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, 0xFC, + 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, + 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, 0xF7, + 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, 0xA2, + 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00, + 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, + 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, + 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, + 0xFF, 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, + 0x2C, 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, + 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, + 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, + 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, + 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, + 0x01, 0x2D, 0xFE, 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, + 0x73, 0x03, 0xE0, 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, + 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, + 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, + 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, + 0xFD, 0xFF, 0x16, 0x00, 0xA5, 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, + 0x01, 0x33, 0xFF, 0x5A, 0xFE, 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, + 0xD2, 0x03, 0x90, 0xFD, 0x5E, 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, + 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, 0xDF, 0x39, 0x5A, 0xF2, 0x00, + 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, 0xFF, 0x13, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, 0xFF, 0xFA, 0xFF, 0x83, + 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, 0xBF, 0xF7, 0x07, 0x05, + 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, + 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, + 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0x07, 0x00, + 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, 0x03, 0x20, + 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, 0x88, 0xFC, + 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, + 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, + 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, + 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, + 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, + 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x53, + 0xFF, 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, + 0xE1, 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, + 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, + 0x45, 0x01, 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, + 0x27, 0x2B, 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, + 0x01, 0xBC, 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, + 0xC3, 0xFF, 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, + 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, + 0x84, 0xFC, 0xF3, 0x06, 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, + 0xF1, 0x16, 0x07, 0x61, 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, + 0xFD, 0xFF, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, + 0x02, 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, + 0xF8, 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD2, 0x01, 0x3D, 0xFC, + 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, 0xC6, 0x35, 0xB7, 0xF1, 0x96, + 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, + 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, + 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, + 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF3, 0x06, + 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, 0x9C, 0x05, 0x7E, + 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, + 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, 0x5E, + 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, 0xFC, + 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, + 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, 0xF4, + 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, 0x77, + 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF0, 0xFF, + 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, + 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, + 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x45, + 0xFF, 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, + 0xFD, 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, + 0x00, 0xD3, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, + 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, + 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, + 0x01, 0x53, 0xFD, 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, + 0x76, 0xFC, 0x1F, 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, + 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, + 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, + 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, + 0xFD, 0x05, 0x03, 0xE1, 0xFB, 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, + 0xAA, 0xFD, 0x18, 0x02, 0x72, 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, + 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB3, 0x01, 0x64, 0xFC, + 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, 0x5A, 0x31, 0x7D, 0xF1, 0xF7, + 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, + 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, 0xFE, 0xAE, 0x01, 0x70, + 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, 0x14, 0xFB, 0x6D, 0x03, + 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, + 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, + 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x10, 0x00, + 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, 0x00, 0x0C, + 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, 0x26, 0xFD, + 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, + 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, + 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDD, 0xFF, + 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, + 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, + 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3B, + 0xFF, 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, + 0x89, 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, + 0x00, 0xE8, 0xFF, 0x07, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, + 0xA4, 0x00, 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, + 0x1D, 0x10, 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, + 0x01, 0xF6, 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, + 0x93, 0xF9, 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, + 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, + 0x41, 0xFD, 0xF6, 0x05, 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, + 0xF2, 0x0F, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, + 0xFD, 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, + 0x4B, 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, + 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, 0xFF, 0x85, 0x01, 0xA6, 0xFC, + 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, 0xAB, 0x2C, 0xA3, 0xF1, 0x26, + 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, 0xFF, 0x30, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, + 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, + 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFD, + 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, + 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, + 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, + 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, + 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x02, 0x00, 0x05, + 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, + 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, + 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, + 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x84, + 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, + 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, + 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, + 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, + 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, 0xFF, 0x64, 0x00, 0x9B, + 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, 0x10, 0x46, 0x03, 0x11, + 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, 0x01, 0x4C, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, 0x42, 0x00, + 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, 0x18, 0x5C, + 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, + 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, + 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, + 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, + 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, + 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, + 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, + 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, + 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, + 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, + 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, + 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, + 0xF2, 0x81, 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, + 0xFB, 0x00, 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, + 0x00, 0x38, 0xFF, 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, + 0xCE, 0x1A, 0x32, 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, + 0x00, 0xFB, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, + 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, + 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, + 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, + 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, + 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, + 0x00, 0xB1, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, + 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, + 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, + 0x8F, 0xFF, 0x1C, 0x00, 0x18, 0x00, 0x9C, 0xFF, 0xD6, 0x00, 0xB1, + 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, 0x9C, 0x48, 0xFD, 0x07, + 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, 0x01, 0x6E, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, 0x65, 0xFF, + 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, 0x0E, 0x6A, + 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, 0x2D, 0x00, + 0xFF, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, + 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, + 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, + 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, + 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, + 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, + 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, + 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, + 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, + 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x30, 0x00, 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, + 0xF1, 0x62, 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, + 0x82, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x3A, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, + 0xDD, 0x24, 0x54, 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, + 0x01, 0xA3, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, + 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, + 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, + 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, + 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, + 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, + 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, + 0xBB, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x43, + 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, 0x7E, 0x07, 0xAF, 0x48, + 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, 0xFE, 0xDD, 0x00, 0x99, + 0xFF, 0x19, 0x00, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, 0x79, 0xFE, + 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, 0x06, 0xC7, + 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, 0x22, 0x00, + 0x00, 0x00, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, + 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, + 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, + 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, + 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, + 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, + 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, + 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, + 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, + 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, + 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, + 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, + 0x20, 0x00, 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, + 0xF1, 0x86, 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, + 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2D, + 0x00, 0x54, 0xFF, 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, + 0x9F, 0x2E, 0xE3, 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, + 0x01, 0x64, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, + 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, + 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, + 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, + 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, + 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, + 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, + 0xE5, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9E, + 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, 0x75, 0x10, 0x48, 0x46, + 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, 0xFF, 0x6B, 0x00, 0xC6, + 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x5B, 0x01, + 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, 0x48, 0x8D, + 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, 0xA4, 0xFF, + 0x16, 0x00, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, + 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, + 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, + 0x00, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, + 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, + 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, + 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, + 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x06, + 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, + 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, + 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x02, 0x00, + 0x05, 0x00, 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, + 0xF3, 0x61, 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, + 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x1A, + 0x00, 0x8A, 0xFF, 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, + 0x82, 0x37, 0x60, 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, + 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x29, 0x00, + 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, + 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x43, + 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, + 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, + 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, + 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, + 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, + 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, 0x38, 0x1A, 0x8C, 0x41, + 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, 0x00, 0x01, 0x00, 0xEE, + 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, 0xAF, 0x01, + 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, 0x45, 0xA9, + 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, 0xD0, 0xFF, + 0x0C, 0x00, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, + 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, + 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, + 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, + 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, + 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, + 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, + 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x0F, + 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, + 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, + 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x08, 0x00, + 0xE1, 0xFF, 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, + 0xF6, 0x77, 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, + 0xC8, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xFD, + 0xFF, 0xD9, 0xFF, 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, + 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x14, 0x00, + 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, + 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x25, 0x00, 0x6A, + 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, + 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, + 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, + 0x22, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, 0x46, 0x24, 0xC8, 0x3A, + 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, 0x01, 0xA8, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, + 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, 0x40, 0x85, + 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, 0xF7, 0xFF, + 0x04, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, + 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, + 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, + 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, + 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, + 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, + 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, + 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0x1A, + 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, + 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, + 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0x12, 0x00, + 0xB6, 0xFF, 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, + 0xFB, 0x69, 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, + 0x81, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD7, + 0xFF, 0x3E, 0x00, 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, + 0x99, 0x44, 0x68, 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, + 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF5, 0xFF, + 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, + 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, + 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, + 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, + 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, + 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, + 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2E, 0x00, 0x52, 0xFF, 0xBC, + 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, 0x11, 0x2E, 0x6B, 0x32, + 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, 0x01, 0x67, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE0, 0x01, + 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, 0x38, 0x2A, + 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, 0x16, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, + 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, + 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, + 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, + 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, + 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, + 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, + 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, + 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0x00, + 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, + 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, + 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0x1D, 0x00, + 0x8A, 0xFF, 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, + 0x02, 0xF2, 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, + 0x1E, 0x01, 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAC, + 0xFF, 0xAE, 0x00, 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, + 0xFD, 0x47, 0x0E, 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, + 0x01, 0x61, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, + 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, + 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, + 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, + 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, + 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, + 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, + 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1C, 0x00, 0x86, 0xFF, 0x59, + 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, 0x04, 0x37, 0xF3, 0x28, + 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x41, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, + 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, + 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, + 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, + 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, + 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, + 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, + 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, + 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, + 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, + 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0xFF, + 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, + 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, + 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, + 0xF9, 0x0E, 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, + 0x05, 0xFF, 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x7F, 0xFF, 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, + 0x9B, 0x04, 0xF2, 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, + 0xFE, 0x04, 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0x17, 0x00, 0xA1, 0xFF, + 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, + 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, + 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC3, 0xFF, 0x71, + 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, + 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, + 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, + 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, + 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, 0x98, 0x3E, 0xF3, 0x1E, + 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, 0x39, 0x01, + 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, 0x26, 0x4B, + 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, + 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, + 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, + 0xFF, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, + 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, + 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, + 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, + 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, + 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, + 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, + 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFF, 0xFF, + 0x32, 0x00, 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, + 0xF6, 0x68, 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, + 0xEA, 0xFF, 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x59, 0xFF, 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, + 0x2D, 0x0D, 0x69, 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, + 0xFF, 0x93, 0x00, 0xB6, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, + 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, + 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, + 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0x1A, 0x00, 0x96, 0xFF, 0xE3, + 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, + 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, + 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, + 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, + 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD9, 0xFF, 0x37, 0x00, 0xF7, + 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, 0x53, 0x44, 0xFB, 0x14, + 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, 0x01, 0x42, 0xFF, 0x32, + 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, 0x00, + 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, 0x1C, 0x47, + 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, + 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, + 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, + 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, + 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, + 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, + 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, + 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, + 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, + 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, + 0xF3, 0x5B, 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, + 0xB4, 0x00, 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, + 0x00, 0x3E, 0xFF, 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, + 0xB6, 0x16, 0x77, 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, + 0x00, 0x25, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFF, 0xFF, 0x2D, 0x00, + 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, + 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, + 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x6C, + 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, + 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, + 0x00, 0x9E, 0xFF, 0x17, 0x00, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, + 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, + 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAE, 0xFF, 0xA7, 0x00, 0x12, + 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, 0xDC, 0x47, 0x95, 0x0B, + 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, 0x01, 0x5F, 0xFF, 0x29, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, 0xC3, 0xFF, + 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, 0x12, 0x19, + 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, 0x30, 0x00, + 0xFF, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, + 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, + 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, + 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, + 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, + 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, + 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, + 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, + 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, + 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x34, 0x00, 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, + 0xF2, 0x60, 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, + 0x51, 0x01, 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, + 0xBA, 0x20, 0x61, 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, + 0x00, 0xC5, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, + 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, + 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, + 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, + 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, + 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, + 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, + 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, + 0xA9, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x82, 0xFF, 0x18, + 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, 0x24, 0x04, 0xF5, 0x48, + 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, 0xFE, 0x0B, 0x01, 0x87, + 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, 0xDB, 0xFE, + 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, 0x09, 0x61, + 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, 0x26, 0x00, + 0x00, 0x00, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, + 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, + 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, + 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, + 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, + 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, + 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, + 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, + 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, + 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, + 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x28, 0x00, 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, + 0xF1, 0xE3, 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, + 0xB7, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x32, + 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, + 0xAE, 0x2A, 0x86, 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, + 0x01, 0x7B, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, + 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, + 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, + 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, + 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, + 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, + 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, + 0xD5, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, 0xFF, 0x7C, + 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, 0xA4, 0x0C, 0x90, 0x47, + 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, 0xFF, 0x99, 0x00, 0xB3, + 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, 0x31, 0x01, + 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, 0x48, 0x73, + 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, 0x91, 0xFF, + 0x1B, 0x00, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, + 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, + 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, + 0x00, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, + 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, + 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, + 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, + 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, + 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x04, + 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, + 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, + 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, + 0x11, 0x00, 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, + 0xF2, 0x54, 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, + 0xE4, 0x01, 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x23, + 0x00, 0x71, 0xFF, 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, + 0xFD, 0x33, 0x62, 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, + 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2F, 0x00, + 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, + 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, + 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3B, + 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, + 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, + 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, + 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, + 0xFB, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC5, + 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, 0x22, 0x16, 0xC3, 0x43, + 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, 0x00, 0x2B, 0x00, 0xDE, + 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x90, 0x01, + 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, 0x46, 0xE1, + 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, 0xBE, 0xFF, + 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, + 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, + 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, + 0x00, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, + 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, + 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, + 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, + 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, + 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, + 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, + 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x05, 0x00, + 0xF0, 0xFF, 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, + 0xF5, 0x32, 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, + 0xDA, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, + 0x00, 0xB6, 0xFF, 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, + 0x1C, 0x3C, 0x81, 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1E, 0x00, + 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, + 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2C, 0x00, 0x57, + 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, + 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, + 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, + 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, + 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, + 0x19, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, + 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, 0x22, 0x20, 0xCA, 0x3D, + 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, 0x00, 0xCA, 0xFF, 0x03, + 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, + 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, 0x42, 0x02, + 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, 0xE8, 0xFF, + 0x07, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, + 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, + 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, + 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, + 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, + 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, + 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, + 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, + 0x00, 0xF4, 0xFF, 0x1A, 0x00, 0xFF, 0x00, 0x07, 0x03, 0x16, 0x06, + 0x7C, 0x09, 0x2A, 0x0C, 0x2E, 0x0D, 0x2A, 0x0C, 0x7C, 0x09, 0x16, + 0x06, 0x07, 0x03, 0xFF, 0x00, 0x1A, 0x00, 0xF4, 0xFF, 0xF2, 0xFF, + 0xA0, 0xFF, 0x71, 0xFF, 0x71, 0x00, 0x86, 0x03, 0x73, 0x08, 0x88, + 0x0D, 0x78, 0x10, 0xC9, 0x0F, 0xD5, 0x0B, 0x8B, 0x06, 0x28, 0x02, + 0xDF, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDC, + 0xFF, 0x80, 0xFF, 0x9A, 0xFF, 0x46, 0x01, 0x1E, 0x05, 0x5A, 0x0A, + 0xED, 0x0E, 0xAA, 0x10, 0xAF, 0x0E, 0xFD, 0x09, 0xCB, 0x04, 0x18, + 0x01, 0x8E, 0xFF, 0x85, 0xFF, 0xE1, 0xFF, 0xFC, 0xFF, 0xBD, 0xFF, + 0x6D, 0xFF, 0xF6, 0xFF, 0x65, 0x02, 0xE5, 0x06, 0x2B, 0x0C, 0xF3, + 0x0F, 0x60, 0x10, 0x3B, 0x0D, 0x16, 0x08, 0x3F, 0x03, 0x50, 0x00, + 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, 0xFF, 0xEF, 0xFF, 0x9A, 0xFF, 0x75, + 0xFF, 0x91, 0x00, 0xC9, 0x03, 0xC8, 0x08, 0xCC, 0x0D, 0x89, 0x10, + 0x9F, 0x0F, 0x85, 0x0B, 0x3B, 0x06, 0xF4, 0x01, 0xCD, 0xFF, 0x72, + 0xFF, 0xC9, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0x7B, 0xFF, + 0xA5, 0xFF, 0x73, 0x01, 0x6A, 0x05, 0xAD, 0x0A, 0x21, 0x0F, 0xA6, + 0x10, 0x74, 0x0E, 0xA9, 0x09, 0x83, 0x04, 0xF0, 0x00, 0x85, 0xFF, + 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, 0xFF, 0xB7, 0xFF, 0x6C, 0xFF, 0x0C, + 0x00, 0x9D, 0x02, 0x37, 0x07, 0x78, 0x0C, 0x15, 0x10, 0x47, 0x10, + 0xF3, 0x0C, 0xC2, 0x07, 0x01, 0x03, 0x35, 0x00, 0x6D, 0xFF, 0xAD, + 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, 0x94, 0xFF, 0x7A, 0xFF, 0xB3, 0x00, + 0x0D, 0x04, 0x1C, 0x09, 0x0D, 0x0E, 0x97, 0x10, 0x73, 0x0F, 0x35, + 0x0B, 0xEB, 0x05, 0xC1, 0x01, 0xBD, 0xFF, 0x75, 0xFF, 0xCE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x77, 0xFF, 0xB3, 0xFF, 0xA1, + 0x01, 0xB7, 0x05, 0xFF, 0x0A, 0x53, 0x0F, 0x9E, 0x10, 0x37, 0x0E, + 0x55, 0x09, 0x3B, 0x04, 0xCB, 0x00, 0x7E, 0xFF, 0x90, 0xFF, 0xE9, + 0xFF, 0xF8, 0xFF, 0xB1, 0xFF, 0x6C, 0xFF, 0x24, 0x00, 0xD8, 0x02, + 0x8A, 0x07, 0xC2, 0x0C, 0x34, 0x10, 0x2A, 0x10, 0xAA, 0x0C, 0x6F, + 0x07, 0xC4, 0x02, 0x1C, 0x00, 0x6C, 0xFF, 0xB3, 0xFF, 0xF9, 0xFF, + 0xE8, 0xFF, 0x8E, 0xFF, 0x80, 0xFF, 0xD7, 0x00, 0x53, 0x04, 0x71, + 0x09, 0x4C, 0x0E, 0xA1, 0x10, 0x43, 0x0F, 0xE3, 0x0A, 0x9D, 0x05, + 0x91, 0x01, 0xAE, 0xFF, 0x79, 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC2, 0xFF, 0xD2, 0x01, 0x06, 0x06, + 0x50, 0x0B, 0x82, 0x0F, 0x93, 0x10, 0xF8, 0x0D, 0x00, 0x09, 0xF6, + 0x03, 0xA7, 0x00, 0x78, 0xFF, 0x96, 0xFF, 0xEC, 0xFF, 0xF6, 0xFF, + 0xAB, 0xFF, 0x6D, 0xFF, 0x3E, 0x00, 0x15, 0x03, 0xDE, 0x07, 0x0B, + 0x0D, 0x50, 0x10, 0x0A, 0x10, 0x5E, 0x0C, 0x1C, 0x07, 0x8A, 0x02, + 0x04, 0x00, 0x6C, 0xFF, 0xB9, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, 0x89, + 0xFF, 0x88, 0xFF, 0xFD, 0x00, 0x9B, 0x04, 0xC5, 0x09, 0x88, 0x0E, + 0xA8, 0x10, 0x10, 0x0F, 0x91, 0x0A, 0x50, 0x05, 0x64, 0x01, 0xA1, + 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, + 0x71, 0xFF, 0xD3, 0xFF, 0x05, 0x02, 0x55, 0x06, 0xA0, 0x0B, 0xAD, + 0x0F, 0x84, 0x10, 0xB6, 0x0D, 0xAC, 0x08, 0xB3, 0x03, 0x86, 0x00, + 0x74, 0xFF, 0x9C, 0xFF, 0xF0, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, 0x6F, + 0xFF, 0x5A, 0x00, 0x54, 0x03, 0x32, 0x08, 0x52, 0x0D, 0x68, 0x10, + 0xE6, 0x0F, 0x11, 0x0C, 0xCA, 0x06, 0x52, 0x02, 0xEF, 0xFF, 0x6E, + 0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xDF, 0xFF, 0x84, 0xFF, 0x91, 0xFF, + 0x25, 0x01, 0xE4, 0x04, 0x19, 0x0A, 0xC2, 0x0E, 0xAA, 0x10, 0xDA, + 0x0E, 0x3E, 0x0A, 0x05, 0x05, 0x38, 0x01, 0x96, 0xFF, 0x81, 0xFF, + 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE6, + 0xFF, 0x3A, 0x02, 0xA6, 0x06, 0xEF, 0x0B, 0xD6, 0x0F, 0x71, 0x10, + 0x71, 0x0D, 0x57, 0x08, 0x71, 0x03, 0x67, 0x00, 0x70, 0xFF, 0xA2, + 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x78, 0x00, + 0x95, 0x03, 0x86, 0x08, 0x98, 0x0D, 0x7C, 0x10, 0xC0, 0x0F, 0xC3, + 0x0B, 0x79, 0x06, 0x1C, 0x02, 0xDB, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, + 0xFE, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9C, 0xFF, 0x50, + 0x01, 0x2F, 0x05, 0x6C, 0x0A, 0xF9, 0x0E, 0xA9, 0x10, 0xA2, 0x0E, + 0xEA, 0x09, 0xBB, 0x04, 0x0F, 0x01, 0x8C, 0xFF, 0x87, 0xFF, 0xE2, + 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xFA, 0xFF, 0x71, 0x02, + 0xF7, 0x06, 0x3C, 0x0C, 0xFB, 0x0F, 0x5B, 0x10, 0x2B, 0x0D, 0x03, + 0x08, 0x31, 0x03, 0x4A, 0x00, 0x6E, 0xFF, 0xA8, 0xFF, 0xF5, 0xFF, + 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x98, 0x00, 0xD8, 0x03, 0xDB, + 0x08, 0xDB, 0x0D, 0x8D, 0x10, 0x96, 0x0F, 0x73, 0x0B, 0x29, 0x06, + 0xE8, 0x01, 0xC9, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, 0x00, + 0x00, 0xD6, 0xFF, 0x7A, 0xFF, 0xA8, 0xFF, 0x7D, 0x01, 0x7B, 0x05, + 0xBF, 0x0A, 0x2D, 0x0F, 0xA5, 0x10, 0x67, 0x0E, 0x96, 0x09, 0x73, + 0x04, 0xE7, 0x00, 0x84, 0xFF, 0x8C, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, + 0xB6, 0xFF, 0x6C, 0xFF, 0x11, 0x00, 0xAA, 0x02, 0x4A, 0x07, 0x88, + 0x0C, 0x1C, 0x10, 0x41, 0x10, 0xE3, 0x0C, 0xAF, 0x07, 0xF3, 0x02, + 0x2F, 0x00, 0x6C, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, + 0xFF, 0x7B, 0xFF, 0xBB, 0x00, 0x1C, 0x04, 0x2F, 0x09, 0x1B, 0x0E, + 0x9A, 0x10, 0x68, 0x0F, 0x23, 0x0B, 0xDA, 0x05, 0xB7, 0x01, 0xB9, + 0xFF, 0x76, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0xFF, + 0x76, 0xFF, 0xB6, 0xFF, 0xAC, 0x01, 0xC8, 0x05, 0x11, 0x0B, 0x5E, + 0x0F, 0x9C, 0x10, 0x29, 0x0E, 0x42, 0x09, 0x2C, 0x04, 0xC2, 0x00, + 0x7D, 0xFF, 0x92, 0xFF, 0xEA, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, 0x6C, + 0xFF, 0x29, 0x00, 0xE6, 0x02, 0x9D, 0x07, 0xD3, 0x0C, 0x3B, 0x10, + 0x23, 0x10, 0x99, 0x0C, 0x5C, 0x07, 0xB7, 0x02, 0x16, 0x00, 0x6C, + 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8D, 0xFF, 0x82, 0xFF, + 0xDF, 0x00, 0x63, 0x04, 0x84, 0x09, 0x59, 0x0E, 0xA3, 0x10, 0x38, + 0x0F, 0xD1, 0x0A, 0x8C, 0x05, 0x87, 0x01, 0xAB, 0xFF, 0x79, 0xFF, + 0xD5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC6, + 0xFF, 0xDD, 0x01, 0x17, 0x06, 0x62, 0x0B, 0x8C, 0x0F, 0x90, 0x10, + 0xE9, 0x0D, 0xED, 0x08, 0xE7, 0x03, 0xA0, 0x00, 0x77, 0xFF, 0x97, + 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xA9, 0xFF, 0x6D, 0xFF, 0x44, 0x00, + 0x23, 0x03, 0xF1, 0x07, 0x1B, 0x0D, 0x55, 0x10, 0x02, 0x10, 0x4D, + 0x0C, 0x0A, 0x07, 0x7E, 0x02, 0xFF, 0xFF, 0x6D, 0xFF, 0xBA, 0xFF, + 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x8A, 0xFF, 0x06, 0x01, 0xAB, + 0x04, 0xD8, 0x09, 0x95, 0x0E, 0xA9, 0x10, 0x05, 0x0F, 0x7F, 0x0A, + 0x40, 0x05, 0x5A, 0x01, 0x9F, 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, + 0x00, 0xFE, 0xFF, 0xC6, 0xFF, 0x70, 0xFF, 0xD7, 0xFF, 0x10, 0x02, + 0x67, 0x06, 0xB1, 0x0B, 0xB7, 0x0F, 0x80, 0x10, 0xA7, 0x0D, 0x99, + 0x08, 0xA4, 0x03, 0x7F, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, 0xFF, + 0xF3, 0xFF, 0xA3, 0xFF, 0x70, 0xFF, 0x60, 0x00, 0x62, 0x03, 0x45, + 0x08, 0x62, 0x0D, 0x6C, 0x10, 0xDE, 0x0F, 0x00, 0x0C, 0xB8, 0x06, + 0x46, 0x02, 0xEA, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFD, 0xFF, 0x00, + 0x00, 0xDE, 0xFF, 0x83, 0xFF, 0x94, 0xFF, 0x2F, 0x01, 0xF4, 0x04, + 0x2B, 0x0A, 0xCE, 0x0E, 0xAA, 0x10, 0xCE, 0x0E, 0x2B, 0x0A, 0xF4, + 0x04, 0x2F, 0x01, 0x94, 0xFF, 0x83, 0xFF, 0xDE, 0xFF, 0xFD, 0xFF, + 0xC0, 0xFF, 0x6E, 0xFF, 0xEA, 0xFF, 0x46, 0x02, 0xB8, 0x06, 0x00, + 0x0C, 0xDE, 0x0F, 0x6C, 0x10, 0x62, 0x0D, 0x45, 0x08, 0x62, 0x03, + 0x60, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, + 0xFF, 0x73, 0xFF, 0x7F, 0x00, 0xA4, 0x03, 0x99, 0x08, 0xA7, 0x0D, + 0x80, 0x10, 0xB7, 0x0F, 0xB1, 0x0B, 0x67, 0x06, 0x10, 0x02, 0xD7, + 0xFF, 0x70, 0xFF, 0xC6, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, 0xFF, + 0x7E, 0xFF, 0x9F, 0xFF, 0x5A, 0x01, 0x40, 0x05, 0x7F, 0x0A, 0x05, + 0x0F, 0xA9, 0x10, 0x95, 0x0E, 0xD8, 0x09, 0xAB, 0x04, 0x06, 0x01, + 0x8A, 0xFF, 0x88, 0xFF, 0xE3, 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, + 0xFF, 0xFF, 0xFF, 0x7E, 0x02, 0x0A, 0x07, 0x4D, 0x0C, 0x02, 0x10, + 0x55, 0x10, 0x1B, 0x0D, 0xF1, 0x07, 0x23, 0x03, 0x44, 0x00, 0x6D, + 0xFF, 0xA9, 0xFF, 0xF6, 0xFF, 0xED, 0xFF, 0x97, 0xFF, 0x77, 0xFF, + 0xA0, 0x00, 0xE7, 0x03, 0xED, 0x08, 0xE9, 0x0D, 0x90, 0x10, 0x8C, + 0x0F, 0x62, 0x0B, 0x17, 0x06, 0xDD, 0x01, 0xC6, 0xFF, 0x73, 0xFF, + 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x79, 0xFF, 0xAB, + 0xFF, 0x87, 0x01, 0x8C, 0x05, 0xD1, 0x0A, 0x38, 0x0F, 0xA3, 0x10, + 0x59, 0x0E, 0x84, 0x09, 0x63, 0x04, 0xDF, 0x00, 0x82, 0xFF, 0x8D, + 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, 0xB4, 0xFF, 0x6C, 0xFF, 0x16, 0x00, + 0xB7, 0x02, 0x5C, 0x07, 0x99, 0x0C, 0x23, 0x10, 0x3B, 0x10, 0xD3, + 0x0C, 0x9D, 0x07, 0xE6, 0x02, 0x29, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, + 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7D, 0xFF, 0xC2, 0x00, 0x2C, + 0x04, 0x42, 0x09, 0x29, 0x0E, 0x9C, 0x10, 0x5E, 0x0F, 0x11, 0x0B, + 0xC8, 0x05, 0xAC, 0x01, 0xB6, 0xFF, 0x76, 0xFF, 0xD1, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB9, 0xFF, 0xB7, 0x01, + 0xDA, 0x05, 0x23, 0x0B, 0x68, 0x0F, 0x9A, 0x10, 0x1B, 0x0E, 0x2F, + 0x09, 0x1C, 0x04, 0xBB, 0x00, 0x7B, 0xFF, 0x93, 0xFF, 0xEA, 0xFF, + 0xF7, 0xFF, 0xAE, 0xFF, 0x6C, 0xFF, 0x2F, 0x00, 0xF3, 0x02, 0xAF, + 0x07, 0xE3, 0x0C, 0x41, 0x10, 0x1C, 0x10, 0x88, 0x0C, 0x4A, 0x07, + 0xAA, 0x02, 0x11, 0x00, 0x6C, 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE6, + 0xFF, 0x8C, 0xFF, 0x84, 0xFF, 0xE7, 0x00, 0x73, 0x04, 0x96, 0x09, + 0x67, 0x0E, 0xA5, 0x10, 0x2D, 0x0F, 0xBF, 0x0A, 0x7B, 0x05, 0x7D, + 0x01, 0xA8, 0xFF, 0x7A, 0xFF, 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, + 0xCA, 0xFF, 0x72, 0xFF, 0xC9, 0xFF, 0xE8, 0x01, 0x29, 0x06, 0x73, + 0x0B, 0x96, 0x0F, 0x8D, 0x10, 0xDB, 0x0D, 0xDB, 0x08, 0xD8, 0x03, + 0x98, 0x00, 0x76, 0xFF, 0x99, 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA8, + 0xFF, 0x6E, 0xFF, 0x4A, 0x00, 0x31, 0x03, 0x03, 0x08, 0x2B, 0x0D, + 0x5B, 0x10, 0xFB, 0x0F, 0x3C, 0x0C, 0xF7, 0x06, 0x71, 0x02, 0xFA, + 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, 0xFC, 0xFF, 0xE2, 0xFF, 0x87, 0xFF, + 0x8C, 0xFF, 0x0F, 0x01, 0xBB, 0x04, 0xEA, 0x09, 0xA2, 0x0E, 0xA9, + 0x10, 0xF9, 0x0E, 0x6C, 0x0A, 0x2F, 0x05, 0x50, 0x01, 0x9C, 0xFF, + 0x7F, 0xFF, 0xDB, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, 0x70, + 0xFF, 0xDB, 0xFF, 0x1C, 0x02, 0x79, 0x06, 0xC3, 0x0B, 0xC0, 0x0F, + 0x7C, 0x10, 0x98, 0x0D, 0x86, 0x08, 0x95, 0x03, 0x78, 0x00, 0x72, + 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA2, 0xFF, 0x70, 0xFF, + 0x67, 0x00, 0x71, 0x03, 0x57, 0x08, 0x71, 0x0D, 0x71, 0x10, 0xD6, + 0x0F, 0xEF, 0x0B, 0xA6, 0x06, 0x3A, 0x02, 0xE6, 0xFF, 0x6E, 0xFF, + 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x96, + 0xFF, 0x38, 0x01, 0x05, 0x05, 0x3E, 0x0A, 0xDA, 0x0E, 0xAA, 0x10, + 0xC2, 0x0E, 0x19, 0x0A, 0xE4, 0x04, 0x25, 0x01, 0x91, 0xFF, 0x84, + 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xBF, 0xFF, 0x6E, 0xFF, 0xEF, 0xFF, + 0x52, 0x02, 0xCA, 0x06, 0x11, 0x0C, 0xE6, 0x0F, 0x68, 0x10, 0x52, + 0x0D, 0x32, 0x08, 0x54, 0x03, 0x5A, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, + 0xF4, 0xFF, 0xF0, 0xFF, 0x9C, 0xFF, 0x74, 0xFF, 0x86, 0x00, 0xB3, + 0x03, 0xAC, 0x08, 0xB6, 0x0D, 0x84, 0x10, 0xAD, 0x0F, 0xA0, 0x0B, + 0x55, 0x06, 0x05, 0x02, 0xD3, 0xFF, 0x71, 0xFF, 0xC7, 0xFF, 0xFE, + 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA1, 0xFF, 0x64, 0x01, + 0x50, 0x05, 0x91, 0x0A, 0x10, 0x0F, 0xA8, 0x10, 0x88, 0x0E, 0xC5, + 0x09, 0x9B, 0x04, 0xFD, 0x00, 0x88, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, + 0xFB, 0xFF, 0xB9, 0xFF, 0x6C, 0xFF, 0x04, 0x00, 0x8A, 0x02, 0x1C, + 0x07, 0x5E, 0x0C, 0x0A, 0x10, 0x50, 0x10, 0x0B, 0x0D, 0xDE, 0x07, + 0x15, 0x03, 0x3E, 0x00, 0x6D, 0xFF, 0xAB, 0xFF, 0xF6, 0xFF, 0xEC, + 0xFF, 0x96, 0xFF, 0x78, 0xFF, 0xA7, 0x00, 0xF6, 0x03, 0x00, 0x09, + 0xF8, 0x0D, 0x93, 0x10, 0x82, 0x0F, 0x50, 0x0B, 0x06, 0x06, 0xD2, + 0x01, 0xC2, 0xFF, 0x74, 0xFF, 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0xD4, 0xFF, 0x79, 0xFF, 0xAE, 0xFF, 0x91, 0x01, 0x9D, 0x05, 0xE3, + 0x0A, 0x43, 0x0F, 0xA1, 0x10, 0x4C, 0x0E, 0x71, 0x09, 0x53, 0x04, + 0xD7, 0x00, 0x80, 0xFF, 0x8E, 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB3, + 0xFF, 0x6C, 0xFF, 0x1C, 0x00, 0xC4, 0x02, 0x6F, 0x07, 0xAA, 0x0C, + 0x2A, 0x10, 0x34, 0x10, 0xC2, 0x0C, 0x8A, 0x07, 0xD8, 0x02, 0x24, + 0x00, 0x6C, 0xFF, 0xB1, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x90, 0xFF, + 0x7E, 0xFF, 0xCB, 0x00, 0x3B, 0x04, 0x55, 0x09, 0x37, 0x0E, 0x9E, + 0x10, 0x53, 0x0F, 0xFF, 0x0A, 0xB7, 0x05, 0xA1, 0x01, 0xB3, 0xFF, + 0x77, 0xFF, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x75, + 0xFF, 0xBD, 0xFF, 0xC1, 0x01, 0xEB, 0x05, 0x35, 0x0B, 0x73, 0x0F, + 0x97, 0x10, 0x0D, 0x0E, 0x1C, 0x09, 0x0D, 0x04, 0xB3, 0x00, 0x7A, + 0xFF, 0x94, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAD, 0xFF, 0x6D, 0xFF, + 0x35, 0x00, 0x01, 0x03, 0xC2, 0x07, 0xF3, 0x0C, 0x47, 0x10, 0x15, + 0x10, 0x78, 0x0C, 0x37, 0x07, 0x9D, 0x02, 0x0C, 0x00, 0x6C, 0xFF, + 0xB7, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, 0xF0, + 0x00, 0x83, 0x04, 0xA9, 0x09, 0x74, 0x0E, 0xA6, 0x10, 0x21, 0x0F, + 0xAD, 0x0A, 0x6A, 0x05, 0x73, 0x01, 0xA5, 0xFF, 0x7B, 0xFF, 0xD7, + 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC9, 0xFF, 0x72, 0xFF, 0xCD, 0xFF, + 0xF4, 0x01, 0x3B, 0x06, 0x85, 0x0B, 0x9F, 0x0F, 0x89, 0x10, 0xCC, + 0x0D, 0xC8, 0x08, 0xC9, 0x03, 0x91, 0x00, 0x75, 0xFF, 0x9A, 0xFF, + 0xEF, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x50, 0x00, 0x3F, + 0x03, 0x16, 0x08, 0x3B, 0x0D, 0x60, 0x10, 0xF3, 0x0F, 0x2B, 0x0C, + 0xE5, 0x06, 0x65, 0x02, 0xF6, 0xFF, 0x6D, 0xFF, 0xBD, 0xFF, 0xFC, + 0xFF, 0xE1, 0xFF, 0x85, 0xFF, 0x8E, 0xFF, 0x18, 0x01, 0xCB, 0x04, + 0xFD, 0x09, 0xAF, 0x0E, 0xAA, 0x10, 0xED, 0x0E, 0x5A, 0x0A, 0x1E, + 0x05, 0x46, 0x01, 0x9A, 0xFF, 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, + 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, 0xFF, 0xDF, 0xFF, 0x28, 0x02, 0x8B, + 0x06, 0xD5, 0x0B, 0xC9, 0x0F, 0x78, 0x10, 0x88, 0x0D, 0x73, 0x08, + 0x86, 0x03, 0x71, 0x00, 0x71, 0xFF, 0xA0, 0xFF, 0xF2, 0xFF, 0xF2, + 0xFF, 0xA1, 0xFF, 0x71, 0xFF, 0x6E, 0x00, 0x7F, 0x03, 0x6A, 0x08, + 0x81, 0x0D, 0x76, 0x10, 0xCD, 0x0F, 0xDD, 0x0B, 0x94, 0x06, 0x2E, + 0x02, 0xE1, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, + 0xDC, 0xFF, 0x80, 0xFF, 0x98, 0xFF, 0x42, 0x01, 0x16, 0x05, 0x50, + 0x0A, 0xE7, 0x0E, 0xAA, 0x10, 0xB5, 0x0E, 0x06, 0x0A, 0xD3, 0x04, + 0x1C, 0x01, 0x8F, 0xFF, 0x85, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, 0xBE, + 0xFF, 0x6D, 0xFF, 0xF3, 0xFF, 0x5E, 0x02, 0xDC, 0x06, 0x23, 0x0C, + 0xEF, 0x0F, 0x63, 0x10, 0x43, 0x0D, 0x1F, 0x08, 0x46, 0x03, 0x53, + 0x00, 0x6E, 0xFF, 0xA6, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, 0xFF, + 0x75, 0xFF, 0x8D, 0x00, 0xC1, 0x03, 0xBE, 0x08, 0xC4, 0x0D, 0x88, + 0x10, 0xA4, 0x0F, 0x8E, 0x0B, 0x43, 0x06, 0xF9, 0x01, 0xCF, 0xFF, + 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, 0x7C, + 0xFF, 0xA4, 0xFF, 0x6E, 0x01, 0x61, 0x05, 0xA3, 0x0A, 0x1C, 0x0F, + 0xA7, 0x10, 0x7B, 0x0E, 0xB2, 0x09, 0x8B, 0x04, 0xF4, 0x00, 0x86, + 0xFF, 0x8A, 0xFF, 0xE4, 0xFF, 0xFA, 0xFF, 0xB8, 0xFF, 0x6C, 0xFF, + 0x09, 0x00, 0x97, 0x02, 0x2E, 0x07, 0x6F, 0x0C, 0x11, 0x10, 0x4A, + 0x10, 0xFB, 0x0C, 0xCB, 0x07, 0x07, 0x03, 0x38, 0x00, 0x6D, 0xFF, + 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, 0xAF, + 0x00, 0x05, 0x04, 0x13, 0x09, 0x06, 0x0E, 0x96, 0x10, 0x78, 0x0F, + 0x3E, 0x0B, 0xF4, 0x05, 0xC7, 0x01, 0xBF, 0xFF, 0x74, 0xFF, 0xCE, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x78, 0xFF, 0xB1, 0xFF, + 0x9C, 0x01, 0xAE, 0x05, 0xF6, 0x0A, 0x4E, 0x0F, 0x9F, 0x10, 0x3E, + 0x0E, 0x5E, 0x09, 0x43, 0x04, 0xCF, 0x00, 0x7F, 0xFF, 0x90, 0xFF, + 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x21, 0x00, 0xD2, + 0x02, 0x81, 0x07, 0xBA, 0x0C, 0x31, 0x10, 0x2E, 0x10, 0xB2, 0x0C, + 0x78, 0x07, 0xCB, 0x02, 0x1E, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, 0xF9, + 0xFF, 0xE8, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xD3, 0x00, 0x4B, 0x04, + 0x67, 0x09, 0x45, 0x0E, 0xA0, 0x10, 0x48, 0x0F, 0xEC, 0x0A, 0xA6, + 0x05, 0x97, 0x01, 0xB0, 0xFF, 0x78, 0xFF, 0xD3, 0xFF, 0x00, 0x00, + 0xFF, 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC0, 0xFF, 0xCC, 0x01, 0xFD, + 0x05, 0x47, 0x0B, 0x7D, 0x0F, 0x94, 0x10, 0xFF, 0x0D, 0x0A, 0x09, + 0xFE, 0x03, 0xAB, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, 0xF7, + 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x3B, 0x00, 0x0E, 0x03, 0xD5, 0x07, + 0x03, 0x0D, 0x4D, 0x10, 0x0E, 0x10, 0x67, 0x0C, 0x25, 0x07, 0x91, + 0x02, 0x07, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, + 0x89, 0xFF, 0x87, 0xFF, 0xF9, 0x00, 0x93, 0x04, 0xBC, 0x09, 0x82, + 0x0E, 0xA7, 0x10, 0x16, 0x0F, 0x9A, 0x0A, 0x59, 0x05, 0x69, 0x01, + 0xA3, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC8, + 0xFF, 0x71, 0xFF, 0xD1, 0xFF, 0xFF, 0x01, 0x4C, 0x06, 0x97, 0x0B, + 0xA9, 0x0F, 0x86, 0x10, 0xBD, 0x0D, 0xB5, 0x08, 0xBA, 0x03, 0x8A, + 0x00, 0x74, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, + 0x6F, 0xFF, 0x57, 0x00, 0x4D, 0x03, 0x29, 0x08, 0x4B, 0x0D, 0x65, + 0x10, 0xEB, 0x0F, 0x1A, 0x0C, 0xD3, 0x06, 0x58, 0x02, 0xF1, 0xFF, + 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x84, 0xFF, 0x90, + 0xFF, 0x21, 0x01, 0xDC, 0x04, 0x10, 0x0A, 0xBB, 0x0E, 0xAA, 0x10, + 0xE1, 0x0E, 0x47, 0x0A, 0x0D, 0x05, 0x3D, 0x01, 0x97, 0xFF, 0x81, + 0xFF, 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC2, 0xFF, 0x6F, 0xFF, + 0xE4, 0xFF, 0x34, 0x02, 0x9D, 0x06, 0xE6, 0x0B, 0xD1, 0x0F, 0x73, + 0x10, 0x79, 0x0D, 0x61, 0x08, 0x78, 0x03, 0x6A, 0x00, 0x70, 0xFF, + 0xA1, 0xFF, 0xF2, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x74, + 0x00, 0x8E, 0x03, 0x7D, 0x08, 0x90, 0x0D, 0x7A, 0x10, 0xC4, 0x0F, + 0xCC, 0x0B, 0x82, 0x06, 0x22, 0x02, 0xDD, 0xFF, 0x6F, 0xFF, 0xC4, + 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9B, 0xFF, + 0x4B, 0x01, 0x26, 0x05, 0x63, 0x0A, 0xF3, 0x0E, 0xAA, 0x10, 0xA8, + 0x0E, 0xF4, 0x09, 0xC3, 0x04, 0x13, 0x01, 0x8D, 0xFF, 0x86, 0xFF, + 0xE1, 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xF8, 0xFF, 0x6B, + 0x02, 0xEE, 0x06, 0x34, 0x0C, 0xF7, 0x0F, 0x5D, 0x10, 0x33, 0x0D, + 0x0D, 0x08, 0x38, 0x03, 0x4D, 0x00, 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, + 0xFF, 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x94, 0x00, 0xD0, 0x03, + 0xD1, 0x08, 0xD3, 0x0D, 0x8B, 0x10, 0x9A, 0x0F, 0x7C, 0x0B, 0x32, + 0x06, 0xEE, 0x01, 0xCB, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, + 0x00, 0x00, 0xD6, 0xFF, 0x7B, 0xFF, 0xA7, 0xFF, 0x78, 0x01, 0x72, + 0x05, 0xB6, 0x0A, 0x27, 0x0F, 0xA5, 0x10, 0x6E, 0x0E, 0xA0, 0x09, + 0x7B, 0x04, 0xEC, 0x00, 0x85, 0xFF, 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, + 0xFF, 0xB6, 0xFF, 0x6C, 0xFF, 0x0E, 0x00, 0xA4, 0x02, 0x41, 0x07, + 0x80, 0x0C, 0x19, 0x10, 0x44, 0x10, 0xEB, 0x0C, 0xB9, 0x07, 0xFA, + 0x02, 0x32, 0x00, 0x6D, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, + 0x93, 0xFF, 0x7B, 0xFF, 0xB7, 0x00, 0x15, 0x04, 0x26, 0x09, 0x14, + 0x0E, 0x98, 0x10, 0x6D, 0x0F, 0x2C, 0x0B, 0xE3, 0x05, 0xBC, 0x01, + 0xBB, 0xFF, 0x75, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, + 0xFF, 0x77, 0xFF, 0xB5, 0xFF, 0xA6, 0x01, 0xC0, 0x05, 0x08, 0x0B, + 0x58, 0x0F, 0x9D, 0x10, 0x30, 0x0E, 0x4B, 0x09, 0x34, 0x04, 0xC6, + 0x00, 0x7D, 0xFF, 0x91, 0xFF, 0xE9, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, + 0x6C, 0xFF, 0x27, 0x00, 0xDF, 0x02, 0x94, 0x07, 0xCA, 0x0C, 0x37, + 0x10, 0x27, 0x10, 0xA1, 0x0C, 0x65, 0x07, 0xBE, 0x02, 0x19, 0x00, + 0x6C, 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8E, 0xFF, 0x81, + 0xFF, 0xDB, 0x00, 0x5B, 0x04, 0x7A, 0x09, 0x53, 0x0E, 0xA2, 0x10, + 0x3D, 0x0F, 0xDA, 0x0A, 0x95, 0x05, 0x8C, 0x01, 0xAD, 0xFF, 0x79, + 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xFF, 0x73, 0xFF, + 0xC4, 0xFF, 0xD7, 0x01, 0x0E, 0x06, 0x59, 0x0B, 0x87, 0x0F, 0x91, + 0x10, 0xF0, 0x0D, 0xF7, 0x08, 0xEF, 0x03, 0xA3, 0x00, 0x78, 0xFF, + 0x97, 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xAA, 0xFF, 0x6D, 0xFF, 0x41, + 0x00, 0x1C, 0x03, 0xE7, 0x07, 0x13, 0x0D, 0x52, 0x10, 0x06, 0x10, + 0x56, 0x0C, 0x13, 0x07, 0x84, 0x02, 0x02, 0x00, 0x6D, 0xFF, 0xBA, + 0xFF, 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x01, 0x01, + 0xA3, 0x04, 0xCE, 0x09, 0x8F, 0x0E, 0xA8, 0x10, 0x0A, 0x0F, 0x88, + 0x0A, 0x48, 0x05, 0x5F, 0x01, 0xA0, 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, + 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, 0x70, 0xFF, 0xD5, 0xFF, 0x0B, + 0x02, 0x5E, 0x06, 0xA9, 0x0B, 0xB2, 0x0F, 0x82, 0x10, 0xAE, 0x0D, + 0xA2, 0x08, 0xAB, 0x03, 0x82, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, + 0xFF, 0xF3, 0xFF, 0xA4, 0xFF, 0x6F, 0xFF, 0x5D, 0x00, 0x5B, 0x03, + 0x3B, 0x08, 0x5A, 0x0D, 0x6A, 0x10, 0xE2, 0x0F, 0x09, 0x0C, 0xC1, + 0x06, 0x4C, 0x02, 0xEC, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFC, 0xFF, + 0xDF, 0xFF, 0x83, 0xFF, 0x93, 0xFF, 0x2A, 0x01, 0xEC, 0x04, 0x22, + 0x0A, 0xC8, 0x0E, 0xAB, 0x10, 0xD4, 0x0E, 0x35, 0x0A, 0xFD, 0x04, + 0x33, 0x01, 0x95, 0xFF, 0x82, 0xFF, 0xDE, 0xFF, 0x00, 0x00, 0xFD, + 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE8, 0xFF, 0x40, 0x02, 0xAF, 0x06, + 0xF7, 0x0B, 0xDA, 0x0F, 0x6F, 0x10, 0x6A, 0x0D, 0x4E, 0x08, 0x6A, + 0x03, 0x64, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, + 0x9E, 0xFF, 0x72, 0xFF, 0x7B, 0x00, 0x9C, 0x03, 0x90, 0x08, 0x9F, + 0x0D, 0x7E, 0x10, 0xBB, 0x0F, 0xBA, 0x0B, 0x70, 0x06, 0x16, 0x02, + 0xD9, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, + 0xFF, 0x7E, 0xFF, 0x9D, 0xFF, 0x55, 0x01, 0x37, 0x05, 0x75, 0x0A, + 0xFF, 0x0E, 0xA9, 0x10, 0x9C, 0x0E, 0xE1, 0x09, 0xB3, 0x04, 0x0A, + 0x01, 0x8B, 0xFF, 0x87, 0xFF, 0xE2, 0xFF, 0xFB, 0xFF, 0xBB, 0xFF, + 0x6D, 0xFF, 0xFD, 0xFF, 0x77, 0x02, 0x01, 0x07, 0x45, 0x0C, 0xFF, + 0x0F, 0x58, 0x10, 0x23, 0x0D, 0xFA, 0x07, 0x2A, 0x03, 0x47, 0x00, + 0x6E, 0xFF, 0xA9, 0xFF, 0xF5, 0xFF, 0xED, 0xFF, 0x98, 0xFF, 0x77, + 0xFF, 0x9C, 0x00, 0xDF, 0x03, 0xE4, 0x08, 0xE2, 0x0D, 0x8E, 0x10, + 0x91, 0x0F, 0x6B, 0x0B, 0x20, 0x06, 0xE3, 0x01, 0xC8, 0xFF, 0x73, + 0xFF, 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x7A, 0xFF, + 0xAA, 0xFF, 0x82, 0x01, 0x83, 0x05, 0xC8, 0x0A, 0x32, 0x0F, 0xA4, + 0x10, 0x60, 0x0E, 0x8D, 0x09, 0x6B, 0x04, 0xE3, 0x00, 0x83, 0xFF, + 0x8D, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, 0xB5, 0xFF, 0x6C, 0xFF, 0x14, + 0x00, 0xB1, 0x02, 0x53, 0x07, 0x91, 0x0C, 0x20, 0x10, 0x3E, 0x10, + 0xDB, 0x0C, 0xA6, 0x07, 0xEC, 0x02, 0x2C, 0x00, 0x6C, 0xFF, 0xAF, + 0xFF, 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7C, 0xFF, 0xBE, 0x00, + 0x24, 0x04, 0x38, 0x09, 0x22, 0x0E, 0x9B, 0x10, 0x63, 0x0F, 0x1A, + 0x0B, 0xD1, 0x05, 0xB1, 0x01, 0xB8, 0xFF, 0x76, 0xFF, 0xD0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB8, 0xFF, 0xB1, + 0x01, 0xD1, 0x05, 0x1A, 0x0B, 0x63, 0x0F, 0x9B, 0x10, 0x22, 0x0E, + 0x38, 0x09, 0x24, 0x04, 0xBE, 0x00, 0x7C, 0xFF, 0x92, 0xFF, 0xEA, + 0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0x6C, 0xFF, 0x2C, 0x00, 0xEC, 0x02, + 0xA6, 0x07, 0xDB, 0x0C, 0x3E, 0x10, 0x20, 0x10, 0x91, 0x0C, 0x53, + 0x07, 0xB1, 0x02, 0x14, 0x00, 0x6C, 0xFF, 0xB5, 0xFF, 0xFA, 0xFF, + 0xE6, 0xFF, 0x8D, 0xFF, 0x83, 0xFF, 0xE3, 0x00, 0x6B, 0x04, 0x8D, + 0x09, 0x60, 0x0E, 0xA4, 0x10, 0x32, 0x0F, 0xC8, 0x0A, 0x83, 0x05, + 0x82, 0x01, 0xAA, 0xFF, 0x7A, 0xFF, 0xD5, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC8, 0xFF, 0xE3, 0x01, 0x20, 0x06, + 0x6B, 0x0B, 0x91, 0x0F, 0x8E, 0x10, 0xE2, 0x0D, 0xE4, 0x08, 0xDF, + 0x03, 0x9C, 0x00, 0x77, 0xFF, 0x98, 0xFF, 0xED, 0xFF, 0xF5, 0xFF, + 0xA9, 0xFF, 0x6E, 0xFF, 0x47, 0x00, 0x2A, 0x03, 0xFA, 0x07, 0x23, + 0x0D, 0x58, 0x10, 0xFF, 0x0F, 0x45, 0x0C, 0x01, 0x07, 0x77, 0x02, + 0xFD, 0xFF, 0x6D, 0xFF, 0xBB, 0xFF, 0xFB, 0xFF, 0xE2, 0xFF, 0x87, + 0xFF, 0x8B, 0xFF, 0x0A, 0x01, 0xB3, 0x04, 0xE1, 0x09, 0x9C, 0x0E, + 0xA9, 0x10, 0xFF, 0x0E, 0x75, 0x0A, 0x37, 0x05, 0x55, 0x01, 0x9D, + 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, + 0x70, 0xFF, 0xD9, 0xFF, 0x16, 0x02, 0x70, 0x06, 0xBA, 0x0B, 0xBB, + 0x0F, 0x7E, 0x10, 0x9F, 0x0D, 0x90, 0x08, 0x9C, 0x03, 0x7B, 0x00, + 0x72, 0xFF, 0x9E, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA3, 0xFF, 0x70, + 0xFF, 0x64, 0x00, 0x6A, 0x03, 0x4E, 0x08, 0x6A, 0x0D, 0x6F, 0x10, + 0xDA, 0x0F, 0xF7, 0x0B, 0xAF, 0x06, 0x40, 0x02, 0xE8, 0xFF, 0x6E, + 0xFF, 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDE, 0xFF, 0x82, 0xFF, + 0x95, 0xFF, 0x33, 0x01, 0xFD, 0x04, 0x35, 0x0A, 0xD4, 0x0E, 0xAB, + 0x10, 0xC8, 0x0E, 0x22, 0x0A, 0xEC, 0x04, 0x2A, 0x01, 0x93, 0xFF, + 0x83, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xC0, 0xFF, 0x6E, 0xFF, 0xEC, + 0xFF, 0x4C, 0x02, 0xC1, 0x06, 0x09, 0x0C, 0xE2, 0x0F, 0x6A, 0x10, + 0x5A, 0x0D, 0x3B, 0x08, 0x5B, 0x03, 0x5D, 0x00, 0x6F, 0xFF, 0xA4, + 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, 0xFF, 0x73, 0xFF, 0x82, 0x00, + 0xAB, 0x03, 0xA2, 0x08, 0xAE, 0x0D, 0x82, 0x10, 0xB2, 0x0F, 0xA9, + 0x0B, 0x5E, 0x06, 0x0B, 0x02, 0xD5, 0xFF, 0x70, 0xFF, 0xC7, 0xFF, + 0xFE, 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA0, 0xFF, 0x5F, + 0x01, 0x48, 0x05, 0x88, 0x0A, 0x0A, 0x0F, 0xA8, 0x10, 0x8F, 0x0E, + 0xCE, 0x09, 0xA3, 0x04, 0x01, 0x01, 0x89, 0xFF, 0x88, 0xFF, 0xE3, + 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, 0xFF, 0x02, 0x00, 0x84, 0x02, + 0x13, 0x07, 0x56, 0x0C, 0x06, 0x10, 0x52, 0x10, 0x13, 0x0D, 0xE7, + 0x07, 0x1C, 0x03, 0x41, 0x00, 0x6D, 0xFF, 0xAA, 0xFF, 0xF6, 0xFF, + 0xED, 0xFF, 0x97, 0xFF, 0x78, 0xFF, 0xA3, 0x00, 0xEF, 0x03, 0xF7, + 0x08, 0xF0, 0x0D, 0x91, 0x10, 0x87, 0x0F, 0x59, 0x0B, 0x0E, 0x06, + 0xD7, 0x01, 0xC4, 0xFF, 0x73, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0xD4, 0xFF, 0x79, 0xFF, 0xAD, 0xFF, 0x8C, 0x01, 0x95, 0x05, + 0xDA, 0x0A, 0x3D, 0x0F, 0xA2, 0x10, 0x53, 0x0E, 0x7A, 0x09, 0x5B, + 0x04, 0xDB, 0x00, 0x81, 0xFF, 0x8E, 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, + 0xB4, 0xFF, 0x6C, 0xFF, 0x19, 0x00, 0xBE, 0x02, 0x65, 0x07, 0xA1, + 0x0C, 0x27, 0x10, 0x37, 0x10, 0xCA, 0x0C, 0x94, 0x07, 0xDF, 0x02, + 0x27, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x91, + 0xFF, 0x7D, 0xFF, 0xC6, 0x00, 0x34, 0x04, 0x4B, 0x09, 0x30, 0x0E, + 0x9D, 0x10, 0x58, 0x0F, 0x08, 0x0B, 0xC0, 0x05, 0xA6, 0x01, 0xB5, + 0xFF, 0x77, 0xFF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, + 0x75, 0xFF, 0xBB, 0xFF, 0xBC, 0x01, 0xE3, 0x05, 0x2C, 0x0B, 0x6D, + 0x0F, 0x98, 0x10, 0x14, 0x0E, 0x26, 0x09, 0x15, 0x04, 0xB7, 0x00, + 0x7B, 0xFF, 0x93, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAE, 0xFF, 0x6D, + 0xFF, 0x32, 0x00, 0xFA, 0x02, 0xB9, 0x07, 0xEB, 0x0C, 0x44, 0x10, + 0x19, 0x10, 0x80, 0x0C, 0x41, 0x07, 0xA4, 0x02, 0x0E, 0x00, 0x6C, + 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, + 0xEC, 0x00, 0x7B, 0x04, 0xA0, 0x09, 0x6E, 0x0E, 0xA5, 0x10, 0x27, + 0x0F, 0xB6, 0x0A, 0x72, 0x05, 0x78, 0x01, 0xA7, 0xFF, 0x7B, 0xFF, + 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xCA, 0xFF, 0x72, 0xFF, 0xCB, + 0xFF, 0xEE, 0x01, 0x32, 0x06, 0x7C, 0x0B, 0x9A, 0x0F, 0x8B, 0x10, + 0xD3, 0x0D, 0xD1, 0x08, 0xD0, 0x03, 0x94, 0x00, 0x76, 0xFF, 0x99, + 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x4D, 0x00, + 0x38, 0x03, 0x0D, 0x08, 0x33, 0x0D, 0x5D, 0x10, 0xF7, 0x0F, 0x34, + 0x0C, 0xEE, 0x06, 0x6B, 0x02, 0xF8, 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, + 0xFC, 0xFF, 0xE1, 0xFF, 0x86, 0xFF, 0x8D, 0xFF, 0x13, 0x01, 0xC3, + 0x04, 0xF4, 0x09, 0xA8, 0x0E, 0xAA, 0x10, 0xF3, 0x0E, 0x63, 0x0A, + 0x26, 0x05, 0x4B, 0x01, 0x9B, 0xFF, 0x7F, 0xFF, 0xDB, 0xFF, 0x00, + 0x00, 0xFD, 0xFF, 0xC4, 0xFF, 0x6F, 0xFF, 0xDD, 0xFF, 0x22, 0x02, + 0x82, 0x06, 0xCC, 0x0B, 0xC4, 0x0F, 0x7A, 0x10, 0x90, 0x0D, 0x7D, + 0x08, 0x8E, 0x03, 0x74, 0x00, 0x72, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xA1, 0xFF, 0x70, 0xFF, 0x6A, 0x00, 0x78, 0x03, 0x61, + 0x08, 0x79, 0x0D, 0x73, 0x10, 0xD1, 0x0F, 0xE6, 0x0B, 0x9D, 0x06, + 0x34, 0x02, 0xE4, 0xFF, 0x6F, 0xFF, 0xC2, 0xFF, 0xFD, 0xFF, 0x00, + 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x97, 0xFF, 0x3D, 0x01, 0x0D, 0x05, + 0x47, 0x0A, 0xE1, 0x0E, 0xAA, 0x10, 0xBB, 0x0E, 0x10, 0x0A, 0xDC, + 0x04, 0x21, 0x01, 0x90, 0xFF, 0x84, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, + 0xBE, 0xFF, 0x6D, 0xFF, 0xF1, 0xFF, 0x58, 0x02, 0xD3, 0x06, 0x1A, + 0x0C, 0xEB, 0x0F, 0x65, 0x10, 0x4B, 0x0D, 0x29, 0x08, 0x4D, 0x03, + 0x57, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, + 0xFF, 0x74, 0xFF, 0x8A, 0x00, 0xBA, 0x03, 0xB5, 0x08, 0xBD, 0x0D, + 0x86, 0x10, 0xA9, 0x0F, 0x97, 0x0B, 0x4C, 0x06, 0xFF, 0x01, 0xD1, + 0xFF, 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, + 0x7C, 0xFF, 0xA3, 0xFF, 0x69, 0x01, 0x59, 0x05, 0x9A, 0x0A, 0x16, + 0x0F, 0xA7, 0x10, 0x82, 0x0E, 0xBC, 0x09, 0x93, 0x04, 0xF9, 0x00, + 0x87, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, 0xFB, 0xFF, 0xB8, 0xFF, 0x6C, + 0xFF, 0x07, 0x00, 0x91, 0x02, 0x25, 0x07, 0x67, 0x0C, 0x0E, 0x10, + 0x4D, 0x10, 0x03, 0x0D, 0xD5, 0x07, 0x0E, 0x03, 0x3B, 0x00, 0x6D, + 0xFF, 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, + 0xAB, 0x00, 0xFE, 0x03, 0x0A, 0x09, 0xFF, 0x0D, 0x94, 0x10, 0x7D, + 0x0F, 0x47, 0x0B, 0xFD, 0x05, 0xCC, 0x01, 0xC0, 0xFF, 0x74, 0xFF, + 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD3, 0xFF, 0x78, 0xFF, 0xB0, + 0xFF, 0x97, 0x01, 0xA6, 0x05, 0xEC, 0x0A, 0x48, 0x0F, 0xA0, 0x10, + 0x45, 0x0E, 0x67, 0x09, 0x4B, 0x04, 0xD3, 0x00, 0x80, 0xFF, 0x8F, + 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x1E, 0x00, + 0xCB, 0x02, 0x78, 0x07, 0xB2, 0x0C, 0x2E, 0x10, 0x31, 0x10, 0xBA, + 0x0C, 0x81, 0x07, 0xD2, 0x02, 0x21, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, + 0xF9, 0xFF, 0xE8, 0xFF, 0x90, 0xFF, 0x7F, 0xFF, 0xCF, 0x00, 0x43, + 0x04, 0x5E, 0x09, 0x3E, 0x0E, 0x9F, 0x10, 0x4E, 0x0F, 0xF6, 0x0A, + 0xAE, 0x05, 0x9C, 0x01, 0xB1, 0xFF, 0x78, 0xFF, 0xD2, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x74, 0xFF, 0xBF, 0xFF, 0xC7, 0x01, + 0xF4, 0x05, 0x3E, 0x0B, 0x78, 0x0F, 0x96, 0x10, 0x06, 0x0E, 0x13, + 0x09, 0x05, 0x04, 0xAF, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, + 0xF7, 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x38, 0x00, 0x07, 0x03, 0xCB, + 0x07, 0xFB, 0x0C, 0x4A, 0x10, 0x11, 0x10, 0x6F, 0x0C, 0x2E, 0x07, + 0x97, 0x02, 0x09, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFA, 0xFF, 0xE4, + 0xFF, 0x8A, 0xFF, 0x86, 0xFF, 0xF4, 0x00, 0x8B, 0x04, 0xB2, 0x09, + 0x7B, 0x0E, 0xA7, 0x10, 0x1C, 0x0F, 0xA3, 0x0A, 0x61, 0x05, 0x6E, + 0x01, 0xA4, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, + 0xC8, 0xFF, 0x71, 0xFF, 0xCF, 0xFF, 0xF9, 0x01, 0x43, 0x06, 0x8E, + 0x0B, 0xA4, 0x0F, 0x88, 0x10, 0xC4, 0x0D, 0xBE, 0x08, 0xC1, 0x03, + 0x8D, 0x00, 0x75, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA6, + 0xFF, 0x6E, 0xFF, 0x53, 0x00, 0x46, 0x03, 0x1F, 0x08, 0x43, 0x0D, + 0x63, 0x10, 0xEF, 0x0F, 0x23, 0x0C, 0xDC, 0x06, 0x5E, 0x02, 0xF3, + 0xFF, 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x85, 0xFF, + 0x8F, 0xFF, 0x1C, 0x01, 0xD3, 0x04, 0x06, 0x0A, 0xB5, 0x0E, 0xAA, + 0x10, 0xE7, 0x0E, 0x50, 0x0A, 0x16, 0x05, 0x42, 0x01, 0x98, 0xFF, + 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, + 0xFF, 0xE1, 0xFF, 0x2E, 0x02, 0x94, 0x06, 0xDD, 0x0B, 0xCD, 0x0F, + 0x76, 0x10, 0x81, 0x0D, 0x6A, 0x08, 0x7F, 0x03, 0x6E, 0x00, 0x71, + 0xFF, 0xA1, 0xFF, 0xF2, 0xFF, 0x00, 0x00, 0x15, 0x00, 0xD1, 0xFF, + 0x8B, 0xFE, 0xBC, 0xFD, 0xE1, 0x00, 0x84, 0x09, 0xB0, 0x13, 0x47, + 0x18, 0xB0, 0x13, 0x84, 0x09, 0xE1, 0x00, 0xBC, 0xFD, 0x8B, 0xFE, + 0xD1, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xDA, 0x00, 0x30, + 0x00, 0x5D, 0xFC, 0xB3, 0xFC, 0x35, 0x0A, 0xC2, 0x1C, 0x24, 0x20, + 0x48, 0x10, 0x5D, 0xFF, 0x74, 0xFB, 0x3A, 0xFF, 0xFB, 0x00, 0x42, + 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2C, 0x00, 0xF3, 0x00, 0xAD, 0xFF, + 0xC5, 0xFB, 0x11, 0xFE, 0xAF, 0x0D, 0xEF, 0x1E, 0x68, 0x1E, 0xBC, + 0x0C, 0xA7, 0xFD, 0xEA, 0xFB, 0xD3, 0xFF, 0xEE, 0x00, 0x24, 0x00, + 0xFA, 0xFF, 0xF7, 0xFF, 0x4C, 0x00, 0xFB, 0x00, 0x0C, 0xFF, 0x5F, + 0xFB, 0xE8, 0xFF, 0x3D, 0x11, 0x7E, 0x20, 0x13, 0x1C, 0x4C, 0x09, + 0x6A, 0xFC, 0x8C, 0xFC, 0x4E, 0x00, 0xD1, 0x00, 0x0E, 0x00, 0xFD, + 0xFF, 0xF7, 0xFF, 0x72, 0x00, 0xEC, 0x00, 0x55, 0xFE, 0x3D, 0xFB, + 0x37, 0x02, 0xBE, 0x14, 0x5D, 0x21, 0x40, 0x19, 0x18, 0x06, 0xA2, + 0xFB, 0x47, 0xFD, 0xA7, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xC0, 0x00, 0x92, 0xFD, 0x73, + 0xFB, 0xF2, 0x04, 0x0E, 0x18, 0x81, 0x21, 0x0C, 0x16, 0x37, 0x03, + 0x47, 0xFB, 0x0B, 0xFE, 0xDF, 0x00, 0x82, 0x00, 0xF9, 0xFF, 0xFE, + 0xFF, 0x08, 0x00, 0xC3, 0x00, 0x74, 0x00, 0xD2, 0xFC, 0x10, 0xFC, + 0x08, 0x08, 0x0A, 0x1B, 0xE9, 0x20, 0x9A, 0x12, 0xBE, 0x00, 0x49, + 0xFB, 0xC8, 0xFE, 0xF9, 0x00, 0x5A, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, + 0x1B, 0x00, 0xE4, 0x00, 0x06, 0x00, 0x24, 0xFC, 0x1E, 0xFD, 0x65, + 0x0B, 0x94, 0x1D, 0x9D, 0x1F, 0x0D, 0x0F, 0xB8, 0xFE, 0x96, 0xFB, + 0x72, 0xFF, 0xF9, 0x00, 0x37, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x36, + 0x00, 0xF8, 0x00, 0x78, 0xFF, 0x9B, 0xFB, 0xA6, 0xFE, 0xE9, 0x0E, + 0x8D, 0x1F, 0xAA, 0x1D, 0x87, 0x0B, 0x2B, 0xFD, 0x1E, 0xFC, 0x02, + 0x00, 0xE5, 0x00, 0x1C, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x58, 0x00, + 0xF9, 0x00, 0xCF, 0xFE, 0x4A, 0xFB, 0xA7, 0x00, 0x77, 0x12, 0xE0, + 0x20, 0x26, 0x1B, 0x28, 0x08, 0x18, 0xFC, 0xCB, 0xFC, 0x71, 0x00, + 0xC5, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x80, 0x00, 0xE1, + 0x00, 0x13, 0xFE, 0x45, 0xFB, 0x1D, 0x03, 0xEB, 0x15, 0x7F, 0x21, + 0x2D, 0x18, 0x0E, 0x05, 0x77, 0xFB, 0x8B, 0xFD, 0xBE, 0x00, 0x9D, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA9, 0x00, + 0xAA, 0x00, 0x4F, 0xFD, 0x9D, 0xFB, 0xFA, 0x05, 0x22, 0x19, 0x62, + 0x21, 0xE0, 0x14, 0x50, 0x02, 0x3E, 0xFB, 0x4E, 0xFE, 0xEB, 0x00, + 0x73, 0x00, 0xF7, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xD0, 0x00, 0x52, + 0x00, 0x93, 0xFC, 0x60, 0xFC, 0x2C, 0x09, 0xFA, 0x1B, 0x8A, 0x20, + 0x60, 0x11, 0xFD, 0xFF, 0x5C, 0xFB, 0x06, 0xFF, 0xFB, 0x00, 0x4D, + 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x23, 0x00, 0xED, 0x00, 0xD9, 0xFF, + 0xEF, 0xFB, 0x98, 0xFD, 0x99, 0x0C, 0x54, 0x1E, 0x02, 0x1F, 0xD2, + 0x0D, 0x20, 0xFE, 0xC0, 0xFB, 0xA7, 0xFF, 0xF4, 0x00, 0x2D, 0x00, + 0xF9, 0xFF, 0xF8, 0xFF, 0x41, 0x00, 0xFB, 0x00, 0x41, 0xFF, 0x78, + 0xFB, 0x4A, 0xFF, 0x25, 0x10, 0x16, 0x20, 0xDA, 0x1C, 0x56, 0x0A, + 0xBE, 0xFC, 0x56, 0xFC, 0x2C, 0x00, 0xDB, 0x00, 0x14, 0x00, 0xFD, + 0xFF, 0xF7, 0xFF, 0x66, 0x00, 0xF4, 0x00, 0x8F, 0xFE, 0x3F, 0xFB, + 0x75, 0x01, 0xAE, 0x13, 0x2C, 0x21, 0x2A, 0x1A, 0x0D, 0x07, 0xD4, + 0xFB, 0x0C, 0xFD, 0x8F, 0x00, 0xB7, 0x00, 0x03, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0xFA, 0xFF, 0x8E, 0x00, 0xD1, 0x00, 0xCF, 0xFD, 0x58, + 0xFB, 0x10, 0x04, 0x10, 0x17, 0x8A, 0x21, 0x10, 0x17, 0x10, 0x04, + 0x58, 0xFB, 0xCF, 0xFD, 0xD1, 0x00, 0x8E, 0x00, 0xFA, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0xB7, 0x00, 0x8F, 0x00, 0x0C, 0xFD, 0xD4, 0xFB, + 0x0D, 0x07, 0x2A, 0x1A, 0x2C, 0x21, 0xAE, 0x13, 0x75, 0x01, 0x3F, + 0xFB, 0x8F, 0xFE, 0xF4, 0x00, 0x66, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, + 0x14, 0x00, 0xDB, 0x00, 0x2C, 0x00, 0x56, 0xFC, 0xBE, 0xFC, 0x56, + 0x0A, 0xDA, 0x1C, 0x16, 0x20, 0x25, 0x10, 0x4A, 0xFF, 0x78, 0xFB, + 0x41, 0xFF, 0xFB, 0x00, 0x41, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2D, + 0x00, 0xF4, 0x00, 0xA7, 0xFF, 0xC0, 0xFB, 0x20, 0xFE, 0xD2, 0x0D, + 0x02, 0x1F, 0x54, 0x1E, 0x99, 0x0C, 0x98, 0xFD, 0xEF, 0xFB, 0xD9, + 0xFF, 0xED, 0x00, 0x23, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4D, 0x00, + 0xFB, 0x00, 0x06, 0xFF, 0x5C, 0xFB, 0xFD, 0xFF, 0x60, 0x11, 0x8A, + 0x20, 0xFA, 0x1B, 0x2C, 0x09, 0x60, 0xFC, 0x93, 0xFC, 0x52, 0x00, + 0xD0, 0x00, 0x0D, 0x00, 0xFE, 0xFF, 0xF7, 0xFF, 0x73, 0x00, 0xEB, + 0x00, 0x4E, 0xFE, 0x3E, 0xFB, 0x50, 0x02, 0xE0, 0x14, 0x62, 0x21, + 0x22, 0x19, 0xFA, 0x05, 0x9D, 0xFB, 0x4F, 0xFD, 0xAA, 0x00, 0xA9, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x9D, 0x00, + 0xBE, 0x00, 0x8B, 0xFD, 0x77, 0xFB, 0x0E, 0x05, 0x2D, 0x18, 0x7F, + 0x21, 0xEB, 0x15, 0x1D, 0x03, 0x45, 0xFB, 0x13, 0xFE, 0xE1, 0x00, + 0x80, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x08, 0x00, 0xC5, 0x00, 0x71, + 0x00, 0xCB, 0xFC, 0x18, 0xFC, 0x28, 0x08, 0x26, 0x1B, 0xE0, 0x20, + 0x77, 0x12, 0xA7, 0x00, 0x4A, 0xFB, 0xCF, 0xFE, 0xF9, 0x00, 0x58, + 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1C, 0x00, 0xE5, 0x00, 0x02, 0x00, + 0x1E, 0xFC, 0x2B, 0xFD, 0x87, 0x0B, 0xAA, 0x1D, 0x8D, 0x1F, 0xE9, + 0x0E, 0xA6, 0xFE, 0x9B, 0xFB, 0x78, 0xFF, 0xF8, 0x00, 0x36, 0x00, + 0xF9, 0xFF, 0xF8, 0xFF, 0x37, 0x00, 0xF9, 0x00, 0x72, 0xFF, 0x96, + 0xFB, 0xB8, 0xFE, 0x0D, 0x0F, 0x9D, 0x1F, 0x94, 0x1D, 0x65, 0x0B, + 0x1E, 0xFD, 0x24, 0xFC, 0x06, 0x00, 0xE4, 0x00, 0x1B, 0x00, 0xFC, + 0xFF, 0xF7, 0xFF, 0x5A, 0x00, 0xF9, 0x00, 0xC8, 0xFE, 0x49, 0xFB, + 0xBE, 0x00, 0x9A, 0x12, 0xE9, 0x20, 0x0A, 0x1B, 0x08, 0x08, 0x10, + 0xFC, 0xD2, 0xFC, 0x74, 0x00, 0xC3, 0x00, 0x08, 0x00, 0xFE, 0xFF, + 0xF9, 0xFF, 0x82, 0x00, 0xDF, 0x00, 0x0B, 0xFE, 0x47, 0xFB, 0x37, + 0x03, 0x0C, 0x16, 0x81, 0x21, 0x0E, 0x18, 0xF2, 0x04, 0x73, 0xFB, + 0x92, 0xFD, 0xC0, 0x00, 0x9B, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xAB, 0x00, 0xA7, 0x00, 0x47, 0xFD, 0xA2, 0xFB, + 0x18, 0x06, 0x40, 0x19, 0x5D, 0x21, 0xBE, 0x14, 0x37, 0x02, 0x3D, + 0xFB, 0x55, 0xFE, 0xEC, 0x00, 0x72, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, + 0x0E, 0x00, 0xD1, 0x00, 0x4E, 0x00, 0x8C, 0xFC, 0x6A, 0xFC, 0x4C, + 0x09, 0x13, 0x1C, 0x7E, 0x20, 0x3D, 0x11, 0xE8, 0xFF, 0x5F, 0xFB, + 0x0C, 0xFF, 0xFB, 0x00, 0x4C, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x24, + 0x00, 0xEE, 0x00, 0xD3, 0xFF, 0xEA, 0xFB, 0xA7, 0xFD, 0xBC, 0x0C, + 0x68, 0x1E, 0xEF, 0x1E, 0xAF, 0x0D, 0x11, 0xFE, 0xC5, 0xFB, 0xAD, + 0xFF, 0xF3, 0x00, 0x2C, 0x00, 0xFA, 0xFF, 0xF8, 0xFF, 0x42, 0x00, + 0xFB, 0x00, 0x3A, 0xFF, 0x74, 0xFB, 0x5D, 0xFF, 0x48, 0x10, 0x24, + 0x20, 0xC2, 0x1C, 0x35, 0x0A, 0xB3, 0xFC, 0x5D, 0xFC, 0x30, 0x00, + 0xDA, 0x00, 0x13, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x67, 0x00, 0xF3, + 0x00, 0x88, 0xFE, 0x3E, 0xFB, 0x8C, 0x01, 0xD0, 0x13, 0x33, 0x21, + 0x0D, 0x1A, 0xEE, 0x06, 0xCD, 0xFB, 0x13, 0xFD, 0x92, 0x00, 0xB6, + 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFA, 0xFF, 0x90, 0x00, + 0xCF, 0x00, 0xC7, 0xFD, 0x5B, 0xFB, 0x2B, 0x04, 0x31, 0x17, 0x8A, + 0x21, 0xF0, 0x16, 0xF4, 0x03, 0x56, 0xFB, 0xD6, 0xFD, 0xD3, 0x00, + 0x8D, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0xB9, 0x00, 0x8C, + 0x00, 0x05, 0xFD, 0xDB, 0xFB, 0x2C, 0x07, 0x47, 0x1A, 0x25, 0x21, + 0x8B, 0x13, 0x5D, 0x01, 0x40, 0xFB, 0x97, 0xFE, 0xF5, 0x00, 0x64, + 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x15, 0x00, 0xDC, 0x00, 0x27, 0x00, + 0x50, 0xFC, 0xCA, 0xFC, 0x78, 0x0A, 0xF2, 0x1C, 0x07, 0x20, 0x02, + 0x10, 0x37, 0xFF, 0x7B, 0xFB, 0x47, 0xFF, 0xFB, 0x00, 0x40, 0x00, + 0xF8, 0xFF, 0xF9, 0xFF, 0x2E, 0x00, 0xF5, 0x00, 0xA2, 0xFF, 0xBB, + 0xFB, 0x31, 0xFE, 0xF5, 0x0D, 0x14, 0x1F, 0x3F, 0x1E, 0x77, 0x0C, + 0x8A, 0xFD, 0xF5, 0xFB, 0xDE, 0xFF, 0xEC, 0x00, 0x22, 0x00, 0xFB, + 0xFF, 0xF7, 0xFF, 0x4E, 0x00, 0xFB, 0x00, 0xFF, 0xFE, 0x59, 0xFB, + 0x11, 0x00, 0x83, 0x11, 0x96, 0x20, 0xE0, 0x1B, 0x0B, 0x09, 0x56, + 0xFC, 0x99, 0xFC, 0x56, 0x00, 0xCE, 0x00, 0x0D, 0x00, 0xFE, 0xFF, + 0xF8, 0xFF, 0x75, 0x00, 0xEA, 0x00, 0x47, 0xFE, 0x3E, 0xFB, 0x69, + 0x02, 0x02, 0x15, 0x66, 0x21, 0x04, 0x19, 0xDC, 0x05, 0x98, 0xFB, + 0x56, 0xFD, 0xAD, 0x00, 0xA8, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x9E, 0x00, 0xBC, 0x00, 0x83, 0xFD, 0x7B, 0xFB, + 0x2B, 0x05, 0x4C, 0x18, 0x7C, 0x21, 0xCA, 0x15, 0x03, 0x03, 0x44, + 0xFB, 0x1A, 0xFE, 0xE2, 0x00, 0x7E, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, + 0x09, 0x00, 0xC6, 0x00, 0x6D, 0x00, 0xC3, 0xFC, 0x20, 0xFC, 0x49, + 0x08, 0x41, 0x1B, 0xD6, 0x20, 0x54, 0x12, 0x92, 0x00, 0x4C, 0xFB, + 0xD6, 0xFE, 0xFA, 0x00, 0x57, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, + 0x00, 0xE6, 0x00, 0xFD, 0xFF, 0x18, 0xFC, 0x38, 0xFD, 0xA9, 0x0B, + 0xC0, 0x1D, 0x7C, 0x1F, 0xC6, 0x0E, 0x95, 0xFE, 0x9F, 0xFB, 0x7E, + 0xFF, 0xF8, 0x00, 0x35, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x38, 0x00, + 0xF9, 0x00, 0x6C, 0xFF, 0x92, 0xFB, 0xC9, 0xFE, 0x2F, 0x0F, 0xAD, + 0x1F, 0x7D, 0x1D, 0x42, 0x0B, 0x12, 0xFD, 0x2A, 0xFC, 0x0B, 0x00, + 0xE3, 0x00, 0x1A, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5B, 0x00, 0xF8, + 0x00, 0xC1, 0xFE, 0x47, 0xFB, 0xD4, 0x00, 0xBC, 0x12, 0xF3, 0x20, + 0xEF, 0x1A, 0xE9, 0x07, 0x08, 0xFC, 0xD9, 0xFC, 0x78, 0x00, 0xC2, + 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0x00, 0xDD, 0x00, + 0x04, 0xFE, 0x49, 0xFB, 0x52, 0x03, 0x2D, 0x16, 0x83, 0x21, 0xEF, + 0x17, 0xD5, 0x04, 0x6F, 0xFB, 0x9A, 0xFD, 0xC3, 0x00, 0x9A, 0x00, + 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xAD, 0x00, 0xA4, + 0x00, 0x40, 0xFD, 0xA8, 0xFB, 0x36, 0x06, 0x5E, 0x19, 0x58, 0x21, + 0x9C, 0x14, 0x1E, 0x02, 0x3D, 0xFB, 0x5D, 0xFE, 0xED, 0x00, 0x70, + 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, 0x00, 0xD2, 0x00, 0x4A, 0x00, + 0x85, 0xFC, 0x74, 0xFC, 0x6D, 0x09, 0x2D, 0x1C, 0x72, 0x20, 0x1A, + 0x11, 0xD4, 0xFF, 0x61, 0xFB, 0x13, 0xFF, 0xFC, 0x00, 0x4A, 0x00, + 0xF7, 0xFF, 0xFA, 0xFF, 0x25, 0x00, 0xEF, 0x00, 0xCE, 0xFF, 0xE4, + 0xFB, 0xB5, 0xFD, 0xDE, 0x0C, 0x7C, 0x1E, 0xDD, 0x1E, 0x8C, 0x0D, + 0x01, 0xFE, 0xCA, 0xFB, 0xB3, 0xFF, 0xF3, 0x00, 0x2B, 0x00, 0xFA, + 0xFF, 0xF8, 0xFF, 0x44, 0x00, 0xFB, 0x00, 0x34, 0xFF, 0x71, 0xFB, + 0x71, 0xFF, 0x6B, 0x10, 0x32, 0x20, 0xA9, 0x1C, 0x13, 0x0A, 0xA8, + 0xFC, 0x63, 0xFC, 0x35, 0x00, 0xD9, 0x00, 0x12, 0x00, 0xFD, 0xFF, + 0xF7, 0xFF, 0x69, 0x00, 0xF2, 0x00, 0x81, 0xFE, 0x3E, 0xFB, 0xA4, + 0x01, 0xF2, 0x13, 0x3A, 0x21, 0xF0, 0x19, 0xCF, 0x06, 0xC7, 0xFB, + 0x1B, 0xFD, 0x96, 0x00, 0xB4, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFB, 0xFF, 0x92, 0x00, 0xCD, 0x00, 0xC0, 0xFD, 0x5E, 0xFB, + 0x47, 0x04, 0x51, 0x17, 0x8A, 0x21, 0xD0, 0x16, 0xD9, 0x03, 0x53, + 0xFB, 0xDE, 0xFD, 0xD5, 0x00, 0x8B, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, + 0x04, 0x00, 0xBA, 0x00, 0x89, 0x00, 0xFD, 0xFC, 0xE2, 0xFB, 0x4B, + 0x07, 0x63, 0x1A, 0x1D, 0x21, 0x69, 0x13, 0x46, 0x01, 0x41, 0xFB, + 0x9E, 0xFE, 0xF5, 0x00, 0x63, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x16, + 0x00, 0xDD, 0x00, 0x23, 0x00, 0x49, 0xFC, 0xD5, 0xFC, 0x99, 0x0A, + 0x09, 0x1D, 0xF9, 0x1F, 0xDF, 0x0F, 0x24, 0xFF, 0x7F, 0xFB, 0x4D, + 0xFF, 0xFB, 0x00, 0x3F, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2F, 0x00, + 0xF5, 0x00, 0x9C, 0xFF, 0xB6, 0xFB, 0x41, 0xFE, 0x17, 0x0E, 0x26, + 0x1F, 0x2B, 0x1E, 0x54, 0x0C, 0x7C, 0xFD, 0xFA, 0xFB, 0xE3, 0xFF, + 0xEB, 0x00, 0x21, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x50, 0x00, 0xFB, + 0x00, 0xF8, 0xFE, 0x57, 0xFB, 0x26, 0x00, 0xA6, 0x11, 0xA1, 0x20, + 0xC6, 0x1B, 0xEA, 0x08, 0x4D, 0xFC, 0xA0, 0xFC, 0x5A, 0x00, 0xCD, + 0x00, 0x0C, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0xE9, 0x00, + 0x3F, 0xFE, 0x3F, 0xFB, 0x82, 0x02, 0x23, 0x15, 0x6B, 0x21, 0xE5, + 0x18, 0xBE, 0x05, 0x93, 0xFB, 0x5E, 0xFD, 0xAF, 0x00, 0xA6, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xA0, 0x00, 0xB9, + 0x00, 0x7C, 0xFD, 0x80, 0xFB, 0x48, 0x05, 0x6B, 0x18, 0x79, 0x21, + 0xA9, 0x15, 0xE9, 0x02, 0x43, 0xFB, 0x21, 0xFE, 0xE3, 0x00, 0x7D, + 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x09, 0x00, 0xC7, 0x00, 0x69, 0x00, + 0xBC, 0xFC, 0x29, 0xFC, 0x69, 0x08, 0x5C, 0x1B, 0xCC, 0x20, 0x32, + 0x12, 0x7C, 0x00, 0x4E, 0xFB, 0xDD, 0xFE, 0xFA, 0x00, 0x56, 0x00, + 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, 0x00, 0xE7, 0x00, 0xF8, 0xFF, 0x12, + 0xFC, 0x45, 0xFD, 0xCB, 0x0B, 0xD6, 0x1D, 0x6C, 0x1F, 0xA3, 0x0E, + 0x84, 0xFE, 0xA4, 0xFB, 0x84, 0xFF, 0xF7, 0x00, 0x34, 0x00, 0xF9, + 0xFF, 0xF8, 0xFF, 0x3A, 0x00, 0xFA, 0x00, 0x66, 0xFF, 0x8E, 0xFB, + 0xDB, 0xFE, 0x53, 0x0F, 0xBD, 0x1F, 0x66, 0x1D, 0x21, 0x0B, 0x05, + 0xFD, 0x30, 0xFC, 0x10, 0x00, 0xE2, 0x00, 0x19, 0x00, 0xFC, 0xFF, + 0xF7, 0xFF, 0x5D, 0x00, 0xF8, 0x00, 0xBA, 0xFE, 0x46, 0xFB, 0xEA, + 0x00, 0xDF, 0x12, 0xFC, 0x20, 0xD3, 0x1A, 0xC9, 0x07, 0x00, 0xFC, + 0xE0, 0xFC, 0x7B, 0x00, 0xC0, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, + 0xFF, 0x85, 0x00, 0xDC, 0x00, 0xFC, 0xFD, 0x4A, 0xFB, 0x6C, 0x03, + 0x4E, 0x16, 0x85, 0x21, 0xCF, 0x17, 0xB8, 0x04, 0x6C, 0xFB, 0xA2, + 0xFD, 0xC5, 0x00, 0x98, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, + 0x01, 0x00, 0xAE, 0x00, 0xA1, 0x00, 0x38, 0xFD, 0xAE, 0xFB, 0x54, + 0x06, 0x7C, 0x19, 0x53, 0x21, 0x7B, 0x14, 0x05, 0x02, 0x3D, 0xFB, + 0x64, 0xFE, 0xEE, 0x00, 0x6F, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, + 0x00, 0xD4, 0x00, 0x46, 0x00, 0x7E, 0xFC, 0x7E, 0xFC, 0x8E, 0x09, + 0x46, 0x1C, 0x66, 0x20, 0xF7, 0x10, 0xC0, 0xFF, 0x64, 0xFB, 0x1A, + 0xFF, 0xFC, 0x00, 0x49, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x26, 0x00, + 0xF0, 0x00, 0xC9, 0xFF, 0xDF, 0xFB, 0xC4, 0xFD, 0x01, 0x0D, 0x90, + 0x1E, 0xCA, 0x1E, 0x69, 0x0D, 0xF1, 0xFD, 0xCF, 0xFB, 0xB8, 0xFF, + 0xF2, 0x00, 0x29, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x45, 0x00, 0xFC, + 0x00, 0x2D, 0xFF, 0x6D, 0xFB, 0x84, 0xFF, 0x8E, 0x10, 0x3F, 0x20, + 0x91, 0x1C, 0xF2, 0x09, 0x9D, 0xFC, 0x6A, 0xFC, 0x39, 0x00, 0xD7, + 0x00, 0x12, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6A, 0x00, 0xF1, 0x00, + 0x7A, 0xFE, 0x3D, 0xFB, 0xBC, 0x01, 0x14, 0x14, 0x41, 0x21, 0xD4, + 0x19, 0xB0, 0x06, 0xC0, 0xFB, 0x22, 0xFD, 0x99, 0x00, 0xB3, 0x00, + 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x93, 0x00, 0xCB, + 0x00, 0xB8, 0xFD, 0x61, 0xFB, 0x63, 0x04, 0x71, 0x17, 0x89, 0x21, + 0xB0, 0x16, 0xBD, 0x03, 0x51, 0xFB, 0xE6, 0xFD, 0xD7, 0x00, 0x8A, + 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0xBC, 0x00, 0x86, 0x00, + 0xF6, 0xFC, 0xE9, 0xFB, 0x6A, 0x07, 0x80, 0x1A, 0x15, 0x21, 0x47, + 0x13, 0x2F, 0x01, 0x42, 0xFB, 0xA5, 0xFE, 0xF6, 0x00, 0x61, 0x00, + 0xF7, 0xFF, 0xFC, 0xFF, 0x16, 0x00, 0xDF, 0x00, 0x1E, 0x00, 0x43, + 0xFC, 0xE1, 0xFC, 0xBB, 0x0A, 0x21, 0x1D, 0xEA, 0x1F, 0xBC, 0x0F, + 0x12, 0xFF, 0x82, 0xFB, 0x54, 0xFF, 0xFA, 0x00, 0x3D, 0x00, 0xF8, + 0xFF, 0xF9, 0xFF, 0x30, 0x00, 0xF6, 0x00, 0x96, 0xFF, 0xB1, 0xFB, + 0x51, 0xFE, 0x3A, 0x0E, 0x38, 0x1F, 0x16, 0x1E, 0x32, 0x0C, 0x6E, + 0xFD, 0x00, 0xFC, 0xE8, 0xFF, 0xEA, 0x00, 0x20, 0x00, 0xFB, 0xFF, + 0xF7, 0xFF, 0x51, 0x00, 0xFB, 0x00, 0xF1, 0xFE, 0x54, 0xFB, 0x3B, + 0x00, 0xC9, 0x11, 0xAD, 0x20, 0xAC, 0x1B, 0xCA, 0x08, 0x44, 0xFC, + 0xA7, 0xFC, 0x5E, 0x00, 0xCC, 0x00, 0x0B, 0x00, 0xFE, 0xFF, 0xF8, + 0xFF, 0x78, 0x00, 0xE7, 0x00, 0x38, 0xFE, 0x40, 0xFB, 0x9B, 0x02, + 0x45, 0x15, 0x6F, 0x21, 0xC7, 0x18, 0xA1, 0x05, 0x8E, 0xFB, 0x65, + 0xFD, 0xB2, 0x00, 0xA5, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xA2, 0x00, 0xB7, 0x00, 0x74, 0xFD, 0x84, 0xFB, 0x66, + 0x05, 0x8A, 0x18, 0x76, 0x21, 0x87, 0x15, 0xCF, 0x02, 0x41, 0xFB, + 0x29, 0xFE, 0xE5, 0x00, 0x7B, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0A, + 0x00, 0xC9, 0x00, 0x66, 0x00, 0xB5, 0xFC, 0x32, 0xFC, 0x89, 0x08, + 0x77, 0x1B, 0xC2, 0x20, 0x0F, 0x12, 0x66, 0x00, 0x50, 0xFB, 0xE4, + 0xFE, 0xFA, 0x00, 0x54, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1E, 0x00, + 0xE8, 0x00, 0xF3, 0xFF, 0x0C, 0xFC, 0x53, 0xFD, 0xED, 0x0B, 0xEB, + 0x1D, 0x5A, 0x1F, 0x80, 0x0E, 0x73, 0xFE, 0xA8, 0xFB, 0x8A, 0xFF, + 0xF7, 0x00, 0x32, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3B, 0x00, 0xFA, + 0x00, 0x60, 0xFF, 0x8A, 0xFB, 0xED, 0xFE, 0x76, 0x0F, 0xCC, 0x1F, + 0x4F, 0x1D, 0xFF, 0x0A, 0xF9, 0xFC, 0x36, 0xFC, 0x15, 0x00, 0xE1, + 0x00, 0x18, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5E, 0x00, 0xF7, 0x00, + 0xB3, 0xFE, 0x44, 0xFB, 0x01, 0x01, 0x02, 0x13, 0x04, 0x21, 0xB8, + 0x1A, 0xA9, 0x07, 0xF8, 0xFB, 0xE7, 0xFC, 0x7F, 0x00, 0xBF, 0x00, + 0x06, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x86, 0x00, 0xDA, 0x00, 0xF5, + 0xFD, 0x4C, 0xFB, 0x87, 0x03, 0x6E, 0x16, 0x86, 0x21, 0xB0, 0x17, + 0x9C, 0x04, 0x68, 0xFB, 0xA9, 0xFD, 0xC7, 0x00, 0x96, 0x00, 0xFB, + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xB0, 0x00, 0x9F, 0x00, + 0x31, 0xFD, 0xB4, 0xFB, 0x73, 0x06, 0x99, 0x19, 0x4D, 0x21, 0x59, + 0x14, 0xED, 0x01, 0x3D, 0xFB, 0x6B, 0xFE, 0xEF, 0x00, 0x6D, 0x00, + 0xF7, 0xFF, 0xFD, 0xFF, 0x10, 0x00, 0xD5, 0x00, 0x42, 0x00, 0x77, + 0xFC, 0x88, 0xFC, 0xAF, 0x09, 0x5F, 0x1C, 0x59, 0x20, 0xD4, 0x10, + 0xAC, 0xFF, 0x67, 0xFB, 0x20, 0xFF, 0xFC, 0x00, 0x48, 0x00, 0xF7, + 0xFF, 0xFA, 0xFF, 0x27, 0x00, 0xF0, 0x00, 0xC3, 0xFF, 0xD9, 0xFB, + 0xD3, 0xFD, 0x24, 0x0D, 0xA3, 0x1E, 0xB7, 0x1E, 0x46, 0x0D, 0xE2, + 0xFD, 0xD4, 0xFB, 0xBE, 0xFF, 0xF1, 0x00, 0x28, 0x00, 0xFA, 0xFF, + 0xF7, 0xFF, 0x46, 0x00, 0xFC, 0x00, 0x27, 0xFF, 0x6A, 0xFB, 0x98, + 0xFF, 0xB1, 0x10, 0x4C, 0x20, 0x78, 0x1C, 0xD1, 0x09, 0x93, 0xFC, + 0x71, 0xFC, 0x3D, 0x00, 0xD6, 0x00, 0x11, 0x00, 0xFD, 0xFF, 0xF7, + 0xFF, 0x6C, 0x00, 0xF0, 0x00, 0x72, 0xFE, 0x3D, 0xFB, 0xD4, 0x01, + 0x36, 0x14, 0x47, 0x21, 0xB6, 0x19, 0x91, 0x06, 0xBA, 0xFB, 0x29, + 0xFD, 0x9C, 0x00, 0xB1, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0xFB, 0xFF, 0x95, 0x00, 0xC9, 0x00, 0xB1, 0xFD, 0x65, 0xFB, 0x80, + 0x04, 0x90, 0x17, 0x88, 0x21, 0x8F, 0x16, 0xA2, 0x03, 0x4E, 0xFB, + 0xED, 0xFD, 0xD9, 0x00, 0x88, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x05, + 0x00, 0xBD, 0x00, 0x82, 0x00, 0xEF, 0xFC, 0xF0, 0xFB, 0x8A, 0x07, + 0x9C, 0x1A, 0x0D, 0x21, 0x24, 0x13, 0x18, 0x01, 0x43, 0xFB, 0xAC, + 0xFE, 0xF7, 0x00, 0x60, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x17, 0x00, + 0xE0, 0x00, 0x1A, 0x00, 0x3D, 0xFC, 0xED, 0xFC, 0xDD, 0x0A, 0x38, + 0x1D, 0xDB, 0x1F, 0x99, 0x0F, 0xFF, 0xFE, 0x86, 0xFB, 0x5A, 0xFF, + 0xFA, 0x00, 0x3C, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x31, 0x00, 0xF6, + 0x00, 0x90, 0xFF, 0xAD, 0xFB, 0x62, 0xFE, 0x5D, 0x0E, 0x49, 0x1F, + 0x01, 0x1E, 0x10, 0x0C, 0x60, 0xFD, 0x06, 0xFC, 0xEE, 0xFF, 0xE9, + 0x00, 0x1F, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x53, 0x00, 0xFB, 0x00, + 0xEB, 0xFE, 0x52, 0xFB, 0x51, 0x00, 0xEC, 0x11, 0xB7, 0x20, 0x91, + 0x1B, 0xA9, 0x08, 0x3B, 0xFC, 0xAE, 0xFC, 0x62, 0x00, 0xCA, 0x00, + 0x0B, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7A, 0x00, 0xE6, 0x00, 0x30, + 0xFE, 0x40, 0xFB, 0xB5, 0x02, 0x66, 0x15, 0x73, 0x21, 0xA9, 0x18, + 0x83, 0x05, 0x89, 0xFB, 0x6D, 0xFD, 0xB4, 0x00, 0xA3, 0x00, 0xFE, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xA3, 0x00, 0xB4, 0x00, + 0x6D, 0xFD, 0x89, 0xFB, 0x83, 0x05, 0xA9, 0x18, 0x73, 0x21, 0x66, + 0x15, 0xB5, 0x02, 0x40, 0xFB, 0x30, 0xFE, 0xE6, 0x00, 0x7A, 0x00, + 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, 0xCA, 0x00, 0x62, 0x00, 0xAE, + 0xFC, 0x3B, 0xFC, 0xA9, 0x08, 0x91, 0x1B, 0xB7, 0x20, 0xEC, 0x11, + 0x51, 0x00, 0x52, 0xFB, 0xEB, 0xFE, 0xFB, 0x00, 0x53, 0x00, 0xF7, + 0xFF, 0xFB, 0xFF, 0x1F, 0x00, 0xE9, 0x00, 0xEE, 0xFF, 0x06, 0xFC, + 0x60, 0xFD, 0x10, 0x0C, 0x01, 0x1E, 0x49, 0x1F, 0x5D, 0x0E, 0x62, + 0xFE, 0xAD, 0xFB, 0x90, 0xFF, 0xF6, 0x00, 0x31, 0x00, 0xF9, 0xFF, + 0xF8, 0xFF, 0x3C, 0x00, 0xFA, 0x00, 0x5A, 0xFF, 0x86, 0xFB, 0xFF, + 0xFE, 0x99, 0x0F, 0xDB, 0x1F, 0x38, 0x1D, 0xDD, 0x0A, 0xED, 0xFC, + 0x3D, 0xFC, 0x1A, 0x00, 0xE0, 0x00, 0x17, 0x00, 0xFC, 0xFF, 0xF7, + 0xFF, 0x60, 0x00, 0xF7, 0x00, 0xAC, 0xFE, 0x43, 0xFB, 0x18, 0x01, + 0x24, 0x13, 0x0D, 0x21, 0x9C, 0x1A, 0x8A, 0x07, 0xF0, 0xFB, 0xEF, + 0xFC, 0x82, 0x00, 0xBD, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, + 0x88, 0x00, 0xD9, 0x00, 0xED, 0xFD, 0x4E, 0xFB, 0xA2, 0x03, 0x8F, + 0x16, 0x88, 0x21, 0x90, 0x17, 0x80, 0x04, 0x65, 0xFB, 0xB1, 0xFD, + 0xC9, 0x00, 0x95, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, + 0x00, 0xB1, 0x00, 0x9C, 0x00, 0x29, 0xFD, 0xBA, 0xFB, 0x91, 0x06, + 0xB6, 0x19, 0x47, 0x21, 0x36, 0x14, 0xD4, 0x01, 0x3D, 0xFB, 0x72, + 0xFE, 0xF0, 0x00, 0x6C, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x11, 0x00, + 0xD6, 0x00, 0x3D, 0x00, 0x71, 0xFC, 0x93, 0xFC, 0xD1, 0x09, 0x78, + 0x1C, 0x4C, 0x20, 0xB1, 0x10, 0x98, 0xFF, 0x6A, 0xFB, 0x27, 0xFF, + 0xFC, 0x00, 0x46, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x28, 0x00, 0xF1, + 0x00, 0xBE, 0xFF, 0xD4, 0xFB, 0xE2, 0xFD, 0x46, 0x0D, 0xB7, 0x1E, + 0xA3, 0x1E, 0x24, 0x0D, 0xD3, 0xFD, 0xD9, 0xFB, 0xC3, 0xFF, 0xF0, + 0x00, 0x27, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x48, 0x00, 0xFC, 0x00, + 0x20, 0xFF, 0x67, 0xFB, 0xAC, 0xFF, 0xD4, 0x10, 0x59, 0x20, 0x5F, + 0x1C, 0xAF, 0x09, 0x88, 0xFC, 0x77, 0xFC, 0x42, 0x00, 0xD5, 0x00, + 0x10, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6D, 0x00, 0xEF, 0x00, 0x6B, + 0xFE, 0x3D, 0xFB, 0xED, 0x01, 0x59, 0x14, 0x4D, 0x21, 0x99, 0x19, + 0x73, 0x06, 0xB4, 0xFB, 0x31, 0xFD, 0x9F, 0x00, 0xB0, 0x00, 0x01, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x96, 0x00, 0xC7, 0x00, + 0xA9, 0xFD, 0x68, 0xFB, 0x9C, 0x04, 0xB0, 0x17, 0x86, 0x21, 0x6E, + 0x16, 0x87, 0x03, 0x4C, 0xFB, 0xF5, 0xFD, 0xDA, 0x00, 0x86, 0x00, + 0xF9, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0xBF, 0x00, 0x7F, 0x00, 0xE7, + 0xFC, 0xF8, 0xFB, 0xA9, 0x07, 0xB8, 0x1A, 0x04, 0x21, 0x02, 0x13, + 0x01, 0x01, 0x44, 0xFB, 0xB3, 0xFE, 0xF7, 0x00, 0x5E, 0x00, 0xF7, + 0xFF, 0xFC, 0xFF, 0x18, 0x00, 0xE1, 0x00, 0x15, 0x00, 0x36, 0xFC, + 0xF9, 0xFC, 0xFF, 0x0A, 0x4F, 0x1D, 0xCC, 0x1F, 0x76, 0x0F, 0xED, + 0xFE, 0x8A, 0xFB, 0x60, 0xFF, 0xFA, 0x00, 0x3B, 0x00, 0xF8, 0xFF, + 0xF9, 0xFF, 0x32, 0x00, 0xF7, 0x00, 0x8A, 0xFF, 0xA8, 0xFB, 0x73, + 0xFE, 0x80, 0x0E, 0x5A, 0x1F, 0xEB, 0x1D, 0xED, 0x0B, 0x53, 0xFD, + 0x0C, 0xFC, 0xF3, 0xFF, 0xE8, 0x00, 0x1E, 0x00, 0xFB, 0xFF, 0xF7, + 0xFF, 0x54, 0x00, 0xFA, 0x00, 0xE4, 0xFE, 0x50, 0xFB, 0x66, 0x00, + 0x0F, 0x12, 0xC2, 0x20, 0x77, 0x1B, 0x89, 0x08, 0x32, 0xFC, 0xB5, + 0xFC, 0x66, 0x00, 0xC9, 0x00, 0x0A, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, + 0x7B, 0x00, 0xE5, 0x00, 0x29, 0xFE, 0x41, 0xFB, 0xCF, 0x02, 0x87, + 0x15, 0x76, 0x21, 0x8A, 0x18, 0x66, 0x05, 0x84, 0xFB, 0x74, 0xFD, + 0xB7, 0x00, 0xA2, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, + 0xFF, 0xA5, 0x00, 0xB2, 0x00, 0x65, 0xFD, 0x8E, 0xFB, 0xA1, 0x05, + 0xC7, 0x18, 0x6F, 0x21, 0x45, 0x15, 0x9B, 0x02, 0x40, 0xFB, 0x38, + 0xFE, 0xE7, 0x00, 0x78, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, + 0xCC, 0x00, 0x5E, 0x00, 0xA7, 0xFC, 0x44, 0xFC, 0xCA, 0x08, 0xAC, + 0x1B, 0xAD, 0x20, 0xC9, 0x11, 0x3B, 0x00, 0x54, 0xFB, 0xF1, 0xFE, + 0xFB, 0x00, 0x51, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x20, 0x00, 0xEA, + 0x00, 0xE8, 0xFF, 0x00, 0xFC, 0x6E, 0xFD, 0x32, 0x0C, 0x16, 0x1E, + 0x38, 0x1F, 0x3A, 0x0E, 0x51, 0xFE, 0xB1, 0xFB, 0x96, 0xFF, 0xF6, + 0x00, 0x30, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3D, 0x00, 0xFA, 0x00, + 0x54, 0xFF, 0x82, 0xFB, 0x12, 0xFF, 0xBC, 0x0F, 0xEA, 0x1F, 0x21, + 0x1D, 0xBB, 0x0A, 0xE1, 0xFC, 0x43, 0xFC, 0x1E, 0x00, 0xDF, 0x00, + 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x61, 0x00, 0xF6, 0x00, 0xA5, + 0xFE, 0x42, 0xFB, 0x2F, 0x01, 0x47, 0x13, 0x15, 0x21, 0x80, 0x1A, + 0x6A, 0x07, 0xE9, 0xFB, 0xF6, 0xFC, 0x86, 0x00, 0xBC, 0x00, 0x05, + 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8A, 0x00, 0xD7, 0x00, 0xE6, 0xFD, + 0x51, 0xFB, 0xBD, 0x03, 0xB0, 0x16, 0x89, 0x21, 0x71, 0x17, 0x63, + 0x04, 0x61, 0xFB, 0xB8, 0xFD, 0xCB, 0x00, 0x93, 0x00, 0xFB, 0xFF, + 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xB3, 0x00, 0x99, 0x00, 0x22, + 0xFD, 0xC0, 0xFB, 0xB0, 0x06, 0xD4, 0x19, 0x41, 0x21, 0x14, 0x14, + 0xBC, 0x01, 0x3D, 0xFB, 0x7A, 0xFE, 0xF1, 0x00, 0x6A, 0x00, 0xF7, + 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD7, 0x00, 0x39, 0x00, 0x6A, 0xFC, + 0x9D, 0xFC, 0xF2, 0x09, 0x91, 0x1C, 0x3F, 0x20, 0x8E, 0x10, 0x84, + 0xFF, 0x6D, 0xFB, 0x2D, 0xFF, 0xFC, 0x00, 0x45, 0x00, 0xF7, 0xFF, + 0xFA, 0xFF, 0x29, 0x00, 0xF2, 0x00, 0xB8, 0xFF, 0xCF, 0xFB, 0xF1, + 0xFD, 0x69, 0x0D, 0xCA, 0x1E, 0x90, 0x1E, 0x01, 0x0D, 0xC4, 0xFD, + 0xDF, 0xFB, 0xC9, 0xFF, 0xF0, 0x00, 0x26, 0x00, 0xFA, 0xFF, 0xF7, + 0xFF, 0x49, 0x00, 0xFC, 0x00, 0x1A, 0xFF, 0x64, 0xFB, 0xC0, 0xFF, + 0xF7, 0x10, 0x66, 0x20, 0x46, 0x1C, 0x8E, 0x09, 0x7E, 0xFC, 0x7E, + 0xFC, 0x46, 0x00, 0xD4, 0x00, 0x0F, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, + 0x6F, 0x00, 0xEE, 0x00, 0x64, 0xFE, 0x3D, 0xFB, 0x05, 0x02, 0x7B, + 0x14, 0x53, 0x21, 0x7C, 0x19, 0x54, 0x06, 0xAE, 0xFB, 0x38, 0xFD, + 0xA1, 0x00, 0xAE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC, + 0xFF, 0x98, 0x00, 0xC5, 0x00, 0xA2, 0xFD, 0x6C, 0xFB, 0xB8, 0x04, + 0xCF, 0x17, 0x85, 0x21, 0x4E, 0x16, 0x6C, 0x03, 0x4A, 0xFB, 0xFC, + 0xFD, 0xDC, 0x00, 0x85, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x07, 0x00, + 0xC0, 0x00, 0x7B, 0x00, 0xE0, 0xFC, 0x00, 0xFC, 0xC9, 0x07, 0xD3, + 0x1A, 0xFC, 0x20, 0xDF, 0x12, 0xEA, 0x00, 0x46, 0xFB, 0xBA, 0xFE, + 0xF8, 0x00, 0x5D, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x19, 0x00, 0xE2, + 0x00, 0x10, 0x00, 0x30, 0xFC, 0x05, 0xFD, 0x21, 0x0B, 0x66, 0x1D, + 0xBD, 0x1F, 0x53, 0x0F, 0xDB, 0xFE, 0x8E, 0xFB, 0x66, 0xFF, 0xFA, + 0x00, 0x3A, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x34, 0x00, 0xF7, 0x00, + 0x84, 0xFF, 0xA4, 0xFB, 0x84, 0xFE, 0xA3, 0x0E, 0x6C, 0x1F, 0xD6, + 0x1D, 0xCB, 0x0B, 0x45, 0xFD, 0x12, 0xFC, 0xF8, 0xFF, 0xE7, 0x00, + 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x56, 0x00, 0xFA, 0x00, 0xDD, + 0xFE, 0x4E, 0xFB, 0x7C, 0x00, 0x32, 0x12, 0xCC, 0x20, 0x5C, 0x1B, + 0x69, 0x08, 0x29, 0xFC, 0xBC, 0xFC, 0x69, 0x00, 0xC7, 0x00, 0x09, + 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7D, 0x00, 0xE3, 0x00, 0x21, 0xFE, + 0x43, 0xFB, 0xE9, 0x02, 0xA9, 0x15, 0x79, 0x21, 0x6B, 0x18, 0x48, + 0x05, 0x80, 0xFB, 0x7C, 0xFD, 0xB9, 0x00, 0xA0, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA6, 0x00, 0xAF, 0x00, 0x5E, + 0xFD, 0x93, 0xFB, 0xBE, 0x05, 0xE5, 0x18, 0x6B, 0x21, 0x23, 0x15, + 0x82, 0x02, 0x3F, 0xFB, 0x3F, 0xFE, 0xE9, 0x00, 0x77, 0x00, 0xF8, + 0xFF, 0xFE, 0xFF, 0x0C, 0x00, 0xCD, 0x00, 0x5A, 0x00, 0xA0, 0xFC, + 0x4D, 0xFC, 0xEA, 0x08, 0xC6, 0x1B, 0xA1, 0x20, 0xA6, 0x11, 0x26, + 0x00, 0x57, 0xFB, 0xF8, 0xFE, 0xFB, 0x00, 0x50, 0x00, 0xF7, 0xFF, + 0xFB, 0xFF, 0x21, 0x00, 0xEB, 0x00, 0xE3, 0xFF, 0xFA, 0xFB, 0x7C, + 0xFD, 0x54, 0x0C, 0x2B, 0x1E, 0x26, 0x1F, 0x17, 0x0E, 0x41, 0xFE, + 0xB6, 0xFB, 0x9C, 0xFF, 0xF5, 0x00, 0x2F, 0x00, 0xF9, 0xFF, 0xF8, + 0xFF, 0x3F, 0x00, 0xFB, 0x00, 0x4D, 0xFF, 0x7F, 0xFB, 0x24, 0xFF, + 0xDF, 0x0F, 0xF9, 0x1F, 0x09, 0x1D, 0x99, 0x0A, 0xD5, 0xFC, 0x49, + 0xFC, 0x23, 0x00, 0xDD, 0x00, 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, + 0x63, 0x00, 0xF5, 0x00, 0x9E, 0xFE, 0x41, 0xFB, 0x46, 0x01, 0x69, + 0x13, 0x1D, 0x21, 0x63, 0x1A, 0x4B, 0x07, 0xE2, 0xFB, 0xFD, 0xFC, + 0x89, 0x00, 0xBA, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8B, + 0x00, 0xD5, 0x00, 0xDE, 0xFD, 0x53, 0xFB, 0xD9, 0x03, 0xD0, 0x16, + 0x8A, 0x21, 0x51, 0x17, 0x47, 0x04, 0x5E, 0xFB, 0xC0, 0xFD, 0xCD, + 0x00, 0x92, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, + 0xB4, 0x00, 0x96, 0x00, 0x1B, 0xFD, 0xC7, 0xFB, 0xCF, 0x06, 0xF0, + 0x19, 0x3A, 0x21, 0xF2, 0x13, 0xA4, 0x01, 0x3E, 0xFB, 0x81, 0xFE, + 0xF2, 0x00, 0x69, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD9, + 0x00, 0x35, 0x00, 0x63, 0xFC, 0xA8, 0xFC, 0x13, 0x0A, 0xA9, 0x1C, + 0x32, 0x20, 0x6B, 0x10, 0x71, 0xFF, 0x71, 0xFB, 0x34, 0xFF, 0xFB, + 0x00, 0x44, 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2B, 0x00, 0xF3, 0x00, + 0xB3, 0xFF, 0xCA, 0xFB, 0x01, 0xFE, 0x8C, 0x0D, 0xDD, 0x1E, 0x7C, + 0x1E, 0xDE, 0x0C, 0xB5, 0xFD, 0xE4, 0xFB, 0xCE, 0xFF, 0xEF, 0x00, + 0x25, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4A, 0x00, 0xFC, 0x00, 0x13, + 0xFF, 0x61, 0xFB, 0xD4, 0xFF, 0x1A, 0x11, 0x72, 0x20, 0x2D, 0x1C, + 0x6D, 0x09, 0x74, 0xFC, 0x85, 0xFC, 0x4A, 0x00, 0xD2, 0x00, 0x0F, + 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x70, 0x00, 0xED, 0x00, 0x5D, 0xFE, + 0x3D, 0xFB, 0x1E, 0x02, 0x9C, 0x14, 0x58, 0x21, 0x5E, 0x19, 0x36, + 0x06, 0xA8, 0xFB, 0x40, 0xFD, 0xA4, 0x00, 0xAD, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x9A, 0x00, 0xC3, 0x00, 0x9A, + 0xFD, 0x6F, 0xFB, 0xD5, 0x04, 0xEF, 0x17, 0x83, 0x21, 0x2D, 0x16, + 0x52, 0x03, 0x49, 0xFB, 0x04, 0xFE, 0xDD, 0x00, 0x83, 0x00, 0xF9, + 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xC2, 0x00, 0x78, 0x00, 0xD9, 0xFC, + 0x08, 0xFC, 0xE9, 0x07, 0xEF, 0x1A, 0xF3, 0x20, 0xBC, 0x12, 0xD4, + 0x00, 0x47, 0xFB, 0xC1, 0xFE, 0xF8, 0x00, 0x5B, 0x00, 0xF7, 0xFF, + 0xFC, 0xFF, 0x1A, 0x00, 0xE3, 0x00, 0x0B, 0x00, 0x2A, 0xFC, 0x12, + 0xFD, 0x42, 0x0B, 0x7D, 0x1D, 0xAD, 0x1F, 0x2F, 0x0F, 0xC9, 0xFE, + 0x92, 0xFB, 0x6C, 0xFF, 0xF9, 0x00, 0x38, 0x00, 0xF8, 0xFF, 0xF9, + 0xFF, 0x35, 0x00, 0xF8, 0x00, 0x7E, 0xFF, 0x9F, 0xFB, 0x95, 0xFE, + 0xC6, 0x0E, 0x7C, 0x1F, 0xC0, 0x1D, 0xA9, 0x0B, 0x38, 0xFD, 0x18, + 0xFC, 0xFD, 0xFF, 0xE6, 0x00, 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, + 0x57, 0x00, 0xFA, 0x00, 0xD6, 0xFE, 0x4C, 0xFB, 0x92, 0x00, 0x54, + 0x12, 0xD6, 0x20, 0x41, 0x1B, 0x49, 0x08, 0x20, 0xFC, 0xC3, 0xFC, + 0x6D, 0x00, 0xC6, 0x00, 0x09, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7E, + 0x00, 0xE2, 0x00, 0x1A, 0xFE, 0x44, 0xFB, 0x03, 0x03, 0xCA, 0x15, + 0x7C, 0x21, 0x4C, 0x18, 0x2B, 0x05, 0x7B, 0xFB, 0x83, 0xFD, 0xBC, + 0x00, 0x9E, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xA8, 0x00, 0xAD, 0x00, 0x56, 0xFD, 0x98, 0xFB, 0xDC, 0x05, 0x04, + 0x19, 0x66, 0x21, 0x02, 0x15, 0x69, 0x02, 0x3E, 0xFB, 0x47, 0xFE, + 0xEA, 0x00, 0x75, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xCE, + 0x00, 0x56, 0x00, 0x99, 0xFC, 0x56, 0xFC, 0x0B, 0x09, 0xE0, 0x1B, + 0x96, 0x20, 0x83, 0x11, 0x11, 0x00, 0x59, 0xFB, 0xFF, 0xFE, 0xFB, + 0x00, 0x4E, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x22, 0x00, 0xEC, 0x00, + 0xDE, 0xFF, 0xF5, 0xFB, 0x8A, 0xFD, 0x77, 0x0C, 0x3F, 0x1E, 0x14, + 0x1F, 0xF5, 0x0D, 0x31, 0xFE, 0xBB, 0xFB, 0xA2, 0xFF, 0xF5, 0x00, + 0x2E, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x40, 0x00, 0xFB, 0x00, 0x47, + 0xFF, 0x7B, 0xFB, 0x37, 0xFF, 0x02, 0x10, 0x07, 0x20, 0xF2, 0x1C, + 0x78, 0x0A, 0xCA, 0xFC, 0x50, 0xFC, 0x27, 0x00, 0xDC, 0x00, 0x15, + 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x64, 0x00, 0xF5, 0x00, 0x97, 0xFE, + 0x40, 0xFB, 0x5D, 0x01, 0x8B, 0x13, 0x25, 0x21, 0x47, 0x1A, 0x2C, + 0x07, 0xDB, 0xFB, 0x05, 0xFD, 0x8C, 0x00, 0xB9, 0x00, 0x04, 0x00, + 0xFF, 0xFF, 0xFA, 0xFF, 0x8D, 0x00, 0xD3, 0x00, 0xD6, 0xFD, 0x56, + 0xFB, 0xF4, 0x03, 0xF0, 0x16, 0x8A, 0x21, 0x31, 0x17, 0x2B, 0x04, + 0x5B, 0xFB, 0xC7, 0xFD, 0xCF, 0x00, 0x90, 0x00, 0xFA, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xB6, 0x00, 0x92, 0x00, 0x13, 0xFD, + 0xCD, 0xFB, 0xEE, 0x06, 0x0D, 0x1A, 0x33, 0x21, 0xD0, 0x13, 0x8C, + 0x01, 0x3E, 0xFB, 0x88, 0xFE, 0xF3, 0x00, 0x67, 0x00, 0xF7, 0xFF, + 0x06, 0x00, 0x1D, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0xA1, 0x02, 0xA6, + 0xF8, 0x56, 0x02, 0xA5, 0x28, 0xA5, 0x28, 0x56, 0x02, 0xA6, 0xF8, + 0xA1, 0x02, 0xFE, 0x00, 0x03, 0xFF, 0x1D, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x21, 0x00, 0xA6, 0xFF, 0x3F, 0xFF, 0x0B, 0x03, 0x42, 0xFE, + 0x3E, 0xF8, 0x7F, 0x15, 0xAC, 0x30, 0x7F, 0x15, 0x3E, 0xF8, 0x42, + 0xFE, 0x0B, 0x03, 0x3F, 0xFF, 0xA6, 0xFF, 0x21, 0x00, 0x00, 0x00, + 0xFA, 0xFF, 0xCE, 0xFF, 0x14, 0x01, 0x00, 0xFD, 0x35, 0x06, 0xD5, + 0xF4, 0xDA, 0x15, 0x92, 0x40, 0xAE, 0xFE, 0xF3, 0xFC, 0x68, 0x03, + 0x86, 0xFD, 0x51, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEC, + 0xFF, 0xF9, 0xFF, 0xC6, 0x00, 0x55, 0xFD, 0x35, 0x06, 0x90, 0xF3, + 0xE5, 0x1C, 0x6B, 0x3D, 0x71, 0xFA, 0x34, 0xFF, 0x46, 0x02, 0xFF, + 0xFD, 0x2D, 0x01, 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDB, 0xFF, + 0x2D, 0x00, 0x60, 0x00, 0xE1, 0xFD, 0xCE, 0x05, 0xED, 0xF2, 0xF3, + 0x23, 0x20, 0x39, 0x22, 0xF7, 0x44, 0x01, 0x1F, 0x01, 0x89, 0xFE, + 0xFB, 0x00, 0x9C, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC9, 0xFF, 0x68, + 0x00, 0xE5, 0xFF, 0xA0, 0xFE, 0xFB, 0x04, 0x0C, 0xF3, 0xC5, 0x2A, + 0xD8, 0x33, 0xC9, 0xF4, 0x0B, 0x03, 0x05, 0x00, 0x1A, 0xFF, 0xC1, + 0x00, 0xAD, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB5, 0xFF, 0xA5, 0x00, + 0x5C, 0xFF, 0x8C, 0xFF, 0xBF, 0x03, 0x06, 0xF4, 0x22, 0x31, 0xC8, + 0x2D, 0x63, 0xF3, 0x76, 0x04, 0x08, 0xFF, 0xA7, 0xFF, 0x84, 0x00, + 0xC0, 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA4, 0xFF, 0xE1, 0x00, 0xCB, + 0xFE, 0x9B, 0x00, 0x21, 0x02, 0xEE, 0xF5, 0xCD, 0x36, 0x24, 0x27, + 0xE1, 0xF2, 0x7A, 0x05, 0x33, 0xFE, 0x2A, 0x00, 0x47, 0x00, 0xD3, + 0xFF, 0x04, 0x00, 0x0F, 0x00, 0x95, 0xFF, 0x17, 0x01, 0x3D, 0xFE, + 0xBD, 0x01, 0x30, 0x00, 0xCC, 0xF8, 0x92, 0x3B, 0x2A, 0x20, 0x2E, + 0xF3, 0x12, 0x06, 0x8F, 0xFD, 0x9A, 0x00, 0x10, 0x00, 0xE5, 0xFF, + 0x02, 0x00, 0x10, 0x00, 0x8C, 0xFF, 0x42, 0x01, 0xBB, 0xFD, 0xE4, + 0x02, 0x01, 0xFE, 0x9C, 0xFC, 0x45, 0x3F, 0x16, 0x19, 0x2D, 0xF4, + 0x41, 0x06, 0x21, 0xFD, 0xF3, 0x00, 0xE0, 0xFF, 0xF4, 0xFF, 0x01, + 0x00, 0x10, 0x00, 0x8B, 0xFF, 0x5D, 0x01, 0x4F, 0xFD, 0xFB, 0x03, + 0xB2, 0xFB, 0x53, 0x01, 0xC2, 0x41, 0x24, 0x12, 0xBA, 0xF5, 0x0F, + 0x06, 0xE9, 0xFC, 0x33, 0x01, 0xBB, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x93, 0xFF, 0x63, 0x01, 0x04, 0xFD, 0xEF, 0x04, 0x62, + 0xF9, 0xD7, 0x06, 0xF2, 0x42, 0x8D, 0x0B, 0xB0, 0xF7, 0x87, 0x05, + 0xE6, 0xFC, 0x58, 0x01, 0xA0, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0xA5, 0xFF, 0x52, 0x01, 0xE2, 0xFC, 0xAD, 0x05, + 0x35, 0xF7, 0x08, 0x0D, 0xCB, 0x42, 0x81, 0x05, 0xE8, 0xF9, 0xBB, + 0x04, 0x12, 0xFD, 0x64, 0x01, 0x90, 0xFF, 0x0E, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xC2, 0xFF, 0x27, 0x01, 0xF1, 0xFC, 0x22, 0x06, 0x54, + 0xF5, 0xB8, 0x13, 0x4A, 0x41, 0x29, 0x00, 0x3C, 0xFC, 0xBD, 0x03, + 0x66, 0xFD, 0x58, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF1, + 0xFF, 0xEB, 0xFF, 0xE1, 0x00, 0x35, 0xFD, 0x40, 0x06, 0xE4, 0xF3, + 0xB7, 0x1A, 0x85, 0x3E, 0xA6, 0xFB, 0x86, 0xFE, 0xA0, 0x02, 0xD7, + 0xFD, 0x39, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xE1, 0xFF, + 0x1C, 0x00, 0x82, 0x00, 0xB0, 0xFD, 0xF9, 0x05, 0x0C, 0xF3, 0xCB, + 0x21, 0x8F, 0x3A, 0x0D, 0xF8, 0xA9, 0x00, 0x79, 0x01, 0x5D, 0xFE, + 0x0B, 0x01, 0x98, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCE, 0xFF, 0x55, + 0x00, 0x0D, 0x00, 0x60, 0xFE, 0x48, 0x05, 0xEC, 0xF2, 0xB6, 0x28, + 0x91, 0x35, 0x68, 0xF5, 0x88, 0x02, 0x5A, 0x00, 0xED, 0xFE, 0xD4, + 0x00, 0xA8, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0x92, 0x00, + 0x87, 0xFF, 0x3F, 0xFF, 0x2B, 0x04, 0xA1, 0xF3, 0x3D, 0x2F, 0xB8, + 0x2F, 0xB8, 0xF3, 0x11, 0x04, 0x52, 0xFF, 0x7C, 0xFF, 0x97, 0x00, + 0xBA, 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA9, 0xFF, 0xCF, 0x00, 0xF8, + 0xFE, 0x44, 0x00, 0xAA, 0x02, 0x3E, 0xF5, 0x24, 0x35, 0x3B, 0x29, + 0xF2, 0xF2, 0x35, 0x05, 0x70, 0xFE, 0x03, 0x00, 0x5A, 0x00, 0xCD, + 0xFF, 0x05, 0x00, 0x0E, 0x00, 0x99, 0xFF, 0x07, 0x01, 0x68, 0xFE, + 0x63, 0x01, 0xD0, 0x00, 0xD0, 0xF7, 0x35, 0x3A, 0x55, 0x22, 0x02, + 0xF3, 0xEF, 0x05, 0xBC, 0xFD, 0x7A, 0x00, 0x20, 0x00, 0xDF, 0xFF, + 0x03, 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x36, 0x01, 0xE1, 0xFD, 0x8A, + 0x02, 0xB2, 0xFE, 0x56, 0xFB, 0x40, 0x3E, 0x42, 0x1B, 0xCE, 0xF3, + 0x3E, 0x06, 0x3D, 0xFD, 0xDB, 0x00, 0xEE, 0xFF, 0xF0, 0xFF, 0x01, + 0x00, 0x11, 0x00, 0x8A, 0xFF, 0x57, 0x01, 0x6D, 0xFD, 0xA8, 0x03, + 0x69, 0xFC, 0xC8, 0xFF, 0x20, 0x41, 0x40, 0x14, 0x33, 0xF5, 0x28, + 0x06, 0xF5, 0xFC, 0x22, 0x01, 0xC5, 0xFF, 0xFD, 0xFF, 0x00, 0x00, + 0x0F, 0x00, 0x8F, 0xFF, 0x64, 0x01, 0x17, 0xFD, 0xA9, 0x04, 0x16, + 0xFA, 0x10, 0x05, 0xB8, 0x42, 0x87, 0x0D, 0x0D, 0xF7, 0xB9, 0x05, + 0xE2, 0xFC, 0x50, 0x01, 0xA7, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0A, 0x00, 0x9E, 0xFF, 0x5A, 0x01, 0xE8, 0xFC, 0x7A, 0x05, + 0xDA, 0xF7, 0x10, 0x0B, 0xFB, 0x42, 0x4B, 0x07, 0x35, 0xF9, 0x00, + 0x05, 0x00, 0xFD, 0x63, 0x01, 0x94, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xB8, 0xFF, 0x37, 0x01, 0xE7, 0xFC, 0x07, 0x06, 0xDE, + 0xF5, 0x9F, 0x11, 0xE4, 0x41, 0xB8, 0x01, 0x84, 0xFB, 0x0F, 0x04, + 0x48, 0xFD, 0x5E, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF5, + 0xFF, 0xDD, 0xFF, 0xF9, 0x00, 0x1B, 0xFD, 0x41, 0x06, 0x47, 0xF4, + 0x8B, 0x18, 0x81, 0x3F, 0xF1, 0xFC, 0xD5, 0xFD, 0xFA, 0x02, 0xB2, + 0xFD, 0x45, 0x01, 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE6, 0xFF, + 0x0C, 0x00, 0xA2, 0x00, 0x85, 0xFD, 0x1A, 0x06, 0x3C, 0xF3, 0x9F, + 0x1F, 0xE6, 0x3B, 0x0E, 0xF9, 0x07, 0x00, 0xD4, 0x01, 0x33, 0xFE, + 0x1B, 0x01, 0x94, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD4, 0xFF, 0x43, + 0x00, 0x33, 0x00, 0x25, 0xFE, 0x89, 0x05, 0xE0, 0xF2, 0x9C, 0x26, + 0x33, 0x37, 0x1E, 0xF6, 0xFD, 0x01, 0xB0, 0x00, 0xC0, 0xFE, 0xE6, + 0x00, 0xA2, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC1, 0xFF, 0x7F, 0x00, + 0xB2, 0xFF, 0xF6, 0xFE, 0x8E, 0x04, 0x51, 0xF3, 0x49, 0x2D, 0x98, + 0x31, 0x23, 0xF4, 0xA2, 0x03, 0xA0, 0xFF, 0x51, 0xFF, 0xAA, 0x00, + 0xB4, 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAE, 0xFF, 0xBD, 0x00, 0x25, + 0xFF, 0xF1, 0xFF, 0x2B, 0x03, 0xA5, 0xF4, 0x68, 0x33, 0x48, 0x2B, + 0x17, 0xF3, 0xE7, 0x04, 0xB1, 0xFE, 0xDB, 0xFF, 0x6C, 0x00, 0xC7, + 0xFF, 0x06, 0x00, 0x0D, 0x00, 0x9E, 0xFF, 0xF7, 0x00, 0x94, 0xFE, + 0x09, 0x01, 0x6A, 0x01, 0xEB, 0xF6, 0xC1, 0x38, 0x7D, 0x24, 0xE8, + 0xF2, 0xC1, 0x05, 0xEE, 0xFD, 0x57, 0x00, 0x31, 0x00, 0xDA, 0xFF, + 0x03, 0x00, 0x10, 0x00, 0x91, 0xFF, 0x29, 0x01, 0x09, 0xFE, 0x2F, + 0x02, 0x5F, 0xFF, 0x27, 0xFA, 0x20, 0x3D, 0x70, 0x1D, 0x7D, 0xF3, + 0x31, 0x06, 0x5E, 0xFD, 0xBF, 0x00, 0xFD, 0xFF, 0xEB, 0xFF, 0x02, + 0x00, 0x11, 0x00, 0x8B, 0xFF, 0x4E, 0x01, 0x8E, 0xFD, 0x52, 0x03, + 0x20, 0xFD, 0x52, 0xFE, 0x60, 0x40, 0x63, 0x16, 0xB7, 0xF4, 0x39, + 0x06, 0x05, 0xFD, 0x0F, 0x01, 0xD1, 0xFF, 0xF9, 0xFF, 0x00, 0x00, + 0x10, 0x00, 0x8D, 0xFF, 0x62, 0x01, 0x2E, 0xFD, 0x5E, 0x04, 0xCC, + 0xFA, 0x5B, 0x03, 0x5E, 0x42, 0x8E, 0x0F, 0x71, 0xF6, 0xE4, 0x05, + 0xE2, 0xFC, 0x45, 0x01, 0xAF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0B, 0x00, 0x99, 0xFF, 0x60, 0x01, 0xF2, 0xFC, 0x40, 0x05, + 0x85, 0xF8, 0x26, 0x09, 0x0C, 0x43, 0x26, 0x09, 0x85, 0xF8, 0x40, + 0x05, 0xF2, 0xFC, 0x60, 0x01, 0x99, 0xFF, 0x0B, 0x00, 0x00, 0x00, + 0x04, 0x00, 0xAF, 0xFF, 0x45, 0x01, 0xE2, 0xFC, 0xE4, 0x05, 0x71, + 0xF6, 0x8E, 0x0F, 0x5E, 0x42, 0x5B, 0x03, 0xCC, 0xFA, 0x5E, 0x04, + 0x2E, 0xFD, 0x62, 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xF9, + 0xFF, 0xD1, 0xFF, 0x0F, 0x01, 0x05, 0xFD, 0x39, 0x06, 0xB7, 0xF4, + 0x63, 0x16, 0x60, 0x40, 0x52, 0xFE, 0x20, 0xFD, 0x52, 0x03, 0x8E, + 0xFD, 0x4E, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEB, 0xFF, + 0xFD, 0xFF, 0xBF, 0x00, 0x5E, 0xFD, 0x31, 0x06, 0x7D, 0xF3, 0x70, + 0x1D, 0x20, 0x3D, 0x27, 0xFA, 0x5F, 0xFF, 0x2F, 0x02, 0x09, 0xFE, + 0x29, 0x01, 0x91, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDA, 0xFF, 0x31, + 0x00, 0x57, 0x00, 0xEE, 0xFD, 0xC1, 0x05, 0xE8, 0xF2, 0x7D, 0x24, + 0xC1, 0x38, 0xEB, 0xF6, 0x6A, 0x01, 0x09, 0x01, 0x94, 0xFE, 0xF7, + 0x00, 0x9E, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC7, 0xFF, 0x6C, 0x00, + 0xDB, 0xFF, 0xB1, 0xFE, 0xE7, 0x04, 0x17, 0xF3, 0x48, 0x2B, 0x68, + 0x33, 0xA5, 0xF4, 0x2B, 0x03, 0xF1, 0xFF, 0x25, 0xFF, 0xBD, 0x00, + 0xAE, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB4, 0xFF, 0xAA, 0x00, 0x51, + 0xFF, 0xA0, 0xFF, 0xA2, 0x03, 0x23, 0xF4, 0x98, 0x31, 0x49, 0x2D, + 0x51, 0xF3, 0x8E, 0x04, 0xF6, 0xFE, 0xB2, 0xFF, 0x7F, 0x00, 0xC1, + 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA2, 0xFF, 0xE6, 0x00, 0xC0, 0xFE, + 0xB0, 0x00, 0xFD, 0x01, 0x1E, 0xF6, 0x33, 0x37, 0x9C, 0x26, 0xE0, + 0xF2, 0x89, 0x05, 0x25, 0xFE, 0x33, 0x00, 0x43, 0x00, 0xD4, 0xFF, + 0x04, 0x00, 0x0F, 0x00, 0x94, 0xFF, 0x1B, 0x01, 0x33, 0xFE, 0xD4, + 0x01, 0x07, 0x00, 0x0E, 0xF9, 0xE6, 0x3B, 0x9F, 0x1F, 0x3C, 0xF3, + 0x1A, 0x06, 0x85, 0xFD, 0xA2, 0x00, 0x0C, 0x00, 0xE6, 0xFF, 0x02, + 0x00, 0x11, 0x00, 0x8C, 0xFF, 0x45, 0x01, 0xB2, 0xFD, 0xFA, 0x02, + 0xD5, 0xFD, 0xF1, 0xFC, 0x81, 0x3F, 0x8B, 0x18, 0x47, 0xF4, 0x41, + 0x06, 0x1B, 0xFD, 0xF9, 0x00, 0xDD, 0xFF, 0xF5, 0xFF, 0x01, 0x00, + 0x10, 0x00, 0x8B, 0xFF, 0x5E, 0x01, 0x48, 0xFD, 0x0F, 0x04, 0x84, + 0xFB, 0xB8, 0x01, 0xE4, 0x41, 0x9F, 0x11, 0xDE, 0xF5, 0x07, 0x06, + 0xE7, 0xFC, 0x37, 0x01, 0xB8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0D, + 0x00, 0x94, 0xFF, 0x63, 0x01, 0x00, 0xFD, 0x00, 0x05, 0x35, 0xF9, + 0x4B, 0x07, 0xFB, 0x42, 0x10, 0x0B, 0xDA, 0xF7, 0x7A, 0x05, 0xE8, + 0xFC, 0x5A, 0x01, 0x9E, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0xA7, 0xFF, 0x50, 0x01, 0xE2, 0xFC, 0xB9, 0x05, 0x0D, + 0xF7, 0x87, 0x0D, 0xB8, 0x42, 0x10, 0x05, 0x16, 0xFA, 0xA9, 0x04, + 0x17, 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0xC5, 0xFF, 0x22, 0x01, 0xF5, 0xFC, 0x28, 0x06, 0x33, 0xF5, + 0x40, 0x14, 0x20, 0x41, 0xC8, 0xFF, 0x69, 0xFC, 0xA8, 0x03, 0x6D, + 0xFD, 0x57, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF0, 0xFF, + 0xEE, 0xFF, 0xDB, 0x00, 0x3D, 0xFD, 0x3E, 0x06, 0xCE, 0xF3, 0x42, + 0x1B, 0x40, 0x3E, 0x56, 0xFB, 0xB2, 0xFE, 0x8A, 0x02, 0xE1, 0xFD, + 0x36, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDF, 0xFF, 0x20, + 0x00, 0x7A, 0x00, 0xBC, 0xFD, 0xEF, 0x05, 0x02, 0xF3, 0x55, 0x22, + 0x35, 0x3A, 0xD0, 0xF7, 0xD0, 0x00, 0x63, 0x01, 0x68, 0xFE, 0x07, + 0x01, 0x99, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCD, 0xFF, 0x5A, 0x00, + 0x03, 0x00, 0x70, 0xFE, 0x35, 0x05, 0xF2, 0xF2, 0x3B, 0x29, 0x24, + 0x35, 0x3E, 0xF5, 0xAA, 0x02, 0x44, 0x00, 0xF8, 0xFE, 0xCF, 0x00, + 0xA9, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBA, 0xFF, 0x97, 0x00, 0x7C, + 0xFF, 0x52, 0xFF, 0x11, 0x04, 0xB8, 0xF3, 0xB8, 0x2F, 0x3D, 0x2F, + 0xA1, 0xF3, 0x2B, 0x04, 0x3F, 0xFF, 0x87, 0xFF, 0x92, 0x00, 0xBB, + 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA8, 0xFF, 0xD4, 0x00, 0xED, 0xFE, + 0x5A, 0x00, 0x88, 0x02, 0x68, 0xF5, 0x91, 0x35, 0xB6, 0x28, 0xEC, + 0xF2, 0x48, 0x05, 0x60, 0xFE, 0x0D, 0x00, 0x55, 0x00, 0xCE, 0xFF, + 0x05, 0x00, 0x0E, 0x00, 0x98, 0xFF, 0x0B, 0x01, 0x5D, 0xFE, 0x79, + 0x01, 0xA9, 0x00, 0x0D, 0xF8, 0x8F, 0x3A, 0xCB, 0x21, 0x0C, 0xF3, + 0xF9, 0x05, 0xB0, 0xFD, 0x82, 0x00, 0x1C, 0x00, 0xE1, 0xFF, 0x03, + 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x39, 0x01, 0xD7, 0xFD, 0xA0, 0x02, + 0x86, 0xFE, 0xA6, 0xFB, 0x85, 0x3E, 0xB7, 0x1A, 0xE4, 0xF3, 0x40, + 0x06, 0x35, 0xFD, 0xE1, 0x00, 0xEB, 0xFF, 0xF1, 0xFF, 0x01, 0x00, + 0x11, 0x00, 0x8A, 0xFF, 0x58, 0x01, 0x66, 0xFD, 0xBD, 0x03, 0x3C, + 0xFC, 0x29, 0x00, 0x4A, 0x41, 0xB8, 0x13, 0x54, 0xF5, 0x22, 0x06, + 0xF1, 0xFC, 0x27, 0x01, 0xC2, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x0E, + 0x00, 0x90, 0xFF, 0x64, 0x01, 0x12, 0xFD, 0xBB, 0x04, 0xE8, 0xF9, + 0x81, 0x05, 0xCB, 0x42, 0x08, 0x0D, 0x35, 0xF7, 0xAD, 0x05, 0xE2, + 0xFC, 0x52, 0x01, 0xA5, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0xA0, 0xFF, 0x58, 0x01, 0xE6, 0xFC, 0x87, 0x05, 0xB0, + 0xF7, 0x8D, 0x0B, 0xF2, 0x42, 0xD7, 0x06, 0x62, 0xF9, 0xEF, 0x04, + 0x04, 0xFD, 0x63, 0x01, 0x93, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xBB, 0xFF, 0x33, 0x01, 0xE9, 0xFC, 0x0F, 0x06, 0xBA, 0xF5, + 0x24, 0x12, 0xC2, 0x41, 0x53, 0x01, 0xB2, 0xFB, 0xFB, 0x03, 0x4F, + 0xFD, 0x5D, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF4, 0xFF, + 0xE0, 0xFF, 0xF3, 0x00, 0x21, 0xFD, 0x41, 0x06, 0x2D, 0xF4, 0x16, + 0x19, 0x45, 0x3F, 0x9C, 0xFC, 0x01, 0xFE, 0xE4, 0x02, 0xBB, 0xFD, + 0x42, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE5, 0xFF, 0x10, + 0x00, 0x9A, 0x00, 0x8F, 0xFD, 0x12, 0x06, 0x2E, 0xF3, 0x2A, 0x20, + 0x92, 0x3B, 0xCC, 0xF8, 0x30, 0x00, 0xBD, 0x01, 0x3D, 0xFE, 0x17, + 0x01, 0x95, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD3, 0xFF, 0x47, 0x00, + 0x2A, 0x00, 0x33, 0xFE, 0x7A, 0x05, 0xE1, 0xF2, 0x24, 0x27, 0xCD, + 0x36, 0xEE, 0xF5, 0x21, 0x02, 0x9B, 0x00, 0xCB, 0xFE, 0xE1, 0x00, + 0xA4, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0x84, 0x00, 0xA7, + 0xFF, 0x08, 0xFF, 0x76, 0x04, 0x63, 0xF3, 0xC8, 0x2D, 0x22, 0x31, + 0x06, 0xF4, 0xBF, 0x03, 0x8C, 0xFF, 0x5C, 0xFF, 0xA5, 0x00, 0xB5, + 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAD, 0xFF, 0xC1, 0x00, 0x1A, 0xFF, + 0x05, 0x00, 0x0B, 0x03, 0xC9, 0xF4, 0xD8, 0x33, 0xC5, 0x2A, 0x0C, + 0xF3, 0xFB, 0x04, 0xA0, 0xFE, 0xE5, 0xFF, 0x68, 0x00, 0xC9, 0xFF, + 0x06, 0x00, 0x0D, 0x00, 0x9C, 0xFF, 0xFB, 0x00, 0x89, 0xFE, 0x1F, + 0x01, 0x44, 0x01, 0x22, 0xF7, 0x20, 0x39, 0xF3, 0x23, 0xED, 0xF2, + 0xCE, 0x05, 0xE1, 0xFD, 0x60, 0x00, 0x2D, 0x00, 0xDB, 0xFF, 0x03, + 0x00, 0x10, 0x00, 0x90, 0xFF, 0x2D, 0x01, 0xFF, 0xFD, 0x46, 0x02, + 0x34, 0xFF, 0x71, 0xFA, 0x6B, 0x3D, 0xE5, 0x1C, 0x90, 0xF3, 0x35, + 0x06, 0x55, 0xFD, 0xC6, 0x00, 0xF9, 0xFF, 0xEC, 0xFF, 0x01, 0x00, + 0x11, 0x00, 0x8B, 0xFF, 0x51, 0x01, 0x86, 0xFD, 0x68, 0x03, 0xF3, + 0xFC, 0xAE, 0xFE, 0x92, 0x40, 0xDA, 0x15, 0xD5, 0xF4, 0x35, 0x06, + 0x00, 0xFD, 0x14, 0x01, 0xCE, 0xFF, 0xFA, 0xFF, 0x00, 0x00, 0x0F, + 0x00, 0x8D, 0xFF, 0x63, 0x01, 0x28, 0xFD, 0x71, 0x04, 0x9E, 0xFA, + 0xC7, 0x03, 0x79, 0x42, 0x0B, 0x0F, 0x97, 0xF6, 0xDA, 0x05, 0xE2, + 0xFC, 0x48, 0x01, 0xAD, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x9A, 0xFF, 0x5F, 0x01, 0xEF, 0xFC, 0x4F, 0x05, 0x5A, + 0xF8, 0x9F, 0x09, 0x0A, 0x43, 0xAE, 0x08, 0xB1, 0xF8, 0x30, 0x05, + 0xF5, 0xFC, 0x61, 0x01, 0x97, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, + 0x00, 0xB1, 0xFF, 0x41, 0x01, 0xE3, 0xFC, 0xED, 0x05, 0x4C, 0xF6, + 0x11, 0x10, 0x42, 0x42, 0xF1, 0x02, 0xFA, 0xFA, 0x4B, 0x04, 0x34, + 0xFD, 0x61, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF8, 0xFF, + 0xD4, 0xFF, 0x0A, 0x01, 0x0A, 0xFD, 0x3C, 0x06, 0x9A, 0xF4, 0xED, + 0x16, 0x2A, 0x40, 0xF8, 0xFD, 0x4D, 0xFD, 0x3C, 0x03, 0x97, 0xFD, + 0x4C, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEA, 0xFF, 0x00, + 0x00, 0xB8, 0x00, 0x67, 0xFD, 0x2C, 0x06, 0x6B, 0xF3, 0xFC, 0x1D, + 0xD3, 0x3C, 0xDF, 0xF9, 0x89, 0xFF, 0x18, 0x02, 0x13, 0xFE, 0x26, + 0x01, 0x92, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD9, 0xFF, 0x36, 0x00, + 0x4E, 0x00, 0xFB, 0xFD, 0xB4, 0x05, 0xE4, 0xF2, 0x04, 0x25, 0x5F, + 0x38, 0xB6, 0xF6, 0x90, 0x01, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, + 0x9F, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC6, 0xFF, 0x71, 0x00, 0xD1, + 0xFF, 0xC2, 0xFE, 0xD1, 0x04, 0x23, 0xF3, 0xC9, 0x2B, 0xF5, 0x32, + 0x83, 0xF4, 0x49, 0x03, 0xDC, 0xFF, 0x30, 0xFF, 0xB8, 0x00, 0xB0, + 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB3, 0xFF, 0xAE, 0x00, 0x46, 0xFF, + 0xB4, 0xFF, 0x85, 0x03, 0x42, 0xF4, 0x0E, 0x32, 0xCA, 0x2C, 0x41, + 0xF3, 0xA5, 0x04, 0xE4, 0xFE, 0xBC, 0xFF, 0x7A, 0x00, 0xC3, 0xFF, + 0x07, 0x00, 0x0D, 0x00, 0xA1, 0xFF, 0xEA, 0x00, 0xB5, 0xFE, 0xC6, + 0x00, 0xD9, 0x01, 0x4F, 0xF6, 0x99, 0x37, 0x16, 0x26, 0xE0, 0xF2, + 0x98, 0x05, 0x16, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0xD6, 0xFF, 0x04, + 0x00, 0x0F, 0x00, 0x93, 0xFF, 0x1F, 0x01, 0x28, 0xFE, 0xEB, 0x01, + 0xDD, 0xFF, 0x52, 0xF9, 0x36, 0x3C, 0x13, 0x1F, 0x4B, 0xF3, 0x20, + 0x06, 0x7B, 0xFD, 0xA9, 0x00, 0x08, 0x00, 0xE7, 0xFF, 0x02, 0x00, + 0x11, 0x00, 0x8C, 0xFF, 0x47, 0x01, 0xA9, 0xFD, 0x10, 0x03, 0xA8, + 0xFD, 0x47, 0xFD, 0xBB, 0x3F, 0x01, 0x18, 0x62, 0xF4, 0x40, 0x06, + 0x15, 0xFD, 0xFF, 0x00, 0xDA, 0xFF, 0xF6, 0xFF, 0x01, 0x00, 0x10, + 0x00, 0x8B, 0xFF, 0x5F, 0x01, 0x41, 0xFD, 0x23, 0x04, 0x56, 0xFB, + 0x1F, 0x02, 0x06, 0x42, 0x19, 0x11, 0x02, 0xF6, 0xFF, 0x05, 0xE5, + 0xFC, 0x3B, 0x01, 0xB6, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, + 0x95, 0xFF, 0x62, 0x01, 0xFC, 0xFC, 0x10, 0x05, 0x09, 0xF9, 0xC1, + 0x07, 0x03, 0x43, 0x94, 0x0A, 0x05, 0xF8, 0x6C, 0x05, 0xEA, 0xFC, + 0x5C, 0x01, 0x9D, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0xA9, 0xFF, 0x4D, 0x01, 0xE1, 0xFC, 0xC4, 0x05, 0xE6, 0xF6, + 0x08, 0x0E, 0xA5, 0x42, 0xA1, 0x04, 0x43, 0xFA, 0x97, 0x04, 0x1D, + 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF, + 0xC8, 0xFF, 0x1E, 0x01, 0xF8, 0xFC, 0x2D, 0x06, 0x13, 0xF5, 0xC8, + 0x14, 0xF2, 0x40, 0x69, 0xFF, 0x97, 0xFC, 0x92, 0x03, 0x75, 0xFD, + 0x55, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEF, 0xFF, 0xF2, + 0xFF, 0xD4, 0x00, 0x45, 0xFD, 0x3B, 0x06, 0xB8, 0xF3, 0xCE, 0x1B, + 0xFB, 0x3D, 0x08, 0xFB, 0xDE, 0xFE, 0x73, 0x02, 0xEB, 0xFD, 0x33, + 0x01, 0x8F, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDE, 0xFF, 0x25, 0x00, + 0x71, 0x00, 0xC8, 0xFD, 0xE5, 0x05, 0xFA, 0xF2, 0xDF, 0x22, 0xDB, + 0x39, 0x94, 0xF7, 0xF7, 0x00, 0x4C, 0x01, 0x73, 0xFE, 0x03, 0x01, + 0x9A, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCC, 0xFF, 0x5E, 0x00, 0xF9, + 0xFF, 0x80, 0xFE, 0x23, 0x05, 0xF9, 0xF2, 0xC0, 0x29, 0xB8, 0x34, + 0x16, 0xF5, 0xCB, 0x02, 0x2F, 0x00, 0x03, 0xFF, 0xCA, 0x00, 0xAA, + 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xB8, 0xFF, 0x9B, 0x00, 0x72, 0xFF, + 0x65, 0xFF, 0xF6, 0x03, 0xD1, 0xF3, 0x31, 0x30, 0xC1, 0x2E, 0x8B, + 0xF3, 0x45, 0x04, 0x2D, 0xFF, 0x92, 0xFF, 0x8D, 0x00, 0xBD, 0xFF, + 0x08, 0x00, 0x0C, 0x00, 0xA6, 0xFF, 0xD8, 0x00, 0xE2, 0xFE, 0x6F, + 0x00, 0x66, 0x02, 0x93, 0xF5, 0xFB, 0x35, 0x31, 0x28, 0xE7, 0xF2, + 0x59, 0x05, 0x51, 0xFE, 0x17, 0x00, 0x50, 0x00, 0xD0, 0xFF, 0x05, + 0x00, 0x0E, 0x00, 0x97, 0xFF, 0x0F, 0x01, 0x53, 0xFE, 0x90, 0x01, + 0x81, 0x00, 0x4B, 0xF8, 0xE6, 0x3A, 0x3F, 0x21, 0x16, 0xF3, 0x02, + 0x06, 0xA5, 0xFD, 0x8A, 0x00, 0x18, 0x00, 0xE2, 0xFF, 0x02, 0x00, + 0x10, 0x00, 0x8D, 0xFF, 0x3C, 0x01, 0xCE, 0xFD, 0xB7, 0x02, 0x5A, + 0xFE, 0xF7, 0xFB, 0xC6, 0x3E, 0x2C, 0x1A, 0xFC, 0xF3, 0x41, 0x06, + 0x2E, 0xFD, 0xE7, 0x00, 0xE7, 0xFF, 0xF2, 0xFF, 0x01, 0x00, 0x10, + 0x00, 0x8B, 0xFF, 0x5A, 0x01, 0x5E, 0xFD, 0xD2, 0x03, 0x0E, 0xFC, + 0x8B, 0x00, 0x75, 0x41, 0x32, 0x13, 0x75, 0xF5, 0x1C, 0x06, 0xEE, + 0xFC, 0x2B, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, + 0x91, 0xFF, 0x64, 0x01, 0x0D, 0xFD, 0xCD, 0x04, 0xBB, 0xF9, 0xF2, + 0x05, 0xD9, 0x42, 0x88, 0x0C, 0x5E, 0xF7, 0xA1, 0x05, 0xE3, 0xFC, + 0x54, 0x01, 0xA3, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0xA2, 0xFF, 0x56, 0x01, 0xE5, 0xFC, 0x94, 0x05, 0x87, 0xF7, + 0x0A, 0x0C, 0xE6, 0x42, 0x64, 0x06, 0x8E, 0xF9, 0xDE, 0x04, 0x09, + 0xFD, 0x64, 0x01, 0x92, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBD, 0xFF, 0x2F, 0x01, 0xEC, 0xFC, 0x16, 0x06, 0x98, 0xF5, 0xAB, + 0x12, 0x9C, 0x41, 0xEE, 0x00, 0xE0, 0xFB, 0xE6, 0x03, 0x57, 0xFD, + 0x5B, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF3, 0xFF, 0xE4, + 0xFF, 0xED, 0x00, 0x27, 0xFD, 0x41, 0x06, 0x14, 0xF4, 0xA1, 0x19, + 0x06, 0x3F, 0x49, 0xFC, 0x2E, 0xFE, 0xCD, 0x02, 0xC4, 0xFD, 0x3F, + 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE3, 0xFF, 0x14, 0x00, + 0x92, 0x00, 0x9A, 0xFD, 0x0A, 0x06, 0x22, 0xF3, 0xB4, 0x20, 0x3C, + 0x3B, 0x8B, 0xF8, 0x58, 0x00, 0xA7, 0x01, 0x48, 0xFE, 0x13, 0x01, + 0x96, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD1, 0xFF, 0x4C, 0x00, 0x20, + 0x00, 0x42, 0xFE, 0x6A, 0x05, 0xE3, 0xF2, 0xAB, 0x27, 0x66, 0x36, + 0xC0, 0xF5, 0x44, 0x02, 0x85, 0x00, 0xD7, 0xFE, 0xDD, 0x00, 0xA5, + 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xBE, 0xFF, 0x89, 0x00, 0x9D, 0xFF, + 0x1A, 0xFF, 0x5E, 0x04, 0x76, 0xF3, 0x45, 0x2E, 0xAA, 0x30, 0xEB, + 0xF3, 0xDB, 0x03, 0x79, 0xFF, 0x67, 0xFF, 0xA0, 0x00, 0xB7, 0xFF, + 0x09, 0x00, 0x0B, 0x00, 0xAC, 0xFF, 0xC6, 0x00, 0x0E, 0xFF, 0x1A, + 0x00, 0xEB, 0x02, 0xEF, 0xF4, 0x49, 0x34, 0x43, 0x2A, 0x02, 0xF3, + 0x0F, 0x05, 0x90, 0xFE, 0xEF, 0xFF, 0x63, 0x00, 0xCA, 0xFF, 0x06, + 0x00, 0x0E, 0x00, 0x9B, 0xFF, 0xFF, 0x00, 0x7E, 0xFE, 0x36, 0x01, + 0x1E, 0x01, 0x5B, 0xF7, 0x7E, 0x39, 0x69, 0x23, 0xF3, 0xF2, 0xD9, + 0x05, 0xD4, 0xFD, 0x69, 0x00, 0x29, 0x00, 0xDD, 0xFF, 0x03, 0x00, + 0x10, 0x00, 0x90, 0xFF, 0x30, 0x01, 0xF5, 0xFD, 0x5C, 0x02, 0x09, + 0xFF, 0xBC, 0xFA, 0xB5, 0x3D, 0x5A, 0x1C, 0xA3, 0xF3, 0x38, 0x06, + 0x4D, 0xFD, 0xCD, 0x00, 0xF5, 0xFF, 0xED, 0xFF, 0x01, 0x00, 0x11, + 0x00, 0x8B, 0xFF, 0x53, 0x01, 0x7E, 0xFD, 0x7D, 0x03, 0xC5, 0xFC, + 0x0B, 0xFF, 0xC3, 0x40, 0x51, 0x15, 0xF4, 0xF4, 0x31, 0x06, 0xFC, + 0xFC, 0x19, 0x01, 0xCB, 0xFF, 0xFB, 0xFF, 0x00, 0x00, 0x0F, 0x00, + 0x8E, 0xFF, 0x63, 0x01, 0x22, 0xFD, 0x84, 0x04, 0x71, 0xFA, 0x34, + 0x04, 0x90, 0x42, 0x89, 0x0E, 0xBE, 0xF6, 0xCF, 0x05, 0xE1, 0xFC, + 0x4A, 0x01, 0xAB, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0x9B, 0xFF, 0x5D, 0x01, 0xEC, 0xFC, 0x5D, 0x05, 0x2F, 0xF8, + 0x19, 0x0A, 0x07, 0x43, 0x37, 0x08, 0xDD, 0xF8, 0x21, 0x05, 0xF8, + 0xFC, 0x62, 0x01, 0x96, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, + 0xB4, 0xFF, 0x3E, 0x01, 0xE4, 0xFC, 0xF6, 0x05, 0x26, 0xF6, 0x95, + 0x10, 0x26, 0x42, 0x87, 0x02, 0x28, 0xFB, 0x37, 0x04, 0x3B, 0xFD, + 0x60, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF7, 0xFF, 0xD7, + 0xFF, 0x04, 0x01, 0x0F, 0xFD, 0x3E, 0x06, 0x7D, 0xF4, 0x76, 0x17, + 0xF4, 0x3F, 0x9F, 0xFD, 0x7B, 0xFD, 0x26, 0x03, 0xA0, 0xFD, 0x4A, + 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE9, 0xFF, 0x04, 0x00, + 0xB1, 0x00, 0x71, 0xFD, 0x26, 0x06, 0x5A, 0xF3, 0x88, 0x1E, 0x87, + 0x3C, 0x98, 0xF9, 0xB3, 0xFF, 0x02, 0x02, 0x1E, 0xFE, 0x22, 0x01, + 0x93, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD7, 0xFF, 0x3A, 0x00, 0x45, + 0x00, 0x09, 0xFE, 0xA7, 0x05, 0xE1, 0xF2, 0x8D, 0x25, 0xFD, 0x37, + 0x82, 0xF6, 0xB5, 0x01, 0xDC, 0x00, 0xAA, 0xFE, 0xEE, 0x00, 0xA0, + 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC4, 0xFF, 0x76, 0x00, 0xC7, 0xFF, + 0xD3, 0xFE, 0xBC, 0x04, 0x31, 0xF3, 0x4A, 0x2C, 0x83, 0x32, 0x61, + 0xF4, 0x68, 0x03, 0xC8, 0xFF, 0x3B, 0xFF, 0xB3, 0x00, 0xB1, 0xFF, + 0x0A, 0x00, 0x0A, 0x00, 0xB1, 0xFF, 0xB3, 0x00, 0x3B, 0xFF, 0xC8, + 0xFF, 0x68, 0x03, 0x61, 0xF4, 0x83, 0x32, 0x4A, 0x2C, 0x31, 0xF3, + 0xBC, 0x04, 0xD3, 0xFE, 0xC7, 0xFF, 0x76, 0x00, 0xC4, 0xFF, 0x06, + 0x00, 0x0D, 0x00, 0xA0, 0xFF, 0xEE, 0x00, 0xAA, 0xFE, 0xDC, 0x00, + 0xB5, 0x01, 0x82, 0xF6, 0xFD, 0x37, 0x8D, 0x25, 0xE1, 0xF2, 0xA7, + 0x05, 0x09, 0xFE, 0x45, 0x00, 0x3A, 0x00, 0xD7, 0xFF, 0x04, 0x00, + 0x0F, 0x00, 0x93, 0xFF, 0x22, 0x01, 0x1E, 0xFE, 0x02, 0x02, 0xB3, + 0xFF, 0x98, 0xF9, 0x87, 0x3C, 0x88, 0x1E, 0x5A, 0xF3, 0x26, 0x06, + 0x71, 0xFD, 0xB1, 0x00, 0x04, 0x00, 0xE9, 0xFF, 0x02, 0x00, 0x11, + 0x00, 0x8B, 0xFF, 0x4A, 0x01, 0xA0, 0xFD, 0x26, 0x03, 0x7B, 0xFD, + 0x9F, 0xFD, 0xF4, 0x3F, 0x76, 0x17, 0x7D, 0xF4, 0x3E, 0x06, 0x0F, + 0xFD, 0x04, 0x01, 0xD7, 0xFF, 0xF7, 0xFF, 0x01, 0x00, 0x10, 0x00, + 0x8C, 0xFF, 0x60, 0x01, 0x3B, 0xFD, 0x37, 0x04, 0x28, 0xFB, 0x87, + 0x02, 0x26, 0x42, 0x95, 0x10, 0x26, 0xF6, 0xF6, 0x05, 0xE4, 0xFC, + 0x3E, 0x01, 0xB4, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x96, + 0xFF, 0x62, 0x01, 0xF8, 0xFC, 0x21, 0x05, 0xDD, 0xF8, 0x37, 0x08, + 0x07, 0x43, 0x19, 0x0A, 0x2F, 0xF8, 0x5D, 0x05, 0xEC, 0xFC, 0x5D, + 0x01, 0x9B, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0xAB, 0xFF, 0x4A, 0x01, 0xE1, 0xFC, 0xCF, 0x05, 0xBE, 0xF6, 0x89, + 0x0E, 0x90, 0x42, 0x34, 0x04, 0x71, 0xFA, 0x84, 0x04, 0x22, 0xFD, + 0x63, 0x01, 0x8E, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0xFF, 0xCB, + 0xFF, 0x19, 0x01, 0xFC, 0xFC, 0x31, 0x06, 0xF4, 0xF4, 0x51, 0x15, + 0xC3, 0x40, 0x0B, 0xFF, 0xC5, 0xFC, 0x7D, 0x03, 0x7E, 0xFD, 0x53, + 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xED, 0xFF, 0xF5, 0xFF, + 0xCD, 0x00, 0x4D, 0xFD, 0x38, 0x06, 0xA3, 0xF3, 0x5A, 0x1C, 0xB5, + 0x3D, 0xBC, 0xFA, 0x09, 0xFF, 0x5C, 0x02, 0xF5, 0xFD, 0x30, 0x01, + 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDD, 0xFF, 0x29, 0x00, 0x69, + 0x00, 0xD4, 0xFD, 0xD9, 0x05, 0xF3, 0xF2, 0x69, 0x23, 0x7E, 0x39, + 0x5B, 0xF7, 0x1E, 0x01, 0x36, 0x01, 0x7E, 0xFE, 0xFF, 0x00, 0x9B, + 0xFF, 0x0E, 0x00, 0x06, 0x00, 0xCA, 0xFF, 0x63, 0x00, 0xEF, 0xFF, + 0x90, 0xFE, 0x0F, 0x05, 0x02, 0xF3, 0x43, 0x2A, 0x49, 0x34, 0xEF, + 0xF4, 0xEB, 0x02, 0x1A, 0x00, 0x0E, 0xFF, 0xC6, 0x00, 0xAC, 0xFF, + 0x0B, 0x00, 0x09, 0x00, 0xB7, 0xFF, 0xA0, 0x00, 0x67, 0xFF, 0x79, + 0xFF, 0xDB, 0x03, 0xEB, 0xF3, 0xAA, 0x30, 0x45, 0x2E, 0x76, 0xF3, + 0x5E, 0x04, 0x1A, 0xFF, 0x9D, 0xFF, 0x89, 0x00, 0xBE, 0xFF, 0x07, + 0x00, 0x0C, 0x00, 0xA5, 0xFF, 0xDD, 0x00, 0xD7, 0xFE, 0x85, 0x00, + 0x44, 0x02, 0xC0, 0xF5, 0x66, 0x36, 0xAB, 0x27, 0xE3, 0xF2, 0x6A, + 0x05, 0x42, 0xFE, 0x20, 0x00, 0x4C, 0x00, 0xD1, 0xFF, 0x04, 0x00, + 0x0F, 0x00, 0x96, 0xFF, 0x13, 0x01, 0x48, 0xFE, 0xA7, 0x01, 0x58, + 0x00, 0x8B, 0xF8, 0x3C, 0x3B, 0xB4, 0x20, 0x22, 0xF3, 0x0A, 0x06, + 0x9A, 0xFD, 0x92, 0x00, 0x14, 0x00, 0xE3, 0xFF, 0x02, 0x00, 0x10, + 0x00, 0x8D, 0xFF, 0x3F, 0x01, 0xC4, 0xFD, 0xCD, 0x02, 0x2E, 0xFE, + 0x49, 0xFC, 0x06, 0x3F, 0xA1, 0x19, 0x14, 0xF4, 0x41, 0x06, 0x27, + 0xFD, 0xED, 0x00, 0xE4, 0xFF, 0xF3, 0xFF, 0x01, 0x00, 0x10, 0x00, + 0x8B, 0xFF, 0x5B, 0x01, 0x57, 0xFD, 0xE6, 0x03, 0xE0, 0xFB, 0xEE, + 0x00, 0x9C, 0x41, 0xAB, 0x12, 0x98, 0xF5, 0x16, 0x06, 0xEC, 0xFC, + 0x2F, 0x01, 0xBD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x92, + 0xFF, 0x64, 0x01, 0x09, 0xFD, 0xDE, 0x04, 0x8E, 0xF9, 0x64, 0x06, + 0xE6, 0x42, 0x0A, 0x0C, 0x87, 0xF7, 0x94, 0x05, 0xE5, 0xFC, 0x56, + 0x01, 0xA2, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0xA3, 0xFF, 0x54, 0x01, 0xE3, 0xFC, 0xA1, 0x05, 0x5E, 0xF7, 0x88, + 0x0C, 0xD9, 0x42, 0xF2, 0x05, 0xBB, 0xF9, 0xCD, 0x04, 0x0D, 0xFD, + 0x64, 0x01, 0x91, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, + 0xFF, 0x2B, 0x01, 0xEE, 0xFC, 0x1C, 0x06, 0x75, 0xF5, 0x32, 0x13, + 0x75, 0x41, 0x8B, 0x00, 0x0E, 0xFC, 0xD2, 0x03, 0x5E, 0xFD, 0x5A, + 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF2, 0xFF, 0xE7, 0xFF, + 0xE7, 0x00, 0x2E, 0xFD, 0x41, 0x06, 0xFC, 0xF3, 0x2C, 0x1A, 0xC6, + 0x3E, 0xF7, 0xFB, 0x5A, 0xFE, 0xB7, 0x02, 0xCE, 0xFD, 0x3C, 0x01, + 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x18, 0x00, 0x8A, + 0x00, 0xA5, 0xFD, 0x02, 0x06, 0x16, 0xF3, 0x3F, 0x21, 0xE6, 0x3A, + 0x4B, 0xF8, 0x81, 0x00, 0x90, 0x01, 0x53, 0xFE, 0x0F, 0x01, 0x97, + 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xD0, 0xFF, 0x50, 0x00, 0x17, 0x00, + 0x51, 0xFE, 0x59, 0x05, 0xE7, 0xF2, 0x31, 0x28, 0xFB, 0x35, 0x93, + 0xF5, 0x66, 0x02, 0x6F, 0x00, 0xE2, 0xFE, 0xD8, 0x00, 0xA6, 0xFF, + 0x0C, 0x00, 0x08, 0x00, 0xBD, 0xFF, 0x8D, 0x00, 0x92, 0xFF, 0x2D, + 0xFF, 0x45, 0x04, 0x8B, 0xF3, 0xC1, 0x2E, 0x31, 0x30, 0xD1, 0xF3, + 0xF6, 0x03, 0x65, 0xFF, 0x72, 0xFF, 0x9B, 0x00, 0xB8, 0xFF, 0x08, + 0x00, 0x0B, 0x00, 0xAA, 0xFF, 0xCA, 0x00, 0x03, 0xFF, 0x2F, 0x00, + 0xCB, 0x02, 0x16, 0xF5, 0xB8, 0x34, 0xC0, 0x29, 0xF9, 0xF2, 0x23, + 0x05, 0x80, 0xFE, 0xF9, 0xFF, 0x5E, 0x00, 0xCC, 0xFF, 0x05, 0x00, + 0x0E, 0x00, 0x9A, 0xFF, 0x03, 0x01, 0x73, 0xFE, 0x4C, 0x01, 0xF7, + 0x00, 0x94, 0xF7, 0xDB, 0x39, 0xDF, 0x22, 0xFA, 0xF2, 0xE5, 0x05, + 0xC8, 0xFD, 0x71, 0x00, 0x25, 0x00, 0xDE, 0xFF, 0x03, 0x00, 0x10, + 0x00, 0x8F, 0xFF, 0x33, 0x01, 0xEB, 0xFD, 0x73, 0x02, 0xDE, 0xFE, + 0x08, 0xFB, 0xFB, 0x3D, 0xCE, 0x1B, 0xB8, 0xF3, 0x3B, 0x06, 0x45, + 0xFD, 0xD4, 0x00, 0xF2, 0xFF, 0xEF, 0xFF, 0x01, 0x00, 0x11, 0x00, + 0x8A, 0xFF, 0x55, 0x01, 0x75, 0xFD, 0x92, 0x03, 0x97, 0xFC, 0x69, + 0xFF, 0xF2, 0x40, 0xC8, 0x14, 0x13, 0xF5, 0x2D, 0x06, 0xF8, 0xFC, + 0x1E, 0x01, 0xC8, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x8F, + 0xFF, 0x64, 0x01, 0x1D, 0xFD, 0x97, 0x04, 0x43, 0xFA, 0xA1, 0x04, + 0xA5, 0x42, 0x08, 0x0E, 0xE6, 0xF6, 0xC4, 0x05, 0xE1, 0xFC, 0x4D, + 0x01, 0xA9, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, + 0x9D, 0xFF, 0x5C, 0x01, 0xEA, 0xFC, 0x6C, 0x05, 0x05, 0xF8, 0x94, + 0x0A, 0x03, 0x43, 0xC1, 0x07, 0x09, 0xF9, 0x10, 0x05, 0xFC, 0xFC, + 0x62, 0x01, 0x95, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB6, + 0xFF, 0x3B, 0x01, 0xE5, 0xFC, 0xFF, 0x05, 0x02, 0xF6, 0x19, 0x11, + 0x06, 0x42, 0x1F, 0x02, 0x56, 0xFB, 0x23, 0x04, 0x41, 0xFD, 0x5F, + 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF6, 0xFF, 0xDA, 0xFF, + 0xFF, 0x00, 0x15, 0xFD, 0x40, 0x06, 0x62, 0xF4, 0x01, 0x18, 0xBB, + 0x3F, 0x47, 0xFD, 0xA8, 0xFD, 0x10, 0x03, 0xA9, 0xFD, 0x47, 0x01, + 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE7, 0xFF, 0x08, 0x00, 0xA9, + 0x00, 0x7B, 0xFD, 0x20, 0x06, 0x4B, 0xF3, 0x13, 0x1F, 0x36, 0x3C, + 0x52, 0xF9, 0xDD, 0xFF, 0xEB, 0x01, 0x28, 0xFE, 0x1F, 0x01, 0x93, + 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD6, 0xFF, 0x3F, 0x00, 0x3C, 0x00, + 0x16, 0xFE, 0x98, 0x05, 0xE0, 0xF2, 0x16, 0x26, 0x99, 0x37, 0x4F, + 0xF6, 0xD9, 0x01, 0xC6, 0x00, 0xB5, 0xFE, 0xEA, 0x00, 0xA1, 0xFF, + 0x0D, 0x00, 0x07, 0x00, 0xC3, 0xFF, 0x7A, 0x00, 0xBC, 0xFF, 0xE4, + 0xFE, 0xA5, 0x04, 0x41, 0xF3, 0xCA, 0x2C, 0x0E, 0x32, 0x42, 0xF4, + 0x85, 0x03, 0xB4, 0xFF, 0x46, 0xFF, 0xAE, 0x00, 0xB3, 0xFF, 0x09, + 0x00, 0x0A, 0x00, 0xB0, 0xFF, 0xB8, 0x00, 0x30, 0xFF, 0xDC, 0xFF, + 0x49, 0x03, 0x83, 0xF4, 0xF5, 0x32, 0xC9, 0x2B, 0x23, 0xF3, 0xD1, + 0x04, 0xC2, 0xFE, 0xD1, 0xFF, 0x71, 0x00, 0xC6, 0xFF, 0x06, 0x00, + 0x0D, 0x00, 0x9F, 0xFF, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, 0x90, + 0x01, 0xB6, 0xF6, 0x5F, 0x38, 0x04, 0x25, 0xE4, 0xF2, 0xB4, 0x05, + 0xFB, 0xFD, 0x4E, 0x00, 0x36, 0x00, 0xD9, 0xFF, 0x04, 0x00, 0x0F, + 0x00, 0x92, 0xFF, 0x26, 0x01, 0x13, 0xFE, 0x18, 0x02, 0x89, 0xFF, + 0xDF, 0xF9, 0xD3, 0x3C, 0xFC, 0x1D, 0x6B, 0xF3, 0x2C, 0x06, 0x67, + 0xFD, 0xB8, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0x02, 0x00, 0x11, 0x00, + 0x8B, 0xFF, 0x4C, 0x01, 0x97, 0xFD, 0x3C, 0x03, 0x4D, 0xFD, 0xF8, + 0xFD, 0x2A, 0x40, 0xED, 0x16, 0x9A, 0xF4, 0x3C, 0x06, 0x0A, 0xFD, + 0x0A, 0x01, 0xD4, 0xFF, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x8C, + 0xFF, 0x61, 0x01, 0x34, 0xFD, 0x4B, 0x04, 0xFA, 0xFA, 0xF1, 0x02, + 0x42, 0x42, 0x11, 0x10, 0x4C, 0xF6, 0xED, 0x05, 0xE3, 0xFC, 0x41, + 0x01, 0xB1, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x97, 0xFF, + 0x61, 0x01, 0xF5, 0xFC, 0x30, 0x05, 0xB1, 0xF8, 0xAE, 0x08, 0x0A, + 0x43, 0x9F, 0x09, 0x5A, 0xF8, 0x4F, 0x05, 0xEF, 0xFC, 0x5F, 0x01, + 0x9A, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xAD, + 0xFF, 0x48, 0x01, 0xE2, 0xFC, 0xDA, 0x05, 0x97, 0xF6, 0x0B, 0x0F, + 0x79, 0x42, 0xC7, 0x03, 0x9E, 0xFA, 0x71, 0x04, 0x28, 0xFD, 0x63, + 0x01, 0x8D, 0xFF, 0x0F, 0x00 +}; + +static u16 +CoefficientSizes[] = { + /* Playback */ + 0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000, + /* Record */ + 0x0020, 0x1260, 0x0020, 0x1260, 0x0000, 0x0040, 0x1260, 0x0000, +}; + +#ifndef JUST_DATA + +static u16 +nm256_getStartOffset (u8 which) +{ + u16 offset = 0; + + while (which-- > 0) + offset += CoefficientSizes[which]; + + return offset; +} + +static void +nm256_loadOneCoefficient (struct nm256_info *card, int devnum, u32 port, + u16 which) +{ + u32 coeffBuf = (which < 8) ? card->coeffBuf : card->allCoeffBuf; + u16 offset = nm256_getStartOffset (which); + u16 size = CoefficientSizes[which]; + + card->coeffsCurrent = 0; + + if (nm256_debug) + printk (KERN_INFO "NM256: Loading coefficient buffer 0x%x-0x%x with coefficient %d, size %d, port 0x%x\n", + coeffBuf, coeffBuf + size - 1, which, size, port); + nm256_writeBuffer8 (card, coefficients + offset, 1, coeffBuf, size); + nm256_writePort32 (card, 2, port + 0, coeffBuf); + /* ??? Record seems to behave differently than playback. */ + if (devnum == 0) + size--; + nm256_writePort32 (card, 2, port + 4, coeffBuf + size); +} + +static void +nm256_loadAllCoefficients (struct nm256_info *card) +{ + nm256_writeBuffer8 (card, coefficients, 1, card->allCoeffBuf, + NM_TOTAL_COEFF_COUNT * 4); + card->coeffsCurrent = 1; +} + +void +nm256_loadCoefficient (struct nm256_info *card, int which, int number) +{ + static u16 addrs[3] = { 0x1c, 0x21c, 0x408 }; + /* The enable register for the specified engine. */ + u32 poffset = (which == 1 ? 0x200 : 1); + + if (nm256_readPort8 (card, 2, poffset) & 1) { + printk (KERN_ERR "NM256: Engine was enabled while loading coefficients!\n"); + return; + } + + /* The recording engine uses coefficient values 8-15. */ + if (which == 1) + number += 8; + + if (! nm256_cachedCoefficients (card)) + nm256_loadOneCoefficient (card, which, addrs[which], number); + else { + u32 base = card->allCoeffBuf; + u32 offset = nm256_getStartOffset (number); + u32 endOffset = offset + CoefficientSizes[number]; + + if (nm256_debug) + printk (KERN_DEBUG "loading coefficient %d at port 0x%x, offset %d (0x%x-0x%x)\n", + number, addrs[which], offset, base + offset, + base + endOffset - 1); + + if (! card->coeffsCurrent) + nm256_loadAllCoefficients (card); + + nm256_writePort32 (card, 2, addrs[which], base + offset); + nm256_writePort32 (card, 2, addrs[which] + 4, base + endOffset - 1); + } +} + +#endif /* JUST_DATA */ + +#endif + +/* + * Local variables: + * c-basic-offset: 4 + * End: + */ diff -Nru a/sound/oss/opl3.c b/sound/oss/opl3.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/opl3.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,1253 @@ +/* + * sound/opl3.c + * + * A low level driver for Yamaha YM3812 and OPL-3 -chips + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Changes + * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) + * Alan Cox modularisation, fixed sound_mem allocs. + * Christoph Hellwig Adapted to module_init/module_exit + * Arnaldo C. de Melo get rid of check_region, use request_region for + * OPL4, release it on exit, some cleanups. + * + * Status + * Believed to work. Badly needs rewriting a bit to support multiple + * OPL3 devices. + */ + +#include +#include +#include + +/* + * Major improvements to the FM handling 30AUG92 by Rob Hooft, + * hooft@chem.ruu.nl + */ + +#include "sound_config.h" + +#include "opl3.h" +#include "opl3_hw.h" + +#define MAX_VOICE 18 +#define OFFS_4OP 11 + +struct voice_info +{ + unsigned char keyon_byte; + long bender; + long bender_range; + unsigned long orig_freq; + unsigned long current_freq; + int volume; + int mode; + int panning; /* 0xffff means not set */ +}; + +typedef struct opl_devinfo +{ + int base; + int left_io, right_io; + int nr_voice; + int lv_map[MAX_VOICE]; + + struct voice_info voc[MAX_VOICE]; + struct voice_alloc_info *v_alloc; + struct channel_info *chn_info; + + struct sbi_instrument i_map[SBFM_MAXINSTR]; + struct sbi_instrument *act_i[MAX_VOICE]; + + struct synth_info fm_info; + + int busy; + int model; + unsigned char cmask; + + int is_opl4; + int *osp; +} opl_devinfo; + +static struct opl_devinfo *devc = NULL; + +static int detected_model; + +static int store_instr(int instr_no, struct sbi_instrument *instr); +static void freq_to_fnum(int freq, int *block, int *fnum); +static void opl3_command(int io_addr, unsigned int addr, unsigned int val); +static int opl3_kill_note(int dev, int voice, int note, int velocity); + +static void enter_4op_mode(void) +{ + int i; + static int v4op[MAX_VOICE] = { + 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17 + }; + + devc->cmask = 0x3f; /* Connect all possible 4 OP voice operators */ + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f); + + for (i = 0; i < 3; i++) + pv_map[i].voice_mode = 4; + for (i = 3; i < 6; i++) + pv_map[i].voice_mode = 0; + + for (i = 9; i < 12; i++) + pv_map[i].voice_mode = 4; + for (i = 12; i < 15; i++) + pv_map[i].voice_mode = 0; + + for (i = 0; i < 12; i++) + devc->lv_map[i] = v4op[i]; + devc->v_alloc->max_voice = devc->nr_voice = 12; +} + +static int opl3_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + struct sbi_instrument ins; + + switch (cmd) { + case SNDCTL_FM_LOAD_INSTR: + printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); + if (copy_from_user(&ins, arg, sizeof(ins))) + return -EFAULT; + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { + printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); + return -EINVAL; + } + return store_instr(ins.channel, &ins); + + case SNDCTL_SYNTH_INFO: + devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice; + if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info))) + return -EFAULT; + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + case SNDCTL_FM_4OP_ENABLE: + if (devc->model == 2) + enter_4op_mode(); + return 0; + + default: + return -EINVAL; + } +} + +int opl3_detect(int ioaddr, int *osp) +{ + /* + * This function returns 1 if the FM chip is present at the given I/O port + * The detection algorithm plays with the timer built in the FM chip and + * looks for a change in the status register. + * + * Note! The timers of the FM chip are not connected to AdLib (and compatible) + * boards. + * + * Note2! The chip is initialized if detected. + */ + + unsigned char stat1, signature; + int i; + + if (devc != NULL) + { + printk(KERN_ERR "opl3: Only one OPL3 supported.\n"); + return 0; + } + + devc = (struct opl_devinfo *)kmalloc(sizeof(*devc), GFP_KERNEL); + + if (devc == NULL) + { + printk(KERN_ERR "opl3: Can't allocate memory for the device control " + "structure \n "); + return 0; + } + + memset(devc, 0, sizeof(*devc)); + strcpy(devc->fm_info.name, "OPL2"); + + if (!request_region(ioaddr, 4, devc->fm_info.name)) { + printk(KERN_WARNING "opl3: I/O port 0x%x already in use\n", ioaddr); + goto cleanup_devc; + } + + devc->osp = osp; + devc->base = ioaddr; + + /* Reset timers 1 and 2 */ + opl3_command(ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); + + /* Reset the IRQ of the FM chip */ + opl3_command(ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); + + signature = stat1 = inb(ioaddr); /* Status register */ + + if (signature != 0x00 && signature != 0x06 && signature != 0x02 && + signature != 0x0f) + { + MDB(printk(KERN_INFO "OPL3 not detected %x\n", signature)); + goto cleanup_region; + } + + if (signature == 0x06) /* OPL2 */ + { + detected_model = 2; + } + else if (signature == 0x00 || signature == 0x0f) /* OPL3 or OPL4 */ + { + unsigned char tmp; + + detected_model = 3; + + /* + * Detect availability of OPL4 (_experimental_). Works probably + * only after a cold boot. In addition the OPL4 port + * of the chip may not be connected to the PC bus at all. + */ + + opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0x00); + opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE); + + if ((tmp = inb(ioaddr)) == 0x02) /* Have a OPL4 */ + { + detected_model = 4; + } + + if (request_region(ioaddr - 8, 2, "OPL4")) /* OPL4 port was free */ + { + int tmp; + + outb((0x02), ioaddr - 8); /* Select OPL4 ID register */ + udelay(10); + tmp = inb(ioaddr - 7); /* Read it */ + udelay(10); + + if (tmp == 0x20) /* OPL4 should return 0x20 here */ + { + detected_model = 4; + outb((0xF8), ioaddr - 8); /* Select OPL4 FM mixer control */ + udelay(10); + outb((0x1B), ioaddr - 7); /* Write value */ + udelay(10); + } + else + { /* release OPL4 port */ + release_region(ioaddr - 8, 2); + detected_model = 3; + } + } + opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0); + } + for (i = 0; i < 9; i++) + opl3_command(ioaddr, KEYON_BLOCK + i, 0); /* + * Note off + */ + + opl3_command(ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT); + opl3_command(ioaddr, PERCOSSION_REGISTER, 0x00); /* + * Melodic mode. + */ + return 1; +cleanup_region: + release_region(ioaddr, 4); +cleanup_devc: + kfree(devc); + devc = NULL; + return 0; +} + +static int opl3_kill_note (int devno, int voice, int note, int velocity) +{ + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return 0; + + devc->v_alloc->map[voice] = 0; + + map = &pv_map[devc->lv_map[voice]]; + DEB(printk("Kill note %d\n", voice)); + + if (map->voice_mode == 0) + return 0; + + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20); + devc->voc[voice].keyon_byte = 0; + devc->voc[voice].bender = 0; + devc->voc[voice].volume = 64; + devc->voc[voice].panning = 0xffff; /* Not set */ + devc->voc[voice].bender_range = 200; + devc->voc[voice].orig_freq = 0; + devc->voc[voice].current_freq = 0; + devc->voc[voice].mode = 0; + return 0; +} + +#define HIHAT 0 +#define CYMBAL 1 +#define TOMTOM 2 +#define SNARE 3 +#define BDRUM 4 +#define UNDEFINED TOMTOM +#define DEFAULT TOMTOM + +static int store_instr(int instr_no, struct sbi_instrument *instr) +{ + if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2)) + printk(KERN_WARNING "FM warning: Invalid patch format field (key) 0x%x\n", instr->key); + memcpy((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof(*instr)); + return 0; +} + +static int opl3_set_instr (int dev, int voice, int instr_no) +{ + if (voice < 0 || voice >= devc->nr_voice) + return 0; + if (instr_no < 0 || instr_no >= SBFM_MAXINSTR) + instr_no = 0; /* Acoustic piano (usually) */ + + devc->act_i[voice] = &devc->i_map[instr_no]; + return 0; +} + +/* + * The next table looks magical, but it certainly is not. Its values have + * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception + * for i=0. This log-table converts a linear volume-scaling (0..127) to a + * logarithmic scaling as present in the FM-synthesizer chips. so : Volume + * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative + * volume -8 it was implemented as a table because it is only 128 bytes and + * it saves a lot of log() calculations. (RH) + */ + +static char fm_volume_table[128] = +{ + -64, -48, -40, -35, -32, -29, -27, -26, + -24, -23, -21, -20, -19, -18, -18, -17, + -16, -15, -15, -14, -13, -13, -12, -12, + -11, -11, -10, -10, -10, -9, -9, -8, + -8, -8, -7, -7, -7, -6, -6, -6, + -5, -5, -5, -5, -4, -4, -4, -4, + -3, -3, -3, -3, -2, -2, -2, -2, + -2, -1, -1, -1, -1, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8 +}; + +static void calc_vol(unsigned char *regbyte, int volume, int main_vol) +{ + int level = (~*regbyte & 0x3f); + + if (main_vol > 127) + main_vol = 127; + volume = (volume * main_vol) / 127; + + if (level) + level += fm_volume_table[volume]; + + if (level > 0x3f) + level = 0x3f; + if (level < 0) + level = 0; + + *regbyte = (*regbyte & 0xc0) | (~level & 0x3f); +} + +static void set_voice_volume(int voice, int volume, int main_vol) +{ + unsigned char vol1, vol2, vol3, vol4; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return; + + map = &pv_map[devc->lv_map[voice]]; + instr = devc->act_i[voice]; + + if (!instr) + instr = &devc->i_map[0]; + + if (instr->channel < 0) + return; + + if (devc->voc[voice].mode == 0) + return; + + if (devc->voc[voice].mode == 2) + { + vol1 = instr->operators[2]; + vol2 = instr->operators[3]; + if ((instr->operators[10] & 0x01)) + { + calc_vol(&vol1, volume, main_vol); + calc_vol(&vol2, volume, main_vol); + } + else + { + calc_vol(&vol2, volume, main_vol); + } + opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2); + } + else + { /* + * 4 OP voice + */ + int connection; + + vol1 = instr->operators[2]; + vol2 = instr->operators[3]; + vol3 = instr->operators[OFFS_4OP + 2]; + vol4 = instr->operators[OFFS_4OP + 3]; + + /* + * The connection method for 4 OP devc->voc is defined by the rightmost + * bits at the offsets 10 and 10+OFFS_4OP + */ + + connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + + switch (connection) + { + case 0: + calc_vol(&vol4, volume, main_vol); + break; + + case 1: + calc_vol(&vol2, volume, main_vol); + calc_vol(&vol4, volume, main_vol); + break; + + case 2: + calc_vol(&vol1, volume, main_vol); + calc_vol(&vol4, volume, main_vol); + break; + + case 3: + calc_vol(&vol1, volume, main_vol); + calc_vol(&vol3, volume, main_vol); + calc_vol(&vol4, volume, main_vol); + break; + + default: + ; + } + opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], vol3); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], vol4); + } +} + +static int opl3_start_note (int dev, int voice, int note, int volume) +{ + unsigned char data, fpc; + int block, fnum, freq, voice_mode, pan; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return 0; + + map = &pv_map[devc->lv_map[voice]]; + pan = devc->voc[voice].panning; + + if (map->voice_mode == 0) + return 0; + + if (note == 255) /* + * Just change the volume + */ + { + set_voice_volume(voice, volume, devc->voc[voice].volume); + return 0; + } + + /* + * Kill previous note before playing + */ + + opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* + * Carrier + * volume to + * min + */ + opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* + * Modulator + * volume to + */ + + if (map->voice_mode == 4) + { + opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff); + } + + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* + * Note + * off + */ + + instr = devc->act_i[voice]; + + if (!instr) + instr = &devc->i_map[0]; + + if (instr->channel < 0) + { + printk(KERN_WARNING "opl3: Initializing voice %d with undefined instrument\n", voice); + return 0; + } + + if (map->voice_mode == 2 && instr->key == OPL3_PATCH) + return 0; /* + * Cannot play + */ + + voice_mode = map->voice_mode; + + if (voice_mode == 4) + { + int voice_shift; + + voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3; + voice_shift += map->voice_num; + + if (instr->key != OPL3_PATCH) /* + * Just 2 OP patch + */ + { + voice_mode = 2; + devc->cmask &= ~(1 << voice_shift); + } + else + { + devc->cmask |= (1 << voice_shift); + } + + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); + } + + /* + * Set Sound Characteristics + */ + + opl3_command(map->ioaddr, AM_VIB + map->op[0], instr->operators[0]); + opl3_command(map->ioaddr, AM_VIB + map->op[1], instr->operators[1]); + + /* + * Set Attack/Decay + */ + + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]); + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]); + + /* + * Set Sustain/Release + */ + + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]); + + /* + * Set Wave Select + */ + + opl3_command(map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]); + opl3_command(map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]); + + /* + * Set Feedback/Connection + */ + + fpc = instr->operators[10]; + + if (pan != 0xffff) + { + fpc &= ~STEREO_BITS; + if (pan < -64) + fpc |= VOICE_TO_LEFT; + else + if (pan > 64) + fpc |= VOICE_TO_RIGHT; + else + fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT); + } + + if (!(fpc & 0x30)) + fpc |= 0x30; /* + * Ensure that at least one chn is enabled + */ + opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc); + + /* + * If the voice is a 4 OP one, initialize the operators 3 and 4 also + */ + + if (voice_mode == 4) + { + /* + * Set Sound Characteristics + */ + + opl3_command(map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); + opl3_command(map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]); + + /* + * Set Attack/Decay + */ + + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]); + + /* + * Set Sustain/Release + */ + + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]); + + /* + * Set Wave Select + */ + + opl3_command(map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); + opl3_command(map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]); + + /* + * Set Feedback/Connection + */ + + fpc = instr->operators[OFFS_4OP + 10]; + if (!(fpc & 0x30)) + fpc |= 0x30; /* + * Ensure that at least one chn is enabled + */ + opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc); + } + + devc->voc[voice].mode = voice_mode; + set_voice_volume(voice, volume, devc->voc[voice].volume); + + freq = devc->voc[voice].orig_freq = note_to_freq(note) / 1000; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0); + devc->voc[voice].current_freq = freq; + + freq_to_fnum(freq, &block, &fnum); + + /* + * Play note + */ + + data = fnum & 0xff; /* + * Least significant bits of fnumber + */ + opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); + + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); + devc->voc[voice].keyon_byte = data; + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); + if (voice_mode == 4) + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); + + return 0; +} + +static void freq_to_fnum (int freq, int *block, int *fnum) +{ + int f, octave; + + /* + * Converts the note frequency to block and fnum values for the FM chip + */ + /* + * First try to compute the block -value (octave) where the note belongs + */ + + f = freq; + + octave = 5; + + if (f == 0) + octave = 0; + else if (f < 261) + { + while (f < 261) + { + octave--; + f <<= 1; + } + } + else if (f > 493) + { + while (f > 493) + { + octave++; + f >>= 1; + } + } + + if (octave > 7) + octave = 7; + + *fnum = freq * (1 << (20 - octave)) / 49716; + *block = octave; +} + +static void opl3_command (int io_addr, unsigned int addr, unsigned int val) +{ + int i; + + /* + * The original 2-OP synth requires a quite long delay after writing to a + * register. The OPL-3 survives with just two INBs + */ + + outb(((unsigned char) (addr & 0xff)), io_addr); + + if (devc->model != 2) + udelay(10); + else + for (i = 0; i < 2; i++) + inb(io_addr); + + outb(((unsigned char) (val & 0xff)), io_addr + 1); + + if (devc->model != 2) + udelay(30); + else + for (i = 0; i < 2; i++) + inb(io_addr); +} + +static void opl3_reset(int devno) +{ + int i; + + for (i = 0; i < 18; i++) + devc->lv_map[i] = i; + + for (i = 0; i < devc->nr_voice; i++) + { + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff); + + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff); + + if (pv_map[devc->lv_map[i]].voice_mode == 4) + { + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff); + + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff); + } + + opl3_kill_note(devno, i, 0, 64); + } + + if (devc->model == 2) + { + devc->v_alloc->max_voice = devc->nr_voice = 18; + + for (i = 0; i < 18; i++) + pv_map[i].voice_mode = 2; + + } +} + +static int opl3_open(int dev, int mode) +{ + int i; + + if (devc->busy) + return -EBUSY; + devc->busy = 1; + + devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; + devc->v_alloc->timestamp = 0; + + for (i = 0; i < 18; i++) + { + devc->v_alloc->map[i] = 0; + devc->v_alloc->alloc_times[i] = 0; + } + + devc->cmask = 0x00; /* + * Just 2 OP mode + */ + if (devc->model == 2) + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); + return 0; +} + +static void opl3_close(int dev) +{ + devc->busy = 0; + devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; + + devc->fm_info.nr_drums = 0; + devc->fm_info.perc_mode = 0; + + opl3_reset(dev); +} + +static void opl3_hw_control(int dev, unsigned char *event) +{ +} + +static int opl3_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct sbi_instrument ins; + + if (count = SBFM_MAXINSTR) + { + printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); + return -EINVAL; + } + ins.key = format; + + return store_instr(ins.channel, &ins); +} + +static void opl3_panning(int dev, int voice, int value) +{ + devc->voc[voice].panning = value; +} + +static void opl3_volume_method(int dev, int mode) +{ +} + +#define SET_VIBRATO(cell) { \ + tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ + if (pressure > 110) \ + tmp |= 0x40; /* Vibrato on */ \ + opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);} + +static void opl3_aftertouch(int dev, int voice, int pressure) +{ + int tmp; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return; + + map = &pv_map[devc->lv_map[voice]]; + + DEB(printk("Aftertouch %d\n", voice)); + + if (map->voice_mode == 0) + return; + + /* + * Adjust the amount of vibrato depending the pressure + */ + + instr = devc->act_i[voice]; + + if (!instr) + instr = &devc->i_map[0]; + + if (devc->voc[voice].mode == 4) + { + int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + + switch (connection) + { + case 0: + SET_VIBRATO(4); + break; + + case 1: + SET_VIBRATO(2); + SET_VIBRATO(4); + break; + + case 2: + SET_VIBRATO(1); + SET_VIBRATO(4); + break; + + case 3: + SET_VIBRATO(1); + SET_VIBRATO(3); + SET_VIBRATO(4); + break; + + } + /* + * Not implemented yet + */ + } + else + { + SET_VIBRATO(1); + + if ((instr->operators[10] & 0x01)) /* + * Additive synthesis + */ + SET_VIBRATO(2); + } +} + +#undef SET_VIBRATO + +static void bend_pitch(int dev, int voice, int value) +{ + unsigned char data; + int block, fnum, freq; + struct physical_voice_info *map; + + map = &pv_map[devc->lv_map[voice]]; + + if (map->voice_mode == 0) + return; + + devc->voc[voice].bender = value; + if (!value) + return; + if (!(devc->voc[voice].keyon_byte & 0x20)) + return; /* + * Not keyed on + */ + + freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0); + devc->voc[voice].current_freq = freq; + + freq_to_fnum(freq, &block, &fnum); + + data = fnum & 0xff; /* + * Least significant bits of fnumber + */ + opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); + + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); + devc->voc[voice].keyon_byte = data; + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); +} + +static void opl3_controller (int dev, int voice, int ctrl_num, int value) +{ + if (voice < 0 || voice >= devc->nr_voice) + return; + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + bend_pitch(dev, voice, value); + break; + + case CTRL_PITCH_BENDER_RANGE: + devc->voc[voice].bender_range = value; + break; + + case CTL_MAIN_VOLUME: + devc->voc[voice].volume = value / 128; + break; + + case CTL_PAN: + devc->voc[voice].panning = (value * 2) - 128; + break; + } +} + +static void opl3_bender(int dev, int voice, int value) +{ + if (voice < 0 || voice >= devc->nr_voice) + return; + + bend_pitch(dev, voice, value - 8192); +} + +static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + int i, p, best, first, avail, best_time = 0x7fffffff; + struct sbi_instrument *instr; + int is4op; + int instr_no; + + if (chn < 0 || chn > 15) + instr_no = 0; + else + instr_no = devc->chn_info[chn].pgm_num; + + instr = &devc->i_map[instr_no]; + if (instr->channel < 0 || /* Instrument not loaded */ + devc->nr_voice != 12) /* Not in 4 OP mode */ + is4op = 0; + else if (devc->nr_voice == 12) /* 4 OP mode */ + is4op = (instr->key == OPL3_PATCH); + else + is4op = 0; + + if (is4op) + { + first = p = 0; + avail = 6; + } + else + { + if (devc->nr_voice == 12) /* 4 OP mode. Use the '2 OP only' operators first */ + first = p = 6; + else + first = p = 0; + avail = devc->nr_voice; + } + + /* + * Now try to find a free voice + */ + best = first; + + for (i = 0; i < avail; i++) + { + if (alloc->map[p] == 0) + { + return p; + } + if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */ + { + best_time = alloc->alloc_times[p]; + best = p; + } + p = (p + 1) % avail; + } + + /* + * Insert some kind of priority mechanism here. + */ + + if (best < 0) + best = 0; + if (best > devc->nr_voice) + best -= devc->nr_voice; + + return best; /* All devc->voc in use. Select the first one. */ +} + +static void opl3_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info = + &synth_devs[dev]->chn_info[chn]; + + opl3_set_instr(dev, voice, info->pgm_num); + + devc->voc[voice].bender = 0; + devc->voc[voice].bender_range = info->bender_range; + devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME]; + devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; +} + +static struct synth_operations opl3_operations = +{ + owner: THIS_MODULE, + id: "OPL", + info: NULL, + midi_dev: 0, + synth_type: SYNTH_TYPE_FM, + synth_subtype: FM_TYPE_ADLIB, + open: opl3_open, + close: opl3_close, + ioctl: opl3_ioctl, + kill_note: opl3_kill_note, + start_note: opl3_start_note, + set_instr: opl3_set_instr, + reset: opl3_reset, + hw_control: opl3_hw_control, + load_patch: opl3_load_patch, + aftertouch: opl3_aftertouch, + controller: opl3_controller, + panning: opl3_panning, + volume_method: opl3_volume_method, + bender: opl3_bender, + alloc_voice: opl3_alloc_voice, + setup_voice: opl3_setup_voice +}; + +int opl3_init(int ioaddr, int *osp, struct module *owner) +{ + int i; + int me; + + if (devc == NULL) + { + printk(KERN_ERR "opl3: Device control structure not initialized.\n"); + return -1; + } + + if ((me = sound_alloc_synthdev()) == -1) + { + printk(KERN_WARNING "opl3: Too many synthesizers\n"); + return -1; + } + + devc->nr_voice = 9; + + devc->fm_info.device = 0; + devc->fm_info.synth_type = SYNTH_TYPE_FM; + devc->fm_info.synth_subtype = FM_TYPE_ADLIB; + devc->fm_info.perc_mode = 0; + devc->fm_info.nr_voices = 9; + devc->fm_info.nr_drums = 0; + devc->fm_info.instr_bank_size = SBFM_MAXINSTR; + devc->fm_info.capabilities = 0; + devc->left_io = ioaddr; + devc->right_io = ioaddr + 2; + + if (detected_model <= 2) + devc->model = 1; + else + { + devc->model = 2; + if (detected_model == 4) + devc->is_opl4 = 1; + } + + opl3_operations.info = &devc->fm_info; + + synth_devs[me] = &opl3_operations; + + if (owner) + synth_devs[me]->owner = owner; + + sequencer_init(); + devc->v_alloc = &opl3_operations.alloc; + devc->chn_info = &opl3_operations.chn_info[0]; + + if (devc->model == 2) + { + if (devc->is_opl4) + strcpy(devc->fm_info.name, "Yamaha OPL4/OPL3 FM"); + else + strcpy(devc->fm_info.name, "Yamaha OPL3"); + + devc->v_alloc->max_voice = devc->nr_voice = 18; + devc->fm_info.nr_drums = 0; + devc->fm_info.synth_subtype = FM_TYPE_OPL3; + devc->fm_info.capabilities |= SYNTH_CAP_OPL3; + + for (i = 0; i < 18; i++) + { + if (pv_map[i].ioaddr == USE_LEFT) + pv_map[i].ioaddr = devc->left_io; + else + pv_map[i].ioaddr = devc->right_io; + } + opl3_command(devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE); + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x00); + } + else + { + strcpy(devc->fm_info.name, "Yamaha OPL2"); + devc->v_alloc->max_voice = devc->nr_voice = 9; + devc->fm_info.nr_drums = 0; + + for (i = 0; i < 18; i++) + pv_map[i].ioaddr = devc->left_io; + }; + conf_printf2(devc->fm_info.name, ioaddr, 0, -1, -1); + + for (i = 0; i < SBFM_MAXINSTR; i++) + devc->i_map[i].channel = -1; + + return me; +} + +EXPORT_SYMBOL(opl3_init); +EXPORT_SYMBOL(opl3_detect); + +static int me; + +static int io = -1; + +MODULE_PARM(io, "i"); + +static int __init init_opl3 (void) +{ + printk(KERN_INFO "YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996\n"); + + if (io != -1) /* User loading pure OPL3 module */ + { + if (!opl3_detect(io, NULL)) + { + return -ENODEV; + } + + me = opl3_init(io, NULL, THIS_MODULE); + } + + return 0; +} + +static void __exit cleanup_opl3(void) +{ + if (devc && io != -1) + { + if (devc->base) { + release_region(devc->base,4); + if (devc->is_opl4) + release_region(devc->base - 8, 2); + } + kfree(devc); + devc = NULL; + sound_unload_synthdev(me); + } +} + +module_init(init_opl3); +module_exit(cleanup_opl3); + +#ifndef MODULE +static int __init setup_opl3(char *str) +{ + /* io */ + int ints[2]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + + return 1; +} + +__setup("opl3=", setup_opl3); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/opl3.h b/sound/oss/opl3.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/opl3.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,11 @@ +/* + * opl3.h + * + * Copyright: Christoph Hellwig + * + */ + +int opl3_detect (int ioaddr, int *osp); +int opl3_init(int ioaddr, int *osp, struct module *owner); + +void enable_opl3_mode(int left, int right, int both); diff -Nru a/sound/oss/opl3_hw.h b/sound/oss/opl3_hw.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/opl3_hw.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,246 @@ +/* + * opl3_hw.h - Definitions of the OPL-3 registers + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * The OPL-3 mode is switched on by writing 0x01, to the offset 5 + * of the right side. + * + * Another special register at the right side is at offset 4. It contains + * a bit mask defining which voices are used as 4 OP voices. + * + * The percussive mode is implemented in the left side only. + * + * With the above exceptions the both sides can be operated independently. + * + * A 4 OP voice can be created by setting the corresponding + * bit at offset 4 of the right side. + * + * For example setting the rightmost bit (0x01) changes the + * first voice on the right side to the 4 OP mode. The fourth + * voice is made inaccessible. + * + * If a voice is set to the 2 OP mode, it works like 2 OP modes + * of the original YM3812 (AdLib). In addition the voice can + * be connected the left, right or both stereo channels. It can + * even be left unconnected. This works with 4 OP voices also. + * + * The stereo connection bits are located in the FEEDBACK_CONNECTION + * register of the voice (0xC0-0xC8). In 4 OP voices these bits are + * in the second half of the voice. + */ + +/* + * Register numbers for the global registers + */ + +#define TEST_REGISTER 0x01 +#define ENABLE_WAVE_SELECT 0x20 + +#define TIMER1_REGISTER 0x02 +#define TIMER2_REGISTER 0x03 +#define TIMER_CONTROL_REGISTER 0x04 /* Left side */ +#define IRQ_RESET 0x80 +#define TIMER1_MASK 0x40 +#define TIMER2_MASK 0x20 +#define TIMER1_START 0x01 +#define TIMER2_START 0x02 + +#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */ +#define RIGHT_4OP_0 0x01 +#define RIGHT_4OP_1 0x02 +#define RIGHT_4OP_2 0x04 +#define LEFT_4OP_0 0x08 +#define LEFT_4OP_1 0x10 +#define LEFT_4OP_2 0x20 + +#define OPL3_MODE_REGISTER 0x05 /* Right side */ +#define OPL3_ENABLE 0x01 +#define OPL4_ENABLE 0x02 + +#define KBD_SPLIT_REGISTER 0x08 /* Left side */ +#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ +#define KEYBOARD_SPLIT 0x40 + +#define PERCOSSION_REGISTER 0xbd /* Left side only */ +#define TREMOLO_DEPTH 0x80 +#define VIBRATO_DEPTH 0x40 +#define PERCOSSION_ENABLE 0x20 +#define BASSDRUM_ON 0x10 +#define SNAREDRUM_ON 0x08 +#define TOMTOM_ON 0x04 +#define CYMBAL_ON 0x02 +#define HIHAT_ON 0x01 + +/* + * Offsets to the register banks for operators. To get the + * register number just add the operator offset to the bank offset + * + * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) + */ +#define AM_VIB 0x20 +#define TREMOLO_ON 0x80 +#define VIBRATO_ON 0x40 +#define SUSTAIN_ON 0x20 +#define KSR 0x10 /* Key scaling rate */ +#define MULTIPLE_MASK 0x0f /* Frequency multiplier */ + + /* + * KSL/Total level (0x40 to 0x55) + */ +#define KSL_LEVEL 0x40 +#define KSL_MASK 0xc0 /* Envelope scaling bits */ +#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ + +/* + * Attack / Decay rate (0x60 to 0x75) + */ +#define ATTACK_DECAY 0x60 +#define ATTACK_MASK 0xf0 +#define DECAY_MASK 0x0f + +/* + * Sustain level / Release rate (0x80 to 0x95) + */ +#define SUSTAIN_RELEASE 0x80 +#define SUSTAIN_MASK 0xf0 +#define RELEASE_MASK 0x0f + +/* + * Wave select (0xE0 to 0xF5) + */ +#define WAVE_SELECT 0xe0 + +/* + * Offsets to the register banks for voices. Just add to the + * voice number to get the register number. + * + * F-Number low bits (0xA0 to 0xA8). + */ +#define FNUM_LOW 0xa0 + +/* + * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) + */ +#define KEYON_BLOCK 0xb0 +#define KEYON_BIT 0x20 +#define BLOCKNUM_MASK 0x1c +#define FNUM_HIGH_MASK 0x03 + +/* + * Feedback / Connection (0xc0 to 0xc8) + * + * These registers have two new bits when the OPL-3 mode + * is selected. These bits controls connecting the voice + * to the stereo channels. For 4 OP voices this bit is + * defined in the second half of the voice (add 3 to the + * register offset). + * + * For 4 OP voices the connection bit is used in the + * both halves (gives 4 ways to connect the operators). + */ +#define FEEDBACK_CONNECTION 0xc0 +#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ +#define CONNECTION_BIT 0x01 +/* + * In the 4 OP mode there is four possible configurations how the + * operators can be connected together (in 2 OP modes there is just + * AM or FM). The 4 OP connection mode is defined by the rightmost + * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves. + * + * First half Second half Mode + * + * +---+ + * v | + * 0 0 >+-1-+--2--3--4--> + * + * + * + * +---+ + * | | + * 0 1 >+-1-+--2-+ + * |-> + * >--3----4-+ + * + * +---+ + * | | + * 1 0 >+-1-+-----+ + * |-> + * >--2--3--4-+ + * + * +---+ + * | | + * 1 1 >+-1-+--+ + * | + * >--2--3-+-> + * | + * >--4----+ + */ +#define STEREO_BITS 0x30 /* OPL-3 only */ +#define VOICE_TO_LEFT 0x10 +#define VOICE_TO_RIGHT 0x20 + +/* + * Definition table for the physical voices + */ + +struct physical_voice_info { + unsigned char voice_num; + unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */ + unsigned short ioaddr; /* I/O port (left or right side) */ + unsigned char op[4]; /* Operator offsets */ + }; + +/* + * There is 18 possible 2 OP voices + * (9 in the left and 9 in the right). + * The first OP is the modulator and 2nd is the carrier. + * + * The first three voices in the both sides may be connected + * with another voice to a 4 OP voice. For example voice 0 + * can be connected with voice 3. The operators of voice 3 are + * used as operators 3 and 4 of the new 4 OP voice. + * In this case the 2 OP voice number 0 is the 'first half' and + * voice 3 is the second. + */ + +#define USE_LEFT 0 +#define USE_RIGHT 1 + +static struct physical_voice_info pv_map[18] = +{ +/* No Mode Side OP1 OP2 OP3 OP4 */ +/* --------------------------------------------------- */ + { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}}, + { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}}, + { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}}, + + { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}}, + { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}}, + { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}}, + + { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */ + { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */ + { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */ + + { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}}, + { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}}, + { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}}, + + { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}}, + { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}}, + { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}}, + + { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}}, + { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}}, + { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}} +}; +/* + * DMA buffer calls + */ diff -Nru a/sound/oss/opl3sa.c b/sound/oss/opl3sa.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/opl3sa.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,337 @@ +/* + * sound/opl3sa.c + * + * Low level driver for Yamaha YMF701B aka OPL3-SA chip + * + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Alan Cox Modularisation + * Christoph Hellwig Adapted to module_init/module_exit + * Arnaldo C. de Melo got rid of attach_uart401 + * + * FIXME: + * Check for install of mpu etc is wrong, should check result of the mss stuff + */ + +#include +#include + +#undef SB_OK + +#include "sound_config.h" + +#include "ad1848.h" +#include "mpu401.h" + +#ifdef SB_OK +#include "sb.h" +static int sb_initialized = 0; +#endif + +static int kilroy_was_here = 0; /* Don't detect twice */ +static int mpu_initialized = 0; + +static int *opl3sa_osp = NULL; + +static unsigned char opl3sa_read(int addr) +{ + unsigned long flags; + unsigned char tmp; + + save_flags(flags); + cli(); + outb((0x1d), 0xf86); /* password */ + outb(((unsigned char) addr), 0xf86); /* address */ + tmp = inb(0xf87); /* data */ + restore_flags(flags); + + return tmp; +} + +static void opl3sa_write(int addr, int data) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outb((0x1d), 0xf86); /* password */ + outb(((unsigned char) addr), 0xf86); /* address */ + outb(((unsigned char) data), 0xf87); /* data */ + restore_flags(flags); +} + +static int __init opl3sa_detect(void) +{ + int tmp; + + if (((tmp = opl3sa_read(0x01)) & 0xc4) != 0x04) + { + DDB(printk("OPL3-SA detect error 1 (%x)\n", opl3sa_read(0x01))); + /* return 0; */ + } + + /* + * Check that the password feature has any effect + */ + + if (inb(0xf87) == tmp) + { + DDB(printk("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb(0xf87))); + return 0; + } + tmp = (opl3sa_read(0x04) & 0xe0) >> 5; + + if (tmp != 0 && tmp != 1) + { + DDB(printk("OPL3-SA detect failed 3 (%d)\n", tmp)); + return 0; + } + DDB(printk("OPL3-SA mode %x detected\n", tmp)); + + opl3sa_write(0x01, 0x00); /* Disable MSS */ + opl3sa_write(0x02, 0x00); /* Disable SB */ + opl3sa_write(0x03, 0x00); /* Disable MPU */ + + return 1; +} + +/* + * Probe and attach routines for the Windows Sound System mode of + * OPL3-SA + */ + +static int __init probe_opl3sa_wss(struct address_info *hw_config) +{ + int ret; + unsigned char tmp = 0x24; /* WSS enable */ + + if (check_region(0xf86, 2)) /* Control port is busy */ + return 0; + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (OPL3-SA for example) + * return 0x00. + */ + + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "OPL3-SA: MSS I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + opl3sa_osp = hw_config->osp; + + if (!opl3sa_detect()) + { + printk(KERN_ERR "OSS: OPL3-SA chip not found\n"); + return 0; + } + + switch (hw_config->io_base) + { + case 0x530: + tmp |= 0x00; + break; + case 0xe80: + tmp |= 0x08; + break; + case 0xf40: + tmp |= 0x10; + break; + case 0x604: + tmp |= 0x18; + break; + default: + printk(KERN_ERR "OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base); + return 0; + } + + opl3sa_write(0x01, tmp); /* WSS setup register */ + kilroy_was_here = 1; + + ret = probe_ms_sound(hw_config); + if (ret) + request_region(0xf86, 2, "OPL3-SA"); + + return ret; +} + +static void __init attach_opl3sa_wss(struct address_info *hw_config) +{ + int nm = num_mixers; + + /* FIXME */ + attach_ms_sound(hw_config, THIS_MODULE); + if (num_mixers > nm) /* A mixer was installed */ + { + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } +} + + +static int __init probe_opl3sa_mpu(struct address_info *hw_config) +{ + unsigned char conf; + static signed char irq_bits[] = { + -1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4 + }; + + if (!kilroy_was_here) + return 0; /* OPL3-SA has not been detected earlier */ + + if (mpu_initialized) + { + DDB(printk("OPL3-SA: MPU mode already initialized\n")); + return 0; + } + if (hw_config->irq > 10) + { + printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + if (irq_bits[hw_config->irq] == -1) + { + printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + switch (hw_config->io_base) + { + case 0x330: + conf = 0x00; + break; + case 0x332: + conf = 0x20; + break; + case 0x334: + conf = 0x40; + break; + case 0x300: + conf = 0x60; + break; + default: + return 0; /* Invalid port */ + } + + conf |= 0x83; /* MPU & OPL3 (synth) & game port enable */ + conf |= irq_bits[hw_config->irq] << 2; + + opl3sa_write(0x03, conf); + + mpu_initialized = 1; + hw_config->name = "OPL3-SA (MPU401)"; + + return probe_uart401(hw_config, THIS_MODULE); +} + +static void __exit unload_opl3sa_wss(struct address_info *hw_config) +{ + int dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = hw_config->dma; + + release_region(0xf86, 2); + release_region(hw_config->io_base, 4); + + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + dma2, + 0); + sound_unload_audiodev(hw_config->slots[0]); +} + +static inline void __exit unload_opl3sa_mpu(struct address_info *hw_config) +{ + unload_uart401(hw_config); +} + +#ifdef SB_OK +static inline void __exit unload_opl3sa_sb(struct address_info *hw_config) +{ + sb_dsp_unload(hw_config); +} +#endif + +static int found_mpu; + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata mpu_io = -1; +static int __initdata mpu_irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(mpu_io,"i"); +MODULE_PARM(mpu_irq,"i"); + +static int __init init_opl3sa(void) +{ + if (io == -1 || irq == -1 || dma == -1) { + printk(KERN_ERR "opl3sa: dma, irq and io must be set.\n"); + return -EINVAL; + } + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + cfg_mpu.io_base = mpu_io; + cfg_mpu.irq = mpu_irq; + + if (probe_opl3sa_wss(&cfg) == 0) + return -ENODEV; + + found_mpu=probe_opl3sa_mpu(&cfg_mpu); + + attach_opl3sa_wss(&cfg); + return 0; +} + +static void __exit cleanup_opl3sa(void) +{ + if(found_mpu) + unload_opl3sa_mpu(&cfg_mpu); + unload_opl3sa_wss(&cfg); +} + +module_init(init_opl3sa); +module_exit(cleanup_opl3sa); + +#ifndef MODULE +static int __init setup_opl3sa(char *str) +{ + /* io, irq, dma, dma2, mpu_io, mpu_irq */ + int ints[7]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + mpu_io = ints[5]; + mpu_irq = ints[6]; + + return 1; +} + +__setup("opl3sa=", setup_opl3sa); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/opl3sa2.c b/sound/oss/opl3sa2.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/opl3sa2.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,1190 @@ +/* + * sound/opl3sa2.c + * + * A low level driver for Yamaha OPL3-SA2 and SA3 cards. + * NOTE: All traces of the name OPL3-SAx have now (December 2000) been + * removed from the driver code, as an email exchange with Yamaha + * provided the information that the YMF-719 is indeed just a + * re-badged 715. + * + * Copyright 1998-2001 Scott Murray + * + * Originally based on the CS4232 driver (in cs4232.c) by Hannu Savolainen + * and others. Now incorporates code/ideas from pss.c, also by Hannu + * Savolainen. Both of those files are distributed with the following + * license: + * + * "Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info." + * + * As such, in accordance with the above license, this file, opl3sa2.c, is + * distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2 (June 1991). + * See the "COPYING" file distributed with this software for more information. + * + * Change History + * -------------- + * Scott Murray Original driver (Jun 14, 1998) + * Paul J.Y. Lahaie Changed probing / attach code order + * Scott Murray Added mixer support (Dec 03, 1998) + * Scott Murray Changed detection code to be more forgiving, + * added force option as last resort, + * fixed ioctl return values. (Dec 30, 1998) + * Scott Murray Simpler detection code should work all the time now + * (with thanks to Ben Hutchings for the heuristic), + * removed now unnecessary force option. (Jan 5, 1999) + * Christoph Hellwig Adapted to module_init/module_exit (Mar 4, 2000) + * Scott Murray Reworked SA2 versus SA3 mixer code, updated chipset + * version detection code (again!). (Dec 5, 2000) + * Scott Murray Adjusted master volume mixer scaling. (Dec 6, 2000) + * Scott Murray Based on a patch by Joel Yliluoma (aka Bisqwit), + * integrated wide mixer and adjusted mic, bass, treble + * scaling. (Dec 6, 2000) + * Scott Murray Based on a patch by Peter Englmaier, integrated + * ymode and loopback options. (Dec 6, 2000) + * Scott Murray Inspired by a patch by Peter Englmaier, and based on + * what ALSA does, added initialization code for the + * default DMA and IRQ settings. (Dec 6, 2000) + * Scott Murray Added some more checks to the card detection code, + * based on what ALSA does. (Dec 12, 2000) + * Scott Murray Inspired by similar patches from John Fremlin, + * Jim Radford, Mike Rolig, and Ingmar Steen, added 2.4 + * ISA PnP API support, mainly based on bits from + * sb_card.c and awe_wave.c. (Dec 12, 2000) + * Scott Murray Some small cleanups to the init code output. + * (Jan 7, 2001) + * Zwane Mwaikambo Added PM support. (Dec 4 2001) + * + */ + +#include +#include +#include +#include +#include +#include +#include "sound_config.h" + +#include "ad1848.h" +#include "mpu401.h" + +/* Useful control port indexes: */ +#define OPL3SA2_PM 0x01 +#define OPL3SA2_SYS_CTRL 0x02 +#define OPL3SA2_IRQ_CONFIG 0x03 +#define OPL3SA2_DMA_CONFIG 0x06 +#define OPL3SA2_MASTER_LEFT 0x07 +#define OPL3SA2_MASTER_RIGHT 0x08 +#define OPL3SA2_MIC 0x09 +#define OPL3SA2_MISC 0x0A + +#define OPL3SA3_WIDE 0x14 +#define OPL3SA3_BASS 0x15 +#define OPL3SA3_TREBLE 0x16 + +/* Useful constants: */ +#define DEFAULT_VOLUME 50 +#define DEFAULT_MIC 50 +#define DEFAULT_TIMBRE 0 + +/* Power saving modes */ +#define OPL3SA2_PM_MODE1 0x05 +#define OPL3SA2_PM_MODE2 0x04 +#define OPL3SA2_PM_MODE3 0x03 + +/* For checking against what the card returns: */ +#define VERSION_UNKNOWN 0 +#define VERSION_YMF711 1 +#define VERSION_YMF715 2 +#define VERSION_YMF715B 3 +#define VERSION_YMF715E 4 +/* also assuming that anything > 4 but <= 7 is a 715E */ + +/* Chipset type constants for use below */ +#define CHIPSET_UNKNOWN -1 +#define CHIPSET_OPL3SA2 0 +#define CHIPSET_OPL3SA3 1 + +#ifdef __ISAPNP__ +#define OPL3SA2_CARDS_MAX 4 +#else +#define OPL3SA2_CARDS_MAX 1 +#endif + +/* This should be pretty obvious */ +static int opl3sa2_cards_num; /* = 0 */ + +/* What's my version(s)? */ +static int chipset[OPL3SA2_CARDS_MAX] = { CHIPSET_UNKNOWN }; + +/* Oh well, let's just cache the name(s) */ +static char chipset_name[OPL3SA2_CARDS_MAX][12]; + +/* Where's my mixer(s)? */ +static int opl3sa2_mixer[OPL3SA2_CARDS_MAX] = { -1 }; + +/* Bag o' mixer data */ +typedef struct opl3sa2_mixerdata_tag { + unsigned short cfg_port; + unsigned short padding; + unsigned char reg; + unsigned int in_suspend; + struct pm_dev *pmdev; + unsigned int card; + unsigned int volume_l; + unsigned int volume_r; + unsigned int mic; + unsigned int bass_l; + unsigned int bass_r; + unsigned int treble_l; + unsigned int treble_r; + unsigned int wide_l; + unsigned int wide_r; +} opl3sa2_mixerdata; +static opl3sa2_mixerdata opl3sa2_data[OPL3SA2_CARDS_MAX]; + +static struct address_info cfg[OPL3SA2_CARDS_MAX]; +static struct address_info cfg_mss[OPL3SA2_CARDS_MAX]; +static struct address_info cfg_mpu[OPL3SA2_CARDS_MAX]; + +/* Our parameters */ +static int __initdata io = -1; +static int __initdata mss_io = -1; +static int __initdata mpu_io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata ymode = -1; +static int __initdata loopback = -1; + +#ifdef __ISAPNP__ +/* PnP specific parameters */ +static int __initdata isapnp = 1; +static int __initdata multiple = 1; + +/* PnP devices */ +struct pci_dev* opl3sa2_dev[OPL3SA2_CARDS_MAX]; + +/* Whether said devices have been activated */ +static int opl3sa2_activated[OPL3SA2_CARDS_MAX]; +#else +static int __initdata isapnp; /* = 0 */ +static int __initdata multiple; /* = 0 */ +#endif + +MODULE_DESCRIPTION("Module for OPL3-SA2 and SA3 sound cards (uses AD1848 MSS driver)."); +MODULE_AUTHOR("Scott Murray "); +MODULE_LICENSE("GPL"); + + +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "Set I/O base of OPL3-SA2 or SA3 card (usually 0x370. Address must be even and must be from 0x100 to 0xFFE)"); + +MODULE_PARM(mss_io, "i"); +MODULE_PARM_DESC(mss_io, "Set MSS (audio) I/O base (0x530, 0xE80, or other. Address must end in 0 or 4 and must be from 0x530 to 0xF48)"); + +MODULE_PARM(mpu_io, "i"); +MODULE_PARM_DESC(mpu_io, "Set MIDI I/O base (0x330 or other. Address must be even and must be from 0x300 to 0x334)"); + +MODULE_PARM(irq, "i"); +MODULE_PARM_DESC(mss_irq, "Set MSS (audio) IRQ (5, 7, 9, 10, 11, 12)"); + +MODULE_PARM(dma, "i"); +MODULE_PARM_DESC(dma, "Set MSS (audio) first DMA channel (0, 1, 3)"); + +MODULE_PARM(dma2, "i"); +MODULE_PARM_DESC(dma2, "Set MSS (audio) second DMA channel (0, 1, 3)"); + +MODULE_PARM(ymode, "i"); +MODULE_PARM_DESC(ymode, "Set Yamaha 3D enhancement mode (0 = Desktop/Normal, 1 = Notebook PC (1), 2 = Notebook PC (2), 3 = Hi-Fi)"); + +MODULE_PARM(loopback, "i"); +MODULE_PARM_DESC(loopback, "Set A/D input source. Useful for echo cancellation (0 = Mic Rch (default), 1 = Mono output loopback)"); + +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "i"); +MODULE_PARM_DESC(isapnp, "When set to 0, ISA PnP support will be disabled"); + +MODULE_PARM(multiple, "i"); +MODULE_PARM_DESC(multiple, "When set to 0, will not search for multiple cards"); +#endif + + +/* + * Standard read and write functions +*/ + +static inline void opl3sa2_write(unsigned short port, + unsigned char index, + unsigned char data) +{ + outb_p(index, port); + outb(data, port + 1); +} + + +static inline void opl3sa2_read(unsigned short port, + unsigned char index, + unsigned char* data) +{ + outb_p(index, port); + *data = inb(port + 1); +} + + +/* + * All of the mixer functions... + */ + +static void opl3sa2_set_volume(opl3sa2_mixerdata* devc, int left, int right) +{ + static unsigned char scale[101] = { + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }; + unsigned char vol; + + vol = scale[left]; + + /* If level is zero, turn on mute */ + if(!left) + vol |= 0x80; + + opl3sa2_write(devc->cfg_port, OPL3SA2_MASTER_LEFT, vol); + + vol = scale[right]; + + /* If level is zero, turn on mute */ + if(!right) + vol |= 0x80; + + opl3sa2_write(devc->cfg_port, OPL3SA2_MASTER_RIGHT, vol); +} + + +static void opl3sa2_set_mic(opl3sa2_mixerdata* devc, int level) +{ + unsigned char vol = 0x1F; + + if((level >= 0) && (level <= 100)) + vol = 0x1F - (unsigned char) (32 * level / 101); + + /* If level is zero, turn on mute */ + if(!level) + vol |= 0x80; + + opl3sa2_write(devc->cfg_port, OPL3SA2_MIC, vol); +} + + +static void opl3sa3_set_bass(opl3sa2_mixerdata* devc, int left, int right) +{ + unsigned char bass; + + bass = left ? ((unsigned char) (8 * left / 101)) : 0; + bass |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4; + + opl3sa2_write(devc->cfg_port, OPL3SA3_BASS, bass); +} + + +static void opl3sa3_set_treble(opl3sa2_mixerdata* devc, int left, int right) +{ + unsigned char treble; + + treble = left ? ((unsigned char) (8 * left / 101)) : 0; + treble |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4; + + opl3sa2_write(devc->cfg_port, OPL3SA3_TREBLE, treble); +} + + +static void opl3sa3_set_wide(opl3sa2_mixerdata* devc, int left, int right) +{ + unsigned char wide; + + wide = left ? ((unsigned char) (8 * left / 101)) : 0; + wide |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4; + + opl3sa2_write(devc->cfg_port, OPL3SA3_WIDE, wide); +} + + +static void opl3sa2_mixer_reset(opl3sa2_mixerdata* devc, int card) +{ + if(devc) { + opl3sa2_set_volume(devc, DEFAULT_VOLUME, DEFAULT_VOLUME); + devc->volume_l = devc->volume_r = DEFAULT_VOLUME; + + opl3sa2_set_mic(devc, DEFAULT_MIC); + devc->mic = DEFAULT_MIC; + + if(chipset[card] == CHIPSET_OPL3SA3) { + opl3sa3_set_bass(devc, DEFAULT_TIMBRE, DEFAULT_TIMBRE); + devc->bass_l = devc->bass_r = DEFAULT_TIMBRE; + opl3sa3_set_treble(devc, DEFAULT_TIMBRE, DEFAULT_TIMBRE); + devc->treble_l = devc->treble_r = DEFAULT_TIMBRE; + } + } +} + + +static void opl3sa2_mixer_restore(opl3sa2_mixerdata* devc, int card) +{ + if (devc) { + opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r); + opl3sa2_set_mic(devc, devc->mic); + + if (chipset[card] == CHIPSET_OPL3SA3) { + opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r); + opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r); + } + } +} + + +static inline void arg_to_vol_mono(unsigned int vol, int* value) +{ + int left; + + left = vol & 0x00ff; + if (left > 100) + left = 100; + *value = left; +} + + +static inline void arg_to_vol_stereo(unsigned int vol, int* aleft, int* aright) +{ + arg_to_vol_mono(vol, aleft); + arg_to_vol_mono(vol >> 8, aright); +} + + +static inline int ret_vol_mono(int vol) +{ + return ((vol << 8) | vol); +} + + +static inline int ret_vol_stereo(int left, int right) +{ + return ((right << 8) | left); +} + + +static int opl3sa2_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int cmdf = cmd & 0xff; + + opl3sa2_mixerdata* devc = (opl3sa2_mixerdata*) mixer_devs[dev]->devc; + + switch(cmdf) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_MIC: + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_STEREODEVS: + case SOUND_MIXER_RECMASK: + case SOUND_MIXER_RECSRC: + case SOUND_MIXER_CAPS: + break; + + default: + return -EINVAL; + } + + if(((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if(_SIOC_DIR (cmd) & _SIOC_WRITE) { + switch (cmdf) { + case SOUND_MIXER_VOLUME: + arg_to_vol_stereo(*(unsigned int*)arg, + &devc->volume_l, &devc->volume_r); + opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r); + *(int*)arg = ret_vol_stereo(devc->volume_l, devc->volume_r); + return 0; + + case SOUND_MIXER_MIC: + arg_to_vol_mono(*(unsigned int*)arg, &devc->mic); + opl3sa2_set_mic(devc, devc->mic); + *(int*)arg = ret_vol_mono(devc->mic); + return 0; + + default: + return -EINVAL; + } + } + else { + /* + * Return parameters + */ + switch (cmdf) { + case SOUND_MIXER_DEVMASK: + *(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_MIC); + return 0; + + case SOUND_MIXER_STEREODEVS: + *(int*)arg = SOUND_MASK_VOLUME; + return 0; + + case SOUND_MIXER_RECMASK: + /* No recording devices */ + return (*(int*)arg = 0); + + case SOUND_MIXER_CAPS: + *(int*)arg = SOUND_CAP_EXCL_INPUT; + return 0; + + case SOUND_MIXER_RECSRC: + /* No recording source */ + return (*(int*)arg = 0); + + case SOUND_MIXER_VOLUME: + *(int*)arg = ret_vol_stereo(devc->volume_l, devc->volume_r); + return 0; + + case SOUND_MIXER_MIC: + *(int*)arg = ret_vol_mono(devc->mic); + return 0; + + default: + return -EINVAL; + } + } +} +/* opl3sa2_mixer_ioctl end */ + + +static int opl3sa3_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int cmdf = cmd & 0xff; + + opl3sa2_mixerdata* devc = (opl3sa2_mixerdata*) mixer_devs[dev]->devc; + + switch(cmdf) { + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + case SOUND_MIXER_DIGITAL1: + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_STEREODEVS: + break; + + default: + return opl3sa2_mixer_ioctl(dev, cmd, arg); + } + + if(((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if(_SIOC_DIR (cmd) & _SIOC_WRITE) { + switch (cmdf) { + case SOUND_MIXER_BASS: + arg_to_vol_stereo(*(unsigned int*)arg, + &devc->bass_l, &devc->bass_r); + opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r); + *(int*)arg = ret_vol_stereo(devc->bass_l, devc->bass_r); + return 0; + + case SOUND_MIXER_TREBLE: + arg_to_vol_stereo(*(unsigned int*)arg, + &devc->treble_l, &devc->treble_r); + opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r); + *(int*)arg = ret_vol_stereo(devc->treble_l, devc->treble_r); + return 0; + + case SOUND_MIXER_DIGITAL1: + arg_to_vol_stereo(*(unsigned int*)arg, + &devc->wide_l, &devc->wide_r); + opl3sa3_set_wide(devc, devc->wide_l, devc->wide_r); + *(int*)arg = ret_vol_stereo(devc->wide_l, devc->wide_r); + return 0; + + default: + return -EINVAL; + } + } + else + { + /* + * Return parameters + */ + switch (cmdf) { + case SOUND_MIXER_DEVMASK: + *(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_MIC | + SOUND_MASK_BASS | SOUND_MASK_TREBLE | + SOUND_MASK_DIGITAL1); + return 0; + + case SOUND_MIXER_STEREODEVS: + *(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_BASS | + SOUND_MASK_TREBLE | SOUND_MASK_DIGITAL1); + return 0; + + case SOUND_MIXER_BASS: + *(int*)arg = ret_vol_stereo(devc->bass_l, devc->bass_r); + return 0; + + case SOUND_MIXER_TREBLE: + *(int*)arg = ret_vol_stereo(devc->treble_l, devc->treble_r); + return 0; + + case SOUND_MIXER_DIGITAL1: + *(int*)arg = ret_vol_stereo(devc->wide_l, devc->wide_r); + return 0; + + default: + return -EINVAL; + } + } +} +/* opl3sa3_mixer_ioctl end */ + + +static struct mixer_operations opl3sa2_mixer_operations = +{ + owner: THIS_MODULE, + id: "OPL3-SA2", + name: "Yamaha OPL3-SA2", + ioctl: opl3sa2_mixer_ioctl +}; + +static struct mixer_operations opl3sa3_mixer_operations = +{ + owner: THIS_MODULE, + id: "OPL3-SA3", + name: "Yamaha OPL3-SA3", + ioctl: opl3sa3_mixer_ioctl +}; + +/* End of mixer-related stuff */ + + +/* + * Component probe, attach, unload functions + */ + +static inline int __init probe_opl3sa2_mpu(struct address_info* hw_config) +{ + return probe_mpu401(hw_config); +} + + +static inline void __init attach_opl3sa2_mpu(struct address_info* hw_config) +{ + attach_mpu401(hw_config, THIS_MODULE); +} + + +static inline void __exit unload_opl3sa2_mpu(struct address_info *hw_config) +{ + unload_mpu401(hw_config); +} + + +static inline int __init probe_opl3sa2_mss(struct address_info* hw_config) +{ + return probe_ms_sound(hw_config); +} + + +static void __init attach_opl3sa2_mss(struct address_info* hw_config) +{ + int initial_mixers; + + initial_mixers = num_mixers; + attach_ms_sound(hw_config, THIS_MODULE); /* Slot 0 */ + if(hw_config->slots[0] != -1) { + /* Did the MSS driver install? */ + if(num_mixers == (initial_mixers + 1)) { + /* The MSS mixer is installed, reroute mixers appropiately */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } + else { + printk(KERN_ERR "opl3sa2: MSS mixer not installed?\n"); + } + } +} + + +static inline void __exit unload_opl3sa2_mss(struct address_info* hw_config) +{ + unload_ms_sound(hw_config); +} + + +static int __init probe_opl3sa2(struct address_info* hw_config, int card) +{ + unsigned char misc; + unsigned char tmp; + unsigned char version; + char tag; + + /* + * Verify that the I/O port range is free. + */ + if(check_region(hw_config->io_base, 2)) { + printk(KERN_ERR "opl3sa2: Control I/O port %#x not free\n", + hw_config->io_base); + return 0; + } + + /* + * Check if writing to the read-only version bits of the miscellaneous + * register succeeds or not (it should not). + */ + opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &misc); + opl3sa2_write(hw_config->io_base, OPL3SA2_MISC, misc ^ 0x07); + opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &tmp); + if(tmp != misc) { + printk(KERN_ERR "opl3sa2: Control I/O port %#x is not a YMF7xx chipset!\n", + hw_config->io_base); + return 0; + } + + /* + * Check if the MIC register is accessible. + */ + opl3sa2_read(hw_config->io_base, OPL3SA2_MIC, &tmp); + opl3sa2_write(hw_config->io_base, OPL3SA2_MIC, 0x8a); + opl3sa2_read(hw_config->io_base, OPL3SA2_MIC, &tmp); + if((tmp & 0x9f) != 0x8a) { + printk(KERN_ERR + "opl3sa2: Control I/O port %#x is not a YMF7xx chipset!\n", + hw_config->io_base); + return 0; + } + opl3sa2_write(hw_config->io_base, OPL3SA2_MIC, tmp); + + /* + * Determine chipset type (SA2 or SA3) + * + * This is done by looking at the chipset version in the lower 3 bits + * of the miscellaneous register. + */ + version = misc & 0x07; + printk(KERN_DEBUG "opl3sa2: chipset version = %#x\n", version); + switch(version) { + case 0: + chipset[card] = CHIPSET_UNKNOWN; + tag = '?'; /* silence compiler warning */ + printk(KERN_ERR + "opl3sa2: Unknown Yamaha audio controller version\n"); + break; + + case VERSION_YMF711: + chipset[card] = CHIPSET_OPL3SA2; + tag = '2'; + printk(KERN_INFO "opl3sa2: Found OPL3-SA2 (YMF711)\n"); + break; + + case VERSION_YMF715: + chipset[card] = CHIPSET_OPL3SA3; + tag = '3'; + printk(KERN_INFO + "opl3sa2: Found OPL3-SA3 (YMF715 or YMF719)\n"); + break; + + case VERSION_YMF715B: + chipset[card] = CHIPSET_OPL3SA3; + tag = '3'; + printk(KERN_INFO + "opl3sa2: Found OPL3-SA3 (YMF715B or YMF719B)\n"); + break; + + case VERSION_YMF715E: + default: + chipset[card] = CHIPSET_OPL3SA3; + tag = '3'; + printk(KERN_INFO + "opl3sa2: Found OPL3-SA3 (YMF715E or YMF719E)\n"); + break; + } + + if(chipset[card] != CHIPSET_UNKNOWN) { + /* Generate a pretty name */ + sprintf(chipset_name[card], "OPL3-SA%c", tag); + return 1; + } + return 0; +} + + +static void __init attach_opl3sa2(struct address_info* hw_config, int card) +{ + request_region(hw_config->io_base, 2, chipset_name[card]); + + /* Initialize IRQ configuration to IRQ-B: -, IRQ-A: WSS+MPU+OPL3 */ + opl3sa2_write(hw_config->io_base, OPL3SA2_IRQ_CONFIG, 0x0d); + + /* Initialize DMA configuration */ + if(hw_config->dma2 == hw_config->dma) { + /* Want DMA configuration DMA-B: -, DMA-A: WSS-P+WSS-R */ + opl3sa2_write(hw_config->io_base, OPL3SA2_DMA_CONFIG, 0x03); + } + else { + /* Want DMA configuration DMA-B: WSS-R, DMA-A: WSS-P */ + opl3sa2_write(hw_config->io_base, OPL3SA2_DMA_CONFIG, 0x21); + } +} + + +static void __init attach_opl3sa2_mixer(struct address_info *hw_config, int card) +{ + struct mixer_operations* mixer_operations; + opl3sa2_mixerdata* devc; + + /* Install master mixer */ + if(chipset[card] == CHIPSET_OPL3SA3) { + mixer_operations = &opl3sa3_mixer_operations; + } + else { + mixer_operations = &opl3sa2_mixer_operations; + } + + if((devc = &opl3sa2_data[card])) { + devc->cfg_port = hw_config->io_base; + + opl3sa2_mixer[card] = sound_install_mixer(MIXER_DRIVER_VERSION, + mixer_operations->name, + mixer_operations, + sizeof(struct mixer_operations), + devc); + if(opl3sa2_mixer[card] < 0) { + printk(KERN_ERR "opl3sa2: Could not install %s master mixer\n", + mixer_operations->name); + } + else + opl3sa2_mixer_reset(devc, card); + } +} + + +static void __init opl3sa2_clear_slots(struct address_info* hw_config) +{ + int i; + + for(i = 0; i < 6; i++) { + hw_config->slots[i] = -1; + } +} + + +static void __init opl3sa2_set_ymode(struct address_info* hw_config, int ymode) +{ + /* + * Set the Yamaha 3D enhancement mode (aka Ymersion) if asked to and + * it's supported. + * + * 0: Desktop (aka normal) 5-12 cm speakers + * 1: Notebook PC mode 1 3 cm speakers + * 2: Notebook PC mode 2 1.5 cm speakers + * 3: Hi-fi 16-38 cm speakers + */ + if(ymode >= 0 && ymode <= 3) { + unsigned char sys_ctrl; + + opl3sa2_read(hw_config->io_base, OPL3SA2_SYS_CTRL, &sys_ctrl); + sys_ctrl = (sys_ctrl & 0xcf) | ((ymode & 3) << 4); + opl3sa2_write(hw_config->io_base, OPL3SA2_SYS_CTRL, sys_ctrl); + } + else { + printk(KERN_ERR "opl3sa2: not setting ymode, it must be one of 0,1,2,3\n"); + } +} + + +static void __init opl3sa2_set_loopback(struct address_info* hw_config, int loopback) +{ + if(loopback >= 0 && loopback <= 1) { + unsigned char misc; + + opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &misc); + misc = (misc & 0xef) | ((loopback & 1) << 4); + opl3sa2_write(hw_config->io_base, OPL3SA2_MISC, misc); + } + else { + printk(KERN_ERR "opl3sa2: not setting loopback, it must be either 0 or 1\n"); + } +} + + +static void __exit unload_opl3sa2(struct address_info* hw_config, int card) +{ + /* Release control ports */ + release_region(hw_config->io_base, 2); + + /* Unload mixer */ + if(opl3sa2_mixer[card] >= 0) + sound_unload_mixerdev(opl3sa2_mixer[card]); +} + + +#ifdef __ISAPNP__ + +struct isapnp_device_id isapnp_opl3sa2_list[] __initdata = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), + 0 }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, isapnp_opl3sa2_list); + +static int __init opl3sa2_isapnp_probe(struct address_info* hw_cfg, + struct address_info* mss_cfg, + struct address_info* mpu_cfg, + int card) +{ + static struct pci_dev* dev; + int ret; + + /* Find and configure device */ + dev = isapnp_find_dev(NULL, + ISAPNP_VENDOR('Y','M','H'), + ISAPNP_FUNCTION(0x0021), + dev); + if(dev == NULL) { + return -ENODEV; + } + + /* + * If device is active, assume configured with /proc/isapnp + * and use anyway. Any other way to check this? + */ + ret = dev->prepare(dev); + if(ret && ret != -EBUSY) { + printk(KERN_ERR "opl3sa2: ISA PnP found device that could not be autoconfigured.\n"); + return -ENODEV; + } + if(ret == -EBUSY) { + opl3sa2_activated[card] = 1; + } + else { + if(dev->activate(dev) < 0) { + printk(KERN_WARNING "opl3sa2: ISA PnP activate failed\n"); + opl3sa2_activated[card] = 0; + return -ENODEV; + } + + printk(KERN_DEBUG + "opl3sa2: Activated ISA PnP card %d (active=%d)\n", + card, dev->active); + + } + + /* Our own config: */ + hw_cfg->io_base = dev->resource[4].start; + hw_cfg->irq = dev->irq_resource[0].start; + hw_cfg->dma = dev->dma_resource[0].start; + hw_cfg->dma2 = dev->dma_resource[1].start; + + /* The MSS config: */ + mss_cfg->io_base = dev->resource[1].start; + mss_cfg->irq = dev->irq_resource[0].start; + mss_cfg->dma = dev->dma_resource[0].start; + mss_cfg->dma2 = dev->dma_resource[1].start; + mss_cfg->card_subtype = 1; /* No IRQ or DMA setup */ + + mpu_cfg->io_base = dev->resource[3].start; + mpu_cfg->irq = dev->irq_resource[0].start; + mpu_cfg->dma = -1; + mpu_cfg->dma2 = -1; + mpu_cfg->always_detect = 1; /* It's there, so use shared IRQs */ + + /* Call me paranoid: */ + opl3sa2_clear_slots(hw_cfg); + opl3sa2_clear_slots(mss_cfg); + opl3sa2_clear_slots(mpu_cfg); + + opl3sa2_dev[card] = dev; + + return 0; +} +#endif /* __ISAPNP__ */ + +/* End of component functions */ + +/* Power Management support functions */ +static int opl3sa2_suspend(struct pm_dev *pdev, unsigned char pm_mode) +{ + unsigned long flags; + opl3sa2_mixerdata *p; + + if (!pdev) + return -EINVAL; + + save_flags(flags); + cli(); + + p = (opl3sa2_mixerdata *) pdev->data; + p->in_suspend = 1; + switch (pm_mode) { + case 1: + pm_mode = OPL3SA2_PM_MODE1; + break; + case 2: + pm_mode = OPL3SA2_PM_MODE2; + break; + case 3: + pm_mode = OPL3SA2_PM_MODE3; + break; + default: + pm_mode = OPL3SA2_PM_MODE3; + break; + } + + /* its supposed to automute before suspending, so we wont bother */ + opl3sa2_read(p->cfg_port, OPL3SA2_PM, &p->reg); + opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->reg | pm_mode); + + restore_flags(flags); + return 0; +} + +static int opl3sa2_resume(struct pm_dev *pdev) +{ + unsigned long flags; + opl3sa2_mixerdata *p; + + if (!pdev) + return -EINVAL; + + p = (opl3sa2_mixerdata *) pdev->data; + save_flags(flags); + cli(); + + /* I don't think this is necessary */ + opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->reg); + opl3sa2_mixer_restore(p, p->card); + p->in_suspend = 0; + + restore_flags(flags); + return 0; +} + +static int opl3sa2_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data) +{ + unsigned char mode = (unsigned char)data; + + switch (rqst) { + case PM_SUSPEND: + return opl3sa2_suspend(pdev, mode); + + case PM_RESUME: + return opl3sa2_resume(pdev); + } + return 0; +} + +/* + * Install OPL3-SA2 based card(s). + * + * Need to have ad1848 and mpu401 loaded ready. + */ +static int __init init_opl3sa2(void) +{ + int card; + int max; + + /* Sanitize isapnp and multiple settings */ + isapnp = isapnp != 0 ? 1 : 0; + multiple = multiple != 0 ? 1 : 0; + + max = (multiple && isapnp) ? OPL3SA2_CARDS_MAX : 1; + for(card = 0; card < max; card++, opl3sa2_cards_num++) { +#ifdef __ISAPNP__ + /* + * Please remember that even with __ISAPNP__ defined one + * should still be able to disable PNP support for this + * single driver! + */ + if(isapnp && opl3sa2_isapnp_probe(&cfg[card], + &cfg_mss[card], + &cfg_mpu[card], + card) < 0) { + if(!opl3sa2_cards_num) + printk(KERN_INFO "opl3sa2: No PnP cards found\n"); + if(io == -1) + break; + isapnp=0; + printk(KERN_INFO "opl3sa2: Search for a card at 0x%d.\n", io); + /* Fall through */ + } +#endif + /* If a user wants an I/O then assume they meant it */ + + if(!isapnp) { + if(io == -1 || irq == -1 || dma == -1 || + dma2 == -1 || mss_io == -1) { + printk(KERN_ERR + "opl3sa2: io, mss_io, irq, dma, and dma2 must be set\n"); + return -EINVAL; + } + + /* + * Our own config: + * (NOTE: IRQ and DMA aren't used, so they're set to + * give pretty output from conf_printf. :) + */ + cfg[card].io_base = io; + cfg[card].irq = irq; + cfg[card].dma = dma; + cfg[card].dma2 = dma2; + + /* The MSS config: */ + cfg_mss[card].io_base = mss_io; + cfg_mss[card].irq = irq; + cfg_mss[card].dma = dma; + cfg_mss[card].dma2 = dma2; + cfg_mss[card].card_subtype = 1; /* No IRQ or DMA setup */ + + cfg_mpu[card].io_base = mpu_io; + cfg_mpu[card].irq = irq; + cfg_mpu[card].dma = -1; + cfg_mpu[card].always_detect = 1; /* Use shared IRQs */ + + /* Call me paranoid: */ + opl3sa2_clear_slots(&cfg[card]); + opl3sa2_clear_slots(&cfg_mss[card]); + opl3sa2_clear_slots(&cfg_mpu[card]); + } + + if(!probe_opl3sa2(&cfg[card], card) || + !probe_opl3sa2_mss(&cfg_mss[card])) { + /* + * If one or more cards are already registered, don't + * return an error but print a warning. Note, this + * should never really happen unless the hardware or + * ISA PnP screwed up. + */ + if(opl3sa2_cards_num) { + printk(KERN_WARNING + "opl3sa2: There was a problem probing one " + " of the ISA PNP cards, continuing\n"); + opl3sa2_cards_num--; + continue; + } else + return -ENODEV; + } + + attach_opl3sa2(&cfg[card], card); + conf_printf(chipset_name[card], &cfg[card]); + attach_opl3sa2_mss(&cfg_mss[card]); + attach_opl3sa2_mixer(&cfg[card], card); + + opl3sa2_data[card].card = card; + /* register our power management capabilities */ + opl3sa2_data[card].pmdev = pm_register(PM_ISA_DEV, card, opl3sa2_pm_callback); + if (opl3sa2_data[card].pmdev) + opl3sa2_data[card].pmdev->data = &opl3sa2_data[card]; + + /* + * Set the Yamaha 3D enhancement mode (aka Ymersion) if asked to and + * it's supported. + */ + if(ymode != -1) { + if(chipset[card] == CHIPSET_OPL3SA2) { + printk(KERN_ERR + "opl3sa2: ymode not supported on OPL3-SA2\n"); + } + else { + opl3sa2_set_ymode(&cfg[card], ymode); + } + } + + + /* Set A/D input to Mono loopback if asked to. */ + if(loopback != -1) { + opl3sa2_set_loopback(&cfg[card], loopback); + } + + /* Attach MPU if we've been asked to do so */ + if(cfg_mpu[card].io_base != -1) { + if(probe_opl3sa2_mpu(&cfg_mpu[card])) { + attach_opl3sa2_mpu(&cfg_mpu[card]); + } + } + } + + if(isapnp) { + printk(KERN_NOTICE "opl3sa2: %d PnP card(s) found.\n", opl3sa2_cards_num); + } + + return 0; +} + + +/* + * Uninstall OPL3-SA2 based card(s). + */ +static void __exit cleanup_opl3sa2(void) +{ + int card; + + for(card = 0; card < opl3sa2_cards_num; card++) { + if (opl3sa2_data[card].pmdev) + pm_unregister(opl3sa2_data[card].pmdev); + + if(cfg_mpu[card].slots[1] != -1) { + unload_opl3sa2_mpu(&cfg_mpu[card]); + } + unload_opl3sa2_mss(&cfg_mss[card]); + unload_opl3sa2(&cfg[card], card); + +#ifdef __ISAPNP__ + if(opl3sa2_activated[card] && opl3sa2_dev[card]) { + opl3sa2_dev[card]->deactivate(opl3sa2_dev[card]); + + printk(KERN_DEBUG + "opl3sa2: Deactivated ISA PnP card %d (active=%d)\n", + card, opl3sa2_dev[card]->active); + } +#endif + } +} + +module_init(init_opl3sa2); +module_exit(cleanup_opl3sa2); + +#ifndef MODULE +static int __init setup_opl3sa2(char *str) +{ + /* io, irq, dma, dma2,... */ +#ifdef __ISAPNP__ + int ints[11]; +#else + int ints[9]; +#endif + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + mss_io = ints[5]; + mpu_io = ints[6]; + ymode = ints[7]; + loopback = ints[8]; +#ifdef __ISAPNP__ + isapnp = ints[9]; + multiple = ints[10]; +#endif + return 1; +} + +__setup("opl3sa2=", setup_opl3sa2); +#endif diff -Nru a/sound/oss/os.h b/sound/oss/os.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/os.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,57 @@ +#define ALLOW_SELECT +#undef NO_INLINE_ASM +#define SHORT_BANNERS +#define MANUAL_PNP +#undef DO_TIMINGS + +#include +#include + +#if LINUX_VERSION_CODE > 131328 +#define LINUX21X +#endif + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __alpha__ +#include +#endif +#include +#include +#include +#include +#endif + +#include +#include + +#define FALSE 0 +#define TRUE 1 + +extern int sound_alloc_dma(int chn, char *deviceID); +extern int sound_open_dma(int chn, char *deviceID); +extern void sound_free_dma(int chn); +extern void sound_close_dma(int chn); + +extern void reprogram_timer(void); + +#define USE_AUTOINIT_DMA + +extern caddr_t sound_mem_blocks[1024]; +extern int sound_nblocks; + +#undef PSEUDO_DMA_AUTOINIT +#define ALLOW_BUFFER_MAPPING + +extern struct file_operations oss_sound_fops; diff -Nru a/sound/oss/pas2.h b/sound/oss/pas2.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/pas2.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,23 @@ +/* + * pas2.h + * + * Copyright: Christoph Hellwig + * + */ + +/* From pas_card.c */ +int pas_set_intr(int mask); +int pas_remove_intr(int mask); +unsigned char pas_read(int ioaddr); +void pas_write(unsigned char data, int ioaddr); + +/* From pas_audio.c */ +void pas_pcm_interrupt(unsigned char status, int cause); +void pas_pcm_init(struct address_info *hw_config); + +/* From pas_mixer.c */ +int pas_init_mixer(void); + +/* From pas_midi.c */ +void pas_midi_init(void); +void pas_midi_interrupt(void); diff -Nru a/sound/oss/pas2_card.c b/sound/oss/pas2_card.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/pas2_card.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,464 @@ +/* + * sound/pas2_card.c + * + * Detection routine for the Pro Audio Spectrum cards. + */ + +#include +#include +#include +#include "sound_config.h" + +#include "pas2.h" +#include "sb.h" + +static unsigned char dma_bits[] = { + 4, 1, 2, 3, 0, 5, 6, 7 +}; + +static unsigned char irq_bits[] = { + 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 +}; + +static unsigned char sb_irq_bits[] = { + 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, + 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 +}; + +static unsigned char sb_dma_bits[] = { + 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 +}; + +/* + * The Address Translation code is used to convert I/O register addresses to + * be relative to the given base -register + */ + +int translate_code = 0; +static int pas_intr_mask = 0; +static int pas_irq = 0; +static int pas_sb_base = 0; +#ifndef CONFIG_PAS_JOYSTICK +static int joystick = 0; +#else +static int joystick = 1; +#endif +#ifdef SYMPHONY_PAS +static int symphony = 1; +#else +static int symphony = 0; +#endif +#ifdef BROKEN_BUS_CLOCK +static int broken_bus_clock = 1; +#else +static int broken_bus_clock = 0; +#endif + +static struct address_info cfg; +static struct address_info cfg2; + +char pas_model = 0; +static char *pas_model_names[] = { + "", + "Pro AudioSpectrum+", + "CDPC", + "Pro AudioSpectrum 16", + "Pro AudioSpectrum 16D" +}; + +/* + * pas_read() and pas_write() are equivalents of inb and outb + * These routines perform the I/O address translation required + * to support other than the default base address + */ + +extern void mix_write(unsigned char data, int ioaddr); + +unsigned char pas_read(int ioaddr) +{ + return inb(ioaddr + translate_code); +} + +void pas_write(unsigned char data, int ioaddr) +{ + outb((data), ioaddr + translate_code); +} + +/******************* Begin of the Interrupt Handler ********************/ + +void pasintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + int status; + + status = pas_read(0x0B89); + pas_write(status, 0x0B89); /* Clear interrupt */ + + if (status & 0x08) + { + pas_pcm_interrupt(status, 1); + status &= ~0x08; + } + if (status & 0x10) + { + pas_midi_interrupt(); + status &= ~0x10; + } +} + +int pas_set_intr(int mask) +{ + if (!mask) + return 0; + + pas_intr_mask |= mask; + + pas_write(pas_intr_mask, 0x0B8B); + return 0; +} + +int pas_remove_intr(int mask) +{ + if (!mask) + return 0; + + pas_intr_mask &= ~mask; + pas_write(pas_intr_mask, 0x0B8B); + + return 0; +} + +/******************* End of the Interrupt handler **********************/ + +/******************* Begin of the Initialization Code ******************/ + +static int __init config_pas_hw(struct address_info *hw_config) +{ + char ok = 1; + unsigned int_ptrs; /* scsi/sound interrupt pointers */ + + pas_irq = hw_config->irq; + + pas_write(0x00, 0x0B8B); + pas_write(0x36, 0x138B); + pas_write(0x36, 0x1388); + pas_write(0, 0x1388); + pas_write(0x74, 0x138B); + pas_write(0x74, 0x1389); + pas_write(0, 0x1389); + + pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A); + pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A); + pas_write(0x01 | 0x02 | 0x04 | 0x10 /* + * | + * 0x80 + */ , 0xB88); + + pas_write(0x80 + | joystick?0x40:0 + ,0xF388); + + if (pas_irq < 0 || pas_irq > 15) + { + printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); + hw_config->irq=-1; + ok = 0; + } + else + { + int_ptrs = pas_read(0xF38A); + int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq]; + pas_write(int_ptrs, 0xF38A); + if (!irq_bits[pas_irq]) + { + printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); + hw_config->irq=-1; + ok = 0; + } + else + { + if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) { + printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq); + hw_config->irq=-1; + ok = 0; + } + } + } + + if (hw_config->dma < 0 || hw_config->dma > 7) + { + printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); + hw_config->dma=-1; + ok = 0; + } + else + { + pas_write(dma_bits[hw_config->dma], 0xF389); + if (!dma_bits[hw_config->dma]) + { + printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); + hw_config->dma=-1; + ok = 0; + } + else + { + if (sound_alloc_dma(hw_config->dma, "PAS16")) + { + printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n"); + hw_config->dma=-1; + ok = 0; + } + } + } + + /* + * This fixes the timing problems of the PAS due to the Symphony chipset + * as per Media Vision. Only define this if your PAS doesn't work correctly. + */ + + if(symphony) + { + outb((0x05), 0xa8); + outb((0x60), 0xa9); + } + + if(broken_bus_clock) + pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388); + else + /* + * pas_write(0x01, 0x8388); + */ + pas_write(0x01 | 0x10 | 0x20, 0x8388); + + pas_write(0x18, 0x838A); /* ??? */ + pas_write(0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */ + pas_write(8, 0xBF8A); + + mix_write(0x80 | 5, 0x078B); + mix_write(5, 0x078B); + +#if !defined(DISABLE_SB_EMULATION) + + { + struct address_info *sb_config; + + sb_config = &cfg2; + if (sb_config->io_base) + { + unsigned char irq_dma; + + /* + * Turn on Sound Blaster compatibility + * bit 1 = SB emulation + * bit 0 = MPU401 emulation (CDPC only :-( ) + */ + + pas_write(0x02, 0xF788); + + /* + * "Emulation address" + */ + + pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789); + pas_sb_base = sb_config->io_base; + + if (!sb_dma_bits[sb_config->dma]) + printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma); + + if (!sb_irq_bits[sb_config->irq]) + printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq); + + irq_dma = sb_dma_bits[sb_config->dma] | + sb_irq_bits[sb_config->irq]; + + pas_write(irq_dma, 0xFB8A); + } + else + pas_write(0x00, 0xF788); + } +#else + pas_write(0x00, 0xF788); +#endif + + if (!ok) + printk(KERN_WARNING "PAS16: Driver not enabled\n"); + + return ok; +} + +static int __init detect_pas_hw(struct address_info *hw_config) +{ + unsigned char board_id, foo; + + /* + * WARNING: Setting an option like W:1 or so that disables warm boot reset + * of the card will screw up this detect code something fierce. Adding code + * to handle this means possibly interfering with other cards on the bus if + * you have something on base port 0x388. SO be forewarned. + */ + + outb((0xBC), 0x9A01); /* Activate first board */ + outb((hw_config->io_base >> 2), 0x9A01); /* Set base address */ + translate_code = hw_config->io_base - 0x388; + pas_write(1, 0xBF88); /* Select one wait states */ + + board_id = pas_read(0x0B8B); + + if (board_id == 0xff) + return 0; + + /* + * We probably have a PAS-series board, now check for a PAS16-series board + * by trying to change the board revision bits. PAS16-series hardware won't + * let you do this - the bits are read-only. + */ + + foo = board_id ^ 0xe0; + + pas_write(foo, 0x0B8B); + foo = pas_read(0x0B8B); + pas_write(board_id, 0x0B8B); + + if (board_id != foo) + return 0; + + pas_model = pas_read(0xFF88); + + return pas_model; +} + +static void __init attach_pas_card(struct address_info *hw_config) +{ + pas_irq = hw_config->irq; + + if (detect_pas_hw(hw_config)) + { + + if ((pas_model = pas_read(0xFF88))) + { + char temp[100]; + + sprintf(temp, + "%s rev %d", pas_model_names[(int) pas_model], + pas_read(0x2789)); + conf_printf(temp, hw_config); + } + if (config_pas_hw(hw_config)) + { + pas_pcm_init(hw_config); + +#if !defined(MODULE) && !defined(DISABLE_SB_EMULATION) + sb_dsp_disable_midi(pas_sb_base); /* No MIDI capability */ +#endif + + pas_midi_init(); + pas_init_mixer(); + } + } +} + +static inline int __init probe_pas(struct address_info *hw_config) +{ + return detect_pas_hw(hw_config); +} + +static void __exit unload_pas(struct address_info *hw_config) +{ + extern int pas_audiodev; + extern int pas2_mididev; + + if (hw_config->dma>0) + sound_free_dma(hw_config->dma); + if (hw_config->irq>0) + free_irq(hw_config->irq, hw_config); + + if(pas_audiodev!=-1) + sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev); + if(pas2_mididev!=-1) + sound_unload_mididev(pas2_mididev); + if(pas_audiodev!=-1) + sound_unload_audiodev(pas_audiodev); +} + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ + +static int __initdata sb_io = 0; +static int __initdata sb_irq = -1; +static int __initdata sb_dma = -1; +static int __initdata sb_dma16 = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma16,"i"); + +MODULE_PARM(sb_io,"i"); +MODULE_PARM(sb_irq,"i"); +MODULE_PARM(sb_dma,"i"); +MODULE_PARM(sb_dma16,"i"); + +MODULE_PARM(joystick,"i"); +MODULE_PARM(symphony,"i"); +MODULE_PARM(broken_bus_clock,"i"); + +MODULE_LICENSE("GPL"); + +static int __init init_pas2(void) +{ + printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma16; + + cfg2.io_base = sb_io; + cfg2.irq = sb_irq; + cfg2.dma = sb_dma; + cfg2.dma2 = sb_dma16; + + if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { + printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); + return -EINVAL; + } + + if (!probe_pas(&cfg)) + return -ENODEV; + attach_pas_card(&cfg); + + return 0; +} + +static void __exit cleanup_pas2(void) +{ + unload_pas(&cfg); +} + +module_init(init_pas2); +module_exit(cleanup_pas2); + +#ifndef MODULE +static int __init setup_pas2(char *str) +{ + /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, sb_dma2 */ + int ints[9]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma16 = ints[4]; + + sb_io = ints[5]; + sb_irq = ints[6]; + sb_dma = ints[7]; + sb_dma16 = ints[8]; + + return 1; +} + +__setup("pas2=", setup_pas2); +#endif diff -Nru a/sound/oss/pas2_midi.c b/sound/oss/pas2_midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/pas2_midi.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,264 @@ +/* + * sound/pas2_midi.c + * + * The low level driver for the PAS Midi Interface. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Bartlomiej Zolnierkiewicz : Added __init to pas_init_mixer() + */ + +#include +#include "sound_config.h" + +#include "pas2.h" + +static int midi_busy = 0, input_opened = 0; +static int my_dev; + +int pas2_mididev=-1; + +static unsigned char tmp_queue[256]; +static volatile int qlen; +static volatile unsigned char qhead, qtail; + +static void (*midi_input_intr) (int dev, unsigned char data); + +static int pas_midi_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + int err; + unsigned long flags; + unsigned char ctrl; + + + if (midi_busy) + return -EBUSY; + + /* + * Reset input and output FIFO pointers + */ + pas_write(0x20 | 0x40, + 0x178b); + + save_flags(flags); + cli(); + + if ((err = pas_set_intr(0x10)) < 0) + { + restore_flags(flags); + return err; + } + /* + * Enable input available and output FIFO empty interrupts + */ + + ctrl = 0; + input_opened = 0; + midi_input_intr = input; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) + { + ctrl |= 0x04; /* Enable input */ + input_opened = 1; + } + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) + { + ctrl |= 0x08 | 0x10; /* Enable output */ + } + pas_write(ctrl, 0x178b); + + /* + * Acknowledge any pending interrupts + */ + + pas_write(0xff, 0x1B88); + + restore_flags(flags); + + midi_busy = 1; + qlen = qhead = qtail = 0; + return 0; +} + +static void pas_midi_close(int dev) +{ + + /* + * Reset FIFO pointers, disable intrs + */ + pas_write(0x20 | 0x40, 0x178b); + + pas_remove_intr(0x10); + midi_busy = 0; +} + +static int dump_to_midi(unsigned char midi_byte) +{ + int fifo_space, x; + + fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f; + + /* + * The MIDI FIFO space register and it's documentation is nonunderstandable. + * There seem to be no way to differentiate between buffer full and buffer + * empty situations. For this reason we don't never write the buffer + * completely full. In this way we can assume that 0 (or is it 15) + * means that the buffer is empty. + */ + + if (fifo_space < 2 && fifo_space != 0) /* Full (almost) */ + return 0; /* Ask upper layers to retry after some time */ + + pas_write(midi_byte, 0x178A); + + return 1; +} + +static int pas_midi_out(int dev, unsigned char midi_byte) +{ + + unsigned long flags; + + /* + * Drain the local queue first + */ + + save_flags(flags); + cli(); + + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + restore_flags(flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi(midi_byte)) + return 1; + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* Local queue full */ + + save_flags(flags); + cli(); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + restore_flags(flags); + + return 1; +} + +static int pas_midi_start_read(int dev) +{ + return 0; +} + +static int pas_midi_end_read(int dev) +{ + return 0; +} + +static void pas_midi_kick(int dev) +{ +} + +static int pas_buffer_status(int dev) +{ + return qlen; +} + +#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations pas_midi_operations = +{ + owner: THIS_MODULE, + info: {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS}, + converter: &std_midi_synth, + in_info: {0}, + open: pas_midi_open, + close: pas_midi_close, + outputc: pas_midi_out, + start_read: pas_midi_start_read, + end_read: pas_midi_end_read, + kick: pas_midi_kick, + buffer_status: pas_buffer_status, +}; + +void __init pas_midi_init(void) +{ + int dev = sound_alloc_mididev(); + + if (dev == -1) + { + printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n"); + return; + } + std_midi_synth.midi_dev = my_dev = dev; + midi_devs[dev] = &pas_midi_operations; + pas2_mididev = dev; + sequencer_init(); +} + +void pas_midi_interrupt(void) +{ + unsigned char stat; + int i, incount; + unsigned long flags; + + stat = pas_read(0x1B88); + + if (stat & 0x04) /* Input data available */ + { + incount = pas_read(0x1B89) & 0x0f; /* Input FIFO size */ + if (!incount) + incount = 16; + + for (i = 0; i < incount; i++) + if (input_opened) + { + midi_input_intr(my_dev, pas_read(0x178A)); + } else + pas_read(0x178A); /* Flush */ + } + if (stat & (0x08 | 0x10)) + { + save_flags(flags); + cli(); + + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + restore_flags(flags); + } + if (stat & 0x40) + { + printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat); + } + pas_write(stat, 0x1B88); /* Acknowledge interrupts */ +} diff -Nru a/sound/oss/pas2_mixer.c b/sound/oss/pas2_mixer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/pas2_mixer.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,335 @@ + +/* + * sound/pas2_mixer.c + * + * Mixer routines for the Pro Audio Spectrum cards. + */ + +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Bartlomiej Zolnierkiewicz : added __init to pas_init_mixer() + */ +#include +#include "sound_config.h" + +#include "pas2.h" + +#ifndef DEB +#define DEB(what) /* (what) */ +#endif + +extern int translate_code; +extern char pas_model; +extern int *pas_osp; +extern int pas_audiodev; + +static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */ +static int mode_control = 0; + +#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_ALTPCM) + +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \ + SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV) + +static int *levels; + +static int default_levels[32] = +{ + 0x3232, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x5050, /* FM */ + 0x4b4b, /* PCM */ + 0x3232, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x4b4b, /* Mic */ + 0x4b4b, /* CD */ + 0x6464, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x6464 /* Recording level */ +}; + +void +mix_write(unsigned char data, int ioaddr) +{ + /* + * The Revision D cards have a problem with their MVA508 interface. The + * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and + * MSBs out of the output byte and to do a 16-bit out to the mixer port - + * 1. We need to do this because it isn't timing problem but chip access + * sequence problem. + */ + + if (pas_model == 4) + { + outw(data | (data << 8), (ioaddr + translate_code) - 1); + outb((0x80), 0); + } else + pas_write(data, ioaddr); +} + +static int +mixer_output(int right_vol, int left_vol, int div, int bits, + int mixer) /* Input or output mixer */ +{ + int left = left_vol * div / 100; + int right = right_vol * div / 100; + + + if (bits & 0x10) + { + left |= mixer; + right |= mixer; + } + if (bits == 0x03 || bits == 0x04) + { + mix_write(0x80 | bits, 0x078B); + mix_write(left, 0x078B); + right_vol = left_vol; + } else + { + mix_write(0x80 | 0x20 | bits, 0x078B); + mix_write(left, 0x078B); + mix_write(0x80 | 0x40 | bits, 0x078B); + mix_write(right, 0x078B); + } + + return (left_vol | (right_vol << 8)); +} + +static void +set_mode(int new_mode) +{ + mix_write(0x80 | 0x05, 0x078B); + mix_write(new_mode, 0x078B); + + mode_control = new_mode; +} + +static int +pas_mixer_set(int whichDev, unsigned int level) +{ + int left, right, devmask, changed, i, mixer = 0; + + DEB(printk("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level)); + + left = level & 0x7f; + right = (level & 0x7f00) >> 8; + + if (whichDev < SOUND_MIXER_NRDEVICES) { + if ((1 << whichDev) & rec_devices) + mixer = 0x20; + else + mixer = 0x00; + } + + switch (whichDev) + { + case SOUND_MIXER_VOLUME: /* Master volume (0-63) */ + levels[whichDev] = mixer_output(right, left, 63, 0x01, 0); + break; + + /* + * Note! Bass and Treble are mono devices. Will use just the left + * channel. + */ + case SOUND_MIXER_BASS: /* Bass (0-12) */ + levels[whichDev] = mixer_output(right, left, 12, 0x03, 0); + break; + case SOUND_MIXER_TREBLE: /* Treble (0-12) */ + levels[whichDev] = mixer_output(right, left, 12, 0x04, 0); + break; + + case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x00, mixer); + break; + case SOUND_MIXER_PCM: /* PAS PCM (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x05, mixer); + break; + case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x07, mixer); + break; + case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x06, mixer); + break; + case SOUND_MIXER_LINE: /* External line (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x02, mixer); + break; + case SOUND_MIXER_CD: /* CD (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x03, mixer); + break; + case SOUND_MIXER_MIC: /* External microphone (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x04, mixer); + break; + case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x01, + 0x00); + break; + case SOUND_MIXER_RECLEV: /* Recording level (0-15) */ + levels[whichDev] = mixer_output(right, left, 15, 0x02, 0); + break; + + + case SOUND_MIXER_RECSRC: + devmask = level & POSSIBLE_RECORDING_DEVICES; + + changed = devmask ^ rec_devices; + rec_devices = devmask; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (changed & (1 << i)) + { + pas_mixer_set(i, levels[i]); + } + return rec_devices; + break; + + default: + return -EINVAL; + } + + return (levels[whichDev]); +} + +/*****/ + +static void +pas_mixer_reset(void) +{ + int foo; + + DEB(printk("pas2_mixer.c: void pas_mixer_reset(void)\n")); + + for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) + pas_mixer_set(foo, levels[foo]); + + set_mode(0x04 | 0x01); +} + +static int pas_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int level,v ; + + DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); + if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */ + if (get_user(level, (int *)arg)) + return -EFAULT; + if (level == -1) /* Return current settings */ + level = (mode_control & 0x04); + else { + mode_control &= ~0x04; + if (level) + mode_control |= 0x04; + set_mode(mode_control); + } + level = !!level; + return put_user(level, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */ + if (get_user(level, (int *)arg)) + return -EFAULT; + if (level == -1) { /* Return current settings */ + if (!(mode_control & 0x03)) + level = 0; + else + level = ((mode_control & 0x03) + 1) * 20; + } else { + int i = 0; + + level &= 0x7f; + if (level) + i = (level / 20) - 1; + mode_control &= ~0x03; + mode_control |= i & 0x03; + set_mode(mode_control); + if (i) + i = (i + 1) * 20; + level = i; + } + return put_user(level, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */ + if (get_user(level, (int *)arg)) + return -EFAULT; + if (level == -1) /* Return current settings */ + level = !(pas_read(0x0B8A) & 0x20); + else { + if (level) + pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A); + else + pas_write(pas_read(0x0B8A) | 0x20, 0x0B8A); + + level = !(pas_read(0x0B8A) & 0x20); + } + return put_user(level, (int *)arg); + } + if (((cmd >> 8) & 0xff) == 'M') { + if (get_user(v, (int *)arg)) + return -EFAULT; + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + v = pas_mixer_set(cmd & 0xff, v); + } else { + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + v = rec_devices; + break; + + case SOUND_MIXER_STEREODEVS: + v = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE); + break; + + case SOUND_MIXER_DEVMASK: + v = SUPPORTED_MIXER_DEVICES; + break; + + case SOUND_MIXER_RECMASK: + v = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES; + break; + + case SOUND_MIXER_CAPS: + v = 0; /* No special capabilities */ + break; + + default: + v = levels[cmd & 0xff]; + break; + } + } + return put_user(v, (int *)arg); + } + return -EINVAL; +} + +static struct mixer_operations pas_mixer_operations = +{ + owner: THIS_MODULE, + id: "PAS16", + name: "Pro Audio Spectrum 16", + ioctl: pas_mixer_ioctl +}; + +int __init +pas_init_mixer(void) +{ + int d; + + levels = load_mixer_volumes("PAS16_1", default_levels, 1); + + pas_mixer_reset(); + + if ((d = sound_alloc_mixerdev()) != -1) + { + audio_devs[pas_audiodev]->mixer_dev = d; + mixer_devs[d] = &pas_mixer_operations; + } + return 1; +} diff -Nru a/sound/oss/pas2_pcm.c b/sound/oss/pas2_pcm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/pas2_pcm.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,438 @@ +/* + * pas2_pcm.c Audio routines for PAS16 + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Alan Cox : Swatted a double allocation of device bug. Made a few + * more things module options. + * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init() + */ + +#include +#include "sound_config.h" + +#include "pas2.h" + +#ifndef DEB +#define DEB(WHAT) +#endif + +#define PAS_PCM_INTRBITS (0x08) +/* + * Sample buffer timer interrupt enable + */ + +#define PCM_NON 0 +#define PCM_DAC 1 +#define PCM_ADC 2 + +static unsigned long pcm_speed = 0; /* sampling rate */ +static unsigned char pcm_channels = 1; /* channels (1 or 2) */ +static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */ +static unsigned char pcm_filter = 0; /* filter FLAG */ +static unsigned char pcm_mode = PCM_NON; +static unsigned long pcm_count = 0; +static unsigned short pcm_bitsok = 8; /* mask of OK bits */ +static int pcm_busy = 0; +int pas_audiodev = -1; +static int open_mode = 0; + +static int pcm_set_speed(int arg) +{ + int foo, tmp; + unsigned long flags; + + if (arg == 0) + return pcm_speed; + + if (arg > 44100) + arg = 44100; + if (arg < 5000) + arg = 5000; + + if (pcm_channels & 2) + { + foo = (596590 + (arg / 2)) / arg; + arg = (596590 + (foo / 2)) / foo; + } + else + { + foo = (1193180 + (arg / 2)) / arg; + arg = (1193180 + (foo / 2)) / foo; + } + + pcm_speed = arg; + + tmp = pas_read(0x0B8A); + + /* + * Set anti-aliasing filters according to sample rate. You really *NEED* + * to enable this feature for all normal recording unless you want to + * experiment with aliasing effects. + * These filters apply to the selected "recording" source. + * I (pfw) don't know the encoding of these 5 bits. The values shown + * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/. + * + * I cleared bit 5 of these values, since that bit controls the master + * mute flag. (Olav Wölfelschneider) + * + */ +#if !defined NO_AUTO_FILTER_SET + tmp &= 0xe0; + if (pcm_speed >= 2 * 17897) + tmp |= 0x01; + else if (pcm_speed >= 2 * 15909) + tmp |= 0x02; + else if (pcm_speed >= 2 * 11931) + tmp |= 0x09; + else if (pcm_speed >= 2 * 8948) + tmp |= 0x11; + else if (pcm_speed >= 2 * 5965) + tmp |= 0x19; + else if (pcm_speed >= 2 * 2982) + tmp |= 0x04; + pcm_filter = tmp; +#endif + + save_flags(flags); + cli(); + + pas_write(tmp & ~(0x40 | 0x80), 0x0B8A); + pas_write(0x00 | 0x30 | 0x04, 0x138B); + pas_write(foo & 0xff, 0x1388); + pas_write((foo >> 8) & 0xff, 0x1388); + pas_write(tmp, 0x0B8A); + + restore_flags(flags); + + return pcm_speed; +} + +static int pcm_set_channels(int arg) +{ + + if ((arg != 1) && (arg != 2)) + return pcm_channels; + + if (arg != pcm_channels) + { + pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A); + + pcm_channels = arg; + pcm_set_speed(pcm_speed); /* The speed must be reinitialized */ + } + return pcm_channels; +} + +static int pcm_set_bits(int arg) +{ + if (arg == 0) + return pcm_bits; + + if ((arg & pcm_bitsok) != arg) + return pcm_bits; + + if (arg != pcm_bits) + { + pas_write(pas_read(0x8389) ^ 0x04, 0x8389); + + pcm_bits = arg; + } + return pcm_bits; +} + +static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val, ret; + + DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); + + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (get_user(val, (int *)arg)) + return -EFAULT; + ret = pcm_set_speed(val); + break; + + case SOUND_PCM_READ_RATE: + ret = pcm_speed; + break; + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + ret = pcm_set_channels(val + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + ret = pcm_set_channels(val); + break; + + case SOUND_PCM_READ_CHANNELS: + ret = pcm_channels; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + ret = pcm_set_bits(val); + break; + + case SOUND_PCM_READ_BITS: + ret = pcm_bits; + break; + + default: + return -EINVAL; + } + return put_user(ret, (int *)arg); +} + +static void pas_audio_reset(int dev) +{ + DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n")); + + pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); /* Disable PCM */ +} + +static int pas_audio_open(int dev, int mode) +{ + int err; + unsigned long flags; + + DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode)); + + save_flags(flags); + cli(); + if (pcm_busy) + { + restore_flags(flags); + return -EBUSY; + } + pcm_busy = 1; + restore_flags(flags); + + if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0) + return err; + + + pcm_count = 0; + open_mode = mode; + + return 0; +} + +static void pas_audio_close(int dev) +{ + unsigned long flags; + + DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n")); + + save_flags(flags); + cli(); + + pas_audio_reset(dev); + pas_remove_intr(PAS_PCM_INTRBITS); + pcm_mode = PCM_NON; + + pcm_busy = 0; + restore_flags(flags); +} + +static void pas_audio_output_block(int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags, cnt; + + DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count)); + + cnt = count; + if (audio_devs[dev]->dmap_out->dma > 3) + cnt >>= 1; + + if (audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == pcm_count) + return; + + save_flags(flags); + cli(); + + pas_write(pas_read(0xF8A) & ~0x40, + 0xF8A); + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + + if (count != pcm_count) + { + pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A); + pas_write(0x40 | 0x30 | 0x04, 0x138B); + pas_write(count & 0xff, 0x1389); + pas_write((count >> 8) & 0xff, 0x1389); + pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A); + + pcm_count = count; + } + pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); +#ifdef NO_TRIGGER + pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); +#endif + + pcm_mode = PCM_DAC; + + restore_flags(flags); +} + +static void pas_audio_start_input(int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags; + int cnt; + + DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count)); + + cnt = count; + if (audio_devs[dev]->dmap_out->dma > 3) + cnt >>= 1; + + if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE && + intrflag && + cnt == pcm_count) + return; + + save_flags(flags); + cli(); + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + + if (count != pcm_count) + { + pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A); + pas_write(0x40 | 0x30 | 0x04, 0x138B); + pas_write(count & 0xff, 0x1389); + pas_write((count >> 8) & 0xff, 0x1389); + pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A); + + pcm_count = count; + } + pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); +#ifdef NO_TRIGGER + pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); +#endif + + pcm_mode = PCM_ADC; + + restore_flags(flags); +} + +#ifndef NO_TRIGGER +static void pas_audio_trigger(int dev, int state) +{ + unsigned long flags; + + save_flags(flags); + cli(); + state &= open_mode; + + if (state & PCM_ENABLE_OUTPUT) + pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); + else if (state & PCM_ENABLE_INPUT) + pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); + else + pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); + + restore_flags(flags); +} +#endif + +static int pas_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + pas_audio_reset(dev); + return 0; +} + +static int pas_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + pas_audio_reset(dev); + return 0; +} + +static struct audio_driver pas_audio_driver = +{ + owner: THIS_MODULE, + open: pas_audio_open, + close: pas_audio_close, + output_block: pas_audio_output_block, + start_input: pas_audio_start_input, + ioctl: pas_audio_ioctl, + prepare_for_input: pas_audio_prepare_for_input, + prepare_for_output: pas_audio_prepare_for_output, + halt_io: pas_audio_reset, + trigger: pas_audio_trigger +}; + +void __init pas_pcm_init(struct address_info *hw_config) +{ + DEB(printk("pas2_pcm.c: long pas_pcm_init()\n")); + + pcm_bitsok = 8; + if (pas_read(0xEF8B) & 0x08) + pcm_bitsok |= 16; + + pcm_set_speed(DSP_DEFAULT_SPEED); + + if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + "Pro Audio Spectrum", + &pas_audio_driver, + sizeof(struct audio_driver), + DMA_AUTOMODE, + AFMT_U8 | AFMT_S16_LE, + NULL, + hw_config->dma, + hw_config->dma)) < 0) + printk(KERN_WARNING "PAS16: Too many PCM devices available\n"); +} + +void pas_pcm_interrupt(unsigned char status, int cause) +{ + if (cause == 1) + { + /* + * Halt the PCM first. Otherwise we don't have time to start a new + * block before the PCM chip proceeds to the next sample + */ + + if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE)) + pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); + + switch (pcm_mode) + { + case PCM_DAC: + DMAbuf_outputintr(pas_audiodev, 1); + break; + + case PCM_ADC: + DMAbuf_inputintr(pas_audiodev); + break; + + default: + printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n"); + } + } +} diff -Nru a/sound/oss/pss.c b/sound/oss/pss.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/pss.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1238 @@ +/* + * sound/pss.c + * + * The low level driver for the Personal Sound System (ECHO ESC614). + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) + * Alan Cox modularisation, clean up. + * + * 98-02-21: Vladimir Michl + * Added mixer device for Beethoven ADSP-16 (master volume, + * bass, treble, synth), only for speakers. + * Fixed bug in pss_write (exchange parameters) + * Fixed config port of SB + * Requested two regions for PSS (PSS mixer, PSS config) + * Modified pss_download_boot + * To probe_pss_mss added test for initialize AD1848 + * 98-05-28: Vladimir Michl + * Fixed computation of mixer volumes + * 04-05-1999: Anthony Barbachan + * Added code that allows the user to enable his cdrom and/or + * joystick through the module parameters pss_cdrom_port and + * pss_enable_joystick. pss_cdrom_port takes a port address as its + * argument. pss_enable_joystick takes either a 0 or a non-0 as its + * argument. + * 04-06-1999: Anthony Barbachan + * Separated some code into new functions for easier reuse. + * Cleaned up and streamlined new code. Added code to allow a user + * to only use this driver for enabling non-sound components + * through the new module parameter pss_no_sound (flag). Added + * code that would allow a user to decide whether the driver should + * reset the configured hardware settings for the PSS board through + * the module parameter pss_keep_settings (flag). This flag will + * allow a user to free up resources in use by this card if needbe, + * furthermore it allows him to use this driver to just enable the + * emulations and then be unloaded as it is no longer needed. Both + * new settings are only available to this driver if compiled as a + * module. The default settings of all new parameters are set to + * load the driver as it did in previous versions. + * 04-07-1999: Anthony Barbachan + * Added module parameter pss_firmware to allow the user to tell + * the driver where the fireware file is located. The default + * setting is the previous hardcoded setting "/etc/sound/pss_synth". + * 00-03-03: Christoph Hellwig + * Adapted to module_init/module_exit + * 11-10-2000: Bartlomiej Zolnierkiewicz + * Added __init to probe_pss(), attach_pss() and probe_pss_mpu() + * 02-Jan-2001: Chris Rankin + * Specify that this module owns the coprocessor + */ + + +#include +#include +#include + +#include "sound_config.h" +#include "sound_firmware.h" + +#include "ad1848.h" +#include "mpu401.h" + +/* + * PSS registers. + */ +#define REG(x) (devc->base+x) +#define PSS_DATA 0 +#define PSS_STATUS 2 +#define PSS_CONTROL 2 +#define PSS_ID 4 +#define PSS_IRQACK 4 +#define PSS_PIO 0x1a + +/* + * Config registers + */ +#define CONF_PSS 0x10 +#define CONF_WSS 0x12 +#define CONF_SB 0x14 +#define CONF_CDROM 0x16 +#define CONF_MIDI 0x18 + +/* + * Status bits. + */ +#define PSS_FLAG3 0x0800 +#define PSS_FLAG2 0x0400 +#define PSS_FLAG1 0x1000 +#define PSS_FLAG0 0x0800 +#define PSS_WRITE_EMPTY 0x8000 +#define PSS_READ_FULL 0x4000 + +/* + * WSS registers + */ +#define WSS_INDEX 4 +#define WSS_DATA 5 + +/* + * WSS status bits + */ +#define WSS_INITIALIZING 0x80 +#define WSS_AUTOCALIBRATION 0x20 + +#define NO_WSS_MIXER -1 + +#include "coproc.h" + +#include "pss_boot.h" + +/* If compiled into kernel, it enable or disable pss mixer */ +#ifdef CONFIG_PSS_MIXER +static unsigned char pss_mixer = 1; +#else +static unsigned char pss_mixer = 0; +#endif + + +typedef struct pss_mixerdata { + unsigned int volume_l; + unsigned int volume_r; + unsigned int bass; + unsigned int treble; + unsigned int synth; +} pss_mixerdata; + +typedef struct pss_confdata { + int base; + int irq; + int dma; + int *osp; + pss_mixerdata mixer; + int ad_mixer_dev; +} pss_confdata; + +static pss_confdata pss_data; +static pss_confdata *devc = &pss_data; + +static int pss_initialized = 0; +static int nonstandard_microcode = 0; +static int pss_cdrom_port = -1; /* Parameter for the PSS cdrom port */ +static int pss_enable_joystick = 0;/* Parameter for enabling the joystick */ + +static void pss_write(pss_confdata *devc, int data) +{ + int i, limit; + + limit = jiffies + HZ/10; /* The timeout is 0.1 seconds */ + /* + * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes + * called while interrupts are disabled. This means that the timer is + * disabled also. However the timeout situation is a abnormal condition. + * Normally the DSP should be ready to accept commands after just couple of + * loops. + */ + + for (i = 0; i < 5000000 && time_before(jiffies, limit); i++) + { + if (inw(REG(PSS_STATUS)) & PSS_WRITE_EMPTY) + { + outw(data, REG(PSS_DATA)); + return; + } + } + printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data); +} + +int __init probe_pss(struct address_info *hw_config) +{ + unsigned short id; + int irq, dma; + + devc->base = hw_config->io_base; + irq = devc->irq = hw_config->irq; + dma = devc->dma = hw_config->dma; + devc->osp = hw_config->osp; + + if (devc->base != 0x220 && devc->base != 0x240) + if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */ + return 0; + + if (check_region(devc->base, 0x19 /*16*/)) { + printk(KERN_ERR "PSS: I/O port conflict\n"); + return 0; + } + id = inw(REG(PSS_ID)); + if ((id >> 8) != 'E') { + printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); + return 0; + } + return 1; +} + +static int set_irq(pss_confdata * devc, int dev, int irq) +{ + static unsigned short irq_bits[16] = + { + 0x0000, 0x0000, 0x0000, 0x0008, + 0x0000, 0x0010, 0x0000, 0x0018, + 0x0000, 0x0020, 0x0028, 0x0030, + 0x0038, 0x0000, 0x0000, 0x0000 + }; + + unsigned short tmp, bits; + + if (irq < 0 || irq > 15) + return 0; + + tmp = inw(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */ + + if ((bits = irq_bits[irq]) == 0 && irq != 0) + { + printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq); + return 0; + } + outw(tmp | bits, REG(dev)); + return 1; +} + +static int set_io_base(pss_confdata * devc, int dev, int base) +{ + unsigned short tmp = inw(REG(dev)) & 0x003f; + unsigned short bits = (base & 0x0ffc) << 4; + + outw(bits | tmp, REG(dev)); + + return 1; +} + +static int set_dma(pss_confdata * devc, int dev, int dma) +{ + static unsigned short dma_bits[8] = + { + 0x0001, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0006, 0x0007 + }; + + unsigned short tmp, bits; + + if (dma < 0 || dma > 7) + return 0; + + tmp = inw(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */ + + if ((bits = dma_bits[dma]) == 0 && dma != 4) + { + printk(KERN_ERR "PSS: Invalid DMA %d\n", dma); + return 0; + } + outw(tmp | bits, REG(dev)); + return 1; +} + +static int pss_reset_dsp(pss_confdata * devc) +{ + unsigned long i, limit = jiffies + HZ/10; + + outw(0x2000, REG(PSS_CONTROL)); + for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) + inw(REG(PSS_CONTROL)); + outw(0x0000, REG(PSS_CONTROL)); + return 1; +} + +static int pss_put_dspword(pss_confdata * devc, unsigned short word) +{ + int i, val; + + for (i = 0; i < 327680; i++) + { + val = inw(REG(PSS_STATUS)); + if (val & PSS_WRITE_EMPTY) + { + outw(word, REG(PSS_DATA)); + return 1; + } + } + return 0; +} + +static int pss_get_dspword(pss_confdata * devc, unsigned short *word) +{ + int i, val; + + for (i = 0; i < 327680; i++) + { + val = inw(REG(PSS_STATUS)); + if (val & PSS_READ_FULL) + { + *word = inw(REG(PSS_DATA)); + return 1; + } + } + return 0; +} + +static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags) +{ + int i, limit, val, count; + + if (flags & CPF_FIRST) + { +/*_____ Warn DSP software that a boot is coming */ + outw(0x00fe, REG(PSS_DATA)); + + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && time_before(jiffies, limit); i++) + if (inw(REG(PSS_DATA)) == 0x5500) + break; + + outw(*block++, REG(PSS_DATA)); + pss_reset_dsp(devc); + } + count = 1; + while ((flags&CPF_LAST) || count= size && flags & CPF_LAST) + break; + else + { + printk("\n"); + printk(KERN_ERR "PSS: Download timeout problems, byte %d=%d\n", count, size); + return 0; + } + } +/*_____ Send the next byte */ + if (count >= size) + { + /* If not data in block send 0xffff */ + outw (0xffff, REG (PSS_DATA)); + } + else + { + /*_____ Send the next byte */ + outw (*block++, REG (PSS_DATA)); + }; + count++; + } + + if (flags & CPF_LAST) + { +/*_____ Why */ + outw(0, REG(PSS_DATA)); + + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && (limit - jiffies >= 0); i++) + val = inw(REG(PSS_STATUS)); + + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) + { + val = inw(REG(PSS_STATUS)); + if (val & 0x4000) + break; + } + + /* now read the version */ + for (i = 0; i < 32000; i++) + { + val = inw(REG(PSS_STATUS)); + if (val & PSS_READ_FULL) + break; + } + if (i == 32000) + return 0; + + val = inw(REG(PSS_DATA)); + /* printk( "", val/16, val % 16); */ + } + return 1; +} + +/* Mixer */ +static void set_master_volume(pss_confdata *devc, int left, int right) +{ + static unsigned char log_scale[101] = { + 0xdb, 0xe0, 0xe3, 0xe5, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, + 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xff, 0xff, 0xff + }; + pss_write(devc, 0x0010); + pss_write(devc, log_scale[left] | 0x0000); + pss_write(devc, 0x0010); + pss_write(devc, log_scale[right] | 0x0100); +} + +static void set_synth_volume(pss_confdata *devc, int volume) +{ + int vol = ((0x8000*volume)/100L); + pss_write(devc, 0x0080); + pss_write(devc, vol); + pss_write(devc, 0x0081); + pss_write(devc, vol); +} + +static void set_bass(pss_confdata *devc, int level) +{ + int vol = (int)(((0xfd - 0xf0) * level)/100L) + 0xf0; + pss_write(devc, 0x0010); + pss_write(devc, vol | 0x0200); +}; + +static void set_treble(pss_confdata *devc, int level) +{ + int vol = (((0xfd - 0xf0) * level)/100L) + 0xf0; + pss_write(devc, 0x0010); + pss_write(devc, vol | 0x0300); +}; + +static void pss_mixer_reset(pss_confdata *devc) +{ + set_master_volume(devc, 33, 33); + set_bass(devc, 50); + set_treble(devc, 50); + set_synth_volume(devc, 30); + pss_write (devc, 0x0010); + pss_write (devc, 0x0800 | 0xce); /* Stereo */ + + if(pss_mixer) + { + devc->mixer.volume_l = devc->mixer.volume_r = 33; + devc->mixer.bass = 50; + devc->mixer.treble = 50; + devc->mixer.synth = 30; + } +} + +static void arg_to_volume_mono(unsigned int volume, int *aleft) +{ + int left; + + left = volume & 0x00ff; + if (left > 100) + left = 100; + *aleft = left; +} + +static void arg_to_volume_stereo(unsigned int volume, int *aleft, int *aright) +{ + arg_to_volume_mono(volume, aleft); + arg_to_volume_mono(volume >> 8, aright); +} + +static int ret_vol_mono(int left) +{ + return ((left << 8) | left); +} + +static int ret_vol_stereo(int left, int right) +{ + return ((right << 8) | left); +} + +static int call_ad_mixer(pss_confdata *devc,unsigned int cmd, caddr_t arg) +{ + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return mixer_devs[devc->ad_mixer_dev]->ioctl(devc->ad_mixer_dev, cmd, arg); + else + return -EINVAL; +} + +static int pss_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + pss_confdata *devc = mixer_devs[dev]->devc; + int cmdf = cmd & 0xff; + + if ((cmdf != SOUND_MIXER_VOLUME) && (cmdf != SOUND_MIXER_BASS) && + (cmdf != SOUND_MIXER_TREBLE) && (cmdf != SOUND_MIXER_SYNTH) && + (cmdf != SOUND_MIXER_DEVMASK) && (cmdf != SOUND_MIXER_STEREODEVS) && + (cmdf != SOUND_MIXER_RECMASK) && (cmdf != SOUND_MIXER_CAPS) && + (cmdf != SOUND_MIXER_RECSRC)) + { + return call_ad_mixer(devc, cmd, arg); + } + + if (((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if (_SIOC_DIR (cmd) & _SIOC_WRITE) + { + switch (cmdf) + { + case SOUND_MIXER_RECSRC: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + { + if (*(int *)arg != 0) + return -EINVAL; + return 0; + } + case SOUND_MIXER_VOLUME: + arg_to_volume_stereo(*(unsigned int *)arg, &devc->mixer.volume_l, + &devc->mixer.volume_r); + set_master_volume(devc, devc->mixer.volume_l, + devc->mixer.volume_r); + return ret_vol_stereo(devc->mixer.volume_l, + devc->mixer.volume_r); + + case SOUND_MIXER_BASS: + arg_to_volume_mono(*(unsigned int *)arg, + &devc->mixer.bass); + set_bass(devc, devc->mixer.bass); + return ret_vol_mono(devc->mixer.bass); + + case SOUND_MIXER_TREBLE: + arg_to_volume_mono(*(unsigned int *)arg, + &devc->mixer.treble); + set_treble(devc, devc->mixer.treble); + return ret_vol_mono(devc->mixer.treble); + + case SOUND_MIXER_SYNTH: + arg_to_volume_mono(*(unsigned int *)arg, + &devc->mixer.synth); + set_synth_volume(devc, devc->mixer.synth); + return ret_vol_mono(devc->mixer.synth); + + default: + return -EINVAL; + } + } + else + { + /* + * Return parameters + */ + switch (cmdf) + { + + case SOUND_MIXER_DEVMASK: + if (call_ad_mixer(devc, cmd, arg) == -EINVAL) + *(int *)arg = 0; /* no mixer devices */ + return (*(int *)arg |= SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH); + + case SOUND_MIXER_STEREODEVS: + if (call_ad_mixer(devc, cmd, arg) == -EINVAL) + *(int *)arg = 0; /* no stereo devices */ + return (*(int *)arg |= SOUND_MASK_VOLUME); + + case SOUND_MIXER_RECMASK: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + return (*(int *)arg = 0); /* no record devices */ + + case SOUND_MIXER_CAPS: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + return (*(int *)arg = SOUND_CAP_EXCL_INPUT); + + case SOUND_MIXER_RECSRC: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + return (*(int *)arg = 0); /* no record source */ + + case SOUND_MIXER_VOLUME: + return (*(int *)arg = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r)); + + case SOUND_MIXER_BASS: + return (*(int *)arg = ret_vol_mono(devc->mixer.bass)); + + case SOUND_MIXER_TREBLE: + return (*(int *)arg = ret_vol_mono(devc->mixer.treble)); + + case SOUND_MIXER_SYNTH: + return (*(int *)arg = ret_vol_mono(devc->mixer.synth)); + default: + return -EINVAL; + } + } +} + +static struct mixer_operations pss_mixer_operations = +{ + owner: THIS_MODULE, + id: "SOUNDPORT", + name: "PSS-AD1848", + ioctl: pss_mixer_ioctl +}; + +void disable_all_emulations(void) +{ + outw(0x0000, REG(CONF_PSS)); /* 0x0400 enables joystick */ + outw(0x0000, REG(CONF_WSS)); + outw(0x0000, REG(CONF_SB)); + outw(0x0000, REG(CONF_MIDI)); + outw(0x0000, REG(CONF_CDROM)); +} + +void configure_nonsound_components(void) +{ + /* Configure Joystick port */ + + if(pss_enable_joystick) + { + outw(0x0400, REG(CONF_PSS)); /* 0x0400 enables joystick */ + printk(KERN_INFO "PSS: joystick enabled.\n"); + } + else + { + printk(KERN_INFO "PSS: joystick port not enabled.\n"); + } + + /* Configure CDROM port */ + + if(pss_cdrom_port == -1) /* If cdrom port enablation wasn't requested */ + { + printk(KERN_INFO "PSS: CDROM port not enabled.\n"); + } + else if(check_region(pss_cdrom_port, 2)) + { + printk(KERN_ERR "PSS: CDROM I/O port conflict.\n"); + } + else if(!set_io_base(devc, CONF_CDROM, pss_cdrom_port)) + { + printk(KERN_ERR "PSS: CDROM I/O port could not be set.\n"); + } + else /* CDROM port successfully configured */ + { + printk(KERN_INFO "PSS: CDROM I/O port set to 0x%x.\n", pss_cdrom_port); + } +} + +void __init attach_pss(struct address_info *hw_config) +{ + unsigned short id; + char tmp[100]; + + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->dma = hw_config->dma; + devc->osp = hw_config->osp; + devc->ad_mixer_dev = NO_WSS_MIXER; + + if (!probe_pss(hw_config)) + return; + + request_region(hw_config->io_base, 0x10, "PSS mixer, SB emulation"); + request_region(hw_config->io_base + 0x10, 0x9, "PSS config"); + + id = inw(REG(PSS_ID)) & 0x00ff; + + /* + * Disable all emulations. Will be enabled later (if required). + */ + + disable_all_emulations(); + +#if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES + if (sound_alloc_dma(hw_config->dma, "PSS")) + { + printk("pss.c: Can't allocate DMA channel.\n"); + return; + } + if (!set_irq(devc, CONF_PSS, devc->irq)) + { + printk("PSS: IRQ allocation error.\n"); + return; + } + if (!set_dma(devc, CONF_PSS, devc->dma)) + { + printk(KERN_ERR "PSS: DMA allocation error\n"); + return; + } +#endif + + configure_nonsound_components(); + pss_initialized = 1; + sprintf(tmp, "ECHO-PSS Rev. %d", id); + conf_printf(tmp, hw_config); +} + +int __init probe_pss_mpu(struct address_info *hw_config) +{ + int timeout; + + if (!pss_initialized) + return 0; + + if (check_region(hw_config->io_base, 2)) + { + printk(KERN_ERR "PSS: MPU I/O port conflict\n"); + return 0; + } + if (!set_io_base(devc, CONF_MIDI, hw_config->io_base)) + { + printk(KERN_ERR "PSS: MIDI base could not be set.\n"); + return 0; + } + if (!set_irq(devc, CONF_MIDI, hw_config->irq)) + { + printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n"); + return 0; + } + if (!pss_synthLen) + { + printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n"); + return 0; + } + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + return 0; + } + + /* + * Finally wait until the DSP algorithm has initialized itself and + * deactivates receive interrupt. + */ + + for (timeout = 900000; timeout > 0; timeout--) + { + if ((inb(hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */ + inb(hw_config->io_base); /* Discard it */ + else + break; /* No more input */ + } + + return probe_mpu401(hw_config); +} + +static int pss_coproc_open(void *dev_info, int sub_device) +{ + switch (sub_device) + { + case COPR_MIDI: + if (pss_synthLen == 0) + { + printk(KERN_ERR "PSS: MIDI synth microcode not available.\n"); + return -EIO; + } + if (nonstandard_microcode) + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + return -EIO; + } + nonstandard_microcode = 0; + break; + + default: + break; + } + return 0; +} + +static void pss_coproc_close(void *dev_info, int sub_device) +{ + return; +} + +static void pss_coproc_reset(void *dev_info) +{ + if (pss_synthLen) + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + } + nonstandard_microcode = 0; +} + +static int download_boot_block(void *dev_info, copr_buffer * buf) +{ + if (buf->len <= 0 || buf->len > sizeof(buf->data)) + return -EINVAL; + + if (!pss_download_boot(devc, buf->data, buf->len, buf->flags)) + { + printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n"); + return -EIO; + } + nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */ + return 0; +} + +static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local) +{ + copr_buffer *buf; + copr_msg *mbuf; + copr_debug_buf dbuf; + unsigned short tmp; + unsigned long flags; + unsigned short *data; + int i, err; + /* printk( "PSS coproc ioctl %x %x %d\n", cmd, arg, local); */ + + switch (cmd) + { + case SNDCTL_COPR_RESET: + pss_coproc_reset(dev_info); + return 0; + + case SNDCTL_COPR_LOAD: + buf = (copr_buffer *) vmalloc(sizeof(copr_buffer)); + if (buf == NULL) + return -ENOSPC; + if (copy_from_user(buf, arg, sizeof(copr_buffer))) { + vfree(buf); + return -EFAULT; + } + err = download_boot_block(dev_info, buf); + vfree(buf); + return err; + + case SNDCTL_COPR_SENDMSG: + mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); + if (mbuf == NULL) + return -ENOSPC; + if (copy_from_user(mbuf, arg, sizeof(copr_msg))) { + vfree(mbuf); + return -EFAULT; + } + data = (unsigned short *)(mbuf->data); + save_flags(flags); + cli(); + for (i = 0; i < mbuf->len; i++) { + if (!pss_put_dspword(devc, *data++)) { + restore_flags(flags); + mbuf->len = i; /* feed back number of WORDs sent */ + err = copy_to_user(arg, mbuf, sizeof(copr_msg)); + vfree(mbuf); + return err ? -EFAULT : -EIO; + } + } + restore_flags(flags); + vfree(mbuf); + return 0; + + case SNDCTL_COPR_RCVMSG: + err = 0; + mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); + if (mbuf == NULL) + return -ENOSPC; + data = (unsigned short *)mbuf->data; + save_flags(flags); + cli(); + for (i = 0; i < sizeof(mbuf->data)/sizeof(unsigned short); i++) { + mbuf->len = i; /* feed back number of WORDs read */ + if (!pss_get_dspword(devc, data++)) { + if (i == 0) + err = -EIO; + break; + } + } + restore_flags(flags); + if (copy_to_user(arg, mbuf, sizeof(copr_msg))) + err = -EFAULT; + vfree(mbuf); + return err; + + case SNDCTL_COPR_RDATA: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d0)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + if (!pss_get_dspword(devc, &tmp)) { + restore_flags(flags); + return -EIO; + } + dbuf.parm1 = tmp; + restore_flags(flags); + if (copy_to_user(arg, &dbuf, sizeof(dbuf))) + return -EFAULT; + return 0; + + case SNDCTL_COPR_WDATA: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d1)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + tmp = (unsigned int)dbuf.parm2 & 0xffff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } + restore_flags(flags); + return 0; + + case SNDCTL_COPR_WCODE: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d3)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + tmp = (unsigned int)dbuf.parm2 & 0x00ff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } + tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } + restore_flags(flags); + return 0; + + case SNDCTL_COPR_RCODE: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d2)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */ + restore_flags(flags); + return -EIO; + } + dbuf.parm1 = tmp << 8; + if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */ + restore_flags(flags); + return -EIO; + } + dbuf.parm1 |= tmp & 0x00ff; + restore_flags(flags); + if (copy_to_user(arg, &dbuf, sizeof(dbuf))) + return -EFAULT; + return 0; + + default: + return -EINVAL; + } + return -EINVAL; +} + +static coproc_operations pss_coproc_operations = +{ + "ADSP-2115", + THIS_MODULE, + pss_coproc_open, + pss_coproc_close, + pss_coproc_ioctl, + pss_coproc_reset, + &pss_data +}; + +static void __init attach_pss_mpu(struct address_info *hw_config) +{ + attach_mpu401(hw_config, THIS_MODULE); /* Slot 1 */ + if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ + midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations; +} + +static int __init probe_pss_mss(struct address_info *hw_config) +{ + volatile int timeout; + + if (!pss_initialized) + return 0; + + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "PSS: WSS I/O port conflicts.\n"); + return 0; + } + if (!set_io_base(devc, CONF_WSS, hw_config->io_base)) + { + printk("PSS: WSS base not settable.\n"); + return 0; + } + if (!set_irq(devc, CONF_WSS, hw_config->irq)) + { + printk("PSS: WSS IRQ allocation error.\n"); + return 0; + } + if (!set_dma(devc, CONF_WSS, hw_config->dma)) + { + printk(KERN_ERR "PSS: WSS DMA allocation error\n"); + return 0; + } + /* + * For some reason the card returns 0xff in the WSS status register + * immediately after boot. Probably MIDI+SB emulation algorithm + * downloaded to the ADSP2115 spends some time initializing the card. + * Let's try to wait until it finishes this task. + */ + for (timeout = 0; timeout < 100000 && (inb(hw_config->io_base + WSS_INDEX) & + WSS_INITIALIZING); timeout++) + ; + + outb((0x0b), hw_config->io_base + WSS_INDEX); /* Required by some cards */ + + for (timeout = 0; (inb(hw_config->io_base + WSS_DATA) & WSS_AUTOCALIBRATION) && + (timeout < 100000); timeout++) + ; + + return probe_ms_sound(hw_config); +} + +static void __init attach_pss_mss(struct address_info *hw_config) +{ + int my_mix = -999; /* gcc shut up */ + + devc->ad_mixer_dev = NO_WSS_MIXER; + if (pss_mixer) + { + if ((my_mix = sound_install_mixer (MIXER_DRIVER_VERSION, + "PSS-SPEAKERS and AD1848 (through MSS audio codec)", + &pss_mixer_operations, + sizeof (struct mixer_operations), + devc)) < 0) + { + printk(KERN_ERR "Could not install PSS mixer\n"); + return; + } + } + pss_mixer_reset(devc); + attach_ms_sound(hw_config, THIS_MODULE); /* Slot 0 */ + + if (hw_config->slots[0] != -1) + { + /* The MSS driver installed itself */ + audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations; + if (pss_mixer && (num_mixers == (my_mix + 2))) + { + /* The MSS mixer installed */ + devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev; + } + } +} + +static inline void __exit unload_pss(struct address_info *hw_config) +{ + release_region(hw_config->io_base, 0x10); + release_region(hw_config->io_base+0x10, 0x9); +} + +static inline void __exit unload_pss_mpu(struct address_info *hw_config) +{ + unload_mpu401(hw_config); +} + +static inline void __exit unload_pss_mss(struct address_info *hw_config) +{ + unload_ms_sound(hw_config); +} + + +static struct address_info cfg; +static struct address_info cfg2; +static struct address_info cfg_mpu; + +static int pss_io __initdata = -1; +static int mss_io __initdata = -1; +static int mss_irq __initdata = -1; +static int mss_dma __initdata = -1; +static int mpu_io __initdata = -1; +static int mpu_irq __initdata = -1; +static int pss_no_sound __initdata = 0; /* Just configure non-sound components */ +static int pss_keep_settings = 1; /* Keep hardware settings at module exit */ +static char *pss_firmware = "/etc/sound/pss_synth"; + +MODULE_PARM(pss_io, "i"); +MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)"); +MODULE_PARM(mss_io, "i"); +MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)"); +MODULE_PARM(mss_irq, "i"); +MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)"); +MODULE_PARM(mss_dma, "i"); +MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)"); +MODULE_PARM(mpu_io, "i"); +MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)"); +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)"); +MODULE_PARM(pss_cdrom_port, "i"); +MODULE_PARM_DESC(pss_cdrom_port, "Set the PSS CDROM port i/o base (0x340 or other)"); +MODULE_PARM(pss_enable_joystick, "i"); +MODULE_PARM_DESC(pss_enable_joystick, "Enables the PSS joystick port (1 to enable, 0 to disable)"); +MODULE_PARM(pss_no_sound, "i"); +MODULE_PARM_DESC(pss_no_sound, "Configure sound compoents (0 - no, 1 - yes)"); +MODULE_PARM(pss_keep_settings, "i"); +MODULE_PARM_DESC(pss_keep_settings, "Keep hardware setting at driver unloading (0 - no, 1 - yes)"); +MODULE_PARM(pss_firmware, "s"); +MODULE_PARM_DESC(pss_firmware, "Location of the firmware file (default - /etc/sound/pss_synth)"); +MODULE_PARM(pss_mixer, "b"); +MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards."); +MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl"); +MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards).\n"); +MODULE_LICENSE("GPL"); + + +static int fw_load = 0; +static int pssmpu = 0, pssmss = 0; + +/* + * Load a PSS sound card module + */ + +static int __init init_pss(void) +{ + + if(pss_no_sound) /* If configuring only nonsound components */ + { + cfg.io_base = pss_io; + if(!probe_pss(&cfg)) + return -ENODEV; + printk(KERN_INFO "ECHO-PSS Rev. %d\n", inw(REG(PSS_ID)) & 0x00ff); + printk(KERN_INFO "PSS: loading in no sound mode.\n"); + disable_all_emulations(); + configure_nonsound_components(); + return 0; + } + + cfg.io_base = pss_io; + + cfg2.io_base = mss_io; + cfg2.irq = mss_irq; + cfg2.dma = mss_dma; + + cfg_mpu.io_base = mpu_io; + cfg_mpu.irq = mpu_irq; + + if (cfg.io_base == -1 || cfg2.io_base == -1 || cfg2.irq == -1 || cfg.dma == -1) { + printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n"); + return -EINVAL; + } + + if (!pss_synth) { + fw_load = 1; + pss_synthLen = mod_firmware_load(pss_firmware, (void *) &pss_synth); + } + if (!probe_pss(&cfg)) + return -ENODEV; + attach_pss(&cfg); + /* + * Attach stuff + */ + if (probe_pss_mpu(&cfg_mpu)) { + pssmpu = 1; + attach_pss_mpu(&cfg_mpu); + } + if (probe_pss_mss(&cfg2)) { + pssmss = 1; + attach_pss_mss(&cfg2); + } + + return 0; +} + +static void __exit cleanup_pss(void) +{ + if(!pss_no_sound) + { + if(fw_load && pss_synth) + vfree(pss_synth); + if(pssmss) + unload_pss_mss(&cfg2); + if(pssmpu) + unload_pss_mpu(&cfg_mpu); + unload_pss(&cfg); + } + + if(!pss_keep_settings) /* Keep hardware settings if asked */ + { + disable_all_emulations(); + printk(KERN_INFO "Resetting PSS sound card configurations.\n"); + } +} + +module_init(init_pss); +module_exit(cleanup_pss); + +#ifndef MODULE +static int __init setup_pss(char *str) +{ + /* io, mss_io, mss_irq, mss_dma, mpu_io, mpu_irq */ + int ints[7]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + pss_io = ints[1]; + mss_io = ints[2]; + mss_irq = ints[3]; + mss_dma = ints[4]; + mpu_io = ints[5]; + mpu_irq = ints[6]; + + return 1; +} + +__setup("pss=", setup_pss); +#endif diff -Nru a/sound/oss/rme96xx.c b/sound/oss/rme96xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/rme96xx.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,1565 @@ +/* (C) 2000 Guenter Geiger + with copy/pastes from the driver of Winfried Ritsch + based on es1370.c + + + + * 10 Jan 2001: 0.1 initial version + * 19 Jan 2001: 0.2 fixed bug in select() + * 27 Apr 2001: 0.3 more than one card usable + * 11 May 2001: 0.4 fixed for SMP, included into kernel source tree + * 17 May 2001: 0.5 draining code didn't work on new cards + * 18 May 2001: 0.6 remove synchronize_irq() call + +TODO: + - test more than one card --- done + - check for pci IOREGION (see es1370) in rme96xx_probe ?? + - error detection + - mmap interface + - mixer mmap interface + - mixer ioctl + - get rid of noise upon first open (why ??) + - allow multiple open(at least for read) + - allow multiple open for non overlapping regions + - recheck the multiple devices part (offsets of different devices, etc) + - do decent draining in _release --- done + - SMP support +*/ + +#ifndef RMEVERSION +#define RMEVERSION "0.6" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rme96xx.h" + +#define NR_DEVICE 2 + +static int devices = 1; +MODULE_PARM(devices, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(devices, "number of dsp devices allocated by the driver"); + + +MODULE_AUTHOR("Guenter Geiger, geiger@debian.org"); +MODULE_DESCRIPTION("RME9652/36 \"Hammerfall\" Driver"); +MODULE_LICENSE("GPL"); + + +#ifdef DEBUG +#define DBG(x) printk("RME_DEBUG:");x +#define COMM(x) printk("RME_COMM: " x "\n"); +#else +#define DBG(x) while (0) {} +#define COMM(x) +#endif + +/*-------------------------------------------------------------------------- + Preporcessor Macros and Definitions + --------------------------------------------------------------------------*/ + +#define RME96xx_MAGIC 0x6473 + +/* Registers-Space in offsets from base address with 16MByte size */ + +#define RME96xx_IO_EXTENT 16l*1024l*1024l +#define RME96xx_CHANNELS_PER_CARD 26 + +/* Write - Register */ + +/* 0,4,8,12,16,20,24,28 ... hardware init (erasing fifo-pointer intern) */ +#define RME96xx_num_of_init_regs 8 + +#define RME96xx_init_buffer (0/4) +#define RME96xx_play_buffer (32/4) /* pointer to 26x64kBit RAM from mainboard */ +#define RME96xx_rec_buffer (36/4) /* pointer to 26x64kBit RAM from mainboard */ +#define RME96xx_control_register (64/4) /* exact meaning see below */ +#define RME96xx_irq_clear (96/4) /* irq acknowledge */ +#define RME96xx_time_code (100/4) /* if used with alesis adat */ +#define RME96xx_thru_base (128/4) /* 132...228 Thru for 26 channels */ +#define RME96xx_thru_channels RME96xx_CHANNELS_PER_CARD + +/* Read Register */ + +#define RME96xx_status_register 0 /* meaning see below */ + + + +/* Status Register: */ +/* ------------------------------------------------------------------------ */ +#define RME96xx_IRQ 0x0000001 /* IRQ is High if not reset by RMExx_irq_clear */ +#define RME96xx_lock_2 0x0000002 /* ADAT 3-PLL: 1=locked, 0=unlocked */ +#define RME96xx_lock_1 0x0000004 /* ADAT 2-PLL: 1=locked, 0=unlocked */ +#define RME96xx_lock_0 0x0000008 /* ADAT 1-PLL: 1=locked, 0=unlocked */ + +#define RME96xx_fs48 0x0000010 /* sample rate 0 ...44.1/88.2, 1 ... 48/96 Khz */ +#define RME96xx_wsel_rd 0x0000020 /* if Word-Clock is used and valid then 1 */ +#define RME96xx_buf_pos1 0x0000040 /* Bit 6..15 : Position of buffer-pointer in 64Bytes-blocks */ +#define RME96xx_buf_pos2 0x0000080 /* resolution +/- 1 64Byte/block (since 64Bytes bursts) */ + +#define RME96xx_buf_pos3 0x0000100 /* 10 bits = 1024 values */ +#define RME96xx_buf_pos4 0x0000200 /* if we mask off the first 6 bits, we can take the status */ +#define RME96xx_buf_pos5 0x0000400 /* register as sample counter in the hardware buffer */ +#define RME96xx_buf_pos6 0x0000800 + +#define RME96xx_buf_pos7 0x0001000 +#define RME96xx_buf_pos8 0x0002000 +#define RME96xx_buf_pos9 0x0004000 +#define RME96xx_buf_pos10 0x0008000 + +#define RME96xx_sync_2 0x0010000 /* if ADAT-IN3 synced to system clock */ +#define RME96xx_sync_1 0x0020000 /* if ADAT-IN2 synced to system clock */ +#define RME96xx_sync_0 0x0040000 /* if ADAT-IN1 synced to system clock */ +#define RME96xx_DS_rd 0x0080000 /* 1=Double Speed, 0=Normal Speed */ + +#define RME96xx_tc_busy 0x0100000 /* 1=time-code copy in progress (960ms) */ +#define RME96xx_tc_out 0x0200000 /* time-code out bit */ +#define RME96xx_F_0 0x0400000 /* 000=64kHz, 100=88.2kHz, 011=96kHz */ +#define RME96xx_F_1 0x0800000 /* 111=32kHz, 110=44.1kHz, 101=48kHz, */ + +#define RME96xx_F_2 0x1000000 /* od external Crystal Chip if ERF=1*/ +#define RME96xx_ERF 0x2000000 /* Error-Flag of SDPIF Receiver (1=No Lock)*/ +#define RME96xx_buffer_id 0x4000000 /* toggles by each interrupt on rec/play */ +#define RME96xx_tc_valid 0x8000000 /* 1 = a signal is detected on time-code input */ + +/* Status Register Fields */ + +#define RME96xx_lock (RME96xx_lock_0|RME96xx_lock_1|RME96xx_lock_2) +#define RME96xx_buf_pos 0x000FFC0 +#define RME96xx_sync (RME96xx_sync_0|RME96xx_sync_1|RME96xx_sync_2) +#define RME96xx_F (RME96xx_F_0|RME96xx_F_1|RME96xx_F_2) + + + +/* Control-Register: */ +/*--------------------------------------------------------------------------------*/ + +#define RME96xx_start_bit 0x0001 /* start record/play */ +#define RME96xx_latency0 0x0002 /* Bit 0 - Buffer size or latency */ +#define RME96xx_latency1 0x0004 /* Bit 1 - Buffer size or latency */ +#define RME96xx_latency2 0x0008 /* Bit 2 - Buffer size or latency */ + +#define RME96xx_Master 0x0010 /* Clock Mode Master=1,Slave/Auto=0 */ +#define RME96xx_IE 0x0020 /* Interupt Enable */ +#define RME96xx_freq 0x0040 /* samplerate 0=44.1/88.2, 1=48/96 kHz*/ + + +#define RME96xx_DS 0x0100 /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */ +#define RME96xx_PRO 0x0200 /* spdif 0=consumer, 1=professional Mode*/ +#define RME96xx_EMP 0x0400 /* spdif Emphasis 0=None, 1=ON */ +#define RME96xx_Dolby 0x0800 /* spdif Non-audio bit 1=set, 0=unset */ + +#define RME96xx_opt_out 0x1000 /* Use 1st optical OUT as SPDIF: 1=yes,0=no */ +#define RME96xx_wsel 0x2000 /* use Wordclock as sync (overwrites master)*/ +#define RME96xx_inp_0 0x4000 /* SPDIF-IN: 00=optical (ADAT1), */ +#define RME96xx_inp_1 0x8000 /* 01=koaxial (Cinch), 10=Internal CDROM*/ + +#define RME96xx_SyncRef0 0x10000 /* preferred sync-source in autosync */ +#define RME96xx_SyncRef1 0x20000 /* 00=ADAT1,01=ADAT2,10=ADAT3,11=SPDIF */ + + +#define RME96xx_ctrl_init (RME96xx_latency0 |\ + RME96xx_Master |\ + RME96xx_inp_1) + + + +/* Control register fields and shortcuts */ + +#define RME96xx_latency (RME96xx_latency0|RME96xx_latency1|RME96xx_latency2) +#define RME96xx_inp (RME96xx_inp_0|RME96xx_inp_1) +#define RME96xx_SyncRef (RME96xx_SyncRef0|RME96xx_SyncRef1) +/* latency = 512Bytes * 2^n, where n is made from Bit3 ... Bit0 */ + +#define RME96xx_SET_LATENCY(x) (((x)&0x7)<<1) +#define RME96xx_GET_LATENCY(x) (((x)>>1)&0x7) +#define RME96xx_SET_inp(x) (((x)&0x3)<<14) +#define RME96xx_GET_inp(x) (((x)>>14)&0x3) +#define RME96xx_SET_SyncRef(x) (((x)&0x3)<<17) +#define RME96xx_GET_SyncRef(x) (((x)>>17)&0x3) + + +/* buffer sizes */ +#define RME96xx_BYTES_PER_SAMPLE 4 /* sizeof(u32) */ +#define RME_16K 16*1024 + +#define RME96xx_DMA_MAX_SAMPLES (RME_16K) +#define RME96xx_DMA_MAX_SIZE (RME_16K * RME96xx_BYTES_PER_SAMPLE) +#define RME96xx_DMA_MAX_SIZE_ALL (RME96xx_DMA_MAX_SIZE * RME96xx_CHANNELS_PER_CARD) + +#define RME96xx_NUM_OF_FRAGMENTS 2 +#define RME96xx_FRAGMENT_MAX_SIZE (RME96xx_DMA_MAX_SIZE/2) +#define RME96xx_FRAGMENT_MAX_SAMPLES (RME96xx_DMA_MAX_SAMPLES/2) +#define RME96xx_MAX_LATENCY 7 /* 16k samples */ + + +#define RME96xx_MAX_DEVS 4 /* we provide some OSS stereodevs */ + +#define RME_MESS "rme96xx:" +/*------------------------------------------------------------------------ + Types, struct and function declarations + ------------------------------------------------------------------------*/ + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT RME_MESS" invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != RME96xx_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + + +static struct file_operations rme96xx_audio_fops; +static struct file_operations rme96xx_mixer_fops; +static int numcards; + +typedef int32_t raw_sample_t; + +typedef struct _rme96xx_info { + + /* hardware settings */ + int magic; + struct pci_dev * pcidev; /* pci_dev structure */ + unsigned long *iobase; + unsigned int irq; + + /* list of rme96xx devices */ + struct list_head devs; + + spinlock_t lock; + + u32 *recbuf; /* memory for rec buffer */ + u32 *playbuf; /* memory for play buffer */ + + u32 control_register; + + u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */ + + int open_count; + + + int rate; + int latency; + unsigned int fragsize; + int started; + + int hwptr; /* can be negativ because of pci burst offset */ + unsigned int hwbufid; /* set by interrupt, buffer which is written/read now */ + + struct dmabuf { + + unsigned int format; + int formatshift; + int inchannels; /* number of channels for device */ + int outchannels; /* number of channels for device */ + int mono; /* if true, we play mono on 2 channels */ + int inoffset; /* which channel is considered the first one */ + int outoffset; + + /* state */ + int opened; /* open() made */ + int started; /* first write/read */ + int mmapped; /* mmap */ + int open_mode; + + struct _rme96xx_info *s; + + /* pointer to read/write position in buffer */ + unsigned readptr; + unsigned writeptr; + + unsigned error; /* over/underruns cleared on sync again */ + + /* waiting and locking */ + wait_queue_head_t wait; + struct semaphore open_sem; + wait_queue_head_t open_wait; + + } dma[RME96xx_MAX_DEVS]; + + int dspnum[RME96xx_MAX_DEVS]; /* register with sound subsystem */ + int mixer; /* register with sound subsystem */ +} rme96xx_info; + + +/* fiddling with the card (first level hardware control) */ + +inline void rme96xx_set_ctrl(rme96xx_info* s,int mask) +{ + + s->control_register|=mask; + writel(s->control_register,s->iobase + RME96xx_control_register); + +} + +inline void rme96xx_unset_ctrl(rme96xx_info* s,int mask) +{ + + s->control_register&=(~mask); + writel(s->control_register,s->iobase + RME96xx_control_register); + +} + + + +/* the hwbuf in the status register seems to have some jitter, to get rid of + it, we first only let the numbers grow, to be on the secure side we + subtract a certain amount RME96xx_BURSTBYTES from the resulting number */ + +/* the function returns the hardware pointer in bytes */ +#define RME96xx_BURSTBYTES -64 /* bytes by which hwptr could be off */ + +inline int rme96xx_gethwptr(rme96xx_info* s,int exact) +{ + long flags; + if (exact) { + unsigned int hwp; +/* the hwptr seems to be rather unreliable :(, so we don't use it */ + spin_lock_irqsave(&s->lock,flags); + + hwp = readl(s->iobase + RME96xx_status_register) & 0xffc0; + s->hwptr = (hwp < s->hwptr) ? s->hwptr : hwp; +// s->hwptr = hwp; + + spin_unlock_irqrestore(&s->lock,flags); + return (s->hwptr+RME96xx_BURSTBYTES) & ((s->fragsize<<1)-1); + } + return (s->hwbufid ? s->fragsize : 0); +} + +inline void rme96xx_setlatency(rme96xx_info* s,int l) +{ + s->latency = l; + s->fragsize = 1<<(8+l); + rme96xx_unset_ctrl(s,RME96xx_latency); + rme96xx_set_ctrl(s,RME96xx_SET_LATENCY(l)); +} + + +static void rme96xx_clearbufs(struct dmabuf* dma) +{ + int i,j; + unsigned long flags; + + /* clear dmabufs */ + for(i=0;ioutchannels + dma->mono;j++) + memset(&dma->s->playbuf[(dma->outoffset + j)*RME96xx_DMA_MAX_SAMPLES], + 0, RME96xx_DMA_MAX_SIZE); + } + spin_lock_irqsave(&dma->s->lock,flags); + dma->writeptr = 0; + dma->readptr = 0; + spin_unlock_irqrestore(&dma->s->lock,flags); +} + +static int rme96xx_startcard(rme96xx_info *s,int stop) +{ + int i; + long flags; + + COMM ("startcard"); + if(s->control_register & RME96xx_IE){ + /* disable interrupt first */ + + rme96xx_unset_ctrl( s,RME96xx_start_bit ); + udelay(10); + rme96xx_unset_ctrl( s,RME96xx_IE); + spin_lock_irqsave(&s->lock,flags); /* timing is critical */ + s->started = 0; + spin_unlock_irqrestore(&s->lock,flags); + if (stop) { + COMM("Sound card stopped"); + return 1; + } + } + COMM ("interupt disabled"); + /* first initialize all pointers on card */ + for(i=0;iiobase + i); + udelay(10); /* ?? */ + } + COMM ("regs cleaned"); + + spin_lock_irqsave(&s->lock,flags); /* timing is critical */ + udelay(10); + s->started = 1; + s->hwptr = 0; + spin_unlock_irqrestore(&s->lock,flags); + + rme96xx_set_ctrl( s, RME96xx_IE | RME96xx_start_bit); + + + COMM("Sound card started"); + + return 1; +} + + +inline int rme96xx_getospace(struct dmabuf * dma, unsigned int hwp) +{ + int cnt; + int swptr; + unsigned long flags; + + spin_lock_irqsave(&dma->s->lock,flags); + swptr = dma->writeptr; + cnt = (hwp - swptr); + + if (cnt < 0) { + cnt = ((dma->s->fragsize<<1) - swptr); + } + spin_unlock_irqrestore(&dma->s->lock,flags); + return cnt; +} + +inline int rme96xx_getispace(struct dmabuf * dma, unsigned int hwp) +{ + int cnt; + int swptr; + unsigned long flags; + + spin_lock_irqsave(&dma->s->lock,flags); + swptr = dma->readptr; + cnt = (hwp - swptr); + + if (cnt < 0) { + cnt = ((dma->s->fragsize<<1) - swptr); + } + spin_unlock_irqrestore(&dma->s->lock,flags); + return cnt; +} + + +inline int rme96xx_copyfromuser(struct dmabuf* dma,const char* buffer,int count,int hop) +{ + int swptr = dma->writeptr; + switch (dma->format) { + case AFMT_S32_BLOCKED: + { + char* buf = (char*)buffer; + int cnt = count/dma->outchannels; + int i; + for (i=0;i < dma->outchannels;i++) { + char* hwbuf =(char*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=swptr; + + if (copy_from_user(hwbuf,buf, cnt)) + return -1; + buf+=hop; + } + swptr+=cnt; + break; + } + case AFMT_S16_LE: + { + int i,j; + int cnt = count/dma->outchannels; + for (i=0;i < dma->outchannels + dma->mono;i++) { + short* sbuf = (short*)buffer + i*(!dma->mono); + short* hwbuf =(short*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=(swptr>>1); + for (j=0;j<(cnt>>1);j++) { + hwbuf++; /* skip the low 16 bits */ + __get_user(*hwbuf++,sbuf++); + sbuf+=(dma->outchannels-1); + } + } + swptr += (cnt<<1); + break; + } + default: + printk(RME_MESS" unsupported format\n"); + return -1; + } /* switch */ + + swptr&=((dma->s->fragsize<<1) -1); + dma->writeptr = swptr; + + return 0; +} + +/* The count argument is the number of bytes */ +inline int rme96xx_copytouser(struct dmabuf* dma,const char* buffer,int count,int hop) +{ + int swptr = dma->readptr; + switch (dma->format) { + case AFMT_S32_BLOCKED: + { + char* buf = (char*)buffer; + int cnt = count/dma->inchannels; + int i; + + for (i=0;i < dma->inchannels;i++) { + char* hwbuf =(char*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=swptr; + + if (copy_to_user(buf,hwbuf,cnt)) + return -1; + buf+=hop; + } + swptr+=cnt; + break; + } + case AFMT_S16_LE: + { + int i,j; + int cnt = count/dma->inchannels; + for (i=0;i < dma->inchannels;i++) { + short* sbuf = (short*)buffer + i; + short* hwbuf =(short*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=(swptr>>1); + for (j=0;j<(cnt>>1);j++) { + hwbuf++; + __put_user(*hwbuf++,sbuf++); + sbuf+=(dma->inchannels-1); + } + } + swptr += (cnt<<1); + break; + } + default: + printk(RME_MESS" unsupported format\n"); + return -1; + } /* switch */ + + swptr&=((dma->s->fragsize<<1) -1); + dma->readptr = swptr; + return 0; +} + + +static void rme96xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int i; + rme96xx_info *s = (rme96xx_info *)dev_id; + struct dmabuf *db; + u32 status; + unsigned long flags; + + status = readl(s->iobase + RME96xx_status_register); + if (!(status & RME96xx_IRQ)) { + return; + } + + spin_lock_irqsave(&s->lock,flags); + writel(0,s->iobase + RME96xx_irq_clear); + + s->hwbufid = (status & RME96xx_buffer_id)>>26; + if ((status & 0xffc0) <= 256) s->hwptr = 0; + for(i=0;idma[i]); + if(db->started > 0) + wake_up(&(db->wait)); + } + spin_unlock_irqrestore(&s->lock,flags); +} + + + +/*---------------------------------------------------------------------------- + PCI detection and module initialization stuff + ----------------------------------------------------------------------------*/ + +void* busmaster_malloc(int size) { + int pg; /* 2 s exponent of memory size */ + char *buf; + + DBG(printk("kernel malloc pages ..\n")); + + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + + buf = (char *) __get_free_pages(GFP_KERNEL | GFP_DMA, pg); + + if (buf) { + struct page* page, *last_page; + + page = virt_to_page(buf); + last_page = virt_to_page(buf + (1 << pg)); + DBG(printk("setting reserved bit\n")); + while (page < last_page) { + SetPageReserved(page); + page++; + } + return buf; + } + DBG(printk("allocated %ld",(long)buf)); + return NULL; +} + +void busmaster_free(void* ptr,int size) { + int pg; + struct page* page, *last_page; + + if (ptr == NULL) + return; + + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + + page = virt_to_page(ptr); + last_page = page + (1 << pg); + while (page < last_page) { + ClearPageReserved(page); + page++; + } + DBG(printk("freeing pages\n")); + free_pages((unsigned long) ptr, pg); + DBG(printk("done\n")); +} + +/* initialize those parts of the info structure which are not pci detectable resources */ + +static int rme96xx_dmabuf_init(rme96xx_info * s,struct dmabuf* dma,int ioffset,int ooffset) { + + init_MUTEX(&dma->open_sem); + init_waitqueue_head(&dma->open_wait); + init_waitqueue_head(&dma->wait); + dma->s = s; + dma->error = 0; + + dma->format = AFMT_S32_BLOCKED; + dma->formatshift = 0; + dma->inchannels = dma->outchannels = 1; + dma->inoffset = ioffset; + dma->outoffset = ooffset; + + dma->opened=0; + dma->started=0; + dma->mmapped=0; + dma->open_mode=0; + dma->mono=0; + + rme96xx_clearbufs(dma); + return 0; +} + + +int rme96xx_init(rme96xx_info* s) +{ + int i; + DBG(printk(__FUNCTION__"\n")); + numcards++; + + s->magic = RME96xx_MAGIC; + + spin_lock_init(&s->lock); + + COMM ("setup busmaster memory") + s->recbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); + s->playbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); + + if (!s->recbuf || !s->playbuf) { + printk(KERN_ERR RME_MESS" Unable to allocate busmaster memory\n"); + return -ENODEV; + } + + COMM ("setting rec and playbuffers") + + writel((u32) virt_to_bus(s->recbuf),s->iobase + RME96xx_rec_buffer); + writel((u32) virt_to_bus(s->playbuf),s->iobase + RME96xx_play_buffer); + + COMM ("initializing control register") + rme96xx_unset_ctrl(s,0xffffffff); + rme96xx_set_ctrl(s,RME96xx_ctrl_init); + + + COMM ("setup devices") + for (i=0;i < devices;i++) { + struct dmabuf * dma = &s->dma[i]; + rme96xx_dmabuf_init(s,dma,2*i,2*i); + } + + s->started = 0; + rme96xx_setlatency(s,7); + + printk(KERN_INFO RME_MESS" card %d initialized\n",numcards); + return 0; +} + + +/* open uses this to figure out which device was opened .. this seems to be + unnecessary complex */ + +static LIST_HEAD(devs); + +static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + int i; + rme96xx_info *s; + + DBG(printk(__FUNCTION__"\n")); + + if (pcidev->irq == 0) + return -1; + if (!pci_dma_supported(pcidev, 0xffffffff)) { + printk(KERN_WARNING RME_MESS" architecture does not support 32bit PCI busmaster DMA\n"); + return -1; + } + if (!(s = kmalloc(sizeof(rme96xx_info), GFP_KERNEL))) { + printk(KERN_WARNING RME_MESS" out of memory\n"); + return -1; + } + memset(s, 0, sizeof(rme96xx_info)); + + s->pcidev = pcidev; + s->iobase = ioremap(pci_resource_start(pcidev, 0),RME96xx_IO_EXTENT); + s->irq = pcidev->irq; + + DBG(printk("remapped iobase: %lx irq %d\n",(long)s->iobase,s->irq)); + + if (pci_enable_device(pcidev)) + goto err_irq; + if (request_irq(s->irq, rme96xx_interrupt, SA_SHIRQ, "es1370", s)) { + printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq); + goto err_irq; + } + + /* initialize the card */ + + i = 0; + if (rme96xx_init(s) < 0) { + printk(KERN_ERR RME_MESS" initialization failed\n"); + goto err_devices; + } + for (i=0;idspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1)) < 0) + goto err_devices; + } + + if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1)) < 0) + goto err_devices; + + pci_set_drvdata(pcidev, s); + pcidev->dma_mask = 0xffffffff; /* ????? */ + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + + DBG(printk("initialization successful\n")); + return 0; + + /* error handler */ + err_devices: + while (i--) + unregister_sound_dsp(s->dspnum[i]); + free_irq(s->irq,s); + err_irq: + kfree(s); + return -1; +} + + +static void __devinit rme96xx_remove(struct pci_dev *dev) +{ + int i; + rme96xx_info *s = pci_get_drvdata(dev); + + if (!s) { + printk(KERN_ERR"device structure not valid\n"); + return ; + } + + if (s->started) rme96xx_startcard(s,0); + + i = devices; + while (i) { + i--; + unregister_sound_dsp(s->dspnum[i]); + } + + unregister_sound_mixer(s->mixer); +/* synchronize_irq(); This call got lost somehow ? */ + free_irq(s->irq,s); + busmaster_free(s->recbuf,RME96xx_DMA_MAX_SIZE_ALL); + busmaster_free(s->playbuf,RME96xx_DMA_MAX_SIZE_ALL); + kfree(s); + pci_set_drvdata(dev, NULL); +} + + +#ifndef PCI_VENDOR_ID_RME +#define PCI_VENDOR_ID_RME 0x10ee +#endif +#ifndef PCI_DEVICE_ID_RME9652 +#define PCI_DEVICE_ID_RME9652 0x3fc4 +#endif +#ifndef PCI_ANY_ID +#define PCI_ANY_ID 0 +#endif + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_RME, PCI_DEVICE_ID_RME9652, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver rme96xx_driver = { + name: "rme96xx", + id_table: id_table, + probe: rme96xx_probe, + remove: rme96xx_remove +}; + +static int __init init_rme96xx(void) +{ + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO RME_MESS" version "RMEVERSION" time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO RME_MESS" reserving %d dsp device(s)\n",devices); + numcards = 0; + return pci_module_init(&rme96xx_driver); +} + +static void __exit cleanup_rme96xx(void) +{ + printk(KERN_INFO RME_MESS" unloading\n"); + pci_unregister_driver(&rme96xx_driver); +} + +module_init(init_rme96xx); +module_exit(cleanup_rme96xx); + + + + + +/*-------------------------------------------------------------------------- + Implementation of file operations +---------------------------------------------------------------------------*/ + +#define RME96xx_FMT (AFMT_S16_LE|AFMT_U8|AFMT_S32_BLOCKED) + + +static int rme96xx_ioctl(struct inode *in, struct file *file, + unsigned int cmd, unsigned long arg) +{ + + + struct dmabuf * dma = (struct dmabuf *)file->private_data; + rme96xx_info *s = dma->s; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val = 0; + + VALIDATE_STATE(s); + + DBG(printk("ioctl %ud\n",cmd)); + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: +#if 0 + if (file->f_mode & FMODE_WRITE) + return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); +#endif + 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: +// rme96xx_clearbufs(dma); + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { +/* generally it's not a problem if we change the speed + if (dma->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) + return -EINVAL; +*/ + spin_lock_irqsave(&s->lock, flags); + + switch (val) { + case 44100: + case 88200: + rme96xx_unset_ctrl(s,RME96xx_freq); + break; + case 48000: + case 96000: + rme96xx_set_ctrl(s,RME96xx_freq); + break; + default: + rme96xx_unset_ctrl(s,RME96xx_freq); + val = 44100; + } + if (val > 50000) + rme96xx_set_ctrl(s,RME96xx_DS); + else + rme96xx_unset_ctrl(s,RME96xx_DS); + s->rate = val; + spin_unlock_irqrestore(&s->lock, flags); + } + DBG(printk("speed set to %d\n",val)); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_STEREO: /* this plays a mono file on two channels */ + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (!val) { + DBG(printk("setting to mono\n")); + dma->mono=1; + dma->inchannels = 1; + dma->outchannels = 1; + } + else { + DBG(printk("setting to stereo\n")); + dma->mono = 0; + dma->inchannels = 2; + dma->outchannels = 2; + } + return 0; + case SNDCTL_DSP_CHANNELS: + /* remember to check for resonable offset/channel pairs here */ + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (file->f_mode & FMODE_WRITE) { + if (val > 0 && (dma->outoffset + val) <= RME96xx_CHANNELS_PER_CARD) + dma->outchannels = val; + else + dma->outchannels = val = 2; + DBG(printk("setting to outchannels %d\n",val)); + } + if (file->f_mode & FMODE_READ) { + if (val > 0 && (dma->inoffset + val) <= RME96xx_CHANNELS_PER_CARD) + dma->inchannels = val; + else + dma->inchannels = val = 2; + DBG(printk("setting to inchannels %d\n",val)); + } + + dma->mono=0; + + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(RME96xx_FMT, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + DBG(printk("setting to format %x\n",val)); + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (val & RME96xx_FMT) + dma->format = val; + switch (dma->format) { + case AFMT_S16_LE: + dma->formatshift=1; + break; + case AFMT_S32_BLOCKED: + dma->formatshift=0; + break; + } + } + return put_user(dma->format, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; +#if 0 + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + val |= PCM_ENABLE_OUTPUT; +#endif + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; +#if 0 + 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_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + start_dac2(s); + } else + stop_dac2(s); + } +#endif + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + val = rme96xx_gethwptr(dma->s,0); + + + count = rme96xx_getospace(dma,val); + if (!s->started) count = s->fragsize*2; + abinfo.fragsize =(s->fragsize*dma->outchannels)>>dma->formatshift; + abinfo.bytes = (count*dma->outchannels)>>dma->formatshift; + abinfo.fragstotal = 2; + abinfo.fragments = (count > s->fragsize); + + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + val = rme96xx_gethwptr(dma->s,0); + + count = rme96xx_getispace(dma,val); + + abinfo.fragsize = (s->fragsize*dma->inchannels)>>dma->formatshift; + abinfo.bytes = (count*dma->inchannels)>>dma->formatshift;; + abinfo.fragstotal = 2; + abinfo.fragments = count > s->fragsize; + 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: /* What shold this exactly do ? , + ATM it is just abinfo.bytes */ + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + val = rme96xx_gethwptr(dma->s,0); + count = val - dma->readptr; + if (count < 0) + count += s->fragsize<<1; + + return put_user(count, (int *)arg); + + +/* check out how to use mmaped mode (can only be blocked !!!) */ + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + val = rme96xx_gethwptr(dma->s,0); + spin_lock_irqsave(&s->lock,flags); + cinfo.bytes = s->fragsize<<1;; + count = val - dma->readptr; + if (count < 0) + count += s->fragsize<<1; + + cinfo.blocks = (count > s->fragsize); + cinfo.ptr = val; + if (dma->mmapped) + dma->readptr &= s->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_READ)) + return -EINVAL; + val = rme96xx_gethwptr(dma->s,0); + spin_lock_irqsave(&s->lock,flags); + cinfo.bytes = s->fragsize<<1;; + count = val - dma->writeptr; + if (count < 0) + count += s->fragsize<<1; + + cinfo.blocks = (count > s->fragsize); + cinfo.ptr = val; + if (dma->mmapped) + dma->writeptr &= s->fragsize<<1; + spin_unlock_irqrestore(&s->lock,flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETBLKSIZE: + return put_user(s->fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + val&=0xffff; + val -= 7; + if (val < 0) val = 0; + if (val > 7) val = 7; + rme96xx_setlatency(s,val); + return 0; + + case SNDCTL_DSP_SUBDIVIDE: +#if 0 + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.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; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; +#endif + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(s->rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user(dma->outchannels, (int *)arg); + + case SOUND_PCM_READ_BITS: + switch (dma->format) { + case AFMT_S32_BLOCKED: + val = 32; + break; + case AFMT_S16_LE: + val = 16; + break; + } + return put_user(val, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + + + return -ENODEV; +} + + + +static int rme96xx_open(struct inode *in, struct file *f) +{ + int minor = MINOR(in->i_rdev); + struct list_head *list; + int devnum = ((minor-3)/16) % devices; /* default = 0 */ + rme96xx_info *s; + struct dmabuf* dma; + DECLARE_WAITQUEUE(wait, current); + + DBG(printk("device num %d open\n",devnum)); + +/* ??? */ + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, rme96xx_info, devs); + if (!((s->dspnum[devnum] ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); +/* ??? */ + + dma = &s->dma[devnum]; + f->private_data = dma; + /* wait for device to become free */ + down(&s->dma[devnum].open_sem); + while (dma->open_mode & f->f_mode) { + if (f->f_flags & O_NONBLOCK) { + up(&dma->open_sem); + return -EBUSY; + } + add_wait_queue(&dma->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&dma->open_sem); + schedule(); + remove_wait_queue(&dma->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&dma->open_sem); + } + + COMM ("hardware open") + + if (!s->dma[devnum].opened) rme96xx_dmabuf_init(dma->s,dma,dma->inoffset,dma->outoffset); + + s->dma[devnum].open_mode |= (f->f_mode & (FMODE_READ | FMODE_WRITE)); + s->dma[devnum].opened = 1; + up(&s->dma[devnum].open_sem); + + DBG(printk("device num %d open finished\n",devnum)); + return 0; +} + +static int rme96xx_release(struct inode *in, struct file *file) +{ + struct dmabuf * dma = (struct dmabuf*) file->private_data; + int hwp; + DBG(printk(__FUNCTION__"\n")); + + COMM ("draining") + if (dma->open_mode & FMODE_WRITE) { +#if 0 /* Why doesn't this work with some cards ?? */ + hwp = rme96xx_gethwptr(dma->s,0); + while (rme96xx_getospace(dma,hwp)) { + interruptible_sleep_on(&(dma->wait)); + hwp = rme96xx_gethwptr(dma->s,0); + } +#endif + rme96xx_clearbufs(dma); + } + + dma->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + if (!(dma->open_mode & (FMODE_READ|FMODE_WRITE))) { + dma->opened = 0; + if (dma->s->started) rme96xx_startcard(dma->s,1); + } + + wake_up(&dma->open_wait); + up(&dma->open_sem); + + return 0; +} + + +static ssize_t rme96xx_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + struct dmabuf *dma = (struct dmabuf *)file->private_data; + ssize_t ret = 0; + int cnt; /* number of bytes from "buffer" that will/can be used */ + int hop = count/dma->outchannels; + int hwp; + int exact = (file->f_flags & O_NONBLOCK); + + + if(dma == NULL || (dma->s) == NULL) + return -ENXIO; + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (dma->mmapped || !dma->opened) + return -ENXIO; + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + if (! (dma->open_mode & FMODE_WRITE)) + return -ENXIO; + + if (!dma->s->started) rme96xx_startcard(dma->s,exact); + hwp = rme96xx_gethwptr(dma->s,0); + + if(!(dma->started)){ + COMM ("first write") + + dma->readptr = hwp; + dma->writeptr = hwp; + dma->started = 1; + COMM ("first write done") + } + + while (count > 0) { + cnt = rme96xx_getospace(dma,hwp); + cnt>>=dma->formatshift; + cnt*=dma->outchannels; + if (cnt > count) + cnt = count; + + if (cnt != 0) { + if (rme96xx_copyfromuser(dma,buffer,cnt,hop)) + return ret ? ret : -EFAULT; + count -= cnt; + buffer += cnt; + ret += cnt; + if (count == 0) return ret; + } + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + + if ((hwp - dma->writeptr) <= 0) { + interruptible_sleep_on(&(dma->wait)); + + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + } + + hwp = rme96xx_gethwptr(dma->s,exact); + + }; /* count > 0 */ + + return ret; +} + +static ssize_t rme96xx_read(struct file *file, char *buffer,size_t count, loff_t *ppos) +{ + struct dmabuf *dma = (struct dmabuf *)file->private_data; + ssize_t ret = 0; + int cnt; + int hop = count/dma->inchannels; + int hwp; + int exact = (file->f_flags & O_NONBLOCK); + + + if(dma == NULL || (dma->s) == NULL) + return -ENXIO; + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (dma->mmapped || !dma->opened) + return -ENXIO; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + if (! (dma->open_mode & FMODE_READ)) + return -ENXIO; + + if (count > ((dma->s->fragsize*dma->inchannels)>>dma->formatshift)) + return -EFAULT; + + if (!dma->s->started) rme96xx_startcard(dma->s,exact); + hwp = rme96xx_gethwptr(dma->s,0); + + if(!(dma->started)){ + COMM ("first read") + + dma->writeptr = hwp; + dma->readptr = hwp; + dma->started = 1; + } + + while (count > 0) { + cnt = rme96xx_getispace(dma,hwp); + cnt>>=dma->formatshift; + cnt*=dma->inchannels; + + if (cnt > count) + cnt = count; + + if (cnt != 0) { + + if (rme96xx_copytouser(dma,buffer,cnt,hop)) + return ret ? ret : -EFAULT; + + count -= cnt; + buffer += cnt; + ret += cnt; + if (count == 0) return ret; + } + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + + if ((hwp - dma->readptr) <= 0) { + interruptible_sleep_on(&(dma->wait)); + + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + } + hwp = rme96xx_gethwptr(dma->s,exact); + + }; /* count > 0 */ + + return ret; +} + +static int rm96xx_mmap(struct file *file, struct vm_area_struct *vma) { + struct dmabuf *dma = (struct dmabuf *)file->private_data; + rme96xx_info* s = dma->s; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + + if (vma->vm_pgoff != 0) { + unlock_kernel(); + return -EINVAL; + } + size = vma->vm_end - vma->vm_start; + if (size > RME96xx_DMA_MAX_SIZE) { + unlock_kernel(); + return -EINVAL; + } + + + if (vma->vm_flags & VM_WRITE) { + if (!s->started) rme96xx_startcard(s,1); + + if (remap_page_range(vma, vma->vm_start, virt_to_phys(s->playbuf + dma->outoffset*RME96xx_DMA_MAX_SIZE), size, vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + } + else if (vma->vm_flags & VM_READ) { + if (!s->started) rme96xx_startcard(s,1); + if (remap_page_range(vma, vma->vm_start, virt_to_phys(s->playbuf + dma->inoffset*RME96xx_DMA_MAX_SIZE), size, vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + } else { + unlock_kernel(); + return -EINVAL; + } + + +/* this is the mapping */ + + dma->mmapped = 1; + unlock_kernel(); + return 0; +} + +static unsigned int rme96xx_poll(struct file *file, struct poll_table_struct *wait) +{ + struct dmabuf *dma = (struct dmabuf *)file->private_data; + rme96xx_info* s = dma->s; + unsigned int mask = 0; + unsigned int hwp,cnt; + + DBG(printk("rme96xx poll_wait ...\n")); + VALIDATE_STATE(s); + + if (!s->started) { + mask |= POLLOUT | POLLWRNORM; + } + poll_wait(file, &dma->wait, wait); + + hwp = rme96xx_gethwptr(dma->s,0); + + DBG(printk("rme96xx poll: ..cnt %d > %d\n",cnt,s->fragsize)); + + cnt = rme96xx_getispace(dma,hwp); + + if (file->f_mode & FMODE_READ) + if (cnt > 0) + mask |= POLLIN | POLLRDNORM; + + + + cnt = rme96xx_getospace(dma,hwp); + + if (file->f_mode & FMODE_WRITE) + if (cnt > 0) + mask |= POLLOUT | POLLWRNORM; + + +// printk("rme96xx poll_wait ...%d > %d\n",rme96xx_getospace(dma,hwp),rme96xx_getispace(dma,hwp)); + + return mask; +} + + +static struct file_operations rme96xx_audio_fops = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + owner: THIS_MODULE, +#endif + read: rme96xx_read, + write: rme96xx_write, + poll: rme96xx_poll, + ioctl: rme96xx_ioctl, + mmap: rm96xx_mmap, + open: rme96xx_open, + release: rme96xx_release +}; + +static int rme96xx_mixer_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *list; + rme96xx_info *s; + + COMM ("mixer open"); + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, rme96xx_info, devs); + if (s->mixer== minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + + COMM ("mixer opened") + return 0; +} + +static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + rme96xx_info *s = (rme96xx_info *)file->private_data; + u32 status; + + status = readl(s->iobase + RME96xx_status_register); + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_PRIVATE1) { + rme_mixer mixer; + copy_from_user(&mixer,(void*)arg,sizeof(mixer)); + + if (file->f_mode & FMODE_WRITE) { + s->dma[mixer.devnr].outoffset = mixer.o_offset; + s->dma[mixer.devnr].inoffset = mixer.i_offset; + } + + mixer.o_offset = s->dma[mixer.devnr].outoffset; + mixer.i_offset = s->dma[mixer.devnr].inoffset; + + return copy_to_user((void *)arg, &mixer, sizeof(mixer)) ? -EFAULT : 0; + } + if (cmd == SOUND_MIXER_PRIVATE2) { + return put_user(status, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE3) { + u32 control; + copy_from_user(&control,(void*)arg,sizeof(control)); + if (file->f_mode & FMODE_WRITE) { + s->control_register = control; + writel(control,s->iobase + RME96xx_control_register); + } + + return put_user(s->control_register, (int *)arg); + } + return -1; +} + + + +static int rme96xx_mixer_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static /*const*/ struct file_operations rme96xx_mixer_fops = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + owner: THIS_MODULE, +#endif + ioctl: rme96xx_mixer_ioctl, + open: rme96xx_mixer_open, + release: rme96xx_mixer_release, +}; diff -Nru a/sound/oss/rme96xx.h b/sound/oss/rme96xx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/rme96xx.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,61 @@ +/* (C) 2000 Guenter Geiger + with copy/pastes from the driver of Winfried Ritsch +*/ + + +#ifndef AFMT_S32_BLOCKED +#define AFMT_S32_BLOCKED 0x0000400 +#endif + +#ifndef AFMT_S16_BLOCKED +#define AFMT_S16_BLOCKED 0x0000800 +#endif + + +typedef struct rme_status { + unsigned int irq:1; /* high or low */ + unsigned int lockmask:3; /* ADAT1, ADAT2, ADAT3 */ + unsigned int sr48:1; /* current sample rate */ + unsigned int wclock:1; /* wordclock used ? */ + unsigned int bufpoint:10; + + unsigned int syncmask:3; /* ADAT1, ADAT2, ADAT3 */ + unsigned int doublespeed:1; + unsigned int tc_busy:1; + unsigned int tc_out:1; + unsigned int crystalrate:3; + unsigned int spdif_error:1; + unsigned int bufid:1; + unsigned int tc_valid:1; +} rme_status_t; + + +typedef struct rme_control { + unsigned int start:1; + unsigned int latency:3; + + unsigned int master:1; + unsigned int ie:1; + unsigned int sr48:1; + unsigned int spare:1; + + unsigned int doublespeed:1; + unsigned int pro:1; + unsigned int emphasis:1; + unsigned int dolby:1; + + unsigned int opt_out:1; + unsigned int wordclock:1; + unsigned int spdif_in:2; + + unsigned int sync_ref:2; +} rme_ctrl_t; + + +typedef struct _rme_mixer { + int i_offset; + int o_offset; + int devnr; + int spare[8]; +} rme_mixer; + diff -Nru a/sound/oss/sb.h b/sound/oss/sb.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sb.h Tue Feb 19 18:08:56 2002 @@ -0,0 +1,187 @@ +#define DSP_RESET (devc->base + 0x6) +#define DSP_READ (devc->base + 0xA) +#define DSP_WRITE (devc->base + 0xC) +#define DSP_COMMAND (devc->base + 0xC) +#define DSP_STATUS (devc->base + 0xC) +#define DSP_DATA_AVAIL (devc->base + 0xE) +#define DSP_DATA_AVL16 (devc->base + 0xF) +#define MIXER_ADDR (devc->base + 0x4) +#define MIXER_DATA (devc->base + 0x5) +#define OPL3_LEFT (devc->base + 0x0) +#define OPL3_RIGHT (devc->base + 0x2) +#define OPL3_BOTH (devc->base + 0x8) +/* DSP Commands */ + +#define DSP_CMD_SPKON 0xD1 +#define DSP_CMD_SPKOFF 0xD3 +#define DSP_CMD_DMAON 0xD0 +#define DSP_CMD_DMAOFF 0xD4 + +#define IMODE_NONE 0 +#define IMODE_OUTPUT PCM_ENABLE_OUTPUT +#define IMODE_INPUT PCM_ENABLE_INPUT +#define IMODE_INIT 3 +#define IMODE_MIDI 4 + +#define NORMAL_MIDI 0 +#define UART_MIDI 1 + + +/* + * Device models + */ +#define MDL_NONE 0 +#define MDL_SB1 1 /* SB1.0 or 1.5 */ +#define MDL_SB2 2 /* SB2.0 */ +#define MDL_SB201 3 /* SB2.01 */ +#define MDL_SBPRO 4 /* SB Pro */ +#define MDL_SB16 5 /* SB16/32/AWE */ +#define MDL_SBPNP 6 /* SB16/32/AWE PnP */ +#define MDL_JAZZ 10 /* Media Vision Jazz16 */ +#define MDL_SMW 11 /* Logitech SoundMan Wave (Jazz16) */ +#define MDL_ESS 12 /* ESS ES688 and ES1688 */ +#define MDL_AZTECH 13 /* Aztech Sound Galaxy family */ +#define MDL_ES1868MIDI 14 /* MIDI port of ESS1868 */ +#define MDL_AEDSP 15 /* Audio Excel DSP 16 */ +#define MDL_ESSPCI 16 /* ESS PCI card */ +#define MDL_YMPCI 17 /* Yamaha PCI sb in emulation */ + +#define SUBMDL_ALS007 42 /* ALS-007 differs from SB16 only in mixer */ + /* register assignment */ +#define SUBMDL_ALS100 43 /* ALS-100 allows sampling rates of up */ + /* to 48kHz */ + +/* + * Config flags + */ +#define SB_NO_MIDI 0x00000001 +#define SB_NO_MIXER 0x00000002 +#define SB_NO_AUDIO 0x00000004 +#define SB_NO_RECORDING 0x00000008 /* No audio recording */ +#define SB_MIDI_ONLY (SB_NO_AUDIO|SB_NO_MIXER) +#define SB_PCI_IRQ 0x00000010 /* PCI shared IRQ */ + +struct mixer_def { + unsigned int regno: 8; + unsigned int bitoffs:4; + unsigned int nbits:4; +}; + +typedef struct mixer_def mixer_tab[32][2]; +typedef struct mixer_def mixer_ent; + +struct sb_module_options +{ + int esstype; /* ESS chip type */ + int acer; /* Do acer notebook init? */ + int sm_games; /* Logitech soundman games? */ +}; + +typedef struct sb_devc { + int dev; + + /* Hardware parameters */ + int *osp; + int minor, major; + int type; + int model, submodel; + int caps; +# define SBCAP_STEREO 0x00000001 +# define SBCAP_16BITS 0x00000002 + + /* Hardware resources */ + int base; + int irq; + int dma8, dma16; + + int pcibase; /* For ESS Maestro etc */ + + /* State variables */ + int opened; + /* new audio fields for full duplex support */ + int fullduplex; + int duplex; + int speed, bits, channels; + volatile int irq_ok; + volatile int intr_active, irq_mode; + /* duplicate audio fields for full duplex support */ + volatile int intr_active_16, irq_mode_16; + + /* Mixer fields */ + int *levels; + mixer_tab *iomap; + int mixer_caps, recmask, outmask, supported_devices; + int supported_rec_devices, supported_out_devices; + int my_mixerdev; + int sbmixnum; + + /* Audio fields */ + unsigned long trg_buf; + int trigger_bits; + int trg_bytes; + int trg_intrflag; + int trg_restart; + /* duplicate audio fields for full duplex support */ + unsigned long trg_buf_16; + int trigger_bits_16; + int trg_bytes_16; + int trg_intrflag_16; + int trg_restart_16; + + unsigned char tconst; + + /* MIDI fields */ + int my_mididev; + int input_opened; + int midi_broken; + void (*midi_input_intr) (int dev, unsigned char data); + void *midi_irq_cookie; /* IRQ cookie for the midi */ + + spinlock_t lock; + + struct sb_module_options sbmo; /* Module options */ + + } sb_devc; + +/* + * PCI card types + */ + +#define SB_PCI_ESSMAESTRO 1 /* ESS Maestro Legacy */ +#define SB_PCI_YAMAHA 2 /* Yamaha Legacy */ + +/* + * Functions + */ + +int sb_dsp_command (sb_devc *devc, unsigned char val); +int sb_dsp_get_byte(sb_devc * devc); +int sb_dsp_reset (sb_devc *devc); +void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value); +unsigned int sb_getmixer (sb_devc *devc, unsigned int port); +int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo); +int sb_dsp_init (struct address_info *hw_config, struct module *owner); +void sb_dsp_unload(struct address_info *hw_config, int sbmpu); +int sb_mixer_init(sb_devc *devc, struct module *owner); +void sb_mixer_unload(sb_devc *devc); +void sb_mixer_set_stereo (sb_devc *devc, int mode); +void smw_mixer_init(sb_devc *devc); +void sb_dsp_midi_init (sb_devc *devc, struct module *owner); +void sb_audio_init (sb_devc *devc, char *name, struct module *owner); +void sb_midi_interrupt (sb_devc *devc); +void sb_chgmixer (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val); +int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right); + +int sb_audio_open(int dev, int mode); +void sb_audio_close(int dev); + +extern sb_devc *last_sb; + +/* From sb_common.c */ +void sb_dsp_disable_midi(int port); +void sb_dsp_disable_recording(int port); +int probe_sbmpu (struct address_info *hw_config, struct module *owner); +void unload_sbmpu (struct address_info *hw_config); + +void unload_sb16(struct address_info *hw_info); +void unload_sb16midi(struct address_info *hw_info); diff -Nru a/sound/oss/sb_audio.c b/sound/oss/sb_audio.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sb_audio.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,1093 @@ +/* + * sound/sb_audio.c + * + * Audio routines for Sound Blaster compatible cards. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes + * Alan Cox : Formatting and clean ups + * + * Status + * Mostly working. Weird uart bug causing irq storms + * + * Daniel J. Rodriksson: Changes to make sb16 work full duplex. + * Maybe other 16 bit cards in this code could behave + * the same. + * Chris Rankin: Use spinlocks instead of CLI/STI + */ + +#include + +#include "sound_config.h" + +#include "sb_mixer.h" +#include "sb.h" + +#include "sb_ess.h" + +int sb_audio_open(int dev, int mode) +{ + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + { + printk(KERN_ERR "Sound Blaster: incomplete initialization.\n"); + return -ENXIO; + } + if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ) + { + if (mode == OPEN_READ) + return -EPERM; + } + spin_lock_irqsave(&devc->lock, flags); + if (devc->opened) + { + spin_unlock_irqrestore(&devc->lock, flags); + return -EBUSY; + } + if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex) + { + if (sound_open_dma(devc->dma16, "Sound Blaster 16 bit")) + { + spin_unlock_irqrestore(&devc->lock, flags); + return -EBUSY; + } + } + devc->opened = mode; + spin_unlock_irqrestore(&devc->lock, flags); + + devc->irq_mode = IMODE_NONE; + devc->irq_mode_16 = IMODE_NONE; + devc->fullduplex = devc->duplex && + ((mode & OPEN_READ) && (mode & OPEN_WRITE)); + sb_dsp_reset(devc); + + /* At first glance this check isn't enough, some ESS chips might not + * have a RECLEV. However if they don't common_mixer_set will refuse + * cause devc->iomap has no register mapping for RECLEV + */ + if (devc->model == MDL_ESS) ess_mixer_reload (devc, SOUND_MIXER_RECLEV); + + /* The ALS007 seems to require that the DSP be removed from the output */ + /* in order for recording to be activated properly. This is done by */ + /* setting the appropriate bits of the output control register 4ch to */ + /* zero. This code assumes that the output control registers are not */ + /* used anywhere else and therefore the DSP bits are *always* ON for */ + /* output and OFF for sampling. */ + + if (devc->submodel == SUBMDL_ALS007) + { + if (mode & OPEN_READ) + sb_setmixer(devc,ALS007_OUTPUT_CTRL2, + sb_getmixer(devc,ALS007_OUTPUT_CTRL2) & 0xf9); + else + sb_setmixer(devc,ALS007_OUTPUT_CTRL2, + sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06); + } + return 0; +} + +void sb_audio_close(int dev) +{ + sb_devc *devc = audio_devs[dev]->devc; + + /* fix things if mmap turned off fullduplex */ + if(devc->duplex + && !devc->fullduplex + && (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE)) + { + struct dma_buffparms *dmap_temp; + dmap_temp = audio_devs[dev]->dmap_out; + audio_devs[dev]->dmap_out = audio_devs[dev]->dmap_in; + audio_devs[dev]->dmap_in = dmap_temp; + } + audio_devs[dev]->dmap_out->dma = devc->dma8; + audio_devs[dev]->dmap_in->dma = ( devc->duplex ) ? + devc->dma16 : devc->dma8; + + if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex) + sound_close_dma(devc->dma16); + + /* For ALS007, turn DSP output back on if closing the device for read */ + + if ((devc->submodel == SUBMDL_ALS007) && (devc->opened & OPEN_READ)) + { + sb_setmixer(devc,ALS007_OUTPUT_CTRL2, + sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06); + } + devc->opened = 0; +} + +static void sb_set_output_parms(int dev, unsigned long buf, int nr_bytes, + int intrflag) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (!devc->fullduplex || devc->bits == AFMT_S16_LE) + { + devc->trg_buf = buf; + devc->trg_bytes = nr_bytes; + devc->trg_intrflag = intrflag; + devc->irq_mode = IMODE_OUTPUT; + } + else + { + devc->trg_buf_16 = buf; + devc->trg_bytes_16 = nr_bytes; + devc->trg_intrflag_16 = intrflag; + devc->irq_mode_16 = IMODE_OUTPUT; + } +} + +static void sb_set_input_parms(int dev, unsigned long buf, int count, int intrflag) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (!devc->fullduplex || devc->bits != AFMT_S16_LE) + { + devc->trg_buf = buf; + devc->trg_bytes = count; + devc->trg_intrflag = intrflag; + devc->irq_mode = IMODE_INPUT; + } + else + { + devc->trg_buf_16 = buf; + devc->trg_bytes_16 = count; + devc->trg_intrflag_16 = intrflag; + devc->irq_mode_16 = IMODE_INPUT; + } +} + +/* + * SB1.x compatible routines + */ + +static void sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_OUTPUT; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x14)) /* 8 bit DAC using DMA */ + { + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + } + else + printk(KERN_WARNING "Sound Blaster: unable to start DAC.\n"); + spin_unlock_irqrestore(&devc->lock, flags); + devc->intr_active = 1; +} + +static void sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_INPUT; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x24)) /* 8 bit ADC using DMA */ + { + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + } + else + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); + spin_unlock_irqrestore(&devc->lock, flags); + + devc->intr_active = 1; +} + +static void sb1_audio_trigger(int dev, int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + + bits &= devc->irq_mode; + + if (!bits) + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + else + { + switch (devc->irq_mode) + { + case IMODE_INPUT: + sb1_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + sb1_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + } + } + devc->trigger_bits = bits; +} + +static int sb1_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKOFF); + spin_unlock_irqrestore(&devc->lock, flags); + + devc->trigger_bits = 0; + return 0; +} + +static int sb1_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKON); + spin_unlock_irqrestore(&devc->lock, flags); + devc->trigger_bits = 0; + return 0; +} + +static int sb1_audio_set_speed(int dev, int speed) +{ + int max_speed = 23000; + sb_devc *devc = audio_devs[dev]->devc; + int tmp; + + if (devc->opened & OPEN_READ) + max_speed = 13000; + + if (speed > 0) + { + if (speed < 4000) + speed = 4000; + + if (speed > max_speed) + speed = max_speed; + + devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; + tmp = 256 - devc->tconst; + speed = (1000000 + tmp / 2) / tmp; + + devc->speed = speed; + } + return devc->speed; +} + +static short sb1_audio_set_channels(int dev, short channels) +{ + sb_devc *devc = audio_devs[dev]->devc; + return devc->channels = 1; +} + +static unsigned int sb1_audio_set_bits(int dev, unsigned int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + return devc->bits = 8; +} + +static void sb1_audio_halt_xfer(int dev) +{ + unsigned long flags; + sb_devc *devc = audio_devs[dev]->devc; + + spin_lock_irqsave(&devc->lock, flags); + sb_dsp_reset(devc); + spin_unlock_irqrestore(&devc->lock, flags); +} + +/* + * SB 2.0 and SB 2.01 compatible routines + */ + +static void sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes, + int intrflag) +{ + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + unsigned char cmd; + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_OUTPUT; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ + { + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + + if (devc->speed * devc->channels <= 23000) + cmd = 0x1c; /* 8 bit PCM output */ + else + cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */ + + if (!sb_dsp_command(devc, cmd)) + printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); + } + else + printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); + spin_unlock_irqrestore(&devc->lock, flags); + devc->intr_active = 1; +} + +static void sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + unsigned char cmd; + + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_INPUT; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ + { + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + + if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000)) + cmd = 0x2c; /* 8 bit PCM input */ + else + cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */ + + if (!sb_dsp_command(devc, cmd)) + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); + } + else + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); + spin_unlock_irqrestore(&devc->lock, flags); + devc->intr_active = 1; +} + +static void sb20_audio_trigger(int dev, int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + bits &= devc->irq_mode; + + if (!bits) + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + else + { + switch (devc->irq_mode) + { + case IMODE_INPUT: + sb20_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + sb20_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + } + } + devc->trigger_bits = bits; +} + +/* + * SB2.01 specific speed setup + */ + +static int sb201_audio_set_speed(int dev, int speed) +{ + sb_devc *devc = audio_devs[dev]->devc; + int tmp; + int s = speed * devc->channels; + + if (speed > 0) + { + if (speed < 4000) + speed = 4000; + if (speed > 44100) + speed = 44100; + if (devc->opened & OPEN_READ && speed > 15000) + speed = 15000; + devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff; + tmp = 256 - devc->tconst; + speed = ((1000000 + tmp / 2) / tmp) / devc->channels; + + devc->speed = speed; + } + return devc->speed; +} + +/* + * SB Pro specific routines + */ + +static int sbpro_audio_prepare_for_input(int dev, int bsize, int bcount) +{ /* For SB Pro and Jazz16 */ + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + unsigned char bits = 0; + + if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) + audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = + devc->bits == 16 ? devc->dma16 : devc->dma8; + + if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) + if (devc->bits == AFMT_S16_LE) + bits = 0x04; /* 16 bit mode */ + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKOFF); + if (devc->channels == 1) + sb_dsp_command(devc, 0xa0 | bits); /* Mono input */ + else + sb_dsp_command(devc, 0xa8 | bits); /* Stereo input */ + spin_unlock_irqrestore(&devc->lock, flags); + + devc->trigger_bits = 0; + return 0; +} + +static int sbpro_audio_prepare_for_output(int dev, int bsize, int bcount) +{ /* For SB Pro and Jazz16 */ + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + unsigned char tmp; + unsigned char bits = 0; + + if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) + audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = devc->bits == 16 ? devc->dma16 : devc->dma8; + if (devc->model == MDL_SBPRO) + sb_mixer_set_stereo(devc, devc->channels == 2); + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKON); + + if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) + { + if (devc->bits == AFMT_S16_LE) + bits = 0x04; /* 16 bit mode */ + + if (devc->channels == 1) + sb_dsp_command(devc, 0xa0 | bits); /* Mono output */ + else + sb_dsp_command(devc, 0xa8 | bits); /* Stereo output */ + } + else + { + tmp = sb_getmixer(devc, 0x0e); + if (devc->channels == 1) + tmp &= ~0x02; + else + tmp |= 0x02; + sb_setmixer(devc, 0x0e, tmp); + } + spin_unlock_irqrestore(&devc->lock, flags); + devc->trigger_bits = 0; + return 0; +} + +static int sbpro_audio_set_speed(int dev, int speed) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (speed > 0) + { + if (speed < 4000) + speed = 4000; + if (speed > 44100) + speed = 44100; + if (devc->channels > 1 && speed > 22050) + speed = 22050; + sb201_audio_set_speed(dev, speed); + } + return devc->speed; +} + +static short sbpro_audio_set_channels(int dev, short channels) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (channels == 1 || channels == 2) + { + if (channels != devc->channels) + { + devc->channels = channels; + if (devc->model == MDL_SBPRO && devc->channels == 2) + sbpro_audio_set_speed(dev, devc->speed); + } + } + return devc->channels; +} + +static int jazz16_audio_set_speed(int dev, int speed) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (speed > 0) + { + int tmp; + int s = speed * devc->channels; + + if (speed < 5000) + speed = 5000; + if (speed > 44100) + speed = 44100; + + devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff; + + tmp = 256 - devc->tconst; + speed = ((1000000 + tmp / 2) / tmp) / devc->channels; + + devc->speed = speed; + } + return devc->speed; +} + +/* + * SB16 specific routines + */ + +static int sb16_audio_set_speed(int dev, int speed) +{ + sb_devc *devc = audio_devs[dev]->devc; + int max_speed = devc->submodel == SUBMDL_ALS100 ? 48000 : 44100; + + if (speed > 0) + { + if (speed < 5000) /* which of these */ + speed = 4000; /* is correct ??? */ + + if (speed > max_speed) + speed = max_speed; + + devc->speed = speed; + } + return devc->speed; +} + +static unsigned int sb16_audio_set_bits(int dev, unsigned int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (bits != 0) + { + if (bits == AFMT_U8 || bits == AFMT_S16_LE) + devc->bits = bits; + else + devc->bits = AFMT_U8; + } + + return devc->bits; +} + +static int sb16_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (!devc->fullduplex) + { + audio_devs[dev]->dmap_out->dma = + audio_devs[dev]->dmap_in->dma = + devc->bits == AFMT_S16_LE ? + devc->dma16 : devc->dma8; + } + else if (devc->bits == AFMT_S16_LE) + { + audio_devs[dev]->dmap_out->dma = devc->dma8; + audio_devs[dev]->dmap_in->dma = devc->dma16; + } + else + { + audio_devs[dev]->dmap_out->dma = devc->dma16; + audio_devs[dev]->dmap_in->dma = devc->dma8; + } + + devc->trigger_bits = 0; + return 0; +} + +static int sb16_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (!devc->fullduplex) + { + audio_devs[dev]->dmap_out->dma = + audio_devs[dev]->dmap_in->dma = + devc->bits == AFMT_S16_LE ? + devc->dma16 : devc->dma8; + } + else if (devc->bits == AFMT_S16_LE) + { + audio_devs[dev]->dmap_out->dma = devc->dma8; + audio_devs[dev]->dmap_in->dma = devc->dma16; + } + else + { + audio_devs[dev]->dmap_out->dma = devc->dma16; + audio_devs[dev]->dmap_in->dma = devc->dma8; + } + + devc->trigger_bits = 0; + return 0; +} + +static void sb16_audio_output_block(int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags, cnt; + sb_devc *devc = audio_devs[dev]->devc; + unsigned long bits; + + if (!devc->fullduplex || devc->bits == AFMT_S16_LE) + { + devc->irq_mode = IMODE_OUTPUT; + devc->intr_active = 1; + } + else + { + devc->irq_mode_16 = IMODE_OUTPUT; + devc->intr_active_16 = 1; + } + + /* save value */ + spin_lock_irqsave(&devc->lock, flags); + bits = devc->bits; + if (devc->fullduplex) + devc->bits = (devc->bits == AFMT_S16_LE) ? + AFMT_U8 : AFMT_S16_LE; + spin_unlock_irqrestore(&devc->lock, flags); + + cnt = count; + if (devc->bits == AFMT_S16_LE) + cnt >>= 1; + cnt--; + + spin_lock_irqsave(&devc->lock, flags); + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ + + sb_dsp_command(devc, 0x41); + sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff)); + sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff)); + + sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6)); + sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + + (devc->bits == AFMT_S16_LE ? 0x10 : 0))); + sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); + sb_dsp_command(devc, (unsigned char) (cnt >> 8)); + + /* restore real value after all programming */ + devc->bits = bits; + spin_unlock_irqrestore(&devc->lock, flags); +} + + +/* + * This fails on the Cyrix MediaGX. If you don't have the DMA enabled + * before the first sample arrives it locks up. However even if you + * do enable the DMA in time you just get DMA timeouts and missing + * interrupts and stuff, so for now I've not bothered fixing this either. + */ + +static void sb16_audio_start_input(int dev, unsigned long buf, int count, int intrflag) +{ + unsigned long flags, cnt; + sb_devc *devc = audio_devs[dev]->devc; + + if (!devc->fullduplex || devc->bits != AFMT_S16_LE) + { + devc->irq_mode = IMODE_INPUT; + devc->intr_active = 1; + } + else + { + devc->irq_mode_16 = IMODE_INPUT; + devc->intr_active_16 = 1; + } + + cnt = count; + if (devc->bits == AFMT_S16_LE) + cnt >>= 1; + cnt--; + + spin_lock_irqsave(&devc->lock, flags); + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ + + sb_dsp_command(devc, 0x42); + sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff)); + sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff)); + + sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce)); + sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + + (devc->bits == AFMT_S16_LE ? 0x10 : 0))); + sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); + sb_dsp_command(devc, (unsigned char) (cnt >> 8)); + + spin_unlock_irqrestore(&devc->lock, flags); +} + +static void sb16_audio_trigger(int dev, int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + + int bits_16 = bits & devc->irq_mode_16; + bits &= devc->irq_mode; + + if (!bits && !bits_16) + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + else + { + if (bits) + { + switch (devc->irq_mode) + { + case IMODE_INPUT: + sb16_audio_start_input(dev, + devc->trg_buf, + devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + sb16_audio_output_block(dev, + devc->trg_buf, + devc->trg_bytes, + devc->trg_intrflag); + break; + } + } + if (bits_16) + { + switch (devc->irq_mode_16) + { + case IMODE_INPUT: + sb16_audio_start_input(dev, + devc->trg_buf_16, + devc->trg_bytes_16, + devc->trg_intrflag_16); + break; + + case IMODE_OUTPUT: + sb16_audio_output_block(dev, + devc->trg_buf_16, + devc->trg_bytes_16, + devc->trg_intrflag_16); + break; + } + } + } + + devc->trigger_bits = bits | bits_16; +} + +static unsigned char lbuf8[2048]; +static signed short *lbuf16 = (signed short *)lbuf8; +#define LBUFCOPYSIZE 1024 +static void +sb16_copy_from_user(int dev, + char *localbuf, int localoffs, + const char *userbuf, int useroffs, + int max_in, int max_out, + int *used, int *returned, + int len) +{ + sb_devc *devc = audio_devs[dev]->devc; + int i, c, p, locallen; + unsigned char *buf8; + signed short *buf16; + + /* if not duplex no conversion */ + if (!devc->fullduplex) + { + copy_from_user (localbuf + localoffs, userbuf + useroffs, len); + *used = len; + *returned = len; + } + else if (devc->bits == AFMT_S16_LE) + { + /* 16 -> 8 */ + /* max_in >> 1, max number of samples in ( 16 bits ) */ + /* max_out, max number of samples out ( 8 bits ) */ + /* len, number of samples that will be taken ( 16 bits )*/ + /* c, count of samples remaining in buffer ( 16 bits )*/ + /* p, count of samples already processed ( 16 bits )*/ + len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1); + c = len; + p = 0; + buf8 = (unsigned char *)(localbuf + localoffs); + while (c) + { + locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c); + /* << 1 in order to get 16 bit samples */ + copy_from_user (lbuf16, + userbuf+useroffs + (p << 1), + locallen << 1); + for (i = 0; i < locallen; i++) + { + buf8[p+i] = ~((lbuf16[i] >> 8) & 0xff) ^ 0x80; + } + c -= locallen; p += locallen; + } + /* used = ( samples * 16 bits size ) */ + *used = len << 1; + /* returned = ( samples * 8 bits size ) */ + *returned = len; + } + else + { + /* 8 -> 16 */ + /* max_in, max number of samples in ( 8 bits ) */ + /* max_out >> 1, max number of samples out ( 16 bits ) */ + /* len, number of samples that will be taken ( 8 bits )*/ + /* c, count of samples remaining in buffer ( 8 bits )*/ + /* p, count of samples already processed ( 8 bits )*/ + len = max_in > (max_out >> 1) ? (max_out >> 1) : max_in; + c = len; + p = 0; + buf16 = (signed short *)(localbuf + localoffs); + while (c) + { + locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c); + copy_from_user (lbuf8, + userbuf+useroffs + p, + locallen); + for (i = 0; i < locallen; i++) + { + buf16[p+i] = (~lbuf8[i] ^ 0x80) << 8; + } + c -= locallen; p += locallen; + } + /* used = ( samples * 8 bits size ) */ + *used = len; + /* returned = ( samples * 16 bits size ) */ + *returned = len << 1; + } +} + +static void +sb16_audio_mmap(int dev) +{ + sb_devc *devc = audio_devs[dev]->devc; + devc->fullduplex = 0; +} + +static struct audio_driver sb1_audio_driver = /* SB1.x */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sb1_audio_prepare_for_input, + prepare_for_output: sb1_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + trigger: sb1_audio_trigger, + set_speed: sb1_audio_set_speed, + set_bits: sb1_audio_set_bits, + set_channels: sb1_audio_set_channels +}; + +static struct audio_driver sb20_audio_driver = /* SB2.0 */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sb1_audio_prepare_for_input, + prepare_for_output: sb1_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + trigger: sb20_audio_trigger, + set_speed: sb1_audio_set_speed, + set_bits: sb1_audio_set_bits, + set_channels: sb1_audio_set_channels +}; + +static struct audio_driver sb201_audio_driver = /* SB2.01 */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sb1_audio_prepare_for_input, + prepare_for_output: sb1_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + trigger: sb20_audio_trigger, + set_speed: sb201_audio_set_speed, + set_bits: sb1_audio_set_bits, + set_channels: sb1_audio_set_channels +}; + +static struct audio_driver sbpro_audio_driver = /* SB Pro */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sbpro_audio_prepare_for_input, + prepare_for_output: sbpro_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + trigger: sb20_audio_trigger, + set_speed: sbpro_audio_set_speed, + set_bits: sb1_audio_set_bits, + set_channels: sbpro_audio_set_channels +}; + +static struct audio_driver jazz16_audio_driver = /* Jazz16 and SM Wave */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sbpro_audio_prepare_for_input, + prepare_for_output: sbpro_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + trigger: sb20_audio_trigger, + set_speed: jazz16_audio_set_speed, + set_bits: sb16_audio_set_bits, + set_channels: sbpro_audio_set_channels +}; + +static struct audio_driver sb16_audio_driver = /* SB16 */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sb16_audio_prepare_for_input, + prepare_for_output: sb16_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + copy_user: sb16_copy_from_user, + trigger: sb16_audio_trigger, + set_speed: sb16_audio_set_speed, + set_bits: sb16_audio_set_bits, + set_channels: sbpro_audio_set_channels, + mmap: sb16_audio_mmap +}; + +void sb_audio_init(sb_devc * devc, char *name, struct module *owner) +{ + int audio_flags = 0; + int format_mask = AFMT_U8; + + struct audio_driver *driver = &sb1_audio_driver; + + switch (devc->model) + { + case MDL_SB1: /* SB1.0 or SB 1.5 */ + DDB(printk("Will use standard SB1.x driver\n")); + audio_flags = DMA_HARDSTOP; + break; + + case MDL_SB2: + DDB(printk("Will use SB2.0 driver\n")); + audio_flags = DMA_AUTOMODE; + driver = &sb20_audio_driver; + break; + + case MDL_SB201: + DDB(printk("Will use SB2.01 (high speed) driver\n")); + audio_flags = DMA_AUTOMODE; + driver = &sb201_audio_driver; + break; + + case MDL_JAZZ: + case MDL_SMW: + DDB(printk("Will use Jazz16 driver\n")); + audio_flags = DMA_AUTOMODE; + format_mask |= AFMT_S16_LE; + driver = &jazz16_audio_driver; + break; + + case MDL_ESS: + DDB(printk("Will use ESS ES688/1688 driver\n")); + driver = ess_audio_init (devc, &audio_flags, &format_mask); + break; + + case MDL_SB16: + DDB(printk("Will use SB16 driver\n")); + audio_flags = DMA_AUTOMODE; + format_mask |= AFMT_S16_LE; + if (devc->dma8 != devc->dma16 && devc->dma16 != -1) + { + audio_flags |= DMA_DUPLEX; + devc->duplex = 1; + } + driver = &sb16_audio_driver; + break; + + default: + DDB(printk("Will use SB Pro driver\n")); + audio_flags = DMA_AUTOMODE; + driver = &sbpro_audio_driver; + } + + if (owner) + driver->owner = owner; + + if ((devc->dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + name,driver, sizeof(struct audio_driver), + audio_flags, format_mask, devc, + devc->dma8, + devc->duplex ? devc->dma16 : devc->dma8)) < 0) + { + printk(KERN_ERR "Sound Blaster: unable to install audio.\n"); + return; + } + audio_devs[devc->dev]->mixer_dev = devc->my_mixerdev; + audio_devs[devc->dev]->min_fragment = 5; +} diff -Nru a/sound/oss/sb_card.c b/sound/oss/sb_card.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sb_card.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,1035 @@ +/* + * sound/sb_card.c + * + * Detection routine for the Sound Blaster cards. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * 26-11-1999 Patched to compile without ISA PnP support in the + * kernel - Daniel Stone (tamriel@ductape.net) + * + * 06-01-2000 Refined and bugfixed ISA PnP support, added + * CMI 8330 support - Alessandro Zummo + * + * 18-01-2000 Separated sb_card and sb_common + * Jeff Garzik + * + * 04-02-2000 Added Soundblaster AWE 64 PnP support, isapnpjump + * Alessandro Zummo + * + * 11-02-2000 Added Soundblaster AWE 32 PnP support, refined PnP code + * Alessandro Zummo + * + * 13-02-2000 Hopefully fixed awe/sb16 related bugs, code cleanup + * Alessandro Zummo + * + * 13-03-2000 Added some more cards, thanks to Torsten Werner. + * Removed joystick and wavetable code, there are better places for them. + * Code cleanup plus some fixes. + * Alessandro Zummo + * + * 26-03-2000 Fixed acer, esstype and sm_games module options. + * Alessandro Zummo + * + * 12-04-2000 ISAPnP cleanup, reorg, fixes, and multiple card support. + * Thanks to Gaël Quéri and Alessandro Zummo for testing and fixes. + * Paul E. Laufer + * + * 06-05-2000 added another card. Daniel M. Newman + * + * 25-05-2000 Added Creative SB AWE64 Gold (CTL00B2). + * Pål-Kristian Engstad + * + * 12-08-2000 Added Creative SB32 PnP (CTL009F). + * Kasatenko Ivan Alex. + * + * 21-09-2000 Got rid of attach_sbmpu + * Arnaldo Carvalho de Melo + * + * 28-10-2000 Added pnplegacy support + * Daniel Church + * + * 01-10-2001 Added a new flavor of Creative SB AWE64 PnP (CTL00E9). + * Jerome Cornet + */ + +#include +#include +#include +#include +#include + +#include "sound_config.h" + +#include "sb_mixer.h" +#include "sb.h" + +#ifdef __ISAPNP__ +#define SB_CARDS_MAX 5 +#else +#define SB_CARDS_MAX 1 +#endif + +static int sbmpu[SB_CARDS_MAX] = {0}; +static int sb_cards_num = 0; + +extern void *smw_free; + +/* + * Note DMA2 of -1 has the right meaning in the SB16 driver as well + * as here. It will cause either an error if it is needed or a fallback + * to the 8bit channel. + */ + +static int __initdata mpu_io = 0; +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ +static int __initdata type = 0; /* Can set this to a specific card type */ +static int __initdata esstype = 0; /* ESS chip type */ +static int __initdata acer = 0; /* Do acer notebook init? */ +static int __initdata sm_games = 0; /* Logitech soundman games? */ + +static void __init attach_sb_card(struct address_info *hw_config) +{ + if(!sb_dsp_init(hw_config, THIS_MODULE)) + hw_config->slots[0] = -1; +} + +static int __init probe_sb(struct address_info *hw_config) +{ + struct sb_module_options sbmo; + + if (hw_config->io_base == -1 || hw_config->dma == -1 || hw_config->irq == -1) + { + printk(KERN_ERR "sb: I/O, IRQ, and DMA are mandatory\n"); + return -EINVAL; + } + +#ifdef CONFIG_MCA + /* MCA code added by ZP Gu (zpg@castle.net) */ + if (MCA_bus) { /* no multiple REPLY card probing */ + int slot; + u8 pos2, pos3, pos4; + + slot = mca_find_adapter( 0x5138, 0 ); + if( slot == MCA_NOTFOUND ) + { + slot = mca_find_adapter( 0x5137, 0 ); + + if (slot != MCA_NOTFOUND) + mca_set_adapter_name( slot, "REPLY SB16 & SCSI Adapter" ); + } + else + { + mca_set_adapter_name( slot, "REPLY SB16 Adapter" ); + } + + if (slot != MCA_NOTFOUND) + { + mca_mark_as_used(slot); + pos2 = mca_read_stored_pos( slot, 2 ); + pos3 = mca_read_stored_pos( slot, 3 ); + pos4 = mca_read_stored_pos( slot, 4 ); + + if (pos2 & 0x4) + { + /* enabled? */ + static unsigned short irq[] = { 0, 5, 7, 10 }; + /* + static unsigned short midiaddr[] = {0, 0x330, 0, 0x300 }; + */ + + hw_config->io_base = 0x220 + 0x20 * (pos2 >> 6); + hw_config->irq = irq[(pos4 >> 5) & 0x3]; + hw_config->dma = pos3 & 0xf; + /* Reply ADF wrong on High DMA, pos[1] should start w/ 00 */ + hw_config->dma2 = (pos3 >> 4) & 0x3; + if (hw_config->dma2 == 0) + hw_config->dma2 = hw_config->dma; + else + hw_config->dma2 += 4; + /* + hw_config->driver_use_2 = midiaddr[(pos2 >> 3) & 0x3]; + */ + + printk(KERN_INFO "sb: Reply MCA SB at slot=%d \ +iobase=0x%x irq=%d lo_dma=%d hi_dma=%d\n", + slot+1, + hw_config->io_base, hw_config->irq, + hw_config->dma, hw_config->dma2); + } + else + { + printk (KERN_INFO "sb: Reply SB Base I/O address disabled\n"); + } + } + } +#endif + + /* Setup extra module options */ + + sbmo.acer = acer; + sbmo.sm_games = sm_games; + sbmo.esstype = esstype; + + return sb_dsp_detect(hw_config, 0, 0, &sbmo); +} + +static void __exit unload_sb(struct address_info *hw_config, int card) +{ + if(hw_config->slots[0]!=-1) + sb_dsp_unload(hw_config, sbmpu[card]); +} + +static struct address_info cfg[SB_CARDS_MAX]; +static struct address_info cfg_mpu[SB_CARDS_MAX]; + +struct pci_dev *sb_dev[SB_CARDS_MAX] = {NULL}, + *mpu_dev[SB_CARDS_MAX] = {NULL}, + *opl_dev[SB_CARDS_MAX] = {NULL}; + + +#ifdef __ISAPNP__ +static int isapnp = 1; +static int isapnpjump = 0; +static int multiple = 1; +static int pnplegacy = 0; +static int reverse = 0; +static int uart401 = 0; + +static int audio_activated[SB_CARDS_MAX] = {0}; +static int mpu_activated[SB_CARDS_MAX] = {0}; +static int opl_activated[SB_CARDS_MAX] = {0}; +#else +static int isapnp = 0; +static int multiple = 0; +static int pnplegacy = 0; +#endif + +MODULE_DESCRIPTION("Soundblaster driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(dma, "i"); +MODULE_PARM(dma16, "i"); +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(type, "i"); +MODULE_PARM(sm_games, "i"); +MODULE_PARM(esstype, "i"); +MODULE_PARM(acer, "i"); + +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "i"); +MODULE_PARM(isapnpjump, "i"); +MODULE_PARM(multiple, "i"); +MODULE_PARM(pnplegacy, "i"); +MODULE_PARM(reverse, "i"); +MODULE_PARM(uart401, "i"); +MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled"); +MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke."); +MODULE_PARM_DESC(multiple, "When set to 0, will not search for multiple cards"); +MODULE_PARM_DESC(pnplegacy, "When set to 1, will search for a legacy SB card along with any PnP cards."); +MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order"); +MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable the mpu on some clones"); +#endif + +MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); +MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)"); +MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)"); +MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)"); +MODULE_PARM_DESC(mpu_io, "Mpu base address"); +MODULE_PARM_DESC(type, "You can set this to specific card type"); +MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games"); +MODULE_PARM_DESC(esstype, "ESS chip type"); +MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks"); + +#ifdef __ISAPNP__ + +/* Please add new entries at the end of the table */ +static struct { + char *name; + unsigned short card_vendor, card_device, + audio_vendor, audio_function, + mpu_vendor, mpu_function, + opl_vendor, opl_function; + short dma, dma2, mpu_io, mpu_irq; /* see sb_init() */ +} sb_isapnp_list[] __initdata = { + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00ed), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16S", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16C", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16CL", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16X", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Creative SB32 PnP", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009F), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64 Gold", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64 Gold", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, + {"ESS 1688", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1868", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1868", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1869 PnP AudioDrive", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1869", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1878", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1879", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), + 0,0,0,0, + 0,1,2,-1}, + {"CMI 8330 SoundPRO", + ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + 0,1,0,-1}, + {"Diamond DT0197H", + ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 0,-1,0,0}, + {"ALS007", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 0,-1,0,0}, + {"ALS100", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {"ALS110", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {"ALS120", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {"ALS200", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {"RTL3000", + ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {0} +}; + +static struct isapnp_device_id id_table[] __devinitdata = { + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00ed), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009F), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), 0 }, + + { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), 0 }, + + { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), 0 }, + + { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, id_table); + +static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) +{ + int err; + + /* Device already active? Let's use it */ + if(dev->active) + return(dev); + + if((err = dev->activate(dev)) < 0) { + printk(KERN_ERR "sb: %s %s config failed (out of resources?)[%d]\n", devname, resname, err); + + dev->deactivate(dev); + + return(NULL); + } + return(dev); +} + +static struct pci_dev *sb_init(struct pci_bus *bus, struct address_info *hw_config, struct address_info *mpu_config, int slot, int card) +{ + + /* Configure Audio device */ + if((sb_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].audio_vendor, sb_isapnp_list[slot].audio_function, NULL))) + { + int ret; + ret = sb_dev[card]->prepare(sb_dev[card]); + /* If device is active, assume configured with /proc/isapnp + * and use anyway. Some other way to check this? */ + if(ret && ret != -EBUSY) { + printk(KERN_ERR "sb: ISAPnP found device that could not be autoconfigured.\n"); + return(NULL); + } + if(ret == -EBUSY) + audio_activated[card] = 1; + + if((sb_dev[card] = activate_dev(sb_isapnp_list[slot].name, "sb", sb_dev[card]))) + { + hw_config->io_base = sb_dev[card]->resource[0].start; + hw_config->irq = sb_dev[card]->irq_resource[0].start; + hw_config->dma = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma].start; + if(sb_isapnp_list[slot].dma2 != -1) + hw_config->dma2 = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma2].start; + else + hw_config->dma2 = -1; + } else + return(NULL); + } else + return(NULL); + + /* Cards with separate OPL3 device (ALS, CMI, etc.) + * This is just to activate the device so the OPL module can use it */ + if(sb_isapnp_list[slot].opl_vendor || sb_isapnp_list[slot].opl_function) { + if((opl_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].opl_vendor, sb_isapnp_list[slot].opl_function, NULL))) { + int ret = opl_dev[card]->prepare(opl_dev[card]); + /* If device is active, assume configured with + * /proc/isapnp and use anyway */ + if(ret && ret != -EBUSY) { + printk(KERN_ERR "sb: OPL device could not be autoconfigured.\n"); + return(sb_dev[card]); + } + if(ret == -EBUSY) + opl_activated[card] = 1; + + /* Some have irq and dma for opl. the opl3 driver wont + * use 'em so don't configure 'em and hope it works -PEL */ + opl_dev[card]->irq_resource[0].flags = 0; + opl_dev[card]->dma_resource[0].flags = 0; + + opl_dev[card] = activate_dev(sb_isapnp_list[slot].name, "opl3", opl_dev[card]); + } else + printk(KERN_ERR "sb: %s isapnp panic: opl3 device not found\n", sb_isapnp_list[slot].name); + } + + /* Cards with MPU as part of Audio device (CTL and ESS) */ + if(!sb_isapnp_list[slot].mpu_vendor) { + mpu_config->io_base = sb_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start; + return(sb_dev[card]); + } + + /* Cards with separate MPU device (ALS, CMI, etc.) */ + if(!uart401) + return(sb_dev[card]); + if((mpu_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].mpu_vendor, sb_isapnp_list[slot].mpu_function, NULL))) + { + int ret = mpu_dev[card]->prepare(mpu_dev[card]); + /* If device is active, assume configured with /proc/isapnp + * and use anyway */ + if(ret && ret != -EBUSY) { + printk(KERN_ERR "sb: MPU device could not be autoconfigured.\n"); + return(sb_dev[card]); + } + if(ret == -EBUSY) + mpu_activated[card] = 1; + + /* Some cards ask for irq but don't need them - azummo */ + if(sb_isapnp_list[slot].mpu_irq == -1) + mpu_dev[card]->irq_resource[0].flags = 0; + + if((mpu_dev[card] = activate_dev(sb_isapnp_list[slot].name, "mpu", mpu_dev[card]))) { + mpu_config->io_base = mpu_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start; + if(sb_isapnp_list[slot].mpu_irq != -1) + mpu_config->irq = mpu_dev[card]->irq_resource[sb_isapnp_list[slot].mpu_irq].start; + } + } + else + printk(KERN_ERR "sb: %s isapnp panic: mpu not found\n", sb_isapnp_list[slot].name); + + return(sb_dev[card]); +} + +static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, int slot, int card) +{ + char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name; + + printk(KERN_INFO "sb: %s detected\n", busname); + + /* Initialize this baby. */ + + if(sb_init(bus, hw_config, mpu_config, slot, card)) { + /* We got it. */ + + printk(KERN_NOTICE "sb: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", + busname, + hw_config->io_base, hw_config->irq, hw_config->dma, + hw_config->dma2); + return 1; + } + else + printk(KERN_INFO "sb: Failed to initialize %s\n", busname); + + return 0; +} + +static int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config, int card) +{ + static int first = 1; + int i; + + /* Count entries in sb_isapnp_list */ + for (i = 0; sb_isapnp_list[i].card_vendor != 0; i++); + i--; + + /* Check and adjust isapnpjump */ + if( isapnpjump < 0 || isapnpjump > i) { + isapnpjump = reverse ? i : 0; + printk(KERN_ERR "sb: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump); + } + + if(!first || !reverse) + i = isapnpjump; + first = 0; + while(sb_isapnp_list[i].card_vendor != 0) { + static struct pci_bus *bus = NULL; + + while ((bus = isapnp_find_card( + sb_isapnp_list[i].card_vendor, + sb_isapnp_list[i].card_device, + bus))) { + + if(sb_isapnp_init(hw_config, mpu_config, bus, i, card)) { + isapnpjump = i; /* start next search from here */ + return 0; + } + } + i += reverse ? -1 : 1; + } + + return -ENODEV; +} +#endif + +static int __init init_sb(void) +{ + int card, max = (multiple && isapnp) ? SB_CARDS_MAX : 1; + + printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + for(card = 0; card < max; card++, sb_cards_num++) { +#ifdef __ISAPNP__ + /* Please remember that even with __ISAPNP__ defined one + * should still be able to disable PNP support for this + * single driver! */ + if((!pnplegacy||card>0) && isapnp && (sb_isapnp_probe(&cfg[card], &cfg_mpu[card], card) < 0) ) { + if(!sb_cards_num) { + /* Found no ISAPnP cards, so check for a non-pnp + * card and set the detection loop for 1 cycle + */ + printk(KERN_NOTICE "sb: No ISAPnP cards found, trying standard ones...\n"); + isapnp = 0; + max = 1; + } else + /* found all the ISAPnP cards so exit the + * detection loop. */ + break; + } +#endif + + if(!isapnp || (pnplegacy&&card==0)) { + cfg[card].io_base = io; + cfg[card].irq = irq; + cfg[card].dma = dma; + cfg[card].dma2 = dma16; + } + + cfg[card].card_subtype = type; + + if (!probe_sb(&cfg[card])) { + /* if one or more cards already registered, don't + * return an error but print a warning. Note, this + * should never really happen unless the hardware + * or ISAPnP screwed up. */ + if (sb_cards_num) { + printk(KERN_WARNING "sb.c: There was a " \ + "problem probing one of your SoundBlaster " \ + "ISAPnP soundcards. Continuing.\n"); + card--; + sb_cards_num--; + continue; + } else if(pnplegacy && isapnp) { + printk(KERN_NOTICE "sb: No legacy SoundBlaster cards " \ + "found. Continuing with PnP detection.\n"); + pnplegacy=0; + card--; + continue; + } else + return -ENODEV; + } + attach_sb_card(&cfg[card]); + + if(cfg[card].slots[0]==-1) { + if(card==0 && pnplegacy && isapnp) { + printk(KERN_NOTICE "sb: No legacy SoundBlaster cards " \ + "found. Continuing with PnP detection.\n"); + pnplegacy=0; + card--; + continue; + } else + return -ENODEV; + } + + if (!isapnp||(pnplegacy&&card==0)) + cfg_mpu[card].io_base = mpu_io; + if (probe_sbmpu(&cfg_mpu[card], THIS_MODULE)) + sbmpu[card] = 1; + } + + if(isapnp) + printk(KERN_NOTICE "sb: %d Soundblaster PnP card(s) found.\n", sb_cards_num); + + return 0; +} + +static void __exit cleanup_sb(void) +{ + int i; + + if (smw_free) { + vfree(smw_free); + smw_free = NULL; + } + + for(i = 0; i < sb_cards_num; i++) { + unload_sb(&cfg[i], i); + if (sbmpu[i]) + unload_sbmpu(&cfg_mpu[i]); + +#ifdef __ISAPNP__ + if(!audio_activated[i] && sb_dev[i]) + sb_dev[i]->deactivate(sb_dev[i]); + if(!mpu_activated[i] && mpu_dev[i]) + mpu_dev[i]->deactivate(mpu_dev[i]); + if(!opl_activated[i] && opl_dev[i]) + opl_dev[i]->deactivate(opl_dev[i]); +#endif + } +} + +module_init(init_sb); +module_exit(cleanup_sb); + +#ifndef MODULE +static int __init setup_sb(char *str) +{ + /* io, irq, dma, dma2 - just the basics */ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma16 = ints[4]; + + return 1; +} +__setup("sb=", setup_sb); +#endif diff -Nru a/sound/oss/sb_common.c b/sound/oss/sb_common.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sb_common.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,1295 @@ +/* + * sound/sb_common.c + * + * Common routines for Sound Blaster compatible cards. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Daniel J. Rodriksson: Modified sbintr to handle 8 and 16 bit interrupts + * for full duplex support ( only sb16 by now ) + * Rolf Fokkens: Added (BETA?) support for ES1887 chips. + * (fokkensr@vertis.nl) Which means: You can adjust the recording levels. + * + * 2000/01/18 - separated sb_card and sb_common - + * Jeff Garzik + * + * 2000/09/18 - got rid of attach_uart401 + * Arnaldo Carvalho de Melo + * + * 2001/01/26 - replaced CLI/STI with spinlocks + * Chris Rankin + */ + +#include +#include +#include +#include +#include + +#include "sound_config.h" +#include "sound_firmware.h" + +#include "mpu401.h" + +#include "sb_mixer.h" +#include "sb.h" +#include "sb_ess.h" + +/* + * global module flag + */ + +int sb_be_quiet; + +static sb_devc *detected_devc; /* For communication from probe to init */ +static sb_devc *last_devc; /* For MPU401 initialization */ + +static unsigned char jazz_irq_bits[] = { + 0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6 +}; + +static unsigned char jazz_dma_bits[] = { + 0, 1, 0, 2, 0, 3, 0, 4 +}; + +void *smw_free; + +/* + * Jazz16 chipset specific control variables + */ + +static int jazz16_base; /* Not detected */ +static unsigned char jazz16_bits; /* I/O relocation bits */ +static spinlock_t jazz16_lock = SPIN_LOCK_UNLOCKED; + +/* + * Logitech Soundman Wave specific initialization code + */ + +#ifdef SMW_MIDI0001_INCLUDED +#include "smw-midi0001.h" +#else +static unsigned char *smw_ucode; +static int smw_ucodeLen; + +#endif + +sb_devc *last_sb; /* Last sb loaded */ + +int sb_dsp_command(sb_devc * devc, unsigned char val) +{ + int i; + unsigned long limit; + + limit = jiffies + HZ / 10; /* Timeout */ + + /* + * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes + * called while interrupts are disabled. This means that the timer is + * disabled also. However the timeout situation is a abnormal condition. + * Normally the DSP should be ready to accept commands after just couple of + * loops. + */ + + for (i = 0; i < 500000 && (limit-jiffies)>0; i++) + { + if ((inb(DSP_STATUS) & 0x80) == 0) + { + outb((val), DSP_COMMAND); + return 1; + } + } + printk(KERN_WARNING "Sound Blaster: DSP command(%x) timeout.\n", val); + return 0; +} + +int sb_dsp_get_byte(sb_devc * devc) +{ + int i; + + for (i = 1000; i; i--) + { + if (inb(DSP_DATA_AVAIL) & 0x80) + return inb(DSP_READ); + } + return 0xffff; +} + +static void sb_intr (sb_devc *devc) +{ + int status; + unsigned char src = 0xff; + + if (devc->model == MDL_SB16) + { + src = sb_getmixer(devc, IRQ_STAT); /* Interrupt source register */ + + if (src & 4) /* MPU401 interrupt */ + if(devc->midi_irq_cookie) + uart401intr(devc->irq, devc->midi_irq_cookie, NULL); + + if (!(src & 3)) + return; /* Not a DSP interrupt */ + } + if (devc->intr_active && (!devc->fullduplex || (src & 0x01))) + { + switch (devc->irq_mode) + { + case IMODE_OUTPUT: + DMAbuf_outputintr(devc->dev, 1); + break; + + case IMODE_INPUT: + DMAbuf_inputintr(devc->dev); + break; + + case IMODE_INIT: + break; + + case IMODE_MIDI: + sb_midi_interrupt(devc); + break; + + default: + /* printk(KERN_WARN "Sound Blaster: Unexpected interrupt\n"); */ + ; + } + } + else if (devc->intr_active_16 && (src & 0x02)) + { + switch (devc->irq_mode_16) + { + case IMODE_OUTPUT: + DMAbuf_outputintr(devc->dev, 1); + break; + + case IMODE_INPUT: + DMAbuf_inputintr(devc->dev); + break; + + case IMODE_INIT: + break; + + default: + /* printk(KERN_WARN "Sound Blaster: Unexpected interrupt\n"); */ + ; + } + } + /* + * Acknowledge interrupts + */ + + if (src & 0x01) + status = inb(DSP_DATA_AVAIL); + + if (devc->model == MDL_SB16 && src & 0x02) + status = inb(DSP_DATA_AVL16); +} + +static void pci_intr(sb_devc *devc) +{ + int src = inb(devc->pcibase+0x1A); + src&=3; + if(src) + sb_intr(devc); +} + +static void sbintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + sb_devc *devc = dev_id; + + devc->irq_ok = 1; + + switch (devc->model) { + case MDL_ESSPCI: + pci_intr (devc); + break; + + case MDL_ESS: + ess_intr (devc); + break; + default: + sb_intr (devc); + break; + } +} + +int sb_dsp_reset(sb_devc * devc) +{ + int loopc; + + DEB(printk("Entered sb_dsp_reset()\n")); + + if (devc->model == MDL_ESS) return ess_dsp_reset (devc); + + /* This is only for non-ESS chips */ + + outb(1, DSP_RESET); + + udelay(10); + outb(0, DSP_RESET); + udelay(30); + + for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++); + + if (inb(DSP_READ) != 0xAA) + { + DDB(printk("sb: No response to RESET\n")); + return 0; /* Sorry */ + } + + DEB(printk("sb_dsp_reset() OK\n")); + + return 1; +} + +static void dsp_get_vers(sb_devc * devc) +{ + int i; + + unsigned long flags; + + DDB(printk("Entered dsp_get_vers()\n")); + spin_lock_irqsave(&devc->lock, flags); + devc->major = devc->minor = 0; + sb_dsp_command(devc, 0xe1); /* Get version */ + + for (i = 100000; i; i--) + { + if (inb(DSP_DATA_AVAIL) & 0x80) + { + if (devc->major == 0) + devc->major = inb(DSP_READ); + else + { + devc->minor = inb(DSP_READ); + break; + } + } + } + spin_unlock_irqrestore(&devc->lock, flags); + DDB(printk("DSP version %d.%02d\n", devc->major, devc->minor)); +} + +static int sb16_set_dma_hw(sb_devc * devc) +{ + int bits; + + if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3) + { + printk(KERN_ERR "SB16: Invalid 8 bit DMA (%d)\n", devc->dma8); + return 0; + } + bits = (1 << devc->dma8); + + if (devc->dma16 >= 5 && devc->dma16 <= 7) + bits |= (1 << devc->dma16); + + sb_setmixer(devc, DMA_NR, bits); + return 1; +} + +static void sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config) +{ + /* + * This routine initializes new MIDI port setup register of SB Vibra (CT2502). + */ + unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06; + + switch (hw_config->io_base) + { + case 0x300: + sb_setmixer(devc, 0x84, bits | 0x04); + break; + + case 0x330: + sb_setmixer(devc, 0x84, bits | 0x00); + break; + + default: + sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */ + printk(KERN_ERR "SB16: Invalid MIDI I/O port %x\n", hw_config->io_base); + } +} + +static int sb16_set_irq_hw(sb_devc * devc, int level) +{ + int ival; + + switch (level) + { + case 5: + ival = 2; + break; + case 7: + ival = 4; + break; + case 9: + ival = 1; + break; + case 10: + ival = 8; + break; + default: + printk(KERN_ERR "SB16: Invalid IRQ%d\n", level); + return 0; + } + sb_setmixer(devc, IRQ_NR, ival); + return 1; +} + +static void relocate_Jazz16(sb_devc * devc, struct address_info *hw_config) +{ + unsigned char bits = 0; + unsigned long flags; + + if (jazz16_base != 0 && jazz16_base != hw_config->io_base) + return; + + switch (hw_config->io_base) + { + case 0x220: + bits = 1; + break; + case 0x240: + bits = 2; + break; + case 0x260: + bits = 3; + break; + default: + return; + } + bits = jazz16_bits = bits << 5; + jazz16_base = hw_config->io_base; + + /* + * Magic wake up sequence by writing to 0x201 (aka Joystick port) + */ + spin_lock_irqsave(&jazz16_lock, flags); + outb((0xAF), 0x201); + outb((0x50), 0x201); + outb((bits), 0x201); + spin_unlock_irqrestore(&jazz16_lock, flags); +} + +static int init_Jazz16(sb_devc * devc, struct address_info *hw_config) +{ + char name[100]; + /* + * First try to check that the card has Jazz16 chip. It identifies itself + * by returning 0x12 as response to DSP command 0xfa. + */ + + if (!sb_dsp_command(devc, 0xfa)) + return 0; + + if (sb_dsp_get_byte(devc) != 0x12) + return 0; + + /* + * OK so far. Now configure the IRQ and DMA channel used by the card. + */ + if (hw_config->irq < 1 || hw_config->irq > 15 || jazz_irq_bits[hw_config->irq] == 0) + { + printk(KERN_ERR "Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq); + return 0; + } + if (hw_config->dma < 0 || hw_config->dma > 3 || jazz_dma_bits[hw_config->dma] == 0) + { + printk(KERN_ERR "Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma); + return 0; + } + if (hw_config->dma2 < 0) + { + printk(KERN_ERR "Jazz16: No 16 bit DMA channel defined\n"); + return 0; + } + if (hw_config->dma2 < 5 || hw_config->dma2 > 7 || jazz_dma_bits[hw_config->dma2] == 0) + { + printk(KERN_ERR "Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2); + return 0; + } + devc->dma16 = hw_config->dma2; + + if (!sb_dsp_command(devc, 0xfb)) + return 0; + + if (!sb_dsp_command(devc, jazz_dma_bits[hw_config->dma] | + (jazz_dma_bits[hw_config->dma2] << 4))) + return 0; + + if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq])) + return 0; + + /* + * Now we have configured a standard Jazz16 device. + */ + devc->model = MDL_JAZZ; + strcpy(name, "Jazz16"); + + hw_config->name = "Jazz16"; + devc->caps |= SB_NO_MIDI; + return 1; +} + +static void relocate_ess1688(sb_devc * devc) +{ + unsigned char bits; + + switch (devc->base) + { + case 0x220: + bits = 0x04; + break; + case 0x230: + bits = 0x05; + break; + case 0x240: + bits = 0x06; + break; + case 0x250: + bits = 0x07; + break; + default: + return; /* Wrong port */ + } + + DDB(printk("Doing ESS1688 address selection\n")); + + /* + * ES1688 supports two alternative ways for software address config. + * First try the so called Read-Sequence-Key method. + */ + + /* Reset the sequence logic */ + inb(0x229); + inb(0x229); + inb(0x229); + + /* Perform the read sequence */ + inb(0x22b); + inb(0x229); + inb(0x22b); + inb(0x229); + inb(0x229); + inb(0x22b); + inb(0x229); + + /* Select the base address by reading from it. Then probe using the port. */ + inb(devc->base); + if (sb_dsp_reset(devc)) /* Bingo */ + return; + +#if 0 /* This causes system lockups (Nokia 386/25 at least) */ + /* + * The last resort is the system control register method. + */ + + outb((0x00), 0xfb); /* 0xFB is the unlock register */ + outb((0x00), 0xe0); /* Select index 0 */ + outb((bits), 0xe1); /* Write the config bits */ + outb((0x00), 0xf9); /* 0xFB is the lock register */ +#endif +} + +int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo) +{ + sb_devc sb_info; + sb_devc *devc = &sb_info; + + memset((char *) &sb_info, 0, sizeof(sb_info)); /* Zero everything */ + + /* Copy module options in place */ + if(sbmo) memcpy(&devc->sbmo, sbmo, sizeof(struct sb_module_options)); + + sb_info.my_mididev = -1; + sb_info.my_mixerdev = -1; + sb_info.dev = -1; + + /* + * Initialize variables + */ + + DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base)); + if (check_region(hw_config->io_base, 16)) + { +#ifdef MODULE + printk(KERN_INFO "sb: I/O region in use.\n"); +#endif + return 0; + } + + devc->lock = SPIN_LOCK_UNLOCKED; + devc->type = hw_config->card_subtype; + + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->dma8 = hw_config->dma; + + devc->dma16 = -1; + devc->pcibase = pciio; + + if(pci == SB_PCI_ESSMAESTRO) + { + devc->model = MDL_ESSPCI; + devc->caps |= SB_PCI_IRQ; + hw_config->driver_use_1 |= SB_PCI_IRQ; + hw_config->card_subtype = MDL_ESSPCI; + } + + if(pci == SB_PCI_YAMAHA) + { + devc->model = MDL_YMPCI; + devc->caps |= SB_PCI_IRQ; + hw_config->driver_use_1 |= SB_PCI_IRQ; + hw_config->card_subtype = MDL_YMPCI; + + printk("Yamaha PCI mode.\n"); + } + + if (devc->sbmo.acer) + { + unsigned long flags; + + spin_lock_irqsave(&devc->lock, flags); + inb(devc->base + 0x09); + inb(devc->base + 0x09); + inb(devc->base + 0x09); + inb(devc->base + 0x0b); + inb(devc->base + 0x09); + inb(devc->base + 0x0b); + inb(devc->base + 0x09); + inb(devc->base + 0x09); + inb(devc->base + 0x0b); + inb(devc->base + 0x09); + inb(devc->base + 0x00); + spin_unlock_irqrestore(&devc->lock, flags); + } + /* + * Detect the device + */ + + if (sb_dsp_reset(devc)) + dsp_get_vers(devc); + else + devc->major = 0; + + if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW) + if (devc->major == 0 || (devc->major == 3 && devc->minor == 1)) + relocate_Jazz16(devc, hw_config); + + if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0)) + relocate_ess1688(devc); + + if (!sb_dsp_reset(devc)) + { + DDB(printk("SB reset failed\n")); +#ifdef MODULE + printk(KERN_INFO "sb: dsp reset failed.\n"); +#endif + return 0; + } + if (devc->major == 0) + dsp_get_vers(devc); + + if (devc->major == 3 && devc->minor == 1) + { + if (devc->type == MDL_AZTECH) /* SG Washington? */ + { + if (sb_dsp_command(devc, 0x09)) + if (sb_dsp_command(devc, 0x00)) /* Enter WSS mode */ + { + int i; + + /* Have some delay */ + for (i = 0; i < 10000; i++) + inb(DSP_DATA_AVAIL); + devc->caps = SB_NO_AUDIO | SB_NO_MIDI; /* Mixer only */ + devc->model = MDL_AZTECH; + } + } + } + + if(devc->type == MDL_ESSPCI) + devc->model = MDL_ESSPCI; + + if(devc->type == MDL_YMPCI) + { + printk("YMPCI selected\n"); + devc->model = MDL_YMPCI; + } + + /* + * Save device information for sb_dsp_init() + */ + + + detected_devc = (sb_devc *)kmalloc(sizeof(sb_devc), GFP_KERNEL); + if (detected_devc == NULL) + { + printk(KERN_ERR "sb: Can't allocate memory for device information\n"); + return 0; + } + memcpy(detected_devc, devc, sizeof(sb_devc)); + MDB(printk(KERN_INFO "SB %d.%02d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base)); + return 1; +} + +int sb_dsp_init(struct address_info *hw_config, struct module *owner) +{ + sb_devc *devc; + char name[100]; + extern int sb_be_quiet; + int mixer22, mixer30; + +/* + * Check if we had detected a SB device earlier + */ + DDB(printk("sb_dsp_init(%x) entered\n", hw_config->io_base)); + name[0] = 0; + + if (detected_devc == NULL) + { + MDB(printk("No detected device\n")); + return 0; + } + devc = detected_devc; + detected_devc = NULL; + + if (devc->base != hw_config->io_base) + { + DDB(printk("I/O port mismatch\n")); + return 0; + } + /* + * Now continue initialization of the device + */ + + devc->caps = hw_config->driver_use_1; + + if (!((devc->caps & SB_NO_AUDIO) && (devc->caps & SB_NO_MIDI)) && hw_config->irq > 0) + { /* IRQ setup */ + + /* + * ESS PCI cards do shared PCI IRQ stuff. Since they + * will get shared PCI irq lines we must cope. + */ + + int i=(devc->caps&SB_PCI_IRQ)?SA_SHIRQ:0; + + if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0) + { + printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq); + return 0; + } + devc->irq_ok = 0; + + if (devc->major == 4) + if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */ + { + free_irq(devc->irq, devc); + return 0; + } + if ((devc->type == 0 || devc->type == MDL_ESS) && + devc->major == 3 && devc->minor == 1) + { /* Handle various chipsets which claim they are SB Pro compatible */ + if ((devc->type != 0 && devc->type != MDL_ESS) || + !ess_init(devc, hw_config)) + { + if ((devc->type != 0 && devc->type != MDL_JAZZ && + devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config)) + { + DDB(printk("This is a genuine SB Pro\n")); + } + } + } + if (devc->major == 4 && devc->minor <= 11 ) /* Won't work */ + devc->irq_ok = 1; + else + { + int n; + + for (n = 0; n < 3 && devc->irq_ok == 0; n++) + { + if (sb_dsp_command(devc, 0xf2)) /* Cause interrupt immediately */ + { + int i; + + for (i = 0; !devc->irq_ok && i < 10000; i++); + } + } + if (!devc->irq_ok) + printk(KERN_WARNING "sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq); + else + { + DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq)); + } + } + } /* IRQ setup */ + request_region(hw_config->io_base, 16, "soundblaster"); + + last_sb = devc; + + switch (devc->major) + { + case 1: /* SB 1.0 or 1.5 */ + devc->model = hw_config->card_subtype = MDL_SB1; + break; + + case 2: /* SB 2.x */ + if (devc->minor == 0) + devc->model = hw_config->card_subtype = MDL_SB2; + else + devc->model = hw_config->card_subtype = MDL_SB201; + break; + + case 3: /* SB Pro and most clones */ + switch (devc->model) { + case 0: + devc->model = hw_config->card_subtype = MDL_SBPRO; + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster Pro (8 BIT ONLY)"; + break; + case MDL_ESS: + ess_dsp_init(devc, hw_config); + break; + } + break; + + case 4: + devc->model = hw_config->card_subtype = MDL_SB16; + /* + * ALS007 and ALS100 return DSP version 4.2 and have 2 post-reset !=0 + * registers at 0x3c and 0x4c (output ctrl registers on ALS007) whereas + * a "standard" SB16 doesn't have a register at 0x4c. ALS100 actively + * updates register 0x22 whenever 0x30 changes, as per the SB16 spec. + * Since ALS007 doesn't, this can be used to differentiate the 2 cards. + */ + if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c)) + { + mixer30 = sb_getmixer(devc,0x30); + sb_setmixer(devc,0x22,(mixer22=sb_getmixer(devc,0x22)) & 0x0f); + sb_setmixer(devc,0x30,0xff); + /* ALS100 will force 0x30 to 0xf8 like SB16; ALS007 will allow 0xff. */ + /* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10. */ + if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10)) + { + devc->submodel = SUBMDL_ALS100; + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster 16 (ALS-100)"; + } + else + { + sb_setmixer(devc,0x3c,0x1f); /* Enable all inputs */ + sb_setmixer(devc,0x4c,0x1f); + sb_setmixer(devc,0x22,mixer22); /* Restore 0x22 to original value */ + devc->submodel = SUBMDL_ALS007; + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster 16 (ALS-007)"; + } + sb_setmixer(devc,0x30,mixer30); + } + else if (hw_config->name == NULL) + hw_config->name = "Sound Blaster 16"; + + if (hw_config->dma2 == -1) + devc->dma16 = devc->dma8; + else if (hw_config->dma2 < 5 || hw_config->dma2 > 7) + { + printk(KERN_WARNING "SB16: Bad or missing 16 bit DMA channel\n"); + devc->dma16 = devc->dma8; + } + else + devc->dma16 = hw_config->dma2; + + if(!sb16_set_dma_hw(devc)) { + free_irq(devc->irq, devc); + release_region(hw_config->io_base, 16); + return 0; + } + + devc->caps |= SB_NO_MIDI; + } + + if (!(devc->caps & SB_NO_MIXER)) + if (devc->major == 3 || devc->major == 4) + sb_mixer_init(devc, owner); + + if (!(devc->caps & SB_NO_MIDI)) + sb_dsp_midi_init(devc, owner); + + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)"; + + sprintf(name, "%s (%d.%02d)", hw_config->name, devc->major, devc->minor); + conf_printf(name, hw_config); + + /* + * Assuming that a sound card is Sound Blaster (compatible) is the most common + * configuration error and the mother of all problems. Usually sound cards + * emulate SB Pro but in addition they have a 16 bit native mode which should be + * used in Unix. See Readme.cards for more information about configuring OSS/Free + * properly. + */ + if (devc->model <= MDL_SBPRO) + { + if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1 (rare ones may have 3.2). */ + { + printk(KERN_INFO "This sound card may not be fully Sound Blaster Pro compatible.\n"); + printk(KERN_INFO "In many cases there is another way to configure OSS so that\n"); + printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n"); + printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n"); + } + else if (!sb_be_quiet && devc->model == MDL_SBPRO) + { + printk(KERN_INFO "SB DSP version is just %d.%02d which means that your card is\n", devc->major, devc->minor); + printk(KERN_INFO "several years old (8 bit only device) or alternatively the sound driver\n"); + printk(KERN_INFO "is incorrectly configured.\n"); + } + } + hw_config->card_subtype = devc->model; + hw_config->slots[0]=devc->dev; + last_devc = devc; /* For SB MPU detection */ + + if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0) + { + if (sound_alloc_dma(devc->dma8, "SoundBlaster8")) + { + printk(KERN_WARNING "Sound Blaster: Can't allocate 8 bit DMA channel %d\n", devc->dma8); + } + if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) + { + if (sound_alloc_dma(devc->dma16, "SoundBlaster16")) + printk(KERN_WARNING "Sound Blaster: can't allocate 16 bit DMA channel %d.\n", devc->dma16); + } + sb_audio_init(devc, name, owner); + hw_config->slots[0]=devc->dev; + } + else + { + MDB(printk("Sound Blaster: no audio devices found.\n")); + } + return 1; +} + +void sb_dsp_disable_midi(int io_base) +{ +} + +void sb_dsp_disable_recording(int io_base) +{ +} + +/* if (sbmpu) below we allow mpu401 to manage the midi devs + otherwise we have to unload them. (Andrzej Krzysztofowicz) */ + +void sb_dsp_unload(struct address_info *hw_config, int sbmpu) +{ + sb_devc *devc; + + devc = audio_devs[hw_config->slots[0]]->devc; + + if (devc && devc->base == hw_config->io_base) + { + if ((devc->model & MDL_ESS) && devc->pcibase) + release_region(devc->pcibase, 8); + + release_region(devc->base, 16); + + if (!(devc->caps & SB_NO_AUDIO)) + { + sound_free_dma(devc->dma8); + if (devc->dma16 >= 0) + sound_free_dma(devc->dma16); + } + if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI)) + { + if (devc->irq > 0) + free_irq(devc->irq, devc); + + sb_mixer_unload(devc); + /* We don't have to do this bit any more the UART401 is its own + master -- Krzysztof Halasa */ + /* But we have to do it, if UART401 is not detected */ + if (!sbmpu) + sound_unload_mididev(devc->my_mididev); + sound_unload_audiodev(devc->dev); + } + kfree(devc); + } + else + release_region(hw_config->io_base, 16); + if(detected_devc) + kfree(detected_devc); +} + +/* + * Mixer access routines + * + * ES1887 modifications: some mixer registers reside in the + * range above 0xa0. These must be accessed in another way. + */ + +void sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value) +{ + unsigned long flags; + + if (devc->model == MDL_ESS) return ess_setmixer (devc, port, value); + + spin_lock_irqsave(&devc->lock, flags); + + outb(((unsigned char) (port & 0xff)), MIXER_ADDR); + udelay(20); + outb(((unsigned char) (value & 0xff)), MIXER_DATA); + udelay(20); + + spin_unlock_irqrestore(&devc->lock, flags); +} + +unsigned int sb_getmixer(sb_devc * devc, unsigned int port) +{ + unsigned int val; + unsigned long flags; + + if (devc->model == MDL_ESS) return ess_getmixer (devc, port); + + spin_lock_irqsave(&devc->lock, flags); + + outb(((unsigned char) (port & 0xff)), MIXER_ADDR); + udelay(20); + val = inb(MIXER_DATA); + udelay(20); + + spin_unlock_irqrestore(&devc->lock, flags); + + return val; +} + +void sb_chgmixer + (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val) +{ + int value; + + value = sb_getmixer(devc, reg); + value = (value & ~mask) | (val & mask); + sb_setmixer(devc, reg, value); +} + +/* + * MPU401 MIDI initialization. + */ + +static void smw_putmem(sb_devc * devc, int base, int addr, unsigned char val) +{ + unsigned long flags; + + spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */ + + outb((addr & 0xff), base + 1); /* Low address bits */ + outb((addr >> 8), base + 2); /* High address bits */ + outb((val), base); /* Data */ + + spin_unlock_irqrestore(&jazz16_lock, flags); +} + +static unsigned char smw_getmem(sb_devc * devc, int base, int addr) +{ + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */ + + outb((addr & 0xff), base + 1); /* Low address bits */ + outb((addr >> 8), base + 2); /* High address bits */ + val = inb(base); /* Data */ + + spin_unlock_irqrestore(&jazz16_lock, flags); + return val; +} + +static int smw_midi_init(sb_devc * devc, struct address_info *hw_config) +{ + int mpu_base = hw_config->io_base; + int mp_base = mpu_base + 4; /* Microcontroller base */ + int i; + unsigned char control; + + + /* + * Reset the microcontroller so that the RAM can be accessed + */ + + control = inb(mpu_base + 7); + outb((control | 3), mpu_base + 7); /* Set last two bits to 1 (?) */ + outb(((control & 0xfe) | 2), mpu_base + 7); /* xxxxxxx0 resets the mc */ + + mdelay(3); /* Wait at least 1ms */ + + outb((control & 0xfc), mpu_base + 7); /* xxxxxx00 enables RAM */ + + /* + * Detect microcontroller by probing the 8k RAM area + */ + smw_putmem(devc, mp_base, 0, 0x00); + smw_putmem(devc, mp_base, 1, 0xff); + udelay(10); + + if (smw_getmem(devc, mp_base, 0) != 0x00 || smw_getmem(devc, mp_base, 1) != 0xff) + { + DDB(printk("SM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1))); + return 0; /* No RAM */ + } + /* + * There is RAM so assume it's really a SM Wave + */ + + devc->model = MDL_SMW; + smw_mixer_init(devc); + +#ifdef MODULE + if (!smw_ucode) + { + smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode); + smw_free = smw_ucode; + } +#endif + if (smw_ucodeLen > 0) + { + if (smw_ucodeLen != 8192) + { + printk(KERN_ERR "SM Wave: Invalid microcode (MIDI0001.BIN) length\n"); + return 1; + } + /* + * Download microcode + */ + + for (i = 0; i < 8192; i++) + smw_putmem(devc, mp_base, i, smw_ucode[i]); + + /* + * Verify microcode + */ + + for (i = 0; i < 8192; i++) + if (smw_getmem(devc, mp_base, i) != smw_ucode[i]) + { + printk(KERN_ERR "SM Wave: Microcode verification failed\n"); + return 0; + } + } + control = 0; +#ifdef SMW_SCSI_IRQ + /* + * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt + * is disabled by default. + * + * FIXME - make this a module option + * + * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10. + */ + { + static unsigned char scsi_irq_bits[] = { + 0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0 + }; + control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6; + } +#endif + +#ifdef SMW_OPL4_ENABLE + /* + * Make the OPL4 chip visible on the PC bus at 0x380. + * + * There is no need to enable this feature since this driver + * doesn't support OPL4 yet. Also there is no RAM in SM Wave so + * enabling OPL4 is pretty useless. + */ + control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */ + /* control |= 0x20; Uncomment this if you want to use IRQ7 */ +#endif + outb((control | 0x03), mpu_base + 7); /* xxxxxx11 restarts */ + hw_config->name = "SoundMan Wave"; + return 1; +} + +static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config) +{ + int mpu_base = hw_config->io_base; + int sb_base = devc->base; + int irq = hw_config->irq; + + unsigned char bits = 0; + unsigned long flags; + + if (irq < 0) + irq *= -1; + + if (irq < 1 || irq > 15 || + jazz_irq_bits[irq] == 0) + { + printk(KERN_ERR "Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq); + return 0; + } + switch (sb_base) + { + case 0x220: + bits = 1; + break; + case 0x240: + bits = 2; + break; + case 0x260: + bits = 3; + break; + default: + return 0; + } + bits = jazz16_bits = bits << 5; + switch (mpu_base) + { + case 0x310: + bits |= 1; + break; + case 0x320: + bits |= 2; + break; + case 0x330: + bits |= 3; + break; + default: + printk(KERN_ERR "Jazz16: Invalid MIDI I/O port %x\n", mpu_base); + return 0; + } + /* + * Magic wake up sequence by writing to 0x201 (aka Joystick port) + */ + spin_lock_irqsave(&jazz16_lock, flags); + outb(0xAF, 0x201); + outb(0x50, 0x201); + outb(bits, 0x201); + spin_unlock_irqrestore(&jazz16_lock, flags); + + hw_config->name = "Jazz16"; + smw_midi_init(devc, hw_config); + + if (!sb_dsp_command(devc, 0xfb)) + return 0; + + if (!sb_dsp_command(devc, jazz_dma_bits[devc->dma8] | + (jazz_dma_bits[devc->dma16] << 4))) + return 0; + + if (!sb_dsp_command(devc, jazz_irq_bits[devc->irq] | + (jazz_irq_bits[irq] << 4))) + return 0; + + return 1; +} + +int probe_sbmpu(struct address_info *hw_config, struct module *owner) +{ + sb_devc *devc = last_devc; + int ret; + + if (last_devc == NULL) + return 0; + + last_devc = 0; + + if (hw_config->io_base <= 0) + { + /* The real vibra16 is fine about this, but we have to go + wipe up after Cyrix again */ + + if(devc->model == MDL_SB16 && devc->minor >= 12) + { + unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06; + sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */ + } + return 0; + } + +#if defined(CONFIG_SOUND_MPU401) + if (devc->model == MDL_ESS) + { + if (check_region(hw_config->io_base, 2)) + { + printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + if (!ess_midi_init(devc, hw_config)) + return 0; + hw_config->name = "ESS1xxx MPU"; + devc->midi_irq_cookie = NULL; + if (!probe_mpu401(hw_config)) + return 0; + attach_mpu401(hw_config, owner); + if (last_sb->irq == -hw_config->irq) + last_sb->midi_irq_cookie=(void *)hw_config->slots[1]; + return 1; + } +#endif + + switch (devc->model) + { + case MDL_SB16: + if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330) + { + printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base); + return 0; + } + hw_config->name = "Sound Blaster 16"; + if (hw_config->irq < 3 || hw_config->irq == devc->irq) + hw_config->irq = -devc->irq; + if (devc->minor > 12) /* What is Vibra's version??? */ + sb16_set_mpu_port(devc, hw_config); + break; + + case MDL_JAZZ: + if (hw_config->irq < 3 || hw_config->irq == devc->irq) + hw_config->irq = -devc->irq; + if (!init_Jazz16_midi(devc, hw_config)) + return 0; + break; + + case MDL_YMPCI: + hw_config->name = "Yamaha PCI Legacy"; + printk("Yamaha PCI legacy UART401 check.\n"); + break; + default: + return 0; + } + + ret = probe_uart401(hw_config, owner); + if (ret) + last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc; + return ret; +} + +void unload_sbmpu(struct address_info *hw_config) +{ +#if defined(CONFIG_SOUND_MPU401) + if (!strcmp (hw_config->name, "ESS1xxx MPU")) { + unload_mpu401(hw_config); + return; + } +#endif + unload_uart401(hw_config); +} + +EXPORT_SYMBOL(sb_dsp_init); +EXPORT_SYMBOL(sb_dsp_detect); +EXPORT_SYMBOL(sb_dsp_unload); +EXPORT_SYMBOL(sb_dsp_disable_midi); +EXPORT_SYMBOL(sb_be_quiet); +EXPORT_SYMBOL(probe_sbmpu); +EXPORT_SYMBOL(unload_sbmpu); +EXPORT_SYMBOL(smw_free); +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sb_ess.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,1826 @@ +#undef FKS_LOGGING +#undef FKS_TEST + +/* + * tabs should be 4 spaces, in vi(m): set tabstop=4 + * + * TODO: consistency speed calculations!! + * cleanup! + * ????: Did I break MIDI support? + * + * History: + * + * Rolf Fokkens (Dec 20 1998): ES188x recording level support on a per + * fokkensr@vertis.nl input basis. + * (Dec 24 1998): Recognition of ES1788, ES1887, ES1888, + * ES1868, ES1869 and ES1878. Could be used for + * specific handling in the future. All except + * ES1887 and ES1888 and ES688 are handled like + * ES1688. + * (Dec 27 1998): RECLEV for all (?) ES1688+ chips. ES188x now + * have the "Dec 20" support + RECLEV + * (Jan 2 1999): Preparation for Full Duplex. This means + * Audio 2 is now used for playback when dma16 + * is specified. The next step would be to use + * Audio 1 and Audio 2 at the same time. + * (Jan 9 1999): Put all ESS stuff into sb_ess.[ch], this + * includes both the ESS stuff that has been in + * sb_*[ch] before I touched it and the ESS support + * I added later + * (Jan 23 1999): Full Duplex seems to work. I wrote a small + * test proggy which works OK. Haven't found + * any applications to test it though. So why did + * I bother to create it anyway?? :) Just for + * fun. + * (May 2 1999): I tried to be too smart by "introducing" + * ess_calc_best_speed (). The idea was that two + * dividers could be used to setup a samplerate, + * ess_calc_best_speed () would choose the best. + * This works for playback, but results in + * recording problems for high samplerates. I + * fixed this by removing ess_calc_best_speed () + * and just doing what the documentation says. + * Andy Sloane (Jun 4 1999): Stole some code from ALSA to fix the playback + * andy@guildsoftware.com speed on ES1869, ES1879, ES1887, and ES1888. + * 1879's were previously ignored by this driver; + * added (untested) support for those. + * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for + * zezo@inet.bg _ALL_ ESS models, not only ES1887 + * + * This files contains ESS chip specifics. It's based on the existing ESS + * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This + * file adds features like: + * - Chip Identification (as shown in /proc/sound) + * - RECLEV support for ES1688 and later + * - 6 bits playback level support chips later than ES1688 + * - Recording level support on a per-device basis for ES1887 + * - Full-Duplex for ES1887 + * + * Full duplex is enabled by specifying dma16. While the normal dma must + * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit + * DMA channel, while the others are 8 bit.. + * + * ESS detection isn't full proof (yet). If it fails an additional module + * parameter esstype can be specified to be one of the following: + * -1, 0, 688, 1688, 1868, 1869, 1788, 1887, 1888 + * -1 means: mimic 2.0 behaviour, + * 0 means: auto detect. + * others: explicitly specify chip + * -1 is default, cause auto detect still doesn't work. + */ + +/* + * About the documentation + * + * I don't know if the chips all are OK, but the documentation is buggy. 'cause + * I don't have all the cips myself, there's a lot I cannot verify. I'll try to + * keep track of my latest insights about his here. If you have additional info, + * please enlighten me (fokkensr@vertis.nl)! + * + * I had the impression that ES1688 also has 6 bit master volume control. The + * documentation about ES1888 (rev C, october '95) claims that ES1888 has + * the following features ES1688 doesn't have: + * - 6 bit master volume + * - Full Duplex + * So ES1688 apparently doesn't have 6 bit master volume control, but the + * ES1688 does have RECLEV control. Makes me wonder: does ES688 have it too? + * Without RECLEV ES688 won't be much fun I guess. + * + * From the ES1888 (rev C, october '95) documentation I got the impression + * that registers 0x68 to 0x6e don't exist which means: no recording volume + * controls. To my surprise the ES888 documentation (1/14/96) claims that + * ES888 does have these record mixer registers, but that ES1888 doesn't have + * 0x69 and 0x6b. So the rest should be there. + * + * I'm trying to get ES1887 Full Duplex. Audio 2 is playback only, while Audio 2 + * is both record and playback. I think I should use Audio 2 for all playback. + * + * The documentation is an adventure: it's close but not fully accurate. I + * found out that after a reset some registers are *NOT* reset, though the + * docs say the would be. Interresting ones are 0x7f, 0x7d and 0x7a. They are + * related to the Audio 2 channel. I also was suprised about the consequenses + * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves + * into ES1888 mode. This means that it claims IRQ 11, which happens to be my + * ISDN adapter. Needless to say it no longer worked. I now understand why + * after rebooting 0x7f already was 0x05, the value of my choice: the BIOS + * did it. + * + * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is decribed + * as if it's exactly the same as register 0xa1. This is *NOT* true. The + * description of 0x70 in ES1869 docs is accurate however. + * Well, the assumption about ES1869 was wrong: register 0x70 is very much + * like register 0xa1, except that bit 7 is allways 1, whatever you want + * it to be. + * + * When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2 + * has effect. + * + * Software reset not being able to reset all registers is great! Especially + * the fact that register 0x78 isn't reset is great when you wanna change back + * to single dma operation (simplex): audio 2 is still operation, and uses the + * same dma as audio 1: your ess changes into a funny echo machine. + * + * Received the new that ES1688 is detected as a ES1788. Did some thinking: + * the ES1887 detection scheme suggests in step 2 to try if bit 3 of register + * 0x64 can be changed. This is inaccurate, first I inverted the * check: "If + * can be modified, it's a 1688", which lead to a correct detection + * of my ES1887. It resulted however in bad detection of 1688 (reported by mail) + * and 1868 (if no PnP detection first): they result in a 1788 being detected. + * I don't have docs on 1688, but I do have docs on 1868: The documentation is + * probably inaccurate in the fact that I should check bit 2, not bit 3. This + * is what I do now. + */ + +/* + * About recognition of ESS chips + * + * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in + * a (preliminary ??) datasheet on ES1887. It's aim is to identify ES1887, but + * during detection the text claims that "this chip may be ..." when a step + * fails. This scheme is used to distinct between the above chips. + * It appears however that some PnP chips like ES1868 are recognized as ES1788 + * by the ES1887 detection scheme. These PnP chips can be detected in another + * way however: ES1868, ES1869 and ES1878 can be recognized (full proof I think) + * by repeatedly reading mixer register 0x40. This is done by ess_identify in + * sb_common.c. + * This results in the following detection steps: + * - distinct between ES688 and ES1688+ (as always done in this driver) + * if ES688 we're ready + * - try to detect ES1868, ES1869 or ES1878 + * if successful we're ready + * - try to detect ES1888, ES1887 or ES1788 + * if successful we're ready + * - Dunno. Must be 1688. Will do in general + * + * About RECLEV support: + * + * The existing ES1688 support didn't take care of the ES1688+ recording + * levels very well. Whenever a device was selected (recmask) for recording + * it's recording level was loud, and it couldn't be changed. The fact that + * internal register 0xb4 could take care of RECLEV, didn't work meaning until + * it's value was restored every time the chip was reset; this reset the + * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with. + * + * About ES1887 support: + * + * The ES1887 has separate registers to control the recording levels, for all + * inputs. The ES1887 specific software makes these levels the same as their + * corresponding playback levels, unless recmask says they aren't recorded. In + * the latter case the recording volumes are 0. + * Now recording levels of inputs can be controlled, by changing the playback + * levels. Futhermore several devices can be recorded together (which is not + * possible with the ES1688. + * Besides the separate recording level control for each input, the common + * recordig level can also be controlled by RECLEV as described above. + * + * Not only ES1887 have this recording mixer. I know the following from the + * documentation: + * ES688 no + * ES1688 no + * ES1868 no + * ES1869 yes + * ES1878 no + * ES1879 yes + * ES1888 no/yes Contradicting documentation; most recent: yes + * ES1946 yes This is a PCI chip; not handled by this driver + */ + +#include +#include + +#include "sound_config.h" +#include "sb_mixer.h" +#include "sb.h" + +#include "sb_ess.h" + +#define ESSTYPE_LIKE20 -1 /* Mimic 2.0 behaviour */ +#define ESSTYPE_DETECT 0 /* Mimic 2.0 behaviour */ + +#define SUBMDL_ES1788 0x10 /* Subtype ES1788 for specific handling */ +#define SUBMDL_ES1868 0x11 /* Subtype ES1868 for specific handling */ +#define SUBMDL_ES1869 0x12 /* Subtype ES1869 for specific handling */ +#define SUBMDL_ES1878 0x13 /* Subtype ES1878 for specific handling */ +#define SUBMDL_ES1879 0x16 /* ES1879 was initially forgotten */ +#define SUBMDL_ES1887 0x14 /* Subtype ES1887 for specific handling */ +#define SUBMDL_ES1888 0x15 /* Subtype ES1888 for specific handling */ + +#define SB_CAP_ES18XX_RATE 0x100 + +#define ES1688_CLOCK1 795444 /* 128 - div */ +#define ES1688_CLOCK2 397722 /* 256 - div */ +#define ES18XX_CLOCK1 793800 /* 128 - div */ +#define ES18XX_CLOCK2 768000 /* 256 - div */ + +#ifdef FKS_LOGGING +static void ess_show_mixerregs (sb_devc *devc); +#endif +static int ess_read (sb_devc * devc, unsigned char reg); +static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data); +static void ess_chgmixer + (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val); + +/**************************************************************************** + * * + * ESS audio * + * * + ****************************************************************************/ + +struct ess_command {short cmd; short data;}; + +/* + * Commands for initializing Audio 1 for input (record) + */ +static struct ess_command ess_i08m[] = /* input 8 bit mono */ + { {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} }; +static struct ess_command ess_i16m[] = /* input 16 bit mono */ + { {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} }; +static struct ess_command ess_i08s[] = /* input 8 bit stereo */ + { {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} }; +static struct ess_command ess_i16s[] = /* input 16 bit stereo */ + { {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} }; + +static struct ess_command *ess_inp_cmds[] = + { ess_i08m, ess_i16m, ess_i08s, ess_i16s }; + + +/* + * Commands for initializing Audio 1 for output (playback) + */ +static struct ess_command ess_o08m[] = /* output 8 bit mono */ + { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} }; +static struct ess_command ess_o16m[] = /* output 16 bit mono */ + { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} }; +static struct ess_command ess_o08s[] = /* output 8 bit stereo */ + { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} }; +static struct ess_command ess_o16s[] = /* output 16 bit stereo */ + { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} }; + +static struct ess_command *ess_out_cmds[] = + { ess_o08m, ess_o16m, ess_o08s, ess_o16s }; + +static void ess_exec_commands + (sb_devc *devc, struct ess_command *cmdtab[]) +{ + struct ess_command *cmd; + + cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ]; + + while (cmd->cmd != -1) { + ess_write (devc, cmd->cmd, cmd->data); + cmd++; + } +} + +static void ess_change + (sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val) +{ + int value; + + value = ess_read (devc, reg); + value = (value & ~mask) | (val & mask); + ess_write (devc, reg, value); +} + +static void ess_set_output_parms + (int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (devc->duplex) { + devc->trg_buf_16 = buf; + devc->trg_bytes_16 = nr_bytes; + devc->trg_intrflag_16 = intrflag; + devc->irq_mode_16 = IMODE_OUTPUT; + } else { + devc->trg_buf = buf; + devc->trg_bytes = nr_bytes; + devc->trg_intrflag = intrflag; + devc->irq_mode = IMODE_OUTPUT; + } +} + +static void ess_set_input_parms + (int dev, unsigned long buf, int count, int intrflag) +{ + sb_devc *devc = audio_devs[dev]->devc; + + devc->trg_buf = buf; + devc->trg_bytes = count; + devc->trg_intrflag = intrflag; + devc->irq_mode = IMODE_INPUT; +} + +static int ess_calc_div (int clock, int revert, int *speedp, int *diffp) +{ + int divider; + int speed, diff; + int retval; + + speed = *speedp; + divider = (clock + speed / 2) / speed; + retval = revert - divider; + if (retval > revert - 1) { + retval = revert - 1; + divider = revert - retval; + } + /* This line is suggested. Must be wrong I think + *speedp = (clock + divider / 2) / divider; + So I chose the next one */ + + *speedp = clock / divider; + diff = speed - *speedp; + if (diff < 0) diff =-diff; + *diffp = diff; + + return retval; +} + +static int ess_calc_best_speed + (int clock1, int rev1, int clock2, int rev2, int *divp, int *speedp) +{ + int speed1 = *speedp, speed2 = *speedp; + int div1, div2; + int diff1, diff2; + int retval; + + div1 = ess_calc_div (clock1, rev1, &speed1, &diff1); + div2 = ess_calc_div (clock2, rev2, &speed2, &diff2); + + if (diff1 < diff2) { + *divp = div1; + *speedp = speed1; + retval = 1; + } else { + /* *divp = div2; */ + *divp = 0x80 | div2; + *speedp = speed2; + retval = 2; + } + + return retval; +} + +/* + * Depending on the audiochannel ESS devices can + * have different clock settings. These are made consistent for duplex + * however. + * callers of ess_speed only do an audionum suggestion, which means + * input suggests 1, output suggests 2. This suggestion is only true + * however when doing duplex. + */ +static void ess_common_speed (sb_devc *devc, int *speedp, int *divp) +{ + int diff = 0, div; + + if (devc->duplex) { + /* + * The 0x80 is important for the first audio channel + */ + if (devc->submodel == SUBMDL_ES1888) { + div = 0x80 | ess_calc_div (795500, 256, speedp, &diff); + } else { + div = 0x80 | ess_calc_div (795500, 128, speedp, &diff); + } + } else if(devc->caps & SB_CAP_ES18XX_RATE) { + if (devc->submodel == SUBMDL_ES1888) { + ess_calc_best_speed(397700, 128, 795500, 256, + &div, speedp); + } else { + ess_calc_best_speed(ES18XX_CLOCK1, 128, ES18XX_CLOCK2, 256, + &div, speedp); + } + } else { + if (*speedp > 22000) { + div = 0x80 | ess_calc_div (ES1688_CLOCK1, 256, speedp, &diff); + } else { + div = 0x00 | ess_calc_div (ES1688_CLOCK2, 128, speedp, &diff); + } + } + *divp = div; +} + +static void ess_speed (sb_devc *devc, int audionum) +{ + int speed; + int div, div2; + + ess_common_speed (devc, &(devc->speed), &div); + +#ifdef FKS_REG_LOGGING +printk (KERN_INFO "FKS: ess_speed (%d) b speed = %d, div=%x\n", audionum, devc->speed, div); +#endif + + /* Set filter roll-off to 90% of speed/2 */ + speed = (devc->speed * 9) / 20; + + div2 = 256 - 7160000 / (speed * 82); + + if (!devc->duplex) audionum = 1; + + if (audionum == 1) { + /* Change behaviour of register A1 * + sb_chg_mixer(devc, 0x71, 0x20, 0x20) + * For ES1869 only??? */ + ess_write (devc, 0xa1, div); + ess_write (devc, 0xa2, div2); + } else { + ess_setmixer (devc, 0x70, div); + /* + * FKS: fascinating: 0x72 doesn't seem to work. + */ + ess_write (devc, 0xa2, div2); + ess_setmixer (devc, 0x72, div2); + } +} + +static int ess_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + + ess_speed(devc, 1); + + sb_dsp_command(devc, DSP_CMD_SPKOFF); + + ess_write (devc, 0xb8, 0x0e); /* Auto init DMA mode */ + ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */ + ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */ + + ess_exec_commands (devc, ess_inp_cmds); + + ess_change (devc, 0xb1, 0xf0, 0x50); + ess_change (devc, 0xb2, 0xf0, 0x50); + + devc->trigger_bits = 0; + return 0; +} + +static int ess_audio_prepare_for_output_audio1 (int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + + sb_dsp_reset(devc); + ess_speed(devc, 1); + ess_write (devc, 0xb8, 4); /* Auto init DMA mode */ + ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */ + ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/request) */ + + ess_exec_commands (devc, ess_out_cmds); + + ess_change (devc, 0xb1, 0xf0, 0x50); /* Enable DMA */ + ess_change (devc, 0xb2, 0xf0, 0x50); /* Enable IRQ */ + + sb_dsp_command(devc, DSP_CMD_SPKON); /* There be sound! */ + + devc->trigger_bits = 0; + return 0; +} + +static int ess_audio_prepare_for_output_audio2 (int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + unsigned char bits; + +/* FKS: qqq + sb_dsp_reset(devc); +*/ + + /* + * Auto-Initialize: + * DMA mode + demand mode (8 bytes/request, yes I want it all!) + * But leave 16-bit DMA bit untouched! + */ + ess_chgmixer (devc, 0x78, 0xd0, 0xd0); + + ess_speed(devc, 2); + + /* bits 4:3 on ES1887 represent recording source. Keep them! */ + bits = ess_getmixer (devc, 0x7a) & 0x18; + + /* Set stereo/mono */ + if (devc->channels != 1) bits |= 0x02; + + /* Init DACs; UNSIGNED mode for 8 bit; SIGNED mode for 16 bit */ + if (devc->bits != AFMT_U8) bits |= 0x05; /* 16 bit */ + + /* Enable DMA, IRQ will be shared (hopefully)*/ + bits |= 0x60; + + ess_setmixer (devc, 0x7a, bits); + + ess_mixer_reload (devc, SOUND_MIXER_PCM); /* There be sound! */ + + devc->trigger_bits = 0; + return 0; +} + +static int ess_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + +#ifdef FKS_REG_LOGGING +printk(KERN_INFO "ess_audio_prepare_for_output: dma_out=%d,dma_in=%d\n" +, audio_devs[dev]->dmap_out->dma, audio_devs[dev]->dmap_in->dma); +#endif + + if (devc->duplex) { + return ess_audio_prepare_for_output_audio2 (dev, bsize, bcount); + } else { + return ess_audio_prepare_for_output_audio1 (dev, bsize, bcount); + } +} + +static void ess_audio_halt_xfer(int dev) +{ + unsigned long flags; + sb_devc *devc = audio_devs[dev]->devc; + + spin_lock_irqsave(&devc->lock, flags); + sb_dsp_reset(devc); + spin_unlock_irqrestore(&devc->lock, flags); + + /* + * Audio 2 may still be operational! Creates awful sounds! + */ + if (devc->duplex) ess_chgmixer(devc, 0x78, 0x03, 0x00); +} + +static void ess_audio_start_input + (int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + short c = -nr_bytes; + + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ + + if (audio_devs[dev]->dmap_in->dma > 3) count >>= 1; + count--; + + devc->irq_mode = IMODE_INPUT; + + ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); + ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); + + ess_change (devc, 0xb8, 0x0f, 0x0f); /* Go */ + devc->intr_active = 1; +} + +static void ess_audio_output_block_audio1 + (int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + short c = -nr_bytes; + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_OUTPUT; + + ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); + ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); + + ess_change (devc, 0xb8, 0x05, 0x05); /* Go */ + devc->intr_active = 1; +} + +static void ess_audio_output_block_audio2 + (int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + short c = -nr_bytes; + + if (audio_devs[dev]->dmap_out->dma > 3) count >>= 1; + count--; + + ess_setmixer (devc, 0x74, (unsigned char) ((unsigned short) c & 0xff)); + ess_setmixer (devc, 0x76, (unsigned char) (((unsigned short) c >> 8) & 0xff)); + ess_chgmixer (devc, 0x78, 0x03, 0x03); /* Go */ + + devc->irq_mode_16 = IMODE_OUTPUT; + devc->intr_active_16 = 1; +} + +static void ess_audio_output_block + (int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (devc->duplex) { + ess_audio_output_block_audio2 (dev, buf, nr_bytes, intrflag); + } else { + ess_audio_output_block_audio1 (dev, buf, nr_bytes, intrflag); + } +} + +/* + * FKS: the if-statements for both bits and bits_16 are quite alike. + * Combine this... + */ +static void ess_audio_trigger(int dev, int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + + int bits_16 = bits & devc->irq_mode_16; + bits &= devc->irq_mode; + + if (!bits && !bits_16) { + /* FKS oh oh.... wrong?? for dma 16? */ + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + } + + if (bits) { + switch (devc->irq_mode) + { + case IMODE_INPUT: + ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + } + } + + if (bits_16) { + switch (devc->irq_mode_16) { + case IMODE_INPUT: + ess_audio_start_input(dev, devc->trg_buf_16, devc->trg_bytes_16, + devc->trg_intrflag_16); + break; + + case IMODE_OUTPUT: + ess_audio_output_block(dev, devc->trg_buf_16, devc->trg_bytes_16, + devc->trg_intrflag_16); + break; + } + } + + devc->trigger_bits = bits | bits_16; +} + +static int ess_audio_set_speed(int dev, int speed) +{ + sb_devc *devc = audio_devs[dev]->devc; + int minspeed, maxspeed, dummydiv; + + if (speed > 0) { + minspeed = (devc->duplex ? 6215 : 5000 ); + maxspeed = (devc->duplex ? 44100 : 48000); + if (speed < minspeed) speed = minspeed; + if (speed > maxspeed) speed = maxspeed; + + ess_common_speed (devc, &speed, &dummydiv); + + devc->speed = speed; + } + return devc->speed; +} + +/* + * FKS: This is a one-on-one copy of sb1_audio_set_bits + */ +static unsigned int ess_audio_set_bits(int dev, unsigned int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (bits != 0) { + if (bits == AFMT_U8 || bits == AFMT_S16_LE) { + devc->bits = bits; + } else { + devc->bits = AFMT_U8; + } + } + + return devc->bits; +} + +/* + * FKS: This is a one-on-one copy of sbpro_audio_set_channels + * (*) Modified it!! + */ +static short ess_audio_set_channels(int dev, short channels) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (channels == 1 || channels == 2) devc->channels = channels; + + return devc->channels; +} + +static struct audio_driver ess_audio_driver = /* ESS ES688/1688 */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: ess_set_output_parms, + start_input: ess_set_input_parms, + prepare_for_input: ess_audio_prepare_for_input, + prepare_for_output: ess_audio_prepare_for_output, + halt_io: ess_audio_halt_xfer, + trigger: ess_audio_trigger, + set_speed: ess_audio_set_speed, + set_bits: ess_audio_set_bits, + set_channels: ess_audio_set_channels +}; + +/* + * ess_audio_init must be called from sb_audio_init + */ +struct audio_driver *ess_audio_init + (sb_devc *devc, int *audio_flags, int *format_mask) +{ + *audio_flags = DMA_AUTOMODE; + *format_mask |= AFMT_S16_LE; + + if (devc->duplex) { + int tmp_dma; + /* + * sb_audio_init thinks dma8 is for playback and + * dma16 is for record. Not now! So swap them. + */ + tmp_dma = devc->dma16; + devc->dma16 = devc->dma8; + devc->dma8 = tmp_dma; + + *audio_flags |= DMA_DUPLEX; + } + + return &ess_audio_driver; +} + +/**************************************************************************** + * * + * ESS common * + * * + ****************************************************************************/ +static void ess_handle_channel + (char *channel, int dev, int intr_active, unsigned char flag, int irq_mode) +{ + if (!intr_active || !flag) return; +#ifdef FKS_REG_LOGGING +printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode); +#endif + switch (irq_mode) { + case IMODE_OUTPUT: + DMAbuf_outputintr (dev, 1); + break; + + case IMODE_INPUT: + DMAbuf_inputintr (dev); + break; + + case IMODE_INIT: + break; + + default:; + /* printk(KERN_WARN "ESS: Unexpected interrupt\n"); */ + } +} + +/* + * FKS: TODO!!! Finish this! + * + * I think midi stuff uses uart401, without interrupts. + * So IMODE_MIDI isn't a value for devc->irq_mode. + */ +void ess_intr (sb_devc *devc) +{ + int status; + unsigned char src; + + if (devc->submodel == SUBMDL_ES1887) { + src = ess_getmixer (devc, 0x7f) >> 4; + } else { + src = 0xff; + } + +#ifdef FKS_REG_LOGGING +printk(KERN_INFO "FKS: sbintr src=%x\n",(int)src); +#endif + ess_handle_channel + ( "Audio 1" + , devc->dev, devc->intr_active , src & 0x01, devc->irq_mode ); + ess_handle_channel + ( "Audio 2" + , devc->dev, devc->intr_active_16, src & 0x02, devc->irq_mode_16); + /* + * Acknowledge interrupts + */ + if (devc->submodel == SUBMDL_ES1887 && (src & 0x02)) { + ess_chgmixer (devc, 0x7a, 0x80, 0x00); + } + + if (src & 0x01) { + status = inb(DSP_DATA_AVAIL); + } +} + +static void ess_extended (sb_devc * devc) +{ + /* Enable extended mode */ + + sb_dsp_command(devc, 0xc6); +} + +static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data) +{ +#ifdef FKS_REG_LOGGING +printk(KERN_INFO "FKS: write reg %x: %x\n", reg, data); +#endif + /* Write a byte to an extended mode register of ES1688 */ + + if (!sb_dsp_command(devc, reg)) + return 0; + + return sb_dsp_command(devc, data); +} + +static int ess_read (sb_devc * devc, unsigned char reg) +{ + /* Read a byte from an extended mode register of ES1688 */ + + /* Read register command */ + if (!sb_dsp_command(devc, 0xc0)) return -1; + + if (!sb_dsp_command(devc, reg )) return -1; + + return sb_dsp_get_byte(devc); +} + +int ess_dsp_reset(sb_devc * devc) +{ + int loopc; + +#ifdef FKS_REG_LOGGING +printk(KERN_INFO "FKS: ess_dsp_reset 1\n"); +ess_show_mixerregs (devc); +#endif + + DEB(printk("Entered ess_dsp_reset()\n")); + + outb(3, DSP_RESET); /* Reset FIFO too */ + + udelay(10); + outb(0, DSP_RESET); + udelay(30); + + for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++); + + if (inb(DSP_READ) != 0xAA) { + DDB(printk("sb: No response to RESET\n")); + return 0; /* Sorry */ + } + ess_extended (devc); + + DEB(printk("sb_dsp_reset() OK\n")); + +#ifdef FKS_LOGGING +printk(KERN_INFO "FKS: dsp_reset 2\n"); +ess_show_mixerregs (devc); +#endif + + return 1; +} + +static int ess_irq_bits (int irq) +{ + switch (irq) { + case 2: + case 9: + return 0; + + case 5: + return 1; + + case 7: + return 2; + + case 10: + return 3; + + default: + printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", irq); + return -1; + } +} + +/* + * Set IRQ configuration register for all ESS models + */ +static int ess_common_set_irq_hw (sb_devc * devc) +{ + int irq_bits; + + if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return 0; + + if (!ess_write (devc, 0xb1, 0x50 | (irq_bits << 2))) { + printk(KERN_ERR "ES1688: Failed to write to IRQ config register\n"); + return 0; + } + return 1; +} + +/* + * I wanna use modern ES1887 mixer irq handling. Funny is the + * fact that my BIOS wants the same. But suppose someone's BIOS + * doesn't do this! + * This is independent of duplex. If there's a 1887 this will + * prevent it from going into 1888 mode. + */ +static void ess_es1887_set_irq_hw (sb_devc * devc) +{ + int irq_bits; + + if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return; + + ess_chgmixer (devc, 0x7f, 0x0f, 0x01 | ((irq_bits + 1) << 1)); +} + +static int ess_set_irq_hw (sb_devc * devc) +{ + if (devc->submodel == SUBMDL_ES1887) ess_es1887_set_irq_hw (devc); + + return ess_common_set_irq_hw (devc); +} + +#ifdef FKS_TEST + +/* + * FKS_test: + * for ES1887: 00, 18, non wr bits: 0001 1000 + * for ES1868: 00, b8, non wr bits: 1011 1000 + * for ES1888: 00, f8, non wr bits: 1111 1000 + * for ES1688: 00, f8, non wr bits: 1111 1000 + * + ES968 + */ + +static void FKS_test (sb_devc * devc) +{ + int val1, val2; + val1 = ess_getmixer (devc, 0x64); + ess_setmixer (devc, 0x64, ~val1); + val2 = ess_getmixer (devc, 0x64) ^ ~val1; + ess_setmixer (devc, 0x64, val1); + val1 ^= ess_getmixer (devc, 0x64); +printk (KERN_INFO "FKS: FKS_test %02x, %02x\n", (val1 & 0x0ff), (val2 & 0x0ff)); +}; +#endif + +static unsigned int ess_identify (sb_devc * devc) +{ + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&devc->lock, flags); + outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR); + + udelay(20); + val = inb(MIXER_DATA) << 8; + udelay(20); + val |= inb(MIXER_DATA); + udelay(20); + spin_unlock_irqrestore(&devc->lock, flags); + + return val; +} + +/* + * ESS technology describes a detection scheme in their docs. It involves + * fiddling with the bits in certain mixer registers. ess_probe is supposed + * to help. + * + * FKS: tracing shows ess_probe writes wrong value to 0x64. Bit 3 reads 1, but + * should be written 0 only. Check this. + */ +static int ess_probe (sb_devc * devc, int reg, int xorval) +{ + int val1, val2, val3; + + val1 = ess_getmixer (devc, reg); + val2 = val1 ^ xorval; + ess_setmixer (devc, reg, val2); + val3 = ess_getmixer (devc, reg); + ess_setmixer (devc, reg, val1); + + return (val2 == val3); +} + +int ess_init(sb_devc * devc, struct address_info *hw_config) +{ + unsigned char cfg; + int ess_major = 0, ess_minor = 0; + int i; + static char name[100], modelname[10]; + + /* + * Try to detect ESS chips. + */ + + sb_dsp_command(devc, 0xe7); /* Return identification */ + + for (i = 1000; i; i--) { + if (inb(DSP_DATA_AVAIL) & 0x80) { + if (ess_major == 0) { + ess_major = inb(DSP_READ); + } else { + ess_minor = inb(DSP_READ); + break; + } + } + } + + if (ess_major == 0) return 0; + + if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) { + sprintf(name, "ESS ES488 AudioDrive (rev %d)", + ess_minor & 0x0f); + hw_config->name = name; + devc->model = MDL_SBPRO; + return 1; + } + + /* + * This the detection heuristic of ESS technology, though somewhat + * changed to actually make it work. + * This results in the following detection steps: + * - distinct between ES688 and ES1688+ (as always done in this driver) + * if ES688 we're ready + * - try to detect ES1868, ES1869 or ES1878 (ess_identify) + * if successful we're ready + * - try to detect ES1888, ES1887 or ES1788 (aim: detect ES1887) + * if successful we're ready + * - Dunno. Must be 1688. Will do in general + * + * This is the most BETA part of the software: Will the detection + * always work? + */ + devc->model = MDL_ESS; + devc->submodel = ess_minor & 0x0f; + + if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) { + char *chip = NULL; + int submodel = -1; + + switch (devc->sbmo.esstype) { + case ESSTYPE_DETECT: + case ESSTYPE_LIKE20: + break; + case 688: + submodel = 0x00; + break; + case 1688: + submodel = 0x08; + break; + case 1868: + submodel = SUBMDL_ES1868; + break; + case 1869: + submodel = SUBMDL_ES1869; + break; + case 1788: + submodel = SUBMDL_ES1788; + break; + case 1878: + submodel = SUBMDL_ES1878; + break; + case 1879: + submodel = SUBMDL_ES1879; + break; + case 1887: + submodel = SUBMDL_ES1887; + break; + case 1888: + submodel = SUBMDL_ES1888; + break; + default: + printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype); + return 0; + }; + if (submodel != -1) { + devc->submodel = submodel; + sprintf (modelname, "ES%d", devc->sbmo.esstype); + chip = modelname; + }; + if (chip == NULL && (ess_minor & 0x0f) < 8) { + chip = "ES688"; + }; +#ifdef FKS_TEST +FKS_test (devc); +#endif + /* + * If Nothing detected yet, and we want 2.0 behaviour... + * Then let's assume it's ES1688. + */ + if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) { + chip = "ES1688"; + }; + + if (chip == NULL) { + int type; + + type = ess_identify (devc); + + switch (type) { + case 0x1868: + chip = "ES1868"; + devc->submodel = SUBMDL_ES1868; + break; + case 0x1869: + chip = "ES1869"; + devc->submodel = SUBMDL_ES1869; + break; + case 0x1878: + chip = "ES1878"; + devc->submodel = SUBMDL_ES1878; + break; + case 0x1879: + chip = "ES1879"; + devc->submodel = SUBMDL_ES1879; + break; + default: + if ((type & 0x00ff) != ((type >> 8) & 0x00ff)) { + printk ("ess_init: Unrecognized %04x\n", type); + } + }; + }; +#if 0 + /* + * this one failed: + * the probing of bit 4 is another thought: from ES1788 and up, all + * chips seem to have hardware volume control. Bit 4 is readonly to + * check if a hardware volume interrupt has fired. + * Cause ES688/ES1688 don't have this feature, bit 4 might be writeable + * for these chips. + */ + if (chip == NULL && !ess_probe(devc, 0x64, (1 << 4))) { +#endif + /* + * the probing of bit 2 is my idea. The ES1887 docs want me to probe + * bit 3. This results in ES1688 being detected as ES1788. + * Bit 2 is for "Enable HWV IRQE", but as ES(1)688 chips don't have + * HardWare Volume, I think they don't have this IRQE. + */ + if (chip == NULL && ess_probe(devc, 0x64, (1 << 2))) { + if (ess_probe (devc, 0x70, 0x7f)) { + if (ess_probe (devc, 0x64, (1 << 5))) { + chip = "ES1887"; + devc->submodel = SUBMDL_ES1887; + } else { + chip = "ES1888"; + devc->submodel = SUBMDL_ES1888; + } + } else { + chip = "ES1788"; + devc->submodel = SUBMDL_ES1788; + } + }; + if (chip == NULL) { + chip = "ES1688"; + }; + + printk ( KERN_INFO "ESS chip %s %s%s\n" + , chip + , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20 + ? "detected" + : "specified" + ) + , ( devc->sbmo.esstype == ESSTYPE_LIKE20 + ? " (kernel 2.0 compatible)" + : "" + ) + ); + + sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f); + } else { + strcpy(name, "Jazz16"); + } + + /* AAS: info stolen from ALSA: these boards have different clocks */ + switch(devc->submodel) { +/* APPARENTLY NOT 1869 AND 1887 + case SUBMDL_ES1869: + case SUBMDL_ES1887: +*/ + case SUBMDL_ES1888: + devc->caps |= SB_CAP_ES18XX_RATE; + break; + } + + hw_config->name = name; + /* FKS: sb_dsp_reset to enable extended mode???? */ + sb_dsp_reset(devc); /* Turn on extended mode */ + + /* + * Enable joystick and OPL3 + */ + cfg = ess_getmixer (devc, 0x40); + ess_setmixer (devc, 0x40, cfg | 0x03); + if (devc->submodel >= 8) { /* ES1688 */ + devc->caps |= SB_NO_MIDI; /* ES1688 uses MPU401 MIDI mode */ + } + sb_dsp_reset (devc); + + /* + * This is important! If it's not done, the IRQ probe in sb_dsp_init + * may fail. + */ + return ess_set_irq_hw (devc); +} + +static int ess_set_dma_hw(sb_devc * devc) +{ + unsigned char cfg, dma_bits = 0, dma16_bits; + int dma; + +#ifdef FKS_LOGGING +printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n" +, devc->dma8, devc->dma16, devc->duplex); +#endif + + /* + * FKS: It seems as if this duplex flag isn't set yet. Check it. + */ + dma = devc->dma8; + + if (dma > 3 || dma < 0 || dma == 2) { + dma_bits = 0; + printk(KERN_ERR "ESS1688: Invalid DMA8 %d\n", dma); + return 0; + } else { + /* Extended mode DMA enable */ + cfg = 0x50; + + if (dma == 3) { + dma_bits = 3; + } else { + dma_bits = dma + 1; + } + } + + if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) { + printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n"); + return 0; + } + + if (devc->duplex) { + dma = devc->dma16; + dma16_bits = 0; + + if (dma >= 0) { + switch (dma) { + case 0: + dma_bits = 0x04; + break; + case 1: + dma_bits = 0x05; + break; + case 3: + dma_bits = 0x06; + break; + case 5: + dma_bits = 0x07; + dma16_bits = 0x20; + break; + default: + printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma); + return 0; + }; + ess_chgmixer (devc, 0x78, 0x20, dma16_bits); + ess_chgmixer (devc, 0x7d, 0x07, dma_bits); + } + } + return 1; +} + +/* + * This one is called from sb_dsp_init. + * + * Return values: + * 0: Failed + * 1: Succeeded or doesn't apply (not SUBMDL_ES1887) + */ +int ess_dsp_init (sb_devc *devc, struct address_info *hw_config) +{ + /* + * Caller also checks this, but anyway + */ + if (devc->model != MDL_ESS) { + printk (KERN_INFO "ess_dsp_init for non ESS chip\n"); + return 1; + } + /* + * This for ES1887 to run Full Duplex. Actually ES1888 + * is allowed to do so too. I have no idea yet if this + * will work for ES1888 however. + * + * For SB16 having both dma8 and dma16 means enable + * Full Duplex. Let's try this for ES1887 too + * + */ + if (devc->submodel == SUBMDL_ES1887) { + if (hw_config->dma2 != -1) { + devc->dma16 = hw_config->dma2; + } + /* + * devc->duplex initialization is put here, cause + * ess_set_dma_hw needs it. + */ + if (devc->dma8 != devc->dma16 && devc->dma16 != -1) { + devc->duplex = 1; + } + } + if (!ess_set_dma_hw (devc)) { + free_irq(devc->irq, devc); + return 0; + } + return 1; +} + +/**************************************************************************** + * * + * ESS mixer * + * * + ****************************************************************************/ + +#define ES688_RECORDING_DEVICES \ + ( SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD ) +#define ES688_MIXER_DEVICES \ + ( SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE \ + | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME \ + | SOUND_MASK_LINE2 | SOUND_MASK_SPEAKER ) + +#define ES1688_RECORDING_DEVICES \ + ( ES688_RECORDING_DEVICES ) +#define ES1688_MIXER_DEVICES \ + ( ES688_MIXER_DEVICES | SOUND_MASK_RECLEV ) + +#define ES1887_RECORDING_DEVICES \ + ( ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 | SOUND_MASK_SYNTH) +#define ES1887_MIXER_DEVICES \ + ( ES1688_MIXER_DEVICES ) + +/* + * Mixer registers of ES1887 + * + * These registers specifically take care of recording levels. To make the + * mapping from playback devices to recording devices every recording + * devices = playback device + ES_REC_MIXER_RECDIFF + */ +#define ES_REC_MIXER_RECBASE (SOUND_MIXER_LINE3 + 1) +#define ES_REC_MIXER_RECDIFF (ES_REC_MIXER_RECBASE - SOUND_MIXER_SYNTH) + +#define ES_REC_MIXER_RECSYNTH (SOUND_MIXER_SYNTH + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECPCM (SOUND_MIXER_PCM + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECSPEAKER (SOUND_MIXER_SPEAKER + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECLINE (SOUND_MIXER_LINE + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECMIC (SOUND_MIXER_MIC + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECCD (SOUND_MIXER_CD + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECIMIX (SOUND_MIXER_IMIX + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECALTPCM (SOUND_MIXER_ALTPCM + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECRECLEV (SOUND_MIXER_RECLEV + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECIGAIN (SOUND_MIXER_IGAIN + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECOGAIN (SOUND_MIXER_OGAIN + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECLINE1 (SOUND_MIXER_LINE1 + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECLINE2 (SOUND_MIXER_LINE2 + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECLINE3 (SOUND_MIXER_LINE3 + ES_REC_MIXER_RECDIFF) + +static mixer_tab es688_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), +MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), +MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) +}; + +/* + * The ES1688 specifics... hopefully correct... + * - 6 bit master volume + * I was wrong, ES1888 docs say ES1688 didn't have it. + * - RECLEV control + * These may apply to ES688 too. I have no idea. + */ +static mixer_tab es1688_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), +MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), +MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) +}; + +static mixer_tab es1688later_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), +MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), +MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) +}; + +/* + * This one is for all ESS chips with a record mixer. + * It's not used (yet) however + */ +static mixer_tab es_rec_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), +MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), +MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4), +MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4), +MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4), +MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4), +MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4), +MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0) +}; + +/* + * This one is for ES1887. It's little different from es_rec_mix: it + * has 0x7c for PCM playback level. This is because ES1887 uses + * Audio 2 for playback. + */ +static mixer_tab es1887_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x7c, 7, 4, 0x7c, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), +MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), +MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4), +MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4), +MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4), +MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4), +MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4), +MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0) +}; + +static int ess_has_rec_mixer (int submodel) +{ + switch (submodel) { + case SUBMDL_ES1887: + return 1; + default: + return 0; + }; +}; + +#ifdef FKS_LOGGING +static int ess_mixer_mon_regs[] + = { 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7d, 0x7f + , 0xa1, 0xa2, 0xa4, 0xa5, 0xa8, 0xa9 + , 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9 + , 0x00}; + +static void ess_show_mixerregs (sb_devc *devc) +{ + int *mp = ess_mixer_mon_regs; + +return; + + while (*mp != 0) { + printk (KERN_INFO "res (%x)=%x\n", *mp, (int)(ess_getmixer (devc, *mp))); + mp++; + } +} +#endif + +void ess_setmixer (sb_devc * devc, unsigned int port, unsigned int value) +{ + unsigned long flags; + +#ifdef FKS_LOGGING +printk(KERN_INFO "FKS: write mixer %x: %x\n", port, value); +#endif + + spin_lock_irqsave(&devc->lock, flags); + if (port >= 0xa0) { + ess_write (devc, port, value); + } else { + outb(((unsigned char) (port & 0xff)), MIXER_ADDR); + + udelay(20); + outb(((unsigned char) (value & 0xff)), MIXER_DATA); + udelay(20); + }; + spin_unlock_irqrestore(&devc->lock, flags); +} + +unsigned int ess_getmixer (sb_devc * devc, unsigned int port) +{ + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&devc->lock, flags); + + if (port >= 0xa0) { + val = ess_read (devc, port); + } else { + outb(((unsigned char) (port & 0xff)), MIXER_ADDR); + + udelay(20); + val = inb(MIXER_DATA); + udelay(20); + } + spin_unlock_irqrestore(&devc->lock, flags); + + return val; +} + +static void ess_chgmixer + (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val) +{ + int value; + + value = ess_getmixer (devc, reg); + value = (value & ~mask) | (val & mask); + ess_setmixer (devc, reg, value); +} + +/* + * ess_mixer_init must be called from sb_mixer_init + */ +void ess_mixer_init (sb_devc * devc) +{ + devc->mixer_caps = SOUND_CAP_EXCL_INPUT; + + /* + * Take care of ES1887 specifics... + */ + switch (devc->submodel) { + case SUBMDL_ES1887: + devc->supported_devices = ES1887_MIXER_DEVICES; + devc->supported_rec_devices = ES1887_RECORDING_DEVICES; +#ifdef FKS_LOGGING +printk (KERN_INFO "FKS: ess_mixer_init dup = %d\n", devc->duplex); +#endif + if (devc->duplex) { + devc->iomap = &es1887_mix; + } else { + devc->iomap = &es_rec_mix; + } + break; + default: + if (devc->submodel < 8) { + devc->supported_devices = ES688_MIXER_DEVICES; + devc->supported_rec_devices = ES688_RECORDING_DEVICES; + devc->iomap = &es688_mix; + } else { + /* + * es1688 has 4 bits master vol. + * later chips have 6 bits (?) + */ + devc->supported_devices = ES1688_MIXER_DEVICES; + devc->supported_rec_devices = ES1688_RECORDING_DEVICES; + if (devc->submodel < 0x10) { + devc->iomap = &es1688_mix; + } else { + devc->iomap = &es1688later_mix; + } + } + } +} + +/* + * Changing playback levels at an ESS chip with record mixer means having to + * take care of recording levels of recorded inputs (devc->recmask) too! + */ +int ess_mixer_set(sb_devc *devc, int dev, int left, int right) +{ + if (ess_has_rec_mixer (devc->submodel) && (devc->recmask & (1 << dev))) { + sb_common_mixer_set (devc, dev + ES_REC_MIXER_RECDIFF, left, right); + } + return sb_common_mixer_set (devc, dev, left, right); +} + +/* + * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After + * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload + * helps. + */ +void ess_mixer_reload (sb_devc *devc, int dev) +{ + int left, right, value; + + value = devc->levels[dev]; + left = value & 0x000000ff; + right = (value & 0x0000ff00) >> 8; + + sb_common_mixer_set(devc, dev, left, right); +} + +int es_rec_set_recmask(sb_devc * devc, int mask) +{ + int i, i_mask, cur_mask, diff_mask; + int value, left, right; + +#ifdef FKS_LOGGING +printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask); +#endif + /* + * Changing the recmask on an ESS chip with recording mixer means: + * (1) Find the differences + * (2) For "turned-on" inputs: make the recording level the playback level + * (3) For "turned-off" inputs: make the recording level zero + */ + cur_mask = devc->recmask; + diff_mask = (cur_mask ^ mask); + + for (i = 0; i < 32; i++) { + i_mask = (1 << i); + if (diff_mask & i_mask) { /* Difference? (1) */ + if (mask & i_mask) { /* Turn it on (2) */ + value = devc->levels[i]; + left = value & 0x000000ff; + right = (value & 0x0000ff00) >> 8; + } else { /* Turn it off (3) */ + left = 0; + left = 0; + right = 0; + } + sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right); + } + } + return mask; +} + +int ess_set_recmask(sb_devc * devc, int *mask) +{ + /* This applies to ESS chips with record mixers only! */ + + if (ess_has_rec_mixer (devc->submodel)) { + *mask = es_rec_set_recmask (devc, *mask); + return 1; /* Applied */ + } else { + return 0; /* Not applied */ + } +} + +/* + * ess_mixer_reset must be called from sb_mixer_reset + */ +int ess_mixer_reset (sb_devc * devc) +{ + /* + * Separate actions for ESS chips with a record mixer: + */ + if (ess_has_rec_mixer (devc->submodel)) { + switch (devc->submodel) { + case SUBMDL_ES1887: + /* + * Separate actions for ES1887: + * Change registers 7a and 1c to make the record mixer the + * actual recording source. + */ + ess_chgmixer(devc, 0x7a, 0x18, 0x08); + ess_chgmixer(devc, 0x1c, 0x07, 0x07); + break; + }; + /* + * Call set_recmask for proper initialization + */ + devc->recmask = devc->supported_rec_devices; + es_rec_set_recmask(devc, 0); + devc->recmask = 0; + + return 1; /* We took care of recmask. */ + } else { + return 0; /* We didn't take care; caller do it */ + } +} + +/**************************************************************************** + * * + * ESS midi * + * * + ****************************************************************************/ + +/* + * FKS: IRQ may be shared. Hm. And if so? Then What? + */ +int ess_midi_init(sb_devc * devc, struct address_info *hw_config) +{ + unsigned char cfg, tmp; + + cfg = ess_getmixer (devc, 0x40) & 0x03; + + if (devc->submodel < 8) { + ess_setmixer (devc, 0x40, cfg | 0x03); /* Enable OPL3 & joystick */ + return 0; /* ES688 doesn't support MPU401 mode */ + } + tmp = (hw_config->io_base & 0x0f0) >> 4; + + if (tmp > 3) { + ess_setmixer (devc, 0x40, cfg); + return 0; + } + cfg |= tmp << 3; + + tmp = 1; /* MPU enabled without interrupts */ + + /* May be shared: if so the value is -ve */ + + switch (abs(hw_config->irq)) { + case 9: + tmp = 0x4; + break; + case 5: + tmp = 0x5; + break; + case 7: + tmp = 0x6; + break; + case 10: + tmp = 0x7; + break; + default: + return 0; + } + + cfg |= tmp << 5; + ess_setmixer (devc, 0x40, cfg | 0x03); + + return 1; +} + diff -Nru a/sound/oss/sb_ess.h b/sound/oss/sb_ess.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sb_ess.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,34 @@ +/* + * Created: 9-Jan-1999 Rolf Fokkens + */ + +extern void ess_intr + (sb_devc *devc); +extern int ess_dsp_init + (sb_devc *devc, struct address_info *hw_config); + +extern struct audio_driver *ess_audio_init + (sb_devc *devc, int *audio_flags, int *format_mask); +extern int ess_midi_init + (sb_devc *devc, struct address_info *hw_config); +extern void ess_mixer_init + (sb_devc *devc); + +extern int ess_init + (sb_devc *devc, struct address_info *hw_config); +extern int ess_dsp_reset + (sb_devc *devc); + +extern void ess_setmixer + (sb_devc *devc, unsigned int port, unsigned int value); +extern unsigned int ess_getmixer + (sb_devc *devc, unsigned int port); +extern int ess_mixer_set + (sb_devc *devc, int dev, int left, int right); +extern int ess_mixer_reset + (sb_devc *devc); +extern void ess_mixer_reload + (sb_devc * devc, int dev); +extern int ess_set_recmask + (sb_devc *devc, int *mask); + diff -Nru a/sound/oss/sb_midi.c b/sound/oss/sb_midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sb_midi.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,205 @@ +/* + * sound/sb_dsp.c + * + * The low level driver for the Sound Blaster DS chips. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +#include + +#include "sound_config.h" + +#include "sb.h" +#undef SB_TEST_IRQ + +/* + * The DSP channel can be used either for input or output. Variable + * 'sb_irq_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + + +static int sb_midi_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + sb_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return -ENXIO; + + spin_lock_irqsave(&devc->lock, flags); + if (devc->opened) + { + spin_unlock_irqrestore(&devc->lock, flags); + return -EBUSY; + } + devc->opened = 1; + spin_unlock_irqrestore(&devc->lock, flags); + + devc->irq_mode = IMODE_MIDI; + devc->midi_broken = 0; + + sb_dsp_reset(devc); + + if (!sb_dsp_command(devc, 0x35)) /* Start MIDI UART mode */ + { + devc->opened = 0; + return -EIO; + } + devc->intr_active = 1; + + if (mode & OPEN_READ) + { + devc->input_opened = 1; + devc->midi_input_intr = input; + } + return 0; +} + +static void sb_midi_close(int dev) +{ + sb_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return; + + spin_lock_irqsave(&devc->lock, flags); + sb_dsp_reset(devc); + devc->intr_active = 0; + devc->input_opened = 0; + devc->opened = 0; + spin_unlock_irqrestore(&devc->lock, flags); +} + +static int sb_midi_out(int dev, unsigned char midi_byte) +{ + sb_devc *devc = midi_devs[dev]->devc; + + if (devc == NULL) + return 1; + + if (devc->midi_broken) + return 1; + + if (!sb_dsp_command(devc, midi_byte)) + { + devc->midi_broken = 1; + return 1; + } + return 1; +} + +static int sb_midi_start_read(int dev) +{ + return 0; +} + +static int sb_midi_end_read(int dev) +{ + sb_devc *devc = midi_devs[dev]->devc; + + if (devc == NULL) + return -ENXIO; + + sb_dsp_reset(devc); + devc->intr_active = 0; + return 0; +} + +static int sb_midi_ioctl(int dev, unsigned cmd, caddr_t arg) +{ + return -EINVAL; +} + +void sb_midi_interrupt(sb_devc * devc) +{ + unsigned long flags; + unsigned char data; + + if (devc == NULL) + return; + + spin_lock_irqsave(&devc->lock, flags); + + data = inb(DSP_READ); + if (devc->input_opened) + devc->midi_input_intr(devc->my_mididev, data); + + spin_unlock_irqrestore(&devc->lock, flags); +} + +#define MIDI_SYNTH_NAME "Sound Blaster Midi" +#define MIDI_SYNTH_CAPS 0 +#include "midi_synth.h" + +static struct midi_operations sb_midi_operations = +{ + owner: THIS_MODULE, + info: {"Sound Blaster", 0, 0, SNDCARD_SB}, + converter: &std_midi_synth, + in_info: {0}, + open: sb_midi_open, + close: sb_midi_close, + ioctl: sb_midi_ioctl, + outputc: sb_midi_out, + start_read: sb_midi_start_read, + end_read: sb_midi_end_read, +}; + +void sb_dsp_midi_init(sb_devc * devc, struct module *owner) +{ + int dev; + + if (devc->model < 2) /* No MIDI support for SB 1.x */ + return; + + dev = sound_alloc_mididev(); + + if (dev == -1) + { + printk(KERN_ERR "sb_midi: too many MIDI devices detected\n"); + return; + } + std_midi_synth.midi_dev = devc->my_mididev = dev; + midi_devs[dev] = (struct midi_operations *)kmalloc(sizeof(struct midi_operations), GFP_KERNEL); + if (midi_devs[dev] == NULL) + { + printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); + sound_unload_mididev(dev); + return; + } + memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations, + sizeof(struct midi_operations)); + + if (owner) + midi_devs[dev]->owner = owner; + + midi_devs[dev]->devc = devc; + + + midi_devs[dev]->converter = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); + if (midi_devs[dev]->converter == NULL) + { + printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); + kfree(midi_devs[dev]); + sound_unload_mididev(dev); + return; + } + memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth, + sizeof(struct synth_operations)); + + midi_devs[dev]->converter->id = "SBMIDI"; + sequencer_init(); +} diff -Nru a/sound/oss/sb_mixer.c b/sound/oss/sb_mixer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sb_mixer.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,757 @@ +/* + * sound/sb_mixer.c + * + * The low level mixer driver for the Sound Blaster compatible cards. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Rolf Fokkens (Dec 20 1998) : Moved ESS stuff into sb_ess.[ch] + * Stanislav Voronyi : Support for AWE 3DSE device (Jun 7 1999) + */ + +#include "sound_config.h" + +#define __SB_MIXER_C__ + +#include "sb.h" +#include "sb_mixer.h" + +#include "sb_ess.h" + +#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) + +/* Same as SB Pro, unless I find otherwise */ +#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES + +#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +/* SG NX Pro has treble and bass settings on the mixer. The 'speaker' + * channel is the COVOX/DisneySoundSource emulation volume control + * on the mixer. It does NOT control speaker volume. Should have own + * mask eventually? + */ +#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \ + SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER ) + +#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD) + +#define SB16_OUTFILTER_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD) + +#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | \ + SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \ + SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \ + SOUND_MASK_IMIX) + +/* These are the only devices that are working at the moment. Others could + * be added once they are identified and a method is found to control them. + */ +#define ALS007_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \ + SOUND_MASK_PCM | SOUND_MASK_MIC | \ + SOUND_MASK_CD | \ + SOUND_MASK_VOLUME) + +static mixer_tab sbpro_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0) +}; + +static mixer_tab sb16_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5), +MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4), +MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4), +MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5), +MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5), +MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5), +MIX_ENT(SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */ +MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2), +MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2) +}; + +static mixer_tab als007_mix = +{ +MIX_ENT(SOUND_MIXER_VOLUME, 0x62, 7, 4, 0x62, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x66, 7, 4, 0x66, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x64, 7, 4, 0x64, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x6e, 7, 4, 0x6e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x6a, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x68, 7, 4, 0x68, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */ +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0) +}; + + +/* SM_GAMES Master volume is lower and PCM & FM volumes + higher than with SB Pro. This improves the + sound quality */ + +static int smg_default_levels[32] = +{ + 0x2020, /* Master Volume */ + 0x4b4b, /* Bass */ + 0x4b4b, /* Treble */ + 0x6464, /* FM */ + 0x6464, /* PCM */ + 0x4b4b, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x0000, /* Mic */ + 0x4b4b, /* CD */ + 0x4b4b, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x4b4b, /* Recording level */ + 0x4b4b, /* Input gain */ + 0x4b4b, /* Output gain */ + 0x4040, /* Line1 */ + 0x4040, /* Line2 */ + 0x1515 /* Line3 */ +}; + +static int sb_default_levels[32] = +{ + 0x5a5a, /* Master Volume */ + 0x4b4b, /* Bass */ + 0x4b4b, /* Treble */ + 0x4b4b, /* FM */ + 0x4b4b, /* PCM */ + 0x4b4b, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x1010, /* Mic */ + 0x4b4b, /* CD */ + 0x0000, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x4b4b, /* Recording level */ + 0x4b4b, /* Input gain */ + 0x4b4b, /* Output gain */ + 0x4040, /* Line1 */ + 0x4040, /* Line2 */ + 0x1515 /* Line3 */ +}; + +static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = +{ + 0x00, /* SOUND_MIXER_VOLUME */ + 0x00, /* SOUND_MIXER_BASS */ + 0x00, /* SOUND_MIXER_TREBLE */ + 0x40, /* SOUND_MIXER_SYNTH */ + 0x00, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x10, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x04, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00, /* SOUND_MIXER_RECLEV */ + 0x00, /* SOUND_MIXER_IGAIN */ + 0x00 /* SOUND_MIXER_OGAIN */ +}; + +static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] = +{ + 0x00, /* SOUND_MIXER_VOLUME */ + 0x00, /* SOUND_MIXER_BASS */ + 0x00, /* SOUND_MIXER_TREBLE */ + 0x20, /* SOUND_MIXER_SYNTH */ + 0x00, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x08, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x02, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00, /* SOUND_MIXER_RECLEV */ + 0x00, /* SOUND_MIXER_IGAIN */ + 0x00 /* SOUND_MIXER_OGAIN */ +}; + +static char smw_mix_regs[] = /* Left mixer registers */ +{ + 0x0b, /* SOUND_MIXER_VOLUME */ + 0x0d, /* SOUND_MIXER_BASS */ + 0x0d, /* SOUND_MIXER_TREBLE */ + 0x05, /* SOUND_MIXER_SYNTH */ + 0x09, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x03, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x07, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00, /* SOUND_MIXER_RECLEV */ + 0x00, /* SOUND_MIXER_IGAIN */ + 0x00, /* SOUND_MIXER_OGAIN */ + 0x00, /* SOUND_MIXER_LINE1 */ + 0x00, /* SOUND_MIXER_LINE2 */ + 0x00 /* SOUND_MIXER_LINE3 */ +}; + +static int sbmixnum = 1; + +static void sb_mixer_reset(sb_devc * devc); + +void sb_mixer_set_stereo(sb_devc * devc, int mode) +{ + sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC)); +} + +static int detect_mixer(sb_devc * devc) +{ + /* Just trust the mixer is there */ + return 1; +} + +static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval) +{ + unsigned char mask; + int shift; + + mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1; + newval = (int) ((newval * mask) + 50) / 100; /* Scale */ + + shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1; + + *regval &= ~(mask << shift); /* Mask out previous value */ + *regval |= (newval & mask) << shift; /* Set the new value */ +} + +static int sb_mixer_get(sb_devc * devc, int dev) +{ + if (!((1 << dev) & devc->supported_devices)) + return -EINVAL; + return devc->levels[dev]; +} + +void smw_mixer_init(sb_devc * devc) +{ + int i; + + sb_setmixer(devc, 0x00, 0x18); /* Mute unused (Telephone) line */ + sb_setmixer(devc, 0x10, 0x38); /* Config register 2 */ + + devc->supported_devices = 0; + for (i = 0; i < sizeof(smw_mix_regs); i++) + if (smw_mix_regs[i] != 0) + devc->supported_devices |= (1 << i); + + devc->supported_rec_devices = devc->supported_devices & + ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME); + sb_mixer_reset(devc); +} + +int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right) +{ + int regoffs; + unsigned char val; + + regoffs = (*devc->iomap)[dev][LEFT_CHN].regno; + + if (regoffs == 0) + return -EINVAL; + + val = sb_getmixer(devc, regoffs); + change_bits(devc, &val, dev, LEFT_CHN, left); + + if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /* + * Change register + */ + { + sb_setmixer(devc, regoffs, val); /* + * Save the old one + */ + regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno; + + if (regoffs == 0) + return left | (left << 8); /* + * Just left channel present + */ + + val = sb_getmixer(devc, regoffs); /* + * Read the new one + */ + } + change_bits(devc, &val, dev, RIGHT_CHN, right); + + sb_setmixer(devc, regoffs, val); + + return left | (right << 8); +} + +static int smw_mixer_set(sb_devc * devc, int dev, int left, int right) +{ + int reg, val; + + switch (dev) + { + case SOUND_MIXER_VOLUME: + sb_setmixer(devc, 0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */ + sb_setmixer(devc, 0x0c, 96 - (96 * right / 100)); + break; + + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + devc->levels[dev] = left | (right << 8); + /* Set left bass and treble values */ + val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4; + val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f; + sb_setmixer(devc, 0x0d, val); + + /* Set right bass and treble values */ + val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4; + val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f; + sb_setmixer(devc, 0x0e, val); + + break; + + default: + reg = smw_mix_regs[dev]; + if (reg == 0) + return -EINVAL; + sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */ + sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40); + } + + devc->levels[dev] = left | (right << 8); + return left | (right << 8); +} + +static int sb_mixer_set(sb_devc * devc, int dev, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int retval; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + if (dev > 31) + return -EINVAL; + + if (!(devc->supported_devices & (1 << dev))) /* + * Not supported + */ + return -EINVAL; + + /* Differentiate depending on the chipsets */ + switch (devc->model) { + case MDL_SMW: + retval = smw_mixer_set(devc, dev, left, right); + break; + case MDL_ESS: + retval = ess_mixer_set(devc, dev, left, right); + break; + default: + retval = sb_common_mixer_set(devc, dev, left, right); + } + if (retval >= 0) devc->levels[dev] = retval; + + return retval; +} + +/* + * set_recsrc doesn't apply to ES188x + */ +static void set_recsrc(sb_devc * devc, int src) +{ + sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7)); +} + +static int set_recmask(sb_devc * devc, int mask) +{ + int devmask, i; + unsigned char regimageL, regimageR; + + devmask = mask & devc->supported_rec_devices; + + switch (devc->model) + { + case MDL_SBPRO: + case MDL_ESS: + case MDL_JAZZ: + case MDL_SMW: + if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) { + break; + }; + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) + { + /* + * More than one device selected. Drop the + * previous selection + */ + devmask &= ~devc->recmask; + } + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) + { + /* + * More than one device selected. Default to + * mic + */ + devmask = SOUND_MASK_MIC; + } + if (devmask ^ devc->recmask) /* + * Input source changed + */ + { + switch (devmask) + { + case SOUND_MASK_MIC: + set_recsrc(devc, SRC__MIC); + break; + + case SOUND_MASK_LINE: + set_recsrc(devc, SRC__LINE); + break; + + case SOUND_MASK_CD: + set_recsrc(devc, SRC__CD); + break; + + default: + set_recsrc(devc, SRC__MIC); + } + } + break; + + case MDL_SB16: + if (!devmask) + devmask = SOUND_MASK_MIC; + + if (devc->submodel == SUBMDL_ALS007) + { + switch (devmask) + { + case SOUND_MASK_LINE: + sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE); + break; + case SOUND_MASK_CD: + sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD); + break; + case SOUND_MASK_SYNTH: + sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH); + break; + default: /* Also takes care of SOUND_MASK_MIC case */ + sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC); + break; + } + } + else + { + regimageL = regimageR = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + { + if ((1 << i) & devmask) + { + regimageL |= sb16_recmasks_L[i]; + regimageR |= sb16_recmasks_R[i]; + } + sb_setmixer (devc, SB16_IMASK_L, regimageL); + sb_setmixer (devc, SB16_IMASK_R, regimageR); + } + } + break; + } + devc->recmask = devmask; + return devc->recmask; +} + +static int set_outmask(sb_devc * devc, int mask) +{ + int devmask, i; + unsigned char regimage; + + devmask = mask & devc->supported_out_devices; + + switch (devc->model) + { + case MDL_SB16: + if (devc->submodel == SUBMDL_ALS007) + break; + else + { + regimage = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + { + if ((1 << i) & devmask) + { + regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]); + } + sb_setmixer (devc, SB16_OMASK, regimage); + } + } + break; + default: + break; + } + + devc->outmask = devmask; + return devc->outmask; +} + +static int sb_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + sb_devc *devc = mixer_devs[dev]->devc; + int val, ret; + + /* + * Use ioctl(fd, SOUND_MIXER_AGC, &mode) to turn AGC off (0) or on (1). + * Use ioctl(fd, SOUND_MIXER_3DSE, &mode) to turn 3DSE off (0) or on (1) + * or mode==2 put 3DSE state to mode. + */ + if (devc->model == MDL_SB16) { + if (cmd == SOUND_MIXER_AGC) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + sb_setmixer(devc, 0x43, (~val) & 0x01); + return 0; + } + if (cmd == SOUND_MIXER_3DSE) + { + /* I put here 15, but I don't know the exact version. + At least my 4.13 havn't 3DSE, 4.16 has it. */ + if (devc->minor < 15) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val == 0 || val == 1) + sb_chgmixer(devc, AWE_3DSE, 0x01, val); + else if (val == 2) + { + ret = sb_getmixer(devc, AWE_3DSE)&0x01; + return put_user(ret, (int *)arg); + } + else + return -EINVAL; + return 0; + } + } + if (((cmd >> 8) & 0xff) == 'M') + { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + ret = set_recmask(devc, val); + break; + + case SOUND_MIXER_OUTSRC: + ret = set_outmask(devc, val); + break; + + default: + ret = sb_mixer_set(devc, cmd & 0xff, val); + } + } + else switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + ret = devc->recmask; + break; + + case SOUND_MIXER_OUTSRC: + ret = devc->outmask; + break; + + case SOUND_MIXER_DEVMASK: + ret = devc->supported_devices; + break; + + case SOUND_MIXER_STEREODEVS: + ret = devc->supported_devices; + /* The ESS seems to have stereo mic controls */ + if (devc->model == MDL_ESS) + ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX); + else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW) + ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); + break; + + case SOUND_MIXER_RECMASK: + ret = devc->supported_rec_devices; + break; + + case SOUND_MIXER_OUTMASK: + ret = devc->supported_out_devices; + break; + + case SOUND_MIXER_CAPS: + ret = devc->mixer_caps; + break; + + default: + ret = sb_mixer_get(devc, cmd & 0xff); + break; + } + return put_user(ret, (int *)arg); + } else + return -EINVAL; +} + +static struct mixer_operations sb_mixer_operations = +{ + owner: THIS_MODULE, + id: "SB", + name: "Sound Blaster", + ioctl: sb_mixer_ioctl +}; + +static struct mixer_operations als007_mixer_operations = +{ + owner: THIS_MODULE, + id: "ALS007", + name: "Avance ALS-007", + ioctl: sb_mixer_ioctl +}; + +static void sb_mixer_reset(sb_devc * devc) +{ + char name[32]; + int i; + + sprintf(name, "SB_%d", devc->sbmixnum); + + if (devc->sbmo.sm_games) + devc->levels = load_mixer_volumes(name, smg_default_levels, 1); + else + devc->levels = load_mixer_volumes(name, sb_default_levels, 1); + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + sb_mixer_set(devc, i, devc->levels[i]); + + if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) { + set_recmask(devc, SOUND_MASK_MIC); + }; +} + +int sb_mixer_init(sb_devc * devc, struct module *owner) +{ + int mixer_type = 0; + int m; + + devc->sbmixnum = sbmixnum++; + devc->levels = NULL; + + sb_setmixer(devc, 0x00, 0); /* Reset mixer */ + + if (!(mixer_type = detect_mixer(devc))) + return 0; /* No mixer. Why? */ + + switch (devc->model) + { + case MDL_ESSPCI: + case MDL_YMPCI: + case MDL_SBPRO: + case MDL_AZTECH: + case MDL_JAZZ: + devc->mixer_caps = SOUND_CAP_EXCL_INPUT; + devc->supported_devices = SBPRO_MIXER_DEVICES; + devc->supported_rec_devices = SBPRO_RECORDING_DEVICES; + devc->iomap = &sbpro_mix; + break; + + case MDL_ESS: + ess_mixer_init (devc); + break; + + case MDL_SMW: + devc->mixer_caps = SOUND_CAP_EXCL_INPUT; + devc->supported_devices = 0; + devc->supported_rec_devices = 0; + devc->iomap = &sbpro_mix; + smw_mixer_init(devc); + break; + + case MDL_SB16: + devc->mixer_caps = 0; + devc->supported_rec_devices = SB16_RECORDING_DEVICES; + devc->supported_out_devices = SB16_OUTFILTER_DEVICES; + if (devc->submodel != SUBMDL_ALS007) + { + devc->supported_devices = SB16_MIXER_DEVICES; + devc->iomap = &sb16_mix; + } + else + { + devc->supported_devices = ALS007_MIXER_DEVICES; + devc->iomap = &als007_mix; + } + break; + + default: + printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model); + return 0; + } + + m = sound_alloc_mixerdev(); + if (m == -1) + return 0; + + mixer_devs[m] = (struct mixer_operations *)kmalloc(sizeof(struct mixer_operations), GFP_KERNEL); + if (mixer_devs[m] == NULL) + { + printk(KERN_ERR "sb_mixer: Can't allocate memory\n"); + sound_unload_mixerdev(m); + return 0; + } + + if (devc->submodel != SUBMDL_ALS007) + memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations)); + else + memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations)); + + mixer_devs[m]->devc = devc; + + if (owner) + mixer_devs[m]->owner = owner; + + devc->my_mixerdev = m; + sb_mixer_reset(devc); + return 1; +} + +void sb_mixer_unload(sb_devc *devc) +{ + if (devc->my_mixerdev == -1) + return; + + kfree(mixer_devs[devc->my_mixerdev]); + sound_unload_mixerdev(devc->my_mixerdev); + sbmixnum--; +} diff -Nru a/sound/oss/sb_mixer.h b/sound/oss/sb_mixer.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sb_mixer.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,105 @@ +/* + * sound/sb_mixer.h + * + * Definitions for the SB Pro and SB16 mixers + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +/* + * Modified: + * Hunyue Yau Jan 6 1994 + * Added defines for the Sound Galaxy NX Pro mixer. + * + * Rolf Fokkens Dec 20 1998 + * Added defines for some ES188x chips. + * + * Rolf Fokkens Dec 27 1998 + * Moved static stuff to sb_mixer.c + * + */ +/* + * Mixer registers + * + * NOTE! RECORD_SRC == IN_FILTER + */ + +/* + * Mixer registers of SB Pro + */ +#define VOC_VOL 0x04 +#define MIC_VOL 0x0A +#define MIC_MIX 0x0A +#define RECORD_SRC 0x0C +#define IN_FILTER 0x0C +#define OUT_FILTER 0x0E +#define MASTER_VOL 0x22 +#define FM_VOL 0x26 +#define CD_VOL 0x28 +#define LINE_VOL 0x2E +#define IRQ_NR 0x80 +#define DMA_NR 0x81 +#define IRQ_STAT 0x82 +#define OPSW 0x3c + +/* + * Additional registers on the SG NX Pro + */ +#define COVOX_VOL 0x42 +#define TREBLE_LVL 0x44 +#define BASS_LVL 0x46 + +#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */ +#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */ +#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */ +#define FILT_OFF (1 << 5) + +#define MONO_DAC 0x00 +#define STEREO_DAC 0x02 + +/* + * Mixer registers of SB16 + */ +#define SB16_OMASK 0x3c +#define SB16_IMASK_L 0x3d +#define SB16_IMASK_R 0x3e + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + +/* + * 3DSE register of AWE32/64 + */ +#define AWE_3DSE 0x90 + +/* + * Mixer registers of ALS007 + */ +#define ALS007_RECORD_SRC 0x6c +#define ALS007_OUTPUT_CTRL1 0x3c +#define ALS007_OUTPUT_CTRL2 0x4c + +#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \ + {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}} + +/* + * Recording sources (SB Pro) + */ + +#define SRC__MIC 1 /* Select Microphone recording source */ +#define SRC__CD 3 /* Select CD recording source */ +#define SRC__LINE 7 /* Use Line-in for recording source */ + +/* + * Recording sources for ALS-007 + */ + +#define ALS007_MIC 4 +#define ALS007_LINE 6 +#define ALS007_CD 2 +#define ALS007_SYNTH 7 diff -Nru a/sound/oss/sequencer.c b/sound/oss/sequencer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sequencer.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1699 @@ +/* + * sound/sequencer.c + * + * The sequencer personality manager. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Alan Cox : reformatted and fixed a pair of null pointer bugs + */ +#include + +#define SEQUENCER_C +#include "sound_config.h" + +#include "midi_ctrl.h" + +static int sequencer_ok = 0; +static struct sound_timer_operations *tmr; +static int tmr_no = -1; /* Currently selected timer */ +static int pending_timer = -1; /* For timer change operation */ +extern unsigned long seq_time; + +static int obsolete_api_used = 0; + +/* + * Local counts for number of synth and MIDI devices. These are initialized + * by the sequencer_open. + */ +static int max_mididev = 0; +static int max_synthdev = 0; + +/* + * The seq_mode gives the operating mode of the sequencer: + * 1 = level1 (the default) + * 2 = level2 (extended capabilities) + */ + +#define SEQ_1 1 +#define SEQ_2 2 +static int seq_mode = SEQ_1; + +static DECLARE_WAIT_QUEUE_HEAD(seq_sleeper); +static DECLARE_WAIT_QUEUE_HEAD(midi_sleeper); + +static int midi_opened[MAX_MIDI_DEV] = { + 0 +}; + +static int midi_written[MAX_MIDI_DEV] = { + 0 +}; + +static unsigned long prev_input_time = 0; +static int prev_event_time; + +#include "tuning.h" + +#define EV_SZ 8 +#define IEV_SZ 8 + +static unsigned char *queue = NULL; +static unsigned char *iqueue = NULL; + +static volatile int qhead = 0, qtail = 0, qlen = 0; +static volatile int iqhead = 0, iqtail = 0, iqlen = 0; +static volatile int seq_playing = 0; +static volatile int sequencer_busy = 0; +static int output_threshold; +static long pre_event_timeout; +static unsigned synth_open_mask; + +static int seq_queue(unsigned char *note, char nonblock); +static void seq_startplay(void); +static int seq_sync(void); +static void seq_reset(void); + +#if MAX_SYNTH_DEV > 15 +#error Too many synthesizer devices enabled. +#endif + +int sequencer_read(int dev, struct file *file, char *buf, int count) +{ + int c = count, p = 0; + int ev_len; + unsigned long flags; + + dev = dev >> 4; + + ev_len = seq_mode == SEQ_1 ? 4 : 8; + + save_flags(flags); + cli(); + + if (!iqlen) + { + if (file->f_flags & O_NONBLOCK) { + restore_flags(flags); + return -EAGAIN; + } + + interruptible_sleep_on_timeout(&midi_sleeper, + pre_event_timeout); + if (!iqlen) + { + restore_flags(flags); + return 0; + } + } + while (iqlen && c >= ev_len) + { + char *fixit = (char *) &iqueue[iqhead * IEV_SZ]; + copy_to_user(&(buf)[p], fixit, ev_len); + p += ev_len; + c -= ev_len; + + iqhead = (iqhead + 1) % SEQ_MAX_QUEUE; + iqlen--; + } + restore_flags(flags); + return count - c; +} + +static void sequencer_midi_output(int dev) +{ + /* + * Currently NOP + */ +} + +void seq_copy_to_input(unsigned char *event_rec, int len) +{ + unsigned long flags; + + /* + * Verify that the len is valid for the current mode. + */ + + if (len != 4 && len != 8) + return; + if ((seq_mode == SEQ_1) != (len == 4)) + return; + + if (iqlen >= (SEQ_MAX_QUEUE - 1)) + return; /* Overflow */ + + save_flags(flags); + cli(); + memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len); + iqlen++; + iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; + wake_up(&midi_sleeper); + restore_flags(flags); +} + +static void sequencer_midi_input(int dev, unsigned char data) +{ + unsigned int tstamp; + unsigned char event_rec[4]; + + if (data == 0xfe) /* Ignore active sensing */ + return; + + tstamp = jiffies - seq_time; + + if (tstamp != prev_input_time) + { + tstamp = (tstamp << 8) | SEQ_WAIT; + seq_copy_to_input((unsigned char *) &tstamp, 4); + prev_input_time = tstamp; + } + event_rec[0] = SEQ_MIDIPUTC; + event_rec[1] = data; + event_rec[2] = dev; + event_rec[3] = 0; + + seq_copy_to_input(event_rec, 4); +} + +void seq_input_event(unsigned char *event_rec, int len) +{ + unsigned long this_time; + + if (seq_mode == SEQ_2) + this_time = tmr->get_time(tmr_no); + else + this_time = jiffies - seq_time; + + if (this_time != prev_input_time) + { + unsigned char tmp_event[8]; + + tmp_event[0] = EV_TIMING; + tmp_event[1] = TMR_WAIT_ABS; + tmp_event[2] = 0; + tmp_event[3] = 0; + *(unsigned int *) &tmp_event[4] = this_time; + + seq_copy_to_input(tmp_event, 8); + prev_input_time = this_time; + } + seq_copy_to_input(event_rec, len); +} + +int sequencer_write(int dev, struct file *file, const char *buf, int count) +{ + unsigned char event_rec[EV_SZ], ev_code; + int p = 0, c, ev_size; + int err; + int mode = translate_mode(file); + + dev = dev >> 4; + + DEB(printk("sequencer_write(dev=%d, count=%d)\n", dev, count)); + + if (mode == OPEN_READ) + return -EIO; + + c = count; + + while (c >= 4) + { + copy_from_user((char *) event_rec, &(buf)[p], 4); + ev_code = event_rec[0]; + + if (ev_code == SEQ_FULLSIZE) + { + int err, fmt; + + dev = *(unsigned short *) &event_rec[2]; + if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL) + return -ENXIO; + + if (!(synth_open_mask & (1 << dev))) + return -ENXIO; + + fmt = (*(short *) &event_rec[0]) & 0xffff; + err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0); + if (err < 0) + return err; + + return err; + } + if (ev_code >= 128) + { + if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) + { + printk(KERN_WARNING "Sequencer: Invalid level 2 event %x\n", ev_code); + return -EINVAL; + } + ev_size = 8; + + if (c < ev_size) + { + if (!seq_playing) + seq_startplay(); + return count - c; + } + copy_from_user((char *) &event_rec[4], &(buf)[p + 4], 4); + + } + else + { + if (seq_mode == SEQ_2) + { + printk(KERN_WARNING "Sequencer: 4 byte event in level 2 mode\n"); + return -EINVAL; + } + ev_size = 4; + + if (event_rec[0] != SEQ_MIDIPUTC) + obsolete_api_used = 1; + } + + if (event_rec[0] == SEQ_MIDIPUTC) + { + if (!midi_opened[event_rec[2]]) + { + int mode; + int dev = event_rec[2]; + + if (dev >= max_mididev || midi_devs[dev]==NULL) + { + /*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/ + return -ENXIO; + } + mode = translate_mode(file); + + if ((err = midi_devs[dev]->open(dev, mode, + sequencer_midi_input, sequencer_midi_output)) < 0) + { + seq_reset(); + printk(KERN_WARNING "Sequencer Error: Unable to open Midi #%d\n", dev); + return err; + } + midi_opened[dev] = 1; + } + } + if (!seq_queue(event_rec, (file->f_flags & (O_NONBLOCK) ? 1 : 0))) + { + int processed = count - c; + + if (!seq_playing) + seq_startplay(); + + if (!processed && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + else + return processed; + } + p += ev_size; + c -= ev_size; + } + + if (!seq_playing) + seq_startplay(); + + return count; +} + +static int seq_queue(unsigned char *note, char nonblock) +{ + + /* + * Test if there is space in the queue + */ + + if (qlen >= SEQ_MAX_QUEUE) + if (!seq_playing) + seq_startplay(); /* + * Give chance to drain the queue + */ + + if (!nonblock && qlen >= SEQ_MAX_QUEUE && !waitqueue_active(&seq_sleeper)) { + /* + * Sleep until there is enough space on the queue + */ + interruptible_sleep_on(&seq_sleeper); + } + if (qlen >= SEQ_MAX_QUEUE) + { + return 0; /* + * To be sure + */ + } + memcpy(&queue[qtail * EV_SZ], note, EV_SZ); + + qtail = (qtail + 1) % SEQ_MAX_QUEUE; + qlen++; + + return 1; +} + +static int extended_event(unsigned char *q) +{ + int dev = q[2]; + + if (dev < 0 || dev >= max_synthdev) + return -ENXIO; + + if (!(synth_open_mask & (1 << dev))) + return -ENXIO; + + switch (q[1]) + { + case SEQ_NOTEOFF: + synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]); + break; + + case SEQ_NOTEON: + if (q[4] > 127 && q[4] != 255) + return 0; + + if (q[5] == 0) + { + synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]); + break; + } + synth_devs[dev]->start_note(dev, q[3], q[4], q[5]); + break; + + case SEQ_PGMCHANGE: + synth_devs[dev]->set_instr(dev, q[3], q[4]); + break; + + case SEQ_AFTERTOUCH: + synth_devs[dev]->aftertouch(dev, q[3], q[4]); + break; + + case SEQ_BALANCE: + synth_devs[dev]->panning(dev, q[3], (char) q[4]); + break; + + case SEQ_CONTROLLER: + synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8))); + break; + + case SEQ_VOLMODE: + if (synth_devs[dev]->volume_method != NULL) + synth_devs[dev]->volume_method(dev, q[3]); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int find_voice(int dev, int chn, int note) +{ + unsigned short key; + int i; + + key = (chn << 8) | (note + 1); + for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) + if (synth_devs[dev]->alloc.map[i] == key) + return i; + return -1; +} + +static int alloc_voice(int dev, int chn, int note) +{ + unsigned short key; + int voice; + + key = (chn << 8) | (note + 1); + + voice = synth_devs[dev]->alloc_voice(dev, chn, note, + &synth_devs[dev]->alloc); + synth_devs[dev]->alloc.map[voice] = key; + synth_devs[dev]->alloc.alloc_times[voice] = + synth_devs[dev]->alloc.timestamp++; + return voice; +} + +static void seq_chn_voice_event(unsigned char *event_rec) +{ +#define dev event_rec[1] +#define cmd event_rec[2] +#define chn event_rec[3] +#define note event_rec[4] +#define parm event_rec[5] + + int voice = -1; + + if ((int) dev > max_synthdev || synth_devs[dev] == NULL) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; + + if (seq_mode == SEQ_2) + { + if (synth_devs[dev]->alloc_voice) + voice = find_voice(dev, chn, note); + + if (cmd == MIDI_NOTEON && parm == 0) + { + cmd = MIDI_NOTEOFF; + parm = 64; + } + } + + switch (cmd) + { + case MIDI_NOTEON: + if (note > 127 && note != 255) /* Not a seq2 feature */ + return; + + if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice) + { + /* Internal synthesizer (FM, GUS, etc) */ + voice = alloc_voice(dev, chn, note); + } + if (voice == -1) + voice = chn; + + if (seq_mode == SEQ_2 && (int) dev < num_synths) + { + /* + * The MIDI channel 10 is a percussive channel. Use the note + * number to select the proper patch (128 to 255) to play. + */ + + if (chn == 9) + { + synth_devs[dev]->set_instr(dev, voice, 128 + note); + synth_devs[dev]->chn_info[chn].pgm_num = 128 + note; + } + synth_devs[dev]->setup_voice(dev, voice, chn); + } + synth_devs[dev]->start_note(dev, voice, note, parm); + break; + + case MIDI_NOTEOFF: + if (voice == -1) + voice = chn; + synth_devs[dev]->kill_note(dev, voice, note, parm); + break; + + case MIDI_KEY_PRESSURE: + if (voice == -1) + voice = chn; + synth_devs[dev]->aftertouch(dev, voice, parm); + break; + + default:; + } +#undef dev +#undef cmd +#undef chn +#undef note +#undef parm +} + + +static void seq_chn_common_event(unsigned char *event_rec) +{ + unsigned char dev = event_rec[1]; + unsigned char cmd = event_rec[2]; + unsigned char chn = event_rec[3]; + unsigned char p1 = event_rec[4]; + + /* unsigned char p2 = event_rec[5]; */ + unsigned short w14 = *(short *) &event_rec[6]; + + if ((int) dev > max_synthdev || synth_devs[dev] == NULL) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; + + switch (cmd) + { + case MIDI_PGM_CHANGE: + if (seq_mode == SEQ_2) + { + synth_devs[dev]->chn_info[chn].pgm_num = p1; + if ((int) dev >= num_synths) + synth_devs[dev]->set_instr(dev, chn, p1); + } + else + synth_devs[dev]->set_instr(dev, chn, p1); + + break; + + case MIDI_CTL_CHANGE: + if (seq_mode == SEQ_2) + { + if (chn > 15 || p1 > 127) + break; + + synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f; + + if (p1 < 32) /* Setting MSB should clear LSB to 0 */ + synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0; + + if ((int) dev < num_synths) + { + int val = w14 & 0x7f; + int i, key; + + if (p1 < 64) /* Combine MSB and LSB */ + { + val = ((synth_devs[dev]-> + chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7) + | (synth_devs[dev]-> + chn_info[chn].controllers[p1 | 32] & 0x7f); + p1 &= ~32; + } + /* Handle all playing notes on this channel */ + + key = ((int) chn << 8); + + for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) + if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) + synth_devs[dev]->controller(dev, i, p1, val); + } + else + synth_devs[dev]->controller(dev, chn, p1, w14); + } + else /* Mode 1 */ + synth_devs[dev]->controller(dev, chn, p1, w14); + break; + + case MIDI_PITCH_BEND: + if (seq_mode == SEQ_2) + { + synth_devs[dev]->chn_info[chn].bender_value = w14; + + if ((int) dev < num_synths) + { + /* Handle all playing notes on this channel */ + int i, key; + + key = (chn << 8); + + for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) + if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) + synth_devs[dev]->bender(dev, i, w14); + } + else + synth_devs[dev]->bender(dev, chn, w14); + } + else /* MODE 1 */ + synth_devs[dev]->bender(dev, chn, w14); + break; + + default:; + } +} + +static int seq_timing_event(unsigned char *event_rec) +{ + unsigned char cmd = event_rec[1]; + unsigned int parm = *(int *) &event_rec[4]; + + if (seq_mode == SEQ_2) + { + int ret; + + if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED) + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); + return ret; + } + switch (cmd) + { + case TMR_WAIT_REL: + parm += prev_event_time; + + /* + * NOTE! No break here. Execution of TMR_WAIT_REL continues in the + * next case (TMR_WAIT_ABS) + */ + + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + time = parm; + prev_event_time = time; + + seq_playing = 1; + request_sound_timer(time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); + return TIMER_ARMED; + } + break; + + case TMR_START: + seq_time = jiffies; + prev_input_time = 0; + prev_event_time = 0; + break; + + case TMR_STOP: + break; + + case TMR_CONTINUE: + break; + + case TMR_TEMPO: + break; + + case TMR_ECHO: + if (seq_mode == SEQ_2) + seq_copy_to_input(event_rec, 8); + else + { + parm = (parm << 8 | SEQ_ECHO); + seq_copy_to_input((unsigned char *) &parm, 4); + } + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static void seq_local_event(unsigned char *event_rec) +{ + unsigned char cmd = event_rec[1]; + unsigned int parm = *((unsigned int *) &event_rec[4]); + + switch (cmd) + { + case LOCL_STARTAUDIO: + DMAbuf_start_devices(parm); + break; + + default:; + } +} + +static void seq_sysex_message(unsigned char *event_rec) +{ + int dev = event_rec[1]; + int i, l = 0; + unsigned char *buf = &event_rec[2]; + + if ((int) dev > max_synthdev) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; + + l = 0; + for (i = 0; i < 6 && buf[i] != 0xff; i++) + l = i + 1; + + if (!synth_devs[dev]->send_sysex) + return; + if (l > 0) + synth_devs[dev]->send_sysex(dev, buf, l); +} + +static int play_event(unsigned char *q) +{ + /* + * NOTE! This routine returns + * 0 = normal event played. + * 1 = Timer armed. Suspend playback until timer callback. + * 2 = MIDI output buffer full. Restore queue and suspend until timer + */ + unsigned int *delay; + + switch (q[0]) + { + case SEQ_NOTEOFF: + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->kill_note(0, q[1], 255, q[3]); + break; + + case SEQ_NOTEON: + if (q[4] < 128 || q[4] == 255) + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->start_note(0, q[1], q[2], q[3]); + break; + + case SEQ_WAIT: + delay = (unsigned int *) q; /* + * Bytes 1 to 3 are containing the * + * delay in 'ticks' + */ + *delay = (*delay >> 8) & 0xffffff; + + if (*delay > 0) + { + long time; + + seq_playing = 1; + time = *delay; + prev_event_time = time; + + request_sound_timer(time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); + /* + * The timer is now active and will reinvoke this function + * after the timer expires. Return to the caller now. + */ + return 1; + } + break; + + case SEQ_PGMCHANGE: + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->set_instr(0, q[1], q[2]); + break; + + case SEQ_SYNCTIMER: /* + * Reset timer + */ + seq_time = jiffies; + prev_input_time = 0; + prev_event_time = 0; + break; + + case SEQ_MIDIPUTC: /* + * Put a midi character + */ + if (midi_opened[q[2]]) + { + int dev; + + dev = q[2]; + + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + break; + + if (!midi_devs[dev]->outputc(dev, q[1])) + { + /* + * Output FIFO is full. Wait one timer cycle and try again. + */ + + seq_playing = 1; + request_sound_timer(-1); + return 2; + } + else + midi_written[dev] = 1; + } + break; + + case SEQ_ECHO: + seq_copy_to_input(q, 4); /* + * Echo back to the process + */ + break; + + case SEQ_PRIVATE: + if ((int) q[1] < max_synthdev) + synth_devs[q[1]]->hw_control(q[1], q); + break; + + case SEQ_EXTENDED: + extended_event(q); + break; + + case EV_CHN_VOICE: + seq_chn_voice_event(q); + break; + + case EV_CHN_COMMON: + seq_chn_common_event(q); + break; + + case EV_TIMING: + if (seq_timing_event(q) == TIMER_ARMED) + { + return 1; + } + break; + + case EV_SEQ_LOCAL: + seq_local_event(q); + break; + + case EV_SYSEX: + seq_sysex_message(q); + break; + + default:; + } + return 0; +} + +static void seq_startplay(void) +{ + unsigned long flags; + int this_one, action; + + while (qlen > 0) + { + + save_flags(flags); + cli(); + qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; + qlen--; + restore_flags(flags); + + seq_playing = 1; + + if ((action = play_event(&queue[this_one * EV_SZ]))) + { /* Suspend playback. Next timer routine invokes this routine again */ + if (action == 2) + { + qlen++; + qhead = this_one; + } + return; + } + } + + seq_playing = 0; + + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); +} + +static void reset_controllers(int dev, unsigned char *controller, int update_dev) +{ + int i; + for (i = 0; i < 128; i++) + controller[i] = ctrl_def_values[i]; +} + +static void setup_mode2(void) +{ + int dev; + + max_synthdev = num_synths; + + for (dev = 0; dev < num_midis; dev++) + { + if (midi_devs[dev] && midi_devs[dev]->converter != NULL) + { + synth_devs[max_synthdev++] = midi_devs[dev]->converter; + } + } + + for (dev = 0; dev < max_synthdev; dev++) + { + int chn; + + synth_devs[dev]->sysex_ptr = 0; + synth_devs[dev]->emulation = 0; + + for (chn = 0; chn < 16; chn++) + { + synth_devs[dev]->chn_info[chn].pgm_num = 0; + reset_controllers(dev, + synth_devs[dev]->chn_info[chn].controllers,0); + synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */ + synth_devs[dev]->chn_info[chn].bender_range = 200; + } + } + max_mididev = 0; + seq_mode = SEQ_2; +} + +int sequencer_open(int dev, struct file *file) +{ + int retval, mode, i; + int level, tmp; + unsigned long flags; + + if (!sequencer_ok) + sequencer_init(); + + level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1; + + dev = dev >> 4; + mode = translate_mode(file); + + DEB(printk("sequencer_open(dev=%d)\n", dev)); + + if (!sequencer_ok) + { +/* printk("Sound card: sequencer not initialized\n");*/ + return -ENXIO; + } + if (dev) /* Patch manager device (obsolete) */ + return -ENXIO; + + if(synth_devs[dev] == NULL) + request_module("synth0"); + + if (mode == OPEN_READ) + { + if (!num_midis) + { + /*printk("Sequencer: No MIDI devices. Input not possible\n");*/ + sequencer_busy = 0; + return -ENXIO; + } + } + save_flags(flags); + cli(); + if (sequencer_busy) + { + restore_flags(flags); + return -EBUSY; + } + sequencer_busy = 1; + obsolete_api_used = 0; + restore_flags(flags); + + max_mididev = num_midis; + max_synthdev = num_synths; + pre_event_timeout = MAX_SCHEDULE_TIMEOUT; + seq_mode = SEQ_1; + + if (pending_timer != -1) + { + tmr_no = pending_timer; + pending_timer = -1; + } + if (tmr_no == -1) /* Not selected yet */ + { + int i, best; + + best = -1; + for (i = 0; i < num_sound_timers; i++) + if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best) + { + tmr_no = i; + best = sound_timer_devs[i]->priority; + } + if (tmr_no == -1) /* Should not be */ + tmr_no = 0; + } + tmr = sound_timer_devs[tmr_no]; + + if (level == 2) + { + if (tmr == NULL) + { + /*printk("sequencer: No timer for level 2\n");*/ + sequencer_busy = 0; + return -ENXIO; + } + setup_mode2(); + } + if (!max_synthdev && !max_mididev) + { + sequencer_busy=0; + return -ENXIO; + } + + synth_open_mask = 0; + + for (i = 0; i < max_mididev; i++) + { + midi_opened[i] = 0; + midi_written[i] = 0; + } + + for (i = 0; i < max_synthdev; i++) + { + if (synth_devs[i]==NULL) + continue; + + if (synth_devs[i]->owner) + __MOD_INC_USE_COUNT (synth_devs[i]->owner); + + if ((tmp = synth_devs[i]->open(i, mode)) < 0) + { + printk(KERN_WARNING "Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp); + if (synth_devs[i]->midi_dev) + printk(KERN_WARNING "(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev); + } + else + { + synth_open_mask |= (1 << i); + if (synth_devs[i]->midi_dev) + midi_opened[synth_devs[i]->midi_dev] = 1; + } + } + + seq_time = jiffies; + + prev_input_time = 0; + prev_event_time = 0; + + if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) + { + /* + * Initialize midi input devices + */ + + for (i = 0; i < max_mididev; i++) + if (!midi_opened[i] && midi_devs[i]) + { + if (midi_devs[i]->owner) + __MOD_INC_USE_COUNT (midi_devs[i]->owner); + + if ((retval = midi_devs[i]->open(i, mode, + sequencer_midi_input, sequencer_midi_output)) >= 0) + { + midi_opened[i] = 1; + } + } + } + + if (seq_mode == SEQ_2) { + if (tmr->owner) + __MOD_INC_USE_COUNT (tmr->owner); + tmr->open(tmr_no, seq_mode); + } + + init_waitqueue_head(&seq_sleeper); + init_waitqueue_head(&midi_sleeper); + output_threshold = SEQ_MAX_QUEUE / 2; + + return 0; +} + +void seq_drain_midi_queues(void) +{ + int i, n; + + /* + * Give the Midi drivers time to drain their output queues + */ + + n = 1; + + while (!signal_pending(current) && n) + { + n = 0; + + for (i = 0; i < max_mididev; i++) + if (midi_opened[i] && midi_written[i]) + if (midi_devs[i]->buffer_status != NULL) + if (midi_devs[i]->buffer_status(i)) + n++; + + /* + * Let's have a delay + */ + + if (n) + interruptible_sleep_on_timeout(&seq_sleeper, + HZ/10); + } +} + +void sequencer_release(int dev, struct file *file) +{ + int i; + int mode = translate_mode(file); + + dev = dev >> 4; + + DEB(printk("sequencer_release(dev=%d)\n", dev)); + + /* + * Wait until the queue is empty (if we don't have nonblock) + */ + + if (mode != OPEN_READ && !(file->f_flags & O_NONBLOCK)) + { + while (!signal_pending(current) && qlen > 0) + { + seq_sync(); + interruptible_sleep_on_timeout(&seq_sleeper, + 3*HZ); + /* Extra delay */ + } + } + + if (mode != OPEN_READ) + seq_drain_midi_queues(); /* + * Ensure the output queues are empty + */ + seq_reset(); + if (mode != OPEN_READ) + seq_drain_midi_queues(); /* + * Flush the all notes off messages + */ + + for (i = 0; i < max_synthdev; i++) + { + if (synth_open_mask & (1 << i)) /* + * Actually opened + */ + if (synth_devs[i]) + { + synth_devs[i]->close(i); + + if (synth_devs[i]->owner) + __MOD_DEC_USE_COUNT (synth_devs[i]->owner); + + if (synth_devs[i]->midi_dev) + midi_opened[synth_devs[i]->midi_dev] = 0; + } + } + + for (i = 0; i < max_mididev; i++) + { + if (midi_opened[i]) { + midi_devs[i]->close(i); + if (midi_devs[i]->owner) + __MOD_DEC_USE_COUNT (midi_devs[i]->owner); + } + } + + if (seq_mode == SEQ_2) { + tmr->close(tmr_no); + if (tmr->owner) + __MOD_DEC_USE_COUNT (tmr->owner); + } + + if (obsolete_api_used) + printk(KERN_WARNING "/dev/music: Obsolete (4 byte) API was used by %s\n", current->comm); + sequencer_busy = 0; +} + +static int seq_sync(void) +{ + unsigned long flags; + + if (qlen && !seq_playing && !signal_pending(current)) + seq_startplay(); + + save_flags(flags); + cli(); + if (qlen > 0) + interruptible_sleep_on_timeout(&seq_sleeper, HZ); + restore_flags(flags); + return qlen; +} + +static void midi_outc(int dev, unsigned char data) +{ + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int n; + unsigned long flags; + + /* + * This routine sends one byte to the Midi channel. + * If the output FIFO is full, it waits until there + * is space in the queue + */ + + n = 3 * HZ; /* Timeout */ + + save_flags(flags); + cli(); + while (n && !midi_devs[dev]->outputc(dev, data)) { + interruptible_sleep_on_timeout(&seq_sleeper, HZ/25); + n--; + } + restore_flags(flags); +} + +static void seq_reset(void) +{ + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int i; + int chn; + unsigned long flags; + + sound_stop_timer(); + + seq_time = jiffies; + prev_input_time = 0; + prev_event_time = 0; + + qlen = qhead = qtail = 0; + iqlen = iqhead = iqtail = 0; + + for (i = 0; i < max_synthdev; i++) + if (synth_open_mask & (1 << i)) + if (synth_devs[i]) + synth_devs[i]->reset(i); + + if (seq_mode == SEQ_2) + { + for (chn = 0; chn < 16; chn++) + for (i = 0; i < max_synthdev; i++) + if (synth_open_mask & (1 << i)) + if (synth_devs[i]) + { + synth_devs[i]->controller(i, chn, 123, 0); /* All notes off */ + synth_devs[i]->controller(i, chn, 121, 0); /* Reset all ctl */ + synth_devs[i]->bender(i, chn, 1 << 13); /* Bender off */ + } + } + else /* seq_mode == SEQ_1 */ + { + for (i = 0; i < max_mididev; i++) + if (midi_written[i]) /* + * Midi used. Some notes may still be playing + */ + { + /* + * Sending just a ACTIVE SENSING message should be enough to stop all + * playing notes. Since there are devices not recognizing the + * active sensing, we have to send some all notes off messages also. + */ + midi_outc(i, 0xfe); + + for (chn = 0; chn < 16; chn++) + { + midi_outc(i, (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */ + midi_outc(i, 0x7b); /* All notes off */ + midi_outc(i, 0); /* Dummy parameter */ + } + + midi_devs[i]->close(i); + + midi_written[i] = 0; + midi_opened[i] = 0; + } + } + + seq_playing = 0; + + save_flags(flags); + cli(); + + if (waitqueue_active(&seq_sleeper)) { + /* printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */ + wake_up(&seq_sleeper); + } + restore_flags(flags); +} + +static void seq_panic(void) +{ + /* + * This routine is called by the application in case the user + * wants to reset the system to the default state. + */ + + seq_reset(); + + /* + * Since some of the devices don't recognize the active sensing and + * all notes off messages, we have to shut all notes manually. + * + * TO BE IMPLEMENTED LATER + */ + + /* + * Also return the controllers to their default states + */ +} + +int sequencer_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg) +{ + int midi_dev, orig_dev, val, err; + int mode = translate_mode(file); + struct synth_info inf; + struct seq_event_rec event_rec; + unsigned long flags; + + orig_dev = dev = dev >> 4; + + switch (cmd) + { + case SNDCTL_TMR_TIMEBASE: + case SNDCTL_TMR_TEMPO: + case SNDCTL_TMR_START: + case SNDCTL_TMR_STOP: + case SNDCTL_TMR_CONTINUE: + case SNDCTL_TMR_METRONOME: + case SNDCTL_TMR_SOURCE: + if (seq_mode != SEQ_2) + return -EINVAL; + return tmr->ioctl(tmr_no, cmd, arg); + + case SNDCTL_TMR_SELECT: + if (seq_mode != SEQ_2) + return -EINVAL; + if (get_user(pending_timer, (int *)arg)) + return -EFAULT; + if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL) + { + pending_timer = -1; + return -EINVAL; + } + val = pending_timer; + break; + + case SNDCTL_SEQ_PANIC: + seq_panic(); + return -EINVAL; + + case SNDCTL_SEQ_SYNC: + if (mode == OPEN_READ) + return 0; + while (qlen > 0 && !signal_pending(current)) + seq_sync(); + return qlen ? -EINTR : 0; + + case SNDCTL_SEQ_RESET: + seq_reset(); + return 0; + + case SNDCTL_SEQ_TESTMIDI: + if (__get_user(midi_dev, (int *)arg)) + return -EFAULT; + if (midi_dev < 0 || midi_dev >= max_mididev || !midi_devs[midi_dev]) + return -ENXIO; + + if (!midi_opened[midi_dev] && + (err = midi_devs[midi_dev]->open(midi_dev, mode, sequencer_midi_input, + sequencer_midi_output)) < 0) + return err; + midi_opened[midi_dev] = 1; + return 0; + + case SNDCTL_SEQ_GETINCOUNT: + if (mode == OPEN_WRITE) + return 0; + val = iqlen; + break; + + case SNDCTL_SEQ_GETOUTCOUNT: + if (mode == OPEN_READ) + return 0; + val = SEQ_MAX_QUEUE - qlen; + break; + + case SNDCTL_SEQ_GETTIME: + if (seq_mode == SEQ_2) + return tmr->ioctl(tmr_no, cmd, arg); + val = jiffies - seq_time; + break; + + case SNDCTL_SEQ_CTRLRATE: + /* + * If *arg == 0, just return the current rate + */ + if (seq_mode == SEQ_2) + return tmr->ioctl(tmr_no, cmd, arg); + + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) + return -EINVAL; + val = HZ; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + case SNDCTL_SYNTH_REMOVESAMPLE: + case SNDCTL_SYNTH_CONTROL: + if (get_user(dev, (int *)arg)) + return -EFAULT; + if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + return synth_devs[dev]->ioctl(dev, cmd, arg); + + case SNDCTL_SEQ_NRSYNTHS: + val = max_synthdev; + break; + + case SNDCTL_SEQ_NRMIDIS: + val = max_mididev; + break; + + case SNDCTL_SYNTH_MEMAVL: + if (get_user(dev, (int *)arg)) + return -EFAULT; + if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + val = synth_devs[dev]->ioctl(dev, cmd, arg); + break; + + case SNDCTL_FM_4OP_ENABLE: + if (get_user(dev, (int *)arg)) + return -EFAULT; + if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) + return -ENXIO; + if (!(synth_open_mask & (1 << dev))) + return -ENXIO; + synth_devs[dev]->ioctl(dev, cmd, arg); + return 0; + + case SNDCTL_SYNTH_INFO: + if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device)))) + return -EFAULT; + if (dev < 0 || dev >= max_synthdev) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + return synth_devs[dev]->ioctl(dev, cmd, arg); + + /* Like SYNTH_INFO but returns ID in the name field */ + case SNDCTL_SYNTH_ID: + if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device)))) + return -EFAULT; + if (dev < 0 || dev >= max_synthdev) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + memcpy(&inf, synth_devs[dev]->info, sizeof(inf)); + strncpy(inf.name, synth_devs[dev]->id, sizeof(inf.name)); + inf.device = dev; + return copy_to_user(arg, &inf, sizeof(inf))?-EFAULT:0; + + case SNDCTL_SEQ_OUTOFBAND: + if (copy_from_user(&event_rec, arg, sizeof(event_rec))) + return -EFAULT; + save_flags(flags); + cli(); + play_event(event_rec.arr); + restore_flags(flags); + return 0; + + case SNDCTL_MIDI_INFO: + if (get_user(dev, (int *)(&(((struct midi_info *)arg)->device)))) + return -EFAULT; + if (dev < 0 || dev >= max_mididev || !midi_devs[dev]) + return -ENXIO; + midi_devs[dev]->info.device = dev; + return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct midi_info))?-EFAULT:0; + + case SNDCTL_SEQ_THRESHOLD: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 1) + val = 1; + if (val >= SEQ_MAX_QUEUE) + val = SEQ_MAX_QUEUE - 1; + output_threshold = val; + return 0; + + case SNDCTL_MIDI_PRETIME: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 0) + val = 0; + val = (HZ * val) / 10; + pre_event_timeout = val; + break; + + default: + if (mode == OPEN_READ) + return -EIO; + if (!synth_devs[0]) + return -ENXIO; + if (!(synth_open_mask & (1 << 0))) + return -ENXIO; + if (!synth_devs[0]->ioctl) + return -EINVAL; + return synth_devs[0]->ioctl(0, cmd, arg); + } + return put_user(val, (int *)arg); +} + +/* No kernel lock - we're using the global irq lock here */ +unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait) +{ + unsigned long flags; + unsigned int mask = 0; + + dev = dev >> 4; + + save_flags(flags); + cli(); + /* input */ + poll_wait(file, &midi_sleeper, wait); + if (iqlen) + mask |= POLLIN | POLLRDNORM; + + /* output */ + poll_wait(file, &seq_sleeper, wait); + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + mask |= POLLOUT | POLLWRNORM; + restore_flags(flags); + return mask; +} + + +void sequencer_timer(unsigned long dummy) +{ + seq_startplay(); +} + +int note_to_freq(int note_num) +{ + + /* + * This routine converts a midi note to a frequency (multiplied by 1000) + */ + + int note, octave, note_freq; + static int notes[] = + { + 261632, 277189, 293671, 311132, 329632, 349232, + 369998, 391998, 415306, 440000, 466162, 493880 + }; + +#define BASE_OCTAVE 5 + + octave = note_num / 12; + note = note_num % 12; + + note_freq = notes[note]; + + if (octave < BASE_OCTAVE) + note_freq >>= (BASE_OCTAVE - octave); + else if (octave > BASE_OCTAVE) + note_freq <<= (octave - BASE_OCTAVE); + + /* + * note_freq >>= 1; + */ + + return note_freq; +} + +unsigned long compute_finetune(unsigned long base_freq, int bend, int range, + int vibrato_cents) +{ + unsigned long amount; + int negative, semitones, cents, multiplier = 1; + + if (!bend) + return base_freq; + if (!range) + return base_freq; + + if (!base_freq) + return base_freq; + + if (range >= 8192) + range = 8192; + + bend = bend * range / 8192; /* Convert to cents */ + bend += vibrato_cents; + + if (!bend) + return base_freq; + + negative = bend < 0 ? 1 : 0; + + if (bend < 0) + bend *= -1; + if (bend > range) + bend = range; + + /* + if (bend > 2399) + bend = 2399; + */ + while (bend > 2399) + { + multiplier *= 4; + bend -= 2400; + } + + semitones = bend / 100; + if (semitones > 99) + semitones = 99; + cents = bend % 100; + + amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000; + + if (negative) + return (base_freq * 10000) / amount; /* Bend down */ + else + return (base_freq * amount) / 10000; /* Bend up */ +} + + +void sequencer_init(void) +{ + /* drag in sequencer_syms.o */ + { + extern char sequencer_syms_symbol; + sequencer_syms_symbol = 0; + } + + if (sequencer_ok) + return; + MIDIbuf_init(); + queue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * EV_SZ); + if (queue == NULL) + { + printk(KERN_ERR "sequencer: Can't allocate memory for sequencer output queue\n"); + return; + } + iqueue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * IEV_SZ); + if (iqueue == NULL) + { + printk(KERN_ERR "sequencer: Can't allocate memory for sequencer input queue\n"); + vfree(queue); + return; + } + sequencer_ok = 1; +} + +void sequencer_unload(void) +{ + if(queue) + { + vfree(queue); + queue=NULL; + } + if(iqueue) + { + vfree(iqueue); + iqueue=NULL; + } +} diff -Nru a/sound/oss/sequencer_syms.c b/sound/oss/sequencer_syms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sequencer_syms.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,32 @@ +/* + * Exported symbols for sequencer driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include + +char sequencer_syms_symbol; + +#include "sound_config.h" +#include "sound_calls.h" + +EXPORT_SYMBOL(note_to_freq); +EXPORT_SYMBOL(compute_finetune); +EXPORT_SYMBOL(seq_copy_to_input); +EXPORT_SYMBOL(seq_input_event); +EXPORT_SYMBOL(sequencer_init); +EXPORT_SYMBOL(sequencer_timer); + +EXPORT_SYMBOL(sound_timer_init); +EXPORT_SYMBOL(sound_timer_interrupt); +EXPORT_SYMBOL(sound_timer_syncinterval); +EXPORT_SYMBOL(reprogram_timer); + +/* Tuning */ + +#define _SEQUENCER_C_ +#include "tuning.h" + +EXPORT_SYMBOL(cent_tuning); +EXPORT_SYMBOL(semitone_tuning); diff -Nru a/sound/oss/sgalaxy.c b/sound/oss/sgalaxy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sgalaxy.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,197 @@ +/* + * sound/sgalaxy.c + * + * Low level driver for Aztech Sound Galaxy cards. + * Copyright 1998 Artur Skawina + * + * Supported cards: + * Aztech Sound Galaxy Waverider Pro 32 - 3D + * Aztech Sound Galaxy Washington 16 + * + * Based on cs4232.c by Hannu Savolainen and Alan Cox. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added __init to sb_rst() and sb_cmd() + */ + +#include +#include + +#include "sound_config.h" +#include "ad1848.h" + +static void sleep( unsigned howlong ) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(howlong); +} + +#define DPORT 0x80 + +/* Sound Blaster regs */ + +#define SBDSP_RESET 0x6 +#define SBDSP_READ 0xA +#define SBDSP_COMMAND 0xC +#define SBDSP_STATUS SBDSP_COMMAND +#define SBDSP_DATA_AVAIL 0xE + +static int __init sb_rst(int base) +{ + int i; + + outb( 1, base+SBDSP_RESET ); /* reset the DSP */ + outb( 0, base+SBDSP_RESET ); + + for ( i=0; i<500; i++ ) /* delay */ + inb(DPORT); + + for ( i=0; i<100000; i++ ) + { + if ( inb( base+SBDSP_DATA_AVAIL )&0x80 ) + break; + } + + if ( inb( base+SBDSP_READ )!=0xAA ) + return 0; + + return 1; +} + +static int __init sb_cmd( int base, unsigned char val ) +{ + int i; + + for ( i=100000; i; i-- ) + { + if ( (inb( base+SBDSP_STATUS )&0x80)==0 ) + { + outb( val, base+SBDSP_COMMAND ); + break; + } + } + return i; /* i>0 == success */ +} + + +#define ai_sgbase driver_use_1 + +static int __init probe_sgalaxy( struct address_info *ai ) +{ + if ( check_region( ai->io_base, 8 ) ) { + printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); + return 0; + } + + if ( ad1848_detect( ai->io_base+4, NULL, ai->osp ) ) + return probe_ms_sound(ai); /* The card is already active, check irq etc... */ + + if ( check_region( ai->ai_sgbase, 0x10 ) ) { + printk(KERN_ERR "sgalaxy: SB IO port 0x%03x not available\n", ai->ai_sgbase); + return 0; + } + + /* switch to MSS/WSS mode */ + + sb_rst( ai->ai_sgbase ); + + sb_cmd( ai->ai_sgbase, 9 ); + sb_cmd( ai->ai_sgbase, 0 ); + + sleep( HZ/10 ); + + return probe_ms_sound(ai); +} + +static void __init attach_sgalaxy( struct address_info *ai ) +{ + int n; + + request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB" ); + + attach_ms_sound(ai, THIS_MODULE); + n=ai->slots[0]; + + if (n!=-1 && audio_devs[n]->mixer_dev != -1 ) { + AD1848_REROUTE( SOUND_MIXER_LINE1, SOUND_MIXER_LINE ); /* Line-in */ + AD1848_REROUTE( SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH ); /* FM+Wavetable*/ + AD1848_REROUTE( SOUND_MIXER_LINE3, SOUND_MIXER_CD ); /* CD */ + } +} + +static void __exit unload_sgalaxy( struct address_info *ai ) +{ + unload_ms_sound( ai ); + release_region( ai->ai_sgbase, 0x10 ); +} + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata sgbase = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(sgbase,"i"); + +static int __init init_sgalaxy(void) +{ + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + cfg.ai_sgbase = sgbase; + + if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.ai_sgbase == -1 ) { + printk(KERN_ERR "sgalaxy: io, irq, dma and sgbase must be set.\n"); + return -EINVAL; + } + + if ( probe_sgalaxy(&cfg) == 0 ) + return -ENODEV; + + attach_sgalaxy(&cfg); + + return 0; +} + +static void __exit cleanup_sgalaxy(void) +{ + unload_sgalaxy(&cfg); +} + +module_init(init_sgalaxy); +module_exit(cleanup_sgalaxy); + +#ifndef MODULE +static int __init setup_sgalaxy(char *str) +{ + /* io, irq, dma, dma2, sgbase */ + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + sgbase = ints[5]; + + return 1; +} + +__setup("sgalaxy=", setup_sgalaxy); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/skeleton.c b/sound/oss/skeleton.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/skeleton.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,223 @@ +/* + * PCI sound skeleton example + * + * (c) 1998 Red Hat Software + * + * This software may be used and distributed according to the + * terms of the GNU General Public License, incorporated herein by + * reference. + * + * This example is designed to be built in the linux/drivers/sound + * directory as part of a kernel build. The example is modular only + * drop me a note once you have a working modular driver and want + * to integrate it with the main code. + * -- Alan + * + * This is a first draft. Please report any errors, corrections or + * improvements to me. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "sound_config.h" + +/* + * Define our PCI vendor ID here + */ + +#ifndef PCI_VENDOR_MYIDENT +#define PCI_VENDOR_MYIDENT 0x125D + +/* + * PCI identity for the card. + */ + +#define PCI_DEVICE_ID_MYIDENT_MYCARD1 0x1969 +#endif + +#define CARD_NAME "ExampleWave 3D Pro Ultra ThingyWotsit" + +#define MAX_CARDS 8 + +/* + * Each address_info object holds the information about one of + * our card resources. In this case the MSS emulation of our + * ficticious card. Its used to manage and attach things. + */ + +static struct address_info mss_data[MAX_CARDS]; +static int cards = 0; + +/* + * Install the actual card. This is an example + */ + +static int mycard_install(struct pci_dev *pcidev) +{ + int iobase; + int mssbase; + int mpubase; + u8 x; + u16 w; + u32 v; + int i; + int dma; + + /* + * Our imaginary code has its I/O on PCI address 0, a + * MSS on PCI address 1 and an MPU on address 2 + * + * For the example we will only initialise the MSS + */ + + iobase = pci_resource_start(pcidev, 0); + mssbase = pci_resource_start(pcidev, 1); + mpubase = pci_resource_start(pcidev, 2); + + /* + * Reset the board + */ + + /* + * Wait for completion. udelay() waits in microseconds + */ + + udelay(100); + + /* + * Ok card ready. Begin setup proper. You might for example + * load the firmware here + */ + + dma = card_specific_magic(ioaddr); + + /* + * Turn on legacy mode (example), There are also byte and + * dword (32bit) PCI configuration function calls + */ + + pci_read_config_word(pcidev, 0x40, &w); + w&=~(1<<15); /* legacy decode on */ + w|=(1<<14); /* Reserved write as 1 in this case */ + w|=(1<<3)|(1<<1)|(1<<0); /* SB on , FM on, MPU on */ + pci_write_config_word(pcidev, 0x40, w); + + /* + * Let the user know we found his toy. + */ + + printk(KERN_INFO "Programmed "CARD_NAME" at 0x%X to legacy mode.\n", + iobase); + + /* + * Now set it up the description of the card + */ + + mss_data[cards].io_base = mssbase; + mss_data[cards].irq = pcidev->irq; + mss_data[cards].dma = dma; + + /* + * Check there is an MSS present + */ + + if(ad1848_detect(mssbase, NULL, mss_data[cards].osp)==0) + return 0; + + /* + * Initialize it + */ + + mss_data[cards].slots[3] = ad1848_init("MyCard MSS 16bit", + mssbase, + mss_data[cards].irq, + mss_data[cards].dma, + mss_data[cards].dma, + 0, + 0, + THIS_MODULE); + + cards++; + return 1; +} + + +/* + * This loop walks the PCI configuration database and finds where + * the sound cards are. + */ + +int init_mycard(void) +{ + struct pci_dev *pcidev=NULL; + int count=0; + + if(!pci_present()) + return -ENODEV; + + + while((pcidev = pci_find_device(PCI_VENDOR_MYIDENT, PCI_DEVICE_ID_MYIDENT_MYCARD1, pcidev))!=NULL) + { + if (pci_enable_device(pcidev)) + continue; + count+=mycard_install(pcidev); + if(count) + return 0; + if(count==MAX_CARDS) + break; + } + + if(count==0) + return -ENODEV; + return 0; +} + +/* + * This function is called when the user or kernel loads the + * module into memory. + */ + + +int init_module(void) +{ + if(init_mycard()<0) + { + printk(KERN_ERR "No "CARD_NAME" cards found.\n"); + return -ENODEV; + } + + return 0; +} + +/* + * This is called when it is removed. It will only be removed + * when its use count is 0. + */ + +void cleanup_module(void) +{ + for(i=0;i< cards; i++) + { + /* + * Free attached resources + */ + + ad1848_unload(mss_data[i].io_base, + mss_data[i].irq, + mss_data[i].dma, + mss_data[i].dma, + 0); + /* + * And disconnect the device from the kernel + */ + sound_unload_audiodevice(mss_data[i].slots[3]); + } +} + diff -Nru a/sound/oss/sonicvibes.c b/sound/oss/sonicvibes.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sonicvibes.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,2767 @@ +/*****************************************************************************/ + +/* + * sonicvibes.c -- S3 Sonic Vibes audio driver. + * + * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * 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. + * + * Special thanks to David C. Niemi + * + * + * Module command line parameters: + * none so far + * + * + * 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 + * + * The card has both an FM and a Wavetable synth, but I have to figure + * out first how to drive them... + * + * Revision history + * 06.05.1998 0.1 Initial release + * 10.05.1998 0.2 Fixed many bugs, esp. ADC rate calculation + * First stab at a simple midi interface (no bells&whistles) + * 13.05.1998 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of + * set_dac_rate in the FMODE_WRITE case in sv_open + * Fix hwptr out of bounds (now mpg123 works) + * 14.05.1998 0.4 Don't allow excessive interrupt rates + * 08.06.1998 0.5 First release using Alan Cox' soundcore instead of miscdevice + * 03.08.1998 0.6 Do not include modversions.h + * Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * 31.08.1998 0.7 Fix realplayer problems - dac.count issues + * 10.12.1998 0.8 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.1998 0.9 Fix a few f_file & FMODE_ bugs + * 06.01.1999 0.10 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.11 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 05.04.1999 0.13 added code to sv_read and sv_write which should detect + * lockups of the sound chip and revive it. This is basically + * an ugly hack, but at least applications using this driver + * won't hang forever. I don't know why these lockups happen, + * it might well be the motherboard chipset (an early 486 PCI + * board with ALI chipset), since every busmastering 100MB + * ethernet card I've tried (Realtek 8139 and Macronix tulip clone) + * exhibit similar behaviour (they work for a couple of packets + * and then lock up and can be revived by ifconfig down/up). + * 07.04.1999 0.14 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Note: dmaio hack might still be wrong on archs other than i386 + * 15.06.1999 0.15 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.16 Add pci_set_master + * 03.08.1999 0.17 adapt to Linus' new __setup/__initcall + * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" + * 12.08.1999 0.18 module_init/__setup fixes + * 24.08.1999 0.19 get rid of the dmaio kludge, replace with allocate_resource + * 31.08.1999 0.20 add spin_lock_init + * use new resource allocation to allocate DDMA IO space + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.21 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 28.10.1999 0.22 More waitqueue races fixed + * 01.12.1999 0.23 New argument to allocate_resource + * 07.12.1999 0.24 More allocate_resource semantics change + * 08.01.2000 0.25 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * use Martin Mares' pci_assign_resource + * 07.02.2000 0.26 Use pci_alloc_consistent and pci_register_driver + * 21.11.2000 0.27 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.28 More dma buffer initializations, patch from + * Tjeerd Mulder + * 31.01.2001 0.29 Register/Unregister gameport + * Fix SETTRIGGER non OSS API conformity + * 18.05.2001 0.30 PCI probing and error values cleaned up by Marcus + * Meissner + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" + + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_S3 +#define PCI_VENDOR_ID_S3 0x5333 +#endif +#ifndef PCI_DEVICE_ID_S3_SONICVIBES +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 +#endif + +#define SV_MAGIC ((PCI_VENDOR_ID_S3<<16)|PCI_DEVICE_ID_S3_SONICVIBES) + +#define SV_EXTENT_SB 0x10 +#define SV_EXTENT_ENH 0x10 +#define SV_EXTENT_SYNTH 0x4 +#define SV_EXTENT_MIDI 0x4 +#define SV_EXTENT_GAME 0x8 +#define SV_EXTENT_DMA 0x10 + +/* + * we are not a bridge and thus use a resource for DDMA that is used for bridges but + * left empty for normal devices + */ +#define RESOURCE_SB 0 +#define RESOURCE_ENH 1 +#define RESOURCE_SYNTH 2 +#define RESOURCE_MIDI 3 +#define RESOURCE_GAME 4 +#define RESOURCE_DDMA 7 + +#define SV_MIDI_DATA 0 +#define SV_MIDI_COMMAND 1 +#define SV_MIDI_STATUS 1 + +#define SV_DMA_ADDR0 0 +#define SV_DMA_ADDR1 1 +#define SV_DMA_ADDR2 2 +#define SV_DMA_ADDR3 3 +#define SV_DMA_COUNT0 4 +#define SV_DMA_COUNT1 5 +#define SV_DMA_COUNT2 6 +#define SV_DMA_MODE 0xb +#define SV_DMA_RESET 0xd +#define SV_DMA_MASK 0xf + +/* + * DONT reset the DMA controllers unless you understand + * the reset semantics. Assuming reset semantics as in + * the 8237 does not work. + */ + +#define DMA_MODE_AUTOINIT 0x10 +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ + +#define SV_CODEC_CONTROL 0 +#define SV_CODEC_INTMASK 1 +#define SV_CODEC_STATUS 2 +#define SV_CODEC_IADDR 4 +#define SV_CODEC_IDATA 5 + +#define SV_CCTRL_RESET 0x80 +#define SV_CCTRL_INTADRIVE 0x20 +#define SV_CCTRL_WAVETABLE 0x08 +#define SV_CCTRL_REVERB 0x04 +#define SV_CCTRL_ENHANCED 0x01 + +#define SV_CINTMASK_DMAA 0x01 +#define SV_CINTMASK_DMAC 0x04 +#define SV_CINTMASK_SPECIAL 0x08 +#define SV_CINTMASK_UPDOWN 0x40 +#define SV_CINTMASK_MIDI 0x80 + +#define SV_CSTAT_DMAA 0x01 +#define SV_CSTAT_DMAC 0x04 +#define SV_CSTAT_SPECIAL 0x08 +#define SV_CSTAT_UPDOWN 0x40 +#define SV_CSTAT_MIDI 0x80 + +#define SV_CIADDR_TRD 0x80 +#define SV_CIADDR_MCE 0x40 + +/* codec indirect registers */ +#define SV_CIMIX_ADCINL 0x00 +#define SV_CIMIX_ADCINR 0x01 +#define SV_CIMIX_AUX1INL 0x02 +#define SV_CIMIX_AUX1INR 0x03 +#define SV_CIMIX_CDINL 0x04 +#define SV_CIMIX_CDINR 0x05 +#define SV_CIMIX_LINEINL 0x06 +#define SV_CIMIX_LINEINR 0x07 +#define SV_CIMIX_MICIN 0x08 +#define SV_CIMIX_SYNTHINL 0x0A +#define SV_CIMIX_SYNTHINR 0x0B +#define SV_CIMIX_AUX2INL 0x0C +#define SV_CIMIX_AUX2INR 0x0D +#define SV_CIMIX_ANALOGINL 0x0E +#define SV_CIMIX_ANALOGINR 0x0F +#define SV_CIMIX_PCMINL 0x10 +#define SV_CIMIX_PCMINR 0x11 + +#define SV_CIGAMECONTROL 0x09 +#define SV_CIDATAFMT 0x12 +#define SV_CIENABLE 0x13 +#define SV_CIUPDOWN 0x14 +#define SV_CIREVISION 0x15 +#define SV_CIADCOUTPUT 0x16 +#define SV_CIDMAABASECOUNT1 0x18 +#define SV_CIDMAABASECOUNT0 0x19 +#define SV_CIDMACBASECOUNT1 0x1c +#define SV_CIDMACBASECOUNT0 0x1d +#define SV_CIPCMSR0 0x1e +#define SV_CIPCMSR1 0x1f +#define SV_CISYNTHSR0 0x20 +#define SV_CISYNTHSR1 0x21 +#define SV_CIADCCLKSOURCE 0x22 +#define SV_CIADCALTSR 0x23 +#define SV_CIADCPLLM 0x24 +#define SV_CIADCPLLN 0x25 +#define SV_CISYNTHPLLM 0x26 +#define SV_CISYNTHPLLN 0x27 +#define SV_CIUARTCONTROL 0x2a +#define SV_CIDRIVECONTROL 0x2b +#define SV_CISRSSPACE 0x2c +#define SV_CISRSCENTER 0x2d +#define SV_CIWAVETABLESRC 0x2e +#define SV_CIANALOGPWRDOWN 0x30 +#define SV_CIDIGITALPWRDOWN 0x31 + + +#define SV_CIMIX_ADCSRC_CD 0x20 +#define SV_CIMIX_ADCSRC_DAC 0x40 +#define SV_CIMIX_ADCSRC_AUX2 0x60 +#define SV_CIMIX_ADCSRC_LINE 0x80 +#define SV_CIMIX_ADCSRC_AUX1 0xa0 +#define SV_CIMIX_ADCSRC_MIC 0xc0 +#define SV_CIMIX_ADCSRC_MIXOUT 0xe0 +#define SV_CIMIX_ADCSRC_MASK 0xe0 + +#define SV_CFMT_STEREO 0x01 +#define SV_CFMT_16BIT 0x02 +#define SV_CFMT_MASK 0x03 +#define SV_CFMT_ASHIFT 0 +#define SV_CFMT_CSHIFT 4 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define SV_CENABLE_PPE 0x4 +#define SV_CENABLE_RE 0x2 +#define SV_CENABLE_PE 0x1 + + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 2 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +/* --------------------------------------------------------------------- */ + +struct sv_state { + /* magic */ + unsigned int magic; + + /* list of sonicvibes devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_midi; + int dev_dmfm; + + /* hardware resources */ + unsigned long iosb, ioenh, iosynth, iomidi; /* long for SPARC */ + unsigned int iodmaa, iodmac, irq; + + /* mixer stuff */ + struct { + unsigned int modcnt; +#ifndef OSS_DOCUMENTED_MIXER_SEMANTICS + unsigned short vol[13]; +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } mix; + + /* wave stuff */ + unsigned int rateadc, ratedac; + unsigned char fmt, enable; + + 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; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + 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 endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct gameport gameport; +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); +static unsigned long wavetable_mem = 0; + +/* --------------------------------------------------------------------- */ + +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; +} + +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ + +#ifdef hweight32 +#undef hweight32 +#endif + +static inline unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +/* --------------------------------------------------------------------- */ + +/* + * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver. + */ + +#undef DMABYTEIO + +static void set_dmaa(struct sv_state *s, unsigned int addr, unsigned int count) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmaa, u; + + count--; + for (u = 4; u > 0; u--, addr >>= 8, io++) + outb(addr & 0xff, io); + for (u = 3; u > 0; u--, count >>= 8, io++) + outb(count & 0xff, io); +#else /* DMABYTEIO */ + count--; + outl(addr, s->iodmaa + SV_DMA_ADDR0); + outl(count, s->iodmaa + SV_DMA_COUNT0); +#endif /* DMABYTEIO */ + outb(0x18, s->iodmaa + SV_DMA_MODE); +} + +static void set_dmac(struct sv_state *s, unsigned int addr, unsigned int count) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmac, u; + + count >>= 1; + count--; + for (u = 4; u > 0; u--, addr >>= 8, io++) + outb(addr & 0xff, io); + for (u = 3; u > 0; u--, count >>= 8, io++) + outb(count & 0xff, io); +#else /* DMABYTEIO */ + count >>= 1; + count--; + outl(addr, s->iodmac + SV_DMA_ADDR0); + outl(count, s->iodmac + SV_DMA_COUNT0); +#endif /* DMABYTEIO */ + outb(0x14, s->iodmac + SV_DMA_MODE); +} + +static inline unsigned get_dmaa(struct sv_state *s) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmaa+6, v = 0, u; + + for (u = 3; u > 0; u--, io--) { + v <<= 8; + v |= inb(io); + } + return v + 1; +#else /* DMABYTEIO */ + return (inl(s->iodmaa + SV_DMA_COUNT0) & 0xffffff) + 1; +#endif /* DMABYTEIO */ +} + +static inline unsigned get_dmac(struct sv_state *s) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmac+6, v = 0, u; + + for (u = 3; u > 0; u--, io--) { + v <<= 8; + v |= inb(io); + } + return (v + 1) << 1; +#else /* DMABYTEIO */ + return ((inl(s->iodmac + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; +#endif /* DMABYTEIO */ +} + +static void wrindir(struct sv_state *s, unsigned char idx, unsigned char data) +{ + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(data, s->ioenh + SV_CODEC_IDATA); + udelay(10); +} + +static unsigned char rdindir(struct sv_state *s, unsigned char idx) +{ + unsigned char v; + + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + v = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + return v; +} + +static void set_fmt(struct sv_state *s, unsigned char mask, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + outb(SV_CIDATAFMT | SV_CIADDR_MCE, s->ioenh + SV_CODEC_IADDR); + if (mask) { + s->fmt = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + } + s->fmt = (s->fmt & mask) | data; + outb(s->fmt, s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(0, s->ioenh + SV_CODEC_IADDR); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); +} + +static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, unsigned char data) +{ + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb((inb(s->ioenh + SV_CODEC_IDATA) & mask) ^ data, s->ioenh + SV_CODEC_IDATA); + udelay(10); +} + +#define REFFREQUENCY 24576000 +#define ADCMULT 512 +#define FULLRATE 48000 + +static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate) +{ + unsigned long flags; + unsigned char r, m=0, n=0; + unsigned xm, xn, xr, xd, metric = ~0U; + /* the warnings about m and n used uninitialized are bogus and may safely be ignored */ + + if (rate < 625000/ADCMULT) + rate = 625000/ADCMULT; + if (rate > 150000000/ADCMULT) + rate = 150000000/ADCMULT; + /* slight violation of specs, needed for continuous sampling rates */ + for (r = 0; rate < 75000000/ADCMULT; r += 0x20, rate <<= 1); + for (xn = 3; xn < 35; xn++) + for (xm = 3; xm < 130; xm++) { + xr = REFFREQUENCY/ADCMULT * xm / xn; + xd = abs((signed)(xr - rate)); + if (xd < metric) { + metric = xd; + m = xm - 2; + n = xn - 2; + } + } + reg &= 0x3f; + spin_lock_irqsave(&s->lock, flags); + outb(reg, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(m, s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(reg+1, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(r | n, s->ioenh + SV_CODEC_IDATA); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); + return (REFFREQUENCY/ADCMULT * (m + 2) / (n + 2)) >> ((r >> 5) & 7); +} + +#if 0 + +static unsigned getpll(struct sv_state *s, unsigned char reg) +{ + unsigned long flags; + unsigned char m, n; + + reg &= 0x3f; + spin_lock_irqsave(&s->lock, flags); + outb(reg, s->ioenh + SV_CODEC_IADDR); + udelay(10); + m = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(reg+1, s->ioenh + SV_CODEC_IADDR); + udelay(10); + n = inb(s->ioenh + SV_CODEC_IDATA); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); + return (REFFREQUENCY/ADCMULT * (m + 2) / ((n & 0x1f) + 2)) >> ((n >> 5) & 7); +} + +#endif + +static void set_dac_rate(struct sv_state *s, unsigned rate) +{ + unsigned div; + unsigned long flags; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + div = (rate * 65536 + FULLRATE/2) / FULLRATE; + if (div > 65535) + div = 65535; + spin_lock_irqsave(&s->lock, flags); + wrindir(s, SV_CIPCMSR1, div >> 8); + wrindir(s, SV_CIPCMSR0, div); + spin_unlock_irqrestore(&s->lock, flags); + s->ratedac = (div * FULLRATE + 32768) / 65536; +} + +static void set_adc_rate(struct sv_state *s, unsigned rate) +{ + unsigned long flags; + unsigned rate1, rate2, div; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + rate1 = setpll(s, SV_CIADCPLLM, rate); + div = (48000 + rate/2) / rate; + if (div > 8) + div = 8; + rate2 = (48000 + div/2) / div; + spin_lock_irqsave(&s->lock, flags); + wrindir(s, SV_CIADCALTSR, (div-1) << 4); + if (abs((signed)(rate-rate2)) <= abs((signed)(rate-rate1))) { + wrindir(s, SV_CIADCCLKSOURCE, 0x10); + s->rateadc = rate2; + } else { + wrindir(s, SV_CIADCCLKSOURCE, 0x00); + s->rateadc = rate1; + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_adc(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->enable &= ~SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->enable &= ~(SV_CENABLE_PPE | SV_CENABLE_PE); + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + s->enable = (s->enable & ~SV_CENABLE_PPE) | SV_CENABLE_PE; + wrindir(s, SV_CIENABLE, s->enable); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->enable |= SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static void dealloc_dmabuf(struct sv_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); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + + +/* DMAA is used for playback, DMAC is used for recording */ + +static int prog_dmabuf(struct sv_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + fmt = s->fmt; + if (rec) { + s->enable &= ~SV_CENABLE_RE; + fmt >>= SV_CFMT_CSHIFT; + } else { + s->enable &= ~SV_CENABLE_PE; + fmt >>= SV_CFMT_ASHIFT; + } + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); + fmt &= SV_CFMT_MASK; + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + 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; + if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) + printk(KERN_DEBUG "sv: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", + virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) + printk(KERN_DEBUG "sv: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", + virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + /* 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); + } + 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--; + 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 & SV_CFMT_16BIT) ? 0 : 0x80, db->dmasize); + spin_lock_irqsave(&s->lock, flags); + if (rec) { + set_dmac(s, db->dmaaddr, db->numfrag << db->fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMACBASECOUNT1, (db->fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, db->fragsamples-1); + } else { + set_dmaa(s, db->dmaaddr, db->numfrag << db->fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMAABASECOUNT1, (db->fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, db->fragsamples-1); + } + spin_unlock_irqrestore(&s->lock, flags); + db->enabled = 1; + db->ready = 1; + return 0; +} + +static inline void clear_advance(struct sv_state *s) +{ + unsigned char c = (s->fmt & (SV_CFMT_16BIT << SV_CFMT_ASHIFT)) ? 0 : 0x80; + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void sv_update_ptr(struct sv_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + hwptr = (s->dma_adc.dmasize - get_dmac(s)) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->enable &= ~SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = (s->dma_dac.dmasize - get_dmaa(s)) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + s->enable &= ~SV_CENABLE_PE; + wrindir(s, SV_CIENABLE, s->enable); + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +/* hold spinlock for the following! */ +static void sv_handle_midi(struct sv_state *s) +{ + unsigned char ch; + int wake; + + wake = 0; + while (!(inb(s->iomidi+1) & 0x80)) { + ch = inb(s->iomidi); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->iomidi); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + +static void sv_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sv_state *s = (struct sv_state *)dev_id; + unsigned int intsrc; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inb(s->ioenh + SV_CODEC_STATUS); + if (!(intsrc & (SV_CSTAT_DMAA | SV_CSTAT_DMAC | SV_CSTAT_MIDI))) + return; + spin_lock(&s->lock); + sv_update_ptr(s); + sv_handle_midi(s); + spin_unlock(&s->lock); +} + +static void sv_midi_timer(unsigned long data) +{ + struct sv_state *s = (struct sv_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "sv: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != SV_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +#define MT_4 1 +#define MT_5MUTE 2 +#define MT_4MUTEMONO 3 +#define MT_6MUTE 4 + +static const struct { + unsigned left:5; + unsigned right:5; + unsigned type:3; + unsigned rec:3; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_RECLEV] = { SV_CIMIX_ADCINL, SV_CIMIX_ADCINR, MT_4, 0 }, + [SOUND_MIXER_LINE1] = { SV_CIMIX_AUX1INL, SV_CIMIX_AUX1INR, MT_5MUTE, 5 }, + [SOUND_MIXER_CD] = { SV_CIMIX_CDINL, SV_CIMIX_CDINR, MT_5MUTE, 1 }, + [SOUND_MIXER_LINE] = { SV_CIMIX_LINEINL, SV_CIMIX_LINEINR, MT_5MUTE, 4 }, + [SOUND_MIXER_MIC] = { SV_CIMIX_MICIN, SV_CIMIX_ADCINL, MT_4MUTEMONO, 6 }, + [SOUND_MIXER_SYNTH] = { SV_CIMIX_SYNTHINL, SV_CIMIX_SYNTHINR, MT_5MUTE, 2 }, + [SOUND_MIXER_LINE2] = { SV_CIMIX_AUX2INL, SV_CIMIX_AUX2INR, MT_5MUTE, 3 }, + [SOUND_MIXER_VOLUME] = { SV_CIMIX_ANALOGINL, SV_CIMIX_ANALOGINR, MT_5MUTE, 7 }, + [SOUND_MIXER_PCM] = { SV_CIMIX_PCMINL, SV_CIMIX_PCMINR, MT_6MUTE, 0 } +}; + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + +static int return_mixval(struct sv_state *s, unsigned i, int *arg) +{ + unsigned long flags; + unsigned char l, r, rl, rr; + + spin_lock_irqsave(&s->lock, flags); + l = rdindir(s, mixtable[i].left); + r = rdindir(s, mixtable[i].right); + spin_unlock_irqrestore(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + r &= 0xf; + l &= 0xf; + rl = 10 + 6 * (l & 15); + rr = 10 + 6 * (r & 15); + break; + + case MT_4MUTEMONO: + rl = 55 - 3 * (l & 15); + if (r & 0x10) + rl += 45; + rr = rl; + r = l; + break; + + case MT_5MUTE: + default: + rl = 100 - 3 * (l & 31); + rr = 100 - 3 * (r & 31); + break; + + case MT_6MUTE: + rl = 100 - 3 * (l & 63) / 2; + rr = 100 - 3 * (r & 63) / 2; + break; + } + if (l & 0x80) + rl = 0; + if (r & 0x80) + rr = 0; + return put_user((rr << 8) | rl, arg); +} + +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + +static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = +{ + [SOUND_MIXER_RECLEV] = 1, + [SOUND_MIXER_LINE1] = 2, + [SOUND_MIXER_CD] = 3, + [SOUND_MIXER_LINE] = 4, + [SOUND_MIXER_MIC] = 5, + [SOUND_MIXER_SYNTH] = 6, + [SOUND_MIXER_LINE2] = 7, + [SOUND_MIXER_VOLUME] = 8, + [SOUND_MIXER_PCM] = 9 +}; + +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + +static unsigned mixer_recmask(struct sv_state *s) +{ + unsigned long flags; + int i, j; + + spin_lock_irqsave(&s->lock, flags); + j = rdindir(s, SV_CIMIX_ADCINL) >> 5; + spin_unlock_irqrestore(&s->lock, flags); + j &= 7; + for (i = 0; i < SOUND_MIXER_NRDEVICES && mixtable[i].rec != j; i++); + return 1 << i; +} + +static int mixer_ioctl(struct sv_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val; + unsigned char l, r, rl, rr; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + 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; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + 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; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (cmd == SOUND_MIXER_PRIVATE1) { /* SRS settings */ + if (get_user(val, (int *)arg)) + return -EFAULT; + spin_lock_irqsave(&s->lock, flags); + if (val & 1) { + if (val & 2) { + l = 4 - ((val >> 2) & 7); + if (l & ~3) + l = 4; + r = 4 - ((val >> 5) & 7); + if (r & ~3) + r = 4; + wrindir(s, SV_CISRSSPACE, l); + wrindir(s, SV_CISRSCENTER, r); + } else + wrindir(s, SV_CISRSSPACE, 0x80); + } + l = rdindir(s, SV_CISRSSPACE); + r = rdindir(s, SV_CISRSCENTER); + spin_unlock_irqrestore(&s->lock, flags); + if (l & 0x80) + return put_user(0, (int *)arg); + return put_user(((4 - (l & 7)) << 2) | ((4 - (r & 7)) << 5) | 2, (int *)arg); + } + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_recmask(s), (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].rec) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) + val |= 1 << i; + return put_user(val, (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 || !mixtable[i].type) + return -EINVAL; +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + return return_mixval(s, i, (int *)arg); +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + if (!volidx[i]) + return -EINVAL; + return put_user(s->mix.vol[volidx[i]-1], (int *)arg); +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } + } + if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + 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); + if (i == 0) + return 0; /*val = mixer_recmask(s);*/ + else if (i > 1) + val &= ~mixer_recmask(s); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (mixtable[i].rec) + break; + } + if (!mixtable[i].rec) + return 0; + spin_lock_irqsave(&s->lock, flags); + frobindir(s, SV_CIMIX_ADCINL, 0x1f, mixtable[i].rec << 5); + frobindir(s, SV_CIMIX_ADCINR, 0x1f, mixtable[i].rec << 5); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + l = val & 0xff; + r = (val >> 8) & 0xff; + if (mixtable[i].type == MT_4MUTEMONO) + l = (r + l) / 2; + if (l > 100) + l = 100; + if (r > 100) + r = 100; + spin_lock_irqsave(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + if (l >= 10) + l -= 10; + if (r >= 10) + r -= 10; + frobindir(s, mixtable[i].left, 0xf0, l / 6); + frobindir(s, mixtable[i].right, 0xf0, l / 6); + break; + + case MT_4MUTEMONO: + rr = 0; + if (l < 10) + rl = 0x80; + else { + if (l >= 55) { + rr = 0x10; + l -= 45; + } + rl = (55 - l) / 3; + } + wrindir(s, mixtable[i].left, rl); + frobindir(s, mixtable[i].right, ~0x10, rr); + break; + + case MT_5MUTE: + if (l < 7) + rl = 0x80; + else + rl = (100 - l) / 3; + if (r < 7) + rr = 0x80; + else + rr = (100 - r) / 3; + wrindir(s, mixtable[i].left, rl); + wrindir(s, mixtable[i].right, rr); + break; + + case MT_6MUTE: + if (l < 6) + rl = 0x80; + else + rl = (100 - l) * 2 / 3; + if (r < 6) + rr = 0x80; + else + rr = (100 - r) * 2 / 3; + wrindir(s, mixtable[i].left, rl); + wrindir(s, mixtable[i].right, rr); + break; + } + spin_unlock_irqrestore(&s->lock, flags); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + return return_mixval(s, i, (int *)arg); +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + if (!volidx[i]) + return -EINVAL; + s->mix.vol[volidx[i]-1] = val; + return put_user(s->mix.vol[volidx[i]-1], (int *)arg); +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } +} + +/* --------------------------------------------------------------------- */ + +static int sv_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (s->dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + return 0; +} + +static int sv_release_mixdev(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int sv_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct sv_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations sv_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: sv_ioctl_mixdev, + open: sv_open_mixdev, + release: sv_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct sv_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + 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) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; + tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "sv: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + 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(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; +#if 0 + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); +#endif + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (!schedule_timeout(HZ)) { + printk(KERN_DEBUG "sv: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + spin_lock_irqsave(&s->lock, flags); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); + } + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t sv_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + 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(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; +#if 0 + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); +#endif + add_wait_queue(&s->dma_dac.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac.enabled) + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (!schedule_timeout(HZ)) { + printk(KERN_DEBUG "sv: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + spin_lock_irqsave(&s->lock, flags); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac.enabled) + start_dac(s); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int sv_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf(s, 1)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf(s, 0)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + 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 sv_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 1)) != 0) + goto out; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + goto out; + db = &s->dma_adc; + } else + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int sv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret; + unsigned char fmtm, fmtd; + + 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: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + 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: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + 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); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) + : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (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) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) + : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->enable & SV_CENABLE_RE) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->enable & SV_CENABLE_PE) + 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(s, 1))) + return ret; + s->dma_adc.enabled = 1; + start_adc(s); + } else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + s->dma_dac.enabled = 1; + start_dac(s); + } else { + s->dma_dac.enabled = 0; + 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(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + count = s->dma_dac.count; + 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; + 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(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + count = s->dma_adc.count; + if (count < 0) + count = 0; + abinfo.bytes = 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 && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + count = s->dma_dac.count; + 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; + if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + 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 && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (count < 0) + count = 0; + cinfo.blocks = 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(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + 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) { + 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 (file->f_mode & FMODE_WRITE) { + 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; + } + 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) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) + : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) + : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? 16 : 8, (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 sv_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned char fmtm = ~0, fmts = 0; + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + 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); + } + if (file->f_mode & FMODE_READ) { + fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_CSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_ASHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + s->dma_dac.enabled = 1; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + return 0; +} + +static int sv_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + + VALIDATE_STATE(s); + 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); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations sv_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: sv_read, + write: sv_write, + poll: sv_poll, + ioctl: sv_ioctl, + mmap: sv_mmap, + open: sv_open, + release: sv_release, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + sv_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int sv_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int sv_midi_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 sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (s->dev_midi == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + 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); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + //outb(inb(s->ioenh + SV_CODEC_CONTROL) | SV_CCTRL_WAVETABLE, s->ioenh + SV_CODEC_CONTROL); + outb(inb(s->ioenh + SV_CODEC_INTMASK) | SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); + wrindir(s, SV_CIUARTCONTROL, 5); /* output MIDI data to external and internal synth */ + wrindir(s, SV_CIWAVETABLESRC, 1); /* Wavetable in PC RAM */ + outb(0xff, s->iomidi+1); /* reset command */ + outb(0x3f, s->iomidi+1); /* uart command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = sv_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + return 0; +} + +static int sv_midi_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + 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; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "sv: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + outb(inb(s->ioenh + SV_CODEC_INTMASK) & ~SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations sv_midi_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: sv_midi_read, + write: sv_midi_write, + poll: sv_midi_poll, + open: sv_midi_open, + release: sv_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +static int sv_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct sv_state *s = (struct sv_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->iosynth + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void *)arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->iosynth); + outb((p.kbd_split & 1) << 6, s->iosynth+1); + outb(0xbd, s->iosynth); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->iosynth+2); + outb(arg, s->iosynth+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->iosynth+2); + outb(arg & 1, s->iosynth+3); + return 0; + + default: + return -EINVAL; + } +} + +static int sv_dmfm_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (s->dev_dmfm == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DMFM) { + 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); + } + /* init the stuff */ + outb(1, s->iosynth); + outb(0x20, s->iosynth+1); /* enable waveforms */ + outb(4, s->iosynth+2); + outb(0, s->iosynth+3); /* no 4op enabled */ + outb(5, s->iosynth+2); + outb(1, s->iosynth+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + up(&s->open_sem); + return 0; +} + +static int sv_dmfm_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + lock_kernel(); + down(&s->open_sem); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations sv_dmfm_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: sv_dmfm_ioctl, + open: sv_dmfm_open, + release: sv_dmfm_release, +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int reverb[NR_DEVICE] = { 0, }; + +#if 0 +static int wavetable[NR_DEVICE] = { 0, }; +#endif + +static unsigned int devindex = 0; + +MODULE_PARM(reverb, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(reverb, "if 1 enables the reverb circuitry. NOTE: your card must have the reverb RAM"); +#if 0 +MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled"); +#endif + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("S3 SonicVibes Driver"); +MODULE_LICENSE("GPL"); + + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 } +}; + +#define RSRCISIOREGION(dev,num) (pci_resource_start((dev), (num)) != 0 && \ + (pci_resource_flags((dev), (num)) & IORESOURCE_IO)) + +static int __devinit sv_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + static char __initdata sv_ddma_name[] = "S3 Inc. SonicVibes DDMA Controller"; + struct sv_state *s; + mm_segment_t fs; + int i, val, ret; + char *ddmaname; + unsigned ddmanamelen; + + if ((ret=pci_enable_device(pcidev))) + return ret; + + if (!RSRCISIOREGION(pcidev, RESOURCE_SB) || + !RSRCISIOREGION(pcidev, RESOURCE_ENH) || + !RSRCISIOREGION(pcidev, RESOURCE_SYNTH) || + !RSRCISIOREGION(pcidev, RESOURCE_MIDI) || + !RSRCISIOREGION(pcidev, RESOURCE_GAME)) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + if (pci_set_dma_mask(pcidev, 0x00ffffff)) { + printk(KERN_WARNING "sonicvibes: architecture does not support 24bit PCI busmaster DMA\n"); + return -ENODEV; + } + /* try to allocate a DDMA resource if not already available */ + if (!RSRCISIOREGION(pcidev, RESOURCE_DDMA)) { + pcidev->resource[RESOURCE_DDMA].start = 0; + pcidev->resource[RESOURCE_DDMA].end = 2*SV_EXTENT_DMA-1; + pcidev->resource[RESOURCE_DDMA].flags = PCI_BASE_ADDRESS_SPACE_IO | IORESOURCE_IO; + ddmanamelen = strlen(sv_ddma_name)+1; + if (!(ddmaname = kmalloc(ddmanamelen, GFP_KERNEL))) + return -1; + memcpy(ddmaname, sv_ddma_name, ddmanamelen); + pcidev->resource[RESOURCE_DDMA].name = ddmaname; + if (pci_assign_resource(pcidev, RESOURCE_DDMA)) { + pcidev->resource[RESOURCE_DDMA].name = NULL; + kfree(ddmaname); + printk(KERN_ERR "sv: cannot allocate DDMA controller io ports\n"); + return -EBUSY; + } + } + if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) { + printk(KERN_WARNING "sv: out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct sv_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = SV_MAGIC; + s->dev = pcidev; + s->iosb = pci_resource_start(pcidev, RESOURCE_SB); + s->ioenh = pci_resource_start(pcidev, RESOURCE_ENH); + s->iosynth = pci_resource_start(pcidev, RESOURCE_SYNTH); + s->iomidi = pci_resource_start(pcidev, RESOURCE_MIDI); + s->iodmaa = pci_resource_start(pcidev, RESOURCE_DDMA); + s->iodmac = pci_resource_start(pcidev, RESOURCE_DDMA) + SV_EXTENT_DMA; + s->gameport.io = pci_resource_start(pcidev, RESOURCE_GAME); + pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9); /* enable and use extended mode */ + pci_write_config_dword(pcidev, 0x48, s->iodmac | 9); /* enable */ + printk(KERN_DEBUG "sv: io ports: %#lx %#lx %#lx %#lx %#x %#x %#x\n", + s->iosb, s->ioenh, s->iosynth, s->iomidi, s->gameport.io, s->iodmaa, s->iodmac); + s->irq = pcidev->irq; + + /* hack */ + pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12); /* wavetable base address */ + + ret = -EBUSY; + if (!request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM")) { + printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1); + goto err_region5; + } + if (!request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA")) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1); + goto err_region4; + } + if (!request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC")) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1); + goto err_region3; + } + if (!request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi")) { + printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1); + goto err_region2; + } + if (!request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth")) { + printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1); + goto err_region1; + } + if (s->gameport.io && !request_region(s->gameport.io, SV_EXTENT_GAME, "ESS Solo1")) { + printk(KERN_ERR "sv: gameport io ports in use\n"); + s->gameport.io = 0; + } + /* initialize codec registers */ + outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */ + udelay(50); + outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */ + udelay(50); + outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE */ + | (reverb[devindex] ? SV_CCTRL_REVERB : 0), s->ioenh + SV_CODEC_CONTROL); + inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */ + wrindir(s, SV_CIDRIVECONTROL, 0); /* drive current 16mA */ + wrindir(s, SV_CIENABLE, s->enable = 0); /* disable DMAA and DMAC */ + outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK); + /* outb(0xff, s->iodmaa + SV_DMA_RESET); */ + /* outb(0xff, s->iodmac + SV_DMA_RESET); */ + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */ + wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */ + wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */ + setpll(s, SV_CIADCPLLM, 8000); + wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */ + wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff); + wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff); + wrindir(s, SV_CIADCOUTPUT, 0); + /* request irq */ + if ((ret=request_irq(s->irq,sv_interrupt,SA_SHIRQ,"S3 SonicVibes",s))) { + printk(KERN_ERR "sv: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n", + s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION)); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0) { + ret = s->dev_audio; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0) { + ret = s->dev_midi; + goto err_dev3; + } + if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) { + ret = s->dev_dmfm; + goto err_dev4; + } + pci_set_master(pcidev); /* enable bus mastering */ + /* initialize the chips */ + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH; + 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); + } + set_fs(fs); + /* register gameport */ + gameport_register_port(&s->gameport); + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_dev4: + unregister_sound_midi(s->dev_midi); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "sv: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + if (s->gameport.io) + release_region(s->gameport.io, SV_EXTENT_GAME); + release_region(s->iosynth, SV_EXTENT_SYNTH); + err_region1: + release_region(s->iomidi, SV_EXTENT_MIDI); + err_region2: + release_region(s->iodmac, SV_EXTENT_DMA); + err_region3: + release_region(s->iodmaa, SV_EXTENT_DMA); + err_region4: + release_region(s->ioenh, SV_EXTENT_ENH); + err_region5: + kfree(s); + return ret; +} + +static void __devinit sv_remove(struct pci_dev *dev) +{ + struct sv_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); + outb(~0, s->ioenh + SV_CODEC_INTMASK); /* disable ints */ + synchronize_irq(); + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIENABLE, 0); /* disable DMAA and DMAC */ + /*outb(0, s->iodmaa + SV_DMA_RESET);*/ + /*outb(0, s->iodmac + SV_DMA_RESET);*/ + free_irq(s->irq, s); + if (s->gameport.io) { + gameport_unregister_port(&s->gameport); + release_region(s->gameport.io, SV_EXTENT_GAME); + } + release_region(s->iodmac, SV_EXTENT_DMA); + release_region(s->iodmaa, SV_EXTENT_DMA); + release_region(s->ioenh, SV_EXTENT_ENH); + release_region(s->iomidi, SV_EXTENT_MIDI); + release_region(s->iosynth, SV_EXTENT_SYNTH); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + unregister_sound_special(s->dev_dmfm); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver sv_driver = { + name: "sonicvibes", + id_table: id_table, + probe: sv_probe, + remove: sv_remove +}; + +static int __init init_sonicvibes(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "sv: version v0.30 time " __TIME__ " " __DATE__ "\n"); +#if 0 + if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) + printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); +#endif + return pci_module_init(&sv_driver); +} + +static void __exit cleanup_sonicvibes(void) +{ + printk(KERN_INFO "sv: unloading\n"); + pci_unregister_driver(&sv_driver); + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); +} + +module_init(init_sonicvibes); +module_exit(cleanup_sonicvibes); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +/* format is: sonicvibes=[reverb] sonicvibesdmaio=dmaioaddr */ + +static int __init sonicvibes_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; +#if 0 + if (get_option(&str, &reverb[nr_dev]) == 2) + (void)get_option(&str, &wavetable[nr_dev]); +#else + (void)get_option(&str, &reverb[nr_dev]); +#endif + + nr_dev++; + return 1; +} + +__setup("sonicvibes=", sonicvibes_setup); + +#endif /* MODULE */ diff -Nru a/sound/oss/sound_calls.h b/sound/oss/sound_calls.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sound_calls.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,91 @@ +/* + * DMA buffer calls + */ + +int DMAbuf_open(int dev, int mode); +int DMAbuf_release(int dev, int mode); +int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock); +int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock); +int DMAbuf_rmchars(int dev, int buff_no, int c); +int DMAbuf_start_output(int dev, int buff_no, int l); +int DMAbuf_move_wrpointer(int dev, int l); +/* int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local); */ +void DMAbuf_init(int dev, int dma1, int dma2); +void DMAbuf_deinit(int dev); +int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); +int DMAbuf_open_dma (int dev); +void DMAbuf_close_dma (int dev); +void DMAbuf_inputintr(int dev); +void DMAbuf_outputintr(int dev, int underflow_flag); +struct dma_buffparms; +int DMAbuf_space_in_queue (int dev); +int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap); +int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction); +void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap); +unsigned int DMAbuf_poll(struct file *file, int dev, poll_table *wait); +void DMAbuf_start_devices(unsigned int devmask); +void DMAbuf_reset (int dev); +int DMAbuf_sync (int dev); + +/* + * System calls for /dev/dsp and /dev/audio (audio.c) + */ + +int audio_read (int dev, struct file *file, char *buf, int count); +int audio_write (int dev, struct file *file, const char *buf, int count); +int audio_open (int dev, struct file *file); +void audio_release (int dev, struct file *file); +int audio_ioctl (int dev, struct file *file, + unsigned int cmd, caddr_t arg); +void audio_init_devices (void); +void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording); +int dma_ioctl (int dev, unsigned int cmd, caddr_t arg); + +/* + * System calls for the /dev/sequencer + */ + +int sequencer_read (int dev, struct file *file, char *buf, int count); +int sequencer_write (int dev, struct file *file, const char *buf, int count); +int sequencer_open (int dev, struct file *file); +void sequencer_release (int dev, struct file *file); +int sequencer_ioctl (int dev, struct file *file, unsigned int cmd, caddr_t arg); +unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait); + +void sequencer_init (void); +void sequencer_unload (void); +void sequencer_timer(unsigned long dummy); +int note_to_freq(int note_num); +unsigned long compute_finetune(unsigned long base_freq, int bend, int range, + int vibrato_bend); +void seq_input_event(unsigned char *event, int len); +void seq_copy_to_input (unsigned char *event, int len); + +/* + * System calls for the /dev/midi + */ + +int MIDIbuf_read (int dev, struct file *file, char *buf, int count); +int MIDIbuf_write (int dev, struct file *file, const char *buf, int count); +int MIDIbuf_open (int dev, struct file *file); +void MIDIbuf_release (int dev, struct file *file); +int MIDIbuf_ioctl (int dev, struct file *file, unsigned int cmd, caddr_t arg); +unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait); +int MIDIbuf_avail(int dev); + +void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count); +void MIDIbuf_init(void); + + +/* From soundcard.c */ +void request_sound_timer (int count); +void sound_stop_timer(void); +void conf_printf(char *name, struct address_info *hw_config); +void conf_printf2(char *name, int base, int irq, int dma, int dma2); + +/* From sound_timer.c */ +void sound_timer_interrupt(void); +void sound_timer_syncinterval(unsigned int new_usecs); + +/* From midi_synth.c */ +void do_midi_msg (int synthno, unsigned char *msg, int mlen); diff -Nru a/sound/oss/sound_config.h b/sound/oss/sound_config.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sound_config.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,154 @@ +/* sound_config.h + * + * A driver for sound cards, misc. configuration parameters. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + + +#ifndef _SOUND_CONFIG_H_ +#define _SOUND_CONFIG_H_ + +#include +#include +#include + +#include "os.h" +#include "soundvers.h" + + +#ifndef SND_DEFAULT_ENABLE +#define SND_DEFAULT_ENABLE 1 +#endif + +#ifndef MAX_REALTIME_FACTOR +#define MAX_REALTIME_FACTOR 4 +#endif + +/* + * Use always 64k buffer size. There is no reason to use shorter. + */ +#undef DSP_BUFFSIZE +#define DSP_BUFFSIZE (64*1024) + +#ifndef DSP_BUFFCOUNT +#define DSP_BUFFCOUNT 1 /* 1 is recommended. */ +#endif + +#define FM_MONO 0x388 /* This is the I/O address used by AdLib */ + +#ifndef CONFIG_PAS_BASE +#define CONFIG_PAS_BASE 0x388 +#endif + +/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the + driver. (There is no need to alter this) */ +#define SEQ_MAX_QUEUE 1024 + +#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */ +/* 128 instruments for general MIDI setup and 16 unassigned */ + +#define SND_NDEVS 256 /* Number of supported devices */ + +#define DSP_DEFAULT_SPEED 8000 + +#define MAX_AUDIO_DEV 5 +#define MAX_MIXER_DEV 5 +#define MAX_SYNTH_DEV 5 +#define MAX_MIDI_DEV 6 +#define MAX_TIMER_DEV 4 + +struct address_info { + int io_base; + int irq; + int dma; + int dma2; + int always_detect; /* 1=Trust me, it's there */ + char *name; + int driver_use_1; /* Driver defined field 1 */ + int driver_use_2; /* Driver defined field 2 */ + int *osp; /* OS specific info */ + int card_subtype; /* Driver specific. Usually 0 */ + void *memptr; /* Module memory chainer */ + int slots[6]; /* To remember driver slot ids */ +}; + +#define SYNTH_MAX_VOICES 32 + +struct voice_alloc_info { + int max_voice; + int used_voices; + int ptr; /* For device specific use */ + unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */ + int timestamp; + int alloc_times[SYNTH_MAX_VOICES]; + }; + +struct channel_info { + int pgm_num; + int bender_value; + int bender_range; + unsigned char controllers[128]; + }; + +/* + * Process wakeup reasons + */ +#define WK_NONE 0x00 +#define WK_WAKEUP 0x01 +#define WK_TIMEOUT 0x02 +#define WK_SIGNAL 0x04 +#define WK_SLEEP 0x08 +#define WK_SELECT 0x10 +#define WK_ABORT 0x20 + +#define OPEN_READ PCM_ENABLE_INPUT +#define OPEN_WRITE PCM_ENABLE_OUTPUT +#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE) + +#if OPEN_READ == FMODE_READ && OPEN_WRITE == FMODE_WRITE + +extern __inline__ int translate_mode(struct file *file) +{ + return file->f_mode; +} + +#else + +extern __inline__ int translate_mode(struct file *file) +{ + return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) | + ((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0); +} + +#endif + + +#include "sound_calls.h" +#include "dev_table.h" + +#ifndef DEB +#define DEB(x) +#endif + +#ifndef DDB +#define DDB(x) {} +#endif + +#ifndef MDB +#ifdef MODULE +#define MDB(x) x +#else +#define MDB(x) +#endif +#endif + +#define TIMER_ARMED 121234 +#define TIMER_NOT_ARMED 1 + +#endif diff -Nru a/sound/oss/sound_firmware.h b/sound/oss/sound_firmware.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sound_firmware.h Tue Feb 19 18:08:58 2002 @@ -0,0 +1,2 @@ +extern int mod_firmware_load(const char *fn, char **fp); + diff -Nru a/sound/oss/sound_syms.c b/sound/oss/sound_syms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sound_syms.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,53 @@ +/* + * The sound core exports the following symbols to the rest of + * modulespace. + * + * (C) Copyright 1997 Alan Cox, Licensed under the GNU GPL + * + * Thu May 27 1999 Andrew J. Kroll + * left out exported symbol... fixed + */ + +#include +#include "sound_config.h" +#include "sound_calls.h" + +char sound_syms_symbol; + +EXPORT_SYMBOL(mixer_devs); +EXPORT_SYMBOL(audio_devs); +EXPORT_SYMBOL(num_mixers); +EXPORT_SYMBOL(num_audiodevs); + +EXPORT_SYMBOL(midi_devs); +EXPORT_SYMBOL(num_midis); +EXPORT_SYMBOL(synth_devs); +EXPORT_SYMBOL(num_synths); + +EXPORT_SYMBOL(sound_timer_devs); +EXPORT_SYMBOL(num_sound_timers); + +EXPORT_SYMBOL(sound_install_audiodrv); +EXPORT_SYMBOL(sound_install_mixer); +EXPORT_SYMBOL(sound_alloc_dma); +EXPORT_SYMBOL(sound_free_dma); +EXPORT_SYMBOL(sound_open_dma); +EXPORT_SYMBOL(sound_close_dma); +EXPORT_SYMBOL(sound_alloc_audiodev); +EXPORT_SYMBOL(sound_alloc_mididev); +EXPORT_SYMBOL(sound_alloc_mixerdev); +EXPORT_SYMBOL(sound_alloc_timerdev); +EXPORT_SYMBOL(sound_alloc_synthdev); +EXPORT_SYMBOL(sound_unload_audiodev); +EXPORT_SYMBOL(sound_unload_mididev); +EXPORT_SYMBOL(sound_unload_mixerdev); +EXPORT_SYMBOL(sound_unload_timerdev); +EXPORT_SYMBOL(sound_unload_synthdev); + +EXPORT_SYMBOL(load_mixer_volumes); + +EXPORT_SYMBOL(conf_printf); +EXPORT_SYMBOL(conf_printf2); + +MODULE_DESCRIPTION("OSS Sound subsystem"); +MODULE_AUTHOR("Hannu Savolainen, et al."); diff -Nru a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sound_timer.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,318 @@ +/* + * sound/sound_timer.c + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + */ +#include + + +#include "sound_config.h" + +static volatile int initialized, opened, tmr_running; +static volatile time_t tmr_offs, tmr_ctr; +static volatile unsigned long ticks_offs; +static volatile int curr_tempo, curr_timebase; +static volatile unsigned long curr_ticks; +static volatile unsigned long next_event_time; +static unsigned long prev_event_time; +static volatile unsigned long usecs_per_tmr; /* Length of the current interval */ + +static struct sound_lowlev_timer *tmr; + +static unsigned long tmr2ticks(int tmr_value) +{ + /* + * Convert timer ticks to MIDI ticks + */ + + unsigned long tmp; + unsigned long scale; + + tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */ + scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ + return (tmp + (scale / 2)) / scale; +} + +void reprogram_timer(void) +{ + unsigned long usecs_per_tick; + + /* + * The user is changing the timer rate before setting a timer + * slap, bad bad not allowed. + */ + + if(!tmr) + return; + + usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase); + + /* + * Don't kill the system by setting too high timer rate + */ + if (usecs_per_tick < 2000) + usecs_per_tick = 2000; + + usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick); +} + +void sound_timer_syncinterval(unsigned int new_usecs) +{ + /* + * This routine is called by the hardware level if + * the clock frequency has changed for some reason. + */ + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + usecs_per_tmr = new_usecs; +} + +static void tmr_reset(void) +{ + unsigned long flags; + + save_flags(flags); + cli(); + tmr_offs = 0; + ticks_offs = 0; + tmr_ctr = 0; + next_event_time = (unsigned long) -1; + prev_event_time = 0; + curr_ticks = 0; + restore_flags(flags); +} + +static int timer_open(int dev, int mode) +{ + if (opened) + return -EBUSY; + tmr_reset(); + curr_tempo = 60; + curr_timebase = 100; + opened = 1; + reprogram_timer(); + return 0; +} + +static void timer_close(int dev) +{ + opened = tmr_running = 0; + tmr->tmr_disable(tmr->dev); +} + +static int timer_event(int dev, unsigned char *event) +{ + unsigned char cmd = event[1]; + unsigned long parm = *(int *) &event[4]; + + switch (cmd) + { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + time = parm; + next_event_time = prev_event_time = time; + return TIMER_ARMED; + } + break; + + case TMR_START: + tmr_reset(); + tmr_running = 1; + reprogram_timer(); + break; + + case TMR_STOP: + tmr_running = 0; + break; + + case TMR_CONTINUE: + tmr_running = 1; + reprogram_timer(); + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 250) + parm = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = parm; + reprogram_timer(); + } + break; + + case TMR_ECHO: + seq_copy_to_input(event, 8); + break; + + default:; + } + return TIMER_NOT_ARMED; +} + +static unsigned long timer_get_time(int dev) +{ + if (!opened) + return 0; + return curr_ticks; +} + +static int timer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val; + + switch (cmd) + { + case SNDCTL_TMR_SOURCE: + val = TMR_INTERNAL; + break; + + case SNDCTL_TMR_START: + tmr_reset(); + tmr_running = 1; + return 0; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + return 0; + + case SNDCTL_TMR_CONTINUE: + tmr_running = 1; + return 0; + + case SNDCTL_TMR_TIMEBASE: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val) + { + if (val < 1) + val = 1; + if (val > 1000) + val = 1000; + curr_timebase = val; + } + val = curr_timebase; + break; + + case SNDCTL_TMR_TEMPO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val) + { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = val; + reprogram_timer(); + } + val = curr_tempo; + break; + + case SNDCTL_SEQ_CTRLRATE: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) /* Can't change */ + return -EINVAL; + val = ((curr_tempo * curr_timebase) + 30) / 60; + break; + + case SNDCTL_SEQ_GETTIME: + val = curr_ticks; + break; + + case SNDCTL_TMR_METRONOME: + default: + return -EINVAL; + } + return put_user(val, (int *)arg); +} + +static void timer_arm(int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + + next_event_time = prev_event_time = time; + return; +} + +static struct sound_timer_operations sound_timer = +{ + owner: THIS_MODULE, + info: {"Sound Timer", 0}, + priority: 1, /* Priority */ + devlink: 0, /* Local device link */ + open: timer_open, + close: timer_close, + event: timer_event, + get_time: timer_get_time, + ioctl: timer_ioctl, + arm_timer: timer_arm +}; + +void sound_timer_interrupt(void) +{ + if (!opened) + return; + + tmr->tmr_restart(tmr->dev); + + if (!tmr_running) + return; + + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); + + if (curr_ticks >= next_event_time) + { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } +} + +void sound_timer_init(struct sound_lowlev_timer *t, char *name) +{ + int n; + + if (initialized) + { + if (t->priority <= tmr->priority) + return; /* There is already a similar or better timer */ + tmr = t; + return; + } + initialized = 1; + tmr = t; + + n = sound_alloc_timerdev(); + if (n == -1) + n = 0; /* Overwrite the system timer */ + strcpy(sound_timer.info.name, name); + sound_timer_devs[n] = &sound_timer; +} diff -Nru a/sound/oss/soundcard.c b/sound/oss/soundcard.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/soundcard.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,782 @@ +/* + * linux/drivers/sound/soundcard.c + * + * Sound card driver for Linux + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * integrated sound_switch.c + * Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat, + * which should disappear in the near future) + * Eric Dumas : devfs support (22-Jan-98) with + * fixups by C. Scott Ananian + * Richard Gooch : moved common (non OSS-specific) devices to sound_core.c + * Rob Riggs : Added persistent DMA buffers support (1998/10/17) + * Christoph Hellwig : Some cleanup work (2000/03/01) + */ + +#include + +#include "sound_config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This ought to be moved into include/asm/dma.h + */ +#ifndef valid_dma +#define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4) +#endif + +/* + * Table for permanently allocated memory (used when unloading the module) + */ +caddr_t sound_mem_blocks[1024]; +int sound_nblocks = 0; + +/* Persistent DMA buffers */ +#ifdef CONFIG_SOUND_DMAP +int sound_dmap_flag = 1; +#else +int sound_dmap_flag = 0; +#endif + +static char dma_alloc_map[MAX_DMA_CHANNELS] = {0}; + +#define DMA_MAP_UNAVAIL 0 +#define DMA_MAP_FREE 1 +#define DMA_MAP_BUSY 2 + + +unsigned long seq_time = 0; /* Time for /dev/sequencer */ + +/* + * Table for configurable mixer volume handling + */ +static mixer_vol_table mixer_vols[MAX_MIXER_DEV]; +static int num_mixer_volumes = 0; + +int *load_mixer_volumes(char *name, int *levels, int present) +{ + int i, n; + + for (i = 0; i < num_mixer_volumes; i++) { + if (strcmp(name, mixer_vols[i].name) == 0) { + if (present) + mixer_vols[i].num = i; + return mixer_vols[i].levels; + } + } + if (num_mixer_volumes >= MAX_MIXER_DEV) { + printk(KERN_ERR "Sound: Too many mixers (%s)\n", name); + return levels; + } + n = num_mixer_volumes++; + + strcpy(mixer_vols[n].name, name); + + if (present) + mixer_vols[n].num = n; + else + mixer_vols[n].num = -1; + + for (i = 0; i < 32; i++) + mixer_vols[n].levels[i] = levels[i]; + return mixer_vols[n].levels; +} + +static int set_mixer_levels(caddr_t arg) +{ + /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */ + mixer_vol_table buf; + + if (__copy_from_user(&buf, arg, sizeof(buf))) + return -EFAULT; + load_mixer_volumes(buf.name, buf.levels, 0); + if (__copy_to_user(arg, &buf, sizeof(buf))) + return -EFAULT; + return 0; +} + +static int get_mixer_levels(caddr_t arg) +{ + int n; + + if (__get_user(n, (int *)(&(((mixer_vol_table *)arg)->num)))) + return -EFAULT; + if (n < 0 || n >= num_mixer_volumes) + return -EINVAL; + if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table))) + return -EFAULT; + return 0; +} + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* 4K page size but our output routines use some slack for overruns */ +#define PROC_BLOCK_SIZE (3*1024) + +static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + int dev = minor(file->f_dentry->d_inode->i_rdev); + int ret = -EINVAL; + + /* + * The OSS drivers aren't remotely happy without this locking, + * and unless someone fixes them when they are about to bite the + * big one anyway, we might as well bandage here.. + */ + + lock_kernel(); + + DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count)); + switch (dev & 0x0f) { + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + ret = audio_read(dev, file, buf, count); + break; + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + ret = sequencer_read(dev, file, buf, count); + break; + + case SND_DEV_MIDIN: + ret = MIDIbuf_read(dev, file, buf, count); + } + unlock_kernel(); + return ret; +} + +static ssize_t sound_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + int dev = minor(file->f_dentry->d_inode->i_rdev); + int ret = -EINVAL; + + lock_kernel(); + DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count)); + switch (dev & 0x0f) { + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + ret = sequencer_write(dev, file, buf, count); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + ret = audio_write(dev, file, buf, count); + break; + + case SND_DEV_MIDIN: + ret = MIDIbuf_write(dev, file, buf, count); + break; + } + unlock_kernel(); + return ret; +} + +static int sound_open(struct inode *inode, struct file *file) +{ + int dev = minor(inode->i_rdev); + int retval; + + DEB(printk("sound_open(dev=%d)\n", dev)); + if ((dev >= SND_NDEVS) || (dev < 0)) { + printk(KERN_ERR "Invalid minor device %d\n", dev); + return -ENXIO; + } + switch (dev & 0x0f) { + case SND_DEV_CTL: + dev >>= 4; + if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { + char modname[20]; + sprintf(modname, "mixer%d", dev); + request_module(modname); + } + if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL)) + return -ENXIO; + + if (mixer_devs[dev]->owner) + __MOD_INC_USE_COUNT (mixer_devs[dev]->owner); + break; + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + if ((retval = sequencer_open(dev, file)) < 0) + return retval; + break; + + case SND_DEV_MIDIN: + if ((retval = MIDIbuf_open(dev, file)) < 0) + return retval; + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + if ((retval = audio_open(dev, file)) < 0) + return retval; + break; + + default: + printk(KERN_ERR "Invalid minor device %d\n", dev); + return -ENXIO; + } + + return 0; +} + +static int sound_release(struct inode *inode, struct file *file) +{ + int dev = minor(inode->i_rdev); + + lock_kernel(); + DEB(printk("sound_release(dev=%d)\n", dev)); + switch (dev & 0x0f) { + case SND_DEV_CTL: + dev >>= 4; + if (mixer_devs[dev]->owner) + __MOD_DEC_USE_COUNT (mixer_devs[dev]->owner); + break; + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + sequencer_release(dev, file); + break; + + case SND_DEV_MIDIN: + MIDIbuf_release(dev, file); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + audio_release(dev, file); + break; + + default: + printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev); + } + unlock_kernel(); + + return 0; +} + +static int get_mixer_info(int dev, caddr_t arg) +{ + mixer_info info; + + strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); + strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + info.modify_counter = mixer_devs[dev]->modify_counter; + if (__copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int get_old_mixer_info(int dev, caddr_t arg) +{ + _old_mixer_info info; + + strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); + strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int sound_mixer_ioctl(int mixdev, unsigned int cmd, caddr_t arg) +{ + if (mixdev < 0 || mixdev >= MAX_MIXER_DEV) + return -ENXIO; + /* Try to load the mixer... */ + if (mixer_devs[mixdev] == NULL) { + char modname[20]; + sprintf(modname, "mixer%d", mixdev); + request_module(modname); + } + if (mixdev >= num_mixers || !mixer_devs[mixdev]) + return -ENXIO; + if (cmd == SOUND_MIXER_INFO) + return get_mixer_info(mixdev, arg); + if (cmd == SOUND_OLD_MIXER_INFO) + return get_old_mixer_info(mixdev, arg); + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + mixer_devs[mixdev]->modify_counter++; + if (!mixer_devs[mixdev]->ioctl) + return -EINVAL; + return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg); +} + +static int sound_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err, len = 0, dtype; + int dev = minor(inode->i_rdev); + + if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) { + /* + * Have to validate the address given by the process. + */ + len = _SIOC_SIZE(cmd); + if (len < 1 || len > 65536 || arg == 0) + return -EFAULT; + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + if ((err = verify_area(VERIFY_READ, (void *)arg, len)) < 0) + return err; + if (_SIOC_DIR(cmd) & _SIOC_READ) + if ((err = verify_area(VERIFY_WRITE, (void *)arg, len)) < 0) + return err; + } + DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); + if (cmd == OSS_GETVERSION) + return __put_user(SOUND_VERSION, (int *)arg); + + if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */ + (dev & 0x0f) != SND_DEV_CTL) { + dtype = dev & 0x0f; + switch (dtype) { + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev, + cmd, (caddr_t)arg); + + default: + return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg); + } + } + switch (dev & 0x0f) { + case SND_DEV_CTL: + if (cmd == SOUND_MIXER_GETLEVELS) + return get_mixer_levels((caddr_t)arg); + if (cmd == SOUND_MIXER_SETLEVELS) + return set_mixer_levels((caddr_t)arg); + return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg); + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_ioctl(dev, file, cmd, (caddr_t)arg); + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_ioctl(dev, file, cmd, (caddr_t)arg); + break; + + case SND_DEV_MIDIN: + return MIDIbuf_ioctl(dev, file, cmd, (caddr_t)arg); + break; + + } + return -EINVAL; +} + +static unsigned int sound_poll(struct file *file, poll_table * wait) +{ + struct inode *inode = file->f_dentry->d_inode; + int dev = minor(inode->i_rdev); + + DEB(printk("sound_poll(dev=%d)\n", dev)); + switch (dev & 0x0f) { + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_poll(dev, file, wait); + + case SND_DEV_MIDIN: + return MIDIbuf_poll(dev, file, wait); + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return DMAbuf_poll(file, dev >> 4, wait); + } + return 0; +} + +static int sound_mmap(struct file *file, struct vm_area_struct *vma) +{ + int dev_class; + unsigned long size; + struct dma_buffparms *dmap = NULL; + int dev = minor(file->f_dentry->d_inode->i_rdev); + + dev_class = dev & 0x0f; + dev >>= 4; + + if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) { + printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n"); + return -EINVAL; + } + lock_kernel(); + if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */ + dmap = audio_devs[dev]->dmap_out; + else if (vma->vm_flags & VM_READ) + dmap = audio_devs[dev]->dmap_in; + else { + printk(KERN_ERR "Sound: Undefined mmap() access\n"); + unlock_kernel(); + return -EINVAL; + } + + if (dmap == NULL) { + printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n"); + unlock_kernel(); + return -EIO; + } + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n"); + unlock_kernel(); + return -EIO; + } + if (dmap->mapping_flags) { + printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n"); + unlock_kernel(); + return -EIO; + } + if (vma->vm_pgoff != 0) { + printk(KERN_ERR "Sound: mmap() offset must be 0.\n"); + unlock_kernel(); + return -EINVAL; + } + size = vma->vm_end - vma->vm_start; + + if (size != dmap->bytes_in_use) { + printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use); + } + if (remap_page_range(vma, vma->vm_start, virt_to_phys(dmap->raw_buf), + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + + dmap->mapping_flags |= DMA_MAP_MAPPED; + + if( audio_devs[dev]->d->mmap) + audio_devs[dev]->d->mmap(dev); + + memset(dmap->raw_buf, + dmap->neutral_byte, + dmap->bytes_in_use); + unlock_kernel(); + return 0; +} + +struct file_operations oss_sound_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: sound_read, + write: sound_write, + poll: sound_poll, + ioctl: sound_ioctl, + mmap: sound_mmap, + open: sound_open, + release: sound_release, +}; + +/* + * Create the required special subdevices + */ + +static int create_special_devices(void) +{ + int seq1,seq2; + seq1=register_sound_special(&oss_sound_fops, 1); + if(seq1==-1) + goto bad; + seq2=register_sound_special(&oss_sound_fops, 8); + if(seq2!=-1) + return 0; + unregister_sound_special(1); +bad: + return -1; +} + + +/* These device names follow the official Linux device list, + * Documentation/devices.txt. Let us know if there are other + * common names we should support for compatibility. + * Only those devices not created by the generic code in sound_core.c are + * registered here. + */ +static const struct { + unsigned short minor; + char *name; + umode_t mode; + int *num; +} dev_list[] = { /* list of minor devices */ +/* seems to be some confusion here -- this device is not in the device list */ + {SND_DEV_DSP16, "dspW", S_IWUGO | S_IRUSR | S_IRGRP, + &num_audiodevs}, + {SND_DEV_AUDIO, "audio", S_IWUGO | S_IRUSR | S_IRGRP, + &num_audiodevs}, +}; + +static char * +soundcard_make_name(char *buf, char *name, int idx) { + if (idx==0) + sprintf(buf, "sound/%s", name); + else + sprintf(buf, "sound/%s%d", name, idx); + return buf; +} + +/* Register/unregister audio entries */ +static void soundcard_register_devfs (int do_register) +{ + char name_buf[32]; + int i, j, num; + + for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) { + num = (dev_list[i].num == NULL) ? 0 : *dev_list[i].num; + for (j = 0; j < num || j == 0; j++) { + soundcard_make_name (name_buf, dev_list[i].name, j); + if (do_register) + devfs_register (NULL, name_buf, DEVFS_FL_NONE, + SOUND_MAJOR, dev_list[i].minor+ (j* 0x10), + S_IFCHR | dev_list[i].mode, + &oss_sound_fops, NULL); + else { + devfs_handle_t de; + + de = devfs_find_handle (NULL, name_buf, 0, 0, + DEVFS_SPECIAL_CHR, 0); + devfs_unregister (de); + } + } + } +} + + +static int dmabuf = 0; +static int dmabug = 0; + +MODULE_PARM(dmabuf, "i"); +MODULE_PARM(dmabug, "i"); + +static int __init oss_init(void) +{ + int err; + + /* drag in sound_syms.o */ + { + extern char sound_syms_symbol; + sound_syms_symbol = 0; + } + +#ifdef CONFIG_PCI + if(dmabug) + isa_dma_bridge_buggy = dmabug; +#endif + + err = create_special_devices(); + if (err) { + printk(KERN_ERR "sound: driver already loaded/included in kernel\n"); + return err; + } + + /* Protecting the innocent */ + sound_dmap_flag = (dmabuf > 0 ? 1 : 0); + + soundcard_register_devfs(1); + + if (sound_nblocks >= 1024) + printk(KERN_ERR "Sound warning: Deallocation table was too small.\n"); + + return 0; +} + +static void __exit oss_cleanup(void) +{ + int i; + + if (MOD_IN_USE) + return; + + soundcard_register_devfs (0); + + unregister_sound_special(1); + unregister_sound_special(8); + + sound_stop_timer(); + + sequencer_unload(); + + for (i = 0; i < MAX_DMA_CHANNELS; i++) + if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) { + printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i); + sound_free_dma(i); + } + + for (i = 0; i < sound_nblocks; i++) + vfree(sound_mem_blocks[i]); + +} + +module_init(oss_init); +module_exit(oss_cleanup); +MODULE_LICENSE("GPL"); + + +int sound_alloc_dma(int chn, char *deviceID) +{ + int err; + + if ((err = request_dma(chn, deviceID)) != 0) + return err; + + dma_alloc_map[chn] = DMA_MAP_FREE; + + return 0; +} + +int sound_open_dma(int chn, char *deviceID) +{ + unsigned long flags; + + if (!valid_dma(chn)) { + printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn); + return 1; + } + save_flags(flags); + cli(); + + if (dma_alloc_map[chn] != DMA_MAP_FREE) { + printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]); + restore_flags(flags); + return 1; + } + dma_alloc_map[chn] = DMA_MAP_BUSY; + restore_flags(flags); + return 0; +} + +void sound_free_dma(int chn) +{ + if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) { + /* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */ + return; + } + free_dma(chn); + dma_alloc_map[chn] = DMA_MAP_UNAVAIL; +} + +void sound_close_dma(int chn) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + if (dma_alloc_map[chn] != DMA_MAP_BUSY) { + printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn); + restore_flags(flags); + return; + } + dma_alloc_map[chn] = DMA_MAP_FREE; + restore_flags(flags); +} + +static void do_sequencer_timer(unsigned long dummy) +{ + sequencer_timer(0); +} + + +static struct timer_list seq_timer = +{function: do_sequencer_timer}; + +void request_sound_timer(int count) +{ + extern unsigned long seq_time; + + if (count < 0) { + seq_timer.expires = (-count) + jiffies; + add_timer(&seq_timer); + return; + } + count += seq_time; + + count -= jiffies; + + if (count < 1) + count = 1; + + seq_timer.expires = (count) + jiffies; + add_timer(&seq_timer); +} + +void sound_stop_timer(void) +{ + del_timer(&seq_timer);; +} + +void conf_printf(char *name, struct address_info *hw_config) +{ +#ifndef CONFIG_SOUND_TRACEINIT + return; +#else + printk("<%s> at 0x%03x", name, hw_config->io_base); + + if (hw_config->irq) + printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq); + + if (hw_config->dma != -1 || hw_config->dma2 != -1) + { + printk(" dma %d", hw_config->dma); + if (hw_config->dma2 != -1) + printk(",%d", hw_config->dma2); + } + printk("\n"); +#endif +} + +void conf_printf2(char *name, int base, int irq, int dma, int dma2) +{ +#ifndef CONFIG_SOUND_TRACEINIT + return; +#else + printk("<%s> at 0x%03x", name, base); + + if (irq) + printk(" irq %d", (irq > 0) ? irq : -irq); + + if (dma != -1 || dma2 != -1) + { + printk(" dma %d", dma); + if (dma2 != -1) + printk(",%d", dma2); + } + printk("\n"); +#endif +} diff -Nru a/sound/oss/soundvers.h b/sound/oss/soundvers.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/soundvers.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,2 @@ +#define SOUND_VERSION_STRING "3.8s2++-971130" +#define SOUND_INTERNAL_VERSION 0x030804 diff -Nru a/sound/oss/sscape.c b/sound/oss/sscape.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sscape.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,1530 @@ +/* + * sound/sscape.c + * + * Low level driver for Ensoniq SoundScape + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Sergey Smitienko : ensoniq p'n'p support + * Christoph Hellwig : adapted to module_init/module_exit + * Bartlomiej Zolnierkiewicz : added __init to attach_sscape() + * Chris Rankin : Specify that this module owns the coprocessor + * Arnaldo C. de Melo : added missing restore_flags in sscape_pnp_upload_file + */ + +#include +#include + +#include "sound_config.h" +#include "sound_firmware.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coproc.h" + +#include "ad1848.h" +#include "mpu401.h" + +/* + * I/O ports + */ +#define MIDI_DATA 0 +#define MIDI_CTRL 1 +#define HOST_CTRL 2 +#define TX_READY 0x02 +#define RX_READY 0x01 +#define HOST_DATA 3 +#define ODIE_ADDR 4 +#define ODIE_DATA 5 + +/* + * Indirect registers + */ + +#define GA_INTSTAT_REG 0 +#define GA_INTENA_REG 1 +#define GA_DMAA_REG 2 +#define GA_DMAB_REG 3 +#define GA_INTCFG_REG 4 +#define GA_DMACFG_REG 5 +#define GA_CDCFG_REG 6 +#define GA_SMCFGA_REG 7 +#define GA_SMCFGB_REG 8 +#define GA_HMCTL_REG 9 + +/* + * DMA channel identifiers (A and B) + */ + +#define SSCAPE_DMA_A 0 +#define SSCAPE_DMA_B 1 + +#define PORT(name) (devc->base+name) + +/* + * Host commands recognized by the OBP microcode + */ + +#define CMD_GEN_HOST_ACK 0x80 +#define CMD_GEN_MPU_ACK 0x81 +#define CMD_GET_BOARD_TYPE 0x82 +#define CMD_SET_CONTROL 0x88 /* Old firmware only */ +#define CMD_GET_CONTROL 0x89 /* Old firmware only */ +#define CTL_MASTER_VOL 0 +#define CTL_MIC_MODE 2 +#define CTL_SYNTH_VOL 4 +#define CTL_WAVE_VOL 7 +#define CMD_SET_EXTMIDI 0x8a +#define CMD_GET_EXTMIDI 0x8b +#define CMD_SET_MT32 0x8c +#define CMD_GET_MT32 0x8d + +#define CMD_ACK 0x80 + +#define IC_ODIE 1 +#define IC_OPUS 2 + +typedef struct sscape_info +{ + int base, irq, dma; + + int codec, codec_irq; /* required to setup pnp cards*/ + int codec_type; + int ic_type; + char* raw_buf; + unsigned long raw_buf_phys; + int buffsize; /* -------------------------- */ + + int ok; /* Properly detected */ + int failed; + int dma_allocated; + int codec_audiodev; + int opened; + int *osp; + int my_audiodev; +} sscape_info; + +static struct sscape_info adev_info = { + 0 +}; + +static struct sscape_info *devc = &adev_info; +static int sscape_mididev = -1; + +/* Some older cards have assigned interrupt bits differently than new ones */ +static char valid_interrupts_old[] = { + 9, 7, 5, 15 +}; + +static char valid_interrupts_new[] = { + 9, 5, 7, 10 +}; + +static char *valid_interrupts = valid_interrupts_new; + +/* + * See the bottom of the driver. This can be set by spea =0/1. + */ + +#ifdef REVEAL_SPEA +static char old_hardware = 1; +#else +static char old_hardware = 0; +#endif + +static void sleep(unsigned howlong) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(howlong); +} + +static unsigned char sscape_read(struct sscape_info *devc, int reg) +{ + unsigned long flags; + unsigned char val; + + save_flags(flags); + cli(); + outb(reg, PORT(ODIE_ADDR)); + val = inb(PORT(ODIE_DATA)); + restore_flags(flags); + return val; +} + +static void sscape_write(struct sscape_info *devc, int reg, int data) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outb(reg, PORT(ODIE_ADDR)); + outb(data, PORT(ODIE_DATA)); + restore_flags(flags); +} + +static unsigned char sscape_pnp_read_codec(sscape_info* devc, unsigned char reg) +{ + unsigned char res; + unsigned long flags; + + save_flags(flags); + cli(); + outb( reg, devc -> codec); + res = inb (devc -> codec + 1); + restore_flags(flags); + return res; + +} + +static void sscape_pnp_write_codec(sscape_info* devc, unsigned char reg, unsigned char data) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outb( reg, devc -> codec); + outb( data, devc -> codec + 1); + restore_flags(flags); +} + +static void host_open(struct sscape_info *devc) +{ + outb((0x00), PORT(HOST_CTRL)); /* Put the board to the host mode */ +} + +static void host_close(struct sscape_info *devc) +{ + outb((0x03), PORT(HOST_CTRL)); /* Put the board to the MIDI mode */ +} + +static int host_write(struct sscape_info *devc, unsigned char *data, int count) +{ + unsigned long flags; + int i, timeout_val; + + save_flags(flags); + cli(); + + /* + * Send the command and data bytes + */ + + for (i = 0; i < count; i++) + { + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + if (inb(PORT(HOST_CTRL)) & TX_READY) + break; + + if (timeout_val <= 0) + { + restore_flags(flags); + return 0; + } + outb(data[i], PORT(HOST_DATA)); + } + restore_flags(flags); + return 1; +} + +static int host_read(struct sscape_info *devc) +{ + unsigned long flags; + int timeout_val; + unsigned char data; + + save_flags(flags); + cli(); + + /* + * Read a byte + */ + + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + if (inb(PORT(HOST_CTRL)) & RX_READY) + break; + + if (timeout_val <= 0) + { + restore_flags(flags); + return -1; + } + data = inb(PORT(HOST_DATA)); + restore_flags(flags); + return data; +} + +#if 0 /* unused */ +static int host_command1(struct sscape_info *devc, int cmd) +{ + unsigned char buf[10]; + buf[0] = (unsigned char) (cmd & 0xff); + return host_write(devc, buf, 1); +} +#endif /* unused */ + + +static int host_command2(struct sscape_info *devc, int cmd, int parm1) +{ + unsigned char buf[10]; + + buf[0] = (unsigned char) (cmd & 0xff); + buf[1] = (unsigned char) (parm1 & 0xff); + + return host_write(devc, buf, 2); +} + +static int host_command3(struct sscape_info *devc, int cmd, int parm1, int parm2) +{ + unsigned char buf[10]; + + buf[0] = (unsigned char) (cmd & 0xff); + buf[1] = (unsigned char) (parm1 & 0xff); + buf[2] = (unsigned char) (parm2 & 0xff); + return host_write(devc, buf, 3); +} + +static void set_mt32(struct sscape_info *devc, int value) +{ + host_open(devc); + host_command2(devc, CMD_SET_MT32, value ? 1 : 0); + if (host_read(devc) != CMD_ACK) + { + /* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */ + } + host_close(devc); +} + +static void set_control(struct sscape_info *devc, int ctrl, int value) +{ + host_open(devc); + host_command3(devc, CMD_SET_CONTROL, ctrl, value); + if (host_read(devc) != CMD_ACK) + { + /* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */ + } + host_close(devc); +} + +static void do_dma(struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode) +{ + unsigned char temp; + + if (dma_chan != SSCAPE_DMA_A) + { + printk(KERN_WARNING "soundscape: Tried to use DMA channel != A. Why?\n"); + return; + } + audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE; + DMAbuf_start_dma(devc->codec_audiodev, buf, blk_size, mode); + audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE; + + temp = devc->dma << 4; /* Setup DMA channel select bits */ + if (devc->dma <= 3) + temp |= 0x80; /* 8 bit DMA channel */ + + temp |= 1; /* Trigger DMA */ + sscape_write(devc, GA_DMAA_REG, temp); + temp &= 0xfe; /* Clear DMA trigger */ + sscape_write(devc, GA_DMAA_REG, temp); +} + +static int verify_mpu(struct sscape_info *devc) +{ + /* + * The SoundScape board could be in three modes (MPU, 8250 and host). + * If the card is not in the MPU mode, enabling the MPU driver will + * cause infinite loop (the driver believes that there is always some + * received data in the buffer. + * + * Detect this by looking if there are more than 10 received MIDI bytes + * (0x00) in the buffer. + */ + + int i; + + for (i = 0; i < 10; i++) + { + if (inb(devc->base + HOST_CTRL) & 0x80) + return 1; + + if (inb(devc->base) != 0x00) + return 1; + } + printk(KERN_WARNING "SoundScape: The device is not in the MPU-401 mode\n"); + return 0; +} + +static int sscape_coproc_open(void *dev_info, int sub_device) +{ + if (sub_device == COPR_MIDI) + { + set_mt32(devc, 0); + if (!verify_mpu(devc)) + return -EIO; + } + return 0; +} + +static void sscape_coproc_close(void *dev_info, int sub_device) +{ + struct sscape_info *devc = dev_info; + unsigned long flags; + + save_flags(flags); + cli(); + if (devc->dma_allocated) + { + sscape_write(devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */ + devc->dma_allocated = 0; + } + restore_flags(flags); + return; +} + +static void sscape_coproc_reset(void *dev_info) +{ +} + +static int sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag) +{ + unsigned long flags; + unsigned char temp; + volatile int done, timeout_val; + static unsigned char codec_dma_bits = 0; + + if (flag & CPF_FIRST) + { + /* + * First block. Have to allocate DMA and to reset the board + * before continuing. + */ + + save_flags(flags); + cli(); + codec_dma_bits = sscape_read(devc, GA_CDCFG_REG); + + if (devc->dma_allocated == 0) + devc->dma_allocated = 1; + + restore_flags(flags); + + sscape_write(devc, GA_HMCTL_REG, + (temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f); /*Reset */ + + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + sscape_read(devc, GA_HMCTL_REG); /* Delay */ + + /* Take board out of reset */ + sscape_write(devc, GA_HMCTL_REG, + (temp = sscape_read(devc, GA_HMCTL_REG)) | 0x80); + } + /* + * Transfer one code block using DMA + */ + if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL) + { + printk(KERN_WARNING "soundscape: DMA buffer not available\n"); + return 0; + } + memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size); + + save_flags(flags); + cli(); + + /******** INTERRUPTS DISABLED NOW ********/ + + do_dma(devc, SSCAPE_DMA_A, + audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys, + size, DMA_MODE_WRITE); + + /* + * Wait until transfer completes. + */ + + done = 0; + timeout_val = 30; + while (!done && timeout_val-- > 0) + { + int resid; + + if (HZ / 50) + sleep(HZ / 50); + clear_dma_ff(devc->dma); + if ((resid = get_dma_residue(devc->dma)) == 0) + done = 1; + } + + restore_flags(flags); + if (!done) + return 0; + + if (flag & CPF_LAST) + { + /* + * Take the board out of reset + */ + outb((0x00), PORT(HOST_CTRL)); + outb((0x00), PORT(MIDI_CTRL)); + + temp = sscape_read(devc, GA_HMCTL_REG); + temp |= 0x40; + sscape_write(devc, GA_HMCTL_REG, temp); /* Kickstart the board */ + + /* + * Wait until the ODB wakes up + */ + + save_flags(flags); + cli(); + done = 0; + timeout_val = 5 * HZ; + while (!done && timeout_val-- > 0) + { + unsigned char x; + + sleep(1); + x = inb(PORT(HOST_DATA)); + if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */ + { + DDB(printk("Soundscape: Acknowledge = %x\n", x)); + done = 1; + } + } + sscape_write(devc, GA_CDCFG_REG, codec_dma_bits); + + restore_flags(flags); + if (!done) + { + printk(KERN_ERR "soundscape: The OBP didn't respond after code download\n"); + return 0; + } + save_flags(flags); + cli(); + done = 0; + timeout_val = 5 * HZ; + while (!done && timeout_val-- > 0) + { + sleep(1); + if (inb(PORT(HOST_DATA)) == 0xfe) /* Host startup acknowledge */ + done = 1; + } + restore_flags(flags); + if (!done) + { + printk(KERN_ERR "soundscape: OBP Initialization failed.\n"); + return 0; + } + printk(KERN_INFO "SoundScape board initialized OK\n"); + set_control(devc, CTL_MASTER_VOL, 100); + set_control(devc, CTL_SYNTH_VOL, 100); + +#ifdef SSCAPE_DEBUG3 + /* + * Temporary debugging aid. Print contents of the registers after + * downloading the code. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x (new value)\n", i, sscape_read(devc, i)); + } +#endif + + } + return 1; +} + +static int download_boot_block(void *dev_info, copr_buffer * buf) +{ + if (buf->len <= 0 || buf->len > sizeof(buf->data)) + return -EINVAL; + + if (!sscape_download_boot(devc, buf->data, buf->len, buf->flags)) + { + printk(KERN_ERR "soundscape: Unable to load microcode block to the OBP.\n"); + return -EIO; + } + return 0; +} + +static int sscape_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local) +{ + copr_buffer *buf; + int err; + + switch (cmd) + { + case SNDCTL_COPR_RESET: + sscape_coproc_reset(dev_info); + return 0; + + case SNDCTL_COPR_LOAD: + buf = (copr_buffer *) vmalloc(sizeof(copr_buffer)); + if (buf == NULL) + return -ENOSPC; + if (copy_from_user(buf, arg, sizeof(copr_buffer))) + { + vfree(buf); + return -EFAULT; + } + err = download_boot_block(dev_info, buf); + vfree(buf); + return err; + + default: + return -EINVAL; + } +} + +static coproc_operations sscape_coproc_operations = +{ + "SoundScape M68K", + THIS_MODULE, + sscape_coproc_open, + sscape_coproc_close, + sscape_coproc_ioctl, + sscape_coproc_reset, + &adev_info +}; + +static int sscape_detected = 0; +static int sscape_is_pnp = 0; + +void __init attach_sscape(struct address_info *hw_config) +{ +#ifndef SSCAPE_REGS + /* + * Config register values for Spea/V7 Media FX and Ensoniq S-2000. + * These values are card + * dependent. If you have another SoundScape based card, you have to + * find the correct values. Do the following: + * - Compile this driver with SSCAPE_DEBUG1 defined. + * - Shut down and power off your machine. + * - Boot with DOS so that the SSINIT.EXE program is run. + * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed + * when detecting the SoundScape. + * - Modify the following list to use the values printed during boot. + * Undefine the SSCAPE_DEBUG1 + */ +#define SSCAPE_REGS { \ +/* I0 */ 0x00, \ +/* I1 */ 0xf0, /* Note! Ignored. Set always to 0xf0 */ \ +/* I2 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \ +/* I3 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \ +/* I4 */ 0xf5, /* Ignored */ \ +/* I5 */ 0x10, \ +/* I6 */ 0x00, \ +/* I7 */ 0x2e, /* I7 MEM config A. Likely to vary between models */ \ +/* I8 */ 0x00, /* I8 MEM config B. Likely to vary between models */ \ +/* I9 */ 0x40 /* Ignored */ \ + } +#endif + + unsigned long flags; + static unsigned char regs[10] = SSCAPE_REGS; + + int i, irq_bits = 0xff; + + if (sscape_detected != hw_config->io_base) + return; + + request_region(devc->base + 2, 6, "SoundScape"); + if (old_hardware) + { + valid_interrupts = valid_interrupts_old; + conf_printf("Ensoniq SoundScape (old)", hw_config); + } + else + conf_printf("Ensoniq SoundScape", hw_config); + + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (hw_config->irq == valid_interrupts[i]) + { + irq_bits = i; + break; + } + } + if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff)) + { + printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq); + return; + } + + if (!sscape_is_pnp) { + + save_flags(flags); + cli(); + for (i = 1; i < 10; i++) + { + switch (i) + { + case 1: /* Host interrupt enable */ + sscape_write(devc, i, 0xf0); /* All interrupts enabled */ + break; + + case 2: /* DMA A status/trigger register */ + case 3: /* DMA B status/trigger register */ + sscape_write(devc, i, 0x20); /* DMA channel disabled */ + break; + + case 4: /* Host interrupt config reg */ + sscape_write(devc, i, 0xf0 | (irq_bits << 2) | irq_bits); + break; + + case 5: /* Don't destroy CD-ROM DMA config bits (0xc0) */ + sscape_write(devc, i, (regs[i] & 0x3f) | (sscape_read(devc, i) & 0xc0)); + break; + + case 6: /* CD-ROM config (WSS codec actually) */ + sscape_write(devc, i, regs[i]); + break; + + case 9: /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */ + sscape_write(devc, i, (sscape_read(devc, i) & 0xf0) | 0x08); + break; + + default: + sscape_write(devc, i, regs[i]); + } + } + restore_flags(flags); + } +#ifdef SSCAPE_DEBUG2 + /* + * Temporary debugging aid. Print contents of the registers after + * changing them. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x (new value)\n", i, sscape_read(devc, i)); + } +#endif + + if (probe_mpu401(hw_config)) + hw_config->always_detect = 1; + hw_config->name = "SoundScape"; + + hw_config->irq *= -1; /* Negative value signals IRQ sharing */ + attach_mpu401(hw_config, THIS_MODULE); + hw_config->irq *= -1; /* Restore it */ + + if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ + { + sscape_mididev = hw_config->slots[1]; + midi_devs[hw_config->slots[1]]->coproc = &sscape_coproc_operations; + } + sscape_write(devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */ + devc->ok = 1; + devc->failed = 0; +} + +static int detect_ga(sscape_info * devc) +{ + unsigned char save; + + DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base)); + + if (check_region(devc->base, 8)) + return 0; + + /* + * First check that the address register of "ODIE" is + * there and that it has exactly 4 writable bits. + * First 4 bits + */ + + if ((save = inb(PORT(ODIE_ADDR))) & 0xf0) + { + DDB(printk("soundscape: Detect error A\n")); + return 0; + } + outb((0x00), PORT(ODIE_ADDR)); + if (inb(PORT(ODIE_ADDR)) != 0x00) + { + DDB(printk("soundscape: Detect error B\n")); + return 0; + } + outb((0xff), PORT(ODIE_ADDR)); + if (inb(PORT(ODIE_ADDR)) != 0x0f) + { + DDB(printk("soundscape: Detect error C\n")); + return 0; + } + outb((save), PORT(ODIE_ADDR)); + + /* + * Now verify that some indirect registers return zero on some bits. + * This may break the driver with some future revisions of "ODIE" but... + */ + + if (sscape_read(devc, 0) & 0x0c) + { + DDB(printk("soundscape: Detect error D (%x)\n", sscape_read(devc, 0))); + return 0; + } + if (sscape_read(devc, 1) & 0x0f) + { + DDB(printk("soundscape: Detect error E\n")); + return 0; + } + if (sscape_read(devc, 5) & 0x0f) + { + DDB(printk("soundscape: Detect error F\n")); + return 0; + } + return 1; +} + +static int sscape_read_host_ctrl(sscape_info* devc) +{ + return host_read(devc); +} + +static void sscape_write_host_ctrl2(sscape_info *devc, int a, int b) +{ + host_command2(devc, a, b); +} + +static int sscape_alloc_dma(sscape_info *devc) +{ + char *start_addr, *end_addr; + int dma_pagesize; + int sz, size; + struct page *page; + + if (devc->raw_buf != NULL) return 0; /* Already done */ + dma_pagesize = (devc->dma < 4) ? (64 * 1024) : (128 * 1024); + devc->raw_buf = NULL; + devc->buffsize = 8192*4; + if (devc->buffsize > dma_pagesize) devc->buffsize = dma_pagesize; + start_addr = NULL; + /* + * Now loop until we get a free buffer. Try to get smaller buffer if + * it fails. Don't accept smaller than 8k buffer for performance + * reasons. + */ + while (start_addr == NULL && devc->buffsize > PAGE_SIZE) { + for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1); + devc->buffsize = PAGE_SIZE * (1 << sz); + start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz); + if (start_addr == NULL) devc->buffsize /= 2; + } + + if (start_addr == NULL) { + printk(KERN_ERR "sscape pnp init error: Couldn't allocate DMA buffer\n"); + return 0; + } else { + /* make some checks */ + end_addr = start_addr + devc->buffsize - 1; + /* now check if it fits into the same dma-pagesize */ + + if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1)) + || end_addr >= (char *) (MAX_DMA_ADDRESS)) { + printk(KERN_ERR "sscape pnp: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, devc->buffsize); + return 0; + } + } + devc->raw_buf = start_addr; + devc->raw_buf_phys = virt_to_bus(start_addr); + + for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) + mem_map_reserve(page); + return 1; +} + +static void sscape_free_dma(sscape_info *devc) +{ + int sz, size; + unsigned long start_addr, end_addr; + struct page *page; + + if (devc->raw_buf == NULL) return; + for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1); + start_addr = (unsigned long) devc->raw_buf; + end_addr = start_addr + devc->buffsize; + + for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) + mem_map_unreserve(page); + + free_pages((unsigned long) devc->raw_buf, sz); + devc->raw_buf = NULL; +} + +/* Intel version !!!!!!!!! */ + +static int sscape_start_dma(int chan, unsigned long physaddr, int count, int dma_mode) +{ + unsigned long flags; + + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + set_dma_mode(chan, dma_mode); + set_dma_addr(chan, physaddr); + set_dma_count(chan, count); + enable_dma(chan); + release_dma_lock(flags); + return 0; +} + +static void sscape_pnp_start_dma(sscape_info* devc, int arg ) +{ + int reg; + if (arg == 0) reg = 2; + else reg = 3; + + sscape_write(devc, reg, sscape_read( devc, reg) | 0x01); + sscape_write(devc, reg, sscape_read( devc, reg) & 0xFE); +} + +static int sscape_pnp_wait_dma (sscape_info* devc, int arg ) +{ + int reg; + unsigned long i; + unsigned char d; + + if (arg == 0) reg = 2; + else reg = 3; + + sleep ( 1 ); + i = 0; + do { + d = sscape_read(devc, reg) & 1; + if ( d == 1) break; + i++; + } while (i < 500000); + d = sscape_read(devc, reg) & 1; + return d; +} + +static int sscape_pnp_alloc_dma(sscape_info* devc) +{ + /* printk(KERN_INFO "sscape: requesting dma\n"); */ + if (request_dma(devc -> dma, "sscape")) return 0; + /* printk(KERN_INFO "sscape: dma channel allocated\n"); */ + if (!sscape_alloc_dma(devc)) { + free_dma(devc -> dma); + return 0; + }; + return 1; +} + +static void sscape_pnp_free_dma(sscape_info* devc) +{ + sscape_free_dma( devc); + free_dma(devc -> dma ); + /* printk(KERN_INFO "sscape: dma released\n"); */ +} + +static int sscape_pnp_upload_file(sscape_info* devc, char* fn) +{ + int done = 0; + int timeout_val; + char* data,*dt; + int len,l; + unsigned long flags; + + sscape_write( devc, 9, sscape_read(devc, 9 ) & 0x3F ); + sscape_write( devc, 2, (devc -> dma << 4) | 0x80 ); + sscape_write( devc, 3, 0x20 ); + sscape_write( devc, 9, sscape_read( devc, 9 ) | 0x80 ); + + len = mod_firmware_load(fn, &data); + if (len == 0) { + printk(KERN_ERR "sscape: file not found: %s\n", fn); + return 0; + } + dt = data; + save_flags(flags); + cli(); + while ( len > 0 ) { + if (len > devc -> buffsize) l = devc->buffsize; + else l = len; + len -= l; + memcpy(devc->raw_buf, dt, l); dt += l; + sscape_start_dma(devc->dma, devc->raw_buf_phys, l, 0x48); + sscape_pnp_start_dma ( devc, 0 ); + if (sscape_pnp_wait_dma ( devc, 0 ) == 0) { + restore_flags(flags); + return 0; + } + } + + restore_flags(flags); + vfree(data); + + outb(0, devc -> base + 2); + outb(0, devc -> base); + + sscape_write ( devc, 9, sscape_read( devc, 9 ) | 0x40); + + timeout_val = 5 * HZ; + while (!done && timeout_val-- > 0) + { + unsigned char x; + sleep(1); + x = inb( devc -> base + 3); + if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */ + { + //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x); + done = 1; + } + } + timeout_val = 5 * HZ; + done = 0; + while (!done && timeout_val-- > 0) + { + unsigned char x; + sleep(1); + x = inb( devc -> base + 3); + if (x == 0xfe) /* OBP startup acknowledge */ + { + //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x); + done = 1; + } + } + + if ( !done ) printk(KERN_ERR "soundscape: OBP Initialization failed.\n"); + + sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40); + sscape_write( devc, 3, (devc -> dma << 4) + 0x80); + return 1; +} + +static void __init sscape_pnp_init_hw(sscape_info* devc) +{ + unsigned char midi_irq = 0, sb_irq = 0; + unsigned i; + static char code_file_name[23] = "/sndscape/sndscape.cox"; + + int sscape_sb_enable = 0; + int sscape_joystic_enable = 0x7f; + int sscape_mic_enable = 0; + int sscape_ext_midi = 0; + + if ( !sscape_pnp_alloc_dma(devc) ) { + printk(KERN_ERR "sscape: faild to allocate dma\n"); + return; + } + + for (i = 0; i < 4; i++) { + if ( devc -> irq == valid_interrupts[i] ) + midi_irq = i; + if ( devc -> codec_irq == valid_interrupts[i] ) + sb_irq = i; + } + + sscape_write( devc, 5, 0x50); + sscape_write( devc, 7, 0x2e); + sscape_write( devc, 8, 0x00); + + sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40); + sscape_write( devc, 3, ( devc -> dma << 4) | 0x80); + + if ( sscape_sb_enable ) + sscape_write (devc, 4, 0xF0 | (sb_irq << 2) | midi_irq); + else + sscape_write (devc, 4, 0xF0 | (midi_irq<<2) | midi_irq); + + i = 0x10; //sscape_read(devc, 9) & (devc->ic_type == IC_ODIE ? 0xf0 : 0xc0); + if ( sscape_sb_enable ) + i |= devc->ic_type == IC_ODIE ? 0x05 : 0x07; + if (sscape_joystic_enable) i |= 8; + + sscape_write (devc, 9, i); + sscape_write (devc, 6, 0x80); + sscape_write (devc, 1, 0x80); + + if (devc -> codec_type == 2) { + sscape_pnp_write_codec( devc, 0x0C, 0x50); + sscape_pnp_write_codec( devc, 0x10, sscape_pnp_read_codec( devc, 0x10) & 0x3F); + sscape_pnp_write_codec( devc, 0x11, sscape_pnp_read_codec( devc, 0x11) | 0xC0); + sscape_pnp_write_codec( devc, 29, 0x20); + } + + if (sscape_pnp_upload_file(devc, "/sndscape/scope.cod") == 0 ) { + printk(KERN_ERR "sscape: faild to upload file /sndscape/scope.cod\n"); + sscape_pnp_free_dma(devc); + return; + } + + i = sscape_read_host_ctrl( devc ); + + if ( (i & 0x0F) > 7 ) { + printk(KERN_ERR "sscape: scope.cod faild\n"); + sscape_pnp_free_dma(devc); + return; + } + if ( i & 0x10 ) sscape_write( devc, 7, 0x2F); + code_file_name[21] = (char) ( i & 0x0F) + 0x30; + if (sscape_pnp_upload_file( devc, code_file_name) == 0) { + printk(KERN_ERR "sscape: faild to upload file %s\n", code_file_name); + sscape_pnp_free_dma(devc); + return; + } + + if (devc->ic_type != IC_ODIE) { + sscape_pnp_write_codec( devc, 10, (sscape_pnp_read_codec(devc, 10) & 0x7f) | + ( sscape_mic_enable == 0 ? 0x00 : 0x80) ); + } + sscape_write_host_ctrl2( devc, 0x84, 0x64 ); /* MIDI volume */ + sscape_write_host_ctrl2( devc, 0x86, 0x64 ); /* MIDI volume?? */ + sscape_write_host_ctrl2( devc, 0x8A, sscape_ext_midi); + + sscape_pnp_write_codec ( devc, 6, 0x3f ); //WAV_VOL + sscape_pnp_write_codec ( devc, 7, 0x3f ); //WAV_VOL + sscape_pnp_write_codec ( devc, 2, 0x1F ); //WD_CDXVOLL + sscape_pnp_write_codec ( devc, 3, 0x1F ); //WD_CDXVOLR + + if (devc -> codec_type == 1) { + sscape_pnp_write_codec ( devc, 4, 0x1F ); + sscape_pnp_write_codec ( devc, 5, 0x1F ); + sscape_write_host_ctrl2( devc, 0x88, sscape_mic_enable); + } else { + int t; + sscape_pnp_write_codec ( devc, 0x10, 0x1F << 1); + sscape_pnp_write_codec ( devc, 0x11, 0xC0 | (0x1F << 1)); + + t = sscape_pnp_read_codec( devc, 0x00) & 0xDF; + if ( (sscape_mic_enable == 0)) t |= 0; + else t |= 0x20; + sscape_pnp_write_codec ( devc, 0x00, t); + t = sscape_pnp_read_codec( devc, 0x01) & 0xDF; + if ( (sscape_mic_enable == 0) ) t |= 0; + else t |= 0x20; + sscape_pnp_write_codec ( devc, 0x01, t); + sscape_pnp_write_codec ( devc, 0x40 | 29 , 0x20); + outb(0, devc -> codec); + } + if (devc -> ic_type == IC_OPUS ) { + int i = sscape_read( devc, 9 ); + sscape_write( devc, 9, i | 3 ); + sscape_write( devc, 3, 0x40); + + if (check_region(0x228, 1)) { + outb(0, 0x228); + release_region(0x228,1); + } + sscape_write( devc, 3, (devc -> dma << 4) | 0x80); + sscape_write( devc, 9, i ); + } + + host_close ( devc ); + sscape_pnp_free_dma(devc); +} + +static int __init detect_sscape_pnp(sscape_info* devc) +{ + long i, irq_bits = 0xff; + unsigned int d; + + DDB(printk("Entered detect_sscape_pnp(%x)\n", devc->base)); + + if (check_region(devc->base, 8)) { + printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->base); + return 0; + } + + if (check_region(devc->codec, 2)) { + printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->codec); + return 0; + } + + if ( (inb( devc -> base + 2) & 0x78) != 0) return 0; + + d = inb ( devc -> base + 4) & 0xF0; + if ( (d & 0x80) != 0) return 0; + + if (d == 0) { + devc->codec_type = 1; + devc->ic_type = IC_ODIE; + } + else if ( (d & 0x60) != 0) { + devc->codec_type = 2; + devc->ic_type = IC_OPUS; + } + else if ( (d & 0x40) != 0) { + devc->codec_type = 2; + devc->ic_type = IC_ODIE; + } + else return 0; + + sscape_is_pnp = 1; + + outb(0xFA, devc -> base+4); + if ((inb( devc -> base+4) & 0x9F) != 0x0A) + return 0; + outb(0xFE, devc -> base+4); + if ( (inb(devc -> base+4) & 0x9F) != 0x0E) + return 0; + if ( (inb(devc -> base+5) & 0x9F) != 0x0E) + return 0; + + if (devc->codec_type == 2) { + if (devc -> codec != devc -> base + 8) + printk("soundscape warning: incorrect codec port specified\n"); + devc -> codec = devc -> base + 8; + d = 0x10 | (sscape_read(devc, 9) & 0xCF); + sscape_write(devc, 9, d); + sscape_write(devc, 6, 0x80); + } else { + //todo: check codec is not base + 8 + } + + d = (sscape_read(devc, 9) & 0x3F) | 0xC0; + sscape_write(devc, 9, d); + + for (i = 0; i < 550000; i++) + if ( !(inb(devc -> codec) & 0x80) ) break; + + d = inb(devc -> codec); + if (d & 0x80) + return 0; + if ( inb(devc -> codec + 2) == 0xFF) + return 0; + + sscape_write(devc, 9, sscape_read(devc, 9) & 0x3F ); + + d = inb(devc -> codec) & 0x80; + if ( d == 0) { + printk(KERN_INFO "soundscape: hardware detected\n"); + valid_interrupts = valid_interrupts_new; + } else { + printk(KERN_INFO "soundscape: board looks like media fx\n"); + valid_interrupts = valid_interrupts_old; + old_hardware = 1; + } + + sscape_write( devc, 9, 0xC0 | (sscape_read(devc, 9) & 0x3F) ); + + for (i = 0; i < 550000; i++) + if ( !(inb(devc -> codec) & 0x80)) + break; + + sscape_pnp_init_hw(devc); + + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (devc->codec_irq == valid_interrupts[i]) { + irq_bits = i; + break; + } + } + sscape_write(devc, GA_INTENA_REG, 0x00); + sscape_write(devc, GA_DMACFG_REG, 0x50); + sscape_write(devc, GA_DMAA_REG, 0x70); + sscape_write(devc, GA_DMAB_REG, 0x20); + sscape_write(devc, GA_INTCFG_REG, 0xf0); + sscape_write(devc, GA_CDCFG_REG, 0x89 | (devc->dma << 4) | (irq_bits << 1)); + + sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 0) | 0x20); + sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 1) | 0x20); + + return 1; +} + +static int __init probe_sscape(struct address_info *hw_config) +{ + + if (sscape_detected != 0 && sscape_detected != hw_config->io_base) + return 0; + + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->dma = hw_config->dma; + devc->osp = hw_config->osp; + +#ifdef SSCAPE_DEBUG1 + /* + * Temporary debugging aid. Print contents of the registers before + * changing them. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x (old value)\n", i, sscape_read(devc, i)); + } +#endif + devc->failed = 1; + + if (!detect_ga(devc)) { + if (detect_sscape_pnp(devc)) { + sscape_detected = hw_config->io_base; + return 1; + } + else return 0; + } + + if (old_hardware) /* Check that it's really an old Spea/Reveal card. */ + { + unsigned char tmp; + int cc; + + if (!((tmp = sscape_read(devc, GA_HMCTL_REG)) & 0xc0)) + { + sscape_write(devc, GA_HMCTL_REG, tmp | 0x80); + for (cc = 0; cc < 200000; ++cc) + inb(devc->base + ODIE_ADDR); + } + } + sscape_detected = hw_config->io_base; + return 1; +} + +static int __init probe_ss_ms_sound(struct address_info *hw_config) +{ + int i, irq_bits = 0xff; + int ad_flags = 0; + + if (devc->failed) + { + printk(KERN_ERR "soundscape: Card not detected\n"); + return 0; + } + if (devc->ok == 0) + { + printk(KERN_ERR "soundscape: Invalid initialization order.\n"); + return 0; + } + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (hw_config->irq == valid_interrupts[i]) + { + irq_bits = i; + break; + } + } + if (hw_config->irq > 15 || irq_bits == 0xff) + { + printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq); + return 0; + } + + if (!sscape_is_pnp) { + if (old_hardware) + ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ + return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); + } + else { + if (old_hardware) + ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ + else + ad_flags = 0x87654321; /* Tell that we have a soundscape pnp with 1845 chip */ + return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); + } +} + +static void __init attach_ss_ms_sound(struct address_info *hw_config) +{ + /* + * This routine configures the SoundScape card for use with the + * Win Sound System driver. The AD1848 codec interface uses the CD-ROM + * config registers of the "ODIE". + */ + + int i, irq_bits = 0xff; + + + if (!sscape_is_pnp) /*pnp is already setup*/ + { + /* + * Setup the DMA polarity. + */ + sscape_write(devc, GA_DMACFG_REG, 0x50); + + /* + * Take the gate-array off of the DMA channel. + */ + sscape_write(devc, GA_DMAB_REG, 0x20); + + /* + * Init the AD1848 (CD-ROM) config reg. + */ + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (hw_config->irq == valid_interrupts[i]) + { + irq_bits = i; + break; + } + } + sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1)); + } + + if (hw_config->irq == devc->irq) + printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n"); + + hw_config->slots[0] = ad1848_init( + sscape_is_pnp ? "SoundScape" : "SoundScape PNP", + hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, + 0, + devc->osp, + THIS_MODULE); + + + if (hw_config->slots[0] != -1) /* The AD1848 driver installed itself */ + { + audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations; + devc->codec_audiodev = hw_config->slots[0]; + devc->my_audiodev = hw_config->slots[0]; + + /* Set proper routings here (what are they) */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); + } + +#ifdef SSCAPE_DEBUG5 + /* + * Temporary debugging aid. Print contents of the registers + * after the AD1848 device has been initialized. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x\n", i, sscape_read(devc, i)); + } +#endif + +} + +static void __exit unload_sscape(struct address_info *hw_config) +{ + release_region(devc->base + 2, 6); + unload_mpu401(hw_config); +} + +static void __exit unload_ss_ms_sound(struct address_info *hw_config) +{ + ad1848_unload(hw_config->io_base, + hw_config->irq, + devc->dma, + devc->dma, + 0); + sound_unload_audiodev(hw_config->slots[0]); +} + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int __initdata spea = -1; +static int __initdata mss = 0; +static int __initdata dma = -1; +static int __initdata irq = -1; +static int __initdata io = -1; +static int __initdata mpu_irq = -1; +static int __initdata mpu_io = -1; + +MODULE_PARM(dma, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(io, "i"); +MODULE_PARM(spea, "i"); /* spea=0/1 set the old_hardware */ +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(mss, "i"); + +static int __init init_sscape(void) +{ + printk(KERN_INFO "Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.irq = irq; + cfg.dma = dma; + cfg.io_base = io; + + cfg_mpu.irq = mpu_irq; + cfg_mpu.io_base = mpu_io; + /* WEH - Try to get right dma channel */ + cfg_mpu.dma = dma; + + devc->codec = cfg.io_base; + devc->codec_irq = cfg.irq; + devc->codec_type = 0; + devc->ic_type = 0; + devc->raw_buf = NULL; + + if (cfg.dma == -1 || cfg.irq == -1 || cfg.io_base == -1) { + printk(KERN_ERR "DMA, IRQ, and IO port must be specified.\n"); + return -EINVAL; + } + + if (cfg_mpu.irq == -1 && cfg_mpu.io_base != -1) { + printk(KERN_ERR "MPU_IRQ must be specified if MPU_IO is set.\n"); + return -EINVAL; + } + + if(spea != -1) { + old_hardware = spea; + printk(KERN_INFO "Forcing %s hardware support.\n", + spea?"new":"old"); + } + if (probe_sscape(&cfg_mpu) == 0) + return -ENODEV; + + attach_sscape(&cfg_mpu); + + mss = probe_ss_ms_sound(&cfg); + + if (mss) + attach_ss_ms_sound(&cfg); + + return 0; +} + +static void __exit cleanup_sscape(void) +{ + if (mss) + unload_ss_ms_sound(&cfg); + unload_sscape(&cfg_mpu); +} + +module_init(init_sscape); +module_exit(cleanup_sscape); + +#ifndef MODULE +static int __init setup_sscape(char *str) +{ + /* io, irq, dma, mpu_io, mpu_irq */ + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + mpu_io = ints[4]; + mpu_irq = ints[5]; + + return 1; +} + +__setup("sscape=", setup_sscape); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/sys_timer.c b/sound/oss/sys_timer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/sys_timer.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,287 @@ +/* + * sound/sys_timer.c + * + * The default timer for the Level 2 sequencer interface + * Uses the (1/HZ sec) timer of kernel. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Andrew Veliath : adapted tmr2ticks from level 1 sequencer (avoid overflow) + */ +#include "sound_config.h" + +static volatile int opened = 0, tmr_running = 0; +static volatile time_t tmr_offs, tmr_ctr; +static volatile unsigned long ticks_offs; +static volatile int curr_tempo, curr_timebase; +static volatile unsigned long curr_ticks; +static volatile unsigned long next_event_time; +static unsigned long prev_event_time; + +static void poll_def_tmr(unsigned long dummy); + + +static struct timer_list def_tmr = +{function: poll_def_tmr}; + +static unsigned long +tmr2ticks(int tmr_value) +{ + /* + * Convert timer ticks to MIDI ticks + */ + + unsigned long tmp; + unsigned long scale; + + /* tmr_value (ticks per sec) * + 1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */ + tmp = tmr_value * (1000000 / HZ); + scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ + return (tmp + scale / 2) / scale; +} + +static void +poll_def_tmr(unsigned long dummy) +{ + + if (opened) + { + + { + def_tmr.expires = (1) + jiffies; + add_timer(&def_tmr); + }; + + if (tmr_running) + { + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); + + if (curr_ticks >= next_event_time) + { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } + } + } +} + +static void +tmr_reset(void) +{ + unsigned long flags; + + save_flags(flags); + cli(); + tmr_offs = 0; + ticks_offs = 0; + tmr_ctr = 0; + next_event_time = (unsigned long) -1; + prev_event_time = 0; + curr_ticks = 0; + restore_flags(flags); +} + +static int +def_tmr_open(int dev, int mode) +{ + if (opened) + return -EBUSY; + + tmr_reset(); + curr_tempo = 60; + curr_timebase = 100; + opened = 1; + + ; + + { + def_tmr.expires = (1) + jiffies; + add_timer(&def_tmr); + }; + + return 0; +} + +static void +def_tmr_close(int dev) +{ + opened = tmr_running = 0; + del_timer(&def_tmr);; +} + +static int +def_tmr_event(int dev, unsigned char *event) +{ + unsigned char cmd = event[1]; + unsigned long parm = *(int *) &event[4]; + + switch (cmd) + { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + + time = parm; + next_event_time = prev_event_time = time; + + return TIMER_ARMED; + } + break; + + case TMR_START: + tmr_reset(); + tmr_running = 1; + break; + + case TMR_STOP: + tmr_running = 0; + break; + + case TMR_CONTINUE: + tmr_running = 1; + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 360) + parm = 360; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = parm; + } + break; + + case TMR_ECHO: + seq_copy_to_input(event, 8); + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static unsigned long +def_tmr_get_time(int dev) +{ + if (!opened) + return 0; + + return curr_ticks; +} + +/* same as sound_timer.c:timer_ioctl!? */ +static int def_tmr_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val; + + switch (cmd) { + case SNDCTL_TMR_SOURCE: + return __put_user(TMR_INTERNAL, (int *)arg); + + case SNDCTL_TMR_START: + tmr_reset(); + tmr_running = 1; + return 0; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + return 0; + + case SNDCTL_TMR_CONTINUE: + tmr_running = 1; + return 0; + + case SNDCTL_TMR_TIMEBASE: + if (__get_user(val, (int *)arg)) + return -EFAULT; + if (val) { + if (val < 1) + val = 1; + if (val > 1000) + val = 1000; + curr_timebase = val; + } + return __put_user(curr_timebase, (int *)arg); + + case SNDCTL_TMR_TEMPO: + if (__get_user(val, (int *)arg)) + return -EFAULT; + if (val) { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = val; + reprogram_timer(); + } + return __put_user(curr_tempo, (int *)arg); + + case SNDCTL_SEQ_CTRLRATE: + if (__get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) /* Can't change */ + return -EINVAL; + val = ((curr_tempo * curr_timebase) + 30) / 60; + return __put_user(val, (int *)arg); + + case SNDCTL_SEQ_GETTIME: + return __put_user(curr_ticks, (int *)arg); + + case SNDCTL_TMR_METRONOME: + /* NOP */ + break; + + default:; + } + return -EINVAL; +} + +static void +def_tmr_arm(int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + + next_event_time = prev_event_time = time; + + return; +} + +struct sound_timer_operations default_sound_timer = +{ + owner: THIS_MODULE, + info: {"System clock", 0}, + priority: 0, /* Priority */ + devlink: 0, /* Local device link */ + open: def_tmr_open, + close: def_tmr_close, + event: def_tmr_event, + get_time: def_tmr_get_time, + ioctl: def_tmr_ioctl, + arm_timer: def_tmr_arm +}; diff -Nru a/sound/oss/trident.c b/sound/oss/trident.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/trident.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,4231 @@ +/* + * OSS driver for Linux 2.4.x for + * + * Trident 4D-Wave + * SiS 7018 + * ALi 5451 + * Tvia/IGST CyberPro 5050 + * + * Driver: Alan Cox + * + * Built from: + * Low level code: from ALSA + * Framework: Thomas Sailer + * Extended by: Zach Brown + * + * Hacked up by: + * Aaron Holtzman + * Ollie Lho SiS 7018 Audio Core Support + * Ching-Ling Lee ALi 5451 Audio Core Support + * Matt Wu ALi 5451 Audio Core Support + * Peter Wächtler CyberPro5050 support + * + * + * 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. + * + * History + * v0.14.9d + * October 8 2001 Arnaldo Carvalho de Melo + * use set_current_state, properly release resources on failure in + * trident_probe, get rid of check_region + * v0.14.9c + * August 10 2001 Peter Wächtler + * added support for Tvia (formerly Integraphics/IGST) CyberPro5050 + * this chip is often found in settop boxes (combined video+audio) + * v0.14.9b + * Switch to static inline not extern inline (gcc 3) + * v0.14.9a + * Aug 6 2001 Alan Cox + * 0.14.9 crashed on rmmod due to a timer/bh left running. Simplified + * the existing logic (the BH doesnt help as ac97 is lock_irqsave) + * and used del_timer_sync to clean up + * Fixed a problem where the ALi change broke my generic card + * v0.14.9 + * Jul 10 2001 Matt Wu + * Add H/W Volume Control + * v0.14.8a + * July 7 2001 Alan Cox + * Moved Matt Wu's ac97 register cache into the card structure + * v0.14.8 + * Apr 30 2001 Matt Wu + * Set EBUF1 and EBUF2 to still mode + * Add dc97/ac97 reset function + * Fix power management: ali_restore_regs + * unreleased + * Mar 09 2001 Matt Wu + * Add cache for ac97 access + * v0.14.7 + * Feb 06 2001 Matt Wu + * Fix ac97 initialization + * Fix bug: an extra tail will be played when playing + * Jan 05 2001 Matt Wu + * Implement multi-channels and S/PDIF in support for ALi 1535+ + * v0.14.6 + * Nov 1 2000 Ching-Ling Lee + * Fix the bug of memory leak when switching 5.1-channels to 2 channels. + * Add lock protection into dynamic changing format of data. + * Oct 18 2000 Ching-Ling Lee + * 5.1-channels support for ALi + * June 28 2000 Ching-Ling Lee + * S/PDIF out/in(playback/record) support for ALi 1535+, using /proc to be selected by user + * Simple Power Management support for ALi + * v0.14.5 May 23 2000 Ollie Lho + * Misc bug fix from the Net + * v0.14.4 May 20 2000 Aaron Holtzman + * Fix kfree'd memory access in release + * Fix race in open while looking for a free virtual channel slot + * remove open_wait wq (which appears to be unused) + * v0.14.3 May 10 2000 Ollie Lho + * fixed a small bug in trident_update_ptr, xmms 1.0.1 no longer uses 100% CPU + * v0.14.2 Mar 29 2000 Ching-Ling Lee + * Add clear to silence advance in trident_update_ptr + * fix invalid data of the end of the sound + * v0.14.1 Mar 24 2000 Ching-Ling Lee + * ALi 5451 support added, playback and recording O.K. + * ALi 5451 originally developed and structured based on sonicvibes, and + * suggested to merge into this file by Alan Cox. + * v0.14 Mar 15 2000 Ollie Lho + * 5.1 channel output support with channel binding. What's the Matrix ? + * v0.13.1 Mar 10 2000 Ollie Lho + * few minor bugs on dual codec support, needs more testing + * v0.13 Mar 03 2000 Ollie Lho + * new pci_* for 2.4 kernel, back ported to 2.2 + * v0.12 Feb 23 2000 Ollie Lho + * Preliminary Recording support + * v0.11.2 Feb 19 2000 Ollie Lho + * removed incomplete full-dulplex support + * v0.11.1 Jan 28 2000 Ollie Lho + * small bug in setting sample rate for 4d-nx (reported by Aaron) + * v0.11 Jan 27 2000 Ollie Lho + * DMA bug, scheduler latency, second try + * v0.10 Jan 24 2000 Ollie Lho + * DMA bug fixed, found kernel scheduling problem + * v0.09 Jan 20 2000 Ollie Lho + * Clean up of channel register access routine (prepare for channel binding) + * v0.08 Jan 14 2000 Ollie Lho + * Isolation of AC97 codec code + * v0.07 Jan 13 2000 Ollie Lho + * Get rid of ugly old low level access routines (e.g. CHRegs.lp****) + * v0.06 Jan 11 2000 Ollie Lho + * Preliminary support for dual (more ?) AC97 codecs + * v0.05 Jan 08 2000 Luca Montecchiani + * adapt to 2.3.x new __setup/__init call + * v0.04 Dec 31 1999 Ollie Lho + * Multiple Open, using Middle Loop Interrupt to smooth playback + * v0.03 Dec 24 1999 Ollie Lho + * mem leak in prog_dmabuf and dealloc_dmabuf removed + * v0.02 Dec 15 1999 Ollie Lho + * SiS 7018 support added, playback O.K. + * v0.01 Alan Cox et. al. + * Initial Release in kernel 2.3.30, does not work + * + * ToDo + * Clean up of low level channel register access code. (done) + * Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done) + * Dual AC97 codecs support (done) + * Recording support (done) + * Mmap support + * "Channel Binding" ioctl extension (done) + * new pci device driver interface for 2.4 kernel (done) + * + * Lock order (high->low) + * lock - hardware lock + * open_sem - guard opens + * sem - guard dmabuf, write re-entry etc + */ + +#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 + +#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC +#include +#endif + +#include "trident.h" + +#include + +#define DRIVER_VERSION "0.14.9d" + +/* magic numbers to protect our data structures */ +#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */ +#define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */ + +#define TRIDENT_DMA_MASK 0x3fffffff /* DMA buffer mask for pci_alloc_consist */ +#define ALI_DMA_MASK 0xffffffff /* ALI Tridents lack the 30-bit limitation */ + +#define NR_HW_CH 32 + +/* maxinum nuber of AC97 codecs connected, AC97 2.0 defined 4, but 7018 and 4D-NX only + have 2 SDATA_IN lines (currently) */ +#define NR_AC97 2 + +/* minor number of /dev/swmodem (temporary, experimental) */ +#define SND_DEV_SWMODEM 7 + +static const unsigned ali_multi_channels_5_1[] = { /*ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL,*/ ALI_CENTER_CHANNEL, ALI_LEF_CHANNEL, ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL}; + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n"; + +enum { + TRIDENT_4D_DX = 0, + TRIDENT_4D_NX, + SIS_7018, + ALI_5451, + CYBER5050 +}; + +static char * card_names[] = { + "Trident 4DWave DX", + "Trident 4DWave NX", + "SiS 7018 PCI Audio", + "ALi Audio Accelerator", + "Tvia/IGST CyberPro 5050" +}; + +static struct pci_device_id trident_pci_tbl [] __devinitdata = { + {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_DX}, + {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_NX}, + {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018}, + {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI_5451}, + { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5050, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, CYBER5050}, + {0,} +}; + +MODULE_DEVICE_TABLE (pci, trident_pci_tbl); + +/* "software" or virtual channel, an instance of opened /dev/dsp */ +struct trident_state { + unsigned int magic; + struct trident_card *card; /* Card info */ + + /* file mode */ + mode_t open_mode; + + /* virtual channel number */ + int virt; + + struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable; + + /* hardware channel */ + struct trident_channel *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 comsumed 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 */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned update_flag; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + + } dmabuf; + + /* 5.1channels */ + struct trident_state *other_states[4]; + int multi_channels_adjust_count; + unsigned chans_num; + unsigned fmt_flag:1; + /* Guard against mmap/write/read races */ + struct semaphore sem; + +}; + +/* hardware channels */ +struct trident_channel { + int num; /* channel number */ + u32 lba; /* Loop Begine Address, where dma buffer starts */ + u32 eso; /* End Sample Offset, wehre dma buffer ends (in the unit of samples) */ + u32 delta; /* delta value, sample rate / 48k for playback, 48k/sample rate for recording */ + u16 attribute; /* control where PCM data go and come */ + u16 fm_vol; + u32 control; /* signed/unsigned, 8/16 bits, mono/stereo */ +}; + +struct trident_pcm_bank_address { + u32 start; + u32 stop; + u32 aint; + u32 aint_en; +}; +static struct trident_pcm_bank_address bank_a_addrs = +{ + T4D_START_A, + T4D_STOP_A, + T4D_AINT_A, + T4D_AINTEN_A +}; +static struct trident_pcm_bank_address bank_b_addrs = +{ + T4D_START_B, + T4D_STOP_B, + T4D_AINT_B, + T4D_AINTEN_B +}; +struct trident_pcm_bank { + /* register addresses to control bank operations */ + struct trident_pcm_bank_address *addresses; + /* each bank has 32 channels */ + u32 bitmap; /* channel allocation bitmap */ + struct trident_channel channels[32]; +}; + +struct trident_card { + unsigned int magic; + + /* We keep trident cards in a linked list */ + struct trident_card *next; + + /* single open lock mechanism, only used for recording */ + struct semaphore open_sem; + + /* The trident has a certain amount of cross channel interaction + so we use a single per card lock */ + spinlock_t lock; + + /* PCI device stuff */ + struct pci_dev * pci_dev; + u16 pci_id; + u8 revision; + + /* soundcore stuff */ + int dev_audio; + + /* structures for abstraction of hardware facilities, codecs, banks and channels*/ + struct ac97_codec *ac97_codec[NR_AC97]; + struct trident_pcm_bank banks[NR_BANKS]; + struct trident_state *states[NR_HW_CH]; + + /* hardware resources */ + unsigned long iobase; + u32 irq; + + /* Function support */ + struct trident_channel *(*alloc_pcm_channel)(struct trident_card *); + struct trident_channel *(*alloc_rec_pcm_channel)(struct trident_card *); + void (*free_pcm_channel)(struct trident_card *, unsigned int chan); + void (*address_interrupt)(struct trident_card *); + + /* Added by Matt Wu 01-05-2001 for spdif in */ + int multi_channel_use_count; + int rec_channel_use_count; + u16 mixer_regs[64][NR_AC97]; /* Made card local by Alan */ + int mixer_regs_ready; + + /* Added for hardware volume control */ + int hwvolctl; + struct timer_list timer; +}; + +/* table to map from CHANNELMASK to channel attribute for SiS 7018 */ +static u16 mask2attr [] = +{ + PCM_LR, PCM_LR, SURR_LR, CENTER_LFE, + HSET, MIC, MODEM_LINE1, MODEM_LINE2, + I2S_LR, SPDIF_LR +}; +/* table to map from channel attribute to CHANNELMASK for SiS 7018 */ +static int attr2mask [] = { + DSP_BIND_MODEM1, DSP_BIND_MODEM2, DSP_BIND_FRONT, DSP_BIND_HANDSET, + DSP_BIND_I2S, DSP_BIND_CENTER_LFE, DSP_BIND_SURR, DSP_BIND_SPDIF +}; + +/* Added by Matt Wu 01-05-2001 for spdif in */ +static int ali_close_multi_channels(void); +static void ali_delay(struct trident_card *card,int interval); +static void ali_detect_spdif_rate(struct trident_card *card); + +static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val); +static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg); + +static struct trident_card *devs; + +static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val); +static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg); + +static int trident_open_mixdev(struct inode *inode, struct file *file); +static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg); + +static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val); +static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg); +static void ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate); +static void ali_enable_special_channel(struct trident_state *stat); +static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card); +static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card); +static void ali_restore_regs(struct trident_card *card); +static void ali_save_regs(struct trident_card *card); +static int trident_suspend(struct pci_dev *dev, u32 unused); +static int trident_resume(struct pci_dev *dev); +static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel); +static int ali_setup_multi_channels(struct trident_card *card, int chan_nums); +static unsigned int ali_get_spdif_in_rate(struct trident_card *card); +static void ali_setup_spdif_in(struct trident_card *card); +static void ali_disable_spdif_in(struct trident_card *card); +static void ali_disable_special_channel(struct trident_card *card, int ch); +static void ali_setup_spdif_out(struct trident_card *card, int flag); +static int ali_write_5_1(struct trident_state *state, const char *buffer,int cnt_for_multi_channel, unsigned int *copy_count, unsigned int *state_cnt); +static int ali_allocate_other_states_resources(struct trident_state *state, int chan_nums); +static void ali_free_other_states_resources(struct trident_state *state); + + +/* save registers for ALi Power Management */ +static struct ali_saved_registers { + unsigned long global_regs[ALI_GLOBAL_REGS]; + unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS]; + unsigned mixer_regs[ALI_MIXER_REGS]; +} ali_registers; + +#define seek_offset(dma_ptr, buffer, cnt, offset, copy_count) (dma_ptr) += (offset); \ + (buffer) += (offset); \ + (cnt) -= (offset); \ + (copy_count) += (offset); + +#define lock_set_fmt(state) {spin_lock_irqsave(&state->card->lock, flags); \ + if (state->fmt_flag) { \ + spin_unlock_irqrestore(&state->card->lock, flags); \ + return -EFAULT; \ + } \ + state->fmt_flag = 1; \ + spin_unlock_irqrestore(&state->card->lock, flags);} + +#define unlock_set_fmt(state) {spin_lock_irqsave(&state->card->lock, flags); \ + state->fmt_flag = 0; \ + spin_unlock_irqrestore(&state->card->lock, flags);} + +static int trident_enable_loop_interrupts(struct trident_card * card) +{ + u32 global_control; + + global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR)); + + switch (card->pci_id) + { + case PCI_DEVICE_ID_SI_7018: + global_control |= (ENDLP_IE | MIDLP_IE| BANK_B_EN); + break; + case PCI_DEVICE_ID_ALI_5451: + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + case PCI_DEVICE_ID_INTERG_5050: + global_control |= (ENDLP_IE | MIDLP_IE); + break; + default: + return FALSE; + } + + outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); + +#ifdef DEBUG + printk("trident: Enable Loop Interrupts, globctl = 0x%08X\n", + inl(TRID_REG(card, T4D_LFO_GC_CIR))); +#endif + return (TRUE); +} + +static int trident_disable_loop_interrupts(struct trident_card * card) +{ + u32 global_control; + + global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR)); + global_control &= ~(ENDLP_IE | MIDLP_IE); + outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); + +#ifdef DEBUG + printk("trident: Disabled Loop Interrupts, globctl = 0x%08X\n", + global_control); +#endif + return (TRUE); +} + +static void trident_enable_voice_irq(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint_en; + + reg = inl(TRID_REG(card, addr)); + reg |= mask; + outl(reg, TRID_REG(card, addr)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, addr)); + printk("trident: enabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); +#endif +} + +static void trident_disable_voice_irq(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint_en; + + reg = inl(TRID_REG(card, addr)); + reg &= ~mask; + outl(reg, TRID_REG(card, addr)); + + /* Ack the channel in case the interrupt was set before we disable it. */ + outl(mask, TRID_REG(card, bank->addresses->aint)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, addr)); + printk("trident: disabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); +#endif +} + +static void trident_start_voice(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 addr = bank->addresses->start; + +#ifdef DEBUG + u32 reg; +#endif + + outl(mask, TRID_REG(card, addr)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, addr)); + printk("trident: start voice on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_START_B? "START_B":"START_A",reg,addr); +#endif +} + +static void trident_stop_voice(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 addr = bank->addresses->stop; + +#ifdef DEBUG + u32 reg; +#endif + + outl(mask, TRID_REG(card, addr)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, addr)); + printk("trident: stop voice on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_STOP_B? "STOP_B":"STOP_A",reg,addr); +#endif +} + +static u32 trident_get_interrupt_mask (struct trident_card * card, unsigned int channel) +{ + struct trident_pcm_bank *bank = &card->banks[channel]; + u32 addr = bank->addresses->aint; + return inl(TRID_REG(card, addr)); +} + +static int trident_check_channel_interrupt(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + u32 reg = trident_get_interrupt_mask (card, channel >> 5); + +#ifdef DEBUG + if (reg & mask) + printk("trident: channel %d has interrupt, %s = 0x%08x\n", + channel,reg==T4D_AINT_B? "AINT_B":"AINT_A", reg); +#endif + return (reg & mask) ? TRUE : FALSE; +} + +static void trident_ack_channel_interrupt(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint; + + reg = inl(TRID_REG(card, addr)); + reg &= mask; + outl(reg, TRID_REG(card, addr)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, T4D_AINT_B)); + printk("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n", + channel, reg); +#endif +} + +static struct trident_channel * trident_alloc_pcm_channel(struct trident_card *card) +{ + struct trident_pcm_bank *bank; + int idx; + + bank = &card->banks[BANK_B]; + + for (idx = 31; idx >= 0; idx--) { + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx + 32; + return channel; + } + } + + /* no more free channels available */ + printk(KERN_ERR "trident: no more channels available on Bank B.\n"); + return NULL; +} + +static void trident_free_pcm_channel(struct trident_card *card, unsigned int channel) +{ + int bank; + unsigned char b; + + if (channel < 31 || channel > 63) + return; + + if (card->pci_id == PCI_DEVICE_ID_TRIDENT_4DWAVE_DX || + card->pci_id == PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) { + b = inb (TRID_REG(card, T4D_REC_CH)); + if ((b & ~0x80) == channel) + outb(0x0, TRID_REG(card, T4D_REC_CH)); + } + + bank = channel >> 5; + channel = channel & 0x1f; + + card->banks[bank].bitmap &= ~(1 << (channel)); +} + +static struct trident_channel * cyber_alloc_pcm_channel(struct trident_card *card) +{ + struct trident_pcm_bank *bank; + int idx; + + /* The cyberpro 5050 has only 32 voices and one bank */ + /* .. at least they are not documented (if you want to call that + * crap documentation), perhaps broken ? */ + + bank = &card->banks[BANK_A]; + + for (idx = 31; idx >= 0; idx--) { + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx; + return channel; + } + } + + /* no more free channels available */ + printk(KERN_ERR "cyberpro5050: no more channels available on Bank A.\n"); + return NULL; +} + +static void cyber_free_pcm_channel(struct trident_card *card, unsigned int channel) +{ + if (channel > 31) + return; + card->banks[BANK_A].bitmap &= ~(1 << (channel)); +} + +static inline void cyber_outidx(int port,int idx,int data) +{ + outb(idx,port); + outb(data,port+1); +} + +static inline int cyber_inidx(int port,int idx) +{ + outb(idx,port); + return inb(port+1); +} + +static int cyber_init_ritual(struct trident_card *card) +{ + /* some black magic, taken from SDK samples */ + /* remove this and nothing will work */ + int portDat; + int ret = 0; + unsigned long flags; + + /* + * Keep interrupts off for the configure - we don't want to + * clash with another cyberpro config event + */ + + save_flags(flags); + cli(); + portDat = cyber_inidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE); + /* enable, if it was disabled */ + if( (portDat & CYBER_BMSK_AUENZ) != CYBER_BMSK_AUENZ_ENABLE ) { + printk(KERN_INFO "cyberpro5050: enabling audio controller\n" ); + cyber_outidx( CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE, + portDat | CYBER_BMSK_AUENZ_ENABLE ); + /* check again if hardware is enabled now */ + portDat = cyber_inidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE); + } + if( (portDat & CYBER_BMSK_AUENZ) != CYBER_BMSK_AUENZ_ENABLE ) + { + printk(KERN_ERR "cyberpro5050: initAudioAccess: no success\n" ); + ret = -1; + } + else + { + cyber_outidx( CYBER_PORT_AUDIO, CYBER_IDX_IRQ_ENABLE, CYBER_BMSK_AUDIO_INT_ENABLE ); + cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x01 ); + cyber_outidx( CYBER_PORT_AUDIO, 0xba, 0x20 ); + cyber_outidx( CYBER_PORT_AUDIO, 0xbb, 0x08 ); + cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x02 ); + cyber_outidx( CYBER_PORT_AUDIO, 0xb3, 0x06 ); + cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x00 ); + } + restore_flags(flags); + return ret; +} + +/* called with spin lock held */ + +static int trident_load_channel_registers(struct trident_card *card, u32 *data, unsigned int channel) +{ + int i; + + if (channel > 63) + return FALSE; + + /* select hardware channel to write */ + outb(channel, TRID_REG(card, T4D_LFO_GC_CIR)); + + /* Output the channel registers, but don't write register + three to an ALI chip. */ + for (i = 0; i < CHANNEL_REGS; i++) { + if (i == 3 && card->pci_id == PCI_DEVICE_ID_ALI_5451) + continue; + outl(data[i], TRID_REG(card, CHANNEL_START + 4*i)); + } + if (card->pci_id == PCI_DEVICE_ID_ALI_5451 || + card->pci_id == PCI_DEVICE_ID_INTERG_5050) { + outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF1)); + outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF2)); + } + return TRUE; +} + +/* called with spin lock held */ +static int trident_write_voice_regs(struct trident_state *state) +{ + unsigned int data[CHANNEL_REGS + 1]; + struct trident_channel *channel; + + channel = state->dmabuf.channel; + + data[1] = channel->lba; + data[4] = channel->control; + + switch (state->card->pci_id) + { + case PCI_DEVICE_ID_ALI_5451: + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = 0; + break; + case PCI_DEVICE_ID_SI_7018: + case PCI_DEVICE_ID_INTERG_5050: + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = (channel->attribute << 16) | (channel->fm_vol & 0xffff); + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = channel->fm_vol & 0xffff; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + data[0] = (channel->delta << 24); + data[2] = ((channel->delta << 16) & 0xff000000) | (channel->eso & 0x00ffffff); + data[3] = channel->fm_vol & 0xffff; + break; + default: + return FALSE; + } + + return trident_load_channel_registers(state->card, data, channel->num); +} + +static int compute_rate_play(u32 rate) +{ + int delta; + /* We special case 44100 and 8000 since rounding with the equation + does not give us an accurate enough value. For 11025 and 22050 + the equation gives us the best answer. All other frequencies will + also use the equation. JDW */ + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + rate) / 48000) & 0x0000ffff; + return delta; +} + +static int compute_rate_rec(u32 rate) +{ + int delta; + + if (rate == 44100) + delta = 0x116a; + else if (rate == 8000) + delta = 0x6000; + else if (rate == 48000) + delta = 0x1000; + else + delta = ((48000 << 12) / rate) & 0x0000ffff; + + return delta; +} +/* set playback sample rate */ +static unsigned int trident_set_dac_rate(struct trident_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + dmabuf->rate = rate; + dmabuf->channel->delta = compute_rate_play(rate); + + trident_write_voice_regs(state); + +#ifdef DEBUG + printk("trident: called trident_set_dac_rate : rate = %d\n", rate); +#endif + + return rate; +} + +/* set recording sample rate */ +static unsigned int trident_set_adc_rate(struct trident_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + dmabuf->rate = rate; + dmabuf->channel->delta = compute_rate_rec(rate); + + trident_write_voice_regs(state); + +#ifdef DEBUG + printk("trident: called trident_set_adc_rate : rate = %d\n", rate); +#endif + return rate; +} + +/* prepare channel attributes for playback */ +static void trident_play_setup(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct trident_channel *channel = dmabuf->channel; + + channel->lba = dmabuf->dma_handle; + channel->delta = compute_rate_play(dmabuf->rate); + + channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; + channel->eso -= 1; + + if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) { + channel->attribute = 0; + if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451) { + if ((channel->num == ALI_SPDIF_IN_CHANNEL) || (channel->num == ALI_PCM_IN_CHANNEL)) + ali_disable_special_channel(state->card, channel->num); + else if ((inl(TRID_REG(state->card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE) + && (channel->num == ALI_SPDIF_OUT_CHANNEL)) + { + ali_set_spdif_out_rate(state->card, state->dmabuf.rate); + state->dmabuf.channel->delta = 0x1000; + } + } + } + + channel->fm_vol = 0x0; + + channel->control = CHANNEL_LOOP; + if (dmabuf->fmt & TRIDENT_FMT_16BIT) { + /* 16-bits */ + channel->control |= CHANNEL_16BITS; + /* signed */ + channel->control |= CHANNEL_SIGNED; + } + if (dmabuf->fmt & TRIDENT_FMT_STEREO) + /* stereo */ + channel->control |= CHANNEL_STEREO; +#ifdef DEBUG + printk("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); +#endif + trident_write_voice_regs(state); +} + +/* prepare channel attributes for recording */ +static void trident_rec_setup(struct trident_state *state) +{ + u16 w; + u8 bval; + + struct trident_card *card = state->card; + struct dmabuf *dmabuf = &state->dmabuf; + struct trident_channel *channel = dmabuf->channel; + unsigned int rate; + + /* Enable AC-97 ADC (capture) */ + switch (card->pci_id) + { + case PCI_DEVICE_ID_ALI_5451: + ali_enable_special_channel(state); + break; + case PCI_DEVICE_ID_SI_7018: + /* for 7018, the ac97 is always in playback/record (duplex) mode */ + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + /* enable and set record channel */ + outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH)); + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + w = inw(TRID_REG(card, T4D_MISCINT)); + outw(w | 0x1000, TRID_REG(card, T4D_MISCINT)); + /* enable and set record channel */ + outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH)); + break; + case PCI_DEVICE_ID_INTERG_5050: + /* don't know yet, using special channel 22 in GC1(0xd4)? */ + break; + default: + return; + } + + channel->lba = dmabuf->dma_handle; + channel->delta = compute_rate_rec(dmabuf->rate); + if ((card->pci_id == PCI_DEVICE_ID_ALI_5451) && (channel->num == ALI_SPDIF_IN_CHANNEL)) { + rate = ali_get_spdif_in_rate(card); + if (rate == 0) + { + printk(KERN_WARNING "trident: ALi 5451 S/PDIF input setup error!\n"); + rate = 48000; + } + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL)); + if (bval & 0x10) + { + outb(bval,TRID_REG(card,ALI_SPDIF_CTRL)); + printk(KERN_WARNING "trident: cleared ALi 5451 S/PDIF parity error flag.\n"); + } + + if (rate != 48000) + channel->delta = ((rate << 12) / dmabuf->rate) & 0x0000ffff; + } + + channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; + channel->eso -= 1; + + if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) { + channel->attribute = 0; + } + + channel->fm_vol = 0x0; + + channel->control = CHANNEL_LOOP; + if (dmabuf->fmt & TRIDENT_FMT_16BIT) { + /* 16-bits */ + channel->control |= CHANNEL_16BITS; + /* signed */ + channel->control |= CHANNEL_SIGNED; + } + if (dmabuf->fmt & TRIDENT_FMT_STEREO) + /* stereo */ + channel->control |= CHANNEL_STEREO; +#ifdef DEBUG + printk("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); +#endif + trident_write_voice_regs(state); +} + +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ +static inline unsigned trident_get_dma_addr(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 cso; + + if (!dmabuf->enable) + return 0; + + outb(dmabuf->channel->num, TRID_REG(state->card, T4D_LFO_GC_CIR)); + + switch (state->card->pci_id) + { + case PCI_DEVICE_ID_ALI_5451: + case PCI_DEVICE_ID_SI_7018: + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + case PCI_DEVICE_ID_INTERG_5050: + /* 16 bits ESO, CSO for 7018 and DX */ + cso = inw(TRID_REG(state->card, CH_DX_CSO_ALPHA_FMS + 2)); + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + /* 24 bits ESO, CSO for NX */ + cso = inl(TRID_REG(state->card, CH_NX_DELTA_CSO)) & 0x00ffffff; + break; + default: + return 0; + } + +#ifdef DEBUG + printk("trident: trident_get_dma_addr: chip reported channel: %d, " + "cso = 0x%04x\n", + dmabuf->channel->num, cso); +#endif + /* ESO and CSO are in units of Samples, convert to byte offset */ + cso <<= sample_shift[dmabuf->fmt]; + + return (cso % dmabuf->dmasize); +} + +/* Stop recording (lock held) */ +static inline void __stop_adc(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; + + dmabuf->enable &= ~ADC_RUNNING; + trident_stop_voice(card, chan_num); + trident_disable_voice_irq(card, chan_num); +} + +static void stop_adc(struct trident_state *state) +{ + struct trident_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static void start_adc(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + if ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) && dmabuf->ready) { + dmabuf->enable |= ADC_RUNNING; + trident_enable_voice_irq(card, chan_num); + trident_start_voice(card, chan_num); + } + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop playback (lock held) */ +static inline void __stop_dac(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; + + dmabuf->enable &= ~DAC_RUNNING; + trident_stop_voice(card, chan_num); + if (state->chans_num == 6) { + trident_stop_voice(card, state->other_states[0]->dmabuf.channel->num); + trident_stop_voice(card, state->other_states[1]->dmabuf.channel->num); + trident_stop_voice(card, state->other_states[2]->dmabuf.channel->num); + trident_stop_voice(card, state->other_states[3]->dmabuf.channel->num); + } + trident_disable_voice_irq(card, chan_num); +} + +static void stop_dac(struct trident_state *state) +{ + struct trident_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __stop_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static void start_dac(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) { + dmabuf->enable |= DAC_RUNNING; + trident_enable_voice_irq(card, chan_num); + trident_start_voice(card, chan_num); + if (state->chans_num == 6) { + trident_start_voice(card, state->other_states[0]->dmabuf.channel->num); + trident_start_voice(card, state->other_states[1]->dmabuf.channel->num); + trident_start_voice(card, state->other_states[2]->dmabuf.channel->num); + trident_start_voice(card, state->other_states[3]->dmabuf.channel->num); + } + } + spin_unlock_irqrestore(&card->lock, flags); +} + +#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* allocate DMA buffer, playback and recording buffer should be allocated seperately */ +static int alloc_dmabuf(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + void *rawbuf = NULL; + int order; + struct page *page, *pend; + + /* alloc as big a chunk as we can, FIXME: is this necessary ?? */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, + PAGE_SIZE << order, + &dmabuf->dma_handle))) + break; + if (!rawbuf) + return -ENOMEM; + +#ifdef DEBUG + printk("trident: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); +#endif + + 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++) + mem_map_reserve(page); + + return 0; +} + +/* free DMA buffer */ +static void dealloc_dmabuf(struct trident_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++) + mem_map_unreserve(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 trident_state *state, unsigned rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned bytepersec; + struct trident_state *s = state; + unsigned bufsize, dma_nums; + unsigned long flags; + int ret, i, order; + struct page *page, *pend; + + lock_set_fmt(state); + if (state->chans_num == 6) + dma_nums = 5; + else dma_nums = 1; + + for (i = 0; i < dma_nums; i++) { + if (i > 0) { + s = state->other_states[i - 1]; + dmabuf = &s->dmabuf; + dmabuf->fmt = state->dmabuf.fmt; + dmabuf->rate = state->dmabuf.rate; + } + + spin_lock_irqsave(&s->card->lock, flags); + dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0; + dmabuf->count = dmabuf->error = 0; + spin_unlock_irqrestore(&s->card->lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf) { + if (i == 0) { + if ((ret = alloc_dmabuf(state))) { + unlock_set_fmt(state); + return ret; + } + } + else { + if ((order = state->dmabuf.buforder - 1) >= DMABUF_MINORDER) { + dmabuf->rawbuf = pci_alloc_consistent(state->card->pci_dev, + PAGE_SIZE << order, + &dmabuf->dma_handle); + } + if (!dmabuf->rawbuf) { + free_pages((unsigned long)state->dmabuf.rawbuf, state->dmabuf.buforder); + state->dmabuf.rawbuf = NULL; + i-=2; + for (; i >= 0; i--) { + pci_free_consistent(state->card->pci_dev, + PAGE_SIZE << state->other_states[i]->dmabuf.buforder, + state->other_states[i]->dmabuf.rawbuf, + state->other_states[i]->dmabuf.dma_handle); + } + unlock_set_fmt(state); + return -ENOMEM; + } + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->buforder = order; + pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + } + /* FIXME: figure out all this OSS fragment stuff */ + bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt]; + bufsize = PAGE_SIZE << dmabuf->buforder; + if (dmabuf->ossfragshift) { + if ((1000 << dmabuf->ossfragshift) < bytepersec) + dmabuf->fragshift = ld2(bytepersec/1000); + else + dmabuf->fragshift = dmabuf->ossfragshift; + } else { + /* lets hand out reasonable big ass buffers by default */ + dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2); + } + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) { + dmabuf->fragshift--; + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + } + dmabuf->fragsize = 1 << dmabuf->fragshift; + if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag) + dmabuf->numfrag = dmabuf->ossmaxfrags; + dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt]; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + + memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80, + dmabuf->dmasize); + + spin_lock_irqsave(&s->card->lock, flags); + if (rec) { + trident_rec_setup(s); + } else { + trident_play_setup(s); + } + spin_unlock_irqrestore(&s->card->lock, flags); + + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + +#ifdef DEBUG + printk("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); +#endif + } + unlock_set_fmt(state); + return 0; +} + +/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e. + |------------|------------| or |xxxxxxxxxxxx|------------| or |xxxxxxxxxxxx|xxxxxxxxxxxx| + but we almost always get this + |xxxxxx------|------------| or |xxxxxxxxxxxx|xxxxx-------| + so we have to clear the tail space to "silence" + |xxxxxx000000|------------| or |xxxxxxxxxxxx|xxxxxx000000| +*/ +static void trident_clear_tail(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned swptr; + unsigned char silence = (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80; + unsigned int len; + unsigned long flags; + + spin_lock_irqsave(&state->card->lock, flags); + swptr = dmabuf->swptr; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize) + return; + + if (swptr < dmabuf->dmasize/2) + len = dmabuf->dmasize/2 - swptr; + else + len = dmabuf->dmasize - swptr; + + memset(dmabuf->rawbuf + swptr, silence, len); + if(state->card->pci_id != PCI_DEVICE_ID_ALI_5451) + { + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr += len; + dmabuf->count += len; + spin_unlock_irqrestore(&state->card->lock, flags); + } + + /* restart the dma machine in case it is halted */ + start_dac(state); +} + +static int drain_dac(struct trident_state *state, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long tmo; + int count; + unsigned long diff = 0; + + if (dmabuf->mapped || !dmabuf->ready) + return 0; + + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + /* It seems that we have to set the current state to TASK_INTERRUPTIBLE + every time to make the process really go to sleep */ + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&state->card->lock, flags); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (count <= 0) + break; + + if (signal_pending(current)) + break; + + if (nonblock) { + remove_wait_queue(&dmabuf->wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + + /* No matter how much data is left in the buffer, we have to wait until + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451 || + state->card->pci_id == PCI_DEVICE_ID_INTERG_5050) + { + diff = dmabuf->swptr - trident_get_dma_addr(state) + dmabuf->dmasize ; + diff = diff % (dmabuf->dmasize); + tmo = (diff * HZ) / dmabuf->rate; + } + else + { + tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; + } + tmo >>= sample_shift[dmabuf->fmt]; + if (!schedule_timeout(tmo ? tmo : 1) && tmo){ + break; + } + } + remove_wait_queue(&dmabuf->wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + + return 0; +} + +/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ +static void trident_update_ptr(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned hwptr, swptr; + int clear_cnt = 0; + int diff; + unsigned char silence; + unsigned half_dmasize; + + /* update hardware pointer */ + hwptr = trident_get_dma_addr(state); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + + /* error handling and process wake up for ADC */ + if (dmabuf->enable == ADC_RUNNING) { + if (dmabuf->mapped) { + dmabuf->count -= diff; + if (dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else { + dmabuf->count += diff; + + if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun, we have no way to recover + it here, just stop the machine and let the process force hwptr + and swptr to sync */ + __stop_adc(state); + dmabuf->error++; + } + if (dmabuf->count < (signed)dmabuf->dmasize/2) + wake_up(&dmabuf->wait); + } + } + + /* error handling and process wake up for DAC */ + if (dmabuf->enable == DAC_RUNNING) { + if (dmabuf->mapped) { + dmabuf->count += diff; + if (dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else { + dmabuf->count -= diff; + + if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun, we have no way to recover + it here, just stop the machine and let the process force hwptr + and swptr to sync */ + __stop_dac(state); + dmabuf->error++; + } + else if (!dmabuf->endcleared) { + swptr = dmabuf->swptr; + silence = (dmabuf->fmt & TRIDENT_FMT_16BIT ? 0 : 0x80); + if (dmabuf->update_flag & ALI_ADDRESS_INT_UPDATE) { + /* We must clear end data of 1/2 dmabuf if needed. + According to 1/2 algorithm of Address Engine Interrupt, + check the validation of the data of half dmasize. */ + half_dmasize = dmabuf->dmasize / 2; + if ((diff = hwptr - half_dmasize) < 0 ) + diff = hwptr; + if ((dmabuf->count + diff) < half_dmasize) { + //there is invalid data in the end of half buffer + if ((clear_cnt = half_dmasize - swptr) < 0) + clear_cnt += half_dmasize; + //clear the invalid data + memset (dmabuf->rawbuf + swptr, + silence, clear_cnt); + if (state->chans_num == 6) { + clear_cnt = clear_cnt / 2; + swptr = swptr / 2; + memset (state->other_states[0]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[1]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[2]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[3]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + } + dmabuf->endcleared = 1; + } + } else if (dmabuf->count < (signed) dmabuf->fragsize) { + clear_cnt = dmabuf->fragsize; + if ((swptr + clear_cnt) > dmabuf->dmasize) + clear_cnt = dmabuf->dmasize - swptr; + memset (dmabuf->rawbuf + swptr, silence, clear_cnt); + if (state->chans_num == 6) { + clear_cnt = clear_cnt / 2; + swptr = swptr / 2; + memset (state->other_states[0]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[1]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[2]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[3]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + } + dmabuf->endcleared = 1; + } + } + /* trident_update_ptr is called by interrupt handler or by process via + ioctl/poll, we only wake up the waiting process when we have more + than 1/2 buffer free (always true for interrupt handler) */ + if (dmabuf->count < (signed)dmabuf->dmasize/2) + wake_up(&dmabuf->wait); + } + } + dmabuf->update_flag &= ~ALI_ADDRESS_INT_UPDATE; +} + +static void trident_address_interrupt(struct trident_card *card) +{ + int i; + struct trident_state *state; + + /* Update the pointers for all channels we are running. */ + /* FIXME: should read interrupt status only once */ + for (i = 0; i < NR_HW_CH; i++) { + if (trident_check_channel_interrupt(card, 63 - i)) { + trident_ack_channel_interrupt(card, 63 - i); + if ((state = card->states[i]) != NULL) { + trident_update_ptr(state); + } else { + printk("trident: spurious channel irq %d.\n", + 63 - i); + trident_stop_voice(card, 63 - i); + trident_disable_voice_irq(card, 63 - i); + } + } + } +} + +static void ali_hwvol_control(struct trident_card *card, int opt) +{ + u16 dwTemp, volume[2], mute, diff, *pVol[2]; + + dwTemp = ali_ac97_read(card->ac97_codec[0], 0x02); + mute = dwTemp & 0x8000; + volume[0] = dwTemp & 0x001f; + volume[1] = (dwTemp & 0x1f00) >> 8; + if (volume[0] < volume [1]) { + pVol[0] = &volume[0]; + pVol[1] = &volume[1]; + } else { + pVol[1] = &volume[0]; + pVol[0] = &volume[1]; + } + diff = *(pVol[1]) - *(pVol[0]); + + if (opt == 1) { // MUTE + dwTemp ^= 0x8000; + ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp); + } else if (opt == 2) { // Down + if (mute) + return; + if (*(pVol[1]) < 0x001f) { + (*pVol[1])++; + *(pVol[0]) = *(pVol[1]) - diff; + } + dwTemp &= 0xe0e0; + dwTemp |= (volume[0]) | (volume[1] << 8); + ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp); + card->ac97_codec[0]->mixer_state[0] = ((32-volume[0])*25/8) | (((32-volume[1])*25/8) << 8); + } else if (opt == 4) { // Up + if (mute) + return; + if (*(pVol[0]) >0) { + (*pVol[0])--; + *(pVol[1]) = *(pVol[0]) + diff; + } + dwTemp &= 0xe0e0; + dwTemp |= (volume[0]) | (volume[1] << 8); + ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp); + card->ac97_codec[0]->mixer_state[0] = ((32-volume[0])*25/8) | (((32-volume[1])*25/8) << 8); + } + else + { + /* Nothing needs doing */ + } +} + +/* + * Re-enable reporting of vol change after 0.1 seconds + */ + +static void ali_timeout(unsigned long ptr) +{ + struct trident_card *card = (struct trident_card *)ptr; + u16 temp = 0; + + /* Enable GPIO IRQ (MISCINT bit 18h)*/ + temp = inw(TRID_REG(card, T4D_MISCINT + 2)); + temp |= 0x0004; + outw(temp, TRID_REG(card, T4D_MISCINT + 2)); +} + +/* + * Set up the timer to clear the vol change notification + */ + +static void ali_set_timer(struct trident_card *card) +{ + /* Add Timer Routine to Enable GPIO IRQ */ + del_timer(&card->timer); /* Never queue twice */ + card->timer.function = ali_timeout; + card->timer.data = (unsigned long) card; + card->timer.expires = jiffies + HZ/10; + add_timer(&card->timer); +} + +/* + * Process a GPIO event + */ + +static void ali_queue_task(struct trident_card *card, int opt) +{ + u16 temp; + + /* Disable GPIO IRQ (MISCINT bit 18h)*/ + temp = inw(TRID_REG(card, T4D_MISCINT + 2)); + temp &= (u16)(~0x0004); + outw(temp, TRID_REG(card, T4D_MISCINT + 2)); + + /* Adjust the volume */ + ali_hwvol_control(card, opt); + + /* Set the timer for 1/10th sec */ + ali_set_timer(card); +} + +static void cyber_address_interrupt(struct trident_card *card) +{ + int i,irq_status; + struct trident_state *state; + + /* Update the pointers for all channels we are running. */ + /* FIXED: read interrupt status only once */ + irq_status=inl(TRID_REG(card, T4D_AINT_A) ); +#ifdef DEBUG + printk("cyber_address_interrupt: irq_status 0x%X\n",irq_status); +#endif + for (i = 0; i < NR_HW_CH; i++) { + if (irq_status & ( 1 << (31 - i)) ) { + + /* clear bit by writing a 1, zeroes are ignored */ + outl( (1 <<(31-i)), TRID_REG(card, T4D_AINT_A)); + +#ifdef DEBUG + printk("cyber_interrupt: channel %d\n", 31-i); +#endif + if ((state = card->states[i]) != NULL) { + trident_update_ptr(state); + } else { + printk("cyber5050: spurious channel irq %d.\n", + 31 - i); + trident_stop_voice(card, 31 - i); + trident_disable_voice_irq(card, 31 - i); + } + } + } +} + +static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct trident_card *card = (struct trident_card *)dev_id; + u32 event; + u32 gpio; + + spin_lock(&card->lock); + event = inl(TRID_REG(card, T4D_MISCINT)); + +#ifdef DEBUG + printk("trident: trident_interrupt called, MISCINT = 0x%08x\n", event); +#endif + + if (event & ADDRESS_IRQ) { + card->address_interrupt(card); + } + + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) + { + /* GPIO IRQ (H/W Volume Control) */ + event = inl(TRID_REG(card, T4D_MISCINT)); + if (event & (1<<25)) { + gpio = inl(TRID_REG(card, ALI_GPIO)); + if (!timer_pending(&card->timer)) + ali_queue_task(card, gpio&0x07); + } + event = inl(TRID_REG(card, T4D_MISCINT)); + outl(event | (ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(card, T4D_MISCINT)); + spin_unlock(&card->lock); + return; + } + + /* manually clear interrupt status, bad hardware design, blame T^2 */ + outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), + TRID_REG(card, T4D_MISCINT)); + spin_unlock(&card->lock); +} + +/* 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 trident_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + +#ifdef DEBUG + printk("trident: trident_read called, count = %d\n", count); +#endif + + VALIDATE_STATE(state); + if (ppos != &file->f_pos) + return -ESPIPE; + + if (dmabuf->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + down(&state->sem); + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + goto out; + + while (count > 0) { + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->count > (signed) dmabuf->dmasize) { + /* buffer overrun, we are recovering from sleep_on_timeout, + resync hwptr and swptr, make process flush the buffer */ + dmabuf->count = dmabuf->dmasize; + dmabuf->swptr = dmabuf->hwptr; + } + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count < cnt) + cnt = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + unsigned long tmo; + /* buffer is empty, start the dma machine and wait for data to be + recorded */ + start_adc(state); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + goto out; + } + + up(&state->sem); + /* No matter how much space left in the buffer, we have to wait until + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); + tmo >>= sample_shift[dmabuf->fmt]; + /* 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 (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { +#ifdef DEBUG + printk(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); +#endif + /* 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)) { + if(!ret) ret = -ERESTARTSYS; + goto out; + } + down(&state->sem); + if(dmabuf->mapped) + { + if(!ret) + ret = -ENXIO; + goto out; + } + continue; + } + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { + if (!ret) ret = -EFAULT; + goto out; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + spin_unlock_irqrestore(&state->card->lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(state); + } +out: + up(&state->sem); + 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 trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned int state_cnt; + unsigned int copy_count; + +#ifdef DEBUG + printk("trident: trident_write called, count = %d\n", count); +#endif + VALIDATE_STATE(state); + if (ppos != &file->f_pos) + return -ESPIPE; + + /* + * Guard against an mmap or ioctl while writing + */ + + down(&state->sem); + + if (dmabuf->mapped) + { + ret = -ENXIO; + goto out; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + goto out; + + if (!access_ok(VERIFY_READ, buffer, count)) + { + ret= -EFAULT; + goto out; + } + + ret = 0; + + while (count > 0) { + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->count < 0) { + /* buffer underrun, we are recovering from sleep_on_timeout, + resync hwptr and swptr */ + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + } + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count + cnt > dmabuf->dmasize) + cnt = dmabuf->dmasize - dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + unsigned long tmo; + /* buffer is full, start the dma machine and wait for data to be + played */ + start_dac(state); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + goto out; + } + /* No matter how much data left in the buffer, we have to wait until + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + lock_set_fmt(state); + tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); + tmo >>= sample_shift[dmabuf->fmt]; + unlock_set_fmt(state); + up(&state->sem); + + /* 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. */ + if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { +#ifdef DEBUG + printk(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); +#endif + /* a buffer underrun, we delay the recovery until next time the + while loop begin and we REALLY have data to play */ + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto out; + } + down(&state->sem); + if(dmabuf->mapped) + { + if(!ret) + ret = -ENXIO; + goto out; + } + continue; + } + lock_set_fmt(state); + if (state->chans_num == 6) { + copy_count = 0; + state_cnt = 0; + if (ali_write_5_1(state, buffer, cnt, ©_count, &state_cnt) == -EFAULT) { + if (state_cnt){ + swptr = (swptr + state_cnt) % dmabuf->dmasize; + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr = swptr; + dmabuf->count += state_cnt; + dmabuf->endcleared = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + } + ret += copy_count; + if (!ret) ret = -EFAULT; + unlock_set_fmt(state); + goto out; + } + } + else { + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + unlock_set_fmt(state); + goto out; + } + state_cnt = cnt; + } + unlock_set_fmt(state); + + swptr = (swptr + state_cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr = swptr; + dmabuf->count += state_cnt; + dmabuf->endcleared = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(state); + } +out: + up(&state->sem); + return ret; +} + + +/* No kernel lock - we have our own spinlock */ +static unsigned int trident_poll(struct file *file, struct poll_table_struct *wait) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(state); + + /* + * Guard against a parallel poll and write causing multiple + * prog_dmabuf events + */ + + down(&state->sem); + + if (file->f_mode & FMODE_WRITE) { + if (!dmabuf->ready && prog_dmabuf(state, 0)) + { + up(&state->sem); + return 0; + } + poll_wait(file, &dmabuf->wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!dmabuf->ready && prog_dmabuf(state, 1)) + { + up(&state->sem); + return 0; + } + poll_wait(file, &dmabuf->wait, wait); + } + + up(&state->sem); + + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + if (file->f_mode & FMODE_READ) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (dmabuf->mapped) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&state->card->lock, flags); + + return mask; +} + +static int trident_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(state); + lock_kernel(); + + /* + * Lock against poll read write or mmap creating buffers. Also lock + * a read or write against an mmap. + */ + + down(&state->sem); + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(state, 0)) != 0) + goto out; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(state, 1)) != 0) + goto out; + } else + 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; + ret = 0; +out: + up(&state->sem); + unlock_kernel(); + return ret; +} + +static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret = 0; + + struct trident_card *card = state->card; + + VALIDATE_STATE(state); + mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) || + ((file->f_mode & FMODE_READ) && dmabuf->mapped); +#ifdef DEBUG + printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n", + _IOC_NR(cmd), arg ? *(int *)arg : 0); +#endif + + switch (cmd) + { + case OSS_GETVERSION: + ret = put_user(SOUND_VERSION, (int *)arg); + break; + + case SNDCTL_DSP_RESET: + /* FIXME: spin_lock ? */ + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + synchronize_irq(); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + synchronize_irq(); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + break; + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + ret = drain_dac(state, file->f_flags & O_NONBLOCK); + break; + + case SNDCTL_DSP_SPEED: /* set smaple rate */ + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + if (val >= 0) { + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + trident_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); + trident_set_adc_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + ret = put_user(dmabuf->rate, (int *)arg); + break; + + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + lock_set_fmt(state); + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + if (val) + dmabuf->fmt |= TRIDENT_FMT_STEREO; + else + dmabuf->fmt &= ~TRIDENT_FMT_STEREO; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + if (val) + dmabuf->fmt |= TRIDENT_FMT_STEREO; + else + dmabuf->fmt &= ~TRIDENT_FMT_STEREO; + } + unlock_set_fmt(state); + break; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(state, 0))) + ret = val; + else + ret = put_user(dmabuf->fragsize, (int *)arg); + break; + } + if (file->f_mode & FMODE_READ) { + if ((val = prog_dmabuf(state, 1))) + ret = val; + else + ret = put_user(dmabuf->fragsize, (int *)arg); + break; + } + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + ret = put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); + break; + + case SNDCTL_DSP_SETFMT: /* Select sample format */ + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + lock_set_fmt(state); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + if (val == AFMT_S16_LE) + dmabuf->fmt |= TRIDENT_FMT_16BIT; + else + dmabuf->fmt &= ~TRIDENT_FMT_16BIT; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + if (val == AFMT_S16_LE) + dmabuf->fmt |= TRIDENT_FMT_16BIT; + else + dmabuf->fmt &= ~TRIDENT_FMT_16BIT; + } + } + unlock_set_fmt(state); + ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + break; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + if (val != 0) { + lock_set_fmt(state); + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + + //prevent from memory leak + if ((state->chans_num > 2) && (state->chans_num != val)) { + ali_free_other_states_resources(state); + state->chans_num = 1; + } + + if (val >= 2) + { + + dmabuf->fmt |= TRIDENT_FMT_STEREO; + if ((val == 6) && (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)) { + + if( card->rec_channel_use_count > 0 ) + { + printk(KERN_ERR "trident: Record is working on the card!\n"); + ret = -EBUSY; + break; + } + + ret = ali_setup_multi_channels(state->card, 6); + if (ret < 0) { + unlock_set_fmt(state); + break; + } + down(&state->card->open_sem); + ret = ali_allocate_other_states_resources(state, 6); + if (ret < 0) { + up(&state->card->open_sem); + unlock_set_fmt(state); + break; + } + state->card->multi_channel_use_count ++; + up(&state->card->open_sem); + } + else val = 2; /*yield to 2-channels*/ + } + else + dmabuf->fmt &= ~TRIDENT_FMT_STEREO; + state->chans_num = val; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + if (val >= 2) { + if (!((file->f_mode & FMODE_WRITE) && (val == 6))) + val = 2; + dmabuf->fmt |= TRIDENT_FMT_STEREO; + } + else + dmabuf->fmt &= ~TRIDENT_FMT_STEREO; + state->chans_num = val; + } + unlock_set_fmt(state); + } + ret = put_user(val, (int *)arg); + break; + + case SNDCTL_DSP_POST: + /* Cause the working fragment to be output */ + break; + + case SNDCTL_DSP_SUBDIVIDE: + if (dmabuf->subdivision) + { + ret = -EINVAL; + break; + } + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + if (val != 1 && val != 2 && val != 4) + { + ret = -EINVAL; + break; + } + dmabuf->subdivision = val; + break; + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + + dmabuf->ossfragshift = val & 0xffff; + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + if (dmabuf->ossfragshift < 4) + dmabuf->ossfragshift = 4; + if (dmabuf->ossfragshift > 15) + dmabuf->ossfragshift = 15; + if (dmabuf->ossmaxfrags < 4) + dmabuf->ossmaxfrags = 4; + + break; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + { + ret = -EINVAL; + break; + } + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + { + ret = val; + break; + } + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->dmasize - dmabuf->count; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + break; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + { + ret = -EINVAL; + break; + } + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + { + ret = val; + break; + } + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->count; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + break; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + break; + + case SNDCTL_DSP_GETCAPS: + ret = put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND, + (int *)arg); + break; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && dmabuf->enable) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && dmabuf->enable) + val |= PCM_ENABLE_OUTPUT; + ret = put_user(val, (int *)arg); + break; + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + break; + start_adc(state); + } else + stop_adc(state); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + break; + start_dac(state); + } else + stop_dac(state); + } + break; + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + { + ret = -EINVAL; + break; + } + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + { + ret = val; + break; + } + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + if (dmabuf->mapped) + dmabuf->count &= dmabuf->fragsize-1; + spin_unlock_irqrestore(&state->card->lock, flags); + ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + break; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + { + ret = -EINVAL; + break; + } + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + { + ret = val; + break; + } + + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + if (dmabuf->mapped) + dmabuf->count &= dmabuf->fragsize-1; + spin_unlock_irqrestore(&state->card->lock, flags); + ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo))?-EFAULT:0; + break; + + case SNDCTL_DSP_SETDUPLEX: + ret = -EINVAL; + break; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + { + ret = -EINVAL; + break; + } + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + { + ret = val; + break; + } + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + val = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + ret = put_user(val, (int *)arg); + break; + + case SOUND_PCM_READ_RATE: + ret = put_user(dmabuf->rate, (int *)arg); + break; + + case SOUND_PCM_READ_CHANNELS: + ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + (int *)arg); + break; + + case SOUND_PCM_READ_BITS: + ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + break; + + case SNDCTL_DSP_GETCHANNELMASK: + ret = put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE, + (int *)arg); + break; + + case SNDCTL_DSP_BIND_CHANNEL: + if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) + { + ret = -EINVAL; + break; + } + + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + if (val == DSP_BIND_QUERY) { + val = dmabuf->channel->attribute | 0x3c00; + val = attr2mask[val >> 8]; + } else { + dmabuf->ready = 0; + if (file->f_mode & FMODE_READ) + dmabuf->channel->attribute = (CHANNEL_REC|SRC_ENABLE); + if (file->f_mode & FMODE_WRITE) + dmabuf->channel->attribute = (CHANNEL_SPC_PB|SRC_ENABLE); + dmabuf->channel->attribute |= mask2attr[ffs(val)]; + } + ret = put_user(val, (int *)arg); + break; + + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + default: + ret = -EINVAL; + break; + + } + return ret; +} + +static int trident_open(struct inode *inode, struct file *file) +{ + int i = 0; + int minor = minor(inode->i_rdev); + struct trident_card *card = devs; + struct trident_state *state = NULL; + struct dmabuf *dmabuf = NULL; + + /* Added by Matt Wu 01-05-2001 */ + if(file->f_mode & FMODE_READ) + { + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + if (card->multi_channel_use_count > 0) + return -EBUSY; + } + } + + /* find an available virtual channel (instance of /dev/dsp) */ + while (card != NULL) { + down(&card->open_sem); + if(file->f_mode & FMODE_READ) + { + /* Skip opens on cards that are in 6 channel mode */ + if (card->multi_channel_use_count > 0) + { + up(&card->open_sem); + card = card->next; + continue; + } + } + for (i = 0; i < NR_HW_CH; i++) { + if (card->states[i] == NULL) { + state = card->states[i] = (struct trident_state *) + kmalloc(sizeof(struct trident_state), GFP_KERNEL); + if (state == NULL) { + return -ENOMEM; + } + memset(state, 0, sizeof(struct trident_state)); + init_MUTEX(&state->sem); + dmabuf = &state->dmabuf; + goto found_virt; + } + } + up(&card->open_sem); + card = card->next; + } + /* no more virtual channel avaiable */ + if (!state) { + return -ENODEV; + } + found_virt: + /* found a free virtual channel, allocate hardware channels */ + if(file->f_mode & FMODE_READ) + dmabuf->channel = card->alloc_rec_pcm_channel(card); + else + dmabuf->channel = card->alloc_pcm_channel(card); + + if (dmabuf->channel == NULL) { + kfree (card->states[i]); + card->states[i] = NULL; + return -ENODEV; + } + + /* initialize the virtual channel */ + state->virt = i; + state->card = card; + state->magic = TRIDENT_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + file->private_data = state; + + /* 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 */ + if (file->f_mode & FMODE_WRITE) { + dmabuf->fmt &= ~TRIDENT_FMT_MASK; + if ((minor & 0x0f) == SND_DEV_DSP16) + dmabuf->fmt |= TRIDENT_FMT_16BIT; + dmabuf->ossfragshift = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + if (card->pci_id == PCI_DEVICE_ID_SI_7018) { + /* set default channel attribute to normal playback */ + dmabuf->channel->attribute = CHANNEL_PB; + } + trident_set_dac_rate(state, 8000); + } + + if (file->f_mode & FMODE_READ) { + /* FIXME: Trident 4d can only record in signed 16-bits stereo, 48kHz sample, + to be dealed with in trident_set_adc_rate() ?? */ + dmabuf->fmt &= ~TRIDENT_FMT_MASK; + if ((minor & 0x0f) == SND_DEV_DSP16) + dmabuf->fmt |= TRIDENT_FMT_16BIT; + dmabuf->ossfragshift = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + if (card->pci_id == PCI_DEVICE_ID_SI_7018) { + /* set default channel attribute to 0x8a80, record from + PCM L/R FIFO and mono = (left + right + 1)/2*/ + dmabuf->channel->attribute = + (CHANNEL_REC|PCM_LR|MONO_MIX); + } + trident_set_adc_rate(state, 8000); + + /* Added by Matt Wu 01-05-2001 */ + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) + card->rec_channel_use_count ++; + } + + state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&card->open_sem); + +#ifdef DEBUG + printk(KERN_ERR "trident: open virtual channel %d, hard channel %d\n", + state->virt, dmabuf->channel->num); +#endif + + return 0; +} + +static int trident_release(struct inode *inode, struct file *file) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct trident_card *card; + struct dmabuf *dmabuf; + unsigned long flags; + + lock_kernel(); + card = state->card; + dmabuf = &state->dmabuf; + VALIDATE_STATE(state); + + if (file->f_mode & FMODE_WRITE) { + trident_clear_tail(state); + drain_dac(state, file->f_flags & O_NONBLOCK); + } + +#ifdef DEBUG + printk(KERN_ERR "trident: closing virtual channel %d, hard channel %d\n", + state->virt, dmabuf->channel->num); +#endif + + /* stop DMA state machine and free DMA buffers/channels */ + down(&card->open_sem); + + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + lock_set_fmt(state); + + unlock_set_fmt(state); + dealloc_dmabuf(state); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); + + /* Added by Matt Wu */ + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + if (state->chans_num > 2) { + if (card->multi_channel_use_count-- < 0) + card->multi_channel_use_count = 0; + if (card->multi_channel_use_count == 0) + ali_close_multi_channels(); + ali_free_other_states_resources(state); + } + } + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dealloc_dmabuf(state); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); + + /* Added by Matt Wu */ + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + if( card->rec_channel_use_count-- < 0 ) + card->rec_channel_use_count = 0; + } + } + + card->states[state->virt] = NULL; + kfree(state); + + /* we're covered by the open_sem */ + up(&card->open_sem); + unlock_kernel(); + + return 0; +} + +static /*const*/ struct file_operations trident_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: trident_read, + write: trident_write, + poll: trident_poll, + ioctl: trident_ioctl, + mmap: trident_mmap, + open: trident_open, + release: trident_release, +}; + +/* trident specific AC97 functions */ +/* Write AC97 codec registers */ +static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val) +{ + struct trident_card *card = (struct trident_card *)codec->private_data; + unsigned int address, mask, busy; + unsigned short count = 0xffff; + unsigned long flags; + u32 data; + + data = ((u32) val) << 16; + + switch (card->pci_id) + { + default: + case PCI_DEVICE_ID_SI_7018: + address = SI_AC97_WRITE; + mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY; + if (codec->id) + mask |= SI_AC97_SECONDARY; + busy = SI_AC97_BUSY_WRITE; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + address = DX_ACR0_AC97_W; + mask = busy = DX_AC97_BUSY_WRITE; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + address = NX_ACR1_AC97_W; + mask = NX_AC97_BUSY_WRITE; + if (codec->id) + mask |= NX_AC97_WRITE_SECONDARY; + busy = NX_AC97_BUSY_WRITE; + break; + case PCI_DEVICE_ID_INTERG_5050: + address = SI_AC97_WRITE; + mask = busy = SI_AC97_BUSY_WRITE; + if (codec->id) + mask |= SI_AC97_SECONDARY; + break; + } + + spin_lock_irqsave(&card->lock, flags); + do { + if ((inw(TRID_REG(card, address)) & busy) == 0) + break; + } while (count--); + + + data |= (mask | (reg & AC97_REG_ADDR)); + + if (count == 0) { + printk(KERN_ERR "trident: AC97 CODEC write timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + return; + } + + outl(data, TRID_REG(card, address)); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* Read AC97 codec registers */ +static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg) +{ + struct trident_card *card = (struct trident_card *)codec->private_data; + unsigned int address, mask, busy; + unsigned short count = 0xffff; + unsigned long flags; + u32 data; + + switch (card->pci_id) + { + default: + case PCI_DEVICE_ID_SI_7018: + address = SI_AC97_READ; + mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY; + if (codec->id) + mask |= SI_AC97_SECONDARY; + busy = SI_AC97_BUSY_READ; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + address = DX_ACR1_AC97_R; + mask = busy = DX_AC97_BUSY_READ; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + if (codec->id) + address = NX_ACR3_AC97_R_SECONDARY; + else + address = NX_ACR2_AC97_R_PRIMARY; + mask = NX_AC97_BUSY_READ; + busy = NX_AC97_BUSY_READ | NX_AC97_BUSY_DATA; + break; + case PCI_DEVICE_ID_INTERG_5050: + address = SI_AC97_READ; + mask = busy = SI_AC97_BUSY_READ; + if (codec->id) + mask |= SI_AC97_SECONDARY; + break; + } + + data = (mask | (reg & AC97_REG_ADDR)); + + spin_lock_irqsave(&card->lock, flags); + outl(data, TRID_REG(card, address)); + do { + data = inl(TRID_REG(card, address)); + if ((data & busy) == 0) + break; + } while (count--); + spin_unlock_irqrestore(&card->lock, flags); + + if (count == 0) { + printk(KERN_ERR "trident: AC97 CODEC read timed out.\n"); + data = 0; + } + return ((u16) (data >> 16)); +} + +/* Write AC97 codec registers for ALi*/ +static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val) +{ + unsigned int address, mask; + unsigned int wCount1 = 0xffff; + unsigned int wCount2= 0xffff; + unsigned long chk1, chk2; + unsigned long flags; + u32 data; + + data = ((u32) val) << 16; + + if(!card) + BUG(); + + address = ALI_AC97_WRITE; + mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY; + if (secondary) + mask |= ALI_AC97_SECONDARY; + if (card->revision == ALI_5451_V02) + mask |= ALI_AC97_WRITE_MIXER_REGISTER; + + spin_lock_irqsave(&card->lock, flags); + while (wCount1--) { + if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_WRITE) == 0) { + data |= (mask | (reg & AC97_REG_ADDR)); + + chk1 = inl(TRID_REG(card, ALI_STIMER)); + chk2 = inl(TRID_REG(card, ALI_STIMER)); + while (wCount2-- && (chk1 == chk2)) + chk2 = inl(TRID_REG(card, ALI_STIMER)); + if (wCount2 == 0) { + spin_unlock_irqrestore(&card->lock, flags); + return; + } + outl(data, TRID_REG(card, address)); //write! + spin_unlock_irqrestore(&card->lock, flags); + return; //success + } + inw(TRID_REG(card, address)); //wait for a read cycle + } + + printk(KERN_ERR "ali: AC97 CODEC write timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + return; +} + +/* Read AC97 codec registers for ALi*/ +static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg) +{ + unsigned int address, mask; + unsigned int wCount1 = 0xffff; + unsigned int wCount2= 0xffff; + unsigned long chk1, chk2; + unsigned long flags; + u32 data; + + if(!card) + BUG(); + + address = ALI_AC97_READ; + if (card->revision == ALI_5451_V02) { + address = ALI_AC97_WRITE; + } + mask = ALI_AC97_READ_ACTION | ALI_AC97_AUDIO_BUSY; + if (secondary) + mask |= ALI_AC97_SECONDARY; + + spin_lock_irqsave(&card->lock, flags); + data = (mask | (reg & AC97_REG_ADDR)); + while (wCount1--) { + if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_READ) == 0) { + chk1 = inl(TRID_REG(card, ALI_STIMER)); + chk2 = inl(TRID_REG(card, ALI_STIMER)); + while (wCount2-- && (chk1 == chk2)) + chk2 = inl(TRID_REG(card, ALI_STIMER)); + if (wCount2 == 0) { + printk(KERN_ERR "ali: AC97 CODEC read timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + return 0; + } + outl(data, TRID_REG(card, address)); //read! + wCount2 = 0xffff; + while (wCount2--) { + if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_READ) == 0) { + data = inl(TRID_REG(card, address)); + spin_unlock_irqrestore(&card->lock, flags); + return ((u16) (data >> 16)); + } + } + } + inw(TRID_REG(card, address)); //wait a read cycle + } + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "ali: AC97 CODEC read timed out.\n"); + return 0; +} + +static void ali_enable_special_channel(struct trident_state *stat) +{ + struct trident_card *card = stat->card; + unsigned long s_channels; + + s_channels = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); + s_channels |= (1<dmabuf.channel->num); + outl(s_channels, TRID_REG(card, ALI_GLOBAL_CONTROL)); +} + +static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg) +{ + int id; + u16 data; + struct trident_card *card = NULL; + + /* Added by Matt Wu */ + if (!codec) + BUG(); + + card = (struct trident_card *)codec->private_data; + + if(!card->mixer_regs_ready) + return ali_ac97_get(card, codec->id, reg); + + if(codec->id) + id = 1; + else + id = 0; + + data = card->mixer_regs[reg/2][id]; + return data; +} + +static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) +{ + int id; + struct trident_card *card; + + /* Added by Matt Wu */ + if (!codec) + BUG(); + + card = (struct trident_card *)codec->private_data; + + if (!card->mixer_regs_ready) + { + ali_ac97_set(card, codec->id, reg, val); + return; + } + + if(codec->id) + id = 1; + else + id = 0; + + card->mixer_regs[reg/2][id] = val; + ali_ac97_set(card, codec->id, reg, val); +} + +/* +flag: ALI_SPDIF_OUT_TO_SPDIF_OUT + ALI_PCM_TO_SPDIF_OUT +*/ + +static void ali_setup_spdif_out(struct trident_card *card, int flag) +{ + unsigned long spdif; + unsigned char ch; + + char temp; + struct pci_dev *pci_dev = NULL; + + pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev); + if (pci_dev == NULL) + return; + pci_read_config_byte(pci_dev, 0x61, &temp); + temp |= 0x40; + pci_write_config_byte(pci_dev, 0x61, temp); + pci_read_config_byte(pci_dev, 0x7d, &temp); + temp |= 0x01; + pci_write_config_byte(pci_dev, 0x7d, temp); + pci_read_config_byte(pci_dev, 0x7e, &temp); + temp &= (~0x20); + temp |= 0x10; + pci_write_config_byte(pci_dev, 0x7e, temp); + + ch = inb(TRID_REG(card, ALI_SCTRL)); + outb(ch | ALI_SPDIF_OUT_ENABLE, TRID_REG(card, ALI_SCTRL)); + ch = inb(TRID_REG(card, ALI_SPDIF_CTRL)); + outb(ch & ALI_SPDIF_OUT_CH_STATUS, TRID_REG(card, ALI_SPDIF_CTRL)); + + if (flag & ALI_SPDIF_OUT_TO_SPDIF_OUT) { + spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif |= ALI_SPDIF_OUT_CH_ENABLE; + spdif &= ALI_SPDIF_OUT_SEL_SPDIF; + outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif = inw(TRID_REG(card, ALI_SPDIF_CS)); + if (flag & ALI_SPDIF_OUT_NON_PCM) + spdif |= 0x0002; + else spdif &= (~0x0002); + outw(spdif, TRID_REG(card, ALI_SPDIF_CS)); + } + else { + spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif |= ALI_SPDIF_OUT_SEL_PCM; + outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); + } +} + +static void ali_disable_special_channel(struct trident_card *card, int ch) +{ + unsigned long sc; + + sc = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); + sc &= ~(1 << ch); + outl(sc, TRID_REG(card, ALI_GLOBAL_CONTROL)); +} + +static void ali_disable_spdif_in(struct trident_card *card) +{ + unsigned long spdif; + + spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif &= (~ALI_SPDIF_IN_SUPPORT); + outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); + + ali_disable_special_channel(card, ALI_SPDIF_IN_CHANNEL); +} + +static void ali_setup_spdif_in(struct trident_card *card) +{ + unsigned long spdif; + + //Set SPDIF IN Supported + spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif |= ALI_SPDIF_IN_SUPPORT; + outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); + + //Set SPDIF IN Rec + spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif |= ALI_SPDIF_IN_CH_ENABLE; + outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); + + spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL)); + spdif |= ALI_SPDIF_IN_CH_STATUS; + outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL)); +/* + spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL)); + spdif |= ALI_SPDIF_IN_FUNC_ENABLE; + outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL)); +*/ +} + +static void ali_delay(struct trident_card *card,int interval) +{ + unsigned long begintimer,currenttimer; + + begintimer = inl(TRID_REG(card, ALI_STIMER)); + currenttimer = inl(TRID_REG(card, ALI_STIMER)); + + while (currenttimer < begintimer + interval) + currenttimer = inl(TRID_REG(card, ALI_STIMER)); +} + +static void ali_detect_spdif_rate(struct trident_card *card) +{ + u16 wval = 0; + u16 count = 0; + u8 bval = 0, R1 = 0, R2 = 0; + + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL)); + bval |= 0x02; + outb(bval,TRID_REG(card,ALI_SPDIF_CTRL)); + + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1)); + bval |= 0x1F; + outb(bval,TRID_REG(card,ALI_SPDIF_CTRL + 1)); + + while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000) + { + count ++; + + ali_delay(card, 6); + + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1)); + R1 = bval & 0x1F; + } + + if (count > 50000) + { + printk(KERN_WARNING "trident: Error in ali_detect_spdif_rate!\n"); + return; + } + + count = 0; + + while (count <= 50000) + { + count ++; + + ali_delay(card, 6); + + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1)); + R2 = bval & 0x1F; + + if(R2 != R1) + R1 = R2; + else + break; + } + + if (count > 50000) + { + printk(KERN_WARNING "trident: Error in ali_detect_spdif_rate!\n"); + return; + } + + switch (R2) + { + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + wval = inw(TRID_REG(card,ALI_SPDIF_CTRL + 2)); + wval &= 0xE0F0; + wval |= (u16)0x09 << 8 | (u16)0x05; + outw(wval,TRID_REG(card,ALI_SPDIF_CTRL + 2)); + + bval = inb(TRID_REG(card,ALI_SPDIF_CS +3)) & 0xF0; + outb(bval|0x02,TRID_REG(card,ALI_SPDIF_CS + 3)); + break; + + case 0x12: + wval = inw(TRID_REG(card,ALI_SPDIF_CTRL + 2)); + wval &= 0xE0F0; + wval |= (u16)0x0E << 8 | (u16)0x08; + outw(wval,TRID_REG(card,ALI_SPDIF_CTRL + 2)); + + bval = inb(TRID_REG(card,ALI_SPDIF_CS +3)) & 0xF0; + outb(bval|0x03,TRID_REG(card,ALI_SPDIF_CS + 3)); + break; + + default: + break; + } + +} + +static unsigned int ali_get_spdif_in_rate(struct trident_card *card) +{ + u32 dwRate = 0; + u8 bval = 0; + + ali_detect_spdif_rate(card); + + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL)); + bval &= 0x7F; + bval |= 0x40; + outb(bval,TRID_REG(card,ALI_SPDIF_CTRL)); + + bval = inb(TRID_REG(card,ALI_SPDIF_CS + 3)); + bval &= 0x0F; + + switch (bval) + { + case 0: + dwRate = 44100; + break; + case 1: + dwRate = 48000; + break; + case 2: + dwRate = 32000; + break; + default: + // Error occurs + break; + } + + return dwRate; + +} + +static int ali_close_multi_channels(void) +{ + char temp = 0; + struct pci_dev *pci_dev = NULL; + + 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_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); + + return 0; +} + +static int ali_setup_multi_channels(struct trident_card *card, int chan_nums) +{ + unsigned long dwValue; + char temp = 0; + struct pci_dev *pci_dev = NULL; + + 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_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, (int)0xB8,(u8) temp); + if (chan_nums == 6) { + dwValue = inl(TRID_REG(card, ALI_SCTRL)) | 0x000f0000; + outl(dwValue, TRID_REG(card, ALI_SCTRL)); + mdelay(4); + dwValue = inl(TRID_REG(card, ALI_SCTRL)); + if (dwValue & 0x2000000) { + ali_ac97_write(card->ac97_codec[0], 0x02, 8080); + ali_ac97_write(card->ac97_codec[0], 0x36, 0); + ali_ac97_write(card->ac97_codec[0], 0x38, 0); + ali_ac97_write(card->ac97_codec[1], 0x36, 0); + ali_ac97_write(card->ac97_codec[1], 0x38, 0); + ali_ac97_write(card->ac97_codec[1], 0x02, 0x0606); + ali_ac97_write(card->ac97_codec[1], 0x18, 0x0303); + ali_ac97_write(card->ac97_codec[1], 0x74, 0x3); + return 1; + } + } + return -EINVAL; +} + +static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel) +{ + int bank; + + if (channel > 31) + return; + + bank = channel >> 5; + channel = channel & 0x1f; + + card->banks[bank].bitmap &= ~(1 << (channel)); +} + +static int ali_allocate_other_states_resources(struct trident_state *state, int chan_nums) +{ + struct trident_card *card = state->card; + struct trident_state *s; + int i, state_count = 0; + struct trident_pcm_bank *bank; + struct trident_channel *channel; + + bank = &card->banks[BANK_A]; + + if (chan_nums == 6) { + for(i = 0;(i < ALI_CHANNELS) && (state_count != 4); i++) { + if (!card->states[i]) { + if (!(bank->bitmap & (1 << ali_multi_channels_5_1[state_count]))) { + bank->bitmap |= (1 << ali_multi_channels_5_1[state_count]); + channel = &bank->channels[ali_multi_channels_5_1[state_count]]; + channel->num = ali_multi_channels_5_1[state_count]; + } + else { + state_count--; + for (; state_count >= 0; state_count--) { + kfree(state->other_states[state_count]); + ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); + } + return -EBUSY; + } + s = card->states[i] = (struct trident_state *) + kmalloc(sizeof(struct trident_state), GFP_KERNEL); + if (!s) { + ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); + state_count--; + for (; state_count >= 0; state_count--) { + ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); + kfree(state->other_states[state_count]); + } + return -ENOMEM; + } + memset(s, 0, sizeof(struct trident_state)); + + s->dmabuf.channel = channel; + s->dmabuf.ossfragshift = s->dmabuf.ossmaxfrags = s->dmabuf.subdivision = 0; + init_waitqueue_head(&s->dmabuf.wait); + s->magic = card->magic; + s->card = card; + s->virt = i; + ali_enable_special_channel(s); + state->other_states[state_count++] = s; + } + } + + if (state_count != 4) { + state_count--; + for (; state_count >= 0; state_count--) { + kfree(state->other_states[state_count]); + ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); + } + return -EBUSY; + } + } + return 0; +} + +static void ali_save_regs(struct trident_card *card) +{ + unsigned long flags; + int i, j; + + save_flags(flags); + cli(); + + ali_registers.global_regs[0x2c] = inl(TRID_REG(card,T4D_MISCINT)); + //ali_registers.global_regs[0x20] = inl(TRID_REG(card,T4D_START_A)); + ali_registers.global_regs[0x21] = inl(TRID_REG(card,T4D_STOP_A)); + + //disable all IRQ bits + outl(ALI_DISABLE_ALL_IRQ, TRID_REG(card, T4D_MISCINT)); + + for (i = 1; i < ALI_MIXER_REGS; i++) + ali_registers.mixer_regs[i] = ali_ac97_read (card->ac97_codec[0], i*2); + + for (i = 0; i < ALI_GLOBAL_REGS; i++) + { + if ((i*4 == T4D_MISCINT) || (i*4 == T4D_STOP_A)) + continue; + ali_registers.global_regs[i] = inl(TRID_REG(card, i*4)); + } + + for (i = 0; i < ALI_CHANNELS; i++) + { + outb(i,TRID_REG(card, T4D_LFO_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + ali_registers.channel_regs[i][j] = inl(TRID_REG(card, j*4 + 0xe0)); + } + + //Stop all HW channel + outl(ALI_STOP_ALL_CHANNELS, TRID_REG(card, T4D_STOP_A)); + + restore_flags(flags); +} + +static void ali_restore_regs(struct trident_card *card) +{ + unsigned long flags; + int i, j; + + save_flags(flags); + cli(); + + for (i = 1; i < ALI_MIXER_REGS; i++) + ali_ac97_write(card->ac97_codec[0], i*2, ali_registers.mixer_regs[i]); + + for (i = 0; i < ALI_CHANNELS; i++) + { + outb(i,TRID_REG(card, T4D_LFO_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + outl(ali_registers.channel_regs[i][j], TRID_REG(card, j*4 + 0xe0)); + } + + for (i = 0; i < ALI_GLOBAL_REGS; i++) + { + if ((i*4 == T4D_MISCINT) || (i*4 == T4D_STOP_A) || (i*4 == T4D_START_A)) + continue; + outl(ali_registers.global_regs[i], TRID_REG(card, i*4)); + } + + //start HW channel + outl(ali_registers.global_regs[0x20], TRID_REG(card,T4D_START_A)); + //restore IRQ enable bits + outl(ali_registers.global_regs[0x2c], TRID_REG(card,T4D_MISCINT)); + + restore_flags(flags); +} + +static int trident_suspend(struct pci_dev *dev, u32 unused) +{ + struct trident_card *card = (struct trident_card *) dev; + + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + ali_save_regs(card); + } + return 0; +} + +static int trident_resume(struct pci_dev *dev) +{ + struct trident_card *card = (struct trident_card *) dev; + + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + ali_restore_regs(card); + } + return 0; +} + +static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card) +{ + struct trident_pcm_bank *bank; + int idx; + + bank = &card->banks[BANK_A]; + + if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & (ALI_SPDIF_OUT_CH_ENABLE)) { + idx = ALI_SPDIF_OUT_CHANNEL; + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx; + return channel; + } + } + + for (idx = ALI_PCM_OUT_CHANNEL_FIRST; idx <= ALI_PCM_OUT_CHANNEL_LAST ; idx++) { + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx; + return channel; + } + } + + /* no more free channels avaliable */ +// printk(KERN_ERR "ali: no more channels available on Bank A.\n"); + return NULL; +} + +static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card) +{ + struct trident_pcm_bank *bank; + int idx; + + if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_IN_SUPPORT) + idx = ALI_SPDIF_IN_CHANNEL; + else idx = ALI_PCM_IN_CHANNEL; + + bank = &card->banks[BANK_A]; + + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx; + return channel; + } + + /* no free recordable channels avaliable */ +// printk(KERN_ERR "ali: no recordable channels available on Bank A.\n"); + return NULL; +} + +static void ali_set_spdif_out_rate(struct trident_card *card, 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(TRID_REG(card, ALI_SPDIF_CTRL)) & ALI_SPDIF_OUT_CH_STATUS; //select spdif_out + + ch_st_sel |= 0x80; //select right + outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL)); + outb(status_rate | 0x20, TRID_REG(card, ALI_SPDIF_CS + 2)); + + ch_st_sel &= (~0x80); //select left + outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL)); + outw(status_rate | 0x10, TRID_REG(card, ALI_SPDIF_CS + 2)); +} + +static void ali_address_interrupt(struct trident_card *card) +{ + int i, channel; + struct trident_state *state; + u32 mask, channel_mask; + + mask = trident_get_interrupt_mask (card, 0); + for (i = 0; i < NR_HW_CH; i++) { + if ((state = card->states[i]) == NULL) + continue; + channel = state->dmabuf.channel->num; + if ((channel_mask = 1 << channel) & mask) { + mask &= ~channel_mask; + trident_ack_channel_interrupt(card, channel); + udelay(100); + state->dmabuf.update_flag |= ALI_ADDRESS_INT_UPDATE; + trident_update_ptr(state); + } + } + if (mask) { + for (i = 0; i < NR_HW_CH; i++) { + if (mask & (1 << i)) { + printk("ali: spurious channel irq %d.\n", i); + trident_ack_channel_interrupt(card, i); + trident_stop_voice(card, i); + trident_disable_voice_irq(card, i); + } + } + } +} + +/* Updating the values of counters of other_states' DMAs without lock +protection is no harm because all DMAs of multi-channels and interrupt +depend on a master state's DMA, and changing the counters of the master +state DMA is protected by a spinlock. +*/ +static int ali_write_5_1(struct trident_state *state, const char *buf, int cnt_for_multi_channel, unsigned int *copy_count, unsigned int *state_cnt) +{ + + struct dmabuf *dmabuf = &state->dmabuf; + struct dmabuf *dmabuf_temp; + const char *buffer = buf; + unsigned swptr, other_dma_nums, sample_s; + unsigned int i, loop; + + other_dma_nums = 4; + sample_s = sample_size[dmabuf->fmt] >> 1; + swptr = dmabuf->swptr; + + if ((i = state->multi_channels_adjust_count) > 0) { + if (i == 1) { + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + i--; + (*state_cnt) += sample_s; + state->multi_channels_adjust_count++; + } + else i = i - (state->chans_num - other_dma_nums); + for (; (i < other_dma_nums) && (cnt_for_multi_channel > 0); i++) { + dmabuf_temp = &state->other_states[i]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + } + if (cnt_for_multi_channel == 0) + state->multi_channels_adjust_count += i; + } + if (cnt_for_multi_channel > 0) { + loop = cnt_for_multi_channel / (state->chans_num * sample_s); + for (i = 0; i < loop; i++) { + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s * 2)) + return -EFAULT; + seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s * 2, *copy_count); + (*state_cnt) += (sample_s * 2); + + dmabuf_temp = &state->other_states[0]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + + dmabuf_temp = &state->other_states[1]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + + dmabuf_temp = &state->other_states[2]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + + dmabuf_temp = &state->other_states[3]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + } + + if (cnt_for_multi_channel > 0) { + state->multi_channels_adjust_count = cnt_for_multi_channel / sample_s; + + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + (*state_cnt) += sample_s; + + if (cnt_for_multi_channel > 0) { + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + (*state_cnt) += sample_s; + + if (cnt_for_multi_channel > 0) { + loop = state->multi_channels_adjust_count - (state->chans_num - other_dma_nums); + for (i = 0; i < loop; i++) { + dmabuf_temp = &state->other_states[i]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + } + } + } + } + else + state->multi_channels_adjust_count = 0; + } + for (i = 0; i < other_dma_nums; i++) { + dmabuf_temp = &state->other_states[i]->dmabuf; + dmabuf_temp->swptr = dmabuf_temp->swptr % dmabuf_temp->dmasize; + } + return *state_cnt; +} + +static void ali_free_other_states_resources(struct trident_state *state) +{ + int i; + struct trident_card *card = state->card; + struct trident_state *s; + unsigned other_states_count; + + other_states_count = state->chans_num - 2; /* except PCM L/R channels*/ + for ( i = 0; i < other_states_count; i++) { + s = state->other_states[i]; + dealloc_dmabuf(s); + ali_disable_special_channel(s->card, s->dmabuf.channel->num); + state->card->free_pcm_channel(s->card, s->dmabuf.channel->num); + card->states[s->virt] = NULL; + kfree(s); + } +} + +#ifdef CONFIG_PROC_FS +struct proc_dir_entry *res; +static int ali_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) +{ + struct trident_card *card = (struct trident_card *)data; + unsigned long flags; + char c; + + if (count<0) + return -EINVAL; + if (count == 0) + return 0; + if (get_user(c, buffer)) + return -EFAULT; + + spin_lock_irqsave(&card->lock, flags); + switch (c) { + case '0': + ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT); + ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL); + break; + case '1': + ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT|ALI_SPDIF_OUT_PCM); + break; + case '2': + ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT|ALI_SPDIF_OUT_NON_PCM); + break; + case '3': + ali_disable_spdif_in(card); //default + break; + case '4': + ali_setup_spdif_in(card); + break; + } + spin_unlock_irqrestore(&card->lock, flags); + + return count; +} +#endif + +/* OSS /dev/mixer file operation methods */ +static int trident_open_mixdev(struct inode *inode, struct file *file) +{ + int i = 0; + int minor = minor(inode->i_rdev); + struct trident_card *card = devs; + + for (card = devs; card != NULL; card = card->next) + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL && + card->ac97_codec[i]->dev_mixer == minor) + goto match; + + if (!card) { + return -ENODEV; + } + match: + file->private_data = card->ac97_codec[i]; + + + return 0; +} + +static int trident_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 trident_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: trident_ioctl_mixdev, + open: trident_open_mixdev, +}; + +static int ali_reset_5451(struct trident_card *card) +{ + struct pci_dev *pci_dev = NULL; + unsigned int dwVal; + unsigned short wCount, wReg; + + pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev); + if (pci_dev == NULL) + return -1; + + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); + udelay(5000); + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); + udelay(5000); + + pci_dev = card->pci_dev; + if (pci_dev == NULL) + return -1; + + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000); + udelay(500); + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff); + udelay(5000); + + wCount = 200; + while(wCount--) { + wReg = ali_ac97_get(card, 0, AC97_POWER_CONTROL); + if((wReg & 0x000f) == 0x000f) + return 0; + udelay(500); + } + return 0; +} + +/* AC97 codec initialisation. */ +static int __init trident_ac97_init(struct trident_card *card) +{ + int num_ac97 = 0; + unsigned long ready_2nd = 0; + struct ac97_codec *codec; + int i = 0; + + + /* initialize controller side of AC link, and find out if secondary codes + really exist */ + switch (card->pci_id) + { + case PCI_DEVICE_ID_ALI_5451: + if (ali_reset_5451(card)) + { + printk(KERN_ERR "trident_ac97_init: error resetting 5451.\n"); + return -1; + } + outl(0x80000001,TRID_REG(card, ALI_GLOBAL_CONTROL)); + outl(0x00000000,TRID_REG(card, T4D_AINTEN_A)); + outl(0xffffffff,TRID_REG(card, T4D_AINT_A)); + outl(0x00000000,TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); + outb(0x10, TRID_REG(card, ALI_MPUR2)); + ready_2nd = inl(TRID_REG(card, ALI_SCTRL)); + ready_2nd &= 0x3fff; + outl(ready_2nd | PCMOUT | 0x8000, TRID_REG(card, ALI_SCTRL)); + ready_2nd = inl(TRID_REG(card, ALI_SCTRL)); + ready_2nd &= SI_AC97_SECONDARY_READY; + if (card->revision < ALI_5451_V02) + ready_2nd = 0; + break; + case PCI_DEVICE_ID_SI_7018: + /* disable AC97 GPIO interrupt */ + outl(0x00, TRID_REG(card, SI_AC97_GPIO)); + /* when power up the AC link is in cold reset mode so stop it */ + outl(PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID, + TRID_REG(card, SI_SERIAL_INTF_CTRL)); + /* it take a long time to recover from a cold reset (especially when you have + more than one codec) */ + udelay(2000); + ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL)); + ready_2nd &= SI_AC97_SECONDARY_READY; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + /* playback on */ + outl(DX_AC97_PLAYBACK, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + /* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */ + outl(NX_AC97_PCM_OUTPUT, TRID_REG(card, NX_ACR0_AC97_COM_STAT)); + ready_2nd = inl(TRID_REG(card, NX_ACR0_AC97_COM_STAT)); + ready_2nd &= NX_AC97_SECONDARY_READY; + break; + case PCI_DEVICE_ID_INTERG_5050: + /* disable AC97 GPIO interrupt */ + outl(0x00, TRID_REG(card, SI_AC97_GPIO)); + /* when power up, the AC link is in cold reset mode, so stop it */ + outl(PCMOUT|SURROUT|CENTEROUT|LFEOUT, + TRID_REG(card, SI_SERIAL_INTF_CTRL)); + /* it take a long time to recover from a cold reset (especially when you have + more than one codec) */ + udelay(2000); + ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL)); + ready_2nd &= SI_AC97_SECONDARY_READY; + break; + } + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = card; + codec->id = num_ac97; + + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + codec->codec_read = ali_ac97_read; + codec->codec_write = ali_ac97_write; + } + else { + codec->codec_read = trident_ac97_get; + codec->codec_write = trident_ac97_set; + } + + if (ac97_probe_codec(codec) == 0) + break; + + if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) { + printk(KERN_ERR "trident: couldn't register mixer!\n"); + kfree(codec); + break; + } + + card->ac97_codec[num_ac97] = codec; + + /* if there is no secondary codec at all, don't probe any more */ + if (!ready_2nd) + break; + } + + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + if (card->ac97_codec[num_ac97] == NULL) + break; + for (i=0; i<64;i++) + card->mixer_regs[i][num_ac97] = ali_ac97_get(card, num_ac97,i*2); + } + } + return num_ac97+1; +} + +/* 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 trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + unsigned long iobase; + struct trident_card *card; + u8 bits; + u8 revision; + int i = 0; + u16 temp; + struct pci_dev *pci_dev_m1533 = NULL; + int rc = -ENODEV; + u64 dma_mask; + + if (pci_enable_device(pci_dev)) + goto out; + + if (pci_dev->device == PCI_DEVICE_ID_ALI_5451) + dma_mask = ALI_DMA_MASK; + else + dma_mask = TRIDENT_DMA_MASK; + if (pci_set_dma_mask(pci_dev, dma_mask)) { + printk(KERN_ERR "trident: architecture does not support" + " %s PCI busmaster DMA\n", + pci_dev->device == PCI_DEVICE_ID_ALI_5451 ? + "32-bit" : "30-bit"); + goto out; + } + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); + + if (pci_id->device == PCI_DEVICE_ID_INTERG_5050) + iobase = pci_resource_start(pci_dev, 1); + else + iobase = pci_resource_start(pci_dev, 0); + + if (!request_region(iobase, 256, card_names[pci_id->driver_data])) { + printk(KERN_ERR "trident: can't allocate I/O space at 0x%4.4lx\n", + iobase); + goto out; + } + + rc = -ENOMEM; + if ((card = kmalloc(sizeof(struct trident_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "trident: out of memory\n"); + goto out_release_region; + } + memset(card, 0, sizeof(*card)); + + card->iobase = iobase; + card->pci_dev = pci_dev; + card->pci_id = pci_id->device; + card->revision = revision; + card->irq = pci_dev->irq; + card->next = devs; + card->magic = TRIDENT_CARD_MAGIC; + card->banks[BANK_A].addresses = &bank_a_addrs; + card->banks[BANK_A].bitmap = 0UL; + card->banks[BANK_B].addresses = &bank_b_addrs; + card->banks[BANK_B].bitmap = 0UL; + + init_MUTEX(&card->open_sem); + spin_lock_init(&card->lock); + init_timer(&card->timer); + + devs = card; + + pci_set_master(pci_dev); + + printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], card->iobase, card->irq); + + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + /* ALi channel Management */ + card->alloc_pcm_channel = ali_alloc_pcm_channel; + card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel; + card->free_pcm_channel = ali_free_pcm_channel; + + card->address_interrupt = ali_address_interrupt; + + /* Added by Matt Wu 01-05-2001 for spdif in */ + card->multi_channel_use_count = 0; + card->rec_channel_use_count = 0; + + /* ALi SPDIF OUT function */ + if(card->revision == ALI_5451_V02) { + ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT); +#ifdef CONFIG_PROC_FS + res = create_proc_entry("ALi5451", 0, NULL); + if (res) { + res->write_proc = ali_write_proc; + res->data = card; + } +#endif + } + + /* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */ + card->hwvolctl = 0; + pci_dev_m1533 = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev_m1533); + rc = -ENODEV; + if (pci_dev_m1533 == NULL) + goto out_proc_fs; + pci_read_config_byte(pci_dev_m1533, 0x63, &bits); + if (bits & (1<<5)) + card->hwvolctl = 1; + if (card->hwvolctl) + { + /* Clear m1533 pci cfg 78h bit 30 to zero, which makes + GPIO11/12/13 work as ACGP_UP/DOWN/MUTE. */ + pci_read_config_byte(pci_dev_m1533, 0x7b, &bits); + bits &= 0xbf; /*clear bit 6 */ + pci_write_config_byte(pci_dev_m1533, 0x7b, bits); + } + } + else if(card->pci_id == PCI_DEVICE_ID_INTERG_5050) + { + card->alloc_pcm_channel = cyber_alloc_pcm_channel; + card->alloc_rec_pcm_channel = cyber_alloc_pcm_channel; + card->free_pcm_channel = cyber_free_pcm_channel; + card->address_interrupt = cyber_address_interrupt; + cyber_init_ritual(card); + } + else + { + card->alloc_pcm_channel = trident_alloc_pcm_channel; + card->alloc_rec_pcm_channel = trident_alloc_pcm_channel; + card->free_pcm_channel = trident_free_pcm_channel; + card->address_interrupt = trident_address_interrupt; + } + + /* claim our irq */ + rc = -ENODEV; + if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ, + card_names[pci_id->driver_data], card)) { + printk(KERN_ERR "trident: unable to allocate irq %d\n", card->irq); + goto out_proc_fs; + } + /* register /dev/dsp */ + if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) { + printk(KERN_ERR "trident: couldn't register DSP device!\n"); + goto out_free_irq; + } + card->mixer_regs_ready = 0; + /* initialize AC97 codec and register /dev/mixer */ + if (trident_ac97_init(card) <= 0) { + /* 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); + kfree (card->ac97_codec[i]); + } + } + goto out_unregister_sound_dsp; + } + card->mixer_regs_ready = 1; + outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); + + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + /* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */ + if(card->hwvolctl) + { + /* Enable GPIO IRQ (MISCINT bit 18h)*/ + temp = inw(TRID_REG(card, T4D_MISCINT + 2)); + temp |= 0x0004; + outw(temp, TRID_REG(card, T4D_MISCINT + 2)); + + /* Enable H/W Volume Control GLOVAL CONTROL bit 0*/ + temp = inw(TRID_REG(card, ALI_GLOBAL_CONTROL)); + temp |= 0x0001; + outw(temp, TRID_REG(card, ALI_GLOBAL_CONTROL)); + + } + if(card->revision == ALI_5451_V02) + ali_close_multi_channels(); + /* edited by HMSEO for GT sound */ +#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC + { + u16 ac97_data; + extern struct hwrpb_struct *hwrpb; + + if ((hwrpb->sys_type) == 201) { + printk(KERN_INFO "trident: Running on Alpha system type Nautilus\n"); + ac97_data = ali_ac97_get(card, 0, AC97_POWER_CONTROL); + ali_ac97_set(card, 0, AC97_POWER_CONTROL, ac97_data | ALI_EAPD_POWER_DOWN); + } + } +#endif + /* edited by HMSEO for GT sound*/ + } + rc = 0; + pci_set_drvdata(pci_dev, card); + + /* Enable Address Engine Interrupts */ + trident_enable_loop_interrupts(card); +out: return rc; +out_unregister_sound_dsp: + unregister_sound_dsp(card->dev_audio); +out_free_irq: + free_irq(card->irq, card); +out_proc_fs: +#ifdef CONFIG_PROC_FS + if (res) { + remove_proc_entry("ALi5451", NULL); + res = NULL; + } +#endif + kfree(card); + devs = NULL; +out_release_region: + release_region(iobase, 256); + goto out; +} + +static void __exit trident_remove(struct pci_dev *pci_dev) +{ + int i; + struct trident_card *card = pci_get_drvdata(pci_dev); + + /* + * Kill running timers before unload. We can't have them + * going off after rmmod! + */ + if(card->hwvolctl) + del_timer_sync(&card->timer); + + /* ALi S/PDIF and Power Management */ + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT); + ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL); + ali_disable_spdif_in(card); +#ifdef CONFIG_PROC_FS + remove_proc_entry("ALi5451", NULL); +#endif + } + + /* Kill interrupts, and SP/DIF */ + trident_disable_loop_interrupts(card); + + /* free hardware resources */ + free_irq(card->irq, card); + 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); + kfree (card->ac97_codec[i]); + } + unregister_sound_dsp(card->dev_audio); + + kfree(card); + + pci_set_drvdata(pci_dev, NULL); +} + +MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho, Ching Ling Lee"); +MODULE_DESCRIPTION("Trident 4DWave/SiS 7018/ALi 5451 and Tvia/IGST CyberPro5050 PCI Audio Driver"); +MODULE_LICENSE("GPL"); + + +#define TRIDENT_MODULE_NAME "trident" + +static struct pci_driver trident_pci_driver = { + name: TRIDENT_MODULE_NAME, + id_table: trident_pci_tbl, + probe: trident_probe, + remove: trident_remove, + suspend: trident_suspend, + resume: trident_resume +}; + +static int __init trident_init_module (void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + + printk(KERN_INFO "Trident 4DWave/SiS 7018/ALi 5451,Tvia CyberPro 5050 PCI Audio, version " + DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + if (!pci_register_driver(&trident_pci_driver)) { + pci_unregister_driver(&trident_pci_driver); + return -ENODEV; + } + return 0; +} + +static void __exit trident_cleanup_module (void) +{ + pci_unregister_driver(&trident_pci_driver); +} + +module_init(trident_init_module); +module_exit(trident_cleanup_module); diff -Nru a/sound/oss/trident.h b/sound/oss/trident.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/trident.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,362 @@ +#ifndef __TRID4DWAVE_H +#define __TRID4DWAVE_H + +/* + * audio@tridentmicro.com + * Fri Feb 19 15:55:28 MST 1999 + * Definitions for Trident 4DWave DX/NX chips + * + * + * 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. + * + */ + +/* PCI vendor and device ID */ +#ifndef PCI_VENDOR_ID_TRIDENT +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#endif + +#ifndef PCI_VENDOR_ID_SI +#define PCI_VENDOR_ID_SI 0x1039 +#endif + +#ifndef PCI_VENDOR_ID_ALI +#define PCI_VENDOR_ID_ALI 0x10b9 +#endif + +#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 +#endif + +#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 +#endif + +#ifndef PCI_DEVICE_ID_SI_7018 +#define PCI_DEVICE_ID_SI_7018 0x7018 +#endif + +#ifndef PCI_DEVICE_ID_ALI_5451 +#define PCI_DEVICE_ID_ALI_5451 0x5451 +#endif + +#ifndef PCI_DEVICE_ID_ALI_1533 +#define PCI_DEVICE_ID_ALI_1533 0x1533 +#endif + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +#define CHANNEL_REGS 5 +#define CHANNEL_START 0xe0 // The first bytes of the contiguous register space. + +#define BANK_A 0 +#define BANK_B 1 +#define NR_BANKS 2 + +#define TRIDENT_FMT_STEREO 0x01 +#define TRIDENT_FMT_16BIT 0x02 +#define TRIDENT_FMT_MASK 0x03 + +#define DAC_RUNNING 0x01 +#define ADC_RUNNING 0x02 + +/* Register Addresses */ + +/* operational registers common to DX, NX, 7018 */ +enum trident_op_registers { + T4D_REC_CH = 0x70, + T4D_START_A = 0x80, T4D_STOP_A = 0x84, + T4D_DLY_A = 0x88, T4D_SIGN_CSO_A = 0x8c, + T4D_CSPF_A = 0x90, T4D_CEBC_A = 0x94, + T4D_AINT_A = 0x98, T4D_EINT_A = 0x9c, + T4D_LFO_GC_CIR = 0xa0, T4D_AINTEN_A = 0xa4, + T4D_MUSICVOL_WAVEVOL = 0xa8, T4D_SBDELTA_DELTA_R = 0xac, + T4D_MISCINT = 0xb0, T4D_START_B = 0xb4, + T4D_STOP_B = 0xb8, T4D_CSPF_B = 0xbc, + T4D_SBBL_SBCL = 0xc0, T4D_SBCTRL_SBE2R_SBDD = 0xc4, + T4D_STIMER = 0xc8, T4D_LFO_B_I2S_DELTA = 0xcc, + T4D_AINT_B = 0xd8, T4D_AINTEN_B = 0xdc, + ALI_MPUR2 = 0x22, ALI_GPIO = 0x7c, + ALI_EBUF1 = 0xf4, + ALI_EBUF2 = 0xf8 +}; + +enum ali_op_registers { + ALI_SCTRL = 0x48, + ALI_GLOBAL_CONTROL = 0xd4, + ALI_STIMER = 0xc8, + ALI_SPDIF_CS = 0x70, + ALI_SPDIF_CTRL = 0x74 +}; + +enum ali_registers_number { + ALI_GLOBAL_REGS = 56, + ALI_CHANNEL_REGS = 8, + ALI_MIXER_REGS = 20 +}; + +enum ali_sctrl_control_bit { + ALI_SPDIF_OUT_ENABLE = 0x20 +}; + +enum ali_global_control_bit { + ALI_SPDIF_OUT_SEL_PCM = 0x00000400, + ALI_SPDIF_IN_SUPPORT = 0x00000800, + ALI_SPDIF_OUT_CH_ENABLE = 0x00008000, + ALI_SPDIF_IN_CH_ENABLE = 0x00080000, + ALI_PCM_IN_DISABLE = 0x7fffffff, + ALI_PCM_IN_ENABLE = 0x80000000, + ALI_SPDIF_IN_CH_DISABLE = 0xfff7ffff, + ALI_SPDIF_OUT_CH_DISABLE = 0xffff7fff, + ALI_SPDIF_OUT_SEL_SPDIF = 0xfffffbff + +}; + +enum ali_spdif_control_bit { + ALI_SPDIF_IN_FUNC_ENABLE = 0x02, + ALI_SPDIF_IN_CH_STATUS = 0x40, + ALI_SPDIF_OUT_CH_STATUS = 0xbf + +}; + +enum ali_control_all { + ALI_DISABLE_ALL_IRQ = 0, + ALI_CHANNELS = 32, + ALI_STOP_ALL_CHANNELS = 0xffffffff, + ALI_MULTI_CHANNELS_START_STOP = 0x07800000 +}; + +enum ali_EMOD_control_bit { + ALI_EMOD_DEC = 0x00000000, + ALI_EMOD_INC = 0x10000000, + ALI_EMOD_Delay = 0x20000000, + ALI_EMOD_Still = 0x30000000 +}; + +enum ali_pcm_in_channel_num { + ALI_NORMAL_CHANNEL = 0, + ALI_SPDIF_OUT_CHANNEL = 15, + ALI_SPDIF_IN_CHANNEL = 19, + ALI_LEF_CHANNEL = 23, + ALI_CENTER_CHANNEL = 24, + ALI_SURR_RIGHT_CHANNEL = 25, + ALI_SURR_LEFT_CHANNEL = 26, + ALI_PCM_IN_CHANNEL = 31 +}; + +enum ali_pcm_out_channel_num { + ALI_PCM_OUT_CHANNEL_FIRST = 0, + ALI_PCM_OUT_CHANNEL_LAST = 31 +}; + +enum ali_ac97_power_control_bit { + ALI_EAPD_POWER_DOWN = 0x8000 +}; + +enum ali_update_ptr_flags { + ALI_ADDRESS_INT_UPDATE = 0x01 +}; + +enum ali_revision { + ALI_5451_V02 = 0x02 +}; + +enum ali_spdif_out_control { + ALI_PCM_TO_SPDIF_OUT = 0, + ALI_SPDIF_OUT_TO_SPDIF_OUT = 1, + ALI_SPDIF_OUT_PCM = 0, + ALI_SPDIF_OUT_NON_PCM = 2 +}; + +/* S/PDIF Operational Registers for 4D-NX */ +enum nx_spdif_registers { + NX_SPCTRL_SPCSO = 0x24, NX_SPLBA = 0x28, + NX_SPESO = 0x2c, NX_SPCSTATUS = 0x64 +}; + +/* OP registers to access each hardware channel */ +enum channel_registers { + CH_DX_CSO_ALPHA_FMS = 0xe0, CH_DX_ESO_DELTA = 0xe8, + CH_DX_FMC_RVOL_CVOL = 0xec, + CH_NX_DELTA_CSO = 0xe0, CH_NX_DELTA_ESO = 0xe8, + CH_NX_ALPHA_FMS_FMC_RVOL_CVOL = 0xec, + CH_LBA = 0xe4, + CH_GVSEL_PAN_VOL_CTRL_EC = 0xf0 +}; + +/* registers to read/write/control AC97 codec */ +enum dx_ac97_registers { + DX_ACR0_AC97_W = 0x40, DX_ACR1_AC97_R = 0x44, + DX_ACR2_AC97_COM_STAT = 0x48 +}; + +enum nx_ac97_registers { + NX_ACR0_AC97_COM_STAT = 0x40, NX_ACR1_AC97_W = 0x44, + NX_ACR2_AC97_R_PRIMARY = 0x48, NX_ACR3_AC97_R_SECONDARY = 0x4c +}; + +enum si_ac97_registers { + SI_AC97_WRITE = 0x40, SI_AC97_READ = 0x44, + SI_SERIAL_INTF_CTRL = 0x48, SI_AC97_GPIO = 0x4c +}; + +enum ali_ac97_registers { + ALI_AC97_WRITE = 0x40, ALI_AC97_READ = 0x44 +}; + +/* Bit mask for operational registers */ +#define AC97_REG_ADDR 0x000000ff + +enum ali_ac97_bits { + ALI_AC97_BUSY_WRITE = 0x8000, ALI_AC97_BUSY_READ = 0x8000, + ALI_AC97_WRITE_ACTION = 0x8000, ALI_AC97_READ_ACTION = 0x8000, + ALI_AC97_AUDIO_BUSY = 0x4000, ALI_AC97_SECONDARY = 0x0080, + ALI_AC97_READ_MIXER_REGISTER = 0xfeff, + ALI_AC97_WRITE_MIXER_REGISTER = 0x0100 +}; + +enum sis7018_ac97_bits { + SI_AC97_BUSY_WRITE = 0x8000, SI_AC97_BUSY_READ = 0x8000, + SI_AC97_AUDIO_BUSY = 0x4000, SI_AC97_MODEM_BUSY = 0x2000, + SI_AC97_SECONDARY = 0x0080 +}; + +enum trident_dx_ac97_bits { + DX_AC97_BUSY_WRITE = 0x8000, DX_AC97_BUSY_READ = 0x8000, + DX_AC97_READY = 0x0010, DX_AC97_RECORD = 0x0008, + DX_AC97_PLAYBACK = 0x0002 +}; + +enum trident_nx_ac97_bits { + /* ACR1-3 */ + NX_AC97_BUSY_WRITE = 0x0800, NX_AC97_BUSY_READ = 0x0800, + NX_AC97_BUSY_DATA = 0x0400, NX_AC97_WRITE_SECONDARY = 0x0100, + /* ACR0 */ + NX_AC97_SECONDARY_READY = 0x0040, NX_AC97_SECONDARY_RECORD = 0x0020, + NX_AC97_SURROUND_OUTPUT = 0x0010, + NX_AC97_PRIMARY_READY = 0x0008, NX_AC97_PRIMARY_RECORD = 0x0004, + NX_AC97_PCM_OUTPUT = 0x0002, + NX_AC97_WARM_RESET = 0x0001 +}; + +enum serial_intf_ctrl_bits { + WARM_REST = 0x00000001, COLD_RESET = 0x00000002, + I2S_CLOCK = 0x00000004, PCM_SEC_AC97= 0x00000008, + AC97_DBL_RATE = 0x00000010, SPDIF_EN = 0x00000020, + I2S_OUTPUT_EN = 0x00000040, I2S_INPUT_EN = 0x00000080, + PCMIN = 0x00000100, LINE1IN = 0x00000200, + MICIN = 0x00000400, LINE2IN = 0x00000800, + HEAD_SET_IN = 0x00001000, GPIOIN = 0x00002000, + /* 7018 spec says id = 01 but the demo board routed to 10 + SECONDARY_ID= 0x00004000, */ + SECONDARY_ID= 0x00004000, + PCMOUT = 0x00010000, SURROUT = 0x00020000, + CENTEROUT = 0x00040000, LFEOUT = 0x00080000, + LINE1OUT = 0x00100000, LINE2OUT = 0x00200000, + GPIOOUT = 0x00400000, + SI_AC97_PRIMARY_READY = 0x01000000, + SI_AC97_SECONDARY_READY = 0x02000000, +}; + +enum global_control_bits { + CHANNLE_IDX = 0x0000003f, PB_RESET = 0x00000100, + PAUSE_ENG = 0x00000200, + OVERRUN_IE = 0x00000400, UNDERRUN_IE = 0x00000800, + ENDLP_IE = 0x00001000, MIDLP_IE = 0x00002000, + ETOG_IE = 0x00004000, + EDROP_IE = 0x00008000, BANK_B_EN = 0x00010000 +}; + +enum channel_control_bits { + CHANNEL_LOOP = 0x00001000, CHANNEL_SIGNED = 0x00002000, + CHANNEL_STEREO = 0x00004000, CHANNEL_16BITS = 0x00008000, +}; + +enum channel_attribute { + /* playback/record select */ + CHANNEL_PB = 0x0000, CHANNEL_SPC_PB = 0x4000, + CHANNEL_REC = 0x8000, CHANNEL_REC_PB = 0xc000, + /* playback destination/record source select */ + MODEM_LINE1 = 0x0000, MODEM_LINE2 = 0x0400, + PCM_LR = 0x0800, HSET = 0x0c00, + I2S_LR = 0x1000, CENTER_LFE = 0x1400, + SURR_LR = 0x1800, SPDIF_LR = 0x1c00, + MIC = 0x1400, + /* mist stuff */ + MONO_LEFT = 0x0000, MONO_RIGHT = 0x0100, + MONO_MIX = 0x0200, SRC_ENABLE = 0x0080, +}; + +enum miscint_bits { + PB_UNDERRUN_IRO = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, + SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, + OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020, + ENVELOPE_IRQ = 0x00000040, ST_IRQ = 0x00000080, + PB_UNDERRUN = 0x00000100, REC_OVERRUN = 0x00000200, + MIXER_UNDERFLOW = 0x00000400, MIXER_OVERFLOW = 0x00000800, + ST_TARGET_REACHED = 0x00008000, PB_24K_MODE = 0x00010000, + ST_IRQ_EN = 0x00800000, ACGPIO_IRQ = 0x01000000 +}; + +#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) ) + +#define CYBER_PORT_AUDIO 0x3CE +#define CYBER_IDX_AUDIO_ENABLE 0x7B +#define CYBER_BMSK_AUDIO_INT_ENABLE 0x09 +#define CYBER_BMSK_AUENZ 0x01 +#define CYBER_BMSK_AUENZ_ENABLE 0x00 +#define CYBER_IDX_IRQ_ENABLE 0x12 + +#define VALIDATE_MAGIC(FOO,MAG) \ +({ \ + if (!(FOO) || (FOO)->magic != MAG) { \ + printk(invalid_magic,__FUNCTION__); \ + return -ENXIO; \ + } \ +}) + +#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,TRIDENT_STATE_MAGIC) +#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,TRIDENT_CARD_MAGIC) + +extern __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; +} + +#endif /* __TRID4DWAVE_H */ + diff -Nru a/sound/oss/trix.c b/sound/oss/trix.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/trix.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,546 @@ +/* + * sound/trix.c + * + * Low level driver for the MediaTrix AudioTrix Pro + * (MT-0002-PC Control Chip) + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes + * Alan Cox Modularisation, cleanup. + * Christoph Hellwig Adapted to module_init/module_exit + * Arnaldo C. de Melo Got rid of attach_uart401 + */ + +#include +#include + +#include "sound_config.h" +#include "sb.h" +#include "sound_firmware.h" + +#include "ad1848.h" +#include "mpu401.h" + +#include "trix_boot.h" + +static int kilroy_was_here = 0; /* Don't detect twice */ +static int sb_initialized = 0; +static int mpu_initialized = 0; + +static int *trix_osp = NULL; + +static int mpu = 0; + +static int joystick=0; + +static unsigned char trix_read(int addr) +{ + outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */ + return inb(0x391); /* MT-0002-PC ASIC data */ +} + +static void trix_write(int addr, int data) +{ + outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */ + outb(((unsigned char) data), 0x391); /* MT-0002-PC ASIC data */ +} + +static void download_boot(int base) +{ + int i = 0, n = trix_boot_len; + + if (trix_boot_len == 0) + return; + + trix_write(0xf8, 0x00); /* ??????? */ + outb((0x01), base + 6); /* Clear the internal data pointer */ + outb((0x00), base + 6); /* Restart */ + + /* + * Write the boot code to the RAM upload/download register. + * Each write increments the internal data pointer. + */ + outb((0x01), base + 6); /* Clear the internal data pointer */ + outb((0x1A), 0x390); /* Select RAM download/upload port */ + + for (i = 0; i < n; i++) + outb((trix_boot[i]), 0x391); + for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */ + outb((0x00), 0x391); + outb((0x00), base + 6); /* Reset */ + outb((0x50), 0x390); /* ?????? */ + +} + +static int trix_set_wss_port(struct address_info *hw_config) +{ + unsigned char addr_bits; + + if (check_region(0x390, 2)) + { + printk(KERN_ERR "AudioTrix: Config port I/O conflict\n"); + return 0; + } + if (kilroy_was_here) /* Already initialized */ + return 0; + + if (trix_read(0x15) != 0x71) /* No ASIC signature */ + { + MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n")); + return 0; + } + kilroy_was_here = 1; + + /* + * Reset some registers. + */ + + trix_write(0x13, 0); + trix_write(0x14, 0); + + /* + * Configure the ASIC to place the codec to the proper I/O location + */ + + switch (hw_config->io_base) + { + case 0x530: + addr_bits = 0; + break; + case 0x604: + addr_bits = 1; + break; + case 0xE80: + addr_bits = 2; + break; + case 0xF40: + addr_bits = 3; + break; + default: + return 0; + } + + trix_write(0x19, (trix_read(0x19) & 0x03) | addr_bits); + return 1; +} + +/* + * Probe and attach routines for the Windows Sound System mode of + * AudioTrix Pro + */ + +static int __init probe_trix_wss(struct address_info *hw_config) +{ + int ret; + + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (AudioTrix Pro for example) + * return 0x00. + */ + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + trix_osp = hw_config->osp; + + if (!trix_set_wss_port(hw_config)) + return 0; + + if ((inb(hw_config->io_base + 3) & 0x3f) != 0x00) + { + MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base)); + return 0; + } + if (hw_config->irq > 11) + { + printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) + { + printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", hw_config->dma); + return 0; + } + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + if (hw_config->dma2 != 0 && hw_config->dma2 != 1 && hw_config->dma2 != 3) + { + printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", hw_config->dma2); + return 0; + } + /* + * Check that DMA0 is not in use with a 8 bit board. + */ + + if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n"); + return 0; + } + if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq); + return 0; + } + ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); + + if (ret) + { + if(joystick==1) + trix_write(0x15, 0x80); + request_region(0x390, 2, "AudioTrix"); + } + return ret; +} + +static void __init attach_trix_wss(struct address_info *hw_config) +{ + static unsigned char interrupt_bits[12] = { + 0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x10, 0x18, 0x20 + }; + char bits; + + static unsigned char dma_bits[4] = { + 1, 2, 0, 3 + }; + + int config_port = hw_config->io_base + 0; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + int old_num_mixers = num_mixers; + + trix_osp = hw_config->osp; + + if (!kilroy_was_here) + { + DDB(printk("AudioTrix: Attach called but not probed yet???\n")); + return; + } + + /* + * Set the IRQ and DMA addresses. + */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == 0) + { + printk("AudioTrix: Bad IRQ (%d)\n", hw_config->irq); + return; + } + outb((bits | 0x40), config_port); + + if (hw_config->dma2 == -1 || hw_config->dma2 == hw_config->dma) + { + bits |= dma_bits[dma1]; + dma2 = dma1; + } + else + { + unsigned char tmp; + + tmp = trix_read(0x13) & ~30; + trix_write(0x13, tmp | 0x80 | (dma1 << 4)); + + tmp = trix_read(0x14) & ~30; + trix_write(0x14, tmp | 0x80 | (dma2 << 4)); + } + + outb((bits), config_port); /* Write IRQ+DMA setup */ + + hw_config->slots[0] = ad1848_init("AudioTrix Pro", hw_config->io_base + 4, + hw_config->irq, + dma1, + dma2, + 0, + hw_config->osp, + THIS_MODULE); + request_region(hw_config->io_base, 4, "MSS config"); + + if (num_mixers > old_num_mixers) /* Mixer got installed */ + { + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); /* Line in */ + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* OPL4 */ + AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM); /* SB */ + } +} + +static int __init probe_trix_sb(struct address_info *hw_config) +{ + + int tmp; + unsigned char conf; + static signed char irq_translate[] = { + -1, -1, -1, 0, 1, 2, -1, 3 + }; + + if (trix_boot_len == 0) + return 0; /* No boot code -> no fun */ + + if (!kilroy_was_here) + return 0; /* AudioTrix Pro has not been detected earlier */ + + if (sb_initialized) + return 0; + + if (check_region(hw_config->io_base, 16)) + { + printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + if ((hw_config->io_base & 0xffffff8f) != 0x200) + return 0; + + tmp = hw_config->irq; + if (tmp > 7) + return 0; + if (irq_translate[tmp] == -1) + return 0; + + tmp = hw_config->dma; + if (tmp != 1 && tmp != 3) + return 0; + + conf = 0x84; /* DMA and IRQ enable */ + conf |= hw_config->io_base & 0x70; /* I/O address bits */ + conf |= irq_translate[hw_config->irq]; + if (hw_config->dma == 3) + conf |= 0x08; + trix_write(0x1b, conf); + + download_boot(hw_config->io_base); + sb_initialized = 1; + + hw_config->name = "AudioTrix SB"; + return sb_dsp_detect(hw_config, 0, 0, NULL); +} + +static void __init attach_trix_sb(struct address_info *hw_config) +{ + extern int sb_be_quiet; + int old_quiet; + + hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING; + + /* Prevent false alarms */ + old_quiet = sb_be_quiet; + sb_be_quiet = 1; + + sb_dsp_init(hw_config, THIS_MODULE); + + sb_be_quiet = old_quiet; +} + +static int __init probe_trix_mpu(struct address_info *hw_config) +{ + unsigned char conf; + static int irq_bits[] = { + -1, -1, -1, 1, 2, 3, -1, 4, -1, 5 + }; + + if (!kilroy_was_here) + { + DDB(printk("Trix: WSS and SB modes must be initialized before MPU\n")); + return 0; /* AudioTrix Pro has not been detected earlier */ + } + if (!sb_initialized) + { + DDB(printk("Trix: SB mode must be initialized before MPU\n")); + return 0; + } + if (mpu_initialized) + { + DDB(printk("Trix: MPU mode already initialized\n")); + return 0; + } + if (hw_config->irq > 9) + { + printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + if (irq_bits[hw_config->irq] == -1) + { + printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + switch (hw_config->io_base) + { + case 0x330: + conf = 0x00; + break; + case 0x370: + conf = 0x04; + break; + case 0x3b0: + conf = 0x08; + break; + case 0x3f0: + conf = 0x0c; + break; + default: + return 0; /* Invalid port */ + } + + conf |= irq_bits[hw_config->irq] << 4; + trix_write(0x19, (trix_read(0x19) & 0x83) | conf); + mpu_initialized = 1; + hw_config->name = "AudioTrix Pro"; + return probe_uart401(hw_config, THIS_MODULE); +} + +static void __exit unload_trix_wss(struct address_info *hw_config) +{ + int dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = hw_config->dma; + + release_region(0x390, 2); + release_region(hw_config->io_base, 4); + + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + dma2, + 0); + sound_unload_audiodev(hw_config->slots[0]); +} + +static inline void __exit unload_trix_mpu(struct address_info *hw_config) +{ + unload_uart401(hw_config); +} + +static inline void __exit unload_trix_sb(struct address_info *hw_config) +{ + sb_dsp_unload(hw_config, mpu); +} + +static struct address_info cfg; +static struct address_info cfg2; +static struct address_info cfg_mpu; + +static int sb = 0; +static int fw_load; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; /* Set this for modules that need it */ +static int __initdata sb_io = -1; +static int __initdata sb_dma = -1; +static int __initdata sb_irq = -1; +static int __initdata mpu_io = -1; +static int __initdata mpu_irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(sb_io,"i"); +MODULE_PARM(sb_dma,"i"); +MODULE_PARM(sb_irq,"i"); +MODULE_PARM(mpu_io,"i"); +MODULE_PARM(mpu_irq,"i"); +MODULE_PARM(joystick, "i"); + +static int __init init_trix(void) +{ + printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + cfg2.io_base = sb_io; + cfg2.irq = sb_irq; + cfg2.dma = sb_dma; + + cfg_mpu.io_base = mpu_io; + cfg_mpu.irq = mpu_irq; + + if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { + printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); + return -EINVAL; + } + + if (cfg2.io_base != -1 && (cfg2.irq == -1 || cfg2.dma == -1)) { + printk(KERN_INFO "CONFIG_SB_IRQ and CONFIG_SB_DMA must be specified if SB_IO is set.\n"); + return -EINVAL; + } + if (cfg_mpu.io_base != -1 && cfg_mpu.irq == -1) { + printk(KERN_INFO "CONFIG_MPU_IRQ must be specified if MPU_IO is set.\n"); + return -EINVAL; + } + if (!trix_boot) + { + fw_load = 1; + trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin", + (char **) &trix_boot); + } + if (!probe_trix_wss(&cfg)) + return -ENODEV; + attach_trix_wss(&cfg); + + /* + * We must attach in the right order to get the firmware + * loaded up in time. + */ + + if (cfg2.io_base != -1) { + sb = probe_trix_sb(&cfg2); + if (sb) + attach_trix_sb(&cfg2); + } + + if (cfg_mpu.io_base != -1) + mpu = probe_trix_mpu(&cfg_mpu); + + return 0; +} + +static void __exit cleanup_trix(void) +{ + if (fw_load && trix_boot) + vfree(trix_boot); + if (sb) + unload_trix_sb(&cfg2); + if (mpu) + unload_trix_mpu(&cfg_mpu); + unload_trix_wss(&cfg); +} + +module_init(init_trix); +module_exit(cleanup_trix); + +#ifndef MODULE +static int __init setup_trix (char *str) +{ + /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, mpu_io, mpu_irq */ + int ints[9]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + sb_io = ints[5]; + sb_irq = ints[6]; + sb_dma = ints[6]; + mpu_io = ints[7]; + mpu_irq = ints[8]; + + return 1; +} + +__setup("trix=", setup_trix); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/tuning.h b/sound/oss/tuning.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/tuning.h Tue Feb 19 18:08:56 2002 @@ -0,0 +1,29 @@ +#ifdef SEQUENCER_C + +unsigned short semitone_tuning[24] = +{ +/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, +/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, +/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 +}; + +unsigned short cent_tuning[100] = +{ +/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, +/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, +/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, +/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, +/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, +/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, +/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, +/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, +/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, +/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, +/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, +/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, +/* 96 */ 10570, 10576, 10582, 10589 +}; +#else +extern unsigned short semitone_tuning[24]; +extern unsigned short cent_tuning[100]; +#endif diff -Nru a/sound/oss/uart401.c b/sound/oss/uart401.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/uart401.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,481 @@ +/* + * sound/uart401.c + * + * MPU-401 UART driver (formerly uart401_midi.c) + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Alan Cox Reformatted, removed sound_mem usage, use normal Linux + * interrupt allocation. Protect against bogus unload + * Fixed to allow IRQ > 15 + * Christoph Hellwig Adapted to module_init/module_exit + * Arnaldo C. de Melo got rid of check_region + * + * Status: + * Untested + */ + +#include +#include + +#include "sound_config.h" + +#include "mpu401.h" + +typedef struct uart401_devc +{ + int base; + int irq; + int *osp; + void (*midi_input_intr) (int dev, unsigned char data); + int opened, disabled; + volatile unsigned char input_byte; + int my_dev; + int share_irq; +} +uart401_devc; + +#define DATAPORT (devc->base) +#define COMDPORT (devc->base+1) +#define STATPORT (devc->base+1) + +static int uart401_status(uart401_devc * devc) +{ + return inb(STATPORT); +} + +#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL)) +#define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY)) + +static void uart401_cmd(uart401_devc * devc, unsigned char cmd) +{ + outb((cmd), COMDPORT); +} + +static int uart401_read(uart401_devc * devc) +{ + return inb(DATAPORT); +} + +static void uart401_write(uart401_devc * devc, unsigned char byte) +{ + outb((byte), DATAPORT); +} + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define MPU_RESET 0xFF +#define UART_MODE_ON 0x3F + +static int reset_uart401(uart401_devc * devc); +static void enter_uart_mode(uart401_devc * devc); + +static void uart401_input_loop(uart401_devc * devc) +{ + int work_limit=30000; + + while (input_avail(devc) && --work_limit) + { + unsigned char c = uart401_read(devc); + + if (c == MPU_ACK) + devc->input_byte = c; + else if (devc->opened & OPEN_READ && devc->midi_input_intr) + devc->midi_input_intr(devc->my_dev, c); + } + if(work_limit==0) + printk(KERN_WARNING "Too much work in interrupt on uart401 (0x%X). UART jabbering ??\n", devc->base); +} + +void uart401intr(int irq, void *dev_id, struct pt_regs *dummy) +{ + uart401_devc *devc = dev_id; + + if (devc == NULL) + { + printk(KERN_ERR "uart401: bad devc\n"); + return; + } + + if (input_avail(devc)) + uart401_input_loop(devc); +} + +static int +uart401_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + + if (devc->opened) + return -EBUSY; + + /* Flush the UART */ + + while (input_avail(devc)) + uart401_read(devc); + + devc->midi_input_intr = input; + devc->opened = mode; + enter_uart_mode(devc); + devc->disabled = 0; + + return 0; +} + +static void uart401_close(int dev) +{ + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + + reset_uart401(devc); + devc->opened = 0; +} + +static int uart401_out(int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + + if (devc->disabled) + return 1; + /* + * Test for input since pending input seems to block the output. + */ + + save_flags(flags); + cli(); + + if (input_avail(devc)) + uart401_input_loop(devc); + + restore_flags(flags); + + /* + * Sometimes it takes about 13000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); + + if (!output_ready(devc)) + { + printk(KERN_WARNING "uart401: Timeout - Device not responding\n"); + devc->disabled = 1; + reset_uart401(devc); + enter_uart_mode(devc); + return 1; + } + uart401_write(devc, midi_byte); + return 1; +} + +static inline int uart401_start_read(int dev) +{ + return 0; +} + +static inline int uart401_end_read(int dev) +{ + return 0; +} + +static inline void uart401_kick(int dev) +{ +} + +static inline int uart401_buffer_status(int dev) +{ + return 0; +} + +#define MIDI_SYNTH_NAME "MPU-401 UART" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static const struct midi_operations uart401_operations = +{ + owner: THIS_MODULE, + info: {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401}, + converter: &std_midi_synth, + in_info: {0}, + open: uart401_open, + close: uart401_close, + outputc: uart401_out, + start_read: uart401_start_read, + end_read: uart401_end_read, + kick: uart401_kick, + buffer_status: uart401_buffer_status, +}; + +static void enter_uart_mode(uart401_devc * devc) +{ + int ok, timeout; + unsigned long flags; + + save_flags(flags); + cli(); + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); + + devc->input_byte = 0; + uart401_cmd(devc, UART_MODE_ON); + + ok = 0; + for (timeout = 50000; timeout > 0 && !ok; timeout--) + if (devc->input_byte == MPU_ACK) + ok = 1; + else if (input_avail(devc)) + if (uart401_read(devc) == MPU_ACK) + ok = 1; + + restore_flags(flags); +} + +static int reset_uart401(uart401_devc * devc) +{ + int ok, timeout, n; + + /* + * Send the RESET command. Try again if no success at the first time. + */ + + ok = 0; + + for (n = 0; n < 2 && !ok; n++) + { + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); + devc->input_byte = 0; + uart401_cmd(devc, MPU_RESET); + + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ + + for (timeout = 50000; timeout > 0 && !ok; timeout--) + { + if (devc->input_byte == MPU_ACK) /* Interrupt */ + ok = 1; + else if (input_avail(devc)) + { + if (uart401_read(devc) == MPU_ACK) + ok = 1; + } + } + } + + + if (ok) + { + DEB(printk("Reset UART401 OK\n")); + } + else + DDB(printk("Reset UART401 failed - No hardware detected.\n")); + + if (ok) + uart401_input_loop(devc); /* + * Flush input before enabling interrupts + */ + + return ok; +} + +int probe_uart401(struct address_info *hw_config, struct module *owner) +{ + uart401_devc *devc; + char *name = "MPU-401 (UART) MIDI"; + int ok = 0; + unsigned long flags; + + DDB(printk("Entered probe_uart401()\n")); + + /* Default to "not found" */ + hw_config->slots[4] = -1; + + if (!request_region(hw_config->io_base, 4, "MPU-401 UART")) { + printk(KERN_INFO "uart401: could not request_region(%d, 4)\n", hw_config->io_base); + return 0; + } + + devc = kmalloc(sizeof(uart401_devc), GFP_KERNEL); + if (!devc) { + printk(KERN_WARNING "uart401: Can't allocate memory\n"); + goto cleanup_region; + } + + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->osp = hw_config->osp; + devc->midi_input_intr = NULL; + devc->opened = 0; + devc->input_byte = 0; + devc->my_dev = 0; + devc->share_irq = 0; + + save_flags(flags); + cli(); + ok = reset_uart401(devc); + restore_flags(flags); + + if (!ok) + goto cleanup_devc; + + if (hw_config->name) + name = hw_config->name; + + if (devc->irq < 0) { + devc->share_irq = 1; + devc->irq *= -1; + } else + devc->share_irq = 0; + + if (!devc->share_irq) + if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0) { + printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq); + devc->share_irq = 1; + } + devc->my_dev = sound_alloc_mididev(); + enter_uart_mode(devc); + + if (devc->my_dev == -1) { + printk(KERN_INFO "uart401: Too many midi devices detected\n"); + goto cleanup_irq; + } + conf_printf(name, hw_config); + midi_devs[devc->my_dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL); + if (!midi_devs[devc->my_dev]) { + printk(KERN_ERR "uart401: Failed to allocate memory\n"); + goto cleanup_unload_mididev; + } + memcpy(midi_devs[devc->my_dev], &uart401_operations, sizeof(struct midi_operations)); + + if (owner) + midi_devs[devc->my_dev]->owner = owner; + + midi_devs[devc->my_dev]->devc = devc; + midi_devs[devc->my_dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL); + if (!midi_devs[devc->my_dev]->converter) { + printk(KERN_WARNING "uart401: Failed to allocate memory\n"); + goto cleanup_midi_devs; + } + memcpy(midi_devs[devc->my_dev]->converter, &std_midi_synth, sizeof(struct synth_operations)); + strcpy(midi_devs[devc->my_dev]->info.name, name); + midi_devs[devc->my_dev]->converter->id = "UART401"; + midi_devs[devc->my_dev]->converter->midi_dev = devc->my_dev; + + if (owner) + midi_devs[devc->my_dev]->converter->owner = owner; + + hw_config->slots[4] = devc->my_dev; + sequencer_init(); + devc->opened = 0; + return 1; +cleanup_midi_devs: + kfree(midi_devs[devc->my_dev]); +cleanup_unload_mididev: + sound_unload_mididev(devc->my_dev); +cleanup_irq: + if (!devc->share_irq) + free_irq(devc->irq, devc); +cleanup_devc: + kfree(devc); +cleanup_region: + release_region(hw_config->io_base, 4); + return 0; +} + +void unload_uart401(struct address_info *hw_config) +{ + uart401_devc *devc; + int n=hw_config->slots[4]; + + /* Not set up */ + if(n==-1 || midi_devs[n]==NULL) + return; + + /* Not allocated (erm ??) */ + + devc = midi_devs[hw_config->slots[4]]->devc; + if (devc == NULL) + return; + + reset_uart401(devc); + release_region(hw_config->io_base, 4); + + if (!devc->share_irq) + free_irq(devc->irq, devc); + if (devc) + { + kfree(midi_devs[devc->my_dev]->converter); + kfree(midi_devs[devc->my_dev]); + kfree(devc); + devc = NULL; + } + /* This kills midi_devs[x] */ + sound_unload_mididev(hw_config->slots[4]); +} + +EXPORT_SYMBOL(probe_uart401); +EXPORT_SYMBOL(unload_uart401); +EXPORT_SYMBOL(uart401intr); + +static struct address_info cfg_mpu; + +static int __initdata io = -1; +static int __initdata irq = -1; + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); + + +static int __init init_uart401(void) +{ + cfg_mpu.irq = irq; + cfg_mpu.io_base = io; + + /* Can be loaded either for module use or to provide functions + to others */ + if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) { + printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997"); + if (!probe_uart401(&cfg_mpu, THIS_MODULE)) + return -ENODEV; + } + + return 0; +} + +static void __exit cleanup_uart401(void) +{ + if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) + unload_uart401(&cfg_mpu); +} + +module_init(init_uart401); +module_exit(cleanup_uart401); + +#ifndef MODULE +static int __init setup_uart401(char *str) +{ + /* io, irq */ + int ints[3]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + + return 1; +} + +__setup("uart401=", setup_uart401); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/uart6850.c b/sound/oss/uart6850.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/uart6850.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,364 @@ +/* + * sound/uart6850.c + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver. + * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2. + * + * Alan Cox: Updated for new modular code. Removed snd_* irq handling. Now + * uses native linux resources + * Christoph Hellwig: Adapted to module_init/module_exit + * Jeff Garzik: Made it work again, in theory + * FIXME: If the request_irq() succeeds, the probe succeeds. Ug. + * + * Status: Testing required (no shit -jgarzik) + * + * + */ + +#include +#include + +/* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl: + * added 6850 support, used with COVOX SoundMaster II and custom cards. + */ + +#include "sound_config.h" + +static int uart6850_base = 0x330; + +static int *uart6850_osp; + +#define DATAPORT (uart6850_base) +#define COMDPORT (uart6850_base+1) +#define STATPORT (uart6850_base+1) + +static int uart6850_status(void) +{ + return inb(STATPORT); +} + +#define input_avail() (uart6850_status()&INPUT_AVAIL) +#define output_ready() (uart6850_status()&OUTPUT_READY) + +static void uart6850_cmd(unsigned char cmd) +{ + outb(cmd, COMDPORT); +} + +static int uart6850_read(void) +{ + return inb(DATAPORT); +} + +static void uart6850_write(unsigned char byte) +{ + outb(byte, DATAPORT); +} + +#define OUTPUT_READY 0x02 /* Mask for data ready Bit */ +#define INPUT_AVAIL 0x01 /* Mask for Data Send Ready Bit */ + +#define UART_RESET 0x95 +#define UART_MODE_ON 0x03 + +static int uart6850_opened; +static int uart6850_irq; +static int uart6850_detected; +static int my_dev; + +static void (*midi_input_intr) (int dev, unsigned char data); +static void poll_uart6850(unsigned long dummy); + + +static struct timer_list uart6850_timer = { + function: poll_uart6850 +}; + +static void uart6850_input_loop(void) +{ + int count = 10; + + while (count) + { + /* + * Not timed out + */ + if (input_avail()) + { + unsigned char c = uart6850_read(); + count = 100; + if (uart6850_opened & OPEN_READ) + midi_input_intr(my_dev, c); + } + else + { + while (!input_avail() && count) + count--; + } + } +} + +void m6850intr(int irq, void *dev_id, struct pt_regs *dummy) +{ + if (input_avail()) + uart6850_input_loop(); +} + +/* + * It looks like there is no input interrupts in the UART mode. Let's try + * polling. + */ + +static void poll_uart6850(unsigned long dummy) +{ + unsigned long flags; + + if (!(uart6850_opened & OPEN_READ)) + return; /* Device has been closed */ + + save_flags(flags); + cli(); + + if (input_avail()) + uart6850_input_loop(); + + uart6850_timer.expires = 1 + jiffies; + add_timer(&uart6850_timer); + + /* + * Come back later + */ + + restore_flags(flags); +} + +static int uart6850_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + if (uart6850_opened) + { +/* printk("Midi6850: Midi busy\n");*/ + return -EBUSY; + }; + + uart6850_cmd(UART_RESET); + uart6850_input_loop(); + midi_input_intr = input; + uart6850_opened = mode; + poll_uart6850(0); /* + * Enable input polling + */ + + return 0; +} + +static void uart6850_close(int dev) +{ + uart6850_cmd(UART_MODE_ON); + del_timer(&uart6850_timer); + uart6850_opened = 0; +} + +static int uart6850_out(int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + + /* + * Test for input since pending input seems to block the output. + */ + + save_flags(flags); + cli(); + + if (input_avail()) + uart6850_input_loop(); + + restore_flags(flags); + + /* + * Sometimes it takes about 13000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /* + * Wait + */ + if (!output_ready()) + { + printk(KERN_WARNING "Midi6850: Timeout\n"); + return 0; + } + uart6850_write(midi_byte); + return 1; +} + +static inline int uart6850_command(int dev, unsigned char *midi_byte) +{ + return 1; +} + +static inline int uart6850_start_read(int dev) +{ + return 0; +} + +static inline int uart6850_end_read(int dev) +{ + return 0; +} + +static inline void uart6850_kick(int dev) +{ +} + +static inline int uart6850_buffer_status(int dev) +{ + return 0; /* + * No data in buffers + */ +} + +#define MIDI_SYNTH_NAME "6850 UART Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations uart6850_operations = +{ + owner: THIS_MODULE, + info: {"6850 UART", 0, 0, SNDCARD_UART6850}, + converter: &std_midi_synth, + in_info: {0}, + open: uart6850_open, + close: uart6850_close, + outputc: uart6850_out, + start_read: uart6850_start_read, + end_read: uart6850_end_read, + kick: uart6850_kick, + command: uart6850_command, + buffer_status: uart6850_buffer_status +}; + + +static void __init attach_uart6850(struct address_info *hw_config) +{ + int ok, timeout; + unsigned long flags; + + if (!uart6850_detected) + return; + + if ((my_dev = sound_alloc_mididev()) == -1) + { + printk(KERN_INFO "uart6850: Too many midi devices detected\n"); + return; + } + uart6850_base = hw_config->io_base; + uart6850_osp = hw_config->osp; + uart6850_irq = hw_config->irq; + + save_flags(flags); + cli(); + + for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /* + * Wait + */ + uart6850_cmd(UART_MODE_ON); + ok = 1; + restore_flags(flags); + + conf_printf("6850 Midi Interface", hw_config); + + std_midi_synth.midi_dev = my_dev; + hw_config->slots[4] = my_dev; + midi_devs[my_dev] = &uart6850_operations; + sequencer_init(); +} + +static inline int reset_uart6850(void) +{ + uart6850_read(); + return 1; /* + * OK + */ +} + +static int __init probe_uart6850(struct address_info *hw_config) +{ + int ok; + + uart6850_osp = hw_config->osp; + uart6850_base = hw_config->io_base; + uart6850_irq = hw_config->irq; + + if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0) + return 0; + + ok = reset_uart6850(); + uart6850_detected = ok; + return ok; +} + +static void __exit unload_uart6850(struct address_info *hw_config) +{ + free_irq(hw_config->irq, NULL); + sound_unload_mididev(hw_config->slots[4]); +} + +static struct address_info cfg_mpu; + +static int __initdata io = -1; +static int __initdata irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); + +static int __init init_uart6850(void) +{ + cfg_mpu.io_base = io; + cfg_mpu.irq = irq; + + if (cfg_mpu.io_base == -1 || cfg_mpu.irq == -1) { + printk(KERN_INFO "uart6850: irq and io must be set.\n"); + return -EINVAL; + } + + if (probe_uart6850(&cfg_mpu)) + return -ENODEV; + attach_uart6850(&cfg_mpu); + + return 0; +} + +static void __exit cleanup_uart6850(void) +{ + unload_uart6850(&cfg_mpu); +} + +module_init(init_uart6850); +module_exit(cleanup_uart6850); + +#ifndef MODULE +static int __init setup_uart6850(char *str) +{ + /* io, irq */ + int ints[3]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + + return 1; +} +__setup("uart6850=", setup_uart6850); +#endif +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/ulaw.h b/sound/oss/ulaw.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ulaw.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,69 @@ +static unsigned char ulaw_dsp[] = { + 3, 7, 11, 15, 19, 23, 27, 31, + 35, 39, 43, 47, 51, 55, 59, 63, + 66, 68, 70, 72, 74, 76, 78, 80, + 82, 84, 86, 88, 90, 92, 94, 96, + 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, + 113, 114, 114, 115, 115, 116, 116, 117, + 117, 118, 118, 119, 119, 120, 120, 121, + 121, 121, 122, 122, 122, 122, 123, 123, + 123, 123, 124, 124, 124, 124, 125, 125, + 125, 125, 125, 125, 126, 126, 126, 126, + 126, 126, 126, 126, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 253, 249, 245, 241, 237, 233, 229, 225, + 221, 217, 213, 209, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 174, 172, 170, 168, 166, 164, 162, 160, + 158, 157, 156, 155, 154, 153, 152, 151, + 150, 149, 148, 147, 146, 145, 144, 143, + 143, 142, 142, 141, 141, 140, 140, 139, + 139, 138, 138, 137, 137, 136, 136, 135, + 135, 135, 134, 134, 134, 134, 133, 133, + 133, 133, 132, 132, 132, 132, 131, 131, + 131, 131, 131, 131, 130, 130, 130, 130, + 130, 130, 130, 130, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, +}; + +static unsigned char dsp_ulaw[] = { + 0, 0, 0, 0, 0, 1, 1, 1, + 1, 2, 2, 2, 2, 3, 3, 3, + 3, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 9, 9, 9, + 9, 10, 10, 10, 10, 11, 11, 11, + 11, 12, 12, 12, 12, 13, 13, 13, + 13, 14, 14, 14, 14, 15, 15, 15, + 15, 16, 16, 17, 17, 18, 18, 19, + 19, 20, 20, 21, 21, 22, 22, 23, + 23, 24, 24, 25, 25, 26, 26, 27, + 27, 28, 28, 29, 29, 30, 30, 31, + 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, + 47, 49, 51, 53, 55, 57, 59, 61, + 63, 66, 70, 74, 78, 84, 92, 104, + 254, 231, 219, 211, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 175, 174, 173, 172, 171, 170, 169, 168, + 167, 166, 165, 164, 163, 162, 161, 160, + 159, 159, 158, 158, 157, 157, 156, 156, + 155, 155, 154, 154, 153, 153, 152, 152, + 151, 151, 150, 150, 149, 149, 148, 148, + 147, 147, 146, 146, 145, 145, 144, 144, + 143, 143, 143, 143, 142, 142, 142, 142, + 141, 141, 141, 141, 140, 140, 140, 140, + 139, 139, 139, 139, 138, 138, 138, 138, + 137, 137, 137, 137, 136, 136, 136, 136, + 135, 135, 135, 135, 134, 134, 134, 134, + 133, 133, 133, 133, 132, 132, 132, 132, + 131, 131, 131, 131, 130, 130, 130, 130, + 129, 129, 129, 129, 128, 128, 128, 128, +}; diff -Nru a/sound/oss/v_midi.c b/sound/oss/v_midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/v_midi.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,290 @@ +/* + * sound/v_midi.c + * + * The low level driver for the Sound Blaster DS chips. + * + * + * Copyright (C) by Hannu Savolainen 1993-1996 + * + * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * ?? + * + * Changes + * Alan Cox Modularisation, changed memory allocations + * Christoph Hellwig Adapted to module_init/module_exit + * + * Status + * Untested + */ + +#include +#include + +#include "sound_config.h" + +#include "v_midi.h" + +static vmidi_devc *v_devc[2] = { NULL, NULL}; +static int midi1,midi2; +static void *midi_mem = NULL; + +/* + * The DSP channel can be used either for input or output. Variable + * 'sb_irq_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + + +void (*midi_input_intr) (int dev, unsigned char data); + +static int v_midi_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return -(ENXIO); + + save_flags (flags); + cli(); + if (devc->opened) + { + restore_flags (flags); + return -(EBUSY); + } + devc->opened = 1; + restore_flags (flags); + + devc->intr_active = 1; + + if (mode & OPEN_READ) + { + devc->input_opened = 1; + devc->midi_input_intr = input; + } + + return 0; +} + +static void v_midi_close (int dev) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return; + + save_flags (flags); + cli (); + devc->intr_active = 0; + devc->input_opened = 0; + devc->opened = 0; + restore_flags (flags); +} + +static int v_midi_out (int dev, unsigned char midi_byte) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + vmidi_devc *pdevc = midi_devs[devc->pair_mididev]->devc; + + if (devc == NULL) + return -(ENXIO); + + if (pdevc->input_opened > 0){ + if (MIDIbuf_avail(pdevc->my_mididev) > 500) + return 0; + pdevc->midi_input_intr (pdevc->my_mididev, midi_byte); + } + return 1; +} + +static inline int v_midi_start_read (int dev) +{ + return 0; +} + +static int v_midi_end_read (int dev) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + if (devc == NULL) + return -ENXIO; + + devc->intr_active = 0; + return 0; +} + +/* why -EPERM and not -EINVAL?? */ + +static inline int v_midi_ioctl (int dev, unsigned cmd, caddr_t arg) +{ + return -EPERM; +} + + +#define MIDI_SYNTH_NAME "Loopback MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT + +#include "midi_synth.h" + +static struct midi_operations v_midi_operations = +{ + owner: THIS_MODULE, + info: {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI}, + converter: &std_midi_synth, + in_info: {0}, + open: v_midi_open, + close: v_midi_close, + ioctl: v_midi_ioctl, + outputc: v_midi_out, + start_read: v_midi_start_read, + end_read: v_midi_end_read, +}; + +static struct midi_operations v_midi_operations2 = +{ + owner: THIS_MODULE, + info: {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI}, + converter: &std_midi_synth, + in_info: {0}, + open: v_midi_open, + close: v_midi_close, + ioctl: v_midi_ioctl, + outputc: v_midi_out, + start_read: v_midi_start_read, + end_read: v_midi_end_read, +}; + +/* + * We kmalloc just one of these - it makes life simpler and the code + * cleaner and the memory handling far more efficient + */ + +struct vmidi_memory +{ + /* Must be first */ + struct midi_operations m_ops[2]; + struct synth_operations s_ops[2]; + struct vmidi_devc v_ops[2]; +}; + +static void __init attach_v_midi (struct address_info *hw_config) +{ + struct vmidi_memory *m; + /* printk("Attaching v_midi device.....\n"); */ + + midi1 = sound_alloc_mididev(); + if (midi1 == -1) + { + printk(KERN_ERR "v_midi: Too many midi devices detected\n"); + return; + } + + m=(struct vmidi_memory *)kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL); + if (m == NULL) + { + printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n"); + sound_unload_mididev(midi1); + return; + } + + midi_mem = m; + + midi_devs[midi1] = &m->m_ops[0]; + + + midi2 = sound_alloc_mididev(); + if (midi2 == -1) + { + printk (KERN_ERR "v_midi: Too many midi devices detected\n"); + kfree(m); + sound_unload_mididev(midi1); + return; + } + + midi_devs[midi2] = &m->m_ops[1]; + + /* printk("VMIDI1: %d VMIDI2: %d\n",midi1,midi2); */ + + /* for MIDI-1 */ + v_devc[0] = &m->v_ops[0]; + memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations, + sizeof (struct midi_operations)); + + v_devc[0]->my_mididev = midi1; + v_devc[0]->pair_mididev = midi2; + v_devc[0]->opened = v_devc[0]->input_opened = 0; + v_devc[0]->intr_active = 0; + v_devc[0]->midi_input_intr = NULL; + + midi_devs[midi1]->devc = v_devc[0]; + + midi_devs[midi1]->converter = &m->s_ops[0]; + std_midi_synth.midi_dev = midi1; + memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth, + sizeof (struct synth_operations)); + midi_devs[midi1]->converter->id = "V_MIDI 1"; + + /* for MIDI-2 */ + v_devc[1] = &m->v_ops[1]; + + memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2, + sizeof (struct midi_operations)); + + v_devc[1]->my_mididev = midi2; + v_devc[1]->pair_mididev = midi1; + v_devc[1]->opened = v_devc[1]->input_opened = 0; + v_devc[1]->intr_active = 0; + v_devc[1]->midi_input_intr = NULL; + + midi_devs[midi2]->devc = v_devc[1]; + midi_devs[midi2]->converter = &m->s_ops[1]; + + std_midi_synth.midi_dev = midi2; + memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth, + sizeof (struct synth_operations)); + midi_devs[midi2]->converter->id = "V_MIDI 2"; + + sequencer_init(); + /* printk("Attached v_midi device\n"); */ +} + +static inline int __init probe_v_midi(struct address_info *hw_config) +{ + return(1); /* always OK */ +} + + +static void __exit unload_v_midi(struct address_info *hw_config) +{ + sound_unload_mididev(midi1); + sound_unload_mididev(midi2); + kfree(midi_mem); +} + +static struct address_info cfg; /* dummy */ + +static int __init init_vmidi(void) +{ + printk("MIDI Loopback device driver\n"); + if (!probe_v_midi(&cfg)) + return -ENODEV; + attach_v_midi(&cfg); + + return 0; +} + +static void __exit cleanup_vmidi(void) +{ + unload_v_midi(&cfg); +} + +module_init(init_vmidi); +module_exit(cleanup_vmidi); +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/v_midi.h b/sound/oss/v_midi.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/v_midi.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,15 @@ +typedef struct vmidi_devc { + int dev; + + /* State variables */ + int opened; + + + /* MIDI fields */ + int my_mididev; + int pair_mididev; + int input_opened; + int intr_active; + void (*midi_input_intr) (int dev, unsigned char data); + } vmidi_devc; + diff -Nru a/sound/oss/via82cxxx_audio.c b/sound/oss/via82cxxx_audio.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/via82cxxx_audio.c Tue Feb 19 18:08:58 2002 @@ -0,0 +1,3541 @@ +/* + * Support for VIA 82Cxxx Audio Codecs + * Copyright 1999,2000 Jeff Garzik + * + * Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2. + * See the "COPYING" file distributed with this software for more info. + * + * 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" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" +#include "dev_table.h" +#include "mpu401.h" + + +#undef VIA_DEBUG /* define to enable debugging output and checks */ +#ifdef VIA_DEBUG +/* note: prints function name for you */ +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#undef VIA_NDEBUG /* define to disable lightweight runtime checks */ +#ifdef VIA_NDEBUG +#define assert(expr) +#else +#define assert(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + +#if defined(CONFIG_PROC_FS) && \ + defined(CONFIG_SOUND_VIA82CXXX_PROCFS) +#define VIA_PROC_FS 1 +#endif + +#define VIA_SUPPORT_MMAP 1 /* buggy, for now... */ + +#define MAX_CARDS 1 + +#define VIA_CARD_NAME "VIA 82Cxxx Audio driver " VIA_VERSION +#define VIA_MODULE_NAME "via82cxxx" +#define PFX VIA_MODULE_NAME ": " + +#define VIA_COUNTER_LIMIT 100000 + +/* size of DMA buffers */ +#define VIA_MAX_BUFFER_DMA_PAGES 32 + +/* buffering default values in ms */ +#define VIA_DEFAULT_FRAG_TIME 20 +#define VIA_DEFAULT_BUFFER_TIME 500 + +#define VIA_MAX_FRAG_SIZE PAGE_SIZE +#define VIA_MIN_FRAG_SIZE 64 + +#define VIA_MIN_FRAG_NUMBER 2 + +/* 82C686 function 5 (audio codec) PCI configuration registers */ +#define VIA_ACLINK_STATUS 0x40 +#define VIA_ACLINK_CTRL 0x41 +#define VIA_FUNC_ENABLE 0x42 +#define VIA_PNP_CONTROL 0x43 +#define VIA_FM_NMI_CTRL 0x48 + +/* + * controller base 0 (scatter-gather) registers + * + * NOTE: Via datasheet lists first channel as "read" + * channel and second channel as "write" channel. + * I changed the naming of the constants to be more + * clear than I felt the datasheet to be. + */ + +#define VIA_BASE0_PCM_OUT_CHAN 0x00 /* output PCM to user */ +#define VIA_BASE0_PCM_OUT_CHAN_STATUS 0x00 +#define VIA_BASE0_PCM_OUT_CHAN_CTRL 0x01 +#define VIA_BASE0_PCM_OUT_CHAN_TYPE 0x02 + +#define VIA_BASE0_PCM_IN_CHAN 0x10 /* input PCM from user */ +#define VIA_BASE0_PCM_IN_CHAN_STATUS 0x10 +#define VIA_BASE0_PCM_IN_CHAN_CTRL 0x11 +#define VIA_BASE0_PCM_IN_CHAN_TYPE 0x12 + +/* offsets from base */ +#define VIA_PCM_STATUS 0x00 +#define VIA_PCM_CONTROL 0x01 +#define VIA_PCM_TYPE 0x02 +#define VIA_PCM_TABLE_ADDR 0x04 +#define VIA_PCM_BLOCK_COUNT 0x0C + +/* XXX unused DMA channel for FM PCM data */ +#define VIA_BASE0_FM_OUT_CHAN 0x20 +#define VIA_BASE0_FM_OUT_CHAN_STATUS 0x20 +#define VIA_BASE0_FM_OUT_CHAN_CTRL 0x21 +#define VIA_BASE0_FM_OUT_CHAN_TYPE 0x22 + +#define VIA_BASE0_AC97_CTRL 0x80 +#define VIA_BASE0_SGD_STATUS_SHADOW 0x84 +#define VIA_BASE0_GPI_INT_ENABLE 0x8C +#define VIA_INTR_OUT ((1<<0) | (1<<4) | (1<<8)) +#define VIA_INTR_IN ((1<<1) | (1<<5) | (1<<9)) +#define VIA_INTR_FM ((1<<2) | (1<<6) | (1<<10)) +#define VIA_INTR_MASK (VIA_INTR_OUT | VIA_INTR_IN | VIA_INTR_FM) + +/* 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 */ +#define VIA_INT_SEL_PCI_LAST_LINE_READ (0) /* int at PCI read of last line */ +#define VIA_INT_SEL_LAST_SAMPLE_SENT (1<<2) /* int at last sample sent */ +#define VIA_INT_SEL_ONE_LINE_LEFT (1<<3) /* int at less than one line to send */ +#define VIA_PCM_FMT_STEREO (1<<4) /* PCM stereo format (bit clear == mono) */ +#define VIA_PCM_FMT_16BIT (1<<5) /* PCM 16-bit format (bit clear == 8-bit) */ +#define VIA_PCM_REC_FIFO (1<<6) /* PCM Recording FIFO */ +#define VIA_RESTART_SGD_ON_EOL (1<<7) /* restart scatter-gather at EOL */ +#define VIA_PCM_FMT_MASK (VIA_PCM_FMT_STEREO|VIA_PCM_FMT_16BIT) +#define VIA_CHAN_TYPE_MASK (VIA_RESTART_SGD_ON_EOL | \ + VIA_IRQ_ON_FLAG | \ + VIA_IRQ_ON_EOL) +#define VIA_CHAN_TYPE_INT_SELECT (VIA_INT_SEL_LAST_SAMPLE_SENT) + +/* PCI configuration register bits and masks */ +#define VIA_CR40_AC97_READY 0x01 +#define VIA_CR40_AC97_LOW_POWER 0x02 +#define VIA_CR40_SECONDARY_READY 0x04 + +#define VIA_CR41_AC97_ENABLE 0x80 /* enable AC97 codec */ +#define VIA_CR41_AC97_RESET 0x40 /* clear bit to reset AC97 */ +#define VIA_CR41_AC97_WAKEUP 0x20 /* wake up from power-down mode */ +#define VIA_CR41_AC97_SDO 0x10 /* force Serial Data Out (SDO) high */ +#define VIA_CR41_VRA 0x08 /* enable variable sample rate */ +#define VIA_CR41_PCM_ENABLE 0x04 /* AC Link SGD Read Channel PCM Data Output */ +#define VIA_CR41_FM_PCM_ENABLE 0x02 /* AC Link FM Channel PCM Data Out */ +#define VIA_CR41_SB_PCM_ENABLE 0x01 /* AC Link SB PCM Data Output */ +#define VIA_CR41_BOOT_MASK (VIA_CR41_AC97_ENABLE | \ + VIA_CR41_AC97_WAKEUP | \ + VIA_CR41_AC97_SDO) +#define VIA_CR41_RUN_MASK (VIA_CR41_AC97_ENABLE | \ + VIA_CR41_AC97_RESET | \ + VIA_CR41_VRA | \ + VIA_CR41_PCM_ENABLE) + +#define VIA_CR42_SB_ENABLE 0x01 +#define VIA_CR42_MIDI_ENABLE 0x02 +#define VIA_CR42_FM_ENABLE 0x04 +#define VIA_CR42_GAME_ENABLE 0x08 +#define VIA_CR42_MIDI_IRQMASK 0x40 +#define VIA_CR42_MIDI_PNP 0x80 + +#define VIA_CR44_SECOND_CODEC_SUPPORT (1 << 6) +#define VIA_CR44_AC_LINK_ACCESS (1 << 7) + +#define VIA_CR48_FM_TRAP_TO_NMI (1 << 2) + +/* controller base 0 register bitmasks */ +#define VIA_INT_DISABLE_MASK (~(0x01|0x02)) +#define VIA_SGD_STOPPED (1 << 2) +#define VIA_SGD_PAUSED (1 << 6) +#define VIA_SGD_ACTIVE (1 << 7) +#define VIA_SGD_TERMINATE (1 << 6) +#define VIA_SGD_FLAG (1 << 0) +#define VIA_SGD_EOL (1 << 1) +#define VIA_SGD_START (1 << 7) + +#define VIA_CR80_FIRST_CODEC 0 +#define VIA_CR80_SECOND_CODEC (1 << 30) +#define VIA_CR80_FIRST_CODEC_VALID (1 << 25) +#define VIA_CR80_VALID (1 << 25) +#define VIA_CR80_SECOND_CODEC_VALID (1 << 27) +#define VIA_CR80_BUSY (1 << 24) +#define VIA_CR83_BUSY (1) +#define VIA_CR83_FIRST_CODEC_VALID (1 << 1) +#define VIA_CR80_READ (1 << 23) +#define VIA_CR80_WRITE_MODE 0 +#define VIA_CR80_REG_IDX(idx) ((((idx) & 0xFF) >> 1) << 16) + +/* capabilities we announce */ +#ifdef VIA_SUPPORT_MMAP +#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | DSP_CAP_MMAP | \ + DSP_CAP_TRIGGER | DSP_CAP_REALTIME) +#else +#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | \ + DSP_CAP_TRIGGER | DSP_CAP_REALTIME) +#endif + +/* scatter-gather DMA table entry, exactly as passed to hardware */ +struct via_sgd_table { + u32 addr; + u32 count; /* includes additional VIA_xxx bits also */ +}; + +#define VIA_EOL (1 << 31) +#define VIA_FLAG (1 << 30) +#define VIA_STOP (1 << 29) + + +enum via_channel_states { + sgd_stopped = 0, + sgd_in_progress = 1, +}; + + +struct via_buffer_pgtbl { + dma_addr_t handle; + void *cpuaddr; +}; + + +struct via_channel { + atomic_t n_frags; + atomic_t hw_ptr; + wait_queue_head_t wait; + + unsigned int sw_ptr; + unsigned int slop_len; + unsigned int n_irqs; + int bytes; + + unsigned is_active : 1; + unsigned is_record : 1; + unsigned is_mapped : 1; + unsigned is_enabled : 1; + u8 pcm_fmt; /* VIA_PCM_FMT_xxx */ + + unsigned rate; /* sample rate */ + unsigned int frag_size; + unsigned int frag_number; + + volatile struct via_sgd_table *sgtable; + dma_addr_t sgt_handle; + + unsigned int page_number; + struct via_buffer_pgtbl pgtbl[VIA_MAX_BUFFER_DMA_PAGES]; + + long iobase; + + const char *name; +}; + + +/* data stored for each chip */ +struct via_info { + struct pci_dev *pdev; + long baseaddr; + + struct ac97_codec ac97; + 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; + + int locked_rate : 1; + + struct semaphore syscall_sem; + struct semaphore open_sem; + + struct via_channel ch_in; + struct via_channel ch_out; + struct via_channel ch_fm; + +#ifdef CONFIG_MIDI_VIA82CXXX + void *midi_devc; + struct address_info midi_info; +#endif +}; + + +/* number of cards, used for assigning unique numbers to cards */ +static unsigned via_num_cards = 0; + + + +/**************************************************************** + * + * prototypes + * + * + */ + +static int via_init_one (struct pci_dev *dev, const struct pci_device_id *id); +static void via_remove_one (struct pci_dev *pdev); + +static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos); +static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos); +static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait); +static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int via_dsp_open (struct inode *inode, struct file *file); +static int via_dsp_release(struct inode *inode, struct file *file); +static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma); + +static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg); +static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value); +static u8 via_ac97_wait_idle (struct via_info *card); + +static void via_chan_free (struct via_info *card, struct via_channel *chan); +static void via_chan_clear (struct via_info *card, struct via_channel *chan); +static void via_chan_pcm_fmt (struct via_channel *chan, int reset); +static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan); + +#ifdef VIA_PROC_FS +static int via_init_proc (void); +static void via_cleanup_proc (void); +static int via_card_init_proc (struct via_info *card); +static void via_card_cleanup_proc (struct via_info *card); +#else +static inline int via_init_proc (void) { return 0; } +static inline void via_cleanup_proc (void) {} +static inline int via_card_init_proc (struct via_info *card) { return 0; } +static inline void via_card_cleanup_proc (struct via_info *card) {} +#endif + + +/**************************************************************** + * + * Various data the driver needs + * + * + */ + + +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_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, + PCI_ANY_ID, PCI_ANY_ID, }, + { 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: via_remove_one, +}; + + +/**************************************************************** + * + * Low-level base 0 register read/write helpers + * + * + */ + +/** + * via_chan_stop - Terminate DMA on specified PCM channel + * @iobase: PCI base address for SGD channel registers + * + * Terminate scatter-gather DMA operation for given + * channel (derived from @iobase), if DMA is active. + * + * Note that @iobase is not the PCI base address, + * but the PCI base address plus an offset to + * one of three PCM channels supported by the chip. + * + */ + +static inline void via_chan_stop (long iobase) +{ + if (inb (iobase + VIA_PCM_STATUS) & VIA_SGD_ACTIVE) + outb (VIA_SGD_TERMINATE, iobase + VIA_PCM_CONTROL); +} + + +/** + * via_chan_status_clear - Clear status flags on specified DMA channel + * @iobase: PCI base address for SGD channel registers + * + * Clear any pending status flags for the given + * DMA channel (derived from @iobase), if any + * flags are asserted. + * + * Note that @iobase is not the PCI base address, + * but the PCI base address plus an offset to + * one of three PCM channels supported by the chip. + * + */ + +static inline void via_chan_status_clear (long iobase) +{ + u8 tmp = inb (iobase + VIA_PCM_STATUS); + + if (tmp != 0) + outb (tmp, iobase + VIA_PCM_STATUS); +} + + +/** + * sg_begin - Begin recording or playback on a PCM channel + * @chan: Channel for which DMA operation shall begin + * + * Start scatter-gather DMA for the given channel. + * + */ + +static inline void sg_begin (struct via_channel *chan) +{ + outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL); +} + + +static int sg_active (long iobase) +{ + u8 tmp = inb (iobase + VIA_PCM_STATUS); + if ((tmp & VIA_SGD_STOPPED) || (tmp & VIA_SGD_PAUSED)) { + printk(KERN_WARNING "via82cxxx warning: SG stopped or paused\n"); + return 0; + } + if (tmp & VIA_SGD_ACTIVE) + return 1; + return 0; +} + + +/**************************************************************** + * + * Miscellaneous debris + * + * + */ + + +/** + * via_syscall_down - down the card-specific syscell semaphore + * @card: Private info for specified board + * @nonblock: boolean, non-zero if O_NONBLOCK is set + * + * Encapsulates standard method of acquiring the syscall sem. + * + * Returns negative errno on error, or zero for success. + */ + +static inline int via_syscall_down (struct via_info *card, int nonblock) +{ + /* Thomas Sailer: + * EAGAIN is supposed to be used if IO is pending, + * not if there is contention on some internal + * synchronization primitive which should be + * held only for a short time anyway + */ + nonblock = 0; + + if (nonblock) { + if (down_trylock (&card->syscall_sem)) + return -EAGAIN; + } else { + if (down_interruptible (&card->syscall_sem)) + return -ERESTARTSYS; + } + + return 0; +} + + +/** + * via_stop_everything - Stop all audio operations + * @card: Private info for specified board + * + * Stops all DMA operations and interrupts, and clear + * any pending status bits resulting from those operations. + */ + +static void via_stop_everything (struct via_info *card) +{ + u8 tmp, new_tmp; + + DPRINTK ("ENTER\n"); + + assert (card != NULL); + + /* + * terminate any existing operations on audio read/write channels + */ + 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); + + /* + * clear any existing stops / flags (sanity check mainly) + */ + 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); + + /* + * clear any enabled interrupt bits + */ + tmp = inb (card->baseaddr + VIA_BASE0_PCM_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_PCM_OUT_CHAN_TYPE); + + tmp = inb (card->baseaddr + VIA_BASE0_PCM_IN_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_PCM_IN_CHAN_TYPE); + + tmp = inb (card->baseaddr + VIA_BASE0_FM_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_FM_OUT_CHAN_TYPE); + + udelay(10); + + /* + * clear any existing flags + */ + 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); + + DPRINTK ("EXIT\n"); +} + + +/** + * via_set_rate - Set PCM rate for given channel + * @ac97: Pointer to generic codec info struct + * @chan: Private info for specified channel + * @rate: Desired PCM sample rate, in Khz + * + * Sets the PCM sample rate for a channel. + * + * Values for @rate are clamped to a range of 4000 Khz through 48000 Khz, + * due to hardware constraints. + */ + +static int via_set_rate (struct ac97_codec *ac97, + struct via_channel *chan, unsigned rate) +{ + struct via_info *card = ac97->private_data; + int rate_reg; + + DPRINTK ("ENTER, rate = %d\n", rate); + + if (chan->rate == rate) + goto out; + if (card->locked_rate) { + chan->rate = 48000; + goto out; + } + + if (rate > 48000) rate = 48000; + if (rate < 4000) rate = 4000; + + 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); + + 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); + + /* the hardware might return a value different than what we + * passed to it, so read the rate value back from hardware + * to see what we came up with + */ + chan->rate = via_ac97_read_reg (ac97, rate_reg); + + if (chan->rate == 0) { + card->locked_rate = 1; + chan->rate = 48000; + printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); + } + +out: + DPRINTK ("EXIT, returning rate %d Hz\n", chan->rate); + return chan->rate; +} + + +/**************************************************************** + * + * Channel-specific operations + * + * + */ + + +/** + * via_chan_init_defaults - Initialize a struct via_channel + * @card: Private audio chip info + * @chan: Channel to be initialized + * + * Zero @chan, and then set all static defaults for the structure. + */ + +static void via_chan_init_defaults (struct via_info *card, struct via_channel *chan) +{ + memset (chan, 0, sizeof (*chan)); + + if (chan == &card->ch_out) { + chan->name = "PCM-OUT"; + 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; + chan->is_record = 1; + } else if (chan == &card->ch_fm) { + chan->name = "PCM-OUT-FM"; + chan->iobase = card->baseaddr + VIA_BASE0_FM_OUT_CHAN; + } else { + BUG(); + } + + init_waitqueue_head (&chan->wait); + + chan->pcm_fmt = VIA_PCM_FMT_MASK; + chan->is_enabled = 1; + + chan->frag_number = 0; + chan->frag_size = 0; + atomic_set(&chan->n_frags, 0); + atomic_set (&chan->hw_ptr, 0); +} + +/** + * via_chan_init - Initialize PCM channel + * @card: Private audio chip info + * @chan: Channel to be initialized + * + * 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. + */ + + +static void via_chan_init (struct via_info *card, struct via_channel *chan) +{ + + DPRINTK ("ENTER\n"); + + /* bzero channel structure, and init members to defaults */ + via_chan_init_defaults (card, chan); + + /* stop any existing channel output */ + via_chan_clear (card, chan); + via_chan_status_clear (chan->iobase); + via_chan_pcm_fmt (chan, 1); + + DPRINTK ("EXIT\n"); +} + +/** + * via_chan_buffer_init - Initialize PCM channel buffer + * @card: Private audio chip info + * @chan: Channel to be initialized + * + * Performs some of the preparations necessary to begin + * using a PCM channel. + * + * Currently the preparations include allocating the + * scatter-gather DMA table and buffers, + * and passing the + * address of the DMA table to the hardware. + * + * Note that special care is taken when passing the + * DMA table address to hardware, because it was found + * during driver development that the hardware did not + * always "take" the address. + */ + +static int via_chan_buffer_init (struct via_info *card, struct via_channel *chan) +{ + int page, offset; + int i; + + DPRINTK ("ENTER\n"); + + if (chan->sgtable != NULL) { + DPRINTK ("EXIT\n"); + return 0; + } + + /* alloc DMA-able memory for scatter-gather table */ + chan->sgtable = pci_alloc_consistent (card->pdev, + (sizeof (struct via_sgd_table) * chan->frag_number), + &chan->sgt_handle); + if (!chan->sgtable) { + printk (KERN_ERR PFX "DMA table alloc fail, aborting\n"); + DPRINTK ("EXIT\n"); + return -ENOMEM; + } + + memset ((void*)chan->sgtable, 0, + (sizeof (struct via_sgd_table) * chan->frag_number)); + + /* alloc DMA-able memory for scatter-gather buffers */ + + chan->page_number = (chan->frag_number * chan->frag_size) / PAGE_SIZE + + (((chan->frag_number * chan->frag_size) % PAGE_SIZE) ? 1 : 0); + + for (i = 0; i < chan->page_number; i++) { + chan->pgtbl[i].cpuaddr = pci_alloc_consistent (card->pdev, PAGE_SIZE, + &chan->pgtbl[i].handle); + + if (!chan->pgtbl[i].cpuaddr) { + chan->page_number = i; + goto err_out_nomem; + } + +#ifndef VIA_NDEBUG + memset (chan->pgtbl[i].cpuaddr, 0xBC, chan->frag_size); +#endif + +#if 1 + DPRINTK ("dmabuf_pg #%d (h=%lx, v2p=%lx, a=%p)\n", + i, (long)chan->pgtbl[i].handle, + virt_to_phys(chan->pgtbl[i].cpuaddr), + chan->pgtbl[i].cpuaddr); +#endif + } + + for (i = 0; i < chan->frag_number; i++) { + + page = i / (PAGE_SIZE / chan->frag_size); + offset = (i % (PAGE_SIZE / chan->frag_size)) * chan->frag_size; + + chan->sgtable[i].count = cpu_to_le32 (chan->frag_size | VIA_FLAG); + chan->sgtable[i].addr = cpu_to_le32 (chan->pgtbl[page].handle + offset); + +#if 1 + DPRINTK ("dmabuf #%d (32(h)=%lx)\n", + i, + (long)chan->sgtable[i].addr); +#endif + } + + /* overwrite the last buffer information */ + chan->sgtable[chan->frag_number - 1].count = cpu_to_le32 (chan->frag_size | VIA_EOL); + + /* set location of DMA-able scatter-gather info table */ + DPRINTK ("outl (0x%X, 0x%04lX)\n", + chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR); + + via_ac97_wait_idle (card); + outl (chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR); + udelay (20); + via_ac97_wait_idle (card); + + DPRINTK ("inl (0x%lX) = %x\n", + chan->iobase + VIA_PCM_TABLE_ADDR, + inl(chan->iobase + VIA_PCM_TABLE_ADDR)); + + DPRINTK ("EXIT\n"); + return 0; + +err_out_nomem: + printk (KERN_ERR PFX "DMA buffer alloc fail, aborting\n"); + via_chan_buffer_free (card, chan); + DPRINTK ("EXIT\n"); + return -ENOMEM; +} + + +/** + * via_chan_free - Release a PCM channel + * @card: Private audio chip info + * @chan: Channel to be released + * + * Performs all the functions necessary to clean up + * an initialized channel. + * + * Currently these functions include disabled any + * active DMA operations, setting the PCM channel + * back to a known state, and releasing any allocated + * sound buffers. + */ + +static void via_chan_free (struct via_info *card, struct via_channel *chan) +{ + DPRINTK ("ENTER\n"); + + spin_lock_irq (&card->lock); + + /* stop any existing channel output */ + via_chan_status_clear (chan->iobase); + via_chan_stop (chan->iobase); + via_chan_status_clear (chan->iobase); + + spin_unlock_irq (&card->lock); + + synchronize_irq(); + + DPRINTK ("EXIT\n"); +} + +static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan) +{ + int i; + + DPRINTK ("ENTER\n"); + + /* zero location of DMA-able scatter-gather info table */ + via_ac97_wait_idle(card); + outl (0, chan->iobase + VIA_PCM_TABLE_ADDR); + + for (i = 0; i < chan->page_number; i++) + if (chan->pgtbl[i].cpuaddr) { + pci_free_consistent (card->pdev, PAGE_SIZE, + chan->pgtbl[i].cpuaddr, + chan->pgtbl[i].handle); + chan->pgtbl[i].cpuaddr = NULL; + chan->pgtbl[i].handle = 0; + } + + chan->page_number = 0; + + if (chan->sgtable) { + pci_free_consistent (card->pdev, + (sizeof (struct via_sgd_table) * chan->frag_number), + (void*)chan->sgtable, chan->sgt_handle); + chan->sgtable = NULL; + } + + DPRINTK ("EXIT\n"); +} + + +/** + * via_chan_pcm_fmt - Update PCM channel settings + * @chan: Channel to be updated + * @reset: Boolean. If non-zero, channel will be reset + * to 8-bit mono mode. + * + * Stores the settings of the current PCM format, + * 8-bit or 16-bit, and mono/stereo, into the + * hardware settings for the specified channel. + * If @reset is non-zero, the channel is reset + * to 8-bit mono mode. Otherwise, the channel + * is set to the values stored in the channel + * information struct @chan. + */ + +static void via_chan_pcm_fmt (struct via_channel *chan, int reset) +{ + DPRINTK ("ENTER, pcm_fmt=0x%02X, reset=%s\n", + chan->pcm_fmt, reset ? "yes" : "no"); + + assert (chan != NULL); + + if (reset) + /* reset to 8-bit mono mode */ + chan->pcm_fmt = 0; + + /* enable interrupts on FLAG and EOL */ + chan->pcm_fmt |= VIA_CHAN_TYPE_MASK; + + /* if we are recording, enable recording fifo bit */ + if (chan->is_record) + chan->pcm_fmt |= VIA_PCM_REC_FIFO; + /* set interrupt select bits where applicable (PCM in & out channels) */ + if (!chan->is_record) + chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT; + + outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE); + + DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n", + chan->pcm_fmt, + inb (chan->iobase + VIA_PCM_TYPE)); +} + + +/** + * via_chan_clear - Stop DMA channel operation, and reset pointers + * @card: the chip to accessed + * @chan: Channel to be cleared + * + * Call via_chan_stop to halt DMA operations, and then resets + * all software pointers which track DMA operation. + */ + +static void via_chan_clear (struct via_info *card, struct via_channel *chan) +{ + DPRINTK ("ENTER\n"); + via_chan_stop (chan->iobase); + via_chan_buffer_free(card, chan); + chan->is_active = 0; + chan->is_mapped = 0; + chan->is_enabled = 1; + chan->slop_len = 0; + chan->sw_ptr = 0; + chan->n_irqs = 0; + atomic_set (&chan->hw_ptr, 0); + DPRINTK ("EXIT\n"); +} + + +/** + * via_chan_set_speed - Set PCM sample rate for given channel + * @card: Private info for specified board + * @chan: Channel whose sample rate will be adjusted + * @val: New sample rate, in Khz + * + * Helper function for the %SNDCTL_DSP_SPEED ioctl. OSS semantics + * demand that all audio operations halt (if they are not already + * halted) when the %SNDCTL_DSP_SPEED is given. + * + * This function halts all audio operations for the given channel + * @chan, and then calls via_set_rate to set the audio hardware + * to the new rate. + */ + +static int via_chan_set_speed (struct via_info *card, + struct via_channel *chan, int val) +{ + DPRINTK ("ENTER, requested rate = %d\n", val); + + via_chan_clear (card, chan); + + val = via_set_rate (&card->ac97, chan, val); + + DPRINTK ("EXIT, returning %d\n", val); + return val; +} + + +/** + * via_chan_set_fmt - Set PCM sample size for given channel + * @card: Private info for specified board + * @chan: Channel whose sample size will be adjusted + * @val: New sample size, use the %AFMT_xxx constants + * + * Helper function for the %SNDCTL_DSP_SETFMT ioctl. OSS semantics + * demand that all audio operations halt (if they are not already + * halted) when the %SNDCTL_DSP_SETFMT is given. + * + * This function halts all audio operations for the given channel + * @chan, and then calls via_chan_pcm_fmt to set the audio hardware + * to the new sample size, either 8-bit or 16-bit. + */ + +static int via_chan_set_fmt (struct via_info *card, + struct via_channel *chan, int val) +{ + DPRINTK ("ENTER, val=%s\n", + val == AFMT_U8 ? "AFMT_U8" : + val == AFMT_S16_LE ? "AFMT_S16_LE" : + "unknown"); + + via_chan_clear (card, chan); + + assert (val != AFMT_QUERY); /* this case is handled elsewhere */ + + switch (val) { + case AFMT_S16_LE: + if ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) == 0) { + chan->pcm_fmt |= VIA_PCM_FMT_16BIT; + via_chan_pcm_fmt (chan, 0); + } + break; + + case AFMT_U8: + if (chan->pcm_fmt & VIA_PCM_FMT_16BIT) { + chan->pcm_fmt &= ~VIA_PCM_FMT_16BIT; + via_chan_pcm_fmt (chan, 0); + } + break; + + default: + DPRINTK ("unknown AFMT: 0x%X\n", val); + val = AFMT_S16_LE; + } + + DPRINTK ("EXIT\n"); + return val; +} + + +/** + * via_chan_set_stereo - Enable or disable stereo for a DMA channel + * @card: Private info for specified board + * @chan: Channel whose stereo setting will be adjusted + * @val: New sample size, use the %AFMT_xxx constants + * + * Helper function for the %SNDCTL_DSP_CHANNELS and %SNDCTL_DSP_STEREO ioctls. OSS semantics + * demand that all audio operations halt (if they are not already + * halted) when %SNDCTL_DSP_CHANNELS or SNDCTL_DSP_STEREO is given. + * + * This function halts all audio operations for the given channel + * @chan, and then calls via_chan_pcm_fmt to set the audio hardware + * to enable or disable stereo. + */ + +static int via_chan_set_stereo (struct via_info *card, + struct via_channel *chan, int val) +{ + DPRINTK ("ENTER, channels = %d\n", val); + + via_chan_clear (card, chan); + + switch (val) { + + /* mono */ + case 1: + chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO; + via_chan_pcm_fmt (chan, 0); + break; + + /* stereo */ + case 2: + chan->pcm_fmt |= VIA_PCM_FMT_STEREO; + via_chan_pcm_fmt (chan, 0); + break; + + /* unknown */ + default: + printk (KERN_WARNING PFX "unknown number of channels\n"); + val = -EINVAL; + break; + } + + DPRINTK ("EXIT, returning %d\n", val); + return val; +} + +static int via_chan_set_buffering (struct via_info *card, + struct via_channel *chan, int val) +{ + int shift; + + DPRINTK ("ENTER\n"); + + /* in both cases the buffer cannot be changed */ + if (chan->is_active || chan->is_mapped) { + DPRINTK ("EXIT\n"); + return -EINVAL; + } + + /* called outside SETFRAGMENT */ + /* set defaults or do nothing */ + if (val < 0) { + + if (chan->frag_size && chan->frag_number) + goto out; + + 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; + + shift = 0; + while (chan->frag_size) { + chan->frag_size >>= 1; + shift++; + } + chan->frag_size = 1 << shift; + + chan->frag_number = (VIA_DEFAULT_BUFFER_TIME / VIA_DEFAULT_FRAG_TIME); + + DPRINTK ("setting default values %d %d\n", chan->frag_size, chan->frag_number); + } else { + chan->frag_size = 1 << (val & 0xFFFF); + chan->frag_number = (val >> 16) & 0xFFFF; + + DPRINTK ("using user values %d %d\n", chan->frag_size, chan->frag_number); + } + + /* quake3 wants frag_number to be a power of two */ + shift = 0; + while (chan->frag_number) { + chan->frag_number >>= 1; + shift++; + } + chan->frag_number = 1 << shift; + + if (chan->frag_size > VIA_MAX_FRAG_SIZE) + chan->frag_size = VIA_MAX_FRAG_SIZE; + else if (chan->frag_size < VIA_MIN_FRAG_SIZE) + chan->frag_size = VIA_MIN_FRAG_SIZE; + + if (chan->frag_number < VIA_MIN_FRAG_NUMBER) + chan->frag_number = VIA_MIN_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; + +out: + if (chan->is_record) + atomic_set (&chan->n_frags, 0); + else + atomic_set (&chan->n_frags, chan->frag_number); + + DPRINTK ("EXIT\n"); + + return 0; +} + +#ifdef VIA_CHAN_DUMP_BUFS +/** + * via_chan_dump_bufs - Display DMA table contents + * @chan: Channel whose DMA table will be displayed + * + * Debugging function which displays the contents of the + * scatter-gather DMA table for the given channel @chan. + */ + +static void via_chan_dump_bufs (struct via_channel *chan) +{ + int i; + + for (i = 0; i < chan->frag_number; i++) { + DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n", + i, chan->sgtable[i].addr, + chan->sgtable[i].count & 0x00FFFFFF, + chan->sgtable[i].count & VIA_FLAG ? 1 : 0, + chan->sgtable[i].count & VIA_EOL ? 1 : 0); + } + DPRINTK ("buf_in_use = %d, nextbuf = %d\n", + atomic_read (&chan->buf_in_use), + atomic_read (&chan->sw_ptr)); +} +#endif /* VIA_CHAN_DUMP_BUFS */ + + +/** + * via_chan_flush_frag - Flush partially-full playback buffer to hardware + * @chan: Channel whose DMA table will be displayed + * + * Flushes partially-full playback buffer to hardware. + */ + +static void via_chan_flush_frag (struct via_channel *chan) +{ + DPRINTK ("ENTER\n"); + + assert (chan->slop_len > 0); + + if (chan->sw_ptr == (chan->frag_number - 1)) + chan->sw_ptr = 0; + else + chan->sw_ptr++; + + chan->slop_len = 0; + + assert (atomic_read (&chan->n_frags) > 0); + atomic_dec (&chan->n_frags); + + DPRINTK ("EXIT\n"); +} + + + +/** + * via_chan_maybe_start - Initiate audio hardware DMA operation + * @chan: Channel whose DMA is to be started + * + * Initiate DMA operation, if the DMA engine for the given + * channel @chan is not already active. + */ + +static inline void via_chan_maybe_start (struct via_channel *chan) +{ + assert (chan->is_active == sg_active(chan->iobase)); + + if (!chan->is_active && chan->is_enabled) { + chan->is_active = 1; + sg_begin (chan); + DPRINTK ("starting channel %s\n", chan->name); + } +} + + +/**************************************************************** + * + * Interface to ac97-codec module + * + * + */ + +/** + * via_ac97_wait_idle - Wait until AC97 codec is not busy + * @card: Private info for specified board + * + * Sleep until the AC97 codec is no longer busy. + * Returns the final value read from the SGD + * register being polled. + */ + +static u8 via_ac97_wait_idle (struct via_info *card) +{ + u8 tmp8; + int counter = VIA_COUNTER_LIMIT; + + DPRINTK ("ENTER/EXIT\n"); + + assert (card != NULL); + assert (card->pdev != NULL); + + do { + udelay (15); + + tmp8 = inb (card->baseaddr + 0x83); + } while ((tmp8 & VIA_CR83_BUSY) && (counter-- > 0)); + + if (tmp8 & VIA_CR83_BUSY) + printk (KERN_WARNING PFX "timeout waiting on AC97 codec\n"); + return tmp8; +} + + +/** + * via_ac97_read_reg - Read AC97 standard register + * @codec: Pointer to generic AC97 codec info + * @reg: Index of AC97 register to be read + * + * Read the value of a single AC97 codec register, + * as defined by the Intel AC97 specification. + * + * Defines the standard AC97 read-register operation + * required by the kernel's ac97_codec interface. + * + * Returns the 16-bit value stored in the specified + * register. + */ + +static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg) +{ + unsigned long data; + struct via_info *card; + int counter; + + DPRINTK ("ENTER\n"); + + assert (codec != NULL); + assert (codec->private_data != NULL); + + card = codec->private_data; + + /* 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 + the same time as the command */ + data = (reg << 16) | VIA_CR80_READ | VIA_CR80_VALID; + + outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); + udelay (20); + + for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) { + udelay (1); + if ((((data = inl(card->baseaddr + VIA_BASE0_AC97_CTRL)) & + (VIA_CR80_VALID|VIA_CR80_BUSY)) == VIA_CR80_VALID)) + goto out; + } + + printk (KERN_WARNING PFX "timeout while reading AC97 codec (0x%lX)\n", data); + goto err_out; + +out: + /* Once the valid bit has become set, we must wait a complete AC97 + frame before the data has settled. */ + udelay(25); + data = (unsigned long) inl (card->baseaddr + VIA_BASE0_AC97_CTRL); + + outb (0x02, card->baseaddr + 0x83); + + if (((data & 0x007F0000) >> 16) == reg) { + DPRINTK ("EXIT, success, data=0x%lx, retval=0x%lx\n", + data, data & 0x0000FFFF); + return data & 0x0000FFFF; + } + + printk (KERN_WARNING "via82cxxx_audio: not our index: reg=0x%x, newreg=0x%lx\n", + reg, ((data & 0x007F0000) >> 16)); + +err_out: + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +/** + * via_ac97_write_reg - Write AC97 standard register + * @codec: Pointer to generic AC97 codec info + * @reg: Index of AC97 register to be written + * @value: Value to be written to AC97 register + * + * Write the value of a single AC97 codec register, + * as defined by the Intel AC97 specification. + * + * Defines the standard AC97 write-register operation + * required by the kernel's ac97_codec interface. + */ + +static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value) +{ + u32 data; + struct via_info *card; + int counter; + + DPRINTK ("ENTER\n"); + + assert (codec != NULL); + assert (codec->private_data != NULL); + + card = codec->private_data; + + data = (reg << 16) + value; + outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); + udelay (10); + + for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) { + if ((inb (card->baseaddr + 0x83) & VIA_CR83_BUSY) == 0) + goto out; + + udelay (15); + } + + printk (KERN_WARNING PFX "timeout after AC97 codec write (0x%X, 0x%X)\n", reg, value); + +out: + DPRINTK ("EXIT\n"); +} + + +static int via_mixer_open (struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct via_info *card; + struct pci_dev *pdev; + struct pci_driver *drvr; + + DPRINTK ("ENTER\n"); + + pci_for_each_dev(pdev) { + drvr = pci_dev_driver (pdev); + if (drvr == &via_driver) { + assert (pci_get_drvdata (pdev) != NULL); + + card = pci_get_drvdata (pdev); + if (card->ac97.dev_mixer == minor) + goto match; + } + } + + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + +match: + file->private_data = &card->ac97; + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + +static int via_mixer_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = file->private_data; + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc; + + DPRINTK ("ENTER\n"); + + assert (codec != NULL); + card = codec->private_data; + assert (card != NULL); + + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + rc = codec->mixer_ioctl(codec, cmd, arg); + + up (&card->syscall_sem); + +out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static struct file_operations via_mixer_fops = { + owner: THIS_MODULE, + open: via_mixer_open, + llseek: no_llseek, + ioctl: via_mixer_ioctl, +}; + + +static int __init via_ac97_reset (struct via_info *card) +{ + struct pci_dev *pdev = card->pdev; + u8 tmp8; + u16 tmp16; + + DPRINTK ("ENTER\n"); + + assert (pdev != NULL); + +#ifndef NDEBUG + { + u8 r40,r41,r42,r43,r44,r48; + pci_read_config_byte (card->pdev, 0x40, &r40); + pci_read_config_byte (card->pdev, 0x41, &r41); + pci_read_config_byte (card->pdev, 0x42, &r42); + pci_read_config_byte (card->pdev, 0x43, &r43); + pci_read_config_byte (card->pdev, 0x44, &r44); + pci_read_config_byte (card->pdev, 0x48, &r48); + DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", + r40,r41,r42,r43,r44,r48); + + spin_lock_irq (&card->lock); + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + spin_unlock_irq (&card->lock); + + } +#endif + + /* + * Reset AC97 controller: enable, disable, enable, + * pausing after each command for good luck. Only + * do this if the codec is not ready, because it causes + * loud pops and such due to such a hard codec reset. + */ + pci_read_config_byte (pdev, VIA_ACLINK_STATUS, &tmp8); + if ((tmp8 & VIA_CR40_AC97_READY) == 0) { + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, + VIA_CR41_AC97_ENABLE | + VIA_CR41_AC97_RESET | + VIA_CR41_AC97_WAKEUP); + udelay (100); + + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, 0); + udelay (100); + + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, + VIA_CR41_AC97_ENABLE | + VIA_CR41_PCM_ENABLE | + VIA_CR41_VRA | VIA_CR41_AC97_RESET); + udelay (100); + } + + /* Make sure VRA is enabled, in case we didn't do a + * complete codec reset, above + */ + pci_read_config_byte (pdev, VIA_ACLINK_CTRL, &tmp8); + if (((tmp8 & VIA_CR41_VRA) == 0) || + ((tmp8 & VIA_CR41_AC97_ENABLE) == 0) || + ((tmp8 & VIA_CR41_PCM_ENABLE) == 0) || + ((tmp8 & VIA_CR41_AC97_RESET) == 0)) { + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, + VIA_CR41_AC97_ENABLE | + VIA_CR41_PCM_ENABLE | + VIA_CR41_VRA | VIA_CR41_AC97_RESET); + udelay (100); + } + +#if 0 /* this breaks on K7M */ + /* 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); + + /* 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); + if ((tmp16 & 1) == 0) + via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static void via_ac97_codec_wait (struct ac97_codec *codec) +{ + assert (codec->private_data != NULL); + via_ac97_wait_idle (codec->private_data); +} + + +static int __init via_ac97_init (struct via_info *card) +{ + int rc; + u16 tmp16; + + DPRINTK ("ENTER\n"); + + 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.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"); + return -EIO; + } + + rc = via_ac97_reset (card); + if (rc) { + printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n"); + goto err_out; + } + + 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); + + /* + * 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); + 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); + if ((tmp16 & 1) == 0) { + card->locked_rate = 1; + printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); + } + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out: + unregister_sound_mixer (card->ac97.dev_mixer); + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static void via_ac97_cleanup (struct via_info *card) +{ + DPRINTK ("ENTER\n"); + + assert (card != NULL); + assert (card->ac97.dev_mixer >= 0); + + unregister_sound_mixer (card->ac97.dev_mixer); + + DPRINTK ("EXIT\n"); +} + + + +/**************************************************************** + * + * Interrupt-related code + * + */ + +/** + * via_intr_channel - handle an interrupt for a single channel + * @chan: handle interrupt for this channel + * + * This is the "meat" of the interrupt handler, + * containing the actions taken each time an interrupt + * occurs. All communication and coordination with + * userspace takes place here. + * + * Locking: inside card->lock + */ + +static void via_intr_channel (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) + return; + + /* acknowledge any flagged bits ASAP */ + outb (status, chan->iobase); + + if (!chan->sgtable) /* XXX: temporary solution */ + return; + + /* grab current h/w ptr value */ + n = atomic_read (&chan->hw_ptr); + + /* sanity check: make sure our h/w ptr doesn't have a weird value */ + 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 + */ + if (n == (chan->frag_number - 1)) { + chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_EOL); + atomic_set (&chan->hw_ptr, 0); + } else { + chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_FLAG); + atomic_inc (&chan->hw_ptr); + } + + /* accounting crap for SNDCTL_DSP_GETxPTR */ + chan->n_irqs++; + chan->bytes += chan->frag_size; + if (chan->bytes < 0) /* handle overflow of 31-bit value */ + chan->bytes = chan->frag_size; + + /* wake up anyone listening to see when interrupts occur */ + if (waitqueue_active (&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)); +} + + +static void via_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_INTR_MASK)) + { +#ifdef CONFIG_MIDI_VIA82CXXX + if (card->midi_devc) + uart401intr(irq, card->midi_devc, regs); +#endif + return; + } + DPRINTK ("intr, status32 == 0x%08X\n", status32); + + /* synchronize interrupt handling under SMP. this spinlock + * goes away completely on UP + */ + spin_lock (&card->lock); + + if (status32 & VIA_INTR_OUT) + via_intr_channel (&card->ch_out); + if (status32 & VIA_INTR_IN) + via_intr_channel (&card->ch_in); + if (status32 & VIA_INTR_FM) + via_intr_channel (&card->ch_fm); + + spin_unlock (&card->lock); +} + + +/** + * via_interrupt_init - Initialize interrupt handling + * @card: Private info for specified board + * + * Obtain and reserve IRQ for using in handling audio events. + * Also, disable any IRQ-generating resources, to make sure + * we don't get interrupts before we want them. + */ + +static int via_interrupt_init (struct via_info *card) +{ + u8 tmp8; + + DPRINTK ("ENTER\n"); + + assert (card != NULL); + assert (card->pdev != NULL); + + /* check for sane IRQ number. can this ever happen? */ + if (card->pdev->irq < 2) { + printk (KERN_ERR PFX "insane IRQ %d, aborting\n", + card->pdev->irq); + DPRINTK ("EXIT, returning -EIO\n"); + 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); + } + + 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; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +/**************************************************************** + * + * OSS DSP device + * + */ + +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, +}; + + +static int __init via_dsp_init (struct via_info *card) +{ + u8 tmp8; + + DPRINTK ("ENTER\n"); + + 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); + } + + via_stop_everything (card); + + card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1); + if (card->dev_dsp < 0) { + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + } + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static void via_dsp_cleanup (struct via_info *card) +{ + DPRINTK ("ENTER\n"); + + assert (card != NULL); + assert (card->dev_dsp >= 0); + + via_stop_everything (card); + + unregister_sound_dsp (card->dev_dsp); + + DPRINTK ("EXIT\n"); +} + + +static struct page * via_mm_nopage (struct vm_area_struct * vma, + unsigned long address, int write_access) +{ + struct via_info *card = vma->vm_private_data; + struct via_channel *chan = &card->ch_out; + struct page *dmapage; + unsigned long pgoff; + int rd, wr; + + DPRINTK ("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh, wr %d\n", + vma->vm_start, + address - vma->vm_start, + (address - vma->vm_start) >> PAGE_SHIFT, + address, + write_access); + + if (address > vma->vm_end) { + DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n"); + return NOPAGE_SIGBUS; /* Disallow mremap */ + } + if (!card) { + DPRINTK ("EXIT, returning NOPAGE_OOM\n"); + return NOPAGE_OOM; /* Nothing allocated */ + } + + pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT); + rd = card->ch_in.is_mapped; + wr = card->ch_out.is_mapped; + +#ifndef VIA_NDEBUG + { + unsigned long max_bufs = chan->frag_number; + if (rd && wr) max_bufs *= 2; + /* via_dsp_mmap() should ensure this */ + assert (pgoff < max_bufs); + } +#endif + + /* if full-duplex (read+write) and we have two sets of bufs, + * then the playback buffers come first, sez soundcard.c */ + if (pgoff >= chan->page_number) { + pgoff -= chan->page_number; + chan = &card->ch_in; + } else if (!wr) + chan = &card->ch_in; + + assert ((((unsigned long)chan->pgtbl[pgoff].cpuaddr) % PAGE_SIZE) == 0); + + dmapage = virt_to_page (chan->pgtbl[pgoff].cpuaddr); + DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n", + dmapage, (unsigned long) chan->pgtbl[pgoff].cpuaddr); + get_page (dmapage); + return dmapage; +} + + +#ifndef VM_RESERVED +static int via_mm_swapout (struct page *page, struct file *filp) +{ + return 0; +} +#endif /* VM_RESERVED */ + + +struct vm_operations_struct via_mm_ops = { + nopage: via_mm_nopage, + +#ifndef VM_RESERVED + swapout: via_mm_swapout, +#endif +}; + + +static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc = -EINVAL, rd=0, wr=0; + unsigned long max_size, size, start, offset; + + assert (file != NULL); + assert (vma != NULL); + card = file->private_data; + assert (card != NULL); + + DPRINTK ("ENTER, start %lXh, size %ld, pgoff %ld\n", + vma->vm_start, + vma->vm_end - vma->vm_start, + vma->vm_pgoff); + + max_size = 0; + if (vma->vm_flags & VM_READ) { + rd = 1; + via_chan_set_buffering(card, &card->ch_in, -1); + via_chan_buffer_init (card, &card->ch_in); + max_size += card->ch_in.page_number << PAGE_SHIFT; + } + if (vma->vm_flags & VM_WRITE) { + wr = 1; + via_chan_set_buffering(card, &card->ch_out, -1); + via_chan_buffer_init (card, &card->ch_out); + max_size += card->ch_out.page_number << PAGE_SHIFT; + } + + start = vma->vm_start; + offset = (vma->vm_pgoff << PAGE_SHIFT); + size = vma->vm_end - vma->vm_start; + + /* some basic size/offset sanity checks */ + if (size > max_size) + goto out; + if (offset > max_size - size) + goto out; + + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + vma->vm_ops = &via_mm_ops; + vma->vm_private_data = card; + +#ifdef VM_RESERVED + vma->vm_flags |= VM_RESERVED; +#endif + + if (rd) + card->ch_in.is_mapped = 1; + if (wr) + card->ch_out.is_mapped = 1; + + up (&card->syscall_sem); + rc = 0; + +out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static ssize_t via_dsp_do_read (struct via_info *card, + char *userbuf, size_t count, + int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + const char *orig_userbuf = userbuf; + struct via_channel *chan = &card->ch_in; + size_t size; + int n, tmp; + ssize_t ret = 0; + + /* if SGD has not yet been started, start it */ + via_chan_maybe_start (chan); + +handle_one_block: + /* just to be a nice neighbor */ + /* Thomas Sailer: + * But also to ourselves, release semaphore if we do so */ + if (need_resched()) { + up(&card->syscall_sem); + schedule (); + ret = via_syscall_down (card, nonblock); + if (ret) + goto out; + } + + /* grab current channel software pointer. In the case of + * recording, this is pointing to the next buffer that + * will receive data from the audio hardware. + */ + n = chan->sw_ptr; + + /* n_frags represents the number of fragments waiting + * to be copied to userland. sleep until at least + * one buffer has been read from the audio hardware. + */ + add_wait_queue(&chan->wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + tmp = atomic_read (&chan->n_frags); + assert (tmp >= 0); + assert (tmp <= chan->frag_number); + if (tmp) + break; + if (nonblock || !chan->is_active) { + ret = -EAGAIN; + break; + } + + up(&card->syscall_sem); + + DPRINTK ("Sleeping on block %d\n", n); + schedule(); + + ret = via_syscall_down (card, nonblock); + if (ret) + break; + + if (signal_pending (current)) { + ret = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&chan->wait, &wait); + if (ret) + goto out; + + /* Now that we have a buffer we can read from, send + * as much as sample data possible to userspace. + */ + while ((count > 0) && (chan->slop_len < chan->frag_size)) { + size_t slop_left = chan->frag_size - chan->slop_len; + void *base = chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr; + unsigned ofs = n % (PAGE_SIZE / chan->frag_size); + + size = (count < slop_left) ? count : slop_left; + if (copy_to_user (userbuf, + base + ofs + chan->slop_len, + size)) { + ret = -EFAULT; + goto out; + } + + count -= size; + chan->slop_len += size; + userbuf += size; + } + + /* If we didn't copy the buffer completely to userspace, + * stop now. + */ + if (chan->slop_len < chan->frag_size) + goto out; + + /* + * If we get to this point, we copied one buffer completely + * to userspace, give the buffer back to the hardware. + */ + + /* advance channel software pointer to point to + * the next buffer from which we will copy + */ + if (chan->sw_ptr == (chan->frag_number - 1)) + chan->sw_ptr = 0; + else + chan->sw_ptr++; + + /* mark one less buffer waiting to be processed */ + assert (atomic_read (&chan->n_frags) > 0); + atomic_dec (&chan->n_frags); + + /* we are at a block boundary, there is no fragment data */ + chan->slop_len = 0; + + 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", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + + if (count > 0) + goto handle_one_block; + +out: + return (userbuf != orig_userbuf) ? (userbuf - orig_userbuf) : ret; +} + + +static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc; + + DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n", + file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); + + assert (file != NULL); + assert (buffer != NULL); + card = file->private_data; + assert (card != NULL); + + if (ppos != &file->f_pos) { + DPRINTK ("EXIT, returning -ESPIPE\n"); + return -ESPIPE; + } + + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + if (card->ch_in.is_mapped) { + rc = -ENXIO; + goto out_up; + } + + via_chan_set_buffering(card, &card->ch_in, -1); + rc = via_chan_buffer_init (card, &card->ch_in); + + if (rc) + goto out_up; + + rc = via_dsp_do_read (card, buffer, count, nonblock); + +out_up: + up (&card->syscall_sem); +out: + DPRINTK ("EXIT, returning %ld\n",(long) rc); + return rc; +} + + +static ssize_t via_dsp_do_write (struct via_info *card, + const char *userbuf, size_t count, + int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + const char *orig_userbuf = userbuf; + struct via_channel *chan = &card->ch_out; + volatile struct via_sgd_table *sgtable = chan->sgtable; + size_t size; + int n, tmp; + ssize_t ret = 0; + +handle_one_block: + /* just to be a nice neighbor */ + /* Thomas Sailer: + * But also to ourselves, release semaphore if we do so */ + if (need_resched()) { + up(&card->syscall_sem); + schedule (); + ret = via_syscall_down (card, nonblock); + if (ret) + goto out; + } + + /* grab current channel fragment pointer. In the case of + * playback, this is pointing to the next fragment that + * should receive data from userland. + */ + n = chan->sw_ptr; + + /* n_frags represents the number of fragments remaining + * to be filled by userspace. Sleep until + * at least one fragment is available for our use. + */ + add_wait_queue(&chan->wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + tmp = atomic_read (&chan->n_frags); + assert (tmp >= 0); + assert (tmp <= chan->frag_number); + if (tmp) + break; + if (nonblock || !chan->is_active) { + ret = -EAGAIN; + break; + } + + up(&card->syscall_sem); + + DPRINTK ("Sleeping on page %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record); + schedule(); + + ret = via_syscall_down (card, nonblock); + if (ret) + break; + + if (signal_pending (current)) { + ret = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&chan->wait, &wait); + if (ret) + goto out; + + /* Now that we have at least one fragment we can write to, fill the buffer + * as much as possible with data from userspace. + */ + while ((count > 0) && (chan->slop_len < chan->frag_size)) { + size_t slop_left = chan->frag_size - chan->slop_len; + + size = (count < slop_left) ? count : slop_left; + if (copy_from_user (chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + (n % (PAGE_SIZE / chan->frag_size)) * chan->frag_size + chan->slop_len, + userbuf, size)) { + ret = -EFAULT; + goto out; + } + + count -= size; + chan->slop_len += size; + userbuf += size; + } + + /* If we didn't fill up the buffer with data, stop now. + * Put a 'stop' marker in the DMA table too, to tell the + * audio hardware to stop if it gets here. + */ + if (chan->slop_len < chan->frag_size) { + sgtable[n].count = cpu_to_le32 (chan->slop_len | VIA_EOL | VIA_STOP); + goto out; + } + + /* + * If we get to this point, we have filled a buffer with + * audio data, flush the buffer to audio hardware. + */ + + /* Record the true size for the audio hardware to notice */ + if (n == (chan->frag_number - 1)) + sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_EOL); + else + sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_FLAG); + + /* advance channel software pointer to point to + * the next buffer we will fill with data + */ + if (chan->sw_ptr == (chan->frag_number - 1)) + chan->sw_ptr = 0; + else + chan->sw_ptr++; + + /* mark one less buffer as being available for userspace consumption */ + assert (atomic_read (&chan->n_frags) > 0); + atomic_dec (&chan->n_frags); + + /* we are at a block boundary, there is no fragment data */ + chan->slop_len = 0; + + /* if SGD has not yet been started, start it */ + via_chan_maybe_start (chan); + + 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", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + + if (count > 0) + goto handle_one_block; + +out: + return userbuf - orig_userbuf; +} + + +static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct via_info *card; + ssize_t rc; + int nonblock = (file->f_flags & O_NONBLOCK); + + DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n", + file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); + + assert (file != NULL); + assert (buffer != NULL); + card = file->private_data; + assert (card != NULL); + + if (ppos != &file->f_pos) { + DPRINTK ("EXIT, returning -ESPIPE\n"); + return -ESPIPE; + } + + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + if (card->ch_out.is_mapped) { + rc = -ENXIO; + goto out_up; + } + + via_chan_set_buffering(card, &card->ch_out, -1); + rc = via_chan_buffer_init (card, &card->ch_out); + + if (rc) + goto out_up; + + rc = via_dsp_do_write (card, buffer, count, nonblock); + +out_up: + up (&card->syscall_sem); +out: + DPRINTK ("EXIT, returning %ld\n",(long) rc); + return rc; +} + + +static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait) +{ + struct via_info *card; + struct via_channel *chan; + unsigned int mask = 0; + + DPRINTK ("ENTER\n"); + + assert (file != NULL); + card = file->private_data; + assert (card != NULL); + + if (file->f_mode & FMODE_READ) { + chan = &card->ch_in; + if (sg_active (chan->iobase)) + poll_wait(file, &chan->wait, wait); + if (atomic_read (&chan->n_frags) > 0) + mask |= POLLIN | POLLRDNORM; + } + + if (file->f_mode & FMODE_WRITE) { + chan = &card->ch_out; + if (sg_active (chan->iobase)) + poll_wait(file, &chan->wait, wait); + if (atomic_read (&chan->n_frags) > 0) + mask |= POLLOUT | POLLWRNORM; + } + + DPRINTK ("EXIT, returning %u\n", mask); + return mask; +} + + +/** + * via_dsp_drain_playback - sleep until all playback samples are flushed + * @card: Private info for specified board + * @chan: Channel to drain + * @nonblock: boolean, non-zero if O_NONBLOCK is set + * + * Sleeps until all playback has been flushed to the audio + * hardware. + * + * Locking: inside card->syscall_sem + */ + +static int via_dsp_drain_playback (struct via_info *card, + struct via_channel *chan, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + int ret = 0; + + DPRINTK ("ENTER, nonblock = %d\n", nonblock); + + if (chan->slop_len > 0) + via_chan_flush_frag (chan); + + if (atomic_read (&chan->n_frags) == chan->frag_number) + goto out; + + via_chan_maybe_start (chan); + + add_wait_queue(&chan->wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + if (atomic_read (&chan->n_frags) >= chan->frag_number) + break; + + if (nonblock) { + DPRINTK ("EXIT, returning -EAGAIN\n"); + ret = -EAGAIN; + break; + } + +#ifdef VIA_DEBUG + { + u8 r40,r41,r42,r43,r44,r48; + pci_read_config_byte (card->pdev, 0x40, &r40); + pci_read_config_byte (card->pdev, 0x41, &r41); + pci_read_config_byte (card->pdev, 0x42, &r42); + pci_read_config_byte (card->pdev, 0x43, &r43); + pci_read_config_byte (card->pdev, 0x44, &r44); + pci_read_config_byte (card->pdev, 0x48, &r48); + DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", + r40,r41,r42,r43,r44,r48); + + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + } + + if (!chan->is_active) + printk (KERN_ERR "sleeping but not active\n"); +#endif + + up(&card->syscall_sem); + + DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_frags)); + schedule(); + + if ((ret = via_syscall_down (card, nonblock))) + break; + + if (signal_pending (current)) { + DPRINTK ("EXIT, returning -ERESTARTSYS\n"); + ret = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&chan->wait, &wait); + +#ifdef VIA_DEBUG + { + u8 r40,r41,r42,r43,r44,r48; + pci_read_config_byte (card->pdev, 0x40, &r40); + pci_read_config_byte (card->pdev, 0x41, &r41); + pci_read_config_byte (card->pdev, 0x42, &r42); + pci_read_config_byte (card->pdev, 0x43, &r43); + pci_read_config_byte (card->pdev, 0x44, &r44); + pci_read_config_byte (card->pdev, 0x48, &r48); + DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", + r40,r41,r42,r43,r44,r48); + + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + + DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_frags)); + } +#endif + +out: + DPRINTK ("EXIT, returning %d\n", ret); + return ret; +} + + +/** + * via_dsp_ioctl_space - get information about channel buffering + * @card: Private info for specified board + * @chan: pointer to channel-specific info + * @arg: user buffer for returned information + * + * Handles SNDCTL_DSP_GETISPACE and SNDCTL_DSP_GETOSPACE. + * + * Locking: inside card->syscall_sem + */ + +static int via_dsp_ioctl_space (struct via_info *card, + struct via_channel *chan, + void *arg) +{ + audio_buf_info info; + + via_chan_set_buffering(card, chan, -1); + + info.fragstotal = chan->frag_number; + info.fragsize = chan->frag_size; + + /* number of full fragments we can read/write without blocking */ + info.fragments = atomic_read (&chan->n_frags); + + if ((chan->slop_len % chan->frag_size > 0) && (info.fragments > 0)) + info.fragments--; + + /* number of bytes that can be read or written immediately + * without blocking. + */ + info.bytes = (info.fragments * chan->frag_size); + if (chan->slop_len % chan->frag_size > 0) + info.bytes += chan->frag_size - (chan->slop_len % chan->frag_size); + + DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n", + info.fragstotal, + info.fragsize, + info.fragments, + info.bytes); + + return copy_to_user (arg, &info, sizeof (info)); +} + + +/** + * via_dsp_ioctl_ptr - get information about hardware buffer ptr + * @card: Private info for specified board + * @chan: pointer to channel-specific info + * @arg: user buffer for returned information + * + * Handles SNDCTL_DSP_GETIPTR and SNDCTL_DSP_GETOPTR. + * + * Locking: inside card->syscall_sem + */ + +static int via_dsp_ioctl_ptr (struct via_info *card, + struct via_channel *chan, + void *arg) +{ + count_info info; + + spin_lock_irq (&card->lock); + + info.bytes = chan->bytes; + info.blocks = chan->n_irqs; + chan->n_irqs = 0; + + spin_unlock_irq (&card->lock); + + 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); + info.ptr += extra; + info.bytes += extra; + } else { + info.ptr = 0; + } + + DPRINTK ("EXIT, returning bytes=%d, blocks=%d, ptr=%d\n", + info.bytes, + info.blocks, + info.ptr); + + return copy_to_user (arg, &info, sizeof (info)); +} + + +static int via_dsp_ioctl_trigger (struct via_channel *chan, int val) +{ + int enable, do_something; + + if (chan->is_record) + enable = (val & PCM_ENABLE_INPUT); + else + enable = (val & PCM_ENABLE_OUTPUT); + + if (!chan->is_enabled && enable) { + do_something = 1; + } else if (chan->is_enabled && !enable) { + do_something = -1; + } else { + do_something = 0; + } + + DPRINTK ("enable=%d, do_something=%d\n", + enable, do_something); + + if (chan->is_active && do_something) + return -EINVAL; + + if (do_something == 1) { + chan->is_enabled = 1; + via_chan_maybe_start (chan); + DPRINTK ("Triggering input\n"); + } + + else if (do_something == -1) { + chan->is_enabled = 0; + DPRINTK ("Setup input trigger\n"); + } + + return 0; +} + + +static int via_dsp_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int rc, rd=0, wr=0, val=0; + struct via_info *card; + struct via_channel *chan; + int nonblock = (file->f_flags & O_NONBLOCK); + + assert (file != NULL); + card = file->private_data; + assert (card != NULL); + + if (file->f_mode & FMODE_WRITE) + wr = 1; + if (file->f_mode & FMODE_READ) + rd = 1; + + rc = via_syscall_down (card, nonblock); + if (rc) + return rc; + rc = -EINVAL; + + switch (cmd) { + + /* OSS API version. XXX unverified */ + case OSS_GETVERSION: + DPRINTK ("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n"); + rc = put_user (SOUND_VERSION, (int *)arg); + break; + + /* list of supported PCM data formats */ + case SNDCTL_DSP_GETFMTS: + DPRINTK ("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n"); + rc = put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg); + break; + + /* query or set current channel's PCM data format */ + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_SETFMT, val==%d\n", val); + if (val != AFMT_QUERY) { + rc = 0; + + if (rd) + rc = via_chan_set_fmt (card, &card->ch_in, val); + + if (rc >= 0 && wr) + rc = via_chan_set_fmt (card, &card->ch_out, val); + + if (rc < 0) + break; + + val = rc; + } else { + if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_16BIT)) || + (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_16BIT))) + val = AFMT_S16_LE; + else + val = AFMT_U8; + } + DPRINTK ("SETFMT EXIT, returning %d\n", val); + rc = put_user (val, (int *)arg); + break; + + /* query or set number of channels (1=mono, 2=stereo) */ + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_CHANNELS, val==%d\n", val); + if (val != 0) { + rc = 0; + + if (rd) + rc = via_chan_set_stereo (card, &card->ch_in, val); + + if (rc >= 0 && wr) + rc = via_chan_set_stereo (card, &card->ch_out, val); + + if (rc < 0) + break; + + 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; + else + val = 1; + } + DPRINTK ("CHANNELS EXIT, returning %d\n", val); + rc = put_user (val, (int *)arg); + break; + + /* enable (val is not zero) or disable (val == 0) stereo */ + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_STEREO, val==%d\n", val); + rc = 0; + + if (rd) + rc = via_chan_set_stereo (card, &card->ch_in, val ? 2 : 1); + if (rc >= 0 && wr) + rc = via_chan_set_stereo (card, &card->ch_out, val ? 2 : 1); + + if (rc < 0) + break; + + val = rc - 1; + + DPRINTK ("STEREO EXIT, returning %d\n", val); + rc = put_user(val, (int *) arg); + break; + + /* query or set sampling rate */ + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_SPEED, val==%d\n", val); + if (val < 0) { + rc = -EINVAL; + break; + } + if (val > 0) { + rc = 0; + + if (rd) + rc = via_chan_set_speed (card, &card->ch_in, val); + if (rc >= 0 && wr) + rc = via_chan_set_speed (card, &card->ch_out, val); + + if (rc < 0) + break; + + val = rc; + } else { + if (rd) + val = card->ch_in.rate; + else if (wr) + val = card->ch_out.rate; + else + val = 0; + } + DPRINTK ("SPEED EXIT, returning %d\n", val); + rc = put_user (val, (int *)arg); + break; + + /* wait until all buffers have been played, and then stop device */ + case SNDCTL_DSP_SYNC: + DPRINTK ("DSP_SYNC\n"); + rc = 0; + if (wr) { + DPRINTK ("SYNC EXIT (after calling via_dsp_drain_playback)\n"); + rc = via_dsp_drain_playback (card, &card->ch_out, nonblock); + } + break; + + /* stop recording/playback immediately */ + case SNDCTL_DSP_RESET: + DPRINTK ("DSP_RESET\n"); + if (rd) { + via_chan_clear (card, &card->ch_in); + card->ch_in.frag_number = 0; + card->ch_in.frag_size = 0; + atomic_set(&card->ch_in.n_frags, 0); + } + + if (wr) { + via_chan_clear (card, &card->ch_out); + card->ch_out.frag_number = 0; + card->ch_out.frag_size = 0; + atomic_set(&card->ch_out.n_frags, 0); + } + + rc = 0; + break; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + rc = 0; + break; + + /* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */ + case SNDCTL_DSP_GETCAPS: + DPRINTK ("DSP_GETCAPS\n"); + rc = put_user(VIA_DSP_CAP, (int *)arg); + break; + + /* obtain buffer fragment size */ + case SNDCTL_DSP_GETBLKSIZE: + DPRINTK ("DSP_GETBLKSIZE\n"); + + if (rd) { + via_chan_set_buffering(card, &card->ch_in, -1); + rc = put_user(card->ch_in.frag_size, (int *)arg); + } else if (wr) { + via_chan_set_buffering(card, &card->ch_out, -1); + rc = put_user(card->ch_out.frag_size, (int *)arg); + } + break; + + /* obtain information about input buffering */ + case SNDCTL_DSP_GETISPACE: + DPRINTK ("DSP_GETISPACE\n"); + if (rd) + rc = via_dsp_ioctl_space (card, &card->ch_in, (void*) arg); + break; + + /* obtain information about output buffering */ + case SNDCTL_DSP_GETOSPACE: + DPRINTK ("DSP_GETOSPACE\n"); + if (wr) + rc = via_dsp_ioctl_space (card, &card->ch_out, (void*) arg); + break; + + /* obtain information about input hardware pointer */ + case SNDCTL_DSP_GETIPTR: + DPRINTK ("DSP_GETIPTR\n"); + if (rd) + rc = via_dsp_ioctl_ptr (card, &card->ch_in, (void*) arg); + break; + + /* obtain information about output hardware pointer */ + case SNDCTL_DSP_GETOPTR: + DPRINTK ("DSP_GETOPTR\n"); + if (wr) + rc = via_dsp_ioctl_ptr (card, &card->ch_out, (void*) arg); + break; + + /* return number of bytes remaining to be played by DMA engine */ + case SNDCTL_DSP_GETODELAY: + { + DPRINTK ("DSP_GETODELAY\n"); + + chan = &card->ch_out; + + if (!wr) + break; + + if (chan->is_active) { + + val = chan->frag_number - atomic_read (&chan->n_frags); + + if (val > 0) { + val *= chan->frag_size; + val -= chan->frag_size - + inl (chan->iobase + VIA_PCM_BLOCK_COUNT); + } + val += chan->slop_len % chan->frag_size; + } else + val = 0; + + assert (val <= (chan->frag_size * chan->frag_number)); + + DPRINTK ("GETODELAY EXIT, val = %d bytes\n", val); + rc = put_user (val, (int *)arg); + break; + } + + /* handle the quick-start of a channel, + * or the notification that a quick-start will + * occur in the future + */ + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n", + rd, wr, card->ch_in.is_active, card->ch_out.is_active, + card->ch_in.is_enabled, card->ch_out.is_enabled); + + rc = 0; + + if (rd) + rc = via_dsp_ioctl_trigger (&card->ch_in, val); + + if (!rc && wr) + rc = via_dsp_ioctl_trigger (&card->ch_out, val); + + break; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && card->ch_in.is_enabled) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && card->ch_out.is_enabled) + val |= PCM_ENABLE_OUTPUT; + rc = put_user(val, (int *)arg); + break; + + /* Enable full duplex. Since we do this as soon as we are opened + * with O_RDWR, this is mainly a no-op that always returns success. + */ + case SNDCTL_DSP_SETDUPLEX: + DPRINTK ("DSP_SETDUPLEX\n"); + if (!rd || !wr) + break; + rc = 0; + break; + + /* set fragment size. implemented as a successful no-op for now */ + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_SETFRAGMENT, val==%d\n", val); + + if (rd) + rc = via_chan_set_buffering(card, &card->ch_in, val); + + if (wr) + rc = via_chan_set_buffering(card, &card->ch_out, val); + + DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n", + val & 0xFFFF, + val & 0xFFFF, + (val >> 16) & 0xFFFF, + (val >> 16) & 0xFFFF); + + rc = 0; + break; + + /* inform device of an upcoming pause in input (or output). */ + case SNDCTL_DSP_POST: + DPRINTK ("DSP_POST\n"); + if (wr) { + if (card->ch_out.slop_len > 0) + via_chan_flush_frag (&card->ch_out); + via_chan_maybe_start (&card->ch_out); + } + + rc = 0; + break; + + /* not implemented */ + default: + DPRINTK ("unhandled ioctl, cmd==%u, arg==%p\n", + cmd, (void*) arg); + break; + } + + up (&card->syscall_sem); + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static int via_dsp_open (struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct via_info *card; + struct pci_dev *pdev; + struct via_channel *chan; + struct pci_driver *drvr; + int nonblock = (file->f_flags & O_NONBLOCK); + + DPRINTK ("ENTER, minor=%d, file->f_mode=0x%x\n", minor, file->f_mode); + + if (!(file->f_mode & (FMODE_READ | FMODE_WRITE))) { + DPRINTK ("EXIT, returning -EINVAL\n"); + return -EINVAL; + } + + card = NULL; + pci_for_each_dev(pdev) { + drvr = pci_dev_driver (pdev); + if (drvr == &via_driver) { + assert (pci_get_drvdata (pdev) != NULL); + + card = pci_get_drvdata (pdev); + DPRINTK ("dev_dsp = %d, minor = %d, assn = %d\n", + card->dev_dsp, minor, + (card->dev_dsp ^ minor) & ~0xf); + + if (((card->dev_dsp ^ minor) & ~0xf) == 0) + goto match; + } + } + + DPRINTK ("no matching %s found\n", card ? "minor" : "driver"); + return -ENODEV; + +match: + if (nonblock) { + if (down_trylock (&card->open_sem)) { + DPRINTK ("EXIT, returning -EAGAIN\n"); + return -EAGAIN; + } + } else { + if (down_interruptible (&card->open_sem)) { + DPRINTK ("EXIT, returning -ERESTARTSYS\n"); + return -ERESTARTSYS; + } + } + + file->private_data = card; + DPRINTK ("file->f_mode == 0x%x\n", file->f_mode); + + /* handle input from analog source */ + if (file->f_mode & FMODE_READ) { + chan = &card->ch_in; + + via_chan_init (card, chan); + + /* why is this forced to 16-bit stereo in all drivers? */ + chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO; + + via_chan_pcm_fmt (chan, 0); + via_set_rate (&card->ac97, chan, 44100); + } + + /* handle output to analog source */ + if (file->f_mode & FMODE_WRITE) { + chan = &card->ch_out; + + via_chan_init (card, chan); + + if (file->f_mode & FMODE_READ) { + /* 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; + via_chan_pcm_fmt (chan, 0); + 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); + } else { + via_chan_pcm_fmt (chan, 1); + via_set_rate (&card->ac97, chan, 8000); + } + } + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static int via_dsp_release(struct inode *inode, struct file *file) +{ + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc; + + DPRINTK ("ENTER\n"); + + assert (file != NULL); + card = file->private_data; + assert (card != NULL); + + rc = via_syscall_down (card, nonblock); + if (rc) { + DPRINTK ("EXIT (syscall_down error), rc=%d\n", rc); + return rc; + } + + if (file->f_mode & FMODE_WRITE) { + rc = via_dsp_drain_playback (card, &card->ch_out, nonblock); + if (rc) + printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc); + + via_chan_free (card, &card->ch_out); + via_chan_buffer_free(card, &card->ch_out); + } + + if (file->f_mode & FMODE_READ) { + via_chan_free (card, &card->ch_in); + via_chan_buffer_free (card, &card->ch_in); + } + + up (&card->syscall_sem); + up (&card->open_sem); + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +/**************************************************************** + * + * Chip setup and kernel registration + * + * + */ + +static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id *id) +{ +#ifdef CONFIG_MIDI_VIA82CXXX + u8 r42; +#endif + int rc; + struct via_info *card; + static int printed_version = 0; + + DPRINTK ("ENTER\n"); + + if (printed_version++ == 0) + printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n"); + + rc = pci_enable_device (pdev); + if (rc) + goto err_out; + + rc = pci_request_regions (pdev, "via82cxxx_audio"); + if (rc) + goto err_out_disable; + + card = kmalloc (sizeof (*card), GFP_KERNEL); + if (!card) { + printk (KERN_ERR PFX "out of memory, aborting\n"); + rc = -ENOMEM; + goto err_out_res; + } + + pci_set_drvdata (pdev, card); + + memset (card, 0, sizeof (*card)); + card->pdev = pdev; + card->baseaddr = pci_resource_start (pdev, 0); + card->card_num = via_num_cards++; + spin_lock_init (&card->lock); + init_MUTEX (&card->syscall_sem); + init_MUTEX (&card->open_sem); + + /* we must init these now, in case the intr handler needs them */ + via_chan_init_defaults (card, &card->ch_out); + via_chan_init_defaults (card, &card->ch_in); + via_chan_init_defaults (card, &card->ch_fm); + + /* if BAR 2 is present, chip is Rev H or later, + * which means it has a few extra features */ + if (pci_resource_start (pdev, 2) > 0) + card->rev_h = 1; + + if (pdev->irq < 1) { + printk (KERN_ERR PFX "invalid PCI IRQ %d, aborting\n", pdev->irq); + rc = -ENODEV; + goto err_out_kfree; + } + + if (!(pci_resource_flags (pdev, 0) & IORESOURCE_IO)) { + printk (KERN_ERR PFX "unable to locate I/O resources, aborting\n"); + rc = -ENODEV; + goto err_out_kfree; + } + + /* + * init AC97 mixer and codec + */ + rc = via_ac97_init (card); + if (rc) { + printk (KERN_ERR PFX "AC97 init failed, aborting\n"); + goto err_out_kfree; + } + + /* + * init DSP device + */ + rc = via_dsp_init (card); + if (rc) { + printk (KERN_ERR PFX "DSP device init failed, aborting\n"); + goto err_out_have_mixer; + } + + /* + * per-card /proc info + */ + rc = via_card_init_proc (card); + if (rc) { + printk (KERN_ERR PFX "card-specific /proc init failed, aborting\n"); + goto err_out_have_dsp; + } + + /* + * init and turn on interrupts, as the last thing we do + */ + rc = via_interrupt_init (card); + if (rc) { + printk (KERN_ERR PFX "interrupt init failed, aborting\n"); + goto err_out_have_proc; + } + + printk (KERN_INFO PFX "board #%d at 0x%04lX, IRQ %d\n", + card->card_num + 1, card->baseaddr, pdev->irq); + +#ifdef CONFIG_MIDI_VIA82CXXX + /* 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 (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"); + } + } +#endif + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_have_proc: + via_card_cleanup_proc (card); + +err_out_have_dsp: + via_dsp_cleanup (card); + +err_out_have_mixer: + via_ac97_cleanup (card); + +err_out_kfree: +#ifndef VIA_NDEBUG + memset (card, 0xAB, sizeof (*card)); /* poison memory */ +#endif + kfree (card); + +err_out_res: + pci_release_regions (pdev); + +err_out_disable: + pci_disable_device (pdev); + +err_out: + pci_set_drvdata (pdev, NULL); + DPRINTK ("EXIT - returning %d\n", rc); + return rc; +} + + +static void __exit via_remove_one (struct pci_dev *pdev) +{ + struct via_info *card; + + DPRINTK ("ENTER\n"); + + assert (pdev != NULL); + card = pci_get_drvdata (pdev); + assert (card != NULL); + +#ifdef CONFIG_MIDI_VIA82CXXX + if (card->midi_info.io_base) + unload_uart401(&card->midi_info); +#endif + + free_irq (card->pdev->irq, card); + via_card_cleanup_proc (card); + via_dsp_cleanup (card); + via_ac97_cleanup (card); + +#ifndef VIA_NDEBUG + memset (card, 0xAB, sizeof (*card)); /* poison memory */ +#endif + kfree (card); + + pci_set_drvdata (pdev, NULL); + + pci_release_regions (pdev); + pci_disable_device (pdev); + pci_set_power_state (pdev, 3); /* ...zzzzzz */ + + DPRINTK ("EXIT\n"); + return; +} + + +/**************************************************************** + * + * Driver initialization and cleanup + * + * + */ + +static int __init init_via82cxxx_audio(void) +{ + int rc; + + DPRINTK ("ENTER\n"); + + rc = via_init_proc (); + if (rc) { + DPRINTK ("EXIT, returning %d\n", rc); + return rc; + } + + rc = pci_register_driver (&via_driver); + if (rc < 1) { + if (rc == 0) + pci_unregister_driver (&via_driver); + via_cleanup_proc (); + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static void __exit cleanup_via82cxxx_audio(void) +{ + DPRINTK ("ENTER\n"); + + pci_unregister_driver (&via_driver); + via_cleanup_proc (); + + DPRINTK ("EXIT\n"); +} + + +module_init(init_via82cxxx_audio); +module_exit(cleanup_via82cxxx_audio); + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_DESCRIPTION("DSP audio and mixer driver for Via 82Cxxx audio devices"); +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; + + + +#ifdef VIA_PROC_FS + +/**************************************************************** + * + * /proc/driver/via/info + * + * + */ + +static int via_info_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ +#define YN(val,bit) (((val) & (bit)) ? "yes" : "no") +#define ED(val,bit) (((val) & (bit)) ? "enable" : "disable") + + int len = 0; + u8 r40, r41, r42, r44; + struct via_info *card = data; + + DPRINTK ("ENTER\n"); + + assert (card != NULL); + + len += sprintf (page+len, VIA_CARD_NAME "\n\n"); + + pci_read_config_byte (card->pdev, 0x40, &r40); + pci_read_config_byte (card->pdev, 0x41, &r41); + pci_read_config_byte (card->pdev, 0x42, &r42); + pci_read_config_byte (card->pdev, 0x44, &r44); + + len += sprintf (page+len, + "Via 82Cxxx PCI registers:\n" + "\n" + "40 Codec Ready: %s\n" + " Codec Low-power: %s\n" + " Secondary Codec Ready: %s\n" + "\n" + "41 Interface Enable: %s\n" + " De-Assert Reset: %s\n" + " Force SYNC high: %s\n" + " Force SDO high: %s\n" + " Variable Sample Rate On-Demand Mode: %s\n" + " SGD Read Channel PCM Data Out: %s\n" + " FM Channel PCM Data Out: %s\n" + " SB PCM Data Out: %s\n" + "\n" + "42 Game port enabled: %s\n" + " SoundBlaster enabled: %s\n" + " FM enabled: %s\n" + " MIDI enabled: %s\n" + "\n" + "44 AC-Link Interface Access: %s\n" + " Secondary Codec Support: %s\n" + + "\n", + + YN (r40, VIA_CR40_AC97_READY), + YN (r40, VIA_CR40_AC97_LOW_POWER), + YN (r40, VIA_CR40_SECONDARY_READY), + + ED (r41, VIA_CR41_AC97_ENABLE), + YN (r41, (1 << 6)), + YN (r41, (1 << 5)), + YN (r41, (1 << 4)), + ED (r41, (1 << 3)), + ED (r41, (1 << 2)), + ED (r41, (1 << 1)), + ED (r41, (1 << 0)), + + YN (r42, VIA_CR42_GAME_ENABLE), + YN (r42, VIA_CR42_SB_ENABLE), + YN (r42, VIA_CR42_FM_ENABLE), + YN (r42, VIA_CR42_MIDI_ENABLE), + + YN (r44, VIA_CR44_AC_LINK_ACCESS), + YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT) + + ); + + DPRINTK ("EXIT, returning %d\n", len); + return len; + +#undef YN +#undef ED +} + + +/**************************************************************** + * + * /proc/driver/via/... setup and cleanup + * + * + */ + +static int __init via_init_proc (void) +{ + DPRINTK ("ENTER\n"); + + if (!proc_mkdir ("driver/via", 0)) + return -EIO; + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static void via_cleanup_proc (void) +{ + DPRINTK ("ENTER\n"); + + remove_proc_entry ("driver/via", NULL); + + DPRINTK ("EXIT\n"); +} + + +static int __init via_card_init_proc (struct via_info *card) +{ + char s[32]; + int rc; + + DPRINTK ("ENTER\n"); + + sprintf (s, "driver/via/%d", card->card_num); + if (!proc_mkdir (s, 0)) { + rc = -EIO; + goto err_out_none; + } + + sprintf (s, "driver/via/%d/info", card->card_num); + if (!create_proc_read_entry (s, 0, 0, via_info_read_proc, card)) { + rc = -EIO; + goto err_out_dir; + } + + sprintf (s, "driver/via/%d/ac97", card->card_num); + if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) { + rc = -EIO; + goto err_out_info; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_info: + sprintf (s, "driver/via/%d/info", card->card_num); + remove_proc_entry (s, NULL); + +err_out_dir: + sprintf (s, "driver/via/%d", card->card_num); + remove_proc_entry (s, NULL); + +err_out_none: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static void via_card_cleanup_proc (struct via_info *card) +{ + char s[32]; + + DPRINTK ("ENTER\n"); + + sprintf (s, "driver/via/%d/ac97", card->card_num); + remove_proc_entry (s, NULL); + + sprintf (s, "driver/via/%d/info", card->card_num); + remove_proc_entry (s, NULL); + + sprintf (s, "driver/via/%d", card->card_num); + remove_proc_entry (s, NULL); + + DPRINTK ("EXIT\n"); +} + +#endif /* VIA_PROC_FS */ diff -Nru a/sound/oss/vidc.c b/sound/oss/vidc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/vidc.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,549 @@ +/* + * linux/drivers/sound/vidc.c + * + * Copyright (C) 1997-2000 by Russell King + * + * 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. + * + * VIDC20 audio driver. + * + * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA + * engine. The DMA transfers fixed-format (16-bit little-endian linear) + * samples to the VIDC20, which then transfers this data serially to the + * DACs. The samplerate is controlled by the VIDC. + * + * We currently support a mixer device, but it is currently non-functional. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sound_config.h" +#include "vidc.h" + +#ifndef _SIOC_TYPE +#define _SIOC_TYPE(x) _IOC_TYPE(x) +#endif +#ifndef _SIOC_NR +#define _SIOC_NR(x) _IOC_NR(x) +#endif + +#define VIDC_SOUND_CLOCK (250000) + +/* + * When using SERIAL SOUND mode (external DAC), the number of physical + * channels is fixed at 2. + */ +static int vidc_busy; +static int vidc_adev; +static int vidc_audio_rate; +static char vidc_audio_format; +static char vidc_audio_channels; + +static unsigned char vidc_level_l[SOUND_MIXER_NRDEVICES] = { + 85, /* master */ + 50, /* bass */ + 50, /* treble */ + 0, /* synth */ + 75, /* pcm */ + 0, /* speaker */ + 100, /* ext line */ + 0, /* mic */ + 100, /* CD */ + 0, +}; + +static unsigned char vidc_level_r[SOUND_MIXER_NRDEVICES] = { + 85, /* master */ + 50, /* bass */ + 50, /* treble */ + 0, /* synth */ + 75, /* pcm */ + 0, /* speaker */ + 100, /* ext line */ + 0, /* mic */ + 100, /* CD */ + 0, +}; + +static unsigned int vidc_audio_volume_l; /* left PCM vol, 0 - 65536 */ +static unsigned int vidc_audio_volume_r; /* right PCM vol, 0 - 65536 */ + +static void (*old_mksound)(unsigned int hz, unsigned int ticks); +extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); +extern void vidc_update_filler(int bits, int channels); +extern int softoss_dev; + +static void +vidc_mksound(unsigned int hz, unsigned int ticks) +{ +// printk("BEEP - %d %d!\n", hz, ticks); +} + +static void +vidc_mixer_set(int mdev, unsigned int level) +{ + unsigned int lev_l = level & 0x007f; + unsigned int lev_r = (level & 0x7f00) >> 8; + unsigned int mlev_l, mlev_r; + + if (lev_l > 100) + lev_l = 100; + if (lev_r > 100) + lev_r = 100; + +#define SCALE(lev,master) ((lev) * (master) * 65536 / 10000) + + mlev_l = vidc_level_l[SOUND_MIXER_VOLUME]; + mlev_r = vidc_level_r[SOUND_MIXER_VOLUME]; + + switch (mdev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_PCM: + vidc_level_l[mdev] = lev_l; + vidc_level_r[mdev] = lev_r; + + vidc_audio_volume_l = SCALE(lev_l, mlev_l); + vidc_audio_volume_r = SCALE(lev_r, mlev_r); +/*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/ + break; + } +#undef SCALE +} + +static int vidc_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + unsigned int val; + unsigned int mdev; + + if (_SIOC_TYPE(cmd) != 'M') + return -EINVAL; + + mdev = _SIOC_NR(cmd); + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + if (get_user(val, (unsigned int *)arg)) + return -EFAULT; + + if (mdev < SOUND_MIXER_NRDEVICES) + vidc_mixer_set(mdev, val); + else + return -EINVAL; + } + + /* + * Return parameters + */ + switch (mdev) { + case SOUND_MIXER_RECSRC: + val = 0; + break; + + case SOUND_MIXER_DEVMASK: + val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH; + break; + + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH; + break; + + case SOUND_MIXER_RECMASK: + val = 0; + break; + + case SOUND_MIXER_CAPS: + val = 0; + break; + + default: + if (mdev < SOUND_MIXER_NRDEVICES) + val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8; + else + return -EINVAL; + } + + return put_user(val, (unsigned int *)arg) ? -EFAULT : 0; +} + +static unsigned int vidc_audio_set_format(int dev, unsigned int fmt) +{ + switch (fmt) { + default: + fmt = AFMT_S16_LE; + case AFMT_U8: + case AFMT_S8: + case AFMT_S16_LE: + vidc_audio_format = fmt; + vidc_update_filler(vidc_audio_format, vidc_audio_channels); + case AFMT_QUERY: + break; + } + return vidc_audio_format; +} + +static int vidc_audio_set_speed(int dev, int rate) +{ + if (rate) { + unsigned int hwctrl, hwrate; + unsigned int newsize, new2size; + + /* + * If we have selected 44.1kHz, use the DAC clock. + */ + if (0 && rate == 44100) { + hwctrl = 0x00000002; + hwrate = 3; + } else { + hwctrl = 0x00000003; + + hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1; + if (hwrate < 3) + hwrate = 3; + if (hwrate > 255) + hwrate = 255; + + rate = VIDC_SOUND_CLOCK / hwrate; + } + + vidc_writel(0xb0000000 | (hwrate - 2)); + vidc_writel(0xb1000000 | hwctrl); + + newsize = (10000 / hwrate) & ~3; + if (newsize < 208) + newsize = 208; + if (newsize > 4096) + newsize = 4096; + for (new2size = 128; new2size < newsize; new2size <<= 1); + if (new2size - newsize > newsize - (new2size >> 1)) + new2size >>= 1; + if (new2size > 4096) { + printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n", + newsize, new2size); + new2size = 4096; + } + dma_bufsize = new2size; + vidc_audio_rate = rate; + } + return vidc_audio_rate; +} + +static short vidc_audio_set_channels(int dev, short channels) +{ + switch (channels) { + default: + channels = 2; + case 1: + case 2: + vidc_audio_channels = channels; + vidc_update_filler(vidc_audio_format, vidc_audio_channels); + case 0: + break; + } + return vidc_audio_channels; +} + +/* + * Open the device + */ +static int vidc_audio_open(int dev, int mode) +{ + /* This audio device does not have recording capability */ + if (mode == OPEN_READ) + return -EPERM; + + if (vidc_busy) + return -EBUSY; + + vidc_busy = 1; + return 0; +} + +/* + * Close the device + */ +static void vidc_audio_close(int dev) +{ + vidc_busy = 0; +} + +/* + * Output a block via DMA to sound device. + * + * We just set the DMA start and count; the DMA interrupt routine + * will take care of formatting the samples (via the appropriate + * vidc_filler routine), and flag via vidc_audio_dma_interrupt when + * more data is required. + */ +static void +vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + unsigned long flags; + + local_irq_save(flags); + dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf; + dma_count = total_count; + local_irq_restore(flags); +} + +static void +vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag) +{ +} + +static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + return -EINVAL; +} + +static void vidc_audio_dma_interrupt(void) +{ + DMAbuf_outputintr(vidc_adev, 1); +} + +/* + * Prepare for outputting samples. + * + * Each buffer that will be passed will be `bsize' bytes long, + * with a total of `bcount' buffers. + */ +static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + struct audio_operations *adev = audio_devs[dev]; + + dma_interrupt = NULL; + adev->dmap_out->flags |= DMA_NODMA; + + return 0; +} + +/* + * Stop our current operation. + */ +static void vidc_audio_reset(int dev) +{ + dma_interrupt = NULL; +} + +static int vidc_audio_local_qlen(int dev) +{ + return /*dma_count !=*/ 0; +} + +static void vidc_audio_trigger(int dev, int enable_bits) +{ + struct audio_operations *adev = audio_devs[dev]; + + if (enable_bits & PCM_ENABLE_OUTPUT) { + if (!(adev->flags & DMA_ACTIVE)) { + unsigned long flags; + + local_irq_save(flags); + + /* prevent recusion */ + adev->flags |= DMA_ACTIVE; + + dma_interrupt = vidc_audio_dma_interrupt; + vidc_sound_dma_irq(0, NULL, NULL); + iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR); + + local_irq_restore(flags); + } + } +} + +static struct audio_driver vidc_audio_driver = +{ + owner: THIS_MODULE, + open: vidc_audio_open, + close: vidc_audio_close, + output_block: vidc_audio_output_block, + start_input: vidc_audio_start_input, + prepare_for_input: vidc_audio_prepare_for_input, + prepare_for_output: vidc_audio_prepare_for_output, + halt_io: vidc_audio_reset, + local_qlen: vidc_audio_local_qlen, + trigger: vidc_audio_trigger, + set_speed: vidc_audio_set_speed, + set_bits: vidc_audio_set_format, + set_channels: vidc_audio_set_channels +}; + +static struct mixer_operations vidc_mixer_operations = { + owner: THIS_MODULE, + id: "VIDC", + name: "VIDCsound", + ioctl: vidc_mixer_ioctl +}; + +void vidc_update_filler(int format, int channels) +{ +#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) + + switch (TYPE(format, channels)) { + default: + case TYPE(AFMT_U8, 1): + vidc_filler = vidc_fill_1x8_u; + break; + + case TYPE(AFMT_U8, 2): + vidc_filler = vidc_fill_2x8_u; + break; + + case TYPE(AFMT_S8, 1): + vidc_filler = vidc_fill_1x8_s; + break; + + case TYPE(AFMT_S8, 2): + vidc_filler = vidc_fill_2x8_s; + break; + + case TYPE(AFMT_S16_LE, 1): + vidc_filler = vidc_fill_1x16_s; + break; + + case TYPE(AFMT_S16_LE, 2): + vidc_filler = vidc_fill_2x16_s; + break; + } +} + +static void __init attach_vidc(struct address_info *hw_config) +{ + char name[32]; + int i, adev; + + sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype); + conf_printf(name, hw_config); + memset(dma_buf, 0, sizeof(dma_buf)); + + adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name, + &vidc_audio_driver, sizeof(vidc_audio_driver), + DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE, + NULL, hw_config->dma, hw_config->dma2); + + if (adev < 0) + goto audio_failed; + + /* + * 1024 bytes => 64 buffers + */ + audio_devs[adev]->min_fragment = 10; + audio_devs[adev]->mixer_dev = num_mixers; + + audio_devs[adev]->mixer_dev = + sound_install_mixer(MIXER_DRIVER_VERSION, + name, &vidc_mixer_operations, + sizeof(vidc_mixer_operations), NULL); + + if (audio_devs[adev]->mixer_dev < 0) + goto mixer_failed; + + for (i = 0; i < 2; i++) { + dma_buf[i] = get_free_page(GFP_KERNEL); + if (!dma_buf[i]) { + printk(KERN_ERR "%s: can't allocate required buffers\n", + name); + goto mem_failed; + } + dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]); + } + + if (sound_alloc_dma(hw_config->dma, hw_config->name)) { + printk(KERN_ERR "%s: DMA %d is in use\n", name, hw_config->dma); + goto dma_failed; + } + + if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0, + hw_config->name, &dma_start)) { + printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq); + goto irq_failed; + } + old_mksound = kd_mksound; + kd_mksound = vidc_mksound; + vidc_adev = adev; + vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8)); + +#if defined(CONFIG_SOUND_SOFTOSS) || defined(CONFIG_SOUND_SOFTOSS_MODULE) + softoss_dev = adev; +#endif + return; + +irq_failed: + sound_free_dma(hw_config->dma); +dma_failed: +mem_failed: + for (i = 0; i < 2; i++) + free_page(dma_buf[i]); + sound_unload_mixerdev(audio_devs[adev]->mixer_dev); +mixer_failed: + sound_unload_audiodev(adev); +audio_failed: + return; +} + +static int __init probe_vidc(struct address_info *hw_config) +{ + hw_config->irq = IRQ_DMAS0; + hw_config->dma = DMA_VIRTUAL_SOUND; + hw_config->dma2 = -1; + hw_config->card_subtype = 16; + hw_config->name = "VIDC20"; + return 1; +} + +static void __exit unload_vidc(struct address_info *hw_config) +{ + int i, adev = vidc_adev; + + vidc_adev = -1; + + if (old_mksound) + kd_mksound = old_mksound; + + free_irq(hw_config->irq, &dma_start); + sound_free_dma(hw_config->dma); + + if (adev >= 0) { + sound_unload_mixerdev(audio_devs[adev]->mixer_dev); + sound_unload_audiodev(adev); + for (i = 0; i < 2; i++) + free_page(dma_buf[i]); + } +} + +static struct address_info cfg; + +static int __init init_vidc(void) +{ + if (probe_vidc(&cfg) == 0) + return -ENODEV; + + attach_vidc(&cfg); + + return 0; +} + +static void __exit cleanup_vidc(void) +{ + unload_vidc(&cfg); +} + +module_init(init_vidc); +module_exit(cleanup_vidc); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("VIDC20 audio driver"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -Nru a/sound/oss/vidc.h b/sound/oss/vidc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/vidc.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,67 @@ +/* + * linux/drivers/sound/vidc.h + * + * Copyright (C) 1997 Russell King + * + * 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. + * + * VIDC sound function prototypes + */ + +/* vidc.c */ + +extern int vidc_busy; + +/* vidc_fill.S */ + +/* + * Filler routines for different channels and sample sizes + */ + +extern unsigned long vidc_fill_1x8_u(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); +extern unsigned long vidc_fill_2x8_u(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); +extern unsigned long vidc_fill_1x8_s(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); +extern unsigned long vidc_fill_2x8_s(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); +extern unsigned long vidc_fill_1x16_s(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); +extern unsigned long vidc_fill_2x16_s(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); + +/* + * DMA Interrupt handler + */ + +extern void vidc_sound_dma_irq(int irqnr, void *ref, struct pt_regs *regs); + +/* + * Filler routine pointer + */ + +extern unsigned long (*vidc_filler) (unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); + +/* + * Virtual DMA buffer exhausted + */ + +extern void (*dma_interrupt) (void); + +/* + * Virtual DMA buffer addresses + */ + +extern unsigned long dma_start, dma_count, dma_bufsize; +extern unsigned long dma_buf[2], dma_pbuf[2]; + +/* vidc_synth.c */ + +extern void vidc_synth_init(struct address_info *hw_config); +extern void vidc_synth_exit(struct address_info *hw_config); +extern int vidc_synth_get_volume(void); +extern int vidc_synth_set_volume(int vol); diff -Nru a/sound/oss/vidc_fill.S b/sound/oss/vidc_fill.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/vidc_fill.S Tue Feb 19 18:08:57 2002 @@ -0,0 +1,218 @@ +/* + * linux/drivers/sound/vidc_fill.S + * + * Copyright (C) 1997 Russell King + * + * 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. + * + * Filler routines for DMA buffers + */ +#define __ASSEMBLY__ +#include +#include +#include +#include + + .text + +ENTRY(vidc_fill_1x8_u) + mov ip, #0xff00 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldrb r4, [r0], #1 + eor r4, r4, #0x80 + and r4, ip, r4, lsl #8 + orr r4, r4, r4, lsl #16 + str r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_2x8_u) + mov ip, #0xff00 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldr r4, [r0], #2 + and r5, r4, ip + and r4, ip, r4, lsl #8 + orr r4, r4, r5, lsl #16 + orr r4, r4, r4, lsr #8 + str r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_1x8_s) + mov ip, #0xff00 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldrb r4, [r0], #1 + and r4, ip, r4, lsl #8 + orr r4, r4, r4, lsl #16 + str r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_2x8_s) + mov ip, #0xff00 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldr r4, [r0], #2 + and r5, r4, ip + and r4, ip, r4, lsl #8 + orr r4, r4, r5, lsl #16 + orr r4, r4, r4, lsr #8 + str r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_1x16_s) + mov ip, #0xff00 + orr ip, ip, ip, lsr #8 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldr r5, [r0], #2 + and r4, r5, ip + orr r4, r4, r4, lsl #16 + str r4, [r2], #4 + cmp r0, r1 + addlt r0, r0, #2 + andlt r4, r5, ip, lsl #16 + orrlt r4, r4, r4, lsr #16 + strlt r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_2x16_s) + mov ip, #0xff00 + orr ip, ip, ip, lsr #8 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldr r4, [r0], #4 + str r4, [r2], #4 + cmp r0, r1 + ldrlt r4, [r0], #4 + strlt r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_noaudio) + mov r0, #0 + mov r1, #0 +2: mov r4, #0 + mov r5, #0 +1: cmp r2, r3 + stmltia r2!, {r0, r1, r4, r5} + blt 1b + mov pc, lr + +ENTRY(vidc_clear) + mov r0, #0 + mov r1, #0 + tst r2, #4 + str r0, [r2], #4 + tst r2, #8 + stmia r2!, {r0, r1} + b 2b + +/* + * Call filler routines with: + * r0 = phys address + * r1 = phys end + * r2 = buffer + * Returns: + * r0 = new buffer address + * r2 = new buffer finish + * r4 = corrupted + * r5 = corrupted + * ip = corrupted + */ + +ENTRY(vidc_sound_dma_irq) + stmfd sp!, {r4 - r8, lr} + ldr r8, =SYMBOL_NAME(dma_start) + ldmia r8, {r0, r1, r2, r3, r4, r5} + teq r1, #0 + adreq r4, SYMBOL_NAME(vidc_fill_noaudio) + moveq r7, #1 << 31 + movne r7, #0 + mov ip, #IOMD_BASE & 0xff000000 + orr ip, ip, #IOMD_BASE & 0x00ff0000 + ldrb r6, [ip, #IOMD_SD0ST] + tst r6, #DMA_ST_OFL @ Check for overrun + eorne r6, r6, #DMA_ST_AB + tst r6, #DMA_ST_AB + moveq r2, r3 @ DMAing A, update B + add r3, r2, r5 @ End of DMA buffer + add r1, r1, r0 @ End of virtual DMA buffer + mov lr, pc + mov pc, r4 @ Call fill routine (uses r4, ip) + sub r1, r1, r0 @ Remaining length + stmia r8, {r0, r1} + mov r0, #0 + tst r2, #4 @ Round buffer up to 4 words + strne r0, [r2], #4 + tst r2, #8 + strne r0, [r2], #4 + strne r0, [r2], #4 + sub r2, r2, #16 + mov r2, r2, lsl #20 + movs r2, r2, lsr #20 + orreq r2, r2, #1 << 30 @ Set L bit + orr r2, r2, r7 + ldmdb r8, {r3, r4, r5} + tst r6, #DMA_ST_AB + mov ip, #IOMD_BASE & 0xff000000 + orr ip, ip, #IOMD_BASE & 0x00ff0000 + streq r4, [ip, #IOMD_SD0CURB] + strne r5, [ip, #IOMD_SD0CURA] + streq r2, [ip, #IOMD_SD0ENDB] + strne r2, [ip, #IOMD_SD0ENDA] + ldr lr, [ip, #IOMD_SD0ST] + tst lr, #DMA_ST_OFL + bne 1f + tst r6, #DMA_ST_AB + strne r4, [ip, #IOMD_SD0CURB] + streq r5, [ip, #IOMD_SD0CURA] + strne r2, [ip, #IOMD_SD0ENDB] + streq r2, [ip, #IOMD_SD0ENDA] +1: teq r7, #0 + mov r0, #0x10 + strneb r0, [ip, #IOMD_SD0CR] + ldmfd sp!, {r4 - r8, lr} + teq r1, #0 @ If we have no more + movne pc, lr + teq r3, #0 + movne pc, r3 @ Call interrupt routine + mov pc, lr + + .data + .globl SYMBOL_NAME(dma_interrupt) +SYMBOL_NAME(dma_interrupt): + .long 0 @ r3 + .globl SYMBOL_NAME(dma_pbuf) +SYMBOL_NAME(dma_pbuf): + .long 0 @ r4 + .long 0 @ r5 + .globl SYMBOL_NAME(dma_start) +SYMBOL_NAME(dma_start): + .long 0 @ r0 + .globl SYMBOL_NAME(dma_count) +SYMBOL_NAME(dma_count): + .long 0 @ r1 + .globl SYMBOL_NAME(dma_buf) +SYMBOL_NAME(dma_buf): + .long 0 @ r2 + .long 0 @ r3 + .globl SYMBOL_NAME(vidc_filler) +SYMBOL_NAME(vidc_filler): + .long SYMBOL_NAME(vidc_fill_noaudio) @ r4 + .globl SYMBOL_NAME(dma_bufsize) +SYMBOL_NAME(dma_bufsize): + .long 0x1000 @ r5 diff -Nru a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/vwsnd.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,3477 @@ +/* + * Sound driver for Silicon Graphics 320 and 540 Visual Workstations' + * onboard audio. See notes in ../../Documentation/sound/vwsnd . + * + * Copyright 1999 Silicon Graphics, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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. + */ + +#undef VWSND_DEBUG /* define for debugging */ + +/* + * XXX to do - + * + * External sync. + * Rename swbuf, hwbuf, u&i, hwptr&swptr to something rational. + * Bug - if select() called before read(), pcm_setup() not called. + * Bug - output doesn't stop soon enough if process killed. + */ + +/* + * Things to test - + * + * Will readv/writev work? Write a test. + * + * insmod/rmmod 100 million times. + * + * Run I/O until int ptrs wrap around (roughly 6.2 hours @ DAT + * rate). + * + * Concurrent threads banging on mixer simultaneously, both UP + * and SMP kernels. Especially, watch for thread A changing + * OUTSRC while thread B changes gain -- both write to the same + * ad1843 register. + * + * What happens if a client opens /dev/audio then forks? + * Do two procs have /dev/audio open? Test. + * + * Pump audio through the CD, MIC and line inputs and verify that + * they mix/mute into the output. + * + * Apps: + * amp + * mpg123 + * x11amp + * mxv + * kmedia + * esound + * need more input apps + * + * Run tests while bombarding with signals. setitimer(2) will do it... */ + +/* + * This driver is organized in nine sections. + * The nine sections are: + * + * debug stuff + * low level lithium access + * high level lithium access + * AD1843 access + * PCM I/O + * audio driver + * mixer driver + * probe/attach/unload + * initialization and loadable kernel module interface + * + * That is roughly the order of increasing abstraction, so forward + * dependencies are minimal. + */ + +/* + * Locking Notes + * + * INC_USE_COUNT and DEC_USE_COUNT keep track of the number of + * open descriptors to this driver. They store it in vwsnd_use_count. + * The global device list, vwsnd_dev_list, is immutable when the IN_USE + * is true. + * + * devc->open_lock is a semaphore that is used to enforce the + * single reader/single writer rule for /dev/audio. The rule is + * that each device may have at most one reader and one writer. + * Open will block until the previous client has closed the + * device, unless O_NONBLOCK is specified. + * + * The semaphore devc->io_sema serializes PCM I/O syscalls. This + * is unnecessary in Linux 2.2, because the kernel lock + * serializes read, write, and ioctl globally, but it's there, + * ready for the brave, new post-kernel-lock world. + * + * Locking between interrupt and baselevel is handled by the + * "lock" spinlock in vwsnd_port (one lock each for read and + * write). Each half holds the lock just long enough to see what + * area it owns and update its pointers. See pcm_output() and + * pcm_input() for most of the gory stuff. + * + * devc->mix_sema serializes all mixer ioctls. This is also + * redundant because of the kernel lock. + * + * The lowest level lock is lith->lithium_lock. It is a + * spinlock which is held during the two-register tango of + * reading/writing an AD1843 register. See + * li_{read,write}_ad1843_reg(). + */ + +/* + * Sample Format Notes + * + * Lithium's DMA engine has two formats: 16-bit 2's complement + * and 8-bit unsigned . 16-bit transfers the data unmodified, 2 + * bytes per sample. 8-bit unsigned transfers 1 byte per sample + * and XORs each byte with 0x80. Lithium can input or output + * either mono or stereo in either format. + * + * The AD1843 has four formats: 16-bit 2's complement, 8-bit + * unsigned, 8-bit mu-Law and 8-bit A-Law. + * + * This driver supports five formats: AFMT_S8, AFMT_U8, + * AFMT_MU_LAW, AFMT_A_LAW, and AFMT_S16_LE. + * + * For AFMT_U8 output, we keep the AD1843 in 16-bit mode, and + * rely on Lithium's XOR to translate between U8 and S8. + * + * For AFMT_S8, AFMT_MU_LAW and AFMT_A_LAW output, we have to XOR + * the 0x80 bit in software to compensate for Lithium's XOR. + * This happens in pcm_copy_{in,out}(). + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added some __init/__exit + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sound_config.h" + +/*****************************************************************************/ +/* debug stuff */ + +#ifdef VWSND_DEBUG + +#include /* for in_interrupt() */ + +static int shut_up = 1; + +/* + * dbgassert - called when an assertion fails. + */ + +static void dbgassert(const char *fcn, int line, const char *expr) +{ + if (in_interrupt()) + panic("ASSERTION FAILED IN INTERRUPT, %s:%s:%d %s\n", + __FILE__, fcn, line, expr); + else { + int x; + printk(KERN_ERR "ASSERTION FAILED, %s:%s:%d %s\n", + __FILE__, fcn, line, expr); + x = * (volatile int *) 0; /* force proc to exit */ + } +} + +/* + * Bunch of useful debug macros: + * + * ASSERT - print unless e nonzero (panic if in interrupt) + * DBGDO - include arbitrary code if debugging + * DBGX - debug print raw (w/o function name) + * DBGP - debug print w/ function name + * DBGE - debug print function entry + * DBGC - debug print function call + * DBGR - debug print function return + * DBGXV - debug print raw when verbose + * DBGPV - debug print when verbose + * DBGEV - debug print function entry when verbose + * DBGRV - debug print function return when verbose + */ + +#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__FUNCTION__, __LINE__, #e)) +#define DBGDO(x) x +#define DBGX(fmt, args...) (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args)) +#define DBGP(fmt, args...) (DBGX(__FUNCTION__ ": " fmt, ##args)) +#define DBGE(fmt, args...) (DBGX(__FUNCTION__ fmt, ##args)) +#define DBGC(rtn) (DBGP("calling %s\n", rtn)) +#define DBGR() (DBGP("returning\n")) +#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args)) +#define DBGPV(fmt, args...) (shut_up ? 0 : DBGP(fmt, ##args)) +#define DBGEV(fmt, args...) (shut_up ? 0 : DBGE(fmt, ##args)) +#define DBGCV(rtn) (shut_up ? 0 : DBGC(rtn)) +#define DBGRV() (shut_up ? 0 : DBGR()) + +#else /* !VWSND_DEBUG */ + +#define ASSERT(e) ((void) 0) +#define DBGDO(x) /* don't */ +#define DBGX(fmt, args...) ((void) 0) +#define DBGP(fmt, args...) ((void) 0) +#define DBGE(fmt, args...) ((void) 0) +#define DBGC(rtn) ((void) 0) +#define DBGR() ((void) 0) +#define DBGPV(fmt, args...) ((void) 0) +#define DBGXV(fmt, args...) ((void) 0) +#define DBGEV(fmt, args...) ((void) 0) +#define DBGCV(rtn) ((void) 0) +#define DBGRV() ((void) 0) + +#endif /* !VWSND_DEBUG */ + +/*****************************************************************************/ +/* low level lithium access */ + +/* + * We need to talk to Lithium registers on three pages. Here are + * the pages' offsets from the base address (0xFF001000). + */ + +enum { + LI_PAGE0_OFFSET = 0x01000 - 0x1000, /* FF001000 */ + LI_PAGE1_OFFSET = 0x0F000 - 0x1000, /* FF00F000 */ + LI_PAGE2_OFFSET = 0x10000 - 0x1000, /* FF010000 */ +}; + +/* low-level lithium data */ + +typedef struct lithium { + caddr_t page0; /* virtual addresses */ + caddr_t page1; + caddr_t page2; + spinlock_t lock; /* protects codec and UST/MSC access */ +} lithium_t; + +/* + * li_create initializes the lithium_t structure and sets up vm mappings + * to access the registers. + * Returns 0 on success, -errno on failure. + */ + +static int li_create(lithium_t *lith, unsigned long baseaddr) +{ + static void li_destroy(lithium_t *); + + lith->lock = SPIN_LOCK_UNLOCKED; + lith->page0 = ioremap_nocache(baseaddr + LI_PAGE0_OFFSET, PAGE_SIZE); + lith->page1 = ioremap_nocache(baseaddr + LI_PAGE1_OFFSET, PAGE_SIZE); + lith->page2 = ioremap_nocache(baseaddr + LI_PAGE2_OFFSET, PAGE_SIZE); + if (!lith->page0 || !lith->page1 || !lith->page2) { + li_destroy(lith); + return -ENOMEM; + } + return 0; +} + +/* + * li_destroy destroys the lithium_t structure and vm mappings. + */ + +static void li_destroy(lithium_t *lith) +{ + if (lith->page0) { + iounmap(lith->page0); + lith->page0 = NULL; + } + if (lith->page1) { + iounmap(lith->page1); + lith->page1 = NULL; + } + if (lith->page2) { + iounmap(lith->page2); + lith->page2 = NULL; + } +} + +/* + * basic register accessors - read/write long/byte + */ + +static __inline__ unsigned long li_readl(lithium_t *lith, int off) +{ + return * (volatile unsigned long *) (lith->page0 + off); +} + +static __inline__ unsigned char li_readb(lithium_t *lith, int off) +{ + return * (volatile unsigned char *) (lith->page0 + off); +} + +static __inline__ void li_writel(lithium_t *lith, int off, unsigned long val) +{ + * (volatile unsigned long *) (lith->page0 + off) = val; +} + +static __inline__ void li_writeb(lithium_t *lith, int off, unsigned char val) +{ + * (volatile unsigned char *) (lith->page0 + off) = val; +} + +/*****************************************************************************/ +/* High Level Lithium Access */ + +/* + * Lithium DMA Notes + * + * Lithium has two dedicated DMA channels for audio. They are known + * as comm1 and comm2 (communication areas 1 and 2). Comm1 is for + * input, and comm2 is for output. Each is controlled by three + * registers: BASE (base address), CFG (config) and CCTL + * (config/control). + * + * Each DMA channel points to a physically contiguous ring buffer in + * main memory of up to 8 Kbytes. (This driver always uses 8 Kb.) + * There are three pointers into the ring buffer: read, write, and + * trigger. The pointers are 8 bits each. Each pointer points to + * 32-byte "chunks" of data. The DMA engine moves 32 bytes at a time, + * so there is no finer-granularity control. + * + * In comm1, the hardware updates the write ptr, and software updates + * the read ptr. In comm2, it's the opposite: hardware updates the + * read ptr, and software updates the write ptr. I designate the + * hardware-updated ptr as the hwptr, and the software-updated ptr as + * the swptr. + * + * The trigger ptr and trigger mask are used to trigger interrupts. + * From the Lithium spec, section 5.6.8, revision of 12/15/1998: + * + * Trigger Mask Value + * + * A three bit wide field that represents a power of two mask + * that is used whenever the trigger pointer is compared to its + * respective read or write pointer. A value of zero here + * implies a mask of 0xFF and a value of seven implies a mask + * 0x01. This value can be used to sub-divide the ring buffer + * into pie sections so that interrupts monitor the progress of + * hardware from section to section. + * + * My interpretation of that is, whenever the hw ptr is updated, it is + * compared with the trigger ptr, and the result is masked by the + * trigger mask. (Actually, by the complement of the trigger mask.) + * If the result is zero, an interrupt is triggered. I.e., interrupt + * if ((hwptr & ~mask) == (trptr & ~mask)). The mask is formed from + * the trigger register value as mask = (1 << (8 - tmreg)) - 1. + * + * In yet different words, setting tmreg to 0 causes an interrupt after + * every 256 DMA chunks (8192 bytes) or once per traversal of the + * ring buffer. Setting it to 7 caues an interrupt every 2 DMA chunks + * (64 bytes) or 128 times per traversal of the ring buffer. + */ + +/* Lithium register offsets and bit definitions */ + +#define LI_HOST_CONTROLLER 0x000 +# define LI_HC_RESET 0x00008000 +# define LI_HC_LINK_ENABLE 0x00004000 +# define LI_HC_LINK_FAILURE 0x00000004 +# define LI_HC_LINK_CODEC 0x00000002 +# define LI_HC_LINK_READY 0x00000001 + +#define LI_INTR_STATUS 0x010 +#define LI_INTR_MASK 0x014 +# define LI_INTR_LINK_ERR 0x00008000 +# define LI_INTR_COMM2_TRIG 0x00000008 +# define LI_INTR_COMM2_UNDERFLOW 0x00000004 +# define LI_INTR_COMM1_TRIG 0x00000002 +# define LI_INTR_COMM1_OVERFLOW 0x00000001 + +#define LI_CODEC_COMMAND 0x018 +# define LI_CC_BUSY 0x00008000 +# define LI_CC_DIR 0x00000080 +# define LI_CC_DIR_RD LI_CC_DIR +# define LI_CC_DIR_WR (!LI_CC_DIR) +# define LI_CC_ADDR_MASK 0x0000007F + +#define LI_CODEC_DATA 0x01C + +#define LI_COMM1_BASE 0x100 +#define LI_COMM1_CTL 0x104 +# define LI_CCTL_RESET 0x80000000 +# define LI_CCTL_SIZE 0x70000000 +# define LI_CCTL_DMA_ENABLE 0x08000000 +# define LI_CCTL_TMASK 0x07000000 /* trigger mask */ +# define LI_CCTL_TPTR 0x00FF0000 /* trigger pointer */ +# define LI_CCTL_RPTR 0x0000FF00 +# define LI_CCTL_WPTR 0x000000FF +#define LI_COMM1_CFG 0x108 +# define LI_CCFG_LOCK 0x00008000 +# define LI_CCFG_SLOT 0x00000070 +# define LI_CCFG_DIRECTION 0x00000008 +# define LI_CCFG_DIR_IN (!LI_CCFG_DIRECTION) +# define LI_CCFG_DIR_OUT LI_CCFG_DIRECTION +# define LI_CCFG_MODE 0x00000004 +# define LI_CCFG_MODE_MONO (!LI_CCFG_MODE) +# define LI_CCFG_MODE_STEREO LI_CCFG_MODE +# define LI_CCFG_FORMAT 0x00000003 +# define LI_CCFG_FMT_8BIT 0x00000000 +# define LI_CCFG_FMT_16BIT 0x00000001 +#define LI_COMM2_BASE 0x10C +#define LI_COMM2_CTL 0x110 + /* bit definitions are the same as LI_COMM1_CTL */ +#define LI_COMM2_CFG 0x114 + /* bit definitions are the same as LI_COMM1_CFG */ + +#define LI_UST_LOW 0x200 /* 64-bit Unadjusted System Time is */ +#define LI_UST_HIGH 0x204 /* microseconds since boot */ + +#define LI_AUDIO1_UST 0x300 /* UST-MSC pairs */ +#define LI_AUDIO1_MSC 0x304 /* MSC (Media Stream Counter) */ +#define LI_AUDIO2_UST 0x308 /* counts samples actually */ +#define LI_AUDIO2_MSC 0x30C /* processed as of time UST */ + +/* + * Lithium's DMA engine operates on chunks of 32 bytes. We call that + * a DMACHUNK. + */ + +#define DMACHUNK_SHIFT 5 +#define DMACHUNK_SIZE (1 << DMACHUNK_SHIFT) +#define BYTES_TO_CHUNKS(bytes) ((bytes) >> DMACHUNK_SHIFT) +#define CHUNKS_TO_BYTES(chunks) ((chunks) << DMACHUNK_SHIFT) + +/* + * Two convenient macros to shift bitfields into/out of position. + * + * Observe that (mask & -mask) is (1 << low_set_bit_of(mask)). + * As long as mask is constant, we trust the compiler will change the + * multipy and divide into shifts. + */ + +#define SHIFT_FIELD(val, mask) (((val) * ((mask) & -(mask))) & (mask)) +#define UNSHIFT_FIELD(val, mask) (((val) & (mask)) / ((mask) & -(mask))) + +/* + * dma_chan_desc is invariant information about a Lithium + * DMA channel. There are two instances, li_comm1 and li_comm2. + * + * Note that the CCTL register fields are write ptr and read ptr, but what + * we care about are which pointer is updated by software and which by + * hardware. + */ + +typedef struct dma_chan_desc { + int basereg; + int cfgreg; + int ctlreg; + int hwptrreg; + int swptrreg; + int ustreg; + int mscreg; + unsigned long swptrmask; + int ad1843_slot; + int direction; /* LI_CCTL_DIR_IN/OUT */ +} dma_chan_desc_t; + +static const dma_chan_desc_t li_comm1 = { + LI_COMM1_BASE, /* base register offset */ + LI_COMM1_CFG, /* config register offset */ + LI_COMM1_CTL, /* control register offset */ + LI_COMM1_CTL + 0, /* hw ptr reg offset (write ptr) */ + LI_COMM1_CTL + 1, /* sw ptr reg offset (read ptr) */ + LI_AUDIO1_UST, /* ust reg offset */ + LI_AUDIO1_MSC, /* msc reg offset */ + LI_CCTL_RPTR, /* sw ptr bitmask in ctlval */ + 2, /* ad1843 serial slot */ + LI_CCFG_DIR_IN /* direction */ +}; + +static const dma_chan_desc_t li_comm2 = { + LI_COMM2_BASE, /* base register offset */ + LI_COMM2_CFG, /* config register offset */ + LI_COMM2_CTL, /* control register offset */ + LI_COMM2_CTL + 1, /* hw ptr reg offset (read ptr) */ + LI_COMM2_CTL + 0, /* sw ptr reg offset (writr ptr) */ + LI_AUDIO2_UST, /* ust reg offset */ + LI_AUDIO2_MSC, /* msc reg offset */ + LI_CCTL_WPTR, /* sw ptr bitmask in ctlval */ + 2, /* ad1843 serial slot */ + LI_CCFG_DIR_OUT /* direction */ +}; + +/* + * dma_chan is variable information about a Lithium DMA channel. + * + * The desc field points to invariant information. + * The lith field points to a lithium_t which is passed + * to li_read* and li_write* to access the registers. + * The *val fields shadow the lithium registers' contents. + */ + +typedef struct dma_chan { + const dma_chan_desc_t *desc; + lithium_t *lith; + unsigned long baseval; + unsigned long cfgval; + unsigned long ctlval; +} dma_chan_t; + +/* + * ustmsc is a UST/MSC pair (Unadjusted System Time/Media Stream Counter). + * UST is time in microseconds since the system booted, and MSC is a + * counter that increments with every audio sample. + */ + +typedef struct ustmsc { + unsigned long long ust; + unsigned long msc; +} ustmsc_t; + +/* + * li_ad1843_wait waits until lithium says the AD1843 register + * exchange is not busy. Returns 0 on success, -EBUSY on timeout. + * + * Locking: must be called with lithium_lock held. + */ + +static int li_ad1843_wait(lithium_t *lith) +{ + unsigned long later = jiffies + 2; + while (li_readl(lith, LI_CODEC_COMMAND) & LI_CC_BUSY) + if (jiffies >= later) + return -EBUSY; + return 0; +} + +/* + * li_read_ad1843_reg returns the current contents of a 16 bit AD1843 register. + * + * Returns unsigned register value on success, -errno on failure. + */ + +static int li_read_ad1843_reg(lithium_t *lith, int reg) +{ + int val; + + ASSERT(!in_interrupt()); + spin_lock(&lith->lock); + { + val = li_ad1843_wait(lith); + if (val == 0) { + li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_RD | reg); + val = li_ad1843_wait(lith); + } + if (val == 0) + val = li_readl(lith, LI_CODEC_DATA); + } + spin_unlock(&lith->lock); + + DBGXV("li_read_ad1843_reg(lith=0x%p, reg=%d) returns 0x%04x\n", + lith, reg, val); + + return val; +} + +/* + * li_write_ad1843_reg writes the specified value to a 16 bit AD1843 register. + */ + +static void li_write_ad1843_reg(lithium_t *lith, int reg, int newval) +{ + spin_lock(&lith->lock); + { + if (li_ad1843_wait(lith) == 0) { + li_writel(lith, LI_CODEC_DATA, newval); + li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_WR | reg); + } + } + spin_unlock(&lith->lock); +} + +/* + * li_setup_dma calculates all the register settings for DMA in a particular + * mode. It takes too many arguments. + */ + +static void li_setup_dma(dma_chan_t *chan, + const dma_chan_desc_t *desc, + lithium_t *lith, + unsigned long buffer_paddr, + int bufshift, + int fragshift, + int channels, + int sampsize) +{ + unsigned long mode, format; + unsigned long size, tmask; + + DBGEV("(chan=0x%p, desc=0x%p, lith=0x%p, buffer_paddr=0x%lx, " + "bufshift=%d, fragshift=%d, channels=%d, sampsize=%d)\n", + chan, desc, lith, buffer_paddr, + bufshift, fragshift, channels, sampsize); + + /* Reset the channel first. */ + + li_writel(lith, desc->ctlreg, LI_CCTL_RESET); + + ASSERT(channels == 1 || channels == 2); + if (channels == 2) + mode = LI_CCFG_MODE_STEREO; + else + mode = LI_CCFG_MODE_MONO; + ASSERT(sampsize == 1 || sampsize == 2); + if (sampsize == 2) + format = LI_CCFG_FMT_16BIT; + else + format = LI_CCFG_FMT_8BIT; + chan->desc = desc; + chan->lith = lith; + + /* + * Lithium DMA address register takes a 40-bit physical + * address, right-shifted by 8 so it fits in 32 bits. Bit 37 + * must be set -- it enables cache coherence. + */ + + ASSERT(!(buffer_paddr & 0xFF)); + chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8); + + chan->cfgval = (!LI_CCFG_LOCK | + SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) | + desc->direction | + mode | + format); + + size = bufshift - 6; + tmask = 13 - fragshift; /* See Lithium DMA Notes above. */ + ASSERT(size >= 2 && size <= 7); + ASSERT(tmask >= 1 && tmask <= 7); + chan->ctlval = (!LI_CCTL_RESET | + SHIFT_FIELD(size, LI_CCTL_SIZE) | + !LI_CCTL_DMA_ENABLE | + SHIFT_FIELD(tmask, LI_CCTL_TMASK) | + SHIFT_FIELD(0, LI_CCTL_TPTR)); + + DBGPV("basereg 0x%x = 0x%lx\n", desc->basereg, chan->baseval); + DBGPV("cfgreg 0x%x = 0x%lx\n", desc->cfgreg, chan->cfgval); + DBGPV("ctlreg 0x%x = 0x%lx\n", desc->ctlreg, chan->ctlval); + + li_writel(lith, desc->basereg, chan->baseval); + li_writel(lith, desc->cfgreg, chan->cfgval); + li_writel(lith, desc->ctlreg, chan->ctlval); + + DBGRV(); +} + +static void li_shutdown_dma(dma_chan_t *chan) +{ + lithium_t *lith = chan->lith; + caddr_t lith1 = lith->page1; + + DBGEV("(chan=0x%p)\n", chan); + + chan->ctlval &= ~LI_CCTL_DMA_ENABLE; + DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval); + li_writel(lith, chan->desc->ctlreg, chan->ctlval); + + /* + * Offset 0x500 on Lithium page 1 is an undocumented, + * unsupported register that holds the zero sample value. + * Lithium is supposed to output zero samples when DMA is + * inactive, and repeat the last sample when DMA underflows. + * But it has a bug, where, after underflow occurs, the zero + * sample is not reset. + * + * I expect this to break in a future rev of Lithium. + */ + + if (lith1 && chan->desc->direction == LI_CCFG_DIR_OUT) + * (volatile unsigned long *) (lith1 + 0x500) = 0; +} + +/* + * li_activate_dma always starts dma at the beginning of the buffer. + * + * N.B., these may be called from interrupt. + */ + +static __inline__ void li_activate_dma(dma_chan_t *chan) +{ + chan->ctlval |= LI_CCTL_DMA_ENABLE; + DBGPV("ctlval = 0x%lx\n", chan->ctlval); + li_writel(chan->lith, chan->desc->ctlreg, chan->ctlval); +} + +static void li_deactivate_dma(dma_chan_t *chan) +{ + lithium_t *lith = chan->lith; + caddr_t lith2 = lith->page2; + + chan->ctlval &= ~(LI_CCTL_DMA_ENABLE | LI_CCTL_RPTR | LI_CCTL_WPTR); + DBGPV("ctlval = 0x%lx\n", chan->ctlval); + DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval); + li_writel(lith, chan->desc->ctlreg, chan->ctlval); + + /* + * Offsets 0x98 and 0x9C on Lithium page 2 are undocumented, + * unsupported registers that are internal copies of the DMA + * read and write pointers. Because of a Lithium bug, these + * registers aren't zeroed correctly when DMA is shut off. So + * we whack them directly. + * + * I expect this to break in a future rev of Lithium. + */ + + if (lith2 && chan->desc->direction == LI_CCFG_DIR_OUT) { + * (volatile unsigned long *) (lith2 + 0x98) = 0; + * (volatile unsigned long *) (lith2 + 0x9C) = 0; + } +} + +/* + * read/write the ring buffer pointers. These routines' arguments and results + * are byte offsets from the beginning of the ring buffer. + */ + +static __inline__ int li_read_swptr(dma_chan_t *chan) +{ + const unsigned long mask = chan->desc->swptrmask; + + return CHUNKS_TO_BYTES(UNSHIFT_FIELD(chan->ctlval, mask)); +} + +static __inline__ int li_read_hwptr(dma_chan_t *chan) +{ + return CHUNKS_TO_BYTES(li_readb(chan->lith, chan->desc->hwptrreg)); +} + +static __inline__ void li_write_swptr(dma_chan_t *chan, int val) +{ + const unsigned long mask = chan->desc->swptrmask; + + ASSERT(!(val & ~CHUNKS_TO_BYTES(0xFF))); + val = BYTES_TO_CHUNKS(val); + chan->ctlval = (chan->ctlval & ~mask) | SHIFT_FIELD(val, mask); + li_writeb(chan->lith, chan->desc->swptrreg, val); +} + +/* li_read_USTMSC() returns a UST/MSC pair for the given channel. */ + +static void li_read_USTMSC(dma_chan_t *chan, ustmsc_t *ustmsc) +{ + lithium_t *lith = chan->lith; + const dma_chan_desc_t *desc = chan->desc; + unsigned long now_low, now_high0, now_high1, chan_ust; + + spin_lock(&lith->lock); + { + /* + * retry until we do all five reads without the + * high word changing. (High word increments + * every 2^32 microseconds, i.e., not often) + */ + do { + now_high0 = li_readl(lith, LI_UST_HIGH); + now_low = li_readl(lith, LI_UST_LOW); + + /* + * Lithium guarantees these two reads will be + * atomic -- ust will not increment after msc + * is read. + */ + + ustmsc->msc = li_readl(lith, desc->mscreg); + chan_ust = li_readl(lith, desc->ustreg); + + now_high1 = li_readl(lith, LI_UST_HIGH); + } while (now_high0 != now_high1); + } + spin_unlock(&lith->lock); + ustmsc->ust = ((unsigned long long) now_high0 << 32 | chan_ust); +} + +static void li_enable_interrupts(lithium_t *lith, unsigned int mask) +{ + DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask); + + /* clear any already-pending interrupts. */ + + li_writel(lith, LI_INTR_STATUS, mask); + + /* enable the interrupts. */ + + mask |= li_readl(lith, LI_INTR_MASK); + li_writel(lith, LI_INTR_MASK, mask); +} + +static void li_disable_interrupts(lithium_t *lith, unsigned int mask) +{ + unsigned int keepmask; + + DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask); + + /* disable the interrupts */ + + keepmask = li_readl(lith, LI_INTR_MASK) & ~mask; + li_writel(lith, LI_INTR_MASK, keepmask); + + /* clear any pending interrupts. */ + + li_writel(lith, LI_INTR_STATUS, mask); +} + +/* Get the interrupt status and clear all pending interrupts. */ + +static unsigned int li_get_clear_intr_status(lithium_t *lith) +{ + unsigned int status; + + status = li_readl(lith, LI_INTR_STATUS); + li_writel(lith, LI_INTR_STATUS, ~0); + return status & li_readl(lith, LI_INTR_MASK); +} + +static int li_init(lithium_t *lith) +{ + /* 1. System power supplies stabilize. */ + + /* 2. Assert the ~RESET signal. */ + + li_writel(lith, LI_HOST_CONTROLLER, LI_HC_RESET); + udelay(1); + + /* 3. Deassert the ~RESET signal and enter a wait period to allow + the AD1843 internal clocks and the external crystal oscillator + to stabilize. */ + + li_writel(lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE); + udelay(1); + + return 0; +} + +/*****************************************************************************/ +/* AD1843 access */ + +/* + * AD1843 bitfield definitions. All are named as in the AD1843 data + * sheet, with ad1843_ prepended and individual bit numbers removed. + * + * E.g., bits LSS0 through LSS2 become ad1843_LSS. + * + * Only the bitfields we need are defined. + */ + +typedef struct ad1843_bitfield { + char reg; + char lo_bit; + char nbits; +} ad1843_bitfield_t; + +static const ad1843_bitfield_t + ad1843_PDNO = { 0, 14, 1 }, /* Converter Power-Down Flag */ + ad1843_INIT = { 0, 15, 1 }, /* Clock Initialization Flag */ + ad1843_RIG = { 2, 0, 4 }, /* Right ADC Input Gain */ + ad1843_RMGE = { 2, 4, 1 }, /* Right ADC Mic Gain Enable */ + ad1843_RSS = { 2, 5, 3 }, /* Right ADC Source Select */ + ad1843_LIG = { 2, 8, 4 }, /* Left ADC Input Gain */ + ad1843_LMGE = { 2, 12, 1 }, /* Left ADC Mic Gain Enable */ + ad1843_LSS = { 2, 13, 3 }, /* Left ADC Source Select */ + ad1843_RX1M = { 4, 0, 5 }, /* Right Aux 1 Mix Gain/Atten */ + ad1843_RX1MM = { 4, 7, 1 }, /* Right Aux 1 Mix Mute */ + ad1843_LX1M = { 4, 8, 5 }, /* Left Aux 1 Mix Gain/Atten */ + ad1843_LX1MM = { 4, 15, 1 }, /* Left Aux 1 Mix Mute */ + ad1843_RX2M = { 5, 0, 5 }, /* Right Aux 2 Mix Gain/Atten */ + ad1843_RX2MM = { 5, 7, 1 }, /* Right Aux 2 Mix Mute */ + ad1843_LX2M = { 5, 8, 5 }, /* Left Aux 2 Mix Gain/Atten */ + ad1843_LX2MM = { 5, 15, 1 }, /* Left Aux 2 Mix Mute */ + ad1843_RMCM = { 7, 0, 5 }, /* Right Mic Mix Gain/Atten */ + ad1843_RMCMM = { 7, 7, 1 }, /* Right Mic Mix Mute */ + ad1843_LMCM = { 7, 8, 5 }, /* Left Mic Mix Gain/Atten */ + ad1843_LMCMM = { 7, 15, 1 }, /* Left Mic Mix Mute */ + ad1843_HPOS = { 8, 4, 1 }, /* Headphone Output Voltage Swing */ + ad1843_HPOM = { 8, 5, 1 }, /* Headphone Output Mute */ + ad1843_RDA1G = { 9, 0, 6 }, /* Right DAC1 Analog/Digital Gain */ + ad1843_RDA1GM = { 9, 7, 1 }, /* Right DAC1 Analog Mute */ + ad1843_LDA1G = { 9, 8, 6 }, /* Left DAC1 Analog/Digital Gain */ + ad1843_LDA1GM = { 9, 15, 1 }, /* Left DAC1 Analog Mute */ + ad1843_RDA1AM = { 11, 7, 1 }, /* Right DAC1 Digital Mute */ + ad1843_LDA1AM = { 11, 15, 1 }, /* Left DAC1 Digital Mute */ + ad1843_ADLC = { 15, 0, 2 }, /* ADC Left Sample Rate Source */ + ad1843_ADRC = { 15, 2, 2 }, /* ADC Right Sample Rate Source */ + ad1843_DA1C = { 15, 8, 2 }, /* DAC1 Sample Rate Source */ + ad1843_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */ + ad1843_C2C = { 20, 0, 16 }, /* Clock 1 Sample Rate Select */ + ad1843_DAADL = { 25, 4, 2 }, /* Digital ADC Left Source Select */ + ad1843_DAADR = { 25, 6, 2 }, /* Digital ADC Right Source Select */ + ad1843_DRSFLT = { 25, 15, 1 }, /* Digital Reampler Filter Mode */ + ad1843_ADLF = { 26, 0, 2 }, /* ADC Left Channel Data Format */ + ad1843_ADRF = { 26, 2, 2 }, /* ADC Right Channel Data Format */ + ad1843_ADTLK = { 26, 4, 1 }, /* ADC Transmit Lock Mode Select */ + ad1843_SCF = { 26, 7, 1 }, /* SCLK Frequency Select */ + ad1843_DA1F = { 26, 8, 2 }, /* DAC1 Data Format Select */ + ad1843_DA1SM = { 26, 14, 1 }, /* DAC1 Stereo/Mono Mode Select */ + ad1843_ADLEN = { 27, 0, 1 }, /* ADC Left Channel Enable */ + ad1843_ADREN = { 27, 1, 1 }, /* ADC Right Channel Enable */ + ad1843_AAMEN = { 27, 4, 1 }, /* Analog to Analog Mix Enable */ + ad1843_ANAEN = { 27, 7, 1 }, /* Analog Channel Enable */ + ad1843_DA1EN = { 27, 8, 1 }, /* DAC1 Enable */ + ad1843_DA2EN = { 27, 9, 1 }, /* DAC2 Enable */ + ad1843_C1EN = { 28, 11, 1 }, /* Clock Generator 1 Enable */ + ad1843_C2EN = { 28, 12, 1 }, /* Clock Generator 2 Enable */ + ad1843_PDNI = { 28, 15, 1 }; /* Converter Power Down */ + +/* + * The various registers of the AD1843 use three different formats for + * specifying gain. The ad1843_gain structure parameterizes the + * formats. + */ + +typedef struct ad1843_gain { + + int negative; /* nonzero if gain is negative. */ + const ad1843_bitfield_t *lfield; + const ad1843_bitfield_t *rfield; + +} ad1843_gain_t; + +static const ad1843_gain_t ad1843_gain_RECLEV + = { 0, &ad1843_LIG, &ad1843_RIG }; +static const ad1843_gain_t ad1843_gain_LINE + = { 1, &ad1843_LX1M, &ad1843_RX1M }; +static const ad1843_gain_t ad1843_gain_CD + = { 1, &ad1843_LX2M, &ad1843_RX2M }; +static const ad1843_gain_t ad1843_gain_MIC + = { 1, &ad1843_LMCM, &ad1843_RMCM }; +static const ad1843_gain_t ad1843_gain_PCM + = { 1, &ad1843_LDA1G, &ad1843_RDA1G }; + +/* read the current value of an AD1843 bitfield. */ + +static int ad1843_read_bits(lithium_t *lith, const ad1843_bitfield_t *field) +{ + int w = li_read_ad1843_reg(lith, field->reg); + int val = w >> field->lo_bit & ((1 << field->nbits) - 1); + + DBGXV("ad1843_read_bits(lith=0x%p, field->{%d %d %d}) returns 0x%x\n", + lith, field->reg, field->lo_bit, field->nbits, val); + + return val; +} + +/* + * write a new value to an AD1843 bitfield and return the old value. + */ + +static int ad1843_write_bits(lithium_t *lith, + const ad1843_bitfield_t *field, + int newval) +{ + int w = li_read_ad1843_reg(lith, field->reg); + int mask = ((1 << field->nbits) - 1) << field->lo_bit; + int oldval = (w & mask) >> field->lo_bit; + int newbits = (newval << field->lo_bit) & mask; + w = (w & ~mask) | newbits; + (void) li_write_ad1843_reg(lith, field->reg, w); + + DBGXV("ad1843_write_bits(lith=0x%p, field->{%d %d %d}, val=0x%x) " + "returns 0x%x\n", + lith, field->reg, field->lo_bit, field->nbits, newval, + oldval); + + return oldval; +} + +/* + * ad1843_read_multi reads multiple bitfields from the same AD1843 + * register. It uses a single read cycle to do it. (Reading the + * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20 + * microseconds.) + * + * Called ike this. + * + * ad1843_read_multi(lith, nfields, + * &ad1843_FIELD1, &val1, + * &ad1843_FIELD2, &val2, ...); + */ + +static void ad1843_read_multi(lithium_t *lith, int argcount, ...) +{ + va_list ap; + const ad1843_bitfield_t *fp; + int w = 0, mask, *value, reg = -1; + + va_start(ap, argcount); + while (--argcount >= 0) { + fp = va_arg(ap, const ad1843_bitfield_t *); + value = va_arg(ap, int *); + if (reg == -1) { + reg = fp->reg; + w = li_read_ad1843_reg(lith, reg); + } + ASSERT(reg == fp->reg); + mask = (1 << fp->nbits) - 1; + *value = w >> fp->lo_bit & mask; + } + va_end(ap); +} + +/* + * ad1843_write_multi stores multiple bitfields into the same AD1843 + * register. It uses one read and one write cycle to do it. + * + * Called like this. + * + * ad1843_write_multi(lith, nfields, + * &ad1843_FIELD1, val1, + * &ad1843_FIELF2, val2, ...); + */ + +static void ad1843_write_multi(lithium_t *lith, int argcount, ...) +{ + va_list ap; + int reg; + const ad1843_bitfield_t *fp; + int value; + int w, m, mask, bits; + + mask = 0; + bits = 0; + reg = -1; + + va_start(ap, argcount); + while (--argcount >= 0) { + fp = va_arg(ap, const ad1843_bitfield_t *); + value = va_arg(ap, int); + if (reg == -1) + reg = fp->reg; + ASSERT(fp->reg == reg); + m = ((1 << fp->nbits) - 1) << fp->lo_bit; + mask |= m; + bits |= (value << fp->lo_bit) & m; + } + va_end(ap); + ASSERT(!(bits & ~mask)); + if (~mask & 0xFFFF) + w = li_read_ad1843_reg(lith, reg); + else + w = 0; + w = (w & ~mask) | bits; + (void) li_write_ad1843_reg(lith, reg, w); +} + +/* + * ad1843_get_gain reads the specified register and extracts the gain value + * using the supplied gain type. It returns the gain in OSS format. + */ + +static int ad1843_get_gain(lithium_t *lith, const ad1843_gain_t *gp) +{ + int lg, rg; + unsigned short mask = (1 << gp->lfield->nbits) - 1; + + ad1843_read_multi(lith, 2, gp->lfield, &lg, gp->rfield, &rg); + if (gp->negative) { + lg = mask - lg; + rg = mask - rg; + } + lg = (lg * 100 + (mask >> 1)) / mask; + rg = (rg * 100 + (mask >> 1)) / mask; + return lg << 0 | rg << 8; +} + +/* + * Set an audio channel's gain. Converts from OSS format to AD1843's + * format. + * + * Returns the new gain, which may be lower than the old gain. + */ + +static int ad1843_set_gain(lithium_t *lith, + const ad1843_gain_t *gp, + int newval) +{ + unsigned short mask = (1 << gp->lfield->nbits) - 1; + + int lg = newval >> 0 & 0xFF; + int rg = newval >> 8; + if (lg < 0 || lg > 100 || rg < 0 || rg > 100) + return -EINVAL; + lg = (lg * mask + (mask >> 1)) / 100; + rg = (rg * mask + (mask >> 1)) / 100; + if (gp->negative) { + lg = mask - lg; + rg = mask - rg; + } + ad1843_write_multi(lith, 2, gp->lfield, lg, gp->rfield, rg); + return ad1843_get_gain(lith, gp); +} + +/* Returns the current recording source, in OSS format. */ + +static int ad1843_get_recsrc(lithium_t *lith) +{ + int ls = ad1843_read_bits(lith, &ad1843_LSS); + + switch (ls) { + case 1: + return SOUND_MASK_MIC; + case 2: + return SOUND_MASK_LINE; + case 3: + return SOUND_MASK_CD; + case 6: + return SOUND_MASK_PCM; + default: + ASSERT(0); + return -1; + } +} + +/* + * Enable/disable digital resample mode in the AD1843. + * + * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down + * while switching modes. So we save DA1's state (DA2's state is not + * interesting), power them down, switch into/out of resample mode, + * power them up, and restore state. + * + * This will cause audible glitches if D/A or A/D is going on, so the + * driver disallows that (in mixer_write_ioctl()). + * + * The open question is, is this worth doing? I'm leaving it in, + * because it's written, but... + */ + +static void ad1843_set_resample_mode(lithium_t *lith, int onoff) +{ + /* Save DA1 mute and gain (addr 9 is DA1 analog gain/attenuation) */ + int save_da1 = li_read_ad1843_reg(lith, 9); + + /* Power down A/D and D/A. */ + ad1843_write_multi(lith, 4, + &ad1843_DA1EN, 0, + &ad1843_DA2EN, 0, + &ad1843_ADLEN, 0, + &ad1843_ADREN, 0); + + /* Switch mode */ + ASSERT(onoff == 0 || onoff == 1); + ad1843_write_bits(lith, &ad1843_DRSFLT, onoff); + + /* Power up A/D and D/A. */ + ad1843_write_multi(lith, 3, + &ad1843_DA1EN, 1, + &ad1843_ADLEN, 1, + &ad1843_ADREN, 1); + + /* Restore DA1 mute and gain. */ + li_write_ad1843_reg(lith, 9, save_da1); +} + +/* + * Set recording source. Arg newsrc specifies an OSS channel mask. + * + * The complication is that when we switch into/out of loopback mode + * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of + * digital resampling mode. + * + * Returns newsrc on success, -errno on failure. + */ + +static int ad1843_set_recsrc(lithium_t *lith, int newsrc) +{ + int bits; + int oldbits; + + switch (newsrc) { + case SOUND_MASK_PCM: + bits = 6; + break; + + case SOUND_MASK_MIC: + bits = 1; + break; + + case SOUND_MASK_LINE: + bits = 2; + break; + + case SOUND_MASK_CD: + bits = 3; + break; + + default: + return -EINVAL; + } + oldbits = ad1843_read_bits(lith, &ad1843_LSS); + if (newsrc == SOUND_MASK_PCM && oldbits != 6) { + DBGP("enabling digital resample mode\n"); + ad1843_set_resample_mode(lith, 1); + ad1843_write_multi(lith, 2, + &ad1843_DAADL, 2, + &ad1843_DAADR, 2); + } else if (newsrc != SOUND_MASK_PCM && oldbits == 6) { + DBGP("disabling digital resample mode\n"); + ad1843_set_resample_mode(lith, 0); + ad1843_write_multi(lith, 2, + &ad1843_DAADL, 0, + &ad1843_DAADR, 0); + } + ad1843_write_multi(lith, 2, &ad1843_LSS, bits, &ad1843_RSS, bits); + return newsrc; +} + +/* + * Return current output sources, in OSS format. + */ + +static int ad1843_get_outsrc(lithium_t *lith) +{ + int pcm, line, mic, cd; + + pcm = ad1843_read_bits(lith, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM; + line = ad1843_read_bits(lith, &ad1843_LX1MM) ? 0 : SOUND_MASK_LINE; + cd = ad1843_read_bits(lith, &ad1843_LX2MM) ? 0 : SOUND_MASK_CD; + mic = ad1843_read_bits(lith, &ad1843_LMCMM) ? 0 : SOUND_MASK_MIC; + + return pcm | line | cd | mic; +} + +/* + * Set output sources. Arg is a mask of active sources in OSS format. + * + * Returns source mask on success, -errno on failure. + */ + +static int ad1843_set_outsrc(lithium_t *lith, int mask) +{ + int pcm, line, mic, cd; + + if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_CD | SOUND_MASK_MIC)) + return -EINVAL; + pcm = (mask & SOUND_MASK_PCM) ? 0 : 1; + line = (mask & SOUND_MASK_LINE) ? 0 : 1; + mic = (mask & SOUND_MASK_MIC) ? 0 : 1; + cd = (mask & SOUND_MASK_CD) ? 0 : 1; + + ad1843_write_multi(lith, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm); + ad1843_write_multi(lith, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line); + ad1843_write_multi(lith, 2, &ad1843_LX2MM, cd, &ad1843_RX2MM, cd); + ad1843_write_multi(lith, 2, &ad1843_LMCMM, mic, &ad1843_RMCMM, mic); + + return mask; +} + +/* Setup ad1843 for D/A conversion. */ + +static void ad1843_setup_dac(lithium_t *lith, + int framerate, + int fmt, + int channels) +{ + int ad_fmt = 0, ad_mode = 0; + + DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n", + lith, framerate, fmt, channels); + + switch (fmt) { + case AFMT_S8: ad_fmt = 1; break; + case AFMT_U8: ad_fmt = 1; break; + case AFMT_S16_LE: ad_fmt = 1; break; + case AFMT_MU_LAW: ad_fmt = 2; break; + case AFMT_A_LAW: ad_fmt = 3; break; + default: ASSERT(0); + } + + switch (channels) { + case 2: ad_mode = 0; break; + case 1: ad_mode = 1; break; + default: ASSERT(0); + } + + DBGPV("ad_mode = %d, ad_fmt = %d\n", ad_mode, ad_fmt); + ASSERT(framerate >= 4000 && framerate <= 49000); + ad1843_write_bits(lith, &ad1843_C1C, framerate); + ad1843_write_multi(lith, 2, + &ad1843_DA1SM, ad_mode, &ad1843_DA1F, ad_fmt); +} + +static void ad1843_shutdown_dac(lithium_t *lith) +{ + ad1843_write_bits(lith, &ad1843_DA1F, 1); +} + +static void ad1843_setup_adc(lithium_t *lith, int framerate, int fmt, int channels) +{ + int da_fmt = 0; + + DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n", + lith, framerate, fmt, channels); + + switch (fmt) { + case AFMT_S8: da_fmt = 1; break; + case AFMT_U8: da_fmt = 1; break; + case AFMT_S16_LE: da_fmt = 1; break; + case AFMT_MU_LAW: da_fmt = 2; break; + case AFMT_A_LAW: da_fmt = 3; break; + default: ASSERT(0); + } + + DBGPV("da_fmt = %d\n", da_fmt); + ASSERT(framerate >= 4000 && framerate <= 49000); + ad1843_write_bits(lith, &ad1843_C2C, framerate); + ad1843_write_multi(lith, 2, + &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt); +} + +static void ad1843_shutdown_adc(lithium_t *lith) +{ + /* nothing to do */ +} + +/* + * Fully initialize the ad1843. As described in the AD1843 data + * sheet, section "START-UP SEQUENCE". The numbered comments are + * subsection headings from the data sheet. See the data sheet, pages + * 52-54, for more info. + * + * return 0 on success, -errno on failure. */ + +static int __init ad1843_init(lithium_t *lith) +{ + unsigned long later; + int err; + + err = li_init(lith); + if (err) + return err; + + if (ad1843_read_bits(lith, &ad1843_INIT) != 0) { + printk(KERN_ERR "vwsnd sound: AD1843 won't initialize\n"); + return -EIO; + } + + ad1843_write_bits(lith, &ad1843_SCF, 1); + + /* 4. Put the conversion resources into standby. */ + + ad1843_write_bits(lith, &ad1843_PDNI, 0); + later = jiffies + HZ / 2; /* roughly half a second */ + DBGDO(shut_up++); + while (ad1843_read_bits(lith, &ad1843_PDNO)) { + if (jiffies > later) { + printk(KERN_ERR + "vwsnd audio: AD1843 won't power up\n"); + return -EIO; + } + schedule(); + } + DBGDO(shut_up--); + + /* 5. Power up the clock generators and enable clock output pins. */ + + ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1); + + /* 6. Configure conversion resources while they are in standby. */ + + /* DAC1 uses clock 1 as source, ADC uses clock 2. Always. */ + + ad1843_write_multi(lith, 3, + &ad1843_DA1C, 1, + &ad1843_ADLC, 2, + &ad1843_ADRC, 2); + + /* 7. Enable conversion resources. */ + + ad1843_write_bits(lith, &ad1843_ADTLK, 1); + ad1843_write_multi(lith, 5, + &ad1843_ANAEN, 1, + &ad1843_AAMEN, 1, + &ad1843_DA1EN, 1, + &ad1843_ADLEN, 1, + &ad1843_ADREN, 1); + + /* 8. Configure conversion resources while they are enabled. */ + + ad1843_write_bits(lith, &ad1843_DA1C, 1); + + /* Unmute all channels. */ + + ad1843_set_outsrc(lith, + (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD)); + ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0); + + /* Set default recording source to Line In and set + * mic gain to +20 dB. + */ + + ad1843_set_recsrc(lith, SOUND_MASK_LINE); + ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1); + + /* Set Speaker Out level to +/- 4V and unmute it. */ + + ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0); + + return 0; +} + +/*****************************************************************************/ +/* PCM I/O */ + +#define READ_INTR_MASK (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW) +#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW) + +typedef enum vwsnd_port_swstate { /* software state */ + SW_OFF, + SW_INITIAL, + SW_RUN, + SW_DRAIN, +} vwsnd_port_swstate_t; + +typedef enum vwsnd_port_hwstate { /* hardware state */ + HW_STOPPED, + HW_RUNNING, +} vwsnd_port_hwstate_t; + +/* + * These flags are read by ISR, but only written at baseline. + */ + +typedef enum vwsnd_port_flags { + DISABLED = 1 << 0, + ERFLOWN = 1 << 1, /* overflown or underflown */ + HW_BUSY = 1 << 2, +} vwsnd_port_flags_t; + +/* + * vwsnd_port is the per-port data structure. Each device has two + * ports, one for input and one for output. + * + * Locking: + * + * port->lock protects: hwstate, flags, swb_[iu]_avail. + * + * devc->io_sema protects: swstate, sw_*, swb_[iu]_idx. + * + * everything else is only written by open/release or + * pcm_{setup,shutdown}(), which are serialized by a + * combination of devc->open_sema and devc->io_sema. + */ + +typedef struct vwsnd_port { + + spinlock_t lock; + wait_queue_head_t queue; + vwsnd_port_swstate_t swstate; + vwsnd_port_hwstate_t hwstate; + vwsnd_port_flags_t flags; + + int sw_channels; + int sw_samplefmt; + int sw_framerate; + int sample_size; + int frame_size; + unsigned int zero_word; /* zero for the sample format */ + + int sw_fragshift; + int sw_fragcount; + int sw_subdivshift; + + unsigned int hw_fragshift; + unsigned int hw_fragsize; + unsigned int hw_fragcount; + + int hwbuf_size; + unsigned long hwbuf_paddr; + unsigned long hwbuf_vaddr; + caddr_t hwbuf; /* hwbuf == hwbuf_vaddr */ + int hwbuf_max; /* max bytes to preload */ + + caddr_t swbuf; + unsigned int swbuf_size; /* size in bytes */ + unsigned int swb_u_idx; /* index of next user byte */ + unsigned int swb_i_idx; /* index of next intr byte */ + unsigned int swb_u_avail; /* # bytes avail to user */ + unsigned int swb_i_avail; /* # bytes avail to intr */ + + dma_chan_t chan; + + /* Accounting */ + + int byte_count; + int frag_count; + int MSC_offset; + +} vwsnd_port_t; + +/* vwsnd_dev is the per-device data structure. */ + +typedef struct vwsnd_dev { + struct vwsnd_dev *next_dev; + int audio_minor; /* minor number of audio device */ + int mixer_minor; /* minor number of mixer device */ + + struct semaphore open_sema; + struct semaphore io_sema; + struct semaphore mix_sema; + mode_t open_mode; + wait_queue_head_t open_wait; + + lithium_t lith; + + vwsnd_port_t rport; + vwsnd_port_t wport; +} vwsnd_dev_t; + +static vwsnd_dev_t *vwsnd_dev_list; /* linked list of all devices */ + +static atomic_t vwsnd_use_count = ATOMIC_INIT(0); + +# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count)) +# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count)) +# define IN_USE (atomic_read(&vwsnd_use_count) != 0) + +/* + * Lithium can only DMA multiples of 32 bytes. Its DMA buffer may + * be up to 8 Kb. This driver always uses 8 Kb. + * + * Memory bug workaround -- I'm not sure what's going on here, but + * somehow pcm_copy_out() was triggering segv's going on to the next + * page of the hw buffer. So, I make the hw buffer one size bigger + * than we actually use. That way, the following page is allocated + * and mapped, and no error. I suspect that something is broken + * in Cobalt, but haven't really investigated. HBO is the actual + * size of the buffer, and HWBUF_ORDER is what we allocate. + */ + +#define HWBUF_SHIFT 13 +#define HWBUF_SIZE (1 << HWBUF_SHIFT) +# define HBO (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0) +# define HWBUF_ORDER (HBO + 1) /* next size bigger */ +#define MIN_SPEED 4000 +#define MAX_SPEED 49000 + +#define MIN_FRAGSHIFT (DMACHUNK_SHIFT + 1) +#define MAX_FRAGSHIFT (PAGE_SHIFT) +#define MIN_FRAGSIZE (1 << MIN_FRAGSHIFT) +#define MAX_FRAGSIZE (1 << MAX_FRAGSHIFT) +#define MIN_FRAGCOUNT(fragsize) 3 +#define MAX_FRAGCOUNT(fragsize) (32 * PAGE_SIZE / (fragsize)) +#define DEFAULT_FRAGSHIFT 12 +#define DEFAULT_FRAGCOUNT 16 +#define DEFAULT_SUBDIVSHIFT 0 + +/* + * The software buffer (swbuf) is a ring buffer shared between user + * level and interrupt level. Each level owns some of the bytes in + * the buffer, and may give bytes away by calling swb_inc_{u,i}(). + * User level calls _u for user, and interrupt level calls _i for + * interrupt. + * + * port->swb_{u,i}_avail is the number of bytes available to that level. + * + * port->swb_{u,i}_idx is the index of the first available byte in the + * buffer. + * + * Each level calls swb_inc_{u,i}() to atomically increment its index, + * recalculate the number of bytes available for both sides, and + * return the number of bytes available. Since each side can only + * give away bytes, the other side can only increase the number of + * bytes available to this side. Each side updates its own index + * variable, swb_{u,i}_idx, so no lock is needed to read it. + * + * To query the number of bytes available, call swb_inc_{u,i} with an + * increment of zero. + */ + +static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc) +{ + if (inc) { + port->swb_u_idx += inc; + port->swb_u_idx %= port->swbuf_size; + port->swb_u_avail -= inc; + port->swb_i_avail += inc; + } + return port->swb_u_avail; +} + +static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&port->lock, flags); + { + ret = __swb_inc_u(port, inc); + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc) +{ + if (inc) { + port->swb_i_idx += inc; + port->swb_i_idx %= port->swbuf_size; + port->swb_i_avail -= inc; + port->swb_u_avail += inc; + } + return port->swb_i_avail; +} + +static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&port->lock, flags); + { + ret = __swb_inc_i(port, inc); + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +/* + * pcm_setup - this routine initializes all port state after + * mode-setting ioctls have been done, but before the first I/O is + * done. + * + * Locking: called with devc->io_sema held. + * + * Returns 0 on success, -errno on failure. + */ + +static int pcm_setup(vwsnd_dev_t *devc, + vwsnd_port_t *rport, + vwsnd_port_t *wport) +{ + vwsnd_port_t *aport = rport ? rport : wport; + int sample_size; + unsigned int zero_word; + + DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport); + + ASSERT(aport != NULL); + if (aport->swbuf != NULL) + return 0; + switch (aport->sw_samplefmt) { + case AFMT_MU_LAW: + sample_size = 1; + zero_word = 0xFFFFFFFF ^ 0x80808080; + break; + + case AFMT_A_LAW: + sample_size = 1; + zero_word = 0xD5D5D5D5 ^ 0x80808080; + break; + + case AFMT_U8: + sample_size = 1; + zero_word = 0x80808080; + break; + + case AFMT_S8: + sample_size = 1; + zero_word = 0x00000000; + break; + + case AFMT_S16_LE: + sample_size = 2; + zero_word = 0x00000000; + break; + + default: + sample_size = 0; /* prevent compiler warning */ + zero_word = 0; + ASSERT(0); + } + aport->sample_size = sample_size; + aport->zero_word = zero_word; + aport->frame_size = aport->sw_channels * aport->sample_size; + aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift; + aport->hw_fragsize = 1 << aport->hw_fragshift; + aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift; + ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE); + ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE); + ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize)); + ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize)); + if (rport) { + int hwfrags, swfrags; + rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE; + hwfrags = rport->hwbuf_max >> aport->hw_fragshift; + swfrags = aport->hw_fragcount - hwfrags; + if (swfrags < 2) + swfrags = 2; + rport->swbuf_size = swfrags * aport->hw_fragsize; + DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags); + DBGPV("read hwbuf_max = %d, swbuf_size = %d\n", + rport->hwbuf_max, rport->swbuf_size); + } + if (wport) { + int hwfrags, swfrags; + int total_bytes = aport->hw_fragcount * aport->hw_fragsize; + wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE; + if (wport->hwbuf_max > total_bytes) + wport->hwbuf_max = total_bytes; + hwfrags = wport->hwbuf_max >> aport->hw_fragshift; + DBGPV("hwfrags = %d\n", hwfrags); + swfrags = aport->hw_fragcount - hwfrags; + if (swfrags < 2) + swfrags = 2; + wport->swbuf_size = swfrags * aport->hw_fragsize; + DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags); + DBGPV("write hwbuf_max = %d, swbuf_size = %d\n", + wport->hwbuf_max, wport->swbuf_size); + } + + aport->swb_u_idx = 0; + aport->swb_i_idx = 0; + aport->byte_count = 0; + + /* + * Is this a Cobalt bug? We need to make this buffer extend + * one page further than we actually use -- somehow memcpy + * causes an exceptoin otherwise. I suspect there's a bug in + * Cobalt (or somewhere) where it's generating a fault on a + * speculative load or something. Obviously, I haven't taken + * the time to track it down. + */ + + aport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE); + if (!aport->swbuf) + return -ENOMEM; + if (rport && wport) { + ASSERT(aport == rport); + ASSERT(wport->swbuf == NULL); + /* One extra page - see comment above. */ + wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE); + if (!wport->swbuf) { + vfree(aport->swbuf); + aport->swbuf = NULL; + return -ENOMEM; + } + wport->sample_size = rport->sample_size; + wport->zero_word = rport->zero_word; + wport->frame_size = rport->frame_size; + wport->hw_fragshift = rport->hw_fragshift; + wport->hw_fragsize = rport->hw_fragsize; + wport->hw_fragcount = rport->hw_fragcount; + wport->swbuf_size = rport->swbuf_size; + wport->hwbuf_max = rport->hwbuf_max; + wport->swb_u_idx = rport->swb_u_idx; + wport->swb_i_idx = rport->swb_i_idx; + wport->byte_count = rport->byte_count; + } + if (rport) { + rport->swb_u_avail = 0; + rport->swb_i_avail = rport->swbuf_size; + rport->swstate = SW_RUN; + li_setup_dma(&rport->chan, + &li_comm1, + &devc->lith, + rport->hwbuf_paddr, + HWBUF_SHIFT, + rport->hw_fragshift, + rport->sw_channels, + rport->sample_size); + ad1843_setup_adc(&devc->lith, + rport->sw_framerate, + rport->sw_samplefmt, + rport->sw_channels); + li_enable_interrupts(&devc->lith, READ_INTR_MASK); + if (!(rport->flags & DISABLED)) { + ustmsc_t ustmsc; + rport->hwstate = HW_RUNNING; + li_activate_dma(&rport->chan); + li_read_USTMSC(&rport->chan, &ustmsc); + rport->MSC_offset = ustmsc.msc; + } + } + if (wport) { + if (wport->hwbuf_max > wport->swbuf_size) + wport->hwbuf_max = wport->swbuf_size; + wport->flags &= ~ERFLOWN; + wport->swb_u_avail = wport->swbuf_size; + wport->swb_i_avail = 0; + wport->swstate = SW_RUN; + li_setup_dma(&wport->chan, + &li_comm2, + &devc->lith, + wport->hwbuf_paddr, + HWBUF_SHIFT, + wport->hw_fragshift, + wport->sw_channels, + wport->sample_size); + ad1843_setup_dac(&devc->lith, + wport->sw_framerate, + wport->sw_samplefmt, + wport->sw_channels); + li_enable_interrupts(&devc->lith, WRITE_INTR_MASK); + } + DBGRV(); + return 0; +} + +/* + * pcm_shutdown_port - shut down one port (direction) for PCM I/O. + * Only called from pcm_shutdown. + */ + +static void pcm_shutdown_port(vwsnd_dev_t *devc, + vwsnd_port_t *aport, + unsigned int mask) +{ + unsigned long flags; + vwsnd_port_hwstate_t hwstate; + DECLARE_WAITQUEUE(wait, current); + + aport->swstate = SW_INITIAL; + add_wait_queue(&aport->queue, &wait); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + spin_lock_irqsave(&aport->lock, flags); + { + hwstate = aport->hwstate; + } + spin_unlock_irqrestore(&aport->lock, flags); + if (hwstate == HW_STOPPED) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&aport->queue, &wait); + li_disable_interrupts(&devc->lith, mask); + if (aport == &devc->rport) + ad1843_shutdown_adc(&devc->lith); + else /* aport == &devc->wport) */ + ad1843_shutdown_dac(&devc->lith); + li_shutdown_dma(&aport->chan); + vfree(aport->swbuf); + aport->swbuf = NULL; + aport->byte_count = 0; +} + +/* + * pcm_shutdown undoes what pcm_setup did. + * Also sets the ports' swstate to newstate. + */ + +static void pcm_shutdown(vwsnd_dev_t *devc, + vwsnd_port_t *rport, + vwsnd_port_t *wport) +{ + DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport); + + if (rport && rport->swbuf) { + DBGPV("shutting down rport\n"); + pcm_shutdown_port(devc, rport, READ_INTR_MASK); + } + if (wport && wport->swbuf) { + DBGPV("shutting down wport\n"); + pcm_shutdown_port(devc, wport, WRITE_INTR_MASK); + } + DBGRV(); +} + +static void pcm_copy_in(vwsnd_port_t *rport, int swidx, int hwidx, int nb) +{ + char *src = rport->hwbuf + hwidx; + char *dst = rport->swbuf + swidx; + int fmt = rport->sw_samplefmt; + + DBGPV("swidx = %d, hwidx = %d\n", swidx, hwidx); + ASSERT(rport->hwbuf != NULL); + ASSERT(rport->swbuf != NULL); + ASSERT(nb > 0 && (nb % 32) == 0); + ASSERT(swidx % 32 == 0 && hwidx % 32 == 0); + ASSERT(swidx >= 0 && swidx + nb <= rport->swbuf_size); + ASSERT(hwidx >= 0 && hwidx + nb <= rport->hwbuf_size); + + if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) { + + /* See Sample Format Notes above. */ + + char *end = src + nb; + while (src < end) + *dst++ = *src++ ^ 0x80; + } else + memcpy(dst, src, nb); +} + +static void pcm_copy_out(vwsnd_port_t *wport, int swidx, int hwidx, int nb) +{ + char *src = wport->swbuf + swidx; + char *dst = wport->hwbuf + hwidx; + int fmt = wport->sw_samplefmt; + + ASSERT(nb > 0 && (nb % 32) == 0); + ASSERT(wport->hwbuf != NULL); + ASSERT(wport->swbuf != NULL); + ASSERT(swidx % 32 == 0 && hwidx % 32 == 0); + ASSERT(swidx >= 0 && swidx + nb <= wport->swbuf_size); + ASSERT(hwidx >= 0 && hwidx + nb <= wport->hwbuf_size); + if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) { + + /* See Sample Format Notes above. */ + + char *end = src + nb; + while (src < end) + *dst++ = *src++ ^ 0x80; + } else + memcpy(dst, src, nb); +} + +/* + * pcm_output() is called both from baselevel and from interrupt level. + * This is where audio frames are copied into the hardware-accessible + * ring buffer. + * + * Locking note: The part of this routine that figures out what to do + * holds wport->lock. The longer part releases wport->lock, but sets + * wport->flags & HW_BUSY. Afterward, it reacquires wport->lock, and + * checks for more work to do. + * + * If another thread calls pcm_output() while HW_BUSY is set, it + * returns immediately, knowing that the thread that set HW_BUSY will + * look for more work to do before returning. + * + * This has the advantage that port->lock is held for several short + * periods instead of one long period. Also, when pcm_output is + * called from base level, it reenables interrupts. + */ + +static void pcm_output(vwsnd_dev_t *devc, int erflown, int nb) +{ + vwsnd_port_t *wport = &devc->wport; + const int hwmax = wport->hwbuf_max; + const int hwsize = wport->hwbuf_size; + const int swsize = wport->swbuf_size; + const int fragsize = wport->hw_fragsize; + unsigned long iflags; + + DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb); + spin_lock_irqsave(&wport->lock, iflags); + if (erflown) + wport->flags |= ERFLOWN; + (void) __swb_inc_u(wport, nb); + if (wport->flags & HW_BUSY) { + spin_unlock_irqrestore(&wport->lock, iflags); + DBGPV("returning: HW BUSY\n"); + return; + } + if (wport->flags & DISABLED) { + spin_unlock_irqrestore(&wport->lock, iflags); + DBGPV("returning: DISABLED\n"); + return; + } + wport->flags |= HW_BUSY; + while (1) { + int swptr, hwptr, hw_avail, sw_avail, swidx; + vwsnd_port_hwstate_t hwstate = wport->hwstate; + vwsnd_port_swstate_t swstate = wport->swstate; + int hw_unavail; + ustmsc_t ustmsc; + + hwptr = li_read_hwptr(&wport->chan); + swptr = li_read_swptr(&wport->chan); + hw_unavail = (swptr - hwptr + hwsize) % hwsize; + hw_avail = (hwmax - hw_unavail) & -fragsize; + sw_avail = wport->swb_i_avail & -fragsize; + if (sw_avail && swstate == SW_RUN) { + if (wport->flags & ERFLOWN) { + wport->flags &= ~ERFLOWN; + } + } else if (swstate == SW_INITIAL || + swstate == SW_OFF || + (swstate == SW_DRAIN && + !sw_avail && + (wport->flags & ERFLOWN))) { + DBGP("stopping. hwstate = %d\n", hwstate); + if (hwstate != HW_STOPPED) { + li_deactivate_dma(&wport->chan); + wport->hwstate = HW_STOPPED; + } + wake_up(&wport->queue); + break; + } + if (!sw_avail || !hw_avail) + break; + spin_unlock_irqrestore(&wport->lock, iflags); + + /* + * We gave up the port lock, but we have the HW_BUSY flag. + * Proceed without accessing any nonlocal state. + * Do not exit the loop -- must check for more work. + */ + + swidx = wport->swb_i_idx; + nb = hw_avail; + if (nb > sw_avail) + nb = sw_avail; + if (nb > hwsize - swptr) + nb = hwsize - swptr; /* don't overflow hwbuf */ + if (nb > swsize - swidx) + nb = swsize - swidx; /* don't overflow swbuf */ + ASSERT(nb > 0); + if (nb % fragsize) { + DBGP("nb = %d, fragsize = %d\n", nb, fragsize); + DBGP("hw_avail = %d\n", hw_avail); + DBGP("sw_avail = %d\n", sw_avail); + DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr); + DBGP("swsize = %d, swidx = %d\n", swsize, swidx); + } + ASSERT(!(nb % fragsize)); + DBGPV("copying swb[%d..%d] to hwb[%d..%d]\n", + swidx, swidx + nb, swptr, swptr + nb); + pcm_copy_out(wport, swidx, swptr, nb); + li_write_swptr(&wport->chan, (swptr + nb) % hwsize); + spin_lock_irqsave(&wport->lock, iflags); + if (hwstate == HW_STOPPED) { + DBGPV("starting\n"); + li_activate_dma(&wport->chan); + wport->hwstate = HW_RUNNING; + li_read_USTMSC(&wport->chan, &ustmsc); + ASSERT(wport->byte_count % wport->frame_size == 0); + wport->MSC_offset = ustmsc.msc - wport->byte_count / wport->frame_size; + } + __swb_inc_i(wport, nb); + wport->byte_count += nb; + wport->frag_count += nb / fragsize; + ASSERT(nb % fragsize == 0); + wake_up(&wport->queue); + } + wport->flags &= ~HW_BUSY; + spin_unlock_irqrestore(&wport->lock, iflags); + DBGRV(); +} + +/* + * pcm_input() is called both from baselevel and from interrupt level. + * This is where audio frames are copied out of the hardware-accessible + * ring buffer. + * + * Locking note: The part of this routine that figures out what to do + * holds rport->lock. The longer part releases rport->lock, but sets + * rport->flags & HW_BUSY. Afterward, it reacquires rport->lock, and + * checks for more work to do. + * + * If another thread calls pcm_input() while HW_BUSY is set, it + * returns immediately, knowing that the thread that set HW_BUSY will + * look for more work to do before returning. + * + * This has the advantage that port->lock is held for several short + * periods instead of one long period. Also, when pcm_input is + * called from base level, it reenables interrupts. + */ + +static void pcm_input(vwsnd_dev_t *devc, int erflown, int nb) +{ + vwsnd_port_t *rport = &devc->rport; + const int hwmax = rport->hwbuf_max; + const int hwsize = rport->hwbuf_size; + const int swsize = rport->swbuf_size; + const int fragsize = rport->hw_fragsize; + unsigned long iflags; + + DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb); + + spin_lock_irqsave(&rport->lock, iflags); + if (erflown) + rport->flags |= ERFLOWN; + (void) __swb_inc_u(rport, nb); + if (rport->flags & HW_BUSY || !rport->swbuf) { + spin_unlock_irqrestore(&rport->lock, iflags); + DBGPV("returning: HW BUSY or !swbuf\n"); + return; + } + if (rport->flags & DISABLED) { + spin_unlock_irqrestore(&rport->lock, iflags); + DBGPV("returning: DISABLED\n"); + return; + } + rport->flags |= HW_BUSY; + while (1) { + int swptr, hwptr, hw_avail, sw_avail, swidx; + vwsnd_port_hwstate_t hwstate = rport->hwstate; + vwsnd_port_swstate_t swstate = rport->swstate; + + hwptr = li_read_hwptr(&rport->chan); + swptr = li_read_swptr(&rport->chan); + hw_avail = (hwptr - swptr + hwsize) % hwsize & -fragsize; + if (hw_avail > hwmax) + hw_avail = hwmax; + sw_avail = rport->swb_i_avail & -fragsize; + if (swstate != SW_RUN) { + DBGP("stopping. hwstate = %d\n", hwstate); + if (hwstate != HW_STOPPED) { + li_deactivate_dma(&rport->chan); + rport->hwstate = HW_STOPPED; + } + wake_up(&rport->queue); + break; + } + if (!sw_avail || !hw_avail) + break; + spin_unlock_irqrestore(&rport->lock, iflags); + + /* + * We gave up the port lock, but we have the HW_BUSY flag. + * Proceed without accessing any nonlocal state. + * Do not exit the loop -- must check for more work. + */ + + swidx = rport->swb_i_idx; + nb = hw_avail; + if (nb > sw_avail) + nb = sw_avail; + if (nb > hwsize - swptr) + nb = hwsize - swptr; /* don't overflow hwbuf */ + if (nb > swsize - swidx) + nb = swsize - swidx; /* don't overflow swbuf */ + ASSERT(nb > 0); + if (nb % fragsize) { + DBGP("nb = %d, fragsize = %d\n", nb, fragsize); + DBGP("hw_avail = %d\n", hw_avail); + DBGP("sw_avail = %d\n", sw_avail); + DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr); + DBGP("swsize = %d, swidx = %d\n", swsize, swidx); + } + ASSERT(!(nb % fragsize)); + DBGPV("copying hwb[%d..%d] to swb[%d..%d]\n", + swptr, swptr + nb, swidx, swidx + nb); + pcm_copy_in(rport, swidx, swptr, nb); + li_write_swptr(&rport->chan, (swptr + nb) % hwsize); + spin_lock_irqsave(&rport->lock, iflags); + __swb_inc_i(rport, nb); + rport->byte_count += nb; + rport->frag_count += nb / fragsize; + ASSERT(nb % fragsize == 0); + wake_up(&rport->queue); + } + rport->flags &= ~HW_BUSY; + spin_unlock_irqrestore(&rport->lock, iflags); + DBGRV(); +} + +/* + * pcm_flush_frag() writes zero samples to fill the current fragment, + * then flushes it to the hardware. + * + * It is only meaningful to flush output, not input. + */ + +static void pcm_flush_frag(vwsnd_dev_t *devc) +{ + vwsnd_port_t *wport = &devc->wport; + + DBGPV("swstate = %d\n", wport->swstate); + if (wport->swstate == SW_RUN) { + int idx = wport->swb_u_idx; + int end = (idx + wport->hw_fragsize - 1) + >> wport->hw_fragshift + << wport->hw_fragshift; + int nb = end - idx; + DBGPV("clearing %d bytes\n", nb); + if (nb) + memset(wport->swbuf + idx, + (char) wport->zero_word, + nb); + wport->swstate = SW_DRAIN; + pcm_output(devc, 0, nb); + } + DBGRV(); +} + +/* + * Wait for output to drain. This sleeps uninterruptibly because + * there is nothing intelligent we can do if interrupted. This + * means the process will be delayed in responding to the signal. + */ + +static void pcm_write_sync(vwsnd_dev_t *devc) +{ + vwsnd_port_t *wport = &devc->wport; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + vwsnd_port_hwstate_t hwstate; + + DBGEV("(devc=0x%p)\n", devc); + add_wait_queue(&wport->queue, &wait); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + spin_lock_irqsave(&wport->lock, flags); + { + hwstate = wport->hwstate; + } + spin_unlock_irqrestore(&wport->lock, flags); + if (hwstate == HW_STOPPED) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + DBGPV("swstate = %d, hwstate = %d\n", wport->swstate, wport->hwstate); + DBGRV(); +} + +/*****************************************************************************/ +/* audio driver */ + +/* + * seek on an audio device always fails. + */ + +static void vwsnd_audio_read_intr(vwsnd_dev_t *devc, unsigned int status) +{ + int overflown = status & LI_INTR_COMM1_OVERFLOW; + + if (status & READ_INTR_MASK) + pcm_input(devc, overflown, 0); +} + +static void vwsnd_audio_write_intr(vwsnd_dev_t *devc, unsigned int status) +{ + int underflown = status & LI_INTR_COMM2_UNDERFLOW; + + if (status & WRITE_INTR_MASK) + pcm_output(devc, underflown, 0); +} + +static void vwsnd_audio_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) dev_id; + unsigned int status; + + DBGEV("(irq=%d, dev_id=0x%p, regs=0x%p)\n", irq, dev_id, regs); + + status = li_get_clear_intr_status(&devc->lith); + vwsnd_audio_read_intr(devc, status); + vwsnd_audio_write_intr(devc, status); +} + +static ssize_t vwsnd_audio_do_read(struct file *file, + char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + vwsnd_port_t *rport = ((file->f_mode & FMODE_READ) ? + &devc->rport : NULL); + int ret, nb; + + DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n", + file, buffer, count, ppos); + + if (!rport) + return -EINVAL; + + if (rport->swbuf == NULL) { + vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL; + ret = pcm_setup(devc, rport, wport); + if (ret < 0) + return ret; + } + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count) { + DECLARE_WAITQUEUE(wait, current); + add_wait_queue(&rport->queue, &wait); + while ((nb = swb_inc_u(rport, 0)) == 0) { + DBGPV("blocking\n"); + set_current_state(TASK_INTERRUPTIBLE); + if (rport->flags & DISABLED || + file->f_flags & O_NONBLOCK) { + current->state = TASK_RUNNING; + remove_wait_queue(&rport->queue, &wait); + return ret ? ret : -EAGAIN; + } + schedule(); + if (signal_pending(current)) { + current->state = TASK_RUNNING; + remove_wait_queue(&rport->queue, &wait); + return ret ? ret : -ERESTARTSYS; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&rport->queue, &wait); + pcm_input(devc, 0, 0); + /* nb bytes are available in userbuf. */ + if (nb > count) + nb = count; + DBGPV("nb = %d\n", nb); + copy_to_user(buffer, rport->swbuf + rport->swb_u_idx, nb); + (void) swb_inc_u(rport, nb); + buffer += nb; + count -= nb; + ret += nb; + } + DBGPV("returning %d\n", ret); + return ret; +} + +static ssize_t vwsnd_audio_read(struct file *file, + char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + ssize_t ret; + + down(&devc->io_sema); + ret = vwsnd_audio_do_read(file, buffer, count, ppos); + up(&devc->io_sema); + return ret; +} + +static ssize_t vwsnd_audio_do_write(struct file *file, + const char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + vwsnd_port_t *wport = ((file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL); + int ret, nb; + + DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n", + file, buffer, count, ppos); + + if (!wport) + return -EINVAL; + + if (wport->swbuf == NULL) { + vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? + &devc->rport : NULL; + ret = pcm_setup(devc, rport, wport); + if (ret < 0) + return ret; + } + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count) { + DECLARE_WAITQUEUE(wait, current); + add_wait_queue(&wport->queue, &wait); + while ((nb = swb_inc_u(wport, 0)) == 0) { + set_current_state(TASK_INTERRUPTIBLE); + if (wport->flags & DISABLED || + file->f_flags & O_NONBLOCK) { + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + return ret ? ret : -EAGAIN; + } + schedule(); + if (signal_pending(current)) { + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + return ret ? ret : -ERESTARTSYS; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + /* nb bytes are available in userbuf. */ + if (nb > count) + nb = count; + DBGPV("nb = %d\n", nb); + copy_from_user(wport->swbuf + wport->swb_u_idx, buffer, nb); + pcm_output(devc, 0, nb); + buffer += nb; + count -= nb; + ret += nb; + } + DBGPV("returning %d\n", ret); + return ret; +} + +static ssize_t vwsnd_audio_write(struct file *file, + const char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + ssize_t ret; + + down(&devc->io_sema); + ret = vwsnd_audio_do_write(file, buffer, count, ppos); + up(&devc->io_sema); + return ret; +} + +/* No kernel lock - fine */ +static unsigned int vwsnd_audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? + &devc->rport : NULL; + vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL; + unsigned int mask = 0; + + DBGEV("(file=0x%p, wait=0x%p)\n", file, wait); + + ASSERT(rport || wport); + if (rport) { + poll_wait(file, &rport->queue, wait); + if (swb_inc_u(rport, 0)) + mask |= (POLLIN | POLLRDNORM); + } + if (wport) { + poll_wait(file, &wport->queue, wait); + if (wport->swbuf == NULL || swb_inc_u(wport, 0)) + mask |= (POLLOUT | POLLWRNORM); + } + + DBGPV("returning 0x%x\n", mask); + return mask; +} + +static int vwsnd_audio_do_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? + &devc->rport : NULL; + vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL; + vwsnd_port_t *aport = rport ? rport : wport; + struct audio_buf_info buf_info; + struct count_info info; + unsigned long flags; + int ival; + + + DBGEV("(inode=0x%p, file=0x%p, cmd=0x%x, arg=0x%lx)\n", + inode, file, cmd, arg); + switch (cmd) { + case OSS_GETVERSION: /* _SIOR ('M', 118, int) */ + DBGX("OSS_GETVERSION\n"); + ival = SOUND_VERSION; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETCAPS: /* _SIOR ('P',15, int) */ + DBGX("SNDCTL_DSP_GETCAPS\n"); + ival = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETFMTS: /* _SIOR ('P',11, int) */ + DBGX("SNDCTL_DSP_GETFMTS\n"); + ival = (AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | + AFMT_U8 | AFMT_S8); + return put_user(ival, (int *) arg); + break; + + case SOUND_PCM_READ_RATE: /* _SIOR ('P', 2, int) */ + DBGX("SOUND_PCM_READ_RATE\n"); + ival = aport->sw_framerate; + return put_user(ival, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: /* _SIOR ('P', 6, int) */ + DBGX("SOUND_PCM_READ_CHANNELS\n"); + ival = aport->sw_channels; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SPEED: /* _SIOWR('P', 2, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_SPEED %d\n", ival); + if (ival) { + if (aport->swstate != SW_INITIAL) { + DBGX("SNDCTL_DSP_SPEED failed: swstate = %d\n", + aport->swstate); + return -EINVAL; + } + if (ival < MIN_SPEED) + ival = MIN_SPEED; + if (ival > MAX_SPEED) + ival = MAX_SPEED; + if (rport) + rport->sw_framerate = ival; + if (wport) + wport->sw_framerate = ival; + } else + ival = aport->sw_framerate; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_STEREO: /* _SIOWR('P', 3, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_STEREO %d\n", ival); + if (ival != 0 && ival != 1) + return -EINVAL; + if (aport->swstate != SW_INITIAL) + return -EINVAL; + if (rport) + rport->sw_channels = ival + 1; + if (wport) + wport->sw_channels = ival + 1; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_CHANNELS: /* _SIOWR('P', 6, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_CHANNELS %d\n", ival); + if (ival != 1 && ival != 2) + return -EINVAL; + if (aport->swstate != SW_INITIAL) + return -EINVAL; + if (rport) + rport->sw_channels = ival; + if (wport) + wport->sw_channels = ival; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETBLKSIZE: /* _SIOWR('P', 4, int) */ + ival = pcm_setup(devc, rport, wport); + if (ival < 0) { + DBGX("SNDCTL_DSP_GETBLKSIZE failed, errno %d\n", ival); + return ival; + } + ival = 1 << aport->sw_fragshift; + DBGX("SNDCTL_DSP_GETBLKSIZE returning %d\n", ival); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SETFRAGMENT: /* _SIOWR('P',10, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_SETFRAGMENT %d:%d\n", + ival >> 16, ival & 0xFFFF); + if (aport->swstate != SW_INITIAL) + return -EINVAL; + { + int sw_fragshift = ival & 0xFFFF; + int sw_subdivshift = aport->sw_subdivshift; + int hw_fragshift = sw_fragshift - sw_subdivshift; + int sw_fragcount = (ival >> 16) & 0xFFFF; + int hw_fragsize; + if (hw_fragshift < MIN_FRAGSHIFT) + hw_fragshift = MIN_FRAGSHIFT; + if (hw_fragshift > MAX_FRAGSHIFT) + hw_fragshift = MAX_FRAGSHIFT; + sw_fragshift = hw_fragshift + aport->sw_subdivshift; + hw_fragsize = 1 << hw_fragshift; + if (sw_fragcount < MIN_FRAGCOUNT(hw_fragsize)) + sw_fragcount = MIN_FRAGCOUNT(hw_fragsize); + if (sw_fragcount > MAX_FRAGCOUNT(hw_fragsize)) + sw_fragcount = MAX_FRAGCOUNT(hw_fragsize); + DBGPV("sw_fragshift = %d\n", sw_fragshift); + DBGPV("rport = 0x%p, wport = 0x%p\n", rport, wport); + if (rport) { + rport->sw_fragshift = sw_fragshift; + rport->sw_fragcount = sw_fragcount; + } + if (wport) { + wport->sw_fragshift = sw_fragshift; + wport->sw_fragcount = sw_fragcount; + } + ival = sw_fragcount << 16 | sw_fragshift; + } + DBGX("SNDCTL_DSP_SETFRAGMENT returns %d:%d\n", + ival >> 16, ival & 0xFFFF); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SUBDIVIDE: /* _SIOWR('P', 9, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_SUBDIVIDE %d\n", ival); + if (aport->swstate != SW_INITIAL) + return -EINVAL; + { + int subdivshift; + int hw_fragshift, hw_fragsize, hw_fragcount; + switch (ival) { + case 1: subdivshift = 0; break; + case 2: subdivshift = 1; break; + case 4: subdivshift = 2; break; + default: return -EINVAL; + } + hw_fragshift = aport->sw_fragshift - subdivshift; + if (hw_fragshift < MIN_FRAGSHIFT || + hw_fragshift > MAX_FRAGSHIFT) + return -EINVAL; + hw_fragsize = 1 << hw_fragshift; + hw_fragcount = aport->sw_fragcount >> subdivshift; + if (hw_fragcount < MIN_FRAGCOUNT(hw_fragsize) || + hw_fragcount > MAX_FRAGCOUNT(hw_fragsize)) + return -EINVAL; + if (rport) + rport->sw_subdivshift = subdivshift; + if (wport) + wport->sw_subdivshift = subdivshift; + } + return 0; + + case SNDCTL_DSP_SETFMT: /* _SIOWR('P',5, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_SETFMT %d\n", ival); + if (ival != AFMT_QUERY) { + if (aport->swstate != SW_INITIAL) { + DBGP("SETFMT failed, swstate = %d\n", + aport->swstate); + return -EINVAL; + } + switch (ival) { + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + case AFMT_S16_LE: + if (rport) + rport->sw_samplefmt = ival; + if (wport) + wport->sw_samplefmt = ival; + break; + default: + return -EINVAL; + } + } + ival = aport->sw_samplefmt; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETOSPACE: /* _SIOR ('P',12, audio_buf_info) */ + DBGXV("SNDCTL_DSP_GETOSPACE\n"); + if (!wport) + return -EINVAL; + ival = pcm_setup(devc, rport, wport); + if (ival < 0) + return ival; + ival = swb_inc_u(wport, 0); + buf_info.fragments = ival >> wport->sw_fragshift; + buf_info.fragstotal = wport->sw_fragcount; + buf_info.fragsize = 1 << wport->sw_fragshift; + buf_info.bytes = ival; + DBGXV("SNDCTL_DSP_GETOSPACE returns { %d %d %d %d }\n", + buf_info.fragments, buf_info.fragstotal, + buf_info.fragsize, buf_info.bytes); + return copy_to_user((void *) arg, &buf_info, sizeof buf_info); + + case SNDCTL_DSP_GETISPACE: /* _SIOR ('P',13, audio_buf_info) */ + DBGX("SNDCTL_DSP_GETISPACE\n"); + if (!rport) + return -EINVAL; + ival = pcm_setup(devc, rport, wport); + if (ival < 0) + return ival; + ival = swb_inc_u(rport, 0); + buf_info.fragments = ival >> rport->sw_fragshift; + buf_info.fragstotal = rport->sw_fragcount; + buf_info.fragsize = 1 << rport->sw_fragshift; + buf_info.bytes = ival; + DBGX("SNDCTL_DSP_GETISPACE returns { %d %d %d %d }\n", + buf_info.fragments, buf_info.fragstotal, + buf_info.fragsize, buf_info.bytes); + return copy_to_user((void *) arg, &buf_info, sizeof buf_info); + + case SNDCTL_DSP_NONBLOCK: /* _SIO ('P',14) */ + DBGX("SNDCTL_DSP_NONBLOCK\n"); + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: /* _SIO ('P', 0) */ + DBGX("SNDCTL_DSP_RESET\n"); + /* + * Nothing special needs to be done for input. Input + * samples sit in swbuf, but it will be reinitialized + * to empty when pcm_setup() is called. + */ + if (wport && wport->swbuf) { + wport->swstate = SW_INITIAL; + pcm_output(devc, 0, 0); + pcm_write_sync(devc); + } + pcm_shutdown(devc, rport, wport); + return 0; + + case SNDCTL_DSP_SYNC: /* _SIO ('P', 1) */ + DBGX("SNDCTL_DSP_SYNC\n"); + if (wport) { + pcm_flush_frag(devc); + pcm_write_sync(devc); + } + pcm_shutdown(devc, rport, wport); + return 0; + + case SNDCTL_DSP_POST: /* _SIO ('P', 8) */ + DBGX("SNDCTL_DSP_POST\n"); + if (!wport) + return -EINVAL; + pcm_flush_frag(devc); + return 0; + + case SNDCTL_DSP_GETIPTR: /* _SIOR ('P', 17, count_info) */ + DBGX("SNDCTL_DSP_GETIPTR\n"); + if (!rport) + return -EINVAL; + spin_lock_irqsave(&rport->lock, flags); + { + ustmsc_t ustmsc; + if (rport->hwstate == HW_RUNNING) { + ASSERT(rport->swstate == SW_RUN); + li_read_USTMSC(&rport->chan, &ustmsc); + info.bytes = ustmsc.msc - rport->MSC_offset; + info.bytes *= rport->frame_size; + } else { + info.bytes = rport->byte_count; + } + info.blocks = rport->frag_count; + info.ptr = 0; /* not implemented */ + rport->frag_count = 0; + } + spin_unlock_irqrestore(&rport->lock, flags); + return copy_to_user((void *) arg, &info, sizeof info); + + case SNDCTL_DSP_GETOPTR: /* _SIOR ('P',18, count_info) */ + DBGX("SNDCTL_DSP_GETOPTR\n"); + if (!wport) + return -EINVAL; + spin_lock_irqsave(&wport->lock, flags); + { + ustmsc_t ustmsc; + if (wport->hwstate == HW_RUNNING) { + ASSERT(wport->swstate == SW_RUN); + li_read_USTMSC(&wport->chan, &ustmsc); + info.bytes = ustmsc.msc - wport->MSC_offset; + info.bytes *= wport->frame_size; + } else { + info.bytes = wport->byte_count; + } + info.blocks = wport->frag_count; + info.ptr = 0; /* not implemented */ + wport->frag_count = 0; + } + spin_unlock_irqrestore(&wport->lock, flags); + return copy_to_user((void *) arg, &info, sizeof info); + + case SNDCTL_DSP_GETODELAY: /* _SIOR ('P', 23, int) */ + DBGX("SNDCTL_DSP_GETODELAY\n"); + if (!wport) + return -EINVAL; + spin_lock_irqsave(&wport->lock, flags); + { + int fsize = wport->frame_size; + ival = wport->swb_i_avail / fsize; + if (wport->hwstate == HW_RUNNING) { + int swptr, hwptr, hwframes, hwbytes, hwsize; + int totalhwbytes; + ustmsc_t ustmsc; + + hwsize = wport->hwbuf_size; + swptr = li_read_swptr(&wport->chan); + li_read_USTMSC(&wport->chan, &ustmsc); + hwframes = ustmsc.msc - wport->MSC_offset; + totalhwbytes = hwframes * fsize; + hwptr = totalhwbytes % hwsize; + hwbytes = (swptr - hwptr + hwsize) % hwsize; + ival += hwbytes / fsize; + } + } + spin_unlock_irqrestore(&wport->lock, flags); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_PROFILE: /* _SIOW ('P', 23, int) */ + DBGX("SNDCTL_DSP_PROFILE\n"); + + /* + * Thomas Sailer explains SNDCTL_DSP_PROFILE + * (private email, March 24, 1999): + * + * This gives the sound driver a hint on what it + * should do with partial fragments + * (i.e. fragments partially filled with write). + * This can direct the driver to zero them or + * leave them alone. But don't ask me what this + * is good for, my driver just zeroes the last + * fragment before the receiver stops, no idea + * what good for any other behaviour could + * be. Implementing it as NOP seems safe. + */ + + break; + + case SNDCTL_DSP_GETTRIGGER: /* _SIOR ('P',16, int) */ + DBGX("SNDCTL_DSP_GETTRIGGER\n"); + ival = 0; + if (rport) { + spin_lock_irqsave(&rport->lock, flags); + { + if (!(rport->flags & DISABLED)) + ival |= PCM_ENABLE_INPUT; + } + spin_unlock_irqrestore(&rport->lock, flags); + } + if (wport) { + spin_lock_irqsave(&wport->lock, flags); + { + if (!(wport->flags & DISABLED)) + ival |= PCM_ENABLE_OUTPUT; + } + spin_unlock_irqrestore(&wport->lock, flags); + } + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: /* _SIOW ('P',16, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_SETTRIGGER %d\n", ival); + + /* + * If user is disabling I/O and port is not in initial + * state, fail with EINVAL. + */ + + if (((rport && !(ival & PCM_ENABLE_INPUT)) || + (wport && !(ival & PCM_ENABLE_OUTPUT))) && + aport->swstate != SW_INITIAL) + return -EINVAL; + + if (rport) { + vwsnd_port_hwstate_t hwstate; + spin_lock_irqsave(&rport->lock, flags); + { + hwstate = rport->hwstate; + if (ival & PCM_ENABLE_INPUT) + rport->flags &= ~DISABLED; + else + rport->flags |= DISABLED; + } + spin_unlock_irqrestore(&rport->lock, flags); + if (hwstate != HW_RUNNING && ival & PCM_ENABLE_INPUT) { + + if (rport->swstate == SW_INITIAL) + pcm_setup(devc, rport, wport); + else + li_activate_dma(&rport->chan); + } + } + if (wport) { + vwsnd_port_flags_t pflags; + spin_lock_irqsave(&wport->lock, flags); + { + pflags = wport->flags; + if (ival & PCM_ENABLE_OUTPUT) + wport->flags &= ~DISABLED; + else + wport->flags |= DISABLED; + } + spin_unlock_irqrestore(&wport->lock, flags); + if (pflags & DISABLED && ival & PCM_ENABLE_OUTPUT) { + if (wport->swstate == SW_RUN) + pcm_output(devc, 0, 0); + } + } + return 0; + + default: + DBGP("unknown ioctl 0x%x\n", cmd); + return -EINVAL; + } + DBGP("unimplemented ioctl 0x%x\n", cmd); + return -EINVAL; +} + +static int vwsnd_audio_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + int ret; + + down(&devc->io_sema); + ret = vwsnd_audio_do_ioctl(inode, file, cmd, arg); + up(&devc->io_sema); + return ret; +} + +/* No mmap. */ + +static int vwsnd_audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + DBGE("(file=0x%p, vma=0x%p)\n", file, vma); + return -ENODEV; +} + +/* + * Open the audio device for read and/or write. + * + * Returns 0 on success, -errno on failure. + */ + +static int vwsnd_audio_open(struct inode *inode, struct file *file) +{ + vwsnd_dev_t *devc; + dev_t minor = MINOR(inode->i_rdev); + int sw_samplefmt; + + DBGE("(inode=0x%p, file=0x%p)\n", inode, file); + + INC_USE_COUNT; + for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) + if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F)) + break; + + if (devc == NULL) { + DEC_USE_COUNT; + return -ENODEV; + } + + down(&devc->open_sema); + while (devc->open_mode & file->f_mode) { + up(&devc->open_sema); + if (file->f_flags & O_NONBLOCK) { + DEC_USE_COUNT; + return -EBUSY; + } + interruptible_sleep_on(&devc->open_wait); + if (signal_pending(current)) { + DEC_USE_COUNT; + return -ERESTARTSYS; + } + down(&devc->open_sema); + } + devc->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&devc->open_sema); + + /* get default sample format from minor number. */ + + sw_samplefmt = 0; + if ((minor & 0xF) == SND_DEV_DSP) + sw_samplefmt = AFMT_U8; + else if ((minor & 0xF) == SND_DEV_AUDIO) + sw_samplefmt = AFMT_MU_LAW; + else if ((minor & 0xF) == SND_DEV_DSP16) + sw_samplefmt = AFMT_S16_LE; + else + ASSERT(0); + + /* Initialize vwsnd_ports. */ + + down(&devc->io_sema); + { + if (file->f_mode & FMODE_READ) { + devc->rport.swstate = SW_INITIAL; + devc->rport.flags = 0; + devc->rport.sw_channels = 1; + devc->rport.sw_samplefmt = sw_samplefmt; + devc->rport.sw_framerate = 8000; + devc->rport.sw_fragshift = DEFAULT_FRAGSHIFT; + devc->rport.sw_fragcount = DEFAULT_FRAGCOUNT; + devc->rport.sw_subdivshift = DEFAULT_SUBDIVSHIFT; + devc->rport.byte_count = 0; + devc->rport.frag_count = 0; + } + if (file->f_mode & FMODE_WRITE) { + devc->wport.swstate = SW_INITIAL; + devc->wport.flags = 0; + devc->wport.sw_channels = 1; + devc->wport.sw_samplefmt = sw_samplefmt; + devc->wport.sw_framerate = 8000; + devc->wport.sw_fragshift = DEFAULT_FRAGSHIFT; + devc->wport.sw_fragcount = DEFAULT_FRAGCOUNT; + devc->wport.sw_subdivshift = DEFAULT_SUBDIVSHIFT; + devc->wport.byte_count = 0; + devc->wport.frag_count = 0; + } + } + up(&devc->io_sema); + + file->private_data = devc; + DBGRV(); + return 0; +} + +/* + * Release (close) the audio device. + */ + +static int vwsnd_audio_release(struct inode *inode, struct file *file) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + vwsnd_port_t *wport = NULL, *rport = NULL; + int err = 0; + + lock_kernel(); + down(&devc->io_sema); + { + DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); + + if (file->f_mode & FMODE_READ) + rport = &devc->rport; + if (file->f_mode & FMODE_WRITE) { + wport = &devc->wport; + pcm_flush_frag(devc); + pcm_write_sync(devc); + } + pcm_shutdown(devc, rport, wport); + if (rport) + rport->swstate = SW_OFF; + if (wport) + wport->swstate = SW_OFF; + } + up(&devc->io_sema); + + down(&devc->open_sema); + { + devc->open_mode &= ~file->f_mode; + } + up(&devc->open_sema); + wake_up(&devc->open_wait); + DEC_USE_COUNT; + DBGR(); + unlock_kernel(); + return err; +} + +static struct file_operations vwsnd_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: vwsnd_audio_read, + write: vwsnd_audio_write, + poll: vwsnd_audio_poll, + ioctl: vwsnd_audio_ioctl, + mmap: vwsnd_audio_mmap, + open: vwsnd_audio_open, + release: vwsnd_audio_release, +}; + +/*****************************************************************************/ +/* mixer driver */ + +/* open the mixer device. */ + +static int vwsnd_mixer_open(struct inode *inode, struct file *file) +{ + vwsnd_dev_t *devc; + + DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); + + INC_USE_COUNT; + for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) + if (devc->mixer_minor == MINOR(inode->i_rdev)) + break; + + if (devc == NULL) { + DEC_USE_COUNT; + return -ENODEV; + } + file->private_data = devc; + return 0; +} + +/* release (close) the mixer device. */ + +static int vwsnd_mixer_release(struct inode *inode, struct file *file) +{ + DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); + DEC_USE_COUNT; + return 0; +} + +/* mixer_read_ioctl handles all read ioctls on the mixer device. */ + +static int mixer_read_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg) +{ + int val = -1; + + DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg); + + switch (nr) { + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + case SOUND_MIXER_DEVMASK: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV); + break; + + case SOUND_MIXER_STEREODEVS: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV); + break; + + case SOUND_MIXER_OUTMASK: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD); + break; + + case SOUND_MIXER_RECMASK: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD); + break; + + case SOUND_MIXER_PCM: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_PCM); + break; + + case SOUND_MIXER_LINE: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_LINE); + break; + + case SOUND_MIXER_MIC: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_MIC); + break; + + case SOUND_MIXER_CD: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_CD); + break; + + case SOUND_MIXER_RECLEV: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_RECLEV); + break; + + case SOUND_MIXER_RECSRC: + val = ad1843_get_recsrc(&devc->lith); + break; + + case SOUND_MIXER_OUTSRC: + val = ad1843_get_outsrc(&devc->lith); + break; + + default: + return -EINVAL; + } + return put_user(val, (int *) arg); +} + +/* mixer_write_ioctl handles all write ioctls on the mixer device. */ + +static int mixer_write_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg) +{ + int val; + int err; + + DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg); + + err = get_user(val, (int *) arg); + if (err) + return -EFAULT; + switch (nr) { + case SOUND_MIXER_PCM: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_PCM, val); + break; + + case SOUND_MIXER_LINE: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_LINE, val); + break; + + case SOUND_MIXER_MIC: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_MIC, val); + break; + + case SOUND_MIXER_CD: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_CD, val); + break; + + case SOUND_MIXER_RECLEV: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_RECLEV, val); + break; + + case SOUND_MIXER_RECSRC: + if (devc->rport.swbuf || devc->wport.swbuf) + return -EBUSY; /* can't change recsrc while running */ + val = ad1843_set_recsrc(&devc->lith, val); + break; + + case SOUND_MIXER_OUTSRC: + val = ad1843_set_outsrc(&devc->lith, val); + break; + + default: + return -EINVAL; + } + if (val < 0) + return val; + return put_user(val, (int *) arg); +} + +/* This is the ioctl entry to the mixer driver. */ + +static int vwsnd_mixer_ioctl(struct inode *ioctl, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + const unsigned int nrmask = _IOC_NRMASK << _IOC_NRSHIFT; + const unsigned int nr = (cmd & nrmask) >> _IOC_NRSHIFT; + int retval; + + DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg); + + down(&devc->mix_sema); + { + if ((cmd & ~nrmask) == MIXER_READ(0)) + retval = mixer_read_ioctl(devc, nr, (caddr_t) arg); + else if ((cmd & ~nrmask) == MIXER_WRITE(0)) + retval = mixer_write_ioctl(devc, nr, (caddr_t) arg); + else + retval = -EINVAL; + } + up(&devc->mix_sema); + return retval; +} + +static struct file_operations vwsnd_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: vwsnd_mixer_ioctl, + open: vwsnd_mixer_open, + release: vwsnd_mixer_release, +}; + +/*****************************************************************************/ +/* probe/attach/unload */ + +/* driver probe routine. Return nonzero if hardware is found. */ + +static int __init probe_vwsnd(struct address_info *hw_config) +{ + lithium_t lith; + int w; + unsigned long later; + + DBGEV("(hw_config=0x%p)\n", hw_config); + + /* XXX verify lithium present (to prevent crash on non-vw) */ + + if (li_create(&lith, hw_config->io_base) != 0) { + printk(KERN_WARNING "probe_vwsnd: can't map lithium\n"); + return 0; + } + later = jiffies + 2; + li_writel(&lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE); + do { + w = li_readl(&lith, LI_HOST_CONTROLLER); + } while (w == LI_HC_LINK_ENABLE && jiffies < later); + + li_destroy(&lith); + + DBGPV("HC = 0x%04x\n", w); + + if ((w == LI_HC_LINK_ENABLE) || (w & LI_HC_LINK_CODEC)) { + + /* This may indicate a beta machine with no audio, + * or a future machine with different audio. + * On beta-release 320 w/ no audio, HC == 0x4000 */ + + printk(KERN_WARNING "probe_vwsnd: audio codec not found\n"); + return 0; + } + + if (w & LI_HC_LINK_FAILURE) { + printk(KERN_WARNING "probe_vwsnd: can't init audio codec\n"); + return 0; + } + + printk(KERN_INFO "probe_vwsnd: lithium audio found\n"); + + return 1; +} + +/* + * driver attach routine. Initialize driver data structures and + * initialize hardware. A new vwsnd_dev_t is allocated and put + * onto the global list, vwsnd_dev_list. + * + * Return +minor_dev on success, -errno on failure. + */ + +static int __init attach_vwsnd(struct address_info *hw_config) +{ + vwsnd_dev_t *devc = NULL; + int err = -ENOMEM; + + DBGEV("(hw_config=0x%p)\n", hw_config); + + devc = kmalloc(sizeof (vwsnd_dev_t), GFP_KERNEL); + if (devc == NULL) + goto fail0; + + err = li_create(&devc->lith, hw_config->io_base); + if (err) + goto fail1; + + init_waitqueue(&devc->open_wait); + + devc->rport.hwbuf_size = HWBUF_SIZE; + devc->rport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER); + if (!devc->rport.hwbuf_vaddr) + goto fail2; + devc->rport.hwbuf = (caddr_t) devc->rport.hwbuf_vaddr; + devc->rport.hwbuf_paddr = virt_to_phys(devc->rport.hwbuf); + + /* + * Quote from the NT driver: + * + * // WARNING!!! HACK to setup output dma!!! + * // This is required because even on output there is some data + * // trickling into the input DMA channel. This is a bug in the + * // Lithium microcode. + * // --sde + * + * We set the input side's DMA base address here. It will remain + * valid until the driver is unloaded. + */ + + li_writel(&devc->lith, LI_COMM1_BASE, + devc->rport.hwbuf_paddr >> 8 | 1 << (37 - 8)); + + devc->wport.hwbuf_size = HWBUF_SIZE; + devc->wport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER); + if (!devc->wport.hwbuf_vaddr) + goto fail3; + devc->wport.hwbuf = (caddr_t) devc->wport.hwbuf_vaddr; + devc->wport.hwbuf_paddr = virt_to_phys(devc->wport.hwbuf); + DBGP("wport hwbuf = 0x%p\n", devc->wport.hwbuf); + + DBGDO(shut_up++); + err = ad1843_init(&devc->lith); + DBGDO(shut_up--); + if (err) + goto fail4; + + /* install interrupt handler */ + + err = request_irq(hw_config->irq, vwsnd_audio_intr, 0, "vwsnd", devc); + if (err) + goto fail5; + + /* register this device's drivers. */ + + devc->audio_minor = register_sound_dsp(&vwsnd_audio_fops, -1); + if ((err = devc->audio_minor) < 0) { + DBGDO(printk(KERN_WARNING + "attach_vwsnd: register_sound_dsp error %d\n", + err)); + goto fail6; + } + devc->mixer_minor = register_sound_mixer(&vwsnd_mixer_fops, + devc->audio_minor >> 4); + if ((err = devc->mixer_minor) < 0) { + DBGDO(printk(KERN_WARNING + "attach_vwsnd: register_sound_mixer error %d\n", + err)); + goto fail7; + } + + /* Squirrel away device indices for unload routine. */ + + hw_config->slots[0] = devc->audio_minor; + + /* Initialize as much of *devc as possible */ + + devc->open_sema = MUTEX; + devc->io_sema = MUTEX; + devc->mix_sema = MUTEX; + devc->open_mode = 0; + devc->rport.lock = SPIN_LOCK_UNLOCKED; + init_waitqueue(&devc->rport.queue); + devc->rport.swstate = SW_OFF; + devc->rport.hwstate = HW_STOPPED; + devc->rport.flags = 0; + devc->rport.swbuf = NULL; + devc->wport.lock = SPIN_LOCK_UNLOCKED; + init_waitqueue(&devc->wport.queue); + devc->wport.swstate = SW_OFF; + devc->wport.hwstate = HW_STOPPED; + devc->wport.flags = 0; + devc->wport.swbuf = NULL; + + /* Success. Link us onto the local device list. */ + + devc->next_dev = vwsnd_dev_list; + vwsnd_dev_list = devc; + return devc->audio_minor; + + /* So many ways to fail. Undo what we did. */ + + fail7: + unregister_sound_dsp(devc->audio_minor); + fail6: + free_irq(hw_config->irq, devc); + fail5: + fail4: + free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER); + fail3: + free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER); + fail2: + li_destroy(&devc->lith); + fail1: + kfree(devc); + fail0: + return err; +} + +static int __exit unload_vwsnd(struct address_info *hw_config) +{ + vwsnd_dev_t *devc, **devcp; + + DBGE("()\n"); + + devcp = &vwsnd_dev_list; + while ((devc = *devcp)) { + if (devc->audio_minor == hw_config->slots[0]) { + *devcp = devc->next_dev; + break; + } + devcp = &devc->next_dev; + } + + if (!devc) + return -ENODEV; + + unregister_sound_mixer(devc->mixer_minor); + unregister_sound_dsp(devc->audio_minor); + free_irq(hw_config->irq, devc); + free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER); + free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER); + li_destroy(&devc->lith); + kfree(devc); + + return 0; +} + +/*****************************************************************************/ +/* initialization and loadable kernel module interface */ + +static struct address_info the_hw_config = { + 0xFF001000, /* lithium phys addr */ + CO_IRQ(CO_APIC_LI_AUDIO) /* irq */ +}; + +MODULE_DESCRIPTION("SGI Visual Workstation sound module"); +MODULE_AUTHOR("Bob Miller "); +MODULE_LICENSE("GPL"); + +static int __init init_vwsnd(void) +{ + int err; + + DBGXV("\n"); + DBGXV("sound::vwsnd::init_module()\n"); + + if(!probe_vwsnd(&the_hw_config)) + return -ENODEV; + err = attach_vwsnd(&the_hw_config); + if (err < 0) + return err; + return 0; +} + +static void __exit cleanup_vwsnd(void) +{ + DBGX("sound::vwsnd::cleanup_module()\n"); + + unload_vwsnd(&the_hw_config); +} + +module_init(init_vwsnd); +module_exit(cleanup_vwsnd); diff -Nru a/sound/oss/waveartist.c b/sound/oss/waveartist.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/waveartist.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,2033 @@ +/* + * linux/drivers/sound/waveartist.c + * + * The low level driver for the RWA010 Rockwell Wave Artist + * codec chip used in the Rebel.com NetWinder. + * + * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk) + * and Pat Beirne (patb@corel.ca) + * + * + * Copyright (C) by Rebel.com 1998-1999 + * + * RWA010 specs received under NDA from Rockwell + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added __init to waveartist_init() + */ + +/* Debugging */ +#define DEBUG_CMD 1 +#define DEBUG_OUT 2 +#define DEBUG_IN 4 +#define DEBUG_INTR 8 +#define DEBUG_MIXER 16 +#define DEBUG_TRIGGER 32 + +#define debug_flg (0) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sound_config.h" +#include "waveartist.h" + +#ifdef CONFIG_ARM +#include +#include +#endif + +#ifndef NO_DMA +#define NO_DMA 255 +#endif + +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\ + SOUND_MASK_PCM |\ + SOUND_MASK_LINE |\ + SOUND_MASK_MIC |\ + SOUND_MASK_LINE1 |\ + SOUND_MASK_RECLEV |\ + SOUND_MASK_VOLUME |\ + SOUND_MASK_IMIX) + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = { + 0x5555, /* Master Volume */ + 0x0000, /* Bass */ + 0x0000, /* Treble */ + 0x2323, /* Synth (FM) */ + 0x4b4b, /* PCM */ + 0x6464, /* PC Speaker */ + 0x0000, /* Ext Line */ + 0x0000, /* Mic */ + 0x0000, /* CD */ + 0x6464, /* Recording monitor */ + 0x0000, /* SB PCM (ALT PCM) */ + 0x0000, /* Recording level */ + 0x6464, /* Input gain */ + 0x6464, /* Output gain */ + 0x0000, /* Line1 (Aux1) */ + 0x0000, /* Line2 (Aux2) */ + 0x0000, /* Line3 (Aux3) */ + 0x0000, /* Digital1 */ + 0x0000, /* Digital2 */ + 0x0000, /* Digital3 */ + 0x0000, /* Phone In */ + 0x6464, /* Phone Out */ + 0x0000, /* Video */ + 0x0000, /* Radio */ + 0x0000 /* Monitor */ +}; + +typedef struct { + struct address_info hw; /* hardware */ + char *chip_name; + + int xfer_count; + int audio_mode; + int open_mode; + int audio_flags; + int record_dev; + int playback_dev; + int dev_no; + + /* Mixer parameters */ + const struct waveartist_mixer_info *mix; + + unsigned short *levels; /* cache of volume settings */ + int recmask; /* currently enabled recording device! */ + +#ifdef CONFIG_ARCH_NETWINDER + signed int slider_vol; /* hardware slider volume */ + unsigned int handset_detect :1; + unsigned int telephone_detect:1; + unsigned int no_autoselect :1;/* handset/telephone autoselects a path */ + unsigned int spkr_mute_state :1;/* set by ioctl or autoselect */ + unsigned int line_mute_state :1;/* set by ioctl or autoselect */ + unsigned int use_slider :1;/* use slider setting for o/p vol */ +#endif +} wavnc_info; + +/* + * This is the implementation specific mixer information. + */ +struct waveartist_mixer_info { + unsigned int supported_devs; /* Supported devices */ + unsigned int recording_devs; /* Recordable devies */ + unsigned int stereo_devs; /* Stereo devices */ + + unsigned int (*select_input)(wavnc_info *, unsigned int, + unsigned char *, unsigned char *); + int (*decode_mixer)(wavnc_info *, int, + unsigned char, unsigned char); + int (*get_mixer)(wavnc_info *, int); +}; + +typedef struct wavnc_port_info { + int open_mode; + int speed; + int channels; + int audio_format; +} wavnc_port_info; + +static int nr_waveartist_devs; +static wavnc_info adev_info[MAX_AUDIO_DEV]; +static spinlock_t waveartist_lock = SPIN_LOCK_UNLOCKED; + +#ifndef CONFIG_ARCH_NETWINDER +#define machine_is_netwinder() 0 +#else +static struct timer_list vnc_timer; +static void vnc_configure_mixer(wavnc_info *devc, unsigned int input_mask); +static int vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg); +static void vnc_slider_tick(unsigned long data); +#endif + +static inline void +waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set) +{ + unsigned int ctlr_port = hw->io_base + CTLR; + + clear = ~clear & inb(ctlr_port); + + outb(clear | set, ctlr_port); +} + +/* Toggle IRQ acknowledge line + */ +static inline void +waveartist_iack(wavnc_info *devc) +{ + unsigned int ctlr_port = devc->hw.io_base + CTLR; + int old_ctlr; + + old_ctlr = inb(ctlr_port) & ~IRQ_ACK; + + outb(old_ctlr | IRQ_ACK, ctlr_port); + outb(old_ctlr, ctlr_port); +} + +static inline int +waveartist_sleep(int timeout_ms) +{ + unsigned int timeout = timeout_ms * 10 * HZ / 100; + + do { + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(timeout); + } while (timeout); + + return 0; +} + +static int +waveartist_reset(wavnc_info *devc) +{ + struct address_info *hw = &devc->hw; + unsigned int timeout, res = -1; + + waveartist_set_ctlr(hw, -1, RESET); + waveartist_sleep(2); + waveartist_set_ctlr(hw, RESET, 0); + + timeout = 500; + do { + mdelay(2); + + if (inb(hw->io_base + STATR) & CMD_RF) { + res = inw(hw->io_base + CMDR); + if (res == 0x55aa) + break; + } + } while (--timeout); + + if (timeout == 0) { + printk(KERN_WARNING "WaveArtist: reset timeout "); + if (res != (unsigned int)-1) + printk("(res=%04X)", res); + printk("\n"); + return 1; + } + return 0; +} + +/* Helper function to send and receive words + * from WaveArtist. It handles all the handshaking + * and can send or receive multiple words. + */ +static int +waveartist_cmd(wavnc_info *devc, + int nr_cmd, unsigned int *cmd, + int nr_resp, unsigned int *resp) +{ + unsigned int io_base = devc->hw.io_base; + unsigned int timed_out = 0; + unsigned int i; + + if (debug_flg & DEBUG_CMD) { + printk("waveartist_cmd: cmd="); + + for (i = 0; i < nr_cmd; i++) + printk("%04X ", cmd[i]); + + printk("\n"); + } + + if (inb(io_base + STATR) & CMD_RF) { + int old_data; + + /* flush the port + */ + + old_data = inw(io_base + CMDR); + + if (debug_flg & DEBUG_CMD) + printk("flushed %04X...", old_data); + + udelay(10); + } + + for (i = 0; !timed_out && i < nr_cmd; i++) { + int count; + + for (count = 5000; count; count--) + if (inb(io_base + STATR) & CMD_WE) + break; + + if (!count) + timed_out = 1; + else + outw(cmd[i], io_base + CMDR); + } + + for (i = 0; !timed_out && i < nr_resp; i++) { + int count; + + for (count = 5000; count; count--) + if (inb(io_base + STATR) & CMD_RF) + break; + + if (!count) + timed_out = 1; + else + resp[i] = inw(io_base + CMDR); + } + + if (debug_flg & DEBUG_CMD) { + if (!timed_out) { + printk("waveartist_cmd: resp="); + + for (i = 0; i < nr_resp; i++) + printk("%04X ", resp[i]); + + printk("\n"); + } else + printk("waveartist_cmd: timed out\n"); + } + + return timed_out ? 1 : 0; +} + +/* + * Send one command word + */ +static inline int +waveartist_cmd1(wavnc_info *devc, unsigned int cmd) +{ + return waveartist_cmd(devc, 1, &cmd, 0, NULL); +} + +/* + * Send one command, receive one word + */ +static inline unsigned int +waveartist_cmd1_r(wavnc_info *devc, unsigned int cmd) +{ + unsigned int ret; + + waveartist_cmd(devc, 1, &cmd, 1, &ret); + + return ret; +} + +/* + * Send a double command, receive one + * word (and throw it away) + */ +static inline int +waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg) +{ + unsigned int vals[2]; + + vals[0] = cmd; + vals[1] = arg; + + return waveartist_cmd(devc, 2, vals, 1, vals); +} + +/* + * Send a triple command + */ +static inline int +waveartist_cmd3(wavnc_info *devc, unsigned int cmd, + unsigned int arg1, unsigned int arg2) +{ + unsigned int vals[3]; + + vals[0] = cmd; + vals[1] = arg1; + vals[2] = arg2; + + return waveartist_cmd(devc, 3, vals, 0, NULL); +} + +static int +waveartist_getrev(wavnc_info *devc, char *rev) +{ + unsigned int temp[2]; + unsigned int cmd = WACMD_GETREV; + + waveartist_cmd(devc, 1, &cmd, 2, temp); + + rev[0] = temp[0] >> 8; + rev[1] = temp[0] & 255; + rev[2] = '\0'; + + return temp[0]; +} + +static void waveartist_halt_output(int dev); +static void waveartist_halt_input(int dev); +static void waveartist_halt(int dev); +static void waveartist_trigger(int dev, int state); + +static int +waveartist_open(int dev, int mode) +{ + wavnc_info *devc; + wavnc_port_info *portc; + unsigned long flags; + + if (dev < 0 || dev >= num_audiodevs) + return -ENXIO; + + devc = (wavnc_info *) audio_devs[dev]->devc; + portc = (wavnc_port_info *) audio_devs[dev]->portc; + + spin_lock_irqsave(&waveartist_lock, flags); + if (portc->open_mode || (devc->open_mode & mode)) { + spin_unlock_irqrestore(&waveartist_lock, flags); + return -EBUSY; + } + + devc->audio_mode = 0; + devc->open_mode |= mode; + portc->open_mode = mode; + waveartist_trigger(dev, 0); + + if (mode & OPEN_READ) + devc->record_dev = dev; + if (mode & OPEN_WRITE) + devc->playback_dev = dev; + spin_unlock_irqrestore(&waveartist_lock, flags); + + return 0; +} + +static void +waveartist_close(int dev) +{ + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + unsigned long flags; + + spin_lock_irqsave(&waveartist_lock, flags); + + waveartist_halt(dev); + + devc->audio_mode = 0; + devc->open_mode &= ~portc->open_mode; + portc->open_mode = 0; + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static void +waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + unsigned long flags; + unsigned int count = __count; + + if (debug_flg & DEBUG_OUT) + printk("waveartist: output block, buf=0x%lx, count=0x%x...\n", + buf, count); + /* + * 16 bit data + */ + if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) + count >>= 1; + + if (portc->channels > 1) + count >>= 1; + + count -= 1; + + if (devc->audio_mode & PCM_ENABLE_OUTPUT && + audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + count == devc->xfer_count) { + devc->audio_mode |= PCM_ENABLE_OUTPUT; + return; /* + * Auto DMA mode on. No need to react + */ + } + + spin_lock_irqsave(&waveartist_lock, flags); + + /* + * set sample count + */ + waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count); + + devc->xfer_count = count; + devc->audio_mode |= PCM_ENABLE_OUTPUT; + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static void +waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + unsigned long flags; + unsigned int count = __count; + + if (debug_flg & DEBUG_IN) + printk("waveartist: start input, buf=0x%lx, count=0x%x...\n", + buf, count); + + if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ + count >>= 1; + + if (portc->channels > 1) + count >>= 1; + + count -= 1; + + if (devc->audio_mode & PCM_ENABLE_INPUT && + audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + count == devc->xfer_count) { + devc->audio_mode |= PCM_ENABLE_INPUT; + return; /* + * Auto DMA mode on. No need to react + */ + } + + spin_lock_irqsave(&waveartist_lock, flags); + + /* + * set sample count + */ + waveartist_cmd2(devc, WACMD_INPUTSIZE, count); + + devc->xfer_count = count; + devc->audio_mode |= PCM_ENABLE_INPUT; + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static int +waveartist_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + return -EINVAL; +} + +static unsigned int +waveartist_get_speed(wavnc_port_info *portc) +{ + unsigned int speed; + + /* + * program the speed, channels, bits + */ + if (portc->speed == 8000) + speed = 0x2E71; + else if (portc->speed == 11025) + speed = 0x4000; + else if (portc->speed == 22050) + speed = 0x8000; + else if (portc->speed == 44100) + speed = 0x0; + else { + /* + * non-standard - just calculate + */ + speed = portc->speed << 16; + + speed = (speed / 44100) & 65535; + } + + return speed; +} + +static unsigned int +waveartist_get_bits(wavnc_port_info *portc) +{ + unsigned int bits; + + if (portc->audio_format == AFMT_S16_LE) + bits = 1; + else if (portc->audio_format == AFMT_S8) + bits = 0; + else + bits = 2; //default AFMT_U8 + + return bits; +} + +static int +waveartist_prepare_for_input(int dev, int bsize, int bcount) +{ + unsigned long flags; + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + unsigned int speed, bits; + + if (devc->audio_mode) + return 0; + + speed = waveartist_get_speed(portc); + bits = waveartist_get_bits(portc); + + spin_lock_irqsave(&waveartist_lock, flags); + + if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) + printk(KERN_WARNING "waveartist: error setting the " + "record format to %d\n", portc->audio_format); + + if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels)) + printk(KERN_WARNING "waveartist: error setting record " + "to %d channels\n", portc->channels); + + /* + * write cmd SetSampleSpeedTimeConstant + */ + if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed)) + printk(KERN_WARNING "waveartist: error setting the record " + "speed to %dHz.\n", portc->speed); + + if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1)) + printk(KERN_WARNING "waveartist: error setting the record " + "data path to 0x%X\n", 1); + + if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) + printk(KERN_WARNING "waveartist: error setting the record " + "format to %d\n", portc->audio_format); + + devc->xfer_count = 0; + spin_unlock_irqrestore(&waveartist_lock, flags); + waveartist_halt_input(dev); + + if (debug_flg & DEBUG_INTR) { + printk("WA CTLR reg: 0x%02X.\n", + inb(devc->hw.io_base + CTLR)); + printk("WA STAT reg: 0x%02X.\n", + inb(devc->hw.io_base + STATR)); + printk("WA IRQS reg: 0x%02X.\n", + inb(devc->hw.io_base + IRQSTAT)); + } + + return 0; +} + +static int +waveartist_prepare_for_output(int dev, int bsize, int bcount) +{ + unsigned long flags; + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + unsigned int speed, bits; + + /* + * program the speed, channels, bits + */ + speed = waveartist_get_speed(portc); + bits = waveartist_get_bits(portc); + + spin_lock_irqsave(&waveartist_lock, flags); + + if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) && + waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed)) + printk(KERN_WARNING "waveartist: error setting the playback " + "speed to %dHz.\n", portc->speed); + + if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels)) + printk(KERN_WARNING "waveartist: error setting the playback " + "to %d channels\n", portc->channels); + + if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0)) + printk(KERN_WARNING "waveartist: error setting the playback " + "data path to 0x%X\n", 0); + + if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits)) + printk(KERN_WARNING "waveartist: error setting the playback " + "format to %d\n", portc->audio_format); + + devc->xfer_count = 0; + spin_unlock_irqrestore(&waveartist_lock, flags); + waveartist_halt_output(dev); + + if (debug_flg & DEBUG_INTR) { + printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR)); + printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR)); + printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT)); + } + + return 0; +} + +static void +waveartist_halt(int dev) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + wavnc_info *devc; + + if (portc->open_mode & OPEN_WRITE) + waveartist_halt_output(dev); + + if (portc->open_mode & OPEN_READ) + waveartist_halt_input(dev); + + devc = (wavnc_info *) audio_devs[dev]->devc; + devc->audio_mode = 0; +} + +static void +waveartist_halt_input(int dev) +{ + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + unsigned long flags; + + spin_lock_irqsave(&waveartist_lock, flags); + + /* + * Stop capture + */ + waveartist_cmd1(devc, WACMD_INPUTSTOP); + + devc->audio_mode &= ~PCM_ENABLE_INPUT; + + /* + * Clear interrupt by toggling + * the IRQ_ACK bit in CTRL + */ + if (inb(devc->hw.io_base + STATR) & IRQ_REQ) + waveartist_iack(devc); + +// devc->audio_mode &= ~PCM_ENABLE_INPUT; + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static void +waveartist_halt_output(int dev) +{ + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + unsigned long flags; + + spin_lock_irqsave(&waveartist_lock, flags); + + waveartist_cmd1(devc, WACMD_OUTPUTSTOP); + + devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + + /* + * Clear interrupt by toggling + * the IRQ_ACK bit in CTRL + */ + if (inb(devc->hw.io_base + STATR) & IRQ_REQ) + waveartist_iack(devc); + +// devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static void +waveartist_trigger(int dev, int state) +{ + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + unsigned long flags; + + if (debug_flg & DEBUG_TRIGGER) { + printk("wavnc: audio trigger "); + if (state & PCM_ENABLE_INPUT) + printk("in "); + if (state & PCM_ENABLE_OUTPUT) + printk("out"); + printk("\n"); + } + + spin_lock_irqsave(&waveartist_lock, flags); + + state &= devc->audio_mode; + + if (portc->open_mode & OPEN_READ && + state & PCM_ENABLE_INPUT) + /* + * enable ADC Data Transfer to PC + */ + waveartist_cmd1(devc, WACMD_INPUTSTART); + + if (portc->open_mode & OPEN_WRITE && + state & PCM_ENABLE_OUTPUT) + /* + * enable DAC data transfer from PC + */ + waveartist_cmd1(devc, WACMD_OUTPUTSTART); + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static int +waveartist_set_speed(int dev, int arg) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + + if (arg <= 0) + return portc->speed; + + if (arg < 5000) + arg = 5000; + if (arg > 44100) + arg = 44100; + + portc->speed = arg; + return portc->speed; + +} + +static short +waveartist_set_channels(int dev, short arg) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + + if (arg != 1 && arg != 2) + return portc->channels; + + portc->channels = arg; + return arg; +} + +static unsigned int +waveartist_set_bits(int dev, unsigned int arg) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + + if (arg == 0) + return portc->audio_format; + + if ((arg != AFMT_U8) && (arg != AFMT_S16_LE) && (arg != AFMT_S8)) + arg = AFMT_U8; + + portc->audio_format = arg; + + return arg; +} + +static struct audio_driver waveartist_audio_driver = { + owner: THIS_MODULE, + open: waveartist_open, + close: waveartist_close, + output_block: waveartist_output_block, + start_input: waveartist_start_input, + ioctl: waveartist_ioctl, + prepare_for_input: waveartist_prepare_for_input, + prepare_for_output: waveartist_prepare_for_output, + halt_io: waveartist_halt, + halt_input: waveartist_halt_input, + halt_output: waveartist_halt_output, + trigger: waveartist_trigger, + set_speed: waveartist_set_speed, + set_bits: waveartist_set_bits, + set_channels: waveartist_set_channels +}; + + +static void +waveartist_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + wavnc_info *devc = (wavnc_info *)dev_id; + int irqstatus, status; + + irqstatus = inb(devc->hw.io_base + IRQSTAT); + status = inb(devc->hw.io_base + STATR); + + if (debug_flg & DEBUG_INTR) + printk("waveartist_intr: stat=%02x, irqstat=%02x\n", + status, irqstatus); + + if (status & IRQ_REQ) /* Clear interrupt */ + waveartist_iack(devc); + else + printk(KERN_WARNING "waveartist: unexpected interrupt\n"); + + if (irqstatus & 0x01) { + int temp = 1; + + /* PCM buffer done + */ + if ((status & DMA0) && (devc->audio_mode & PCM_ENABLE_OUTPUT)) { + DMAbuf_outputintr(devc->playback_dev, 1); + temp = 0; + } + if ((status & DMA1) && (devc->audio_mode & PCM_ENABLE_INPUT)) { + DMAbuf_inputintr(devc->record_dev); + temp = 0; + } + if (temp) //default: + printk(KERN_WARNING "waveartist: Unknown interrupt\n"); + } + if (irqstatus & 0x2) + // We do not use SB mode natively... + printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n"); +} + +/* ------------------------------------------------------------------------- + * Mixer stuff + */ +struct mix_ent { + unsigned char reg_l; + unsigned char reg_r; + unsigned char shift; + unsigned char max; +}; + +static const struct mix_ent mix_devs[SOUND_MIXER_NRDEVICES] = { + { 2, 6, 1, 7 }, /* SOUND_MIXER_VOLUME */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_BASS */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_TREBLE */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_SYNTH */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_PCM */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_SPEAKER */ + { 0, 4, 6, 31 }, /* SOUND_MIXER_LINE */ + { 2, 6, 4, 3 }, /* SOUND_MIXER_MIC */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_CD */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_IMIX */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_ALTPCM */ +#if 0 + { 3, 7, 0, 10 }, /* SOUND_MIXER_RECLEV */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_IGAIN */ +#else + { 0, 0, 0, 0 }, /* SOUND_MIXER_RECLEV */ + { 3, 7, 0, 7 }, /* SOUND_MIXER_IGAIN */ +#endif + { 0, 0, 0, 0 }, /* SOUND_MIXER_OGAIN */ + { 0, 4, 1, 31 }, /* SOUND_MIXER_LINE1 */ + { 1, 5, 6, 31 }, /* SOUND_MIXER_LINE2 */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_LINE3 */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL1 */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL2 */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL3 */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEIN */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEOUT */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_VIDEO */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_RADIO */ + { 0, 0, 0, 0 } /* SOUND_MIXER_MONITOR */ +}; + +static void +waveartist_mixer_update(wavnc_info *devc, int whichDev) +{ + unsigned int lev_left, lev_right; + + lev_left = devc->levels[whichDev] & 0xff; + lev_right = devc->levels[whichDev] >> 8; + + if (lev_left > 100) + lev_left = 100; + if (lev_right > 100) + lev_right = 100; + +#define SCALE(lev,max) ((lev) * (max) / 100) + + if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT) + whichDev = SOUND_MIXER_VOLUME; + + if (mix_devs[whichDev].reg_l || mix_devs[whichDev].reg_r) { + const struct mix_ent *mix = mix_devs + whichDev; + unsigned int mask, left, right; + + mask = mix->max << mix->shift; + lev_left = SCALE(lev_left, mix->max) << mix->shift; + lev_right = SCALE(lev_right, mix->max) << mix->shift; + + /* read left setting */ + left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | + mix->reg_l << 8); + + /* read right setting */ + right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | + mix->reg_r << 8); + + left = (left & ~mask) | (lev_left & mask); + right = (right & ~mask) | (lev_right & mask); + + /* write left,right back */ + waveartist_cmd3(devc, WACMD_SET_MIXER, left, right); + } else { + switch(whichDev) { + case SOUND_MIXER_PCM: + waveartist_cmd3(devc, WACMD_SET_LEVEL, + SCALE(lev_left, 32767), + SCALE(lev_right, 32767)); + break; + + case SOUND_MIXER_SYNTH: + waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL, + SCALE(lev_left, 32767), + SCALE(lev_right, 32767)); + break; + } + } +} + +/* + * Set the ADC MUX to the specified values. We do NOT do any + * checking of the values passed, since we assume that the + * relevant *_select_input function has done that for us. + */ +static void +waveartist_set_adc_mux(wavnc_info *devc, char left_dev, char right_dev) +{ + unsigned int reg_08, reg_09; + + reg_08 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0800); + reg_09 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0900); + + reg_08 = (reg_08 & ~0x3f) | right_dev << 3 | left_dev; + + waveartist_cmd3(devc, WACMD_SET_MIXER, reg_08, reg_09); +} + +/* + * Decode a recording mask into a mixer selection as follows: + * + * OSS Source WA Source Actual source + * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848) + * SOUND_MASK_LINE Line Line in + * SOUND_MASK_LINE1 Aux 1 Aux 1 in + * SOUND_MASK_LINE2 Aux 2 Aux 2 in + * SOUND_MASK_MIC Mic Microphone + */ +static unsigned int +waveartist_select_input(wavnc_info *devc, unsigned int recmask, + unsigned char *dev_l, unsigned char *dev_r) +{ + unsigned int recdev = ADC_MUX_NONE; + + if (recmask & SOUND_MASK_IMIX) { + recmask = SOUND_MASK_IMIX; + recdev = ADC_MUX_MIXER; + } else if (recmask & SOUND_MASK_LINE2) { + recmask = SOUND_MASK_LINE2; + recdev = ADC_MUX_AUX2; + } else if (recmask & SOUND_MASK_LINE1) { + recmask = SOUND_MASK_LINE1; + recdev = ADC_MUX_AUX1; + } else if (recmask & SOUND_MASK_LINE) { + recmask = SOUND_MASK_LINE; + recdev = ADC_MUX_LINE; + } else if (recmask & SOUND_MASK_MIC) { + recmask = SOUND_MASK_MIC; + recdev = ADC_MUX_MIC; + } + + *dev_l = *dev_r = recdev; + + return recmask; +} + +static int +waveartist_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l, + unsigned char lev_r) +{ + switch (dev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_SYNTH: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: + case SOUND_MIXER_MIC: + case SOUND_MIXER_IGAIN: + case SOUND_MIXER_LINE1: + case SOUND_MIXER_LINE2: + devc->levels[dev] = lev_l | lev_r << 8; + break; + + case SOUND_MIXER_IMIX: + break; + + default: + dev = -EINVAL; + break; + } + + return dev; +} + +static int waveartist_get_mixer(wavnc_info *devc, int dev) +{ + return devc->levels[dev]; +} + +static const struct waveartist_mixer_info waveartist_mixer = { + supported_devs: SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN, + recording_devs: SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | + SOUND_MASK_IMIX, + stereo_devs: (SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN) & ~ + (SOUND_MASK_SPEAKER | SOUND_MASK_IMIX), + select_input: waveartist_select_input, + decode_mixer: waveartist_decode_mixer, + get_mixer: waveartist_get_mixer, +}; + +static void +waveartist_set_recmask(wavnc_info *devc, unsigned int recmask) +{ + unsigned char dev_l, dev_r; + + recmask &= devc->mix->recording_devs; + + /* + * If more than one recording device selected, + * disable the device that is currently in use. + */ + if (hweight32(recmask) > 1) + recmask &= ~devc->recmask; + + /* + * Translate the recording device mask into + * the ADC multiplexer settings. + */ + devc->recmask = devc->mix->select_input(devc, recmask, + &dev_l, &dev_r); + + waveartist_set_adc_mux(devc, dev_l, dev_r); +} + +static int +waveartist_set_mixer(wavnc_info *devc, int dev, unsigned int level) +{ + unsigned int lev_left = level & 0x00ff; + unsigned int lev_right = (level & 0xff00) >> 8; + + if (lev_left > 100) + lev_left = 100; + if (lev_right > 100) + lev_right = 100; + + /* + * Mono devices have their right volume forced to their + * left volume. (from ALSA driver OSS emulation). + */ + if (!(devc->mix->stereo_devs & (1 << dev))) + lev_right = lev_left; + + dev = devc->mix->decode_mixer(devc, dev, lev_left, lev_right); + + if (dev >= 0) + waveartist_mixer_update(devc, dev); + + return dev < 0 ? dev : 0; +} + +static int +waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; + int ret = 0, val, nr; + + /* + * All SOUND_MIXER_* ioctls use type 'M' + */ + if (((cmd >> 8) & 255) != 'M') + return -ENOIOCTLCMD; + +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) { + ret = vnc_private_ioctl(dev, cmd, arg); + if (ret != -ENOIOCTLCMD) + return ret; + else + ret = 0; + } +#endif + + nr = cmd & 0xff; + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (nr) { + case SOUND_MIXER_RECSRC: + waveartist_set_recmask(devc, val); + break; + + default: + ret = -EINVAL; + if (nr < SOUND_MIXER_NRDEVICES && + devc->mix->supported_devs & (1 << nr)) + ret = waveartist_set_mixer(devc, nr, val); + } + } + + if (ret == 0 && _SIOC_DIR(cmd) & _SIOC_READ) { + ret = -EINVAL; + + switch (nr) { + case SOUND_MIXER_RECSRC: + ret = devc->recmask; + break; + + case SOUND_MIXER_DEVMASK: + ret = devc->mix->supported_devs; + break; + + case SOUND_MIXER_STEREODEVS: + ret = devc->mix->stereo_devs; + break; + + case SOUND_MIXER_RECMASK: + ret = devc->mix->recording_devs; + break; + + case SOUND_MIXER_CAPS: + ret = SOUND_CAP_EXCL_INPUT; + break; + + default: + if (nr < SOUND_MIXER_NRDEVICES) + ret = devc->mix->get_mixer(devc, nr); + break; + } + + if (ret >= 0) + ret = put_user(ret, (int *)arg) ? -EFAULT : 0; + } + + return ret; +} + +static struct mixer_operations waveartist_mixer_operations = +{ + owner: THIS_MODULE, + id: "WaveArtist", + name: "WaveArtist", + ioctl: waveartist_mixer_ioctl +}; + +static void +waveartist_mixer_reset(wavnc_info *devc) +{ + int i; + + if (debug_flg & DEBUG_MIXER) + printk("%s: mixer_reset\n", devc->hw.name); + + /* + * reset mixer cmd + */ + waveartist_cmd1(devc, WACMD_RST_MIXER); + + /* + * set input for ADC to come from 'quiet' + * turn on default modes + */ + waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836); + + /* + * set mixer input select to none, RX filter gains 0 dB + */ + waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00); + + /* + * set bit 0 reg 2 to 1 - unmute MonoOut + */ + waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800); + + /* set default input device = internal mic + * current recording device = none + */ + waveartist_set_recmask(devc, 0); + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + waveartist_mixer_update(devc, i); +} + +static int __init waveartist_init(wavnc_info *devc) +{ + wavnc_port_info *portc; + char rev[3], dev_name[64]; + int my_dev; + + if (waveartist_reset(devc)) + return -ENODEV; + + sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name); + + if (waveartist_getrev(devc, rev)) { + strcat(dev_name, " rev. "); + strcat(dev_name, rev); + } + strcat(dev_name, ")"); + + conf_printf2(dev_name, devc->hw.io_base, devc->hw.irq, + devc->hw.dma, devc->hw.dma2); + + portc = (wavnc_port_info *)kmalloc(sizeof(wavnc_port_info), GFP_KERNEL); + if (portc == NULL) + goto nomem; + + memset(portc, 0, sizeof(wavnc_port_info)); + + my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name, + &waveartist_audio_driver, sizeof(struct audio_driver), + devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8, + devc, devc->hw.dma, devc->hw.dma2); + + if (my_dev < 0) + goto free; + + audio_devs[my_dev]->portc = portc; + + waveartist_mixer_reset(devc); + + /* + * clear any pending interrupt + */ + waveartist_iack(devc); + + if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) { + printk(KERN_ERR "%s: IRQ %d in use\n", + devc->hw.name, devc->hw.irq); + goto uninstall; + } + + if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) { + printk(KERN_ERR "%s: Can't allocate DMA%d\n", + devc->hw.name, devc->hw.dma); + goto uninstall_irq; + } + + if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA) + if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) { + printk(KERN_ERR "%s: can't allocate DMA%d\n", + devc->hw.name, devc->hw.dma2); + goto uninstall_dma; + } + + waveartist_set_ctlr(&devc->hw, 0, DMA1_IE | DMA0_IE); + + audio_devs[my_dev]->mixer_dev = + sound_install_mixer(MIXER_DRIVER_VERSION, + dev_name, + &waveartist_mixer_operations, + sizeof(struct mixer_operations), + devc); + + return my_dev; + +uninstall_dma: + sound_free_dma(devc->hw.dma); + +uninstall_irq: + free_irq(devc->hw.irq, devc); + +uninstall: + sound_unload_audiodev(my_dev); + +free: + kfree(portc); + +nomem: + return -1; +} + +static int __init probe_waveartist(struct address_info *hw_config) +{ + wavnc_info *devc = &adev_info[nr_waveartist_devs]; + + if (nr_waveartist_devs >= MAX_AUDIO_DEV) { + printk(KERN_WARNING "waveartist: too many audio devices\n"); + return 0; + } + + if (check_region(hw_config->io_base, 15)) { + printk(KERN_WARNING "WaveArtist: I/O port conflict\n"); + return 0; + } + + if (hw_config->irq > 15 || hw_config->irq < 0) { + printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n", + hw_config->irq); + return 0; + } + + if (hw_config->dma != 3) { + printk(KERN_WARNING "WaveArtist: Bad DMA %d\n", + hw_config->dma); + return 0; + } + + hw_config->name = "WaveArtist"; + devc->hw = *hw_config; + devc->open_mode = 0; + devc->chip_name = "RWA-010"; + + return 1; +} + +static void __init +attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *mix) +{ + wavnc_info *devc = &adev_info[nr_waveartist_devs]; + + /* + * NOTE! If irq < 0, there is another driver which has allocated the + * IRQ so that this driver doesn't need to allocate/deallocate it. + * The actually used IRQ is ABS(irq). + */ + devc->hw = *hw; + devc->hw.irq = (hw->irq > 0) ? hw->irq : 0; + devc->open_mode = 0; + devc->playback_dev = 0; + devc->record_dev = 0; + devc->audio_flags = DMA_AUTOMODE; + devc->levels = levels; + + if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA) + devc->audio_flags |= DMA_DUPLEX; + + request_region(hw->io_base, 15, devc->hw.name); + + devc->mix = mix; + devc->dev_no = waveartist_init(devc); + + if (devc->dev_no < 0) + release_region(hw->io_base, 15); + else { +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) { + init_timer(&vnc_timer); + vnc_timer.function = vnc_slider_tick; + vnc_timer.expires = jiffies; + vnc_timer.data = nr_waveartist_devs; + add_timer(&vnc_timer); + + vnc_configure_mixer(devc, 0); + + devc->no_autoselect = 1; + } +#endif + nr_waveartist_devs += 1; + } +} + +static void __exit unload_waveartist(struct address_info *hw) +{ + wavnc_info *devc = NULL; + int i; + + for (i = 0; i < nr_waveartist_devs; i++) + if (hw->io_base == adev_info[i].hw.io_base) { + devc = adev_info + i; + break; + } + + if (devc != NULL) { + int mixer; + +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) + del_timer(&vnc_timer); +#endif + + release_region(devc->hw.io_base, 15); + + waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0); + + if (devc->hw.irq >= 0) + free_irq(devc->hw.irq, devc); + + sound_free_dma(devc->hw.dma); + + if (devc->hw.dma != devc->hw.dma2 && + devc->hw.dma2 != NO_DMA) + sound_free_dma(devc->hw.dma2); + + mixer = audio_devs[devc->dev_no]->mixer_dev; + + if (mixer >= 0) + sound_unload_mixerdev(mixer); + + if (devc->dev_no >= 0) + sound_unload_audiodev(devc->dev_no); + + nr_waveartist_devs -= 1; + + for (; i < nr_waveartist_devs; i++) + adev_info[i] = adev_info[i + 1]; + } else + printk(KERN_WARNING "waveartist: can't find device " + "to unload\n"); +} + +#ifdef CONFIG_ARCH_NETWINDER + +/* + * Rebel.com Netwinder specifics... + */ + +#include + +#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec + +#define MIXER_PRIVATE3_RESET 0x53570000 +#define MIXER_PRIVATE3_READ 0x53570001 +#define MIXER_PRIVATE3_WRITE 0x53570002 + +#define VNC_MUTE_INTERNAL_SPKR 0x01 //the sw mute on/off control bit +#define VNC_MUTE_LINE_OUT 0x10 +#define VNC_PHONE_DETECT 0x20 +#define VNC_HANDSET_DETECT 0x40 +#define VNC_DISABLE_AUTOSWITCH 0x80 + +extern spinlock_t gpio_lock; + +static inline void +vnc_mute_spkr(wavnc_info *devc) +{ + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE); + spin_unlock_irqrestore(&gpio_lock, flags); +} + +static void +vnc_mute_lout(wavnc_info *devc) +{ + unsigned int left, right; + + left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL); + right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x400); + + if (devc->line_mute_state) { + left &= ~1; + right &= ~1; + } else { + left |= 1; + right |= 1; + } + waveartist_cmd3(devc, WACMD_SET_MIXER, left, right); + +} + +static int +vnc_volume_slider(wavnc_info *devc) +{ + static signed int old_slider_volume; + unsigned long flags; + signed int volume = 255; + + *CSR_TIMER1_LOAD = 0x00ffffff; + + save_flags(flags); + cli(); + + outb(0xFF, 0x201); + *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; + + while (volume && (inb(0x201) & 0x01)) + volume--; + + *CSR_TIMER1_CNTL = 0; + + restore_flags(flags); + + volume = 0x00ffffff - *CSR_TIMER1_VALUE; + + +#ifndef REVERSE + volume = 150 - (volume >> 5); +#else + volume = (volume >> 6) - 25; +#endif + + if (volume < 0) + volume = 0; + + if (volume > 100) + volume = 100; + + /* + * slider quite often reads +-8, so debounce this random noise + */ + if (abs(volume - old_slider_volume) > 7) { + old_slider_volume = volume; + + if (debug_flg & DEBUG_MIXER) + printk(KERN_DEBUG "Slider volume: %d.\n", volume); + } + + return old_slider_volume; +} + +/* + * Decode a recording mask into a mixer selection on the NetWinder + * as follows: + * + * OSS Source WA Source Actual source + * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848) + * SOUND_MASK_LINE Line Line in + * SOUND_MASK_LINE1 Left Mic Handset + * SOUND_MASK_PHONEIN Left Aux Telephone microphone + * SOUND_MASK_MIC Right Mic Builtin microphone + */ +static unsigned int +netwinder_select_input(wavnc_info *devc, unsigned int recmask, + unsigned char *dev_l, unsigned char *dev_r) +{ + unsigned int recdev_l = ADC_MUX_NONE, recdev_r = ADC_MUX_NONE; + + if (recmask & SOUND_MASK_IMIX) { + recmask = SOUND_MASK_IMIX; + recdev_l = ADC_MUX_MIXER; + recdev_r = ADC_MUX_MIXER; + } else if (recmask & SOUND_MASK_LINE) { + recmask = SOUND_MASK_LINE; + recdev_l = ADC_MUX_LINE; + recdev_r = ADC_MUX_LINE; + } else if (recmask & SOUND_MASK_LINE1) { + recmask = SOUND_MASK_LINE1; + waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ + recdev_l = ADC_MUX_MIC; + recdev_r = ADC_MUX_NONE; + } else if (recmask & SOUND_MASK_PHONEIN) { + recmask = SOUND_MASK_PHONEIN; + waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ + recdev_l = ADC_MUX_AUX1; + recdev_r = ADC_MUX_NONE; + } else if (recmask & SOUND_MASK_MIC) { + recmask = SOUND_MASK_MIC; + waveartist_cmd1(devc, WACMD_SET_MONO | 0x100); /* right */ + recdev_l = ADC_MUX_NONE; + recdev_r = ADC_MUX_MIC; + } + + *dev_l = recdev_l; + *dev_r = recdev_r; + + return recmask; +} + +static int +netwinder_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l, + unsigned char lev_r) +{ + switch (dev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_SYNTH: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: + case SOUND_MIXER_IGAIN: + devc->levels[dev] = lev_l | lev_r << 8; + break; + + case SOUND_MIXER_MIC: /* right mic only */ + devc->levels[SOUND_MIXER_MIC] &= 0xff; + devc->levels[SOUND_MIXER_MIC] |= lev_l << 8; + break; + + case SOUND_MIXER_LINE1: /* left mic only */ + devc->levels[SOUND_MIXER_MIC] &= 0xff00; + devc->levels[SOUND_MIXER_MIC] |= lev_l; + dev = SOUND_MIXER_MIC; + break; + + case SOUND_MIXER_PHONEIN: /* left aux only */ + devc->levels[SOUND_MIXER_LINE1] = lev_l; + dev = SOUND_MIXER_LINE1; + break; + + case SOUND_MIXER_IMIX: + case SOUND_MIXER_PHONEOUT: + break; + + default: + dev = -EINVAL; + break; + } + return dev; +} + +static int netwinder_get_mixer(wavnc_info *devc, int dev) +{ + int levels; + + switch (dev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_SYNTH: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: + case SOUND_MIXER_IGAIN: + levels = devc->levels[dev]; + break; + + case SOUND_MIXER_MIC: /* builtin mic: right mic only */ + levels = devc->levels[SOUND_MIXER_MIC] >> 8; + levels |= levels << 8; + break; + + case SOUND_MIXER_LINE1: /* handset mic: left mic only */ + levels = devc->levels[SOUND_MIXER_MIC] & 0xff; + levels |= levels << 8; + break; + + case SOUND_MIXER_PHONEIN: /* phone mic: left aux1 only */ + levels = devc->levels[SOUND_MIXER_LINE1] & 0xff; + levels |= levels << 8; + break; + + default: + levels = 0; + } + + return levels; +} + +/* + * Waveartist specific mixer information. + */ +static const struct waveartist_mixer_info netwinder_mixer = { + supported_devs: SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | + SOUND_MASK_PCM | SOUND_MASK_SPEAKER | + SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_IMIX | SOUND_MASK_LINE1 | + SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT| + SOUND_MASK_IGAIN, + + recording_devs: SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_IMIX | SOUND_MASK_LINE1 | + SOUND_MASK_PHONEIN, + + stereo_devs: SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | + SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_IMIX | SOUND_MASK_IGAIN, + + select_input: netwinder_select_input, + decode_mixer: netwinder_decode_mixer, + get_mixer: netwinder_get_mixer, +}; + +static void +vnc_configure_mixer(wavnc_info *devc, unsigned int recmask) +{ + if (!devc->no_autoselect) { + if (devc->handset_detect) { + recmask = SOUND_MASK_LINE1; + devc->spkr_mute_state = devc->line_mute_state = 1; + } else if (devc->telephone_detect) { + recmask = SOUND_MASK_PHONEIN; + devc->spkr_mute_state = devc->line_mute_state = 1; + } else { + /* unless someone has asked for LINE-IN, + * we default to MIC + */ + if ((devc->recmask & SOUND_MASK_LINE) == 0) + devc->recmask = SOUND_MASK_MIC; + devc->spkr_mute_state = devc->line_mute_state = 0; + } + vnc_mute_spkr(devc); + vnc_mute_lout(devc); + + if (recmask != devc->recmask) + waveartist_set_recmask(devc, recmask); + } +} + +static int +vnc_slider(wavnc_info *devc) +{ + signed int slider_volume; + unsigned int temp, old_hs, old_td; + + /* + * read the "buttons" state. + * Bit 4 = 0 means handset present + * Bit 5 = 1 means phone offhook + */ + temp = inb(0x201); + + old_hs = devc->handset_detect; + old_td = devc->telephone_detect; + + devc->handset_detect = !(temp & 0x10); + devc->telephone_detect = !!(temp & 0x20); + + if (!devc->no_autoselect && + (old_hs != devc->handset_detect || + old_td != devc->telephone_detect)) + vnc_configure_mixer(devc, devc->recmask); + + slider_volume = vnc_volume_slider(devc); + + /* + * If we're using software controlled volume, and + * the slider moves by more than 20%, then we + * switch back to slider controlled volume. + */ + if (abs(devc->slider_vol - slider_volume) > 20) + devc->use_slider = 1; + + /* + * use only left channel + */ + temp = levels[SOUND_MIXER_VOLUME] & 0xFF; + + if (slider_volume != temp && devc->use_slider) { + devc->slider_vol = slider_volume; + + waveartist_set_mixer(devc, SOUND_MIXER_VOLUME, + slider_volume | slider_volume << 8); + + return 1; + } + + return 0; +} + +static void +vnc_slider_tick(unsigned long data) +{ + int next_timeout; + + if (vnc_slider(adev_info + data)) + next_timeout = 5; // mixer reported change + else + next_timeout = VNC_TIMER_PERIOD; + + mod_timer(&vnc_timer, jiffies + next_timeout); +} + +static int +vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; + int val; + + switch (cmd) { + case SOUND_MIXER_PRIVATE1: + { + u_int prev_spkr_mute, prev_line_mute, prev_auto_state; + int val; + + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* check if parameter is logical */ + if (val & ~(VNC_MUTE_INTERNAL_SPKR | + VNC_MUTE_LINE_OUT | + VNC_DISABLE_AUTOSWITCH)) + return -EINVAL; + + prev_auto_state = devc->no_autoselect; + prev_spkr_mute = devc->spkr_mute_state; + prev_line_mute = devc->line_mute_state; + + devc->no_autoselect = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0; + devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0; + devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0; + + if (prev_spkr_mute != devc->spkr_mute_state) + vnc_mute_spkr(devc); + + if (prev_line_mute != devc->line_mute_state) + vnc_mute_lout(devc); + + if (prev_auto_state != devc->no_autoselect) + vnc_configure_mixer(devc, devc->recmask); + + return 0; + } + + case SOUND_MIXER_PRIVATE2: + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { +#define VNC_SOUND_PAUSE 0x53 //to pause the DSP +#define VNC_SOUND_RESUME 0x57 //to unpause the DSP + case VNC_SOUND_PAUSE: + waveartist_cmd1(devc, 0x16); + break; + + case VNC_SOUND_RESUME: + waveartist_cmd1(devc, 0x18); + break; + + default: + return -EINVAL; + } + return 0; + + /* private ioctl to allow bulk access to waveartist */ + case SOUND_MIXER_PRIVATE3: + { + unsigned long flags; + int mixer_reg[15], i, val; + + if (get_user(val, (int *)arg)) + return -EFAULT; + if (copy_from_user(mixer_reg, (void *)val, sizeof(mixer_reg))) + return -EFAULT; + + switch (mixer_reg[14]) { + case MIXER_PRIVATE3_RESET: + waveartist_mixer_reset(devc); + break; + + case MIXER_PRIVATE3_WRITE: + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]); + + waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]); + waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]); + break; + + case MIXER_PRIVATE3_READ: + spin_lock_irqsave(&waveartist_lock, flags); + + for (i = 0x30; i < 14 << 8; i += 1 << 8) + waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8)); + + spin_unlock_irqrestore(&waveartist_lock, flags); + + if (copy_to_user((void *)val, mixer_reg, sizeof(mixer_reg))) + return -EFAULT; + break; + + default: + return -EINVAL; + } + return 0; + } + + /* read back the state from PRIVATE1 */ + case SOUND_MIXER_PRIVATE4: + val = (devc->spkr_mute_state ? VNC_MUTE_INTERNAL_SPKR : 0) | + (devc->line_mute_state ? VNC_MUTE_LINE_OUT : 0) | + (devc->handset_detect ? VNC_HANDSET_DETECT : 0) | + (devc->telephone_detect ? VNC_PHONE_DETECT : 0) | + (devc->no_autoselect ? VNC_DISABLE_AUTOSWITCH : 0); + + return put_user(val, (int *)arg) ? -EFAULT : 0; + } + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + /* + * special case for master volume: if we + * received this call - switch from hw + * volume control to a software volume + * control, till the hw volume is modified + * to signal that user wants to be back in + * hardware... + */ + if ((cmd & 0xff) == SOUND_MIXER_VOLUME) + devc->use_slider = 0; + + /* speaker output */ + if ((cmd & 0xff) == SOUND_MIXER_SPEAKER) { + unsigned int val, l, r; + + if (get_user(val, (int *)arg)) + return -EFAULT; + + l = val & 0x7f; + r = (val & 0x7f00) >> 8; + val = (l + r) / 2; + devc->levels[SOUND_MIXER_SPEAKER] = val | (val << 8); + devc->spkr_mute_state = (val <= 50); + vnc_mute_spkr(devc); + return 0; + } + } + + return -ENOIOCTLCMD; +} + +#endif + +static struct address_info cfg; + +static int attached; + +static int __initdata io = 0; +static int __initdata irq = 0; +static int __initdata dma = 0; +static int __initdata dma2 = 0; + + +static int __init init_waveartist(void) +{ + const struct waveartist_mixer_info *mix; + + if (!io && machine_is_netwinder()) { + /* + * The NetWinder WaveArtist is at a fixed address. + * If the user does not supply an address, use the + * well-known parameters. + */ + io = 0x250; + irq = 12; + dma = 3; + dma2 = 7; + } + + mix = &waveartist_mixer; +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) + mix = &netwinder_mixer; +#endif + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + if (!probe_waveartist(&cfg)) + return -ENODEV; + + attach_waveartist(&cfg, mix); + attached = 1; + + return 0; +} + +static void __exit cleanup_waveartist(void) +{ + if (attached) + unload_waveartist(&cfg); +} + +module_init(init_waveartist); +module_exit(cleanup_waveartist); + +#ifndef MODULE +static int __init setup_waveartist(char *str) +{ + /* io, irq, dma, dma2 */ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + + return 1; +} +__setup("waveartist=", setup_waveartist); +#endif + +MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver"); +MODULE_PARM(io, "i"); /* IO base */ +MODULE_PARM(irq, "i"); /* IRQ */ +MODULE_PARM(dma, "i"); /* DMA */ +MODULE_PARM(dma2, "i"); /* DMA2 */ +MODULE_LICENSE("GPL"); diff -Nru a/sound/oss/waveartist.h b/sound/oss/waveartist.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/waveartist.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,92 @@ +/* + * linux/drivers/sound/waveartist.h + * + * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder + */ + +//registers +#define CMDR 0 +#define DATR 2 +#define CTLR 4 +#define STATR 5 +#define IRQSTAT 12 + +//bit defs +//reg STATR +#define CMD_WE 0x80 +#define CMD_RF 0x40 +#define DAT_WE 0x20 +#define DAT_RF 0x10 + +#define IRQ_REQ 0x08 +#define DMA1 0x04 +#define DMA0 0x02 + +//bit defs +//reg CTLR +#define CMD_WEIE 0x80 +#define CMD_RFIE 0x40 +#define DAT_WEIE 0x20 +#define DAT_RFIE 0x10 + +#define RESET 0x08 +#define DMA1_IE 0x04 +#define DMA0_IE 0x02 +#define IRQ_ACK 0x01 + +//commands + +#define WACMD_SYSTEMID 0x00 +#define WACMD_GETREV 0x00 +#define WACMD_INPUTFORMAT 0x10 //0-8S, 1-16S, 2-8U +#define WACMD_INPUTCHANNELS 0x11 //1-Mono, 2-Stereo +#define WACMD_INPUTSPEED 0x12 //sampling rate +#define WACMD_INPUTDMA 0x13 //0-8bit, 1-16bit, 2-PIO +#define WACMD_INPUTSIZE 0x14 //samples to interrupt +#define WACMD_INPUTSTART 0x15 //start ADC +#define WACMD_INPUTPAUSE 0x16 //pause ADC +#define WACMD_INPUTSTOP 0x17 //stop ADC +#define WACMD_INPUTRESUME 0x18 //resume ADC +#define WACMD_INPUTPIO 0x19 //PIO ADC + +#define WACMD_OUTPUTFORMAT 0x20 //0-8S, 1-16S, 2-8U +#define WACMD_OUTPUTCHANNELS 0x21 //1-Mono, 2-Stereo +#define WACMD_OUTPUTSPEED 0x22 //sampling rate +#define WACMD_OUTPUTDMA 0x23 //0-8bit, 1-16bit, 2-PIO +#define WACMD_OUTPUTSIZE 0x24 //samples to interrupt +#define WACMD_OUTPUTSTART 0x25 //start ADC +#define WACMD_OUTPUTPAUSE 0x26 //pause ADC +#define WACMD_OUTPUTSTOP 0x27 //stop ADC +#define WACMD_OUTPUTRESUME 0x28 //resume ADC +#define WACMD_OUTPUTPIO 0x29 //PIO ADC + +#define WACMD_GET_LEVEL 0x30 +#define WACMD_SET_LEVEL 0x31 +#define WACMD_SET_MIXER 0x32 +#define WACMD_RST_MIXER 0x33 +#define WACMD_SET_MONO 0x34 + +/* + * Definitions for left/right recording input mux + */ +#define ADC_MUX_NONE 0 +#define ADC_MUX_MIXER 1 +#define ADC_MUX_LINE 2 +#define ADC_MUX_AUX2 3 +#define ADC_MUX_AUX1 4 +#define ADC_MUX_MIC 5 + +/* + * Definitions for mixer gain settings + */ +#define MIX_GAIN_LINE 0 /* line in */ +#define MIX_GAIN_AUX1 1 /* aux1 */ +#define MIX_GAIN_AUX2 2 /* aux2 */ +#define MIX_GAIN_XMIC 3 /* crossover mic */ +#define MIX_GAIN_MIC 4 /* normal mic */ +#define MIX_GAIN_PREMIC 5 /* preamp mic */ +#define MIX_GAIN_OUT 6 /* output */ +#define MIX_GAIN_MONO 7 /* mono in */ + +int wa_sendcmd(unsigned int cmd); +int wa_writecmd(unsigned int cmd, unsigned int arg); diff -Nru a/sound/oss/wavfront.c b/sound/oss/wavfront.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/wavfront.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,3529 @@ +/* -*- linux-c -*- + * + * sound/wavfront.c + * + * A Linux driver for Turtle Beach WaveFront Series (Maui, Tropez, Tropez Plus) + * + * This driver supports the onboard wavetable synthesizer (an ICS2115), + * including patch, sample and program loading and unloading, conversion + * of GUS patches during loading, and full user-level access to all + * WaveFront commands. It tries to provide semi-intelligent patch and + * sample management as well. + * + * It also provides support for the ICS emulation of an MPU-401. Full + * support for the ICS emulation's "virtual MIDI mode" is provided in + * wf_midi.c. + * + * Support is also provided for the Tropez Plus' onboard FX processor, + * a Yamaha YSS225. Currently, code exists to configure the YSS225, + * and there is an interface allowing tweaking of any of its memory + * addresses. However, I have been unable to decipher the logical + * positioning of the configuration info for various effects, so for + * now, you just get the YSS225 in the same state as Turtle Beach's + * "SETUPSND.EXE" utility leaves it. + * + * The boards' DAC/ADC (a Crystal CS4232) is supported by cs4232.[co], + * This chip also controls the configuration of the card: the wavefront + * synth is logical unit 4. + * + * + * Supported devices: + * + * /dev/dsp - using cs4232+ad1848 modules, OSS compatible + * /dev/midiNN and /dev/midiNN+1 - using wf_midi code, OSS compatible + * /dev/synth00 - raw synth interface + * + ********************************************************************** + * + * Copyright (C) by Paul Barton-Davis 1998 + * + * Some portions of this file are taken from work that is + * copyright (C) by Hannu Savolainen 1993-1996 + * + * Although the relevant code here is all new, the handling of + * sample/alias/multi- samples is entirely based on a driver by Matt + * Martin and Rutger Nijlunsing which demonstrated how to get things + * to work correctly. The GUS patch loading code has been almost + * unaltered by me, except to fit formatting and function names in the + * rest of the file. Many thanks to them. + * + * Appreciation and thanks to Hannu Savolainen for his early work on the Maui + * driver, and answering a few questions while this one was developed. + * + * Absolutely NO thanks to Turtle Beach/Voyetra and Yamaha for their + * complete lack of help in developing this driver, and in particular + * for their utter silence in response to questions about undocumented + * aspects of configuring a WaveFront soundcard, particularly the + * effects processor. + * + * $Id: wavfront.c,v 0.7 1998/09/09 15:47:36 pbd Exp $ + * + * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added some __init and __initdata to entries in yss225.c + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "sound_config.h" + +#include + +#define _MIDI_SYNTH_C_ +#define MIDI_SYNTH_NAME "WaveFront MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +/* Compile-time control of the extent to which OSS is supported. + + I consider /dev/sequencer to be an anachronism, but given its + widespread usage by various Linux MIDI software, it seems worth + offering support to it if its not too painful. Instead of using + /dev/sequencer, I recommend: + + for synth programming and patch loading: /dev/synthNN + for kernel-synchronized MIDI sequencing: the ALSA sequencer + for direct MIDI control: /dev/midiNN + + I have never tried static compilation into the kernel. The #if's + for this are really just notes to myself about what the code is + for. +*/ + +#define OSS_SUPPORT_SEQ 0x1 /* use of /dev/sequencer */ +#define OSS_SUPPORT_STATIC_INSTALL 0x2 /* static compilation into kernel */ + +#define OSS_SUPPORT_LEVEL 0x1 /* just /dev/sequencer for now */ + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ +static int (*midi_load_patch) (int devno, int format, const char *addr, + int offs, int count, int pmgr_flag) = NULL; +#endif /* OSS_SUPPORT_SEQ */ + +/* if WF_DEBUG not defined, no run-time debugging messages will + be available via the debug flag setting. Given the current + beta state of the driver, this will remain set until a future + version. +*/ + +#define WF_DEBUG 1 + +#ifdef WF_DEBUG + +/* Thank goodness for gcc's preprocessor ... */ + +#define DPRINT(cond, format, args...) \ + if ((dev.debug & (cond)) == (cond)) { \ + printk (KERN_DEBUG LOGNAME format, ## args); \ + } +#else +#define DPRINT(cond, format, args...) +#endif + +#define LOGNAME "WaveFront: " + +/* bitmasks for WaveFront status port value */ + +#define STAT_RINTR_ENABLED 0x01 +#define STAT_CAN_READ 0x02 +#define STAT_INTR_READ 0x04 +#define STAT_WINTR_ENABLED 0x10 +#define STAT_CAN_WRITE 0x20 +#define STAT_INTR_WRITE 0x40 + +/*** Module-accessible parameters ***************************************/ + +int wf_raw; /* we normally check for "raw state" to firmware + loading. if set, then during driver loading, the + state of the board is ignored, and we reset the + board and load the firmware anyway. + */ + +int fx_raw = 1; /* if this is zero, we'll leave the FX processor in + whatever state it is when the driver is loaded. + The default is to download the microprogram and + associated coefficients to set it up for "default" + operation, whatever that means. + */ + +int debug_default; /* you can set this to control debugging + during driver loading. it takes any combination + of the WF_DEBUG_* flags defined in + wavefront.h + */ + +/* XXX this needs to be made firmware and hardware version dependent */ + +char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed + version of the WaveFront OS + */ + +int wait_polls = 2000; /* This is a number of tries we poll the status register + before resorting to sleeping. WaveFront being an ISA + card each poll takes about 1.2us. So before going to + sleep we wait up to 2.4ms in a loop. + */ + +int sleep_length = HZ/100; /* This says how long we're going to sleep between polls. + 10ms sounds reasonable for fast response. + */ + +int sleep_tries = 50; /* Wait for status 0.5 seconds total. */ + +int reset_time = 2; /* hundreths of a second we wait after a HW reset for + the expected interrupt. + */ + +int ramcheck_time = 20; /* time in seconds to wait while ROM code + checks on-board RAM. + */ + +int osrun_time = 10; /* time in seconds we wait for the OS to + start running. + */ + +MODULE_PARM(wf_raw,"i"); +MODULE_PARM(fx_raw,"i"); +MODULE_PARM(debug_default,"i"); +MODULE_PARM(wait_polls,"i"); +MODULE_PARM(sleep_length,"i"); +MODULE_PARM(sleep_tries,"i"); +MODULE_PARM(ospath,"s"); +MODULE_PARM(reset_time,"i"); +MODULE_PARM(ramcheck_time,"i"); +MODULE_PARM(osrun_time,"i"); + +/***************************************************************************/ + +/* Note: because this module doesn't export any symbols, this really isn't + a global variable, even if it looks like one. I was quite confused by + this when I started writing this as a (newer) module -- pbd. +*/ + +struct wf_config { + int devno; /* device number from kernel */ + int irq; /* "you were one, one of the few ..." */ + int base; /* low i/o port address */ + +#define mpu_data_port base +#define mpu_command_port base + 1 /* write semantics */ +#define mpu_status_port base + 1 /* read semantics */ +#define data_port base + 2 +#define status_port base + 3 /* read semantics */ +#define control_port base + 3 /* write semantics */ +#define block_port base + 4 /* 16 bit, writeonly */ +#define last_block_port base + 6 /* 16 bit, writeonly */ + + /* FX ports. These are mapped through the ICS2115 to the YS225. + The ICS2115 takes care of flipping the relevant pins on the + YS225 so that access to each of these ports does the right + thing. Note: these are NOT documented by Turtle Beach. + */ + +#define fx_status base + 8 +#define fx_op base + 8 +#define fx_lcr base + 9 +#define fx_dsp_addr base + 0xa +#define fx_dsp_page base + 0xb +#define fx_dsp_lsb base + 0xc +#define fx_dsp_msb base + 0xd +#define fx_mod_addr base + 0xe +#define fx_mod_data base + 0xf + + volatile int irq_ok; /* set by interrupt handler */ + volatile int irq_cnt; /* ditto */ + int opened; /* flag, holds open(2) mode */ + char debug; /* debugging flags */ + int freemem; /* installed RAM, in bytes */ + + int synth_dev; /* devno for "raw" synth */ + int mididev; /* devno for internal MIDI */ + int ext_mididev; /* devno for external MIDI */ + int fx_mididev; /* devno for FX MIDI interface */ +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + int oss_dev; /* devno for OSS sequencer synth */ +#endif /* OSS_SUPPORT_SEQ */ + + char fw_version[2]; /* major = [0], minor = [1] */ + char hw_version[2]; /* major = [0], minor = [1] */ + char israw; /* needs Motorola microcode */ + char has_fx; /* has FX processor (Tropez+) */ + char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ + char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ + char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ + int samples_used; /* how many */ + char interrupts_on; /* h/w MPU interrupts enabled ? */ + char rom_samples_rdonly; /* can we write on ROM samples */ + wait_queue_head_t interrupt_sleeper; +} dev; + +static int detect_wffx(void); +static int wffx_ioctl (wavefront_fx_info *); +static int wffx_init (void); + +static int wavefront_delete_sample (int sampnum); +static int wavefront_find_free_sample (void); + +/* From wf_midi.c */ + +extern int virtual_midi_enable (void); +extern int virtual_midi_disable (void); +extern int detect_wf_mpu (int, int); +extern int install_wf_mpu (void); +extern int uninstall_wf_mpu (void); + +typedef struct { + int cmd; + char *action; + unsigned int read_cnt; + unsigned int write_cnt; + int need_ack; +} wavefront_command; + +static struct { + int errno; + const char *errstr; +} wavefront_errors[] = { + { 0x01, "Bad sample number" }, + { 0x02, "Out of sample memory" }, + { 0x03, "Bad patch number" }, + { 0x04, "Error in number of voices" }, + { 0x06, "Sample load already in progress" }, + { 0x0B, "No sample load request pending" }, + { 0x0E, "Bad MIDI channel number" }, + { 0x10, "Download Record Error" }, + { 0x80, "Success" }, + { 0x0, 0x0 } +}; + +#define NEEDS_ACK 1 + +static wavefront_command wavefront_commands[] = { + { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, + { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, + { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, + { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, + { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, + { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, + { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, + { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, + { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, + { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, + { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, + { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, + { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, + { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, + { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, + { WFC_DOWNLOAD_SAMPLE, "download sample", + 0, WF_SAMPLE_BYTES, NEEDS_ACK }, + { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, + { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", + 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, + + /* This command requires a variable number of bytes to be written. + There is a hack in wavefront_cmd() to support this. The actual + count is passed in as the read buffer ptr, cast appropriately. + Ugh. + */ + + { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, + + /* This one is a hack as well. We just read the first byte of the + response, don't fetch an ACK, and leave the rest to the + calling function. Ugly, ugly, ugly. + */ + + { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, + { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", + 0, WF_ALIAS_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, + { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, + { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, + { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, + { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, + { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, + { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, + { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, + { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, + { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, + NEEDS_ACK}, + { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, + { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", + 0, 1, NEEDS_ACK }, + { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, + { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", + 32, 0, 0 }, + { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, + { 0x00 } +}; + +static const char * +wavefront_errorstr (int errnum) + +{ + int i; + + for (i = 0; wavefront_errors[i].errstr; i++) { + if (wavefront_errors[i].errno == errnum) { + return wavefront_errors[i].errstr; + } + } + + return "Unknown WaveFront error"; +} + +static wavefront_command * +wavefront_get_command (int cmd) + +{ + int i; + + for (i = 0; wavefront_commands[i].cmd != 0; i++) { + if (cmd == wavefront_commands[i].cmd) { + return &wavefront_commands[i]; + } + } + + return (wavefront_command *) 0; +} + +static inline int +wavefront_status (void) + +{ + return inb (dev.status_port); +} + +static int +wavefront_wait (int mask) + +{ + int i; + + for (i = 0; i < wait_polls; i++) + if (wavefront_status() & mask) + return 1; + + for (i = 0; i < sleep_tries; i++) { + + if (wavefront_status() & mask) { + set_current_state(TASK_RUNNING); + return 1; + } + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(sleep_length); + if (signal_pending(current)) + break; + } + + set_current_state(TASK_RUNNING); + return 0; +} + +static int +wavefront_read (void) + +{ + if (wavefront_wait (STAT_CAN_READ)) + return inb (dev.data_port); + + DPRINT (WF_DEBUG_DATA, "read timeout.\n"); + + return -1; +} + +static int +wavefront_write (unsigned char data) + +{ + if (wavefront_wait (STAT_CAN_WRITE)) { + outb (data, dev.data_port); + return 0; + } + + DPRINT (WF_DEBUG_DATA, "write timeout.\n"); + + return -1; +} + +static int +wavefront_cmd (int cmd, unsigned char *rbuf, unsigned char *wbuf) + +{ + int ack; + int i; + int c; + wavefront_command *wfcmd; + + if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { + printk (KERN_WARNING LOGNAME "command 0x%x not supported.\n", + cmd); + return 1; + } + + /* Hack to handle the one variable-size write command. See + wavefront_send_multisample() for the other half of this + gross and ugly strategy. + */ + + if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { + wfcmd->write_cnt = (unsigned int) rbuf; + rbuf = 0; + } + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + + if (wavefront_write (cmd)) { + DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + } + + if (wfcmd->write_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "writing %d bytes " + "for 0x%x\n", + wfcmd->write_cnt, cmd); + + for (i = 0; i < wfcmd->write_cnt; i++) { + if (wavefront_write (wbuf[i])) { + DPRINT (WF_DEBUG_IO, "bad write for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n", + i, wbuf[i]); + } + } + + if (wfcmd->read_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "reading %d ints " + "for 0x%x\n", + wfcmd->read_cnt, cmd); + + for (i = 0; i < wfcmd->read_cnt; i++) { + + if ((c = wavefront_read()) == -1) { + DPRINT (WF_DEBUG_IO, "bad read for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + /* Now handle errors. Lots of special cases here */ + + if (c == 0xff) { + if ((c = wavefront_read ()) == -1) { + DPRINT (WF_DEBUG_IO, "bad read for " + "error byte at " + "read byte %d " + "of 0x%x [%s].\n", + i, cmd, + wfcmd->action); + return 1; + } + + /* Can you believe this madness ? */ + + if (c == 1 && + wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { + rbuf[0] = WF_ST_EMPTY; + return (0); + + } else if (c == 3 && + wfcmd->cmd == WFC_UPLOAD_PATCH) { + + return 3; + + } else if (c == 1 && + wfcmd->cmd == WFC_UPLOAD_PROGRAM) { + + return 1; + + } else { + + DPRINT (WF_DEBUG_IO, "error %d (%s) " + "during " + "read for byte " + "%d of 0x%x " + "[%s].\n", + c, + wavefront_errorstr (c), + i, cmd, + wfcmd->action); + return 1; + + } + + } else { + rbuf[i] = c; + } + + DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]); + } + } + + if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { + + DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd); + + /* Some commands need an ACK, but return zero instead + of the standard value. + */ + + if ((ack = wavefront_read()) == 0) { + ack = WF_ACK; + } + + if (ack != WF_ACK) { + if (ack == -1) { + DPRINT (WF_DEBUG_IO, "cannot read ack for " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + + } else { + int err = -1; /* something unknown */ + + if (ack == 0xff) { /* explicit error */ + + if ((err = wavefront_read ()) == -1) { + DPRINT (WF_DEBUG_DATA, + "cannot read err " + "for 0x%x [%s].\n", + cmd, wfcmd->action); + } + } + + DPRINT (WF_DEBUG_IO, "0x%x [%s] " + "failed (0x%x, 0x%x, %s)\n", + cmd, wfcmd->action, ack, err, + wavefront_errorstr (err)); + + return -err; + } + } + + DPRINT (WF_DEBUG_DATA, "ack received " + "for 0x%x [%s]\n", + cmd, wfcmd->action); + } else { + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need " + "ACK (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + } + + return 0; + +} + +/*********************************************************************** +WaveFront: data munging + +Things here are weird. All data written to the board cannot +have its most significant bit set. Any data item with values +potentially > 0x7F (127) must be split across multiple bytes. + +Sometimes, we need to munge numeric values that are represented on +the x86 side as 8-32 bit values. Sometimes, we need to munge data +that is represented on the x86 side as an array of bytes. The most +efficient approach to handling both cases seems to be to use 2 +different functions for munging and 2 for de-munging. This avoids +weird casting and worrying about bit-level offsets. + +**********************************************************************/ + +static +unsigned char * +munge_int32 (unsigned int src, + unsigned char *dst, + unsigned int dst_size) +{ + int i; + + for (i = 0;i < dst_size; i++) { + *dst = src & 0x7F; /* Mask high bit of LSB */ + src = src >> 7; /* Rotate Right 7 bits */ + /* Note: we leave the upper bits in place */ + + dst++; + }; + return dst; +}; + +static int +demunge_int32 (unsigned char* src, int src_size) + +{ + int i; + int outval = 0; + + for (i = src_size - 1; i >= 0; i--) { + outval=(outval<<7)+src[i]; + } + + return outval; +}; + +static +unsigned char * +munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) + +{ + int i; + unsigned int last = dst_size / 2; + + for (i = 0; i < last; i++) { + *dst++ = src[i] & 0x7f; + *dst++ = src[i] >> 7; + } + return dst; +} + +static +unsigned char * +demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) + +{ + int i; + unsigned char *end = src + src_bytes; + + end = src + src_bytes; + + /* NOTE: src and dst *CAN* point to the same address */ + + for (i = 0; src != end; i++) { + dst[i] = *src++; + dst[i] |= (*src++)<<7; + } + + return dst; +} + +/*********************************************************************** +WaveFront: sample, patch and program management. +***********************************************************************/ + +static int +wavefront_delete_sample (int sample_num) + +{ + unsigned char wbuf[2]; + int x; + + wbuf[0] = sample_num & 0x7f; + wbuf[1] = sample_num >> 7; + + if ((x = wavefront_cmd (WFC_DELETE_SAMPLE, 0, wbuf)) == 0) { + dev.sample_status[sample_num] = WF_ST_EMPTY; + } + + return x; +} + +static int +wavefront_get_sample_status (int assume_rom) + +{ + int i; + unsigned char rbuf[32], wbuf[32]; + unsigned int sc_real, sc_alias, sc_multi; + + /* check sample status */ + + if (wavefront_cmd (WFC_GET_NSAMPLES, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME "cannot request sample count.\n"); + return -1; + } + + sc_real = sc_alias = sc_multi = dev.samples_used = 0; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + + wbuf[0] = i & 0x7f; + wbuf[1] = i >> 7; + + if (wavefront_cmd (WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "cannot identify sample " + "type of slot %d\n", i); + dev.sample_status[i] = WF_ST_EMPTY; + continue; + } + + dev.sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); + + if (assume_rom) { + dev.sample_status[i] |= WF_SLOT_ROM; + } + + switch (rbuf[0] & WF_ST_MASK) { + case WF_ST_SAMPLE: + sc_real++; + break; + case WF_ST_MULTISAMPLE: + sc_multi++; + break; + case WF_ST_ALIAS: + sc_alias++; + break; + case WF_ST_EMPTY: + break; + + default: + printk (KERN_WARNING LOGNAME "unknown sample type for " + "slot %d (0x%x)\n", + i, rbuf[0]); + } + + if (rbuf[0] != WF_ST_EMPTY) { + dev.samples_used++; + } + } + + printk (KERN_INFO LOGNAME + "%d samples used (%d real, %d aliases, %d multi), " + "%d empty\n", dev.samples_used, sc_real, sc_alias, sc_multi, + WF_MAX_SAMPLE - dev.samples_used); + + + return (0); + +} + +static int +wavefront_get_patch_status (void) + +{ + unsigned char patchbuf[WF_PATCH_BYTES]; + unsigned char patchnum[2]; + wavefront_patch *p; + int i, x, cnt, cnt2; + + for (i = 0; i < WF_MAX_PATCH; i++) { + patchnum[0] = i & 0x7f; + patchnum[1] = i >> 7; + + if ((x = wavefront_cmd (WFC_UPLOAD_PATCH, patchbuf, + patchnum)) == 0) { + + dev.patch_status[i] |= WF_SLOT_FILLED; + p = (wavefront_patch *) patchbuf; + dev.sample_status + [p->sample_number|(p->sample_msb<<7)] |= + WF_SLOT_USED; + + } else if (x == 3) { /* Bad patch number */ + dev.patch_status[i] = 0; + } else { + printk (KERN_ERR LOGNAME "upload patch " + "error 0x%x\n", x); + dev.patch_status[i] = 0; + return 1; + } + } + + /* program status has already filled in slot_used bits */ + + for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { + if (dev.patch_status[i] & WF_SLOT_FILLED) { + cnt++; + } + if (dev.patch_status[i] & WF_SLOT_USED) { + cnt2++; + } + + } + printk (KERN_INFO LOGNAME + "%d patch slots filled, %d in use\n", cnt, cnt2); + + return (0); +} + +static int +wavefront_get_program_status (void) + +{ + unsigned char progbuf[WF_PROGRAM_BYTES]; + wavefront_program prog; + unsigned char prognum; + int i, x, l, cnt; + + for (i = 0; i < WF_MAX_PROGRAM; i++) { + prognum = i; + + if ((x = wavefront_cmd (WFC_UPLOAD_PROGRAM, progbuf, + &prognum)) == 0) { + + dev.prog_status[i] |= WF_SLOT_USED; + + demunge_buf (progbuf, (unsigned char *) &prog, + WF_PROGRAM_BYTES); + + for (l = 0; l < WF_NUM_LAYERS; l++) { + if (prog.layer[l].mute) { + dev.patch_status + [prog.layer[l].patch_number] |= + WF_SLOT_USED; + } + } + } else if (x == 1) { /* Bad program number */ + dev.prog_status[i] = 0; + } else { + printk (KERN_ERR LOGNAME "upload program " + "error 0x%x\n", x); + dev.prog_status[i] = 0; + } + } + + for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { + if (dev.prog_status[i]) { + cnt++; + } + } + + printk (KERN_INFO LOGNAME "%d programs slots in use\n", cnt); + + return (0); +} + +static int +wavefront_send_patch (wavefront_patch_info *header) + +{ + unsigned char buf[WF_PATCH_BYTES+2]; + unsigned char *bptr; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", + header->number); + + dev.patch_status[header->number] |= WF_SLOT_FILLED; + + bptr = buf; + bptr = munge_int32 (header->number, buf, 2); + munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); + + if (wavefront_cmd (WFC_DOWNLOAD_PATCH, 0, buf)) { + printk (KERN_ERR LOGNAME "download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_send_program (wavefront_patch_info *header) + +{ + unsigned char buf[WF_PROGRAM_BYTES+1]; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", + header->number); + + dev.prog_status[header->number] = WF_SLOT_USED; + + /* XXX need to zero existing SLOT_USED bit for program_status[i] + where `i' is the program that's being (potentially) overwritten. + */ + + for (i = 0; i < WF_NUM_LAYERS; i++) { + if (header->hdr.pr.layer[i].mute) { + dev.patch_status[header->hdr.pr.layer[i].patch_number] |= + WF_SLOT_USED; + + /* XXX need to mark SLOT_USED for sample used by + patch_number, but this means we have to load it. Ick. + */ + } + } + + buf[0] = header->number; + munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); + + if (wavefront_cmd (WFC_DOWNLOAD_PROGRAM, 0, buf)) { + printk (KERN_WARNING LOGNAME "download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_freemem (void) + +{ + char rbuf[8]; + + if (wavefront_cmd (WFC_REPORT_FREE_MEMORY, rbuf, 0)) { + printk (KERN_WARNING LOGNAME "can't get memory stats.\n"); + return -1; + } else { + return demunge_int32 (rbuf, 4); + } +} + +static int +wavefront_send_sample (wavefront_patch_info *header, + UINT16 *dataptr, + int data_is_unsigned) + +{ + /* samples are downloaded via a 16-bit wide i/o port + (you could think of it as 2 adjacent 8-bit wide ports + but its less efficient that way). therefore, all + the blocksizes and so forth listed in the documentation, + and used conventionally to refer to sample sizes, + which are given in 8-bit units (bytes), need to be + divided by 2. + */ + + UINT16 sample_short; + UINT32 length; + UINT16 *data_end = 0; + unsigned int i; + const int max_blksize = 4096/2; + unsigned int written; + unsigned int blocksize; + int dma_ack; + int blocknum; + unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; + unsigned char *shptr; + int skip = 0; + int initial_skip = 0; + + DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, " + "type %d, %d bytes from 0x%x\n", + header->size ? "" : "header ", + header->number, header->subkey, + header->size, + (int) header->dataptr); + + if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) { + int x; + + if ((x = wavefront_find_free_sample ()) < 0) { + return -ENOMEM; + } + printk (KERN_DEBUG LOGNAME "unspecified sample => %d\n", x); + header->number = x; + } + + if (header->size) { + + /* XXX its a debatable point whether or not RDONLY semantics + on the ROM samples should cover just the sample data or + the sample header. For now, it only covers the sample data, + so anyone is free at all times to rewrite sample headers. + + My reason for this is that we have the sample headers + available in the WFB file for General MIDI, and so these + can always be reset if needed. The sample data, however, + cannot be recovered without a complete reset and firmware + reload of the ICS2115, which is a very expensive operation. + + So, doing things this way allows us to honor the notion of + "RESETSAMPLES" reasonably cheaply. Note however, that this + is done purely at user level: there is no WFB parser in + this driver, and so a complete reset (back to General MIDI, + or theoretically some other configuration) is the + responsibility of the user level library. + + To try to do this in the kernel would be a little + crazy: we'd need 158K of kernel space just to hold + a copy of the patch/program/sample header data. + */ + + if (dev.rom_samples_rdonly) { + if (dev.sample_status[header->number] & WF_SLOT_ROM) { + printk (KERN_ERR LOGNAME "sample slot %d " + "write protected\n", + header->number); + return -EACCES; + } + } + + wavefront_delete_sample (header->number); + } + + if (header->size) { + dev.freemem = wavefront_freemem (); + + if (dev.freemem < header->size) { + printk (KERN_ERR LOGNAME + "insufficient memory to " + "load %d byte sample.\n", + header->size); + return -ENOMEM; + } + + } + + skip = WF_GET_CHANNEL(&header->hdr.s); + + if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { + printk (KERN_ERR LOGNAME "channel selection only " + "possible on 16-bit samples"); + return -(EINVAL); + } + + switch (skip) { + case 0: + initial_skip = 0; + skip = 1; + break; + case 1: + initial_skip = 0; + skip = 2; + break; + case 2: + initial_skip = 1; + skip = 2; + break; + case 3: + initial_skip = 2; + skip = 3; + break; + case 4: + initial_skip = 3; + skip = 4; + break; + case 5: + initial_skip = 4; + skip = 5; + break; + case 6: + initial_skip = 5; + skip = 6; + break; + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => " + "initial skip = %d, skip = %d\n", + WF_GET_CHANNEL (&header->hdr.s), + initial_skip, skip); + + /* Be safe, and zero the "Unused" bits ... */ + + WF_SET_CHANNEL(&header->hdr.s, 0); + + /* adjust size for 16 bit samples by dividing by two. We always + send 16 bits per write, even for 8 bit samples, so the length + is always half the size of the sample data in bytes. + */ + + length = header->size / 2; + + /* the data we're sent has not been munged, and in fact, the + header we have to send isn't just a munged copy either. + so, build the sample header right here. + */ + + shptr = &sample_hdr[0]; + + shptr = munge_int32 (header->number, shptr, 2); + + if (header->size) { + shptr = munge_int32 (length, shptr, 4); + } + + /* Yes, a 4 byte result doesn't contain all of the offset bits, + but the offset only uses 24 bits. + */ + + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopEndOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset), + shptr, 4); + + /* This one is truly weird. What kind of weirdo decided that in + a system dominated by 16 and 32 bit integers, they would use + a just 12 bits ? + */ + + shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); + + /* Why is this nybblified, when the MSB is *always* zero ? + Anyway, we can't take address of bitfield, so make a + good-faith guess at where it starts. + */ + + shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), + shptr, 2); + + if (wavefront_cmd (header->size ? + WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, + 0, sample_hdr)) { + printk (KERN_WARNING LOGNAME "sample %sdownload refused.\n", + header->size ? "" : "header "); + return -(EIO); + } + + if (header->size == 0) { + goto sent; /* Sorry. Just had to have one somewhere */ + } + + data_end = dataptr + length; + + /* Do any initial skip over an unused channel's data */ + + dataptr += initial_skip; + + for (written = 0, blocknum = 0; + written < length; written += max_blksize, blocknum++) { + + if ((length - written) > max_blksize) { + blocksize = max_blksize; + } else { + /* round to nearest 16-byte value */ + blocksize = ((length-written+7)&~0x7); + } + + if (wavefront_cmd (WFC_DOWNLOAD_BLOCK, 0, 0)) { + printk (KERN_WARNING LOGNAME "download block " + "request refused.\n"); + return -(EIO); + } + + for (i = 0; i < blocksize; i++) { + + if (dataptr < data_end) { + + __get_user (sample_short, dataptr); + dataptr += skip; + + if (data_is_unsigned) { /* GUS ? */ + + if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { + + /* 8 bit sample + resolution, sign + extend both bytes. + */ + + ((unsigned char*) + &sample_short)[0] += 0x7f; + ((unsigned char*) + &sample_short)[1] += 0x7f; + + } else { + + /* 16 bit sample + resolution, sign + extend the MSB. + */ + + sample_short += 0x7fff; + } + } + + } else { + + /* In padding section of final block: + + Don't fetch unsupplied data from + user space, just continue with + whatever the final value was. + */ + } + + if (i < blocksize - 1) { + outw (sample_short, dev.block_port); + } else { + outw (sample_short, dev.last_block_port); + } + } + + /* Get "DMA page acknowledge", even though its really + nothing to do with DMA at all. + */ + + if ((dma_ack = wavefront_read ()) != WF_DMA_ACK) { + if (dma_ack == -1) { + printk (KERN_ERR LOGNAME "upload sample " + "DMA ack timeout\n"); + return -(EIO); + } else { + printk (KERN_ERR LOGNAME "upload sample " + "DMA ack error 0x%x\n", + dma_ack); + return -(EIO); + } + } + } + + dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); + + /* Note, label is here because sending the sample header shouldn't + alter the sample_status info at all. + */ + + sent: + return (0); +} + +static int +wavefront_send_alias (wavefront_patch_info *header) + +{ + unsigned char alias_hdr[WF_ALIAS_BYTES]; + + DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is " + "alias for %d\n", + header->number, + header->hdr.a.OriginalSample); + + munge_int32 (header->number, &alias_hdr[0], 2); + munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), + &alias_hdr[4], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), + &alias_hdr[8], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), + &alias_hdr[12], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), + &alias_hdr[16], 4); + munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); + munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); + + if (wavefront_cmd (WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) { + printk (KERN_ERR LOGNAME "download alias failed.\n"); + return -(EIO); + } + + dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); + + return (0); +} + +static int +wavefront_send_multisample (wavefront_patch_info *header) +{ + int i; + int num_samples; + unsigned char msample_hdr[WF_MSAMPLE_BYTES]; + + munge_int32 (header->number, &msample_hdr[0], 2); + + /* You'll recall at this point that the "number of samples" value + in a wavefront_multisample struct is actually the log2 of the + real number of samples. + */ + + num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); + msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; + + DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n", + header->number, + header->hdr.ms.NumberOfSamples, + num_samples); + + for (i = 0; i < num_samples; i++) { + DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + munge_int32 (header->hdr.ms.SampleNumber[i], + &msample_hdr[3+(i*2)], 2); + } + + /* Need a hack here to pass in the number of bytes + to be written to the synth. This is ugly, and perhaps + one day, I'll fix it. + */ + + if (wavefront_cmd (WFC_DOWNLOAD_MULTISAMPLE, + (unsigned char *) ((num_samples*2)+3), + msample_hdr)) { + printk (KERN_ERR LOGNAME "download of multisample failed.\n"); + return -(EIO); + } + + dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); + + return (0); +} + +static int +wavefront_fetch_multisample (wavefront_patch_info *header) +{ + int i; + unsigned char log_ns[1]; + unsigned char number[2]; + int num_samples; + + munge_int32 (header->number, number, 2); + + if (wavefront_cmd (WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { + printk (KERN_ERR LOGNAME "upload multisample failed.\n"); + return -(EIO); + } + + DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n", + header->number, log_ns[0]); + + header->hdr.ms.NumberOfSamples = log_ns[0]; + + /* get the number of samples ... */ + + num_samples = (1 << log_ns[0]); + + for (i = 0; i < num_samples; i++) { + char d[2]; + + if ((d[0] = wavefront_read ()) == -1) { + printk (KERN_ERR LOGNAME "upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + + if ((d[1] = wavefront_read ()) == -1) { + printk (KERN_ERR LOGNAME "upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + + header->hdr.ms.SampleNumber[i] = + demunge_int32 ((unsigned char *) d, 2); + + DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } + + return (0); +} + + +static int +wavefront_send_drum (wavefront_patch_info *header) + +{ + unsigned char drumbuf[WF_DRUM_BYTES]; + wavefront_drum *drum = &header->hdr.d; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI " + "note %d, patch = %d\n", + header->number, drum->PatchNumber); + + drumbuf[0] = header->number & 0x7f; + + for (i = 0; i < 4; i++) { + munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); + } + + if (wavefront_cmd (WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) { + printk (KERN_ERR LOGNAME "download drum failed.\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_find_free_sample (void) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(dev.sample_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING LOGNAME "no free sample slots!\n"); + return -1; +} + +static int +wavefront_find_free_patch (void) + +{ + int i; + + for (i = 0; i < WF_MAX_PATCH; i++) { + if (!(dev.patch_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING LOGNAME "no free patch slots!\n"); + return -1; +} + +static int +log2_2048(int n) + +{ + int tbl[]={0, 0, 2048, 3246, 4096, 4755, 5294, 5749, 6143, + 6492, 6803, 7084, 7342, 7578, 7797, 8001, 8192, + 8371, 8540, 8699, 8851, 8995, 9132, 9264, 9390, + 9510, 9626, 9738, 9845, 9949, 10049, 10146}; + int i; + + /* Returns 2048*log2(n) */ + + /* FIXME: this is like doing integer math + on quantum particles (RuN) */ + + i=0; + while(n>=32*256) { + n>>=8; + i+=2048*8; + } + while(n>=32) { + n>>=1; + i+=2048; + } + i+=tbl[n]; + return(i); +} + +static int +wavefront_load_gus_patch (int devno, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info guspatch; + wavefront_patch_info samp, pat, prog; + wavefront_patch *patp; + wavefront_sample *sampp; + wavefront_program *progp; + + int i,base_note; + long sizeof_patch; + + /* Copy in the header of the GUS patch */ + + sizeof_patch = (long) &guspatch.data[0] - (long) &guspatch; + copy_from_user (&((char *) &guspatch)[offs], + &(addr)[offs], sizeof_patch - offs); + + if ((i = wavefront_find_free_patch ()) == -1) { + return -EBUSY; + } + pat.number = i; + pat.subkey = WF_ST_PATCH; + patp = &pat.hdr.p; + + if ((i = wavefront_find_free_sample ()) == -1) { + return -EBUSY; + } + samp.number = i; + samp.subkey = WF_ST_SAMPLE; + samp.size = guspatch.len; + sampp = &samp.hdr.s; + + prog.number = guspatch.instr_no; + progp = &prog.hdr.pr; + + /* Setup the patch structure */ + + patp->amplitude_bias=guspatch.volume; + patp->portamento=0; + patp->sample_number= samp.number & 0xff; + patp->sample_msb= samp.number>>8; + patp->pitch_bend= /*12*/ 0; + patp->mono=1; + patp->retrigger=1; + patp->nohold=(guspatch.mode & WAVE_SUSTAIN_ON) ? 0:1; + patp->frequency_bias=0; + patp->restart=0; + patp->reuse=0; + patp->reset_lfo=1; + patp->fm_src2=0; + patp->fm_src1=WF_MOD_MOD_WHEEL; + patp->am_src=WF_MOD_PRESSURE; + patp->am_amount=127; + patp->fc1_mod_amount=0; + patp->fc2_mod_amount=0; + patp->fm_amount1=0; + patp->fm_amount2=0; + patp->envelope1.attack_level=127; + patp->envelope1.decay1_level=127; + patp->envelope1.decay2_level=127; + patp->envelope1.sustain_level=127; + patp->envelope1.release_level=0; + patp->envelope2.attack_velocity=127; + patp->envelope2.attack_level=127; + patp->envelope2.decay1_level=127; + patp->envelope2.decay2_level=127; + patp->envelope2.sustain_level=127; + patp->envelope2.release_level=0; + patp->envelope2.attack_velocity=127; + patp->randomizer=0; + + /* Program for this patch */ + + progp->layer[0].patch_number= pat.number; /* XXX is this right ? */ + progp->layer[0].mute=1; + progp->layer[0].pan_or_mod=1; + progp->layer[0].pan=7; + progp->layer[0].mix_level=127 /* guspatch.volume */; + progp->layer[0].split_type=0; + progp->layer[0].split_point=0; + progp->layer[0].play_below=0; + + for (i = 1; i < 4; i++) { + progp->layer[i].mute=0; + } + + /* Sample data */ + + sampp->SampleResolution=((~guspatch.mode & WAVE_16_BITS)<<1); + + for (base_note=0; + note_to_freq (base_note) < guspatch.base_note; + base_note++); + + if ((guspatch.base_note-note_to_freq(base_note)) + >(note_to_freq(base_note)-guspatch.base_note)) + base_note++; + + printk(KERN_DEBUG "ref freq=%d,base note=%d\n", + guspatch.base_freq, + base_note); + + sampp->FrequencyBias = (29550 - log2_2048(guspatch.base_freq) + + base_note*171); + printk(KERN_DEBUG "Freq Bias is %d\n", sampp->FrequencyBias); + sampp->Loop=(guspatch.mode & WAVE_LOOPING) ? 1:0; + sampp->sampleStartOffset.Fraction=0; + sampp->sampleStartOffset.Integer=0; + sampp->loopStartOffset.Fraction=0; + sampp->loopStartOffset.Integer=guspatch.loop_start + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->loopEndOffset.Fraction=0; + sampp->loopEndOffset.Integer=guspatch.loop_end + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->sampleEndOffset.Fraction=0; + sampp->sampleEndOffset.Integer=guspatch.len >> (guspatch.mode&1); + sampp->Bidirectional=(guspatch.mode&WAVE_BIDIR_LOOP) ? 1:0; + sampp->Reverse=(guspatch.mode&WAVE_LOOP_BACK) ? 1:0; + + /* Now ship it down */ + + wavefront_send_sample (&samp, + (unsigned short *) &(addr)[sizeof_patch], + (guspatch.mode & WAVE_UNSIGNED) ? 1:0); + wavefront_send_patch (&pat); + wavefront_send_program (&prog); + + /* Now pan as best we can ... use the slave/internal MIDI device + number if it exists (since it talks to the WaveFront), or the + master otherwise. + */ + + if (dev.mididev > 0) { + midi_synth_controller (dev.mididev, guspatch.instr_no, 10, + ((guspatch.panning << 4) > 127) ? + 127 : (guspatch.panning << 4)); + } + + return(0); +} + +static int +wavefront_load_patch (const char *addr) + + +{ + wavefront_patch_info header; + + if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) - + sizeof(wavefront_any))) { + printk (KERN_WARNING LOGNAME "bad address for load patch.\n"); + return -(EINVAL); + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "download " + "Sample type: %d " + "Sample number: %d " + "Sample size: %d\n", + header.subkey, + header.number, + header.size); + + switch (header.subkey) { + case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ + + copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_sample)); + + return wavefront_send_sample (&header, header.dataptr, 0); + + case WF_ST_MULTISAMPLE: + + copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_multisample)); + + return wavefront_send_multisample (&header); + + + case WF_ST_ALIAS: + + copy_from_user ((unsigned char *) &header.hdr.a, + (unsigned char *) header.hdrptr, + sizeof (wavefront_alias)); + + return wavefront_send_alias (&header); + + case WF_ST_DRUM: + copy_from_user ((unsigned char *) &header.hdr.d, + (unsigned char *) header.hdrptr, + sizeof (wavefront_drum)); + + return wavefront_send_drum (&header); + + case WF_ST_PATCH: + copy_from_user ((unsigned char *) &header.hdr.p, + (unsigned char *) header.hdrptr, + sizeof (wavefront_patch)); + + return wavefront_send_patch (&header); + + case WF_ST_PROGRAM: + copy_from_user ((unsigned char *) &header.hdr.pr, + (unsigned char *) header.hdrptr, + sizeof (wavefront_program)); + + return wavefront_send_program (&header); + + default: + printk (KERN_ERR LOGNAME "unknown patch type %d.\n", + header.subkey); + return -(EINVAL); + } + + return 0; +} + +/*********************************************************************** +WaveFront: /dev/sequencer{,2} and other hardware-dependent interfaces +***********************************************************************/ + +static void +process_sample_hdr (UCHAR8 *buf) + +{ + wavefront_sample s; + UCHAR8 *ptr; + + ptr = buf; + + /* The board doesn't send us an exact copy of a "wavefront_sample" + in response to an Upload Sample Header command. Instead, we + have to convert the data format back into our data structure, + just as in the Download Sample command, where we have to do + something very similar in the reverse direction. + */ + + *((UINT32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; + + s.SampleResolution = *ptr & 0x3; + s.Loop = *ptr & 0x8; + s.Bidirectional = *ptr & 0x10; + s.Reverse = *ptr & 0x40; + + /* Now copy it back to where it came from */ + + memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); +} + +static int +wavefront_synth_control (int cmd, wavefront_control *wc) + +{ + unsigned char patchnumbuf[2]; + int i; + + DPRINT (WF_DEBUG_CMD, "synth control with " + "cmd 0x%x\n", wc->cmd); + + /* Pre-handling of or for various commands */ + + switch (wc->cmd) { + case WFC_DISABLE_INTERRUPTS: + printk (KERN_INFO LOGNAME "interrupts disabled.\n"); + outb (0x80|0x20, dev.control_port); + dev.interrupts_on = 0; + return 0; + + case WFC_ENABLE_INTERRUPTS: + printk (KERN_INFO LOGNAME "interrupts enabled.\n"); + outb (0x80|0x40|0x20, dev.control_port); + dev.interrupts_on = 1; + return 0; + + case WFC_INTERRUPT_STATUS: + wc->rbuf[0] = dev.interrupts_on; + return 0; + + case WFC_ROMSAMPLES_RDONLY: + dev.rom_samples_rdonly = wc->wbuf[0]; + wc->status = 0; + return 0; + + case WFC_IDENTIFY_SLOT_TYPE: + i = wc->wbuf[0] | (wc->wbuf[1] << 7); + if (i <0 || i >= WF_MAX_SAMPLE) { + printk (KERN_WARNING LOGNAME "invalid slot ID %d\n", + i); + wc->status = EINVAL; + return 0; + } + wc->rbuf[0] = dev.sample_status[i]; + wc->status = 0; + return 0; + + case WFC_DEBUG_DRIVER: + dev.debug = wc->wbuf[0]; + printk (KERN_INFO LOGNAME "debug = 0x%x\n", dev.debug); + return 0; + + case WFC_FX_IOCTL: + wffx_ioctl ((wavefront_fx_info *) &wc->wbuf[0]); + return 0; + + case WFC_UPLOAD_PATCH: + munge_int32 (*((UINT32 *) wc->wbuf), patchnumbuf, 2); + memcpy (wc->wbuf, patchnumbuf, 2); + break; + + case WFC_UPLOAD_MULTISAMPLE: + /* multisamples have to be handled differently, and + cannot be dealt with properly by wavefront_cmd() alone. + */ + wc->status = wavefront_fetch_multisample + ((wavefront_patch_info *) wc->rbuf); + return 0; + + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO LOGNAME "support for sample alias upload " + "being considered.\n"); + wc->status = EINVAL; + return -EINVAL; + } + + wc->status = wavefront_cmd (wc->cmd, wc->rbuf, wc->wbuf); + + /* Post-handling of certain commands. + + In particular, if the command was an upload, demunge the data + so that the user-level doesn't have to think about it. + */ + + if (wc->status == 0) { + switch (wc->cmd) { + /* intercept any freemem requests so that we know + we are always current with the user-level view + of things. + */ + + case WFC_REPORT_FREE_MEMORY: + dev.freemem = demunge_int32 (wc->rbuf, 4); + break; + + case WFC_UPLOAD_PATCH: + demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES); + break; + + case WFC_UPLOAD_PROGRAM: + demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES); + break; + + case WFC_UPLOAD_EDRUM_PROGRAM: + demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1); + break; + + case WFC_UPLOAD_SAMPLE_HEADER: + process_sample_hdr (wc->rbuf); + break; + + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO LOGNAME "support for " + "sample aliases still " + "being considered.\n"); + break; + + case WFC_VMIDI_OFF: + if (virtual_midi_disable () < 0) { + return -(EIO); + } + break; + + case WFC_VMIDI_ON: + if (virtual_midi_enable () < 0) { + return -(EIO); + } + break; + } + } + + return 0; +} + + +/***********************************************************************/ +/* WaveFront: Linux file system interface (for access via raw synth) */ +/***********************************************************************/ + +static int +wavefront_open (struct inode *inode, struct file *file) +{ + /* XXX fix me */ + dev.opened = file->f_flags; + return 0; +} + +static int +wavefront_release(struct inode *inode, struct file *file) +{ + lock_kernel(); + dev.opened = 0; + dev.debug = 0; + unlock_kernel(); + return 0; +} + +static int +wavefront_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + wavefront_control wc; + int err; + + switch (cmd) { + + case WFCTL_WFCMD: + copy_from_user (&wc, (void *) arg, sizeof (wc)); + + if ((err = wavefront_synth_control (cmd, &wc)) == 0) { + copy_to_user ((void *) arg, &wc, sizeof (wc)); + } + + return err; + + case WFCTL_LOAD_SPP: + return wavefront_load_patch ((const char *) arg); + + default: + printk (KERN_WARNING LOGNAME "invalid ioctl %#x\n", cmd); + return -(EINVAL); + + } + return 0; +} + +static /*const*/ struct file_operations wavefront_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: wavefront_ioctl, + open: wavefront_open, + release: wavefront_release, +}; + + +/***********************************************************************/ +/* WaveFront: OSS installation and support interface */ +/***********************************************************************/ + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + +static struct synth_info wavefront_info = +{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT, + 0, 32, 0, 0, SYNTH_CAP_INPUT}; + +static int +wavefront_oss_open (int devno, int mode) + +{ + dev.opened = mode; + return 0; +} + +static void +wavefront_oss_close (int devno) + +{ + dev.opened = 0; + dev.debug = 0; + return; +} + +static int +wavefront_oss_ioctl (int devno, unsigned int cmd, caddr_t arg) + +{ + wavefront_control wc; + int err; + + switch (cmd) { + case SNDCTL_SYNTH_INFO: + if(copy_to_user(&((char *) arg)[0], &wavefront_info, + sizeof (wavefront_info))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_RESETSAMPLES: +// printk (KERN_WARNING LOGNAME "driver cannot reset samples.\n"); + return 0; /* don't force an error */ + + case SNDCTL_SEQ_PERCMODE: + return 0; /* don't force an error */ + + case SNDCTL_SYNTH_MEMAVL: + if ((dev.freemem = wavefront_freemem ()) < 0) { + printk (KERN_ERR LOGNAME "cannot get memory size\n"); + return -EIO; + } else { + return dev.freemem; + } + break; + + case SNDCTL_SYNTH_CONTROL: + if(copy_from_user (&wc, arg, sizeof (wc))) + err = -EFAULT; + else if ((err = wavefront_synth_control (cmd, &wc)) == 0) { + if(copy_to_user (arg, &wc, sizeof (wc))) + err = -EFAULT; + } + + return err; + + default: + return -(EINVAL); + } +} + +int +wavefront_oss_load_patch (int devno, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + + if (format == SYSEX_PATCH) { /* Handled by midi_synth.c */ + if (midi_load_patch == NULL) { + printk (KERN_ERR LOGNAME + "SYSEX not loadable: " + "no midi patch loader!\n"); + return -(EINVAL); + } + + return midi_load_patch (devno, format, addr, + offs, count, pmgr_flag); + + } else if (format == GUS_PATCH) { + return wavefront_load_gus_patch (devno, format, + addr, offs, count, pmgr_flag); + + } else if (format != WAVEFRONT_PATCH) { + printk (KERN_ERR LOGNAME "unknown patch format %d\n", format); + return -(EINVAL); + } + + if (count < sizeof (wavefront_patch_info)) { + printk (KERN_ERR LOGNAME "sample header too short\n"); + return -(EINVAL); + } + + /* "addr" points to a user-space wavefront_patch_info */ + + return wavefront_load_patch (addr); +} + +static struct synth_operations wavefront_operations = +{ + owner: THIS_MODULE, + id: "WaveFront", + info: &wavefront_info, + midi_dev: 0, + synth_type: SYNTH_TYPE_SAMPLE, + synth_subtype: SAMPLE_TYPE_WAVEFRONT, + open: wavefront_oss_open, + close: wavefront_oss_close, + ioctl: wavefront_oss_ioctl, + kill_note: midi_synth_kill_note, + start_note: midi_synth_start_note, + set_instr: midi_synth_set_instr, + reset: midi_synth_reset, + load_patch: midi_synth_load_patch, + aftertouch: midi_synth_aftertouch, + controller: midi_synth_controller, + panning: midi_synth_panning, + bender: midi_synth_bender, + setup_voice: midi_synth_setup_voice +}; +#endif /* OSS_SUPPORT_SEQ */ + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_STATIC_INSTALL + +static void __init attach_wavefront (struct address_info *hw_config) +{ + (void) install_wavefront (); +} + +static int __init probe_wavefront (struct address_info *hw_config) +{ + return !detect_wavefront (hw_config->irq, hw_config->io_base); +} + +static void __exit unload_wavefront (struct address_info *hw_config) +{ + (void) uninstall_wavefront (); +} + +#endif /* OSS_SUPPORT_STATIC_INSTALL */ + +/***********************************************************************/ +/* WaveFront: Linux modular sound kernel installation interface */ +/***********************************************************************/ + +void +wavefrontintr (int irq, void *dev_id, struct pt_regs *dummy) +{ + struct wf_config *hw = dev_id; + + /* + Some comments on interrupts. I attempted a version of this + driver that used interrupts throughout the code instead of + doing busy and/or sleep-waiting. Alas, it appears that once + the Motorola firmware is downloaded, the card *never* + generates an RX interrupt. These are successfully generated + during firmware loading, and after that wavefront_status() + reports that an interrupt is pending on the card from time + to time, but it never seems to be delivered to this + driver. Note also that wavefront_status() continues to + report that RX interrupts are enabled, suggesting that I + didn't goof up and disable them by mistake. + + Thus, I stepped back to a prior version of + wavefront_wait(), the only place where this really + matters. Its sad, but I've looked through the code to check + on things, and I really feel certain that the Motorola + firmware prevents RX-ready interrupts. + */ + + if ((wavefront_status() & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) { + return; + } + + hw->irq_ok = 1; + hw->irq_cnt++; + wake_up_interruptible (&hw->interrupt_sleeper); +} + +/* STATUS REGISTER + +0 Host Rx Interrupt Enable (1=Enabled) +1 Host Rx Register Full (1=Full) +2 Host Rx Interrupt Pending (1=Interrupt) +3 Unused +4 Host Tx Interrupt (1=Enabled) +5 Host Tx Register empty (1=Empty) +6 Host Tx Interrupt Pending (1=Interrupt) +7 Unused +*/ + +int +wavefront_interrupt_bits (int irq) + +{ + int bits; + + switch (irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + printk (KERN_WARNING LOGNAME "invalid IRQ %d\n", irq); + bits = -1; + } + + return bits; +} + +void +wavefront_should_cause_interrupt (int val, int port, int timeout) + +{ + unsigned long flags; + + save_flags (flags); + cli(); + dev.irq_ok = 0; + outb (val,port); + interruptible_sleep_on_timeout (&dev.interrupt_sleeper, timeout); + restore_flags (flags); +} + +static int __init wavefront_hw_reset (void) +{ + int bits; + int hwv[2]; + unsigned long irq_mask; + short reported_irq; + + /* IRQ already checked in init_module() */ + + bits = wavefront_interrupt_bits (dev.irq); + + printk (KERN_DEBUG LOGNAME "autodetecting WaveFront IRQ\n"); + + sti (); + + irq_mask = probe_irq_on (); + + outb (0x0, dev.control_port); + outb (0x80 | 0x40 | bits, dev.data_port); + wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1, + dev.control_port, + (reset_time*HZ)/100); + + reported_irq = probe_irq_off (irq_mask); + + if (reported_irq != dev.irq) { + if (reported_irq == 0) { + printk (KERN_ERR LOGNAME + "No unassigned interrupts detected " + "after h/w reset\n"); + } else if (reported_irq < 0) { + printk (KERN_ERR LOGNAME + "Multiple unassigned interrupts detected " + "after h/w reset\n"); + } else { + printk (KERN_ERR LOGNAME "autodetected IRQ %d not the " + "value provided (%d)\n", reported_irq, + dev.irq); + } + dev.irq = -1; + return 1; + } else { + printk (KERN_INFO LOGNAME "autodetected IRQ at %d\n", + reported_irq); + } + + if (request_irq (dev.irq, wavefrontintr, + SA_INTERRUPT|SA_SHIRQ, + "wavefront synth", &dev) < 0) { + printk (KERN_WARNING LOGNAME "IRQ %d not available!\n", + dev.irq); + return 1; + } + + /* try reset of port */ + + outb (0x0, dev.control_port); + + /* At this point, the board is in reset, and the H/W initialization + register is accessed at the same address as the data port. + + Bit 7 - Enable IRQ Driver + 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs + 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. + + Bit 6 - MIDI Interface Select + + 0 - Use the MIDI Input from the 26-pin WaveBlaster + compatible header as the serial MIDI source + 1 - Use the MIDI Input from the 9-pin D connector as the + serial MIDI source. + + Bits 5:3 - IRQ Selection + 0 0 0 - IRQ 2/9 + 0 0 1 - IRQ 5 + 0 1 0 - IRQ 12 + 0 1 1 - IRQ 15 + 1 0 0 - Reserved + 1 0 1 - Reserved + 1 1 0 - Reserved + 1 1 1 - Reserved + + Bits 2:1 - Reserved + Bit 0 - Disable Boot ROM + 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM + 1 - memory accesses to 03FC30-03FFFFH are directed to external + storage. + + */ + + /* configure hardware: IRQ, enable interrupts, + plus external 9-pin MIDI interface selected + */ + + outb (0x80 | 0x40 | bits, dev.data_port); + + /* CONTROL REGISTER + + 0 Host Rx Interrupt Enable (1=Enabled) 0x1 + 1 Unused 0x2 + 2 Unused 0x4 + 3 Unused 0x8 + 4 Host Tx Interrupt Enable 0x10 + 5 Mute (0=Mute; 1=Play) 0x20 + 6 Master Interrupt Enable (1=Enabled) 0x40 + 7 Master Reset (0=Reset; 1=Run) 0x80 + + Take us out of reset, mute output, master + TX + RX interrupts on. + + We'll get an interrupt presumably to tell us that the TX + register is clear. + */ + + wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1, + dev.control_port, + (reset_time*HZ)/100); + + /* Note: data port is now the data port, not the h/w initialization + port. + */ + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "intr not received after h/w un-reset.\n"); + goto gone_bad; + } + + dev.interrupts_on = 1; + + /* Note: data port is now the data port, not the h/w initialization + port. + + At this point, only "HW VERSION" or "DOWNLOAD OS" commands + will work. So, issue one of them, and wait for TX + interrupt. This can take a *long* time after a cold boot, + while the ISC ROM does its RAM test. The SDK says up to 4 + seconds - with 12MB of RAM on a Tropez+, it takes a lot + longer than that (~16secs). Note that the card understands + the difference between a warm and a cold boot, so + subsequent ISC2115 reboots (say, caused by module + reloading) will get through this much faster. + + XXX Interesting question: why is no RX interrupt received first ? + */ + + wavefront_should_cause_interrupt(WFC_HARDWARE_VERSION, + dev.data_port, ramcheck_time*HZ); + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "post-RAM-check interrupt not received.\n"); + goto gone_bad; + } + + if (!wavefront_wait (STAT_CAN_READ)) { + printk (KERN_WARNING LOGNAME + "no response to HW version cmd.\n"); + goto gone_bad; + } + + if ((hwv[0] = wavefront_read ()) == -1) { + printk (KERN_WARNING LOGNAME + "board not responding correctly.\n"); + goto gone_bad; + } + + if (hwv[0] == 0xFF) { /* NAK */ + + /* Board's RAM test failed. Try to read error code, + and tell us about it either way. + */ + + if ((hwv[0] = wavefront_read ()) == -1) { + printk (KERN_WARNING LOGNAME "on-board RAM test failed " + "(bad error code).\n"); + } else { + printk (KERN_WARNING LOGNAME "on-board RAM test failed " + "(error code: 0x%x).\n", + hwv[0]); + } + goto gone_bad; + } + + /* We're OK, just get the next byte of the HW version response */ + + if ((hwv[1] = wavefront_read ()) == -1) { + printk (KERN_WARNING LOGNAME "incorrect h/w response.\n"); + goto gone_bad; + } + + printk (KERN_INFO LOGNAME "hardware version %d.%d\n", + hwv[0], hwv[1]); + + return 0; + + + gone_bad: + if (dev.irq >= 0) { + free_irq (dev.irq, &dev); + dev.irq = -1; + } + return (1); +} + +static int __init detect_wavefront (int irq, int io_base) +{ + unsigned char rbuf[4], wbuf[4]; + + /* TB docs say the device takes up 8 ports, but we know that + if there is an FX device present (i.e. a Tropez+) it really + consumes 16. + */ + + if (check_region (io_base, 16)) { + printk (KERN_ERR LOGNAME "IO address range 0x%x - 0x%x " + "already in use - ignored\n", dev.base, + dev.base+15); + return -1; + } + + dev.irq = irq; + dev.base = io_base; + dev.israw = 0; + dev.debug = debug_default; + dev.interrupts_on = 0; + dev.irq_cnt = 0; + dev.rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */ + + if (wavefront_cmd (WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { + + dev.fw_version[0] = rbuf[0]; + dev.fw_version[1] = rbuf[1]; + printk (KERN_INFO LOGNAME + "firmware %d.%d already loaded.\n", + rbuf[0], rbuf[1]); + + /* check that a command actually works */ + + if (wavefront_cmd (WFC_HARDWARE_VERSION, + rbuf, wbuf) == 0) { + dev.hw_version[0] = rbuf[0]; + dev.hw_version[1] = rbuf[1]; + } else { + printk (KERN_WARNING LOGNAME "not raw, but no " + "hardware version!\n"); + return 0; + } + + if (!wf_raw) { + return 1; + } else { + printk (KERN_INFO LOGNAME + "reloading firmware anyway.\n"); + dev.israw = 1; + } + + } else { + + dev.israw = 1; + printk (KERN_INFO LOGNAME + "no response to firmware probe, assume raw.\n"); + + } + + init_waitqueue_head (&dev.interrupt_sleeper); + + if (wavefront_hw_reset ()) { + printk (KERN_WARNING LOGNAME "hardware reset failed\n"); + return 0; + } + + /* Check for FX device, present only on Tropez+ */ + + dev.has_fx = (detect_wffx () == 0); + + return 1; +} + +#include "os.h" +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include + +static int errno; + +static int +wavefront_download_firmware (char *path) + +{ + unsigned char section[WF_SECTION_MAX]; + char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ + int section_cnt_downloaded = 0; + int fd; + int c; + int i; + mm_segment_t fs; + + /* This tries to be a bit cleverer than the stuff Alan Cox did for + the generic sound firmware, in that it actually knows + something about the structure of the Motorola firmware. In + particular, it uses a version that has been stripped of the + 20K of useless header information, and had section lengths + added, making it possible to load the entire OS without any + [kv]malloc() activity, since the longest entity we ever read is + 42 bytes (well, WF_SECTION_MAX) long. + */ + + fs = get_fs(); + set_fs (get_ds()); + + if ((fd = open (path, 0, 0)) < 0) { + printk (KERN_WARNING LOGNAME "Unable to load \"%s\".\n", + path); + return 1; + } + + while (1) { + int x; + + if ((x = read (fd, §ion_length, sizeof (section_length))) != + sizeof (section_length)) { + printk (KERN_ERR LOGNAME "firmware read error.\n"); + goto failure; + } + + if (section_length == 0) { + break; + } + + if (read (fd, section, section_length) != section_length) { + printk (KERN_ERR LOGNAME "firmware section " + "read error.\n"); + goto failure; + } + + /* Send command */ + + if (wavefront_write (WFC_DOWNLOAD_OS)) { + goto failure; + } + + for (i = 0; i < section_length; i++) { + if (wavefront_write (section[i])) { + goto failure; + } + } + + /* get ACK */ + + if (wavefront_wait (STAT_CAN_READ)) { + + if ((c = inb (dev.data_port)) != WF_ACK) { + + printk (KERN_ERR LOGNAME "download " + "of section #%d not " + "acknowledged, ack = 0x%x\n", + section_cnt_downloaded + 1, c); + goto failure; + + } + + } else { + printk (KERN_ERR LOGNAME "time out for firmware ACK.\n"); + goto failure; + } + + } + + close (fd); + set_fs (fs); + return 0; + + failure: + close (fd); + set_fs (fs); + printk (KERN_ERR "\nWaveFront: firmware download failed!!!\n"); + return 1; +} + +static int __init wavefront_config_midi (void) +{ + unsigned char rbuf[4], wbuf[4]; + + if (detect_wf_mpu (dev.irq, dev.base) < 0) { + printk (KERN_WARNING LOGNAME + "could not find working MIDI device\n"); + return -1; + } + + if ((dev.mididev = install_wf_mpu ()) < 0) { + printk (KERN_WARNING LOGNAME + "MIDI interfaces not configured\n"); + return -1; + } + + /* Route external MIDI to WaveFront synth (by default) */ + + if (wavefront_cmd (WFC_MISYNTH_ON, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "cannot enable MIDI-IN to synth routing.\n"); + /* XXX error ? */ + } + + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + /* Get the regular MIDI patch loading function, so we can + use it if we ever get handed a SYSEX patch. This is + unlikely, because its so damn slow, but we may as well + leave this functionality from maui.c behind, since it + could be useful for sequencer applications that can + only use MIDI to do patch loading. + */ + + if (midi_devs[dev.mididev]->converter != NULL) { + midi_load_patch = midi_devs[dev.mididev]->converter->load_patch; + midi_devs[dev.mididev]->converter->load_patch = + &wavefront_oss_load_patch; + } + +#endif /* OSS_SUPPORT_SEQ */ + + /* Turn on Virtual MIDI, but first *always* turn it off, + since otherwise consectutive reloads of the driver will + never cause the hardware to generate the initial "internal" or + "external" source bytes in the MIDI data stream. This + is pretty important, since the internal hardware generally will + be used to generate none or very little MIDI output, and + thus the only source of MIDI data is actually external. Without + the switch bytes, the driver will think it all comes from + the internal interface. Duh. + */ + + if (wavefront_cmd (WFC_VMIDI_OFF, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "virtual MIDI mode not disabled\n"); + return 0; /* We're OK, but missing the external MIDI dev */ + } + + if ((dev.ext_mididev = virtual_midi_enable ()) < 0) { + printk (KERN_WARNING LOGNAME "no virtual MIDI access.\n"); + } else { + if (wavefront_cmd (WFC_VMIDI_ON, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "cannot enable virtual MIDI mode.\n"); + virtual_midi_disable (); + } + } + + return 0; +} + +static int __init wavefront_do_reset (int atboot) +{ + char voices[1]; + + if (!atboot && wavefront_hw_reset ()) { + printk (KERN_WARNING LOGNAME "hw reset failed.\n"); + goto gone_bad; + } + + if (dev.israw) { + if (wavefront_download_firmware (ospath)) { + goto gone_bad; + } + + dev.israw = 0; + + /* Wait for the OS to get running. The protocol for + this is non-obvious, and was determined by + using port-IO tracing in DOSemu and some + experimentation here. + + Rather than using timed waits, use interrupts creatively. + */ + + wavefront_should_cause_interrupt (WFC_NOOP, + dev.data_port, + (osrun_time*HZ)); + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "no post-OS interrupt.\n"); + goto gone_bad; + } + + /* Now, do it again ! */ + + wavefront_should_cause_interrupt (WFC_NOOP, + dev.data_port, (10*HZ)); + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "no post-OS interrupt(2).\n"); + goto gone_bad; + } + + /* OK, no (RX/TX) interrupts any more, but leave mute + in effect. + */ + + outb (0x80|0x40, dev.control_port); + + /* No need for the IRQ anymore */ + + free_irq (dev.irq, &dev); + + } + + if (dev.has_fx && fx_raw) { + wffx_init (); + } + + /* SETUPSND.EXE asks for sample memory config here, but since i + have no idea how to interpret the result, we'll forget + about it. + */ + + if ((dev.freemem = wavefront_freemem ()) < 0) { + goto gone_bad; + } + + printk (KERN_INFO LOGNAME "available DRAM %dk\n", dev.freemem / 1024); + + if (wavefront_write (0xf0) || + wavefront_write (1) || + (wavefront_read () < 0)) { + dev.debug = 0; + printk (KERN_WARNING LOGNAME "MPU emulation mode not set.\n"); + goto gone_bad; + } + + voices[0] = 32; + + if (wavefront_cmd (WFC_SET_NVOICES, 0, voices)) { + printk (KERN_WARNING LOGNAME + "cannot set number of voices to 32.\n"); + goto gone_bad; + } + + + return 0; + + gone_bad: + /* reset that sucker so that it doesn't bother us. */ + + outb (0x0, dev.control_port); + dev.interrupts_on = 0; + if (dev.irq >= 0) { + free_irq (dev.irq, &dev); + } + return 1; +} + +static int __init wavefront_init (int atboot) +{ + int samples_are_from_rom; + + if (dev.israw) { + samples_are_from_rom = 1; + } else { + /* XXX is this always true ? */ + samples_are_from_rom = 0; + } + + if (dev.israw || fx_raw) { + if (wavefront_do_reset (atboot)) { + return -1; + } + } + + wavefront_get_sample_status (samples_are_from_rom); + wavefront_get_program_status (); + wavefront_get_patch_status (); + + /* Start normal operation: unreset, master interrupt enabled, no mute + */ + + outb (0x80|0x40|0x20, dev.control_port); + + return (0); +} + +static int __init install_wavefront (void) + +{ + if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1)) < 0) { + printk (KERN_ERR LOGNAME "cannot register raw synth\n"); + return -1; + } + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + if ((dev.oss_dev = sound_alloc_synthdev()) == -1) { + printk (KERN_ERR LOGNAME "Too many sequencers\n"); + return -1; + } else { + synth_devs[dev.oss_dev] = &wavefront_operations; + } +#endif /* OSS_SUPPORT_SEQ */ + + if (wavefront_init (1) < 0) { + printk (KERN_WARNING LOGNAME "initialization failed.\n"); + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + sound_unload_synthdev (dev.oss_dev); +#endif /* OSS_SUPPORT_SEQ */ + + return -1; + } + + request_region (dev.base+2, 6, "wavefront synth"); + + if (dev.has_fx) { + request_region (dev.base+8, 8, "wavefront fx"); + } + + if (wavefront_config_midi ()) { + printk (KERN_WARNING LOGNAME "could not initialize MIDI.\n"); + } + + return dev.oss_dev; +} + +static void __exit uninstall_wavefront (void) +{ + /* the first two i/o addresses are freed by the wf_mpu code */ + release_region (dev.base+2, 6); + + if (dev.has_fx) { + release_region (dev.base+8, 8); + } + + unregister_sound_synth (dev.synth_dev); + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + sound_unload_synthdev (dev.oss_dev); +#endif /* OSS_SUPPORT_SEQ */ + uninstall_wf_mpu (); +} + +/***********************************************************************/ +/* WaveFront FX control */ +/***********************************************************************/ + +#include "yss225.h" + +/* Control bits for the Load Control Register + */ + +#define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ +#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ +#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ + +static int +wffx_idle (void) + +{ + int i; + unsigned int x = 0x80; + + for (i = 0; i < 1000; i++) { + x = inb (dev.fx_status); + if ((x & 0x80) == 0) { + break; + } + } + + if (x & 0x80) { + printk (KERN_ERR LOGNAME "FX device never idle.\n"); + return 0; + } + + return (1); +} + +int __init detect_wffx (void) +{ + /* This is a crude check, but its the best one I have for now. + Certainly on the Maui and the Tropez, wffx_idle() will + report "never idle", which suggests that this test should + work OK. + */ + + if (inb (dev.fx_status) & 0x80) { + printk (KERN_INFO LOGNAME "Hmm, probably a Maui or Tropez.\n"); + return -1; + } + + return 0; +} + +int __init attach_wffx (void) +{ + if ((dev.fx_mididev = sound_alloc_mididev ()) < 0) { + printk (KERN_WARNING LOGNAME "cannot install FX Midi driver\n"); + return -1; + } + + return 0; +} + +void +wffx_mute (int onoff) + +{ + if (!wffx_idle()) { + return; + } + + outb (onoff ? 0x02 : 0x00, dev.fx_op); +} + +static int +wffx_memset (int page, + int addr, int cnt, unsigned short *data) +{ + if (page < 0 || page > 7) { + printk (KERN_ERR LOGNAME "FX memset: " + "page must be >= 0 and <= 7\n"); + return -(EINVAL); + } + + if (addr < 0 || addr > 0x7f) { + printk (KERN_ERR LOGNAME "FX memset: " + "addr must be >= 0 and <= 7f\n"); + return -(EINVAL); + } + + if (cnt == 1) { + + outb (FX_LSB_TRANSFER, dev.fx_lcr); + outb (page, dev.fx_dsp_page); + outb (addr, dev.fx_dsp_addr); + outb ((data[0] >> 8), dev.fx_dsp_msb); + outb ((data[0] & 0xff), dev.fx_dsp_lsb); + + printk (KERN_INFO LOGNAME "FX: addr %d:%x set to 0x%x\n", + page, addr, data[0]); + + } else { + int i; + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (page, dev.fx_dsp_page); + outb (addr, dev.fx_dsp_addr); + + for (i = 0; i < cnt; i++) { + outb ((data[i] >> 8), dev.fx_dsp_msb); + outb ((data[i] & 0xff), dev.fx_dsp_lsb); + if (!wffx_idle ()) { + break; + } + } + + if (i != cnt) { + printk (KERN_WARNING LOGNAME + "FX memset " + "(0x%x, 0x%x, 0x%x, %d) incomplete\n", + page, addr, (int) data, cnt); + return -(EIO); + } + } + + return 0; +} + +static int +wffx_ioctl (wavefront_fx_info *r) + +{ + unsigned short page_data[256]; + unsigned short *pd; + + switch (r->request) { + case WFFX_MUTE: + wffx_mute (r->data[0]); + return 0; + + case WFFX_MEMSET: + + if (r->data[2] <= 0) { + printk (KERN_ERR LOGNAME "cannot write " + "<= 0 bytes to FX\n"); + return -(EINVAL); + } else if (r->data[2] == 1) { + pd = (unsigned short *) &r->data[3]; + } else { + if (r->data[2] > sizeof (page_data)) { + printk (KERN_ERR LOGNAME "cannot write " + "> 255 bytes to FX\n"); + return -(EINVAL); + } + copy_from_user (page_data, (unsigned char *) r->data[3], + r->data[2]); + pd = page_data; + } + + return wffx_memset (r->data[0], /* page */ + r->data[1], /* addr */ + r->data[2], /* cnt */ + pd); + + default: + printk (KERN_WARNING LOGNAME + "FX: ioctl %d not yet supported\n", + r->request); + return -(EINVAL); + } +} + +/* YSS225 initialization. + + This code was developed using DOSEMU. The Turtle Beach SETUPSND + utility was run with I/O tracing in DOSEMU enabled, and a reconstruction + of the port I/O done, using the Yamaha faxback document as a guide + to add more logic to the code. Its really pretty weird. + + There was an alternative approach of just dumping the whole I/O + sequence as a series of port/value pairs and a simple loop + that output it. However, I hope that eventually I'll get more + control over what this code does, and so I tried to stick with + a somewhat "algorithmic" approach. +*/ + +static int __init wffx_init (void) +{ + int i; + int j; + + /* Set all bits for all channels on the MOD unit to zero */ + /* XXX But why do this twice ? */ + + for (j = 0; j < 2; j++) { + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle ()) { + return (-1); + } + + outb (i, dev.fx_mod_addr); + outb (0x0, dev.fx_mod_data); + } + } + + if (!wffx_idle()) return (-1); + outb (0x02, dev.fx_op); /* mute on */ + + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x44, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x42, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x43, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x7c, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x7e, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x46, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x49, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x47, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x4a, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + + /* either because of stupidity by TB's programmers, or because it + actually does something, rezero the MOD page. + */ + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle ()) { + return (-1); + } + + outb (i, dev.fx_mod_addr); + outb (0x0, dev.fx_mod_data); + } + /* load page zero */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x00, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero); i += 2) { + outb (page_zero[i], dev.fx_dsp_msb); + outb (page_zero[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Now load page one */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x01, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_one); i += 2) { + outb (page_one[i], dev.fx_dsp_msb); + outb (page_one[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x02, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_two); i++) { + outb (page_two[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x03, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_three); i++) { + outb (page_three[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x04, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_four); i++) { + outb (page_four[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Load memory area (page six) */ + + outb (FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x06, dev.fx_dsp_page); + + for (i = 0; i < sizeof (page_six); i += 3) { + outb (page_six[i], dev.fx_dsp_addr); + outb (page_six[i+1], dev.fx_dsp_msb); + outb (page_six[i+2], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x07, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven); i += 2) { + outb (page_seven[i], dev.fx_dsp_msb); + outb (page_seven[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Now setup the MOD area. We do this algorithmically in order to + save a little data space. It could be done in the same fashion + as the "pages". + */ + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev.fx_mod_addr); + outb (i, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0x02, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0xb0; i <= 0xbf; i++) { + outb (i, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0xf0; i <= 0xff; i++) { + outb (i, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0x10; i <= 0x1d; i++) { + outb (i, dev.fx_mod_addr); + outb (0xff, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x1e, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x1f; i <= 0x2d; i++) { + outb (i, dev.fx_mod_addr); + outb (0xff, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x2e, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x2f; i <= 0x3e; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x3f, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x40; i <= 0x4d; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x4e, dev.fx_mod_addr); + outb (0x0e, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0x4f, dev.fx_mod_addr); + outb (0x0e, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + + for (i = 0x50; i <= 0x6b; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x6c, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + outb (0x6d, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + outb (0x6e, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + outb (0x6f, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x70; i <= 0x7f; i++) { + outb (i, dev.fx_mod_addr); + outb (0xc0, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0x80; i <= 0xaf; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0xc0; i <= 0xdd; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0xde, dev.fx_mod_addr); + outb (0x10, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0xdf, dev.fx_mod_addr); + outb (0x10, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0xe0; i <= 0xef; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev.fx_mod_addr); + outb (i, dev.fx_mod_data); + outb (0x02, dev.fx_mod_addr); + outb (0x01, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x02, dev.fx_op); /* mute on */ + + /* Now set the coefficients and so forth for the programs above */ + + for (i = 0; i < sizeof (coefficients); i += 4) { + outb (coefficients[i], dev.fx_dsp_page); + outb (coefficients[i+1], dev.fx_dsp_addr); + outb (coefficients[i+2], dev.fx_dsp_msb); + outb (coefficients[i+3], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Some settings (?) that are too small to bundle into loops */ + + if (!wffx_idle()) return (-1); + outb (0x1e, dev.fx_mod_addr); + outb (0x14, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0xde, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0xdf, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + + /* some more coefficients */ + + if (!wffx_idle()) return (-1); + outb (0x06, dev.fx_dsp_page); + outb (0x78, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x40, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x03, dev.fx_dsp_addr); + outb (0x0f, dev.fx_dsp_msb); + outb (0xff, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x0b, dev.fx_dsp_addr); + outb (0x0f, dev.fx_dsp_msb); + outb (0xff, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x02, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x0a, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x46, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x49, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + + /* Now, for some strange reason, lets reload every page + and all the coefficients over again. I have *NO* idea + why this is done. I do know that no sound is produced + is this phase is omitted. + */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x00, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero_v2); i += 2) { + outb (page_zero_v2[i], dev.fx_dsp_msb); + outb (page_zero_v2[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x01, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_one_v2); i += 2) { + outb (page_one_v2[i], dev.fx_dsp_msb); + outb (page_one_v2[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + if (!wffx_idle()) return (-1); + if (!wffx_idle()) return (-1); + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x02, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_two_v2); i++) { + outb (page_two_v2[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x03, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_three_v2); i++) { + outb (page_three_v2[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x04, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_four_v2); i++) { + outb (page_four_v2[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x06, dev.fx_dsp_page); + + /* Page six v.2 is algorithmic */ + + for (i = 0x10; i <= 0x3e; i += 2) { + outb (i, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x07, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven_v2); i += 2) { + outb (page_seven_v2[i], dev.fx_dsp_msb); + outb (page_seven_v2[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + for (i = 0x00; i < sizeof(mod_v2); i += 2) { + outb (mod_v2[i], dev.fx_mod_addr); + outb (mod_v2[i+1], dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0; i < sizeof (coefficients2); i += 4) { + outb (coefficients2[i], dev.fx_dsp_page); + outb (coefficients2[i+1], dev.fx_dsp_addr); + outb (coefficients2[i+2], dev.fx_dsp_msb); + outb (coefficients2[i+3], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + for (i = 0; i < sizeof (coefficients3); i += 2) { + int x; + + outb (0x07, dev.fx_dsp_page); + x = (i % 4) ? 0x4e : 0x4c; + outb (x, dev.fx_dsp_addr); + outb (coefficients3[i], dev.fx_dsp_msb); + outb (coefficients3[i+1], dev.fx_dsp_lsb); + } + + outb (0x00, dev.fx_op); /* mute off */ + if (!wffx_idle()) return (-1); + + return (0); +} + +static int io = -1; +static int irq = -1; + +MODULE_AUTHOR ("Paul Barton-Davis "); +MODULE_DESCRIPTION ("Turtle Beach WaveFront Linux Driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM (io,"i"); +MODULE_PARM (irq,"i"); + +static int __init init_wavfront (void) +{ + printk ("Turtle Beach WaveFront Driver\n" + "Copyright (C) by Hannu Solvainen, " + "Paul Barton-Davis 1993-1998.\n"); + + /* XXX t'would be lovely to ask the CS4232 for these values, eh ? */ + + if (io == -1 || irq == -1) { + printk (KERN_INFO LOGNAME "irq and io options must be set.\n"); + return -EINVAL; + } + + if (wavefront_interrupt_bits (irq) < 0) { + printk (KERN_INFO LOGNAME + "IRQ must be 9, 5, 12 or 15 (not %d)\n", irq); + return -ENODEV; + } + + if (detect_wavefront (irq, io) < 0) { + return -ENODEV; + } + + if (install_wavefront () < 0) { + return -EIO; + } + + return 0; +} + +static void __exit cleanup_wavfront (void) +{ + uninstall_wavefront (); +} + +module_init(init_wavfront); +module_exit(cleanup_wavfront); diff -Nru a/sound/oss/wf_midi.c b/sound/oss/wf_midi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/wf_midi.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,880 @@ +/* + * sound/wf_midi.c + * + * The low level driver for the WaveFront ICS2115 MIDI interface(s) + * Note that there is also an MPU-401 emulation (actually, a UART-401 + * emulation) on the CS4232 on the Tropez Plus. This code has nothing + * to do with that interface at all. + * + * The interface is essentially just a UART-401, but is has the + * interesting property of supporting what Turtle Beach called + * "Virtual MIDI" mode. In this mode, there are effectively *two* + * MIDI buses accessible via the interface, one that is routed + * solely to/from the external WaveFront synthesizer and the other + * corresponding to the pin/socket connector used to link external + * MIDI devices to the board. + * + * This driver fully supports this mode, allowing two distinct + * midi devices (/dev/midiNN and /dev/midiNN+1) to be used + * completely independently, giving 32 channels of MIDI routing, + * 16 to the WaveFront synth and 16 to the external MIDI bus. + * + * Switching between the two is accomplished externally by the driver + * using the two otherwise unused MIDI bytes. See the code for more details. + * + * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see wavefront.c) + * + * The main reason to turn off Virtual MIDI mode is when you want to + * tightly couple the WaveFront synth with an external MIDI + * device. You won't be able to distinguish the source of any MIDI + * data except via SysEx ID, but thats probably OK, since for the most + * part, the WaveFront won't be sending any MIDI data at all. + * + * The main reason to turn on Virtual MIDI Mode is to provide two + * completely independent 16-channel MIDI buses, one to the + * WaveFront and one to any external MIDI devices. Given the 32 + * voice nature of the WaveFront, its pretty easy to find a use + * for all 16 channels driving just that synth. + * + */ + +/* + * Copyright (C) by Paul Barton-Davis 1998 + * Some portions of this file are derived from work that is: + * + * CopyriGht (C) by Hannu Savolainen 1993-1996 + * + * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +#include +#include "sound_config.h" + +#include + +#ifdef MODULE + +struct wf_mpu_config { + int base; +#define DATAPORT(d) (d)->base +#define COMDPORT(d) (d)->base+1 +#define STATPORT(d) (d)->base+1 + + int irq; + int opened; + int devno; + int synthno; + int mode; +#define MODE_MIDI 1 +#define MODE_SYNTH 2 + + void (*inputintr) (int dev, unsigned char data); + char isvirtual; /* do virtual I/O stuff */ +}; + +static struct wf_mpu_config devs[2]; +static struct wf_mpu_config *phys_dev = &devs[0]; +static struct wf_mpu_config *virt_dev = &devs[1]; + +static void start_uart_mode (void); + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define UART_MODE_ON 0x3F + +static inline int wf_mpu_status (void) +{ + return inb (STATPORT (phys_dev)); +} + +static inline int input_avail (void) +{ + return !(wf_mpu_status() & INPUT_AVAIL); +} + +static inline int output_ready (void) +{ + return !(wf_mpu_status() & OUTPUT_READY); +} + +static inline int read_data (void) +{ + return inb (DATAPORT (phys_dev)); +} + +static inline void write_data (unsigned char byte) +{ + outb (byte, DATAPORT (phys_dev)); +} + +/* + * States for the input scanner (should be in dev_table.h) + */ + +#define MST_SYSMSG 100 /* System message (sysx etc). */ +#define MST_MTC 102 /* Midi Time Code (MTC) qframe msg */ +#define MST_SONGSEL 103 /* Song select */ +#define MST_SONGPOS 104 /* Song position pointer */ +#define MST_TIMED 105 /* Leading timing byte rcvd */ + +/* buffer space check for input scanner */ + +#define BUFTEST(mi) if (mi->m_ptr >= MI_MAX || mi->m_ptr < 0) \ +{printk(KERN_ERR "WF-MPU: Invalid buffer pointer %d/%d, s=%d\n", \ + mi->m_ptr, mi->m_left, mi->m_state);mi->m_ptr--;} + +static unsigned char len_tab[] = /* # of data bytes following a status + */ +{ + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ +}; + +static int +wf_mpu_input_scanner (int devno, int synthdev, unsigned char midic) + +{ + struct midi_input_info *mi = &midi_devs[devno]->in_info; + + switch (mi->m_state) { + case MST_INIT: + switch (midic) { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + break; + + case 0xfd: + /* XXX do something useful with this. If there is + an external MIDI timer (e.g. a hardware sequencer, + a useful timer can be derived ... + + For now, no timer support. + */ + break; + + case 0xfe: + return MPU_ACK; + break; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + break; + + case 0xf9: + break; + + case 0xff: + mi->m_state = MST_SYSMSG; + break; + + default: + if (midic <= 0xef) { + mi->m_state = MST_TIMED; + } + else + printk (KERN_ERR " ", + midic); + } + break; + + case MST_TIMED: + { + int msg = ((int) (midic & 0xf0) >> 4); + + mi->m_state = MST_DATA; + + if (msg < 8) { /* Data byte */ + + msg = ((int) (mi->m_prev_status & 0xf0) >> 4); + msg -= 8; + mi->m_left = len_tab[msg] - 1; + + mi->m_ptr = 2; + mi->m_buf[0] = mi->m_prev_status; + mi->m_buf[1] = midic; + + if (mi->m_left <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + } else if (msg == 0xf) { /* MPU MARK */ + + mi->m_state = MST_INIT; + + switch (midic) { + case 0xf8: + break; + + case 0xf9: + break; + + case 0xfc: + break; + + default: + break; + } + } else { + mi->m_prev_status = midic; + msg -= 8; + mi->m_left = len_tab[msg]; + + mi->m_ptr = 1; + mi->m_buf[0] = midic; + + if (mi->m_left <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + } + } + break; + + case MST_SYSMSG: + switch (midic) { + case 0xf0: + mi->m_state = MST_SYSEX; + break; + + case 0xf1: + mi->m_state = MST_MTC; + break; + + case 0xf2: + mi->m_state = MST_SONGPOS; + mi->m_ptr = 0; + break; + + case 0xf3: + mi->m_state = MST_SONGSEL; + break; + + case 0xf6: + mi->m_state = MST_INIT; + + /* + * Real time messages + */ + case 0xf8: + /* midi clock */ + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xfA: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFB: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFC: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFE: + /* active sensing */ + mi->m_state = MST_INIT; + break; + + case 0xff: + mi->m_state = MST_INIT; + break; + + default: + printk (KERN_ERR "unknown MIDI sysmsg %0x\n", midic); + mi->m_state = MST_INIT; + } + break; + + case MST_MTC: + mi->m_state = MST_INIT; + break; + + case MST_SYSEX: + if (midic == 0xf7) { + mi->m_state = MST_INIT; + } else { + /* XXX fix me */ + } + break; + + case MST_SONGPOS: + BUFTEST (mi); + mi->m_buf[mi->m_ptr++] = midic; + if (mi->m_ptr == 2) { + mi->m_state = MST_INIT; + mi->m_ptr = 0; + /* XXX need ext MIDI timer support */ + } + break; + + case MST_DATA: + BUFTEST (mi); + mi->m_buf[mi->m_ptr++] = midic; + if ((--mi->m_left) <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + break; + + default: + printk (KERN_ERR "Bad state %d ", mi->m_state); + mi->m_state = MST_INIT; + } + + return 1; +} + +void +wf_mpuintr (int irq, void *dev_id, struct pt_regs *dummy) + +{ + struct wf_mpu_config *physical_dev = dev_id; + static struct wf_mpu_config *input_dev = 0; + struct midi_input_info *mi = &midi_devs[physical_dev->devno]->in_info; + int n; + + if (!input_avail()) { /* not for us */ + return; + } + + if (mi->m_busy) return; + mi->m_busy = 1; + sti (); + + if (!input_dev) { + input_dev = physical_dev; + } + + n = 50; /* XXX why ? */ + + do { + unsigned char c = read_data (); + + if (phys_dev->isvirtual) { + + if (c == WF_EXTERNAL_SWITCH) { + input_dev = virt_dev; + continue; + } else if (c == WF_INTERNAL_SWITCH) { + input_dev = phys_dev; + continue; + } /* else just leave it as it is */ + + } else { + input_dev = phys_dev; + } + + if (input_dev->mode == MODE_SYNTH) { + + wf_mpu_input_scanner (input_dev->devno, + input_dev->synthno, c); + + } else if (input_dev->opened & OPEN_READ) { + + if (input_dev->inputintr) { + input_dev->inputintr (input_dev->devno, c); + } + } + + } while (input_avail() && n-- > 0); + + mi->m_busy = 0; +} + +static int +wf_mpu_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) + ) +{ + struct wf_mpu_config *devc; + + if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) + return -(ENXIO); + + if (phys_dev->devno == dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return -(EINVAL); + } + + if (devc->opened) { + return -(EBUSY); + } + + devc->mode = MODE_MIDI; + devc->opened = mode; + devc->synthno = 0; + + devc->inputintr = input; + return 0; +} + +static void +wf_mpu_close (int dev) +{ + struct wf_mpu_config *devc; + + if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) + return; + + if (phys_dev->devno == dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return; + } + + devc->mode = 0; + devc->inputintr = NULL; + devc->opened = 0; +} + +static int +wf_mpu_out (int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + static int lastoutdev = -1; + unsigned char switchch; + + if (phys_dev->isvirtual && lastoutdev != dev) { + + if (dev == phys_dev->devno) { + switchch = WF_INTERNAL_SWITCH; + } else if (dev == virt_dev->devno) { + switchch = WF_EXTERNAL_SWITCH; + } else { + printk (KERN_ERR "WF-MPU: bad device number %d", dev); + return (0); + } + + /* XXX fix me */ + + for (timeout = 30000; timeout > 0 && !output_ready (); + timeout--); + + save_flags (flags); + cli (); + + if (!output_ready ()) { + printk (KERN_WARNING "WF-MPU: Send switch " + "byte timeout\n"); + restore_flags (flags); + return 0; + } + + write_data (switchch); + restore_flags (flags); + } + + lastoutdev = dev; + + /* + * Sometimes it takes about 30000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + /* XXX fix me */ + + for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); + + save_flags (flags); + cli (); + if (!output_ready ()) { + printk (KERN_WARNING "WF-MPU: Send data timeout\n"); + restore_flags (flags); + return 0; + } + + write_data (midi_byte); + restore_flags (flags); + + return 1; +} + +static inline int wf_mpu_start_read (int dev) { + return 0; +} + +static inline int wf_mpu_end_read (int dev) { + return 0; +} + +static int wf_mpu_ioctl (int dev, unsigned cmd, caddr_t arg) +{ + printk (KERN_WARNING + "WF-MPU: Intelligent mode not supported by hardware.\n"); + return -(EINVAL); +} + +static int wf_mpu_buffer_status (int dev) +{ + return 0; +} + +static struct synth_operations wf_mpu_synth_operations[2]; +static struct midi_operations wf_mpu_midi_operations[2]; + +static struct midi_operations wf_mpu_midi_proto = +{ + owner: THIS_MODULE, + info: {"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, + in_info: {0}, /* in_info */ + open: wf_mpu_open, + close: wf_mpu_close, + ioctl: wf_mpu_ioctl, + outputc: wf_mpu_out, + start_read: wf_mpu_start_read, + end_read: wf_mpu_end_read, + buffer_status: wf_mpu_buffer_status, +}; + +static struct synth_info wf_mpu_synth_info_proto = +{"WaveFront MPU-401 interface", 0, + SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT}; + +static struct synth_info wf_mpu_synth_info[2]; + +static int +wf_mpu_synth_ioctl (int dev, + unsigned int cmd, caddr_t arg) +{ + int midi_dev; + int index; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) + return -(ENXIO); + + if (midi_dev == phys_dev->devno) { + index = 0; + } else if (phys_dev->isvirtual && midi_dev == virt_dev->devno) { + index = 1; + } else { + return -(EINVAL); + } + + switch (cmd) { + + case SNDCTL_SYNTH_INFO: + if(copy_to_user (&((char *) arg)[0], + &wf_mpu_synth_info[index], + sizeof (struct synth_info))) + return -EFAULT; + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + default: + return -EINVAL; + } +} + +static int +wf_mpu_synth_open (int dev, int mode) +{ + int midi_dev; + struct wf_mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) { + return -(ENXIO); + } + + if (phys_dev->devno == midi_dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return -(EINVAL); + } + + if (devc->opened) { + return -(EBUSY); + } + + devc->mode = MODE_SYNTH; + devc->synthno = dev; + devc->opened = mode; + devc->inputintr = NULL; + return 0; +} + +static void +wf_mpu_synth_close (int dev) +{ + int midi_dev; + struct wf_mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (phys_dev->devno == midi_dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return; + } + + devc->inputintr = NULL; + devc->opened = 0; + devc->mode = 0; +} + +#define _MIDI_SYNTH_C_ +#define MIDI_SYNTH_NAME "WaveFront (MIDI)" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct synth_operations wf_mpu_synth_proto = +{ + owner: THIS_MODULE, + id: "WaveFront (ICS2115)", + info: NULL, /* info field, filled in during configuration */ + midi_dev: 0, /* MIDI dev XXX should this be -1 ? */ + synth_type: SYNTH_TYPE_MIDI, + synth_subtype: SAMPLE_TYPE_WAVEFRONT, + open: wf_mpu_synth_open, + close: wf_mpu_synth_close, + ioctl: wf_mpu_synth_ioctl, + kill_note: midi_synth_kill_note, + start_note: midi_synth_start_note, + set_instr: midi_synth_set_instr, + reset: midi_synth_reset, + hw_control: midi_synth_hw_control, + load_patch: midi_synth_load_patch, + aftertouch: midi_synth_aftertouch, + controller: midi_synth_controller, + panning: midi_synth_panning, + bender: midi_synth_bender, + setup_voice: midi_synth_setup_voice, + send_sysex: midi_synth_send_sysex +}; + +static int +config_wf_mpu (struct wf_mpu_config *dev) + +{ + int is_external; + char *name; + int index; + + if (dev == phys_dev) { + name = "WaveFront internal MIDI"; + is_external = 0; + index = 0; + memcpy ((char *) &wf_mpu_synth_operations[index], + (char *) &wf_mpu_synth_proto, + sizeof (struct synth_operations)); + } else { + name = "WaveFront external MIDI"; + is_external = 1; + index = 1; + /* no synth operations for an external MIDI interface */ + } + + memcpy ((char *) &wf_mpu_synth_info[dev->devno], + (char *) &wf_mpu_synth_info_proto, + sizeof (struct synth_info)); + + strcpy (wf_mpu_synth_info[index].name, name); + + wf_mpu_synth_operations[index].midi_dev = dev->devno; + wf_mpu_synth_operations[index].info = &wf_mpu_synth_info[index]; + + memcpy ((char *) &wf_mpu_midi_operations[index], + (char *) &wf_mpu_midi_proto, + sizeof (struct midi_operations)); + + if (is_external) { + wf_mpu_midi_operations[index].converter = NULL; + } else { + wf_mpu_midi_operations[index].converter = + &wf_mpu_synth_operations[index]; + } + + strcpy (wf_mpu_midi_operations[index].info.name, name); + + midi_devs[dev->devno] = &wf_mpu_midi_operations[index]; + midi_devs[dev->devno]->in_info.m_busy = 0; + midi_devs[dev->devno]->in_info.m_state = MST_INIT; + midi_devs[dev->devno]->in_info.m_ptr = 0; + midi_devs[dev->devno]->in_info.m_left = 0; + midi_devs[dev->devno]->in_info.m_prev_status = 0; + + devs[index].opened = 0; + devs[index].mode = 0; + + return (0); +} + +int virtual_midi_enable (void) + +{ + if ((virt_dev->devno < 0) && + (virt_dev->devno = sound_alloc_mididev()) == -1) { + printk (KERN_ERR + "WF-MPU: too many midi devices detected\n"); + return -1; + } + + config_wf_mpu (virt_dev); + + phys_dev->isvirtual = 1; + return virt_dev->devno; +} + +int +virtual_midi_disable (void) + +{ + unsigned long flags; + + save_flags (flags); + cli(); + + wf_mpu_close (virt_dev->devno); + /* no synth on virt_dev, so no need to call wf_mpu_synth_close() */ + phys_dev->isvirtual = 0; + + restore_flags (flags); + + return 0; +} + +int __init detect_wf_mpu (int irq, int io_base) +{ + if (check_region (io_base, 2)) { + printk (KERN_WARNING "WF-MPU: I/O port %x already in use.\n", + io_base); + return -1; + } + + phys_dev->base = io_base; + phys_dev->irq = irq; + phys_dev->devno = -1; + virt_dev->devno = -1; + + return 0; +} + +int __init install_wf_mpu (void) +{ + if ((phys_dev->devno = sound_alloc_mididev()) < 0){ + + printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n"); + return -1; + + } + + request_region (phys_dev->base, 2, "wavefront midi"); + phys_dev->isvirtual = 0; + + if (config_wf_mpu (phys_dev)) { + + printk (KERN_WARNING + "WF-MPU: configuration for MIDI device %d failed\n", + phys_dev->devno); + sound_unload_mididev (phys_dev->devno); + + } + + /* OK, now we're configured to handle an interrupt ... */ + + if (request_irq (phys_dev->irq, wf_mpuintr, SA_INTERRUPT|SA_SHIRQ, + "wavefront midi", phys_dev) < 0) { + + printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n", + phys_dev->irq); + return -1; + + } + + /* This being a WaveFront (ICS-2115) emulated MPU-401, we have + to switch it into UART (dumb) mode, because otherwise, it + won't do anything at all. + */ + + start_uart_mode (); + + return phys_dev->devno; +} + +void +uninstall_wf_mpu (void) + +{ + release_region (phys_dev->base, 2); + free_irq (phys_dev->irq, phys_dev); + sound_unload_mididev (phys_dev->devno); + + if (virt_dev->devno >= 0) { + sound_unload_mididev (virt_dev->devno); + } +} + +static void +start_uart_mode (void) + +{ + int ok, i; + unsigned long flags; + + save_flags (flags); + cli (); + + /* XXX fix me */ + + for (i = 0; i < 30000 && !output_ready (); i++); + + outb (UART_MODE_ON, COMDPORT(phys_dev)); + + for (ok = 0, i = 50000; i > 0 && !ok; i--) { + if (input_avail ()) { + if (read_data () == MPU_ACK) { + ok = 1; + } + } + } + + restore_flags (flags); +} +#endif diff -Nru a/sound/oss/ymfpci.c b/sound/oss/ymfpci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ymfpci.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,2671 @@ +/* + * Copyright 1999 Jaroslav Kysela + * Copyright 2000 Alan Cox + * Copyright 2001 Kai Germaschewski + * Copyright 2002 Pete Zaitcev + * + * Yamaha YMF7xx driver. + * + * This code is a result of high-speed collision + * between ymfpci.c of ALSA and cs46xx.c of Linux. + * -- Pete Zaitcev ; 2000/09/18 + * + * 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. + * + * TODO: + * - Use P44Slot for 44.1 playback (beware of idle buzzing in P44Slot). + * - 96KHz playback for DVD - use pitch of 2.0. + * - Retain DMA buffer on close, do not wait the end of frame. + * - Resolve XXX tagged questions. + * - Cannot play 5133Hz. + * - 2001/01/07 Consider if we can remove voice_lock, like so: + * : Allocate/deallocate voices in open/close under semafore. + * : We access voices in interrupt, that only for pcms that open. + * voice_lock around playback_prepare closes interrupts for insane duration. + * - Revisit the way voice_alloc is done - too confusing, overcomplicated. + * Should support various channel types, however. + * - Remove prog_dmabuf from read/write, leave it in open. + * - 2001/01/07 Replace the OPL3 part of CONFIG_SOUND_YMFPCI_LEGACY code with + * native synthesizer through a playback slot. + * - 2001/11/29 ac97_save_state + * Talk to Kai to remove ac97_save_state before it's too late! + * - Second AC97 + * - Restore S/PDIF - Toshibas have it. + * + * Kai used pci_alloc_consistent for DMA buffer, which sounds a little + * unconventional. However, given how small our fragments can be, + * a little uncached access is perhaps better than endless flushing. + * On i386 and other I/O-coherent architectures pci_alloc_consistent + * is entirely harmless. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY +# include "sound_config.h" +# include "mpu401.h" +#endif +#include "ymfpci.h" + +/* + * I do not believe in debug levels as I never can guess what + * part of the code is going to be problematic in the future. + * Don't forget to run your klogd with -c 8. + * + * Example (do not remove): + * #define YMFDBG(fmt, arg...) do{ printk(KERN_DEBUG fmt, ##arg); }while(0) + */ +#define YMFDBGW(fmt, arg...) /* */ /* write counts */ +#define YMFDBGI(fmt, arg...) /* */ /* interrupts */ +#define YMFDBGX(fmt, arg...) /* */ /* ioctl */ + +static int ymf_playback_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); +static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); +static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice); +static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank); +static int ymf_playback_prepare(struct ymf_state *state); +static int ymf_capture_prepare(struct ymf_state *state); +static struct ymf_state *ymf_state_alloc(ymfpci_t *unit); + +static void ymfpci_aclink_reset(struct pci_dev * pci); +static void ymfpci_disable_dsp(ymfpci_t *unit); +static void ymfpci_download_image(ymfpci_t *codec); +static void ymf_memload(ymfpci_t *unit); + +static LIST_HEAD(ymf_devs); + +/* + * constants + */ + +static struct pci_device_id ymf_id_tbl[] __devinitdata = { +#define DEV(v, d, data) \ + { PCI_VENDOR_ID_##v, PCI_DEVICE_ID_##v##_##d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)data } + DEV (YAMAHA, 724, "YMF724"), + DEV (YAMAHA, 724F, "YMF724F"), + DEV (YAMAHA, 740, "YMF740"), + DEV (YAMAHA, 740C, "YMF740C"), + DEV (YAMAHA, 744, "YMF744"), + DEV (YAMAHA, 754, "YMF754"), +#undef DEV + { } +}; +MODULE_DEVICE_TABLE(pci, ymf_id_tbl); + +/* + * common I/O routines + */ + +static inline u8 ymfpci_readb(ymfpci_t *codec, u32 offset) +{ + return readb(codec->reg_area_virt + offset); +} + +static inline void ymfpci_writeb(ymfpci_t *codec, u32 offset, u8 val) +{ + writeb(val, codec->reg_area_virt + offset); +} + +static inline u16 ymfpci_readw(ymfpci_t *codec, u32 offset) +{ + return readw(codec->reg_area_virt + offset); +} + +static inline void ymfpci_writew(ymfpci_t *codec, u32 offset, u16 val) +{ + writew(val, codec->reg_area_virt + offset); +} + +static inline u32 ymfpci_readl(ymfpci_t *codec, u32 offset) +{ + return readl(codec->reg_area_virt + offset); +} + +static inline void ymfpci_writel(ymfpci_t *codec, u32 offset, u32 val) +{ + writel(val, codec->reg_area_virt + offset); +} + +static int ymfpci_codec_ready(ymfpci_t *codec, int secondary, int sched) +{ + signed long end_time; + u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; + + end_time = jiffies + 3 * (HZ / 4); + do { + if ((ymfpci_readw(codec, reg) & 0x8000) == 0) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + printk(KERN_ERR "ymfpci_codec_ready: codec %i is not ready [0x%x]\n", + secondary, ymfpci_readw(codec, reg)); + return -EBUSY; +} + +static void ymfpci_codec_write(struct ac97_codec *dev, u8 reg, u16 val) +{ + ymfpci_t *codec = dev->private_data; + u32 cmd; + + /* 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); +} + +static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg) +{ + ymfpci_t *unit = dev->private_data; + int i; + + if (ymfpci_codec_ready(unit, 0, 0)) + return ~0; + ymfpci_writew(unit, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); + if (ymfpci_codec_ready(unit, 0, 0)) + return ~0; + if (unit->pci->device == PCI_DEVICE_ID_YAMAHA_744 && unit->rev < 2) { + for (i = 0; i < 600; i++) + ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); + } + return ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); +} + +/* + * Misc routines + */ + +/* + * Calculate the actual sampling rate relatetively to the base clock (48kHz). + */ +static u32 ymfpci_calc_delta(u32 rate) +{ + switch (rate) { + case 8000: return 0x02aaab00; + case 11025: return 0x03accd00; + case 16000: return 0x05555500; + case 22050: return 0x07599a00; + case 32000: return 0x0aaaab00; + case 44100: return 0x0eb33300; + default: return ((rate << 16) / 48000) << 12; + } +} + +static u32 def_rate[8] = { + 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 +}; + +static u32 ymfpci_calc_lpfK(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000, + 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000 + }; + + if (rate == 44100) + return 0x40000000; /* FIXME: What's the right value? */ + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 ymfpci_calc_lpfQ(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x35280000, 0x34A70000, 0x32020000, 0x31770000, + 0x31390000, 0x31C90000, 0x33D00000, 0x40000000 + }; + + if (rate == 44100) + return 0x370A0000; + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 ymf_calc_lend(u32 rate) +{ + return (rate * YMF_SAMPF) / 48000; +} + +/* + * We ever allow only a few formats, but let's be generic, for smaller surprise. + */ +static int ymf_pcm_format_width(int format) +{ + static int mask16 = AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE; + + if ((format & (format-1)) != 0) { + printk(KERN_ERR "ymfpci: format 0x%x is not a power of 2\n", format); + return 8; + } + + if (format == AFMT_IMA_ADPCM) return 4; + if ((format & mask16) != 0) return 16; + return 8; +} + +static void ymf_pcm_update_shift(struct ymf_pcm_format *f) +{ + f->shift = 0; + if (f->voices == 2) + f->shift++; + if (ymf_pcm_format_width(f->format) == 16) + f->shift++; +} + +/* Are you sure 32K is not too much? See if mpg123 skips on loaded systems. */ +#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* + * Allocate DMA buffer + */ +static int alloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) +{ + void *rawbuf = NULL; + dma_addr_t dma_addr; + int order; + struct page *map, *mapend; + + /* alloc as big a chunk as we can */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { + rawbuf = pci_alloc_consistent(unit->pci, PAGE_SIZE << order, &dma_addr); + if (rawbuf) + break; + } + if (!rawbuf) + return -ENOMEM; + +#if 0 + printk(KERN_DEBUG "ymfpci: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); +#endif + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->dma_addr = dma_addr; + dmabuf->buforder = order; + + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (map = virt_to_page(rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &map->flags); + + return 0; +} + +/* + * Free DMA buffer + */ +static void dealloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) +{ + struct page *map, *mapend; + + if (dmabuf->rawbuf) { + /* undo marking the pages as reserved */ + mapend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &map->flags); + + pci_free_consistent(unit->pci, PAGE_SIZE << dmabuf->buforder, + dmabuf->rawbuf, dmabuf->dma_addr); + } + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; +} + +static int prog_dmabuf(struct ymf_state *state, int rec) +{ + struct ymf_dmabuf *dmabuf; + int w_16; + unsigned bufsize; + unsigned long flags; + int redzone, redfrags; + int ret; + + w_16 = ymf_pcm_format_width(state->format.format) == 16; + dmabuf = rec ? &state->rpcm.dmabuf : &state->wpcm.dmabuf; + + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf) + if ((ret = alloc_dmabuf(state->unit, dmabuf))) + return ret; + + /* + * Create fake fragment sizes and numbers for OSS ioctls. + * Import what Doom might have set with SNDCTL_DSP_SETFRAGMENT. + */ + bufsize = PAGE_SIZE << dmabuf->buforder; + /* By default we give 4 big buffers. */ + dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT - 2); + if (dmabuf->ossfragshift > 3 && + dmabuf->ossfragshift < dmabuf->fragshift) { + /* If OSS set smaller fragments, give more smaller buffers. */ + dmabuf->fragshift = dmabuf->ossfragshift; + } + dmabuf->fragsize = 1 << dmabuf->fragshift; + + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + + if (dmabuf->ossmaxfrags >= 2) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= state->format.shift; + redzone *= 3; + redfrags = (redzone + dmabuf->fragsize-1) >> dmabuf->fragshift; + + if (dmabuf->ossmaxfrags + redfrags < dmabuf->numfrag) { + dmabuf->numfrag = dmabuf->ossmaxfrags + redfrags; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + } + } + + memset(dmabuf->rawbuf, w_16 ? 0 : 0x80, dmabuf->dmasize); + + /* + * Now set up the ring + */ + + /* XXX ret = rec? cap_pre(): pbk_pre(); */ + spin_lock_irqsave(&state->unit->voice_lock, flags); + if (rec) { + if ((ret = ymf_capture_prepare(state)) != 0) { + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + return ret; + } + } else { + if ((ret = ymf_playback_prepare(state)) != 0) { + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + return ret; + } + } + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + + /* set the ready flag for the dma buffer (this comment is not stupid) */ + dmabuf->ready = 1; + +#if 0 + printk(KERN_DEBUG "prog_dmabuf: rate %d format 0x%x," + " numfrag %d fragsize %d dmasize %d\n", + state->format.rate, state->format.format, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); +#endif + + return 0; +} + +static void ymf_start_dac(struct ymf_state *state) +{ + ymf_playback_trigger(state->unit, &state->wpcm, 1); +} + +// static void ymf_start_adc(struct ymf_state *state) +// { +// ymf_capture_trigger(state->unit, &state->rpcm, 1); +// } + +/* + * Wait until output is drained. + * This does not kill the hardware for the sake of ioctls. + */ +static void ymf_wait_dac(struct ymf_state *state) +{ + struct ymf_unit *unit = state->unit; + struct ymf_pcm *ypcm = &state->wpcm; + DECLARE_WAITQUEUE(waita, current); + unsigned long flags; + + add_wait_queue(&ypcm->dmabuf.wait, &waita); + + spin_lock_irqsave(&unit->reg_lock, flags); + if (ypcm->dmabuf.count != 0 && !ypcm->running) { + ymf_playback_trigger(unit, ypcm, 1); + } + +#if 0 + if (file->f_flags & O_NONBLOCK) { + /* + * XXX Our mistake is to attach DMA buffer to state + * rather than to some per-device structure. + * Cannot skip waiting, can only make it shorter. + */ + } +#endif + + set_current_state(TASK_UNINTERRUPTIBLE); + while (ypcm->running) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + schedule(); + spin_lock_irqsave(&unit->reg_lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + } + spin_unlock_irqrestore(&unit->reg_lock, flags); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&ypcm->dmabuf.wait, &waita); + + /* + * This function may take up to 4 seconds to reach this point + * (32K circular buffer, 8000 Hz). User notices. + */ +} + +/* Can just stop, without wait. Or can we? */ +static void ymf_stop_adc(struct ymf_state *state) +{ + struct ymf_unit *unit = state->unit; + unsigned long flags; + + spin_lock_irqsave(&unit->reg_lock, flags); + ymf_capture_trigger(unit, &state->rpcm, 0); + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +/* + * Hardware start management + */ + +static void ymfpci_hw_start(ymfpci_t *unit) +{ + unsigned long flags; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->start_count++ == 0) { + ymfpci_writel(unit, YDSXGR_MODE, + ymfpci_readl(unit, YDSXGR_MODE) | 3); + unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; + } + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +static void ymfpci_hw_stop(ymfpci_t *unit) +{ + unsigned long flags; + long timeout = 1000; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (--unit->start_count == 0) { + ymfpci_writel(unit, YDSXGR_MODE, + ymfpci_readl(unit, YDSXGR_MODE) & ~3); + while (timeout-- > 0) { + if ((ymfpci_readl(unit, YDSXGR_STATUS) & 2) == 0) + break; + } + } + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +/* + * Playback voice management + */ + +static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t *rvoice[]) +{ + ymfpci_voice_t *voice, *voice2; + int idx; + + for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) { + voice = &codec->voices[idx]; + voice2 = pair ? &codec->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case YMFPCI_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case YMFPCI_SYNTH: + voice->synth = 1; + break; + case YMFPCI_MIDI: + voice->midi = 1; + break; + } + ymfpci_hw_start(codec); + rvoice[0] = voice; + if (voice2) { + ymfpci_hw_start(codec); + rvoice[1] = voice2; + } + return 0; + } + return -EBUSY; /* Your audio channel is open by someone else. */ +} + +static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice) +{ + ymfpci_hw_stop(unit); + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->ypcm = NULL; +} + +/* + */ + +static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) +{ + struct ymf_pcm *ypcm; + int redzone; + int pos, delta, swptr; + int played, distance; + struct ymf_state *state; + struct ymf_dmabuf *dmabuf; + char silence; + + if ((ypcm = voice->ypcm) == NULL) { + return; + } + if ((state = ypcm->state) == NULL) { + ypcm->running = 0; // lock it + return; + } + dmabuf = &ypcm->dmabuf; + spin_lock(&codec->reg_lock); + if (ypcm->running) { + YMFDBGI("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n", + voice->number, codec->active_bank, dmabuf->count, + le32_to_cpu(voice->bank[0].start), + le32_to_cpu(voice->bank[1].start)); + silence = (ymf_pcm_format_width(state->format.format) == 16) ? + 0 : 0x80; + /* We need actual left-hand-side redzone size here. */ + redzone = ymf_calc_lend(state->format.rate); + redzone <<= (state->format.shift + 1); + swptr = dmabuf->swptr; + + pos = le32_to_cpu(voice->bank[codec->active_bank].start); + pos <<= state->format.shift; + if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ + printk(KERN_ERR "ymfpci%d: runaway voice %d: hwptr %d=>%d dmasize %d\n", + codec->dev_audio, voice->number, + dmabuf->hwptr, pos, dmabuf->dmasize); + pos = 0; + } + if (pos < dmabuf->hwptr) { + delta = dmabuf->dmasize - dmabuf->hwptr; + memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); + delta += pos; + memset(dmabuf->rawbuf, silence, pos); + } else { + delta = pos - dmabuf->hwptr; + memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); + } + dmabuf->hwptr = pos; + + if (dmabuf->count == 0) { + printk(KERN_ERR "ymfpci%d: %d: strain: hwptr %d\n", + codec->dev_audio, voice->number, dmabuf->hwptr); + ymf_playback_trigger(codec, ypcm, 0); + } + + if (swptr <= pos) { + distance = pos - swptr; + } else { + distance = dmabuf->dmasize - (swptr - pos); + } + if (distance < redzone) { + /* + * hwptr inside redzone => DMA ran out of samples. + */ + if (delta < dmabuf->count) { + /* + * Lost interrupt or other screwage. + */ + printk(KERN_ERR "ymfpci%d: %d: lost: delta %d" + " hwptr %d swptr %d distance %d count %d\n", + codec->dev_audio, voice->number, delta, + dmabuf->hwptr, swptr, distance, dmabuf->count); + } else { + /* + * Normal end of DMA. + */ + YMFDBGI("ymfpci%d: %d: done: delta %d" + " hwptr %d swptr %d distance %d count %d\n", + codec->dev_audio, voice->number, delta, + dmabuf->hwptr, swptr, distance, dmabuf->count); + } + played = dmabuf->count; + if (ypcm->running) { + ymf_playback_trigger(codec, ypcm, 0); + } + } else { + /* + * hwptr is chipping away towards a remote swptr. + * Calculate other distance and apply it to count. + */ + if (swptr >= pos) { + distance = swptr - pos; + } else { + distance = dmabuf->dmasize - (pos - swptr); + } + if (distance < dmabuf->count) { + played = dmabuf->count - distance; + } else { + played = 0; + } + } + + dmabuf->total_bytes += played; + dmabuf->count -= played; + if (dmabuf->count < dmabuf->dmasize / 2) { + wake_up(&dmabuf->wait); + } + } + spin_unlock(&codec->reg_lock); +} + +static void ymf_cap_interrupt(ymfpci_t *unit, struct ymf_capture *cap) +{ + struct ymf_pcm *ypcm; + int redzone; + struct ymf_state *state; + struct ymf_dmabuf *dmabuf; + int pos, delta; + int cnt; + + if ((ypcm = cap->ypcm) == NULL) { + return; + } + if ((state = ypcm->state) == NULL) { + ypcm->running = 0; // lock it + return; + } + dmabuf = &ypcm->dmabuf; + spin_lock(&unit->reg_lock); + if (ypcm->running) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= (state->format.shift + 1); + + pos = le32_to_cpu(cap->bank[unit->active_bank].start); + // pos <<= state->format.shift; + if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ + printk(KERN_ERR "ymfpci%d: runaway capture %d: hwptr %d=>%d dmasize %d\n", + unit->dev_audio, ypcm->capture_bank_number, + dmabuf->hwptr, pos, dmabuf->dmasize); + pos = 0; + } + if (pos < dmabuf->hwptr) { + delta = dmabuf->dmasize - dmabuf->hwptr; + delta += pos; + } else { + delta = pos - dmabuf->hwptr; + } + dmabuf->hwptr = pos; + + cnt = dmabuf->count; + cnt += delta; + if (cnt + redzone > dmabuf->dmasize) { + /* Overflow - bump swptr */ + dmabuf->count = dmabuf->dmasize - redzone; + dmabuf->swptr = dmabuf->hwptr + redzone; + if (dmabuf->swptr >= dmabuf->dmasize) { + dmabuf->swptr -= dmabuf->dmasize; + } + } else { + dmabuf->count = cnt; + } + + dmabuf->total_bytes += delta; + if (dmabuf->count) { /* && is_sleeping XXX */ + wake_up(&dmabuf->wait); + } + } + spin_unlock(&unit->reg_lock); +} + +static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) +{ + + if (ypcm->voices[0] == NULL) { + return -EINVAL; + } + if (cmd != 0) { + codec->ctrl_playback[ypcm->voices[0]->number + 1] = + cpu_to_le32(ypcm->voices[0]->bank_ba); + if (ypcm->voices[1] != NULL) + codec->ctrl_playback[ypcm->voices[1]->number + 1] = + cpu_to_le32(ypcm->voices[1]->bank_ba); + ypcm->running = 1; + } else { + codec->ctrl_playback[ypcm->voices[0]->number + 1] = 0; + if (ypcm->voices[1] != NULL) + codec->ctrl_playback[ypcm->voices[1]->number + 1] = 0; + ypcm->running = 0; + } + return 0; +} + +static void ymf_capture_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) +{ + u32 tmp; + + if (cmd != 0) { + tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); + ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); + ypcm->running = 1; + } else { + tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); + ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); + ypcm->running = 0; + } +} + +static int ymfpci_pcm_voice_alloc(struct ymf_pcm *ypcm, int voices) +{ + struct ymf_unit *unit; + int err; + + unit = ypcm->state->unit; + if (ypcm->voices[1] != NULL && voices < 2) { + ymfpci_voice_free(unit, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (voices == 1 && ypcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) + return 0; /* already allocated */ + if (voices > 1) { + if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { + ymfpci_voice_free(unit, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + if ((err = voice_alloc(unit, YMFPCI_PCM, 1, ypcm->voices)) < 0) + return err; + ypcm->voices[0]->ypcm = ypcm; + ypcm->voices[1]->ypcm = ypcm; + } else { + if ((err = voice_alloc(unit, YMFPCI_PCM, 0, ypcm->voices)) < 0) + return err; + ypcm->voices[0]->ypcm = ypcm; + } + return 0; +} + +static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo, + int rate, int w_16, unsigned long addr, unsigned int end, int spdif) +{ + u32 format; + u32 delta = ymfpci_calc_delta(rate); + u32 lpfQ = ymfpci_calc_lpfQ(rate); + u32 lpfK = ymfpci_calc_lpfK(rate); + ymfpci_playback_bank_t *bank; + int nbank; + + /* + * The gain is a floating point number. According to the manual, + * bit 31 indicates a sign bit, bit 30 indicates an integer part, + * and bits [29:15] indicate a decimal fraction part. Thus, + * for a gain of 1.0 the constant of 0x40000000 is loaded. + */ + unsigned default_gain = cpu_to_le32(0x40000000); + + format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); + if (stereo) + end >>= 1; + if (w_16) + end >>= 1; + for (nbank = 0; nbank < 2; nbank++) { + bank = &voice->bank[nbank]; + bank->format = cpu_to_le32(format); + bank->loop_default = 0; /* 0-loops forever, otherwise count */ + bank->base = cpu_to_le32(addr); + bank->loop_start = 0; + bank->loop_end = cpu_to_le32(end); + bank->loop_frac = 0; + bank->eg_gain_end = default_gain; + bank->lpfQ = cpu_to_le32(lpfQ); + bank->status = 0; + bank->num_of_frames = 0; + bank->loop_count = 0; + bank->start = 0; + bank->start_frac = 0; + bank->delta = + bank->delta_end = cpu_to_le32(delta); + bank->lpfK = + bank->lpfK_end = cpu_to_le32(lpfK); + bank->eg_gain = default_gain; + bank->lpfD1 = + bank->lpfD2 = 0; + + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = + bank->eff1_gain = + bank->eff2_gain = + bank->eff3_gain = + bank->eff1_gain_end = + bank->eff2_gain_end = + bank->eff3_gain_end = 0; + + if (!stereo) { + if (!spdif) { + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = default_gain; + } else { + bank->eff2_gain = + bank->eff2_gain_end = + bank->eff3_gain = + bank->eff3_gain_end = default_gain; + } + } else { + if (!spdif) { + if ((voice->number & 1) == 0) { + bank->left_gain = + bank->left_gain_end = default_gain; + } else { + bank->format |= cpu_to_le32(1); + bank->right_gain = + bank->right_gain_end = default_gain; + } + } else { + if ((voice->number & 1) == 0) { + bank->eff2_gain = + bank->eff2_gain_end = default_gain; + } else { + bank->format |= cpu_to_le32(1); + bank->eff3_gain = + bank->eff3_gain_end = default_gain; + } + } + } + } +} + +/* + * XXX Capture channel allocation is entirely fake at the moment. + * We use only one channel and mark it busy as required. + */ +static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank) +{ + struct ymf_capture *cap; + int cbank; + + cbank = 1; /* Only ADC slot is used for now. */ + cap = &unit->capture[cbank]; + if (cap->use) + return -EBUSY; + cap->use = 1; + *pbank = cbank; + return 0; +} + +static int ymf_playback_prepare(struct ymf_state *state) +{ + struct ymf_pcm *ypcm = &state->wpcm; + int err, nvoice; + + if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) { + /* Somebody started 32 mpg123's in parallel? */ + printk(KERN_INFO "ymfpci%d: cannot allocate voice\n", + state->unit->dev_audio); + return err; + } + + for (nvoice = 0; nvoice < state->format.voices; nvoice++) { + ymf_pcm_init_voice(ypcm->voices[nvoice], + state->format.voices == 2, state->format.rate, + ymf_pcm_format_width(state->format.format) == 16, + ypcm->dmabuf.dma_addr, ypcm->dmabuf.dmasize, + ypcm->spdif); + } + return 0; +} + +static int ymf_capture_prepare(struct ymf_state *state) +{ + ymfpci_t *unit = state->unit; + struct ymf_pcm *ypcm = &state->rpcm; + ymfpci_capture_bank_t * bank; + /* XXX This is confusing, gotta rename one of them banks... */ + int nbank; /* flip-flop bank */ + int cbank; /* input [super-]bank */ + struct ymf_capture *cap; + u32 rate, format; + + if (ypcm->capture_bank_number == -1) { + if (ymf_capture_alloc(unit, &cbank) != 0) + return -EBUSY; + + ypcm->capture_bank_number = cbank; + + cap = &unit->capture[cbank]; + cap->bank = unit->bank_capture[cbank][0]; + cap->ypcm = ypcm; + ymfpci_hw_start(unit); + } + + // ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream); + // frag_size is replaced with nonfragged byte-aligned rolling buffer + rate = ((48000 * 4096) / state->format.rate) - 1; + format = 0; + if (state->format.voices == 2) + format |= 2; + if (ymf_pcm_format_width(state->format.format) == 8) + format |= 1; + switch (ypcm->capture_bank_number) { + case 0: + ymfpci_writel(unit, YDSXGR_RECFORMAT, format); + ymfpci_writel(unit, YDSXGR_RECSLOTSR, rate); + break; + case 1: + ymfpci_writel(unit, YDSXGR_ADCFORMAT, format); + ymfpci_writel(unit, YDSXGR_ADCSLOTSR, rate); + break; + } + for (nbank = 0; nbank < 2; nbank++) { + bank = unit->bank_capture[ypcm->capture_bank_number][nbank]; + bank->base = cpu_to_le32(ypcm->dmabuf.dma_addr); + // bank->loop_end = ypcm->dmabuf.dmasize >> state->format.shift; + bank->loop_end = cpu_to_le32(ypcm->dmabuf.dmasize); + bank->start = 0; + bank->num_of_loops = 0; + } +#if 0 /* s/pdif */ + if (state->digital.dig_valid) + /*state->digital.type == SND_PCM_DIG_AES_IEC958*/ + ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS, + state->digital.dig_status[0] | (state->digital.dig_status[1] << 8)); +#endif + return 0; +} + +void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ymfpci_t *codec = dev_id; + u32 status, nvoice, mode; + struct ymf_voice *voice; + struct ymf_capture *cap; + + status = ymfpci_readl(codec, YDSXGR_STATUS); + if (status & 0x80000000) { + codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1; + spin_lock(&codec->voice_lock); + for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) { + voice = &codec->voices[nvoice]; + if (voice->use) + ymf_pcm_interrupt(codec, voice); + } + for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) { + cap = &codec->capture[nvoice]; + if (cap->use) + ymf_cap_interrupt(codec, cap); + } + spin_unlock(&codec->voice_lock); + spin_lock(&codec->reg_lock); + ymfpci_writel(codec, YDSXGR_STATUS, 0x80000000); + mode = ymfpci_readl(codec, YDSXGR_MODE) | 2; + ymfpci_writel(codec, YDSXGR_MODE, mode); + spin_unlock(&codec->reg_lock); + } + + status = ymfpci_readl(codec, YDSXGR_INTFLAG); + if (status & 1) { + /* timer handler */ + ymfpci_writel(codec, YDSXGR_INTFLAG, ~0); + } +} + +static void ymf_pcm_free_substream(struct ymf_pcm *ypcm) +{ + unsigned long flags; + struct ymf_unit *unit; + + unit = ypcm->state->unit; + + if (ypcm->type == PLAYBACK_VOICE) { + spin_lock_irqsave(&unit->voice_lock, flags); + if (ypcm->voices[1]) + ymfpci_voice_free(unit, ypcm->voices[1]); + if (ypcm->voices[0]) + ymfpci_voice_free(unit, ypcm->voices[0]); + spin_unlock_irqrestore(&unit->voice_lock, flags); + } else { + if (ypcm->capture_bank_number != -1) { + unit->capture[ypcm->capture_bank_number].use = 0; + ypcm->capture_bank_number = -1; + ymfpci_hw_stop(unit); + } + } +} + +static struct ymf_state *ymf_state_alloc(ymfpci_t *unit) +{ + struct ymf_pcm *ypcm; + struct ymf_state *state; + + if ((state = kmalloc(sizeof(struct ymf_state), GFP_KERNEL)) == NULL) { + goto out0; + } + memset(state, 0, sizeof(struct ymf_state)); + + ypcm = &state->wpcm; + ypcm->state = state; + ypcm->type = PLAYBACK_VOICE; + ypcm->capture_bank_number = -1; + init_waitqueue_head(&ypcm->dmabuf.wait); + + ypcm = &state->rpcm; + ypcm->state = state; + ypcm->type = CAPTURE_AC97; + ypcm->capture_bank_number = -1; + init_waitqueue_head(&ypcm->dmabuf.wait); + + state->unit = unit; + + state->format.format = AFMT_U8; + state->format.rate = 8000; + state->format.voices = 1; + ymf_pcm_update_shift(&state->format); + + return state; + +out0: + return NULL; +} + +/* AES/IEC958 channel status bits */ +#define SND_PCM_AES0_PROFESSIONAL (1<<0) /* 0 = consumer, 1 = professional */ +#define SND_PCM_AES0_NONAUDIO (1<<1) /* 0 = audio, 1 = non-audio */ +#define SND_PCM_AES0_PRO_EMPHASIS (7<<2) /* mask - emphasis */ +#define SND_PCM_AES0_PRO_EMPHASIS_NOTID (0<<2) /* emphasis not indicated */ +#define SND_PCM_AES0_PRO_EMPHASIS_NONE (1<<2) /* none emphasis */ +#define SND_PCM_AES0_PRO_EMPHASIS_5015 (3<<2) /* 50/15us emphasis */ +#define SND_PCM_AES0_PRO_EMPHASIS_CCITT (7<<2) /* CCITT J.17 emphasis */ +#define SND_PCM_AES0_PRO_FREQ_UNLOCKED (1<<5) /* source sample frequency: 0 = locked, 1 = unlocked */ +#define SND_PCM_AES0_PRO_FS (3<<6) /* mask - sample frequency */ +#define SND_PCM_AES0_PRO_FS_NOTID (0<<6) /* fs not indicated */ +#define SND_PCM_AES0_PRO_FS_44100 (1<<6) /* 44.1kHz */ +#define SND_PCM_AES0_PRO_FS_48000 (2<<6) /* 48kHz */ +#define SND_PCM_AES0_PRO_FS_32000 (3<<6) /* 32kHz */ +#define SND_PCM_AES0_CON_NOT_COPYRIGHT (1<<2) /* 0 = copyright, 1 = not copyright */ +#define SND_PCM_AES0_CON_EMPHASIS (7<<3) /* mask - emphasis */ +#define SND_PCM_AES0_CON_EMPHASIS_NONE (0<<3) /* none emphasis */ +#define SND_PCM_AES0_CON_EMPHASIS_5015 (1<<3) /* 50/15us emphasis */ +#define SND_PCM_AES0_CON_MODE (3<<6) /* mask - mode */ +#define SND_PCM_AES1_PRO_MODE (15<<0) /* mask - channel mode */ +#define SND_PCM_AES1_PRO_MODE_NOTID (0<<0) /* not indicated */ +#define SND_PCM_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */ +#define SND_PCM_AES1_PRO_MODE_SINGLE (4<<0) /* single channel */ +#define SND_PCM_AES1_PRO_MODE_TWO (8<<0) /* two channels */ +#define SND_PCM_AES1_PRO_MODE_PRIMARY (12<<0) /* primary/secondary */ +#define SND_PCM_AES1_PRO_MODE_BYTE3 (15<<0) /* vector to byte 3 */ +#define SND_PCM_AES1_PRO_USERBITS (15<<4) /* mask - user bits */ +#define SND_PCM_AES1_PRO_USERBITS_NOTID (0<<4) /* not indicated */ +#define SND_PCM_AES1_PRO_USERBITS_192 (8<<4) /* 192-bit structure */ +#define SND_PCM_AES1_PRO_USERBITS_UDEF (12<<4) /* user defined application */ +#define SND_PCM_AES1_CON_CATEGORY 0x7f +#define SND_PCM_AES1_CON_GENERAL 0x00 +#define SND_PCM_AES1_CON_EXPERIMENTAL 0x40 +#define SND_PCM_AES1_CON_SOLIDMEM_MASK 0x0f +#define SND_PCM_AES1_CON_SOLIDMEM_ID 0x08 +#define SND_PCM_AES1_CON_BROADCAST1_MASK 0x07 +#define SND_PCM_AES1_CON_BROADCAST1_ID 0x04 +#define SND_PCM_AES1_CON_DIGDIGCONV_MASK 0x07 +#define SND_PCM_AES1_CON_DIGDIGCONV_ID 0x02 +#define SND_PCM_AES1_CON_ADC_COPYRIGHT_MASK 0x1f +#define SND_PCM_AES1_CON_ADC_COPYRIGHT_ID 0x06 +#define SND_PCM_AES1_CON_ADC_MASK 0x1f +#define SND_PCM_AES1_CON_ADC_ID 0x16 +#define SND_PCM_AES1_CON_BROADCAST2_MASK 0x0f +#define SND_PCM_AES1_CON_BROADCAST2_ID 0x0e +#define SND_PCM_AES1_CON_LASEROPT_MASK 0x07 +#define SND_PCM_AES1_CON_LASEROPT_ID 0x01 +#define SND_PCM_AES1_CON_MUSICAL_MASK 0x07 +#define SND_PCM_AES1_CON_MUSICAL_ID 0x05 +#define SND_PCM_AES1_CON_MAGNETIC_MASK 0x07 +#define SND_PCM_AES1_CON_MAGNETIC_ID 0x03 +#define SND_PCM_AES1_CON_IEC908_CD (SND_PCM_AES1_CON_LASEROPT_ID|0x00) +#define SND_PCM_AES1_CON_NON_IEC908_CD (SND_PCM_AES1_CON_LASEROPT_ID|0x08) +#define SND_PCM_AES1_CON_PCM_CODER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x00) +#define SND_PCM_AES1_CON_SAMPLER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x20) +#define SND_PCM_AES1_CON_MIXER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x10) +#define SND_PCM_AES1_CON_RATE_CONVERTER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x18) +#define SND_PCM_AES1_CON_SYNTHESIZER (SND_PCM_AES1_CON_MUSICAL_ID|0x00) +#define SND_PCM_AES1_CON_MICROPHONE (SND_PCM_AES1_CON_MUSICAL_ID|0x08) +#define SND_PCM_AES1_CON_DAT (SND_PCM_AES1_CON_MAGNETIC_ID|0x00) +#define SND_PCM_AES1_CON_VCR (SND_PCM_AES1_CON_MAGNETIC_ID|0x08) +#define SND_PCM_AES1_CON_ORIGINAL (1<<7) /* this bits depends on the category code */ +#define SND_PCM_AES2_PRO_SBITS (7<<0) /* mask - sample bits */ +#define SND_PCM_AES2_PRO_SBITS_20 (2<<0) /* 20-bit - coordination */ +#define SND_PCM_AES2_PRO_SBITS_24 (4<<0) /* 24-bit - main audio */ +#define SND_PCM_AES2_PRO_SBITS_UDEF (6<<0) /* user defined application */ +#define SND_PCM_AES2_PRO_WORDLEN (7<<3) /* mask - source word length */ +#define SND_PCM_AES2_PRO_WORDLEN_NOTID (0<<3) /* not indicated */ +#define SND_PCM_AES2_PRO_WORDLEN_22_18 (2<<3) /* 22-bit or 18-bit */ +#define SND_PCM_AES2_PRO_WORDLEN_23_19 (4<<3) /* 23-bit or 19-bit */ +#define SND_PCM_AES2_PRO_WORDLEN_24_20 (5<<3) /* 24-bit or 20-bit */ +#define SND_PCM_AES2_PRO_WORDLEN_20_16 (6<<3) /* 20-bit or 16-bit */ +#define SND_PCM_AES2_CON_SOURCE (15<<0) /* mask - source number */ +#define SND_PCM_AES2_CON_SOURCE_UNSPEC (0<<0) /* unspecified */ +#define SND_PCM_AES2_CON_CHANNEL (15<<4) /* mask - channel number */ +#define SND_PCM_AES2_CON_CHANNEL_UNSPEC (0<<4) /* unspecified */ +#define SND_PCM_AES3_CON_FS (15<<0) /* mask - sample frequency */ +#define SND_PCM_AES3_CON_FS_44100 (0<<0) /* 44.1kHz */ +#define SND_PCM_AES3_CON_FS_48000 (2<<0) /* 48kHz */ +#define SND_PCM_AES3_CON_FS_32000 (3<<0) /* 32kHz */ +#define SND_PCM_AES3_CON_CLOCK (3<<4) /* mask - clock accuracy */ +#define SND_PCM_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */ +#define SND_PCM_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */ +#define SND_PCM_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */ + +/* + * User interface + */ + +/* + * 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 +ymf_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf = &state->rpcm.dmabuf; + struct ymf_unit *unit = state->unit; + DECLARE_WAITQUEUE(waita, current); + ssize_t ret; + unsigned long flags; + unsigned int swptr; + int cnt; /* This many to go in this revolution */ + + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + ret = 0; + + add_wait_queue(&dmabuf->wait, &waita); + set_current_state(TASK_INTERRUPTIBLE); + while (count > 0) { + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count < cnt) + cnt = dmabuf->count; + spin_unlock_irqrestore(&unit->reg_lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + unsigned long tmo; + /* buffer is empty, start the dma machine and wait for data to be + recorded */ + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (!state->rpcm.running) { + ymf_capture_trigger(state->unit, &state->rpcm, 1); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + break; + } + /* This isnt strictly right for the 810 but it'll do */ + tmo = (dmabuf->dmasize * HZ) / (state->format.rate * 2); + tmo >>= state->format.shift; + /* 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. */ + tmo = schedule_timeout(tmo); + spin_lock_irqsave(&state->unit->reg_lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tmo == 0 && dmabuf->count == 0) { + printk(KERN_ERR "ymfpci%d: recording schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + state->unit->dev_audio, + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + break; + } + continue; + } + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { + if (!ret) ret = -EFAULT; + break; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + continue; + } + + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + // spin_unlock_irqrestore(&unit->reg_lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + // spin_lock_irqsave(&unit->reg_lock, flags); + if (!state->rpcm.running) { + ymf_capture_trigger(unit, &state->rpcm, 1); + } + spin_unlock_irqrestore(&unit->reg_lock, flags); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + + return ret; +} + +static ssize_t +ymf_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; + struct ymf_unit *unit = state->unit; + DECLARE_WAITQUEUE(waita, current); + ssize_t ret; + unsigned long flags; + unsigned int swptr; + int cnt; /* This many to go in this revolution */ + int redzone; + int delay; + + YMFDBGW("ymf_write: count %d\n", count); + + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + ret = 0; + + /* + * Alan's cs46xx works without a red zone - marvel of ingenuity. + * We are not so brilliant... Red zone does two things: + * 1. allows for safe start after a pause as we have no way + * to know what the actual, relentlessly advancing, hwptr is. + * 2. makes computations in ymf_pcm_interrupt simpler. + */ + redzone = ymf_calc_lend(state->format.rate) << state->format.shift; + redzone *= 3; /* 2 redzone + 1 possible uncertainty reserve. */ + + add_wait_queue(&dmabuf->wait, &waita); + set_current_state(TASK_INTERRUPTIBLE); + while (count > 0) { + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } + if (dmabuf->count < 0) { + printk(KERN_ERR + "ymf_write: count %d, was legal in cs46xx\n", + dmabuf->count); + dmabuf->count = 0; + } + if (dmabuf->count == 0) { + swptr = dmabuf->hwptr; + if (state->wpcm.running) { + /* + * Add uncertainty reserve. + */ + cnt = ymf_calc_lend(state->format.rate); + cnt <<= state->format.shift; + if ((swptr += cnt) >= dmabuf->dmasize) { + swptr -= dmabuf->dmasize; + } + } + dmabuf->swptr = swptr; + } else { + /* + * XXX This is not right if dmabuf->count is small - + * about 2*x frame size or less. We cannot count on + * on appending and not causing an artefact. + * Should use a variation of the count==0 case above. + */ + swptr = dmabuf->swptr; + } + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count + cnt > dmabuf->dmasize - redzone) + cnt = (dmabuf->dmasize - redzone) - dmabuf->count; + spin_unlock_irqrestore(&unit->reg_lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + YMFDBGW("ymf_write: full, count %d swptr %d\n", + dmabuf->count, dmabuf->swptr); + /* + * buffer is full, start the dma machine and + * wait for data to be played + */ + spin_lock_irqsave(&unit->reg_lock, flags); + if (!state->wpcm.running) { + ymf_playback_trigger(unit, &state->wpcm, 1); + } + spin_unlock_irqrestore(&unit->reg_lock, flags); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + break; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + break; + } + + if ((swptr += cnt) >= dmabuf->dmasize) { + swptr -= dmabuf->dmasize; + } + + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + continue; + } + dmabuf->swptr = swptr; + dmabuf->count += cnt; + + /* + * Start here is a bad idea - may cause startup click + * in /bin/play when dmabuf is not full yet. + * However, some broken applications do not make + * any use of SNDCTL_DSP_SYNC (Doom is the worst). + * One frame is about 5.3ms, Doom write size is 46ms. + */ + delay = state->format.rate / 20; /* 50ms */ + delay <<= state->format.shift; + if (dmabuf->count >= delay && !state->wpcm.running) { + ymf_playback_trigger(unit, &state->wpcm, 1); + } + + spin_unlock_irqrestore(&unit->reg_lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + + YMFDBGW("ymf_write: ret %d dmabuf.count %d\n", ret, dmabuf->count); + return ret; +} + +static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf; + int redzone; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &state->wpcm.dmabuf.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &state->rpcm.dmabuf.wait, wait); + + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (file->f_mode & FMODE_READ) { + dmabuf = &state->rpcm.dmabuf; + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= state->format.shift; + redzone *= 3; + + dmabuf = &state->wpcm.dmabuf; + if (dmabuf->mapped) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + /* + * Don't select unless a full fragment is available. + * Otherwise artsd does GETOSPACE, sees 0, and loops. + */ + if (dmabuf->count + redzone + dmabuf->fragsize + <= dmabuf->dmasize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + + return mask; +} + +static int ymf_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; + int ret; + unsigned long size; + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(state, 0)) != 0) + return ret; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(state, 1)) != 0) + return ret; + } else + return -EINVAL; + + if (vma->vm_pgoff != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << dmabuf->buforder)) + return -EINVAL; + if (remap_page_range(vma, vma->vm_start, virt_to_phys(dmabuf->rawbuf), + size, vma->vm_page_prot)) + return -EAGAIN; + dmabuf->mapped = 1; + +/* P3 */ printk(KERN_INFO "ymfpci: using memory mapped sound, untested!\n"); + return 0; +} + +static int ymf_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int redzone; + int val; + + switch (cmd) { + case OSS_GETVERSION: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETVER) arg 0x%lx\n", cmd, arg); + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_RESET: + YMFDBGX("ymf_ioctl: cmd 0x%x(RESET)\n", cmd); + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = dmabuf->total_bytes = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = dmabuf->total_bytes = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + return 0; + + case SNDCTL_DSP_SYNC: + YMFDBGX("ymf_ioctl: cmd 0x%x(SYNC)\n", cmd); + if (file->f_mode & FMODE_WRITE) { + dmabuf = &state->wpcm.dmabuf; + if (file->f_flags & O_NONBLOCK) { + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (dmabuf->count != 0 && !state->wpcm.running) { + ymf_start_dac(state); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } else { + ymf_wait_dac(state); + } + } + /* XXX What does this do for reading? dmabuf->count=0; ? */ + return 0; + + case SNDCTL_DSP_SPEED: /* set smaple rate */ + if (get_user(val, (int *)arg)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(SPEED) sp %d\n", cmd, val); + if (val >= 8000 && val <= 48000) { + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.rate = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.rate = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + return put_user(state->format.rate, (int *)arg); + + /* + * OSS manual does not mention SNDCTL_DSP_STEREO at all. + * All channels are mono and if you want stereo, you + * play into two channels with SNDCTL_DSP_CHANNELS. + * However, mpg123 calls it. I wonder, why Michael Hipp used it. + */ + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(STEREO) st %d\n", cmd, val); + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.voices = val ? 2 : 1; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.voices = val ? 2 : 1; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETBLK)\n", cmd); + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(state, 0))) + return val; + val = state->wpcm.dmabuf.fragsize; + YMFDBGX("ymf_ioctl: GETBLK w %d\n", val); + return put_user(val, (int *)arg); + } + if (file->f_mode & FMODE_READ) { + if ((val = prog_dmabuf(state, 1))) + return val; + val = state->rpcm.dmabuf.fragsize; + YMFDBGX("ymf_ioctl: GETBLK r %d\n", val); + return put_user(val, (int *)arg); + } + return -EINVAL; + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + YMFDBGX("ymf_ioctl: cmd 0x%x(GETFMTS)\n", cmd); + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Select sample format */ + if (get_user(val, (int *)arg)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(SETFMT) fmt %d\n", cmd, val); + if (val == AFMT_S16_LE || val == AFMT_U8) { + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.format = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.format = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + return put_user(state->format.format, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(CHAN) ch %d\n", cmd, val); + if (val != 0) { + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + if (val == 1 || val == 2) { + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf = &state->wpcm.dmabuf; + dmabuf->ready = 0; + state->format.voices = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + if (val == 1 || val == 2) { + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf = &state->rpcm.dmabuf; + dmabuf->ready = 0; + state->format.voices = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + } + return put_user(state->format.voices, (int *)arg); + + case SNDCTL_DSP_POST: + YMFDBGX("ymf_ioctl: cmd 0x%x(POST)\n", cmd); + /* + * Quoting OSS PG: + * The ioctl SNDCTL_DSP_POST is a lightweight version of + * SNDCTL_DSP_SYNC. It just tells to the driver that there + * is likely to be a pause in the output. This makes it + * possible for the device to handle the pause more + * intelligently. This ioctl doesn't block the application. + * + * The paragraph above is a clumsy way to say "flush ioctl". + * This ioctl is used by mpg123. + */ + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (state->wpcm.dmabuf.count != 0 && !state->wpcm.running) { + ymf_start_dac(state); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(SETFRAG) fr 0x%04x:%04x(%d:%d)\n", + cmd, + (val >> 16) & 0xFFFF, val & 0xFFFF, + (val >> 16) & 0xFFFF, val & 0xFFFF); + dmabuf = &state->wpcm.dmabuf; + dmabuf->ossfragshift = val & 0xffff; + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + if (dmabuf->ossfragshift < 4) + dmabuf->ossfragshift = 4; + if (dmabuf->ossfragshift > 15) + dmabuf->ossfragshift = 15; + return 0; + + case SNDCTL_DSP_GETOSPACE: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETOSPACE)\n", cmd); + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + dmabuf = &state->wpcm.dmabuf; + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + redzone = ymf_calc_lend(state->format.rate); + redzone <<= state->format.shift; + redzone *= 3; + spin_lock_irqsave(&state->unit->reg_lock, flags); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->dmasize - dmabuf->count - redzone; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETISPACE)\n", cmd); + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + dmabuf = &state->rpcm.dmabuf; + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + return val; + spin_lock_irqsave(&state->unit->reg_lock, flags); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->count; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + YMFDBGX("ymf_ioctl: cmd 0x%x(NONBLOCK)\n", cmd); + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETCAPS)\n", cmd); + /* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, + (int *)arg); */ + return put_user(0, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETIPTR)\n", cmd); + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + YMFDBGX("ymf_ioctl: GETIPTR ptr %d bytes %d\n", + cinfo.ptr, cinfo.bytes); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETOPTR: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETOPTR)\n", cmd); + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + YMFDBGX("ymf_ioctl: GETOPTR ptr %d bytes %d\n", + cinfo.ptr, cinfo.bytes); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_SETDUPLEX: + YMFDBGX("ymf_ioctl: cmd 0x%x(SETDUPLEX)\n", cmd); + return 0; /* Always duplex */ + + case SOUND_PCM_READ_RATE: + YMFDBGX("ymf_ioctl: cmd 0x%x(READ_RATE)\n", cmd); + return put_user(state->format.rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + YMFDBGX("ymf_ioctl: cmd 0x%x(READ_CH)\n", cmd); + return put_user(state->format.voices, (int *)arg); + + case SOUND_PCM_READ_BITS: + YMFDBGX("ymf_ioctl: cmd 0x%x(READ_BITS)\n", cmd); + return put_user(AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + YMFDBGX("ymf_ioctl: cmd 0x%x unsupported\n", cmd); + return -ENOTTY; + + default: + /* + * Some programs mix up audio devices and ioctls + * or perhaps they expect "universal" ioctls, + * for instance we get SNDCTL_TMR_CONTINUE here. + * (mpg123 -g 100 ends here too - to be fixed.) + */ + YMFDBGX("ymf_ioctl: cmd 0x%x unknown\n", cmd); + break; + } + return -ENOTTY; +} + +/* + * open(2) + * We use upper part of the minor to distinguish between soundcards. + * Channels are opened with a clone open. + */ +static int ymf_open(struct inode *inode, struct file *file) +{ + struct list_head *list; + ymfpci_t *unit = NULL; + int minor; + struct ymf_state *state; + int err; + + minor = minor(inode->i_rdev); + if ((minor & 0x0F) == 3) { /* /dev/dspN */ + ; + } else { + return -ENXIO; + } + + unit = NULL; /* gcc warns */ + list_for_each(list, &ymf_devs) { + unit = list_entry(list, ymfpci_t, ymf_devs); + if (((unit->dev_audio ^ minor) & ~0x0F) == 0) + break; + } + if (list == &ymf_devs) + return -ENODEV; + + down(&unit->open_sem); + + if ((state = ymf_state_alloc(unit)) == NULL) { + up(&unit->open_sem); + return -ENOMEM; + } + list_add_tail(&state->chain, &unit->states); + + file->private_data = state; + + /* + * ymf_read and ymf_write that we borrowed from cs46xx + * allocate buffers with prog_dmabuf(). We call prog_dmabuf + * here so that in case of DMA memory exhaustion open + * fails rather than write. + * + * XXX prog_dmabuf allocates voice. Should allocate explicitly, above. + */ + if (file->f_mode & FMODE_WRITE) { + if (!state->wpcm.dmabuf.ready) { + if ((err = prog_dmabuf(state, 0)) != 0) { + goto out_nodma; + } + } + } + if (file->f_mode & FMODE_READ) { + if (!state->rpcm.dmabuf.ready) { + if ((err = prog_dmabuf(state, 1)) != 0) { + goto out_nodma; + } + } + } + +#if 0 /* test if interrupts work */ + ymfpci_writew(unit, YDSXGR_TIMERCOUNT, 0xfffe); /* ~ 680ms */ + ymfpci_writeb(unit, YDSXGR_TIMERCTRL, + (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN)); +#endif + up(&unit->open_sem); + + return 0; + +out_nodma: + /* + * XXX Broken custom: "goto out_xxx" in other place is + * a nestable exception, but here it is not nestable due to semaphore. + * XXX Doubtful technique of self-describing objects.... + */ + dealloc_dmabuf(unit, &state->wpcm.dmabuf); + dealloc_dmabuf(unit, &state->rpcm.dmabuf); + ymf_pcm_free_substream(&state->wpcm); + ymf_pcm_free_substream(&state->rpcm); + + list_del(&state->chain); + kfree(state); + + up(&unit->open_sem); + return err; +} + +static int ymf_release(struct inode *inode, struct file *file) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + ymfpci_t *unit = state->unit; + +#if 0 /* test if interrupts work */ + ymfpci_writeb(unit, YDSXGR_TIMERCTRL, 0); +#endif + + down(&unit->open_sem); + + /* + * XXX Solve the case of O_NONBLOCK close - don't deallocate here. + * Deallocate when unloading the driver and we can wait. + */ + ymf_wait_dac(state); + ymf_stop_adc(state); /* fortunately, it's immediate */ + dealloc_dmabuf(unit, &state->wpcm.dmabuf); + dealloc_dmabuf(unit, &state->rpcm.dmabuf); + ymf_pcm_free_substream(&state->wpcm); + ymf_pcm_free_substream(&state->rpcm); + + list_del(&state->chain); + file->private_data = NULL; /* Can you tell I programmed Solaris */ + kfree(state); + + up(&unit->open_sem); + + return 0; +} + +/* + * Mixer operations are based on cs46xx. + */ +static int ymf_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct list_head *list; + ymfpci_t *unit; + int i; + + list_for_each(list, &ymf_devs) { + unit = list_entry(list, ymfpci_t, ymf_devs); + for (i = 0; i < NR_AC97; i++) { + if (unit->ac97_codec[i] != NULL && + unit->ac97_codec[i]->dev_mixer == minor) { + goto match; + } + } + } + return -ENODEV; + + match: + file->private_data = unit->ac97_codec[i]; + + return 0; +} + +static int ymf_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 int ymf_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + +static /*const*/ struct file_operations ymf_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: ymf_read, + write: ymf_write, + poll: ymf_poll, + ioctl: ymf_ioctl, + mmap: ymf_mmap, + open: ymf_open, + release: ymf_release, +}; + +static /*const*/ struct file_operations ymf_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: ymf_ioctl_mixdev, + open: ymf_open_mixdev, + release: ymf_release_mixdev, +}; + +/* + */ + +static int ymf_suspend(struct pci_dev *pcidev, u32 unused) +{ + struct ymf_unit *unit = pci_get_drvdata(pcidev); + unsigned long flags; + struct ymf_dmabuf *dmabuf; + struct list_head *p; + struct ymf_state *state; + struct ac97_codec *codec; + int i; + + spin_lock_irqsave(&unit->reg_lock, flags); + + unit->suspended = 1; + + for (i = 0; i < NR_AC97; i++) { + if ((codec = unit->ac97_codec[i]) != NULL) + ac97_save_state(codec); + } + + list_for_each(p, &unit->states) { + state = list_entry(p, struct ymf_state, chain); + + dmabuf = &state->wpcm.dmabuf; + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + + dmabuf = &state->rpcm.dmabuf; + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + } + + ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0); + ymfpci_disable_dsp(unit); + + spin_unlock_irqrestore(&unit->reg_lock, flags); + + return 0; +} + +static int ymf_resume(struct pci_dev *pcidev) +{ + struct ymf_unit *unit = pci_get_drvdata(pcidev); + unsigned long flags; + struct list_head *p; + struct ymf_state *state; + struct ac97_codec *codec; + int i; + + ymfpci_aclink_reset(unit->pci); + ymfpci_codec_ready(unit, 0, 1); /* prints diag if not ready. */ + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + /* XXX At this time the legacy registers are probably deprogrammed. */ +#endif + + ymfpci_download_image(unit); + + ymf_memload(unit); + + spin_lock_irqsave(&unit->reg_lock, flags); + + if (unit->start_count) { + ymfpci_writel(unit, YDSXGR_MODE, 3); + unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; + } + + for (i = 0; i < NR_AC97; i++) { + if ((codec = unit->ac97_codec[i]) != NULL) + ac97_restore_state(codec); + } + + unit->suspended = 0; + list_for_each(p, &unit->states) { + state = list_entry(p, struct ymf_state, chain); + wake_up(&state->wpcm.dmabuf.wait); + wake_up(&state->rpcm.dmabuf.wait); + } + + spin_unlock_irqrestore(&unit->reg_lock, flags); + return 0; +} + +/* + * initialization routines + */ + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + +static int ymfpci_setup_legacy(ymfpci_t *unit, struct pci_dev *pcidev) +{ + int v; + int mpuio = -1, oplio = -1; + + switch (unit->iomidi) { + case 0x330: + mpuio = 0; + break; + case 0x300: + mpuio = 1; + break; + case 0x332: + mpuio = 2; + break; + case 0x334: + mpuio = 3; + break; + default: ; + } + + switch (unit->iosynth) { + case 0x388: + oplio = 0; + break; + case 0x398: + oplio = 1; + break; + case 0x3a0: + oplio = 2; + break; + case 0x3a8: + oplio = 3; + break; + default: ; + } + + if (mpuio >= 0 || oplio >= 0) { + /* 0x0020: 1 - 10 bits of I/O address decoded, 0 - 16 bits. */ + v = 0x001e; + pci_write_config_word(pcidev, PCIR_LEGCTRL, v); + + switch (pcidev->device) { + case PCI_DEVICE_ID_YAMAHA_724: + case PCI_DEVICE_ID_YAMAHA_740: + case PCI_DEVICE_ID_YAMAHA_724F: + case PCI_DEVICE_ID_YAMAHA_740C: + v = 0x8800; + if (mpuio >= 0) { v |= mpuio<<4; } + if (oplio >= 0) { v |= oplio; } + pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); + break; + + case PCI_DEVICE_ID_YAMAHA_744: + case PCI_DEVICE_ID_YAMAHA_754: + v = 0x8800; + pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); + if (oplio >= 0) { + pci_write_config_word(pcidev, PCIR_OPLADR, unit->iosynth); + } + if (mpuio >= 0) { + pci_write_config_word(pcidev, PCIR_MPUADR, unit->iomidi); + } + break; + + default: + printk(KERN_ERR "ymfpci: Unknown device ID: 0x%x\n", + pcidev->device); + return -EINVAL; + } + } + + return 0; +} +#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ + +static void ymfpci_aclink_reset(struct pci_dev * pci) +{ + u8 cmd; + + /* + * In the 744, 754 only 0x01 exists, 0x02 is undefined. + * It does not seem to hurt to trip both regardless of revision. + */ + pci_read_config_byte(pci, PCIR_DSXGCTRL, &cmd); + pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); + pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd | 0x03); + pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); + + pci_write_config_word(pci, PCIR_DSXPWRCTRL1, 0); + pci_write_config_word(pci, PCIR_DSXPWRCTRL2, 0); +} + +static void ymfpci_enable_dsp(ymfpci_t *codec) +{ + ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000001); +} + +static void ymfpci_disable_dsp(ymfpci_t *codec) +{ + u32 val; + int timeout = 1000; + + val = ymfpci_readl(codec, YDSXGR_CONFIG); + if (val) + ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000000); + while (timeout-- > 0) { + val = ymfpci_readl(codec, YDSXGR_STATUS); + if ((val & 0x00000002) == 0) + break; + } +} + +#include "ymfpci_image.h" + +static void ymfpci_download_image(ymfpci_t *codec) +{ + int i, ver_1e; + u16 ctrl; + + ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x00000000); + ymfpci_disable_dsp(codec); + ymfpci_writel(codec, YDSXGR_MODE, 0x00010000); + ymfpci_writel(codec, YDSXGR_MODE, 0x00000000); + ymfpci_writel(codec, YDSXGR_MAPOFREC, 0x00000000); + ymfpci_writel(codec, YDSXGR_MAPOFEFFECT, 0x00000000); + ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0x00000000); + ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0x00000000); + ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0x00000000); + ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); + ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + + /* setup DSP instruction code */ + for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) + ymfpci_writel(codec, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]); + + switch (codec->pci->device) { + case PCI_DEVICE_ID_YAMAHA_724F: + case PCI_DEVICE_ID_YAMAHA_740C: + case PCI_DEVICE_ID_YAMAHA_744: + case PCI_DEVICE_ID_YAMAHA_754: + ver_1e = 1; + break; + default: + ver_1e = 0; + } + + if (ver_1e) { + /* setup control instruction code */ + for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) + ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst1E[i]); + } else { + for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) + ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst[i]); + } + + ymfpci_enable_dsp(codec); + + /* 0.02s sounds not too bad, we may do schedule_timeout() later. */ + mdelay(20); /* seems we need some delay after downloading image.. */ +} + +static int ymfpci_memalloc(ymfpci_t *codec) +{ + unsigned int playback_ctrl_size; + unsigned int bank_size_playback; + unsigned int bank_size_capture; + unsigned int bank_size_effect; + unsigned int size; + unsigned int off; + char *ptr; + dma_addr_t pba; + int voice, bank; + + playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES; + bank_size_playback = ymfpci_readl(codec, YDSXGR_PLAYCTRLSIZE) << 2; + bank_size_capture = ymfpci_readl(codec, YDSXGR_RECCTRLSIZE) << 2; + bank_size_effect = ymfpci_readl(codec, YDSXGR_EFFCTRLSIZE) << 2; + codec->work_size = YDSXG_DEFAULT_WORK_SIZE; + + size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) + + ((bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0xff) & ~0xff) + + ((bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0xff) & ~0xff) + + ((bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0xff) & ~0xff) + + codec->work_size; + + ptr = pci_alloc_consistent(codec->pci, size + 0xff, &pba); + if (ptr == NULL) + return -ENOMEM; + codec->dma_area_va = ptr; + codec->dma_area_ba = pba; + codec->dma_area_size = size + 0xff; + + if ((off = ((uint) ptr) & 0xff) != 0) { + ptr += 0x100 - off; + pba += 0x100 - off; + } + + /* + * Hardware requires only ptr[playback_ctrl_size] zeroed, + * but in our judgement it is a wrong kind of savings, so clear it all. + */ + memset(ptr, 0, size); + + codec->ctrl_playback = (u32 *)ptr; + codec->ctrl_playback_ba = pba; + codec->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES); + ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff; + pba += (playback_ctrl_size + 0x00ff) & ~0x00ff; + + off = 0; + for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) { + codec->voices[voice].number = voice; + codec->voices[voice].bank = + (ymfpci_playback_bank_t *) (ptr + off); + codec->voices[voice].bank_ba = pba + off; + off += 2 * bank_size_playback; /* 2 banks */ + } + off = (off + 0xff) & ~0xff; + ptr += off; + pba += off; + + off = 0; + codec->bank_base_capture = pba; + for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + codec->bank_capture[voice][bank] = + (ymfpci_capture_bank_t *) (ptr + off); + off += bank_size_capture; + } + off = (off + 0xff) & ~0xff; + ptr += off; + pba += off; + + off = 0; + codec->bank_base_effect = pba; + for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + codec->bank_effect[voice][bank] = + (ymfpci_effect_bank_t *) (ptr + off); + off += bank_size_effect; + } + off = (off + 0xff) & ~0xff; + ptr += off; + pba += off; + + codec->work_base = pba; + + return 0; +} + +static void ymfpci_memfree(ymfpci_t *codec) +{ + ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0); + ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0); + ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0); + ymfpci_writel(codec, YDSXGR_WORKBASE, 0); + ymfpci_writel(codec, YDSXGR_WORKSIZE, 0); + pci_free_consistent(codec->pci, + codec->dma_area_size, codec->dma_area_va, codec->dma_area_ba); +} + +static void ymf_memload(ymfpci_t *unit) +{ + + ymfpci_writel(unit, YDSXGR_PLAYCTRLBASE, unit->ctrl_playback_ba); + ymfpci_writel(unit, YDSXGR_RECCTRLBASE, unit->bank_base_capture); + ymfpci_writel(unit, YDSXGR_EFFCTRLBASE, unit->bank_base_effect); + ymfpci_writel(unit, YDSXGR_WORKBASE, unit->work_base); + ymfpci_writel(unit, YDSXGR_WORKSIZE, unit->work_size >> 2); + + /* S/PDIF output initialization */ + ymfpci_writew(unit, YDSXGR_SPDIFOUTCTRL, 0); + ymfpci_writew(unit, YDSXGR_SPDIFOUTSTATUS, + SND_PCM_AES0_CON_EMPHASIS_NONE | + (SND_PCM_AES1_CON_ORIGINAL << 8) | + (SND_PCM_AES1_CON_PCM_CODER << 8)); + + /* S/PDIF input initialization */ + ymfpci_writew(unit, YDSXGR_SPDIFINCTRL, 0); + + /* move this volume setup to mixer */ + ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); + ymfpci_writel(unit, YDSXGR_BUF441OUTVOL, 0); + ymfpci_writel(unit, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); + ymfpci_writel(unit, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); +} + +static int ymf_ac97_init(ymfpci_t *unit, int num_ac97) +{ + struct ac97_codec *codec; + u16 eid; + + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = unit; + codec->id = num_ac97; + + codec->codec_read = ymfpci_codec_read; + codec->codec_write = ymfpci_codec_write; + + if (ac97_probe_codec(codec) == 0) { + printk(KERN_ERR "ymfpci: ac97_probe_codec failed\n"); + goto out_kfree; + } + + eid = ymfpci_codec_read(codec, AC97_EXTENDED_ID); + if (eid==0xFFFFFF) { + printk(KERN_WARNING "ymfpci: no codec attached ?\n"); + goto out_kfree; + } + + unit->ac97_features = eid; + + if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) { + printk(KERN_ERR "ymfpci: couldn't register mixer!\n"); + goto out_kfree; + } + + unit->ac97_codec[num_ac97] = codec; + + return 0; + out_kfree: + kfree(codec); + return -ENODEV; +} + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY +# ifdef MODULE +static int mpu_io = 0; +static int synth_io = 0; +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(synth_io, "i"); +# else +static int mpu_io = 0x330; +static int synth_io = 0x388; +# endif +static int assigned; +#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ + +static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent) +{ + u16 ctrl; + unsigned long base; + ymfpci_t *codec; + + int err; + + if ((err = pci_enable_device(pcidev)) != 0) { + printk(KERN_ERR "ymfpci: pci_enable_device failed\n"); + return err; + } + base = pci_resource_start(pcidev, 0); + + if ((codec = kmalloc(sizeof(ymfpci_t), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "ymfpci: no core\n"); + return -ENOMEM; + } + memset(codec, 0, sizeof(*codec)); + + spin_lock_init(&codec->reg_lock); + spin_lock_init(&codec->voice_lock); + init_MUTEX(&codec->open_sem); + INIT_LIST_HEAD(&codec->states); + codec->pci = pcidev; + + pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev); + + if (request_mem_region(base, 0x8000, "ymfpci") == NULL) { + printk(KERN_ERR "ymfpci: unable to request mem region\n"); + goto out_free; + } + + if ((codec->reg_area_virt = ioremap(base, 0x8000)) == NULL) { + printk(KERN_ERR "ymfpci: unable to map registers\n"); + goto out_release_region; + } + + pci_set_master(pcidev); + + printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n", + (char *)ent->driver_data, base, pcidev->irq); + + ymfpci_aclink_reset(pcidev); + if (ymfpci_codec_ready(codec, 0, 1) < 0) + goto out_unmap; + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + if (assigned == 0) { + codec->iomidi = mpu_io; + codec->iosynth = synth_io; + if (ymfpci_setup_legacy(codec, pcidev) < 0) + goto out_unmap; + assigned = 1; + } +#endif + + ymfpci_download_image(codec); + + if (ymfpci_memalloc(codec) < 0) + goto out_disable_dsp; + ymf_memload(codec); + + if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) { + printk(KERN_ERR "ymfpci: unable to request IRQ %d\n", + pcidev->irq); + goto out_memfree; + } + + /* register /dev/dsp */ + if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) { + printk(KERN_ERR "ymfpci: unable to register dsp\n"); + goto out_free_irq; + } + + /* + * Poke just the primary for the moment. + */ + if ((err = ymf_ac97_init(codec, 0)) != 0) + goto out_unregister_sound_dsp; + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + codec->opl3_data.name = "ymfpci"; + codec->mpu_data.name = "ymfpci"; + + codec->opl3_data.io_base = codec->iosynth; + codec->opl3_data.irq = -1; + + codec->mpu_data.io_base = codec->iomidi; + codec->mpu_data.irq = -1; /* May be different from our PCI IRQ. */ + + if (codec->iomidi) { + if (!probe_uart401(&codec->mpu_data, THIS_MODULE)) { + codec->iomidi = 0; /* XXX kludge */ + } + } +#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ + + /* put it into driver list */ + list_add_tail(&codec->ymf_devs, &ymf_devs); + pci_set_drvdata(pcidev, codec); + + return 0; + + out_unregister_sound_dsp: + unregister_sound_dsp(codec->dev_audio); + out_free_irq: + free_irq(pcidev->irq, codec); + out_memfree: + ymfpci_memfree(codec); + out_disable_dsp: + ymfpci_disable_dsp(codec); + ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); + ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + ymfpci_writel(codec, YDSXGR_STATUS, ~0); + out_unmap: + iounmap(codec->reg_area_virt); + out_release_region: + release_mem_region(pci_resource_start(pcidev, 0), 0x8000); + out_free: + kfree(codec); + return -ENODEV; +} + +static void __devexit ymf_remove_one(struct pci_dev *pcidev) +{ + __u16 ctrl; + ymfpci_t *codec = pci_get_drvdata(pcidev); + + /* remove from list of devices */ + list_del(&codec->ymf_devs); + + unregister_sound_mixer(codec->ac97_codec[0]->dev_mixer); + kfree(codec->ac97_codec[0]); + unregister_sound_dsp(codec->dev_audio); + free_irq(pcidev->irq, codec); + ymfpci_memfree(codec); + ymfpci_writel(codec, YDSXGR_STATUS, ~0); + ymfpci_disable_dsp(codec); + ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); + ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + iounmap(codec->reg_area_virt); + release_mem_region(pci_resource_start(pcidev, 0), 0x8000); +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + if (codec->iomidi) { + unload_uart401(&codec->mpu_data); + } +#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ + kfree(codec); +} + +MODULE_AUTHOR("Jaroslav Kysela"); +MODULE_DESCRIPTION("Yamaha YMF7xx PCI Audio"); +MODULE_LICENSE("GPL"); + +static struct pci_driver ymfpci_driver = { + name: "ymfpci", + id_table: ymf_id_tbl, + probe: ymf_probe_one, + remove: ymf_remove_one, + suspend: ymf_suspend, + resume: ymf_resume +}; + +static int __init ymf_init_module(void) +{ + return pci_module_init(&ymfpci_driver); +} + +static void __exit ymf_cleanup_module (void) +{ + pci_unregister_driver(&ymfpci_driver); +} + +module_init(ymf_init_module); +module_exit(ymf_cleanup_module); diff -Nru a/sound/oss/ymfpci.h b/sound/oss/ymfpci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ymfpci.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,359 @@ +#ifndef __YMFPCI_H +#define __YMFPCI_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for Yahama YMF724/740/744/754 chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include + +/* + * Direct registers + */ + +/* #define YMFREG(codec, reg) (codec->port + YDSXGR_##reg) */ + +#define YDSXGR_INTFLAG 0x0004 +#define YDSXGR_ACTIVITY 0x0006 +#define YDSXGR_GLOBALCTRL 0x0008 +#define YDSXGR_ZVCTRL 0x000A +#define YDSXGR_TIMERCTRL 0x0010 +#define YDSXGR_TIMERCTRL_TEN 0x0001 +#define YDSXGR_TIMERCTRL_TIEN 0x0002 +#define YDSXGR_TIMERCOUNT 0x0012 +#define YDSXGR_SPDIFOUTCTRL 0x0018 +#define YDSXGR_SPDIFOUTSTATUS 0x001C +#define YDSXGR_EEPROMCTRL 0x0020 +#define YDSXGR_SPDIFINCTRL 0x0034 +#define YDSXGR_SPDIFINSTATUS 0x0038 +#define YDSXGR_DSPPROGRAMDL 0x0048 +#define YDSXGR_DLCNTRL 0x004C +#define YDSXGR_GPIOININTFLAG 0x0050 +#define YDSXGR_GPIOININTENABLE 0x0052 +#define YDSXGR_GPIOINSTATUS 0x0054 +#define YDSXGR_GPIOOUTCTRL 0x0056 +#define YDSXGR_GPIOFUNCENABLE 0x0058 +#define YDSXGR_GPIOTYPECONFIG 0x005A +#define YDSXGR_AC97CMDDATA 0x0060 +#define YDSXGR_AC97CMDADR 0x0062 +#define YDSXGR_PRISTATUSDATA 0x0064 +#define YDSXGR_PRISTATUSADR 0x0066 +#define YDSXGR_SECSTATUSDATA 0x0068 +#define YDSXGR_SECSTATUSADR 0x006A +#define YDSXGR_SECCONFIG 0x0070 +#define YDSXGR_LEGACYOUTVOL 0x0080 +#define YDSXGR_LEGACYOUTVOLL 0x0080 +#define YDSXGR_LEGACYOUTVOLR 0x0082 +#define YDSXGR_NATIVEDACOUTVOL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLR 0x0086 +#define YDSXGR_SPDIFOUTVOL 0x0088 +#define YDSXGR_SPDIFOUTVOLL 0x0088 +#define YDSXGR_SPDIFOUTVOLR 0x008A +#define YDSXGR_AC3OUTVOL 0x008C +#define YDSXGR_AC3OUTVOLL 0x008C +#define YDSXGR_AC3OUTVOLR 0x008E +#define YDSXGR_PRIADCOUTVOL 0x0090 +#define YDSXGR_PRIADCOUTVOLL 0x0090 +#define YDSXGR_PRIADCOUTVOLR 0x0092 +#define YDSXGR_LEGACYLOOPVOL 0x0094 +#define YDSXGR_LEGACYLOOPVOLL 0x0094 +#define YDSXGR_LEGACYLOOPVOLR 0x0096 +#define YDSXGR_NATIVEDACLOOPVOL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLR 0x009A +#define YDSXGR_SPDIFLOOPVOL 0x009C +#define YDSXGR_SPDIFLOOPVOLL 0x009E +#define YDSXGR_SPDIFLOOPVOLR 0x009E +#define YDSXGR_AC3LOOPVOL 0x00A0 +#define YDSXGR_AC3LOOPVOLL 0x00A0 +#define YDSXGR_AC3LOOPVOLR 0x00A2 +#define YDSXGR_PRIADCLOOPVOL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLR 0x00A6 +#define YDSXGR_NATIVEADCINVOL 0x00A8 +#define YDSXGR_NATIVEADCINVOLL 0x00A8 +#define YDSXGR_NATIVEADCINVOLR 0x00AA +#define YDSXGR_NATIVEDACINVOL 0x00AC +#define YDSXGR_NATIVEDACINVOLL 0x00AC +#define YDSXGR_NATIVEDACINVOLR 0x00AE +#define YDSXGR_BUF441OUTVOL 0x00B0 +#define YDSXGR_BUF441OUTVOLL 0x00B0 +#define YDSXGR_BUF441OUTVOLR 0x00B2 +#define YDSXGR_BUF441LOOPVOL 0x00B4 +#define YDSXGR_BUF441LOOPVOLL 0x00B4 +#define YDSXGR_BUF441LOOPVOLR 0x00B6 +#define YDSXGR_SPDIFOUTVOL2 0x00B8 +#define YDSXGR_SPDIFOUTVOL2L 0x00B8 +#define YDSXGR_SPDIFOUTVOL2R 0x00BA +#define YDSXGR_SPDIFLOOPVOL2 0x00BC +#define YDSXGR_SPDIFLOOPVOL2L 0x00BC +#define YDSXGR_SPDIFLOOPVOL2R 0x00BE +#define YDSXGR_ADCSLOTSR 0x00C0 +#define YDSXGR_RECSLOTSR 0x00C4 +#define YDSXGR_ADCFORMAT 0x00C8 +#define YDSXGR_RECFORMAT 0x00CC +#define YDSXGR_P44SLOTSR 0x00D0 +#define YDSXGR_STATUS 0x0100 +#define YDSXGR_CTRLSELECT 0x0104 +#define YDSXGR_MODE 0x0108 +#define YDSXGR_SAMPLECOUNT 0x010C +#define YDSXGR_NUMOFSAMPLES 0x0110 +#define YDSXGR_CONFIG 0x0114 +#define YDSXGR_PLAYCTRLSIZE 0x0140 +#define YDSXGR_RECCTRLSIZE 0x0144 +#define YDSXGR_EFFCTRLSIZE 0x0148 +#define YDSXGR_WORKSIZE 0x014C +#define YDSXGR_MAPOFREC 0x0150 +#define YDSXGR_MAPOFEFFECT 0x0154 +#define YDSXGR_PLAYCTRLBASE 0x0158 +#define YDSXGR_RECCTRLBASE 0x015C +#define YDSXGR_EFFCTRLBASE 0x0160 +#define YDSXGR_WORKBASE 0x0164 +#define YDSXGR_DSPINSTRAM 0x1000 +#define YDSXGR_CTRLINSTRAM 0x4000 + +#define YDSXG_AC97READCMD 0x8000 +#define YDSXG_AC97WRITECMD 0x0000 + +#define PCIR_LEGCTRL 0x40 +#define PCIR_ELEGCTRL 0x42 +#define PCIR_DSXGCTRL 0x48 +#define PCIR_DSXPWRCTRL1 0x4a +#define PCIR_DSXPWRCTRL2 0x4e +#define PCIR_OPLADR 0x60 +#define PCIR_SBADR 0x62 +#define PCIR_MPUADR 0x64 + +#define YDSXG_DSPLENGTH 0x0080 +#define YDSXG_CTRLLENGTH 0x3000 + +#define YDSXG_DEFAULT_WORK_SIZE 0x0400 + +#define YDSXG_PLAYBACK_VOICES 64 +#define YDSXG_CAPTURE_VOICES 2 +#define YDSXG_EFFECT_VOICES 5 + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +#define YMF_SAMPF 256 /* Samples per frame @48000 */ + +/* + * The slot/voice control bank (2 of these per voice) + */ + +typedef struct stru_ymfpci_playback_bank { + u32 format; + u32 loop_default; + u32 base; /* 32-bit address */ + u32 loop_start; /* 32-bit offset */ + u32 loop_end; /* 32-bit offset */ + u32 loop_frac; /* 8-bit fraction - loop_start */ + u32 delta_end; /* pitch delta end */ + u32 lpfK_end; + u32 eg_gain_end; + u32 left_gain_end; + u32 right_gain_end; + u32 eff1_gain_end; + u32 eff2_gain_end; + u32 eff3_gain_end; + u32 lpfQ; + u32 status; /* P3: Always 0 for some reason. */ + u32 num_of_frames; + u32 loop_count; + u32 start; /* P3: J. reads this to know where chip is. */ + u32 start_frac; + u32 delta; + u32 lpfK; + u32 eg_gain; + u32 left_gain; + u32 right_gain; + u32 eff1_gain; + u32 eff2_gain; + u32 eff3_gain; + u32 lpfD1; + u32 lpfD2; +} ymfpci_playback_bank_t; + +typedef struct stru_ymfpci_capture_bank { + u32 base; /* 32-bit address (aligned at 4) */ + u32 loop_end; /* size in BYTES (aligned at 4) */ + u32 start; /* 32-bit offset */ + u32 num_of_loops; /* counter */ +} ymfpci_capture_bank_t; + +typedef struct stru_ymfpci_effect_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 temp; +} ymfpci_effect_bank_t; + +typedef struct ymf_voice ymfpci_voice_t; +/* + * Throughout the code Yaroslav names YMF unit pointer "codec" + * even though it does not correspond to any codec. Must be historic. + * We replace it with "unit" over time. + * AC97 parts use "codec" to denote a codec, naturally. + */ +typedef struct ymf_unit ymfpci_t; + +typedef enum { + YMFPCI_PCM, + YMFPCI_SYNTH, + YMFPCI_MIDI +} ymfpci_voice_type_t; + +struct ymf_voice { + // ymfpci_t *codec; + int number; + char use, pcm, synth, midi; // bool + ymfpci_playback_bank_t *bank; + struct ymf_pcm *ypcm; + dma_addr_t bank_ba; +}; + +struct ymf_capture { + // struct ymf_unit *unit; + int use; + ymfpci_capture_bank_t *bank; + struct ymf_pcm *ypcm; +}; + +struct ymf_unit { + u8 rev; /* PCI revision */ + void *reg_area_virt; + void *dma_area_va; + dma_addr_t dma_area_ba; + unsigned int dma_area_size; + + dma_addr_t bank_base_capture; + dma_addr_t bank_base_effect; + dma_addr_t work_base; + unsigned int work_size; + + u32 *ctrl_playback; + dma_addr_t ctrl_playback_ba; + ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2]; + ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2]; + ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2]; + + int start_count; + int suspended; + + u32 active_bank; + struct ymf_voice voices[YDSXG_PLAYBACK_VOICES]; + struct ymf_capture capture[YDSXG_CAPTURE_VOICES]; + + struct ac97_codec *ac97_codec[NR_AC97]; + u16 ac97_features; + + struct pci_dev *pci; + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + /* legacy hardware resources */ + unsigned int iosynth, iomidi; + struct address_info opl3_data, mpu_data; +#endif + + spinlock_t reg_lock; + spinlock_t voice_lock; + + /* soundcore stuff */ + int dev_audio; + struct semaphore open_sem; + + struct list_head ymf_devs; + struct list_head states; /* List of states for this unit */ +}; + +struct ymf_dmabuf { + dma_addr_t dma_addr; + void *rawbuf; + unsigned buforder; + + /* OSS buffer management stuff */ + unsigned numfrag; + unsigned fragshift; + + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started */ + unsigned swptr; /* where driver last clear/filled */ + int count; /* fill count */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; /* Total rawbuf[] size */ + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; +}; + +struct ymf_pcm_format { + int format; /* OSS format */ + int rate; /* rate in Hz */ + int voices; /* number of voices */ + int shift; /* redundant, computed from the above */ +}; + +typedef enum { + PLAYBACK_VOICE, + CAPTURE_REC, + CAPTURE_AC97, + EFFECT_DRY_LEFT, + EFFECT_DRY_RIGHT, + EFFECT_EFF1, + EFFECT_EFF2, + EFFECT_EFF3 +} ymfpci_pcm_type_t; + +/* This is variant record, but we hate unions. Little waste on pointers []. */ +struct ymf_pcm { + ymfpci_pcm_type_t type; + struct ymf_state *state; + + ymfpci_voice_t *voices[2]; + int capture_bank_number; + + struct ymf_dmabuf dmabuf; + int running; + int spdif; +}; + +/* + * "Software" or virtual channel, an instance of opened /dev/dsp. + * It may have two physical channels (pcms) for duplex operations. + */ + +struct ymf_state { + struct list_head chain; + struct ymf_unit *unit; /* backpointer */ + struct ymf_pcm rpcm, wpcm; + struct ymf_pcm_format format; +}; + +#endif /* __YMFPCI_H */ diff -Nru a/sound/oss/ymfpci_image.h b/sound/oss/ymfpci_image.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/ymfpci_image.h Tue Feb 19 18:08:57 2002 @@ -0,0 +1,1565 @@ +#ifndef _HWMCODE_ +#define _HWMCODE_ + +static u32 DspInst[YDSXG_DSPLENGTH / 4] = { + 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, + 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, + 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, + 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +static u32 CntrlInst[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x001A82, 0x032D0D, 0x000810, 0x10043A, + 0x02D38D, 0x000810, 0x18043A, 0x00010D, + 0x020015, 0x0000FD, 0x000020, 0x038860, + 0x039060, 0x038060, 0x038040, 0x038040, + 0x038040, 0x018040, 0x000A7D, 0x038040, + 0x038040, 0x018040, 0x200402, 0x000882, + 0x08001A, 0x000904, 0x015986, 0x000007, + 0x260007, 0x000007, 0x000007, 0x018A06, + 0x000007, 0x030C8D, 0x000810, 0x18043A, + 0x260007, 0x00087D, 0x018042, 0x00160A, + 0x04A206, 0x000007, 0x00218D, 0x000810, + 0x08043A, 0x21C206, 0x000007, 0x0007FD, + 0x018042, 0x08000A, 0x000904, 0x029386, + 0x000195, 0x090D04, 0x000007, 0x000820, + 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, + 0x032206, 0x018040, 0x000A7D, 0x038042, + 0x13804A, 0x18000A, 0x001820, 0x059060, + 0x058860, 0x018040, 0x0000FD, 0x018042, + 0x70000A, 0x000115, 0x071144, 0x032386, + 0x030000, 0x007020, 0x034A06, 0x018040, + 0x00348D, 0x000810, 0x08043A, 0x21EA06, + 0x000007, 0x02D38D, 0x000810, 0x18043A, + 0x018206, 0x000007, 0x240007, 0x000F8D, + 0x000810, 0x00163A, 0x002402, 0x005C02, + 0x0028FD, 0x000020, 0x018040, 0x08000D, + 0x000815, 0x510984, 0x000007, 0x00004D, + 0x000E5D, 0x000E02, 0x00418D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00008D, + 0x000924, 0x000F02, 0x00458D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x018386, 0x000007, 0x01AA06, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x218086, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x055A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x034986, 0x000007, 0x002104, 0x034986, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x06C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00688D, 0x000810, 0x08043A, 0x288A06, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x060206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x215886, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x212086, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x07DA86, 0x00057D, 0x002820, + 0x03B060, 0x07F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x07FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x008D8D, 0x000810, + 0x08043A, 0x288A06, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x095186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x007FBD, 0x383DC4, + 0x000007, 0x001A7D, 0x001375, 0x018042, + 0x09004A, 0x10000A, 0x0B8D04, 0x139504, + 0x000007, 0x000820, 0x019060, 0x001104, + 0x212086, 0x010040, 0x0017FD, 0x018042, + 0x08000A, 0x000904, 0x212286, 0x000007, + 0x00197D, 0x038042, 0x09804A, 0x10000A, + 0x000924, 0x001664, 0x0011FD, 0x038042, + 0x2B804A, 0x19804A, 0x00008D, 0x218944, + 0x000007, 0x002244, 0x0AE186, 0x000007, + 0x001A64, 0x002A24, 0x00197D, 0x080102, + 0x100122, 0x000820, 0x039060, 0x018040, + 0x003DFD, 0x00008D, 0x000820, 0x018040, + 0x001375, 0x001A7D, 0x010042, 0x09804A, + 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, + 0x309144, 0x000007, 0x00060D, 0x000A15, + 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, + 0x000464, 0x01B3E4, 0x0232E4, 0x000464, + 0x000464, 0x000464, 0x000464, 0x00040D, + 0x08B1C4, 0x000007, 0x000820, 0x000BF5, + 0x030040, 0x00197D, 0x038042, 0x09804A, + 0x000A24, 0x08000A, 0x080E64, 0x000007, + 0x100122, 0x000820, 0x031060, 0x010040, + 0x0064AC, 0x00027D, 0x000020, 0x018040, + 0x00107D, 0x018042, 0x0011FD, 0x3B804A, + 0x09804A, 0x20000A, 0x000095, 0x1A1144, + 0x00A144, 0x0D2086, 0x00040D, 0x00B984, + 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, + 0x09804A, 0x28000A, 0x000095, 0x010924, + 0x002A64, 0x0D1186, 0x000007, 0x002904, + 0x0D2286, 0x000007, 0x0D2A06, 0x080002, + 0x00008D, 0x00387D, 0x000820, 0x018040, + 0x00127D, 0x018042, 0x10000A, 0x003904, + 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, + 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, + 0x000015, 0x00082D, 0x02C78D, 0x000820, + 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, + 0x0E7186, 0x400025, 0x00008D, 0x110944, + 0x000007, 0x00018D, 0x109504, 0x000007, + 0x009164, 0x000424, 0x000424, 0x000424, + 0x100102, 0x280002, 0x02C68D, 0x000820, + 0x0EC206, 0x00018D, 0x00042D, 0x00008D, + 0x109504, 0x000007, 0x00020D, 0x109184, + 0x000007, 0x02C70D, 0x000820, 0x00008D, + 0x0038FD, 0x018040, 0x003BFD, 0x001020, + 0x03A860, 0x000815, 0x313184, 0x212184, + 0x000007, 0x03B060, 0x03A060, 0x018040, + 0x0022FD, 0x000095, 0x010924, 0x000424, + 0x000424, 0x001264, 0x100102, 0x000820, + 0x039060, 0x018040, 0x001924, 0x00FB8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x000424, + 0x000424, 0x00117D, 0x018042, 0x08000A, + 0x000A24, 0x280502, 0x280C02, 0x09800D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x0022FD, 0x018042, 0x08000A, 0x000095, + 0x280DC4, 0x011924, 0x00197D, 0x018042, + 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, + 0x113144, 0x0A8D04, 0x000007, 0x080A44, + 0x129504, 0x000007, 0x0023FD, 0x001020, + 0x038040, 0x101244, 0x000007, 0x000820, + 0x039060, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x10FA86, 0x000007, + 0x003BFD, 0x000100, 0x000A10, 0x0B807A, + 0x13804A, 0x090984, 0x000007, 0x000095, + 0x013D04, 0x118086, 0x10000A, 0x100002, + 0x090984, 0x000007, 0x038042, 0x11804A, + 0x090D04, 0x000007, 0x10000A, 0x090D84, + 0x000007, 0x00257D, 0x000820, 0x018040, + 0x00010D, 0x000810, 0x28143A, 0x00127D, + 0x018042, 0x20000A, 0x00197D, 0x018042, + 0x00117D, 0x31804A, 0x10000A, 0x003124, + 0x01280D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x300102, 0x003124, 0x000424, 0x000424, + 0x001224, 0x280502, 0x001A4C, 0x130186, + 0x700002, 0x00002D, 0x030000, 0x00387D, + 0x018042, 0x10000A, 0x132A06, 0x002124, + 0x0000AD, 0x100002, 0x00010D, 0x000924, + 0x006B24, 0x01368D, 0x00397D, 0x000820, + 0x058040, 0x038042, 0x09844A, 0x000606, + 0x08040A, 0x003264, 0x00008D, 0x000A24, + 0x001020, 0x00227D, 0x018040, 0x013C0D, + 0x000810, 0x08043A, 0x29D206, 0x000007, + 0x002820, 0x00207D, 0x018040, 0x00117D, + 0x038042, 0x13804A, 0x33800A, 0x00387D, + 0x018042, 0x08000A, 0x000904, 0x163A86, + 0x000007, 0x00008D, 0x030964, 0x01478D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x380102, + 0x000424, 0x000424, 0x001224, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x14A286, + 0x000007, 0x280502, 0x001A4C, 0x163986, + 0x000007, 0x032164, 0x00632C, 0x003DFD, + 0x018042, 0x08000A, 0x000095, 0x090904, + 0x000007, 0x000820, 0x001A4C, 0x156186, + 0x018040, 0x030000, 0x157A06, 0x002124, + 0x00010D, 0x000924, 0x006B24, 0x015B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x003A64, + 0x000095, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x01628D, 0x000810, 0x08043A, 0x29D206, + 0x000007, 0x14D206, 0x000007, 0x007020, + 0x08010A, 0x10012A, 0x0020FD, 0x038860, + 0x039060, 0x018040, 0x00227D, 0x018042, + 0x003DFD, 0x08000A, 0x31844A, 0x000904, + 0x16D886, 0x18008B, 0x00008D, 0x189904, + 0x00312C, 0x17AA06, 0x000007, 0x00324C, + 0x173386, 0x000007, 0x001904, 0x173086, + 0x000007, 0x000095, 0x199144, 0x00222C, + 0x003124, 0x00636C, 0x000E3D, 0x001375, + 0x000BFD, 0x010042, 0x09804A, 0x10000A, + 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, + 0x000007, 0x00008D, 0x189904, 0x00226C, + 0x00322C, 0x30050A, 0x301DAB, 0x002083, + 0x0018FD, 0x018042, 0x08000A, 0x018924, + 0x300502, 0x001083, 0x001875, 0x010042, + 0x10000A, 0x00008D, 0x010924, 0x001375, + 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, + 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, + 0x305C8B, 0x006083, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x187A86, 0x000007, + 0x001E2D, 0x0005FD, 0x018042, 0x08000A, + 0x028924, 0x280502, 0x00060D, 0x000810, + 0x280C3A, 0x00008D, 0x000810, 0x28143A, + 0x0A808D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001275, 0x030042, 0x21004A, + 0x00008D, 0x1A0944, 0x000007, 0x01980D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0001F5, 0x030042, 0x0D004A, 0x10000A, + 0x089144, 0x000007, 0x000820, 0x010040, + 0x0025F5, 0x0A3144, 0x000007, 0x000820, + 0x032860, 0x030040, 0x00217D, 0x038042, + 0x0B804A, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00008D, 0x000124, 0x00012C, + 0x000E64, 0x001A64, 0x00636C, 0x08010A, + 0x10012A, 0x000820, 0x031060, 0x030040, + 0x0020FD, 0x018042, 0x08000A, 0x00227D, + 0x018042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00197D, 0x018042, 0x08000A, + 0x0022FD, 0x038042, 0x10000A, 0x000820, + 0x031060, 0x030040, 0x090D04, 0x000007, + 0x000820, 0x030040, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x038042, 0x13804A, 0x19804A, 0x110D04, + 0x198D04, 0x000007, 0x08000A, 0x001020, + 0x031860, 0x030860, 0x030040, 0x00008D, + 0x0B0944, 0x000007, 0x000820, 0x010040, + 0x0005F5, 0x030042, 0x08000A, 0x000820, + 0x010040, 0x0000F5, 0x010042, 0x08000A, + 0x000904, 0x1C6086, 0x001E75, 0x030042, + 0x01044A, 0x000C0A, 0x1C7206, 0x000007, + 0x000402, 0x000C02, 0x00177D, 0x001AF5, + 0x018042, 0x03144A, 0x031C4A, 0x03244A, + 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, + 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, + 0x030042, 0x0B004A, 0x1B804A, 0x13804A, + 0x20000A, 0x089144, 0x19A144, 0x0389E4, + 0x0399EC, 0x005502, 0x005D0A, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x006502, 0x006D0A, 0x030042, 0x0B004A, + 0x19004A, 0x2B804A, 0x13804A, 0x21804A, + 0x30000A, 0x089144, 0x19A144, 0x2AB144, + 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, + 0x03A9E4, 0x000702, 0x00107D, 0x000415, + 0x018042, 0x08000A, 0x0109E4, 0x000F02, + 0x002AF5, 0x0019FD, 0x010042, 0x09804A, + 0x10000A, 0x000934, 0x001674, 0x0029F5, + 0x010042, 0x10000A, 0x00917C, 0x002075, + 0x010042, 0x08000A, 0x000904, 0x1ED286, + 0x0026F5, 0x0027F5, 0x030042, 0x09004A, + 0x10000A, 0x000A3C, 0x00167C, 0x001A75, + 0x000BFD, 0x010042, 0x51804A, 0x48000A, + 0x160007, 0x001075, 0x010042, 0x282C0A, + 0x281D12, 0x282512, 0x001F32, 0x1E0007, + 0x0E0007, 0x001975, 0x010042, 0x002DF5, + 0x0D004A, 0x10000A, 0x009144, 0x1FB286, + 0x010042, 0x28340A, 0x000E5D, 0x00008D, + 0x000375, 0x000820, 0x010040, 0x05D2F4, + 0x54D104, 0x00735C, 0x205386, 0x000007, + 0x0C0007, 0x080007, 0x0A0007, 0x02040D, + 0x000810, 0x08043A, 0x332206, 0x000007, + 0x205A06, 0x000007, 0x080007, 0x002275, + 0x010042, 0x20000A, 0x002104, 0x212086, + 0x001E2D, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x209286, 0x000007, 0x002010, + 0x30043A, 0x00057D, 0x0180C3, 0x08000A, + 0x028924, 0x280502, 0x280C02, 0x0A810D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x0004FD, 0x018042, 0x70000A, 0x030000, + 0x007020, 0x06FA06, 0x018040, 0x02180D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x218A86, 0x000007, 0x01F206, 0x000007, + 0x000875, 0x0009FD, 0x00010D, 0x220A06, + 0x000295, 0x000B75, 0x00097D, 0x00000D, + 0x000515, 0x010042, 0x18000A, 0x001904, + 0x287886, 0x0006F5, 0x001020, 0x010040, + 0x0004F5, 0x000820, 0x010040, 0x000775, + 0x010042, 0x09804A, 0x10000A, 0x001124, + 0x000904, 0x22BA86, 0x000815, 0x080102, + 0x101204, 0x22DA06, 0x000575, 0x081204, + 0x000007, 0x100102, 0x000575, 0x000425, + 0x021124, 0x100102, 0x000820, 0x031060, + 0x010040, 0x001924, 0x287886, 0x00008D, + 0x000464, 0x009D04, 0x278886, 0x180102, + 0x000575, 0x010042, 0x28040A, 0x00018D, + 0x000924, 0x280D02, 0x00000D, 0x000924, + 0x281502, 0x10000D, 0x000820, 0x0002F5, + 0x010040, 0x200007, 0x001175, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x23C286, + 0x000007, 0x000100, 0x080B20, 0x130B60, + 0x1B0B60, 0x030A60, 0x010040, 0x050042, + 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, + 0x0006F5, 0x010042, 0x28140A, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x24CA86, 0x004015, 0x000095, 0x010D04, + 0x24B886, 0x100022, 0x10002A, 0x24E206, + 0x000007, 0x333104, 0x2AA904, 0x000007, + 0x032124, 0x280502, 0x001124, 0x000424, + 0x000424, 0x003224, 0x00292C, 0x00636C, + 0x25F386, 0x000007, 0x02B164, 0x000464, + 0x000464, 0x00008D, 0x000A64, 0x280D02, + 0x10008D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x00008D, 0x38B904, 0x000007, + 0x03296C, 0x30010A, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x25BA86, 0x000007, + 0x02312C, 0x28050A, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x267A86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x26C086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x26CA86, 0x000007, 0x003124, 0x300502, + 0x003924, 0x300583, 0x000883, 0x0005F5, + 0x010042, 0x28040A, 0x00008D, 0x008124, + 0x280D02, 0x00008D, 0x008124, 0x281502, + 0x10018D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001025, 0x000575, 0x030042, + 0x09004A, 0x10000A, 0x0A0904, 0x121104, + 0x000007, 0x001020, 0x050860, 0x050040, + 0x0006FD, 0x018042, 0x09004A, 0x10000A, + 0x0000A5, 0x0A0904, 0x121104, 0x000007, + 0x000820, 0x019060, 0x010040, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x284286, + 0x000007, 0x230A06, 0x000007, 0x000606, + 0x000007, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x289286, 0x000007, 0x000100, + 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, + 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, + 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, + 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, + 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, + 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, + 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, + 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, + 0x000606, 0x018040, 0x00008D, 0x000A64, + 0x280D02, 0x000A24, 0x00027D, 0x018042, + 0x10000A, 0x001224, 0x0003FD, 0x018042, + 0x08000A, 0x000904, 0x2A8286, 0x000007, + 0x00018D, 0x000A24, 0x000464, 0x000464, + 0x080102, 0x000924, 0x000424, 0x000424, + 0x100102, 0x02000D, 0x009144, 0x2AD986, + 0x000007, 0x0001FD, 0x018042, 0x08000A, + 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x00027D, 0x001020, 0x000606, 0x018040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x2B2A86, 0x000007, 0x00037D, 0x018042, + 0x08000A, 0x000904, 0x2B5A86, 0x000007, + 0x000075, 0x002E7D, 0x010042, 0x0B804A, + 0x000020, 0x000904, 0x000686, 0x010040, + 0x31844A, 0x30048B, 0x000883, 0x00008D, + 0x000810, 0x28143A, 0x00008D, 0x000810, + 0x280C3A, 0x000675, 0x010042, 0x08000A, + 0x003815, 0x010924, 0x280502, 0x0B000D, + 0x000820, 0x0002F5, 0x010040, 0x000606, + 0x220007, 0x000464, 0x000464, 0x000606, + 0x000007, 0x000134, 0x007F8D, 0x00093C, + 0x281D12, 0x282512, 0x001F32, 0x0E0007, + 0x00010D, 0x00037D, 0x000820, 0x018040, + 0x05D2F4, 0x000007, 0x080007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2D0286, + 0x000007, 0x000606, 0x000007, 0x000007, + 0x000012, 0x100007, 0x320007, 0x600007, + 0x100080, 0x48001A, 0x004904, 0x2D6186, + 0x000007, 0x001210, 0x58003A, 0x000145, + 0x5C5D04, 0x000007, 0x000080, 0x48001A, + 0x004904, 0x2DB186, 0x000007, 0x001210, + 0x50003A, 0x005904, 0x2E0886, 0x000045, + 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, + 0x004224, 0x500102, 0x200502, 0x000082, + 0x40001A, 0x004104, 0x2E3986, 0x000007, + 0x003865, 0x40001A, 0x004020, 0x00104D, + 0x04C184, 0x301B86, 0x000040, 0x040007, + 0x000165, 0x000145, 0x004020, 0x000040, + 0x000765, 0x080080, 0x40001A, 0x004104, + 0x2EC986, 0x000007, 0x001210, 0x40003A, + 0x004104, 0x2F2286, 0x00004D, 0x0000CD, + 0x004810, 0x20043A, 0x000882, 0x40001A, + 0x004104, 0x2F3186, 0x000007, 0x004820, + 0x005904, 0x300886, 0x000040, 0x0007E5, + 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, + 0x4216E0, 0x021260, 0x000040, 0x000032, + 0x400075, 0x00007D, 0x07D574, 0x200512, + 0x000082, 0x40001A, 0x004104, 0x2FE186, + 0x000007, 0x037206, 0x640007, 0x060007, + 0x0000E5, 0x000020, 0x000040, 0x000A65, + 0x000020, 0x020040, 0x020040, 0x000040, + 0x000165, 0x000042, 0x70000A, 0x007104, + 0x30A286, 0x000007, 0x018206, 0x640007, + 0x050000, 0x007020, 0x000040, 0x037206, + 0x640007, 0x000007, 0x00306D, 0x028860, + 0x029060, 0x08000A, 0x028860, 0x008040, + 0x100012, 0x00100D, 0x009184, 0x314186, + 0x000E0D, 0x009184, 0x325186, 0x000007, + 0x300007, 0x001020, 0x003B6D, 0x008040, + 0x000080, 0x08001A, 0x000904, 0x316186, + 0x000007, 0x001220, 0x000DED, 0x008040, + 0x008042, 0x10000A, 0x40000D, 0x109544, + 0x000007, 0x001020, 0x000DED, 0x008040, + 0x008042, 0x20040A, 0x000082, 0x08001A, + 0x000904, 0x31F186, 0x000007, 0x003B6D, + 0x008042, 0x08000A, 0x000E15, 0x010984, + 0x329B86, 0x600007, 0x08001A, 0x000C15, + 0x010984, 0x328386, 0x000020, 0x1A0007, + 0x0002ED, 0x008040, 0x620007, 0x00306D, + 0x028042, 0x0A804A, 0x000820, 0x0A804A, + 0x000606, 0x10804A, 0x000007, 0x282512, + 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, + 0x000786, 0x000007, 0x0C0007, 0x0A0007, + 0x1C0007, 0x003465, 0x020040, 0x004820, + 0x025060, 0x40000A, 0x024060, 0x000040, + 0x454944, 0x000007, 0x004020, 0x003AE5, + 0x000040, 0x0028E5, 0x000042, 0x48000A, + 0x004904, 0x386886, 0x002C65, 0x000042, + 0x40000A, 0x0000D5, 0x454104, 0x000007, + 0x000655, 0x054504, 0x34F286, 0x0001D5, + 0x054504, 0x34F086, 0x002B65, 0x000042, + 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, + 0x000007, 0x454504, 0x000007, 0x0000CD, + 0x444944, 0x000007, 0x454504, 0x000007, + 0x00014D, 0x554944, 0x000007, 0x045144, + 0x34E986, 0x002C65, 0x000042, 0x48000A, + 0x4CD104, 0x000007, 0x04C144, 0x34F386, + 0x000007, 0x160007, 0x002CE5, 0x040042, + 0x40000A, 0x004020, 0x000040, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x356086, + 0x000007, 0x002402, 0x36A206, 0x005C02, + 0x0025E5, 0x000042, 0x40000A, 0x004274, + 0x002AE5, 0x000042, 0x40000A, 0x004274, + 0x500112, 0x0029E5, 0x000042, 0x40000A, + 0x004234, 0x454104, 0x000007, 0x004020, + 0x000040, 0x003EE5, 0x000020, 0x000040, + 0x002DE5, 0x400152, 0x50000A, 0x045144, + 0x364A86, 0x0000C5, 0x003EE5, 0x004020, + 0x000040, 0x002BE5, 0x000042, 0x40000A, + 0x404254, 0x000007, 0x002AE5, 0x004020, + 0x000040, 0x500132, 0x040134, 0x005674, + 0x0029E5, 0x020042, 0x42000A, 0x000042, + 0x50000A, 0x05417C, 0x0028E5, 0x000042, + 0x48000A, 0x0000C5, 0x4CC144, 0x371086, + 0x0026E5, 0x0027E5, 0x020042, 0x40004A, + 0x50000A, 0x00423C, 0x00567C, 0x0028E5, + 0x004820, 0x000040, 0x281D12, 0x282512, + 0x001F72, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x37AA86, 0x0E0007, 0x160007, + 0x1E0007, 0x003EE5, 0x000042, 0x40000A, + 0x004104, 0x37E886, 0x002D65, 0x000042, + 0x28340A, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, + 0x54D104, 0x00735C, 0x385186, 0x000007, + 0x000606, 0x080007, 0x0C0007, 0x080007, + 0x0A0007, 0x0001E5, 0x020045, 0x004020, + 0x000060, 0x000365, 0x000040, 0x002E65, + 0x001A20, 0x0A1A60, 0x000040, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x000606, 0x50004A, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +// -------------------------------------------- +// DS-1E Controller InstructionRAM Code +// 1999/06/21 +// Buf441 slot is Enabled. +// -------------------------------------------- +// 04/09 creat +// 04/12 stop nise fix +// 06/21 WorkingOff timming +static u32 CntrlInst1E[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x00800D, 0x000810, 0x20043A, 0x001A82, + 0x03460D, 0x000810, 0x10043A, 0x02EC0D, + 0x000810, 0x18043A, 0x00010D, 0x020015, + 0x0000FD, 0x000020, 0x038860, 0x039060, + 0x038060, 0x038040, 0x038040, 0x038040, + 0x018040, 0x000A7D, 0x038040, 0x038040, + 0x018040, 0x200402, 0x000882, 0x08001A, + 0x000904, 0x017186, 0x000007, 0x260007, + 0x400007, 0x000007, 0x03258D, 0x000810, + 0x18043A, 0x260007, 0x284402, 0x00087D, + 0x018042, 0x00160A, 0x05A206, 0x000007, + 0x440007, 0x00230D, 0x000810, 0x08043A, + 0x22FA06, 0x000007, 0x0007FD, 0x018042, + 0x08000A, 0x000904, 0x02AB86, 0x000195, + 0x090D04, 0x000007, 0x000820, 0x0000F5, + 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, + 0x018040, 0x000A7D, 0x038042, 0x13804A, + 0x18000A, 0x001820, 0x059060, 0x058860, + 0x018040, 0x0000FD, 0x018042, 0x70000A, + 0x000115, 0x071144, 0x033B86, 0x030000, + 0x007020, 0x036206, 0x018040, 0x00360D, + 0x000810, 0x08043A, 0x232206, 0x000007, + 0x02EC0D, 0x000810, 0x18043A, 0x019A06, + 0x000007, 0x240007, 0x000F8D, 0x000810, + 0x00163A, 0x002402, 0x005C02, 0x0028FD, + 0x000020, 0x018040, 0x08000D, 0x000815, + 0x510984, 0x000007, 0x00004D, 0x000E5D, + 0x000E02, 0x00430D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x00008D, 0x000924, + 0x000F02, 0x00470D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x480480, 0x001210, + 0x28043A, 0x00778D, 0x000810, 0x280C3A, + 0x00068D, 0x000810, 0x28143A, 0x284402, + 0x03258D, 0x000810, 0x18043A, 0x07FF8D, + 0x000820, 0x0002FD, 0x018040, 0x260007, + 0x200007, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x051286, 0x000007, 0x240007, + 0x02EC0D, 0x000810, 0x18043A, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x019B86, 0x000007, 0x01B206, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x22B886, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x065A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x036186, 0x000007, 0x002104, 0x036186, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x07C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00788D, 0x000810, 0x08043A, 0x2A1206, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x070206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x229086, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x225886, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x08DA86, 0x00057D, 0x002820, + 0x03B060, 0x08F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x08FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x009D8D, 0x000810, + 0x08043A, 0x2A1206, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x0A5186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x00107D, 0x018042, + 0x08000A, 0x000215, 0x010984, 0x3A8186, + 0x000007, 0x007FBD, 0x383DC4, 0x000007, + 0x001A7D, 0x001375, 0x018042, 0x09004A, + 0x10000A, 0x0B8D04, 0x139504, 0x000007, + 0x000820, 0x019060, 0x001104, 0x225886, + 0x010040, 0x0017FD, 0x018042, 0x08000A, + 0x000904, 0x225A86, 0x000007, 0x00197D, + 0x038042, 0x09804A, 0x10000A, 0x000924, + 0x001664, 0x0011FD, 0x038042, 0x2B804A, + 0x19804A, 0x00008D, 0x218944, 0x000007, + 0x002244, 0x0C1986, 0x000007, 0x001A64, + 0x002A24, 0x00197D, 0x080102, 0x100122, + 0x000820, 0x039060, 0x018040, 0x003DFD, + 0x00008D, 0x000820, 0x018040, 0x001375, + 0x001A7D, 0x010042, 0x09804A, 0x10000A, + 0x00021D, 0x0189E4, 0x2992E4, 0x309144, + 0x000007, 0x00060D, 0x000A15, 0x000C1D, + 0x001025, 0x00A9E4, 0x012BE4, 0x000464, + 0x01B3E4, 0x0232E4, 0x000464, 0x000464, + 0x000464, 0x000464, 0x00040D, 0x08B1C4, + 0x000007, 0x000820, 0x000BF5, 0x030040, + 0x00197D, 0x038042, 0x09804A, 0x000A24, + 0x08000A, 0x080E64, 0x000007, 0x100122, + 0x000820, 0x031060, 0x010040, 0x0064AC, + 0x00027D, 0x000020, 0x018040, 0x00107D, + 0x018042, 0x0011FD, 0x3B804A, 0x09804A, + 0x20000A, 0x000095, 0x1A1144, 0x00A144, + 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, + 0x0018FD, 0x018042, 0x0010FD, 0x09804A, + 0x28000A, 0x000095, 0x010924, 0x002A64, + 0x0E4986, 0x000007, 0x002904, 0x0E5A86, + 0x000007, 0x0E6206, 0x080002, 0x00008D, + 0x00387D, 0x000820, 0x018040, 0x00127D, + 0x018042, 0x10000A, 0x003904, 0x0F0986, + 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, + 0x000025, 0x0FB206, 0x00002D, 0x000015, + 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, + 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, + 0x400025, 0x00008D, 0x110944, 0x000007, + 0x00018D, 0x109504, 0x000007, 0x009164, + 0x000424, 0x000424, 0x000424, 0x100102, + 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, + 0x00018D, 0x00042D, 0x00008D, 0x109504, + 0x000007, 0x00020D, 0x109184, 0x000007, + 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, + 0x018040, 0x003BFD, 0x001020, 0x03A860, + 0x000815, 0x313184, 0x212184, 0x000007, + 0x03B060, 0x03A060, 0x018040, 0x0022FD, + 0x000095, 0x010924, 0x000424, 0x000424, + 0x001264, 0x100102, 0x000820, 0x039060, + 0x018040, 0x001924, 0x010F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x000424, 0x000424, + 0x00117D, 0x018042, 0x08000A, 0x000A24, + 0x280502, 0x280C02, 0x09800D, 0x000820, + 0x0002FD, 0x018040, 0x200007, 0x0022FD, + 0x018042, 0x08000A, 0x000095, 0x280DC4, + 0x011924, 0x00197D, 0x018042, 0x0011FD, + 0x09804A, 0x10000A, 0x0000B5, 0x113144, + 0x0A8D04, 0x000007, 0x080A44, 0x129504, + 0x000007, 0x0023FD, 0x001020, 0x038040, + 0x101244, 0x000007, 0x000820, 0x039060, + 0x018040, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x123286, 0x000007, 0x003BFD, + 0x000100, 0x000A10, 0x0B807A, 0x13804A, + 0x090984, 0x000007, 0x000095, 0x013D04, + 0x12B886, 0x10000A, 0x100002, 0x090984, + 0x000007, 0x038042, 0x11804A, 0x090D04, + 0x000007, 0x10000A, 0x090D84, 0x000007, + 0x00257D, 0x000820, 0x018040, 0x00010D, + 0x000810, 0x28143A, 0x00127D, 0x018042, + 0x20000A, 0x00197D, 0x018042, 0x00117D, + 0x31804A, 0x10000A, 0x003124, 0x013B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x300102, + 0x003124, 0x000424, 0x000424, 0x001224, + 0x280502, 0x001A4C, 0x143986, 0x700002, + 0x00002D, 0x030000, 0x00387D, 0x018042, + 0x10000A, 0x146206, 0x002124, 0x0000AD, + 0x100002, 0x00010D, 0x000924, 0x006B24, + 0x014A0D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x003264, 0x00008D, 0x000A24, 0x001020, + 0x00227D, 0x018040, 0x014F8D, 0x000810, + 0x08043A, 0x2B5A06, 0x000007, 0x002820, + 0x00207D, 0x018040, 0x00117D, 0x038042, + 0x13804A, 0x33800A, 0x00387D, 0x018042, + 0x08000A, 0x000904, 0x177286, 0x000007, + 0x00008D, 0x030964, 0x015B0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x380102, 0x000424, + 0x000424, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x280502, 0x001A4C, 0x177186, 0x000007, + 0x032164, 0x00632C, 0x003DFD, 0x018042, + 0x08000A, 0x000095, 0x090904, 0x000007, + 0x000820, 0x001A4C, 0x169986, 0x018040, + 0x030000, 0x16B206, 0x002124, 0x00010D, + 0x000924, 0x006B24, 0x016F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x003A64, 0x000095, + 0x001224, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x171286, 0x000007, 0x01760D, + 0x000810, 0x08043A, 0x2B5A06, 0x000007, + 0x160A06, 0x000007, 0x007020, 0x08010A, + 0x10012A, 0x0020FD, 0x038860, 0x039060, + 0x018040, 0x00227D, 0x018042, 0x003DFD, + 0x08000A, 0x31844A, 0x000904, 0x181086, + 0x18008B, 0x00008D, 0x189904, 0x00312C, + 0x18E206, 0x000007, 0x00324C, 0x186B86, + 0x000007, 0x001904, 0x186886, 0x000007, + 0x000095, 0x199144, 0x00222C, 0x003124, + 0x00636C, 0x000E3D, 0x001375, 0x000BFD, + 0x010042, 0x09804A, 0x10000A, 0x038AEC, + 0x0393EC, 0x00224C, 0x18E186, 0x000007, + 0x00008D, 0x189904, 0x00226C, 0x00322C, + 0x30050A, 0x301DAB, 0x002083, 0x0018FD, + 0x018042, 0x08000A, 0x018924, 0x300502, + 0x001083, 0x001875, 0x010042, 0x10000A, + 0x00008D, 0x010924, 0x001375, 0x330542, + 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, + 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, + 0x006083, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x19B286, 0x000007, 0x001E2D, + 0x0005FD, 0x018042, 0x08000A, 0x028924, + 0x280502, 0x00060D, 0x000810, 0x280C3A, + 0x00008D, 0x000810, 0x28143A, 0x0A808D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x001275, 0x030042, 0x21004A, 0x00008D, + 0x1A0944, 0x000007, 0x01AB8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, + 0x030042, 0x0D004A, 0x10000A, 0x089144, + 0x000007, 0x000820, 0x010040, 0x0025F5, + 0x0A3144, 0x000007, 0x000820, 0x032860, + 0x030040, 0x00217D, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00008D, 0x000124, 0x00012C, 0x000E64, + 0x001A64, 0x00636C, 0x08010A, 0x10012A, + 0x000820, 0x031060, 0x030040, 0x0020FD, + 0x018042, 0x08000A, 0x00227D, 0x018042, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00197D, 0x018042, 0x08000A, 0x0022FD, + 0x038042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x090D04, 0x000007, 0x000820, + 0x030040, 0x038042, 0x0B804A, 0x10000A, + 0x000820, 0x031060, 0x030040, 0x038042, + 0x13804A, 0x19804A, 0x110D04, 0x198D04, + 0x000007, 0x08000A, 0x001020, 0x031860, + 0x030860, 0x030040, 0x00008D, 0x0B0944, + 0x000007, 0x000820, 0x010040, 0x0005F5, + 0x030042, 0x08000A, 0x000820, 0x010040, + 0x0000F5, 0x010042, 0x08000A, 0x000904, + 0x1D9886, 0x001E75, 0x030042, 0x01044A, + 0x000C0A, 0x1DAA06, 0x000007, 0x000402, + 0x000C02, 0x00177D, 0x001AF5, 0x018042, + 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, + 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, + 0x00043D, 0x0013F5, 0x001AFD, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x005502, 0x005D0A, 0x030042, 0x0B004A, + 0x1B804A, 0x13804A, 0x20000A, 0x089144, + 0x19A144, 0x0389E4, 0x0399EC, 0x006502, + 0x006D0A, 0x030042, 0x0B004A, 0x19004A, + 0x2B804A, 0x13804A, 0x21804A, 0x30000A, + 0x089144, 0x19A144, 0x2AB144, 0x0389E4, + 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, + 0x000702, 0x00107D, 0x000415, 0x018042, + 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, + 0x0019FD, 0x010042, 0x09804A, 0x10000A, + 0x000934, 0x001674, 0x0029F5, 0x010042, + 0x10000A, 0x00917C, 0x002075, 0x010042, + 0x08000A, 0x000904, 0x200A86, 0x0026F5, + 0x0027F5, 0x030042, 0x09004A, 0x10000A, + 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, + 0x010042, 0x51804A, 0x48000A, 0x160007, + 0x001075, 0x010042, 0x282C0A, 0x281D12, + 0x282512, 0x001F32, 0x1E0007, 0x0E0007, + 0x001975, 0x010042, 0x002DF5, 0x0D004A, + 0x10000A, 0x009144, 0x20EA86, 0x010042, + 0x28340A, 0x000E5D, 0x00008D, 0x000375, + 0x000820, 0x010040, 0x05D2F4, 0x54D104, + 0x00735C, 0x218B86, 0x000007, 0x0C0007, + 0x080007, 0x0A0007, 0x02178D, 0x000810, + 0x08043A, 0x34B206, 0x000007, 0x219206, + 0x000007, 0x080007, 0x002275, 0x010042, + 0x20000A, 0x002104, 0x225886, 0x001E2D, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x21CA86, 0x000007, 0x002010, 0x30043A, + 0x00057D, 0x0180C3, 0x08000A, 0x028924, + 0x280502, 0x280C02, 0x0A810D, 0x000820, + 0x0002F5, 0x010040, 0x220007, 0x0004FD, + 0x018042, 0x70000A, 0x030000, 0x007020, + 0x07FA06, 0x018040, 0x022B8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x22C286, + 0x000007, 0x020206, 0x000007, 0x000875, + 0x0009FD, 0x00010D, 0x234206, 0x000295, + 0x000B75, 0x00097D, 0x00000D, 0x000515, + 0x010042, 0x18000A, 0x001904, 0x2A0086, + 0x0006F5, 0x001020, 0x010040, 0x0004F5, + 0x000820, 0x010040, 0x000775, 0x010042, + 0x09804A, 0x10000A, 0x001124, 0x000904, + 0x23F286, 0x000815, 0x080102, 0x101204, + 0x241206, 0x000575, 0x081204, 0x000007, + 0x100102, 0x000575, 0x000425, 0x021124, + 0x100102, 0x000820, 0x031060, 0x010040, + 0x001924, 0x2A0086, 0x00008D, 0x000464, + 0x009D04, 0x291086, 0x180102, 0x000575, + 0x010042, 0x28040A, 0x00018D, 0x000924, + 0x280D02, 0x00000D, 0x000924, 0x281502, + 0x10000D, 0x000820, 0x0002F5, 0x010040, + 0x200007, 0x001175, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x24FA86, 0x000007, + 0x000100, 0x080B20, 0x130B60, 0x1B0B60, + 0x030A60, 0x010040, 0x050042, 0x3D004A, + 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, + 0x010042, 0x28140A, 0x0004F5, 0x010042, + 0x08000A, 0x000315, 0x010D04, 0x260286, + 0x004015, 0x000095, 0x010D04, 0x25F086, + 0x100022, 0x10002A, 0x261A06, 0x000007, + 0x333104, 0x2AA904, 0x000007, 0x032124, + 0x280502, 0x284402, 0x001124, 0x400102, + 0x000424, 0x000424, 0x003224, 0x00292C, + 0x00636C, 0x277386, 0x000007, 0x02B164, + 0x000464, 0x000464, 0x00008D, 0x000A64, + 0x280D02, 0x10008D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x00008D, 0x38B904, + 0x000007, 0x03296C, 0x30010A, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x270286, + 0x000007, 0x00212C, 0x28050A, 0x00316C, + 0x00046C, 0x00046C, 0x28450A, 0x001124, + 0x006B64, 0x100102, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x004124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x27FA86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x284086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x284A86, 0x000007, 0x284402, 0x003124, + 0x300502, 0x003924, 0x300583, 0x000883, + 0x0005F5, 0x010042, 0x28040A, 0x00008D, + 0x008124, 0x280D02, 0x00008D, 0x008124, + 0x281502, 0x10018D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001025, 0x000575, + 0x030042, 0x09004A, 0x10000A, 0x0A0904, + 0x121104, 0x000007, 0x001020, 0x050860, + 0x050040, 0x0006FD, 0x018042, 0x09004A, + 0x10000A, 0x0000A5, 0x0A0904, 0x121104, + 0x000007, 0x000820, 0x019060, 0x010040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x29CA86, 0x000007, 0x244206, 0x000007, + 0x000606, 0x000007, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x2A1A86, 0x000007, + 0x000100, 0x080B20, 0x138B60, 0x1B8B60, + 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, + 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, + 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, + 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, + 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, + 0x038A60, 0x000606, 0x018040, 0x00008D, + 0x000A64, 0x280D02, 0x000A24, 0x00027D, + 0x018042, 0x10000A, 0x001224, 0x0003FD, + 0x018042, 0x08000A, 0x000904, 0x2C0A86, + 0x000007, 0x00018D, 0x000A24, 0x000464, + 0x000464, 0x080102, 0x000924, 0x000424, + 0x000424, 0x100102, 0x02000D, 0x009144, + 0x2C6186, 0x000007, 0x0001FD, 0x018042, + 0x08000A, 0x000A44, 0x2C4386, 0x018042, + 0x0A000D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00027D, 0x001020, 0x000606, + 0x018040, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x2CB286, 0x000007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2CE286, + 0x000007, 0x000075, 0x002E7D, 0x010042, + 0x0B804A, 0x000020, 0x000904, 0x000686, + 0x010040, 0x31844A, 0x30048B, 0x000883, + 0x00008D, 0x000810, 0x28143A, 0x00008D, + 0x000810, 0x280C3A, 0x000675, 0x010042, + 0x08000A, 0x003815, 0x010924, 0x280502, + 0x0B000D, 0x000820, 0x0002F5, 0x010040, + 0x000606, 0x220007, 0x000464, 0x000464, + 0x000606, 0x000007, 0x000134, 0x007F8D, + 0x00093C, 0x281D12, 0x282512, 0x001F32, + 0x0E0007, 0x00010D, 0x00037D, 0x000820, + 0x018040, 0x05D2F4, 0x000007, 0x080007, + 0x00037D, 0x018042, 0x08000A, 0x000904, + 0x2E8A86, 0x000007, 0x000606, 0x000007, + 0x000007, 0x000012, 0x100007, 0x320007, + 0x600007, 0x460007, 0x100080, 0x48001A, + 0x004904, 0x2EF186, 0x000007, 0x001210, + 0x58003A, 0x000145, 0x5C5D04, 0x000007, + 0x000080, 0x48001A, 0x004904, 0x2F4186, + 0x000007, 0x001210, 0x50003A, 0x005904, + 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, + 0x7FFF7D, 0x07D524, 0x004224, 0x500102, + 0x200502, 0x000082, 0x40001A, 0x004104, + 0x2FC986, 0x000007, 0x003865, 0x40001A, + 0x004020, 0x00104D, 0x04C184, 0x31AB86, + 0x000040, 0x040007, 0x000165, 0x000145, + 0x004020, 0x000040, 0x000765, 0x080080, + 0x40001A, 0x004104, 0x305986, 0x000007, + 0x001210, 0x40003A, 0x004104, 0x30B286, + 0x00004D, 0x0000CD, 0x004810, 0x20043A, + 0x000882, 0x40001A, 0x004104, 0x30C186, + 0x000007, 0x004820, 0x005904, 0x319886, + 0x000040, 0x0007E5, 0x200480, 0x2816A0, + 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, + 0x000040, 0x000032, 0x400075, 0x00007D, + 0x07D574, 0x200512, 0x000082, 0x40001A, + 0x004104, 0x317186, 0x000007, 0x038A06, + 0x640007, 0x0000E5, 0x000020, 0x000040, + 0x000A65, 0x000020, 0x020040, 0x020040, + 0x000040, 0x000165, 0x000042, 0x70000A, + 0x007104, 0x323286, 0x000007, 0x060007, + 0x019A06, 0x640007, 0x050000, 0x007020, + 0x000040, 0x038A06, 0x640007, 0x000007, + 0x00306D, 0x028860, 0x029060, 0x08000A, + 0x028860, 0x008040, 0x100012, 0x00100D, + 0x009184, 0x32D186, 0x000E0D, 0x009184, + 0x33E186, 0x000007, 0x300007, 0x001020, + 0x003B6D, 0x008040, 0x000080, 0x08001A, + 0x000904, 0x32F186, 0x000007, 0x001220, + 0x000DED, 0x008040, 0x008042, 0x10000A, + 0x40000D, 0x109544, 0x000007, 0x001020, + 0x000DED, 0x008040, 0x008042, 0x20040A, + 0x000082, 0x08001A, 0x000904, 0x338186, + 0x000007, 0x003B6D, 0x008042, 0x08000A, + 0x000E15, 0x010984, 0x342B86, 0x600007, + 0x08001A, 0x000C15, 0x010984, 0x341386, + 0x000020, 0x1A0007, 0x0002ED, 0x008040, + 0x620007, 0x00306D, 0x028042, 0x0A804A, + 0x000820, 0x0A804A, 0x000606, 0x10804A, + 0x000007, 0x282512, 0x001F32, 0x05D2F4, + 0x54D104, 0x00735C, 0x000786, 0x000007, + 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, + 0x020040, 0x004820, 0x025060, 0x40000A, + 0x024060, 0x000040, 0x454944, 0x000007, + 0x004020, 0x003AE5, 0x000040, 0x0028E5, + 0x000042, 0x48000A, 0x004904, 0x39F886, + 0x002C65, 0x000042, 0x40000A, 0x0000D5, + 0x454104, 0x000007, 0x000655, 0x054504, + 0x368286, 0x0001D5, 0x054504, 0x368086, + 0x002B65, 0x000042, 0x003AE5, 0x50004A, + 0x40000A, 0x45C3D4, 0x000007, 0x454504, + 0x000007, 0x0000CD, 0x444944, 0x000007, + 0x454504, 0x000007, 0x00014D, 0x554944, + 0x000007, 0x045144, 0x367986, 0x002C65, + 0x000042, 0x48000A, 0x4CD104, 0x000007, + 0x04C144, 0x368386, 0x000007, 0x160007, + 0x002CE5, 0x040042, 0x40000A, 0x004020, + 0x000040, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x36F086, 0x000007, 0x002402, + 0x383206, 0x005C02, 0x0025E5, 0x000042, + 0x40000A, 0x004274, 0x002AE5, 0x000042, + 0x40000A, 0x004274, 0x500112, 0x0029E5, + 0x000042, 0x40000A, 0x004234, 0x454104, + 0x000007, 0x004020, 0x000040, 0x003EE5, + 0x000020, 0x000040, 0x002DE5, 0x400152, + 0x50000A, 0x045144, 0x37DA86, 0x0000C5, + 0x003EE5, 0x004020, 0x000040, 0x002BE5, + 0x000042, 0x40000A, 0x404254, 0x000007, + 0x002AE5, 0x004020, 0x000040, 0x500132, + 0x040134, 0x005674, 0x0029E5, 0x020042, + 0x42000A, 0x000042, 0x50000A, 0x05417C, + 0x0028E5, 0x000042, 0x48000A, 0x0000C5, + 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, + 0x020042, 0x40004A, 0x50000A, 0x00423C, + 0x00567C, 0x0028E5, 0x004820, 0x000040, + 0x281D12, 0x282512, 0x001F72, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x393A86, + 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, + 0x000042, 0x40000A, 0x004104, 0x397886, + 0x002D65, 0x000042, 0x28340A, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, + 0x39E186, 0x000007, 0x000606, 0x080007, + 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, + 0x020045, 0x004020, 0x000060, 0x000365, + 0x000040, 0x002E65, 0x001A20, 0x0A1A60, + 0x000040, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x000606, 0x50004A, + 0x0017FD, 0x018042, 0x08000A, 0x000904, + 0x225A86, 0x000007, 0x00107D, 0x018042, + 0x0011FD, 0x33804A, 0x19804A, 0x20000A, + 0x000095, 0x2A1144, 0x01A144, 0x3B9086, + 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, + 0x018042, 0x0010FD, 0x09804A, 0x38000A, + 0x000095, 0x010924, 0x003A64, 0x3B8186, + 0x000007, 0x003904, 0x3B9286, 0x000007, + 0x3B9A06, 0x00000D, 0x00008D, 0x000820, + 0x00387D, 0x018040, 0x700002, 0x00117D, + 0x018042, 0x00197D, 0x29804A, 0x30000A, + 0x380002, 0x003124, 0x000424, 0x000424, + 0x002A24, 0x280502, 0x00068D, 0x000810, + 0x28143A, 0x00750D, 0x00B124, 0x002264, + 0x3D0386, 0x284402, 0x000810, 0x280C3A, + 0x0B800D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00758D, 0x00B124, 0x100102, + 0x012144, 0x3E4986, 0x001810, 0x10003A, + 0x00387D, 0x018042, 0x08000A, 0x000904, + 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, + 0x00008D, 0x023164, 0x000A64, 0x280D02, + 0x0B808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00387D, 0x018042, 0x08000A, + 0x000904, 0x3E3286, 0x030000, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x3D8286, + 0x000007, 0x002810, 0x28043A, 0x00750D, + 0x030924, 0x002264, 0x280D02, 0x02316C, + 0x28450A, 0x0B810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x00008D, 0x000A24, + 0x3E4A06, 0x100102, 0x001810, 0x10003A, + 0x0000BD, 0x003810, 0x30043A, 0x00187D, + 0x018042, 0x0018FD, 0x09804A, 0x20000A, + 0x0000AD, 0x028924, 0x07212C, 0x001010, + 0x300583, 0x300D8B, 0x3014BB, 0x301C83, + 0x002083, 0x00137D, 0x038042, 0x33844A, + 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, + 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, + 0x001E0D, 0x0005FD, 0x018042, 0x20000A, + 0x020924, 0x00068D, 0x00A96C, 0x00009D, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x3F6A86, 0x000007, 0x280502, 0x280D0A, + 0x284402, 0x001810, 0x28143A, 0x0C008D, + 0x000820, 0x0002FD, 0x018040, 0x220007, + 0x003904, 0x225886, 0x001E0D, 0x00057D, + 0x018042, 0x20000A, 0x020924, 0x0000A5, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x402A86, 0x000007, 0x280502, 0x280C02, + 0x002010, 0x28143A, 0x0C010D, 0x000820, + 0x0002FD, 0x018040, 0x225A06, 0x220007, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +#endif //_HWMCODE_ diff -Nru a/sound/oss/yss225.c b/sound/oss/yss225.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/yss225.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,319 @@ +#include + +unsigned char page_zero[] __initdata = { +0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, +0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, +0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, +0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, +0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, +0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, +0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, +0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, +0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, +0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, +0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, +0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, +0x1d, 0x02, 0xdf +}; + +unsigned char page_one[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, +0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, +0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, +0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, +0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, +0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, +0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, +0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, +0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, +0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, +0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, +0x60, 0x00, 0x1b +}; + +unsigned char page_two[] __initdata = { +0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, +0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, +0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, +0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, +0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, +0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, +0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, +0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 +}; + +unsigned char page_three[] __initdata = { +0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, +0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, +0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, +0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, +0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, +0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 +}; + +unsigned char page_four[] __initdata = { +0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, +0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, +0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, +0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, +0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 +}; + +unsigned char page_six[] __initdata = { +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, +0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, +0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, +0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, +0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, +0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, +0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, +0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, +0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, +0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, +0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, +0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, +0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, +0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, +0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, +0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, +0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, +0x80, 0x00, 0x7e, 0x80, 0x80 +}; + +unsigned char page_seven[] __initdata = { +0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, +0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, +0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, +0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, +0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, +0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, +0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, +0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, +0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, +0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, +0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, +0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x02, 0x00 +}; + +unsigned char page_zero_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_one_v2[] __initdata = { +0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_two_v2[] __initdata = { +0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +unsigned char page_three_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +unsigned char page_four_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_seven_v2[] __initdata = { +0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned char mod_v2[] __initdata = { +0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, +0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, +0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, +0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, +0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, +0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, +0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, +0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, +0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, +0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, +0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, +0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, +0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, +0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, +0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, +0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, +0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, +0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, +0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, +0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, +0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, +0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, +0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, +0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, +0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, +0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, +0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, +0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 +}; +unsigned char coefficients[] __initdata = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, +0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, +0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, +0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, +0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, +0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, +0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, +0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, +0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, +0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, +0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, +0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, +0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, +0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, +0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, +0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, +0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, +0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, +0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, +0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, +0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, +0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, +0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, +0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, +0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, +0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, +0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, +0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, +0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, +0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, +0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, +0xba +}; +unsigned char coefficients2[] __initdata = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, +0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, +0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, +0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, +0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 +}; +unsigned char coefficients3[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, +0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, +0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, +0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, +0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, +0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, +0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, +0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, +0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, +0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, +0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, +0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, +0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, +0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, +0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, +0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, +0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, +0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, +0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, +0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, +0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, +0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, +0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, +0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, +0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, +0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, +0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, +0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, +0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, +0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, +0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, +0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, +0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, +0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, +0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, +0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, +0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff +}; + diff -Nru a/sound/oss/yss225.h b/sound/oss/yss225.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/oss/yss225.h Tue Feb 19 18:08:59 2002 @@ -0,0 +1,24 @@ +#ifndef __yss255_h__ +#define __yss255_h__ + +extern unsigned char page_zero[256]; +extern unsigned char page_one[256]; +extern unsigned char page_two[128]; +extern unsigned char page_three[128]; +extern unsigned char page_four[128]; +extern unsigned char page_six[192]; +extern unsigned char page_seven[256]; +extern unsigned char page_zero_v2[96]; +extern unsigned char page_one_v2[96]; +extern unsigned char page_two_v2[48]; +extern unsigned char page_three_v2[48]; +extern unsigned char page_four_v2[48]; +extern unsigned char page_seven_v2[96]; +extern unsigned char mod_v2[304]; +extern unsigned char coefficients[364]; +extern unsigned char coefficients2[56]; +extern unsigned char coefficients3[404]; + + +#endif /* __ys225_h__ */ + diff -Nru a/sound/pci/Config.in b/sound/pci/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/Config.in Tue Feb 19 18:09:00 2002 @@ -0,0 +1,32 @@ +# ALSA PCI drivers + +mainmenu_option next_comment +comment 'PCI devices' + +dep_tristate 'ALi PCI Audio M5451' CONFIG_SND_ALI5451 $CONFIG_SND +dep_tristate 'Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x' CONFIG_SND_CS46XX $CONFIG_SND +if [ "$CONFIG_SND_CS46XX" != "n" ]; then + bool ' Cirrus Logic (Sound Fusion) MMAP support for OSS' CONFIG_SND_CS46XX_ACCEPT_VALID +fi +dep_tristate 'EMU10K1 (SB Live!, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND +dep_tristate 'Korg 1212 IO' CONFIG_SND_KORG1212 $CONFIG_SND +dep_tristate 'NeoMagic NM256AV/ZX' CONFIG_SND_NM256 $CONFIG_SND +dep_tristate 'RME Digi96, 96/8, 96/8 PRO' CONFIG_SND_RME96 $CONFIG_SND +dep_tristate 'RME Digi9652 (Hammerfall)' CONFIG_SND_RME9652 $CONFIG_SND +dep_tristate 'Trident 4D-Wave DX/NX; SiS 7018' CONFIG_SND_TRIDENT $CONFIG_SND +dep_tristate 'Yamaha YMF724/740/744/754' CONFIG_SND_YMFPCI $CONFIG_SND +dep_tristate 'Avance Logic ALS4000' CONFIG_SND_ALS4000 $CONFIG_SND +dep_tristate 'C-Media 8738, 8338' CONFIG_SND_CMIPCI $CONFIG_SND +dep_tristate '(Creative) Ensoniq AudioPCI 1370' CONFIG_SND_ENS1370 $CONFIG_SND +dep_tristate '(Creative) Ensoniq AudioPCI 1371/1373' CONFIG_SND_ENS1371 $CONFIG_SND +dep_tristate 'ESS ES1938/1946 (Solo-1)' CONFIG_SND_ES1938 $CONFIG_SND +dep_tristate 'ESS ES1968/1978 (Maestro-1/2/2E)' CONFIG_SND_ES1968 $CONFIG_SND +dep_tristate 'ESS Allegro/Maestro3' CONFIG_SND_MAESTRO3 $CONFIG_SND +dep_tristate 'ForteMedia FM801' CONFIG_SND_FM801 $CONFIG_SND +dep_tristate 'ICEnsemble ICE1712 (Envy24)' CONFIG_SND_ICE1712 $CONFIG_SND +dep_tristate 'Intel i810/i820/i830/i840/MX440 integrated audio' CONFIG_SND_INTEL8X0 $CONFIG_SND +dep_tristate 'S3 SonicVibes' CONFIG_SND_SONICVIBES $CONFIG_SND +dep_tristate 'VIA 82C686A/B South Bridge' CONFIG_SND_VIA686 $CONFIG_SND +dep_tristate 'VIA 8233 South Bridge' CONFIG_SND_VIA8233 $CONFIG_SND + +endmenu diff -Nru a/sound/pci/Makefile b/sound/pci/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,94 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := pci.o + +subdir-y := ac97 ali5451 cs46xx emu10k1 korg1212 nm256 rme9652 trident ymfpci +subdir-m := $(subdir-y) + +list-multi := snd-als4000.o snd-cmipci.o snd-cs4281.o snd-ens1370.o \ + snd-ens1371.o snd-es1938.o snd-es1968.o snd-fm801.o \ + snd-ice1712.o snd-intel8x0.o snd-maestro3.o snd-rme96.o \ + snd-sonicvibes.o snd-via686.o snd-via8233.o + +snd-als4000-objs := als4000.o +snd-cmipci-objs := cmipci.o +snd-cs4281-objs := cs4281.o +snd-ens1370-objs := ens1370.o +snd-ens1371-objs := ens1371.o +snd-es1938-objs := es1938.o +snd-es1968-objs := es1968.o +snd-fm801-objs := fm801.o +snd-ice1712-objs := ice1712.o +snd-intel8x0-objs := intel8x0.o +snd-maestro3-objs := maestro3.o +snd-rme96-objs := rme96.o +snd-sonicvibes-objs := sonicvibes.o +snd-via686-objs := via686.o +snd-via8233-objs := via8233.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS4000) += snd-als4000.o +obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o +obj-$(CONFIG_SND_CS4281) += snd-cs4281.o +obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o +obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o +obj-$(CONFIG_SND_ES1938) += snd-es1938.o +obj-$(CONFIG_SND_ES1968) += snd-es1968.o +obj-$(CONFIG_SND_FM801) += snd-fm801.o +obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o +obj-$(CONFIG_SND_INTEL8X0) += snd-intel8x0.o +obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o +obj-$(CONFIG_SND_RME96) += snd-rme96.o +obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o +obj-$(CONFIG_SND_VIA686) += snd-via686.o +obj-$(CONFIG_SND_VIA8233) += snd-via8233.o + +include $(TOPDIR)/Rules.make + +snd-als4000.o: $(snd-als4000-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-als4000-objs) + +snd-cmipci.o: $(snd-cmipci-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cmipci-objs) + +snd-cs4281.o: $(snd-cs4281-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4281-objs) + +snd-ens1370.o: $(snd-ens1370-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ens1370-objs) + +snd-ens1371.o: $(snd-ens1371-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ens1371-objs) + +snd-es1938.o: $(snd-es1938-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1938-objs) + +snd-es1968.o: $(snd-es1968-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1968-objs) + +snd-fm801.o: $(snd-fm801-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-fm801-objs) + +snd-ice1712.o: $(snd-ice1712-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ice1712-objs) + +snd-intel8x0.o: $(snd-intel8x0-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-intel8x0-objs) + +snd-maestro3.o: $(snd-maestro3-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-maestro3-objs) + +snd-rme96.o: $(snd-rme96-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rme96-objs) + +snd-sonicvibes.o: $(snd-sonicvibes-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sonicvibes-objs) + +snd-via686.o: $(snd-via686-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-via686-objs) + +snd-via8233.o: $(snd-via8233-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-via8233-objs) diff -Nru a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ac97/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,39 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ac97.o + +list-multi := snd-ac97-codec.o snd-ak4531-codec.o + +export-objs := ac97_codec.o ak4531_codec.o + +snd-ac97-codec-objs := ac97_codec.o +snd-ak4531-codec-objs := ak4531_codec.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_CS4281) += snd-ac97-codec.o +obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o +obj-$(CONFIG_SND_ENS1371) += snd-ac97-codec.o +obj-$(CONFIG_SND_ES1968) += snd-ac97-codec.o +obj-$(CONFIG_SND_FM801) += snd-ac97-codec.o +obj-$(CONFIG_SND_ICE1712) += snd-ac97-codec.o +obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o +obj-$(CONFIG_SND_MAESTRO3) += snd-ac97-codec.o +obj-$(CONFIG_SND_VIA686) += snd-ac97-codec.o +obj-$(CONFIG_SND_VIA8233) += snd-ac97-codec.o +obj-$(CONFIG_SND_ALI5451) += snd-ac97-codec.o +obj-$(CONFIG_SND_CS46XX) += snd-ac97-codec.o +obj-$(CONFIG_SND_EMU10K1) += snd-ac97-codec.o +obj-$(CONFIG_SND_NM256) += snd-ac97-codec.o +obj-$(CONFIG_SND_TRIDENT) += snd-ac97-codec.o +obj-$(CONFIG_SND_YMFPCI) += snd-ac97-codec.o + +include $(TOPDIR)/Rules.make + +snd-ac97-codec.o: $(snd-ac97-codec-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ac97-codec-objs) + +snd-ak4531-codec.o: $(snd-ak4531-codec-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ak4531-codec-objs) diff -Nru a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ac97/ac97_codec.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2082 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.2 + * by Intel Corporation (http://developer.intel.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Universal interface for Audio Codec '97"); +MODULE_LICENSE("GPL"); + +static int enable_loopback = 0; + +MODULE_PARM(enable_loopback, "i"); +MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); +MODULE_PARM_SYNTAX(enable_loopback, SNDRV_BOOLEAN_FALSE_DESC); + +#define chip_t ac97_t + +/* + + */ + +static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97); +static void snd_ac97_proc_done(ac97_t * ac97); + +static int patch_wolfson(ac97_t * ac97); + +static int patch_tritech_tr28028(ac97_t * ac97); +static int patch_sigmatel_stac9708(ac97_t * ac97); +static int patch_sigmatel_stac9721(ac97_t * ac97); +static int patch_sigmatel_stac9744(ac97_t * ac97); +static int patch_sigmatel_stac9756(ac97_t * ac97); +static int patch_cirrus_cs4299(ac97_t * ac97); +static int patch_ad1819(ac97_t * ac97); +static int patch_ad1881(ac97_t * ac97); + +typedef struct { + unsigned int id; + unsigned int mask; + char *name; + int (*patch)(ac97_t *ac97); +} ac97_codec_id_t; + +static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = { +{ 0x414b4d00, 0xffffff00, "Asahi Kasei", NULL }, +{ 0x41445300, 0xffffff00, "Analog Devices", NULL }, +{ 0x414c4300, 0xffffff00, "Realtek", NULL }, +{ 0x414c4700, 0xffffff00, "Avance Logic", NULL }, +{ 0x43525900, 0xffffff00, "Cirrus Logic", NULL }, +{ 0x48525300, 0xffffff00, "Intersil", NULL }, +{ 0x49434500, 0xffffff00, "ICEnsemble", NULL }, +{ 0x4e534300, 0xffffff00, "National Semiconductor", NULL }, +{ 0x53494c00, 0xffffff00, "Silicon Laboratory", NULL }, +{ 0x54524100, 0xffffff00, "TriTech", NULL }, +{ 0x54584e00, 0xffffff00, "Texas Instruments", NULL }, +{ 0x57454300, 0xffffff00, "Winbond", NULL }, +{ 0x574d4c00, 0xffffff00, "Wolfson", patch_wolfson }, +{ 0x594d4800, 0xffffff00, "Yamaha", NULL }, +{ 0x83847600, 0xffffff00, "SigmaTel", NULL }, +{ 0x45838300, 0xffffff00, "ESS Technology", NULL }, +{ 0, 0, NULL, NULL } +}; + +static const ac97_codec_id_t snd_ac97_codec_ids[] = { +{ 0x414b4d00, 0xffffffff, "AK4540", NULL }, +{ 0x414b4d01, 0xffffffff, "AK4542", NULL }, +{ 0x414b4d02, 0xffffffff, "AK4543", NULL }, +{ 0x414b4d06, 0xffffffff, "AK4544A", NULL }, +{ 0x414b4d07, 0xffffffff, "AK4545", NULL }, +{ 0x41445303, 0xffffffff, "AD1819", patch_ad1819 }, +{ 0x41445340, 0xffffffff, "AD1881", patch_ad1881 }, +{ 0x41445348, 0xffffffff, "AD1881A", patch_ad1881 }, +{ 0x41445360, 0xffffffff, "AD1885", patch_ad1881 }, +{ 0x41445361, 0xffffffff, "AD1886", patch_ad1881 }, +{ 0x41445362, 0xffffffff, "AD1887", patch_ad1881 }, +{ 0x414c4300, 0xfffffff0, "RL5306", NULL }, +{ 0x414c4310, 0xfffffff0, "RL5382", NULL }, +{ 0x414c4320, 0xfffffff0, "RL5383", NULL }, +{ 0x414c4710, 0xffffffff, "ALC200/200P", NULL }, +{ 0x43525900, 0xfffffff8, "CS4297", NULL }, +{ 0x43525910, 0xfffffff8, "CS4297A", NULL }, +{ 0x42525920, 0xfffffff8, "CS4294/4298", NULL }, +{ 0x42525928, 0xfffffff8, "CS4294", NULL }, +{ 0x43525930, 0xfffffff8, "CS4299", patch_cirrus_cs4299 }, +{ 0x43525948, 0xfffffff8, "CS4201", NULL }, +{ 0x43525958, 0xfffffff8, "CS4205", NULL }, +{ 0x43525960, 0xfffffff8, "CS4291", NULL }, +{ 0x48525300, 0xffffff00, "HMP9701", NULL }, +{ 0x49434501, 0xffffffff, "ICE1230", NULL }, +{ 0x49434511, 0xffffffff, "ICE1232", NULL }, // only guess --jk +{ 0x4e534300, 0xffffffff, "LM4540/43/45/46/48", NULL }, // only guess --jk +{ 0x4e534331, 0xffffffff, "LM4549", NULL }, +{ 0x53494c22, 0xffffffff, "Si3036", NULL }, +{ 0x53494c23, 0xffffffff, "Si3038", NULL }, +{ 0x54524108, 0xffffffff, "TR28028", patch_tritech_tr28028 }, // added by xin jin [07/09/99] +{ 0x54524123, 0xffffffff, "TR28602", NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)] +{ 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL }, +{ 0x57454301, 0xffffffff, "W83971D", NULL }, +{ 0x574d4c00, 0xffffffff, "WM9701A", NULL }, +{ 0x574d4c03, 0xffffffff, "WM9703/9704", NULL }, +{ 0x574d4c04, 0xffffffff, "WM9704 (quad)", NULL }, +{ 0x594d4800, 0xffffffff, "YMF743", NULL }, +{ 0x83847600, 0xffffffff, "STAC9700/83/84", NULL }, +{ 0x83847604, 0xffffffff, "STAC9701/3/4/5", NULL }, +{ 0x83847605, 0xffffffff, "STAC9704", NULL }, +{ 0x83847608, 0xffffffff, "STAC9708/11", patch_sigmatel_stac9708 }, +{ 0x83847609, 0xffffffff, "STAC9721/23", patch_sigmatel_stac9721 }, +{ 0x83847644, 0xffffffff, "STAC9744", patch_sigmatel_stac9744 }, +{ 0x83847656, 0xffffffff, "STAC9756/57", patch_sigmatel_stac9756 }, +{ 0x45838308, 0xffffffff, "ESS1988", NULL }, +{ 0, 0, NULL, NULL } +}; + +#define AC97_ID_AK4540 0x414b4d00 +#define AC97_ID_AK4542 0x414b4d01 +#define AC97_ID_AD1819 0x41445303 +#define AC97_ID_AD1881 0x41445340 +#define AC97_ID_AD1881A 0x41445348 +#define AC97_ID_AD1885 0x41445360 +#define AC97_ID_AD1886 0x41445361 +#define AC97_ID_AD1887 0x41445362 +#define AC97_ID_TR28028 0x54524108 +#define AC97_ID_STAC9700 0x83847600 +#define AC97_ID_STAC9704 0x83847604 +#define AC97_ID_STAC9705 0x83847605 +#define AC97_ID_STAC9708 0x83847608 +#define AC97_ID_STAC9721 0x83847609 +#define AC97_ID_STAC9744 0x83847644 +#define AC97_ID_STAC9756 0x83847656 + +static const char *snd_ac97_stereo_enhancements[] = +{ + /* 0 */ "No 3D Stereo Enhancement", + /* 1 */ "Analog Devices Phat Stereo", + /* 2 */ "Creative Stereo Enhancement", + /* 3 */ "National Semi 3D Stereo Enhancement", + /* 4 */ "YAMAHA Ymersion", + /* 5 */ "BBE 3D Stereo Enhancement", + /* 6 */ "Crystal Semi 3D Stereo Enhancement", + /* 7 */ "Qsound QXpander", + /* 8 */ "Spatializer 3D Stereo Enhancement", + /* 9 */ "SRS 3D Stereo Enhancement", + /* 10 */ "Platform Tech 3D Stereo Enhancement", + /* 11 */ "AKM 3D Audio", + /* 12 */ "Aureal Stereo Enhancement", + /* 13 */ "Aztech 3D Enhancement", + /* 14 */ "Binaura 3D Audio Enhancement", + /* 15 */ "ESS Technology Stereo Enhancement", + /* 16 */ "Harman International VMAx", + /* 17 */ "Nvidea 3D Stereo Enhancement", + /* 18 */ "Philips Incredible Sound", + /* 19 */ "Texas Instruments 3D Stereo Enhancement", + /* 20 */ "VLSI Technology 3D Stereo Enhancement", + /* 21 */ "TriTech 3D Stereo Enhancement", + /* 22 */ "Realtek 3D Stereo Enhancement", + /* 23 */ "Samsung 3D Stereo Enhancement", + /* 24 */ "Wolfson Microelectronics 3D Enhancement", + /* 25 */ "Delta Integration 3D Enhancement", + /* 26 */ "SigmaTel 3D Enhancement", + /* 27 */ "Reserved 27", + /* 28 */ "Rockwell 3D Stereo Enhancement", + /* 29 */ "Reserved 29", + /* 30 */ "Reserved 30", + /* 31 */ "Reserved 31" +}; + +/* + * I/O routines + */ + +static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg) +{ + /* filter some registers for buggy codecs */ + switch (ac97->id) { + case AC97_ID_AK4540: + case AC97_ID_AK4542: + if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c) + return 1; + return 0; + case AC97_ID_AD1819: /* AD1819 */ + case AC97_ID_AD1881: /* AD1881 */ + case AC97_ID_AD1881A: /* AD1881A */ + if (reg >= 0x3a && reg <= 0x6e) /* 0x59 */ + return 0; + return 1; + case AC97_ID_AD1885: /* AD1885 */ + case AC97_ID_AD1886: /* AD1886 */ + case AC97_ID_AD1887: /* AD1887 - !!verify!! --jk */ + if (reg == 0x5a) + return 1; + if (reg >= 0x3c && reg <= 0x6e) /* 0x59 */ + return 0; + return 1; + case AC97_ID_STAC9700: + case AC97_ID_STAC9704: + case AC97_ID_STAC9705: + case AC97_ID_STAC9708: + case AC97_ID_STAC9721: + case AC97_ID_STAC9744: + case AC97_ID_STAC9756: + if (reg <= 0x3a || reg >= 0x5a) + return 1; + return 0; + } + return 1; +} + +void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + if (!snd_ac97_valid_reg(ac97, reg)) + return; + ac97->write(ac97, reg, value); +} + +unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg) +{ + if (!snd_ac97_valid_reg(ac97, reg)) + return 0; + return ac97->read(ac97, reg); +} + +void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + if (!snd_ac97_valid_reg(ac97, reg)) + return; + spin_lock(&ac97->reg_lock); + ac97->write(ac97, reg, ac97->regs[reg] = value); + spin_unlock(&ac97->reg_lock); + set_bit(reg, ac97->reg_accessed); +} + +#ifndef CONFIG_SND_DEBUG +#define snd_ac97_write_cache_test snd_ac97_write_cache +#else +static void snd_ac97_write_cache_test(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + return snd_ac97_write_cache(ac97, reg, value); + if (!snd_ac97_valid_reg(ac97, reg)) + return; + spin_lock(&ac97->reg_lock); + ac97->write(ac97, reg, value); + ac97->regs[reg] = ac97->read(ac97, reg); + if (value != ac97->regs[reg]) + snd_printk("AC97 reg=%02x val=%04x real=%04x\n", reg, value, ac97->regs[reg]); + spin_unlock(&ac97->reg_lock); +} +#endif + +int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + int change; + + if (!snd_ac97_valid_reg(ac97, reg)) + return -EINVAL; + spin_lock(&ac97->reg_lock); + change = ac97->regs[reg] != value; + if (change) { + ac97->write(ac97, reg, value); + ac97->regs[reg] = value; + } + spin_unlock(&ac97->reg_lock); + return change; +} + +int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + if (!snd_ac97_valid_reg(ac97, reg)) + return -EINVAL; + spin_lock(&ac97->reg_lock); + old = ac97->regs[reg]; + new = (old & ~mask) | value; + change = old != new; + if (change) { + ac97->write(ac97, reg, new); + ac97->regs[reg] = new; + } + spin_unlock(&ac97->reg_lock); + return change; +} + +int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + down(&ac97->spec.ad18xx.mutex); + spin_lock(&ac97->reg_lock); + old = ac97->spec.ad18xx.pcmreg[codec]; + new = (old & ~mask) | value; + change = old != new; + if (change) { + /* select single codec */ + ac97->write(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); + /* update PCM bits */ + ac97->write(ac97, AC97_PCM, new); + /* select all codecs */ + ac97->write(ac97, AC97_AD_SERIAL_CFG, 0x7000); + ac97->spec.ad18xx.pcmreg[codec] = new; + } + spin_unlock(&ac97->reg_lock); + up(&ac97->spec.ad18xx.mutex); + return change; +} + +/* + * + */ + +static int snd_ac97_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Mic", "CD", "Video", "Aux", "Line", + "Mix", "Mix Mono", "Phone" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = ac97->regs[AC97_REC_SEL]; + ucontrol->value.enumerated.item[0] = (val >> 8) & 7; + ucontrol->value.enumerated.item[1] = (val >> 0) & 7; + return 0; +} + +static int snd_ac97_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if (ucontrol->value.enumerated.item[0] > 7 || + ucontrol->value.enumerated.item[1] > 7) + return -EINVAL; + val = (ucontrol->value.enumerated.item[0] << 8) | + (ucontrol->value.enumerated.item[1] << 0); + return snd_ac97_update(ac97, AC97_REC_SEL, val); +} + +#define AC97_ENUM_DOUBLE(xname, reg, shift, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_info_enum_double, \ + get: snd_ac97_get_enum_double, put: snd_ac97_put_enum_double, \ + private_value: reg | (shift << 8) | (invert << 24) } + +static int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts1[2] = { "pre 3D", "post 3D" }; + static char *texts2[2] = { "Mix", "Mic" }; + static char *texts3[2] = { "Mic1", "Mic2" }; + char **texts = NULL; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + + switch (reg) { + case AC97_GENERAL_PURPOSE: + switch (shift) { + case 15: texts = texts1; break; + case 9: texts = texts2; break; + case 8: texts = texts3; break; + } + } + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + val = (ac97->regs[reg] >> shift) & 1; + if (invert) + val ^= 1; + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; + val = !!ucontrol->value.enumerated.item[0]; + if (invert) + val = !val; + return snd_ac97_update_bits(ac97, reg, 1 << shift, val << shift); +} + +#define AC97_SINGLE(xname, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_info_single, \ + get: snd_ac97_get_single, put: snd_ac97_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_ac97_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ac97_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + ucontrol->value.integer.value[0] = (ac97->regs[reg] >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ac97_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + return snd_ac97_update_bits(ac97, reg, mask << shift, val << shift); +} + +#define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: (xname), info: snd_ac97_info_double, \ + get: snd_ac97_get_double, put: snd_ac97_put_double, \ + private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } + +static int snd_ac97_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ac97_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock(&ac97->reg_lock); + ucontrol->value.integer.value[0] = (ac97->regs[reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (ac97->regs[reg] >> shift_right) & mask; + spin_unlock(&ac97->reg_lock); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_ac97_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + return snd_ac97_update_bits(ac97, reg, + (mask << shift_left) | (mask << shift_right), + (val1 << shift_left) | (val2 << shift_right)); +} + +static const snd_kcontrol_new_t snd_ac97_controls_master[2] = { +AC97_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1), +AC97_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_headphone[2] = { +AC97_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1), +AC97_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_master_mono[2] = { +AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), +AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_tone[2] = { +AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1), +AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_pc_beep[2] = { +AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1), +AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_phone[2] = { +AC97_SINGLE("Phone Playback Switch", AC97_PHONE, 15, 1, 1), +AC97_SINGLE("Phone Playback Volume", AC97_PHONE, 0, 15, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_mic[3] = { +AC97_SINGLE("Mic Playback Switch", AC97_MIC, 15, 1, 1), +AC97_SINGLE("Mic Playback Volume", AC97_MIC, 0, 15, 1), +AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_line[2] = { +AC97_SINGLE("Line Playback Switch", AC97_LINE, 15, 1, 1), +AC97_DOUBLE("Line Playback Volume", AC97_LINE, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_cd[2] = { +AC97_SINGLE("CD Playback Switch", AC97_CD, 15, 1, 1), +AC97_DOUBLE("CD Playback Volume", AC97_CD, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_video[2] = { +AC97_SINGLE("Video Playback Switch", AC97_VIDEO, 15, 1, 1), +AC97_DOUBLE("Video Playback Volume", AC97_VIDEO, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_aux[2] = { +AC97_SINGLE("Aux Playback Switch", AC97_AUX, 15, 1, 1), +AC97_DOUBLE("Aux Playback Volume", AC97_AUX, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_pcm[2] = { +AC97_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1), +AC97_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_capture[3] = { +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_ac97_info_mux, + get: snd_ac97_get_mux, + put: snd_ac97_put_mux, +}, +AC97_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1), +AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_mic_capture[2] = { +AC97_SINGLE("Mic Capture Switch", AC97_REC_GAIN_MIC, 15, 1, 1), +AC97_SINGLE("Mic Capture Volume", AC97_REC_GAIN_MIC, 0, 15, 0) +}; + +typedef enum { + AC97_GENERAL_PCM_OUT = 0, + AC97_GENERAL_STEREO_ENHANCEMENT, + AC97_GENERAL_3D, + AC97_GENERAL_LOUDNESS, + AC97_GENERAL_MONO, + AC97_GENERAL_MIC, + AC97_GENERAL_LOOPBACK +} ac97_general_index_t; + +static const snd_kcontrol_new_t snd_ac97_controls_general[7] = { +AC97_ENUM_DOUBLE("PCM Out Path & Mute", AC97_GENERAL_PURPOSE, 15, 0), +AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0), +AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0), +AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0), +AC97_ENUM_DOUBLE("Mono Output Select", AC97_GENERAL_PURPOSE, 9, 0), +AC97_ENUM_DOUBLE("Mic Select", AC97_GENERAL_PURPOSE, 8, 0), +AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_3d[2] = { +AC97_SINGLE("3D Control - Center", AC97_3D_CONTROL, 8, 15, 0), +AC97_SINGLE("3D Control - Depth", AC97_3D_CONTROL, 0, 15, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_center[2] = { +AC97_SINGLE("Center Playback Switch", AC97_CENTER_LFE_MASTER, 7, 1, 1), +AC97_SINGLE("Center Playback Volume", AC97_CENTER_LFE_MASTER, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_lfe[2] = { +AC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1), +AC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_surround[2] = { +AC97_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1), +AC97_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1), +}; + +static const snd_kcontrol_new_t snd_ac97_sigmatel_controls[] = { +AC97_SINGLE("Sigmatel DAC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 1, 1, 0), +AC97_SINGLE("Sigmatel ADC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 0, 1, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_control_eapd = +AC97_SINGLE("External Amplifier Power Down", AC97_POWERDOWN, 15, 1, 0); + +static int snd_ac97_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ac97_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | + IEC958_AES0_NONAUDIO | + IEC958_AES0_CON_EMPHASIS_5015 | + IEC958_AES0_CON_NOT_COPYRIGHT; + ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY | + IEC958_AES1_CON_ORIGINAL; + ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS; + return 0; +} + +static int snd_ac97_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + /* FIXME: AC'97 spec doesn't say which bits are used for what */ + ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | + IEC958_AES0_NONAUDIO | + IEC958_AES0_PRO_FS | + IEC958_AES0_PRO_EMPHASIS_5015; + return 0; +} + +static int snd_ac97_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + + spin_lock(&ac97->reg_lock); + ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff; + ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff; + spin_unlock(&ac97->reg_lock); + return 0; +} + +static int snd_ac97_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned int new = 0; + unsigned short val = 0; + int change = 0; + + spin_lock(&ac97->reg_lock); + new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO); + if (ucontrol->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) { + new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015); + switch (new & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break; + case IEC958_AES0_PRO_FS_32000: val |= 2<<12; break; + case IEC958_AES0_PRO_FS_48000: val |= 1<<12; break; + default: val |= 1<<12; break; + } + if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015) + val |= 1<<3; + } else { + new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT); + new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8); + new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 8); + if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015) + val |= 1<<3; + if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT)) + val |= 1<<2; + val |= ((new >> 8) & 0xff) << 4; // category + original + switch ((new >> 24) & 0xff) { + case IEC958_AES3_CON_FS_44100: val |= 0<<12; break; + case IEC958_AES3_CON_FS_48000: val |= 2<<12; break; + case IEC958_AES3_CON_FS_32000: val |= 3<<12; break; + default: val |= 1<<12; break; + } + } + change = snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val); + change |= ac97->spdif_status != new; + ac97->spdif_status = new; + spin_unlock(&ac97->reg_lock); + return change; +} + +static const snd_kcontrol_new_t snd_ac97_controls_spdif[5] = { + { + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_ac97_spdif_mask_info, + get: snd_ac97_spdif_cmask_get, + }, + { + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + info: snd_ac97_spdif_mask_info, + get: snd_ac97_spdif_pmask_get, + }, + { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_ac97_spdif_mask_info, + get: snd_ac97_spdif_default_get, + put: snd_ac97_spdif_default_put, + }, + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),AC97_EXTENDED_STATUS, 2, 1, 0), + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA",AC97_EXTENDED_STATUS, 4, 3, 0) +}; + +#define AD18XX_PCM_BITS(xname, codec, shift, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_ad18xx_pcm_info_bits, \ + get: snd_ac97_ad18xx_pcm_get_bits, put: snd_ac97_ad18xx_pcm_put_bits, \ + private_value: codec | (shift << 8) | (mask << 16) } + +static int snd_ac97_ad18xx_pcm_info_bits(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ac97_ad18xx_pcm_get_bits(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + + ucontrol->value.integer.value[0] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> shift) & mask); + return 0; +} + +static int snd_ac97_ad18xx_pcm_put_bits(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + unsigned short val; + + val = mask - (ucontrol->value.integer.value[0] & mask); + return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, mask << shift, val << shift); +} + +#define AD18XX_PCM_VOLUME(xname, codec) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_ad18xx_pcm_info_volume, \ + get: snd_ac97_ad18xx_pcm_get_volume, put: snd_ac97_ad18xx_pcm_put_volume, \ + private_value: codec } + +static int snd_ac97_ad18xx_pcm_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 31; + return 0; +} + +static int snd_ac97_ad18xx_pcm_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + + spin_lock(&ac97->reg_lock); + ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31); + ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31); + spin_unlock(&ac97->reg_lock); + return 0; +} + +static int snd_ac97_ad18xx_pcm_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + unsigned short val1, val2; + + val1 = 31 - (ucontrol->value.integer.value[0] & 31); + val2 = 31 - (ucontrol->value.integer.value[1] & 31); + return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, 0x1f1f, (val1 << 8) | val2); +} + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_pcm[2] = { +AD18XX_PCM_BITS("PCM Playback Switch", 0, 15, 1), +AD18XX_PCM_VOLUME("PCM Playback Volume", 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_surround[2] = { +AD18XX_PCM_BITS("Surround Playback Switch", 1, 15, 1), +AD18XX_PCM_VOLUME("Surround Playback Volume", 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_center[2] = { +AD18XX_PCM_BITS("Center Playback Switch", 2, 15, 1), +AD18XX_PCM_BITS("Center Playback Volume", 2, 8, 31) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_lfe[1] = { +AD18XX_PCM_BITS("LFE Playback Volume", 2, 0, 31) +}; + +/* + * + */ + +static int snd_ac97_free(ac97_t *ac97) +{ + if (ac97) { + snd_ac97_proc_done(ac97); + if (ac97->private_free) + ac97->private_free(ac97); + snd_magic_kfree(ac97); + } + return 0; +} + +static int snd_ac97_dev_free(snd_device_t *device) +{ + ac97_t *ac97 = snd_magic_cast(ac97_t, device->device_data, return -ENXIO); + return snd_ac97_free(ac97); +} + +static int snd_ac97_try_volume_mix(ac97_t * ac97, int reg) +{ + unsigned short val, mask = 0x8000; + + switch (reg) { + case AC97_MASTER_TONE: + return ac97->caps & 0x04 ? 1 : 0; + case AC97_HEADPHONE: + return ac97->caps & 0x10 ? 1 : 0; + case AC97_REC_GAIN_MIC: + return ac97->caps & 0x01 ? 1 : 0; + case AC97_3D_CONTROL: + if (ac97->caps & 0x7c00) { + val = snd_ac97_read(ac97, reg); + /* if nonzero - fixed and we can't set it */ + return val == 0; + } + return 0; + case AC97_CENTER_LFE_MASTER: /* center */ + if ((ac97->ext_id & 0x40) == 0) + return 0; + break; + case AC97_CENTER_LFE_MASTER+1: /* lfe */ + if ((ac97->ext_id & 0x100) == 0) + return 0; + reg = AC97_CENTER_LFE_MASTER; + mask = 0x0080; + break; + case AC97_SURROUND_MASTER: + if ((ac97->ext_id & 0x80) == 0) + return 0; + break; + } + val = snd_ac97_read(ac97, reg); + if (!(val & mask)) { + /* nothing seems to be here - mute flag is not set */ + /* try another test */ + snd_ac97_write_cache_test(ac97, reg, val | mask); + val = snd_ac97_read(ac97, reg); + if (!(val & mask)) + return 0; /* nothing here */ + } + return 1; /* success, useable */ +} + +static int snd_ac97_try_bit(ac97_t * ac97, int reg, int bit) +{ + unsigned short mask, val, orig, res; + + mask = 1 << bit; + orig = snd_ac97_read(ac97, reg); + val = orig ^ mask; + snd_ac97_write(ac97, reg, val); + res = snd_ac97_read(ac97, reg); + snd_ac97_write_cache(ac97, reg, orig); + return res == val; +} + +static void snd_ac97_change_volume_params1(ac97_t * ac97, int reg, unsigned char *max) +{ + unsigned short val, val1; + + *max = 63; + val = 0x8000 | 0x0020; + snd_ac97_write(ac97, reg, val); + val1 = snd_ac97_read(ac97, reg); + if (val != val1) { + *max = 31; + } + /* reset volume to zero */ + snd_ac97_write_cache(ac97, reg, 0x8000); +} + +static void snd_ac97_change_volume_params2(ac97_t * ac97, int reg, int shift, unsigned char *max) +{ + unsigned short val, val1; + + *max = 63; + val = 0x8080 | (0x20 << shift); + snd_ac97_write(ac97, reg, val); + val1 = snd_ac97_read(ac97, reg); + if (val != val1) { + *max = 31; + } + /* reset volume to zero */ + snd_ac97_write_cache(ac97, reg, 0x8080); +} + +static inline int printable(unsigned int x) +{ + x &= 0xff; + if (x < ' ' || x >= 0x71) { + if (x <= 0x89) + return x - 0x71 + 'A'; + return '?'; + } + return x; +} + +static snd_kcontrol_t *snd_ac97_cnew(const snd_kcontrol_new_t *_template, ac97_t * ac97) +{ + snd_kcontrol_new_t template; + memcpy(&template, _template, sizeof(template)); + snd_runtime_check(!template.index, return NULL); + template.index = ac97->num; + return snd_ctl_new1(&template, ac97); +} + +static int snd_ac97_mixer_build(snd_card_t * card, ac97_t * ac97) +{ + snd_kcontrol_t *kctl; + int err, idx; + unsigned char max; + + /* build master controls */ + /* AD claims to remove this control from AD1887, although spec v2.2 don't allow this */ + if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_master[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_master[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params1(ac97, AC97_MASTER, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_MASTER, 0x8000 | max | (max << 8)); + } + + ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080; + + /* build center controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max); + } + + /* build LFE controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8); + } + + /* build surround controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_surround[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_surround[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params2(ac97, AC97_SURROUND_MASTER, 0, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x8080 | max | (max << 8)); + } + + /* build headphone controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_headphone[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_headphone[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params1(ac97, AC97_HEADPHONE, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_HEADPHONE, 0x8000 | max | (max << 8)); + } + + /* build master mono controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_master_mono[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_master_mono[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params1(ac97, AC97_MASTER_MONO, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_MASTER_MONO, 0x8000 | max); + } + + /* build master tone controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_MASTER_TONE, 0x0f0f); + } + + /* build PC Speaker controls */ + if ((ac97->flags & AC97_HAS_PC_BEEP) || + snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e); + } + + /* build Phone controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_phone[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_PHONE, 0x801f); + } + + /* build MIC controls */ + for (idx = 0; idx < 3; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_MIC, 0x801f); + + /* build Line controls */ + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_line[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_LINE, 0x9f1f); + + /* build CD controls */ + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_cd[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_CD, 0x9f1f); + + /* build Video controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_video[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_VIDEO, 0x9f1f); + } + + /* build Aux controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_aux[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_AUX, 0x9f1f); + } + + /* build PCM controls */ + if (ac97->flags & AC97_AD_MULTI) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0) + return err; + ac97->spec.ad18xx.pcmreg[0] = 0x9f1f; + if (ac97->scaps & AC97_SCAP_SURROUND_DAC) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0) + return err; + ac97->spec.ad18xx.pcmreg[1] = 0x9f1f; + } + if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[0], ac97))) < 0) + return err; + ac97->spec.ad18xx.pcmreg[2] = 0x9f1f; + } + } else { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pcm[idx], ac97))) < 0) + return err; + } + snd_ac97_write_cache(ac97, AC97_PCM, 0x9f1f); + + /* build Capture controls */ + for (idx = 0; idx < 3; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_capture[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000); + snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000); + + /* build MIC Capture controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000); + } + + /* build PCM out path & mute control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97))) < 0) + return err; + } + + /* build Simulated Stereo Enhancement control */ + if (ac97->caps & 0x0008) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0) + return err; + } + + /* build 3D Stereo Enhancement control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97))) < 0) + return err; + } + + /* build Loudness control */ + if (ac97->caps & 0x0020) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0) + return err; + } + + /* build Mono output select control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97))) < 0) + return err; + } + + /* build Mic select control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97))) < 0) + return err; + } + + /* build ADC/DAC loopback control */ + if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0) + return err; + } + + snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0x0000); + + /* build 3D controls */ + switch (ac97->id) { + case AC97_ID_STAC9708: + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + kctl->private_value = AC97_3D_CONTROL | (3 << 16); + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); + kctl->private_value = AC97_3D_CONTROL | (2 << 8) | (3 << 16); + snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); + break; + case AC97_ID_STAC9700: + case AC97_ID_STAC9721: + case AC97_ID_STAC9744: + case AC97_ID_STAC9756: + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + kctl->private_value = AC97_3D_CONTROL | (3 << 16); + snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); + break; + default: + if (snd_ac97_try_volume_mix(ac97, AC97_3D_CONTROL)) { + unsigned short val; + val = 0x0707; + snd_ac97_write(ac97, AC97_3D_CONTROL, val); + val = snd_ac97_read(ac97, AC97_3D_CONTROL); + val = val == 0x0606; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + if (val) + kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16); + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0) + return err; + if (val) + kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16); + snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); + } + } + + /* build S/PDIF controls */ + if (ac97->ext_id & 0x0004) { + for (idx = 0; idx < 5; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0) + return err; + /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original */ + snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20); + ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF; + } + + /* build Sigmatel specific controls */ + switch (ac97->id) { + case AC97_ID_STAC9700: + case AC97_ID_STAC9708: + case AC97_ID_STAC9721: + case AC97_ID_STAC9744: + case AC97_ID_STAC9756: + snd_ac97_write_cache_test(ac97, AC97_SIGMATEL_ANALOG, snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) & ~0x0003); + if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1)) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_sigmatel_controls[0], ac97))) < 0) + return err; + if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0)) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_sigmatel_controls[1], ac97))) < 0) + return err; + default: + /* nothing */ + break; + } + + if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_eapd, ac97))) < 0) + return err; + } + + return 0; +} + +static int snd_ac97_test_rate(ac97_t *ac97, int reg, int rate) +{ + unsigned short val; + unsigned int tmp; + + tmp = ((unsigned int)rate * ac97->clock) / 48000; + snd_ac97_write_cache_test(ac97, reg, tmp & 0xffff); + val = snd_ac97_read(ac97, reg); + return val == (tmp & 0xffff); +} + +static void snd_ac97_determine_rates(ac97_t *ac97, int reg, unsigned int *r_result) +{ + unsigned int result = 0; + + /* test a non-standard rate */ + if (snd_ac97_test_rate(ac97, reg, 11000)) + result |= SNDRV_PCM_RATE_CONTINUOUS; + /* let's try to obtain standard rates */ + if (snd_ac97_test_rate(ac97, reg, 8000)) + result |= SNDRV_PCM_RATE_8000; + if (snd_ac97_test_rate(ac97, reg, 11025)) + result |= SNDRV_PCM_RATE_11025; + if (snd_ac97_test_rate(ac97, reg, 16000)) + result |= SNDRV_PCM_RATE_16000; + if (snd_ac97_test_rate(ac97, reg, 22050)) + result |= SNDRV_PCM_RATE_22050; + if (snd_ac97_test_rate(ac97, reg, 32000)) + result |= SNDRV_PCM_RATE_32000; + if (snd_ac97_test_rate(ac97, reg, 44100)) + result |= SNDRV_PCM_RATE_44100; + if (snd_ac97_test_rate(ac97, reg, 48000)) + result |= SNDRV_PCM_RATE_48000; + *r_result = result; +} + +static void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name) +{ + const ac97_codec_id_t *pid; + + sprintf(name, "0x%x %c%c%c", id, + printable(id >> 24), + printable(id >> 16), + printable(id >> 8)); + for (pid = snd_ac97_codec_id_vendors; pid->id; pid++) + if (pid->id == (id & pid->mask)) { + strcpy(name, pid->name); + if (ac97 && pid->patch) + pid->patch(ac97); + goto __vendor_ok; + } + return; + + __vendor_ok: + for (pid = snd_ac97_codec_ids; pid->id; pid++) + if (pid->id == (id & pid->mask)) { + strcat(name, " "); + strcat(name, pid->name); + if (pid->mask != 0xffffffff) + sprintf(name + strlen(name), " rev %d", id & ~pid->mask); + if (ac97 && pid->patch) + pid->patch(ac97); + return; + } + sprintf(name + strlen(name), " (%x)", id & 0xff); +} + +int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97) +{ + int err; + ac97_t *ac97; + char name[64]; + signed long end_time; + static snd_device_ops_t ops = { + dev_free: snd_ac97_dev_free, + }; + + snd_assert(rac97 != NULL, return -EINVAL); + *rac97 = NULL; + snd_assert(card != NULL && _ac97 != NULL, return -EINVAL); + ac97 = snd_magic_kcalloc(ac97_t, 0, GFP_KERNEL); + if (ac97 == NULL) + return -ENOMEM; + *ac97 = *_ac97; + ac97->card = card; + spin_lock_init(&ac97->reg_lock); + snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */ + udelay(50); + + /* it's necessary to wait awhile until registers are accessible after RESET */ + /* because the PCM or MASTER volume registers can be modified, */ + /* the REC_GAIN register is used for tests */ + end_time = jiffies + (HZ / 2); + do { + /* use preliminary reads to settle the communication */ + snd_ac97_read(ac97, AC97_RESET); + snd_ac97_read(ac97, AC97_VENDOR_ID1); + snd_ac97_read(ac97, AC97_VENDOR_ID2); + /* test if we can write to the PCM volume register */ + snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); + if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05) + goto __access_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/100); + } while (end_time - (signed long)jiffies >= 0); + snd_printd("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err); + snd_ac97_free(ac97); + return -ENXIO; + + __access_ok: + ac97->caps = snd_ac97_read(ac97, AC97_RESET); + ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; + ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); + ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ac97->ext_id == 0xffff) /* invalid combination */ + ac97->ext_id = 0; + if (ac97->id == 0x00000000 || ac97->id == 0xffffffff) { + snd_printk("AC'97 %d:%d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->addr, ac97->id); + snd_ac97_free(ac97); + return -EIO; + } + /* FIXME: add powerdown control */ + /* nothing should be in powerdown mode */ + snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0); + snd_ac97_write_cache_test(ac97, AC97_RESET, 0); /* reset to defaults */ + udelay(100); + /* nothing should be in powerdown mode */ + snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0); + snd_ac97_write_cache_test(ac97, AC97_GENERAL_PURPOSE, 0); + end_time = jiffies + (HZ / 10); + do { + if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f) + goto __ready_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("AC'97 %d:%d analog subsections not ready\n", ac97->num, ac97->addr); + __ready_ok: + if (ac97->clock == 0) + ac97->clock = 48000; /* standard value */ + if (ac97->ext_id & 0x0189) /* L/R, MIC, SDAC, LDAC VRA support */ + snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189); + if (ac97->ext_id & 0x0001) { /* VRA support */ + snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates_front_dac); + snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates_adc); + } else { + ac97->rates_front_dac = SNDRV_PCM_RATE_48000; + ac97->rates_adc = SNDRV_PCM_RATE_48000; + } + if (ac97->ext_id & 0x0008) { /* MIC VRA support */ + snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates_mic_adc); + } else { + ac97->rates_mic_adc = SNDRV_PCM_RATE_48000; + } + if (ac97->ext_id & 0x0080) { /* SDAC support */ + snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates_surr_dac); + ac97->scaps |= AC97_SCAP_SURROUND_DAC; + } + if (ac97->ext_id & 0x0100) { /* LDAC support */ + snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates_lfe_dac); + ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; + } + if (ac97->init) + ac97->init(ac97); + snd_ac97_get_name(ac97, ac97->id, name); + snd_ac97_get_name(NULL, ac97->id, name); // ac97->id might be changed in the special setup code + if (card->mixername[0] == '\0') { + strcpy(card->mixername, name); + } else { + if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { + strcat(card->mixername, ","); + strcat(card->mixername, name); + } + } + if ((err = snd_component_add(card, "AC97")) < 0) { + snd_ac97_free(ac97); + return err; + } + if (snd_ac97_mixer_build(card, ac97) < 0) { + snd_ac97_free(ac97); + return -ENOMEM; + } + snd_ac97_proc_init(card, ac97); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ac97, &ops)) < 0) { + snd_ac97_free(ac97); + return err; + } + *rac97 = ac97; + return 0; +} + +/* + + */ + +static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx) +{ + char name[64]; + unsigned int id; + unsigned short val, tmp, ext; + static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " SPDIF=6/9", " SPDIF=res" }; + static const char *spdif_rates[4] = { " Rate=44.1kHz", " Rate=res", " Rate=48kHz", " Rate=32kHz" }; + + id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; + id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); + snd_ac97_get_name(NULL, id, name); + snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name); + val = snd_ac97_read(ac97, AC97_RESET); + snd_iprintf(buffer, "Capabilities :%s%s%s%s%s%s\n", + val & 0x0001 ? " -dedicated MIC PCM IN channel-" : "", + val & 0x0002 ? " -reserved1-" : "", + val & 0x0004 ? " -bass & treble-" : "", + val & 0x0008 ? " -simulated stereo-" : "", + val & 0x0010 ? " -headphone out-" : "", + val & 0x0020 ? " -loudness-" : ""); + tmp = ac97->caps & 0x00c0; + snd_iprintf(buffer, "DAC resolution : %s%s%s%s\n", + tmp == 0x0000 ? "16-bit" : "", + tmp == 0x0040 ? "18-bit" : "", + tmp == 0x0080 ? "20-bit" : "", + tmp == 0x00c0 ? "???" : ""); + tmp = ac97->caps & 0x0300; + snd_iprintf(buffer, "ADC resolution : %s%s%s%s\n", + tmp == 0x0000 ? "16-bit" : "", + tmp == 0x0100 ? "18-bit" : "", + tmp == 0x0200 ? "20-bit" : "", + tmp == 0x0300 ? "???" : ""); + snd_iprintf(buffer, "3D enhancement : %s\n", + snd_ac97_stereo_enhancements[(val >> 10) & 0x1f]); + snd_iprintf(buffer, "\nCurrent setup\n"); + val = snd_ac97_read(ac97, AC97_MIC); + snd_iprintf(buffer, "Mic gain : %s [%s]\n", val & 0x0040 ? "+20dB" : "+0dB", ac97->regs[AC97_MIC] & 0x0040 ? "+20dB" : "+0dB"); + val = snd_ac97_read(ac97, AC97_GENERAL_PURPOSE); + snd_iprintf(buffer, "POP path : %s 3D\n" + "Sim. stereo : %s\n" + "3D enhancement : %s\n" + "Loudness : %s\n" + "Mono output : %s\n" + "Mic select : %s\n" + "ADC/DAC loopback : %s\n", + val & 0x8000 ? "post" : "pre", + val & 0x4000 ? "on" : "off", + val & 0x2000 ? "on" : "off", + val & 0x1000 ? "on" : "off", + val & 0x0200 ? "Mic" : "MIX", + val & 0x0100 ? "Mic2" : "Mic1", + val & 0x0080 ? "on" : "off"); + ext = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ext == 0) + return; + snd_iprintf(buffer, "Extended ID : codec=%i rev=%i%s%s%s%s DSA=%i%s%s%s%s\n", + (ext >> 14) & 3, + (ext >> 10) & 3, + ext & 0x0200 ? " AMAP" : "", + ext & 0x0100 ? " LDAC" : "", + ext & 0x0080 ? " SDAC" : "", + ext & 0x0040 ? " CDAC" : "", + (ext >> 4) & 3, + ext & 0x0008 ? " VRM" : "", + ext & 0x0004 ? " SPDIF" : "", + ext & 0x0002 ? " DRA" : "", + ext & 0x0001 ? " VRA" : ""); + val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS); + snd_iprintf(buffer, "Extended status :%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + val & 0x4000 ? " PRL" : "", + val & 0x2000 ? " PRK" : "", + val & 0x1000 ? " PRJ" : "", + val & 0x0800 ? " PRI" : "", + val & 0x0400 ? " SPCV" : "", + val & 0x0200 ? " MADC" : "", + val & 0x0100 ? " LDAC" : "", + val & 0x0080 ? " SDAC" : "", + val & 0x0040 ? " CDAC" : "", + ext & 0x0004 ? spdif_slots[(val & 0x0030) >> 4] : "", + val & 0x0008 ? " VRM" : "", + val & 0x0004 ? " SPDIF" : "", + val & 0x0002 ? " DRA" : "", + val & 0x0001 ? " VRA" : ""); + if (ext & 1) { /* VRA */ + val = snd_ac97_read(ac97, AC97_PCM_FRONT_DAC_RATE); + snd_iprintf(buffer, "PCM front DAC : %iHz\n", val); + if (ext & 0x0080) { + val = snd_ac97_read(ac97, AC97_PCM_SURR_DAC_RATE); + snd_iprintf(buffer, "PCM Surr DAC : %iHz\n", val); + } + if (ext & 0x0100) { + val = snd_ac97_read(ac97, AC97_PCM_LFE_DAC_RATE); + snd_iprintf(buffer, "PCM LFE DAC : %iHz\n", val); + } + val = snd_ac97_read(ac97, AC97_PCM_LR_ADC_RATE); + snd_iprintf(buffer, "PCM ADC : %iHz\n", val); + } + if (ext & 0x0008) { + val = snd_ac97_read(ac97, AC97_PCM_MIC_ADC_RATE); + snd_iprintf(buffer, "PCM MIC ADC : %iHz\n", val); + } + if (ext & 0x0004) { + val = snd_ac97_read(ac97, AC97_SPDIF); + snd_iprintf(buffer, "SPDIF Control :%s%s%s%s Category=0x%x Generation=%i%s%s%s\n", + val & 0x0001 ? " PRO" : " Consumer", + val & 0x0002 ? " Non-audio" : " PCM", + val & 0x0004 ? " Copyright" : "", + val & 0x0008 ? " Preemph50/15" : "", + (val & 0x07f0) >> 4, + (val & 0x0800) >> 11, + spdif_rates[(val & 0x3000) >> 12], + val & 0x4000 ? " DRS" : "", + val & 0x8000 ? " Validity" : ""); + } +} + +static void snd_ac97_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return); + + if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86 + int idx; + down(&ac97->spec.ad18xx.mutex); + for (idx = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.id[idx]) { + /* select single codec */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]); + snd_ac97_proc_read_main(ac97, buffer, idx); + snd_iprintf(buffer, "\n\n"); + } + /* select all codecs */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); + up(&ac97->spec.ad18xx.mutex); + + snd_iprintf(buffer, "\nAD18XX configuration\n"); + snd_iprintf(buffer, "Unchained : 0x%04x,0x%04x,0x%04x\n", + ac97->spec.ad18xx.unchained[0], + ac97->spec.ad18xx.unchained[1], + ac97->spec.ad18xx.unchained[2]); + snd_iprintf(buffer, "Chained : 0x%04x,0x%04x,0x%04x\n", + ac97->spec.ad18xx.chained[0], + ac97->spec.ad18xx.chained[1], + ac97->spec.ad18xx.chained[2]); + } else { + snd_ac97_proc_read_main(ac97, buffer, 0); + } +} + +static void snd_ac97_proc_regs_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx) +{ + int reg, val; + + for (reg = 0; reg < 0x80; reg += 2) { + val = snd_ac97_read(ac97, reg); + snd_iprintf(buffer, "%i:%02x = %04x\n", subidx, reg, val); + } +} + +static void snd_ac97_proc_regs_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return); + + if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86 + int idx; + down(&ac97->spec.ad18xx.mutex); + for (idx = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.id[idx]) { + /* select single codec */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]); + snd_ac97_proc_regs_read_main(ac97, buffer, idx); + } + /* select all codecs */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); + up(&ac97->spec.ad18xx.mutex); + } else { + snd_ac97_proc_regs_read_main(ac97, buffer, 0); + } +} + +static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97) +{ + snd_info_entry_t *entry; + char name[12]; + + sprintf(name, "ac97#%d", ac97->addr); + if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = ac97; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 512; + entry->c.text.read = snd_ac97_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ac97->proc_entry = entry; + sprintf(name, "ac97#%dregs", ac97->addr); + if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = ac97; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 1024; + entry->c.text.read = snd_ac97_proc_regs_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ac97->proc_regs_entry = entry; +} + +static void snd_ac97_proc_done(ac97_t * ac97) +{ + if (ac97->proc_regs_entry) { + snd_info_unregister(ac97->proc_regs_entry); + ac97->proc_regs_entry = NULL; + } + if (ac97->proc_entry) { + snd_info_unregister(ac97->proc_entry); + ac97->proc_entry = NULL; + } +} + +/* + * Chip specific initialization + */ + +static int patch_wolfson(ac97_t * ac97) +{ + // for all wolfson codecs (is this correct? --jk) + snd_ac97_write_cache(ac97, 0x72, 0x0808); + snd_ac97_write_cache(ac97, 0x74, 0x0808); + + // patch for DVD noise + snd_ac97_write_cache(ac97, 0x5a, 0x0200); + + // init vol + snd_ac97_write_cache(ac97, 0x70, 0x0808); + + snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000); + return 0; +} + +static int patch_tritech_tr28028(ac97_t * ac97) +{ + snd_ac97_write_cache(ac97, 0x26, 0x0300); + snd_ac97_write_cache(ac97, 0x26, 0x0000); + snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000); + snd_ac97_write_cache(ac97, AC97_SPDIF, 0x0000); + return 0; +} + +static int patch_sigmatel_stac9708(ac97_t * ac97) +{ + unsigned int codec72, codec6c; + + codec72 = snd_ac97_read(ac97, AC97_SIGMATEL_BIAS2) & 0x8000; + codec6c = snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG); + + if ((codec72==0) && (codec6c==0)) { + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1000); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0007); + } else if ((codec72==0x8000) && (codec6c==0)) { + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1001); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_DAC2INVERT, 0x0008); + } else if ((codec72==0x8000) && (codec6c==0x0080)) { + /* nothing */ + } + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +static int patch_sigmatel_stac9721(ac97_t * ac97) +{ + if (snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) == 0) { + // patch for SigmaTel + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x4000); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); + } + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +static int patch_sigmatel_stac9744(ac97_t * ac97) +{ + // patch for SigmaTel + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000); /* is this correct? --jk */ + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +static int patch_sigmatel_stac9756(ac97_t * ac97) +{ + // patch for SigmaTel + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000); /* is this correct? --jk */ + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +static int patch_cirrus_cs4299(ac97_t * ac97) +{ + ac97->flags |= AC97_HAS_PC_BEEP; /* force the detection of PC Beep */ + return 0; +} + +static int patch_ad1819(ac97_t * ac97) +{ + // patch for Analog Devices + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); /* select all codecs */ + return 0; +} + +static unsigned short patch_ad1881_unchained(ac97_t * ac97, int idx, unsigned short mask) +{ + unsigned short val; + + // test for unchained codec + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, mask); + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000); /* ID0C, ID1C, SDIE = off */ + val = snd_ac97_read(ac97, AC97_VENDOR_ID2); + if ((val & 0xff40) != 0x5340) + return 0; + ac97->spec.ad18xx.unchained[idx] = mask; + ac97->spec.ad18xx.id[idx] = val; + return mask; +} + +static int patch_ad1881_chained1(ac97_t * ac97, int idx, unsigned short codec_bits) +{ + static int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 }; + unsigned short val; + + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, cfg_bits[idx]); + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0004); // SDIE + val = snd_ac97_read(ac97, AC97_VENDOR_ID2); + if ((val & 0xff40) != 0x5340) + return 0; + if (codec_bits) + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, codec_bits); + ac97->spec.ad18xx.chained[idx] = cfg_bits[idx]; + ac97->spec.ad18xx.id[idx] = val; + return 1; +} + +static void patch_ad1881_chained(ac97_t * ac97, int unchained_idx, int cidx1, int cidx2) +{ + // already detected? + if (ac97->spec.ad18xx.unchained[cidx1] || ac97->spec.ad18xx.chained[cidx1]) + cidx1 = -1; + if (ac97->spec.ad18xx.unchained[cidx2] || ac97->spec.ad18xx.chained[cidx2]) + cidx2 = -1; + if (cidx1 < 0 && cidx2 < 0) + return; + // test for chained codecs + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[unchained_idx]); + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0002); // ID1C + if (cidx1 >= 0) { + if (patch_ad1881_chained1(ac97, cidx1, 0x0006)) // SDIE | ID1C + patch_ad1881_chained1(ac97, cidx2, 0); + else if (patch_ad1881_chained1(ac97, cidx2, 0x0006)) // SDIE | ID1C + patch_ad1881_chained1(ac97, cidx1, 0); + } else if (cidx2 >= 0) { + patch_ad1881_chained1(ac97, cidx2, 0); + } +} + +static int patch_ad1881(ac97_t * ac97) +{ + static const char cfg_idxs[3][2] = { + {2, 1}, + {0, 2}, + {0, 1} + }; + + // patch for Analog Devices + unsigned short codecs[3]; + int idx, num; + + init_MUTEX(&ac97->spec.ad18xx.mutex); + + codecs[0] = patch_ad1881_unchained(ac97, 0, (1<<12)); + codecs[1] = patch_ad1881_unchained(ac97, 1, (1<<14)); + codecs[2] = patch_ad1881_unchained(ac97, 2, (1<<13)); + + snd_runtime_check(codecs[0] | codecs[1] | codecs[2], goto __end); + + for (idx = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.unchained[idx]) + patch_ad1881_chained(ac97, idx, cfg_idxs[idx][0], cfg_idxs[idx][1]); + + if (ac97->spec.ad18xx.id[1]) { + ac97->flags |= AC97_AD_MULTI; + ac97->scaps |= AC97_SCAP_SURROUND_DAC; + } + if (ac97->spec.ad18xx.id[2]) { + ac97->flags |= AC97_AD_MULTI; + ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; + } + + __end: + /* select all codecs */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); + /* check if only one codec is present */ + for (idx = num = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.id[idx]) + num++; + if (num == 1) { + /* ok, deselect all ID bits */ + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000); + } + /* required for AD1886/AD1885 combination */ + ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ac97->spec.ad18xx.id[0]) { + ac97->id &= 0xffff0000; + ac97->id |= ac97->spec.ad18xx.id[0]; + } + return 0; +} + +/* + * PCM support + */ + +int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate) +{ + unsigned short mask; + unsigned int tmp; + signed long end_time; + + switch (reg) { + case AC97_PCM_MIC_ADC_RATE: + mask = 0x0000; + if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0008) == 0) /* MIC VRA */ + if (rate != 48000) + return -EINVAL; + break; + case AC97_PCM_FRONT_DAC_RATE: + mask = 0x0200; + if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0) /* VRA */ + if (rate != 48000) + return -EINVAL; + break; + case AC97_PCM_LR_ADC_RATE: + mask = 0x0100; + if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0) /* VRA */ + if (rate != 48000) + return -EINVAL; + break; + default: return -EINVAL; + } + tmp = ((unsigned int)rate * ac97->clock) / 48000; + if (tmp > 65535) + return -EINVAL; + snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, mask); + end_time = jiffies + (HZ / 50); + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_ac97_update(ac97, reg, tmp & 0xffff); + udelay(10); + snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, 0); + end_time = jiffies + (HZ / 50); + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + end_time = jiffies + (HZ / 10); + do { + if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0003) == 0x0003) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + return 0; +} + + +#ifdef CONFIG_PM +/* + * general suspend procedure + */ +void snd_ac97_suspend(ac97_t *ac97) +{ + unsigned short power = (ac97->regs[AC97_POWERDOWN] ^ 0x8000) & ~0x8000; /* invert EAPD */ + + power |= 0x4000; /* Headphone amplifier powerdown */ + power |= 0x0300; /* ADC & DAC powerdown */ + snd_ac97_write(ac97, AC97_POWERDOWN, power); + udelay(100); + power |= 0x0400; /* Analog Mixer powerdown (Vref on) */ + snd_ac97_write(ac97, AC97_POWERDOWN, power); + udelay(100); + power |= 0x3800; /* AC-link powerdown, internal Clk disable */ + snd_ac97_write(ac97, AC97_POWERDOWN, power); +} + +/* + * general resume procedure + */ +void snd_ac97_resume(ac97_t *ac97) +{ + int i; + + snd_ac97_write(ac97, AC97_POWERDOWN, 0); + snd_ac97_write(ac97, AC97_RESET, 0); + udelay(100); + snd_ac97_write(ac97, AC97_POWERDOWN, 0); + snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0); + + snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]); + snd_ac97_write(ac97, AC97_MASTER, 0x8000); + for (i = 0; i < 10; i++) { + if (snd_ac97_read(ac97, AC97_MASTER) == 0x8000) + break; + mdelay(1); + } + + if (ac97->init) + ac97->init(ac97); + + /* restore ac97 status */ + for (i = 2; i < 0x7c ; i += 2) { + if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID) + continue; + /* restore only accessible registers + * some chip (e.g. nm256) may hang up when unsupported registers + * are accessed..! + */ + if (test_bit(i, ac97->reg_accessed)) + snd_ac97_write(ac97, i, ac97->regs[i]); + } +} +#endif + + +/* + * Exported symbols + */ + +EXPORT_SYMBOL(snd_ac97_write); +EXPORT_SYMBOL(snd_ac97_read); +EXPORT_SYMBOL(snd_ac97_write_cache); +EXPORT_SYMBOL(snd_ac97_update); +EXPORT_SYMBOL(snd_ac97_update_bits); +EXPORT_SYMBOL(snd_ac97_mixer); +EXPORT_SYMBOL(snd_ac97_set_rate); +#ifdef CONFIG_PM +EXPORT_SYMBOL(snd_ac97_resume); +#endif + +/* + * INIT part + */ + +static int __init alsa_ac97_init(void) +{ + return 0; +} + +static void __exit alsa_ac97_exit(void) +{ +} + +module_init(alsa_ac97_init) +module_exit(alsa_ac97_exit) diff -Nru a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ac97/ak4531_codec.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,466 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Universal routines for AK4531 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Universal routines for AK4531 codec"); +MODULE_LICENSE("GPL"); + +#define chip_t ak4531_t + +static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531); +static void snd_ak4531_proc_done(ak4531_t * ak4531); + +/* + * + */ + +#if 0 + +static void snd_ak4531_dump(ak4531_t *ak4531) +{ + int idx; + + for (idx = 0; idx < 0x19; idx++) + printk("ak4531 0x%x: 0x%x\n", idx, ak4531->regs[idx]); +} + +#endif + +/* + * + */ + +#define AK4531_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ak4531_info_single, \ + get: snd_ak4531_get_single, put: snd_ak4531_put_single, \ + private_value: reg | (shift << 16) | (mask << 24) | (invert << 22) } + +static int snd_ak4531_info_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ak4531_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int val; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + val = (ak4531->regs[reg] >> shift) & mask; + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + if (invert) { + val = mask - val; + } + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int snd_ak4531_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + int val; + + val = ucontrol->value.integer.value[0] & mask; + if (invert) { + val = mask - val; + } + val <<= shift; + spin_lock_irqsave(&ak4531->reg_lock, flags); + val = (ak4531->regs[reg] & ~(mask << shift)) | val; + change = val != ak4531->regs[reg]; + ak4531->write(ak4531, reg, ak4531->regs[reg] = val); + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return change; +} + +#define AK4531_DOUBLE(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ak4531_info_double, \ + get: snd_ak4531_get_double, put: snd_ak4531_put_double, \ + private_value: left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) } + +static int snd_ak4531_info_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ak4531_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int left, right; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + left = (ak4531->regs[left_reg] >> left_shift) & mask; + right = (ak4531->regs[right_reg] >> right_shift) & mask; + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + if (invert) { + left = mask - left; + right = mask - right; + } + ucontrol->value.integer.value[0] = left; + ucontrol->value.integer.value[1] = right; + return 0; +} + +static int snd_ak4531_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + int left, right; + + left = ucontrol->value.integer.value[0] & mask; + right = ucontrol->value.integer.value[1] & mask; + if (invert) { + left = mask - left; + right = mask - right; + } + left <<= left_shift; + right <<= right_shift; + spin_lock_irqsave(&ak4531->reg_lock, flags); + if (left_reg == right_reg) { + left = (ak4531->regs[left_reg] & ~((mask << left_shift) | (mask << right_shift))) | left | right; + change = left != ak4531->regs[left_reg]; + ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left); + } else { + left = (ak4531->regs[left_reg] & ~(mask << left_shift)) | left; + right = (ak4531->regs[right_reg] & ~(mask << right_shift)) | right; + change = left != ak4531->regs[left_reg] || right != ak4531->regs[right_reg]; + ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left); + ak4531->write(ak4531, right_reg, ak4531->regs[right_reg] = right); + } + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return change; +} + +#define AK4531_INPUT_SW(xname, xindex, reg1, reg2, left_shift, right_shift) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ak4531_info_input_sw, \ + get: snd_ak4531_get_input_sw, put: snd_ak4531_put_input_sw, \ + private_value: reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) } + +static int snd_ak4531_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ak4531_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + ucontrol->value.integer.value[0] = (ak4531->regs[reg1] >> left_shift) & 1; + ucontrol->value.integer.value[1] = (ak4531->regs[reg2] >> left_shift) & 1; + ucontrol->value.integer.value[2] = (ak4531->regs[reg1] >> right_shift) & 1; + ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1; + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return 0; +} + +static int snd_ak4531_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + int change; + int val1, val2; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + val1 = ak4531->regs[reg1] & ~((1 << left_shift) | (1 << right_shift)); + val2 = ak4531->regs[reg2] & ~((1 << left_shift) | (1 << right_shift)); + val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift; + val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift; + val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift; + val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift; + change = val1 != ak4531->regs[reg1] || val2 != ak4531->regs[reg2]; + ak4531->write(ak4531, reg1, ak4531->regs[reg1] = val1); + ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2); + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return change; +} + +#define AK4531_CONTROLS (sizeof(snd_ak4531_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ak4531_controls[] = { + +AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1), +AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1), + +AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1), +AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1), + +AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1), +AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1), +AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0), +AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0), + +AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1), +AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1), +AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0), +AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5), + +AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1), +AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1), +AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0), +AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1), + +AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1), +AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1), +AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0), +AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3), + +AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1), +AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1), +AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0), +AK4531_INPUT_SW("Aux Input Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3), + +AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1), +AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1), +AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0), +AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0), + +AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1), +AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1), +AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0), +AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0), + +AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1), +AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1), +AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0), +AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0), + +AK4531_DOUBLE("Mic Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 7, 7, 1, 0), +AK4531_DOUBLE("Mono1 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 6, 6, 1, 0), +AK4531_DOUBLE("Mono2 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 5, 5, 1, 0), + +AK4531_SINGLE("AD Input Select", 0, AK4531_AD_IN, 0, 1, 0), +AK4531_SINGLE("Mic Boost (+30dB)", 0, AK4531_MIC_GAIN, 0, 1, 0) +}; + +static int snd_ak4531_free(ak4531_t *ak4531) +{ + if (ak4531) { + snd_ak4531_proc_done(ak4531); + if (ak4531->private_free) + ak4531->private_free(ak4531); + snd_magic_kfree(ak4531); + } + return 0; +} + +static int snd_ak4531_dev_free(snd_device_t *device) +{ + ak4531_t *ak4531 = snd_magic_cast(ak4531_t, device->device_data, return -ENXIO); + return snd_ak4531_free(ak4531); +} + +static u8 snd_ak4531_initial_map[0x19 + 1] = { + 0x9f, /* 00: Master Volume Lch */ + 0x9f, /* 01: Master Volume Rch */ + 0x9f, /* 02: Voice Volume Lch */ + 0x9f, /* 03: Voice Volume Rch */ + 0x9f, /* 04: FM Volume Lch */ + 0x9f, /* 05: FM Volume Rch */ + 0x9f, /* 06: CD Audio Volume Lch */ + 0x9f, /* 07: CD Audio Volume Rch */ + 0x9f, /* 08: Line Volume Lch */ + 0x9f, /* 09: Line Volume Rch */ + 0x9f, /* 0a: Aux Volume Lch */ + 0x9f, /* 0b: Aux Volume Rch */ + 0x9f, /* 0c: Mono1 Volume */ + 0x9f, /* 0d: Mono2 Volume */ + 0x9f, /* 0e: Mic Volume */ + 0x87, /* 0f: Mono-out Volume */ + 0x00, /* 10: Output Mixer SW1 */ + 0x00, /* 11: Output Mixer SW2 */ + 0x00, /* 12: Lch Input Mixer SW1 */ + 0x00, /* 13: Rch Input Mixer SW1 */ + 0x00, /* 14: Lch Input Mixer SW2 */ + 0x00, /* 15: Rch Input Mixer SW2 */ + 0x00, /* 16: Reset & Power Down */ + 0x00, /* 17: Clock Select */ + 0x00, /* 18: AD Input Select */ + 0x01 /* 19: Mic Amp Setup */ +}; + +int snd_ak4531_mixer(snd_card_t * card, ak4531_t * _ak4531, ak4531_t ** rak4531) +{ + int idx, err; + ak4531_t * ak4531; + static snd_device_ops_t ops = { + dev_free: snd_ak4531_dev_free, + }; + + snd_assert(rak4531 != NULL, return -EINVAL); + *rak4531 = NULL; + snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL); + ak4531 = snd_magic_kcalloc(ak4531_t, 0, GFP_KERNEL); + if (ak4531 == NULL) + return -ENOMEM; + *ak4531 = *_ak4531; + spin_lock_init(&ak4531->reg_lock); + if ((err = snd_component_add(card, "AK4531")) < 0) { + snd_ak4531_free(ak4531); + return err; + } + strcpy(card->mixername, "Asahi Kasei AK4531"); + ak4531->write(ak4531, AK4531_RESET, 0x03); /* no RST, PD */ + udelay(100); + ak4531->write(ak4531, AK4531_CLOCK, 0x00); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */ + for (idx = 0; idx < 0x19; idx++) { + if (idx == AK4531_RESET || idx == AK4531_CLOCK) + continue; + ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]); /* recording source is mixer */ + } + for (idx = 0; idx < AK4531_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531))) < 0) { + snd_ak4531_free(ak4531); + return err; + } + } + snd_ak4531_proc_init(card, ak4531); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ak4531, &ops)) < 0) { + snd_ak4531_free(ak4531); + return err; + } + +#if 0 + snd_ak4531_dump(ak4531); +#endif + *rak4531 = ak4531; + return 0; +} + +/* + + */ + +static void snd_ak4531_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ak4531_t *ak4531 = snd_magic_cast(ak4531_t, entry->private_data, return); + + snd_iprintf(buffer, "Asahi Kasei AK4531\n\n"); + snd_iprintf(buffer, "Recording source : %s\n" + "MIC gain : %s\n", + ak4531->regs[AK4531_AD_IN] & 1 ? "external" : "mixer", + ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB"); +} + +static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(card, "ak4531", card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = ak4531; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_ak4531_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ak4531->proc_entry = entry; +} + +static void snd_ak4531_proc_done(ak4531_t * ak4531) +{ + if (ak4531->proc_entry) { + snd_info_unregister(ak4531->proc_entry); + ak4531->proc_entry = NULL; + } +} + +EXPORT_SYMBOL(snd_ak4531_mixer); + +/* + * INIT part + */ + +static int __init alsa_ak4531_init(void) +{ + return 0; +} + +static void __exit alsa_ak4531_exit(void) +{ +} + +module_init(alsa_ak4531_init) +module_exit(alsa_ak4531_exit) diff -Nru a/sound/pci/ali5451/Makefile b/sound/pci/ali5451/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ali5451/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ali5451.o + +list-multi := snd-ali5451.o + +snd-ali5451-objs := ali5451.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALI5451) += snd-ali5451.o + +include $(TOPDIR)/Rules.make + +snd-ali5451.o: $(snd-ali5451-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ali5451-objs) diff -Nru a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ali5451/ali5451.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2291 @@ +/* + * Matt Wu + * Apr 26, 2001 + * Routines for control of ALi pci audio M5451 + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Lcodecnse as published by + * the Free Software Foundation; either version 2 of the Lcodecnse, 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 Lcodecnse for more details. + * + * You should have received a copy of the GNU General Public Lcodecnse + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __SNDRV_OSS_COMPAT__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Matt Wu "); +MODULE_DESCRIPTION("ALI M5451"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALI,M5451,pci},{ALI,M5451}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ALI M5451 PCI Audio."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ALI M5451 PCI Audio."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ALI 5451 PCI Audio."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_pcm_channels, "PCM Channels"); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}"); + +/* + * Debug part definations + */ + +//#define ALI_DEBUG + +#ifdef ALI_DEBUG +#define snd_ali_printk(format, args...) printk(format, ##args); +#else +#define snd_ali_printk(format, args...) +#endif + +/* + * Constants defination + */ + +#ifndef PCI_VENDOR_ID_ALI +#define PCI_VENDOR_ID_ALI 0x10b9 +#endif + +#ifndef PCI_DEVICE_ID_ALI_5451 +#define PCI_DEVICE_ID_ALI_5451 0x5451 +#endif + +#define DEVICE_ID_ALI5451 ((PCI_VENDOR_ID_ALI<<16)|PCI_DEVICE_ID_ALI_5451) + + +#define ALI_CHANNELS 32 + +#define ALI_PCM_IN_CHANNEL 31 +#define ALI_SPDIF_IN_CHANNEL 19 +#define ALI_SPDIF_OUT_CHANNEL 15 +#define ALI_CENTER_CHANNEL 24 +#define ALI_LEF_CHANNEL 23 +#define ALI_SURR_LEFT_CHANNEL 26 +#define ALI_SURR_RIGHT_CHANNEL 25 + +#define SNDRV_ALI_VOICE_TYPE_PCM 01 +#define SNDRV_ALI_VOICE_TYPE_OTH 02 + +#define ALI_5451_V02 0x02 + +/* + * Direct Registers + */ + +#define ALI_LEGACY_DMAR0 0x00 // ADR0 +#define ALI_LEGACY_DMAR4 0x04 // CNT0 +#define ALI_LEGACY_DMAR11 0x0b // MOD +#define ALI_LEGACY_DMAR15 0x0f // MMR +#define ALI_MPUR0 0x20 +#define ALI_MPUR1 0x21 +#define ALI_MPUR2 0x22 +#define ALI_MPUR3 0x23 + +#define ALI_AC97_WRITE 0x40 +#define ALI_AC97_READ 0x44 + +#define ALI_SCTRL 0x48 +#define ALI_AC97_GPIO 0x4c +#define ALI_SPDIF_CS 0x70 +#define ALI_SPDIF_CTRL 0x74 +#define ALI_START 0x80 +#define ALI_STOP 0x84 +#define ALI_CSPF 0x90 +#define ALI_AINT 0x98 +#define ALI_GC_CIR 0xa0 + #define ENDLP_IE 0x00001000 + #define MIDLP_IE 0x00002000 +#define ALI_AINTEN 0xa4 +#define ALI_VOLUME 0xa8 +#define ALI_SBDELTA_DELTA_R 0xac +#define ALI_MISCINT 0xb0 + #define ADDRESS_IRQ 0x00000020 + #define TARGET_REACHED 0x00008000 + #define MIXER_OVERFLOW 0x00000800 + #define MIXER_UNDERFLOW 0x00000400 +#define ALI_SBBL_SBCL 0xc0 +#define ALI_SBCTRL_SBE2R_SBDD 0xc4 +#define ALI_STIMER 0xc8 +#define ALI_GLOBAL_CONTROL 0xd4 + +#define ALI_CSO_ALPHA_FMS 0xe0 +#define ALI_LBA 0xe4 +#define ALI_ESO_DELTA 0xe8 +#define ALI_GVSEL_PAN_VOC_CTRL_EC 0xf0 +#define ALI_EBUF1 0xf4 +#define ALI_EBUF2 0xf8 + +#define ALI_REG(codec, x) ((codec)->port + x) + +typedef struct snd_stru_ali ali_t; +typedef struct snd_ali_stru_voice snd_ali_voice_t; +#define chip_t ali_t + +typedef struct snd_ali_channel_control { + // register data + struct REGDATA { + unsigned int start; + unsigned int stop; + unsigned int aint; + unsigned int ainten; + } data; + + // register addresses + struct REGS { + unsigned int start; + unsigned int stop; + unsigned int aint; + unsigned int ainten; + unsigned int ac97read; + unsigned int ac97write; + } regs; + +} snd_ali_channel_control_t; + +struct snd_ali_stru_voice { + unsigned int number; + int use: 1, + pcm: 1, + midi: 1, + mode: 1, + synth: 1; + + /* PCM data */ + ali_t *codec; + snd_pcm_substream_t *substream; + snd_ali_voice_t *extra; + + int running: 1; + + int eso; /* final ESO value for channel */ + int count; /* runtime->period_size */ + + /* --- */ + + void *private_data; + void (*private_free)(void *private_data); +}; + + +typedef struct snd_stru_alidev { + + snd_ali_voice_t voices[ALI_CHANNELS]; + + unsigned int chcnt; /* num of opened channels */ + unsigned int chmap; /* bitmap for opened channels */ + unsigned int synthcount; + +} alidev_t; + + +#ifdef CONFIG_PM +#define ALI_GLOBAL_REGS 56 +#define ALI_CHANNEL_REGS 8 +typedef struct snd_ali_image { + unsigned long regs[ALI_GLOBAL_REGS]; + unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS]; +} ali_image_t; +#endif + + +struct snd_stru_ali { + unsigned long irq; + unsigned long port; + unsigned char revision; + + struct resource *res_port; + + struct pci_dev *pci; + struct pci_dev *pci_m1533; + struct pci_dev *pci_m7101; + + snd_card_t *card; + snd_pcm_t *pcm; + alidev_t synth; + snd_ali_channel_control_t chregs; + + /* S/PDIF Mask */ + unsigned int spdif_mask; + + unsigned int spurious_irq_count; + unsigned int spurious_irq_max_delta; + + ac97_t *ac97; + unsigned short ac97_ext_id; + unsigned short ac97_ext_status; + + spinlock_t reg_lock; + spinlock_t voice_alloc; + +#ifdef CONFIG_PM + ali_image_t *image; +#endif +}; + +static struct pci_device_id snd_ali_ids[] __devinitdata = { + {0x10b9, 0x5451, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + {0, } +}; +MODULE_DEVICE_TABLE(pci, snd_ali_ids); + +static void snd_ali_clear_voices(ali_t *, unsigned int, unsigned int); +static unsigned short snd_ali_codec_peek(ali_t *, int, unsigned short); +static void snd_ali_codec_poke(ali_t *, int, unsigned short, unsigned short); + +/* + * Debug Part + */ + +#ifdef ALI_DEBUG + +static void ali_read_regs(ali_t *codec, int channel) +{ + int i,j; + unsigned int dwVal; + + printk("channel %d registers map:\n", channel); + outb((unsigned char)(channel & 0x001f), ALI_REG(codec,ALI_GC_CIR)); + + printk(" "); + for(j=0;j<8;j++) + printk("%2.2x ", j*4); + printk("\n"); + + for (i=0; i<=0xf8/4;i++) { + if(i%8 == 0) + printk("%2.2x ", (i*4/0x10)*0x10); + dwVal = inl(ALI_REG(codec,i*4)); + printk("%8.8x ", dwVal); + if ((i+1)%8 == 0) + printk("\n"); + } + printk("\n"); +} +static void ali_read_cfg(unsigned int vendor, unsigned deviceid) +{ + unsigned int dwVal; + struct pci_dev *pci_dev = NULL; + int i,j; + + + pci_dev = pci_find_device(vendor, deviceid, pci_dev); + if (pci_dev == NULL) + return ; + + printk("\nM%x PCI CFG\n", deviceid); + printk(" "); + for(j=0;j<8;j++) + printk("%d ",j); + printk("\n"); + + for(i=0;i<8;i++) { + printk("%d ",i); + for(j=0;j<8;j++) + { + pci_read_config_dword(pci_dev, i*0x20+j*4, &dwVal); + printk("%8.8x ", dwVal); + } + printk("\n"); + } + } +static void ali_read_ac97regs(ali_t *codec, int secondary) +{ + unsigned short i,j; + unsigned short wVal; + + printk("\ncodec %d registers map:\n", secondary); + + printk(" "); + for(j=0;j<8;j++) + printk("%2.2x ",j*2); + printk("\n"); + + for (i=0; i<64;i++) { + if(i%8 == 0) + printk("%2.2x ", (i/8)*0x10); + wVal = snd_ali_codec_peek(codec, secondary, i*2); + printk("%4.4x ", wVal); + if ((i+1)%8 == 0) + printk("\n"); + } + printk("\n"); +} + +#endif + +/* + * AC97 ACCESS + */ + +static inline unsigned int snd_ali_5451_peek(ali_t *codec, + unsigned int port ) +{ + return (unsigned int)inl(ALI_REG(codec, port)); +} + +static inline void snd_ali_5451_poke( ali_t *codec, + unsigned int port, + unsigned int val ) +{ + outl((unsigned int)val, ALI_REG(codec, port)); +} + +static int snd_ali_codec_ready( ali_t *codec, + unsigned int port, + int sched ) +{ + signed long end_time; + + end_time = jiffies + 10 * (HZ >> 2); + do { + if (!(snd_ali_5451_peek(codec,port) & 0x8000)) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + snd_printk("ali_codec_ready: codec is not ready.\n "); + return -EIO; +} + +static int snd_ali_stimer_ready(ali_t *codec, int sched) +{ + signed long end_time; + unsigned long dwChk1,dwChk2; + + dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER); + dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); + + end_time = jiffies + 10 * (HZ >> 2); + do { + dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); + if (dwChk2 != dwChk1) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + snd_printk("ali_stimer_read: stimer is not ready.\n"); + return -EIO; +} + +static void snd_ali_codec_poke(ali_t *codec,int secondary, + unsigned short reg, + unsigned short val) +{ + unsigned int dwVal = 0; + unsigned int port = 0; + + if (reg >= 0x80) { + snd_printk("ali_codec_poke: reg(%xh) invalid.\n", reg); + return; + } + + port = codec->chregs.regs.ac97write; + + if (snd_ali_codec_ready(codec, port, 0) < 0) + return; + if (snd_ali_stimer_ready(codec, 0) < 0) + return; + + dwVal = (unsigned int) (reg & 0xff); + dwVal |= 0x8000 | (val << 16); + if (secondary) dwVal |= 0x0080; + if (codec->revision == ALI_5451_V02) dwVal |= 0x0100; + + snd_ali_5451_poke(codec,port,dwVal); + + return ; +} + +static unsigned short snd_ali_codec_peek( ali_t *codec, + int secondary, + unsigned short reg) +{ + unsigned int dwVal = 0; + unsigned int port = 0; + + if (reg >= 0x80) { + snd_printk("ali_codec_peek: reg(%xh) invalid.\n", reg); + return ~0; + } + + port = codec->chregs.regs.ac97read; + + if (snd_ali_codec_ready(codec, port, 0) < 0) + return ~0; + if (snd_ali_stimer_ready(codec, 0) < 0) + return ~0; + + dwVal = (unsigned int) (reg & 0xff); + dwVal |= 0x8000; /* bit 15*/ + if (secondary) dwVal |= 0x0080; + + snd_ali_5451_poke(codec, port, dwVal); + + if (snd_ali_stimer_ready(codec, 0) < 0) + return ~0; + if (snd_ali_codec_ready(codec, port, 0) < 0) + return ~0; + + return (snd_ali_5451_peek(codec, port) & 0xffff0000)>>16; +} + +static void snd_ali_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val ) +{ + ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return); + + snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val); + snd_ali_codec_poke(codec, 0, reg, val); + return ; +} + + +static unsigned short snd_ali_codec_read(ac97_t *ac97, unsigned short reg) +{ + ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return -ENXIO); + + snd_ali_printk("codec_read reg=%xh.\n", reg); + return (snd_ali_codec_peek(codec, 0, reg)); +} + +/* + * AC97 Reset + */ + +static int snd_ali_reset_5451(ali_t *codec) +{ + struct pci_dev *pci_dev = NULL; + unsigned short wCount, wReg; + unsigned int dwVal; + + if ((pci_dev = codec->pci_m1533) != NULL) { + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); + udelay(5000); + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); + udelay(5000); + } + + pci_dev = codec->pci; + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000); + udelay(500); + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff); + udelay(5000); + + wCount = 200; + while(wCount--) { + wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN); + if((wReg & 0x000f) == 0x000f) + return 0; + udelay(5000); + } + + return -1; +} + +#ifdef CODEC_RESET + +static int snd_ali_reset_codec(ali_t *codec) +{ + struct pci_dev *pci_dev = NULL; + unsigned char bVal = 0; + unsigned int dwVal; + unsigned short wCount, wReg; + + pci_dev = codec->pci_m1533; + + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); + udelay(5000); + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); + udelay(5000); + + bVal = inb(ALI_REG(codec,ALI_SCTRL)); + bVal |= 0x02; + outb(ALI_REG(codec,ALI_SCTRL),bVal); + udelay(5000); + bVal = inb(ALI_REG(codec,ALI_SCTRL)); + bVal &= 0xfd; + outb(ALI_REG(codec,ALI_SCTRL),bVal); + udelay(15000); + + wCount = 200; + while(wCount--) { + wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN); + if((wReg & 0x000f) == 0x000f) + return 0; + udelay(5000); + } + return -1; +} + +#endif + +/* + * ALI 5451 Controller + */ + +static void snd_ali_enable_special_channel(ali_t *codec, unsigned int channel) +{ + unsigned long dwVal = 0; + + dwVal = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL)); + dwVal |= 1 << (channel & 0x0000001f); + outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL)); +} + +static void snd_ali_disable_special_channel(ali_t *codec, unsigned int channel) +{ + unsigned long dwVal = 0; + + dwVal = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL)); + dwVal &= ~(1 << (channel & 0x0000001f)); + outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL)); +} + +static void snd_ali_enable_address_interrupt(ali_t * codec) +{ + unsigned int gc; + + gc = inl(ALI_REG(codec, ALI_GC_CIR)); + gc |= ENDLP_IE; + gc |= MIDLP_IE; + outl( gc, ALI_REG(codec, ALI_GC_CIR)); +} + +static void snd_ali_disable_address_interrupt(ali_t * codec) +{ + unsigned int gc; + + gc = inl(ALI_REG(codec, ALI_GC_CIR)); + gc &= ~ENDLP_IE; + gc &= ~MIDLP_IE; + outl(gc, ALI_REG(codec, ALI_GC_CIR)); +} + +#if 0 // not used +static void snd_ali_enable_voice_irq(ali_t *codec, unsigned int channel) +{ + unsigned int mask; + snd_ali_channel_control_t *pchregs = &(codec->chregs); + + snd_ali_printk("enable_voice_irq channel=%d\n",channel); + + mask = 1 << (channel & 0x1f); + pchregs->data.ainten = inl(ALI_REG(codec,pchregs->regs.ainten)); + pchregs->data.ainten |= mask; + outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten)); +} +#endif + +static void snd_ali_disable_voice_irq(ali_t *codec, unsigned int channel) +{ + unsigned int mask; + snd_ali_channel_control_t *pchregs = &(codec->chregs); + + snd_ali_printk("disable_voice_irq channel=%d\n",channel); + + mask = 1 << (channel & 0x1f); + pchregs->data.ainten = inl(ALI_REG(codec,pchregs->regs.ainten)); + pchregs->data.ainten &= ~mask; + outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten)); +} + +static int snd_ali_alloc_pcm_channel(ali_t *codec, int channel) +{ + unsigned int idx = channel & 0x1f; + + if (codec->synth.chcnt >= ALI_CHANNELS){ + snd_printk("ali_alloc_pcm_channel: no free channels.\n"); + return -1; + } + + if (!(codec->synth.chmap & (1 << idx))) { + codec->synth.chmap |= 1 << idx; + codec->synth.chcnt++; + snd_ali_printk("alloc_pcm_channel no. %d.\n",idx); + return idx; + } + return -1; +} + +static int snd_ali_find_free_channel(ali_t * codec, int rec) +{ + int idx; + int result = -1; + + snd_ali_printk("find_free_channel: for %s\n",rec ? "rec" : "pcm"); + + // recording + if (rec) { + if (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<11) && + ( codec->revision == ALI_5451_V02 )) + idx = ALI_SPDIF_IN_CHANNEL; + else + idx = ALI_PCM_IN_CHANNEL; + + if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) { + return result; + } else { + snd_printk("ali_find_free_channel: record channel is busy now.\n"); + return -1; + } + } + + //playback... + if (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<15)) { + idx = ALI_SPDIF_OUT_CHANNEL; + if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) { + return result; + } else { + snd_printk("ali_find_free_channel: S/PDIF out channel is in busy now.\n"); + } + } + + for (idx = 0; idx < ALI_CHANNELS; idx++) { + if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) + return result; + } + snd_printk("ali_find_free_channel: no free channels.\n"); + return -1; +} + +static void snd_ali_free_channel_pcm(ali_t *codec, int channel) +{ + unsigned int idx = channel & 0x0000001f; + + snd_ali_printk("free_channel_pcm channel=%d\n",channel); + + if (channel < 0 || channel >= ALI_CHANNELS) + return; + + if (!(codec->synth.chmap & (1 << idx))) { + snd_printk("ali_free_channel_pcm: channel %d is not in use.\n",channel); + return; + } else { + codec->synth.chmap &= ~(1 << idx); + codec->synth.chcnt--; + } +} + +#if 0 // not used +static void snd_ali_start_voice(ali_t * codec, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + + snd_ali_printk("start_voice: channel=%d\n",channel); + outl(mask, ALI_REG(codec,codec->chregs.regs.start)); +} +#endif + +static void snd_ali_stop_voice(ali_t * codec, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + + snd_ali_printk("stop_voice: channel=%d\n",channel); + outl(mask, ALI_REG(codec, codec->chregs.regs.stop)); +} + +/* + * S/PDIF Part + */ + +static void snd_ali_delay(ali_t *codec,int interval) +{ + unsigned long begintimer,currenttimer; + + begintimer = inl(ALI_REG(codec, ALI_STIMER)); + currenttimer = inl(ALI_REG(codec, ALI_STIMER)); + + while (currenttimer < begintimer + interval) { + if(snd_ali_stimer_ready(codec, 1) < 0) + break; + currenttimer = inl(ALI_REG(codec, ALI_STIMER)); + } +} + +static void snd_ali_detect_spdif_rate(ali_t *codec) +{ + u16 wval = 0; + u16 count = 0; + u8 bval = 0, R1 = 0, R2 = 0; + + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + bval |= 0x1F; + outb(bval,ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + + while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000) { + count ++; + snd_ali_delay(codec, 6); + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + R1 = bval & 0x1F; + } + + if (count > 50000) { + snd_printk("ali_detect_spdif_rate: timeout!\n"); + return; + } + + count = 0; + while (count++ <= 50000) { + snd_ali_delay(codec, 6); + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + R2 = bval & 0x1F; + if (R2 != R1) R1 = R2; else break; + } + + if (count > 50000) { + snd_printk("ali_detect_spdif_rate: timeout!\n"); + return; + } + + if (R2 >= 0x0b && R2 <= 0x0e) { + wval = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + wval &= 0xE0F0; + wval |= (u16)0x09 << 8 | (u16)0x05; + outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + + bval = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0; + outb(bval|0x02,ALI_REG(codec,ALI_SPDIF_CS + 3)); + } else if (R2 == 0x12) { + wval = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + wval &= 0xE0F0; + wval |= (u16)0x0E << 8 | (u16)0x08; + outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + + bval = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0; + outb(bval|0x03,ALI_REG(codec,ALI_SPDIF_CS + 3)); + } +} + +static unsigned int snd_ali_get_spdif_in_rate(ali_t *codec) +{ + u32 dwRate = 0; + u8 bval = 0; + + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL)); + bval &= 0x7F; + bval |= 0x40; + outb(bval, ALI_REG(codec,ALI_SPDIF_CTRL)); + + snd_ali_detect_spdif_rate(codec); + + bval = inb(ALI_REG(codec,ALI_SPDIF_CS + 3)); + bval &= 0x0F; + + if (bval == 0) dwRate = 44100; + if (bval == 1) dwRate = 48000; + if (bval == 2) dwRate = 32000; + + return dwRate; +} + +static void snd_ali_enable_spdif_in(ali_t *codec) +{ + unsigned int dwVal; + + dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + dwVal |= 1<<11; + outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + dwVal |= 0x02; + outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + + snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); +} + +static void snd_ali_disable_spdif_in(ali_t *codec) +{ + unsigned int dwVal; + + dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + dwVal &= ~(1<<11); + outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); +} + + +static void snd_ali_set_spdif_out_rate(ali_t *codec, unsigned int rate) +{ + unsigned char bVal; + unsigned int dwRate = 0; + + if (rate == 32000) dwRate = 0x300; + if (rate == 44100) dwRate = 0; + if (rate == 48000) dwRate = 0x200; + + bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + bVal &= (unsigned char)(~(1<<6)); + + bVal |= 0x80; //select right + outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2)); + + bVal &= (~0x80); //select left + outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2)); +} + +static void snd_ali_enable_spdif_out(ali_t *codec) +{ + unsigned short wVal; + unsigned char bVal; + + struct pci_dev *pci_dev = NULL; + + pci_dev = codec->pci_m1533; + if (pci_dev == NULL) + return; + pci_read_config_byte(pci_dev, 0x61, &bVal); + bVal |= 0x40; + pci_write_config_byte(pci_dev, 0x61, bVal); + pci_read_config_byte(pci_dev, 0x7d, &bVal); + bVal |= 0x01; + pci_write_config_byte(pci_dev, 0x7d, bVal); + + pci_read_config_byte(pci_dev, 0x7e, &bVal); + bVal &= (~0x20); + bVal |= 0x10; + pci_write_config_byte(pci_dev, 0x7e, bVal); + + bVal = inb(ALI_REG(codec, ALI_SCTRL)); + outb(bVal | 0x20, ALI_REG(codec, ALI_SCTRL)); + + bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + outb(bVal & ~(1<<6), ALI_REG(codec, ALI_SPDIF_CTRL)); + + { + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal |= (1<<10); + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + snd_ali_disable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL); + } +} + +static void snd_ali_enable_spdif_chnout(ali_t *codec) +{ + unsigned short wVal = 0; + + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal &= ~(1<<10); + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); +/* + wVal = inw(ALI_REG(codec, ALI_SPDIF_CS)); + if (flag & ALI_SPDIF_OUT_NON_PCM) + wVal |= 0x0002; + else + wVal &= (~0x0002); + outw(wVal, ALI_REG(codec, ALI_SPDIF_CS)); +*/ + snd_ali_enable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL); +} + +static void snd_ali_disable_spdif_chnout(ali_t *codec) +{ + unsigned short wVal = 0; + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal |= (1<<10); + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); +} + +static void snd_ali_disable_spdif_out(ali_t *codec) +{ + unsigned char bVal; + + bVal = inb(ALI_REG(codec, ALI_SCTRL)); + outb(bVal & (~0x20), ALI_REG(codec, ALI_SCTRL)); + + snd_ali_disable_spdif_chnout(codec); +} + +static void snd_ali_update_ptr(ali_t *codec,int channel) +{ + snd_ali_voice_t *pvoice = NULL; + snd_pcm_runtime_t *runtime; + snd_ali_channel_control_t *pchregs = NULL; + unsigned int old, mask; +#ifdef ALI_DEBUG + unsigned int temp, cspf; +#endif + + pchregs = &(codec->chregs); + + // check if interrupt occured for channel + old = pchregs->data.aint; + mask = ((unsigned int) 1L) << (channel & 0x1f); + + if (!(old & mask)) + return; + + pvoice = &codec->synth.voices[channel]; + runtime = pvoice->substream->runtime; + + udelay(100); + spin_lock(&codec->reg_lock); + + if (pvoice->pcm && pvoice->substream) { + /* pcm interrupt */ +#ifdef ALI_DEBUG + outb((u8)(pvoice->number), ALI_REG(codec, ALI_GC_CIR)); + temp = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + cspf = (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask; +#endif + if (pvoice->running) { + snd_ali_printk("update_ptr: cso=%4.4x cspf=%d.\n",(u16)temp,cspf); + spin_unlock(&codec->reg_lock); + snd_pcm_period_elapsed(pvoice->substream); + spin_lock(&codec->reg_lock); + } else { + snd_ali_stop_voice(codec, channel); + snd_ali_disable_voice_irq(codec, channel); + } + } else if (codec->synth.voices[channel].synth) { + /* synth interrupt */ + } else if (codec->synth.voices[channel].midi) { + /* midi interrupt */ + } else { + /* unknown interrupt */ + snd_ali_stop_voice(codec, channel); + snd_ali_disable_voice_irq(codec, channel); + } + spin_unlock(&codec->reg_lock); + outl(mask,ALI_REG(codec,pchregs->regs.aint)); + pchregs->data.aint = old & (~mask); +} + +static void snd_ali_interrupt(ali_t * codec) +{ + int channel; + unsigned int audio_int; + snd_ali_channel_control_t *pchregs = NULL; + pchregs = &(codec->chregs); + + audio_int = inl(ALI_REG(codec, ALI_MISCINT)); + if (audio_int & ADDRESS_IRQ) { + // get interrupt status for all channels + pchregs->data.aint = inl(ALI_REG(codec,pchregs->regs.aint)); + for (channel = 0; channel < ALI_CHANNELS; channel++) { + snd_ali_update_ptr(codec, channel); + } + } + outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), + ALI_REG(codec,ALI_MISCINT)); +} + + +static void snd_ali_card_interrupt(int irq, + void *dev_id, + struct pt_regs *regs) +{ + ali_t *codec = snd_magic_cast(ali_t, dev_id, return); + + if (codec == NULL) + return; + snd_ali_interrupt(codec); +} + + +static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec) +{ + snd_ali_voice_t *pvoice = NULL; + unsigned long flags; + int idx; + + snd_ali_printk("alloc_voice: type=%d rec=%d\n",type,rec); + + spin_lock_irqsave(&codec->voice_alloc, flags); + if (type == SNDRV_ALI_VOICE_TYPE_PCM) { + idx = snd_ali_find_free_channel(codec,rec); + if(idx < 0) { + snd_printk("ali_alloc_voice: err.\n"); + spin_unlock_irqrestore(&codec->voice_alloc, flags); + return NULL; + } + pvoice = &(codec->synth.voices[idx]); + pvoice->use = 1; + pvoice->pcm = 1; + pvoice->mode = rec; + spin_unlock_irqrestore(&codec->voice_alloc, flags); + return pvoice; + } + spin_unlock_irqrestore(&codec->voice_alloc, flags); + return NULL; +} + + +static void snd_ali_free_voice(ali_t * codec, snd_ali_voice_t *pvoice) +{ + unsigned long flags; + void (*private_free)(void *); + void *private_data; + + snd_ali_printk("free_voice: channel=%d\n",pvoice->number); + if (pvoice == NULL || !pvoice->use) + return; + snd_ali_clear_voices(codec, pvoice->number, pvoice->number); + spin_lock_irqsave(&codec->voice_alloc, flags); + private_free = pvoice->private_free; + private_data = pvoice->private_data; + pvoice->private_free = NULL; + pvoice->private_data = NULL; + if (pvoice->pcm) { + snd_ali_free_channel_pcm(codec, pvoice->number); + } + pvoice->use = pvoice->pcm = pvoice->synth = 0; + pvoice->substream = NULL; + spin_unlock_irqrestore(&codec->voice_alloc, flags); + if (private_free) + private_free(private_data); +} + + +static void snd_ali_clear_voices(ali_t * codec, + unsigned int v_min, + unsigned int v_max) +{ + unsigned int i; + + for (i = v_min; i <= v_max; i++) { + snd_ali_stop_voice(codec, i); + snd_ali_disable_voice_irq(codec, i); + } +} + +static void snd_ali_write_voice_regs(ali_t * codec, + unsigned int Channel, + unsigned int LBA, + unsigned int CSO, + unsigned int ESO, + unsigned int DELTA, + unsigned int ALPHA_FMS, + unsigned int GVSEL, + unsigned int PAN, + unsigned int VOL, + unsigned int CTRL, + unsigned int EC) +{ + unsigned int ctlcmds[4]; + + outb((unsigned char)(Channel & 0x001f),ALI_REG(codec,ALI_GC_CIR)); + + ctlcmds[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff); + ctlcmds[1] = LBA; + ctlcmds[2] = (ESO << 16) | (DELTA & 0x0ffff); + ctlcmds[3] = (GVSEL << 31) | + ((PAN & 0x0000007f) << 24) | + ((VOL & 0x000000ff) << 16) | + ((CTRL & 0x0000000f) << 12) | + (EC & 0x00000fff); + + outb(Channel, ALI_REG(codec, ALI_GC_CIR)); + + outl(ctlcmds[0], ALI_REG(codec,ALI_CSO_ALPHA_FMS)); + outl(ctlcmds[1], ALI_REG(codec,ALI_LBA)); + outl(ctlcmds[2], ALI_REG(codec,ALI_ESO_DELTA)); + outl(ctlcmds[3], ALI_REG(codec,ALI_GVSEL_PAN_VOC_CTRL_EC)); + + outl(0x30000000, ALI_REG(codec, ALI_EBUF1)); /* Still Mode */ + outl(0x30000000, ALI_REG(codec, ALI_EBUF2)); /* Still Mode */ +} + +static unsigned int snd_ali_convert_rate(unsigned int rate, int rec) +{ + unsigned int delta; + + if (rate < 4000) rate = 4000; + if (rate > 48000) rate = 48000; + + if (rec) { + if (rate == 44100) + delta = 0x116a; + else if (rate == 8000) + delta = 0x6000; + else if (rate == 48000) + delta = 0x1000; + else + delta = ((48000 << 12) / rate) & 0x0000ffff; + } else { + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + rate) / 48000) & 0x0000ffff; + } + + return delta; +} + +static unsigned int snd_ali_control_mode(snd_pcm_substream_t *substream) +{ + unsigned int CTRL; + snd_pcm_runtime_t *runtime = substream->runtime; + + /* set ctrl mode + CTRL default: 8-bit (unsigned) mono, loop mode enabled + */ + CTRL = 0x00000001; + if (snd_pcm_format_width(runtime->format) == 16) + CTRL |= 0x00000008; // 16-bit data + if (!snd_pcm_format_unsigned(runtime->format)) + CTRL |= 0x00000002; // signed data + if (runtime->channels > 1) + CTRL |= 0x00000004; // stereo data + return CTRL; +} + +/* + * PCM part + */ + +static int snd_ali_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_ali_trigger(snd_pcm_substream_t *substream, + int cmd) + +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_substream_t *s; + unsigned int what, whati, capture_flag; + snd_ali_voice_t *pvoice = NULL, *evoice = NULL; + unsigned int val; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + what = whati = capture_flag = 0; + s = substream; + do { + if ((ali_t *) _snd_pcm_chip(s->pcm) == codec) { + pvoice = (snd_ali_voice_t *) s->runtime->private_data; + evoice = pvoice->extra; + what |= 1 << (pvoice->number & 0x1f); + if (evoice == NULL) { + whati |= 1 << (pvoice->number & 0x1f); + } else { + whati |= 1 << (evoice->number & 0x1f); + what |= 1 << (evoice->number & 0x1f); + } + if (cmd == SNDRV_PCM_TRIGGER_START) { + pvoice->running = 1; + if (evoice != NULL) + evoice->running = 1; + } + snd_pcm_trigger_done(s, substream); + if (pvoice->mode) + capture_flag = 1; + } + s = s->link_next; + } while (s != substream); + spin_lock(&codec->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + outl(what, ALI_REG(codec, ALI_STOP)); + pvoice->running = 0; + if (evoice != NULL) + evoice->running = 0; + } + val = inl(ALI_REG(codec, ALI_AINTEN)); + if (cmd == SNDRV_PCM_TRIGGER_START) { + val |= whati; + } else { + val &= ~whati; + } + outl(val, ALI_REG(codec, ALI_AINTEN)); + if (cmd == SNDRV_PCM_TRIGGER_START) { + outl(what, ALI_REG(codec, ALI_START)); + } + snd_ali_printk("trigger: what=%xh whati=%xh\n",what,whati); + spin_unlock(&codec->reg_lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int snd_ali_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + snd_ali_voice_t *evoice = pvoice->extra; + int err; + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + if (err < 0) return err; + + /* voice management */ + + if (params_buffer_size(hw_params)/2 != params_period_size(hw_params)) { + if (evoice == NULL) { + evoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0); + if (evoice == NULL) + return -ENOMEM; + pvoice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice != NULL) { + snd_ali_free_voice(codec, evoice); + pvoice->extra = evoice = NULL; + } + } + + return 0; +} + +static int snd_ali_playback_hw_free(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + snd_ali_voice_t *evoice = pvoice ? pvoice->extra : NULL; + + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_ali_free_voice(codec, evoice); + pvoice->extra = NULL; + } + return 0; +} + +static int snd_ali_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ali_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ali_playback_prepare(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + snd_ali_voice_t *evoice = pvoice->extra; + unsigned long flags; + + unsigned int LBA; + unsigned int Delta; + unsigned int ESO; + unsigned int CTRL; + unsigned int GVSEL; + unsigned int PAN; + unsigned int VOL; + unsigned int EC; + + snd_ali_printk("playback_prepare ...\n"); + + spin_lock_irqsave(&codec->reg_lock, flags); + + /* set Delta (rate) value */ + Delta = snd_ali_convert_rate(runtime->rate, 0); + + if ((pvoice->number == ALI_SPDIF_IN_CHANNEL) || + (pvoice->number == ALI_PCM_IN_CHANNEL)) + snd_ali_disable_special_channel(codec, pvoice->number); + else if ((inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<15)) + && (pvoice->number == ALI_SPDIF_OUT_CHANNEL)) { + if (codec->revision == ALI_5451_V02) { + snd_ali_set_spdif_out_rate(codec, runtime->rate); + Delta = 0x1000; + } + } + + /* set Loop Back Address */ + LBA = runtime->dma_addr; + + /* set interrupt count size */ + pvoice->count = runtime->period_size; + + /* set target ESO for channel */ + pvoice->eso = runtime->buffer_size; + + snd_ali_printk("playback_prepare: eso=%xh count=%xh\n",pvoice->eso,pvoice->count); + + /* set ESO to capture first MIDLP interrupt */ + ESO = pvoice->eso -1; + /* set ctrl mode */ + CTRL = snd_ali_control_mode(substream); + + GVSEL = 1; + PAN = 0; + VOL = 0; + EC = 0; + snd_ali_printk("playback_prepare:\n ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL); + snd_ali_write_voice_regs( codec, + pvoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + PAN, + VOL, + CTRL, + EC); + if (evoice != NULL) { + evoice->count = pvoice->count; + evoice->eso = pvoice->count << 1; + ESO = evoice->eso - 1; + snd_ali_write_voice_regs(codec, + evoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + (unsigned int)0x7f, + (unsigned int)0x3ff, + CTRL, + EC); + } + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; +} + + +static int snd_ali_capture_prepare(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + unsigned long flags; + unsigned int LBA; + unsigned int Delta; + unsigned int ESO; + unsigned int CTRL; + unsigned int GVSEL; + unsigned int PAN; + unsigned int VOL; + unsigned int EC; + u8 bValue; + + spin_lock_irqsave(&codec->reg_lock, flags); + + snd_ali_printk("capture_prepare...\n"); + + snd_ali_enable_special_channel(codec,pvoice->number); + + Delta = snd_ali_convert_rate(runtime->rate, 1); + + // Prepare capture intr channel + if (pvoice->number == ALI_SPDIF_IN_CHANNEL) { + + unsigned int rate; + + if (codec->revision != ALI_5451_V02) + return -1; + rate = snd_ali_get_spdif_in_rate(codec); + if (rate == 0) { + snd_printk("ali_capture_preapre: spdif rate detect err!\n"); + rate = 48000; + } + bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL)); + if (bValue & 0x10) { + outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL)); + printk("clear SPDIF parity error flag.\n"); + } + + if (rate != 48000) + Delta = ((rate << 12)/runtime->rate)&0x00ffff; + } + + // set target ESO for channel + pvoice->eso = runtime->buffer_size; + + // set interrupt count size + pvoice->count = runtime->period_size; + + // set Loop Back Address + LBA = runtime->dma_addr; + + // set ESO to capture first MIDLP interrupt + ESO = pvoice->eso - 1; + CTRL = snd_ali_control_mode(substream); + GVSEL = 0; + PAN = 0x00; + VOL = 0x00; + EC = 0; + + snd_ali_write_voice_regs( codec, + pvoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + PAN, + VOL, + CTRL, + EC); + + + spin_unlock_irqrestore(&codec->reg_lock, flags); + + return 0; +} + + +static snd_pcm_uframes_t snd_ali_playback_pointer(snd_pcm_substream_t *substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + unsigned int cso; + unsigned long flags; + + spin_lock_irqsave(&codec->reg_lock, flags); + if (!pvoice->running) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; + } + outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); + cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + spin_unlock_irqrestore(&codec->reg_lock, flags); + snd_ali_printk("playback pointer returned cso=%xh.\n", cso); + + return cso; +} + + +static snd_pcm_uframes_t snd_ali_capture_pointer(snd_pcm_substream_t *substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + unsigned int cso; + unsigned long flags; + + spin_lock_irqsave(&codec->reg_lock, flags); + if (!pvoice->running) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; + } + outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); + cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + spin_unlock_irqrestore(&codec->reg_lock, flags); + + return cso; +} + +static snd_pcm_hardware_t snd_ali_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (256*1024), + period_bytes_min: 64, + period_bytes_max: (256*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * Capture support device description + */ + +static snd_pcm_hardware_t snd_ali_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static void snd_ali_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + unsigned long flags; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + ali_t *codec; + + if (pvoice) { + codec = pvoice->codec; + spin_lock_irqsave(&codec->reg_lock, flags); + snd_ali_free_voice(pvoice->codec, pvoice); + spin_unlock_irqrestore(&codec->reg_lock, flags); + } +} + +static int snd_ali_playback_open(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice; + unsigned long flags = 0; + + spin_lock_irqsave(&codec->reg_lock, flags); + pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0); + if (pvoice == NULL) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return -EAGAIN; + } + pvoice->codec = codec; + spin_unlock_irqrestore(&codec->reg_lock, flags); + + pvoice->substream = substream; + runtime->private_data = pvoice; + runtime->private_free = snd_ali_pcm_free_substream; + + runtime->hw = snd_ali_playback; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + + +static int snd_ali_capture_open(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice; + unsigned long flags; + + spin_lock_irqsave(&codec->reg_lock, flags); + pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 1); + if (pvoice == NULL) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return -EAGAIN; + } + pvoice->codec = codec; + spin_unlock_irqrestore(&codec->reg_lock, flags); + + pvoice->substream = substream; + runtime->private_data = pvoice; + runtime->private_free = snd_ali_pcm_free_substream; + runtime->hw = snd_ali_capture; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + + +static int snd_ali_playback_close(snd_pcm_substream_t * substream) +{ + return 0; +} + +static int snd_ali_capture_close(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + + snd_ali_disable_special_channel(codec,pvoice->number); + + return 0; +} + +static snd_pcm_ops_t snd_ali_playback_ops = { + open: snd_ali_playback_open, + close: snd_ali_playback_close, + ioctl: snd_ali_ioctl, + hw_params: snd_ali_playback_hw_params, + hw_free: snd_ali_playback_hw_free, + prepare: snd_ali_playback_prepare, + trigger: snd_ali_trigger, + pointer: snd_ali_playback_pointer, +}; + +static snd_pcm_ops_t snd_ali_capture_ops = { + open: snd_ali_capture_open, + close: snd_ali_capture_close, + ioctl: snd_ali_ioctl, + hw_params: snd_ali_capture_hw_params, + hw_free: snd_ali_capture_hw_free, + prepare: snd_ali_capture_prepare, + trigger: snd_ali_trigger, + pointer: snd_ali_capture_pointer, +}; + + +static void snd_ali_pcm_free(snd_pcm_t *pcm) +{ + ali_t *codec = snd_magic_cast(ali_t, pcm->private_data, return); + codec->pcm = NULL; +} + +static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) *rpcm = NULL; + err = snd_pcm_new(codec->card, "ALI 5451", device, ALI_CHANNELS, 1, &pcm); + if (err < 0) { + snd_printk("snd_ali_pcm: err called snd_pcm_new.\n"); + return err; + } + pcm->private_data = codec; + pcm->private_free = snd_ali_pcm_free; + pcm->info_flags = 0; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ali_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ali_capture_ops); + + snd_pcm_lib_preallocate_pci_pages_for_all(codec->pci, pcm, 64*1024, 128*1024); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, "ALI 5451"); + codec->pcm = pcm; + if (rpcm) *rpcm = pcm; + return 0; +} + +#define ALI5451_SPDIF(xname, xindex, value) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\ +info: snd_ali5451_spdif_info, get: snd_ali5451_spdif_get, \ +put: snd_ali5451_spdif_put, private_value: value} + +static int snd_ali5451_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ali5451_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ali_t *codec = snd_magic_cast(ali_t, kcontrol->private_data, -ENXIO); + unsigned int enable; + + enable = ucontrol->value.integer.value[0] ? 1 : 0; + + spin_lock_irqsave(&codec->reg_lock, flags); + switch(kcontrol->private_value) { + case 0: + enable = (codec->spdif_mask & 0x02) ? 1 : 0; + break; + case 1: + enable = ((codec->spdif_mask & 0x02) && (codec->spdif_mask & 0x04)) ? 1 : 0; + break; + case 2: + enable = (codec->spdif_mask & 0x01) ? 1 : 0; + break; + default: + break; + } + ucontrol->value.integer.value[0] = enable; + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; +} + +static int snd_ali5451_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ali_t *codec = snd_magic_cast(ali_t, kcontrol->private_data, -ENXIO); + unsigned int change = 0, enable = 0; + + enable = ucontrol->value.integer.value[0] ? 1 : 0; + + spin_lock_irqsave(&codec->reg_lock, flags); + switch (kcontrol->private_value) { + case 0: + change = (codec->spdif_mask & 0x02) ? 1 : 0; + change = change ^ enable; + if (change) { + if (enable) { + codec->spdif_mask |= 0x02; + snd_ali_enable_spdif_out(codec); + } else { + codec->spdif_mask &= ~(0x02); + codec->spdif_mask &= ~(0x04); + snd_ali_disable_spdif_out(codec); + } + } + break; + case 1: + change = (codec->spdif_mask & 0x04) ? 1 : 0; + change = change ^ enable; + if (change && (codec->spdif_mask & 0x02)) { + if (enable) { + codec->spdif_mask |= 0x04; + snd_ali_enable_spdif_chnout(codec); + } else { + codec->spdif_mask &= ~(0x04); + snd_ali_disable_spdif_chnout(codec); + } + } + break; + case 2: + change = (codec->spdif_mask & 0x01) ? 1 : 0; + change = change ^ enable; + if (change) { + if (enable) { + codec->spdif_mask |= 0x01; + snd_ali_enable_spdif_in(codec); + } else { + codec->spdif_mask &= ~(0x01); + snd_ali_disable_spdif_in(codec); + } + } + break; + default: + break; + } + spin_unlock_irqrestore(&codec->reg_lock, flags); + + return change; +} + +static snd_kcontrol_new_t snd_ali5451_mixer_spdif[] __devinit = { + /* spdif aplayback switch */ + ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, 0), + /* spdif out to spdif channel */ + ALI5451_SPDIF("IEC958 Channel Output Switch", 0, 1), + /* spdif in from spdif channel */ + ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2) +}; + +static void snd_ali_mixer_free_ac97(ac97_t *ac97) +{ + ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return); + codec->ac97 = NULL; +} + +static int __devinit snd_ali_mixer(ali_t * codec) +{ + ac97_t ac97; + int err, idx; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ali_codec_write; + ac97.read = snd_ali_codec_read; + ac97.private_data = codec; + ac97.private_free = snd_ali_mixer_free_ac97; + if ((err = snd_ac97_mixer(codec->card, &ac97, &codec->ac97)) < 0) { + snd_printk("ali mixer creating error.\n"); + return err; + } + if (codec->revision == ALI_5451_V02) { + for(idx = 0; idx < 3; idx++) { + err=snd_ctl_add(codec->card, snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec)); + if (err < 0) return err; + } + } + return 0; +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_ali_suspend(struct pci_dev *dev, u32 state) +#else +static void snd_ali_suspend(struct pci_dev *dev) +#endif +{ +#ifndef PCI_OLD_SUSPEND + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return -ENXIO); +#else + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return); +#endif + ali_image_t *im; + unsigned long flags; + int i, j; + + im = chip->image; + if (! im) +#ifndef PCI_OLD_SUSPEND + return -ENXIO; +#else + return; +#endif + + save_flags(flags); + cli(); + + im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT)); + // im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START)); + im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP)); + + // disable all IRQ bits + outl(0, ALI_REG(chip, ALI_MISCINT)); + + for (i = 0; i < ALI_GLOBAL_REGS; i++) { + if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP)) + continue; + im->regs[i] = inl(ALI_REG(chip, i*4)); + } + + for (i = 0; i < ALI_CHANNELS; i++) { + outb(i, ALI_REG(chip, ALI_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0)); + } + + // stop all HW channel + outl(0xffffffff, ALI_REG(chip, ALI_STOP)); + + restore_flags(flags); +#ifndef PCI_OLD_SUSPEND + return 0; +#endif +} + +#ifndef PCI_OLD_SUSPEND +static int snd_ali_resume(struct pci_dev *dev) +#else +static void snd_ali_resume(struct pci_dev *dev) +#endif +{ +#ifndef PCI_OLD_SUSPEND + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return -ENXIO); +#else + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return); +#endif + ali_image_t *im; + unsigned long flags; + int i, j; + + im = chip->image; + if (! im) +#ifndef PCI_OLD_SUSPEND + return -ENXIO; +#else + return; +#endif + + pci_enable_device(chip->pci); + + save_flags(flags); + cli(); + + for (i = 0; i < ALI_CHANNELS; i++) { + outb(i, ALI_REG(chip, ALI_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0)); + } + + for (i = 0; i < ALI_GLOBAL_REGS; i++) { + if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) || (i*4 == ALI_START)) + continue; + outl(im->regs[i], ALI_REG(chip, i*4)); + } + + snd_ac97_resume(chip->ac97); + + // start HW channel + outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START)); + // restore IRQ enable bits + outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT)); + + restore_flags(flags); +#ifndef PCI_OLD_SUSPEND + return 0; +#endif +} +#endif + +static int snd_ali_free(ali_t * codec) +{ + snd_ali_disable_address_interrupt(codec); + synchronize_irq(); + if (codec->irq >=0) + free_irq(codec->irq, (void *)codec); + if (codec->res_port) { + release_resource(codec->res_port); + kfree_nocheck(codec->res_port); + } +#ifdef CONFIG_PM + if (codec->image) + kfree(codec->image); +#endif + snd_magic_kfree(codec); + return 0; +} + +static int snd_ali_chip_init(ali_t *codec) +{ + unsigned int legacy; + unsigned char temp; + struct pci_dev *pci_dev = NULL; + + snd_ali_printk("chip initializing ... \n"); + + if (snd_ali_reset_5451(codec)) { + snd_printk("ali_chip_init: reset 5451 error.\n"); + return -1; + } + + if (codec->revision == ALI_5451_V02) { + pci_dev = codec->pci_m1533; + if (pci_dev == NULL) + return -1; + pci_read_config_byte(pci_dev, 0x59, &temp); + + pci_dev = pci_find_device(0x10b9,0x7101, pci_dev); + if (pci_dev == NULL) + return -1; + pci_read_config_byte(pci_dev,0xb8,&temp); + temp |= 1 << 6; + pci_write_config_byte(pci_dev, 0xB8, temp); + } + + pci_read_config_dword(codec->pci, 0x44, &legacy); + legacy &= 0xff00ff00; + legacy |= 0x000800aa; + pci_write_config_dword(codec->pci, 0x44, legacy); + + outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + outl(0x00000000, ALI_REG(codec, ALI_AINTEN)); + outl(0xffffffff, ALI_REG(codec, ALI_AINT)); + outl(0x00000000, ALI_REG(codec, ALI_VOLUME)); + outb(0x10, ALI_REG(codec, ALI_MPUR2)); + + codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID); + codec->ac97_ext_status = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_STATUS); + if (codec->revision == ALI_5451_V02) { + snd_ali_enable_spdif_out(codec); + codec->spdif_mask = 0x00000002; + } + + snd_ali_printk("chip initialize succeed.\n"); + return 0; + +} + +static int __devinit snd_ali_resources(ali_t *codec) +{ + snd_ali_printk("resouces allocation ...\n"); + if ((codec->res_port = request_region(codec->port, 0x100, "ALI 5451")) == NULL) { + snd_ali_free(codec); + snd_printk("Unalbe to request io ports.\n"); + return -EBUSY; + } + + if (request_irq(codec->pci->irq, snd_ali_card_interrupt, SA_INTERRUPT|SA_SHIRQ, "ALI 5451", (void *)codec)) { + snd_ali_free(codec); + snd_printk("Unable to request irq.\n"); + return -EBUSY; + } + codec->irq = codec->pci->irq; + snd_ali_printk("resouces allocated.\n"); + return 0; +} +static int snd_ali_dev_free(snd_device_t *device) +{ + ali_t *codec=snd_magic_cast(ali_t, device->device_data, return -ENXIO); + snd_ali_free(codec); + return 0; +} + +static int __devinit snd_ali_create(snd_card_t * card, + struct pci_dev *pci, + int pcm_streams, + ali_t ** r_ali) +{ + ali_t *codec; + int i, err; + unsigned short cmdw = 0; + struct pci_dev *pci_dev = NULL; + static snd_device_ops_t ops = { + (snd_dev_free_t *)snd_ali_dev_free, + NULL, + NULL + }; + + *r_ali = NULL; + + snd_ali_printk("creating ...\n"); + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 30 bits */ + if (!pci_dma_supported(pci, 0x3fffffff)) { + snd_printk("architecture does not support 30bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x3fffffff); + + if ((codec = snd_magic_kcalloc(ali_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + + spin_lock_init(&codec->reg_lock); + spin_lock_init(&codec->voice_alloc); + + codec->card = card; + codec->pci = pci; + codec->irq = -1; + codec->port = pci_resource_start(pci, 0); + pci_read_config_byte(pci, PCI_REVISION_ID, &codec->revision); + + if (pcm_streams < 1) + pcm_streams = 1; + if (pcm_streams > 32) + pcm_streams = 32; + + pci_set_master(pci); + pci_read_config_word(pci, PCI_COMMAND, &cmdw); + if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) { + cmdw |= PCI_COMMAND_IO; + pci_write_config_word(pci, PCI_COMMAND, cmdw); + } + pci_set_master(pci); + + if (snd_ali_resources(codec)) { + return -EBUSY; + } + + synchronize_irq(); + + codec->synth.chmap = 0; + codec->synth.chcnt = 0; + codec->spdif_mask = 0; + codec->synth.synthcount = 0; + + if (codec->revision == ALI_5451_V02) + codec->chregs.regs.ac97read = ALI_AC97_WRITE; + else + codec->chregs.regs.ac97read = ALI_AC97_READ; + codec->chregs.regs.ac97write = ALI_AC97_WRITE; + + codec->chregs.regs.start = ALI_START; + codec->chregs.regs.stop = ALI_STOP; + codec->chregs.regs.aint = ALI_AINT; + codec->chregs.regs.ainten = ALI_AINTEN; + + codec->chregs.data.start = 0x00; + codec->chregs.data.stop = 0x00; + codec->chregs.data.aint = 0x00; + codec->chregs.data.ainten = 0x00; + + pci_dev = pci_find_device(0x10b9,0x1533, pci_dev); + codec->pci_m1533 = pci_dev; + pci_dev = pci_find_device(0x10b9,0x7101, pci_dev); + codec->pci_m7101 = pci_dev; + + snd_ali_printk("snd_device_new is called.\n"); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) { + snd_ali_free(codec); + return err; + } + + /* initialise synth voices*/ + for (i = 0; i < ALI_CHANNELS; i++ ) { + codec->synth.voices[i].number = i; + } + + if ((err = snd_ali_chip_init(codec)) < 0) { + snd_printk("ali create: chip init error.\n"); + snd_ali_free(codec); + return err; + } + +#ifdef CONFIG_PM + codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL); + if (! codec->image) + snd_printk("can't allocate apm buffer\n"); +#endif + + snd_ali_enable_address_interrupt(codec); + + *r_ali = codec; + snd_ali_printk("created.\n"); + return 0; +} + +static int __devinit snd_ali_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + ali_t *codec; + int err; + + snd_ali_printk("probe ...\n"); + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ali_create(card, pci, snd_pcm_channels[dev], &codec)) < 0) { + snd_card_free(card); + return err; + } + + snd_ali_printk("mixer building ...\n"); + if ((err = snd_ali_mixer(codec)) < 0) { + snd_card_free(card); + return err; + } + + snd_ali_printk("pcm building ...\n"); + if ((err = snd_ali_pcm(codec, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "ALI5451"); + strcpy(card->shortname, "ALI 5451"); + + sprintf(card->longname, "%s at 0x%lx, irq %li", + card->shortname, codec->port, codec->irq); + + snd_ali_printk("register card.\n"); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, codec); + dev++; + return 0; +} + +static void __devexit snd_ali_remove(struct pci_dev *pci) +{ + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "ALI 5451", + id_table: snd_ali_ids, + probe: snd_ali_probe, + remove: __devexit_p(snd_ali_remove), +#ifdef CONFIG_PM + suspend: snd_ali_suspend, + resume: snd_ali_resume, +#endif +}; + +static int __init alsa_card_ali_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("ALi pci audio not found or device busy.\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ali_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ali_init) +module_exit(alsa_card_ali_exit) + +#ifndef MODULE + +/* format is: snd-ali5451=snd_enable,snd_index,snd_id,snd_pcm_channels */ + +static int __init alsa_card_ali_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_pcm_channels[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ali5451=", alsa_card_ali_setup); + +#endif /* ifndef */ diff -Nru a/sound/pci/als4000.c b/sound/pci/als4000.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/als4000.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,729 @@ +/* + * card-als4000.c - driver for Avance Logic ALS4000 based soundcards. + * Copyright (C) 2000 by Bart Hartgers , + * Jaroslav Kysela + * + * Framework borrowed from Massimo Piccioni's card-als100.c. + * + * NOTES + * + * Since Avance does not provide any meaningful documentation, and I + * bought an ALS4000 based soundcard, I was forced to base this driver + * on reverse engineering. + * + * The ALS4000 seems to be the PCI-cousin of the ALS100. It contains an + * ALS100-like SB DSP/mixer, an OPL3 synth, a MPU401 and a gameport + * interface. These subsystems can be mapped into ISA io-port space, + * using the PCI-interface. In addition, the PCI-bit provides DMA and IRQ + * services to the subsystems. + * + * While ALS4000 is very similar to a SoundBlaster, the differences in + * DMA and capturing require more changes to the SoundBlaster than + * desirable, so I made this separate driver. + * + * The ALS4000 can do real full duplex playback/capture. + * + * BUGS + * The box suggests there is some support for 3D sound, but I did not + * investigate this yet. + * + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Bart Hartgers "); +MODULE_DESCRIPTION("Avance Logic ALS4000"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Avance Logic,ALS4000}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_joystick_port[SNDRV_CARDS] = +#ifdef CONFIG_ISA + {0x200}; /* enable as default */ +#else + {0}; /* disabled */ +#endif + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ALS4000 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ALS4000 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ALS4000 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_INDEX_DESC); +MODULE_PARM(snd_joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)"); +MODULE_PARM_SYNTAX(snd_joystick_port, SNDRV_ENABLED); + +#define chip_t sb_t + +typedef struct { + unsigned long gcr; +} snd_card_als4000_t; + +static struct pci_device_id snd_als4000_ids[] __devinitdata = { + { 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ALS4000 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_als4000_ids); + +static inline void snd_als4000_gcr_write_addr(unsigned long port, u32 reg, u32 val) +{ + outb(reg, port+0x0c); + outl(val, port+0x08); +} + +static inline void snd_als4000_gcr_write(sb_t *sb, u32 reg, u32 val) +{ + snd_als4000_gcr_write_addr(sb->alt_port, reg, val); +} + +static inline u32 snd_als4000_gcr_read_addr(unsigned long port, u32 reg) +{ + outb(reg, port+0x0c); + return inl(port+0x08); +} + +static inline u32 snd_als4000_gcr_read(sb_t *sb, u32 reg) +{ + return snd_als4000_gcr_read_addr(sb->alt_port, reg); +} + +static void snd_als4000_set_rate(sb_t *chip, unsigned int rate) +{ + if (!(chip->mode & SB_RATE_LOCK)) { + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT); + snd_sbdsp_command(chip, rate>>8); + snd_sbdsp_command(chip, rate); + } +} + +static void snd_als4000_set_capture_dma(sb_t *chip, dma_addr_t addr, unsigned size) +{ + snd_als4000_gcr_write(chip, 0xa2, addr); + snd_als4000_gcr_write(chip, 0xa3, (size-1)); +} + +static void snd_als4000_set_playback_dma(sb_t *chip, dma_addr_t addr, unsigned size) +{ + snd_als4000_gcr_write(chip, 0x91, addr); + snd_als4000_gcr_write(chip, 0x92, (size-1)|0x180000); +} + +#define ALS4000_FORMAT_SIGNED (1<<0) +#define ALS4000_FORMAT_16BIT (1<<1) +#define ALS4000_FORMAT_STEREO (1<<2) + +static int snd_als4000_get_format(snd_pcm_runtime_t *runtime) +{ + int result; + + result = 0; + if (snd_pcm_format_signed(runtime->format)) + result |= ALS4000_FORMAT_SIGNED; + if (snd_pcm_format_physical_width(runtime->format) == 16) + result |= ALS4000_FORMAT_16BIT; + if (runtime->channels > 1) + result |= ALS4000_FORMAT_STEREO; + return result; +} + +/* structure for setting up playback */ +static struct { + unsigned char dsp_cmd, dma_on, dma_off, format; +} playback_cmd_vals[]={ +/* ALS4000_FORMAT_U8_MONO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_MONO }, +/* ALS4000_FORMAT_S8_MONO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_MONO }, +/* ALS4000_FORMAT_U16L_MONO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_MONO }, +/* ALS4000_FORMAT_S16L_MONO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_MONO }, +/* ALS4000_FORMAT_U8_STEREO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_STEREO }, +/* ALS4000_FORMAT_S8_STEREO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_STEREO }, +/* ALS4000_FORMAT_U16L_STEREO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_STEREO }, +/* ALS4000_FORMAT_S16L_STEREO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_STEREO }, +}; +#define playback_cmd(chip) (playback_cmd_vals[(chip)->playback_format]) + +/* structure for setting up capture */ +enum { CMD_WIDTH8=0x04, CMD_SIGNED=0x10, CMD_MONO=0x80, CMD_STEREO=0xA0 }; +static unsigned char capture_cmd_vals[]= +{ +CMD_WIDTH8|CMD_MONO, /* ALS4000_FORMAT_U8_MONO */ +CMD_WIDTH8|CMD_SIGNED|CMD_MONO, /* ALS4000_FORMAT_S8_MONO */ +CMD_MONO, /* ALS4000_FORMAT_U16L_MONO */ +CMD_SIGNED|CMD_MONO, /* ALS4000_FORMAT_S16L_MONO */ +CMD_WIDTH8|CMD_STEREO, /* ALS4000_FORMAT_U8_STEREO */ +CMD_WIDTH8|CMD_SIGNED|CMD_STEREO, /* ALS4000_FORMAT_S8_STEREO */ +CMD_STEREO, /* ALS4000_FORMAT_U16L_STEREO */ +CMD_SIGNED|CMD_STEREO, /* ALS4000_FORMAT_S16L_STEREO */ +}; +#define capture_cmd(chip) (capture_cmd_vals[(chip)->capture_format]) + +static int snd_als4000_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_als4000_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_als4000_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long size; + unsigned count; + + chip->capture_format = snd_als4000_get_format(runtime); + + size = snd_pcm_lib_buffer_bytes(substream); + count = snd_pcm_lib_period_bytes(substream); + + if (chip->capture_format & ALS4000_FORMAT_16BIT) + count >>=1; + count--; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_als4000_set_rate(chip, runtime->rate); + snd_als4000_set_capture_dma(chip, runtime->dma_addr, size); + spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_lock_irqsave(&chip->mixer_lock, flags ); + snd_sbmixer_write(chip, 0xdc, count); + snd_sbmixer_write(chip, 0xdd, count>>8); + spin_unlock_irqrestore(&chip->mixer_lock, flags ); + return 0; +} + +static int snd_als4000_playback_prepare(snd_pcm_substream_t *substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long size; + unsigned count; + + chip->playback_format = snd_als4000_get_format(runtime); + + size = snd_pcm_lib_buffer_bytes(substream); + count = snd_pcm_lib_period_bytes(substream); + + if (chip->playback_format & ALS4000_FORMAT_16BIT) + count >>=1; + count--; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_als4000_set_rate(chip, runtime->rate); + snd_als4000_set_playback_dma(chip, runtime->dma_addr, size); + + snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); + snd_sbdsp_command(chip, playback_cmd(chip).dsp_cmd); + snd_sbdsp_command(chip, playback_cmd(chip).format); + snd_sbdsp_command(chip, count); + snd_sbdsp_command(chip, count>>8); + snd_sbdsp_command(chip, playback_cmd(chip).dma_off); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + +static int snd_als4000_capture_trigger(snd_pcm_substream_t * substream, int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock_irqsave(&chip->mixer_lock, flags); + if (cmd == SNDRV_PCM_TRIGGER_START) { + chip->mode |= SB_RATE_LOCK_CAPTURE; + snd_sbmixer_write(chip, 0xde, capture_cmd(chip)); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + chip->mode &= ~SB_RATE_LOCK_CAPTURE; + snd_sbmixer_write(chip, 0xde, 0); + } else { + result = -EINVAL; + } + spin_unlock_irqrestore(&chip->mixer_lock, flags); + return result; +} + +static int snd_als4000_playback_trigger(snd_pcm_substream_t * substream, int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (cmd == SNDRV_PCM_TRIGGER_START) { + chip->mode |= SB_RATE_LOCK_PLAYBACK; + snd_sbdsp_command(chip, playback_cmd(chip).dma_on); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + snd_sbdsp_command(chip, playback_cmd(chip).dma_off); + chip->mode &= ~SB_RATE_LOCK_PLAYBACK; + } else { + result = -EINVAL; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +static snd_pcm_uframes_t snd_als4000_capture_pointer(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int result; + + spin_lock_irqsave(&chip->reg_lock, flags); + result = snd_als4000_gcr_read(chip, 0xa4) & 0xffff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames( substream->runtime, result ); +} + +static snd_pcm_uframes_t snd_als4000_playback_pointer(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned result; + + spin_lock_irqsave(&chip->reg_lock, flags); + result = snd_als4000_gcr_read(chip, 0xa0) & 0xffff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames( substream->runtime, result ); +} + +static void snd_als4000_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + unsigned long flags; + unsigned gcr_status; + unsigned sb_status; + + /* find out which bit of the ALS4000 produced the interrupt */ + gcr_status = inb(chip->alt_port + 0xe); + + if ((gcr_status & 0x80) && (chip->playback_substream)) /* playback */ + snd_pcm_period_elapsed(chip->playback_substream); + if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */ + snd_pcm_period_elapsed(chip->capture_substream); + if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interupt */ + snd_mpu401_uart_interrupt(irq, chip->rmidi, regs); + /* release the gcr */ + outb(gcr_status, chip->alt_port + 0xe); + + spin_lock_irqsave(&chip->mixer_lock, flags); + sb_status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + if (sb_status & SB_IRQTYPE_8BIT) + inb(SBP(chip, DATA_AVAIL)); + if (sb_status & SB_IRQTYPE_16BIT) + inb(SBP(chip, DATA_AVAIL_16)); + if (sb_status & SB_IRQTYPE_MPUIN) + inb(chip->mpu_port); + if (sb_status & 0x20) + inb(SBP(chip, RESET)); +} + +/*****************************************************************/ + +static snd_pcm_hardware_t snd_als4000_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE, /* formats */ + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0 +}; + +static snd_pcm_hardware_t snd_als4000_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE, /* formats */ + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0 +}; + +/*****************************************************************/ + +static int snd_als4000_playback_open(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->playback_substream = substream; + runtime->hw = snd_als4000_playback; + return 0; +} + +static int snd_als4000_playback_close(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_als4000_capture_open(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture_substream = substream; + runtime->hw = snd_als4000_capture; + return 0; +} + +static int snd_als4000_capture_close(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_pcm_lib_free_pages(substream); + return 0; +} + +/******************************************************************/ + +static snd_pcm_ops_t snd_als4000_playback_ops = { + open: snd_als4000_playback_open, + close: snd_als4000_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_als4000_hw_params, + hw_free: snd_als4000_hw_free, + prepare: snd_als4000_playback_prepare, + trigger: snd_als4000_playback_trigger, + pointer: snd_als4000_playback_pointer +}; + +static snd_pcm_ops_t snd_als4000_capture_ops = { + open: snd_als4000_capture_open, + close: snd_als4000_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_als4000_hw_params, + hw_free: snd_als4000_hw_free, + prepare: snd_als4000_capture_prepare, + trigger: snd_als4000_capture_trigger, + pointer: snd_als4000_capture_pointer +}; + +static void snd_als4000_pcm_free(snd_pcm_t *pcm) +{ + sb_t *chip = snd_magic_cast(sb_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_als4000_pcm(sb_t *chip, int device) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm)) < 0) + return err; + pcm->private_free = snd_als4000_pcm_free; + pcm->private_data = chip; + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops); + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + + chip->pcm = pcm; + + return 0; +} + +/******************************************************************/ + +static void __devinit snd_als4000_set_addr(unsigned long gcr, + unsigned int sb, + unsigned int mpu, + unsigned int opl, + unsigned int game) +{ + u32 confA = 0; + u32 confB = 0; + + if (mpu > 0) + confB |= (mpu | 1) << 16; + if (sb > 0) + confB |= (sb | 1); + if (game > 0) + confA |= (game | 1) << 16; + if (opl > 0) + confA |= (opl | 1); + snd_als4000_gcr_write_addr(gcr, 0xa8, confA); + snd_als4000_gcr_write_addr(gcr, 0xa9, confB); +} + +static void __devinit snd_als4000_configure(sb_t *chip) +{ + unsigned long flags; + unsigned tmp; + int i; + + /* do some more configuration */ + spin_lock_irqsave(&chip->mixer_lock, flags); + tmp = snd_sbmixer_read(chip, 0xc0); + snd_sbmixer_write(chip, 0xc0, tmp|0x80); + /* always select DMA channel 0, since we do not actually use DMA */ + snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0); + snd_sbmixer_write(chip, 0xc0, tmp&0x7f); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + spin_lock_irqsave(&chip->reg_lock,flags); + /* magic number. Enables interrupts(?) */ + snd_als4000_gcr_write(chip, 0x8c, 0x28000); + for(i = 0x91; i <= 0x96; ++i) + snd_als4000_gcr_write(chip, i, 0); + + snd_als4000_gcr_write(chip, 0x99, snd_als4000_gcr_read(chip, 0x99)); + spin_unlock_irqrestore(&chip->reg_lock,flags); +} + +static void snd_card_als4k_free( snd_card_t *card ) +{ + snd_card_als4000_t * acard = (snd_card_als4000_t *)card->private_data; + /* make sure that interrupts are disabled */ + snd_als4000_gcr_write_addr( acard->gcr, 0x8c, 0); +} + +static int __devinit snd_card_als4k_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + snd_card_als4000_t *acard; + unsigned long gcr; + struct resource *res_gcr_port; + sb_t *chip; + opl3_t *opl3; + unsigned short word; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) { + return err; + } + /* check, if we can restrict PCI DMA transfers to 24 bits */ + if (!pci_dma_supported(pci, 0x00ffffff)) { + snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x00ffffff); + + gcr = pci_resource_start(pci, 0); + if ((res_gcr_port = request_region(gcr, 0x40, "ALS4000")) == NULL) { + snd_printk("unable to grab region 0x%lx-0x%lx\n", gcr, gcr + 0x40 - 1); + return -EBUSY; + } + + pci_read_config_word(pci, PCI_COMMAND, &word); + pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO); + pci_set_master(pci); + + /* disable all legacy ISA stuff except for joystick */ + snd_als4000_set_addr(gcr, 0, 0, 0, snd_joystick_port[dev]); + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof( snd_card_als4000_t ) ); + if (card == NULL) { + release_resource(res_gcr_port); + kfree_nocheck(res_gcr_port); + return -ENOMEM; + } + + acard = (snd_card_als4000_t *)card->private_data; + acard->gcr = gcr; + card->private_free = snd_card_als4k_free; + + if ((err = snd_sbdsp_create(card, + gcr + 0x10, + pci->irq, + snd_als4000_interrupt, + -1, + -1, + SB_HW_ALS4000, + &chip)) < 0) { + release_resource(res_gcr_port); + kfree_nocheck(res_gcr_port); + snd_card_free(card); + return err; + } + + chip->pci = pci; + chip->alt_port = gcr; + chip->res_alt_port = res_gcr_port; + + snd_als4000_configure(chip); + + if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000, + gcr+0x30, 1, pci->irq, 0, + &chip->rmidi)) < 0) { + snd_card_free(card); + snd_printk("no MPU-401device at 0x%lx ?\n", gcr+0x30); + return err; + } + + if ((err = snd_als4000_pcm(chip, 0)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_opl3_create(card, gcr+0x10, gcr+0x12, + OPL3_HW_AUTO, 1, &opl3) < 0) { + snd_printk("no OPL device at 0x%lx-0x%lx ?\n", + gcr+0x10, gcr+0x12 ); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + strcpy(card->driver, "ALS4000"); + strcpy(card->shortname, "Avance Logic ALS4000"); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->alt_port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_card_als4k_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "ALS4000", + id_table: snd_als4000_ids, + probe: snd_card_als4k_probe, + remove: __devexit_p(snd_card_als4k_remove), +}; + +static int __init alsa_card_als4k_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("no ALS4000 based soundcards found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_als4k_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_als4k_init) +module_exit(alsa_card_als4k_exit) + +#ifndef MODULE + +/* format is: snd-als4000=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_als4000_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-als4000=", alsa_card_als4000_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/cmipci.c b/sound/pci/cmipci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/cmipci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2751 @@ +/* + * Driver for C-Media CMI8338 and 8738 PCI soundcards. + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("C-Media CMI8x38 PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{C-Media,CMI8738}," + "{C-Media,CMI8738B}," + "{C-Media,CMI8338A}," + "{C-Media,CMI8338B}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ +static long snd_mpu_port[SNDRV_CARDS] = {0x330, [1 ... (SNDRV_CARDS-1)]=-1}; +static long snd_fm_port[SNDRV_CARDS] = {0x388, [1 ... (SNDRV_CARDS-1)]=-1}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for C-Media PCI soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for C-Media PCI soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable C-Media PCI soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{-1},{0x330},{0x320},{0x310},{0x300}},dialog:list"); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{-1},{0x388},{0x3c8},{0x3e0},{0x3e8}},dialog:list"); + +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738 +#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 +#endif + +/* + * CM8x38 registers definition + */ + +#define CM_REG_FUNCTRL0 0x00 +#define CM_RST_CH1 0x00080000 +#define CM_RST_CH0 0x00040000 +#define CM_CHEN1 0x00020000 /* ch1: enable */ +#define CM_CHEN0 0x00010000 /* ch0: enable */ +#define CM_PAUSE1 0x00000008 /* ch1: pause */ +#define CM_PAUSE0 0x00000004 /* ch0: pause */ +#define CM_CHADC1 0x00000002 /* ch1, 0:playback, 1:record */ +#define CM_CHADC0 0x00000001 /* ch0, 0:playback, 1:record */ + +#define CM_REG_FUNCTRL1 0x04 +#define CM_ASFC_MASK 0x0000E000 /* ADC sampling frequency */ +#define CM_ASFC_SHIFT 13 +#define CM_DSFC_MASK 0x00001C00 /* DAC sampling frequency */ +#define CM_DSFC_SHIFT 10 +#define CM_SPDF_1 0x00000200 /* SPDIF IN/OUT at channel B */ +#define CM_SPDF_0 0x00000100 /* SPDIF OUT only channel A */ +#define CM_SPDFLOOP 0x00000080 /* ext. SPDIIF/OUT -> IN loopback */ +#define CM_SPDO2DAC 0x00000040 /* SPDIF/OUT can be heard from internal DAC */ +#define CM_INTRM 0x00000020 /* master control block (MCB) interrupt enabled */ +#define CM_BREQ 0x00000010 /* bus master enabled */ +#define CM_VOICE_EN 0x00000008 /* legacy voice (SB16,FM) */ +#define CM_UART_EN 0x00000004 /* UART */ +#define CM_JYSTK_EN 0x00000002 /* joy stick */ + +#define CM_REG_CHFORMAT 0x08 + +#define CM_CHB3D5C 0x80000000 /* 5 channels */ +#define CM_CHB3D 0x20000000 /* 4,5,6 channels */ + +#define CM_CHIP_MASK1 0x1f000000 +#define CM_CHIP_037 0x01000000 + +#define CM_AC3EN1 0x00100000 /* enable AC3: model 037 */ +#define CM_SPD24SEL 0x00020000 /* 24bit spdif: model 037 */ + +#define CM_ADCBITLEN_MASK 0x0000C000 +#define CM_ADCBITLEN_16 0x00000000 +#define CM_ADCBITLEN_15 0x00004000 +#define CM_ADCBITLEN_14 0x00008000 +#define CM_ADCBITLEN_13 0x0000C000 + +#define CM_ADCDACLEN_MASK 0x00003000 +#define CM_ADCDACLEN_060 0x00000000 +#define CM_ADCDACLEN_066 0x00001000 +#define CM_ADCDACLEN_130 0x00002000 +#define CM_ADCDACLEN_280 0x00003000 + +#define CM_CH1_SRATE_176K 0x00000800 +#define CM_CH1_SRATE_88K 0x00000400 +#define CM_CH0_SRATE_176K 0x00000200 +#define CM_CH0_SRATE_88K 0x00000100 + +#define CM_CH1FMT_MASK 0x0000000C +#define CM_CH1FMT_SHIFT 2 +#define CM_CH0FMT_MASK 0x00000003 +#define CM_CH0FMT_SHIFT 0 + +#define CM_REG_INT_HLDCLR 0x0C +#define CM_CHIP_MASK2 0xff000000 +#define CM_CHIP_039 0x04000000 +#define CM_CHIP_039_6CH 0x01000000 +#define CM_TDMA_INT_EN 0x00040000 +#define CM_CH1_INT_EN 0x00020000 +#define CM_CH0_INT_EN 0x00010000 +#define CM_INT_HOLD 0x00000002 +#define CM_INT_CLEAR 0x00000001 + +#define CM_REG_INT_STATUS 0x10 +#define CM_INTR 0x80000000 +#define CM_UARTINT 0x00010000 +#define CM_LTDMAINT 0x00008000 +#define CM_HTDMAINT 0x00004000 +#define CM_CH1BUSY 0x00000008 +#define CM_CH0BUSY 0x00000004 +#define CM_CHINT1 0x00000002 +#define CM_CHINT0 0x00000001 + +#define CM_REG_LEGACY_CTRL 0x14 +#define CM_NXCHG 0x80000000 /* h/w multi channels? */ +#define CM_VMPU_MASK 0x60000000 /* MPU401 i/o port address */ +#define CM_VMPU_330 0x00000000 +#define CM_VMPU_320 0x20000000 +#define CM_VMPU_310 0x40000000 +#define CM_VMPU_300 0x60000000 +#define CM_VSBSEL_MASK 0x0C000000 /* SB16 base address */ +#define CM_VSBSEL_220 0x00000000 +#define CM_VSBSEL_240 0x04000000 +#define CM_VSBSEL_260 0x08000000 +#define CM_VSBSEL_280 0x0C000000 +#define CM_FMSEL_MASK 0x03000000 /* FM OPL3 base address */ +#define CM_FMSEL_388 0x00000000 +#define CM_FMSEL_3C8 0x01000000 +#define CM_FMSEL_3E0 0x02000000 +#define CM_FMSEL_3E8 0x03000000 +#define CM_ENSPDOUT 0x00800000 /* enable XPDIF/OUT to I/O interface */ +#define CM_SPDCOPYRHT 0x00400000 /* set copyright spdif in/out */ +#define CM_DAC2SPDO 0x00200000 /* enable wave+fm_midi -> SPDIF/OUT */ +#define CM_SETRETRY 0x00010000 /* 0: legacy i/o wait (default), 1: legacy i/o bus retry */ +#define CM_CHB3D6C 0x00008000 /* 5.1 channels support */ +#define CM_LINE_AS_BASS 0x00006000 /* use line-in as bass */ + +#define CM_REG_MISC_CTRL 0x18 +#define CM_PWD 0x80000000 +#define CM_RESET 0x40000000 +#define CM_SFIL_MASK 0x30000000 +#define CM_TXVX 0x08000000 +#define CM_N4SPK3D 0x04000000 /* 4ch output */ +#define CM_SPDO5V 0x02000000 /* 5V spdif output */ +#define CM_SPDIF48K 0x01000000 /* write */ +#define CM_SPATUS48K 0x01000000 /* read */ +#define CM_ENDBDAC 0x00800000 /* enable dual dac */ +#define CM_XCHGDAC 0x00400000 /* 0: front=ch0, 1: front=ch1 */ +#define CM_SPD32SEL 0x00200000 /* 0: 16bit SPDIF, 1: 32bit */ +#define CM_SPDFLOOPI 0x00100000 /* int. SPDIF-IN -> int. OUT */ +#define CM_FM_EN 0x00080000 /* enalbe FM */ +#define CM_AC3EN2 0x00040000 /* enable AC3: model 039 */ +#define CM_VIDWPDSB 0x00010000 +#define CM_SPDF_AC97 0x00008000 /* 0: SPDIF/OUT 44.1K, 1: 48K */ +#define CM_MASK_EN 0x00004000 +#define CM_VIDWPPRT 0x00002000 +#define CM_SFILENB 0x00001000 +#define CM_MMODE_MASK 0x00000E00 +#define CM_ENCENTER 0x00000080 /* shared with FLINKON? */ +#define CM_FLINKON 0x00000080 +#define CM_FLINKOFF 0x00000040 +#define CM_MIDSMP 0x00000010 +#define CM_UPDDMA_MASK 0x0000000C +#define CM_TWAIT_MASK 0x00000003 + + /* byte */ +#define CM_REG_MIXER0 0x20 + +#define CM_REG_SB16_DATA 0x22 +#define CM_REG_SB16_ADDR 0x23 + +#define CM_REG_MIXER1 0x24 +#define CM_FMMUTE 0x80 /* mute FM */ +#define CM_FMMUTE_SHIFT 7 +#define CM_WSMUTE 0x40 /* mute PCM */ +#define CM_WSMUTE_SHIFT 6 +#define CM_SPK4 0x20 /* lin-in -> rear line out */ +#define CM_SPK4_SHIFT 5 +#define CM_REAR2FRONT 0x10 /* exchange rear/front */ +#define CM_REAR2FRONT_SHIFT 4 +#define CM_WAVEINL 0x08 /* digital wave rec. left chan */ +#define CM_WAVEINL_SHIFT 3 +#define CM_WAVEINR 0x04 /* digical wave rec. right */ +#define CM_WAVEINR_SHIFT 2 +#define CM_X3DEN 0x02 /* 3D surround enable */ +#define CM_X3DEN_SHIFT 1 +#define CM_CDPLAY 0x01 /* enable SPDIF/IN PCM -> DAC */ +#define CM_CDPLAY_SHIFT 0 + +#define CM_REG_MIXER2 0x25 +#define CM_RAUXREN 0x80 /* AUX right capture */ +#define CM_RAUXREN_SHIFT 7 +#define CM_RAUXLEN 0x40 /* AUX left capture */ +#define CM_RAUXLEN_SHIFT 6 +#define CM_VAUXRM 0x20 /* AUX right mute */ +#define CM_VAUXRM_SHIFT 5 +#define CM_VAUXLM 0x10 /* AUX left mute */ +#define CM_VAUXLM_SHIFT 4 +#define CM_VADMIC_MASK 0x0e /* mic gain level (0-3) << 1 */ +#define CM_VADMIC_SHIFT 1 +#define CM_MICGAINZ 0x01 /* mic boost */ +#define CM_MICGAINZ_SHIFT 0 + +#define CM_REG_AUX_VOL 0x26 +#define CM_VAUXL_MASK 0xf0 +#define CM_VAUXR_MASK 0x0f + +#define CM_REG_MISC 0x27 +#define CM_XGPO1 0x20 +#define CM_XGPBIO 0x04 +#define CM_SPDVALID 0x02 /* spdif input valid check */ +#define CM_DMAUTO 0x01 + +#define CM_REG_AC97 0x28 /* hmmm.. do we have ac97 link? */ + +/* + * extended registers + */ +#define CM_REG_CH0_FRAME1 0x80 /* base address */ +#define CM_REG_CH0_FRAME2 0x84 +#define CM_REG_CH1_FRAME1 0x88 /* 0-15: count of samples at bus master; buffer size */ +#define CM_REG_CH1_FRAME2 0x8C /* 16-31: count of samples at codec; fragment size */ + +/* + * size of i/o region + */ +#define CM_EXTENT_CODEC 0x100 +#define CM_EXTENT_MIDI 0x2 +#define CM_EXTENT_SYNTH 0x4 + +/* + * pci ids + */ +#ifndef PCI_VENDOR_ID_CMEDIA +#define PCI_VENDOR_ID_CMEDIA 0x13F6 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A +#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B +#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738 +#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 +#endif + +/* + * channels for playback / capture + */ +#define CM_CH_PLAY 0 +#define CM_CH_CAPT 1 + +/* + * flags to check device open/close + */ +#define CM_OPEN_NONE 0 +#define CM_OPEN_CH_MASK 0x01 +#define CM_OPEN_DAC 0x10 +#define CM_OPEN_ADC 0x20 +#define CM_OPEN_SPDIF 0x40 +#define CM_OPEN_PLAYBACK (CM_CH_PLAY | CM_OPEN_DAC) +#define CM_OPEN_PLAYBACK2 (CM_CH_CAPT | CM_OPEN_DAC) +#define CM_OPEN_CAPTURE (CM_CH_CAPT | CM_OPEN_ADC) +#define CM_OPEN_SPDIF_PLAYBACK (CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_SPDIF) +#define CM_OPEN_SPDIF_CAPTURE (CM_CH_CAPT | CM_OPEN_ADC | CM_OPEN_SPDIF) + + +#if CM_CH_PLAY == 1 +#define CM_PLAYBACK_SRATE_176K CM_CH1_SRATE_176K +#define CM_PLAYBACK_SPDF CM_SPDF_1 +#define CM_CAPTURE_SPDF CM_SPDF_0 +#else +#define CM_PLAYBACK_SRATE_176K CM_CH0_SRATE_176K +#define CM_PLAYBACK_SPDF CM_SPDF_0 +#define CM_CAPTURE_SPDF CM_SPDF_1 +#endif + + +/* + * define this if you want soft ac3 encoding - it's still buggy.. + */ +/* #define DO_SOFT_AC3 */ + + +/* + * driver data + */ + +typedef struct snd_stru_cmipci cmipci_t; +typedef struct snd_stru_cmipci_pcm cmipci_pcm_t; + +#define chip_t cmipci_t + +struct snd_stru_cmipci_pcm { + snd_pcm_substream_t *substream; + int running; /* dac/adc running? */ + unsigned int dma_size; /* in frames */ + unsigned int period_size; /* in frames */ + unsigned int offset; /* physical address of the buffer */ + unsigned int fmt; /* format bits */ + int ch; /* channel (0/1) */ + int is_dac; /* is dac? */ + int bytes_per_frame; + int shift; + int ac3_shift; /* extra shift: 1 on soft ac3 mode */ +}; + +/* mixer elements toggled/resumed during ac3 playback */ +struct cmipci_mixer_auto_switches { + const char *name; /* switch to toggle */ + int toggle_on; /* value to change when ac3 mode */ +}; +static const struct cmipci_mixer_auto_switches cm_saved_mixer[] = { + {"PCM Playback Switch", 0}, + {"IEC958 Output Switch", 1}, + {"IEC958 Mix Analog", 0}, + // {"IEC958 Out To DAC", 1}, // no longer used + {"IEC958 Loop", 0}, +}; +#define CM_SAVED_MIXERS (sizeof(cm_saved_mixer)/sizeof(cm_saved_mixer[0])) + +struct snd_stru_cmipci { + snd_card_t *card; + + struct pci_dev *pci; + unsigned int device; /* device ID */ + int irq; + + unsigned long iobase; + struct resource *res_iobase; + unsigned int ctrl; /* FUNCTRL0 current value */ + + snd_pcm_t *pcm; /* DAC/ADC PCM */ + snd_pcm_t *pcm2; /* 2nd DAC */ + snd_pcm_t *pcm_spdif; /* SPDIF */ + + int chip_version; + int max_channels; + unsigned int has_dual_dac: 1; + unsigned int can_ac3_sw: 1; + unsigned int can_ac3_hw: 1; + unsigned int can_multi_ch: 1; + int spdif_counter; /* for software AC3 */ + + unsigned int dig_status; + unsigned int dig_pcm_status; + snd_kcontrol_t *spdif_pcm_ctl; + + snd_pcm_hardware_t *hw_info[3]; /* for playbacks */ + + int opened[2]; /* open mode */ + struct semaphore open_mutex; + + int mixer_insensitive: 1; + snd_kcontrol_t *mixer_res_ctl[CM_SAVED_MIXERS]; + int mixer_res_status[CM_SAVED_MIXERS]; + + opl3_t *opl3; + snd_hwdep_t *opl3hwdep; + + cmipci_pcm_t channel[2]; /* ch0 - DAC, ch1 - ADC or 2nd DAC */ + + /* external MIDI */ + snd_rawmidi_t *rmidi; + + spinlock_t reg_lock; + snd_info_entry_t *proc_entry; +}; + + +/* read/write operations for dword register */ +inline static void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data) +{ + outl(data, cm->iobase + cmd); +} +inline static unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd) +{ + return inl(cm->iobase + cmd); +} + +/* read/write operations for word register */ +inline static void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data) +{ + outw(data, cm->iobase + cmd); +} +inline static unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd) +{ + return inw(cm->iobase + cmd); +} + +/* read/write operations for byte register */ +inline static void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data) +{ + outb(data, cm->iobase + cmd); +} + +inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd) +{ + return inb(cm->iobase + cmd); +} + +/* bit operations for dword register */ +static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag) +{ + unsigned int val; + val = inl(cm->iobase + cmd); + val |= flag; + outl(val, cm->iobase + cmd); +} + +static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag) +{ + unsigned int val; + val = inl(cm->iobase + cmd); + val &= ~flag; + outl(val, cm->iobase + cmd); +} + +#if 0 // not used +/* bit operations for byte register */ +static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag) +{ + unsigned char val; + val = inb(cm->iobase + cmd); + val |= flag; + outb(val, cm->iobase + cmd); +} + +static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag) +{ + unsigned char val; + val = inb(cm->iobase + cmd); + val &= ~flag; + outb(val, cm->iobase + cmd); +} +#endif + + +/* + * PCM interface + */ + +/* + * calculate frequency + */ + +static int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 }; +#define RATES (sizeof(rates) / sizeof(rates[0])) + +static unsigned int snd_cmipci_rate_freq(unsigned int rate) +{ + int i; + for (i = 0; i < RATES; i++) { + if (rates[i] == rate) + return i; + } + snd_BUG(); + return 0; +} + +static int snd_cmipci_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_cmipci_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + + +/* + */ + +static unsigned int hw_channels[] = {1, 2, 4, 5, 6}; +static snd_pcm_hw_constraint_list_t hw_constraints_channels_4 = { + count: 3, + list: hw_channels, + mask: 0, +}; +static snd_pcm_hw_constraint_list_t hw_constraints_channels_6 = { + count: 5, + list: hw_channels, + mask: 0, +}; + +static int set_dac_channels(cmipci_t *cm, cmipci_pcm_t *rec, int channels) +{ + unsigned long flags; + + if (channels > 2) { + if (! cm->can_multi_ch) + return -EINVAL; + if (rec->fmt != 0x03) /* stereo 16bit only */ + return -EINVAL; + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); + if (channels > 4) { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); + } else { + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); + } + if (channels == 6) { + snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); + } else { + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + + } else { + if (cm->can_multi_ch) { + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); + spin_unlock_irqrestore(&cm->reg_lock, flags); + } + } + return 0; +} + + +/* + * prepare playback/capture channel + * channel to be used must have been set in rec->ch. + */ +static int snd_cmipci_pcm_prepare(cmipci_t *cm, cmipci_pcm_t *rec, + snd_pcm_substream_t *substream) +{ + unsigned long flags; + unsigned int reg, freq, val; + snd_pcm_runtime_t *runtime = substream->runtime; + + rec->fmt = 0; + rec->shift = 0; + if (snd_pcm_format_width(runtime->format) >= 16) { + rec->fmt |= 0x02; + if (snd_pcm_format_width(runtime->format) > 16) + rec->shift++; /* 24/32bit */ + } + if (runtime->channels > 1) + rec->fmt |= 0x01; + if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) { + snd_printd("cannot set dac channels\n"); + return -EINVAL; + } + + rec->offset = runtime->dma_addr; + /* buffer and period sizes in frame */ + rec->dma_size = runtime->buffer_size << rec->shift; + rec->period_size = runtime->period_size << rec->shift; + rec->dma_size <<= rec->ac3_shift; + rec->period_size <<= rec->ac3_shift; + + spin_lock_irqsave(&cm->reg_lock, flags); + + /* set buffer address */ + reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; + snd_cmipci_write(cm, reg, rec->offset); + /* program sample counts */ + reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; + snd_cmipci_write_w(cm, reg, rec->dma_size - 1); + snd_cmipci_write_w(cm, reg + 2, rec->period_size - 1); + + /* set adc/dac flag */ + val = rec->ch ? CM_CHADC1 : CM_CHADC0; + if (rec->is_dac) + cm->ctrl &= ~val; + else + cm->ctrl |= val; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl); + + /* set sample rate */ + freq = snd_cmipci_rate_freq(runtime->rate); + val = snd_cmipci_read(cm, CM_REG_FUNCTRL1); + if (rec->ch) { + val &= ~CM_ASFC_MASK; + val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK; + } else { + val &= ~CM_DSFC_MASK; + val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_MASK; + } + snd_cmipci_write(cm, CM_REG_FUNCTRL1, val); + //snd_printd("cmipci: functrl1 = %08x\n", val); + + /* set format */ + val = snd_cmipci_read(cm, CM_REG_CHFORMAT); + if (rec->ch) { + val &= ~CM_CH1FMT_MASK; + val |= rec->fmt << CM_CH1FMT_SHIFT; + } else { + val &= ~CM_CH0FMT_MASK; + val |= rec->fmt << CM_CH0FMT_SHIFT; + } + snd_cmipci_write(cm, CM_REG_CHFORMAT, val); + //snd_printd("cmipci: chformat = %08x\n", val); + + rec->running = 0; + spin_unlock_irqrestore(&cm->reg_lock, flags); + + return 0; +} + +/* + * PCM trigger/stop + */ +static int snd_cmipci_pcm_trigger(cmipci_t *cm, cmipci_pcm_t *rec, + snd_pcm_substream_t *substream, int cmd) +{ + unsigned int inthld, chen, reset, pause; + int result = 0; + + inthld = CM_CH0_INT_EN << rec->ch; + chen = CM_CHEN0 << rec->ch; + reset = CM_RST_CH0 << rec->ch; + pause = CM_PAUSE0 << rec->ch; + + spin_lock(&cm->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + rec->running = 1; + /* set interrupt */ + snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, inthld); + cm->ctrl |= chen; + /* enable channel */ + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl); + break; + case SNDRV_PCM_TRIGGER_STOP: + rec->running = 0; + /* disable interrupt */ + snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, inthld); + /* reset */ + cm->ctrl &= ~chen; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset); + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + cm->ctrl |= pause; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cm->ctrl &= ~pause; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&cm->reg_lock); + return result; +} + +/* + * return the current pointer + */ +static snd_pcm_uframes_t snd_cmipci_pcm_pointer(cmipci_t *cm, cmipci_pcm_t *rec, + snd_pcm_substream_t *substream) +{ + size_t ptr; + unsigned int reg; + if (!rec->running) + return 0; +#if 1 // this seems better.. + reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; + ptr = rec->dma_size - (snd_cmipci_read_w(cm, reg) + 1); + ptr >>= rec->shift; +#else + reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; + ptr = snd_cmipci_read(cm, reg) - rec->offset; + ptr = bytes_to_frames(substream->runtime, ptr); +#endif + ptr >>= rec->ac3_shift; + return ptr; +} + +/* + * playback + */ + +static int snd_cmipci_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_PLAY], substream, cmd); +} + +static snd_pcm_uframes_t snd_cmipci_playback_pointer(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_PLAY], substream); +} + + + +/* + * capture + */ + +static int snd_cmipci_capture_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_CAPT], substream, cmd); +} + +static snd_pcm_uframes_t snd_cmipci_capture_pointer(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_CAPT], substream); +} + +#ifdef DO_SOFT_AC3 +/* + * special tricks for soft ac3 transfer: + * + * we compose an iec958 subframe from 16bit ac3 sample and + * write the raw subframe via 32bit data mode. + */ + +/* find parity for bit 4~30 */ +static unsigned parity(unsigned int data) +{ + unsigned int parity = 0; + int counter = 4; + + data >>= 4; /* start from bit 4 */ + while (counter <= 30) { + if (data & 1) + parity++; + data >>= 1; + counter++; + } + return parity & 1; +} + +/* + * compose 32bit iec958 subframe with non-audio data. + * bit 0-3 = preamble + * 4-7 = aux (=0) + * 8-27 = data (12-27 for 16bit) + * 28 = validity (=0) + * 29 = user data (=0) + * 30 = channel status + * 31 = parity + * + * channel status is assumed as consumer, non-audio + * thus all 0 except bit 1 + */ +inline static u32 convert_ac3_32bit(cmipci_t *cm, u32 val) +{ + u32 data = (u32)val << 12; + + if (cm->spdif_counter == 2 || cm->spdif_counter == 3) /* bit 1 */ + data |= 0x40000000; /* indicate AC-3 raw data */ + if (parity(data)) /* parity bit 4-30 */ + data |= 0x80000000; + if (cm->spdif_counter == 0) + data |= 3; /* preamble 'M' */ + else if (cm->spdif_counter & 1) + data |= 5; /* odd, 'W' */ + else + data |= 9; /* even, 'M' */ + + cm->spdif_counter++; + if (cm->spdif_counter == 384) + cm->spdif_counter = 0; + + return data; +} + + +static int snd_cmipci_ac3_copy(snd_pcm_substream_t *subs, int channel, + snd_pcm_uframes_t pos, void *src, + snd_pcm_uframes_t count) +{ + cmipci_t *cm = snd_pcm_substream_chip(subs); + u32 *dst; + u16 *srcp = src, val; + snd_pcm_uframes_t offset; + snd_pcm_runtime_t *runtime = subs->runtime; + + if (! cm->channel[CM_CH_PLAY].ac3_shift) + return copy_from_user(runtime->dma_area + frames_to_bytes(runtime, pos), + src, frames_to_bytes(runtime, count)); + + if (! access_ok(VERIFY_READ, src, count)) + return -EFAULT; + + /* frame = 16bit stereo */ + offset = (pos << 1) % (cm->channel[CM_CH_PLAY].dma_size << 2); + dst = (u32*)(runtime->dma_area + offset); + + count /= 2; + while (count-- > 0) { + get_user(val, srcp); + srcp++; + *dst++ = convert_ac3_32bit(cm, val); + } + + return 0; +} + +static int snd_cmipci_ac3_silence(snd_pcm_substream_t *subs, int channel, + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + cmipci_t *cm = snd_pcm_substream_chip(subs); + u32 *dst; + snd_pcm_uframes_t offset; + snd_pcm_runtime_t *runtime = subs->runtime; + + if (! cm->channel[CM_CH_PLAY].ac3_shift) + return snd_pcm_format_set_silence(runtime->format, + runtime->dma_area + frames_to_bytes(runtime, pos), count); + + /* frame = 16bit stereo */ + offset = (pos << 1) % (cm->channel[CM_CH_PLAY].dma_size << 2); + dst = (u32*)(subs->runtime->dma_area + offset); + + count /= 2; + while (count-- > 0) { + *dst++ = convert_ac3_32bit(cm, 0); + } + + return 0; +} +#endif /* DO_SOFT_AC3 */ + + +/* + * hw preparation for spdif + */ + +static int snd_cmipci_spdif_default_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cmipci_spdif_default_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cmipci_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i, change; + unsigned int val; + + val = 0; + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); + change = val != chip->dig_status; + chip->dig_status = val; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_cmipci_spdif_default __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_cmipci_spdif_default_info, + get: snd_cmipci_spdif_default_get, + put: snd_cmipci_spdif_default_put +}; + +static int snd_cmipci_spdif_mask_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cmipci_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static snd_kcontrol_new_t snd_cmipci_spdif_mask __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_cmipci_spdif_mask_info, + get: snd_cmipci_spdif_mask_get, +}; + +static int snd_cmipci_spdif_stream_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cmipci_spdif_stream_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cmipci_spdif_stream_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i, change; + unsigned int val; + + val = 0; + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); + change = val != chip->dig_pcm_status; + chip->dig_pcm_status = val; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_cmipci_spdif_stream __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_cmipci_spdif_stream_info, + get: snd_cmipci_spdif_stream_get, + put: snd_cmipci_spdif_stream_put +}; + +/* + */ + +/* save mixer setting and mute for AC3 playback */ +static void save_mixer_state(cmipci_t *cm) +{ + if (! cm->mixer_insensitive) { + int i; + for (i = 0; i < CM_SAVED_MIXERS; i++) { + snd_kcontrol_t *ctl = cm->mixer_res_ctl[i]; + if (ctl) { + snd_ctl_elem_value_t val; + int event; + memset(&val, 0, sizeof(val)); + ctl->get(ctl, &val); + cm->mixer_res_status[i] = val.value.integer.value[0]; + val.value.integer.value[0] = cm_saved_mixer[i].toggle_on; + event = SNDRV_CTL_EVENT_MASK_INFO; + if (cm->mixer_res_status[i] != val.value.integer.value[0]) { + ctl->put(ctl, &val); /* toggle */ + event |= SNDRV_CTL_EVENT_MASK_VALUE; + } + ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(cm->card, event, &ctl->id); + } + } + cm->mixer_insensitive = 1; + } +} + + +/* restore the previously saved mixer status */ +static void restore_mixer_state(cmipci_t *cm) +{ + if (cm->mixer_insensitive) { + int i; + cm->mixer_insensitive = 0; /* at first clear this; + otherwise the changes will be ignored */ + for (i = 0; i < CM_SAVED_MIXERS; i++) { + snd_kcontrol_t *ctl = cm->mixer_res_ctl[i]; + if (ctl) { + snd_ctl_elem_value_t val; + int event; + + memset(&val, 0, sizeof(val)); + ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + ctl->get(ctl, &val); + event = SNDRV_CTL_EVENT_MASK_INFO; + if (val.value.integer.value[0] != cm->mixer_res_status[i]) { + val.value.integer.value[0] = cm->mixer_res_status[i]; + ctl->put(ctl, &val); + event |= SNDRV_CTL_EVENT_MASK_VALUE; + } + snd_ctl_notify(cm->card, event, &ctl->id); + } + } + } +} + +/* spinlock held! */ +static void setup_ac3(cmipci_t *cm, int do_ac3, int rate) +{ + cm->channel[CM_CH_PLAY].ac3_shift = 0; + cm->spdif_counter = 0; + + if (do_ac3) { + /* AC3EN for 037 */ + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1); + /* AC3EN for 039 */ + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2); + + if (cm->can_ac3_hw) { + /* SPD24SEL for 037, 0x02 */ + /* SPD24SEL for 039, 0x20, but cannot be set */ + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); + } else { /* can_ac3_sw */ +#ifdef DO_SOFT_AC3 + /* FIXME: ugly hack! */ + subs->runtime->buffer_size /= 2; + /* SPD32SEL for 037 & 039, 0x20 */ + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); + /* set 176K sample rate to fix 033 HW bug */ + if (cm->chip_version == 33) { + if (rate >= 48000) { + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_176K); + } else { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); + } + } + cm->channel[CM_CH_PLAY].ac3_shift = 1; /* use 32bit */ +#endif /* DO_SOFT_AC3 */ + } + + } else { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2); + + if (cm->can_ac3_hw) { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); + } else { + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); + } + } +} + +static void setup_spdif_playback(cmipci_t *cm, snd_pcm_substream_t *subs, int up, int do_ac3) +{ + int rate; + unsigned long flags; + + rate = subs->runtime->rate; + + if (up && do_ac3) + save_mixer_state(cm); + + spin_lock_irqsave(&cm->reg_lock, flags); + if (up) { + /* they are controlled via "IEC958 Output Switch" */ + /* snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ + /* snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + setup_ac3(cm, do_ac3, rate); + + if (rate == 48000) + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97); + else + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97); + + } else { + /* they are controlled via "IEC958 Output Switch" */ + /* snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ + /* snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ + snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + setup_ac3(cm, 0, 0); + } + spin_unlock_irqrestore(&cm->reg_lock, flags); +} + + +/* + * preparation + */ + +/* playback - enable spdif only on the certain condition */ +static int snd_cmipci_playback_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + int rate = substream->runtime->rate; + int do_spdif, do_ac3; + do_spdif = ((rate == 44100 || rate == 48000) && + substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE && + substream->runtime->channels == 2); + do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO; +#ifdef DO_SOFT_AC3 + if (do_ac3 && cm->can_ac3_sw) + do_spdif = 0; +#endif + setup_spdif_playback(cm, substream, do_spdif, do_ac3); + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); +} + +/* playback (via device #2) - enable spdif always */ +static int snd_cmipci_playback_spdif_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + setup_spdif_playback(cm, substream, 1, cm->dig_pcm_status & IEC958_AES0_NONAUDIO); + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); +} + +static int snd_cmipci_playback_hw_free(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + setup_spdif_playback(cm, substream, 0, 0); + restore_mixer_state(cm); + return snd_cmipci_hw_free(substream); +} + +/* capture */ +static int snd_cmipci_capture_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); +} + +/* capture with spdif (via device #2) */ +static int snd_cmipci_capture_spdif_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); + spin_unlock_irqrestore(&cm->reg_lock, flags); + + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); +} + +static int snd_cmipci_capture_spdif_hw_free(snd_pcm_substream_t *subs) +{ + cmipci_t *cm = snd_pcm_substream_chip(subs); + unsigned long flags; + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); + spin_unlock_irqrestore(&cm->reg_lock, flags); + + return snd_cmipci_hw_free(subs); +} + + +/* + * interrupt handler + */ +static void snd_cmipci_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cmipci_t *cm = snd_magic_cast(cmipci_t, dev_id, return); + unsigned int status; + + /* fastpath out, to ease interrupt sharing */ + status = snd_cmipci_read(cm, CM_REG_INT_STATUS); + if (!(status & CM_INTR)) + return; + + /* acknowledge interrupt */ + spin_lock(&cm->reg_lock); + if (status & CM_CHINT0) { + snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, CM_CH0_INT_EN); + snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, CM_CH0_INT_EN); + } + if (status & CM_CHINT1) { + snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, CM_CH1_INT_EN); + snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, CM_CH1_INT_EN); + } + spin_unlock(&cm->reg_lock); + + if (cm->rmidi && (status & CM_UARTINT)) + snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data, regs); + + if (cm->pcm) { + if ((status & CM_CHINT0) && cm->channel[0].running) + snd_pcm_period_elapsed(cm->channel[0].substream); + if ((status & CM_CHINT1) && cm->channel[1].running) + snd_pcm_period_elapsed(cm->channel[1].substream); + } +} + +/* + * h/w infos + */ + +/* playback on channel A */ +static snd_pcm_hardware_t snd_cmipci_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, + rate_min: 5512, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 1024, + fifo_size: 0, +}; + +/* capture on channel B */ +static snd_pcm_hardware_t snd_cmipci_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, + rate_min: 5512, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 1024, + fifo_size: 0, +}; + +/* playback on channel B - stereo 16bit only? */ +static snd_pcm_hardware_t snd_cmipci_playback2 = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, + rate_min: 5512, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 1024, + fifo_size: 0, +}; + +/* spdif playback on channel A */ +static snd_pcm_hardware_t snd_cmipci_playback_spdif = +{ + info: (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER), + formats: SNDRV_PCM_FMTBIT_S16_LE /*| SNDRV_PCM_FMTBIT_S32_LE*/, + rates: SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + rate_min: 44100, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 1024, + fifo_size: 0, +}; + +/* spdif capture on channel B */ +static snd_pcm_hardware_t snd_cmipci_capture_spdif = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + rate_min: 44100, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * check device open/close + */ +static int open_device_check(cmipci_t *cm, int mode, snd_pcm_substream_t *subs) +{ + unsigned long flags; + int ch = mode & CM_OPEN_CH_MASK; + + /* FIXME: a file should wait until the device becomes free + * when it's opened on blocking mode. however, since the current + * pcm framework doesn't pass file pointer before actually opened, + * we can't know whether blocking mode or not in open callback.. + */ + down(&cm->open_mutex); + if (cm->opened[ch]) { + up(&cm->open_mutex); + return -EBUSY; + } + cm->opened[ch] = mode; + cm->channel[ch].substream = subs; + if (! (mode & CM_OPEN_DAC)) { + /* disable dual DAC mode */ + cm->channel[ch].is_dac = 0; + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); + spin_unlock_irqrestore(&cm->reg_lock, flags); + } + up(&cm->open_mutex); + return 0; +} + +static void close_device_check(cmipci_t *cm, int mode) +{ + unsigned long flags; + int ch = mode & CM_OPEN_CH_MASK; + + down(&cm->open_mutex); + if (cm->opened[ch] == mode) { + cm->channel[ch].running = 0; + cm->channel[ch].substream = NULL; + cm->opened[ch] = 0; + if (! cm->channel[ch].is_dac) { + /* enable dual DAC mode again */ + cm->channel[ch].is_dac = 1; + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); + spin_unlock_irqrestore(&cm->reg_lock, flags); + } + } + up(&cm->open_mutex); +} + +/* + */ + +static int snd_cmipci_playback_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0) + return err; + runtime->hw = snd_cmipci_playback; + if (cm->can_multi_ch) { + runtime->hw.channels_max = cm->max_channels; + if (cm->max_channels == 4) + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_4); + else + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_6); + } + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); + return 0; +} + +static int snd_cmipci_capture_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_CAPTURE, substream)) < 0) + return err; + runtime->hw = snd_cmipci_capture; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); + return 0; +} + +static int snd_cmipci_playback2_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream)) < 0) /* use channel B */ + return err; + runtime->hw = snd_cmipci_playback2; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); + return 0; +} + +static int snd_cmipci_playback_spdif_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream)) < 0) /* use channel A */ + return err; + runtime->hw = snd_cmipci_playback_spdif; + if (cm->can_ac3_hw) { + runtime->hw.info |= SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID; + } + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000); + cm->dig_pcm_status = cm->dig_status; + return 0; +} + +static int snd_cmipci_capture_spdif_open(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */ + return err; + runtime->hw = snd_cmipci_capture_spdif; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000); + return 0; +} + + +/* + */ + +static int snd_cmipci_playback_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_PLAYBACK); + return 0; +} + +static int snd_cmipci_capture_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_CAPTURE); + return 0; +} + +static int snd_cmipci_playback2_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_PLAYBACK2); + return 0; +} + +static int snd_cmipci_playback_spdif_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_SPDIF_PLAYBACK); + return 0; +} + +static int snd_cmipci_capture_spdif_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_SPDIF_CAPTURE); + return 0; +} + + +/* + */ + +static snd_pcm_ops_t snd_cmipci_playback_ops = { + open: snd_cmipci_playback_open, + close: snd_cmipci_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cmipci_hw_params, + hw_free: snd_cmipci_playback_hw_free, + prepare: snd_cmipci_playback_prepare, + trigger: snd_cmipci_playback_trigger, + pointer: snd_cmipci_playback_pointer, +}; + +static snd_pcm_ops_t snd_cmipci_capture_ops = { + open: snd_cmipci_capture_open, + close: snd_cmipci_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cmipci_hw_params, + hw_free: snd_cmipci_hw_free, + prepare: snd_cmipci_capture_prepare, + trigger: snd_cmipci_capture_trigger, + pointer: snd_cmipci_capture_pointer, +}; + +static snd_pcm_ops_t snd_cmipci_playback2_ops = { + open: snd_cmipci_playback2_open, + close: snd_cmipci_playback2_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cmipci_hw_params, + hw_free: snd_cmipci_hw_free, + prepare: snd_cmipci_capture_prepare, /* channel B */ + trigger: snd_cmipci_capture_trigger, /* channel B */ + pointer: snd_cmipci_capture_pointer, /* channel B */ +}; + +static snd_pcm_ops_t snd_cmipci_playback_spdif_ops = { + open: snd_cmipci_playback_spdif_open, + close: snd_cmipci_playback_spdif_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cmipci_hw_params, + hw_free: snd_cmipci_playback_hw_free, + prepare: snd_cmipci_playback_spdif_prepare, /* set up rate */ + trigger: snd_cmipci_playback_trigger, + pointer: snd_cmipci_playback_pointer, +#ifdef DO_SOFT_AC3 + copy: snd_cmipci_ac3_copy, + silence: snd_cmipci_ac3_silence, +#endif +}; + +static snd_pcm_ops_t snd_cmipci_capture_spdif_ops = { + open: snd_cmipci_capture_spdif_open, + close: snd_cmipci_capture_spdif_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cmipci_hw_params, + hw_free: snd_cmipci_capture_spdif_hw_free, + prepare: snd_cmipci_capture_spdif_prepare, + trigger: snd_cmipci_capture_trigger, + pointer: snd_cmipci_capture_pointer, +}; + + +/* + */ + +static void snd_cmipci_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_cmipci_pcm_new(cmipci_t *cm, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_ops); + + pcm->private_data = cm; + pcm->private_free = snd_cmipci_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "C-Media PCI DAC/ADC"); + cm->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + + return 0; +} + +static int __devinit snd_cmipci_pcm2_new(cmipci_t *cm, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback2_ops); + + pcm->private_data = cm; + pcm->private_free = snd_cmipci_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "C-Media PCI 2nd DAC"); + cm->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + + return 0; +} + +static int __devinit snd_cmipci_pcm_spdif_new(cmipci_t *cm, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_spdif_ops); + + pcm->private_data = cm; + pcm->private_free = snd_cmipci_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "C-Media PCI IEC958"); + cm->pcm_spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + + return 0; +} + +/* + * mixer interface: + * - CM8338/8738 has a compatible mixer interface with SB16, but + * lack of some elements like tone control, i/o gain and AGC. + * - Access to native registers: + * - A 3D switch + * - Output mute switches + */ + +static void snd_cmipci_mixer_write(cmipci_t *s, unsigned char idx, unsigned char data) +{ + outb(idx, s->iobase + CM_REG_SB16_ADDR); + outb(data, s->iobase + CM_REG_SB16_DATA); +} + +static unsigned char snd_cmipci_mixer_read(cmipci_t *s, unsigned char idx) +{ + unsigned char v; + + outb(idx, s->iobase + CM_REG_SB16_ADDR); + v = inb(s->iobase + CM_REG_SB16_DATA); + return v; +} + +/* + * general mixer element + */ +typedef struct cmipci_sb_reg { + unsigned int left_reg, right_reg; + unsigned int left_shift, right_shift; + unsigned int mask; + unsigned int invert: 1; + unsigned int stereo: 1; +} cmipci_sb_reg_t; + +#define COMPOSE_SB_REG(lreg,rreg,lshift,rshift,mask,invert,stereo) \ + ((lreg) | ((rreg) << 8) | (lshift << 16) | (rshift << 19) | (mask << 24) | (invert << 22) | (stereo << 23)) + +#define CMIPCI_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask, invert, stereo) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_volume, \ + get: snd_cmipci_get_volume, put: snd_cmipci_put_volume, \ + private_value: COMPOSE_SB_REG(left_reg, right_reg, left_shift, right_shift, mask, invert, stereo), \ +} + +#define CMIPCI_SB_VOL_STEREO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg+1, shift, shift, mask, 0, 1) +#define CMIPCI_SB_VOL_MONO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg, shift, shift, mask, 0, 0) +#define CMIPCI_SB_SW_STEREO(xname,lshift,rshift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, lshift, rshift, 1, 0, 1) +#define CMIPCI_SB_SW_MONO(xname,shift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, shift, shift, 1, 0, 0) + +static void cmipci_sb_reg_decode(cmipci_sb_reg_t *r, unsigned long val) +{ + r->left_reg = val & 0xff; + r->right_reg = (val >> 8) & 0xff; + r->left_shift = (val >> 16) & 0x07; + r->right_shift = (val >> 19) & 0x07; + r->invert = (val >> 22) & 1; + r->stereo = (val >> 23) & 1; + r->mask = (val >> 24) & 0xff; +} + +static int snd_cmipci_info_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + cmipci_sb_reg_t reg; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = reg.stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = reg.mask; + return 0; +} + +static int snd_cmipci_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int val; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[0] = val; + if (reg.stereo) { + val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[1] = val; + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; +} + +static int snd_cmipci_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int change; + int left, right, oleft, oright; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + left = ucontrol->value.integer.value[0] & reg.mask; + if (reg.invert) + left = reg.mask - left; + left <<= reg.left_shift; + if (reg.stereo) { + right = ucontrol->value.integer.value[1] & reg.mask; + if (reg.invert) + right = reg.mask - right; + right <<= reg.right_shift; + } else + right = 0; + spin_lock_irqsave(&cm->reg_lock, flags); + oleft = snd_cmipci_mixer_read(cm, reg.left_reg); + left |= oleft & ~(reg.mask << reg.left_shift); + change = left != oleft; + if (reg.stereo) { + if (reg.left_reg != reg.right_reg) { + snd_cmipci_mixer_write(cm, reg.left_reg, left); + oright = snd_cmipci_mixer_read(cm, reg.right_reg); + } else + oright = left; + right |= oright & ~(reg.mask << reg.right_shift); + change |= right != oright; + snd_cmipci_mixer_write(cm, reg.right_reg, right); + } else + snd_cmipci_mixer_write(cm, reg.left_reg, left); + spin_unlock_irqrestore(&cm->reg_lock, flags); + return change; +} + +/* + * input route (left,right) -> (left,right) + */ +#define CMIPCI_SB_INPUT_SW(xname, left_shift, right_shift) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_input_sw, \ + get: snd_cmipci_get_input_sw, put: snd_cmipci_put_input_sw, \ + private_value: COMPOSE_SB_REG(SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, left_shift, right_shift, 1, 0, 1), \ +} + +static int snd_cmipci_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_cmipci_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int val1, val2; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + val1 = snd_cmipci_mixer_read(cm, reg.left_reg); + val2 = snd_cmipci_mixer_read(cm, reg.right_reg); + spin_unlock_irqrestore(&cm->reg_lock, flags); + ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1; + ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1; + ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1; + ucontrol->value.integer.value[3] = (val2 >> reg.right_shift) & 1; + return 0; +} + +static int snd_cmipci_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int change; + int val1, val2, oval1, oval2; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + oval1 = snd_cmipci_mixer_read(cm, reg.left_reg); + oval2 = snd_cmipci_mixer_read(cm, reg.right_reg); + val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); + val2 = oval2 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); + val1 |= (ucontrol->value.integer.value[0] & 1) << reg.left_shift; + val2 |= (ucontrol->value.integer.value[1] & 1) << reg.left_shift; + val1 |= (ucontrol->value.integer.value[2] & 1) << reg.right_shift; + val2 |= (ucontrol->value.integer.value[3] & 1) << reg.right_shift; + change = val1 != oval1 || val2 != oval2; + snd_cmipci_mixer_write(cm, reg.left_reg, val1); + snd_cmipci_mixer_write(cm, reg.right_reg, val2); + spin_unlock_irqrestore(&cm->reg_lock, flags); + return change; +} + +/* + * native mixer switches/volumes + */ + +#define CMIPCI_MIXER_SW_STEREO(xname, reg, lshift, rshift, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_native_mixer, \ + get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \ + private_value: COMPOSE_SB_REG(reg, reg, lshift, rshift, 1, invert, 1), \ +} + +#define CMIPCI_MIXER_SW_MONO(xname, reg, shift, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_native_mixer, \ + get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \ + private_value: COMPOSE_SB_REG(reg, reg, shift, shift, 1, invert, 0), \ +} + +#define CMIPCI_MIXER_VOL_STEREO(xname, reg, lshift, rshift, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_native_mixer, \ + get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \ + private_value: COMPOSE_SB_REG(reg, reg, lshift, rshift, mask, 0, 1), \ +} + +#define CMIPCI_MIXER_VOL_MONO(xname, reg, shift, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_native_mixer, \ + get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \ + private_value: COMPOSE_SB_REG(reg, reg, shift, shift, mask, 0, 0), \ +} + +static int snd_cmipci_info_native_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + cmipci_sb_reg_t reg; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = reg.stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = reg.mask; + return 0; + +} + +static int snd_cmipci_get_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + cmipci_sb_reg_t reg; + unsigned long flags; + unsigned char oreg, val; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + oreg = inb(cm->iobase + reg.left_reg); + val = (oreg >> reg.left_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[0] = val; + if (reg.stereo) { + val = (oreg >> reg.right_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[1] = val; + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; +} + +static int snd_cmipci_put_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + cmipci_sb_reg_t reg; + unsigned long flags; + unsigned char oreg, nreg, val; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + oreg = inb(cm->iobase + reg.left_reg); + val = ucontrol->value.integer.value[0] & reg.mask; + if (reg.invert) + val = reg.mask - val; + nreg = oreg & ~(reg.mask << reg.left_shift); + nreg |= (val << reg.left_shift); + if (reg.stereo) { + val = ucontrol->value.integer.value[1] & reg.mask; + if (reg.invert) + val = reg.mask - val; + nreg &= ~(reg.mask << reg.right_shift); + nreg |= (val << reg.right_shift); + } + outb(nreg, cm->iobase + reg.left_reg); + spin_unlock_irqrestore(&cm->reg_lock, flags); + return (nreg != oreg); +} + +/* + * special case - check mixer sensitivity + */ +static int snd_cmipci_get_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + //cmipci_t *cm = snd_kcontrol_chip(kcontrol); + return snd_cmipci_get_native_mixer(kcontrol, ucontrol); +} + +static int snd_cmipci_put_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + if (cm->mixer_insensitive) { + /* ignored */ + return 0; + } + return snd_cmipci_put_native_mixer(kcontrol, ucontrol); +} + + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cmipci_mixers[] __devinitdata = { + CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31), + CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0), + CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31), + //CMIPCI_MIXER_SW_MONO("PCM Playback Switch", CM_REG_MIXER1, CM_WSMUTE_SHIFT, 1), + { /* switch with sensitivity */ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Playback Switch", + info: snd_cmipci_info_native_mixer, + get: snd_cmipci_get_native_mixer_sensitive, + put: snd_cmipci_put_native_mixer_sensitive, + private_value: COMPOSE_SB_REG(CM_REG_MIXER1, CM_REG_MIXER1, CM_WSMUTE_SHIFT, CM_WSMUTE_SHIFT, 1, 1, 0), + }, + CMIPCI_MIXER_SW_STEREO("PCM Capture Switch", CM_REG_MIXER1, CM_WAVEINL_SHIFT, CM_WAVEINR_SHIFT, 0), + CMIPCI_SB_VOL_STEREO("Synth Playback Volume", SB_DSP4_SYNTH_DEV, 3, 31), + CMIPCI_MIXER_SW_MONO("Synth Playback Switch", CM_REG_MIXER1, CM_FMMUTE_SHIFT, 1), + CMIPCI_SB_INPUT_SW("Synth Capture Route", 6, 5), + CMIPCI_SB_VOL_STEREO("CD Playback Volume", SB_DSP4_CD_DEV, 3, 31), + CMIPCI_SB_SW_STEREO("CD Playback Switch", 2, 1), + CMIPCI_SB_INPUT_SW("CD Capture Route", 2, 1), + CMIPCI_SB_VOL_STEREO("Line Playback Volume", SB_DSP4_LINE_DEV, 3, 31), + CMIPCI_SB_SW_STEREO("Line Playback Switch", 4, 3), + CMIPCI_SB_INPUT_SW("Line Capture Route", 4, 3), + CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), + CMIPCI_SB_SW_MONO("Mic Playback Switch", 0), + CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0), + CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3), + CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15), + CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0), + CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0), + CMIPCI_MIXER_SW_MONO("Mic Boost", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1), + CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7), +}; + +/* + * other switches + */ + +typedef struct snd_cmipci_switch_args { + int reg; /* register index */ + unsigned int mask; /* mask bits */ + unsigned int mask_on; /* mask bits to turn on */ + int is_byte: 1; /* byte access? */ + int ac3_sensitive: 1; /* access forbidden during non-audio operation? */ +} snd_cmipci_switch_args_t; + +static int snd_cmipci_uswitch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int _snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args) +{ + unsigned long flags; + unsigned int val; + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + + spin_lock_irqsave(&cm->reg_lock, flags); + if (args->ac3_sensitive && cm->mixer_insensitive) { + ucontrol->value.integer.value[0] = 0; + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; + } + if (args->is_byte) + val = inb(cm->iobase + args->reg); + else + val = snd_cmipci_read(cm, args->reg); + ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0; + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; +} + +static int snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value; + snd_assert(args != NULL, return -EINVAL); + return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args); +} + +static int _snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args) +{ + unsigned long flags; + unsigned int val; + int change; + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + + spin_lock_irqsave(&cm->reg_lock, flags); + if (args->ac3_sensitive && cm->mixer_insensitive) { + /* ignored */ + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; + } + if (args->is_byte) + val = inb(cm->iobase + args->reg); + else + val = snd_cmipci_read(cm, args->reg); + change = (val & args->mask) != (ucontrol->value.integer.value[0] ? args->mask : 0); + if (change) { + val &= ~args->mask; + if (ucontrol->value.integer.value[0]) + val |= args->mask_on; + else + val |= (args->mask & ~args->mask_on); + if (args->is_byte) + outb((unsigned char)val, cm->iobase + args->reg); + else + snd_cmipci_write(cm, args->reg, val); + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + return change; +} + +static int snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value; + snd_assert(args != NULL, return -EINVAL); + return _snd_cmipci_uswitch_put(kcontrol, ucontrol, args); +} + +#define DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask_on, xis_byte, xac3) \ +static snd_cmipci_switch_args_t cmipci_switch_arg_##sname = { \ + reg: xreg, \ + mask: xmask, \ + mask_on: xmask_on, \ + is_byte: xis_byte, \ + ac3_sensitive: xac3, \ +} + +#define DEFINE_BIT_SWITCH_ARG(sname, xreg, xmask, xis_byte, xac3) \ + DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask, xis_byte, xac3) + +#if 0 /* these will be controlled in pcm device */ +DEFINE_BIT_SWITCH_ARG(spdif_in, CM_REG_FUNCTRL1, CM_SPDF_1, 0); +DEFINE_BIT_SWITCH_ARG(spdif_0, CM_REG_FUNCTRL1, CM_SPDF_0, 0); +DEFINE_BIT_SWITCH_ARG(spdo_48k, CM_REG_MISC_CTRL, CM_SPDF_AC97|CM_SPDIF48K, 0); +#endif +DEFINE_BIT_SWITCH_ARG(spdif_enable, CM_REG_LEGACY_CTRL, CM_ENSPDOUT, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdo2dac, CM_REG_FUNCTRL1, CM_SPDO2DAC, 0, 1); +DEFINE_BIT_SWITCH_ARG(spdi_valid, CM_REG_MISC, CM_SPDVALID, 1, 0); +DEFINE_BIT_SWITCH_ARG(spdif_copyright, CM_REG_LEGACY_CTRL, CM_SPDCOPYRHT, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdif_dac_out, CM_REG_LEGACY_CTRL, CM_DAC2SPDO, 0, 1); +DEFINE_SWITCH_ARG(spdo_5v, CM_REG_MISC_CTRL, CM_SPDO5V, 0, 0, 0); /* inverse: 0 = 5V */ +DEFINE_BIT_SWITCH_ARG(spdif_loop, CM_REG_FUNCTRL1, CM_SPDFLOOP, 0, 1); +DEFINE_BIT_SWITCH_ARG(spdi_monitor, CM_REG_MIXER1, CM_CDPLAY, 1, 0); +DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_MISC, 0x04, 0, 0); +#if CM_CH_PLAY == 1 +DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, 0, 0, 0); /* reversed */ +#else +DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0); +#endif +DEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0); +DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0); +DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0); +DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0); +DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0); + +#define DEFINE_SWITCH(sname, stype, sarg) \ +{ name: sname, \ + iface: stype, \ + info: snd_cmipci_uswitch_info, \ + get: snd_cmipci_uswitch_get, \ + put: snd_cmipci_uswitch_put, \ + private_value: (unsigned long)&cmipci_switch_arg_##sarg,\ +} + +#define DEFINE_CARD_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_CARD, sarg) +#define DEFINE_MIXER_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_MIXER, sarg) + + +/* + * callbacks for spdif output switch + * needs toggle two registers.. + */ +static int snd_cmipci_spdout_enable_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + int changed; + changed = _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable); + changed |= _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac); + return changed; +} + +static int snd_cmipci_spdout_enable_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + int changed; + changed = _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable); + changed |= _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac); + return changed; +} + + +/* both for CM8338/8738 */ +static snd_kcontrol_new_t snd_cmipci_mixer_switches[] __devinitdata = { + DEFINE_MIXER_SWITCH("Exchange DAC", exchange_dac), + DEFINE_MIXER_SWITCH("Four Channel Mode", fourch), + DEFINE_MIXER_SWITCH("Line-In As Rear", line_rear), +}; + +/* only for CM8738 */ +static snd_kcontrol_new_t snd_cmipci_8738_mixer_switches[] __devinitdata = { +#if 0 /* controlled in pcm device */ + DEFINE_MIXER_SWITCH("IEC958 In Record", spdif_in), + DEFINE_MIXER_SWITCH("IEC958 Out", spdif_0), + DEFINE_MIXER_SWITCH("IEC958 Out 48KHz", spdo_48k), + DEFINE_MIXER_SWITCH("IEC958 Out To DAC", spdo2dac), +#endif + // DEFINE_MIXER_SWITCH("IEC958 Output Switch", spdif_enable), + { name: "IEC958 Output Switch", + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + info: snd_cmipci_uswitch_info, + get: snd_cmipci_spdout_enable_get, + put: snd_cmipci_spdout_enable_put, + }, + DEFINE_MIXER_SWITCH("IEC958 In Valid", spdi_valid), + DEFINE_MIXER_SWITCH("IEC958 Copyright", spdif_copyright), + DEFINE_MIXER_SWITCH("IEC958 5V", spdo_5v), + DEFINE_MIXER_SWITCH("IEC958 Loop", spdif_loop), + DEFINE_MIXER_SWITCH("IEC958 In Monitor", spdi_monitor), + DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase), +}; + +/* only for model 033/037 */ +static snd_kcontrol_new_t snd_cmipci_old_mixer_switches[] __devinitdata = { + DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out), +}; + +/* only for model 039 */ +static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = { + DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass), +}; + +/* card control switches */ +static snd_kcontrol_new_t snd_cmipci_control_switches[] __devinitdata = { + DEFINE_CARD_SWITCH("Joystick", joystick), + DEFINE_CARD_SWITCH("Modem", modem), +}; + + +static int __devinit snd_cmipci_mixer_new(cmipci_t *cm, int pcm_spdif_device) +{ + unsigned long flags; + snd_card_t *card; + snd_kcontrol_new_t *sw; + snd_kcontrol_t *kctl; + int idx, err; + + snd_assert(cm != NULL && cm->card != NULL, return -EINVAL); + + card = cm->card; + + strcpy(card->mixername, "CMedia PCI"); + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */ + spin_unlock_irqrestore(&cm->reg_lock, flags); + + for (idx = 0; idx < num_controls(snd_cmipci_mixers); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm))) < 0) + return err; + } + + /* mixer switches */ + sw = snd_cmipci_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + if (cm->device == PCI_DEVICE_ID_CMEDIA_CM8738 || + cm->device == PCI_DEVICE_ID_CMEDIA_CM8738B) { + sw = snd_cmipci_8738_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_8738_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + cm->spdif_pcm_ctl = kctl; + if (cm->chip_version <= 37) { + sw = snd_cmipci_old_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_extra_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + } + } + if (cm->chip_version >= 39) { + sw = snd_cmipci_extra_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_extra_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + } + + /* card switches */ + sw = snd_cmipci_control_switches; + for (idx = 0; idx < num_controls(snd_cmipci_control_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + + for (idx = 0; idx < CM_SAVED_MIXERS; idx++) { + snd_ctl_elem_id_t id; + snd_kcontrol_t *ctl; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, cm_saved_mixer[idx].name); + if ((ctl = snd_ctl_find_id(cm->card, &id)) != NULL) + cm->mixer_res_ctl[idx] = ctl; + } + + return 0; +} + + +/* + * proc interface + */ + +static void snd_cmipci_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) +{ + cmipci_t *cm = snd_magic_cast(cmipci_t, entry->private_data, return); + int i; + + snd_iprintf(buffer, "%s\n\n", cm->card->longname); + for (i = 0; i < 0x40; i++) { + int v = inb(cm->iobase + i); + if (i % 4 == 0) + snd_iprintf(buffer, "%02x: ", i); + snd_iprintf(buffer, "%02x", v); + if (i % 4 == 3) + snd_iprintf(buffer, "\n"); + else + snd_iprintf(buffer, " "); + } +} + +static void __devinit snd_cmipci_proc_init(cmipci_t *cm) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(cm->card, "cmipci", cm->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = cm; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_cmipci_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + cm->proc_entry = entry; +} + +static void snd_cmipci_proc_done(cmipci_t *cm) +{ + if (cm->proc_entry) { + snd_info_unregister(cm->proc_entry); + cm->proc_entry = NULL; + } +} + + +static struct pci_device_id snd_cmipci_ids[] __devinitdata = { + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_AL, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,}, +}; + + +/* + * check chip version and capabilities + * driver name is modified according to the chip model + */ +static void __devinit query_chip(cmipci_t *cm) +{ + unsigned int detect; + + /* check reg 0Ch, bit 24-31 */ + detect = snd_cmipci_read(cm, CM_REG_INT_HLDCLR) & CM_CHIP_MASK2; + if (! detect) { + /* check reg 08h, bit 24-28 */ + detect = snd_cmipci_read(cm, CM_REG_CHFORMAT) & CM_CHIP_MASK1; + if (! detect) { + cm->chip_version = 33; + cm->max_channels = 2; + cm->can_ac3_sw = 1; + cm->has_dual_dac = 1; + } else { + cm->chip_version = 37; + cm->max_channels = 2; + cm->can_ac3_hw = 1; + cm->has_dual_dac = 1; + } + } else { + /* check reg 0Ch, bit 26 */ + if (detect & CM_CHIP_039) { + cm->chip_version = 39; + if (detect & CM_CHIP_039_6CH) + cm->max_channels = 6; + else + cm->max_channels = 4; + cm->can_ac3_hw = 1; + cm->has_dual_dac = 1; + cm->can_multi_ch = 1; + } else { + cm->chip_version = 55; /* 4 or 6 channels */ + cm->max_channels = 6; + cm->can_ac3_hw = 1; + cm->has_dual_dac = 1; + cm->can_multi_ch = 1; + } + } + + /* added -MCx suffix for chip supporting multi-channels */ + if (cm->can_multi_ch) + sprintf(cm->card->driver + strlen(cm->card->driver), + "-MC%d", cm->max_channels); +} + + +static int snd_cmipci_free(cmipci_t *cm) +{ + snd_cmipci_proc_done(cm); + + if (cm->irq >= 0) { + snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */ + snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */ + + /* reset mixer */ + snd_cmipci_mixer_write(cm, 0, 0); + + synchronize_irq(); + + free_irq(cm->irq, (void *)cm); + } + if (cm->res_iobase) { + release_resource(cm->res_iobase); + kfree_nocheck(cm->res_iobase); + } + snd_magic_kfree(cm); + return 0; +} + +static int snd_cmipci_dev_free(snd_device_t *device) +{ + cmipci_t *cm = snd_magic_cast(cmipci_t, device->device_data, return -ENXIO); + return snd_cmipci_free(cm); +} + +static int __devinit snd_cmipci_create(snd_card_t *card, + struct pci_dev *pci, + unsigned long iomidi, + unsigned long iosynth, + cmipci_t **rcmipci) +{ + cmipci_t *cm; + int err; + static snd_device_ops_t ops = { + dev_free: snd_cmipci_dev_free, + }; + unsigned int val = 0; + int pcm_index, pcm_spdif_index; + + *rcmipci = NULL; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + cm = snd_magic_kcalloc(cmipci_t, 0, GFP_KERNEL); + if (cm == NULL) + return -ENOMEM; + + spin_lock_init(&cm->reg_lock); + init_MUTEX(&cm->open_mutex); + cm->device = pci->device; + cm->card = card; + cm->pci = pci; + cm->irq = -1; + cm->iobase = pci_resource_start(pci, 0); + cm->channel[0].ch = 0; + cm->channel[1].ch = 1; + cm->channel[0].is_dac = cm->channel[1].is_dac = 1; /* dual DAC mode */ + + if ((cm->res_iobase = request_region(cm->iobase, CM_EXTENT_CODEC, card->driver)) == NULL) { + snd_printk("unable to grab ports 0x%lx-0x%lx\n", cm->iobase, cm->iobase + CM_EXTENT_CODEC - 1); + err = -EBUSY; + goto __error; + } + if (request_irq(pci->irq, snd_cmipci_interrupt, SA_INTERRUPT|SA_SHIRQ, card->driver, (void *)cm)) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + err = -EBUSY; + goto __error; + } + cm->irq = pci->irq; + + pci_set_master(cm->pci); + + + /* + * check chip version, max channels and capabilities + */ + + cm->chip_version = 0; + cm->max_channels = 2; + + query_chip(cm); + + cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF; + cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF; + +#if CM_CH_PLAY == 1 + cm->ctrl = CM_CHADC0; /* default FUNCNTRL0 */ +#else + cm->ctrl = CM_CHADC1; /* default FUNCNTRL0 */ +#endif + + /* initialize codec registers */ + snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */ + snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */ + snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0); + + snd_cmipci_write(cm, CM_REG_CHFORMAT, 0); + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC|CM_N4SPK3D); +#if CM_CH_PLAY == 1 + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); +#else + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); +#endif + + /* set MPU address */ + switch (iomidi) { + case 0x320: val = CM_VMPU_320; break; + case 0x310: val = CM_VMPU_310; break; + case 0x300: val = CM_VMPU_300; break; + case 0x330: val = CM_VMPU_330; break; + default: + iomidi = 0; break; + } + if (iomidi > 0) { + snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val); + /* enable UART */ + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_UART_EN); + } + + /* set FM address */ + val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL) & ~CM_FMSEL_MASK; + switch (iosynth) { + case 0x3E8: val |= CM_FMSEL_3E8; break; + case 0x3E0: val |= CM_FMSEL_3E0; break; + case 0x3C8: val |= CM_FMSEL_3C8; break; + case 0x388: val |= CM_FMSEL_388; break; + default: + iosynth = 0; break; + } + if (iosynth > 0) { + snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val); + /* enable FM */ + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); + + if (snd_opl3_create(card, iosynth, iosynth + 2, + OPL3_HW_OPL3, 0, &cm->opl3) < 0) { + snd_printk("no OPL device at 0x%lx\n", iosynth); + } else { + if ((err = snd_opl3_hwdep_new(cm->opl3, 0, 1, &cm->opl3hwdep)) < 0) + snd_printk("cannot create OPL3 hwdep\n"); + } + } + + /* reset mixer */ + snd_cmipci_mixer_write(cm, 0, 0); + + snd_cmipci_proc_init(cm); + + /* create pcm devices */ + pcm_index = pcm_spdif_index = 0; + if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0) + goto __error; + pcm_index++; + if (cm->has_dual_dac) { + if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0) + goto __error; + pcm_index++; + } + if (cm->can_ac3_hw || cm->can_ac3_sw) { + pcm_spdif_index = pcm_index; + if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0) + goto __error; + } + + /* create mixer interface & switches */ + if ((err = snd_cmipci_mixer_new(cm, pcm_spdif_index)) < 0) + goto __error; + + if (iomidi > 0) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, + iomidi, 0, + cm->irq, 0, &cm->rmidi)) < 0) { + snd_printk("cmipci: no UART401 device at 0x%lx\n", iomidi); + } + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) { + snd_cmipci_free(cm); + return err; + } + + *rcmipci = cm; + return 0; + + __error: + snd_cmipci_free(cm); + return err; +} + +/* + */ + +MODULE_DEVICE_TABLE(pci, snd_cmipci_ids); + +static int __devinit snd_cmipci_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + cmipci_t *cm; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (! snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (pci->device) { + case PCI_DEVICE_ID_CMEDIA_CM8738: + case PCI_DEVICE_ID_CMEDIA_CM8738B: + strcpy(card->driver, "CMI8738"); + break; + case PCI_DEVICE_ID_CMEDIA_CM8338A: + case PCI_DEVICE_ID_CMEDIA_CM8338B: + strcpy(card->driver, "CMI8338"); + break; + default: + strcpy(card->driver, "CMIPCI"); + break; + } + + if ((err = snd_cmipci_create(card, pci, + snd_mpu_port[dev], + snd_fm_port[dev], + &cm)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->shortname, "C-Media PCI %s", card->driver); + sprintf(card->longname, "%s (model %d) at 0x%lx, irq %i", + card->shortname, + cm->chip_version, + cm->iobase, + cm->irq); + + //snd_printd("%s is detected\n", card->longname); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; + +} + +static void __devexit snd_cmipci_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + + +static struct pci_driver driver = { + name: "C-Media PCI", + id_table: snd_cmipci_ids, + probe: snd_cmipci_probe, + remove: __devexit_p(snd_cmipci_remove), +}; + +static int __init alsa_card_cmipci_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("C-Media PCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_cmipci_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_cmipci_init) +module_exit(alsa_card_cmipci_exit) + +#ifndef MODULE + +/* format is: snd-cmipci=snd_enable,snd_index,snd_id, + snd_mpu_port,snd_fm_port */ + +static int __init alsa_card_cmipci_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cmipci=", alsa_card_cmipci_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/cs4281.c b/sound/pci/cs4281.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/cs4281.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1947 @@ +/* + * Driver for Cirrus Logic CS4281 based PCI soundcard + * Copyright (c) by Jaroslav Kysela , + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#ifndef LINUX_2_2 +#if defined(CONFIG_INPUT_GAMEPORT) || defined(CONFIG_INPUT_GAMEPORT_MODULE) +#define HAVE_GAMEPORT_SUPPORT +#endif +#endif + +#ifdef HAVE_GAMEPORT_SUPPORT +#include +#endif + + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Cirrus Logic CS4281"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Cirrus Logic,CS4281}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for CS4281 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for CS4281 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable CS4281 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); + +/* + * + */ + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4281 +#define PCI_DEVICE_ID_CIRRUS_4281 0x6005 +#endif + +/* + * Direct registers + */ + +#define CS4281_BA0_SIZE 0x1000 +#define CS4281_BA1_SIZE 0x10000 + +/* + * BA0 registers + */ +#define BA0_HISR 0x0000 /* Host Interrupt Status Register */ +#define BA0_HISR_INTENA (1<<31) /* Internal Interrupt Enable Bit */ +#define BA0_HISR_MIDI (1<<22) /* MIDI port interrupt */ +#define BA0_HISR_FIFOI (1<<20) /* FIFO polled interrupt */ +#define BA0_HISR_DMAI (1<<18) /* DMA interrupt (half or end) */ +#define BA0_HISR_FIFO(c) (1<<(12+(c))) /* FIFO channel interupt */ +#define BA0_HISR_DMA(c) (1<<(8+(c))) /* DMA channel interrupt */ +#define BA0_HISR_GPPI (1<<5) /* General Purpose Input (Primary chip) */ +#define BA0_HISR_GPSI (1<<4) /* General Purpose Input (Secondary chip) */ +#define BA0_HISR_GP3I (1<<3) /* GPIO3 pin Interrupt */ +#define BA0_HISR_GP1I (1<<2) /* GPIO1 pin Interrupt */ +#define BA0_HISR_VUPI (1<<1) /* VOLUP pin Interrupt */ +#define BA0_HISR_VDNI (1<<0) /* VOLDN pin Interrupt */ + +#define BA0_HICR 0x0008 /* Host Interrupt Control Register */ +#define BA0_HICR_CHGM (1<<1) /* INTENA Change Mask */ +#define BA0_HICR_IEV (1<<0) /* INTENA Value */ +#define BA0_HICR_EOI (3<<0) /* End of Interrupt command */ + +#define BA0_HIMR 0x000c /* Host Interrupt Mask Register */ + /* Use same contants as for BA0_HISR */ + +#define BA0_IIER 0x0010 /* ISA Interrupt Enable Register */ + +#define BA0_HDSR0 0x00f0 /* Host DMA Engine 0 Status Register */ +#define BA0_HDSR1 0x00f4 /* Host DMA Engine 1 Status Register */ +#define BA0_HDSR2 0x00f8 /* Host DMA Engine 2 Status Register */ +#define BA0_HDSR3 0x00fc /* Host DMA Engine 3 Status Register */ + +#define BA0_HDSR_CH1P (1<<25) /* Channel 1 Pending */ +#define BA0_HDSR_CH2P (1<<24) /* Channel 2 Pending */ +#define BA0_HDSR_DHTC (1<<17) /* DMA Half Terminal Count */ +#define BA0_HDSR_DTC (1<<16) /* DMA Terminal Count */ +#define BA0_HDSR_DRUN (1<<15) /* DMA Running */ +#define BA0_HDSR_RQ (1<<7) /* Pending Request */ + +#define BA0_DCA0 0x0110 /* Host DMA Engine 0 Current Address */ +#define BA0_DCC0 0x0114 /* Host DMA Engine 0 Current Count */ +#define BA0_DBA0 0x0118 /* Host DMA Engine 0 Base Address */ +#define BA0_DBC0 0x011c /* Host DMA Engine 0 Base Count */ +#define BA0_DCA1 0x0120 /* Host DMA Engine 1 Current Address */ +#define BA0_DCC1 0x0124 /* Host DMA Engine 1 Current Count */ +#define BA0_DBA1 0x0128 /* Host DMA Engine 1 Base Address */ +#define BA0_DBC1 0x012c /* Host DMA Engine 1 Base Count */ +#define BA0_DCA2 0x0130 /* Host DMA Engine 2 Current Address */ +#define BA0_DCC2 0x0134 /* Host DMA Engine 2 Current Count */ +#define BA0_DBA2 0x0138 /* Host DMA Engine 2 Base Address */ +#define BA0_DBC2 0x013c /* Host DMA Engine 2 Base Count */ +#define BA0_DCA3 0x0140 /* Host DMA Engine 3 Current Address */ +#define BA0_DCC3 0x0144 /* Host DMA Engine 3 Current Count */ +#define BA0_DBA3 0x0148 /* Host DMA Engine 3 Base Address */ +#define BA0_DBC3 0x014c /* Host DMA Engine 3 Base Count */ +#define BA0_DMR0 0x0150 /* Host DMA Engine 0 Mode */ +#define BA0_DCR0 0x0154 /* Host DMA Engine 0 Command */ +#define BA0_DMR1 0x0158 /* Host DMA Engine 1 Mode */ +#define BA0_DCR1 0x015c /* Host DMA Engine 1 Command */ +#define BA0_DMR2 0x0160 /* Host DMA Engine 2 Mode */ +#define BA0_DCR2 0x0164 /* Host DMA Engine 2 Command */ +#define BA0_DMR3 0x0168 /* Host DMA Engine 3 Mode */ +#define BA0_DCR3 0x016c /* Host DMA Engine 3 Command */ + +#define BA0_DMR_DMA (1<<29) /* Enable DMA mode */ +#define BA0_DMR_POLL (1<<28) /* Enable poll mode */ +#define BA0_DMR_TBC (1<<25) /* Transfer By Channel */ +#define BA0_DMR_CBC (1<<24) /* Count By Channel (0 = frame resolution) */ +#define BA0_DMR_SWAPC (1<<22) /* Swap Left/Right Channels */ +#define BA0_DMR_SIZE20 (1<<20) /* Sample is 20-bit */ +#define BA0_DMR_USIGN (1<<19) /* Unsigned */ +#define BA0_DMR_BEND (1<<18) /* Big Endian */ +#define BA0_DMR_MONO (1<<17) /* Mono */ +#define BA0_DMR_SIZE8 (1<<16) /* Sample is 8-bit */ +#define BA0_DMR_TYPE_DEMAND (0<<6) +#define BA0_DMR_TYPE_SINGLE (1<<6) +#define BA0_DMR_TYPE_BLOCK (2<<6) +#define BA0_DMR_TYPE_CASCADE (3<<6) /* Not supported */ +#define BA0_DMR_DEC (1<<5) /* Access Increment (0) or Decrement (1) */ +#define BA0_DMR_AUTO (1<<4) /* Auto-Initialize */ +#define BA0_DMR_TR_VERIFY (0<<2) /* Verify Transfer */ +#define BA0_DMR_TR_WRITE (1<<2) /* Write Transfer */ +#define BA0_DMR_TR_READ (2<<2) /* Read Transfer */ + +#define BA0_DCR_HTCIE (1<<17) /* Half Terminal Count Interrupt */ +#define BA0_DCR_TCIE (1<<16) /* Terminal Count Interrupt */ +#define BA0_DCR_MSK (1<<0) /* DMA Mask bit */ + +#define BA0_FCR0 0x0180 /* FIFO Control 0 */ +#define BA0_FCR1 0x0184 /* FIFO Control 1 */ +#define BA0_FCR2 0x0188 /* FIFO Control 2 */ +#define BA0_FCR3 0x018c /* FIFO Control 3 */ + +#define BA0_FCR_FEN (1<<31) /* FIFO Enable bit */ +#define BA0_FCR_DACZ (1<<30) /* DAC Zero */ +#define BA0_FCR_PSH (1<<29) /* Previous Sample Hold */ +#define BA0_FCR_RS(x) (((x)&0x1f)<<24) /* Right Slot Mapping */ +#define BA0_FCR_LS(x) (((x)&0x1f)<<16) /* Left Slot Mapping */ +#define BA0_FCR_SZ(x) (((x)&0x7f)<<8) /* FIFO buffer size (in samples) */ +#define BA0_FCR_OF(x) (((x)&0x7f)<<0) /* FIFO starting offset (in samples) */ + +#define BA0_FPDR0 0x0190 /* FIFO Polled Data 0 */ +#define BA0_FPDR1 0x0194 /* FIFO Polled Data 1 */ +#define BA0_FPDR2 0x0198 /* FIFO Polled Data 2 */ +#define BA0_FPDR3 0x019c /* FIFO Polled Data 3 */ + +#define BA0_FCHS 0x020c /* FIFO Channel Status */ +#define BA0_FCHS_RCO(x) (1<<(7+(((x)&3)<<3))) /* Right Channel Out */ +#define BA0_FCHS_LCO(x) (1<<(6+(((x)&3)<<3))) /* Left Channel Out */ +#define BA0_FCHS_MRP(x) (1<<(5+(((x)&3)<<3))) /* Move Read Pointer */ +#define BA0_FCHS_FE(x) (1<<(4+(((x)&3)<<3))) /* FIFO Empty */ +#define BA0_FCHS_FF(x) (1<<(3+(((x)&3)<<3))) /* FIFO Full */ +#define BA0_FCHS_IOR(x) (1<<(2+(((x)&3)<<3))) /* Internal Overrun Flag */ +#define BA0_FCHS_RCI(x) (1<<(1+(((x)&3)<<3))) /* Right Channel In */ +#define BA0_FCHS_LCI(x) (1<<(0+(((x)&3)<<3))) /* Left Channel In */ + +#define BA0_FSIC0 0x0210 /* FIFO Status and Interrupt Control 0 */ +#define BA0_FSIC1 0x0214 /* FIFO Status and Interrupt Control 1 */ +#define BA0_FSIC2 0x0218 /* FIFO Status and Interrupt Control 2 */ +#define BA0_FSIC3 0x021c /* FIFO Status and Interrupt Control 3 */ + +#define BA0_FSIC_FIC(x) (((x)&0x7f)<<24) /* FIFO Interrupt Count */ +#define BA0_FSIC_FORIE (1<<23) /* FIFO OverRun Interrupt Enable */ +#define BA0_FSIC_FURIE (1<<22) /* FIFO UnderRun Interrupt Enable */ +#define BA0_FSIC_FSCIE (1<<16) /* FIFO Sample Count Interrupt Enable */ +#define BA0_FSIC_FSC(x) (((x)&0x7f)<<8) /* FIFO Sample Count */ +#define BA0_FSIC_FOR (1<<7) /* FIFO OverRun */ +#define BA0_FSIC_FUR (1<<6) /* FIFO UnderRun */ +#define BA0_FSIC_FSCR (1<<0) /* FIFO Sample Count Reached */ + +#define BA0_PMCS 0x0344 /* Power Management Control/Status */ +#define BA0_CWPR 0x03e0 /* Configuration Write Protect */ +#define BA0_EPPMC 0x03e4 /* Extended PCI Power Management Control */ +#define BA0_GPIOR 0x03e8 /* GPIO Pin Interface Register */ + +#define BA0_SPMC 0x03ec /* Serial Port Power Management Control (& ASDIN2 enable) */ +#define BA0_SPMC_GIPPEN (1<<15) /* GP INT Primary PME# Enable */ +#define BA0_SPMC_GISPEN (1<<14) /* GP INT Secondary PME# Enable */ +#define BA0_SPMC_EESPD (1<<9) /* EEPROM Serial Port Disable */ +#define BA0_SPMC_ASDI2E (1<<8) /* ASDIN2 Enable */ +#define BA0_SPMC_ASDO (1<<7) /* Asynchronous ASDOUT Assertion */ +#define BA0_SPMC_WUP2 (1<<3) /* Wakeup for Secondary Input */ +#define BA0_SPMC_WUP1 (1<<2) /* Wakeup for Primary Input */ +#define BA0_SPMC_ASYNC (1<<1) /* Asynchronous ASYNC Assertion */ +#define BA0_SPMC_RSTN (1<<0) /* Reset Not! */ + +#define BA0_CFLR 0x03f0 /* Configuration Load Register (EEPROM or BIOS) */ +#define BA0_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */ +#define BA0_IISR 0x03f4 /* ISA Interrupt Select */ +#define BA0_TMS 0x03f8 /* Test Register */ +#define BA0_SSVID 0x03fc /* Subsystem ID register */ + +#define BA0_CLKCR1 0x0400 /* Clock Control Register 1 */ +#define BA0_CLKCR1_CLKON (1<<25) /* Read Only */ +#define BA0_CLKCR1_DLLRDY (1<<24) /* DLL Ready */ +#define BA0_CLKCR1_DLLOS (1<<6) /* DLL Output Select */ +#define BA0_CLKCR1_SWCE (1<<5) /* Clock Enable */ +#define BA0_CLKCR1_DLLP (1<<4) /* DLL PowerUp */ +#define BA0_CLKCR1_DLLSS (((x)&3)<<3) /* DLL Source Select */ + +#define BA0_FRR 0x0410 /* Feature Reporting Register */ +#define BA0_SLT12O 0x041c /* Slot 12 GPIO Output Register for AC-Link */ + +#define BA0_SERMC 0x0420 /* Serial Port Master Control */ +#define BA0_SERMC_FCRN (1<<27) /* Force Codec Ready Not */ +#define BA0_SERMC_ODSEN2 (1<<25) /* On-Demand Support Enable ASDIN2 */ +#define BA0_SERMC_ODSEN1 (1<<24) /* On-Demand Support Enable ASDIN1 */ +#define BA0_SERMC_SXLB (1<<21) /* ASDIN2 to ASDOUT Loopback */ +#define BA0_SERMC_SLB (1<<20) /* ASDOUT to ASDIN2 Loopback */ +#define BA0_SERMC_LOVF (1<<19) /* Loopback Output Valid Frame bit */ +#define BA0_SERMC_TCID(x) (((x)&3)<<16) /* Target Secondary Codec ID */ +#define BA0_SERMC_PXLB (5<<1) /* Primary Port External Loopback */ +#define BA0_SERMC_PLB (4<<1) /* Primary Port Internal Loopback */ +#define BA0_SERMC_PTC (7<<1) /* Port Timing Configuration */ +#define BA0_SERMC_PTC_AC97 (1<<1) /* AC97 mode */ +#define BA0_SERMC_MSPE (1<<0) /* Master Serial Port Enable */ + +#define BA0_SERC1 0x0428 /* Serial Port Configuration 1 */ +#define BA0_SERC1_SO1F(x) (((x)&7)>>1) /* Primary Output Port Format */ +#define BA0_SERC1_AC97 (1<<1) +#define BA0_SERC1_SO1EN (1<<0) /* Primary Output Port Enable */ + +#define BA0_SERC2 0x042c /* Serial Port Configuration 2 */ +#define BA0_SERC2_SI1F(x) (((x)&7)>>1) */ Primary Input Port Format */ +#define BA0_SERC2_AC97 (1<<1) +#define BA0_SERC2_SI1EN (1<<0) /* Primary Input Port Enable */ + +#define BA0_SLT12M 0x045c /* Slot 12 Monitor Register for Primary AC-Link */ + +#define BA0_ACCTL 0x0460 /* AC'97 Control */ +#define BA0_ACCTL_TC (1<<6) /* Target Codec */ +#define BA0_ACCTL_CRW (1<<4) /* 0=Write, 1=Read Command */ +#define BA0_ACCTL_DCV (1<<3) /* Dynamic Command Valid */ +#define BA0_ACCTL_VFRM (1<<2) /* Valid Frame */ +#define BA0_ACCTL_ESYN (1<<1) /* Enable Sync */ + +#define BA0_ACSTS 0x0464 /* AC'97 Status */ +#define BA0_ACSTS_VSTS (1<<1) /* Valid Status */ +#define BA0_ACSTS_CRDY (1<<0) /* Codec Ready */ + +#define BA0_ACOSV 0x0468 /* AC'97 Output Slot Valid */ +#define BA0_ACOSV_SLV(x) (1<<((x)-3)) + +#define BA0_ACCAD 0x046c /* AC'97 Command Address */ +#define BA0_ACCDA 0x0470 /* AC'97 Command Data */ + +#define BA0_ACISV 0x0474 /* AC'97 Input Slot Valid */ +#define BA0_ACISV_SLV(x) (1<<((x)-3)) + +#define BA0_ACSAD 0x0478 /* AC'97 Status Address */ +#define BA0_ACSDA 0x047c /* AC'97 Status Data */ +#define BA0_JSPT 0x0480 /* Joystick poll/trigger */ +#define BA0_JSCTL 0x0484 /* Joystick control */ +#define BA0_JSC1 0x0488 /* Joystick control */ +#define BA0_JSC2 0x048c /* Joystick control */ +#define BA0_JSIO 0x04a0 + +#define BA0_MIDCR 0x0490 /* MIDI Control */ +#define BA0_MIDCR_MRST (1<<5) /* Reset MIDI Interface */ +#define BA0_MIDCR_MLB (1<<4) /* MIDI Loop Back Enable */ +#define BA0_MIDCR_TIE (1<<3) /* MIDI Transmuit Interrupt Enable */ +#define BA0_MIDCR_RIE (1<<2) /* MIDI Receive Interrupt Enable */ +#define BA0_MIDCR_RXE (1<<1) /* MIDI Receive Enable */ +#define BA0_MIDCR_TXE (1<<0) /* MIDI Transmit Enable */ + +#define BA0_MIDCMD 0x0494 /* MIDI Command (wo) */ + +#define BA0_MIDSR 0x0494 /* MIDI Status (ro) */ +#define BA0_MIDSR_RDA (1<<15) /* Sticky bit (RBE 1->0) */ +#define BA0_MIDSR_TBE (1<<14) /* Sticky bit (TBF 0->1) */ +#define BA0_MIDSR_RBE (1<<7) /* Receive Buffer Empty */ +#define BA0_MIDSR_TBF (1<<6) /* Transmit Buffer Full */ + +#define BA0_MIDWP 0x0498 /* MIDI Write */ +#define BA0_MIDRP 0x049c /* MIDI Read (ro) */ + +#define BA0_AODSD1 0x04a8 /* AC'97 On-Demand Slot Disable for primary link (ro) */ +#define BA0_AODSD1_NDS(x) (1<<((x)-3)) + +#define BA0_AODSD2 0x04ac /* AC'97 On-Demand Slot Disable for secondary link (ro) */ +#define BA0_AODSD2_NDS(x) (1<<((x)-3)) + +#define BA0_CFGI 0x04b0 /* Configure Interface (EEPROM interface) */ +#define BA0_SLT12M2 0x04dc /* Slot 12 Monitor Register 2 for secondary AC-link */ +#define BA0_ACSTS2 0x04e4 /* AC'97 Status Register 2 */ +#define BA0_ACISV2 0x04f4 /* AC'97 Input Slot Valid Register 2 */ +#define BA0_ACSAD2 0x04f8 /* AC'97 Status Address Register 2 */ +#define BA0_ACSDA2 0x04fc /* AC'97 Status Data Register 2 */ +#define BA0_FMSR 0x0730 /* FM Synthesis Status (ro) */ +#define BA0_B0AP 0x0730 /* FM Bank 0 Address Port (wo) */ +#define BA0_FMDP 0x0734 /* FM Data Port */ +#define BA0_B1AP 0x0738 /* FM Bank 1 Address Port */ +#define BA0_B1DP 0x073c /* FM Bank 1 Data Port */ + +#define BA0_SSPM 0x0740 /* Sound System Power Management */ +#define BA0_SSPM_MIXEN (1<<6) /* Playback SRC + FM/Wavetable MIX */ +#define BA0_SSPM_CSRCEN (1<<5) /* Capture Sample Rate Converter Enable */ +#define BA0_SSPM_PSRCEN (1<<4) /* Playback Sample Rate Converter Enable */ +#define BA0_SSPM_JSEN (1<<3) /* Joystick Enable */ +#define BA0_SSPM_ACLEN (1<<2) /* Serial Port Engine and AC-Link Enable */ +#define BA0_SSPM_FMEN (1<<1) /* FM Synthesis Block Enable */ + +#define BA0_DACSR 0x0744 /* DAC Sample Rate - Playback SRC */ +#define BA0_ADCSR 0x0748 /* ADC Sample Rate - Capture SRC */ + +#define BA0_SSCR 0x074c /* Sound System Control Register */ +#define BA0_SSCR_HVS1 (1<<23) /* Hardwave Volume Step (0=1,1=2) */ +#define BA0_SSCR_MVCS (1<<19) /* Master Volume Codec Select */ +#define BA0_SSCR_MVLD (1<<18) /* Master Volume Line Out Disable */ +#define BA0_SSCR_MVAD (1<<17) /* Master Volume Alternate Out Disable */ +#define BA0_SSCR_MVMD (1<<16) /* Master Volume Mono Out Disable */ +#define BA0_SSCR_XLPSRC (1<<8) /* External SRC Loopback Mode */ +#define BA0_SSCR_LPSRC (1<<7) /* SRC Loopback Mode */ +#define BA0_SSCR_CDTX (1<<5) /* CD Transfer Data */ +#define BA0_SSCR_HVC (1<<3) /* Harware Volume Control Enable */ + +#define BA0_FMLVC 0x0754 /* FM Synthesis Left Volume Control */ +#define BA0_FMRVC 0x0758 /* FM Synthesis Right Volume Control */ +#define BA0_SRCSA 0x075c /* SRC Slot Assignments */ +#define BA0_PPLVC 0x0760 /* PCM Playback Left Volume Control */ +#define BA0_PPRVC 0x0764 /* PCM Playback Right Volume Control */ + +/* Source Slot Numbers - Playback */ +#define SRCSLOT_LEFT_PCM_PLAYBACK 0 +#define SRCSLOT_RIGHT_PCM_PLAYBACK 1 +#define SRCSLOT_PHONE_LINE_1_DAC 2 +#define SRCSLOT_CENTER_PCM_PLAYBACK 3 +#define SRCSLOT_LEFT_SURROUND_PCM_PLAYBACK 4 +#define SRCSLOT_RIGHT_SURROUND_PCM_PLAYBACK 5 +#define SRCSLOT_LFE_PCM_PLAYBACK 6 +#define SRCSLOT_PHONE_LINE_2_DAC 7 +#define SRCSLOT_HEADSET_DAC 8 +#define SRCSLOT_LEFT_WT 29 /* invalid for BA0_SRCSA */ +#define SRCSLOT_RIGHT_WT 30 /* invalid for BA0_SRCSA */ + +/* Source Slot Numbers - Capture */ +#define SRCSLOT_LEFT_PCM_RECORD 10 +#define SRCSLOT_RIGHT_PCM_RECORD 11 +#define SRCSLOT_PHONE_LINE_1_ADC 12 +#define SRCSLOT_MIC_ADC 13 +#define SRCSLOT_PHONE_LINE_2_ADC 17 +#define SRCSLOT_HEADSET_ADC 18 +#define SRCSLOT_SECONDARY_LEFT_PCM_RECORD 20 +#define SRCSLOT_SECONDARY_RIGHT_PCM_RECORD 21 +#define SRCSLOT_SECONDARY_PHONE_LINE_1_ADC 22 +#define SRCSLOT_SECONDARY_MIC_ADC 23 +#define SRCSLOT_SECONDARY_PHONE_LINE_2_ADC 27 +#define SRCSLOT_SECONDARY_HEADSET_ADC 28 + +/* Source Slot Numbers - Others */ +#define SRCSLOT_POWER_DOWN 31 + +/* MIDI modes */ +#define CS4281_MODE_OUTPUT (1<<0) +#define CS4281_MODE_INPUT (1<<1) + +/* joystick bits */ +/* Bits for JSPT */ +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* Bits for JSCTL */ +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* Data register pairs masks */ +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* JS GPIO */ +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + * + */ + +#define chip_t cs4281_t + +typedef struct snd_cs4281 cs4281_t; +typedef struct snd_cs4281_dma cs4281_dma_t; + +struct snd_cs4281_dma { + snd_pcm_substream_t *substream; + unsigned int regDBA; /* offset to DBA register */ + unsigned int regDCA; /* offset to DCA register */ + unsigned int regDBC; /* offset to DBC register */ + unsigned int regDCC; /* offset to DCC register */ + unsigned int regDMR; /* offset to DMR register */ + unsigned int regDCR; /* offset to DCR register */ + unsigned int regHDSR; /* offset to HDSR register */ + unsigned int regFCR; /* offset to FCR register */ + unsigned int regFSIC; /* offset to FSIC register */ + unsigned int valDMR; /* DMA mode */ + unsigned int valDCR; /* DMA command */ + unsigned int valFCR; /* FIFO control */ + unsigned int fifo_offset; /* FIFO offset within BA1 */ + unsigned char left_slot; /* FIFO left slot */ + unsigned char right_slot; /* FIFO right slot */ + int frag; /* period number */ +}; + +#ifdef HAVE_GAMEPORT_SUPPORT +typedef struct snd_cs4281_gameport { + struct gameport info; + cs4281_t *chip; +} cs4281_gameport_t; +#endif + +struct snd_cs4281 { + int irq; + + unsigned long ba0; /* virtual (accessible) address */ + unsigned long ba1; /* virtual (accessible) address */ + unsigned long ba0_addr; + unsigned long ba1_addr; + struct resource *ba0_res; + struct resource *ba1_res; + + ac97_t *ac97; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_input; + snd_rawmidi_substream_t *midi_output; + + cs4281_dma_t dma[4]; + + unsigned char src_left_play_slot; + unsigned char src_right_play_slot; + unsigned char src_left_rec_slot; + unsigned char src_right_rec_slot; + + unsigned int spurious_dhtc_irq; + unsigned int spurious_dtc_irq; + + void *proc_entry_BA0; + void *proc_entry_BA1; + + spinlock_t reg_lock; + unsigned int midcr; + unsigned int uartm; + snd_info_entry_t *proc_entry; + +#ifdef HAVE_GAMEPORT_SUPPORT + cs4281_gameport_t *gameport; +#endif +}; + +static void snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct pci_device_id snd_cs4281_ids[] __devinitdata = { + { 0x1013, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4281 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_cs4281_ids); + +/* + * constants + */ + +#define CS4281_FIFO_SIZE 32 + +/* + * common I/O routines + */ + +static void snd_cs4281_delay(unsigned int delay) +{ + if (delay > 999) { + signed long end_time; + delay = (delay * HZ) / 1000000; + if (delay < 1) + delay = 1; + end_time = jiffies + delay; + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + } else { + udelay(delay); + } +} + +static inline void snd_cs4281_pokeBA0(cs4281_t *chip, unsigned long offset, unsigned int val) +{ + writel(val, chip->ba0 + offset); +} + +static inline unsigned int snd_cs4281_peekBA0(cs4281_t *chip, unsigned long offset) +{ + return readl(chip->ba0 + offset); +} + +static void snd_cs4281_ac97_write(ac97_t *ac97, + unsigned short reg, unsigned short val) +{ + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h + * 5. if DCV not cleared, break and return error + */ + cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return); + int count; + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * reset CRW - Write command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs4281_pokeBA0(chip, BA0_ACCDA, val); + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_VFRM | + BA0_ACCTL_ESYN); + for (count = 0; count < 2000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the write has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 07h + */ + if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) { + return; + } + } + snd_printk("AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val); +} + +static unsigned short snd_cs4281_ac97_read(ac97_t *ac97, + unsigned short reg) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return -ENXIO); + int count; + unsigned short result; + + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h + * 5. if DCV not cleared, break and return error + * 6. Read ACSTS = Status Register = 464h, check VSTS bit + */ + + snd_cs4281_peekBA0(chip, BA0_ACSDA); + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * set CRW - Read command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + + snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs4281_pokeBA0(chip, BA0_ACCDA, 0); + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_CRW | + BA0_ACCTL_VFRM | BA0_ACCTL_ESYN); + + + /* + * Wait for the read to occur. + */ + for (count = 0; count < 500; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the read has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 17h + */ + if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) + goto __ok1; + } + + snd_printk("AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg); + result = 0xffff; + goto __end; + + __ok1: + /* + * Wait for the valid status bit to go active. + */ + for (count = 0; count < 100; count++) { + /* + * Read the AC97 status register. + * ACSTS = Status Register = 464h + * VSTS - Valid Status + */ + if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_VSTS) + goto __ok2; + udelay(10); + } + + snd_printk("AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg); + result = 0xffff; + goto __end; + + __ok2: + /* + * Read the data returned from the AC97 register. + * ACSDA = Status Data Register = 474h + */ + result = snd_cs4281_peekBA0(chip, BA0_ACSDA); + + __end: + return result; +} + +/* + * PCM part + */ + +static int snd_cs4281_trigger(snd_pcm_substream_t *substream, int cmd) +{ + cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dma->valDCR |= BA0_DCR_MSK; + dma->valFCR |= BA0_FCR_FEN; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dma->valDCR &= ~BA0_DCR_MSK; + dma->valFCR &= ~BA0_FCR_FEN; + break; + case SNDRV_PCM_TRIGGER_START: + snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR & ~BA0_DMR_DMA); + dma->valDMR |= BA0_DMR_DMA; + dma->valDCR &= ~BA0_DCR_MSK; + dma->valFCR |= BA0_FCR_FEN; + break; + case SNDRV_PCM_TRIGGER_STOP: + dma->valDMR &= ~(BA0_DMR_DMA|BA0_DMR_POLL); + dma->valDCR |= BA0_DCR_MSK; + dma->valFCR &= ~BA0_FCR_FEN; + break; + default: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -EINVAL; + } + snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR); + snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR); + snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate) +{ + unsigned int val = ~0; + + if (real_rate) + *real_rate = rate; + /* special "hardcoded" rates */ + switch (rate) { + case 8000: return 5; + case 11025: return 4; + case 16000: return 3; + case 22050: return 2; + case 44100: return 1; + case 48000: return 0; + default: + goto __variable; + } + __variable: + val = 1536000 / rate; + if (real_rate) + *real_rate = 1536000 / val; + return val; +} + +static void snd_cs4281_mode(cs4281_t *chip, cs4281_dma_t *dma, snd_pcm_runtime_t *runtime, int capture, int src) +{ + int rec_mono; + + dma->valDMR = BA0_DMR_TYPE_SINGLE | BA0_DMR_AUTO | + (capture ? BA0_DMR_TR_WRITE : BA0_DMR_TR_READ); + if (runtime->channels == 1) + dma->valDMR |= BA0_DMR_MONO; + if (snd_pcm_format_unsigned(runtime->format) > 0) + dma->valDMR |= BA0_DMR_USIGN; + if (snd_pcm_format_big_endian(runtime->format) > 0) + dma->valDMR |= BA0_DMR_BEND; + switch (snd_pcm_format_width(runtime->format)) { + case 8: dma->valDMR |= BA0_DMR_SIZE8; + if (runtime->channels == 1) + dma->valDMR |= BA0_DMR_SWAPC; + break; + case 32: dma->valDMR |= BA0_DMR_SIZE20; break; + } + dma->frag = 0; /* for workaround */ + dma->valDCR = BA0_DCR_TCIE | BA0_DCR_MSK; + if (runtime->buffer_size != runtime->period_size) + dma->valDCR |= BA0_DCR_HTCIE; + /* Initialize DMA */ + snd_cs4281_pokeBA0(chip, dma->regDBA, runtime->dma_addr); + snd_cs4281_pokeBA0(chip, dma->regDBC, runtime->buffer_size - 1); + rec_mono = (chip->dma[1].valDMR & BA0_DMR_MONO) == BA0_DMR_MONO; + snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) | + (chip->src_right_play_slot << 8) | + (chip->src_left_rec_slot << 16) | + ((rec_mono ? 31 : chip->src_right_rec_slot) << 24)); + if (!src) + goto __skip_src; + if (!capture) { + if (dma->left_slot == chip->src_left_play_slot) { + unsigned int val = snd_cs4281_rate(runtime->rate, NULL); + snd_assert(dma->right_slot == chip->src_right_play_slot, ); + snd_cs4281_pokeBA0(chip, BA0_DACSR, val); + } + } else { + if (dma->left_slot == chip->src_left_rec_slot) { + unsigned int val = snd_cs4281_rate(runtime->rate, NULL); + snd_assert(dma->right_slot == chip->src_right_rec_slot, ); + snd_cs4281_pokeBA0(chip, BA0_ADCSR, val); + } + } + __skip_src: + /* Initialize FIFO */ + dma->valFCR = BA0_FCR_LS(dma->left_slot) | + BA0_FCR_RS(capture && (dma->valDMR & BA0_DMR_MONO) ? 31 : dma->right_slot) | + BA0_FCR_SZ(CS4281_FIFO_SIZE) | + BA0_FCR_OF(dma->fifo_offset); + snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | (capture ? BA0_FCR_PSH : 0)); + /* Clear FIFO Status and Interrupt Control Register */ + snd_cs4281_pokeBA0(chip, dma->regFSIC, 0); +} + +static int snd_cs4281_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_cs4281_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_cs4281_playback_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4281_mode(chip, dma, runtime, 0, 1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_capture_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4281_mode(chip, dma, runtime, 1, 1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_cs4281_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + + // printk("DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n", snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size, jiffies); + return runtime->buffer_size - + snd_cs4281_peekBA0(chip, dma->regDCC); +} + +static snd_pcm_hardware_t snd_cs4281_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (512*1024), + period_bytes_min: 64, + period_bytes_max: (512*1024), + periods_min: 1, + periods_max: 2, + fifo_size: CS4281_FIFO_SIZE, +}; + +static snd_pcm_hardware_t snd_cs4281_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE | SNDRV_PCM_FMTBIT_S32_BE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (512*1024), + period_bytes_min: 64, + period_bytes_max: (512*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: CS4281_FIFO_SIZE, +}; + +static int snd_cs4281_playback_open(snd_pcm_substream_t * substream) +{ + cs4281_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma; + + dma = &chip->dma[0]; + dma->substream = substream; + dma->left_slot = 0; + dma->right_slot = 1; + runtime->private_data = dma; + runtime->hw = snd_cs4281_playback; + snd_pcm_set_sync(substream); + /* should be detected from the AC'97 layer, but it seems + that although CS4297A rev B reports 18-bit ADC resolution, + samples are 20-bit */ + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20); + return 0; +} + +static int snd_cs4281_capture_open(snd_pcm_substream_t * substream) +{ + cs4281_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma; + + dma = &chip->dma[1]; + dma->substream = substream; + dma->left_slot = 10; + dma->right_slot = 11; + runtime->private_data = dma; + runtime->hw = snd_cs4281_capture; + snd_pcm_set_sync(substream); + /* should be detected from the AC'97 layer, but it seems + that although CS4297A rev B reports 18-bit ADC resolution, + samples are 20-bit */ + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20); + return 0; +} + +static int snd_cs4281_playback_close(snd_pcm_substream_t * substream) +{ + cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data; + + dma->substream = NULL; + return 0; +} + +static int snd_cs4281_capture_close(snd_pcm_substream_t * substream) +{ + cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data; + + dma->substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_cs4281_playback_ops = { + open: snd_cs4281_playback_open, + close: snd_cs4281_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs4281_hw_params, + hw_free: snd_cs4281_hw_free, + prepare: snd_cs4281_playback_prepare, + trigger: snd_cs4281_trigger, + pointer: snd_cs4281_pointer, +}; + +static snd_pcm_ops_t snd_cs4281_capture_ops = { + open: snd_cs4281_capture_open, + close: snd_cs4281_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs4281_hw_params, + hw_free: snd_cs4281_hw_free, + prepare: snd_cs4281_capture_prepare, + trigger: snd_cs4281_trigger, + pointer: snd_cs4281_pointer, +}; + +static void snd_cs4281_pcm_free(snd_pcm_t *pcm) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_cs4281_pcm(cs4281_t * chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "CS4281", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4281_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4281_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_cs4281_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "CS4281"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 512*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer section + */ + +static void snd_cs4281_mixer_free_ac97(ac97_t *ac97) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +static int __devinit snd_cs4281_mixer(cs4281_t * chip) +{ + snd_card_t *card = chip->card; + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_cs4281_ac97_write; + ac97.read = snd_cs4281_ac97_read; + ac97.private_data = chip; + ac97.private_free = snd_cs4281_mixer_free_ac97; + if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97)) < 0) + return err; + return 0; +} + +/* + + */ + +static void snd_cs4281_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return); + + snd_iprintf(buffer, "Cirrus Logic CS4281\n\n"); + snd_iprintf(buffer, "Spurious half IRQs : %u\n", chip->spurious_dhtc_irq); + snd_iprintf(buffer, "Spurious end IRQs : %u\n", chip->spurious_dtc_irq); +} + +static long snd_cs4281_BA0_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return -ENXIO); + + size = count; + if (file->f_pos + size > CS4281_BA0_SIZE) + size = (long)CS4281_BA0_SIZE - file->f_pos; + if (size > 0) { + char *tmp; + long res; + unsigned long virt; + if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL) + return -ENOMEM; + virt = chip->ba0 + file->f_pos; + memcpy_fromio(tmp, virt, size); + if (copy_to_user(buf, tmp, size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static long snd_cs4281_BA1_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return -ENXIO); + + size = count; + if (file->f_pos + size > CS4281_BA1_SIZE) + size = (long)CS4281_BA1_SIZE - file->f_pos; + if (size > 0) { + char *tmp; + long res; + unsigned long virt; + if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL) + return -ENOMEM; + virt = chip->ba1 + file->f_pos; + memcpy_fromio(tmp, virt, size); + if (copy_to_user(buf, tmp, size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = { + read: snd_cs4281_BA0_read, +}; + +static struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = { + read: snd_cs4281_BA1_read, +}; + +static void __devinit snd_cs4281_proc_init(cs4281_t * chip) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(chip->card, "cs4281", chip->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = chip; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_cs4281_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + chip->proc_entry = entry; + if ((entry = snd_info_create_card_entry(chip->card, "cs4281_BA0", chip->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &snd_cs4281_proc_ops_BA0; + entry->size = CS4281_BA0_SIZE; + if (snd_info_register(entry) < 0) { + snd_info_unregister(entry); + entry = NULL; + } + } + chip->proc_entry_BA0 = entry; + if ((entry = snd_info_create_card_entry(chip->card, "cs4281_BA1", chip->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &snd_cs4281_proc_ops_BA1; + entry->size = CS4281_BA1_SIZE; + if (snd_info_register(entry) < 0) { + snd_info_unregister(entry); + entry = NULL; + } + } + chip->proc_entry_BA1 = entry; +} + +static void snd_cs4281_proc_done(cs4281_t * chip) +{ + if (chip->proc_entry_BA1) { + snd_info_unregister(chip->proc_entry_BA1); + chip->proc_entry_BA1 = NULL; + } + if (chip->proc_entry_BA0) { + snd_info_unregister(chip->proc_entry_BA0); + chip->proc_entry_BA0 = NULL; + } + if (chip->proc_entry) { + snd_info_unregister(chip->proc_entry); + chip->proc_entry = NULL; + } +} + +/* + + */ + +static int snd_cs4281_free(cs4281_t *chip) +{ +#ifdef HAVE_GAMEPORT_SUPPORT + if (chip->gameport) { + gameport_unregister_port(&chip->gameport->info); + kfree(chip->gameport); + } +#endif + snd_cs4281_proc_done(chip); + synchronize_irq(); + + /* Mask interrupts */ + snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff); + /* Stop the DLL Clock logic. */ + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); + /* Sound System Power Management - Turn Everything OFF */ + snd_cs4281_pokeBA0(chip, BA0_SSPM, 0); + /* PCI interface - D3 state */ + pci_set_power_state(chip->pci, 3); + + if (chip->ba0) + iounmap((void *) chip->ba0); + if (chip->ba1) + iounmap((void *) chip->ba1); + if (chip->ba0_res) { + release_resource(chip->ba0_res); + kfree_nocheck(chip->ba0_res); + } + if (chip->ba1_res) { + release_resource(chip->ba1_res); + kfree_nocheck(chip->ba1_res); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_cs4281_dev_free(snd_device_t *device) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, device->device_data, return -ENXIO); + return snd_cs4281_free(chip); +} + +static int __devinit snd_cs4281_create(snd_card_t * card, + struct pci_dev *pci, + cs4281_t ** rchip) +{ + cs4281_t *chip; + unsigned int tmp; + signed long end_time; + int err; + static snd_device_ops_t ops = { + dev_free: snd_cs4281_dev_free, + }; + + *rchip = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + chip = snd_magic_kcalloc(cs4281_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->ba0_addr = pci_resource_start(pci, 0); + chip->ba1_addr = pci_resource_start(pci, 1); + pci_set_master(pci); + + if ((chip->ba0_res = request_mem_region(chip->ba0_addr, CS4281_BA0_SIZE, "CS4281 BA0")) == NULL) { + snd_cs4281_free(chip); + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->ba0_addr, chip->ba0_addr + CS4281_BA0_SIZE - 1); + return -ENOMEM; + } + if ((chip->ba1_res = request_mem_region(chip->ba1_addr, CS4281_BA1_SIZE, "CS4281 BA1")) == NULL) { + snd_cs4281_free(chip); + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->ba1_addr, chip->ba1_addr + CS4281_BA1_SIZE - 1); + return -ENOMEM; + } + if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS4281", (void *)chip)) { + snd_cs4281_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -ENOMEM; + } + chip->irq = pci->irq; + + chip->ba0 = (unsigned long) ioremap_nocache(chip->ba0_addr, CS4281_BA0_SIZE); + chip->ba1 = (unsigned long) ioremap_nocache(chip->ba1_addr, CS4281_BA1_SIZE); + if (!chip->ba0 || !chip->ba1) { + snd_cs4281_free(chip); + return -ENOMEM; + } + + tmp = snd_cs4281_peekBA0(chip, BA0_CFLR); + if (tmp != BA0_CFLR_DEFAULT) { + snd_cs4281_pokeBA0(chip, BA0_CFLR, BA0_CFLR_DEFAULT); + tmp = snd_cs4281_peekBA0(chip, BA0_CFLR); + if (tmp != BA0_CFLR_DEFAULT) { + snd_printk("CFLR setup failed (0x%x)\n", tmp); + snd_cs4281_free(chip); + return -EIO; + } + } + + /* Set the 'Configuration Write Protect' register + * to 4281h. Allows vendor-defined configuration + * space between 0e4h and 0ffh to be written. */ + snd_cs4281_pokeBA0(chip, BA0_CWPR, 0x4281); + + if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC1)) != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) { + snd_printk("SERC1 AC'97 check failed (0x%x)\n", tmp); + snd_cs4281_free(chip); + return -EIO; + } + if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC2)) != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) { + snd_printk("SERC2 AC'97 check failed (0x%x)\n", tmp); + snd_cs4281_free(chip); + return -EIO; + } + + /* Sound System Power Management */ + snd_cs4281_pokeBA0(chip, BA0_SSPM, BA0_SSPM_MIXEN | BA0_SSPM_CSRCEN | + BA0_SSPM_PSRCEN | BA0_SSPM_JSEN | + BA0_SSPM_ACLEN | BA0_SSPM_FMEN); + + /* Serial Port Power Management */ + /* Blast the clock control register to zero so that the + * PLL starts out in a known state, and blast the master serial + * port control register to zero so that the serial ports also + * start out in a known state. */ + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); + snd_cs4281_pokeBA0(chip, BA0_SERMC, 0); + + /* Make ESYN go to zero to turn off + * the Sync pulse on the AC97 link. */ + snd_cs4281_pokeBA0(chip, BA0_ACCTL, 0); + udelay(50); + + /* Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 + * spec) and then drive it high. This is done for non AC97 modes since + * there might be logic external to the CS4281 that uses the ARST# line + * for a reset. */ + snd_cs4281_pokeBA0(chip, BA0_SPMC, 0); + udelay(50); + snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN); + snd_cs4281_delay(50000); + + /* + * Set the serial port timing configuration. + */ + snd_cs4281_pokeBA0(chip, BA0_SERMC, BA0_SERMC_TCID(1) | BA0_SERMC_PTC_AC97 | BA0_SERMC_MSPE); + + /* + * Start the DLL Clock logic. + */ + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_DLLP); + snd_cs4281_delay(50000); + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_SWCE | BA0_CLKCR1_DLLP); + + /* + * Wait for the DLL ready signal from the clock logic. + */ + end_time = (jiffies + HZ / 4) + 1; + do { + /* + * Read the AC97 status register to see if we've seen a CODEC + * signal from the AC97 codec. + */ + if (snd_cs4281_peekBA0(chip, BA0_CLKCR1) & BA0_CLKCR1_DLLRDY) + goto __ok0; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + + snd_printk("DLLRDY not seen\n"); + snd_cs4281_free(chip); + return -EIO; + + __ok0: + + /* + * The first thing we do here is to enable sync generation. As soon + * as we start receiving bit clock, we'll start producing the SYNC + * signal. + */ + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_ESYN); + + /* + * Wait for the codec ready signal from the AC97 codec. + */ + end_time = (jiffies + (3 * HZ) / 4) + 1; + do { + /* + * Read the AC97 status register to see if we've seen a CODEC + * signal from the AC97 codec. + */ + if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_CRDY) + goto __ok1; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + + snd_printk("never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS)); + snd_cs4281_free(chip); + return -EIO; + + __ok1: + + /* + * Assert the valid frame signal so that we can start sending commands + * to the AC97 codec. + */ + + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_VFRM | BA0_ACCTL_ESYN); + + /* + * Wait until we've sampled input slots 3 and 4 as valid, meaning that + * the codec is pumping ADC data across the AC-link. + */ + + end_time = (jiffies + (5 * HZ) / 4) + 1; + do { + /* + * Read the input slot valid register and see if input slots 3 + * 4 are valid yet. + */ + if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) + goto __ok2; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + + snd_printk("never read ISV3 and ISV4 from AC'97\n"); + snd_cs4281_free(chip); + return -EIO; + + __ok2: + + /* + * Now, assert valid frame and the slot 3 and 4 valid bits. This will + * commense the transfer of digital audio data to the AC97 codec. + */ + snd_cs4281_pokeBA0(chip, BA0_ACOSV, BA0_ACOSV_SLV(3) | BA0_ACOSV_SLV(4)); + + /* + * Initialize DMA structures + */ + for (tmp = 0; tmp < 4; tmp++) { + cs4281_dma_t *dma = &chip->dma[tmp]; + dma->regDBA = BA0_DBA0 + (tmp * 0x10); + dma->regDCA = BA0_DCA0 + (tmp * 0x10); + dma->regDBC = BA0_DBC0 + (tmp * 0x10); + dma->regDCC = BA0_DCC0 + (tmp * 0x10); + dma->regDMR = BA0_DMR0 + (tmp * 8); + dma->regDCR = BA0_DCR0 + (tmp * 8); + dma->regHDSR = BA0_HDSR0 + (tmp * 4); + dma->regFCR = BA0_FCR0 + (tmp * 4); + dma->regFSIC = BA0_FSIC0 + (tmp * 4); + dma->fifo_offset = tmp * CS4281_FIFO_SIZE; + snd_cs4281_pokeBA0(chip, dma->regFCR, + BA0_FCR_LS(31) | + BA0_FCR_RS(31) | + BA0_FCR_SZ(CS4281_FIFO_SIZE) | + BA0_FCR_OF(dma->fifo_offset)); + } + + chip->src_left_play_slot = 0; /* AC'97 left PCM playback (3) */ + chip->src_right_play_slot = 1; /* AC'97 right PCM playback (4) */ + chip->src_left_rec_slot = 10; /* AC'97 left PCM record (3) */ + chip->src_right_rec_slot = 11; /* AC'97 right PCM record (4) */ + + /* Initialize digital volume */ + snd_cs4281_pokeBA0(chip, BA0_PPLVC, 0); + snd_cs4281_pokeBA0(chip, BA0_PPRVC, 0); + + snd_cs4281_proc_init(chip); + + /* Enable IRQs */ + snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); + /* Unmask interrupts */ + snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff & ~( + BA0_HISR_MIDI | + BA0_HISR_DMAI | + BA0_HISR_DMA(0) | + BA0_HISR_DMA(1) | + BA0_HISR_DMA(2) | + BA0_HISR_DMA(3))); + synchronize_irq(); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_cs4281_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +/* + * MIDI section + */ + +static void snd_cs4281_midi_reset(cs4281_t *chip) +{ + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr | BA0_MIDCR_MRST); + udelay(100); + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); +} + +static int snd_cs4281_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr |= BA0_MIDCR_RXE; + chip->midi_input = substream; + if (!(chip->uartm & CS4281_MODE_OUTPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE); + chip->midi_input = NULL; + if (!(chip->uartm & CS4281_MODE_OUTPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS4281_MODE_INPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->uartm |= CS4281_MODE_OUTPUT; + chip->midcr |= BA0_MIDCR_TXE; + chip->midi_input = substream; + if (!(chip->uartm & CS4281_MODE_INPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE); + chip->midi_output = NULL; + if (!(chip->uartm & CS4281_MODE_INPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS4281_MODE_OUTPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static void snd_cs4281_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & BA0_MIDCR_RIE) == 0) { + chip->midcr |= BA0_MIDCR_RIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & BA0_MIDCR_RIE) { + chip->midcr &= ~BA0_MIDCR_RIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4281_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return); + unsigned char byte; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & BA0_MIDCR_TIE) == 0) { + chip->midcr |= BA0_MIDCR_TIE; + /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ + while ((chip->midcr & BA0_MIDCR_TIE) && + (snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + chip->midcr &= ~BA0_MIDCR_TIE; + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDWP, byte); + } + } + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & BA0_MIDCR_TIE) { + chip->midcr &= ~BA0_MIDCR_TIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static snd_rawmidi_ops_t snd_cs4281_midi_output = +{ + open: snd_cs4281_midi_output_open, + close: snd_cs4281_midi_output_close, + trigger: snd_cs4281_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_cs4281_midi_input = +{ + open: snd_cs4281_midi_input_open, + close: snd_cs4281_midi_input_close, + trigger: snd_cs4281_midi_input_trigger, +}; + +static int __devinit snd_cs4281_midi(cs4281_t * chip, int device, snd_rawmidi_t **rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, "CS4281"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = chip; + chip->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} + +/* + * Interrupt handler + */ + +static void snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, dev_id, return); + unsigned int status, dma, val; + cs4281_dma_t *cdma; + + if (chip == NULL) + return; + status = snd_cs4281_peekBA0(chip, BA0_HISR); + if ((status & 0x7fffffff) == 0) { + snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); + return; + } + + if (status & (BA0_HISR_DMA(0)|BA0_HISR_DMA(1)|BA0_HISR_DMA(2)|BA0_HISR_DMA(3))) { + for (dma = 0; dma < 4; dma++) + if (status & BA0_HISR_DMA(dma)) { + cdma = &chip->dma[dma]; + spin_lock(&chip->reg_lock); + /* ack DMA IRQ */ + val = snd_cs4281_peekBA0(chip, cdma->regHDSR); + /* workaround, sometimes CS4281 acknowledges */ + /* end or middle transfer position twice */ + cdma->frag++; + if ((val & BA0_HDSR_DHTC) && !(cdma->frag & 1)) { + cdma->frag--; + chip->spurious_dhtc_irq++; + spin_unlock(&chip->reg_lock); + continue; + } + if ((val & BA0_HDSR_DTC) && (cdma->frag & 1)) { + cdma->frag--; + chip->spurious_dtc_irq++; + spin_unlock(&chip->reg_lock); + continue; + } + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(cdma->substream); + } + } + + if ((status & BA0_HISR_MIDI) && chip->rmidi) { + unsigned char c; + + spin_lock(&chip->reg_lock); + while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_RBE) == 0) { + c = snd_cs4281_peekBA0(chip, BA0_MIDRP); + if ((chip->midcr & BA0_MIDCR_RIE) == 0) + continue; + snd_rawmidi_receive(chip->midi_input, &c, 1); + } + while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { + if ((chip->midcr & BA0_MIDCR_TIE) == 0) + break; + if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) { + chip->midcr &= ~BA0_MIDCR_TIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + break; + } + snd_cs4281_pokeBA0(chip, BA0_MIDWP, c); + } + spin_unlock(&chip->reg_lock); + } + + /* EOI to the PCI part... reenables interrupts */ + snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); +} + +#ifdef HAVE_GAMEPORT_SUPPORT +/* + * joystick support + */ +static void snd_cs4281_gameport_trigger(struct gameport *gameport) +{ + cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; + cs4281_t *chip; + snd_assert(gp, return); + chip = snd_magic_cast(cs4281_t, gp->chip, return); + snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff); +} + +static unsigned char snd_cs4281_gameport_read(struct gameport *gameport) +{ + cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; + cs4281_t *chip; + snd_assert(gp, return 0); + chip = snd_magic_cast(cs4281_t, gp->chip, return 0); + return snd_cs4281_peekBA0(chip, BA0_JSPT); +} + +#ifdef COOKED_MODE +static int snd_cs4281_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; + cs4281_t *chip; + unsigned js1, js2, jst; + + snd_assert(gp, return); + chip = snd_magic_cast(cs4281_t, gp->chip, return); + + js1 = snd_cs4281_peekBA0(chip, BA0_JSC1); + js2 = snd_cs4281_peekBA0(chip, BA0_JSC2); + jst = snd_cs4281_peekBA0(chip, BA0_JSPT); + + *buttons = (~jst >> 4) & 0x0F; + + axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF; + axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF; + axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF; + axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF; + + for(jst=0;jst<4;++jst) + if(axes[jst]==0xFFFF) axes[jst] = -1; + return 0; +} +#endif + +static int snd_cs4281_gameport_open(struct gameport *gameport, int mode) +{ + switch (mode) { +#ifdef COOKED_MODE + case GAMEPORT_MODE_COOKED: + return 0; +#endif + case GAMEPORT_MODE_RAW: + return 0; + default: + return -1; + } + return 0; +} + +static void __devinit snd_cs4281_gameport(cs4281_t *chip) +{ + cs4281_gameport_t *gp; + gp = kmalloc(sizeof(*gp), GFP_KERNEL); + if (! gp) { + snd_printk("cannot allocate gameport area\n"); + return; + } + memset(gp, 0, sizeof(*gp)); + gp->info.open = snd_cs4281_gameport_open; + gp->info.read = snd_cs4281_gameport_read; + gp->info.trigger = snd_cs4281_gameport_trigger; +#ifdef COOKED_MODE + gp->info.cooked_read = snd_cs4281_gameport_cooked_read; +#endif + gp->chip = chip; + chip->gameport = gp; + + snd_cs4281_pokeBA0(chip, BA0_JSIO, 0xFF); // ? + snd_cs4281_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW); + gameport_register_port(&gp->info); +} + +#endif /* HAVE_GAMEPORT_SUPPORT */ + + +static int __devinit snd_cs4281_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + cs4281_t *chip; + opl3_t *opl3; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_cs4281_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_cs4281_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4281_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4281_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_create(card, + (chip->ba0 + BA0_B0AP) >> 2, + (chip->ba0 + BA0_B1AP) >> 2, + OPL3_HW_OPL3_CS4281, 1, &opl3)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifdef HAVE_GAMEPORT_SUPPORT + snd_cs4281_gameport(chip); +#endif + strcpy(card->driver, "CS4281"); + strcpy(card->shortname, "Cirrus Logic CS4281"); + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, + chip->ba0_addr, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_cs4281_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "CS4281", + id_table: snd_cs4281_ids, + probe: snd_cs4281_probe, + remove: __devexit_p(snd_cs4281_remove), +}; + +static int __init alsa_card_cs4281_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("CS4281 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_cs4281_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_cs4281_init) +module_exit(alsa_card_cs4281_exit) + +#ifndef MODULE + +/* format is: snd-cs4281=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_cs4281_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cs4281=", alsa_card_cs4281_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/cs46xx/Makefile b/sound/pci/cs46xx/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/cs46xx/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _cs46xx.o + +list-multi := snd-cs46xx.o + +snd-cs46xx-objs := cs46xx.o cs46xx_lib.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_CS46XX) += snd-cs46xx.o + +include $(TOPDIR)/Rules.make + +snd-cs46xx.o: $(snd-cs46xx-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs46xx-objs) diff -Nru a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/cs46xx/cs46xx.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,220 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + NOTES: + - sometimes the sound is metallic and sibilant, unloading and + reloading the module may solve this. +*/ + +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Cirrus Logic Sound Fusion CS46XX"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Cirrus Logic,Sound Fusion (CS4280)}," + "{Cirrus Logic,Sound Fusion (CS4610)}," + "{Cirrus Logic,Sound Fusion (CS4612)}," + "{Cirrus Logic,Sound Fusion (CS4615)}," + "{Cirrus Logic,Sound Fusion (CS4622)}," + "{Cirrus Logic,Sound Fusion (CS4624)}," + "{Cirrus Logic,Sound Fusion (CS4630)}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_thinkpad[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for the CS46xx soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for the CS46xx soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable CS46xx soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_external_amp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_external_amp, "Force to enable external amplifer."); +MODULE_PARM_SYNTAX(snd_external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(snd_thinkpad, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_thinkpad, "Force to enable Thinkpad's CLKRUN control."); +MODULE_PARM_SYNTAX(snd_thinkpad, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); + +static struct pci_device_id snd_cs46xx_ids[] __devinitdata = { + { 0x1013, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4280 */ + { 0x1013, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4612 */ + { 0x1013, 0x6004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4615 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_cs46xx_ids); + +static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + cs46xx_t *chip; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if ((err = snd_cs46xx_create(card, pci, + snd_external_amp[dev], snd_thinkpad[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs46xx_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs46xx_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs46xx_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + strcpy(card->driver, "CS46xx"); + strcpy(card->shortname, "Sound Fusion CS46xx"); + sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i", + card->shortname, + chip->ba0_addr, + chip->ba1_addr, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_card_cs46xx_suspend(struct pci_dev *pci, u32 state) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return -ENXIO); + snd_cs46xx_suspend(chip); + return 0; +} +static int snd_card_cs46xx_resume(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return -ENXIO); + snd_cs46xx_resume(chip); + return 0; +} +#else +static void snd_card_cs46xx_suspend(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return); + snd_cs46xx_suspend(chip); +} +static void snd_card_cs46xx_resume(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return); + snd_cs46xx_resume(chip); +} +#endif +#endif + +static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "Sound Fusion CS46xx", + id_table: snd_cs46xx_ids, + probe: snd_card_cs46xx_probe, + remove: __devexit_p(snd_card_cs46xx_remove), +#ifdef CONFIG_PM + suspend: snd_card_cs46xx_suspend, + resume: snd_card_cs46xx_resume, +#endif +}; + +static int __init alsa_card_cs46xx_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("Sound Fusion CS46xx soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_cs46xx_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_cs46xx_init) +module_exit(alsa_card_cs46xx_exit) + +#ifndef MODULE + +/* format is: snd-cs46xx=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_cs46xx_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cs46xx=", alsa_card_cs46xx_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/cs46xx/cs46xx_image.h b/sound/pci/cs46xx/cs46xx_image.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/cs46xx/cs46xx_image.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,3459 @@ +struct BA1struct BA1Struct = { +{{ 0x00000000, 0x00003000 },{ 0x00010000, 0x00003800 },{ 0x00020000, 0x00007000 }}, +{0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000163,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00200040,0x00008010,0x00000000, +0x00000000,0x80000001,0x00000001,0x00060000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00900080,0x00000173,0x00000000, +0x00000000,0x00000010,0x00800000,0x00900000, +0xf2c0000f,0x00000200,0x00000000,0x00010600, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000163,0x330300c2, +0x06000000,0x00000000,0x80008000,0x80008000, +0x3fc0000f,0x00000301,0x00010400,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00b00000,0x00d0806d,0x330480c3, +0x04800000,0x00000001,0x00800001,0x0000ffff, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x066a0600,0x06350070,0x0000929d,0x929d929d, +0x00000000,0x0000735a,0x00000600,0x00000000, +0x929d735a,0x8734abfe,0x00010000,0x735a735a, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000804f,0x000000c3, +0x05000000,0x00a00010,0x00000000,0x80008000, +0x00000000,0x00000000,0x00000700,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000080,0x00a00000,0x0000809a,0x000000c2, +0x07400000,0x00000000,0x80008000,0xffffffff, +0x00c80028,0x00005555,0x00000000,0x000107a0, +0x00c80028,0x000000c2,0x06800000,0x00000000, +0x06e00080,0x00300000,0x000080bb,0x000000c9, +0x07a00000,0x04000000,0x80008000,0xffffffff, +0x00c80028,0x00005555,0x00000000,0x00000780, +0x00c80028,0x000000c5,0xff800000,0x00000000, +0x00640080,0x00c00000,0x00008197,0x000000c9, +0x07800000,0x04000000,0x80008000,0xffffffff, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000805e,0x000000c1, +0x00000000,0x00800000,0x80008000,0x80008000, +0x00020000,0x0000ffff,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x929d0600,0x929d929d,0x929d929d,0x929d0000, +0x929d929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x00100635,0x060b013f,0x00000004, +0x00000001,0x007a0002,0x00000000,0x066e0610, +0x0105929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0xa431ac75,0x0001735a,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051, +0x00000000,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0x00000000,0x06400136, +0x0000270f,0x00010000,0x007a0000,0x00000000, +0x068e0645,0x0105929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0xa431ac75,0x0001735a, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0x735a0100,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00010004, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00001705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00009705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00011705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00019705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00021705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00029705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00031705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00039705,0x00001400,0x000a411e,0x00001003, +0x000fe19e,0x00001003,0x0009c730,0x00001003, +0x0008e19c,0x00001003,0x000083c1,0x00093040, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00009705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00011705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00019705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00021705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00029705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00031705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00039705,0x00001400,0x000a211e,0x00001003, +0x0000a730,0x00001008,0x000e2730,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x00000000,0x00000000,0x000f619c,0x00001003, +0x0007f801,0x000c0000,0x00000037,0x00001000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x000c0000,0x00000000,0x00000000, +0x0000373c,0x00001000,0x00000000,0x00000000, +0x000ee19c,0x00001003,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000273c,0x00001000, +0x00000033,0x00001000,0x000e679e,0x00001003, +0x00007705,0x00001400,0x000ac71e,0x00001003, +0x00087fc1,0x000c3be0,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000a730,0x00001003, +0x00000033,0x00001000,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x000c0000, +0x00000032,0x00001000,0x0000273d,0x00001000, +0x0004a730,0x00001003,0x00000f41,0x00097140, +0x0000a841,0x0009b240,0x0000a0c1,0x0009f040, +0x0001c641,0x00093540,0x0001cec1,0x0009b5c0, +0x00000000,0x00000000,0x0001bf05,0x0003fc40, +0x00002725,0x000aa400,0x00013705,0x00093a00, +0x0000002e,0x0009d6c0,0x00038630,0x00001004, +0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000, +0x00000000,0x000c70e0,0x0007d182,0x0002c640, +0x00000630,0x00001004,0x000799b8,0x0002c6c0, +0x00031705,0x00092240,0x00039f05,0x000932c0, +0x0003520a,0x00000000,0x00040731,0x0000100b, +0x00010705,0x000b20c0,0x00000000,0x000eba44, +0x00032108,0x000c60c4,0x00065208,0x000c2917, +0x000406b0,0x00001007,0x00012f05,0x00036880, +0x0002818e,0x000c0000,0x0004410a,0x00000000, +0x00040630,0x00001007,0x00029705,0x000c0000, +0x00000000,0x00000000,0x00003fc1,0x0003fc40, +0x000037c1,0x00091b40,0x00003fc1,0x000911c0, +0x000037c1,0x000957c0,0x00003fc1,0x000951c0, +0x000037c1,0x00000000,0x00003fc1,0x000991c0, +0x000037c1,0x00000000,0x00003fc1,0x0009d1c0, +0x000037c1,0x00000000,0x0001ccc1,0x000915c0, +0x0001c441,0x0009d800,0x0009cdc1,0x00091240, +0x0001c541,0x00091d00,0x0009cfc1,0x00095240, +0x0001c741,0x00095c80,0x000e8ca9,0x00099240, +0x000e85ad,0x00095640,0x00069ca9,0x00099d80, +0x000e952d,0x00099640,0x000eaca9,0x0009d6c0, +0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80, +0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0, +0x000ec5ad,0x0009da40,0x000edca9,0x0009d300, +0x000a6e0a,0x00001000,0x000ed52d,0x00091e40, +0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40, +0x0006fca9,0x00002500,0x000fb208,0x000c59a0, +0x000ef52d,0x0009de40,0x00068ca9,0x000912c1, +0x000683ad,0x00095241,0x00020f05,0x000991c1, +0x00000000,0x00000000,0x00086f88,0x00001000, +0x0009cf81,0x000b5340,0x0009c701,0x000b92c0, +0x0009de81,0x000bd300,0x0009d601,0x000b1700, +0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0, +0x000a0f81,0x000bd740,0x00020701,0x000b5c80, +0x000a1681,0x000b97c0,0x00021601,0x00002500, +0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0, +0x00021681,0x00002d00,0x00020f81,0x000bd800, +0x000a0701,0x000b5bc0,0x00021601,0x00003500, +0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0, +0x00021681,0x00003d00,0x00020f81,0x000b1d00, +0x000a0701,0x000b1fc0,0x00021601,0x00020500, +0x00020f81,0x000b1341,0x000a0701,0x000b9fc0, +0x00021681,0x00020d00,0x00020f81,0x000bde80, +0x000a0701,0x000bdfc0,0x00021601,0x00021500, +0x00020f81,0x000b9341,0x00020701,0x000b53c1, +0x00021681,0x00021d00,0x000a0f81,0x000d0380, +0x0000b601,0x000b15c0,0x00007b01,0x00000000, +0x00007b81,0x000bd1c0,0x00007b01,0x00000000, +0x00007b81,0x000b91c0,0x00007b01,0x000b57c0, +0x00007b81,0x000b51c0,0x00007b01,0x000b1b40, +0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0, +0x0007e488,0x000d7e45,0x00000000,0x000d7a44, +0x0007e48a,0x00000000,0x00011f05,0x00084080, +0x00000000,0x00000000,0x00001705,0x000b3540, +0x00008a01,0x000bf040,0x00007081,0x000bb5c0, +0x00055488,0x00000000,0x0000d482,0x0003fc40, +0x0003fc88,0x00000000,0x0001e401,0x000b3a00, +0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784, +0x000c86b0,0x00001007,0x00008281,0x000bb240, +0x0000b801,0x000b7140,0x00007888,0x00000000, +0x0000073c,0x00001000,0x0007f188,0x000c0000, +0x00000000,0x00000000,0x00055288,0x000c555c, +0x0005528a,0x000c0000,0x0009fa88,0x000c5d00, +0x0000fa88,0x00000000,0x00000032,0x00001000, +0x0000073d,0x00001000,0x0007f188,0x000c0000, +0x00000000,0x00000000,0x0008c01c,0x00001003, +0x00002705,0x00001008,0x0008b201,0x000c1392, +0x0000ba01,0x00000000,0x00008731,0x00001400, +0x0004c108,0x000fe0c4,0x00057488,0x00000000, +0x000a6388,0x00001001,0x0008b334,0x000bc141, +0x0003020e,0x00000000,0x000886b0,0x00001008, +0x00003625,0x000c5dfa,0x000a638a,0x00001001, +0x0008020e,0x00001002,0x0008a6b0,0x00001008, +0x0007f301,0x00000000,0x00000000,0x00000000, +0x00002725,0x000a8c40,0x000000ae,0x00000000, +0x000d8630,0x00001008,0x00000000,0x000c74e0, +0x0007d182,0x0002d640,0x000a8630,0x00001008, +0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5, +0x0007420a,0x000c0000,0x00062208,0x000c4117, +0x00070630,0x00001009,0x00000000,0x000c0000, +0x0001022e,0x00000000,0x0003a630,0x00001009, +0x00000000,0x000c0000,0x00000036,0x00001000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x0002a730,0x00001008,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0002a730,0x00001008, +0x00000033,0x00001000,0x0002a705,0x00001008, +0x00007a01,0x000c0000,0x000e6288,0x000d550a, +0x0006428a,0x00000000,0x00060730,0x0000100a, +0x00000000,0x000c0000,0x00000000,0x00000000, +0x0007aab0,0x00034880,0x00078fb0,0x0000100b, +0x00057488,0x00000000,0x00033b94,0x00081140, +0x000183ae,0x00000000,0x000786b0,0x0000100b, +0x00022f05,0x000c3545,0x0000eb8a,0x00000000, +0x00042731,0x00001003,0x0007aab0,0x00034880, +0x00048fb0,0x0000100a,0x00057488,0x00000000, +0x00033b94,0x00081140,0x000183ae,0x00000000, +0x000806b0,0x0000100b,0x00022f05,0x00000000, +0x00007401,0x00091140,0x00048f05,0x000951c0, +0x00042731,0x00001003,0x0000473d,0x00001000, +0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, +0x000fe19e,0x00001003,0x00000000,0x00000000, +0x0008e19c,0x00001003,0x000083c1,0x00093040, +0x00000f41,0x00097140,0x0000a841,0x0009b240, +0x0000a0c1,0x0009f040,0x0001c641,0x00093540, +0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44, +0x00055208,0x00000000,0x00010705,0x000a2880, +0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5, +0x0004ef0a,0x000c0000,0x00012f05,0x00036880, +0x00065308,0x000c2997,0x000d86b0,0x0000100a, +0x0004410a,0x000d40c7,0x00000000,0x00000000, +0x00080730,0x00001004,0x00056f0a,0x000ea105, +0x00000000,0x00000000,0x0000473d,0x00001000, +0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, +0x0000273d,0x00001000,0x00000000,0x000eba44, +0x00048f05,0x0000f440,0x00007401,0x0000f7c0, +0x00000734,0x00001000,0x00010705,0x000a6880, +0x00006a88,0x000c75c4,0x00000000,0x000e5084, +0x00000000,0x000eba44,0x00087401,0x000e4782, +0x00000734,0x00001000,0x00010705,0x000a6880, +0x00006a88,0x000c75c4,0x0007c108,0x000c0000, +0x0007e721,0x000bed40,0x00005f25,0x000badc0, +0x0003ba97,0x000beb80,0x00065590,0x000b2e00, +0x00033217,0x00003ec0,0x00065590,0x000b8e40, +0x0003ed80,0x000491c0,0x00073fb0,0x00074c80, +0x000283a0,0x0000100c,0x000ee388,0x00042970, +0x00008301,0x00021ef2,0x000b8f14,0x0000000f, +0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6, +0x000032ac,0x000c3916,0x0004edc2,0x00074c80, +0x00078898,0x00001000,0x00038894,0x00000032, +0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6, +0x0004edc2,0x000c1956,0x0000722c,0x00034a00, +0x00041705,0x0009ed40,0x00058730,0x00001400, +0x000d7488,0x000c3a00,0x00048f05,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000} + }; diff -Nru a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/cs46xx/cs46xx_lib.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2305 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Abramo Bagnara + * Cirrus Logic, Inc. + * Routines for control of Cirrus Logic CS461x chips + * + * BUGS: + * -- + * + * TODO: + * We need a DSP code to support multichannel outputs and S/PDIF. + * Unfortunately, it seems that Cirrus Logic, Inc. is not willing + * to provide us sufficient information about the DSP processor, + * so we can't update the driver. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t cs46xx_t + +/* + * constants + */ + +#if 0 +#define SND_CONFIG_CS46XX_ACCEPT_VALID /* REQUIRED ONLY FOR OSS EMULATION */ +#endif + +#define CS46XX_BA0_SIZE 0x1000 +#define CS46XX_BA1_DATA0_SIZE 0x3000 +#define CS46XX_BA1_DATA1_SIZE 0x3800 +#define CS46XX_BA1_PRG_SIZE 0x7000 +#define CS46XX_BA1_REG_SIZE 0x0100 + + +#define CS46XX_PERIOD_SIZE 2048 +#define CS46XX_FRAGS 2 +#define CS46XX_BUFFER_SIZE CS46XX_PERIOD_SIZE * CS46XX_FRAGS + +extern snd_pcm_ops_t snd_cs46xx_playback_ops; +extern snd_pcm_ops_t snd_cs46xx_playback_indirect_ops; +extern snd_pcm_ops_t snd_cs46xx_capture_ops; +extern snd_pcm_ops_t snd_cs46xx_capture_indirect_ops; + + +/* + * common I/O routines + */ + +static inline void snd_cs46xx_poke(cs46xx_t *chip, unsigned long reg, unsigned int val) +{ + unsigned int bank = reg >> 16; + unsigned int offset = reg & 0xffff; + writel(val, chip->region.idx[bank+1].remap_addr + offset); +} + +static inline unsigned int snd_cs46xx_peek(cs46xx_t *chip, unsigned long reg) +{ + unsigned int bank = reg >> 16; + unsigned int offset = reg & 0xffff; + return readl(chip->region.idx[bank+1].remap_addr + offset); +} + +static inline void snd_cs46xx_pokeBA0(cs46xx_t *chip, unsigned long offset, unsigned int val) +{ + writel(val, chip->region.name.ba0.remap_addr + offset); +} + +static inline unsigned int snd_cs46xx_peekBA0(cs46xx_t *chip, unsigned long offset) +{ + return readl(chip->region.name.ba0.remap_addr + offset); +} + + +static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip, + unsigned short reg) +{ + int count; + unsigned short result; + + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h + * 5. if DCV not cleared, break and return error + * 6. Read ACSTS = Status Register = 464h, check VSTS bit + */ + + snd_cs46xx_peekBA0(chip, BA0_ACSDA); + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * set CRW - Read command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + + snd_cs46xx_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs46xx_pokeBA0(chip, BA0_ACCDA, 0); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW | + ACCTL_VFRM | ACCTL_ESYN | + ACCTL_RSTN); + + + /* + * Wait for the read to occur. + */ + for (count = 0; count < 1000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the read has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 17h + */ + if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) + goto ok1; + } + + snd_printk("AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg); + result = 0xffff; + goto end; + + ok1: + /* + * Wait for the valid status bit to go active. + */ + for (count = 0; count < 100; count++) { + /* + * Read the AC97 status register. + * ACSTS = Status Register = 464h + * VSTS - Valid Status + */ + if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_VSTS) + goto ok2; + udelay(10); + } + + snd_printk("AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg); + result = 0xffff; + goto end; + + ok2: + /* + * Read the data returned from the AC97 register. + * ACSDA = Status Data Register = 474h + */ +#if 0 + printk("e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg, + snd_cs46xx_peekBA0(chip, BA0_ACSDA), + snd_cs46xx_peekBA0(chip, BA0_ACCAD)); +#endif + result = snd_cs46xx_peekBA0(chip, BA0_ACSDA); + end: + return result; +} + +static unsigned short snd_cs46xx_ac97_read(ac97_t * ac97, + unsigned short reg) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return -ENXIO); + unsigned short val; + chip->active_ctrl(chip, 1); + val = snd_cs46xx_codec_read(chip, reg); + chip->active_ctrl(chip, -1); + return val; +} + + +static void snd_cs46xx_codec_write(cs46xx_t *chip, + unsigned short reg, + unsigned short val) +{ + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h + * 5. if DCV not cleared, break and return error + */ + int count; + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * reset CRW - Write command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs46xx_pokeBA0(chip, BA0_ACCDA, val); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM | + ACCTL_ESYN | ACCTL_RSTN); + for (count = 0; count < 4000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the write has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 07h + */ + if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) { + return; + } + } + snd_printk("AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val); +} + +static void snd_cs46xx_ac97_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return); + int val2 = 0; + + chip->active_ctrl(chip, 1); + if (reg == AC97_CD) + val2 = snd_cs46xx_codec_read(chip, AC97_CD); + + snd_cs46xx_codec_write(chip, reg, val); + + + /* + * Adjust power if the mixer is selected/deselected according + * to the CD. + * + * IF the CD is a valid input source (mixer or direct) AND + * the CD is not muted THEN power is needed + * + * We do two things. When record select changes the input to + * add/remove the CD we adjust the power count if the CD is + * unmuted. + * + * When the CD mute changes we adjust the power level if the + * CD was a valid input. + * + * We also check for CD volume != 0, as the CD mute isn't + * normally tweaked from userspace. + */ + + /* CD mute change ? */ + + if (reg == AC97_CD) { + /* Mute bit change ? */ + if ((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) { + /* Mute on */ + if(val&0x8000 || val == 0x1f1f) + chip->amplifier_ctrl(chip, -1); + else /* Mute off power on */ + chip->amplifier_ctrl(chip, 1); + } + } + + chip->active_ctrl(chip, -1); +} + + +/* + * Chip initialization + */ + +int snd_cs46xx_download(cs46xx_t *chip, + u32 *src, + unsigned long offset, + unsigned long len) +{ + unsigned long dst; + unsigned int bank = offset >> 16; + offset = offset & 0xffff; + + snd_assert(!(offset & 3) && !(len & 3), return -EINVAL); + dst = chip->region.idx[bank+1].remap_addr + offset; + len /= sizeof(u32); + + /* writel already converts 32-bit value to right endianess */ + while (len-- > 0) { + writel(*src++, dst); + dst += sizeof(u32); + } + return 0; +} + +/* 3*1024 parameter, 3.5*1024 sample, 2*3.5*1024 code */ +#define BA1_DWORD_SIZE (13 * 1024 + 512) +#define BA1_MEMORY_COUNT 3 + +struct BA1struct { + struct { + unsigned long offset; + unsigned long size; + } memory[BA1_MEMORY_COUNT]; + u32 map[BA1_DWORD_SIZE]; +}; + +static +#include "cs46xx_image.h" + +int snd_cs46xx_download_image(cs46xx_t *chip) +{ + int idx, err; + unsigned long offset = 0; + + for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) { + if ((err = snd_cs46xx_download(chip, + &BA1Struct.map[offset], + BA1Struct.memory[idx].offset, + BA1Struct.memory[idx].size)) < 0) + return err; + offset += BA1Struct.memory[idx].size >> 2; + } + return 0; +} + +/* + * Chip reset + */ + +static void snd_cs46xx_reset(cs46xx_t *chip) +{ + int idx; + + /* + * Write the reset bit of the SP control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RSTSP); + + /* + * Write the control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, SPCR_DRQEN); + + /* + * Clear the trap registers. + */ + for (idx = 0; idx < 8; idx++) { + snd_cs46xx_poke(chip, BA1_DREG, DREG_REGID_TRAP_SELECT + idx); + snd_cs46xx_poke(chip, BA1_TWPR, 0xFFFF); + } + snd_cs46xx_poke(chip, BA1_DREG, 0); + + /* + * Set the frame timer to reflect the number of cycles per frame. + */ + snd_cs46xx_poke(chip, BA1_FRMT, 0xadf); +} + +static void snd_cs46xx_clear_serial_FIFOs(cs46xx_t *chip) +{ + int idx, loop, powerdown = 0; + unsigned int tmp; + + /* + * See if the devices are powered down. If so, we must power them up first + * or they will not respond. + */ + tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1); + if (!(tmp & CLKCR1_SWCE)) { + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE); + powerdown = 1; + } + + /* + * We want to clear out the serial port FIFOs so we don't end up playing + * whatever random garbage happens to be in them. We fill the sample FIFOS + * with zero (silence). + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0); + + /* + * Fill all 256 sample FIFO locations. + */ + for (idx = 0; idx < 256; idx++) { + /* + * Make sure the previous FIFO write operation has completed. + */ + for (loop = 0; loop < 5; loop++) { + udelay(50); + if (!(snd_cs46xx_peekBA0(chip, BA0_SERBST) & SERBST_WBSY)) + break; + } + if (snd_cs46xx_peekBA0(chip, BA0_SERBST) & SERBST_WBSY) { + if (powerdown) + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); + } + /* + * Write the serial port FIFO index. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx); + /* + * Tell the serial port to load the new value into the FIFO location. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC); + } + /* + * Now, if we powered up the devices, then power them back down again. + * This is kinda ugly, but should never happen. + */ + if (powerdown) + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); +} + +static void snd_cs46xx_proc_start(cs46xx_t *chip) +{ + int cnt; + + /* + * Set the frame timer to reflect the number of cycles per frame. + */ + snd_cs46xx_poke(chip, BA1_FRMT, 0xadf); + /* + * Turn on the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN); + /* + * Wait until the run at frame bit resets itself in the SP control + * register. + */ + for (cnt = 0; cnt < 25; cnt++) { + udelay(50); + if (!(snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)) + break; + } + + if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR) + snd_printk("SPCR_RUNFR never reset\n"); +} + +static void snd_cs46xx_proc_stop(cs46xx_t *chip) +{ + /* + * Turn off the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, 0); +} + +/* + * Sample rate routines + */ + +#define GOF_PER_SEC 200 + +static void snd_cs46xx_set_play_sample_rate(cs46xx_t *chip, unsigned int rate) +{ + unsigned long flags; + unsigned int tmp1, tmp2; + unsigned int phiIncr; + unsigned int correctionPerGOF, correctionPerSec; + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M + * GOF_PER_SEC * correctionPerGOF + * + * i.e. + * + * phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + */ + tmp1 = rate << 16; + phiIncr = tmp1 / 48000; + tmp1 -= phiIncr * 48000; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / 48000; + phiIncr += tmp2; + tmp1 -= tmp2 * 48000; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + + /* + * Fill in the SampleRateConverter control block. + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs46xx_poke(chip, BA1_PSRC, + ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); + snd_cs46xx_poke(chip, BA1_PPI, phiIncr); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs46xx_set_capture_sample_rate(cs46xx_t *chip, unsigned int rate) +{ + unsigned long flags; + unsigned int phiIncr, coeffIncr, tmp1, tmp2; + unsigned int correctionPerGOF, correctionPerSec, initialDelay; + unsigned int frameGroupLength, cnt; + + /* + * We can only decimate by up to a factor of 1/9th the hardware rate. + * Correct the value if an attempt is made to stray outside that limit. + */ + if ((rate * 9) < 48000) + rate = 48000 / 9; + + /* + * We can not capture at at rate greater than the Input Rate (48000). + * Return an error if an attempt is made to stray outside that limit. + */ + if (rate > 48000) + rate = 48000; + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * coeffIncr = -floor((Fs,out * 2^23) / Fs,in) + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - + * GOF_PER_SEC * correctionPerGOF + * initialDelay = ceil((24 * Fs,in) / Fs,out) + * + * i.e. + * + * coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in)) + * phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + * initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out) + */ + + tmp1 = rate << 16; + coeffIncr = tmp1 / 48000; + tmp1 -= coeffIncr * 48000; + tmp1 <<= 7; + coeffIncr <<= 7; + coeffIncr += tmp1 / 48000; + coeffIncr ^= 0xFFFFFFFF; + coeffIncr++; + tmp1 = 48000 << 16; + phiIncr = tmp1 / rate; + tmp1 -= phiIncr * rate; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / rate; + phiIncr += tmp2; + tmp1 -= tmp2 * rate; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + initialDelay = ((48000 * 24) + rate - 1) / rate; + + /* + * Fill in the VariDecimate control block. + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs46xx_poke(chip, BA1_CSRC, + ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); + snd_cs46xx_poke(chip, BA1_CCI, coeffIncr); + snd_cs46xx_poke(chip, BA1_CD, + (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80); + snd_cs46xx_poke(chip, BA1_CPI, phiIncr); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* + * Figure out the frame group length for the write back task. Basically, + * this is just the factors of 24000 (2^6*3*5^3) that are not present in + * the output sample rate. + */ + frameGroupLength = 1; + for (cnt = 2; cnt <= 64; cnt *= 2) { + if (((rate / cnt) * cnt) != rate) + frameGroupLength *= 2; + } + if (((rate / 3) * 3) != rate) { + frameGroupLength *= 3; + } + for (cnt = 5; cnt <= 125; cnt *= 5) { + if (((rate / cnt) * cnt) != rate) + frameGroupLength *= 5; + } + + /* + * Fill in the WriteBack control block. + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs46xx_poke(chip, BA1_CFG1, frameGroupLength); + snd_cs46xx_poke(chip, BA1_CFG2, (0x00800000 | frameGroupLength)); + snd_cs46xx_poke(chip, BA1_CCST, 0x0000FFFF); + snd_cs46xx_poke(chip, BA1_CSPB, ((65536 * rate) / 24000)); + snd_cs46xx_poke(chip, (BA1_CSPB + 4), 0x0000FFFF); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* + * PCM part + */ + +static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream, + snd_pcm_uframes_t frames) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->play.appl_ptr; + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + chip->play.sw_ready += diff << chip->play.shift; + } + chip->play.sw_ready += frames << chip->play.shift; + chip->play.appl_ptr = runtime->control->appl_ptr + frames; + while (chip->play.hw_ready < CS46XX_BUFFER_SIZE && + chip->play.sw_ready > 0) { + size_t hw_to_end = CS46XX_BUFFER_SIZE - chip->play.hw_data; + size_t sw_to_end = chip->play.sw_bufsize - chip->play.sw_data; + size_t bytes = CS46XX_BUFFER_SIZE - chip->play.hw_ready; + if (chip->play.sw_ready < bytes) + bytes = chip->play.sw_ready; + if (hw_to_end < bytes) + bytes = hw_to_end; + if (sw_to_end < bytes) + bytes = sw_to_end; + memcpy(chip->play.hw_area + chip->play.hw_data, + runtime->dma_area + chip->play.sw_data, + bytes); + chip->play.hw_data += bytes; + if (chip->play.hw_data == CS46XX_BUFFER_SIZE) + chip->play.hw_data = 0; + chip->play.sw_data += bytes; + if (chip->play.sw_data == chip->play.sw_bufsize) + chip->play.sw_data = 0; + chip->play.hw_ready += bytes; + chip->play.sw_ready -= bytes; + } + return 0; +} + +static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream, + snd_pcm_uframes_t frames) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->capt.appl_ptr; + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + chip->capt.sw_ready -= diff << chip->capt.shift; + } + chip->capt.sw_ready -= frames << chip->capt.shift; + chip->capt.appl_ptr = runtime->control->appl_ptr + frames; + while (chip->capt.hw_ready > 0 && + chip->capt.sw_ready < chip->capt.sw_bufsize) { + size_t hw_to_end = CS46XX_BUFFER_SIZE - chip->capt.hw_data; + size_t sw_to_end = chip->capt.sw_bufsize - chip->capt.sw_data; + size_t bytes = chip->capt.sw_bufsize - chip->capt.sw_ready; + if (chip->capt.hw_ready < bytes) + bytes = chip->capt.hw_ready; + if (hw_to_end < bytes) + bytes = hw_to_end; + if (sw_to_end < bytes) + bytes = sw_to_end; + memcpy(runtime->dma_area + chip->capt.sw_data, + chip->capt.hw_area + chip->capt.hw_data, + bytes); + chip->capt.hw_data += bytes; + if (chip->capt.hw_data == CS46XX_BUFFER_SIZE) + chip->capt.hw_data = 0; + chip->capt.sw_data += bytes; + if (chip->capt.sw_data == chip->capt.sw_bufsize) + chip->capt.sw_data = 0; + chip->capt.hw_ready -= bytes; + chip->capt.sw_ready += bytes; + } + return 0; +} + +static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr = snd_cs46xx_peek(chip, BA1_PBA) - chip->play.hw_addr; + return ptr >> chip->play.shift; +} + +static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr = snd_cs46xx_peek(chip, BA1_PBA) - chip->play.hw_addr; + ssize_t bytes = ptr - chip->play.hw_io; + if (bytes < 0) + bytes += CS46XX_BUFFER_SIZE; + chip->play.hw_io = ptr; + chip->play.hw_ready -= bytes; + chip->play.sw_io += bytes; + if (chip->play.sw_io > chip->play.sw_bufsize) + chip->play.sw_io -= chip->play.sw_bufsize; + snd_cs46xx_playback_transfer(substream, 0); + return chip->play.sw_io >> chip->play.shift; +} + +static snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; + return ptr >> chip->capt.shift; +} + +static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; + ssize_t bytes = ptr - chip->capt.hw_io; + if (bytes < 0) + bytes += CS46XX_BUFFER_SIZE; + chip->capt.hw_io = ptr; + chip->capt.hw_ready += bytes; + chip->capt.sw_io += bytes; + if (chip->capt.sw_io > chip->capt.sw_bufsize) + chip->capt.sw_io -= chip->capt.sw_bufsize; + snd_cs46xx_capture_transfer(substream, 0); + return chip->capt.sw_io >> chip->capt.shift; +} + +static int snd_cs46xx_playback_copy(snd_pcm_substream_t *substream, + int channel, + snd_pcm_uframes_t hwoff, + void *src, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t hwoffb = hwoff << chip->play.shift; + size_t bytes = frames << chip->play.shift; + char *hwbuf = runtime->dma_area + hwoffb; + if (copy_from_user(hwbuf, src, bytes)) + return -EFAULT; + spin_lock_irq(&runtime->lock); + snd_cs46xx_playback_transfer(substream, frames); + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_cs46xx_capture_copy(snd_pcm_substream_t *substream, + int channel, + snd_pcm_uframes_t hwoff, + void *dst, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t hwoffb = hwoff << chip->capt.shift; + size_t bytes = frames << chip->capt.shift; + char *hwbuf = runtime->dma_area + hwoffb; + if (copy_to_user(dst, hwbuf, bytes)) + return -EFAULT; + spin_lock_irq(&runtime->lock); + snd_cs46xx_capture_transfer(substream, frames); + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + unsigned int tmp; + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->runtime->periods != CS46XX_FRAGS) + snd_cs46xx_playback_transfer(substream, 0); + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + tmp &= 0x0000ffff; + snd_cs46xx_poke(chip, BA1_PCTL, chip->play.ctl | tmp); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + tmp &= 0x0000ffff; + snd_cs46xx_poke(chip, BA1_PCTL, tmp); + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_cs46xx_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + unsigned int tmp; + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + tmp &= 0xffff0000; + snd_cs46xx_poke(chip, BA1_CCTL, chip->capt.ctl | tmp); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + tmp &= 0xffff0000; + snd_cs46xx_poke(chip, BA1_CCTL, tmp); + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if (params_periods(hw_params) == CS46XX_FRAGS) { + if (runtime->dma_area != chip->play.hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = chip->play.hw_area; + runtime->dma_addr = chip->play.hw_addr; + runtime->dma_bytes = chip->play.hw_size; + substream->ops = &snd_cs46xx_playback_ops; + } else { + if (runtime->dma_area == chip->play.hw_area) { + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + substream->ops = &snd_cs46xx_playback_indirect_ops; + } + return 0; +} + +static int snd_cs46xx_playback_hw_free(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (runtime->dma_area != chip->play.hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + return 0; +} + +static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned int tmp; + unsigned int pfie; + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + pfie = snd_cs46xx_peek(chip, BA1_PFIE); + pfie &= ~0x0000f03f; + + chip->play.shift = 2; + if (runtime->channels == 1) { + chip->play.shift--; + pfie |= 0x00002000; + } + if (snd_pcm_format_width(runtime->format) == 8) { + chip->play.shift--; + pfie |= 0x00001000; + } + if (snd_pcm_format_unsigned(runtime->format)) + pfie |= 0x00008000; + if (snd_pcm_format_big_endian(runtime->format)) + pfie |= 0x00004000; + + + chip->play.sw_bufsize = snd_pcm_lib_buffer_bytes(substream); + chip->play.sw_data = chip->play.sw_io = chip->play.sw_ready = 0; + chip->play.hw_data = chip->play.hw_io = chip->play.hw_ready = 0; + chip->play.appl_ptr = 0; + snd_cs46xx_poke(chip, BA1_PBA, chip->play.hw_addr); + + tmp = snd_cs46xx_peek(chip, BA1_PDTC); + tmp &= ~0x000003ff; + tmp |= (4 << chip->play.shift) - 1; + snd_cs46xx_poke(chip, BA1_PDTC, tmp); + + snd_cs46xx_poke(chip, BA1_PFIE, pfie); + + snd_cs46xx_set_play_sample_rate(chip, runtime->rate); + return 0; +} + +static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if (runtime->periods == CS46XX_FRAGS) { + if (runtime->dma_area != chip->capt.hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = chip->capt.hw_area; + runtime->dma_addr = chip->capt.hw_addr; + runtime->dma_bytes = chip->capt.hw_size; + substream->ops = &snd_cs46xx_capture_ops; + } else { + if (runtime->dma_area == chip->capt.hw_area) { + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + substream->ops = &snd_cs46xx_capture_indirect_ops; + } + return 0; +} + +static int snd_cs46xx_capture_hw_free(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (runtime->dma_area != chip->capt.hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + return 0; +} + +static int snd_cs46xx_capture_prepare(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_addr); + chip->capt.shift = 2; + chip->capt.sw_bufsize = snd_pcm_lib_buffer_bytes(substream); + chip->capt.sw_data = chip->capt.sw_io = chip->capt.sw_ready = 0; + chip->capt.hw_data = chip->capt.hw_io = chip->capt.hw_ready = 0; + chip->capt.appl_ptr = 0; + snd_cs46xx_set_capture_sample_rate(chip, runtime->rate); + return 0; +} + +static void snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, dev_id, return); + unsigned int status; + + /* + * Read the Interrupt Status Register to clear the interrupt + */ + status = snd_cs46xx_peekBA0(chip, BA0_HISR); + if ((status & 0x7fffffff) == 0) { + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV); + return; + } + + if ((status & HISR_VC0) && chip->pcm) { + if (chip->play.substream) + snd_pcm_period_elapsed(chip->play.substream); + } + if ((status & HISR_VC1) && chip->pcm) { + if (chip->capt.substream) + snd_pcm_period_elapsed(chip->capt.substream); + } + if ((status & HISR_MIDI) && chip->rmidi) { + unsigned char c; + + spin_lock(&chip->reg_lock); + while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_RBE) == 0) { + c = snd_cs46xx_peekBA0(chip, BA0_MIDRP); + if ((chip->midcr & MIDCR_RIE) == 0) + continue; + snd_rawmidi_receive(chip->midi_input, &c, 1); + } + while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) { + if ((chip->midcr & MIDCR_TIE) == 0) + break; + if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) { + chip->midcr &= ~MIDCR_TIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + break; + } + snd_cs46xx_pokeBA0(chip, BA0_MIDWP, c); + } + spin_unlock(&chip->reg_lock); + } + /* + * EOI to the PCI part....reenables interrupts + */ + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV); +} + +static snd_pcm_hardware_t snd_cs46xx_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | +#ifdef SND_CONFIG_CS46XX_ACCEPT_VALID + /* NOT TRUE!!! OSS REQUIRES IT */ + SNDRV_PCM_INFO_MMAP_VALID | +#endif + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME), + formats: (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (256 * 1024), + period_bytes_min: CS46XX_PERIOD_SIZE, + period_bytes_max: CS46XX_PERIOD_SIZE, + periods_min: CS46XX_FRAGS, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_cs46xx_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | +#ifdef SND_CONFIG_CS46XX_ACCEPT_VALID + /* NOT TRUE!!! OSS REQUIRES IT */ + SNDRV_PCM_INFO_MMAP_VALID | +#endif + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (256 * 1024), + period_bytes_min: CS46XX_PERIOD_SIZE, + period_bytes_max: CS46XX_PERIOD_SIZE, + periods_min: CS46XX_FRAGS, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + if ((chip->play.hw_area = snd_malloc_pci_pages(chip->pci, chip->play.hw_size, &chip->play.hw_addr)) == NULL) + return -ENOMEM; + chip->play.substream = substream; + substream->runtime->hw = snd_cs46xx_playback; + chip->active_ctrl(chip, 1); + chip->amplifier_ctrl(chip, 1); + return 0; +} + +static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + if ((chip->capt.hw_area = snd_malloc_pci_pages(chip->pci, chip->capt.hw_size, &chip->capt.hw_addr)) == NULL) + return -ENOMEM; + chip->capt.substream = substream; + substream->runtime->hw = snd_cs46xx_capture; + chip->active_ctrl(chip, 1); + chip->amplifier_ctrl(chip, 1); + return 0; +} + +static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + chip->play.substream = NULL; + snd_free_pci_pages(chip->pci, chip->play.hw_size, chip->play.hw_area, chip->play.hw_addr); + chip->active_ctrl(chip, -1); + chip->amplifier_ctrl(chip, -1); + return 0; +} + +static int snd_cs46xx_capture_close(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + chip->capt.substream = NULL; + snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr); + chip->active_ctrl(chip, -1); + chip->amplifier_ctrl(chip, -1); + return 0; +} + +snd_pcm_ops_t snd_cs46xx_playback_ops = { + open: snd_cs46xx_playback_open, + close: snd_cs46xx_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs46xx_playback_hw_params, + hw_free: snd_cs46xx_playback_hw_free, + prepare: snd_cs46xx_playback_prepare, + trigger: snd_cs46xx_playback_trigger, + pointer: snd_cs46xx_playback_direct_pointer, +}; + +snd_pcm_ops_t snd_cs46xx_playback_indirect_ops = { + open: snd_cs46xx_playback_open, + close: snd_cs46xx_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs46xx_playback_hw_params, + hw_free: snd_cs46xx_playback_hw_free, + prepare: snd_cs46xx_playback_prepare, + trigger: snd_cs46xx_playback_trigger, + copy: snd_cs46xx_playback_copy, + pointer: snd_cs46xx_playback_indirect_pointer, +}; + +snd_pcm_ops_t snd_cs46xx_capture_ops = { + open: snd_cs46xx_capture_open, + close: snd_cs46xx_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs46xx_capture_hw_params, + hw_free: snd_cs46xx_capture_hw_free, + prepare: snd_cs46xx_capture_prepare, + trigger: snd_cs46xx_capture_trigger, + pointer: snd_cs46xx_capture_direct_pointer, +}; + +snd_pcm_ops_t snd_cs46xx_capture_indirect_ops = { + open: snd_cs46xx_capture_open, + close: snd_cs46xx_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs46xx_capture_hw_params, + hw_free: snd_cs46xx_capture_hw_free, + prepare: snd_cs46xx_capture_prepare, + trigger: snd_cs46xx_capture_trigger, + copy: snd_cs46xx_capture_copy, + pointer: snd_cs46xx_capture_indirect_pointer, +}; + +static void snd_cs46xx_pcm_free(snd_pcm_t *pcm) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "CS46xx", device, 1, 1, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_cs46xx_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs46xx_capture_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "CS46xx"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer routines + */ + +static void snd_cs46xx_mixer_free_ac97(ac97_t *ac97) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return); + chip->ac97 = NULL; + chip->eapd_switch = NULL; +} + +static int snd_cs46xx_vol_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 32767; + return 0; +} + +static int snd_cs46xx_vol_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + unsigned int val = snd_cs46xx_peek(chip, reg); + ucontrol->value.integer.value[0] = 0xffff - (val >> 16); + ucontrol->value.integer.value[1] = 0xffff - (val & 0xffff); + return 0; +} + +static int snd_cs46xx_vol_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + unsigned int val = ((0xffff - ucontrol->value.integer.value[0]) << 16 | + (0xffff - ucontrol->value.integer.value[1])); + unsigned int old = snd_cs46xx_peek(chip, reg); + int change = (old != val); + if (change) + snd_cs46xx_poke(chip, reg, val); + return change; +} + +static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = { +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "DAC Volume", + info: snd_cs46xx_vol_info, + get: snd_cs46xx_vol_get, + put: snd_cs46xx_vol_put, + private_value: BA1_PVOL, +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "ADC Volume", + info: snd_cs46xx_vol_info, + get: snd_cs46xx_vol_get, + put: snd_cs46xx_vol_put, + private_value: BA1_CVOL, +}}; + +int __devinit snd_cs46xx_mixer(cs46xx_t *chip) +{ + snd_card_t *card = chip->card; + ac97_t ac97; + snd_ctl_elem_id_t id; + int err; + int idx; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_cs46xx_ac97_write; + ac97.read = snd_cs46xx_ac97_read; + ac97.private_data = chip; + ac97.private_free = snd_cs46xx_mixer_free_ac97; + + snd_cs46xx_ac97_write(&ac97, AC97_MASTER, 0x8000); + for (idx = 0; idx < 100; ++idx) { + if (snd_cs46xx_ac97_read(&ac97, AC97_MASTER) == 0x8000) + goto _ok; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/100); + } + return -ENXIO; + + _ok: + if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97)) < 0) + return err; + for (idx = 0; idx < sizeof(snd_cs46xx_controls) / + sizeof(snd_cs46xx_controls[0]); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_cs46xx_controls[idx], chip); + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + } + + /* get EAPD mixer switch (for voyetra hack) */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "External Amplifier Power Down"); + chip->eapd_switch = snd_ctl_find_id(chip->card, &id); + + return 0; +} + +/* + * RawMIDI interface + */ + +static void snd_cs46xx_midi_reset(cs46xx_t *chip) +{ + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, MIDCR_MRST); + udelay(100); + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); +} + +static int snd_cs46xx_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + chip->active_ctrl(chip, 1); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->uartm |= CS46XX_MODE_INPUT; + chip->midcr |= MIDCR_RXE; + chip->midi_input = substream; + if (!(chip->uartm & CS46XX_MODE_OUTPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs46xx_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE); + chip->midi_input = NULL; + if (!(chip->uartm & CS46XX_MODE_OUTPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS46XX_MODE_INPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->active_ctrl(chip, -1); + return 0; +} + +static int snd_cs46xx_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + chip->active_ctrl(chip, 1); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->uartm |= CS46XX_MODE_OUTPUT; + chip->midcr |= MIDCR_TXE; + chip->midi_output = substream; + if (!(chip->uartm & CS46XX_MODE_INPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs46xx_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE); + chip->midi_output = NULL; + if (!(chip->uartm & CS46XX_MODE_INPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS46XX_MODE_OUTPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->active_ctrl(chip, -1); + return 0; +} + +static void snd_cs46xx_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & MIDCR_RIE) == 0) { + chip->midcr |= MIDCR_RIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & MIDCR_RIE) { + chip->midcr &= ~MIDCR_RIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs46xx_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return); + unsigned char byte; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & MIDCR_TIE) == 0) { + chip->midcr |= MIDCR_TIE; + /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ + while ((chip->midcr & MIDCR_TIE) && + (snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + chip->midcr &= ~MIDCR_TIE; + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDWP, byte); + } + } + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & MIDCR_TIE) { + chip->midcr &= ~MIDCR_TIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static snd_rawmidi_ops_t snd_cs46xx_midi_output = +{ + open: snd_cs46xx_midi_output_open, + close: snd_cs46xx_midi_output_close, + trigger: snd_cs46xx_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_cs46xx_midi_input = +{ + open: snd_cs46xx_midi_input_open, + close: snd_cs46xx_midi_input_close, + trigger: snd_cs46xx_midi_input_trigger, +}; + +int __devinit snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, "CS46XX"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = chip; + chip->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = NULL; + return 0; +} + +/* + * proc interface + */ + +static long snd_cs46xx_io_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + snd_cs46xx_region_t *region = (snd_cs46xx_region_t *)entry->private_data; + + size = count; + if (file->f_pos + size > region->size) + size = region->size - file->f_pos; + if (size > 0) { + char *tmp; + long res; + unsigned long virt; + if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL) + return -ENOMEM; + virt = region->remap_addr + file->f_pos; + memcpy_fromio(tmp, virt, size); + if (copy_to_user(buf, tmp, size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = { + read: snd_cs46xx_io_read, +}; + +static int __devinit snd_cs46xx_proc_init(snd_card_t * card, cs46xx_t *chip) +{ + snd_info_entry_t *entry; + int idx; + + for (idx = 0; idx < 5; idx++) { + snd_cs46xx_region_t *region = &chip->region.idx[idx]; + entry = snd_info_create_card_entry(card, region->name, card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &snd_cs46xx_proc_io_ops; + entry->size = region->size; + entry->mode = S_IFREG | S_IRUSR; + if (snd_info_register(entry) < 0) { + snd_info_unregister(entry); + entry = NULL; + } + } + region->proc_entry = entry; + } + return 0; +} + +static int snd_cs46xx_proc_done(cs46xx_t *chip) +{ + int idx; + + for (idx = 0; idx < 5; idx++) { + snd_cs46xx_region_t *region = &chip->region.idx[idx]; + if (region->proc_entry) { + snd_info_unregister((snd_info_entry_t *) region->proc_entry); + region->proc_entry = NULL; + } + } + return 0; +} + +/* + * stop the h/w + */ +static void snd_cs46xx_hw_stop(cs46xx_t *chip) +{ + unsigned int tmp; + + tmp = snd_cs46xx_peek(chip, BA1_PFIE); + tmp &= ~0x0000f03f; + tmp |= 0x00000010; + snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt disable */ + + tmp = snd_cs46xx_peek(chip, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000011; + snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt disable */ + + /* + * Stop playback DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000); + + /* + * Reset the processor. + */ + snd_cs46xx_reset(chip); + + snd_cs46xx_proc_stop(chip); + + /* + * Power down the PLL. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0); + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); +} + + +static int snd_cs46xx_free(cs46xx_t *chip) +{ + int idx; + + snd_assert(chip != NULL, return -EINVAL); + + if (chip->active_ctrl) + chip->active_ctrl(chip, 1); + +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->amplifier_ctrl) + chip->amplifier_ctrl(chip, -chip->amplifier); /* force to off */ + + snd_cs46xx_proc_done(chip); + + if (chip->region.idx[0].resource) + snd_cs46xx_hw_stop(chip); + + for (idx = 0; idx < 5; idx++) { + snd_cs46xx_region_t *region = &chip->region.idx[idx]; + if (region->remap_addr) + iounmap((void *) region->remap_addr); + if (region->resource) { + release_resource(region->resource); + kfree_nocheck(region->resource); + } + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + if (chip->active_ctrl) + chip->active_ctrl(chip, -chip->amplifier); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_cs46xx_dev_free(snd_device_t *device) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, device->device_data, return -ENXIO); + return snd_cs46xx_free(chip); +} + +/* + * initialize chip + */ + +static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait) +{ + unsigned int tmp; + int timeout; + + /* + * First, blast the clock control register to zero so that the PLL starts + * out in a known state, and blast the master serial port control register + * to zero so that the serial ports also start out in a known state. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0); + snd_cs46xx_pokeBA0(chip, BA0_SERMC1, 0); + + /* + * If we are in AC97 mode, then we must set the part to a host controlled + * AC-link. Otherwise, we won't be able to bring up the link. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_1_03); /* 1.03 codec */ + /* snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0); */ /* 2.00 codec */ + + /* + * Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 + * spec) and then drive it high. This is done for non AC97 modes since + * there might be logic external to the CS461x that uses the ARST# line + * for a reset. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, 0); + udelay(50); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_RSTN); + + /* + * The first thing we do here is to enable sync generation. As soon + * as we start receiving bit clock, we'll start producing the SYNC + * signal. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN); + + /* + * Now wait for a short while to allow the AC97 part to start + * generating bit clock (so we don't try to start the PLL without an + * input clock). + */ + mdelay(1); + + /* + * Set the serial port timing configuration, so that + * the clock control circuit gets its clock from the correct place. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97); + + /* + * Write the selected clock control setup to the hardware. Do not turn on + * SWCE yet (if requested), so that the devices clocked by the output of + * PLL are not clocked until the PLL is stable. + */ + snd_cs46xx_pokeBA0(chip, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ); + snd_cs46xx_pokeBA0(chip, BA0_PLLM, 0x3a); + snd_cs46xx_pokeBA0(chip, BA0_CLKCR2, CLKCR2_PDIVS_8); + + /* + * Power up the PLL. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP); + + /* + * Wait until the PLL has stabilized. + */ + mdelay(1); + + /* + * Turn on clocking of the core so that we can setup the serial ports. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP | CLKCR1_SWCE); + + /* + * Fill the serial port FIFOs with silence. + */ + snd_cs46xx_clear_serial_FIFOs(chip); + + /* + * Set the serial port FIFO pointer to the first sample in the FIFO. + */ + /* snd_cs46xx_pokeBA0(chip, BA0_SERBSP, 0); */ + + /* + * Write the serial port configuration to the part. The master + * enable bit is not set until all other values have been written. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN); + snd_cs46xx_pokeBA0(chip, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN); + snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE); + + /* + * Wait for the codec ready signal from the AC97 codec. + */ + timeout = 150; + while (timeout-- > 0) { + /* + * Read the AC97 status register to see if we've seen a CODEC READY + * signal from the AC97 codec. + */ + if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_CRDY) + goto ok1; + if (busywait) + mdelay(10); + else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ+99)/100); + } + } + + + snd_printk("create - never read codec ready from AC'97\n"); + snd_printk("it is not probably bug, try to use CS4236 driver\n"); + snd_cs46xx_free(chip); + return -EIO; + ok1: + /* + * Assert the vaid frame signal so that we can start sending commands + * to the AC97 codec. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); + + /* + * Wait until we've sampled input slots 3 and 4 as valid, meaning that + * the codec is pumping ADC data across the AC-link. + */ + timeout = 150; + while (timeout-- > 0) { + /* + * Read the input slot valid register and see if input slots 3 and + * 4 are valid yet. + */ + if ((snd_cs46xx_peekBA0(chip, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) + goto ok2; + if (busywait) + mdelay(10); + else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ+99)/100); + } + } + + snd_printk("create - never read ISV3 & ISV4 from AC'97\n"); + snd_cs46xx_free(chip); + return -EIO; + ok2: + + /* + * Now, assert valid frame and the slot 3 and 4 valid bits. This will + * commense the transfer of digital audio data to the AC97 codec. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4); + + /* + * Power down the DAC and ADC. We will power them up (if) when we need + * them. + */ + /* snd_cs46xx_pokeBA0(chip, BA0_AC97_POWERDOWN, 0x300); */ + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + /* tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; */ + /* snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); */ + + /* + * Reset the processor. + */ + snd_cs46xx_reset(chip); + + /* + * Download the image to the processor. + */ + if (snd_cs46xx_download_image(chip) < 0) { + snd_printk("image download error\n"); + snd_cs46xx_free(chip); + return -EIO; + } + + /* + * Stop playback DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + chip->play.ctl = tmp & 0xffff0000; + snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + chip->capt.ctl = tmp & 0x0000ffff; + snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000); + + snd_cs46xx_set_play_sample_rate(chip, 8000); + snd_cs46xx_set_capture_sample_rate(chip, 8000); + + snd_cs46xx_proc_start(chip); + + /* + * Enable interrupts on the part. + */ + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM); + + tmp = snd_cs46xx_peek(chip, BA1_PFIE); + tmp &= ~0x0000f03f; + snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */ + + tmp = snd_cs46xx_peek(chip, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000001; + snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */ + + /* set the attenuation to 0dB */ + snd_cs46xx_poke(chip, BA1_PVOL, 0x80008000); + snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000); + + return 0; +} + + +/* + * AMP control - null AMP + */ + +static void amp_none(cs46xx_t *chip, int change) +{ +} + +/* + * Crystal EAPD mode + */ + +static void amp_voyetra(cs46xx_t *chip, int change) +{ + /* Manage the EAPD bit on the Crystal 4297 + and the Analog AD1885 */ + + int old = chip->amplifier; + int oval, val; + + chip->amplifier += change; + oval = snd_cs46xx_codec_read(chip, AC97_POWERDOWN); + val = oval; + if (chip->amplifier && !old) { + /* Turn the EAPD amp on */ + val |= 0x8000; + } else if (old && !chip->amplifier) { + /* Turn the EAPD amp off */ + val &= ~0x8000; + } + if (val != oval) { + snd_cs46xx_codec_write(chip, AC97_POWERDOWN, val); + if (chip->eapd_switch) + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->eapd_switch->id); + } +} + + +/* + * Game Theatre XP card - EGPIO[2] is used to enable the external amp. + */ + +static void amp_hercules(cs46xx_t *chip, int change) +{ + int old = chip->amplifier; + + chip->amplifier += change; + if (chip->amplifier && !old) { + snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, + EGPIODR_GPOE2); /* enable EGPIO2 output */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, + EGPIOPTR_GPPT2); /* open-drain on output */ + } else if (old && !chip->amplifier) { + snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 0); /* disable */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 0); /* disable */ + } +} + + +#if 0 +/* + * Untested + */ + +static void amp_voyetra_4294(cs46xx_t *chip, int change) +{ + chip->amplifier += change; + + if (chip->amplifier) { + /* Switch the GPIO pins 7 and 8 to open drain */ + snd_cs46xx_codec_write(chip, 0x4C, + snd_cs46xx_codec_read(chip, 0x4C) & 0xFE7F); + snd_cs46xx_codec_write(chip, 0x4E, + snd_cs46xx_codec_read(chip, 0x4E) | 0x0180); + /* Now wake the AMP (this might be backwards) */ + snd_cs46xx_codec_write(chip, 0x54, + snd_cs46xx_codec_read(chip, 0x54) & ~0x0180); + } else { + snd_cs46xx_codec_write(chip, 0x54, + snd_cs46xx_codec_read(chip, 0x54) | 0x0180); + } +} +#endif + + +/* + * piix4 pci ids + */ +#ifndef PCI_VENDOR_ID_INTEL +#define PCI_VENDOR_ID_INTEL 0x8086 +#endif /* PCI_VENDOR_ID_INTEL */ + +#ifndef PCI_DEVICE_ID_INTEL_82371AB_3 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#endif /* PCI_DEVICE_ID_INTEL_82371AB_3 */ + +/* + * Handle the CLKRUN on a thinkpad. We must disable CLKRUN support + * whenever we need to beat on the chip. + * + * The original idea and code for this hack comes from David Kaiser at + * Linuxcare. Perhaps one day Crystal will document their chips well + * enough to make them useful. + */ + +static void clkrun_hack(cs46xx_t *chip, int change) +{ + u16 control; + int old; + + if (chip->acpi_dev == NULL) + return; + + old = chip->amplifier; + chip->amplifier += change; + + /* Read ACPI port */ + control = inw(chip->acpi_port + 0x10); + + /* Flip CLKRUN off while running */ + if (! chip->amplifier && old) + outw(control | 0x2000, chip->acpi_port + 0x10); + else if (chip->amplifier && ! old) + outw(control & ~0x2000, chip->acpi_port + 0x10); +} + + +/* + * detect intel piix4 + */ +static void clkrun_init(cs46xx_t *chip) +{ + u8 pp; + + chip->acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + if (chip->acpi_dev == NULL) + return; /* Not a thinkpad thats for sure */ + + /* Find the control port */ + pci_read_config_byte(chip->acpi_dev, 0x41, &pp); + chip->acpi_port = pp << 8; +} + + +/* + * Card subid table + */ + +struct cs_card_type +{ + u16 vendor; + u16 id; + char *name; + void (*init)(cs46xx_t *); + void (*amp)(cs46xx_t *, int); + void (*active)(cs46xx_t *, int); +}; + +static struct cs_card_type __initdata cards[] = { + {0x1489, 0x7001, "Genius Soundmaker 128 value", NULL, amp_none, NULL}, + {0x5053, 0x3357, "Voyetra", NULL, amp_voyetra, NULL}, + {0x1071, 0x6003, "Mitac MI6020/21", NULL, amp_voyetra, NULL}, + {0x14AF, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + {0x1681, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + {0x1681, 0x0051, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + {0x1681, 0x0052, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + {0x1681, 0x0053, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + {0x1681, 0x0054, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + /* Not sure if the 570 needs the clkrun hack */ + {PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", clkrun_init, NULL, clkrun_hack}, + {PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", clkrun_init, NULL, clkrun_hack}, + {PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL}, + {0, 0, "Card without SSID set", NULL, NULL, NULL }, + {0, 0, NULL, NULL, NULL, NULL} +}; + + +/* + * APM support + */ +#ifdef CONFIG_PM +void snd_cs46xx_suspend(cs46xx_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + snd_pcm_suspend_all(chip->pcm); + // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL); + // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE); + snd_cs46xx_hw_stop(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +void snd_cs46xx_resume(cs46xx_t *chip) +{ + snd_card_t *card = chip->card; + int amp_saved; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + pci_enable_device(chip->pci); + amp_saved = chip->amplifier; + chip->amplifier = 0; + chip->active_ctrl(chip, 1); /* force to on */ + + snd_cs46xx_chip_init(chip, 1); + +#if 0 + snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, + chip->ac97_general_purpose); + snd_cs46xx_codec_write(chip, AC97_POWER_CONTROL, + chip->ac97_powerdown); + mdelay(10); + snd_cs46xx_codec_write(chip, BA0_AC97_POWERDOWN, + chip->ac97_powerdown); + mdelay(5); +#endif + + snd_ac97_resume(chip->ac97); + + if (amp_saved) + chip->amplifier_ctrl(chip, 1); /* try to turn on */ + if (! amp_saved) { + chip->amplifier = 1; + chip->active_ctrl(chip, -1); + } + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +static int snd_cs46xx_set_power_state(snd_card_t *card, unsigned int power_state) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_cs46xx_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_cs46xx_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} +#endif /* CONFIG_PM */ + + +/* + */ + +int __devinit snd_cs46xx_create(snd_card_t * card, + struct pci_dev * pci, + int external_amp, int thinkpad, + cs46xx_t ** rchip) +{ + cs46xx_t *chip; + int err, idx; + snd_cs46xx_region_t *region; + struct cs_card_type *cp; + u16 ss_card, ss_vendor; + static snd_device_ops_t ops = { + dev_free: snd_cs46xx_dev_free, + }; + + *rchip = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = snd_magic_kcalloc(cs46xx_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + chip->card = card; + chip->pci = pci; + chip->play.hw_size = PAGE_SIZE; + chip->capt.hw_size = PAGE_SIZE; + chip->irq = -1; + chip->ba0_addr = pci_resource_start(pci, 0); + chip->ba1_addr = pci_resource_start(pci, 1); + if (chip->ba0_addr == 0 || chip->ba0_addr == ~0 || + chip->ba1_addr == 0 || chip->ba1_addr == ~0) { + snd_cs46xx_free(chip); + snd_printk("wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n", chip->ba0_addr, chip->ba1_addr); + return -ENOMEM; + } + + region = &chip->region.name.ba0; + strcpy(region->name, "CS46xx_BA0"); + region->base = chip->ba0_addr; + region->size = CS46XX_BA0_SIZE; + + region = &chip->region.name.data0; + strcpy(region->name, "CS46xx_BA1_data0"); + region->base = chip->ba1_addr + BA1_SP_DMEM0; + region->size = CS46XX_BA1_DATA0_SIZE; + + region = &chip->region.name.data1; + strcpy(region->name, "CS46xx_BA1_data1"); + region->base = chip->ba1_addr + BA1_SP_DMEM1; + region->size = CS46XX_BA1_DATA1_SIZE; + + region = &chip->region.name.pmem; + strcpy(region->name, "CS46xx_BA1_pmem"); + region->base = chip->ba1_addr + BA1_SP_PMEM; + region->size = CS46XX_BA1_PRG_SIZE; + + region = &chip->region.name.reg; + strcpy(region->name, "CS46xx_BA1_reg"); + region->base = chip->ba1_addr + BA1_SP_REG; + region->size = CS46XX_BA1_REG_SIZE; + + /* set up amp and clkrun hack */ + pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &ss_card); + + for (cp = &cards[0]; cp->name; cp++) { + if (cp->vendor == ss_vendor && cp->id == ss_card) { + snd_printd("hack for %s enabled\n", cp->name); + if (cp->init) + cp->init(chip); + chip->amplifier_ctrl = cp->amp; + chip->active_ctrl = cp->active; + break; + } + } + + if (external_amp) { + snd_printk("Crystal EAPD support forced on.\n"); + chip->amplifier_ctrl = amp_voyetra; + } + + if (thinkpad) { + snd_printk("Activating CLKRUN hack for Thinkpad.\n"); + chip->active_ctrl = clkrun_hack; + clkrun_init(chip); + } + + if (chip->amplifier_ctrl == NULL) + chip->amplifier_ctrl = amp_none; + if (chip->active_ctrl == NULL) + chip->active_ctrl = amp_none; + + chip->active_ctrl(chip, 1); + + pci_set_master(pci); + + for (idx = 0; idx < 5; idx++) { + region = &chip->region.idx[idx]; + if ((region->resource = request_mem_region(region->base, region->size, region->name)) == NULL) { + snd_cs46xx_free(chip); + snd_printk("unable to request memory region 0x%lx-0x%lx\n", region->base, region->base + region->size - 1); + return -EBUSY; + } + region->remap_addr = (unsigned long) ioremap_nocache(region->base, region->size); + if (region->remap_addr == 0) { + snd_cs46xx_free(chip); + snd_printk("%s ioremap problem\n", region->name); + return -ENOMEM; + } + } + if (request_irq(pci->irq, snd_cs46xx_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS46XX", (void *) chip)) { + snd_cs46xx_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + + err = snd_cs46xx_chip_init(chip, 0); + if (err < 0) { + snd_cs46xx_free(chip); + return err; + } + + snd_cs46xx_proc_init(card, chip); + +#ifdef CONFIG_PM + card->set_power_state = snd_cs46xx_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_cs46xx_free(chip); + return err; + } + + chip->active_ctrl(chip, -1); + + *rchip = chip; + return 0; +} diff -Nru a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,29 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _emu10k1.o + +list-multi := snd-emu10k1.o snd-emu10k1-synth.o + +export-objs := emu10k1_main.o + +snd-emu10k1-objs := emu10k1.o emu10k1_main.o \ + irq.o memory.o voice.o emumpu401.o emupcm.o io.o \ + emuproc.o emumixer.o emufx.o +snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o snd-emu10k1-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-emu10k1.o: $(snd-emu10k1-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emu10k1-objs) + +snd-emu10k1-synth.o: $(snd-emu10k1-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emu10k1-synth-objs) diff -Nru a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emu10k1.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,250 @@ +/* + * The driver for the EMU10K1 (SB Live!) based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("EMU10K1"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Creative Labs,SB Live!/PCI512/E-mu APS}," + "{Creative Labs,SB Audigy}}"); + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#define ENABLE_SYNTH +#include +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_extin[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_extout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; +static int snd_max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64}; +static int snd_max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128}; +static int snd_enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for the EMU10K1 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for the EMU10K1 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable the EMU10K1 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_extin, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_extin, "Available external inputs for FX8010. Zero=default."); +MODULE_PARM_SYNTAX(snd_extin, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16"); +MODULE_PARM(snd_extout, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_extout, "Available external outputs for FX8010. Zero=default."); +MODULE_PARM_SYNTAX(snd_extout, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16"); +MODULE_PARM(snd_seq_ports, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_seq_ports, "Allocated sequencer ports for internal synthesizer."); +MODULE_PARM_SYNTAX(snd_seq_ports, SNDRV_ENABLED "allows:{{0,32}}"); +MODULE_PARM(snd_max_synth_voices, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_max_synth_voices, "Maximum number of voices for WaveTable."); +MODULE_PARM_SYNTAX(snd_max_synth_voices, SNDRV_ENABLED); +MODULE_PARM(snd_max_buffer_size, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_max_buffer_size, "Maximum sample buffer size in MB."); +MODULE_PARM_SYNTAX(snd_max_buffer_size, SNDRV_ENABLED); +MODULE_PARM(snd_enable_ir, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable_ir, "Enable IR."); +MODULE_PARM_SYNTAX(snd_enable_ir, SNDRV_ENABLE_DESC); + +static struct pci_device_id snd_emu10k1_ids[] __devinitdata = { + { 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* EMU10K1 */ + { 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids); + +static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + emu10k1_t *emu; +#ifdef ENABLE_SYNTH + snd_seq_device_t *wave = NULL; +#endif + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if (snd_max_buffer_size[dev] < 32) + snd_max_buffer_size[dev] = 32; + else if (snd_max_buffer_size[dev] > 1024) + snd_max_buffer_size[dev] = 1024; + if ((err = snd_emu10k1_create(card, pci, snd_extin[dev], snd_extout[dev], + (long)snd_max_buffer_size[dev] * 1024 * 1024, + snd_enable_ir[dev], + &emu)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_fx8010_pcm(emu, 3, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (!emu->APS) { /* APS board has not an AC97 mixer */ + if ((err = snd_emu10k1_mixer(emu)) < 0) { + snd_card_free(card); + return err; + } + } + if (emu->audigy) { + if ((err = snd_emu10k1_audigy_midi(emu)) < 0) { + snd_card_free(card); + return err; + } + } else { + if ((err = snd_emu10k1_midi(emu)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifdef ENABLE_SYNTH + if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, + sizeof(snd_emu10k1_synth_arg_t), &wave) < 0 || + wave == NULL) { + snd_printk("can't initialize Emu10k1 wavetable synth\n"); + } else { + snd_emu10k1_synth_arg_t *arg; + arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); + strcpy(wave->name, "Emu-10k1 Synth"); + arg->hwptr = emu; + arg->index = 1; + arg->seq_ports = snd_seq_ports[dev]; + arg->max_voices = snd_max_synth_voices[dev]; + } +#endif + + if (emu->audigy) { + strcpy(card->driver, "Audigy"); + strcpy(card->shortname, "Sound Blaster Audigy"); + } else if (emu->APS) { + strcpy(card->driver, "E-mu APS"); + strcpy(card->shortname, "E-mu APS"); + } else { + strcpy(card->driver, "EMU10K1"); + strcpy(card->shortname, "Sound Blaster Live!"); + } + sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, emu->port, emu->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_card_emu10k1_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "EMU10K1/Audigy", + id_table: snd_emu10k1_ids, + probe: snd_card_emu10k1_probe, + remove: __devexit_p(snd_card_emu10k1_remove), +}; + +static int __init alsa_card_emu10k1_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("EMU10K1/Audigy soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_emu10k1_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_emu10k1_init) +module_exit(alsa_card_emu10k1_exit) + +#ifndef MODULE + +/* format is: snd-emu10k1=snd_enable,snd_index,snd_id, + snd_seq_ports,snd_max_synth_voices */ + +static int __init alsa_card_emu10k1_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_seq_ports[nr_dev]) == 2 && + get_option(&str,&snd_max_synth_voices[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-emu10k1=", alsa_card_emu10k1_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emu10k1_callback.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,541 @@ +/* + * synth callback routines for Emu10k1 + * + * Copyright (C) 2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "emu10k1_synth_local.h" +#include + +/* voice status */ +enum { + V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END +}; + +/* Keeps track of what we are finding */ +typedef struct best_voice { + unsigned int time; + int voice; +} best_voice_t; + +/* + * prototypes + */ +static void lookup_voices(snd_emux_t *emu, emu10k1_t *hw, best_voice_t *best, int active_only); +static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port); +static int start_voice(snd_emux_voice_t *vp); +static void trigger_voice(snd_emux_voice_t *vp); +static void release_voice(snd_emux_voice_t *vp); +static void update_voice(snd_emux_voice_t *vp, int update); +static void terminate_voice(snd_emux_voice_t *vp); +static void free_voice(snd_emux_voice_t *vp); + +static void set_fmmod(emu10k1_t *hw, snd_emux_voice_t *vp); +static void set_fm2frq2(emu10k1_t *hw, snd_emux_voice_t *vp); +static void set_filterQ(emu10k1_t *hw, snd_emux_voice_t *vp); + +/* + * Ensure a value is between two points + * macro evaluates its args more than once, so changed to upper-case. + */ +#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) +#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) + + +/* + * set up operators + */ +static snd_emux_operators_t emu10k1_ops = { + owner: THIS_MODULE, + get_voice: get_voice, + prepare: start_voice, + trigger: trigger_voice, + release: release_voice, + update: update_voice, + terminate: terminate_voice, + free_voice: free_voice, + sample_new: snd_emu10k1_sample_new, + sample_free: snd_emu10k1_sample_free, +}; + +void +snd_emu10k1_ops_setup(snd_emux_t *emu) +{ + emu->ops = emu10k1_ops; +} + + +/* + * get more voice for pcm + * + * terminate most inactive voice and give it as a pcm voice. + */ +int +snd_emu10k1_synth_get_voice(emu10k1_t *hw) +{ + snd_emux_t *emu; + snd_emux_voice_t *vp; + best_voice_t best[V_END]; + unsigned long flags; + int i; + + emu = snd_magic_cast(snd_emux_t, hw->synth, return -EINVAL); + + spin_lock_irqsave(&emu->voice_lock, flags); + lookup_voices(emu, hw, best, 1); /* no OFF voices */ + for (i = 0; i < V_END; i++) { + if (best[i].voice >= 0) { + int ch; + vp = &emu->voices[best[i].voice]; + if ((ch = vp->ch) < 0) { + //printk("synth_get_voice: ch < 0 (%d) ??", i); + continue; + } + vp->emu->num_voices--; + vp->ch = -1; + vp->state = SNDRV_EMUX_ST_OFF; + spin_unlock_irqrestore(&emu->voice_lock, flags); + return ch; + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + + /* not found */ + return -ENOMEM; +} + + +/* + * turn off the voice (not terminated) + */ +static void +release_voice(snd_emux_voice_t *vp) +{ + int dcysusv; + emu10k1_t *hw; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; + snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK; + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv); +} + + +/* + * terminate the voice + */ +static void +terminate_voice(snd_emux_voice_t *vp) +{ + emu10k1_t *hw; + + snd_assert(vp, return); + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); + if (vp->block) { + emu10k1_memblk_t *emem; + emem = (emu10k1_memblk_t *)vp->block; + if (emem->map_locked > 0) + emem->map_locked--; + } +} + +/* + * release the voice to system + */ +static void +free_voice(snd_emux_voice_t *vp) +{ + emu10k1_t *hw; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + if (vp->ch >= 0) { + snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00); + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); + // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0); + snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff); + snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff); + snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]); + vp->emu->num_voices--; + vp->ch = -1; + } +} + + +/* + * update registers + */ +static void +update_voice(snd_emux_voice_t *vp, int update) +{ + emu10k1_t *hw; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + if (update & SNDRV_EMUX_UPDATE_VOLUME) + snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol); + if (update & SNDRV_EMUX_UPDATE_PITCH) + snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); + if (update & SNDRV_EMUX_UPDATE_PAN) { + snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan); + snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux); + } + if (update & SNDRV_EMUX_UPDATE_FMMOD) + set_fmmod(hw, vp); + if (update & SNDRV_EMUX_UPDATE_TREMFREQ) + snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); + if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) + set_fm2frq2(hw, vp); + if (update & SNDRV_EMUX_UPDATE_Q) + set_filterQ(hw, vp); +} + + +/* + * look up voice table - get the best voice in order of preference + */ +/* spinlock held! */ +static void +lookup_voices(snd_emux_t *emu, emu10k1_t *hw, best_voice_t *best, int active_only) +{ + snd_emux_voice_t *vp; + best_voice_t *bp; + int i; + + for (i = 0; i < V_END; i++) { + best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */; + best[i].voice = -1; + } + + /* + * Go through them all and get a best one to use. + * NOTE: could also look at volume and pick the quietest one. + */ + for (i = 0; i < emu->max_voices; i++) { + int state, val; + + vp = &emu->voices[i]; + state = vp->state; + if (state == SNDRV_EMUX_ST_OFF) { + if (vp->ch < 0) { + if (active_only) + continue; + bp = best + V_FREE; + } else + bp = best + V_OFF; + } + else if (state == SNDRV_EMUX_ST_RELEASED || + state == SNDRV_EMUX_ST_PENDING) { + bp = best + V_RELEASED; +#if 0 + val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch); + if (! val) + bp = best + V_OFF; +#endif + } + else if (state == SNDRV_EMUX_ST_STANDBY) + continue; + else if (state & SNDRV_EMUX_ST_ON) + bp = best + V_PLAYING; + else + continue; + + /* check if sample is finished playing (non-looping only) */ + if (bp != best + V_OFF && bp != best + V_FREE && + (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { + val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch); + if (val >= vp->reg.loopstart) + bp = best + V_OFF; + } + + if (vp->time < bp->time) { + bp->time = vp->time; + bp->voice = i; + } + } +} + +/* + * get an empty voice + * + * emu->voice_lock is already held. + */ +static snd_emux_voice_t * +get_voice(snd_emux_t *emu, snd_emux_port_t *port) +{ + emu10k1_t *hw; + snd_emux_voice_t *vp; + best_voice_t best[V_END]; + int i; + + hw = snd_magic_cast(emu10k1_t, emu->hw, return NULL); + + lookup_voices(emu, hw, best, 0); + for (i = 0; i < V_END; i++) { + if (best[i].voice >= 0) { + vp = &emu->voices[best[i].voice]; + if (vp->ch < 0) { + /* allocate a voice */ + emu10k1_voice_t *hwvoice; + if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 0, &hwvoice) < 0 || hwvoice == NULL) + continue; + vp->ch = hwvoice->number; + emu->num_voices++; + } + return vp; + } + } + + /* not found */ + return NULL; +} + +/* + * prepare envelopes and LFOs + */ +static int +start_voice(snd_emux_voice_t *vp) +{ + unsigned int temp; + int ch; + unsigned int addr, mapped_offset; + snd_midi_channel_t *chan; + emu10k1_t *hw; + emu10k1_memblk_t *emem; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return -EINVAL); + ch = vp->ch; + snd_assert(ch >= 0, return -EINVAL); + chan = vp->chan; + + emem = (emu10k1_memblk_t *)vp->block; + if (emem == NULL) + return -EINVAL; + emem->map_locked++; + if (snd_emu10k1_memblk_map(hw, emem) < 0) { + // printk("emu: cannot map!\n"); + return -ENOMEM; + } + mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1; + vp->reg.start += mapped_offset; + vp->reg.end += mapped_offset; + vp->reg.loopstart += mapped_offset; + vp->reg.loopend += mapped_offset; + + /* set channel routing */ + /* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */ + if (hw->audigy) { + temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | + (FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24); + snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp); + } else { + temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | + (FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28); + snd_emu10k1_ptr_write(hw, FXRT, ch, temp); + } + + /* channel to be silent and idle */ + snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0080); + snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF); + snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF); + snd_emu10k1_ptr_write(hw, PTRX, ch, 0); + snd_emu10k1_ptr_write(hw, CPF, ch, 0); + + /* set pitch offset */ + snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); + + /* set envelope parameters */ + snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay); + snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld); + snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus); + snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay); + snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld); + /* decay/sustain parameter for volume envelope is used + for triggerg the voice */ + + /* cutoff and volume */ + temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol; + snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp); + + /* modulation envelope heights */ + snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe); + + /* lfo1/2 delay */ + snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay); + snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay); + + /* lfo1 pitch & cutoff shift */ + set_fmmod(hw, vp); + /* lfo1 volume & freq */ + snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); + /* lfo2 pitch & freq */ + set_fm2frq2(hw, vp); + + /* reverb and loop start (reverb 8bit, MSB) */ + temp = vp->reg.parm.reverb; + temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + addr = vp->reg.loopstart; + snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr); + + /* chorus & loop end (chorus 8bit, MSB) */ + addr = vp->reg.loopend; + temp = vp->reg.parm.chorus; + temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + temp = (temp <<24) | addr; + snd_emu10k1_ptr_write(hw, DSL, ch, temp); + + /* clear filter delay memory */ + snd_emu10k1_ptr_write(hw, Z1, ch, 0); + snd_emu10k1_ptr_write(hw, Z2, ch, 0); + + /* invalidate maps */ + temp = (hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + snd_emu10k1_ptr_write(hw, MAPA, ch, temp); + snd_emu10k1_ptr_write(hw, MAPB, ch, temp); +#if 0 + /* cache */ + { + unsigned int val, sample; + val = 32; + if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) + sample = 0x80808080; + else { + sample = 0; + val *= 2; + } + + /* cache */ + snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16); + snd_emu10k1_ptr_write(hw, CDE, ch, sample); + snd_emu10k1_ptr_write(hw, CDF, ch, sample); + + /* invalidate maps */ + temp = ((unsigned int)hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + snd_emu10k1_ptr_write(hw, MAPA, ch, temp); + snd_emu10k1_ptr_write(hw, MAPB, ch, temp); + + /* fill cache */ + val -= 4; + val <<= 25; + val |= 0x1c << 16; + snd_emu10k1_ptr_write(hw, CCR, ch, val); + } +#endif + + /* Q & current address (Q 4bit value, MSB) */ + addr = vp->reg.start; + temp = vp->reg.parm.filterQ; + temp = (temp<<28) | addr; + if (vp->apitch < 0xe400) + temp |= CCCA_INTERPROM_0; + else { + unsigned int shift = (vp->apitch - 0xe000) >> 10; + temp |= shift << 25; + } + if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) + temp |= CCCA_8BITSELECT; + snd_emu10k1_ptr_write(hw, CCCA, ch, temp); + + /* reset volume */ + temp = (unsigned int)vp->vtarget << 16; + snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget); + snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00); + return 0; +} + +/* + * Start envelope + */ +static void +trigger_voice(snd_emux_voice_t *vp) +{ + unsigned int temp, ptarget; + emu10k1_t *hw; + emu10k1_memblk_t *emem; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + + emem = (emu10k1_memblk_t *)vp->block; + if (! emem || emem->mapped_page < 0) + return; /* not mapped */ + +#if 0 + ptarget = (unsigned int)vp->ptarget << 16; +#else + ptarget = IP_TO_CP(vp->apitch); +#endif + /* set pitch target and pan (volume) */ + temp = ptarget | (vp->apan << 8) | vp->aaux; + snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp); + + /* pitch target */ + snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget); + + /* trigger voice */ + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK); +} + +#define MOD_SENSE 18 + +/* set lfo1 modulation height and cutoff */ +static void +set_fmmod(emu10k1_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fmmod; + short pitch; + unsigned char cutoff; + int modulation; + + pitch = (char)(vp->reg.parm.fmmod>>8); + cutoff = (vp->reg.parm.fmmod & 0xff); + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fmmod = ((unsigned char)pitch<<8) | cutoff; + snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod); +} + +/* set lfo2 pitch & frequency */ +static void +set_fm2frq2(emu10k1_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fm2frq2; + short pitch; + unsigned char freq; + int modulation; + + pitch = (char)(vp->reg.parm.fm2frq2>>8); + freq = vp->reg.parm.fm2frq2 & 0xff; + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fm2frq2 = ((unsigned char)pitch<<8) | freq; + snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2); +} + +/* set filterQ */ +static void +set_filterQ(emu10k1_t *hw, snd_emux_voice_t *vp) +{ + unsigned int val; + val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE; + val |= (vp->reg.parm.filterQ << 28); + snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val); +} diff -Nru a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emu10k1_main.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,681 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +#if 0 +MODULE_AUTHOR("Jaroslav Kysela , Creative Labs, Inc."); +MODULE_DESCRIPTION("Routines for control of EMU10K1 chips"); +MODULE_LICENSE("GPL"); +#endif + +/************************************************************************* + * EMU10K1 init / done + *************************************************************************/ + +void snd_emu10k1_voice_init(emu10k1_t * emu, int ch) +{ + snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0); + snd_emu10k1_ptr_write(emu, IP, ch, 0); + snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff); + snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff); + snd_emu10k1_ptr_write(emu, PTRX, ch, 0); + snd_emu10k1_ptr_write(emu, CPF, ch, 0); + snd_emu10k1_ptr_write(emu, CCR, ch, 0); + + snd_emu10k1_ptr_write(emu, PSST, ch, 0); + snd_emu10k1_ptr_write(emu, DSL, ch, 0x10); + snd_emu10k1_ptr_write(emu, CCCA, ch, 0); + snd_emu10k1_ptr_write(emu, Z1, ch, 0); + snd_emu10k1_ptr_write(emu, Z2, ch, 0); + snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000); + + snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0); + snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0); + snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff); + snd_emu10k1_ptr_write(emu, PEFE, ch, 0); + snd_emu10k1_ptr_write(emu, FMMOD, ch, 0); + snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24); /* 1 Hz */ + snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24); /* 1 Hz */ + snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0); + + /*** these are last so OFF prevents writing ***/ + snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0); + snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0); + snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0); + snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0); + snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0); + + /* Audigy extra stuffs */ + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100); + snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f); + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0); + } +} + +static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) +{ + int ch, idx, err; + unsigned int silent_page; + + emu->fx8010.itram_size = (16 * 1024)/2; + emu->fx8010.etram_size = 0; + + /* disable audio and lock cache */ + outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG); + + /* reset recording buffers */ + snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, MICBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, FXBA, 0, 0); + snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, ADCBA, 0, 0); + + /* disable channel interrupt */ + outl(0, emu->port + INTE); + snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); + snd_emu10k1_ptr_write(emu, CLIEH, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEL, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); + + if (emu->audigy){ + snd_emu10k1_ptr_write(emu, 0x5e, 0, 0xf00); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x5f, 0, 0x3); /* ?? */ + } + + /* init envelope engine */ + for (ch = 0; ch < NUM_G; ch++) { + emu->voices[ch].emu = emu; + emu->voices[ch].number = ch; + snd_emu10k1_voice_init(emu, ch); + } + + /* + * Init to 0x02109204 : + * Clock accuracy = 0 (1000ppm) + * Sample Rate = 2 (48kHz) + * Audio Channel = 1 (Left of 2) + * Source Number = 0 (Unspecified) + * Generation Status = 1 (Original for Cat Code 12) + * Cat Code = 12 (Digital Signal Mixer) + * Mode = 0 (Mode 0) + * Emphasis = 0 (None) + * CP = 1 (Copyright unasserted) + * AN = 0 (Audio data) + * P = 0 (Consumer) + */ + snd_emu10k1_ptr_write(emu, SPCS0, 0, + emu->spdif_bits[0] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + snd_emu10k1_ptr_write(emu, SPCS1, 0, + emu->spdif_bits[1] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + snd_emu10k1_ptr_write(emu, SPCS2, 0, + emu->spdif_bits[2] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + /* + * Clear page with silence & setup all pointers to this page + */ + memset(emu->silent_page, 0, PAGE_SIZE); + silent_page = emu->silent_page_dmaaddr << 1; + for (idx = 0; idx < MAXPAGES; idx++) + emu->ptb_pages[idx] = silent_page | idx; + snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages_dmaaddr); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */ + snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */ + + silent_page = (emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + for (ch = 0; ch < NUM_G; ch++) { + snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page); + snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page); + } + + /* + * Hokay, setup HCFG + * Mute Disable Audio = 0 + * Lock Tank Memory = 1 + * Lock Sound Memory = 0 + * Auto Mute = 1 + */ + if (emu->audigy) + outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + else if (emu->model == 0x20 || + emu->model == 0xc400 || + (emu->model == 0x21 && emu->revision < 6)) + outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG); + else + // With on-chip joystick + outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + + if (enable_ir) { /* enable IR for SB Live */ + unsigned int reg = inl(emu->port + HCFG); + outl(reg | HCFG_GPOUT2, emu->port + HCFG); + udelay(500); + outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG); + udelay(100); + outl(reg, emu->port + HCFG); + } + + if (!emu->APS) { /* enable analog output */ + unsigned int reg = inl(emu->port + HCFG); + outl(reg | HCFG_GPOUT0, emu->port + HCFG); + } + + /* + * Initialize the effect engine + */ + if ((err = snd_emu10k1_init_efx(emu)) < 0) + return err; + + /* + * Enable the audio bit + */ + outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG); + +#if 0 + { + unsigned int tmp; + /* FIXME: the following routine disables LiveDrive-II !! */ + // TOSLink detection + emu->tos_link = 0; + tmp = inl(emu->port + HCFG); + if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { + outl(tmp|0x800, emu->port + HCFG); + udelay(50); + if (tmp != (inl(emu->port + HCFG) & ~0x800)) { + emu->tos_link = 1; + outl(tmp, emu->port + HCFG); + } + } + } +#endif + + snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE); + + emu->reserved_page = (emu10k1_memblk_t *)snd_emu10k1_synth_alloc(emu, 4096); + if (emu->reserved_page) + emu->reserved_page->map_locked = 1; + + return 0; +} + +static int snd_emu10k1_done(emu10k1_t * emu) +{ + int ch; + + outl(0, emu->port + INTE); + + /* + * Shutdown the chip + */ + for (ch = 0; ch < NUM_G; ch++) + snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0); + for (ch = 0; ch < NUM_G; ch++) { + snd_emu10k1_ptr_write(emu, VTFT, ch, 0); + snd_emu10k1_ptr_write(emu, CVCF, ch, 0); + snd_emu10k1_ptr_write(emu, PTRX, ch, 0); + snd_emu10k1_ptr_write(emu, CPF, ch, 0); + } + + /* reset recording buffers */ + snd_emu10k1_ptr_write(emu, MICBS, 0, 0); + snd_emu10k1_ptr_write(emu, MICBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXBS, 0, 0); + snd_emu10k1_ptr_write(emu, FXBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, ADCBA, 0, 0); + snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP_ADDR); + else + snd_emu10k1_ptr_write(emu, DBG, 0, 0x8000); + + /* disable channel interrupt */ + snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); + snd_emu10k1_ptr_write(emu, CLIEH, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEL, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); + + /* remove reserved page */ + if (emu->reserved_page != NULL) { + snd_emu10k1_synth_free(emu, (snd_util_memblk_t *)emu->reserved_page); + emu->reserved_page = NULL; + } + + /* disable audio and lock cache */ + outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG); + snd_emu10k1_ptr_write(emu, PTB, 0, 0); + + snd_emu10k1_free_efx(emu); + + return 0; +} + +/************************************************************************* + * ECARD functional implementation + *************************************************************************/ + +/* In A1 Silicon, these bits are in the HC register */ +#define HOOKN_BIT (1L << 12) +#define HANDN_BIT (1L << 11) +#define PULSEN_BIT (1L << 10) + +#define EC_GDI1 (1 << 13) +#define EC_GDI0 (1 << 14) + +#define EC_NUM_CONTROL_BITS 20 + +#define EC_AC3_DATA_SELN 0x0001L +#define EC_EE_DATA_SEL 0x0002L +#define EC_EE_CNTRL_SELN 0x0004L +#define EC_EECLK 0x0008L +#define EC_EECS 0x0010L +#define EC_EESDO 0x0020L +#define EC_TRIM_CSN 0x0040L +#define EC_TRIM_SCLK 0x0080L +#define EC_TRIM_SDATA 0x0100L +#define EC_TRIM_MUTEN 0x0200L +#define EC_ADCCAL 0x0400L +#define EC_ADCRSTN 0x0800L +#define EC_DACCAL 0x1000L +#define EC_DACMUTEN 0x2000L +#define EC_LEDN 0x4000L + +#define EC_SPDIF0_SEL_SHIFT 15 +#define EC_SPDIF1_SEL_SHIFT 17 +#define EC_SPDIF0_SEL_MASK (0x3L << EC_SPDIF0_SEL_SHIFT) +#define EC_SPDIF1_SEL_MASK (0x7L << EC_SPDIF1_SEL_SHIFT) +#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK) +#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK) +#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory. This should + * be incremented any time the EEPROM's + * format is changed. */ + +#define EC_EEPROM_SIZE 0x40 /* ECARD EEPROM has 64 16-bit words */ + +/* Addresses for special values stored in to EEPROM */ +#define EC_PROM_VERSION_ADDR 0x20 /* Address of the current prom version */ +#define EC_BOARDREV0_ADDR 0x21 /* LSW of board rev */ +#define EC_BOARDREV1_ADDR 0x22 /* MSW of board rev */ + +#define EC_LAST_PROMFILE_ADDR 0x2f + +#define EC_SERIALNUM_ADDR 0x30 /* First word of serial number. The + * can be up to 30 characters in length + * and is stored as a NULL-terminated + * ASCII string. Any unused bytes must be + * filled with zeros */ +#define EC_CHECKSUM_ADDR 0x3f /* Location at which checksum is stored */ + + +/* Most of this stuff is pretty self-evident. According to the hardware + * dudes, we need to leave the ADCCAL bit low in order to avoid a DC + * offset problem. Weird. + */ +#define EC_RAW_RUN_MODE (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \ + EC_TRIM_CSN) + + +#define EC_DEFAULT_ADC_GAIN 0xC4C4 +#define EC_DEFAULT_SPDIF0_SEL 0x0 +#define EC_DEFAULT_SPDIF1_SEL 0x4 + +/************************************************************************** + * @func Clock bits into the Ecard's control latch. The Ecard uses a + * control latch will is loaded bit-serially by toggling the Modem control + * lines from function 2 on the E8010. This function hides these details + * and presents the illusion that we are actually writing to a distinct + * register. + */ + +static void snd_emu10k1_ecard_write(emu10k1_t * emu, unsigned int value) +{ + unsigned short count; + unsigned int data; + unsigned long hc_port; + unsigned int hc_value; + + hc_port = emu->port + HCFG; + hc_value = inl(hc_port) & ~(HOOKN_BIT | HANDN_BIT | PULSEN_BIT); + outl(hc_value, hc_port); + + for (count = 0; count < EC_NUM_CONTROL_BITS; count++) { + + /* Set up the value */ + data = ((value & 0x1) ? PULSEN_BIT : 0); + value >>= 1; + + outl(hc_value | data, hc_port); + + /* Clock the shift register */ + outl(hc_value | data | HANDN_BIT, hc_port); + outl(hc_value | data, hc_port); + } + + /* Latch the bits */ + outl(hc_value | HOOKN_BIT, hc_port); + outl(hc_value, hc_port); +} + +/************************************************************************** + * @func Set the gain of the ECARD's CS3310 Trim/gain controller. The + * trim value consists of a 16bit value which is composed of two + * 8 bit gain/trim values, one for the left channel and one for the + * right channel. The following table maps from the Gain/Attenuation + * value in decibels into the corresponding bit pattern for a single + * channel. + */ + +static void snd_emu10k1_ecard_setadcgain(emu10k1_t * emu, + unsigned short gain) +{ + unsigned int bit; + + /* Enable writing to the TRIM registers */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN); + + /* Do it again to insure that we meet hold time requirements */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN); + + for (bit = (1 << 15); bit; bit >>= 1) { + unsigned int value; + + value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA); + + if (gain & bit) + value |= EC_TRIM_SDATA; + + /* Clock the bit */ + snd_emu10k1_ecard_write(emu, value); + snd_emu10k1_ecard_write(emu, value | EC_TRIM_SCLK); + snd_emu10k1_ecard_write(emu, value); + } + + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl); +} + +static int __devinit snd_emu10k1_ecard_init(emu10k1_t * emu) +{ + unsigned int hc_value; + + /* Set up the initial settings */ + emu->ecard_ctrl = EC_RAW_RUN_MODE | + EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) | + EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL); + + /* Step 0: Set the codec type in the hardware control register + * and enable audio output */ + hc_value = inl(emu->port + HCFG); + outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG); + inl(emu->port + HCFG); + + /* Step 1: Turn off the led and deassert TRIM_CS */ + snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 2: Calibrate the ADC and DAC */ + snd_emu10k1_ecard_write(emu, EC_DACCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 3: Wait for awhile; XXX We can't get away with this + * under a real operating system; we'll need to block and wait that + * way. */ + snd_emu10k1_wait(emu, 48000); + + /* Step 4: Switch off the DAC and ADC calibration. Note + * That ADC_CAL is actually an inverted signal, so we assert + * it here to stop calibration. */ + snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 4: Switch into run mode */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl); + + /* Step 5: Set the analog input gain */ + snd_emu10k1_ecard_setadcgain(emu, EC_DEFAULT_ADC_GAIN); + + return 0; +} + +/* + * Create the EMU10K1 instance + */ + +static int snd_emu10k1_free(emu10k1_t *emu) +{ + snd_emu10k1_proc_done(emu); + if (emu->res_port != NULL) { /* avoid access to already used hardware */ + snd_emu10k1_fx8010_tram_setup(emu, 0); + snd_emu10k1_done(emu); + } + if (emu->memhdr) + snd_util_memhdr_free(emu->memhdr); + if (emu->silent_page) + snd_free_pci_pages(emu->pci, EMUPAGESIZE, emu->silent_page, emu->silent_page_dmaaddr); + if (emu->ptb_pages) + snd_free_pci_pages(emu->pci, 32 * 1024, (void *)emu->ptb_pages, emu->ptb_pages_dmaaddr); + if (emu->page_ptr_table) + vfree(emu->page_ptr_table); + if (emu->page_addr_table) + vfree(emu->page_addr_table); + if (emu->res_port) { + release_resource(emu->res_port); + kfree_nocheck(emu->res_port); + } + if (emu->irq >= 0) + free_irq(emu->irq, (void *)emu); + snd_magic_kfree(emu); + return 0; +} + +static int snd_emu10k1_dev_free(snd_device_t *device) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, device->device_data, return -ENXIO); + return snd_emu10k1_free(emu); +} + +int __devinit snd_emu10k1_create(snd_card_t * card, + struct pci_dev * pci, + unsigned short extin_mask, + unsigned short extout_mask, + long max_cache_bytes, + int enable_ir, + emu10k1_t ** remu) +{ + emu10k1_t *emu; + int err; + static snd_device_ops_t ops = { + dev_free: snd_emu10k1_dev_free, + }; + + *remu = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 31 bits */ + if (!pci_dma_supported(pci, 0x7fffffff)) { + snd_printk("architecture does not support 31bit PCI busmaster DMA\n"); + return -ENXIO; + } + if (pci->driver_data) + pci_set_dma_mask(pci, 0xffffffff); /* audigy */ + else + pci_set_dma_mask(pci, 0x7fffffff); + + emu = snd_magic_kcalloc(emu10k1_t, 0, GFP_KERNEL); + if (emu == NULL) + return -ENOMEM; + emu->card = card; + spin_lock_init(&emu->reg_lock); + spin_lock_init(&emu->emu_lock); + spin_lock_init(&emu->voice_lock); + spin_lock_init(&emu->synth_lock); + spin_lock_init(&emu->memblk_lock); + init_MUTEX(&emu->ptb_lock); + init_MUTEX(&emu->fx8010.lock); + INIT_LIST_HEAD(&emu->mapped_link_head); + INIT_LIST_HEAD(&emu->mapped_order_link_head); + emu->pci = pci; + emu->irq = -1; + emu->synth = NULL; + emu->get_synth_voice = NULL; + emu->port = pci_resource_start(pci, 0); + + // emu->audigy = (int)pci->driver_data; + if (pci->device == 0x0004) + emu->audigy = 1; + + if (emu->audigy) + emu->gpr_base = A_FXGPREGBASE; + else + emu->gpr_base = FXGPREGBASE; + + if ((emu->res_port = request_region(emu->port, 0x20, "EMU10K1")) == NULL) { + snd_emu10k1_free(emu); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_emu10k1_interrupt, SA_INTERRUPT|SA_SHIRQ, "EMU10K1", (void *)emu)) { + snd_emu10k1_free(emu); + return -EBUSY; + } + emu->irq = pci->irq; + + emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT; + emu->ptb_pages = snd_malloc_pci_pages(pci, 32 * 1024, &emu->ptb_pages_dmaaddr); + if (emu->ptb_pages == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + + emu->page_ptr_table = (void **)vmalloc(emu->max_cache_pages * sizeof(void*)); + emu->page_addr_table = (unsigned long*)vmalloc(emu->max_cache_pages * sizeof(unsigned long)); + if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + + emu->silent_page = snd_malloc_pci_pages(pci, EMUPAGESIZE, &emu->silent_page_dmaaddr); + if (emu->silent_page == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE); + if (emu->memhdr == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + emu->memhdr->block_extra_size = sizeof(emu10k1_memblk_t) - sizeof(snd_util_memblk_t); + + pci_set_master(pci); + /* read revision & serial */ + pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&emu->revision); + pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model); + emu->card_type = EMU10K1_CARD_CREATIVE; + if (emu->serial == 0x40011102) { + emu->card_type = EMU10K1_CARD_EMUAPS; + emu->APS = 1; + } + + emu->fx8010.fxbus_mask = 0x303f; + if (extin_mask == 0) { + if (emu->audigy) + extin_mask = 0x000f; + else + extin_mask = 0x1fcf; + } + if (extout_mask == 0) { + if (emu->audigy) + extout_mask = 0x3fff; + else + extout_mask = 0x3fff; + } + emu->fx8010.extin_mask = extin_mask; + emu->fx8010.extout_mask = extout_mask; + + if (emu->APS) { + if ((err = snd_emu10k1_ecard_init(emu)) < 0) { + snd_emu10k1_free(emu); + return err; + } + } else { + /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version + does not support this, it shouldn't do any harm */ + snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE); + } + + if ((err = snd_emu10k1_init(emu, enable_ir)) < 0) { + snd_emu10k1_free(emu); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops)) < 0) { + snd_emu10k1_free(emu); + return err; + } + + snd_emu10k1_proc_init(emu); + + *remu = emu; + return 0; +} + +/* memory.c */ +EXPORT_SYMBOL(snd_emu10k1_synth_alloc); +EXPORT_SYMBOL(snd_emu10k1_synth_free); +EXPORT_SYMBOL(snd_emu10k1_synth_bzero); +EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user); +EXPORT_SYMBOL(snd_emu10k1_memblk_map); +/* voice.c */ +EXPORT_SYMBOL(snd_emu10k1_voice_alloc); +EXPORT_SYMBOL(snd_emu10k1_voice_free); +/* io.c */ +EXPORT_SYMBOL(snd_emu10k1_ptr_read); +EXPORT_SYMBOL(snd_emu10k1_ptr_write); diff -Nru a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emu10k1_patch.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,224 @@ +/* + * Patch transfer callback for Emu10k1 + * + * Copyright (C) 2000 Takashi iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * All the code for loading in a patch. There is very little that is + * chip specific here. Just the actual writing to the board. + */ + +#define __NO_VERSION__ +#include "emu10k1_synth_local.h" + +/* + */ +#define BLANK_LOOP_START 4 +#define BLANK_LOOP_END 8 +#define BLANK_LOOP_SIZE 12 +#define BLANK_HEAD_SIZE 32 + +/* + * allocate a sample block and copy data from userspace + */ +int +snd_emu10k1_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr, const void *data, long count) +{ + int offset; + int truesize, size, loopsize, blocksize; + int loopend, sampleend; + unsigned int start_addr; + emu10k1_t *emu; + + emu = snd_magic_cast(emu10k1_t, rec->hw, return -ENXIO); + snd_assert(sp != NULL, return -EINVAL); + snd_assert(hdr != NULL, return -EINVAL); + + if (sp->v.size == 0) { + snd_printd("emu: rom font for sample %d\n", sp->v.sample); + return 0; + } + + /* recalculate address offset */ + sp->v.end -= sp->v.start; + sp->v.loopstart -= sp->v.start; + sp->v.loopend -= sp->v.start; + sp->v.start = 0; + + /* some samples have invalid data. the addresses are corrected in voice info */ + sampleend = sp->v.end; + if (sampleend > sp->v.size) + sampleend = sp->v.size; + loopend = sp->v.loopend; + if (loopend > sampleend) + loopend = sampleend; + + /* be sure loop points start < end */ + if (sp->v.loopstart >= sp->v.loopend) { + int tmp = sp->v.loopstart; + sp->v.loopstart = sp->v.loopend; + sp->v.loopend = tmp; + } + + /* compute true data size to be loaded */ + truesize = sp->v.size + BLANK_HEAD_SIZE; + loopsize = 0; +#if 0 /* not supported */ + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) + loopsize = sp->v.loopend - sp->v.loopstart; + truesize += loopsize; +#endif + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) + truesize += BLANK_LOOP_SIZE; + + /* try to allocate a memory block */ + blocksize = truesize; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + blocksize *= 2; + sp->block = snd_emu10k1_synth_alloc(emu, blocksize); + if (sp->block == NULL) { + snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize); + /* not ENOMEM (for compatibility with OSS) */ + return -ENOSPC; + } + /* set the total size */ + sp->v.truesize = blocksize; + + /* write blank samples at head */ + offset = 0; + size = BLANK_HEAD_SIZE; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + size *= 2; + snd_assert(offset + size <= blocksize, return -EINVAL); + snd_emu10k1_synth_bzero(emu, sp->block, offset, size); + offset += size; + + /* copy start->loopend */ + size = loopend; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + size *= 2; + snd_assert(offset + size <= blocksize, return -EINVAL); + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + return -EFAULT; + } + offset += size; + data += size; + +#if 0 /* not suppported yet */ + /* handle reverse (or bidirectional) loop */ + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { + /* copy loop in reverse */ + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { + int woffset; + unsigned short *wblock = (unsigned short*)block; + woffset = offset / 2; + snd_assert(offset + loopsize*2 <= blocksize, return -EINVAL); + for (i = 0; i < loopsize; i++) + wblock[woffset + i] = wblock[woffset - i -1]; + offset += loopsize * 2; + } else { + snd_assert(offset + loopsize <= blocksize, return -EINVAL); + for (i = 0; i < loopsize; i++) + block[offset + i] = block[offset - i -1]; + offset += loopsize; + } + + /* modify loop pointers */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { + sp->v.loopend += loopsize; + } else { + sp->v.loopstart += loopsize; + sp->v.loopend += loopsize; + } + /* add sample pointer */ + sp->v.end += loopsize; + } +#endif + + /* loopend -> sample end */ + size = sp->v.size - loopend; + snd_assert(size >= 0, return -EINVAL); + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + size *= 2; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + return -EFAULT; + } + offset += size; + + /* clear rest of samples (if any) */ + if (offset < blocksize) + snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset); + + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { + /* if no blank loop is attached in the sample, add it */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { + sp->v.loopstart = sp->v.end + BLANK_LOOP_START; + sp->v.loopend = sp->v.end + BLANK_LOOP_END; + } + } + +#if 0 /* not supported yet */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) { + /* unsigned -> signed */ + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { + unsigned short *wblock = (unsigned short*)block; + for (i = 0; i < truesize; i++) + wblock[i] ^= 0x8000; + } else { + for (i = 0; i < truesize; i++) + block[i] ^= 0x80; + } + } +#endif + + /* recalculate offset */ + start_addr = BLANK_HEAD_SIZE * 2; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + start_addr >>= 1; + sp->v.start += start_addr; + sp->v.end += start_addr; + sp->v.loopstart += start_addr; + sp->v.loopend += start_addr; + + return 0; +} + +/* + * free a sample block + */ +int +snd_emu10k1_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr) +{ + emu10k1_t *emu; + + emu = snd_magic_cast(emu10k1_t, rec->hw, return -ENXIO); + snd_assert(sp != NULL, return -EINVAL); + snd_assert(hdr != NULL, return -EINVAL); + + if (sp->block) { + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + } + return 0; +} + diff -Nru a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emu10k1_synth.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Routines for control of EMU10K1 WaveTable synth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu10k1_synth_local.h" +#include + +MODULE_AUTHOR("Takashi Iwai"); +MODULE_DESCRIPTION("Routines for control of EMU10K1 WaveTable synth"); +MODULE_LICENSE("GPL"); + +/* + * create a new hardware dependent device for Emu10k1 + */ +int snd_emu10k1_synth_new_device(snd_seq_device_t *dev) +{ + snd_emux_t *emu; + emu10k1_t *hw; + snd_emu10k1_synth_arg_t *arg; + unsigned long flags; + + arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (arg == NULL) + return -EINVAL; + + if (arg->seq_ports <= 0) + return 0; /* nothing */ + if (arg->max_voices < 1) + arg->max_voices = 1; + else if (arg->max_voices > 64) + arg->max_voices = 64; + + if (snd_emux_new(&emu) < 0) + return -ENOMEM; + + snd_emu10k1_ops_setup(emu); + emu->hw = hw = arg->hwptr; + emu->max_voices = arg->max_voices; + emu->num_ports = arg->seq_ports; + emu->pitch_shift = -501; + emu->memhdr = hw->memhdr; + emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */ + emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */ + + if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) { + snd_emux_free(emu); + emu->hw = NULL; + return -ENOMEM; + } + + spin_lock_irqsave(&hw->voice_lock, flags); + hw->synth = emu; + hw->get_synth_voice = snd_emu10k1_synth_get_voice; + spin_unlock_irqrestore(&hw->voice_lock, flags); + + dev->driver_data = emu; + + return 0; +} + +int snd_emu10k1_synth_delete_device(snd_seq_device_t *dev) +{ + snd_emux_t *emu; + emu10k1_t *hw; + unsigned long flags; + + if (dev->driver_data == NULL) + return 0; /* not registered actually */ + + emu = snd_magic_cast(snd_emux_t, dev->driver_data, return -EINVAL); + + hw = snd_magic_cast(emu10k1_t, emu->hw, return -EINVAL); + spin_lock_irqsave(&hw->voice_lock, flags); + hw->synth = NULL; + hw->get_synth_voice = NULL; + spin_unlock_irqrestore(&hw->voice_lock, flags); + + snd_emux_free(emu); + return 0; +} + + +EXPORT_NO_SYMBOLS; + + +/* + * INIT part + */ + +static int __init alsa_emu10k1_synth_init(void) +{ + + static snd_seq_dev_ops_t ops = { + snd_emu10k1_synth_new_device, + snd_emu10k1_synth_delete_device, + }; + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops, sizeof(snd_emu10k1_synth_arg_t)); +} + +static void __exit alsa_emu10k1_synth_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH); +} + +module_init(alsa_emu10k1_synth_init) +module_exit(alsa_emu10k1_synth_exit) diff -Nru a/sound/pci/emu10k1/emu10k1_synth_local.h b/sound/pci/emu10k1/emu10k1_synth_local.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emu10k1_synth_local.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,38 @@ +#ifndef __EMU10K1_SYNTH_LOCAL_H +#define __EMU10K1_SYNTH_LOCAL_H +/* + * Local defininitons for Emu10k1 wavetable + * + * Copyright (C) 2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +/* emu10k1_patch.c */ +int snd_emu10k1_sample_new(snd_emux_t *private_data, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *_data, long count); +int snd_emu10k1_sample_free(snd_emux_t *private_data, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); +int snd_emu10k1_memhdr_init(snd_emux_t *emu); + +/* emu10k1_callback.c */ +void snd_emu10k1_ops_setup(snd_emux_t *emu); +int snd_emu10k1_synth_get_voice(emu10k1_t *hw); + + +#endif /* __EMU10K1_SYNTH_LOCAL_H */ diff -Nru a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emufx.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2207 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for effect processor FX8010 + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#define chip_t emu10k1_t + +#if 0 /* for testing purposes - digital out -> capture */ +#define EMU10K1_CAPTURE_DIGITAL_OUT +#endif +#if 0 /* for testing purposes - set S/PDIF to AC3 output */ +#define EMU10K1_SET_AC3_IEC958 +#endif +#if 0 /* for testing purposes - feed the front signal to Center/LFE outputs */ +#define EMU10K1_CENTER_LFE_FROM_FRONT +#endif + +/* + * Tables + */ + +static char *fxbuses[16] = { + /* 0x00 */ "PCM Left", + /* 0x01 */ "PCM Right", + /* 0x02 */ "PCM Surround Left", + /* 0x03 */ "PCM Surround Right", + /* 0x04 */ "MIDI Left", + /* 0x05 */ "MIDI Right", + /* 0x06 */ "Center", + /* 0x07 */ "LFE", + /* 0x08 */ NULL, + /* 0x09 */ NULL, + /* 0x0a */ NULL, + /* 0x0b */ NULL, + /* 0x0c */ "MIDI Reverb", + /* 0x0d */ "MIDI Chorus", + /* 0x0e */ NULL, + /* 0x0f */ NULL +}; + +static char *creative_ins[16] = { + /* 0x00 */ "AC97 Left", + /* 0x01 */ "AC97 Right", + /* 0x02 */ "TTL IEC958 Left", + /* 0x03 */ "TTL IEC958 Right", + /* 0x04 */ "Zoom Video Left", + /* 0x05 */ "Zoom Video Right", + /* 0x06 */ "Optical IEC958 Left", + /* 0x07 */ "Optical IEC958 Right", + /* 0x08 */ "Line/Mic 1 Left", + /* 0x09 */ "Line/Mic 1 Right", + /* 0x0a */ "Coaxial IEC958 Left", + /* 0x0b */ "Coaxial IEC958 Right", + /* 0x0c */ "Line/Mic 2 Left", + /* 0x0d */ "Line/Mic 2 Right", + /* 0x0e */ NULL, + /* 0x0f */ NULL +}; + +static char *audigy_ins[16] = { + /* 0x00 */ "AC97 Left", + /* 0x01 */ "AC97 Right", + /* 0x02 */ "Audigy CD Left", + /* 0x03 */ "Audigy CD Right", + /* 0x04 */ NULL, + /* 0x05 */ NULL, + /* 0x06 */ NULL, + /* 0x07 */ NULL, + /* 0x08 */ NULL, + /* 0x09 */ NULL, + /* 0x0a */ NULL, + /* 0x0b */ NULL, + /* 0x0c */ NULL, + /* 0x0d */ NULL, + /* 0x0e */ NULL, + /* 0x0f */ NULL +}; + +static char *creative_outs[32] = { + /* 0x00 */ "AC97 Left", + /* 0x01 */ "AC97 Right", + /* 0x02 */ "Optical IEC958 Left", + /* 0x03 */ "Optical IEC958 Right", + /* 0x04 */ "Center", + /* 0x05 */ "LFE", + /* 0x06 */ "Headphone Left", + /* 0x07 */ "Headphone Right", + /* 0x08 */ "Surround Left", + /* 0x09 */ "Surround Right", + /* 0x0a */ "PCM Capture Left", + /* 0x0b */ "PCM Capture Right", + /* 0x0c */ "MIC Capture", + /* 0x0d */ NULL, + /* 0x0e */ NULL, + /* 0x0f */ NULL, + /* 0x10 */ NULL, + /* 0x11 */ "Analog Center", + /* 0x12 */ "Analog LFE", + /* 0x13 */ NULL, + /* 0x14 */ NULL, + /* 0x15 */ NULL, + /* 0x16 */ NULL, + /* 0x17 */ NULL, + /* 0x18 */ NULL, + /* 0x19 */ NULL, + /* 0x1a */ NULL, + /* 0x1b */ NULL, + /* 0x1c */ NULL, + /* 0x1d */ NULL, + /* 0x1e */ NULL, + /* 0x1f */ NULL, +}; + +static char *audigy_outs[32] = { + /* 0x00 */ NULL, + /* 0x01 */ NULL, + /* 0x02 */ NULL, + /* 0x03 */ NULL, + /* 0x04 */ "Headphone Left", + /* 0x05 */ "Headphone Right", + /* 0x06 */ NULL, + /* 0x07 */ NULL, + /* 0x08 */ "Front Left", + /* 0x09 */ "Front Right", + /* 0x0a */ "Center", + /* 0x0b */ "LFE", + /* 0x0c */ NULL, + /* 0x0d */ NULL, + /* 0x0e */ "Rear Left", + /* 0x0f */ "Rear Right", + /* 0x10 */ NULL, + /* 0x11 */ NULL, + /* 0x12 */ NULL, + /* 0x13 */ NULL, + /* 0x14 */ NULL, + /* 0x15 */ NULL, + /* 0x16 */ "ADC Capture Left", + /* 0x17 */ "ADC Capture Right", + /* 0x18 */ NULL, + /* 0x19 */ NULL, + /* 0x1a */ NULL, + /* 0x1b */ NULL, + /* 0x1c */ NULL, + /* 0x1d */ NULL, + /* 0x1e */ NULL, + /* 0x1f */ NULL, +}; + +static const u32 bass_table[41][5] = { + { 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 }, + { 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d }, + { 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee }, + { 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c }, + { 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b }, + { 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 }, + { 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f }, + { 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 }, + { 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 }, + { 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 }, + { 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 }, + { 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be }, + { 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b }, + { 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c }, + { 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b }, + { 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 }, + { 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a }, + { 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 }, + { 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 }, + { 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e }, + { 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 }, + { 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 }, + { 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 }, + { 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 }, + { 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e }, + { 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 }, + { 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 }, + { 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 }, + { 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 }, + { 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 }, + { 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca }, + { 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 }, + { 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 }, + { 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 }, + { 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 }, + { 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a }, + { 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f }, + { 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 }, + { 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 }, + { 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 } +}; + +static const u32 treble_table[41][5] = { + { 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 }, + { 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 }, + { 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 }, + { 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca }, + { 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 }, + { 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 }, + { 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 }, + { 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 }, + { 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 }, + { 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df }, + { 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff }, + { 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 }, + { 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c }, + { 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 }, + { 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 }, + { 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 }, + { 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 }, + { 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d }, + { 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 }, + { 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 }, + { 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f }, + { 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb }, + { 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 }, + { 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 }, + { 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd }, + { 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 }, + { 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad }, + { 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 }, + { 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 }, + { 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c }, + { 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 }, + { 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 }, + { 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 }, + { 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 }, + { 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 }, + { 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 }, + { 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d }, + { 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b }, + { 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 }, + { 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd }, + { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 } +}; + +static const u32 db_table[101] = { + 0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540, + 0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8, + 0x0207567a, 0x021fd03d, 0x0239714c, 0x02544792, 0x027061a1, + 0x028dcebb, 0x02ac9edc, 0x02cce2bf, 0x02eeabe8, 0x03120cb0, + 0x0337184e, 0x035de2df, 0x03868173, 0x03b10a18, 0x03dd93e9, + 0x040c3713, 0x043d0cea, 0x04702ff3, 0x04a5bbf2, 0x04ddcdfb, + 0x0518847f, 0x0555ff62, 0x05966005, 0x05d9c95d, 0x06206005, + 0x066a4a52, 0x06b7b067, 0x0708bc4c, 0x075d9a01, 0x07b6779d, + 0x08138561, 0x0874f5d5, 0x08dafde1, 0x0945d4ed, 0x09b5b4fd, + 0x0a2adad1, 0x0aa58605, 0x0b25f936, 0x0bac7a24, 0x0c3951d8, + 0x0ccccccc, 0x0d673b17, 0x0e08f093, 0x0eb24510, 0x0f639481, + 0x101d3f2d, 0x10dfa9e6, 0x11ab3e3f, 0x12806ac3, 0x135fa333, + 0x144960c5, 0x153e2266, 0x163e6cfe, 0x174acbb7, 0x1863d04d, + 0x198a1357, 0x1abe349f, 0x1c00db77, 0x1d52b712, 0x1eb47ee6, + 0x2026f30f, 0x21aadcb6, 0x23410e7e, 0x24ea64f9, 0x26a7c71d, + 0x287a26c4, 0x2a62812c, 0x2c61df84, 0x2e795779, 0x30aa0bcf, + 0x32f52cfe, 0x355bf9d8, 0x37dfc033, 0x3a81dda4, 0x3d43c038, + 0x4026e73c, 0x432ce40f, 0x46575af8, 0x49a8040f, 0x4d20ac2a, + 0x50c335d3, 0x54919a57, 0x588dead1, 0x5cba514a, 0x611911ea, + 0x65ac8c2f, 0x6a773c39, 0x6f7bbc23, 0x74bcc56c, 0x7a3d3272, + 0x7fffffff, +}; + +static const u32 onoff_table[2] = { + 0x00000000, 0x00000001 +}; + +/* + */ + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +/* + * controls + */ + +static int snd_emu10k1_gpr_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value; + + if (ctl->min == 0 && ctl->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = ctl->vcount; + uinfo->value.integer.min = ctl->min; + uinfo->value.integer.max = ctl->max; + return 0; +} + +static int snd_emu10k1_gpr_ctl_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value; + unsigned long flags; + int i; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (i = 0; i < ctl->vcount; i++) + ucontrol->value.integer.value[i] = ctl->value[i]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_gpr_ctl_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value; + unsigned long flags; + unsigned int nval, val; + int i, j, change = 0; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (i = 0; i < ctl->vcount; i++) { + nval = ucontrol->value.integer.value[i]; + if (nval < ctl->min) + nval = ctl->min; + if (nval > ctl->max) + nval = ctl->max; + if (nval != ctl->value[i]) + change = 1; + val = ctl->value[i] = nval; + switch (ctl->translation) { + case EMU10K1_GPR_TRANSLATION_NONE: + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val); + break; + case EMU10K1_GPR_TRANSLATION_TABLE100: + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]); + break; + case EMU10K1_GRP_TRANSLATION_BASS: + snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error); + for (j = 0; j < 5; j++) + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, bass_table[val][j]); + break; + case EMU10K1_GRP_TRANSLATION_TREBLE: + snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error); + for (j = 0; j < 5; j++) + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, treble_table[val][j]); + break; + case EMU10K1_GPR_TRANSLATION_ONOFF: + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, onoff_table[val]); + break; + } + } + __error: + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +/* + * Interrupt handler + */ + +static void snd_emu10k1_fx8010_interrupt(emu10k1_t *emu) +{ + snd_emu10k1_fx8010_irq_t *irq, *nirq; + + irq = emu->fx8010.irq_handlers; + while (irq) { + nirq = irq->next; /* irq ptr can be removed from list */ + if (snd_emu10k1_ptr_read(emu, emu->gpr_base + irq->gpr_running, 0) & 0xffff0000) { + if (irq->handler) + irq->handler(emu, irq->private_data); + snd_emu10k1_ptr_write(emu, emu->gpr_base + irq->gpr_running, 0, 1); + } + irq = nirq; + } +} + +static int snd_emu10k1_fx8010_register_irq_handler(emu10k1_t *emu, + snd_fx8010_irq_handler_t *handler, + unsigned char gpr_running, + void *private_data, + snd_emu10k1_fx8010_irq_t **r_irq) +{ + snd_emu10k1_fx8010_irq_t *irq; + unsigned long flags; + + snd_runtime_check(emu, return -EINVAL); + snd_runtime_check(handler, return -EINVAL); + irq = kmalloc(sizeof(*irq), GFP_KERNEL); + if (irq == NULL) + return -ENOMEM; + irq->handler = handler; + irq->gpr_running = gpr_running; + irq->private_data = private_data; + irq->next = NULL; + spin_lock_irqsave(&emu->fx8010.irq_lock, flags); + if (emu->fx8010.irq_handlers == NULL) { + emu->fx8010.irq_handlers = irq; + emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt; + snd_emu10k1_intr_enable(emu, INTE_FXDSPENABLE); + } else { + irq->next = emu->fx8010.irq_handlers; + emu->fx8010.irq_handlers = irq; + } + spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags); + if (r_irq) + *r_irq = irq; + return 0; +} + +static int snd_emu10k1_fx8010_unregister_irq_handler(emu10k1_t *emu, + snd_emu10k1_fx8010_irq_t *irq) +{ + snd_emu10k1_fx8010_irq_t *tmp; + unsigned long flags; + + snd_runtime_check(irq, return -EINVAL); + spin_lock_irqsave(&emu->fx8010.irq_lock, flags); + if ((tmp = emu->fx8010.irq_handlers) == irq) { + emu->fx8010.irq_handlers = tmp->next; + if (emu->fx8010.irq_handlers == NULL) { + snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); + emu->dsp_interrupt = NULL; + } + } else { + while (tmp && tmp->next != irq) + tmp = tmp->next; + if (tmp) + tmp->next = tmp->next->next; + } + spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags); + kfree(irq); + return 0; +} + +/* + * PCM streams + */ + +#define INITIAL_TRAM_SHIFT 14 +#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1) + +static void snd_emu10k1_fx8010_playback_irq(emu10k1_t *emu, void *private_data) +{ + snd_pcm_substream_t *substream = snd_magic_cast(snd_pcm_substream_t, private_data, return); + snd_pcm_period_elapsed(substream); +} + +static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left, + unsigned short *dst_right, + unsigned short *src, + unsigned int count, + unsigned int tram_shift) +{ + // printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); + if ((tram_shift & 1) == 0) { + while (count--) { + *dst_left-- = *src++; + *dst_right-- = *src++; + } + } else { + while (count--) { + *dst_right-- = *src++; + *dst_left-- = *src++; + } + } +} + +static void snd_emu10k1_fx8010_playback_tram_poke(emu10k1_t *emu, + unsigned int *tram_pos, + unsigned int *tram_shift, + unsigned int tram_size, + unsigned short *src, + unsigned int frames) +{ + unsigned int count; + + while (frames > *tram_pos) { + count = *tram_pos + 1; + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, + (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, + src, count, *tram_shift); + src += count * 2; + frames -= count; + *tram_pos = (tram_size / 2) - 1; + (*tram_shift)++; + } + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, + (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, + src, frames, *tram_shift++); + *tram_pos -= frames; +} + +static int snd_emu10k1_fx8010_playback_transfer(snd_pcm_substream_t *substream, + snd_pcm_uframes_t frames) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff = appl_ptr - pcm->appl_ptr; + snd_pcm_uframes_t buffer_size = pcm->buffer_size / 2; + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + pcm->sw_ready += diff; + } + pcm->sw_ready += frames; + pcm->appl_ptr = appl_ptr + frames; + while (pcm->hw_ready < buffer_size && + pcm->sw_ready > 0) { + size_t hw_to_end = buffer_size - pcm->hw_data; + size_t sw_to_end = (runtime->buffer_size << 2) - pcm->sw_data; + size_t tframes = buffer_size - pcm->hw_ready; + if (pcm->sw_ready < tframes) + tframes = pcm->sw_ready; + if (hw_to_end < tframes) + tframes = hw_to_end; + if (sw_to_end < tframes) + tframes = sw_to_end; + snd_emu10k1_fx8010_playback_tram_poke(emu, &pcm->tram_pos, &pcm->tram_shift, + pcm->buffer_size, + (unsigned short *)(runtime->dma_area + (pcm->sw_data << 2)), + tframes); + pcm->hw_data += tframes; + if (pcm->hw_data == buffer_size) + pcm->hw_data = 0; + pcm->sw_data += tframes; + if (pcm->sw_data == runtime->buffer_size) + pcm->sw_data = 0; + pcm->hw_ready += tframes; + pcm->sw_ready -= tframes; + } + return 0; +} + +static int snd_emu10k1_fx8010_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_emu10k1_fx8010_playback_hw_free(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + int i; + + for (i = 0; i < pcm->channels; i++) + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0); + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_emu10k1_fx8010_playback_prepare(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + int i; + + // printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); + pcm->sw_data = pcm->sw_io = pcm->sw_ready = 0; + pcm->hw_data = pcm->hw_io = pcm->hw_ready = 0; + pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); + pcm->tram_shift = 0; + pcm->appl_ptr = 0; + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size); + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size); + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size); + for (i = 0; i < pcm->channels; i++) + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels)); + return 0; +} + +static int snd_emu10k1_fx8010_playback_trigger(snd_pcm_substream_t * substream, int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + unsigned long flags; + int result = 0; + + spin_lock_irqsave(&emu->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* follow thru */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +#ifdef EMU10K1_SET_AC3_IEC958 + { + int i; + for (i = 0; i < 3; i++) { + unsigned int bits; + bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | + 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA; + snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits); + } + } +#endif + result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq); + if (result < 0) + goto __err; + snd_emu10k1_fx8010_playback_transfer(substream, 0); /* roll the ball */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL; + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); + pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); + pcm->tram_shift = 0; + break; + default: + result = -EINVAL; + break; + } + __err: + spin_unlock_irqrestore(&emu->reg_lock, flags); + return result; +} + +static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + size_t ptr; + snd_pcm_sframes_t frames; + + if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0)) + return 0; + ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0); + frames = ptr - pcm->hw_io; + if (frames < 0) + frames += runtime->buffer_size; + pcm->hw_io = ptr; + pcm->hw_ready -= frames; + pcm->sw_io += frames; + if (pcm->sw_io > runtime->buffer_size) + pcm->sw_io -= runtime->buffer_size; + snd_emu10k1_fx8010_playback_transfer(substream, 0); + return pcm->sw_io; +} + +static int snd_emu10k1_fx8010_playback_copy(snd_pcm_substream_t *substream, + int channel, + snd_pcm_uframes_t hwoff, + void *src, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + size_t hwoffb = hwoff << 2; + size_t bytes = frames << 2; + char *hwbuf = runtime->dma_area + hwoffb; + if (copy_from_user(hwbuf, src, bytes)) + return -EFAULT; + spin_lock_irq(&runtime->lock); + snd_emu10k1_fx8010_playback_transfer(substream, frames); + spin_unlock_irq(&runtime->lock); + return 0; +} + +static snd_pcm_hardware_t snd_emu10k1_fx8010_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_48000, + rate_min: 48000, + rate_max: 48000, + channels_min: 1, + channels_max: 1, + buffer_bytes_max: (128*1024), + period_bytes_min: 1024, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + + runtime->hw = snd_emu10k1_fx8010_playback; + runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels; + runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2; + spin_lock(&emu->reg_lock); + if (pcm->valid == 0) { + spin_unlock(&emu->reg_lock); + return -ENODEV; + } + pcm->opened = 1; + spin_unlock(&emu->reg_lock); + return 0; +} + +static int snd_emu10k1_fx8010_playback_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + + spin_lock(&emu->reg_lock); + pcm->opened = 0; + spin_unlock(&emu->reg_lock); + return 0; +} + +static snd_pcm_ops_t snd_emu10k1_fx8010_playback_ops = { + open: snd_emu10k1_fx8010_playback_open, + close: snd_emu10k1_fx8010_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_emu10k1_fx8010_playback_hw_params, + hw_free: snd_emu10k1_fx8010_playback_hw_free, + prepare: snd_emu10k1_fx8010_playback_prepare, + trigger: snd_emu10k1_fx8010_playback_trigger, + copy: snd_emu10k1_fx8010_playback_copy, + pointer: snd_emu10k1_fx8010_playback_pointer, +}; + +static void snd_emu10k1_fx8010_pcm_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm_fx8010 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1", device, 8, 0, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_fx8010_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, "EMU10K1 FX8010"); + emu->pcm_fx8010 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 0); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +/************************************************************************* + * EMU10K1 effect manager + *************************************************************************/ + +static void snd_emu10k1_write_op(emu10k1_fx8010_code_t *icode, unsigned int *ptr, + u32 op, u32 r, u32 a, u32 x, u32 y) +{ + snd_assert(*ptr < 512, return); + set_bit(*ptr, &icode->code_valid); + icode->code[*ptr ][0] = ((x & 0x3ff) << 10) | (y & 0x3ff); + icode->code[(*ptr)++][1] = ((op & 0x0f) << 20) | ((r & 0x3ff) << 10) | (a & 0x3ff); +} + +#define OP(icode, ptr, op, r, a, x, y) \ + snd_emu10k1_write_op(icode, ptr, op, r, a, x, y) + +static void snd_emu10k1_audigy_write_op(emu10k1_fx8010_code_t *icode, unsigned int *ptr, + u32 op, u32 r, u32 a, u32 x, u32 y) +{ + snd_assert(*ptr < 512, return); + set_bit(*ptr, &icode->code_valid); + icode->code[*ptr ][0] = ((x & 0x7ff) << 12) | (y & 0x7ff); + icode->code[(*ptr)++][1] = ((op & 0x0f) << 24) | ((r & 0x7ff) << 12) | (a & 0x7ff); +} + +#define A_OP(icode, ptr, op, r, a, x, y) \ + snd_emu10k1_audigy_write_op(icode, ptr, op, r, a, x, y) + +void snd_emu10k1_efx_write(emu10k1_t *emu, unsigned int pc, unsigned int data) +{ + pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE; + snd_emu10k1_ptr_write(emu, pc, 0, data); +} + +unsigned int snd_emu10k1_efx_read(emu10k1_t *emu, unsigned int pc) +{ + pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE; + return snd_emu10k1_ptr_read(emu, pc, 0); +} + +static void snd_emu10k1_gpr_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int gpr; + + for (gpr = 0; gpr < 0x100; gpr++) { + if (!test_bit(gpr, &icode->gpr_valid)) + continue; + snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, icode->gpr_map[gpr]); + } +} + +static void snd_emu10k1_gpr_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int gpr; + + for (gpr = 0; gpr < 0x100; gpr++) { + set_bit(gpr, &icode->gpr_valid); + icode->gpr_map[gpr] = snd_emu10k1_ptr_read(emu, emu->gpr_base + gpr, 0); + } +} + +static void snd_emu10k1_tram_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int tram; + + for (tram = 0; tram < 0xa0; tram++) { + if (!test_bit(tram, &icode->tram_valid)) + continue; + snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, icode->tram_data_map[tram]); + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, icode->tram_addr_map[tram]); + } +} + +static void snd_emu10k1_tram_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int tram; + + for (tram = 0; tram < 0xa0; tram++) { + set_bit(tram, &icode->tram_valid); + icode->tram_data_map[tram] = snd_emu10k1_ptr_read(emu, TANKMEMDATAREGBASE + tram, 0); + icode->tram_addr_map[tram] = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0); + } +} + +static void snd_emu10k1_code_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + u32 pc; + + for (pc = 0; pc < 512; pc++) { + if (!test_bit(pc, &icode->code_valid)) + continue; + snd_emu10k1_efx_write(emu, pc * 2, icode->code[pc][0]); + snd_emu10k1_efx_write(emu, pc * 2 + 1, icode->code[pc][1]); + } +} + +static void snd_emu10k1_code_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + u32 pc; + + for (pc = 0; pc < 512; pc++) { + set_bit(pc, &icode->code_valid); + icode->code[pc][0] = snd_emu10k1_efx_read(emu, pc * 2); + icode->code[pc][1] = snd_emu10k1_efx_read(emu, pc * 2 + 1); + } +} + +static snd_emu10k1_fx8010_ctl_t *snd_emu10k1_look_for_ctl(emu10k1_t *emu, snd_ctl_elem_id_t *id) +{ + snd_emu10k1_fx8010_ctl_t *ctl; + snd_kcontrol_t *kcontrol; + struct list_head *list; + + list_for_each(list, &emu->fx8010.gpr_ctl) { + ctl = emu10k1_gpr_ctl(list); + kcontrol = ctl->kcontrol; + if (kcontrol->id.iface == id->iface && + !strcmp(kcontrol->id.name, id->name) && + kcontrol->id.index == id->index) + return ctl; + } + return NULL; +} + +static int snd_emu10k1_verify_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int i; + snd_ctl_elem_id_t *_id, id; + emu10k1_fx8010_control_gpr_t *_gctl, gctl; + + for (i = 0, _id = icode->gpr_del_controls; + i < icode->gpr_del_control_count; i++, _id++) { + if (copy_from_user(&id, _id, sizeof(id))) + return -EFAULT; + if (snd_emu10k1_look_for_ctl(emu, &id) == NULL) + return -ENOENT; + } + for (i = 0, _gctl = icode->gpr_add_controls; + i < icode->gpr_add_control_count; i++) { + if (copy_from_user(&gctl, _gctl, sizeof(gctl))) + return -EFAULT; + if (snd_emu10k1_look_for_ctl(emu, &gctl.id)) + continue; + if (snd_ctl_find_id(emu->card, &gctl.id) != NULL) + return -EEXIST; + if (gctl.id.iface != SNDRV_CTL_ELEM_IFACE_MIXER && + gctl.id.iface != SNDRV_CTL_ELEM_IFACE_PCM) + return -EINVAL; + } + return 0; +} + +static void snd_emu10k1_ctl_private_free(snd_kcontrol_t *kctl) +{ + snd_emu10k1_fx8010_ctl_t *ctl; + + ctl = (snd_emu10k1_fx8010_ctl_t *)kctl->private_value; + kctl->private_value = 0; + list_del(&ctl->list); + kfree(ctl); +} + +static void snd_emu10k1_add_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int i, j; + emu10k1_fx8010_control_gpr_t *_gctl, gctl; + snd_emu10k1_fx8010_ctl_t *ctl, nctl; + snd_kcontrol_new_t knew; + snd_kcontrol_t *kctl; + snd_ctl_elem_value_t val; + + for (i = 0, _gctl = icode->gpr_add_controls; + i < icode->gpr_add_control_count; i++, _gctl++) { + if (copy_from_user(&gctl, _gctl, sizeof(gctl))) + return; + snd_runtime_check(gctl.id.iface == SNDRV_CTL_ELEM_IFACE_MIXER || + gctl.id.iface == SNDRV_CTL_ELEM_IFACE_PCM, continue); + snd_runtime_check(gctl.id.name[0] != '\0', continue); + ctl = snd_emu10k1_look_for_ctl(emu, &gctl.id); + memset(&knew, 0, sizeof(knew)); + knew.iface = gctl.id.iface; + knew.name = gctl.id.name; + knew.index = gctl.id.index; + knew.info = snd_emu10k1_gpr_ctl_info; + knew.get = snd_emu10k1_gpr_ctl_get; + knew.put = snd_emu10k1_gpr_ctl_put; + memset(&nctl, 0, sizeof(nctl)); + nctl.vcount = gctl.vcount; + nctl.count = gctl.count; + for (j = 0; j < 32; j++) { + nctl.gpr[j] = gctl.gpr[j]; + nctl.value[j] = ~gctl.value[j]; + val.value.integer.value[j] = gctl.value[j]; + } + nctl.min = gctl.min; + nctl.max = gctl.max; + nctl.translation = gctl.translation; + if (ctl == NULL) { + ctl = (snd_emu10k1_fx8010_ctl_t *)kmalloc(sizeof(*ctl), GFP_KERNEL); + if (ctl == NULL) + continue; + knew.private_value = (unsigned long)ctl; + memcpy(ctl, &nctl, sizeof(nctl)); + if (snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu)) < 0) { + kfree(ctl); + continue; + } + kctl->private_free = snd_emu10k1_ctl_private_free; + ctl->kcontrol = kctl; + list_add_tail(&ctl->list, &emu->fx8010.gpr_ctl); + } else { + /* overwrite */ + nctl.list = ctl->list; + nctl.kcontrol = ctl->kcontrol; + memcpy(ctl, &nctl, sizeof(nctl)); + snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &ctl->kcontrol->id); + } + snd_emu10k1_gpr_ctl_put(ctl->kcontrol, &val); + } +} + +static void snd_emu10k1_del_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int i; + snd_ctl_elem_id_t *_id, id; + snd_emu10k1_fx8010_ctl_t *ctl; + + for (i = 0, _id = icode->gpr_del_controls; + i < icode->gpr_del_control_count; i++, _id++) { + snd_runtime_check(copy_from_user(&id, _id, sizeof(id)) == 0, continue); + ctl = snd_emu10k1_look_for_ctl(emu, &id); + snd_runtime_check(ctl == NULL, continue); + snd_ctl_remove(emu->card, ctl->kcontrol); + } +} + +static int snd_emu10k1_icode_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int err = 0; + + down(&emu->fx8010.lock); + if ((err = snd_emu10k1_verify_controls(emu, icode)) < 0) + goto __error; + strncpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name)-1); + emu->fx8010.name[sizeof(emu->fx8010.name)-1] = '\0'; + /* stop FX processor - this may be dangerous, but it's better to miss + some samples than generate wrong ones - [jk] */ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP_ADDR); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP); + /* ok, do the main job */ + snd_emu10k1_del_controls(emu, icode); + snd_emu10k1_gpr_poke(emu, icode); + snd_emu10k1_tram_poke(emu, icode); + snd_emu10k1_code_poke(emu, icode); + snd_emu10k1_add_controls(emu, icode); + /* start FX processor when the DSP code is updated */ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg); + __error: + up(&emu->fx8010.lock); + return err; +} + +static int snd_emu10k1_icode_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + down(&emu->fx8010.lock); + strncpy(icode->name, emu->fx8010.name, sizeof(icode->name)-1); + emu->fx8010.name[sizeof(emu->fx8010.name)-1] = '\0'; + /* ok, do the main job */ + snd_emu10k1_gpr_peek(emu, icode); + snd_emu10k1_tram_peek(emu, icode); + snd_emu10k1_code_peek(emu, icode); + up(&emu->fx8010.lock); + return 0; +} + +static int snd_emu10k1_ipcm_poke(emu10k1_t *emu, emu10k1_fx8010_pcm_t *ipcm) +{ + int err = 0, i; + snd_emu10k1_fx8010_pcm_t *pcm; + + if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT) + return -EINVAL; + if (ipcm->channels > 32) + return -EINVAL; + pcm = &emu->fx8010.pcm[ipcm->substream]; + down(&emu->fx8010.lock); + spin_lock_irq(&emu->reg_lock); + if (pcm->opened) { + err = -EBUSY; + goto __error; + } + if (ipcm->channels == 0) { /* remove */ + pcm->valid = 0; + } else { + /* FIXME: we need to add universal code to the PCM transfer routine */ + if (ipcm->channels != 2) { + err = -EINVAL; + goto __error; + } + pcm->valid = 1; + pcm->opened = 0; + pcm->channels = ipcm->channels; + pcm->tram_start = ipcm->tram_start; + pcm->buffer_size = ipcm->buffer_size; + pcm->gpr_size = ipcm->gpr_size; + pcm->gpr_count = ipcm->gpr_count; + pcm->gpr_tmpcount = ipcm->gpr_tmpcount; + pcm->gpr_ptr = ipcm->gpr_ptr; + pcm->gpr_trigger = ipcm->gpr_trigger; + pcm->gpr_running = ipcm->gpr_running; + for (i = 0; i < pcm->channels; i++) + pcm->etram[i] = ipcm->etram[i]; + } + __error: + spin_unlock_irq(&emu->reg_lock); + up(&emu->fx8010.lock); + return err; +} + +static int snd_emu10k1_ipcm_peek(emu10k1_t *emu, emu10k1_fx8010_pcm_t *ipcm) +{ + int err = 0, i; + snd_emu10k1_fx8010_pcm_t *pcm; + + if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT) + return -EINVAL; + pcm = &emu->fx8010.pcm[ipcm->substream]; + down(&emu->fx8010.lock); + spin_lock_irq(&emu->reg_lock); + ipcm->channels = pcm->channels; + ipcm->tram_start = pcm->tram_start; + ipcm->buffer_size = pcm->buffer_size; + ipcm->gpr_size = pcm->gpr_size; + ipcm->gpr_ptr = pcm->gpr_ptr; + ipcm->gpr_count = pcm->gpr_count; + ipcm->gpr_tmpcount = pcm->gpr_tmpcount; + ipcm->gpr_trigger = pcm->gpr_trigger; + ipcm->gpr_running = pcm->gpr_running; + for (i = 0; i < pcm->channels; i++) + ipcm->etram[i] = pcm->etram[i]; + ipcm->res1 = ipcm->res2 = 0; + ipcm->pad = 0; + spin_unlock_irq(&emu->reg_lock); + up(&emu->fx8010.lock); + return err; +} + +#define SND_EMU10K1_GPR_CONTROLS 41 +#define SND_EMU10K1_INPUTS 10 +#define SND_EMU10K1_PLAYBACK_CHANNELS 6 +#define SND_EMU10K1_CAPTURE_CHANNELS 4 + +static void __devinit snd_emu10k1_init_mono_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 1; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->min = 0; + ctl->max = 100; + ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; +} + +static void __devinit snd_emu10k1_init_stereo_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 2; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; + ctl->min = 0; + ctl->max = 100; + ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; +} + +static void __devinit snd_emu10k1_init_mono_onoff_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 1; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->min = 0; + ctl->max = 1; + ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF; +} + +static void __devinit snd_emu10k1_init_stereo_onoff_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 2; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; + ctl->min = 0; + ctl->max = 1; + ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF; +} + + +/* + * initial DSP configuration for Audigy + */ + +static int __devinit _snd_emu10k1_audigy_init_efx(emu10k1_t *emu) +{ + int err, i, gpr, tmp, playback, capture, nctl; + u32 ptr; + emu10k1_fx8010_code_t *icode; + emu10k1_fx8010_control_gpr_t *controls; + mm_segment_t seg; + + spin_lock_init(&emu->fx8010.irq_lock); + INIT_LIST_HEAD(&emu->fx8010.gpr_ctl); + + if ((icode = snd_kcalloc(sizeof(emu10k1_fx8010_code_t), GFP_KERNEL)) == NULL) + return -ENOMEM; + if ((controls = snd_kcalloc(sizeof(emu10k1_fx8010_control_gpr_t) * SND_EMU10K1_GPR_CONTROLS, GFP_KERNEL)) == NULL) { + kfree(icode); + return -ENOMEM; + } + + /* clear free GPRs */ + for (i = 0; i < 256; i++) + set_bit(i, &icode->gpr_valid); + + strcpy(icode->name, "Audigy DSP code for ALSA"); + ptr = 0; + nctl = 0; + playback = 10; + capture = playback + 6; /* 5+1 channels */ + gpr = capture + 2; /* so far only a stereo capture */ + tmp = 0x80; + + /* stop FX processor */ + snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP_ADDR); + +#define A_C_ZERO 0x0c0 + + /* Wave Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_ZERO, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_ZERO, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100); + gpr += 2; + + /* Wave Surround Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_ZERO, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_ZERO, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Surround Playback Volume", gpr, 0); + gpr += 2; + + /* Wave Center Playback */ + /* Center = sub = Left/2 + Right/2 */ + A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_FXBUS(FXBUS_PCM_LEFT), 0xcd, A_FXBUS(FXBUS_PCM_RIGHT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_ZERO, A_GPR(gpr), A_GPR(tmp)); + snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Center Playback Volume", gpr, 0); + gpr++; + + /* Wave LFE Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_ZERO, A_GPR(gpr), A_GPR(tmp)); + snd_emu10k1_init_mono_control(&controls[nctl++], "Wave LFE Playback Volume", gpr, 0); + gpr++; + + /* Music Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+0), A_GPR(playback+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Music Playback Volume", gpr, 100); + gpr += 2; + + /* Wave Capture */ + A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_ZERO, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_ZERO, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Capture Volume", gpr, 0); + gpr += 2; + + /* Music Capture */ + A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Music Capture Volume", gpr, 0); + gpr += 2; + + /* Center Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER)); + snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 0); + gpr++; + + /* LFE Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE)); + snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0); + gpr++; + + /* + * inputs + */ +#define A_ADD_VOLUME_IN(var,vol,input) \ +A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) + if (emu->fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extout_mask & (1<<(out))) A_PUT_OUTPUT(out,src) +#define A_PUT_STEREO_OUTPUT(out1,out2,src) \ + if (emu->fx8010.extout_mask & ((1<<(out1))|(1<<(out2)))) {\ + A_PUT_OUTPUT(out1,src);\ + A_PUT_OUTPUT(out2,src+1);} + + /* digital outputs */ + A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback); + A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2); + A_PUT_MONO_OUTPUT(A_EXTOUT_CENTER, playback+4); + A_PUT_MONO_OUTPUT(A_EXTOUT_LFE, playback+5); + + /* analog speakers */ + A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback); + A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2); + A_PUT_MONO_OUTPUT(A_EXTOUT_ACENTER, playback+4); + A_PUT_MONO_OUTPUT(A_EXTOUT_ALFE, playback+5); + + /* headphone */ + A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback); + + /* ADC buffer */ + A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_L, capture); + A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1); + + /* + * ok, set up done.. + */ + + if (gpr > tmp) { + snd_BUG(); + err = -EIO; + goto __err; + } + /* clear remaining instruction memory */ + while (ptr < 0x200) + A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0); + + seg = snd_enter_user(); + icode->gpr_add_control_count = nctl; + icode->gpr_add_controls = controls; + err = snd_emu10k1_icode_poke(emu, icode); + snd_leave_user(seg); + + __err: + kfree(controls); + kfree(icode); + return err; +} + + +/* + * initial DSP configuration for Emu10k1 + */ + +/* when volume = max, then copy only to avoid volume modification */ +/* with iMAC0 (negative values) */ +static void __devinit _volume(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol) +{ + OP(icode, ptr, iMAC0, dst, C_00000000, src, vol); + OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff); + OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000001); + OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000); +} +static void __devinit _volume_add(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol) +{ + OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff); + OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002); + OP(icode, ptr, iMACINT0, dst, dst, src, C_00000001); + OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001); + OP(icode, ptr, iMAC0, dst, dst, src, vol); +} +static void __devinit _volume_out(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol) +{ + OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff); + OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002); + OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000); + OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001); + OP(icode, ptr, iMAC0, dst, C_00000000, src, vol); +} + +#define VOLUME(icode, ptr, dst, src, vol) \ + _volume(icode, ptr, GPR(dst), GPR(src), GPR(vol)) +#define VOLUME_IN(icode, ptr, dst, src, vol) \ + _volume(icode, ptr, GPR(dst), EXTIN(src), GPR(vol)) +#define VOLUME_ADD(icode, ptr, dst, src, vol) \ + _volume_add(icode, ptr, GPR(dst), GPR(src), GPR(vol)) +#define VOLUME_ADDIN(icode, ptr, dst, src, vol) \ + _volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol)) +#define VOLUME_OUT(icode, ptr, dst, src, vol) \ + _volume_out(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol)) +#define _SWITCH(icode, ptr, dst, src, sw) \ + OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw); +#define SWITCH(icode, ptr, dst, src, sw) \ + _SWITCH(icode, ptr, GPR(dst), GPR(src), GPR(sw)) +#define SWITCH_IN(icode, ptr, dst, src, sw) \ + _SWITCH(icode, ptr, GPR(dst), EXTIN(src), GPR(sw)) +#define _SWITCH_NEG(icode, ptr, dst, src) \ + OP((icode), ptr, iANDXOR, dst, src, C_00000001, C_00000001); +#define SWITCH_NEG(icode, ptr, dst, src) \ + _SWITCH_NEG(icode, ptr, GPR(dst), GPR(src)) + + +static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) +{ + int err, i, z, gpr, tmp, playback, capture; + u32 ptr; + emu10k1_fx8010_code_t *icode; + emu10k1_fx8010_pcm_t *ipcm; + emu10k1_fx8010_control_gpr_t *controls, *ctl; + mm_segment_t seg; + + spin_lock_init(&emu->fx8010.irq_lock); + INIT_LIST_HEAD(&emu->fx8010.gpr_ctl); + + if ((icode = snd_kcalloc(sizeof(emu10k1_fx8010_code_t), GFP_KERNEL)) == NULL) + return -ENOMEM; + if ((controls = snd_kcalloc(sizeof(emu10k1_fx8010_control_gpr_t) * SND_EMU10K1_GPR_CONTROLS, GFP_KERNEL)) == NULL) { + kfree(icode); + return -ENOMEM; + } + if ((ipcm = snd_kcalloc(sizeof(emu10k1_fx8010_pcm_t), GFP_KERNEL)) == NULL) { + kfree(controls); + kfree(icode); + return -ENOMEM; + } + + /* clear free GPRs */ + for (i = 0; i < 256; i++) + set_bit(i, &icode->gpr_valid); + + /* clear TRAM data & address lines */ + for (i = 0; i < 160; i++) + set_bit(i, &icode->tram_valid); + + strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela"); + ptr = 0; i = 0; + /* we have 10 inputs */ + playback = SND_EMU10K1_INPUTS; + /* we have 6 playback channels and tone control doubles */ + capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); + gpr = capture + SND_EMU10K1_CAPTURE_CHANNELS; + tmp = 0x88; /* we need 4 temporary GPR */ + /* from 0x8c to 0xff is the area for tone control */ + + /* stop FX processor */ + snd_emu10k1_ptr_write(emu, DBG, 0, (emu->fx8010.dbg = 0) | EMU10K1_DBG_SINGLE_STEP); + + /* + * Process FX Buses + */ + OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000); /* S/PDIF left */ + OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000); /* S/PDIF right */ + + /* Raw S/PDIF PCM */ + ipcm->substream = 0; + ipcm->channels = 2; + ipcm->tram_start = 0; + ipcm->buffer_size = (64 * 1024) / 2; + ipcm->gpr_size = gpr++; + ipcm->gpr_ptr = gpr++; + ipcm->gpr_count = gpr++; + ipcm->gpr_tmpcount = gpr++; + ipcm->gpr_trigger = gpr++; + ipcm->gpr_running = gpr++; + ipcm->etram[0] = 0; + ipcm->etram[1] = 1; + + icode->gpr_map[gpr + 0] = 0xfffff000; + icode->gpr_map[gpr + 1] = 0xffff0000; + icode->gpr_map[gpr + 2] = 0x70000000; + icode->gpr_map[gpr + 3] = 0x00000007; + icode->gpr_map[gpr + 4] = 0x001f << 11; + icode->gpr_map[gpr + 5] = 0x001c << 11; + icode->gpr_map[gpr + 6] = (0x22 - 0x01) - 1; /* skip at 01 to 22 */ + icode->gpr_map[gpr + 7] = (0x22 - 0x06) - 1; /* skip at 06 to 22 */ + icode->gpr_map[gpr + 8] = 0x2000000 + (2<<11); + icode->gpr_map[gpr + 9] = 0x4000000 + (2<<11); + icode->gpr_map[gpr + 10] = 1<<11; + icode->gpr_map[gpr + 11] = (0x24 - 0x0a) - 1; /* skip at 0a to 24 */ + icode->gpr_map[gpr + 12] = 0; + + /* if the trigger flag is not set, skip */ + /* 00: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_trigger), C_00000000, C_00000000); + /* 01: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_ZERO, GPR(gpr + 6)); + /* if the running flag is set, we're running */ + /* 02: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_running), C_00000000, C_00000000); + /* 03: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000004); + /* wait until ((GPR_DBAC>>11) & 0x1f) == 0x1c) */ + /* 04: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), GPR_DBAC, GPR(gpr + 4), C_00000000); + /* 05: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(gpr + 5)); + /* 06: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 7)); + /* 07: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000010, C_00000001, C_00000000); + + /* 08: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000000, C_00000001); + /* 09: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), GPR(gpr + 12), C_ffffffff, C_00000000); + /* 0a: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 11)); + /* 0b: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000001, C_00000000, C_00000000); + + /* 0c: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[0]), GPR(gpr + 0), C_00000000); + /* 0d: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000); + /* 0e: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2)); + /* 0f: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001); + /* 10: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(8), GPR(gpr + 1), GPR(gpr + 2)); + + /* 11: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[1]), GPR(gpr + 0), C_00000000); + /* 12: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000); + /* 13: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2)); + /* 14: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001); + /* 15: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(9), GPR(gpr + 1), GPR(gpr + 2)); + + /* 16: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(ipcm->gpr_ptr), C_00000001, C_00000000); + /* 17: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(ipcm->gpr_size)); + /* 18: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_MINUS, C_00000001); + /* 19: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), C_00000000, C_00000000, C_00000000); + /* 1a: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_ptr), GPR(tmp + 0), C_00000000, C_00000000); + + /* 1b: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_tmpcount), C_ffffffff, C_00000000); + /* 1c: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002); + /* 1d: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_count), C_00000000, C_00000000); + /* 1e: */ OP(icode, &ptr, iACC3, GPR_IRQ, C_80000000, C_00000000, C_00000000); + /* 1f: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000001, C_00010000); + + /* 20: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00010000, C_00000001); + /* 21: */ OP(icode, &ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000002); + + /* 22: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[0]), GPR(gpr + 8), GPR_DBAC, C_ffffffff); + /* 23: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[1]), GPR(gpr + 9), GPR_DBAC, C_ffffffff); + + /* 24: */ + gpr += 13; + + /* Wave Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME(icode, &ptr, playback + z, z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Wave Playback Volume", gpr, 100); + gpr += 2; + + /* Wave Surround Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME(icode, &ptr, playback + 2 + z, z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Wave Surround Playback Volume", gpr, 0); + gpr += 2; + + /* Wave Center/LFE Playback Volume */ + OP(icode, &ptr, iACC3, GPR(tmp + 0), FXBUS(FXBUS_PCM_LEFT), FXBUS(FXBUS_PCM_RIGHT), C_00000000); + OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000002); + VOLUME(icode, &ptr, playback + 4, tmp + 0, gpr); + snd_emu10k1_init_mono_control(controls + i++, "Wave Center Playback Volume", gpr++, 0); + VOLUME(icode, &ptr, playback + 5, tmp + 0, gpr); + snd_emu10k1_init_mono_control(controls + i++, "Wave LFE Playback Volume", gpr++, 0); + + /* Wave Capture Volume + Switch */ + for (z = 0; z < 2; z++) { + SWITCH(icode, &ptr, tmp + 0, z, gpr + 2 + z); + VOLUME(icode, &ptr, capture + z, tmp + 0, gpr + z); + } + snd_emu10k1_init_stereo_control(controls + i++, "Wave Capture Volume", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Wave Capture Switch", gpr + 2, 0); + gpr += 4; + + /* Music Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME_ADD(icode, &ptr, playback + z, 2 + z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Music Playback Volume", gpr, 100); + gpr += 2; + + /* Music Capture Volume + Switch */ + for (z = 0; z < 2; z++) { + SWITCH(icode, &ptr, tmp + 0, 2 + z, gpr + 2 + z); + VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z); + } + snd_emu10k1_init_stereo_control(controls + i++, "Music Capture Volume", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Music Capture Switch", gpr + 2, 0); + gpr += 4; + + /* Surround Digital Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME_ADD(icode, &ptr, playback + 2 + z, 4 + z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Playback Volume", gpr, 100); + gpr += 2; + + /* Surround Digital Capture Volume + Switch */ + for (z = 0; z < 2; z++) { + SWITCH(icode, &ptr, tmp + 0, 4 + z, gpr + 2 + z); + VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z); + } + snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Capture Volume", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Surround Digital Capture Switch", gpr + 2, 0); + gpr += 4; + + /* Center Playback Volume */ + VOLUME_ADD(icode, &ptr, playback + 4, 6, gpr); + snd_emu10k1_init_mono_control(controls + i++, "Center Playback Volume", gpr++, 100); + + /* LFE Playback Volume + Switch */ + VOLUME_ADD(icode, &ptr, playback + 5, 7, gpr); + snd_emu10k1_init_mono_control(controls + i++, "LFE Playback Volume", gpr++, 100); + + /* + * Process inputs + */ + + if (emu->fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, "Tone Control - Bass"); + ctl->vcount = 2; + ctl->count = 10; + ctl->min = 0; + ctl->max = 40; + ctl->value[0] = ctl->value[1] = 20; + ctl->translation = EMU10K1_GRP_TRANSLATION_BASS; + ctl = &controls[i + 1]; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, "Tone Control - Treble"); + ctl->vcount = 2; + ctl->count = 10; + ctl->min = 0; + ctl->max = 40; + ctl->value[0] = ctl->value[1] = 20; + ctl->translation = EMU10K1_GRP_TRANSLATION_TREBLE; + +#define BASS_GPR 0x8c +#define TREBLE_GPR 0x96 + + for (z = 0; z < 5; z++) { + int j; + for (j = 0; j < 2; j++) { + controls[i + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j; + controls[i + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j; + } + } + for (z = 0; z < 3; z++) { /* front/rear/center-lfe */ + int j, k, l, d; + for (j = 0; j < 2; j++) { /* left/right */ + k = 0xa0 + (z * 8) + (j * 4); + l = 0xd0 + (z * 8) + (j * 4); + d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j; + + OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(d), GPR(BASS_GPR + 0 + j)); + OP(icode, &ptr, iMACMV, GPR(k+1), GPR(k), GPR(k+1), GPR(BASS_GPR + 4 + j)); + OP(icode, &ptr, iMACMV, GPR(k), GPR(d), GPR(k), GPR(BASS_GPR + 2 + j)); + OP(icode, &ptr, iMACMV, GPR(k+3), GPR(k+2), GPR(k+3), GPR(BASS_GPR + 8 + j)); + OP(icode, &ptr, iMAC0, GPR(k+2), GPR_ACCU, GPR(k+2), GPR(BASS_GPR + 6 + j)); + OP(icode, &ptr, iACC3, GPR(k+2), GPR(k+2), GPR(k+2), C_00000000); + + OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(k+2), GPR(TREBLE_GPR + 0 + j)); + OP(icode, &ptr, iMACMV, GPR(l+1), GPR(l), GPR(l+1), GPR(TREBLE_GPR + 4 + j)); + OP(icode, &ptr, iMACMV, GPR(l), GPR(k+2), GPR(l), GPR(TREBLE_GPR + 2 + j)); + OP(icode, &ptr, iMACMV, GPR(l+3), GPR(l+2), GPR(l+3), GPR(TREBLE_GPR + 8 + j)); + OP(icode, &ptr, iMAC0, GPR(l+2), GPR_ACCU, GPR(l+2), GPR(TREBLE_GPR + 6 + j)); + OP(icode, &ptr, iMACINT0, GPR(l+2), C_00000000, GPR(l+2), C_00000010); + + OP(icode, &ptr, iACC3, GPR(d), GPR(l+2), C_00000000, C_00000000); + + if (z == 2) /* center */ + break; + } + } + i += 2; + +#undef BASS_GPR +#undef TREBLE_GPR + + for (z = 0; z < 6; z++) { + SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0); + SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0); + SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1); + OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000); + } + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0); + gpr += 2; + + /* + * Process outputs + */ + if (emu->fx8010.extout_mask & ((1<fx8010.extout_mask & ((1<fx8010.extout_mask & ((1<fx8010.extout_mask & ((1<fx8010.extout_mask & (1<fx8010.extout_mask & (1<fx8010.extout_mask & (1< tmp) { + snd_BUG(); + err = -EIO; + goto __err; + } + if (i > SND_EMU10K1_GPR_CONTROLS) { + snd_BUG(); + err = -EIO; + goto __err; + } + + /* clear remaining instruction memory */ + while (ptr < 0x200) + OP(icode, &ptr, iACC3, C_00000000, C_00000000, C_00000000, C_00000000); + + if ((err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size)) < 0) + goto __err; + seg = snd_enter_user(); + icode->gpr_add_control_count = i; + icode->gpr_add_controls = controls; + err = snd_emu10k1_icode_poke(emu, icode); + snd_leave_user(seg); + if (err >= 0) + err = snd_emu10k1_ipcm_poke(emu, ipcm); + __err: + kfree(ipcm); + kfree(controls); + kfree(icode); + return err; +} + +int __devinit snd_emu10k1_init_efx(emu10k1_t *emu) +{ + if (emu->audigy) + return _snd_emu10k1_audigy_init_efx(emu); + else + return _snd_emu10k1_init_efx(emu); +} + +void snd_emu10k1_free_efx(emu10k1_t *emu) +{ + /* stop processor */ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = A_DBG_SINGLE_STEP_ADDR); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = EMU10K1_DBG_SINGLE_STEP); +} + +#if 0 // FIXME: who use them? +int snd_emu10k1_fx8010_tone_control_activate(emu10k1_t *emu, int output) +{ + snd_runtime_check(output >= 0 && output < 6, return -EINVAL); + snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 1); + return 0; +} + +int snd_emu10k1_fx8010_tone_control_deactivate(emu10k1_t *emu, int output) +{ + snd_runtime_check(output >= 0 && output < 6, return -EINVAL); + snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 0); + return 0; +} +#endif + +int snd_emu10k1_fx8010_tram_setup(emu10k1_t *emu, u32 size) +{ + u8 size_reg = 0; + + /* size is in samples */ + if (size != 0) { + size = (size - 1) >> 13; + + while (size) { + size >>= 1; + size_reg++; + } + size = 0x2000 << size_reg; + } + if (emu->fx8010.etram_size == size) + return 0; + spin_lock_irq(&emu->emu_lock); + outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG); + spin_unlock_irq(&emu->emu_lock); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); + snd_emu10k1_ptr_write(emu, TCBS, 0, 0); + if (emu->fx8010.etram_pages != NULL) { + snd_free_pci_pages(emu->pci, emu->fx8010.etram_size * 2, emu->fx8010.etram_pages, emu->fx8010.etram_pages_dmaaddr); + emu->fx8010.etram_pages = NULL; + emu->fx8010.etram_size = 0; + } + + if (size > 0) { + emu->fx8010.etram_pages = snd_malloc_pci_pages(emu->pci, size * 2, &emu->fx8010.etram_pages_dmaaddr); + if (emu->fx8010.etram_pages == NULL) + return -ENOMEM; + memset(emu->fx8010.etram_pages, 0, size * 2); + snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages_dmaaddr); + snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg); + spin_lock_irq(&emu->emu_lock); + outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG); + spin_unlock_irq(&emu->emu_lock); + } + + emu->fx8010.etram_size = size; + return 0; +} + +static int snd_emu10k1_fx8010_open(snd_hwdep_t * hw, struct file *file) +{ + return 0; +} + +static void copy_string(char *dst, char *src, char *null, int idx) +{ + if (src == NULL) + sprintf(dst, "%s %02X", null, idx); + else + strcpy(dst, src); +} + +static int snd_emu10k1_fx8010_info(emu10k1_t *emu, emu10k1_fx8010_info_t *info) +{ + char **fxbus, **extin, **extout; + unsigned short fxbus_mask, extin_mask, extout_mask; + int res; + + memset(info, 0, sizeof(info)); + info->card = emu->card_type; + info->internal_tram_size = emu->fx8010.itram_size; + info->external_tram_size = emu->fx8010.etram_size; + fxbus = fxbuses; + extin = emu->audigy ? audigy_ins : creative_ins; + extout = emu->audigy ? audigy_outs : creative_outs; + fxbus_mask = emu->fx8010.fxbus_mask; + extin_mask = emu->fx8010.extin_mask; + extout_mask = emu->fx8010.extout_mask; + for (res = 0; res < 16; res++, fxbus++, extin++, extout++) { + copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res); + copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res); + copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + } + for (res = 16; res < 32; res++, extout++) + copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + info->gpr_controls = emu->fx8010.gpr_count; + return 0; +} + +static int snd_emu10k1_fx8010_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, hw->private_data, return -ENXIO); + emu10k1_fx8010_info_t info; + emu10k1_fx8010_code_t *icode; + emu10k1_fx8010_pcm_t *ipcm; + unsigned int addr; + int res; + + switch (cmd) { + case SNDRV_EMU10K1_IOCTL_INFO: + if ((res = snd_emu10k1_fx8010_info(emu, &info)) < 0) + return res; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + case SNDRV_EMU10K1_IOCTL_CODE_POKE: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + icode = (emu10k1_fx8010_code_t *)kmalloc(sizeof(*icode), GFP_KERNEL); + if (icode == NULL) + return -ENOMEM; + if (copy_from_user(icode, (void *)arg, sizeof(*icode))) { + kfree(icode); + return -EFAULT; + } + res = snd_emu10k1_icode_poke(emu, icode); + kfree(icode); + return res; + case SNDRV_EMU10K1_IOCTL_CODE_PEEK: + icode = (emu10k1_fx8010_code_t *)snd_kcalloc(sizeof(*icode), GFP_KERNEL); + if (icode == NULL) + return -ENOMEM; + res = snd_emu10k1_icode_peek(emu, icode); + if (res == 0 && copy_to_user((void *)arg, icode, sizeof(*icode))) { + kfree(icode); + return -EFAULT; + } + kfree(icode); + return res; + case SNDRV_EMU10K1_IOCTL_PCM_POKE: + if (emu->audigy) + return -EINVAL; + ipcm = (emu10k1_fx8010_pcm_t *)snd_kcalloc(sizeof(*ipcm), GFP_KERNEL); + if (ipcm == NULL) + return -ENOMEM; + res = snd_emu10k1_ipcm_poke(emu, ipcm); + kfree(ipcm); + return res; + case SNDRV_EMU10K1_IOCTL_PCM_PEEK: + if (emu->audigy) + return -EINVAL; + ipcm = (emu10k1_fx8010_pcm_t *)snd_kcalloc(sizeof(*ipcm), GFP_KERNEL); + if (ipcm == NULL) + return -ENOMEM; + res = snd_emu10k1_ipcm_peek(emu, ipcm); + if (res == 0 && copy_to_user((void *)arg, ipcm, sizeof(*ipcm))) { + kfree(ipcm); + return -EFAULT; + } + kfree(ipcm); + return res; + case SNDRV_EMU10K1_IOCTL_TRAM_SETUP: + if (emu->audigy) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (get_user(addr, (unsigned int *)arg)) + return -EFAULT; + down(&emu->fx8010.lock); + res = snd_emu10k1_fx8010_tram_setup(emu, addr); + up(&emu->fx8010.lock); + return res; + case SNDRV_EMU10K1_IOCTL_STOP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP_ADDR); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP); + return 0; + case SNDRV_EMU10K1_IOCTL_CONTINUE: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = 0); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = 0); + return 0; + case SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_ZC); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_ZC); + udelay(10); + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg); + return 0; + case SNDRV_EMU10K1_IOCTL_SINGLE_STEP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (get_user(addr, (unsigned int *)arg)) + return -EFAULT; + if (addr > 0x1ff) + return -EINVAL; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP_ADDR | addr); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr); + udelay(10); + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP_ADDR | A_DBG_STEP_ADDR | addr); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr); + return 0; + case SNDRV_EMU10K1_IOCTL_DBG_READ: + if (emu->audigy) + addr = snd_emu10k1_ptr_read(emu, A_DBG, 0); + else + addr = snd_emu10k1_ptr_read(emu, DBG, 0); + if (put_user(addr, (unsigned int *)arg)) + return -EFAULT; + return 0; + } + return -ENOTTY; +} + +static int snd_emu10k1_fx8010_release(snd_hwdep_t * hw, struct file *file) +{ + return 0; +} + +int __devinit snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t ** rhwdep) +{ + snd_hwdep_t *hw; + int err; + + if (rhwdep) + *rhwdep = NULL; + if ((err = snd_hwdep_new(emu->card, "FX8010", device, &hw)) < 0) + return err; + strcpy(hw->name, "EMU10K1 (FX8010)"); + hw->iface = SNDRV_HWDEP_IFACE_EMU10K1; + hw->ops.open = snd_emu10k1_fx8010_open; + hw->ops.ioctl = snd_emu10k1_fx8010_ioctl; + hw->ops.release = snd_emu10k1_fx8010_release; + hw->private_data = emu; + if (rhwdep) + *rhwdep = hw; + return 0; +} diff -Nru a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emumixer.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,466 @@ +/* + * Copyright (c) by Jaroslav Kysela , + * Takashi Iwai + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips / mixer routines + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +#define chip_t emu10k1_t + +static int snd_emu10k1_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_emu10k1_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + unsigned long flags; + + spin_lock_irqsave(&emu->reg_lock, flags); + ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_spdif_get_mask(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value, change; + unsigned int val; + unsigned long flags; + + val = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + spin_lock_irqsave(&emu->reg_lock, flags); + change = val != emu->spdif_bits[idx]; + if (change) { + snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val); + emu->spdif_bits[idx] = val; + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_spdif_mask_control = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + info: snd_emu10k1_spdif_info, + get: snd_emu10k1_spdif_get_mask +}; + +static snd_kcontrol_new_t snd_emu10k1_spdif_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_emu10k1_spdif_info, + get: snd_emu10k1_spdif_get, + put: snd_emu10k1_spdif_put +}; + +/* FIXME: audigy has more routing/effects */ +static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 3*4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f; + return 0; +} + +static int snd_emu10k1_send_routing_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int voice, idx; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (voice = 0; voice < 3; voice++) + for (idx = 0; idx < 4; idx++) + ucontrol->value.integer.value[(voice * 4) + idx] = emu->audigy ? + ((mix->send_routing[voice] >> (idx * 8)) & 0x3f) : + ((mix->send_routing[voice] >> (idx * 4)) & 0x0f); + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_send_routing_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int change = 0, voice, idx, val; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (voice = 0; voice < 3; voice++) + for (idx = 0; idx < 4; idx++) { + val = ucontrol->value.integer.value[(voice * 4) + idx]; + if (emu->audigy) { + int shift = idx * 8; + val &= 0x3f; + if (((mix->send_routing[voice] >> shift) & 0x3f) != val) { + mix->send_routing[voice] &= ~(0x3f << shift); + mix->send_routing[voice] |= val << shift; + change = 1; + } + } else { + int shift = idx * 4; + val = ucontrol->value.integer.value[(voice * 4) + idx] & 15; + if (((mix->send_routing[voice] >> shift) & 15) != val) { + mix->send_routing[voice] &= ~(15 << shift); + mix->send_routing[voice] |= val << shift; + change = 1; + } + } + } + if (change && mix->epcm) { + if (mix->epcm->voices[0] && mix->epcm->voices[1]) { + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXRT1, mix->epcm->voices[0]->number, mix->send_routing[1]); + snd_emu10k1_ptr_write(emu, A_FXRT1, mix->epcm->voices[1]->number, mix->send_routing[2]); + } else { + snd_emu10k1_ptr_write(emu, FXRT, mix->epcm->voices[0]->number, mix->send_routing[1] << 16); + snd_emu10k1_ptr_write(emu, FXRT, mix->epcm->voices[1]->number, mix->send_routing[2] << 16); + } + } else if (mix->epcm->voices[0]) { + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXRT1, mix->epcm->voices[0]->number, mix->send_routing[0]); + } else { + snd_emu10k1_ptr_write(emu, FXRT, mix->epcm->voices[0]->number, mix->send_routing[0] << 16); + } + } + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_send_routing_control = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "EMU10K1 PCM Send Routing", + info: snd_emu10k1_send_routing_info, + get: snd_emu10k1_send_routing_get, + put: snd_emu10k1_send_routing_put +}; + +static int snd_emu10k1_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 3*4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_emu10k1_send_volume_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int idx; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3*4; idx++) + ucontrol->value.integer.value[idx] = mix->send_volume[idx/4][idx%4]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_send_volume_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int change = 0, idx, val; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3*4; idx++) { + val = ucontrol->value.integer.value[idx] & 255; + if (mix->send_volume[idx/4][idx%4] != val) { + mix->send_volume[idx/4][idx%4] = val; + change = 1; + } + } + if (change && mix->epcm) { + u32 voice; + if (mix->epcm->voices[0] && mix->epcm->voices[1]) { + voice = mix->epcm->voices[0]->number; + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, mix->send_volume[1][0]); + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, mix->send_volume[1][1]); + snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, mix->send_volume[1][2]); + snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, mix->send_volume[1][3]); + voice = mix->epcm->voices[1]->number; + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, mix->send_volume[2][0]); + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, mix->send_volume[2][1]); + snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, mix->send_volume[2][2]); + snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, mix->send_volume[2][3]); + } else if (mix->epcm->voices[0]) { + voice = mix->epcm->voices[0]->number; + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, mix->send_volume[0][0]); + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, mix->send_volume[0][1]); + snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, mix->send_volume[0][2]); + snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, mix->send_volume[0][3]); + } + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_send_volume_control = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "EMU10K1 PCM Send Volume", + info: snd_emu10k1_send_volume_info, + get: snd_emu10k1_send_volume_get, + put: snd_emu10k1_send_volume_put +}; + +static int snd_emu10k1_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 3; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; + return 0; +} + +static int snd_emu10k1_attn_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int idx; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3; idx++) + ucontrol->value.integer.value[idx] = mix->attn[idx]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_attn_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int change = 0, idx, val; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3; idx++) { + val = ucontrol->value.integer.value[idx] & 0xffff; + if (mix->attn[idx] != val) { + mix->attn[idx] = val; + change = 1; + } + } + if (change && mix->epcm) { + if (mix->epcm->voices[0] && mix->epcm->voices[1]) { + snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]); + snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]); + } else if (mix->epcm->voices[0]) { + snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]); + } + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_attn_control = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "EMU10K1 PCM Volume", + info: snd_emu10k1_attn_info, + get: snd_emu10k1_attn_get, + put: snd_emu10k1_attn_put +}; + +static int snd_emu10k1_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_emu10k1_shared_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 0 : 1; + return 0; +} + +static int snd_emu10k1_shared_spdif_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned int reg, val; + int change; + + spin_lock_irqsave(&emu->reg_lock, flags); + reg = inl(emu->port + HCFG); + val = ucontrol->value.integer.value[0] & 1 ? 0 : HCFG_GPOUT0; + change = (reg & HCFG_GPOUT0) != val; + reg &= ~HCFG_GPOUT0; + reg |= val; + outl(reg | val, emu->port + HCFG); + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_shared_spdif = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "SB Live Analog/Digital Output Jack", + info: snd_emu10k1_shared_spdif_info, + get: snd_emu10k1_shared_spdif_get, + put: snd_emu10k1_shared_spdif_put +}; + +static void snd_emu10k1_mixer_free_ac97(ac97_t *ac97) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return); + emu->ac97 = NULL; +} + +int __devinit snd_emu10k1_mixer(emu10k1_t *emu) +{ + ac97_t ac97; + int err, pcm, idx; + snd_kcontrol_t *kctl; + snd_card_t *card = emu->card; + + if (!emu->APS) { + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_emu10k1_ac97_write; + ac97.read = snd_emu10k1_ac97_read; + ac97.private_data = emu; + ac97.private_free = snd_emu10k1_mixer_free_ac97; + if ((err = snd_ac97_mixer(emu->card, &ac97, &emu->ac97)) < 0) + return err; + } else { + strcpy(emu->card->mixername, "EMU APS"); + } + + for (pcm = 0; pcm < 32; pcm++) { + emu10k1_pcm_mixer_t *mix; + + mix = &emu->pcm_mixer[pcm]; + mix->epcm = NULL; + + if ((kctl = mix->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = (long)mix; + kctl->id.index = pcm; + if ((err = snd_ctl_add(card, kctl))) + return err; + if (emu->audigy) { + mix->send_routing[0] = mix->send_routing[1] = mix->send_routing[2] = 0x3210; + } else { + mix->send_routing[0] = mix->send_routing[1] = mix->send_routing[2] = 0x03020100; + } + + if ((kctl = mix->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = (long)mix; + kctl->id.index = pcm; + if ((err = snd_ctl_add(card, kctl))) + return err; + memset(&mix->send_volume, 0, sizeof(mix->send_volume)); + mix->send_volume[0][0] = mix->send_volume[0][1] = + mix->send_volume[1][0] = mix->send_volume[2][1] = 255; + + if ((kctl = mix->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = (long)mix; + kctl->id.index = pcm; + if ((err = snd_ctl_add(card, kctl))) + return err; + mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff; + } + + for (idx = 0; idx < 3; idx++) { + if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = idx; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = idx; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + } + + if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; + + return 0; +} diff -Nru a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emumpu401.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,376 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of EMU10K1 MPU-401 in UART mode + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +#define EMU10K1_MIDI_MODE_INPUT (1<<0) +#define EMU10K1_MIDI_MODE_OUTPUT (1<<1) + +static inline unsigned char mpu401_read(emu10k1_t *emu, emu10k1_midi_t *mpu, int idx) +{ + if (emu->audigy) + return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0); + else + return inb(emu->port + mpu->port + idx); +} + +static inline void mpu401_write(emu10k1_t *emu, emu10k1_midi_t *mpu, int data, int idx) +{ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data); + else + outb(data, emu->port + mpu->port + idx); +} + +#define mpu401_write_data(emu, mpu, data) mpu401_write(emu, mpu, data, 0) +#define mpu401_write_cmd(emu, mpu, data) mpu401_write(emu, mpu, data, 1) +#define mpu401_read_data(emu, mpu) mpu401_read(emu, mpu, 0) +#define mpu401_read_stat(emu, mpu) mpu401_read(emu, mpu, 1) + +#define mpu401_input_avail(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x80)) +#define mpu401_output_ready(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x40)) + +#define MPU401_RESET 0xff +#define MPU401_ENTER_UART 0x3f +#define MPU401_ACK 0xfe + +static void mpu401_clear_rx(emu10k1_t *emu, emu10k1_midi_t *mpu) +{ + int timeout = 100000; + for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--) + mpu401_read_data(emu, mpu); +#ifdef CONFIG_SND_DEBUG + if (timeout <= 0) + snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu)); +#endif +} + +/* + + */ + +static void do_emu10k1_midi_interrupt(emu10k1_t *emu, emu10k1_midi_t *midi, unsigned int status) +{ + unsigned char byte; + + if (midi->rmidi == NULL) { + snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable); + return; + } + + spin_lock(&midi->input_lock); + if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) { + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { + mpu401_clear_rx(emu, midi); + } else { + byte = mpu401_read_data(emu, midi); + spin_unlock(&midi->input_lock); + if (midi->substream_input) + snd_rawmidi_receive(midi->substream_input, &byte, 1); + spin_lock(&midi->input_lock); + } + } + spin_unlock(&midi->input_lock); + + spin_lock(&midi->output_lock); + if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) { + if (midi->substream_output && + snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { + mpu401_write_data(emu, midi, byte); + } else { + snd_emu10k1_intr_disable(emu, midi->tx_enable); + } + } + spin_unlock(&midi->output_lock); +} + +static void snd_emu10k1_midi_interrupt(emu10k1_t *emu, unsigned int status) +{ + do_emu10k1_midi_interrupt(emu, &emu->midi, status); +} + +static void snd_emu10k1_midi_interrupt2(emu10k1_t *emu, unsigned int status) +{ + do_emu10k1_midi_interrupt(emu, &emu->midi2, status); +} + +static void snd_emu10k1_midi_cmd(emu10k1_t * emu, emu10k1_midi_t *midi, unsigned char cmd, int ack) +{ + unsigned long flags; + int timeout, ok; + + spin_lock_irqsave(&midi->input_lock, flags); + mpu401_write_data(emu, midi, 0x00); + /* mpu401_clear_rx(emu, midi); */ + + mpu401_write_cmd(emu, midi, cmd); + if (ack) { + ok = 0; + timeout = 10000; + while (!ok && timeout-- > 0) { + if (mpu401_input_avail(emu, midi)) { + if (mpu401_read_data(emu, midi) == MPU401_ACK) + ok = 1; + } + } + if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK) + ok = 1; + } else { + ok = 1; + } + spin_unlock_irqrestore(&midi->input_lock, flags); + if (!ok) + snd_printk("midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n", + cmd, emu->port, + mpu401_read_stat(emu, midi), + mpu401_read_data(emu, midi)); +} + +static int snd_emu10k1_midi_input_open(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT; + midi->substream_input = substream; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1); + snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static int snd_emu10k1_midi_output_open(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT; + midi->substream_output = substream; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1); + snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static int snd_emu10k1_midi_input_close(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + snd_emu10k1_intr_disable(emu, midi->rx_enable); + midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT; + midi->substream_input = NULL; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static int snd_emu10k1_midi_output_close(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + snd_emu10k1_intr_disable(emu, midi->tx_enable); + midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT; + midi->substream_output = NULL; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static void snd_emu10k1_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + emu = midi->emu; + snd_assert(emu, return); + + if (up) + snd_emu10k1_intr_enable(emu, midi->rx_enable); + else + snd_emu10k1_intr_disable(emu, midi->rx_enable); +} + +static void snd_emu10k1_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return); + + if (up) { + int max = 4; + unsigned char byte; + + /* try to send some amount of bytes here before interrupts */ + spin_lock_irqsave(&midi->output_lock, flags); + while (max > 0) { + if (mpu401_output_ready(emu, midi)) { + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) || + snd_rawmidi_transmit(substream, &byte, 1) != 1) { + /* no more data */ + spin_unlock_irqrestore(&midi->output_lock, flags); + return; + } + mpu401_write_data(emu, midi, byte); + max--; + } else { + break; + } + } + spin_unlock_irqrestore(&midi->output_lock, flags); + snd_emu10k1_intr_enable(emu, midi->tx_enable); + } else { + snd_emu10k1_intr_disable(emu, midi->tx_enable); + } +} + +/* + + */ + +static snd_rawmidi_ops_t snd_emu10k1_midi_output = +{ + open: snd_emu10k1_midi_output_open, + close: snd_emu10k1_midi_output_close, + trigger: snd_emu10k1_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_emu10k1_midi_input = +{ + open: snd_emu10k1_midi_input_open, + close: snd_emu10k1_midi_input_close, + trigger: snd_emu10k1_midi_input_trigger, +}; + +static void snd_emu10k1_midi_free(snd_rawmidi_t *rmidi) +{ + emu10k1_midi_t *midi = (emu10k1_midi_t *)rmidi->private_data; + midi->interrupt = NULL; + midi->rmidi = NULL; +} + +static int __devinit emu10k1_midi_init(emu10k1_t *emu, emu10k1_midi_t *midi, int device, char *name) +{ + snd_rawmidi_t *rmidi; + int err; + + if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0) + return err; + midi->emu = emu; + spin_lock_init(&midi->open_lock); + spin_lock_init(&midi->input_lock); + spin_lock_init(&midi->output_lock); + strcpy(rmidi->name, name); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = midi; + rmidi->private_free = snd_emu10k1_midi_free; + midi->rmidi = rmidi; + return 0; +} + +int __devinit snd_emu10k1_midi(emu10k1_t *emu) +{ + emu10k1_midi_t *midi = &emu->midi; + int err; + + if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0) + return err; + + midi->tx_enable = INTE_MIDITXENABLE; + midi->rx_enable = INTE_MIDIRXENABLE; + midi->port = MUDATA; + midi->ipr_tx = IPR_MIDITRANSBUFEMPTY; + midi->ipr_rx = IPR_MIDIRECVBUFEMPTY; + midi->interrupt = snd_emu10k1_midi_interrupt; + return 0; +} + +int __devinit snd_emu10k1_audigy_midi(emu10k1_t *emu) +{ + emu10k1_midi_t *midi; + int err; + + midi = &emu->midi; + if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0) + return err; + + midi->tx_enable = INTE_MIDITXENABLE; + midi->rx_enable = INTE_MIDIRXENABLE; + midi->port = A_MUDATA1; + midi->ipr_tx = IPR_MIDITRANSBUFEMPTY; + midi->ipr_rx = IPR_MIDIRECVBUFEMPTY; + midi->interrupt = snd_emu10k1_midi_interrupt; + + midi = &emu->midi2; + if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0) + return err; + + midi->tx_enable = INTE_A_MIDITXENABLE2; + midi->rx_enable = INTE_A_MIDIRXENABLE2; + midi->port = A_MUDATA2; + midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2; + midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2; + midi->interrupt = snd_emu10k1_midi_interrupt2; + return 0; +} diff -Nru a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emupcm.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1121 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips / PCM routines + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#define chip_t emu10k1_t + +static void snd_emu10k1_pcm_interrupt(emu10k1_t *emu, emu10k1_voice_t *voice) +{ + emu10k1_pcm_t *epcm; + + if ((epcm = voice->epcm) == NULL) + return; + if (epcm->substream == NULL) + return; +#if 0 + printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n", + epcm->substream->runtime->hw->pointer(emu, epcm->substream), + snd_pcm_lib_period_bytes(epcm->substream), + snd_pcm_lib_buffer_bytes(epcm->substream)); +#endif + snd_pcm_period_elapsed(epcm->substream); +} + +static void snd_emu10k1_pcm_ac97adc_interrupt(emu10k1_t *emu, unsigned int status) +{ +#if 0 + if (status & IPR_ADCBUFHALFFULL) { + if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) + return; + } +#endif + snd_pcm_period_elapsed(emu->pcm_capture_substream); +} + +static void snd_emu10k1_pcm_ac97mic_interrupt(emu10k1_t *emu, unsigned int status) +{ +#if 0 + if (status & IPR_MICBUFHALFFULL) { + if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) + return; + } +#endif + snd_pcm_period_elapsed(emu->pcm_capture_mic_substream); +} + +static void snd_emu10k1_pcm_efx_interrupt(emu10k1_t *emu, unsigned int status) +{ +#if 0 + if (status & IPR_EFXBUFHALFFULL) { + if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) + return; + } +#endif + snd_pcm_period_elapsed(emu->pcm_capture_efx_substream); +} + +static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices) +{ + int err; + + if (epcm->voices[1] != NULL && voices < 2) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); + epcm->voices[1] = NULL; + } + if (voices == 1 && epcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && epcm->voices[0] != NULL && epcm->voices[1] != NULL) + return 0; + if (voices > 1) { + if (epcm->voices[0] != NULL && epcm->voices[1] == NULL) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); + epcm->voices[0] = NULL; + } + } + err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, voices > 1, &epcm->voices[0]); + if (err < 0) + return err; + epcm->voices[0]->epcm = epcm; + if (voices > 1) { + epcm->voices[1] = &epcm->emu->voices[epcm->voices[0]->number + 1]; + epcm->voices[1]->epcm = epcm; + } + if (epcm->extra == NULL) { + err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, 0, &epcm->extra); + if (err < 0) { + // printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); + snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); + epcm->voices[0] = NULL; + if (epcm->voices[1]) + snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); + epcm->voices[1] = NULL; + return err; + } + epcm->extra->epcm = epcm; + epcm->extra->interrupt = snd_emu10k1_pcm_interrupt; + } + return 0; +} + +static unsigned int capture_period_sizes[31] = { + 384, 448, 512, 640, + 384*2, 448*2, 512*2, 640*2, + 384*4, 448*4, 512*4, 640*4, + 384*8, 448*8, 512*8, 640*8, + 384*16, 448*16, 512*16, 640*16, + 384*32, 448*32, 512*32, 640*32, + 384*64, 448*64, 512*64, 640*64, + 384*128,448*128,512*128 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_capture_period_sizes = { + count: 31, + list: capture_period_sizes, + mask: 0 +}; + +static unsigned int capture_rates[8] = { + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_capture_rates = { + count: 8, + list: capture_rates, + mask: 0 +}; + +static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate) +{ + switch (rate) { + case 8000: return ADCCR_SAMPLERATE_8; + case 11025: return ADCCR_SAMPLERATE_11; + case 16000: return ADCCR_SAMPLERATE_16; + case 22050: return ADCCR_SAMPLERATE_22; + case 24000: return ADCCR_SAMPLERATE_24; + case 32000: return ADCCR_SAMPLERATE_32; + case 44100: return ADCCR_SAMPLERATE_44; + case 48000: return ADCCR_SAMPLERATE_48; + default: + snd_BUG(); + return ADCCR_SAMPLERATE_8; + } +} + +static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate) +{ + switch (rate) { + case 8000: return A_ADCCR_SAMPLERATE_8; + case 11025: return A_ADCCR_SAMPLERATE_11; + case 12000: return A_ADCCR_SAMPLERATE_12; /* really supported? */ + case 16000: return ADCCR_SAMPLERATE_16; + case 22050: return ADCCR_SAMPLERATE_22; + case 24000: return ADCCR_SAMPLERATE_24; + case 32000: return ADCCR_SAMPLERATE_32; + case 44100: return ADCCR_SAMPLERATE_44; + case 48000: return ADCCR_SAMPLERATE_48; + default: + snd_BUG(); + return A_ADCCR_SAMPLERATE_8; + } +} + +static unsigned int emu10k1_calc_pitch_target(unsigned int rate) +{ + unsigned int pitch_target; + + pitch_target = (rate << 8) / 375; + pitch_target = (pitch_target >> 1) + (pitch_target & 1); + return pitch_target; +} + +#define PITCH_48000 0x00004000 +#define PITCH_96000 0x00008000 +#define PITCH_85000 0x00007155 +#define PITCH_80726 0x00006ba2 +#define PITCH_67882 0x00005a82 +#define PITCH_57081 0x00004c1c + +static unsigned int emu10k1_select_interprom(unsigned int pitch_target) +{ + if (pitch_target == PITCH_48000) + return CCCA_INTERPROM_0; + else if (pitch_target < PITCH_48000) + return CCCA_INTERPROM_1; + else if (pitch_target >= PITCH_96000) + return CCCA_INTERPROM_0; + else if (pitch_target >= PITCH_85000) + return CCCA_INTERPROM_6; + else if (pitch_target >= PITCH_80726) + return CCCA_INTERPROM_5; + else if (pitch_target >= PITCH_67882) + return CCCA_INTERPROM_4; + else if (pitch_target >= PITCH_57081) + return CCCA_INTERPROM_3; + else + return CCCA_INTERPROM_2; +} + + +static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, + int master, int extra, + emu10k1_voice_t *evoice, + unsigned int start_addr, + unsigned int end_addr) +{ + snd_pcm_substream_t *substream = evoice->epcm->substream; + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; + unsigned int silent_page, tmp; + int voice, stereo, w_16; + unsigned char attn, send_a, send_b, send_c, send_d; + unsigned short send_routing; + unsigned long flags; + unsigned int pitch_target; + + voice = evoice->number; + stereo = runtime->channels == 2; + w_16 = snd_pcm_format_width(runtime->format) == 16; + + if (!extra && stereo) { + start_addr >>= 1; + end_addr >>= 1; + } + if (w_16) { + start_addr >>= 1; + end_addr >>= 1; + } + + spin_lock_irqsave(&emu->reg_lock, flags); + + /* volume parameters */ + if (extra) { + attn = 0; + send_routing = emu->audigy ? 0x03020100 : 0x3210; + send_a = send_b = send_c = send_d = 0x00; + } else { + tmp = stereo ? (master ? 1 : 2) : 0; + send_a = mix->send_volume[tmp][0]; + send_b = mix->send_volume[tmp][1]; + send_c = mix->send_volume[tmp][2]; + send_d = mix->send_volume[tmp][3]; + send_routing = mix->send_routing[tmp]; + } + + if (master) { + unsigned int ccis = stereo ? 28 : 30; + if (w_16) + ccis *= 2; + evoice->epcm->ccca_start_addr = start_addr + ccis; + if (extra) { + start_addr += ccis; + end_addr += ccis; + } + if (stereo && !extra) { + snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK); + snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK); + } else { + snd_emu10k1_ptr_write(emu, CPF, voice, 0); + } + } + + // setup routing + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXRT1, voice, send_routing); + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, 0); /* no effects */ + snd_emu10k1_ptr_write(emu, A_FXRT2, voice, 0); /* channels EFGH */ + } else + snd_emu10k1_ptr_write(emu, FXRT, voice, send_routing << 16); + // Stop CA + // Assumption that PT is already 0 so no harm overwriting + snd_emu10k1_ptr_write(emu, PTRX, voice, (send_a << 8) | send_b); + snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_d << 24)); + snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_c << 24)); + pitch_target = emu10k1_calc_pitch_target(runtime->rate); + snd_emu10k1_ptr_write(emu, CCCA, voice, evoice->epcm->ccca_start_addr | + emu10k1_select_interprom(pitch_target) | + (w_16 ? 0 : CCCA_8BITSELECT)); + // Clear filter delay memory + snd_emu10k1_ptr_write(emu, Z1, voice, 0); + snd_emu10k1_ptr_write(emu, Z2, voice, 0); + // invalidate maps + silent_page = ((unsigned int)emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page); + snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page); + // modulation envelope + snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); + snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); + snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0); + snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f); + snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000); + snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000); + snd_emu10k1_ptr_write(emu, FMMOD, voice, 0); + snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0); + snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0); + snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000); + // volume envelope + snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f); + snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000); + // filter envelope + snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f); + // pitch envelope + snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0); + + spin_unlock_irqrestore(&emu->reg_lock, flags); +} + +static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + int err; + + if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0) + return err; + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0) { /* change */ + snd_util_memblk_t *memblk; + if (epcm->memblk != NULL) + snd_emu10k1_free_pages(emu, epcm->memblk); + memblk = snd_emu10k1_alloc_pages(emu, runtime->dma_addr, runtime->dma_bytes); + if ((epcm->memblk = memblk) == NULL || ((emu10k1_memblk_t *)memblk)->mapped_page < 0) { + epcm->start_addr = 0; + return -ENOMEM; + } + epcm->start_addr = ((emu10k1_memblk_t *)memblk)->mapped_page << PAGE_SHIFT; + } + return 0; +} + +static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm; + + if (runtime->private_data == NULL) + return 0; + epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + if (epcm->extra) { + snd_emu10k1_voice_free(epcm->emu, epcm->extra); + epcm->extra = NULL; + } + if (epcm->voices[1]) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); + epcm->voices[1] = NULL; + } + if (epcm->voices[0]) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); + epcm->voices[0] = NULL; + } + if (epcm->memblk) { + snd_emu10k1_free_pages(emu, epcm->memblk); + epcm->memblk = NULL; + epcm->start_addr = 0; + } + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned int start_addr, end_addr; + + start_addr = epcm->start_addr; + end_addr = snd_pcm_lib_period_bytes(substream); + if (runtime->channels == 2) + end_addr >>= 1; + end_addr += start_addr; + snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, + start_addr, end_addr); + end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); + snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], + start_addr, end_addr); + if (epcm->voices[1]) + snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1], + start_addr, end_addr); + return 0; +} + +static int snd_emu10k1_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_emu10k1_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_emu10k1_capture_prepare(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + int idx; + + snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); + switch (epcm->type) { + case CAPTURE_AC97ADC: + snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); + break; + case CAPTURE_EFX: + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + break; + default: + break; + } + snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr); + epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream); + epcm->capture_bs_val = 0; + for (idx = 0; idx < 31; idx++) { + if (capture_period_sizes[idx] == epcm->capture_bufsize) { + epcm->capture_bs_val = idx + 1; + break; + } + } + if (epcm->capture_bs_val == 0) { + snd_BUG(); + epcm->capture_bs_val++; + } + if (epcm->type == CAPTURE_AC97ADC) { + epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE; + if (runtime->channels > 1) + epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE; + epcm->capture_cr_val |= emu->audigy ? + snd_emu10k1_audigy_capture_rate_reg(runtime->rate) : + snd_emu10k1_capture_rate_reg(runtime->rate); + } + return 0; +} + +static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_t *evoice) +{ + snd_pcm_runtime_t *runtime; + unsigned int voice, i, ccis, cra = 64, cs, sample; + + if (evoice == NULL) + return; + runtime = evoice->epcm->substream->runtime; + voice = evoice->number; + sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080; + if (runtime->channels > 1) { + ccis = 28; + cs = 4; + } else { + ccis = 30; + cs = 2; + } + if (sample == 0) /* 16-bit */ + ccis *= 2; + for (i = 0; i < cs; i++) + snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample); + // reset cache + snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0); + snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra); + if (runtime->channels > 1) { + snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0); + snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra); + } + // fill cache + snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis); +} + +static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + emu10k1_pcm_mixer_t *mix; + unsigned int voice, pitch, pitch_target, tmp; + unsigned int attn; + + if (evoice == NULL) /* skip second voice for mono */ + return; + substream = evoice->epcm->substream; + runtime = substream->runtime; + mix = &emu->pcm_mixer[substream->number]; + voice = evoice->number; + pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; + pitch_target = emu10k1_calc_pitch_target(runtime->rate); + attn = extra ? 0 : 0x00ff; + tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0; + snd_emu10k1_ptr_write(emu, IFATN, voice, attn); + snd_emu10k1_ptr_write(emu, VTFT, voice, (mix->attn[tmp] << 16) | 0xffff); + snd_emu10k1_ptr_write(emu, CVCF, voice, (mix->attn[tmp] << 16) | 0xffff); + snd_emu10k1_voice_clear_loop_stop(emu, voice); + if (extra) + snd_emu10k1_voice_intr_enable(emu, voice); + snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f); + snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target); + if (master) + snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target); + snd_emu10k1_ptr_write(emu, IP, voice, pitch); +} + +static void snd_emu10k1_playback_stop_voice(emu10k1_t *emu, emu10k1_voice_t *evoice) +{ + unsigned int voice; + + if (evoice == NULL) + return; + voice = evoice->number; + snd_emu10k1_voice_intr_disable(emu, voice); + snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0); + snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0); + snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff); + snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); + snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); + snd_emu10k1_ptr_write(emu, IP, voice, 0); +} + +static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned long flags; + int result = 0; + + // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); + spin_lock_irqsave(&emu->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_emu10k1_playback_invalidate_cache(emu, epcm->extra); /* do we need this? */ + snd_emu10k1_playback_invalidate_cache(emu, epcm->voices[0]); + /* follow thru */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0); + snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0); + snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1); + epcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + epcm->running = 0; + snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]); + snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]); + snd_emu10k1_playback_stop_voice(emu, epcm->extra); + break; + default: + result = -EINVAL; + break; + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return result; +} + +static int snd_emu10k1_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned long flags; + int result = 0; + + // printk("trigger - emu10k1 = %p, cmd = %i, pointer = %i\n", emu, cmd, substream->ops->pointer(substream)); + spin_lock_irqsave(&emu->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + outl(epcm->capture_ipr, emu->port + IPR); + snd_emu10k1_intr_enable(emu, epcm->capture_inte); + // printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); + switch (epcm->type) { + case CAPTURE_AC97ADC: + snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val); + break; + case CAPTURE_EFX: + snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val); + printk(">> FXWC = 0x%x\n", snd_emu10k1_ptr_read(emu, FXWC, 0)); + break; + default: + break; + } + snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val); + epcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + epcm->running = 0; + snd_emu10k1_intr_disable(emu, epcm->capture_inte); + outl(epcm->capture_ipr, emu->port + IPR); + snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); + switch (epcm->type) { + case CAPTURE_AC97ADC: + snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); + break; + case CAPTURE_EFX: + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + break; + default: + break; + } + break; + default: + result = -EINVAL; + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return result; +} + +static snd_pcm_uframes_t snd_emu10k1_playback_pointer(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned int ptr; + + if (!epcm->running) + return 0; + ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff; +#if 0 /* Perex's code */ + ptr += runtime->buffer_size; + ptr -= epcm->ccca_start_addr; + ptr %= runtime->buffer_size; +#else /* EMU10K1 Open Source code from Creative */ + if (ptr < epcm->ccca_start_addr) + ptr += runtime->buffer_size - epcm->ccca_start_addr; + else + ptr -= epcm->ccca_start_addr; +#endif + // printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size); + return ptr; +} + +static snd_pcm_uframes_t snd_emu10k1_capture_pointer(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned int ptr; + + if (!epcm->running) + return 0; + ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff; + return bytes_to_frames(runtime, ptr); +} + +/* + * Playback support device description + */ + +static snd_pcm_hardware_t snd_emu10k1_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * Capture support device description + */ + +static snd_pcm_hardware_t snd_emu10k1_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (64*1024), + period_bytes_min: 384, + period_bytes_max: (64*1024), + periods_min: 2, + periods_max: 2, + fifo_size: 0, +}; + +/* + * + */ + +static void snd_emu10k1_pcm_mixer_notify1(snd_card_t *card, snd_kcontrol_t *kctl, int activate) +{ + snd_runtime_check(kctl != NULL, return); + if (activate) + kctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + else + kctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); +} + +static void snd_emu10k1_pcm_mixer_notify(snd_card_t *card, emu10k1_pcm_mixer_t *mix, int activate) +{ + snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_send_routing, activate); + snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_send_volume, activate); + snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_attn, activate); +} + +static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return); + + if (epcm) + snd_magic_kfree(epcm); +} + +static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_t *epcm; + emu10k1_pcm_mixer_t *mix; + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = PLAYBACK_EMUVOICE; + epcm->substream = substream; + runtime->private_data = epcm; + runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_playback; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { + snd_magic_kfree(epcm); + return err; + } + if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) { + snd_magic_kfree(epcm); + return err; + } + mix = &emu->pcm_mixer[substream->number]; + mix->send_routing[0] = mix->send_routing[1] = mix->send_routing[2] = + emu->audigy ? 0x03020100 : 0x3210; + memset(&mix->send_volume, 0, sizeof(mix->send_volume)); + mix->send_volume[0][0] = mix->send_volume[0][1] = + mix->send_volume[1][0] = mix->send_volume[2][1] = 255; + mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff; + mix->epcm = epcm; + snd_emu10k1_pcm_mixer_notify(emu->card, mix, 1); + return 0; +} + +static int snd_emu10k1_playback_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; + + mix->epcm = NULL; + snd_emu10k1_pcm_mixer_notify(emu->card, mix, 0); + return 0; +} + +static int snd_emu10k1_capture_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = CAPTURE_AC97ADC; + epcm->substream = substream; + epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL; + epcm->capture_inte = INTE_ADCBUFENABLE; + epcm->capture_ba_reg = ADCBA; + epcm->capture_bs_reg = ADCBS; + epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX; + runtime->private_data = epcm; + runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_capture; + emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt; + emu->pcm_capture_substream = substream; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates); + return 0; +} + +static int snd_emu10k1_capture_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + + emu->capture_interrupt = NULL; + emu->pcm_capture_substream = NULL; + return 0; +} + +static int snd_emu10k1_capture_mic_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_t *epcm; + snd_pcm_runtime_t *runtime = substream->runtime; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = CAPTURE_AC97MIC; + epcm->substream = substream; + epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL; + epcm->capture_inte = INTE_MICBUFENABLE; + epcm->capture_ba_reg = MICBA; + epcm->capture_bs_reg = MICBS; + epcm->capture_idx_reg = MICIDX; + substream->runtime->private_data = epcm; + substream->runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_capture; + runtime->hw.rates = SNDRV_PCM_RATE_8000; + runtime->hw.rate_min = runtime->hw.rate_max = 8000; + runtime->hw.channels_min = 1; + emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt; + emu->pcm_capture_mic_substream = substream; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + return 0; +} + +static int snd_emu10k1_capture_mic_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + + emu->capture_interrupt = NULL; + emu->pcm_capture_mic_substream = NULL; + return 0; +} + +static int snd_emu10k1_capture_efx_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_t *epcm; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + int idx; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = CAPTURE_EFX; + epcm->substream = substream; + epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL; + epcm->capture_inte = INTE_EFXBUFENABLE; + epcm->capture_ba_reg = FXBA; + epcm->capture_bs_reg = FXBS; + epcm->capture_idx_reg = FXIDX; + substream->runtime->private_data = epcm; + substream->runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_capture; + runtime->hw.rates = SNDRV_PCM_RATE_48000; + runtime->hw.rate_min = runtime->hw.rate_max = 48000; + spin_lock_irqsave(&emu->reg_lock, flags); + runtime->hw.channels_min = runtime->hw.channels_max = 0; + for (idx = 0; idx < 32; idx++) { + if (emu->efx_voices_mask & (1 << idx)) { + runtime->hw.channels_min++; + runtime->hw.channels_max++; + } + } + epcm->capture_cr_val = emu->efx_voices_mask; + spin_unlock_irqrestore(&emu->reg_lock, flags); + emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt; + emu->pcm_capture_efx_substream = substream; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + return 0; +} + +static int snd_emu10k1_capture_efx_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + + emu->capture_interrupt = NULL; + emu->pcm_capture_efx_substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_emu10k1_playback_ops = { + open: snd_emu10k1_playback_open, + close: snd_emu10k1_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_emu10k1_playback_hw_params, + hw_free: snd_emu10k1_playback_hw_free, + prepare: snd_emu10k1_playback_prepare, + trigger: snd_emu10k1_playback_trigger, + pointer: snd_emu10k1_playback_pointer, +}; + +static snd_pcm_ops_t snd_emu10k1_capture_ops = { + open: snd_emu10k1_capture_open, + close: snd_emu10k1_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_emu10k1_capture_hw_params, + hw_free: snd_emu10k1_capture_hw_free, + prepare: snd_emu10k1_capture_prepare, + trigger: snd_emu10k1_capture_trigger, + pointer: snd_emu10k1_capture_pointer, +}; + +static void snd_emu10k1_pcm_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_ops); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, "EMU10K1"); + emu->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +static snd_pcm_ops_t snd_emu10k1_capture_mic_ops = { + open: snd_emu10k1_capture_mic_open, + close: snd_emu10k1_capture_mic_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_emu10k1_capture_hw_params, + hw_free: snd_emu10k1_capture_hw_free, + prepare: snd_emu10k1_capture_prepare, + trigger: snd_emu10k1_capture_trigger, + pointer: snd_emu10k1_capture_pointer, +}; + +static void snd_emu10k1_pcm_mic_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm_mic = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_pcm_mic_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, "EMU10K1 MIC"); + emu->pcm_mic = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static int snd_emu10k1_pcm_efx_voices_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 32; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_emu10k1_pcm_efx_voices_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int idx; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 32; idx++) + ucontrol->value.integer.value[idx] = (emu->efx_voices_mask & (1 << idx)) ? 1 : 0; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_pcm_efx_voices_mask_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int nval, bits; + int change, idx; + + for (idx = 0, nval = bits = 0; idx < 32; idx++) + if (ucontrol->value.integer.value[idx]) { + nval |= 1 << idx; + bits++; + } + if (bits != 1 && bits != 2 && bits != 4 && bits != 8) + return -EINVAL; + spin_lock_irqsave(&emu->reg_lock, flags); + change = nval != emu->efx_voices_mask; + emu->efx_voices_mask = nval; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_pcm_efx_voices_mask = { + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "EFX voices mask", + info: snd_emu10k1_pcm_efx_voices_mask_info, + get: snd_emu10k1_pcm_efx_voices_mask_get, + put: snd_emu10k1_pcm_efx_voices_mask_put +}; + +static snd_pcm_ops_t snd_emu10k1_capture_efx_ops = { + open: snd_emu10k1_capture_efx_open, + close: snd_emu10k1_capture_efx_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_emu10k1_capture_hw_params, + hw_free: snd_emu10k1_capture_hw_free, + prepare: snd_emu10k1_capture_prepare, + trigger: snd_emu10k1_capture_trigger, + pointer: snd_emu10k1_capture_pointer, +}; + +static void snd_emu10k1_pcm_efx_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm_efx = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 0, 1, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_pcm_efx_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, "EMU10K1 EFX"); + emu->pcm_efx = pcm; + if (rpcm) + *rpcm = pcm; + + emu->efx_voices_mask = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; + snd_ctl_add(emu->card, snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu)); + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024); + + return 0; +} diff -Nru a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/emuproc.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,343 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips / proc interface routines + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +static void snd_emu10k1_proc_spdif_status(emu10k1_t * emu, + snd_info_buffer_t * buffer, + char *title, + int status_reg, + int rate_reg) +{ + static char *clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" }; + static int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + static char *channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" }; + static char *emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" }; + unsigned int status, rate = 0; + + status = snd_emu10k1_ptr_read(emu, status_reg, 0); + if (rate_reg > 0) + rate = snd_emu10k1_ptr_read(emu, rate_reg, 0); + + snd_iprintf(buffer, "\n%s\n", title); + + snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no"); + snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no"); + snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no"); + snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]); + snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6); + snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8); + snd_iprintf(buffer, "Generation Status : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy"); + snd_iprintf(buffer, "Source Mask : %i\n", (status & SPCS_SOURCENUMMASK) >> 16); + snd_iprintf(buffer, "Channel Number : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]); + snd_iprintf(buffer, "Sample Rate : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]); + snd_iprintf(buffer, "Clock Accuracy : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]); + + if (rate_reg > 0) { + snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off"); + snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off"); + snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", rate & SRCS_ESTSAMPLERATE); + } +} + +static void snd_emu10k1_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + static char *outputs[32] = { + /* 00 */ "PCM Left", + /* 01 */ "PCM Right", + /* 02 */ "PCM Surround Left", + /* 03 */ "PCM Surround Right", + /* 04 */ "MIDI Left", + /* 05 */ "MIDI Right", + /* 06 */ "PCM Center", + /* 07 */ "PCM LFE", + /* 08 */ "???", + /* 09 */ "???", + /* 10 */ "???", + /* 11 */ "???", + /* 12 */ "MIDI Reverb", + /* 13 */ "MIDI Chorus", + /* 14 */ "???", + /* 15 */ "???", + /* 16 */ "???", + /* 17 */ "???", + /* 18 */ "ADC Left / CDROM S/PDIF Left", + /* 19 */ "ADC Right / CDROM S/PDIF Right", + /* 20 */ "MIC / Zoom Video Left", + /* 21 */ "Zoom Video Right", + /* 22 */ "S/PDIF Left", + /* 23 */ "S/PDIF Right", + /* 24 */ "???", + /* 25 */ "???", + /* 26 */ "???", + /* 27 */ "???", + /* 28 */ "???", + /* 29 */ "???", + /* 30 */ "???", + /* 31 */ "???" + }; + emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return); + unsigned int val; + int idx; + + snd_iprintf(buffer, "EMU10K1\n\n"); + val = emu->audigy ? + snd_emu10k1_ptr_read(emu, A_FXRT1, 0) : + snd_emu10k1_ptr_read(emu, FXRT, 0); + snd_iprintf(buffer, "Card : %s\n", + emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative")); + snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size); + snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", emu->fx8010.etram_size); + snd_iprintf(buffer, "\n"); + if (emu->audigy) { + snd_iprintf(buffer, "Effect Send Routing : A=%i, B=%i, C=%i, D=%i\n", + val & 0x3f, + (val >> 8) & 0x3f, + (val >> 16) & 0x3f, + (val >> 24) & 0x3f); + } else { + snd_iprintf(buffer, "Effect Send Routing : A=%i, B=%i, C=%i, D=%i\n", + (val >> 16) & 0x0f, + (val >> 20) & 0x0f, + (val >> 24) & 0x0f, + (val >> 28) & 0x0f); + } + snd_iprintf(buffer, "\nCaptured FX Outputs :\n"); + for (idx = 0; idx < 32; idx++) { + if (emu->efx_voices_mask & (1 << idx)) + snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]); + } + snd_iprintf(buffer, "\nAll FX Outputs :\n"); + for (idx = 0; idx < 32; idx++) + snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]); + snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 0", SPCS0, -1); + snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 1", SPCS1, -1); + snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 2/3", SPCS2, -1); + snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF", CDCS, CDSRCS); + snd_emu10k1_proc_spdif_status(emu, buffer, "General purpose S/PDIF", GPSCS, GPSRCS); + val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0); + snd_iprintf(buffer, "\nZoomed Video\n"); + snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off"); + snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE); +} + +static void snd_emu10k1_proc_acode_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + u32 pc; + emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return); + + snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name); + snd_iprintf(buffer, " Code dump :\n"); + for (pc = 0; pc < 512; pc++) { + u32 low, high; + + low = snd_emu10k1_efx_read(emu, pc * 2); + high = snd_emu10k1_efx_read(emu, pc * 2 + 1); + if (emu->audigy) + snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n", + (high >> 24) & 0x0f, + (high >> 12) & 0x7ff, + (high >> 0) & 0x7ff, + (low >> 12) & 0x7ff, + (low >> 0) & 0x7ff, + pc, + high, low); + else + snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n", + (high >> 20) & 0x0f, + (high >> 10) & 0x3ff, + (high >> 0) & 0x3ff, + (low >> 10) & 0x3ff, + (low >> 0) & 0x3ff, + pc, + high, low); + } +} + +#define TOTAL_SIZE_GPR (0x100*4) +#define TOTAL_SIZE_TANKMEM_DATA (0xa0*4) +#define TOTAL_SIZE_TANKMEM_ADDR (0xa0*4) +#define TOTAL_SIZE_CODE (0x200*8) + +static long snd_emu10k1_fx8010_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return -ENXIO); + unsigned int offset; + + if (!strcmp(entry->name, "fx8010_tram_addr")) { + if (emu->audigy) return -EINVAL; + offset = TANKMEMADDRREGBASE; + } else if (!strcmp(entry->name, "fx8010_tram_data")) { + if (emu->audigy) return -EINVAL; + offset = TANKMEMDATAREGBASE; + } else if (!strcmp(entry->name, "fx8010_code")) { + offset = emu->audigy ? A_MICROCODEBASE : MICROCODEBASE; + } else { + offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE; + } + size = count; + if (file->f_pos + size > entry->size) + size = (long)entry->size - file->f_pos; + if (size > 0) { + unsigned int *tmp; + long res; + unsigned int idx; + if ((tmp = kmalloc(size + 8, GFP_KERNEL)) == NULL) + return -ENOMEM; + for (idx = 0; idx < ((file->f_pos & 3) + size + 3) >> 2; idx++) + tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (file->f_pos >> 2), 0); + if (copy_to_user(buf, ((char *)tmp) + (file->f_pos & 3), size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = { + read: snd_emu10k1_fx8010_read, +}; + +int __devinit snd_emu10k1_proc_init(emu10k1_t * emu) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(emu->card, "emu10k1", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 4096; + entry->c.text.read = snd_emu10k1_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry = entry; + if ((entry = snd_info_create_card_entry(emu->card, "fx8010_gpr", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->size = TOTAL_SIZE_GPR; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry_fx8010_gpr = entry; + if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_data", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->size = TOTAL_SIZE_TANKMEM_DATA; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry_fx8010_tram_data = entry; + if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_addr", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->size = TOTAL_SIZE_TANKMEM_ADDR; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry_fx8010_tram_addr = entry; + if ((entry = snd_info_create_card_entry(emu->card, "fx8010_code", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->size = TOTAL_SIZE_CODE; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry_fx8010_code = entry; + if ((entry = snd_info_create_card_entry(emu->card, "fx8010_acode", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 64*1024; + entry->c.text.read = snd_emu10k1_proc_acode_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry_fx8010_iblocks = entry; + return 0; +} + +int snd_emu10k1_proc_done(emu10k1_t * emu) +{ + if (emu->proc_entry) { + snd_info_unregister(emu->proc_entry); + emu->proc_entry = NULL; + } + if (emu->proc_entry_fx8010_gpr) { + snd_info_unregister(emu->proc_entry_fx8010_gpr); + emu->proc_entry_fx8010_gpr = NULL; + } + if (emu->proc_entry_fx8010_tram_data) { + snd_info_unregister(emu->proc_entry_fx8010_tram_data); + emu->proc_entry_fx8010_tram_data = NULL; + } + if (emu->proc_entry_fx8010_tram_addr) { + snd_info_unregister(emu->proc_entry_fx8010_tram_addr); + emu->proc_entry_fx8010_tram_addr = NULL; + } + if (emu->proc_entry_fx8010_code) { + snd_info_unregister(emu->proc_entry_fx8010_code); + emu->proc_entry_fx8010_code = NULL; + } + if (emu->proc_entry_fx8010_iblocks) { + snd_info_unregister(emu->proc_entry_fx8010_iblocks); + emu->proc_entry_fx8010_iblocks = NULL; + } + return 0; +} diff -Nru a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/io.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,341 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn) +{ + unsigned long flags; + unsigned int regptr, val; + unsigned int mask; + + mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; + regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) { + unsigned char size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + val = inl(emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + + return (val & mask) >> offset; + } else { + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + val = inl(emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + return val; + } +} + +void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data) +{ + unsigned int regptr; + unsigned long flags; + unsigned int mask; + + mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; + regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) { + unsigned char size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + data = (data << offset) & mask; + + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + data |= inl(emu->port + DATA) & ~mask; + outl(data, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + } else { + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + outl(data, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + } +} + +void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb) +{ + unsigned long flags; + unsigned int enable; + + spin_lock_irqsave(&emu->emu_lock, flags); + enable = inl(emu->port + INTE) | intrenb; + outl(enable, emu->port + INTE); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb) +{ + unsigned long flags; + unsigned int enable; + + spin_lock_irqsave(&emu->emu_lock, flags); + enable = inl(emu->port + INTE) & ~intrenb; + outl(enable, emu->port + INTE); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(CLIEH << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val |= 1 << (voicenum - 32); + } else { + outl(CLIEL << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val |= 1 << voicenum; + } + outl(val, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(CLIEH << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val &= ~(1 << (voicenum - 32)); + } else { + outl(CLIEL << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val &= ~(1 << voicenum); + } + outl(val, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(CLIPH << 16, emu->port + PTR); + voicenum = 1 << (voicenum - 32); + } else { + outl(CLIPL << 16, emu->port + PTR); + voicenum = 1 << voicenum; + } + outl(voicenum, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int sol; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(SOLEH << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol |= 1 << (voicenum - 32); + } else { + outl(SOLEL << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol |= 1 << voicenum; + } + outl(sol, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int sol; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(SOLEH << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol &= ~(1 << (voicenum - 32)); + } else { + outl(SOLEL << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol &= ~(1 << voicenum); + } + outl(sol, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait) +{ + volatile unsigned count; + unsigned int newtime = 0, curtime; + + curtime = inl(emu->port + WC) >> 6; + while (wait-- > 0) { + count = 0; + while (count++ < 16384) { + newtime = inl(emu->port + WC) >> 6; + if (newtime != curtime) + break; + } + if (count >= 16384) + break; + curtime = newtime; + } +} + +unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return -ENXIO); + unsigned long flags; + unsigned short val; + + spin_lock_irqsave(&emu->emu_lock, flags); + outb(reg, emu->port + AC97ADDRESS); + val = inw(emu->port + AC97DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + return val; +} + +void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&emu->emu_lock, flags); + outb(reg, emu->port + AC97ADDRESS); + outw(data, emu->port + AC97DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +/* + * convert rate to pitch + */ + +unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate) +{ + static u32 logMagTable[128] = { + 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, + 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, + 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, + 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, + 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, + 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, + 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, + 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, + 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, + 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, + 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, + 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, + 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, + 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, + 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, + 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df + }; + static char logSlopeTable[128] = { + 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, + 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, + 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, + 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, + 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, + 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, + 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, + 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, + 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, + 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, + 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, + 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f + }; + int i; + + if (rate == 0) + return 0; /* Bail out if no leading "1" */ + rate *= 11185; /* Scale 48000 to 0x20002380 */ + for (i = 31; i > 0; i--) { + if (rate & 0x80000000) { /* Detect leading "1" */ + return (((unsigned int) (i - 15) << 20) + + logMagTable[0x7f & (rate >> 24)] + + (0x7f & (rate >> 17)) * + logSlopeTable[0x7f & (rate >> 24)]); + } + rate <<= 1; + } + + return 0; /* Should never reach this point */ +} + +/* + * Returns an attenuation based upon a cumulative volume value + * Algorithm calculates 0x200 - 0x10 log2 (input) + */ + +unsigned char snd_emu10k1_sum_vol_attn(unsigned int value) +{ + unsigned short count = 16, ans; + + if (value == 0) + return 0xFF; + + /* Find first SET bit. This is the integer part of the value */ + while ((value & 0x10000) == 0) { + value <<= 1; + count--; + } + + /* The REST of the data is the fractional part. */ + ans = (unsigned short) (0x110 - ((count << 4) + ((value & 0x0FFFFL) >> 12))); + if (ans > 0xFF) + ans = 0xFF; + + return (unsigned char) ans; +} diff -Nru a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/irq.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,149 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for IRQ control of EMU10K1 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +void snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, dev_id, return); + unsigned int status; + + while ((status = inl(emu->port + IPR)) != 0) { + // printk("irq - status = 0x%x\n", status); + if (status & IPR_PCIERROR) { + snd_printk("interrupt: PCI error\n"); + snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE); + } + if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) { + if (emu->hwvol_interrupt) + emu->hwvol_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE); + outl(status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE), emu->port + IPR); + } + if (status & IPR_CHANNELLOOP) { + int voice; + int voice_max = status & IPR_CHANNELNUMBERMASK; + int voice_max_l; + u32 val; + emu10k1_voice_t *pvoice = emu->voices; + + val = snd_emu10k1_ptr_read(emu, CLIPL, 0); + voice_max_l = voice_max; + if (voice_max_l >= 0x20) + voice_max_l = 0x1f; + for (voice = 0; voice <= voice_max_l; voice++) { + if (val & 1) { + if (pvoice->use && pvoice->interrupt != NULL) { + pvoice->interrupt(emu, pvoice); + snd_emu10k1_voice_intr_ack(emu, voice); + } else { + snd_emu10k1_voice_intr_disable(emu, voice); + } + } + val >>= 1; + pvoice++; + } + if (voice_max > 0x1f) { + val = snd_emu10k1_ptr_read(emu, CLIPH, 0); + for (; voice <= voice_max; voice++) { + if(val & 1) { + if (pvoice->use && pvoice->interrupt != NULL) { + pvoice->interrupt(emu, pvoice); + snd_emu10k1_voice_intr_ack(emu, voice); + } else { + snd_emu10k1_voice_intr_disable(emu, voice); + } + } + val >>= 1; + pvoice++; + } + } + outl(IPR_CHANNELLOOP | (status & IPR_CHANNELNUMBERMASK), emu->port + IPR); + } + if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) { + if (emu->capture_interrupt) + emu->capture_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE); + outl(status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL), emu->port + IPR); + } + if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) { + if (emu->capture_mic_interrupt) + emu->capture_mic_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE); + outl(status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL), emu->port + IPR); + } + if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) { + if (emu->capture_efx_interrupt) + emu->capture_efx_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE); + outl(status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL), emu->port + IPR); + } + if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { + if (emu->midi.interrupt) + emu->midi.interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); + outl(status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY), emu->port + IPR); + } + if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) { + if (emu->midi2.interrupt) + emu->midi2.interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2); + outl(status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2), emu->port + IPR); + } + if (status & IPR_INTERVALTIMER) { + if (emu->timer_interrupt) + emu->timer_interrupt(emu); + else + snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); + outl(IPR_INTERVALTIMER, emu->port + IPR); + } + if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) { + if (emu->spdif_interrupt) + emu->spdif_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE); + outl(status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE), emu->port + IPR); + } + if (status & IPR_FXDSP) { + if (emu->dsp_interrupt) + emu->dsp_interrupt(emu); + else + snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); + outl(IPR_FXDSP, emu->port + IPR); + } + } +} diff -Nru a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/memory.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,541 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Copyright (c) by Takashi Iwai + * + * EMU10K1 memory page allocation (PTB area) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +/* page arguments of these two macros are Emu page (4096 bytes), not like + * aligned pages in others + */ +#define __set_ptb_entry(emu,page,addr) \ + ((emu)->ptb_pages[page] = ((addr) << 1) | (page)) + +#define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE) +#define MAX_ALIGN_PAGES (MAXPAGES / UNIT_PAGES) +/* get aligned page from offset address */ +#define get_aligned_page(offset) ((offset) >> PAGE_SHIFT) +/* get offset address from aligned page */ +#define aligned_page_offset(page) ((page) << PAGE_SHIFT) + +#if PAGE_SIZE == 4096 +/* page size == EMUPAGESIZE */ +/* fill PTB entrie(s) corresponding to page with addr */ +#define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr) +/* fill PTB entrie(s) corresponding to page with silence pointer */ +#define set_silent_ptb(emu,page) __set_ptb_entry(emu,page,emu->silent_page_dmaaddr) +#else +/* fill PTB entries -- we need to fill UNIT_PAGES entries */ +static inline void set_ptb_entry(emu10k1_t *emu, int page, dma_addr_t addr) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) { + __set_ptb_entry(emu, page, addr); + addr += EMUPAGESIZE; + } +} +static inline void set_silent_ptb(emu10k1_t *emu, int page) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) + /* do not increment ptr */ + __set_ptb_entry(emu, page, emu->silent_page_dmaaddr); +} +#endif /* PAGE_SIZE */ + + +/* + */ +static int synth_alloc_pages(emu10k1_t *hw, emu10k1_memblk_t *blk); +static int synth_free_pages(emu10k1_t *hw, emu10k1_memblk_t *blk); + +#define get_emu10k1_memblk(l,member) list_entry(l, emu10k1_memblk_t, member) + + +/* initialize emu10k1 part */ +static void emu10k1_memblk_init(emu10k1_memblk_t *blk) +{ + blk->mapped_page = -1; + INIT_LIST_HEAD(&blk->mapped_link); + INIT_LIST_HEAD(&blk->mapped_order_link); + blk->map_locked = 0; + + blk->first_page = get_aligned_page(blk->mem.offset); + blk->last_page = get_aligned_page(blk->mem.offset + blk->mem.size - 1); + blk->pages = blk->last_page - blk->first_page + 1; +} + +/* + * search empty region on PTB with the given size + * + * if an empty region is found, return the page and store the next mapped block + * in nextp + * if not found, return a negative error code. + */ +static int search_empty_map_area(emu10k1_t *emu, int npages, struct list_head **nextp) +{ + int page = 0, found_page = -ENOMEM; + int max_size = npages; + int size; + struct list_head *candidate = &emu->mapped_link_head; + struct list_head *pos; + + list_for_each (pos, &emu->mapped_link_head) { + emu10k1_memblk_t *blk = get_emu10k1_memblk(pos, mapped_link); + snd_assert(blk->mapped_page >= 0, continue); + size = blk->mapped_page - page; + if (size == npages) { + *nextp = pos; + return page; + } + else if (size > max_size) { + /* we look for the maximum empty hole */ + max_size = size; + candidate = pos; + found_page = page; + } + page = blk->mapped_page + blk->pages; + } + size = MAX_ALIGN_PAGES - page; + if (size >= max_size) { + *nextp = pos; + return page; + } + *nextp = candidate; + return found_page; +} + +/* + * map a memory block onto emu10k1's PTB + * + * call with memblk_lock held + */ +static int map_memblk(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int page, pg; + struct list_head *next; + + page = search_empty_map_area(emu, blk->pages, &next); + if (page < 0) /* not found */ + return page; + /* insert this block in the proper position of mapped list */ + list_add_tail(&blk->mapped_link, next); + /* append this as a newest block in order list */ + list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head); + blk->mapped_page = page; + /* fill PTB */ + for (pg = blk->first_page; pg <= blk->last_page; pg++) { + set_ptb_entry(emu, page, emu->page_addr_table[pg]); + page++; + } + return 0; +} + +/* + * unmap the block + * return the size of resultant empty pages + * + * call with memblk_lock held + */ +static int unmap_memblk(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int start_page, end_page, mpage, pg; + struct list_head *p; + emu10k1_memblk_t *q; + + /* calculate the expected size of empty region */ + if ((p = blk->mapped_link.prev) != &emu->mapped_link_head) { + q = get_emu10k1_memblk(p, mapped_link); + start_page = q->mapped_page + q->pages; + } else + start_page = 0; + if ((p = blk->mapped_link.next) != &emu->mapped_link_head) { + q = get_emu10k1_memblk(p, mapped_link); + end_page = q->mapped_page; + } else + end_page = MAX_ALIGN_PAGES; + + /* remove links */ + list_del(&blk->mapped_link); + list_del(&blk->mapped_order_link); + /* clear PTB */ + mpage = blk->mapped_page; + for (pg = blk->first_page; pg <= blk->last_page; pg++) { + set_silent_ptb(emu, mpage); + mpage++; + } + blk->mapped_page = -1; + return end_page - start_page; /* return the new empty size */ +} + +/* + * search empty pages with the given size, and create a memory block + * + * unlike synth_alloc the memory block is aligned to the page start + */ +static emu10k1_memblk_t * +search_empty(emu10k1_t *emu, int size) +{ + struct list_head *p; + emu10k1_memblk_t *blk; + int page, psize; + + psize = get_aligned_page(size + PAGE_SIZE -1); + page = 0; + list_for_each(p, &emu->memhdr->block) { + blk = get_emu10k1_memblk(p, mem.list); + if (page + psize <= blk->first_page) + goto __found_pages; + page = blk->last_page + 1; + } + if (page + psize > emu->max_cache_pages) + return NULL; + +__found_pages: + /* create a new memory block */ + blk = (emu10k1_memblk_t *)__snd_util_memblk_new(emu->memhdr, psize << PAGE_SHIFT, p->prev); + if (blk == NULL) + return NULL; + blk->mem.offset = aligned_page_offset(page); /* set aligned offset */ + emu10k1_memblk_init(blk); + return blk; +} + + +/* + * check if the given pointer is valid for pages + */ +static int is_valid_page(dma_addr_t addr) +{ + if (addr & ~0x7fffffffUL) { + snd_printk("max memory size is 2GB!!\n"); + return 0; + } + if (addr & (EMUPAGESIZE-1)) { + snd_printk("page is not aligned\n"); + return 0; + } + return 1; +} + +/* + * map the given memory block on PTB. + * if the block is already mapped, update the link order. + * if no empty pages are found, tries to release unsed memory blocks + * and retry the mapping. + */ +int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int err; + int size; + struct list_head *p, *nextp; + emu10k1_memblk_t *deleted; + unsigned long flags; + + spin_lock_irqsave(&emu->memblk_lock, flags); + if (blk->mapped_page >= 0) { + /* update order link */ + list_del(&blk->mapped_order_link); + list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head); + spin_unlock_irqrestore(&emu->memblk_lock, flags); + return 0; + } + if ((err = map_memblk(emu, blk)) < 0) { + /* no enough page - try to unmap some blocks */ + /* starting from the oldest block */ + p = emu->mapped_order_link_head.next; + for (; p != &emu->mapped_order_link_head; p = nextp) { + nextp = p->next; + deleted = get_emu10k1_memblk(p, mapped_order_link); + if (deleted->map_locked) + continue; + size = unmap_memblk(emu, deleted); + if (size >= blk->pages) { + /* ok the empty region is enough large */ + err = map_memblk(emu, blk); + break; + } + } + } + spin_unlock_irqrestore(&emu->memblk_lock, flags); + return err; +} + +/* + * page allocation for DMA + */ +snd_util_memblk_t * +snd_emu10k1_alloc_pages(emu10k1_t *emu, dma_addr_t addr, unsigned long size) +{ + snd_util_memhdr_t *hdr; + emu10k1_memblk_t *blk; + int page, err; + + snd_assert(emu, return NULL); + snd_assert(size > 0 && size < MAXPAGES * EMUPAGESIZE, return NULL); + hdr = emu->memhdr; + snd_assert(hdr, return NULL); + + if (!is_valid_page(addr)) + return NULL; + + down(&hdr->block_mutex); + blk = search_empty(emu, size); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + /* fill buffer addresses but pointers are not stored so that + * snd_free_pci_pages() is not called in in synth_free() + */ + for (page = blk->first_page; page <= blk->last_page; page++) { + emu->page_addr_table[page] = addr; + emu->page_ptr_table[page] = NULL; + addr += PAGE_SIZE; + } + + /* set PTB entries */ + blk->map_locked = 1; /* do not unmap this block! */ + err = snd_emu10k1_memblk_map(emu, blk); + if (err < 0) { + __snd_util_mem_free(hdr, (snd_util_memblk_t *)blk); + up(&hdr->block_mutex); + return NULL; + } + up(&hdr->block_mutex); + return (snd_util_memblk_t *)blk; +} + + +/* + * release DMA buffer from page table + */ +int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk) +{ + snd_assert(emu && blk, return -EINVAL); + return snd_emu10k1_synth_free(emu, blk); +} + + +/* + * memory allocation using multiple pages (for synth) + * Unlike the DMA allocation above, non-contiguous pages are assined. + */ + +/* + * allocate a synth sample area + */ +snd_util_memblk_t * +snd_emu10k1_synth_alloc(emu10k1_t *hw, unsigned int size) +{ + emu10k1_memblk_t *blk; + snd_util_memhdr_t *hdr = hw->memhdr; + + down(&hdr->block_mutex); + blk = (emu10k1_memblk_t *)__snd_util_mem_alloc(hdr, size); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + if (synth_alloc_pages(hw, blk)) { + __snd_util_mem_free(hdr, (snd_util_memblk_t *)blk); + up(&hdr->block_mutex); + return NULL; + } + snd_emu10k1_memblk_map(hw, blk); + up(&hdr->block_mutex); + return (snd_util_memblk_t *)blk; +} + + +/* + * free a synth sample area + */ +int +snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *memblk) +{ + snd_util_memhdr_t *hdr = emu->memhdr; + emu10k1_memblk_t *blk = (emu10k1_memblk_t *)memblk; + unsigned long flags; + + down(&hdr->block_mutex); + spin_lock_irqsave(&emu->memblk_lock, flags); + if (blk->mapped_page >= 0) + unmap_memblk(emu, blk); + spin_unlock_irqrestore(&emu->memblk_lock, flags); + synth_free_pages(emu, blk); + __snd_util_mem_free(hdr, memblk); + up(&hdr->block_mutex); + return 0; +} + + +/* check new allocation range */ +static void get_single_page_range(snd_util_memhdr_t *hdr, emu10k1_memblk_t *blk, int *first_page_ret, int *last_page_ret) +{ + struct list_head *p; + emu10k1_memblk_t *q; + int first_page, last_page; + first_page = blk->first_page; + if ((p = blk->mem.list.prev) != &hdr->block) { + q = get_emu10k1_memblk(p, mem.list); + if (q->last_page == first_page) + first_page++; /* first page was already allocated */ + } + last_page = blk->last_page; + if ((p = blk->mem.list.next) != &hdr->block) { + q = get_emu10k1_memblk(p, mem.list); + if (q->first_page == last_page) + last_page--; /* last page was already allocated */ + } + *first_page_ret = first_page; + *last_page_ret = last_page; +} + +/* + * allocate kernel pages + */ +static int synth_alloc_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int page, first_page, last_page; + void *ptr; + dma_addr_t addr; + + emu10k1_memblk_init(blk); + get_single_page_range(emu->memhdr, blk, &first_page, &last_page); + /* allocate kernel pages */ + for (page = first_page; page <= last_page; page++) { + ptr = snd_malloc_pci_pages(emu->pci, PAGE_SIZE, &addr); + if (ptr == NULL) + goto __fail; + if (! is_valid_page(addr)) { + snd_free_pci_pages(emu->pci, PAGE_SIZE, ptr, addr); + goto __fail; + } + emu->page_addr_table[page] = addr; + emu->page_ptr_table[page] = ptr; + } + return 0; + +__fail: + /* release allocated pages */ + last_page = page - 1; + for (page = first_page; page <= last_page; page++) { + snd_free_pci_pages(emu->pci, PAGE_SIZE, emu->page_ptr_table[page], emu->page_addr_table[page]); + emu->page_addr_table[page] = 0; + emu->page_ptr_table[page] = NULL; + } + + return -ENOMEM; +} + +/* + * free pages + */ +static int synth_free_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int page, first_page, last_page; + + get_single_page_range(emu->memhdr, blk, &first_page, &last_page); + for (page = first_page; page <= last_page; page++) { + if (emu->page_ptr_table[page]) + snd_free_pci_pages(emu->pci, PAGE_SIZE, emu->page_ptr_table[page], emu->page_addr_table[page]); + emu->page_addr_table[page] = 0; + emu->page_ptr_table[page] = NULL; + } + + return 0; +} + +/* calculate buffer pointer from offset address */ +inline static void *offset_ptr(emu10k1_t *emu, int page, int offset) +{ + char *ptr; + snd_assert(page >= 0 && page < emu->max_cache_pages, return NULL); + ptr = emu->page_ptr_table[page]; + if (! ptr) { + printk("emu10k1: access to NULL ptr: page = %d\n", page); + return NULL; + } + ptr += offset & (PAGE_SIZE - 1); + return (void*)ptr; +} + +/* + * bzero(blk + offset, size) + */ +int snd_emu10k1_synth_bzero(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, int size) +{ + int page, nextofs, end_offset, temp, temp1; + void *ptr; + emu10k1_memblk_t *p = (emu10k1_memblk_t *)blk; + + offset += blk->offset & (PAGE_SIZE - 1); + end_offset = offset + size; + page = get_aligned_page(offset); + do { + nextofs = aligned_page_offset(page + 1); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + ptr = offset_ptr(emu, page + p->first_page, offset); + if (ptr) + memset(ptr, 0, temp); + offset = nextofs; + page++; + } while (offset < end_offset); + return 0; +} + +/* + * copy_from_user(blk + offset, data, size) + */ +int snd_emu10k1_synth_copy_from_user(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, const char *data, int size) +{ + int page, nextofs, end_offset, temp, temp1; + void *ptr; + emu10k1_memblk_t *p = (emu10k1_memblk_t *)blk; + + offset += blk->offset & (PAGE_SIZE - 1); + end_offset = offset + size; + page = get_aligned_page(offset); + do { + nextofs = aligned_page_offset(page + 1); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + ptr = offset_ptr(emu, page + p->first_page, offset); + if (ptr && copy_from_user(ptr, data, temp)) + return -EFAULT; + offset = nextofs; + data += temp; + page++; + } while (offset < end_offset); + return 0; +} diff -Nru a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/emu10k1/voice.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,112 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips - voice manager + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice) +{ + emu10k1_voice_t *voice, *voice2; + int idx; + + *rvoice = NULL; + for (idx = 0; idx < 64; idx += pair ? 2 : 1) { + voice = &emu->voices[idx]; + voice2 = pair ? &emu->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case EMU10K1_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case EMU10K1_SYNTH: + voice->synth = 1; + break; + case EMU10K1_MIDI: + voice->midi = 1; + break; + } + // printk("voice alloc - %i, pair = %i\n", voice->number, pair); + *rvoice = voice; + return 0; + } + return -ENOMEM; +} + +int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice) +{ + unsigned long flags; + int result; + + snd_assert(rvoice != NULL, return -EINVAL); + snd_assert(!pair || type == EMU10K1_PCM, return -EINVAL); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (;;) { + result = voice_alloc(emu, type, pair, rvoice); + if (result == 0 || type != EMU10K1_PCM) + break; + + /* free a voice from synth */ + if (emu->get_synth_voice) { + result = emu->get_synth_voice(emu); + if (result >= 0) { + emu10k1_voice_t *pvoice = &emu->voices[result]; + pvoice->interrupt = NULL; + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->epcm = NULL; + } + } + if (result < 0) + break; + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + + return result; +} + +int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice) +{ + unsigned long flags; + + snd_assert(pvoice != NULL, return -EINVAL); + spin_lock_irqsave(&emu->voice_lock, flags); + pvoice->interrupt = NULL; + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->epcm = NULL; + snd_emu10k1_voice_init(emu, pvoice->number); + spin_unlock_irqrestore(&emu->voice_lock, flags); + return 0; +} diff -Nru a/sound/pci/ens1370.c b/sound/pci/ens1370.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ens1370.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2090 @@ +/* + * Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard + * Copyright (c) by Jaroslav Kysela , + * Thomas Sailer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t ensoniq_t + +#ifndef CHIP1371 +#undef CHIP1370 +#define CHIP1370 +#endif + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela , Thomas Sailer "); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#ifdef CHIP1370 +MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370"); +MODULE_DEVICES("{{Ensoniq,AudioPCI-97 ES1370}," + "{Creative Labs,SB PCI64/128 (ES1370)}}"); +#endif +#ifdef CHIP1371 +MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+"); +MODULE_DEVICES("{{Ensoniq,AudioPCI ES1371/73}," + "{Ensoniq,AudioPCI ES1373}," + "{Creative Labs,Ectiva EV1938}," + "{Creative Labs,SB PCI64/128 (ES1371/73)}," + "{Creative Labs,Vibra PCI128}," + "{Ectiva,EV1938}}"); +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Ensoniq AudioPCI soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Ensoniq AudioPCI soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Ensoniq AudioPCI soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); + +#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880 +#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 +#endif +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 +#endif + +/* ES1371 chip ID */ +/* This is a little confusing because all ES1371 compatible chips have the + same DEVICE_ID, the only thing differentiating them is the REV_ID field. + This is only significant if you want to enable features on the later parts. + Yes, I know it's stupid and why didn't we use the sub IDs? +*/ +#define ES1371REV_ES1373_A 0x04 +#define ES1371REV_ES1373_B 0x06 +#define ES1371REV_CT5880_A 0x07 +#define CT5880REV_CT5880_C 0x02 +#define CT5880REV_CT5880_D 0x03 /* ??? -jk */ +#define CT5880REV_CT5880_E 0x04 /* mw */ +#define ES1371REV_ES1371_B 0x09 +#define EV1938REV_EV1938_A 0x00 +#define ES1371REV_ES1373_8 0x08 + +/* + * Direct registers + */ + +#define ES_REG(ensoniq, x) ((ensoniq)->port + ES_REG_##x) + +#define ES_REG_CONTROL 0x00 /* R/W: Interrupt/Chip select control register */ +#define ES_1370_ADC_STOP (1<<31) /* disable capture buffer transfers */ +#define ES_1370_XCTL1 (1<<30) /* general purpose output bit */ +#define ES_1371_SPDIF_EN (1<<26) /* SPDIF enable */ +#define ES_1371_JOY_ASEL(o) (((o)&0x03)<<24) /* joystick port mapping */ +#define ES_1371_JOY_ASELM (0x03<<24) /* mask for above */ +#define ES_1371_JOY_ASELI(i) (((i)>>24)&0x03) +#define ES_1371_GPIO_IN(i) (((i)>>20)&0x0f) /* GPIO in [3:0] pins - R/O */ +#define ES_1370_PCLKDIVO(o) (((o)&0x1fff)<<16) /* clock divide ratio for DAC2 */ +#define ES_1370_PCLKDIVM ((0x1fff)<<16) /* mask for above */ +#define ES_1370_PCLKDIVI(i) (((i)>>16)&0x1fff) /* clock divide ratio for DAC2 */ +#define ES_1371_GPIO_OUT(o) (((o)&0x0f)<<16) /* GPIO out [3:0] pins - W/R */ +#define ES_1371_GPIO_OUTM (0x0f<<16) /* mask for above */ +#define ES_MSFMTSEL (1<<15) /* MPEG serial data format; 0 = SONY, 1 = I2S */ +#define ES_1370_M_SBB (1<<14) /* clock source for DAC - 0 = clock generator; 1 = MPEG clocks */ +#define ES_1371_SYNC_RES (1<<14) /* Warm AC97 reset */ +#define ES_1370_WTSRSEL(o) (((o)&0x03)<<12) /* fixed frequency clock for DAC1 */ +#define ES_1370_WTSRSELM (0x03<<12) /* mask for above */ +#define ES_1371_ADC_STOP (1<<13) /* disable CCB transfer capture information */ +#define ES_1371_PWR_INTRM (1<<12) /* power level change interrupts enable */ +#define ES_1370_DAC_SYNC (1<<11) /* DAC's are synchronous */ +#define ES_1371_M_CB (1<<11) /* capture clock source; 0 = ADC; 1 = I2S */ +#define ES_CCB_INTRM (1<<10) /* CCB voice interrupts enable */ +#define ES_1370_M_CB (1<<9) /* capture clock source; 0 = ADC; 1 = MPEG */ +#define ES_1370_XCTL0 (1<<8) /* generap purpose output bit */ +#define ES_1371_PDLEV(o) (((o)&0x03)<<8) /* current power down level */ +#define ES_1371_PDLEVM (0x03<<8) /* mask for above */ +#define ES_BREQ (1<<7) /* memory bus request enable */ +#define ES_DAC1_EN (1<<6) /* DAC1 playback channel enable */ +#define ES_DAC2_EN (1<<5) /* DAC2 playback channel enable */ +#define ES_ADC_EN (1<<4) /* ADC capture channel enable */ +#define ES_UART_EN (1<<3) /* UART enable */ +#define ES_JYSTK_EN (1<<2) /* Joystick module enable */ +#define ES_1370_CDC_EN (1<<1) /* Codec interface enable */ +#define ES_1371_XTALCKDIS (1<<1) /* Xtal clock disable */ +#define ES_1370_SERR_DISABLE (1<<0) /* PCI serr signal disable */ +#define ES_1371_PCICLKDIS (1<<0) /* PCI clock disable */ +#define ES_REG_STATUS 0x04 /* R/O: Interrupt/Chip select status register */ +#define ES_INTR (1<<31) /* Interrupt is pending */ +#define ES_1371_ST_AC97_RST (1<<29) /* CT5880 AC'97 Reset bit */ +#define ES_1371_ST_SPDIF_EN (1<<18) /* SPDIF enable */ +#define ES_1371_ST_SPDIF_TEST (1<<17) /* SPDIF test */ +#define ES_1371_TEST (1<<16) /* test ASIC */ +#define ES_1370_CSTAT (1<<10) /* CODEC is busy or register write in progress */ +#define ES_1370_CBUSY (1<<9) /* CODEC is busy */ +#define ES_1370_CWRIP (1<<8) /* CODEC register write in progress */ +#define ES_1371_SYNC_ERR (1<<8) /* CODEC synchronization error occured */ +#define ES_1371_VC(i) (((i)>>6)&0x03) /* voice code from CCB module */ +#define ES_1370_VC(i) (((i)>>5)&0x03) /* voice code from CCB module */ +#define ES_1371_MPWR (1<<5) /* power level interrupt pending */ +#define ES_MCCB (1<<4) /* CCB interrupt pending */ +#define ES_UART (1<<3) /* UART interrupt pending */ +#define ES_DAC1 (1<<2) /* DAC1 channel interrupt pending */ +#define ES_DAC2 (1<<1) /* DAC2 channel interrupt pending */ +#define ES_ADC (1<<0) /* ADC channel interrupt pending */ +#define ES_REG_UART_DATA 0x08 /* R/W: UART data register */ +#define ES_REG_UART_STATUS 0x09 /* R/O: UART status register */ +#define ES_RXINT (1<<7) /* RX interrupt occured */ +#define ES_TXINT (1<<2) /* TX interrupt occured */ +#define ES_TXRDY (1<<1) /* transmitter ready */ +#define ES_RXRDY (1<<0) /* receiver ready */ +#define ES_REG_UART_CONTROL 0x09 /* W/O: UART control register */ +#define ES_RXINTEN (1<<7) /* RX interrupt enable */ +#define ES_TXINTENO(o) (((o)&0x03)<<5) /* TX interrupt enable */ +#define ES_TXINTENM (0x03<<5) /* mask for above */ +#define ES_TXINTENI(i) (((i)>>5)&0x03) +#define ES_CNTRL(o) (((o)&0x03)<<0) /* control */ +#define ES_CNTRLM (0x03<<0) /* mask for above */ +#define ES_REG_UART_RES 0x0a /* R/W: UART reserver register */ +#define ES_TEST_MODE (1<<0) /* test mode enabled */ +#define ES_REG_MEM_PAGE 0x0c /* R/W: Memory page register */ +#define ES_MEM_PAGEO(o) (((o)&0x0f)<<0) /* memory page select - out */ +#define ES_MEM_PAGEM (0x0f<<0) /* mask for above */ +#define ES_MEM_PAGEI(i) (((i)>>0)&0x0f) /* memory page select - in */ +#define ES_REG_1370_CODEC 0x10 /* W/O: Codec write register address */ +#define ES_1370_CODEC_WRITE(a,d) ((((a)&0xff)<<8)|(((d)&0xff)<<0)) +#define ES_REG_1371_CODEC 0x14 /* W/R: Codec Read/Write register address */ +#define ES_1371_CODEC_RDY (1<<31) /* codec ready */ +#define ES_1371_CODEC_WIP (1<<30) /* codec register access in progress */ +#define ES_1371_CODEC_PIRD (1<<23) /* codec read/write select register */ +#define ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0)) +#define ES_1371_CODEC_READS(a) ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD) +#define ES_1371_CODEC_READ(i) (((i)>>0)&0xffff) + +#define ES_REG_1371_SMPRATE 0x10 /* W/R: Codec rate converter interface register */ +#define ES_1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25) /* address of the sample rate converter */ +#define ES_1371_SRC_RAM_ADDRM (0x7f<<25) /* mask for above */ +#define ES_1371_SRC_RAM_ADDRI(i) (((i)>>25)&0x7f) /* address of the sample rate converter */ +#define ES_1371_SRC_RAM_WE (1<<24) /* R/W: read/write control for sample rate converter */ +#define ES_1371_SRC_RAM_BUSY (1<<23) /* R/O: sample rate memory is busy */ +#define ES_1371_SRC_DISABLE (1<<22) /* sample rate converter disable */ +#define ES_1371_DIS_P1 (1<<21) /* playback channel 1 accumulator update disable */ +#define ES_1371_DIS_P2 (1<<20) /* playback channel 1 accumulator update disable */ +#define ES_1371_DIS_R1 (1<<19) /* capture channel accumulator update disable */ +#define ES_1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0) /* current value of the sample rate converter */ +#define ES_1371_SRC_RAM_DATAM (0xffff<<0) /* mask for above */ +#define ES_1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff) /* current value of the sample rate converter */ + +#define ES_REG_1371_LEGACY 0x18 /* W/R: Legacy control/status register */ +#define ES_1371_JFAST (1<<31) /* fast joystick timing */ +#define ES_1371_HIB (1<<30) /* host interrupt blocking enable */ +#define ES_1371_VSB (1<<29) /* SB; 0 = addr 0x220xH, 1 = 0x22FxH */ +#define ES_1371_VMPUO(o) (((o)&0x03)<<27) /* base register address; 0 = 0x320xH; 1 = 0x330xH; 2 = 0x340xH; 3 = 0x350xH */ +#define ES_1371_VMPUM (0x03<<27) /* mask for above */ +#define ES_1371_VMPUI(i) (((i)>>27)&0x03) /* base register address */ +#define ES_1371_VCDCO(o) (((o)&0x03)<<25) /* CODEC; 0 = 0x530xH; 1 = undefined; 2 = 0xe80xH; 3 = 0xF40xH */ +#define ES_1371_VCDCM (0x03<<25) /* mask for above */ +#define ES_1371_VCDCI(i) (((i)>>25)&0x03) /* CODEC address */ +#define ES_1371_FIRQ (1<<24) /* force an interrupt */ +#define ES_1371_SDMACAP (1<<23) /* enable event capture for slave DMA controller */ +#define ES_1371_SPICAP (1<<22) /* enable event capture for slave IRQ controller */ +#define ES_1371_MDMACAP (1<<21) /* enable event capture for master DMA controller */ +#define ES_1371_MPICAP (1<<20) /* enable event capture for master IRQ controller */ +#define ES_1371_ADCAP (1<<19) /* enable event capture for ADLIB register; 0x388xH */ +#define ES_1371_SVCAP (1<<18) /* enable event capture for SB registers */ +#define ES_1371_CDCCAP (1<<17) /* enable event capture for CODEC registers */ +#define ES_1371_BACAP (1<<16) /* enable event capture for SoundScape base address */ +#define ES_1371_EXI(i) (((i)>>8)&0x07) /* event number */ +#define ES_1371_AI(i) (((i)>>3)&0x1f) /* event significant I/O address */ +#define ES_1371_WR (1<<2) /* event capture; 0 = read; 1 = write */ +#define ES_1371_LEGINT (1<<0) /* interrupt for legacy events; 0 = interrupt did occur */ + +#define ES_REG_SERIAL 0x20 /* R/W: Serial interface control register */ +#define ES_1371_DAC_TEST (1<<22) /* DAC test mode enable */ +#define ES_P2_END_INCO(o) (((o)&0x07)<<19) /* binary offset value to increment / loop end */ +#define ES_P2_END_INCM (0x07<<19) /* mask for above */ +#define ES_P2_END_INCI(i) (((i)>>16)&0x07) /* binary offset value to increment / loop end */ +#define ES_P2_ST_INCO(o) (((o)&0x07)<<16) /* binary offset value to increment / start */ +#define ES_P2_ST_INCM (0x07<<16) /* mask for above */ +#define ES_P2_ST_INCI(i) (((i)<<16)&0x07) /* binary offset value to increment / start */ +#define ES_R1_LOOP_SEL (1<<15) /* ADC; 0 - loop mode; 1 = stop mode */ +#define ES_P2_LOOP_SEL (1<<14) /* DAC2; 0 - loop mode; 1 = stop mode */ +#define ES_P1_LOOP_SEL (1<<13) /* DAC1; 0 - loop mode; 1 = stop mode */ +#define ES_P2_PAUSE (1<<12) /* DAC2; 0 - play mode; 1 = pause mode */ +#define ES_P1_PAUSE (1<<11) /* DAC1; 0 - play mode; 1 = pause mode */ +#define ES_R1_INT_EN (1<<10) /* ADC interrupt enable */ +#define ES_P2_INT_EN (1<<9) /* DAC2 interrupt enable */ +#define ES_P1_INT_EN (1<<8) /* DAC1 interrupt enable */ +#define ES_P1_SCT_RLD (1<<7) /* force sample counter reload for DAC1 */ +#define ES_P2_DAC_SEN (1<<6) /* when stop mode: 0 - DAC2 play back zeros; 1 = DAC2 play back last sample */ +#define ES_R1_MODEO(o) (((o)&0x03)<<4) /* ADC mode; 0 = 8-bit mono; 1 = 8-bit stereo; 2 = 16-bit mono; 3 = 16-bit stereo */ +#define ES_R1_MODEM (0x03<<4) /* mask for above */ +#define ES_R1_MODEI(i) (((i)>>4)&0x03) +#define ES_P2_MODEO(o) (((o)&0x03)<<2) /* DAC2 mode; -- '' -- */ +#define ES_P2_MODEM (0x03<<2) /* mask for above */ +#define ES_P2_MODEI(i) (((i)>>2)&0x03) +#define ES_P1_MODEO(o) (((o)&0x03)<<0) /* DAC1 mode; -- '' -- */ +#define ES_P1_MODEM (0x03<<0) /* mask for above */ +#define ES_P1_MODEI(i) (((i)>>0)&0x03) + +#define ES_REG_DAC1_COUNT 0x24 /* R/W: DAC1 sample count register */ +#define ES_REG_DAC2_COUNT 0x28 /* R/W: DAC2 sample count register */ +#define ES_REG_ADC_COUNT 0x2c /* R/W: ADC sample count register */ +#define ES_REG_CURR_COUNT(i) (((i)>>16)&0xffff) +#define ES_REG_COUNTO(o) (((o)&0xffff)<<0) +#define ES_REG_COUNTM (0xffff<<0) +#define ES_REG_COUNTI(i) (((i)>>0)&0xffff) + +#define ES_REG_DAC1_FRAME 0x30 /* R/W: PAGE 0x0c; DAC1 frame address */ +#define ES_REG_DAC1_SIZE 0x34 /* R/W: PAGE 0x0c; DAC1 frame size */ +#define ES_REG_DAC2_FRAME 0x38 /* R/W: PAGE 0x0c; DAC2 frame address */ +#define ES_REG_DAC2_SIZE 0x3c /* R/W: PAGE 0x0c; DAC2 frame size */ +#define ES_REG_ADC_FRAME 0x30 /* R/W: PAGE 0x0d; ADC frame address */ +#define ES_REG_ADC_SIZE 0x34 /* R/W: PAGE 0x0d; ADC frame size */ +#define ES_REG_FCURR_COUNTO(o) (((o)&0xffff)<<16) +#define ES_REG_FCURR_COUNTM (0xffff<<16) +#define ES_REG_FCURR_COUNTI(i) (((i)>>14)&0x3fffc) +#define ES_REG_FSIZEO(o) (((o)&0xffff)<<0) +#define ES_REG_FSIZEM (0xffff<<0) +#define ES_REG_FSIZEI(i) (((i)>>0)&0xffff) +#define ES_REG_PHANTOM_FRAME 0x38 /* R/W: PAGE 0x0d: phantom frame address */ +#define ES_REG_PHANTOM_COUNT 0x3c /* R/W: PAGE 0x0d: phantom frame count */ + +#define ES_REG_UART_FIFO 0x30 /* R/W: PAGE 0x0e; UART FIFO register */ +#define ES_REG_UF_VALID (1<<8) +#define ES_REG_UF_BYTEO(o) (((o)&0xff)<<0) +#define ES_REG_UF_BYTEM (0xff<<0) +#define ES_REG_UF_BYTEI(i) (((i)>>0)&0xff) + + +/* + * Pages + */ + +#define ES_PAGE_DAC 0x0c +#define ES_PAGE_ADC 0x0d +#define ES_PAGE_UART 0x0e +#define ES_PAGE_UART1 0x0f + +/* + * Sample rate converter addresses + */ + +#define ES_SMPREG_DAC1 0x70 +#define ES_SMPREG_DAC2 0x74 +#define ES_SMPREG_ADC 0x78 +#define ES_SMPREG_VOL_ADC 0x6c +#define ES_SMPREG_VOL_DAC1 0x7c +#define ES_SMPREG_VOL_DAC2 0x7e +#define ES_SMPREG_TRUNC_N 0x00 +#define ES_SMPREG_INT_REGS 0x01 +#define ES_SMPREG_ACCUM_FRAC 0x02 +#define ES_SMPREG_VFREQ_FRAC 0x03 + +/* + * Some contants + */ + +#define ES_1370_SRCLOCK 1411200 +#define ES_1370_SRTODIV(x) (ES_1370_SRCLOCK/(x)-2) + +/* + * Open modes + */ + +#define ES_MODE_PLAY1 0x0001 +#define ES_MODE_PLAY2 0x0002 +#define ES_MODE_CAPTURE 0x0004 + +#define ES_MODE_OUTPUT 0x0001 /* for MIDI */ +#define ES_MODE_INPUT 0x0002 /* for MIDI */ + +/* + + */ + +typedef struct _snd_ensoniq ensoniq_t; + +struct _snd_ensoniq { + spinlock_t reg_lock; + + int irq; + + unsigned long playback1size; + unsigned long playback2size; + unsigned long capture3size; + + unsigned long port; + struct resource *res_port; + unsigned int mode; + unsigned int uartm; /* UART mode */ + + unsigned int ctrl; /* control register */ + unsigned int sctrl; /* serial control register */ + unsigned int cssr; /* control status register */ + unsigned int uartc; /* uart control register */ + unsigned int rev; /* chip revision */ + + union { + struct { + ac97_t *ac97; + } es1371; + struct { + int pclkdiv_lock; + ak4531_t *ak4531; + } es1370; + } u; + + struct pci_dev *pci; + unsigned short subsystem_vendor_id; + unsigned short subsystem_device_id; + snd_card_t *card; + snd_pcm_t *pcm1; /* DAC1/ADC PCM */ + snd_pcm_t *pcm2; /* DAC2 PCM */ + snd_pcm_substream_t *playback1_substream; + snd_pcm_substream_t *playback2_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p1_dma_size; + unsigned int p2_dma_size; + unsigned int c_dma_size; + unsigned int p1_period_size; + unsigned int p2_period_size; + unsigned int c_period_size; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_input; + snd_rawmidi_substream_t *midi_output; + + snd_info_entry_t *proc_entry; + +#ifdef CHIP1370 + unsigned char *bugbuf; + dma_addr_t bugbuf_addr; +#endif +}; + +static void snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct pci_device_id snd_audiopci_ids[] __devinitdata = { +#ifdef CHIP1370 + { 0x1274, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1370 */ +#endif +#ifdef CHIP1371 + { 0x1274, 0x1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1371 */ + { 0x1274, 0x5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1373 - CT5880 */ + { 0x1102, 0x8938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ectiva EV1938 */ +#endif + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_audiopci_ids); + +/* + * constants + */ + +#define POLL_COUNT 0xa000 + +#ifdef CHIP1370 +static unsigned int snd_es1370_fixed_rates[] = + {5512, 11025, 22050, 44100}; +static snd_pcm_hw_constraint_list_t snd_es1370_hw_constraints_rates = { + count: 4, + list: snd_es1370_fixed_rates, + mask: 0, +}; +static ratnum_t es1370_clock = { + num: ES_1370_SRCLOCK, + den_min: 29, + den_max: 353, + den_step: 1, +}; +static snd_pcm_hw_constraint_ratnums_t snd_es1370_hw_constraints_clock = { + nrats: 1, + rats: &es1370_clock, +}; +#else +static ratden_t es1371_dac_clock = { + num_min: 3000 * (1 << 15), + num_max: 48000 * (1 << 15), + num_step: 3000, + den: 1 << 15, +}; +static snd_pcm_hw_constraint_ratdens_t snd_es1371_hw_constraints_dac_clock = { + nrats: 1, + rats: &es1371_dac_clock, +}; +static ratnum_t es1371_adc_clock = { + num: 48000 << 15, + den_min: 32768, + den_max: 393216, + den_step: 1, +}; +static snd_pcm_hw_constraint_ratnums_t snd_es1371_hw_constraints_adc_clock = { + nrats: 1, + rats: &es1371_adc_clock, +}; +#endif +static const unsigned int snd_ensoniq_sample_shift[] = + {0, 1, 1, 2}; + +/* + * common I/O routines + */ + +#ifdef CHIP1371 + +static unsigned int snd_es1371_wait_src_ready(ensoniq_t * ensoniq) +{ + unsigned int t, r = 0; + + for (t = 0; t < POLL_COUNT; t++) { + r = inl(ES_REG(ensoniq, 1371_SMPRATE)); + if ((r & ES_1371_SRC_RAM_BUSY) == 0) + return r; + } + snd_printk("wait source ready timeout 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_SMPRATE), r); + return 0; +} + +static unsigned int snd_es1371_src_read(ensoniq_t * ensoniq, unsigned short reg) +{ + unsigned int temp, i, orig, r; + + /* wait for ready */ + temp = orig = snd_es1371_wait_src_ready(ensoniq); + + /* expose the SRC state bits */ + r = temp & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg) | 0x10000; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + + /* now, wait for busy and the correct time to read */ + temp = snd_es1371_wait_src_ready(ensoniq); + + if ((temp & 0x00870000) != 0x00010000) { + /* wait for the right state */ + for (i = 0; i < POLL_COUNT; i++) { + temp = inl(ES_REG(ensoniq, 1371_SMPRATE)); + if ((temp & 0x00870000) == 0x00010000) + break; + } + } + + /* hide the state bits */ + r = orig & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + + return temp; +} + +static void snd_es1371_src_write(ensoniq_t * ensoniq, + unsigned short reg, unsigned short data) +{ + unsigned int r; + + r = snd_es1371_wait_src_ready(ensoniq) & + (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg) | ES_1371_SRC_RAM_DATAO(data); + outl(r | ES_1371_SRC_RAM_WE, ES_REG(ensoniq, 1371_SMPRATE)); +} + +#endif /* CHIP1371 */ + +#ifdef CHIP1370 + +static void snd_es1370_codec_write(ak4531_t *ak4531, + unsigned short reg, unsigned short val) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return); + unsigned long flags; + signed long end_time = jiffies + HZ / 10; + +#if 0 + printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n", reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC)); +#endif + do { + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) { + outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return; + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +#if 0 + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); +#endif + } while ((signed long)(end_time - jiffies) > 0); + snd_printk("codec write timeout, status = 0x%x\n", inl(ES_REG(ensoniq, STATUS))); +} + +#endif /* CHIP1370 */ + +#ifdef CHIP1371 + +static void snd_es1371_codec_write(ac97_t *ac97, + unsigned short reg, unsigned short val) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return); + unsigned long flags; + unsigned int t, x; + + for (t = 0; t < POLL_COUNT; t++) { + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) { + /* save the current state for latter */ + x = snd_es1371_wait_src_ready(ensoniq); + outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000, + ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for not busy (state 0) first to avoid + transition states */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000) + break; + } + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00010000) + break; + } + outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC)); + /* restore SRC reg */ + snd_es1371_wait_src_ready(ensoniq); + outl(x, ES_REG(ensoniq, 1371_SMPRATE)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return; + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + } + snd_printk("codec write timeout at 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC))); +} + +static unsigned short snd_es1371_codec_read(ac97_t *ac97, + unsigned short reg) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return -ENXIO); + unsigned long flags; + unsigned int t, x, fail = 0; + + __again: + for (t = 0; t < POLL_COUNT; t++) { + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) { + /* save the current state for latter */ + x = snd_es1371_wait_src_ready(ensoniq); + outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000, + ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for not busy (state 0) first to avoid + transition states */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000) + break; + } + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00010000) + break; + } + outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC)); + /* restore SRC reg */ + snd_es1371_wait_src_ready(ensoniq); + outl(x, ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for WIP again */ + for (t = 0; t < POLL_COUNT; t++) { + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) + break; + } + /* now wait for the stinkin' data (RDY) */ + for (t = 0; t < POLL_COUNT; t++) { + if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) { + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return ES_1371_CODEC_READ(x); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + if (++fail > 10) { + snd_printk("codec read timeout (final) at 0x%lx, reg = 0x%x [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), reg, inl(ES_REG(ensoniq, 1371_CODEC))); + return 0; + } + goto __again; + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + } + snd_printk("es1371: codec read timeout at 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC))); + return 0; +} + +static void snd_es1371_adc_rate(ensoniq_t * ensoniq, unsigned int rate) +{ + unsigned int n, truncm, freq, result; + + n = rate / 3000; + if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) + n--; + truncm = (21 * n - 1) | 1; + freq = ((48000UL << 15) / rate) * n; + result = (48000UL << 15) / (freq / n); + if (rate >= 24000) { + if (truncm > 239) + truncm = 239; + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, + (((239 - truncm) >> 1) << 9) | (n << 4)); + } else { + if (truncm > 119) + truncm = 119; + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, + 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); + } + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8); +} + +static void snd_es1371_dac1_rate(ensoniq_t * ensoniq, unsigned int rate) +{ + unsigned int freq, r; + + freq = ((rate << 15) + 1500) / 3000; + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1)) | ES_1371_DIS_P1; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1)); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); +} + +static void snd_es1371_dac2_rate(ensoniq_t * ensoniq, unsigned int rate) +{ + unsigned int freq, r; + + freq = ((rate << 15) + 1500) / 3000; + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1)) | ES_1371_DIS_P2; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1)); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); +} + +#endif /* CHIP1371 */ + +static int snd_ensoniq_trigger(snd_pcm_substream_t *substream, int cmd) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == ensoniq->playback1_substream) { + what |= ES_P1_PAUSE; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->playback2_substream) { + what |= ES_P2_PAUSE; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->capture_substream) + return -EINVAL; + s = s->link_next; + } while (s != substream); + spin_lock(&ensoniq->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) + ensoniq->sctrl |= what; + else + ensoniq->sctrl &= ~what; + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + spin_unlock(&ensoniq->reg_lock); + break; + } + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == ensoniq->playback1_substream) { + what |= ES_DAC1_EN; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->playback2_substream) { + what |= ES_DAC2_EN; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->capture_substream) { + what |= ES_ADC_EN; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&ensoniq->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) + ensoniq->ctrl |= what; + else + ensoniq->ctrl &= ~what; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock(&ensoniq->reg_lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +/* + * PCM part + */ + +static int snd_ensoniq_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ensoniq_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ensoniq_playback1_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->p1_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->p1_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl &= ~ES_DAC1_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME)); + outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE)); + ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM); + ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC1_COUNT)); +#ifdef CHIP1370 + ensoniq->ctrl &= ~ES_1370_WTSRSELM; + switch (runtime->rate) { + case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break; + case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break; + case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break; + case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break; + default: snd_BUG(); + } +#else + snd_es1371_dac1_rate(ensoniq, runtime->rate); +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_playback2_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->p2_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->p2_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl &= ~ES_DAC2_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME)); + outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE)); + ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN | + ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM); + ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) | + ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC2_COUNT)); +#ifdef CHIP1370 + if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) { + ensoniq->ctrl &= ~ES_1370_PCLKDIVM; + ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate)); + ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2; + } +#else + snd_es1371_dac2_rate(ensoniq, runtime->rate); +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->c_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->c_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl &= ~ES_ADC_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME)); + outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE)); + ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM); + ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, ADC_COUNT)); +#ifdef CHIP1370 + if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) { + ensoniq->ctrl &= ~ES_1370_PCLKDIVM; + ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate)); + ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE; + } +#else + snd_es1371_adc_rate(ensoniq, runtime->rate); +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_ensoniq_playback1_pointer(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC1_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC1_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC2_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC2_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_uframes_t snd_ensoniq_capture_pointer(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_ADC_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, ADC_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_hardware_t snd_ensoniq_playback1 = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: +#ifndef CHIP1370 + SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, +#else + (SNDRV_PCM_RATE_KNOT | /* 5512Hz rate */ + SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_44100), +#endif + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ensoniq_playback2 = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ensoniq_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_ensoniq_playback1_open(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_PLAY1; + ensoniq->playback1_substream = substream; + runtime->hw = snd_ensoniq_playback1; + snd_pcm_set_sync(substream); +#ifdef CHIP1370 + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_rates); +#else + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_dac_clock); +#endif + return 0; +} + +static int snd_ensoniq_playback2_open(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_PLAY2; + ensoniq->playback2_substream = substream; + runtime->hw = snd_ensoniq_playback2; + snd_pcm_set_sync(substream); +#ifdef CHIP1370 + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_clock); +#else + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_dac_clock); +#endif + return 0; +} + +static int snd_ensoniq_capture_open(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_CAPTURE; + ensoniq->capture_substream = substream; + runtime->hw = snd_ensoniq_capture; + snd_pcm_set_sync(substream); +#ifdef CHIP1370 + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_clock); +#else + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_adc_clock); +#endif + return 0; +} + +static int snd_ensoniq_playback1_close(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->playback1_substream = NULL; + ensoniq->mode &= ~ES_MODE_PLAY1; + return 0; +} + +static int snd_ensoniq_playback2_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->playback2_substream = NULL; + spin_lock_irqsave(&ensoniq->reg_lock, flags); +#ifdef CHIP1370 + ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2; +#endif + ensoniq->mode &= ~ES_MODE_PLAY2; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_capture_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->capture_substream = NULL; + spin_lock_irqsave(&ensoniq->reg_lock, flags); +#ifdef CHIP1370 + ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE; +#endif + ensoniq->mode &= ~ES_MODE_CAPTURE; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static snd_pcm_ops_t snd_ensoniq_playback1_ops = { + open: snd_ensoniq_playback1_open, + close: snd_ensoniq_playback1_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ensoniq_hw_params, + hw_free: snd_ensoniq_hw_free, + prepare: snd_ensoniq_playback1_prepare, + trigger: snd_ensoniq_trigger, + pointer: snd_ensoniq_playback1_pointer, +}; + +static snd_pcm_ops_t snd_ensoniq_playback2_ops = { + open: snd_ensoniq_playback2_open, + close: snd_ensoniq_playback2_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ensoniq_hw_params, + hw_free: snd_ensoniq_hw_free, + prepare: snd_ensoniq_playback2_prepare, + trigger: snd_ensoniq_trigger, + pointer: snd_ensoniq_playback2_pointer, +}; + +static snd_pcm_ops_t snd_ensoniq_capture_ops = { + open: snd_ensoniq_capture_open, + close: snd_ensoniq_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ensoniq_hw_params, + hw_free: snd_ensoniq_hw_free, + prepare: snd_ensoniq_capture_prepare, + trigger: snd_ensoniq_trigger, + pointer: snd_ensoniq_capture_pointer, +}; + +static void snd_ensoniq_pcm_free(snd_pcm_t *pcm) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, pcm->private_data, return); + ensoniq->pcm1 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ensoniq_pcm(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; +#ifdef CHIP1370 + err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm); +#else + err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm); +#endif + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ensoniq_capture_ops); + + pcm->private_data = ensoniq; + pcm->private_free = snd_ensoniq_pcm_free; + pcm->info_flags = 0; +#ifdef CHIP1370 + strcpy(pcm->name, "ES1370 DAC2/ADC"); +#else + strcpy(pcm->name, "ES1371 DAC2/ADC"); +#endif + ensoniq->pcm1 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static void snd_ensoniq_pcm_free2(snd_pcm_t *pcm) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, pcm->private_data, return); + ensoniq->pcm2 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; +#ifdef CHIP1370 + err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm); +#else + err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm); +#endif + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops); + + pcm->private_data = ensoniq; + pcm->private_free = snd_ensoniq_pcm_free2; + pcm->info_flags = 0; +#ifdef CHIP1370 + strcpy(pcm->name, "ES1370 DAC1"); +#else + strcpy(pcm->name, "ES1371 DAC1"); +#endif + ensoniq->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer section + */ + +#ifdef CHIP1371 + +#define ES1371_SPDIF(xname) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_es1371_spdif_info, \ + get: snd_es1371_spdif_get, put: snd_es1371_spdif_put } + +static int snd_es1371_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1371_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1371_SPDIF_EN ? 1 : 0; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_es1371_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int nval1, nval2; + int change; + + nval1 = ucontrol->value.integer.value[0] ? ES_1371_SPDIF_EN : 0; + nval2 = ucontrol->value.integer.value[0] ? ES_1371_ST_SPDIF_EN : 0; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + change = (ensoniq->ctrl & ES_1371_SPDIF_EN) != nval1; + ensoniq->ctrl &= ~ES_1371_SPDIF_EN; + ensoniq->ctrl |= nval1; + ensoniq->cssr &= ~ES_1371_ST_SPDIF_EN; + ensoniq->cssr |= nval2; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_es1371_mixer_spdif __devinitdata = +ES1371_SPDIF("IEC958 Playback Switch"); + +static void snd_ensoniq_mixer_free_ac97(ac97_t *ac97) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return); + ensoniq->u.es1371.ac97 = NULL; +} + +static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq) +{ + snd_card_t *card = ensoniq->card; + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_es1371_codec_write; + ac97.read = snd_es1371_codec_read; + ac97.private_data = ensoniq; + ac97.private_free = snd_ensoniq_mixer_free_ac97; + if ((err = snd_ac97_mixer(card, &ac97, &ensoniq->u.es1371.ac97)) < 0) + return err; + if (ensoniq->rev >= ES1371REV_ES1373_A) + snd_ctl_add(card, snd_ctl_new1(&snd_es1371_mixer_spdif, ensoniq)); + return 0; +} + +#endif /* CHIP1371 */ + +#define ENSONIQ_CONTROL(xname, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_CARD, name: xname, info: snd_ensoniq_control_info, \ + get: snd_ensoniq_control_get, put: snd_ensoniq_control_put, \ + private_value: mask } + +static int snd_ensoniq_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ensoniq_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int mask = kcontrol->private_value; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ucontrol->value.integer.value[0] = ensoniq->ctrl & mask ? 1 : 0; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int mask = kcontrol->private_value; + unsigned int nval; + int change; + + nval = ucontrol->value.integer.value[0] ? mask : 0; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + change = (ensoniq->ctrl & mask) != nval; + ensoniq->ctrl &= ~mask; + ensoniq->ctrl |= nval; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return change; +} + +#ifdef CHIP1370 + +#define ES1370_CONTROLS 2 + +static snd_kcontrol_new_t snd_es1370_controls[2] __devinitdata = { +ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0), +ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1) +}; + +static void snd_ensoniq_mixer_free_ak4531(ak4531_t *ak4531) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return); + ensoniq->u.es1370.ak4531 = NULL; +} + +static int __devinit snd_ensoniq_1370_mixer(ensoniq_t * ensoniq) +{ + snd_card_t *card = ensoniq->card; + ak4531_t ak4531; + int err, idx; + + /* try reset AK4531 */ + outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC)); + udelay(100); + outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC)); + udelay(100); + + memset(&ak4531, 0, sizeof(ak4531)); + ak4531.write = snd_es1370_codec_write; + ak4531.private_data = ensoniq; + ak4531.private_free = snd_ensoniq_mixer_free_ak4531; + if ((err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531)) < 0) + return err; + for (idx = 0; idx < ES1370_CONTROLS; idx++) + snd_ctl_add(card, snd_ctl_new1(&snd_es1370_controls[idx], ensoniq)); + return 0; +} + +#endif /* CHIP1370 */ + +/* + * General Switches... + */ + +static snd_kcontrol_new_t snd_ensoniq_control_joystick __devinitdata = +ENSONIQ_CONTROL("Joystick Enable", ES_JYSTK_EN); + +#ifdef CHIP1371 + +#define ES1371_JOYSTICK_ADDR(xname) \ +{ iface: SNDRV_CTL_ELEM_IFACE_CARD, name: xname, info: snd_es1371_joystick_addr_info, \ + get: snd_es1371_joystick_addr_get, put: snd_es1371_joystick_addr_put } + +static int snd_es1371_joystick_addr_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item >= 3) + uinfo->value.enumerated.item = 2; + sprintf(uinfo->value.enumerated.name, "port 0x%x", (uinfo->value.enumerated.item * 8) + 0x200); + return 0; +} + +static int snd_es1371_joystick_addr_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ucontrol->value.enumerated.item[0] = ES_1371_JOY_ASELI(ensoniq->ctrl); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_es1371_joystick_addr_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int nval; + int change; + + nval = ES_1371_JOY_ASEL(ucontrol->value.integer.value[0]); + spin_lock_irqsave(&ensoniq->reg_lock, flags); + change = (ensoniq->ctrl & ES_1371_JOY_ASELM) != nval; + ensoniq->ctrl &= ~ES_1371_JOY_ASELM; + ensoniq->ctrl |= nval; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_es1371_joystick_addr __devinitdata = +ES1371_JOYSTICK_ADDR("Joystick Address"); + +#endif /* CHIP1371 */ + +/* + + */ + +static void snd_ensoniq_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, entry->private_data, return); + +#ifdef CHIP1370 + snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n"); +#else + snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n"); +#endif + snd_iprintf(buffer, "Joystick enable : %s\n", ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off"); +#ifdef CHIP1370 + snd_iprintf(buffer, "MIC +5V bias : %s\n", ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off"); + snd_iprintf(buffer, "Line In to AOUT : %s\n", ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off"); +#else + snd_iprintf(buffer, "Joystick port : 0x%x\n", (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200); +#endif +} + +static void __devinit snd_ensoniq_proc_init(ensoniq_t * ensoniq) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(ensoniq->card, "audiopci", ensoniq->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = ensoniq; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_ensoniq_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ensoniq->proc_entry = entry; +} + +static void snd_ensoniq_proc_done(ensoniq_t * ensoniq) +{ + if (ensoniq->proc_entry) { + snd_info_unregister(ensoniq->proc_entry); + ensoniq->proc_entry = NULL; + } +} + +/* + + */ + +static int snd_ensoniq_free(ensoniq_t *ensoniq) +{ + snd_ensoniq_proc_done(ensoniq); + if (ensoniq->irq < 0) + goto __hw_end; +#ifdef CHIP1370 + outl(ES_1370_SERR_DISABLE, ES_REG(ensoniq, CONTROL)); /* switch everything off */ + outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */ +#else + outl(0, ES_REG(ensoniq, CONTROL)); /* switch everything off */ + outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */ +#endif + synchronize_irq(); + pci_set_power_state(ensoniq->pci, 3); + __hw_end: +#ifdef CHIP1370 + if (ensoniq->bugbuf) + snd_free_pci_pages(ensoniq->pci, 16, ensoniq->bugbuf, ensoniq->bugbuf_addr); +#endif + if (ensoniq->res_port) { + release_resource(ensoniq->res_port); + kfree_nocheck(ensoniq->res_port); + } + if (ensoniq->irq >= 0) + free_irq(ensoniq->irq, (void *)ensoniq); + snd_magic_kfree(ensoniq); + return 0; +} + +static int snd_ensoniq_dev_free(snd_device_t *device) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, device->device_data, return -ENXIO); + return snd_ensoniq_free(ensoniq); +} + +#ifdef CHIP1371 +static struct { + unsigned short svid; /* subsystem vendor ID */ + unsigned short sdid; /* subsystem device ID */ +} es1371_amplifier_hack[] = { + { svid: 0x107b, sdid: 0x2150 }, /* Gateway Solo 2150 */ + { svid: 0x13bd, sdid: 0x100c }, /* EV1938 on Mebius PC-MJ100V */ + { svid: 0x1102, sdid: 0x5938 }, /* Targa Xtender300 */ + { svid: 0x1102, sdid: 0x8938 }, /* IPC Topnote G notebook */ + { svid: PCI_ANY_ID, sdid: PCI_ANY_ID } +}; +static struct { + unsigned short vid; /* vendor ID */ + unsigned short did; /* device ID */ + unsigned char rev; /* revision */ +} es1371_ac97_reset_hack[] = { + { vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_C }, + { vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_D }, + { vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_E }, + { vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_ES1371, rev: ES1371REV_CT5880_A }, + { vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_ES1371, rev: ES1371REV_ES1373_8 }, + { vid: PCI_ANY_ID, did: PCI_ANY_ID, rev: 0 } +}; +#endif + +static int __devinit snd_ensoniq_create(snd_card_t * card, + struct pci_dev *pci, + ensoniq_t ** rensoniq) +{ + ensoniq_t *ensoniq; + unsigned short cmdw; + unsigned char cmdb; +#ifdef CHIP1371 + int idx; +#endif + int err; + static snd_device_ops_t ops = { + dev_free: snd_ensoniq_dev_free, + }; + + *rensoniq = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + ensoniq = snd_magic_kcalloc(ensoniq_t, 0, GFP_KERNEL); + if (ensoniq == NULL) + return -ENOMEM; + spin_lock_init(&ensoniq->reg_lock); + ensoniq->card = card; + ensoniq->pci = pci; + ensoniq->irq = -1; + ensoniq->port = pci_resource_start(pci, 0); + if ((ensoniq->res_port = request_region(ensoniq->port, 0x40, "Ensoniq AudioPCI")) == NULL) { + snd_ensoniq_free(ensoniq); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ensoniq->port, ensoniq->port + 0x40 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_audiopci_interrupt, SA_INTERRUPT|SA_SHIRQ, "Ensoniq AudioPCI", (void *)ensoniq)) { + snd_ensoniq_free(ensoniq); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + ensoniq->irq = pci->irq; +#ifdef CHIP1370 + if ((ensoniq->bugbuf = snd_malloc_pci_pages(pci, 16, &ensoniq->bugbuf_addr)) == NULL) { + snd_ensoniq_free(ensoniq); + snd_printk("unable to allocate space for phantom area - bugbuf\n"); + return -EBUSY; + } +#endif + pci_set_master(pci); + pci_read_config_byte(pci, PCI_REVISION_ID, &cmdb); + ensoniq->rev = cmdb; + pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &cmdw); + ensoniq->subsystem_vendor_id = cmdw; + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &cmdw); + ensoniq->subsystem_device_id = cmdw; + snd_ensoniq_proc_init(ensoniq); +#ifdef CHIP1370 +#if 0 + ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_SERR_DISABLE | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000)); +#else /* get microphone working */ + ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000)); +#endif + ensoniq->sctrl = 0; + /* initialize the chips */ + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + outl(ensoniq->bugbuf_addr, ES_REG(ensoniq, PHANTOM_FRAME)); + outl(0, ES_REG(ensoniq, PHANTOM_COUNT)); +#else + ensoniq->ctrl = 0; + ensoniq->sctrl = 0; + ensoniq->cssr = 0; + for (idx = 0; es1371_amplifier_hack[idx].svid != (unsigned short)PCI_ANY_ID; idx++) + if (ensoniq->subsystem_vendor_id == es1371_amplifier_hack[idx].svid && + ensoniq->subsystem_device_id == es1371_amplifier_hack[idx].sdid) { + ensoniq->ctrl |= ES_1371_GPIO_OUT(1); /* turn amplifier on */ + break; + } + /* initialize the chips */ + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl(0, ES_REG(ensoniq, 1371_LEGACY)); + for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++) + if (pci->vendor == es1371_ac97_reset_hack[idx].vid && + pci->device == es1371_ac97_reset_hack[idx].did && + ensoniq->rev == es1371_ac97_reset_hack[idx].rev) { + unsigned long tmo; + signed long tmo2; + + ensoniq->cssr |= ES_1371_ST_AC97_RST; + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + /* need to delay around 20ms(bleech) to give + some CODECs enough time to wakeup */ + tmo = jiffies + (HZ / 50) + 1; + while (1) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(tmo2); + } + break; + } + /* AC'97 warm reset to start the bitclk */ + outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL)); + udelay(20); + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + /* Init the sample rate converter */ + snd_es1371_wait_src_ready(ensoniq); + outl(ES_1371_SRC_DISABLE, ES_REG(ensoniq, 1371_SMPRATE)); + for (idx = 0; idx < 0x80; idx++) + snd_es1371_src_write(ensoniq, idx, 0); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1 + 1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2 + 1, 1 << 12); + snd_es1371_adc_rate(ensoniq, 22050); + snd_es1371_dac1_rate(ensoniq, 22050); + snd_es1371_dac2_rate(ensoniq, 22050); + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) - Thomas Sailer + */ + snd_es1371_wait_src_ready(ensoniq); + outl(0, ES_REG(ensoniq, 1371_SMPRATE)); + /* try reset codec directly */ + outl(ES_1371_CODEC_WRITE(0, 0), ES_REG(ensoniq, 1371_CODEC)); +#endif + outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL)); + outb(0x00, ES_REG(ensoniq, UART_RES)); + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + snd_ctl_add(card, snd_ctl_new1(&snd_ensoniq_control_joystick, ensoniq)); +#ifdef CHIP1371 + snd_ctl_add(card, snd_ctl_new1(&snd_es1371_joystick_addr, ensoniq)); +#endif + synchronize_irq(); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ensoniq, &ops)) < 0) { + snd_ensoniq_free(ensoniq); + return err; + } + + *rensoniq = ensoniq; + return 0; +} + +/* + * MIDI section + */ + +static void snd_ensoniq_midi_interrupt(ensoniq_t * ensoniq) +{ + snd_rawmidi_t * rmidi = ensoniq->rmidi; + unsigned char status, mask, byte; + + if (rmidi == NULL) + return; + /* do Rx at first */ + spin_lock(&ensoniq->reg_lock); + mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0; + while (mask) { + status = inb(ES_REG(ensoniq, UART_STATUS)); + if ((status & mask) == 0) + break; + byte = inb(ES_REG(ensoniq, UART_DATA)); + spin_unlock(&ensoniq->reg_lock); + snd_rawmidi_receive(ensoniq->midi_input, &byte, 1); + spin_lock(&ensoniq->reg_lock); + } + spin_unlock(&ensoniq->reg_lock); + + /* do Tx at second */ + spin_lock(&ensoniq->reg_lock); + mask = ensoniq->uartm & ES_MODE_OUTPUT ? ES_TXRDY : 0; + while (mask) { + status = inb(ES_REG(ensoniq, UART_STATUS)); + if ((status & mask) == 0) + break; + if (snd_rawmidi_transmit(ensoniq->midi_output, &byte, 1) != 1) { + ensoniq->uartc &= ~ES_TXINTENM; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + mask &= ~ES_TXRDY; + } else { + outb(byte, ES_REG(ensoniq, UART_DATA)); + } + } + spin_unlock(&ensoniq->reg_lock); +} + +static int snd_ensoniq_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->uartm |= ES_MODE_INPUT; + ensoniq->midi_input = substream; + if (!(ensoniq->uartm & ES_MODE_OUTPUT)) { + outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL)); + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(ensoniq->uartm & ES_MODE_OUTPUT)) { + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } else { + outb(ensoniq->uartc &= ~ES_RXINTEN, ES_REG(ensoniq, UART_CONTROL)); + } + ensoniq->midi_input = NULL; + ensoniq->uartm &= ~ES_MODE_INPUT; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->uartm |= ES_MODE_OUTPUT; + ensoniq->midi_output = substream; + if (!(ensoniq->uartm & ES_MODE_INPUT)) { + outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL)); + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(ensoniq->uartm & ES_MODE_INPUT)) { + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } else { + outb(ensoniq->uartc &= ~ES_TXINTENM, ES_REG(ensoniq, UART_CONTROL)); + } + ensoniq->midi_output = NULL; + ensoniq->uartm &= ~ES_MODE_OUTPUT; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static void snd_ensoniq_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return); + int idx; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (up) { + if ((ensoniq->uartc & ES_RXINTEN) == 0) { + /* empty input FIFO */ + for (idx = 0; idx < 32; idx++) + inb(ES_REG(ensoniq, UART_DATA)); + ensoniq->uartc |= ES_RXINTEN; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } else { + if (ensoniq->uartc & ES_RXINTEN) { + ensoniq->uartc &= ~ES_RXINTEN; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +} + +static void snd_ensoniq_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return); + unsigned char byte; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (up) { + if (ES_TXINTENI(ensoniq->uartc) == 0) { + ensoniq->uartc |= ES_TXINTENO(1); + /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ + while (ES_TXINTENI(ensoniq->uartc) == 1 && + (inb(ES_REG(ensoniq, UART_STATUS)) & ES_TXRDY)) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + ensoniq->uartc &= ~ES_TXINTENM; + } else { + outb(byte, ES_REG(ensoniq, UART_DATA)); + } + } + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } else { + if (ES_TXINTENI(ensoniq->uartc) == 1) { + ensoniq->uartc &= ~ES_TXINTENM; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +} + +static snd_rawmidi_ops_t snd_ensoniq_midi_output = +{ + open: snd_ensoniq_midi_output_open, + close: snd_ensoniq_midi_output_close, + trigger: snd_ensoniq_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_ensoniq_midi_input = +{ + open: snd_ensoniq_midi_input_open, + close: snd_ensoniq_midi_input_close, + trigger: snd_ensoniq_midi_input_trigger, +}; + +static int __devinit snd_ensoniq_midi(ensoniq_t * ensoniq, int device, snd_rawmidi_t **rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0) + return err; +#ifdef CHIP1370 + strcpy(rmidi->name, "ES1370"); +#else + strcpy(rmidi->name, "ES1371"); +#endif + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = ensoniq; + ensoniq->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} + +/* + * Interrupt handler + */ + +static void snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, dev_id, return); + unsigned int status, sctrl; + + if (ensoniq == NULL) + return; + + status = inl(ES_REG(ensoniq, STATUS)); + if (!(status & ES_INTR)) + return; + + spin_lock(&ensoniq->reg_lock); + sctrl = ensoniq->sctrl; + if (status & ES_DAC1) + sctrl &= ~ES_P1_INT_EN; + if (status & ES_DAC2) + sctrl &= ~ES_P2_INT_EN; + if (status & ES_ADC) + sctrl &= ~ES_R1_INT_EN; + outl(sctrl, ES_REG(ensoniq, SERIAL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + spin_unlock(&ensoniq->reg_lock); + + if (status & ES_UART) + snd_ensoniq_midi_interrupt(ensoniq); + if ((status & ES_DAC2) && ensoniq->playback2_substream) + snd_pcm_period_elapsed(ensoniq->playback2_substream); + if ((status & ES_ADC) && ensoniq->capture_substream) + snd_pcm_period_elapsed(ensoniq->capture_substream); + if ((status & ES_DAC1) && ensoniq->playback1_substream) + snd_pcm_period_elapsed(ensoniq->playback1_substream); +} + +static int __devinit snd_audiopci_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + ensoniq_t *ensoniq; + int err, pcm_devs[2]; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ensoniq_create(card, pci, &ensoniq)) < 0) { + snd_card_free(card); + return err; + } + + pcm_devs[0] = 0; pcm_devs[1] = 1; +#ifdef CHIP1370 + if ((err = snd_ensoniq_1370_mixer(ensoniq)) < 0) { + snd_card_free(card); + return err; + } +#endif +#ifdef CHIP1371 + if ((err = snd_ensoniq_1371_mixer(ensoniq)) < 0) { + snd_card_free(card); + return err; + } +#endif + if ((err = snd_ensoniq_pcm(ensoniq, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ensoniq_pcm2(ensoniq, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ensoniq_midi(ensoniq, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifdef CHIP1370 + strcpy(card->driver, "ES1370"); +#endif +#ifdef CHIP1371 + strcpy(card->driver, "ES1371"); +#endif + strcpy(card->shortname, "Ensoniq AudioPCI"); + sprintf(card->longname, "%s %s at 0x%lx, irq %i", + card->shortname, + card->driver, + ensoniq->port, + ensoniq->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_audiopci_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "Ensoniq AudioPCI", + id_table: snd_audiopci_ids, + probe: snd_audiopci_probe, + remove: __devexit_p(snd_audiopci_remove), +}; + +static int __init alsa_card_ens137x_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("Ensoniq AudioPCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ens137x_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ens137x_init) +module_exit(alsa_card_ens137x_exit) + +#ifndef MODULE + +/* format is: snd-ens1370=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_ens137x_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +#if defined(CHIP1370) +__setup("snd-ens1370=", alsa_card_ens137x_setup); +#elif defined(CHIP1371) +__setup("snd-ens1371=", alsa_card_ens137x_setup); +#endif + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/ens1371.c b/sound/pci/ens1371.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ens1371.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2 @@ +#define CHIP1371 +#include "ens1370.c" diff -Nru a/sound/pci/es1938.c b/sound/pci/es1938.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/es1938.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1720 @@ +/* + * Driver for ESS Solo-1 (ES1938, ES1946) soundcard + * Copyright (c) by Jaromir Koutek , + * Jaroslav Kysela , + * Thomas Sailer , + * Abramo Bagnara , + * Markus Gruber + * + * Rewritted from sonicvibes.c source. + * + * TODO: + * Rewrite better spinlocks + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + NOTES: + - Capture data is written unaligned starting from dma_base + 1 so I need to + disable mmap and to add a copy callback. + - After several cycle of the following: + while : ; do arecord -d1 -f cd -t raw | aplay -f cd ; done + a "playback write error (DMA or IRQ trouble?)" may happen. + This is due to playback interrupts not generated. + I suspect a timing issue. + - Sometimes the interrupt handler is invoked wrongly during playback. + This generate some harmless "Unexpected hw_pointer: wrong interrupt + acknowledge". + I've seen that using small period sizes. + Reproducible with: + mpg123 test.mp3 & + hdparm -t -T /dev/hda +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#ifndef LINUX_2_2 +#include +#endif + +#define chip_t es1938_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaromir Koutek "); +MODULE_DESCRIPTION("ESS Solo-1"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,ES1938}," + "{TerraTec,128i PCI}}"); + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125d +#endif +#ifndef PCI_DEVICE_ID_ESS_ES1938 +#define PCI_DEVICE_ID_ESS_ES1938 0x1969 +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ESS Solo-1 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ESS Solo-1 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ESS Solo-1 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); + +#define SLIO_REG(chip, x) ((chip)->io_port + ESSIO_REG_##x) + +#define SLDM_REG(chip, x) ((chip)->ddma_port + ESSDM_REG_##x) + +#define SLSB_REG(chip, x) ((chip)->sb_port + ESSSB_REG_##x) + +#define SL_PCI_LEGACYCONTROL 0x40 +#define SL_PCI_CONFIG 0x50 +#define SL_PCI_DDMACONTROL 0x60 + +#define ESSIO_REG_AUDIO2DMAADDR 0 +#define ESSIO_REG_AUDIO2DMACOUNT 4 +#define ESSIO_REG_AUDIO2MODE 6 +#define ESSIO_REG_IRQCONTROL 7 + +#define ESSDM_REG_DMAADDR 0x00 +#define ESSDM_REG_DMACOUNT 0x04 +#define ESSDM_REG_DMACOMMAND 0x08 +#define ESSDM_REG_DMASTATUS 0x08 +#define ESSDM_REG_DMAMODE 0x0b +#define ESSDM_REG_DMACLEAR 0x0d +#define ESSDM_REG_DMAMASK 0x0f + +#define ESSSB_REG_FMLOWADDR 0x00 +#define ESSSB_REG_FMHIGHADDR 0x02 +#define ESSSB_REG_MIXERADDR 0x04 +#define ESSSB_REG_MIXERDATA 0x05 + +#define ESSSB_IREG_AUDIO1 0x14 +#define ESSSB_IREG_MICMIX 0x1a +#define ESSSB_IREG_RECSRC 0x1c +#define ESSSB_IREG_MASTER 0x32 +#define ESSSB_IREG_FM 0x36 +#define ESSSB_IREG_AUXACD 0x38 +#define ESSSB_IREG_AUXB 0x3a +#define ESSSB_IREG_PCSPEAKER 0x3c +#define ESSSB_IREG_LINE 0x3e +#define ESSSB_IREG_SPATCONTROL 0x50 +#define ESSSB_IREG_SPATLEVEL 0x52 +#define ESSSB_IREG_MASTER_LEFT 0x60 +#define ESSSB_IREG_MASTER_RIGHT 0x62 +#define ESSSB_IREG_MPU401CONTROL 0x64 +#define ESSSB_IREG_MICMIXRECORD 0x68 +#define ESSSB_IREG_AUDIO2RECORD 0x69 +#define ESSSB_IREG_AUXACDRECORD 0x6a +#define ESSSB_IREG_FMRECORD 0x6b +#define ESSSB_IREG_AUXBRECORD 0x6c +#define ESSSB_IREG_MONO 0x6d +#define ESSSB_IREG_LINERECORD 0x6e +#define ESSSB_IREG_MONORECORD 0x6f +#define ESSSB_IREG_AUDIO2SAMPLE 0x70 +#define ESSSB_IREG_AUDIO2MODE 0x71 +#define ESSSB_IREG_AUDIO2FILTER 0x72 +#define ESSSB_IREG_AUDIO2TCOUNTL 0x74 +#define ESSSB_IREG_AUDIO2TCOUNTH 0x76 +#define ESSSB_IREG_AUDIO2CONTROL1 0x78 +#define ESSSB_IREG_AUDIO2CONTROL2 0x7a +#define ESSSB_IREG_AUDIO2 0x7c + +#define ESSSB_REG_RESET 0x06 + +#define ESSSB_REG_READDATA 0x0a +#define ESSSB_REG_WRITEDATA 0x0c +#define ESSSB_REG_READSTATUS 0x0c + +#define ESSSB_REG_STATUS 0x0e + +#define ESS_CMD_EXTSAMPLERATE 0xa1 +#define ESS_CMD_FILTERDIV 0xa2 +#define ESS_CMD_DMACNTRELOADL 0xa4 +#define ESS_CMD_DMACNTRELOADH 0xa5 +#define ESS_CMD_ANALOGCONTROL 0xa8 +#define ESS_CMD_IRQCONTROL 0xb1 +#define ESS_CMD_DRQCONTROL 0xb2 +#define ESS_CMD_RECLEVEL 0xb4 +#define ESS_CMD_SETFORMAT 0xb6 +#define ESS_CMD_SETFORMAT2 0xb7 +#define ESS_CMD_DMACONTROL 0xb8 +#define ESS_CMD_DMATYPE 0xb9 +#define ESS_CMD_OFFSETLEFT 0xba +#define ESS_CMD_OFFSETRIGHT 0xbb +#define ESS_CMD_READREG 0xc0 +#define ESS_CMD_ENABLEEXT 0xc6 +#define ESS_CMD_PAUSEDMA 0xd0 +#define ESS_CMD_ENABLEAUDIO1 0xd1 +#define ESS_CMD_STOPAUDIO1 0xd3 +#define ESS_CMD_AUDIO1STATUS 0xd8 +#define ESS_CMD_CONTDMA 0xd4 +#define ESS_CMD_TESTIRQ 0xf2 + +#define ESS_RECSRC_MIC 0 +#define ESS_RECSRC_AUXACD 2 +#define ESS_RECSRC_AUXB 5 +#define ESS_RECSRC_LINE 6 +#define ESS_RECSRC_NONE 7 + +#define DAC1 0x01 +#define ADC1 0x02 +#define DAC2 0x04 + +/* + + */ + +typedef struct _snd_es1938 es1938_t; + +struct _snd_es1938 { + unsigned long dma1size; + unsigned long dma2size; + int irq; + + unsigned long io_port; + struct resource *res_io_port; + unsigned long sb_port; + struct resource *res_sb_port; + unsigned long vc_port; + struct resource *res_vc_port; + unsigned long mpu_port; + struct resource *res_mpu_port; + unsigned long game_port; + struct resource *res_game_port; + unsigned long ddma_port; + + unsigned char irqmask; + unsigned char revision; + + snd_kcontrol_t *hw_volume; + snd_kcontrol_t *hw_switch; + snd_kcontrol_t *master_volume; + snd_kcontrol_t *master_switch; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_substream_t *capture_substream; + snd_pcm_substream_t *playback1_substream; + snd_pcm_substream_t *playback2_substream; + snd_kmixer_t *mixer; + snd_rawmidi_t *rmidi; + + unsigned int dma1_size; + unsigned int dma2_size; + unsigned int dma1_start; + unsigned int dma2_start; + unsigned int dma1_shift; + unsigned int dma2_shift; + unsigned int active; + + spinlock_t reg_lock; + spinlock_t mixer_lock; + snd_info_entry_t *proc_entry; + +#ifndef LINUX_2_2 + struct gameport gameport; +#endif +}; + +static void snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct pci_device_id snd_es1938_ids[] __devinitdata = { + { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Solo-1 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_es1938_ids); + +#define RESET_LOOP_TIMEOUT 0x10000 +#define WRITE_LOOP_TIMEOUT 0x10000 +#define GET_LOOP_TIMEOUT 0x01000 + +#undef REG_DEBUG +/* ----------------------------------------------------------------- + * Write to a mixer register + * -----------------------------------------------------------------*/ +static void snd_es1938_mixer_write(es1938_t *chip, unsigned char reg, unsigned char val) +{ + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, SLSB_REG(chip, MIXERADDR)); + outb(val, SLSB_REG(chip, MIXERDATA)); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x set to %02x\n", reg, val); +#endif +} + +/* ----------------------------------------------------------------- + * Read from a mixer register + * -----------------------------------------------------------------*/ +static int snd_es1938_mixer_read(es1938_t *chip, unsigned char reg) +{ + int data; + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, SLSB_REG(chip, MIXERADDR)); + data = inb(SLSB_REG(chip, MIXERDATA)); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x now is %02x\n", reg, data); +#endif + return data; +} + +/* ----------------------------------------------------------------- + * Write to some bits of a mixer register (return old value) + * -----------------------------------------------------------------*/ +static int snd_es1938_mixer_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val) +{ + unsigned long flags; + unsigned char old, new, oval; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, SLSB_REG(chip, MIXERADDR)); + old = inb(SLSB_REG(chip, MIXERDATA)); + oval = old & mask; + if (val != oval) { + new = (old & ~mask) | (val & mask); + outb(new, SLSB_REG(chip, MIXERDATA)); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new); +#endif + } + spin_unlock_irqrestore(&chip->mixer_lock, flags); + return oval; +} + +/* ----------------------------------------------------------------- + * Write command to Controller Registers + * -----------------------------------------------------------------*/ +static void snd_es1938_write_cmd(es1938_t *chip, unsigned char cmd) +{ + int i; + unsigned char v; + for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) { + if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) { + outb(cmd, SLSB_REG(chip, WRITEDATA)); + return; + } + } + printk("snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v); +} + +/* ----------------------------------------------------------------- + * Read the Read Data Buffer + * -----------------------------------------------------------------*/ +static int snd_es1938_get_byte(es1938_t *chip) +{ + int i; + unsigned char v; + for (i = GET_LOOP_TIMEOUT; i; i--) + if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80) + return inb(SLSB_REG(chip, READDATA)); + snd_printk("get_byte timeout: status 0x02%x\n", v); + return -ENODEV; +} + +/* ----------------------------------------------------------------- + * Write value cmd register + * -----------------------------------------------------------------*/ +static void snd_es1938_write(es1938_t *chip, unsigned char reg, unsigned char val) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1938_write_cmd(chip, reg); + snd_es1938_write_cmd(chip, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#ifdef REG_DEBUG + snd_printk("Reg %02x set to %02x\n", reg, val); +#endif +} + +/* ----------------------------------------------------------------- + * Read data from cmd register and return it + * -----------------------------------------------------------------*/ +static unsigned char snd_es1938_read(es1938_t *chip, unsigned char reg) +{ + unsigned char val; + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1938_write_cmd(chip, ESS_CMD_READREG); + snd_es1938_write_cmd(chip, reg); + val = snd_es1938_get_byte(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#ifdef REG_DEBUG + snd_printk("Reg %02x now is %02x\n", reg, val); +#endif + return val; +} + +/* ----------------------------------------------------------------- + * Write data to cmd register and return old value + * -----------------------------------------------------------------*/ +static int snd_es1938_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val) +{ + unsigned long flags; + unsigned char old, new, oval; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1938_write_cmd(chip, ESS_CMD_READREG); + snd_es1938_write_cmd(chip, reg); + old = snd_es1938_get_byte(chip); + oval = old & mask; + if (val != oval) { + snd_es1938_write_cmd(chip, reg); + new = (old & ~mask) | (val & mask); + snd_es1938_write_cmd(chip, new); +#ifdef REG_DEBUG + snd_printk("Reg %02x was %02x, set to %02x\n", reg, old, new); +#endif + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return oval; +} + +/* -------------------------------------------------------------------- + * Reset the chip + * --------------------------------------------------------------------*/ +static void snd_es1938_reset(es1938_t *chip) +{ + int i; + + outb(3, SLSB_REG(chip, RESET)); + inb(SLSB_REG(chip, RESET)); + outb(0, SLSB_REG(chip, RESET)); + for (i = 0; i < RESET_LOOP_TIMEOUT; i++) { + if (inb(SLSB_REG(chip, STATUS)) & 0x80) { + if (inb(SLSB_REG(chip, READDATA)) == 0xaa) + goto __next; + } + } + snd_printk("ESS Solo-1 reset failed\n"); + + __next: + snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT); + + /* Demand transfer DMA: 4 bytes per DMA request */ + snd_es1938_write(chip, ESS_CMD_DMATYPE, 2); + + /* Change behaviour of register A1 + 4x oversampling + 2nd channel DAC asynchronous */ + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2MODE, 0x32); + /* enable/select DMA channel and IRQ channel */ + snd_es1938_bits(chip, ESS_CMD_IRQCONTROL, 0xf0, 0x50); + snd_es1938_bits(chip, ESS_CMD_DRQCONTROL, 0xf0, 0x50); + snd_es1938_write_cmd(chip, ESS_CMD_ENABLEAUDIO1); + /* Set spatializer parameters to recommended values */ + snd_es1938_mixer_write(chip, 0x54, 0x8f); + snd_es1938_mixer_write(chip, 0x56, 0x95); + snd_es1938_mixer_write(chip, 0x58, 0x94); + snd_es1938_mixer_write(chip, 0x5a, 0x80); +} + +/* -------------------------------------------------------------------- + * Reset the FIFOs + * --------------------------------------------------------------------*/ +static void snd_es1938_reset_fifo(es1938_t *chip) +{ + outb(2, SLSB_REG(chip, RESET)); + outb(0, SLSB_REG(chip, RESET)); +} + +static ratnum_t clocks[2] = { + { + num: 793800, + den_min: 1, + den_max: 128, + den_step: 1, + }, + { + num: 768000, + den_min: 1, + den_max: 128, + den_step: 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + nrats: 2, + rats: clocks, +}; + + +static void snd_es1938_rate_set(es1938_t *chip, + snd_pcm_substream_t *substream, + int mode) +{ + unsigned int bits, div0; + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->rate_num == clocks[0].num) + bits = 128 - runtime->rate_den; + else + bits = 256 - runtime->rate_den; + + /* set filter register */ + div0 = 256 - 7160000*20/(8*82*runtime->rate); + + if (mode == DAC2) { + snd_es1938_mixer_write(chip, 0x70, bits); + snd_es1938_mixer_write(chip, 0x72, div0); + } else { + snd_es1938_write(chip, 0xA1, bits); + snd_es1938_write(chip, 0xA2, div0); + } +} + +/* -------------------------------------------------------------------- + * Configure Solo1 builtin DMA Controller + * --------------------------------------------------------------------*/ + +static void snd_es1938_playback1_setdma(es1938_t *chip) +{ + outb(0x00, SLIO_REG(chip, AUDIO2MODE)); + outl(chip->dma2_start, SLIO_REG(chip, AUDIO2DMAADDR)); + outw(0, SLIO_REG(chip, AUDIO2DMACOUNT)); + outw(chip->dma2_size, SLIO_REG(chip, AUDIO2DMACOUNT)); +} + +static void snd_es1938_playback2_setdma(es1938_t *chip) +{ + /* Enable DMA controller */ + outb(0xc4, SLDM_REG(chip, DMACOMMAND)); + /* 1. Master reset */ + outb(0, SLDM_REG(chip, DMACLEAR)); + /* 2. Mask DMA */ + outb(1, SLDM_REG(chip, DMAMASK)); + outb(0x18, SLDM_REG(chip, DMAMODE)); + outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); + outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); + /* 3. Unmask DMA */ + outb(0, SLDM_REG(chip, DMAMASK)); +} + +static void snd_es1938_capture_setdma(es1938_t *chip) +{ + /* Enable DMA controller */ + outb(0xc4, SLDM_REG(chip, DMACOMMAND)); + /* 1. Master reset */ + outb(0, SLDM_REG(chip, DMACLEAR)); + /* 2. Mask DMA */ + outb(1, SLDM_REG(chip, DMAMASK)); + outb(0x14, SLDM_REG(chip, DMAMODE)); + outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); + outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); + /* 3. Unmask DMA */ + outb(0, SLDM_REG(chip, DMAMASK)); +} + +/* ---------------------------------------------------------------------- + * + * *** PCM part *** + */ + +static int snd_es1938_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + int val; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = 0x0f; + chip->active |= ADC1; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = 0x00; + chip->active &= ~ADC1; + break; + default: + return -EINVAL; + } + snd_es1938_write(chip, ESS_CMD_DMACONTROL, val); + return 0; +} + +static int snd_es1938_playback1_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* According to the documentation this should be: + 0x13 but that value may random swap stereo channels */ + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x93); + outb(0x0a, SLIO_REG(chip, AUDIO2MODE)); + chip->active |= DAC2; + break; + case SNDRV_PCM_TRIGGER_STOP: + outb(0, SLIO_REG(chip, AUDIO2MODE)); + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0); + chip->active &= ~DAC2; + break; + default: + return -EINVAL; + } + return 0; +} + +static int snd_es1938_playback2_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + int val; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = 5; + chip->active |= DAC1; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = 0; + chip->active &= ~DAC1; + break; + default: + return -EINVAL; + } + snd_es1938_write(chip, ESS_CMD_DMACONTROL, val); + return 0; +} + +static int snd_es1938_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + switch (substream->number) { + case 0: + return snd_es1938_playback1_trigger(substream, cmd); + case 1: + return snd_es1938_playback2_trigger(substream, cmd); + } + snd_BUG(); + return -EINVAL; +} + +/* -------------------------------------------------------------------- + * First channel for Extended Mode Audio 1 ADC Operation + * --------------------------------------------------------------------*/ +static int snd_es1938_capture_prepare(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int u, is8, mono; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + chip->dma1_start = runtime->dma_addr; + + mono = (runtime->channels > 1) ? 0 : 1; + is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1; + u = snd_pcm_format_unsigned(runtime->format); + + chip->dma1_shift = 2 - mono - is8; + + snd_es1938_reset_fifo(chip); + + /* program type */ + snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1)); + + /* set clock and counters */ + snd_es1938_rate_set(chip, substream, ADC1); + + count = 0x10000 - count; + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff); + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8); + + /* initialize and configure ADC */ + snd_es1938_write(chip, ESS_CMD_SETFORMAT2, u ? 0x51 : 0x71); + snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 0x90 | + (u ? 0x00 : 0x20) | + (is8 ? 0x00 : 0x04) | + (mono ? 0x40 : 0x08)); + + // snd_es1938_reset_fifo(chip); + + /* 11. configure system interrupt controller and DMA controller */ + snd_es1938_capture_setdma(chip); + + return 0; +} + + +/* ------------------------------------------------------------------------------ + * Second Audio channel DAC Operation + * ------------------------------------------------------------------------------*/ +static int snd_es1938_playback1_prepare(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int u, is8, mono; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma2_size = size; + chip->dma2_start = runtime->dma_addr; + + mono = (runtime->channels > 1) ? 0 : 1; + is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1; + u = snd_pcm_format_unsigned(runtime->format); + + chip->dma2_shift = 2 - mono - is8; + + /* set clock and counters */ + snd_es1938_rate_set(chip, substream, DAC2); + + count >>= 1; + count = 0x10000 - count; + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTL, count & 0xff); + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTH, count >> 8); + + /* initialize and configure Audio 2 DAC */ + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x40 | (u ? 0 : 4) | (mono ? 0 : 2) | (is8 ? 0 : 1)); + + /* program DMA */ + snd_es1938_playback1_setdma(chip); + + return 0; +} + +static int snd_es1938_playback2_prepare(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int u, is8, mono; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + chip->dma1_start = runtime->dma_addr; + + mono = (runtime->channels > 1) ? 0 : 1; + is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1; + u = snd_pcm_format_unsigned(runtime->format); + + chip->dma1_shift = 2 - mono - is8; + + count = 0x10000 - count; + + /* reset */ + snd_es1938_reset_fifo(chip); + + snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1)); + + /* set clock and counters */ + snd_es1938_rate_set(chip, substream, DAC1); + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff); + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8); + + /* initialized and configure DAC */ + snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x80 : 0x00); + snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x51 : 0x71); + snd_es1938_write(chip, ESS_CMD_SETFORMAT2, + 0x90 | (mono ? 0x40 : 0x08) | + (is8 ? 0x00 : 0x04) | (u ? 0x00 : 0x20)); + + /* program DMA */ + snd_es1938_playback2_setdma(chip); + + return 0; +} + +static int snd_es1938_playback_prepare(snd_pcm_substream_t *substream) +{ + switch (substream->number) { + case 0: + return snd_es1938_playback1_prepare(substream); + case 1: + return snd_es1938_playback2_prepare(substream); + } + snd_BUG(); + return -EINVAL; +} + +static snd_pcm_uframes_t snd_es1938_capture_pointer(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + size_t old, new; +#if 1 + /* This stuff is *needed*, don't ask why - AB */ + old = inw(SLDM_REG(chip, DMACOUNT)); + while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) + old = new; + ptr = chip->dma1_size - 1 - new; +#else + ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; +#endif + return ptr >> chip->dma1_shift; +} + +static snd_pcm_uframes_t snd_es1938_playback1_pointer(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; +#if 1 + ptr = chip->dma2_size - inw(SLIO_REG(chip, AUDIO2DMACOUNT)); +#else + ptr = inl(SLIO_REG(chip, AUDIO2DMAADDR)) - chip->dma2_start; +#endif + return ptr >> chip->dma2_shift; +} + +static snd_pcm_uframes_t snd_es1938_playback2_pointer(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + size_t old, new; +#if 1 + /* This stuff is *needed*, don't ask why - AB */ + old = inw(SLDM_REG(chip, DMACOUNT)); + while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) + old = new; + ptr = chip->dma1_size - 1 - new; +#else + ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; +#endif + return ptr >> chip->dma1_shift; +} + +static snd_pcm_uframes_t snd_es1938_playback_pointer(snd_pcm_substream_t *substream) +{ + switch (substream->number) { + case 0: + return snd_es1938_playback1_pointer(substream); + case 1: + return snd_es1938_playback2_pointer(substream); + } + snd_BUG(); + return -EINVAL; +} + +static int snd_es1938_capture_copy(snd_pcm_substream_t *substream, + int channel, + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es1938_t *chip = snd_pcm_substream_chip(substream); + pos <<= chip->dma1_shift; + count <<= chip->dma1_shift; + snd_assert(pos + count <= chip->dma1_size, return -EINVAL); + if (pos + count < chip->dma1_size) + memcpy(dst, runtime->dma_area + pos + 1, count); + else { + memcpy(dst, runtime->dma_area + pos + 1, count - 1); + ((unsigned char *)dst)[count - 1] = runtime->dma_area[0]; + } + return 0; +} + +/* ---------------------------------------------------------------------- + * Audio1 Capture (ADC) + * ----------------------------------------------------------------------*/ +static snd_pcm_hardware_t snd_es1938_capture = +{ + info: (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 6000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 256, +}; + +/* ----------------------------------------------------------------------- + * Audio2 Playback (DAC) + * -----------------------------------------------------------------------*/ +static snd_pcm_hardware_t snd_es1938_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 6000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 256, +}; + +static int snd_es1938_capture_open(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (chip->playback2_substream) + return -EAGAIN; + if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma2size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) + return -ENOMEM; + chip->capture_substream = substream; + runtime->hw = snd_es1938_capture; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00); + return 0; +} + +static int snd_es1938_playback_open(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + switch (substream->number) { + case 0: + if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) + return -ENOMEM; + chip->playback1_substream = substream; + break; + case 1: + if (chip->capture_substream) + return -EAGAIN; + if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) + return -ENOMEM; + chip->playback2_substream = substream; + break; + default: + snd_BUG(); + return -EINVAL; + } + runtime->hw = snd_es1938_playback; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00); + return 0; +} + +static int snd_es1938_capture_close(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture_substream = NULL; + snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + return 0; +} + +static int snd_es1938_playback_close(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + switch (substream->number) { + case 0: + chip->playback1_substream = NULL; + break; + case 1: + chip->playback2_substream = NULL; + break; + default: + snd_BUG(); + return -EINVAL; + } + snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + return 0; +} + +static snd_pcm_ops_t snd_es1938_playback_ops = { + open: snd_es1938_playback_open, + close: snd_es1938_playback_close, + ioctl: snd_pcm_lib_ioctl, + prepare: snd_es1938_playback_prepare, + trigger: snd_es1938_playback_trigger, + pointer: snd_es1938_playback_pointer, +}; + +static snd_pcm_ops_t snd_es1938_capture_ops = { + open: snd_es1938_capture_open, + close: snd_es1938_capture_close, + ioctl: snd_pcm_lib_ioctl, + prepare: snd_es1938_capture_prepare, + trigger: snd_es1938_capture_trigger, + pointer: snd_es1938_capture_pointer, + copy: snd_es1938_capture_copy, +}; + +static void snd_es1938_free_pcm(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __init snd_es1938_new_pcm(es1938_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm)) < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1938_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1938_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_es1938_free_pcm; + pcm->info_flags = 0; + strcpy(pcm->name, "ESS Solo-1"); + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* ------------------------------------------------------------------- + * + * *** Mixer part *** + */ + +static int snd_es1938_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Mic", "Mic Master", "CD", "AOUT", + "Mic1", "Mix", "Line", "Master" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_es1938_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = snd_es1938_mixer_read(chip, 0x1c) & 0x07; + return 0; +} + +static int snd_es1938_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = ucontrol->value.enumerated.item[0]; + + if (val > 7) + return -EINVAL; + return snd_es1938_mixer_bits(chip, 0x1c, 0x07, val) != val; +} + +static int snd_es1938_info_spatializer_enable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1938_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = snd_es1938_mixer_read(chip, 0x50); + ucontrol->value.integer.value[0] = !!(val & 8); + return 0; +} + +static int snd_es1938_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char oval, nval; + int change; + nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04; + oval = snd_es1938_mixer_read(chip, 0x50) & 0x0c; + change = nval != oval; + if (change) { + snd_es1938_mixer_write(chip, 0x50, nval & ~0x04); + snd_es1938_mixer_write(chip, 0x50, nval); + } + return change; +} + +static int snd_es1938_info_hw_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 63; + return 0; +} + +static int snd_es1938_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = snd_es1938_mixer_read(chip, 0x61) & 0x3f; + ucontrol->value.integer.value[1] = snd_es1938_mixer_read(chip, 0x63) & 0x3f; + return 0; +} + +static int snd_es1938_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1938_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = !(snd_es1938_mixer_read(chip, 0x61) & 0x40); + ucontrol->value.integer.value[1] = !(snd_es1938_mixer_read(chip, 0x63) & 0x40); + return 0; +} + +static void snd_es1938_hwv_free(snd_kcontrol_t *kcontrol) +{ + es1938_t *chip = snd_magic_cast(es1938_t, _snd_kcontrol_chip(kcontrol), return); + chip->master_volume = NULL; + chip->master_switch = NULL; + chip->hw_volume = NULL; + chip->hw_switch = NULL; +} + +static int snd_es1938_reg_bits(es1938_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + if (reg < 0xa0) + return snd_es1938_mixer_bits(chip, reg, mask, val); + else + return snd_es1938_bits(chip, reg, mask, val); +} + +static int snd_es1938_reg_read(es1938_t *chip, unsigned char reg) +{ + if (reg < 0xa0) + return snd_es1938_mixer_read(chip, reg); + else + return snd_es1938_read(chip, reg); +} + +#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es1938_info_single, \ + get: snd_es1938_get_single, put: snd_es1938_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_es1938_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1938_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int val; + + val = snd_es1938_reg_read(chip, reg); + ucontrol->value.integer.value[0] = (val >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_es1938_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned char val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + mask <<= shift; + val <<= shift; + return snd_es1938_reg_bits(chip, reg, mask, val) != val; +} + +#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es1938_info_double, \ + get: snd_es1938_get_double, put: snd_es1938_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_es1938_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1938_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + unsigned char left, right; + + left = snd_es1938_reg_read(chip, left_reg); + if (left_reg != right_reg) + right = snd_es1938_reg_read(chip, right_reg); + else + right = left; + ucontrol->value.integer.value[0] = (left >> shift_left) & mask; + ucontrol->value.integer.value[1] = (right >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_es1938_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned char val1, val2, mask1, mask2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + mask1 = mask << shift_left; + mask2 = mask << shift_right; + if (left_reg != right_reg) { + change = 0; + if (snd_es1938_reg_bits(chip, left_reg, mask1, val1) != val1) + change = 1; + if (snd_es1938_reg_bits(chip, right_reg, mask2, val2) != val2) + change = 1; + } else { + change = (snd_es1938_reg_bits(chip, left_reg, mask1 | mask2, + val1 | val2) != (val1 | val2)); + } + return change; +} + +static snd_kcontrol_new_t snd_es1938_controls[] = { +ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), +ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Hardware Master Playback Volume", + access: SNDRV_CTL_ELEM_ACCESS_READ, + info: snd_es1938_info_hw_volume, + get: snd_es1938_get_hw_volume, +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Hardware Master Playback Switch", + access: SNDRV_CTL_ELEM_ACCESS_READ, + info: snd_es1938_info_hw_switch, + get: snd_es1938_get_hw_switch, +}, +ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0), +ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), +ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), +ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), +ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), +ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), +ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), +ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), +ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0), +ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), +ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_es1938_info_mux, + get: snd_es1938_get_mux, + put: snd_es1938_put_mux, +}, +ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), +ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), +ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), +ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), +ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), +ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0), +ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), +ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0), +ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), +ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0), +ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "3D Control - Switch", + info: snd_es1938_info_spatializer_enable, + get: snd_es1938_get_spatializer_enable, + put: snd_es1938_put_spatializer_enable, +}, +ES1938_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0) +}; + + +/* ---------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------- */ + +static int snd_es1938_free(es1938_t *chip) +{ +#ifndef LINUX_2_2 + if (chip->gameport.io) + gameport_unregister_port(&chip->gameport); +#endif + if (chip->res_io_port) { + release_resource(chip->res_io_port); + kfree_nocheck(chip->res_io_port); + } + if (chip->res_sb_port) { + release_resource(chip->res_sb_port); + kfree_nocheck(chip->res_sb_port); + } + if (chip->res_vc_port) { + release_resource(chip->res_vc_port); + kfree_nocheck(chip->res_vc_port); + } + if (chip->res_mpu_port) { + release_resource(chip->res_mpu_port); + kfree_nocheck(chip->res_mpu_port); + } + if (chip->res_game_port) { + release_resource(chip->res_game_port); + kfree_nocheck(chip->res_game_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +static int snd_es1938_dev_free(snd_device_t *device) +{ + es1938_t *chip = snd_magic_cast(es1938_t, device->device_data, return -ENXIO); + return snd_es1938_free(chip); +} + +static int __init snd_es1938_create(snd_card_t * card, + struct pci_dev * pci, + unsigned long dma1size, + unsigned long dma2size, + es1938_t ** rchip) +{ + es1938_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_es1938_dev_free, + }; + + *rchip = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 24 bits */ + if (!pci_dma_supported(pci, 0x00ffffff)) { + snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x00ffffff); + + chip = snd_magic_kcalloc(es1938_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->mixer_lock); + chip->card = card; + chip->pci = pci; + chip->dma1size = dma1size; + chip->dma2size = dma2size; + chip->io_port = pci_resource_start(pci, 0); + if ((chip->res_io_port = request_region(chip->io_port, 8, "ESS Solo-1")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 8 - 1); + return -EBUSY; + } + chip->sb_port = pci_resource_start(pci, 1); + if ((chip->res_sb_port = request_region(chip->sb_port, 0x10, "ESS Solo-1 SB")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab SB region 0x%lx-0x%lx\n", chip->sb_port, chip->sb_port + 0x10 - 1); + return -EBUSY; + } + chip->vc_port = pci_resource_start(pci, 2); + if ((chip->res_vc_port = request_region(chip->vc_port, 0x10, "ESS Solo-1 VC (DMA)")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab VC (DMA) region 0x%lx-0x%lx\n", chip->vc_port, chip->vc_port + 0x10 - 1); + return -EBUSY; + } + chip->mpu_port = pci_resource_start(pci, 3); + if ((chip->res_mpu_port = request_region(chip->mpu_port, 4, "ESS Solo-1 MIDI")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab MIDI region 0x%lx-0x%lx\n", chip->mpu_port, chip->mpu_port + 4 - 1); + return -EBUSY; + } + chip->game_port = pci_resource_start(pci, 4); + if ((chip->res_game_port = request_region(chip->game_port, 4, "ESS Solo-1 GAME")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab GAME region 0x%lx-0x%lx\n", chip->game_port, chip->game_port + 4 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ, "ES1938", (void *)chip)) { + snd_es1938_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; +#ifdef ES1938_DDEBUG + snd_printk("create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n", + chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port); +#endif + /* reset chip */ + snd_es1938_reset(chip); + + /* configure native mode */ + + /* enable bus master */ + pci_set_master(pci); + + /* disable legacy audio */ + pci_write_config_word(pci, SL_PCI_LEGACYCONTROL, 0x805f); + + /* set DDMA base */ + chip->ddma_port = chip->vc_port + 0x00; /* fix from Thomas Sailer */ + pci_write_config_word(pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1); + + /* set DMA/IRQ policy */ + pci_write_config_dword(pci, SL_PCI_CONFIG, 0); + + /* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/ + outb(0xf0, SLIO_REG(chip, IRQCONTROL)); + + /* reset DMA */ + outb(0, SLDM_REG(chip, DMACLEAR)); + + /* enable bus mastering */ + pci_set_master(pci); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es1938_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +/* -------------------------------------------------------------------- + * Interrupt handler + * -------------------------------------------------------------------- */ +static void snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es1938_t *chip = snd_magic_cast(es1938_t, dev_id, return); + unsigned char status, audiostatus; + + status = inb(SLIO_REG(chip, IRQCONTROL)); +#if 0 + printk("Es1938debug - interrupt status: =0x%x\n", status); +#endif + + /* AUDIO 1 */ + if (status & 0x10) { +#if 0 + printk("Es1938debug - AUDIO channel 1 interrupt\n"); + printk("Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n", inw(SLDM_REG(chip, DMACOUNT))); + printk("Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n", inl(SLDM_REG(chip, DMAADDR))); + printk("Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n", inl(SLDM_REG(chip, DMASTATUS))); +#endif + /* clear irq */ + audiostatus = inb(SLSB_REG(chip, STATUS)); + if (chip->active & ADC1) + snd_pcm_period_elapsed(chip->capture_substream); + else if (chip->active & DAC1) + snd_pcm_period_elapsed(chip->playback2_substream); + } + + /* AUDIO 2 */ + if (status & 0x20) { +#if 0 + printk("Es1938debug - AUDIO channel 2 interrupt\n"); + printk("Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n", inw(SLIO_REG(chip, AUDIO2DMACOUNT))); + printk("Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n", inl(SLIO_REG(chip, AUDIO2DMAADDR))); + +#endif + /* clear irq */ + snd_es1938_mixer_bits(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x80, 0); + if (chip->active & DAC2) + snd_pcm_period_elapsed(chip->playback1_substream); + } + + /* Hardware volume */ + if (status & 0x40) { + int split = snd_es1938_mixer_read(chip, 0x64) & 0x80; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id); + if (!split) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); + } + /* ack interrupt */ + snd_es1938_mixer_write(chip, 0x66, 0x00); + } + + /* MPU401 */ + if (status & 0x80) { + /* ack */ + snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0); + printk("midi interrupt..\n"); + if (chip->rmidi) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + } +} + +#define ES1938_DMA_SIZE 64 + +static int __init snd_es1938_mixer(snd_pcm_t *pcm) +{ + snd_card_t *card; + es1938_t *chip; + int err, idx; + + snd_assert(pcm != NULL && pcm->card != NULL, return -EINVAL); + + card = pcm->card; + chip = snd_pcm_chip(pcm); + + strcpy(card->mixername, pcm->name); + + for (idx = 0; idx < sizeof(snd_es1938_controls) / + sizeof(snd_es1938_controls[0]); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip); + switch (idx) { + case 0: + chip->master_volume = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + case 1: + chip->master_switch = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + case 2: + chip->hw_volume = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + case 3: + chip->hw_switch = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + } + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + } + return 0; +} + + +static int __devinit snd_es1938_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + es1938_t *chip; + snd_pcm_t *pcm; + opl3_t *opl3; + int idx, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + for (idx = 0; idx < 5; idx++) { + if (pci_resource_start(pci, idx) == 0 || + !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) { + snd_card_free(card); + return -ENODEV; + } + } + if ((err = snd_es1938_create(card, pci, + 64 * 1024, + 64 * 1024, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1938_new_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1938_mixer(pcm)) < 0) { + snd_card_free(card); + return err; + } + if (snd_opl3_create(card, + SLSB_REG(chip, FMLOWADDR), + SLSB_REG(chip, FMHIGHADDR), + OPL3_HW_OPL3, 1, &opl3) < 0) { + snd_printk("OPL3 not detected at 0x%lx\n", + SLSB_REG(chip, FMLOWADDR)); + } else { + if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) { + snd_printk("unable to initialize MPU-401\n"); + } +#ifndef LINUX_2_2 + chip->gameport.io = chip->game_port; + gameport_register_port(&chip->gameport); +#endif + + strcpy(card->driver, "ES1938"); + strcpy(card->shortname, "ESS ES1938 (Solo-1)"); + sprintf(card->longname, "%s rev %i, irq %i", + card->shortname, + chip->revision, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_es1938_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "ESS ES1938 (Solo-1)", + id_table: snd_es1938_ids, + probe: snd_es1938_probe, + remove: __devexit_p(snd_es1938_remove), +}; + +static int __init alsa_card_es1938_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("ESS Solo-1 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_es1938_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_es1938_init) +module_exit(alsa_card_es1938_exit) + +#ifndef MODULE + +/* format is: snd-es1938=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_es1938_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es1938=", alsa_card_es1938_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/es1968.c b/sound/pci/es1968.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/es1968.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,2840 @@ +/* + * Driver for ESS Maestro 1/2/2E Sound Card (started 21.8.99) + * Copyright (c) by Matze Braun . + * Takashi Iwai + * + * Most of the driver code comes from Zach Brown(zab@redhat.com) + * Alan Cox OSS Driver + * Rewritted from card-es1938.c source. + * + * TODO: + * Perhaps Synth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Notes from Zach Brown about the driver code + * + * Hardware Description + * + * A working Maestro setup contains the Maestro chip wired to a + * codec or 2. In the Maestro we have the APUs, the ASSP, and the + * Wavecache. The APUs can be though of as virtual audio routing + * channels. They can take data from a number of sources and perform + * basic encodings of the data. The wavecache is a storehouse for + * PCM data. Typically it deals with PCI and interracts with the + * APUs. The ASSP is a wacky DSP like device that ESS is loth + * to release docs on. Thankfully it isn't required on the Maestro + * until you start doing insane things like FM emulation and surround + * encoding. The codecs are almost always AC-97 compliant codecs, + * but it appears that early Maestros may have had PT101 (an ESS + * part?) wired to them. The only real difference in the Maestro + * families is external goop like docking capability, memory for + * the ASSP, and initialization differences. + * + * Driver Operation + * + * We only drive the APU/Wavecache as typical DACs and drive the + * mixers in the codecs. There are 64 APUs. We assign 6 to each + * /dev/dsp? device. 2 channels for output, and 4 channels for + * input. + * + * Each APU can do a number of things, but we only really use + * 3 basic functions. For playback we use them to convert PCM + * data fetched over PCI by the wavecahche into analog data that + * is handed to the codec. One APU for mono, and a pair for stereo. + * When in stereo, the combination of smarts in the APU and Wavecache + * decide which wavecache gets the left or right channel. + * + * For record we still use the old overly mono system. For each in + * coming channel the data comes in from the codec, through a 'input' + * APU, through another rate converter APU, and then into memory via + * the wavecache and PCI. If its stereo, we mash it back into LRLR in + * software. The pass between the 2 APUs is supposedly what requires us + * to have a 512 byte buffer sitting around in wavecache/memory. + * + * The wavecache makes our life even more fun. First off, it can + * only address the first 28 bits of PCI address space, making it + * useless on quite a few architectures. Secondly, its insane. + * It claims to fetch from 4 regions of PCI space, each 4 meg in length. + * But that doesn't really work. You can only use 1 region. So all our + * allocations have to be in 4meg of each other. Booo. Hiss. + * So we have a module parameter, dsps_order, that is the order of + * the number of dsps to provide. All their buffer space is allocated + * on open time. The sonicvibes OSS routines we inherited really want + * power of 2 buffers, so we have all those next to each other, then + * 512 byte regions for the recording wavecaches. This ends up + * wasting quite a bit of memory. The only fixes I can see would be + * getting a kernel allocator that could work in zones, or figuring out + * just how to coerce the WP into doing what we want. + * + * The indirection of the various registers means we have to spinlock + * nearly all register accesses. We have the main register indirection + * like the wave cache, maestro registers, etc. Then we have beasts + * like the APU interface that is indirect registers gotten at through + * the main maestro indirection. Ouch. We spinlock around the actual + * ports on a per card basis. This means spinlock activity at each IO + * operation, but the only IO operation clusters are in non critical + * paths and it makes the code far easier to follow. Interrupts are + * blocked while holding the locks because the int handler has to + * get at some of them :(. The mixer interface doesn't, however. + * We also have an OSS state lock that is thrown around in a few + * places. + */ + +#define __SND_OSS_COMPAT__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t es1968_t + +#define CARD_NAME "ESS Maestro1/2" +#define DRIVER_NAME "ES1968" + +EXPORT_NO_SYMBOLS; +MODULE_DESCRIPTION("ESS Maestro"); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); +MODULE_DEVICES("{{ESS,Maestro 2e}," + "{ESS,Maestro 2}," + "{ESS,Maestro 1}," + "{TerraTec,DMX}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 }; +static int snd_pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 }; +static int snd_pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 }; +static int snd_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_total_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_total_bufsize, "Total buffer size in kB."); +MODULE_PARM_SYNTAX(snd_total_bufsize, SNDRV_ENABLED ",allows:{{1,4096}},skill:advanced"); +MODULE_PARM(snd_pcm_substreams_p, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_substreams_p, "PCM Playback substreams for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_pcm_substreams_p, SNDRV_ENABLED ",allows:{{1,8}}"); +MODULE_PARM(snd_pcm_substreams_c, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_substreams_c, "PCM Capture substreams for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_pcm_substreams_c, SNDRV_ENABLED ",allows:{{0,8}}"); +MODULE_PARM(snd_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_clock, "Clock on " CARD_NAME " soundcard. (0 = auto-detect)"); +MODULE_PARM_SYNTAX(snd_clock, SNDRV_ENABLED); + + +/* PCI Dev ID's */ + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125D +#endif + +#define PCI_VENDOR_ID_ESS_OLD 0x1285 /* Platform Tech, the people the ESS + was bought form */ + +#ifndef PCI_DEVICE_ID_ESS_M2E +#define PCI_DEVICE_ID_ESS_M2E 0x1978 +#endif +#ifndef PCI_DEVICE_ID_ESS_M2 +#define PCI_DEVICE_ID_ESS_M2 0x1968 +#endif +#ifndef PCI_DEVICE_ID_ESS_M1 +#define PCI_DEVICE_ID_ESS_M1 0x0100 +#endif + +#define NR_APUS 64 +#define NR_APU_REGS 16 + +/* NEC Versas ? */ +#define NEC_VERSA_SUBID1 0x80581033 +#define NEC_VERSA_SUBID2 0x803c1033 + +/* Mode Flags */ +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +/* Values for the ESM_LEGACY_AUDIO_CONTROL */ + +#define ESS_ENABLE_AUDIO 0x8000 +#define ESS_ENABLE_SERIAL_IRQ 0x4000 +#define IO_ADRESS_ALIAS 0x0020 +#define MPU401_IRQ_ENABLE 0x0010 +#define MPU401_IO_ENABLE 0x0008 +#define GAME_IO_ENABLE 0x0004 +#define FM_IO_ENABLE 0x0002 +#define SB_IO_ENABLE 0x0001 + +/* Values for the ESM_CONFIG_A */ + +#define PIC_SNOOP1 0x4000 +#define PIC_SNOOP2 0x2000 +#define SAFEGUARD 0x0800 +#define DMA_CLEAR 0x0700 +#define DMA_DDMA 0x0000 +#define DMA_TDMA 0x0100 +#define DMA_PCPCI 0x0200 +#define POST_WRITE 0x0080 +#define ISA_TIMING 0x0040 +#define SWAP_LR 0x0020 +#define SUBTR_DECODE 0x0002 + +/* Values for the ESM_CONFIG_B */ + +#define SPDIF_CONFB 0x0100 +#define HWV_CONFB 0x0080 +#define DEBOUNCE 0x0040 +#define GPIO_CONFB 0x0020 +#define CHI_CONFB 0x0010 +#define IDMA_CONFB 0x0008 /*undoc */ +#define MIDI_FIX 0x0004 /*undoc */ +#define IRQ_TO_ISA 0x0001 /*undoc */ + +/* Values for Ring Bus Control B */ +#define RINGB_2CODEC_ID_MASK 0x0003 +#define RINGB_DIS_VALIDATION 0x0008 +#define RINGB_EN_SPDIF 0x0010 +#define RINGB_EN_2CODEC 0x0020 +#define RINGB_SING_BIT_DUAL 0x0040 + +/* ****Port Adresses**** */ + +/* Write & Read */ +#define ESM_INDEX 0x02 +#define ESM_DATA 0x00 + +/* AC97 + RingBus */ +#define ESM_AC97_INDEX 0x30 +#define ESM_AC97_DATA 0x32 +#define ESM_RING_BUS_DEST 0x34 +#define ESM_RING_BUS_CONTR_A 0x36 +#define ESM_RING_BUS_CONTR_B 0x38 +#define ESM_RING_BUS_SDO 0x3A + +/* WaveCache*/ +#define WC_INDEX 0x10 +#define WC_DATA 0x12 +#define WC_CONTROL 0x14 + +/* ASSP*/ +#define ASSP_INDEX 0x80 +#define ASSP_MEMORY 0x82 +#define ASSP_DATA 0x84 +#define ASSP_CONTROL_A 0xA2 +#define ASSP_CONTROL_B 0xA4 +#define ASSP_CONTROL_C 0xA6 +#define ASSP_HOSTW_INDEX 0xA8 +#define ASSP_HOSTW_DATA 0xAA +#define ASSP_HOSTW_IRQ 0xAC +/* Midi */ +#define ESM_MPU401_PORT 0x98 +/* Others */ +#define ESM_PORT_HOST_IRQ 0x18 + +#define IDR0_DATA_PORT 0x00 +#define IDR1_CRAM_POINTER 0x01 +#define IDR2_CRAM_DATA 0x02 +#define IDR3_WAVE_DATA 0x03 +#define IDR4_WAVE_PTR_LOW 0x04 +#define IDR5_WAVE_PTR_HI 0x05 +#define IDR6_TIMER_CTRL 0x06 +#define IDR7_WAVE_ROMRAM 0x07 + +#define WRITEABLE_MAP 0xEFFFFF +#define READABLE_MAP 0x64003F + +/* PCI Register */ + +#define ESM_LEGACY_AUDIO_CONTROL 0x40 +#define ESM_ACPI_COMMAND 0x54 +#define ESM_CONFIG_A 0x50 +#define ESM_CONFIG_B 0x52 +#define ESM_DDMA 0x60 + +/* Bob Bits */ +#define ESM_BOB_ENABLE 0x0001 +#define ESM_BOB_START 0x0001 + +/* Host IRQ Control Bits */ +#define ESM_RESET_MAESTRO 0x8000 +#define ESM_RESET_DIRECTSOUND 0x4000 +#define ESM_HIRQ_ClkRun 0x0100 +#define ESM_HIRQ_HW_VOLUME 0x0040 +#define ESM_HIRQ_HARPO 0x0030 /* What's that? */ +#define ESM_HIRQ_ASSP 0x0010 +#define ESM_HIRQ_DSIE 0x0004 +#define ESM_HIRQ_MPU401 0x0002 +#define ESM_HIRQ_SB 0x0001 + +/* Host IRQ Status Bits */ +#define ESM_MPU401_IRQ 0x02 +#define ESM_SB_IRQ 0x01 +#define ESM_SOUND_IRQ 0x04 +#define ESM_ASSP_IRQ 0x10 +#define ESM_HWVOL_IRQ 0x40 + +#define ESS_SYSCLK 50000000 +#define ESM_BOB_FREQ 200 +#define ESM_BOB_FREQ_MAX 800 + +#define ESM_FREQ_ESM1 (49152000L / 1024L) /* default rate 48000 */ +#define ESM_FREQ_ESM2 (50000000L / 1024L) + +/* APU Modes: reg 0x00, bit 4-7 */ +#define ESM_APU_MODE_SHIFT 4 +#define ESM_APU_MODE_MASK (0xf << 4) +#define ESM_APU_OFF 0x00 +#define ESM_APU_16BITLINEAR 0x01 /* 16-Bit Linear Sample Player */ +#define ESM_APU_16BITSTEREO 0x02 /* 16-Bit Stereo Sample Player */ +#define ESM_APU_8BITLINEAR 0x03 /* 8-Bit Linear Sample Player */ +#define ESM_APU_8BITSTEREO 0x04 /* 8-Bit Stereo Sample Player */ +#define ESM_APU_8BITDIFF 0x05 /* 8-Bit Differential Sample Playrer */ +#define ESM_APU_DIGITALDELAY 0x06 /* Digital Delay Line */ +#define ESM_APU_DUALTAP 0x07 /* Dual Tap Reader */ +#define ESM_APU_CORRELATOR 0x08 /* Correlator */ +#define ESM_APU_INPUTMIXER 0x09 /* Input Mixer */ +#define ESM_APU_WAVETABLE 0x0A /* Wave Table Mode */ +#define ESM_APU_SRCONVERTOR 0x0B /* Sample Rate Convertor */ +#define ESM_APU_16BITPINGPONG 0x0C /* 16-Bit Ping-Pong Sample Player */ +#define ESM_APU_RESERVED1 0x0D /* Reserved 1 */ +#define ESM_APU_RESERVED2 0x0E /* Reserved 2 */ +#define ESM_APU_RESERVED3 0x0F /* Reserved 3 */ + +/* reg 0x00 */ +#define ESM_APU_FILTER_Q_SHIFT 0 +#define ESM_APU_FILTER_Q_MASK (3 << 0) +/* APU Filtey Q Control */ +#define ESM_APU_FILTER_LESSQ 0x00 +#define ESM_APU_FILTER_MOREQ 0x03 + +#define ESM_APU_FILTER_TYPE_SHIFT 2 +#define ESM_APU_FILTER_TYPE_MASK (3 << 2) +#define ESM_APU_ENV_TYPE_SHIFT 8 +#define ESM_APU_ENV_TYPE_MASK (3 << 8) +#define ESM_APU_ENV_STATE_SHIFT 10 +#define ESM_APU_ENV_STATE_MASK (3 << 10) +#define ESM_APU_END_CURVE (1 << 12) +#define ESM_APU_INT_ON_LOOP (1 << 13) +#define ESM_APU_DMA_ENABLE (1 << 14) + +/* reg 0x02 */ +#define ESM_APU_SUBMIX_GROUP_SHIRT 0 +#define ESM_APU_SUBMIX_GROUP_MASK (7 << 0) +#define ESM_APU_SUBMIX_MODE (1 << 3) +#define ESM_APU_6dB (1 << 4) +#define ESM_APU_DUAL_EFFECT (1 << 5) +#define ESM_APU_EFFECT_CHANNELS_SHIFT 6 +#define ESM_APU_EFFECT_CHANNELS_MASK (3 << 6) + +/* reg 0x03 */ +#define ESM_APU_STEP_SIZE_MASK 0x0fff + +/* reg 0x04 */ +#define ESM_APU_PHASE_SHIFT 0 +#define ESM_APU_PHASE_MASK (0xff << 0) +#define ESM_APU_WAVE64K_PAGE_SHIFT 8 /* most 8bit of wave start offset */ +#define ESM_APU_WAVE64K_PAGE_MASK (0xff << 8) + +/* reg 0x05 - wave start offset */ +/* reg 0x06 - wave end offset */ +/* reg 0x07 - wave loop length */ + +/* reg 0x08 */ +#define ESM_APU_EFFECT_GAIN_SHIFT 0 +#define ESM_APU_EFFECT_GAIN_MASK (0xff << 0) +#define ESM_APU_TREMOLO_DEPTH_SHIFT 8 +#define ESM_APU_TREMOLO_DEPTH_MASK (0xf << 8) +#define ESM_APU_TREMOLO_RATE_SHIFT 12 +#define ESM_APU_TREMOLO_RATE_MASK (0xf << 12) + +/* reg 0x09 */ +/* bit 0-7 amplitude dest? */ +#define ESM_APU_AMPLITUDE_NOW_SHIFT 8 +#define ESM_APU_AMPLITUDE_NOW_MASK (0xff << 8) + +/* reg 0x0a */ +#define ESM_APU_POLAR_PAN_SHIFT 0 +#define ESM_APU_POLAR_PAN_MASK (0x3f << 0) +/* Polar Pan Control */ +#define ESM_APU_PAN_CENTER_CIRCLE 0x00 +#define ESM_APU_PAN_MIDDLE_RADIUS 0x01 +#define ESM_APU_PAN_OUTSIDE_RADIUS 0x02 + +#define ESM_APU_FILTER_TUNING_SHIFT 8 +#define ESM_APU_FILTER_TUNING_MASK (0xff << 8) + +/* reg 0x0b */ +#define ESM_APU_DATA_SRC_A_SHIFT 0 +#define ESM_APU_DATA_SRC_A_MASK (0x7f << 0) +#define ESM_APU_INV_POL_A (1 << 7) +#define ESM_APU_DATA_SRC_B_SHIFT 8 +#define ESM_APU_DATA_SRC_B_MASK (0x7f << 8) +#define ESM_APU_INV_POL_B (1 << 15) + +#define ESM_APU_VIBRATO_RATE_SHIFT 0 +#define ESM_APU_VIBRATO_RATE_MASK (0xf << 0) +#define ESM_APU_VIBRATO_DEPTH_SHIFT 4 +#define ESM_APU_VIBRATO_DEPTH_MASK (0xf << 4) +#define ESM_APU_VIBRATO_PHASE_SHIFT 8 +#define ESM_APU_VIBRATO_PHASE_MASK (0xff << 8) + +/* reg 0x0c */ +#define ESM_APU_RADIUS_SELECT (1 << 6) + +/* APU Filter Control */ +#define ESM_APU_FILTER_2POLE_LOPASS 0x00 +#define ESM_APU_FILTER_2POLE_BANDPASS 0x01 +#define ESM_APU_FILTER_2POLE_HIPASS 0x02 +#define ESM_APU_FILTER_1POLE_LOPASS 0x03 +#define ESM_APU_FILTER_1POLE_HIPASS 0x04 +#define ESM_APU_FILTER_OFF 0x05 + +/* APU ATFP Type */ +#define ESM_APU_ATFP_AMPLITUDE 0x00 +#define ESM_APU_ATFP_TREMELO 0x01 +#define ESM_APU_ATFP_FILTER 0x02 +#define ESM_APU_ATFP_PAN 0x03 + +/* APU ATFP Flags */ +#define ESM_APU_ATFP_FLG_OFF 0x00 +#define ESM_APU_ATFP_FLG_WAIT 0x01 +#define ESM_APU_ATFP_FLG_DONE 0x02 +#define ESM_APU_ATFP_FLG_INPROCESS 0x03 + + +/* capture mixing buffer size */ +#define ESM_MIXBUF_SIZE 512 + +#define ESM_MODE_PLAY 0 +#define ESM_MODE_CAPTURE 1 + +/* acpi states */ +enum { + ACPI_D0=0, + ACPI_D1, + ACPI_D2, + ACPI_D3 +}; + +/* bits in the acpi masks */ +#define ACPI_12MHZ ( 1 << 15) +#define ACPI_24MHZ ( 1 << 14) +#define ACPI_978 ( 1 << 13) +#define ACPI_SPDIF ( 1 << 12) +#define ACPI_GLUE ( 1 << 11) +#define ACPI__10 ( 1 << 10) /* reserved */ +#define ACPI_PCIINT ( 1 << 9) +#define ACPI_HV ( 1 << 8) /* hardware volume */ +#define ACPI_GPIO ( 1 << 7) +#define ACPI_ASSP ( 1 << 6) +#define ACPI_SB ( 1 << 5) /* sb emul */ +#define ACPI_FM ( 1 << 4) /* fm emul */ +#define ACPI_RB ( 1 << 3) /* ringbus / aclink */ +#define ACPI_MIDI ( 1 << 2) +#define ACPI_GP ( 1 << 1) /* game port */ +#define ACPI_WP ( 1 << 0) /* wave processor */ + +#define ACPI_ALL (0xffff) +#define ACPI_SLEEP (~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \ + ACPI_MIDI|ACPI_GP|ACPI_WP)) +#define ACPI_NONE (ACPI__10) + +/* these masks indicate which units we care about at + which states */ +static u16 acpi_state_mask[] = { + [ACPI_D0] = ACPI_ALL, + [ACPI_D1] = ACPI_SLEEP, + [ACPI_D2] = ACPI_SLEEP, + [ACPI_D3] = ACPI_NONE +}; + + +typedef struct snd_es1968 es1968_t; +typedef struct snd_esschan esschan_t; +typedef struct snd_esm_memory esm_memory_t; + +/* APU use in the driver */ +enum snd_enum_apu_type { + ESM_APU_PCM_PLAY, + ESM_APU_PCM_CAPTURE, + ESM_APU_PCM_RATECONV, + ESM_APU_FREE +}; + +/* DMA Hack! */ +struct snd_esm_memory { + char *buf; + unsigned long addr; + int size; + int empty; /* status */ + struct list_head list; +}; + +/* Playback Channel */ +struct snd_esschan { + int running; + + u8 apu[4]; + u8 apu_mode[4]; + + /* playback/capture pcm buffer */ + esm_memory_t *memory; + /* capture mixer buffer */ + esm_memory_t *mixbuf; + + unsigned int hwptr; /* current hw pointer in bytes */ + unsigned int count; /* sample counter in bytes */ + unsigned int dma_size; /* total buffer size in bytes */ + unsigned int frag_size; /* period size in bytes */ + unsigned int wav_shift; + u16 base[4]; /* offset for ptr */ + + /* stereo/16bit flag */ + unsigned char fmt; + int mode; /* playback / capture */ + + int bob_freq; /* required timer frequency */ + + snd_pcm_substream_t *substream; + + /* linked list */ + struct list_head list; + +#ifdef CONFIG_PM + u16 wc_map[4]; +#endif +}; + +struct snd_es1968 { + /* Module Config */ + int total_bufsize; /* in bytes */ + + int playback_streams, capture_streams; + + unsigned int clock; /* clock */ + + /* buffer */ + void *dma_buf; + dma_addr_t dma_buf_addr; + unsigned long dma_buf_size; + + /* Resources... */ + int irq; + unsigned long io_port; + struct resource *res_io_port; + int type; + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + + /* DMA memory block */ + struct list_head buf_list; + + /* ALSA Stuff */ + ac97_t *ac97; + snd_kcontrol_t *master_switch; /* for h/w volume control */ + snd_kcontrol_t *master_volume; + + snd_rawmidi_t *rmidi; + + spinlock_t reg_lock; + struct tasklet_struct hwvol_tq; + + /* Maestro Stuff */ + u16 maestro_map[32]; + atomic_t bobclient; /* active timer instancs */ + int bob_freq; /* timer frequency */ + spinlock_t bob_lock; + struct semaphore memory_mutex; /* memory lock */ + + /* APU states */ + unsigned char apu[NR_APUS]; + + /* active substreams */ + struct list_head substream_list; + spinlock_t substream_lock; + +#ifdef CONFIG_PM + u16 apu_map[NR_APUS][NR_APU_REGS]; +#endif +}; + +static void snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +#define CARD_TYPE_ESS_ESOLDM1 0x12850100 +#define CARD_TYPE_ESS_ES1968 0x125d1968 +#define CARD_TYPE_ESS_ES1978 0x125d1978 + +static struct pci_device_id snd_es1968_ids[] __devinitdata = { + /* Maestro 1 */ + { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, + /* Maestro 2 */ + { 0x125d, 0x1968, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, + /* Maestro 2E */ + { 0x125d, 0x1978, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_es1968_ids); + +/* ********************* + * Low Level Funcs! * + *********************/ + +/* no spinlock */ +static void __maestro_write(es1968_t *chip, u16 reg, u16 data) +{ + outw(reg, chip->io_port + ESM_INDEX); + outw(data, chip->io_port + ESM_DATA); + chip->maestro_map[reg] = data; +} + +inline static void maestro_write(es1968_t *chip, u16 reg, u16 data) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + __maestro_write(chip, reg, data); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* no spinlock */ +static u16 __maestro_read(es1968_t *chip, u16 reg) +{ + if (READABLE_MAP & (1 << reg)) { + outw(reg, chip->io_port + ESM_INDEX); + chip->maestro_map[reg] = inw(chip->io_port + ESM_DATA); + } + return chip->maestro_map[reg]; +} + +inline static u16 maestro_read(es1968_t *chip, u16 reg) +{ + unsigned long flags; + u16 result; + spin_lock_irqsave(&chip->reg_lock, flags); + result = __maestro_read(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +/* Wait for the codec bus to be free */ +static int snd_es1968_ac97_wait(es1968_t *chip) +{ + int timeout = 100000; + + while (timeout-- > 0) { + if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1)) + return 0; + } + snd_printd("es1968: ac97 timeout\n"); + return 1; /* timeout */ +} + +static void snd_es1968_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) +{ + es1968_t *chip = snd_magic_cast(es1968_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + + snd_es1968_ac97_wait(chip); + + /* Write the bus */ + outw(val, chip->io_port + ESM_AC97_DATA); + mdelay(1); + outb(reg, chip->io_port + ESM_AC97_INDEX); + mdelay(1); + + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static unsigned short snd_es1968_ac97_read(ac97_t *ac97, unsigned short reg) +{ + u16 data = 0; + es1968_t *chip = snd_magic_cast(es1968_t, ac97->private_data, return 0); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + + snd_es1968_ac97_wait(chip); + + outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX); + mdelay(1); + + if (! snd_es1968_ac97_wait(chip)) { + data = inw(chip->io_port + ESM_AC97_DATA); + mdelay(1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return data; +} + +/* no spinlock */ +static void apu_index_set(es1968_t *chip, u16 index) +{ + int i; + __maestro_write(chip, IDR1_CRAM_POINTER, index); + for (i = 0; i < 1000; i++) + if (__maestro_read(chip, IDR1_CRAM_POINTER) == index) + return; + snd_printd("es1968: APU register select failed. (Timeout)\n"); +} + +/* no spinlock */ +static void apu_data_set(es1968_t *chip, u16 data) +{ + int i; + for (i = 0; i < 1000; i++) { + if (__maestro_read(chip, IDR0_DATA_PORT) == data) + return; + __maestro_write(chip, IDR0_DATA_PORT, data); + } + snd_printd("es1968: APU register set probably failed (Timeout)!\n"); +} + +/* no spinlock */ +static void __apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data) +{ + snd_assert(channel < NR_APUS, return); +#ifdef CONFIG_PM + chip->apu_map[channel][reg] = data; +#endif + reg |= (channel << 4); + apu_index_set(chip, reg); + apu_data_set(chip, data); +} + +inline static void apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + __apu_set_register(chip, channel, reg, data); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static u16 __apu_get_register(es1968_t *chip, u16 channel, u8 reg) +{ + snd_assert(channel < NR_APUS, return 0); + reg |= (channel << 4); + apu_index_set(chip, reg); + return __maestro_read(chip, IDR0_DATA_PORT); +} + +inline static u16 apu_get_register(es1968_t *chip, u16 channel, u8 reg) +{ + unsigned long flags; + u16 v; + spin_lock_irqsave(&chip->reg_lock, flags); + v = __apu_get_register(chip, channel, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return v; +} + +#if 0 /* ASSP is not supported */ + +static void assp_set_register(es1968_t *chip, u32 reg, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + outl(reg, chip->io_port + ASSP_INDEX); + outl(value, chip->io_port + ASSP_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static u32 assp_get_register(es1968_t *chip, u32 reg) +{ + unsigned long flags; + u32 value; + + spin_lock_irqsave(&chip->reg_lock, flags); + outl(reg, chip->io_port + ASSP_INDEX); + value = inl(chip->io_port + ASSP_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return value; +} + +#endif + +static void wave_set_register(es1968_t *chip, u16 reg, u16 value) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + outw(reg, chip->io_port + WC_INDEX); + outw(value, chip->io_port + WC_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static u16 wave_get_register(es1968_t *chip, u16 reg) +{ + unsigned long flags; + u16 value; + + spin_lock_irqsave(&chip->reg_lock, flags); + outw(reg, chip->io_port + WC_INDEX); + value = inw(chip->io_port + WC_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return value; +} + +/* ******************* + * Bob the Timer! * + *******************/ + +static void snd_es1968_bob_stop(es1968_t *chip) +{ + u16 reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + reg = __maestro_read(chip, 0x11); + reg &= ~ESM_BOB_ENABLE; + __maestro_write(chip, 0x11, reg); + reg = __maestro_read(chip, 0x17); + reg &= ~ESM_BOB_START; + __maestro_write(chip, 0x17, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_es1968_bob_start(es1968_t *chip) +{ + int prescale; + int divide; + unsigned long flags; + + /* compute ideal interrupt frequency for buffer size & play rate */ + /* first, find best prescaler value to match freq */ + for (prescale = 5; prescale < 12; prescale++) + if (chip->bob_freq > (ESS_SYSCLK >> (prescale + 9))) + break; + + /* next, back off prescaler whilst getting divider into optimum range */ + divide = 1; + while ((prescale > 5) && (divide < 32)) { + prescale--; + divide <<= 1; + } + divide >>= 1; + + /* now fine-tune the divider for best match */ + for (; divide < 31; divide++) + if (chip->bob_freq > + ((ESS_SYSCLK >> (prescale + 9)) / (divide + 1))) break; + + /* divide = 0 is illegal, but don't let prescale = 4! */ + if (divide == 0) { + divide++; + if (prescale > 5) + prescale--; + } else if (divide > 1) + divide--; + + spin_lock_irqsave(&chip->reg_lock, flags); + __maestro_write(chip, 6, 0x9000 | (prescale << 5) | divide); /* set reg */ + + /* Now set IDR 11/17 */ + __maestro_write(chip, 0x11, __maestro_read(chip, 0x11) | 1); + __maestro_write(chip, 0x17, __maestro_read(chip, 0x17) | 1); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_es1968_bob_inc(es1968_t *chip, int freq) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->bob_lock, flags); + atomic_inc(&chip->bobclient); + if (atomic_read(&chip->bobclient) == 1) { + chip->bob_freq = freq; + snd_es1968_bob_start(chip); + } else if (chip->bob_freq < freq) { + snd_es1968_bob_stop(chip); + chip->bob_freq = freq; + snd_es1968_bob_start(chip); + } + spin_unlock_irqrestore(&chip->bob_lock, flags); +} + +static void snd_es1968_bob_dec(es1968_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->bob_lock, flags); + atomic_dec(&chip->bobclient); + if (atomic_read(&chip->bobclient) <= 0) + snd_es1968_bob_stop(chip); + else if (chip->bob_freq > ESM_BOB_FREQ) { + /* check reduction of timer frequency */ + struct list_head *p; + int max_freq = ESM_BOB_FREQ; + spin_lock(&chip->substream_lock); + list_for_each(p, &chip->substream_list) { + esschan_t *es = list_entry(p, esschan_t, list); + if (max_freq < es->bob_freq) + max_freq = es->bob_freq; + } + spin_unlock(&chip->substream_lock); + if (max_freq != chip->bob_freq) { + snd_es1968_bob_stop(chip); + chip->bob_freq = max_freq; + snd_es1968_bob_start(chip); + } + } + spin_unlock_irqrestore(&chip->bob_lock, flags); +} + +static int +snd_es1968_calc_bob_rate(es1968_t *chip, esschan_t *es, + snd_pcm_runtime_t *runtime) +{ + /* we acquire 4 interrupts per period for precise control.. */ + int freq = runtime->rate * 4; + if (es->fmt & ESS_FMT_STEREO) + freq <<= 1; + if (es->fmt & ESS_FMT_16BIT) + freq <<= 1; + freq /= es->frag_size; + if (freq < ESM_BOB_FREQ) + freq = ESM_BOB_FREQ; + else if (freq > ESM_BOB_FREQ_MAX) + freq = ESM_BOB_FREQ_MAX; + return freq; +} + + +/************* + * PCM Part * + *************/ + +static u32 snd_es1968_compute_rate(es1968_t *chip, u32 freq) +{ + u32 rate = (freq << 16) / chip->clock; +#if 0 /* XXX: do we need this? */ + if (rate > 0x10000) + rate = 0x10000; +#endif + return rate; +} + +/* get current pointer */ +inline static unsigned int +snd_es1968_get_dma_ptr(es1968_t *chip, esschan_t *es) +{ + unsigned int offset; + + offset = apu_get_register(chip, es->apu[0], 5); + + offset -= es->base[0]; + + return (offset & 0xFFFE); /* hardware is in words */ +} + +static void snd_es1968_apu_set_freq(es1968_t *chip, int apu, int freq) +{ + apu_set_register(chip, apu, 2, + (apu_get_register(chip, apu, 2) & 0x00FF) | + ((freq & 0xff) << 8) | 0x10); + apu_set_register(chip, apu, 3, freq >> 8); +} + +/* spin lock held */ +inline static void snd_es1968_trigger_apu(es1968_t *esm, int apu, int mode) +{ + /* dma on, no envelopes, filter to all 1s) */ + __apu_set_register(esm, apu, 0, 0x400f | mode); +} + +static void snd_es1968_pcm_start(es1968_t *chip, esschan_t *es) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (es->running) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + __apu_set_register(chip, es->apu[0], 5, es->base[0]); + snd_es1968_trigger_apu(chip, es->apu[0], es->apu_mode[0]); + if (es->mode == ESM_MODE_CAPTURE) { + __apu_set_register(chip, es->apu[2], 5, es->base[2]); + snd_es1968_trigger_apu(chip, es->apu[2], es->apu_mode[2]); + } + if (es->fmt & ESS_FMT_STEREO) { + __apu_set_register(chip, es->apu[1], 5, es->base[1]); + snd_es1968_trigger_apu(chip, es->apu[1], es->apu_mode[1]); + if (es->mode == ESM_MODE_CAPTURE) { + __apu_set_register(chip, es->apu[3], 5, es->base[3]); + snd_es1968_trigger_apu(chip, es->apu[3], es->apu_mode[3]); + } + } + es->running = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_es1968_pcm_stop(es1968_t *chip, esschan_t *es) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (! es->running) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + snd_es1968_trigger_apu(chip, es->apu[0], 0); + snd_es1968_trigger_apu(chip, es->apu[1], 0); + if (es->mode == ESM_MODE_CAPTURE) { + snd_es1968_trigger_apu(chip, es->apu[2], 0); + snd_es1968_trigger_apu(chip, es->apu[3], 0); + } + es->running = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* set the wavecache control reg */ +static void snd_es1968_program_wavecache(es1968_t *chip, esschan_t *es, + int channel, u32 addr, int capture) +{ + u32 tmpval = (addr - 0x10) & 0xFFF8; + + if (! capture) { + if (!(es->fmt & ESS_FMT_16BIT)) + tmpval |= 4; /* 8bit */ + if (es->fmt & ESS_FMT_STEREO) + tmpval |= 2; /* stereo */ + } + + /* set the wavecache control reg */ + wave_set_register(chip, es->apu[channel] << 3, tmpval); + +#ifdef CONFIG_PM + es->wc_map[channel] = tmpval; +#endif +} + + +static void snd_es1968_playback_setup(es1968_t *chip, esschan_t *es, + snd_pcm_runtime_t *runtime) +{ + u32 pa; + int high_apu = 0; + int channel, apu; + int i, size; + unsigned long flags; + u32 freq; + + size = es->dma_size >> es->wav_shift; + + if (es->fmt & ESS_FMT_STEREO) + high_apu++; + + for (channel = 0; channel <= high_apu; channel++) { + apu = es->apu[channel]; + + snd_es1968_program_wavecache(chip, es, channel, es->memory->addr, 0); + + /* Offset to PCMBAR */ + pa = es->memory->addr; + pa -= chip->dma_buf_addr; + pa >>= 1; /* words */ + + pa |= 0x00400000; /* System RAM (Bit 22) */ + + if (es->fmt & ESS_FMT_STEREO) { + /* Enable stereo */ + if (channel) + pa |= 0x00800000; /* (Bit 23) */ + if (es->fmt & ESS_FMT_16BIT) + pa >>= 1; + } + + /* base offset of dma calcs when reading the pointer + on this left one */ + es->base[channel] = pa & 0xFFFF; + + for (i = 0; i < 16; i++) + apu_set_register(chip, apu, i, 0x0000); + + /* Load the buffer into the wave engine */ + apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8); + apu_set_register(chip, apu, 5, pa & 0xFFFF); + apu_set_register(chip, apu, 6, (pa + size) & 0xFFFF); + /* setting loop == sample len */ + apu_set_register(chip, apu, 7, size); + + /* clear effects/env.. */ + apu_set_register(chip, apu, 8, 0x0000); + /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ + apu_set_register(chip, apu, 9, 0xD000); + + /* clear routing stuff */ + apu_set_register(chip, apu, 11, 0x0000); + /* dma on, no envelopes, filter to all 1s) */ + // apu_set_register(chip, apu, 0, 0x400F); + + if (es->fmt & ESS_FMT_16BIT) + es->apu_mode[channel] = 0x10; /* 16bit mono */ + else + es->apu_mode[channel] = 0x30; /* 8bit mono */ + + if (es->fmt & ESS_FMT_STEREO) { + /* set panning: left or right */ + /* Check: different panning. On my Canyon 3D Chipset the + Channels are swapped. I don't know, about the output + to the SPDif Link. Perhaps you have to change this + and not the APU Regs 4-5. */ + apu_set_register(chip, apu, 10, + 0x8F00 | (channel ? 0 : 0x10)); + es->apu_mode[channel] += 0x10; /* stereo */ + } else + apu_set_register(chip, apu, 10, 0x8F08); + } + + spin_lock_irqsave(&chip->reg_lock, flags); + /* clear WP interupts */ + outw(1, chip->io_port + 0x04); + /* enable WP ints */ + outw(inw(chip->io_port + 0x18) | 4, chip->io_port + 0x18); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + freq = runtime->rate; + /* set frequency */ + if (freq > 48000) + freq = 48000; + if (freq < 4000) + freq = 4000; + + /* hmmm.. */ + if (!(es->fmt & ESS_FMT_16BIT) && !(es->fmt & ESS_FMT_STEREO)) + freq >>= 1; + + freq = snd_es1968_compute_rate(chip, freq); + + /* Load the frequency, turn on 6dB */ + snd_es1968_apu_set_freq(chip, es->apu[0], freq); + snd_es1968_apu_set_freq(chip, es->apu[1], freq); +} + +static void snd_es1968_capture_setup(es1968_t *chip, esschan_t *es, + snd_pcm_runtime_t *runtime) +{ + int apu_step = 2; + int channel, apu; + int i, size; + u32 freq; + unsigned long flags; + + size = es->dma_size >> es->wav_shift; + + /* we're given the full size of the buffer, but + in stereo each channel will only use its half */ + if (es->fmt & ESS_FMT_STEREO) + apu_step = 1; + + /* APU assignments: + 0 = mono/left SRC + 1 = right SRC + 2 = mono/left Input Mixer + 3 = right Input Mixer */ + for (channel = 0; channel < 4; channel += apu_step) { + int bsize, route; + u32 pa; + + apu = es->apu[channel]; + + /* data seems to flow from the codec, through an apu into + the 'mixbuf' bit of page, then through the SRC apu + and out to the real 'buffer'. ok. sure. */ + + if (channel & 2) { + /* ok, we're an input mixer going from adc + through the mixbuf to the other apus */ + + if (!(channel & 0x01)) { + pa = es->mixbuf->addr; + } else { + pa = es->mixbuf->addr + ESM_MIXBUF_SIZE / 2; + } + + /* we source from a 'magic' apu */ + bsize = ESM_MIXBUF_SIZE / 4; /* half of this channels alloc, in words */ + /* parallel in crap, see maestro reg 0xC [8-11] */ + route = 0x14 + channel - 2; + es->apu_mode[channel] = 0x90; /* Input Mixer */ + } else { + /* we're a rate converter taking + input from the input apus and outputing it to + system memory */ + if (!(channel & 0x01)) + pa = es->memory->addr; + else + pa = es->memory->addr + size * 2; /* size is in word */ + + es->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ + + bsize = size; + /* get input from inputing apu */ + route = es->apu[channel + 2]; + } + + /* set the wavecache control reg */ + snd_es1968_program_wavecache(chip, es, channel, pa, 1); + + /* Offset to PCMBAR */ + pa -= chip->dma_buf_addr; + pa >>= 1; /* words */ + + /* base offset of dma calcs when reading the pointer + on this left one */ + es->base[channel] = pa & 0xFFFF; + + pa |= 0x00400000; /* bit 22 -> System RAM */ + + /* Begin loading the APU */ + for (i = 0; i < 16; i++) + apu_set_register(chip, apu, i, 0x0000); + + /* need to enable subgroups.. and we should probably + have different groups for different /dev/dsps.. */ + apu_set_register(chip, apu, 2, 0x8); + + /* Load the buffer into the wave engine */ + apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8); + /* XXX reg is little endian.. */ + apu_set_register(chip, apu, 5, pa & 0xFFFF); + apu_set_register(chip, apu, 6, (pa + bsize) & 0xFFFF); + apu_set_register(chip, apu, 7, bsize); +#if 0 + if (es->fmt & ESS_FMT_STEREO) /* ??? really ??? */ + apu_set_register(chip, apu, 7, bsize - 1); +#endif + + /* clear effects/env.. */ + apu_set_register(chip, apu, 8, 0x00F0); + /* amplitude now? sure. why not. */ + apu_set_register(chip, apu, 9, 0x0000); + /* set filter tune, radius, polar pan */ + apu_set_register(chip, apu, 10, 0x8F08); + /* route input */ + apu_set_register(chip, apu, 11, route); + /* dma on, no envelopes, filter to all 1s) */ + // apu_set_register(chip, apu, 0, 0x400F); + } + + spin_lock_irqsave(&chip->reg_lock, flags); + /* clear WP interupts */ + outw(1, chip->io_port + 0x04); + /* enable WP ints */ + outw(inw(chip->io_port + 0x18) | 4, chip->io_port + 0x18); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + freq = runtime->rate; + /* Sample Rate conversion APUs don't like 0x10000 for their rate */ + if (freq > 47999) + freq = 47999; + if (freq < 4000) + freq = 4000; + + freq = snd_es1968_compute_rate(chip, freq); + + /* Load the frequency, turn on 6dB */ + snd_es1968_apu_set_freq(chip, es->apu[0], freq); + snd_es1968_apu_set_freq(chip, es->apu[1], freq); + + /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ + freq = 0x10000; + snd_es1968_apu_set_freq(chip, es->apu[2], freq); + snd_es1968_apu_set_freq(chip, es->apu[3], freq); +} + +/******************* + * ALSA Interface * + *******************/ + +static int snd_es1968_pcm_prepare(snd_pcm_substream_t *substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *es = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + + es->dma_size = snd_pcm_lib_buffer_bytes(substream); + es->frag_size = snd_pcm_lib_period_bytes(substream); + + es->wav_shift = 1; /* maestro handles always 16bit */ + es->fmt = 0; + if (snd_pcm_format_width(runtime->format) == 16) + es->fmt |= ESS_FMT_16BIT; + if (runtime->channels > 1) { + es->fmt |= ESS_FMT_STEREO; + if (es->fmt & ESS_FMT_16BIT) /* 8bit is already word shifted */ + es->wav_shift++; + } + es->bob_freq = snd_es1968_calc_bob_rate(chip, es, runtime); + + switch (es->mode) { + case ESM_MODE_PLAY: + snd_es1968_playback_setup(chip, es, runtime); + break; + case ESM_MODE_CAPTURE: + snd_es1968_capture_setup(chip, es, runtime); + break; + } + + return 0; +} + +static int snd_es1968_pcm_trigger(snd_pcm_substream_t *substream, int cmd) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + unsigned long flags; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (es->running) + return 0; + snd_es1968_bob_inc(chip, es->bob_freq); + es->count = 0; + es->hwptr = 0; + snd_es1968_pcm_start(chip, es); + spin_lock_irqsave(&chip->substream_lock, flags); + list_add(&es->list, &chip->substream_list); + spin_unlock_irqrestore(&chip->substream_lock, flags); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (! es->running) + return 0; + snd_es1968_pcm_stop(chip, es); + spin_lock_irqsave(&chip->substream_lock, flags); + list_del(&es->list); + spin_unlock_irqrestore(&chip->substream_lock, flags); + snd_es1968_bob_dec(chip); + break; + } + return 0; +} + +static snd_pcm_uframes_t snd_es1968_pcm_pointer(snd_pcm_substream_t *substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + unsigned int ptr; + + ptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift; + + return bytes_to_frames(substream->runtime, ptr % es->dma_size); +} + +static snd_pcm_hardware_t snd_es1968_playback = { + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 256, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_es1968_capture = { + info: (SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + formats: /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 256, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* ************************* + * DMA memory management * + *************************/ + +/* Because the Maestro can only take adresses relative to the PCM base adress + register :( */ + +static int calc_available_memory_size(es1968_t *chip) +{ + struct list_head *p; + int max_size = 0; + + down(&chip->memory_mutex); + list_for_each(p, &chip->buf_list) { + esm_memory_t *buf = list_entry(p, esm_memory_t, list); + if (buf->empty && buf->size > max_size) + max_size = buf->size; + } + up(&chip->memory_mutex); + if (max_size >= 128*1024) + max_size = 127*1024; + return max_size; +} + +/* allocate a new memory chunk with the specified size */ +static esm_memory_t *snd_es1968_new_memory(es1968_t *chip, int size) +{ + esm_memory_t *buf; + struct list_head *p; + + down(&chip->memory_mutex); + list_for_each(p, &chip->buf_list) { + buf = list_entry(p, esm_memory_t, list); + if (buf->empty && buf->size >= size) + goto __found; + } + up(&chip->memory_mutex); + return NULL; + +__found: + if (buf->size > size) { + esm_memory_t *chunk = kmalloc(sizeof(*chunk), GFP_KERNEL); + if (chunk == NULL) + return NULL; + chunk->size = buf->size - size; + chunk->buf = buf->buf + size; + chunk->addr = buf->addr + size; + chunk->empty = 1; + buf->size = size; + list_add(&chunk->list, &buf->list); + } + buf->empty = 0; + up(&chip->memory_mutex); + return buf; +} + +/* free a memory chunk */ +static void snd_es1968_free_memory(es1968_t *chip, esm_memory_t *buf) +{ + esm_memory_t *chunk; + + down(&chip->memory_mutex); + buf->empty = 1; + if (buf->list.prev != &chip->buf_list) { + chunk = list_entry(buf->list.prev, esm_memory_t, list); + if (chunk->empty) { + chunk->size += buf->size; + list_del(&buf->list); + kfree(buf); + buf = chunk; + } + } + if (buf->list.next != &chip->buf_list) { + chunk = list_entry(buf->list.next, esm_memory_t, list); + if (chunk->empty) { + buf->size += chunk->size; + list_del(&chunk->list); + kfree(chunk); + } + } + up(&chip->memory_mutex); +} + +static void snd_es1968_free_dmabuf(es1968_t *chip) +{ + struct list_head *p; + + if (! chip->dma_buf) + return; + snd_free_pci_pages(chip->pci, chip->dma_buf_size, chip->dma_buf, chip->dma_buf_addr); + while ((p = chip->buf_list.next) != &chip->buf_list) { + esm_memory_t *chunk = list_entry(p, esm_memory_t, list); + list_del(p); + kfree(chunk); + } +} + +static int __devinit +snd_es1968_init_dmabuf(es1968_t *chip) +{ + esm_memory_t *chunk; + chip->dma_buf = snd_malloc_pci_pages_fallback(chip->pci, chip->total_bufsize, + &chip->dma_buf_addr, &chip->dma_buf_size); + //snd_printd("es1968: allocated buffer size %ld at %p\n", chip->dma_buf_size, chip->dma_buf); + if (chip->dma_buf == NULL) { + snd_printk("es1968: can't allocate dma pages for size %d\n", + chip->total_bufsize); + return -ENOMEM; + } + if ((chip->dma_buf_addr + chip->dma_buf_size - 1) & ~((1 << 28) - 1)) { + snd_es1968_free_dmabuf(chip); + snd_printk("es1968: DMA buffer beyond 256MB.\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&chip->buf_list); + /* allocate an empty chunk */ + chunk = kmalloc(sizeof(*chunk), GFP_KERNEL); + if (chunk == NULL) { + snd_es1968_free_dmabuf(chip); + return -ENOMEM; + } + memset(chip->dma_buf, 0, 512); + chunk->buf = chip->dma_buf + 512; + chunk->addr = chip->dma_buf_addr + 512; + chunk->size = chip->dma_buf_size - 512; + chunk->empty = 1; + list_add(&chunk->list, &chip->buf_list); + + return 0; +} + +/* setup the dma_areas */ +/* buffer is extracted from the pre-allocated memory chunk */ +static int snd_es1968_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *chan = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + int size = params_buffer_bytes(hw_params); + + if (chan->memory) { + if (chan->memory->size >= size) { + runtime->dma_bytes = size; + return 0; + } + snd_es1968_free_memory(chip, chan->memory); + } + chan->memory = snd_es1968_new_memory(chip, size); + if (chan->memory == NULL) { + // snd_printd("cannot allocate dma buffer: size = %d\n", size); + return -ENOMEM; + } + runtime->dma_bytes = size; + runtime->dma_area = chan->memory->buf; + runtime->dma_addr = chan->memory->addr; + return 1; /* area was changed */ +} + +/* remove dma areas if allocated */ +static int snd_es1968_hw_free(snd_pcm_substream_t * substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *chan; + + if (runtime->private_data == NULL) + return 0; + chan = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + if (chan->memory) { + snd_es1968_free_memory(chip, chan->memory); + chan->memory = NULL; + } + return 0; +} + + +/* + * allocate APU pair + */ +static int snd_es1968_alloc_apu_pair(es1968_t *chip, int type) +{ + int apu; + + for (apu = 0; apu < NR_APUS; apu += 2) { + if (chip->apu[apu] == ESM_APU_FREE && + chip->apu[apu + 1] == ESM_APU_FREE) { + chip->apu[apu] = chip->apu[apu + 1] = type; + return apu; + } + } + return -EBUSY; +} + +/* + * release APU pair + */ +static void snd_es1968_free_apu_pair(es1968_t *chip, int apu) +{ + chip->apu[apu] = chip->apu[apu + 1] = ESM_APU_FREE; +} + + +/****************** + * PCM open/close * + ******************/ + +static int snd_es1968_playback_open(snd_pcm_substream_t *substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *es; + int apu1; + + /* search 2 APUs */ + apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY); + if (apu1 < 0) + return apu1; + + es = snd_magic_kcalloc(esschan_t, 0, GFP_KERNEL); + if (!es) { + snd_es1968_free_apu_pair(chip, apu1); + return -ENOMEM; + } + + es->apu[0] = apu1; + es->apu[1] = apu1 + 1; + es->apu_mode[0] = 0; + es->apu_mode[1] = 0; + es->running = 0; + es->substream = substream; + es->mode = ESM_MODE_PLAY; + INIT_LIST_HEAD(&es->list); + + runtime->private_data = es; + runtime->hw = snd_es1968_playback; + runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max = + calc_available_memory_size(chip); + + return 0; +} + +static int snd_es1968_capture_copy(snd_pcm_substream_t *substream, + int channel, snd_pcm_uframes_t pos, + void *buf, snd_pcm_uframes_t count) +{ + //es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *es = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + char *src = runtime->dma_area; + + if (runtime->channels == 1) + return copy_to_user(buf, src + pos, count) ? -EFAULT : 0; + else { + count /= 2; + pos /= 2; + if (copy_to_user(buf, src + pos, count)) + return -EFAULT; + if (copy_to_user(buf + count, src + pos + es->dma_size/2, count)) + return -EFAULT; + return 0; + } +} + +static int snd_es1968_capture_open(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es; + int apu1, apu2; + + apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE); + if (apu1 < 0) + return apu1; + apu2 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_RATECONV); + if (apu2 < 0) { + snd_es1968_free_apu_pair(chip, apu1); + return apu2; + } + + es = snd_magic_kcalloc(esschan_t, 0, GFP_KERNEL); + if (!es) { + snd_es1968_free_apu_pair(chip, apu1); + snd_es1968_free_apu_pair(chip, apu2); + return -ENOMEM; + } + + es->apu[0] = apu1; + es->apu[1] = apu1 + 1; + es->apu[2] = apu2; + es->apu[3] = apu2 + 1; + es->apu_mode[0] = 0; + es->apu_mode[1] = 0; + es->apu_mode[2] = 0; + es->apu_mode[3] = 0; + es->running = 0; + es->substream = substream; + es->mode = ESM_MODE_CAPTURE; + INIT_LIST_HEAD(&es->list); + + /* get mixbuffer */ + if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) { + snd_es1968_free_apu_pair(chip, apu1); + snd_es1968_free_apu_pair(chip, apu2); + snd_magic_kfree(es); + return -ENOMEM; + } + + runtime->private_data = es; + runtime->hw = snd_es1968_capture; + runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max = + calc_available_memory_size(chip) - 1024; + + return 0; +} + +static int snd_es1968_playback_close(snd_pcm_substream_t * substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es; + if (substream->runtime->private_data == NULL) + return 0; + es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + snd_es1968_free_apu_pair(chip, es->apu[0]); + snd_magic_kfree(es); + + return 0; +} + +static int snd_es1968_capture_close(snd_pcm_substream_t * substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es; + if (substream->runtime->private_data == NULL) + return 0; + es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + snd_es1968_free_memory(chip, es->mixbuf); + snd_es1968_free_apu_pair(chip, es->apu[0]); + snd_es1968_free_apu_pair(chip, es->apu[2]); + snd_magic_kfree(es); + + return 0; +} + +static snd_pcm_ops_t snd_es1968_playback_ops = { + open: snd_es1968_playback_open, + close: snd_es1968_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_es1968_hw_params, + hw_free: snd_es1968_hw_free, + prepare: snd_es1968_pcm_prepare, + trigger: snd_es1968_pcm_trigger, + pointer: snd_es1968_pcm_pointer, +}; + +static snd_pcm_ops_t snd_es1968_capture_ops = { + open: snd_es1968_capture_open, + close: snd_es1968_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_es1968_hw_params, + hw_free: snd_es1968_hw_free, + prepare: snd_es1968_pcm_prepare, + trigger: snd_es1968_pcm_trigger, + pointer: snd_es1968_pcm_pointer, + copy: snd_es1968_capture_copy, +}; + + +/* + * measure clock + */ +#define CLOCK_MEASURE_BUFSIZE 16768 /* enough large for a single shot */ + +static void __devinit es1968_measure_clock(es1968_t *chip) +{ + int i, apu; + unsigned int pa, offset, t; + esm_memory_t *memory; + unsigned long flags; + struct timeval start_time, stop_time; + + if (chip->clock == 0) + chip->clock = 48000; /* default clock value */ + + /* search 2 APUs (although one apu is enough) */ + if ((apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY)) < 0) { + snd_printk("Hmm, cannot find empty APU pair!?\n"); + return; + } + if ((memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE)) == NULL) { + snd_printk("cannot allocate dma buffer - using default clock %d\n", chip->clock); + snd_es1968_free_apu_pair(chip, apu); + return; + } + + memset(memory->buf, 0, CLOCK_MEASURE_BUFSIZE); + + wave_set_register(chip, apu << 3, (memory->addr - 0x10) & 0xfff8); + + pa = (unsigned int)((memory->addr - chip->dma_buf_addr) >> 1); + pa |= 0x00400000; /* System RAM (Bit 22) */ + + /* initialize apu */ + for (i = 0; i < 16; i++) + apu_set_register(chip, apu, i, 0x0000); + + apu_set_register(chip, apu, 4, ((pa >> 16) & 0xff) << 8); + apu_set_register(chip, apu, 5, pa & 0xffff); + apu_set_register(chip, apu, 6, (pa + CLOCK_MEASURE_BUFSIZE/2) & 0xffff); + apu_set_register(chip, apu, 7, CLOCK_MEASURE_BUFSIZE/2); + apu_set_register(chip, apu, 8, 0x0000); + apu_set_register(chip, apu, 9, 0xD000); + apu_set_register(chip, apu, 10, 0x8F08); + apu_set_register(chip, apu, 11, 0x0000); + spin_lock_irqsave(&chip->reg_lock, flags); + outw(1, chip->io_port + 0x04); /* clear WP interupts */ + outw(inw(chip->io_port + 0x18) | 4, chip->io_port + 0x18); /* enable WP ints */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */ + + spin_lock_irqsave(&chip->reg_lock, flags); + __apu_set_register(chip, apu, 5, pa & 0xffff); + snd_es1968_trigger_apu(chip, apu, 0x10); /* 16bit mono */ + do_gettimeofday(&start_time); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); /* 50 msec */ +#else + /* FIXME: + * schedule() above may be too inaccurate and the pointer can + * overlap the boundary.. + */ + mdelay(50); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + offset = __apu_get_register(chip, apu, 5); + do_gettimeofday(&stop_time); + snd_es1968_trigger_apu(chip, apu, 0); /* stop */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* check the current position */ + offset -= (pa & 0xffff); + offset &= 0xfffe; + + t = stop_time.tv_sec - start_time.tv_sec; + t *= 1000000; + if (stop_time.tv_usec < start_time.tv_usec) + t -= start_time.tv_usec - stop_time.tv_usec; + else + t += stop_time.tv_usec - start_time.tv_usec; + if (t == 0) { + snd_printk("?? calculation error..\n"); + } else { + offset *= 1000; + offset = (offset / t) * 1000 + ((offset % t) * 1000) / t; + if (offset < 47500 || offset > 48500) { + if (offset >= 40000 && offset <= 50000) + chip->clock = (chip->clock * offset) / 48000; + } + printk(KERN_INFO "es1968: clocking to %d\n", chip->clock); + } + snd_es1968_free_memory(chip, memory); + snd_es1968_free_apu_pair(chip, apu); +} + + +/* + */ + +static void snd_es1968_pcm_free(snd_pcm_t *pcm) +{ + es1968_t *esm = snd_magic_cast(es1968_t, pcm->private_data, return); + snd_es1968_free_dmabuf(esm); + esm->pcm = NULL; +} + +static int __devinit +snd_es1968_pcm(es1968_t *chip, int device) +{ + snd_pcm_t *pcm; + int err; + + /* get DMA buffer */ + if ((err = snd_es1968_init_dmabuf(chip)) < 0) + return err; + + /* set PCMBAR */ + wave_set_register(chip, 0x01FC, chip->dma_buf_addr >> 12); + wave_set_register(chip, 0x01FD, chip->dma_buf_addr >> 12); + wave_set_register(chip, 0x01FE, chip->dma_buf_addr >> 12); + wave_set_register(chip, 0x01FF, chip->dma_buf_addr >> 12); + + if ((err = snd_pcm_new(chip->card, "ESS Maestro", device, + chip->playback_streams, + chip->capture_streams, &pcm)) < 0) + return err; + + pcm->private_data = chip; + pcm->private_free = snd_es1968_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1968_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1968_capture_ops); + + pcm->info_flags = 0; + + strcpy(pcm->name, "ESS Maestro"); + + chip->pcm = pcm; + + return 0; +} + +/* + * update pointer + */ +static void snd_es1968_update_pcm(es1968_t *chip, esschan_t *es) +{ + unsigned int hwptr; + unsigned int diff; + snd_pcm_substream_t *subs = es->substream; + + if (subs == NULL || !es->running) + return; + + hwptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift; + hwptr %= es->dma_size; + + diff = (es->dma_size + hwptr - es->hwptr) % es->dma_size; + + es->hwptr = hwptr; + es->count += diff; + + while (es->count > es->frag_size) { + spin_unlock(&chip->substream_lock); + snd_pcm_period_elapsed(subs); + spin_lock(&chip->substream_lock); + es->count -= es->frag_size; + } +} + +/* + */ +static void es1968_update_hw_volume(unsigned long private_data) +{ + es1968_t *chip = snd_magic_cast(es1968_t, (void*)private_data, return); + int x, val; + + /* Figure out which volume control button was pushed, + based on differences from the default register + values. */ + x = inb(chip->io_port + 0x1c); + /* Reset the volume control registers. */ + outb(0x88, chip->io_port + 0x1c); + outb(0x88, chip->io_port + 0x1d); + outb(0x88, chip->io_port + 0x1e); + outb(0x88, chip->io_port + 0x1f); + + if (! chip->master_switch || ! chip->master_volume) + return; + + /* FIXME: more clean up is needed.. */ + val = chip->ac97->regs[AC97_MASTER]; + if (x & 1) { + /* mute */ + snd_ac97_write_cache(chip->ac97, 0, val ^ 0x8000); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_switch->id); + } else { + val &= 0x7fff; + if (((x>>1) & 7) > 4) { + /* volume up */ + if ((val & 0xff) > 0) + val--; + if ((val & 0xff00) > 0x100) + val -= 0x0100; + } else { + /* volume down */ + if ((val & 0xff) < 0x1f) + val++; + if ((val & 0xff00) < 0x1f00) + val += 0x0100; + } + snd_ac97_write_cache(chip->ac97, 0, val); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_volume->id); + } +} + +/* + * interrupt handler + */ +static void snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es1968_t *chip = snd_magic_cast(es1968_t, dev_id, return); + u32 event; + + if (!(event = inb(chip->io_port + 0x1A))) + return; + + outw(inw(chip->io_port + 4) & 1, chip->io_port + 4); + + if (event & ESM_HWVOL_IRQ) + tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */ + + /* else ack 'em all, i imagine */ + outb(0xFF, chip->io_port + 0x1A); + + if ((event & ESM_MPU401_IRQ) && chip->rmidi) { + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + } + + if (event & ESM_SOUND_IRQ) { + struct list_head *p, *n; + spin_lock(&chip->substream_lock); + /* we need to use list_for_each_safe here since the substream + * can be deleted in period_elapsed(). + */ + list_for_each_safe(p, n, &chip->substream_list) { + esschan_t *es = list_entry(p, esschan_t, list); + snd_es1968_update_pcm(chip, es); + } + spin_unlock(&chip->substream_lock); + } +} + +/* + * Mixer stuff + */ + +static int __devinit +snd_es1968_mixer(es1968_t *chip) +{ + ac97_t ac97; + snd_ctl_elem_id_t id; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_es1968_ac97_write; + ac97.read = snd_es1968_ac97_read; + ac97.private_data = chip; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + /* attach master switch / volumes for h/w volume control */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Master Playback Switch"); + chip->master_switch = snd_ctl_find_id(chip->card, &id); + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Master Playback Volume"); + chip->master_volume = snd_ctl_find_id(chip->card, &id); + + return 0; +} + +/* + * reset ac97 codec + */ + +static void snd_es1968_ac97_reset(es1968_t *chip) +{ + unsigned long ioaddr = chip->io_port; + + unsigned short save_ringbus_a; + unsigned short save_68; + unsigned short w; + unsigned int vend; + + /* save configuration */ + save_ringbus_a = inw(ioaddr + 0x36); + + //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); /* clear second codec id? */ + /* set command/status address i/o to 1st codec */ + outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + + /* disable ac link */ + outw(0x0000, ioaddr + 0x36); + save_68 = inw(ioaddr + 0x68); + pci_read_config_word(chip->pci, 0x58, &w); /* something magical with gpio and bus arb. */ + pci_read_config_dword(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend); + if (w & 1) + save_68 |= 0x10; + outw(0xfffe, ioaddr + 0x64); /* unmask gpio 0 */ + outw(0x0001, ioaddr + 0x68); /* gpio write */ + outw(0x0000, ioaddr + 0x60); /* write 0 to gpio 0 */ + udelay(20); + outw(0x0001, ioaddr + 0x60); /* write 1 to gpio 1 */ + mdelay(20); + + outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ + outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38); + outw((inw(ioaddr + 0x3a) & 0xfffc) | 0x1, ioaddr + 0x3a); + outw((inw(ioaddr + 0x3c) & 0xfffc) | 0x1, ioaddr + 0x3c); + + /* now the second codec */ + /* disable ac link */ + outw(0x0000, ioaddr + 0x36); + outw(0xfff7, ioaddr + 0x64); /* unmask gpio 3 */ + save_68 = inw(ioaddr + 0x68); + outw(0x0009, ioaddr + 0x68); /* gpio write 0 & 3 ?? */ + outw(0x0001, ioaddr + 0x60); /* write 1 to gpio */ + udelay(20); + outw(0x0009, ioaddr + 0x60); /* write 9 to gpio */ + mdelay(500); /* .. ouch.. */ + //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + +#if 0 /* the loop here needs to be much better if we want it.. */ + snd_printk("trying software reset\n"); + /* try and do a software reset */ + outb(0x80 | 0x7c, ioaddr + 0x30); + for (w = 0;; w++) { + if ((inw(ioaddr + 0x30) & 1) == 0) { + if (inb(ioaddr + 0x32) != 0) + break; + + outb(0x80 | 0x7d, ioaddr + 0x30); + if (((inw(ioaddr + 0x30) & 1) == 0) + && (inb(ioaddr + 0x32) != 0)) + break; + outb(0x80 | 0x7f, ioaddr + 0x30); + if (((inw(ioaddr + 0x30) & 1) == 0) + && (inb(ioaddr + 0x32) != 0)) + break; + } + + if (w > 10000) { + outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ + mdelay(500); /* oh my.. */ + outb(inb(ioaddr + 0x37) & ~0x08, + ioaddr + 0x37); + udelay(1); + outw(0x80, ioaddr + 0x30); + for (w = 0; w < 10000; w++) { + if ((inw(ioaddr + 0x30) & 1) == 0) + break; + } + } + } +#endif + if (vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { + /* turn on external amp? */ + outw(0xf9ff, ioaddr + 0x64); + outw(inw(ioaddr + 0x68) | 0x600, ioaddr + 0x68); + outw(0x0209, ioaddr + 0x60); + } + + /* restore.. */ + outw(save_ringbus_a, ioaddr + 0x36); + + /* Turn on the 978 docking chip. + First frob the "master output enable" bit, + then set most of the playback volume control registers to max. */ + outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); + outb(0xff, ioaddr+0xc3); + outb(0xff, ioaddr+0xc4); + outb(0xff, ioaddr+0xc6); + outb(0xff, ioaddr+0xc8); + outb(0x3f, ioaddr+0xcf); + outb(0x3f, ioaddr+0xd0); +} + +static void snd_es1968_reset(es1968_t *chip) +{ + /* Reset */ + outw(ESM_RESET_MAESTRO | ESM_RESET_DIRECTSOUND, + chip->io_port + ESM_PORT_HOST_IRQ); + udelay(10); + outw(0x0000, chip->io_port + ESM_PORT_HOST_IRQ); + udelay(10); +} + +/* + * power management + */ +static void snd_es1968_set_acpi(es1968_t *chip, int state) +{ + u16 active_mask = acpi_state_mask[state]; + + pci_set_power_state(chip->pci, state); + /* make sure the units we care about are on + XXX we might want to do this before state flipping? */ + pci_write_config_word(chip->pci, 0x54, ~ active_mask); + pci_write_config_word(chip->pci, 0x56, ~ active_mask); +} + + +/* + * initialize maestro chip + */ +static void snd_es1968_chip_init(es1968_t *chip) +{ + struct pci_dev *pci = chip->pci; + int i; + unsigned long iobase = chip->io_port; + u16 w; + u32 n; + + /* We used to muck around with pci config space that + * we had no business messing with. We don't know enough + * about the machine to know which DMA mode is appropriate, + * etc. We were guessing wrong on some machines and making + * them unhappy. We now trust in the BIOS to do things right, + * which almost certainly means a new host of problems will + * arise with broken BIOS implementations. screw 'em. + * We're already intolerant of machines that don't assign + * IRQs. + */ + + /* do config work at full power */ + snd_es1968_set_acpi(chip, ACPI_D0); + + /* Config Reg A */ + pci_read_config_word(pci, ESM_CONFIG_A, &w); + + /* Use TDMA for now. TDMA works on all boards, so while its + * not the most efficient its the simplest. */ + w &= ~DMA_CLEAR; /* Clear DMA bits */ + w |= DMA_TDMA; /* TDMA on */ + w &= ~(PIC_SNOOP1 | PIC_SNOOP2); /* Clear Pic Snoop Mode Bits */ + w &= ~SAFEGUARD; /* Safeguard off */ + w |= POST_WRITE; /* Posted write */ + w |= ISA_TIMING; /* ISA timing on */ + /* XXX huh? claims to be reserved.. */ + w &= ~SWAP_LR; /* swap left/right + seems to only have effect on SB + Emulation */ + w &= ~SUBTR_DECODE; /* Subtractive decode off */ + + pci_write_config_word(pci, ESM_CONFIG_A, w); + + /* Config Reg B */ + + pci_read_config_word(pci, ESM_CONFIG_B, &w); + + w &= ~(1 << 15); /* Turn off internal clock multiplier */ + /* XXX how do we know which to use? */ + w &= ~(1 << 14); /* External clock */ + + w &= ~SPDIF_CONFB; /* disable S/PDIF output */ + w |= HWV_CONFB; /* HWV on */ + w |= DEBOUNCE; /* Debounce off: easier to push the HW buttons */ + w &= ~GPIO_CONFB; /* GPIO 4:5 */ + w |= CHI_CONFB; /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ + w &= ~IDMA_CONFB; /* IDMA off (undocumented) */ + w &= ~MIDI_FIX; /* MIDI fix off (undoc) */ + w &= ~(1 << 1); /* reserved, always write 0 */ + w &= ~IRQ_TO_ISA; /* IRQ to ISA off (undoc) */ + + pci_write_config_word(pci, ESM_CONFIG_B, w); + + /* DDMA off */ + + pci_read_config_word(pci, ESM_DDMA, &w); + w &= ~(1 << 0); + pci_write_config_word(pci, ESM_DDMA, w); + + /* + * Legacy mode + */ + + pci_read_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, &w); + + w &= ~ESS_ENABLE_AUDIO; /* Disable Legacy Audio */ + w &= ~ESS_ENABLE_SERIAL_IRQ; /* Disable SIRQ */ + w &= ~(0x1f); /* disable mpu irq/io, game port, fm, SB */ + + pci_write_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, w); + + /* Set up 978 docking control chip. */ + pci_read_config_word(pci, 0x58, &w); + w|=1<<2; /* Enable 978. */ + w|=1<<3; /* Turn on 978 hardware volume control. */ + w&=~(1<<11); /* Turn on 978 mixer volume control. */ + pci_write_config_word(pci, 0x58, w); + + /* Sound Reset */ + + snd_es1968_reset(chip); + + /* + * Ring Bus Setup + */ + + /* setup usual 0x34 stuff.. 0x36 may be chip specific */ + outw(0xC090, iobase + ESM_RING_BUS_DEST); /* direct sound, stereo */ + udelay(20); + outw(0x3000, iobase + ESM_RING_BUS_CONTR_A); /* enable ringbus/serial */ + udelay(20); + + /* + * Reset the CODEC + */ + + snd_es1968_ac97_reset(chip); + + /* Ring Bus Control B */ + + n = inl(iobase + ESM_RING_BUS_CONTR_B); + n &= ~RINGB_EN_SPDIF; /* SPDIF off */ + //w |= RINGB_EN_2CODEC; /* enable 2nd codec */ + outl(n, iobase + ESM_RING_BUS_CONTR_B); + + /* Set hardware volume control registers to midpoints. + We can tell which button was pushed based on how they change. */ + outb(0x88, iobase+0x1c); + outb(0x88, iobase+0x1d); + outb(0x88, iobase+0x1e); + outb(0x88, iobase+0x1f); + + /* it appears some maestros (dell 7500) only work if these are set, + regardless of wether we use the assp or not. */ + + outb(0, iobase + ASSP_CONTROL_B); + outb(3, iobase + ASSP_CONTROL_A); /* M: Reserved bits... */ + outb(0, iobase + ASSP_CONTROL_C); /* M: Disable ASSP, ASSP IRQ's and FM Port */ + + /* Enable IRQ's */ + w = ESM_HIRQ_DSIE | ESM_HIRQ_MPU401; + outw(w, iobase + ESM_PORT_HOST_IRQ); + + /* + * set up wavecache + */ + for (i = 0; i < 16; i++) { + /* Write 0 into the buffer area 0x1E0->1EF */ + outw(0x01E0 + i, iobase + WC_INDEX); + outw(0x0000, iobase + WC_DATA); + + /* The 1.10 test program seem to write 0 into the buffer area + * 0x1D0-0x1DF too.*/ + outw(0x01D0 + i, iobase + WC_INDEX); + outw(0x0000, iobase + WC_DATA); + } + wave_set_register(chip, IDR7_WAVE_ROMRAM, + (wave_get_register(chip, IDR7_WAVE_ROMRAM) & 0xFF00)); + wave_set_register(chip, IDR7_WAVE_ROMRAM, + wave_get_register(chip, IDR7_WAVE_ROMRAM) | 0x100); + wave_set_register(chip, IDR7_WAVE_ROMRAM, + wave_get_register(chip, IDR7_WAVE_ROMRAM) & ~0x200); + wave_set_register(chip, IDR7_WAVE_ROMRAM, + wave_get_register(chip, IDR7_WAVE_ROMRAM) | ~0x400); + + + maestro_write(chip, IDR2_CRAM_DATA, 0x0000); + /* Now back to the DirectSound stuff */ + /* audio serial configuration.. ? */ + maestro_write(chip, 0x08, 0xB004); + maestro_write(chip, 0x09, 0x001B); + maestro_write(chip, 0x0A, 0x8000); + maestro_write(chip, 0x0B, 0x3F37); + maestro_write(chip, 0x0C, 0x0098); + + /* parallel in, has something to do with recording :) */ + maestro_write(chip, 0x0C, + (maestro_read(chip, 0x0C) & ~0xF000) | 0x8000); + /* parallel out */ + maestro_write(chip, 0x0C, + (maestro_read(chip, 0x0C) & ~0x0F00) | 0x0500); + + maestro_write(chip, 0x0D, 0x7632); + + /* Wave cache control on - test off, sg off, + enable, enable extra chans 1Mb */ + + w = inw(iobase + WC_CONTROL); + + w &= ~0xFA00; /* Seems to be reserved? I don't know */ + w |= 0xA000; /* reserved... I don't know */ + w &= ~0x0200; /* Channels 56,57,58,59 as Extra Play,Rec Channel enable + Seems to crash the Computer if enabled... */ + w |= 0x0100; /* Wave Cache Operation Enabled */ + w |= 0x0080; /* Channels 60/61 as Placback/Record enabled */ + w &= ~0x0060; /* Clear Wavtable Size */ + w |= 0x0020; /* Wavetable Size : 1MB */ + /* Bit 4 is reserved */ + w &= ~0x000C; /* DMA Stuff? I don't understand what the datasheet means */ + /* Bit 1 is reserved */ + w &= ~0x0001; /* Test Mode off */ + + outw(w, iobase + WC_CONTROL); + + /* Now clear the APU control ram */ + for (i = 0; i < NR_APUS; i++) { + for (w = 0; w < NR_APU_REGS; w++) + apu_set_register(chip, i, w, 0); + + } +} + +#ifdef CONFIG_PM +/* + * PM support + */ +static void es1968_suspend(es1968_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + snd_pcm_suspend_all(chip->pcm); + snd_es1968_bob_stop(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void es1968_resume(es1968_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + /* restore all our config */ + pci_enable_device(chip->pci); + snd_es1968_chip_init(chip); + + /* need to restore the base pointers.. */ + if (chip->dma_buf_addr) { + /* set PCMBAR */ + wave_set_register(chip, 0x01FC, chip->dma_buf_addr >> 12); + } + + /* restore ac97 state */ + snd_ac97_resume(chip->ac97); + + /* start timer again */ + if (atomic_read(&chip->bobclient)) + snd_es1968_bob_start(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_es1968_suspend(struct pci_dev *dev, u32 state) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return -ENXIO); + es1968_suspend(chip); + return 0; +} +static int snd_es1968_resume(struct pci_dev *dev) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return -ENXIO); + es1968_resume(chip); + return 0; +} +#else +static void snd_es1968_suspend(struct pci_dev *dev) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return); + es1968_suspend(chip); +} +static void snd_es1968_resume(struct pci_dev *dev) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return); + es1968_resume(chip); +} +#endif + +/* callback */ +static int snd_es1968_set_power_state(snd_card_t *card, unsigned int power_state) +{ + es1968_t *chip = snd_magic_cast(es1968_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + es1968_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + es1968_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +static int snd_es1968_free(es1968_t *chip) +{ + snd_es1968_set_acpi(chip, ACPI_D3); + chip->master_switch = NULL; + chip->master_volume = NULL; + if (chip->res_io_port) { + release_resource(chip->res_io_port); + kfree_nocheck(chip->res_io_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +static int snd_es1968_dev_free(snd_device_t *device) +{ + es1968_t *chip = snd_magic_cast(es1968_t, device->device_data, return -ENXIO); + return snd_es1968_free(chip); +} + +static int __devinit snd_es1968_create(snd_card_t * card, + struct pci_dev *pci, + int total_bufsize, + int play_streams, + int capt_streams, + es1968_t **chip_ret) +{ + static snd_device_ops_t ops = { + dev_free: snd_es1968_dev_free, + }; + es1968_t *chip; + int i, err; + + *chip_ret = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 28 bits */ + if (!pci_dma_supported(pci, 0x0fffffff)) { + snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + chip = (es1968_t *) snd_magic_kcalloc(es1968_t, 0, GFP_KERNEL); + if (! chip) + return -ENOMEM; + + /* Set Vars */ + chip->type = (pci->vendor << 16) | pci->device; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->substream_lock); + spin_lock_init(&chip->bob_lock); + INIT_LIST_HEAD(&chip->buf_list); + INIT_LIST_HEAD(&chip->substream_list); + init_MUTEX(&chip->memory_mutex); + tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->total_bufsize = total_bufsize; /* in bytes */ + chip->playback_streams = play_streams; + chip->capture_streams = capt_streams; + + chip->io_port = pci_resource_start(pci, 0); + if ((chip->res_io_port = request_region(chip->io_port, 0x100, "ESS Maestro")) == NULL) { + snd_es1968_free(chip); + snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 0x100 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_es1968_interrupt, SA_INTERRUPT|SA_SHIRQ, + "ESS Maestro", (void*)chip)) { + snd_es1968_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + + /* Clear Maestro_map */ + for (i = 0; i < 32; i++) + chip->maestro_map[i] = 0; + + /* Clear Apu Map */ + for (i = 0; i < NR_APUS; i++) + chip->apu[i] = ESM_APU_FREE; + + atomic_set(&chip->bobclient, 0); + + /* just to be sure */ + pci_set_master(pci); + + snd_es1968_chip_init(chip); + +#ifdef CONFIG_PM + card->set_power_state = snd_es1968_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es1968_free(chip); + return err; + } + + *chip_ret = chip; + + return 0; +} + + +/* + * joystick + */ + +static int snd_es1968_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1968_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + es1968_t *chip = snd_kcontrol_chip(kcontrol); + u16 val; + + pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &val); + ucontrol->value.integer.value[0] = (val & 0x04) ? 1 : 0; + return 0; +} + +static int snd_es1968_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + es1968_t *chip = snd_kcontrol_chip(kcontrol); + u16 val, oval; + + pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &oval); + val = oval & ~0x04; + if (ucontrol->value.integer.value[0]) + val |= 0x04; + if (val != oval); { + pci_write_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, val); + return 1; + } + return 0; +} + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_es1968_control_switches[] __devinitdata = { + { + name: "Joystick", + iface: SNDRV_CTL_ELEM_IFACE_CARD, + info: snd_es1968_joystick_info, + get: snd_es1968_joystick_get, + put: snd_es1968_joystick_put, + } +}; + +/* + */ +static int __devinit snd_es1968_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + es1968_t *chip; + int i, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (!card) + return -ENOMEM; + + if (snd_total_bufsize[dev] < 128) + snd_total_bufsize[dev] = 128; + if (snd_total_bufsize[dev] > 4096) + snd_total_bufsize[dev] = 4096; + if ((err = snd_es1968_create(card, pci, + snd_total_bufsize[dev] * 1024, /* in bytes */ + snd_pcm_substreams_p[dev], + snd_pcm_substreams_c[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + switch (chip->type) { + case CARD_TYPE_ESS_ES1978: + strcpy(card->driver, "ES1978"); + strcpy(card->shortname, "ESS ES1978 (Maestro 2E)"); + break; + case CARD_TYPE_ESS_ES1968: + strcpy(card->driver, "ES1968"); + strcpy(card->shortname, "ESS ES1968 (Maestro 2)"); + break; + case CARD_TYPE_ESS_ESOLDM1: + strcpy(card->driver, "ESM1"); + strcpy(card->shortname, "ESS Maestro 1"); + break; + } + + if ((err = snd_es1968_pcm(chip, 0)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_es1968_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + chip->io_port + ESM_MPU401_PORT, 1, + chip->irq, 0, &chip->rmidi)) < 0) { + printk(KERN_INFO "es1968: skipping MPU-401 MIDI support..\n"); + } + + /* card switches */ + for (i = 0; i < num_controls(snd_es1968_control_switches); i++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_es1968_control_switches[i], chip)); + if (err < 0) { + snd_card_free(card); + return err; + } + } + + chip->clock = snd_clock[dev]; + if (! chip->clock) + es1968_measure_clock(chip); + + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->io_port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_es1968_remove(struct pci_dev *pci) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "ES1968 (ESS Maestro)", + id_table: snd_es1968_ids, + probe: snd_es1968_probe, + remove: __devexit_p(snd_es1968_remove), +#ifdef CONFIG_PM + suspend: snd_es1968_suspend, + resume: snd_es1968_resume, +#endif +}; + +#if 0 // do we really need this? +static int snd_es1968_notifier(struct notifier_block *nb, unsigned long event, void *buf) +{ + pci_unregister_driver(&driver); + return NOTIFY_OK; +} + +static struct notifier_block snd_es1968_nb = {snd_es1968_notifier, NULL, 0}; +#endif + +static int __init alsa_card_es1968_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("ESS Maestro soundcard not found or device busy\n"); +#endif + return err; + } +#if 0 // do we really need this? + /* If this driver is not shutdown cleanly at reboot, it can + leave the speaking emitting an annoying noise, so we catch + shutdown events. */ + if (register_reboot_notifier(&snd_es1968_nb)) { + snd_printk("reboot notifier registration failed; may make noise at shutdown.\n"); + } +#endif + return 0; +} + +static void __exit alsa_card_es1968_exit(void) +{ +#if 0 // do we really need this? + unregister_reboot_notifier(&snd_es1968_nb); +#endif + pci_unregister_driver(&driver); +} + +module_init(alsa_card_es1968_init) +module_exit(alsa_card_es1968_exit) + +#ifndef MODULE + +/* format is: snd-es1968=snd_enable,snd_index,snd_id, + snd_total_bufsize, + snd_pcm_substreams_p, + snd_pcm_substreams_c, + snd_clock +*/ + +static int __init alsa_card_es1968_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_total_bufsize[nr_dev]) == 2 && + get_option(&str,&snd_pcm_substreams_p[nr_dev]) == 2 && + get_option(&str,&snd_pcm_substreams_c[nr_dev]) == 2 && + get_option(&str,&snd_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es1968=", alsa_card_es1968_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/fm801.c b/sound/pci/fm801.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/fm801.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1141 @@ +/* + * The driver for the ForteMedia FM801 based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t fm801_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ForteMedia FM801"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ForteMedia,FM801}," + "{Genius,SoundMaker Live 5.1}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for the FM801 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for the FM801 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable FM801 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); + +/* + * Direct registers + */ + +#define FM801_REG(chip, reg) (chip->port + FM801_##reg) + +#define FM801_PCM_VOL 0x00 /* PCM Output Volume */ +#define FM801_FM_VOL 0x02 /* FM Output Volume */ +#define FM801_I2S_VOL 0x04 /* I2S Volume */ +#define FM801_REC_SRC 0x06 /* Record Source */ +#define FM801_PLY_CTRL 0x08 /* Playback Control */ +#define FM801_PLY_COUNT 0x0a /* Playback Count */ +#define FM801_PLY_BUF1 0x0c /* Playback Bufer I */ +#define FM801_PLY_BUF2 0x10 /* Playback Buffer II */ +#define FM801_CAP_CTRL 0x14 /* Capture Control */ +#define FM801_CAP_COUNT 0x16 /* Capture Count */ +#define FM801_CAP_BUF1 0x18 /* Capture Buffer I */ +#define FM801_CAP_BUF2 0x1c /* Capture Buffer II */ +#define FM801_CODEC_CTRL 0x22 /* Codec Control */ +#define FM801_I2S_MODE 0x24 /* I2S Mode Control */ +#define FM801_VOLUME 0x26 /* Volume Up/Down/Mute Status */ +#define FM801_I2C_CTRL 0x29 /* I2C Control */ +#define FM801_AC97_CMD 0x2a /* AC'97 Command */ +#define FM801_AC97_DATA 0x2c /* AC'97 Data */ +#define FM801_MPU401_DATA 0x30 /* MPU401 Data */ +#define FM801_MPU401_CMD 0x31 /* MPU401 Command */ +#define FM801_GPIO_CTRL 0x52 /* General Purpose I/O Control */ +#define FM801_GEN_CTRL 0x54 /* General Control */ +#define FM801_IRQ_MASK 0x56 /* Interrupt Mask */ +#define FM801_IRQ_STATUS 0x5a /* Interrupt Status */ +#define FM801_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */ +#define FM801_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */ +#define FM801_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */ +#define FM801_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ +#define FM801_POWERDOWN 0x70 /* Blocks Power Down Control */ + +#define FM801_AC97_ADDR_SHIFT 10 + +/* playback and record control register bits */ +#define FM801_BUF1_LAST (1<<1) +#define FM801_BUF2_LAST (1<<2) +#define FM801_START (1<<5) +#define FM801_PAUSE (1<<6) +#define FM801_IMMED_STOP (1<<7) +#define FM801_RATE_SHIFT 8 +#define FM801_RATE_MASK (15 << FM801_RATE_SHIFT) +#define FM801_CHANNELS_4 (1<<12) /* playback only */ +#define FM801_CHANNELS_6 (2<<12) /* playback only */ +#define FM801_CHANNELS_6MS (3<<12) /* playback only */ +#define FM801_CHANNELS_MASK (3<<12) +#define FM801_16BIT (1<<14) +#define FM801_STEREO (1<<15) + +/* IRQ status bits */ +#define FM801_IRQ_PLAYBACK (1<<8) +#define FM801_IRQ_CAPTURE (1<<9) +#define FM801_IRQ_VOLUME (1<<14) +#define FM801_IRQ_MPU (1<<15) + +/* + + */ + +typedef struct _snd_fm801 fm801_t; + +struct _snd_fm801 { + int irq; + + unsigned long port; /* I/O port number */ + struct resource *res_port; + unsigned int multichannel: 1, /* multichannel support */ + secondary: 1; /* secondary codec */ + unsigned char secondary_addr; /* addres of the secondary codec */ + + unsigned short ply_ctrl; /* playback control */ + unsigned short cap_ctrl; /* capture control */ + + unsigned long ply_buffer; + unsigned int ply_buf; + unsigned int ply_count; + unsigned int ply_size; + unsigned int ply_pos; + + unsigned long cap_buffer; + unsigned int cap_buf; + unsigned int cap_count; + unsigned int cap_size; + unsigned int cap_pos; + + ac97_t *ac97; + ac97_t *ac97_sec; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p_dma_size; + unsigned int c_dma_size; + + spinlock_t reg_lock; + snd_info_entry_t *proc_entry; +}; + +static struct pci_device_id snd_fm801_ids[] __devinitdata = { + { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* FM801 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_fm801_ids); + +/* + * common I/O routines + */ + +static int snd_fm801_update_bits(fm801_t *chip, unsigned short reg, + unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + spin_lock(&chip->reg_lock); + old = inw(chip->port + reg); + new = (old & ~mask) | value; + change = old != new; + if (change) + outw(new, chip->port + reg); + spin_unlock(&chip->reg_lock); + return change; +} + +static void snd_fm801_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return); + int idx; + + /* + * Wait until the codec interface is not ready.. + */ + for (idx = 0; idx < 100; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + goto ok1; + udelay(10); + } + snd_printk("AC'97 interface is busy (1)\n"); + return; + + ok1: + /* write data and address */ + outw(val, FM801_REG(chip, AC97_DATA)); + outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); + /* + * Wait until the write command is not completed.. + */ + for (idx = 0; idx < 1000; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + return; + udelay(10); + } + snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num); +} + +static unsigned short snd_fm801_codec_read(ac97_t *ac97, unsigned short reg) +{ + fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return -ENXIO); + int idx; + + /* + * Wait until the codec interface is not ready.. + */ + for (idx = 0; idx < 100; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + goto ok1; + udelay(10); + } + snd_printk("AC'97 interface is busy (1)\n"); + return 0; + + ok1: + /* read command */ + outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | (1<<7), FM801_REG(chip, AC97_CMD)); + for (idx = 0; idx < 100; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + goto ok2; + udelay(10); + } + snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num); + return 0; + + ok2: + for (idx = 0; idx < 1000; idx++) { + if (inw(FM801_REG(chip, AC97_CMD)) & (1<<8)) + goto ok3; + udelay(10); + } + snd_printk("AC'97 interface #%d is not valid (2)\n", ac97->num); + return 0; + + ok3: + return inw(FM801_REG(chip, AC97_DATA)); +} + +static unsigned int rates[] = { + 5500, 8000, 9600, 11025, + 16000, 19200, 22050, 32000, + 38400, 44100, 48000 +}; + +#define RATES sizeof(rates) / sizeof(rates[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + count: RATES, + list: rates, + mask: 0, +}; + +static unsigned int channels[] = { + 2, 4, 6 +}; + +#define CHANNELS sizeof(channels) / sizeof(channels[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_channels = { + count: CHANNELS, + list: channels, + mask: 0, +}; + +/* + * Sample rate routines + */ + +static unsigned short snd_fm801_rate_bits(int rate) +{ + unsigned int idx; + + for (idx = 0; idx < 11; idx++) + if (rates[idx] == rate) + return idx; + snd_BUG(); + return RATES - 1; +} + +/* + * PCM part + */ + +static int snd_fm801_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock(&chip->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + chip->ply_ctrl &= ~(FM801_BUF1_LAST | + FM801_BUF2_LAST | + FM801_PAUSE); + chip->ply_ctrl |= FM801_START | + FM801_IMMED_STOP; + outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + chip->ply_ctrl &= ~FM801_START; + outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); + } else { + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_fm801_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock(&chip->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + chip->cap_ctrl &= ~(FM801_BUF1_LAST | + FM801_BUF2_LAST | + FM801_PAUSE); + chip->cap_ctrl |= FM801_START | + FM801_IMMED_STOP; + outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + chip->cap_ctrl &= ~FM801_START; + outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); + } else { + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_fm801_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_fm801_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_fm801_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->ply_size = snd_pcm_lib_buffer_bytes(substream); + chip->ply_count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->ply_ctrl &= ~(FM801_START | FM801_16BIT | + FM801_STEREO | FM801_RATE_MASK | + FM801_CHANNELS_MASK); + if (snd_pcm_format_width(runtime->format) == 16) + chip->ply_ctrl |= FM801_16BIT; + if (runtime->channels > 1) { + chip->ply_ctrl |= FM801_STEREO; + if (runtime->channels == 4) + chip->ply_ctrl |= FM801_CHANNELS_4; + else if (runtime->channels == 6) + chip->ply_ctrl |= FM801_CHANNELS_6; + } + chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; + chip->ply_buf = 0; + outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); + outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT)); + chip->ply_buffer = runtime->dma_addr; + chip->ply_pos = 0; + outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1)); + outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_fm801_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->cap_size = snd_pcm_lib_buffer_bytes(substream); + chip->cap_count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->cap_ctrl &= ~(FM801_START | FM801_16BIT | + FM801_STEREO | FM801_RATE_MASK); + if (snd_pcm_format_width(runtime->format) == 16) + chip->cap_ctrl |= FM801_16BIT; + if (runtime->channels > 1) + chip->cap_ctrl |= FM801_STEREO; + chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; + chip->cap_buf = 0; + outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); + outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT)); + chip->cap_buffer = runtime->dma_addr; + chip->cap_pos = 0; + outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1)); + outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_fm801_playback_pointer(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + size_t ptr; + + if (!(chip->ply_ctrl & FM801_START)) + return 0; + spin_lock_irqsave(&chip->reg_lock, flags); + ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT)); + if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) { + ptr += chip->ply_count; + ptr %= chip->ply_size; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_fm801_capture_pointer(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + size_t ptr; + + if (!(chip->cap_ctrl & FM801_START)) + return 0; + spin_lock_irqsave(&chip->reg_lock, flags); + ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT)); + if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) { + ptr += chip->cap_count; + ptr %= chip->cap_size; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames(substream->runtime, ptr); +} + +static void snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + fm801_t *chip = snd_magic_cast(fm801_t, dev_id, return); + unsigned short status; + unsigned int tmp; + + status = inw(FM801_REG(chip, IRQ_STATUS)); + if ((status & (FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME)) == 0) + return; + if (chip->pcm && (status & FM801_IRQ_PLAYBACK)) { + spin_lock(&chip->reg_lock); + chip->ply_buf++; + chip->ply_pos += chip->ply_count; + chip->ply_pos %= chip->ply_size; + tmp = chip->ply_pos + chip->ply_count; + tmp %= chip->ply_size; + outl(chip->ply_buffer + tmp, + (chip->ply_buf & 1) ? + FM801_REG(chip, PLY_BUF1) : + FM801_REG(chip, PLY_BUF2)); + outw(FM801_IRQ_PLAYBACK, FM801_REG(chip, IRQ_STATUS)); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(chip->playback_substream); + } + if (chip->pcm && (status & FM801_IRQ_CAPTURE)) { + spin_lock(&chip->reg_lock); + chip->cap_buf++; + chip->cap_pos += chip->cap_count; + chip->cap_pos %= chip->cap_size; + tmp = chip->cap_pos + chip->cap_count; + tmp %= chip->cap_size; + outl(chip->cap_buffer + tmp, + (chip->cap_buf & 1) ? + FM801_REG(chip, CAP_BUF1) : + FM801_REG(chip, CAP_BUF2)); + outw(FM801_IRQ_CAPTURE, FM801_REG(chip, IRQ_STATUS)); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(chip->capture_substream); + } + if ((status & FM801_IRQ_MPU) && chip->rmidi != NULL) { + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + outw(FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); + } + if (status & FM801_IRQ_VOLUME) { + /* TODO */ + outw(FM801_IRQ_VOLUME, FM801_REG(chip, IRQ_STATUS)); + } +} + +static snd_pcm_hardware_t snd_fm801_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_fm801_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_fm801_playback_open(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->playback_substream = substream; + runtime->hw = snd_fm801_playback; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (chip->multichannel) { + runtime->hw.channels_max = 6; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); + } + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_fm801_capture_open(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->capture_substream = substream; + runtime->hw = snd_fm801_capture; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_fm801_playback_close(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + return 0; +} + +static int snd_fm801_capture_close(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_fm801_playback_ops = { + open: snd_fm801_playback_open, + close: snd_fm801_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_fm801_hw_params, + hw_free: snd_fm801_hw_free, + prepare: snd_fm801_playback_prepare, + trigger: snd_fm801_playback_trigger, + pointer: snd_fm801_playback_pointer, +}; + +static snd_pcm_ops_t snd_fm801_capture_ops = { + open: snd_fm801_capture_open, + close: snd_fm801_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_fm801_hw_params, + hw_free: snd_fm801_hw_free, + prepare: snd_fm801_capture_prepare, + trigger: snd_fm801_capture_trigger, + pointer: snd_fm801_capture_pointer, +}; + +static void snd_fm801_pcm_free(snd_pcm_t *pcm) +{ + fm801_t *chip = snd_magic_cast(fm801_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_fm801_pcm(fm801_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_fm801_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_fm801_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "FM801"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, chip->multichannel ? 128*1024 : 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer routines + */ + +#define FM801_SINGLE(xname, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_fm801_info_single, \ + get: snd_fm801_get_single, put: snd_fm801_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_fm801_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_fm801_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_fm801_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + return snd_fm801_update_bits(chip, reg, mask << shift, val << shift); +} + +#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_fm801_info_double, \ + get: snd_fm801_get_double, put: snd_fm801_put_double, \ + private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } + +static int snd_fm801_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_fm801_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock(&chip->reg_lock); + ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask; + ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask; + spin_unlock(&chip->reg_lock); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_fm801_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + return snd_fm801_update_bits(chip, reg, + (mask << shift_left) | (mask << shift_right), + (val1 << shift_left ) | (val2 << shift_right)); +} + +static int snd_fm801_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[5] = { + "AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 5; + if (uinfo->value.enumerated.item > 4) + uinfo->value.enumerated.item = 4; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_fm801_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = inw(FM801_REG(chip, REC_SRC)) & 7; + if (val > 4) + val = 4; + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int snd_fm801_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if ((val = ucontrol->value.enumerated.item[0]) > 4) + return -EINVAL; + return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val); +} + +#define FM801_CONTROLS (sizeof(snd_fm801_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_fm801_controls[] __devinitdata = { +FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1), +FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1), +FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1), +FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1), +FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1), +FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Digital Capture Source", + info: snd_fm801_info_mux, + get: snd_fm801_get_mux, + put: snd_fm801_put_mux, +} +}; + +#define FM801_CONTROLS_MULTI (sizeof(snd_fm801_controls_multi)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_fm801_controls_multi[] __devinitdata = { +FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0), +FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0), +FM801_SINGLE("IEC958 Capture Switch", FM801_I2S_MODE, 8, 1, 0), +FM801_SINGLE("IEC958 Raw Data Playback Switch", FM801_I2S_MODE, 9, 1, 0), +FM801_SINGLE("IEC958 Raw Data Capture Switch", FM801_I2S_MODE, 10, 1, 0), +}; + +static void snd_fm801_mixer_free_ac97(ac97_t *ac97) +{ + fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return); + if (ac97->num == 0) { + chip->ac97 = NULL; + } else { + chip->ac97_sec = NULL; + } +} + +static int __init snd_fm801_mixer(fm801_t *chip) +{ + ac97_t ac97; + int err, i; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_fm801_codec_write; + ac97.read = snd_fm801_codec_read; + ac97.private_data = chip; + ac97.private_free = snd_fm801_mixer_free_ac97; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + if (chip->secondary) { + ac97.num = 1; + ac97.addr = chip->secondary_addr; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97_sec)) < 0) + return err; + } + for (i = 0; i < FM801_CONTROLS; i++) + snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip)); + if (chip->multichannel) { + for (i = 0; i < FM801_CONTROLS_MULTI; i++) + snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip)); + } + return 0; +} + +/* + * initialization routines + */ + +static int snd_fm801_free(fm801_t *chip) +{ + unsigned short cmdw; + + if (chip->irq < 0) + goto __end_hw; + + /* interrupt setup - mask everything */ + cmdw = inw(FM801_REG(chip, IRQ_MASK)); + cmdw |= 0x00c3; + outw(cmdw, FM801_REG(chip, IRQ_MASK)); + + __end_hw: + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_fm801_dev_free(snd_device_t *device) +{ + fm801_t *chip = snd_magic_cast(fm801_t, device->device_data, return -ENXIO); + return snd_fm801_free(chip); +} + +static int __devinit snd_fm801_create(snd_card_t * card, + struct pci_dev * pci, + fm801_t ** rchip) +{ + fm801_t *chip; + unsigned char rev, id; + unsigned short cmdw; + signed long timeout; + int err; + static snd_device_ops_t ops = { + dev_free: snd_fm801_dev_free, + }; + + *rchip = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + chip = snd_magic_kcalloc(fm801_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 0x80, "FM801")) == NULL) { + snd_fm801_free(chip); + snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->port, chip->port + 0x80 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ, "FM801", (void *)chip)) { + snd_fm801_free(chip); + snd_printk("unable to grab IRQ %d\n", chip->irq); + return -EBUSY; + } + chip->irq = pci->irq; + pci_set_master(pci); + + pci_read_config_byte(pci, PCI_REVISION_ID, &rev); + if (rev >= 0xb1) /* FM801-AU */ + chip->multichannel = 1; + + /* codec cold reset + AC'97 warm reset */ + outw((1<<5)|(1<<6), FM801_REG(chip, CODEC_CTRL)); + udelay(100); + outw(0, FM801_REG(chip, CODEC_CTRL)); + + timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */ + + outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); + udelay(5); + do { + if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) + goto __ac97_secondary; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while ((timeout - (signed long)jiffies) > 0); + snd_printk("Primary AC'97 codec not found\n"); + snd_fm801_free(chip); + return -EIO; + + __ac97_secondary: + if (!chip->multichannel) /* lookup is not required */ + goto __ac97_ok; + for (id = 3; id > 0; id--) { /* my card has the secondary codec */ + /* at address #3, so the loop is inverted */ + + if ((timeout - (signed long)jiffies) < HZ / 20) + timeout = jiffies + HZ / 20; + + outw((1<<7) | (id << FM801_AC97_ADDR_SHIFT) | AC97_VENDOR_ID1, FM801_REG(chip, AC97_CMD)); + udelay(5); + do { + if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) { + cmdw = inw(FM801_REG(chip, AC97_DATA)); + if (cmdw != 0xffff && cmdw != 0) { + chip->secondary = 1; + chip->secondary_addr = id; + goto __ac97_ok; + } + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while ((timeout - (signed long)jiffies) > 0); + } + + /* the recovery phase, it seems that probing for non-existing codec might */ + /* cause timeout problems */ + timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */ + + outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); + udelay(5); + do { + if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) + goto __ac97_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while ((timeout - (signed long)jiffies) > 0); + snd_printk("Primary AC'97 codec not responding\n"); + snd_fm801_free(chip); + return -EIO; + + __ac97_ok: + + /* init volume */ + outw(0x0808, FM801_REG(chip, PCM_VOL)); + outw(0x9f1f, FM801_REG(chip, FM_VOL)); + outw(0x8808, FM801_REG(chip, I2S_VOL)); + + /* I2S control - I2S mode */ + outw(0x0003, FM801_REG(chip, I2S_MODE)); + + /* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */ + cmdw = inw(FM801_REG(chip, IRQ_MASK)); + cmdw &= ~0x0083; + outw(cmdw, FM801_REG(chip, IRQ_MASK)); + + /* interrupt clear */ + outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_fm801_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static int __devinit snd_card_fm801_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + fm801_t *chip; + opl3_t *opl3; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if ((err = snd_fm801_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_fm801_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801, + FM801_REG(chip, MPU401_DATA), 1, + chip->irq, 0, &chip->rmidi)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0), + FM801_REG(chip, OPL3_BANK1), + OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "FM801"); + strcpy(card->shortname, "ForteMedia FM801-"); + strcat(card->shortname, chip->multichannel ? "AU" : "AS"); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_card_fm801_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "FM801", + id_table: snd_fm801_ids, + probe: snd_card_fm801_probe, + remove: __devexit_p(snd_card_fm801_remove), +}; + +static int __init alsa_card_fm801_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("ForteMedia FM801 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_fm801_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_fm801_init) +module_exit(alsa_card_fm801_exit) + +#ifndef MODULE + +/* format is: snd-fm801=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_fm801_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-fm801=", alsa_card_fm801_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/ice1712.c b/sound/pci/ice1712.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ice1712.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,4366 @@ +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + NOTES: + - spdif nonaudio consumer mode does not work (at least with my + Sony STR-DB830) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +#define SND_CS8403 +#define SND_CS8404 +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Hoontech SoundTrack DSP 24}," + "{MidiMan M Audio,Delta 1010}," + "{MidiMan M Audio,Delta DiO 2496}," + "{MidiMan M Audio,Delta 66}," + "{MidiMan M Audio,Delta 44}," + "{MidiMan M Audio,Audiophile 24/96}," + "{TerraTec,EWX 24/96}," + "{TerraTec,EWS 88MT}," + "{TerraTec,EWS 88D}," + "{TerraTec,DMX 6Fire}," + "{ICEnsemble,Generic ICE1712}," + "{ICEnsemble,Generic Envy24}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_omni[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; /* Delta44 & 66 Omni I/O support */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ICE1712 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ICE1712 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ICE1712 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_omni, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_omni, "Enable Midiman M-Audio Delta Omni I/O support."); +MODULE_PARM_SYNTAX(snd_omni, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); + +#ifndef PCI_VENDOR_ID_ICE +#define PCI_VENDOR_ID_ICE 0x1412 +#endif +#ifndef PCI_DEVICE_ID_ICE_1712 +#define PCI_DEVICE_ID_ICE_1712 0x1712 +#endif + +#define ICE1712_SUBDEVICE_STDSP24 0x12141217 /* Hoontech SoundTrack Audio DSP 24 */ +#define ICE1712_SUBDEVICE_DELTA1010 0x121430d6 +#define ICE1712_SUBDEVICE_DELTADIO2496 0x121431d6 +#define ICE1712_SUBDEVICE_DELTA66 0x121432d6 +#define ICE1712_SUBDEVICE_DELTA44 0x121433d6 +#define ICE1712_SUBDEVICE_AUDIOPHILE 0x121434d6 +#define ICE1712_SUBDEVICE_EWX2496 0x3b153011 +#define ICE1712_SUBDEVICE_EWS88MT 0x3b151511 +#define ICE1712_SUBDEVICE_EWS88D 0x3b152b11 +#define ICE1712_SUBDEVICE_DMX6FIRE 0x3b153811 + +/* + * Direct registers + */ + +#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x) + +#define ICE1712_REG_CONTROL 0x00 /* byte */ +#define ICE1712_RESET 0x80 /* reset whole chip */ +#define ICE1712_SERR_LEVEL 0x04 /* SERR# level otherwise edge */ +#define ICE1712_NATIVE 0x01 /* native mode otherwise SB */ +#define ICE1712_REG_IRQMASK 0x01 /* byte */ +#define ICE1712_IRQ_MPU1 0x80 +#define ICE1712_IRQ_TIMER 0x40 +#define ICE1712_IRQ_MPU2 0x20 +#define ICE1712_IRQ_PROPCM 0x10 +#define ICE1712_IRQ_FM 0x08 /* FM/MIDI - legacy */ +#define ICE1712_IRQ_PBKDS 0x04 /* playback DS channels */ +#define ICE1712_IRQ_CONCAP 0x02 /* consumer capture */ +#define ICE1712_IRQ_CONPBK 0x01 /* consumer playback */ +#define ICE1712_REG_IRQSTAT 0x02 /* byte */ +/* look to ICE1712_IRQ_* */ +#define ICE1712_REG_INDEX 0x03 /* byte - indirect CCIxx regs */ +#define ICE1712_REG_DATA 0x04 /* byte - indirect CCIxx regs */ +#define ICE1712_REG_NMI_STAT1 0x05 /* byte */ +#define ICE1712_REG_NMI_DATA 0x06 /* byte */ +#define ICE1712_REG_NMI_INDEX 0x07 /* byte */ +#define ICE1712_REG_AC97_INDEX 0x08 /* byte */ +#define ICE1712_REG_AC97_CMD 0x09 /* byte */ +#define ICE1712_AC97_COLD 0x80 /* cold reset */ +#define ICE1712_AC97_WARM 0x40 /* warm reset */ +#define ICE1712_AC97_WRITE 0x20 /* W: write, R: write in progress */ +#define ICE1712_AC97_READ 0x10 /* W: read, R: read in progress */ +#define ICE1712_AC97_READY 0x08 /* codec ready status bit */ +#define ICE1712_AC97_PBK_VSR 0x02 /* playback VSR */ +#define ICE1712_AC97_CAP_VSR 0x01 /* capture VSR */ +#define ICE1712_REG_AC97_DATA 0x0a /* word (little endian) */ +#define ICE1712_REG_MPU1_CTRL 0x0c /* byte */ +#define ICE1712_REG_MPU1_DATA 0x0d /* byte */ +#define ICE1712_REG_I2C_DEV_ADDR 0x10 /* byte */ +#define ICE1712_I2C_WRITE 0x01 /* write direction */ +#define ICE1712_REG_I2C_BYTE_ADDR 0x11 /* byte */ +#define ICE1712_REG_I2C_DATA 0x12 /* byte */ +#define ICE1712_REG_I2C_CTRL 0x13 /* byte */ +#define ICE1712_I2C_EEPROM 0x80 /* EEPROM exists */ +#define ICE1712_I2C_BUSY 0x01 /* busy bit */ +#define ICE1712_REG_CONCAP_ADDR 0x14 /* dword - consumer capture */ +#define ICE1712_REG_CONCAP_COUNT 0x18 /* word - current/base count */ +#define ICE1712_REG_SERR_SHADOW 0x1b /* byte */ +#define ICE1712_REG_MPU2_CTRL 0x1c /* byte */ +#define ICE1712_REG_MPU2_DATA 0x1d /* byte */ +#define ICE1712_REG_TIMER 0x1e /* word */ + +/* + * Indirect registers + */ + +#define ICE1712_IREG_PBK_COUNT_HI 0x00 +#define ICE1712_IREG_PBK_COUNT_LO 0x01 +#define ICE1712_IREG_PBK_CTRL 0x02 +#define ICE1712_IREG_PBK_LEFT 0x03 /* left volume */ +#define ICE1712_IREG_PBK_RIGHT 0x04 /* right volume */ +#define ICE1712_IREG_PBK_SOFT 0x05 /* soft volume */ +#define ICE1712_IREG_PBK_RATE_LO 0x06 +#define ICE1712_IREG_PBK_RATE_MID 0x07 +#define ICE1712_IREG_PBK_RATE_HI 0x08 +#define ICE1712_IREG_CAP_COUNT_HI 0x10 +#define ICE1712_IREG_CAP_COUNT_LO 0x11 +#define ICE1712_IREG_CAP_CTRL 0x12 +#define ICE1712_IREG_GPIO_DATA 0x20 +#define ICE1712_IREG_GPIO_WRITE_MASK 0x21 +#define ICE1712_IREG_GPIO_DIRECTION 0x22 +#define ICE1712_IREG_CONSUMER_POWERDOWN 0x30 +#define ICE1712_IREG_PRO_POWERDOWN 0x31 + +/* + * Consumer section direct DMA registers + */ + +#define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x) + +#define ICE1712_DS_INTMASK 0x00 /* word - interrupt mask */ +#define ICE1712_DS_INTSTAT 0x02 /* word - interrupt status */ +#define ICE1712_DS_DATA 0x04 /* dword - channel data */ +#define ICE1712_DS_INDEX 0x08 /* dword - channel index */ + +/* + * Consumer section channel registers + */ + +#define ICE1712_DSC_ADDR0 0x00 /* dword - base address 0 */ +#define ICE1712_DSC_COUNT0 0x01 /* word - count 0 */ +#define ICE1712_DSC_ADDR1 0x02 /* dword - base address 1 */ +#define ICE1712_DSC_COUNT1 0x03 /* word - count 1 */ +#define ICE1712_DSC_CONTROL 0x04 /* byte - control & status */ +#define ICE1712_BUFFER1 0x80 /* buffer1 is active */ +#define ICE1712_BUFFER1_AUTO 0x40 /* buffer1 auto init */ +#define ICE1712_BUFFER0_AUTO 0x20 /* buffer0 auto init */ +#define ICE1712_FLUSH 0x10 /* flush FIFO */ +#define ICE1712_STEREO 0x08 /* stereo */ +#define ICE1712_16BIT 0x04 /* 16-bit data */ +#define ICE1712_PAUSE 0x02 /* pause */ +#define ICE1712_START 0x01 /* start */ +#define ICE1712_DSC_RATE 0x05 /* dword - rate */ +#define ICE1712_DSC_VOLUME 0x06 /* word - volume control */ + +/* + * Professional multi-track direct control registers + */ + +#define ICEMT(ice, x) ((ice)->profi_port + ICE1712_MT_##x) + +#define ICE1712_MT_IRQ 0x00 /* byte - interrupt mask */ +#define ICE1712_MULTI_CAPTURE 0x80 /* capture IRQ */ +#define ICE1712_MULTI_PLAYBACK 0x40 /* playback IRQ */ +#define ICE1712_MULTI_CAPSTATUS 0x02 /* capture IRQ status */ +#define ICE1712_MULTI_PBKSTATUS 0x01 /* playback IRQ status */ +#define ICE1712_MT_RATE 0x01 /* byte - sampling rate select */ +#define ICE1712_SPDIF_MASTER 0x10 /* S/PDIF input is master clock */ +#define ICE1712_MT_I2S_FORMAT 0x02 /* byte - I2S data format */ +#define ICE1712_MT_AC97_INDEX 0x04 /* byte - AC'97 index */ +#define ICE1712_MT_AC97_CMD 0x05 /* byte - AC'97 command & status */ +/* look to ICE1712_AC97_* */ +#define ICE1712_MT_AC97_DATA 0x06 /* word - AC'97 data */ +#define ICE1712_MT_PLAYBACK_ADDR 0x10 /* dword - playback address */ +#define ICE1712_MT_PLAYBACK_SIZE 0x14 /* word - playback size */ +#define ICE1712_MT_PLAYBACK_COUNT 0x16 /* word - playback count */ +#define ICE1712_MT_PLAYBACK_CONTROL 0x18 /* byte - control */ +#define ICE1712_CAPTURE_START_SHADOW 0x04 /* capture start */ +#define ICE1712_PLAYBACK_PAUSE 0x02 /* playback pause */ +#define ICE1712_PLAYBACK_START 0x01 /* playback start */ +#define ICE1712_MT_CAPTURE_ADDR 0x20 /* dword - capture address */ +#define ICE1712_MT_CAPTURE_SIZE 0x24 /* word - capture size */ +#define ICE1712_MT_CAPTURE_COUNT 0x26 /* word - capture count */ +#define ICE1712_MT_CAPTURE_CONTROL 0x28 /* byte - control */ +#define ICE1712_CAPTURE_START 0x01 /* capture start */ +#define ICE1712_MT_ROUTE_PSDOUT03 0x30 /* word */ +#define ICE1712_MT_ROUTE_SPDOUT 0x32 /* word */ +#define ICE1712_MT_ROUTE_CAPTURE 0x34 /* dword */ +#define ICE1712_MT_MONITOR_VOLUME 0x38 /* word */ +#define ICE1712_MT_MONITOR_INDEX 0x3a /* byte */ +#define ICE1712_MT_MONITOR_RATE 0x3b /* byte */ +#define ICE1712_MT_MONITOR_ROUTECTRL 0x3c /* byte */ +#define ICE1712_ROUTE_AC97 0x01 /* route digital mixer output to AC'97 */ +#define ICE1712_MT_MONITOR_PEAKINDEX 0x3e /* byte */ +#define ICE1712_MT_MONITOR_PEAKDATA 0x3f /* byte */ + +/* + * Codec configuration bits + */ + +/* PCI[60] System Configuration */ +#define ICE1712_CFG_CLOCK 0xc0 +#define ICE1712_CFG_CLOCK512 0x00 /* 22.5692Mhz, 44.1kHz*512 */ +#define ICE1712_CFG_CLOCK384 0x40 /* 16.9344Mhz, 44.1kHz*384 */ +#define ICE1712_CFG_EXT 0x80 /* external clock */ +#define ICE1712_CFG_2xMPU401 0x20 /* two MPU401 UARTs */ +#define ICE1712_CFG_NO_CON_AC97 0x10 /* consumer AC'97 codec is not present */ +#define ICE1712_CFG_ADC_MASK 0x0c /* one, two, three, four stereo ADCs */ +#define ICE1712_CFG_DAC_MASK 0x03 /* one, two, three, four stereo DACs */ +/* PCI[61] AC-Link Configuration */ +#define ICE1712_CFG_PRO_I2S 0x80 /* multitrack converter: I2S or AC'97 */ +#define ICE1712_CFG_AC97_PACKED 0x01 /* split or packed mode - AC'97 */ +/* PCI[62] I2S Features */ +#define ICE1712_CFG_I2S_VOLUME 0x80 /* volume/mute capability */ +#define ICE1712_CFG_I2S_96KHZ 0x40 /* supports 96kHz sampling */ +#define ICE1712_CFG_I2S_RESMASK 0x30 /* resolution mask, 16,18,20,24-bit */ +#define ICE1712_CFG_I2S_OTHER 0x0f /* other I2S IDs */ +/* PCI[63] S/PDIF Configuration */ +#define ICE1712_CFG_I2S_CHIPID 0xfc /* I2S chip ID */ +#define ICE1712_CFG_SPDIF_IN 0x02 /* S/PDIF input is present */ +#define ICE1712_CFG_SPDIF_OUT 0x01 /* S/PDIF output is present */ + +/* + * MidiMan M-Audio Delta GPIO definitions + */ + +/* MidiMan M-Audio Delta1010 */ +#define ICE1712_DELTA_DFS 0x01 /* fast/slow sample rate mode */ + /* (>48kHz must be 1) */ +#define ICE1712_DELTA_SPDIF_IN_STAT 0x02 + /* S/PDIF input status */ + /* 0 = valid signal is present */ + /* all except Delta44 */ + /* look to CS8414 datasheet */ +#define ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK 0x04 + /* S/PDIF output status clock */ + /* (writting on rising edge - 0->1) */ + /* all except Delta44 */ + /* look to CS8404A datasheet */ +#define ICE1712_DELTA_SPDIF_OUT_STAT_DATA 0x08 + /* S/PDIF output status data */ + /* all except Delta44 */ + /* look to CS8404A datasheet */ +/* MidiMan M-Audio DeltaDiO */ +/* 0x01 = DFS */ +/* 0x02 = SPDIF_IN_STAT */ +/* 0x04 = SPDIF_OUT_STAT_CLOCK */ +/* 0x08 = SPDIF_OUT_STAT_DATA */ +#define ICE1712_DELTA_SPDIF_INPUT_SELECT 0x10 + /* coaxial (0), optical (1) */ + /* S/PDIF input select*/ + +/* MidiMan M-Audio Delta1010 */ +/* 0x01 = DFS */ +/* 0x02 = SPDIF_IN_STAT */ +/* 0x04 = SPDIF_OUT_STAT_CLOCK */ +/* 0x08 = SPDIF_OUT_STAT_DATA */ +#define ICE1712_DELTA_WORD_CLOCK_SELECT 0x10 + /* 1 - clock are taken from S/PDIF input */ + /* 0 - clock are taken from Word Clock input */ + /* affected SPMCLKIN pin of Envy24 */ +#define ICE1712_DELTA_WORD_CLOCK_STATUS 0x20 + /* 0 = valid word clock signal is present */ + +/* MidiMan M-Audio Delta66 */ +/* 0x01 = DFS */ +/* 0x02 = SPDIF_IN_STAT */ +/* 0x04 = SPDIF_OUT_STAT_CLOCK */ +/* 0x08 = SPDIF_OUT_STAT_DATA */ +#define ICE1712_DELTA_CODEC_SERIAL_DATA 0x10 + /* AKM4524 serial data */ +#define ICE1712_DELTA_CODEC_SERIAL_CLOCK 0x20 + /* AKM4524 serial clock */ + /* (writting on rising edge - 0->1 */ +#define ICE1712_DELTA_CODEC_CHIP_A 0x40 +#define ICE1712_DELTA_CODEC_CHIP_B 0x80 + /* 1 - select chip A or B */ + +/* MidiMan M-Audio Delta44 */ +/* 0x01 = DFS */ +/* 0x10 = CODEC_SERIAL_DATA */ +/* 0x20 = CODEC_SERIAL_CLOCK */ +/* 0x40 = CODEC_CHIP_A */ +/* 0x80 = CODEC_CHIP_B */ + +/* MidiMan M-Audio Audiophile definitions */ +/* 0x01 = DFS */ +#define ICE1712_DELTA_AP_CCLK 0x02 /* SPI clock */ + /* (clocking on rising edge - 0->1) */ +#define ICE1712_DELTA_AP_DIN 0x04 /* data input */ +#define ICE1712_DELTA_AP_DOUT 0x08 /* data output */ +#define ICE1712_DELTA_AP_CS_DIGITAL 0x10 /* CS8427 chip select */ + /* low signal = select */ +#define ICE1712_DELTA_AP_CS_CODEC 0x20 /* AK4528 chip select */ + /* low signal = select */ + +/* Hoontech SoundTrack Audio DSP 24 GPIO definitions */ + +#define ICE1712_STDSP24_0_BOX(r, x) r[0] = ((r[0] & ~3) | ((x)&3)) +#define ICE1712_STDSP24_0_DAREAR(r, x) r[0] = ((r[0] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_1_CHN1(r, x) r[1] = ((r[1] & ~1) | ((x)&1)) +#define ICE1712_STDSP24_1_CHN2(r, x) r[1] = ((r[1] & ~2) | (((x)&1)<<1)) +#define ICE1712_STDSP24_1_CHN3(r, x) r[1] = ((r[1] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_2_CHN4(r, x) r[2] = ((r[2] & ~1) | ((x)&1)) +#define ICE1712_STDSP24_2_MIDIIN(r, x) r[2] = ((r[2] & ~2) | (((x)&1)<<1)) +#define ICE1712_STDSP24_2_MIDI1(r, x) r[2] = ((r[2] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_3_MIDI2(r, x) r[3] = ((r[3] & ~1) | ((x)&1)) +#define ICE1712_STDSP24_3_MUTE(r, x) r[3] = ((r[3] & ~2) | (((x)&1)<<1)) +#define ICE1712_STDSP24_3_INSEL(r, x) r[3] = ((r[3] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_SET_ADDR(r, a) r[a&3] = ((r[a&3] & ~0x18) | (((a)&3)<<3)) +#define ICE1712_STDSP24_CLOCK(r, a, c) r[a&3] = ((r[a&3] & ~0x20) | (((c)&1)<<5)) +#define ICE1712_STDSP24_CLOCK_BIT (1<<5) + +/* Hoontech SoundTrack Audio DSP 24 box configuration definitions */ + +#define ICE1712_STDSP24_DAREAR (1<<0) +#define ICE1712_STDSP24_MUTE (1<<1) +#define ICE1712_STDSP24_INSEL (1<<2) + +#define ICE1712_STDSP24_BOX_CHN1 (1<<0) /* input channel 1 */ +#define ICE1712_STDSP24_BOX_CHN2 (1<<1) /* input channel 2 */ +#define ICE1712_STDSP24_BOX_CHN3 (1<<2) /* input channel 3 */ +#define ICE1712_STDSP24_BOX_CHN4 (1<<3) /* input channel 4 */ +#define ICE1712_STDSP24_BOX_MIDI1 (1<<8) +#define ICE1712_STDSP24_BOX_MIDI2 (1<<9) + +/* TerraTec EWX 24/96 configuration definitions */ + +#define ICE1712_EWX2496_AK4524_CS 0x01 /* AK4524 chip select; low = active */ +#define ICE1712_EWX2496_AIN_SEL 0x02 /* input sensitivity switch; high = louder */ +#define ICE1712_EWX2496_AOUT_SEL 0x04 /* output sensitivity switch; high = louder */ +#define ICE1712_EWX2496_RW 0x08 /* read/write switch for i2c; high = write */ +#define ICE1712_EWX2496_SERIAL_DATA 0x10 /* i2c & ak4524 data */ +#define ICE1712_EWX2496_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */ +#define ICE1712_EWX2496_TX2 0x40 /* MIDI2 (not used) */ +#define ICE1712_EWX2496_RX2 0x80 /* MIDI2 (not used) */ + +/* TerraTec EWS 88MT/D configuration definitions */ +/* RW, SDA snd SCLK are identical with EWX24/96 */ +#define ICE1712_EWS88_CS8414_RATE 0x07 /* CS8414 sample rate: gpio 0-2 */ +#define ICE1712_EWS88_RW 0x08 /* read/write switch for i2c; high = write */ +#define ICE1712_EWS88_SERIAL_DATA 0x10 /* i2c & ak4524 data */ +#define ICE1712_EWS88_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */ +#define ICE1712_EWS88_TX2 0x40 /* MIDI2 (only on 88D) */ +#define ICE1712_EWS88_RX2 0x80 /* MIDI2 (only on 88D) */ + +/* i2c address */ +#define ICE1712_EWS88MT_CS8404_ADDR (0x40>>1) +#define ICE1712_EWS88MT_INPUT_ADDR (0x46>>1) +#define ICE1712_EWS88MT_OUTPUT_ADDR (0x48>>1) +#define ICE1712_EWS88MT_OUTPUT_SENSE 0x40 /* mask */ +#define ICE1712_EWS88D_PCF_ADDR (0x40>>1) + +/* TerraTec DMX 6Fire configuration definitions */ +#define ICE1712_6FIRE_AK4524_CS_MASK 0x07 /* AK4524 chip select #1-#3 */ +#define ICE1712_6FIRE_RW 0x08 /* read/write switch for i2c; high = write */ +#define ICE1712_6FIRE_SERIAL_DATA 0x10 /* i2c & ak4524 data */ +#define ICE1712_6FIRE_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */ +#define ICE1712_6FIRE_TX2 0x40 /* MIDI2 */ +#define ICE1712_6FIRE_RX2 0x80 /* MIDI2 */ + +#define ICE1712_6FIRE_CS8427_ADDR (0x22>>1) /* ?? */ + +/* + * DMA mode values + * identical with DMA_XXX on i386 architecture. + */ +#define ICE1712_DMA_MODE_WRITE 0x48 +#define ICE1712_DMA_AUTOINIT 0x10 + + +/* + * + */ + +typedef struct _snd_ice1712 ice1712_t; + +typedef struct { + unsigned int subvendor; /* PCI[2c-2f] */ + unsigned char size; /* size of EEPROM image in bytes */ + unsigned char version; /* must be 1 */ + unsigned char codec; /* codec configuration PCI[60] */ + unsigned char aclink; /* ACLink configuration PCI[61] */ + unsigned char i2sID; /* PCI[62] */ + unsigned char spdif; /* S/PDIF configuration PCI[63] */ + unsigned char gpiomask; /* GPIO initial mask, 0 = write, 1 = don't */ + unsigned char gpiostate; /* GPIO initial state */ + unsigned char gpiodir; /* GPIO direction state */ + unsigned short ac97main; + unsigned short ac97pcm; + unsigned short ac97rec; + unsigned char ac97recsrc; + unsigned char dacID[4]; /* I2S IDs for DACs */ + unsigned char adcID[4]; /* I2S IDs for ADCs */ + unsigned char extra[4]; +} ice1712_eeprom_t; + +struct _snd_ice1712 { + unsigned long conp_dma_size; + unsigned long conc_dma_size; + unsigned long prop_dma_size; + unsigned long proc_dma_size; + int irq; + + unsigned long port; + struct resource *res_port; + unsigned long ddma_port; + struct resource *res_ddma_port; + unsigned long dmapath_port; + struct resource *res_dmapath_port; + unsigned long profi_port; + struct resource *res_profi_port; + + unsigned int config; /* system configuration */ + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_t *pcm_ds; + snd_pcm_t *pcm_pro; + snd_pcm_substream_t *playback_con_substream; + snd_pcm_substream_t *playback_con_substream_ds[6]; + snd_pcm_substream_t *capture_con_substream; + snd_pcm_substream_t *playback_pro_substream; + snd_pcm_substream_t *capture_pro_substream; + unsigned int playback_pro_size; + unsigned int capture_pro_size; + unsigned int playback_con_virt_addr[6]; + unsigned int playback_con_active_buf[6]; + unsigned int capture_con_virt_addr; + unsigned int ac97_ext_id; + ac97_t *ac97; + snd_rawmidi_t *rmidi[2]; + + spinlock_t reg_lock; + struct semaphore gpio_mutex; + snd_info_entry_t *proc_entry; + + ice1712_eeprom_t eeprom; + + unsigned int pro_volumes[20]; + int ak4528: 1, /* AK4524 or AK4528 */ + omni: 1; /* Delta Omni I/O */ + int num_adcs; /* AK4524 or AK4528 ADCs */ + int num_dacs; /* AK4524 or AK4528 DACs */ + int num_total_dacs; /* total DACs */ + unsigned char ak4524_images[4][8]; + unsigned char ak4524_ipga_gain[4][2]; + unsigned char hoontech_boxbits[4]; + unsigned int hoontech_config; + unsigned short hoontech_boxconfig[4]; + + snd_i2c_bus_t *i2c; /* I2C bus */ + snd_i2c_device_t *cs8404; /* CS8404A I2C device */ + snd_i2c_device_t *cs8427; /* CS8427 I2C device */ + snd_i2c_device_t *pcf8574[2]; /* PCF8574 Output/Input (EWS88MT) */ + snd_i2c_device_t *pcf8575; /* PCF8575 (EWS88D) */ + + unsigned char cs8403_spdif_bits; + unsigned char cs8403_spdif_stream_bits; + snd_kcontrol_t *spdif_stream_ctl; + + unsigned char gpio_direction, gpio_write_mask; +}; + +#define chip_t ice1712_t + +static struct pci_device_id snd_ice1712_ids[] __devinitdata = { + { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ICE1712 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_ice1712_ids); + +static int snd_ice1712_build_pro_mixer(ice1712_t *ice); +static int snd_ice1712_build_controls(ice1712_t *ice); + +/* + * Basic I/O + */ + +static inline void snd_ice1712_write(ice1712_t * ice, u8 addr, u8 data) +{ + outb(addr, ICEREG(ice, INDEX)); + outb(data, ICEREG(ice, DATA)); +} + +static inline u8 snd_ice1712_read(ice1712_t * ice, u8 addr) +{ + outb(addr, ICEREG(ice, INDEX)); + return inb(ICEREG(ice, DATA)); +} + +static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data) +{ + outb((channel << 4) | addr, ICEDS(ice, INDEX)); + outl(data, ICEDS(ice, DATA)); +} + +static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr) +{ + outb((channel << 4) | addr, ICEDS(ice, INDEX)); + return inl(ICEDS(ice, DATA)); +} + +static void snd_ice1712_ac97_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEREG(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEREG(ice, AC97_INDEX)); + outw(val, ICEREG(ice, AC97_DATA)); + old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR); + outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0) + break; +} + +static unsigned short snd_ice1712_ac97_read(ac97_t *ac97, + unsigned short reg) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEREG(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEREG(ice, AC97_INDEX)); + outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0) + break; + if (tm >= 0x10000) /* timeout */ + return ~0; + return inw(ICEREG(ice, AC97_DATA)); +} + +/* + * pro ac97 section + */ + +static void snd_ice1712_pro_ac97_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEMT(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEMT(ice, AC97_INDEX)); + outw(val, ICEMT(ice, AC97_DATA)); + old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR); + outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0) + break; +} + + +static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97, + unsigned short reg) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEMT(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEMT(ice, AC97_INDEX)); + outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0) + break; + if (tm >= 0x10000) /* timeout */ + return ~0; + return inw(ICEMT(ice, AC97_DATA)); +} + +static int snd_ice1712_digmix_route_ac97_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ice->reg_lock, flags); + ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0; + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char val, nval; + unsigned long flags; + + spin_lock_irqsave(&ice->reg_lock, flags); + val = inb(ICEMT(ice, MONITOR_ROUTECTRL)); + nval = val & ~ICE1712_ROUTE_AC97; + if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97; + outb(nval, ICEMT(ice, MONITOR_ROUTECTRL)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return val != nval; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Digital Mixer To AC97", + info: snd_ice1712_digmix_route_ac97_info, + get: snd_ice1712_digmix_route_ac97_get, + put: snd_ice1712_digmix_route_ac97_put, +}; + + +/* + */ + +static void snd_ice1712_delta_cs8403_spdif_write(ice1712_t *ice, unsigned char bits) +{ + unsigned char tmp, mask1, mask2; + int idx; + /* send byte to transmitter */ + mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK; + mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA; + down(&ice->gpio_mutex); + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + for (idx = 7; idx >= 0; idx--) { + tmp &= ~(mask1 | mask2); + if (bits & (1 << idx)) + tmp |= mask2; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(100); + tmp |= mask1; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(100); + } + tmp &= ~mask1; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + up(&ice->gpio_mutex); +} + + +/* + * set gpio direction, write mask and data + */ +static void snd_ice1712_gpio_write_bits(ice1712_t *ice, int mask, int bits) +{ + ice->gpio_direction |= mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, mask & bits); +} + +/* + */ +static void save_gpio_status(ice1712_t *ice, unsigned char *tmp) +{ + down(&ice->gpio_mutex); + tmp[0] = ice->gpio_direction; + tmp[1] = ice->gpio_write_mask; +} + +static void restore_gpio_status(ice1712_t *ice, unsigned char *tmp) +{ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, tmp[0]); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, tmp[1]); + ice->gpio_direction = tmp[0]; + ice->gpio_write_mask = tmp[1]; + up(&ice->gpio_mutex); +} + +/* + * CS8427 via SPI mode (for Audiophile), emulated I2C + */ + +/* send 8 bits */ +static void ap_cs8427_write_byte(ice1712_t *ice, unsigned char data, unsigned char tmp) +{ + int idx; + + for (idx = 7; idx >= 0; idx--) { + tmp &= ~(ICE1712_DELTA_AP_DOUT|ICE1712_DELTA_AP_CCLK); + if (data & (1 << idx)) + tmp |= ICE1712_DELTA_AP_DOUT; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + tmp |= ICE1712_DELTA_AP_CCLK; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + } +} + +/* read 8 bits */ +static unsigned char ap_cs8427_read_byte(ice1712_t *ice, unsigned char tmp) +{ + unsigned char data = 0; + int idx; + + for (idx = 7; idx >= 0; idx--) { + tmp &= ~ICE1712_DELTA_AP_CCLK; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + if (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_DELTA_AP_DIN) + data |= 1 << idx; + tmp |= ICE1712_DELTA_AP_CCLK; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + } + return data; +} + +/* assert chip select */ +static unsigned char ap_cs8427_codec_select(ice1712_t *ice) +{ + unsigned char tmp; + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC; + tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + return tmp; +} + +/* deassert chip select */ +static void ap_cs8427_codec_deassert(ice1712_t *ice, unsigned char tmp) +{ + tmp |= ICE1712_DELTA_AP_CS_DIGITAL; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); +} + +/* sequential write */ +static int ap_cs8427_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO); + int res = count; + unsigned char tmp; + + down(&ice->gpio_mutex); + tmp = ap_cs8427_codec_select(ice); + ap_cs8427_write_byte(ice, (device->addr << 1) | 0, tmp); /* address + write mode */ + while (count-- > 0) + ap_cs8427_write_byte(ice, *bytes++, tmp); + ap_cs8427_codec_deassert(ice, tmp); + up(&ice->gpio_mutex); + return res; +} + +/* sequential read */ +static int ap_cs8427_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO); + int res = count; + unsigned char tmp; + + down(&ice->gpio_mutex); + tmp = ap_cs8427_codec_select(ice); + ap_cs8427_write_byte(ice, (device->addr << 1) | 1, tmp); /* address + read mode */ + while (count-- > 0) + *bytes++ = ap_cs8427_read_byte(ice, tmp); + ap_cs8427_codec_deassert(ice, tmp); + up(&ice->gpio_mutex); + return res; +} + +static int ap_cs8427_probeaddr(snd_i2c_bus_t *bus, unsigned short addr) +{ + if (addr == 0x10) + return 1; + return -ENOENT; +} + +static snd_i2c_ops_t ap_cs8427_i2c_ops = { + sendbytes: ap_cs8427_sendbytes, + readbytes: ap_cs8427_readbytes, + probeaddr: ap_cs8427_probeaddr, +}; + +/* + * access via i2c mode (for EWX 24/96, EWS 88MT&D) + */ + +/* send SDA and SCL */ +static void ewx_i2c_setlines(snd_i2c_bus_t *bus, int clk, int data) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + unsigned char tmp = 0; + if (clk) + tmp |= ICE1712_EWX2496_SERIAL_CLOCK; + if (data) + tmp |= ICE1712_EWX2496_SERIAL_DATA; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); +} + +static int ewx_i2c_getclock(snd_i2c_bus_t *bus) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO); + return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_CLOCK ? 1 : 0; +} + +static int ewx_i2c_getdata(snd_i2c_bus_t *bus, int ack) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO); + int bit; + /* set RW pin to low */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_RW); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, 0); + if (ack) + udelay(5); + bit = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_DATA ? 1 : 0; + /* set RW pin to high */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_EWX2496_RW); + /* reset write mask */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_SERIAL_CLOCK); + return bit; +} + +static void ewx_i2c_start(snd_i2c_bus_t *bus) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + unsigned char mask; + + save_gpio_status(ice, (unsigned char *)&bus->private_value); + /* set RW high */ + mask = ICE1712_EWX2496_RW; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + mask |= ICE1712_EWX2496_AK4524_CS; /* CS high also */ + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + mask |= ICE1712_6FIRE_AK4524_CS_MASK; /* CS high also */ + break; + } + snd_ice1712_gpio_write_bits(ice, mask, mask); +} + +static void ewx_i2c_stop(snd_i2c_bus_t *bus) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + restore_gpio_status(ice, (unsigned char *)&bus->private_value); +} + +static void ewx_i2c_direction(snd_i2c_bus_t *bus, int clock, int data) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + unsigned char mask = 0; + + if (clock) + mask |= ICE1712_EWX2496_SERIAL_CLOCK; /* write SCL */ + if (data) + mask |= ICE1712_EWX2496_SERIAL_DATA; /* write SDA */ + ice->gpio_direction &= ~(ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA); + ice->gpio_direction |= mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask); +} + +static snd_i2c_bit_ops_t snd_ice1712_ewx_cs8427_bit_ops = { + start: ewx_i2c_start, + stop: ewx_i2c_stop, + direction: ewx_i2c_direction, + setlines: ewx_i2c_setlines, + getclock: ewx_i2c_getclock, + getdata: ewx_i2c_getdata, +}; + +/* AK4524 chip select; address 0x48 bit 0-3 */ +static void snd_ice1712_ews88mt_chip_select(ice1712_t *ice, int chip_mask) +{ + unsigned char data, ndata; + + snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return); + snd_i2c_lock(ice->i2c); + snd_runtime_check(snd_i2c_readbytes(ice->pcf8574[1], &data, 1) == 1, snd_i2c_unlock(ice->i2c); return); + ndata = (data & 0xf0) | chip_mask; + if (ndata != data) + snd_runtime_check(snd_i2c_sendbytes(ice->pcf8574[1], &ndata, 1) == 1, snd_i2c_unlock(ice->i2c); return); + snd_i2c_unlock(ice->i2c); +} + +/* + * write AK4524 register + */ +static void snd_ice1712_ak4524_write(ice1712_t *ice, int chip, + unsigned char addr, unsigned char data) +{ + unsigned char tmp, data_mask, clk_mask, saved[2]; + unsigned char codecs_mask; + int idx, cif; + unsigned int addrdata; + + snd_assert(chip >= 0 && chip < 4, return); + + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWS88MT) { + /* assert AK4524 CS */ + snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f); + //snd_ice1712_ews88mt_chip_select(ice, 0x0f); + } + + cif = 0; /* the default level of the CIF pin from AK4524 */ + save_gpio_status(ice, saved); + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + data_mask = ICE1712_DELTA_AP_DOUT; + clk_mask = ICE1712_DELTA_AP_CCLK; + codecs_mask = ICE1712_DELTA_AP_CS_CODEC; /* select AK4528 codec */ + tmp |= ICE1712_DELTA_AP_CS_DIGITAL; /* assert digital high */ + break; + case ICE1712_SUBDEVICE_EWX2496: + data_mask = ICE1712_EWX2496_SERIAL_DATA; + clk_mask = ICE1712_EWX2496_SERIAL_CLOCK; + codecs_mask = ICE1712_EWX2496_AK4524_CS; + tmp |= ICE1712_EWX2496_RW; /* set rw bit high */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, + ice->gpio_direction | data_mask | clk_mask | + codecs_mask | ICE1712_EWX2496_RW); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, + ~(data_mask | clk_mask | + codecs_mask | ICE1712_EWX2496_RW)); + cif = 1; /* CIF high */ + break; + case ICE1712_SUBDEVICE_EWS88MT: + data_mask = ICE1712_EWS88_SERIAL_DATA; + clk_mask = ICE1712_EWS88_SERIAL_CLOCK; + codecs_mask = 0; /* no chip select on gpio */ + tmp |= ICE1712_EWS88_RW; /* set rw bit high */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, + ice->gpio_direction | data_mask | clk_mask | + codecs_mask | ICE1712_EWS88_RW); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, + ~(data_mask | clk_mask | + codecs_mask | ICE1712_EWS88_RW)); + cif = 1; /* CIF high */ + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + data_mask = ICE1712_6FIRE_SERIAL_DATA; + clk_mask = ICE1712_6FIRE_SERIAL_CLOCK; + codecs_mask = (1 << chip) & ICE1712_6FIRE_AK4524_CS_MASK; + tmp |= ICE1712_6FIRE_RW; /* set rw bit high */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, + ice->gpio_direction | data_mask | clk_mask | + codecs_mask | ICE1712_6FIRE_RW); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, + ~(data_mask | clk_mask | + codecs_mask | ICE1712_6FIRE_RW)); + cif = 1; /* CIF high */ + break; + default: + data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA; + clk_mask = ICE1712_DELTA_CODEC_SERIAL_CLOCK; + codecs_mask = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A : ICE1712_DELTA_CODEC_CHIP_B; + break; + } + + if (cif) { + tmp |= codecs_mask; /* start without chip select */ + } else { + tmp &= ~codecs_mask; /* chip select low */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(1); + } + + addr &= 0x07; + /* build I2C address + data byte */ + addrdata = 0xa000 | (addr << 8) | data; + for (idx = 15; idx >= 0; idx--) { + tmp &= ~(data_mask|clk_mask); + if (addrdata & (1 << idx)) + tmp |= data_mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + //udelay(200); + udelay(1); + tmp |= clk_mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(1); + } + + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWS88MT) { + restore_gpio_status(ice, saved); + //snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f); + udelay(1); + snd_ice1712_ews88mt_chip_select(ice, 0x0f); + } else { + if (cif) { + /* assert a cs pulse to trigger */ + tmp &= ~codecs_mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(1); + } + tmp |= codecs_mask; /* chip select high to trigger */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(1); + restore_gpio_status(ice, saved); + } + + if ((addr != 0x04 && addr != 0x05) || (data & 0x80) == 0) + ice->ak4524_images[chip][addr] = data; + else + ice->ak4524_ipga_gain[chip][addr-4] = data; +} + +static void snd_ice1712_ak4524_reset(ice1712_t *ice, int state) +{ + int chip; + unsigned char reg; + + for (chip = 0; chip < ice->num_dacs/2; chip++) { + snd_ice1712_ak4524_write(ice, chip, 0x01, state ? 0x00 : 0x03); + if (state) + continue; + for (reg = 0x04; reg < (ice->ak4528 ? 0x06 : 0x08); reg++) + snd_ice1712_ak4524_write(ice, chip, reg, ice->ak4524_images[chip][reg]); + if (ice->ak4528) + continue; + for (reg = 0x04; reg < 0x06; reg++) + snd_ice1712_ak4524_write(ice, chip, reg, ice->ak4524_ipga_gain[chip][reg-4]); + } +} + +static void snd_ice1712_ews_cs8404_spdif_write(ice1712_t *ice, unsigned char bits) +{ + unsigned char bytes[2]; + + snd_i2c_lock(ice->i2c); + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWS88MT: + snd_runtime_check(snd_i2c_sendbytes(ice->cs8404, &bits, 1) == 1, snd_i2c_unlock(ice->i2c); return); + break; + case ICE1712_SUBDEVICE_EWS88D: + snd_runtime_check(snd_i2c_readbytes(ice->pcf8575, bytes, 2) == 2, snd_i2c_unlock(ice->i2c); return); + if (bits != bytes[1]) { + bytes[1] = bits; + snd_runtime_check(snd_i2c_readbytes(ice->pcf8575, bytes, 2) == 2, snd_i2c_unlock(ice->i2c); return); + } + break; + } + snd_i2c_unlock(ice->i2c); +} + + +/* + * change the input clock selection + * spdif_clock = 1 - IEC958 input, 0 - Envy24 + */ +static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock) +{ + unsigned char reg[2] = { 0x80 | 4, 0 }; /* CS8427 auto increment | register number 4 + data */ + unsigned char val, nval; + snd_i2c_lock(ice->i2c); + if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + nval = val & 0xf0; + if (spdif_clock) + nval |= 0x01; + else + nval |= 0x04; + if (val != nval) { + if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + return 1; + } + snd_i2c_unlock(ice->i2c); + return 0; +} + +/* + */ +static void snd_ice1712_set_pro_rate(ice1712_t *ice, snd_pcm_substream_t *substream) +{ + unsigned long flags; + unsigned int rate; + unsigned char val, tmp; + + spin_lock_irqsave(&ice->reg_lock, flags); + if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW| + ICE1712_PLAYBACK_PAUSE| + ICE1712_PLAYBACK_START)) + goto __end; + if (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER) + goto __end; + rate = substream->runtime->rate; + switch (rate) { + case 8000: val = 6; break; + case 9600: val = 3; break; + case 11025: val = 10; break; + case 12000: val = 2; break; + case 16000: val = 5; break; + case 22050: val = 9; break; + case 24000: val = 1; break; + case 32000: val = 4; break; + case 44100: val = 8; break; + case 48000: val = 0; break; + case 64000: val = 15; break; + case 88200: val = 11; break; + case 96000: val = 7; break; + default: + snd_BUG(); + val = 0; + break; + } + outb(val, ICEMT(ice, RATE)); + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_DELTA44: + case ICE1712_SUBDEVICE_AUDIOPHILE: + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ice1712_ak4524_reset(ice, 1); + down(&ice->gpio_mutex); + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + if (val == 15 || val == 11 || val == 7) { + tmp |= ICE1712_DELTA_DFS; + } else { + tmp &= ~ICE1712_DELTA_DFS; + } + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + up(&ice->gpio_mutex); + snd_ice1712_ak4524_reset(ice, 0); + return; + } + __end: + spin_unlock_irqrestore(&ice->reg_lock, flags); +} + +/* + * Interrupt handler + */ + +static void snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, dev_id, return); + unsigned char status; + + while (1) { + status = inb(ICEREG(ice, IRQSTAT)); + if (status == 0) + break; + if (status & ICE1712_IRQ_MPU1) { + if (ice->rmidi[0]) + snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs); + outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT)); + status &= ~ICE1712_IRQ_MPU1; + } + if (status & ICE1712_IRQ_TIMER) + outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT)); + if (status & ICE1712_IRQ_MPU2) { + if (ice->rmidi[1]) + snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs); + outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT)); + status &= ~ICE1712_IRQ_MPU2; + } + if (status & ICE1712_IRQ_PROPCM) { + unsigned char mtstat = inb(ICEMT(ice, IRQ)); + if (mtstat & ICE1712_MULTI_PBKSTATUS) { + if (ice->playback_pro_substream) + snd_pcm_period_elapsed(ice->playback_pro_substream); + outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ)); + } + if (mtstat & ICE1712_MULTI_CAPSTATUS) { + if (ice->capture_pro_substream) + snd_pcm_period_elapsed(ice->capture_pro_substream); + outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ)); + } + } + if (status & ICE1712_IRQ_FM) + outb(ICE1712_IRQ_FM, ICEREG(ice, IRQSTAT)); + if (status & ICE1712_IRQ_PBKDS) { + u32 idx; + u16 pbkstatus; + snd_pcm_substream_t *substream; + pbkstatus = inw(ICEDS(ice, INTSTAT)); + //printk("pbkstatus = 0x%x\n", pbkstatus); + for (idx = 0; idx < 6; idx++) { + if ((pbkstatus & (3 << (idx * 2))) == 0) + continue; + if ((substream = ice->playback_con_substream_ds[idx]) != NULL) + snd_pcm_period_elapsed(substream); + outw(3 << (idx * 2), ICEDS(ice, INTSTAT)); + } + outb(ICE1712_IRQ_PBKDS, ICEREG(ice, IRQSTAT)); + } + if (status & ICE1712_IRQ_CONCAP) { + if (ice->capture_con_substream) + snd_pcm_period_elapsed(ice->capture_con_substream); + outb(ICE1712_IRQ_CONCAP, ICEREG(ice, IRQSTAT)); + } + if (status & ICE1712_IRQ_CONPBK) { + if (ice->playback_con_substream) + snd_pcm_period_elapsed(ice->playback_con_substream); + outb(ICE1712_IRQ_CONPBK, ICEREG(ice, IRQSTAT)); + } + } +} + +/* + * PCM part - misc + */ + +static int snd_ice1712_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ice1712_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* + * PCM part - consumer I/O + */ + +static int snd_ice1712_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + int result = 0; + u32 tmp; + + spin_lock(&ice->reg_lock); + tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL); + if (cmd == SNDRV_PCM_TRIGGER_START) { + tmp |= 1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + tmp &= ~1; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) { + tmp |= 2; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) { + tmp &= ~2; + } else { + result = -EINVAL; + } + snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp); + spin_unlock(&ice->reg_lock); + return result; +} + +static int snd_ice1712_playback_ds_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + int result = 0; + u32 tmp; + + spin_lock(&ice->reg_lock); + tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL); + if (cmd == SNDRV_PCM_TRIGGER_START) { + tmp |= 1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + tmp &= ~1; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) { + tmp |= 2; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) { + tmp &= ~2; + } else { + result = -EINVAL; + } + snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp); + spin_unlock(&ice->reg_lock); + return result; +} + +static int snd_ice1712_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + int result = 0; + u8 tmp; + + spin_lock(&ice->reg_lock); + tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL); + if (cmd == SNDRV_PCM_TRIGGER_START) { + tmp |= 1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + tmp &= ~1; + } else { + result = -EINVAL; + } + snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp); + spin_unlock(&ice->reg_lock); + return result; +} + +static int snd_ice1712_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + u32 period_size, buf_size, rate, tmp; + + period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1; + buf_size = snd_pcm_lib_buffer_bytes(substream) - 1; + tmp = 0x0000; + if (snd_pcm_format_width(runtime->format) == 16) + tmp |= 0x10; + if (runtime->channels == 2) + tmp |= 0x08; + rate = (runtime->rate * 8192) / 375; + if (rate > 0x000fffff) + rate = 0x000fffff; + spin_lock_irqsave(&ice->reg_lock, flags); + outb(0, ice->ddma_port + 15); + outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b); + outl(runtime->dma_addr, ice->ddma_port + 0); + outw(buf_size, ice->ddma_port + 4); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_LO, rate & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_MID, (rate >> 8) & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_HI, (rate >> 16) & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp); + snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_LO, period_size & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8); + snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_playback_ds_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + u32 period_size, buf_size, rate, tmp, chn; + + period_size = snd_pcm_lib_period_bytes(substream) - 1; + buf_size = snd_pcm_lib_buffer_bytes(substream) - 1; + tmp = 0x0064; + if (snd_pcm_format_width(runtime->format) == 16) + tmp &= ~0x04; + if (runtime->channels == 2) + tmp |= 0x08; + rate = (runtime->rate * 8192) / 375; + if (rate > 0x000fffff) + rate = 0x000fffff; + ice->playback_con_active_buf[substream->number] = 0; + ice->playback_con_virt_addr[substream->number] = runtime->dma_addr; + chn = substream->number * 2; + spin_lock_irqsave(&ice->reg_lock, flags); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0)); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT1, period_size); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_RATE, rate); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_VOLUME, 0); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_CONTROL, tmp); + if (runtime->channels == 2) { + snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate); + snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0); + } + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + u32 period_size, buf_size; + u8 tmp; + + period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1; + buf_size = snd_pcm_lib_buffer_bytes(substream) - 1; + tmp = 0x06; + if (snd_pcm_format_width(runtime->format) == 16) + tmp &= ~0x04; + if (runtime->channels == 2) + tmp &= ~0x02; + spin_lock_irqsave(&ice->reg_lock, flags); + outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR)); + outw(buf_size, ICEREG(ice, CONCAP_COUNT)); + snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8); + snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp); + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + return 0; +} + +static snd_pcm_uframes_t snd_ice1712_playback_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + size_t ptr; + + if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1)) + return 0; + ptr = runtime->buffer_size - inw(ice->ddma_port + 4); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + u8 addr; + size_t ptr; + + if (!(snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL) & 1)) + return 0; + if (ice->playback_con_active_buf[substream->number]) + addr = ICE1712_DSC_ADDR1; + else + addr = ICE1712_DSC_ADDR0; + ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) - + ice->playback_con_virt_addr[substream->number]; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ice1712_capture_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1)) + return 0; + ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_ice1712_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (64*1024), + period_bytes_min: 64, + period_bytes_max: (64*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ice1712_playback_ds = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 2, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ice1712_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (64*1024), + period_bytes_min: 64, + period_bytes_max: (64*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_ice1712_playback_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_con_substream = substream; + runtime->hw = snd_ice1712_playback; + return 0; +} + +static int snd_ice1712_playback_ds_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + unsigned long flags; + u32 tmp; + + ice->playback_con_substream_ds[substream->number] = substream; + runtime->hw = snd_ice1712_playback_ds; + spin_lock_irqsave(&ice->reg_lock, flags); + tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2)); + outw(tmp, ICEDS(ice, INTMASK)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->capture_con_substream = substream; + runtime->hw = snd_ice1712_capture; + runtime->hw.rates = ice->ac97->rates_adc; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + return 0; +} + +static int snd_ice1712_playback_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_con_substream = NULL; + return 0; +} + +static int snd_ice1712_playback_ds_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&ice->reg_lock, flags); + tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2)); + outw(tmp, ICEDS(ice, INTMASK)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + ice->playback_con_substream_ds[substream->number] = NULL; + return 0; +} + +static int snd_ice1712_capture_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + /* disable ADC power */ + snd_ac97_update_bits(ice->ac97, AC97_POWERDOWN, 0x0100, 0x0100); + ice->capture_con_substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_ice1712_playback_ops = { + open: snd_ice1712_playback_open, + close: snd_ice1712_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ice1712_hw_params, + hw_free: snd_ice1712_hw_free, + prepare: snd_ice1712_playback_prepare, + trigger: snd_ice1712_playback_trigger, + pointer: snd_ice1712_playback_pointer, +}; + +static snd_pcm_ops_t snd_ice1712_playback_ds_ops = { + open: snd_ice1712_playback_ds_open, + close: snd_ice1712_playback_ds_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ice1712_hw_params, + hw_free: snd_ice1712_hw_free, + prepare: snd_ice1712_playback_ds_prepare, + trigger: snd_ice1712_playback_ds_trigger, + pointer: snd_ice1712_playback_ds_pointer, +}; + +static snd_pcm_ops_t snd_ice1712_capture_ops = { + open: snd_ice1712_capture_open, + close: snd_ice1712_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ice1712_hw_params, + hw_free: snd_ice1712_hw_free, + prepare: snd_ice1712_capture_prepare, + trigger: snd_ice1712_capture_trigger, + pointer: snd_ice1712_capture_pointer, +}; + +static void snd_ice1712_pcm_free(snd_pcm_t *pcm) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ice1712_pcm(ice1712_t * ice, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_ops); + + pcm->private_data = ice; + pcm->private_free = snd_ice1712_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1712 consumer"); + ice->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + + printk("Consumer PCM code does not work well at the moment --jk\n"); + + return 0; +} + +static void snd_ice1712_pcm_free_ds(snd_pcm_t *pcm) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice->pcm_ds = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ice1712_pcm_ds(ice1712_t * ice, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ds_ops); + + pcm->private_data = ice; + pcm->private_free = snd_ice1712_pcm_free_ds; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1712 consumer (DS)"); + ice->pcm_ds = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +/* + * PCM code - professional part (multitrack) + */ + +static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 64000, 88200, 96000 }; + +#define RATES sizeof(rates) / sizeof(rates[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + count: RATES, + list: rates, + mask: 0, +}; + +#if 0 + +static int snd_ice1712_playback_pro_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_IOCTL1_DIG_INFO: + { + snd_pcm_dig_info_t *info = arg; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + return snd_ice1712_dig_cs8403_info(substream, info); + } + } + case SNDRV_PCM_IOCTL1_DIG_PARAMS: + { + snd_pcm_dig_params_t *params = arg; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + return snd_ice1712_dig_cs8403_params(substream, params); + } + } + default: + break; + } + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +#endif + +static int snd_ice1712_pro_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + { + unsigned int what; + unsigned int old; + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + what = ICE1712_PLAYBACK_PAUSE; + snd_pcm_trigger_done(substream, substream); + spin_lock(&ice->reg_lock); + old = inl(ICEMT(ice, PLAYBACK_CONTROL)); + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) + old |= what; + else + old &= ~what; + outl(old, ICEMT(ice, PLAYBACK_CONTROL)); + spin_unlock(&ice->reg_lock); + break; + } + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + unsigned int old; + snd_pcm_substream_t *s = substream; + do { + if (s == ice->playback_pro_substream) { + what |= ICE1712_PLAYBACK_START; + snd_pcm_trigger_done(s, substream); + } else if (s == ice->capture_pro_substream) { + what |= ICE1712_CAPTURE_START_SHADOW; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&ice->reg_lock); + old = inl(ICEMT(ice, PLAYBACK_CONTROL)); + if (cmd == SNDRV_PCM_TRIGGER_START) + old |= what; + else + old &= ~what; + outl(old, ICEMT(ice, PLAYBACK_CONTROL)); + spin_unlock(&ice->reg_lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int snd_ice1712_playback_pro_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ice1712_t *ice = snd_pcm_substream_chip(substream); + unsigned int tmp; + int change; + + ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream); + snd_ice1712_set_pro_rate(ice, substream); + spin_lock_irqsave(&ice->reg_lock, flags); + outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR)); + outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE)); + outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT)); + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + /* setup S/PDIF */ + tmp = ice->cs8403_spdif_stream_bits; + if (tmp & 0x01) /* consumer */ + tmp &= (tmp & 0x01) ? ~0x06 : ~0x18; + switch (substream->runtime->rate) { + case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break; + case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break; + case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break; + default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break; + } + change = ice->cs8403_spdif_stream_bits != tmp; + ice->cs8403_spdif_stream_bits = tmp; + spin_unlock_irqrestore(&ice->reg_lock, flags); + if (change) + snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id); + snd_ice1712_delta_cs8403_spdif_write(ice, tmp); + return 0; + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_AUDIOPHILE: + snd_cs8427_iec958_pcm(ice->cs8427, substream->runtime->rate); + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + /* setup S/PDIF */ + tmp = ice->cs8403_spdif_stream_bits; + if (tmp & 0x10) /* consumer */ + tmp &= (tmp & 0x01) ? ~0x06 : ~0x60; + switch (substream->runtime->rate) { + case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break; + case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break; + case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break; + default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break; + } + change = ice->cs8403_spdif_stream_bits != tmp; + ice->cs8403_spdif_stream_bits = tmp; + spin_unlock_irqrestore(&ice->reg_lock, flags); + if (change) + snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id); + snd_ice1712_ews_cs8404_spdif_write(ice, tmp); + return 0; + } + + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_capture_pro_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream); + snd_ice1712_set_pro_rate(ice, substream); + spin_lock_irqsave(&ice->reg_lock, flags); + outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR)); + outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE)); + outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START)) + return 0; + ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW)) + return 0; + ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_ice1712_playback_pro = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_S32_LE, + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000, + rate_min: 4000, + rate_max: 96000, + channels_min: 10, + channels_max: 10, + buffer_bytes_max: (256*1024), + period_bytes_min: 10 * 4 * 2, + period_bytes_max: 131040, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ice1712_capture_pro = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_S32_LE, + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000, + rate_min: 4000, + rate_max: 96000, + channels_min: 12, + channels_max: 12, + buffer_bytes_max: (256*1024), + period_bytes_min: 12 * 4 * 2, + period_bytes_max: 131040, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_ice1712_playback_pro_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_pro_substream = substream; + runtime->hw = snd_ice1712_playback_pro; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + + ice->cs8403_spdif_stream_bits = ice->cs8403_spdif_bits; + if (ice->cs8427) + snd_cs8427_iec958_active(ice->cs8427, 1); + + return 0; +} + +static int snd_ice1712_capture_pro_open(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ice->capture_pro_substream = substream; + runtime->hw = snd_ice1712_capture_pro; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return 0; +} + +static int snd_ice1712_playback_pro_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_pro_substream = NULL; + if (ice->cs8427) + snd_cs8427_iec958_active(ice->cs8427, 0); + + return 0; +} + +static int snd_ice1712_capture_pro_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->capture_pro_substream = NULL; + return 0; +} + +static void snd_ice1712_pcm_profi_free(snd_pcm_t *pcm) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice->pcm_pro = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static snd_pcm_ops_t snd_ice1712_playback_pro_ops = { + open: snd_ice1712_playback_pro_open, + close: snd_ice1712_playback_pro_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ice1712_hw_params, + hw_free: snd_ice1712_hw_free, + prepare: snd_ice1712_playback_pro_prepare, + trigger: snd_ice1712_pro_trigger, + pointer: snd_ice1712_playback_pro_pointer, +}; + +static snd_pcm_ops_t snd_ice1712_capture_pro_ops = { + open: snd_ice1712_capture_pro_open, + close: snd_ice1712_capture_pro_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ice1712_hw_params, + hw_free: snd_ice1712_hw_free, + prepare: snd_ice1712_capture_pro_prepare, + trigger: snd_ice1712_pro_trigger, + pointer: snd_ice1712_capture_pro_pointer, +}; + +static int __devinit snd_ice1712_pcm_profi(ice1712_t * ice, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_pro_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_pro_ops); + + pcm->private_data = ice; + pcm->private_free = snd_ice1712_pcm_profi_free; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1712 multi"); + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024); + + ice->pcm_pro = pcm; + if (rpcm) + *rpcm = pcm; + + if (ice->cs8427) { + /* assign channels to iec958 */ + err = snd_cs8427_iec958_build(ice->cs8427, + pcm->streams[0].substream, + pcm->streams[1].substream); + if (err < 0) + return err; + } + + if ((err = snd_ice1712_build_pro_mixer(ice)) < 0) + return err; + return 0; +} + +/* + * Mixer section + */ + +static void snd_ice1712_update_volume(ice1712_t *ice, int index) +{ + unsigned int vol = ice->pro_volumes[index]; + unsigned short val = 0; + + val |= (vol & 0x8000) == 0 ? (96 - (vol & 0x7f)) : 0x7f; + val |= ((vol & 0x80000000) == 0 ? (96 - ((vol >> 16) & 0x7f)) : 0x7f) << 8; + outb(index, ICEMT(ice, MONITOR_INDEX)); + outw(val, ICEMT(ice, MONITOR_VOLUME)); +} + +static int snd_ice1712_pro_mixer_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_pro_mixer_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int index = kcontrol->private_value; + + spin_lock_irqsave(&ice->reg_lock, flags); + ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1); + ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_pro_mixer_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + unsigned int nval, change; + + nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) | + (ucontrol->value.integer.value[1] ? 0 : 0x80000000); + spin_lock_irqsave(&ice->reg_lock, flags); + nval |= ice->pro_volumes[index] & ~0x80008000; + change = nval != ice->pro_volumes[index]; + ice->pro_volumes[index] = nval; + snd_ice1712_update_volume(ice, index); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return change; +} + +static int snd_ice1712_pro_mixer_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 96; + return 0; +} + +static int snd_ice1712_pro_mixer_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int index = kcontrol->private_value; + + spin_lock_irqsave(&ice->reg_lock, flags); + ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127; + ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127; + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_pro_mixer_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + unsigned int nval, change; + + nval = (ucontrol->value.integer.value[0] & 127) | + ((ucontrol->value.integer.value[1] & 127) << 16); + spin_lock_irqsave(&ice->reg_lock, flags); + nval |= ice->pro_volumes[index] & ~0x007f007f; + change = nval != ice->pro_volumes[index]; + ice->pro_volumes[index] = nval; + snd_ice1712_update_volume(ice, index); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return change; +} + +static int snd_ice1712_ak4524_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_ice1712_ak4524_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->private_value / 8; + int addr = kcontrol->private_value % 8; + ucontrol->value.integer.value[0] = ice->ak4524_images[chip][addr]; + return 0; +} + +static int snd_ice1712_ak4524_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->private_value / 8; + int addr = kcontrol->private_value % 8; + unsigned char nval = ucontrol->value.integer.value[0]; + int change = ice->ak4524_images[chip][addr] != nval; + if (change) + snd_ice1712_ak4524_write(ice, chip, addr, nval); + return change; +} + +static int snd_ice1712_ak4524_ipga_gain_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 36; + return 0; +} + +static int snd_ice1712_ak4524_ipga_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->private_value / 8; + int addr = kcontrol->private_value % 8; + ucontrol->value.integer.value[0] = ice->ak4524_ipga_gain[chip][addr-4] & 0x7f; + return 0; +} + +static int snd_ice1712_ak4524_ipga_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->private_value / 8; + int addr = kcontrol->private_value % 8; + unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80; + int change = ice->ak4524_ipga_gain[chip][addr] != nval; + if (change) + snd_ice1712_ak4524_write(ice, chip, addr, nval); + return change; +} + +static int snd_ice1712_ak4524_deemphasis_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[4] = { + "44.1kHz", "Off", "48kHz", "32kHz", + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_ak4524_deemphasis_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->id.index; + ucontrol->value.enumerated.item[0] = ice->ak4524_images[chip][3] & 3; + return 0; +} + +static int snd_ice1712_ak4524_deemphasis_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->id.index; + unsigned char nval = ucontrol->value.enumerated.item[0]; + int change; + nval |= (nval & 3) | (ice->ak4524_images[chip][3] & ~3); + change = ice->ak4524_images[chip][3] != nval; + if (change) + snd_ice1712_ak4524_write(ice, chip, 3, nval); + return change; +} + +static int __init snd_ice1712_build_pro_mixer(ice1712_t *ice) +{ + snd_card_t * card = ice->card; + snd_kcontrol_t ctl; + int idx, err; + + /* PCM playback */ + for (idx = 0; idx < 10; idx++) { + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Playback Switch"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_switch_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_switch_get; + ctl.put = snd_ice1712_pro_mixer_switch_put; + ctl.private_value = idx; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Playback Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_volume_get; + ctl.put = snd_ice1712_pro_mixer_volume_put; + ctl.private_value = idx; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + } + + /* PCM capture */ + for (idx = 0; idx < 10; idx++) { + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Capture Switch"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_switch_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_switch_get; + ctl.put = snd_ice1712_pro_mixer_switch_put; + ctl.private_value = idx + 10; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Capture Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_volume_get; + ctl.put = snd_ice1712_pro_mixer_volume_put; + ctl.private_value = idx + 10; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + } + + /* initialize volumes */ + for (idx = 0; idx < 20; idx++) { + ice->pro_volumes[idx] = 0x80008000; /* mute */ + snd_ice1712_update_volume(ice, idx); + } + return 0; +} + +static void snd_ice1712_ac97_init(ac97_t *ac97) +{ + // ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return); + + /* disable center DAC/surround DAC/LFE DAC/MIC ADC */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800); +} + +static void snd_ice1712_mixer_free_ac97(ac97_t *ac97) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return); + ice->ac97 = NULL; +} + +static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice) +{ + int err; + + if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) { + ac97_t ac97; + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ice1712_ac97_write; + ac97.read = snd_ice1712_ac97_read; + ac97.init = snd_ice1712_ac97_init; + ac97.private_data = ice; + ac97.private_free = snd_ice1712_mixer_free_ac97; + if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) { + snd_printk("ice1712: cannot initialize ac97 for consumer, skipped\n"); + // return err; + } else { + if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0) + return err; + } + return 0; + } + /* hmm.. can we have both consumer and pro ac97 mixers? */ + if (! (ice->eeprom.aclink & ICE1712_CFG_PRO_I2S)) { + ac97_t ac97; + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ice1712_pro_ac97_write; + ac97.read = snd_ice1712_pro_ac97_read; + ac97.init = snd_ice1712_ac97_init; + ac97.private_data = ice; + ac97.private_free = snd_ice1712_mixer_free_ac97; + if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) { + snd_printk("ice1712: cannot initialize pro ac97, skipped\n"); + // return err; + } + return 0; + } + /* I2S mixer only */ + strcat(ice->card->mixername, "ICE1712 - multitrack"); + return 0; +} + +/* + * + */ + +static void snd_ice1712_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, entry->private_data, return); + unsigned int idx; + + snd_iprintf(buffer, "ICE1712\n\n"); + snd_iprintf(buffer, "EEPROM:\n"); + snd_iprintf(buffer, " Subvendor : 0x%x\n", ice->eeprom.subvendor); + snd_iprintf(buffer, " Size : %i bytes\n", ice->eeprom.size); + snd_iprintf(buffer, " Version : %i\n", ice->eeprom.version); + snd_iprintf(buffer, " Codec : 0x%x\n", ice->eeprom.codec); + snd_iprintf(buffer, " ACLink : 0x%x\n", ice->eeprom.aclink); + snd_iprintf(buffer, " I2S ID : 0x%x\n", ice->eeprom.i2sID); + snd_iprintf(buffer, " S/PDIF : 0x%x\n", ice->eeprom.spdif); + snd_iprintf(buffer, " GPIO mask : 0x%x\n", ice->eeprom.gpiomask); + snd_iprintf(buffer, " GPIO state : 0x%x\n", ice->eeprom.gpiostate); + snd_iprintf(buffer, " GPIO direction : 0x%x\n", ice->eeprom.gpiodir); + snd_iprintf(buffer, " AC'97 main : 0x%x\n", ice->eeprom.ac97main); + snd_iprintf(buffer, " AC'97 pcm : 0x%x\n", ice->eeprom.ac97pcm); + snd_iprintf(buffer, " AC'97 record : 0x%x\n", ice->eeprom.ac97rec); + snd_iprintf(buffer, " AC'97 record src : 0x%x\n", ice->eeprom.ac97recsrc); + for (idx = 0; idx < 4; idx++) + snd_iprintf(buffer, " DAC ID #%i : 0x%x\n", idx, ice->eeprom.dacID[idx]); + for (idx = 0; idx < 4; idx++) + snd_iprintf(buffer, " ADC ID #%i : 0x%x\n", idx, ice->eeprom.adcID[idx]); + for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++) + snd_iprintf(buffer, " Extra #%02i : 0x%x\n", idx, ice->eeprom.extra[idx - 0x1c]); +} + +static void __devinit snd_ice1712_proc_init(ice1712_t * ice) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(ice->card, "ice1712", ice->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = ice; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 2048; + entry->c.text.read = snd_ice1712_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ice->proc_entry = entry; +} + +static void snd_ice1712_proc_done(ice1712_t * ice) +{ + if (ice->proc_entry) { + snd_info_unregister(ice->proc_entry); + ice->proc_entry = NULL; + } +} + +/* + * + */ + +static int snd_ice1712_eeprom_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 32; + return 0; +} + +static int snd_ice1712_eeprom_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + memcpy(ucontrol->value.bytes.data, &ice->eeprom, 32); + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_eeprom __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_CARD, + name: "ICE1712 EEPROM", + access: SNDRV_CTL_ELEM_ACCESS_READ, + info: snd_ice1712_eeprom_info, + get: snd_ice1712_eeprom_get +}; + +/* + */ +static int snd_ice1712_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ice1712_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_bits); + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_bits); + break; + } + return 0; +} + +static int snd_ice1712_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irqsave(&ice->reg_lock, flags); + change = ice->cs8403_spdif_bits != val; + ice->cs8403_spdif_bits = val; + if (change && ice->playback_pro_substream == NULL) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ice1712_delta_cs8403_spdif_write(ice, val); + } else { + spin_unlock_irqrestore(&ice->reg_lock, flags); + } + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irqsave(&ice->reg_lock, flags); + change = ice->cs8403_spdif_bits != val; + ice->cs8403_spdif_bits = val; + if (change && ice->playback_pro_substream == NULL) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ice1712_ews_cs8404_spdif_write(ice, val); + } else { + spin_unlock_irqrestore(&ice->reg_lock, flags); + } + break; + default: + change = 0; + } + return change; +} + +static snd_kcontrol_new_t snd_ice1712_spdif_default __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_ice1712_spdif_default_info, + get: snd_ice1712_spdif_default_get, + put: snd_ice1712_spdif_default_put +}; + +static int snd_ice1712_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ice1712_spdif_maskc_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_NOT_COPYRIGHT | + IEC958_AES0_CON_EMPHASIS; + ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL | + IEC958_AES1_CON_CATEGORY; + ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS; + break; + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_EWX2496: + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + ucontrol->value.iec958.status[4] = 0xff; + break; + } + return 0; +} + +static int snd_ice1712_spdif_maskp_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_FS | + IEC958_AES0_PRO_EMPHASIS; + ucontrol->value.iec958.status[1] = IEC958_AES1_PRO_MODE; + break; + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_EWX2496: + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + ucontrol->value.iec958.status[4] = 0xff; + break; + } + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_spdif_maskc __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_ice1712_spdif_mask_info, + get: snd_ice1712_spdif_maskc_get, +}; + +static snd_kcontrol_new_t snd_ice1712_spdif_maskp __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + info: snd_ice1712_spdif_mask_info, + get: snd_ice1712_spdif_maskp_get, +}; + +static int snd_ice1712_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ice1712_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_stream_bits); + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_stream_bits); + break; + } + return 0; +} + +static int snd_ice1712_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irqsave(&ice->reg_lock, flags); + change = ice->cs8403_spdif_stream_bits != val; + ice->cs8403_spdif_stream_bits = val; + if (change && ice->playback_pro_substream != NULL) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ice1712_delta_cs8403_spdif_write(ice, val); + } else { + spin_unlock_irqrestore(&ice->reg_lock, flags); + } + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irqsave(&ice->reg_lock, flags); + change = ice->cs8403_spdif_stream_bits != val; + ice->cs8403_spdif_stream_bits = val; + if (change && ice->playback_pro_substream != NULL) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ice1712_ews_cs8404_spdif_write(ice, val); + } else { + spin_unlock_irqrestore(&ice->reg_lock, flags); + } + break; + default: + change = 0; + } + return change; +} + +static snd_kcontrol_new_t snd_ice1712_spdif_stream __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_ice1712_spdif_stream_info, + get: snd_ice1712_spdif_stream_get, + put: snd_ice1712_spdif_stream_put +}; + +#define ICE1712_GPIO(xiface, xname, xindex, mask, invert, xaccess) \ +{ iface: xiface, name: xname, access: xaccess, info: snd_ice1712_gpio_info, \ + get: snd_ice1712_gpio_get, put: snd_ice1712_gpio_put, \ + private_value: mask | (invert << 24) } + +static int snd_ice1712_gpio_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0; + unsigned char saved[2]; + + save_gpio_status(ice, saved); + ucontrol->value.integer.value[0] = (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0) ^ invert; + restore_gpio_status(ice, saved); + return 0; +} + +static int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value & (1<<24)) ? mask : 0; + unsigned char saved[2]; + int val, nval; + + if (kcontrol->private_value & (1 << 31)) + return -EPERM; + nval = (ucontrol->value.integer.value[0] ? mask : 0) ^ invert; + save_gpio_status(ice, saved); + val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + nval |= val & ~mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval); + restore_gpio_status(ice, saved); + return val != nval; +} + +static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_select __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0); +static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_status __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); +static snd_kcontrol_new_t snd_ice1712_deltadio2496_spdif_in_select __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0); +static snd_kcontrol_new_t snd_ice1712_delta_spdif_in_status __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); + +static int snd_ice1712_pro_spdif_master_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_pro_spdif_master_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ice->reg_lock, flags); + ucontrol->value.integer.value[0] = inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER ? 1 : 0; + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_pro_spdif_master_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int nval, change; + + nval = ucontrol->value.integer.value[0] ? ICE1712_SPDIF_MASTER : 0; + spin_lock_irqsave(&ice->reg_lock, flags); + nval |= inb(ICEMT(ice, RATE)) & ~ICE1712_SPDIF_MASTER; + change = inb(ICEMT(ice, RATE)) != nval; + outb(nval, ICEMT(ice, RATE)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + + if (ice->cs8427) { + /* change CS8427 clock source too */ + snd_ice1712_cs8427_set_input_clock(ice, ucontrol->value.integer.value[0]); + } + + return change; +} + +static snd_kcontrol_new_t snd_ice1712_pro_spdif_master __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Multi Track IEC958 Master", + info: snd_ice1712_pro_spdif_master_info, + get: snd_ice1712_pro_spdif_master_get, + put: snd_ice1712_pro_spdif_master_put +}; + +/* + * routing + */ +static int snd_ice1712_pro_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { + "PCM Out", /* 0 */ + "H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */ + "H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */ + "IEC958 In L", "IEC958 In R", /* 9-10 */ + "Digital Mixer", /* 11 - optional */ + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = kcontrol->id.index < 2 ? 12 : 11; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_pro_route_analog_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->id.index; + unsigned int val, cval; + val = inw(ICEMT(ice, ROUTE_PSDOUT03)); + val >>= ((idx % 2) * 8) + ((idx / 2) * 2); + val &= 3; + cval = inl(ICEMT(ice, ROUTE_CAPTURE)); + cval >>= ((idx / 2) * 8) + ((idx % 2) * 4); + if (val == 1 && idx < 2) + ucontrol->value.enumerated.item[0] = 11; + else if (val == 2) + ucontrol->value.enumerated.item[0] = (cval & 7) + 1; + else if (val == 3) + ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9; + else + ucontrol->value.enumerated.item[0] = 0; + return 0; +} + +static int snd_ice1712_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change, shift; + int idx = kcontrol->id.index; + unsigned int val, old_val, nval; + + /* update PSDOUT */ + if (ucontrol->value.enumerated.item[0] >= 11) + nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */ + else if (ucontrol->value.enumerated.item[0] >= 9) + nval = 3; /* spdif in */ + else if (ucontrol->value.enumerated.item[0] >= 1) + nval = 2; /* analog in */ + else + nval = 0; /* pcm */ + shift = ((idx % 2) * 8) + ((idx / 2) * 2); + val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03)); + val &= ~(0x03 << shift); + val |= nval << shift; + change = val != old_val; + if (change) + outw(val, ICEMT(ice, ROUTE_PSDOUT03)); + if (nval < 2) /* dig mixer of pcm */ + return change; + + /* update CAPTURE */ + val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE)); + shift = ((idx / 2) * 8) + ((idx % 2) * 4); + if (nval == 2) { /* analog in */ + nval = ucontrol->value.enumerated.item[0] - 1; + val &= ~(0x07 << shift); + val |= nval << shift; + } else { /* spdif in */ + nval = (ucontrol->value.enumerated.item[0] - 9) << 3; + val &= ~(0x08 << shift); + val |= nval << shift; + } + if (val != old_val) { + change = 1; + outl(val, ICEMT(ice, ROUTE_CAPTURE)); + } + return change; +} + +static int snd_ice1712_pro_route_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->id.index; + unsigned int val, cval; + val = inw(ICEMT(ice, ROUTE_SPDOUT)); + cval = (val >> (idx * 4 + 8)) & 0x0f; + val = (val >> (idx * 2)) & 0x03; + if (val == 1) + ucontrol->value.enumerated.item[0] = 11; + else if (val == 2) + ucontrol->value.enumerated.item[0] = (cval & 7) + 1; + else if (val == 3) + ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9; + else + ucontrol->value.enumerated.item[0] = 0; + return 0; +} + +static int snd_ice1712_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change, shift; + int idx = kcontrol->id.index; + unsigned int val, old_val, nval; + + /* update SPDOUT */ + val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT)); + if (ucontrol->value.enumerated.item[0] >= 11) + nval = 1; + else if (ucontrol->value.enumerated.item[0] >= 9) + nval = 3; + else if (ucontrol->value.enumerated.item[0] >= 1) + nval = 2; + else + nval = 0; + shift = idx * 2; + val &= ~(0x03 << shift); + val |= nval << shift; + shift = idx * 4 + 8; + if (nval == 2) { + nval = ucontrol->value.enumerated.item[0] - 1; + val &= ~(0x07 << shift); + val |= nval << shift; + } else if (nval == 3) { + nval = (ucontrol->value.enumerated.item[0] - 9) << 3; + val &= ~(0x08 << shift); + val |= nval << shift; + } + change = val != old_val; + if (change) + outw(val, ICEMT(ice, ROUTE_SPDOUT)); + return change; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_analog_route __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "H/W Playback Route", + info: snd_ice1712_pro_route_info, + get: snd_ice1712_pro_route_analog_get, + put: snd_ice1712_pro_route_analog_put, +}; + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_spdif_route __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "IEC958 Playback Route", + info: snd_ice1712_pro_route_info, + get: snd_ice1712_pro_route_spdif_get, + put: snd_ice1712_pro_route_spdif_put, +}; + + +static int snd_ice1712_pro_volume_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_ice1712_pro_volume_rate_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ice->reg_lock, flags); + ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_pro_volume_rate_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change; + + spin_lock_irqsave(&ice->reg_lock, flags); + change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0]; + outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_volume_rate __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Multi Track Volume Rate", + info: snd_ice1712_pro_volume_rate_info, + get: snd_ice1712_pro_volume_rate_get, + put: snd_ice1712_pro_volume_rate_put +}; + +static int snd_ice1712_pro_peak_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 22; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_ice1712_pro_peak_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int idx; + + spin_lock_irqsave(&ice->reg_lock, flags); + for (idx = 0; idx < 22; idx++) { + outb(idx, ICEMT(ice, MONITOR_PEAKINDEX)); + ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA)); + } + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_peak __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Multi Track Peak", + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + info: snd_ice1712_pro_peak_info, + get: snd_ice1712_pro_peak_get +}; + +/* + * EWX 24/96 + */ + +static int snd_ice1712_ewx_io_sense_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ + + static char *texts[4] = { + "+4dBu", "-10dBV", + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_ewx_io_sense_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + unsigned char saved[2]; + + save_gpio_status(ice, saved); + ucontrol->value.enumerated.item[0] = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0; + restore_gpio_status(ice, saved); + return 0; +} + +static int snd_ice1712_ewx_io_sense_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + unsigned char saved[2]; + int val, nval; + + if (kcontrol->private_value & (1 << 31)) + return -EPERM; + nval = ucontrol->value.enumerated.item[0] ? mask : 0; + save_gpio_status(ice, saved); + val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + nval |= val & ~mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval); + restore_gpio_status(ice, saved); + return val != nval; +} + +static snd_kcontrol_new_t snd_ice1712_ewx_input_sense __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Input Sensitivity Switch", + info: snd_ice1712_ewx_io_sense_info, + get: snd_ice1712_ewx_io_sense_get, + put: snd_ice1712_ewx_io_sense_put, + private_value: ICE1712_EWX2496_AIN_SEL, +}; + +static snd_kcontrol_new_t snd_ice1712_ewx_output_sense __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Output Sensitivity Switch", + info: snd_ice1712_ewx_io_sense_info, + get: snd_ice1712_ewx_io_sense_get, + put: snd_ice1712_ewx_io_sense_put, + private_value: ICE1712_EWX2496_AOUT_SEL, +}; + + +/* + * EWS88MT + */ +/* analog output sensitivity;; address 0x48 bit 6 */ +static int snd_ice1712_ews88mt_output_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char data; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8574[1], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + ucontrol->value.enumerated.item[0] = data & ICE1712_EWS88MT_OUTPUT_SENSE ? 1 : 0; /* high = -10dBV, low = +4dBu */ + return 0; +} + +/* analog output sensitivity;; address 0x48 bit 6 */ +static int snd_ice1712_ews88mt_output_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char data, ndata; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8574[1], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0); + if (ndata != data && snd_i2c_sendbytes(ice->pcf8574[1], &ndata, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + return ndata != data; +} + +/* analog input sensitivity; address 0x46 */ +static int snd_ice1712_ews88mt_input_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int channel = kcontrol->id.index; + unsigned char data; + + snd_assert(channel >= 0 && channel <= 7, return 0); + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8574[0], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + /* reversed; high = +4dBu, low = -10dBV */ + ucontrol->value.enumerated.item[0] = data & (1 << channel) ? 0 : 1; + return 0; +} + +/* analog output sensitivity; address 0x46 */ +static int snd_ice1712_ews88mt_input_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int channel = kcontrol->id.index; + unsigned char data, ndata; + + snd_assert(channel >= 0 && channel <= 7, return 0); + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8574[0], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel)); + if (ndata != data && snd_i2c_sendbytes(ice->pcf8574[0], &ndata, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + return ndata != data; +} + +static snd_kcontrol_new_t snd_ice1712_ews88mt_input_sense __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Input Sensitivity Switch", + info: snd_ice1712_ewx_io_sense_info, + get: snd_ice1712_ews88mt_input_sense_get, + put: snd_ice1712_ews88mt_input_sense_put, +}; + +static snd_kcontrol_new_t snd_ice1712_ews88mt_output_sense __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Output Sensitivity Switch", + info: snd_ice1712_ewx_io_sense_info, + get: snd_ice1712_ews88mt_output_sense_get, + put: snd_ice1712_ews88mt_output_sense_put, +}; + + +/* + * EWS88D controls + */ + +static int snd_ice1712_ews88d_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_ews88d_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + unsigned char data[2]; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8575, data, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + data[0] = (data[shift >> 3] >> (shift & 7)) & 0x01; + if (invert) + data[0] ^= 0x01; + ucontrol->value.integer.value[0] = data[0]; + return 0; +} + +static int snd_ice1712_ews88d_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + unsigned char data[2], ndata[2]; + int change; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8575, data, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + ndata[shift >> 3] = data[shift >> 3] & ~(1 << (shift & 7)); + if (invert) { + if (! ucontrol->value.integer.value[0]) + ndata[shift >> 3] |= (1 << (shift & 7)); + } else { + if (ucontrol->value.integer.value[0]) + ndata[shift >> 3] |= (1 << (shift & 7)); + } + change = (data[shift >> 3] != ndata[shift >> 3]); + if (change && snd_i2c_sendbytes(ice->pcf8575, data, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + return change; +} + +#define EWS88D_CONTROL(xiface, xname, xshift, xinvert, xaccess) \ +{ iface: xiface,\ + name: xname,\ + access: xaccess,\ + info: snd_ice1712_ews88d_control_info,\ + get: snd_ice1712_ews88d_control_get,\ + put: snd_ice1712_ews88d_control_put,\ + private_value: xshift | (xinvert << 8),\ +} + +static snd_kcontrol_new_t snd_ice1712_ews88d_controls[] __devinitdata = { + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */ + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0), + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0), + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Enable ADAT", 3, 0, 0), + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Through", 4, 1, 0), +}; + + +/* + * DMX 6Fire controls + */ + +#if 0 // XXX not working yet +static int snd_ice1712_6fire_read_pca(ice1712_t *ice) +{ + unsigned char byte; + snd_i2c_lock(ice->i2c); + byte = 0; /* read port */ + snd_i2c_sendbytes(ice->pcf8575, &byte, 1); + if (snd_i2c_readbytes(ice->pcf8575, &byte, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + return byte; +} + +static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char data) +{ + unsigned char bytes[2]; + snd_i2c_lock(ice->i2c); + bytes[0] = 1; /* write port */ + bytes[1] = data; + if (snd_i2c_sendbytes(ice->pcf8575, bytes, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + return 0; +} + +static int snd_ice1712_6fire_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_6fire_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + int data; + + if ((data = snd_ice1712_6fire_read_pca(ice)) < 0) + return data; + data = (data >> shift) & 1; + if (invert) + data ^= 1; + ucontrol->value.integer.value[0] = data; + return 0; +} + +static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + int data, ndata; + + if ((data = snd_ice1712_6fire_read_pca(ice)) < 0) + return data; + ndata = data & ~(1 << shift); + if (ucontrol->value.integer.value[0]) + ndata |= (1 << shift); + if (invert) + ndata ^= (1 << shift); + if (data != ndata) { + snd_ice1712_6fire_write_pca(ice, (unsigned char)ndata); + return 1; + } + return 0; +} + +#define DMX6FIRE_CONTROL(xiface, xname, xshift, xinvert, xaccess) \ +{ iface: xiface,\ + name: xname,\ + access: xaccess,\ + info: snd_ice1712_6fire_control_info,\ + get: snd_ice1712_6fire_control_get,\ + put: snd_ice1712_6fire_control_put,\ + private_value: xshift | (xinvert << 8),\ +} + +static snd_kcontrol_new_t snd_ice1712_6fire_led __devinitdata = +DMX6FIRE_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Breakbox LED", 6, 0, 0); + +#endif // XXX not working yet + + +/* + * + */ + +static unsigned char __devinit snd_ice1712_read_i2c(ice1712_t *ice, + unsigned char dev, + unsigned char addr) +{ + long t = 0x10000; + + outb(addr, ICEREG(ice, I2C_BYTE_ADDR)); + outb(dev & ~ICE1712_I2C_WRITE, ICEREG(ice, I2C_DEV_ADDR)); + while (t-- > 0 && (inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_BUSY)) ; + return inb(ICEREG(ice, I2C_DATA)); +} + +static int __devinit snd_ice1712_read_eeprom(ice1712_t *ice) +{ + int dev = 0xa0; /* EEPROM device address */ + unsigned int idx; + + if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) == 0) { + snd_printk("ICE1712 has not detected EEPROM\n"); + return -EIO; + } + ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) | + (snd_ice1712_read_i2c(ice, dev, 0x01) << 8) | + (snd_ice1712_read_i2c(ice, dev, 0x02) << 16) | + (snd_ice1712_read_i2c(ice, dev, 0x03) << 24); + ice->eeprom.size = snd_ice1712_read_i2c(ice, dev, 0x04); + if (ice->eeprom.size < 28) { + snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size); + return -EIO; + } + ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05); + if (ice->eeprom.version != 1) { + snd_printk("invalid EEPROM version %i\n", ice->eeprom.version); + return -EIO; + } + ice->eeprom.codec = snd_ice1712_read_i2c(ice, dev, 0x06); + ice->eeprom.aclink = snd_ice1712_read_i2c(ice, dev, 0x07); + ice->eeprom.i2sID = snd_ice1712_read_i2c(ice, dev, 0x08); + ice->eeprom.spdif = snd_ice1712_read_i2c(ice, dev, 0x09); + ice->eeprom.gpiomask = snd_ice1712_read_i2c(ice, dev, 0x0a); + ice->eeprom.gpiostate = snd_ice1712_read_i2c(ice, dev, 0x0b); + ice->eeprom.gpiodir = snd_ice1712_read_i2c(ice, dev, 0x0c); + ice->eeprom.ac97main = (snd_ice1712_read_i2c(ice, dev, 0x0d) << 0) | + (snd_ice1712_read_i2c(ice, dev, 0x0e) << 8); + ice->eeprom.ac97pcm = (snd_ice1712_read_i2c(ice, dev, 0x0f) << 0) | + (snd_ice1712_read_i2c(ice, dev, 0x10) << 8); + ice->eeprom.ac97rec = (snd_ice1712_read_i2c(ice, dev, 0x11) << 0) | + (snd_ice1712_read_i2c(ice, dev, 0x12) << 8); + ice->eeprom.ac97recsrc = snd_ice1712_read_i2c(ice, dev, 0x13) << 0; + for (idx = 0; idx < 4; idx++) { + ice->eeprom.dacID[idx] = snd_ice1712_read_i2c(ice, dev, 0x14 + idx); + ice->eeprom.adcID[idx] = snd_ice1712_read_i2c(ice, dev, 0x18 + idx); + } + for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++) + ice->eeprom.extra[idx - 0x1c] = snd_ice1712_read_i2c(ice, dev, idx); + return 0; +} + +static void __devinit snd_ice1712_ak4524_init(ice1712_t *ice) +{ + static unsigned char inits[] = { + 0x00, 0x07, /* 0: all power up */ + 0x01, 0x00, /* 1: ADC/DAC reset */ + 0x02, 0x60, /* 2: 24bit I2S */ + 0x03, 0x19, /* 3: deemphasis off */ + 0x01, 0x03, /* 1: ADC/DAC enable */ + 0x04, 0x00, /* 4: ADC left muted */ + 0x05, 0x00, /* 5: ADC right muted */ + 0x04, 0x80, /* 4: ADC IPGA gain 0dB */ + 0x05, 0x80, /* 5: ADC IPGA gain 0dB */ + 0x06, 0x00, /* 6: DAC left muted */ + 0x07, 0x00, /* 7: DAC right muted */ + 0xff, 0xff + }; + int chip, idx; + unsigned char *ptr, reg, data; + + for (chip = idx = 0; chip < ice->num_dacs/2; chip++) { + ptr = inits; + while (*ptr != 0xff) { + reg = *ptr++; + data = *ptr++; + if (ice->ak4528) { + if (reg > 5) + continue; + if (reg >= 4 && (data & 0x80)) + continue; + } + if (reg == 0x03 && ice->ak4528) + data = 0x0d; /* deemphasis off, turn LR highpass filters on */ + snd_ice1712_ak4524_write(ice, chip, reg, data); + } + } +} + +static void __devinit snd_ice1712_stdsp24_gpio_write(ice1712_t *ice, unsigned char byte) +{ + byte |= ICE1712_STDSP24_CLOCK_BIT; + udelay(100); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte); + byte &= ~ICE1712_STDSP24_CLOCK_BIT; + udelay(100); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte); + byte |= ICE1712_STDSP24_CLOCK_BIT; + udelay(100); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte); +} + +static void __devinit snd_ice1712_stdsp24_darear(ice1712_t *ice, int activate) +{ + down(&ice->gpio_mutex); + ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]); + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_mute(ice1712_t *ice, int activate) +{ + down(&ice->gpio_mutex); + ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]); + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_insel(ice1712_t *ice, int activate) +{ + down(&ice->gpio_mutex); + ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]); + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_box_channel(ice1712_t *ice, int box, int chn, int activate) +{ + down(&ice->gpio_mutex); + + /* select box */ + ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]); + + /* prepare for write */ + if (chn == 3) + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + udelay(100); + if (chn == 3) { + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + } else { + switch (chn) { + case 0: ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 0); break; + case 1: ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 0); break; + case 2: ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 0); break; + } + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]); + } + udelay(100); + ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + udelay(100); + + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_box_midi(ice1712_t *ice, int box, int master, int slave) +{ + down(&ice->gpio_mutex); + + /* select box */ + ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]); + + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, master); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + udelay(100); + + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + udelay(100); + + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + udelay(100); + + /* MIDI2 is direct */ + ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, slave); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]); + + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_init(ice1712_t *ice) +{ + int box, chn; + + ice->hoontech_boxbits[0] = + ice->hoontech_boxbits[1] = + ice->hoontech_boxbits[2] = + ice->hoontech_boxbits[3] = 0; /* should be already */ + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 0, 1); + ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, 0); + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 1, 1); + ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1); + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 2); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 2, 1); + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0); + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 3); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 3, 1); + ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, 0); + + /* let's go - activate only functions in first box */ + ice->hoontech_config = 0; + /* ICE1712_STDSP24_MUTE | + ICE1712_STDSP24_INSEL | + ICE1712_STDSP24_DAREAR; */ + ice->hoontech_boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 | + ICE1712_STDSP24_BOX_CHN2 | + ICE1712_STDSP24_BOX_CHN3 | + ICE1712_STDSP24_BOX_CHN4 | + ICE1712_STDSP24_BOX_MIDI1 | + ICE1712_STDSP24_BOX_MIDI2; + ice->hoontech_boxconfig[1] = + ice->hoontech_boxconfig[2] = + ice->hoontech_boxconfig[3] = 0; + snd_ice1712_stdsp24_darear(ice, (ice->hoontech_config & ICE1712_STDSP24_DAREAR) ? 1 : 0); + snd_ice1712_stdsp24_mute(ice, (ice->hoontech_config & ICE1712_STDSP24_MUTE) ? 1 : 0); + snd_ice1712_stdsp24_insel(ice, (ice->hoontech_config & ICE1712_STDSP24_INSEL) ? 1 : 0); + for (box = 0; box < 4; box++) { + for (chn = 0; chn < 4; chn++) + snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->hoontech_boxconfig[box] & (1 << chn)) ? 1 : 0); + snd_ice1712_stdsp24_box_midi(ice, box, + (ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0, + (ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) ? 1 : 0); + } +} + +static int __devinit snd_ice1712_chip_init(ice1712_t *ice) +{ + int err, has_i2c = 0; + + outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL)); + udelay(200); + outb(ICE1712_NATIVE, ICEREG(ice, CONTROL)); + udelay(200); + pci_write_config_byte(ice->pci, 0x60, ice->eeprom.codec); + pci_write_config_byte(ice->pci, 0x61, ice->eeprom.aclink); + pci_write_config_byte(ice->pci, 0x62, ice->eeprom.i2sID); + pci_write_config_byte(ice->pci, 0x63, ice->eeprom.spdif); + if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) { + ice->gpio_write_mask = ice->eeprom.gpiomask; + ice->gpio_direction = ice->eeprom.gpiodir; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate); + } else { + ice->gpio_write_mask = 0xc0; + ice->gpio_direction = 0xff; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 0xc0); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, 0xff); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_STDSP24_CLOCK_BIT); + } + snd_ice1712_write(ice, ICE1712_IREG_PRO_POWERDOWN, 0); + if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) { + outb(ICE1712_AC97_WARM, ICEREG(ice, AC97_CMD)); + udelay(100); + outb(0, ICEREG(ice, AC97_CMD)); + udelay(200); + snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0); + } + + /* determine I2C, DACs and ADCs */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + ice->ak4528 = 1; + /* follow thru */ + case ICE1712_SUBDEVICE_EWX2496: + has_i2c = 1; + ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 2; + break; + case ICE1712_SUBDEVICE_DELTA44: + case ICE1712_SUBDEVICE_DELTA66: + ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 4; + if (ice->omni) + ice->num_total_dacs = 8; + break; + case ICE1712_SUBDEVICE_EWS88MT: + has_i2c = 1; + /* follow thru */ + case ICE1712_SUBDEVICE_DELTA1010: + ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 8; + break; + case ICE1712_SUBDEVICE_EWS88D: + has_i2c = 1; + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + has_i2c = 1; + ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 6; + break; + } + + if (has_i2c) { + if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) { + snd_printk("unable to create I2C bus\n"); + return err; + } + ice->i2c->private_data = ice; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + ice->i2c->ops = &ap_cs8427_i2c_ops; + break; + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + case ICE1712_SUBDEVICE_DMX6FIRE: + ice->i2c->hw_ops.bit = &snd_ice1712_ewx_cs8427_bit_ops; + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_EWX2496: + if ((err = snd_cs8427_create(ice->i2c, CS8427_BASE_ADDR, &ice->cs8427)) < 0) { + snd_printk("CS8427 initialization failed\n"); + return err; + } + break; + case ICE1712_SUBDEVICE_DMX6FIRE: +#if 0 // XXX not working yet + if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", 0x40>>1, &ice->pcf8575)) < 0) + return err; + if ((err = snd_cs8427_create(ice->i2c, 0x11, &ice->cs8427)) < 0) { + snd_printk("CS8427 initialization failed\n"); + return err; + } +#endif // XXX not working yet + break; + case ICE1712_SUBDEVICE_EWS88MT: + if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->cs8404)) < 0) + return err; + if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->pcf8574[0])) < 0) + return err; + if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->pcf8574[1])) < 0) + return err; + break; + case ICE1712_SUBDEVICE_EWS88D: + if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->pcf8575)) < 0) + return err; + break; + } + } + /* second stage of initialization, analog parts and others */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_DELTA44: + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_DMX6FIRE: + snd_ice1712_ak4524_init(ice); + break; + case ICE1712_SUBDEVICE_STDSP24: + snd_ice1712_stdsp24_init(ice); + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + /* Set spdif defaults */ + snd_ice1712_delta_cs8403_spdif_write(ice, ice->cs8403_spdif_bits); + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + /* Set spdif defaults */ + snd_ice1712_ews_cs8404_spdif_write(ice, ice->cs8403_spdif_bits); + break; + } + return 0; +} + +static int __init snd_ice1712_build_controls(ice1712_t *ice) +{ + unsigned int idx; + snd_kcontrol_t *kctl; + int err; + + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_eeprom, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_spdif_master, ice)); + if (err < 0) + return err; + for (idx = 0; idx < ice->num_total_dacs; idx++) { + kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_analog_route, ice); + if (kctl == NULL) + return -ENOMEM; + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + for (idx = 0; idx < 2; idx++) { + kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_spdif_route, ice); + if (kctl == NULL) + return -ENOMEM; + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice)); + if (err < 0) + return err; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_select, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_status, ice)); + if (err < 0) + return err; + break; + case ICE1712_SUBDEVICE_DELTADIO2496: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_deltadio2496_spdif_in_select, ice)); + if (err < 0) + return err; + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + snd_assert(ice->pcm_pro != NULL, return -EIO); + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + ice->spdif_stream_ctl = kctl; + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta_spdif_in_status, ice)); + if (err < 0) + return err; + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_DELTA44: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_DMX6FIRE: + for (idx = 0; idx < ice->num_dacs; ++idx) { + snd_kcontrol_t ctl; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "DAC Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_ak4524_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_ak4524_volume_get; + ctl.put = snd_ice1712_ak4524_volume_put; + if (ice->ak4528) + ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */ + else + ctl.private_value = (idx / 2) * 8 + (idx % 2) + 6; /* register 6 & 7 */ + ctl.private_data = ice; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + } + for (idx = 0; idx < ice->num_adcs && !ice->ak4528; ++idx) { + snd_kcontrol_t ctl; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "ADC Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_ak4524_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_ak4524_volume_get; + ctl.put = snd_ice1712_ak4524_volume_put; + ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */ + ctl.private_data = ice; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "IPGA Analog Capture Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_ak4524_ipga_gain_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_ak4524_ipga_gain_get; + ctl.put = snd_ice1712_ak4524_ipga_gain_put; + ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */ + ctl.private_data = ice; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + } + for (idx = 0; idx < ice->num_dacs/2; idx++) { + snd_kcontrol_t ctl; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Deemphasis"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_ak4524_deemphasis_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_ak4524_deemphasis_get; + ctl.put = snd_ice1712_ak4524_deemphasis_put; + ctl.private_data = ice; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + } + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx_input_sense, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx_output_sense, ice)); + if (err < 0) + return err; + break; + case ICE1712_SUBDEVICE_EWS88MT: + for (idx = 0; idx < 8; idx++) { + kctl = snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice); + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_output_sense, ice)); + if (err < 0) + return err; + break; + case ICE1712_SUBDEVICE_EWS88D: + for (idx = 0; idx < sizeof(snd_ice1712_ews88d_controls)/sizeof(snd_ice1712_ews88d_controls[0]); idx++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_controls[idx], ice)); + if (err < 0) + return err; + } + break; + case ICE1712_SUBDEVICE_DMX6FIRE: +#if 0 // XXX not working yet + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_led, ice)); + if (err < 0) + return err; +#endif + break; + } + + return 0; +} + +static int snd_ice1712_free(ice1712_t *ice) +{ + if (ice->res_port == NULL) + goto __hw_end; + /* mask all interrupts */ + outb(0xc0, ICEMT(ice, IRQ)); + outb(0xff, ICEREG(ice, IRQMASK)); + /* --- */ + __hw_end: + snd_ice1712_proc_done(ice); + synchronize_irq(); + if (ice->irq) + free_irq(ice->irq, (void *) ice); + if (ice->res_port) { + release_resource(ice->res_port); + kfree_nocheck(ice->res_port); + } + if (ice->res_ddma_port) { + release_resource(ice->res_ddma_port); + kfree_nocheck(ice->res_ddma_port); + } + if (ice->res_dmapath_port) { + release_resource(ice->res_dmapath_port); + kfree_nocheck(ice->res_dmapath_port); + } + if (ice->res_profi_port) { + release_resource(ice->res_profi_port); + kfree_nocheck(ice->res_profi_port); + } + snd_magic_kfree(ice); + return 0; +} + +static int snd_ice1712_dev_free(snd_device_t *device) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, device->device_data, return -ENXIO); + return snd_ice1712_free(ice); +} + +static int __devinit snd_ice1712_create(snd_card_t * card, + struct pci_dev *pci, + int omni, + ice1712_t ** r_ice1712) +{ + ice1712_t *ice; + int err; + static snd_device_ops_t ops = { + dev_free: snd_ice1712_dev_free, + }; + + *r_ice1712 = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 28 bits */ + if (!pci_dma_supported(pci, 0x0fffffff)) { + snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + ice = snd_magic_kcalloc(ice1712_t, 0, GFP_KERNEL); + if (ice == NULL) + return -ENOMEM; + ice->omni = omni ? 1 : 0; + spin_lock_init(&ice->reg_lock); + init_MUTEX(&ice->gpio_mutex); + ice->cs8403_spdif_bits = + ice->cs8403_spdif_stream_bits = (0x01 | /* consumer format */ + 0x10 | /* no emphasis */ + 0x20); /* PCM encoder/decoder */ + ice->card = card; + ice->pci = pci; + ice->irq = -1; + ice->port = pci_resource_start(pci, 0); + ice->ddma_port = pci_resource_start(pci, 1); + ice->dmapath_port = pci_resource_start(pci, 2); + ice->profi_port = pci_resource_start(pci, 3); + pci_set_master(pci); + pci_write_config_word(ice->pci, 0x40, 0x807f); + pci_write_config_word(ice->pci, 0x42, 0x0006); + snd_ice1712_proc_init(ice); + synchronize_irq(); + + if ((ice->res_port = request_region(ice->port, 32, "ICE1712 - Controller")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->port, ice->port + 32 - 1); + return -EIO; + } + if ((ice->res_ddma_port = request_region(ice->ddma_port, 16, "ICE1712 - DDMA")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->ddma_port, ice->ddma_port + 16 - 1); + return -EIO; + } + if ((ice->res_dmapath_port = request_region(ice->dmapath_port, 16, "ICE1712 - DMA path")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->dmapath_port, ice->dmapath_port + 16 - 1); + return -EIO; + } + if ((ice->res_profi_port = request_region(ice->profi_port, 64, "ICE1712 - Professional")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->profi_port, ice->profi_port + 16 - 1); + return -EIO; + } + if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1712", (void *) ice)) { + snd_ice1712_free(ice); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EIO; + } + ice->irq = pci->irq; + + if (snd_ice1712_read_eeprom(ice) < 0) { + snd_ice1712_free(ice); + return -EIO; + } + if (snd_ice1712_chip_init(ice) < 0) { + snd_ice1712_free(ice); + return -EIO; + } + + /* unmask used interrupts */ + outb((ice->eeprom.codec & ICE1712_CFG_2xMPU401) == 0 ? ICE1712_IRQ_MPU2 : 0 | + (ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97) ? ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0, + ICEREG(ice, IRQMASK)); + outb(0x00, ICEMT(ice, IRQ)); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) { + snd_ice1712_free(ice); + return err; + } + + *r_ice1712 = ice; + return 0; +} + +static int __devinit snd_ice1712_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + ice1712_t *ice; + int pcm_dev = 0, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ice1712_create(card, pci, snd_omni[dev], &ice)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) + if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ice1712_ac97_mixer(ice)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ice1712_build_controls(ice)) < 0) { + snd_card_free(card); + return err; + } + + if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) + if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "ICE1712"); + strcpy(card->shortname, "ICEnsemble ICE1712"); + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_STDSP24: + strcpy(card->shortname, "Hoontech SoundTrack Audio DSP24"); + break; + case ICE1712_SUBDEVICE_DELTA1010: + strcpy(card->shortname, "M Audio Delta 1010"); + break; + case ICE1712_SUBDEVICE_DELTADIO2496: + strcpy(card->shortname, "M Audio Delta DiO 2496"); + goto __no_mpu401; + case ICE1712_SUBDEVICE_DELTA66: + strcpy(card->shortname, "M Audio Delta 66"); + goto __no_mpu401; + case ICE1712_SUBDEVICE_DELTA44: + strcpy(card->shortname, "M Audio Delta 44"); + goto __no_mpu401; + case ICE1712_SUBDEVICE_AUDIOPHILE: + strcpy(card->shortname, "M Audio Audiophile 24/96"); + break; + case ICE1712_SUBDEVICE_EWX2496: + strcpy(card->shortname, "TerraTec EWX 24/96"); + break; + case ICE1712_SUBDEVICE_EWS88MT: + strcpy(card->shortname, "TerraTec EWS 88MT"); + break; + case ICE1712_SUBDEVICE_EWS88D: + strcpy(card->shortname, "TerraTec EWS 88D"); + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + strcpy(card->shortname, "TerraTec DMX 6Fire"); + break; + } + + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, + ICEREG(ice, MPU1_CTRL), 1, + ice->irq, 0, + &ice->rmidi[0])) < 0) { + snd_card_free(card); + return err; + } + + if (ice->eeprom.codec & ICE1712_CFG_2xMPU401) + if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712, + ICEREG(ice, MPU2_CTRL), 1, + ice->irq, 0, + &ice->rmidi[1])) < 0) { + snd_card_free(card); + return err; + } + + __no_mpu401: + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, ice->port, ice->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_ice1712_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "ICE1712", + id_table: snd_ice1712_ids, + probe: snd_ice1712_probe, + remove: __devexit_p(snd_ice1712_remove), +}; + +static int __init alsa_card_ice1712_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("ICE1712 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ice1712_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ice1712_init) +module_exit(alsa_card_ice1712_exit) + +#ifndef MODULE + +/* format is: snd-ice1712=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_ice1712_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ice1712=", alsa_card_ice1712_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/intel8x0.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1495 @@ +/* + * ALSA driver for Intel ICH (i8x0) chipsets + * + * Copyright (c) 2000 Jaroslav Kysela + * + * + * This code also contains alpha support for SiS 735 chipsets provided + * by Mike Pieper . We have no datasheet + * for SiS735, so the code is not fully functional. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,MX440; SiS 7012"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Intel,82801AA}," + "{Intel,82901AB}," + "{Intel,82801BA}," + "{Intel,ICH3}," + "{Intel,MX440}," + "{SiS,SI7012}," + "{NVidia,NForce Audio}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Intel i8x0 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Intel i8x0 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Intel i8x0 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (0 = auto-detect)."); +MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:0"); + +/* + * Direct registers + */ + +#ifndef PCI_DEVICE_ID_INTEL_82801 +#define PCI_DEVICE_ID_INTEL_82801 0x2415 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82901 +#define PCI_DEVICE_ID_INTEL_82901 0x2425 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82801BA +#define PCI_DEVICE_ID_INTEL_82801BA 0x2445 +#endif +#ifndef PCI_DEVICE_ID_INTEL_440MX +#define PCI_DEVICE_ID_INTEL_440MX 0x7195 +#endif +#ifndef PCI_DEVICE_ID_INTEL_ICH3 +#define PCI_DEVICE_ID_INTEL_ICH3 0x2485 +#endif +#ifndef PCI_DEVICE_ID_SI_7012 +#define PCI_DEVICE_ID_SI_7012 0x7012 +#endif +#ifndef PCI_DEVICE_ID_NVIDIA_MCP_AUDIO +#define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO 0x01b1 +#endif + +#define DEVICE_INTEL 0 +#define DEVICE_SIS 1 + +#define ICHREG(ice, x) ((ice)->bmport + ICH_REG_##x) +#define ICHREG2(ice, x) ((ice)->bmport + x) + +/* capture block */ +#define ICH_REG_PI_BDBAR 0x00 /* dword - buffer descriptor list base address */ +#define ICH_REG_PI_CIV 0x04 /* byte - current index value */ +#define ICH_REG_PI_LVI 0x05 /* byte - last valid index */ +#define ICH_REG_LVI_MASK 0x1f +#define ICH_REG_PI_SR 0x06 /* byte - status register */ +#define ICH_FIFOE 0x10 /* FIFO error */ +#define ICH_BCIS 0x08 /* buffer completion interrupt status */ +#define ICH_LVBCI 0x04 /* last valid buffer completion interrupt */ +#define ICH_CELV 0x02 /* current equals last valid */ +#define ICH_DCH 0x01 /* DMA controller halted */ +#define ICH_REG_PI_PICB 0x08 /* word - position in current buffer */ +#define ICH_REG_PI_PIV 0x0a /* byte - prefetched index value */ +#define ICH_REG_PIV_MASK 0x1f /* mask */ +#define ICH_REG_PI_CR 0x0b /* byte - control register */ +#define ICH_IOCE 0x10 /* interrupt on completion enable */ +#define ICH_FEIE 0x08 /* fifo error interrupt enable */ +#define ICH_LVBIE 0x04 /* last valid buffer interrupt enable */ +#define ICH_RESETREGS 0x02 /* reset busmaster registers */ +#define ICH_STARTBM 0x01 /* start busmaster operation */ +/* playback block */ +#define ICH_REG_PO_BDBAR 0x10 /* dword - buffer descriptor list base address */ +#define ICH_REG_PO_CIV 0x14 /* byte - current index value */ +#define ICH_REG_PO_LVI 0x15 /* byte - last valid command */ +#define ICH_REG_PO_SR 0x16 /* byte - status register */ +#define ICH_REG_PO_PICB 0x18 /* word - position in current buffer */ +#define ICH_REG_PO_PIV 0x1a /* byte - prefetched index value */ +#define ICH_REG_PO_CR 0x1b /* byte - control register */ +/* mic capture block */ +#define ICH_REG_MC_BDBAR 0x20 /* dword - buffer descriptor list base address */ +#define ICH_REG_MC_CIV 0x24 /* byte - current index value */ +#define ICH_REG_MC_LVI 0x25 /* byte - last valid command */ +#define ICH_REG_MC_SR 0x26 /* byte - status register */ +#define ICH_REG_MC_PICB 0x28 /* word - position in current buffer */ +#define ICH_REG_MC_PIV 0x2a /* byte - prefetched index value */ +#define ICH_REG_MC_CR 0x2b /* byte - control register */ +/* global block */ +#define ICH_REG_GLOB_CNT 0x2c /* dword - global control */ +#define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */ +#define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */ +#define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */ +#define ICH_PCM_2 0x00000000 /* 2 channels (stereo) */ +#define ICH_SRIE 0x00000020 /* secondary resume interrupt enable */ +#define ICH_PRIE 0x00000010 /* primary resume interrupt enable */ +#define ICH_ACLINK 0x00000008 /* AClink shut off */ +#define ICH_AC97WARM 0x00000004 /* AC'97 warm reset */ +#define ICH_AC97COLD 0x00000002 /* AC'97 cold reset */ +#define ICH_GIE 0x00000001 /* GPI interrupt enable */ +#define ICH_REG_GLOB_STA 0x30 /* dword - global status */ +#define ICH_MD3 0x00020000 /* modem power down semaphore */ +#define ICH_AD3 0x00010000 /* audio power down semaphore */ +#define ICH_RCS 0x00008000 /* read completion status */ +#define ICH_BIT3 0x00004000 /* bit 3 slot 12 */ +#define ICH_BIT2 0x00002000 /* bit 2 slot 12 */ +#define ICH_BIT1 0x00001000 /* bit 1 slot 12 */ +#define ICH_SRI 0x00000800 /* secondary resume interrupt */ +#define ICH_PRI 0x00000400 /* primary resume interrupt */ +#define ICH_SCR 0x00000200 /* secondary codec ready */ +#define ICH_PCR 0x00000100 /* primary codec ready */ +#define ICH_MCINT 0x00000080 /* MIC capture interrupt */ +#define ICH_POINT 0x00000040 /* playback interrupt */ +#define ICH_PIINT 0x00000020 /* capture interrupt */ +#define ICH_MOINT 0x00000004 /* modem playback interrupt */ +#define ICH_MIINT 0x00000002 /* modem capture interrupt */ +#define ICH_GSCI 0x00000001 /* GPI status change interrupt */ +#define ICH_REG_ACC_SEMA 0x34 /* byte - codec write semaphore */ +#define ICH_CAS 0x01 /* codec access semaphore */ + +#define ICH_MAX_FRAGS 32 /* max hw frags */ + +/* + * + */ + +typedef struct { + unsigned long reg_offset; + u32 *bdbar; /* CPU address (32bit) */ + unsigned int bdbar_addr; /* PCI bus address (32bit) */ + snd_pcm_substream_t *substream; + unsigned int physbuf; /* physical address (32bit) */ + unsigned int size; + unsigned int fragsize; + unsigned int fragsize1; + unsigned int position; + int frags; + int lvi; + int lvi_frag; + int ack; + int ack_reload; +#ifdef CONFIG_PM + unsigned char civ_saved; + unsigned char piv_saved; + unsigned short picb_saved; +#endif +} ichdev_t; + +typedef struct _snd_intel8x0 intel8x0_t; +#define chip_t intel8x0_t + +struct _snd_intel8x0 { + unsigned int device_type; + + unsigned long dma_playback_size; + unsigned long dma_capture_size; + unsigned long dma_mic_size; + + int irq; + + unsigned long port; + struct resource *res_port; + unsigned long bmport; + struct resource *res_bmport; + + struct pci_dev *pci; + snd_card_t *card; + + snd_pcm_t *pcm; + snd_pcm_t *pcm_mic; + ichdev_t playback; + ichdev_t capture; + ichdev_t capture_mic; + + int multi4: 1, + multi6: 1; + int in_ac97_init: 1; + + ac97_t *ac97; + ac97_t *ac97sec; + + spinlock_t reg_lock; + spinlock_t ac97_lock; + snd_info_entry_t *proc_entry; + + u32 *bdbars; + dma_addr_t bdbars_addr; + + unsigned int reg_pi_sr; + unsigned int reg_pi_picb; + unsigned int reg_po_sr; + unsigned int reg_po_picb; + unsigned int reg_mc_sr; + unsigned int reg_mc_picb; + +#ifdef CONFIG_PM + int in_suspend; +#endif +}; + +static struct pci_device_id snd_intel8x0_ids[] __devinitdata = { + { 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801AA */ + { 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */ + { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */ + { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */ + { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ + { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ + { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */ + { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids); + +/* + * Basic I/O + */ +static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec) +{ + int time; + + /* codec ready ? */ + if ((inl(ICHREG(chip, GLOB_STA)) & (codec ? ICH_SCR : ICH_PCR)) == 0) + return -EIO; + + /* Anyone holding a semaphore for 1 msec should be shot... */ + time = 100; + do { + if (!(inb(ICHREG(chip, ACC_SEMA)) & ICH_CAS)) + return 0; + udelay(10); + } while (time--); + + /* access to some forbidden (non existant) ac97 registers will not + * reset the semaphore. So even if you don't get the semaphore, still + * continue the access. We don't need the semaphore anyway. */ + snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n", + inb(ICHREG(chip, ACC_SEMA)), inl(ICHREG(chip, GLOB_STA))); + inw(chip->port); /* clear semaphore flag */ + /* I don't care about the semaphore */ + return -EBUSY; +} + +static void snd_intel8x0_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + + spin_lock(&chip->ac97_lock); + if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) { + if (! chip->in_ac97_init) + snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg); + } + outw(val, chip->port + reg + ac97->num * 0x80); + spin_unlock(&chip->ac97_lock); +} + +static unsigned short snd_intel8x0_codec_read(ac97_t *ac97, + unsigned short reg) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0); + unsigned short res; + unsigned int tmp; + + spin_lock(&chip->ac97_lock); + if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) { + if (! chip->in_ac97_init) + snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg); + res = 0xffff; + } else { + res = inw(chip->port + reg + ac97->num * 0x80); + if ((tmp = inl(ICHREG(chip, GLOB_STA))) & ICH_RCS) { + /* reset RCS and preserve other R/WC bits */ + outl(tmp & ~(ICH_SRI|ICH_PRI|ICH_GSCI), ICHREG(chip, GLOB_STA)); + if (! chip->in_ac97_init) + snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg); + res = 0xffff; + } + } + spin_unlock(&chip->ac97_lock); + return res; +} + +static int snd_intel8x0_trigger(intel8x0_t *chip, ichdev_t *ichdev, int cmd) +{ + unsigned char val = 0; + unsigned long port = chip->bmport + ichdev->reg_offset; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + val = ICH_IOCE | ICH_STARTBM; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + val = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = ICH_IOCE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = ICH_IOCE | ICH_STARTBM; + break; + default: + return -EINVAL; + } + outb(val, port + ICH_REG_PI_CR); + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + /* reset whole DMA things */ + while (!(inb(port + chip->reg_pi_sr) & ICH_DCH)) ; + outb(ICH_RESETREGS, port + ICH_REG_PI_CR); + } + return 0; +} + +static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev) +{ + int idx; + u32 *bdbar = ichdev->bdbar; + unsigned long port = chip->bmport + ichdev->reg_offset; + int shiftlen = (chip->device_type == DEVICE_SIS) ? 0 : 1; + + outl(ichdev->bdbar_addr, port + ICH_REG_PI_BDBAR); + if (ichdev->size == ichdev->fragsize) { + ichdev->ack_reload = ichdev->ack = 2; + ichdev->fragsize1 = ichdev->fragsize >> 1; + for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 4) { + bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf); + bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */ + ichdev->fragsize1 >> shiftlen); + bdbar[idx + 2] = cpu_to_le32(ichdev->physbuf + (ichdev->size >> 1)); + bdbar[idx + 3] = cpu_to_le32(0x80000000 | /* interrupt on completion */ + ichdev->fragsize1 >> shiftlen); + } + ichdev->frags = 2; + } else { + ichdev->ack_reload = ichdev->ack = 1; + ichdev->fragsize1 = ichdev->fragsize; + for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 2) { + bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf + (((idx >> 1) * ichdev->fragsize) % ichdev->size)); + bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */ + ichdev->fragsize >> shiftlen); + // printk("bdbar[%i] = 0x%x [0x%x]\n", idx + 0, bdbar[idx + 0], bdbar[idx + 1]); + } + ichdev->frags = ichdev->size / ichdev->fragsize; + } + outb(ichdev->lvi = ICH_REG_LVI_MASK, port + ICH_REG_PI_LVI); + ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags; + ichdev->position = 0; +#if 0 + printk("lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n", + ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1); +#endif + /* clear interrupts */ + outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr); +} + +/* + * Interrupt handler + */ + +static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev) +{ + unsigned long port = chip->bmport + ichdev->reg_offset; + int ack = 0; + + spin_lock(&chip->reg_lock); + ichdev->position += ichdev->fragsize1; + ichdev->position %= ichdev->size; + ichdev->lvi++; + ichdev->lvi &= ICH_REG_LVI_MASK; + outb(ichdev->lvi, port + ICH_REG_PI_LVI); + ichdev->lvi_frag++; + ichdev->lvi_frag %= ichdev->frags; + ichdev->bdbar[ichdev->lvi * 2] = ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1; + // printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_PI_PIV + port), inl(port + 4), inb(port + ICH_REG_PI_CR)); + if ((ack = (--ichdev->ack == 0)) != 0) + ichdev->ack = ichdev->ack_reload; + spin_unlock(&chip->reg_lock); + if (ack && ichdev->substream) + snd_pcm_period_elapsed(ichdev->substream); + outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr); +} + +static void snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return); + unsigned int status; + + status = inl(ICHREG(chip, GLOB_STA)); + if ((status & (ICH_MCINT | ICH_POINT | ICH_PIINT)) == 0) + return; + if (status & ICH_POINT) + snd_intel8x0_update(chip, &chip->playback); + if (status & ICH_PIINT) + snd_intel8x0_update(chip, &chip->capture); + if (status & ICH_MCINT) + snd_intel8x0_update(chip, &chip->capture_mic); +} + +/* + * PCM part + */ + +static int snd_intel8x0_playback_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + int result; + result = snd_pcm_lib_ioctl(substream, cmd, arg); + if (result < 0) + return result; + return 0; +} + +static int snd_intel8x0_capture_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + int result; + result = snd_pcm_lib_ioctl(substream, cmd, arg); + if (result < 0) + return result; + return 0; +} + +static int snd_intel8x0_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_trigger(chip, &chip->playback, cmd); +} + +static int snd_intel8x0_capture_trigger(snd_pcm_substream_t *substream, int cmd) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_trigger(chip, &chip->capture, cmd); +} + +static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_intel8x0_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static void snd_intel8x0_setup_multi_channels(intel8x0_t *chip, int channels) +{ + unsigned int cnt = inl(ICHREG(chip, GLOB_CNT)) & ~ICH_PCM_246_MASK; + if (chip->multi4 && channels == 4) + cnt |= ICH_PCM_4; + else if (chip->multi6 && channels == 6) + cnt |= ICH_PCM_6; + outl(cnt, ICHREG(chip, GLOB_CNT)); +} + +static int snd_intel8x0_playback_prepare(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->playback.physbuf = runtime->dma_addr; + chip->playback.size = snd_pcm_lib_buffer_bytes(substream); + chip->playback.fragsize = snd_pcm_lib_period_bytes(substream); + spin_lock(&chip->reg_lock); + snd_intel8x0_setup_multi_channels(chip, runtime->channels); + spin_unlock(&chip->reg_lock); + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_intel8x0_setup_periods(chip, &chip->playback); + return 0; +} + +static int snd_intel8x0_capture_prepare(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture.physbuf = runtime->dma_addr; + chip->capture.size = snd_pcm_lib_buffer_bytes(substream); + chip->capture.fragsize = snd_pcm_lib_period_bytes(substream); + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + snd_intel8x0_setup_periods(chip, &chip->capture); + return 0; +} + +static snd_pcm_uframes_t snd_intel8x0_playback_pointer(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + ptr = chip->playback.fragsize1; + if (chip->device_type == DEVICE_SIS) + ptr -= inw(ICHREG2(chip,chip->reg_po_picb)); + else + ptr -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1; + ptr += chip->playback.position; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_intel8x0_capture_pointer(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + ptr = chip->capture.fragsize1; + if (chip->device_type == DEVICE_SIS) + ptr -= inw(ICHREG2(chip,chip->reg_pi_picb)); + else + ptr -= inw(ICHREG2(chip,chip->reg_pi_picb)) << 1; + ptr += chip->capture.position; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_intel8x0_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_intel8x0_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static unsigned int channels4[] = { + 2, 4, +}; + +#define CHANNELS4 sizeof(channels4) / sizeof(channels4[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_channels4 = { + count: CHANNELS4, + list: channels4, + mask: 0, +}; + +static unsigned int channels6[] = { + 2, 4, 6, +}; + +#define CHANNELS6 sizeof(channels6) / sizeof(channels6[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_channels6 = { + count: CHANNELS6, + list: channels6, + mask: 0, +}; + +static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->playback.substream = substream; + runtime->hw = snd_intel8x0_playback; + runtime->hw.rates = chip->ac97->rates_front_dac; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if (chip->device_type == DEVICE_SIS) { + runtime->hw.buffer_bytes_max = 64*1024; + runtime->hw.period_bytes_max = 64*1024; + } + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + if (chip->multi6) { + runtime->hw.channels_max = 6; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6); + } else if (chip->multi4) { + runtime->hw.channels_max = 4; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels4); + } + return 0; +} + +static int snd_intel8x0_capture_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->capture.substream = substream; + runtime->hw = snd_intel8x0_capture; + runtime->hw.rates = chip->ac97->rates_adc; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if (chip->device_type == DEVICE_SIS) { + runtime->hw.buffer_bytes_max = 64*1024; + runtime->hw.period_bytes_max = 64*1024; + } + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->playback.substream = NULL; + /* disable DAC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200); + return 0; +} + +static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->capture.substream = NULL; + /* disable ADC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100); + return 0; +} + +static snd_pcm_ops_t snd_intel8x0_playback_ops = { + open: snd_intel8x0_playback_open, + close: snd_intel8x0_playback_close, + ioctl: snd_intel8x0_playback_ioctl, + hw_params: snd_intel8x0_hw_params, + hw_free: snd_intel8x0_hw_free, + prepare: snd_intel8x0_playback_prepare, + trigger: snd_intel8x0_playback_trigger, + pointer: snd_intel8x0_playback_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_capture_ops = { + open: snd_intel8x0_capture_open, + close: snd_intel8x0_capture_close, + ioctl: snd_intel8x0_capture_ioctl, + hw_params: snd_intel8x0_hw_params, + hw_free: snd_intel8x0_hw_free, + prepare: snd_intel8x0_capture_prepare, + trigger: snd_intel8x0_capture_trigger, + pointer: snd_intel8x0_capture_pointer, +}; + +static void snd_intel8x0_pcm_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_intel8x0_pcm(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "Intel ICH", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->shortname); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * PCM code - MIC + */ + +static int snd_intel8x0_capture_mic_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + int result; + result = snd_pcm_lib_ioctl(substream, cmd, arg); + if (result < 0) + return result; + return 0; +} + +static int snd_intel8x0_capture_mic_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_trigger(chip, &chip->capture_mic, cmd); +} + +static int snd_intel8x0_capture_mic_prepare(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture_mic.physbuf = runtime->dma_addr; + chip->capture_mic.size = snd_pcm_lib_buffer_bytes(substream); + chip->capture_mic.fragsize = snd_pcm_lib_period_bytes(substream); + snd_ac97_set_rate(chip->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate); + snd_intel8x0_setup_periods(chip, &chip->capture_mic); + return 0; +} + +static snd_pcm_uframes_t snd_intel8x0_capture_mic_pointer(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + ptr = chip->capture_mic.fragsize1; + if (chip->device_type == DEVICE_SIS) + ptr -= inw(ICHREG2(chip,chip->reg_mc_picb)); + else + ptr -= inw(ICHREG2(chip,chip->reg_mc_picb)) << 1; + ptr += chip->capture_mic.position; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_intel8x0_capture_mic = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 1, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_intel8x0_capture_mic_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture_mic.substream = substream; + runtime->hw = snd_intel8x0_capture_mic; + runtime->hw.rates = chip->ac97->rates_mic_adc; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if (chip->device_type == DEVICE_SIS) { + runtime->hw.buffer_bytes_max = 64*1024; + runtime->hw.period_bytes_max = 64*1024; + } + return 0; +} + +static int snd_intel8x0_capture_mic_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_mic.substream = NULL; + /* disable ADC power */ + snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, 0x4000, 0x4000); + return 0; +} + +static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = { + open: snd_intel8x0_capture_mic_open, + close: snd_intel8x0_capture_mic_close, + ioctl: snd_intel8x0_capture_mic_ioctl, + hw_params: snd_intel8x0_hw_params, + hw_free: snd_intel8x0_hw_free, + prepare: snd_intel8x0_capture_mic_prepare, + trigger: snd_intel8x0_capture_mic_trigger, + pointer: snd_intel8x0_capture_mic_pointer, +}; + +static void snd_intel8x0_pcm_mic_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm_mic = NULL; +} + +static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "Intel ICH - MIC ADC", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic_ops); + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_pcm_mic_free; + pcm->info_flags = 0; + sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname); + + chip->pcm_mic = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer part + */ + +static void snd_intel8x0_codec_init(ac97_t *ac97) +{ + // intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + + /* disable DAC & ADC power */ + snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300); + /* disable center DAC/surround DAC/LFE DAC/MIC ADC */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800); +} + +static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + if (ac97->num == 0) { + chip->ac97 = NULL; + } else { + chip->ac97sec = NULL; + } +} + +static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock) +{ + ac97_t ac97; + int err; + + chip->in_ac97_init = 1; + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_intel8x0_codec_write; + ac97.read = snd_intel8x0_codec_read; + ac97.init = snd_intel8x0_codec_init; + ac97.private_data = chip; + ac97.private_free = snd_intel8x0_mixer_free_ac97; + if (ac97_clock >= 8000 && ac97_clock <= 48000) + ac97.clock = ac97_clock; + else + ac97.clock = 48000; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; +#if 0 /* it seems that SDIN signals are mixed together (at least for AD CNR boards) */ + if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR) { + ac97.num = 1; + ac97.addr = 1; + snd_ac97_mixer(chip->card, &ac97, &chip->ac97sec); + } +#endif + if ((inl(ICHREG(chip, GLOB_STA)) & (ICH_PCM_4|ICH_PCM_6)) != (ICH_PCM_4|ICH_PCM_6)) + return 0; + if ((chip->ac97->scaps & AC97_SCAP_SURROUND_DAC) || + (chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_SURROUND_DAC))) { + chip->multi4 = 1; + if ((chip->ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) || + (chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_CENTER_LFE_DAC))) + chip->multi6 = 1; + } + chip->in_ac97_init = 0; + return 0; +} + + +/* + * + */ + +static int snd_intel8x0_chip_init(intel8x0_t *chip) +{ + signed long end_time; + unsigned int cnt; + + /* put logic to right state */ + /* first clear status bits */ + cnt = inl(ICHREG(chip, GLOB_STA)); + outl(cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT), ICHREG(chip, GLOB_STA)); + + /* ACLink on, 2 channels */ + cnt = inl(ICHREG(chip, GLOB_CNT)); + cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK); + /* finish cold or do warm reset */ + cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM; + outl(cnt, ICHREG(chip, GLOB_CNT)); + end_time = (jiffies + (HZ / 4)) + 1; + do { + if ((inl(ICHREG(chip, GLOB_CNT)) & ICH_AC97WARM) == 0) + goto __ok; +#ifdef CONFIG_PM + if (chip->in_suspend) { + mdelay(10); + continue; + } +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("AC'97 warm reset still in progress? [0x%x]\n", inl(ICHREG(chip, GLOB_CNT))); + return -EIO; + + __ok: + /* wait for primary codec ready status. + * Once it becomes ready it should remain ready + * as long as we do not disable the ac97 link. + */ + end_time = jiffies + HZ; + do { + if (inl(ICHREG(chip, GLOB_STA)) & ICH_PCR) + goto __ok1; +#ifdef CONFIG_PM + if (chip->in_suspend) { + mdelay(10); + continue; + } +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("codec_ready: primary codec is not ready [0x%x]\n", inl(ICHREG(chip, GLOB_STA))); + return -EIO; + + __ok1: + /* wait for secondary codec ready status. No secondary codec? , ok */ + /* the end_time variable is not initialized again */ + do { + if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR) + break; +#ifdef CONFIG_PM + if (chip->in_suspend) { + mdelay(10); + continue; + } +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + + inw(chip->port); /* clear semaphore flag */ + + /* disable interrupts */ + outb(0x00, ICHREG(chip, PI_CR)); + outb(0x00, ICHREG(chip, PO_CR)); + outb(0x00, ICHREG(chip, MC_CR)); + /* reset channels */ + outb(ICH_RESETREGS, ICHREG(chip, PI_CR)); + outb(ICH_RESETREGS, ICHREG(chip, PO_CR)); + outb(ICH_RESETREGS, ICHREG(chip, MC_CR)); + /* initialize Buffer Descriptor Lists */ + outl(chip->playback.bdbar_addr, ICHREG(chip, PO_BDBAR)); + outl(chip->capture.bdbar_addr, ICHREG(chip, PI_BDBAR)); + outl(chip->capture_mic.bdbar_addr, ICHREG(chip, MC_BDBAR)); + return 0; +} + +static int snd_intel8x0_free(intel8x0_t *chip) +{ + if (chip->irq < 0) + goto __hw_end; + /* disable interrupts */ + outb(0x00, ICHREG(chip, PI_CR)); + outb(0x00, ICHREG(chip, PO_CR)); + outb(0x00, ICHREG(chip, MC_CR)); + /* reset channels */ + outb(ICH_RESETREGS, ICHREG(chip, PI_CR)); + outb(ICH_RESETREGS, ICHREG(chip, PO_CR)); + outb(ICH_RESETREGS, ICHREG(chip, MC_CR)); + /* --- */ + synchronize_irq(); + __hw_end: + if (chip->bdbars) + snd_free_pci_pages(chip->pci, 3 * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr); + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_bmport) { + release_resource(chip->res_bmport); + kfree_nocheck(chip->res_bmport); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +#ifdef CONFIG_PM +/* + * power management + */ +static void intel8x0_suspend(intel8x0_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + snd_pcm_suspend_all(chip->pcm); + if (chip->pcm_mic) + snd_pcm_suspend_all(chip->pcm_mic); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void intel8x0_resume(intel8x0_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + pci_enable_device(chip->pci); + snd_intel8x0_chip_init(chip); + snd_ac97_resume(chip->ac97); + + chip->in_suspend = 0; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_intel8x0_suspend(struct pci_dev *dev, u32 state) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return -ENXIO); + intel8x0_suspend(chip); + return 0; +} +static int snd_intel8x0_resume(struct pci_dev *dev) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return -ENXIO); + intel8x0_resume(chip); + return 0; +} +#else +static void snd_intel8x0_suspend(struct pci_dev *dev) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return); + intel8x0_suspend(chip); +} +static void snd_intel8x0_resume(struct pci_dev *dev) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return); + intel8x0_resume(chip); +} +#endif + +/* callback */ +static int snd_intel8x0_set_power_state(snd_card_t *card, unsigned int power_state) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + intel8x0_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + intel8x0_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +#define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */ + +static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip) +{ + snd_pcm_substream_t *subs; + unsigned long port; + unsigned long pos, t; + unsigned long flags; + struct timeval start_time, stop_time; + + if (chip->ac97->clock != 48000) + return; /* specified in module option */ + + subs = chip->pcm->streams[0].substream; + if (! subs || subs->dma_bytes < INTEL8X0_TESTBUF_SIZE) { + snd_printk("no playback buffer allocated - aborting measure ac97 clock\n"); + return; + } + chip->playback.physbuf = subs->dma_addr; + chip->playback.size = chip->playback.fragsize = INTEL8X0_TESTBUF_SIZE; + chip->playback.substream = NULL; /* don't process interrupts */ + + spin_lock_irqsave(&chip->reg_lock, flags); + /* set rate */ + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, 48000); + snd_intel8x0_setup_periods(chip, &chip->playback); + port = chip->bmport + chip->playback.reg_offset; + outb(ICH_IOCE | ICH_STARTBM, port + ICH_REG_PI_CR); /* trigger */ + do_gettimeofday(&start_time); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); +#else + /* FIXME: schedule() can take too long time and overlap the boundary.. */ + mdelay(50); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + /* check the position */ + pos = chip->playback.fragsize1; + if (chip->device_type == DEVICE_SIS) + pos -= inw(ICHREG2(chip,chip->reg_po_picb)); + else + pos -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1; + pos += chip->playback.position; + do_gettimeofday(&stop_time); + outb(0, port + ICH_REG_PI_CR); /* stop */ + /* reset whole DMA things */ + while (!(inb(port + chip->reg_pi_sr) & ICH_DCH)) + ; + outb(ICH_RESETREGS, port + ICH_REG_PI_CR); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + t = stop_time.tv_sec - start_time.tv_sec; + t *= 1000000; + if (stop_time.tv_usec < start_time.tv_usec) + t -= start_time.tv_usec - stop_time.tv_usec; + else + t += stop_time.tv_usec - start_time.tv_usec; + if (t == 0) { + snd_printk("?? calculation error..\n"); + return; + } + pos = (pos / 4) * 1000; + pos = (pos / t) * 1000 + ((pos % t) * 1000) / t; + if ((pos > 40000 && pos < 47500) || + (pos > 48500 && pos < 50000)) { + chip->ac97->clock = (chip->ac97->clock * 48000) / pos; + printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97->clock); + } +} + +static int snd_intel8x0_dev_free(snd_device_t *device) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, device->device_data, return -ENXIO); + return snd_intel8x0_free(chip); +} + +static int __devinit snd_intel8x0_create(snd_card_t * card, + struct pci_dev *pci, + unsigned long device_type, + intel8x0_t ** r_intel8x0) +{ + intel8x0_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_intel8x0_dev_free, + }; + char name[32]; + + *r_intel8x0 = NULL; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = snd_magic_kcalloc(intel8x0_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ac97_lock); + chip->device_type = device_type; + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->port = pci_resource_start(pci, 0); + sprintf(name, "%s - AC'97", card->shortname); + if ((chip->res_port = request_region(chip->port, 256, name)) == NULL) { + snd_intel8x0_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); + return -EBUSY; + } + sprintf(name, "%s - Controller", card->shortname); + chip->bmport = pci_resource_start(pci, 1); + if ((chip->res_bmport = request_region(chip->bmport, 64, name)) == NULL) { + snd_intel8x0_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmport, chip->bmport + 64 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) { + snd_intel8x0_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + pci_set_master(pci); + synchronize_irq(); + + /* initialize offsets */ + chip->reg_pi_sr = ICH_REG_PI_SR; + chip->reg_pi_picb = ICH_REG_PI_PICB; + chip->reg_po_sr = ICH_REG_PO_SR; + chip->reg_po_picb = ICH_REG_PO_PICB; + chip->reg_mc_sr = ICH_REG_MC_SR; + chip->reg_mc_picb = ICH_REG_MC_PICB; + if (device_type == DEVICE_SIS) { + chip->reg_pi_sr = ICH_REG_PI_PICB; + chip->reg_pi_picb = ICH_REG_PI_SR; + chip->reg_po_sr = ICH_REG_PO_PICB; + chip->reg_po_picb = ICH_REG_PO_SR; + chip->reg_mc_sr = ICH_REG_MC_PICB; + chip->reg_mc_picb = ICH_REG_MC_SR; + } + chip->playback.reg_offset = 0x10; + chip->capture.reg_offset = 0; + chip->capture_mic.reg_offset = 0x20; + + /* allocate buffer descriptor lists */ + /* the start of each lists must be aligned to 8 bytes */ + chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, 3 * sizeof(unsigned int) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr); + if (chip->bdbars == NULL) { + snd_intel8x0_free(chip); + return -ENOMEM; + } + /* tables must be aligned to 8 bytes here, but the kernel pages + are much bigger, so we don't care (on i386) */ +#ifndef __i386__ + /* .. not sure on other architectures, so we check now. */ + if (chip->bdbars_addr & ~((dma_addr_t)0xffffffff | 0x07)) { + snd_printk("invalid i/o port address %lx\n", (unsigned long)chip->bdbars_addr); + snd_intel8x0_free(chip); + return -ENOMEM; + } +#endif + chip->playback.bdbar = chip->bdbars; /* crop to 32bit */ + chip->playback.bdbar_addr = (unsigned int)chip->bdbars_addr; + chip->capture.bdbar = chip->playback.bdbar + ICH_MAX_FRAGS * 2; + chip->capture.bdbar_addr = chip->playback.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2; + chip->capture_mic.bdbar = chip->capture.bdbar + ICH_MAX_FRAGS * 2; + chip->capture_mic.bdbar_addr = chip->capture.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2; + + if ((err = snd_intel8x0_chip_init(chip)) < 0) { + snd_intel8x0_free(chip); + return err; + } + +#ifdef CONFIG_PM + card->set_power_state = snd_intel8x0_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_intel8x0_free(chip); + return err; + } + + *r_intel8x0 = chip; + return 0; +} + +static struct shortname_table { + unsigned int id; + const char *s; +} shortnames[] __devinitdata = { + { PCI_DEVICE_ID_INTEL_82801, "Intel ICH 82801AA" }, + { PCI_DEVICE_ID_INTEL_82901, "Intel ICH 82901AB" }, + { PCI_DEVICE_ID_INTEL_440MX, "Intel 440MX" }, + { PCI_DEVICE_ID_INTEL_ICH3, "Intel ICH3" }, + { PCI_DEVICE_ID_SI_7012, "SiS SI7012" }, + { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" }, + { 0, 0 }, +}; + +static int __devinit snd_intel8x0_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + intel8x0_t *chip; + int pcm_dev = 0, err; + struct shortname_table *name; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "ICH"); + strcpy(card->shortname, "Intel ICH"); + for (name = shortnames; name->id; name++) { + if (pci->device == name->id) { + strcpy(card->shortname, name->s); + break; + } + } + + if ((err = snd_intel8x0_create(card, pci, id->driver_data, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_intel8x0_mixer(chip, snd_ac97_clock[dev])) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_intel8x0_pcm(chip, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (chip->ac97->ext_id & 0x0008) { /* MIC VRM */ + if ((err = snd_intel8x0_pcm_mic(chip, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->port, chip->irq); + + if (! snd_ac97_clock[dev]) + intel8x0_measure_ac97_clock(chip); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_intel8x0_remove(struct pci_dev *pci) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "Intel ICH", + id_table: snd_intel8x0_ids, + probe: snd_intel8x0_probe, + remove: __devexit_p(snd_intel8x0_remove), +#ifdef CONFIG_PM + suspend: snd_intel8x0_suspend, + resume: snd_intel8x0_resume, +#endif +}; + +static int __init alsa_card_intel8x0_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("Intel ICH soundcard not found or device busy\n"); +#endif + return err; + } + return 0; + +} + +static void __exit alsa_card_intel8x0_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_intel8x0_init) +module_exit(alsa_card_intel8x0_exit) + +#ifndef MODULE + +/* format is: snd-intel8x0=snd_enable,snd_index,snd_id,snd_ac97_clock */ + +static int __init alsa_card_intel8x0_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_ac97_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-intel8x0=", alsa_card_intel8x0_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/korg1212/Makefile b/sound/pci/korg1212/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/korg1212/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _korg1212.o + +list-multi := snd-korg1212.o + +snd-korg1212-objs := korg1212.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_KORG1212) += snd-korg1212.o + +include $(TOPDIR)/Rules.make + +snd-korg1212.o: $(snd-korg1212-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-korg1212-objs) diff -Nru a/sound/pci/korg1212/korg1212-firmware.h b/sound/pci/korg1212/korg1212-firmware.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/korg1212/korg1212-firmware.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,987 @@ +static char dspCode [] = { +0x01,0xff,0x18,0xff,0xf5,0xff,0xcf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x26,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x38,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,0x67,0xff,0x40,0xff,0xff,0xff,0xc0,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x0c,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff, +0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x72,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x02,0xff,0x91,0xff,0xff,0xff,0x80,0xff, +0x02,0xff,0x91,0xff,0xff,0xff,0x90,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0xff,0xff,0x47,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x17,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x17,0xff, +0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff, +0x02,0xff,0x34,0xff,0xff,0xff,0x4a,0xff,0x02,0xff,0x38,0xff,0xff,0xff,0x4a,0xff, +0x01,0xff,0x34,0xff,0xff,0xff,0x2b,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x2b,0xff, +0x80,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x81,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x60,0xff, +0x84,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x70,0xff, +0x85,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x82,0xff,0x37,0xff,0xff,0xff,0x81,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x88,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x82,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x8c,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x83,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x8a,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x82,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x8e,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x82,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x83,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0x80,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x07,0xff, +0x40,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x02,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xbb,0xff, +0x02,0xff,0x41,0xff,0xff,0xff,0x82,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xcb,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0xe2,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xdb,0xff, +0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x41,0xff,0xff,0xff,0x02,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0x82,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0x9b,0xff,0x03,0xff,0x41,0xff,0xff,0xff,0x02,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xa2,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x21,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x40,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x85,0xff,0x0a,0xff,0x14,0xff,0xff,0xff,0xae,0xff, +0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff, +0x0a,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff, +0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x0b,0xff,0x14,0xff,0xff,0xff,0x4e,0xff, +0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x01,0xff, +0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x03,0xff,0x35,0xff,0xff,0xff,0x01,0xff, +0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x5b,0xff,0x40,0xff,0xff,0xff,0xf0,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0xdf,0xff,0x40,0xff,0xff,0xff,0xf0,0xff, +0xfe,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0xc1,0xff,0x41,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x04,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x23,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff, +0x59,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x0c,0xff,0x14,0xff,0xff,0xff,0xe4,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x24,0xff, +0x00,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0xd0,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0x30,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x84,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff, +0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x76,0xff,0x1c,0xff,0xff,0xff,0x9f,0xff, +0x86,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0x64,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0x81,0xff, +0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x61,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,0x77,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff, +0x63,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff, +0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff, +0x0f,0xff,0x14,0xff,0xff,0xff,0x6e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0xe0,0xff, +0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff, +0x10,0xff,0x14,0xff,0xff,0xff,0x0e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x79,0xff,0x1c,0xff,0xff,0xff,0xff,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff, +0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x80,0xff,0x35,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x40,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x11,0xff,0x14,0xff,0xff,0xff,0x4e,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff, +0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x63,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x83,0xff,0x37,0xff,0xff,0xff,0x80,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x83,0xff,0x43,0xff,0xff,0xff,0x00,0xff, +0x87,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x40,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x90,0xff, +0x80,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xa0,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x13,0xff,0x14,0xff,0xff,0xff,0xbe,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x00,0xff, +0x92,0xff,0x43,0xff,0xff,0xff,0x01,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0xe1,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x72,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff, +0x38,0xff,0x42,0xff,0xff,0xff,0x50,0xff,0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x20,0xff,0x1c,0xff,0xff,0xff,0xcf,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff, +0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff, +0x79,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x19,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff, +0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff, +0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff, +0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff, +0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x01,0xff, +0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff, +0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x8b,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x40,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff, +0x1b,0xff,0x14,0xff,0xff,0xff,0xce,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe1,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x9b,0xff,0xff,0xff,0xa0,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x1d,0xff,0x14,0xff,0xff,0xff,0x3e,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x51,0xff, +0x79,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x14,0xff,0xff,0xff,0xd5,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff, +0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff, +0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff, +0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff, +0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x8d,0xff,0x8f,0xff,0xff,0xff,0xc5,0xff,0x20,0xff,0x14,0xff,0xff,0xff,0xae,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff, +0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,0x21,0xff,0x14,0xff,0xff,0xff,0x5e,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff, +0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0c,0xff,0x18,0xff,0xff,0xff,0x90,0xff, +0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff, +0x98,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xa5,0xff, +0x24,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff, +0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff, +0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff, +0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff, +0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff, +0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff, +0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x58,0xff, +0x10,0xff,0x0f,0xff,0xff,0xff,0x01,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x26,0xff,0x18,0xff,0xff,0xff,0x94,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x80,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x16,0xff,0x0f,0xff,0xff,0xff,0x02,0xff, +0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff, +0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff, +0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,0x27,0xff,0x18,0xff,0xff,0xff,0xd2,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x29,0xff,0x18,0xff,0xff,0xff,0x92,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x03,0xff, +0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x31,0xff,0x18,0xff,0xff,0xff,0x12,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x34,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x55,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x18,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x48,0xff,0x10,0xff,0x0f,0xff,0xff,0xff,0xfe,0xff, +0x87,0xff,0x93,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x2e,0xff, +0x02,0xff,0x40,0xff,0xff,0xff,0x06,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa1,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x28,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x38,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff, +0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0xff,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x32,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x44,0xff, +0x08,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x8a,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x5a,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x35,0xff,0x18,0xff,0xff,0xff,0xd2,0xff, +0x00,0xff,0x4c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff, +0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff, +0x35,0xff,0x1c,0xff,0xff,0xff,0x24,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x33,0xff,0x18,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xc0,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x34,0xff,0x18,0xff,0xff,0xff,0x85,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x80,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff, +0x0d,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff, +0xff,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x90,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x37,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x30,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x40,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x50,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x39,0xff,0x18,0xff,0xff,0xff,0x22,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x20,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x38,0xff,0x1c,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x41,0xff,0x18,0xff,0xff,0xff,0x30,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xe4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x42,0xff,0x18,0xff,0xff,0xff,0x40,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xd4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x47,0xff,0x18,0xff,0xff,0xff,0xa0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xc4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x18,0xff,0xff,0xff,0xd0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xb4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x48,0xff,0x18,0xff,0xff,0xff,0xe0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xa4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4a,0xff,0x18,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x94,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4c,0xff,0x18,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x84,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4d,0xff,0x18,0xff,0xff,0xff,0xe0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x74,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0x20,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x64,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xf4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x44,0xff,0x18,0xff,0xff,0xff,0x40,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xe4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x45,0xff,0x18,0xff,0xff,0xff,0x50,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3d,0xff,0x18,0xff,0xff,0xff,0xd0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x10,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x80,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x34,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x44,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x40,0xff,0x18,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x21,0xff,0x40,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x25,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0xe9,0xff,0x41,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0xed,0xff,0x41,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf1,0xff,0x41,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x04,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0x23,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff, +0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0xe2,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x0c,0xff,0x0d,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf2,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0xe3,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff, +0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x23,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x32,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff, +0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x05,0xff,0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x0c,0xff,0x0d,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xc7,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff, +0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe7,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x51,0xff,0x14,0xff,0xff,0xff,0x81,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff, +0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff, +0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff, +0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff, +0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x18,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff, +0x54,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff, +0x54,0xff,0x14,0xff,0xff,0xff,0x1e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb3,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x55,0xff,0x14,0xff,0xff,0xff,0x21,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x31,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x54,0xff,0x18,0xff,0xff,0xff,0xd5,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x82,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff, +0xff,0xff,0x4f,0xff,0xff,0xff,0xf1,0xff,0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x56,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x54,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x14,0xff,0xff,0xff,0x91,0xff, +0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff, +0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff, +0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff, +0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff, +0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff, +0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x57,0xff,0x18,0xff,0xff,0xff,0x10,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff, +0x5a,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff, +0x5a,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x59,0xff,0x18,0xff,0xff,0xff,0xd3,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x5b,0xff,0x14,0xff,0xff,0xff,0x61,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x5e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x5b,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x05,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x5e,0xff,0x1c,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x50,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x5f,0xff,0x1c,0xff,0xff,0xff,0x54,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0xa8,0xff, +0x10,0xff,0x0f,0xff,0xff,0xff,0x08,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x90,0xff, +0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0xb6,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xfa,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff, +0x90,0xff,0x80,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe2,0xff, +0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0xe8,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xac,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0xd4,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff, +0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xa8,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xb0,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xd8,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff, +0x01,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x01,0xff,0x42,0xff,0xff,0xff,0x80,0xff, +0x00,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x07,0xff,0x42,0xff,0xff,0xff,0x80,0xff, +0x03,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x02,0xff,0x42,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x08,0xff,0x42,0xff,0xff,0xff,0x00,0xff, +0x03,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x01,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x04,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x04,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff, +0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff, +0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,0x65,0xff,0x14,0xff,0xff,0xff,0x9e,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff, +0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x80,0xff,0xff,0xff,0x48,0xff, +0x10,0xff,0x0f,0xff,0xff,0xff,0x03,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x74,0xff,0x18,0xff,0xff,0xff,0x54,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0xc0,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff, +0x86,0xff,0x83,0xff,0xff,0xff,0x80,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x8a,0xff, +0x67,0xff,0x1c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xd0,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x8b,0xff,0xff,0xff,0xb0,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0xf4,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x44,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x55,0xff, +0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0xa0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0x24,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0x35,0xff,0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff, +0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff, +0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x6a,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xb0,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x01,0xff,0x34,0xff,0xff,0xff,0x04,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,0x87,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x88,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x01,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x6a,0xff,0x14,0xff,0xff,0xff,0x9e,0xff, +0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x6a,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x82,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xf4,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x86,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x64,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff, +0x01,0xff,0x34,0xff,0xff,0xff,0x96,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x65,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x96,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x49,0xff, +0x02,0xff,0x38,0xff,0xff,0xff,0x49,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff, +0x10,0xff,0x40,0xff,0xff,0xff,0x02,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6d,0xff,0x14,0xff,0xff,0xff,0xbe,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc5,0xff, +0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xc6,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6e,0xff,0x14,0xff,0xff,0xff,0x8e,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc4,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xe9,0xff, +0x01,0xff,0x38,0xff,0xff,0xff,0x28,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x29,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x66,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x75,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff, +0x00,0xff,0x41,0xff,0xff,0xff,0x07,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x98,0xff,0x70,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff, +0x70,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x41,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff, +0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x46,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x45,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x41,0xff, +0x63,0xff,0x6a,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff, +0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x44,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0xf0,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x44,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff, +0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x72,0xff,0x14,0xff,0xff,0xff,0x35,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x30,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x10,0xff,0x90,0xff,0xff,0xff,0x10,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x10,0xff,0x90,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x10,0xff,0x90,0xff,0xff,0xff,0xb0,0xff,0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff, +0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x40,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff, +0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x10,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff, +0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x20,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff, +0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x48,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xb0,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x86,0xff,0x97,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0x90,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xb0,0xff, +0x88,0xff,0x37,0xff,0xff,0xff,0x03,0xff,0x8c,0xff,0x3b,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff, +0x82,0xff,0x43,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x60,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x0c,0xff,0x0d,0xff,0xff,0xff,0x80,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x80,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff, +0x76,0xff,0x14,0xff,0xff,0xff,0xde,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff, +0x84,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,0x77,0xff,0x14,0xff,0xff,0xff,0x2e,0xff, +0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff, +0x77,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x64,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x77,0xff,0x14,0xff,0xff,0xff,0xe5,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff, +0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x78,0xff,0x14,0xff,0xff,0xff,0x35,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x56,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x01,0xff,0x40,0xff,0xff,0xff,0xc1,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x15,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x45,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xc8,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x01,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff, +0x7e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xb1,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xc5,0xff, +0x7a,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0x46,0xff, +0xe1,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x7a,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xa7,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff }; diff -Nru a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/korg1212/korg1212.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2350 @@ +/* + * Driver for the Korg 1212 IO PCI card + * + * Copyright (c) 2001 Haroldo Gamal + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define DEBUG 1 +//#define LARGEALLOC 1 +#define PRINTK printk + +// ---------------------------------------------------------------------------- +// the following enum defines the valid states of the Korg 1212 I/O card. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_STATE_NONEXISTENT, // there is no card here + K1212_STATE_UNINITIALIZED, // the card is awaiting DSP download + K1212_STATE_DSP_IN_PROCESS, // the card is currently downloading its DSP code + K1212_STATE_DSP_COMPLETE, // the card has finished the DSP download + K1212_STATE_READY, // the card can be opened by an application. Any application + // requests prior to this state should fail. Only an open + // request can be made at this state. + K1212_STATE_OPEN, // an application has opened the card + K1212_STATE_SETUP, // the card has been setup for play + K1212_STATE_PLAYING, // the card is playing + K1212_STATE_MONITOR, // the card is in the monitor mode + K1212_STATE_CALIBRATING, // the card is currently calibrating + K1212_STATE_ERRORSTOP, // the card has stopped itself because of an error and we + // are in the process of cleaning things up. + K1212_STATE_MAX_STATE // state values of this and beyond are invalid +} CardState; + +// ---------------------------------------------------------------------------- +// The following enumeration defines the constants written to the card's +// host-to-card doorbell to initiate a command. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_DB_RequestForData = 0, // sent by the card to request a buffer fill. + K1212_DB_TriggerPlay = 1, // starts playback/record on the card. + K1212_DB_SelectPlayMode = 2, // select monitor, playback setup, or stop. + K1212_DB_ConfigureBufferMemory = 3, // tells card where the host audio buffers are. + K1212_DB_RequestAdatTimecode = 4, // asks the card for the latest ADAT timecode value. + K1212_DB_SetClockSourceRate = 5, // sets the clock source and rate for the card. + K1212_DB_ConfigureMiscMemory = 6, // tells card where other buffers are. + K1212_DB_TriggerFromAdat = 7, // tells card to trigger from Adat at a specific + // timecode value. + K1212_DB_RebootCard = 0xA0, // instructs the card to reboot. + K1212_DB_BootFromDSPPage4 = 0xA4, // instructs the card to boot from the DSP microcode + // on page 4 (local page to card). + K1212_DB_DSPDownloadDone = 0xAE, // sent by the card to indicate the download has + // completed. + K1212_DB_StartDSPDownload = 0xAF // tells the card to download its DSP firmware. +} korg1212_dbcnst_t; + +#define K1212_ISRCODE_DMAERROR 0x80 +#define K1212_ISRCODE_CARDSTOPPED 0x81 + +// ---------------------------------------------------------------------------- +// The following enumeration defines return codes for DeviceIoControl() calls +// to the Korg 1212 I/O driver. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_CMDRET_Success = 0, // command was successfully placed + K1212_CMDRET_DIOCFailure, // the DeviceIoControl call failed + K1212_CMDRET_PMFailure, // the protected mode call failed + K1212_CMDRET_FailUnspecified, // unspecified failure + K1212_CMDRET_FailBadState, // the specified command can not be given in + // the card's current state. (or the wave device's + // state) + K1212_CMDRET_CardUninitialized, // the card is uninitialized and cannot be used + K1212_CMDRET_BadIndex, // an out of range card index was specified + K1212_CMDRET_BadHandle, // an invalid card handle was specified + K1212_CMDRET_NoFillRoutine, // a play request has been made before a fill routine set + K1212_CMDRET_FillRoutineInUse, // can't set a new fill routine while one is in use + K1212_CMDRET_NoAckFromCard, // the card never acknowledged a command + K1212_CMDRET_BadParams, // bad parameters were provided by the caller + + // -------------------------------------------------------------- + // the following return errors are specific to the wave device + // driver interface. These will not be encountered by users of + // the 32 bit DIOC interface (a.k.a. custom or native API). + // -------------------------------------------------------------- + K1212_CMDRET_BadDevice, // the specified wave device was out of range + K1212_CMDRET_BadFormat // the specified wave format is unsupported +} snd_korg1212rc; + +// ---------------------------------------------------------------------------- +// The following enumeration defines the constants used to select the play +// mode for the card in the SelectPlayMode command. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_MODE_SetupPlay = 0x00000001, // provides card with pre-play information + K1212_MODE_MonitorOn = 0x00000002, // tells card to turn on monitor mode + K1212_MODE_MonitorOff = 0x00000004, // tells card to turn off monitor mode + K1212_MODE_StopPlay = 0x00000008 // stops playback on the card +} PlayModeSelector; + +// ---------------------------------------------------------------------------- +// The following enumeration defines the constants used to select the monitor +// mode for the card in the SetMonitorMode command. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_MONMODE_Off = 0, // tells card to turn off monitor mode + K1212_MONMODE_On // tells card to turn on monitor mode +} MonitorModeSelector; + +#define MAILBOX0_OFFSET 0x40 // location of mailbox 0 relative to base address +#define MAILBOX1_OFFSET 0x44 // location of mailbox 1 relative to base address +#define MAILBOX2_OFFSET 0x48 // location of mailbox 2 relative to base address +#define MAILBOX3_OFFSET 0x4c // location of mailbox 3 relative to base address +#define OUT_DOORBELL_OFFSET 0x60 // location of PCI to local doorbell " +#define IN_DOORBELL_OFFSET 0x64 // location of local to PCI doorbell " +#define STATUS_REG_OFFSET 0x68 // location of interrupt control/status register " +#define PCI_CONTROL_OFFSET 0x6c // location of the EEPROM, PCI, User I/O, init control + // register +#define SENS_CONTROL_OFFSET 0x6e // location of the input sensitivity setting register. + // this is the upper word of the PCI control reg. +#define DEV_VEND_ID_OFFSET 0x70 // location of the device and vendor ID register + +#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement + // from the card after sending a command. +#define INTERCOMMAND_DELAY 40 +#define MAX_COMMAND_RETRIES 5 // maximum number of times the driver will attempt + // to send a command before giving up. +#define COMMAND_ACK_MASK 0x8000 // the MSB is set in the command acknowledgment from + // the card. +#define DOORBELL_VAL_MASK 0x00FF // the doorbell value is one byte + +#define CARD_BOOT_DELAY_IN_MS 10 + +#define DSP_BOOT_DELAY_IN_MS 200 + +#define kNumBuffers 8 +#define k1212MaxCards 4 +#define k1212NumWaveDevices 6 +#define k16BitChannels 10 +#define k32BitChannels 2 +#define kAudioChannels (k16BitChannels + k32BitChannels) +#define kPlayBufferFrames 1024 + +#define K1212_CHANNELS 16 +#define K1212_FRAME_SIZE (sizeof(KorgAudioFrame)) +#define K1212_MAX_SAMPLES (kPlayBufferFrames*kNumBuffers) +#define K1212_PERIODS (K1212_BUF_SIZE/K1212_BLOCK_SIZE) +#define K1212_PERIOD_BYTES (K1212_BLOCK_SIZE) +#define K1212_BLOCK_SIZE (K1212_FRAME_SIZE*kPlayBufferFrames) +#define K1212_BUF_SIZE (K1212_BLOCK_SIZE*kNumBuffers) + +#define k1212MinADCSens 0x7f +#define k1212MaxADCSens 0x00 +#define k1212MaxVolume 0x7fff +#define k1212MaxWaveVolume 0xffff +#define k1212MinVolume 0x0000 +#define k1212MaxVolInverted 0x8000 + +// ----------------------------------------------------------------- +// the following bits are used for controlling interrupts in the +// interrupt control/status reg +// ----------------------------------------------------------------- +#define PCI_INT_ENABLE_BIT 0x00000100 +#define PCI_DOORBELL_INT_ENABLE_BIT 0x00000200 +#define LOCAL_INT_ENABLE_BIT 0x00010000 +#define LOCAL_DOORBELL_INT_ENABLE_BIT 0x00020000 +#define LOCAL_DMA1_INT_ENABLE_BIT 0x00080000 + +// ----------------------------------------------------------------- +// the following bits are defined for the PCI command register +// ----------------------------------------------------------------- +#define PCI_CMD_MEM_SPACE_ENABLE_BIT 0x0002 +#define PCI_CMD_IO_SPACE_ENABLE_BIT 0x0001 +#define PCI_CMD_BUS_MASTER_ENABLE_BIT 0x0004 + +// ----------------------------------------------------------------- +// the following bits are defined for the PCI status register +// ----------------------------------------------------------------- +#define PCI_STAT_PARITY_ERROR_BIT 0x8000 +#define PCI_STAT_SYSTEM_ERROR_BIT 0x4000 +#define PCI_STAT_MASTER_ABORT_RCVD_BIT 0x2000 +#define PCI_STAT_TARGET_ABORT_RCVD_BIT 0x1000 +#define PCI_STAT_TARGET_ABORT_SENT_BIT 0x0800 + +// ------------------------------------------------------------------------ +// the following constants are used in setting the 1212 I/O card's input +// sensitivity. +// ------------------------------------------------------------------------ +#define SET_SENS_LOCALINIT_BITPOS 15 +#define SET_SENS_DATA_BITPOS 10 +#define SET_SENS_CLOCK_BITPOS 8 +#define SET_SENS_LOADSHIFT_BITPOS 0 + +#define SET_SENS_LEFTCHANID 0x00 +#define SET_SENS_RIGHTCHANID 0x01 + +#define K1212SENSUPDATE_DELAY_IN_MS 50 + +// -------------------------------------------------------------------------- +// WaitRTCTicks +// +// This function waits the specified number of real time clock ticks. +// According to the DDK, each tick is ~0.8 microseconds. +// The defines following the function declaration can be used for the +// numTicksToWait parameter. +// -------------------------------------------------------------------------- +#define ONE_RTC_TICK 1 +#define SENSCLKPULSE_WIDTH 4 +#define LOADSHIFT_DELAY 4 +#define INTERCOMMAND_DELAY 40 +#define STOPCARD_DELAY 300 // max # RTC ticks for the card to stop once we write + // the command register. (could be up to 180 us) +#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement + // from the card after sending a command. + +#include "korg1212-firmware.h" + +typedef struct _snd_korg1212 korg1212_t; + +typedef u16 K1212Sample; // channels 0-9 use 16 bit samples +typedef u32 K1212SpdifSample; // channels 10-11 use 32 bits - only 20 are sent + // across S/PDIF. +typedef u32 K1212TimeCodeSample; // holds the ADAT timecode value + +typedef enum { + K1212_CLKIDX_AdatAt44_1K = 0, // selects source as ADAT at 44.1 kHz + K1212_CLKIDX_AdatAt48K, // selects source as ADAT at 48 kHz + K1212_CLKIDX_WordAt44_1K, // selects source as S/PDIF at 44.1 kHz + K1212_CLKIDX_WordAt48K, // selects source as S/PDIF at 48 kHz + K1212_CLKIDX_LocalAt44_1K, // selects source as local clock at 44.1 kHz + K1212_CLKIDX_LocalAt48K, // selects source as local clock at 48 kHz + K1212_CLKIDX_Invalid // used to check validity of the index +} ClockSourceIndex; + +typedef enum { + K1212_CLKIDX_Adat = 0, // selects source as ADAT + K1212_CLKIDX_Word, // selects source as S/PDIF + K1212_CLKIDX_Local // selects source as local clock +} ClockSourceType; + +typedef struct KorgAudioFrame { + K1212Sample frameData16[k16BitChannels]; + K1212SpdifSample frameData32[k32BitChannels]; + K1212TimeCodeSample timeCodeVal; +} KorgAudioFrame; + +typedef struct KorgAudioBuffer { + KorgAudioFrame bufferData[kPlayBufferFrames]; /* buffer definition */ +} KorgAudioBuffer; + +typedef struct KorgSharedBuffer { +#ifdef LARGEALLOC + KorgAudioBuffer playDataBufs[kNumBuffers]; + KorgAudioBuffer recordDataBufs[kNumBuffers]; +#endif + short volumeData[kAudioChannels]; + u32 cardCommand; + u16 routeData [kAudioChannels]; + u32 AdatTimeCode; // ADAT timecode value +} KorgSharedBuffer; + +typedef struct SensBits { + union { + struct { + unsigned int leftChanVal:8; + unsigned int leftChanId:8; + } v; + u16 leftSensBits; + } l; + union { + struct { + unsigned int rightChanVal:8; + unsigned int rightChanId:8; + } v; + u16 rightSensBits; + } r; +} SensBits; + +struct _snd_korg1212 { + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm16; + int irq; + + spinlock_t lock; + + wait_queue_head_t wait; + + unsigned long iomem; + unsigned long ioport; + unsigned long iomem2; + unsigned long irqcount; + unsigned long inIRQ; + unsigned long iobase; + + struct resource *res_iomem; + struct resource *res_ioport; + struct resource *res_iomem2; + + u32 dspCodeSize; + u32 dspMemPhy; // DSP memory block handle (Physical Address) + void * dspMemPtr; // block memory (Virtual Address) + + u32 DataBufsSize; + + KorgAudioBuffer * playDataBufsPtr; + KorgAudioBuffer * recordDataBufsPtr; + + KorgSharedBuffer * sharedBufferPtr; + u32 RecDataPhy; + u32 PlayDataPhy; + unsigned long sharedBufferPhy; + u32 VolumeTablePhy; + u32 RoutingTablePhy; + u32 AdatTimeCodePhy; + + u32 * statusRegPtr; // address of the interrupt status/control register + u32 * outDoorbellPtr; // address of the host->card doorbell register + u32 * inDoorbellPtr; // address of the card->host doorbell register + u32 * mailbox0Ptr; // address of mailbox 0 on the card + u32 * mailbox1Ptr; // address of mailbox 1 on the card + u32 * mailbox2Ptr; // address of mailbox 2 on the card + u32 * mailbox3Ptr; // address of mailbox 3 on the card + u32 * controlRegPtr; // address of the EEPROM, PCI, I/O, Init ctrl reg + u16 * sensRegPtr; // address of the sensitivity setting register + u32 * idRegPtr; // address of the device and vendor ID registers + + + size_t periodsize; + size_t currentBuffer; + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_info_entry_t * proc_entry; + + CardState cardState; + int running; + int idleMonitorOn; // indicates whether the card is in idle monitor mode. + u32 cmdRetryCount; // tracks how many times we have retried sending to the card. + + ClockSourceIndex clkSrcRate; // sample rate and clock source + + ClockSourceType clkSource; // clock source + int clkRate; // clock rate + + int volumePhase[kAudioChannels]; + + u16 leftADCInSens; // ADC left channel input sensitivity + u16 rightADCInSens; // ADC right channel input sensitivity +}; + +EXPORT_NO_SYMBOLS; +MODULE_DESCRIPTION("korg1212"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{KORG,korg1212}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Korg 1212 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Korg 1212 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Korg 1212 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_AUTHOR("Haroldo Gamal "); + +static struct pci_device_id snd_korg1212_ids[] __devinitdata = { + { 0x10b5, 0x906d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + +static char* stateName[] = { + "Non-existent", + "Uninitialized", + "DSP download in process", + "DSP download complete", + "Ready", + "Open", + "Setup for play", + "Playing", + "Monitor mode on", + "Calibrating" + "Invalid" +}; + +static char* clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" }; + +static char* clockSourceName[] = { + "ADAT at 44.1 kHz", + "ADAT at 48 kHz", + "S/PDIF at 44.1 kHz", + "S/PDIF at 48 kHz", + "local clock at 44.1 kHz", + "local clock at 48 kHz" +}; + +static char* channelName[] = { + "ADAT-1", + "ADAT-2", + "ADAT-3", + "ADAT-4", + "ADAT-5", + "ADAT-6", + "ADAT-7", + "ADAT-8", + "Analog-L", + "Analog-R", + "SPDIF-L", + "SPDIF-R", +}; + +u16 ClockSourceSelector[] = {0x8000, // selects source as ADAT at 44.1 kHz + 0x0000, // selects source as ADAT at 48 kHz + 0x8001, // selects source as S/PDIF at 44.1 kHz + 0x0001, // selects source as S/PDIF at 48 kHz + 0x8002, // selects source as local clock at 44.1 kHz + 0x0002 // selects source as local clock at 48 kHz + }; + +snd_korg1212rc rc; + + +MODULE_DEVICE_TABLE(pci, snd_korg1212_ids); + +typedef union swap_u32 { unsigned char c[4]; u32 i; } swap_u32; + +#ifdef SNDRV_BIG_ENDIAN +static u32 LowerWordSwap(u32 swappee) +#else +static u32 UpperWordSwap(u32 swappee) +#endif +{ + swap_u32 retVal, swapper; + + swapper.i = swappee; + retVal.c[2] = swapper.c[3]; + retVal.c[3] = swapper.c[2]; + retVal.c[1] = swapper.c[1]; + retVal.c[0] = swapper.c[0]; + + return retVal.i; +} + +#ifdef SNDRV_BIG_ENDIAN +static u32 UpperWordSwap(u32 swappee) +#else +static u32 LowerWordSwap(u32 swappee) +#endif +{ + swap_u32 retVal, swapper; + + swapper.i = swappee; + retVal.c[2] = swapper.c[2]; + retVal.c[3] = swapper.c[3]; + retVal.c[1] = swapper.c[0]; + retVal.c[0] = swapper.c[1]; + + return retVal.i; +} + +#if 0 /* not used */ + +static u32 EndianSwap(u32 swappee) +{ + swap_u32 retVal, swapper; + + swapper.i = swappee; + retVal.c[0] = swapper.c[3]; + retVal.c[1] = swapper.c[2]; + retVal.c[2] = swapper.c[1]; + retVal.c[3] = swapper.c[0]; + + return retVal.i; +} + +#endif /* not used */ + +void TickDelay(int time) +{ + udelay(time); +} + +#define SetBitInWord(theWord,bitPosition) (*theWord) |= (0x0001 << bitPosition) +#define SetBitInDWord(theWord,bitPosition) (*theWord) |= (0x00000001 << bitPosition) +#define ClearBitInWord(theWord,bitPosition) (*theWord) &= ~(0x0001 << bitPosition) +#define ClearBitInDWord(theWord,bitPosition) (*theWord) &= ~(0x00000001 << bitPosition) + +static snd_korg1212rc snd_korg1212_Send1212Command(korg1212_t *korg1212, korg1212_dbcnst_t doorbellVal, + u32 mailBox0Val, u32 mailBox1Val, u32 mailBox2Val, u32 mailBox3Val) +{ + u32 retryCount; + u16 mailBox3Lo; + + if (korg1212->outDoorbellPtr) { +#ifdef DEBUG + PRINTK("DEBUG: Card <- 0x%08x 0x%08x [%s]\n", doorbellVal, mailBox0Val, stateName[korg1212->cardState]); +#endif + for (retryCount = 0; retryCount < MAX_COMMAND_RETRIES; retryCount++) { + + writel(mailBox3Val, korg1212->mailbox3Ptr); + writel(mailBox2Val, korg1212->mailbox2Ptr); + writel(mailBox1Val, korg1212->mailbox1Ptr); + writel(mailBox0Val, korg1212->mailbox0Ptr); + writel(doorbellVal, korg1212->outDoorbellPtr); // interrupt the card + + // -------------------------------------------------------------- + // the reboot command will not give an acknowledgement. + // -------------------------------------------------------------- + switch (doorbellVal) { + case K1212_DB_RebootCard: + case K1212_DB_BootFromDSPPage4: + case K1212_DB_StartDSPDownload: + return K1212_CMDRET_Success; + default: + break; + } + + // -------------------------------------------------------------- + // See if the card acknowledged the command. Wait a bit, then + // read in the low word of mailbox3. If the MSB is set and the + // low byte is equal to the doorbell value, then it ack'd. + // -------------------------------------------------------------- + TickDelay(COMMAND_ACK_DELAY); + mailBox3Lo = readl(korg1212->mailbox3Ptr); + if (mailBox3Lo & COMMAND_ACK_MASK) { + if ((mailBox3Lo & DOORBELL_VAL_MASK) == (doorbellVal & DOORBELL_VAL_MASK)) { + korg1212->cmdRetryCount += retryCount; + return K1212_CMDRET_Success; + } + } + } + korg1212->cmdRetryCount += retryCount; + return K1212_CMDRET_NoAckFromCard; + } else { + return K1212_CMDRET_CardUninitialized; + } +} + +static void snd_korg1212_WaitForCardStopAck(korg1212_t *korg1212) +{ + unsigned long endtime = jiffies + 20 * HZ; + +#ifdef DEBUG + PRINTK("DEBUG: WaitForCardStopAck [%s]\n", stateName[korg1212->cardState]); +#endif // DEBUG + + if (korg1212->inIRQ) + return; + + do { + if (readl(&korg1212->sharedBufferPtr->cardCommand) == 0) + return; + if (!korg1212->inIRQ) + schedule(); + } while (jiffies < endtime); + + writel(0, &korg1212->sharedBufferPtr->cardCommand); +} + +static void snd_korg1212_TurnOnIdleMonitor(korg1212_t *korg1212) +{ + TickDelay(INTERCOMMAND_DELAY); + korg1212->idleMonitorOn = 1; + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_MonitorOn, 0, 0, 0); +} + +static void snd_korg1212_TurnOffIdleMonitor(korg1212_t *korg1212) +{ + if (korg1212->idleMonitorOn) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + korg1212->idleMonitorOn = 0; + } +} + +static void snd_korg1212_setCardState(korg1212_t * korg1212, CardState csState) +{ + switch (csState) { + case K1212_STATE_READY: + snd_korg1212_TurnOnIdleMonitor(korg1212); + break; + + case K1212_STATE_OPEN: + snd_korg1212_TurnOffIdleMonitor(korg1212); + break; + + default: + break; + } + + korg1212->cardState = csState; +} + +static int snd_korg1212_OpenCard(korg1212_t * korg1212) +{ +#ifdef DEBUG + PRINTK("DEBUG: OpenCard [%s]\n", stateName[korg1212->cardState]); +#endif + snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN); + return 1; +} + +static int snd_korg1212_CloseCard(korg1212_t * korg1212) +{ +#ifdef DEBUG + PRINTK("DEBUG: CloseCard [%s]\n", stateName[korg1212->cardState]); +#endif + + if (korg1212->cardState == K1212_STATE_SETUP) { + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_StopPlay, 0, 0, 0); +#ifdef DEBUG + if (rc) PRINTK("DEBUG: CloseCard - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + if (rc != K1212_CMDRET_Success) + return 0; + } else if (korg1212->cardState > K1212_STATE_SETUP) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + } + + if (korg1212->cardState > K1212_STATE_READY) + snd_korg1212_setCardState(korg1212, K1212_STATE_READY); + + return 0; +} + +static int snd_korg1212_SetupForPlay(korg1212_t * korg1212) +{ +#ifdef DEBUG + PRINTK("DEBUG: SetupForPlay [%s]\n", stateName[korg1212->cardState]); +#endif + + snd_korg1212_setCardState(korg1212, K1212_STATE_SETUP); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_SetupPlay, 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: SetupForPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + if (rc != K1212_CMDRET_Success) { + return 0; + } + return 1; +} + +static int snd_korg1212_TriggerPlay(korg1212_t * korg1212) +{ +#ifdef DEBUG + PRINTK("DEBUG: TriggerPlay [%s]\n", stateName[korg1212->cardState]); +#endif + + snd_korg1212_setCardState(korg1212, K1212_STATE_PLAYING); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_TriggerPlay, 0, 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: TriggerPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + if (rc != K1212_CMDRET_Success) { + return 0; + } + return 1; +} + +static int snd_korg1212_StopPlay(korg1212_t * korg1212) +{ +#ifdef DEBUG + PRINTK("DEBUG: StopPlay [%s]\n", stateName[korg1212->cardState]); +#endif + + if (korg1212->cardState != K1212_STATE_ERRORSTOP) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + } + snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN); + return 1; +} + +static void snd_korg1212_EnableCardInterrupts(korg1212_t * korg1212) +{ + * korg1212->statusRegPtr = PCI_INT_ENABLE_BIT | + PCI_DOORBELL_INT_ENABLE_BIT | + LOCAL_INT_ENABLE_BIT | + LOCAL_DOORBELL_INT_ENABLE_BIT | + LOCAL_DMA1_INT_ENABLE_BIT; +} + +#if 0 /* not used */ + +static int snd_korg1212_SetMonitorMode(korg1212_t *korg1212, MonitorModeSelector mode) +{ +#ifdef DEBUG + PRINTK("DEBUG: SetMonitorMode [%s]\n", stateName[korg1212->cardState]); +#endif + + switch (mode) { + case K1212_MONMODE_Off: + if (korg1212->cardState != K1212_STATE_MONITOR) { + return 0; + } else { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN); + } + break; + + case K1212_MONMODE_On: + if (korg1212->cardState != K1212_STATE_OPEN) { + return 0; + } else { + snd_korg1212_setCardState(korg1212, K1212_STATE_MONITOR); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_MonitorOn, 0, 0, 0); + if (rc != K1212_CMDRET_Success) { + return 0; + } + } + break; + + default: + return 0; + } + + return 1; +} + +#endif /* not used */ + +static int snd_korg1212_SetRate(korg1212_t *korg1212, int rate) +{ + static ClockSourceIndex s44[] = { K1212_CLKIDX_AdatAt44_1K, + K1212_CLKIDX_WordAt44_1K, + K1212_CLKIDX_LocalAt44_1K }; + static ClockSourceIndex s48[] = { + K1212_CLKIDX_AdatAt48K, + K1212_CLKIDX_WordAt48K, + K1212_CLKIDX_LocalAt48K }; + int parm; + + switch(rate) { + case 44100: + parm = s44[korg1212->clkSource]; + break; + + case 48000: + parm = s48[korg1212->clkSource]; + break; + + default: + return -EINVAL; + } + + korg1212->clkSrcRate = parm; + korg1212->clkRate = rate; + + TickDelay(INTERCOMMAND_DELAY); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate, + ClockSourceSelector[korg1212->clkSrcRate], + 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + return 0; +} + +static int snd_korg1212_SetClockSource(korg1212_t *korg1212, int source) +{ + + if (source<0 || source >2) + return -EINVAL; + + korg1212->clkSource = source; + + snd_korg1212_SetRate(korg1212, korg1212->clkRate); + + return 0; +} + +static void snd_korg1212_DisableCardInterrupts(korg1212_t *korg1212) +{ + * korg1212->statusRegPtr = 0; +} + +static int snd_korg1212_WriteADCSensitivity(korg1212_t *korg1212) +{ + SensBits sensVals; + int bitPosition; + int channel; + int clkIs48K; + int monModeSet; + u16 controlValue; // this keeps the current value to be written to + // the card's eeprom control register. + u16 count; + +#ifdef DEBUG + PRINTK("DEBUG: WriteADCSensivity [%s]\n", stateName[korg1212->cardState]); +#endif + + // ---------------------------------------------------------------------------- + // initialize things. The local init bit is always set when writing to the + // card's control register. + // ---------------------------------------------------------------------------- + controlValue = 0; + SetBitInWord(&controlValue, SET_SENS_LOCALINIT_BITPOS); // init the control value + + // ---------------------------------------------------------------------------- + // make sure the card is not in monitor mode when we do this update. + // ---------------------------------------------------------------------------- + if (korg1212->cardState == K1212_STATE_MONITOR || korg1212->idleMonitorOn) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + monModeSet = 1; + snd_korg1212_WaitForCardStopAck(korg1212); + } else + monModeSet = 0; + + // ---------------------------------------------------------------------------- + // we are about to send new values to the card, so clear the new values queued + // flag. Also, clear out mailbox 3, so we don't lockup. + // ---------------------------------------------------------------------------- + writel(0, korg1212->mailbox3Ptr); + TickDelay(LOADSHIFT_DELAY); + + // ---------------------------------------------------------------------------- + // determine whether we are running a 48K or 44.1K clock. This info is used + // later when setting the SPDIF FF after the volume has been shifted in. + // ---------------------------------------------------------------------------- + switch (korg1212->clkSrcRate) { + case K1212_CLKIDX_AdatAt44_1K: + case K1212_CLKIDX_WordAt44_1K: + case K1212_CLKIDX_LocalAt44_1K: + clkIs48K = 0; + break; + + case K1212_CLKIDX_WordAt48K: + case K1212_CLKIDX_AdatAt48K: + case K1212_CLKIDX_LocalAt48K: + default: + clkIs48K = 1; + break; + } + + // ---------------------------------------------------------------------------- + // start the update. Setup the bit structure and then shift the bits. + // ---------------------------------------------------------------------------- + sensVals.l.v.leftChanId = SET_SENS_LEFTCHANID; + sensVals.r.v.rightChanId = SET_SENS_RIGHTCHANID; + sensVals.l.v.leftChanVal = korg1212->leftADCInSens; + sensVals.r.v.rightChanVal = korg1212->rightADCInSens; + + // ---------------------------------------------------------------------------- + // now start shifting the bits in. Start with the left channel then the right. + // ---------------------------------------------------------------------------- + for (channel = 0; channel < 2; channel++) { + + // ---------------------------------------------------------------------------- + // Bring the load/shift line low, then wait - the spec says >150ns from load/ + // shift low to the first rising edge of the clock. + // ---------------------------------------------------------------------------- + ClearBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS); + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // load/shift goes low + TickDelay(LOADSHIFT_DELAY); + + for (bitPosition = 15; bitPosition >= 0; bitPosition--) { // for all the bits + if (channel == 0) { + if (sensVals.l.leftSensBits & (0x0001 << bitPosition)) { + SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set high + } else { + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set low + } + } else { + if (sensVals.r.rightSensBits & (0x0001 << bitPosition)) { + SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set high + } else { + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set low + } + } + + ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes low + TickDelay(SENSCLKPULSE_WIDTH); + SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes high + TickDelay(SENSCLKPULSE_WIDTH); + } + + // ---------------------------------------------------------------------------- + // finish up SPDIF for left. Bring the load/shift line high, then write a one + // bit if the clock rate is 48K otherwise write 0. + // ---------------------------------------------------------------------------- + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); + ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + SetBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // load shift goes high - clk low + TickDelay(SENSCLKPULSE_WIDTH); + + if (clkIs48K) + SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); + + writew(controlValue, korg1212->sensRegPtr); // set/clear data bit + TickDelay(ONE_RTC_TICK); + SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes high + TickDelay(SENSCLKPULSE_WIDTH); + ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes low + TickDelay(SENSCLKPULSE_WIDTH); + } + + // ---------------------------------------------------------------------------- + // The update is complete. Set a timeout. This is the inter-update delay. + // Also, if the card was in monitor mode, restore it. + // ---------------------------------------------------------------------------- + for (count = 0; count < 10; count++) + TickDelay(SENSCLKPULSE_WIDTH); + + if (monModeSet) { + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_MonitorOn, 0, 0, 0); +#ifdef DEBUG + if (rc) PRINTK("DEBUG: WriteADCSensivity - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + } + + return 1; +} + +static void snd_korg1212_OnDSPDownloadComplete(korg1212_t *korg1212) +{ + int channel; + +#ifdef DEBUG + PRINTK("DEBUG: DSP download is complete. [%s]\n", stateName[korg1212->cardState]); +#endif + + // ---------------------------------------------------- + // tell the card to boot + // ---------------------------------------------------- + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_BootFromDSPPage4, 0, 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Boot from Page 4 - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + mdelay(DSP_BOOT_DELAY_IN_MS); + + // -------------------------------------------------------------------------------- + // Let the card know where all the buffers are. + // -------------------------------------------------------------------------------- + rc = snd_korg1212_Send1212Command(korg1212, + K1212_DB_ConfigureBufferMemory, + LowerWordSwap(korg1212->PlayDataPhy), + LowerWordSwap(korg1212->RecDataPhy), + ((kNumBuffers * kPlayBufferFrames) / 2), // size given to the card + // is based on 2 buffers + 0 + ); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Configure Buffer Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + TickDelay(INTERCOMMAND_DELAY); + + rc = snd_korg1212_Send1212Command(korg1212, + K1212_DB_ConfigureMiscMemory, + LowerWordSwap(korg1212->VolumeTablePhy), + LowerWordSwap(korg1212->RoutingTablePhy), + LowerWordSwap(korg1212->AdatTimeCodePhy), + 0 + ); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Configure Misc Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + + // -------------------------------------------------------------------------------- + // Initialize the routing and volume tables, then update the card's state. + // -------------------------------------------------------------------------------- + TickDelay(INTERCOMMAND_DELAY); + + for (channel = 0; channel < kAudioChannels; channel++) { + korg1212->sharedBufferPtr->volumeData[channel] = k1212MaxVolume; + //korg1212->sharedBufferPtr->routeData[channel] = channel; + korg1212->sharedBufferPtr->routeData[channel] = 8 + (channel & 1); + } + + snd_korg1212_WriteADCSensitivity(korg1212); + + TickDelay(INTERCOMMAND_DELAY); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate, + ClockSourceSelector[korg1212->clkSrcRate], + 0, 0, 0); +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + snd_korg1212_setCardState(korg1212, K1212_STATE_READY); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Set Monitor On - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + wake_up_interruptible(&korg1212->wait); +} + + +static void snd_korg1212_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 doorbellValue; + korg1212_t *korg1212 = snd_magic_cast(korg1212_t, dev_id, return); + + if(irq != korg1212->irq) + return; + + doorbellValue = readl(korg1212->inDoorbellPtr); + + if (!doorbellValue) + return; + + writel(doorbellValue, korg1212->inDoorbellPtr); + + korg1212->irqcount++; + + korg1212->inIRQ++; + + + switch (doorbellValue) { + case K1212_DB_DSPDownloadDone: +#ifdef DEBUG + PRINTK("DEBUG: IRQ DNLD count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]); +#endif + if (korg1212->cardState == K1212_STATE_DSP_IN_PROCESS) { + snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_COMPLETE); + snd_korg1212_OnDSPDownloadComplete(korg1212); + } + break; + + // ------------------------------------------------------------------------ + // an error occurred - stop the card + // ------------------------------------------------------------------------ + case K1212_ISRCODE_DMAERROR: +#ifdef DEBUG + PRINTK("DEBUG: IRQ DMAE count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]); +#endif + writel(0, &korg1212->sharedBufferPtr->cardCommand); + break; + + // ------------------------------------------------------------------------ + // the card has stopped by our request. Clear the command word and signal + // the semaphore in case someone is waiting for this. + // ------------------------------------------------------------------------ + case K1212_ISRCODE_CARDSTOPPED: +#ifdef DEBUG + PRINTK("DEBUG: IRQ CSTP count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]); +#endif + writel(0, &korg1212->sharedBufferPtr->cardCommand); + break; + + default: +#ifdef XDEBUG + PRINTK("DEBUG: IRQ DFLT count - %ld, %x, cpos=%d [%s].\n", korg1212->irqcount, doorbellValue, + korg1212->currentBuffer, stateName[korg1212->cardState]); +#endif + if ((korg1212->cardState > K1212_STATE_SETUP) || korg1212->idleMonitorOn) { + korg1212->currentBuffer++; + + if (korg1212->currentBuffer >= kNumBuffers) + korg1212->currentBuffer = 0; + + if (!korg1212->running) + break; + + if (korg1212->capture_substream) { + snd_pcm_period_elapsed(korg1212->capture_substream); + } + + if (korg1212->playback_substream) { + snd_pcm_period_elapsed(korg1212->playback_substream); + } + } + break; + } + + korg1212->inIRQ--; +} + +static int snd_korg1212_downloadDSPCode(korg1212_t *korg1212) +{ + +#ifdef DEBUG + PRINTK("DEBUG: DSP download is starting... [%s]\n", stateName[korg1212->cardState]); +#endif + + // --------------------------------------------------------------- + // verify the state of the card before proceeding. + // --------------------------------------------------------------- + if (korg1212->cardState >= K1212_STATE_DSP_IN_PROCESS) { + return 1; + } + + snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS); + + memcpy(korg1212->dspMemPtr, dspCode, korg1212->dspCodeSize); + + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload, + UpperWordSwap(korg1212->dspMemPhy), + 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Start DSP Download RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + interruptible_sleep_on_timeout(&korg1212->wait, HZ * 4); + + return 0; +} + +static snd_pcm_hardware_t snd_korg1212_playback_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + rate_min: 44100, + rate_max: 48000, + channels_min: K1212_CHANNELS, + channels_max: K1212_CHANNELS, + buffer_bytes_max: K1212_BUF_SIZE, + period_bytes_min: K1212_PERIOD_BYTES, + period_bytes_max: K1212_PERIOD_BYTES, + periods_min: K1212_PERIODS, + periods_max: K1212_PERIODS, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_korg1212_capture_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + rate_min: 44100, + rate_max: 48000, + channels_min: K1212_CHANNELS, + channels_max: K1212_CHANNELS, + buffer_bytes_max: K1212_BUF_SIZE, + period_bytes_min: K1212_PERIOD_BYTES, + period_bytes_max: K1212_PERIOD_BYTES, + periods_min: K1212_PERIODS, + periods_max: K1212_PERIODS, + fifo_size: 0, +}; + +static void snd_korg1212_free_pcm(snd_pcm_t *pcm) +{ + korg1212_t *korg1212 = (korg1212_t *) pcm->private_data; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_free_pcm [%s]\n", stateName[korg1212->cardState]); +#endif + + korg1212->pcm16 = NULL; +} + +static unsigned int period_bytes[] = { K1212_PERIOD_BYTES }; + +#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = { + count: PERIOD_BYTES, + list: period_bytes, + mask: 0 +}; + +static int snd_korg1212_playback_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_playback_open [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + snd_korg1212_OpenCard(korg1212); + + snd_pcm_set_sync(substream); // ??? + + runtime->hw = snd_korg1212_playback_info; + runtime->dma_area = (char *) korg1212->playDataBufsPtr; + runtime->dma_bytes = K1212_BUF_SIZE; + + korg1212->playback_substream = substream; + korg1212->periodsize = K1212_PERIODS; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, K1212_BUF_SIZE, K1212_BUF_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + return 0; +} + +static int snd_korg1212_capture_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_capture_open [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + snd_korg1212_OpenCard(korg1212); + + snd_pcm_set_sync(substream); // ??? + + runtime->hw = snd_korg1212_capture_info; + runtime->dma_area = (char *) korg1212->recordDataBufsPtr; + runtime->dma_bytes = K1212_BUF_SIZE; + + korg1212->capture_substream = substream; + korg1212->periodsize = K1212_PERIODS; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, K1212_BUF_SIZE, K1212_BUF_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + return 0; +} + +static int snd_korg1212_playback_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_playback_close [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + korg1212->playback_substream = NULL; + korg1212->periodsize = 0; + + snd_korg1212_CloseCard(korg1212); + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_capture_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_capture_close [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + korg1212->capture_substream = NULL; + korg1212->periodsize = 0; + + snd_korg1212_CloseCard(korg1212); + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_channel_info(snd_pcm_substream_t *substream, + snd_pcm_channel_info_t *info) +{ + int chn = info->channel; + + // snd_assert(info->channel < kAudioChannels + 1, return -EINVAL); + + info->offset = 0; + // if (chn < k16BitChannels) { + info->first = chn * 16; + // } else { + // info->first = k16BitChannels * 16 + (chn - k16BitChannels - 1) * 32; + // } + info->step = sizeof(KorgAudioFrame) * 8; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_channel_info %d:, offset=%ld, first=%d, step=%d\n", chn, info->offset, info->first, info->step); +#endif + + return 0; +} + +static int snd_korg1212_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_ioctl: cmd=%d\n", cmd); +#endif + if (cmd == SNDRV_PCM_IOCTL1_CHANNEL_INFO ) { + snd_pcm_channel_info_t *info = arg; + return snd_korg1212_channel_info(substream, info); + } + + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_korg1212_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + int err; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_hw_params [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + if ((err = snd_korg1212_SetRate(korg1212, params_rate(params))) < 0) { + spin_unlock_irqrestore(&korg1212->lock, flags); + return err; + } + + if (params_format(params) != SNDRV_PCM_FORMAT_S16_LE) { + spin_unlock_irqrestore(&korg1212->lock, flags); + return -EINVAL; + } + + korg1212->periodsize = K1212_BLOCK_SIZE; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_prepare(snd_pcm_substream_t *substream) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + unsigned long flags; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_prepare [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + snd_korg1212_SetupForPlay(korg1212); + + korg1212->currentBuffer = -1; + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_trigger [%s] cmd=%d\n", stateName[korg1212->cardState], cmd); +#endif + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + korg1212->running = 1; + snd_korg1212_TriggerPlay(korg1212); + break; + + case SNDRV_PCM_TRIGGER_STOP: + korg1212->running = 0; + snd_korg1212_StopPlay(korg1212); + break; + + default: + return -EINVAL; + } + return 0; +} + +static snd_pcm_uframes_t snd_korg1212_pointer(snd_pcm_substream_t *substream) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + snd_pcm_uframes_t pos; + + if (korg1212->currentBuffer < 0) + return 0; + + pos = korg1212->currentBuffer * kPlayBufferFrames; + +#ifdef XDEBUG + PRINTK("DEBUG: snd_korg1212_pointer [%s] %ld\n", stateName[korg1212->cardState], pos); +#endif + + return pos; +} + +static int snd_korg1212_playback_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count); +#endif + + snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); + + copy_from_user(dst, src, count * K1212_FRAME_SIZE); + + return 0; +} + +static int snd_korg1212_capture_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + KorgAudioFrame * src = korg1212->recordDataBufsPtr[0].bufferData + pos; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count); +#endif + + snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); + + copy_to_user(dst, src, count * K1212_FRAME_SIZE); + + return 0; +} + +static int snd_korg1212_playback_silence(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_playback_silence [%s]\n", stateName[korg1212->cardState]); +#endif + + snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); + + memset(dst, 0, count * K1212_FRAME_SIZE); + + return 0; +} + +static snd_pcm_ops_t snd_korg1212_playback_ops = { + open: snd_korg1212_playback_open, + close: snd_korg1212_playback_close, + ioctl: snd_korg1212_ioctl, + hw_params: snd_korg1212_hw_params, + prepare: snd_korg1212_prepare, + trigger: snd_korg1212_trigger, + pointer: snd_korg1212_pointer, + copy: snd_korg1212_playback_copy, + silence: snd_korg1212_playback_silence, +}; + +static snd_pcm_ops_t snd_korg1212_capture_ops = { + open: snd_korg1212_capture_open, + close: snd_korg1212_capture_close, + ioctl: snd_korg1212_ioctl, + hw_params: snd_korg1212_hw_params, + prepare: snd_korg1212_prepare, + trigger: snd_korg1212_trigger, + pointer: snd_korg1212_pointer, + copy: snd_korg1212_capture_copy, +}; + +/* + * Control Interface + */ + +static int snd_korg1212_control_phase_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; + return 0; +} + +static int snd_korg1212_control_phase_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i = kcontrol->private_value; + + spin_lock_irqsave(&korg1212->lock, flags); + + u->value.integer.value[0] = korg1212->volumePhase[i]; + + if (i >= 8) + u->value.integer.value[1] = korg1212->volumePhase[i+1]; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_phase_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + int i, val; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + + korg1212->volumePhase[i] = u->value.integer.value[0]; + + val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value]; + + if ((u->value.integer.value[0] > 0) != (val < 0)) { + val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1); + korg1212->sharedBufferPtr->volumeData[i] = val; + change = 1; + } + + if (i >= 8) { + korg1212->volumePhase[i+1] = u->value.integer.value[1]; + + val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1]; + + if ((u->value.integer.value[1] > 0) != (val < 0)) { + val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1); + korg1212->sharedBufferPtr->volumeData[i+1] = val; + change = 1; + } + } + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; + uinfo->value.integer.min = k1212MinVolume; + uinfo->value.integer.max = k1212MaxVolume; + return 0; +} + +static int snd_korg1212_control_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + u->value.integer.value[0] = abs(korg1212->sharedBufferPtr->volumeData[i]); + + if (i >= 8) + u->value.integer.value[1] = abs(korg1212->sharedBufferPtr->volumeData[i+1]); + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + int i; + int val; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + + if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) { + val = korg1212->volumePhase[i] > 0 ? -1 : 1; + val *= u->value.integer.value[0]; + korg1212->sharedBufferPtr->volumeData[i] = val; + change = 1; + } + + if (i >= 8) { + if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) { + val = korg1212->volumePhase[i+1] > 0 ? -1 : 1; + val *= u->value.integer.value[1]; + korg1212->sharedBufferPtr->volumeData[i+1] = val; + change = 1; + } + } + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; + uinfo->value.enumerated.items = kAudioChannels; + if (uinfo->value.enumerated.item > kAudioChannels-1) { + uinfo->value.enumerated.item = kAudioChannels-1; + } + strcpy(uinfo->value.enumerated.name, channelName[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_korg1212_control_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + u->value.enumerated.item[0] = korg1212->sharedBufferPtr->routeData[i]; + + if (i >= 8) + u->value.enumerated.item[1] = korg1212->sharedBufferPtr->routeData[i+1]; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0, i; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + + if (u->value.enumerated.item[0] != korg1212->sharedBufferPtr->volumeData[i]) { + korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0]; + change = 1; + } + + if (i >= 8) { + if (u->value.enumerated.item[1] != korg1212->sharedBufferPtr->volumeData[i+1]) { + korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1]; + change = 1; + } + } + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_analog_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = k1212MaxADCSens; + uinfo->value.integer.max = k1212MinADCSens; + return 0; +} + +static int snd_korg1212_control_analog_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&korg1212->lock, flags); + + u->value.integer.value[0] = korg1212->leftADCInSens; + u->value.integer.value[1] = korg1212->rightADCInSens; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_analog_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + + spin_lock_irqsave(&korg1212->lock, flags); + + if (u->value.integer.value[0] != korg1212->leftADCInSens) { + korg1212->leftADCInSens = u->value.integer.value[0]; + change = 1; + } + if (u->value.integer.value[1] != korg1212->rightADCInSens) { + korg1212->rightADCInSens = u->value.integer.value[1]; + change = 1; + } + + if (change) + snd_korg1212_WriteADCSensitivity(korg1212); + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_sync_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) { + uinfo->value.enumerated.item = 2; + } + strcpy(uinfo->value.enumerated.name, clockSourceTypeName[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_korg1212_control_sync_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&korg1212->lock, flags); + + ucontrol->value.enumerated.item[0] = korg1212->clkSource; + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_control_sync_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&korg1212->lock, flags); + change = val != korg1212->clkSource; + snd_korg1212_SetClockSource(korg1212, val); + spin_unlock_irqrestore(&korg1212->lock, flags); + return change; +} + +#define MON_MIXER(ord,c_name) \ + { \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, \ + name: c_name " Monitor Volume", \ + info: snd_korg1212_control_volume_info, \ + get: snd_korg1212_control_volume_get, \ + put: snd_korg1212_control_volume_put, \ + private_value: ord, \ + }, \ + { \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, \ + name: c_name " Monitor Route", \ + info: snd_korg1212_control_route_info, \ + get: snd_korg1212_control_route_get, \ + put: snd_korg1212_control_route_put, \ + private_value: ord, \ + }, \ + { \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \ + iface: SNDRV_CTL_ELEM_IFACE_PCM, \ + name: c_name " Monitor Phase Invert", \ + info: snd_korg1212_control_phase_info, \ + get: snd_korg1212_control_phase_get, \ + put: snd_korg1212_control_phase_put, \ + private_value: ord, \ + } + +static snd_kcontrol_new_t snd_korg1212_controls[] = { + MON_MIXER(8, "Analog"), + MON_MIXER(10, "SPDIF"), + MON_MIXER(0, "ADAT-1"), MON_MIXER(1, "ADAT-2"), MON_MIXER(2, "ADAT-3"), MON_MIXER(3, "ADAT-4"), + MON_MIXER(4, "ADAT-5"), MON_MIXER(5, "ADAT-6"), MON_MIXER(6, "ADAT-7"), MON_MIXER(7, "ADAT-8"), + { + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Sync Source", + info: snd_korg1212_control_sync_info, + get: snd_korg1212_control_sync_get, + put: snd_korg1212_control_sync_put, + }, + { + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "ADC Attenuation", + info: snd_korg1212_control_analog_info, + get: snd_korg1212_control_analog_get, + put: snd_korg1212_control_analog_put, + } +}; + +#define K1212_CONTROL_ELEMENTS (sizeof(snd_korg1212_controls) / sizeof(snd_korg1212_controls[0])) + +/* + * proc interface + */ + +static void snd_korg1212_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + int n; + korg1212_t *korg1212 = (korg1212_t *)entry->private_data; + + snd_iprintf(buffer, korg1212->card->longname); + snd_iprintf(buffer, " (index #%d)\n", korg1212->card->number + 1); + snd_iprintf(buffer, "\nGeneral settings\n"); + snd_iprintf(buffer, " period size: %d bytes\n", K1212_BLOCK_SIZE); + snd_iprintf(buffer, " clock mode: %s\n", clockSourceName[korg1212->clkSrcRate] ); + snd_iprintf(buffer, " left ADC Sens: %d\n", korg1212->leftADCInSens ); + snd_iprintf(buffer, " right ADC Sens: %d\n", korg1212->rightADCInSens ); + snd_iprintf(buffer, " Volume Info:\n"); + for (n=0; n %s [%d]\n", n, + channelName[n], + channelName[korg1212->sharedBufferPtr->routeData[n]], + korg1212->sharedBufferPtr->volumeData[n]); + snd_iprintf(buffer, "\nGeneral status\n"); + snd_iprintf(buffer, " ADAT Time Code: %d\n", korg1212->sharedBufferPtr->AdatTimeCode); + snd_iprintf(buffer, " Card State: %s\n", stateName[korg1212->cardState]); + snd_iprintf(buffer, "Idle mon. State: %d\n", korg1212->idleMonitorOn); + snd_iprintf(buffer, "Cmd retry count: %d\n", korg1212->cmdRetryCount); + snd_iprintf(buffer, " Irq count: %ld\n", korg1212->irqcount); +} + +static void __init snd_korg1212_proc_init(korg1212_t *korg1212) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(korg1212->card, "korg1212", korg1212->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = korg1212; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_korg1212_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + korg1212->proc_entry = entry; +} + +static void snd_korg1212_proc_done(korg1212_t * korg1212) +{ + if (korg1212->proc_entry) { + snd_info_unregister(korg1212->proc_entry); + korg1212->proc_entry = NULL; + } +} + +static int __init snd_korg1212_create(korg1212_t *korg1212) +{ + struct pci_dev *pci = korg1212->pci; + int err; + int i; + unsigned ioport_size, iomem_size, iomem2_size; + dma_addr_t phys_addr; + + korg1212->irq = -1; + korg1212->clkSource = K1212_CLKIDX_Local; + korg1212->clkRate = 44100; + korg1212->inIRQ = 0; + korg1212->running = 0; + snd_korg1212_setCardState(korg1212, K1212_STATE_UNINITIALIZED); + korg1212->idleMonitorOn = 0; + korg1212->clkSrcRate = K1212_CLKIDX_LocalAt44_1K; + korg1212->leftADCInSens = k1212MaxADCSens; + korg1212->rightADCInSens = k1212MaxADCSens; + + for (i=0; ivolumePhase[i] = 0; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + korg1212->iomem = pci_resource_start(korg1212->pci, 0); + korg1212->ioport = pci_resource_start(korg1212->pci, 1); + korg1212->iomem2 = pci_resource_start(korg1212->pci, 2); + + iomem_size = pci_resource_len(korg1212->pci, 0); + ioport_size = pci_resource_len(korg1212->pci, 1); + iomem2_size = pci_resource_len(korg1212->pci, 2); + +#ifdef DEBUG + PRINTK("DEBUG: resources:\n" + " iomem = 0x%lx (%d)\n" + " ioport = 0x%lx (%d)\n" + " iomem = 0x%lx (%d)\n" + " [%s]\n", + korg1212->iomem, iomem_size, + korg1212->ioport, ioport_size, + korg1212->iomem2, iomem2_size, + stateName[korg1212->cardState]); +#endif + + korg1212->res_iomem = request_mem_region(korg1212->iomem, iomem_size, "korg1212"); + if (korg1212->res_iomem == NULL) { + PRINTK("unable to grab region 0x%lx-0x%lx\n", + korg1212->iomem, korg1212->iomem + iomem_size - 1); + return -EBUSY; + } + + korg1212->res_ioport = request_region(korg1212->ioport, ioport_size, "korg1212"); + if (korg1212->res_ioport == NULL) { + PRINTK("unable to grab region 0x%lx-0x%lx\n", + korg1212->ioport, korg1212->ioport + ioport_size - 1); + return -EBUSY; + } + + korg1212->res_iomem2 = request_mem_region(korg1212->iomem2, iomem2_size, "korg1212"); + if (korg1212->res_iomem2 == NULL) { + PRINTK("unable to grab region 0x%lx-0x%lx\n", + korg1212->iomem2, korg1212->iomem2 + iomem2_size - 1); + return -EBUSY; + } + + if ((korg1212->iobase = (unsigned long) ioremap(korg1212->iomem, iomem_size)) == 0) { + PRINTK("unable to remap memory region 0x%lx-0x%lx\n", korg1212->iobase, + korg1212->iobase + iomem_size - 1); + return -EBUSY; + } + + err = request_irq(pci->irq, snd_korg1212_interrupt, + SA_INTERRUPT|SA_SHIRQ, + "korg1212", (void *) korg1212); + + if (err) { + PRINTK("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + + korg1212->irq = pci->irq; + + init_waitqueue_head(&korg1212->wait); + spin_lock_init(&korg1212->lock); + pci_set_master(korg1212->pci); + + korg1212->statusRegPtr = (u32 *) (korg1212->iobase + STATUS_REG_OFFSET); + korg1212->outDoorbellPtr = (u32 *) (korg1212->iobase + OUT_DOORBELL_OFFSET); + korg1212->inDoorbellPtr = (u32 *) (korg1212->iobase + IN_DOORBELL_OFFSET); + korg1212->mailbox0Ptr = (u32 *) (korg1212->iobase + MAILBOX0_OFFSET); + korg1212->mailbox1Ptr = (u32 *) (korg1212->iobase + MAILBOX1_OFFSET); + korg1212->mailbox2Ptr = (u32 *) (korg1212->iobase + MAILBOX2_OFFSET); + korg1212->mailbox3Ptr = (u32 *) (korg1212->iobase + MAILBOX3_OFFSET); + korg1212->controlRegPtr = (u32 *) (korg1212->iobase + PCI_CONTROL_OFFSET); + korg1212->sensRegPtr = (u16 *) (korg1212->iobase + SENS_CONTROL_OFFSET); + korg1212->idRegPtr = (u32 *) (korg1212->iobase + DEV_VEND_ID_OFFSET); + +#ifdef DEBUG + PRINTK("DEBUG: card registers:\n" + " Status register = 0x%p\n" + " OutDoorbell = 0x%p\n" + " InDoorbell = 0x%p\n" + " Mailbox0 = 0x%p\n" + " Mailbox1 = 0x%p\n" + " Mailbox2 = 0x%p\n" + " Mailbox3 = 0x%p\n" + " ControlReg = 0x%p\n" + " SensReg = 0x%p\n" + " IDReg = 0x%p\n" + " [%s]\n", + korg1212->statusRegPtr, + korg1212->outDoorbellPtr, + korg1212->inDoorbellPtr, + korg1212->mailbox0Ptr, + korg1212->mailbox1Ptr, + korg1212->mailbox2Ptr, + korg1212->mailbox3Ptr, + korg1212->controlRegPtr, + korg1212->sensRegPtr, + korg1212->idRegPtr, + stateName[korg1212->cardState]); +#endif + + korg1212->sharedBufferPtr = (KorgSharedBuffer *) snd_malloc_pci_pages(korg1212->pci, sizeof(KorgSharedBuffer), &phys_addr); + korg1212->sharedBufferPhy = (unsigned long)phys_addr; + + if (korg1212->sharedBufferPtr == NULL) { + PRINTK("can not allocate shared buffer memory (%d bytes)\n", sizeof(KorgSharedBuffer)); + return -ENOMEM; + } + +#ifdef DEBUG + PRINTK("DEBUG: Shared Buffer Area = 0x%p (0x%08lx), %d bytes\n", korg1212->sharedBufferPtr, korg1212->sharedBufferPhy, sizeof(KorgSharedBuffer)); +#endif + +#ifndef LARGEALLOC + + korg1212->DataBufsSize = sizeof(KorgAudioBuffer) * kNumBuffers; + + korg1212->playDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr); + korg1212->PlayDataPhy = (u32)phys_addr; + + if (korg1212->playDataBufsPtr == NULL) { + PRINTK("can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize); + return -ENOMEM; + } + +#ifdef DEBUG + PRINTK("DEBUG: Play Data Area = 0x%p (0x%08x), %d bytes\n", + korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize); +#endif + + korg1212->recordDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr); + korg1212->RecDataPhy = (u32)phys_addr; + + if (korg1212->recordDataBufsPtr == NULL) { + PRINTK("can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize); + return -ENOMEM; + } + +#ifdef DEBUG + PRINTK("DEBUG: Record Data Area = 0x%p (0x%08x), %d bytes\n", + korg1212->recordDataBufsPtr, korg1212->RecDataPhy, korg1212->DataBufsSize); +#endif + +#else // LARGEALLOC + + korg1212->recordDataBufsPtr = korg1212->sharedBufferPtr->recordDataBufs; + korg1212->playDataBufsPtr = korg1212->sharedBufferPtr->playDataBufs; + korg1212->PlayDataPhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->playDataBufs; + korg1212->RecDataPhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->recordDataBufs; + +#endif // LARGEALLOC + + korg1212->dspCodeSize = sizeof (dspCode); + + korg1212->VolumeTablePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->volumeData; + korg1212->RoutingTablePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->routeData; + korg1212->AdatTimeCodePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->AdatTimeCode; + + korg1212->dspMemPtr = snd_malloc_pci_pages(korg1212->pci, korg1212->dspCodeSize, &phys_addr); + korg1212->dspMemPhy = (u32)phys_addr; + + if (korg1212->dspMemPtr == NULL) { + PRINTK("can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize); + return -ENOMEM; + } + +#ifdef DEBUG + PRINTK("DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n", + korg1212->dspMemPtr, korg1212->dspMemPhy, korg1212->dspCodeSize, + stateName[korg1212->cardState]); +#endif + + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Reboot Card - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + snd_korg1212_EnableCardInterrupts(korg1212); + + mdelay(CARD_BOOT_DELAY_IN_MS); + + if (snd_korg1212_downloadDSPCode(korg1212)) + return -EBUSY; + + PRINTK("dspMemPhy = %08x U[%08x]\n" + "PlayDataPhy = %08x L[%08x]\n" + "RecDataPhy = %08x L[%08x]\n" + "VolumeTablePhy = %08x L[%08x]\n" + "RoutingTablePhy = %08x L[%08x]\n" + "AdatTimeCodePhy = %08x L[%08x]\n", + korg1212->dspMemPhy, UpperWordSwap(korg1212->dspMemPhy), + korg1212->PlayDataPhy, LowerWordSwap(korg1212->PlayDataPhy), + korg1212->RecDataPhy, LowerWordSwap(korg1212->RecDataPhy), + korg1212->VolumeTablePhy, LowerWordSwap(korg1212->VolumeTablePhy), + korg1212->RoutingTablePhy, LowerWordSwap(korg1212->RoutingTablePhy), + korg1212->AdatTimeCodePhy, LowerWordSwap(korg1212->AdatTimeCodePhy)); + + if ((err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm16)) < 0) + return err; + + korg1212->pcm16->private_data = korg1212; + korg1212->pcm16->private_free = snd_korg1212_free_pcm; + strcpy(korg1212->pcm16->name, "korg1212"); + + snd_pcm_set_ops(korg1212->pcm16, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops); + snd_pcm_set_ops(korg1212->pcm16, SNDRV_PCM_STREAM_CAPTURE, &snd_korg1212_capture_ops); + + korg1212->pcm16->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + for (i = 0; i < K1212_CONTROL_ELEMENTS; i++) { + err = snd_ctl_add(korg1212->card, snd_ctl_new1(&snd_korg1212_controls[i], korg1212)); + if (err < 0) + return err; + } + + snd_korg1212_proc_init(korg1212); + + return 0; + +} + +static void +snd_korg1212_free(void *private_data) +{ + korg1212_t *korg1212 = (korg1212_t *)private_data; + + if (korg1212 == NULL) { + return; + } + + snd_korg1212_proc_done(korg1212); + + snd_korg1212_TurnOffIdleMonitor(korg1212); + + snd_korg1212_DisableCardInterrupts(korg1212); + + if (korg1212->irq >= 0) { + free_irq(korg1212->irq, (void *)korg1212); + korg1212->irq = -1; + } + if (korg1212->iobase != 0) { + iounmap((void *)korg1212->iobase); + korg1212->iobase = 0; + } + if (korg1212->res_iomem != NULL) { + release_resource(korg1212->res_iomem); + kfree_nocheck(korg1212->res_iomem); + korg1212->res_iomem = NULL; + } + if (korg1212->res_ioport != NULL) { + release_resource(korg1212->res_ioport); + kfree_nocheck(korg1212->res_ioport); + korg1212->res_ioport = NULL; + } + if (korg1212->res_iomem2 != NULL) { + release_resource(korg1212->res_iomem2); + kfree_nocheck(korg1212->res_iomem2); + korg1212->res_iomem2 = NULL; + } + + // ---------------------------------------------------- + // free up memory resources used for the DSP download. + // ---------------------------------------------------- + if (korg1212->dspMemPtr) { + snd_free_pci_pages(korg1212->pci, korg1212->dspCodeSize, + korg1212->dspMemPtr, (dma_addr_t)korg1212->dspMemPhy); + korg1212->dspMemPhy = 0; + korg1212->dspMemPtr = 0; + korg1212->dspCodeSize = 0; + } + +#ifndef LARGEALLOC + + // ------------------------------------------------------ + // free up memory resources used for the Play/Rec Buffers + // ------------------------------------------------------ + if (korg1212->playDataBufsPtr) { + snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize, + korg1212->playDataBufsPtr, (dma_addr_t)korg1212->PlayDataPhy); + korg1212->PlayDataPhy = 0; + korg1212->playDataBufsPtr = NULL; + } + + if (korg1212->recordDataBufsPtr) { + snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize, + korg1212->recordDataBufsPtr, (dma_addr_t)korg1212->RecDataPhy); + korg1212->RecDataPhy = 0; + korg1212->recordDataBufsPtr = NULL; + } + +#endif + + // ---------------------------------------------------- + // free up memory resources used for the Shared Buffers + // ---------------------------------------------------- + if (korg1212->sharedBufferPtr) { + snd_free_pci_pages(korg1212->pci, (u32) sizeof(KorgSharedBuffer), + korg1212->sharedBufferPtr, (dma_addr_t)korg1212->sharedBufferPhy); + korg1212->sharedBufferPhy = 0; + korg1212->sharedBufferPtr = NULL; + } +} + +/* + * Card initialisation + */ + +static void snd_korg1212_card_free(snd_card_t *card) +{ +#ifdef DEBUG + PRINTK("DEBUG: Freeing card\n"); +#endif + snd_korg1212_free(card->private_data); +} + +static int __devinit +snd_korg1212_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + korg1212_t *korg1212; + snd_card_t *card; + int err; + + if (dev >= SNDRV_CARDS) { + return -ENODEV; + } + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(korg1212_t))) == NULL) + return -ENOMEM; + + card->private_free = snd_korg1212_card_free; + korg1212 = (korg1212_t *)card->private_data; + korg1212->card = card; + korg1212->pci = pci; + + if ((err = snd_korg1212_create(korg1212)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "korg1212"); + strcpy(card->shortname, "korg1212"); + sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, + korg1212->iomem, korg1212->irq); + +#ifdef DEBUG + PRINTK("DEBUG: %s\n", card->longname); +#endif + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_korg1212_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "korg1212", + id_table: snd_korg1212_ids, + probe: snd_korg1212_probe, + remove: __devexit_p(snd_korg1212_remove), +}; + +static int __init alsa_card_korg1212_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + PRINTK("No Korg 1212IO cards found\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_korg1212_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_korg1212_init) +module_exit(alsa_card_korg1212_exit) + +#ifndef MODULE + +/* format is: snd-korg1212=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_korg1212_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-korg1212=", alsa_card_korg1212_setup); + +#endif /* ifndef MODULE */ + diff -Nru a/sound/pci/maestro3.c b/sound/pci/maestro3.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/maestro3.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2715 @@ +/* + * Driver for ESS Maestro3/Allegro soundcards. + * Copyright (c) 2000 by Zach Brown + * Takashi Iwai + * + * Most of the hardware init stuffs are based on maestro3 driver for + * OSS/Free by Zach Brown. Many thanks to Zach! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * ChangeLog: + * Aug. 27, 2001 + * - Fixed deadlock on capture + * - Added Canyon3D-2 support by Rob Riggs + * + */ + +#define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2" +#define DRIVER_NAME "Maestro3" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Zach Brown , Takashi Iwai "); +MODULE_DESCRIPTION("ESS Maestro3 PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,Maestro3 PCI}," + "{ESS,Allegro PCI}," + "{ESS,Allegro-1 PCI}," + "{ESS,Canyon3D-2/LE PCI}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* all enabled */ +static int snd_external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +static int snd_amp_gpio[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable this soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_external_amp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_external_amp, "Enable external amp for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); +MODULE_PARM(snd_amp_gpio, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_amp_gpio, "GPIO pin number for external amp. (default = -1)"); +MODULE_PARM_SYNTAX(snd_amp_gpio, SNDRV_ENABLED); + +#define MAX_PLAYBACKS 2 +#define MAX_CAPTURES 1 +#define NR_DSPS (MAX_PLAYBACKS + MAX_CAPTURES) + + +/* + * maestro3 registers + */ + +/* Allegro PCI configuration registers */ +#define PCI_LEGACY_AUDIO_CTRL 0x40 +#define SOUND_BLASTER_ENABLE 0x00000001 +#define FM_SYNTHESIS_ENABLE 0x00000002 +#define GAME_PORT_ENABLE 0x00000004 +#define MPU401_IO_ENABLE 0x00000008 +#define MPU401_IRQ_ENABLE 0x00000010 +#define ALIAS_10BIT_IO 0x00000020 +#define SB_DMA_MASK 0x000000C0 +#define SB_DMA_0 0x00000040 +#define SB_DMA_1 0x00000040 +#define SB_DMA_R 0x00000080 +#define SB_DMA_3 0x000000C0 +#define SB_IRQ_MASK 0x00000700 +#define SB_IRQ_5 0x00000000 +#define SB_IRQ_7 0x00000100 +#define SB_IRQ_9 0x00000200 +#define SB_IRQ_10 0x00000300 +#define MIDI_IRQ_MASK 0x00003800 +#define SERIAL_IRQ_ENABLE 0x00004000 +#define DISABLE_LEGACY 0x00008000 + +#define PCI_ALLEGRO_CONFIG 0x50 +#define SB_ADDR_240 0x00000004 +#define MPU_ADDR_MASK 0x00000018 +#define MPU_ADDR_330 0x00000000 +#define MPU_ADDR_300 0x00000008 +#define MPU_ADDR_320 0x00000010 +#define MPU_ADDR_340 0x00000018 +#define USE_PCI_TIMING 0x00000040 +#define POSTED_WRITE_ENABLE 0x00000080 +#define DMA_POLICY_MASK 0x00000700 +#define DMA_DDMA 0x00000000 +#define DMA_TDMA 0x00000100 +#define DMA_PCPCI 0x00000200 +#define DMA_WBDMA16 0x00000400 +#define DMA_WBDMA4 0x00000500 +#define DMA_WBDMA2 0x00000600 +#define DMA_WBDMA1 0x00000700 +#define DMA_SAFE_GUARD 0x00000800 +#define HI_PERF_GP_ENABLE 0x00001000 +#define PIC_SNOOP_MODE_0 0x00002000 +#define PIC_SNOOP_MODE_1 0x00004000 +#define SOUNDBLASTER_IRQ_MASK 0x00008000 +#define RING_IN_ENABLE 0x00010000 +#define SPDIF_TEST_MODE 0x00020000 +#define CLK_MULT_MODE_SELECT_2 0x00040000 +#define EEPROM_WRITE_ENABLE 0x00080000 +#define CODEC_DIR_IN 0x00100000 +#define HV_BUTTON_FROM_GD 0x00200000 +#define REDUCED_DEBOUNCE 0x00400000 +#define HV_CTRL_ENABLE 0x00800000 +#define SPDIF_ENABLE 0x01000000 +#define CLK_DIV_SELECT 0x06000000 +#define CLK_DIV_BY_48 0x00000000 +#define CLK_DIV_BY_49 0x02000000 +#define CLK_DIV_BY_50 0x04000000 +#define CLK_DIV_RESERVED 0x06000000 +#define PM_CTRL_ENABLE 0x08000000 +#define CLK_MULT_MODE_SELECT 0x30000000 +#define CLK_MULT_MODE_SHIFT 28 +#define CLK_MULT_MODE_0 0x00000000 +#define CLK_MULT_MODE_1 0x10000000 +#define CLK_MULT_MODE_2 0x20000000 +#define CLK_MULT_MODE_3 0x30000000 +#define INT_CLK_SELECT 0x40000000 +#define INT_CLK_MULT_RESET 0x80000000 + +/* M3 */ +#define INT_CLK_SRC_NOT_PCI 0x00100000 +#define INT_CLK_MULT_ENABLE 0x80000000 + +#define PCI_ACPI_CONTROL 0x54 +#define PCI_ACPI_D0 0x00000000 +#define PCI_ACPI_D1 0xB4F70000 +#define PCI_ACPI_D2 0xB4F7B4F7 + +#define PCI_USER_CONFIG 0x58 +#define EXT_PCI_MASTER_ENABLE 0x00000001 +#define SPDIF_OUT_SELECT 0x00000002 +#define TEST_PIN_DIR_CTRL 0x00000004 +#define AC97_CODEC_TEST 0x00000020 +#define TRI_STATE_BUFFER 0x00000080 +#define IN_CLK_12MHZ_SELECT 0x00000100 +#define MULTI_FUNC_DISABLE 0x00000200 +#define EXT_MASTER_PAIR_SEL 0x00000400 +#define PCI_MASTER_SUPPORT 0x00000800 +#define STOP_CLOCK_ENABLE 0x00001000 +#define EAPD_DRIVE_ENABLE 0x00002000 +#define REQ_TRI_STATE_ENABLE 0x00004000 +#define REQ_LOW_ENABLE 0x00008000 +#define MIDI_1_ENABLE 0x00010000 +#define MIDI_2_ENABLE 0x00020000 +#define SB_AUDIO_SYNC 0x00040000 +#define HV_CTRL_TEST 0x00100000 +#define SOUNDBLASTER_TEST 0x00400000 + +#define PCI_USER_CONFIG_C 0x5C + +#define PCI_DDMA_CTRL 0x60 +#define DDMA_ENABLE 0x00000001 + + +/* Allegro registers */ +#define HOST_INT_CTRL 0x18 +#define SB_INT_ENABLE 0x0001 +#define MPU401_INT_ENABLE 0x0002 +#define ASSP_INT_ENABLE 0x0010 +#define RING_INT_ENABLE 0x0020 +#define HV_INT_ENABLE 0x0040 +#define CLKRUN_GEN_ENABLE 0x0100 +#define HV_CTRL_TO_PME 0x0400 +#define SOFTWARE_RESET_ENABLE 0x8000 + +/* + * should be using the above defines, probably. + */ +#define REGB_ENABLE_RESET 0x01 +#define REGB_STOP_CLOCK 0x10 + +#define HOST_INT_STATUS 0x1A +#define SB_INT_PENDING 0x01 +#define MPU401_INT_PENDING 0x02 +#define ASSP_INT_PENDING 0x10 +#define RING_INT_PENDING 0x20 +#define HV_INT_PENDING 0x40 + +#define HARDWARE_VOL_CTRL 0x1B +#define SHADOW_MIX_REG_VOICE 0x1C +#define HW_VOL_COUNTER_VOICE 0x1D +#define SHADOW_MIX_REG_MASTER 0x1E +#define HW_VOL_COUNTER_MASTER 0x1F + +#define CODEC_COMMAND 0x30 +#define CODEC_READ_B 0x80 + +#define CODEC_STATUS 0x30 +#define CODEC_BUSY_B 0x01 + +#define CODEC_DATA 0x32 + +#define RING_BUS_CTRL_A 0x36 +#define RAC_PME_ENABLE 0x0100 +#define RAC_SDFS_ENABLE 0x0200 +#define LAC_PME_ENABLE 0x0400 +#define LAC_SDFS_ENABLE 0x0800 +#define SERIAL_AC_LINK_ENABLE 0x1000 +#define IO_SRAM_ENABLE 0x2000 +#define IIS_INPUT_ENABLE 0x8000 + +#define RING_BUS_CTRL_B 0x38 +#define SECOND_CODEC_ID_MASK 0x0003 +#define SPDIF_FUNC_ENABLE 0x0010 +#define SECOND_AC_ENABLE 0x0020 +#define SB_MODULE_INTF_ENABLE 0x0040 +#define SSPE_ENABLE 0x0040 +#define M3I_DOCK_ENABLE 0x0080 + +#define SDO_OUT_DEST_CTRL 0x3A +#define COMMAND_ADDR_OUT 0x0003 +#define PCM_LR_OUT_LOCAL 0x0000 +#define PCM_LR_OUT_REMOTE 0x0004 +#define PCM_LR_OUT_MUTE 0x0008 +#define PCM_LR_OUT_BOTH 0x000C +#define LINE1_DAC_OUT_LOCAL 0x0000 +#define LINE1_DAC_OUT_REMOTE 0x0010 +#define LINE1_DAC_OUT_MUTE 0x0020 +#define LINE1_DAC_OUT_BOTH 0x0030 +#define PCM_CLS_OUT_LOCAL 0x0000 +#define PCM_CLS_OUT_REMOTE 0x0040 +#define PCM_CLS_OUT_MUTE 0x0080 +#define PCM_CLS_OUT_BOTH 0x00C0 +#define PCM_RLF_OUT_LOCAL 0x0000 +#define PCM_RLF_OUT_REMOTE 0x0100 +#define PCM_RLF_OUT_MUTE 0x0200 +#define PCM_RLF_OUT_BOTH 0x0300 +#define LINE2_DAC_OUT_LOCAL 0x0000 +#define LINE2_DAC_OUT_REMOTE 0x0400 +#define LINE2_DAC_OUT_MUTE 0x0800 +#define LINE2_DAC_OUT_BOTH 0x0C00 +#define HANDSET_OUT_LOCAL 0x0000 +#define HANDSET_OUT_REMOTE 0x1000 +#define HANDSET_OUT_MUTE 0x2000 +#define HANDSET_OUT_BOTH 0x3000 +#define IO_CTRL_OUT_LOCAL 0x0000 +#define IO_CTRL_OUT_REMOTE 0x4000 +#define IO_CTRL_OUT_MUTE 0x8000 +#define IO_CTRL_OUT_BOTH 0xC000 + +#define SDO_IN_DEST_CTRL 0x3C +#define STATUS_ADDR_IN 0x0003 +#define PCM_LR_IN_LOCAL 0x0000 +#define PCM_LR_IN_REMOTE 0x0004 +#define PCM_LR_RESERVED 0x0008 +#define PCM_LR_IN_BOTH 0x000C +#define LINE1_ADC_IN_LOCAL 0x0000 +#define LINE1_ADC_IN_REMOTE 0x0010 +#define LINE1_ADC_IN_MUTE 0x0020 +#define MIC_ADC_IN_LOCAL 0x0000 +#define MIC_ADC_IN_REMOTE 0x0040 +#define MIC_ADC_IN_MUTE 0x0080 +#define LINE2_DAC_IN_LOCAL 0x0000 +#define LINE2_DAC_IN_REMOTE 0x0400 +#define LINE2_DAC_IN_MUTE 0x0800 +#define HANDSET_IN_LOCAL 0x0000 +#define HANDSET_IN_REMOTE 0x1000 +#define HANDSET_IN_MUTE 0x2000 +#define IO_STATUS_IN_LOCAL 0x0000 +#define IO_STATUS_IN_REMOTE 0x4000 + +#define SPDIF_IN_CTRL 0x3E +#define SPDIF_IN_ENABLE 0x0001 + +#define GPIO_DATA 0x60 +#define GPIO_DATA_MASK 0x0FFF +#define GPIO_HV_STATUS 0x3000 +#define GPIO_PME_STATUS 0x4000 + +#define GPIO_MASK 0x64 +#define GPIO_DIRECTION 0x68 +#define GPO_PRIMARY_AC97 0x0001 +#define GPI_LINEOUT_SENSE 0x0004 +#define GPO_SECONDARY_AC97 0x0008 +#define GPI_VOL_DOWN 0x0010 +#define GPI_VOL_UP 0x0020 +#define GPI_IIS_CLK 0x0040 +#define GPI_IIS_LRCLK 0x0080 +#define GPI_IIS_DATA 0x0100 +#define GPI_DOCKING_STATUS 0x0100 +#define GPI_HEADPHONE_SENSE 0x0200 +#define GPO_EXT_AMP_SHUTDOWN 0x1000 + +#define GPO_EXT_AMP_M3 1 /* default m3 amp */ +#define GPO_EXT_AMP_ALLEGRO 8 /* default allegro amp */ + +/* M3 */ +#define GPO_M3_EXT_AMP_SHUTDN 0x0002 + +#define ASSP_INDEX_PORT 0x80 +#define ASSP_MEMORY_PORT 0x82 +#define ASSP_DATA_PORT 0x84 + +#define MPU401_DATA_PORT 0x98 +#define MPU401_STATUS_PORT 0x99 + +#define CLK_MULT_DATA_PORT 0x9C + +#define ASSP_CONTROL_A 0xA2 +#define ASSP_0_WS_ENABLE 0x01 +#define ASSP_CTRL_A_RESERVED1 0x02 +#define ASSP_CTRL_A_RESERVED2 0x04 +#define ASSP_CLK_49MHZ_SELECT 0x08 +#define FAST_PLU_ENABLE 0x10 +#define ASSP_CTRL_A_RESERVED3 0x20 +#define DSP_CLK_36MHZ_SELECT 0x40 + +#define ASSP_CONTROL_B 0xA4 +#define RESET_ASSP 0x00 +#define RUN_ASSP 0x01 +#define ENABLE_ASSP_CLOCK 0x00 +#define STOP_ASSP_CLOCK 0x10 +#define RESET_TOGGLE 0x40 + +#define ASSP_CONTROL_C 0xA6 +#define ASSP_HOST_INT_ENABLE 0x01 +#define FM_ADDR_REMAP_DISABLE 0x02 +#define HOST_WRITE_PORT_ENABLE 0x08 + +#define ASSP_HOST_INT_STATUS 0xAC +#define DSP2HOST_REQ_PIORECORD 0x01 +#define DSP2HOST_REQ_I2SRATE 0x02 +#define DSP2HOST_REQ_TIMER 0x04 + +/* AC97 registers */ +/* XXX fix this crap up */ +/*#define AC97_RESET 0x00*/ + +#define AC97_VOL_MUTE_B 0x8000 +#define AC97_VOL_M 0x1F +#define AC97_LEFT_VOL_S 8 + +#define AC97_MASTER_VOL 0x02 +#define AC97_LINE_LEVEL_VOL 0x04 +#define AC97_MASTER_MONO_VOL 0x06 +#define AC97_PC_BEEP_VOL 0x0A +#define AC97_PC_BEEP_VOL_M 0x0F +#define AC97_SROUND_MASTER_VOL 0x38 +#define AC97_PC_BEEP_VOL_S 1 + +/*#define AC97_PHONE_VOL 0x0C +#define AC97_MIC_VOL 0x0E*/ +#define AC97_MIC_20DB_ENABLE 0x40 + +/*#define AC97_LINEIN_VOL 0x10 +#define AC97_CD_VOL 0x12 +#define AC97_VIDEO_VOL 0x14 +#define AC97_AUX_VOL 0x16*/ +#define AC97_PCM_OUT_VOL 0x18 +/*#define AC97_RECORD_SELECT 0x1A*/ +#define AC97_RECORD_MIC 0x00 +#define AC97_RECORD_CD 0x01 +#define AC97_RECORD_VIDEO 0x02 +#define AC97_RECORD_AUX 0x03 +#define AC97_RECORD_MONO_MUX 0x02 +#define AC97_RECORD_DIGITAL 0x03 +#define AC97_RECORD_LINE 0x04 +#define AC97_RECORD_STEREO 0x05 +#define AC97_RECORD_MONO 0x06 +#define AC97_RECORD_PHONE 0x07 + +/*#define AC97_RECORD_GAIN 0x1C*/ +#define AC97_RECORD_VOL_M 0x0F + +/*#define AC97_GENERAL_PURPOSE 0x20*/ +#define AC97_POWER_DOWN_CTRL 0x26 +#define AC97_ADC_READY 0x0001 +#define AC97_DAC_READY 0x0002 +#define AC97_ANALOG_READY 0x0004 +#define AC97_VREF_ON 0x0008 +#define AC97_PR0 0x0100 +#define AC97_PR1 0x0200 +#define AC97_PR2 0x0400 +#define AC97_PR3 0x0800 +#define AC97_PR4 0x1000 + +#define AC97_RESERVED1 0x28 + +#define AC97_VENDOR_TEST 0x5A + +#define AC97_CLOCK_DELAY 0x5C +#define AC97_LINEOUT_MUX_SEL 0x0001 +#define AC97_MONO_MUX_SEL 0x0002 +#define AC97_CLOCK_DELAY_SEL 0x1F +#define AC97_DAC_CDS_SHIFT 6 +#define AC97_ADC_CDS_SHIFT 11 + +#define AC97_MULTI_CHANNEL_SEL 0x74 + +/*#define AC97_VENDOR_ID1 0x7C +#define AC97_VENDOR_ID2 0x7E*/ + +/* + * ASSP control regs + */ +#define DSP_PORT_TIMER_COUNT 0x06 + +#define DSP_PORT_MEMORY_INDEX 0x80 + +#define DSP_PORT_MEMORY_TYPE 0x82 +#define MEMTYPE_INTERNAL_CODE 0x0002 +#define MEMTYPE_INTERNAL_DATA 0x0003 +#define MEMTYPE_MASK 0x0003 + +#define DSP_PORT_MEMORY_DATA 0x84 + +#define DSP_PORT_CONTROL_REG_A 0xA2 +#define DSP_PORT_CONTROL_REG_B 0xA4 +#define DSP_PORT_CONTROL_REG_C 0xA6 + +#define REV_A_CODE_MEMORY_BEGIN 0x0000 +#define REV_A_CODE_MEMORY_END 0x0FFF +#define REV_A_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_A_CODE_MEMORY_LENGTH (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1) + +#define REV_B_CODE_MEMORY_BEGIN 0x0000 +#define REV_B_CODE_MEMORY_END 0x0BFF +#define REV_B_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_B_CODE_MEMORY_LENGTH (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1) + +#define REV_A_DATA_MEMORY_BEGIN 0x1000 +#define REV_A_DATA_MEMORY_END 0x2FFF +#define REV_A_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_A_DATA_MEMORY_LENGTH (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1) + +#define REV_B_DATA_MEMORY_BEGIN 0x1000 +#define REV_B_DATA_MEMORY_END 0x2BFF +#define REV_B_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_B_DATA_MEMORY_LENGTH (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1) + + +#define NUM_UNITS_KERNEL_CODE 16 +#define NUM_UNITS_KERNEL_DATA 2 + +#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16 +#define NUM_UNITS_KERNEL_DATA_WITH_HSP 5 + +/* + * Kernel data layout + */ + +#define DP_SHIFT_COUNT 7 + +#define KDATA_BASE_ADDR 0x1000 +#define KDATA_BASE_ADDR2 0x1080 + +#define KDATA_TASK0 (KDATA_BASE_ADDR + 0x0000) +#define KDATA_TASK1 (KDATA_BASE_ADDR + 0x0001) +#define KDATA_TASK2 (KDATA_BASE_ADDR + 0x0002) +#define KDATA_TASK3 (KDATA_BASE_ADDR + 0x0003) +#define KDATA_TASK4 (KDATA_BASE_ADDR + 0x0004) +#define KDATA_TASK5 (KDATA_BASE_ADDR + 0x0005) +#define KDATA_TASK6 (KDATA_BASE_ADDR + 0x0006) +#define KDATA_TASK7 (KDATA_BASE_ADDR + 0x0007) +#define KDATA_TASK_ENDMARK (KDATA_BASE_ADDR + 0x0008) + +#define KDATA_CURRENT_TASK (KDATA_BASE_ADDR + 0x0009) +#define KDATA_TASK_SWITCH (KDATA_BASE_ADDR + 0x000A) + +#define KDATA_INSTANCE0_POS3D (KDATA_BASE_ADDR + 0x000B) +#define KDATA_INSTANCE1_POS3D (KDATA_BASE_ADDR + 0x000C) +#define KDATA_INSTANCE2_POS3D (KDATA_BASE_ADDR + 0x000D) +#define KDATA_INSTANCE3_POS3D (KDATA_BASE_ADDR + 0x000E) +#define KDATA_INSTANCE4_POS3D (KDATA_BASE_ADDR + 0x000F) +#define KDATA_INSTANCE5_POS3D (KDATA_BASE_ADDR + 0x0010) +#define KDATA_INSTANCE6_POS3D (KDATA_BASE_ADDR + 0x0011) +#define KDATA_INSTANCE7_POS3D (KDATA_BASE_ADDR + 0x0012) +#define KDATA_INSTANCE8_POS3D (KDATA_BASE_ADDR + 0x0013) +#define KDATA_INSTANCE_POS3D_ENDMARK (KDATA_BASE_ADDR + 0x0014) + +#define KDATA_INSTANCE0_SPKVIRT (KDATA_BASE_ADDR + 0x0015) +#define KDATA_INSTANCE_SPKVIRT_ENDMARK (KDATA_BASE_ADDR + 0x0016) + +#define KDATA_INSTANCE0_SPDIF (KDATA_BASE_ADDR + 0x0017) +#define KDATA_INSTANCE_SPDIF_ENDMARK (KDATA_BASE_ADDR + 0x0018) + +#define KDATA_INSTANCE0_MODEM (KDATA_BASE_ADDR + 0x0019) +#define KDATA_INSTANCE_MODEM_ENDMARK (KDATA_BASE_ADDR + 0x001A) + +#define KDATA_INSTANCE0_SRC (KDATA_BASE_ADDR + 0x001B) +#define KDATA_INSTANCE1_SRC (KDATA_BASE_ADDR + 0x001C) +#define KDATA_INSTANCE_SRC_ENDMARK (KDATA_BASE_ADDR + 0x001D) + +#define KDATA_INSTANCE0_MINISRC (KDATA_BASE_ADDR + 0x001E) +#define KDATA_INSTANCE1_MINISRC (KDATA_BASE_ADDR + 0x001F) +#define KDATA_INSTANCE2_MINISRC (KDATA_BASE_ADDR + 0x0020) +#define KDATA_INSTANCE3_MINISRC (KDATA_BASE_ADDR + 0x0021) +#define KDATA_INSTANCE_MINISRC_ENDMARK (KDATA_BASE_ADDR + 0x0022) + +#define KDATA_INSTANCE0_CPYTHRU (KDATA_BASE_ADDR + 0x0023) +#define KDATA_INSTANCE1_CPYTHRU (KDATA_BASE_ADDR + 0x0024) +#define KDATA_INSTANCE_CPYTHRU_ENDMARK (KDATA_BASE_ADDR + 0x0025) + +#define KDATA_CURRENT_DMA (KDATA_BASE_ADDR + 0x0026) +#define KDATA_DMA_SWITCH (KDATA_BASE_ADDR + 0x0027) +#define KDATA_DMA_ACTIVE (KDATA_BASE_ADDR + 0x0028) + +#define KDATA_DMA_XFER0 (KDATA_BASE_ADDR + 0x0029) +#define KDATA_DMA_XFER1 (KDATA_BASE_ADDR + 0x002A) +#define KDATA_DMA_XFER2 (KDATA_BASE_ADDR + 0x002B) +#define KDATA_DMA_XFER3 (KDATA_BASE_ADDR + 0x002C) +#define KDATA_DMA_XFER4 (KDATA_BASE_ADDR + 0x002D) +#define KDATA_DMA_XFER5 (KDATA_BASE_ADDR + 0x002E) +#define KDATA_DMA_XFER6 (KDATA_BASE_ADDR + 0x002F) +#define KDATA_DMA_XFER7 (KDATA_BASE_ADDR + 0x0030) +#define KDATA_DMA_XFER8 (KDATA_BASE_ADDR + 0x0031) +#define KDATA_DMA_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0032) + +#define KDATA_I2S_SAMPLE_COUNT (KDATA_BASE_ADDR + 0x0033) +#define KDATA_I2S_INT_METER (KDATA_BASE_ADDR + 0x0034) +#define KDATA_I2S_ACTIVE (KDATA_BASE_ADDR + 0x0035) + +#define KDATA_TIMER_COUNT_RELOAD (KDATA_BASE_ADDR + 0x0036) +#define KDATA_TIMER_COUNT_CURRENT (KDATA_BASE_ADDR + 0x0037) + +#define KDATA_HALT_SYNCH_CLIENT (KDATA_BASE_ADDR + 0x0038) +#define KDATA_HALT_SYNCH_DMA (KDATA_BASE_ADDR + 0x0039) +#define KDATA_HALT_ACKNOWLEDGE (KDATA_BASE_ADDR + 0x003A) + +#define KDATA_ADC1_XFER0 (KDATA_BASE_ADDR + 0x003B) +#define KDATA_ADC1_XFER_ENDMARK (KDATA_BASE_ADDR + 0x003C) +#define KDATA_ADC1_LEFT_VOLUME (KDATA_BASE_ADDR + 0x003D) +#define KDATA_ADC1_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x003E) +#define KDATA_ADC1_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x003F) +#define KDATA_ADC1_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0040) + +#define KDATA_ADC2_XFER0 (KDATA_BASE_ADDR + 0x0041) +#define KDATA_ADC2_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0042) +#define KDATA_ADC2_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0043) +#define KDATA_ADC2_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x0044) +#define KDATA_ADC2_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x0045) +#define KDATA_ADC2_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0046) + +#define KDATA_CD_XFER0 (KDATA_BASE_ADDR + 0x0047) +#define KDATA_CD_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0048) +#define KDATA_CD_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0049) +#define KDATA_CD_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x004A) +#define KDATA_CD_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x004B) +#define KDATA_CD_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x004C) + +#define KDATA_MIC_XFER0 (KDATA_BASE_ADDR + 0x004D) +#define KDATA_MIC_XFER_ENDMARK (KDATA_BASE_ADDR + 0x004E) +#define KDATA_MIC_VOLUME (KDATA_BASE_ADDR + 0x004F) +#define KDATA_MIC_SUR_VOL (KDATA_BASE_ADDR + 0x0050) + +#define KDATA_I2S_XFER0 (KDATA_BASE_ADDR + 0x0051) +#define KDATA_I2S_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0052) + +#define KDATA_CHI_XFER0 (KDATA_BASE_ADDR + 0x0053) +#define KDATA_CHI_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0054) + +#define KDATA_SPDIF_XFER (KDATA_BASE_ADDR + 0x0055) +#define KDATA_SPDIF_CURRENT_FRAME (KDATA_BASE_ADDR + 0x0056) +#define KDATA_SPDIF_FRAME0 (KDATA_BASE_ADDR + 0x0057) +#define KDATA_SPDIF_FRAME1 (KDATA_BASE_ADDR + 0x0058) +#define KDATA_SPDIF_FRAME2 (KDATA_BASE_ADDR + 0x0059) + +#define KDATA_SPDIF_REQUEST (KDATA_BASE_ADDR + 0x005A) +#define KDATA_SPDIF_TEMP (KDATA_BASE_ADDR + 0x005B) + +#define KDATA_SPDIFIN_XFER0 (KDATA_BASE_ADDR + 0x005C) +#define KDATA_SPDIFIN_XFER_ENDMARK (KDATA_BASE_ADDR + 0x005D) +#define KDATA_SPDIFIN_INT_METER (KDATA_BASE_ADDR + 0x005E) + +#define KDATA_DSP_RESET_COUNT (KDATA_BASE_ADDR + 0x005F) +#define KDATA_DEBUG_OUTPUT (KDATA_BASE_ADDR + 0x0060) + +#define KDATA_KERNEL_ISR_LIST (KDATA_BASE_ADDR + 0x0061) + +#define KDATA_KERNEL_ISR_CBSR1 (KDATA_BASE_ADDR + 0x0062) +#define KDATA_KERNEL_ISR_CBER1 (KDATA_BASE_ADDR + 0x0063) +#define KDATA_KERNEL_ISR_CBCR (KDATA_BASE_ADDR + 0x0064) +#define KDATA_KERNEL_ISR_AR0 (KDATA_BASE_ADDR + 0x0065) +#define KDATA_KERNEL_ISR_AR1 (KDATA_BASE_ADDR + 0x0066) +#define KDATA_KERNEL_ISR_AR2 (KDATA_BASE_ADDR + 0x0067) +#define KDATA_KERNEL_ISR_AR3 (KDATA_BASE_ADDR + 0x0068) +#define KDATA_KERNEL_ISR_AR4 (KDATA_BASE_ADDR + 0x0069) +#define KDATA_KERNEL_ISR_AR5 (KDATA_BASE_ADDR + 0x006A) +#define KDATA_KERNEL_ISR_BRCR (KDATA_BASE_ADDR + 0x006B) +#define KDATA_KERNEL_ISR_PASR (KDATA_BASE_ADDR + 0x006C) +#define KDATA_KERNEL_ISR_PAER (KDATA_BASE_ADDR + 0x006D) + +#define KDATA_CLIENT_SCRATCH0 (KDATA_BASE_ADDR + 0x006E) +#define KDATA_CLIENT_SCRATCH1 (KDATA_BASE_ADDR + 0x006F) +#define KDATA_KERNEL_SCRATCH (KDATA_BASE_ADDR + 0x0070) +#define KDATA_KERNEL_ISR_SCRATCH (KDATA_BASE_ADDR + 0x0071) + +#define KDATA_OUEUE_LEFT (KDATA_BASE_ADDR + 0x0072) +#define KDATA_QUEUE_RIGHT (KDATA_BASE_ADDR + 0x0073) + +#define KDATA_ADC1_REQUEST (KDATA_BASE_ADDR + 0x0074) +#define KDATA_ADC2_REQUEST (KDATA_BASE_ADDR + 0x0075) +#define KDATA_CD_REQUEST (KDATA_BASE_ADDR + 0x0076) +#define KDATA_MIC_REQUEST (KDATA_BASE_ADDR + 0x0077) + +#define KDATA_ADC1_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0078) +#define KDATA_ADC2_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0079) +#define KDATA_CD_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007A) +#define KDATA_MIC_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007B) +#define KDATA_MIC_SYNC_COUNTER (KDATA_BASE_ADDR + 0x007C) + +/* + * second 'segment' (?) reserved for mixer + * buffers.. + */ + +#define KDATA_MIXER_WORD0 (KDATA_BASE_ADDR2 + 0x0000) +#define KDATA_MIXER_WORD1 (KDATA_BASE_ADDR2 + 0x0001) +#define KDATA_MIXER_WORD2 (KDATA_BASE_ADDR2 + 0x0002) +#define KDATA_MIXER_WORD3 (KDATA_BASE_ADDR2 + 0x0003) +#define KDATA_MIXER_WORD4 (KDATA_BASE_ADDR2 + 0x0004) +#define KDATA_MIXER_WORD5 (KDATA_BASE_ADDR2 + 0x0005) +#define KDATA_MIXER_WORD6 (KDATA_BASE_ADDR2 + 0x0006) +#define KDATA_MIXER_WORD7 (KDATA_BASE_ADDR2 + 0x0007) +#define KDATA_MIXER_WORD8 (KDATA_BASE_ADDR2 + 0x0008) +#define KDATA_MIXER_WORD9 (KDATA_BASE_ADDR2 + 0x0009) +#define KDATA_MIXER_WORDA (KDATA_BASE_ADDR2 + 0x000A) +#define KDATA_MIXER_WORDB (KDATA_BASE_ADDR2 + 0x000B) +#define KDATA_MIXER_WORDC (KDATA_BASE_ADDR2 + 0x000C) +#define KDATA_MIXER_WORDD (KDATA_BASE_ADDR2 + 0x000D) +#define KDATA_MIXER_WORDE (KDATA_BASE_ADDR2 + 0x000E) +#define KDATA_MIXER_WORDF (KDATA_BASE_ADDR2 + 0x000F) + +#define KDATA_MIXER_XFER0 (KDATA_BASE_ADDR2 + 0x0010) +#define KDATA_MIXER_XFER1 (KDATA_BASE_ADDR2 + 0x0011) +#define KDATA_MIXER_XFER2 (KDATA_BASE_ADDR2 + 0x0012) +#define KDATA_MIXER_XFER3 (KDATA_BASE_ADDR2 + 0x0013) +#define KDATA_MIXER_XFER4 (KDATA_BASE_ADDR2 + 0x0014) +#define KDATA_MIXER_XFER5 (KDATA_BASE_ADDR2 + 0x0015) +#define KDATA_MIXER_XFER6 (KDATA_BASE_ADDR2 + 0x0016) +#define KDATA_MIXER_XFER7 (KDATA_BASE_ADDR2 + 0x0017) +#define KDATA_MIXER_XFER8 (KDATA_BASE_ADDR2 + 0x0018) +#define KDATA_MIXER_XFER9 (KDATA_BASE_ADDR2 + 0x0019) +#define KDATA_MIXER_XFER_ENDMARK (KDATA_BASE_ADDR2 + 0x001A) + +#define KDATA_MIXER_TASK_NUMBER (KDATA_BASE_ADDR2 + 0x001B) +#define KDATA_CURRENT_MIXER (KDATA_BASE_ADDR2 + 0x001C) +#define KDATA_MIXER_ACTIVE (KDATA_BASE_ADDR2 + 0x001D) +#define KDATA_MIXER_BANK_STATUS (KDATA_BASE_ADDR2 + 0x001E) +#define KDATA_DAC_LEFT_VOLUME (KDATA_BASE_ADDR2 + 0x001F) +#define KDATA_DAC_RIGHT_VOLUME (KDATA_BASE_ADDR2 + 0x0020) + +#define MAX_INSTANCE_MINISRC (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC) +#define MAX_VIRTUAL_DMA_CHANNELS (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0) +#define MAX_VIRTUAL_MIXER_CHANNELS (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0) +#define MAX_VIRTUAL_ADC1_CHANNELS (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0) + +/* + * client data area offsets + */ +#define CDATA_INSTANCE_READY 0x00 + +#define CDATA_HOST_SRC_ADDRL 0x01 +#define CDATA_HOST_SRC_ADDRH 0x02 +#define CDATA_HOST_SRC_END_PLUS_1L 0x03 +#define CDATA_HOST_SRC_END_PLUS_1H 0x04 +#define CDATA_HOST_SRC_CURRENTL 0x05 +#define CDATA_HOST_SRC_CURRENTH 0x06 + +#define CDATA_IN_BUF_CONNECT 0x07 +#define CDATA_OUT_BUF_CONNECT 0x08 + +#define CDATA_IN_BUF_BEGIN 0x09 +#define CDATA_IN_BUF_END_PLUS_1 0x0A +#define CDATA_IN_BUF_HEAD 0x0B +#define CDATA_IN_BUF_TAIL 0x0C +#define CDATA_OUT_BUF_BEGIN 0x0D +#define CDATA_OUT_BUF_END_PLUS_1 0x0E +#define CDATA_OUT_BUF_HEAD 0x0F +#define CDATA_OUT_BUF_TAIL 0x10 + +#define CDATA_DMA_CONTROL 0x11 +#define CDATA_RESERVED 0x12 + +#define CDATA_FREQUENCY 0x13 +#define CDATA_LEFT_VOLUME 0x14 +#define CDATA_RIGHT_VOLUME 0x15 +#define CDATA_LEFT_SUR_VOL 0x16 +#define CDATA_RIGHT_SUR_VOL 0x17 + +#define CDATA_HEADER_LEN 0x18 + +#define SRC3_DIRECTION_OFFSET CDATA_HEADER_LEN +#define SRC3_MODE_OFFSET (CDATA_HEADER_LEN + 1) +#define SRC3_WORD_LENGTH_OFFSET (CDATA_HEADER_LEN + 2) +#define SRC3_PARAMETER_OFFSET (CDATA_HEADER_LEN + 3) +#define SRC3_COEFF_ADDR_OFFSET (CDATA_HEADER_LEN + 8) +#define SRC3_FILTAP_ADDR_OFFSET (CDATA_HEADER_LEN + 10) +#define SRC3_TEMP_INBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 16) +#define SRC3_TEMP_OUTBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 17) + +#define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 ) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 ) +#define MINISRC_BIQUAD_STAGE 2 +#define MINISRC_COEF_LOC 0x175 + +#define DMACONTROL_BLOCK_MASK 0x000F +#define DMAC_BLOCK0_SELECTOR 0x0000 +#define DMAC_BLOCK1_SELECTOR 0x0001 +#define DMAC_BLOCK2_SELECTOR 0x0002 +#define DMAC_BLOCK3_SELECTOR 0x0003 +#define DMAC_BLOCK4_SELECTOR 0x0004 +#define DMAC_BLOCK5_SELECTOR 0x0005 +#define DMAC_BLOCK6_SELECTOR 0x0006 +#define DMAC_BLOCK7_SELECTOR 0x0007 +#define DMAC_BLOCK8_SELECTOR 0x0008 +#define DMAC_BLOCK9_SELECTOR 0x0009 +#define DMAC_BLOCKA_SELECTOR 0x000A +#define DMAC_BLOCKB_SELECTOR 0x000B +#define DMAC_BLOCKC_SELECTOR 0x000C +#define DMAC_BLOCKD_SELECTOR 0x000D +#define DMAC_BLOCKE_SELECTOR 0x000E +#define DMAC_BLOCKF_SELECTOR 0x000F +#define DMACONTROL_PAGE_MASK 0x00F0 +#define DMAC_PAGE0_SELECTOR 0x0030 +#define DMAC_PAGE1_SELECTOR 0x0020 +#define DMAC_PAGE2_SELECTOR 0x0010 +#define DMAC_PAGE3_SELECTOR 0x0000 +#define DMACONTROL_AUTOREPEAT 0x1000 +#define DMACONTROL_STOPPED 0x2000 +#define DMACONTROL_DIRECTION 0x0100 + +/* + * an arbitrary volume we set the internal + * volume settings to so that the ac97 volume + * range is a little less insane. 0x7fff is + * max. + */ +#define ARB_VOLUME ( 0x6800 ) + +/* + */ + +typedef struct snd_m3_dma m3_dma_t; +typedef struct snd_m3 m3_t; +#define chip_t m3_t + + +struct m3_list { + int curlen; + int mem_addr; + int max; +}; + +struct snd_m3_dma { + + int number; + m3_t *chip; + snd_pcm_substream_t *substream; + + struct assp_instance { + unsigned short code, data; + } inst; + + int running; + int opened; + + unsigned long buffer_addr; + int dma_size; + int period_size; + unsigned int hwptr; + int count; + + int index[3]; + struct m3_list *index_list[3]; + + int in_lists; + + struct list_head list; + +}; + +struct snd_m3 { + + snd_card_t *card; + + unsigned long iobase; + struct resource *iobase_res; + + int irq; + int allegro_flag : 1; + + ac97_t *ac97; + + snd_pcm_t *pcm; + + struct pci_dev *pci; + /* pci_dev in 2.2 kernel doesn't have them, so we keep them private */ + u16 subsystem_vendor; + u16 subsystem_device; + + int dacs_active; + int timer_users; + + struct m3_list msrc_list; + struct m3_list mixer_list; + struct m3_list adc1_list; + struct m3_list dma_list; + + /* for storing reset state..*/ + u8 reset_state; + + int external_amp; + int amp_gpio; + + /* pcm streams */ + int num_substreams; + m3_dma_t *substreams; + + spinlock_t reg_lock; + +#ifdef CONFIG_PM + u16 *suspend_mem; +#endif +}; + +/* + * pci ids + */ + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125D +#endif +#ifndef PCI_DEVICE_ID_ESS_ALLEGRO_1 +#define PCI_DEVICE_ID_ESS_ALLEGRO_1 0x1988 +#endif +#ifndef PCI_DEVICE_ID_ESS_ALLEGRO +#define PCI_DEVICE_ID_ESS_ALLEGRO 0x1989 +#endif +#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2LE +#define PCI_DEVICE_ID_ESS_CANYON3D_2LE 0x1990 +#endif +#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2 +#define PCI_DEVICE_ID_ESS_CANYON3D_2 0x1992 +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3 +#define PCI_DEVICE_ID_ESS_MAESTRO3 0x1998 +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_1 +#define PCI_DEVICE_ID_ESS_MAESTRO3_1 0x1999 +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_HW +#define PCI_DEVICE_ID_ESS_MAESTRO3_HW 0x199a +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_2 +#define PCI_DEVICE_ID_ESS_MAESTRO3_2 0x199b +#endif + +static struct pci_device_id snd_m3_ids[] __devinitdata = { + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2LE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_HW, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, snd_m3_ids); + +/* + * lowlevel functions + */ + +inline static void snd_m3_outw(m3_t *chip, u16 value, unsigned long reg) +{ + outw(value, chip->iobase + reg); +} + +inline static u16 snd_m3_inw(m3_t *chip, unsigned long reg) +{ + return inw(chip->iobase + reg); +} + +inline static void snd_m3_outb(m3_t *chip, u8 value, unsigned long reg) +{ + outb(value, chip->iobase + reg); +} + +inline static u8 snd_m3_inb(m3_t *chip, unsigned long reg) +{ + return inb(chip->iobase + reg); +} + +/* + * access 16bit words to the code or data regions of the dsp's memory. + * index addresses 16bit words. + */ +static u16 snd_m3_assp_read(m3_t *chip, u16 region, u16 index) +{ + snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX); + return snd_m3_inw(chip, DSP_PORT_MEMORY_DATA); +} + +static void snd_m3_assp_write(m3_t *chip, u16 region, u16 index, u16 data) +{ + snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX); + snd_m3_outw(chip, data, DSP_PORT_MEMORY_DATA); +} + +static void snd_m3_assp_halt(m3_t *chip) +{ + chip->reset_state = snd_m3_inb(chip, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK; + mdelay(10); + snd_m3_outb(chip, chip->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + +static void snd_m3_assp_continue(m3_t *chip) +{ + snd_m3_outb(chip, chip->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + + +/* + * This makes me sad. the maestro3 has lists + * internally that must be packed.. 0 terminates, + * apparently, or maybe all unused entries have + * to be 0, the lists have static lengths set + * by the binary code images. + */ + +static int snd_m3_add_list(m3_t *chip, struct m3_list *list, u16 val) +{ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + list->curlen, + val); + return list->curlen++; +} + +static void snd_m3_remove_list(m3_t *chip, struct m3_list *list, int index) +{ + u16 val; + int lastindex = list->curlen - 1; + + if (index != lastindex) { + val = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + index, + val); + } + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex, + 0); + + list->curlen--; +} + +static void snd_m3_inc_timer_users(m3_t *chip) +{ + chip->timer_users++; + if (chip->timer_users != 1) + return; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 240); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 240); + + snd_m3_outw(chip, + snd_m3_inw(chip, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +} + +static void snd_m3_dec_timer_users(m3_t *chip) +{ + chip->timer_users--; + if (chip->timer_users > 0) + return; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 0); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 0); + + snd_m3_outw(chip, + snd_m3_inw(chip, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +} + +/* + * start/stop + */ + +/* spinlock held! */ +static int snd_m3_pcm_start(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + if (! s || ! subs) + return -EINVAL; + + snd_m3_inc_timer_users(chip); + switch (subs->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + chip->dacs_active++; + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_INSTANCE_READY, 1); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + chip->dacs_active); + break; + case SNDRV_PCM_STREAM_CAPTURE: + snd_m3_assp_write(s->chip, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 1); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_INSTANCE_READY, 1); + break; + } + return 0; +} + +/* spinlock held! */ +static int snd_m3_pcm_stop(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + if (! s || ! subs) + return -EINVAL; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_INSTANCE_READY, 0); + snd_m3_dec_timer_users(chip); + switch (subs->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + chip->dacs_active--; + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + chip->dacs_active); + break; + case SNDRV_PCM_STREAM_CAPTURE: + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 0); + break; + } + return 0; +} + +static int +snd_m3_pcm_trigger(snd_pcm_substream_t *subs, int cmd) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data; + unsigned long flags; + int err = -EINVAL; + + snd_assert(s != NULL, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (s->running) + err = -EBUSY; + else { + s->running = 1; + err = snd_m3_pcm_start(chip, s, subs); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (! s->running) + err = 0; /* should return error? */ + else { + s->running = 0; + err = snd_m3_pcm_stop(chip, s, subs); + } + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return err; +} + +/* + * setup + */ +static void +snd_m3_pcm_setup1(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + int dsp_in_size, dsp_out_size, dsp_in_buffer, dsp_out_buffer; + snd_pcm_runtime_t *runtime = subs->runtime; + + if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2); + dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2); + } else { + dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x10 * 2); + dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2); + } + dsp_in_buffer = s->inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); + dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; + + s->dma_size = frames_to_bytes(runtime, runtime->buffer_size); + s->period_size = frames_to_bytes(runtime, runtime->period_size); + s->hwptr = 0; + s->count = 0; + +#define LO(x) ((x) & 0xffff) +#define HI(x) LO((x) >> 16) + + /* host dma buffer pointers */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_ADDRL, + LO(s->buffer_addr)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_ADDRH, + HI(s->buffer_addr)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_END_PLUS_1L, + LO(s->buffer_addr + s->dma_size)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_END_PLUS_1H, + HI(s->buffer_addr + s->dma_size)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTL, + LO(s->buffer_addr)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTH, + HI(s->buffer_addr)); +#undef LO +#undef HI + + /* dsp buffers */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_BEGIN, + dsp_in_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_END_PLUS_1, + dsp_in_buffer + (dsp_in_size / 2)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_HEAD, + dsp_in_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_TAIL, + dsp_in_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_BEGIN, + dsp_out_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_END_PLUS_1, + dsp_out_buffer + (dsp_out_size / 2)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_HEAD, + dsp_out_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_TAIL, + dsp_out_buffer); +} + +static void snd_m3_pcm_setup2(m3_t *chip, m3_dma_t *s, snd_pcm_runtime_t *runtime) +{ + u32 freq; + + /* + * put us in the lists if we're not already there + */ + if (! s->in_lists) { + s->index[0] = snd_m3_add_list(chip, s->index_list[0], + s->inst.data >> DP_SHIFT_COUNT); + s->index[1] = snd_m3_add_list(chip, s->index_list[1], + s->inst.data >> DP_SHIFT_COUNT); + s->index[2] = snd_m3_add_list(chip, s->index_list[2], + s->inst.data >> DP_SHIFT_COUNT); + s->in_lists = 1; + } + + /* write to 'mono' word */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 1, + runtime->channels == 2 ? 0 : 1); + /* write to '8bit' word */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 2, + snd_pcm_format_width(runtime->format) == 16 ? 0 : 1); + + /* set up dac/adc rate */ + freq = ((runtime->rate << 15) + 24000 ) / 48000; + if (freq) + freq--; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_FREQUENCY, + freq); +} + + +static struct play_vals { + u16 addr, val; +} pv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 0} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */ + {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */ +}; + + +/* the mode passed should be already shifted and masked */ +static void +snd_m3_playback_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + int i; + + /* + * some per client initializers + */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 12, + s->inst.data + 40 + 8); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 19, + s->inst.code + MINISRC_COEF_LOC); + + /* enable or disable low pass filter? */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 22, + subs->runtime->rate > 45000 ? 0xff : 0); + + /* tell it which way dma is going? */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_DMA_CONTROL, + DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for (i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + pv[i].addr, pv[i].val); +} + +/* + * Native record driver + */ +static struct rec_vals { + u16 addr, val; +} rv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 1} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */ + {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */ + {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */ +}; + +static void +snd_m3_capture_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + int i; + + /* + * some per client initializers + */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 12, + s->inst.data + 40 + 8); + + /* tell it which way dma is going? */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_DMA_CONTROL, + DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for (i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + rv[i].addr, rv[i].val); +} + +static int snd_m3_pcm_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + m3_dma_t *s = (m3_dma_t*) substream->runtime->private_data; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + /* set buffer address */ + s->buffer_addr = substream->runtime->dma_addr; + if (s->buffer_addr & 0x3) { + snd_printk("oh my, not aligned\n"); + s->buffer_addr = s->buffer_addr & ~0x3; + } + return 0; +} + +static int snd_m3_pcm_hw_free(snd_pcm_substream_t * substream) +{ + m3_dma_t *s; + + if (substream->runtime->private_data == NULL) + return 0; + s = (m3_dma_t*) substream->runtime->private_data; + snd_pcm_lib_free_pages(substream); + s->buffer_addr = 0; + return 0; +} + +static int +snd_m3_pcm_prepare(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + m3_dma_t *s = (m3_dma_t*)runtime->private_data; + unsigned long flags; + + snd_assert(s != NULL, return -ENXIO); + + if (runtime->format != SNDRV_PCM_FORMAT_U8 && + runtime->format != SNDRV_PCM_FORMAT_S16_LE) + return -EINVAL; + if (runtime->rate > 48000 || + runtime->rate < 8000) + return -EINVAL; + + spin_lock_irqsave(&chip->reg_lock, flags); + + snd_m3_pcm_setup1(chip, s, subs); + + if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_m3_playback_setup(chip, s, subs); + else + snd_m3_capture_setup(chip, s, subs); + + snd_m3_pcm_setup2(chip, s, runtime); + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + +/* + * get current pointer + */ +static unsigned int +snd_m3_get_pointer(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + u16 hi = 0, lo = 0; + int retry = 10; + u32 addr; + + /* + * try and get a valid answer + */ + while (retry--) { + hi = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTH); + + lo = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTL); + + if (hi == snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTH)) + break; + } + addr = lo | ((u32)hi<<16); + return (unsigned int)(addr - s->buffer_addr); +} + +static snd_pcm_uframes_t +snd_m3_pcm_pointer(snd_pcm_substream_t * subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data; + snd_assert(s != NULL, return 0); + return bytes_to_frames(subs->runtime, snd_m3_get_pointer(chip, s, subs)); +} + + +/* update pointer */ +/* spinlock held! */ +static void snd_m3_update_ptr(m3_t *chip, m3_dma_t *s) +{ + snd_pcm_substream_t *subs = s->substream; + unsigned int hwptr; + int diff; + + if (! s->running) + return; + + hwptr = snd_m3_get_pointer(chip, s, subs) % s->dma_size; + diff = (s->dma_size + hwptr - s->hwptr) % s->dma_size; + s->hwptr = hwptr; + s->count += diff; + while (s->count >= (signed)s->period_size) { + s->count -= s->period_size; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(subs); + spin_lock(&chip->reg_lock); + } +} + +static void +snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + m3_t *chip = snd_magic_cast(m3_t, dev_id, ); + u8 status; + int i; + + status = inb(chip->iobase + 0x1A); + + if (status == 0xff) + return; + + /* presumably acking the ints? */ + outw(status, chip->iobase + 0x1A); + + /*if (in_suspend) + return;*/ + + /* + * ack an assp int if its running + * and has an int pending + */ + if (status & ASSP_INT_PENDING) { + u8 ctl = inb(chip->iobase + ASSP_CONTROL_B); + if (!(ctl & STOP_ASSP_CLOCK)) { + ctl = inb(chip->iobase + ASSP_HOST_INT_STATUS); + if (ctl & DSP2HOST_REQ_TIMER) { + outb(DSP2HOST_REQ_TIMER, chip->iobase + ASSP_HOST_INT_STATUS); + /* update adc/dac info if it was a timer int */ + spin_lock(&chip->reg_lock); + for (i = 0; i < chip->num_substreams; i++) { + m3_dma_t *s = &chip->substreams[i]; + if (s->running) + snd_m3_update_ptr(chip, s); + } + spin_unlock(&chip->reg_lock); + } + } + } + + /* XXX is this needed? */ + if (status & 0x40) + outb(0x40, chip->iobase+0x1A); +} + + +/* + */ + +static snd_pcm_hardware_t snd_m3_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (512*1024), + period_bytes_min: 64, + period_bytes_max: (512*1024), + periods_min: 1, + periods_max: 1024, +}; + +static snd_pcm_hardware_t snd_m3_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (512*1024), + period_bytes_min: 64, + period_bytes_max: (512*1024), + periods_min: 1, + periods_max: 1024, +}; + + +/* + */ + +static int +snd_m3_substream_open(m3_t *chip, snd_pcm_substream_t *subs) +{ + int i; + m3_dma_t *s; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < chip->num_substreams; i++) { + s = &chip->substreams[i]; + if (! s->opened) + goto __found; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENOMEM; +__found: + s->opened = 1; + s->running = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + subs->runtime->private_data = s; + s->substream = subs; + + /* set list owners */ + if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) { + s->index_list[0] = &chip->mixer_list; + } else + s->index_list[0] = &chip->adc1_list; + s->index_list[1] = &chip->msrc_list; + s->index_list[2] = &chip->dma_list; + + return 0; +} + +static void +snd_m3_substream_close(m3_t *chip, snd_pcm_substream_t *subs) +{ + m3_dma_t *s = (m3_dma_t*) subs->runtime->private_data; + unsigned long flags; + + if (s == NULL) + return; /* not opened properly */ + + spin_lock_irqsave(&chip->reg_lock, flags); + if (s->substream && s->running) + snd_m3_pcm_stop(chip, s, s->substream); /* does this happen? */ + if (s->in_lists) { + snd_m3_remove_list(chip, s->index_list[0], s->index[0]); + snd_m3_remove_list(chip, s->index_list[1], s->index[1]); + snd_m3_remove_list(chip, s->index_list[2], s->index[2]); + s->in_lists = 0; + } + s->running = 0; + s->opened = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static int +snd_m3_playback_open(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + int err; + + if ((err = snd_m3_substream_open(chip, subs)) < 0) + return err; + + runtime->hw = snd_m3_playback; + snd_pcm_set_sync(subs); + + return 0; +} + +static int +snd_m3_playback_close(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + + snd_m3_substream_close(chip, subs); + return 0; +} + +static int +snd_m3_capture_open(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + int err; + + if ((err = snd_m3_substream_open(chip, subs)) < 0) + return err; + + runtime->hw = snd_m3_capture; + snd_pcm_set_sync(subs); + + return 0; +} + +static int +snd_m3_capture_close(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + + snd_m3_substream_close(chip, subs); + return 0; +} + +/* + * create pcm instance + */ + +static snd_pcm_ops_t snd_m3_playback_ops = { + open: snd_m3_playback_open, + close: snd_m3_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_m3_pcm_hw_params, + hw_free: snd_m3_pcm_hw_free, + prepare: snd_m3_pcm_prepare, + trigger: snd_m3_pcm_trigger, + pointer: snd_m3_pcm_pointer, +}; + +static snd_pcm_ops_t snd_m3_capture_ops = { + open: snd_m3_capture_open, + close: snd_m3_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_m3_pcm_hw_params, + hw_free: snd_m3_pcm_hw_free, + prepare: snd_m3_pcm_prepare, + trigger: snd_m3_pcm_trigger, + pointer: snd_m3_pcm_pointer, +}; + +static int __devinit +snd_m3_pcm(m3_t * chip, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(chip->card, chip->card->driver, device, + MAX_PLAYBACKS, MAX_CAPTURES, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_m3_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_m3_capture_ops); + + pcm->private_data = chip; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->driver); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + + return 0; +} + + +/* + * ac97 interface + */ + +/* + * Wait for the ac97 serial bus to be free. + * return nonzero if the bus is still busy. + */ +static int snd_m3_ac97_wait(m3_t *chip) +{ + int i = 10000; + + do { + if (! (snd_m3_inb(chip, 0x30) & 1)) + return 0; + } while (i-- > 0); + + snd_printk("ac97 serial bus busy\n"); + return 1; +} + +static unsigned short +snd_m3_ac97_read(ac97_t *ac97, unsigned short reg) +{ + m3_t *chip = snd_magic_cast(m3_t, ac97->private_data, return -ENXIO); + unsigned short ret = 0; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (snd_m3_ac97_wait(chip)) + goto __error; + snd_m3_outb(chip, 0x80 | (reg & 0x7f), 0x30); + if (snd_m3_ac97_wait(chip)) + goto __error; + ret = snd_m3_inw(chip, 0x32); +__error: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return ret; +} + +static void +snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) +{ + m3_t *chip = snd_magic_cast(m3_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (snd_m3_ac97_wait(chip)) + goto __error; + snd_m3_outw(chip, val, 0x32); + snd_m3_outb(chip, reg & 0x7f, 0x30); +__error: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + + +static void snd_m3_remote_codec_config(int io, int isremote) +{ + isremote = isremote ? 1 : 0; + + outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote, + io + RING_BUS_CTRL_B); + outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote, + io + SDO_OUT_DEST_CTRL); + outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote, + io + SDO_IN_DEST_CTRL); +} + +/* + * hack, returns non zero on err + */ +static int snd_m3_try_read_vendor(m3_t *chip) +{ + u16 ret; + + if (snd_m3_ac97_wait(chip)) + return 1; + + snd_m3_outb(chip, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30); + + if (snd_m3_ac97_wait(chip)) + return 1; + + ret = snd_m3_inw(chip, 0x32); + + return (ret == 0) || (ret == 0xffff); +} + +static void snd_m3_ac97_reset(m3_t *chip, int busywait) +{ + u16 dir; + int delay1 = 0, delay2 = 0, i; + int io = chip->iobase; + + if (chip->allegro_flag) { + /* + * the onboard codec on the allegro seems + * to want to wait a very long time before + * coming back to life + */ + delay1 = 50; + delay2 = 800; + } else { + /* maestro3 */ + delay1 = 20; + delay2 = 500; + } + + for (i = 0; i < 5; i++) { + dir = inw(io + GPIO_DIRECTION); + if (chip->subsystem_vendor == 0x1028 && + chip->subsystem_device == 0x00b0) /* Dell Inspiron 4000 */ + ; /* seems conflicting with IrDA */ + else + dir |= 0x10; /* assuming pci bus master? */ + + snd_m3_remote_codec_config(io, 0); + + outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A); + udelay(20); + + outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION); + outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK); + outw(0, io + GPIO_DATA); + outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION); + + if (busywait) { + mdelay(delay1); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay1 * HZ) / 1000); + } + + outw(GPO_PRIMARY_AC97, io + GPIO_DATA); + udelay(5); + /* ok, bring back the ac-link */ + outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A); + outw(~0, io + GPIO_MASK); + + if (busywait) { + mdelay(delay2); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay2 * HZ) / 1000); + } + if (! snd_m3_try_read_vendor(chip)) + break; + + delay1 += 10; + delay2 += 100; + + snd_printd("maestro3: retrying codec reset with delays of %d and %d ms\n", + delay1, delay2); + } + +#if 0 + /* more gung-ho reset that doesn't + * seem to work anywhere :) + */ + tmp = inw(io + RING_BUS_CTRL_A); + outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A); + mdelay(20); + outw(tmp, io + RING_BUS_CTRL_A); + mdelay(50); +#endif +} + +static int snd_m3_mixer(m3_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_m3_ac97_write; + ac97.read = snd_m3_ac97_read; + ac97.private_data = chip; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + /* seems ac97 PCM needs initialization.. hack hack.. */ + snd_ac97_write(chip->ac97, AC97_PCM, 0x8000 | (15 << 8) | 15); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 10); + snd_ac97_write(chip->ac97, AC97_PCM, 0); + + return 0; +} + + +/* + * DSP Code images + */ + +static u16 assp_kernel_image[] = { + 0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, + 0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, + 0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, + 0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, + 0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, + 0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, + 0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, + 0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, + 0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, + 0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, + 0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, + 0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, + 0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, + 0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, + 0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, + 0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, + 0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, + 0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, + 0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, + 0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, + 0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, + 0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, + 0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, + 0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, + 0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, + 0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, + 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, + 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, + 0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, + 0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, + 0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, + 0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, + 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, + 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, + 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, + 0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, + 0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, + 0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, + 0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, + 0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, + 0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, + 0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, + 0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, + 0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, + 0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, + 0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, + 0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, + 0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, + 0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, + 0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, + 0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, + 0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, + 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, + 0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, + 0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, + 0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, + 0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, + 0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, + 0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, + 0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, + 0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, + 0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, + 0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, + 0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, + 0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, + 0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, + 0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, + 0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, + 0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, + 0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, + 0xBE3A, +}; + +/* + * Mini sample rate converter code image + * that is to be loaded at 0x400 on the DSP. + */ +static u16 assp_minisrc_image[] = { + + 0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, + 0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, + 0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, + 0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, + 0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, + 0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, + 0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, + 0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, + 0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, + 0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, + 0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, + 0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, + 0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, + 0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, + 0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, + 0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, + 0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, + 0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, + 0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, + 0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, + 0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, + 0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, + 0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, + 0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, + 0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, + 0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, + 0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, + 0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, + 0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, + 0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, + 0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, + 0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + + +/* + * initialize ASSP + */ + +#define MINISRC_LPF_LEN 10 +static u16 minisrc_lpf[MINISRC_LPF_LEN] = { + 0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C, + 0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F +}; + +static void snd_m3_assp_init(m3_t *chip) +{ + int i; + + /* zero kernel data */ + for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR + i, 0); + + /* zero mixer data? */ + for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR2 + i, 0); + + /* init dma pointer */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_CURRENT_DMA, + KDATA_DMA_XFER0); + + /* write kernel into code memory.. */ + for (i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + REV_B_CODE_MEMORY_BEGIN + i, + assp_kernel_image[i]); + } + + /* + * We only have this one client and we know that 0x400 + * is free in our kernel's mem map, so lets just + * drop it there. It seems that the minisrc doesn't + * need vectors, so we won't bother with them.. + */ + for (i = 0; i < sizeof(assp_minisrc_image) / 2; i++) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + 0x400 + i, + assp_minisrc_image[i]); + } + + /* + * write the coefficients for the low pass filter? + */ + for (i = 0; i < MINISRC_LPF_LEN ; i++) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + i, + minisrc_lpf[i]); + } + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN, + 0x8000); + + /* + * the minisrc is the only thing on + * our task list.. + */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TASK0, + 0x400); + + /* + * init the mixer number.. + */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER,0); + + /* + * EXTREME KERNEL MASTER VOLUME + */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_LEFT_VOLUME, ARB_VOLUME); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME); + + chip->mixer_list.curlen = 0; + chip->mixer_list.mem_addr = KDATA_MIXER_XFER0; + chip->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS; + chip->adc1_list.curlen = 0; + chip->adc1_list.mem_addr = KDATA_ADC1_XFER0; + chip->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS; + chip->dma_list.curlen = 0; + chip->dma_list.mem_addr = KDATA_DMA_XFER0; + chip->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS; + chip->msrc_list.curlen = 0; + chip->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC; + chip->msrc_list.max = MAX_INSTANCE_MINISRC; +} + + +static int snd_m3_assp_client_init(m3_t *chip, m3_dma_t *s, int index) +{ + int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + + MINISRC_IN_BUFFER_SIZE / 2 + + 1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 ); + int address, i; + + /* + * the revb memory map has 0x1100 through 0x1c00 + * free. + */ + + /* + * align instance address to 256 bytes so that it's + * shifted list address is aligned. + * list address = (mem address >> 1) >> 7; + */ + data_bytes = (data_bytes + 255) & ~255; + address = 0x1100 + ((data_bytes/2) * index); + + if ((address + (data_bytes/2)) >= 0x1c00) { + snd_printk("no memory for %d bytes at ind %d (addr 0x%x)\n", + data_bytes, index, address); + return -ENOMEM; + } + + s->number = index; + s->inst.code = 0x400; + s->inst.data = address; + + for (i = data_bytes / 2; i > 0; address++, i--) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + address, 0); + } + + return 0; +} + + +/* + * this works for the reference board, have to find + * out about others + * + * this needs more magic for 4 speaker, but.. + */ +static void +snd_m3_amp_enable(m3_t *chip, int enable) +{ + int io = chip->iobase; + u16 gpo, polarity; + + if (! chip->external_amp) + return; + + polarity = enable ? 0 : 1; + polarity = polarity << chip->amp_gpio; + gpo = 1 << chip->amp_gpio; + + outw(~gpo, io + GPIO_MASK); + + outw(inw(io + GPIO_DIRECTION) | gpo, + io + GPIO_DIRECTION); + + outw((GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity), + io + GPIO_DATA); + + outw(0xffff, io + GPIO_MASK); +} + +static int +snd_m3_chip_init(m3_t *chip) +{ + struct pci_dev *pcidev = chip->pci; + u32 n; + u8 t; /* makes as much sense as 'n', no? */ + + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= REDUCED_DEBOUNCE; + n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + outb(RESET_ASSP, chip->iobase + ASSP_CONTROL_B); + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= ~INT_CLK_SELECT; + if (!chip->allegro_flag) { + n &= ~INT_CLK_MULT_ENABLE; + n |= INT_CLK_SRC_NOT_PCI; + } + n &= ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 ); + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + if (chip->allegro_flag) { + pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n); + n |= IN_CLK_12MHZ_SELECT; + pci_write_config_dword(pcidev, PCI_USER_CONFIG, n); + } + + t = inb(chip->iobase + ASSP_CONTROL_A); + t &= ~( DSP_CLK_36MHZ_SELECT | ASSP_CLK_49MHZ_SELECT); + t |= ASSP_CLK_49MHZ_SELECT; + t |= ASSP_0_WS_ENABLE; + outb(t, chip->iobase + ASSP_CONTROL_A); + + outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B); + + return 0; +} + +static void +snd_m3_enable_ints(m3_t *chip) +{ + unsigned long io = chip->iobase; + + outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL); + outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, + io + ASSP_CONTROL_C); +} + + +/* + */ + +static int snd_m3_free(m3_t *chip) +{ + unsigned long flags; + m3_dma_t *s; + int i; + + if (chip->substreams) { + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < chip->num_substreams; i++) { + s = &chip->substreams[i]; + /* check surviving pcms; this should not happen though.. */ + if (s->substream && s->running) + snd_m3_pcm_stop(chip, s, s->substream); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + kfree(chip->substreams); + } +#ifdef CONFIG_PM + if (chip->suspend_mem) + vfree(chip->suspend_mem); +#endif + + synchronize_irq(); + + if (chip->iobase_res) { + release_resource(chip->iobase_res); + kfree_nocheck(chip->iobase_res); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + snd_magic_kfree(chip); + return 0; +} + + +/* + * APM support + */ +#ifdef CONFIG_PM + +static void m3_suspend(m3_t *chip) +{ + snd_card_t *card = chip->card; + int i, index; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + snd_pcm_suspend_all(chip->pcm); + + mdelay(10); /* give the assp a chance to idle.. */ + + snd_m3_assp_halt(chip); + + /* save dsp image */ + index = 0; + for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) + chip->suspend_mem[index++] = + snd_m3_assp_read(chip, MEMTYPE_INTERNAL_CODE, i); + for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + chip->suspend_mem[index++] = + snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i); + + /* power down apci registers */ + snd_m3_outw(chip, 0xffff, 0x54); + snd_m3_outw(chip, 0xffff, 0x56); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void m3_resume(m3_t *chip) +{ + snd_card_t *card = chip->card; + int i, index; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + /* first lets just bring everything back. .*/ + snd_m3_outw(chip, 0, 0x54); + snd_m3_outw(chip, 0, 0x56); + + snd_m3_chip_init(chip); + snd_m3_assp_halt(chip); + snd_m3_ac97_reset(chip, 1); + + /* restore dsp image */ + index = 0; + for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, i, + chip->suspend_mem[index++]); + for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, i, + chip->suspend_mem[index++]); + + /* tell the dma engine to restart itself */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_DMA_ACTIVE, 0); + + /* restore ac97 registers */ + snd_ac97_resume(chip->ac97); + + snd_m3_assp_continue(chip); + snd_m3_enable_ints(chip); + snd_m3_amp_enable(chip, 1); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_m3_suspend(struct pci_dev *pci, u32 state) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return -ENXIO); + m3_suspend(chip); + return 0; +} +static int snd_m3_resume(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return -ENXIO); + m3_resume(chip); + return 0; +} +#else +static void snd_m3_suspend(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return); + m3_suspend(chip); +} +static void snd_m3_resume(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return); + m3_resume(chip); +} +#endif + +/* callback */ +static int snd_m3_set_power_state(snd_card_t *card, unsigned int power_state) +{ + m3_t *chip = snd_magic_cast(m3_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + m3_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + m3_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + + +/* + */ + +static int snd_m3_dev_free(snd_device_t *device) +{ + m3_t *chip = snd_magic_cast(m3_t, device->device_data, return -ENXIO); + return snd_m3_free(chip); +} + +static int __devinit +snd_m3_create(snd_card_t *card, struct pci_dev *pci, + int enable_amp, + int amp_gpio, + m3_t **chip_ret) +{ + m3_t *chip; + int i, err; + static snd_device_ops_t ops = { + dev_free: snd_m3_dev_free, + }; + + *chip_ret = NULL; + + if (pci_enable_device(pci)) + return -EIO; + + /* check, if we can restrict PCI DMA transfers to 28 bits */ + if (!pci_dma_supported(pci, 0x0fffffff)) { + snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + chip = snd_magic_kcalloc(m3_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + spin_lock_init(&chip->reg_lock); + switch (pci->device) { + case PCI_DEVICE_ID_ESS_ALLEGRO: + case PCI_DEVICE_ID_ESS_ALLEGRO_1: + case PCI_DEVICE_ID_ESS_CANYON3D_2LE: + case PCI_DEVICE_ID_ESS_CANYON3D_2: + chip->allegro_flag = 1; + break; + } + +#ifndef LINUX_2_2 + chip->subsystem_vendor = pci->subsystem_vendor; + chip->subsystem_device = pci->subsystem_device; +#else + pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->subsystem_vendor); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->subsystem_device); +#endif + + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->external_amp = enable_amp; + if (amp_gpio >= 0 && amp_gpio <= 0x0f) + chip->amp_gpio = amp_gpio; + else if (chip->allegro_flag) { + /* panasonic CF-28 "toughbook" has different GPIO connection.. */ + if (chip->subsystem_vendor == 0x10f7 && + chip->subsystem_device == 0x833e) + chip->amp_gpio = 0x0d; + else + chip->amp_gpio = GPO_EXT_AMP_ALLEGRO; + } else + chip->amp_gpio = GPO_EXT_AMP_M3; /* presumably this is for all 'maestro3's.. */ + chip->num_substreams = NR_DSPS; + chip->substreams = kmalloc(sizeof(m3_dma_t) * chip->num_substreams, GFP_KERNEL); + if (chip->substreams == NULL) { + snd_magic_kfree(chip); + return -ENOMEM; + } + memset(chip->substreams, 0, sizeof(m3_dma_t) * chip->num_substreams); + + chip->iobase = pci_resource_start(pci, 0); + if ((chip->iobase_res = request_region(chip->iobase, 256, + card->driver)) == NULL) { + snd_m3_free(chip); + snd_printk("unable to grab i/o ports %ld\n", chip->iobase); + return -EBUSY; + } + + /* just to be sure */ + pci_set_master(pci); + + snd_m3_chip_init(chip); + snd_m3_assp_halt(chip); + + snd_m3_ac97_reset(chip, 0); + + if ((err = snd_m3_mixer(chip)) < 0) { + snd_m3_free(chip); + return err; + } + + snd_m3_assp_init(chip); + snd_m3_amp_enable(chip, 1); + + for (i = 0; i < chip->num_substreams; i++) { + m3_dma_t *s = &chip->substreams[i]; + s->chip = chip; + if ((err = snd_m3_assp_client_init(chip, s, i)) < 0) { + snd_m3_free(chip); + return err; + } + } + + if ((err = snd_m3_pcm(chip, 0)) < 0) { + snd_m3_free(chip); + return err; + } + + if (request_irq(pci->irq, snd_m3_interrupt, SA_INTERRUPT|SA_SHIRQ, + card->driver, (void *)chip)) { + snd_m3_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -ENOMEM; + } + chip->irq = pci->irq; + +#ifdef CONFIG_PM + chip->suspend_mem = vmalloc(sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH)); + if (chip->suspend_mem == NULL) + snd_printk("can't allocate apm buffer\n"); + card->set_power_state = snd_m3_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_m3_free(chip); + return err; + } + + snd_m3_enable_ints(chip); + snd_m3_assp_continue(chip); + + *chip_ret = chip; + + return 0; +} + +/* + */ +static int __devinit +snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + m3_t *chip; + int err; + + /* don't pick up modems */ + if (((pci->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) + return 0; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (pci->device) { + case PCI_DEVICE_ID_ESS_ALLEGRO: + case PCI_DEVICE_ID_ESS_ALLEGRO_1: + strcpy(card->driver, "Allegro"); + break; + case PCI_DEVICE_ID_ESS_CANYON3D_2LE: + case PCI_DEVICE_ID_ESS_CANYON3D_2: + strcpy(card->driver, "Canyon3D-2"); + break; + default: + strcpy(card->driver, "Maestro3"); + break; + } + + if ((err = snd_m3_create(card, pci, + snd_external_amp[dev], + snd_amp_gpio[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->shortname, "ESS %s PCI", card->driver); + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, chip->iobase, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_m3_remove(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "Maestro3", + id_table: snd_m3_ids, + probe: snd_m3_probe, + remove: __devexit_p(snd_m3_remove), +#ifdef CONFIG_PM + suspend: snd_m3_suspend, + resume: snd_m3_resume, +#endif +}; + +static int __init alsa_card_m3_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("Maestro3/Allegro soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_m3_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_m3_init) +module_exit(alsa_card_m3_exit) + +#ifndef MODULE + +/* format is: snd-maestro3=snd_enable,snd_index,snd_id,snd_external_amp,snd_amp_gpio */ + +static int __init alsa_card_maestro3_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_external_amp[nr_dev]) == 2 && + get_option(&str,&snd_amp_gpio[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-maestro3=", alsa_card_maestro3_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/nm256/Makefile b/sound/pci/nm256/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/nm256/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _nm256.o + +list-multi := snd-nm256.o + +snd-nm256-objs := nm256.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_NM256) += snd-nm256.o + +include $(TOPDIR)/Rules.make + +snd-nm256.o: $(snd-nm256-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-nm256-objs) diff -Nru a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/nm256/nm256.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1696 @@ +/* + * Driver for NeoMagic 256AV and 256ZX chipsets. + * Copyright (c) 2000 by Takashi Iwai + * + * Based on nm256_audio.c OSS driver in linux kernel. + * The original author of OSS nm256 driver wishes to remain anonymous, + * so I just put my acknoledgment to him/her here. + * The original author's web page is found at + * http://www.uglx.org/sony.html + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define CARD_NAME "NeoMagic 256AV/ZX" +#define DRIVER_NAME "NM256" + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("NeoMagic NM256AV/ZX"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{NeoMagic,NM256AV}," + "{NeoMagic,NM256ZX}}"); + +/* + * some compile conditions. + */ + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static int snd_playback_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 16}; +static int snd_capture_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 16}; +static int snd_force_ac97[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled as default */ +static int snd_buffer_top[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* not specified */ +static int snd_use_cache[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled */ +static int snd_vaio_hack[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable this soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_playback_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_playback_bufsize, SNDRV_ENABLED); +MODULE_PARM(snd_capture_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_capture_bufsize, SNDRV_ENABLED); +MODULE_PARM(snd_force_ac97, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_force_ac97, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(snd_buffer_top, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_buffer_top, SNDRV_ENABLED); +MODULE_PARM(snd_use_cache, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_use_cache, "Enable the cache for coefficient table access."); +MODULE_PARM_SYNTAX(snd_use_cache, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(snd_vaio_hack, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_vaio_hack, "Enable workaround for Sony VAIO notebooks."); +MODULE_PARM_SYNTAX(snd_vaio_hack, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); + +/* + * hw definitions + */ + +/* The BIOS signature. */ +#define NM_SIGNATURE 0x4e4d0000 +/* Signature mask. */ +#define NM_SIG_MASK 0xffff0000 + +/* Size of the second memory area. */ +#define NM_PORT2_SIZE 4096 + +/* The base offset of the mixer in the second memory area. */ +#define NM_MIXER_OFFSET 0x600 + +/* The maximum size of a coefficient entry. */ +#define NM_MAX_PLAYBACK_COEF_SIZE 0x5000 +#define NM_MAX_RECORD_COEF_SIZE 0x1260 + +/* The interrupt register. */ +#define NM_INT_REG 0xa04 +/* And its bits. */ +#define NM_PLAYBACK_INT 0x40 +#define NM_RECORD_INT 0x100 +#define NM_MISC_INT_1 0x4000 +#define NM_MISC_INT_2 0x1 +#define NM_ACK_INT(chip, X) snd_nm256_writew(chip, NM_INT_REG, (X) << 1) + +/* The AV's "mixer ready" status bit and location. */ +#define NM_MIXER_STATUS_OFFSET 0xa04 +#define NM_MIXER_READY_MASK 0x0800 +#define NM_MIXER_PRESENCE 0xa06 +#define NM_PRESENCE_MASK 0x0050 +#define NM_PRESENCE_VALUE 0x0040 + +/* + * For the ZX. It uses the same interrupt register, but it holds 32 + * bits instead of 16. + */ +#define NM2_PLAYBACK_INT 0x10000 +#define NM2_RECORD_INT 0x80000 +#define NM2_MISC_INT_1 0x8 +#define NM2_MISC_INT_2 0x2 +#define NM2_ACK_INT(chip, X) snd_nm256_writel(chip, NM_INT_REG, (X)) + +/* The ZX's "mixer ready" status bit and location. */ +#define NM2_MIXER_STATUS_OFFSET 0xa06 +#define NM2_MIXER_READY_MASK 0x0800 + +/* The playback registers start from here. */ +#define NM_PLAYBACK_REG_OFFSET 0x0 +/* The record registers start from here. */ +#define NM_RECORD_REG_OFFSET 0x200 + +/* The rate register is located 2 bytes from the start of the register area. */ +#define NM_RATE_REG_OFFSET 2 + +/* Mono/stereo flag, number of bits on playback, and rate mask. */ +#define NM_RATE_STEREO 1 +#define NM_RATE_BITS_16 2 +#define NM_RATE_MASK 0xf0 + +/* Playback enable register. */ +#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1) +#define NM_PLAYBACK_ENABLE_FLAG 1 +#define NM_PLAYBACK_ONESHOT 2 +#define NM_PLAYBACK_FREERUN 4 + +/* Mutes the audio output. */ +#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18) +#define NM_AUDIO_MUTE_LEFT 0x8000 +#define NM_AUDIO_MUTE_RIGHT 0x0080 + +/* Recording enable register. */ +#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0) +#define NM_RECORD_ENABLE_FLAG 1 +#define NM_RECORD_FREERUN 2 + +/* coefficient buffer pointer */ +#define NM_COEFF_START_OFFSET 0x1c +#define NM_COEFF_END_OFFSET 0x20 + +/* DMA buffer offsets */ +#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4) +#define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10) +#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc) +#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8) + +#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4) +#define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14) +#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc) +#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8) + +/* + * type definitions + */ + +typedef struct snd_nm256 nm256_t; +typedef struct snd_nm256_stream nm256_stream_t; +#define chip_t nm256_t + +struct snd_nm256_stream { + + nm256_t *chip; + snd_pcm_substream_t *substream; + int running; + + u32 buf; /* offset from chip->buffer */ + int bufsize; /* buffer size in bytes */ + unsigned long bufptr; /* mapped pointer */ + unsigned long bufptr_addr; /* physical address of the mapped pointer */ + + int dma_size; /* buffer size of the substream in bytes */ + int period_size; /* period size in bytes */ + int periods; /* # of periods */ + int shift; /* bit shifts */ + int cur_period; /* current period # */ + +}; + +struct snd_nm256 { + + snd_card_t *card; + + unsigned long cport; /* control port */ + struct resource *res_cport; /* its resource */ + unsigned long cport_addr; /* physical address */ + + unsigned long buffer; /* buffer */ + struct resource *res_buffer; /* its resource */ + unsigned long buffer_addr; /* buffer phyiscal address */ + + u32 buffer_start; /* start offset from pci resource 0 */ + u32 buffer_end; /* end offset */ + u32 buffer_size; /* total buffer size */ + + u32 all_coeff_buf; /* coefficient buffer */ + u32 coeff_buf[2]; /* coefficient buffer for each stream */ + + int coeffs_current; /* coeff. table is loaded? */ + int use_cache; /* use one big coef. table */ + + int mixer_base; /* register offset of ac97 mixer */ + int mixer_status_offset; /* offset of mixer status reg. */ + int mixer_status_mask; /* bit mask to test the mixer status */ + + int irq; + void (*interrupt)(int, void *, struct pt_regs *); + int badintrcount; /* counter to check bogus interrupts */ + + nm256_stream_t streams[2]; + + ac97_t *ac97; + + snd_pcm_t *pcm; + + struct pci_dev *pci; + + spinlock_t reg_lock; + +}; + + +/* + * include coefficient table + */ +#include "nm256_coef.c" + + +/* + * PCI ids + */ + +#ifndef PCI_VENDOR_ID_NEOMAGIC +#define PCI_VENDOR_ID_NEOMEGIC 0x10c8 +#endif +#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO +#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 +#endif +#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO +#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 +#endif + + +static struct pci_device_id snd_nm256_ids[] __devinitdata = { + {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, snd_nm256_ids); + + +/* + * lowlvel stuffs + */ + +inline static u8 +snd_nm256_readb(nm256_t *chip, int offset) +{ + return readb(chip->cport + offset); +} + +inline static u16 +snd_nm256_readw(nm256_t *chip, int offset) +{ + return readw(chip->cport + offset); +} + +inline static u32 +snd_nm256_readl(nm256_t *chip, int offset) +{ + return readl(chip->cport + offset); +} + +inline static void +snd_nm256_writeb(nm256_t *chip, int offset, u8 val) +{ + writeb(val, chip->cport + offset); +} + +inline static void +snd_nm256_writew(nm256_t *chip, int offset, u16 val) +{ + writew(val, chip->cport + offset); +} + +inline static void +snd_nm256_writel(nm256_t *chip, int offset, u32 val) +{ + writel(val, chip->cport + offset); +} + +inline static void +snd_nm256_write_buffer(nm256_t *chip, void *src, int offset, int size) +{ + offset -= chip->buffer_start; +#ifdef SNDRV_CONFIG_DEBUG + if (offset < 0 || offset >= chip->buffer_size) { + printk("nm256: write_buffer invalid offset = %d size = %d\n", offset, size); + return; + } +#endif + memcpy_toio(chip->buffer + offset, src, size); +} + +/* + * coefficient handlers -- what a magic! + */ + +static u16 +snd_nm256_get_start_offset(int which) +{ + u16 offset = 0; + while (which-- > 0) + offset += coefficient_sizes[which]; + return offset; +} + +static void +snd_nm256_load_one_coefficient(nm256_t *chip, int stream, u32 port, int which) +{ + u32 coeff_buf = chip->coeff_buf[stream]; + u16 offset = snd_nm256_get_start_offset(which); + u16 size = coefficient_sizes[which]; + + snd_nm256_write_buffer(chip, coefficients + offset, coeff_buf, size); + snd_nm256_writel(chip, port, coeff_buf); + /* ??? Record seems to behave differently than playback. */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + size--; + snd_nm256_writel(chip, port + 4, coeff_buf + size); +} + +static void +snd_nm256_load_coefficient(nm256_t *chip, int stream, int number) +{ + /* The enable register for the specified engine. */ + u32 poffset = (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_ENABLE_REG : NM_PLAYBACK_ENABLE_REG); + u32 addr = NM_COEFF_START_OFFSET; + + addr += (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET); + + if (snd_nm256_readb(chip, poffset) & 1) { + snd_printd("NM256: Engine was enabled while loading coefficients!\n"); + return; + } + + /* The recording engine uses coefficient values 8-15. */ + number &= 7; + if (stream == SNDRV_PCM_STREAM_CAPTURE) + number += 8; + + if (! chip->use_cache) { + snd_nm256_load_one_coefficient(chip, stream, addr, number); + return; + } + if (! chip->coeffs_current) { + snd_nm256_write_buffer(chip, coefficients, chip->all_coeff_buf, + NM_TOTAL_COEFF_COUNT * 4); + chip->coeffs_current = 1; + } else { + u32 base = chip->all_coeff_buf; + u32 offset = snd_nm256_get_start_offset(number); + u32 end_offset = offset + coefficient_sizes[number]; + snd_nm256_writel(chip, addr, base + offset); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + end_offset--; + snd_nm256_writel(chip, addr + 4, base + end_offset); + } +} + + +/* The actual rates supported by the card. */ +static int samplerates[8] = { + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, +}; +#define NUM_SAMPLERATES (sizeof(samplerates) / sizeof(samplerates[0])) +static snd_pcm_hw_constraint_list_t constraints_rates = { + count: NUM_SAMPLERATES, + list: samplerates, + mask: 0, +}; + +/* + * return the index of the target rate + */ +static int +snd_nm256_fixed_rate(int rate) +{ + int i; + for (i = 0; i < NUM_SAMPLERATES; i++) { + if (rate == samplerates[i]) + return i; + } + snd_BUG(); + return 0; +} + +/* + * set sample rate and format + */ +static void +snd_nm256_set_format(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int rate_index = snd_nm256_fixed_rate(runtime->rate); + unsigned char ratebits = (rate_index << 4) & NM_RATE_MASK; + + s->shift = 0; + if (snd_pcm_format_width(runtime->format) == 16) { + ratebits |= NM_RATE_BITS_16; + s->shift++; + } + if (runtime->channels > 1) { + ratebits |= NM_RATE_STEREO; + s->shift++; + } + + runtime->rate = samplerates[rate_index]; + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + snd_nm256_load_coefficient(chip, 0, rate_index); /* 0 = playback */ + snd_nm256_writeb(chip, + NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); + break; + case SNDRV_PCM_STREAM_CAPTURE: + snd_nm256_load_coefficient(chip, 1, rate_index); /* 1 = record */ + snd_nm256_writeb(chip, + NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); + break; + } +} + +/* + * start / stop + */ + +/* update the watermark (current period) */ +static void snd_nm256_pcm_mark(nm256_t *chip, nm256_stream_t *s, int reg) +{ + s->cur_period++; + s->cur_period %= s->periods; + snd_nm256_writel(chip, reg, s->buf + s->cur_period * s->period_size); +} + +#define snd_nm256_playback_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_PBUFFER_WMARK) +#define snd_nm256_capture_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_RBUFFER_WMARK) + +static void +snd_nm256_playback_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) +{ + /* program buffer pointers */ + snd_nm256_writel(chip, NM_PBUFFER_START, s->buf); + snd_nm256_writel(chip, NM_PBUFFER_END, s->buf + s->dma_size - (1 << s->shift)); + snd_nm256_writel(chip, NM_PBUFFER_CURRP, s->buf); + snd_nm256_playback_mark(chip, s); + + /* Enable playback engine and interrupts. */ + snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, + NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); + /* Enable both channels. */ + snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 0x0); +} + +static void +snd_nm256_capture_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) +{ + /* program buffer pointers */ + snd_nm256_writel(chip, NM_RBUFFER_START, s->buf); + snd_nm256_writel(chip, NM_RBUFFER_END, s->buf + s->dma_size); + snd_nm256_writel(chip, NM_RBUFFER_CURRP, s->buf); + snd_nm256_capture_mark(chip, s); + + /* Enable playback engine and interrupts. */ + snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, + NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN); +} + +/* Stop the play engine. */ +static void +snd_nm256_playback_stop(nm256_t *chip) +{ + /* Shut off sound from both channels. */ + snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, + NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); + /* Disable play engine. */ + snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 0); +} + +static void +snd_nm256_capture_stop(nm256_t *chip) +{ + /* Disable recording engine. */ + snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 0); +} + +static int +snd_nm256_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long flags; + int err = 0; + + snd_assert(s != NULL, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (! s->running) { + snd_nm256_playback_start(chip, s, substream); + s->running = 1; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (s->running) { + snd_nm256_playback_stop(chip); + s->running = 0; + } + break; + default: + err = -EINVAL; + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return err; +} + +static int +snd_nm256_capture_trigger(snd_pcm_substream_t *substream, int cmd) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long flags; + int err = 0; + + snd_assert(s != NULL, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (! s->running) { + snd_nm256_capture_start(chip, s, substream); + s->running = 1; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (s->running) { + snd_nm256_capture_stop(chip); + s->running = 0; + } + break; + default: + err = -EINVAL; + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return err; +} + + +/* + * prepare playback/capture channel + */ +static int snd_nm256_pcm_prepare(snd_pcm_substream_t *substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + unsigned long flags; + + snd_assert(s, return -ENXIO); + s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size); + s->period_size = frames_to_bytes(runtime, substream->runtime->period_size); + s->periods = substream->runtime->periods; + s->cur_period = 0; + + spin_lock_irqsave(&chip->reg_lock, flags); + s->running = 0; + snd_nm256_set_format(chip, s, substream); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + + +/* + * get the current pointer + */ +static snd_pcm_uframes_t +snd_nm256_playback_pointer(snd_pcm_substream_t * substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long curp; + + snd_assert(s, return 0); + curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf; + curp %= s->dma_size; + return bytes_to_frames(substream->runtime, curp); +} + +static snd_pcm_uframes_t +snd_nm256_capture_pointer(snd_pcm_substream_t * substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long curp; + + snd_assert(s != NULL, return 0); + curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf; + curp %= s->dma_size; + return bytes_to_frames(substream->runtime, curp); +} + +#ifndef __i386__ +/* FIXME: I/O space is not accessible via pointers on all architectures */ + +/* + * silence / copy for playback + */ +static int +snd_nm256_playback_silence(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + count = frames_to_bytes(runtime, count); + pos = frames_to_bytes(runtime, pos); + memset_io(s->bufptr + pos, 0, count); + return 0; +} + +static int +snd_nm256_playback_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + count = frames_to_bytes(runtime, count); + pos = frames_to_bytes(runtime, pos); + if (copy_from_user_toio(s->bufptr + pos, src, count)) + return -EFAULT; + return 0; +} + +/* + * copy to user + */ +static int +snd_nm256_capture_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + count = frames_to_bytes(runtime, count); + pos = frames_to_bytes(runtime, pos); + if (copy_to_user_fromio(dst, s->bufptr + pos, count)) + return -EFAULT; + return 0; +} + +#endif /* !__i386__ */ + + +/* + * update playback/capture watermarks + */ + +/* spinlock held! */ +static void +snd_nm256_playback_update(nm256_t *chip) +{ + nm256_stream_t *s; + + s = &chip->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (s->running && s->substream) { + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(s->substream); + spin_lock(&chip->reg_lock); + snd_nm256_playback_mark(chip, s); + } +} + +/* spinlock held! */ +static void +snd_nm256_capture_update(nm256_t *chip) +{ + nm256_stream_t *s; + + s = &chip->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (s->running && s->substream) { + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(s->substream); + spin_lock(&chip->reg_lock); + snd_nm256_capture_mark(chip, s); + } +} + +/* + * hardware info + */ +static snd_pcm_hardware_t snd_nm256_playback = +{ + info: +#ifdef __i386__ + SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID| +#endif + SNDRV_PCM_INFO_INTERLEAVED | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME, + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + periods_min: 2, + periods_max: 1024, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 256, + period_bytes_max: 128 * 1024, +}; + +static snd_pcm_hardware_t snd_nm256_capture = +{ + info: +#ifdef __i386__ + SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID| +#endif + SNDRV_PCM_INFO_INTERLEAVED | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME, + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + periods_min: 2, + periods_max: 1024, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 256, + period_bytes_max: 128 * 1024, +}; + + +/* set dma transfer size */ +static int snd_nm256_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params) +{ + /* area and addr are already set and unchanged */ + substream->runtime->dma_bytes = params_buffer_bytes(hw_params); + return 0; +} + +/* + * open + */ +static void snd_nm256_setup_stream(nm256_t *chip, nm256_stream_t *s, + snd_pcm_substream_t *substream, + snd_pcm_hardware_t *hw_ptr) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + s->running = 0; + runtime->hw = *hw_ptr; + runtime->hw.buffer_bytes_max = s->bufsize; + runtime->hw.period_bytes_max = s->bufsize / 2; + runtime->dma_area = (void*) s->bufptr; + runtime->dma_addr = s->bufptr_addr; + runtime->dma_bytes = s->bufsize; + runtime->private_data = s; + s->substream = substream; + + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); +} + +static int +snd_nm256_playback_open(snd_pcm_substream_t *substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + + snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK], + substream, &snd_nm256_playback); + return 0; +} + +static int +snd_nm256_capture_open(snd_pcm_substream_t *substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + + snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE], + substream, &snd_nm256_capture); + return 0; +} + +/* + * close - we don't have to do special.. + */ +static int +snd_nm256_playback_close(snd_pcm_substream_t *substream) +{ + return 0; +} + + +static int +snd_nm256_capture_close(snd_pcm_substream_t *substream) +{ + return 0; +} + +/* + * create a pcm instance + */ +static snd_pcm_ops_t snd_nm256_playback_ops = { + open: snd_nm256_playback_open, + close: snd_nm256_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_nm256_pcm_hw_params, + prepare: snd_nm256_pcm_prepare, + trigger: snd_nm256_playback_trigger, + pointer: snd_nm256_playback_pointer, +#ifndef __i386__ + copy: snd_nm256_playback_copy, + silence: snd_nm256_playback_silence, +#endif +}; + +static snd_pcm_ops_t snd_nm256_capture_ops = { + open: snd_nm256_capture_open, + close: snd_nm256_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_nm256_pcm_hw_params, + prepare: snd_nm256_pcm_prepare, + trigger: snd_nm256_capture_trigger, + pointer: snd_nm256_capture_pointer, +#ifndef __i386__ + copy: snd_nm256_capture_copy, +#endif +}; + +static int __init +snd_nm256_pcm(nm256_t *chip, int device) +{ + snd_pcm_t *pcm; + int i, err; + + for (i = 0; i < 2; i++) { + nm256_stream_t *s = &chip->streams[i]; + s->bufptr = chip->buffer + s->buf - chip->buffer_start; + s->bufptr_addr = chip->buffer_addr + s->buf - chip->buffer_start; + } + + err = snd_pcm_new(chip->card, chip->card->driver, device, + 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops); + + pcm->private_data = chip; + pcm->info_flags = 0; + chip->pcm = pcm; + + return 0; +} + + +/* + * Initialize the hardware. + */ +static void +snd_nm256_init_chip(nm256_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + /* Reset everything. */ + snd_nm256_writeb(chip, 0x0, 0x11); + snd_nm256_writew(chip, 0x214, 0); + /* stop sounds.. */ + //snd_nm256_playback_stop(chip); + //snd_nm256_capture_stop(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + + +inline static void +snd_nm256_intr_check(nm256_t *chip) +{ + if (chip->badintrcount++ > 1000) { + /* + * I'm not sure if the best thing is to stop the card from + * playing or just release the interrupt (after all, we're in + * a bad situation, so doing fancy stuff may not be such a good + * idea). + * + * I worry about the card engine continuing to play noise + * over and over, however--that could become a very + * obnoxious problem. And we know that when this usually + * happens things are fairly safe, it just means the user's + * inserted a PCMCIA card and someone's spamming us with IRQ 9s. + */ + if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) + snd_nm256_playback_stop(chip); + if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) + snd_nm256_capture_stop(chip); + chip->badintrcount = 0; + } +} + +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * + * I don't like the cut-n-paste job here either between the two routines, + * but there are sufficient differences between the two interrupt handlers + * that parameterizing it isn't all that great either. (Could use a macro, + * I suppose...yucky bleah.) + */ + +static void +snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy) +{ + nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return); + u16 status; + u8 cbyte; + + status = snd_nm256_readw(chip, NM_INT_REG); + + /* Not ours. */ + if (status == 0) { + snd_nm256_intr_check(chip); + return; + } + + chip->badintrcount = 0; + + /* Rather boring; check for individual interrupts and process them. */ + + spin_lock(&chip->reg_lock); + if (status & NM_PLAYBACK_INT) { + status &= ~NM_PLAYBACK_INT; + NM_ACK_INT(chip, NM_PLAYBACK_INT); + snd_nm256_playback_update(chip); + } + + if (status & NM_RECORD_INT) { + status &= ~NM_RECORD_INT; + NM_ACK_INT(chip, NM_RECORD_INT); + snd_nm256_capture_update(chip); + } + + if (status & NM_MISC_INT_1) { + status &= ~NM_MISC_INT_1; + NM_ACK_INT(chip, NM_MISC_INT_1); + snd_printd("NM256: Got misc interrupt #1\n"); + snd_nm256_writew(chip, NM_INT_REG, 0x8000); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte | 2); + } + + if (status & NM_MISC_INT_2) { + status &= ~NM_MISC_INT_2; + NM_ACK_INT(chip, NM_MISC_INT_2); + snd_printd("NM256: Got misc interrupt #2\n"); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte & ~2); + } + + /* Unknown interrupt. */ + if (status) { + snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n", + status); + /* Pray. */ + NM_ACK_INT(chip, status); + } + + spin_unlock(&chip->reg_lock); +} + +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * This handler is for the 256ZX, and is very similar to the non-ZX + * routine. + */ + +static void +snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy) +{ + nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return); + u32 status; + u8 cbyte; + + status = snd_nm256_readl(chip, NM_INT_REG); + + /* Not ours. */ + if (status == 0) { + snd_nm256_intr_check(chip); + return; + } + + chip->badintrcount = 0; + + /* Rather boring; check for individual interrupts and process them. */ + + spin_lock(&chip->reg_lock); + if (status & NM2_PLAYBACK_INT) { + status &= ~NM2_PLAYBACK_INT; + NM2_ACK_INT(chip, NM2_PLAYBACK_INT); + snd_nm256_playback_update(chip); + } + + if (status & NM2_RECORD_INT) { + status &= ~NM2_RECORD_INT; + NM2_ACK_INT(chip, NM2_RECORD_INT); + snd_nm256_capture_update(chip); + } + + if (status & NM2_MISC_INT_1) { + status &= ~NM2_MISC_INT_1; + NM2_ACK_INT(chip, NM2_MISC_INT_1); + snd_printd("NM256: Got misc interrupt #1\n"); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte | 2); + } + + if (status & NM2_MISC_INT_2) { + status &= ~NM2_MISC_INT_2; + NM2_ACK_INT(chip, NM2_MISC_INT_2); + snd_printd("NM256: Got misc interrupt #2\n"); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte & ~2); + } + + /* Unknown interrupt. */ + if (status) { + snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n", + status); + /* Pray. */ + NM2_ACK_INT(chip, status); + } + + spin_unlock(&chip->reg_lock); +} + +/* + * AC97 interface + */ + +/* + * Waits for the mixer to become ready to be written; returns a zero value + * if it timed out. + */ +static int +snd_nm256_ac97_ready(nm256_t *chip) +{ + int timeout = 10; + u32 testaddr; + u16 testb; + + testaddr = chip->mixer_status_offset; + testb = chip->mixer_status_mask; + + /* + * Loop around waiting for the mixer to become ready. + */ + while (timeout-- > 0) { + if ((snd_nm256_readw(chip, testaddr) & testb) == 0) + return 1; + udelay(100); + } + return 0; +} + +/* + */ +static unsigned short +snd_nm256_ac97_read(ac97_t *ac97, unsigned short reg) +{ + nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return -ENXIO); + int res; + + if (reg >= 128) + return 0; + + if (! snd_nm256_ac97_ready(chip)) + return 0; + res = snd_nm256_readw(chip, chip->mixer_base + reg); + /* Magic delay. Bleah yucky. */ + udelay(1000); + return res; +} + +/* + */ +static void +snd_nm256_ac97_write(ac97_t *ac97, + unsigned short reg, unsigned short val) +{ + nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return); + int tries = 2; + u32 base; + + base = chip->mixer_base; + + snd_nm256_ac97_ready(chip); + + /* Wait for the write to take, too. */ + while (tries-- > 0) { + snd_nm256_writew(chip, base + reg, val); + udelay(1000); /* a little delay here seems better.. */ + if (snd_nm256_ac97_ready(chip)) + return; + } + snd_printd("nm256: ac97 codec not ready..\n"); +} + +/* initialize the ac97 into a known state */ +static void +snd_nm256_ac97_reset(ac97_t *ac97) +{ + nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + /* Reset the mixer. 'Tis magic! */ + snd_nm256_writeb(chip, 0x6c0, 1); +#if 0 /* Dell latitude LS will lock up by this */ + snd_nm256_writeb(chip, 0x6cc, 0x87); +#endif + snd_nm256_writeb(chip, 0x6cc, 0x80); + snd_nm256_writeb(chip, 0x6cc, 0x0); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* create an ac97 mixer interface */ +static int __init +snd_nm256_mixer(nm256_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.init = snd_nm256_ac97_reset; + ac97.write = snd_nm256_ac97_write; + ac97.read = snd_nm256_ac97_read; + ac97.private_data = chip; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + return 0; +} + +/* + * See if the signature left by the NM256 BIOS is intact; if so, we use + * the associated address as the end of our audio buffer in the video + * RAM. + */ + +static int __init +snd_nm256_peek_for_sig(nm256_t *chip) +{ + /* The signature is located 1K below the end of video RAM. */ + unsigned long temp; + /* Default buffer end is 5120 bytes below the top of RAM. */ + unsigned long pointer_found = chip->buffer_end - 0x1400; + u32 sig; + + temp = (unsigned long) ioremap_nocache(chip->buffer_addr + chip->buffer_end - 0x400, 16); + if (temp == 0) { + snd_printk("Unable to scan for card signature in video RAM\n"); + return -EBUSY; + } + + sig = readl(temp); + if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { + u32 pointer = readl(temp + 4); + + /* + * If it's obviously invalid, don't use it + */ + if (pointer == 0xffffffff || + pointer < chip->buffer_size || + pointer > chip->buffer_end) { + snd_printk("invalid signature found: 0x%x\n", pointer); + iounmap((void *)temp); + return -ENODEV; + } else { + pointer_found = pointer; + printk(KERN_INFO "nm256: found card signature in video RAM: 0x%x\n", pointer); + } + } + + iounmap((void *)temp); + chip->buffer_end = pointer_found; + + return 0; +} + +#ifdef CONFIG_PM +/* + * APM event handler, so the card is properly reinitialized after a power + * event. + */ +static void nm256_suspend(nm256_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + snd_pcm_suspend_all(chip->pcm); + chip->coeffs_current = 0; + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void nm256_resume(nm256_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + /* Perform a full reset on the hardware */ + pci_enable_device(chip->pci); + snd_nm256_init_chip(chip); + + /* restore ac97 */ + snd_ac97_resume(chip->ac97); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_nm256_suspend(struct pci_dev *dev, u32 state) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return -ENXIO); + nm256_suspend(chip); + return 0; +} +static int snd_nm256_resume(struct pci_dev *dev) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return -ENXIO); + nm256_resume(chip); + return 0; +} +#else +static void snd_nm256_suspend(struct pci_dev *dev) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return); + nm256_suspend(chip); +} +static void snd_nm256_resume(struct pci_dev *dev) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return); + nm256_resume(chip); +} +#endif + +/* callback */ +static int snd_nm256_set_power_state(snd_card_t *card, unsigned int power_state) +{ + nm256_t *chip = snd_magic_cast(nm256_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + nm256_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + nm256_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +static int snd_nm256_free(nm256_t *chip) +{ + if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) + snd_nm256_playback_stop(chip); + if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) + snd_nm256_capture_stop(chip); + + synchronize_irq(); + + if (chip->cport) + iounmap((void *) chip->cport); + if (chip->buffer) + iounmap((void *) chip->buffer); + if (chip->res_cport) { + release_resource(chip->res_cport); + kfree_nocheck(chip->res_cport); + } + if (chip->res_buffer) { + release_resource(chip->res_buffer); + kfree_nocheck(chip->res_buffer); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void*)chip); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_nm256_dev_free(snd_device_t *device) +{ + nm256_t *chip = snd_magic_cast(nm256_t, device->device_data, return -ENXIO); + return snd_nm256_free(chip); +} + +static int __init +snd_nm256_create(snd_card_t *card, struct pci_dev *pci, + int play_bufsize, int capt_bufsize, + int force_load, + u32 buffertop, + int usecache, + nm256_t **chip_ret) +{ + nm256_t *chip; + int err, pval; + static snd_device_ops_t ops = { + dev_free: snd_nm256_dev_free, + }; + u32 addr; + + *chip_ret = NULL; + + chip = snd_magic_kcalloc(nm256_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->card = card; + chip->pci = pci; + chip->use_cache = usecache; + spin_lock_init(&chip->reg_lock); + chip->irq = -1; + + chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = play_bufsize; + chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capt_bufsize; + + /* + * The NM256 has two memory ports. The first port is nothing + * more than a chunk of video RAM, which is used as the I/O ring + * buffer. The second port has the actual juicy stuff (like the + * mixer and the playback engine control registers). + */ + + chip->buffer_addr = pci_resource_start(pci, 0); + chip->cport_addr = pci_resource_start(pci, 1); + + /* Init the memory port info. */ + /* remap control port (#2) */ + chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE, + card->driver); + if (chip->res_cport == NULL) { + snd_printk("memory region 0x%lx (size 0x%x) busy\n", + chip->cport_addr, NM_PORT2_SIZE); + err = -EBUSY; + goto __error; + } + chip->cport = (unsigned long) ioremap_nocache(chip->cport_addr, NM_PORT2_SIZE); + if (chip->cport == 0) { + snd_printk("unable to map control port %lx\n", chip->cport_addr); + err = -ENOMEM; + goto __error; + } + + if (!strcmp(card->driver, "NM256AV")) { + /* Ok, try to see if this is a non-AC97 version of the hardware. */ + pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE); + if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { + if (! force_load) { + printk(KERN_INFO "nm256: no ac97 is found!\n"); + printk(KERN_INFO " force the driver to load by passing in the module parameter\n"); + printk(KERN_INFO " snd_force_ac97=1\n"); + printk(KERN_INFO " or try sb16 or cs423x drivers instead.\n"); + err = -ENXIO; + goto __error; + } + } + chip->buffer_end = 2560 * 1024; + chip->interrupt = snd_nm256_interrupt; + chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET; + chip->mixer_status_mask = NM_MIXER_READY_MASK; + } else { + /* Not sure if there is any relevant detect for the ZX or not. */ + if (snd_nm256_readb(chip, 0xa0b) != 0) + chip->buffer_end = 6144 * 1024; + else + chip->buffer_end = 4096 * 1024; + + chip->interrupt = snd_nm256_interrupt_zx; + chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; + chip->mixer_status_mask = NM2_MIXER_READY_MASK; + } + + chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize + chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; + if (chip->use_cache) + chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4; + else + chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE; + + if (buffertop >= chip->buffer_size && buffertop < chip->buffer_end) + chip->buffer_end = buffertop; + else { + /* get buffer end pointer from signature */ + if ((err = snd_nm256_peek_for_sig(chip)) < 0) + goto __error; + } + + chip->buffer_start = chip->buffer_end - chip->buffer_size; + chip->buffer_addr += chip->buffer_start; + + snd_printd("NM256: Mapping port 1 from 0x%x - 0x%x\n", + chip->buffer_start, chip->buffer_end); + + chip->res_buffer = request_mem_region(chip->buffer_addr, + chip->buffer_size, + card->driver); + if (chip->res_buffer == NULL) { + snd_printk("nm256: buffer 0x%lx (size 0x%x) busy\n", + chip->buffer_addr, chip->buffer_size); + err = -EBUSY; + goto __error; + } + chip->buffer = (unsigned long) ioremap_nocache(chip->buffer_addr, chip->buffer_size); + if (chip->buffer == 0) { + err = -ENOMEM; + snd_printk("unable to map ring buffer at %lx\n", chip->buffer_addr); + goto __error; + } + + /* set offsets */ + addr = chip->buffer_start; + chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr; + addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize; + chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr; + addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; + if (chip->use_cache) { + chip->all_coeff_buf = addr; + } else { + chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr; + addr += NM_MAX_PLAYBACK_COEF_SIZE; + chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr; + } + + /* acquire interrupt */ + if (request_irq(pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ, + card->driver, (void*)chip)) { + err = -EBUSY; + snd_printk("unable to grab IRQ %d\n", pci->irq); + goto __error; + } + chip->irq = pci->irq; + + /* Fixed setting. */ + chip->mixer_base = NM_MIXER_OFFSET; + + chip->coeffs_current = 0; + + snd_nm256_init_chip(chip); + + if ((err = snd_nm256_pcm(chip, 0)) < 0) + goto __error; + + if ((err = snd_nm256_mixer(chip) < 0)) + goto __error; + + // pci_set_master(pci); /* needed? */ + +#ifdef CONFIG_PM + card->set_power_state = snd_nm256_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __error; + + *chip_ret = chip; + return 0; + +__error: + snd_nm256_free(chip); + return err; +} + + +static int __devinit snd_nm256_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + nm256_t *chip; + int err; + unsigned int buffer_top; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (pci->device) { + case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO: + strcpy(card->driver, "NM256AV"); + break; + case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO: + strcpy(card->driver, "NM256ZX"); + break; + default: + snd_printk("invalid device id 0x%x\n", pci->device); + snd_card_free(card); + return -EINVAL; + } + + if (snd_vaio_hack[dev]) + buffer_top = 0x25a800; /* this avoids conflicts with XFree86 server */ + else + buffer_top = snd_buffer_top[dev]; + + if (snd_playback_bufsize[dev] < 4) + snd_playback_bufsize[dev] = 4; + if (snd_playback_bufsize[dev] > 128) + snd_playback_bufsize[dev] = 128; + if (snd_capture_bufsize[dev] < 4) + snd_capture_bufsize[dev] = 4; + if (snd_capture_bufsize[dev] > 128) + snd_capture_bufsize[dev] = 128; + if ((err = snd_nm256_create(card, pci, + snd_playback_bufsize[dev] * 1024, /* in bytes */ + snd_capture_bufsize[dev] * 1024, /* in bytes */ + snd_force_ac97[dev], + buffer_top, + snd_use_cache[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->shortname, "NeoMagic %s", card->driver); + sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d", + card->shortname, + chip->buffer_addr, chip->cport_addr, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_nm256_remove(struct pci_dev *pci) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + + +static struct pci_driver driver = { + name: "NeoMagic 256", + id_table: snd_nm256_ids, + probe: snd_nm256_probe, + remove: __devexit_p(snd_nm256_remove), +#ifdef CONFIG_PM + suspend: snd_nm256_suspend, + resume: snd_nm256_resume, +#endif +}; + + +static int __init alsa_card_nm256_init(void) +{ + int err; + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("NeoMagic 256 audio soundchip not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_nm256_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_nm256_init) +module_exit(alsa_card_nm256_exit) + +#ifndef MODULE + +/* format is: snd-nm256=snd_enable,snd_index,snd_id, + snd_playback_bufsize,snd_capture_bufsize, + snd_force_ac97,snd_buffer_top,snd_use_cache */ + +static int __init alsa_card_nm256_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_playback_bufsize[nr_dev]) == 2 && + get_option(&str,&snd_capture_bufsize[nr_dev]) == 2 && + get_option(&str,&snd_force_ac97[nr_dev]) == 2 && + get_option(&str,&snd_buffer_top[nr_dev]) == 2 && + get_option(&str,&snd_use_cache[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-nm256=", alsa_card_nm256_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/nm256/nm256_coef.c b/sound/pci/nm256/nm256_coef.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/nm256/nm256_coef.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,4607 @@ +#define NM_TOTAL_COEFF_COUNT 0x3158 + +static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { + 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21, + 0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01, + 0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, + 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, + 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFD, 0xFF, + 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, + 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, + 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x02, 0x00, 0x05, + 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, + 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, + 0x60, 0x00, 0xA4, 0xFF, 0xDE, 0xFF, 0xB5, 0x01, 0x5E, 0xF9, 0xE9, + 0x45, 0x62, 0x11, 0x87, 0xF7, 0x21, 0x05, 0xEF, 0xFC, 0xA5, 0x01, + 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, + 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, + 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, + 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xCA, 0x01, 0x95, 0xFC, 0xEA, 0x05, 0xBB, 0xF5, 0x25, 0x17, 0x3C, + 0x43, 0x8D, 0xF6, 0x43, 0x03, 0xF5, 0xFE, 0x26, 0x00, 0x20, 0x00, + 0xE2, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, + 0x01, 0x4C, 0xFC, 0x26, 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, + 0x8F, 0xF1, 0xCA, 0x06, 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD5, 0xFF, 0xBC, 0x00, + 0xF0, 0xFD, 0xEC, 0x04, 0xD9, 0xF3, 0xB1, 0x3E, 0xCD, 0x1E, 0xC1, + 0xF3, 0xAF, 0x06, 0x49, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, + 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, + 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0x98, 0x01, 0x0D, 0xFD, + 0xE0, 0x04, 0x14, 0xF8, 0xC3, 0x0F, 0x89, 0x46, 0x4C, 0xFA, 0x38, + 0x01, 0x25, 0x00, 0x7D, 0xFF, 0x73, 0x00, 0xC2, 0xFF, 0x0F, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, + 0x07, 0x84, 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, + 0x41, 0xFD, 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x15, 0x00, 0x97, 0xFF, 0x37, 0x01, 0x22, 0xFD, 0x23, 0x06, + 0x2F, 0xF2, 0x11, 0x39, 0x7B, 0x26, 0x50, 0xF2, 0x1B, 0x07, 0x32, + 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC8, 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, + 0xF9, 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, + 0xA2, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, + 0x00, 0x6A, 0xFF, 0x53, 0x01, 0xA6, 0xFD, 0xA6, 0x03, 0xA1, 0xFA, + 0xDE, 0x08, 0x76, 0x48, 0x0C, 0xFF, 0xDE, 0xFE, 0x73, 0x01, 0xC9, + 0xFE, 0xCA, 0x00, 0xA0, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, + 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, + 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x68, + 0xFF, 0x93, 0x01, 0x92, 0xFC, 0xE2, 0x06, 0x83, 0xF1, 0x8C, 0x32, + 0xED, 0x2D, 0x90, 0xF1, 0x1E, 0x07, 0x57, 0xFC, 0xBD, 0x01, 0x51, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, + 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, + 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, 0x03, 0x01, 0x53, + 0xFE, 0x53, 0x02, 0x39, 0xFD, 0xA9, 0x02, 0xF2, 0x48, 0xB9, 0x04, + 0x54, 0xFC, 0xCA, 0x02, 0x16, 0xFE, 0x20, 0x01, 0x7F, 0xFF, 0x20, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC3, 0x01, + 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, 0x43, 0x20, + 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, 0xDD, 0xFF, + 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCD, 0x01, 0x43, + 0xFC, 0x2A, 0x07, 0xBC, 0xF1, 0x64, 0x2B, 0xE3, 0x34, 0xA3, 0xF1, + 0xAE, 0x06, 0xBD, 0xFC, 0x77, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, 0xFD, + 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, 0xC8, + 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x14, 0x00, 0xAC, 0xFF, 0xAC, 0x00, 0x08, 0xFF, 0xFD, 0x00, 0xB5, + 0xFF, 0x4B, 0xFD, 0xF4, 0x47, 0x30, 0x0B, 0xBC, 0xF9, 0x17, 0x04, + 0x6E, 0xFD, 0x6D, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, 0x26, 0xFD, 0xAD, 0x04, + 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, 0xFB, 0xD4, 0x00, 0x5D, + 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, 0x10, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0x01, 0x07, 0xBE, + 0xF2, 0xD6, 0x23, 0x1F, 0x3B, 0xA5, 0xF2, 0xC5, 0x05, 0x62, 0xFD, + 0x10, 0x01, 0xAB, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, + 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, 0x4D, 0x06, 0x00, 0xF2, + 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, 0x07, 0x34, 0xFC, 0xDD, + 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, + 0x56, 0x00, 0xB9, 0xFF, 0xB8, 0xFF, 0xF7, 0x01, 0xE2, 0xF8, 0x8D, + 0x45, 0x46, 0x12, 0x3C, 0xF7, 0x43, 0x05, 0xDF, 0xFC, 0xAC, 0x01, + 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, + 0xFF, 0x46, 0x01, 0xC3, 0xFD, 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, + 0xA6, 0x48, 0xF8, 0xFF, 0x70, 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, + 0x00, 0x9A, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xDE, 0x01, 0x5D, 0xFC, 0x74, 0x06, 0x63, 0xF4, 0x23, 0x1C, 0x66, + 0x40, 0xAA, 0xF4, 0x65, 0x04, 0x44, 0xFE, 0x8B, 0x00, 0xEE, 0xFF, + 0xF5, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, + 0x01, 0x80, 0xFC, 0xF7, 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, + 0x83, 0xF1, 0x13, 0x07, 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, + 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xED, 0xFF, 0x05, 0x00, 0x5D, 0x00, + 0x95, 0xFE, 0xE2, 0x03, 0x7F, 0xF5, 0xCC, 0x41, 0xC7, 0x19, 0xFF, + 0xF4, 0x37, 0x06, 0x75, 0xFC, 0xD6, 0x01, 0x39, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1B, 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, + 0x02, 0xAA, 0xFD, 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, + 0x05, 0x03, 0xF7, 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBB, 0x01, 0xBA, 0xFC, + 0x95, 0x05, 0x83, 0xF6, 0x8C, 0x14, 0x87, 0x44, 0xBB, 0xF7, 0x98, + 0x02, 0x5A, 0xFF, 0xEE, 0xFF, 0x3C, 0x00, 0xD8, 0xFF, 0x0A, 0x00, + 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, + 0x07, 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, + 0xD5, 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, + 0x00, 0x07, 0x00, 0xBE, 0xFF, 0xEA, 0x00, 0xA2, 0xFD, 0x65, 0x05, + 0x28, 0xF3, 0xDB, 0x3C, 0x78, 0x21, 0x30, 0xF3, 0xDF, 0x06, 0x3A, + 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, + 0xB2, 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, + 0xFC, 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, + 0x79, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x58, 0xFF, 0x82, 0x01, 0x3F, 0xFD, 0x78, 0x04, 0xF2, 0xF8, + 0x50, 0x0D, 0x5E, 0x47, 0xD5, 0xFB, 0x6F, 0x00, 0x96, 0x00, 0x40, + 0xFF, 0x91, 0x00, 0xB7, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, + 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, + 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x85, + 0xFF, 0x5B, 0x01, 0xE9, 0xFC, 0x73, 0x06, 0xD8, 0xF1, 0xE5, 0x36, + 0x19, 0x29, 0xF8, 0xF1, 0x29, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x42, + 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, 0xFF, 0x47, 0x00, + 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, 0x8D, + 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, 0xFF, + 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x75, 0xFF, 0x39, + 0x01, 0xE0, 0xFD, 0x33, 0x03, 0x87, 0xFB, 0xA2, 0x06, 0xCB, 0x48, + 0xEA, 0x00, 0x01, 0xFE, 0xE9, 0x01, 0x8A, 0xFE, 0xE8, 0x00, 0x95, + 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, 0xDA, 0x01, + 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, 0x41, 0x1F, + 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, 0xF0, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5B, 0xFF, 0xAB, 0x01, 0x6F, + 0xFC, 0x08, 0x07, 0x7E, 0xF1, 0x21, 0x30, 0x67, 0x30, 0x7D, 0xF1, + 0x05, 0x07, 0x73, 0xFC, 0xA8, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, + 0xFF, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0x67, 0xFE, + 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, 0xA6, 0xF4, 0x5A, + 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x96, 0xFF, 0xE5, 0x00, 0x91, 0xFE, 0xDC, 0x01, 0x1A, + 0xFE, 0xB3, 0x00, 0xC3, 0x48, 0xE1, 0x06, 0x6E, 0xFB, 0x40, 0x03, + 0xDA, 0xFD, 0x3C, 0x01, 0x74, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, + 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, + 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x41, 0xFF, 0xD9, 0x01, 0x36, 0xFC, 0x28, 0x07, 0x01, + 0xF2, 0xCE, 0x28, 0x23, 0x37, 0xE0, 0xF1, 0x6B, 0x06, 0xEF, 0xFC, + 0x57, 0x01, 0x87, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, + 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, 0xFD, 0x9C, 0x05, 0xDC, 0xF2, + 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, 0xF3, 0x06, 0x35, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xB8, 0xFF, + 0x8E, 0x00, 0x46, 0xFF, 0x8A, 0x00, 0x86, 0x00, 0xA7, 0xFB, 0x48, + 0x47, 0x95, 0x0D, 0xD9, 0xF8, 0x84, 0x04, 0x39, 0xFD, 0x85, 0x01, + 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, + 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, + 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, + 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE6, 0x01, 0x3B, 0xFC, 0xDA, 0x06, 0x3F, 0xF3, 0x2C, 0x21, 0x11, + 0x3D, 0x3A, 0xF3, 0x58, 0x05, 0xAA, 0xFD, 0xE5, 0x00, 0xC1, 0xFF, + 0x06, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, + 0x01, 0xCF, 0xFC, 0x96, 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, + 0xD4, 0xF1, 0x2B, 0x07, 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, + 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD9, 0xFF, 0x39, 0x00, 0xF4, 0xFF, + 0x4E, 0xFF, 0xAC, 0x02, 0x98, 0xF7, 0x65, 0x44, 0xD6, 0x14, 0x6C, + 0xF6, 0x9F, 0x05, 0xB6, 0xFC, 0xBD, 0x01, 0x42, 0xFF, 0x32, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, + 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, + 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD5, 0x01, 0x78, 0xFC, + 0x2F, 0x06, 0x13, 0xF5, 0x7C, 0x19, 0xF7, 0x41, 0x9B, 0xF5, 0xD1, + 0x03, 0x9F, 0xFE, 0x57, 0x00, 0x08, 0x00, 0xEC, 0xFF, 0x06, 0x00, + 0xFD, 0xFF, 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, + 0x07, 0x85, 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, + 0x84, 0xFC, 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, + 0x00, 0xF6, 0xFF, 0xEB, 0xFF, 0x91, 0x00, 0x3B, 0xFE, 0x75, 0x04, + 0x92, 0xF4, 0x36, 0x40, 0x6E, 0x1C, 0x50, 0xF4, 0x7B, 0x06, 0x5B, + 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, + 0x9C, 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, + 0xFF, 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, + 0x49, 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x49, 0xFF, 0xAA, 0x01, 0xE4, 0xFC, 0x38, 0x05, 0x54, 0xF7, + 0xFE, 0x11, 0xAA, 0x45, 0x09, 0xF9, 0xE2, 0x01, 0xC4, 0xFF, 0xB3, + 0xFF, 0x59, 0x00, 0xCD, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, + 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, + 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA9, + 0xFF, 0x15, 0x01, 0x5B, 0xFD, 0xD0, 0x05, 0x97, 0xF2, 0xE6, 0x3A, + 0x21, 0x24, 0xB1, 0xF2, 0x04, 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x39, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, + 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, + 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, + 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x6A, + 0x01, 0x74, 0xFD, 0x0A, 0x04, 0xD5, 0xF9, 0xED, 0x0A, 0x03, 0x48, + 0x7C, 0xFD, 0x9E, 0xFF, 0x0A, 0x01, 0x01, 0xFF, 0xAF, 0x00, 0xAB, + 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, + 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, 0x3D, 0x91, + 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, 0x02, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x75, 0xFF, 0x7A, 0x01, 0xB8, + 0xFC, 0xB4, 0x06, 0x9E, 0xF1, 0xA2, 0x34, 0xAD, 0x2B, 0xB6, 0xF1, + 0x29, 0x07, 0x45, 0xFC, 0xCB, 0x01, 0x49, 0xFF, 0x31, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, 0xFF, + 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, 0xCA, + 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0x1C, 0x01, 0x1C, 0xFE, 0xBD, + 0x02, 0x6E, 0xFC, 0x7D, 0x04, 0xF3, 0x48, 0xE2, 0x02, 0x1F, 0xFD, + 0x60, 0x02, 0x4C, 0xFE, 0x06, 0x01, 0x89, 0xFF, 0x1D, 0x00, 0xFE, + 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, 0x88, 0xFC, 0x09, 0x06, + 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, 0xF6, 0x83, 0x03, 0xCF, + 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, + 0x2E, 0x00, 0x50, 0xFF, 0xBF, 0x01, 0x54, 0xFC, 0x20, 0x07, 0x94, + 0xF1, 0xA6, 0x2D, 0xD0, 0x32, 0x85, 0xF1, 0xDD, 0x06, 0x96, 0xFC, + 0x90, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, + 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, 0xB9, 0x04, 0x27, 0xF4, + 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, 0x06, 0x50, 0xFC, 0xE2, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA2, 0xFF, + 0xC7, 0x00, 0xD0, 0xFE, 0x65, 0x01, 0xF6, 0xFE, 0xD9, 0xFE, 0x6A, + 0x48, 0x1F, 0x09, 0x87, 0xFA, 0xB3, 0x03, 0xA0, 0xFD, 0x56, 0x01, + 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, + 0xFF, 0xA0, 0x01, 0xFB, 0xFC, 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, + 0x2B, 0x46, 0xBB, 0xF9, 0x83, 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, + 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, + 0xE1, 0x01, 0x31, 0xFC, 0x19, 0x07, 0x5B, 0xF2, 0x30, 0x26, 0x4B, + 0x39, 0x3B, 0xF2, 0x1A, 0x06, 0x29, 0xFD, 0x33, 0x01, 0x99, 0xFF, + 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, + 0x01, 0x3A, 0xFD, 0x00, 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, + 0x79, 0xF2, 0x12, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC4, 0xFF, 0x70, 0x00, 0x84, 0xFF, + 0x19, 0x00, 0x4D, 0x01, 0x22, 0xFA, 0x70, 0x46, 0x0A, 0x10, 0xFC, + 0xF7, 0xEB, 0x04, 0x08, 0xFD, 0x9A, 0x01, 0x4F, 0xFF, 0x2E, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, + 0xFD, 0xD2, 0x03, 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, + 0x33, 0xFF, 0x45, 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, + 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4B, 0xFC, + 0xA9, 0x06, 0xD2, 0xF3, 0x81, 0x1E, 0xE4, 0x3E, 0xEF, 0xF3, 0xDE, + 0x04, 0xF9, 0xFD, 0xB7, 0x00, 0xD8, 0xFF, 0xFD, 0xFF, 0x03, 0x00, + 0xFD, 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, + 0x06, 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, + 0x4E, 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE4, 0xFF, 0x1D, 0x00, 0x2D, 0x00, 0xEA, 0xFE, 0x56, 0x03, + 0x6D, 0xF6, 0x17, 0x43, 0x70, 0x17, 0xA6, 0xF5, 0xF3, 0x05, 0x91, + 0xFC, 0xCC, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x86, 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, + 0x03, 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, + 0x14, 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, + 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x99, 0xFC, 0xE1, 0x05, 0xD1, 0xF5, + 0xDC, 0x16, 0x65, 0x43, 0xAD, 0xF6, 0x31, 0x03, 0x00, 0xFF, 0x20, + 0x00, 0x23, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, + 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, + 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, + 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xD3, + 0xFF, 0xC1, 0x00, 0xE7, 0xFD, 0xFA, 0x04, 0xC4, 0xF3, 0x7E, 0x3E, + 0x19, 0x1F, 0xB0, 0xF3, 0xB5, 0x06, 0x47, 0xFC, 0xE4, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, 0x00, + 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, 0x47, + 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x96, + 0x01, 0x13, 0xFD, 0xD5, 0x04, 0x2C, 0xF8, 0x7D, 0x0F, 0xA3, 0x46, + 0x76, 0xFA, 0x22, 0x01, 0x32, 0x00, 0x76, 0xFF, 0x76, 0x00, 0xC1, + 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, 0xE4, 0x01, + 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, 0x3A, 0x74, + 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, 0x11, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x95, 0xFF, 0x3B, 0x01, 0x1B, + 0xFD, 0x2D, 0x06, 0x24, 0xF2, 0xD3, 0x38, 0xC6, 0x26, 0x45, 0xF2, + 0x1D, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, 0xFF, 0xE2, 0xFF, + 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, 0x8F, 0xF7, 0x1D, + 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x51, 0x01, 0xAC, 0xFD, 0x9A, + 0x03, 0xBA, 0xFA, 0x9E, 0x08, 0x81, 0x48, 0x40, 0xFF, 0xC6, 0xFE, + 0x80, 0x01, 0xC2, 0xFE, 0xCE, 0x00, 0x9F, 0xFF, 0x17, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, + 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, + 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFD, 0xFF, + 0x27, 0x00, 0x66, 0xFF, 0x96, 0x01, 0x8E, 0xFC, 0xE7, 0x06, 0x81, + 0xF1, 0x48, 0x32, 0x34, 0x2E, 0x8D, 0xF1, 0x1C, 0x07, 0x5A, 0xFC, + 0xBB, 0x01, 0x53, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, + 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, 0xFE, 0xA6, 0x03, 0xE4, 0xF5, + 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, 0x1A, 0x06, 0x81, 0xFC, 0xD2, + 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8B, 0xFF, + 0xFF, 0x00, 0x5A, 0xFE, 0x46, 0x02, 0x52, 0xFD, 0x70, 0x02, 0xED, + 0x48, 0xF5, 0x04, 0x3B, 0xFC, 0xD7, 0x02, 0x0F, 0xFE, 0x23, 0x01, + 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, + 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, + 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, + 0xCE, 0x01, 0x41, 0xFC, 0x2A, 0x07, 0xC2, 0xF1, 0x1B, 0x2B, 0x25, + 0x35, 0xA8, 0xF1, 0xA7, 0x06, 0xC2, 0xFC, 0x74, 0x01, 0x78, 0xFF, + 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, + 0x00, 0xBF, 0xFD, 0x38, 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, + 0x66, 0xF3, 0xCE, 0x06, 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAE, 0xFF, 0xA9, 0x00, 0x0F, 0xFF, + 0xF0, 0x00, 0xCD, 0xFF, 0x1B, 0xFD, 0xE4, 0x47, 0x73, 0x0B, 0xA2, + 0xF9, 0x23, 0x04, 0x68, 0xFD, 0x70, 0x01, 0x5F, 0xFF, 0x29, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, + 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, + 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, + 0xFD, 0x06, 0xCB, 0xF2, 0x8A, 0x23, 0x58, 0x3B, 0xB4, 0xF2, 0xBA, + 0x05, 0x6A, 0xFD, 0x0B, 0x01, 0xAE, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, + 0x06, 0xF7, 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, + 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, + 0x00, 0xCF, 0xFF, 0x52, 0x00, 0xC0, 0xFF, 0xAC, 0xFF, 0x0C, 0x02, + 0xBC, 0xF8, 0x6D, 0x45, 0x8E, 0x12, 0x24, 0xF7, 0x4D, 0x05, 0xDB, + 0xFC, 0xAE, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x24, 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, + 0xFB, 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, + 0xA3, 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xDD, 0x01, 0x60, 0xFC, 0x6D, 0x06, 0x76, 0xF4, + 0xD8, 0x1B, 0x95, 0x40, 0xC3, 0xF4, 0x56, 0x04, 0x4E, 0xFE, 0x85, + 0x00, 0xF1, 0xFF, 0xF4, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, + 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, + 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, + 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x02, + 0x00, 0x63, 0x00, 0x8A, 0xFE, 0xF3, 0x03, 0x63, 0xF5, 0xA1, 0x41, + 0x12, 0x1A, 0xEB, 0xF4, 0x3F, 0x06, 0x72, 0xFC, 0xD7, 0x01, 0x39, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, + 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, + 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, + 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBA, + 0x01, 0xBF, 0xFC, 0x8B, 0x05, 0x99, 0xF6, 0x43, 0x14, 0xA9, 0x44, + 0xDE, 0xF7, 0x85, 0x02, 0x65, 0xFF, 0xE7, 0xFF, 0x3F, 0x00, 0xD6, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD5, 0x01, + 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, 0x36, 0xC5, + 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, 0x1E, 0x00, + 0xFE, 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBC, 0xFF, 0xEF, 0x00, 0x9A, + 0xFD, 0x72, 0x05, 0x16, 0xF3, 0xA5, 0x3C, 0xC4, 0x21, 0x21, 0xF3, + 0xE4, 0x06, 0x39, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, 0x00, + 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, 0x5A, + 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x80, 0x01, 0x45, 0xFD, 0x6C, + 0x04, 0x0B, 0xF9, 0x0B, 0x0D, 0x73, 0x47, 0x02, 0xFC, 0x58, 0x00, + 0xA3, 0x00, 0x39, 0xFF, 0x94, 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, 0xFC, 0xEB, 0x06, + 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, 0xF2, 0x84, 0x05, 0x8D, + 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, + 0x1D, 0x00, 0x83, 0xFF, 0x5E, 0x01, 0xE3, 0xFC, 0x7B, 0x06, 0xD0, + 0xF1, 0xA5, 0x36, 0x62, 0x29, 0xEF, 0xF1, 0x29, 0x07, 0x39, 0xFC, + 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, + 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, 0x67, 0x02, 0x14, 0xF8, + 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, 0x05, 0xC5, 0xFC, 0xB7, + 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, + 0x76, 0xFF, 0x35, 0x01, 0xE7, 0xFD, 0x26, 0x03, 0xA1, 0xFB, 0x64, + 0x06, 0xD2, 0x48, 0x21, 0x01, 0xE8, 0xFD, 0xF7, 0x01, 0x83, 0xFE, + 0xEC, 0x00, 0x93, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, + 0xFF, 0xD9, 0x01, 0x6D, 0xFC, 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, + 0x5F, 0x41, 0x3A, 0xF5, 0x0C, 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, + 0xFF, 0xEF, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, + 0xAD, 0x01, 0x6C, 0xFC, 0x0C, 0x07, 0x7F, 0xF1, 0xDC, 0x2F, 0xAD, + 0x30, 0x7D, 0xF1, 0x01, 0x07, 0x76, 0xFC, 0xA6, 0x01, 0x5E, 0xFF, + 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, + 0x00, 0x5D, 0xFE, 0x3E, 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, + 0x93, 0xF4, 0x62, 0x06, 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x97, 0xFF, 0xE2, 0x00, 0x98, 0xFE, + 0xCF, 0x01, 0x33, 0xFE, 0x7D, 0x00, 0xBB, 0x48, 0x1F, 0x07, 0x54, + 0xFB, 0x4C, 0x03, 0xD3, 0xFD, 0x3F, 0x01, 0x73, 0xFF, 0x23, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, + 0xFC, 0x5D, 0x05, 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, + 0x2A, 0x02, 0x9A, 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, + 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDA, 0x01, 0x35, 0xFC, + 0x27, 0x07, 0x09, 0xF2, 0x85, 0x28, 0x63, 0x37, 0xE9, 0xF1, 0x63, + 0x06, 0xF5, 0xFC, 0x53, 0x01, 0x89, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x00, 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, + 0x05, 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, + 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, + 0x00, 0xB9, 0xFF, 0x8A, 0x00, 0x4D, 0xFF, 0x7D, 0x00, 0x9C, 0x00, + 0x7B, 0xFB, 0x31, 0x47, 0xD9, 0x0D, 0xC0, 0xF8, 0x8F, 0x04, 0x34, + 0xFD, 0x87, 0x01, 0x56, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, + 0xF9, 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, + 0x19, 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD5, 0x06, 0x4F, 0xF3, + 0xE0, 0x20, 0x45, 0x3D, 0x4D, 0xF3, 0x4B, 0x05, 0xB3, 0xFD, 0xE0, + 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, + 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, + 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, + 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xDA, 0xFF, 0x36, + 0x00, 0xFA, 0xFF, 0x43, 0xFF, 0xBF, 0x02, 0x75, 0xF7, 0x42, 0x44, + 0x20, 0x15, 0x55, 0xF6, 0xA9, 0x05, 0xB2, 0xFC, 0xBF, 0x01, 0x41, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, + 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, 0xEA, + 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, 0x00, + 0x8D, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD4, + 0x01, 0x7C, 0xFC, 0x27, 0x06, 0x28, 0xF5, 0x31, 0x19, 0x21, 0x42, + 0xB8, 0xF5, 0xC0, 0x03, 0xAA, 0xFE, 0x51, 0x00, 0x0B, 0x00, 0xEA, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, 0xB7, 0x01, + 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, 0x31, 0x7E, + 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, 0x28, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xE8, 0xFF, 0x96, 0x00, 0x31, + 0xFE, 0x84, 0x04, 0x79, 0xF4, 0x07, 0x40, 0xBA, 0x1C, 0x3E, 0xF4, + 0x82, 0x06, 0x58, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, 0xFE, 0x93, 0x01, + 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, 0xE1, 0xFA, 0x86, + 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA8, 0x01, 0xE9, 0xFC, 0x2D, + 0x05, 0x6B, 0xF7, 0xB6, 0x11, 0xC8, 0x45, 0x30, 0xF9, 0xCD, 0x01, + 0xD0, 0xFF, 0xAC, 0xFF, 0x5C, 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, + 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, + 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x10, 0x00, 0xA7, 0xFF, 0x19, 0x01, 0x53, 0xFD, 0xDB, 0x05, 0x88, + 0xF2, 0xAD, 0x3A, 0x6D, 0x24, 0xA4, 0xF2, 0x08, 0x07, 0x32, 0xFC, + 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBF, + 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, 0x00, 0x01, 0x01, 0xB6, 0xFA, + 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, 0xC4, 0x04, 0x1B, 0xFD, 0x92, + 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x63, 0xFF, 0x67, 0x01, 0x7A, 0xFD, 0xFE, 0x03, 0xEE, 0xF9, 0xAA, + 0x0A, 0x16, 0x48, 0xAC, 0xFD, 0x86, 0xFF, 0x17, 0x01, 0xFA, 0xFE, + 0xB3, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, + 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, + 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x73, 0xFF, + 0x7D, 0x01, 0xB3, 0xFC, 0xBB, 0x06, 0x9A, 0xF1, 0x60, 0x34, 0xF5, + 0x2B, 0xB0, 0xF1, 0x28, 0x07, 0x47, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, + 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, + 0x00, 0x10, 0xFF, 0x15, 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, + 0xF1, 0xF5, 0xD3, 0x05, 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x19, 0x01, + 0x23, 0xFE, 0xB0, 0x02, 0x87, 0xFC, 0x41, 0x04, 0xF4, 0x48, 0x1C, + 0x03, 0x06, 0xFD, 0x6E, 0x02, 0x45, 0xFE, 0x09, 0x01, 0x88, 0xFF, + 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, + 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, + 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4F, 0xFF, 0xC1, 0x01, 0x52, 0xFC, + 0x22, 0x07, 0x98, 0xF1, 0x5E, 0x2D, 0x13, 0x33, 0x87, 0xF1, 0xD8, + 0x06, 0x9B, 0xFC, 0x8D, 0x01, 0x6B, 0xFF, 0x25, 0x00, 0xFD, 0xFF, + 0x03, 0x00, 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, + 0x04, 0x10, 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, + 0x4E, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, + 0x00, 0xA3, 0xFF, 0xC3, 0x00, 0xD7, 0xFE, 0x58, 0x01, 0x0F, 0xFF, + 0xA6, 0xFE, 0x5D, 0x48, 0x61, 0x09, 0x6E, 0xFA, 0xC0, 0x03, 0x99, + 0xFD, 0x59, 0x01, 0x68, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2E, 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, + 0xF7, 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, + 0x8E, 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x16, 0x07, 0x67, 0xF2, + 0xE5, 0x25, 0x87, 0x39, 0x47, 0xF2, 0x10, 0x06, 0x30, 0xFD, 0x2F, + 0x01, 0x9C, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x00, + 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, + 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC5, 0xFF, 0x6D, + 0x00, 0x8B, 0xFF, 0x0D, 0x00, 0x63, 0x01, 0xF9, 0xF9, 0x55, 0x46, + 0x51, 0x10, 0xE3, 0xF7, 0xF7, 0x04, 0x03, 0xFD, 0x9D, 0x01, 0x4E, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, + 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, + 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, + 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, + 0x01, 0x4D, 0xFC, 0xA3, 0x06, 0xE4, 0xF3, 0x36, 0x1E, 0x16, 0x3F, + 0x05, 0xF4, 0xCF, 0x04, 0x02, 0xFE, 0xB2, 0x00, 0xDB, 0xFF, 0xFC, + 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, 0x8B, 0x01, + 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, 0x2D, 0x9A, + 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, 0x2F, 0x00, + 0xFD, 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x1A, 0x00, 0x33, 0x00, 0xDF, + 0xFE, 0x68, 0x03, 0x4E, 0xF6, 0xEE, 0x42, 0xBB, 0x17, 0x90, 0xF5, + 0xFC, 0x05, 0x8E, 0xFC, 0xCD, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, 0x02, + 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, 0xA9, + 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC7, 0x01, 0x9D, 0xFC, 0xD8, + 0x05, 0xE7, 0xF5, 0x91, 0x16, 0x89, 0x43, 0xCD, 0xF6, 0x1E, 0x03, + 0x0B, 0xFF, 0x1A, 0x00, 0x26, 0x00, 0xE0, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, 0x48, 0xFC, 0x28, 0x07, + 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, 0xF1, 0xBE, 0x06, 0xB0, + 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, + 0x00, 0x00, 0xD0, 0xFF, 0xC7, 0x00, 0xDE, 0xFD, 0x08, 0x05, 0xB0, + 0xF3, 0x4A, 0x3E, 0x64, 0x1F, 0xA0, 0xF3, 0xBB, 0x06, 0x45, 0xFC, + 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA9, + 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, 0x7A, 0xFF, 0xC5, 0xFD, + 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, 0x03, 0x7D, 0xFD, 0x66, + 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, + 0x52, 0xFF, 0x93, 0x01, 0x18, 0xFD, 0xC9, 0x04, 0x45, 0xF8, 0x36, + 0x0F, 0xBB, 0x46, 0xA1, 0xFA, 0x0C, 0x01, 0x3E, 0x00, 0x70, 0xFF, + 0x7A, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, + 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, + 0x8F, 0x3A, 0x82, 0xF2, 0xE1, 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, + 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x17, 0x00, 0x93, 0xFF, + 0x3F, 0x01, 0x15, 0xFD, 0x36, 0x06, 0x19, 0xF2, 0x97, 0x38, 0x11, + 0x27, 0x3B, 0xF2, 0x1F, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, + 0xFF, 0xD6, 0xFF, 0xC3, 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, + 0x77, 0xF7, 0x28, 0x05, 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6D, 0xFF, 0x4E, 0x01, + 0xB3, 0xFD, 0x8D, 0x03, 0xD4, 0xFA, 0x5D, 0x08, 0x8D, 0x48, 0x74, + 0xFF, 0xAE, 0xFE, 0x8D, 0x01, 0xBB, 0xFE, 0xD1, 0x00, 0x9E, 0xFF, + 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, + 0xFC, 0x85, 0x06, 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, + 0x8C, 0x04, 0x2C, 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, + 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x65, 0xFF, 0x98, 0x01, 0x8A, 0xFC, + 0xEC, 0x06, 0x7F, 0xF1, 0x04, 0x32, 0x7B, 0x2E, 0x8A, 0xF1, 0x1A, + 0x07, 0x5D, 0xFC, 0xB8, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, + 0x06, 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, + 0x03, 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, + 0x7D, 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, + 0x00, 0x8D, 0xFF, 0xFC, 0x00, 0x61, 0xFE, 0x39, 0x02, 0x6B, 0xFD, + 0x37, 0x02, 0xEB, 0x48, 0x31, 0x05, 0x21, 0xFC, 0xE4, 0x02, 0x08, + 0xFE, 0x26, 0x01, 0x7C, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x32, 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, + 0xF6, 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, + 0xFE, 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x32, + 0x00, 0x47, 0xFF, 0xD0, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xCA, 0xF1, + 0xD1, 0x2A, 0x65, 0x35, 0xAE, 0xF1, 0xA0, 0x06, 0xC7, 0xFC, 0x70, + 0x01, 0x7A, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, + 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, + 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA5, + 0x00, 0x16, 0xFF, 0xE3, 0x00, 0xE4, 0xFF, 0xEB, 0xFC, 0xD2, 0x47, + 0xB6, 0x0B, 0x89, 0xF9, 0x2F, 0x04, 0x62, 0xFD, 0x72, 0x01, 0x5E, + 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x56, 0xFF, + 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, 0x26, + 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, 0x00, + 0xBA, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, + 0x01, 0x34, 0xFC, 0xF9, 0x06, 0xD9, 0xF2, 0x3F, 0x23, 0x90, 0x3B, + 0xC4, 0xF2, 0xAE, 0x05, 0x72, 0xFD, 0x07, 0x01, 0xB0, 0xFF, 0x0C, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, 0x51, 0x01, + 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, 0x28, 0x0E, + 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x4F, 0x00, 0xC7, 0xFF, 0xA0, + 0xFF, 0x20, 0x02, 0x96, 0xF8, 0x4E, 0x45, 0xD7, 0x12, 0x0D, 0xF7, + 0x58, 0x05, 0xD6, 0xFC, 0xB0, 0x01, 0x47, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, 0x01, 0xD0, 0xFD, + 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, 0x62, 0x00, 0x3F, + 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, 0xFF, 0x19, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x63, 0xFC, 0x66, + 0x06, 0x89, 0xF4, 0x8C, 0x1B, 0xC3, 0x40, 0xDD, 0xF4, 0x46, 0x04, + 0x58, 0xFE, 0x80, 0x00, 0xF4, 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, + 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, + 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, + 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, + 0xEF, 0xFF, 0xFF, 0xFF, 0x69, 0x00, 0x80, 0xFE, 0x04, 0x04, 0x48, + 0xF5, 0x74, 0x41, 0x5D, 0x1A, 0xD7, 0xF4, 0x47, 0x06, 0x6F, 0xFC, + 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x93, + 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, 0x01, 0xDC, 0xFD, 0x3C, 0x01, + 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, 0x1F, 0x03, 0xEA, 0xFD, 0x34, + 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, + 0x44, 0xFF, 0xB8, 0x01, 0xC3, 0xFC, 0x81, 0x05, 0xB0, 0xF6, 0xFA, + 0x13, 0xCC, 0x44, 0x02, 0xF8, 0x71, 0x02, 0x71, 0xFF, 0xE1, 0xFF, + 0x42, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, + 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, + 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, + 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x09, 0x00, 0xBA, 0xFF, + 0xF4, 0x00, 0x91, 0xFD, 0x7E, 0x05, 0x05, 0xF3, 0x6E, 0x3C, 0x10, + 0x22, 0x12, 0xF3, 0xE9, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, + 0xFF, 0xA9, 0x00, 0x4D, 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, + 0x18, 0xF9, 0x66, 0x04, 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5A, 0xFF, 0x7D, 0x01, + 0x4B, 0xFD, 0x60, 0x04, 0x24, 0xF9, 0xC6, 0x0C, 0x86, 0x47, 0x30, + 0xFC, 0x41, 0x00, 0xB0, 0x00, 0x32, 0xFF, 0x98, 0x00, 0xB4, 0xFF, + 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, + 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, + 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x81, 0xFF, 0x62, 0x01, 0xDD, 0xFC, + 0x83, 0x06, 0xC9, 0xF1, 0x66, 0x36, 0xAC, 0x29, 0xE7, 0xF1, 0x2A, + 0x07, 0x3A, 0xFC, 0xD5, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, + 0x02, 0xF0, 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, + 0xC1, 0xFC, 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x77, 0xFF, 0x32, 0x01, 0xED, 0xFD, 0x19, 0x03, + 0xBB, 0xFB, 0x26, 0x06, 0xD7, 0x48, 0x58, 0x01, 0xCF, 0xFD, 0x04, + 0x02, 0x7D, 0xFE, 0xEF, 0x00, 0x92, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, + 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, + 0xF4, 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, + 0x66, 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2B, + 0x00, 0x59, 0xFF, 0xB0, 0x01, 0x69, 0xFC, 0x0F, 0x07, 0x80, 0xF1, + 0x96, 0x2F, 0xF2, 0x30, 0x7C, 0xF1, 0xFD, 0x06, 0x7A, 0xFC, 0xA3, + 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF4, 0xFF, + 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, + 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x98, 0xFF, 0xDE, + 0x00, 0x9F, 0xFE, 0xC2, 0x01, 0x4B, 0xFE, 0x48, 0x00, 0xB3, 0x48, + 0x5E, 0x07, 0x3B, 0xFB, 0x59, 0x03, 0xCD, 0xFD, 0x42, 0x01, 0x71, + 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, + 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, + 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, + 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDB, + 0x01, 0x35, 0xFC, 0x25, 0x07, 0x13, 0xF2, 0x3A, 0x28, 0xA0, 0x37, + 0xF2, 0xF1, 0x5A, 0x06, 0xFB, 0xFC, 0x4F, 0x01, 0x8B, 0xFF, 0x1A, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, 0x09, 0x01, + 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, 0x23, 0xD2, + 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x11, 0x00, 0xBB, 0xFF, 0x87, 0x00, 0x54, 0xFF, 0x70, + 0x00, 0xB3, 0x00, 0x4E, 0xFB, 0x1A, 0x47, 0x1F, 0x0E, 0xA8, 0xF8, + 0x9B, 0x04, 0x2E, 0xFD, 0x8A, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, 0xFD, + 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, 0xD9, + 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD0, + 0x06, 0x5E, 0xF3, 0x94, 0x20, 0x7B, 0x3D, 0x60, 0xF3, 0x3E, 0x05, + 0xBB, 0xFD, 0xDB, 0x00, 0xC6, 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, + 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, 0xC4, 0xFC, 0xA4, 0x06, + 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, 0xF1, 0x2A, 0x07, 0x40, + 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, + 0xDB, 0xFF, 0x33, 0x00, 0x01, 0x00, 0x38, 0xFF, 0xD3, 0x02, 0x53, + 0xF7, 0x1F, 0x44, 0x69, 0x15, 0x3F, 0xF6, 0xB2, 0x05, 0xAD, 0xFC, + 0xC1, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, + 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, 0xDE, 0x02, 0x2E, 0xFC, + 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, 0xFD, 0x3F, 0x02, 0x5D, + 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3B, 0xFF, 0xD3, 0x01, 0x7F, 0xFC, 0x1F, 0x06, 0x3C, 0xF5, 0xE6, + 0x18, 0x4D, 0x42, 0xD5, 0xF5, 0xAF, 0x03, 0xB4, 0xFE, 0x4B, 0x00, + 0x0E, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, + 0xFF, 0xBA, 0x01, 0x5B, 0xFC, 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, + 0x26, 0x32, 0x80, 0xF1, 0xEA, 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, + 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, 0xFF, 0xE6, 0xFF, + 0x9C, 0x00, 0x27, 0xFE, 0x94, 0x04, 0x61, 0xF4, 0xD7, 0x3F, 0x06, + 0x1D, 0x2B, 0xF4, 0x89, 0x06, 0x56, 0xFC, 0xE0, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, + 0xFE, 0x86, 0x01, 0xBA, 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, + 0xC7, 0xFA, 0x93, 0x03, 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA6, 0x01, + 0xEE, 0xFC, 0x23, 0x05, 0x83, 0xF7, 0x6E, 0x11, 0xE5, 0x45, 0x57, + 0xF9, 0xB8, 0x01, 0xDC, 0xFF, 0xA5, 0xFF, 0x5F, 0x00, 0xCA, 0xFF, + 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, + 0xFC, 0x1E, 0x07, 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, + 0x32, 0x06, 0x18, 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA4, 0xFF, 0x1D, 0x01, 0x4C, 0xFD, + 0xE6, 0x05, 0x7B, 0xF2, 0x71, 0x3A, 0xB8, 0x24, 0x97, 0xF2, 0x0B, + 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x0F, 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, + 0x01, 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, + 0x15, 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x64, 0xFF, 0x65, 0x01, 0x81, 0xFD, 0xF2, 0x03, + 0x08, 0xFA, 0x68, 0x0A, 0x25, 0x48, 0xDE, 0xFD, 0x6E, 0xFF, 0x24, + 0x01, 0xF3, 0xFE, 0xB6, 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, + 0xF3, 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, + 0xC4, 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x23, + 0x00, 0x71, 0xFF, 0x81, 0x01, 0xAE, 0xFC, 0xC1, 0x06, 0x95, 0xF1, + 0x1E, 0x34, 0x3E, 0x2C, 0xAB, 0xF1, 0x27, 0x07, 0x49, 0xFC, 0xC8, + 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, + 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, + 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, + 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, + 0xFF, 0x16, 0x01, 0x2A, 0xFE, 0xA3, 0x02, 0xA1, 0xFC, 0x06, 0x04, + 0xF5, 0x48, 0x56, 0x03, 0xED, 0xFC, 0x7B, 0x02, 0x3E, 0xFE, 0x0C, + 0x01, 0x86, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, 0x02, + 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, 0x00, + 0xE4, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, + 0x01, 0x4F, 0xFC, 0x24, 0x07, 0x9C, 0xF1, 0x17, 0x2D, 0x57, 0x33, + 0x8A, 0xF1, 0xD3, 0x06, 0x9F, 0xFC, 0x8A, 0x01, 0x6D, 0xFF, 0x25, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, 0xB4, 0x00, + 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, + 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC0, 0x00, 0xDE, 0xFE, 0x4B, + 0x01, 0x27, 0xFF, 0x73, 0xFE, 0x4F, 0x48, 0xA2, 0x09, 0x54, 0xFA, + 0xCC, 0x03, 0x93, 0xFD, 0x5C, 0x01, 0x67, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, 0x01, 0x05, 0xFD, + 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, 0x0D, 0xFA, 0x58, + 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x14, + 0x07, 0x73, 0xF2, 0x99, 0x25, 0xC2, 0x39, 0x54, 0xF2, 0x05, 0x06, + 0x37, 0xFD, 0x2B, 0x01, 0x9E, 0xFF, 0x13, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, + 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, + 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC6, 0xFF, 0x69, 0x00, 0x91, 0xFF, 0x00, 0x00, 0x78, 0x01, 0xD0, + 0xF9, 0x39, 0x46, 0x98, 0x10, 0xCB, 0xF7, 0x02, 0x05, 0xFE, 0xFC, + 0x9F, 0x01, 0x4D, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, + 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, 0xFD, 0xB9, 0x03, 0x7B, 0xFA, + 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, 0x03, 0xFF, 0x5F, 0x01, 0xD4, + 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE2, 0x01, 0x4F, 0xFC, 0x9C, 0x06, 0xF5, 0xF3, 0xEA, + 0x1D, 0x47, 0x3F, 0x1B, 0xF4, 0xC1, 0x04, 0x0B, 0xFE, 0xAC, 0x00, + 0xDE, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, + 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, + 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x17, 0x00, + 0x39, 0x00, 0xD4, 0xFE, 0x7A, 0x03, 0x2F, 0xF6, 0xC7, 0x42, 0x06, + 0x18, 0x7B, 0xF5, 0x05, 0x06, 0x8A, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, + 0xFE, 0x67, 0x02, 0x13, 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, + 0x7A, 0xFC, 0xB6, 0x02, 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, + 0xA1, 0xFC, 0xCF, 0x05, 0xFC, 0xF5, 0x47, 0x16, 0xB0, 0x43, 0xEE, + 0xF6, 0x0C, 0x03, 0x16, 0xFF, 0x14, 0x00, 0x29, 0x00, 0xDF, 0xFF, + 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, + 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, + 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCE, 0xFF, 0xCC, 0x00, 0xD5, 0xFD, + 0x16, 0x05, 0x9B, 0xF3, 0x18, 0x3E, 0xB1, 0x1F, 0x8F, 0xF3, 0xC0, + 0x06, 0x43, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x15, 0x00, 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, + 0xFF, 0x94, 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, + 0x77, 0xFD, 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x91, 0x01, 0x1E, 0xFD, 0xBE, 0x04, + 0x5E, 0xF8, 0xF0, 0x0E, 0xD3, 0x46, 0xCB, 0xFA, 0xF6, 0x00, 0x4B, + 0x00, 0x69, 0xFF, 0x7D, 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, + 0xF2, 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, + 0x17, 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, + 0x00, 0x91, 0xFF, 0x43, 0x01, 0x0E, 0xFD, 0x40, 0x06, 0x0F, 0xF2, + 0x5B, 0x38, 0x5C, 0x27, 0x30, 0xF2, 0x21, 0x07, 0x33, 0xFC, 0xDE, + 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCC, 0xFF, + 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, + 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, + 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6E, + 0xFF, 0x4B, 0x01, 0xB9, 0xFD, 0x80, 0x03, 0xEE, 0xFA, 0x1D, 0x08, + 0x98, 0x48, 0xA8, 0xFF, 0x95, 0xFE, 0x9A, 0x01, 0xB4, 0xFE, 0xD4, + 0x00, 0x9C, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, + 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, + 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9B, + 0x01, 0x86, 0xFC, 0xF1, 0x06, 0x7E, 0xF1, 0xC0, 0x31, 0xC2, 0x2E, + 0x87, 0xF1, 0x17, 0x07, 0x5F, 0xFC, 0xB6, 0x01, 0x55, 0xFF, 0x2D, + 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, 0x54, 0x00, + 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, 0x19, 0x1E, + 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1C, 0x00, 0x8E, 0xFF, 0xF9, 0x00, 0x68, 0xFE, 0x2C, + 0x02, 0x84, 0xFD, 0xFF, 0x01, 0xE6, 0x48, 0x6E, 0x05, 0x07, 0xFC, + 0xF1, 0x02, 0x01, 0xFE, 0x29, 0x01, 0x7B, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, 0xFC, + 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, 0xB6, + 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, 0x00, + 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3E, 0xFC, 0x2B, + 0x07, 0xD0, 0xF1, 0x89, 0x2A, 0xA6, 0x35, 0xB4, 0xF1, 0x99, 0x06, + 0xCD, 0xFC, 0x6D, 0x01, 0x7C, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, + 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, 0xAE, 0xFD, 0x52, 0x05, + 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, 0xF3, 0xD8, 0x06, 0x3C, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, + 0xB0, 0xFF, 0xA2, 0x00, 0x1D, 0xFF, 0xD6, 0x00, 0xFC, 0xFF, 0xBC, + 0xFC, 0xC0, 0x47, 0xFA, 0x0B, 0x70, 0xF9, 0x3C, 0x04, 0x5C, 0xFD, + 0x75, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, 0x89, 0x04, 0xCD, 0xF8, + 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, 0x00, 0x83, 0x00, 0x4A, + 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF5, 0x06, 0xE7, 0xF2, 0xF2, + 0x22, 0xC7, 0x3B, 0xD4, 0xF2, 0xA2, 0x05, 0x7A, 0xFD, 0x02, 0x01, + 0xB2, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, + 0xFF, 0x55, 0x01, 0xF2, 0xFC, 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, + 0xAA, 0x28, 0x05, 0xF2, 0x27, 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, + 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD2, 0xFF, 0x4C, 0x00, + 0xCD, 0xFF, 0x94, 0xFF, 0x34, 0x02, 0x70, 0xF8, 0x2E, 0x45, 0x20, + 0x13, 0xF6, 0xF6, 0x62, 0x05, 0xD1, 0xFC, 0xB2, 0x01, 0x46, 0xFF, + 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, + 0x01, 0xD6, 0xFD, 0x46, 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, + 0x98, 0x00, 0x26, 0xFE, 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, + 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, + 0x66, 0xFC, 0x5E, 0x06, 0x9C, 0xF4, 0x40, 0x1B, 0xEF, 0x40, 0xF7, + 0xF4, 0x35, 0x04, 0x62, 0xFE, 0x7A, 0x00, 0xF7, 0xFF, 0xF2, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, + 0xFC, 0x03, 0x07, 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, + 0x0A, 0x07, 0x6E, 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, + 0xFF, 0x05, 0x00, 0xF0, 0xFF, 0xFC, 0xFF, 0x6E, 0x00, 0x76, 0xFE, + 0x15, 0x04, 0x2C, 0xF5, 0x49, 0x41, 0xA9, 0x1A, 0xC3, 0xF4, 0x4F, + 0x06, 0x6C, 0xFC, 0xD9, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, + 0xFD, 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, + 0xE4, 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB6, 0x01, 0xC8, 0xFC, 0x77, 0x05, + 0xC7, 0xF6, 0xB1, 0x13, 0xED, 0x44, 0x26, 0xF8, 0x5D, 0x02, 0x7D, + 0xFF, 0xDA, 0xFF, 0x46, 0x00, 0xD4, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, + 0xF1, 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, + 0x5C, 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, + 0x00, 0xB7, 0xFF, 0xF9, 0x00, 0x89, 0xFD, 0x8A, 0x05, 0xF4, 0xF2, + 0x37, 0x3C, 0x5B, 0x22, 0x03, 0xF3, 0xED, 0x06, 0x37, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB6, 0xFF, + 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, + 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, + 0xFF, 0x7A, 0x01, 0x50, 0xFD, 0x54, 0x04, 0x3D, 0xF9, 0x82, 0x0C, + 0x9A, 0x47, 0x5E, 0xFC, 0x2A, 0x00, 0xBD, 0x00, 0x2B, 0xFF, 0x9B, + 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, 0xC0, + 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, 0xFF, + 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x80, 0xFF, 0x66, + 0x01, 0xD8, 0xFC, 0x8B, 0x06, 0xC1, 0xF1, 0x27, 0x36, 0xF6, 0x29, + 0xDF, 0xF1, 0x2A, 0x07, 0x3B, 0xFC, 0xD4, 0x01, 0x44, 0xFF, 0x32, + 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, 0xEA, 0xFF, + 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, 0x14, 0x8E, + 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, 0x32, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x79, 0xFF, 0x2F, 0x01, 0xF4, + 0xFD, 0x0C, 0x03, 0xD4, 0xFB, 0xE9, 0x05, 0xDE, 0x48, 0x8F, 0x01, + 0xB6, 0xFD, 0x11, 0x02, 0x76, 0xFE, 0xF2, 0x00, 0x91, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, 0x01, 0x73, 0xFC, + 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, 0x71, 0xF5, 0xEB, + 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, 0xFF, 0x06, 0x00, + 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB2, 0x01, 0x65, 0xFC, 0x12, + 0x07, 0x82, 0xF1, 0x50, 0x2F, 0x38, 0x31, 0x7C, 0xF1, 0xF9, 0x06, + 0x7E, 0xFC, 0xA1, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, + 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, + 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, + 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, + 0x9A, 0xFF, 0xDB, 0x00, 0xA6, 0xFE, 0xB4, 0x01, 0x64, 0xFE, 0x12, + 0x00, 0xAA, 0x48, 0x9E, 0x07, 0x21, 0xFB, 0x66, 0x03, 0xC6, 0xFD, + 0x45, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, 0xFC, 0x48, 0x05, 0x30, 0xF7, + 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, 0x01, 0x02, 0xB2, 0xFF, 0xBD, + 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x24, 0x07, 0x1C, 0xF2, 0xF0, + 0x27, 0xDF, 0x37, 0xFB, 0xF1, 0x51, 0x06, 0x01, 0xFD, 0x4B, 0x01, + 0x8D, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, + 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, + 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBC, 0xFF, 0x84, 0x00, + 0x5B, 0xFF, 0x64, 0x00, 0xC9, 0x00, 0x22, 0xFB, 0x02, 0x47, 0x64, + 0x0E, 0x8F, 0xF8, 0xA7, 0x04, 0x29, 0xFD, 0x8C, 0x01, 0x54, 0xFF, + 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, + 0x01, 0x6B, 0xFD, 0x1D, 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, + 0x33, 0xFD, 0xC1, 0xFF, 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, + 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x40, 0xFC, 0xCB, 0x06, 0x6E, 0xF3, 0x49, 0x20, 0xB0, 0x3D, 0x73, + 0xF3, 0x31, 0x05, 0xC4, 0xFD, 0xD6, 0x00, 0xC8, 0xFF, 0x03, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, + 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, + 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x2F, 0x00, 0x07, 0x00, 0x2C, 0xFF, + 0xE6, 0x02, 0x31, 0xF7, 0xFA, 0x43, 0xB3, 0x15, 0x29, 0xF6, 0xBC, + 0x05, 0xA9, 0xFC, 0xC2, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, + 0x02, 0x47, 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, + 0x4D, 0x02, 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFE, + 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, 0x83, 0xFC, 0x16, 0x06, + 0x51, 0xF5, 0x9B, 0x18, 0x75, 0x42, 0xF3, 0xF5, 0x9D, 0x03, 0xBF, + 0xFE, 0x45, 0x00, 0x11, 0x00, 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, + 0x2E, 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, + 0xF1, 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, + 0x94, 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF9, + 0xFF, 0xE3, 0xFF, 0xA1, 0x00, 0x1E, 0xFE, 0xA3, 0x04, 0x49, 0xF4, + 0xA8, 0x3F, 0x52, 0x1D, 0x19, 0xF4, 0x90, 0x06, 0x53, 0xFC, 0xE1, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, + 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, + 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, + 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, + 0xFF, 0xA3, 0x01, 0xF3, 0xFC, 0x18, 0x05, 0x9B, 0xF7, 0x27, 0x11, + 0x02, 0x46, 0x7F, 0xF9, 0xA3, 0x01, 0xE8, 0xFF, 0x9F, 0xFF, 0x63, + 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, + 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, + 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, + 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA2, 0xFF, 0x22, + 0x01, 0x45, 0xFD, 0xF1, 0x05, 0x6D, 0xF2, 0x38, 0x3A, 0x03, 0x25, + 0x8B, 0xF2, 0x0E, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x3A, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, 0x7A, 0xFF, + 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, 0x0F, 0x20, + 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, 0x2E, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x62, 0x01, 0x87, + 0xFD, 0xE5, 0x03, 0x21, 0xFA, 0x25, 0x0A, 0x33, 0x48, 0x0F, 0xFE, + 0x57, 0xFF, 0x31, 0x01, 0xEC, 0xFE, 0xB9, 0x00, 0xA7, 0xFF, 0x15, + 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, 0xFC, + 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, 0xF3, + 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, 0x00, + 0xFE, 0xFF, 0x23, 0x00, 0x70, 0xFF, 0x84, 0x01, 0xA9, 0xFC, 0xC7, + 0x06, 0x91, 0xF1, 0xDC, 0x33, 0x87, 0x2C, 0xA5, 0xF1, 0x26, 0x07, + 0x4B, 0xFC, 0xC6, 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, 0xFA, 0xFE, 0x3A, 0x03, + 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, 0xF5, 0xE6, 0x05, 0x97, + 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, + 0x1E, 0x00, 0x84, 0xFF, 0x13, 0x01, 0x31, 0xFE, 0x95, 0x02, 0xBA, + 0xFC, 0xCB, 0x03, 0xF7, 0x48, 0x91, 0x03, 0xD3, 0xFC, 0x88, 0x02, + 0x38, 0xFE, 0x10, 0x01, 0x85, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, + 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, 0xEF, 0x05, 0xB0, 0xF5, + 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, 0x03, 0xEF, 0xFE, 0x2A, + 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4D, 0xFF, 0xC4, 0x01, 0x4D, 0xFC, 0x25, 0x07, 0xA1, 0xF1, 0xCE, + 0x2C, 0x99, 0x33, 0x8E, 0xF1, 0xCD, 0x06, 0xA4, 0xFC, 0x87, 0x01, + 0x6E, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD7, + 0xFF, 0xBA, 0x00, 0xF4, 0xFD, 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, + 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBD, 0x00, + 0xE5, 0xFE, 0x3E, 0x01, 0x3F, 0xFF, 0x41, 0xFE, 0x41, 0x48, 0xE4, + 0x09, 0x3B, 0xFA, 0xD9, 0x03, 0x8D, 0xFD, 0x5F, 0x01, 0x66, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, + 0x01, 0x0B, 0xFD, 0xE6, 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, + 0x37, 0xFA, 0x42, 0x01, 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, + 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, + 0x31, 0xFC, 0x11, 0x07, 0x7F, 0xF2, 0x4E, 0x25, 0xFD, 0x39, 0x60, + 0xF2, 0xFB, 0x05, 0x3E, 0xFD, 0x26, 0x01, 0xA0, 0xFF, 0x12, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, + 0xFD, 0x1E, 0x06, 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, + 0x1A, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x66, 0x00, 0x98, 0xFF, 0xF4, 0xFF, + 0x8E, 0x01, 0xA7, 0xF9, 0x1D, 0x46, 0xDF, 0x10, 0xB3, 0xF7, 0x0D, + 0x05, 0xF8, 0xFC, 0xA1, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, + 0x03, 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, + 0x6C, 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x51, 0xFC, 0x96, 0x06, + 0x07, 0xF4, 0x9E, 0x1D, 0x77, 0x3F, 0x32, 0xF4, 0xB2, 0x04, 0x15, + 0xFE, 0xA7, 0x00, 0xE0, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, + 0x26, 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, + 0xF1, 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, + 0xBE, 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE7, + 0xFF, 0x14, 0x00, 0x3F, 0x00, 0xC9, 0xFE, 0x8C, 0x03, 0x11, 0xF6, + 0x9E, 0x42, 0x50, 0x18, 0x66, 0xF5, 0x0D, 0x06, 0x86, 0xFC, 0xD0, + 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, + 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, + 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, + 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC4, 0x01, 0xA5, 0xFC, 0xC5, 0x05, 0x13, 0xF6, 0xFD, 0x15, + 0xD4, 0x43, 0x0F, 0xF7, 0xF9, 0x02, 0x21, 0xFF, 0x0D, 0x00, 0x2C, + 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, 0xFF, + 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, 0xC3, + 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, 0xFF, + 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, 0xCB, 0xFF, 0xD1, + 0x00, 0xCC, 0xFD, 0x24, 0x05, 0x87, 0xF3, 0xE4, 0x3D, 0xFD, 0x1F, + 0x7F, 0xF3, 0xC6, 0x06, 0x41, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, 0x05, 0xFF, + 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, 0x0B, 0xC8, + 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, 0x28, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x8F, 0x01, 0x23, + 0xFD, 0xB2, 0x04, 0x76, 0xF8, 0xAA, 0x0E, 0xED, 0x46, 0xF7, 0xFA, + 0xDF, 0x00, 0x57, 0x00, 0x62, 0xFF, 0x80, 0x00, 0xBD, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x33, 0xFC, + 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, 0x9E, 0xF2, 0xCB, + 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x18, 0x00, 0x8F, 0xFF, 0x47, 0x01, 0x08, 0xFD, 0x49, + 0x06, 0x05, 0xF2, 0x1D, 0x38, 0xA6, 0x27, 0x26, 0xF2, 0x23, 0x07, + 0x33, 0xFC, 0xDD, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, + 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, + 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, + 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x24, 0x00, 0x6F, 0xFF, 0x48, 0x01, 0xC0, 0xFD, 0x73, 0x03, 0x07, + 0xFB, 0xDD, 0x07, 0xA1, 0x48, 0xDD, 0xFF, 0x7D, 0xFE, 0xA7, 0x01, + 0xAD, 0xFE, 0xD8, 0x00, 0x9B, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, 0xFC, 0x78, 0x06, 0x5A, 0xF4, + 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, 0x6D, 0x04, 0x3F, 0xFE, 0x8E, + 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x62, 0xFF, 0x9E, 0x01, 0x82, 0xFC, 0xF5, 0x06, 0x7D, 0xF1, 0x7B, + 0x31, 0x09, 0x2F, 0x84, 0xF1, 0x15, 0x07, 0x62, 0xFC, 0xB4, 0x01, + 0x56, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, + 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, + 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x8F, 0xFF, 0xF5, 0x00, + 0x6F, 0xFE, 0x1E, 0x02, 0x9D, 0xFD, 0xC7, 0x01, 0xE1, 0x48, 0xAB, + 0x05, 0xEE, 0xFB, 0xFE, 0x02, 0xFB, 0xFD, 0x2C, 0x01, 0x7A, 0xFF, + 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, + 0x01, 0xB8, 0xFC, 0x9A, 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, + 0xA9, 0xF7, 0xA2, 0x02, 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD3, 0x01, + 0x3C, 0xFC, 0x2A, 0x07, 0xD8, 0xF1, 0x3F, 0x2A, 0xE6, 0x35, 0xBB, + 0xF1, 0x92, 0x06, 0xD2, 0xFC, 0x69, 0x01, 0x7E, 0xFF, 0x1F, 0x00, + 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, + 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, + 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0x9F, 0x00, 0x24, 0xFF, 0xC9, 0x00, + 0x13, 0x00, 0x8D, 0xFC, 0xAE, 0x47, 0x3E, 0x0C, 0x56, 0xF9, 0x48, + 0x04, 0x56, 0xFD, 0x78, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, + 0x04, 0xE6, 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, + 0x90, 0x00, 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xF1, 0x06, + 0xF5, 0xF2, 0xA7, 0x22, 0xFF, 0x3B, 0xE4, 0xF2, 0x96, 0x05, 0x81, + 0xFD, 0xFD, 0x00, 0xB5, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, + 0x1C, 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, + 0xF1, 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, + 0xD8, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, + 0xFF, 0x49, 0x00, 0xD4, 0xFF, 0x88, 0xFF, 0x49, 0x02, 0x4B, 0xF8, + 0x0D, 0x45, 0x68, 0x13, 0xDF, 0xF6, 0x6C, 0x05, 0xCC, 0xFC, 0xB4, + 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, + 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, + 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDA, 0x01, 0x69, 0xFC, 0x57, 0x06, 0xAF, 0xF4, 0xF5, 0x1A, + 0x1D, 0x41, 0x11, 0xF5, 0x25, 0x04, 0x6C, 0xFE, 0x74, 0x00, 0xF9, + 0xFF, 0xF1, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, + 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, + 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, + 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF1, 0xFF, 0xF9, 0xFF, 0x74, + 0x00, 0x6C, 0xFE, 0x25, 0x04, 0x11, 0xF5, 0x1D, 0x41, 0xF5, 0x1A, + 0xAF, 0xF4, 0x57, 0x06, 0x69, 0xFC, 0xDA, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, 0x8E, 0xFE, + 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, 0x06, 0x7B, + 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, 0x23, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB4, 0x01, 0xCC, + 0xFC, 0x6C, 0x05, 0xDF, 0xF6, 0x68, 0x13, 0x0D, 0x45, 0x4B, 0xF8, + 0x49, 0x02, 0x88, 0xFF, 0xD4, 0xFF, 0x49, 0x00, 0xD3, 0xFF, 0x0B, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, 0xFC, + 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, 0x6F, + 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, + 0x01, 0x00, 0x0B, 0x00, 0xB5, 0xFF, 0xFD, 0x00, 0x81, 0xFD, 0x96, + 0x05, 0xE4, 0xF2, 0xFF, 0x3B, 0xA7, 0x22, 0xF5, 0xF2, 0xF1, 0x06, + 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, + 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, 0x90, 0x00, 0x7A, 0x00, + 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, 0xF8, 0x7E, 0x04, 0x3C, + 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x5C, 0xFF, 0x78, 0x01, 0x56, 0xFD, 0x48, 0x04, 0x56, + 0xF9, 0x3E, 0x0C, 0xAE, 0x47, 0x8D, 0xFC, 0x13, 0x00, 0xC9, 0x00, + 0x24, 0xFF, 0x9F, 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, 0xDD, 0x06, 0x37, 0xF3, + 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, 0x05, 0xA6, 0xFD, 0xE8, + 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, + 0x7E, 0xFF, 0x69, 0x01, 0xD2, 0xFC, 0x92, 0x06, 0xBB, 0xF1, 0xE6, + 0x35, 0x3F, 0x2A, 0xD8, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, + 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, + 0x00, 0xF1, 0xFF, 0x54, 0xFF, 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, + 0xB1, 0x14, 0x77, 0xF6, 0x9A, 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, + 0x2C, 0x01, 0xFB, 0xFD, 0xFE, 0x02, 0xEE, 0xFB, 0xAB, 0x05, 0xE1, + 0x48, 0xC7, 0x01, 0x9D, 0xFD, 0x1E, 0x02, 0x6F, 0xFE, 0xF5, 0x00, + 0x8F, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, + 0x01, 0x77, 0xFC, 0x33, 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, + 0x8D, 0xF5, 0xDA, 0x03, 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x56, 0xFF, 0xB4, 0x01, + 0x62, 0xFC, 0x15, 0x07, 0x84, 0xF1, 0x09, 0x2F, 0x7B, 0x31, 0x7D, + 0xF1, 0xF5, 0x06, 0x82, 0xFC, 0x9E, 0x01, 0x62, 0xFF, 0x28, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, + 0xFE, 0x6D, 0x04, 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, + 0x78, 0x06, 0x5C, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x18, 0x00, 0x9B, 0xFF, 0xD8, 0x00, 0xAD, 0xFE, 0xA7, 0x01, + 0x7D, 0xFE, 0xDD, 0xFF, 0xA1, 0x48, 0xDD, 0x07, 0x07, 0xFB, 0x73, + 0x03, 0xC0, 0xFD, 0x48, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, + 0x05, 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, + 0xBE, 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDD, 0x01, 0x33, 0xFC, 0x23, 0x07, + 0x26, 0xF2, 0xA6, 0x27, 0x1D, 0x38, 0x05, 0xF2, 0x49, 0x06, 0x08, + 0xFD, 0x47, 0x01, 0x8F, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x0E, 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, + 0xF2, 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, + 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBD, + 0xFF, 0x80, 0x00, 0x62, 0xFF, 0x57, 0x00, 0xDF, 0x00, 0xF7, 0xFA, + 0xED, 0x46, 0xAA, 0x0E, 0x76, 0xF8, 0xB2, 0x04, 0x23, 0xFD, 0x8F, + 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, + 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, + 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x41, 0xFC, 0xC6, 0x06, 0x7F, 0xF3, 0xFD, 0x1F, + 0xE4, 0x3D, 0x87, 0xF3, 0x24, 0x05, 0xCC, 0xFD, 0xD1, 0x00, 0xCB, + 0xFF, 0x02, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, 0xFF, + 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, 0x89, + 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2C, 0x00, 0x0D, + 0x00, 0x21, 0xFF, 0xF9, 0x02, 0x0F, 0xF7, 0xD4, 0x43, 0xFD, 0x15, + 0x13, 0xF6, 0xC5, 0x05, 0xA5, 0xFC, 0xC4, 0x01, 0x40, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0x1E, 0x01, + 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, 0x48, 0xC6, + 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, 0x8A, 0xFF, + 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD0, 0x01, 0x86, + 0xFC, 0x0D, 0x06, 0x66, 0xF5, 0x50, 0x18, 0x9E, 0x42, 0x11, 0xF6, + 0x8C, 0x03, 0xC9, 0xFE, 0x3F, 0x00, 0x14, 0x00, 0xE7, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, 0x01, 0x56, 0xFC, + 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, 0x84, 0xF1, 0xE0, + 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, + 0x03, 0x00, 0xFA, 0xFF, 0xE0, 0xFF, 0xA7, 0x00, 0x15, 0xFE, 0xB2, + 0x04, 0x32, 0xF4, 0x77, 0x3F, 0x9E, 0x1D, 0x07, 0xF4, 0x96, 0x06, + 0x51, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, + 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, + 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, + 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2F, 0x00, 0x4C, 0xFF, 0xA1, 0x01, 0xF8, 0xFC, 0x0D, 0x05, 0xB3, + 0xF7, 0xDF, 0x10, 0x1D, 0x46, 0xA7, 0xF9, 0x8E, 0x01, 0xF4, 0xFF, + 0x98, 0xFF, 0x66, 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, 0xFC, 0x1A, 0x07, 0x56, 0xF2, + 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, 0x1E, 0x06, 0x25, 0xFD, 0x35, + 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x12, 0x00, + 0xA0, 0xFF, 0x26, 0x01, 0x3E, 0xFD, 0xFB, 0x05, 0x60, 0xF2, 0xFD, + 0x39, 0x4E, 0x25, 0x7F, 0xF2, 0x11, 0x07, 0x31, 0xFC, 0xE3, 0x01, + 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, + 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, + 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, + 0x5F, 0x01, 0x8D, 0xFD, 0xD9, 0x03, 0x3B, 0xFA, 0xE4, 0x09, 0x41, + 0x48, 0x41, 0xFE, 0x3F, 0xFF, 0x3E, 0x01, 0xE5, 0xFE, 0xBD, 0x00, + 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x4A, 0xFC, 0xAC, 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, + 0xE4, 0xF3, 0xE5, 0x04, 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, + 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6E, 0xFF, 0x87, 0x01, + 0xA4, 0xFC, 0xCD, 0x06, 0x8E, 0xF1, 0x99, 0x33, 0xCE, 0x2C, 0xA1, + 0xF1, 0x25, 0x07, 0x4D, 0xFC, 0xC4, 0x01, 0x4D, 0xFF, 0x2F, 0x00, + 0xFD, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, + 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, + 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x85, 0xFF, 0x10, 0x01, 0x38, 0xFE, 0x88, 0x02, + 0xD3, 0xFC, 0x91, 0x03, 0xF7, 0x48, 0xCB, 0x03, 0xBA, 0xFC, 0x95, + 0x02, 0x31, 0xFE, 0x13, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, + 0x05, 0xC6, 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, + 0xFA, 0xFE, 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4C, 0xFF, 0xC6, 0x01, 0x4B, 0xFC, 0x26, 0x07, + 0xA5, 0xF1, 0x87, 0x2C, 0xDC, 0x33, 0x91, 0xF1, 0xC7, 0x06, 0xA9, + 0xFC, 0x84, 0x01, 0x70, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x03, 0x00, + 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, + 0xF3, 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, + 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA7, + 0xFF, 0xB9, 0x00, 0xEC, 0xFE, 0x31, 0x01, 0x57, 0xFF, 0x0F, 0xFE, + 0x33, 0x48, 0x25, 0x0A, 0x21, 0xFA, 0xE5, 0x03, 0x87, 0xFD, 0x62, + 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, + 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, + 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, + 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, + 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0E, 0x07, 0x8B, 0xF2, 0x03, 0x25, + 0x38, 0x3A, 0x6D, 0xF2, 0xF1, 0x05, 0x45, 0xFD, 0x22, 0x01, 0xA2, + 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, + 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, + 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, + 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x63, 0x00, 0x9F, + 0xFF, 0xE8, 0xFF, 0xA3, 0x01, 0x7F, 0xF9, 0x02, 0x46, 0x27, 0x11, + 0x9B, 0xF7, 0x18, 0x05, 0xF3, 0xFC, 0xA3, 0x01, 0x4C, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, 0x52, 0x01, + 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, 0x48, 0x26, + 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, 0xA0, 0xFF, + 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE1, 0x01, 0x53, + 0xFC, 0x90, 0x06, 0x19, 0xF4, 0x52, 0x1D, 0xA8, 0x3F, 0x49, 0xF4, + 0xA3, 0x04, 0x1E, 0xFE, 0xA1, 0x00, 0xE3, 0xFF, 0xF9, 0xFF, 0x04, + 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, 0xFC, + 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, 0x1D, + 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x07, 0x00, 0xE8, 0xFF, 0x11, 0x00, 0x45, 0x00, 0xBF, 0xFE, 0x9D, + 0x03, 0xF3, 0xF5, 0x75, 0x42, 0x9B, 0x18, 0x51, 0xF5, 0x16, 0x06, + 0x83, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, + 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, 0x4D, 0x02, 0x45, 0xFD, + 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, 0xFC, 0xD1, 0x02, 0x12, + 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x33, 0x00, 0x40, 0xFF, 0xC2, 0x01, 0xA9, 0xFC, 0xBC, 0x05, 0x29, + 0xF6, 0xB3, 0x15, 0xFA, 0x43, 0x31, 0xF7, 0xE6, 0x02, 0x2C, 0xFF, + 0x07, 0x00, 0x2F, 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, + 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, 0x2A, 0x07, 0xBF, 0xF1, + 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, 0x06, 0xBF, 0xFC, 0x75, + 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, + 0xC8, 0xFF, 0xD6, 0x00, 0xC4, 0xFD, 0x31, 0x05, 0x73, 0xF3, 0xB0, + 0x3D, 0x49, 0x20, 0x6E, 0xF3, 0xCB, 0x06, 0x40, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, + 0x00, 0x0C, 0xFF, 0xF7, 0x00, 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, + 0x51, 0x0B, 0xAF, 0xF9, 0x1D, 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, + 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, + 0x8C, 0x01, 0x29, 0xFD, 0xA7, 0x04, 0x8F, 0xF8, 0x64, 0x0E, 0x02, + 0x47, 0x22, 0xFB, 0xC9, 0x00, 0x64, 0x00, 0x5B, 0xFF, 0x84, 0x00, + 0xBC, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0xFF, 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, + 0xAD, 0xF2, 0xBF, 0x05, 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8D, 0xFF, 0x4B, 0x01, + 0x01, 0xFD, 0x51, 0x06, 0xFB, 0xF1, 0xDF, 0x37, 0xF0, 0x27, 0x1C, + 0xF2, 0x24, 0x07, 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, + 0xFF, 0x01, 0x02, 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, + 0x48, 0x05, 0xDD, 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x45, 0x01, 0xC6, 0xFD, + 0x66, 0x03, 0x21, 0xFB, 0x9E, 0x07, 0xAA, 0x48, 0x12, 0x00, 0x64, + 0xFE, 0xB4, 0x01, 0xA6, 0xFE, 0xDB, 0x00, 0x9A, 0xFF, 0x19, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, + 0x06, 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, + 0x49, 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFD, + 0xFF, 0x29, 0x00, 0x61, 0xFF, 0xA1, 0x01, 0x7E, 0xFC, 0xF9, 0x06, + 0x7C, 0xF1, 0x38, 0x31, 0x50, 0x2F, 0x82, 0xF1, 0x12, 0x07, 0x65, + 0xFC, 0xB2, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, + 0xED, 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, + 0xF5, 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, + 0xD7, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, + 0xFF, 0xF2, 0x00, 0x76, 0xFE, 0x11, 0x02, 0xB6, 0xFD, 0x8F, 0x01, + 0xDE, 0x48, 0xE9, 0x05, 0xD4, 0xFB, 0x0C, 0x03, 0xF4, 0xFD, 0x2F, + 0x01, 0x79, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, + 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, + 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, + 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, + 0xFF, 0xD4, 0x01, 0x3B, 0xFC, 0x2A, 0x07, 0xDF, 0xF1, 0xF6, 0x29, + 0x27, 0x36, 0xC1, 0xF1, 0x8B, 0x06, 0xD8, 0xFC, 0x66, 0x01, 0x80, + 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xBD, 0xFF, + 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, 0x9E, + 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x9B, 0x00, 0x2B, + 0xFF, 0xBD, 0x00, 0x2A, 0x00, 0x5E, 0xFC, 0x9A, 0x47, 0x82, 0x0C, + 0x3D, 0xF9, 0x54, 0x04, 0x50, 0xFD, 0x7A, 0x01, 0x5B, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x81, 0x01, + 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, 0x47, 0xEB, + 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, 0xB6, 0xFF, + 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, + 0xFC, 0xED, 0x06, 0x03, 0xF3, 0x5B, 0x22, 0x37, 0x3C, 0xF4, 0xF2, + 0x8A, 0x05, 0x89, 0xFD, 0xF9, 0x00, 0xB7, 0xFF, 0x0A, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, 0x01, 0xE6, 0xFC, + 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, 0xF3, 0xF1, 0x29, + 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD4, 0xFF, 0x46, 0x00, 0xDA, 0xFF, 0x7D, 0xFF, 0x5D, + 0x02, 0x26, 0xF8, 0xED, 0x44, 0xB1, 0x13, 0xC7, 0xF6, 0x77, 0x05, + 0xC8, 0xFC, 0xB6, 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, + 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, + 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x35, 0x00, 0x38, 0xFF, 0xD9, 0x01, 0x6C, 0xFC, 0x4F, 0x06, 0xC3, + 0xF4, 0xA9, 0x1A, 0x49, 0x41, 0x2C, 0xF5, 0x15, 0x04, 0x76, 0xFE, + 0x6E, 0x00, 0xFC, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, + 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, 0xFC, 0x0A, 0x07, 0x7E, 0xF1, + 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, 0x03, 0x07, 0x75, 0xFC, 0xA7, + 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF2, 0xFF, + 0xF7, 0xFF, 0x7A, 0x00, 0x62, 0xFE, 0x35, 0x04, 0xF7, 0xF4, 0xEF, + 0x40, 0x40, 0x1B, 0x9C, 0xF4, 0x5E, 0x06, 0x66, 0xFC, 0xDB, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, + 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, + 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, + 0xB2, 0x01, 0xD1, 0xFC, 0x62, 0x05, 0xF6, 0xF6, 0x20, 0x13, 0x2E, + 0x45, 0x70, 0xF8, 0x34, 0x02, 0x94, 0xFF, 0xCD, 0xFF, 0x4C, 0x00, + 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, + 0x01, 0x36, 0xFC, 0x27, 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, + 0xE4, 0xF1, 0x67, 0x06, 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, 0x00, 0xB2, 0xFF, 0x02, 0x01, + 0x7A, 0xFD, 0xA2, 0x05, 0xD4, 0xF2, 0xC7, 0x3B, 0xF2, 0x22, 0xE7, + 0xF2, 0xF5, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, + 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, + 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x75, 0x01, 0x5C, 0xFD, + 0x3C, 0x04, 0x70, 0xF9, 0xFA, 0x0B, 0xC0, 0x47, 0xBC, 0xFC, 0xFC, + 0xFF, 0xD6, 0x00, 0x1D, 0xFF, 0xA2, 0x00, 0xB0, 0xFF, 0x13, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, + 0x06, 0x47, 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, + 0xAE, 0xFD, 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0xFE, + 0xFF, 0x1F, 0x00, 0x7C, 0xFF, 0x6D, 0x01, 0xCD, 0xFC, 0x99, 0x06, + 0xB4, 0xF1, 0xA6, 0x35, 0x89, 0x2A, 0xD0, 0xF1, 0x2B, 0x07, 0x3E, + 0xFC, 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, + 0xD9, 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, + 0xF7, 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, + 0xBE, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, + 0x00, 0x7B, 0xFF, 0x29, 0x01, 0x01, 0xFE, 0xF1, 0x02, 0x07, 0xFC, + 0x6E, 0x05, 0xE6, 0x48, 0xFF, 0x01, 0x84, 0xFD, 0x2C, 0x02, 0x68, + 0xFE, 0xF9, 0x00, 0x8E, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, + 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, + 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x55, + 0xFF, 0xB6, 0x01, 0x5F, 0xFC, 0x17, 0x07, 0x87, 0xF1, 0xC2, 0x2E, + 0xC0, 0x31, 0x7E, 0xF1, 0xF1, 0x06, 0x86, 0xFC, 0x9B, 0x01, 0x63, + 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, + 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, + 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9C, 0xFF, 0xD4, 0x00, 0xB4, + 0xFE, 0x9A, 0x01, 0x95, 0xFE, 0xA8, 0xFF, 0x98, 0x48, 0x1D, 0x08, + 0xEE, 0xFA, 0x80, 0x03, 0xB9, 0xFD, 0x4B, 0x01, 0x6E, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA9, 0x01, + 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, 0x45, 0x1C, + 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, 0xCC, 0xFF, + 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDE, 0x01, 0x33, + 0xFC, 0x21, 0x07, 0x30, 0xF2, 0x5C, 0x27, 0x5B, 0x38, 0x0F, 0xF2, + 0x40, 0x06, 0x0E, 0xFD, 0x43, 0x01, 0x91, 0xFF, 0x18, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, 0xFD, + 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, 0x06, + 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x10, 0x00, 0xBE, 0xFF, 0x7D, 0x00, 0x69, 0xFF, 0x4B, 0x00, 0xF6, + 0x00, 0xCB, 0xFA, 0xD3, 0x46, 0xF0, 0x0E, 0x5E, 0xF8, 0xBE, 0x04, + 0x1E, 0xFD, 0x91, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, 0x77, 0xFD, 0x04, 0x04, + 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, 0xFD, 0x92, 0xFF, 0x10, + 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x43, 0xFC, 0xC0, 0x06, 0x8F, + 0xF3, 0xB1, 0x1F, 0x18, 0x3E, 0x9B, 0xF3, 0x16, 0x05, 0xD5, 0xFD, + 0xCC, 0x00, 0xCE, 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, + 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, 0xB8, 0x06, 0x9C, 0xF1, + 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, 0x07, 0x46, 0xFC, 0xCA, + 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, + 0x29, 0x00, 0x14, 0x00, 0x16, 0xFF, 0x0C, 0x03, 0xEE, 0xF6, 0xB0, + 0x43, 0x47, 0x16, 0xFC, 0xF5, 0xCF, 0x05, 0xA1, 0xFC, 0xC6, 0x01, + 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, + 0xFF, 0x1B, 0x01, 0x20, 0xFE, 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, + 0xF4, 0x48, 0xFF, 0x02, 0x13, 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, + 0x01, 0x88, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, + 0xCF, 0x01, 0x8A, 0xFC, 0x05, 0x06, 0x7B, 0xF5, 0x06, 0x18, 0xC7, + 0x42, 0x2F, 0xF6, 0x7A, 0x03, 0xD4, 0xFE, 0x39, 0x00, 0x17, 0x00, + 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, + 0x01, 0x53, 0xFC, 0x21, 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, + 0x86, 0xF1, 0xDB, 0x06, 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDE, 0xFF, 0xAC, 0x00, + 0x0B, 0xFE, 0xC1, 0x04, 0x1B, 0xF4, 0x47, 0x3F, 0xEA, 0x1D, 0xF5, + 0xF3, 0x9C, 0x06, 0x4F, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, + 0x01, 0x03, 0xFF, 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, + 0xB9, 0x03, 0x9D, 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4D, 0xFF, 0x9F, 0x01, 0xFE, 0xFC, + 0x02, 0x05, 0xCB, 0xF7, 0x98, 0x10, 0x39, 0x46, 0xD0, 0xF9, 0x78, + 0x01, 0x00, 0x00, 0x91, 0xFF, 0x69, 0x00, 0xC6, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, + 0x07, 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, + 0x2C, 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x13, 0x00, 0x9E, 0xFF, 0x2B, 0x01, 0x37, 0xFD, 0x05, 0x06, + 0x54, 0xF2, 0xC2, 0x39, 0x99, 0x25, 0x73, 0xF2, 0x14, 0x07, 0x31, + 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC4, 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, + 0xFA, 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, + 0x9C, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, + 0x00, 0x67, 0xFF, 0x5C, 0x01, 0x93, 0xFD, 0xCC, 0x03, 0x54, 0xFA, + 0xA2, 0x09, 0x4F, 0x48, 0x73, 0xFE, 0x27, 0xFF, 0x4B, 0x01, 0xDE, + 0xFE, 0xC0, 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, + 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, + 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6D, + 0xFF, 0x8A, 0x01, 0x9F, 0xFC, 0xD3, 0x06, 0x8A, 0xF1, 0x57, 0x33, + 0x17, 0x2D, 0x9C, 0xF1, 0x24, 0x07, 0x4F, 0xFC, 0xC3, 0x01, 0x4E, + 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE4, 0xFF, 0x1B, 0x00, + 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, 0x96, + 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x86, 0xFF, 0x0C, 0x01, 0x3E, + 0xFE, 0x7B, 0x02, 0xED, 0xFC, 0x56, 0x03, 0xF5, 0x48, 0x06, 0x04, + 0xA1, 0xFC, 0xA3, 0x02, 0x2A, 0xFE, 0x16, 0x01, 0x83, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC8, 0x01, + 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, 0x43, 0xBD, + 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, 0xE1, 0xFF, + 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC8, 0x01, 0x49, + 0xFC, 0x27, 0x07, 0xAB, 0xF1, 0x3E, 0x2C, 0x1E, 0x34, 0x95, 0xF1, + 0xC1, 0x06, 0xAE, 0xFC, 0x81, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, 0x00, 0xE2, 0xFD, + 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, 0xA8, 0xF3, 0xB8, + 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x15, 0x00, 0xA8, 0xFF, 0xB6, 0x00, 0xF3, 0xFE, 0x24, 0x01, 0x6E, + 0xFF, 0xDE, 0xFD, 0x25, 0x48, 0x68, 0x0A, 0x08, 0xFA, 0xF2, 0x03, + 0x81, 0xFD, 0x65, 0x01, 0x64, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, + 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, + 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0B, 0x07, 0x97, + 0xF2, 0xB8, 0x24, 0x71, 0x3A, 0x7B, 0xF2, 0xE6, 0x05, 0x4C, 0xFD, + 0x1D, 0x01, 0xA4, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, + 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, 0xFD, 0x32, 0x06, 0x1F, 0xF2, + 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, 0x1E, 0x07, 0x32, 0xFC, 0xDF, + 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, + 0x5F, 0x00, 0xA5, 0xFF, 0xDC, 0xFF, 0xB8, 0x01, 0x57, 0xF9, 0xE5, + 0x45, 0x6E, 0x11, 0x83, 0xF7, 0x23, 0x05, 0xEE, 0xFC, 0xA6, 0x01, + 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, + 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, + 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, + 0x00, 0x9E, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE0, 0x01, 0x56, 0xFC, 0x89, 0x06, 0x2B, 0xF4, 0x06, 0x1D, 0xD7, + 0x3F, 0x61, 0xF4, 0x94, 0x04, 0x27, 0xFE, 0x9C, 0x00, 0xE6, 0xFF, + 0xF8, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, + 0x01, 0x8C, 0xFC, 0xEA, 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, + 0x8B, 0xF1, 0x1B, 0x07, 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, + 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, 0xFF, 0x0E, 0x00, 0x4B, 0x00, + 0xB4, 0xFE, 0xAF, 0x03, 0xD5, 0xF5, 0x4D, 0x42, 0xE6, 0x18, 0x3C, + 0xF5, 0x1F, 0x06, 0x7F, 0xFC, 0xD3, 0x01, 0x3B, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, + 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, + 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xC1, 0x01, 0xAD, 0xFC, + 0xB2, 0x05, 0x3F, 0xF6, 0x69, 0x15, 0x1F, 0x44, 0x53, 0xF7, 0xD3, + 0x02, 0x38, 0xFF, 0x01, 0x00, 0x33, 0x00, 0xDB, 0xFF, 0x09, 0x00, + 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, + 0x07, 0xC6, 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, + 0xC4, 0xFC, 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, + 0x00, 0x04, 0x00, 0xC6, 0xFF, 0xDB, 0x00, 0xBB, 0xFD, 0x3E, 0x05, + 0x60, 0xF3, 0x7B, 0x3D, 0x94, 0x20, 0x5E, 0xF3, 0xD0, 0x06, 0x3E, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, + 0xAE, 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, + 0xFD, 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, + 0x71, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, + 0x00, 0x55, 0xFF, 0x8A, 0x01, 0x2E, 0xFD, 0x9B, 0x04, 0xA8, 0xF8, + 0x1F, 0x0E, 0x1A, 0x47, 0x4E, 0xFB, 0xB3, 0x00, 0x70, 0x00, 0x54, + 0xFF, 0x87, 0x00, 0xBB, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, + 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, + 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8B, + 0xFF, 0x4F, 0x01, 0xFB, 0xFC, 0x5A, 0x06, 0xF2, 0xF1, 0xA0, 0x37, + 0x3A, 0x28, 0x13, 0xF2, 0x25, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, + 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, + 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, + 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, + 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x42, + 0x01, 0xCD, 0xFD, 0x59, 0x03, 0x3B, 0xFB, 0x5E, 0x07, 0xB3, 0x48, + 0x48, 0x00, 0x4B, 0xFE, 0xC2, 0x01, 0x9F, 0xFE, 0xDE, 0x00, 0x98, + 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDD, 0x01, + 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, 0x40, 0xD0, + 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, 0xF4, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA3, 0x01, 0x7A, + 0xFC, 0xFD, 0x06, 0x7C, 0xF1, 0xF2, 0x30, 0x96, 0x2F, 0x80, 0xF1, + 0x0F, 0x07, 0x69, 0xFC, 0xB0, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, + 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, 0xFE, + 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, 0x43, + 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0x1B, 0x00, 0x92, 0xFF, 0xEF, 0x00, 0x7D, 0xFE, 0x04, 0x02, 0xCF, + 0xFD, 0x58, 0x01, 0xD7, 0x48, 0x26, 0x06, 0xBB, 0xFB, 0x19, 0x03, + 0xED, 0xFD, 0x32, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, 0xC1, 0xFC, 0x86, 0x05, + 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, 0xF7, 0x7B, 0x02, 0x6B, + 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x43, 0xFF, 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE7, + 0xF1, 0xAC, 0x29, 0x66, 0x36, 0xC9, 0xF1, 0x83, 0x06, 0xDD, 0xFC, + 0x62, 0x01, 0x81, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x08, + 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, 0x78, 0x05, 0x0E, 0xF3, + 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, 0x06, 0x38, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB4, 0xFF, + 0x98, 0x00, 0x32, 0xFF, 0xB0, 0x00, 0x41, 0x00, 0x30, 0xFC, 0x86, + 0x47, 0xC6, 0x0C, 0x24, 0xF9, 0x60, 0x04, 0x4B, 0xFD, 0x7D, 0x01, + 0x5A, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x5A, + 0xFF, 0x7E, 0x01, 0x48, 0xFD, 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, + 0x7C, 0x47, 0x19, 0xFC, 0x4D, 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, + 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x38, 0xFC, 0xE9, 0x06, 0x12, 0xF3, 0x10, 0x22, 0x6E, + 0x3C, 0x05, 0xF3, 0x7E, 0x05, 0x91, 0xFD, 0xF4, 0x00, 0xBA, 0xFF, + 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, + 0x01, 0xE0, 0xFC, 0x7F, 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, + 0xEB, 0xF1, 0x2A, 0x07, 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, 0xFF, 0x42, 0x00, 0xE1, 0xFF, + 0x71, 0xFF, 0x71, 0x02, 0x02, 0xF8, 0xCC, 0x44, 0xFA, 0x13, 0xB0, + 0xF6, 0x81, 0x05, 0xC3, 0xFC, 0xB8, 0x01, 0x44, 0xFF, 0x31, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, + 0xFD, 0x1F, 0x03, 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, + 0xDC, 0xFD, 0xFD, 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x6F, 0xFC, + 0x47, 0x06, 0xD7, 0xF4, 0x5D, 0x1A, 0x74, 0x41, 0x48, 0xF5, 0x04, + 0x04, 0x80, 0xFE, 0x69, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0x05, 0x00, + 0xFD, 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, + 0x07, 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, + 0x78, 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, + 0x00, 0xF3, 0xFF, 0xF4, 0xFF, 0x80, 0x00, 0x58, 0xFE, 0x46, 0x04, + 0xDD, 0xF4, 0xC3, 0x40, 0x8C, 0x1B, 0x89, 0xF4, 0x66, 0x06, 0x63, + 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, + 0x98, 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, + 0x00, 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, + 0x40, 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x47, 0xFF, 0xB0, 0x01, 0xD6, 0xFC, 0x58, 0x05, 0x0D, 0xF7, + 0xD7, 0x12, 0x4E, 0x45, 0x96, 0xF8, 0x20, 0x02, 0xA0, 0xFF, 0xC7, + 0xFF, 0x4F, 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, + 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, + 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0xB0, + 0xFF, 0x07, 0x01, 0x72, 0xFD, 0xAE, 0x05, 0xC4, 0xF2, 0x90, 0x3B, + 0x3F, 0x23, 0xD9, 0xF2, 0xF9, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, 0x00, + 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, 0xFC, + 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, 0xFF, + 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5E, 0xFF, 0x72, + 0x01, 0x62, 0xFD, 0x2F, 0x04, 0x89, 0xF9, 0xB6, 0x0B, 0xD2, 0x47, + 0xEB, 0xFC, 0xE4, 0xFF, 0xE3, 0x00, 0x16, 0xFF, 0xA5, 0x00, 0xAF, + 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, 0x3D, 0x56, + 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, 0x05, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x7A, 0xFF, 0x70, 0x01, 0xC7, + 0xFC, 0xA0, 0x06, 0xAE, 0xF1, 0x65, 0x35, 0xD1, 0x2A, 0xCA, 0xF1, + 0x2A, 0x07, 0x40, 0xFC, 0xD0, 0x01, 0x47, 0xFF, 0x32, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x3D, 0xFF, + 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, 0x4A, 0xF6, 0xAD, + 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, 0x26, 0x01, 0x08, 0xFE, 0xE4, + 0x02, 0x21, 0xFC, 0x31, 0x05, 0xEB, 0x48, 0x37, 0x02, 0x6B, 0xFD, + 0x39, 0x02, 0x61, 0xFE, 0xFC, 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFE, + 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, + 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, + 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFD, 0xFF, + 0x2D, 0x00, 0x54, 0xFF, 0xB8, 0x01, 0x5D, 0xFC, 0x1A, 0x07, 0x8A, + 0xF1, 0x7B, 0x2E, 0x04, 0x32, 0x7F, 0xF1, 0xEC, 0x06, 0x8A, 0xFC, + 0x98, 0x01, 0x65, 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, + 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, 0xFE, 0x8C, 0x04, 0x6D, 0xF4, + 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, 0x85, 0x06, 0x57, 0xFC, 0xE0, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9E, 0xFF, + 0xD1, 0x00, 0xBB, 0xFE, 0x8D, 0x01, 0xAE, 0xFE, 0x74, 0xFF, 0x8D, + 0x48, 0x5D, 0x08, 0xD4, 0xFA, 0x8D, 0x03, 0xB3, 0xFD, 0x4E, 0x01, + 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, + 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, + 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, + 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xDF, 0x01, 0x32, 0xFC, 0x1F, 0x07, 0x3B, 0xF2, 0x11, 0x27, 0x97, + 0x38, 0x19, 0xF2, 0x36, 0x06, 0x15, 0xFD, 0x3F, 0x01, 0x93, 0xFF, + 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, + 0x01, 0x50, 0xFD, 0xE1, 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, + 0x9D, 0xF2, 0x09, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0x7A, 0x00, 0x70, 0xFF, + 0x3E, 0x00, 0x0C, 0x01, 0xA1, 0xFA, 0xBB, 0x46, 0x36, 0x0F, 0x45, + 0xF8, 0xC9, 0x04, 0x18, 0xFD, 0x93, 0x01, 0x52, 0xFF, 0x2D, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, + 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, + 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x45, 0xFC, + 0xBB, 0x06, 0xA0, 0xF3, 0x64, 0x1F, 0x4A, 0x3E, 0xB0, 0xF3, 0x08, + 0x05, 0xDE, 0xFD, 0xC7, 0x00, 0xD0, 0xFF, 0x00, 0x00, 0x02, 0x00, + 0xFE, 0xFF, 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, + 0x06, 0x97, 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, + 0x48, 0xFC, 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE0, 0xFF, 0x26, 0x00, 0x1A, 0x00, 0x0B, 0xFF, 0x1E, 0x03, + 0xCD, 0xF6, 0x89, 0x43, 0x91, 0x16, 0xE7, 0xF5, 0xD8, 0x05, 0x9D, + 0xFC, 0xC7, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x1F, 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, + 0xFC, 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, + 0x42, 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, + 0x00, 0x3C, 0xFF, 0xCD, 0x01, 0x8E, 0xFC, 0xFC, 0x05, 0x90, 0xF5, + 0xBB, 0x17, 0xEE, 0x42, 0x4E, 0xF6, 0x68, 0x03, 0xDF, 0xFE, 0x33, + 0x00, 0x1A, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, + 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, + 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0xDB, + 0xFF, 0xB2, 0x00, 0x02, 0xFE, 0xCF, 0x04, 0x05, 0xF4, 0x16, 0x3F, + 0x36, 0x1E, 0xE4, 0xF3, 0xA3, 0x06, 0x4D, 0xFC, 0xE3, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, + 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, + 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, + 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9D, + 0x01, 0x03, 0xFD, 0xF7, 0x04, 0xE3, 0xF7, 0x51, 0x10, 0x55, 0x46, + 0xF9, 0xF9, 0x63, 0x01, 0x0D, 0x00, 0x8B, 0xFF, 0x6D, 0x00, 0xC5, + 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, + 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, 0x39, 0x4D, + 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, 0x13, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x9C, 0xFF, 0x2F, 0x01, 0x30, + 0xFD, 0x10, 0x06, 0x47, 0xF2, 0x87, 0x39, 0xE5, 0x25, 0x67, 0xF2, + 0x16, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, 0x00, + 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, 0xFC, + 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x59, 0x01, 0x99, 0xFD, 0xC0, + 0x03, 0x6E, 0xFA, 0x61, 0x09, 0x5D, 0x48, 0xA6, 0xFE, 0x0F, 0xFF, + 0x58, 0x01, 0xD7, 0xFE, 0xC3, 0x00, 0xA3, 0xFF, 0x16, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4E, 0xFC, 0xA0, 0x06, + 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, 0xF4, 0xC8, 0x04, 0x07, + 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, 0x03, 0x00, 0xFD, 0xFF, + 0x25, 0x00, 0x6B, 0xFF, 0x8D, 0x01, 0x9B, 0xFC, 0xD8, 0x06, 0x87, + 0xF1, 0x13, 0x33, 0x5E, 0x2D, 0x98, 0xF1, 0x22, 0x07, 0x52, 0xFC, + 0xC1, 0x01, 0x4F, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE5, + 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, 0x71, 0x03, 0x3F, 0xF6, + 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, 0x06, 0x8C, 0xFC, 0xCE, + 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, + 0x09, 0x01, 0x45, 0xFE, 0x6E, 0x02, 0x06, 0xFD, 0x1C, 0x03, 0xF4, + 0x48, 0x41, 0x04, 0x87, 0xFC, 0xB0, 0x02, 0x23, 0xFE, 0x19, 0x01, + 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, + 0xFF, 0xC6, 0x01, 0x9F, 0xFC, 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, + 0x9E, 0x43, 0xDD, 0xF6, 0x15, 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, + 0x00, 0xDF, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xCA, 0x01, 0x47, 0xFC, 0x28, 0x07, 0xB0, 0xF1, 0xF5, 0x2B, 0x60, + 0x34, 0x9A, 0xF1, 0xBB, 0x06, 0xB3, 0xFC, 0x7D, 0x01, 0x73, 0xFF, + 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, + 0x00, 0xDA, 0xFD, 0x0F, 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, + 0x97, 0xF3, 0xBD, 0x06, 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xAA, 0xFF, 0xB3, 0x00, 0xFA, 0xFE, + 0x17, 0x01, 0x86, 0xFF, 0xAC, 0xFD, 0x16, 0x48, 0xAA, 0x0A, 0xEE, + 0xF9, 0xFE, 0x03, 0x7A, 0xFD, 0x67, 0x01, 0x63, 0xFF, 0x28, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, + 0xFD, 0xC4, 0x04, 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, + 0x01, 0x01, 0x44, 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, + 0x08, 0x07, 0xA4, 0xF2, 0x6D, 0x24, 0xAD, 0x3A, 0x88, 0xF2, 0xDB, + 0x05, 0x53, 0xFD, 0x19, 0x01, 0xA7, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, + 0x06, 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, + 0x33, 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, + 0x00, 0xCB, 0xFF, 0x5C, 0x00, 0xAC, 0xFF, 0xD0, 0xFF, 0xCD, 0x01, + 0x30, 0xF9, 0xC8, 0x45, 0xB6, 0x11, 0x6B, 0xF7, 0x2D, 0x05, 0xE9, + 0xFC, 0xA8, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x25, 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, + 0xFA, 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, + 0xB8, 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x58, 0xFC, 0x82, 0x06, 0x3E, 0xF4, + 0xBA, 0x1C, 0x07, 0x40, 0x79, 0xF4, 0x84, 0x04, 0x31, 0xFE, 0x96, + 0x00, 0xE8, 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, + 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, + 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEA, 0xFF, 0x0B, + 0x00, 0x51, 0x00, 0xAA, 0xFE, 0xC0, 0x03, 0xB8, 0xF5, 0x21, 0x42, + 0x31, 0x19, 0x28, 0xF5, 0x27, 0x06, 0x7C, 0xFC, 0xD4, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, 0x00, + 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, 0x50, + 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, 0xFF, + 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xBF, + 0x01, 0xB2, 0xFC, 0xA9, 0x05, 0x55, 0xF6, 0x20, 0x15, 0x42, 0x44, + 0x75, 0xF7, 0xBF, 0x02, 0x43, 0xFF, 0xFA, 0xFF, 0x36, 0x00, 0xDA, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, + 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, 0x35, 0xB1, + 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, 0x20, 0x00, + 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE0, 0x00, 0xB3, + 0xFD, 0x4B, 0x05, 0x4D, 0xF3, 0x45, 0x3D, 0xE0, 0x20, 0x4F, 0xF3, + 0xD5, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, 0xFF, 0xDD, 0x00, + 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, 0x7C, 0xF9, 0x35, + 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2C, 0x00, 0x56, 0xFF, 0x87, 0x01, 0x34, 0xFD, 0x8F, + 0x04, 0xC0, 0xF8, 0xD9, 0x0D, 0x31, 0x47, 0x7B, 0xFB, 0x9C, 0x00, + 0x7D, 0x00, 0x4D, 0xFF, 0x8A, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, + 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, + 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x89, 0xFF, 0x53, 0x01, 0xF5, 0xFC, 0x63, 0x06, 0xE9, + 0xF1, 0x63, 0x37, 0x85, 0x28, 0x09, 0xF2, 0x27, 0x07, 0x35, 0xFC, + 0xDA, 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD1, + 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, 0xFF, 0x2A, 0x02, 0x83, 0xF8, + 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, 0x5D, 0x05, 0xD3, 0xFC, 0xB1, + 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x73, 0xFF, 0x3F, 0x01, 0xD3, 0xFD, 0x4C, 0x03, 0x54, 0xFB, 0x1F, + 0x07, 0xBB, 0x48, 0x7D, 0x00, 0x33, 0xFE, 0xCF, 0x01, 0x98, 0xFE, + 0xE2, 0x00, 0x97, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, + 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, + 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5E, 0xFF, + 0xA6, 0x01, 0x76, 0xFC, 0x01, 0x07, 0x7D, 0xF1, 0xAD, 0x30, 0xDC, + 0x2F, 0x7F, 0xF1, 0x0C, 0x07, 0x6C, 0xFC, 0xAD, 0x01, 0x5A, 0xFF, + 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, + 0x00, 0x7B, 0xFE, 0x0C, 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, + 0xCD, 0xF4, 0x4B, 0x06, 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, + 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x93, 0xFF, 0xEC, 0x00, 0x83, 0xFE, + 0xF7, 0x01, 0xE8, 0xFD, 0x21, 0x01, 0xD2, 0x48, 0x64, 0x06, 0xA1, + 0xFB, 0x26, 0x03, 0xE7, 0xFD, 0x35, 0x01, 0x76, 0xFF, 0x22, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, + 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, + 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x39, 0xFC, + 0x29, 0x07, 0xEF, 0xF1, 0x62, 0x29, 0xA5, 0x36, 0xD0, 0xF1, 0x7B, + 0x06, 0xE3, 0xFC, 0x5E, 0x01, 0x83, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, + 0x01, 0x00, 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, + 0x05, 0xFD, 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, + 0x37, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, + 0x00, 0xB5, 0xFF, 0x94, 0x00, 0x39, 0xFF, 0xA3, 0x00, 0x58, 0x00, + 0x02, 0xFC, 0x73, 0x47, 0x0B, 0x0D, 0x0B, 0xF9, 0x6C, 0x04, 0x45, + 0xFD, 0x80, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, + 0xF9, 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, + 0x2E, 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x39, 0xFC, 0xE4, 0x06, 0x21, 0xF3, + 0xC4, 0x21, 0xA5, 0x3C, 0x16, 0xF3, 0x72, 0x05, 0x9A, 0xFD, 0xEF, + 0x00, 0xBC, 0xFF, 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, + 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD6, 0xFF, 0x3F, + 0x00, 0xE7, 0xFF, 0x65, 0xFF, 0x85, 0x02, 0xDE, 0xF7, 0xA9, 0x44, + 0x43, 0x14, 0x99, 0xF6, 0x8B, 0x05, 0xBF, 0xFC, 0xBA, 0x01, 0x43, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, + 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, + 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, + 0x91, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, + 0x01, 0x72, 0xFC, 0x3F, 0x06, 0xEB, 0xF4, 0x12, 0x1A, 0xA1, 0x41, + 0x63, 0xF5, 0xF3, 0x03, 0x8A, 0xFE, 0x63, 0x00, 0x02, 0x00, 0xEE, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, 0xB1, 0x01, + 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, 0x31, 0x7C, + 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, 0x29, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF4, 0xFF, 0xF1, 0xFF, 0x85, 0x00, 0x4E, + 0xFE, 0x56, 0x04, 0xC3, 0xF4, 0x95, 0x40, 0xD8, 0x1B, 0x76, 0xF4, + 0x6D, 0x06, 0x60, 0xFC, 0xDD, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, 0x01, + 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, 0x60, + 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAE, 0x01, 0xDB, 0xFC, 0x4D, + 0x05, 0x24, 0xF7, 0x8E, 0x12, 0x6D, 0x45, 0xBC, 0xF8, 0x0C, 0x02, + 0xAC, 0xFF, 0xC0, 0xFF, 0x52, 0x00, 0xCF, 0xFF, 0x0C, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x25, 0x07, + 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, 0xF1, 0x56, 0x06, 0xFE, + 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x0D, 0x00, 0xAE, 0xFF, 0x0B, 0x01, 0x6A, 0xFD, 0xBA, 0x05, 0xB4, + 0xF2, 0x58, 0x3B, 0x8A, 0x23, 0xCB, 0xF2, 0xFD, 0x06, 0x34, 0xFC, + 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBB, + 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, 0xBE, 0x00, 0x38, 0xFB, + 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, 0x04, 0x2B, 0xFD, 0x8B, + 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, + 0x5F, 0xFF, 0x70, 0x01, 0x68, 0xFD, 0x23, 0x04, 0xA2, 0xF9, 0x73, + 0x0B, 0xE4, 0x47, 0x1B, 0xFD, 0xCD, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, + 0xA9, 0x00, 0xAE, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE6, 0x01, 0x3F, 0xFC, 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, + 0x96, 0x3D, 0x69, 0xF3, 0x38, 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, + 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x78, 0xFF, + 0x74, 0x01, 0xC2, 0xFC, 0xA7, 0x06, 0xA8, 0xF1, 0x25, 0x35, 0x1B, + 0x2B, 0xC2, 0xF1, 0x2A, 0x07, 0x41, 0xFC, 0xCE, 0x01, 0x47, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, + 0x00, 0x32, 0xFF, 0xDC, 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, + 0x34, 0xF6, 0xB7, 0x05, 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x23, 0x01, + 0x0F, 0xFE, 0xD7, 0x02, 0x3B, 0xFC, 0xF5, 0x04, 0xED, 0x48, 0x70, + 0x02, 0x52, 0xFD, 0x46, 0x02, 0x5A, 0xFE, 0xFF, 0x00, 0x8B, 0xFF, + 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, + 0xFC, 0x1A, 0x06, 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, + 0xA6, 0x03, 0xB9, 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x53, 0xFF, 0xBB, 0x01, 0x5A, 0xFC, + 0x1C, 0x07, 0x8D, 0xF1, 0x34, 0x2E, 0x48, 0x32, 0x81, 0xF1, 0xE7, + 0x06, 0x8E, 0xFC, 0x96, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, + 0x04, 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, + 0x04, 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, + 0x55, 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, + 0x00, 0x9F, 0xFF, 0xCE, 0x00, 0xC2, 0xFE, 0x80, 0x01, 0xC6, 0xFE, + 0x40, 0xFF, 0x81, 0x48, 0x9E, 0x08, 0xBA, 0xFA, 0x9A, 0x03, 0xAC, + 0xFD, 0x51, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2F, 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, + 0xF7, 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, + 0xA2, 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3D, 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1D, 0x07, 0x45, 0xF2, + 0xC6, 0x26, 0xD3, 0x38, 0x24, 0xF2, 0x2D, 0x06, 0x1B, 0xFD, 0x3B, + 0x01, 0x95, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, + 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, + 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, + 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC1, 0xFF, 0x76, + 0x00, 0x76, 0xFF, 0x32, 0x00, 0x22, 0x01, 0x76, 0xFA, 0xA3, 0x46, + 0x7D, 0x0F, 0x2C, 0xF8, 0xD5, 0x04, 0x13, 0xFD, 0x96, 0x01, 0x51, + 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, 0xFF, + 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, 0x2C, + 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, 0x00, + 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x47, 0xFC, 0xB5, 0x06, 0xB0, 0xF3, 0x19, 0x1F, 0x7E, 0x3E, + 0xC4, 0xF3, 0xFA, 0x04, 0xE7, 0xFD, 0xC1, 0x00, 0xD3, 0xFF, 0xFF, + 0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, 0x82, 0x01, + 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, 0x2C, 0xA8, + 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, 0x30, 0x00, + 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, 0x23, 0x00, 0x20, 0x00, 0x00, + 0xFF, 0x31, 0x03, 0xAD, 0xF6, 0x65, 0x43, 0xDC, 0x16, 0xD1, 0xF5, + 0xE1, 0x05, 0x99, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, 0x01, 0x2D, 0xFE, + 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, 0x73, 0x03, 0xE0, + 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, 0xFF, 0x1E, 0x00, + 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCC, 0x01, 0x91, 0xFC, 0xF3, + 0x05, 0xA6, 0xF5, 0x70, 0x17, 0x17, 0x43, 0x6D, 0xF6, 0x56, 0x03, + 0xEA, 0xFE, 0x2D, 0x00, 0x1D, 0x00, 0xE4, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, + 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, + 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, + 0xFD, 0xFF, 0xD8, 0xFF, 0xB7, 0x00, 0xF9, 0xFD, 0xDE, 0x04, 0xEF, + 0xF3, 0xE4, 0x3E, 0x81, 0x1E, 0xD2, 0xF3, 0xA9, 0x06, 0x4B, 0xFC, + 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA5, + 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, 0x01, 0x33, 0xFF, 0x5A, 0xFE, + 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, 0xD2, 0x03, 0x90, 0xFD, 0x5E, + 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, + 0x4F, 0xFF, 0x9A, 0x01, 0x08, 0xFD, 0xEB, 0x04, 0xFC, 0xF7, 0x0A, + 0x10, 0x70, 0x46, 0x22, 0xFA, 0x4D, 0x01, 0x19, 0x00, 0x84, 0xFF, + 0x70, 0x00, 0xC4, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, + 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, + 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, + 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x99, 0xFF, + 0x33, 0x01, 0x29, 0xFD, 0x1A, 0x06, 0x3B, 0xF2, 0x4B, 0x39, 0x30, + 0x26, 0x5B, 0xF2, 0x19, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, + 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, + 0xFF, 0xFA, 0xFF, 0x83, 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, + 0xBF, 0xF7, 0x07, 0x05, 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x56, 0x01, + 0xA0, 0xFD, 0xB3, 0x03, 0x87, 0xFA, 0x1F, 0x09, 0x6A, 0x48, 0xD9, + 0xFE, 0xF6, 0xFE, 0x65, 0x01, 0xD0, 0xFE, 0xC7, 0x00, 0xA2, 0xFF, + 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, + 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, + 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, + 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x69, 0xFF, 0x90, 0x01, 0x96, 0xFC, + 0xDD, 0x06, 0x85, 0xF1, 0xD0, 0x32, 0xA6, 0x2D, 0x94, 0xF1, 0x20, + 0x07, 0x54, 0xFC, 0xBF, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x07, 0x00, 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, + 0x03, 0x20, 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, + 0x88, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, + 0x00, 0x89, 0xFF, 0x06, 0x01, 0x4C, 0xFE, 0x60, 0x02, 0x1F, 0xFD, + 0xE2, 0x02, 0xF3, 0x48, 0x7D, 0x04, 0x6E, 0xFC, 0xBD, 0x02, 0x1C, + 0xFE, 0x1C, 0x01, 0x80, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x33, 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, + 0xF6, 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, + 0x11, 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, + 0x00, 0x49, 0xFF, 0xCB, 0x01, 0x45, 0xFC, 0x29, 0x07, 0xB6, 0xF1, + 0xAD, 0x2B, 0xA2, 0x34, 0x9E, 0xF1, 0xB4, 0x06, 0xB8, 0xFC, 0x7A, + 0x01, 0x75, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, + 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, + 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAB, 0xFF, 0xAF, + 0x00, 0x01, 0xFF, 0x0A, 0x01, 0x9E, 0xFF, 0x7C, 0xFD, 0x03, 0x48, + 0xED, 0x0A, 0xD5, 0xF9, 0x0A, 0x04, 0x74, 0xFD, 0x6A, 0x01, 0x62, + 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, + 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, + 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, + 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0x04, 0x07, 0xB1, 0xF2, 0x21, 0x24, 0xE6, 0x3A, + 0x97, 0xF2, 0xD0, 0x05, 0x5B, 0xFD, 0x15, 0x01, 0xA9, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, 0x45, 0x01, + 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, 0x27, 0x2B, + 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0D, 0x00, 0xCD, 0xFF, 0x59, 0x00, 0xB3, 0xFF, 0xC4, + 0xFF, 0xE2, 0x01, 0x09, 0xF9, 0xAA, 0x45, 0xFE, 0x11, 0x54, 0xF7, + 0x38, 0x05, 0xE4, 0xFC, 0xAA, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, 0xFD, + 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, 0x89, + 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5B, 0xFC, 0x7B, + 0x06, 0x50, 0xF4, 0x6E, 0x1C, 0x36, 0x40, 0x92, 0xF4, 0x75, 0x04, + 0x3B, 0xFE, 0x91, 0x00, 0xEB, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, + 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, 0x84, 0xFC, 0xF3, 0x06, + 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, 0xF1, 0x16, 0x07, 0x61, + 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, + 0xEC, 0xFF, 0x08, 0x00, 0x57, 0x00, 0x9F, 0xFE, 0xD1, 0x03, 0x9B, + 0xF5, 0xF7, 0x41, 0x7C, 0x19, 0x13, 0xF5, 0x2F, 0x06, 0x78, 0xFC, + 0xD5, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8F, + 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, 0x91, 0xFD, 0xE3, 0x01, + 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, 0x02, 0xFE, 0xFD, 0x2B, + 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, + 0x42, 0xFF, 0xBD, 0x01, 0xB6, 0xFC, 0x9F, 0x05, 0x6C, 0xF6, 0xD6, + 0x14, 0x65, 0x44, 0x98, 0xF7, 0xAC, 0x02, 0x4E, 0xFF, 0xF4, 0xFF, + 0x39, 0x00, 0xD9, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, + 0xFF, 0xD2, 0x01, 0x3D, 0xFC, 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, + 0xC6, 0x35, 0xB7, 0xF1, 0x96, 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, + 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x06, 0x00, 0xC1, 0xFF, + 0xE5, 0x00, 0xAA, 0xFD, 0x58, 0x05, 0x3A, 0xF3, 0x11, 0x3D, 0x2C, + 0x21, 0x3F, 0xF3, 0xDA, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, + 0xFF, 0xD0, 0x00, 0x07, 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, + 0x63, 0xF9, 0x42, 0x04, 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x85, 0x01, + 0x39, 0xFD, 0x84, 0x04, 0xD9, 0xF8, 0x95, 0x0D, 0x48, 0x47, 0xA7, + 0xFB, 0x86, 0x00, 0x8A, 0x00, 0x46, 0xFF, 0x8E, 0x00, 0xB8, 0xFF, + 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, + 0xFC, 0xF3, 0x06, 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, + 0x9C, 0x05, 0x7E, 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x87, 0xFF, 0x57, 0x01, 0xEF, 0xFC, + 0x6B, 0x06, 0xE0, 0xF1, 0x23, 0x37, 0xCE, 0x28, 0x01, 0xF2, 0x28, + 0x07, 0x36, 0xFC, 0xD9, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, + 0x02, 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, + 0xCF, 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x23, 0x00, 0x74, 0xFF, 0x3C, 0x01, 0xDA, 0xFD, 0x40, 0x03, + 0x6E, 0xFB, 0xE1, 0x06, 0xC3, 0x48, 0xB3, 0x00, 0x1A, 0xFE, 0xDC, + 0x01, 0x91, 0xFE, 0xE5, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, + 0xF4, 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, + 0x77, 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, + 0x00, 0x5C, 0xFF, 0xA8, 0x01, 0x73, 0xFC, 0x05, 0x07, 0x7D, 0xF1, + 0x67, 0x30, 0x21, 0x30, 0x7E, 0xF1, 0x08, 0x07, 0x6F, 0xFC, 0xAB, + 0x01, 0x5B, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF0, 0xFF, + 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, + 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, + 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE8, + 0x00, 0x8A, 0xFE, 0xE9, 0x01, 0x01, 0xFE, 0xEA, 0x00, 0xCB, 0x48, + 0xA2, 0x06, 0x87, 0xFB, 0x33, 0x03, 0xE0, 0xFD, 0x39, 0x01, 0x75, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, + 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, 0xFD, + 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, 0x00, + 0xD3, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD8, + 0x01, 0x37, 0xFC, 0x29, 0x07, 0xF8, 0xF1, 0x19, 0x29, 0xE5, 0x36, + 0xD8, 0xF1, 0x73, 0x06, 0xE9, 0xFC, 0x5B, 0x01, 0x85, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, 0xFB, 0x00, + 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, 0x22, 0xFC, + 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x12, 0x00, 0xB7, 0xFF, 0x91, 0x00, 0x40, 0xFF, 0x96, + 0x00, 0x6F, 0x00, 0xD5, 0xFB, 0x5E, 0x47, 0x50, 0x0D, 0xF2, 0xF8, + 0x78, 0x04, 0x3F, 0xFD, 0x82, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, 0x01, 0x53, 0xFD, + 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, 0x76, 0xFC, 0x1F, + 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, 0xFF, 0x13, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xDF, + 0x06, 0x30, 0xF3, 0x78, 0x21, 0xDB, 0x3C, 0x28, 0xF3, 0x65, 0x05, + 0xA2, 0xFD, 0xEA, 0x00, 0xBE, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, + 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, + 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, + 0xD8, 0xFF, 0x3C, 0x00, 0xEE, 0xFF, 0x5A, 0xFF, 0x98, 0x02, 0xBB, + 0xF7, 0x87, 0x44, 0x8C, 0x14, 0x83, 0xF6, 0x95, 0x05, 0xBA, 0xFC, + 0xBB, 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, + 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, 0xFD, 0x05, 0x03, 0xE1, 0xFB, + 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, 0xAA, 0xFD, 0x18, 0x02, 0x72, + 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x39, 0xFF, 0xD6, 0x01, 0x75, 0xFC, 0x37, 0x06, 0xFF, 0xF4, 0xC7, + 0x19, 0xCC, 0x41, 0x7F, 0xF5, 0xE2, 0x03, 0x95, 0xFE, 0x5D, 0x00, + 0x05, 0x00, 0xED, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, + 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, + 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, + 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF5, 0xFF, 0xEE, 0xFF, + 0x8B, 0x00, 0x44, 0xFE, 0x65, 0x04, 0xAA, 0xF4, 0x66, 0x40, 0x23, + 0x1C, 0x63, 0xF4, 0x74, 0x06, 0x5D, 0xFC, 0xDE, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, + 0xFE, 0xAE, 0x01, 0x70, 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, + 0x14, 0xFB, 0x6D, 0x03, 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAC, 0x01, + 0xDF, 0xFC, 0x43, 0x05, 0x3C, 0xF7, 0x46, 0x12, 0x8D, 0x45, 0xE2, + 0xF8, 0xF7, 0x01, 0xB8, 0xFF, 0xB9, 0xFF, 0x56, 0x00, 0xCE, 0xFF, + 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, + 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, + 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAB, 0xFF, 0x10, 0x01, 0x62, 0xFD, + 0xC5, 0x05, 0xA5, 0xF2, 0x1F, 0x3B, 0xD6, 0x23, 0xBE, 0xF2, 0x01, + 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x10, 0x00, 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, + 0x00, 0x0C, 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, + 0x26, 0xFD, 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6D, 0x01, 0x6E, 0xFD, 0x17, 0x04, + 0xBC, 0xF9, 0x30, 0x0B, 0xF4, 0x47, 0x4B, 0xFD, 0xB5, 0xFF, 0xFD, + 0x00, 0x08, 0xFF, 0xAC, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, + 0xF3, 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, + 0xD4, 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, + 0x00, 0x77, 0xFF, 0x77, 0x01, 0xBD, 0xFC, 0xAE, 0x06, 0xA3, 0xF1, + 0xE3, 0x34, 0x64, 0x2B, 0xBC, 0xF1, 0x2A, 0x07, 0x43, 0xFC, 0xCD, + 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDD, 0xFF, + 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, + 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, + 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, + 0xFF, 0x20, 0x01, 0x16, 0xFE, 0xCA, 0x02, 0x54, 0xFC, 0xB9, 0x04, + 0xF2, 0x48, 0xA9, 0x02, 0x39, 0xFD, 0x53, 0x02, 0x53, 0xFE, 0x03, + 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, + 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, + 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, + 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBD, + 0x01, 0x57, 0xFC, 0x1E, 0x07, 0x90, 0xF1, 0xED, 0x2D, 0x8C, 0x32, + 0x83, 0xF1, 0xE2, 0x06, 0x92, 0xFC, 0x93, 0x01, 0x68, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, 0xA4, 0x00, + 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, 0x1D, 0x10, + 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, 0xCA, 0x00, 0xC9, 0xFE, 0x73, + 0x01, 0xDE, 0xFE, 0x0C, 0xFF, 0x76, 0x48, 0xDE, 0x08, 0xA1, 0xFA, + 0xA6, 0x03, 0xA6, 0xFD, 0x53, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, 0xFC, + 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, 0x98, + 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x32, 0xFC, 0x1B, + 0x07, 0x50, 0xF2, 0x7B, 0x26, 0x11, 0x39, 0x2F, 0xF2, 0x23, 0x06, + 0x22, 0xFD, 0x37, 0x01, 0x97, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, 0x41, 0xFD, 0xF6, 0x05, + 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, 0xF2, 0x0F, 0x07, 0x31, + 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, + 0xC2, 0xFF, 0x73, 0x00, 0x7D, 0xFF, 0x25, 0x00, 0x38, 0x01, 0x4C, + 0xFA, 0x89, 0x46, 0xC3, 0x0F, 0x14, 0xF8, 0xE0, 0x04, 0x0D, 0xFD, + 0x98, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, + 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, 0xDF, 0x03, 0x2E, 0xFA, + 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, 0xFF, 0x38, 0x01, 0xE9, + 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE4, 0x01, 0x49, 0xFC, 0xAF, 0x06, 0xC1, 0xF3, 0xCD, + 0x1E, 0xB1, 0x3E, 0xD9, 0xF3, 0xEC, 0x04, 0xF0, 0xFD, 0xBC, 0x00, + 0xD5, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, + 0xFF, 0x85, 0x01, 0xA6, 0xFC, 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, + 0xAB, 0x2C, 0xA3, 0xF1, 0x26, 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, + 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE2, 0xFF, 0x20, 0x00, + 0x26, 0x00, 0xF5, 0xFE, 0x43, 0x03, 0x8D, 0xF6, 0x3C, 0x43, 0x25, + 0x17, 0xBB, 0xF5, 0xEA, 0x05, 0x95, 0xFC, 0xCA, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, + 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, + 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, + 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, + 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, + 0xFD, 0x4E, 0x05, 0x4A, 0xF3, 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, + 0xD6, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, + 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, + 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, + 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, 0x01, 0x4C, 0xFC, 0x26, + 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, 0x8F, 0xF1, 0xCA, 0x06, + 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x16, + 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, + 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, + 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, 0x07, 0x84, + 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, 0x41, 0xFD, + 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, + 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, + 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, + 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, + 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, + 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0x07, 0x00, 0xE8, 0xFF, 0x12, + 0x00, 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, + 0x76, 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, + 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, + 0xC3, 0x01, 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, + 0x43, 0x20, 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, + 0xDD, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, + 0x00, 0xC8, 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, + 0x76, 0xF3, 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, + 0x26, 0xFD, 0xAD, 0x04, 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, + 0xFB, 0xD4, 0x00, 0x5D, 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, + 0x10, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, + 0xFD, 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, + 0x23, 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x46, 0x01, 0xC3, 0xFD, + 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, 0xA6, 0x48, 0xF8, 0xFF, 0x70, + 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, 0x00, 0x9A, 0xFF, 0x19, 0x00, + 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, + 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, + 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, + 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, 0x02, 0xAA, 0xFD, + 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, 0x05, 0x03, 0xF7, + 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, 0xDC, + 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, 0xFC, + 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xB2, + 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, 0xFC, + 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, 0x79, + 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, + 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, + 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xD3, 0xFF, 0x47, + 0x00, 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, + 0x8D, 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, + 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, + 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, + 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, + 0xF0, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, + 0x00, 0x67, 0xFE, 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, + 0xA6, 0xF4, 0x5A, 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, + 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, + 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, + 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, + 0xFD, 0x9C, 0x05, 0xDC, 0xF2, 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, + 0xF3, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x76, 0x01, 0x59, 0xFD, + 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, 0xB6, 0x47, 0xA4, 0xFC, 0x07, + 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, 0x00, 0xB1, 0xFF, 0x13, 0x00, + 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, 0x01, 0xCF, 0xFC, 0x96, + 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, 0xD4, 0xF1, 0x2B, 0x07, + 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, + 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, + 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, + 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, 0x07, 0x85, + 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, 0x84, 0xFC, + 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9C, + 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, + 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, + 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, + 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, + 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, + 0x00, 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, + 0xCD, 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, + 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE5, 0x01, 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, + 0x3D, 0x91, 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, + 0x02, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, + 0x00, 0x1B, 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, + 0x07, 0xF6, 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, + 0x88, 0xFC, 0x09, 0x06, 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, + 0xF6, 0x83, 0x03, 0xCF, 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, + 0x07, 0x00, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, + 0xFE, 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, + 0x99, 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, 0xFF, 0xA0, 0x01, 0xFB, 0xFC, + 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, 0x2B, 0x46, 0xBB, 0xF9, 0x83, + 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, 0x00, 0xC7, 0xFF, 0x0E, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, + 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, + 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, 0xFD, 0xD2, 0x03, + 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, 0x33, 0xFF, 0x45, + 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, 0x00, 0xFD, 0xFF, + 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, 0x8C, + 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, 0xFC, + 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x86, + 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, 0x03, + 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, 0x14, + 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, + 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, + 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, + 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, + 0x00, 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, + 0x47, 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, + 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, + 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, + 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, + 0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, + 0xFF, 0xE2, 0xFF, 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, + 0x8F, 0xF7, 0x1D, 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, + 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, + 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, + 0x04, 0x00, 0x07, 0x00, 0xE9, 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, + 0xFE, 0xA6, 0x03, 0xE4, 0xF5, 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, + 0x1A, 0x06, 0x81, 0xFC, 0xD2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC1, 0x01, 0xAB, 0xFC, + 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, 0x0B, 0x44, 0x42, 0xF7, 0xDC, + 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, 0x00, 0xDC, 0xFF, 0x09, 0x00, + 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, 0x00, 0xBF, 0xFD, 0x38, + 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, 0x66, 0xF3, 0xCE, 0x06, + 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, + 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, + 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, 0x00, 0xFF, 0xFF, + 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, 0x06, 0xF7, + 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, 0x34, 0xFC, + 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, + 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, + 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, + 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, + 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, + 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, + 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, + 0x00, 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, + 0x07, 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, + 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, + 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, + 0x36, 0xC5, 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, + 0x1E, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, + 0xFF, 0xB6, 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, + 0x31, 0xF9, 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, + 0x37, 0xFC, 0xEB, 0x06, 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, + 0xF2, 0x84, 0x05, 0x8D, 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, + 0x01, 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, + 0xFF, 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, + 0x7C, 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, + 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD9, 0x01, 0x6D, 0xFC, + 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, 0x5F, 0x41, 0x3A, 0xF5, 0x0C, + 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, 0xFF, 0xEF, 0xFF, 0x05, 0x00, + 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, + 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, + 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, + 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, 0xFC, 0x5D, 0x05, + 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, 0x2A, 0x02, 0x9A, + 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, 0xCC, + 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, 0xFC, + 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, + 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, 0xF9, + 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, 0x19, + 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x20, 0x00, + 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, + 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, + 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, + 0xFF, 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, + 0xEA, 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, + 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, + 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, + 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, + 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, + 0xFE, 0x93, 0x01, 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, + 0xE1, 0xFA, 0x86, 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, + 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, + 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, + 0xFF, 0xFF, 0x10, 0x00, 0xBF, 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, + 0x00, 0x01, 0x01, 0xB6, 0xFA, 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, + 0xC4, 0x04, 0x1B, 0xFD, 0x92, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x44, 0xFC, + 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, 0x31, 0x3E, 0xA5, 0xF3, 0x0F, + 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, 0xFF, 0x01, 0x00, 0x02, 0x00, + 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, 0x00, 0x10, 0xFF, 0x15, + 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, 0xF1, 0xF5, 0xD3, 0x05, + 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, + 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, + 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0x03, 0x00, + 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, 0x04, 0x10, + 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, 0x4E, 0xFC, + 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, + 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, + 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, + 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x13, 0x00, + 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, + 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, + 0xFF, 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, + 0x57, 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, + 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, + 0x8B, 0x01, 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, + 0x2D, 0x9A, 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, + 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, + 0xFE, 0x74, 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, + 0x94, 0xFC, 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, + 0x48, 0xFC, 0x28, 0x07, 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, + 0xF1, 0xBE, 0x06, 0xB0, 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, + 0xFE, 0xFF, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, + 0x01, 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, + 0xF8, 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, + 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, 0x8F, 0x3A, 0x82, 0xF2, 0xE1, + 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, + 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, + 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, 0xFC, 0x85, 0x06, + 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, 0x8C, 0x04, 0x2C, + 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, 0x00, 0x06, 0x00, + 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, 0xC7, + 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, 0xFC, + 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, + 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, 0xF6, + 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, 0xFE, + 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x05, 0x00, + 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, + 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x56, + 0xFF, 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, + 0x26, 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, + 0x00, 0xBA, 0xFF, 0x11, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, + 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, + 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, + 0x01, 0xD0, 0xFD, 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, + 0x62, 0x00, 0x3F, 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, + 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, + 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, + 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, + 0xFD, 0xFF, 0x1B, 0x00, 0x93, 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, + 0x01, 0xDC, 0xFD, 0x3C, 0x01, 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, + 0x1F, 0x03, 0xEA, 0xFD, 0x34, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, 0xFF, 0xD6, 0x01, 0x39, 0xFC, + 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, 0x85, 0x36, 0xCC, 0xF1, 0x7F, + 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, + 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, 0xFF, 0xA9, 0x00, 0x4D, + 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, 0x18, 0xF9, 0x66, 0x04, + 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, + 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, + 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, 0x00, 0x0B, 0x00, + 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, 0x02, 0xF0, + 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, 0xC1, 0xFC, + 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, + 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, + 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, + 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0x05, 0x00, 0xF4, 0xFF, + 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, + 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x47, + 0xFF, 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, + 0x5C, 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, + 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, + 0x09, 0x01, 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, + 0x23, 0xD2, 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, + 0x01, 0x65, 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, + 0x03, 0xFD, 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, + 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, + 0xC4, 0xFC, 0xA4, 0x06, 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, + 0xF1, 0x2A, 0x07, 0x40, 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, + 0xFE, 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, + 0x5E, 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, + 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0xBA, 0x01, 0x5B, 0xFC, + 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, 0x26, 0x32, 0x80, 0xF1, 0xEA, + 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, + 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, + 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, + 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, 0xFC, 0x1E, 0x07, + 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, 0x32, 0x06, 0x18, + 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, + 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, 0x8B, + 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, 0xFD, + 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, 0xF3, + 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, 0xC4, + 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xE1, 0xFF, + 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, + 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, + 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, + 0xFF, 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, + 0x02, 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, + 0x00, 0xE4, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, + 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, + 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, + 0x01, 0x05, 0xFD, 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, + 0x0D, 0xFA, 0x58, 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, + 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, + 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, + 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, + 0xFD, 0xB9, 0x03, 0x7B, 0xFA, 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, + 0x03, 0xFF, 0x5F, 0x01, 0xD4, 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, + 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, 0xFF, 0x8E, 0x01, 0x99, 0xFC, + 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, 0x82, 0x2D, 0x96, 0xF1, 0x21, + 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, 0xFE, 0x67, 0x02, 0x13, + 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, 0x7A, 0xFC, 0xB6, 0x02, + 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, + 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, + 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x15, 0x00, + 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, 0xFF, 0x94, + 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, 0x77, 0xFD, + 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, + 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, + 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xCC, 0xFF, + 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, + 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, + 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, + 0xFF, 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, + 0x1F, 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, + 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, + 0x54, 0x00, 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, + 0x19, 0x1E, 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, + 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, + 0x01, 0xB4, 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, + 0x86, 0xF7, 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, + 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, + 0xAE, 0xFD, 0x52, 0x05, 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, + 0xF3, 0xD8, 0x06, 0x3C, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, + 0xFD, 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, + 0x91, 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, + 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, 0xFF, 0x55, 0x01, 0xF2, 0xFC, + 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, 0xAA, 0x28, 0x05, 0xF2, 0x27, + 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, + 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, + 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFD, + 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, 0xFC, 0x03, 0x07, + 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, 0x0A, 0x07, 0x6E, + 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x1A, 0x00, + 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, 0x05, + 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, 0xFD, + 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, + 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, 0xF1, + 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, 0x5C, + 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB6, 0xFF, + 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, + 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, + 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, + 0xC0, 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, + 0xFF, 0x07, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, + 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, + 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, + 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, + 0x01, 0x73, 0xFC, 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, + 0x71, 0xF5, 0xEB, 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, + 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, + 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, + 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, + 0xFC, 0x48, 0x05, 0x30, 0xF7, 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, + 0x01, 0x02, 0xB2, 0xFF, 0xBD, 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, + 0x00, 0x00, 0x00, 0x0E, 0x00, 0xAC, 0xFF, 0x0E, 0x01, 0x66, 0xFD, + 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, 0xB0, 0x23, 0xC4, 0xF2, 0xFF, + 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, 0x01, 0x6B, 0xFD, 0x1D, + 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, 0x33, 0xFD, 0xC1, 0xFF, + 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, 0xFF, 0x14, 0x00, 0xFE, + 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, + 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, + 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, 0x02, 0x47, + 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, 0x4D, 0x02, + 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, + 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, + 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, + 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA0, 0xFF, + 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, + 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, + 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, + 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, + 0xF2, 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, + 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, + 0x7A, 0xFF, 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, + 0x0F, 0x20, 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, + 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x48, 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, + 0xCF, 0xF3, 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, + 0xFA, 0xFE, 0x3A, 0x03, 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, + 0xF5, 0xE6, 0x05, 0x97, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, + 0xFE, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, + 0xFC, 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, + 0x4D, 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, + 0x00, 0x03, 0x00, 0xFE, 0xFF, 0xD7, 0xFF, 0xBA, 0x00, 0xF4, 0xFD, + 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, + 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, + 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, + 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0xFF, + 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, 0xFD, 0x1E, 0x06, + 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, 0x1A, 0x07, 0x31, + 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, 0x94, + 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, 0x01, + 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, + 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, 0xF1, + 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, 0xBE, + 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, + 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, + 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, + 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, + 0xFF, 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, + 0xC3, 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, + 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, + 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, + 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, + 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, + 0x9E, 0xF2, 0xCB, 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, + 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, + 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, + 0xFC, 0x78, 0x06, 0x5A, 0xF4, 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, + 0x6D, 0x04, 0x3F, 0xFE, 0x8E, 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, + 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x5A, 0x00, 0x9A, 0xFE, + 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, 0xA1, 0x19, 0x09, 0xF5, 0x33, + 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, 0x01, 0xB8, 0xFC, 0x9A, + 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, 0xA9, 0xF7, 0xA2, 0x02, + 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, 0xFF, 0x0A, 0x00, 0x01, + 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, + 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, 0x04, 0xE6, + 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, 0x90, 0x00, + 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFE, 0xFF, 0x1C, + 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, + 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, + 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, + 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, + 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, + 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, + 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, + 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, + 0x8E, 0xFE, 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, + 0x06, 0x7B, 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, + 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, + 0x01, 0x37, 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, + 0xDC, 0xF1, 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x11, 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, + 0x90, 0x00, 0x7A, 0x00, 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, + 0xF8, 0x7E, 0x04, 0x3C, 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, + 0xFC, 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, + 0x5F, 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, + 0x00, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, 0x00, 0xF1, 0xFF, 0x54, 0xFF, + 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, 0xB1, 0x14, 0x77, 0xF6, 0x9A, + 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, + 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, + 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x04, + 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, 0xFE, 0x6D, 0x04, + 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, 0x78, 0x06, 0x5C, + 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, + 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, 0x48, + 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, 0xFF, + 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0E, + 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, 0xF2, + 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, 0xE5, + 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, + 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, + 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, + 0xFF, 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, + 0x89, 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, + 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, + 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, + 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, + 0x8A, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, + 0x01, 0x56, 0xFC, 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, + 0x84, 0xF1, 0xE0, 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, + 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, + 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, + 0xFC, 0x1A, 0x07, 0x56, 0xF2, 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, + 0x1E, 0x06, 0x25, 0xFD, 0x35, 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, + 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, 0x00, 0x81, 0xFF, 0x1F, 0x00, + 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, 0xE7, 0x0F, 0x08, 0xF8, 0xE6, + 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x4A, 0xFC, 0xAC, + 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, 0xE4, 0xF3, 0xE5, 0x04, + 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0x08, + 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, + 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, + 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFE, 0xFF, + 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, 0x05, 0xC6, + 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, 0xFA, 0xFE, + 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFF, + 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, + 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, + 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, + 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, + 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, + 0xFF, 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, + 0xA0, 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, + 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, + 0x52, 0x01, 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, + 0x48, 0x26, 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, + 0xA0, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, + 0x01, 0x90, 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, + 0x8E, 0xF1, 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, + 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, + 0x4D, 0x02, 0x45, 0xFD, 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, + 0xFC, 0xD1, 0x02, 0x12, 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, + 0xFC, 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, + 0xAB, 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, + 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, 0x00, 0x0C, 0xFF, 0xF7, 0x00, + 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, 0x51, 0x0B, 0xAF, 0xF9, 0x1D, + 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, + 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, + 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, 0xFF, 0x01, 0x02, + 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, 0x48, 0x05, 0xDD, + 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, + 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, 0x6C, + 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, 0xFE, + 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xED, + 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, 0xF5, + 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, 0xD7, + 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, + 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, + 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, + 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x07, 0x00, 0xBD, + 0xFF, 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, + 0x9E, 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, + 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, + 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, + 0xB6, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, + 0x01, 0xE6, 0xFC, 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, + 0xF3, 0xF1, 0x29, 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, + 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, + 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, + 0x1A, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, + 0xFC, 0x0A, 0x07, 0x7E, 0xF1, 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, + 0x03, 0x07, 0x75, 0xFC, 0xA7, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, + 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, 0x00, 0x95, 0xFE, 0xD5, 0x01, + 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, 0x00, 0x07, 0x61, 0xFB, 0x46, + 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, 0xFF, 0x23, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, 0x01, 0x36, 0xFC, 0x27, + 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, 0xE4, 0xF1, 0x67, 0x06, + 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x11, + 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, + 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, + 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, 0x06, 0x47, + 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, 0xAE, 0xFD, + 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD9, + 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, + 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, + 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, + 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, + 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF7, 0xFF, 0xEA, + 0xFF, 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, + 0x94, 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xA9, 0x01, 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, + 0x45, 0x1C, 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, + 0xCC, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, + 0x01, 0x57, 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, + 0xAA, 0xF2, 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, + 0x77, 0xFD, 0x04, 0x04, 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, + 0xFD, 0x92, 0xFF, 0x10, 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, + 0x15, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, + 0xFC, 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, + 0x29, 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x1B, 0x01, 0x20, 0xFE, + 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, 0xF4, 0x48, 0xFF, 0x02, 0x13, + 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, 0x01, 0x88, 0xFF, 0x1D, 0x00, + 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, + 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, + 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, + 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, 0x01, 0x03, 0xFF, + 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, 0xB9, 0x03, 0x9D, + 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, 0x61, + 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, 0xFD, + 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC4, + 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, 0xFA, + 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, 0x9C, + 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, + 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, + 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE4, 0xFF, 0x1B, + 0x00, 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, + 0x96, 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, + 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, + 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, + 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, + 0xE1, 0xFF, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, + 0x00, 0xE2, 0xFD, 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, + 0xA8, 0xF3, 0xB8, 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, + 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, + 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, + 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, + 0xFD, 0x32, 0x06, 0x1F, 0xF2, 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, + 0x1E, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x4F, 0x01, 0xB0, 0xFD, + 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, 0x86, 0x48, 0x5A, 0xFF, 0xBA, + 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, 0x00, 0x9E, 0xFF, 0x17, 0x00, + 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, 0x01, 0x8C, 0xFC, 0xEA, + 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, 0x8B, 0xF1, 0x1B, 0x07, + 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, + 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, + 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, + 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xC6, + 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, 0xC4, 0xFC, + 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAE, + 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, + 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, + 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, + 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, + 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, + 0x00, 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, + 0xB2, 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, + 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, + 0xDD, 0x01, 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, + 0x40, 0xD0, 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, + 0xF4, 0xFF, 0x05, 0x00, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, + 0x00, 0x85, 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, + 0xE1, 0xF4, 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, + 0xC1, 0xFC, 0x86, 0x05, 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, + 0xF7, 0x7B, 0x02, 0x6B, 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, + 0x0B, 0x00, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, + 0xFD, 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, + 0xE6, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x5A, 0xFF, 0x7E, 0x01, 0x48, 0xFD, + 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, 0x7C, 0x47, 0x19, 0xFC, 0x4D, + 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, 0x00, 0xB5, 0xFF, 0x12, 0x00, + 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, + 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, + 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, 0xFD, 0x1F, 0x03, + 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, 0xDC, 0xFD, 0xFD, + 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, + 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, 0x80, + 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, 0xFC, + 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x98, + 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, 0x00, + 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, 0x40, + 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, + 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, + 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, + 0x00, 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, + 0xFC, 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, + 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, + 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, + 0x05, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x3D, 0xFF, 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, + 0x4A, 0xF6, 0xAD, 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, + 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, + 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, + 0x06, 0x00, 0x04, 0x00, 0xF8, 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, + 0xFE, 0x8C, 0x04, 0x6D, 0xF4, 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, + 0x85, 0x06, 0x57, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, 0xFF, 0xA7, 0x01, 0xEC, 0xFC, + 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, 0xD7, 0x45, 0x43, 0xF9, 0xC3, + 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, 0x00, 0xCB, 0xFF, 0x0D, 0x00, + 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, 0x01, 0x50, 0xFD, 0xE1, + 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, 0x9D, 0xF2, 0x09, 0x07, + 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, + 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, + 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, 0x00, 0xFE, 0xFF, + 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, 0x06, 0x97, + 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, 0x48, 0xFC, + 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, + 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, + 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, + 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, + 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, + 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, + 0x00, 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, + 0x81, 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, + 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, + 0xE2, 0x01, 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, + 0x39, 0x4D, 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, + 0x13, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, + 0xFF, 0x06, 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, + 0xD7, 0xF7, 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, + 0x4E, 0xFC, 0xA0, 0x06, 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, + 0xF4, 0xC8, 0x04, 0x07, 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, + 0x03, 0x00, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, + 0xFE, 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, + 0x00, 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, 0x9F, 0xFC, + 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, 0x9E, 0x43, 0xDD, 0xF6, 0x15, + 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, 0x00, 0xDF, 0xFF, 0x09, 0x00, + 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, + 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, + 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, 0xFD, 0xC4, 0x04, + 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, 0x01, 0x01, 0x44, + 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, 0x00, 0xFF, 0xFF, + 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, 0x14, + 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, 0xFC, + 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, + 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, 0xFA, + 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, 0xB8, + 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, + 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, + 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, + 0x00, 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, + 0x50, 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, + 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, + 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, + 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, + 0x20, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, + 0xFF, 0xDD, 0x00, 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, + 0x7C, 0xF9, 0x35, 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, + 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, + 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0xD1, 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, + 0xFF, 0x2A, 0x02, 0x83, 0xF8, 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, + 0x5D, 0x05, 0xD3, 0xFC, 0xB1, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, + 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x64, 0xFC, + 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, 0xD9, 0x40, 0xEA, 0xF4, 0x3E, + 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, 0xFF, 0xF3, 0xFF, 0x05, 0x00, + 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x7B, 0xFE, 0x0C, + 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, 0xCD, 0xF4, 0x4B, 0x06, + 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, + 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, + 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, + 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0x01, 0x00, + 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, 0x05, 0xFD, + 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, 0x37, 0xFC, + 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, + 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, + 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, + 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, + 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, + 0xFF, 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, + 0xDB, 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, + 0x00, 0x91, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, + 0xB1, 0x01, 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, + 0x31, 0x7C, 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, + 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, + 0xFE, 0xBB, 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, + 0x2E, 0xFB, 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, + 0x34, 0xFC, 0x25, 0x07, 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, + 0xF1, 0x56, 0x06, 0xFE, 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, + 0xFF, 0xFF, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, + 0x00, 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, + 0xA1, 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3F, 0xFC, + 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, 0x96, 0x3D, 0x69, 0xF3, 0x38, + 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, 0xFF, 0x04, 0x00, 0x02, 0x00, + 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, + 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, + 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, 0xFC, 0x1A, 0x06, + 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, 0xA6, 0x03, 0xB9, + 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0x04, 0x00, + 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, 0x55, + 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, 0xFC, + 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, + 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, 0xF7, + 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, 0xA2, + 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00, + 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, + 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, + 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, + 0xFF, 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, + 0x2C, 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, + 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, + 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, + 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, + 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, + 0x01, 0x2D, 0xFE, 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, + 0x73, 0x03, 0xE0, 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, + 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, + 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, + 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, + 0xFD, 0xFF, 0x16, 0x00, 0xA5, 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, + 0x01, 0x33, 0xFF, 0x5A, 0xFE, 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, + 0xD2, 0x03, 0x90, 0xFD, 0x5E, 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, + 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, 0xDF, 0x39, 0x5A, 0xF2, 0x00, + 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, 0xFF, 0x13, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, 0xFF, 0xFA, 0xFF, 0x83, + 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, 0xBF, 0xF7, 0x07, 0x05, + 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, + 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, + 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0x07, 0x00, + 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, 0x03, 0x20, + 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, 0x88, 0xFC, + 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, + 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, + 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, + 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, + 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, + 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x53, + 0xFF, 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, + 0xE1, 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, + 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, + 0x45, 0x01, 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, + 0x27, 0x2B, 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, + 0x01, 0xBC, 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, + 0xC3, 0xFF, 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, + 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, + 0x84, 0xFC, 0xF3, 0x06, 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, + 0xF1, 0x16, 0x07, 0x61, 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, + 0xFD, 0xFF, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, + 0x02, 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, + 0xF8, 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD2, 0x01, 0x3D, 0xFC, + 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, 0xC6, 0x35, 0xB7, 0xF1, 0x96, + 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, + 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, + 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, + 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF3, 0x06, + 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, 0x9C, 0x05, 0x7E, + 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, + 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, 0x5E, + 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, 0xFC, + 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, + 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, 0xF4, + 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, 0x77, + 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF0, 0xFF, + 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, + 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, + 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x45, + 0xFF, 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, + 0xFD, 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, + 0x00, 0xD3, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, + 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, + 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, + 0x01, 0x53, 0xFD, 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, + 0x76, 0xFC, 0x1F, 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, + 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, + 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, + 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, + 0xFD, 0x05, 0x03, 0xE1, 0xFB, 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, + 0xAA, 0xFD, 0x18, 0x02, 0x72, 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, + 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB3, 0x01, 0x64, 0xFC, + 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, 0x5A, 0x31, 0x7D, 0xF1, 0xF7, + 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, + 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, 0xFE, 0xAE, 0x01, 0x70, + 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, 0x14, 0xFB, 0x6D, 0x03, + 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, + 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, + 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x10, 0x00, + 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, 0x00, 0x0C, + 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, 0x26, 0xFD, + 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, + 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, + 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDD, 0xFF, + 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, + 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, + 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3B, + 0xFF, 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, + 0x89, 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, + 0x00, 0xE8, 0xFF, 0x07, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, + 0xA4, 0x00, 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, + 0x1D, 0x10, 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, + 0x01, 0xF6, 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, + 0x93, 0xF9, 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, + 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, + 0x41, 0xFD, 0xF6, 0x05, 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, + 0xF2, 0x0F, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, + 0xFD, 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, + 0x4B, 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, + 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, 0xFF, 0x85, 0x01, 0xA6, 0xFC, + 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, 0xAB, 0x2C, 0xA3, 0xF1, 0x26, + 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, 0xFF, 0x30, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, + 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, + 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFD, + 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, + 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, + 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, + 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, + 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x02, 0x00, 0x05, + 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, + 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, + 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, + 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x84, + 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, + 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, + 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, + 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, + 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, 0xFF, 0x64, 0x00, 0x9B, + 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, 0x10, 0x46, 0x03, 0x11, + 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, 0x01, 0x4C, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, 0x42, 0x00, + 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, 0x18, 0x5C, + 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, + 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, + 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, + 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, + 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, + 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, + 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, + 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, + 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, + 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, + 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, + 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, + 0xF2, 0x81, 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, + 0xFB, 0x00, 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, + 0x00, 0x38, 0xFF, 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, + 0xCE, 0x1A, 0x32, 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, + 0x00, 0xFB, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, + 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, + 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, + 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, + 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, + 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, + 0x00, 0xB1, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, + 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, + 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, + 0x8F, 0xFF, 0x1C, 0x00, 0x18, 0x00, 0x9C, 0xFF, 0xD6, 0x00, 0xB1, + 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, 0x9C, 0x48, 0xFD, 0x07, + 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, 0x01, 0x6E, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, 0x65, 0xFF, + 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, 0x0E, 0x6A, + 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, 0x2D, 0x00, + 0xFF, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, + 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, + 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, + 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, + 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, + 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, + 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, + 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, + 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, + 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x30, 0x00, 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, + 0xF1, 0x62, 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, + 0x82, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x3A, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, + 0xDD, 0x24, 0x54, 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, + 0x01, 0xA3, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, + 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, + 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, + 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, + 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, + 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, + 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, + 0xBB, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x43, + 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, 0x7E, 0x07, 0xAF, 0x48, + 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, 0xFE, 0xDD, 0x00, 0x99, + 0xFF, 0x19, 0x00, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, 0x79, 0xFE, + 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, 0x06, 0xC7, + 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, 0x22, 0x00, + 0x00, 0x00, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, + 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, + 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, + 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, + 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, + 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, + 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, + 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, + 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, + 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, + 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, + 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, + 0x20, 0x00, 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, + 0xF1, 0x86, 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, + 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2D, + 0x00, 0x54, 0xFF, 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, + 0x9F, 0x2E, 0xE3, 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, + 0x01, 0x64, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, + 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, + 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, + 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, + 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, + 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, + 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, + 0xE5, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9E, + 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, 0x75, 0x10, 0x48, 0x46, + 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, 0xFF, 0x6B, 0x00, 0xC6, + 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x5B, 0x01, + 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, 0x48, 0x8D, + 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, 0xA4, 0xFF, + 0x16, 0x00, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, + 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, + 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, + 0x00, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, + 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, + 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, + 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, + 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x06, + 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, + 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, + 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x02, 0x00, + 0x05, 0x00, 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, + 0xF3, 0x61, 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, + 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x1A, + 0x00, 0x8A, 0xFF, 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, + 0x82, 0x37, 0x60, 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, + 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x29, 0x00, + 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, + 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x43, + 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, + 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, + 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, + 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, + 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, + 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, 0x38, 0x1A, 0x8C, 0x41, + 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, 0x00, 0x01, 0x00, 0xEE, + 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, 0xAF, 0x01, + 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, 0x45, 0xA9, + 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, 0xD0, 0xFF, + 0x0C, 0x00, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, + 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, + 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, + 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, + 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, + 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, + 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, + 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x0F, + 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, + 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, + 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x08, 0x00, + 0xE1, 0xFF, 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, + 0xF6, 0x77, 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, + 0xC8, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xFD, + 0xFF, 0xD9, 0xFF, 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, + 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x14, 0x00, + 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, + 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x25, 0x00, 0x6A, + 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, + 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, + 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, + 0x22, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, 0x46, 0x24, 0xC8, 0x3A, + 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, 0x01, 0xA8, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, + 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, 0x40, 0x85, + 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, 0xF7, 0xFF, + 0x04, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, + 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, + 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, + 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, + 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, + 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, + 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, + 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0x1A, + 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, + 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, + 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0x12, 0x00, + 0xB6, 0xFF, 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, + 0xFB, 0x69, 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, + 0x81, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD7, + 0xFF, 0x3E, 0x00, 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, + 0x99, 0x44, 0x68, 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, + 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF5, 0xFF, + 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, + 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, + 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, + 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, + 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, + 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, + 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2E, 0x00, 0x52, 0xFF, 0xBC, + 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, 0x11, 0x2E, 0x6B, 0x32, + 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, 0x01, 0x67, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE0, 0x01, + 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, 0x38, 0x2A, + 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, 0x16, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, + 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, + 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, + 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, + 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, + 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, + 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, + 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, + 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0x00, + 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, + 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, + 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0x1D, 0x00, + 0x8A, 0xFF, 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, + 0x02, 0xF2, 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, + 0x1E, 0x01, 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAC, + 0xFF, 0xAE, 0x00, 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, + 0xFD, 0x47, 0x0E, 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, + 0x01, 0x61, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, + 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, + 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, + 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, + 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, + 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, + 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, + 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1C, 0x00, 0x86, 0xFF, 0x59, + 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, 0x04, 0x37, 0xF3, 0x28, + 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x41, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, + 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, + 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, + 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, + 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, + 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, + 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, + 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, + 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, + 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, + 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0xFF, + 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, + 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, + 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, + 0xF9, 0x0E, 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, + 0x05, 0xFF, 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x7F, 0xFF, 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, + 0x9B, 0x04, 0xF2, 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, + 0xFE, 0x04, 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0x17, 0x00, 0xA1, 0xFF, + 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, + 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, + 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC3, 0xFF, 0x71, + 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, + 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, + 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, + 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, + 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, 0x98, 0x3E, 0xF3, 0x1E, + 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, 0x39, 0x01, + 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, 0x26, 0x4B, + 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, + 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, + 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, + 0xFF, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, + 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, + 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, + 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, + 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, + 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, + 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, + 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFF, 0xFF, + 0x32, 0x00, 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, + 0xF6, 0x68, 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, + 0xEA, 0xFF, 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x59, 0xFF, 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, + 0x2D, 0x0D, 0x69, 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, + 0xFF, 0x93, 0x00, 0xB6, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, + 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, + 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, + 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0x1A, 0x00, 0x96, 0xFF, 0xE3, + 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, + 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, + 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, + 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, + 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD9, 0xFF, 0x37, 0x00, 0xF7, + 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, 0x53, 0x44, 0xFB, 0x14, + 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, 0x01, 0x42, 0xFF, 0x32, + 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, 0x00, + 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, 0x1C, 0x47, + 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, + 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, + 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, + 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, + 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, + 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, + 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, + 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, + 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, + 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, + 0xF3, 0x5B, 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, + 0xB4, 0x00, 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, + 0x00, 0x3E, 0xFF, 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, + 0xB6, 0x16, 0x77, 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, + 0x00, 0x25, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFF, 0xFF, 0x2D, 0x00, + 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, + 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, + 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x6C, + 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, + 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, + 0x00, 0x9E, 0xFF, 0x17, 0x00, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, + 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, + 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAE, 0xFF, 0xA7, 0x00, 0x12, + 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, 0xDC, 0x47, 0x95, 0x0B, + 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, 0x01, 0x5F, 0xFF, 0x29, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, 0xC3, 0xFF, + 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, 0x12, 0x19, + 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, 0x30, 0x00, + 0xFF, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, + 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, + 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, + 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, + 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, + 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, + 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, + 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, + 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, + 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x34, 0x00, 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, + 0xF2, 0x60, 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, + 0x51, 0x01, 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, + 0xBA, 0x20, 0x61, 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, + 0x00, 0xC5, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, + 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, + 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, + 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, + 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, + 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, + 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, + 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, + 0xA9, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x82, 0xFF, 0x18, + 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, 0x24, 0x04, 0xF5, 0x48, + 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, 0xFE, 0x0B, 0x01, 0x87, + 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, 0xDB, 0xFE, + 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, 0x09, 0x61, + 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, 0x26, 0x00, + 0x00, 0x00, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, + 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, + 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, + 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, + 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, + 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, + 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, + 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, + 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, + 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, + 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x28, 0x00, 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, + 0xF1, 0xE3, 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, + 0xB7, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x32, + 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, + 0xAE, 0x2A, 0x86, 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, + 0x01, 0x7B, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, + 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, + 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, + 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, + 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, + 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, + 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, + 0xD5, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, 0xFF, 0x7C, + 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, 0xA4, 0x0C, 0x90, 0x47, + 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, 0xFF, 0x99, 0x00, 0xB3, + 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, 0x31, 0x01, + 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, 0x48, 0x73, + 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, 0x91, 0xFF, + 0x1B, 0x00, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, + 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, + 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, + 0x00, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, + 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, + 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, + 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, + 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, + 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x04, + 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, + 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, + 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, + 0x11, 0x00, 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, + 0xF2, 0x54, 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, + 0xE4, 0x01, 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x23, + 0x00, 0x71, 0xFF, 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, + 0xFD, 0x33, 0x62, 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, + 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2F, 0x00, + 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, + 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, + 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3B, + 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, + 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, + 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, + 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, + 0xFB, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC5, + 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, 0x22, 0x16, 0xC3, 0x43, + 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, 0x00, 0x2B, 0x00, 0xDE, + 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x90, 0x01, + 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, 0x46, 0xE1, + 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, 0xBE, 0xFF, + 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, + 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, + 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, + 0x00, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, + 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, + 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, + 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, + 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, + 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, + 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, + 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x05, 0x00, + 0xF0, 0xFF, 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, + 0xF5, 0x32, 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, + 0xDA, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, + 0x00, 0xB6, 0xFF, 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, + 0x1C, 0x3C, 0x81, 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1E, 0x00, + 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, + 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2C, 0x00, 0x57, + 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, + 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, + 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, + 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, + 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, + 0x19, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, + 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, 0x22, 0x20, 0xCA, 0x3D, + 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, 0x00, 0xCA, 0xFF, 0x03, + 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, + 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, 0x42, 0x02, + 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, 0xE8, 0xFF, + 0x07, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, + 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, + 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, + 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, + 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, + 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, + 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, + 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, + 0x00, 0xF4, 0xFF, 0x1A, 0x00, 0xFF, 0x00, 0x07, 0x03, 0x16, 0x06, + 0x7C, 0x09, 0x2A, 0x0C, 0x2E, 0x0D, 0x2A, 0x0C, 0x7C, 0x09, 0x16, + 0x06, 0x07, 0x03, 0xFF, 0x00, 0x1A, 0x00, 0xF4, 0xFF, 0xF2, 0xFF, + 0xA0, 0xFF, 0x71, 0xFF, 0x71, 0x00, 0x86, 0x03, 0x73, 0x08, 0x88, + 0x0D, 0x78, 0x10, 0xC9, 0x0F, 0xD5, 0x0B, 0x8B, 0x06, 0x28, 0x02, + 0xDF, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDC, + 0xFF, 0x80, 0xFF, 0x9A, 0xFF, 0x46, 0x01, 0x1E, 0x05, 0x5A, 0x0A, + 0xED, 0x0E, 0xAA, 0x10, 0xAF, 0x0E, 0xFD, 0x09, 0xCB, 0x04, 0x18, + 0x01, 0x8E, 0xFF, 0x85, 0xFF, 0xE1, 0xFF, 0xFC, 0xFF, 0xBD, 0xFF, + 0x6D, 0xFF, 0xF6, 0xFF, 0x65, 0x02, 0xE5, 0x06, 0x2B, 0x0C, 0xF3, + 0x0F, 0x60, 0x10, 0x3B, 0x0D, 0x16, 0x08, 0x3F, 0x03, 0x50, 0x00, + 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, 0xFF, 0xEF, 0xFF, 0x9A, 0xFF, 0x75, + 0xFF, 0x91, 0x00, 0xC9, 0x03, 0xC8, 0x08, 0xCC, 0x0D, 0x89, 0x10, + 0x9F, 0x0F, 0x85, 0x0B, 0x3B, 0x06, 0xF4, 0x01, 0xCD, 0xFF, 0x72, + 0xFF, 0xC9, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0x7B, 0xFF, + 0xA5, 0xFF, 0x73, 0x01, 0x6A, 0x05, 0xAD, 0x0A, 0x21, 0x0F, 0xA6, + 0x10, 0x74, 0x0E, 0xA9, 0x09, 0x83, 0x04, 0xF0, 0x00, 0x85, 0xFF, + 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, 0xFF, 0xB7, 0xFF, 0x6C, 0xFF, 0x0C, + 0x00, 0x9D, 0x02, 0x37, 0x07, 0x78, 0x0C, 0x15, 0x10, 0x47, 0x10, + 0xF3, 0x0C, 0xC2, 0x07, 0x01, 0x03, 0x35, 0x00, 0x6D, 0xFF, 0xAD, + 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, 0x94, 0xFF, 0x7A, 0xFF, 0xB3, 0x00, + 0x0D, 0x04, 0x1C, 0x09, 0x0D, 0x0E, 0x97, 0x10, 0x73, 0x0F, 0x35, + 0x0B, 0xEB, 0x05, 0xC1, 0x01, 0xBD, 0xFF, 0x75, 0xFF, 0xCE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x77, 0xFF, 0xB3, 0xFF, 0xA1, + 0x01, 0xB7, 0x05, 0xFF, 0x0A, 0x53, 0x0F, 0x9E, 0x10, 0x37, 0x0E, + 0x55, 0x09, 0x3B, 0x04, 0xCB, 0x00, 0x7E, 0xFF, 0x90, 0xFF, 0xE9, + 0xFF, 0xF8, 0xFF, 0xB1, 0xFF, 0x6C, 0xFF, 0x24, 0x00, 0xD8, 0x02, + 0x8A, 0x07, 0xC2, 0x0C, 0x34, 0x10, 0x2A, 0x10, 0xAA, 0x0C, 0x6F, + 0x07, 0xC4, 0x02, 0x1C, 0x00, 0x6C, 0xFF, 0xB3, 0xFF, 0xF9, 0xFF, + 0xE8, 0xFF, 0x8E, 0xFF, 0x80, 0xFF, 0xD7, 0x00, 0x53, 0x04, 0x71, + 0x09, 0x4C, 0x0E, 0xA1, 0x10, 0x43, 0x0F, 0xE3, 0x0A, 0x9D, 0x05, + 0x91, 0x01, 0xAE, 0xFF, 0x79, 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC2, 0xFF, 0xD2, 0x01, 0x06, 0x06, + 0x50, 0x0B, 0x82, 0x0F, 0x93, 0x10, 0xF8, 0x0D, 0x00, 0x09, 0xF6, + 0x03, 0xA7, 0x00, 0x78, 0xFF, 0x96, 0xFF, 0xEC, 0xFF, 0xF6, 0xFF, + 0xAB, 0xFF, 0x6D, 0xFF, 0x3E, 0x00, 0x15, 0x03, 0xDE, 0x07, 0x0B, + 0x0D, 0x50, 0x10, 0x0A, 0x10, 0x5E, 0x0C, 0x1C, 0x07, 0x8A, 0x02, + 0x04, 0x00, 0x6C, 0xFF, 0xB9, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, 0x89, + 0xFF, 0x88, 0xFF, 0xFD, 0x00, 0x9B, 0x04, 0xC5, 0x09, 0x88, 0x0E, + 0xA8, 0x10, 0x10, 0x0F, 0x91, 0x0A, 0x50, 0x05, 0x64, 0x01, 0xA1, + 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, + 0x71, 0xFF, 0xD3, 0xFF, 0x05, 0x02, 0x55, 0x06, 0xA0, 0x0B, 0xAD, + 0x0F, 0x84, 0x10, 0xB6, 0x0D, 0xAC, 0x08, 0xB3, 0x03, 0x86, 0x00, + 0x74, 0xFF, 0x9C, 0xFF, 0xF0, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, 0x6F, + 0xFF, 0x5A, 0x00, 0x54, 0x03, 0x32, 0x08, 0x52, 0x0D, 0x68, 0x10, + 0xE6, 0x0F, 0x11, 0x0C, 0xCA, 0x06, 0x52, 0x02, 0xEF, 0xFF, 0x6E, + 0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xDF, 0xFF, 0x84, 0xFF, 0x91, 0xFF, + 0x25, 0x01, 0xE4, 0x04, 0x19, 0x0A, 0xC2, 0x0E, 0xAA, 0x10, 0xDA, + 0x0E, 0x3E, 0x0A, 0x05, 0x05, 0x38, 0x01, 0x96, 0xFF, 0x81, 0xFF, + 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE6, + 0xFF, 0x3A, 0x02, 0xA6, 0x06, 0xEF, 0x0B, 0xD6, 0x0F, 0x71, 0x10, + 0x71, 0x0D, 0x57, 0x08, 0x71, 0x03, 0x67, 0x00, 0x70, 0xFF, 0xA2, + 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x78, 0x00, + 0x95, 0x03, 0x86, 0x08, 0x98, 0x0D, 0x7C, 0x10, 0xC0, 0x0F, 0xC3, + 0x0B, 0x79, 0x06, 0x1C, 0x02, 0xDB, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, + 0xFE, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9C, 0xFF, 0x50, + 0x01, 0x2F, 0x05, 0x6C, 0x0A, 0xF9, 0x0E, 0xA9, 0x10, 0xA2, 0x0E, + 0xEA, 0x09, 0xBB, 0x04, 0x0F, 0x01, 0x8C, 0xFF, 0x87, 0xFF, 0xE2, + 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xFA, 0xFF, 0x71, 0x02, + 0xF7, 0x06, 0x3C, 0x0C, 0xFB, 0x0F, 0x5B, 0x10, 0x2B, 0x0D, 0x03, + 0x08, 0x31, 0x03, 0x4A, 0x00, 0x6E, 0xFF, 0xA8, 0xFF, 0xF5, 0xFF, + 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x98, 0x00, 0xD8, 0x03, 0xDB, + 0x08, 0xDB, 0x0D, 0x8D, 0x10, 0x96, 0x0F, 0x73, 0x0B, 0x29, 0x06, + 0xE8, 0x01, 0xC9, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, 0x00, + 0x00, 0xD6, 0xFF, 0x7A, 0xFF, 0xA8, 0xFF, 0x7D, 0x01, 0x7B, 0x05, + 0xBF, 0x0A, 0x2D, 0x0F, 0xA5, 0x10, 0x67, 0x0E, 0x96, 0x09, 0x73, + 0x04, 0xE7, 0x00, 0x84, 0xFF, 0x8C, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, + 0xB6, 0xFF, 0x6C, 0xFF, 0x11, 0x00, 0xAA, 0x02, 0x4A, 0x07, 0x88, + 0x0C, 0x1C, 0x10, 0x41, 0x10, 0xE3, 0x0C, 0xAF, 0x07, 0xF3, 0x02, + 0x2F, 0x00, 0x6C, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, + 0xFF, 0x7B, 0xFF, 0xBB, 0x00, 0x1C, 0x04, 0x2F, 0x09, 0x1B, 0x0E, + 0x9A, 0x10, 0x68, 0x0F, 0x23, 0x0B, 0xDA, 0x05, 0xB7, 0x01, 0xB9, + 0xFF, 0x76, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0xFF, + 0x76, 0xFF, 0xB6, 0xFF, 0xAC, 0x01, 0xC8, 0x05, 0x11, 0x0B, 0x5E, + 0x0F, 0x9C, 0x10, 0x29, 0x0E, 0x42, 0x09, 0x2C, 0x04, 0xC2, 0x00, + 0x7D, 0xFF, 0x92, 0xFF, 0xEA, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, 0x6C, + 0xFF, 0x29, 0x00, 0xE6, 0x02, 0x9D, 0x07, 0xD3, 0x0C, 0x3B, 0x10, + 0x23, 0x10, 0x99, 0x0C, 0x5C, 0x07, 0xB7, 0x02, 0x16, 0x00, 0x6C, + 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8D, 0xFF, 0x82, 0xFF, + 0xDF, 0x00, 0x63, 0x04, 0x84, 0x09, 0x59, 0x0E, 0xA3, 0x10, 0x38, + 0x0F, 0xD1, 0x0A, 0x8C, 0x05, 0x87, 0x01, 0xAB, 0xFF, 0x79, 0xFF, + 0xD5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC6, + 0xFF, 0xDD, 0x01, 0x17, 0x06, 0x62, 0x0B, 0x8C, 0x0F, 0x90, 0x10, + 0xE9, 0x0D, 0xED, 0x08, 0xE7, 0x03, 0xA0, 0x00, 0x77, 0xFF, 0x97, + 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xA9, 0xFF, 0x6D, 0xFF, 0x44, 0x00, + 0x23, 0x03, 0xF1, 0x07, 0x1B, 0x0D, 0x55, 0x10, 0x02, 0x10, 0x4D, + 0x0C, 0x0A, 0x07, 0x7E, 0x02, 0xFF, 0xFF, 0x6D, 0xFF, 0xBA, 0xFF, + 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x8A, 0xFF, 0x06, 0x01, 0xAB, + 0x04, 0xD8, 0x09, 0x95, 0x0E, 0xA9, 0x10, 0x05, 0x0F, 0x7F, 0x0A, + 0x40, 0x05, 0x5A, 0x01, 0x9F, 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, + 0x00, 0xFE, 0xFF, 0xC6, 0xFF, 0x70, 0xFF, 0xD7, 0xFF, 0x10, 0x02, + 0x67, 0x06, 0xB1, 0x0B, 0xB7, 0x0F, 0x80, 0x10, 0xA7, 0x0D, 0x99, + 0x08, 0xA4, 0x03, 0x7F, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, 0xFF, + 0xF3, 0xFF, 0xA3, 0xFF, 0x70, 0xFF, 0x60, 0x00, 0x62, 0x03, 0x45, + 0x08, 0x62, 0x0D, 0x6C, 0x10, 0xDE, 0x0F, 0x00, 0x0C, 0xB8, 0x06, + 0x46, 0x02, 0xEA, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFD, 0xFF, 0x00, + 0x00, 0xDE, 0xFF, 0x83, 0xFF, 0x94, 0xFF, 0x2F, 0x01, 0xF4, 0x04, + 0x2B, 0x0A, 0xCE, 0x0E, 0xAA, 0x10, 0xCE, 0x0E, 0x2B, 0x0A, 0xF4, + 0x04, 0x2F, 0x01, 0x94, 0xFF, 0x83, 0xFF, 0xDE, 0xFF, 0xFD, 0xFF, + 0xC0, 0xFF, 0x6E, 0xFF, 0xEA, 0xFF, 0x46, 0x02, 0xB8, 0x06, 0x00, + 0x0C, 0xDE, 0x0F, 0x6C, 0x10, 0x62, 0x0D, 0x45, 0x08, 0x62, 0x03, + 0x60, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, + 0xFF, 0x73, 0xFF, 0x7F, 0x00, 0xA4, 0x03, 0x99, 0x08, 0xA7, 0x0D, + 0x80, 0x10, 0xB7, 0x0F, 0xB1, 0x0B, 0x67, 0x06, 0x10, 0x02, 0xD7, + 0xFF, 0x70, 0xFF, 0xC6, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, 0xFF, + 0x7E, 0xFF, 0x9F, 0xFF, 0x5A, 0x01, 0x40, 0x05, 0x7F, 0x0A, 0x05, + 0x0F, 0xA9, 0x10, 0x95, 0x0E, 0xD8, 0x09, 0xAB, 0x04, 0x06, 0x01, + 0x8A, 0xFF, 0x88, 0xFF, 0xE3, 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, + 0xFF, 0xFF, 0xFF, 0x7E, 0x02, 0x0A, 0x07, 0x4D, 0x0C, 0x02, 0x10, + 0x55, 0x10, 0x1B, 0x0D, 0xF1, 0x07, 0x23, 0x03, 0x44, 0x00, 0x6D, + 0xFF, 0xA9, 0xFF, 0xF6, 0xFF, 0xED, 0xFF, 0x97, 0xFF, 0x77, 0xFF, + 0xA0, 0x00, 0xE7, 0x03, 0xED, 0x08, 0xE9, 0x0D, 0x90, 0x10, 0x8C, + 0x0F, 0x62, 0x0B, 0x17, 0x06, 0xDD, 0x01, 0xC6, 0xFF, 0x73, 0xFF, + 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x79, 0xFF, 0xAB, + 0xFF, 0x87, 0x01, 0x8C, 0x05, 0xD1, 0x0A, 0x38, 0x0F, 0xA3, 0x10, + 0x59, 0x0E, 0x84, 0x09, 0x63, 0x04, 0xDF, 0x00, 0x82, 0xFF, 0x8D, + 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, 0xB4, 0xFF, 0x6C, 0xFF, 0x16, 0x00, + 0xB7, 0x02, 0x5C, 0x07, 0x99, 0x0C, 0x23, 0x10, 0x3B, 0x10, 0xD3, + 0x0C, 0x9D, 0x07, 0xE6, 0x02, 0x29, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, + 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7D, 0xFF, 0xC2, 0x00, 0x2C, + 0x04, 0x42, 0x09, 0x29, 0x0E, 0x9C, 0x10, 0x5E, 0x0F, 0x11, 0x0B, + 0xC8, 0x05, 0xAC, 0x01, 0xB6, 0xFF, 0x76, 0xFF, 0xD1, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB9, 0xFF, 0xB7, 0x01, + 0xDA, 0x05, 0x23, 0x0B, 0x68, 0x0F, 0x9A, 0x10, 0x1B, 0x0E, 0x2F, + 0x09, 0x1C, 0x04, 0xBB, 0x00, 0x7B, 0xFF, 0x93, 0xFF, 0xEA, 0xFF, + 0xF7, 0xFF, 0xAE, 0xFF, 0x6C, 0xFF, 0x2F, 0x00, 0xF3, 0x02, 0xAF, + 0x07, 0xE3, 0x0C, 0x41, 0x10, 0x1C, 0x10, 0x88, 0x0C, 0x4A, 0x07, + 0xAA, 0x02, 0x11, 0x00, 0x6C, 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE6, + 0xFF, 0x8C, 0xFF, 0x84, 0xFF, 0xE7, 0x00, 0x73, 0x04, 0x96, 0x09, + 0x67, 0x0E, 0xA5, 0x10, 0x2D, 0x0F, 0xBF, 0x0A, 0x7B, 0x05, 0x7D, + 0x01, 0xA8, 0xFF, 0x7A, 0xFF, 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, + 0xCA, 0xFF, 0x72, 0xFF, 0xC9, 0xFF, 0xE8, 0x01, 0x29, 0x06, 0x73, + 0x0B, 0x96, 0x0F, 0x8D, 0x10, 0xDB, 0x0D, 0xDB, 0x08, 0xD8, 0x03, + 0x98, 0x00, 0x76, 0xFF, 0x99, 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA8, + 0xFF, 0x6E, 0xFF, 0x4A, 0x00, 0x31, 0x03, 0x03, 0x08, 0x2B, 0x0D, + 0x5B, 0x10, 0xFB, 0x0F, 0x3C, 0x0C, 0xF7, 0x06, 0x71, 0x02, 0xFA, + 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, 0xFC, 0xFF, 0xE2, 0xFF, 0x87, 0xFF, + 0x8C, 0xFF, 0x0F, 0x01, 0xBB, 0x04, 0xEA, 0x09, 0xA2, 0x0E, 0xA9, + 0x10, 0xF9, 0x0E, 0x6C, 0x0A, 0x2F, 0x05, 0x50, 0x01, 0x9C, 0xFF, + 0x7F, 0xFF, 0xDB, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, 0x70, + 0xFF, 0xDB, 0xFF, 0x1C, 0x02, 0x79, 0x06, 0xC3, 0x0B, 0xC0, 0x0F, + 0x7C, 0x10, 0x98, 0x0D, 0x86, 0x08, 0x95, 0x03, 0x78, 0x00, 0x72, + 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA2, 0xFF, 0x70, 0xFF, + 0x67, 0x00, 0x71, 0x03, 0x57, 0x08, 0x71, 0x0D, 0x71, 0x10, 0xD6, + 0x0F, 0xEF, 0x0B, 0xA6, 0x06, 0x3A, 0x02, 0xE6, 0xFF, 0x6E, 0xFF, + 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x96, + 0xFF, 0x38, 0x01, 0x05, 0x05, 0x3E, 0x0A, 0xDA, 0x0E, 0xAA, 0x10, + 0xC2, 0x0E, 0x19, 0x0A, 0xE4, 0x04, 0x25, 0x01, 0x91, 0xFF, 0x84, + 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xBF, 0xFF, 0x6E, 0xFF, 0xEF, 0xFF, + 0x52, 0x02, 0xCA, 0x06, 0x11, 0x0C, 0xE6, 0x0F, 0x68, 0x10, 0x52, + 0x0D, 0x32, 0x08, 0x54, 0x03, 0x5A, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, + 0xF4, 0xFF, 0xF0, 0xFF, 0x9C, 0xFF, 0x74, 0xFF, 0x86, 0x00, 0xB3, + 0x03, 0xAC, 0x08, 0xB6, 0x0D, 0x84, 0x10, 0xAD, 0x0F, 0xA0, 0x0B, + 0x55, 0x06, 0x05, 0x02, 0xD3, 0xFF, 0x71, 0xFF, 0xC7, 0xFF, 0xFE, + 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA1, 0xFF, 0x64, 0x01, + 0x50, 0x05, 0x91, 0x0A, 0x10, 0x0F, 0xA8, 0x10, 0x88, 0x0E, 0xC5, + 0x09, 0x9B, 0x04, 0xFD, 0x00, 0x88, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, + 0xFB, 0xFF, 0xB9, 0xFF, 0x6C, 0xFF, 0x04, 0x00, 0x8A, 0x02, 0x1C, + 0x07, 0x5E, 0x0C, 0x0A, 0x10, 0x50, 0x10, 0x0B, 0x0D, 0xDE, 0x07, + 0x15, 0x03, 0x3E, 0x00, 0x6D, 0xFF, 0xAB, 0xFF, 0xF6, 0xFF, 0xEC, + 0xFF, 0x96, 0xFF, 0x78, 0xFF, 0xA7, 0x00, 0xF6, 0x03, 0x00, 0x09, + 0xF8, 0x0D, 0x93, 0x10, 0x82, 0x0F, 0x50, 0x0B, 0x06, 0x06, 0xD2, + 0x01, 0xC2, 0xFF, 0x74, 0xFF, 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0xD4, 0xFF, 0x79, 0xFF, 0xAE, 0xFF, 0x91, 0x01, 0x9D, 0x05, 0xE3, + 0x0A, 0x43, 0x0F, 0xA1, 0x10, 0x4C, 0x0E, 0x71, 0x09, 0x53, 0x04, + 0xD7, 0x00, 0x80, 0xFF, 0x8E, 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB3, + 0xFF, 0x6C, 0xFF, 0x1C, 0x00, 0xC4, 0x02, 0x6F, 0x07, 0xAA, 0x0C, + 0x2A, 0x10, 0x34, 0x10, 0xC2, 0x0C, 0x8A, 0x07, 0xD8, 0x02, 0x24, + 0x00, 0x6C, 0xFF, 0xB1, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x90, 0xFF, + 0x7E, 0xFF, 0xCB, 0x00, 0x3B, 0x04, 0x55, 0x09, 0x37, 0x0E, 0x9E, + 0x10, 0x53, 0x0F, 0xFF, 0x0A, 0xB7, 0x05, 0xA1, 0x01, 0xB3, 0xFF, + 0x77, 0xFF, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x75, + 0xFF, 0xBD, 0xFF, 0xC1, 0x01, 0xEB, 0x05, 0x35, 0x0B, 0x73, 0x0F, + 0x97, 0x10, 0x0D, 0x0E, 0x1C, 0x09, 0x0D, 0x04, 0xB3, 0x00, 0x7A, + 0xFF, 0x94, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAD, 0xFF, 0x6D, 0xFF, + 0x35, 0x00, 0x01, 0x03, 0xC2, 0x07, 0xF3, 0x0C, 0x47, 0x10, 0x15, + 0x10, 0x78, 0x0C, 0x37, 0x07, 0x9D, 0x02, 0x0C, 0x00, 0x6C, 0xFF, + 0xB7, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, 0xF0, + 0x00, 0x83, 0x04, 0xA9, 0x09, 0x74, 0x0E, 0xA6, 0x10, 0x21, 0x0F, + 0xAD, 0x0A, 0x6A, 0x05, 0x73, 0x01, 0xA5, 0xFF, 0x7B, 0xFF, 0xD7, + 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC9, 0xFF, 0x72, 0xFF, 0xCD, 0xFF, + 0xF4, 0x01, 0x3B, 0x06, 0x85, 0x0B, 0x9F, 0x0F, 0x89, 0x10, 0xCC, + 0x0D, 0xC8, 0x08, 0xC9, 0x03, 0x91, 0x00, 0x75, 0xFF, 0x9A, 0xFF, + 0xEF, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x50, 0x00, 0x3F, + 0x03, 0x16, 0x08, 0x3B, 0x0D, 0x60, 0x10, 0xF3, 0x0F, 0x2B, 0x0C, + 0xE5, 0x06, 0x65, 0x02, 0xF6, 0xFF, 0x6D, 0xFF, 0xBD, 0xFF, 0xFC, + 0xFF, 0xE1, 0xFF, 0x85, 0xFF, 0x8E, 0xFF, 0x18, 0x01, 0xCB, 0x04, + 0xFD, 0x09, 0xAF, 0x0E, 0xAA, 0x10, 0xED, 0x0E, 0x5A, 0x0A, 0x1E, + 0x05, 0x46, 0x01, 0x9A, 0xFF, 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, + 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, 0xFF, 0xDF, 0xFF, 0x28, 0x02, 0x8B, + 0x06, 0xD5, 0x0B, 0xC9, 0x0F, 0x78, 0x10, 0x88, 0x0D, 0x73, 0x08, + 0x86, 0x03, 0x71, 0x00, 0x71, 0xFF, 0xA0, 0xFF, 0xF2, 0xFF, 0xF2, + 0xFF, 0xA1, 0xFF, 0x71, 0xFF, 0x6E, 0x00, 0x7F, 0x03, 0x6A, 0x08, + 0x81, 0x0D, 0x76, 0x10, 0xCD, 0x0F, 0xDD, 0x0B, 0x94, 0x06, 0x2E, + 0x02, 0xE1, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, + 0xDC, 0xFF, 0x80, 0xFF, 0x98, 0xFF, 0x42, 0x01, 0x16, 0x05, 0x50, + 0x0A, 0xE7, 0x0E, 0xAA, 0x10, 0xB5, 0x0E, 0x06, 0x0A, 0xD3, 0x04, + 0x1C, 0x01, 0x8F, 0xFF, 0x85, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, 0xBE, + 0xFF, 0x6D, 0xFF, 0xF3, 0xFF, 0x5E, 0x02, 0xDC, 0x06, 0x23, 0x0C, + 0xEF, 0x0F, 0x63, 0x10, 0x43, 0x0D, 0x1F, 0x08, 0x46, 0x03, 0x53, + 0x00, 0x6E, 0xFF, 0xA6, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, 0xFF, + 0x75, 0xFF, 0x8D, 0x00, 0xC1, 0x03, 0xBE, 0x08, 0xC4, 0x0D, 0x88, + 0x10, 0xA4, 0x0F, 0x8E, 0x0B, 0x43, 0x06, 0xF9, 0x01, 0xCF, 0xFF, + 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, 0x7C, + 0xFF, 0xA4, 0xFF, 0x6E, 0x01, 0x61, 0x05, 0xA3, 0x0A, 0x1C, 0x0F, + 0xA7, 0x10, 0x7B, 0x0E, 0xB2, 0x09, 0x8B, 0x04, 0xF4, 0x00, 0x86, + 0xFF, 0x8A, 0xFF, 0xE4, 0xFF, 0xFA, 0xFF, 0xB8, 0xFF, 0x6C, 0xFF, + 0x09, 0x00, 0x97, 0x02, 0x2E, 0x07, 0x6F, 0x0C, 0x11, 0x10, 0x4A, + 0x10, 0xFB, 0x0C, 0xCB, 0x07, 0x07, 0x03, 0x38, 0x00, 0x6D, 0xFF, + 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, 0xAF, + 0x00, 0x05, 0x04, 0x13, 0x09, 0x06, 0x0E, 0x96, 0x10, 0x78, 0x0F, + 0x3E, 0x0B, 0xF4, 0x05, 0xC7, 0x01, 0xBF, 0xFF, 0x74, 0xFF, 0xCE, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x78, 0xFF, 0xB1, 0xFF, + 0x9C, 0x01, 0xAE, 0x05, 0xF6, 0x0A, 0x4E, 0x0F, 0x9F, 0x10, 0x3E, + 0x0E, 0x5E, 0x09, 0x43, 0x04, 0xCF, 0x00, 0x7F, 0xFF, 0x90, 0xFF, + 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x21, 0x00, 0xD2, + 0x02, 0x81, 0x07, 0xBA, 0x0C, 0x31, 0x10, 0x2E, 0x10, 0xB2, 0x0C, + 0x78, 0x07, 0xCB, 0x02, 0x1E, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, 0xF9, + 0xFF, 0xE8, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xD3, 0x00, 0x4B, 0x04, + 0x67, 0x09, 0x45, 0x0E, 0xA0, 0x10, 0x48, 0x0F, 0xEC, 0x0A, 0xA6, + 0x05, 0x97, 0x01, 0xB0, 0xFF, 0x78, 0xFF, 0xD3, 0xFF, 0x00, 0x00, + 0xFF, 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC0, 0xFF, 0xCC, 0x01, 0xFD, + 0x05, 0x47, 0x0B, 0x7D, 0x0F, 0x94, 0x10, 0xFF, 0x0D, 0x0A, 0x09, + 0xFE, 0x03, 0xAB, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, 0xF7, + 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x3B, 0x00, 0x0E, 0x03, 0xD5, 0x07, + 0x03, 0x0D, 0x4D, 0x10, 0x0E, 0x10, 0x67, 0x0C, 0x25, 0x07, 0x91, + 0x02, 0x07, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, + 0x89, 0xFF, 0x87, 0xFF, 0xF9, 0x00, 0x93, 0x04, 0xBC, 0x09, 0x82, + 0x0E, 0xA7, 0x10, 0x16, 0x0F, 0x9A, 0x0A, 0x59, 0x05, 0x69, 0x01, + 0xA3, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC8, + 0xFF, 0x71, 0xFF, 0xD1, 0xFF, 0xFF, 0x01, 0x4C, 0x06, 0x97, 0x0B, + 0xA9, 0x0F, 0x86, 0x10, 0xBD, 0x0D, 0xB5, 0x08, 0xBA, 0x03, 0x8A, + 0x00, 0x74, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, + 0x6F, 0xFF, 0x57, 0x00, 0x4D, 0x03, 0x29, 0x08, 0x4B, 0x0D, 0x65, + 0x10, 0xEB, 0x0F, 0x1A, 0x0C, 0xD3, 0x06, 0x58, 0x02, 0xF1, 0xFF, + 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x84, 0xFF, 0x90, + 0xFF, 0x21, 0x01, 0xDC, 0x04, 0x10, 0x0A, 0xBB, 0x0E, 0xAA, 0x10, + 0xE1, 0x0E, 0x47, 0x0A, 0x0D, 0x05, 0x3D, 0x01, 0x97, 0xFF, 0x81, + 0xFF, 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC2, 0xFF, 0x6F, 0xFF, + 0xE4, 0xFF, 0x34, 0x02, 0x9D, 0x06, 0xE6, 0x0B, 0xD1, 0x0F, 0x73, + 0x10, 0x79, 0x0D, 0x61, 0x08, 0x78, 0x03, 0x6A, 0x00, 0x70, 0xFF, + 0xA1, 0xFF, 0xF2, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x74, + 0x00, 0x8E, 0x03, 0x7D, 0x08, 0x90, 0x0D, 0x7A, 0x10, 0xC4, 0x0F, + 0xCC, 0x0B, 0x82, 0x06, 0x22, 0x02, 0xDD, 0xFF, 0x6F, 0xFF, 0xC4, + 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9B, 0xFF, + 0x4B, 0x01, 0x26, 0x05, 0x63, 0x0A, 0xF3, 0x0E, 0xAA, 0x10, 0xA8, + 0x0E, 0xF4, 0x09, 0xC3, 0x04, 0x13, 0x01, 0x8D, 0xFF, 0x86, 0xFF, + 0xE1, 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xF8, 0xFF, 0x6B, + 0x02, 0xEE, 0x06, 0x34, 0x0C, 0xF7, 0x0F, 0x5D, 0x10, 0x33, 0x0D, + 0x0D, 0x08, 0x38, 0x03, 0x4D, 0x00, 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, + 0xFF, 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x94, 0x00, 0xD0, 0x03, + 0xD1, 0x08, 0xD3, 0x0D, 0x8B, 0x10, 0x9A, 0x0F, 0x7C, 0x0B, 0x32, + 0x06, 0xEE, 0x01, 0xCB, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, + 0x00, 0x00, 0xD6, 0xFF, 0x7B, 0xFF, 0xA7, 0xFF, 0x78, 0x01, 0x72, + 0x05, 0xB6, 0x0A, 0x27, 0x0F, 0xA5, 0x10, 0x6E, 0x0E, 0xA0, 0x09, + 0x7B, 0x04, 0xEC, 0x00, 0x85, 0xFF, 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, + 0xFF, 0xB6, 0xFF, 0x6C, 0xFF, 0x0E, 0x00, 0xA4, 0x02, 0x41, 0x07, + 0x80, 0x0C, 0x19, 0x10, 0x44, 0x10, 0xEB, 0x0C, 0xB9, 0x07, 0xFA, + 0x02, 0x32, 0x00, 0x6D, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, + 0x93, 0xFF, 0x7B, 0xFF, 0xB7, 0x00, 0x15, 0x04, 0x26, 0x09, 0x14, + 0x0E, 0x98, 0x10, 0x6D, 0x0F, 0x2C, 0x0B, 0xE3, 0x05, 0xBC, 0x01, + 0xBB, 0xFF, 0x75, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, + 0xFF, 0x77, 0xFF, 0xB5, 0xFF, 0xA6, 0x01, 0xC0, 0x05, 0x08, 0x0B, + 0x58, 0x0F, 0x9D, 0x10, 0x30, 0x0E, 0x4B, 0x09, 0x34, 0x04, 0xC6, + 0x00, 0x7D, 0xFF, 0x91, 0xFF, 0xE9, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, + 0x6C, 0xFF, 0x27, 0x00, 0xDF, 0x02, 0x94, 0x07, 0xCA, 0x0C, 0x37, + 0x10, 0x27, 0x10, 0xA1, 0x0C, 0x65, 0x07, 0xBE, 0x02, 0x19, 0x00, + 0x6C, 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8E, 0xFF, 0x81, + 0xFF, 0xDB, 0x00, 0x5B, 0x04, 0x7A, 0x09, 0x53, 0x0E, 0xA2, 0x10, + 0x3D, 0x0F, 0xDA, 0x0A, 0x95, 0x05, 0x8C, 0x01, 0xAD, 0xFF, 0x79, + 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xFF, 0x73, 0xFF, + 0xC4, 0xFF, 0xD7, 0x01, 0x0E, 0x06, 0x59, 0x0B, 0x87, 0x0F, 0x91, + 0x10, 0xF0, 0x0D, 0xF7, 0x08, 0xEF, 0x03, 0xA3, 0x00, 0x78, 0xFF, + 0x97, 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xAA, 0xFF, 0x6D, 0xFF, 0x41, + 0x00, 0x1C, 0x03, 0xE7, 0x07, 0x13, 0x0D, 0x52, 0x10, 0x06, 0x10, + 0x56, 0x0C, 0x13, 0x07, 0x84, 0x02, 0x02, 0x00, 0x6D, 0xFF, 0xBA, + 0xFF, 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x01, 0x01, + 0xA3, 0x04, 0xCE, 0x09, 0x8F, 0x0E, 0xA8, 0x10, 0x0A, 0x0F, 0x88, + 0x0A, 0x48, 0x05, 0x5F, 0x01, 0xA0, 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, + 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, 0x70, 0xFF, 0xD5, 0xFF, 0x0B, + 0x02, 0x5E, 0x06, 0xA9, 0x0B, 0xB2, 0x0F, 0x82, 0x10, 0xAE, 0x0D, + 0xA2, 0x08, 0xAB, 0x03, 0x82, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, + 0xFF, 0xF3, 0xFF, 0xA4, 0xFF, 0x6F, 0xFF, 0x5D, 0x00, 0x5B, 0x03, + 0x3B, 0x08, 0x5A, 0x0D, 0x6A, 0x10, 0xE2, 0x0F, 0x09, 0x0C, 0xC1, + 0x06, 0x4C, 0x02, 0xEC, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFC, 0xFF, + 0xDF, 0xFF, 0x83, 0xFF, 0x93, 0xFF, 0x2A, 0x01, 0xEC, 0x04, 0x22, + 0x0A, 0xC8, 0x0E, 0xAB, 0x10, 0xD4, 0x0E, 0x35, 0x0A, 0xFD, 0x04, + 0x33, 0x01, 0x95, 0xFF, 0x82, 0xFF, 0xDE, 0xFF, 0x00, 0x00, 0xFD, + 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE8, 0xFF, 0x40, 0x02, 0xAF, 0x06, + 0xF7, 0x0B, 0xDA, 0x0F, 0x6F, 0x10, 0x6A, 0x0D, 0x4E, 0x08, 0x6A, + 0x03, 0x64, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, + 0x9E, 0xFF, 0x72, 0xFF, 0x7B, 0x00, 0x9C, 0x03, 0x90, 0x08, 0x9F, + 0x0D, 0x7E, 0x10, 0xBB, 0x0F, 0xBA, 0x0B, 0x70, 0x06, 0x16, 0x02, + 0xD9, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, + 0xFF, 0x7E, 0xFF, 0x9D, 0xFF, 0x55, 0x01, 0x37, 0x05, 0x75, 0x0A, + 0xFF, 0x0E, 0xA9, 0x10, 0x9C, 0x0E, 0xE1, 0x09, 0xB3, 0x04, 0x0A, + 0x01, 0x8B, 0xFF, 0x87, 0xFF, 0xE2, 0xFF, 0xFB, 0xFF, 0xBB, 0xFF, + 0x6D, 0xFF, 0xFD, 0xFF, 0x77, 0x02, 0x01, 0x07, 0x45, 0x0C, 0xFF, + 0x0F, 0x58, 0x10, 0x23, 0x0D, 0xFA, 0x07, 0x2A, 0x03, 0x47, 0x00, + 0x6E, 0xFF, 0xA9, 0xFF, 0xF5, 0xFF, 0xED, 0xFF, 0x98, 0xFF, 0x77, + 0xFF, 0x9C, 0x00, 0xDF, 0x03, 0xE4, 0x08, 0xE2, 0x0D, 0x8E, 0x10, + 0x91, 0x0F, 0x6B, 0x0B, 0x20, 0x06, 0xE3, 0x01, 0xC8, 0xFF, 0x73, + 0xFF, 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x7A, 0xFF, + 0xAA, 0xFF, 0x82, 0x01, 0x83, 0x05, 0xC8, 0x0A, 0x32, 0x0F, 0xA4, + 0x10, 0x60, 0x0E, 0x8D, 0x09, 0x6B, 0x04, 0xE3, 0x00, 0x83, 0xFF, + 0x8D, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, 0xB5, 0xFF, 0x6C, 0xFF, 0x14, + 0x00, 0xB1, 0x02, 0x53, 0x07, 0x91, 0x0C, 0x20, 0x10, 0x3E, 0x10, + 0xDB, 0x0C, 0xA6, 0x07, 0xEC, 0x02, 0x2C, 0x00, 0x6C, 0xFF, 0xAF, + 0xFF, 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7C, 0xFF, 0xBE, 0x00, + 0x24, 0x04, 0x38, 0x09, 0x22, 0x0E, 0x9B, 0x10, 0x63, 0x0F, 0x1A, + 0x0B, 0xD1, 0x05, 0xB1, 0x01, 0xB8, 0xFF, 0x76, 0xFF, 0xD0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB8, 0xFF, 0xB1, + 0x01, 0xD1, 0x05, 0x1A, 0x0B, 0x63, 0x0F, 0x9B, 0x10, 0x22, 0x0E, + 0x38, 0x09, 0x24, 0x04, 0xBE, 0x00, 0x7C, 0xFF, 0x92, 0xFF, 0xEA, + 0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0x6C, 0xFF, 0x2C, 0x00, 0xEC, 0x02, + 0xA6, 0x07, 0xDB, 0x0C, 0x3E, 0x10, 0x20, 0x10, 0x91, 0x0C, 0x53, + 0x07, 0xB1, 0x02, 0x14, 0x00, 0x6C, 0xFF, 0xB5, 0xFF, 0xFA, 0xFF, + 0xE6, 0xFF, 0x8D, 0xFF, 0x83, 0xFF, 0xE3, 0x00, 0x6B, 0x04, 0x8D, + 0x09, 0x60, 0x0E, 0xA4, 0x10, 0x32, 0x0F, 0xC8, 0x0A, 0x83, 0x05, + 0x82, 0x01, 0xAA, 0xFF, 0x7A, 0xFF, 0xD5, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC8, 0xFF, 0xE3, 0x01, 0x20, 0x06, + 0x6B, 0x0B, 0x91, 0x0F, 0x8E, 0x10, 0xE2, 0x0D, 0xE4, 0x08, 0xDF, + 0x03, 0x9C, 0x00, 0x77, 0xFF, 0x98, 0xFF, 0xED, 0xFF, 0xF5, 0xFF, + 0xA9, 0xFF, 0x6E, 0xFF, 0x47, 0x00, 0x2A, 0x03, 0xFA, 0x07, 0x23, + 0x0D, 0x58, 0x10, 0xFF, 0x0F, 0x45, 0x0C, 0x01, 0x07, 0x77, 0x02, + 0xFD, 0xFF, 0x6D, 0xFF, 0xBB, 0xFF, 0xFB, 0xFF, 0xE2, 0xFF, 0x87, + 0xFF, 0x8B, 0xFF, 0x0A, 0x01, 0xB3, 0x04, 0xE1, 0x09, 0x9C, 0x0E, + 0xA9, 0x10, 0xFF, 0x0E, 0x75, 0x0A, 0x37, 0x05, 0x55, 0x01, 0x9D, + 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, + 0x70, 0xFF, 0xD9, 0xFF, 0x16, 0x02, 0x70, 0x06, 0xBA, 0x0B, 0xBB, + 0x0F, 0x7E, 0x10, 0x9F, 0x0D, 0x90, 0x08, 0x9C, 0x03, 0x7B, 0x00, + 0x72, 0xFF, 0x9E, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA3, 0xFF, 0x70, + 0xFF, 0x64, 0x00, 0x6A, 0x03, 0x4E, 0x08, 0x6A, 0x0D, 0x6F, 0x10, + 0xDA, 0x0F, 0xF7, 0x0B, 0xAF, 0x06, 0x40, 0x02, 0xE8, 0xFF, 0x6E, + 0xFF, 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDE, 0xFF, 0x82, 0xFF, + 0x95, 0xFF, 0x33, 0x01, 0xFD, 0x04, 0x35, 0x0A, 0xD4, 0x0E, 0xAB, + 0x10, 0xC8, 0x0E, 0x22, 0x0A, 0xEC, 0x04, 0x2A, 0x01, 0x93, 0xFF, + 0x83, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xC0, 0xFF, 0x6E, 0xFF, 0xEC, + 0xFF, 0x4C, 0x02, 0xC1, 0x06, 0x09, 0x0C, 0xE2, 0x0F, 0x6A, 0x10, + 0x5A, 0x0D, 0x3B, 0x08, 0x5B, 0x03, 0x5D, 0x00, 0x6F, 0xFF, 0xA4, + 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, 0xFF, 0x73, 0xFF, 0x82, 0x00, + 0xAB, 0x03, 0xA2, 0x08, 0xAE, 0x0D, 0x82, 0x10, 0xB2, 0x0F, 0xA9, + 0x0B, 0x5E, 0x06, 0x0B, 0x02, 0xD5, 0xFF, 0x70, 0xFF, 0xC7, 0xFF, + 0xFE, 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA0, 0xFF, 0x5F, + 0x01, 0x48, 0x05, 0x88, 0x0A, 0x0A, 0x0F, 0xA8, 0x10, 0x8F, 0x0E, + 0xCE, 0x09, 0xA3, 0x04, 0x01, 0x01, 0x89, 0xFF, 0x88, 0xFF, 0xE3, + 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, 0xFF, 0x02, 0x00, 0x84, 0x02, + 0x13, 0x07, 0x56, 0x0C, 0x06, 0x10, 0x52, 0x10, 0x13, 0x0D, 0xE7, + 0x07, 0x1C, 0x03, 0x41, 0x00, 0x6D, 0xFF, 0xAA, 0xFF, 0xF6, 0xFF, + 0xED, 0xFF, 0x97, 0xFF, 0x78, 0xFF, 0xA3, 0x00, 0xEF, 0x03, 0xF7, + 0x08, 0xF0, 0x0D, 0x91, 0x10, 0x87, 0x0F, 0x59, 0x0B, 0x0E, 0x06, + 0xD7, 0x01, 0xC4, 0xFF, 0x73, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0xD4, 0xFF, 0x79, 0xFF, 0xAD, 0xFF, 0x8C, 0x01, 0x95, 0x05, + 0xDA, 0x0A, 0x3D, 0x0F, 0xA2, 0x10, 0x53, 0x0E, 0x7A, 0x09, 0x5B, + 0x04, 0xDB, 0x00, 0x81, 0xFF, 0x8E, 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, + 0xB4, 0xFF, 0x6C, 0xFF, 0x19, 0x00, 0xBE, 0x02, 0x65, 0x07, 0xA1, + 0x0C, 0x27, 0x10, 0x37, 0x10, 0xCA, 0x0C, 0x94, 0x07, 0xDF, 0x02, + 0x27, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x91, + 0xFF, 0x7D, 0xFF, 0xC6, 0x00, 0x34, 0x04, 0x4B, 0x09, 0x30, 0x0E, + 0x9D, 0x10, 0x58, 0x0F, 0x08, 0x0B, 0xC0, 0x05, 0xA6, 0x01, 0xB5, + 0xFF, 0x77, 0xFF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, + 0x75, 0xFF, 0xBB, 0xFF, 0xBC, 0x01, 0xE3, 0x05, 0x2C, 0x0B, 0x6D, + 0x0F, 0x98, 0x10, 0x14, 0x0E, 0x26, 0x09, 0x15, 0x04, 0xB7, 0x00, + 0x7B, 0xFF, 0x93, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAE, 0xFF, 0x6D, + 0xFF, 0x32, 0x00, 0xFA, 0x02, 0xB9, 0x07, 0xEB, 0x0C, 0x44, 0x10, + 0x19, 0x10, 0x80, 0x0C, 0x41, 0x07, 0xA4, 0x02, 0x0E, 0x00, 0x6C, + 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, + 0xEC, 0x00, 0x7B, 0x04, 0xA0, 0x09, 0x6E, 0x0E, 0xA5, 0x10, 0x27, + 0x0F, 0xB6, 0x0A, 0x72, 0x05, 0x78, 0x01, 0xA7, 0xFF, 0x7B, 0xFF, + 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xCA, 0xFF, 0x72, 0xFF, 0xCB, + 0xFF, 0xEE, 0x01, 0x32, 0x06, 0x7C, 0x0B, 0x9A, 0x0F, 0x8B, 0x10, + 0xD3, 0x0D, 0xD1, 0x08, 0xD0, 0x03, 0x94, 0x00, 0x76, 0xFF, 0x99, + 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x4D, 0x00, + 0x38, 0x03, 0x0D, 0x08, 0x33, 0x0D, 0x5D, 0x10, 0xF7, 0x0F, 0x34, + 0x0C, 0xEE, 0x06, 0x6B, 0x02, 0xF8, 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, + 0xFC, 0xFF, 0xE1, 0xFF, 0x86, 0xFF, 0x8D, 0xFF, 0x13, 0x01, 0xC3, + 0x04, 0xF4, 0x09, 0xA8, 0x0E, 0xAA, 0x10, 0xF3, 0x0E, 0x63, 0x0A, + 0x26, 0x05, 0x4B, 0x01, 0x9B, 0xFF, 0x7F, 0xFF, 0xDB, 0xFF, 0x00, + 0x00, 0xFD, 0xFF, 0xC4, 0xFF, 0x6F, 0xFF, 0xDD, 0xFF, 0x22, 0x02, + 0x82, 0x06, 0xCC, 0x0B, 0xC4, 0x0F, 0x7A, 0x10, 0x90, 0x0D, 0x7D, + 0x08, 0x8E, 0x03, 0x74, 0x00, 0x72, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xA1, 0xFF, 0x70, 0xFF, 0x6A, 0x00, 0x78, 0x03, 0x61, + 0x08, 0x79, 0x0D, 0x73, 0x10, 0xD1, 0x0F, 0xE6, 0x0B, 0x9D, 0x06, + 0x34, 0x02, 0xE4, 0xFF, 0x6F, 0xFF, 0xC2, 0xFF, 0xFD, 0xFF, 0x00, + 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x97, 0xFF, 0x3D, 0x01, 0x0D, 0x05, + 0x47, 0x0A, 0xE1, 0x0E, 0xAA, 0x10, 0xBB, 0x0E, 0x10, 0x0A, 0xDC, + 0x04, 0x21, 0x01, 0x90, 0xFF, 0x84, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, + 0xBE, 0xFF, 0x6D, 0xFF, 0xF1, 0xFF, 0x58, 0x02, 0xD3, 0x06, 0x1A, + 0x0C, 0xEB, 0x0F, 0x65, 0x10, 0x4B, 0x0D, 0x29, 0x08, 0x4D, 0x03, + 0x57, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, + 0xFF, 0x74, 0xFF, 0x8A, 0x00, 0xBA, 0x03, 0xB5, 0x08, 0xBD, 0x0D, + 0x86, 0x10, 0xA9, 0x0F, 0x97, 0x0B, 0x4C, 0x06, 0xFF, 0x01, 0xD1, + 0xFF, 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, + 0x7C, 0xFF, 0xA3, 0xFF, 0x69, 0x01, 0x59, 0x05, 0x9A, 0x0A, 0x16, + 0x0F, 0xA7, 0x10, 0x82, 0x0E, 0xBC, 0x09, 0x93, 0x04, 0xF9, 0x00, + 0x87, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, 0xFB, 0xFF, 0xB8, 0xFF, 0x6C, + 0xFF, 0x07, 0x00, 0x91, 0x02, 0x25, 0x07, 0x67, 0x0C, 0x0E, 0x10, + 0x4D, 0x10, 0x03, 0x0D, 0xD5, 0x07, 0x0E, 0x03, 0x3B, 0x00, 0x6D, + 0xFF, 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, + 0xAB, 0x00, 0xFE, 0x03, 0x0A, 0x09, 0xFF, 0x0D, 0x94, 0x10, 0x7D, + 0x0F, 0x47, 0x0B, 0xFD, 0x05, 0xCC, 0x01, 0xC0, 0xFF, 0x74, 0xFF, + 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD3, 0xFF, 0x78, 0xFF, 0xB0, + 0xFF, 0x97, 0x01, 0xA6, 0x05, 0xEC, 0x0A, 0x48, 0x0F, 0xA0, 0x10, + 0x45, 0x0E, 0x67, 0x09, 0x4B, 0x04, 0xD3, 0x00, 0x80, 0xFF, 0x8F, + 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x1E, 0x00, + 0xCB, 0x02, 0x78, 0x07, 0xB2, 0x0C, 0x2E, 0x10, 0x31, 0x10, 0xBA, + 0x0C, 0x81, 0x07, 0xD2, 0x02, 0x21, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, + 0xF9, 0xFF, 0xE8, 0xFF, 0x90, 0xFF, 0x7F, 0xFF, 0xCF, 0x00, 0x43, + 0x04, 0x5E, 0x09, 0x3E, 0x0E, 0x9F, 0x10, 0x4E, 0x0F, 0xF6, 0x0A, + 0xAE, 0x05, 0x9C, 0x01, 0xB1, 0xFF, 0x78, 0xFF, 0xD2, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x74, 0xFF, 0xBF, 0xFF, 0xC7, 0x01, + 0xF4, 0x05, 0x3E, 0x0B, 0x78, 0x0F, 0x96, 0x10, 0x06, 0x0E, 0x13, + 0x09, 0x05, 0x04, 0xAF, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, + 0xF7, 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x38, 0x00, 0x07, 0x03, 0xCB, + 0x07, 0xFB, 0x0C, 0x4A, 0x10, 0x11, 0x10, 0x6F, 0x0C, 0x2E, 0x07, + 0x97, 0x02, 0x09, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFA, 0xFF, 0xE4, + 0xFF, 0x8A, 0xFF, 0x86, 0xFF, 0xF4, 0x00, 0x8B, 0x04, 0xB2, 0x09, + 0x7B, 0x0E, 0xA7, 0x10, 0x1C, 0x0F, 0xA3, 0x0A, 0x61, 0x05, 0x6E, + 0x01, 0xA4, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, + 0xC8, 0xFF, 0x71, 0xFF, 0xCF, 0xFF, 0xF9, 0x01, 0x43, 0x06, 0x8E, + 0x0B, 0xA4, 0x0F, 0x88, 0x10, 0xC4, 0x0D, 0xBE, 0x08, 0xC1, 0x03, + 0x8D, 0x00, 0x75, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA6, + 0xFF, 0x6E, 0xFF, 0x53, 0x00, 0x46, 0x03, 0x1F, 0x08, 0x43, 0x0D, + 0x63, 0x10, 0xEF, 0x0F, 0x23, 0x0C, 0xDC, 0x06, 0x5E, 0x02, 0xF3, + 0xFF, 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x85, 0xFF, + 0x8F, 0xFF, 0x1C, 0x01, 0xD3, 0x04, 0x06, 0x0A, 0xB5, 0x0E, 0xAA, + 0x10, 0xE7, 0x0E, 0x50, 0x0A, 0x16, 0x05, 0x42, 0x01, 0x98, 0xFF, + 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, + 0xFF, 0xE1, 0xFF, 0x2E, 0x02, 0x94, 0x06, 0xDD, 0x0B, 0xCD, 0x0F, + 0x76, 0x10, 0x81, 0x0D, 0x6A, 0x08, 0x7F, 0x03, 0x6E, 0x00, 0x71, + 0xFF, 0xA1, 0xFF, 0xF2, 0xFF, 0x00, 0x00, 0x15, 0x00, 0xD1, 0xFF, + 0x8B, 0xFE, 0xBC, 0xFD, 0xE1, 0x00, 0x84, 0x09, 0xB0, 0x13, 0x47, + 0x18, 0xB0, 0x13, 0x84, 0x09, 0xE1, 0x00, 0xBC, 0xFD, 0x8B, 0xFE, + 0xD1, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xDA, 0x00, 0x30, + 0x00, 0x5D, 0xFC, 0xB3, 0xFC, 0x35, 0x0A, 0xC2, 0x1C, 0x24, 0x20, + 0x48, 0x10, 0x5D, 0xFF, 0x74, 0xFB, 0x3A, 0xFF, 0xFB, 0x00, 0x42, + 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2C, 0x00, 0xF3, 0x00, 0xAD, 0xFF, + 0xC5, 0xFB, 0x11, 0xFE, 0xAF, 0x0D, 0xEF, 0x1E, 0x68, 0x1E, 0xBC, + 0x0C, 0xA7, 0xFD, 0xEA, 0xFB, 0xD3, 0xFF, 0xEE, 0x00, 0x24, 0x00, + 0xFA, 0xFF, 0xF7, 0xFF, 0x4C, 0x00, 0xFB, 0x00, 0x0C, 0xFF, 0x5F, + 0xFB, 0xE8, 0xFF, 0x3D, 0x11, 0x7E, 0x20, 0x13, 0x1C, 0x4C, 0x09, + 0x6A, 0xFC, 0x8C, 0xFC, 0x4E, 0x00, 0xD1, 0x00, 0x0E, 0x00, 0xFD, + 0xFF, 0xF7, 0xFF, 0x72, 0x00, 0xEC, 0x00, 0x55, 0xFE, 0x3D, 0xFB, + 0x37, 0x02, 0xBE, 0x14, 0x5D, 0x21, 0x40, 0x19, 0x18, 0x06, 0xA2, + 0xFB, 0x47, 0xFD, 0xA7, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xC0, 0x00, 0x92, 0xFD, 0x73, + 0xFB, 0xF2, 0x04, 0x0E, 0x18, 0x81, 0x21, 0x0C, 0x16, 0x37, 0x03, + 0x47, 0xFB, 0x0B, 0xFE, 0xDF, 0x00, 0x82, 0x00, 0xF9, 0xFF, 0xFE, + 0xFF, 0x08, 0x00, 0xC3, 0x00, 0x74, 0x00, 0xD2, 0xFC, 0x10, 0xFC, + 0x08, 0x08, 0x0A, 0x1B, 0xE9, 0x20, 0x9A, 0x12, 0xBE, 0x00, 0x49, + 0xFB, 0xC8, 0xFE, 0xF9, 0x00, 0x5A, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, + 0x1B, 0x00, 0xE4, 0x00, 0x06, 0x00, 0x24, 0xFC, 0x1E, 0xFD, 0x65, + 0x0B, 0x94, 0x1D, 0x9D, 0x1F, 0x0D, 0x0F, 0xB8, 0xFE, 0x96, 0xFB, + 0x72, 0xFF, 0xF9, 0x00, 0x37, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x36, + 0x00, 0xF8, 0x00, 0x78, 0xFF, 0x9B, 0xFB, 0xA6, 0xFE, 0xE9, 0x0E, + 0x8D, 0x1F, 0xAA, 0x1D, 0x87, 0x0B, 0x2B, 0xFD, 0x1E, 0xFC, 0x02, + 0x00, 0xE5, 0x00, 0x1C, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x58, 0x00, + 0xF9, 0x00, 0xCF, 0xFE, 0x4A, 0xFB, 0xA7, 0x00, 0x77, 0x12, 0xE0, + 0x20, 0x26, 0x1B, 0x28, 0x08, 0x18, 0xFC, 0xCB, 0xFC, 0x71, 0x00, + 0xC5, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x80, 0x00, 0xE1, + 0x00, 0x13, 0xFE, 0x45, 0xFB, 0x1D, 0x03, 0xEB, 0x15, 0x7F, 0x21, + 0x2D, 0x18, 0x0E, 0x05, 0x77, 0xFB, 0x8B, 0xFD, 0xBE, 0x00, 0x9D, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA9, 0x00, + 0xAA, 0x00, 0x4F, 0xFD, 0x9D, 0xFB, 0xFA, 0x05, 0x22, 0x19, 0x62, + 0x21, 0xE0, 0x14, 0x50, 0x02, 0x3E, 0xFB, 0x4E, 0xFE, 0xEB, 0x00, + 0x73, 0x00, 0xF7, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xD0, 0x00, 0x52, + 0x00, 0x93, 0xFC, 0x60, 0xFC, 0x2C, 0x09, 0xFA, 0x1B, 0x8A, 0x20, + 0x60, 0x11, 0xFD, 0xFF, 0x5C, 0xFB, 0x06, 0xFF, 0xFB, 0x00, 0x4D, + 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x23, 0x00, 0xED, 0x00, 0xD9, 0xFF, + 0xEF, 0xFB, 0x98, 0xFD, 0x99, 0x0C, 0x54, 0x1E, 0x02, 0x1F, 0xD2, + 0x0D, 0x20, 0xFE, 0xC0, 0xFB, 0xA7, 0xFF, 0xF4, 0x00, 0x2D, 0x00, + 0xF9, 0xFF, 0xF8, 0xFF, 0x41, 0x00, 0xFB, 0x00, 0x41, 0xFF, 0x78, + 0xFB, 0x4A, 0xFF, 0x25, 0x10, 0x16, 0x20, 0xDA, 0x1C, 0x56, 0x0A, + 0xBE, 0xFC, 0x56, 0xFC, 0x2C, 0x00, 0xDB, 0x00, 0x14, 0x00, 0xFD, + 0xFF, 0xF7, 0xFF, 0x66, 0x00, 0xF4, 0x00, 0x8F, 0xFE, 0x3F, 0xFB, + 0x75, 0x01, 0xAE, 0x13, 0x2C, 0x21, 0x2A, 0x1A, 0x0D, 0x07, 0xD4, + 0xFB, 0x0C, 0xFD, 0x8F, 0x00, 0xB7, 0x00, 0x03, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0xFA, 0xFF, 0x8E, 0x00, 0xD1, 0x00, 0xCF, 0xFD, 0x58, + 0xFB, 0x10, 0x04, 0x10, 0x17, 0x8A, 0x21, 0x10, 0x17, 0x10, 0x04, + 0x58, 0xFB, 0xCF, 0xFD, 0xD1, 0x00, 0x8E, 0x00, 0xFA, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0xB7, 0x00, 0x8F, 0x00, 0x0C, 0xFD, 0xD4, 0xFB, + 0x0D, 0x07, 0x2A, 0x1A, 0x2C, 0x21, 0xAE, 0x13, 0x75, 0x01, 0x3F, + 0xFB, 0x8F, 0xFE, 0xF4, 0x00, 0x66, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, + 0x14, 0x00, 0xDB, 0x00, 0x2C, 0x00, 0x56, 0xFC, 0xBE, 0xFC, 0x56, + 0x0A, 0xDA, 0x1C, 0x16, 0x20, 0x25, 0x10, 0x4A, 0xFF, 0x78, 0xFB, + 0x41, 0xFF, 0xFB, 0x00, 0x41, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2D, + 0x00, 0xF4, 0x00, 0xA7, 0xFF, 0xC0, 0xFB, 0x20, 0xFE, 0xD2, 0x0D, + 0x02, 0x1F, 0x54, 0x1E, 0x99, 0x0C, 0x98, 0xFD, 0xEF, 0xFB, 0xD9, + 0xFF, 0xED, 0x00, 0x23, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4D, 0x00, + 0xFB, 0x00, 0x06, 0xFF, 0x5C, 0xFB, 0xFD, 0xFF, 0x60, 0x11, 0x8A, + 0x20, 0xFA, 0x1B, 0x2C, 0x09, 0x60, 0xFC, 0x93, 0xFC, 0x52, 0x00, + 0xD0, 0x00, 0x0D, 0x00, 0xFE, 0xFF, 0xF7, 0xFF, 0x73, 0x00, 0xEB, + 0x00, 0x4E, 0xFE, 0x3E, 0xFB, 0x50, 0x02, 0xE0, 0x14, 0x62, 0x21, + 0x22, 0x19, 0xFA, 0x05, 0x9D, 0xFB, 0x4F, 0xFD, 0xAA, 0x00, 0xA9, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x9D, 0x00, + 0xBE, 0x00, 0x8B, 0xFD, 0x77, 0xFB, 0x0E, 0x05, 0x2D, 0x18, 0x7F, + 0x21, 0xEB, 0x15, 0x1D, 0x03, 0x45, 0xFB, 0x13, 0xFE, 0xE1, 0x00, + 0x80, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x08, 0x00, 0xC5, 0x00, 0x71, + 0x00, 0xCB, 0xFC, 0x18, 0xFC, 0x28, 0x08, 0x26, 0x1B, 0xE0, 0x20, + 0x77, 0x12, 0xA7, 0x00, 0x4A, 0xFB, 0xCF, 0xFE, 0xF9, 0x00, 0x58, + 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1C, 0x00, 0xE5, 0x00, 0x02, 0x00, + 0x1E, 0xFC, 0x2B, 0xFD, 0x87, 0x0B, 0xAA, 0x1D, 0x8D, 0x1F, 0xE9, + 0x0E, 0xA6, 0xFE, 0x9B, 0xFB, 0x78, 0xFF, 0xF8, 0x00, 0x36, 0x00, + 0xF9, 0xFF, 0xF8, 0xFF, 0x37, 0x00, 0xF9, 0x00, 0x72, 0xFF, 0x96, + 0xFB, 0xB8, 0xFE, 0x0D, 0x0F, 0x9D, 0x1F, 0x94, 0x1D, 0x65, 0x0B, + 0x1E, 0xFD, 0x24, 0xFC, 0x06, 0x00, 0xE4, 0x00, 0x1B, 0x00, 0xFC, + 0xFF, 0xF7, 0xFF, 0x5A, 0x00, 0xF9, 0x00, 0xC8, 0xFE, 0x49, 0xFB, + 0xBE, 0x00, 0x9A, 0x12, 0xE9, 0x20, 0x0A, 0x1B, 0x08, 0x08, 0x10, + 0xFC, 0xD2, 0xFC, 0x74, 0x00, 0xC3, 0x00, 0x08, 0x00, 0xFE, 0xFF, + 0xF9, 0xFF, 0x82, 0x00, 0xDF, 0x00, 0x0B, 0xFE, 0x47, 0xFB, 0x37, + 0x03, 0x0C, 0x16, 0x81, 0x21, 0x0E, 0x18, 0xF2, 0x04, 0x73, 0xFB, + 0x92, 0xFD, 0xC0, 0x00, 0x9B, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xAB, 0x00, 0xA7, 0x00, 0x47, 0xFD, 0xA2, 0xFB, + 0x18, 0x06, 0x40, 0x19, 0x5D, 0x21, 0xBE, 0x14, 0x37, 0x02, 0x3D, + 0xFB, 0x55, 0xFE, 0xEC, 0x00, 0x72, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, + 0x0E, 0x00, 0xD1, 0x00, 0x4E, 0x00, 0x8C, 0xFC, 0x6A, 0xFC, 0x4C, + 0x09, 0x13, 0x1C, 0x7E, 0x20, 0x3D, 0x11, 0xE8, 0xFF, 0x5F, 0xFB, + 0x0C, 0xFF, 0xFB, 0x00, 0x4C, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x24, + 0x00, 0xEE, 0x00, 0xD3, 0xFF, 0xEA, 0xFB, 0xA7, 0xFD, 0xBC, 0x0C, + 0x68, 0x1E, 0xEF, 0x1E, 0xAF, 0x0D, 0x11, 0xFE, 0xC5, 0xFB, 0xAD, + 0xFF, 0xF3, 0x00, 0x2C, 0x00, 0xFA, 0xFF, 0xF8, 0xFF, 0x42, 0x00, + 0xFB, 0x00, 0x3A, 0xFF, 0x74, 0xFB, 0x5D, 0xFF, 0x48, 0x10, 0x24, + 0x20, 0xC2, 0x1C, 0x35, 0x0A, 0xB3, 0xFC, 0x5D, 0xFC, 0x30, 0x00, + 0xDA, 0x00, 0x13, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x67, 0x00, 0xF3, + 0x00, 0x88, 0xFE, 0x3E, 0xFB, 0x8C, 0x01, 0xD0, 0x13, 0x33, 0x21, + 0x0D, 0x1A, 0xEE, 0x06, 0xCD, 0xFB, 0x13, 0xFD, 0x92, 0x00, 0xB6, + 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFA, 0xFF, 0x90, 0x00, + 0xCF, 0x00, 0xC7, 0xFD, 0x5B, 0xFB, 0x2B, 0x04, 0x31, 0x17, 0x8A, + 0x21, 0xF0, 0x16, 0xF4, 0x03, 0x56, 0xFB, 0xD6, 0xFD, 0xD3, 0x00, + 0x8D, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0xB9, 0x00, 0x8C, + 0x00, 0x05, 0xFD, 0xDB, 0xFB, 0x2C, 0x07, 0x47, 0x1A, 0x25, 0x21, + 0x8B, 0x13, 0x5D, 0x01, 0x40, 0xFB, 0x97, 0xFE, 0xF5, 0x00, 0x64, + 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x15, 0x00, 0xDC, 0x00, 0x27, 0x00, + 0x50, 0xFC, 0xCA, 0xFC, 0x78, 0x0A, 0xF2, 0x1C, 0x07, 0x20, 0x02, + 0x10, 0x37, 0xFF, 0x7B, 0xFB, 0x47, 0xFF, 0xFB, 0x00, 0x40, 0x00, + 0xF8, 0xFF, 0xF9, 0xFF, 0x2E, 0x00, 0xF5, 0x00, 0xA2, 0xFF, 0xBB, + 0xFB, 0x31, 0xFE, 0xF5, 0x0D, 0x14, 0x1F, 0x3F, 0x1E, 0x77, 0x0C, + 0x8A, 0xFD, 0xF5, 0xFB, 0xDE, 0xFF, 0xEC, 0x00, 0x22, 0x00, 0xFB, + 0xFF, 0xF7, 0xFF, 0x4E, 0x00, 0xFB, 0x00, 0xFF, 0xFE, 0x59, 0xFB, + 0x11, 0x00, 0x83, 0x11, 0x96, 0x20, 0xE0, 0x1B, 0x0B, 0x09, 0x56, + 0xFC, 0x99, 0xFC, 0x56, 0x00, 0xCE, 0x00, 0x0D, 0x00, 0xFE, 0xFF, + 0xF8, 0xFF, 0x75, 0x00, 0xEA, 0x00, 0x47, 0xFE, 0x3E, 0xFB, 0x69, + 0x02, 0x02, 0x15, 0x66, 0x21, 0x04, 0x19, 0xDC, 0x05, 0x98, 0xFB, + 0x56, 0xFD, 0xAD, 0x00, 0xA8, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x9E, 0x00, 0xBC, 0x00, 0x83, 0xFD, 0x7B, 0xFB, + 0x2B, 0x05, 0x4C, 0x18, 0x7C, 0x21, 0xCA, 0x15, 0x03, 0x03, 0x44, + 0xFB, 0x1A, 0xFE, 0xE2, 0x00, 0x7E, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, + 0x09, 0x00, 0xC6, 0x00, 0x6D, 0x00, 0xC3, 0xFC, 0x20, 0xFC, 0x49, + 0x08, 0x41, 0x1B, 0xD6, 0x20, 0x54, 0x12, 0x92, 0x00, 0x4C, 0xFB, + 0xD6, 0xFE, 0xFA, 0x00, 0x57, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, + 0x00, 0xE6, 0x00, 0xFD, 0xFF, 0x18, 0xFC, 0x38, 0xFD, 0xA9, 0x0B, + 0xC0, 0x1D, 0x7C, 0x1F, 0xC6, 0x0E, 0x95, 0xFE, 0x9F, 0xFB, 0x7E, + 0xFF, 0xF8, 0x00, 0x35, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x38, 0x00, + 0xF9, 0x00, 0x6C, 0xFF, 0x92, 0xFB, 0xC9, 0xFE, 0x2F, 0x0F, 0xAD, + 0x1F, 0x7D, 0x1D, 0x42, 0x0B, 0x12, 0xFD, 0x2A, 0xFC, 0x0B, 0x00, + 0xE3, 0x00, 0x1A, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5B, 0x00, 0xF8, + 0x00, 0xC1, 0xFE, 0x47, 0xFB, 0xD4, 0x00, 0xBC, 0x12, 0xF3, 0x20, + 0xEF, 0x1A, 0xE9, 0x07, 0x08, 0xFC, 0xD9, 0xFC, 0x78, 0x00, 0xC2, + 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0x00, 0xDD, 0x00, + 0x04, 0xFE, 0x49, 0xFB, 0x52, 0x03, 0x2D, 0x16, 0x83, 0x21, 0xEF, + 0x17, 0xD5, 0x04, 0x6F, 0xFB, 0x9A, 0xFD, 0xC3, 0x00, 0x9A, 0x00, + 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xAD, 0x00, 0xA4, + 0x00, 0x40, 0xFD, 0xA8, 0xFB, 0x36, 0x06, 0x5E, 0x19, 0x58, 0x21, + 0x9C, 0x14, 0x1E, 0x02, 0x3D, 0xFB, 0x5D, 0xFE, 0xED, 0x00, 0x70, + 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, 0x00, 0xD2, 0x00, 0x4A, 0x00, + 0x85, 0xFC, 0x74, 0xFC, 0x6D, 0x09, 0x2D, 0x1C, 0x72, 0x20, 0x1A, + 0x11, 0xD4, 0xFF, 0x61, 0xFB, 0x13, 0xFF, 0xFC, 0x00, 0x4A, 0x00, + 0xF7, 0xFF, 0xFA, 0xFF, 0x25, 0x00, 0xEF, 0x00, 0xCE, 0xFF, 0xE4, + 0xFB, 0xB5, 0xFD, 0xDE, 0x0C, 0x7C, 0x1E, 0xDD, 0x1E, 0x8C, 0x0D, + 0x01, 0xFE, 0xCA, 0xFB, 0xB3, 0xFF, 0xF3, 0x00, 0x2B, 0x00, 0xFA, + 0xFF, 0xF8, 0xFF, 0x44, 0x00, 0xFB, 0x00, 0x34, 0xFF, 0x71, 0xFB, + 0x71, 0xFF, 0x6B, 0x10, 0x32, 0x20, 0xA9, 0x1C, 0x13, 0x0A, 0xA8, + 0xFC, 0x63, 0xFC, 0x35, 0x00, 0xD9, 0x00, 0x12, 0x00, 0xFD, 0xFF, + 0xF7, 0xFF, 0x69, 0x00, 0xF2, 0x00, 0x81, 0xFE, 0x3E, 0xFB, 0xA4, + 0x01, 0xF2, 0x13, 0x3A, 0x21, 0xF0, 0x19, 0xCF, 0x06, 0xC7, 0xFB, + 0x1B, 0xFD, 0x96, 0x00, 0xB4, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFB, 0xFF, 0x92, 0x00, 0xCD, 0x00, 0xC0, 0xFD, 0x5E, 0xFB, + 0x47, 0x04, 0x51, 0x17, 0x8A, 0x21, 0xD0, 0x16, 0xD9, 0x03, 0x53, + 0xFB, 0xDE, 0xFD, 0xD5, 0x00, 0x8B, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, + 0x04, 0x00, 0xBA, 0x00, 0x89, 0x00, 0xFD, 0xFC, 0xE2, 0xFB, 0x4B, + 0x07, 0x63, 0x1A, 0x1D, 0x21, 0x69, 0x13, 0x46, 0x01, 0x41, 0xFB, + 0x9E, 0xFE, 0xF5, 0x00, 0x63, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x16, + 0x00, 0xDD, 0x00, 0x23, 0x00, 0x49, 0xFC, 0xD5, 0xFC, 0x99, 0x0A, + 0x09, 0x1D, 0xF9, 0x1F, 0xDF, 0x0F, 0x24, 0xFF, 0x7F, 0xFB, 0x4D, + 0xFF, 0xFB, 0x00, 0x3F, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2F, 0x00, + 0xF5, 0x00, 0x9C, 0xFF, 0xB6, 0xFB, 0x41, 0xFE, 0x17, 0x0E, 0x26, + 0x1F, 0x2B, 0x1E, 0x54, 0x0C, 0x7C, 0xFD, 0xFA, 0xFB, 0xE3, 0xFF, + 0xEB, 0x00, 0x21, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x50, 0x00, 0xFB, + 0x00, 0xF8, 0xFE, 0x57, 0xFB, 0x26, 0x00, 0xA6, 0x11, 0xA1, 0x20, + 0xC6, 0x1B, 0xEA, 0x08, 0x4D, 0xFC, 0xA0, 0xFC, 0x5A, 0x00, 0xCD, + 0x00, 0x0C, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0xE9, 0x00, + 0x3F, 0xFE, 0x3F, 0xFB, 0x82, 0x02, 0x23, 0x15, 0x6B, 0x21, 0xE5, + 0x18, 0xBE, 0x05, 0x93, 0xFB, 0x5E, 0xFD, 0xAF, 0x00, 0xA6, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xA0, 0x00, 0xB9, + 0x00, 0x7C, 0xFD, 0x80, 0xFB, 0x48, 0x05, 0x6B, 0x18, 0x79, 0x21, + 0xA9, 0x15, 0xE9, 0x02, 0x43, 0xFB, 0x21, 0xFE, 0xE3, 0x00, 0x7D, + 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x09, 0x00, 0xC7, 0x00, 0x69, 0x00, + 0xBC, 0xFC, 0x29, 0xFC, 0x69, 0x08, 0x5C, 0x1B, 0xCC, 0x20, 0x32, + 0x12, 0x7C, 0x00, 0x4E, 0xFB, 0xDD, 0xFE, 0xFA, 0x00, 0x56, 0x00, + 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, 0x00, 0xE7, 0x00, 0xF8, 0xFF, 0x12, + 0xFC, 0x45, 0xFD, 0xCB, 0x0B, 0xD6, 0x1D, 0x6C, 0x1F, 0xA3, 0x0E, + 0x84, 0xFE, 0xA4, 0xFB, 0x84, 0xFF, 0xF7, 0x00, 0x34, 0x00, 0xF9, + 0xFF, 0xF8, 0xFF, 0x3A, 0x00, 0xFA, 0x00, 0x66, 0xFF, 0x8E, 0xFB, + 0xDB, 0xFE, 0x53, 0x0F, 0xBD, 0x1F, 0x66, 0x1D, 0x21, 0x0B, 0x05, + 0xFD, 0x30, 0xFC, 0x10, 0x00, 0xE2, 0x00, 0x19, 0x00, 0xFC, 0xFF, + 0xF7, 0xFF, 0x5D, 0x00, 0xF8, 0x00, 0xBA, 0xFE, 0x46, 0xFB, 0xEA, + 0x00, 0xDF, 0x12, 0xFC, 0x20, 0xD3, 0x1A, 0xC9, 0x07, 0x00, 0xFC, + 0xE0, 0xFC, 0x7B, 0x00, 0xC0, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, + 0xFF, 0x85, 0x00, 0xDC, 0x00, 0xFC, 0xFD, 0x4A, 0xFB, 0x6C, 0x03, + 0x4E, 0x16, 0x85, 0x21, 0xCF, 0x17, 0xB8, 0x04, 0x6C, 0xFB, 0xA2, + 0xFD, 0xC5, 0x00, 0x98, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, + 0x01, 0x00, 0xAE, 0x00, 0xA1, 0x00, 0x38, 0xFD, 0xAE, 0xFB, 0x54, + 0x06, 0x7C, 0x19, 0x53, 0x21, 0x7B, 0x14, 0x05, 0x02, 0x3D, 0xFB, + 0x64, 0xFE, 0xEE, 0x00, 0x6F, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, + 0x00, 0xD4, 0x00, 0x46, 0x00, 0x7E, 0xFC, 0x7E, 0xFC, 0x8E, 0x09, + 0x46, 0x1C, 0x66, 0x20, 0xF7, 0x10, 0xC0, 0xFF, 0x64, 0xFB, 0x1A, + 0xFF, 0xFC, 0x00, 0x49, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x26, 0x00, + 0xF0, 0x00, 0xC9, 0xFF, 0xDF, 0xFB, 0xC4, 0xFD, 0x01, 0x0D, 0x90, + 0x1E, 0xCA, 0x1E, 0x69, 0x0D, 0xF1, 0xFD, 0xCF, 0xFB, 0xB8, 0xFF, + 0xF2, 0x00, 0x29, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x45, 0x00, 0xFC, + 0x00, 0x2D, 0xFF, 0x6D, 0xFB, 0x84, 0xFF, 0x8E, 0x10, 0x3F, 0x20, + 0x91, 0x1C, 0xF2, 0x09, 0x9D, 0xFC, 0x6A, 0xFC, 0x39, 0x00, 0xD7, + 0x00, 0x12, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6A, 0x00, 0xF1, 0x00, + 0x7A, 0xFE, 0x3D, 0xFB, 0xBC, 0x01, 0x14, 0x14, 0x41, 0x21, 0xD4, + 0x19, 0xB0, 0x06, 0xC0, 0xFB, 0x22, 0xFD, 0x99, 0x00, 0xB3, 0x00, + 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x93, 0x00, 0xCB, + 0x00, 0xB8, 0xFD, 0x61, 0xFB, 0x63, 0x04, 0x71, 0x17, 0x89, 0x21, + 0xB0, 0x16, 0xBD, 0x03, 0x51, 0xFB, 0xE6, 0xFD, 0xD7, 0x00, 0x8A, + 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0xBC, 0x00, 0x86, 0x00, + 0xF6, 0xFC, 0xE9, 0xFB, 0x6A, 0x07, 0x80, 0x1A, 0x15, 0x21, 0x47, + 0x13, 0x2F, 0x01, 0x42, 0xFB, 0xA5, 0xFE, 0xF6, 0x00, 0x61, 0x00, + 0xF7, 0xFF, 0xFC, 0xFF, 0x16, 0x00, 0xDF, 0x00, 0x1E, 0x00, 0x43, + 0xFC, 0xE1, 0xFC, 0xBB, 0x0A, 0x21, 0x1D, 0xEA, 0x1F, 0xBC, 0x0F, + 0x12, 0xFF, 0x82, 0xFB, 0x54, 0xFF, 0xFA, 0x00, 0x3D, 0x00, 0xF8, + 0xFF, 0xF9, 0xFF, 0x30, 0x00, 0xF6, 0x00, 0x96, 0xFF, 0xB1, 0xFB, + 0x51, 0xFE, 0x3A, 0x0E, 0x38, 0x1F, 0x16, 0x1E, 0x32, 0x0C, 0x6E, + 0xFD, 0x00, 0xFC, 0xE8, 0xFF, 0xEA, 0x00, 0x20, 0x00, 0xFB, 0xFF, + 0xF7, 0xFF, 0x51, 0x00, 0xFB, 0x00, 0xF1, 0xFE, 0x54, 0xFB, 0x3B, + 0x00, 0xC9, 0x11, 0xAD, 0x20, 0xAC, 0x1B, 0xCA, 0x08, 0x44, 0xFC, + 0xA7, 0xFC, 0x5E, 0x00, 0xCC, 0x00, 0x0B, 0x00, 0xFE, 0xFF, 0xF8, + 0xFF, 0x78, 0x00, 0xE7, 0x00, 0x38, 0xFE, 0x40, 0xFB, 0x9B, 0x02, + 0x45, 0x15, 0x6F, 0x21, 0xC7, 0x18, 0xA1, 0x05, 0x8E, 0xFB, 0x65, + 0xFD, 0xB2, 0x00, 0xA5, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xA2, 0x00, 0xB7, 0x00, 0x74, 0xFD, 0x84, 0xFB, 0x66, + 0x05, 0x8A, 0x18, 0x76, 0x21, 0x87, 0x15, 0xCF, 0x02, 0x41, 0xFB, + 0x29, 0xFE, 0xE5, 0x00, 0x7B, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0A, + 0x00, 0xC9, 0x00, 0x66, 0x00, 0xB5, 0xFC, 0x32, 0xFC, 0x89, 0x08, + 0x77, 0x1B, 0xC2, 0x20, 0x0F, 0x12, 0x66, 0x00, 0x50, 0xFB, 0xE4, + 0xFE, 0xFA, 0x00, 0x54, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1E, 0x00, + 0xE8, 0x00, 0xF3, 0xFF, 0x0C, 0xFC, 0x53, 0xFD, 0xED, 0x0B, 0xEB, + 0x1D, 0x5A, 0x1F, 0x80, 0x0E, 0x73, 0xFE, 0xA8, 0xFB, 0x8A, 0xFF, + 0xF7, 0x00, 0x32, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3B, 0x00, 0xFA, + 0x00, 0x60, 0xFF, 0x8A, 0xFB, 0xED, 0xFE, 0x76, 0x0F, 0xCC, 0x1F, + 0x4F, 0x1D, 0xFF, 0x0A, 0xF9, 0xFC, 0x36, 0xFC, 0x15, 0x00, 0xE1, + 0x00, 0x18, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5E, 0x00, 0xF7, 0x00, + 0xB3, 0xFE, 0x44, 0xFB, 0x01, 0x01, 0x02, 0x13, 0x04, 0x21, 0xB8, + 0x1A, 0xA9, 0x07, 0xF8, 0xFB, 0xE7, 0xFC, 0x7F, 0x00, 0xBF, 0x00, + 0x06, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x86, 0x00, 0xDA, 0x00, 0xF5, + 0xFD, 0x4C, 0xFB, 0x87, 0x03, 0x6E, 0x16, 0x86, 0x21, 0xB0, 0x17, + 0x9C, 0x04, 0x68, 0xFB, 0xA9, 0xFD, 0xC7, 0x00, 0x96, 0x00, 0xFB, + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xB0, 0x00, 0x9F, 0x00, + 0x31, 0xFD, 0xB4, 0xFB, 0x73, 0x06, 0x99, 0x19, 0x4D, 0x21, 0x59, + 0x14, 0xED, 0x01, 0x3D, 0xFB, 0x6B, 0xFE, 0xEF, 0x00, 0x6D, 0x00, + 0xF7, 0xFF, 0xFD, 0xFF, 0x10, 0x00, 0xD5, 0x00, 0x42, 0x00, 0x77, + 0xFC, 0x88, 0xFC, 0xAF, 0x09, 0x5F, 0x1C, 0x59, 0x20, 0xD4, 0x10, + 0xAC, 0xFF, 0x67, 0xFB, 0x20, 0xFF, 0xFC, 0x00, 0x48, 0x00, 0xF7, + 0xFF, 0xFA, 0xFF, 0x27, 0x00, 0xF0, 0x00, 0xC3, 0xFF, 0xD9, 0xFB, + 0xD3, 0xFD, 0x24, 0x0D, 0xA3, 0x1E, 0xB7, 0x1E, 0x46, 0x0D, 0xE2, + 0xFD, 0xD4, 0xFB, 0xBE, 0xFF, 0xF1, 0x00, 0x28, 0x00, 0xFA, 0xFF, + 0xF7, 0xFF, 0x46, 0x00, 0xFC, 0x00, 0x27, 0xFF, 0x6A, 0xFB, 0x98, + 0xFF, 0xB1, 0x10, 0x4C, 0x20, 0x78, 0x1C, 0xD1, 0x09, 0x93, 0xFC, + 0x71, 0xFC, 0x3D, 0x00, 0xD6, 0x00, 0x11, 0x00, 0xFD, 0xFF, 0xF7, + 0xFF, 0x6C, 0x00, 0xF0, 0x00, 0x72, 0xFE, 0x3D, 0xFB, 0xD4, 0x01, + 0x36, 0x14, 0x47, 0x21, 0xB6, 0x19, 0x91, 0x06, 0xBA, 0xFB, 0x29, + 0xFD, 0x9C, 0x00, 0xB1, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0xFB, 0xFF, 0x95, 0x00, 0xC9, 0x00, 0xB1, 0xFD, 0x65, 0xFB, 0x80, + 0x04, 0x90, 0x17, 0x88, 0x21, 0x8F, 0x16, 0xA2, 0x03, 0x4E, 0xFB, + 0xED, 0xFD, 0xD9, 0x00, 0x88, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x05, + 0x00, 0xBD, 0x00, 0x82, 0x00, 0xEF, 0xFC, 0xF0, 0xFB, 0x8A, 0x07, + 0x9C, 0x1A, 0x0D, 0x21, 0x24, 0x13, 0x18, 0x01, 0x43, 0xFB, 0xAC, + 0xFE, 0xF7, 0x00, 0x60, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x17, 0x00, + 0xE0, 0x00, 0x1A, 0x00, 0x3D, 0xFC, 0xED, 0xFC, 0xDD, 0x0A, 0x38, + 0x1D, 0xDB, 0x1F, 0x99, 0x0F, 0xFF, 0xFE, 0x86, 0xFB, 0x5A, 0xFF, + 0xFA, 0x00, 0x3C, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x31, 0x00, 0xF6, + 0x00, 0x90, 0xFF, 0xAD, 0xFB, 0x62, 0xFE, 0x5D, 0x0E, 0x49, 0x1F, + 0x01, 0x1E, 0x10, 0x0C, 0x60, 0xFD, 0x06, 0xFC, 0xEE, 0xFF, 0xE9, + 0x00, 0x1F, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x53, 0x00, 0xFB, 0x00, + 0xEB, 0xFE, 0x52, 0xFB, 0x51, 0x00, 0xEC, 0x11, 0xB7, 0x20, 0x91, + 0x1B, 0xA9, 0x08, 0x3B, 0xFC, 0xAE, 0xFC, 0x62, 0x00, 0xCA, 0x00, + 0x0B, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7A, 0x00, 0xE6, 0x00, 0x30, + 0xFE, 0x40, 0xFB, 0xB5, 0x02, 0x66, 0x15, 0x73, 0x21, 0xA9, 0x18, + 0x83, 0x05, 0x89, 0xFB, 0x6D, 0xFD, 0xB4, 0x00, 0xA3, 0x00, 0xFE, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xA3, 0x00, 0xB4, 0x00, + 0x6D, 0xFD, 0x89, 0xFB, 0x83, 0x05, 0xA9, 0x18, 0x73, 0x21, 0x66, + 0x15, 0xB5, 0x02, 0x40, 0xFB, 0x30, 0xFE, 0xE6, 0x00, 0x7A, 0x00, + 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, 0xCA, 0x00, 0x62, 0x00, 0xAE, + 0xFC, 0x3B, 0xFC, 0xA9, 0x08, 0x91, 0x1B, 0xB7, 0x20, 0xEC, 0x11, + 0x51, 0x00, 0x52, 0xFB, 0xEB, 0xFE, 0xFB, 0x00, 0x53, 0x00, 0xF7, + 0xFF, 0xFB, 0xFF, 0x1F, 0x00, 0xE9, 0x00, 0xEE, 0xFF, 0x06, 0xFC, + 0x60, 0xFD, 0x10, 0x0C, 0x01, 0x1E, 0x49, 0x1F, 0x5D, 0x0E, 0x62, + 0xFE, 0xAD, 0xFB, 0x90, 0xFF, 0xF6, 0x00, 0x31, 0x00, 0xF9, 0xFF, + 0xF8, 0xFF, 0x3C, 0x00, 0xFA, 0x00, 0x5A, 0xFF, 0x86, 0xFB, 0xFF, + 0xFE, 0x99, 0x0F, 0xDB, 0x1F, 0x38, 0x1D, 0xDD, 0x0A, 0xED, 0xFC, + 0x3D, 0xFC, 0x1A, 0x00, 0xE0, 0x00, 0x17, 0x00, 0xFC, 0xFF, 0xF7, + 0xFF, 0x60, 0x00, 0xF7, 0x00, 0xAC, 0xFE, 0x43, 0xFB, 0x18, 0x01, + 0x24, 0x13, 0x0D, 0x21, 0x9C, 0x1A, 0x8A, 0x07, 0xF0, 0xFB, 0xEF, + 0xFC, 0x82, 0x00, 0xBD, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, + 0x88, 0x00, 0xD9, 0x00, 0xED, 0xFD, 0x4E, 0xFB, 0xA2, 0x03, 0x8F, + 0x16, 0x88, 0x21, 0x90, 0x17, 0x80, 0x04, 0x65, 0xFB, 0xB1, 0xFD, + 0xC9, 0x00, 0x95, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, + 0x00, 0xB1, 0x00, 0x9C, 0x00, 0x29, 0xFD, 0xBA, 0xFB, 0x91, 0x06, + 0xB6, 0x19, 0x47, 0x21, 0x36, 0x14, 0xD4, 0x01, 0x3D, 0xFB, 0x72, + 0xFE, 0xF0, 0x00, 0x6C, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x11, 0x00, + 0xD6, 0x00, 0x3D, 0x00, 0x71, 0xFC, 0x93, 0xFC, 0xD1, 0x09, 0x78, + 0x1C, 0x4C, 0x20, 0xB1, 0x10, 0x98, 0xFF, 0x6A, 0xFB, 0x27, 0xFF, + 0xFC, 0x00, 0x46, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x28, 0x00, 0xF1, + 0x00, 0xBE, 0xFF, 0xD4, 0xFB, 0xE2, 0xFD, 0x46, 0x0D, 0xB7, 0x1E, + 0xA3, 0x1E, 0x24, 0x0D, 0xD3, 0xFD, 0xD9, 0xFB, 0xC3, 0xFF, 0xF0, + 0x00, 0x27, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x48, 0x00, 0xFC, 0x00, + 0x20, 0xFF, 0x67, 0xFB, 0xAC, 0xFF, 0xD4, 0x10, 0x59, 0x20, 0x5F, + 0x1C, 0xAF, 0x09, 0x88, 0xFC, 0x77, 0xFC, 0x42, 0x00, 0xD5, 0x00, + 0x10, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6D, 0x00, 0xEF, 0x00, 0x6B, + 0xFE, 0x3D, 0xFB, 0xED, 0x01, 0x59, 0x14, 0x4D, 0x21, 0x99, 0x19, + 0x73, 0x06, 0xB4, 0xFB, 0x31, 0xFD, 0x9F, 0x00, 0xB0, 0x00, 0x01, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x96, 0x00, 0xC7, 0x00, + 0xA9, 0xFD, 0x68, 0xFB, 0x9C, 0x04, 0xB0, 0x17, 0x86, 0x21, 0x6E, + 0x16, 0x87, 0x03, 0x4C, 0xFB, 0xF5, 0xFD, 0xDA, 0x00, 0x86, 0x00, + 0xF9, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0xBF, 0x00, 0x7F, 0x00, 0xE7, + 0xFC, 0xF8, 0xFB, 0xA9, 0x07, 0xB8, 0x1A, 0x04, 0x21, 0x02, 0x13, + 0x01, 0x01, 0x44, 0xFB, 0xB3, 0xFE, 0xF7, 0x00, 0x5E, 0x00, 0xF7, + 0xFF, 0xFC, 0xFF, 0x18, 0x00, 0xE1, 0x00, 0x15, 0x00, 0x36, 0xFC, + 0xF9, 0xFC, 0xFF, 0x0A, 0x4F, 0x1D, 0xCC, 0x1F, 0x76, 0x0F, 0xED, + 0xFE, 0x8A, 0xFB, 0x60, 0xFF, 0xFA, 0x00, 0x3B, 0x00, 0xF8, 0xFF, + 0xF9, 0xFF, 0x32, 0x00, 0xF7, 0x00, 0x8A, 0xFF, 0xA8, 0xFB, 0x73, + 0xFE, 0x80, 0x0E, 0x5A, 0x1F, 0xEB, 0x1D, 0xED, 0x0B, 0x53, 0xFD, + 0x0C, 0xFC, 0xF3, 0xFF, 0xE8, 0x00, 0x1E, 0x00, 0xFB, 0xFF, 0xF7, + 0xFF, 0x54, 0x00, 0xFA, 0x00, 0xE4, 0xFE, 0x50, 0xFB, 0x66, 0x00, + 0x0F, 0x12, 0xC2, 0x20, 0x77, 0x1B, 0x89, 0x08, 0x32, 0xFC, 0xB5, + 0xFC, 0x66, 0x00, 0xC9, 0x00, 0x0A, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, + 0x7B, 0x00, 0xE5, 0x00, 0x29, 0xFE, 0x41, 0xFB, 0xCF, 0x02, 0x87, + 0x15, 0x76, 0x21, 0x8A, 0x18, 0x66, 0x05, 0x84, 0xFB, 0x74, 0xFD, + 0xB7, 0x00, 0xA2, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, + 0xFF, 0xA5, 0x00, 0xB2, 0x00, 0x65, 0xFD, 0x8E, 0xFB, 0xA1, 0x05, + 0xC7, 0x18, 0x6F, 0x21, 0x45, 0x15, 0x9B, 0x02, 0x40, 0xFB, 0x38, + 0xFE, 0xE7, 0x00, 0x78, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, + 0xCC, 0x00, 0x5E, 0x00, 0xA7, 0xFC, 0x44, 0xFC, 0xCA, 0x08, 0xAC, + 0x1B, 0xAD, 0x20, 0xC9, 0x11, 0x3B, 0x00, 0x54, 0xFB, 0xF1, 0xFE, + 0xFB, 0x00, 0x51, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x20, 0x00, 0xEA, + 0x00, 0xE8, 0xFF, 0x00, 0xFC, 0x6E, 0xFD, 0x32, 0x0C, 0x16, 0x1E, + 0x38, 0x1F, 0x3A, 0x0E, 0x51, 0xFE, 0xB1, 0xFB, 0x96, 0xFF, 0xF6, + 0x00, 0x30, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3D, 0x00, 0xFA, 0x00, + 0x54, 0xFF, 0x82, 0xFB, 0x12, 0xFF, 0xBC, 0x0F, 0xEA, 0x1F, 0x21, + 0x1D, 0xBB, 0x0A, 0xE1, 0xFC, 0x43, 0xFC, 0x1E, 0x00, 0xDF, 0x00, + 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x61, 0x00, 0xF6, 0x00, 0xA5, + 0xFE, 0x42, 0xFB, 0x2F, 0x01, 0x47, 0x13, 0x15, 0x21, 0x80, 0x1A, + 0x6A, 0x07, 0xE9, 0xFB, 0xF6, 0xFC, 0x86, 0x00, 0xBC, 0x00, 0x05, + 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8A, 0x00, 0xD7, 0x00, 0xE6, 0xFD, + 0x51, 0xFB, 0xBD, 0x03, 0xB0, 0x16, 0x89, 0x21, 0x71, 0x17, 0x63, + 0x04, 0x61, 0xFB, 0xB8, 0xFD, 0xCB, 0x00, 0x93, 0x00, 0xFB, 0xFF, + 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xB3, 0x00, 0x99, 0x00, 0x22, + 0xFD, 0xC0, 0xFB, 0xB0, 0x06, 0xD4, 0x19, 0x41, 0x21, 0x14, 0x14, + 0xBC, 0x01, 0x3D, 0xFB, 0x7A, 0xFE, 0xF1, 0x00, 0x6A, 0x00, 0xF7, + 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD7, 0x00, 0x39, 0x00, 0x6A, 0xFC, + 0x9D, 0xFC, 0xF2, 0x09, 0x91, 0x1C, 0x3F, 0x20, 0x8E, 0x10, 0x84, + 0xFF, 0x6D, 0xFB, 0x2D, 0xFF, 0xFC, 0x00, 0x45, 0x00, 0xF7, 0xFF, + 0xFA, 0xFF, 0x29, 0x00, 0xF2, 0x00, 0xB8, 0xFF, 0xCF, 0xFB, 0xF1, + 0xFD, 0x69, 0x0D, 0xCA, 0x1E, 0x90, 0x1E, 0x01, 0x0D, 0xC4, 0xFD, + 0xDF, 0xFB, 0xC9, 0xFF, 0xF0, 0x00, 0x26, 0x00, 0xFA, 0xFF, 0xF7, + 0xFF, 0x49, 0x00, 0xFC, 0x00, 0x1A, 0xFF, 0x64, 0xFB, 0xC0, 0xFF, + 0xF7, 0x10, 0x66, 0x20, 0x46, 0x1C, 0x8E, 0x09, 0x7E, 0xFC, 0x7E, + 0xFC, 0x46, 0x00, 0xD4, 0x00, 0x0F, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, + 0x6F, 0x00, 0xEE, 0x00, 0x64, 0xFE, 0x3D, 0xFB, 0x05, 0x02, 0x7B, + 0x14, 0x53, 0x21, 0x7C, 0x19, 0x54, 0x06, 0xAE, 0xFB, 0x38, 0xFD, + 0xA1, 0x00, 0xAE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC, + 0xFF, 0x98, 0x00, 0xC5, 0x00, 0xA2, 0xFD, 0x6C, 0xFB, 0xB8, 0x04, + 0xCF, 0x17, 0x85, 0x21, 0x4E, 0x16, 0x6C, 0x03, 0x4A, 0xFB, 0xFC, + 0xFD, 0xDC, 0x00, 0x85, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x07, 0x00, + 0xC0, 0x00, 0x7B, 0x00, 0xE0, 0xFC, 0x00, 0xFC, 0xC9, 0x07, 0xD3, + 0x1A, 0xFC, 0x20, 0xDF, 0x12, 0xEA, 0x00, 0x46, 0xFB, 0xBA, 0xFE, + 0xF8, 0x00, 0x5D, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x19, 0x00, 0xE2, + 0x00, 0x10, 0x00, 0x30, 0xFC, 0x05, 0xFD, 0x21, 0x0B, 0x66, 0x1D, + 0xBD, 0x1F, 0x53, 0x0F, 0xDB, 0xFE, 0x8E, 0xFB, 0x66, 0xFF, 0xFA, + 0x00, 0x3A, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x34, 0x00, 0xF7, 0x00, + 0x84, 0xFF, 0xA4, 0xFB, 0x84, 0xFE, 0xA3, 0x0E, 0x6C, 0x1F, 0xD6, + 0x1D, 0xCB, 0x0B, 0x45, 0xFD, 0x12, 0xFC, 0xF8, 0xFF, 0xE7, 0x00, + 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x56, 0x00, 0xFA, 0x00, 0xDD, + 0xFE, 0x4E, 0xFB, 0x7C, 0x00, 0x32, 0x12, 0xCC, 0x20, 0x5C, 0x1B, + 0x69, 0x08, 0x29, 0xFC, 0xBC, 0xFC, 0x69, 0x00, 0xC7, 0x00, 0x09, + 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7D, 0x00, 0xE3, 0x00, 0x21, 0xFE, + 0x43, 0xFB, 0xE9, 0x02, 0xA9, 0x15, 0x79, 0x21, 0x6B, 0x18, 0x48, + 0x05, 0x80, 0xFB, 0x7C, 0xFD, 0xB9, 0x00, 0xA0, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA6, 0x00, 0xAF, 0x00, 0x5E, + 0xFD, 0x93, 0xFB, 0xBE, 0x05, 0xE5, 0x18, 0x6B, 0x21, 0x23, 0x15, + 0x82, 0x02, 0x3F, 0xFB, 0x3F, 0xFE, 0xE9, 0x00, 0x77, 0x00, 0xF8, + 0xFF, 0xFE, 0xFF, 0x0C, 0x00, 0xCD, 0x00, 0x5A, 0x00, 0xA0, 0xFC, + 0x4D, 0xFC, 0xEA, 0x08, 0xC6, 0x1B, 0xA1, 0x20, 0xA6, 0x11, 0x26, + 0x00, 0x57, 0xFB, 0xF8, 0xFE, 0xFB, 0x00, 0x50, 0x00, 0xF7, 0xFF, + 0xFB, 0xFF, 0x21, 0x00, 0xEB, 0x00, 0xE3, 0xFF, 0xFA, 0xFB, 0x7C, + 0xFD, 0x54, 0x0C, 0x2B, 0x1E, 0x26, 0x1F, 0x17, 0x0E, 0x41, 0xFE, + 0xB6, 0xFB, 0x9C, 0xFF, 0xF5, 0x00, 0x2F, 0x00, 0xF9, 0xFF, 0xF8, + 0xFF, 0x3F, 0x00, 0xFB, 0x00, 0x4D, 0xFF, 0x7F, 0xFB, 0x24, 0xFF, + 0xDF, 0x0F, 0xF9, 0x1F, 0x09, 0x1D, 0x99, 0x0A, 0xD5, 0xFC, 0x49, + 0xFC, 0x23, 0x00, 0xDD, 0x00, 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, + 0x63, 0x00, 0xF5, 0x00, 0x9E, 0xFE, 0x41, 0xFB, 0x46, 0x01, 0x69, + 0x13, 0x1D, 0x21, 0x63, 0x1A, 0x4B, 0x07, 0xE2, 0xFB, 0xFD, 0xFC, + 0x89, 0x00, 0xBA, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8B, + 0x00, 0xD5, 0x00, 0xDE, 0xFD, 0x53, 0xFB, 0xD9, 0x03, 0xD0, 0x16, + 0x8A, 0x21, 0x51, 0x17, 0x47, 0x04, 0x5E, 0xFB, 0xC0, 0xFD, 0xCD, + 0x00, 0x92, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, + 0xB4, 0x00, 0x96, 0x00, 0x1B, 0xFD, 0xC7, 0xFB, 0xCF, 0x06, 0xF0, + 0x19, 0x3A, 0x21, 0xF2, 0x13, 0xA4, 0x01, 0x3E, 0xFB, 0x81, 0xFE, + 0xF2, 0x00, 0x69, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD9, + 0x00, 0x35, 0x00, 0x63, 0xFC, 0xA8, 0xFC, 0x13, 0x0A, 0xA9, 0x1C, + 0x32, 0x20, 0x6B, 0x10, 0x71, 0xFF, 0x71, 0xFB, 0x34, 0xFF, 0xFB, + 0x00, 0x44, 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2B, 0x00, 0xF3, 0x00, + 0xB3, 0xFF, 0xCA, 0xFB, 0x01, 0xFE, 0x8C, 0x0D, 0xDD, 0x1E, 0x7C, + 0x1E, 0xDE, 0x0C, 0xB5, 0xFD, 0xE4, 0xFB, 0xCE, 0xFF, 0xEF, 0x00, + 0x25, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4A, 0x00, 0xFC, 0x00, 0x13, + 0xFF, 0x61, 0xFB, 0xD4, 0xFF, 0x1A, 0x11, 0x72, 0x20, 0x2D, 0x1C, + 0x6D, 0x09, 0x74, 0xFC, 0x85, 0xFC, 0x4A, 0x00, 0xD2, 0x00, 0x0F, + 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x70, 0x00, 0xED, 0x00, 0x5D, 0xFE, + 0x3D, 0xFB, 0x1E, 0x02, 0x9C, 0x14, 0x58, 0x21, 0x5E, 0x19, 0x36, + 0x06, 0xA8, 0xFB, 0x40, 0xFD, 0xA4, 0x00, 0xAD, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x9A, 0x00, 0xC3, 0x00, 0x9A, + 0xFD, 0x6F, 0xFB, 0xD5, 0x04, 0xEF, 0x17, 0x83, 0x21, 0x2D, 0x16, + 0x52, 0x03, 0x49, 0xFB, 0x04, 0xFE, 0xDD, 0x00, 0x83, 0x00, 0xF9, + 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xC2, 0x00, 0x78, 0x00, 0xD9, 0xFC, + 0x08, 0xFC, 0xE9, 0x07, 0xEF, 0x1A, 0xF3, 0x20, 0xBC, 0x12, 0xD4, + 0x00, 0x47, 0xFB, 0xC1, 0xFE, 0xF8, 0x00, 0x5B, 0x00, 0xF7, 0xFF, + 0xFC, 0xFF, 0x1A, 0x00, 0xE3, 0x00, 0x0B, 0x00, 0x2A, 0xFC, 0x12, + 0xFD, 0x42, 0x0B, 0x7D, 0x1D, 0xAD, 0x1F, 0x2F, 0x0F, 0xC9, 0xFE, + 0x92, 0xFB, 0x6C, 0xFF, 0xF9, 0x00, 0x38, 0x00, 0xF8, 0xFF, 0xF9, + 0xFF, 0x35, 0x00, 0xF8, 0x00, 0x7E, 0xFF, 0x9F, 0xFB, 0x95, 0xFE, + 0xC6, 0x0E, 0x7C, 0x1F, 0xC0, 0x1D, 0xA9, 0x0B, 0x38, 0xFD, 0x18, + 0xFC, 0xFD, 0xFF, 0xE6, 0x00, 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, + 0x57, 0x00, 0xFA, 0x00, 0xD6, 0xFE, 0x4C, 0xFB, 0x92, 0x00, 0x54, + 0x12, 0xD6, 0x20, 0x41, 0x1B, 0x49, 0x08, 0x20, 0xFC, 0xC3, 0xFC, + 0x6D, 0x00, 0xC6, 0x00, 0x09, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7E, + 0x00, 0xE2, 0x00, 0x1A, 0xFE, 0x44, 0xFB, 0x03, 0x03, 0xCA, 0x15, + 0x7C, 0x21, 0x4C, 0x18, 0x2B, 0x05, 0x7B, 0xFB, 0x83, 0xFD, 0xBC, + 0x00, 0x9E, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xA8, 0x00, 0xAD, 0x00, 0x56, 0xFD, 0x98, 0xFB, 0xDC, 0x05, 0x04, + 0x19, 0x66, 0x21, 0x02, 0x15, 0x69, 0x02, 0x3E, 0xFB, 0x47, 0xFE, + 0xEA, 0x00, 0x75, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xCE, + 0x00, 0x56, 0x00, 0x99, 0xFC, 0x56, 0xFC, 0x0B, 0x09, 0xE0, 0x1B, + 0x96, 0x20, 0x83, 0x11, 0x11, 0x00, 0x59, 0xFB, 0xFF, 0xFE, 0xFB, + 0x00, 0x4E, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x22, 0x00, 0xEC, 0x00, + 0xDE, 0xFF, 0xF5, 0xFB, 0x8A, 0xFD, 0x77, 0x0C, 0x3F, 0x1E, 0x14, + 0x1F, 0xF5, 0x0D, 0x31, 0xFE, 0xBB, 0xFB, 0xA2, 0xFF, 0xF5, 0x00, + 0x2E, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x40, 0x00, 0xFB, 0x00, 0x47, + 0xFF, 0x7B, 0xFB, 0x37, 0xFF, 0x02, 0x10, 0x07, 0x20, 0xF2, 0x1C, + 0x78, 0x0A, 0xCA, 0xFC, 0x50, 0xFC, 0x27, 0x00, 0xDC, 0x00, 0x15, + 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x64, 0x00, 0xF5, 0x00, 0x97, 0xFE, + 0x40, 0xFB, 0x5D, 0x01, 0x8B, 0x13, 0x25, 0x21, 0x47, 0x1A, 0x2C, + 0x07, 0xDB, 0xFB, 0x05, 0xFD, 0x8C, 0x00, 0xB9, 0x00, 0x04, 0x00, + 0xFF, 0xFF, 0xFA, 0xFF, 0x8D, 0x00, 0xD3, 0x00, 0xD6, 0xFD, 0x56, + 0xFB, 0xF4, 0x03, 0xF0, 0x16, 0x8A, 0x21, 0x31, 0x17, 0x2B, 0x04, + 0x5B, 0xFB, 0xC7, 0xFD, 0xCF, 0x00, 0x90, 0x00, 0xFA, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xB6, 0x00, 0x92, 0x00, 0x13, 0xFD, + 0xCD, 0xFB, 0xEE, 0x06, 0x0D, 0x1A, 0x33, 0x21, 0xD0, 0x13, 0x8C, + 0x01, 0x3E, 0xFB, 0x88, 0xFE, 0xF3, 0x00, 0x67, 0x00, 0xF7, 0xFF, + 0x06, 0x00, 0x1D, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0xA1, 0x02, 0xA6, + 0xF8, 0x56, 0x02, 0xA5, 0x28, 0xA5, 0x28, 0x56, 0x02, 0xA6, 0xF8, + 0xA1, 0x02, 0xFE, 0x00, 0x03, 0xFF, 0x1D, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x21, 0x00, 0xA6, 0xFF, 0x3F, 0xFF, 0x0B, 0x03, 0x42, 0xFE, + 0x3E, 0xF8, 0x7F, 0x15, 0xAC, 0x30, 0x7F, 0x15, 0x3E, 0xF8, 0x42, + 0xFE, 0x0B, 0x03, 0x3F, 0xFF, 0xA6, 0xFF, 0x21, 0x00, 0x00, 0x00, + 0xFA, 0xFF, 0xCE, 0xFF, 0x14, 0x01, 0x00, 0xFD, 0x35, 0x06, 0xD5, + 0xF4, 0xDA, 0x15, 0x92, 0x40, 0xAE, 0xFE, 0xF3, 0xFC, 0x68, 0x03, + 0x86, 0xFD, 0x51, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEC, + 0xFF, 0xF9, 0xFF, 0xC6, 0x00, 0x55, 0xFD, 0x35, 0x06, 0x90, 0xF3, + 0xE5, 0x1C, 0x6B, 0x3D, 0x71, 0xFA, 0x34, 0xFF, 0x46, 0x02, 0xFF, + 0xFD, 0x2D, 0x01, 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDB, 0xFF, + 0x2D, 0x00, 0x60, 0x00, 0xE1, 0xFD, 0xCE, 0x05, 0xED, 0xF2, 0xF3, + 0x23, 0x20, 0x39, 0x22, 0xF7, 0x44, 0x01, 0x1F, 0x01, 0x89, 0xFE, + 0xFB, 0x00, 0x9C, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC9, 0xFF, 0x68, + 0x00, 0xE5, 0xFF, 0xA0, 0xFE, 0xFB, 0x04, 0x0C, 0xF3, 0xC5, 0x2A, + 0xD8, 0x33, 0xC9, 0xF4, 0x0B, 0x03, 0x05, 0x00, 0x1A, 0xFF, 0xC1, + 0x00, 0xAD, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB5, 0xFF, 0xA5, 0x00, + 0x5C, 0xFF, 0x8C, 0xFF, 0xBF, 0x03, 0x06, 0xF4, 0x22, 0x31, 0xC8, + 0x2D, 0x63, 0xF3, 0x76, 0x04, 0x08, 0xFF, 0xA7, 0xFF, 0x84, 0x00, + 0xC0, 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA4, 0xFF, 0xE1, 0x00, 0xCB, + 0xFE, 0x9B, 0x00, 0x21, 0x02, 0xEE, 0xF5, 0xCD, 0x36, 0x24, 0x27, + 0xE1, 0xF2, 0x7A, 0x05, 0x33, 0xFE, 0x2A, 0x00, 0x47, 0x00, 0xD3, + 0xFF, 0x04, 0x00, 0x0F, 0x00, 0x95, 0xFF, 0x17, 0x01, 0x3D, 0xFE, + 0xBD, 0x01, 0x30, 0x00, 0xCC, 0xF8, 0x92, 0x3B, 0x2A, 0x20, 0x2E, + 0xF3, 0x12, 0x06, 0x8F, 0xFD, 0x9A, 0x00, 0x10, 0x00, 0xE5, 0xFF, + 0x02, 0x00, 0x10, 0x00, 0x8C, 0xFF, 0x42, 0x01, 0xBB, 0xFD, 0xE4, + 0x02, 0x01, 0xFE, 0x9C, 0xFC, 0x45, 0x3F, 0x16, 0x19, 0x2D, 0xF4, + 0x41, 0x06, 0x21, 0xFD, 0xF3, 0x00, 0xE0, 0xFF, 0xF4, 0xFF, 0x01, + 0x00, 0x10, 0x00, 0x8B, 0xFF, 0x5D, 0x01, 0x4F, 0xFD, 0xFB, 0x03, + 0xB2, 0xFB, 0x53, 0x01, 0xC2, 0x41, 0x24, 0x12, 0xBA, 0xF5, 0x0F, + 0x06, 0xE9, 0xFC, 0x33, 0x01, 0xBB, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x93, 0xFF, 0x63, 0x01, 0x04, 0xFD, 0xEF, 0x04, 0x62, + 0xF9, 0xD7, 0x06, 0xF2, 0x42, 0x8D, 0x0B, 0xB0, 0xF7, 0x87, 0x05, + 0xE6, 0xFC, 0x58, 0x01, 0xA0, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0xA5, 0xFF, 0x52, 0x01, 0xE2, 0xFC, 0xAD, 0x05, + 0x35, 0xF7, 0x08, 0x0D, 0xCB, 0x42, 0x81, 0x05, 0xE8, 0xF9, 0xBB, + 0x04, 0x12, 0xFD, 0x64, 0x01, 0x90, 0xFF, 0x0E, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xC2, 0xFF, 0x27, 0x01, 0xF1, 0xFC, 0x22, 0x06, 0x54, + 0xF5, 0xB8, 0x13, 0x4A, 0x41, 0x29, 0x00, 0x3C, 0xFC, 0xBD, 0x03, + 0x66, 0xFD, 0x58, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF1, + 0xFF, 0xEB, 0xFF, 0xE1, 0x00, 0x35, 0xFD, 0x40, 0x06, 0xE4, 0xF3, + 0xB7, 0x1A, 0x85, 0x3E, 0xA6, 0xFB, 0x86, 0xFE, 0xA0, 0x02, 0xD7, + 0xFD, 0x39, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xE1, 0xFF, + 0x1C, 0x00, 0x82, 0x00, 0xB0, 0xFD, 0xF9, 0x05, 0x0C, 0xF3, 0xCB, + 0x21, 0x8F, 0x3A, 0x0D, 0xF8, 0xA9, 0x00, 0x79, 0x01, 0x5D, 0xFE, + 0x0B, 0x01, 0x98, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCE, 0xFF, 0x55, + 0x00, 0x0D, 0x00, 0x60, 0xFE, 0x48, 0x05, 0xEC, 0xF2, 0xB6, 0x28, + 0x91, 0x35, 0x68, 0xF5, 0x88, 0x02, 0x5A, 0x00, 0xED, 0xFE, 0xD4, + 0x00, 0xA8, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0x92, 0x00, + 0x87, 0xFF, 0x3F, 0xFF, 0x2B, 0x04, 0xA1, 0xF3, 0x3D, 0x2F, 0xB8, + 0x2F, 0xB8, 0xF3, 0x11, 0x04, 0x52, 0xFF, 0x7C, 0xFF, 0x97, 0x00, + 0xBA, 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA9, 0xFF, 0xCF, 0x00, 0xF8, + 0xFE, 0x44, 0x00, 0xAA, 0x02, 0x3E, 0xF5, 0x24, 0x35, 0x3B, 0x29, + 0xF2, 0xF2, 0x35, 0x05, 0x70, 0xFE, 0x03, 0x00, 0x5A, 0x00, 0xCD, + 0xFF, 0x05, 0x00, 0x0E, 0x00, 0x99, 0xFF, 0x07, 0x01, 0x68, 0xFE, + 0x63, 0x01, 0xD0, 0x00, 0xD0, 0xF7, 0x35, 0x3A, 0x55, 0x22, 0x02, + 0xF3, 0xEF, 0x05, 0xBC, 0xFD, 0x7A, 0x00, 0x20, 0x00, 0xDF, 0xFF, + 0x03, 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x36, 0x01, 0xE1, 0xFD, 0x8A, + 0x02, 0xB2, 0xFE, 0x56, 0xFB, 0x40, 0x3E, 0x42, 0x1B, 0xCE, 0xF3, + 0x3E, 0x06, 0x3D, 0xFD, 0xDB, 0x00, 0xEE, 0xFF, 0xF0, 0xFF, 0x01, + 0x00, 0x11, 0x00, 0x8A, 0xFF, 0x57, 0x01, 0x6D, 0xFD, 0xA8, 0x03, + 0x69, 0xFC, 0xC8, 0xFF, 0x20, 0x41, 0x40, 0x14, 0x33, 0xF5, 0x28, + 0x06, 0xF5, 0xFC, 0x22, 0x01, 0xC5, 0xFF, 0xFD, 0xFF, 0x00, 0x00, + 0x0F, 0x00, 0x8F, 0xFF, 0x64, 0x01, 0x17, 0xFD, 0xA9, 0x04, 0x16, + 0xFA, 0x10, 0x05, 0xB8, 0x42, 0x87, 0x0D, 0x0D, 0xF7, 0xB9, 0x05, + 0xE2, 0xFC, 0x50, 0x01, 0xA7, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0A, 0x00, 0x9E, 0xFF, 0x5A, 0x01, 0xE8, 0xFC, 0x7A, 0x05, + 0xDA, 0xF7, 0x10, 0x0B, 0xFB, 0x42, 0x4B, 0x07, 0x35, 0xF9, 0x00, + 0x05, 0x00, 0xFD, 0x63, 0x01, 0x94, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xB8, 0xFF, 0x37, 0x01, 0xE7, 0xFC, 0x07, 0x06, 0xDE, + 0xF5, 0x9F, 0x11, 0xE4, 0x41, 0xB8, 0x01, 0x84, 0xFB, 0x0F, 0x04, + 0x48, 0xFD, 0x5E, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF5, + 0xFF, 0xDD, 0xFF, 0xF9, 0x00, 0x1B, 0xFD, 0x41, 0x06, 0x47, 0xF4, + 0x8B, 0x18, 0x81, 0x3F, 0xF1, 0xFC, 0xD5, 0xFD, 0xFA, 0x02, 0xB2, + 0xFD, 0x45, 0x01, 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE6, 0xFF, + 0x0C, 0x00, 0xA2, 0x00, 0x85, 0xFD, 0x1A, 0x06, 0x3C, 0xF3, 0x9F, + 0x1F, 0xE6, 0x3B, 0x0E, 0xF9, 0x07, 0x00, 0xD4, 0x01, 0x33, 0xFE, + 0x1B, 0x01, 0x94, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD4, 0xFF, 0x43, + 0x00, 0x33, 0x00, 0x25, 0xFE, 0x89, 0x05, 0xE0, 0xF2, 0x9C, 0x26, + 0x33, 0x37, 0x1E, 0xF6, 0xFD, 0x01, 0xB0, 0x00, 0xC0, 0xFE, 0xE6, + 0x00, 0xA2, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC1, 0xFF, 0x7F, 0x00, + 0xB2, 0xFF, 0xF6, 0xFE, 0x8E, 0x04, 0x51, 0xF3, 0x49, 0x2D, 0x98, + 0x31, 0x23, 0xF4, 0xA2, 0x03, 0xA0, 0xFF, 0x51, 0xFF, 0xAA, 0x00, + 0xB4, 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAE, 0xFF, 0xBD, 0x00, 0x25, + 0xFF, 0xF1, 0xFF, 0x2B, 0x03, 0xA5, 0xF4, 0x68, 0x33, 0x48, 0x2B, + 0x17, 0xF3, 0xE7, 0x04, 0xB1, 0xFE, 0xDB, 0xFF, 0x6C, 0x00, 0xC7, + 0xFF, 0x06, 0x00, 0x0D, 0x00, 0x9E, 0xFF, 0xF7, 0x00, 0x94, 0xFE, + 0x09, 0x01, 0x6A, 0x01, 0xEB, 0xF6, 0xC1, 0x38, 0x7D, 0x24, 0xE8, + 0xF2, 0xC1, 0x05, 0xEE, 0xFD, 0x57, 0x00, 0x31, 0x00, 0xDA, 0xFF, + 0x03, 0x00, 0x10, 0x00, 0x91, 0xFF, 0x29, 0x01, 0x09, 0xFE, 0x2F, + 0x02, 0x5F, 0xFF, 0x27, 0xFA, 0x20, 0x3D, 0x70, 0x1D, 0x7D, 0xF3, + 0x31, 0x06, 0x5E, 0xFD, 0xBF, 0x00, 0xFD, 0xFF, 0xEB, 0xFF, 0x02, + 0x00, 0x11, 0x00, 0x8B, 0xFF, 0x4E, 0x01, 0x8E, 0xFD, 0x52, 0x03, + 0x20, 0xFD, 0x52, 0xFE, 0x60, 0x40, 0x63, 0x16, 0xB7, 0xF4, 0x39, + 0x06, 0x05, 0xFD, 0x0F, 0x01, 0xD1, 0xFF, 0xF9, 0xFF, 0x00, 0x00, + 0x10, 0x00, 0x8D, 0xFF, 0x62, 0x01, 0x2E, 0xFD, 0x5E, 0x04, 0xCC, + 0xFA, 0x5B, 0x03, 0x5E, 0x42, 0x8E, 0x0F, 0x71, 0xF6, 0xE4, 0x05, + 0xE2, 0xFC, 0x45, 0x01, 0xAF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0B, 0x00, 0x99, 0xFF, 0x60, 0x01, 0xF2, 0xFC, 0x40, 0x05, + 0x85, 0xF8, 0x26, 0x09, 0x0C, 0x43, 0x26, 0x09, 0x85, 0xF8, 0x40, + 0x05, 0xF2, 0xFC, 0x60, 0x01, 0x99, 0xFF, 0x0B, 0x00, 0x00, 0x00, + 0x04, 0x00, 0xAF, 0xFF, 0x45, 0x01, 0xE2, 0xFC, 0xE4, 0x05, 0x71, + 0xF6, 0x8E, 0x0F, 0x5E, 0x42, 0x5B, 0x03, 0xCC, 0xFA, 0x5E, 0x04, + 0x2E, 0xFD, 0x62, 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xF9, + 0xFF, 0xD1, 0xFF, 0x0F, 0x01, 0x05, 0xFD, 0x39, 0x06, 0xB7, 0xF4, + 0x63, 0x16, 0x60, 0x40, 0x52, 0xFE, 0x20, 0xFD, 0x52, 0x03, 0x8E, + 0xFD, 0x4E, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEB, 0xFF, + 0xFD, 0xFF, 0xBF, 0x00, 0x5E, 0xFD, 0x31, 0x06, 0x7D, 0xF3, 0x70, + 0x1D, 0x20, 0x3D, 0x27, 0xFA, 0x5F, 0xFF, 0x2F, 0x02, 0x09, 0xFE, + 0x29, 0x01, 0x91, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDA, 0xFF, 0x31, + 0x00, 0x57, 0x00, 0xEE, 0xFD, 0xC1, 0x05, 0xE8, 0xF2, 0x7D, 0x24, + 0xC1, 0x38, 0xEB, 0xF6, 0x6A, 0x01, 0x09, 0x01, 0x94, 0xFE, 0xF7, + 0x00, 0x9E, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC7, 0xFF, 0x6C, 0x00, + 0xDB, 0xFF, 0xB1, 0xFE, 0xE7, 0x04, 0x17, 0xF3, 0x48, 0x2B, 0x68, + 0x33, 0xA5, 0xF4, 0x2B, 0x03, 0xF1, 0xFF, 0x25, 0xFF, 0xBD, 0x00, + 0xAE, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB4, 0xFF, 0xAA, 0x00, 0x51, + 0xFF, 0xA0, 0xFF, 0xA2, 0x03, 0x23, 0xF4, 0x98, 0x31, 0x49, 0x2D, + 0x51, 0xF3, 0x8E, 0x04, 0xF6, 0xFE, 0xB2, 0xFF, 0x7F, 0x00, 0xC1, + 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA2, 0xFF, 0xE6, 0x00, 0xC0, 0xFE, + 0xB0, 0x00, 0xFD, 0x01, 0x1E, 0xF6, 0x33, 0x37, 0x9C, 0x26, 0xE0, + 0xF2, 0x89, 0x05, 0x25, 0xFE, 0x33, 0x00, 0x43, 0x00, 0xD4, 0xFF, + 0x04, 0x00, 0x0F, 0x00, 0x94, 0xFF, 0x1B, 0x01, 0x33, 0xFE, 0xD4, + 0x01, 0x07, 0x00, 0x0E, 0xF9, 0xE6, 0x3B, 0x9F, 0x1F, 0x3C, 0xF3, + 0x1A, 0x06, 0x85, 0xFD, 0xA2, 0x00, 0x0C, 0x00, 0xE6, 0xFF, 0x02, + 0x00, 0x11, 0x00, 0x8C, 0xFF, 0x45, 0x01, 0xB2, 0xFD, 0xFA, 0x02, + 0xD5, 0xFD, 0xF1, 0xFC, 0x81, 0x3F, 0x8B, 0x18, 0x47, 0xF4, 0x41, + 0x06, 0x1B, 0xFD, 0xF9, 0x00, 0xDD, 0xFF, 0xF5, 0xFF, 0x01, 0x00, + 0x10, 0x00, 0x8B, 0xFF, 0x5E, 0x01, 0x48, 0xFD, 0x0F, 0x04, 0x84, + 0xFB, 0xB8, 0x01, 0xE4, 0x41, 0x9F, 0x11, 0xDE, 0xF5, 0x07, 0x06, + 0xE7, 0xFC, 0x37, 0x01, 0xB8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0D, + 0x00, 0x94, 0xFF, 0x63, 0x01, 0x00, 0xFD, 0x00, 0x05, 0x35, 0xF9, + 0x4B, 0x07, 0xFB, 0x42, 0x10, 0x0B, 0xDA, 0xF7, 0x7A, 0x05, 0xE8, + 0xFC, 0x5A, 0x01, 0x9E, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0xA7, 0xFF, 0x50, 0x01, 0xE2, 0xFC, 0xB9, 0x05, 0x0D, + 0xF7, 0x87, 0x0D, 0xB8, 0x42, 0x10, 0x05, 0x16, 0xFA, 0xA9, 0x04, + 0x17, 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0xC5, 0xFF, 0x22, 0x01, 0xF5, 0xFC, 0x28, 0x06, 0x33, 0xF5, + 0x40, 0x14, 0x20, 0x41, 0xC8, 0xFF, 0x69, 0xFC, 0xA8, 0x03, 0x6D, + 0xFD, 0x57, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF0, 0xFF, + 0xEE, 0xFF, 0xDB, 0x00, 0x3D, 0xFD, 0x3E, 0x06, 0xCE, 0xF3, 0x42, + 0x1B, 0x40, 0x3E, 0x56, 0xFB, 0xB2, 0xFE, 0x8A, 0x02, 0xE1, 0xFD, + 0x36, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDF, 0xFF, 0x20, + 0x00, 0x7A, 0x00, 0xBC, 0xFD, 0xEF, 0x05, 0x02, 0xF3, 0x55, 0x22, + 0x35, 0x3A, 0xD0, 0xF7, 0xD0, 0x00, 0x63, 0x01, 0x68, 0xFE, 0x07, + 0x01, 0x99, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCD, 0xFF, 0x5A, 0x00, + 0x03, 0x00, 0x70, 0xFE, 0x35, 0x05, 0xF2, 0xF2, 0x3B, 0x29, 0x24, + 0x35, 0x3E, 0xF5, 0xAA, 0x02, 0x44, 0x00, 0xF8, 0xFE, 0xCF, 0x00, + 0xA9, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBA, 0xFF, 0x97, 0x00, 0x7C, + 0xFF, 0x52, 0xFF, 0x11, 0x04, 0xB8, 0xF3, 0xB8, 0x2F, 0x3D, 0x2F, + 0xA1, 0xF3, 0x2B, 0x04, 0x3F, 0xFF, 0x87, 0xFF, 0x92, 0x00, 0xBB, + 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA8, 0xFF, 0xD4, 0x00, 0xED, 0xFE, + 0x5A, 0x00, 0x88, 0x02, 0x68, 0xF5, 0x91, 0x35, 0xB6, 0x28, 0xEC, + 0xF2, 0x48, 0x05, 0x60, 0xFE, 0x0D, 0x00, 0x55, 0x00, 0xCE, 0xFF, + 0x05, 0x00, 0x0E, 0x00, 0x98, 0xFF, 0x0B, 0x01, 0x5D, 0xFE, 0x79, + 0x01, 0xA9, 0x00, 0x0D, 0xF8, 0x8F, 0x3A, 0xCB, 0x21, 0x0C, 0xF3, + 0xF9, 0x05, 0xB0, 0xFD, 0x82, 0x00, 0x1C, 0x00, 0xE1, 0xFF, 0x03, + 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x39, 0x01, 0xD7, 0xFD, 0xA0, 0x02, + 0x86, 0xFE, 0xA6, 0xFB, 0x85, 0x3E, 0xB7, 0x1A, 0xE4, 0xF3, 0x40, + 0x06, 0x35, 0xFD, 0xE1, 0x00, 0xEB, 0xFF, 0xF1, 0xFF, 0x01, 0x00, + 0x11, 0x00, 0x8A, 0xFF, 0x58, 0x01, 0x66, 0xFD, 0xBD, 0x03, 0x3C, + 0xFC, 0x29, 0x00, 0x4A, 0x41, 0xB8, 0x13, 0x54, 0xF5, 0x22, 0x06, + 0xF1, 0xFC, 0x27, 0x01, 0xC2, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x0E, + 0x00, 0x90, 0xFF, 0x64, 0x01, 0x12, 0xFD, 0xBB, 0x04, 0xE8, 0xF9, + 0x81, 0x05, 0xCB, 0x42, 0x08, 0x0D, 0x35, 0xF7, 0xAD, 0x05, 0xE2, + 0xFC, 0x52, 0x01, 0xA5, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0xA0, 0xFF, 0x58, 0x01, 0xE6, 0xFC, 0x87, 0x05, 0xB0, + 0xF7, 0x8D, 0x0B, 0xF2, 0x42, 0xD7, 0x06, 0x62, 0xF9, 0xEF, 0x04, + 0x04, 0xFD, 0x63, 0x01, 0x93, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xBB, 0xFF, 0x33, 0x01, 0xE9, 0xFC, 0x0F, 0x06, 0xBA, 0xF5, + 0x24, 0x12, 0xC2, 0x41, 0x53, 0x01, 0xB2, 0xFB, 0xFB, 0x03, 0x4F, + 0xFD, 0x5D, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF4, 0xFF, + 0xE0, 0xFF, 0xF3, 0x00, 0x21, 0xFD, 0x41, 0x06, 0x2D, 0xF4, 0x16, + 0x19, 0x45, 0x3F, 0x9C, 0xFC, 0x01, 0xFE, 0xE4, 0x02, 0xBB, 0xFD, + 0x42, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE5, 0xFF, 0x10, + 0x00, 0x9A, 0x00, 0x8F, 0xFD, 0x12, 0x06, 0x2E, 0xF3, 0x2A, 0x20, + 0x92, 0x3B, 0xCC, 0xF8, 0x30, 0x00, 0xBD, 0x01, 0x3D, 0xFE, 0x17, + 0x01, 0x95, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD3, 0xFF, 0x47, 0x00, + 0x2A, 0x00, 0x33, 0xFE, 0x7A, 0x05, 0xE1, 0xF2, 0x24, 0x27, 0xCD, + 0x36, 0xEE, 0xF5, 0x21, 0x02, 0x9B, 0x00, 0xCB, 0xFE, 0xE1, 0x00, + 0xA4, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0x84, 0x00, 0xA7, + 0xFF, 0x08, 0xFF, 0x76, 0x04, 0x63, 0xF3, 0xC8, 0x2D, 0x22, 0x31, + 0x06, 0xF4, 0xBF, 0x03, 0x8C, 0xFF, 0x5C, 0xFF, 0xA5, 0x00, 0xB5, + 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAD, 0xFF, 0xC1, 0x00, 0x1A, 0xFF, + 0x05, 0x00, 0x0B, 0x03, 0xC9, 0xF4, 0xD8, 0x33, 0xC5, 0x2A, 0x0C, + 0xF3, 0xFB, 0x04, 0xA0, 0xFE, 0xE5, 0xFF, 0x68, 0x00, 0xC9, 0xFF, + 0x06, 0x00, 0x0D, 0x00, 0x9C, 0xFF, 0xFB, 0x00, 0x89, 0xFE, 0x1F, + 0x01, 0x44, 0x01, 0x22, 0xF7, 0x20, 0x39, 0xF3, 0x23, 0xED, 0xF2, + 0xCE, 0x05, 0xE1, 0xFD, 0x60, 0x00, 0x2D, 0x00, 0xDB, 0xFF, 0x03, + 0x00, 0x10, 0x00, 0x90, 0xFF, 0x2D, 0x01, 0xFF, 0xFD, 0x46, 0x02, + 0x34, 0xFF, 0x71, 0xFA, 0x6B, 0x3D, 0xE5, 0x1C, 0x90, 0xF3, 0x35, + 0x06, 0x55, 0xFD, 0xC6, 0x00, 0xF9, 0xFF, 0xEC, 0xFF, 0x01, 0x00, + 0x11, 0x00, 0x8B, 0xFF, 0x51, 0x01, 0x86, 0xFD, 0x68, 0x03, 0xF3, + 0xFC, 0xAE, 0xFE, 0x92, 0x40, 0xDA, 0x15, 0xD5, 0xF4, 0x35, 0x06, + 0x00, 0xFD, 0x14, 0x01, 0xCE, 0xFF, 0xFA, 0xFF, 0x00, 0x00, 0x0F, + 0x00, 0x8D, 0xFF, 0x63, 0x01, 0x28, 0xFD, 0x71, 0x04, 0x9E, 0xFA, + 0xC7, 0x03, 0x79, 0x42, 0x0B, 0x0F, 0x97, 0xF6, 0xDA, 0x05, 0xE2, + 0xFC, 0x48, 0x01, 0xAD, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x9A, 0xFF, 0x5F, 0x01, 0xEF, 0xFC, 0x4F, 0x05, 0x5A, + 0xF8, 0x9F, 0x09, 0x0A, 0x43, 0xAE, 0x08, 0xB1, 0xF8, 0x30, 0x05, + 0xF5, 0xFC, 0x61, 0x01, 0x97, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, + 0x00, 0xB1, 0xFF, 0x41, 0x01, 0xE3, 0xFC, 0xED, 0x05, 0x4C, 0xF6, + 0x11, 0x10, 0x42, 0x42, 0xF1, 0x02, 0xFA, 0xFA, 0x4B, 0x04, 0x34, + 0xFD, 0x61, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF8, 0xFF, + 0xD4, 0xFF, 0x0A, 0x01, 0x0A, 0xFD, 0x3C, 0x06, 0x9A, 0xF4, 0xED, + 0x16, 0x2A, 0x40, 0xF8, 0xFD, 0x4D, 0xFD, 0x3C, 0x03, 0x97, 0xFD, + 0x4C, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEA, 0xFF, 0x00, + 0x00, 0xB8, 0x00, 0x67, 0xFD, 0x2C, 0x06, 0x6B, 0xF3, 0xFC, 0x1D, + 0xD3, 0x3C, 0xDF, 0xF9, 0x89, 0xFF, 0x18, 0x02, 0x13, 0xFE, 0x26, + 0x01, 0x92, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD9, 0xFF, 0x36, 0x00, + 0x4E, 0x00, 0xFB, 0xFD, 0xB4, 0x05, 0xE4, 0xF2, 0x04, 0x25, 0x5F, + 0x38, 0xB6, 0xF6, 0x90, 0x01, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, + 0x9F, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC6, 0xFF, 0x71, 0x00, 0xD1, + 0xFF, 0xC2, 0xFE, 0xD1, 0x04, 0x23, 0xF3, 0xC9, 0x2B, 0xF5, 0x32, + 0x83, 0xF4, 0x49, 0x03, 0xDC, 0xFF, 0x30, 0xFF, 0xB8, 0x00, 0xB0, + 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB3, 0xFF, 0xAE, 0x00, 0x46, 0xFF, + 0xB4, 0xFF, 0x85, 0x03, 0x42, 0xF4, 0x0E, 0x32, 0xCA, 0x2C, 0x41, + 0xF3, 0xA5, 0x04, 0xE4, 0xFE, 0xBC, 0xFF, 0x7A, 0x00, 0xC3, 0xFF, + 0x07, 0x00, 0x0D, 0x00, 0xA1, 0xFF, 0xEA, 0x00, 0xB5, 0xFE, 0xC6, + 0x00, 0xD9, 0x01, 0x4F, 0xF6, 0x99, 0x37, 0x16, 0x26, 0xE0, 0xF2, + 0x98, 0x05, 0x16, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0xD6, 0xFF, 0x04, + 0x00, 0x0F, 0x00, 0x93, 0xFF, 0x1F, 0x01, 0x28, 0xFE, 0xEB, 0x01, + 0xDD, 0xFF, 0x52, 0xF9, 0x36, 0x3C, 0x13, 0x1F, 0x4B, 0xF3, 0x20, + 0x06, 0x7B, 0xFD, 0xA9, 0x00, 0x08, 0x00, 0xE7, 0xFF, 0x02, 0x00, + 0x11, 0x00, 0x8C, 0xFF, 0x47, 0x01, 0xA9, 0xFD, 0x10, 0x03, 0xA8, + 0xFD, 0x47, 0xFD, 0xBB, 0x3F, 0x01, 0x18, 0x62, 0xF4, 0x40, 0x06, + 0x15, 0xFD, 0xFF, 0x00, 0xDA, 0xFF, 0xF6, 0xFF, 0x01, 0x00, 0x10, + 0x00, 0x8B, 0xFF, 0x5F, 0x01, 0x41, 0xFD, 0x23, 0x04, 0x56, 0xFB, + 0x1F, 0x02, 0x06, 0x42, 0x19, 0x11, 0x02, 0xF6, 0xFF, 0x05, 0xE5, + 0xFC, 0x3B, 0x01, 0xB6, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, + 0x95, 0xFF, 0x62, 0x01, 0xFC, 0xFC, 0x10, 0x05, 0x09, 0xF9, 0xC1, + 0x07, 0x03, 0x43, 0x94, 0x0A, 0x05, 0xF8, 0x6C, 0x05, 0xEA, 0xFC, + 0x5C, 0x01, 0x9D, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0xA9, 0xFF, 0x4D, 0x01, 0xE1, 0xFC, 0xC4, 0x05, 0xE6, 0xF6, + 0x08, 0x0E, 0xA5, 0x42, 0xA1, 0x04, 0x43, 0xFA, 0x97, 0x04, 0x1D, + 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF, + 0xC8, 0xFF, 0x1E, 0x01, 0xF8, 0xFC, 0x2D, 0x06, 0x13, 0xF5, 0xC8, + 0x14, 0xF2, 0x40, 0x69, 0xFF, 0x97, 0xFC, 0x92, 0x03, 0x75, 0xFD, + 0x55, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEF, 0xFF, 0xF2, + 0xFF, 0xD4, 0x00, 0x45, 0xFD, 0x3B, 0x06, 0xB8, 0xF3, 0xCE, 0x1B, + 0xFB, 0x3D, 0x08, 0xFB, 0xDE, 0xFE, 0x73, 0x02, 0xEB, 0xFD, 0x33, + 0x01, 0x8F, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDE, 0xFF, 0x25, 0x00, + 0x71, 0x00, 0xC8, 0xFD, 0xE5, 0x05, 0xFA, 0xF2, 0xDF, 0x22, 0xDB, + 0x39, 0x94, 0xF7, 0xF7, 0x00, 0x4C, 0x01, 0x73, 0xFE, 0x03, 0x01, + 0x9A, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCC, 0xFF, 0x5E, 0x00, 0xF9, + 0xFF, 0x80, 0xFE, 0x23, 0x05, 0xF9, 0xF2, 0xC0, 0x29, 0xB8, 0x34, + 0x16, 0xF5, 0xCB, 0x02, 0x2F, 0x00, 0x03, 0xFF, 0xCA, 0x00, 0xAA, + 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xB8, 0xFF, 0x9B, 0x00, 0x72, 0xFF, + 0x65, 0xFF, 0xF6, 0x03, 0xD1, 0xF3, 0x31, 0x30, 0xC1, 0x2E, 0x8B, + 0xF3, 0x45, 0x04, 0x2D, 0xFF, 0x92, 0xFF, 0x8D, 0x00, 0xBD, 0xFF, + 0x08, 0x00, 0x0C, 0x00, 0xA6, 0xFF, 0xD8, 0x00, 0xE2, 0xFE, 0x6F, + 0x00, 0x66, 0x02, 0x93, 0xF5, 0xFB, 0x35, 0x31, 0x28, 0xE7, 0xF2, + 0x59, 0x05, 0x51, 0xFE, 0x17, 0x00, 0x50, 0x00, 0xD0, 0xFF, 0x05, + 0x00, 0x0E, 0x00, 0x97, 0xFF, 0x0F, 0x01, 0x53, 0xFE, 0x90, 0x01, + 0x81, 0x00, 0x4B, 0xF8, 0xE6, 0x3A, 0x3F, 0x21, 0x16, 0xF3, 0x02, + 0x06, 0xA5, 0xFD, 0x8A, 0x00, 0x18, 0x00, 0xE2, 0xFF, 0x02, 0x00, + 0x10, 0x00, 0x8D, 0xFF, 0x3C, 0x01, 0xCE, 0xFD, 0xB7, 0x02, 0x5A, + 0xFE, 0xF7, 0xFB, 0xC6, 0x3E, 0x2C, 0x1A, 0xFC, 0xF3, 0x41, 0x06, + 0x2E, 0xFD, 0xE7, 0x00, 0xE7, 0xFF, 0xF2, 0xFF, 0x01, 0x00, 0x10, + 0x00, 0x8B, 0xFF, 0x5A, 0x01, 0x5E, 0xFD, 0xD2, 0x03, 0x0E, 0xFC, + 0x8B, 0x00, 0x75, 0x41, 0x32, 0x13, 0x75, 0xF5, 0x1C, 0x06, 0xEE, + 0xFC, 0x2B, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, + 0x91, 0xFF, 0x64, 0x01, 0x0D, 0xFD, 0xCD, 0x04, 0xBB, 0xF9, 0xF2, + 0x05, 0xD9, 0x42, 0x88, 0x0C, 0x5E, 0xF7, 0xA1, 0x05, 0xE3, 0xFC, + 0x54, 0x01, 0xA3, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0xA2, 0xFF, 0x56, 0x01, 0xE5, 0xFC, 0x94, 0x05, 0x87, 0xF7, + 0x0A, 0x0C, 0xE6, 0x42, 0x64, 0x06, 0x8E, 0xF9, 0xDE, 0x04, 0x09, + 0xFD, 0x64, 0x01, 0x92, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBD, 0xFF, 0x2F, 0x01, 0xEC, 0xFC, 0x16, 0x06, 0x98, 0xF5, 0xAB, + 0x12, 0x9C, 0x41, 0xEE, 0x00, 0xE0, 0xFB, 0xE6, 0x03, 0x57, 0xFD, + 0x5B, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF3, 0xFF, 0xE4, + 0xFF, 0xED, 0x00, 0x27, 0xFD, 0x41, 0x06, 0x14, 0xF4, 0xA1, 0x19, + 0x06, 0x3F, 0x49, 0xFC, 0x2E, 0xFE, 0xCD, 0x02, 0xC4, 0xFD, 0x3F, + 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE3, 0xFF, 0x14, 0x00, + 0x92, 0x00, 0x9A, 0xFD, 0x0A, 0x06, 0x22, 0xF3, 0xB4, 0x20, 0x3C, + 0x3B, 0x8B, 0xF8, 0x58, 0x00, 0xA7, 0x01, 0x48, 0xFE, 0x13, 0x01, + 0x96, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD1, 0xFF, 0x4C, 0x00, 0x20, + 0x00, 0x42, 0xFE, 0x6A, 0x05, 0xE3, 0xF2, 0xAB, 0x27, 0x66, 0x36, + 0xC0, 0xF5, 0x44, 0x02, 0x85, 0x00, 0xD7, 0xFE, 0xDD, 0x00, 0xA5, + 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xBE, 0xFF, 0x89, 0x00, 0x9D, 0xFF, + 0x1A, 0xFF, 0x5E, 0x04, 0x76, 0xF3, 0x45, 0x2E, 0xAA, 0x30, 0xEB, + 0xF3, 0xDB, 0x03, 0x79, 0xFF, 0x67, 0xFF, 0xA0, 0x00, 0xB7, 0xFF, + 0x09, 0x00, 0x0B, 0x00, 0xAC, 0xFF, 0xC6, 0x00, 0x0E, 0xFF, 0x1A, + 0x00, 0xEB, 0x02, 0xEF, 0xF4, 0x49, 0x34, 0x43, 0x2A, 0x02, 0xF3, + 0x0F, 0x05, 0x90, 0xFE, 0xEF, 0xFF, 0x63, 0x00, 0xCA, 0xFF, 0x06, + 0x00, 0x0E, 0x00, 0x9B, 0xFF, 0xFF, 0x00, 0x7E, 0xFE, 0x36, 0x01, + 0x1E, 0x01, 0x5B, 0xF7, 0x7E, 0x39, 0x69, 0x23, 0xF3, 0xF2, 0xD9, + 0x05, 0xD4, 0xFD, 0x69, 0x00, 0x29, 0x00, 0xDD, 0xFF, 0x03, 0x00, + 0x10, 0x00, 0x90, 0xFF, 0x30, 0x01, 0xF5, 0xFD, 0x5C, 0x02, 0x09, + 0xFF, 0xBC, 0xFA, 0xB5, 0x3D, 0x5A, 0x1C, 0xA3, 0xF3, 0x38, 0x06, + 0x4D, 0xFD, 0xCD, 0x00, 0xF5, 0xFF, 0xED, 0xFF, 0x01, 0x00, 0x11, + 0x00, 0x8B, 0xFF, 0x53, 0x01, 0x7E, 0xFD, 0x7D, 0x03, 0xC5, 0xFC, + 0x0B, 0xFF, 0xC3, 0x40, 0x51, 0x15, 0xF4, 0xF4, 0x31, 0x06, 0xFC, + 0xFC, 0x19, 0x01, 0xCB, 0xFF, 0xFB, 0xFF, 0x00, 0x00, 0x0F, 0x00, + 0x8E, 0xFF, 0x63, 0x01, 0x22, 0xFD, 0x84, 0x04, 0x71, 0xFA, 0x34, + 0x04, 0x90, 0x42, 0x89, 0x0E, 0xBE, 0xF6, 0xCF, 0x05, 0xE1, 0xFC, + 0x4A, 0x01, 0xAB, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0x9B, 0xFF, 0x5D, 0x01, 0xEC, 0xFC, 0x5D, 0x05, 0x2F, 0xF8, + 0x19, 0x0A, 0x07, 0x43, 0x37, 0x08, 0xDD, 0xF8, 0x21, 0x05, 0xF8, + 0xFC, 0x62, 0x01, 0x96, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, + 0xB4, 0xFF, 0x3E, 0x01, 0xE4, 0xFC, 0xF6, 0x05, 0x26, 0xF6, 0x95, + 0x10, 0x26, 0x42, 0x87, 0x02, 0x28, 0xFB, 0x37, 0x04, 0x3B, 0xFD, + 0x60, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF7, 0xFF, 0xD7, + 0xFF, 0x04, 0x01, 0x0F, 0xFD, 0x3E, 0x06, 0x7D, 0xF4, 0x76, 0x17, + 0xF4, 0x3F, 0x9F, 0xFD, 0x7B, 0xFD, 0x26, 0x03, 0xA0, 0xFD, 0x4A, + 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE9, 0xFF, 0x04, 0x00, + 0xB1, 0x00, 0x71, 0xFD, 0x26, 0x06, 0x5A, 0xF3, 0x88, 0x1E, 0x87, + 0x3C, 0x98, 0xF9, 0xB3, 0xFF, 0x02, 0x02, 0x1E, 0xFE, 0x22, 0x01, + 0x93, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD7, 0xFF, 0x3A, 0x00, 0x45, + 0x00, 0x09, 0xFE, 0xA7, 0x05, 0xE1, 0xF2, 0x8D, 0x25, 0xFD, 0x37, + 0x82, 0xF6, 0xB5, 0x01, 0xDC, 0x00, 0xAA, 0xFE, 0xEE, 0x00, 0xA0, + 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC4, 0xFF, 0x76, 0x00, 0xC7, 0xFF, + 0xD3, 0xFE, 0xBC, 0x04, 0x31, 0xF3, 0x4A, 0x2C, 0x83, 0x32, 0x61, + 0xF4, 0x68, 0x03, 0xC8, 0xFF, 0x3B, 0xFF, 0xB3, 0x00, 0xB1, 0xFF, + 0x0A, 0x00, 0x0A, 0x00, 0xB1, 0xFF, 0xB3, 0x00, 0x3B, 0xFF, 0xC8, + 0xFF, 0x68, 0x03, 0x61, 0xF4, 0x83, 0x32, 0x4A, 0x2C, 0x31, 0xF3, + 0xBC, 0x04, 0xD3, 0xFE, 0xC7, 0xFF, 0x76, 0x00, 0xC4, 0xFF, 0x06, + 0x00, 0x0D, 0x00, 0xA0, 0xFF, 0xEE, 0x00, 0xAA, 0xFE, 0xDC, 0x00, + 0xB5, 0x01, 0x82, 0xF6, 0xFD, 0x37, 0x8D, 0x25, 0xE1, 0xF2, 0xA7, + 0x05, 0x09, 0xFE, 0x45, 0x00, 0x3A, 0x00, 0xD7, 0xFF, 0x04, 0x00, + 0x0F, 0x00, 0x93, 0xFF, 0x22, 0x01, 0x1E, 0xFE, 0x02, 0x02, 0xB3, + 0xFF, 0x98, 0xF9, 0x87, 0x3C, 0x88, 0x1E, 0x5A, 0xF3, 0x26, 0x06, + 0x71, 0xFD, 0xB1, 0x00, 0x04, 0x00, 0xE9, 0xFF, 0x02, 0x00, 0x11, + 0x00, 0x8B, 0xFF, 0x4A, 0x01, 0xA0, 0xFD, 0x26, 0x03, 0x7B, 0xFD, + 0x9F, 0xFD, 0xF4, 0x3F, 0x76, 0x17, 0x7D, 0xF4, 0x3E, 0x06, 0x0F, + 0xFD, 0x04, 0x01, 0xD7, 0xFF, 0xF7, 0xFF, 0x01, 0x00, 0x10, 0x00, + 0x8C, 0xFF, 0x60, 0x01, 0x3B, 0xFD, 0x37, 0x04, 0x28, 0xFB, 0x87, + 0x02, 0x26, 0x42, 0x95, 0x10, 0x26, 0xF6, 0xF6, 0x05, 0xE4, 0xFC, + 0x3E, 0x01, 0xB4, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x96, + 0xFF, 0x62, 0x01, 0xF8, 0xFC, 0x21, 0x05, 0xDD, 0xF8, 0x37, 0x08, + 0x07, 0x43, 0x19, 0x0A, 0x2F, 0xF8, 0x5D, 0x05, 0xEC, 0xFC, 0x5D, + 0x01, 0x9B, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0xAB, 0xFF, 0x4A, 0x01, 0xE1, 0xFC, 0xCF, 0x05, 0xBE, 0xF6, 0x89, + 0x0E, 0x90, 0x42, 0x34, 0x04, 0x71, 0xFA, 0x84, 0x04, 0x22, 0xFD, + 0x63, 0x01, 0x8E, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0xFF, 0xCB, + 0xFF, 0x19, 0x01, 0xFC, 0xFC, 0x31, 0x06, 0xF4, 0xF4, 0x51, 0x15, + 0xC3, 0x40, 0x0B, 0xFF, 0xC5, 0xFC, 0x7D, 0x03, 0x7E, 0xFD, 0x53, + 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xED, 0xFF, 0xF5, 0xFF, + 0xCD, 0x00, 0x4D, 0xFD, 0x38, 0x06, 0xA3, 0xF3, 0x5A, 0x1C, 0xB5, + 0x3D, 0xBC, 0xFA, 0x09, 0xFF, 0x5C, 0x02, 0xF5, 0xFD, 0x30, 0x01, + 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDD, 0xFF, 0x29, 0x00, 0x69, + 0x00, 0xD4, 0xFD, 0xD9, 0x05, 0xF3, 0xF2, 0x69, 0x23, 0x7E, 0x39, + 0x5B, 0xF7, 0x1E, 0x01, 0x36, 0x01, 0x7E, 0xFE, 0xFF, 0x00, 0x9B, + 0xFF, 0x0E, 0x00, 0x06, 0x00, 0xCA, 0xFF, 0x63, 0x00, 0xEF, 0xFF, + 0x90, 0xFE, 0x0F, 0x05, 0x02, 0xF3, 0x43, 0x2A, 0x49, 0x34, 0xEF, + 0xF4, 0xEB, 0x02, 0x1A, 0x00, 0x0E, 0xFF, 0xC6, 0x00, 0xAC, 0xFF, + 0x0B, 0x00, 0x09, 0x00, 0xB7, 0xFF, 0xA0, 0x00, 0x67, 0xFF, 0x79, + 0xFF, 0xDB, 0x03, 0xEB, 0xF3, 0xAA, 0x30, 0x45, 0x2E, 0x76, 0xF3, + 0x5E, 0x04, 0x1A, 0xFF, 0x9D, 0xFF, 0x89, 0x00, 0xBE, 0xFF, 0x07, + 0x00, 0x0C, 0x00, 0xA5, 0xFF, 0xDD, 0x00, 0xD7, 0xFE, 0x85, 0x00, + 0x44, 0x02, 0xC0, 0xF5, 0x66, 0x36, 0xAB, 0x27, 0xE3, 0xF2, 0x6A, + 0x05, 0x42, 0xFE, 0x20, 0x00, 0x4C, 0x00, 0xD1, 0xFF, 0x04, 0x00, + 0x0F, 0x00, 0x96, 0xFF, 0x13, 0x01, 0x48, 0xFE, 0xA7, 0x01, 0x58, + 0x00, 0x8B, 0xF8, 0x3C, 0x3B, 0xB4, 0x20, 0x22, 0xF3, 0x0A, 0x06, + 0x9A, 0xFD, 0x92, 0x00, 0x14, 0x00, 0xE3, 0xFF, 0x02, 0x00, 0x10, + 0x00, 0x8D, 0xFF, 0x3F, 0x01, 0xC4, 0xFD, 0xCD, 0x02, 0x2E, 0xFE, + 0x49, 0xFC, 0x06, 0x3F, 0xA1, 0x19, 0x14, 0xF4, 0x41, 0x06, 0x27, + 0xFD, 0xED, 0x00, 0xE4, 0xFF, 0xF3, 0xFF, 0x01, 0x00, 0x10, 0x00, + 0x8B, 0xFF, 0x5B, 0x01, 0x57, 0xFD, 0xE6, 0x03, 0xE0, 0xFB, 0xEE, + 0x00, 0x9C, 0x41, 0xAB, 0x12, 0x98, 0xF5, 0x16, 0x06, 0xEC, 0xFC, + 0x2F, 0x01, 0xBD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x92, + 0xFF, 0x64, 0x01, 0x09, 0xFD, 0xDE, 0x04, 0x8E, 0xF9, 0x64, 0x06, + 0xE6, 0x42, 0x0A, 0x0C, 0x87, 0xF7, 0x94, 0x05, 0xE5, 0xFC, 0x56, + 0x01, 0xA2, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0xA3, 0xFF, 0x54, 0x01, 0xE3, 0xFC, 0xA1, 0x05, 0x5E, 0xF7, 0x88, + 0x0C, 0xD9, 0x42, 0xF2, 0x05, 0xBB, 0xF9, 0xCD, 0x04, 0x0D, 0xFD, + 0x64, 0x01, 0x91, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, + 0xFF, 0x2B, 0x01, 0xEE, 0xFC, 0x1C, 0x06, 0x75, 0xF5, 0x32, 0x13, + 0x75, 0x41, 0x8B, 0x00, 0x0E, 0xFC, 0xD2, 0x03, 0x5E, 0xFD, 0x5A, + 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF2, 0xFF, 0xE7, 0xFF, + 0xE7, 0x00, 0x2E, 0xFD, 0x41, 0x06, 0xFC, 0xF3, 0x2C, 0x1A, 0xC6, + 0x3E, 0xF7, 0xFB, 0x5A, 0xFE, 0xB7, 0x02, 0xCE, 0xFD, 0x3C, 0x01, + 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x18, 0x00, 0x8A, + 0x00, 0xA5, 0xFD, 0x02, 0x06, 0x16, 0xF3, 0x3F, 0x21, 0xE6, 0x3A, + 0x4B, 0xF8, 0x81, 0x00, 0x90, 0x01, 0x53, 0xFE, 0x0F, 0x01, 0x97, + 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xD0, 0xFF, 0x50, 0x00, 0x17, 0x00, + 0x51, 0xFE, 0x59, 0x05, 0xE7, 0xF2, 0x31, 0x28, 0xFB, 0x35, 0x93, + 0xF5, 0x66, 0x02, 0x6F, 0x00, 0xE2, 0xFE, 0xD8, 0x00, 0xA6, 0xFF, + 0x0C, 0x00, 0x08, 0x00, 0xBD, 0xFF, 0x8D, 0x00, 0x92, 0xFF, 0x2D, + 0xFF, 0x45, 0x04, 0x8B, 0xF3, 0xC1, 0x2E, 0x31, 0x30, 0xD1, 0xF3, + 0xF6, 0x03, 0x65, 0xFF, 0x72, 0xFF, 0x9B, 0x00, 0xB8, 0xFF, 0x08, + 0x00, 0x0B, 0x00, 0xAA, 0xFF, 0xCA, 0x00, 0x03, 0xFF, 0x2F, 0x00, + 0xCB, 0x02, 0x16, 0xF5, 0xB8, 0x34, 0xC0, 0x29, 0xF9, 0xF2, 0x23, + 0x05, 0x80, 0xFE, 0xF9, 0xFF, 0x5E, 0x00, 0xCC, 0xFF, 0x05, 0x00, + 0x0E, 0x00, 0x9A, 0xFF, 0x03, 0x01, 0x73, 0xFE, 0x4C, 0x01, 0xF7, + 0x00, 0x94, 0xF7, 0xDB, 0x39, 0xDF, 0x22, 0xFA, 0xF2, 0xE5, 0x05, + 0xC8, 0xFD, 0x71, 0x00, 0x25, 0x00, 0xDE, 0xFF, 0x03, 0x00, 0x10, + 0x00, 0x8F, 0xFF, 0x33, 0x01, 0xEB, 0xFD, 0x73, 0x02, 0xDE, 0xFE, + 0x08, 0xFB, 0xFB, 0x3D, 0xCE, 0x1B, 0xB8, 0xF3, 0x3B, 0x06, 0x45, + 0xFD, 0xD4, 0x00, 0xF2, 0xFF, 0xEF, 0xFF, 0x01, 0x00, 0x11, 0x00, + 0x8A, 0xFF, 0x55, 0x01, 0x75, 0xFD, 0x92, 0x03, 0x97, 0xFC, 0x69, + 0xFF, 0xF2, 0x40, 0xC8, 0x14, 0x13, 0xF5, 0x2D, 0x06, 0xF8, 0xFC, + 0x1E, 0x01, 0xC8, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x8F, + 0xFF, 0x64, 0x01, 0x1D, 0xFD, 0x97, 0x04, 0x43, 0xFA, 0xA1, 0x04, + 0xA5, 0x42, 0x08, 0x0E, 0xE6, 0xF6, 0xC4, 0x05, 0xE1, 0xFC, 0x4D, + 0x01, 0xA9, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, + 0x9D, 0xFF, 0x5C, 0x01, 0xEA, 0xFC, 0x6C, 0x05, 0x05, 0xF8, 0x94, + 0x0A, 0x03, 0x43, 0xC1, 0x07, 0x09, 0xF9, 0x10, 0x05, 0xFC, 0xFC, + 0x62, 0x01, 0x95, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB6, + 0xFF, 0x3B, 0x01, 0xE5, 0xFC, 0xFF, 0x05, 0x02, 0xF6, 0x19, 0x11, + 0x06, 0x42, 0x1F, 0x02, 0x56, 0xFB, 0x23, 0x04, 0x41, 0xFD, 0x5F, + 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF6, 0xFF, 0xDA, 0xFF, + 0xFF, 0x00, 0x15, 0xFD, 0x40, 0x06, 0x62, 0xF4, 0x01, 0x18, 0xBB, + 0x3F, 0x47, 0xFD, 0xA8, 0xFD, 0x10, 0x03, 0xA9, 0xFD, 0x47, 0x01, + 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE7, 0xFF, 0x08, 0x00, 0xA9, + 0x00, 0x7B, 0xFD, 0x20, 0x06, 0x4B, 0xF3, 0x13, 0x1F, 0x36, 0x3C, + 0x52, 0xF9, 0xDD, 0xFF, 0xEB, 0x01, 0x28, 0xFE, 0x1F, 0x01, 0x93, + 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD6, 0xFF, 0x3F, 0x00, 0x3C, 0x00, + 0x16, 0xFE, 0x98, 0x05, 0xE0, 0xF2, 0x16, 0x26, 0x99, 0x37, 0x4F, + 0xF6, 0xD9, 0x01, 0xC6, 0x00, 0xB5, 0xFE, 0xEA, 0x00, 0xA1, 0xFF, + 0x0D, 0x00, 0x07, 0x00, 0xC3, 0xFF, 0x7A, 0x00, 0xBC, 0xFF, 0xE4, + 0xFE, 0xA5, 0x04, 0x41, 0xF3, 0xCA, 0x2C, 0x0E, 0x32, 0x42, 0xF4, + 0x85, 0x03, 0xB4, 0xFF, 0x46, 0xFF, 0xAE, 0x00, 0xB3, 0xFF, 0x09, + 0x00, 0x0A, 0x00, 0xB0, 0xFF, 0xB8, 0x00, 0x30, 0xFF, 0xDC, 0xFF, + 0x49, 0x03, 0x83, 0xF4, 0xF5, 0x32, 0xC9, 0x2B, 0x23, 0xF3, 0xD1, + 0x04, 0xC2, 0xFE, 0xD1, 0xFF, 0x71, 0x00, 0xC6, 0xFF, 0x06, 0x00, + 0x0D, 0x00, 0x9F, 0xFF, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, 0x90, + 0x01, 0xB6, 0xF6, 0x5F, 0x38, 0x04, 0x25, 0xE4, 0xF2, 0xB4, 0x05, + 0xFB, 0xFD, 0x4E, 0x00, 0x36, 0x00, 0xD9, 0xFF, 0x04, 0x00, 0x0F, + 0x00, 0x92, 0xFF, 0x26, 0x01, 0x13, 0xFE, 0x18, 0x02, 0x89, 0xFF, + 0xDF, 0xF9, 0xD3, 0x3C, 0xFC, 0x1D, 0x6B, 0xF3, 0x2C, 0x06, 0x67, + 0xFD, 0xB8, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0x02, 0x00, 0x11, 0x00, + 0x8B, 0xFF, 0x4C, 0x01, 0x97, 0xFD, 0x3C, 0x03, 0x4D, 0xFD, 0xF8, + 0xFD, 0x2A, 0x40, 0xED, 0x16, 0x9A, 0xF4, 0x3C, 0x06, 0x0A, 0xFD, + 0x0A, 0x01, 0xD4, 0xFF, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x8C, + 0xFF, 0x61, 0x01, 0x34, 0xFD, 0x4B, 0x04, 0xFA, 0xFA, 0xF1, 0x02, + 0x42, 0x42, 0x11, 0x10, 0x4C, 0xF6, 0xED, 0x05, 0xE3, 0xFC, 0x41, + 0x01, 0xB1, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x97, 0xFF, + 0x61, 0x01, 0xF5, 0xFC, 0x30, 0x05, 0xB1, 0xF8, 0xAE, 0x08, 0x0A, + 0x43, 0x9F, 0x09, 0x5A, 0xF8, 0x4F, 0x05, 0xEF, 0xFC, 0x5F, 0x01, + 0x9A, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xAD, + 0xFF, 0x48, 0x01, 0xE2, 0xFC, 0xDA, 0x05, 0x97, 0xF6, 0x0B, 0x0F, + 0x79, 0x42, 0xC7, 0x03, 0x9E, 0xFA, 0x71, 0x04, 0x28, 0xFD, 0x63, + 0x01, 0x8D, 0xFF, 0x0F, 0x00 +}; + +static u16 +coefficient_sizes[8 * 2] = { + /* Playback */ + 0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000, + /* capture */ + 0x0020, 0x1260, 0x0020, 0x1260, 0x0000, 0x0040, 0x1260, 0x0000, +}; + diff -Nru a/sound/pci/rme96.c b/sound/pci/rme96.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/rme96.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,2523 @@ +/* + * ALSA driver for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST audio + * interfaces + * + * Copyright (c) 2000, 2001 Anders Torger + * + * Thanks to Henk Hesselink for the analog volume control + * code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +/* note, two last pcis should be equal, it is not a bug */ +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Anders Torger "); +MODULE_DESCRIPTION("RME Digi96, Digi96/8, Digi96/8 PRO, Digi96/8 PST, " + "Digi96/8 PAD"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{RME,Digi96}," + "{RME,Digi96/8}," + "{RME,Digi96/8 PRO}," + "{RME,Digi96/8 PST}," + "{RME,Digi96/8 PAD}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for RME Digi96 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for RME Digi96 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable RME Digi96 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); + +/* + * Defines for RME Digi96 series, from internal RME reference documents + * dated 12.01.00 + */ + +#define RME96_SPDIF_NCHANNELS 2 + +/* Playback and capture buffer size */ +#define RME96_BUFFER_SIZE 0x10000 + +/* IO area size */ +#define RME96_IO_SIZE 0x60000 + +/* IO area offsets */ +#define RME96_IO_PLAY_BUFFER 0x0 +#define RME96_IO_REC_BUFFER 0x10000 +#define RME96_IO_CONTROL_REGISTER 0x20000 +#define RME96_IO_ADDITIONAL_REG 0x20004 +#define RME96_IO_CONFIRM_PLAY_IRQ 0x20008 +#define RME96_IO_CONFIRM_REC_IRQ 0x2000C +#define RME96_IO_SET_PLAY_POS 0x40000 +#define RME96_IO_RESET_PLAY_POS 0x4FFFC +#define RME96_IO_SET_REC_POS 0x50000 +#define RME96_IO_RESET_REC_POS 0x5FFFC +#define RME96_IO_GET_PLAY_POS 0x20000 +#define RME96_IO_GET_REC_POS 0x30000 + +/* Write control register bits */ +#define RME96_WCR_START (1 << 0) +#define RME96_WCR_START_2 (1 << 1) +#define RME96_WCR_GAIN_0 (1 << 2) +#define RME96_WCR_GAIN_1 (1 << 3) +#define RME96_WCR_MODE24 (1 << 4) +#define RME96_WCR_MODE24_2 (1 << 5) +#define RME96_WCR_BM (1 << 6) +#define RME96_WCR_BM_2 (1 << 7) +#define RME96_WCR_ADAT (1 << 8) +#define RME96_WCR_FREQ_0 (1 << 9) +#define RME96_WCR_FREQ_1 (1 << 10) +#define RME96_WCR_DS (1 << 11) +#define RME96_WCR_PRO (1 << 12) +#define RME96_WCR_EMP (1 << 13) +#define RME96_WCR_SEL (1 << 14) +#define RME96_WCR_MASTER (1 << 15) +#define RME96_WCR_PD (1 << 16) +#define RME96_WCR_INP_0 (1 << 17) +#define RME96_WCR_INP_1 (1 << 18) +#define RME96_WCR_THRU_0 (1 << 19) +#define RME96_WCR_THRU_1 (1 << 20) +#define RME96_WCR_THRU_2 (1 << 21) +#define RME96_WCR_THRU_3 (1 << 22) +#define RME96_WCR_THRU_4 (1 << 23) +#define RME96_WCR_THRU_5 (1 << 24) +#define RME96_WCR_THRU_6 (1 << 25) +#define RME96_WCR_THRU_7 (1 << 26) +#define RME96_WCR_DOLBY (1 << 27) +#define RME96_WCR_MONITOR_0 (1 << 28) +#define RME96_WCR_MONITOR_1 (1 << 29) +#define RME96_WCR_ISEL (1 << 30) +#define RME96_WCR_IDIS (1 << 31) + +#define RME96_WCR_BITPOS_GAIN_0 2 +#define RME96_WCR_BITPOS_GAIN_1 3 +#define RME96_WCR_BITPOS_FREQ_0 9 +#define RME96_WCR_BITPOS_FREQ_1 10 +#define RME96_WCR_BITPOS_INP_0 17 +#define RME96_WCR_BITPOS_INP_1 18 +#define RME96_WCR_BITPOS_MONITOR_0 28 +#define RME96_WCR_BITPOS_MONITOR_1 29 + +/* Read control register bits */ +#define RME96_RCR_AUDIO_ADDR_MASK 0xFFFF +#define RME96_RCR_IRQ_2 (1 << 16) +#define RME96_RCR_T_OUT (1 << 17) +#define RME96_RCR_DEV_ID_0 (1 << 21) +#define RME96_RCR_DEV_ID_1 (1 << 22) +#define RME96_RCR_LOCK (1 << 23) +#define RME96_RCR_VERF (1 << 26) +#define RME96_RCR_F0 (1 << 27) +#define RME96_RCR_F1 (1 << 28) +#define RME96_RCR_F2 (1 << 29) +#define RME96_RCR_AUTOSYNC (1 << 30) +#define RME96_RCR_IRQ (1 << 31) + +#define RME96_RCR_BITPOS_F0 27 +#define RME96_RCR_BITPOS_F1 28 +#define RME96_RCR_BITPOS_F2 29 + +/* Additonal register bits */ +#define RME96_AR_WSEL (1 << 0) +#define RME96_AR_ANALOG (1 << 1) +#define RME96_AR_FREQPAD_0 (1 << 2) +#define RME96_AR_FREQPAD_1 (1 << 3) +#define RME96_AR_FREQPAD_2 (1 << 4) +#define RME96_AR_PD2 (1 << 5) +#define RME96_AR_DAC_EN (1 << 6) +#define RME96_AR_CLATCH (1 << 7) +#define RME96_AR_CCLK (1 << 8) +#define RME96_AR_CDATA (1 << 9) + +#define RME96_AR_BITPOS_F0 2 +#define RME96_AR_BITPOS_F1 3 +#define RME96_AR_BITPOS_F2 4 + +/* Monitor tracks */ +#define RME96_MONITOR_TRACKS_1_2 0 +#define RME96_MONITOR_TRACKS_3_4 1 +#define RME96_MONITOR_TRACKS_5_6 2 +#define RME96_MONITOR_TRACKS_7_8 3 + +/* Attenuation */ +#define RME96_ATTENUATION_0 0 +#define RME96_ATTENUATION_6 1 +#define RME96_ATTENUATION_12 2 +#define RME96_ATTENUATION_18 3 + +/* Input types */ +#define RME96_INPUT_OPTICAL 0 +#define RME96_INPUT_COAXIAL 1 +#define RME96_INPUT_INTERNAL 2 +#define RME96_INPUT_XLR 3 +#define RME96_INPUT_ANALOG 4 + +/* Clock modes */ +#define RME96_CLOCKMODE_SLAVE 0 +#define RME96_CLOCKMODE_MASTER 1 +#define RME96_CLOCKMODE_WORDCLOCK 2 + +/* Block sizes in bytes */ +#define RME96_SMALL_BLOCK_SIZE 2048 +#define RME96_LARGE_BLOCK_SIZE 8192 + +/* Volume control */ +#define RME96_AD1852_VOL_BITS 14 +#define RME96_AD1855_VOL_BITS 10 + +/* + * PCI vendor/device ids, could in the future be defined in , + * therefore #ifndef is used. + */ +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10ee +#endif +#ifndef PCI_DEVICE_ID_DIGI96 +#define PCI_DEVICE_ID_DIGI96 0x3fc0 +#endif +#ifndef PCI_DEVICE_ID_DIGI96_8 +#define PCI_DEVICE_ID_DIGI96_8 0x3fc1 +#endif +#ifndef PCI_DEVICE_ID_DIGI96_8_PRO +#define PCI_DEVICE_ID_DIGI96_8_PRO 0x3fc2 +#endif +#ifndef PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST +#define PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST 0x3fc3 +#endif + +typedef struct snd_rme96 { + spinlock_t lock; + int irq; + unsigned long port; + struct resource *res_port; + unsigned long iobase; + + u32 wcreg; /* cached write control register value */ + u32 wcreg_spdif; /* S/PDIF setup */ + u32 wcreg_spdif_stream; /* S/PDIF setup (temporary) */ + u32 rcreg; /* cached read control register value */ + u32 areg; /* cached additional register value */ + u16 vol[2]; /* cached volume of analog output */ + + u8 rev; /* card revision number */ + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + int playback_frlog; /* log2 of framesize */ + int capture_frlog; + + size_t playback_periodsize; /* in bytes, zero if not used */ + size_t capture_periodsize; /* in bytes, zero if not used */ + + snd_pcm_uframes_t playback_last_appl_ptr; + size_t playback_ptr; + size_t capture_ptr; + + snd_card_t *card; + snd_pcm_t *spdif_pcm; + snd_pcm_t *adat_pcm; + struct pci_dev *pci; + snd_info_entry_t *proc_entry; + snd_kcontrol_t *spdif_ctl; +} rme96_t; + +static struct pci_device_id snd_rme96_ids[] __devinitdata = { + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PRO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_rme96_ids); + +#define RME96_ISPLAYING(rme96) ((rme96)->wcreg & RME96_WCR_START) +#define RME96_ISRECORDING(rme96) ((rme96)->wcreg & RME96_WCR_START_2) +#define RME96_HAS_ANALOG_IN(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST) +#define RME96_HAS_ANALOG_OUT(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO || \ + (rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST) +#define RME96_DAC_IS_1852(rme96) (RME96_HAS_ANALOG_OUT(rme96) && (rme96)->rev >= 4) +#define RME96_DAC_IS_1855(rme96) (((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && (rme96)->rev < 4) || \ + ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO && (rme96)->rev == 2)) +#define RME96_185X_MAX_OUT(rme96) ((1 << (RME96_DAC_IS_1852(rme96) ? RME96_AD1852_VOL_BITS : RME96_AD1855_VOL_BITS)) - 1) + +static int +snd_rme96_playback_prepare(snd_pcm_substream_t *substream); + +static int +snd_rme96_capture_prepare(snd_pcm_substream_t *substream); + +static int +snd_rme96_playback_trigger(snd_pcm_substream_t *substream, + int cmd); + +static int +snd_rme96_capture_trigger(snd_pcm_substream_t *substream, + int cmd); + +static snd_pcm_uframes_t +snd_rme96_playback_pointer(snd_pcm_substream_t *substream); + +static snd_pcm_uframes_t +snd_rme96_capture_pointer(snd_pcm_substream_t *substream); + +static void __init +snd_rme96_proc_init(rme96_t *rme96); + +static void +snd_rme96_proc_done(rme96_t *rme96); + +static int +snd_rme96_create_switches(snd_card_t *card, + rme96_t *rme96); + +static inline unsigned int +snd_rme96_playback_ptr(rme96_t *rme96) +{ + return (readl(rme96->iobase + RME96_IO_GET_PLAY_POS) + & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->playback_frlog; +} + +static inline unsigned int +snd_rme96_capture_ptr(rme96_t *rme96) +{ + return (readl(rme96->iobase + RME96_IO_GET_REC_POS) + & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->capture_frlog; +} + +static int +snd_rme96_playback_silence(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + count <<= rme96->playback_frlog; + pos <<= rme96->playback_frlog; + memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, + 0, count); + return 0; +} + +static int +snd_rme96_playback_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + count <<= rme96->playback_frlog; + pos <<= rme96->playback_frlog; + copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, + count); + return 0; +} + +static int +snd_rme96_capture_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + count <<= rme96->capture_frlog; + pos <<= rme96->capture_frlog; + copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, + count); + return 0; +} + +/* + * Digital output capabilites (S/PDIF) + */ +static snd_pcm_hardware_t snd_rme96_playback_spdif_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + formats: (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + rates: (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + rate_min: 32000, + rate_max: 96000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: RME96_BUFFER_SIZE, + period_bytes_min: RME96_SMALL_BLOCK_SIZE, + period_bytes_max: RME96_LARGE_BLOCK_SIZE, + periods_min: RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + periods_max: RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + fifo_size: 0, +}; + +/* + * Digital input capabilites (S/PDIF) + */ +static snd_pcm_hardware_t snd_rme96_capture_spdif_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + formats: (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + rates: (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + rate_min: 32000, + rate_max: 96000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: RME96_BUFFER_SIZE, + period_bytes_min: RME96_SMALL_BLOCK_SIZE, + period_bytes_max: RME96_LARGE_BLOCK_SIZE, + periods_min: RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + periods_max: RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + fifo_size: 0, +}; + +/* + * Digital output capabilites (ADAT) + */ +static snd_pcm_hardware_t snd_rme96_playback_adat_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + rate_min: 44100, + rate_max: 48000, + channels_min: 8, + channels_max: 8, + buffer_bytes_max: RME96_BUFFER_SIZE, + period_bytes_min: RME96_SMALL_BLOCK_SIZE, + period_bytes_max: RME96_LARGE_BLOCK_SIZE, + periods_min: RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + periods_max: RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + fifo_size: 0, +}; + +/* + * Digital input capabilites (ADAT) + */ +static snd_pcm_hardware_t snd_rme96_capture_adat_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + rate_min: 44100, + rate_max: 48000, + channels_min: 8, + channels_max: 8, + buffer_bytes_max: RME96_BUFFER_SIZE, + period_bytes_min: RME96_SMALL_BLOCK_SIZE, + period_bytes_max: RME96_LARGE_BLOCK_SIZE, + periods_min: RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + periods_max: RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + fifo_size: 0, +}; + +/* + * The CDATA, CCLK and CLATCH bits can be used to write to the SPI interface + * of the AD1852 or AD1852 D/A converter on the board. CDATA must be set up + * on the falling edge of CCLK and be stable on the rising edge. The rising + * edge of CLATCH after the last data bit clocks in the whole data word. + * A fast processor could probably drive the SPI interface faster than the + * DAC can handle (3MHz for the 1855, unknown for the 1852). The udelay(1) + * limits the data rate to 500KHz and only causes a delay of 33 microsecs. + * + * NOTE: increased delay from 1 to 10, since there where problems setting + * the volume. + */ +static void +snd_rme96_write_SPI(rme96_t *rme96, u16 val) +{ + int i; + + for (i = 0; i < 16; i++) { + if (val & 0x8000) { + rme96->areg |= RME96_AR_CDATA; + } else { + rme96->areg &= ~RME96_AR_CDATA; + } + rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CLATCH); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + udelay(10); + rme96->areg |= RME96_AR_CCLK; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + udelay(10); + val <<= 1; + } + rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CDATA); + rme96->areg |= RME96_AR_CLATCH; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + udelay(10); + rme96->areg &= ~RME96_AR_CLATCH; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); +} + +static void +snd_rme96_apply_dac_volume(rme96_t *rme96) +{ + if (RME96_DAC_IS_1852(rme96)) { + snd_rme96_write_SPI(rme96, (rme96->vol[0] << 2) | 0x0); + snd_rme96_write_SPI(rme96, (rme96->vol[1] << 2) | 0x2); + } else if (RME96_DAC_IS_1855(rme96)) { + snd_rme96_write_SPI(rme96, (rme96->vol[0] & 0x3FF) | 0x000); + snd_rme96_write_SPI(rme96, (rme96->vol[1] & 0x3FF) | 0x400); + } +} + +static void +snd_rme96_reset_dac(rme96_t *rme96) +{ + writel(rme96->wcreg | RME96_WCR_PD, + rme96->iobase + RME96_IO_CONTROL_REGISTER); + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static int +snd_rme96_getmontracks(rme96_t *rme96) +{ + return ((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_1) & 1) << 1); +} + +static int +snd_rme96_setmontracks(rme96_t *rme96, + int montracks) +{ + if (montracks & 1) { + rme96->wcreg |= RME96_WCR_MONITOR_0; + } else { + rme96->wcreg &= ~RME96_WCR_MONITOR_0; + } + if (montracks & 2) { + rme96->wcreg |= RME96_WCR_MONITOR_1; + } else { + rme96->wcreg &= ~RME96_WCR_MONITOR_1; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_getattenuation(rme96_t *rme96) +{ + return ((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_1) & 1) << 1); +} + +static int +snd_rme96_setattenuation(rme96_t *rme96, + int attenuation) +{ + switch (attenuation) { + case 0: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) & + ~RME96_WCR_GAIN_1; + break; + case 1: + rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) & + ~RME96_WCR_GAIN_1; + break; + case 2: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) | + RME96_WCR_GAIN_1; + break; + case 3: + rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) | + RME96_WCR_GAIN_1; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_capture_getrate(rme96_t *rme96, + int *is_adat) +{ + int n, rate; + + *is_adat = 0; + if (rme96->areg & RME96_AR_ANALOG) { + /* Analog input, overrides S/PDIF setting */ + n = ((rme96->areg >> RME96_AR_BITPOS_F0) & 1) + + (((rme96->areg >> RME96_AR_BITPOS_F1) & 1) << 1); + switch (n) { + case 1: + rate = 32000; + break; + case 2: + rate = 44100; + break; + case 3: + rate = 48000; + break; + default: + return -1; + } + return (rme96->areg & RME96_AR_BITPOS_F2) ? rate << 1 : rate; + } + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_LOCK) { + /* ADAT rate */ + *is_adat = 1; + if (rme96->rcreg & RME96_RCR_T_OUT) { + return 48000; + } + return 44100; + } + + if (rme96->rcreg & RME96_RCR_VERF) { + return -1; + } + + /* S/PDIF rate */ + n = ((rme96->rcreg >> RME96_RCR_BITPOS_F0) & 1) + + (((rme96->rcreg >> RME96_RCR_BITPOS_F1) & 1) << 1) + + (((rme96->rcreg >> RME96_RCR_BITPOS_F2) & 1) << 2); + + switch (n) { + case 0: + if (rme96->rcreg & RME96_RCR_T_OUT) { + return 64000; + } + return -1; + case 3: return 96000; + case 4: return 88200; + case 5: return 48000; + case 6: return 44100; + case 7: return 32000; + default: + break; + } + return -1; +} + +static int +snd_rme96_playback_getrate(rme96_t *rme96) +{ + int rate, dummy; + + if (!(rme96->wcreg & RME96_WCR_MASTER) && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + return rate; + } + rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1); + switch (rate) { + case 1: + rate = 32000; + break; + case 2: + rate = 44100; + break; + case 3: + rate = 48000; + break; + default: + return -1; + } + return (rme96->wcreg & RME96_WCR_DS) ? rate << 1 : rate; +} + +static int +snd_rme96_playback_setrate(rme96_t *rme96, + int rate) +{ + int ds; + + ds = rme96->wcreg & RME96_WCR_DS; + switch (rate) { + case 32000: + rme96->wcreg &= ~RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) & + ~RME96_WCR_FREQ_1; + break; + case 44100: + rme96->wcreg &= ~RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) & + ~RME96_WCR_FREQ_0; + break; + case 48000: + rme96->wcreg &= ~RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) | + RME96_WCR_FREQ_1; + break; + case 64000: + rme96->wcreg |= RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) & + ~RME96_WCR_FREQ_1; + break; + case 88200: + rme96->wcreg |= RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) & + ~RME96_WCR_FREQ_0; + break; + case 96000: + rme96->wcreg |= RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) | + RME96_WCR_FREQ_1; + break; + default: + return -EINVAL; + } + if ((!ds && rme96->wcreg & RME96_WCR_DS) || + (ds && !(rme96->wcreg & RME96_WCR_DS))) + { + /* change to/from double-speed: reset the DAC (if available) */ + snd_rme96_reset_dac(rme96); + } else { + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + } + return 0; +} + +static int +snd_rme96_capture_analog_setrate(rme96_t *rme96, + int rate) +{ + switch (rate) { + case 32000: + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) & + ~RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; + break; + case 44100: + rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; + break; + case 48000: + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; + break; + case 64000: + if (rme96->rev < 4) { + return -EINVAL; + } + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) & + ~RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; + break; + case 88200: + if (rme96->rev < 4) { + return -EINVAL; + } + rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; + break; + case 96000: + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; + break; + default: + return -EINVAL; + } + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + return 0; +} + +static int +snd_rme96_setclockmode(rme96_t *rme96, + int mode) +{ + switch (mode) { + case RME96_CLOCKMODE_SLAVE: + rme96->wcreg &= ~RME96_WCR_MASTER; + rme96->areg &= ~RME96_AR_WSEL; + break; + case RME96_CLOCKMODE_MASTER: + rme96->wcreg |= RME96_WCR_MASTER; + rme96->areg &= ~RME96_AR_WSEL; + break; + case RME96_CLOCKMODE_WORDCLOCK: + /* Word clock is a master mode */ + rme96->wcreg |= RME96_WCR_MASTER; + rme96->areg |= RME96_AR_WSEL; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + return 0; +} + +static int +snd_rme96_getclockmode(rme96_t *rme96) +{ + if (rme96->areg & RME96_AR_WSEL) { + return RME96_CLOCKMODE_WORDCLOCK; + } + return (rme96->wcreg & RME96_WCR_MASTER) ? RME96_CLOCKMODE_MASTER : + RME96_CLOCKMODE_SLAVE; +} + +static int +snd_rme96_setinputtype(rme96_t *rme96, + int type) +{ + int n; + + switch (type) { + case RME96_INPUT_OPTICAL: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) & + ~RME96_WCR_INP_1; + break; + case RME96_INPUT_COAXIAL: + rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) & + ~RME96_WCR_INP_1; + break; + case RME96_INPUT_INTERNAL: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) | + RME96_WCR_INP_1; + break; + case RME96_INPUT_XLR: + if ((rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && + rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PRO) || + (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && + rme96->rev > 4)) + { + /* Only Digi96/8 PRO and Digi96/8 PAD supports XLR */ + return -EINVAL; + } + rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) | + RME96_WCR_INP_1; + break; + case RME96_INPUT_ANALOG: + if (!RME96_HAS_ANALOG_IN(rme96)) { + return -EINVAL; + } + rme96->areg |= RME96_AR_ANALOG; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + if (rme96->rev < 4) { + /* + * Revision less than 004 does not support 64 and + * 88.2 kHz + */ + if (snd_rme96_capture_getrate(rme96, &n) == 88200) { + snd_rme96_capture_analog_setrate(rme96, 44100); + } + if (snd_rme96_capture_getrate(rme96, &n) == 64000) { + snd_rme96_capture_analog_setrate(rme96, 32000); + } + } + return 0; + default: + return -EINVAL; + } + if (type != RME96_INPUT_ANALOG && RME96_HAS_ANALOG_IN(rme96)) { + rme96->areg &= ~RME96_AR_ANALOG; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_getinputtype(rme96_t *rme96) +{ + if (rme96->areg & RME96_AR_ANALOG) { + return RME96_INPUT_ANALOG; + } + return ((rme96->wcreg >> RME96_WCR_BITPOS_INP_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_INP_1) & 1) << 1); +} + +static void +snd_rme96_setframelog(rme96_t *rme96, + int n_channels, + int is_playback) +{ + int frlog; + + if (n_channels == 2) { + frlog = 1; + } else { + /* assume 8 channels */ + frlog = 3; + } + if (is_playback) { + frlog += (rme96->wcreg & RME96_WCR_MODE24) ? 2 : 1; + rme96->playback_frlog = frlog; + } else { + frlog += (rme96->wcreg & RME96_WCR_MODE24_2) ? 2 : 1; + rme96->capture_frlog = frlog; + } +} + +static int +snd_rme96_playback_setformat(rme96_t *rme96, + int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + rme96->wcreg &= ~RME96_WCR_MODE24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rme96->wcreg |= RME96_WCR_MODE24; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_capture_setformat(rme96_t *rme96, + int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + rme96->wcreg &= ~RME96_WCR_MODE24_2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rme96->wcreg |= RME96_WCR_MODE24_2; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static void +snd_rme96_set_period_properties(rme96_t *rme96, + size_t period_bytes) +{ + switch (period_bytes) { + case RME96_LARGE_BLOCK_SIZE: + rme96->wcreg &= ~RME96_WCR_ISEL; + break; + case RME96_SMALL_BLOCK_SIZE: + rme96->wcreg |= RME96_WCR_ISEL; + break; + default: + snd_BUG(); + break; + } + rme96->wcreg &= ~RME96_WCR_IDIS; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static int +snd_rme96_playback_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) + return err; + spin_lock_irqsave(&rme96->lock, flags); + if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + snd_rme96_setframelog(rme96, params_channels(params), 1); + if (rme96->capture_periodsize != 0) { + if (params_period_size(params) << rme96->playback_frlog != + rme96->capture_periodsize) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + } + rme96->playback_periodsize = + params_period_size(params) << rme96->playback_frlog; + snd_rme96_set_period_properties(rme96, rme96->playback_periodsize); + /* S/PDIF setup */ + if ((rme96->wcreg & RME96_WCR_ADAT) == 0) { + rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP); + writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER); + } + spin_unlock_irqrestore(&rme96->lock, flags); + + return 0; +} + +static int +snd_rme96_playback_hw_free(snd_pcm_substream_t *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int +snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + int err, isadat; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) + return err; + spin_lock_irqsave(&rme96->lock, flags); + if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { + if ((err = snd_rme96_capture_analog_setrate(rme96, + params_rate(params))) < 0) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + } else if (params_rate(params) != snd_rme96_capture_getrate(rme96, &isadat)) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + snd_rme96_setframelog(rme96, params_channels(params), 0); + if (rme96->playback_periodsize != 0) { + if (params_period_size(params) << rme96->capture_frlog != + rme96->playback_periodsize) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + } + rme96->capture_periodsize = + params_period_size(params) << rme96->capture_frlog; + snd_rme96_set_period_properties(rme96, rme96->capture_periodsize); + spin_unlock_irqrestore(&rme96->lock, flags); + + return 0; +} + +static int +snd_rme96_capture_hw_free(snd_pcm_substream_t *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static void +snd_rme96_playback_start(rme96_t *rme96, + int from_pause) +{ + if (!from_pause) { + writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); + rme96->playback_last_appl_ptr = 0; + rme96->playback_ptr = 0; + } + + rme96->wcreg |= RME96_WCR_START; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_capture_start(rme96_t *rme96, + int from_pause) +{ + if (!from_pause) { + writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); + rme96->capture_ptr = 0; + } + + rme96->wcreg |= RME96_WCR_START_2; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_playback_stop(rme96_t *rme96) +{ + /* + * Check if there is an unconfirmed IRQ, if so confirm it, or else + * the hardware will not stop generating interrupts + */ + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_IRQ) { + writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); + } + rme96->wcreg &= ~RME96_WCR_START; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_capture_stop(rme96_t *rme96) +{ + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_IRQ_2) { + writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); + } + rme96->wcreg &= ~RME96_WCR_START_2; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_interrupt(int irq, + void *dev_id, + struct pt_regs *regs) +{ + rme96_t *rme96 = (rme96_t *)dev_id; + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + /* fastpath out, to ease interrupt sharing */ + if (!((rme96->rcreg & RME96_RCR_IRQ) || + (rme96->rcreg & RME96_RCR_IRQ_2))) + { + return; + } + + if (rme96->rcreg & RME96_RCR_IRQ) { + /* playback */ + snd_pcm_period_elapsed(rme96->playback_substream); + writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); + } + if (rme96->rcreg & RME96_RCR_IRQ_2) { + /* capture */ + snd_pcm_period_elapsed(rme96->capture_substream); + writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); + } +} + +static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE }; + +#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = { + count: PERIOD_BYTES, + list: period_bytes, + mask: 0 +}; + +static int +snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme96->lock, flags); + rme96->wcreg &= ~RME96_WCR_ADAT; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + rme96->playback_substream = substream; + rme96->playback_last_appl_ptr = 0; + rme96->playback_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + runtime->hw = snd_rme96_playback_spdif_info; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + + rme96->wcreg_spdif_stream = rme96->wcreg_spdif; + rme96->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id); + return 0; +} + +static int +snd_rme96_capture_spdif_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + int isadat; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (snd_rme96_capture_getrate(rme96, &isadat) < 0) { + /* no input */ + return -EIO; + } + if (isadat) { + /* ADAT input */ + return -EBUSY; + } + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme96->lock, flags); + rme96->capture_substream = substream; + rme96->capture_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + runtime->hw = snd_rme96_capture_spdif_info; + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + + return 0; +} + +static int +snd_rme96_playback_adat_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme96->lock, flags); + rme96->wcreg |= RME96_WCR_ADAT; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + rme96->playback_substream = substream; + rme96->playback_last_appl_ptr = 0; + rme96->playback_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + runtime->hw = snd_rme96_playback_adat_info; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + return 0; +} + +static int +snd_rme96_capture_adat_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + int isadat; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (snd_rme96_capture_getrate(rme96, &isadat) < 0) { + /* no input */ + return -EIO; + } + if (!isadat) { + /* S/PDIF input */ + return -EBUSY; + } + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme96->lock, flags); + rme96->capture_substream = substream; + rme96->capture_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + runtime->hw = snd_rme96_capture_adat_info; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + return 0; +} + +static int +snd_rme96_playback_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + int spdif = 0; + + spin_lock_irqsave(&rme96->lock, flags); + rme96->playback_substream = NULL; + rme96->playback_periodsize = 0; + spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0; + spin_unlock_irqrestore(&rme96->lock, flags); + if (spdif) { + rme96->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id); + } + return 0; +} + +static int +snd_rme96_capture_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + + spin_lock_irqsave(&rme96->lock, flags); + rme96->capture_substream = NULL; + rme96->capture_periodsize = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} + +static int +snd_rme96_playback_prepare(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISPLAYING(rme96)) { + snd_rme96_playback_stop(rme96); + } + writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} + +static int +snd_rme96_capture_prepare(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISRECORDING(rme96)) { + snd_rme96_capture_stop(rme96); + } + writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} + +static int +snd_rme96_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!RME96_ISPLAYING(rme96)) { + if (substream != rme96->playback_substream) { + return -EBUSY; + } + snd_rme96_playback_start(rme96, 0); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + if (RME96_ISPLAYING(rme96)) { + if (substream != rme96->playback_substream) { + return -EBUSY; + } + snd_rme96_playback_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (RME96_ISPLAYING(rme96)) { + snd_rme96_playback_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!RME96_ISPLAYING(rme96)) { + snd_rme96_playback_start(rme96, 1); + } + break; + + default: + return -EINVAL; + } + return 0; +} + +static int +snd_rme96_capture_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!RME96_ISRECORDING(rme96)) { + if (substream != rme96->capture_substream) { + return -EBUSY; + } + snd_rme96_capture_start(rme96, 0); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + if (RME96_ISRECORDING(rme96)) { + if (substream != rme96->capture_substream) { + return -EBUSY; + } + snd_rme96_capture_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (RME96_ISRECORDING(rme96)) { + snd_rme96_capture_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!RME96_ISRECORDING(rme96)) { + snd_rme96_capture_start(rme96, 1); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t +snd_rme96_playback_pointer(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t diff; + size_t bytes; + + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + diff = runtime->control->appl_ptr - + rme96->playback_last_appl_ptr; + rme96->playback_last_appl_ptr = runtime->control->appl_ptr; + if (diff != 0 && + diff < -(snd_pcm_sframes_t)(runtime->boundary >> 1)) + { + diff += runtime->boundary; + } + bytes = diff << rme96->playback_frlog; + + if (bytes > RME96_BUFFER_SIZE - rme96->playback_ptr) { + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + + rme96->playback_ptr, + runtime->dma_area + rme96->playback_ptr, + RME96_BUFFER_SIZE - rme96->playback_ptr); + bytes -= RME96_BUFFER_SIZE - rme96->playback_ptr; + if (bytes > RME96_BUFFER_SIZE) { + bytes = RME96_BUFFER_SIZE; + } + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER, + runtime->dma_area, + bytes); + rme96->playback_ptr = bytes; + } else if (bytes != 0) { + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + + rme96->playback_ptr, + runtime->dma_area + rme96->playback_ptr, + bytes); + rme96->playback_ptr += bytes; + } + } + return snd_rme96_playback_ptr(rme96); +} + +static snd_pcm_uframes_t +snd_rme96_capture_pointer(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t frameptr; + size_t ptr; + + frameptr = snd_rme96_capture_ptr(rme96); + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + ptr = frameptr << rme96->capture_frlog; + if (ptr > rme96->capture_ptr) { + memcpy_fromio(runtime->dma_area + rme96->capture_ptr, + rme96->iobase + RME96_IO_REC_BUFFER + + rme96->capture_ptr, + ptr - rme96->capture_ptr); + rme96->capture_ptr += ptr - rme96->capture_ptr; + } else if (ptr < rme96->capture_ptr) { + memcpy_fromio(runtime->dma_area + rme96->capture_ptr, + rme96->iobase + RME96_IO_REC_BUFFER + + rme96->capture_ptr, + RME96_BUFFER_SIZE - rme96->capture_ptr); + memcpy_fromio(runtime->dma_area, + rme96->iobase + RME96_IO_REC_BUFFER, + ptr); + rme96->capture_ptr = ptr; + } + } + return frameptr; +} + +static snd_pcm_ops_t snd_rme96_playback_spdif_ops = { + open: snd_rme96_playback_spdif_open, + close: snd_rme96_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_rme96_playback_hw_params, + hw_free: snd_rme96_playback_hw_free, + prepare: snd_rme96_playback_prepare, + trigger: snd_rme96_playback_trigger, + pointer: snd_rme96_playback_pointer, + copy: snd_rme96_playback_copy, + silence: snd_rme96_playback_silence, +}; + +static snd_pcm_ops_t snd_rme96_capture_spdif_ops = { + open: snd_rme96_capture_spdif_open, + close: snd_rme96_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_rme96_capture_hw_params, + hw_free: snd_rme96_capture_hw_free, + prepare: snd_rme96_capture_prepare, + trigger: snd_rme96_capture_trigger, + pointer: snd_rme96_capture_pointer, + copy: snd_rme96_capture_copy, +}; + +static snd_pcm_ops_t snd_rme96_playback_adat_ops = { + open: snd_rme96_playback_adat_open, + close: snd_rme96_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_rme96_playback_hw_params, + hw_free: snd_rme96_playback_hw_free, + prepare: snd_rme96_playback_prepare, + trigger: snd_rme96_playback_trigger, + pointer: snd_rme96_playback_pointer, + copy: snd_rme96_playback_copy, + silence: snd_rme96_playback_silence, +}; + +static snd_pcm_ops_t snd_rme96_capture_adat_ops = { + open: snd_rme96_capture_adat_open, + close: snd_rme96_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_rme96_capture_hw_params, + hw_free: snd_rme96_capture_hw_free, + prepare: snd_rme96_capture_prepare, + trigger: snd_rme96_capture_trigger, + pointer: snd_rme96_capture_pointer, + copy: snd_rme96_capture_copy, +}; + +static void +snd_rme96_free(void *private_data) +{ + rme96_t *rme96 = (rme96_t *)private_data; + + if (rme96 == NULL) { + return; + } + if (rme96->irq >= 0) { + snd_rme96_playback_stop(rme96); + snd_rme96_capture_stop(rme96); + rme96->areg &= ~RME96_AR_DAC_EN; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + snd_rme96_proc_done(rme96); + free_irq(rme96->irq, (void *)rme96); + rme96->irq = -1; + } + if (rme96->iobase) { + iounmap((void *)rme96->iobase); + rme96->iobase = 0; + } + if (rme96->res_port != NULL) { + release_resource(rme96->res_port); + kfree_nocheck(rme96->res_port); + rme96->res_port = NULL; + } +} + +static void +snd_rme96_free_spdif_pcm(snd_pcm_t *pcm) +{ + rme96_t *rme96 = (rme96_t *) pcm->private_data; + rme96->spdif_pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void +snd_rme96_free_adat_pcm(snd_pcm_t *pcm) +{ + rme96_t *rme96 = (rme96_t *) pcm->private_data; + rme96->adat_pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __init +snd_rme96_create(rme96_t *rme96) +{ + struct pci_dev *pci = rme96->pci; + int err; + + rme96->irq = -1; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + rme96->port = pci_resource_start(rme96->pci, 0); + + if ((rme96->res_port = request_mem_region(rme96->port, RME96_IO_SIZE, "RME96")) == NULL) { + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + rme96->irq = pci->irq; + + spin_lock_init(&rme96->lock); + if ((rme96->iobase = (unsigned long) ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) { + snd_printk("unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); + return -ENOMEM; + } + + /* read the card's revision number */ + pci_read_config_byte(pci, 8, &rme96->rev); + + /* set up ALSA pcm device for S/PDIF */ + if ((err = snd_pcm_new(rme96->card, "Digi96 IEC958", 0, + 1, 1, &rme96->spdif_pcm)) < 0) + { + return err; + } + rme96->spdif_pcm->private_data = rme96; + rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm; + strcpy(rme96->spdif_pcm->name, "Digi96 IEC958"); + snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops); + snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops); + + rme96->spdif_pcm->info_flags = 0; + + snd_pcm_lib_preallocate_pages_for_all(rme96->spdif_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL); + + /* set up ALSA pcm device for ADAT */ + if (pci->device == PCI_DEVICE_ID_DIGI96) { + /* ADAT is not available on the base model */ + rme96->adat_pcm = NULL; + } else { + if ((err = snd_pcm_new(rme96->card, "Digi96 ADAT", 1, + 1, 1, &rme96->adat_pcm)) < 0) + { + return err; + } + rme96->adat_pcm->private_data = rme96; + rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm; + strcpy(rme96->adat_pcm->name, "Digi96 ADAT"); + snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops); + snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops); + + rme96->adat_pcm->info_flags = 0; + + snd_pcm_lib_preallocate_pages_for_all(rme96->adat_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL); + } + + rme96->playback_periodsize = 0; + rme96->capture_periodsize = 0; + + /* make sure playback/capture is stopped, if by some reason active */ + snd_rme96_playback_stop(rme96); + snd_rme96_capture_stop(rme96); + + /* set default values in registers */ + rme96->wcreg = + RME96_WCR_FREQ_1 | /* set 44.1 kHz playback */ + RME96_WCR_SEL | /* normal playback */ + RME96_WCR_MASTER | /* set to master clock mode */ + RME96_WCR_INP_0; /* set coaxial input */ + + rme96->areg = RME96_AR_FREQPAD_1; /* set 44.1 kHz analog capture */ + + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset the ADC */ + writel(rme96->areg | RME96_AR_PD2, + rme96->iobase + RME96_IO_ADDITIONAL_REG); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset and enable the DAC (order is important). */ + snd_rme96_reset_dac(rme96); + rme96->areg |= RME96_AR_DAC_EN; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset playback and record buffer pointers */ + writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); + writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); + + /* reset volume */ + rme96->vol[0] = rme96->vol[1] = 0; + if (RME96_HAS_ANALOG_OUT(rme96)) { + snd_rme96_apply_dac_volume(rme96); + } + + /* init switch interface */ + if ((err = snd_rme96_create_switches(rme96->card, rme96)) < 0) { + return err; + } + + /* init proc interface */ + snd_rme96_proc_init(rme96); + + return 0; +} + +/* + * proc interface + */ + +static void +snd_rme96_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + int n; + rme96_t *rme96 = (rme96_t *)entry->private_data; + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + + snd_iprintf(buffer, rme96->card->longname); + snd_iprintf(buffer, " (index #%d)\n", rme96->card->number + 1); + + snd_iprintf(buffer, "\nGeneral settings\n"); + if (rme96->wcreg & RME96_WCR_IDIS) { + snd_iprintf(buffer, " period size: N/A (interrupts " + "disabled)\n"); + } else if (rme96->wcreg & RME96_WCR_ISEL) { + snd_iprintf(buffer, " period size: 2048 bytes\n"); + } else { + snd_iprintf(buffer, " period size: 8192 bytes\n"); + } + snd_iprintf(buffer, "\nInput settings\n"); + switch (snd_rme96_getinputtype(rme96)) { + case RME96_INPUT_OPTICAL: + snd_iprintf(buffer, " input: optical"); + break; + case RME96_INPUT_COAXIAL: + snd_iprintf(buffer, " input: coaxial"); + break; + case RME96_INPUT_INTERNAL: + snd_iprintf(buffer, " input: internal"); + break; + case RME96_INPUT_XLR: + snd_iprintf(buffer, " input: XLR"); + break; + case RME96_INPUT_ANALOG: + snd_iprintf(buffer, " input: analog"); + break; + } + if (snd_rme96_capture_getrate(rme96, &n) < 0) { + snd_iprintf(buffer, "\n sample rate: no valid signal\n"); + } else { + if (n) { + snd_iprintf(buffer, " (8 channels)\n"); + } else { + snd_iprintf(buffer, " (2 channels)\n"); + } + snd_iprintf(buffer, " sample rate: %d Hz\n", + snd_rme96_capture_getrate(rme96, &n)); + } + if (rme96->wcreg & RME96_WCR_MODE24_2) { + snd_iprintf(buffer, " sample format: 24 bit\n"); + } else { + snd_iprintf(buffer, " sample format: 16 bit\n"); + } + + snd_iprintf(buffer, "\nOutput settings\n"); + if (rme96->wcreg & RME96_WCR_SEL) { + snd_iprintf(buffer, " output signal: normal playback\n"); + } else { + snd_iprintf(buffer, " output signal: same as input\n"); + } + snd_iprintf(buffer, " sample rate: %d Hz\n", + snd_rme96_playback_getrate(rme96)); + if (rme96->wcreg & RME96_WCR_MODE24) { + snd_iprintf(buffer, " sample format: 24 bit\n"); + } else { + snd_iprintf(buffer, " sample format: 16 bit\n"); + } + if (rme96->areg & RME96_AR_WSEL) { + snd_iprintf(buffer, " clock mode: word clock\n"); + } else if (rme96->wcreg & RME96_WCR_MASTER) { + snd_iprintf(buffer, " clock mode: master\n"); + } else { + snd_iprintf(buffer, " clock mode: slave\n"); + } + if (rme96->wcreg & RME96_WCR_PRO) { + snd_iprintf(buffer, " format: AES/EBU (professional)\n"); + } else { + snd_iprintf(buffer, " format: IEC958 (consumer)\n"); + } + if (rme96->wcreg & RME96_WCR_EMP) { + snd_iprintf(buffer, " emphasis: on\n"); + } else { + snd_iprintf(buffer, " emphasis: off\n"); + } + if (rme96->wcreg & RME96_WCR_DOLBY) { + snd_iprintf(buffer, " non-audio (dolby): on\n"); + } else { + snd_iprintf(buffer, " non-audio (dolby): off\n"); + } + if (RME96_HAS_ANALOG_IN(rme96)) { + snd_iprintf(buffer, "\nAnalog output settings\n"); + switch (snd_rme96_getmontracks(rme96)) { + case RME96_MONITOR_TRACKS_1_2: + snd_iprintf(buffer, " monitored ADAT tracks: 1+2\n"); + break; + case RME96_MONITOR_TRACKS_3_4: + snd_iprintf(buffer, " monitored ADAT tracks: 3+4\n"); + break; + case RME96_MONITOR_TRACKS_5_6: + snd_iprintf(buffer, " monitored ADAT tracks: 5+6\n"); + break; + case RME96_MONITOR_TRACKS_7_8: + snd_iprintf(buffer, " monitored ADAT tracks: 7+8\n"); + break; + } + switch (snd_rme96_getattenuation(rme96)) { + case RME96_ATTENUATION_0: + snd_iprintf(buffer, " attenuation: 0 dB\n"); + break; + case RME96_ATTENUATION_6: + snd_iprintf(buffer, " attenuation: -6 dB\n"); + break; + case RME96_ATTENUATION_12: + snd_iprintf(buffer, " attenuation: -12 dB\n"); + break; + case RME96_ATTENUATION_18: + snd_iprintf(buffer, " attenuation: -18 dB\n"); + break; + } + snd_iprintf(buffer, " volume left: %u\n", rme96->vol[0]); + snd_iprintf(buffer, " volume right: %u\n", rme96->vol[1]); + } +} + +static void __init +snd_rme96_proc_init(rme96_t *rme96) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(rme96->card, "rme96", rme96->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = rme96; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_rme96_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + rme96->proc_entry = entry; +} + +static void +snd_rme96_proc_done(rme96_t * rme96) +{ + if (rme96->proc_entry) { + snd_info_unregister(rme96->proc_entry); + rme96->proc_entry = NULL; + } +} + +/* + * control interface + */ + +static int +snd_rme96_info_loopback_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} +static int +snd_rme96_get_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.integer.value[0] = rme96->wcreg & RME96_WCR_SEL ? 0 : 1; + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.integer.value[0] ? 0 : RME96_WCR_SEL; + spin_lock_irqsave(&rme96->lock, flags); + val = (rme96->wcreg & ~RME96_WCR_SEL) | val; + change = val != rme96->wcreg; + writel(rme96->wcreg = val, rme96->iobase + RME96_IO_CONTROL_REGISTER); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_inputtype_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" }; + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + case PCI_DEVICE_ID_DIGI96_8: + uinfo->value.enumerated.items = 3; + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + uinfo->value.enumerated.items = 4; + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + if (rme96->rev > 4) { + /* PST */ + uinfo->value.enumerated.items = 4; + texts[3] = _texts[4]; /* Analog instead of XLR */ + } else { + /* PAD */ + uinfo->value.enumerated.items = 5; + } + break; + default: + snd_BUG(); + break; + } + if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) { + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int items = 3; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getinputtype(rme96); + + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + case PCI_DEVICE_ID_DIGI96_8: + items = 3; + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + items = 4; + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + if (rme96->rev > 4) { + /* for handling PST case, (INPUT_ANALOG is moved to INPUT_XLR */ + if (ucontrol->value.enumerated.item[0] == RME96_INPUT_ANALOG) { + ucontrol->value.enumerated.item[0] = RME96_INPUT_XLR; + } + items = 4; + } else { + items = 5; + } + break; + default: + snd_BUG(); + break; + } + if (ucontrol->value.enumerated.item[0] >= items) { + ucontrol->value.enumerated.item[0] = items - 1; + } + + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change, items = 3; + + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + case PCI_DEVICE_ID_DIGI96_8: + items = 3; + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + items = 4; + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + if (rme96->rev > 4) { + items = 4; + } else { + items = 5; + } + break; + default: + snd_BUG(); + break; + } + val = ucontrol->value.enumerated.item[0] % items; + + /* special case for PST */ + if (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && rme96->rev > 4) { + if (val == RME96_INPUT_XLR) { + val = RME96_INPUT_ANALOG; + } + } + + spin_lock_irqsave(&rme96->lock, flags); + change = val != snd_rme96_getinputtype(rme96); + snd_rme96_setinputtype(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_clockmode_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { "Slave", "Master", "Wordclock" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) { + uinfo->value.enumerated.item = 2; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getclockmode(rme96); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&rme96->lock, flags); + change = val != snd_rme96_getclockmode(rme96); + snd_rme96_setclockmode(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_attenuation_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) { + uinfo->value.enumerated.item = 3; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getattenuation(rme96); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 4; + spin_lock_irqsave(&rme96->lock, flags); + change = val != snd_rme96_getattenuation(rme96); + snd_rme96_setattenuation(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_montracks_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) { + uinfo->value.enumerated.item = 3; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getmontracks(rme96); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 4; + spin_lock_irqsave(&rme96->lock, flags); + change = val != snd_rme96_getmontracks(rme96); + snd_rme96_setmontracks(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static u32 snd_rme96_convert_from_aes(snd_aes_iec958_t *aes) +{ + u32 val = 0; + val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME96_WCR_PRO : 0; + val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME96_WCR_DOLBY : 0; + if (val & RME96_WCR_PRO) + val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME96_WCR_EMP : 0; + else + val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME96_WCR_EMP : 0; + return val; +} + +static void snd_rme96_convert_to_aes(snd_aes_iec958_t *aes, u32 val) +{ + aes->status[0] = ((val & RME96_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0) | + ((val & RME96_WCR_DOLBY) ? IEC958_AES0_NONAUDIO : 0); + if (val & RME96_WCR_PRO) + aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0; + else + aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0; +} + +static int snd_rme96_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme96_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + + snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif); + return 0; +} + +static int snd_rme96_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme96_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme96->lock, flags); + change = val != rme96->wcreg_spdif; + rme96->wcreg_spdif = val; + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int snd_rme96_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme96_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + + snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif_stream); + return 0; +} + +static int snd_rme96_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme96_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme96->lock, flags); + change = val != rme96->wcreg_spdif_stream; + rme96->wcreg_spdif_stream = val; + rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP); + writel(rme96->wcreg |= val, rme96->iobase + RME96_IO_CONTROL_REGISTER); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int snd_rme96_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme96_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = kcontrol->private_value; + return 0; +} + +static int +snd_rme96_dac_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = RME96_185X_MAX_OUT(rme96); + return 0; +} + +static int +snd_rme96_dac_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + u->value.integer.value[0] = rme96->vol[0]; + u->value.integer.value[1] = rme96->vol[1]; + spin_unlock_irqrestore(&rme96->lock, flags); + + return 0; +} + +static int +snd_rme96_dac_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + + if (!RME96_HAS_ANALOG_OUT(rme96)) { + return -EINVAL; + } + spin_lock_irqsave(&rme96->lock, flags); + if (u->value.integer.value[0] != rme96->vol[0]) { + rme96->vol[0] = u->value.integer.value[0]; + change = 1; + } + if (u->value.integer.value[1] != rme96->vol[1]) { + rme96->vol[1] = u->value.integer.value[1]; + change = 1; + } + if (change) { + snd_rme96_apply_dac_volume(rme96); + } + spin_unlock_irqrestore(&rme96->lock, flags); + + return change; +} + +static snd_kcontrol_new_t snd_rme96_controls[] = { +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_rme96_control_spdif_info, + get: snd_rme96_control_spdif_get, + put: snd_rme96_control_spdif_put +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_rme96_control_spdif_stream_info, + get: snd_rme96_control_spdif_stream_get, + put: snd_rme96_control_spdif_stream_put +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_rme96_control_spdif_mask_info, + get: snd_rme96_control_spdif_mask_get, + private_value: IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_EMPHASIS +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + info: snd_rme96_control_spdif_mask_info, + get: snd_rme96_control_spdif_mask_get, + private_value: IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_EMPHASIS +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Input Connector", + info: snd_rme96_info_inputtype_control, + get: snd_rme96_get_inputtype_control, + put: snd_rme96_put_inputtype_control +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Loopback Input", + info: snd_rme96_info_loopback_control, + get: snd_rme96_get_loopback_control, + put: snd_rme96_put_loopback_control +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Clock Mode", + info: snd_rme96_info_clockmode_control, + get: snd_rme96_get_clockmode_control, + put: snd_rme96_put_clockmode_control +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Monitor Tracks", + info: snd_rme96_info_montracks_control, + get: snd_rme96_get_montracks_control, + put: snd_rme96_put_montracks_control +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Attenuation", + info: snd_rme96_info_attenuation_control, + get: snd_rme96_get_attenuation_control, + put: snd_rme96_put_attenuation_control +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "DAC Playback Volume", + info: snd_rme96_dac_volume_info, + get: snd_rme96_dac_volume_get, + put: snd_rme96_dac_volume_put +} +}; + +static int +snd_rme96_create_switches(snd_card_t *card, + rme96_t *rme96) +{ + int idx, err; + snd_kcontrol_t *kctl; + + for (idx = 0; idx < 7; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0) + return err; + if (idx == 1) /* IEC958 (S/PDIF) Stream */ + rme96->spdif_ctl = kctl; + } + + if (RME96_HAS_ANALOG_OUT(rme96)) { + for (idx = 7; idx < 10; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0) + return err; + } + + return 0; +} + +/* + * Card initialisation + */ + +static void snd_rme96_card_free(snd_card_t *card) +{ + snd_rme96_free(card->private_data); +} + +static int __devinit +snd_rme96_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + rme96_t *rme96; + snd_card_t *card; + int err; + u8 val; + + if (dev >= SNDRV_CARDS) { + return -ENODEV; + } + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(rme96_t))) == NULL) + return -ENOMEM; + card->private_free = snd_rme96_card_free; + rme96 = (rme96_t *)card->private_data; + rme96->card = card; + rme96->pci = pci; + if ((err = snd_rme96_create(rme96)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "Digi96"); + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + strcpy(card->shortname, "RME Digi96"); + break; + case PCI_DEVICE_ID_DIGI96_8: + strcpy(card->shortname, "RME Digi96/8"); + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + strcpy(card->shortname, "RME Digi96/8 PRO"); + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + pci_read_config_byte(rme96->pci, 8, &val); + if (val < 5) { + strcpy(card->shortname, "RME Digi96/8 PAD"); + } else { + strcpy(card->shortname, "RME Digi96/8 PST"); + } + break; + } + sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, + rme96->port, rme96->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_rme96_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "RME Digi96", + id_table: snd_rme96_ids, + probe: snd_rme96_probe, + remove: __devexit_p(snd_rme96_remove), +}; + +static int __init alsa_card_rme96_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("No RME Digi96 cards found\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_rme96_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_rme96_init) +module_exit(alsa_card_rme96_exit) + +#ifndef MODULE + +/* format is: snd-rme96=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_rme96_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-rme96=", alsa_card_rme96_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/rme9652/Makefile b/sound/pci/rme9652/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/rme9652/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,24 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _rme9652.o + +list-multi := snd-rme9652-mem.o snd-rme9652.o + +export-objs := rme9652_mem.o + +snd-rme9652-mem-objs := rme9652_mem.o +snd-rme9652-objs := rme9652.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_RME9652) += snd-rme9652.o snd-rme9652-mem.o + +include $(TOPDIR)/Rules.make + +snd-rme9652-mem.o: $(snd-rme9652-mem-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rme9652-mem-objs) + +snd-rme9652.o: $(snd-rme9652-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rme9652-objs) diff -Nru a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/rme9652/rme9652.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,2770 @@ +/* + * ALSA driver for RME Digi9652 audio interfaces + * + * Copyright (c) 1999 IEM - Winfried Ritsch + * Copyright (c) 1999-2001 Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_precise_ptr[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Enable precise pointer */ + +EXPORT_NO_SYMBOLS; +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for RME Digi9652 (Hammerfall) soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for RME Digi9652 (Hammerfall) soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable/disable specific RME96{52,36} soundcards."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_precise_ptr, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_precise_ptr, "Enable precise pointer (doesn't work reliably)."); +MODULE_PARM_SYNTAX(snd_precise_ptr, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_AUTHOR("Paul Davis , Winfried Ritsch"); +MODULE_DESCRIPTION("RME Digi9652/Digi9636"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{RME,Hammerfall}," + "{RME,Hammerfall-Light}}"); + +/* The Hammerfall has two sets of 24 ADAT + 2 S/PDIF channels, one for + capture, one for playback. Both the ADAT and S/PDIF channels appear + to the host CPU in the same block of memory. There is no functional + difference between them in terms of access. + + The Hammerfall Light is identical to the Hammerfall, except that it + has 2 sets 18 channels (16 ADAT + 2 S/PDIF) for capture and playback. +*/ + +#define RME9652_NCHANNELS 26 +#define RME9636_NCHANNELS 18 + +/* Preferred sync source choices - used by "sync_pref" control switch */ + +#define RME9652_SYNC_FROM_SPDIF 0 +#define RME9652_SYNC_FROM_ADAT1 1 +#define RME9652_SYNC_FROM_ADAT2 2 +#define RME9652_SYNC_FROM_ADAT3 3 + +/* Possible sources of S/PDIF input */ + +#define RME9652_SPDIFIN_OPTICAL 0 /* optical (ADAT1) */ +#define RME9652_SPDIFIN_COAXIAL 1 /* coaxial (RCA) */ +#define RME9652_SPDIFIN_INTERN 2 /* internal (CDROM) */ + +/* ------------- Status-Register bits --------------------- */ + +#define RME9652_IRQ (1<<0) /* IRQ is High if not reset by irq_clear */ +#define RME9652_lock_2 (1<<1) /* ADAT 3-PLL: 1=locked, 0=unlocked */ +#define RME9652_lock_1 (1<<2) /* ADAT 2-PLL: 1=locked, 0=unlocked */ +#define RME9652_lock_0 (1<<3) /* ADAT 1-PLL: 1=locked, 0=unlocked */ +#define RME9652_fs48 (1<<4) /* sample rate is 0=44.1/88.2,1=48/96 Khz */ +#define RME9652_wsel_rd (1<<5) /* if Word-Clock is used and valid then 1 */ + /* bits 6-15 encode h/w buffer pointer position */ +#define RME9652_sync_2 (1<<16) /* if ADAT-IN 3 in sync to system clock */ +#define RME9652_sync_1 (1<<17) /* if ADAT-IN 2 in sync to system clock */ +#define RME9652_sync_0 (1<<18) /* if ADAT-IN 1 in sync to system clock */ +#define RME9652_DS_rd (1<<19) /* 1=Double Speed Mode, 0=Normal Speed */ +#define RME9652_tc_busy (1<<20) /* 1=time-code copy in progress (960ms) */ +#define RME9652_tc_out (1<<21) /* time-code out bit */ +#define RME9652_F_0 (1<<22) /* 000=64kHz, 100=88.2kHz, 011=96kHz */ +#define RME9652_F_1 (1<<23) /* 111=32kHz, 110=44.1kHz, 101=48kHz, */ +#define RME9652_F_2 (1<<24) /* external Crystal Chip if ERF=1 */ +#define RME9652_ERF (1<<25) /* Error-Flag of SDPIF Receiver (1=No Lock) */ +#define RME9652_buffer_id (1<<26) /* toggles by each interrupt on rec/play */ +#define RME9652_tc_valid (1<<27) /* 1 = a signal is detected on time-code input */ +#define RME9652_SPDIF_READ (1<<28) /* byte available from Rev 1.5+ S/PDIF interface */ + +#define RME9652_sync (RME9652_sync_0|RME9652_sync_1|RME9652_sync_2) +#define RME9652_lock (RME9652_lock_0|RME9652_lock_1|RME9652_lock_2) +#define RME9652_F (RME9652_F_0|RME9652_F_1|RME9652_F_2) +#define rme9652_decode_spdif_rate(x) ((x)>>22) + +/* Bit 6..15 : h/w buffer pointer */ + +#define RME9652_buf_pos 0x000FFC0 + +/* Bits 31,30,29 are bits 5,4,3 of h/w pointer position on later + Rev G EEPROMS and Rev 1.5 cards or later. +*/ + +#define RME9652_REV15_buf_pos(x) ((((x)&0xE0000000)>>26)|((x)&RME9652_buf_pos)) + +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10ee +#endif +#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL +#define PCI_DEVICE_ID_XILINX_HAMMERFALL 0x3fc4 +#endif + +/* amount of io space we remap for register access. i'm not sure we + even need this much, but 1K is nice round number :) +*/ + +#define RME9652_IO_EXTENT 1024 + +#define RME9652_init_buffer 0 +#define RME9652_play_buffer 32 /* holds ptr to 26x64kBit host RAM */ +#define RME9652_rec_buffer 36 /* holds ptr to 26x64kBit host RAM */ +#define RME9652_control_register 64 +#define RME9652_irq_clear 96 +#define RME9652_time_code 100 /* useful if used with alesis adat */ +#define RME9652_thru_base 128 /* 132...228 Thru for 26 channels */ + +/* Read-only registers */ + +/* Writing to any of the register locations writes to the status + register. We'll use the first location as our point of access. +*/ + +#define RME9652_status_register 0 + +/* --------- Control-Register Bits ---------------- */ + + +#define RME9652_start_bit (1<<0) /* start record/play */ + /* bits 1-3 encode buffersize/latency */ +#define RME9652_Master (1<<4) /* Clock Mode Master=1,Slave/Auto=0 */ +#define RME9652_IE (1<<5) /* Interupt Enable */ +#define RME9652_freq (1<<6) /* samplerate 0=44.1/88.2, 1=48/96 kHz */ +#define RME9652_freq1 (1<<7) /* if 0, 32kHz, else always 1 */ +#define RME9652_DS (1<<8) /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */ +#define RME9652_PRO (1<<9) /* S/PDIF out: 0=consumer, 1=professional */ +#define RME9652_EMP (1<<10) /* Emphasis 0=None, 1=ON */ +#define RME9652_Dolby (1<<11) /* Non-audio bit 1=set, 0=unset */ +#define RME9652_opt_out (1<<12) /* Use 1st optical OUT as SPDIF: 1=yes,0=no */ +#define RME9652_wsel (1<<13) /* use Wordclock as sync (overwrites master) */ +#define RME9652_inp_0 (1<<14) /* SPDIF-IN: 00=optical (ADAT1), */ +#define RME9652_inp_1 (1<<15) /* 01=koaxial (Cinch), 10=Internal CDROM */ +#define RME9652_SyncPref_ADAT2 (1<<16) +#define RME9652_SyncPref_ADAT3 (1<<17) +#define RME9652_SPDIF_RESET (1<<18) /* Rev 1.5+: h/w S/PDIF receiver */ +#define RME9652_SPDIF_SELECT (1<<19) +#define RME9652_SPDIF_CLOCK (1<<20) +#define RME9652_SPDIF_WRITE (1<<21) +#define RME9652_ADAT1_INTERNAL (1<<22) /* Rev 1.5+: if set, internal CD connector carries ADAT */ + +/* buffersize = 512Bytes * 2^n, where n is made from Bit2 ... Bit0 */ + +#define RME9652_latency 0x0e +#define rme9652_encode_latency(x) (((x)&0x7)<<1) +#define rme9652_decode_latency(x) (((x)>>1)&0x7) +#define rme9652_running_double_speed(s) ((s)->control_register & RME9652_DS) +#define RME9652_inp (RME9652_inp_0|RME9652_inp_1) +#define rme9652_encode_spdif_in(x) (((x)&0x3)<<14) +#define rme9652_decode_spdif_in(x) (((x)>>14)&0x3) + +#define RME9652_SyncPref_Mask (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3) +#define RME9652_SyncPref_ADAT1 0 +#define RME9652_SyncPref_SPDIF (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3) + +/* the size of a substream (1 mono data stream) */ + +#define RME9652_CHANNEL_BUFFER_SAMPLES (16*1024) +#define RME9652_CHANNEL_BUFFER_BYTES (4*RME9652_CHANNEL_BUFFER_SAMPLES) + +/* the size of the area we need to allocate for DMA transfers. the + size is the same regardless of the number of channels - the + 9636 still uses the same memory area. + + Note that we allocate 1 more channel than is apparently needed + because the h/w seems to write 1 byte beyond the end of the last + page. Sigh. +*/ + +#define RME9652_DMA_AREA_BYTES ((RME9652_NCHANNELS+1) * RME9652_CHANNEL_BUFFER_BYTES) +#define RME9652_DMA_AREA_KILOBYTES (RME9652_DMA_AREA_BYTES/1024) + +typedef struct snd_rme9652 { + int dev; + + spinlock_t lock; + int irq; + unsigned long port; + struct resource *res_port; + unsigned long iobase; + + int precise_ptr; + + u32 control_register; /* cached value */ + u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */ + + u32 creg_spdif; + u32 creg_spdif_stream; + + char *card_name; /* hammerfall or hammerfall light names */ + + size_t hw_offsetmask; /* &-with status register to get real hw_offset */ + size_t prev_hw_offset; /* previous hw offset */ + size_t max_jitter; /* maximum jitter in frames for + hw pointer */ + size_t period_bytes; /* guess what this is */ + + unsigned char ds_channels; + unsigned char ss_channels; /* different for hammerfall/hammerfall-light */ + + void *capture_buffer_unaligned; /* original buffer addresses */ + void *playback_buffer_unaligned; /* original buffer addresses */ + unsigned char *capture_buffer; /* suitably aligned address */ + unsigned char *playback_buffer; /* suitably aligned address */ + dma_addr_t capture_buffer_addr; + dma_addr_t playback_buffer_addr; + + pid_t capture_pid; + pid_t playback_pid; + + snd_pcm_substream_t *capture_substream; + snd_pcm_substream_t *playback_substream; + int running; + + int passthru; /* non-zero if doing pass-thru */ + int hw_rev; /* h/w rev * 10 (i.e. 1.5 has hw_rev = 15) */ + + int last_spdif_sample_rate; /* so that we can catch externally ... */ + int last_adat_sample_rate; /* ... induced rate changes */ + + char *channel_map; + + snd_card_t *card; + snd_pcm_t *pcm; + struct pci_dev *pci; + snd_info_entry_t *proc_entry; + snd_kcontrol_t *spdif_ctl; + +} rme9652_t; + +/* These tables map the ALSA channels 1..N to the channels that we + need to use in order to find the relevant channel buffer. RME + refer to this kind of mapping as between "the ADAT channel and + the DMA channel." We index it using the logical audio channel, + and the value is the DMA channel (i.e. channel buffer number) + where the data for that channel can be read/written from/to. +*/ + +static char channel_map_9652_ss[26] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25 +}; + +static char channel_map_9636_ss[26] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + /* channels 16 and 17 are S/PDIF */ + 24, 25, + /* channels 18-25 don't exist */ + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_9652_ds[26] = { + /* ADAT channels are remapped */ + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, + /* channels 12 and 13 are S/PDIF */ + 24, 25, + /* others don't exist */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_9636_ds[26] = { + /* ADAT channels are remapped */ + 1, 3, 5, 7, 9, 11, 13, 15, + /* channels 8 and 9 are S/PDIF */ + 24, 25 + /* others don't exist */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +#define RME9652_PREALLOCATE_MEMORY /* via module snd-rme9652_mem */ + +#ifdef RME9652_PREALLOCATE_MEMORY +extern void *snd_rme9652_get_buffer(int card, dma_addr_t *dmaaddr); +extern void snd_rme9652_free_buffer(int card, void *ptr); +#endif + +static struct pci_device_id snd_rme9652_ids[] __devinitdata = { + {0x10ee, 0x3fc4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, /* RME Digi9652 */ + {0,} +}; + +MODULE_DEVICE_TABLE(pci, snd_rme9652_ids); + +static inline void rme9652_write(rme9652_t *rme9652, int reg, int val) +{ + writel(val, rme9652->iobase + reg); +} + +static inline unsigned int rme9652_read(rme9652_t *rme9652, int reg) +{ + return readl(rme9652->iobase + reg); +} + +static inline int snd_rme9652_use_is_exclusive(rme9652_t *rme9652) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&rme9652->lock, flags); + if ((rme9652->playback_pid != rme9652->capture_pid) && + (rme9652->playback_pid >= 0) && (rme9652->capture_pid >= 0)) { + ret = 0; + } + spin_unlock_irqrestore(&rme9652->lock, flags); + return ret; +} + +static inline int rme9652_adat_sample_rate(rme9652_t *rme9652) +{ + if (rme9652_running_double_speed(rme9652)) { + return (rme9652_read(rme9652, RME9652_status_register) & + RME9652_fs48) ? 96000 : 88200; + } else { + return (rme9652_read(rme9652, RME9652_status_register) & + RME9652_fs48) ? 48000 : 44100; + } +} + +static inline void rme9652_compute_period_size(rme9652_t *rme9652) +{ + unsigned int i; + + i = rme9652->control_register & RME9652_latency; + rme9652->period_bytes = 1 << ((rme9652_decode_latency(i) + 8)); + rme9652->hw_offsetmask = + (rme9652->period_bytes * 2 - 1) & RME9652_buf_pos; + rme9652->max_jitter = 80; +} + +static snd_pcm_uframes_t rme9652_hw_pointer(rme9652_t *rme9652) +{ + int status; + int offset, frag; + snd_pcm_uframes_t period_size = rme9652->period_bytes / 4; + snd_pcm_sframes_t delta; + + status = rme9652_read(rme9652, RME9652_status_register); + if (!rme9652->precise_ptr) + return (status & RME9652_buffer_id) ? period_size : 0; + offset = status & RME9652_buf_pos; + + /* The hardware may give a backward movement for up to 80 frames + Martin Kirst knows the details. + */ + + delta = rme9652->prev_hw_offset - offset; + delta &= 0xffff; + if (delta <= rme9652->max_jitter * 4) + offset = rme9652->prev_hw_offset; + else + rme9652->prev_hw_offset = offset; + offset &= rme9652->hw_offsetmask; + offset /= 4; + frag = status & RME9652_buffer_id; + + if (offset < period_size) { + if (offset > rme9652->max_jitter) { + if (frag) + printk("Unexpected hw_pointer position (bufid == 0): status: %x offset: %d\n", status, offset); + } else if (!frag) + return 0; + offset -= rme9652->max_jitter; + if (offset < 0) + offset += period_size * 2; + } else { + if (offset > period_size + rme9652->max_jitter) { + if (!frag) + printk("Unexpected hw_pointer position (bufid == 1): status: %x offset: %d\n", status, offset); + } else if (frag) + return period_size; + offset -= rme9652->max_jitter; + } + + return offset; +} + +static inline void rme9652_reset_hw_pointer(rme9652_t *rme9652) +{ + int i; + + /* reset the FIFO pointer to zero. We do this by writing to 8 + registers, each of which is a 32bit wide register, and set + them all to zero. Note that s->iobase is a pointer to + int32, not pointer to char. + */ + + for (i = 0; i < 8; i++) { + rme9652_write(rme9652, i * 4, 0); + udelay(10); + } + rme9652->prev_hw_offset = 0; +} + +static inline void rme9652_start(rme9652_t *s) +{ + s->control_register |= (RME9652_IE | RME9652_start_bit); + rme9652_write(s, RME9652_control_register, s->control_register); +} + +static inline void rme9652_stop(rme9652_t *s) +{ + s->control_register &= ~(RME9652_start_bit | RME9652_IE); + rme9652_write(s, RME9652_control_register, s->control_register); +} + +static int rme9652_set_interrupt_interval(rme9652_t *s, + unsigned int frames) +{ + int restart = 0; + int n; + + spin_lock_irq(&s->lock); + + if ((restart = s->running)) { + rme9652_stop(s); + } + + frames >>= 7; + n = 0; + while (frames) { + n++; + frames >>= 1; + } + + s->control_register &= ~RME9652_latency; + s->control_register |= rme9652_encode_latency(n); + + rme9652_write(s, RME9652_control_register, s->control_register); + + rme9652_compute_period_size(s); + + if (restart) + rme9652_start(s); + + spin_unlock_irq(&s->lock); + + return 0; +} + +static int rme9652_set_rate(rme9652_t *rme9652, int rate) +{ + int restart; + int reject_if_open = 0; + int xrate; + + /* Changing from a "single speed" to a "double speed" rate is + not allowed if any substreams are open. This is because + such a change causes a shift in the location of + the DMA buffers and a reduction in the number of available + buffers. + + Note that a similar but essentially insoluble problem + exists for externally-driven rate changes. All we can do + is to flag rate changes in the read/write routines. + */ + + spin_lock_irq(&rme9652->lock); + xrate = rme9652_adat_sample_rate(rme9652); + + switch (rate) { + case 44100: + if (xrate > 48000) { + reject_if_open = 1; + } + rate = 0; + break; + case 48000: + if (xrate > 48000) { + reject_if_open = 1; + } + rate = RME9652_freq; + break; + case 88200: + if (xrate < 48000) { + reject_if_open = 1; + } + rate = RME9652_DS; + break; + case 96000: + if (xrate < 48000) { + reject_if_open = 1; + } + rate = RME9652_DS | RME9652_freq; + break; + default: + return -EINVAL; + } + + if (reject_if_open && + (rme9652->capture_pid >= 0 || rme9652->playback_pid >= 0)) { + spin_unlock_irq(&rme9652->lock); + return -EBUSY; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652->control_register &= ~(RME9652_freq | RME9652_DS); + rme9652->control_register |= rate; + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + if (rate & RME9652_DS) { + if (rme9652->ss_channels == RME9652_NCHANNELS) { + rme9652->channel_map = channel_map_9652_ds; + } else { + rme9652->channel_map = channel_map_9636_ds; + } + } else { + if (rme9652->ss_channels == RME9652_NCHANNELS) { + rme9652->channel_map = channel_map_9652_ss; + } else { + rme9652->channel_map = channel_map_9636_ss; + } + } + + spin_unlock_irq(&rme9652->lock); + return 0; +} + +static void rme9652_set_thru(rme9652_t *rme9652, int channel, int enable) +{ + int i; + + rme9652->passthru = 0; + + if (channel < 0) { + + /* set thru for all channels */ + + if (enable) { + for (i = 0; i < RME9652_NCHANNELS; i++) { + rme9652->thru_bits |= (1 << i); + rme9652_write(rme9652, RME9652_thru_base + i * 4, 1); + } + } else { + for (i = 0; i < RME9652_NCHANNELS; i++) { + rme9652->thru_bits &= ~(1 << i); + rme9652_write(rme9652, RME9652_thru_base + i * 4, 0); + } + } + + } else { + int mapped_channel; + + snd_assert(channel == RME9652_NCHANNELS, return); + + mapped_channel = rme9652->channel_map[channel]; + + if (enable) { + rme9652->thru_bits |= (1 << mapped_channel); + } else { + rme9652->thru_bits &= ~(1 << mapped_channel); + } + + rme9652_write(rme9652, + RME9652_thru_base + mapped_channel * 4, + enable ? 1 : 0); + } +} + +static int rme9652_set_passthru(rme9652_t *rme9652, int onoff) +{ + if (onoff) { + rme9652_set_thru(rme9652, -1, 1); + + /* we don't want interrupts, so do a + custom version of rme9652_start(). + */ + + rme9652->control_register = + RME9652_inp_0 | + rme9652_encode_latency(7) | + RME9652_start_bit; + + rme9652_reset_hw_pointer(rme9652); + + rme9652_write(rme9652, RME9652_control_register, + rme9652->control_register); + rme9652->passthru = 1; + } else { + rme9652_set_thru(rme9652, -1, 0); + rme9652_stop(rme9652); + rme9652->passthru = 0; + } + + return 0; +} + +static void rme9652_spdif_set_bit (rme9652_t *rme9652, int mask, int onoff) +{ + if (onoff) + rme9652->control_register |= mask; + else + rme9652->control_register &= ~mask; + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); +} + +static void rme9652_spdif_write_byte (rme9652_t *rme9652, const int val) +{ + long mask; + long i; + + for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) { + if (val & mask) + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 1); + else + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 0); + + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0); + } +} + +static int rme9652_spdif_read_byte (rme9652_t *rme9652) +{ + long mask; + long val; + long i; + + val = 0; + + for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) { + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1); + if (rme9652_read (rme9652, RME9652_status_register) & RME9652_SPDIF_READ) + val |= mask; + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0); + } + + return val; +} + +static void rme9652_write_spdif_codec (rme9652_t *rme9652, const int address, const int data) +{ + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1); + rme9652_spdif_write_byte (rme9652, 0x20); + rme9652_spdif_write_byte (rme9652, address); + rme9652_spdif_write_byte (rme9652, data); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0); +} + + +static int rme9652_spdif_read_codec (rme9652_t *rme9652, const int address) +{ + int ret; + + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1); + rme9652_spdif_write_byte (rme9652, 0x20); + rme9652_spdif_write_byte (rme9652, address); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1); + + rme9652_spdif_write_byte (rme9652, 0x21); + ret = rme9652_spdif_read_byte (rme9652); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0); + + return ret; +} + +static void rme9652_initialize_spdif_receiver (rme9652_t *rme9652) +{ + /* XXX what unsets this ? */ + + rme9652->control_register |= RME9652_SPDIF_RESET; + + rme9652_write_spdif_codec (rme9652, 4, 0x40); + rme9652_write_spdif_codec (rme9652, 17, 0x13); + rme9652_write_spdif_codec (rme9652, 6, 0x02); +} + +static inline int rme9652_spdif_sample_rate(rme9652_t *s) +{ + unsigned int rate_bits; + + if (rme9652_read(s, RME9652_status_register) & RME9652_ERF) { + return -1; /* error condition */ + } + + if (s->hw_rev == 15) { + + int x, y, ret; + + x = rme9652_spdif_read_codec (s, 30); + + if (x != 0) + y = 48000 * 64 / x; + else + y = 0; + + if (y > 30400 && y < 33600) ret = 32000; + else if (y > 41900 && y < 46000) ret = 44100; + else if (y > 46000 && y < 50400) ret = 48000; + else if (y > 60800 && y < 67200) ret = 64000; + else if (y > 83700 && y < 92000) ret = 88200; + else if (y > 92000 && y < 100000) ret = 96000; + else ret = 0; + return ret; + } + + rate_bits = rme9652_read(s, RME9652_status_register) & RME9652_F; + + switch (rme9652_decode_spdif_rate(rate_bits)) { + case 0x7: + return 32000; + break; + + case 0x6: + return 44100; + break; + + case 0x5: + return 48000; + break; + + case 0x4: + return 88200; + break; + + case 0x3: + return 96000; + break; + + case 0x0: + return 64000; + break; + + default: + snd_printk("%s: unknown S/PDIF input rate (bits = 0x%x)\n", + s->card_name, rate_bits); + return 0; + break; + } +} + +/*----------------------------------------------------------------------------- + Control Interface + ----------------------------------------------------------------------------*/ + +static u32 snd_rme9652_convert_from_aes(snd_aes_iec958_t *aes) +{ + u32 val = 0; + val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME9652_PRO : 0; + val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME9652_Dolby : 0; + if (val & RME9652_PRO) + val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME9652_EMP : 0; + else + val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME9652_EMP : 0; + return val; +} + +static void snd_rme9652_convert_to_aes(snd_aes_iec958_t *aes, u32 val) +{ + aes->status[0] = ((val & RME9652_PRO) ? IEC958_AES0_PROFESSIONAL : 0) | + ((val & RME9652_Dolby) ? IEC958_AES0_NONAUDIO : 0); + if (val & RME9652_PRO) + aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0; + else + aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0; +} + +static int snd_rme9652_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme9652_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif); + return 0; +} + +static int snd_rme9652_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652->creg_spdif; + rme9652->creg_spdif = val; + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +static int snd_rme9652_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme9652_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif_stream); + return 0; +} + +static int snd_rme9652_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652->creg_spdif_stream; + rme9652->creg_spdif_stream = val; + rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP); + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +static int snd_rme9652_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme9652_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = kcontrol->private_value; + return 0; +} + +#define RME9652_ADAT1_IN(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_adat1_in, \ + get: snd_rme9652_get_adat1_in, \ + put: snd_rme9652_put_adat1_in } + +static unsigned int rme9652_adat1_in(rme9652_t *rme9652) +{ + if (rme9652->control_register & RME9652_ADAT1_INTERNAL) + return 1; + return 0; +} + +static int rme9652_set_adat1_input(rme9652_t *rme9652, int internal) +{ + int restart = 0; + + if (internal) { + rme9652->control_register |= RME9652_ADAT1_INTERNAL; + } else { + rme9652->control_register &= ~RME9652_ADAT1_INTERNAL; + } + + /* XXX do we actually need to stop the card when we do this ? */ + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_adat1_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[2] = {"ADAT1", "Internal"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_adat1_in(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + val = ucontrol->value.enumerated.item[0] % 2; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_adat1_in(rme9652); + if (change) + rme9652_set_adat1_input(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SPDIF_IN(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_spdif_in, \ + get: snd_rme9652_get_spdif_in, put: snd_rme9652_put_spdif_in } + +static unsigned int rme9652_spdif_in(rme9652_t *rme9652) +{ + return rme9652_decode_spdif_in(rme9652->control_register & + RME9652_inp); +} + +static int rme9652_set_spdif_input(rme9652_t *rme9652, int in) +{ + int restart = 0; + + rme9652->control_register &= ~RME9652_inp; + rme9652->control_register |= rme9652_encode_spdif_in(in); + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_spdif_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = {"ADAT1", "Coaxial", "Internal"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_spdif_in(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_spdif_in(rme9652); + if (change) + rme9652_set_spdif_input(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SPDIF_OUT(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_spdif_out, \ + get: snd_rme9652_get_spdif_out, put: snd_rme9652_put_spdif_out } + +static int rme9652_spdif_out(rme9652_t *rme9652) +{ + return (rme9652->control_register & RME9652_opt_out) ? 1 : 0; +} + +static int rme9652_set_spdif_output(rme9652_t *rme9652, int out) +{ + int restart = 0; + + if (out) { + rme9652->control_register |= RME9652_opt_out; + } else { + rme9652->control_register &= ~RME9652_opt_out; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_spdif_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.integer.value[0] = rme9652_spdif_out(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_spdif_out(rme9652); + rme9652_set_spdif_output(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SYNC_MODE(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_sync_mode, \ + get: snd_rme9652_get_sync_mode, put: snd_rme9652_put_sync_mode } + +static int rme9652_sync_mode(rme9652_t *rme9652) +{ + if (rme9652->control_register & RME9652_wsel) { + return 2; + } else if (rme9652->control_register & RME9652_Master) { + return 1; + } else { + return 0; + } +} + +static int rme9652_set_sync_mode(rme9652_t *rme9652, int mode) +{ + int restart = 0; + + switch (mode) { + case 0: + rme9652->control_register &= + ~(RME9652_Master | RME9652_wsel); + break; + case 1: + rme9652->control_register = + (rme9652->control_register & ~RME9652_wsel) | RME9652_Master; + break; + case 2: + rme9652->control_register |= + (RME9652_Master | RME9652_wsel); + break; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_sync_mode(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = {"AutoSync", "Master", "Word Clock"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_sync_mode(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_sync_mode(rme9652); + rme9652_set_sync_mode(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SYNC_PREF(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_sync_pref, \ + get: snd_rme9652_get_sync_pref, put: snd_rme9652_put_sync_pref } + +static int rme9652_sync_pref(rme9652_t *rme9652) +{ + switch (rme9652->control_register & RME9652_SyncPref_Mask) { + case RME9652_SyncPref_ADAT1: + return RME9652_SYNC_FROM_ADAT1; + case RME9652_SyncPref_ADAT2: + return RME9652_SYNC_FROM_ADAT2; + case RME9652_SyncPref_ADAT3: + return RME9652_SYNC_FROM_ADAT3; + case RME9652_SyncPref_SPDIF: + return RME9652_SYNC_FROM_SPDIF; + } + /* Not reachable */ + return 0; +} + +static int rme9652_set_sync_pref(rme9652_t *rme9652, int pref) +{ + int restart; + + rme9652->control_register &= ~RME9652_SyncPref_Mask; + switch (pref) { + case RME9652_SYNC_FROM_ADAT1: + rme9652->control_register |= RME9652_SyncPref_ADAT1; + break; + case RME9652_SYNC_FROM_ADAT2: + rme9652->control_register |= RME9652_SyncPref_ADAT2; + break; + case RME9652_SYNC_FROM_ADAT3: + rme9652->control_register |= RME9652_SyncPref_ADAT3; + break; + case RME9652_SYNC_FROM_SPDIF: + rme9652->control_register |= RME9652_SyncPref_SPDIF; + break; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_sync_pref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = {"IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"}; + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_sync_pref(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, max; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + max = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3; + val = ucontrol->value.enumerated.item[0] % max; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_sync_pref(rme9652); + rme9652_set_sync_pref(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +static int snd_rme9652_info_thru(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = rme9652->ss_channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned int k; + u32 thru_bits = rme9652->thru_bits; + + for (k = 0; k < rme9652->ss_channels; ++k) { + ucontrol->value.integer.value[k] = !!(thru_bits & (1 << k)); + } + return 0; +} + +static int snd_rme9652_put_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int chn; + u32 thru_bits = 0; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + + for (chn = 0; chn < rme9652->ss_channels; ++chn) { + if (ucontrol->value.integer.value[chn]) + thru_bits |= 1 << chn; + } + + spin_lock_irqsave(&rme9652->lock, flags); + change = thru_bits ^ rme9652->thru_bits; + if (change) { + for (chn = 0; chn < rme9652->ss_channels; ++chn) { + if (!(change & (1 << chn))) + continue; + rme9652_set_thru(rme9652,chn,thru_bits&(1<lock, flags); + return !!change; +} + +#define RME9652_PASSTHRU(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_passthru, \ + put: snd_rme9652_put_passthru, \ + get: snd_rme9652_get_passthru } + +static int snd_rme9652_info_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.integer.value[0] = rme9652->passthru; + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + int err = 0; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irqsave(&rme9652->lock, flags); + change = (ucontrol->value.integer.value[0] != rme9652->passthru); + if (change) + err = rme9652_set_passthru(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return err ? err : change; +} + +/* Read-only switches */ + +#define RME9652_SPDIF_RATE(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + info: snd_rme9652_info_spdif_rate, \ + get: snd_rme9652_get_spdif_rate } + +static int snd_rme9652_info_spdif_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 96000; + return 0; +} + +static int snd_rme9652_get_spdif_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.integer.value[0] = rme9652_spdif_sample_rate(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +#define RME9652_ADAT_SYNC(xname, xindex, xidx) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + info: snd_rme9652_info_adat_sync, \ + get: snd_rme9652_get_adat_sync, private_value: xidx } + +static int snd_rme9652_info_adat_sync(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = {"No Lock", "Lock", "No Lock Sync", "Lock Sync"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_adat_sync(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned int mask1, mask2, val; + + switch (kcontrol->private_value) { + case 0: mask1 = RME9652_lock_0; mask2 = RME9652_sync_0; break; + case 1: mask1 = RME9652_lock_1; mask2 = RME9652_sync_1; break; + case 2: mask1 = RME9652_lock_2; mask2 = RME9652_sync_2; break; + default: return -EINVAL; + } + val = rme9652_read(rme9652, RME9652_status_register); + ucontrol->value.enumerated.item[0] = (val & mask1) ? 1 : 0; + ucontrol->value.enumerated.item[0] |= (val & mask2) ? 2 : 0; + return 0; +} + +#define RME9652_TC_VALID(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + info: snd_rme9652_info_tc_valid, \ + get: snd_rme9652_get_tc_valid } + +static int snd_rme9652_info_tc_valid(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_tc_valid(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + (rme9652_read(rme9652, RME9652_status_register) & RME9652_tc_valid) ? 1 : 0; + return 0; +} + +#if ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE + +/* FIXME: this routine needs a port to the new control API --jk */ + +static int snd_rme9652_get_tc_value(void *private_data, + snd_kswitch_t *kswitch, + snd_switch_t *uswitch) +{ + rme9652_t *s = (rme9652_t *) private_data; + u32 value; + int i; + + uswitch->type = SNDRV_SW_TYPE_DWORD; + + if ((rme9652_read(s, RME9652_status_register) & + RME9652_tc_valid) == 0) { + uswitch->value.data32[0] = 0; + return 0; + } + + /* timecode request */ + + rme9652_write(s, RME9652_time_code, 0); + + /* XXX bug alert: loop-based timing !!!! */ + + for (i = 0; i < 50; i++) { + if (!(rme9652_read(s, i * 4) & RME9652_tc_busy)) + break; + } + + if (!(rme9652_read(s, i * 4) & RME9652_tc_busy)) { + return -EIO; + } + + value = 0; + + for (i = 0; i < 32; i++) { + value >>= 1; + + if (rme9652_read(s, i * 4) & RME9652_tc_out) + value |= 0x80000000; + } + + if (value > 2 * 60 * 48000) { + value -= 2 * 60 * 48000; + } else { + value = 0; + } + + uswitch->value.data32[0] = value; + + return 0; +} + +#endif /* ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE */ + +#define RME9652_CONTROLS (sizeof(snd_rme9652_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_rme9652_controls[] = { +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_rme9652_control_spdif_info, + get: snd_rme9652_control_spdif_get, + put: snd_rme9652_control_spdif_put, +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_rme9652_control_spdif_stream_info, + get: snd_rme9652_control_spdif_stream_get, + put: snd_rme9652_control_spdif_stream_put, +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_rme9652_control_spdif_mask_info, + get: snd_rme9652_control_spdif_mask_get, + private_value: IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_EMPHASIS, +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + info: snd_rme9652_control_spdif_mask_info, + get: snd_rme9652_control_spdif_mask_get, + private_value: IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_EMPHASIS, +}, +RME9652_SPDIF_IN("IEC958 Input Connector", 0), +RME9652_SPDIF_OUT("IEC958 Output also on ADAT1", 0), +RME9652_SYNC_MODE("Sync Mode", 0), +RME9652_SYNC_PREF("Preferred Sync Source", 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Channels Thru", + index: 0, + info: snd_rme9652_info_thru, + get: snd_rme9652_get_thru, + put: snd_rme9652_put_thru, +}, +RME9652_SPDIF_RATE("IEC958 Sample Rate", 0), +RME9652_ADAT_SYNC("ADAT1 Sync Check", 0, 0), +RME9652_ADAT_SYNC("ADAT2 Sync Check", 0, 1), +RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2), +RME9652_TC_VALID("Timecode Valid", 0), +RME9652_PASSTHRU("Passthru", 0) +}; + +static snd_kcontrol_new_t snd_rme9652_adat3_check = +RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2); + +static snd_kcontrol_new_t snd_rme9652_adat1_input = +RME9652_ADAT1_IN("ADAT1 Input Source", 0); + +int snd_rme9652_create_controls(snd_card_t *card, rme9652_t *rme9652) +{ + int idx, err; + snd_kcontrol_t *kctl; + + for (idx = 0; idx < RME9652_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_controls[idx], rme9652))) < 0) + return err; + if (idx == 1) /* IEC958 (S/PDIF) Stream */ + rme9652->spdif_ctl = kctl; + } + + if (rme9652->ss_channels == RME9652_NCHANNELS) + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat3_check, rme9652))) < 0) + return err; + + if (rme9652->hw_rev >= 15) + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat1_input, rme9652))) < 0) + return err; + + return 0; +} + +/*------------------------------------------------------------ + /proc interface + ------------------------------------------------------------*/ + +static void +snd_rme9652_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + rme9652_t *rme9652 = (rme9652_t *) entry->private_data; + u32 thru_bits = rme9652->thru_bits; + int show_auto_sync_source = 0; + int i; + unsigned int status; + int x; + + status = rme9652_read(rme9652, RME9652_status_register); + + snd_iprintf(buffer, "%s (Card #%d)\n", rme9652->card_name, rme9652->card->number + 1); + snd_iprintf(buffer, "Buffers: capture %p playback %p\n", + rme9652->capture_buffer, rme9652->playback_buffer); + snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", + rme9652->irq, rme9652->port, rme9652->iobase); + snd_iprintf(buffer, "Control register: %x\n", rme9652->control_register); + + snd_iprintf(buffer, "\n"); + + x = 1 << (6 + rme9652_decode_latency(rme9652->control_register & + RME9652_latency)); + + snd_iprintf(buffer, "Latency: %d samples (2 periods of %lu bytes)\n", + x, (unsigned long) rme9652->period_bytes); + snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", + rme9652_hw_pointer(rme9652)); + snd_iprintf(buffer, "Passthru: %s\n", + rme9652->passthru ? "yes" : "no"); + + if ((rme9652->control_register & (RME9652_Master | RME9652_wsel)) == 0) { + snd_iprintf(buffer, "Clock mode: autosync\n"); + show_auto_sync_source = 1; + } else if (rme9652->control_register & RME9652_wsel) { + if (status & RME9652_wsel_rd) { + snd_iprintf(buffer, "Clock mode: word clock\n"); + } else { + snd_iprintf(buffer, "Clock mode: word clock (no signal)\n"); + } + } else { + snd_iprintf(buffer, "Clock mode: master\n"); + } + + if (show_auto_sync_source) { + switch (rme9652->control_register & RME9652_SyncPref_Mask) { + case RME9652_SyncPref_ADAT1: + snd_iprintf(buffer, "Pref. sync source: ADAT1\n"); + break; + case RME9652_SyncPref_ADAT2: + snd_iprintf(buffer, "Pref. sync source: ADAT2\n"); + break; + case RME9652_SyncPref_ADAT3: + snd_iprintf(buffer, "Pref. sync source: ADAT3\n"); + break; + case RME9652_SyncPref_SPDIF: + snd_iprintf(buffer, "Pref. sync source: IEC958\n"); + break; + default: + snd_iprintf(buffer, "Pref. sync source: ???\n"); + } + } + + if (rme9652->hw_rev >= 15) + snd_iprintf(buffer, "\nADAT1 Input source: %s\n", + (rme9652->control_register & RME9652_ADAT1_INTERNAL) ? + "Internal" : "ADAT1 optical"); + + snd_iprintf(buffer, "\n"); + + switch (rme9652_decode_spdif_in(rme9652->control_register & + RME9652_inp)) { + case RME9652_SPDIFIN_OPTICAL: + snd_iprintf(buffer, "IEC958 input: ADAT1\n"); + break; + case RME9652_SPDIFIN_COAXIAL: + snd_iprintf(buffer, "IEC958 input: Coaxial\n"); + break; + case RME9652_SPDIFIN_INTERN: + snd_iprintf(buffer, "IEC958 input: Internal\n"); + break; + default: + snd_iprintf(buffer, "IEC958 input: ???\n"); + break; + } + + if (rme9652->control_register & RME9652_opt_out) { + snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n"); + } else { + snd_iprintf(buffer, "IEC958 output: Coaxial only\n"); + } + + if (rme9652->control_register & RME9652_PRO) { + snd_iprintf(buffer, "IEC958 quality: Professional\n"); + } else { + snd_iprintf(buffer, "IEC958 quality: Consumer\n"); + } + + if (rme9652->control_register & RME9652_EMP) { + snd_iprintf(buffer, "IEC958 emphasis: on\n"); + } else { + snd_iprintf(buffer, "IEC958 emphasis: off\n"); + } + + if (rme9652->control_register & RME9652_Dolby) { + snd_iprintf(buffer, "IEC958 Dolby: on\n"); + } else { + snd_iprintf(buffer, "IEC958 Dolby: off\n"); + } + + i = rme9652_spdif_sample_rate(rme9652); + + if (i < 0) { + snd_iprintf(buffer, + "IEC958 sample rate: error flag set\n"); + } else if (i == 0) { + snd_iprintf(buffer, "IEC958 sample rate: undetermined\n"); + } else { + snd_iprintf(buffer, "IEC958 sample rate: %d\n", i); + } + + snd_iprintf(buffer, "\n"); + + snd_iprintf(buffer, "ADAT Sample rate: %dHz\n", + rme9652_adat_sample_rate(rme9652)); + + /* Sync Check */ + + x = status & RME9652_sync_0; + if (status & RME9652_lock_0) { + snd_iprintf(buffer, "ADAT1: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT1: No Lock\n"); + } + + x = status & RME9652_sync_1; + if (status & RME9652_lock_1) { + snd_iprintf(buffer, "ADAT2: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT2: No Lock\n"); + } + + x = status & RME9652_sync_2; + if (status & RME9652_lock_2) { + snd_iprintf(buffer, "ADAT3: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT3: No Lock\n"); + } + + snd_iprintf(buffer, "\n"); + + snd_iprintf(buffer, "Timecode signal: %s\n", + (status & RME9652_tc_valid) ? "yes" : "no"); + + /* thru modes */ + + snd_iprintf(buffer, "Punch Status:\n\n"); + + for (i = 0; i < rme9652->ss_channels; i++) { + if (thru_bits & (1 << i)) { + snd_iprintf(buffer, "%2d: on ", i + 1); + } else { + snd_iprintf(buffer, "%2d: off ", i + 1); + } + + if (((i + 1) % 8) == 0) { + snd_iprintf(buffer, "\n"); + } + } + + snd_iprintf(buffer, "\n"); +} + +static void __init snd_rme9652_proc_init(rme9652_t *rme9652) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(rme9652->card, "rme9652", rme9652->card->proc_root)) != + NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = rme9652; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_rme9652_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + rme9652->proc_entry = entry; +} + +static void snd_rme9652_proc_done(rme9652_t *rme9652) +{ + if (rme9652->proc_entry) { + snd_info_unregister(rme9652->proc_entry); + rme9652->proc_entry = NULL; + } +} + +static void snd_rme9652_free_buffers(rme9652_t *rme9652) +{ + if (rme9652->capture_buffer_unaligned) { +#ifndef RME9652_PREALLOCATE_MEMORY + snd_free_pci_pages(rme9652->pci, + RME9652_DMA_AREA_BYTES, + rme9652->capture_buffer_unaligned, + rme9652->capture_buffer_addr); +#else + snd_rme9652_free_buffer(rme9652->dev, rme9652->capture_buffer_unaligned); +#endif + } + + if (rme9652->playback_buffer_unaligned) { +#ifndef RME9652_PREALLOCATE_MEMORY + snd_free_pci_pages(rme9652->pci, + RME9652_DMA_AREA_BYTES, + rme9652->playback_buffer_unaligned, + rme9652->playback_buffer_addr); +#else + snd_rme9652_free_buffer(rme9652->dev, rme9652->playback_buffer_unaligned); +#endif + } +} + +static int snd_rme9652_free(rme9652_t *rme9652) +{ + if (rme9652->irq >= 0) + rme9652_stop(rme9652); + snd_rme9652_proc_done(rme9652); + snd_rme9652_free_buffers(rme9652); + + if (rme9652->iobase) + iounmap((void *) rme9652->iobase); + if (rme9652->res_port) { + release_resource(rme9652->res_port); + kfree_nocheck(rme9652->res_port); + } + if (rme9652->irq >= 0) + free_irq(rme9652->irq, (void *)rme9652); + return 0; +} + +static int __init snd_rme9652_initialize_memory(rme9652_t *rme9652) +{ + void *pb, *cb; + dma_addr_t pb_addr, cb_addr; + unsigned long pb_bus, cb_bus; + +#ifndef RME9652_PREALLOCATE_MEMORY + cb = snd_malloc_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, &cb_addr); + pb = snd_malloc_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, &pb_addr); +#else + cb = snd_rme9652_get_buffer(rme9652->dev, &cb_addr); + pb = snd_rme9652_get_buffer(rme9652->dev, &pb_addr); +#endif + + if (cb == 0 || pb == 0) { + if (cb) { +#ifdef RME9652_PREALLOCATE_MEMORY + snd_rme9652_free_buffer(rme9652->dev, cb); +#else + snd_free_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, cb, cb_addr); +#endif + } + if (pb) { +#ifdef RME9652_PREALLOCATE_MEMORY + snd_rme9652_free_buffer(rme9652->dev, pb); +#else + snd_free_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, pb, pb_addr); +#endif + } + + snd_printk("%s: no buffers available\n", + rme9652->card_name); + return -ENOMEM; + } + + /* save raw addresses for use when freeing memory later */ + + rme9652->capture_buffer_unaligned = cb; + rme9652->playback_buffer_unaligned = pb; + rme9652->capture_buffer_addr = cb_addr; + rme9652->playback_buffer_addr = pb_addr; + + /* Align to bus-space 64K boundary */ + + cb_bus = cb_addr; + cb_bus = (cb_bus + 0xFFFF) & ~0xFFFFl; + + pb_bus = pb_addr; + pb_bus = (pb_bus + 0xFFFF) & ~0xFFFFl; + + /* Tell the card where it is */ + + rme9652_write(rme9652, RME9652_rec_buffer, cb_bus); + rme9652_write(rme9652, RME9652_play_buffer, pb_bus); + +#if 0 // not all architectures have this macro + rme9652->capture_buffer = bus_to_virt(cb_bus); + rme9652->playback_buffer = bus_to_virt(pb_bus); +#else + rme9652->capture_buffer += cb_bus - cb_addr; + rme9652->playback_buffer += pb_bus - pb_addr; +#endif + + return 0; +} + +static void snd_rme9652_set_defaults(rme9652_t *rme9652) +{ + unsigned int k; + + /* ASSUMPTION: rme9652->lock is either held, or + there is no need to hold it (e.g. during module + initalization). + */ + + /* set defaults: + + SPDIF Input via Coax + autosync clock mode + maximum latency (7 = 8192 samples, 64Kbyte buffer, + which implies 2 4096 sample, 32Kbyte periods). + + if rev 1.5, initialize the S/PDIF receiver. + + */ + + rme9652->control_register = + RME9652_inp_0 | rme9652_encode_latency(7); + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + rme9652_reset_hw_pointer(rme9652); + rme9652_compute_period_size(rme9652); + + /* default: thru off for all channels */ + + for (k = 0; k < RME9652_NCHANNELS; ++k) + rme9652_write(rme9652, RME9652_thru_base + k * 4, 0); + + rme9652->thru_bits = 0; + rme9652->passthru = 0; + + /* set a default rate so that the channel map is set up */ + + rme9652_set_rate(rme9652, 48000); +} + +void snd_rme9652_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + rme9652_t *rme9652 = (rme9652_t *) dev_id; + + if (!(rme9652_read(rme9652, RME9652_status_register) & RME9652_IRQ)) { + return; + } + + rme9652_write(rme9652, RME9652_irq_clear, 0); + + if (rme9652->capture_substream) { + snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + } + + if (rme9652->playback_substream) { + snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream); + } +} + +static snd_pcm_uframes_t snd_rme9652_hw_pointer(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + return rme9652_hw_pointer(rme9652); +} + +static char *rme9652_channel_buffer_location(rme9652_t *rme9652, + int stream, + int channel) + +{ + int mapped_channel; + + snd_assert(channel >= 0 || channel < RME9652_NCHANNELS, return NULL); + + if ((mapped_channel = rme9652->channel_map[channel]) < 0) { + return NULL; + } + + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + return rme9652->capture_buffer + + (mapped_channel * RME9652_CHANNEL_BUFFER_BYTES); + } else { + return rme9652->playback_buffer + + (mapped_channel * RME9652_CHANNEL_BUFFER_BYTES); + } +} + +static int snd_rme9652_playback_copy(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + char *channel_buf; + + snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); + + channel_buf = rme9652_channel_buffer_location (rme9652, + substream->pstr->stream, + channel); + snd_assert(channel_buf != NULL, return -EIO); + copy_from_user(channel_buf + pos * 4, src, count * 4); + return count; +} + +static int snd_rme9652_capture_copy(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + char *channel_buf; + + snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); + + channel_buf = rme9652_channel_buffer_location (rme9652, + substream->pstr->stream, + channel); + snd_assert(channel_buf != NULL, return -EIO); + copy_to_user(dst, channel_buf + pos * 4, count * 4); + return count; +} + +static int snd_rme9652_hw_silence(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + char *channel_buf; + + channel_buf = rme9652_channel_buffer_location (rme9652, + substream->pstr->stream, + channel); + snd_assert(channel_buf != NULL, return -EIO); + memset(channel_buf + pos * 4, 0, count * 4); + return count; +} + +static int snd_rme9652_reset(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + snd_pcm_substream_t *other; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + other = rme9652->capture_substream; + else + other = rme9652->playback_substream; + if (rme9652->running) + runtime->status->hw_ptr = rme9652_hw_pointer(rme9652); + else + runtime->status->hw_ptr = 0; + if (other) { + snd_pcm_substream_t *s = substream; + snd_pcm_runtime_t *oruntime = other->runtime; + do { + s = s->link_next; + if (s == other) { + oruntime->status->hw_ptr = runtime->status->hw_ptr; + break; + } + } while (s != substream); + } + return 0; +} + +static int snd_rme9652_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + int err; + pid_t this_pid; + pid_t other_pid; + + spin_lock_irq(&rme9652->lock); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP); + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= rme9652->creg_spdif_stream); + this_pid = rme9652->playback_pid; + other_pid = rme9652->capture_pid; + } else { + this_pid = rme9652->capture_pid; + other_pid = rme9652->playback_pid; + } + + if ((other_pid > 0) && (this_pid != other_pid)) { + + /* The other stream is open, and not by the same + task as this one. Make sure that the parameters + that matter are the same. + */ + + if (params_rate(params) != + rme9652_adat_sample_rate(rme9652)) { + spin_unlock_irq(&rme9652->lock); + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); + return -EBUSY; + } + + if (params_period_size(params) != rme9652->period_bytes / 4) { + spin_unlock_irq(&rme9652->lock); + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + return -EBUSY; + } + + /* We're fine. */ + + spin_unlock_irq(&rme9652->lock); + return 0; + + } else { + spin_unlock_irq(&rme9652->lock); + } + + /* how to make sure that the rate matches an externally-set one ? + */ + + if ((err = rme9652_set_rate(rme9652, params_rate(params))) < 0) { + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); + return err; + } + + if ((err = rme9652_set_interrupt_interval(rme9652, params_period_size(params))) < 0) { + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + return err; + } + + return 0; +} + +static int snd_rme9652_channel_info(snd_pcm_substream_t *substream, + snd_pcm_channel_info_t *info) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + int chn; + + snd_assert(info->channel < RME9652_NCHANNELS, return -EINVAL); + + if ((chn = rme9652->channel_map[info->channel]) < 0) { + return -EINVAL; + } + + info->offset = chn * RME9652_CHANNEL_BUFFER_BYTES; + info->first = 0; + info->step = 32; + return 0; +} + +static int snd_rme9652_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case SNDRV_PCM_IOCTL1_RESET: + { + return snd_rme9652_reset(substream); + } + case SNDRV_PCM_IOCTL1_CHANNEL_INFO: + { + snd_pcm_channel_info_t *info = arg; + return snd_rme9652_channel_info(substream, info); + } + default: + break; + } + + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static void rme9652_silence_playback(rme9652_t *rme9652) +{ + memset(rme9652->playback_buffer, 0, RME9652_DMA_AREA_BYTES); +} + +static int snd_rme9652_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + snd_pcm_substream_t *other; + int running; + spin_lock(&rme9652->lock); + running = rme9652->running; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + running |= 1 << substream->stream; + break; + case SNDRV_PCM_TRIGGER_STOP: + running &= ~(1 << substream->stream); + break; + default: + snd_BUG(); + spin_unlock(&rme9652->lock); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + other = rme9652->capture_substream; + else + other = rme9652->playback_substream; + + if (other) { + snd_pcm_substream_t *s = substream; + do { + s = s->link_next; + if (s == other) { + snd_pcm_trigger_done(s, substream); + if (cmd == SNDRV_PCM_TRIGGER_START) + running |= 1 << s->stream; + else + running &= ~(1 << s->stream); + goto _ok; + } + } while (s != substream); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) && + substream->stream == SNDRV_PCM_STREAM_CAPTURE) + rme9652_silence_playback(rme9652); + } else { + if (running && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rme9652_silence_playback(rme9652); + } + } + _ok: + snd_pcm_trigger_done(substream, substream); + if (!rme9652->running && running) + rme9652_start(rme9652); + else if (rme9652->running && !running) + rme9652_stop(rme9652); + rme9652->running = running; + spin_unlock(&rme9652->lock); + + return 0; +} + +static int snd_rme9652_prepare(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock_irq(&rme9652->lock); + if (!rme9652->running) + rme9652_reset_hw_pointer(rme9652); + spin_unlock_irq(&rme9652->lock); + return result; +} + +static snd_pcm_hardware_t snd_rme9652_playback_subinfo = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_DOUBLE), + formats: SNDRV_PCM_FMTBIT_S32_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + rate_min: 44100, + rate_max: 96000, + channels_min: 10, + channels_max: 26, + buffer_bytes_max: 1024*1024, + period_bytes_min: 1, + period_bytes_max: 1024*1024, + periods_min: 2, + periods_max: 2, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_rme9652_capture_subinfo = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_S32_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + rate_min: 44100, + rate_max: 96000, + channels_min: 10, + channels_max: 26, + buffer_bytes_max: 1024*1024, + period_bytes_min: 1, + period_bytes_max: 1024*1024, + periods_min: 2, + periods_max: 2, + fifo_size: 0, +}; + +static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; + +#define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = { + count: PERIOD_SIZES, + list: period_sizes, + mask: 0 +}; + +static int snd_rme9652_hw_rule_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + rme9652_t *rme9652 = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + unsigned int list[2] = { rme9652->ds_channels, rme9652->ss_channels }; + return snd_interval_list(c, 2, list, 0); +} + +static int snd_rme9652_hw_rule_channels_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + rme9652_t *rme9652 = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (r->min > 48000) { + snd_interval_t t = { + min: rme9652->ds_channels, + max: rme9652->ds_channels, + integer: 1, + }; + return snd_interval_refine(c, &t); + } else if (r->max < 88200) { + snd_interval_t t = { + min: rme9652->ss_channels, + max: rme9652->ss_channels, + integer: 1, + }; + return snd_interval_refine(c, &t); + } + return 0; +} + +static int snd_rme9652_hw_rule_rate_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + rme9652_t *rme9652 = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (c->min >= rme9652->ss_channels) { + snd_interval_t t = { + min: 44100, + max: 48000, + integer: 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= rme9652->ds_channels) { + snd_interval_t t = { + min: 88200, + max: 96000, + integer: 1, + }; + return snd_interval_refine(r, &t); + } + return 0; +} + +static int snd_rme9652_playback_open(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&rme9652->lock, flags); + + snd_pcm_set_sync(substream); + + runtime->hw = snd_rme9652_playback_subinfo; + runtime->dma_area = rme9652->playback_buffer; + runtime->dma_bytes = RME9652_DMA_AREA_BYTES; + + if (rme9652->capture_substream == NULL) { + rme9652_stop(rme9652); + rme9652_set_thru(rme9652, -1, 0); + } + + rme9652->playback_pid = current->pid; + rme9652->playback_substream = substream; + + spin_unlock_irqrestore(&rme9652->lock, flags); + + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels_rate, rme9652, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_rme9652_hw_rule_rate_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + rme9652->creg_spdif_stream = rme9652->creg_spdif; + rme9652->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id); + return 0; +} + +static int snd_rme9652_playback_release(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + + rme9652->playback_pid = -1; + rme9652->playback_substream = NULL; + + spin_unlock_irqrestore(&rme9652->lock, flags); + + rme9652->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id); + return 0; +} + + +static int snd_rme9652_capture_open(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&rme9652->lock, flags); + + snd_pcm_set_sync(substream); + + runtime->hw = snd_rme9652_capture_subinfo; + runtime->dma_area = rme9652->capture_buffer; + runtime->dma_bytes = RME9652_DMA_AREA_BYTES; + + if (rme9652->playback_substream == NULL) { + rme9652_stop(rme9652); + rme9652_set_thru(rme9652, -1, 0); + } + + rme9652->capture_pid = current->pid; + rme9652->capture_substream = substream; + + spin_unlock_irqrestore(&rme9652->lock, flags); + + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels_rate, rme9652, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_rme9652_hw_rule_rate_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + return 0; +} + +static int snd_rme9652_capture_release(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + + rme9652->capture_pid = -1; + rme9652->capture_substream = NULL; + + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static snd_pcm_ops_t snd_rme9652_playback_ops = { + open: snd_rme9652_playback_open, + close: snd_rme9652_playback_release, + ioctl: snd_rme9652_ioctl, + hw_params: snd_rme9652_hw_params, + prepare: snd_rme9652_prepare, + trigger: snd_rme9652_trigger, + pointer: snd_rme9652_hw_pointer, + copy: snd_rme9652_playback_copy, + silence: snd_rme9652_hw_silence, +}; + +static snd_pcm_ops_t snd_rme9652_capture_ops = { + open: snd_rme9652_capture_open, + close: snd_rme9652_capture_release, + ioctl: snd_rme9652_ioctl, + hw_params: snd_rme9652_hw_params, + prepare: snd_rme9652_prepare, + trigger: snd_rme9652_trigger, + pointer: snd_rme9652_hw_pointer, + copy: snd_rme9652_capture_copy, +}; + +static int __init snd_rme9652_create_pcm(snd_card_t *card, + rme9652_t *rme9652) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(card, + rme9652->card_name, + 0, 1, 1, &pcm)) < 0) { + return err; + } + + rme9652->pcm = pcm; + pcm->private_data = rme9652; + strcpy(pcm->name, rme9652->card_name); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme9652_capture_ops); + + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + return 0; +} + +static int __init snd_rme9652_create(snd_card_t *card, + rme9652_t *rme9652, + int precise_ptr) +{ + struct pci_dev *pci = rme9652->pci; + int err; + int status; + unsigned short rev; + + rme9652->irq = -1; + rme9652->card = card; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + spin_lock_init(&rme9652->lock); + + rme9652->port = pci_resource_start(pci, 0); + if ((rme9652->res_port = request_mem_region(rme9652->port, RME9652_IO_EXTENT, "rme9652")) == NULL) { + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_rme9652_interrupt, SA_INTERRUPT|SA_SHIRQ, "rme9652", (void *)rme9652)) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + rme9652->irq = pci->irq; + + rme9652->iobase = (unsigned long) ioremap_nocache(rme9652->port, RME9652_IO_EXTENT); + if (rme9652->iobase == 0) { + snd_printk("unable to remap region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1); + return -EBUSY; + } + + rme9652->precise_ptr = precise_ptr; + + /* Determine the h/w rev level of the card. This seems like + a particularly kludgy way to encode it, but its what RME + chose to do, so we follow them ... + */ + + status = rme9652_read(rme9652, RME9652_status_register); + if (rme9652_decode_spdif_rate(status&RME9652_F) == 1) { + rme9652->hw_rev = 15; + } else { + rme9652->hw_rev = 11; + } + + /* Differentiate between the standard Hammerfall, and the + "Light", which does not have the expansion board. This + method comes from information received from Mathhias + Clausen at RME. Display the EEPROM and h/w revID where + relevant. + */ + + pci_read_config_word(rme9652->pci, PCI_CLASS_REVISION, &rev); + strcpy(card->driver, "RME9652"); + switch (rev & 0xff) { + case 8: /* original eprom */ + strcpy(card->driver, "RME9636"); + if (rme9652->hw_rev == 15) { + rme9652->card_name = "RME Digi9636 (Rev 1.5)"; + } else { + rme9652->card_name = "RME Digi9636"; + } + rme9652->ss_channels = RME9636_NCHANNELS; + break; + case 9: /* W36_G EPROM */ + strcpy(card->driver, "RME9636"); + rme9652->card_name = "RME Digi9636 (Rev G)"; + rme9652->ss_channels = RME9636_NCHANNELS; + break; + case 4: /* W52_G EPROM */ + rme9652->card_name = "RME Digi9652 (Rev G)"; + rme9652->ss_channels = RME9652_NCHANNELS; + break; + default: + case 3: /* original eprom */ + if (rme9652->hw_rev == 15) { + rme9652->card_name = "RME Digi9652 (Rev 1.5)"; + } else { + rme9652->card_name = "RME Digi9652"; + } + rme9652->ss_channels = RME9652_NCHANNELS; + break; + } + + rme9652->ds_channels = (rme9652->ss_channels - 2) / 2 + 2; + + pci_set_master(rme9652->pci); + + if ((err = snd_rme9652_initialize_memory(rme9652)) < 0) { + return err; + } + + if ((err = snd_rme9652_create_pcm(card, rme9652)) < 0) { + return err; + } + + if ((err = snd_rme9652_create_controls(card, rme9652)) < 0) { + return err; + } + + snd_rme9652_proc_init(rme9652); + + rme9652->last_spdif_sample_rate = -1; + rme9652->last_adat_sample_rate = -1; + rme9652->playback_pid = -1; + rme9652->capture_pid = -1; + rme9652->capture_substream = NULL; + rme9652->playback_substream = NULL; + + snd_rme9652_set_defaults(rme9652); + + if (rme9652->hw_rev == 15) { + rme9652_initialize_spdif_receiver (rme9652); + } + + return 0; +} + +static void snd_rme9652_card_free(snd_card_t *card) +{ + rme9652_t *rme9652 = (rme9652_t *) card->private_data; + + if (rme9652) + snd_rme9652_free(rme9652); +} + +static int __devinit snd_rme9652_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + rme9652_t *rme9652; + snd_card_t *card; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(rme9652_t)); + + if (!card) + return -ENOMEM; + + rme9652 = (rme9652_t *) card->private_data; + card->private_free = snd_rme9652_card_free; + rme9652->dev = dev; + rme9652->pci = pci; + + if ((err = snd_rme9652_create(card, rme9652, snd_precise_ptr[dev])) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->shortname, rme9652->card_name); + + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, rme9652->port, rme9652->irq); + + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_rme9652_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name:"RME Digi9652 (Hammerfall)", + id_table:snd_rme9652_ids, + probe:snd_rme9652_probe, + remove:__devexit_p(snd_rme9652_remove), +}; + +static int __init alsa_card_hammerfall_init(void) +{ + if (pci_module_init(&driver) < 0) { +#ifdef MODULE + snd_printk("RME Digi9652/Digi9636: no cards found\n"); +#endif + return -ENODEV; + } + + return 0; +} + +static void __exit alsa_card_hammerfall_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_hammerfall_init) +module_exit(alsa_card_hammerfall_exit) + +#ifndef MODULE + +/* format is: snd-rme9652=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_rme9652_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-rme9652=", alsa_card_rme9652_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/rme9652/rme9652_mem.c b/sound/pci/rme9652/rme9652_mem.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/rme9652/rme9652_mem.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,278 @@ +/* + ALSA memory allocation module for the RME Digi9652 + + Copyright(c) 1999 IEM - Winfried Ritsch + Copyright (C) 1999 Paul Barton-Davis + + This module is only needed if you compiled the rme9652 driver with + the PREALLOCATE_MEMORY option. It allocates the memory need to + run the board and holds it until the module is unloaded. Because + we need 2 contiguous 1.6MB regions for the board, it can be + a problem getting them once the system memory has become fairly + fragmented. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id: rme9652_mem.c,v 1.6 2002/02/04 10:21:33 tiwai Exp $ + + + Tue Oct 17 2000 Jaroslav Kysela + * space is allocated only for physical devices + * added support for 2.4 kernels (pci_alloc_consistent) + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define RME9652_CARDS 8 +#define RME9652_CHANNEL_BUFFER_SAMPLES (16*1024) +#define RME9652_CHANNEL_BUFFER_BYTES (4*RME9652_CHANNEL_BUFFER_SAMPLES) + +/* export */ + +static int snd_enable[8] = {1,1,1,1,1,1,1,1}; +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(RME9652_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable cards to allocate buffers for."); + +MODULE_AUTHOR("Winfried Ritsch, Paul Barton-Davis "); +MODULE_DESCRIPTION("Memory allocator for RME Hammerfall"); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); + +/* Since we don't know at this point if we're allocating memory for a + Hammerfall or a Hammerfall/Light, assume the worst and allocate + space for the maximum number of channels. + + See note in rme9652.h about why we allocate for an extra channel. +*/ + +#define TOTAL_SIZE (26+1)*(RME9652_CHANNEL_BUFFER_BYTES) +#define NBUFS 2*RME9652_CARDS + +#define RME9652_BUF_ALLOCATED 0x1 +#define RME9652_BUF_USED 0x2 + +typedef struct rme9652_buf_stru rme9652_buf_t; + +struct rme9652_buf_stru { + struct pci_dev *pci; + void *buf; + dma_addr_t addr; + char flags; +}; + +static rme9652_buf_t rme9652_buffers[NBUFS]; + +/* These are here so that we have absolutely no dependencies on any + other modules. Dependencies can (1) cause us to lose in the rush + for 2x 1.6MB chunks of contiguous memory and (2) make driver + debugging difficult because unloading and reloading the snd module + causes us to have to do the same for this one. Since on 2.2 + kernels, and before, we can rarely if ever allocate memory after + starting things running, this would be bad. +*/ + +/* remove hack for pci_alloc_consistent to avoid dependecy on snd module */ +#ifdef HACK_PCI_ALLOC_CONSISTENT +#undef pci_alloc_consistent +#endif + +static void *rme9652_malloc_pages(struct pci_dev *pci, + unsigned long size, + dma_addr_t *dmaaddr) +{ + void *res; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0) + res = (void *) pci_alloc_consistent(pci, size, dmaaddr); +#else + int pg; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + res = (void *)__get_free_pages(GFP_KERNEL, pg); + if (res != NULL) + *dmaaddr = virt_to_bus(res); +#endif + if (res != NULL) { + mem_map_t *page = virt_to_page(res); + mem_map_t *last_page = page + (size + PAGE_SIZE - 1) / PAGE_SIZE; + while (page < last_page) + set_bit(PG_reserved, &(page++)->flags); + } + return res; +} + +static void rme9652_free_pages(struct pci_dev *pci, unsigned long size, + void *ptr, dma_addr_t dmaaddr) +{ + mem_map_t *page, *last_page; + + if (ptr == NULL) + return; + page = virt_to_page(ptr); + last_page = virt_to_page(ptr) + (size + PAGE_SIZE - 1) / PAGE_SIZE; + while (page < last_page) + clear_bit(PG_reserved, &(page++)->flags); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0) + pci_free_consistent(pci, size, ptr, dmaaddr); +#else + { + int pg; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + if (bus_to_virt(dmaaddr) != ptr) { + printk(KERN_ERR "rme9652_free_pages: dmaaddr != ptr\n"); + return; + } + free_pages((unsigned long)ptr, pg); + } +#endif +} + +void *snd_rme9652_get_buffer (int card, dma_addr_t *dmaaddr) + +{ + int i; + rme9652_buf_t *rbuf; + + if (card < 0 || card >= RME9652_CARDS) { + printk(KERN_ERR "snd_rme9652_get_buffer: card %d is out of range", card); + return NULL; + } + for (i = card * 2; i < card * 2 + 2; i++) { + rbuf = &rme9652_buffers[i]; + if (rbuf->flags == RME9652_BUF_ALLOCATED) { + rbuf->flags |= RME9652_BUF_USED; + MOD_INC_USE_COUNT; + *dmaaddr = rbuf->addr; + return rbuf->buf; + } + } + + return NULL; +} + +void snd_rme9652_free_buffer (int card, void *addr) + +{ + int i; + rme9652_buf_t *rbuf; + + if (card < 0 || card >= RME9652_CARDS) { + printk(KERN_ERR "snd_rme9652_get_buffer: card %d is out of range", card); + return; + } + for (i = card * 2; i < card * 2 + 2; i++) { + rbuf = &rme9652_buffers[i]; + if (rbuf->buf == addr) { + MOD_DEC_USE_COUNT; + rbuf->flags &= ~RME9652_BUF_USED; + return; + } + } + + printk ("RME9652 memory allocator: unknown buffer address passed to free buffer"); +} + +static void __exit rme9652_free_buffers (void) + +{ + int i; + rme9652_buf_t *rbuf; + + for (i = 0; i < NBUFS; i++) { + + /* We rely on general module code to prevent + us from being unloaded with buffers in use. + + However, not quite. Do not release memory + if it is still marked as in use. This might + be unnecessary. + */ + + rbuf = &rme9652_buffers[i]; + + if (rbuf->flags == RME9652_BUF_ALLOCATED) { + rme9652_free_pages (rbuf->pci, TOTAL_SIZE, rbuf->buf, rbuf->addr); + rbuf->buf = NULL; + rbuf->flags = 0; + } + } +} + +static int __init alsa_rme9652_mem_init(void) +{ + int i; + struct pci_dev *pci; + rme9652_buf_t *rbuf; + + /* make sure our buffer records are clean */ + + for (i = 0; i < NBUFS; i++) { + rbuf = &rme9652_buffers[i]; + rbuf->pci = NULL; + rbuf->buf = NULL; + rbuf->flags = 0; + } + + /* ensure sane values for the number of buffers */ + + /* Remember: 2 buffers per card, one for capture, one for + playback. + */ + + i = 0; /* card number */ + rbuf = rme9652_buffers; + pci_for_each_dev(pci) { + int k; + if (pci->vendor != 0x10ee || pci->device != 0x3fc4) + continue; + + if (!snd_enable[i]) + continue; + + for (k = 0; k < 2; ++k) { + rbuf->buf = rme9652_malloc_pages(pci, TOTAL_SIZE, &rbuf->addr); + if (rbuf->buf == NULL) { + rme9652_free_buffers(); + printk(KERN_ERR "RME9652 memory allocator: no memory available for card %d buffer %d\n", i, k + 1); + return -ENOMEM; + } + rbuf->flags = RME9652_BUF_ALLOCATED; + rbuf++; + } + i++; + } + + if (i == 0) + printk(KERN_ERR "RME9652 memory allocator: no RME9652 card found...\n"); + + return 0; +} + +static void __exit alsa_rme9652_mem_exit(void) +{ + rme9652_free_buffers(); +} + +module_init(alsa_rme9652_mem_init) +module_exit(alsa_rme9652_mem_exit) + +EXPORT_SYMBOL(snd_rme9652_get_buffer); +EXPORT_SYMBOL(snd_rme9652_free_buffer); diff -Nru a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/sonicvibes.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1592 @@ +/* + * Driver for S3 SonicVibes soundcard + * Copyright (c) by Jaroslav Kysela + * + * BUGS: + * It looks like 86c617 rev 3 doesn't supports DDMA buffers above 16MB? + * Driver sometimes hangs... Nobody knows why at this moment... + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#ifndef LINUX_2_2 +#include +#endif + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("S3 SonicVibes PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{S3,SonicVibes PCI}}"); + +#ifndef PCI_VENDOR_ID_S3 +#define PCI_VENDOR_ID_S3 0x5333 +#endif +#ifndef PCI_DEVICE_ID_S3_SONICVIBES +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_reverb[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_mge[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static unsigned int snd_dmaio = 0x7a00; /* DDMA i/o address */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_reverb, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_reverb, "Enable reverb (SRAM is present) for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_reverb, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +MODULE_PARM(snd_mge, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mge, "MIC Gain Enable for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_mge, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +MODULE_PARM(snd_dmaio, "i"); +MODULE_PARM_DESC(snd_dmaio, "DDMA i/o base address for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_dmaio, "global," SNDRV_PORT_DESC); + +/* + * Enhanced port direct registers + */ + +#define SV_REG(sonic, x) ((sonic)->enh_port + SV_REG_##x) + +#define SV_REG_CONTROL 0x00 /* R/W: CODEC/Mixer control register */ +#define SV_ENHANCED 0x01 /* audio mode select - enhanced mode */ +#define SV_TEST 0x02 /* test bit */ +#define SV_REVERB 0x04 /* reverb enable */ +#define SV_WAVETABLE 0x08 /* wavetable active / FM active if not set */ +#define SV_INTA 0x20 /* INTA driving - should be always 1 */ +#define SV_RESET 0x80 /* reset chip */ +#define SV_REG_IRQMASK 0x01 /* R/W: CODEC/Mixer interrupt mask register */ +#define SV_DMAA_MASK 0x01 /* mask DMA-A interrupt */ +#define SV_DMAC_MASK 0x04 /* mask DMA-C interrupt */ +#define SV_SPEC_MASK 0x08 /* special interrupt mask - should be always masked */ +#define SV_UD_MASK 0x40 /* Up/Down button interrupt mask */ +#define SV_MIDI_MASK 0x80 /* mask MIDI interrupt */ +#define SV_REG_STATUS 0x02 /* R/O: CODEC/Mixer status register */ +#define SV_DMAA_IRQ 0x01 /* DMA-A interrupt */ +#define SV_DMAC_IRQ 0x04 /* DMA-C interrupt */ +#define SV_SPEC_IRQ 0x08 /* special interrupt */ +#define SV_UD_IRQ 0x40 /* Up/Down interrupt */ +#define SV_MIDI_IRQ 0x80 /* MIDI interrupt */ +#define SV_REG_INDEX 0x04 /* R/W: CODEC/Mixer index address register */ +#define SV_MCE 0x40 /* mode change enable */ +#define SV_TRD 0x80 /* DMA transfer request disabled */ +#define SV_REG_DATA 0x05 /* R/W: CODEC/Mixer index data register */ + +/* + * Enhanced port indirect registers + */ + +#define SV_IREG_LEFT_ADC 0x00 /* Left ADC Input Control */ +#define SV_IREG_RIGHT_ADC 0x01 /* Right ADC Input Control */ +#define SV_IREG_LEFT_AUX1 0x02 /* Left AUX1 Input Control */ +#define SV_IREG_RIGHT_AUX1 0x03 /* Right AUX1 Input Control */ +#define SV_IREG_LEFT_CD 0x04 /* Left CD Input Control */ +#define SV_IREG_RIGHT_CD 0x05 /* Right CD Input Control */ +#define SV_IREG_LEFT_LINE 0x06 /* Left Line Input Control */ +#define SV_IREG_RIGHT_LINE 0x07 /* Right Line Input Control */ +#define SV_IREG_MIC 0x08 /* MIC Input Control */ +#define SV_IREG_GAME_PORT 0x09 /* Game Port Control */ +#define SV_IREG_LEFT_SYNTH 0x0a /* Left Synth Input Control */ +#define SV_IREG_RIGHT_SYNTH 0x0b /* Right Synth Input Control */ +#define SV_IREG_LEFT_AUX2 0x0c /* Left AUX2 Input Control */ +#define SV_IREG_RIGHT_AUX2 0x0d /* Right AUX2 Input Control */ +#define SV_IREG_LEFT_ANALOG 0x0e /* Left Analog Mixer Output Control */ +#define SV_IREG_RIGHT_ANALOG 0x0f /* Right Analog Mixer Output Control */ +#define SV_IREG_LEFT_PCM 0x10 /* Left PCM Input Control */ +#define SV_IREG_RIGHT_PCM 0x11 /* Right PCM Input Control */ +#define SV_IREG_DMA_DATA_FMT 0x12 /* DMA Data Format */ +#define SV_IREG_PC_ENABLE 0x13 /* Playback/Capture Enable Register */ +#define SV_IREG_UD_BUTTON 0x14 /* Up/Down Button Register */ +#define SV_IREG_REVISION 0x15 /* Revision */ +#define SV_IREG_ADC_OUTPUT_CTRL 0x16 /* ADC Output Control */ +#define SV_IREG_DMA_A_UPPER 0x18 /* DMA A Upper Base Count */ +#define SV_IREG_DMA_A_LOWER 0x19 /* DMA A Lower Base Count */ +#define SV_IREG_DMA_C_UPPER 0x1c /* DMA C Upper Base Count */ +#define SV_IREG_DMA_C_LOWER 0x1d /* DMA C Lower Base Count */ +#define SV_IREG_PCM_RATE_LOW 0x1e /* PCM Sampling Rate Low Byte */ +#define SV_IREG_PCM_RATE_HIGH 0x1f /* PCM Sampling Rate High Byte */ +#define SV_IREG_SYNTH_RATE_LOW 0x20 /* Synthesizer Sampling Rate Low Byte */ +#define SV_IREG_SYNTH_RATE_HIGH 0x21 /* Synthesizer Sampling Rate High Byte */ +#define SV_IREG_ADC_CLOCK 0x22 /* ADC Clock Source Selection */ +#define SV_IREG_ADC_ALT_RATE 0x23 /* ADC Alternative Sampling Rate Selection */ +#define SV_IREG_ADC_PLL_M 0x24 /* ADC PLL M Register */ +#define SV_IREG_ADC_PLL_N 0x25 /* ADC PLL N Register */ +#define SV_IREG_SYNTH_PLL_M 0x26 /* Synthesizer PLL M Register */ +#define SV_IREG_SYNTH_PLL_N 0x27 /* Synthesizer PLL N Register */ +#define SV_IREG_MPU401 0x2a /* MPU-401 UART Operation */ +#define SV_IREG_DRIVE_CTRL 0x2b /* Drive Control */ +#define SV_IREG_SRS_SPACE 0x2c /* SRS Space Control */ +#define SV_IREG_SRS_CENTER 0x2d /* SRS Center Control */ +#define SV_IREG_WAVE_SOURCE 0x2e /* Wavetable Sample Source Select */ +#define SV_IREG_ANALOG_POWER 0x30 /* Analog Power Down Control */ +#define SV_IREG_DIGITAL_POWER 0x31 /* Digital Power Down Control */ + +#define SV_IREG_ADC_PLL SV_IREG_ADC_PLL_M +#define SV_IREG_SYNTH_PLL SV_IREG_SYNTH_PLL_M + +/* + * DMA registers + */ + +#define SV_DMA_ADDR0 0x00 +#define SV_DMA_ADDR1 0x01 +#define SV_DMA_ADDR2 0x02 +#define SV_DMA_ADDR3 0x03 +#define SV_DMA_COUNT0 0x04 +#define SV_DMA_COUNT1 0x05 +#define SV_DMA_COUNT2 0x06 +#define SV_DMA_MODE 0x0b +#define SV_DMA_RESET 0x0d +#define SV_DMA_MASK 0x0f + +/* + * Record sources + */ + +#define SV_RECSRC_RESERVED (0x00<<5) +#define SV_RECSRC_CD (0x01<<5) +#define SV_RECSRC_DAC (0x02<<5) +#define SV_RECSRC_AUX2 (0x03<<5) +#define SV_RECSRC_LINE (0x04<<5) +#define SV_RECSRC_AUX1 (0x05<<5) +#define SV_RECSRC_MIC (0x06<<5) +#define SV_RECSRC_OUT (0x07<<5) + +/* + * constants + */ + +#define SV_FULLRATE 48000 +#define SV_REFFREQUENCY 24576000 +#define SV_ADCMULT 512 + +#define SV_MODE_PLAY 1 +#define SV_MODE_CAPTURE 2 + +/* + + */ + +typedef struct _snd_sonicvibes sonicvibes_t; +#define chip_t sonicvibes_t + +struct _snd_sonicvibes { + unsigned long dma1size; + unsigned long dma2size; + int irq; + + unsigned long sb_port; + struct resource *res_sb_port; + unsigned long enh_port; + struct resource *res_enh_port; + unsigned long synth_port; + struct resource *res_synth_port; + unsigned long midi_port; + struct resource *res_midi_port; + unsigned long game_port; + unsigned int dmaa_port; + struct resource *res_dmaa; + unsigned int dmac_port; + struct resource *res_dmac; + + unsigned char enable; + unsigned char irqmask; + unsigned char revision; + unsigned char format; + unsigned char srs_space; + unsigned char srs_center; + unsigned char mpu_switch; + unsigned char wave_source; + + unsigned int mode; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_rawmidi_t *rmidi; + snd_hwdep_t *fmsynth; /* S3FM */ + + spinlock_t reg_lock; + snd_info_entry_t *proc_entry; + + unsigned int p_dma_size; + unsigned int c_dma_size; + + snd_kcontrol_t *master_mute; + snd_kcontrol_t *master_volume; + +#ifndef LINUX_2_2 + struct gameport gameport; +#endif +}; + +static struct pci_device_id snd_sonic_ids[] __devinitdata = { + { 0x5333, 0xca00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_sonic_ids); + +static ratden_t sonicvibes_adc_clock = { + num_min: 4000 * 65536, + num_max: 48000UL * 65536, + num_step: 1, + den: 65536, +}; +static snd_pcm_hw_constraint_ratdens_t snd_sonicvibes_hw_constraints_adc_clock = { + nrats: 1, + rats: &sonicvibes_adc_clock, +}; + +/* + * common I/O routines + */ + +static inline void snd_sonicvibes_setdmaa(sonicvibes_t * sonic, + unsigned int addr, + unsigned int count) +{ + count--; + outl(addr, sonic->dmaa_port + SV_DMA_ADDR0); + outl(count, sonic->dmaa_port + SV_DMA_COUNT0); + outb(0x18, sonic->dmaa_port + SV_DMA_MODE); +#if 0 + printk("program dmaa: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmaa_port + SV_DMA_ADDR0)); +#endif +} + +static inline void snd_sonicvibes_setdmac(sonicvibes_t * sonic, + unsigned int addr, + unsigned int count) +{ + /* note: dmac is working in word mode!!! */ + count >>= 1; + count--; + outl(addr, sonic->dmac_port + SV_DMA_ADDR0); + outl(count, sonic->dmac_port + SV_DMA_COUNT0); + outb(0x14, sonic->dmac_port + SV_DMA_MODE); +#if 0 + printk("program dmac: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmac_port + SV_DMA_ADDR0)); +#endif +} + +static inline unsigned int snd_sonicvibes_getdmaa(sonicvibes_t * sonic) +{ + return (inl(sonic->dmaa_port + SV_DMA_COUNT0) & 0xffffff) + 1; +} + +static inline unsigned int snd_sonicvibes_getdmac(sonicvibes_t * sonic) +{ + /* note: dmac is working in word mode!!! */ + return ((inl(sonic->dmac_port + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; +} + +static void snd_sonicvibes_out1(sonicvibes_t * sonic, + unsigned char reg, + unsigned char value) +{ + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + outb(value, SV_REG(sonic, DATA)); + udelay(10); +} + +static void snd_sonicvibes_out(sonicvibes_t * sonic, + unsigned char reg, + unsigned char value) +{ + unsigned long flags; + + spin_lock_irqsave(&sonic->reg_lock, flags); + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + outb(value, SV_REG(sonic, DATA)); + udelay(10); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static unsigned char snd_sonicvibes_in1(sonicvibes_t * sonic, unsigned char reg) +{ + unsigned char value; + + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + value = inb(SV_REG(sonic, DATA)); + udelay(10); + return value; +} + +static unsigned char snd_sonicvibes_in(sonicvibes_t * sonic, unsigned char reg) +{ + unsigned long flags; + unsigned char value; + + spin_lock_irqsave(&sonic->reg_lock, flags); + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + value = inb(SV_REG(sonic, DATA)); + udelay(10); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return value; +} + +#ifdef CONFIG_SND_DEBUG +void snd_sonicvibes_debug(sonicvibes_t * sonic) +{ + printk("SV REGS: INDEX = 0x%02x ", inb(SV_REG(sonic, INDEX))); + printk(" STATUS = 0x%02x\n", inb(SV_REG(sonic, STATUS))); + printk(" 0x00: left input = 0x%02x ", snd_sonicvibes_in(sonic, 0x00)); + printk(" 0x20: synth rate low = 0x%02x\n", snd_sonicvibes_in(sonic, 0x20)); + printk(" 0x01: right input = 0x%02x ", snd_sonicvibes_in(sonic, 0x01)); + printk(" 0x21: synth rate high = 0x%02x\n", snd_sonicvibes_in(sonic, 0x21)); + printk(" 0x02: left AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x02)); + printk(" 0x22: ADC clock = 0x%02x\n", snd_sonicvibes_in(sonic, 0x22)); + printk(" 0x03: right AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x03)); + printk(" 0x23: ADC alt rate = 0x%02x\n", snd_sonicvibes_in(sonic, 0x23)); + printk(" 0x04: left CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x04)); + printk(" 0x24: ADC pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x24)); + printk(" 0x05: right CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x05)); + printk(" 0x25: ADC pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x25)); + printk(" 0x06: left line = 0x%02x ", snd_sonicvibes_in(sonic, 0x06)); + printk(" 0x26: Synth pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x26)); + printk(" 0x07: right line = 0x%02x ", snd_sonicvibes_in(sonic, 0x07)); + printk(" 0x27: Synth pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x27)); + printk(" 0x08: MIC = 0x%02x ", snd_sonicvibes_in(sonic, 0x08)); + printk(" 0x28: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x28)); + printk(" 0x09: Game port = 0x%02x ", snd_sonicvibes_in(sonic, 0x09)); + printk(" 0x29: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x29)); + printk(" 0x0a: left synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0a)); + printk(" 0x2a: MPU401 = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2a)); + printk(" 0x0b: right synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0b)); + printk(" 0x2b: drive ctrl = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2b)); + printk(" 0x0c: left AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0c)); + printk(" 0x2c: SRS space = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2c)); + printk(" 0x0d: right AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0d)); + printk(" 0x2d: SRS center = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2d)); + printk(" 0x0e: left analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0e)); + printk(" 0x2e: wave source = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2e)); + printk(" 0x0f: right analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0f)); + printk(" 0x2f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2f)); + printk(" 0x10: left PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x10)); + printk(" 0x30: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x30)); + printk(" 0x11: right PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x11)); + printk(" 0x31: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x31)); + printk(" 0x12: DMA data format = 0x%02x ", snd_sonicvibes_in(sonic, 0x12)); + printk(" 0x32: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x32)); + printk(" 0x13: P/C enable = 0x%02x ", snd_sonicvibes_in(sonic, 0x13)); + printk(" 0x33: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x33)); + printk(" 0x14: U/D button = 0x%02x ", snd_sonicvibes_in(sonic, 0x14)); + printk(" 0x34: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x34)); + printk(" 0x15: revision = 0x%02x ", snd_sonicvibes_in(sonic, 0x15)); + printk(" 0x35: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x35)); + printk(" 0x16: ADC output ctrl = 0x%02x ", snd_sonicvibes_in(sonic, 0x16)); + printk(" 0x36: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x36)); + printk(" 0x17: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x17)); + printk(" 0x37: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x37)); + printk(" 0x18: DMA A upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x18)); + printk(" 0x38: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x38)); + printk(" 0x19: DMA A lower cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x19)); + printk(" 0x39: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x39)); + printk(" 0x1a: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1a)); + printk(" 0x3a: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3a)); + printk(" 0x1b: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1b)); + printk(" 0x3b: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3b)); + printk(" 0x1c: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1c)); + printk(" 0x3c: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3c)); + printk(" 0x1d: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1d)); + printk(" 0x3d: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3d)); + printk(" 0x1e: PCM rate low = 0x%02x ", snd_sonicvibes_in(sonic, 0x1e)); + printk(" 0x3e: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3e)); + printk(" 0x1f: PCM rate high = 0x%02x ", snd_sonicvibes_in(sonic, 0x1f)); + printk(" 0x3f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3f)); +} + +#endif + +static void snd_sonicvibes_setfmt(sonicvibes_t * sonic, + unsigned char mask, + unsigned char value) +{ + unsigned long flags; + + spin_lock_irqsave(&sonic->reg_lock, flags); + outb(SV_MCE | SV_IREG_DMA_DATA_FMT, SV_REG(sonic, INDEX)); + if (mask) { + sonic->format = inb(SV_REG(sonic, DATA)); + udelay(10); + } + sonic->format = (sonic->format & mask) | value; + outb(sonic->format, SV_REG(sonic, DATA)); + udelay(10); + outb(0, SV_REG(sonic, INDEX)); + udelay(10); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static void snd_sonicvibes_pll(unsigned int rate, + unsigned int *res_r, + unsigned int *res_m, + unsigned int *res_n) +{ + unsigned int r, m = 0, n = 0; + unsigned int xm, xn, xr, xd, metric = ~0U; + + if (rate < 625000 / SV_ADCMULT) + rate = 625000 / SV_ADCMULT; + if (rate > 150000000 / SV_ADCMULT) + rate = 150000000 / SV_ADCMULT; + /* slight violation of specs, needed for continuous sampling rates */ + for (r = 0; rate < 75000000 / SV_ADCMULT; r += 0x20, rate <<= 1); + for (xn = 3; xn < 33; xn++) /* 35 */ + for (xm = 3; xm < 257; xm++) { + xr = ((SV_REFFREQUENCY / SV_ADCMULT) * xm) / xn; + if (xr >= rate) + xd = xr - rate; + else + xd = rate - xr; + if (xd < metric) { + metric = xd; + m = xm - 2; + n = xn - 2; + } + } + *res_r = r; + *res_m = m; + *res_n = n; +#if 0 + printk("metric = %i, xm = %i, xn = %i\n", metric, xm, xn); + printk("pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n); +#endif +} + +static void snd_sonicvibes_setpll(sonicvibes_t * sonic, + unsigned char reg, + unsigned int rate) +{ + unsigned long flags; + unsigned int r, m, n; + + snd_sonicvibes_pll(rate, &r, &m, &n); + if (sonic != NULL) { + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_out1(sonic, reg, m); + snd_sonicvibes_out1(sonic, reg + 1, r | n); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + } +} + +static void snd_sonicvibes_set_adc_rate(sonicvibes_t * sonic, unsigned int rate) +{ + unsigned long flags; + unsigned int div; + unsigned char clock; + + div = 48000 / rate; + if (div > 8) + div = 8; + if ((48000 / div) == rate) { /* use the alternate clock */ + clock = 0x10; + } else { /* use the PLL source */ + clock = 0x00; + snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, rate); + } + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_out1(sonic, SV_IREG_ADC_ALT_RATE, (div - 1) << 4); + snd_sonicvibes_out1(sonic, SV_IREG_ADC_CLOCK, clock); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static int snd_sonicvibes_hw_constraint_dac_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned int rate, div, r, m, n; + + if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min == + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max) { + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min; + div = 48000 / rate; + if (div > 8) + div = 8; + if ((48000 / div) == rate) { + params->rate_num = rate; + params->rate_den = 1; + } else { + snd_sonicvibes_pll(rate, &r, &m, &n); + snd_assert((SV_REFFREQUENCY % 16) == 0, return -EINVAL); + snd_assert((SV_ADCMULT % 512) == 0, return -EINVAL); + params->rate_num = (SV_REFFREQUENCY/16) * (n+2) * r; + params->rate_den = (SV_ADCMULT/512) * (m+2); + } + } + return 0; +} + +static void snd_sonicvibes_set_dac_rate(sonicvibes_t * sonic, unsigned int rate) +{ + unsigned int div; + unsigned long flags; + + div = (rate * 65536 + SV_FULLRATE / 2) / SV_FULLRATE; + if (div > 65535) + div = 65535; + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_HIGH, div >> 8); + snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_LOW, div); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static int snd_sonicvibes_trigger(sonicvibes_t * sonic, int what, int cmd) +{ + int result = 0; + + spin_lock(&sonic->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (!(sonic->enable & what)) { + sonic->enable |= what; + snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable); + } + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (sonic->enable & what) { + sonic->enable &= ~what; + snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable); + } + } else { + result = -EINVAL; + } + spin_unlock(&sonic->reg_lock); + return result; +} + +static void snd_sonicvibes_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, dev_id, return); + unsigned char status; + + status = inb(SV_REG(sonic, STATUS)); + if (!(status & (SV_DMAA_IRQ | SV_DMAC_IRQ | SV_MIDI_IRQ))) + return; + if (status == 0xff) { /* failure */ + outb(sonic->irqmask = ~0, SV_REG(sonic, IRQMASK)); + snd_printk("IRQ failure - interrupts disabled!!\n"); + return; + } + if (sonic->pcm) { + if (status & SV_DMAA_IRQ) + snd_pcm_period_elapsed(sonic->playback_substream); + if (status & SV_DMAC_IRQ) + snd_pcm_period_elapsed(sonic->capture_substream); + } + if (sonic->rmidi) { + if (status & SV_MIDI_IRQ) + snd_mpu401_uart_interrupt(irq, sonic->rmidi->private_data, regs); + } + if (status & SV_UD_IRQ) { + unsigned char udreg; + int vol, oleft, oright, mleft, mright; + + spin_lock(&sonic->reg_lock); + udreg = snd_sonicvibes_in1(sonic, SV_IREG_UD_BUTTON); + vol = udreg & 0x3f; + if (!(udreg & 0x40)) + vol = -vol; + oleft = mleft = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ANALOG); + oright = mright = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ANALOG); + oleft &= 0x1f; + oright &= 0x1f; + oleft += vol; + if (oleft < 0) + oleft = 0; + if (oleft > 0x1f) + oleft = 0x1f; + oright += vol; + if (oright < 0) + oright = 0; + if (oright > 0x1f) + oright = 0x1f; + if (udreg & 0x80) { + mleft ^= 0x80; + mright ^= 0x80; + } + oleft |= mleft & 0x80; + oright |= mright & 0x80; + snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ANALOG, oleft); + snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ANALOG, oright); + spin_unlock(&sonic->reg_lock); + snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_mute->id); + snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_volume->id); + } +} + +/* + * PCM part + */ + +static int snd_sonicvibes_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + return snd_sonicvibes_trigger(sonic, 1, cmd); +} + +static int snd_sonicvibes_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + return snd_sonicvibes_trigger(sonic, 2, cmd); +} + +static int snd_sonicvibes_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_sonicvibes_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_sonicvibes_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char fmt = 0; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + sonic->p_dma_size = size; + count--; + if (runtime->channels > 1) + fmt |= 1; + if (snd_pcm_format_width(runtime->format) == 16) + fmt |= 2; + snd_sonicvibes_setfmt(sonic, ~3, fmt); + snd_sonicvibes_set_dac_rate(sonic, runtime->rate); + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_setdmaa(sonic, runtime->dma_addr, size); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_UPPER, count >> 8); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_LOWER, count); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return 0; +} + +static int snd_sonicvibes_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char fmt = 0; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + sonic->c_dma_size = size; + count >>= 1; + count--; + if (runtime->channels > 1) + fmt |= 0x10; + if (snd_pcm_format_width(runtime->format) == 16) + fmt |= 0x20; + snd_sonicvibes_setfmt(sonic, ~0x30, fmt); + snd_sonicvibes_set_adc_rate(sonic, runtime->rate); + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_setdmac(sonic, runtime->dma_addr, size); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_UPPER, count >> 8); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_LOWER, count); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_sonicvibes_playback_pointer(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(sonic->enable & 1)) + return 0; + ptr = sonic->p_dma_size - snd_sonicvibes_getdmaa(sonic); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_sonicvibes_capture_pointer(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + size_t ptr; + if (!(sonic->enable & 2)) + return 0; + ptr = sonic->c_dma_size - snd_sonicvibes_getdmac(sonic); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_sonicvibes_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 32, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_sonicvibes_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 32, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_sonicvibes_playback_open(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + sonic->mode |= SV_MODE_PLAY; + sonic->playback_substream = substream; + runtime->hw = snd_sonicvibes_playback; + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_sonicvibes_hw_constraint_dac_rate, 0, SNDRV_PCM_HW_PARAM_RATE, -1); + return 0; +} + +static int snd_sonicvibes_capture_open(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + sonic->mode |= SV_MODE_CAPTURE; + sonic->capture_substream = substream; + runtime->hw = snd_sonicvibes_capture; + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_sonicvibes_hw_constraints_adc_clock); + return 0; +} + +static int snd_sonicvibes_playback_close(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + + sonic->playback_substream = NULL; + sonic->mode &= ~SV_MODE_PLAY; + return 0; +} + +static int snd_sonicvibes_capture_close(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + + sonic->capture_substream = NULL; + sonic->mode &= ~SV_MODE_CAPTURE; + return 0; +} + +static snd_pcm_ops_t snd_sonicvibes_playback_ops = { + open: snd_sonicvibes_playback_open, + close: snd_sonicvibes_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sonicvibes_hw_params, + hw_free: snd_sonicvibes_hw_free, + prepare: snd_sonicvibes_playback_prepare, + trigger: snd_sonicvibes_playback_trigger, + pointer: snd_sonicvibes_playback_pointer, +}; + +static snd_pcm_ops_t snd_sonicvibes_capture_ops = { + open: snd_sonicvibes_capture_open, + close: snd_sonicvibes_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sonicvibes_hw_params, + hw_free: snd_sonicvibes_hw_free, + prepare: snd_sonicvibes_capture_prepare, + trigger: snd_sonicvibes_capture_trigger, + pointer: snd_sonicvibes_capture_pointer, +}; + +static void snd_sonicvibes_pcm_free(snd_pcm_t *pcm) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, pcm->private_data, return); + sonic->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_sonicvibes_pcm(sonicvibes_t * sonic, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0) + return err; + snd_assert(pcm != NULL, return -EINVAL); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sonicvibes_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sonicvibes_capture_ops); + + pcm->private_data = sonic; + pcm->private_free = snd_sonicvibes_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "S3 SonicVibes"); + sonic->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(sonic->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer part + */ + +#define SONICVIBES_MUX(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_sonicvibes_info_mux, \ + get: snd_sonicvibes_get_mux, put: snd_sonicvibes_put_mux } + +static int snd_sonicvibes_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[7] = { + "CD", "PCM", "Aux1", "Line", "Aux0", "Mic", "Mix" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 7; + if (uinfo->value.enumerated.item >= 7) + uinfo->value.enumerated.item = 6; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_sonicvibes_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&sonic->reg_lock, flags); + ucontrol->value.enumerated.item[0] = ((snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC) & SV_RECSRC_OUT) >> 5) - 1; + ucontrol->value.enumerated.item[1] = ((snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC) & SV_RECSRC_OUT) >> 5) - 1; + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return 0; +} + +static int snd_sonicvibes_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right, oval1, oval2; + int change; + + if (ucontrol->value.enumerated.item[0] >= 7 || + ucontrol->value.enumerated.item[1] >= 7) + return -EINVAL; + left = (ucontrol->value.enumerated.item[0] + 1) << 5; + right = (ucontrol->value.enumerated.item[1] + 1) << 5; + spin_lock_irqsave(&sonic->reg_lock, flags); + oval1 = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC); + oval2 = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC); + left = (oval1 & ~SV_RECSRC_OUT) | left; + right = (oval2 & ~SV_RECSRC_OUT) | right; + change = left != oval1 || right != oval2; + snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ADC, left); + snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ADC, right); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return change; +} + +#define SONICVIBES_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_sonicvibes_info_single, \ + get: snd_sonicvibes_get_single, put: snd_sonicvibes_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_sonicvibes_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sonicvibes_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&sonic->reg_lock, flags); + ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, reg)>> shift) & mask; + spin_unlock_irqrestore(&sonic->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_sonicvibes_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val, oval; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&sonic->reg_lock, flags); + oval = snd_sonicvibes_in1(sonic, reg); + val = (oval & ~(mask << shift)) | val; + change = val != oval; + snd_sonicvibes_out1(sonic, reg, val); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return change; +} + +#define SONICVIBES_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_sonicvibes_info_double, \ + get: snd_sonicvibes_get_double, put: snd_sonicvibes_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_sonicvibes_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sonicvibes_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&sonic->reg_lock, flags); + ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, left_reg) >> shift_left) & mask; + ucontrol->value.integer.value[1] = (snd_sonicvibes_in1(sonic, right_reg) >> shift_right) & mask; + spin_unlock_irqrestore(&sonic->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_sonicvibes_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2, oval1, oval2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&sonic->reg_lock, flags); + oval1 = snd_sonicvibes_in1(sonic, left_reg); + oval2 = snd_sonicvibes_in1(sonic, right_reg); + val1 = (oval1 & ~(mask << shift_left)) | val1; + val2 = (oval2 & ~(mask << shift_right)) | val2; + change = val1 != oval1 || val2 != oval2; + snd_sonicvibes_out1(sonic, left_reg, val1); + snd_sonicvibes_out1(sonic, right_reg, val2); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return change; +} + +#define SONICVIBES_CONTROLS (sizeof(snd_sonicvibes_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sonicvibes_controls[] __devinitdata = { +SONICVIBES_DOUBLE("Capture Volume", 0, SV_IREG_LEFT_ADC, SV_IREG_RIGHT_ADC, 0, 0, 15, 0), +SONICVIBES_DOUBLE("Aux Playback Switch", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Aux Playback Volume", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 0, 0, 31, 1), +SONICVIBES_DOUBLE("CD Playback Switch", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 7, 7, 1, 1), +SONICVIBES_DOUBLE("CD Playback Volume", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 0, 0, 31, 1), +SONICVIBES_DOUBLE("Line Playback Switch", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Line Playback Volume", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 0, 0, 31, 1), +SONICVIBES_SINGLE("Mic Playback Switch", 0, SV_IREG_MIC, 7, 1, 1), +SONICVIBES_SINGLE("Mic Playback Volume", 0, SV_IREG_MIC, 0, 15, 1), +SONICVIBES_SINGLE("Mic Boost", 0, SV_IREG_LEFT_ADC, 4, 1, 0), +SONICVIBES_DOUBLE("Synth Playback Switch", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Synth Playback Volume", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 0, 0, 31, 1), +SONICVIBES_DOUBLE("Aux Playback Switch", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Aux Playback Volume", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 0, 0, 31, 1), +SONICVIBES_DOUBLE("Master Playback Switch", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Master Playback Volume", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 0, 0, 31, 1), +SONICVIBES_DOUBLE("PCM Playback Switch", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 7, 7, 1, 1), +SONICVIBES_DOUBLE("PCM Playback Volume", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 0, 0, 63, 1), +SONICVIBES_SINGLE("Loopback Capture Switch", 0, SV_IREG_ADC_OUTPUT_CTRL, 0, 1, 0), +SONICVIBES_SINGLE("Loopback Capture Volume", 0, SV_IREG_ADC_OUTPUT_CTRL, 2, 63, 1), +SONICVIBES_MUX("Capture Source", 0) +}; + +static void snd_sonicvibes_master_free(snd_kcontrol_t *kcontrol) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, _snd_kcontrol_chip(kcontrol), return); + sonic->master_mute = NULL; + sonic->master_volume = NULL; +} + +static int __devinit snd_sonicvibes_mixer(sonicvibes_t * sonic) +{ + snd_card_t *card; + snd_kcontrol_t *kctl; + int idx, err; + + snd_assert(sonic != NULL && sonic->card != NULL, return -EINVAL); + card = sonic->card; + strcpy(card->mixername, "S3 SonicVibes"); + + for (idx = 0; idx < SONICVIBES_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic))) < 0) + return err; + switch (idx) { + case 0: + case 1: kctl->private_free = snd_sonicvibes_master_free; break; + } + } + return 0; +} + +/* + + */ + +static void snd_sonicvibes_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, entry->private_data, return); + unsigned char tmp; + + tmp = sonic->srs_space & 0x0f; + snd_iprintf(buffer, "SRS 3D : %s\n", + sonic->srs_space & 0x80 ? "off" : "on"); + snd_iprintf(buffer, "SRS Space : %s\n", + tmp == 0x00 ? "100%" : + tmp == 0x01 ? "75%" : + tmp == 0x02 ? "50%" : + tmp == 0x03 ? "25%" : "0%"); + tmp = sonic->srs_center & 0x0f; + snd_iprintf(buffer, "SRS Center : %s\n", + tmp == 0x00 ? "100%" : + tmp == 0x01 ? "75%" : + tmp == 0x02 ? "50%" : + tmp == 0x03 ? "25%" : "0%"); + tmp = sonic->wave_source & 0x03; + snd_iprintf(buffer, "WaveTable Source : %s\n", + tmp == 0x00 ? "on-board ROM" : + tmp == 0x01 ? "PCI bus" : "on-board ROM + PCI bus"); + tmp = sonic->mpu_switch; + snd_iprintf(buffer, "Onboard synth : %s\n", tmp & 0x01 ? "on" : "off"); + snd_iprintf(buffer, "Ext. Rx to synth : %s\n", tmp & 0x02 ? "on" : "off"); + snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", tmp & 0x04 ? "on" : "off"); +} + +static void __devinit snd_sonicvibes_proc_init(sonicvibes_t * sonic) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(sonic->card, "sonicvibes", sonic->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = sonic; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_sonicvibes_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + sonic->proc_entry = entry; +} + +static void snd_sonicvibes_proc_done(sonicvibes_t * sonic) +{ + if (sonic->proc_entry) { + snd_info_unregister(sonic->proc_entry); + sonic->proc_entry = NULL; + } +} + +/* + + */ + +static snd_kcontrol_new_t snd_sonicvibes_game_control __devinitdata = +SONICVIBES_SINGLE("Joystick Speed", 0, SV_IREG_GAME_PORT, 1, 15, 0); + +static int snd_sonicvibes_free(sonicvibes_t *sonic) +{ +#ifndef LINUX_2_2 + if (sonic->gameport.io) + gameport_unregister_port(&sonic->gameport); +#endif + snd_sonicvibes_proc_done(sonic); + pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port); + pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port); + if (sonic->res_sb_port) { + release_resource(sonic->res_sb_port); + kfree_nocheck(sonic->res_sb_port); + } + if (sonic->res_enh_port) { + release_resource(sonic->res_enh_port); + kfree_nocheck(sonic->res_enh_port); + } + if (sonic->res_synth_port) { + release_resource(sonic->res_synth_port); + kfree_nocheck(sonic->res_synth_port); + } + if (sonic->res_midi_port) { + release_resource(sonic->res_midi_port); + kfree_nocheck(sonic->res_midi_port); + } + if (sonic->res_dmaa) { + release_resource(sonic->res_dmaa); + kfree_nocheck(sonic->res_dmaa); + } + if (sonic->res_dmac) { + release_resource(sonic->res_dmac); + kfree_nocheck(sonic->res_dmac); + } + if (sonic->irq >= 0) + free_irq(sonic->irq, (void *)sonic); + snd_magic_kfree(sonic); + return 0; +} + +static int snd_sonicvibes_dev_free(snd_device_t *device) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, device->device_data, return -ENXIO); + return snd_sonicvibes_free(sonic); +} + +static int __devinit snd_sonicvibes_create(snd_card_t * card, + struct pci_dev *pci, + int reverb, + int mge, + sonicvibes_t ** rsonic) +{ + sonicvibes_t *sonic; + unsigned int dmaa, dmac; + int err; + static snd_device_ops_t ops = { + dev_free: snd_sonicvibes_dev_free, + }; + + *rsonic = NULL; + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 24 bits */ + if (!pci_dma_supported(pci, 0x00ffffff)) { + snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x00ffffff); + + sonic = snd_magic_kcalloc(sonicvibes_t, 0, GFP_KERNEL); + if (sonic == NULL) + return -ENOMEM; + spin_lock_init(&sonic->reg_lock); + sonic->card = card; + sonic->pci = pci; + sonic->irq = -1; + sonic->sb_port = pci_resource_start(pci, 0); + if ((sonic->res_sb_port = request_region(sonic->sb_port, 0x10, "S3 SonicVibes SB")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab SB port at 0x%lx-0x%lx\n", sonic->sb_port, sonic->sb_port + 0x10 - 1); + return -EBUSY; + } + sonic->enh_port = pci_resource_start(pci, 1); + if ((sonic->res_enh_port = request_region(sonic->enh_port, 0x10, "S3 SonicVibes Enhanced")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab PCM port at 0x%lx-0x%lx\n", sonic->enh_port, sonic->enh_port + 0x10 - 1); + return -EBUSY; + } + sonic->synth_port = pci_resource_start(pci, 2); + if ((sonic->res_synth_port = request_region(sonic->synth_port, 4, "S3 SonicVibes Synth")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab synth port at 0x%lx-0x%lx\n", sonic->synth_port, sonic->synth_port + 4 - 1); + return -EBUSY; + } + sonic->midi_port = pci_resource_start(pci, 3); + if ((sonic->res_midi_port = request_region(sonic->midi_port, 4, "S3 SonicVibes Midi")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab MIDI port at 0x%lx-0x%lx\n", sonic->midi_port, sonic->midi_port + 4 - 1); + return -EBUSY; + } + sonic->game_port = pci_resource_start(pci, 4); + if (request_irq(pci->irq, snd_sonicvibes_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", (void *)sonic)) { + snd_magic_kfree(sonic); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + sonic->irq = pci->irq; + + pci_read_config_dword(pci, 0x40, &dmaa); + pci_read_config_dword(pci, 0x48, &dmac); + snd_dmaio &= ~0x0f; + dmaa &= ~0x0f; + dmac &= ~0x0f; + if (!dmaa) { + dmaa = snd_dmaio; + snd_dmaio += 0x10; + snd_printk("BIOS did not allocate DDMA channel A i/o, allocated at 0x%x\n", dmaa); + } + if (!dmac) { + dmac = snd_dmaio; + snd_dmaio += 0x10; + snd_printk("BIOS did not allocate DDMA channel C i/o, allocated at 0x%x\n", dmac); + } + pci_write_config_dword(pci, 0x40, dmaa); + pci_write_config_dword(pci, 0x48, dmac); + + if ((sonic->res_dmaa = request_region(dmaa, 0x10, "S3 SonicVibes DDMA-A")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab DDMA-A port at 0x%x-0x%x\n", dmaa, dmaa + 0x10 - 1); + return -EBUSY; + } + if ((sonic->res_dmac = request_region(dmac, 0x10, "S3 SonicVibes DDMA-C")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab DDMA-C port at 0x%x-0x%x\n", dmac, dmac + 0x10 - 1); + return -EBUSY; + } + + pci_read_config_dword(pci, 0x40, &sonic->dmaa_port); + pci_read_config_dword(pci, 0x48, &sonic->dmac_port); + sonic->dmaa_port &= ~0x0f; + sonic->dmac_port &= ~0x0f; + pci_write_config_dword(pci, 0x40, sonic->dmaa_port | 9); /* enable + enhanced */ + pci_write_config_dword(pci, 0x48, sonic->dmac_port | 9); /* enable */ + /* ok.. initialize S3 SonicVibes chip */ + outb(SV_RESET, SV_REG(sonic, CONTROL)); /* reset chip */ + udelay(100); + outb(0, SV_REG(sonic, CONTROL)); /* release reset */ + udelay(100); + outb(SV_ENHANCED | SV_INTA | (reverb ? SV_REVERB : 0), SV_REG(sonic, CONTROL)); + inb(SV_REG(sonic, STATUS)); /* clear IRQs */ +#if 1 + snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0); /* drive current 16mA */ +#else + snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0x40); /* drive current 8mA */ +#endif + snd_sonicvibes_out(sonic, SV_IREG_PC_ENABLE, sonic->enable = 0); /* disable playback & capture */ + outb(sonic->irqmask = ~(SV_DMAA_MASK | SV_DMAC_MASK | SV_UD_MASK), SV_REG(sonic, IRQMASK)); + inb(SV_REG(sonic, STATUS)); /* clear IRQs */ + snd_sonicvibes_out(sonic, SV_IREG_ADC_CLOCK, 0); /* use PLL as clock source */ + snd_sonicvibes_out(sonic, SV_IREG_ANALOG_POWER, 0); /* power up analog parts */ + snd_sonicvibes_out(sonic, SV_IREG_DIGITAL_POWER, 0); /* power up digital parts */ + snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, 8000); + snd_sonicvibes_out(sonic, SV_IREG_SRS_SPACE, sonic->srs_space = 0x80); /* SRS space off */ + snd_sonicvibes_out(sonic, SV_IREG_SRS_CENTER, sonic->srs_center = 0x00);/* SRS center off */ + snd_sonicvibes_out(sonic, SV_IREG_MPU401, sonic->mpu_switch = 0x05); /* MPU-401 switch */ + snd_sonicvibes_out(sonic, SV_IREG_WAVE_SOURCE, sonic->wave_source = 0x00); /* onboard ROM */ + snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_LOW, (8000 * 65536 / SV_FULLRATE) & 0xff); + snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_HIGH, ((8000 * 65536 / SV_FULLRATE) >> 8) & 0xff); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_ADC, mge ? 0xd0 : 0xc0); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ADC, 0xc0); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX1, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX1, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_CD, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_CD, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_LINE, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_LINE, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_MIC, 0x8f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_SYNTH, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_SYNTH, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX2, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX2, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_ANALOG, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ANALOG, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_PCM, 0xbf); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_PCM, 0xbf); + snd_sonicvibes_out(sonic, SV_IREG_ADC_OUTPUT_CTRL, 0xfc); +#if 0 + snd_sonicvibes_debug(sonic); +#endif + sonic->revision = snd_sonicvibes_in(sonic, SV_IREG_REVISION); + snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_game_control, sonic)); + snd_sonicvibes_proc_init(sonic); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sonic, &ops)) < 0) { + snd_sonicvibes_free(sonic); + return err; + } + + *rsonic = sonic; + return 0; +} + +/* + * MIDI section + */ + +#define SONICVIBES_MIDI_CONTROLS (sizeof(snd_sonicvibes_midi_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sonicvibes_midi_controls[] __devinitdata = { +SONICVIBES_SINGLE("SonicVibes Wave Source RAM", 0, SV_IREG_WAVE_SOURCE, 0, 1, 0), +SONICVIBES_SINGLE("SonicVibes Wave Source RAM+ROM", 0, SV_IREG_WAVE_SOURCE, 1, 1, 0), +SONICVIBES_SINGLE("SonicVibes Onboard Synth", 0, SV_IREG_MPU401, 0, 1, 0), +SONICVIBES_SINGLE("SonicVibes External Rx to Synth", 0, SV_IREG_MPU401, 1, 1, 0), +SONICVIBES_SINGLE("SonicVibes External Tx", 0, SV_IREG_MPU401, 2, 1, 0) +}; + +static int snd_sonicvibes_midi_input_open(mpu401_t * mpu) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return -EIO); + outb(sonic->irqmask &= ~SV_MIDI_MASK, SV_REG(sonic, IRQMASK)); + return 0; +} + +static void snd_sonicvibes_midi_input_close(mpu401_t * mpu) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return); + outb(sonic->irqmask |= SV_MIDI_MASK, SV_REG(sonic, IRQMASK)); +} + +static int __devinit snd_sonicvibes_midi(sonicvibes_t * sonic, snd_rawmidi_t * rmidi) +{ + mpu401_t * mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return -ENXIO); + snd_card_t *card = sonic->card; + snd_rawmidi_str_t *dir; + int idx, err; + + mpu->private_data = sonic; + mpu->open_input = snd_sonicvibes_midi_input_open; + mpu->close_input = snd_sonicvibes_midi_input_close; + dir = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + for (idx = 0; idx < SONICVIBES_MIDI_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_midi_controls[idx], sonic))) < 0) + return err; + return 0; +} + +static int __devinit snd_sonic_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + sonicvibes_t *sonic; + snd_rawmidi_t *midi_uart; + opl3_t *opl3; + int idx, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + for (idx = 0; idx < 5; idx++) { + if (pci_resource_start(pci, idx) == 0 || + !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) { + snd_card_free(card); + return -ENODEV; + } + } + if ((err = snd_sonicvibes_create(card, pci, + snd_reverb[dev] ? 1 : 0, + snd_mge[dev] ? 1 : 0, + &sonic)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sonicvibes_mixer(sonic)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES, + sonic->midi_port, 1, + sonic->irq, 0, + &midi_uart)) < 0) { + snd_card_free(card); + return err; + } + snd_sonicvibes_midi(sonic, midi_uart); + if ((err = snd_opl3_create(card, sonic->synth_port, + sonic->synth_port + 2, + OPL3_HW_OPL3_SV, 1, &opl3)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifndef LINUX_2_2 + sonic->gameport.io = sonic->game_port; + gameport_register_port(&sonic->gameport); +#endif + strcpy(card->driver, "SonicVibes"); + strcpy(card->shortname, "S3 SonicVibes"); + sprintf(card->longname, "%s rev %i at 0x%lx, irq %i", + card->shortname, + sonic->revision, + pci_resource_start(pci, 1), + sonic->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_sonic_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "S3 SonicVibes", + id_table: snd_sonic_ids, + probe: snd_sonic_probe, + remove: __devexit_p(snd_sonic_remove), +}; + +static int __init alsa_card_sonicvibes_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("S3 SonicVibes soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_sonicvibes_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_sonicvibes_init) +module_exit(alsa_card_sonicvibes_exit) + +#ifndef MODULE + +/* format is: snd-sonicvibes=snd_enable,snd_index,snd_id, + snd_reverb,snd_mge,snd_dmaio */ + +static int __init alsa_card_sonicvibes_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_reverb[nr_dev]) == 2 && + get_option(&str,&snd_mge[nr_dev]) == 2 && + get_option(&str,(int *)&snd_dmaio) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sonicvibes=", alsa_card_sonicvibes_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/trident/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,27 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _trident.o + +list-multi := snd-trident.o snd-trident-synth.o + +export-objs := trident_main.o + +snd-trident-objs := trident.o trident_main.o trident_memory.o +snd-trident-synth-objs := trident_synth.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_TRIDENT) += snd-trident.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_TRIDENT) += snd-trident-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-trident.o: $(snd-trident-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-trident-objs) + +snd-trident-synth.o: $(snd-trident-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-trident-synth-objs) diff -Nru a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/trident/trident.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,263 @@ +/* + * Driver for Trident 4DWave DX/NX & SiS SI7018 Audio PCI soundcard + * + * Driver was originated by Trident + * Fri Feb 19 15:55:28 MST 1999 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela , "); +MODULE_DESCRIPTION("Trident 4D-WaveDX/NX & SiS SI7018"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Trident,4DWave DX}," + "{Trident,4DWave NX}," + "{SiS,SI7018 PCI Audio}," + "{Best Union,Miss Melody 4DWave PCI}," + "{HIS,4DWave PCI}," + "{Warpspeed,ONSpeed 4DWave PCI}," + "{Aztech Systems,PCI 64-Q3D}," + "{Addonics,SV 750}," + "{CHIC,True Sound 4Dwave}," + "{Shark,Predator4D-PCI}," + "{Jaton,SonicWave 4D}," + "{Hoontech,SoundTrack Digital 4DWave NX}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32}; +static int snd_wavetable_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8192}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Trident 4DWave PCI soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Trident 4DWave PCI soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Trident 4DWave PCI soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_channels, "Number of hardware channels assigned for PCM."); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}"); +MODULE_PARM(snd_wavetable_size, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_wavetable_size, "Maximum memory size in kB for wavetable synth."); +MODULE_PARM_SYNTAX(snd_wavetable_size, SNDRV_ENABLED ",default:8192,skill:advanced"); + +static struct pci_device_id snd_trident_ids[] __devinitdata = { + { 0x1023, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave DX PCI Audio */ + { 0x1023, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave NX PCI Audio */ + { 0x1039, 0x7018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* SiS SI7018 PCI Audio */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_trident_ids); + +static int __devinit snd_trident_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + trident_t *trident; + const char *str; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_trident_create(card, pci, + snd_pcm_channels[dev], + 2, + snd_wavetable_size[dev], + &trident)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_trident_pcm(trident, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + if ((err = snd_trident_foldback_pcm(trident, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + break; + } + if (trident->device == TRIDENT_DEVICE_ID_NX) { + if ((err = snd_trident_spdif_pcm(trident, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE, + trident->midi_port, 1, + trident->irq, 0, &trident->rmidi)) < 0) { + snd_card_free(card); + return err; + } + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if ((err = snd_trident_attach_synthesizer(trident)) < 0) { + snd_card_free(card); + return err; + } +#endif + + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + str = "TRID4DWAVEDX"; + break; + case TRIDENT_DEVICE_ID_NX: + str = "TRID4DWAVENX"; + break; + case TRIDENT_DEVICE_ID_SI7018: + str = "SI7018"; + break; + default: + str = "Unknown"; + } + strcpy(card->driver, str); + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + strcpy(card->shortname, "SiS "); + } else { + strcpy(card->shortname, "Trident "); + } + strcat(card->shortname, card->driver); + sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d", + card->shortname, trident->port, trident->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, trident); + dev++; + return 0; +} + +static void __devexit snd_trident_remove(struct pci_dev *pci) +{ + trident_t *trident = snd_magic_cast(trident_t, pci_get_drvdata(pci), return); + if (trident) + snd_card_free(trident->card); + pci_set_drvdata(pci, NULL); +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_card_trident_suspend(struct pci_dev *pci, u32 state) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return -ENXIO); + snd_trident_suspend(chip); + return 0; +} +static int snd_card_trident_resume(struct pci_dev *pci) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return -ENXIO); + snd_trident_resume(chip); + return 0; +} +#else +static void snd_card_trident_suspend(struct pci_dev *pci) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return); + snd_trident_suspend(chip); +} +static void snd_card_trident_resume(struct pci_dev *pci) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return); + snd_trident_resume(chip); +} +#endif +#endif + +static struct pci_driver driver = { + name: "Trident4DWaveAudio", + id_table: snd_trident_ids, + probe: snd_trident_probe, + remove: __devexit_p(snd_trident_remove), +#ifdef CONFIG_PM + suspend: snd_card_trident_suspend, + resume: snd_card_trident_resume, +#endif +}; + +static int __init alsa_card_trident_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("Trident 4DWave PCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_trident_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_trident_init) +module_exit(alsa_card_trident_exit) + +#ifndef MODULE + +/* format is: snd-trident=snd_enable,snd_index,snd_id, + snd_pcm_channels,snd_wavetable_size */ + +static int __init alsa_card_trident_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_pcm_channels[nr_dev]) == 2 && + get_option(&str,&snd_wavetable_size[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-trident=", alsa_card_trident_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/trident/trident_main.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,3636 @@ +/* + * Maintained by Jaroslav Kysela + * Originated by audio@tridentmicro.com + * Fri Feb 19 15:55:28 MST 1999 + * Routines for control of Trident 4DWave (DX and NX) chip + * + * BUGS: + * + * TODO: + * --- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t trident_t + +static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream); +static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream); +static void snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs); +#ifdef CONFIG_PM +static int snd_trident_set_power_state(snd_card_t *card, unsigned int power_state); +#endif + +/* + * common I/O routines + */ + + +#if 0 +static void snd_trident_print_voice_regs(trident_t *trident, int voice) +{ + unsigned int val, tmp; + + printk("Trident voice %i:\n", voice); + outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR)); + val = inl(TRID_REG(trident, CH_LBA)); + printk("LBA: 0x%x\n", val); + val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); + printk("GVSel: %i\n", val >> 31); + printk("Pan: 0x%x\n", (val >> 24) & 0x7f); + printk("Vol: 0x%x\n", (val >> 16) & 0xff); + printk("CTRL: 0x%x\n", (val >> 12) & 0x0f); + printk("EC: 0x%x\n", val & 0x0fff); + if (trident->device != TRIDENT_DEVICE_ID_NX) { + val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS)); + printk("CSO: 0x%x\n", val >> 16); + printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff); + printk("FMS: 0x%x\n", val & 0x0f); + val = inl(TRID_REG(trident, CH_DX_ESO_DELTA)); + printk("ESO: 0x%x\n", val >> 16); + printk("Delta: 0x%x\n", val & 0xffff); + val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL)); + } else { // TRIDENT_DEVICE_ID_NX + val = inl(TRID_REG(trident, CH_NX_DELTA_CSO)); + tmp = (val >> 24) & 0xff; + printk("CSO: 0x%x\n", val & 0x00ffffff); + val = inl(TRID_REG(trident, CH_NX_DELTA_ESO)); + tmp |= (val >> 16) & 0xff00; + printk("Delta: 0x%x\n", tmp); + printk("ESO: 0x%x\n", val & 0x00ffffff); + val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL)); + printk("Alpha: 0x%x\n", val >> 20); + printk("FMS: 0x%x\n", (val >> 16) & 0x0f); + } + printk("FMC: 0x%x\n", (val >> 14) & 3); + printk("RVol: 0x%x\n", (val >> 7) & 0x7f); + printk("CVol: 0x%x\n", val & 0x7f); +} +#endif + +/*--------------------------------------------------------------------------- + unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg) + + Description: This routine will do all of the reading from the external + CODEC (AC97). + + Parameters: ac97 - ac97 codec structure + reg - CODEC register index, from AC97 Hal. + + returns: 16 bit value read from the AC97. + + ---------------------------------------------------------------------------*/ +static unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg) +{ + unsigned int data = 0, treg; + unsigned short count = 0xffff; + unsigned long flags; + trident_t *trident = snd_magic_cast(trident_t, ac97->private_data, return -ENXIO); + + spin_lock_irqsave(&trident->reg_lock, flags); + if (trident->device == TRIDENT_DEVICE_ID_DX) { + data = (DX_AC97_BUSY_READ | (reg & 0x000000ff)); + outl(data, TRID_REG(trident, DX_ACR1_AC97_R)); + do { + data = inl(TRID_REG(trident, DX_ACR1_AC97_R)); + if ((data & DX_AC97_BUSY_READ) == 0) + break; + } while (--count); + } else if (trident->device == TRIDENT_DEVICE_ID_NX) { + data = (NX_AC97_BUSY_READ | (reg & 0x000000ff)); + treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY; + outl(data, TRID_REG(trident, treg)); + do { + data = inl(TRID_REG(trident, treg)); + if ((data & 0x00000C00) == 0) + break; + } while (--count); + } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff); + if (ac97->num == 1) + data |= SI_AC97_SECONDARY; + outl(data, TRID_REG(trident, SI_AC97_READ)); + do { + data = inl(TRID_REG(trident, SI_AC97_READ)); + if ((data & (SI_AC97_BUSY_READ)) == 0) + break; + } while (--count); + } + + if (count == 0) { + snd_printk("ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n", reg, data); + data = 0; + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + return ((unsigned short) (data >> 16)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata) + + Description: This routine will do all of the writing to the external + CODEC (AC97). + + Parameters: ac97 - ac97 codec structure + reg - CODEC register index, from AC97 Hal. + data - Lower 16 bits are the data to write to CODEC. + + returns: TRUE if everything went ok, else FALSE. + + ---------------------------------------------------------------------------*/ +static void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata) +{ + unsigned int address, data; + unsigned short count = 0xffff; + unsigned long flags; + trident_t *trident = snd_magic_cast(trident_t, ac97->private_data, return); + + data = ((unsigned long) wdata) << 16; + + spin_lock_irqsave(&trident->reg_lock, flags); + if (trident->device == TRIDENT_DEVICE_ID_DX) { + address = DX_ACR0_AC97_W; + + /* read AC-97 write register status */ + do { + if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0) + break; + } while (--count); + + data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff)); + } else if (trident->device == TRIDENT_DEVICE_ID_NX) { + address = NX_ACR1_AC97_W; + + /* read AC-97 write register status */ + do { + if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0) + break; + } while (--count); + + data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff)); + } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + address = SI_AC97_WRITE; + + /* read AC-97 write register status */ + do { + if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0) + break; + } while (--count); + + data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff); + if (ac97->num == 1) + data |= SI_AC97_SECONDARY; + } else { + address = 0; /* keep GCC happy */ + count = 0; /* return */ + } + + if (count == 0) { + spin_unlock_irqrestore(&trident->reg_lock, flags); + return; + } + outl(data, TRID_REG(trident, address)); + spin_unlock_irqrestore(&trident->reg_lock, flags); +} + +/*--------------------------------------------------------------------------- + void snd_trident_enable_eso(trident_t *trident) + + Description: This routine will enable end of loop interrupts. + End of loop interrupts will occur when a running + channel reaches ESO. + Also enables middle of loop interrupts. + + Parameters: trident - pointer to target device class for 4DWave. + + ---------------------------------------------------------------------------*/ + +static void snd_trident_enable_eso(trident_t * trident) +{ + unsigned int val; + + val = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); + val |= ENDLP_IE; + val |= MIDLP_IE; + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + val |= BANK_B_EN; + outl(val, TRID_REG(trident, T4D_LFO_GC_CIR)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_disable_eso(trident_t *trident) + + Description: This routine will disable end of loop interrupts. + End of loop interrupts will occur when a running + channel reaches ESO. + Also disables middle of loop interrupts. + + Parameters: + trident - pointer to target device class for 4DWave. + + returns: TRUE if everything went ok, else FALSE. + + ---------------------------------------------------------------------------*/ + +static void snd_trident_disable_eso(trident_t * trident) +{ + unsigned int tmp; + + tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); + tmp &= ~ENDLP_IE; + tmp &= ~MIDLP_IE; + outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_start_voice(trident_t * trident, unsigned int voice) + + Description: Start a voice, any channel 0 thru 63. + This routine automatically handles the fact that there are + more than 32 channels available. + + Parameters : voice - Voice number 0 thru n. + trident - pointer to target device class for 4DWave. + + Return Value: None. + + ---------------------------------------------------------------------------*/ + +void snd_trident_start_voice(trident_t * trident, unsigned int voice) +{ + unsigned int mask = 1 << (voice & 0x1f); + unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A; + + outl(mask, TRID_REG(trident, reg)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_stop_voice(trident_t * trident, unsigned int voice) + + Description: Stop a voice, any channel 0 thru 63. + This routine automatically handles the fact that there are + more than 32 channels available. + + Parameters : voice - Voice number 0 thru n. + trident - pointer to target device class for 4DWave. + + Return Value: None. + + ---------------------------------------------------------------------------*/ + +void snd_trident_stop_voice(trident_t * trident, unsigned int voice) +{ + unsigned int mask = 1 << (voice & 0x1f); + unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A; + + outl(mask, TRID_REG(trident, reg)); +} + +/*--------------------------------------------------------------------------- + int snd_trident_allocate_pcm_channel(trident_t *trident) + + Description: Allocate hardware channel in Bank B (32-63). + + Parameters : trident - pointer to target device class for 4DWave. + + Return Value: hardware channel - 32-63 or -1 when no channel is available + + ---------------------------------------------------------------------------*/ + +static int snd_trident_allocate_pcm_channel(trident_t * trident) +{ + int idx; + + if (trident->ChanPCMcnt >= trident->ChanPCM) + return -1; + for (idx = 31; idx >= 0; idx--) { + if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) { + trident->ChanMap[T4D_BANK_B] |= 1 << idx; + trident->ChanPCMcnt++; + return idx + 32; + } + } + return -1; +} + +/*--------------------------------------------------------------------------- + void snd_trident_free_pcm_channel(int channel) + + Description: Free hardware channel in Bank B (32-63) + + Parameters : trident - pointer to target device class for 4DWave. + channel - hardware channel number 0-63 + + Return Value: none + + ---------------------------------------------------------------------------*/ + +static void snd_trident_free_pcm_channel(trident_t *trident, int channel) +{ + if (channel < 32 || channel > 63) + return; + channel &= 0x1f; + if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) { + trident->ChanMap[T4D_BANK_B] &= ~(1 << channel); + trident->ChanPCMcnt--; + } +} + +/*--------------------------------------------------------------------------- + unsigned int snd_trident_allocate_synth_channel(void) + + Description: Allocate hardware channel in Bank A (0-31). + + Parameters : trident - pointer to target device class for 4DWave. + + Return Value: hardware channel - 0-31 or -1 when no channel is available + + ---------------------------------------------------------------------------*/ + +static int snd_trident_allocate_synth_channel(trident_t * trident) +{ + int idx; + + for (idx = 31; idx >= 0; idx--) { + if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) { + trident->ChanMap[T4D_BANK_A] |= 1 << idx; + trident->synth.ChanSynthCount++; + return idx; + } + } + return -1; +} + +/*--------------------------------------------------------------------------- + void snd_trident_free_synth_channel( int channel ) + + Description: Free hardware channel in Bank B (0-31). + + Parameters : trident - pointer to target device class for 4DWave. + channel - hardware channel number 0-63 + + Return Value: none + + ---------------------------------------------------------------------------*/ + +static void snd_trident_free_synth_channel(trident_t *trident, int channel) +{ + if (channel < 0 || channel > 31) + return; + channel &= 0x1f; + if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) { + trident->ChanMap[T4D_BANK_A] &= ~(1 << channel); + trident->synth.ChanSynthCount--; + } +} + +/*--------------------------------------------------------------------------- + snd_trident_write_voice_regs + + Description: This routine will complete and write the 5 hardware channel + registers to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + Each register field. + + ---------------------------------------------------------------------------*/ + +void snd_trident_write_voice_regs(trident_t * trident, + snd_trident_voice_t * voice) +{ + unsigned int FmcRvolCvol; + unsigned int regs[5]; + + regs[1] = voice->LBA; + regs[4] = (voice->GVSel << 31) | + ((voice->Pan & 0x0000007f) << 24) | + ((voice->CTRL & 0x0000000f) << 12); + FmcRvolCvol = ((voice->FMC & 3) << 14) | + ((voice->RVol & 0x7f) << 7) | + (voice->CVol & 0x7f); + + switch (trident->device) { + case TRIDENT_DEVICE_ID_SI7018: + regs[4] |= voice->number > 31 ? + (voice->Vol & 0x000003ff) : + ((voice->Vol & 0x00003fc) << (16-2)) | + (voice->EC & 0x00000fff); + regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f); + regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff); + regs[3] = (voice->Attribute << 16) | FmcRvolCvol; + break; + case TRIDENT_DEVICE_ID_DX: + regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) | + (voice->EC & 0x00000fff); + regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f); + regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff); + regs[3] = FmcRvolCvol; + break; + case TRIDENT_DEVICE_ID_NX: + regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) | + (voice->EC & 0x00000fff); + regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff); + regs[2] = ((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff); + regs[3] = (voice->Alpha << 20) | ((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol; + break; + default: + snd_BUG(); + } + + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outl(regs[0], TRID_REG(trident, CH_START + 0)); + outl(regs[1], TRID_REG(trident, CH_START + 4)); + outl(regs[2], TRID_REG(trident, CH_START + 8)); + outl(regs[3], TRID_REG(trident, CH_START + 12)); + outl(regs[4], TRID_REG(trident, CH_START + 16)); +} + +/*--------------------------------------------------------------------------- + snd_trident_write_cso_reg + + Description: This routine will write the new CSO offset + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + CSO - new CSO value + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_cso_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int CSO) +{ + voice->CSO = CSO; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + if (trident->device != TRIDENT_DEVICE_ID_NX) { + outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2); + } else { + outl((voice->Delta << 24) | (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO)); + } +} + +/*--------------------------------------------------------------------------- + snd_trident_write_vol_reg + + Description: This routine will write the new voice volume + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + Vol - new voice volume + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_vol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int Vol) +{ + voice->Vol = Vol; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2)); + break; + case TRIDENT_DEVICE_ID_SI7018: + // printk("voice->Vol = 0x%x\n", voice->Vol); + outw((voice->CTRL << 12) | voice->Vol, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); + break; + } +} + +/*--------------------------------------------------------------------------- + snd_trident_write_pan_reg + + Description: This routine will write the new voice pan + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + Pan - new pan value + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_pan_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int Pan) +{ + voice->Pan = Pan; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3)); +} + +/*--------------------------------------------------------------------------- + snd_trident_write_rvol_reg + + Description: This routine will write the new reverb volume + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + RVol - new reverb volume + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_rvol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int RVol) +{ + voice->RVol = RVol; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f), + TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL)); +} + +/*--------------------------------------------------------------------------- + snd_trident_write_cvol_reg + + Description: This routine will write the new chorus volume + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + CVol - new chorus volume + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_cvol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int CVol) +{ + voice->CVol = CVol; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f), + TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL)); +} + +/*--------------------------------------------------------------------------- + snd_trident_convert_rate + + Description: This routine converts rate in HZ to hardware delta value. + + Paramters: trident - pointer to target device class for 4DWave. + rate - Real or Virtual channel number. + + Returns: Delta value. + + ---------------------------------------------------------------------------*/ +unsigned int snd_trident_convert_rate(unsigned int rate) +{ + unsigned int delta; + + // We special case 44100 and 8000 since rounding with the equation + // does not give us an accurate enough value. For 11025 and 22050 + // the equation gives us the best answer. All other frequencies will + // also use the equation. JDW + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff; + return delta; +} + +/*--------------------------------------------------------------------------- + snd_trident_convert_adc_rate + + Description: This routine converts rate in HZ to hardware delta value. + + Paramters: trident - pointer to target device class for 4DWave. + rate - Real or Virtual channel number. + + Returns: Delta value. + + ---------------------------------------------------------------------------*/ +static unsigned int snd_trident_convert_adc_rate(unsigned int rate) +{ + unsigned int delta; + + // We special case 44100 and 8000 since rounding with the equation + // does not give us an accurate enough value. For 11025 and 22050 + // the equation gives us the best answer. All other frequencies will + // also use the equation. JDW + if (rate == 44100) + delta = 0x116a; + else if (rate == 8000) + delta = 0x6000; + else if (rate == 48000) + delta = 0x1000; + else + delta = ((48000 << 12) / rate) & 0x0000ffff; + return delta; +} + +/*--------------------------------------------------------------------------- + snd_trident_spurious_threshold + + Description: This routine converts rate in HZ to spurious threshold. + + Paramters: trident - pointer to target device class for 4DWave. + rate - Real or Virtual channel number. + + Returns: Delta value. + + ---------------------------------------------------------------------------*/ +unsigned int snd_trident_spurious_threshold(unsigned int rate, unsigned int period_size) +{ + unsigned int res = (rate * period_size) / 48000; + if (res < 64) + res = res / 2; + else + res -= 32; + return res; +} + +/*--------------------------------------------------------------------------- + snd_trident_control_mode + + Description: This routine returns a control mode for a PCM channel. + + Paramters: trident - pointer to target device class for 4DWave. + substream - PCM substream + + Returns: Control value. + + ---------------------------------------------------------------------------*/ +unsigned int snd_trident_control_mode(snd_pcm_substream_t *substream) +{ + unsigned int CTRL; + snd_pcm_runtime_t *runtime = substream->runtime; + + /* set ctrl mode + CTRL default: 8-bit (unsigned) mono, loop mode enabled + */ + CTRL = 0x00000001; + if (snd_pcm_format_width(runtime->format) == 16) + CTRL |= 0x00000008; // 16-bit data + if (snd_pcm_format_signed(runtime->format)) + CTRL |= 0x00000002; // signed data + if (runtime->channels > 1) + CTRL |= 0x00000004; // stereo data + return CTRL; +} + +/* + * PCM part + */ + +/*--------------------------------------------------------------------------- + snd_trident_ioctl + + Description: Device I/O control handler for playback/capture parameters. + + Paramters: substream - PCM substream class + cmd - what ioctl message to process + arg - additional message infoarg + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + /* FIXME: it seems that with small periods the behaviour of + trident hardware is unpredictable and interrupt generator + is broken */ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_hw_params + + Description: Set the hardware parameters for the playback device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0 && trident->tlb.entries) { + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + spin_lock_irqsave(&trident->reg_lock, flags); + voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + spin_unlock_irqrestore(&trident->reg_lock, flags); + if (voice->memblk == NULL) + return -ENOMEM; + } + + /* voice management */ + + if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) { + if (evoice == NULL) { + evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (evoice == NULL) + return -ENOMEM; + voice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = evoice = NULL; + } + } + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_hw_free + + Description: Release the hardware resources for the playback device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_playback_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice ? voice->extra : NULL; + + if (trident->tlb.entries && voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = NULL; + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_prepare + + Description: Prepare playback device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_playback_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[substream->number]; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + + /* set delta (rate) value */ + voice->Delta = snd_trident_convert_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + /* set Loop Begin Address */ + if (voice->memblk) + voice->LBA = voice->memblk->offset; + else + voice->LBA = runtime->dma_addr; + + voice->CSO = 0; + voice->ESO = runtime->buffer_size - 1; /* in samples */ + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 3; + voice->GVSel = 1; + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Vol = mix->vol; + voice->RVol = mix->rvol; + voice->CVol = mix->cvol; + voice->Pan = mix->pan; + voice->Attribute = 0; + + snd_trident_write_voice_regs(trident, voice); + + if (evoice != NULL) { + evoice->Delta = voice->Delta; + evoice->spurious_threshold = voice->spurious_threshold; + evoice->LBA = voice->LBA; + evoice->CSO = 0; + evoice->ESO = (runtime->period_size * 2) - 1; /* in samples */ + evoice->CTRL = voice->CTRL; + evoice->FMC = 3; + evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; + evoice->EC = 0; + evoice->Alpha = 0; + evoice->FMS = 0; + evoice->Vol = 0x3ff; /* mute */ + evoice->RVol = evoice->CVol = 0x7f; /* mute */ + evoice->Pan = 0x7f; /* mute */ +#if 0 + evoice->Attribute = (1<<(30-16))|(2<<(26-16))| + (1<<(24-16))|(0x1f<<(19-16)); +#else + evoice->Attribute = 0; +#endif + snd_trident_write_voice_regs(trident, evoice); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_hw_params + + Description: Set the hardware parameters for the capture device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0 && trident->tlb.entries) { + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + spin_lock_irqsave(&trident->reg_lock, flags); + voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + spin_unlock_irqrestore(&trident->reg_lock, flags); + if (voice->memblk == NULL) + return -ENOMEM; + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_hw_free + + Description: Release the hardware resources for the capture device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + + if (trident->tlb.entries && voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + snd_pcm_lib_free_pages(substream); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_prepare + + Description: Prepare capture device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int val, ESO_bytes; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + + // Initilize the channel and set channel Mode + outb(0, TRID_REG(trident, LEGACY_DMAR15)); + + // Set DMA channel operation mode register + outb(0x54, TRID_REG(trident, LEGACY_DMAR11)); + + // Set channel buffer Address + voice->LBA = runtime->dma_addr; + outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0)); + if (voice->memblk) + voice->LBA = voice->memblk->offset; + + // set ESO + ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1; + outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6)); + outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4)); + ESO_bytes++; + + // Set channel sample rate, 4.12 format + val = ((unsigned int) 48000L << 12) / runtime->rate; + outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R)); + + // Set channel interrupt blk length + if (snd_pcm_format_width(runtime->format) == 16) { + val = (unsigned short) ((ESO_bytes >> 1) - 1); + } else { + val = (unsigned short) (ESO_bytes - 1); + } + + outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL)); + + // Right now, set format and start to run captureing, + // continuous run loop enable. + trident->bDMAStart = 0x19; // 0001 1001b + + if (snd_pcm_format_width(runtime->format) == 16) + trident->bDMAStart |= 0x80; + if (snd_pcm_format_signed(runtime->format)) + trident->bDMAStart |= 0x20; + if (runtime->channels > 1) + trident->bDMAStart |= 0x40; + + // Prepare capture intr channel + + voice->Delta = snd_trident_convert_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + // Set voice parameters + voice->CSO = 0; + /* the +2 is a correction for a h/w problem. if not + used, the ESO interrupt is received before the capture pointer + has actually reached the ESO point. this causes errors in + the mid-level code. + */ + voice->ESO = (runtime->period_size * 2) + 2 - 1; + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 3; + voice->RVol = 0x7f; + voice->CVol = 0x7f; + voice->GVSel = 1; + voice->Pan = 0x7f; /* mute */ + voice->Vol = 0x3ff; /* mute */ + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Attribute = 0; + + snd_trident_write_voice_regs(trident, voice); + + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_si7018_capture_hw_params + + Description: Set the hardware parameters for the capture device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_si7018_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + + /* voice management */ + + if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) { + if (evoice == NULL) { + evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (evoice == NULL) + return -ENOMEM; + voice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = evoice = NULL; + } + } + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_si7018_capture_hw_free + + Description: Release the hardware resources for the capture device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_si7018_capture_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice ? voice->extra : NULL; + + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = NULL; + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_si7018_capture_prepare + + Description: Prepare capture device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_si7018_capture_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + + voice->LBA = runtime->dma_addr; + voice->Delta = snd_trident_convert_adc_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + // Set voice parameters + voice->CSO = 0; + voice->ESO = runtime->buffer_size - 1; /* in samples */ + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 0; + voice->RVol = 0; + voice->CVol = 0; + voice->GVSel = 1; + voice->Pan = T4D_DEFAULT_PCM_PAN; + voice->Vol = 0; + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + + voice->Attribute = (2 << (30-16)) | + (2 << (26-16)) | + (2 << (24-16)) | + (1 << (23-16)); + + snd_trident_write_voice_regs(trident, voice); + + if (evoice != NULL) { + evoice->Delta = snd_trident_convert_rate(runtime->rate); + evoice->spurious_threshold = voice->spurious_threshold; + evoice->LBA = voice->LBA; + evoice->CSO = 0; + evoice->ESO = (runtime->period_size * 2) + 8 - 1; /* in samples, 8 means correction */ + evoice->CTRL = voice->CTRL; + evoice->FMC = 3; + evoice->GVSel = 0; + evoice->EC = 0; + evoice->Alpha = 0; + evoice->FMS = 0; + evoice->Vol = 0x3ff; /* mute */ + evoice->RVol = evoice->CVol = 0x7f; /* mute */ + evoice->Pan = 0x7f; /* mute */ + evoice->Attribute = 0; + snd_trident_write_voice_regs(trident, evoice); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_hw_params + + Description: Set the hardware parameters for the foldback device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_foldback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0 && trident->tlb.entries) { + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + spin_lock_irqsave(&trident->reg_lock, flags); + voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + spin_unlock_irqrestore(&trident->reg_lock, flags); + if (voice->memblk == NULL) + return -ENOMEM; + } + + /* voice management */ + + if (params_buffer_size(hw_params) / 2 != params_buffer_size(hw_params)) { + if (evoice == NULL) { + evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (evoice == NULL) + return -ENOMEM; + voice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = evoice = NULL; + } + } + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_hw_free + + Description: Release the hardware resources for the foldback device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_foldback_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice ? voice->extra : NULL; + + if (trident->tlb.entries && voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = NULL; + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_prepare + + Description: Prepare foldback capture device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_foldback_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + + /* Set channel buffer Address */ + if (voice->memblk) + voice->LBA = voice->memblk->offset; + else + voice->LBA = runtime->dma_addr; + + /* set target ESO for channel */ + voice->ESO = runtime->buffer_size - 1; /* in samples */ + + /* set sample rate */ + voice->Delta = 0x1000; + voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size); + + voice->CSO = 0; + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 3; + voice->RVol = 0x7f; + voice->CVol = 0x7f; + voice->GVSel = 1; + voice->Pan = 0x7f; /* mute */ + voice->Vol = 0x3ff; /* mute */ + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Attribute = 0; + + /* set up capture channel */ + outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan)); + + snd_trident_write_voice_regs(trident, voice); + + if (evoice != NULL) { + evoice->Delta = voice->Delta; + evoice->spurious_threshold = voice->spurious_threshold; + evoice->LBA = voice->LBA; + evoice->CSO = 0; + evoice->ESO = (runtime->period_size * 2) - 1; /* in samples */ + evoice->CTRL = voice->CTRL; + evoice->FMC = 3; + evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; + evoice->EC = 0; + evoice->Alpha = 0; + evoice->FMS = 0; + evoice->Vol = 0x3ff; /* mute */ + evoice->RVol = evoice->CVol = 0x7f; /* mute */ + evoice->Pan = 0x7f; /* mute */ + evoice->Attribute = 0; + snd_trident_write_voice_regs(trident, evoice); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_hw_params + + Description: Set the hardware parameters for the spdif device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned long flags; + unsigned int old_bits = 0, change = 0; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0 && trident->tlb.entries) { + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + spin_lock_irqsave(&trident->reg_lock, flags); + voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + spin_unlock_irqrestore(&trident->reg_lock, flags); + if (voice->memblk == NULL) + return -ENOMEM; + } + + /* prepare SPDIF channel */ + spin_lock_irqsave(&trident->reg_lock, flags); + old_bits = trident->spdif_pcm_bits; + if (old_bits & IEC958_AES0_PROFESSIONAL) + trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS; + else + trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24); + if (params_rate(hw_params) >= 48000) { + trident->spdif_pcm_ctrl = 0x3c; // 48000 Hz + trident->spdif_pcm_bits |= + trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? + IEC958_AES0_PRO_FS_48000 : + (IEC958_AES3_CON_FS_48000 << 24); + } + else if (params_rate(hw_params) >= 44100) { + trident->spdif_pcm_ctrl = 0x3e; // 44100 Hz + trident->spdif_pcm_bits |= + trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? + IEC958_AES0_PRO_FS_44100 : + (IEC958_AES3_CON_FS_44100 << 24); + } + else { + trident->spdif_pcm_ctrl = 0x3d; // 32000 Hz + trident->spdif_pcm_bits |= + trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? + IEC958_AES0_PRO_FS_32000 : + (IEC958_AES3_CON_FS_32000 << 24); + } + change = old_bits != trident->spdif_pcm_bits; + spin_unlock_irqrestore(&trident->reg_lock, flags); + + if (change) + snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id); + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_hw_free + + Description: Release the hardware resources for the spdif device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + + if (trident->tlb.entries && voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + snd_pcm_lib_free_pages(substream); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_prepare + + Description: Prepare SPDIF device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int RESO, LBAO; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + + /* set delta (rate) value */ + voice->Delta = snd_trident_convert_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + /* set Loop Back Address */ + LBAO = runtime->dma_addr; + if (voice->memblk) + voice->LBA = voice->memblk->offset; + else + voice->LBA = LBAO; + + /* set target ESO for channel */ + RESO = runtime->buffer_size - 1; + voice->ESO = (runtime->period_size * 2) - 1; + + /* set ctrl mode */ + voice->CTRL = snd_trident_control_mode(substream); + + voice->FMC = 3; + voice->RVol = 0x7f; + voice->CVol = 0x7f; + voice->GVSel = 1; + voice->Pan = 0x7f; + voice->Vol = 0x3ff; + voice->EC = 0; + voice->CSO = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Attribute = 0; + + /* prepare surrogate IRQ channel */ + snd_trident_write_voice_regs(trident, voice); + + outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO)); + outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2)); + outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA)); + outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO)); + outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2)); + + /* set SPDIF setting */ + outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); + + spin_unlock_irqrestore(&trident->reg_lock, flags); + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_trigger + + Description: Start/stop devices + + Parameters: substream - PCM substream class + cmd - trigger command (STOP, GO) + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_trigger(snd_pcm_substream_t *substream, + int cmd) + +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_substream_t *s; + unsigned int what, whati, capture_flag, spdif_flag; + snd_trident_voice_t *voice, *evoice; + unsigned int val, go; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + go = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + go = 0; + break; + default: + return -EINVAL; + } + what = whati = capture_flag = spdif_flag = 0; + s = substream; + spin_lock(&trident->reg_lock); + val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; + do { + if ((trident_t *) _snd_pcm_chip(s->pcm) == trident) { + voice = (snd_trident_voice_t *) s->runtime->private_data; + evoice = voice->extra; + what |= 1 << (voice->number & 0x1f); + if (evoice == NULL) { + whati |= 1 << (voice->number & 0x1f); + } else { + what |= 1 << (evoice->number & 0x1f); + whati |= 1 << (evoice->number & 0x1f); + } + if (go) { + voice->running = 1; + voice->stimer = val; + } else { + voice->running = 0; + } + snd_pcm_trigger_done(s, substream); + if (voice->capture) + capture_flag = 1; + if (voice->spdif) + spdif_flag = 1; + } + s = s->link_next; + } while (s != substream); + if (spdif_flag) { + outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + } + if (!go) + outl(what, TRID_REG(trident, T4D_STOP_B)); + val = inl(TRID_REG(trident, T4D_AINTEN_B)); + if (go) { + val |= whati; + } else { + val &= ~whati; + } + outl(val, TRID_REG(trident, T4D_AINTEN_B)); + if (go) { + outl(what, TRID_REG(trident, T4D_START_B)); + + if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018) + outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); + } else { + if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018) + outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); + } + spin_unlock(&trident->reg_lock); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_pointer + + Description: This routine return the playback position + + Parameters: substream - PCM substream class + + Returns: position of buffer + + ---------------------------------------------------------------------------*/ + +static snd_pcm_uframes_t snd_trident_playback_pointer(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int cso; + + if (!voice->running) + return 0; + + spin_lock(&trident->reg_lock); + + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + + if (trident->device != TRIDENT_DEVICE_ID_NX) { + cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2)); + } else { // ID_4DWAVE_NX + cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff; + } + + if (++cso > runtime->buffer_size) + cso = runtime->buffer_size; + + spin_unlock(&trident->reg_lock); + + return cso; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_pointer + + Description: This routine return the capture position + + Paramters: pcm1 - PCM device class + + Returns: position of buffer + + ---------------------------------------------------------------------------*/ + +static snd_pcm_uframes_t snd_trident_capture_pointer(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int result; + + if (!voice->running) + return 0; + + result = inw(TRID_REG(trident, T4D_SBBL_SBCL)); + if (runtime->channels > 1) + result >>= 1; + result = runtime->buffer_size - result; + + // printk("capture result = 0x%x, cso = 0x%x\n", result, cso); + + return result; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_pointer + + Description: This routine return the SPDIF playback position + + Parameters: substream - PCM substream class + + Returns: position of buffer + + ---------------------------------------------------------------------------*/ + +static snd_pcm_uframes_t snd_trident_spdif_pointer(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int result; + + if (!voice->running) + return 0; + + result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff; + + return result; +} + +/* + * Playback support device description + */ + +static snd_pcm_hardware_t snd_trident_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (256*1024), + period_bytes_min: 64, + period_bytes_max: (256*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * Capture support device description + */ + +static snd_pcm_hardware_t snd_trident_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * Foldback capture support device description + */ + +static snd_pcm_hardware_t snd_trident_foldback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_48000, + rate_min: 48000, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * SPDIF playback support device description + */ + +static snd_pcm_hardware_t snd_trident_spdif = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + rate_min: 32000, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static void snd_trident_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + unsigned long flags; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + trident_t *trident; + + if (voice) { + trident = voice->trident; + spin_lock_irqsave(&trident->reg_lock, flags); + snd_trident_free_voice(trident, voice); + spin_unlock_irqrestore(&trident->reg_lock, flags); + } +} + +static int snd_trident_playback_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice; + + spin_lock_irq(&trident->reg_lock); + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) { + spin_unlock_irq(&trident->reg_lock); + return -EAGAIN; + } + spin_unlock_irq(&trident->reg_lock); + snd_trident_pcm_mixer_build(trident, voice, substream); + voice->substream = substream; + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_playback; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_close + + Description: This routine will close the 4DWave playback device. For now + we will simply free the dma transfer buffer. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ +static int snd_trident_playback_close(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + + snd_trident_pcm_mixer_free(trident, voice, substream); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_open + + Description: This routine will open the 4DWave SPDIF device. + + Parameters: substream - PCM substream class + + Returns: status - success or failure flag + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irq(&trident->reg_lock); + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) { + spin_unlock_irq(&trident->reg_lock); + return -EAGAIN; + } + voice->spdif = 1; + voice->substream = substream; + trident->spdif_pcm_bits = trident->spdif_bits; + spin_unlock_irq(&trident->reg_lock); + + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_spdif; + + trident->spdif_pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + + +/*--------------------------------------------------------------------------- + snd_trident_spdif_close + + Description: This routine will close the 4DWave SPDIF device. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_close(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + + spin_lock_irq(&trident->reg_lock); + // restore default SPDIF setting + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + spin_unlock_irq(&trident->reg_lock); + trident->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_open + + Description: This routine will open the 4DWave capture device. + + Parameters: substream - PCM substream class + + Returns: status - success or failure flag + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irq(&trident->reg_lock); + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) { + spin_unlock_irq(&trident->reg_lock); + return -EAGAIN; + } + voice->capture = 1; + spin_unlock_irq(&trident->reg_lock); + voice->substream = substream; + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_capture; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_close + + Description: This routine will close the 4DWave capture device. For now + we will simply free the dma transfer buffer. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ +static int snd_trident_capture_close(snd_pcm_substream_t * substream) +{ + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_open + + Description: This routine will open the 4DWave foldback capture device. + + Parameters: substream - PCM substream class + + Returns: status - success or failure flag + + ---------------------------------------------------------------------------*/ + +static int snd_trident_foldback_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irq(&trident->reg_lock); + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) { + spin_unlock_irq(&trident->reg_lock); + return -EAGAIN; + } + if (trident->tlb.entries) { + voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + if (voice->memblk == NULL) { + snd_trident_free_voice(trident, voice); + spin_unlock_irq(&trident->reg_lock); + return -ENOMEM; + } + } + voice->substream = substream; + voice->foldback_chan = substream->number; + spin_unlock_irq(&trident->reg_lock); + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_foldback; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_close + + Description: This routine will close the 4DWave foldback capture device. + For now we will simply free the dma transfer buffer. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ +static int snd_trident_foldback_close(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + voice = (snd_trident_voice_t *) runtime->private_data; + + /* stop capture channel */ + spin_lock_irq(&trident->reg_lock); + outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan)); + spin_unlock_irq(&trident->reg_lock); + return 0; +} + +/*--------------------------------------------------------------------------- + PCM operations + ---------------------------------------------------------------------------*/ + +static snd_pcm_ops_t snd_trident_playback_ops = { + open: snd_trident_playback_open, + close: snd_trident_playback_close, + ioctl: snd_trident_ioctl, + hw_params: snd_trident_playback_hw_params, + hw_free: snd_trident_playback_hw_free, + prepare: snd_trident_playback_prepare, + trigger: snd_trident_trigger, + pointer: snd_trident_playback_pointer, +}; + +static snd_pcm_ops_t snd_trident_capture_ops = { + open: snd_trident_capture_open, + close: snd_trident_capture_close, + ioctl: snd_trident_ioctl, + hw_params: snd_trident_capture_hw_params, + hw_free: snd_trident_capture_hw_free, + prepare: snd_trident_capture_prepare, + trigger: snd_trident_trigger, + pointer: snd_trident_capture_pointer, +}; + +static snd_pcm_ops_t snd_trident_si7018_capture_ops = { + open: snd_trident_capture_open, + close: snd_trident_capture_close, + ioctl: snd_trident_ioctl, + hw_params: snd_trident_si7018_capture_hw_params, + hw_free: snd_trident_si7018_capture_hw_free, + prepare: snd_trident_si7018_capture_prepare, + trigger: snd_trident_trigger, + pointer: snd_trident_playback_pointer, +}; + +static snd_pcm_ops_t snd_trident_foldback_ops = { + open: snd_trident_foldback_open, + close: snd_trident_foldback_close, + ioctl: snd_trident_ioctl, + hw_params: snd_trident_foldback_hw_params, + hw_free: snd_trident_foldback_hw_free, + prepare: snd_trident_foldback_prepare, + trigger: snd_trident_trigger, + pointer: snd_trident_playback_pointer, +}; + +static snd_pcm_ops_t snd_trident_spdif_ops = { + open: snd_trident_spdif_open, + close: snd_trident_spdif_close, + ioctl: snd_trident_ioctl, + hw_params: snd_trident_spdif_hw_params, + hw_free: snd_trident_spdif_hw_free, + prepare: snd_trident_spdif_prepare, + trigger: snd_trident_trigger, + pointer: snd_trident_spdif_pointer, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_free + + Description: This routine release the 4DWave private data. + + Paramters: private_data - pointer to 4DWave device info. + + Returns: None + + ---------------------------------------------------------------------------*/ +static void snd_trident_pcm_free(snd_pcm_t *pcm) +{ + trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void snd_trident_foldback_pcm_free(snd_pcm_t *pcm) +{ + trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident->foldback = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void snd_trident_spdif_pcm_free(snd_pcm_t *pcm) +{ + trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident->spdif = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +/*--------------------------------------------------------------------------- + snd_trident_pcm + + Description: This routine registers the 4DWave device for PCM support. + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: None + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0) + return err; + + pcm->private_data = trident; + pcm->private_free = snd_trident_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + trident->device != TRIDENT_DEVICE_ID_SI7018 ? + &snd_trident_capture_ops : + &snd_trident_si7018_capture_ops); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, "Trident 4DWave"); + trident->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_pcm + + Description: This routine registers the 4DWave device for foldback PCM support. + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: None + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *foldback; + int err; + int num_chan = 3; + snd_pcm_substream_t *substream; + + if (rpcm) + *rpcm = NULL; + if (trident->device == TRIDENT_DEVICE_ID_NX) + num_chan = 4; + if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0) + return err; + + foldback->private_data = trident; + foldback->private_free = snd_trident_foldback_pcm_free; + snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); + foldback->info_flags = 0; + strcpy(foldback->name, "Trident 4DWave"); + substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + strcpy(substream->name, "Front Mixer"); + substream = substream->next; + strcpy(substream->name, "Reverb Mixer"); + substream = substream->next; + strcpy(substream->name, "Chorus Mixer"); + if (num_chan == 4) { + substream = substream->next; + strcpy(substream->name, "Second AC'97 ADC"); + } + trident->foldback = foldback; + + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024); + + if (rpcm) + *rpcm = foldback; + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif + + Description: This routine registers the 4DWave-NX device for SPDIF support. + + Paramters: trident - pointer to target device class for 4DWave-NX. + + Returns: None + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *spdif; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0) + return err; + + spdif->private_data = trident; + spdif->private_free = snd_trident_spdif_pcm_free; + snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops); + spdif->info_flags = 0; + strcpy(spdif->name, "Trident 4DWave IEC958"); + trident->spdif = spdif; + + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024); + + if (rpcm) + *rpcm = spdif; + return 0; +} + +/* + * Mixer part + */ + + +/*--------------------------------------------------------------------------- + snd_trident_spdif_control + + Description: enable/disable S/PDIF out from ac97 mixer + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_trident_spdif_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->spdif_ctrl; + ucontrol->value.integer.value[0] = val == kcontrol->private_value; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_spdif_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + int change; + + val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00; + spin_lock_irqsave(&trident->reg_lock, flags); + /* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */ + change = trident->spdif_ctrl != val; + trident->spdif_ctrl = val; + if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) { + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + } + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_spdif_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), + info: snd_trident_spdif_control_info, + get: snd_trident_spdif_control_get, + put: snd_trident_spdif_control_put, + private_value: 0x28, +}; + +/*--------------------------------------------------------------------------- + snd_trident_spdif_default + + Description: put/get the S/PDIF default settings + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_trident_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + spin_lock_irqsave(&trident->reg_lock, flags); + change = trident->spdif_bits != val; + trident->spdif_bits = val; + if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_spdif_default __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_trident_spdif_default_info, + get: snd_trident_spdif_default_get, + put: snd_trident_spdif_default_put +}; + +/*--------------------------------------------------------------------------- + snd_trident_spdif_mask + + Description: put/get the S/PDIF mask + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_trident_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static snd_kcontrol_new_t snd_trident_spdif_mask __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + info: snd_trident_spdif_mask_info, + get: snd_trident_spdif_mask_get, +}; + +/*--------------------------------------------------------------------------- + snd_trident_spdif_stream + + Description: put/get the S/PDIF stream settings + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_trident_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + spin_lock_irqsave(&trident->reg_lock, flags); + change = trident->spdif_pcm_bits != val; + trident->spdif_pcm_bits = val; + if (trident->spdif != NULL) + outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_spdif_stream __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_trident_spdif_stream_info, + get: snd_trident_spdif_stream_get, + put: snd_trident_spdif_stream_put +}; + +/*--------------------------------------------------------------------------- + snd_trident_ac97_control + + Description: enable/disable rear path for ac97 + ---------------------------------------------------------------------------*/ + +static int snd_trident_ac97_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_trident_ac97_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_ac97_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + int change = 0; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + val &= ~(1 << kcontrol->private_value); + if (ucontrol->value.integer.value[0]) + val |= 1 << kcontrol->private_value; + change = val != trident->ac97_ctrl; + trident->ac97_ctrl = val; + outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_ac97_rear_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Rear Path", + info: snd_trident_ac97_control_info, + get: snd_trident_ac97_control_get, + put: snd_trident_ac97_control_put, + private_value: 4, +}; + +/*--------------------------------------------------------------------------- + snd_trident_vol_control + + Description: wave & music volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_trident_vol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned int val; + + val = trident->musicvol_wavevol; + ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff); + ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff); + return 0; +} + +static int snd_trident_vol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->musicvol_wavevol; + val &= ~(0xffff << kcontrol->private_value); + val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) | + ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value; + change = val != trident->musicvol_wavevol; + outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_vol_music_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Music Playback Volume", + info: snd_trident_vol_control_info, + get: snd_trident_vol_control_get, + put: snd_trident_vol_control_put, + private_value: 16, +}; + +static snd_kcontrol_new_t snd_trident_vol_wave_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Wave Playback Volume", + info: snd_trident_vol_control_info, + get: snd_trident_vol_control_get, + put: snd_trident_vol_control_put, + private_value: 0, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_vol_control + + Description: PCM front volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + uinfo->value.integer.max = 1023; + return 0; +} + +static int snd_trident_pcm_vol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + ucontrol->value.integer.value[0] = 1023 - mix->vol; + } else { + ucontrol->value.integer.value[0] = 255 - (mix->vol>>2); + } + return 0; +} + +static int snd_trident_pcm_vol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned int val; + int change = 0; + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + val = 1023 - (ucontrol->value.integer.value[0] & 1023); + } else { + val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2; + } + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->vol; + mix->vol = val; + if (mix->voice != NULL) + snd_trident_write_vol_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_vol_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Front Playback Volume", + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + info: snd_trident_pcm_vol_control_info, + get: snd_trident_pcm_vol_control_get, + put: snd_trident_pcm_vol_control_put, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_pan_control + + Description: PCM front pan control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_pan_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_trident_pcm_pan_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = mix->pan; + if (ucontrol->value.integer.value[0] & 0x40) { + ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)); + } else { + ucontrol->value.integer.value[0] |= 0x40; + } + return 0; +} + +static int snd_trident_pcm_pan_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned char val; + int change = 0; + + if (ucontrol->value.integer.value[0] & 0x40) + val = ucontrol->value.integer.value[0] & 0x3f; + else + val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40; + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->pan; + mix->pan = val; + if (mix->voice != NULL) + snd_trident_write_pan_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_pan_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Pan Playback Control", + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + info: snd_trident_pcm_pan_control_info, + get: snd_trident_pcm_pan_control_get, + put: snd_trident_pcm_pan_control_put, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_rvol_control + + Description: PCM reverb volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_rvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_trident_pcm_rvol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = 127 - mix->rvol; + return 0; +} + +static int snd_trident_pcm_rvol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned short val; + int change = 0; + + val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->rvol; + mix->rvol = val; + if (mix->voice != NULL) + snd_trident_write_rvol_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_rvol_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Reverb Playback Volume", + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + info: snd_trident_pcm_rvol_control_info, + get: snd_trident_pcm_rvol_control_get, + put: snd_trident_pcm_rvol_control_put, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_cvol_control + + Description: PCM chorus volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_cvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_trident_pcm_cvol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = 127 - mix->cvol; + return 0; +} + +static int snd_trident_pcm_cvol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned short val; + int change = 0; + + val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->cvol; + mix->cvol = val; + if (mix->voice != NULL) + snd_trident_write_cvol_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_cvol_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Chorus Playback Volume", + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + info: snd_trident_pcm_cvol_control_info, + get: snd_trident_pcm_cvol_control_get, + put: snd_trident_pcm_cvol_control_put, +}; + +static void snd_trident_notify_pcm_change1(snd_card_t * card, snd_kcontrol_t *kctl, int activate) +{ + snd_runtime_check(kctl != NULL, return); + if (activate) + kctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + else + kctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); +} + +static void snd_trident_notify_pcm_change(snd_card_t * card, snd_trident_pcm_mixer_t * tmix, int activate) +{ + snd_trident_notify_pcm_change1(card, tmix->ctl_vol, activate); + snd_trident_notify_pcm_change1(card, tmix->ctl_pan, activate); + snd_trident_notify_pcm_change1(card, tmix->ctl_rvol, activate); + snd_trident_notify_pcm_change1(card, tmix->ctl_cvol, activate); +} + +static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t *voice, snd_pcm_substream_t *substream) +{ + snd_trident_pcm_mixer_t *tmix; + + snd_assert(trident != NULL && voice != NULL && substream != NULL, return -EINVAL); + tmix = &trident->pcm_mixer[substream->number]; + tmix->voice = voice; + tmix->vol = T4D_DEFAULT_PCM_VOL; + tmix->pan = T4D_DEFAULT_PCM_PAN; + tmix->rvol = T4D_DEFAULT_PCM_RVOL; + tmix->cvol = T4D_DEFAULT_PCM_CVOL; + snd_trident_notify_pcm_change(trident->card, tmix, 1); + return 0; +} + +static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t *voice, snd_pcm_substream_t *substream) +{ + snd_trident_pcm_mixer_t *tmix; + + snd_assert(trident != NULL && substream != NULL, return -EINVAL); + tmix = &trident->pcm_mixer[substream->number]; + tmix->voice = NULL; + snd_trident_notify_pcm_change(trident->card, tmix, 0); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_mixer + + Description: This routine registers the 4DWave device for mixer support. + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: None + + ---------------------------------------------------------------------------*/ + +static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device) +{ + ac97_t _ac97, *ac97; + snd_card_t * card = trident->card; + snd_kcontrol_t *kctl; + snd_ctl_elem_value_t uctl; + int idx, err; + + memset(&uctl, 0, sizeof(uctl)); + + memset(&_ac97, 0, sizeof(_ac97)); + _ac97.write = snd_trident_codec_write; + _ac97.read = snd_trident_codec_read; + _ac97.private_data = trident; + if ((err = snd_ac97_mixer(trident->card, &_ac97, &ac97)) < 0) + return err; + + if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + } else { + outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + } + + for (idx = 0; idx < 32; idx++) { + snd_trident_pcm_mixer_t *tmix; + + tmix = &trident->pcm_mixer[idx]; + tmix->voice = NULL; + if ((kctl = tmix->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + + if ((kctl = tmix->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + + if ((kctl = tmix->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + + if ((kctl = tmix->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + } + + if (trident->device == TRIDENT_DEVICE_ID_NX) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_default, trident))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + trident->spdif_pcm_ctl = kctl; + } + + return 0; +} + +/* + * /proc interface + */ + +static void snd_trident_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + trident_t *trident = snd_magic_cast(trident_t, entry->private_data, return); + char *s; + + switch (trident->device) { + case TRIDENT_DEVICE_ID_SI7018: + s = "SiS 7018 Audio"; + break; + case TRIDENT_DEVICE_ID_DX: + s = "Trident 4DWave PCI DX"; + break; + case TRIDENT_DEVICE_ID_NX: + s = "Trident 4DWave PCI NX"; + break; + default: + s = "???"; + } + snd_iprintf(buffer, "%s\n\n", s); + snd_iprintf(buffer, "Spurious IRQs : %d\n", trident->spurious_irq_count); + snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off"); + snd_iprintf(buffer, "Rear Speakers : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off"); + if (trident->tlb.entries) { + snd_iprintf(buffer,"\nVirtual Memory\n"); + snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size); + snd_iprintf(buffer, "Memory Used : %d\n", trident->tlb.memhdr->used); + snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr)); + } + } +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + snd_iprintf(buffer,"\nWavetable Synth\n"); + snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size); + snd_iprintf(buffer, "Memory Used : %d\n", trident->synth.current_size); + snd_iprintf(buffer, "Memory Free : %d\n", (trident->synth.max_size-trident->synth.current_size)); +#endif +} + +static void __devinit snd_trident_proc_init(trident_t * trident) +{ + snd_info_entry_t *entry; + char *s = "trident"; + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + s = "sis7018"; + if ((entry = snd_info_create_card_entry(trident->card, s, trident->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = trident; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_trident_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + trident->proc_entry = entry; +} + +static void snd_trident_proc_done(trident_t * trident) +{ + if (trident->proc_entry) { + snd_info_unregister(trident->proc_entry); + trident->proc_entry = NULL; + } +} + +static int snd_trident_dev_free(snd_device_t *device) +{ + trident_t *trident = snd_magic_cast(trident_t, device->device_data, return -ENXIO); + return snd_trident_free(trident); +} + +/*--------------------------------------------------------------------------- + snd_trident_create + + Description: This routine will create the device specific class for + the 4DWave card. It will also perform basic initialization. + + Paramters: card - which card to create + pci - interface to PCI bus resource info + dma1ptr - playback dma buffer + dma2ptr - capture dma buffer + irqptr - interrupt resource info + + Returns: 4DWave device class private data + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_create(snd_card_t * card, + struct pci_dev *pci, + int pcm_streams, + int pcm_spdif_device, + int max_wavetable_size, + trident_t ** rtrident) +{ + trident_t *trident; + unsigned int i; + int err; + signed long end_time; + snd_trident_voice_t *voice; + snd_trident_pcm_mixer_t *tmix; + static snd_device_ops_t ops = { + dev_free: snd_trident_dev_free, + }; + + *rtrident = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 30 bits */ + if (!pci_dma_supported(pci, 0x3fffffff)) { + snd_printk("architecture does not support 30bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x3fffffff); + + trident = snd_magic_kcalloc(trident_t, 0, GFP_KERNEL); + if (trident == NULL) + return -ENOMEM; + trident->device = (pci->vendor << 16) | pci->device; + trident->card = card; + trident->pci = pci; + spin_lock_init(&trident->reg_lock); + spin_lock_init(&trident->event_lock); + spin_lock_init(&trident->voice_alloc); + if (pcm_streams < 1) + pcm_streams = 1; + if (pcm_streams > 32) + pcm_streams = 32; + trident->ChanPCM = pcm_streams; + if (max_wavetable_size < 0 ) + max_wavetable_size = 0; + trident->synth.max_size = max_wavetable_size * 1024; + trident->port = pci_resource_start(pci, 0); + trident->irq = -1; + + trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE); + pci_set_master(pci); + trident->port = pci_resource_start(pci, 0); + + if ((trident->res_port = request_region(trident->port, 0x100, "Trident Audio")) == NULL) { + snd_trident_free(trident); + snd_printk("unable to grab I/O region 0x%lx-0x%lx\n", trident->port, trident->port + 0x100 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_trident_interrupt, SA_INTERRUPT|SA_SHIRQ, "Trident Audio", (void *) trident)) { + snd_trident_free(trident); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + trident->irq = pci->irq; + + /* allocate 16k-aligned TLB for NX cards */ + trident->tlb.entries = NULL; + trident->tlb.buffer = NULL; + if (trident->device == TRIDENT_DEVICE_ID_NX) { + /* allocate and setup TLB page table */ + /* each entry has 4 bytes (physical PCI address) */ + /* TLB array must be aligned to 16kB !!! so we allocate + 32kB region and correct offset when necessary */ + trident->tlb.buffer = snd_malloc_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer_dmaaddr); + if (trident->tlb.buffer == NULL) { + snd_trident_free(trident); + snd_printk("unable to allocate TLB buffer\n"); + return -ENOMEM; + } + trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); + trident->tlb.entries_dmaaddr = (trident->tlb.buffer_dmaaddr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); + /* allocate shadow TLB page table (virtual addresses) */ + trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long)); + if (trident->tlb.shadow_entries == NULL) { + snd_trident_free(trident); + snd_printk("unable to allocate shadow TLB entries\n"); + return -ENOMEM; + } + /* allocate and setup silent page and initialise TLB entries */ + trident->tlb.silent_page = snd_malloc_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page_dmaaddr); + if (trident->tlb.silent_page == 0UL) { + snd_trident_free(trident); + snd_printk("unable to allocate silent page\n"); + return -ENOMEM; + } + memset(trident->tlb.silent_page, 0, SNDRV_TRIDENT_PAGE_SIZE); + for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) { + trident->tlb.entries[i] = trident->tlb.silent_page_dmaaddr & ~(SNDRV_TRIDENT_PAGE_SIZE-1); + trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page; + } + + /* use emu memory block manager code to manage tlb page allocation */ + trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES); + if (trident->tlb.memhdr == NULL) { + snd_trident_free(trident); + return -ENOMEM; + } + trident->tlb.memhdr->block_extra_size = sizeof(snd_trident_memblk_arg_t); + } + + /* reset the legacy configuration and whole audio/wavetable block */ + if (trident->device == TRIDENT_DEVICE_ID_DX || + trident->device == TRIDENT_DEVICE_ID_NX) { + pci_write_config_dword(pci, 0x40, 0); /* DDMA */ + pci_write_config_byte(pci, 0x44, 0); /* ports */ + pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ + if (trident->device == TRIDENT_DEVICE_ID_DX) { + pci_write_config_byte(pci, 0x46, 4); /* reset */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0); /* release reset */ + udelay(100); + } else /* NX */ { + pci_write_config_byte(pci, 0x46, 1); /* reset */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0); /* release reset */ + udelay(100); + } + } + + /* initialize chip */ + + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + /* warm reset of the AC'97 codec */ + outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + udelay(100); + outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + /* DAC on, disable SB IRQ and try to force ADC valid signal */ + trident->ac97_ctrl = 0x0000004a; + outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0) + goto __dx_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("AC'97 codec ready error\n"); + snd_trident_free(trident); + return -EIO; + __dx_ok: + break; + case TRIDENT_DEVICE_ID_NX: + /* warm reset of the AC'97 codec */ + outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + udelay(100); + outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0) + goto __nx_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT))); + snd_trident_free(trident); + return -EIO; + __nx_ok: + /* DAC on */ + trident->ac97_ctrl = 0x00000002; + outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + /* disable SB IRQ */ + outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT)); + break; + case TRIDENT_DEVICE_ID_SI7018: + pci_write_config_byte(pci, 0x46, 0x04); /* SOFTWARE RESET */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0x00); + udelay(100); + /* disable AC97 GPIO interrupt */ + outb(0x00, TRID_REG(trident, SI_AC97_GPIO)); + /* initialize serial interface, force cold reset */ + i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET; + outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + udelay(1000); + /* remove cold reset */ + i &= ~COLD_RESET; + outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + udelay(2000); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) + goto __si7018_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL))); + snd_trident_free(trident); + return -EIO; + __si7018_ok: + /* enable 64 channel mode */ + outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR)); + break; + } + + outl(0xffffffff, TRID_REG(trident, T4D_STOP_A)); + outl(0xffffffff, TRID_REG(trident, T4D_STOP_B)); + outl(0, TRID_REG(trident, T4D_AINTEN_A)); + outl(0, TRID_REG(trident, T4D_AINTEN_B)); + + if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0) { + snd_trident_free(trident); + return err; + } + + if (trident->device == TRIDENT_DEVICE_ID_NX) { + if (trident->tlb.entries != NULL) { + /* enable virtual addressing via TLB */ + i = trident->tlb.entries_dmaaddr; + i |= 0x00000001; + outl(i, TRID_REG(trident, NX_TLBC)); + } else { + outl(0, TRID_REG(trident, NX_TLBC)); + } + /* initialize S/PDIF */ + trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + } + + /* initialise synth voices */ + for (i = 0; i < 64; i++) { + voice = &trident->synth.voices[i]; + voice->number = i; + voice->trident = trident; + } + /* initialize pcm mixer entries */ + for (i = 0; i < 32; i++) { + tmix = &trident->pcm_mixer[i]; + tmix->vol = T4D_DEFAULT_PCM_VOL; + tmix->pan = T4D_DEFAULT_PCM_PAN; + tmix->rvol = T4D_DEFAULT_PCM_RVOL; + tmix->cvol = T4D_DEFAULT_PCM_CVOL; + } + + snd_trident_enable_eso(trident); + +#ifdef CONFIG_PM + card->set_power_state = snd_trident_set_power_state; + card->power_state_private_data = trident; +#endif + + snd_trident_proc_init(trident); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) { + snd_trident_free(trident); + return err; + } + *rtrident = trident; + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_free + + Description: This routine will free the device specific class for + q the 4DWave card. + + Paramters: trident - device specific private data for 4DWave card + + Returns: None. + + ---------------------------------------------------------------------------*/ + +int snd_trident_free(trident_t *trident) +{ + snd_trident_disable_eso(trident); + // Disable S/PDIF out + if (trident->device == TRIDENT_DEVICE_ID_NX) + outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + snd_trident_proc_done(trident); + if (trident->tlb.buffer) { + outl(0, TRID_REG(trident, NX_TLBC)); + if (trident->tlb.memhdr) + snd_util_memhdr_free(trident->tlb.memhdr); + if (trident->tlb.silent_page) + snd_free_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); + if (trident->tlb.shadow_entries) + vfree(trident->tlb.shadow_entries); + snd_free_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, trident->tlb.buffer, trident->tlb.buffer_dmaaddr); + } + if (trident->irq >= 0) + free_irq(trident->irq, (void *)trident); + if (trident->res_port) { + release_resource(trident->res_port); + kfree_nocheck(trident->res_port); + } + snd_magic_kfree(trident); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_interrupt + + Description: ISR for Trident 4DWave device + + Paramters: trident - device specific private data for 4DWave card + + Problems: It seems that Trident chips generates interrupts more than + one time in special cases. The spurious interrupts are + detected via sample timer (T4D_STIMER) and computing + corresponding delta value. The limits are detected with + the method try & fail so it is possible that it won't + work on all computers. [jaroslav] + + Returns: None. + + ---------------------------------------------------------------------------*/ + +static void snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + trident_t *trident = snd_magic_cast(trident_t, dev_id, return); + unsigned int audio_int, chn_int, stimer, channel, mask; + int delta; + snd_trident_voice_t *voice; + + audio_int = inl(TRID_REG(trident, T4D_MISCINT)); + if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0) + return; + if (audio_int & ADDRESS_IRQ) { + // get interrupt status for all channels + spin_lock(&trident->reg_lock); + stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; + chn_int = inl(TRID_REG(trident, T4D_AINT_A)); + if (chn_int == 0) + goto __skip1; + outl(chn_int, TRID_REG(trident, T4D_AINT_A)); /* ack */ + __skip1: + chn_int = inl(TRID_REG(trident, T4D_AINT_B)); + if (chn_int == 0) + goto __skip2; + for (channel = 63; channel >= 32; channel--) { + mask = 1 << (channel&0x1f); + if ((chn_int & mask) == 0) + continue; + voice = &trident->synth.voices[channel]; + if (!voice->pcm || voice->substream == NULL) { + outl(mask, TRID_REG(trident, T4D_STOP_B)); + continue; + } + delta = (int)stimer - (int)voice->stimer; + if (delta < 0) + delta = -delta; + if (delta < voice->spurious_threshold) { + /* do some statistics here */ + trident->spurious_irq_count++; + if (trident->spurious_irq_max_delta < delta) + trident->spurious_irq_max_delta = delta; + continue; + } + voice->stimer = stimer; + if (voice->extra) { + /* update CSO for extra voice to preserve sync */ + snd_trident_stop_voice(trident, voice->extra->number); + snd_trident_write_cso_reg(trident, voice->extra, 0); + snd_trident_start_voice(trident, voice->extra->number); + } else if (voice->spdif) { + snd_trident_stop_voice(trident, voice->number); + snd_trident_write_cso_reg(trident, voice, 0); + snd_trident_start_voice(trident, voice->number); + } + spin_unlock(&trident->reg_lock); + snd_pcm_period_elapsed(voice->substream); + spin_lock(&trident->reg_lock); + } + outl(chn_int, TRID_REG(trident, T4D_AINT_B)); /* ack */ + __skip2: + spin_unlock(&trident->reg_lock); + } + if (audio_int & MPU401_IRQ) { + if (trident->rmidi) { + snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data, regs); + } else { + inb(TRID_REG(trident, T4D_MPUR0)); + } + } + // outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT)); +} + +/*--------------------------------------------------------------------------- + snd_trident_attach_synthesizer, snd_trident_detach_synthesizer + + Description: Attach/detach synthesizer hooks + + Paramters: trident - device specific private data for 4DWave card + + Returns: None. + + ---------------------------------------------------------------------------*/ +int snd_trident_attach_synthesizer(trident_t *trident) +{ +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT, + sizeof(trident_t*), &trident->seq_dev) >= 0) { + strcpy(trident->seq_dev->name, "4DWave"); + *(trident_t**)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident; + } +#endif + return 0; +} + +int snd_trident_detach_synthesizer(trident_t *trident) +{ +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (trident->seq_dev) { + snd_device_free(trident->card, trident->seq_dev); + trident->seq_dev = NULL; + } +#endif + return 0; +} + +snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int client, int port) +{ + snd_trident_voice_t *pvoice; + unsigned long flags; + int idx; + + spin_lock_irqsave(&trident->voice_alloc, flags); + if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) { + idx = snd_trident_allocate_pcm_channel(trident); + if(idx < 0) { + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return NULL; + } + pvoice = &trident->synth.voices[idx]; + pvoice->use = 1; + pvoice->pcm = 1; + pvoice->capture = 0; + pvoice->spdif = 0; + pvoice->memblk = NULL; + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return pvoice; + } + if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) { + idx = snd_trident_allocate_synth_channel(trident); + if(idx < 0) { + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return NULL; + } + pvoice = &trident->synth.voices[idx]; + pvoice->use = 1; + pvoice->synth = 1; + pvoice->client = client; + pvoice->port = port; + pvoice->memblk = NULL; + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return pvoice; + } + if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) { + } + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return NULL; +} + +void snd_trident_free_voice(trident_t * trident, snd_trident_voice_t *voice) +{ + unsigned long flags; + void (*private_free)(snd_trident_voice_t *); + void *private_data; + + if (voice == NULL || !voice->use) + return; + snd_trident_clear_voices(trident, voice->number, voice->number); + spin_lock_irqsave(&trident->voice_alloc, flags); + private_free = voice->private_free; + private_data = voice->private_data; + voice->private_free = NULL; + voice->private_data = NULL; + if (voice->pcm) + snd_trident_free_pcm_channel(trident, voice->number); + if (voice->synth) + snd_trident_free_synth_channel(trident, voice->number); + voice->use = voice->pcm = voice->synth = voice->midi = 0; + voice->capture = voice->spdif = 0; + voice->sample_ops = NULL; + voice->substream = NULL; + voice->extra = NULL; + spin_unlock_irqrestore(&trident->voice_alloc, flags); + if (private_free) + private_free(voice); +} + +void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max) +{ + unsigned int i, val, mask[2] = { 0, 0 }; + + snd_assert(v_min <= 63, return); + snd_assert(v_max <= 63, return); + for (i = v_min; i <= v_max; i++) + mask[i >> 5] |= 1 << (i & 0x1f); + if (mask[0]) { + outl(mask[0], TRID_REG(trident, T4D_STOP_A)); + val = inl(TRID_REG(trident, T4D_AINTEN_A)); + outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A)); + } + if (mask[1]) { + outl(mask[1], TRID_REG(trident, T4D_STOP_B)); + val = inl(TRID_REG(trident, T4D_AINTEN_B)); + outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B)); + } +} + +#ifdef CONFIG_PM + +void snd_trident_suspend(trident_t *trident) +{ + snd_card_t *card = trident->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + snd_pcm_suspend_all(trident->pcm); + if (trident->foldback) + snd_pcm_suspend_all(trident->foldback); + if (trident->spdif) + snd_pcm_suspend_all(trident->spdif); + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + break; /* TODO */ + case TRIDENT_DEVICE_ID_SI7018: + break; + } + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +void snd_trident_resume(trident_t *trident) +{ + snd_card_t *card = trident->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + break; /* TODO */ + case TRIDENT_DEVICE_ID_SI7018: + break; + } + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +static int snd_trident_set_power_state(snd_card_t *card, unsigned int power_state) +{ + trident_t *chip = snd_magic_cast(trident_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_trident_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_trident_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +EXPORT_SYMBOL(snd_trident_alloc_voice); +EXPORT_SYMBOL(snd_trident_free_voice); +EXPORT_SYMBOL(snd_trident_start_voice); +EXPORT_SYMBOL(snd_trident_stop_voice); +EXPORT_SYMBOL(snd_trident_write_voice_regs); +EXPORT_SYMBOL(snd_trident_clear_voices); +/* trident_memory.c symbols */ +EXPORT_SYMBOL(snd_trident_synth_alloc); +EXPORT_SYMBOL(snd_trident_synth_free); +EXPORT_SYMBOL(snd_trident_synth_bzero); +EXPORT_SYMBOL(snd_trident_synth_copy_from_user); diff -Nru a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/trident/trident_memory.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,428 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Copyright (c) by Takashi Iwai + * Copyright (c) by Scott McNab + * + * Trident 4DWave-NX memory page allocation (TLB area) + * Trident chip can handle only 16MByte of the memory at the same time. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +/* page arguments of these two macros are Trident page (4096 bytes), not like + * aligned pages in others + */ +#define __set_tlb_bus(trident,page,ptr,addr) \ + do { (trident)->tlb.entries[page] = (addr) & ~(SNDRV_TRIDENT_PAGE_SIZE-1); \ + (trident)->tlb.shadow_entries[page] = (ptr); } while (0) +#define __tlb_to_ptr(trident,page) \ + (void*)((trident)->tlb.shadow_entries[page]) +#define __tlb_to_addr(trident,page) \ + (dma_addr_t)((trident->tlb.entries[page]) & ~(SNDRV_TRIDENT_PAGE_SIZE - 1)) + +#if PAGE_SIZE == 4096 +/* page size == SNDRV_TRIDENT_PAGE_SIZE */ +#define ALIGN_PAGE_SIZE PAGE_SIZE /* minimum page size for allocation */ +#define MAX_ALIGN_PAGES SNDRV_TRIDENT_MAX_PAGES /* maxmium aligned pages */ +/* fill TLB entrie(s) corresponding to page with ptr */ +#define set_tlb_bus(trident,page,ptr,addr) __set_tlb_bus(trident,page,ptr,addr) +/* fill TLB entrie(s) corresponding to page with silence pointer */ +#define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr) +/* get aligned page from offset address */ +#define get_aligned_page(offset) ((offset) >> 12) +/* get offset address from aligned page */ +#define aligned_page_offset(page) ((page) << 12) +/* get buffer address from aligned page */ +#define page_to_ptr(trident,page) __tlb_to_ptr(trident, page) +/* get PCI physical address from aligned page */ +#define page_to_addr(trident,page) __tlb_to_addr(trident, page) + +#elif PAGE_SIZE == 8192 +/* page size == SNDRV_TRIDENT_PAGE_SIZE x 2*/ +#define ALIGN_PAGE_SIZE PAGE_SIZE +#define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / 2) +#define get_aligned_page(offset) ((offset) >> 13) +#define aligned_page_offset(page) ((page) << 13) +#define page_to_ptr(trident,page) __tlb_to_ptr(trident, (page) << 1) +#define page_to_addr(trident,page) __tlb_to_addr(trident, (page) << 1) + +/* fill TLB entries -- we need to fill two entries */ +static inline void set_tlb_bus(trident_t *trident, int page, unsigned long ptr, dma_addr_t addr) +{ + page <<= 1; + __set_tlb_bus(trident, page, ptr, addr); + __set_tlb_bus(trident, page+1, ptr + SNDRV_TRIDENT_PAGE_SIZE, addr + SNDRV_TRIDENT_PAGE_SIZE); +} +static inline void set_silent_tlb(trident_t *trident, int page) +{ + page <<= 1; + __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); + __set_tlb_bus(trident, page+1, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); +} + +#else +/* arbitrary size */ +#define UNIT_PAGES (PAGE_SIZE / SNDRV_TRIDENT_PAGE_SIZE) +#define ALIGN_PAGE_SIZE (SNDRV_TRIDENT_PAGE_SIZE * UNIT_PAGES) +#define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / UNIT_PAGES) +/* Note: if alignment doesn't match to the maximum size, the last few blocks + * become unusable. To use such blocks, you'll need to check the validity + * of accessing page in set_tlb_bus and set_silent_tlb. search_empty() + * should also check it, too. + */ +#define get_aligned_page(offset) ((offset) / ALIGN_PAGE_SIZE) +#define aligned_page_offset(page) ((page) * ALIGN_PAGE_SIZE) +#define page_to_ptr(trident,page) __tlb_to_ptr(trident, (page) * UNIT_PAGES) +#define page_to_addr(trident,page) __tlb_to_addr(trident, (page) * UNIT_PAGES) + +/* fill TLB entries -- UNIT_PAGES entries must be filled */ +static inline void set_tlb_bus(trident_t *trident, int page, unsigned long ptr, dma_addr_t addr) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) { + __set_tlb_bus(trident, page, ptr, addr); + ptr += SNDRV_TRIDENT_PAGE_SIZE; + addr += SNDRV_TRIDENT_PAGE_SIZE; + } +} +static inline void set_silent_tlb(trident_t *trident, int page) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) + __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); +} + +#endif /* PAGE_SIZE */ + +/* calculate buffer pointer from offset address */ +inline static void *offset_ptr(trident_t *trident, int offset) +{ + char *ptr; + ptr = page_to_ptr(trident, get_aligned_page(offset)); + ptr += offset % ALIGN_PAGE_SIZE; + return (void*)ptr; +} + +/* first and last (aligned) pages of memory block */ +#define firstpg(blk) (((snd_trident_memblk_arg_t*)snd_util_memblk_argptr(blk))->first_page) +#define lastpg(blk) (((snd_trident_memblk_arg_t*)snd_util_memblk_argptr(blk))->last_page) + +/* + * search empty pages which may contain given size + */ +static snd_util_memblk_t * +search_empty(snd_util_memhdr_t *hdr, int size) +{ + snd_util_memblk_t *blk, *prev; + int page, psize; + struct list_head *p; + + psize = get_aligned_page(size + ALIGN_PAGE_SIZE -1); + prev = NULL; + page = 0; + list_for_each(p, &hdr->block) { + blk = list_entry(p, snd_util_memblk_t, list); + if (page + psize <= firstpg(blk)) + goto __found_pages; + page = lastpg(blk) + 1; + } + if (page + psize > MAX_ALIGN_PAGES) + return NULL; + +__found_pages: + /* create a new memory block */ + blk = __snd_util_memblk_new(hdr, psize * ALIGN_PAGE_SIZE, p->prev); + if (blk == NULL) + return NULL; + blk->offset = aligned_page_offset(page); /* set aligned offset */ + firstpg(blk) = page; + lastpg(blk) = page + psize - 1; + return blk; +} + + +/* + * check if the given pointer is valid for pages + */ +static int is_valid_page(void *pages) +{ + unsigned long ptr = (unsigned long)virt_to_phys(pages); + if (ptr & ~0x3fffffffUL) { + snd_printk("max memory size is 1GB!!\n"); + return 0; + } + if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) { + snd_printk("page is not aligned\n"); + return 0; + } + return 1; +} + +/* + * page allocation for DMA + */ +snd_util_memblk_t * +snd_trident_alloc_pages(trident_t *trident, void *pages, dma_addr_t addr, unsigned long size) +{ + unsigned long ptr; + snd_util_memhdr_t *hdr; + snd_util_memblk_t *blk; + int page; + + snd_assert(trident != NULL, return NULL); + snd_assert(size > 0 && size < SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); + hdr = trident->tlb.memhdr; + snd_assert(hdr != NULL, return NULL); + + if (! is_valid_page(pages)) + return NULL; + + down(&hdr->block_mutex); + blk = search_empty(hdr, size); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + /* set TLB entries */ + ptr = (unsigned long)pages; + for (page = firstpg(blk); page <= lastpg(blk); page++) { + set_tlb_bus(trident, page, ptr, addr); + ptr += ALIGN_PAGE_SIZE; + addr += ALIGN_PAGE_SIZE; + } + up(&hdr->block_mutex); + return blk; +} + + +/* + * release DMA buffer from page table + */ +int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk) +{ + snd_util_memhdr_t *hdr; + int page; + + snd_assert(trident != NULL, return -EINVAL); + snd_assert(blk != NULL, return -EINVAL); + + hdr = trident->tlb.memhdr; + down(&hdr->block_mutex); + /* reset TLB entries */ + for (page = firstpg(blk); page <= lastpg(blk); page++) + set_silent_tlb(trident, page); + /* free memory block */ + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return 0; +} + + +/*---------------------------------------------------------------- + * memory allocation using multiple pages (for synth) + *---------------------------------------------------------------- + * Unlike the DMA allocation above, non-contiguous pages are + * assigned to TLB. + *----------------------------------------------------------------*/ + +/* + */ +static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk); +static int synth_free_pages(trident_t *hw, snd_util_memblk_t *blk); + +/* + * allocate a synth sample area + */ +snd_util_memblk_t * +snd_trident_synth_alloc(trident_t *hw, unsigned int size) +{ + snd_util_memblk_t *blk; + snd_util_memhdr_t *hdr = hw->tlb.memhdr; + + down(&hdr->block_mutex); + blk = __snd_util_mem_alloc(hdr, size); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + if (synth_alloc_pages(hw, blk)) { + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return NULL; + } + up(&hdr->block_mutex); + return blk; +} + + +/* + * free a synth sample area + */ +int +snd_trident_synth_free(trident_t *hw, snd_util_memblk_t *blk) +{ + snd_util_memhdr_t *hdr = hw->tlb.memhdr; + + down(&hdr->block_mutex); + synth_free_pages(hw, blk); + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return 0; +} + + +/* + * reset TLB entry and free kernel page + */ +static void clear_tlb(trident_t *trident, int page) +{ + void *ptr = page_to_ptr(trident, page); + dma_addr_t addr = page_to_addr(trident, page); + set_silent_tlb(trident, page); + snd_free_pci_pages(trident->pci, ALIGN_PAGE_SIZE, ptr, addr); +} + +/* check new allocation range */ +static void get_single_page_range(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk, int *first_page_ret, int *last_page_ret) +{ + struct list_head *p; + snd_util_memblk_t *q; + int first_page, last_page; + first_page = firstpg(blk); + if ((p = blk->list.prev) != &hdr->block) { + q = list_entry(p, snd_util_memblk_t, list); + if (lastpg(q) == first_page) + first_page++; /* first page was already allocated */ + } + last_page = lastpg(blk); + if ((p = blk->list.next) != &hdr->block) { + q = list_entry(p, snd_util_memblk_t, list); + if (firstpg(q) == last_page) + last_page--; /* last page was already allocated */ + } + *first_page_ret = first_page; + *last_page_ret = last_page; +} + +/* + * allocate kernel pages and assign them to TLB + */ +static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk) +{ + int page, first_page, last_page; + void *ptr; + dma_addr_t addr; + + firstpg(blk) = get_aligned_page(blk->offset); + lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1); + get_single_page_range(hw->tlb.memhdr, blk, &first_page, &last_page); + + /* allocate a kernel page for each Trident page - + * fortunately Trident page size and kernel PAGE_SIZE is identical! + */ + for (page = first_page; page <= last_page; page++) { + ptr = snd_malloc_pci_pages(hw->pci, ALIGN_PAGE_SIZE, &addr); + if (ptr == NULL) + goto __fail; + if (! is_valid_page(ptr)) { + snd_free_pci_pages(hw->pci, ALIGN_PAGE_SIZE, ptr, addr); + goto __fail; + } + set_tlb_bus(hw, page, (unsigned long)ptr, addr); + } + return 0; + +__fail: + /* release allocated pages */ + last_page = page - 1; + for (page = first_page; page <= last_page; page++) + clear_tlb(hw, page); + + return -ENOMEM; +} + +/* + * free pages + */ +static int synth_free_pages(trident_t *trident, snd_util_memblk_t *blk) +{ + int page, first_page, last_page; + + get_single_page_range(trident->tlb.memhdr, blk, &first_page, &last_page); + for (page = first_page; page <= last_page; page++) + clear_tlb(trident, page); + + return 0; +} + +/* + * bzero(blk + offset, size) + */ +int snd_trident_synth_bzero(trident_t *trident, snd_util_memblk_t *blk, int offset, int size) +{ + int page, nextofs, end_offset, temp, temp1; + + offset += blk->offset; + end_offset = offset + size; + page = get_aligned_page(offset) + 1; + do { + nextofs = aligned_page_offset(page); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + memset(offset_ptr(trident, offset), 0, temp); + offset = nextofs; + page++; + } while (offset < end_offset); + return 0; +} + +/* + * copy_from_user(blk + offset, data, size) + */ +int snd_trident_synth_copy_from_user(trident_t *trident, snd_util_memblk_t *blk, int offset, const char *data, int size) +{ + int page, nextofs, end_offset, temp, temp1; + + offset += blk->offset; + end_offset = offset + size; + page = get_aligned_page(offset) + 1; + do { + nextofs = aligned_page_offset(page); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + if (copy_from_user(offset_ptr(trident, offset), data, temp)) + return -EFAULT; + offset = nextofs; + data += temp; + page++; + } while (offset < end_offset); + return 0; +} + diff -Nru a/sound/pci/trident/trident_synth.c b/sound/pci/trident/trident_synth.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/trident/trident_synth.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1029 @@ +/* + * Routines for Trident 4DWave NX/DX soundcards - Synthesizer + * Copyright (c) by Scott McNab + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Scott McNab "); +MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer"); +MODULE_LICENSE("GPL"); + +/* linear to log pan conversion table (4.2 channel attenuation format) */ +static unsigned int pan_table[63] = { + 7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507, + 6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168, + 5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105, + 3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261, + 3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590, + 2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057, + 1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634, + 1588, 1543, 1499, 1456, 1415, 1375, 1336 +}; + +#define LOG_TABLE_SIZE 386 + +/* Linear half-attenuation to log conversion table in the format: + * {linear volume, logarithmic attenuation equivalent}, ... + * + * Provides conversion from a linear half-volume value in the range + * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB. + * Halving the linear volume is equivalent to an additional 6dB of + * logarithmic attenuation. The algorithm used in log_from_linear() + * therefore uses this table as follows: + * + * - loop and for every time the volume is less than half the maximum + * volume (16384), add another 6dB and halve the maximum value used + * for this comparison. + * - when the volume is greater than half the maximum volume, take + * the difference of the volume to half volume (in the range [0,8192]) + * and look up the log_table[] to find the nearest entry. + * - take the logarithic component of this entry and add it to the + * resulting attenuation. + * + * Thus this routine provides a linear->log conversion for a range of + * [0,16384] using only 386 table entries + * + * Note: although this table stores log attenuation in 8.8 format, values + * were only calculated for 6 bits fractional precision, since that is + * the most precision offered by the trident hardware. + */ + +static unsigned short log_table[LOG_TABLE_SIZE*2] = +{ + 4, 0x0604, 19, 0x0600, 34, 0x05fc, + 49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8, + 123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4, + 198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0, + 274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac, + 350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598, + 428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584, + 506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570, + 584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c, + 663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548, + 743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534, + 824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520, + 906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c, + 988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8, + 1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4, + 1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0, + 1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc, + 1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8, + 1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494, + 1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480, + 1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c, + 1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458, + 1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444, + 1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430, + 1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c, + 2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408, + 2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4, + 2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0, + 2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc, + 2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8, + 2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4, + 2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390, + 2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c, + 2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368, + 2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354, + 2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340, + 3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c, + 3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318, + 3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304, + 3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0, + 3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc, + 3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8, + 3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4, + 3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0, + 3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c, + 4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278, + 4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264, + 4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250, + 4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c, + 4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228, + 4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214, + 4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200, + 4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec, + 4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8, + 5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4, + 5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0, + 5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c, + 5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188, + 5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174, + 5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160, + 5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c, + 5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138, + 6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124, + 6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110, + 6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc, + 6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8, + 6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4, + 6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0, + 6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac, + 6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098, + 7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084, + 7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070, + 7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c, + 7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048, + 7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034, + 7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020, + 7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c, + 8133, 0x0008, 8162, 0x0004, 8192, 0x0000 +}; + +static unsigned short lookup_volume_table( unsigned short value ) +{ + /* This code is an optimised version of: + * int i = 0; + * while( volume_table[i*2] < value ) + * i++; + * return volume_table[i*2+1]; + */ + unsigned short *ptr = log_table; + while( *ptr < value ) + ptr += 2; + return *(ptr+1); +} + +/* this function calculates a 8.8 fixed point logarithmic attenuation + * value from a linear volume value in the range 0 to 16384 */ +static unsigned short log_from_linear( unsigned short value ) +{ + if (value >= 16384) + return 0x0000; + if (value) { + unsigned short result = 0; + int v, c; + for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) { + if( value >= v ) { + result += lookup_volume_table( (value - v) << c ); + return result; + } + result += 0x0605; /* 6.0205 (result of -20*log10(0.5)) */ + } + } + return 0xffff; +} + +/* + * Sample handling operations + */ + +static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position); +static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode); +static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq); +static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume); +static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop); +static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position); +static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data); + +static snd_trident_sample_ops_t sample_ops = +{ + sample_start, + sample_stop, + sample_freq, + sample_volume, + sample_loop, + sample_pos, + sample_private1 +}; + +static void snd_trident_simple_init(snd_trident_voice_t * voice) +{ + //voice->handler_wave = interrupt_wave; + //voice->handler_volume = interrupt_volume; + //voice->handler_effect = interrupt_effect; + //voice->volume_change = NULL; + voice->sample_ops = &sample_ops; +} + +static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position) +{ + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + unsigned long flags; + unsigned int loop_start, loop_end, sample_start, sample_end, start_offset; + unsigned int value; + unsigned int shift = 0; + + instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + + spin_lock_irqsave(&trident->reg_lock, flags); + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + voice->GVSel = 1; /* route to Wave volume */ + + voice->CTRL = 0; + voice->Alpha = 0; + voice->FMS = 0; + + loop_start = simple->loop_start >> 4; + loop_end = simple->loop_end >> 4; + sample_start = (simple->start + position) >> 4; + if( sample_start >= simple->size ) + sample_start = simple->start >> 4; + sample_end = simple->size; + start_offset = position >> 4; + + if (simple->format & SIMPLE_WAVE_16BIT) { + voice->CTRL |= 8; + shift++; + } + if (simple->format & SIMPLE_WAVE_STEREO) { + voice->CTRL |= 4; + shift++; + } + if (!(simple->format & SIMPLE_WAVE_UNSIGNED)) + voice->CTRL |= 2; + + voice->LBA = simple->address.memory; + + if (simple->format & SIMPLE_WAVE_LOOP) { + voice->CTRL |= 1; + voice->LBA += loop_start << shift; + if( start_offset >= loop_start ) { + voice->CSO = start_offset - loop_start; + voice->negCSO = 0; + } else { + voice->CSO = loop_start - start_offset; + voice->negCSO = 1; + } + voice->ESO = loop_end - loop_start - 1; + } else { + voice->LBA += start_offset << shift; + voice->CSO = sample_start; + voice->ESO = sample_end - 1; + voice->negCSO = 0; + } + + if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) { + snd_trident_stop_voice(trident, voice->number); + voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; + } + + /* set CSO sign */ + value = inl(TRID_REG(trident, T4D_SIGN_CSO_A)); + if( voice->negCSO ) { + value |= 1 << (voice->number&31); + } else { + value &= ~(1 << (voice->number&31)); + } + outl(value,TRID_REG(trident, T4D_SIGN_CSO_A)); + + voice->Attribute = 0; + snd_trident_write_voice_regs(trident, voice); + snd_trident_start_voice(trident, voice->number); + voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING; + spin_unlock_irqrestore(&trident->reg_lock, flags); + snd_seq_instr_free_use(trident->synth.ilist, instr); +} + +static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode) +{ + unsigned long flags; + + if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING)) + return; + + switch (mode) { + default: + spin_lock_irqsave(&trident->reg_lock, flags); + snd_trident_stop_voice(trident, voice->number); + voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; + spin_unlock_irqrestore(&trident->reg_lock, flags); + break; + case SAMPLE_STOP_LOOP: /* disable loop only */ + voice->CTRL &= ~1; + spin_lock_irqsave(&trident->reg_lock, flags); + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC); + spin_unlock_irqrestore(&trident->reg_lock, flags); + break; + } +} + +static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq) +{ + unsigned long flags; + freq >>= 4; + + spin_lock_irqsave(&trident->reg_lock, flags); + if (freq == 44100) + voice->Delta = 0xeb3; + else if (freq == 8000) + voice->Delta = 0x2ab; + else if (freq == 48000) + voice->Delta = 0x1000; + else + voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff; + + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3)); + outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3)); + } else { + outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA)); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); +} + +static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume) +{ + unsigned long flags; + unsigned short value; + + spin_lock_irqsave(&trident->reg_lock, flags); + voice->GVSel = 0; /* use global music volume */ + voice->FMC = 0x03; /* fixme: can we do something useful with FMC? */ + if (volume->volume >= 0) { + volume->volume &= 0x3fff; + /* linear volume -> logarithmic attenuation conversion + * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits) + * Vol register used when additional attenuation is required */ + voice->RVol = 0; + voice->CVol = 0; + value = log_from_linear( volume->volume ); + voice->Vol = 0; + voice->EC = (value & 0x3fff) >> 2; + if (value > 0x3fff) { + voice->EC |= 0xfc0; + if (value < 0x5f00 ) + voice->Vol = ((value >> 8) - 0x3f) << 5; + else { + voice->Vol = 0x3ff; + voice->EC = 0xfff; + } + } + } + if (volume->lr >= 0) { + volume->lr &= 0x3fff; + /* approximate linear pan by attenuating channels */ + if (volume->lr >= 0x2000) { /* attenuate left (pan right) */ + value = 0x3fff - volume->lr; + for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) + if (value >= pan_table[voice->Pan] ) + break; + } else { /* attenuate right (pan left) */ + for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) + if (volume->lr >= pan_table[voice->Pan] ) + break; + voice->Pan |= 0x40; + } + } + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) | + ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) | + (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); + value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f); + outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL)); + spin_unlock_irqrestore(&trident->reg_lock, flags); +} + +static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop) +{ + unsigned long flags; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + unsigned int loop_start, loop_end; + + instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + + loop_start = loop->start >> 4; + loop_end = loop->end >> 4; + + spin_lock_irqsave(&trident->reg_lock, flags); + + voice->LBA = simple->address.memory + loop_start; + voice->CSO = 0; + voice->ESO = loop_end - loop_start - 1; + + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2)); + outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA)); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2)); + outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO)); + outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2)); + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO)); + } else { + outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2)); + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2)); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + snd_seq_instr_free_use(trident->synth.ilist, instr); +} + +static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position) +{ + unsigned long flags; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + unsigned int value; + + instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + + spin_lock_irqsave(&trident->reg_lock, flags); + + if (simple->format & SIMPLE_WAVE_LOOP) { + if( position >= simple->loop_start ) { + voice->CSO = (position - simple->loop_start) >> 4; + voice->negCSO = 0; + } else { + voice->CSO = (simple->loop_start - position) >> 4; + voice->negCSO = 1; + } + } else { + voice->CSO = position >> 4; + voice->negCSO = 0; + } + + /* set CSO sign */ + value = inl(TRID_REG(trident, T4D_SIGN_CSO_A)); + if( voice->negCSO ) { + value |= 1 << (voice->number&31); + } else { + value &= ~(1 << (voice->number&31)); + } + outl(value,TRID_REG(trident, T4D_SIGN_CSO_A)); + + + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO)); + outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2)); + } else { + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + snd_seq_instr_free_use(trident->synth.ilist, instr); +} + +static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data) +{ +} + +/* + * Memory management / sample loading + */ + +static int snd_trident_simple_put_sample(void *private_data, simple_instrument_t * instr, + char *data, long len, int atomic) +{ + trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + unsigned char *block = NULL; + int size = instr->size; + int shift = 0; + + if (instr->format & SIMPLE_WAVE_BACKWARD || + instr->format & SIMPLE_WAVE_BIDIR || + instr->format & SIMPLE_WAVE_ULAW) + return -EINVAL; /* not supported */ + + if (instr->format & SIMPLE_WAVE_16BIT) + shift++; + if (instr->format & SIMPLE_WAVE_STEREO) + shift++; + size <<= shift; + + if (trident->synth.current_size + size > trident->synth.max_size) + return -ENOMEM; + + if (verify_area(VERIFY_READ, data, size)) + return -EFAULT; + + if (trident->tlb.entries) { + snd_util_memblk_t *memblk; + memblk = snd_trident_synth_alloc(trident,size); + if (memblk == NULL) + return -ENOMEM; + if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) { + snd_trident_synth_free(trident, memblk); + return -EFAULT; + } + instr->address.ptr = (unsigned char*)memblk; + instr->address.memory = memblk->offset; + } else { + dma_addr_t addr; + block = (unsigned char *) snd_malloc_pci_pages(trident->pci, size, &addr); + if (block == NULL) + return -ENOMEM; + + if (copy_from_user(block, data, size)) { + snd_free_pci_pages(trident->pci, size, block, addr); + return -EFAULT; + } + instr->address.ptr = block; + instr->address.memory = addr; + } + + trident->synth.current_size += size; + return 0; +} + +static int snd_trident_simple_get_sample(void *private_data, simple_instrument_t * instr, + char *data, long len, int atomic) +{ + //trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + int size = instr->size; + int shift = 0; + + if (instr->format & SIMPLE_WAVE_16BIT) + shift++; + if (instr->format & SIMPLE_WAVE_STEREO) + shift++; + size <<= shift; + + if (verify_area(VERIFY_WRITE, data, size)) + return -EFAULT; + + /* FIXME: not implemented yet */ + + return -EBUSY; +} + +static int snd_trident_simple_remove_sample(void *private_data, simple_instrument_t * instr, + int atomic) +{ + trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + int size = instr->size; + + if (trident->tlb.entries) { + snd_util_memblk_t *memblk = (snd_util_memblk_t*)instr->address.ptr; + if (memblk) + snd_trident_synth_free(trident, memblk); + else + return -EFAULT; + } else { + kfree(instr->address.ptr); + } + + if (instr->format & SIMPLE_WAVE_16BIT) + size <<= 1; + if (instr->format & SIMPLE_WAVE_STEREO) + size <<= 1; + + trident->synth.current_size -= size; + if (trident->synth.current_size < 0) /* shouldnt need this check... */ + trident->synth.current_size = 0; + + return 0; +} + +static void select_instrument(trident_t * trident, snd_trident_voice_t * v) +{ + snd_seq_kinstr_t *instr; + instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1); + if (instr != NULL) { + if (instr->ops) { + if (instr->ops->instr_type == snd_seq_simple_id) + snd_trident_simple_init(v); + } + snd_seq_instr_free_use(trident->synth.ilist, instr); + } +} + +/* + + */ + +static void event_sample(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.std = ev->data.sample.param.sample.std; + if (v->instr.std & 0xff000000) { /* private instrument */ + v->instr.std &= 0x00ffffff; + v->instr.std |= (unsigned int)ev->source.client << 24; + } + v->instr.bank = ev->data.sample.param.sample.bank; + v->instr.prg = ev->data.sample.param.sample.prg; + select_instrument(p->trident, v); +} + +static void event_cluster(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.cluster = ev->data.sample.param.cluster.cluster; + select_instrument(p->trident, v); +} + +static void event_start(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_start) + v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position); +} + +static void event_stop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode); +} + +static void event_freq(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_freq) + v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency); +} + +static void event_volume(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_volume) + v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume); +} + +static void event_loop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_loop) + v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop); +} + +static void event_position(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_pos) + v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position); +} + +static void event_private1(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_private1) + v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8); +} + +typedef void (trident_sample_event_handler_t) (snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v); + +static trident_sample_event_handler_t *trident_sample_event_handlers[9] = +{ + event_sample, + event_cluster, + event_start, + event_stop, + event_freq, + event_volume, + event_loop, + event_position, + event_private1 +}; + +static void snd_trident_sample_event(snd_seq_event_t * ev, snd_trident_port_t * p) +{ + int idx, voice; + trident_t *trident = p->trident; + snd_trident_voice_t *v; + unsigned long flags; + + idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; + if (idx < 0 || idx > 8) + return; + for (voice = 0; voice < 64; voice++) { + v = &trident->synth.voices[voice]; + if (v->use && v->client == ev->source.client && + v->port == ev->source.port && + v->index == ev->data.sample.channel) { + spin_lock_irqsave(&trident->event_lock, flags); + trident_sample_event_handlers[idx] (ev, p, v); + spin_unlock_irqrestore(&trident->event_lock, flags); + return; + } + } +} + +/* + + */ + +static void snd_trident_synth_free_voices(trident_t * trident, int client, int port) +{ + int idx; + snd_trident_voice_t *voice; + + for (idx = 0; idx < 32; idx++) { + voice = &trident->synth.voices[idx]; + if (voice->use && voice->client == client && voice->port == port) + snd_trident_free_voice(trident, voice); + } +} + +static int snd_trident_synth_use(void *private_data, snd_seq_port_subscribe_t * info) +{ + snd_trident_port_t *port = (snd_trident_port_t *) private_data; + trident_t *trident = port->trident; + snd_trident_voice_t *voice; + int idx; + unsigned long flags; + + if (info->voices > 32) + return -EINVAL; + spin_lock_irqsave(&trident->reg_lock, flags); + for (idx = 0; idx < info->voices; idx++) { + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); + if (voice == NULL) { + snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return -EBUSY; + } + voice->index = idx; + voice->Vol = 0x3ff; + voice->EC = 0x0fff; + } +#if 0 + for (idx = 0; idx < info->midi_voices; idx++) { + port->midi_has_voices = 1; + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port); + if (voice == NULL) { + snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return -EBUSY; + } + voice->Vol = 0x3ff; + voice->EC = 0x0fff; + } +#endif + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info) +{ + snd_trident_port_t *port = (snd_trident_port_t *) private_data; + trident_t *trident = port->trident; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +/* + + */ + +static void snd_trident_synth_free_private_instruments(snd_trident_port_t * p, int client) +{ + snd_seq_instr_header_t ifree; + + memset(&ifree, 0, sizeof(ifree)); + ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; + snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0); +} + +int snd_trident_synth_event_input(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop) +{ + snd_trident_port_t *p = (snd_trident_port_t *) private_data; + + if (p == NULL) + return -EINVAL; + if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && + ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { + snd_trident_sample_event(ev, p); + return 0; + } + if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && + ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { + if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { + snd_trident_synth_free_private_instruments(p, ev->data.addr.client); + return 0; + } + } + if (direct) { + if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { + snd_seq_instr_event(&p->trident->synth.simple_ops.kops, + p->trident->synth.ilist, ev, + p->trident->synth.seq_client, atomic, hop); + return 0; + } + } + return 0; +} + +static void snd_trident_synth_instr_notify(void *private_data, + snd_seq_kinstr_t * instr, + int what) +{ + int idx; + trident_t *trident = snd_magic_cast(trident_t, private_data, return); + snd_trident_voice_t *pvoice; + unsigned long flags; + + spin_lock_irqsave(&trident->event_lock, flags); + for (idx = 0; idx < 64; idx++) { + pvoice = &trident->synth.voices[idx]; + if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { + if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { + pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY); + } else { + snd_trident_stop_voice(trident, pvoice->number); + pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; + } + } + } + spin_unlock_irqrestore(&trident->event_lock, flags); +} + +/* + + */ + +static void snd_trident_synth_free_port(void *private_data) +{ + snd_trident_port_t *p = (snd_trident_port_t *) private_data; + + if (p) + snd_midi_channel_free_set(p->chset); +} + +static int snd_trident_synth_create_port(trident_t * trident, int idx) +{ + snd_trident_port_t *p; + snd_seq_port_callback_t callbacks; + char name[32]; + char *str; + int result; + + p = &trident->synth.seq_ports[idx]; + p->chset = snd_midi_channel_alloc_set(16); + if (p->chset == NULL) + return -ENOMEM; + p->chset->private_data = p; + p->trident = trident; + p->client = trident->synth.seq_client; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.use = snd_trident_synth_use; + callbacks.unuse = snd_trident_synth_unuse; + callbacks.event_input = snd_trident_synth_event_input; + callbacks.private_free = snd_trident_synth_free_port; + callbacks.private_data = p; + + str = "???"; + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break; + case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break; + case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break; + } + sprintf(name, "%s port %i", str, idx); + p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client, + &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_MIDI_GS | + SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | + SNDRV_SEQ_PORT_TYPE_SYNTH, + name); + if (p->chset->port < 0) { + result = p->chset->port; + snd_trident_synth_free_port(p); + return result; + } + p->port = p->chset->port; + return 0; +} + +/* + + */ + +static int snd_trident_synth_new_device(snd_seq_device_t *dev) +{ + trident_t *trident; + int client, i; + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + snd_seq_port_subscribe_t sub; + snd_simple_ops_t *simpleops; + char *str; + + trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (trident == NULL) + return -EINVAL; + + trident->synth.seq_client = -1; + + /* allocate new client */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = trident; + callbacks.allow_output = callbacks.allow_input = 1; + client = trident->synth.seq_client = + snd_seq_create_kernel_client(trident->card, 1, &callbacks); + if (client < 0) + return client; + + /* change name of client */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + str = "???"; + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break; + case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break; + case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break; + } + sprintf(cinfo.name, str); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + for (i = 0; i < 4; i++) + snd_trident_synth_create_port(trident, i); + + trident->synth.ilist = snd_seq_instr_list_new(); + if (trident->synth.ilist == NULL) { + snd_seq_delete_kernel_client(client); + trident->synth.seq_client = -1; + return -ENOMEM; + } + trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; + + simpleops = &trident->synth.simple_ops; + snd_seq_simple_init(simpleops, trident, NULL); + simpleops->put_sample = snd_trident_simple_put_sample; + simpleops->get_sample = snd_trident_simple_get_sample; + simpleops->remove_sample = snd_trident_simple_remove_sample; + simpleops->notify = snd_trident_synth_instr_notify; + + memset(&sub, 0, sizeof(sub)); + sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; + sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + sub.dest.client = client; + sub.dest.port = 0; + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); + + return 0; +} + +static int snd_trident_synth_delete_device(snd_seq_device_t *dev) +{ + trident_t *trident; + + trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (trident == NULL) + return -EINVAL; + + if (trident->synth.seq_client >= 0) { + snd_seq_delete_kernel_client(trident->synth.seq_client); + trident->synth.seq_client = -1; + } + if (trident->synth.ilist) + snd_seq_instr_list_free(&trident->synth.ilist); + return 0; +} + +static int __init alsa_trident_synth_init(void) +{ + static snd_seq_dev_ops_t ops = + { + snd_trident_synth_new_device, + snd_trident_synth_delete_device + }; + + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops, + sizeof(trident_t*)); +} + +static void __exit alsa_trident_synth_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT); +} + +module_init(alsa_trident_synth_init) +module_exit(alsa_trident_synth_exit) diff -Nru a/sound/pci/via686.c b/sound/pci/via686.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/via686.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1279 @@ +/* + * ALSA driver for VIA VT82C686A (South Bridge) + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("VIA VT82C686A"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{VIA,VT82C686A,pci},{VIA,VT82C686B}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for VIA 82C686A bridge."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for VIA 82C686A bridge."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 82C686A bridge."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC); +MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz)."); +MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000"); + +/* + * Direct registers + */ + +#ifndef PCI_DEVICE_ID_VIA_82C686_5 +#define PCI_DEVICE_ID_VIA_82C686_5 0x3058 +#endif + +#define VIAREG(via, x) ((via)->port + VIA_REG_##x) + +/* offsets */ +#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_STAT_ACTIVE 0x80 /* RO */ +#define VIA_REG_STAT_PAUSED 0x40 /* RO */ +#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */ +#define VIA_REG_STAT_STOPPED 0x04 /* RWC */ +#define VIA_REG_STAT_EOL 0x02 /* RWC */ +#define VIA_REG_STAT_FLAG 0x01 /* RWC */ +#define VIA_REG_OFFSET_CONTROL 0x01 /* byte - channel control */ +#define VIA_REG_CTRL_START 0x80 /* WO */ +#define VIA_REG_CTRL_TERMINATE 0x40 /* WO */ +#define VIA_REG_CTRL_PAUSE 0x08 /* RW */ +#define VIA_REG_CTRL_RESET 0x01 /* RW - probably reset? undocumented */ +#define VIA_REG_OFFSET_TYPE 0x02 /* byte - channel type */ +#define VIA_REG_TYPE_AUTOSTART 0x80 /* RW - autostart at EOL */ +#define VIA_REG_TYPE_16BIT 0x20 /* RW */ +#define VIA_REG_TYPE_STEREO 0x10 /* RW */ +#define VIA_REG_TYPE_INT_LLINE 0x00 +#define VIA_REG_TYPE_INT_LSAMPLE 0x04 +#define VIA_REG_TYPE_INT_LESSONE 0x08 +#define VIA_REG_TYPE_INT_MASK 0x0c +#define VIA_REG_TYPE_INT_EOL 0x02 +#define VIA_REG_TYPE_INT_FLAG 0x01 +#define VIA_REG_OFFSET_TABLE_PTR 0x04 /* dword - channel table pointer */ +#define VIA_REG_OFFSET_CURR_PTR 0x04 /* dword - channel current pointer */ +#define VIA_REG_OFFSET_CURR_COUNT 0x0c /* dword - channel current count */ +/* playback block */ +#define VIA_REG_PLAYBACK_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_PLAYBACK_CONTROL 0x01 /* byte - channel control */ +#define VIA_REG_PLAYBACK_TYPE 0x02 /* byte - channel type */ +#define VIA_REG_PLAYBACK_TABLE_PTR 0x04 /* dword - channel table pointer */ +#define VIA_REG_PLAYBACK_CURR_PTR 0x04 /* dword - channel current pointer */ +#define VIA_REG_PLAYBACK_CURR_COUNT 0x0c /* dword - channel current count */ +/* capture block */ +#define VIA_REG_CAPTURE_STATUS 0x10 /* byte - channel status */ +#define VIA_REG_CAPTURE_CONTROL 0x11 /* byte - channel control */ +#define VIA_REG_CAPTURE_TYPE 0x12 /* byte - channel type */ +#define VIA_REG_CAPTURE_TABLE_PTR 0x14 /* dword - channel table pointer */ +#define VIA_REG_CAPTURE_CURR_PTR 0x14 /* dword - channel current pointer */ +#define VIA_REG_CAPTURE_CURR_COUNT 0x1c /* dword - channel current count */ +/* FM block */ +#define VIA_REG_FM_STATUS 0x20 /* byte - channel status */ +#define VIA_REG_FM_CONTROL 0x21 /* byte - channel control */ +#define VIA_REG_FM_TYPE 0x22 /* byte - channel type */ +#define VIA_REG_FM_TABLE_PTR 0x24 /* dword - channel table pointer */ +#define VIA_REG_FM_CURR_PTR 0x24 /* dword - channel current pointer */ +#define VIA_REG_FM_CURR_COUNT 0x2c /* dword - channel current count */ +/* AC'97 */ +#define VIA_REG_AC97 0x80 /* dword */ +#define VIA_REG_AC97_CODEC_ID_MASK (3<<30) +#define VIA_REG_AC97_CODEC_ID_SHIFT 30 +#define VIA_REG_AC97_CODEC_ID_PRIMARY 0x00 +#define VIA_REG_AC97_CODEC_ID_SECONDARY 0x01 +#define VIA_REG_AC97_SECONDARY_VALID (1<<27) +#define VIA_REG_AC97_PRIMARY_VALID (1<<25) +#define VIA_REG_AC97_BUSY (1<<24) +#define VIA_REG_AC97_READ (1<<23) +#define VIA_REG_AC97_CMD_SHIFT 16 +#define VIA_REG_AC97_CMD_MASK 0x7e +#define VIA_REG_AC97_DATA_SHIFT 0 +#define VIA_REG_AC97_DATA_MASK 0xffff +#define VIA_REG_SGD_SHADOW 0x84 /* dword */ + +/* + * + */ + +#define VIA_MAX_FRAGS 32 + +/* + * + */ + +typedef struct { + unsigned long reg_offset; + unsigned int *table; + dma_addr_t table_addr; + snd_pcm_substream_t *substream; + dma_addr_t physbuf; + unsigned int size; + unsigned int fragsize; + unsigned int frags; + unsigned int lastptr; + unsigned int lastcount; +} viadev_t; + +typedef struct _snd_via686a via686a_t; +#define chip_t via686a_t + +struct _snd_via686a { + int irq; + + unsigned long port; + struct resource *res_port; + unsigned char revision; + + unsigned char old_legacy; + unsigned char old_legacy_cfg; + + struct pci_dev *pci; + snd_card_t *card; + + snd_pcm_t *pcm; + snd_pcm_t *pcm_fm; + viadev_t playback; + viadev_t capture; + viadev_t playback_fm; + + snd_rawmidi_t *rmidi; + + ac97_t *ac97; + unsigned int ac97_clock; + unsigned int ac97_secondary; /* secondary AC'97 codec is present */ + + spinlock_t reg_lock; + spinlock_t ac97_lock; + snd_info_entry_t *proc_entry; + + void *tables; + dma_addr_t tables_addr; +}; + +static struct pci_device_id snd_via686a_ids[] __devinitdata = { + { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 686A */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_via686a_ids); + +/* + * Basic I/O + */ + +static inline unsigned int snd_via686a_codec_xread(via686a_t *chip) +{ + return inl(VIAREG(chip, AC97)); +} + +static inline void snd_via686a_codec_xwrite(via686a_t *chip, unsigned int val) +{ + outl(val, VIAREG(chip, AC97)); +} + +static int snd_via686a_codec_ready(via686a_t *chip, int secondary) +{ + unsigned int timeout = 1000; /* 1ms */ + unsigned int val; + + while (timeout-- > 0) { + udelay(1); + if (!((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY)) + return val & 0xffff; + } + snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via686a_codec_xread(chip)); + return -EIO; +} + +static int snd_via686a_codec_valid(via686a_t *chip, int secondary) +{ + unsigned int timeout = 1000; /* 1ms */ + unsigned int val; + unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID : + VIA_REG_AC97_SECONDARY_VALID; + + while (timeout-- > 0) { + udelay(1); + if ((val = snd_via686a_codec_xread(chip)) & stat) + return val & 0xffff; + } + snd_printk("codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via686a_codec_xread(chip)); + return -EIO; +} + +static void snd_via686a_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); + unsigned int xval; + + xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; + xval <<= VIA_REG_AC97_CODEC_ID_SHIFT; + xval |= reg << VIA_REG_AC97_CMD_SHIFT; + xval |= val << VIA_REG_AC97_DATA_SHIFT; + spin_lock(&chip->ac97_lock); + snd_via686a_codec_xwrite(chip, xval); + snd_via686a_codec_ready(chip, ac97->num); + spin_unlock(&chip->ac97_lock); +} + +static unsigned short snd_via686a_codec_read(ac97_t *ac97, unsigned short reg) +{ + via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return ~0); + unsigned int xval, val = 0xffff; + int again = 0; + + xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; + xval <<= VIA_REG_AC97_CODEC_ID_SHIFT; + xval = (!ac97->num ? VIA_REG_AC97_PRIMARY_VALID : VIA_REG_AC97_SECONDARY_VALID); + xval |= VIA_REG_AC97_READ; + xval |= reg << VIA_REG_AC97_CMD_SHIFT; + spin_lock(&chip->ac97_lock); + while (1) { + if (again++ > 3) { + spin_unlock(&chip->ac97_lock); + return 0xffff; + } + snd_via686a_codec_xwrite(chip, xval); + if (snd_via686a_codec_ready(chip, ac97->num) < 0) + continue; + if (snd_via686a_codec_valid(chip, ac97->num) >= 0) { + udelay(25); + val = snd_via686a_codec_xread(chip); + break; + } + } + spin_unlock(&chip->ac97_lock); + return val & 0xffff; +} + +#if 0 +static void snd_via686a_channel_print(via686a_t *chip, viadev_t *viadev) +{ + unsigned long port = chip->port + viadev->reg_offset; + + printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n", + port, + inb(port + VIA_REG_OFFSET_STATUS), + inb(port + VIA_REG_OFFSET_CONTROL), + inb(port + VIA_REG_OFFSET_TYPE), + inl(port + VIA_REG_OFFSET_CURR_PTR), + inl(port + VIA_REG_OFFSET_CURR_COUNT)); +} +#endif + +static void snd_via686a_channel_reset(via686a_t *chip, viadev_t *viadev) +{ + unsigned long port = chip->port + viadev->reg_offset; + + outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, port + VIA_REG_OFFSET_CONTROL); + udelay(50); + outb(0x00, port + VIA_REG_OFFSET_CONTROL); + outb(0xff, port + VIA_REG_OFFSET_STATUS); + outb(0x00, port + VIA_REG_OFFSET_TYPE); + outl(0, port + VIA_REG_OFFSET_CURR_PTR); +} + +static int snd_via686a_trigger(via686a_t *chip, viadev_t *viadev, int cmd) +{ + unsigned char val = 0; + unsigned long port = chip->port + viadev->reg_offset; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = VIA_REG_CTRL_START; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = VIA_REG_CTRL_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = 0; + break; + default: + return -EINVAL; + } + outb(val, port + VIA_REG_OFFSET_CONTROL); + if (cmd == SNDRV_PCM_TRIGGER_STOP) + snd_via686a_channel_reset(chip, viadev); + return 0; +} + +static void snd_via686a_setup_periods(via686a_t *chip, viadev_t *viadev, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int idx, frags; + unsigned int *table = viadev->table; + unsigned long port = chip->port + viadev->reg_offset; + + viadev->physbuf = runtime->dma_addr; + viadev->size = snd_pcm_lib_buffer_bytes(substream); + viadev->fragsize = snd_pcm_lib_period_bytes(substream); + viadev->frags = runtime->periods; + viadev->lastptr = ~0; + viadev->lastcount = ~0; + + snd_via686a_channel_reset(chip, viadev); + outl(viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); + outb(VIA_REG_TYPE_AUTOSTART | + (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) | + (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) | + ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) | + VIA_REG_TYPE_INT_EOL | + VIA_REG_TYPE_INT_FLAG, port + VIA_REG_OFFSET_TYPE); + if (viadev->size == viadev->fragsize) { + table[0] = cpu_to_le32(viadev->physbuf); + table[1] = cpu_to_le32(0xc0000000 | /* EOL + flag */ + viadev->fragsize); + } else { + frags = viadev->size / viadev->fragsize; + for (idx = 0; idx < frags - 1; idx++) { + table[(idx << 1) + 0] = cpu_to_le32(viadev->physbuf + (idx * viadev->fragsize)); + table[(idx << 1) + 1] = cpu_to_le32(0x40000000 | /* flag */ + viadev->fragsize); + } + table[((frags-1) << 1) + 0] = cpu_to_le32(viadev->physbuf + ((frags-1) * viadev->fragsize)); + table[((frags-1) << 1) + 1] = cpu_to_le32(0x80000000 | /* EOL */ + viadev->fragsize); + } +} + +/* + * Interrupt handler + */ + +static inline void snd_via686a_update(via686a_t *chip, viadev_t *viadev) +{ + snd_pcm_period_elapsed(viadev->substream); + outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset); +} + +static void snd_via686a_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + via686a_t *chip = snd_magic_cast(via686a_t, dev_id, return); + unsigned int status; + + status = inl(VIAREG(chip, SGD_SHADOW)); + if ((status & 0x00000077) == 0) { + if (chip->rmidi != NULL) { + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + } + return; + } + if (inb(VIAREG(chip, PLAYBACK_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via686a_update(chip, &chip->playback); + if (inb(VIAREG(chip, FM_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via686a_update(chip, &chip->playback_fm); + if (inb(VIAREG(chip, CAPTURE_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via686a_update(chip, &chip->capture); +} + +/* + * PCM part + */ + +static int snd_via686a_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + + return snd_via686a_trigger(chip, &chip->playback, cmd); +} + +static int snd_via686a_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + + return snd_via686a_trigger(chip, &chip->capture, cmd); +} + +static int snd_via686a_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_via686a_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_via686a_playback_prepare(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_via686a_setup_periods(chip, &chip->playback, substream); + return 0; +} + +static int snd_via686a_capture_prepare(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + snd_via686a_setup_periods(chip, &chip->capture, substream); + return 0; +} + +static inline unsigned int snd_via686a_cur_ptr(via686a_t *chip, viadev_t *viadev) +{ + unsigned int val, ptr, count; + // unsigned int tmp; + + ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset); + count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset); + if (ptr == viadev->lastptr && count > viadev->lastcount) + ptr += 8; + if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE)) + return 0; + // tmp = + val = (((unsigned int)(ptr - viadev->table_addr) / 8) - 1) % viadev->frags; + val *= viadev->fragsize; + val += viadev->fragsize - count; + viadev->lastptr = ptr; + viadev->lastcount = count; + // printk("pointer: ptr = 0x%x (%i), count = 0x%x, val = 0x%x\n", ptr, tmp, count, val); + return val; +} + +static snd_pcm_uframes_t snd_via686a_playback_pointer(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->playback)); +} + +static snd_pcm_uframes_t snd_via686a_capture_pointer(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->capture)); +} + +static snd_pcm_hardware_t snd_via686a_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: VIA_MAX_FRAGS, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_via686a_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: VIA_MAX_FRAGS, + fifo_size: 0, +}; + +static int snd_via686a_playback_open(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->playback.substream = substream; + runtime->hw = snd_via686a_playback; + runtime->hw.rates = chip->ac97->rates_front_dac; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_via686a_capture_open(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->capture.substream = substream; + runtime->hw = snd_via686a_capture; + runtime->hw.rates = chip->ac97->rates_adc; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_via686a_playback_close(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + + chip->playback.substream = NULL; + /* disable DAC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200); + return 0; +} + +static int snd_via686a_capture_close(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + + chip->capture.substream = NULL; + /* disable ADC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100); + return 0; +} + +static snd_pcm_ops_t snd_via686a_playback_ops = { + open: snd_via686a_playback_open, + close: snd_via686a_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_via686a_hw_params, + hw_free: snd_via686a_hw_free, + prepare: snd_via686a_playback_prepare, + trigger: snd_via686a_playback_trigger, + pointer: snd_via686a_playback_pointer, +}; + +static snd_pcm_ops_t snd_via686a_capture_ops = { + open: snd_via686a_capture_open, + close: snd_via686a_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_via686a_hw_params, + hw_free: snd_via686a_hw_free, + prepare: snd_via686a_capture_prepare, + trigger: snd_via686a_capture_trigger, + pointer: snd_via686a_capture_pointer, +}; + +static void snd_via686a_pcm_free(snd_pcm_t *pcm) +{ + via686a_t *chip = snd_magic_cast(via686a_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_via686a_pcm(via686a_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "VIA 82C686A", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686a_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686a_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_via686a_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "VIA 82C686A"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = NULL; + return 0; +} + +#if 0 + +/* + * PCM code - FM channel + */ + +static int snd_via686a_playback_fm_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_via686a_playback_fm_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + + return snd_via686a_trigger(chip, &chip->playback_fm, cmd); +} + +static int snd_via686a_playback_fm_prepare(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_via686a_setup_periods(chip, &chip->playback_fm, substream); + return 0; +} + +static snd_pcm_uframes_t snd_via686a_playback_fm_pointer(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->playback_fm)); +} + +static snd_pcm_hardware_t snd_via686a_playback_fm = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_KNOT, + rate_min: 24000, + rate_max: 24000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: VIA_MAX_FRAGS, + fifo_size: 0, +}; + +static int snd_via686a_playback_fm_open(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma_fm_size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) + return -ENOMEM; + chip->playback_fm.substream = substream; + runtime->hw = snd_via686a_playback_fm; +#if 0 + runtime->hw.rates = chip->ac97->rates_front_dac; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.min_rate = 48000; +#endif + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_via686a_playback_fm_close(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->playback_fm.substream = NULL; + snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + /* disable DAC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200); + return 0; +} + +static snd_pcm_ops_t snd_via686a_playback_fm_ops = { + open: snd_via686a_playback_fm_open, + close: snd_via686a_playback_fm_close, + ioctl: snd_pcm_lib_ioctl, + prepare: snd_via686a_playback_fm_prepare, + trigger: snd_via686a_playback_fm_trigger, + pointer: snd_via686a_playback_fm_pointer, +}; + +static void snd_via686a_pcm_fm_free(void *private_data) +{ + via686a_t *chip = snd_magic_cast(via686a_t, private_data, return); + chip->pcm_fm = NULL; + snd_pcm_lib_preallocate_pci_free_for_all(ensoniq->pci, pcm); +} + +static int __devinit snd_via686a_pcm_fm(via686a_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "VIA 82C686A - FM DAC", device, 1, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686a_playback_fm_ops); + + pcm->private_data = chip; + pcm->private_free = snd_via686a_pcm_fm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "VIA 82C686A - FM DAC"); + + snd_pcm_add_buffer_bytes_controls(pcm); + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024); + + chip->pcm_fm = pcm; + if (rpcm) + *rpcm = NULL; + return 0; +} + +#endif + +/* + * Mixer part + */ + +static void snd_via686a_codec_init(ac97_t *ac97) +{ + // via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); + + /* disable DAC & ADC power */ + snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300); + /* disable center DAC/surround DAC/LFE DAC/MIC ADC */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800); +} + +static void snd_via686a_mixer_free_ac97(ac97_t *ac97) +{ + via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +static int __devinit snd_via686a_mixer(via686a_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_via686a_codec_write; + ac97.read = snd_via686a_codec_read; + ac97.init = snd_via686a_codec_init; + ac97.private_data = chip; + ac97.private_free = snd_via686a_mixer_free_ac97; + ac97.clock = chip->ac97_clock; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + return 0; +} + +/* + * joystick + */ + +static int snd_via686a_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_via686a_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via686a_t *chip = snd_kcontrol_chip(kcontrol); + u16 val; + + pci_read_config_word(chip->pci, 0x42, &val); + ucontrol->value.integer.value[0] = (val & 0x08) ? 1 : 0; + return 0; +} + +static int snd_via686a_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via686a_t *chip = snd_kcontrol_chip(kcontrol); + u16 val, oval; + + pci_read_config_word(chip->pci, 0x42, &oval); + val = oval & ~0x08; + if (ucontrol->value.integer.value[0]) + val |= 0x08; + if (val != oval) { + pci_write_config_word(chip->pci, 0x42, val); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_via686a_joystick_control __devinitdata = { + name: "Joystick", + iface: SNDRV_CTL_ELEM_IFACE_CARD, + info: snd_via686a_joystick_info, + get: snd_via686a_joystick_get, + put: snd_via686a_joystick_put, +}; + +/* + * + */ + +static int __devinit snd_via686a_chip_init(via686a_t *chip) +{ + ac97_t ac97; + unsigned int val; + int max_count; + unsigned char pval; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + +#if 0 /* broken on K7M? */ + /* disable all legacy ports */ + pci_write_config_byte(chip->pci, 0x42, 0); +#endif + + /* deassert ACLink reset, force SYNC */ + pci_write_config_byte(chip->pci, 0x41, 0xe0); + udelay(100); + pci_write_config_byte(chip->pci, 0x41, 0x00); + udelay(100); + /* ACLink on, deassert ACLink reset, VSR, SGD data out */ + /* note - FM data out has trouble with non VRA codecs !! */ + pci_write_config_byte(chip->pci, 0x41, 0xcc); + udelay(100); + + /* wait until codec ready */ + max_count = ((3 * HZ) / 4) + 1; + do { + pci_read_config_byte(chip->pci, 0x40, &pval); + if (pval & 0x01) /* primary codec ready */ + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (--max_count > 0); + + if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY) + snd_printk("AC'97 codec is not ready [0x%x]\n", val); + + /* and then reset codec.. */ + snd_via686a_codec_write(&ac97, AC97_RESET, 0x0000); + + /* check the primary codec */ + snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ | + VIA_REG_AC97_PRIMARY_VALID | + (VIA_REG_AC97_CODEC_ID_PRIMARY << VIA_REG_AC97_CODEC_ID_SHIFT)); + max_count = ((3 * HZ) / 4) + 1; + do { + if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_PRIMARY_VALID) + goto __ac97_ok1; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (--max_count > 0); + snd_printk("Primary AC'97 codec is not valid [0x%x]\n", val); + + __ac97_ok1: +#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */ + snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ | + VIA_REG_AC97_SECONDARY_VALID | + (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); + max_count = ((3 * HZ) / 4) + 1; + snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ | + VIA_REG_AC97_SECONDARY_VALID | + (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); + do { + if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) { + chip->ac97_secondary = 1; + goto __ac97_ok2; + } + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } while (--max_count > 0); + /* This is ok, the most of motherboards have only one codec */ + + __ac97_ok2: +#endif + +#if 0 + { + unsigned char cmdb; + + pci_read_config_byte(chip->pci, 0x40, &cmdb); + printk("PCI[0x40] = 0x%x\n", cmdb); + pci_read_config_byte(chip->pci, 0x42, &cmdb); + printk("PCI[0x42] = 0x%x\n", cmdb); + pci_read_config_byte(chip->pci, 0x43, &cmdb); + printk("PCI[0x43] = 0x%x\n", cmdb); + pci_read_config_byte(chip->pci, 0x44, &cmdb); + printk("PCI[0x44] = 0x%x\n", cmdb); + pci_read_config_byte(chip->pci, 0x48, &cmdb); + printk("PCI[0x48] = 0x%x\n", cmdb); + } +#endif + + /* route FM trap to IRQ, disable FM trap */ + pci_write_config_byte(chip->pci, 0x48, 0); + + /* disable all GPI interrupts */ + outl(0, chip->port + 0x8c); + + /* disable interrupts */ + snd_via686a_channel_reset(chip, &chip->playback); + snd_via686a_channel_reset(chip, &chip->capture); + snd_via686a_channel_reset(chip, &chip->playback_fm); + return 0; +} + +static int snd_via686a_free(via686a_t *chip) +{ + if (chip->irq < 0) + goto __end_hw; + /* disable interrupts */ + snd_via686a_channel_reset(chip, &chip->playback); + snd_via686a_channel_reset(chip, &chip->capture); + snd_via686a_channel_reset(chip, &chip->playback_fm); + /* --- */ + __end_hw: + synchronize_irq(); + if (chip->tables) + snd_free_pci_pages(chip->pci, 3 * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, chip->tables, chip->tables_addr); + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + pci_write_config_byte(chip->pci, 0x42, chip->old_legacy); + pci_write_config_byte(chip->pci, 0x43, chip->old_legacy_cfg); + snd_magic_kfree(chip); + return 0; +} + +static int snd_via686a_dev_free(snd_device_t *device) +{ + via686a_t *chip = snd_magic_cast(via686a_t, device->device_data, return -ENXIO); + return snd_via686a_free(chip); +} + +static int __devinit snd_via686a_create(snd_card_t * card, + struct pci_dev *pci, + unsigned int ac97_clock, + unsigned char old_legacy, + unsigned char old_legacy_cfg, + via686a_t ** r_via) +{ + via686a_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_via686a_dev_free, + }; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + if ((chip = snd_magic_kcalloc(via686a_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + + chip->old_legacy = old_legacy; + chip->old_legacy_cfg = old_legacy_cfg; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ac97_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 256, "VIA686A")) == NULL) { + snd_via686a_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_via686a_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA686A", (void *)chip)) { + snd_via686a_free(chip); + snd_printk("unable to grab IRQ %d\n", chip->irq); + return -EBUSY; + } + chip->irq = pci->irq; + if (ac97_clock >= 8000 && ac97_clock <= 48000) + chip->ac97_clock = ac97_clock; + pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision); + synchronize_irq(); + + /* initialize offsets */ + chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS; + chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS; + chip->playback_fm.reg_offset = VIA_REG_FM_STATUS; + + /* allocate buffer descriptor lists */ + /* the start of each lists must be aligned to 8 bytes */ + chip->tables = (unsigned int *)snd_malloc_pci_pages(pci, 3 * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, &chip->tables_addr); + if (chip->tables == NULL) { + snd_via686a_free(chip); + return -ENOMEM; + } + /* tables must be aligned to 8 bytes, but the kernel pages + are much bigger, so we don't care */ + chip->playback.table = chip->tables; + chip->playback.table_addr = chip->tables_addr; + chip->capture.table = chip->playback.table + VIA_MAX_FRAGS * 2; + chip->capture.table_addr = chip->playback.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2; + chip->playback_fm.table = chip->capture.table + VIA_MAX_FRAGS * 2; + chip->playback_fm.table_addr = chip->capture.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2; + + if ((err = snd_via686a_chip_init(chip)) < 0) { + snd_via686a_free(chip); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_via686a_free(chip); + return err; + } + + *r_via = chip; + return 0; +} + +static int __devinit snd_via686a_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + via686a_t *chip; + int pcm_dev = 0; + unsigned char legacy; + unsigned char legacy_cfg; + int rev_h = 0, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + pci_read_config_byte(pci, 0x42, &legacy); + pci_read_config_byte(pci, 0x43, &legacy_cfg); + + if ((err = snd_via686a_create(card, + pci, + snd_ac97_clock[dev], + legacy, + legacy_cfg, + &chip)) < 0) { + snd_card_free(card); + return err; + } + + + if (snd_via686a_mixer(chip) < 0) { + snd_card_free(card); + return err; + } + if (snd_via686a_pcm(chip, pcm_dev++, NULL) < 0) { + snd_card_free(card); + return err; + } +#if 0 + if (snd_via686a_pcm_fm(chip, pcm_dev++, NULL) < 0) { + snd_card_free(card); + return err; + } +#endif + + legacy |= 0x40; /* disable MIDI */ + legacy &= ~0x08; /* disable joystick */ + if (chip->revision >= 0x20) { + if (check_region(pci_resource_start(pci, 2), 4)) { + rev_h = 0; + legacy &= ~0x80; /* disable PCI I/O 2 */ + } else { + rev_h = 1; + legacy |= 0x80; /* enable PCI I/O 2 */ + } + } + pci_write_config_byte(pci, 0x42, legacy); + pci_write_config_byte(pci, 0x43, legacy_cfg); + if (rev_h && snd_mpu_port[dev] >= 0x200) { /* force MIDI */ + legacy |= 0x02; /* enable MPU */ + pci_write_config_dword(pci, 0x18, (snd_mpu_port[dev] & 0xfffc) | 0x01); + } else { + if (rev_h && (legacy & 0x02)) { + snd_mpu_port[dev] = pci_resource_start(pci, 2); + if (snd_mpu_port[dev] < 0x200) /* bad value */ + legacy &= ~0x02; /* disable MIDI */ + } else { + switch (snd_mpu_port[dev]) { /* force MIDI */ + case 0x300: + case 0x310: + case 0x320: + case 0x330: + legacy_cfg &= ~(3 << 2); + legacy_cfg |= (snd_mpu_port[dev] & 0x0030) >> 2; + legacy |= 0x02; + break; + default: /* no, use BIOS settings */ + if (legacy & 0x02) + snd_mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2); + } + } + } + pci_write_config_byte(pci, 0x42, legacy); + pci_write_config_byte(pci, 0x43, legacy_cfg); + if (legacy & 0x02) { + if (check_region(snd_mpu_port[dev], 2)) { + snd_printk("unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]); + legacy &= ~0x02; + pci_write_config_byte(pci, 0x42, legacy); + goto __skip_mpu; + } + if (snd_mpu401_uart_new(card, 0, MPU401_HW_VIA686A, + snd_mpu_port[dev], 0, + pci->irq, 0, + &chip->rmidi) < 0) { + snd_printk("unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]); + legacy &= ~0x02; + pci_write_config_byte(pci, 0x42, legacy); + goto __skip_mpu; + } + legacy &= ~0x40; /* enable MIDI interrupt */ + pci_write_config_byte(pci, 0x42, legacy); + __skip_mpu: + ; + } + + /* card switches */ + err = snd_ctl_add(card, snd_ctl_new1(&snd_via686a_joystick_control, chip)); + if (err < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "VIA686A"); + strcpy(card->shortname, "VIA 82C686A/B"); + + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, chip->port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_via686a_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "VIA 82C686A/B", + id_table: snd_via686a_ids, + probe: snd_via686a_probe, + remove: __devexit_p(snd_via686a_remove), +}; + +static int __init alsa_card_via686a_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("VIA 82C686A soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_via686a_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_via686a_init) +module_exit(alsa_card_via686a_exit) + +#ifndef MODULE + +/* format is: snd-via686a=snd_enable,snd_index,snd_id, + snd_mpu_port,snd_ac97_clock */ + +static int __init alsa_card_via686a_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_ac97_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-via686a=", alsa_card_via686a_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/via8233.c b/sound/pci/via8233.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/via8233.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,915 @@ +/* + * ALSA driver for VIA VT8233 (South Bridge) + * + * Copyright (c) 2000 Jaroslav Kysela , + * Tjeerd.Mulder@fujitsu-siemens.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Tjeerd.Mulder@fujitsu-siemens.com"); +MODULE_DESCRIPTION("VIA VT8233"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{VIA,VT8233,pci}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for VIA 8233 bridge."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for VIA 8233 bridge."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 8233 bridge."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz)."); +MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000"); + +/* + * Direct registers + */ + +#ifndef PCI_DEVICE_ID_VIA_8233_5 +#define PCI_DEVICE_ID_VIA_8233_5 0x3059 +#endif + +#define VIAREG(via, x) ((via)->port + VIA_REG_##x) + +/* offsets */ +#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_STAT_ACTIVE 0x80 /* RO */ +#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */ +#define VIA_REG_STAT_STOPPED 0x04 /* RWC */ +#define VIA_REG_STAT_EOL 0x02 /* RWC */ +#define VIA_REG_STAT_FLAG 0x01 /* RWC */ +#define VIA_REG_OFFSET_CONTROL 0x01 /* byte - channel control */ +#define VIA_REG_CTRL_START 0x80 /* WO */ +#define VIA_REG_CTRL_TERMINATE 0x40 /* WO */ +#define VIA_REG_CTRL_AUTOSTART 0x20 +#define VIA_REG_CTRL_PAUSE 0x08 /* RW */ +#define VIA_REG_CTRL_INT_STOP 0x04 +#define VIA_REG_CTRL_INT_EOL 0x02 +#define VIA_REG_CTRL_INT_FLAG 0x01 +#define VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART) +#define VIA_REG_OFFSET_TABLE_PTR 0x04 /* dword - channel table pointer */ +#define VIA_REG_OFFSET_CURR_PTR 0x04 /* dword - channel current pointer */ +#define VIA_REG_OFFSET_TYPE 0x08 /* long - stop index, channel type, sample rate */ +#define VIA_REG_TYPE_16BIT 0x00200000 /* RW */ +#define VIA_REG_TYPE_STEREO 0x00100000 /* RW */ +#define VIA_REG_OFFSET_CURR_COUNT 0x0c /* dword - channel current count (24 bit) */ +#define VIA_REG_OFFSET_CURR_INDEX 0x0f /* byte - channel current index */ + +#define VIA_NUM_OF_DMA_CHANNELS 2 +/* playback block */ +#define VIA_REG_PLAYBACK_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_PLAYBACK_CONTROL 0x01 /* byte - channel control */ +#define VIA_REG_PLAYBACK_VOLUME_L 0x02 /* byte */ +#define VIA_REG_PLAYBACK_VOLUME_R 0x03 /* byte */ +#define VIA_REG_PLAYBACK_TABLE_PTR 0x04 /* dword - channel table pointer */ +#define VIA_REG_PLAYBACK_CURR_PTR 0x04 /* dword - channel current pointer */ +#define VIA_REG_PLAYBACK_TYPE 0x08 /* long - stop index, channel type, sample rate */ /* byte - channel type */ +#define VIA_REG_PLAYBACK_CURR_COUNT 0x0c /* dword - channel current count (24 bit) */ +#define VIA_REG_PLAYBACK_CURR_INDEX 0x0f /* byte - channel current index */ +/* capture block */ +#define VIA_REG_CAPTURE_STATUS 0x60 /* byte - channel status */ +#define VIA_REG_CAPTURE_CONTROL 0x61 /* byte - channel control */ +#define VIA_REG_CAPTURE_FIFO 0x62 /* byte - bit 6 = fifo enable */ +#define VIA_REG_CAPTURE_FIFO_ENABLE 0x40 +#define VIA_REG_CAPTURE_CHANNEL 0x63 /* byte - input select */ +#define VIA_REG_CAPTURE_CHANNEL_MIC 0x4 +#define VIA_REG_CAPTURE_CHANNEL_LINE 0 +#define VIA_REG_CAPTURE_TABLE_PTR 0x64 /* dword - channel table pointer */ +#define VIA_REG_CAPTURE_CURR_PTR 0x64 /* dword - channel current pointer */ +#define VIA_REG_CAPTURE_TYPE 0x68 /* byte - channel type */ +#define VIA_REG_CAPTURE_CURR_COUNT 0x6c /* dword - channel current count (24 bit) */ +#define VIA_REG_CAPTURE_CURR_INDEX 0x6f /* byte - channel current index */ +/* AC'97 */ +#define VIA_REG_AC97 0x80 /* dword */ +#define VIA_REG_AC97_CODEC_ID_MASK (3<<30) +#define VIA_REG_AC97_CODEC_ID_SHIFT 30 +#define VIA_REG_AC97_SECONDARY_VALID (1<<27) +#define VIA_REG_AC97_PRIMARY_VALID (1<<25) +#define VIA_REG_AC97_ANY_VALID (VIA_REG_AC97_PRIMARY_VALID | VIA_REG_AC97_SECONDARY_VALID | (1<<28)| (1<<29)) +#define VIA_REG_AC97_BUSY (1<<24) +#define VIA_REG_AC97_READ (1<<23) +#define VIA_REG_AC97_CMD_SHIFT 16 +#define VIA_REG_AC97_CMD_MASK 0x7e +#define VIA_REG_AC97_DATA_SHIFT 0 +#define VIA_REG_AC97_DATA_MASK 0xffff +#define VIA_REG_SGD_SHADOW 0x84 /* dword */ + +/* + * + */ + +#define VIA_MAX_FRAGS 32 + +/* + * + */ + +typedef struct { + unsigned long reg_offset; + unsigned int *table; + dma_addr_t table_addr; + snd_pcm_substream_t *substream; + dma_addr_t physbuf; + unsigned int size; + unsigned int fragsize; + unsigned int frags; +} viadev_t; + +typedef struct _snd_via8233 via8233_t; +#define chip_t via8233_t + +struct _snd_via8233 { + int irq; + + unsigned long port; + struct resource *res_port; + unsigned char revision; + + struct pci_dev *pci; + snd_card_t *card; + + snd_pcm_t *pcm; + viadev_t playback; + viadev_t capture; + + ac97_t *ac97; + unsigned int ac97_clock; + + spinlock_t reg_lock; + spinlock_t update_lock; + snd_info_entry_t *proc_entry; + + void *tables; + dma_addr_t tables_addr; +}; + +static struct pci_device_id snd_via8233_ids[] __devinitdata = { + { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* VT8233 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_via8233_ids); + +/* + * Basic I/O + */ + +static inline unsigned int snd_via8233_codec_xread(via8233_t *chip) +{ + /* this acces should be atomic */ + return inl(VIAREG(chip, AC97)); +} + +static inline void snd_via8233_codec_xwrite(via8233_t *chip, unsigned int val) +{ + /* this acces should be atomic */ + outl(val, VIAREG(chip, AC97)); +} + +static int snd_via8233_codec_ready(via8233_t *chip, int secondary) +{ + int time; + + time = 1000; + do { + udelay(1); + if ((snd_via8233_codec_xread(chip) & VIA_REG_AC97_BUSY) == 0) + return 0; + } while (time--); + snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via8233_codec_xread(chip)); + return -EIO; +} + +static void snd_via8233_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return); + unsigned int xval; + + xval = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT; + xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID; + xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT; + xval |= val << VIA_REG_AC97_DATA_SHIFT; + spin_lock(&chip->reg_lock); + snd_via8233_codec_ready(chip, ac97->num); + snd_via8233_codec_xwrite(chip, xval); + snd_via8233_codec_ready(chip, ac97->num); + spin_unlock(&chip->reg_lock); +} + +static unsigned short snd_via8233_codec_read(ac97_t *ac97, unsigned short reg) +{ + via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return ~0); + unsigned int val; + int valid = ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID; + int i; + + val = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT; + val |= valid; + val |= VIA_REG_AC97_READ; + val |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT; + spin_lock(&chip->reg_lock); + snd_via8233_codec_ready(chip, ac97->num); + snd_via8233_codec_xwrite(chip, val); + snd_via8233_codec_ready(chip, ac97->num); + for (i=1000; i--;) { + val = snd_via8233_codec_xread(chip); + if (val & valid) { + spin_unlock(&chip->reg_lock); + return (unsigned short)val; + } + } + spin_unlock(&chip->reg_lock); + snd_printk("codec_read: codec %i is not valid [0x%x]\n", ac97->num, val); + /* have to return some value, this is better then 0 */ + return ~0; +} + +#if 0 +static void snd_via8233_channel_print(via8233_t *chip, viadev_t *viadev) +{ + unsigned long port = chip->port + viadev->reg_offset; + + printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n", + port, + inb(port + VIA_REG_OFFSET_STATUS), + inb(port + VIA_REG_OFFSET_CONTROL), + inl(port + VIA_REG_OFFSET_TYPE), + inl(port + VIA_REG_OFFSET_CURR_PTR), + inl(port + VIA_REG_OFFSET_CURR_COUNT)); +} +#endif + +static void snd_via8233_channel_reset(via8233_t *chip, viadev_t *viadev) +{ + unsigned long port = chip->port + viadev->reg_offset; + + outb(VIA_REG_CTRL_TERMINATE, port + VIA_REG_OFFSET_CONTROL); + udelay(50); + /* disable interrupts */ + outb(0, port + VIA_REG_OFFSET_CONTROL); + /* clear interrupts */ + outb(0x3, port + VIA_REG_OFFSET_STATUS); +} + +static int snd_via8233_trigger(via8233_t *chip, viadev_t *viadev, int cmd) +{ + unsigned char val; + unsigned long port = chip->port + viadev->reg_offset; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = VIA_REG_CTRL_INT | VIA_REG_CTRL_START; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = VIA_REG_CTRL_TERMINATE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = VIA_REG_CTRL_INT | VIA_REG_CTRL_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = VIA_REG_CTRL_INT; + break; + default: + return -EINVAL; + } + outb(val, port + VIA_REG_OFFSET_CONTROL); + if (cmd == SNDRV_PCM_TRIGGER_STOP) + snd_via8233_channel_reset(chip, viadev); + return 0; +} + +static void snd_via8233_setup_periods(via8233_t *chip, viadev_t *viadev, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int idx, frags; + unsigned int *table = viadev->table; + unsigned long port = chip->port + viadev->reg_offset; + + viadev->physbuf = runtime->dma_addr; + viadev->size = snd_pcm_lib_buffer_bytes(substream); + viadev->fragsize = snd_pcm_lib_period_bytes(substream); + viadev->frags = runtime->periods; + + snd_via8233_channel_reset(chip, viadev); + outl(viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); + outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) | + (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) | + 0xff000000, /* STOP index is never reached */ + port + VIA_REG_OFFSET_TYPE); + + if (viadev->size == viadev->fragsize) { + table[0] = cpu_to_le32(viadev->physbuf); + table[1] = cpu_to_le32(0xc0000000 | /* EOL + flag */ + viadev->fragsize); + } else { + frags = viadev->size / viadev->fragsize; + for (idx = 0; idx < frags - 1; idx++) { + table[(idx << 1) + 0] = cpu_to_le32(viadev->physbuf + (idx * viadev->fragsize)); + table[(idx << 1) + 1] = cpu_to_le32(0x40000000 | /* flag */ + viadev->fragsize); + } + table[((frags-1) << 1) + 0] = cpu_to_le32(viadev->physbuf + ((frags-1) * viadev->fragsize)); + table[((frags-1) << 1) + 1] = cpu_to_le32(0x80000000 | /* EOL */ + viadev->fragsize); + } +#if 0 + printk("%s: size = %d frags = %d fragsize = %d\n", __FUNCTION__, viadev->size, frags, viadev->fragsize); + for (idx=0; idx < frags; idx++) + printk(" address %x, count %x\n", table[idx*2], table[idx*2+1]); +#endif +} + +/* + * Interrupt handler + */ + +static inline void snd_via8233_update(via8233_t *chip, viadev_t *viadev) +{ + snd_pcm_period_elapsed(viadev->substream); + outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset); +} + +static void snd_via8233_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + via8233_t *chip = snd_magic_cast(via8233_t, dev_id, return); + + if (inb(VIAREG(chip, PLAYBACK_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via8233_update(chip, &chip->playback); + if (inb(VIAREG(chip, CAPTURE_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via8233_update(chip, &chip->capture); +} + +/* + * PCM part + */ + +static int snd_via8233_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + + return snd_via8233_trigger(chip, &chip->playback, cmd); +} + +static int snd_via8233_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + return snd_via8233_trigger(chip, &chip->capture, cmd); +} + +static int snd_via8233_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_via8233_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_via8233_playback_prepare(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long tmp; + + if (inb(VIAREG(chip, PLAYBACK_STATUS)) & VIA_REG_STAT_ACTIVE) + return 0; + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_via8233_setup_periods(chip, &chip->playback, substream); + /* I don't understand this stuff but its from the documentation and this way it works */ + outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L)); + outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R)); + tmp = inl(VIAREG(chip, PLAYBACK_TYPE)) & ~0xfffff; + outl(tmp | (0xffff * runtime->rate)/(48000/16), VIAREG(chip, PLAYBACK_TYPE)); + return 0; +} + +static int snd_via8233_capture_prepare(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + snd_via8233_setup_periods(chip, &chip->capture, substream); + outb(VIA_REG_CAPTURE_CHANNEL_LINE, VIAREG(chip, CAPTURE_CHANNEL)); + outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO)); + return 0; +} + +static inline unsigned int snd_via8233_cur_ptr(via8233_t *chip, viadev_t *viadev) +{ + unsigned int val, count; + + count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff; + /* The via686a does not have this current index register, + * this register makes life easier for us here. */ + val = inb(VIAREG(chip, OFFSET_CURR_INDEX) + viadev->reg_offset) % viadev->frags; + val *= viadev->fragsize; + val += viadev->fragsize - count; + // printk("pointer: ptr = 0x%x, count = 0x%x, val = 0x%x\n", ptr, count, val); + return val; +} + +static snd_pcm_uframes_t snd_via8233_playback_pointer(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->playback)); +} + +static snd_pcm_uframes_t snd_via8233_capture_pointer(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->capture)); +} + +static snd_pcm_hardware_t snd_via8233_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: VIA_MAX_FRAGS, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_via8233_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: VIA_MAX_FRAGS, + fifo_size: 0, +}; + +static int snd_via8233_playback_open(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->playback.substream = substream; + runtime->hw = snd_via8233_playback; + runtime->hw.rates = chip->ac97->rates_front_dac; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_via8233_capture_open(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->capture.substream = substream; + runtime->hw = snd_via8233_capture; + runtime->hw.rates = chip->ac97->rates_adc; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_via8233_playback_close(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + + snd_via8233_channel_reset(chip, &chip->playback); + chip->playback.substream = NULL; + /* disable DAC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200); + return 0; +} + +static int snd_via8233_capture_close(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + + snd_via8233_channel_reset(chip, &chip->capture); + chip->capture.substream = NULL; + /* disable ADC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100); + return 0; +} + +static snd_pcm_ops_t snd_via8233_playback_ops = { + open: snd_via8233_playback_open, + close: snd_via8233_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_via8233_hw_params, + hw_free: snd_via8233_hw_free, + prepare: snd_via8233_playback_prepare, + trigger: snd_via8233_playback_trigger, + pointer: snd_via8233_playback_pointer, +}; + +static snd_pcm_ops_t snd_via8233_capture_ops = { + open: snd_via8233_capture_open, + close: snd_via8233_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_via8233_hw_params, + hw_free: snd_via8233_hw_free, + prepare: snd_via8233_capture_prepare, + trigger: snd_via8233_capture_trigger, + pointer: snd_via8233_capture_pointer, +}; + +static void snd_via8233_pcm_free(snd_pcm_t *pcm) +{ + via8233_t *chip = snd_magic_cast(via8233_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_via8233_pcm(via8233_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "VIA 8233", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_via8233_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "VIA 8233"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = NULL; + return 0; +} + +/* + * Mixer part + */ + +static void snd_via8233_codec_init(ac97_t *ac97) +{ + // via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return); + + /* disable DAC & ADC power */ + snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300); + /* disable center DAC/surround DAC/LFE DAC/MIC ADC */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800); +} + +static void snd_via8233_mixer_free_ac97(ac97_t *ac97) +{ + via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +static int __devinit snd_via8233_mixer(via8233_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_via8233_codec_write; + ac97.read = snd_via8233_codec_read; + ac97.init = snd_via8233_codec_init; + ac97.private_data = chip; + ac97.private_free = snd_via8233_mixer_free_ac97; + ac97.clock = chip->ac97_clock; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + return 0; +} + +/* + * + */ + +static int __devinit snd_via8233_chip_init(via8233_t *chip) +{ + ac97_t ac97; + unsigned char stat; + int i; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + + /* deassert ACLink reset */ + pci_write_config_byte(chip->pci, 0x41, 0x40); + udelay(100); + /* deassert ACLink reset, force SYNC (warm AC'97 reset) */ + pci_write_config_byte(chip->pci, 0x41, 0x60); + udelay(2); + /* ACLink on, deassert ACLink reset, VSR, SGD data out */ + pci_write_config_byte(chip->pci, 0x41, 0xcc); + + /* Wait for codec ready to be accessed. */ + for (i=HZ; i--; ) { + pci_read_config_byte(chip->pci, 0x40, &stat); + if (stat & 1) + break; + if (!i) { + snd_printk("chip_init: failed to access primary codec.\n"); + return ~0; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + snd_via8233_codec_ready(chip, 0); + snd_via8233_codec_write(&ac97, AC97_RESET, 0x0000); + snd_via8233_codec_read(&ac97, 0); + + /* disable interrupts */ + snd_via8233_channel_reset(chip, &chip->playback); + snd_via8233_channel_reset(chip, &chip->capture); + return 0; +} + +static int snd_via8233_free(via8233_t *chip) +{ + if (chip->irq < 0) + goto __end_hw; + /* disable interrupts */ + snd_via8233_channel_reset(chip, &chip->playback); + snd_via8233_channel_reset(chip, &chip->capture); + /* --- */ + __end_hw: + synchronize_irq(); + if (chip->tables) + snd_free_pci_pages(chip->pci, + VIA_NUM_OF_DMA_CHANNELS * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, + chip->tables, + chip->tables_addr); + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +static int snd_via8233_dev_free(snd_device_t *device) +{ + via8233_t *chip = snd_magic_cast(via8233_t, device->device_data, return -ENXIO); + return snd_via8233_free(chip); +} + +static int __devinit snd_via8233_create(snd_card_t * card, + struct pci_dev *pci, + unsigned int ac97_clock, + via8233_t ** r_via) +{ + via8233_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_via8233_dev_free, + }; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + if ((chip = snd_magic_kcalloc(via8233_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->update_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 256, "VIA8233")) == NULL) { + snd_via8233_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_via8233_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA8233", (void *)chip)) { + snd_via8233_free(chip); + snd_printk("unable to grab IRQ %d\n", chip->irq); + return -EBUSY; + } + chip->irq = pci->irq; + if (ac97_clock >= 8000 && ac97_clock <= 48000) + chip->ac97_clock = ac97_clock; + pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision); + synchronize_irq(); + + /* initialize offsets */ + chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS; + chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS; + + /* allocate buffer descriptor lists */ + /* the start of each lists must be aligned to 8 bytes */ + chip->tables = (unsigned int *)snd_malloc_pci_pages(pci, + VIA_NUM_OF_DMA_CHANNELS * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, + &chip->tables_addr); + if (chip->tables == NULL) { + snd_via8233_free(chip); + return -ENOMEM; + } + /* tables must be aligned to 8 bytes, but the kernel pages + are much bigger, so we don't care */ + chip->playback.table = chip->tables; + chip->playback.table_addr = chip->tables_addr; + chip->capture.table = chip->playback.table + VIA_MAX_FRAGS * 2; + chip->capture.table_addr = chip->playback.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2; + if ((err = snd_via8233_chip_init(chip)) < 0) { + snd_via8233_free(chip); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_via8233_free(chip); + return err; + } + + /* The 8233 ac97 controller does not implement the master bit + * in the pci command register. IMHO this is a violation of the PCI spec. + * We call pci_set_master here because it does not hurt. */ + pci_set_master(pci); + + *r_via = chip; + return 0; +} + +static int __devinit snd_via8233_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + via8233_t *chip; + int pcm_dev = 0; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_via8233_create(card, + pci, + snd_ac97_clock[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + + if (snd_via8233_mixer(chip) < 0) { + snd_card_free(card); + return err; + } + if (snd_via8233_pcm(chip, pcm_dev++, NULL) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "VIA8233"); + strcpy(card->shortname, "VIA 8233"); + + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, chip->port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_via8233_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "VIA 8233", + id_table: snd_via8233_ids, + probe: snd_via8233_probe, + remove: __devexit_p(snd_via8233_remove), +}; + +static int __init alsa_card_via8233_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("VIA 8233 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_via8233_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_via8233_init) +module_exit(alsa_card_via8233_exit) + +#ifndef MODULE + +/* format is: snd-via8233=snd_enable,snd_index,snd_id,snd_ac97_clock */ + +static int __init alsa_card_via8233_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_ac97_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-via8233=", alsa_card_via8233_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/ymfpci/Makefile b/sound/pci/ymfpci/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ymfpci/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ymfpci.o + +list-multi := snd-ymfpci.o + +snd-ymfpci-objs := ymfpci.o ymfpci_main.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_YMFPCI) += snd-ymfpci.o + +include $(TOPDIR)/Rules.make + +snd-ymfpci.o: $(snd-ymfpci-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ymfpci-objs) diff -Nru a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ymfpci/ymfpci.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,327 @@ +/* + * The driver for the Yamaha's DS1/DS1E cards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Yamaha DS-XG PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Yamaha,YMF724}," + "{Yamaha,YMF724F}," + "{Yamaha,YMF740}," + "{Yamaha,YMF740C}," + "{Yamaha,YMF744}," + "{Yamaha,YMF754}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static long snd_fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for the Yamaha DS-XG PCI soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for the Yamaha DS-XG PCI soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Yamaha DS-XG soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 Port."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM OPL-3 Port."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED); + +static struct pci_device_id snd_ymfpci_ids[] __devinitdata = { + { 0x1073, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724 */ + { 0x1073, 0x000d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724F */ + { 0x1073, 0x000a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740 */ + { 0x1073, 0x000c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740C */ + { 0x1073, 0x0010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF744 */ + { 0x1073, 0x0012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF754 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_ymfpci_ids); + +static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + ymfpci_t *chip; + opl3_t *opl3; + char *str; + int err; + u16 legacy_ctrl, legacy_ctrl2, old_legacy_ctrl; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (id->device) { + case 0x0004: str = "YMF724"; break; + case 0x000d: str = "YMF724F"; break; + case 0x000a: str = "YMF740"; break; + case 0x000c: str = "YMF740C"; break; + case 0x0010: str = "YMF744"; break; + case 0x0012: str = "YMF754"; break; + default: str = "???"; break; + } + + legacy_ctrl = 0; + legacy_ctrl2 = 0; + + if (id->device >= 0x0010) { /* YMF 744/754 */ + if (snd_fm_port[dev] < 0) + snd_fm_port[dev] = pci_resource_start(pci, 1); + else if (check_region(snd_fm_port[dev], 4)) + snd_fm_port[dev] = -1; + if (snd_fm_port[dev] >= 0) { + legacy_ctrl |= 2; + pci_write_config_word(pci, PCIR_DSXG_FMBASE, snd_fm_port[dev]); + } + if (snd_mpu_port[dev] < 0) + snd_mpu_port[dev] = pci_resource_start(pci, 1) + 0x20; + else if (check_region(snd_mpu_port[dev], 2)) + snd_mpu_port[dev] = -1; + if (snd_mpu_port[dev] >= 0) { + legacy_ctrl |= 8; + pci_write_config_word(pci, PCIR_DSXG_MPU401BASE, snd_mpu_port[dev]); + snd_printd("MPU401 supported on 0x%lx\n", snd_mpu_port[dev]); + } + } else { + switch (snd_fm_port[dev]) { + case 0x388: legacy_ctrl2 |= 0; break; + case 0x398: legacy_ctrl2 |= 1; break; + case 0x3a0: legacy_ctrl2 |= 2; break; + case 0x3a8: legacy_ctrl2 |= 3; break; + default: snd_fm_port[dev] = -1; break; + } + if (snd_fm_port[dev] > 0 && check_region(snd_fm_port[dev], 4) == 0) + legacy_ctrl |= 2; + else { + legacy_ctrl2 &= ~3; + snd_fm_port[dev] = -1; + } + switch (snd_mpu_port[dev]) { + case 0x330: legacy_ctrl2 |= 0 << 4; break; + case 0x300: legacy_ctrl2 |= 1 << 4; break; + case 0x332: legacy_ctrl2 |= 2 << 4; break; + case 0x334: legacy_ctrl2 |= 3 << 4; break; + default: snd_mpu_port[dev] = -1; break; + } + if (snd_mpu_port[dev] > 0 && check_region(snd_mpu_port[dev], 2) == 0) { + snd_printd("MPU401 supported on 0x%lx\n", snd_mpu_port[dev]); + legacy_ctrl |= 8; + } else { + legacy_ctrl2 &= ~(3 << 4); + snd_mpu_port[dev] = -1; + } + } + if (snd_mpu_port[dev] > 0) { + legacy_ctrl |= 0x10; /* MPU401 irq enable */ + legacy_ctrl2 |= 1 << 15; /* IMOD */ + } + pci_read_config_word(pci, PCIR_DSXG_LEGACY, &old_legacy_ctrl); + //snd_printdd("legacy_ctrl = 0x%x\n", legacy_ctrl); + pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); + //snd_printdd("legacy_ctrl2 = 0x%x\n", legacy_ctrl2); + pci_write_config_word(pci, PCIR_DSXG_ELEGACY, legacy_ctrl2); + if ((err = snd_ymfpci_create(card, pci, + old_legacy_ctrl, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm_spdif(chip, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm_4ch(chip, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm2(chip, 3, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if (snd_mpu_port[dev] > 0) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI, + snd_mpu_port[dev], 0, + pci->irq, 0, &chip->rawmidi)) < 0) { + printk(KERN_INFO "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", snd_mpu_port[dev]); + } else { + legacy_ctrl &= ~0x10; /* disable MPU401 irq */ + pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); + } + } + if (snd_fm_port[dev] > 0) { + if ((err = snd_opl3_create(card, + snd_fm_port[dev], + snd_fm_port[dev] + 2, + OPL3_HW_OPL3, 0, &opl3)) < 0) { + printk(KERN_INFO "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", snd_fm_port[dev]); + } else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + snd_printk("cannot create opl3 hwdep\n"); + return err; + } + } + if ((err = snd_ymfpci_joystick(chip)) < 0) { + printk(KERN_INFO "ymfpci: cannot initialize joystick, skipping...\n"); + } + strcpy(card->driver, str); + sprintf(card->shortname, "Yamaha DS-XG PCI (%s)", str); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, + chip->reg_area_virt, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_card_ymfpci_suspend(struct pci_dev *pci, u32 state) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return -ENXIO); + snd_ymfpci_suspend(chip); + return 0; +} +static int snd_card_ymfpci_resume(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return -ENXIO); + snd_ymfpci_resume(chip); + return 0; +} +#else +static void snd_card_ymfpci_suspend(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return); + snd_ymfpci_suspend(chip); +} +static void snd_card_ymfpci_resume(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return); + snd_ymfpci_resume(chip); +} +#endif +#endif + +static void __devexit snd_card_ymfpci_remove(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "Yamaha DS-XG PCI", + id_table: snd_ymfpci_ids, + probe: snd_card_ymfpci_probe, + remove: __devexit_p(snd_card_ymfpci_remove), +#ifdef CONFIG_PM + suspend: snd_card_ymfpci_suspend, + resume: snd_card_ymfpci_resume, +#endif +}; + +static int __init alsa_card_ymfpci_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk("Yamaha DS-XG PCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ymfpci_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ymfpci_init) +module_exit(alsa_card_ymfpci_exit) + +#ifndef MODULE + +/* format is: snd-ymfpci=snd_enable,snd_index,snd_id, + snd_fm_port,snd_mpu_port */ + +static int __init alsa_card_ymfpci_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ymfpci=", alsa_card_ymfpci_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/ymfpci/ymfpci_image.h b/sound/pci/ymfpci/ymfpci_image.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ymfpci/ymfpci_image.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1565 @@ +#ifndef _HWMCODE_ +#define _HWMCODE_ + +static unsigned long DspInst[YDSXG_DSPLENGTH / 4] = { + 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, + 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, + 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, + 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +static unsigned long CntrlInst[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x001A82, 0x032D0D, 0x000810, 0x10043A, + 0x02D38D, 0x000810, 0x18043A, 0x00010D, + 0x020015, 0x0000FD, 0x000020, 0x038860, + 0x039060, 0x038060, 0x038040, 0x038040, + 0x038040, 0x018040, 0x000A7D, 0x038040, + 0x038040, 0x018040, 0x200402, 0x000882, + 0x08001A, 0x000904, 0x015986, 0x000007, + 0x260007, 0x000007, 0x000007, 0x018A06, + 0x000007, 0x030C8D, 0x000810, 0x18043A, + 0x260007, 0x00087D, 0x018042, 0x00160A, + 0x04A206, 0x000007, 0x00218D, 0x000810, + 0x08043A, 0x21C206, 0x000007, 0x0007FD, + 0x018042, 0x08000A, 0x000904, 0x029386, + 0x000195, 0x090D04, 0x000007, 0x000820, + 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, + 0x032206, 0x018040, 0x000A7D, 0x038042, + 0x13804A, 0x18000A, 0x001820, 0x059060, + 0x058860, 0x018040, 0x0000FD, 0x018042, + 0x70000A, 0x000115, 0x071144, 0x032386, + 0x030000, 0x007020, 0x034A06, 0x018040, + 0x00348D, 0x000810, 0x08043A, 0x21EA06, + 0x000007, 0x02D38D, 0x000810, 0x18043A, + 0x018206, 0x000007, 0x240007, 0x000F8D, + 0x000810, 0x00163A, 0x002402, 0x005C02, + 0x0028FD, 0x000020, 0x018040, 0x08000D, + 0x000815, 0x510984, 0x000007, 0x00004D, + 0x000E5D, 0x000E02, 0x00418D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00008D, + 0x000924, 0x000F02, 0x00458D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x018386, 0x000007, 0x01AA06, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x218086, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x055A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x034986, 0x000007, 0x002104, 0x034986, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x06C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00688D, 0x000810, 0x08043A, 0x288A06, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x060206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x215886, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x212086, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x07DA86, 0x00057D, 0x002820, + 0x03B060, 0x07F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x07FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x008D8D, 0x000810, + 0x08043A, 0x288A06, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x095186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x007FBD, 0x383DC4, + 0x000007, 0x001A7D, 0x001375, 0x018042, + 0x09004A, 0x10000A, 0x0B8D04, 0x139504, + 0x000007, 0x000820, 0x019060, 0x001104, + 0x212086, 0x010040, 0x0017FD, 0x018042, + 0x08000A, 0x000904, 0x212286, 0x000007, + 0x00197D, 0x038042, 0x09804A, 0x10000A, + 0x000924, 0x001664, 0x0011FD, 0x038042, + 0x2B804A, 0x19804A, 0x00008D, 0x218944, + 0x000007, 0x002244, 0x0AE186, 0x000007, + 0x001A64, 0x002A24, 0x00197D, 0x080102, + 0x100122, 0x000820, 0x039060, 0x018040, + 0x003DFD, 0x00008D, 0x000820, 0x018040, + 0x001375, 0x001A7D, 0x010042, 0x09804A, + 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, + 0x309144, 0x000007, 0x00060D, 0x000A15, + 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, + 0x000464, 0x01B3E4, 0x0232E4, 0x000464, + 0x000464, 0x000464, 0x000464, 0x00040D, + 0x08B1C4, 0x000007, 0x000820, 0x000BF5, + 0x030040, 0x00197D, 0x038042, 0x09804A, + 0x000A24, 0x08000A, 0x080E64, 0x000007, + 0x100122, 0x000820, 0x031060, 0x010040, + 0x0064AC, 0x00027D, 0x000020, 0x018040, + 0x00107D, 0x018042, 0x0011FD, 0x3B804A, + 0x09804A, 0x20000A, 0x000095, 0x1A1144, + 0x00A144, 0x0D2086, 0x00040D, 0x00B984, + 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, + 0x09804A, 0x28000A, 0x000095, 0x010924, + 0x002A64, 0x0D1186, 0x000007, 0x002904, + 0x0D2286, 0x000007, 0x0D2A06, 0x080002, + 0x00008D, 0x00387D, 0x000820, 0x018040, + 0x00127D, 0x018042, 0x10000A, 0x003904, + 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, + 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, + 0x000015, 0x00082D, 0x02C78D, 0x000820, + 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, + 0x0E7186, 0x400025, 0x00008D, 0x110944, + 0x000007, 0x00018D, 0x109504, 0x000007, + 0x009164, 0x000424, 0x000424, 0x000424, + 0x100102, 0x280002, 0x02C68D, 0x000820, + 0x0EC206, 0x00018D, 0x00042D, 0x00008D, + 0x109504, 0x000007, 0x00020D, 0x109184, + 0x000007, 0x02C70D, 0x000820, 0x00008D, + 0x0038FD, 0x018040, 0x003BFD, 0x001020, + 0x03A860, 0x000815, 0x313184, 0x212184, + 0x000007, 0x03B060, 0x03A060, 0x018040, + 0x0022FD, 0x000095, 0x010924, 0x000424, + 0x000424, 0x001264, 0x100102, 0x000820, + 0x039060, 0x018040, 0x001924, 0x00FB8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x000424, + 0x000424, 0x00117D, 0x018042, 0x08000A, + 0x000A24, 0x280502, 0x280C02, 0x09800D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x0022FD, 0x018042, 0x08000A, 0x000095, + 0x280DC4, 0x011924, 0x00197D, 0x018042, + 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, + 0x113144, 0x0A8D04, 0x000007, 0x080A44, + 0x129504, 0x000007, 0x0023FD, 0x001020, + 0x038040, 0x101244, 0x000007, 0x000820, + 0x039060, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x10FA86, 0x000007, + 0x003BFD, 0x000100, 0x000A10, 0x0B807A, + 0x13804A, 0x090984, 0x000007, 0x000095, + 0x013D04, 0x118086, 0x10000A, 0x100002, + 0x090984, 0x000007, 0x038042, 0x11804A, + 0x090D04, 0x000007, 0x10000A, 0x090D84, + 0x000007, 0x00257D, 0x000820, 0x018040, + 0x00010D, 0x000810, 0x28143A, 0x00127D, + 0x018042, 0x20000A, 0x00197D, 0x018042, + 0x00117D, 0x31804A, 0x10000A, 0x003124, + 0x01280D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x300102, 0x003124, 0x000424, 0x000424, + 0x001224, 0x280502, 0x001A4C, 0x130186, + 0x700002, 0x00002D, 0x030000, 0x00387D, + 0x018042, 0x10000A, 0x132A06, 0x002124, + 0x0000AD, 0x100002, 0x00010D, 0x000924, + 0x006B24, 0x01368D, 0x00397D, 0x000820, + 0x058040, 0x038042, 0x09844A, 0x000606, + 0x08040A, 0x003264, 0x00008D, 0x000A24, + 0x001020, 0x00227D, 0x018040, 0x013C0D, + 0x000810, 0x08043A, 0x29D206, 0x000007, + 0x002820, 0x00207D, 0x018040, 0x00117D, + 0x038042, 0x13804A, 0x33800A, 0x00387D, + 0x018042, 0x08000A, 0x000904, 0x163A86, + 0x000007, 0x00008D, 0x030964, 0x01478D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x380102, + 0x000424, 0x000424, 0x001224, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x14A286, + 0x000007, 0x280502, 0x001A4C, 0x163986, + 0x000007, 0x032164, 0x00632C, 0x003DFD, + 0x018042, 0x08000A, 0x000095, 0x090904, + 0x000007, 0x000820, 0x001A4C, 0x156186, + 0x018040, 0x030000, 0x157A06, 0x002124, + 0x00010D, 0x000924, 0x006B24, 0x015B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x003A64, + 0x000095, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x01628D, 0x000810, 0x08043A, 0x29D206, + 0x000007, 0x14D206, 0x000007, 0x007020, + 0x08010A, 0x10012A, 0x0020FD, 0x038860, + 0x039060, 0x018040, 0x00227D, 0x018042, + 0x003DFD, 0x08000A, 0x31844A, 0x000904, + 0x16D886, 0x18008B, 0x00008D, 0x189904, + 0x00312C, 0x17AA06, 0x000007, 0x00324C, + 0x173386, 0x000007, 0x001904, 0x173086, + 0x000007, 0x000095, 0x199144, 0x00222C, + 0x003124, 0x00636C, 0x000E3D, 0x001375, + 0x000BFD, 0x010042, 0x09804A, 0x10000A, + 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, + 0x000007, 0x00008D, 0x189904, 0x00226C, + 0x00322C, 0x30050A, 0x301DAB, 0x002083, + 0x0018FD, 0x018042, 0x08000A, 0x018924, + 0x300502, 0x001083, 0x001875, 0x010042, + 0x10000A, 0x00008D, 0x010924, 0x001375, + 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, + 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, + 0x305C8B, 0x006083, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x187A86, 0x000007, + 0x001E2D, 0x0005FD, 0x018042, 0x08000A, + 0x028924, 0x280502, 0x00060D, 0x000810, + 0x280C3A, 0x00008D, 0x000810, 0x28143A, + 0x0A808D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001275, 0x030042, 0x21004A, + 0x00008D, 0x1A0944, 0x000007, 0x01980D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0001F5, 0x030042, 0x0D004A, 0x10000A, + 0x089144, 0x000007, 0x000820, 0x010040, + 0x0025F5, 0x0A3144, 0x000007, 0x000820, + 0x032860, 0x030040, 0x00217D, 0x038042, + 0x0B804A, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00008D, 0x000124, 0x00012C, + 0x000E64, 0x001A64, 0x00636C, 0x08010A, + 0x10012A, 0x000820, 0x031060, 0x030040, + 0x0020FD, 0x018042, 0x08000A, 0x00227D, + 0x018042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00197D, 0x018042, 0x08000A, + 0x0022FD, 0x038042, 0x10000A, 0x000820, + 0x031060, 0x030040, 0x090D04, 0x000007, + 0x000820, 0x030040, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x038042, 0x13804A, 0x19804A, 0x110D04, + 0x198D04, 0x000007, 0x08000A, 0x001020, + 0x031860, 0x030860, 0x030040, 0x00008D, + 0x0B0944, 0x000007, 0x000820, 0x010040, + 0x0005F5, 0x030042, 0x08000A, 0x000820, + 0x010040, 0x0000F5, 0x010042, 0x08000A, + 0x000904, 0x1C6086, 0x001E75, 0x030042, + 0x01044A, 0x000C0A, 0x1C7206, 0x000007, + 0x000402, 0x000C02, 0x00177D, 0x001AF5, + 0x018042, 0x03144A, 0x031C4A, 0x03244A, + 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, + 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, + 0x030042, 0x0B004A, 0x1B804A, 0x13804A, + 0x20000A, 0x089144, 0x19A144, 0x0389E4, + 0x0399EC, 0x005502, 0x005D0A, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x006502, 0x006D0A, 0x030042, 0x0B004A, + 0x19004A, 0x2B804A, 0x13804A, 0x21804A, + 0x30000A, 0x089144, 0x19A144, 0x2AB144, + 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, + 0x03A9E4, 0x000702, 0x00107D, 0x000415, + 0x018042, 0x08000A, 0x0109E4, 0x000F02, + 0x002AF5, 0x0019FD, 0x010042, 0x09804A, + 0x10000A, 0x000934, 0x001674, 0x0029F5, + 0x010042, 0x10000A, 0x00917C, 0x002075, + 0x010042, 0x08000A, 0x000904, 0x1ED286, + 0x0026F5, 0x0027F5, 0x030042, 0x09004A, + 0x10000A, 0x000A3C, 0x00167C, 0x001A75, + 0x000BFD, 0x010042, 0x51804A, 0x48000A, + 0x160007, 0x001075, 0x010042, 0x282C0A, + 0x281D12, 0x282512, 0x001F32, 0x1E0007, + 0x0E0007, 0x001975, 0x010042, 0x002DF5, + 0x0D004A, 0x10000A, 0x009144, 0x1FB286, + 0x010042, 0x28340A, 0x000E5D, 0x00008D, + 0x000375, 0x000820, 0x010040, 0x05D2F4, + 0x54D104, 0x00735C, 0x205386, 0x000007, + 0x0C0007, 0x080007, 0x0A0007, 0x02040D, + 0x000810, 0x08043A, 0x332206, 0x000007, + 0x205A06, 0x000007, 0x080007, 0x002275, + 0x010042, 0x20000A, 0x002104, 0x212086, + 0x001E2D, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x209286, 0x000007, 0x002010, + 0x30043A, 0x00057D, 0x0180C3, 0x08000A, + 0x028924, 0x280502, 0x280C02, 0x0A810D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x0004FD, 0x018042, 0x70000A, 0x030000, + 0x007020, 0x06FA06, 0x018040, 0x02180D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x218A86, 0x000007, 0x01F206, 0x000007, + 0x000875, 0x0009FD, 0x00010D, 0x220A06, + 0x000295, 0x000B75, 0x00097D, 0x00000D, + 0x000515, 0x010042, 0x18000A, 0x001904, + 0x287886, 0x0006F5, 0x001020, 0x010040, + 0x0004F5, 0x000820, 0x010040, 0x000775, + 0x010042, 0x09804A, 0x10000A, 0x001124, + 0x000904, 0x22BA86, 0x000815, 0x080102, + 0x101204, 0x22DA06, 0x000575, 0x081204, + 0x000007, 0x100102, 0x000575, 0x000425, + 0x021124, 0x100102, 0x000820, 0x031060, + 0x010040, 0x001924, 0x287886, 0x00008D, + 0x000464, 0x009D04, 0x278886, 0x180102, + 0x000575, 0x010042, 0x28040A, 0x00018D, + 0x000924, 0x280D02, 0x00000D, 0x000924, + 0x281502, 0x10000D, 0x000820, 0x0002F5, + 0x010040, 0x200007, 0x001175, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x23C286, + 0x000007, 0x000100, 0x080B20, 0x130B60, + 0x1B0B60, 0x030A60, 0x010040, 0x050042, + 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, + 0x0006F5, 0x010042, 0x28140A, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x24CA86, 0x004015, 0x000095, 0x010D04, + 0x24B886, 0x100022, 0x10002A, 0x24E206, + 0x000007, 0x333104, 0x2AA904, 0x000007, + 0x032124, 0x280502, 0x001124, 0x000424, + 0x000424, 0x003224, 0x00292C, 0x00636C, + 0x25F386, 0x000007, 0x02B164, 0x000464, + 0x000464, 0x00008D, 0x000A64, 0x280D02, + 0x10008D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x00008D, 0x38B904, 0x000007, + 0x03296C, 0x30010A, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x25BA86, 0x000007, + 0x02312C, 0x28050A, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x267A86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x26C086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x26CA86, 0x000007, 0x003124, 0x300502, + 0x003924, 0x300583, 0x000883, 0x0005F5, + 0x010042, 0x28040A, 0x00008D, 0x008124, + 0x280D02, 0x00008D, 0x008124, 0x281502, + 0x10018D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001025, 0x000575, 0x030042, + 0x09004A, 0x10000A, 0x0A0904, 0x121104, + 0x000007, 0x001020, 0x050860, 0x050040, + 0x0006FD, 0x018042, 0x09004A, 0x10000A, + 0x0000A5, 0x0A0904, 0x121104, 0x000007, + 0x000820, 0x019060, 0x010040, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x284286, + 0x000007, 0x230A06, 0x000007, 0x000606, + 0x000007, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x289286, 0x000007, 0x000100, + 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, + 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, + 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, + 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, + 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, + 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, + 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, + 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, + 0x000606, 0x018040, 0x00008D, 0x000A64, + 0x280D02, 0x000A24, 0x00027D, 0x018042, + 0x10000A, 0x001224, 0x0003FD, 0x018042, + 0x08000A, 0x000904, 0x2A8286, 0x000007, + 0x00018D, 0x000A24, 0x000464, 0x000464, + 0x080102, 0x000924, 0x000424, 0x000424, + 0x100102, 0x02000D, 0x009144, 0x2AD986, + 0x000007, 0x0001FD, 0x018042, 0x08000A, + 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x00027D, 0x001020, 0x000606, 0x018040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x2B2A86, 0x000007, 0x00037D, 0x018042, + 0x08000A, 0x000904, 0x2B5A86, 0x000007, + 0x000075, 0x002E7D, 0x010042, 0x0B804A, + 0x000020, 0x000904, 0x000686, 0x010040, + 0x31844A, 0x30048B, 0x000883, 0x00008D, + 0x000810, 0x28143A, 0x00008D, 0x000810, + 0x280C3A, 0x000675, 0x010042, 0x08000A, + 0x003815, 0x010924, 0x280502, 0x0B000D, + 0x000820, 0x0002F5, 0x010040, 0x000606, + 0x220007, 0x000464, 0x000464, 0x000606, + 0x000007, 0x000134, 0x007F8D, 0x00093C, + 0x281D12, 0x282512, 0x001F32, 0x0E0007, + 0x00010D, 0x00037D, 0x000820, 0x018040, + 0x05D2F4, 0x000007, 0x080007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2D0286, + 0x000007, 0x000606, 0x000007, 0x000007, + 0x000012, 0x100007, 0x320007, 0x600007, + 0x100080, 0x48001A, 0x004904, 0x2D6186, + 0x000007, 0x001210, 0x58003A, 0x000145, + 0x5C5D04, 0x000007, 0x000080, 0x48001A, + 0x004904, 0x2DB186, 0x000007, 0x001210, + 0x50003A, 0x005904, 0x2E0886, 0x000045, + 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, + 0x004224, 0x500102, 0x200502, 0x000082, + 0x40001A, 0x004104, 0x2E3986, 0x000007, + 0x003865, 0x40001A, 0x004020, 0x00104D, + 0x04C184, 0x301B86, 0x000040, 0x040007, + 0x000165, 0x000145, 0x004020, 0x000040, + 0x000765, 0x080080, 0x40001A, 0x004104, + 0x2EC986, 0x000007, 0x001210, 0x40003A, + 0x004104, 0x2F2286, 0x00004D, 0x0000CD, + 0x004810, 0x20043A, 0x000882, 0x40001A, + 0x004104, 0x2F3186, 0x000007, 0x004820, + 0x005904, 0x300886, 0x000040, 0x0007E5, + 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, + 0x4216E0, 0x021260, 0x000040, 0x000032, + 0x400075, 0x00007D, 0x07D574, 0x200512, + 0x000082, 0x40001A, 0x004104, 0x2FE186, + 0x000007, 0x037206, 0x640007, 0x060007, + 0x0000E5, 0x000020, 0x000040, 0x000A65, + 0x000020, 0x020040, 0x020040, 0x000040, + 0x000165, 0x000042, 0x70000A, 0x007104, + 0x30A286, 0x000007, 0x018206, 0x640007, + 0x050000, 0x007020, 0x000040, 0x037206, + 0x640007, 0x000007, 0x00306D, 0x028860, + 0x029060, 0x08000A, 0x028860, 0x008040, + 0x100012, 0x00100D, 0x009184, 0x314186, + 0x000E0D, 0x009184, 0x325186, 0x000007, + 0x300007, 0x001020, 0x003B6D, 0x008040, + 0x000080, 0x08001A, 0x000904, 0x316186, + 0x000007, 0x001220, 0x000DED, 0x008040, + 0x008042, 0x10000A, 0x40000D, 0x109544, + 0x000007, 0x001020, 0x000DED, 0x008040, + 0x008042, 0x20040A, 0x000082, 0x08001A, + 0x000904, 0x31F186, 0x000007, 0x003B6D, + 0x008042, 0x08000A, 0x000E15, 0x010984, + 0x329B86, 0x600007, 0x08001A, 0x000C15, + 0x010984, 0x328386, 0x000020, 0x1A0007, + 0x0002ED, 0x008040, 0x620007, 0x00306D, + 0x028042, 0x0A804A, 0x000820, 0x0A804A, + 0x000606, 0x10804A, 0x000007, 0x282512, + 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, + 0x000786, 0x000007, 0x0C0007, 0x0A0007, + 0x1C0007, 0x003465, 0x020040, 0x004820, + 0x025060, 0x40000A, 0x024060, 0x000040, + 0x454944, 0x000007, 0x004020, 0x003AE5, + 0x000040, 0x0028E5, 0x000042, 0x48000A, + 0x004904, 0x386886, 0x002C65, 0x000042, + 0x40000A, 0x0000D5, 0x454104, 0x000007, + 0x000655, 0x054504, 0x34F286, 0x0001D5, + 0x054504, 0x34F086, 0x002B65, 0x000042, + 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, + 0x000007, 0x454504, 0x000007, 0x0000CD, + 0x444944, 0x000007, 0x454504, 0x000007, + 0x00014D, 0x554944, 0x000007, 0x045144, + 0x34E986, 0x002C65, 0x000042, 0x48000A, + 0x4CD104, 0x000007, 0x04C144, 0x34F386, + 0x000007, 0x160007, 0x002CE5, 0x040042, + 0x40000A, 0x004020, 0x000040, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x356086, + 0x000007, 0x002402, 0x36A206, 0x005C02, + 0x0025E5, 0x000042, 0x40000A, 0x004274, + 0x002AE5, 0x000042, 0x40000A, 0x004274, + 0x500112, 0x0029E5, 0x000042, 0x40000A, + 0x004234, 0x454104, 0x000007, 0x004020, + 0x000040, 0x003EE5, 0x000020, 0x000040, + 0x002DE5, 0x400152, 0x50000A, 0x045144, + 0x364A86, 0x0000C5, 0x003EE5, 0x004020, + 0x000040, 0x002BE5, 0x000042, 0x40000A, + 0x404254, 0x000007, 0x002AE5, 0x004020, + 0x000040, 0x500132, 0x040134, 0x005674, + 0x0029E5, 0x020042, 0x42000A, 0x000042, + 0x50000A, 0x05417C, 0x0028E5, 0x000042, + 0x48000A, 0x0000C5, 0x4CC144, 0x371086, + 0x0026E5, 0x0027E5, 0x020042, 0x40004A, + 0x50000A, 0x00423C, 0x00567C, 0x0028E5, + 0x004820, 0x000040, 0x281D12, 0x282512, + 0x001F72, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x37AA86, 0x0E0007, 0x160007, + 0x1E0007, 0x003EE5, 0x000042, 0x40000A, + 0x004104, 0x37E886, 0x002D65, 0x000042, + 0x28340A, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, + 0x54D104, 0x00735C, 0x385186, 0x000007, + 0x000606, 0x080007, 0x0C0007, 0x080007, + 0x0A0007, 0x0001E5, 0x020045, 0x004020, + 0x000060, 0x000365, 0x000040, 0x002E65, + 0x001A20, 0x0A1A60, 0x000040, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x000606, 0x50004A, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +// -------------------------------------------- +// DS-1E Controller InstructionRAM Code +// 1999/06/21 +// Buf441 slot is Enabled. +// -------------------------------------------- +// 04/09 creat +// 04/12 stop nise fix +// 06/21 WorkingOff timming +static unsigned long CntrlInst1E[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x00800D, 0x000810, 0x20043A, 0x001A82, + 0x03460D, 0x000810, 0x10043A, 0x02EC0D, + 0x000810, 0x18043A, 0x00010D, 0x020015, + 0x0000FD, 0x000020, 0x038860, 0x039060, + 0x038060, 0x038040, 0x038040, 0x038040, + 0x018040, 0x000A7D, 0x038040, 0x038040, + 0x018040, 0x200402, 0x000882, 0x08001A, + 0x000904, 0x017186, 0x000007, 0x260007, + 0x400007, 0x000007, 0x03258D, 0x000810, + 0x18043A, 0x260007, 0x284402, 0x00087D, + 0x018042, 0x00160A, 0x05A206, 0x000007, + 0x440007, 0x00230D, 0x000810, 0x08043A, + 0x22FA06, 0x000007, 0x0007FD, 0x018042, + 0x08000A, 0x000904, 0x02AB86, 0x000195, + 0x090D04, 0x000007, 0x000820, 0x0000F5, + 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, + 0x018040, 0x000A7D, 0x038042, 0x13804A, + 0x18000A, 0x001820, 0x059060, 0x058860, + 0x018040, 0x0000FD, 0x018042, 0x70000A, + 0x000115, 0x071144, 0x033B86, 0x030000, + 0x007020, 0x036206, 0x018040, 0x00360D, + 0x000810, 0x08043A, 0x232206, 0x000007, + 0x02EC0D, 0x000810, 0x18043A, 0x019A06, + 0x000007, 0x240007, 0x000F8D, 0x000810, + 0x00163A, 0x002402, 0x005C02, 0x0028FD, + 0x000020, 0x018040, 0x08000D, 0x000815, + 0x510984, 0x000007, 0x00004D, 0x000E5D, + 0x000E02, 0x00430D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x00008D, 0x000924, + 0x000F02, 0x00470D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x480480, 0x001210, + 0x28043A, 0x00778D, 0x000810, 0x280C3A, + 0x00068D, 0x000810, 0x28143A, 0x284402, + 0x03258D, 0x000810, 0x18043A, 0x07FF8D, + 0x000820, 0x0002FD, 0x018040, 0x260007, + 0x200007, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x051286, 0x000007, 0x240007, + 0x02EC0D, 0x000810, 0x18043A, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x019B86, 0x000007, 0x01B206, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x22B886, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x065A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x036186, 0x000007, 0x002104, 0x036186, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x07C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00788D, 0x000810, 0x08043A, 0x2A1206, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x070206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x229086, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x225886, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x08DA86, 0x00057D, 0x002820, + 0x03B060, 0x08F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x08FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x009D8D, 0x000810, + 0x08043A, 0x2A1206, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x0A5186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x00107D, 0x018042, + 0x08000A, 0x000215, 0x010984, 0x3A8186, + 0x000007, 0x007FBD, 0x383DC4, 0x000007, + 0x001A7D, 0x001375, 0x018042, 0x09004A, + 0x10000A, 0x0B8D04, 0x139504, 0x000007, + 0x000820, 0x019060, 0x001104, 0x225886, + 0x010040, 0x0017FD, 0x018042, 0x08000A, + 0x000904, 0x225A86, 0x000007, 0x00197D, + 0x038042, 0x09804A, 0x10000A, 0x000924, + 0x001664, 0x0011FD, 0x038042, 0x2B804A, + 0x19804A, 0x00008D, 0x218944, 0x000007, + 0x002244, 0x0C1986, 0x000007, 0x001A64, + 0x002A24, 0x00197D, 0x080102, 0x100122, + 0x000820, 0x039060, 0x018040, 0x003DFD, + 0x00008D, 0x000820, 0x018040, 0x001375, + 0x001A7D, 0x010042, 0x09804A, 0x10000A, + 0x00021D, 0x0189E4, 0x2992E4, 0x309144, + 0x000007, 0x00060D, 0x000A15, 0x000C1D, + 0x001025, 0x00A9E4, 0x012BE4, 0x000464, + 0x01B3E4, 0x0232E4, 0x000464, 0x000464, + 0x000464, 0x000464, 0x00040D, 0x08B1C4, + 0x000007, 0x000820, 0x000BF5, 0x030040, + 0x00197D, 0x038042, 0x09804A, 0x000A24, + 0x08000A, 0x080E64, 0x000007, 0x100122, + 0x000820, 0x031060, 0x010040, 0x0064AC, + 0x00027D, 0x000020, 0x018040, 0x00107D, + 0x018042, 0x0011FD, 0x3B804A, 0x09804A, + 0x20000A, 0x000095, 0x1A1144, 0x00A144, + 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, + 0x0018FD, 0x018042, 0x0010FD, 0x09804A, + 0x28000A, 0x000095, 0x010924, 0x002A64, + 0x0E4986, 0x000007, 0x002904, 0x0E5A86, + 0x000007, 0x0E6206, 0x080002, 0x00008D, + 0x00387D, 0x000820, 0x018040, 0x00127D, + 0x018042, 0x10000A, 0x003904, 0x0F0986, + 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, + 0x000025, 0x0FB206, 0x00002D, 0x000015, + 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, + 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, + 0x400025, 0x00008D, 0x110944, 0x000007, + 0x00018D, 0x109504, 0x000007, 0x009164, + 0x000424, 0x000424, 0x000424, 0x100102, + 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, + 0x00018D, 0x00042D, 0x00008D, 0x109504, + 0x000007, 0x00020D, 0x109184, 0x000007, + 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, + 0x018040, 0x003BFD, 0x001020, 0x03A860, + 0x000815, 0x313184, 0x212184, 0x000007, + 0x03B060, 0x03A060, 0x018040, 0x0022FD, + 0x000095, 0x010924, 0x000424, 0x000424, + 0x001264, 0x100102, 0x000820, 0x039060, + 0x018040, 0x001924, 0x010F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x000424, 0x000424, + 0x00117D, 0x018042, 0x08000A, 0x000A24, + 0x280502, 0x280C02, 0x09800D, 0x000820, + 0x0002FD, 0x018040, 0x200007, 0x0022FD, + 0x018042, 0x08000A, 0x000095, 0x280DC4, + 0x011924, 0x00197D, 0x018042, 0x0011FD, + 0x09804A, 0x10000A, 0x0000B5, 0x113144, + 0x0A8D04, 0x000007, 0x080A44, 0x129504, + 0x000007, 0x0023FD, 0x001020, 0x038040, + 0x101244, 0x000007, 0x000820, 0x039060, + 0x018040, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x123286, 0x000007, 0x003BFD, + 0x000100, 0x000A10, 0x0B807A, 0x13804A, + 0x090984, 0x000007, 0x000095, 0x013D04, + 0x12B886, 0x10000A, 0x100002, 0x090984, + 0x000007, 0x038042, 0x11804A, 0x090D04, + 0x000007, 0x10000A, 0x090D84, 0x000007, + 0x00257D, 0x000820, 0x018040, 0x00010D, + 0x000810, 0x28143A, 0x00127D, 0x018042, + 0x20000A, 0x00197D, 0x018042, 0x00117D, + 0x31804A, 0x10000A, 0x003124, 0x013B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x300102, + 0x003124, 0x000424, 0x000424, 0x001224, + 0x280502, 0x001A4C, 0x143986, 0x700002, + 0x00002D, 0x030000, 0x00387D, 0x018042, + 0x10000A, 0x146206, 0x002124, 0x0000AD, + 0x100002, 0x00010D, 0x000924, 0x006B24, + 0x014A0D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x003264, 0x00008D, 0x000A24, 0x001020, + 0x00227D, 0x018040, 0x014F8D, 0x000810, + 0x08043A, 0x2B5A06, 0x000007, 0x002820, + 0x00207D, 0x018040, 0x00117D, 0x038042, + 0x13804A, 0x33800A, 0x00387D, 0x018042, + 0x08000A, 0x000904, 0x177286, 0x000007, + 0x00008D, 0x030964, 0x015B0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x380102, 0x000424, + 0x000424, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x280502, 0x001A4C, 0x177186, 0x000007, + 0x032164, 0x00632C, 0x003DFD, 0x018042, + 0x08000A, 0x000095, 0x090904, 0x000007, + 0x000820, 0x001A4C, 0x169986, 0x018040, + 0x030000, 0x16B206, 0x002124, 0x00010D, + 0x000924, 0x006B24, 0x016F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x003A64, 0x000095, + 0x001224, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x171286, 0x000007, 0x01760D, + 0x000810, 0x08043A, 0x2B5A06, 0x000007, + 0x160A06, 0x000007, 0x007020, 0x08010A, + 0x10012A, 0x0020FD, 0x038860, 0x039060, + 0x018040, 0x00227D, 0x018042, 0x003DFD, + 0x08000A, 0x31844A, 0x000904, 0x181086, + 0x18008B, 0x00008D, 0x189904, 0x00312C, + 0x18E206, 0x000007, 0x00324C, 0x186B86, + 0x000007, 0x001904, 0x186886, 0x000007, + 0x000095, 0x199144, 0x00222C, 0x003124, + 0x00636C, 0x000E3D, 0x001375, 0x000BFD, + 0x010042, 0x09804A, 0x10000A, 0x038AEC, + 0x0393EC, 0x00224C, 0x18E186, 0x000007, + 0x00008D, 0x189904, 0x00226C, 0x00322C, + 0x30050A, 0x301DAB, 0x002083, 0x0018FD, + 0x018042, 0x08000A, 0x018924, 0x300502, + 0x001083, 0x001875, 0x010042, 0x10000A, + 0x00008D, 0x010924, 0x001375, 0x330542, + 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, + 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, + 0x006083, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x19B286, 0x000007, 0x001E2D, + 0x0005FD, 0x018042, 0x08000A, 0x028924, + 0x280502, 0x00060D, 0x000810, 0x280C3A, + 0x00008D, 0x000810, 0x28143A, 0x0A808D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x001275, 0x030042, 0x21004A, 0x00008D, + 0x1A0944, 0x000007, 0x01AB8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, + 0x030042, 0x0D004A, 0x10000A, 0x089144, + 0x000007, 0x000820, 0x010040, 0x0025F5, + 0x0A3144, 0x000007, 0x000820, 0x032860, + 0x030040, 0x00217D, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00008D, 0x000124, 0x00012C, 0x000E64, + 0x001A64, 0x00636C, 0x08010A, 0x10012A, + 0x000820, 0x031060, 0x030040, 0x0020FD, + 0x018042, 0x08000A, 0x00227D, 0x018042, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00197D, 0x018042, 0x08000A, 0x0022FD, + 0x038042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x090D04, 0x000007, 0x000820, + 0x030040, 0x038042, 0x0B804A, 0x10000A, + 0x000820, 0x031060, 0x030040, 0x038042, + 0x13804A, 0x19804A, 0x110D04, 0x198D04, + 0x000007, 0x08000A, 0x001020, 0x031860, + 0x030860, 0x030040, 0x00008D, 0x0B0944, + 0x000007, 0x000820, 0x010040, 0x0005F5, + 0x030042, 0x08000A, 0x000820, 0x010040, + 0x0000F5, 0x010042, 0x08000A, 0x000904, + 0x1D9886, 0x001E75, 0x030042, 0x01044A, + 0x000C0A, 0x1DAA06, 0x000007, 0x000402, + 0x000C02, 0x00177D, 0x001AF5, 0x018042, + 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, + 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, + 0x00043D, 0x0013F5, 0x001AFD, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x005502, 0x005D0A, 0x030042, 0x0B004A, + 0x1B804A, 0x13804A, 0x20000A, 0x089144, + 0x19A144, 0x0389E4, 0x0399EC, 0x006502, + 0x006D0A, 0x030042, 0x0B004A, 0x19004A, + 0x2B804A, 0x13804A, 0x21804A, 0x30000A, + 0x089144, 0x19A144, 0x2AB144, 0x0389E4, + 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, + 0x000702, 0x00107D, 0x000415, 0x018042, + 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, + 0x0019FD, 0x010042, 0x09804A, 0x10000A, + 0x000934, 0x001674, 0x0029F5, 0x010042, + 0x10000A, 0x00917C, 0x002075, 0x010042, + 0x08000A, 0x000904, 0x200A86, 0x0026F5, + 0x0027F5, 0x030042, 0x09004A, 0x10000A, + 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, + 0x010042, 0x51804A, 0x48000A, 0x160007, + 0x001075, 0x010042, 0x282C0A, 0x281D12, + 0x282512, 0x001F32, 0x1E0007, 0x0E0007, + 0x001975, 0x010042, 0x002DF5, 0x0D004A, + 0x10000A, 0x009144, 0x20EA86, 0x010042, + 0x28340A, 0x000E5D, 0x00008D, 0x000375, + 0x000820, 0x010040, 0x05D2F4, 0x54D104, + 0x00735C, 0x218B86, 0x000007, 0x0C0007, + 0x080007, 0x0A0007, 0x02178D, 0x000810, + 0x08043A, 0x34B206, 0x000007, 0x219206, + 0x000007, 0x080007, 0x002275, 0x010042, + 0x20000A, 0x002104, 0x225886, 0x001E2D, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x21CA86, 0x000007, 0x002010, 0x30043A, + 0x00057D, 0x0180C3, 0x08000A, 0x028924, + 0x280502, 0x280C02, 0x0A810D, 0x000820, + 0x0002F5, 0x010040, 0x220007, 0x0004FD, + 0x018042, 0x70000A, 0x030000, 0x007020, + 0x07FA06, 0x018040, 0x022B8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x22C286, + 0x000007, 0x020206, 0x000007, 0x000875, + 0x0009FD, 0x00010D, 0x234206, 0x000295, + 0x000B75, 0x00097D, 0x00000D, 0x000515, + 0x010042, 0x18000A, 0x001904, 0x2A0086, + 0x0006F5, 0x001020, 0x010040, 0x0004F5, + 0x000820, 0x010040, 0x000775, 0x010042, + 0x09804A, 0x10000A, 0x001124, 0x000904, + 0x23F286, 0x000815, 0x080102, 0x101204, + 0x241206, 0x000575, 0x081204, 0x000007, + 0x100102, 0x000575, 0x000425, 0x021124, + 0x100102, 0x000820, 0x031060, 0x010040, + 0x001924, 0x2A0086, 0x00008D, 0x000464, + 0x009D04, 0x291086, 0x180102, 0x000575, + 0x010042, 0x28040A, 0x00018D, 0x000924, + 0x280D02, 0x00000D, 0x000924, 0x281502, + 0x10000D, 0x000820, 0x0002F5, 0x010040, + 0x200007, 0x001175, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x24FA86, 0x000007, + 0x000100, 0x080B20, 0x130B60, 0x1B0B60, + 0x030A60, 0x010040, 0x050042, 0x3D004A, + 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, + 0x010042, 0x28140A, 0x0004F5, 0x010042, + 0x08000A, 0x000315, 0x010D04, 0x260286, + 0x004015, 0x000095, 0x010D04, 0x25F086, + 0x100022, 0x10002A, 0x261A06, 0x000007, + 0x333104, 0x2AA904, 0x000007, 0x032124, + 0x280502, 0x284402, 0x001124, 0x400102, + 0x000424, 0x000424, 0x003224, 0x00292C, + 0x00636C, 0x277386, 0x000007, 0x02B164, + 0x000464, 0x000464, 0x00008D, 0x000A64, + 0x280D02, 0x10008D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x00008D, 0x38B904, + 0x000007, 0x03296C, 0x30010A, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x270286, + 0x000007, 0x00212C, 0x28050A, 0x00316C, + 0x00046C, 0x00046C, 0x28450A, 0x001124, + 0x006B64, 0x100102, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x004124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x27FA86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x284086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x284A86, 0x000007, 0x284402, 0x003124, + 0x300502, 0x003924, 0x300583, 0x000883, + 0x0005F5, 0x010042, 0x28040A, 0x00008D, + 0x008124, 0x280D02, 0x00008D, 0x008124, + 0x281502, 0x10018D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001025, 0x000575, + 0x030042, 0x09004A, 0x10000A, 0x0A0904, + 0x121104, 0x000007, 0x001020, 0x050860, + 0x050040, 0x0006FD, 0x018042, 0x09004A, + 0x10000A, 0x0000A5, 0x0A0904, 0x121104, + 0x000007, 0x000820, 0x019060, 0x010040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x29CA86, 0x000007, 0x244206, 0x000007, + 0x000606, 0x000007, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x2A1A86, 0x000007, + 0x000100, 0x080B20, 0x138B60, 0x1B8B60, + 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, + 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, + 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, + 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, + 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, + 0x038A60, 0x000606, 0x018040, 0x00008D, + 0x000A64, 0x280D02, 0x000A24, 0x00027D, + 0x018042, 0x10000A, 0x001224, 0x0003FD, + 0x018042, 0x08000A, 0x000904, 0x2C0A86, + 0x000007, 0x00018D, 0x000A24, 0x000464, + 0x000464, 0x080102, 0x000924, 0x000424, + 0x000424, 0x100102, 0x02000D, 0x009144, + 0x2C6186, 0x000007, 0x0001FD, 0x018042, + 0x08000A, 0x000A44, 0x2C4386, 0x018042, + 0x0A000D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00027D, 0x001020, 0x000606, + 0x018040, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x2CB286, 0x000007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2CE286, + 0x000007, 0x000075, 0x002E7D, 0x010042, + 0x0B804A, 0x000020, 0x000904, 0x000686, + 0x010040, 0x31844A, 0x30048B, 0x000883, + 0x00008D, 0x000810, 0x28143A, 0x00008D, + 0x000810, 0x280C3A, 0x000675, 0x010042, + 0x08000A, 0x003815, 0x010924, 0x280502, + 0x0B000D, 0x000820, 0x0002F5, 0x010040, + 0x000606, 0x220007, 0x000464, 0x000464, + 0x000606, 0x000007, 0x000134, 0x007F8D, + 0x00093C, 0x281D12, 0x282512, 0x001F32, + 0x0E0007, 0x00010D, 0x00037D, 0x000820, + 0x018040, 0x05D2F4, 0x000007, 0x080007, + 0x00037D, 0x018042, 0x08000A, 0x000904, + 0x2E8A86, 0x000007, 0x000606, 0x000007, + 0x000007, 0x000012, 0x100007, 0x320007, + 0x600007, 0x460007, 0x100080, 0x48001A, + 0x004904, 0x2EF186, 0x000007, 0x001210, + 0x58003A, 0x000145, 0x5C5D04, 0x000007, + 0x000080, 0x48001A, 0x004904, 0x2F4186, + 0x000007, 0x001210, 0x50003A, 0x005904, + 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, + 0x7FFF7D, 0x07D524, 0x004224, 0x500102, + 0x200502, 0x000082, 0x40001A, 0x004104, + 0x2FC986, 0x000007, 0x003865, 0x40001A, + 0x004020, 0x00104D, 0x04C184, 0x31AB86, + 0x000040, 0x040007, 0x000165, 0x000145, + 0x004020, 0x000040, 0x000765, 0x080080, + 0x40001A, 0x004104, 0x305986, 0x000007, + 0x001210, 0x40003A, 0x004104, 0x30B286, + 0x00004D, 0x0000CD, 0x004810, 0x20043A, + 0x000882, 0x40001A, 0x004104, 0x30C186, + 0x000007, 0x004820, 0x005904, 0x319886, + 0x000040, 0x0007E5, 0x200480, 0x2816A0, + 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, + 0x000040, 0x000032, 0x400075, 0x00007D, + 0x07D574, 0x200512, 0x000082, 0x40001A, + 0x004104, 0x317186, 0x000007, 0x038A06, + 0x640007, 0x0000E5, 0x000020, 0x000040, + 0x000A65, 0x000020, 0x020040, 0x020040, + 0x000040, 0x000165, 0x000042, 0x70000A, + 0x007104, 0x323286, 0x000007, 0x060007, + 0x019A06, 0x640007, 0x050000, 0x007020, + 0x000040, 0x038A06, 0x640007, 0x000007, + 0x00306D, 0x028860, 0x029060, 0x08000A, + 0x028860, 0x008040, 0x100012, 0x00100D, + 0x009184, 0x32D186, 0x000E0D, 0x009184, + 0x33E186, 0x000007, 0x300007, 0x001020, + 0x003B6D, 0x008040, 0x000080, 0x08001A, + 0x000904, 0x32F186, 0x000007, 0x001220, + 0x000DED, 0x008040, 0x008042, 0x10000A, + 0x40000D, 0x109544, 0x000007, 0x001020, + 0x000DED, 0x008040, 0x008042, 0x20040A, + 0x000082, 0x08001A, 0x000904, 0x338186, + 0x000007, 0x003B6D, 0x008042, 0x08000A, + 0x000E15, 0x010984, 0x342B86, 0x600007, + 0x08001A, 0x000C15, 0x010984, 0x341386, + 0x000020, 0x1A0007, 0x0002ED, 0x008040, + 0x620007, 0x00306D, 0x028042, 0x0A804A, + 0x000820, 0x0A804A, 0x000606, 0x10804A, + 0x000007, 0x282512, 0x001F32, 0x05D2F4, + 0x54D104, 0x00735C, 0x000786, 0x000007, + 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, + 0x020040, 0x004820, 0x025060, 0x40000A, + 0x024060, 0x000040, 0x454944, 0x000007, + 0x004020, 0x003AE5, 0x000040, 0x0028E5, + 0x000042, 0x48000A, 0x004904, 0x39F886, + 0x002C65, 0x000042, 0x40000A, 0x0000D5, + 0x454104, 0x000007, 0x000655, 0x054504, + 0x368286, 0x0001D5, 0x054504, 0x368086, + 0x002B65, 0x000042, 0x003AE5, 0x50004A, + 0x40000A, 0x45C3D4, 0x000007, 0x454504, + 0x000007, 0x0000CD, 0x444944, 0x000007, + 0x454504, 0x000007, 0x00014D, 0x554944, + 0x000007, 0x045144, 0x367986, 0x002C65, + 0x000042, 0x48000A, 0x4CD104, 0x000007, + 0x04C144, 0x368386, 0x000007, 0x160007, + 0x002CE5, 0x040042, 0x40000A, 0x004020, + 0x000040, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x36F086, 0x000007, 0x002402, + 0x383206, 0x005C02, 0x0025E5, 0x000042, + 0x40000A, 0x004274, 0x002AE5, 0x000042, + 0x40000A, 0x004274, 0x500112, 0x0029E5, + 0x000042, 0x40000A, 0x004234, 0x454104, + 0x000007, 0x004020, 0x000040, 0x003EE5, + 0x000020, 0x000040, 0x002DE5, 0x400152, + 0x50000A, 0x045144, 0x37DA86, 0x0000C5, + 0x003EE5, 0x004020, 0x000040, 0x002BE5, + 0x000042, 0x40000A, 0x404254, 0x000007, + 0x002AE5, 0x004020, 0x000040, 0x500132, + 0x040134, 0x005674, 0x0029E5, 0x020042, + 0x42000A, 0x000042, 0x50000A, 0x05417C, + 0x0028E5, 0x000042, 0x48000A, 0x0000C5, + 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, + 0x020042, 0x40004A, 0x50000A, 0x00423C, + 0x00567C, 0x0028E5, 0x004820, 0x000040, + 0x281D12, 0x282512, 0x001F72, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x393A86, + 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, + 0x000042, 0x40000A, 0x004104, 0x397886, + 0x002D65, 0x000042, 0x28340A, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, + 0x39E186, 0x000007, 0x000606, 0x080007, + 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, + 0x020045, 0x004020, 0x000060, 0x000365, + 0x000040, 0x002E65, 0x001A20, 0x0A1A60, + 0x000040, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x000606, 0x50004A, + 0x0017FD, 0x018042, 0x08000A, 0x000904, + 0x225A86, 0x000007, 0x00107D, 0x018042, + 0x0011FD, 0x33804A, 0x19804A, 0x20000A, + 0x000095, 0x2A1144, 0x01A144, 0x3B9086, + 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, + 0x018042, 0x0010FD, 0x09804A, 0x38000A, + 0x000095, 0x010924, 0x003A64, 0x3B8186, + 0x000007, 0x003904, 0x3B9286, 0x000007, + 0x3B9A06, 0x00000D, 0x00008D, 0x000820, + 0x00387D, 0x018040, 0x700002, 0x00117D, + 0x018042, 0x00197D, 0x29804A, 0x30000A, + 0x380002, 0x003124, 0x000424, 0x000424, + 0x002A24, 0x280502, 0x00068D, 0x000810, + 0x28143A, 0x00750D, 0x00B124, 0x002264, + 0x3D0386, 0x284402, 0x000810, 0x280C3A, + 0x0B800D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00758D, 0x00B124, 0x100102, + 0x012144, 0x3E4986, 0x001810, 0x10003A, + 0x00387D, 0x018042, 0x08000A, 0x000904, + 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, + 0x00008D, 0x023164, 0x000A64, 0x280D02, + 0x0B808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00387D, 0x018042, 0x08000A, + 0x000904, 0x3E3286, 0x030000, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x3D8286, + 0x000007, 0x002810, 0x28043A, 0x00750D, + 0x030924, 0x002264, 0x280D02, 0x02316C, + 0x28450A, 0x0B810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x00008D, 0x000A24, + 0x3E4A06, 0x100102, 0x001810, 0x10003A, + 0x0000BD, 0x003810, 0x30043A, 0x00187D, + 0x018042, 0x0018FD, 0x09804A, 0x20000A, + 0x0000AD, 0x028924, 0x07212C, 0x001010, + 0x300583, 0x300D8B, 0x3014BB, 0x301C83, + 0x002083, 0x00137D, 0x038042, 0x33844A, + 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, + 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, + 0x001E0D, 0x0005FD, 0x018042, 0x20000A, + 0x020924, 0x00068D, 0x00A96C, 0x00009D, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x3F6A86, 0x000007, 0x280502, 0x280D0A, + 0x284402, 0x001810, 0x28143A, 0x0C008D, + 0x000820, 0x0002FD, 0x018040, 0x220007, + 0x003904, 0x225886, 0x001E0D, 0x00057D, + 0x018042, 0x20000A, 0x020924, 0x0000A5, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x402A86, 0x000007, 0x280502, 0x280C02, + 0x002010, 0x28143A, 0x0C010D, 0x000820, + 0x0002FD, 0x018040, 0x225A06, 0x220007, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +#endif //_HWMCODE_ diff -Nru a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/ymfpci/ymfpci_main.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,2088 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of YMF724/740/744/754 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t ymfpci_t + +/* + * constants + */ + +/* + * common I/O routines + */ + +static void snd_ymfpci_irq_wait(ymfpci_t *chip); + +static inline u8 snd_ymfpci_readb(ymfpci_t *chip, u32 offset) +{ + return readb(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writeb(ymfpci_t *chip, u32 offset, u8 val) +{ + writeb(val, chip->reg_area_virt + offset); +} + +static inline u16 snd_ymfpci_readw(ymfpci_t *chip, u32 offset) +{ + return readw(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writew(ymfpci_t *chip, u32 offset, u16 val) +{ + writew(val, chip->reg_area_virt + offset); +} + +static inline u32 snd_ymfpci_readl(ymfpci_t *chip, u32 offset) +{ + return readl(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writel(ymfpci_t *chip, u32 offset, u32 val) +{ + writel(val, chip->reg_area_virt + offset); +} + +static int snd_ymfpci_codec_ready(ymfpci_t *chip, int secondary, int sched) +{ + signed long end_time; + u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; + + end_time = (jiffies + ((3 * HZ) / 4)) + 1; + do { + if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg)); + return -EBUSY; +} + +static void snd_ymfpci_codec_write(ac97_t *ac97, u16 reg, u16 val) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return); + u32 cmd; + + snd_ymfpci_codec_ready(chip, 0, 0); + cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val; + snd_ymfpci_writel(chip, YDSXGR_AC97CMDDATA, cmd); +} + +static u16 snd_ymfpci_codec_read(ac97_t *ac97, u16 reg) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return -ENXIO); + + if (snd_ymfpci_codec_ready(chip, 0, 0)) + return ~0; + snd_ymfpci_writew(chip, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); + if (snd_ymfpci_codec_ready(chip, 0, 0)) + return ~0; + if (chip->device_id == PCI_DEVICE_ID_YAMAHA_744 && chip->rev < 2) { + int i; + for (i = 0; i < 600; i++) + snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA); + } + return snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA); +} + +/* + * Misc routines + */ + +static u32 snd_ymfpci_calc_delta(u32 rate) +{ + switch (rate) { + case 8000: return 0x02aaab00; + case 11025: return 0x03accd00; + case 16000: return 0x05555500; + case 22050: return 0x07599a00; + case 32000: return 0x0aaaab00; + case 44100: return 0x0eb33300; + default: return ((rate << 16) / 375) << 5; + } +} + +static u32 def_rate[8] = { + 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 +}; + +static u32 snd_ymfpci_calc_lpfK(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000, + 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000 + }; + + if (rate == 44100) + return 0x40000000; /* FIXME: What's the right value? */ + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 snd_ymfpci_calc_lpfQ(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x35280000, 0x34A70000, 0x32020000, 0x31770000, + 0x31390000, 0x31C90000, 0x33D00000, 0x40000000 + }; + + if (rate == 44100) + return 0x370A0000; + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +/* + * Hardware start management + */ + +static void snd_ymfpci_hw_start(ymfpci_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->start_count++ > 0) + goto __end; + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) | 3); + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1; + __end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_ymfpci_hw_stop(ymfpci_t *chip) +{ + unsigned long flags; + long timeout = 1000; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (--chip->start_count > 0) + goto __end; + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) & ~3); + while (timeout-- > 0) { + if ((snd_ymfpci_readl(chip, YDSXGR_STATUS) & 2) == 0) + break; + } + if (atomic_read(&chip->interrupt_sleep_count)) { + atomic_set(&chip->interrupt_sleep_count, 0); + wake_up(&chip->interrupt_sleep); + } + __end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* + * Playback voice management + */ + +static int voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice) +{ + ymfpci_voice_t *voice, *voice2; + int idx; + + *rvoice = NULL; + for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) { + voice = &chip->voices[idx]; + voice2 = pair ? &chip->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case YMFPCI_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case YMFPCI_SYNTH: + voice->synth = 1; + break; + case YMFPCI_MIDI: + voice->midi = 1; + break; + } + snd_ymfpci_hw_start(chip); + if (voice2) + snd_ymfpci_hw_start(chip); + *rvoice = voice; + return 0; + } + return -ENOMEM; +} + +int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice) +{ + unsigned long flags; + int result; + + snd_assert(rvoice != NULL, return -EINVAL); + snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL); + + spin_lock_irqsave(&chip->voice_lock, flags); + for (;;) { + result = voice_alloc(chip, type, pair, rvoice); + if (result == 0 || type != YMFPCI_PCM) + break; + /* TODO: synth/midi voice deallocation */ + break; + } + spin_unlock_irqrestore(&chip->voice_lock, flags); + return result; +} + +int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice) +{ + unsigned long flags; + + snd_assert(pvoice != NULL, return -EINVAL); + snd_ymfpci_hw_stop(chip); + spin_lock_irqsave(&chip->voice_lock, flags); + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->ypcm = NULL; + pvoice->interrupt = NULL; + spin_unlock_irqrestore(&chip->voice_lock, flags); + return 0; +} + +/* + * PCM part + */ + +static void snd_ymfpci_pcm_interrupt(ymfpci_t *chip, ymfpci_voice_t *voice) +{ + ymfpci_pcm_t *ypcm; + u32 pos, delta; + + if ((ypcm = voice->ypcm) == NULL) + return; + if (ypcm->substream == NULL) + return; + spin_lock(&chip->reg_lock); + if (ypcm->running) { + pos = le32_to_cpu(voice->bank[chip->active_bank].start); + if (pos < ypcm->last_pos) + delta = pos + (ypcm->buffer_size - ypcm->last_pos); + else + delta = pos - ypcm->last_pos; + ypcm->period_pos += delta; + ypcm->last_pos = pos; + while (ypcm->period_pos >= ypcm->period_size) { + // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(ypcm->substream); + spin_lock(&chip->reg_lock); + ypcm->period_pos -= ypcm->period_size; + } + } + spin_unlock(&chip->reg_lock); +} + +static void snd_ymfpci_pcm_capture_interrupt(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return); + ymfpci_t *chip = ypcm->chip; + u32 pos, delta; + + spin_lock(&chip->reg_lock); + if (ypcm->running) { + pos = le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift; + if (pos < ypcm->last_pos) + delta = pos + (ypcm->buffer_size - ypcm->last_pos); + else + delta = pos - ypcm->last_pos; + ypcm->period_pos += delta; + ypcm->last_pos = pos; + while (ypcm->period_pos >= ypcm->period_size) { + ypcm->period_pos = 0; + // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(substream); + spin_lock(&chip->reg_lock); + } + } + spin_unlock(&chip->reg_lock); +} + +static int snd_ymfpci_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO); + int result = 0; + + spin_lock(&chip->reg_lock); + if (ypcm->voices[0] == NULL) { + result = -EINVAL; + goto __unlock; + } + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr); + if (ypcm->voices[1] != NULL) + chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr); + ypcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0; + if (ypcm->voices[1] != NULL) + chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0; + ypcm->running = 0; + break; + default: + result = -EINVAL; + break; + } + __unlock: + spin_unlock(&chip->reg_lock); + return result; +} +static int snd_ymfpci_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO); + int result = 0; + u32 tmp; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp); + ypcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp); + ypcm->running = 0; + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices) +{ + int err; + + if (ypcm->voices[1] != NULL && voices < 2) { + snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (voices == 1 && ypcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) + return 0; /* already allocated */ + if (voices > 1) { + if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { + snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + } + err = snd_ymfpci_voice_alloc(ypcm->chip, YMFPCI_PCM, voices > 1, &ypcm->voices[0]); + if (err < 0) + return err; + ypcm->voices[0]->ypcm = ypcm; + ypcm->voices[0]->interrupt = snd_ymfpci_pcm_interrupt; + if (voices > 1) { + ypcm->voices[1] = &ypcm->chip->voices[ypcm->voices[0]->number + 1]; + ypcm->voices[1]->ypcm = ypcm; + } + return 0; +} + +static void snd_ymfpci_pcm_init_voice(ymfpci_voice_t *voice, int stereo, + int rate, int w_16, unsigned long addr, + unsigned int end, int eff2) +{ + u32 format; + u32 delta = snd_ymfpci_calc_delta(rate); + u32 lpfQ = snd_ymfpci_calc_lpfQ(rate); + u32 lpfK = snd_ymfpci_calc_lpfK(rate); + snd_ymfpci_playback_bank_t *bank; + unsigned int nbank; + + snd_assert(voice != NULL, return); + format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); + for (nbank = 0; nbank < 2; nbank++) { + bank = &voice->bank[nbank]; + bank->format = cpu_to_le32(format); + bank->loop_default = 0; + bank->base = cpu_to_le32(addr); + bank->loop_start = 0; + bank->loop_end = cpu_to_le32(end); + bank->loop_frac = 0; + bank->eg_gain_end = cpu_to_le32(0x40000000); + bank->lpfQ = cpu_to_le32(lpfQ); + bank->status = 0; + bank->num_of_frames = 0; + bank->loop_count = 0; + bank->start = 0; + bank->start_frac = 0; + bank->delta = + bank->delta_end = cpu_to_le32(delta); + bank->lpfK = + bank->lpfK_end = cpu_to_le32(lpfK); + bank->eg_gain = cpu_to_le32(0x40000000); + bank->lpfD1 = + bank->lpfD2 = 0; + + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = + bank->eff1_gain = + bank->eff2_gain = + bank->eff3_gain = + bank->eff1_gain_end = + bank->eff2_gain_end = + bank->eff3_gain_end = 0; + + if (!stereo) { + if (!eff2) { + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = cpu_to_le32(0x40000000); + } else { + bank->eff2_gain = + bank->eff2_gain_end = + bank->eff3_gain = + bank->eff3_gain_end = cpu_to_le32(0x40000000); + } + } else { + if (!eff2) { + if ((voice->number & 1) == 0) { + bank->left_gain = + bank->left_gain_end = cpu_to_le32(0x40000000); + } else { + bank->format |= cpu_to_le32(1); + bank->right_gain = + bank->right_gain_end = cpu_to_le32(0x40000000); + } + } else { + if ((voice->number & 1) == 0) { + bank->eff3_gain = + bank->eff3_gain_end = cpu_to_le32(0x40000000); + } else { + bank->format |= cpu_to_le32(1); + bank->eff2_gain = + bank->eff2_gain_end = cpu_to_le32(0x40000000); + } + } + } + } +} + +static int snd_ymfpci_ac3_init(ymfpci_t *chip) +{ + unsigned char *ptr; + dma_addr_t ptr_addr; + + if (chip->ac3_tmp_base != NULL) + return -EBUSY; + if ((ptr = snd_malloc_pci_pages(chip->pci, 4096, &ptr_addr)) == NULL) + return -ENOMEM; + + chip->ac3_tmp_base = ptr; + chip->ac3_tmp_base_addr = ptr_addr; + chip->bank_effect[3][0]->base = + chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr); + chip->bank_effect[3][0]->loop_end = + chip->bank_effect[3][1]->loop_end = cpu_to_le32(1024); + chip->bank_effect[4][0]->base = + chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr + 2048); + chip->bank_effect[4][0]->loop_end = + chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024); + + spin_lock_irq(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, + snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) | 3 << 3); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int snd_ymfpci_ac3_done(ymfpci_t *chip) +{ + spin_lock_irq(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, + snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3)); + spin_unlock_irq(&chip->reg_lock); + snd_ymfpci_irq_wait(chip); + if (chip->ac3_tmp_base) { + snd_free_pci_pages(chip->pci, 4096, chip->ac3_tmp_base, chip->ac3_tmp_base_addr); + chip->ac3_tmp_base = NULL; + } + return 0; +} + +static int snd_ymfpci_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if ((err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params))) < 0) + return err; + if (ypcm->spdif || ypcm->mode4ch) + if ((err = snd_ymfpci_ac3_init(chip)) < 0) + return err; + return 0; +} + +static int snd_ymfpci_playback_hw_free(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + + + if (runtime->private_data == NULL) + return 0; + ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + + /* wait, until the PCI operations are not finished */ + snd_ymfpci_irq_wait(chip); + snd_pcm_lib_free_pages(substream); + if (ypcm->voices[1]) { + snd_ymfpci_voice_free(chip, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (ypcm->voices[0]) { + snd_ymfpci_voice_free(chip, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + if (ypcm->spdif || ypcm->mode4ch) + snd_ymfpci_ac3_done(chip); + return 0; +} + +static int snd_ymfpci_playback_prepare(snd_pcm_substream_t * substream) +{ + // ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + int nvoice; + + ypcm->period_size = runtime->period_size; + ypcm->buffer_size = runtime->buffer_size; + ypcm->period_pos = 0; + ypcm->last_pos = 0; + for (nvoice = 0; nvoice < runtime->channels; nvoice++) + snd_ymfpci_pcm_init_voice(ypcm->voices[nvoice], + runtime->channels == 2, + runtime->rate, + snd_pcm_format_width(runtime->format) == 16, + runtime->dma_addr, + ypcm->buffer_size, + ypcm->spdif || ypcm->mode4ch); + return 0; +} + +static int snd_ymfpci_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ymfpci_capture_hw_free(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + + /* wait, until the PCI operations are not finished */ + snd_ymfpci_irq_wait(chip); + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ymfpci_capture_prepare(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + snd_ymfpci_capture_bank_t * bank; + int nbank; + u32 rate, format; + + ypcm->period_size = runtime->period_size; + ypcm->buffer_size = runtime->buffer_size; + ypcm->period_pos = 0; + ypcm->last_pos = 0; + ypcm->shift = 0; + rate = ((48000 * 4096) / runtime->rate) - 1; + format = 0; + if (runtime->channels == 2) { + format |= 2; + ypcm->shift++; + } + if (snd_pcm_format_width(runtime->format) == 8) + format |= 1; + else + ypcm->shift++; + switch (ypcm->capture_bank_number) { + case 0: + snd_ymfpci_writel(chip, YDSXGR_RECFORMAT, format); + snd_ymfpci_writel(chip, YDSXGR_RECSLOTSR, rate); + break; + case 1: + snd_ymfpci_writel(chip, YDSXGR_ADCFORMAT, format); + snd_ymfpci_writel(chip, YDSXGR_ADCSLOTSR, rate); + break; + } + for (nbank = 0; nbank < 2; nbank++) { + bank = chip->bank_capture[ypcm->capture_bank_number][nbank]; + bank->base = cpu_to_le32(runtime->dma_addr); + bank->loop_end = cpu_to_le32(ypcm->buffer_size << ypcm->shift); + bank->start = 0; + bank->num_of_loops = 0; + } + return 0; +} + +static snd_pcm_uframes_t snd_ymfpci_playback_pointer(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + ymfpci_voice_t *voice = ypcm->voices[0]; + + if (!(ypcm->running && voice)) + return 0; + return le32_to_cpu(voice->bank[chip->active_bank].start); +} + +static snd_pcm_uframes_t snd_ymfpci_capture_pointer(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + + if (!ypcm->running) + return 0; + return le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift; +} + +static void snd_ymfpci_irq_wait(ymfpci_t *chip) +{ + wait_queue_t wait; + int loops = 4; + + while (loops-- > 0) { + if ((snd_ymfpci_readl(chip, YDSXGR_MODE) & 3) == 0) + continue; + init_waitqueue_entry(&wait, current); + add_wait_queue(&chip->interrupt_sleep, &wait); + atomic_inc(&chip->interrupt_sleep_count); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + remove_wait_queue(&chip->interrupt_sleep, &wait); + } +} + +static void snd_ymfpci_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, dev_id, return); + u32 status, nvoice, mode; + ymfpci_voice_t *voice; + + status = snd_ymfpci_readl(chip, YDSXGR_STATUS); + if (status & 0x80000000) { + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1; + spin_lock(&chip->voice_lock); + for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) { + voice = &chip->voices[nvoice]; + if (voice->interrupt) + voice->interrupt(chip, voice); + } + for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) { + if (chip->capture_substream[nvoice]) + snd_ymfpci_pcm_capture_interrupt(chip->capture_substream[nvoice]); + } +#if 0 + for (nvoice = 0; nvoice < YDSXG_EFFECT_VOICES; nvoice++) { + if (chip->effect_substream[nvoice]) + snd_ymfpci_pcm_effect_interrupt(chip->effect_substream[nvoice]); + } +#endif + spin_unlock(&chip->voice_lock); + spin_lock(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_STATUS, 0x80000000); + mode = snd_ymfpci_readl(chip, YDSXGR_MODE) | 2; + snd_ymfpci_writel(chip, YDSXGR_MODE, mode); + spin_unlock(&chip->reg_lock); + + if (atomic_read(&chip->interrupt_sleep_count)) { + atomic_set(&chip->interrupt_sleep_count, 0); + wake_up(&chip->interrupt_sleep); + } + } + + status = snd_ymfpci_readl(chip, YDSXGR_INTFLAG); + if (status & 1) { + /* timer handler */ + snd_ymfpci_writel(chip, YDSXGR_INTFLAG, ~0); + } + + if (chip->rawmidi) + snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data, regs); +} + +static snd_pcm_hardware_t snd_ymfpci_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 256 * 1024, /* FIXME: enough? */ + period_bytes_min: 64, + period_bytes_max: 256 * 1024, /* FIXME: enough? */ + periods_min: 3, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ymfpci_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 256 * 1024, /* FIXME: enough? */ + period_bytes_min: 64, + period_bytes_max: 256 * 1024, /* FIXME: enough? */ + periods_min: 3, + periods_max: 1024, + fifo_size: 0, +}; + +static void snd_ymfpci_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return); + + if (ypcm) + snd_magic_kfree(ypcm); +} + +static int snd_ymfpci_playback_open(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + + ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL); + if (ypcm == NULL) + return -ENOMEM; + ypcm->chip = chip; + ypcm->type = PLAYBACK_VOICE; + ypcm->substream = substream; + runtime->hw = snd_ymfpci_playback; + runtime->private_data = ypcm; + runtime->private_free = snd_ymfpci_pcm_free_substream; + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + return 0; +} + +static int snd_ymfpci_playback_spdif_open(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + unsigned long flags; + int err; + + if ((err = snd_ymfpci_playback_open(substream)) < 0) + return err; + ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0); + ypcm->spdif = 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, + snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2); + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30)); + chip->spdif_pcm_bits = chip->spdif_bits; + snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->spdif_pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id); + + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + return 0; +} + +static int snd_ymfpci_playback_4ch_open(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + unsigned long flags; + int err; + + if ((err = snd_ymfpci_playback_open(substream)) < 0) + return err; + ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0); + ypcm->mode4ch = 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writew(chip, YDSXGR_SECCONFIG, + (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0030) | 0x0010); + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + return 0; +} + +static int snd_ymfpci_capture_open(snd_pcm_substream_t * substream, + u32 capture_bank_number) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + + ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL); + if (ypcm == NULL) + return -ENOMEM; + ypcm->chip = chip; + ypcm->type = capture_bank_number + CAPTURE_REC; + ypcm->substream = substream; + ypcm->capture_bank_number = capture_bank_number; + chip->capture_substream[capture_bank_number] = substream; + runtime->hw = snd_ymfpci_capture; + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + runtime->private_data = ypcm; + runtime->private_free = snd_ymfpci_pcm_free_substream; + snd_ymfpci_hw_start(chip); + return 0; +} + +static int snd_ymfpci_capture_rec_open(snd_pcm_substream_t * substream) +{ + return snd_ymfpci_capture_open(substream, 0); +} + +static int snd_ymfpci_capture_ac97_open(snd_pcm_substream_t * substream) +{ + return snd_ymfpci_capture_open(substream, 1); +} + +static int snd_ymfpci_playback_close(snd_pcm_substream_t * substream) +{ + return 0; +} + +static int snd_ymfpci_playback_spdif_close(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30)); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, + snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id); + return snd_ymfpci_playback_close(substream); +} + +static int snd_ymfpci_playback_4ch_close(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30)); + snd_ymfpci_writew(chip, YDSXGR_SECCONFIG, + (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) | 0x0010); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return snd_ymfpci_playback_close(substream); +} + +static int snd_ymfpci_capture_close(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + + if (ypcm != NULL) { + chip->capture_substream[ypcm->capture_bank_number] = NULL; + snd_ymfpci_hw_stop(chip); + } + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_playback_ops = { + open: snd_ymfpci_playback_open, + close: snd_ymfpci_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ymfpci_playback_hw_params, + hw_free: snd_ymfpci_playback_hw_free, + prepare: snd_ymfpci_playback_prepare, + trigger: snd_ymfpci_playback_trigger, + pointer: snd_ymfpci_playback_pointer, +}; + +static snd_pcm_ops_t snd_ymfpci_capture_rec_ops = { + open: snd_ymfpci_capture_rec_open, + close: snd_ymfpci_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ymfpci_capture_hw_params, + hw_free: snd_ymfpci_capture_hw_free, + prepare: snd_ymfpci_capture_prepare, + trigger: snd_ymfpci_capture_trigger, + pointer: snd_ymfpci_capture_pointer, +}; + +static void snd_ymfpci_pcm_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_rec_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_capture_ac97_ops = { + open: snd_ymfpci_capture_ac97_open, + close: snd_ymfpci_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ymfpci_capture_hw_params, + hw_free: snd_ymfpci_capture_hw_free, + prepare: snd_ymfpci_capture_prepare, + trigger: snd_ymfpci_capture_trigger, + pointer: snd_ymfpci_capture_pointer, +}; + +static void snd_ymfpci_pcm2_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm2 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - AC'97", device, 0, 1, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm2_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_ac97_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - AC'97"); + chip->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_playback_spdif_ops = { + open: snd_ymfpci_playback_spdif_open, + close: snd_ymfpci_playback_spdif_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ymfpci_playback_hw_params, + hw_free: snd_ymfpci_playback_hw_free, + prepare: snd_ymfpci_playback_prepare, + trigger: snd_ymfpci_playback_trigger, + pointer: snd_ymfpci_playback_pointer, +}; + +static void snd_ymfpci_pcm_spdif_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm_spdif = NULL; +} + +int __devinit snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm_spdif_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_spdif_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - IEC958"); + chip->pcm_spdif = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_playback_4ch_ops = { + open: snd_ymfpci_playback_4ch_open, + close: snd_ymfpci_playback_4ch_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ymfpci_playback_hw_params, + hw_free: snd_ymfpci_playback_hw_free, + prepare: snd_ymfpci_playback_prepare, + trigger: snd_ymfpci_playback_trigger, + pointer: snd_ymfpci_playback_pointer, +}; + +static void snd_ymfpci_pcm_4ch_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm_4ch = NULL; +} + +int __devinit snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm_4ch_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_4ch_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - Rear PCM"); + chip->pcm_4ch = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +static int snd_ymfpci_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.iec958.status[0] = (chip->spdif_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (chip->spdif_bits >> 8) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ymfpci_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) | + (ucontrol->value.iec958.status[1] << 8); + spin_lock_irqsave(&chip->reg_lock, flags); + change = chip->spdif_bits != val; + chip->spdif_bits = val; + if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 1) && chip->pcm_spdif == NULL) + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_ymfpci_spdif_default __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_ymfpci_spdif_default_info, + get: snd_ymfpci_spdif_default_get, + put: snd_ymfpci_spdif_default_put +}; + +static int snd_ymfpci_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.iec958.status[0] = 0x3e; + ucontrol->value.iec958.status[1] = 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static snd_kcontrol_new_t snd_ymfpci_spdif_mask __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_ymfpci_spdif_mask_info, + get: snd_ymfpci_spdif_mask_get, +}; + +static int snd_ymfpci_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.iec958.status[0] = (chip->spdif_pcm_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (chip->spdif_pcm_bits >> 8) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ymfpci_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) | + (ucontrol->value.iec958.status[1] << 8); + spin_lock_irqsave(&chip->reg_lock, flags); + change = chip->spdif_pcm_bits != val; + chip->spdif_pcm_bits = val; + if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 2)) + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_ymfpci_spdif_stream __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_ymfpci_spdif_stream_info, + get: snd_ymfpci_spdif_stream_get, + put: snd_ymfpci_spdif_stream_put +}; + +/* + * Mixer controls + */ + +#define YMFPCI_SINGLE(xname, xindex, reg) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ymfpci_info_single, \ + get: snd_ymfpci_get_single, put: snd_ymfpci_put_single, \ + private_value: reg } + +static int snd_ymfpci_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + unsigned int mask = 1; + + switch (kcontrol->private_value) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ymfpci_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + unsigned int shift = 0, mask = 1, invert = 0; + + switch (kcontrol->private_value) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + ucontrol->value.integer.value[0] = (snd_ymfpci_readl(chip, reg) >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value; + unsigned int shift = 0, mask = 1, invert = 0; + int change; + unsigned int val, oval; + + switch (kcontrol->private_value) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_ymfpci_readl(chip, reg); + val = (oval & ~(mask << shift)) | val; + change = val != oval; + snd_ymfpci_writel(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define YMFPCI_DOUBLE(xname, xindex, reg) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ymfpci_info_double, \ + get: snd_ymfpci_get_double, put: snd_ymfpci_put_double, \ + private_value: reg } + +static int snd_ymfpci_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + unsigned int reg = kcontrol->private_value; + unsigned int mask = 16383; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int reg = kcontrol->private_value; + unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0; + unsigned int val; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + spin_lock_irqsave(&chip->reg_lock, flags); + val = snd_ymfpci_readl(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (val >> shift_left) & mask; + ucontrol->value.integer.value[1] = (val >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int reg = kcontrol->private_value; + unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0; + int change; + unsigned int val1, val2, oval; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_ymfpci_readl(chip, reg); + val1 = (oval & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != oval; + snd_ymfpci_writel(chip, reg, val1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define YMFPCI_CONTROLS (sizeof(snd_ymfpci_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ymfpci_controls[] __devinitdata = { +YMFPCI_DOUBLE("Wave Playback Volume", 0, YDSXGR_NATIVEDACOUTVOL), +YMFPCI_DOUBLE("Wave Capture Volume", 0, YDSXGR_NATIVEDACLOOPVOL), +YMFPCI_DOUBLE("Digital Capture Volume", 0, YDSXGR_NATIVEDACINVOL), +YMFPCI_DOUBLE("Digital Capture Volume", 1, YDSXGR_NATIVEADCINVOL), +YMFPCI_DOUBLE("ADC Playback Volume", 0, YDSXGR_PRIADCOUTVOL), +YMFPCI_DOUBLE("ADC Capture Volume", 0, YDSXGR_PRIADCLOOPVOL), +YMFPCI_DOUBLE("ADC Playback Volume", 1, YDSXGR_SECADCOUTVOL), +YMFPCI_DOUBLE("ADC Capture Volume", 1, YDSXGR_SECADCLOOPVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ", PLAYBACK,VOLUME), 0, YDSXGR_ZVOUTVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("", CAPTURE,VOLUME), 0, YDSXGR_ZVLOOPVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ",PLAYBACK,VOLUME), 1, YDSXGR_SPDIFOUTVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL), +YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL), +YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL) +}; + +/* + * Mixer routines + */ + +static void snd_ymfpci_mixer_free_ac97(ac97_t *ac97) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +int __devinit snd_ymfpci_mixer(ymfpci_t *chip) +{ + ac97_t ac97; + snd_kcontrol_t *kctl; + int err, idx; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ymfpci_codec_write; + ac97.read = snd_ymfpci_codec_read; + ac97.private_data = chip; + ac97.private_free = snd_ymfpci_mixer_free_ac97; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + for (idx = 0; idx < YMFPCI_CONTROLS; idx++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0) + return err; + } + + /* add S/PDIF control */ + snd_assert(chip->pcm_spdif != NULL, return -EIO); + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + chip->spdif_pcm_ctl = kctl; + + return 0; +} + + +/* + * joystick support + */ + +static int ymfpci_joystick_ports[4] = { + 0x201, 0x202, 0x204, 0x205 +}; + +static void setup_joystick_base(ymfpci_t *chip) +{ + if (chip->pci->device >= 0x0010) /* YMF 744/754 */ + pci_write_config_word(chip->pci, PCIR_DSXG_JOYBASE, + ymfpci_joystick_ports[chip->joystick_port]); + else { + u16 legacy_ctrl2; + pci_read_config_word(chip->pci, PCIR_DSXG_ELEGACY, &legacy_ctrl2); + legacy_ctrl2 &= ~(3 << 6); + legacy_ctrl2 |= chip->joystick_port << 6; + pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY, legacy_ctrl2); + } +} + +static int snd_ymfpci_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ymfpci_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + u16 val; + + pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY, &val); + ucontrol->value.integer.value[0] = (val & 0x04) ? 1 : 0; + return 0; +} + +static int snd_ymfpci_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + u16 val, oval; + + pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY, &oval); + val = oval & ~0x04; + if (ucontrol->value.integer.value[0]) + val |= 0x04; + if (val != oval) { + setup_joystick_base(chip); + pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY, val); + return 1; + } + return 0; +} + +static int snd_ymfpci_joystick_addr_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 3; + sprintf(uinfo->value.enumerated.name, "port 0x%x", ymfpci_joystick_ports[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ymfpci_joystick_addr_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->joystick_port; + return 0; +} + +static int snd_ymfpci_joystick_addr_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.integer.value[0] != chip->joystick_port) { + snd_assert(ucontrol->value.integer.value[0] >= 0 && ucontrol->value.integer.value[0] < 4, return -EINVAL); + chip->joystick_port = ucontrol->value.integer.value[0]; + setup_joystick_base(chip); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_ymfpci_control_joystick __devinitdata = { + name: "Joystick", + iface: SNDRV_CTL_ELEM_IFACE_CARD, + info: snd_ymfpci_joystick_info, + get: snd_ymfpci_joystick_get, + put: snd_ymfpci_joystick_put, +}; + +static snd_kcontrol_new_t snd_ymfpci_control_joystick_addr __devinitdata = { + name: "Joystick Address", + iface: SNDRV_CTL_ELEM_IFACE_CARD, + info: snd_ymfpci_joystick_addr_info, + get: snd_ymfpci_joystick_addr_get, + put: snd_ymfpci_joystick_addr_put, +}; + +int __devinit snd_ymfpci_joystick(ymfpci_t *chip) +{ + int err; + + chip->joystick_port = 0; /* default */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_control_joystick, chip))) < 0) + return err; + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_control_joystick_addr, chip))) < 0) + return err; + return 0; +} + + +/* + * proc interface + */ + +static void snd_ymfpci_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + // ymfpci_t *chip = snd_magic_cast(ymfpci_t, private_data, return); + + snd_iprintf(buffer, "YMFPCI\n\n"); +} + +static int __devinit snd_ymfpci_proc_init(snd_card_t * card, ymfpci_t *chip) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_card_entry(card, "ymfpci", card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = chip; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 4096; + entry->c.text.read = snd_ymfpci_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_unregister(entry); + entry = NULL; + } + } + chip->proc_entry = entry; + return 0; +} + +static int snd_ymfpci_proc_done(ymfpci_t *chip) +{ + if (chip->proc_entry) + snd_info_unregister((snd_info_entry_t *) chip->proc_entry); + return 0; +} + +/* + * initialization routines + */ + +static void snd_ymfpci_aclink_reset(struct pci_dev * pci) +{ + u8 cmd; + + pci_read_config_byte(pci, PCIR_DSXG_CTRL, &cmd); +#if 0 // force to reset + if (cmd & 0x03) { +#endif + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc); + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd | 0x03); + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc); + pci_write_config_word(pci, PCIR_DSXG_PWRCTRL1, 0); + pci_write_config_word(pci, PCIR_DSXG_PWRCTRL2, 0); +#if 0 + } +#endif +} + +static void snd_ymfpci_enable_dsp(ymfpci_t *chip) +{ + snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000001); +} + +static void snd_ymfpci_disable_dsp(ymfpci_t *chip) +{ + u32 val; + int timeout = 1000; + + val = snd_ymfpci_readl(chip, YDSXGR_CONFIG); + if (val) + snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000000); + while (timeout-- > 0) { + val = snd_ymfpci_readl(chip, YDSXGR_STATUS); + if ((val & 0x00000002) == 0) + break; + } +} + +#include "ymfpci_image.h" + +static void snd_ymfpci_download_image(ymfpci_t *chip) +{ + int i; + u16 ctrl; + unsigned long *inst; + + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000); + snd_ymfpci_disable_dsp(chip); + snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00010000); + snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0x00000000); + ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); + snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + + /* setup DSP instruction code */ + for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) + snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]); + + /* setup control instruction code */ + switch (chip->device_id) { + case PCI_DEVICE_ID_YAMAHA_724F: + case PCI_DEVICE_ID_YAMAHA_740C: + case PCI_DEVICE_ID_YAMAHA_744: + case PCI_DEVICE_ID_YAMAHA_754: + inst = CntrlInst1E; + break; + default: + inst = CntrlInst; + break; + } + for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) + snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]); + + snd_ymfpci_enable_dsp(chip); +} + +static int __devinit snd_ymfpci_memalloc(ymfpci_t *chip) +{ + long size, playback_ctrl_size; + int voice, bank, reg; + u8 *ptr; + dma_addr_t ptr_addr; + + playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES; + chip->bank_size_playback = snd_ymfpci_readl(chip, YDSXGR_PLAYCTRLSIZE) << 2; + chip->bank_size_capture = snd_ymfpci_readl(chip, YDSXGR_RECCTRLSIZE) << 2; + chip->bank_size_effect = snd_ymfpci_readl(chip, YDSXGR_EFFCTRLSIZE) << 2; + chip->work_size = YDSXG_DEFAULT_WORK_SIZE; + + size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) + + ((chip->bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0x00ff) & ~0x00ff) + + ((chip->bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0x00ff) & ~0x00ff) + + ((chip->bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0x00ff) & ~0x00ff) + + chip->work_size; + /* work_ptr must be aligned to 256 bytes, but it's already + covered with the kernel page allocation mechanism */ + if ((ptr = snd_malloc_pci_pages(chip->pci, size, &ptr_addr)) == NULL) + return -ENOMEM; + memset(ptr, 0, size); /* for sure */ + chip->work_ptr = ptr; + chip->work_ptr_addr = ptr_addr; + chip->work_ptr_size = size; + + chip->bank_base_playback = ptr; + chip->bank_base_playback_addr = ptr_addr; + chip->ctrl_playback = (u32 *)ptr; + chip->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES); + ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff; + ptr_addr += (playback_ctrl_size + 0x00ff) & ~0x00ff; + for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) { + chip->voices[voice].number = voice; + chip->voices[voice].bank = (snd_ymfpci_playback_bank_t *)ptr; + chip->voices[voice].bank_addr = ptr_addr; + for (bank = 0; bank < 2; bank++) { + chip->bank_playback[voice][bank] = (snd_ymfpci_playback_bank_t *)ptr; + ptr += chip->bank_size_playback; + ptr_addr += chip->bank_size_playback; + } + } + ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff); + ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff; + chip->bank_base_capture = ptr; + chip->bank_base_capture_addr = ptr_addr; + for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + chip->bank_capture[voice][bank] = (snd_ymfpci_capture_bank_t *)ptr; + ptr += chip->bank_size_capture; + ptr_addr += chip->bank_size_capture; + } + ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff); + ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff; + chip->bank_base_effect = ptr; + chip->bank_base_effect_addr = ptr_addr; + for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + chip->bank_effect[voice][bank] = (snd_ymfpci_effect_bank_t *)ptr; + ptr += chip->bank_size_effect; + ptr_addr += chip->bank_size_effect; + } + ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff); + ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff; + chip->work_base = ptr; + chip->work_base_addr = ptr_addr; + + snd_assert(ptr + chip->work_size == chip->work_ptr + chip->work_ptr_size, ); + + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, chip->bank_base_effect_addr); + snd_ymfpci_writel(chip, YDSXGR_WORKBASE, chip->work_base_addr); + snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, chip->work_size >> 2); + + /* S/PDIF output initialization */ + chip->spdif_bits = chip->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF & 0xffff; + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, 0); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + + /* S/PDIF input initialization */ + snd_ymfpci_writew(chip, YDSXGR_SPDIFINCTRL, 0); + + /* digital mixer setup */ + for (reg = 0x80; reg < 0xc0; reg += 4) + snd_ymfpci_writel(chip, reg, 0); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_PRIADCLOOPVOL, 0x3fff3fff); + + return 0; +} + +static int snd_ymfpci_free(ymfpci_t *chip) +{ + u16 ctrl; + + snd_assert(chip != NULL, return -EINVAL); + snd_ymfpci_proc_done(chip); + + if (chip->res_reg_area) { /* don't touch busy hardware */ + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); + snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0); + snd_ymfpci_writel(chip, YDSXGR_STATUS, ~0); + snd_ymfpci_disable_dsp(chip); + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_WORKBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, 0); + ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); + snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + } + + /* Set PCI device to D3 state */ + // pci_set_power_state(chip->pci, 3); + +#ifdef CONFIG_PM + if (chip->saved_regs) + kfree(chip->saved_regs); +#endif + if (chip->reg_area_virt) + iounmap((void *)chip->reg_area_virt); + if (chip->work_ptr) + snd_free_pci_pages(chip->pci, chip->work_ptr_size, chip->work_ptr, chip->work_ptr_addr); + + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + if (chip->res_reg_area) { + release_resource(chip->res_reg_area); + kfree_nocheck(chip->res_reg_area); + } + + pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_ymfpci_dev_free(snd_device_t *device) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, device->device_data, return -ENXIO); + return snd_ymfpci_free(chip); +} + +#ifdef CONFIG_PM +static int saved_regs_index[] = { + /* spdif */ + YDSXGR_SPDIFOUTCTRL, + YDSXGR_SPDIFOUTSTATUS, + YDSXGR_SPDIFINCTRL, + /* volumes */ + YDSXGR_PRIADCLOOPVOL, + YDSXGR_NATIVEDACINVOL, + YDSXGR_NATIVEDACOUTVOL, + // YDSXGR_BUF441OUTVOL, + YDSXGR_NATIVEADCINVOL, + YDSXGR_SPDIFLOOPVOL, + YDSXGR_SPDIFOUTVOL, + YDSXGR_ZVOUTVOL, + /* address bases */ + YDSXGR_PLAYCTRLBASE, + YDSXGR_RECCTRLBASE, + YDSXGR_EFFCTRLBASE, + YDSXGR_WORKBASE, + /* capture set up */ + YDSXGR_MAPOFREC, + YDSXGR_RECFORMAT, + YDSXGR_RECSLOTSR, + YDSXGR_ADCFORMAT, + YDSXGR_ADCSLOTSR, +}; +#define YDSXGR_NUM_SAVED_REGS (sizeof(saved_regs_index)/sizeof(saved_regs_index[0])) + +void snd_ymfpci_suspend(ymfpci_t *chip) +{ + snd_card_t *card = chip->card; + int i; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + snd_pcm_suspend_all(chip->pcm); + snd_pcm_suspend_all(chip->pcm2); + snd_pcm_suspend_all(chip->pcm_spdif); + snd_pcm_suspend_all(chip->pcm_4ch); + for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) + chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]); + chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); + snd_ymfpci_disable_dsp(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +void snd_ymfpci_resume(ymfpci_t *chip) +{ + snd_card_t *card = chip->card; + int i; + + snd_power_lock(card); + + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + pci_enable_device(chip->pci); + pci_set_master(chip->pci); + snd_ymfpci_aclink_reset(chip->pci); + snd_ymfpci_codec_ready(chip, 0, 0); + snd_ymfpci_download_image(chip); + udelay(100); + + for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) + snd_ymfpci_writel(chip, saved_regs_index[i], chip->saved_regs[i]); + + snd_ac97_resume(chip->ac97); + + /* start hw again */ + if (chip->start_count > 0) { + spin_lock(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MODE, chip->saved_ydsxgr_mode); + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT); + spin_unlock(&chip->reg_lock); + } + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +static int snd_ymfpci_set_power_state(snd_card_t *card, unsigned int power_state) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_ymfpci_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_ymfpci_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} +#endif /* CONFIG_PM */ + +int __devinit snd_ymfpci_create(snd_card_t * card, + struct pci_dev * pci, + unsigned short old_legacy_ctrl, + ymfpci_t ** rchip) +{ + ymfpci_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_ymfpci_dev_free, + }; + + *rchip = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = snd_magic_kcalloc(ymfpci_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->old_legacy_ctrl = old_legacy_ctrl; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->voice_lock); + init_waitqueue_head(&chip->interrupt_sleep); + atomic_set(&chip->interrupt_sleep_count, 0); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->device_id = pci->device; + pci_read_config_byte(pci, PCI_REVISION_ID, (u8 *)&chip->rev); + chip->reg_area_phys = pci_resource_start(pci, 0); + chip->reg_area_virt = (unsigned long)ioremap_nocache(chip->reg_area_phys, 0x8000); + pci_set_master(pci); + + if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) { + snd_ymfpci_free(chip); + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_ymfpci_interrupt, SA_INTERRUPT|SA_SHIRQ, "YMFPCI", (void *) chip)) { + snd_ymfpci_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + + snd_ymfpci_aclink_reset(pci); + if (snd_ymfpci_codec_ready(chip, 0, 1) < 0) { + snd_ymfpci_free(chip); + return -EIO; + } + + snd_ymfpci_download_image(chip); + + udelay(100); /* seems we need a delay after downloading image.. */ + + if (snd_ymfpci_memalloc(chip) < 0) { + snd_ymfpci_free(chip); + return -EIO; + } + +#ifdef CONFIG_PM + chip->saved_regs = kmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32), GFP_KERNEL); + if (chip->saved_regs == NULL) { + snd_ymfpci_free(chip); + return -ENOMEM; + } + card->set_power_state = snd_ymfpci_set_power_state; + card->power_state_private_data = chip; +#endif + + snd_ymfpci_proc_init(card, chip); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_ymfpci_free(chip); + return err; + } + + *rchip = chip; + return 0; +} diff -Nru a/sound/ppc/Config.in b/sound/ppc/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/Config.in Tue Feb 19 18:09:01 2002 @@ -0,0 +1,8 @@ +# ALSA PowerMac drivers + +mainmenu_option next_comment +comment 'ALSA PowerMac devices' + +dep_tristate 'PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)' CONFIG_SND_POWERMAC $CONFIG_SND + +endmenu diff -Nru a/sound/ppc/Makefile b/sound/ppc/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := ppc.o + +list-multi := snd-powermac.o + +snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o + +include $(TOPDIR)/Rules.make + +snd-powermac.o: $(snd-powermac-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-powermac-objs) diff -Nru a/sound/ppc/awacs.c b/sound/ppc/awacs.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/awacs.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,420 @@ +/* + * PMac AWACS lowlevel functions + * + * Copyright (c) by Takashi Iwai + * code based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pmac.h" + +#define chip_t pmac_t + + +/* + * write AWACS register + */ +static void +snd_pmac_awacs_write(pmac_t *chip, int val) +{ + long timeout = 5000000; + + if (chip->model <= PMAC_SCREAMER) + return; + + while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) { + if (! --timeout) { + snd_printd("snd_pmac_awacs_write timeout\n"); + break; + } + } + out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22)); +} + +static void +snd_pmac_awacs_write_reg(pmac_t *chip, int reg, int val) +{ + snd_pmac_awacs_write(chip, val | (reg << 12)); + chip->awacs_reg[reg] = val; +} + +static void +snd_pmac_awacs_write_noreg(pmac_t *chip, int reg, int val) +{ + snd_pmac_awacs_write(chip, val | (reg << 12)); +} + +#ifdef CONFIG_PMAC_PBOOK +static void +screamer_recalibrate(pmac_t *chip) +{ + /* Sorry for the horrible delays... I hope to get that improved + * by making the whole PM process asynchronous in a future version + */ + mdelay(750); + snd_pmac_awacs_write_noreg(chip, 1, + chip->awacs_reg[1] | MASK_RECALIBRATE | MASK_CMUTE | MASK_AMUTE); + mdelay(1000); + snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); +} +#endif + + +/* + * additional callback to set the pcm format + */ +static void snd_pmac_awacs_set_format(pmac_t *chip) +{ + chip->awacs_reg[1] &= ~MASK_SAMPLERATE; + chip->awacs_reg[1] |= chip->rate_index << 3; + snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]); +} + + +#ifdef PMAC_AMP_AVAIL +/* Turn on sound output, needed on G3 desktop powermacs */ +/* vol = 0 - 31, stereo */ +static void +snd_pmac_awacs_enable_amp(pmac_t *chip, int lvol, int rvol) +{ + struct adb_request req; + + if (! chip->amp_only) + return; + + /* turn on headphones */ + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 4, 0); + while (!req.complete) cuda_poll(); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 6, 0); + while (!req.complete) cuda_poll(); + + /* turn on speaker */ + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 3, lvol & 0xff); + while (!req.complete) cuda_poll(); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 5, rvol & 0xff); + while (!req.complete) cuda_poll(); + + cuda_request(&req, NULL, 5, CUDA_PACKET, + CUDA_GET_SET_IIC, 0x8a, 1, 0x29); + while (!req.complete) cuda_poll(); + + /* update */ + chip->amp_vol[0] = lvol; + chip->amp_vol[1] = rvol; +} +#endif + +/* + * AWACS volume callbacks + */ +/* + * volumes: 0-15 stereo + */ +static int snd_pmac_awacs_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 15; + return 0; +} + +static int snd_pmac_awacs_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int n = kcontrol->private_value & 0xff; + int lshift = (kcontrol->private_value >> 8) & 0xff; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = 0x0f - ((chip->awacs_reg[n] >> lshift) & 0xf); + ucontrol->value.integer.value[1] = 0x0f - (chip->awacs_reg[n] & 0xf); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_pmac_awacs_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int n = kcontrol->private_value & 0xff; + int lshift = (kcontrol->private_value >> 8) & 0xff; + int rn, oldval; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + oldval = chip->awacs_reg[n]; + rn = oldval & ~(0xf | (0xf << lshift)); + rn |= ((0x0f - (ucontrol->value.integer.value[0] & 0xf)) << lshift); + rn |= 0x0f - (ucontrol->value.integer.value[1] & 0xf); + snd_pmac_awacs_write_reg(chip, n, rn); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return oldval != rn; +} + + +#define AWACS_VOLUME(xname, xreg, xshift) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: 0, \ + info: snd_pmac_awacs_info_volume, \ + get: snd_pmac_awacs_get_volume, \ + put: snd_pmac_awacs_put_volume, \ + private_value: (xreg) | (xshift << 8) } + +/* + * mute master/ogain for AWACS: mono + */ +static int snd_pmac_awacs_info_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_pmac_awacs_get_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int mask = kcontrol->private_value & 0xff; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->awacs_reg[1] & mask) ? 0 : 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_pmac_awacs_put_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int mask = kcontrol->private_value & 0xff; + int val, changed; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + val = chip->awacs_reg[1] & ~mask; + if (! ucontrol->value.integer.value[0]) + val |= mask; + snd_pmac_awacs_write_reg(chip, 1, val); + changed = chip->awacs_reg[1] != val; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return changed; +} + +#define AWACS_SWITCH(xname, xreg, xmask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: 0, \ + info: snd_pmac_awacs_info_switch, \ + get: snd_pmac_awacs_get_switch, \ + put: snd_pmac_awacs_put_switch, \ + private_value: (xreg) | ((xmask) << 8) } + + +#ifdef PMAC_AMP_AVAIL +/* + * Master volume for awacs revision 3 + */ +static int snd_pmac_awacs_info_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 31; + return 0; +} + +static int snd_pmac_awacs_get_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->amp_vol[0]; + ucontrol->value.integer.value[1] = chip->amp_vol[1]; + return 0; +} + +static int snd_pmac_awacs_put_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int changed = ucontrol->value.integer.value[0] != chip->amp_vol[0] || + ucontrol->value.integer.value[1] != chip->amp_vol[1]; + snd_pmac_awacs_enable_amp(chip, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + return changed; +} +#endif /* PMAC_AMP_AVAIL */ + + +/* + * lists of mixer elements + */ +static snd_kcontrol_new_t snd_pmac_awacs_mixers1[] = { + AWACS_VOLUME("Master Playback Volume", 2, 6), + AWACS_SWITCH("Master Playback Switch", 1, MASK_AMUTE), + AWACS_SWITCH("Master Capture Switch", 1, MASK_LOOPTHRU), +}; + +static snd_kcontrol_new_t snd_pmac_awacs_mixers2[] = { + AWACS_VOLUME("Capture Volume", 0, 4), + AWACS_SWITCH("Line Capture Switch", 0, MASK_MUX_AUDIN), + AWACS_SWITCH("CD Capture Switch", 0, MASK_MUX_CD), + AWACS_SWITCH("Mic Capture Switch", 0, MASK_MUX_MIC), + AWACS_SWITCH("Mic Boost", 0, MASK_GAINLINE), +}; + +static snd_kcontrol_new_t snd_pmac_awacs_speaker_mixers[] = { + AWACS_VOLUME("PC Speaker Playback Volume", 4, 6), + AWACS_SWITCH("PC Speaker Playback Switch", 1, MASK_CMUTE), +}; + +#ifdef PMAC_AMP_AVAIL +static snd_kcontrol_new_t snd_pmac_awacs_amp_mixers[] = { + { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PC Speaker Playback Volume", index: 0, + info: snd_pmac_awacs_info_volume_amp, \ + get: snd_pmac_awacs_get_volume_amp, + put: snd_pmac_awacs_put_volume_amp, + }, +}; +#endif + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +/* + * add new mixer elements to the card + */ +static int build_mixers(pmac_t *chip, int nums, snd_kcontrol_new_t *mixers) +{ + int i, err; + + for (i = 0; i < nums; i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0) + return err; + } + return 0; +} + +#ifdef CONFIG_PMAC_PBOOK +static void snd_pmac_awacs_resume(pmac_t *chip) +{ + snd_pmac_awacs_write_reg(chip, 0, chip->awacs_reg[0]); + snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]); + snd_pmac_awacs_write_reg(chip, 2, chip->awacs_reg[2]); + snd_pmac_awacs_write_reg(chip, 4, chip->awacs_reg[4]); + if (chip->model == PMAC_SCREAMER) { + snd_pmac_awacs_write_reg(chip, 5, chip->awacs_reg[5]); + snd_pmac_awacs_write_reg(chip, 6, chip->awacs_reg[6]); + snd_pmac_awacs_write_reg(chip, 7, chip->awacs_reg[7]); + screamer_recalibrate(chip); + } +} +#endif /* CONFIG_PMAC_PBOOK */ + +/* + * initialize chip + */ +int __init +snd_pmac_awacs_init(pmac_t *chip) +{ + int err, vol; + + snd_pmac_awacs_write_reg(chip, 0, MASK_MUX_CD); + /* FIXME: Only machines with external SRS module need MASK_PAROUT */ + chip->awacs_reg[1] = MASK_LOOPTHRU; + if (chip->has_iic || chip->device_id == 0x5 || + /*chip->_device_id == 0x8 || */ + chip->device_id == 0xb) + chip->awacs_reg[1] |= MASK_PAROUT; + snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]); + /* get default volume from nvram */ + vol = (~nvram_read_byte(0x1308) & 7) << 1; + snd_pmac_awacs_write_reg(chip, 2, vol + (vol << 6)); + snd_pmac_awacs_write_reg(chip, 4, vol + (vol << 6)); + if (chip->model == PMAC_SCREAMER) { + snd_pmac_awacs_write_reg(chip, 5, 0); + snd_pmac_awacs_write_reg(chip, 6, 0); + snd_pmac_awacs_write_reg(chip, 7, 0); + } + +#ifdef CONFIG_PMAC_PBOOK + /* Recalibrate chip */ + if (chip->model == PMAC_SCREAMER) + screamer_recalibrate(chip); +#endif + + if (chip->model <= PMAC_SCREAMER && chip->revision == 0) { + chip->revision = + (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf; + if (chip->revision == 3) { +#ifdef PMAC_AMP_AVAIL +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + if (adb_hardware == ADB_VIACUDA) + chip->amp_only = 1; +#elif defined(CONFIG_ADB_CUDA) + if (sys_ctrler == SYS_CTRLER_CUDA) + chip->amp_only = 1; +#endif + if (chip->amp_only) { + /*chip->amp_vol[0] = chip->amp_vol[1] = 31;*/ + snd_pmac_awacs_enable_amp(chip, chip->amp_vol[0], chip->amp_vol[1]); + } +#endif /* PMAC_AMP_AVAIL */ + } + } + + /* + * build mixers + */ + strcpy(chip->card->mixername, "PowerMac AWACS"); + + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_mixers1), + snd_pmac_awacs_mixers1)) < 0) + return err; +#ifdef PMAC_AMP_AVAIL + if (chip->amp_only) { + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_amp_mixers), + snd_pmac_awacs_amp_mixers)) < 0) + return err; + } else { +#endif /* PMAC_AMP_AVAIL */ + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_speaker_mixers), + snd_pmac_awacs_speaker_mixers)) < 0) + return err; +#ifdef PMAC_AMP_AVAIL + } +#endif /* PMAC_AMP_AVAIL */ + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_mixers2), + snd_pmac_awacs_mixers2)) < 0) + return err; + + /* + * set lowlevel callbacks + */ + chip->set_format = snd_pmac_awacs_set_format; +#ifdef CONFIG_PMAC_PBOOK + chip->resume = snd_pmac_awacs_resume; +#endif + + return 0; +} diff -Nru a/sound/ppc/awacs.h b/sound/ppc/awacs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/awacs.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,182 @@ +/* + * Driver for PowerMac AWACS onboard soundchips + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __AWACS_H +#define __AWACS_H + +/*******************************/ +/* AWACs Audio Register Layout */ +/*******************************/ + +struct awacs_regs { + unsigned control; /* Audio control register */ + unsigned pad0[3]; + unsigned codec_ctrl; /* Codec control register */ + unsigned pad1[3]; + unsigned codec_stat; /* Codec status register */ + unsigned pad2[3]; + unsigned clip_count; /* Clipping count register */ + unsigned pad3[3]; + unsigned byteswap; /* Data is little-endian if 1 */ +}; + +/*******************/ +/* Audio Bit Masks */ +/*******************/ + +/* Audio Control Reg Bit Masks */ +/* ----- ------- --- --- ----- */ +#define MASK_ISFSEL (0xf) /* Input SubFrame Select */ +#define MASK_OSFSEL (0xf << 4) /* Output SubFrame Select */ +#define MASK_RATE (0x7 << 8) /* Sound Rate */ +#define MASK_CNTLERR (0x1 << 11) /* Error */ +#define MASK_PORTCHG (0x1 << 12) /* Port Change */ +#define MASK_IEE (0x1 << 13) /* Enable Interrupt on Error */ +#define MASK_IEPC (0x1 << 14) /* Enable Interrupt on Port Change */ +#define MASK_SSFSEL (0x3 << 15) /* Status SubFrame Select */ + +/* Audio Codec Control Reg Bit Masks */ +/* ----- ----- ------- --- --- ----- */ +#define MASK_NEWECMD (0x1 << 24) /* Lock: don't write to reg when 1 */ +#define MASK_EMODESEL (0x3 << 22) /* Send info out on which frame? */ +#define MASK_EXMODEADDR (0x3ff << 12) /* Extended Mode Address -- 10 bits */ +#define MASK_EXMODEDATA (0xfff) /* Extended Mode Data -- 12 bits */ + +/* Audio Codec Control Address Values / Masks */ +/* ----- ----- ------- ------- ------ - ----- */ +#define MASK_ADDR0 (0x0 << 12) /* Expanded Data Mode Address 0 */ +#define MASK_ADDR_MUX MASK_ADDR0 /* Mux Control */ +#define MASK_ADDR_GAIN MASK_ADDR0 + +#define MASK_ADDR1 (0x1 << 12) /* Expanded Data Mode Address 1 */ +#define MASK_ADDR_MUTE MASK_ADDR1 +#define MASK_ADDR_RATE MASK_ADDR1 + +#define MASK_ADDR2 (0x2 << 12) /* Expanded Data Mode Address 2 */ +#define MASK_ADDR_VOLA MASK_ADDR2 /* Volume Control A -- Headphones */ +#define MASK_ADDR_VOLHD MASK_ADDR2 + +#define MASK_ADDR4 (0x4 << 12) /* Expanded Data Mode Address 4 */ +#define MASK_ADDR_VOLC MASK_ADDR4 /* Volume Control C -- Speaker */ +#define MASK_ADDR_VOLSPK MASK_ADDR4 + +/* additional registers of screamer */ +#define MASK_ADDR5 (0x5 << 12) /* Expanded Data Mode Address 5 */ +#define MASK_ADDR6 (0x6 << 12) /* Expanded Data Mode Address 6 */ +#define MASK_ADDR7 (0x7 << 12) /* Expanded Data Mode Address 7 */ + +/* Address 0 Bit Masks & Macros */ +/* ------- - --- ----- - ------ */ +#define MASK_GAINRIGHT (0xf) /* Gain Right Mask */ +#define MASK_GAINLEFT (0xf << 4) /* Gain Left Mask */ +#define MASK_GAINLINE (0x1 << 8) /* Disable Mic preamp */ +#define MASK_GAINMIC (0x0 << 8) /* Enable Mic preamp */ + +#define MASK_MUX_CD (0x1 << 9) /* Select CD in MUX */ +#define MASK_MUX_MIC (0x1 << 10) /* Select Mic in MUX */ +#define MASK_MUX_AUDIN (0x1 << 11) /* Select Audio In in MUX */ +#define MASK_MUX_LINE MASK_MUX_AUDIN + +#define GAINRIGHT(x) ((x) & MASK_GAINRIGHT) +#define GAINLEFT(x) (((x) << 4) & MASK_GAINLEFT) + +/* Address 1 Bit Masks */ +/* ------- - --- ----- */ +#define MASK_ADDR1RES1 (0x3) /* Reserved */ +#define MASK_RECALIBRATE (0x1 << 2) /* Recalibrate */ +#define MASK_SAMPLERATE (0x7 << 3) /* Sample Rate: */ +#define MASK_LOOPTHRU (0x1 << 6) /* Loopthrough Enable */ +#define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */ +#define MASK_SPKMUTE MASK_CMUTE +#define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */ +#define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */ +#define MASK_HDMUTE MASK_AMUTE +#define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */ + +#define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */ +#define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */ +#define SAMPLERATE_24000 (0x2 << 3) /* 24 or 22.05 kHz */ +#define SAMPLERATE_19200 (0x3 << 3) /* 19.2 or 17.64 kHz */ +#define SAMPLERATE_16000 (0x4 << 3) /* 16 or 14.7 kHz */ +#define SAMPLERATE_12000 (0x5 << 3) /* 12 or 11.025 kHz */ +#define SAMPLERATE_9600 (0x6 << 3) /* 9.6 or 8.82 kHz */ +#define SAMPLERATE_8000 (0x7 << 3) /* 8 or 7.35 kHz */ + +/* Address 2 & 4 Bit Masks & Macros */ +/* ------- - - - --- ----- - ------ */ +#define MASK_OUTVOLRIGHT (0xf) /* Output Right Volume */ +#define MASK_ADDR2RES1 (0x2 << 4) /* Reserved */ +#define MASK_ADDR4RES1 MASK_ADDR2RES1 +#define MASK_OUTVOLLEFT (0xf << 6) /* Output Left Volume */ +#define MASK_ADDR2RES2 (0x2 << 10) /* Reserved */ +#define MASK_ADDR4RES2 MASK_ADDR2RES2 + +#define VOLRIGHT(x) (((~(x)) & MASK_OUTVOLRIGHT)) +#define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT) + +/* Audio Codec Status Reg Bit Masks */ +/* ----- ----- ------ --- --- ----- */ +#define MASK_EXTEND (0x1 << 23) /* Extend */ +#define MASK_VALID (0x1 << 22) /* Valid Data? */ +#define MASK_OFLEFT (0x1 << 21) /* Overflow Left */ +#define MASK_OFRIGHT (0x1 << 20) /* Overflow Right */ +#define MASK_ERRCODE (0xf << 16) /* Error Code */ +#define MASK_REVISION (0xf << 12) /* Revision Number */ +#define MASK_MFGID (0xf << 8) /* Mfg. ID */ +#define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */ +#define MASK_INPPORT (0xf) /* Input Port */ +#define MASK_HDPCONN 8 /* headphone plugged in */ + +/* Clipping Count Reg Bit Masks */ +/* -------- ----- --- --- ----- */ +#define MASK_CLIPLEFT (0xff << 7) /* Clipping Count, Left Channel */ +#define MASK_CLIPRIGHT (0xff) /* Clipping Count, Right Channel */ + +/* DBDMA ChannelStatus Bit Masks */ +/* ----- ------------- --- ----- */ +#define MASK_CSERR (0x1 << 7) /* Error */ +#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */ +#define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */ +#define MASK_WAIT (0x1) /* Wait */ + +/* Various Rates */ +/* ------- ----- */ +#define RATE_48000 (0x0 << 8) /* 48 kHz */ +#define RATE_44100 (0x0 << 8) /* 44.1 kHz */ +#define RATE_32000 (0x1 << 8) /* 32 kHz */ +#define RATE_29400 (0x1 << 8) /* 29.4 kHz */ +#define RATE_24000 (0x2 << 8) /* 24 kHz */ +#define RATE_22050 (0x2 << 8) /* 22.05 kHz */ +#define RATE_19200 (0x3 << 8) /* 19.2 kHz */ +#define RATE_17640 (0x3 << 8) /* 17.64 kHz */ +#define RATE_16000 (0x4 << 8) /* 16 kHz */ +#define RATE_14700 (0x4 << 8) /* 14.7 kHz */ +#define RATE_12000 (0x5 << 8) /* 12 kHz */ +#define RATE_11025 (0x5 << 8) /* 11.025 kHz */ +#define RATE_9600 (0x6 << 8) /* 9.6 kHz */ +#define RATE_8820 (0x6 << 8) /* 8.82 kHz */ +#define RATE_8000 (0x7 << 8) /* 8 kHz */ +#define RATE_7350 (0x7 << 8) /* 7.35 kHz */ + +#define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */ + + +#endif /* __AWACS_H */ diff -Nru a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/burgundy.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,378 @@ +/* + * PMac Burgundy lowlevel functions + * + * Copyright (c) by Takashi Iwai + * code based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pmac.h" +#include "burgundy.h" + +#define chip_t pmac_t + + +/* Waits for busy flag to clear */ +inline static void +snd_pmac_burgundy_busy_wait(pmac_t *chip) +{ + while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) + ; +} + +inline static void +snd_pmac_burgundy_extend_wait(pmac_t *chip) +{ + while (!(in_le32(&chip->awacs->codec_stat) & MASK_EXTEND)) + ; + while (in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) + ; +} + +static void +snd_pmac_burgundy_wcw(pmac_t *chip, unsigned addr, unsigned val) +{ + out_le32(&chip->awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); + snd_pmac_burgundy_busy_wait(chip); + out_le32(&chip->awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); + snd_pmac_burgundy_busy_wait(chip); + out_le32(&chip->awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); + snd_pmac_burgundy_busy_wait(chip); + out_le32(&chip->awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); + snd_pmac_burgundy_busy_wait(chip); +} + +static unsigned +snd_pmac_burgundy_rcw(pmac_t *chip, unsigned addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + spin_lock_irqsave(&chip->reg_lock, flags); + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100000); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff; + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100100); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<8; + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100200); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<16; + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100300); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<24; + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return val; +} + +static void +snd_pmac_burgundy_wcb(pmac_t *chip, unsigned int addr, unsigned int val) +{ + out_le32(&chip->awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); + snd_pmac_burgundy_busy_wait(chip); +} + +static unsigned +snd_pmac_burgundy_rcb(pmac_t *chip, unsigned int addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + spin_lock_irqsave(&chip->reg_lock, flags); + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100000); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff; + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return val; +} + +/* + * Burgundy volume: 0 - 100, stereo + */ +static void +snd_pmac_burgundy_write_volume(pmac_t *chip, unsigned int address, long *volume, int shift) +{ + int hardvolume, lvolume, rvolume; + + lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; + rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; + + hardvolume = lvolume + (rvolume << shift); + if (shift == 8) + hardvolume |= hardvolume << 16; + + snd_pmac_burgundy_wcw(chip, address, hardvolume); +} + +static void +snd_pmac_burgundy_read_volume(pmac_t *chip, unsigned int address, long *volume, int shift) +{ + int wvolume; + + wvolume = snd_pmac_burgundy_rcw(chip, address); + + volume[0] = wvolume & 0xff; + if (volume[0] >= BURGUNDY_VOLUME_OFFSET) + volume[0] -= BURGUNDY_VOLUME_OFFSET; + else + volume[0] = 0; + volume[1] = (wvolume >> shift) & 0xff; + if (volume[1] >= BURGUNDY_VOLUME_OFFSET) + volume[1] -= BURGUNDY_VOLUME_OFFSET; + else + volume[1] = 0; +} + + +/* + */ + +#define BASE2ADDR(base) ((base) << 12) +#define ADDR2BASE(addr) ((addr) >> 12) + +static int snd_pmac_burgundy_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_pmac_burgundy_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int shift = (kcontrol->private_value >> 8) & 0xff; + snd_pmac_burgundy_read_volume(chip, addr, ucontrol->value.integer.value, shift); + return 0; +} + +static int snd_pmac_burgundy_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int shift = (kcontrol->private_value >> 8) & 0xff; + long nvoices[2]; + + snd_pmac_burgundy_write_volume(chip, addr, ucontrol->value.integer.value, shift); + snd_pmac_burgundy_read_volume(chip, addr, nvoices, shift); + return (nvoices[0] != ucontrol->value.integer.value[0] || + nvoices[1] != ucontrol->value.integer.value[1]); +} + +#define BURGUNDY_VOLUME(xname, xindex, addr, shift) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\ + info: snd_pmac_burgundy_info_volume,\ + get: snd_pmac_burgundy_get_volume,\ + put: snd_pmac_burgundy_put_volume,\ + private_value: ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) } + +/* lineout/speaker */ + +static int snd_pmac_burgundy_info_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + int stereo = (kcontrol->private_value >> 24) & 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_pmac_burgundy_get_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int lmask = kcontrol->private_value & 0xff; + int rmask = (kcontrol->private_value >> 8) & 0xff; + int stereo = (kcontrol->private_value >> 24) & 1; + int val = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; + if (stereo) + ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; + return 0; +} + +static int snd_pmac_burgundy_put_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int lmask = kcontrol->private_value & 0xff; + int rmask = (kcontrol->private_value >> 8) & 0xff; + int stereo = (kcontrol->private_value >> 24) & 1; + int val, oval; + oval = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + val = oval & ~(lmask | rmask); + if (ucontrol->value.integer.value[0]) + val |= lmask; + if (stereo && ucontrol->value.integer.value[1]) + val |= rmask; + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, val); + return val != oval; +} + +#define BURGUNDY_OUTPUT_SWITCH(xname, xindex, lmask, rmask, stereo) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\ + info: snd_pmac_burgundy_info_switch_out,\ + get: snd_pmac_burgundy_get_switch_out,\ + put: snd_pmac_burgundy_put_switch_out,\ + private_value: ((lmask) | ((rmask) << 8) | ((stereo) << 24)) } + +/* line/speaker output volume */ +static int snd_pmac_burgundy_info_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + int stereo = (kcontrol->private_value >> 24) & 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 15; + return 0; +} + +static int snd_pmac_burgundy_get_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int stereo = (kcontrol->private_value >> 24) & 1; + int oval; + + oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; + ucontrol->value.integer.value[0] = oval & 0xf; + if (stereo) + ucontrol->value.integer.value[1] = (oval >> 4) & 0xf; + return 0; +} + +static int snd_pmac_burgundy_put_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int stereo = (kcontrol->private_value >> 24) & 1; + int oval, val; + + oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; + val = ucontrol->value.integer.value[0]; + if (stereo) + val |= ucontrol->value.integer.value[1] << 4; + else + val |= ucontrol->value.integer.value[0] << 4; + val = ~val & 0xff; + snd_pmac_burgundy_wcb(chip, addr, val); + return val != oval; +} + +#define BURGUNDY_OUTPUT_VOLUME(xname, xindex, addr, stereo) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\ + info: snd_pmac_burgundy_info_volume_out,\ + get: snd_pmac_burgundy_get_volume_out,\ + put: snd_pmac_burgundy_put_volume_out,\ + private_value: (ADDR2BASE(addr) | ((stereo) << 24)) } + +static snd_kcontrol_new_t snd_pmac_burgundy_mixers[] = { + BURGUNDY_VOLUME("Master Playback Volume", 0, MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8), + BURGUNDY_VOLUME("Line Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLLINE, 16), + BURGUNDY_VOLUME("CD Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLCD, 16), + BURGUNDY_VOLUME("Mic Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLMIC, 16), + BURGUNDY_OUTPUT_VOLUME("PC Speaker Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENHP, 0), + /*BURGUNDY_OUTPUT_VOLUME("PCM Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1),*/ + BURGUNDY_OUTPUT_VOLUME("Headphone Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1), + BURGUNDY_OUTPUT_SWITCH("PC Speaker Playback Switch", 0, BURGUNDY_OUTPUT_INTERN, 0, 0), + BURGUNDY_OUTPUT_SWITCH("Headphone Playback Switch", 0, BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1), +}; + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + + +/* + * initialize burgundy + */ +int __init snd_pmac_burgundy_init(pmac_t *chip) +{ + int i, err; + + /* Checks to see the chip is alive and kicking */ + if ((in_le32(&chip->awacs->codec_ctrl) & MASK_ERRCODE) == 0xf0000) { + printk(KERN_WARNING "pmac burgundy: disabled by MacOS :-(\n"); + return 1; + } + + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES, + DEF_BURGUNDY_OUTPUTENABLES); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + DEF_BURGUNDY_MORE_OUTPUTENABLES); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTSELECTS, + DEF_BURGUNDY_OUTPUTSELECTS); + + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL21, + DEF_BURGUNDY_INPSEL21); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL3, + DEF_BURGUNDY_INPSEL3); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINCD, + DEF_BURGUNDY_GAINCD); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINLINE, + DEF_BURGUNDY_GAINLINE); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMIC, + DEF_BURGUNDY_GAINMIC); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMODEM, + DEF_BURGUNDY_GAINMODEM); + + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENSPEAKER, + DEF_BURGUNDY_ATTENSPEAKER); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENLINEOUT, + DEF_BURGUNDY_ATTENLINEOUT); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENHP, + DEF_BURGUNDY_ATTENHP); + + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_MASTER_VOLUME, + DEF_BURGUNDY_MASTER_VOLUME); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLCD, + DEF_BURGUNDY_VOLCD); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLLINE, + DEF_BURGUNDY_VOLLINE); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLMIC, + DEF_BURGUNDY_VOLMIC); + + /* + * build burgundy mixers + */ + strcpy(chip->card->mixername, "PowerMac Burgundy"); + + for (i = 0; i < num_controls(snd_pmac_burgundy_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip))) < 0) + return err; + } + return 0; +} diff -Nru a/sound/ppc/burgundy.h b/sound/ppc/burgundy.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/burgundy.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,95 @@ +/* + * Driver for PowerMac Burgundy onboard soundchips + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __BURGUNDY_H +#define __BURGUNDY_H + +#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12) +#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12) + +#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12) + +#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12) +#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12) + +#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12) + +#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12) + +#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12) +#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12) +#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1) +#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2) +#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3) +#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + +#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1) +#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2) +#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3) +#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + + +/* These are all default values for the burgundy */ +#define DEF_BURGUNDY_INPSEL21 (0xAA) +#define DEF_BURGUNDY_INPSEL3 (0x0A) + +#define DEF_BURGUNDY_GAINCD (0x33) +#define DEF_BURGUNDY_GAINLINE (0x44) +#define DEF_BURGUNDY_GAINMIC (0x44) +#define DEF_BURGUNDY_GAINMODEM (0x06) + +/* Remember: lowest volume here is 0x9b */ +#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC) +#define DEF_BURGUNDY_VOLLINE (0x00000000) +#define DEF_BURGUNDY_VOLMIC (0x00000000) +#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC) + +#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f) +#define DEF_BURGUNDY_OUTPUTENABLES (0x0A) + +/* #define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) */ /* too loud */ +#define DEF_BURGUNDY_MASTER_VOLUME (0xDDDDDDDD) + +#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E) + +#define DEF_BURGUNDY_ATTENSPEAKER (0x44) +#define DEF_BURGUNDY_ATTENLINEOUT (0xCC) +#define DEF_BURGUNDY_ATTENHP (0xCC) + +/* OUTPUTENABLES bits */ +#define BURGUNDY_OUTPUT_LEFT 0x02 +#define BURGUNDY_OUTPUT_RIGHT 0x04 +#define BURGUNDY_OUTPUT_INTERN 0x80 + +/* volume offset */ +#define BURGUNDY_VOLUME_OFFSET 155 + +#endif /* __BURGUNDY_H */ diff -Nru a/sound/ppc/daca.c b/sound/ppc/daca.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/daca.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,274 @@ +/* + * PMac DACA lowlevel functions + * + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define __NO_VERSION__ +#include +#include +#include +#include "pmac.h" + +#define chip_t pmac_t + +/* i2c address */ +#define DACA_I2C_ADDR 0x4d + +/* registers */ +#define DACA_REG_SR 0x01 +#define DACA_REG_AVOL 0x02 +#define DACA_REG_GCFG 0x03 + +/* maximum volume value */ +#define DACA_VOL_MAX 0x38 + + +typedef struct pmac_daca_t { + pmac_keywest_t i2c; + int left_vol, right_vol; + unsigned int deemphasis : 1; + unsigned int amp_on : 1; +} pmac_daca_t; + + +/* + * initialize / detect DACA + */ +static int daca_init_client(pmac_t *chip, pmac_keywest_t *i2c) +{ + unsigned short wdata = 0x00; + /* SR: no swap, 1bit delay, 32-48kHz */ + /* GCFG: power amp inverted, DAC on */ + if (snd_pmac_keywest_write_byte(i2c, DACA_REG_SR, 0x08) < 0 || + snd_pmac_keywest_write_byte(i2c, DACA_REG_GCFG, 0x05) < 0) + return -EINVAL; + return snd_pmac_keywest_write(i2c, DACA_REG_AVOL, 2, (unsigned char*)&wdata); +} + +/* + * update volume + */ +static int daca_set_volume(pmac_daca_t *mix) +{ + unsigned char data[2]; + + if (! mix->i2c.base) + return -ENODEV; + + if (mix->right_vol > DACA_VOL_MAX) + data[0] = DACA_VOL_MAX; + else + data[0] = mix->right_vol; + if (mix->left_vol > DACA_VOL_MAX) + data[1] = DACA_VOL_MAX; + else + data[1] = mix->left_vol; + data[1] |= mix->deemphasis ? 0x40 : 0; + if (snd_pmac_keywest_write(&mix->i2c, DACA_REG_AVOL, 2, data) < 0) { + snd_printk("failed to set volume \n"); + return -EINVAL; + } + return 0; +} + + +/* deemphasis switch */ +static int daca_info_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int daca_get_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->deemphasis ? 1 : 0; + return 0; +} + +static int daca_put_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->deemphasis != ucontrol->value.integer.value[0]; + if (change) { + mix->deemphasis = ucontrol->value.integer.value[0]; + daca_set_volume(mix); + } + return change; +} + +/* output volume */ +static int daca_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = DACA_VOL_MAX; + return 0; +} + +static int daca_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->left_vol; + ucontrol->value.integer.value[1] = mix->right_vol; + return 0; +} + +static int daca_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->left_vol != ucontrol->value.integer.value[0] || + mix->right_vol != ucontrol->value.integer.value[1]; + if (change) { + mix->left_vol = ucontrol->value.integer.value[0]; + mix->right_vol = ucontrol->value.integer.value[1]; + daca_set_volume(mix); + } + return change; +} + +/* amplifier switch */ +#define daca_info_amp daca_info_deemphasis + +static int daca_get_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->amp_on ? 1 : 0; + return 0; +} + +static int daca_put_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->amp_on != ucontrol->value.integer.value[0]; + if (change) { + mix->amp_on = ucontrol->value.integer.value[0]; + snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_GCFG, + mix->amp_on ? 0x05 : 0x04); + } + return change; +} + +static snd_kcontrol_new_t daca_mixers[] = { + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Deemphasis Switch", + info: daca_info_deemphasis, + get: daca_get_deemphasis, + put: daca_put_deemphasis + }, + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Master Playback Volume", + info: daca_info_volume, + get: daca_get_volume, + put: daca_put_volume + }, + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Power Amplifier Switch", + info: daca_info_amp, + get: daca_get_amp, + put: daca_put_amp + }, +}; + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + + +#ifdef CONFIG_PMAC_PBOOK +static void daca_resume(pmac_t *chip) +{ + pmac_daca_t *mix = chip->mixer_data; + snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_SR, 0x08); + snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_GCFG, + mix->amp_on ? 0x05 : 0x04); + daca_set_volume(mix); +} +#endif /* CONFIG_PMAC_PBOOK */ + + +static void daca_cleanup(pmac_t *chip) +{ + pmac_daca_t *mix = chip->mixer_data; + if (! mix) + return; + snd_pmac_keywest_cleanup(&mix->i2c); + kfree(mix); + chip->mixer_data = NULL; +} + +/* exported */ +int __init snd_pmac_daca_init(pmac_t *chip) +{ + int i, err; + pmac_daca_t *mix; + + mix = kmalloc(sizeof(*mix), GFP_KERNEL); + if (! mix) + return -ENOMEM; + memset(mix, 0, sizeof(*mix)); + chip->mixer_data = mix; + chip->mixer_free = daca_cleanup; + mix->amp_on = 1; /* default on */ + + if ((err = snd_pmac_keywest_find(chip, &mix->i2c, DACA_I2C_ADDR, daca_init_client)) < 0) + return err; + + /* + * build mixers + */ + strcpy(chip->card->mixername, "PowerMac DACA"); + + for (i = 0; i < num_controls(daca_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip))) < 0) + return err; + } + +#ifdef CONFIG_PMAC_PBOOK + chip->resume = daca_resume; +#endif + + return 0; +} diff -Nru a/sound/ppc/keywest.c b/sound/ppc/keywest.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/keywest.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,228 @@ +/* + * Keywest i2c code + * + * Copyright (c) by Takashi Iwai + * + * + * based on i2c-keywest.c from lm_sensors. + * Copyright (c) 2000 Philip Edelbrock + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define __NO_VERSION__ +#include +#include +#include +#include "pmac.h" + +/* The Tumbler audio equalizer can be really slow sometimes */ +#define KW_POLL_SANITY 10000 + +/* address indices */ +#define KW_ADDR_MODE 0 +#define KW_ADDR_CONTROL 1 +#define KW_ADDR_STATUS 2 +#define KW_ADDR_ISR 3 +#define KW_ADDR_IER 4 +#define KW_ADDR_ADDR 5 +#define KW_ADDR_SUBADDR 6 +#define KW_ADDR_DATA 7 + +#define KW_I2C_ADDR(i2c,type) ((i2c)->base + (type) * (i2c)->steps) + +/* keywest needs a small delay to defuddle itself after changing a setting */ +inline static void keywest_writeb_wait(pmac_keywest_t *i2c, int addr, int value) +{ + writeb(value, KW_I2C_ADDR(i2c, addr)); + udelay(10); +} + +inline static void keywest_writeb(pmac_keywest_t *i2c, int addr, int value) +{ + writeb(value, KW_I2C_ADDR(i2c, addr)); +} + +inline unsigned char keywest_readb(pmac_keywest_t *i2c, int addr) +{ + return readb(KW_I2C_ADDR(i2c, addr)); +} + +static int keywest_poll_interrupt(pmac_keywest_t *i2c) +{ + int i, res; + for (i = 0; i < KW_POLL_SANITY; i++) { + udelay(100); + res = keywest_readb(i2c, KW_ADDR_ISR) & 0x0f; + if (res > 0) + return res; + } + + //snd_printd("Sanity check failed! Expected interrupt never happened.\n"); + return -ENODEV; +} + + +static void keywest_reset(pmac_keywest_t *i2c) +{ + int interrupt_state; + + /* Clear all past interrupts */ + interrupt_state = keywest_readb(i2c, KW_ADDR_ISR) & 0x0f; + if (interrupt_state > 0) + keywest_writeb_wait(i2c, KW_ADDR_ISR, interrupt_state); +} + +static int keywest_start(pmac_keywest_t *i2c, unsigned char cmd, int is_read) +{ + int interrupt_state; + int ack; + + keywest_reset(i2c); + + /* Set up address and r/w bit */ + keywest_writeb_wait(i2c, KW_ADDR_ADDR, (i2c->addr << 1) | (is_read ? 1 : 0)); + + /* Set up 'sub address' which I'm guessing is the command field? */ + keywest_writeb_wait(i2c, KW_ADDR_SUBADDR, cmd); + + /* Start sending address */ + keywest_writeb_wait(i2c, KW_ADDR_CONTROL, keywest_readb(i2c, KW_ADDR_CONTROL) | 2); + interrupt_state = keywest_poll_interrupt(i2c); + if (interrupt_state < 0) + return interrupt_state; + + ack = keywest_readb(i2c, KW_ADDR_STATUS) & 0x0f; + if ((ack & 0x02) == 0) { + snd_printd("Ack Status on addr expected but got: 0x%02x on addr: 0x%02x\n", ack, i2c->addr); + return -EINVAL; + } + return interrupt_state; +} + +/* exported */ +int snd_pmac_keywest_write(pmac_keywest_t *i2c, unsigned char cmd, int len, unsigned char *data) +{ + int interrupt_state; + int error_state = 0; + int i; + + snd_assert(len >= 1 && len <= 32, return -EINVAL); + + if ((interrupt_state = keywest_start(i2c, cmd, 0)) < 0) + return -EINVAL; + + for(i = 0; i < len; i++) { + keywest_writeb_wait(i2c, KW_ADDR_DATA, data[i]); + + /* Clear interrupt and go */ + keywest_writeb_wait(i2c, KW_ADDR_ISR, interrupt_state); + + interrupt_state = keywest_poll_interrupt(i2c); + if (interrupt_state < 0) { + error_state = -EINVAL; + interrupt_state = 0; + } + if ((keywest_readb(i2c, KW_ADDR_STATUS) & 0x02) == 0) { + snd_printd("Ack Expected by not received(block)!\n"); + error_state = -EINVAL; + } + } + + /* Send stop */ + keywest_writeb_wait(i2c, KW_ADDR_CONTROL, + keywest_readb(i2c, KW_ADDR_CONTROL) | 4); + + keywest_writeb_wait(i2c, KW_ADDR_CONTROL, interrupt_state); + + interrupt_state = keywest_poll_interrupt(i2c); + if (interrupt_state < 0) { + error_state = -EINVAL; + interrupt_state = 0; + } + keywest_writeb_wait(i2c, KW_ADDR_ISR, interrupt_state); + + return error_state; +} + +/* exported */ +void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c) +{ + if (i2c->base) { + iounmap((void*)i2c->base); + i2c->base = 0; + } +} + +/* exported */ +int snd_pmac_keywest_find(pmac_t *chip, pmac_keywest_t *i2c, int addr, + int (*init_client)(pmac_t *, pmac_keywest_t *)) +{ + struct device_node *i2c_device; + void **temp; + void *base = NULL; + u32 steps = 0; + + i2c_device = find_compatible_devices("i2c", "keywest"); + + if (i2c_device == 0) { + snd_printk("No Keywest i2c devices found.\n"); + return -ENODEV; + } + + for (; i2c_device; i2c_device = i2c_device->next) { + snd_printd("Keywest device found: %s\n", i2c_device->full_name); + temp = (void **) get_property(i2c_device, "AAPL,address", NULL); + if (temp != NULL) { + base = *temp; + } else { + snd_printd("pmac: no 'address' prop!\n"); + continue; + } + + temp = (void **) get_property(i2c_device, "AAPL,address-step", NULL); + if (temp != NULL) { + steps = *(uint *)temp; + } else { + snd_printd("pmac: no 'address-step' prop!\n"); + continue; + } + + i2c->base = (unsigned long)ioremap((unsigned long)base, steps * 8); + i2c->steps = steps; + + /* Select standard sub mode + * + * ie for
... style transactions + */ + keywest_writeb_wait(i2c, KW_ADDR_MODE, 0x08); + + /* Enable interrupts */ + keywest_writeb_wait(i2c, KW_ADDR_IER, 1 + 2 + 4 + 8); + + keywest_reset(i2c); + + i2c->addr = addr; + if (init_client(chip, i2c) < 0) { + snd_pmac_keywest_cleanup(i2c); + continue; + } + + return 0; /* ok */ + } + + return -ENODEV; +} diff -Nru a/sound/ppc/pmac.c b/sound/ppc/pmac.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/pmac.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,1431 @@ +/* + * PMac DBDMA lowlevel functions + * + * Copyright (c) by Takashi Iwai + * code based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "pmac.h" +#include +#ifdef CONFIG_PPC_HAS_FEATURE_CALLS +#include +#else +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define pmu_suspend() /**/ +#define pmu_resume() /**/ +#endif + + +#define chip_t pmac_t + + +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) +static int snd_pmac_register_sleep_notifier(pmac_t *chip); +static int snd_pmac_unregister_sleep_notifier(pmac_t *chip); +static int snd_pmac_set_power_state(snd_card_t *card, unsigned int power_state); +#endif + + +/* fixed frequency table for awacs, screamer, burgundy, DACA (44100 max) */ +static int awacs_freqs[8] = { + 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 +}; +/* fixed frequency table for tumbler */ +static int tumbler_freqs[2] = { + 48000, 44100 +}; + +/* + * allocate DBDMA command arrays + */ +static int snd_pmac_dbdma_alloc(pmac_dbdma_t *rec, int size) +{ + rec->space = kmalloc(sizeof(struct dbdma_cmd) * (size + 1), GFP_KERNEL); + if (rec->space == NULL) + return -ENOMEM; + rec->size = size; + memset(rec->space, 0, sizeof(struct dbdma_cmd) * (size + 1)); + rec->cmds = (void*)DBDMA_ALIGN(rec->space); + rec->addr = virt_to_bus(rec->cmds); + return 0; +} + +static void snd_pmac_dbdma_free(pmac_dbdma_t *rec) +{ + if (rec && rec->space) + kfree(rec->space); +} + + +/* + * pcm stuff + */ + +/* + * look up frequency table + */ + +static unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate) +{ + int i, ok, found; + + ok = rec->cur_freqs; + if (rate > chip->freq_table[0]) + return 0; + found = 0; + for (i = 0; i < chip->num_freqs; i++, ok >>= 1) { + if (! (ok & 1)) continue; + found = i; + if (rate >= chip->freq_table[i]) + break; + } + return found; +} + +/* + * check whether another stream is active + */ +static inline int another_stream(int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; +} + +/* + * allocate buffers + */ +static int snd_pmac_pcm_hw_params(snd_pcm_substream_t *subs, + snd_pcm_hw_params_t *hw_params) +{ + return snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw_params)); +} + +/* + * release buffers + */ +static int snd_pmac_pcm_hw_free(snd_pcm_substream_t *subs) +{ + snd_pcm_lib_free_pages(subs); + return 0; +} + +/* + * get a stream of the opposite direction + */ +static pmac_stream_t *snd_pmac_get_stream(pmac_t *chip, int stream) +{ + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + return &chip->playback; + case SNDRV_PCM_STREAM_CAPTURE: + return &chip->capture; + default: + snd_BUG(); + return NULL; + } +} + +/* + * wait while run status is on + */ +inline static void +snd_pmac_wait_ack(pmac_stream_t *rec) +{ + int timeout = 50000; + while ((in_le32(&rec->dma->status) & RUN) && timeout-- > 0) + udelay(1); +} + +/* + * set the format and rate to the chip. + * call the lowlevel function if defined (e.g. for AWACS). + */ +static void snd_pmac_pcm_set_format(pmac_t *chip) +{ + /* set up frequency and format */ + out_le32(&chip->awacs->control, chip->control_mask | (chip->rate_index << 8)); + out_le32(&chip->awacs->byteswap, chip->format == SNDRV_PCM_FORMAT_S16_LE); + if (chip->set_format) + chip->set_format(chip); +} + +/* + * stop the DMA transfer + */ +inline static void snd_pmac_dma_stop(pmac_stream_t *rec) +{ + out_le32(&rec->dma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + snd_pmac_wait_ack(rec); +} + +/* + * set the command pointer address + */ +inline static void snd_pmac_dma_set_command(pmac_stream_t *rec, pmac_dbdma_t *cmd) +{ + out_le32(&rec->dma->cmdptr, cmd->addr); +} + +/* + * start the DMA + */ +inline static void snd_pmac_dma_run(pmac_stream_t *rec, int status) +{ + out_le32(&rec->dma->control, status | (status << 16)); +} + + +/* + * prepare playback/capture stream + */ +static int snd_pmac_pcm_prepare(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +{ + int i; + volatile struct dbdma_cmd *cp; + unsigned long flags; + snd_pcm_runtime_t *runtime = subs->runtime; + int rate_index; + long offset; + pmac_stream_t *astr; + + rec->dma_size = snd_pcm_lib_buffer_bytes(subs); + rec->period_size = snd_pcm_lib_period_bytes(subs); + rec->nperiods = rec->dma_size / rec->period_size; + rec->cur_period = 0; + rate_index = snd_pmac_rate_index(chip, rec, runtime->rate); + + /* set up constraints */ + astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); + snd_runtime_check(astr, return -EINVAL); + astr->cur_freqs = 1 << rate_index; + astr->cur_formats = 1 << runtime->format; + chip->rate_index = rate_index; + chip->format = runtime->format; + + /* We really want to execute a DMA stop command, after the AWACS + * is initialized. + * For reasons I don't understand, it stops the hissing noise + * common to many PowerBook G3 systems (like mine :-). + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_pmac_dma_stop(rec); + if (rec->stream == SNDRV_PCM_STREAM_PLAYBACK) { + st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); + snd_pmac_dma_set_command(rec, &chip->extra_dma); + snd_pmac_dma_run(rec, RUN); + } + offset = runtime->dma_addr; + for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) { + st_le32(&cp->phy_addr, offset); + st_le16(&cp->req_count, rec->period_size); + /*st_le16(&cp->res_count, 0);*/ + st_le16(&cp->xfer_status, 0); + offset += rec->period_size; + } + /* make loop */ + st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); + st_le32(&cp->cmd_dep, rec->cmd.addr); + + snd_pmac_dma_stop(rec); + snd_pmac_dma_set_command(rec, &rec->cmd); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + + +/* + * stop beep if running (no spinlock!) + */ +static void snd_pmac_beep_stop(pmac_t *chip) +{ + pmac_beep_t *beep = chip->beep; + + if (beep && beep->running) { + beep->running = 0; + del_timer(&beep->timer); + snd_pmac_dma_stop(&chip->playback); + st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); + } +} + +/* + * PCM trigger/stop + */ +static int snd_pmac_pcm_trigger(pmac_t *chip, pmac_stream_t *rec, + snd_pcm_substream_t *subs, int cmd) +{ + unsigned long flags; + volatile struct dbdma_cmd *cp; + int i, command; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (rec->running) + return -EBUSY; + command = (subs->stream == SNDRV_PCM_STREAM_PLAYBACK ? + OUTPUT_MORE : INPUT_MORE) + INTR_ALWAYS; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_pmac_beep_stop(chip); + snd_pmac_pcm_set_format(chip); + for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) + out_le16(&cp->command, command); + snd_pmac_dma_set_command(rec, &rec->cmd); + (void)in_le32(&rec->dma->status); + snd_pmac_dma_run(rec, RUN|WAKE); + rec->running = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + spin_lock_irqsave(&chip->reg_lock, flags); + rec->running = 0; + /*printk("stopped!!\n");*/ + snd_pmac_dma_stop(rec); + for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) + out_le16(&cp->command, DBDMA_STOP); + spin_unlock_irqrestore(&chip->reg_lock, flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * return the current pointer + */ +inline +static snd_pcm_uframes_t snd_pmac_pcm_pointer(pmac_t *chip, pmac_stream_t *rec, + snd_pcm_substream_t *subs) +{ + int count; + +#if 0 /* hmm.. how can we get the current dma pointer?? */ + if (! rec->cmd.cmds[rec->cur_period].xfer_status) { + count = in_le16(&rec->cmd.cmds[rec->cur_period].res_count); + count = rec->period_size - count; + } else +#endif + count = 0; + count += rec->cur_period * rec->period_size; + /*printk("pointer=%d\n", count);*/ + return bytes_to_frames(subs->runtime, count); +} + +/* + * playback + */ + +static int snd_pmac_playback_prepare(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_prepare(chip, &chip->playback, subs); +} + +static int snd_pmac_playback_trigger(snd_pcm_substream_t *subs, + int cmd) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_trigger(chip, &chip->playback, subs, cmd); +} + +static snd_pcm_uframes_t snd_pmac_playback_pointer(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_pointer(chip, &chip->playback, subs); +} + + +/* + * capture + */ + +static int snd_pmac_capture_prepare(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_prepare(chip, &chip->capture, subs); +} + +static int snd_pmac_capture_trigger(snd_pcm_substream_t *subs, + int cmd) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_trigger(chip, &chip->capture, subs, cmd); +} + +static snd_pcm_uframes_t snd_pmac_capture_pointer(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_pointer(chip, &chip->capture, subs); +} + + +/* + * update playback/capture pointer from interrupts + */ +static void snd_pmac_pcm_update(pmac_t *chip, pmac_stream_t *rec) +{ + volatile struct dbdma_cmd *cp; + int c; + int stat; + + spin_lock(&chip->reg_lock); + if (rec->running) { + cp = &rec->cmd.cmds[rec->cur_period]; + for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */ + stat = ld_le16(&cp->xfer_status); + if (! (stat & ACTIVE)) + break; + /*printk("update frag %d\n", rec->cur_period);*/ + st_le16(&cp->xfer_status, 0); + st_le16(&cp->req_count, rec->period_size); + /*st_le16(&cp->res_count, 0);*/ + rec->cur_period++; + if (rec->cur_period >= rec->nperiods) { + rec->cur_period = 0; + cp = rec->cmd.cmds; + } else + cp++; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(rec->substream); + spin_lock(&chip->reg_lock); + } + } + spin_unlock(&chip->reg_lock); +} + + +/* + * hw info + */ + +static snd_pcm_hardware_t snd_pmac_playback = +{ + info: (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_8000_44100, + rate_min: 7350, + rate_max: 44100, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: 32768, + period_bytes_min: 256, + period_bytes_max: 16384, + periods_min: 1, + periods_max: PMAC_MAX_FRAGS, +}; + +static snd_pcm_hardware_t snd_pmac_capture = +{ + info: (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_8000_44100, + rate_min: 7350, + rate_max: 44100, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: 32768, + period_bytes_min: 256, + period_bytes_max: 16384, + periods_min: 1, + periods_max: PMAC_MAX_FRAGS, +}; + + +#if 0 // NYI +static int snd_pmac_hw_rule_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + pmac_t *chip = rule->private; + pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]); + int i, freq_table[8], num_freqs; + + snd_runtime_check(rec, return -EINVAL); + num_freqs = 0; + for (i = chip->num_freqs - 1; i >= 0; i--) { + if (rec->cur_freqs & (1 << i)) + freq_table[num_freqs++] = chip->freq_table[i]; + } + + return snd_interval_list(hw_param_interval(params, rule->var), + num_freqs, freq_table, 0); +} + +static int snd_pmac_hw_rule_format(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + pmac_t *chip = rule->private; + pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]); + + snd_runtime_check(rec, return -EINVAL); + return snd_mask_refine_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + rec->cur_formats); +} +#endif // NYI + +static int snd_pmac_pcm_open(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + int i, j, fflags; + static int typical_freqs[] = { + 48000, + 44100, + 22050, + 11025, + 0, + }; + static int typical_freq_flags[] = { + SNDRV_PCM_RATE_48000, + SNDRV_PCM_RATE_44100, + SNDRV_PCM_RATE_22050, + SNDRV_PCM_RATE_11025, + 0, + }; + + /* look up frequency table and fill bit mask */ + runtime->hw.rates = 0; + fflags = chip->freqs_ok; + for (i = 0; typical_freqs[i]; i++) { + for (j = 0; j < chip->num_freqs; j++) { + if ((chip->freqs_ok & (1 << j)) && + chip->freq_table[j] == typical_freqs[i]) { + runtime->hw.rates |= typical_freq_flags[i]; + fflags &= ~(1 << j); + break; + } + } + } + if (fflags) /* rest */ + runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + + /* check for minimum and maximum rates */ + for (i = 0; i < chip->num_freqs; i++) { + if (chip->freqs_ok & (1 << i)) { + runtime->hw.rate_max = chip->freq_table[i]; + break; + } + } + for (i = chip->num_freqs - 1; i >= 0; i--) { + if (chip->freqs_ok & (1 << i)) { + runtime->hw.rate_min = chip->freq_table[i]; + break; + } + } + runtime->hw.formats = chip->formats_ok; + if (chip->can_duplex) + runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + runtime->private_data = rec; + rec->substream = subs; + +#if 0 /* FIXME: still under development.. */ + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pmac_hw_rule_rate, chip, rec->stream, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + snd_pmac_hw_rule_format, chip, rec->stream, -1); +#endif + + runtime->hw.periods_max = rec->cmd.size - 1; + + if (chip->can_duplex) + snd_pcm_set_sync(subs); + + return 0; +} + +static int snd_pmac_pcm_close(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +{ + pmac_stream_t *astr; + + snd_pmac_dma_stop(rec); + + astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); + snd_runtime_check(astr, return -EINVAL); + + /* reset constraints */ + astr->cur_freqs = chip->freqs_ok; + astr->cur_formats = chip->formats_ok; + + return 0; +} + +static int snd_pmac_playback_open(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + subs->runtime->hw = snd_pmac_playback; + return snd_pmac_pcm_open(chip, &chip->playback, subs); +} + +static int snd_pmac_capture_open(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + subs->runtime->hw = snd_pmac_capture; + return snd_pmac_pcm_open(chip, &chip->capture, subs); +} + +static int snd_pmac_playback_close(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + return snd_pmac_pcm_close(chip, &chip->playback, subs); +} + +static int snd_pmac_capture_close(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + return snd_pmac_pcm_close(chip, &chip->capture, subs); +} + +/* + */ + +static snd_pcm_ops_t snd_pmac_playback_ops = { + open: snd_pmac_playback_open, + close: snd_pmac_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_pmac_pcm_hw_params, + hw_free: snd_pmac_pcm_hw_free, + prepare: snd_pmac_playback_prepare, + trigger: snd_pmac_playback_trigger, + pointer: snd_pmac_playback_pointer, +}; + +static snd_pcm_ops_t snd_pmac_capture_ops = { + open: snd_pmac_capture_open, + close: snd_pmac_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_pmac_pcm_hw_params, + hw_free: snd_pmac_pcm_hw_free, + prepare: snd_pmac_capture_prepare, + trigger: snd_pmac_capture_trigger, + pointer: snd_pmac_capture_pointer, +}; + +int __init snd_pmac_pcm_new(pmac_t *chip) +{ + snd_pcm_t *pcm; + int err; + int num_captures = 1; + + if (! chip->can_capture) + num_captures = 0; + err = snd_pcm_new(chip->card, chip->card->driver, 0, 1, num_captures, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pmac_playback_ops); + if (chip->can_capture) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pmac_capture_ops); + + pcm->private_data = chip; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->shortname); + chip->pcm = pcm; + + chip->formats_ok = SNDRV_PCM_FMTBIT_S16_BE; + if (chip->can_byte_swap) + chip->formats_ok |= SNDRV_PCM_FMTBIT_S16_LE; + + chip->playback.cur_formats = chip->formats_ok; + chip->capture.cur_formats = chip->formats_ok; + chip->playback.cur_freqs = chip->freqs_ok; + chip->capture.cur_freqs = chip->freqs_ok; + + return 0; +} + + + +/* + * beep stuff + */ + +/* + * Stuff for outputting a beep. The values range from -327 to +327 + * so we can multiply by an amplitude in the range 0..100 to get a + * signed short value to put in the output buffer. + */ +static short beep_wform[256] = { + 0, 40, 79, 117, 153, 187, 218, 245, + 269, 288, 304, 316, 323, 327, 327, 324, + 318, 310, 299, 288, 275, 262, 249, 236, + 224, 213, 204, 196, 190, 186, 183, 182, + 182, 183, 186, 189, 192, 196, 200, 203, + 206, 208, 209, 209, 209, 207, 204, 201, + 197, 193, 188, 183, 179, 174, 170, 166, + 163, 161, 160, 159, 159, 160, 161, 162, + 164, 166, 168, 169, 171, 171, 171, 170, + 169, 167, 163, 159, 155, 150, 144, 139, + 133, 128, 122, 117, 113, 110, 107, 105, + 103, 103, 103, 103, 104, 104, 105, 105, + 105, 103, 101, 97, 92, 86, 78, 68, + 58, 45, 32, 18, 3, -11, -26, -41, + -55, -68, -79, -88, -95, -100, -102, -102, + -99, -93, -85, -75, -62, -48, -33, -16, + 0, 16, 33, 48, 62, 75, 85, 93, + 99, 102, 102, 100, 95, 88, 79, 68, + 55, 41, 26, 11, -3, -18, -32, -45, + -58, -68, -78, -86, -92, -97, -101, -103, + -105, -105, -105, -104, -104, -103, -103, -103, + -103, -105, -107, -110, -113, -117, -122, -128, + -133, -139, -144, -150, -155, -159, -163, -167, + -169, -170, -171, -171, -171, -169, -168, -166, + -164, -162, -161, -160, -159, -159, -160, -161, + -163, -166, -170, -174, -179, -183, -188, -193, + -197, -201, -204, -207, -209, -209, -209, -208, + -206, -203, -200, -196, -192, -189, -186, -183, + -182, -182, -183, -186, -190, -196, -204, -213, + -224, -236, -249, -262, -275, -288, -299, -310, + -318, -324, -327, -327, -323, -316, -304, -288, + -269, -245, -218, -187, -153, -117, -79, -40, +}; + +#define BEEP_SRATE 22050 /* 22050 Hz sample rate */ +#define BEEP_BUFLEN 512 +#define BEEP_VOLUME 15 /* 0 - 100 */ + +static void snd_pmac_beep_stop_callback(unsigned long data) +{ + pmac_t *chip = snd_magic_cast(pmac_t, (void*)data,); + + spin_lock(&chip->reg_lock); + snd_pmac_beep_stop(chip); + snd_pmac_pcm_set_format(chip); + spin_unlock(&chip->reg_lock); +} + +/* because mksound callback takes no private argument, we must keep + the chip pointer here as static variable. + This means that only one chip can beep. Well, it's ok - + anyway we don't like hearing loud beeps from every chip + at the same time :) +*/ + +static pmac_t *beeping_chip = NULL; + +static void snd_pmac_mksound(unsigned int hz, unsigned int ticks) +{ + pmac_t *chip; + pmac_stream_t *rec; + pmac_beep_t *beep; + unsigned long flags; + int beep_speed = 0; + int srate; + int period, ncycles, nsamples; + int i, j, f; + short *p; + + if ((chip = beeping_chip) == NULL || (beep = chip->beep) == NULL) + return; + rec = &chip->playback; + + beep_speed = snd_pmac_rate_index(chip, rec, BEEP_SRATE); + srate = chip->freq_table[beep_speed]; + + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { + /* this is a hack for broken X server code */ + hz = 750; + ticks = 12; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->playback.running || chip->capture.running || beep->running) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + beep->running = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + if (hz == beep->hz && beep->volume == beep->volume_play) { + nsamples = beep->nsamples; + } else { + period = srate * 256 / hz; /* fixed point */ + ncycles = BEEP_BUFLEN * 256 / period; + nsamples = (period * ncycles) >> 8; + f = ncycles * 65536 / nsamples; + j = 0; + p = beep->buf; + for (i = 0; i < nsamples; ++i, p += 2) { + p[0] = p[1] = beep_wform[j >> 8] * beep->volume; + j = (j + f) & 0xffff; + } + beep->hz = hz; + beep->volume_play = beep->volume; + beep->nsamples = nsamples; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + if (beep->running) { + if (ticks <= 0) + ticks = 1; + del_timer(&beep->timer); + beep->timer.expires = jiffies + ticks; + beep->timer.function = snd_pmac_beep_stop_callback; + beep->timer.data = (unsigned long)chip; + add_timer(&beep->timer); + snd_pmac_dma_stop(rec); + st_le16(&chip->extra_dma.cmds->req_count, nsamples * 4); + st_le16(&chip->extra_dma.cmds->xfer_status, 0); + st_le32(&chip->extra_dma.cmds->cmd_dep, chip->extra_dma.addr); + st_le32(&chip->extra_dma.cmds->phy_addr, beep->addr); + st_le16(&chip->extra_dma.cmds->command, OUTPUT_MORE + BR_ALWAYS); + out_le32(&chip->awacs->control, + (in_le32(&chip->awacs->control) & ~0x1f00) + | (beep_speed << 8)); + out_le32(&chip->awacs->byteswap, 0); + snd_pmac_dma_set_command(rec, &chip->extra_dma); + snd_pmac_dma_run(rec, RUN); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* + * beep volume mixer + */ +static int snd_pmac_info_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_pmac_get_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + snd_runtime_check(chip->beep, return -ENXIO); + ucontrol->value.integer.value[0] = chip->beep->volume; + return 0; +} + +static int snd_pmac_put_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int oval; + snd_runtime_check(chip->beep, return -ENXIO); + oval = chip->beep->volume; + chip->beep->volume = ucontrol->value.integer.value[0]; + return oval != chip->beep->volume; +} + +static snd_kcontrol_new_t snd_pmac_beep_mixer = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Beep Playback Volume", + index: 0, + info: snd_pmac_info_beep, + get: snd_pmac_get_beep, + put: snd_pmac_put_beep, +}; + +static void snd_pmac_beep_free(snd_kcontrol_t *control) +{ + pmac_t *chip = snd_magic_cast(pmac_t, _snd_kcontrol_chip(control),); + if (chip->beep) { + /* restore */ + kd_mksound = chip->beep->orig_mksound; + kfree(chip->beep->buf); + kfree(chip->beep); + chip->beep = NULL; + } +} + +/* Initialize beep stuff */ +int __init snd_pmac_attach_beep(pmac_t *chip) +{ + pmac_beep_t *beep; + int err; + + beep = kmalloc(sizeof(*beep), GFP_KERNEL); + if (! beep) + return -ENOMEM; + + beep->buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); + if (! beep->buf) { + kfree(beep); + return -ENOMEM; + } + beep->addr = virt_to_bus(beep->buf); + init_timer(&beep->timer); + beep->timer.function = snd_pmac_beep_stop_callback; + beep->timer.data = (unsigned long) chip; + beep->orig_mksound = kd_mksound; + beep->volume = BEEP_VOLUME; + beep->running = 0; + beep->control = snd_ctl_new1(&snd_pmac_beep_mixer, chip); + if (beep->control == NULL) { + kfree(beep); + return -ENOMEM; + } + beep->control->private_free = snd_pmac_beep_free; + if ((err = snd_ctl_add(chip->card, beep->control)) < 0) { + kfree(beep); + return err; + } + + /* hook */ + beeping_chip = chip; + chip->beep = beep; + kd_mksound = snd_pmac_mksound; + + return 0; +} + +static void snd_pmac_dbdma_reset(pmac_t *chip) +{ + out_le32(&chip->playback.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); + snd_pmac_wait_ack(&chip->playback); + out_le32(&chip->capture.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); + snd_pmac_wait_ack(&chip->capture); +} + + +/* + * interrupt handlers + */ +static void +snd_pmac_tx_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + snd_pmac_pcm_update(chip, &chip->playback); +} + + +static void +snd_pmac_rx_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + snd_pmac_pcm_update(chip, &chip->capture); +} + + +static void +snd_pmac_ctrl_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + int ctrl = in_le32(&chip->awacs->control); + + printk("pmac: control interrupt.. 0x%x\n", ctrl); + if (ctrl & MASK_PORTCHG) { + /* do something when headphone is plugged/unplugged? */ + if (chip->port_change) + chip->port_change(chip); + } + if (ctrl & MASK_CNTLERR) { + int err = (in_le32(&chip->awacs->codec_stat) & MASK_ERRCODE) >> 16; + if (err && chip->model <= PMAC_SCREAMER) + snd_printk("error %x\n", err); + } + /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ + out_le32(&chip->awacs->control, ctrl); +} + + +/* + * feature + */ +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) +static void snd_pmac_sound_feature(pmac_t *chip, int enable) +{ +#ifdef CONFIG_PPC_HAS_FEATURE_CALLS + ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable); +#else + if (chip->is_pbook_G3) { + pmu_suspend(); + feature_clear(chip->node, FEATURE_Sound_power); + feature_clear(chip->node, FEATURE_Sound_CLK_enable); + mdelay(1000); /* XXX */ + pmu_resume(); + } + if (chip->is_pbook_3400) { + feature_set(chip->node, FEATURE_IOBUS_enable); + udelay(10); + } +#endif +} +#else /* CONFIG_PM && CONFIG_PMAC_PBOOK */ +#define snd_pmac_sound_feature(chip,enable) /**/ +#endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */ + +/* + * release resources + */ + +static int snd_pmac_free(pmac_t *chip) +{ + /* stop sounds */ + if (chip->initialized) { + snd_pmac_dbdma_reset(chip); + /* disable interrupts from awacs interface */ + out_le32(&chip->awacs->control, in_le32(&chip->awacs->control) & 0xfff); + } + + snd_pmac_sound_feature(chip, 0); +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) + snd_pmac_unregister_sleep_notifier(chip); +#endif + + /* clean up mixer if any */ + if (chip->mixer_free) + chip->mixer_free(chip); + + /* release resources */ + if (chip->irq >= 0) + free_irq(chip->irq, (void*)chip); + if (chip->tx_irq >= 0) + free_irq(chip->tx_irq, (void*)chip); + if (chip->rx_irq >= 0) + free_irq(chip->rx_irq, (void*)chip); + snd_pmac_dbdma_free(&chip->playback.cmd); + snd_pmac_dbdma_free(&chip->capture.cmd); + snd_pmac_dbdma_free(&chip->extra_dma); + if (chip->macio_base) + iounmap(chip->macio_base); + if (chip->latch_base) + iounmap(chip->latch_base); + if (chip->awacs) + iounmap((void*)chip->awacs); + if (chip->playback.dma) + iounmap((void*)chip->playback.dma); + if (chip->capture.dma) + iounmap((void*)chip->capture.dma); + snd_magic_kfree(chip); + return 0; +} + + +/* + * free the device + */ +static int snd_pmac_dev_free(snd_device_t *device) +{ + pmac_t *chip = snd_magic_cast(pmac_t, device->device_data, return -ENXIO); + return snd_pmac_free(chip); +} + + +/* + * detect a sound chip + */ +static int __init snd_pmac_detect(pmac_t *chip) +{ + struct device_node *sound; + unsigned int *prop, l; + + if (_machine != _MACH_Pmac) + return -ENODEV; + + chip->subframe = 0; + chip->revision = 0; + chip->freqs_ok = 0xff; /* all ok */ + chip->model = PMAC_AWACS; + chip->can_byte_swap = 1; + chip->can_duplex = 1; + chip->can_capture = 1; + chip->num_freqs = 8; + chip->freq_table = awacs_freqs; + + chip->control_mask = MASK_IEPC | MASK_IEE | 0x11; /* default */ + + /* it seems the Pismo & iBook can't byte-swap in hardware. */ + if (machine_is_compatible("PowerBook3,1") || + machine_is_compatible("PowerBook2,1")) + chip->can_byte_swap = 0 ; + if (machine_is_compatible("PowerBook2,1")) + chip->can_duplex = 0; + + /* check machine type */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) + chip->is_pbook_3400 = 1; + else if (machine_is_compatible("PowerBook1,1") + || machine_is_compatible("AAPL,PowerBook1998")) + chip->is_pbook_G3 = 1; + chip->node = find_devices("awacs"); + if (chip->node) + return 0; /* ok */ + + /* + * powermac G3 models have a node called "davbus" + * with a child called "sound". + */ + chip->node = find_devices("davbus"); + /* + * if we didn't find a davbus device, try 'i2s-a' since + * this seems to be what iBooks have + */ + if (! chip->node) + chip->node = find_devices("i2s-a"); + if (! chip->node) + return -ENODEV; + sound = find_devices("sound"); + while (sound && sound->parent != chip->node) + sound = sound->next; + if (! sound) + return -ENODEV; + prop = (unsigned int *) get_property(sound, "sub-frame", 0); + if (prop && *prop < 16) + chip->subframe = *prop; + /* This should be verified on older screamers */ + if (device_is_compatible(sound, "screamer")) { + chip->model = PMAC_SCREAMER; + chip->can_byte_swap = 0; + } + if (device_is_compatible(sound, "burgundy")) { + chip->model = PMAC_BURGUNDY; + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } + if (device_is_compatible(sound, "daca")) { + chip->model = PMAC_DACA; + chip->can_capture = 0; /* no capture */ + chip->can_duplex = 0; + chip->can_byte_swap = 0; /* no le support */ + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } + if (device_is_compatible(sound, "tumbler")) { + chip->model = PMAC_TUMBLER; + chip->can_capture = 0; /* no capture */ + chip->can_duplex = 0; + chip->can_byte_swap = 0; /* no le support */ + chip->num_freqs = 2; + chip->freq_table = tumbler_freqs; + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } + prop = (unsigned int *)get_property(sound, "device-id", 0); + if (prop) + chip->device_id = *prop; + chip->has_iic = (find_devices("perch") != NULL); + + /* look for a property saying what sample rates + are available */ + prop = (unsigned int *) get_property(sound, "sample-rates", &l); + if (! prop) + prop = (unsigned int *) get_property(sound, "output-frame-rates", &l); + if (prop) { + int i; + chip->freqs_ok = 0; + for (l /= sizeof(int); l > 0; --l) { + unsigned int r = *prop++; + /* Apple 'Fixed' format */ + if (r >= 0x10000) + r >>= 16; + for (i = 0; i < chip->num_freqs; ++i) { + if (r == chip->freq_table[i]) { + chip->freqs_ok |= (1 << i); + break; + } + } + } + } else { + /* assume only 44.1khz */ + chip->freqs_ok = 1; + } + return 0; +} + + +/* + * create and detect a pmac chip record + */ +int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return) +{ + pmac_t *chip; + struct device_node *np; + int err; + static snd_device_ops_t ops = { + dev_free: snd_pmac_dev_free, + }; + + snd_runtime_check(chip_return, return -EINVAL); + *chip_return = NULL; + + chip = snd_magic_kcalloc(pmac_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->card = card; + + spin_lock_init(&chip->reg_lock); + chip->irq = chip->tx_irq = chip->rx_irq = -1; + + chip->playback.stream = SNDRV_PCM_STREAM_PLAYBACK; + chip->capture.stream = SNDRV_PCM_STREAM_CAPTURE; + + if ((err = snd_pmac_detect(chip)) < 0) + goto __error; + + if (snd_pmac_dbdma_alloc(&chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 || + snd_pmac_dbdma_alloc(&chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 || + snd_pmac_dbdma_alloc(&chip->extra_dma, 2) < 0) { + err = -ENOMEM; + goto __error; + } + + np = chip->node; + if (np->n_addrs < 3 || np->n_intrs < 3) { + err = -ENODEV; + goto __error; + } + + chip->awacs = (volatile struct awacs_regs *) ioremap(np->addrs[0].address, 0x1000); + chip->playback.dma = (volatile struct dbdma_regs *) ioremap(np->addrs[1].address, 0x100); + chip->capture.dma = (volatile struct dbdma_regs *) ioremap(np->addrs[2].address, 0x100); + if (request_irq(np->intrs[0].line, snd_pmac_ctrl_intr, 0, + "PMac", (void*)chip)) { + snd_printk("unable to grab IRQ %d\n", np->intrs[0].line); + err = -EBUSY; + goto __error; + } + chip->irq = np->intrs[0].line; + if (request_irq(np->intrs[1].line, snd_pmac_tx_intr, 0, + "PMac Output", (void*)chip)) { + snd_printk("unable to grab IRQ %d\n", np->intrs[1].line); + err = -EBUSY; + goto __error; + } + chip->tx_irq = np->intrs[1].line; + if (request_irq(np->intrs[2].line, snd_pmac_rx_intr, 0, + "PMac Input", (void*)chip)) { + snd_printk("unable to grab IRQ %d\n", np->intrs[2].line); + err = -EBUSY; + goto __error; + } + chip->rx_irq = np->intrs[2].line; + + snd_pmac_sound_feature(chip, 1); + + /* reset */ + out_le32(&chip->awacs->control, 0x11); + + /* Powerbooks have odd ways of enabling inputs such as + an expansion-bay CD or sound from an internal modem + or a PC-card modem. */ + if (chip->is_pbook_3400) { + /* Enable CD and PC-card sound inputs. */ + /* This is done by reading from address + * f301a000, + 0x10 to enable the expansion-bay + * CD sound input, + 0x80 to enable the PC-card + * sound input. The 0x100 enables the SCSI bus + * terminator power. + */ + chip->latch_base = (unsigned char *) ioremap (0xf301a000, 0x1000); + in_8(chip->latch_base + 0x190); + } else if (chip->is_pbook_G3) { + struct device_node* mio; + for (mio = chip->node->parent; mio; mio = mio->parent) { + if (strcmp(mio->name, "mac-io") == 0 + && mio->n_addrs > 0) { + chip->macio_base = (unsigned char *) ioremap + (mio->addrs[0].address, 0x40); + break; + } + } + /* Enable CD sound input. */ + /* The relevant bits for writing to this byte are 0x8f. + * I haven't found out what the 0x80 bit does. + * For the 0xf bits, writing 3 or 7 enables the CD + * input, any other value disables it. Values + * 1, 3, 5, 7 enable the microphone. Values 0, 2, + * 4, 6, 8 - f enable the input from the modem. + */ + if (chip->macio_base) + out_8(chip->macio_base + 0x37, 3); + } + + /* Reset dbdma channels */ + snd_pmac_dbdma_reset(chip); + +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) + /* add sleep notifier */ + snd_pmac_register_sleep_notifier(chip); + card->set_power_state = snd_pmac_set_power_state; + card->power_state_private_data = chip; +#endif + + chip->initialized = 1; + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __error; + + *chip_return = chip; + return 0; + + __error: + snd_pmac_free(chip); + return err; +} + + +/* + * sleep notify for powerbook + */ + +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) + +/* + * Save state when going to sleep, restore it afterwards. + */ + +static void snd_pmac_suspend(pmac_t *chip, int can_schedule) +{ + unsigned long flags; + snd_card_t *card = chip->card; + + snd_power_lock(card, can_schedule); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + if (chip->suspend) + chip->suspend(chip); + snd_pcm_suspend_all(chip->pcm); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->beep && chip->beep->running) + snd_pmac_beep_stop(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); + disable_irq(chip->irq); + disable_irq(chip->tx_irq); + disable_irq(chip->rx_irq); + snd_pmac_sound_feature(chip, 0); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void snd_pmac_resume(pmac_t *chip, int can_schedule) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card, can_schedule); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + snd_pmac_sound_feature(chip, 1); + if (chip->resume) + chip->resume(chip); + /* enable CD sound input */ + if (chip->macio_base && chip->is_pbook_G3) { + out_8(chip->macio_base + 0x37, 3); + } else if (chip->is_pbook_3400) { + in_8(chip->latch_base + 0x190); + } + + snd_pmac_pcm_set_format(chip); + + enable_irq(chip->irq); + enable_irq(chip->tx_irq); + enable_irq(chip->rx_irq); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +/* the chip is stored statically by snd_pmac_register_sleep_notifier + * because we can't have any private data for notify callback. + */ +static pmac_t *sleeping_pmac = NULL; + +static int snd_pmac_sleep_notify(struct pmu_sleep_notifier *self, int when) +{ + pmac_t *chip; + + chip = sleeping_pmac; + snd_runtime_check(chip, return 0); + + switch (when) { + case PBOOK_SLEEP_NOW: + snd_pmac_suspend(chip, 0); + break; + case PBOOK_WAKE: + snd_pmac_resume(chip, 0); + break; + } + return PBOOK_SLEEP_OK; +} + +static struct pmu_sleep_notifier snd_pmac_sleep_notifier = { + snd_pmac_sleep_notify, SLEEP_LEVEL_SOUND, +}; + +static int __init snd_pmac_register_sleep_notifier(pmac_t *chip) +{ + /* should be protected here.. */ + if (sleeping_pmac) { + snd_printd("sleep notifier already reigistered\n"); + return -EBUSY; + } + sleeping_pmac = chip; + pmu_register_sleep_notifier(&snd_pmac_sleep_notifier); + chip->sleep_registered = 1; + return 0; +} + +static int snd_pmac_unregister_sleep_notifier(pmac_t *chip) +{ + if (! chip->sleep_registered) + return 0; + /* should be protected here.. */ + if (sleeping_pmac != chip) + return -ENODEV; + pmu_unregister_sleep_notifier(&snd_pmac_sleep_notifier); + sleeping_pmac = NULL; + return 0; +} + +/* callback */ +static int snd_pmac_set_power_state(snd_card_t *card, unsigned int power_state) +{ + pmac_t *chip = snd_magic_cast(pmac_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_pmac_resume(chip, 1); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_pmac_suspend(chip, 1); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */ diff -Nru a/sound/ppc/pmac.h b/sound/ppc/pmac.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/pmac.h Tue Feb 19 18:09:01 2002 @@ -0,0 +1,220 @@ +/* + * Driver for PowerMac onboard soundchips + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __PMAC_H +#define __PMAC_H + +#include +#include +#include "awacs.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#include +#include +#include +#else /* 2.4.0 kernel */ +#include +#ifdef CONFIG_ADB_CUDA +#include +#endif +#ifdef CONFIG_ADB_PMU +#include +#endif +#endif +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) || defined(CONFIG_ADB_CUDA) +#define PMAC_AMP_AVAIL +#endif + +/* maximum number of fragments */ +#define PMAC_MAX_FRAGS 32 + + +/* + * typedefs + */ +typedef struct snd_pmac pmac_t; +typedef struct snd_pmac_stream pmac_stream_t; +typedef struct snd_pmac_beep pmac_beep_t; +typedef struct snd_pmac_dbdma pmac_dbdma_t; + + +/* + * DBDMA space + */ +struct snd_pmac_dbdma { + unsigned long addr; + struct dbdma_cmd *cmds; + void *space; + int size; +}; + +/* + * playback/capture stream + */ +struct snd_pmac_stream { + int running; /* boolean */ + + int stream; /* PLAYBACK/CAPTURE */ + + int dma_size; /* in bytes */ + int period_size; /* in bytes */ + int buffer_size; /* in kbytes */ + int nperiods, cur_period; + + pmac_dbdma_t cmd; + volatile struct dbdma_regs *dma; + + snd_pcm_substream_t *substream; + + unsigned int cur_freqs; /* currently available frequences */ + unsigned int cur_formats; /* currently available formats */ +}; + + +/* + * beep using pcm + */ +struct snd_pmac_beep { + int running; /* boolean */ + int volume; /* mixer volume: 0-100 */ + int volume_play; /* currently playing volume */ + int hz; + int nsamples; + short *buf; /* allocated wave buffer */ + unsigned long addr; /* physical address of buffer */ + struct timer_list timer; /* timer list for stopping beep */ + void (*orig_mksound)(unsigned int, unsigned int); + /* pointer to restore */ + snd_kcontrol_t *control; /* mixer element */ +}; + + +/* + */ + +enum snd_pmac_model { + PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER +}; + +struct snd_pmac { + snd_card_t *card; + + /* h/w info */ + struct device_node *node; + unsigned int revision; + unsigned int subframe; + unsigned int device_id; + enum snd_pmac_model model; + + unsigned int has_iic : 1; + unsigned int is_pbook_3400 : 1; + unsigned int is_pbook_G3 : 1; + + unsigned int can_byte_swap : 1; + unsigned int can_duplex : 1; + unsigned int can_capture : 1; + +#ifdef PMAC_AMP_AVAIL + unsigned int amp_only; + int amp_vol[2]; +#endif + + unsigned int initialized : 1; + unsigned int feature_is_set : 1; + + int num_freqs; + int *freq_table; + unsigned int freqs_ok; /* bit flags */ + unsigned int formats_ok; /* pcm hwinfo */ + int active; + int rate_index; + int format; /* current format */ + + spinlock_t reg_lock; + volatile struct awacs_regs *awacs; + int awacs_reg[8]; /* register cache */ + + unsigned char *latch_base; + unsigned char *macio_base; + + pmac_stream_t playback; + pmac_stream_t capture; + + pmac_dbdma_t extra_dma; + + int irq, tx_irq, rx_irq; + + snd_pcm_t *pcm; + + pmac_beep_t *beep; + + unsigned int control_mask; /* control mask */ + + /* mixer stuffs */ + void *mixer_data; + void (*mixer_free)(pmac_t *); + + /* lowlevel callbacks */ + void (*set_format)(pmac_t *chip); + void (*port_change)(pmac_t *chip); +#ifdef CONFIG_PMAC_PBOOK + unsigned int sleep_registered : 1; + void (*suspend)(pmac_t *chip); + void (*resume)(pmac_t *chip); +#endif + +}; + + +/* exported functions */ +int snd_pmac_new(snd_card_t *card, pmac_t **chip_return); +int snd_pmac_pcm_new(pmac_t *chip); +int snd_pmac_attach_beep(pmac_t *chip); + +/* initialize mixer */ +int snd_pmac_awacs_init(pmac_t *chip); +int snd_pmac_burgundy_init(pmac_t *chip); +int snd_pmac_daca_init(pmac_t *chip); +int snd_pmac_tumbler_init(pmac_t *chip); + +/* i2c functions */ +typedef struct snd_pmac_keywest { + unsigned long base; + int addr; + int steps; +} pmac_keywest_t; + +int snd_pmac_keywest_find(pmac_t *chip, pmac_keywest_t *i2c, int addr, int (*init_client)(pmac_t *, pmac_keywest_t *)); +void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c); +int snd_pmac_keywest_write(pmac_keywest_t *i2c, unsigned char cmd, int len, unsigned char *data); +inline static int snd_pmac_keywest_write_byte(pmac_keywest_t *i2c, unsigned char cmd, unsigned char data) +{ + return snd_pmac_keywest_write(i2c, cmd, 1, &data); +} + + +#endif /* __PMAC_H */ diff -Nru a/sound/ppc/powermac.c b/sound/ppc/powermac.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/powermac.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,187 @@ +/* + * Driver for PowerMac AWACS + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#define SNDRV_GET_ID +#include +#include "pmac.h" +#include "awacs.h" +#include "burgundy.h" + +#define CHIP_NAME "PMac" + +EXPORT_NO_SYMBOLS; +MODULE_DESCRIPTION("PowerMac"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Apple,PowerMac}}"); +MODULE_LICENSE("GPL"); + +static int snd_index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *snd_id = SNDRV_DEFAULT_STR1; /* ID for this card */ +static int snd_enable = 1; +static int snd_enable_beep = 1; + +MODULE_PARM(snd_index, "i"); +MODULE_PARM_DESC(snd_index, "Index value for " CHIP_NAME " soundchip."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "s"); +MODULE_PARM_DESC(snd_id, "ID string for " CHIP_NAME " soundchip."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "i"); +MODULE_PARM_DESC(snd_enable, "Enable this soundchip."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_enable_beep, "i"); +MODULE_PARM_DESC(snd_enable_beep, "Enable beep using PCM."); +MODULE_PARM_SYNTAX(snd_enable_beep, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); + + +/* + * card entry + */ + +static snd_card_t *snd_pmac_card = NULL; + +/* + */ + +static int __init snd_pmac_probe(void) +{ + snd_card_t *card; + pmac_t *chip; + char *name_ext; + int err; + + card = snd_card_new(snd_index, snd_id, THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_pmac_new(card, &chip)) < 0) + goto __error; + + switch (chip->model) { + case PMAC_BURGUNDY: + strcpy(card->driver, "PMac Burgundy"); + strcpy(card->shortname, "PowerMac Burgundy"); + sprintf(card->longname, "%s (Dev %d) Sub-frame %d", + card->shortname, chip->device_id, chip->subframe); + if ((err = snd_pmac_burgundy_init(chip)) < 0) + goto __error; + break; + case PMAC_DACA: + strcpy(card->driver, "PMac DACA"); + strcpy(card->shortname, "PowerMac DACA"); + sprintf(card->longname, "%s (Dev %d) Sub-frame %d", + card->shortname, chip->device_id, chip->subframe); + if ((err = snd_pmac_daca_init(chip)) < 0) + goto __error; + break; + case PMAC_TUMBLER: + strcpy(card->driver, "PMac Tumbler"); + strcpy(card->shortname, "PowerMac Tumbler"); + sprintf(card->longname, "%s (Dev %d) Sub-frame %d", + card->shortname, chip->device_id, chip->subframe); + if ((err = snd_pmac_tumbler_init(chip)) < 0) + goto __error; + break; + case PMAC_AWACS: + case PMAC_SCREAMER: + name_ext = chip->model == PMAC_SCREAMER ? "Screamer" : "AWACS"; + sprintf(card->driver, "PMac %s", name_ext); + sprintf(card->shortname, "PowerMac %s", name_ext); + if (chip->is_pbook_3400) + name_ext = " [PB3400]"; + else if (chip->is_pbook_G3) + name_ext = " [PBG3]"; + else + name_ext = ""; + sprintf(card->longname, "%s%s Rev %d", + card->shortname, name_ext, chip->revision); + if ((err = snd_pmac_awacs_init(chip)) < 0) + goto __error; + break; + default: + snd_printk("unsupported hardware %d\n", chip->model); + err = -EINVAL; + goto __error; + } + + if ((err = snd_pmac_pcm_new(chip)) < 0) + goto __error; + if (snd_enable_beep) + snd_pmac_attach_beep(chip); + + if ((err = snd_card_register(card)) < 0) + goto __error; + + snd_pmac_card = card; + return 0; + +__error: + snd_card_free(card); + return err; +} + + +/* + * MODULE sutff + */ + +static int __init alsa_card_pmac_init(void) +{ + int err; + if ((err = snd_pmac_probe() < 0)) { +#ifdef MODULE + snd_printk("no PMac soundchip found\n"); +#endif + return err; + } + return 0; + +} + +static void __exit alsa_card_pmac_exit(void) +{ + if (snd_pmac_card) + snd_card_free(snd_pmac_card); +} + +module_init(alsa_card_pmac_init) +module_exit(alsa_card_pmac_exit) + +#ifndef MODULE + +/* format is: snd-pmac=snd_enable,snd_index,snd_id,snd_enable_beep + */ + +static int __init alsa_card_pmac_setup(char *str) +{ + (void)(get_option(&str,&snd_enable) == 2 && + get_option(&str,&snd_index) == 2 && + get_id(&str,&snd_id) == 2 && + get_option(&str,&snd_enable_beep) == 2 + ); + return 1; +} + +__setup("snd-pmac=", alsa_card_pmac_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/ppc/tumbler.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,479 @@ +/* + * PMac Tumbler lowlevel functions + * + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pmac.h" + +// #define TUMBLER_TONE_CONTROL_SUPPORT + +#define chip_t pmac_t + +/* i2c address for tumbler */ +#define TAS_I2C_ADDR 0x34 + +/* registers */ +#define TAS_REG_MCS 0x01 +#define TAS_REG_VOL 0x04 +#define TAS_VOL_MAX ((1<<20) - 1) + +#define TAS_REG_TREBLE 0x05 +#define TAS_VOL_MAX_TREBLE 0x96 /* 1 = max, 0x96 = min */ +#define TAS_REG_BASS 0x06 +#define TAS_VOL_MAX_BASS 0x86 /* 1 = max, 0x86 = min */ + +#define TAS_MIXER_VOL_MAX 500 + +typedef struct pmac_tumber_t { + pmac_keywest_t i2c; + void *amp_mute; + void *headphone_mute; + void *headphone_status; + int headphone_irq; + int left_vol, right_vol; + int bass_vol, treble_vol; +} pmac_tumbler_t; + + +/* + * initialize / detect tumbler + */ +static int tumbler_init_client(pmac_t *chip, pmac_keywest_t *i2c) +{ + /* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */ + return snd_pmac_keywest_write_byte(i2c, TAS_REG_MCS, + (1<<6)+(2<<4)+(2<<2)+0); +} + +/* + * update volume + */ +static int tumbler_set_volume(pmac_tumbler_t *mix) +{ + unsigned char block[6]; + unsigned int left_vol, right_vol; + + if (! mix->i2c.base) + return -ENODEV; + + left_vol = mix->left_vol << 6; + right_vol = mix->right_vol << 6; + + if (left_vol > TAS_VOL_MAX) + left_vol = TAS_VOL_MAX; + if (right_vol > TAS_VOL_MAX) + right_vol = TAS_VOL_MAX; + + block[0] = (left_vol >> 16) & 0xff; + block[1] = (left_vol >> 8) & 0xff; + block[2] = (left_vol >> 0) & 0xff; + + block[3] = (right_vol >> 16) & 0xff; + block[4] = (right_vol >> 8) & 0xff; + block[5] = (right_vol >> 0) & 0xff; + + if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_VOL, 6, block) < 0) { + snd_printk("failed to set volume \n"); + return -EINVAL; + } + return 0; +} + + +/* output volume */ +static int tumbler_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = TAS_MIXER_VOL_MAX; + return 0; +} + +static int tumbler_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->left_vol; + ucontrol->value.integer.value[1] = mix->right_vol; + return 0; +} + +static int tumbler_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->left_vol != ucontrol->value.integer.value[0] || + mix->right_vol != ucontrol->value.integer.value[1]; + if (change) { + mix->left_vol = ucontrol->value.integer.value[0]; + mix->right_vol = ucontrol->value.integer.value[1]; + tumbler_set_volume(mix); + } + return change; +} + + +#ifdef TUMBLER_TONE_CONTROL_SUPPORT +static int tumbler_set_bass(pmac_tumbler_t *mix) +{ + unsigned char data; + int val; + + if (! mix->i2c.base) + return -ENODEV; + + val = TAS_VOL_MAX_BASS - mix->bass_vol + 1; + if (val < 1) + data = 1; + else if (val > TAS_VOL_MAX_BASS) + data = TAS_VOL_MAX_BASS; + else + data = val; + if (snd_pmac_keywest_write(&mix->i2c TAS_REG_BASS, 1, &data) < 0) { + snd_printk("failed to set bass volume\n"); + return -EINVAL; + } + return 0; +} + +static int tumbler_set_treble(pmac_tumbler_t *mix) +{ + unsigned char data; + int val; + + if (! mix->i2c.base) + return -ENODEV; + + val = TAS_VOL_MAX_TREBLE - mix->treble_vol + 1; + if (val < 1) + data = 1; + else if (val > TAS_VOL_MAX_BASS) + data = TAS_VOL_MAX_BASS; + else + data = val; + if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_TREBLE, 1, &data) < 0) { + snd_printk("failed to set treble volume\n"); + return -EINVAL; + } + return 0; +} + +/* bass volume */ +static int tumbler_info_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = TAS_VOL_MAX_BASS - 1; + return 0; +} + +static int tumbler_get_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->bass_vol; + return 0; +} + +static int tumbler_put_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->bass_vol != ucontrol->value.integer.value[0]; + if (change) { + mix->bass_vol = ucontrol->value.integer.value[0]; + tumbler_set_bass(mix); + } + return change; +} + +static int tumbler_info_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = TAS_VOL_MAX_TREBLE - 1; + return 0; +} + +static int tumbler_get_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->treble_vol; + return 0; +} + +static int tumbler_put_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->treble_vol != ucontrol->value.integer.value[0]; + if (change) { + mix->treble_vol = ucontrol->value.integer.value[0]; + tumbler_set_treble(mix); + } + return change; +} + +#endif + + +static snd_kcontrol_new_t tumbler_mixers[] = { + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Master Playback Volume", + info: tumbler_info_volume, + get: tumbler_get_volume, + put: tumbler_put_volume + }, +#ifdef TUMBLER_TONE_CONTROL_SUPPORT + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Tone Control - Bass", + info: tumbler_info_bass, + get: tumbler_get_bass, + put: tumbler_put_bass + }, + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Tone Control - Treble", + info: tumbler_info_treble, + get: tumbler_get_treble, + put: tumbler_put_treble + }, +#endif +}; + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +/* mute either amp or headphone according to the plug status */ +static void tumbler_update_headphone(pmac_t *chip) +{ + pmac_tumbler_t *mix = chip->mixer_data; + + if (! mix) + return; + + if (readb(mix->headphone_status) & 2) { + writeb(4 + 1, mix->amp_mute); + writeb(4 + 0, mix->headphone_mute); + } else { + writeb(4 + 0, mix->amp_mute); + writeb(4 + 1, mix->headphone_mute); + } +} + +/* interrupt - headphone plug changed */ +static void headphone_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + tumbler_update_headphone(chip); +} + +/* look for audio-gpio device */ +static struct device_node *find_audio_device(const char *name) +{ + struct device_node *np; + + if (! (np = find_devices("gpio"))) + return NULL; + + for (np = np->child; np; np = np->sibling) { + char *property = get_property(np, "audio-gpio", NULL); + if (property && strcmp(property, name) == 0) + return np; + } + return NULL; +} + +/* find an audio device and get its address */ +static unsigned long tumbler_find_device(const char *device) +{ + struct device_node *node; + void *base; + + node = find_audio_device(device); + if (! node) { + snd_printd("cannot find device %s\n", device); + return 0; + } + + base = (void *)get_property(node, "AAPL,address", NULL); + if (! base) { + snd_printd("cannot find address for device %s\n", device); + return 0; + } + + return *(unsigned long *)base; +} + +/* reset audio */ +static int tumbler_reset_audio(pmac_t *chip) +{ + unsigned long base; + void *map; + + if (! (base = tumbler_find_device("audio-hw-reset"))) + return -ENODEV; + + map = ioremap(base, 1); + writeb(5, map); + mdelay(100); + writeb(4, map); + mdelay(1); + writeb(5, map); + mdelay(1); + iounmap(map); + return 0; +} + +#ifdef CONFIG_PMAC_PBOOK +/* resume mixer */ +static void tumbler_resume(pmac_t *chip) +{ + pmac_tumbler_t *mix = chip->mixer_data; + tumbler_reset_audio(chip); + snd_pmac_keywest_write_byte(&mix->i2c, TAS_REG_MCS, + (1<<6)+(2<<4)+(2<<2)+0); + tumbler_set_volume(mix); + tumbler_update_headphone(chip); /* update mute */ +} +#endif + +/* initialize tumbler */ +static int __init tumbler_init(pmac_t *chip) +{ + unsigned long base; + struct device_node *node; + int err; + pmac_tumbler_t *mix = chip->mixer_data; + + snd_assert(mix, return -EINVAL); + + /* reset audio */ + if (tumbler_reset_audio(chip) < 0) + return -ENODEV; + + /* get amp-mute */ + if (! (base = tumbler_find_device("amp-mute"))) + return -ENODEV; + mix->amp_mute = ioremap(base, 1); + if (! (base = tumbler_find_device("headphone-mute"))) + return -ENODEV; + mix->headphone_mute = ioremap(base, 1); + if (! (base = tumbler_find_device("headphone-detect"))) + return -ENODEV; + mix->headphone_status = ioremap(base, 1); + + /* activate headphone status interrupts */ + writeb(readb(mix->headphone_status) | (1<<7), mix->headphone_status); + + if (! (node = find_audio_device("headphone-detect"))) + return -ENODEV; + if (node->n_intrs == 0) + return -ENODEV; + + if ((err = request_irq(node->intrs[0].line, headphone_intr, 0, + "Tumbler Headphone Detection", chip)) < 0) + return err; + mix->headphone_irq = node->intrs[0].line; + + tumbler_update_headphone(chip); + return 0; +} + +static void tumbler_cleanup(pmac_t *chip) +{ + pmac_tumbler_t *mix = chip->mixer_data; + if (! mix) + return; + + if (mix->headphone_irq >= 0) + free_irq(mix->headphone_irq, chip); + if (mix->amp_mute) + iounmap(mix->amp_mute); + if (mix->headphone_mute) + iounmap(mix->headphone_mute); + if (mix->headphone_status) + iounmap(mix->headphone_status); + snd_pmac_keywest_cleanup(&mix->i2c); + kfree(mix); + chip->mixer_data = NULL; +} + +/* exported */ +int __init snd_pmac_tumbler_init(pmac_t *chip) +{ + int i, err; + pmac_tumbler_t *mix; + + mix = kmalloc(sizeof(*mix), GFP_KERNEL); + if (! mix) + return -ENOMEM; + memset(mix, 0, sizeof(*mix)); + mix->headphone_irq = -1; + + chip->mixer_data = mix; + chip->mixer_free = tumbler_cleanup; + + if ((err = tumbler_init(chip)) < 0) + return err; + + if ((err = snd_pmac_keywest_find(chip, &mix->i2c, TAS_I2C_ADDR, tumbler_init_client)) < 0) + return err; + + /* + * build mixers + */ + strcpy(chip->card->mixername, "PowerMac Tumbler"); + + for (i = 0; i < num_controls(tumbler_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0) + return err; + } + +#ifdef CONFIG_PMAC_PBOOK + chip->resume = tumbler_resume; +#endif + + return 0; +} diff -Nru a/sound/sound_core.c b/sound/sound_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/sound_core.c Tue Feb 19 18:08:59 2002 @@ -0,0 +1,571 @@ +/* + * Sound core handling. Breaks out sound functions to submodules + * + * Author: Alan Cox + * + * Fixes: + * + * + * 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. + * + * -------------------- + * + * Top level handler for the sound subsystem. Various devices can + * plug into this. The fact they dont all go via OSS doesn't mean + * they don't have to implement the OSS API. There is a lot of logic + * to keeping much of the OSS weight out of the code in a compatibility + * module, but its up to the driver to rember to load it... + * + * The code provides a set of functions for registration of devices + * by type. This is done rather than providing a single call so that + * we can hide any future changes in the internals (eg when we go to + * 32bit dev_t) from the modules and their interface. + * + * Secondly we need to allocate the dsp, dsp16 and audio devices as + * one. Thus we misuse the chains a bit to simplify this. + * + * Thirdly to make it more fun and for 2.3.x and above we do all + * of this using fine grained locking. + * + * FIXME: we have to resolve modules and fine grained load/unload + * locking at some point in 2.3.x. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOUND_STEP 16 + + +struct sound_unit +{ + int unit_minor; + struct file_operations *unit_fops; + struct sound_unit *next; + devfs_handle_t de; +}; + +#ifdef CONFIG_SOUND_MSNDCLAS +extern int msnd_classic_init(void); +#endif +#ifdef CONFIG_SOUND_MSNDPIN +extern int msnd_pinnacle_init(void); +#endif + +/* + * Low level list operator. Scan the ordered list, find a hole and + * join into it. Called with the lock asserted + */ + +static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, struct file_operations *fops, int index, int low, int top) +{ + int n=low; + + if (index < 0) { /* first free */ + + while (*list && (*list)->unit_minornext); + + while(nunit_minor>n) + break; + list=&((*list)->next); + n+=SOUND_STEP; + } + + if(n>=top) + return -ENOENT; + } else { + n = low+(index*16); + while (*list) { + if ((*list)->unit_minor==n) + return -EBUSY; + if ((*list)->unit_minor>n) + break; + list=&((*list)->next); + } + } + + /* + * Fill it in + */ + + s->unit_minor=n; + s->unit_fops=fops; + + /* + * Link it + */ + + s->next=*list; + *list=s; + + + return n; +} + +/* + * Remove a node from the chain. Called with the lock asserted + */ + +static void __sound_remove_unit(struct sound_unit **list, int unit) +{ + while(*list) + { + struct sound_unit *p=*list; + if(p->unit_minor==unit) + { + *list=p->next; + devfs_unregister (p->de); + kfree(p); + return; + } + list=&(p->next); + } + printk(KERN_ERR "Sound device %d went missing!\n", unit); +} + +/* + * This lock guards the sound loader list. + */ + +static spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED; + +/* + * Allocate the controlling structure and add it to the sound driver + * list. Acquires locks as needed + */ + +static devfs_handle_t devfs_handle; + +static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode) +{ + int r; + struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL); + char name_buf[16]; + + if(s==NULL) + return -ENOMEM; + + spin_lock(&sound_loader_lock); + r=__sound_insert_unit(s,list,fops,index,low,top); + spin_unlock(&sound_loader_lock); + + if(r<0) + { + kfree(s); + return r; + } + + if (r == low) + sprintf (name_buf, "%s", name); + else + sprintf (name_buf, "%s%d", name, (r - low) / SOUND_STEP); + s->de = devfs_register (devfs_handle, name_buf, + DEVFS_FL_NONE, SOUND_MAJOR, s->unit_minor, + S_IFCHR | mode, fops, NULL); + return r; +} + +/* + * Remove a unit. Acquires locks as needed. The drivers MUST have + * completed the removal before their file operations become + * invalid. + */ + +static void sound_remove_unit(struct sound_unit **list, int unit) +{ + spin_lock(&sound_loader_lock); + __sound_remove_unit(list, unit); + spin_unlock(&sound_loader_lock); +} + +/* + * Allocations + * + * 0 *16 Mixers + * 1 *8 Sequencers + * 2 *16 Midi + * 3 *16 DSP + * 4 *16 SunDSP + * 5 *16 DSP16 + * 6 -- sndstat (obsolete) + * 7 *16 unused + * 8 -- alternate sequencer (see above) + * 9 *16 raw synthesizer access + * 10 *16 unused + * 11 *16 unused + * 12 *16 unused + * 13 *16 unused + * 14 *16 unused + * 15 *16 unused + */ + +static struct sound_unit *chains[16]; + +/** + * register_sound_special - register a special sound node + * @fops: File operations for the driver + * @unit: Unit number to allocate + * + * Allocate a special sound device by minor number from the sound + * subsystem. The allocated number is returned on succes. On failure + * a negative error code is returned. + */ + +int register_sound_special(struct file_operations *fops, int unit) +{ + char *name; + + switch (unit) { + case 0: + name = "mixer"; + break; + case 1: + name = "sequencer"; + break; + case 2: + name = "midi00"; + break; + case 3: + name = "dsp"; + break; + case 4: + name = "audio"; + break; + case 5: + name = "unknown5"; + break; + case 6: /* Was once sndstat */ + name = "unknown6"; + break; + case 7: + name = "unknown7"; + break; + case 8: + name = "sequencer2"; + break; + case 9: + name = "dmmidi"; + break; + case 10: + name = "dmfm"; + break; + case 11: + name = "unknown11"; + break; + case 12: + name = "adsp"; + break; + case 13: + name = "amidi"; + break; + case 14: + name = "admmidi"; + break; + default: + name = "unknown"; + break; + } + return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1, + name, S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_special); + +/** + * register_sound_mixer - register a mixer device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a mixer device. Unit is the number of the mixer requested. + * Pass -1 to request the next free mixer unit. On success the allocated + * number is returned, on failure a negative error code is returned. + */ + +int register_sound_mixer(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[0], fops, dev, 0, 128, + "mixer", S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_mixer); + +/** + * register_sound_midi - register a midi device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a midi device. Unit is the number of the midi device requested. + * Pass -1 to request the next free midi unit. On success the allocated + * number is returned, on failure a negative error code is returned. + */ + +int register_sound_midi(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[2], fops, dev, 2, 130, + "midi", S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_midi); + +/* + * DSP's are registered as a triple. Register only one and cheat + * in open - see below. + */ + +/** + * register_sound_dsp - register a DSP device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a DSP device. Unit is the number of the DSP requested. + * Pass -1 to request the next free DSP unit. On success the allocated + * number is returned, on failure a negative error code is returned. + * + * This function allocates both the audio and dsp device entries together + * and will always allocate them as a matching pair - eg dsp3/audio3 + */ + +int register_sound_dsp(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[3], fops, dev, 3, 131, + "dsp", S_IWUSR | S_IRUSR); +} + +EXPORT_SYMBOL(register_sound_dsp); + +/** + * register_sound_synth - register a synth device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a synth device. Unit is the number of the synth device requested. + * Pass -1 to request the next free synth unit. On success the allocated + * number is returned, on failure a negative error code is returned. + */ + + +int register_sound_synth(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[9], fops, dev, 9, 137, + "synth", S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_synth); + +/** + * unregister_sound_special - unregister a special sound device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with + * register_sound_special(). The unit passed is the return value from + * the register function. + */ + + +void unregister_sound_special(int unit) +{ + sound_remove_unit(&chains[unit&15], unit); +} + +EXPORT_SYMBOL(unregister_sound_special); + +/** + * unregister_sound_mixer - unregister a mixer + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_mixer(). + * The unit passed is the return value from the register function. + */ + +void unregister_sound_mixer(int unit) +{ + sound_remove_unit(&chains[0], unit); +} + +EXPORT_SYMBOL(unregister_sound_mixer); + +/** + * unregister_sound_midi - unregister a midi device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_midi(). + * The unit passed is the return value from the register function. + */ + +void unregister_sound_midi(int unit) +{ + return sound_remove_unit(&chains[2], unit); +} + +EXPORT_SYMBOL(unregister_sound_midi); + +/** + * unregister_sound_dsp - unregister a DSP device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_dsp(). + * The unit passed is the return value from the register function. + * + * Both of the allocated units are released together automatically. + */ + +void unregister_sound_dsp(int unit) +{ + return sound_remove_unit(&chains[3], unit); +} + + +EXPORT_SYMBOL(unregister_sound_dsp); + +/** + * unregister_sound_synth - unregister a synth device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_synth(). + * The unit passed is the return value from the register function. + */ + +void unregister_sound_synth(int unit) +{ + return sound_remove_unit(&chains[9], unit); +} + +EXPORT_SYMBOL(unregister_sound_synth); + +/* + * Now our file operations + */ + +static int soundcore_open(struct inode *, struct file *); + +static struct file_operations soundcore_fops= +{ + /* We must have an owner or the module locking fails */ + owner: THIS_MODULE, + open: soundcore_open, +}; + +static struct sound_unit *__look_for_unit(int chain, int unit) +{ + struct sound_unit *s; + + s=chains[chain]; + while(s && s->unit_minor <= unit) + { + if(s->unit_minor==unit) + return s; + s=s->next; + } + return NULL; +} + +int soundcore_open(struct inode *inode, struct file *file) +{ + int chain; + int unit = minor(inode->i_rdev); + struct sound_unit *s; + struct file_operations *new_fops = NULL; + + chain=unit&0x0F; + if(chain==4 || chain==5) /* dsp/audio/dsp16 */ + { + unit&=0xF0; + unit|=3; + chain=3; + } + + spin_lock(&sound_loader_lock); + s = __look_for_unit(chain, unit); + if (s) + new_fops = fops_get(s->unit_fops); + if (!new_fops) { + char mod[32]; + + spin_unlock(&sound_loader_lock); + /* + * Please, don't change this order or code. + * For ALSA slot means soundcard and OSS emulation code + * comes as add-on modules which aren't depend on + * ALSA toplevel modules for soundcards, thus we need + * load them at first. [Jaroslav Kysela ] + */ + sprintf(mod, "sound-slot-%i", unit>>4); + request_module(mod); + sprintf(mod, "sound-service-%i-%i", unit>>4, chain); + request_module(mod); + spin_lock(&sound_loader_lock); + s = __look_for_unit(chain, unit); + if (s) + new_fops = fops_get(s->unit_fops); + } + if (new_fops) { + /* + * We rely upon the fact that we can't be unloaded while the + * subdriver is there, so if ->open() is successful we can + * safely drop the reference counter and if it is not we can + * revert to old ->f_op. Ugly, indeed, but that's the cost of + * switching ->f_op in the first place. + */ + int err = 0; + struct file_operations *old_fops = file->f_op; + file->f_op = new_fops; + spin_unlock(&sound_loader_lock); + if(file->f_op->open) + err = file->f_op->open(inode,file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; + } + spin_unlock(&sound_loader_lock); + return -ENODEV; +} + +extern int mod_firmware_load(const char *, char **); +EXPORT_SYMBOL(mod_firmware_load); + + +MODULE_DESCRIPTION("Core sound module"); +MODULE_AUTHOR("Alan Cox"); +MODULE_LICENSE("GPL"); + +static void __exit cleanup_soundcore(void) +{ + /* We have nothing to really do here - we know the lists must be + empty */ + devfs_unregister_chrdev(SOUND_MAJOR, "sound"); + devfs_unregister (devfs_handle); +} + +static int __init init_soundcore(void) +{ + if(devfs_register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) + { + printk(KERN_ERR "soundcore: sound device already in use.\n"); + return -EBUSY; + } + devfs_handle = devfs_mk_dir (NULL, "sound", NULL); + + return 0; +} + +module_init(init_soundcore); +module_exit(cleanup_soundcore); diff -Nru a/sound/sound_firmware.c b/sound/sound_firmware.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/sound_firmware.c Tue Feb 19 18:08:57 2002 @@ -0,0 +1,78 @@ +#include +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include +#include + +static int errno; +static int do_mod_firmware_load(const char *fn, char **fp) +{ + int fd; + long l; + char *dp; + + fd = open(fn, 0, 0); + if (fd == -1) + { + printk(KERN_INFO "Unable to load '%s'.\n", fn); + return 0; + } + l = lseek(fd, 0L, 2); + if (l <= 0 || l > 131072) + { + printk(KERN_INFO "Invalid firmware '%s'\n", fn); + sys_close(fd); + return 0; + } + lseek(fd, 0L, 0); + dp = vmalloc(l); + if (dp == NULL) + { + printk(KERN_INFO "Out of memory loading '%s'.\n", fn); + sys_close(fd); + return 0; + } + if (read(fd, dp, l) != l) + { + printk(KERN_INFO "Failed to read '%s'.\n", fn); + vfree(dp); + sys_close(fd); + return 0; + } + close(fd); + *fp = dp; + return (int) l; +} + +/** + * mod_firmware_load - load sound driver firmware + * @fn: filename + * @fp: return for the buffer. + * + * Load the firmware for a sound module (up to 128K) into a buffer. + * The buffer is returned in *fp. It is allocated with vmalloc so is + * virtually linear and not DMAable. The caller should free it with + * vfree when finished. + * + * The length of the buffer is returned on a successful load, the + * value zero on a failure. + * + * Caution: This API is not recommended. Firmware should be loaded via + * an ioctl call and a setup application. This function may disappear + * in future. + */ + +int mod_firmware_load(const char *fn, char **fp) +{ + int r; + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + r = do_mod_firmware_load(fn, fp); + set_fs(fs); + return r; +} + diff -Nru a/sound/synth/Makefile b/sound/synth/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/Makefile Tue Feb 19 18:09:00 2002 @@ -0,0 +1,27 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := synth.o + +subdir-y := emux +subdir-m := $(subdir-y) + +list-multi := snd-util-mem.o + +export-objs := util_mem.o + +snd-util-mem-objs := util_mem.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o +obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_SBAWE) += snd-util-mem.o +endif + +include $(TOPDIR)/Rules.make + +snd-util-mem.o: $(snd-util-mem-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-util-mem-objs) diff -Nru a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/emux/Makefile Tue Feb 19 18:09:01 2002 @@ -0,0 +1,24 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _emux.o + +list-multi := snd-emux-synth.o + +export-objs := emux.o + +snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \ + emux_effect.o emux_proc.o emux_oss.o soundfont.o + +# Toplevel Module Dependency +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_SBAWE) += snd-emux-synth.o + obj-$(CONFIG_SND_EMU10K1) += snd-emux-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-emux-synth.o: $(snd-emux-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emux-synth-objs) diff -Nru a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/emux/emux.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Routines for control of EMU WaveTable chip + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "emux_voice.h" + +MODULE_AUTHOR("Takashi Iwai"); +MODULE_DESCRIPTION("Routines for control of EMU WaveTable chip"); +MODULE_LICENSE("GPL"); + +/* + * create a new hardware dependent device for Emu8000/Emu10k1 + */ +int snd_emux_new(snd_emux_t **remu) +{ + snd_emux_t *emu; + + *remu = NULL; + emu = snd_magic_kcalloc(snd_emux_t, 0, GFP_KERNEL); + if (emu == NULL) + return -ENOMEM; + + spin_lock_init(&emu->voice_lock); + init_MUTEX(&emu->register_mutex); + + emu->client = -1; +#ifdef CONFIG_SND_OSSEMUL + emu->oss_synth = NULL; +#endif + emu->max_voices = 0; + emu->use_time = 0; + + emu->tlist.function = snd_emux_timer_callback; + emu->tlist.data = (unsigned long)emu; + emu->timer_active = 0; + + *remu = emu; + return 0; +} + + +/* + */ +int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name) +{ + snd_sf_callback_t sf_cb; + + snd_assert(emu->hw != NULL, return -EINVAL); + snd_assert(emu->max_voices > 0, return -EINVAL); + snd_assert(card != NULL, return -EINVAL); + snd_assert(name != NULL, return -EINVAL); + + emu->card = card; + emu->name = snd_kmalloc_strdup(name, GFP_KERNEL); + emu->voices = snd_kcalloc(sizeof(snd_emux_voice_t) * emu->max_voices, GFP_KERNEL); + if (emu->voices == NULL) + return -ENOMEM; + + /* create soundfont list */ + memset(&sf_cb, 0, sizeof(sf_cb)); + sf_cb.private_data = emu; + sf_cb.sample_new = (snd_sf_sample_new_t)emu->ops.sample_new; + sf_cb.sample_free = (snd_sf_sample_free_t)emu->ops.sample_free; + sf_cb.sample_reset = (snd_sf_sample_reset_t)emu->ops.sample_reset; + emu->sflist = snd_sf_new(&sf_cb, emu->memhdr); + if (emu->sflist == NULL) + return -ENOMEM; + + snd_emux_init_voices(emu); + + snd_emux_init_seq(emu, card, index); +#ifdef CONFIG_SND_OSSEMUL + snd_emux_init_seq_oss(emu); +#endif + snd_emux_init_virmidi(emu, card); + +#ifdef CONFIG_PROC_FS + snd_emux_proc_init(emu, card, index); +#endif + return 0; +} + + +/* + */ +int snd_emux_free(snd_emux_t *emu) +{ + unsigned long flags; + + if (! emu) + return -EINVAL; + + spin_lock_irqsave(&emu->voice_lock, flags); + if (emu->timer_active) + del_timer(&emu->tlist); + spin_unlock_irqrestore(&emu->voice_lock, flags); + +#ifdef CONFIG_PROC_FS + snd_emux_proc_free(emu); +#endif + snd_emux_delete_virmidi(emu); +#ifdef CONFIG_SND_OSSEMUL + snd_emux_detach_seq_oss(emu); +#endif + snd_emux_detach_seq(emu); + + if (emu->sflist) + snd_sf_free(emu->sflist); + + if (emu->voices) + kfree(emu->voices); + + if (emu->name) + kfree(emu->name); + + snd_magic_kfree(emu); + return 0; +} + + +EXPORT_SYMBOL(snd_emux_new); +EXPORT_SYMBOL(snd_emux_register); +EXPORT_SYMBOL(snd_emux_free); + +EXPORT_SYMBOL(snd_emux_terminate_all); +EXPORT_SYMBOL(snd_emux_lock_voice); +EXPORT_SYMBOL(snd_emux_unlock_voice); + + +/* + * INIT part + */ + +static int __init alsa_emux_init(void) +{ + return 0; +} + +static void __exit alsa_emux_exit(void) +{ +} + +module_init(alsa_emux_init) +module_exit(alsa_emux_exit) diff -Nru a/sound/synth/emux/emux_effect.c b/sound/synth/emux/emux_effect.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/emux/emux_effect.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,309 @@ +/* + * Midi synth routines for the Emu8k/Emu10k1 + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * Contains code based on awe_wave.c by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include "emux_voice.h" +#include + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT +/* + * effects table + */ + +#define xoffsetof(type,tag) ((long)(&((type)NULL)->tag) - (long)(NULL)) + +#define parm_offset(tag) xoffsetof(soundfont_voice_parm_t*, tag) + +#define PARM_IS_BYTE (1 << 0) +#define PARM_IS_WORD (1 << 1) +#define PARM_IS_ALIGNED (3 << 2) +#define PARM_IS_ALIGN_HI (1 << 2) +#define PARM_IS_ALIGN_LO (2 << 2) +#define PARM_IS_SIGNED (1 << 4) + +#define PARM_WORD (PARM_IS_WORD) +#define PARM_BYTE_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO) +#define PARM_BYTE_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI) +#define PARM_BYTE (PARM_IS_BYTE) +#define PARM_SIGN_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO|PARM_IS_SIGNED) +#define PARM_SIGN_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI|PARM_IS_SIGNED) + +static struct emux_parm_defs { + int type; /* byte or word */ + int low, high; /* value range */ + long offset; /* offset in parameter record (-1 = not written) */ + int update; /* flgas for real-time update */ +} parm_defs[EMUX_NUM_EFFECTS] = { + {PARM_WORD, 0, 0x8000, parm_offset(moddelay), 0}, /* env1 delay */ + {PARM_BYTE_LO, 1, 0x80, parm_offset(modatkhld), 0}, /* env1 attack */ + {PARM_BYTE_HI, 0, 0x7e, parm_offset(modatkhld), 0}, /* env1 hold */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(moddcysus), 0}, /* env1 decay */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(modrelease), 0}, /* env1 release */ + {PARM_BYTE_HI, 0, 0x7f, parm_offset(moddcysus), 0}, /* env1 sustain */ + {PARM_BYTE_HI, 0, 0xff, parm_offset(pefe), 0}, /* env1 pitch */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(pefe), 0}, /* env1 fc */ + + {PARM_WORD, 0, 0x8000, parm_offset(voldelay), 0}, /* env2 delay */ + {PARM_BYTE_LO, 1, 0x80, parm_offset(volatkhld), 0}, /* env2 attack */ + {PARM_BYTE_HI, 0, 0x7e, parm_offset(volatkhld), 0}, /* env2 hold */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(voldcysus), 0}, /* env2 decay */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(volrelease), 0}, /* env2 release */ + {PARM_BYTE_HI, 0, 0x7f, parm_offset(voldcysus), 0}, /* env2 sustain */ + + {PARM_WORD, 0, 0x8000, parm_offset(lfo1delay), 0}, /* lfo1 delay */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 freq */ + {PARM_SIGN_HI, -128, 127, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 vol */ + {PARM_SIGN_HI, -128, 127, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 pitch */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 cutoff */ + + {PARM_WORD, 0, 0x8000, parm_offset(lfo2delay), 0}, /* lfo2 delay */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 freq */ + {PARM_SIGN_HI, -128, 127, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 pitch */ + + {PARM_WORD, 0, 0xffff, -1, SNDRV_EMUX_UPDATE_PITCH}, /* initial pitch */ + {PARM_BYTE, 0, 0xff, parm_offset(chorus), 0}, /* chorus */ + {PARM_BYTE, 0, 0xff, parm_offset(reverb), 0}, /* reverb */ + {PARM_BYTE, 0, 0xff, parm_offset(cutoff), SNDRV_EMUX_UPDATE_VOLUME}, /* cutoff */ + {PARM_BYTE, 0, 15, parm_offset(filterQ), SNDRV_EMUX_UPDATE_Q}, /* resonance */ + + {PARM_WORD, 0, 0xffff, -1, 0}, /* sample start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* loop start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* loop end */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse sample start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop end */ + {PARM_BYTE, 0, 0xff, -1, SNDRV_EMUX_UPDATE_VOLUME}, /* initial attenuation */ +}; + +/* set byte effect value */ +static void +effect_set_byte(unsigned char *valp, snd_midi_channel_t *chan, int type) +{ + short effect; + snd_emux_effect_table_t *fx = chan->private; + + effect = fx->val[type]; + if (fx->flag[type] == EMUX_FX_FLAG_ADD) { + if (parm_defs[type].type & PARM_IS_SIGNED) + effect += *(char*)valp; + else + effect += *valp; + } + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + *valp = (unsigned char)effect; +} + +/* set word effect value */ +static void +effect_set_word(unsigned short *valp, snd_midi_channel_t *chan, int type) +{ + int effect; + snd_emux_effect_table_t *fx = chan->private; + + effect = *(unsigned short*)&fx->val[type]; + if (fx->flag[type] == EMUX_FX_FLAG_ADD) + effect += *valp; + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + *valp = (unsigned short)effect; +} + +/* address offset */ +static int +effect_get_offset(snd_midi_channel_t *chan, int lo, int hi, int mode) +{ + int addr = 0; + snd_emux_effect_table_t *fx = chan->private; + + if (fx->flag[hi]) + addr = (short)fx->val[hi]; + addr = addr << 15; + if (fx->flag[lo]) + addr += (short)fx->val[lo]; + if (!(mode & SNDRV_SFNT_SAMPLE_8BITS)) + addr /= 2; + return addr; +} + +#ifdef CONFIG_SND_OSSEMUL +/* change effects - for OSS sequencer compatibility */ +void +snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val) +{ + int mode; + + if (type & 0x40) + mode = EMUX_FX_FLAG_OFF; + else if (type & 0x80) + mode = EMUX_FX_FLAG_ADD; + else + mode = EMUX_FX_FLAG_SET; + type &= 0x3f; + + snd_emux_send_effect(port, chan, type, val, mode); +} +#endif + +/* Modify the effect value. + * if update is necessary, call emu8000_control + */ +void +snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode) +{ + int i; + int offset; + unsigned char *srcp, *origp; + snd_emux_t *emu; + snd_emux_effect_table_t *fx; + unsigned long flags; + + emu = port->emu; + fx = chan->private; + if (emu == NULL || fx == NULL) + return; + if (type < 0 || type >= EMUX_NUM_EFFECTS) + return; + + fx->val[type] = val; + fx->flag[type] = mode; + + /* do we need to modify the register in realtime ? */ + if (! parm_defs[type].update || (offset = parm_defs[type].offset) < 0) + return; + +#ifdef SNDRV_LITTLE_ENDIAN + if (parm_defs[type].type & PARM_IS_ALIGN_HI) + offset++; +#else + if (parm_defs[type].type & PARM_IS_ALIGN_LO) + offset++; +#endif + /* modify the register values */ + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + snd_emux_voice_t *vp = &emu->voices[i]; + if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan) + continue; + srcp = (unsigned char*)&vp->reg.parm + offset; + origp = (unsigned char*)&vp->zone->v.parm + offset; + if (parm_defs[i].type & PARM_IS_BYTE) { + *srcp = *origp; + effect_set_byte(srcp, chan, type); + } else { + *(unsigned short*)srcp = *(unsigned short*)origp; + effect_set_word((unsigned short*)srcp, chan, type); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + + /* activate them */ + snd_emux_update_channel(port, chan, parm_defs[type].update); +} + + +/* copy wavetable registers to voice table */ +void +snd_emux_setup_effect(snd_emux_voice_t *vp) +{ + snd_midi_channel_t *chan = vp->chan; + snd_emux_effect_table_t *fx; + unsigned char *srcp; + int i; + + if (! (fx = chan->private)) + return; + + /* modify the register values via effect table */ + for (i = 0; i < EMUX_FX_END; i++) { + int offset; + if (! fx->flag[i] || (offset = parm_defs[i].offset) < 0) + continue; +#ifdef SNDRV_LITTLE_ENDIAN + if (parm_defs[i].type & PARM_IS_ALIGN_HI) + offset++; +#else + if (parm_defs[i].type & PARM_IS_ALIGN_LO) + offset++; +#endif + srcp = (unsigned char*)&vp->reg.parm + offset; + if (parm_defs[i].type & PARM_IS_BYTE) + effect_set_byte(srcp, chan, i); + else + effect_set_word((unsigned short*)srcp, chan, i); + } + + /* correct sample and loop points */ + vp->reg.start += effect_get_offset(chan, EMUX_FX_SAMPLE_START, + EMUX_FX_COARSE_SAMPLE_START, + vp->reg.sample_mode); + + vp->reg.loopstart += effect_get_offset(chan, EMUX_FX_LOOP_START, + EMUX_FX_COARSE_LOOP_START, + vp->reg.sample_mode); + + vp->reg.loopend += effect_get_offset(chan, EMUX_FX_LOOP_END, + EMUX_FX_COARSE_LOOP_END, + vp->reg.sample_mode); +} + +/* + * effect table + */ +void +snd_emux_create_effect(snd_emux_port_t *p) +{ + int i; + p->effect = snd_kcalloc(sizeof(snd_emux_effect_table_t) * p->chset.max_channels, GFP_KERNEL); + if (p->effect) { + for (i = 0; i < p->chset.max_channels; i++) + p->chset.channels[i].private = p->effect + i; + } else { + for (i = 0; i < p->chset.max_channels; i++) + p->chset.channels[i].private = NULL; + } +} + +void +snd_emux_delete_effect(snd_emux_port_t *p) +{ + if (p->effect) { + kfree(p->effect); + p->effect = NULL; + } +} + +void +snd_emux_clear_effect(snd_emux_port_t *p) +{ + if (p->effect) { + memset(p->effect, 0, sizeof(snd_emux_effect_table_t) * p->chset.max_channels); + } +} + +#endif /* SNDRV_EMUX_USE_RAW_EFFECT */ diff -Nru a/sound/synth/emux/emux_nrpn.c b/sound/synth/emux/emux_nrpn.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/emux/emux_nrpn.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,401 @@ +/* + * NRPN / SYSEX callbacks for Emu8k/Emu10k1 + * + * Copyright (c) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include "emux_voice.h" +#include + +#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) + +/* + * conversion from NRPN/control parameters to Emu8000 raw parameters + */ + +/* NRPN / CC -> Emu8000 parameter converter */ +typedef struct { + int control; + int effect; + int (*convert)(int val); +} nrpn_conv_table; + +/* effect sensitivity */ + +#define FX_CUTOFF 0 +#define FX_RESONANCE 1 +#define FX_ATTACK 2 +#define FX_RELEASE 3 +#define FX_VIBRATE 4 +#define FX_VIBDEPTH 5 +#define FX_VIBDELAY 6 +#define FX_NUMS 7 + +/* + * convert NRPN/control values + */ + +static int send_converted_effect(nrpn_conv_table *table, int num_tables, + snd_emux_port_t *port, snd_midi_channel_t *chan, + int type, int val, int mode) +{ + int i, cval; + for (i = 0; i < num_tables; i++) { + if (table[i].control == type) { + cval = table[i].convert(val); + snd_emux_send_effect(port, chan, table[i].effect, + cval, mode); + return 1; + } + } + return 0; +} + +#define DEF_FX_CUTOFF 170 +#define DEF_FX_RESONANCE 6 +#define DEF_FX_ATTACK 50 +#define DEF_FX_RELEASE 50 +#define DEF_FX_VIBRATE 30 +#define DEF_FX_VIBDEPTH 4 +#define DEF_FX_VIBDELAY 1500 + +/* effect sensitivities for GS NRPN: + * adjusted for chaos 8MB soundfonts + */ +static int gs_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; + +/* effect sensitivies for XG controls: + * adjusted for chaos 8MB soundfonts + */ +static int xg_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; + + +/* + * AWE32 NRPN effects + */ + +static int fx_delay(int val); +static int fx_attack(int val); +static int fx_hold(int val); +static int fx_decay(int val); +static int fx_the_value(int val); +static int fx_twice_value(int val); +static int fx_conv_pitch(int val); +static int fx_conv_Q(int val); + +/* function for each NRPN */ /* [range] units */ +#define fx_env1_delay fx_delay /* [0,5900] 4msec */ +#define fx_env1_attack fx_attack /* [0,5940] 1msec */ +#define fx_env1_hold fx_hold /* [0,8191] 1msec */ +#define fx_env1_decay fx_decay /* [0,5940] 4msec */ +#define fx_env1_release fx_decay /* [0,5940] 4msec */ +#define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */ +#define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */ + +#define fx_env2_delay fx_delay /* [0,5900] 4msec */ +#define fx_env2_attack fx_attack /* [0,5940] 1msec */ +#define fx_env2_hold fx_hold /* [0,8191] 1msec */ +#define fx_env2_decay fx_decay /* [0,5940] 4msec */ +#define fx_env2_release fx_decay /* [0,5940] 4msec */ +#define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */ + +#define fx_lfo1_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */ +#define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */ + +#define fx_lfo2_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */ + +#define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */ +#define fx_chorus fx_the_value /* [0,255] -- */ +#define fx_reverb fx_the_value /* [0,255] -- */ +#define fx_cutoff fx_twice_value /* [0,127] 62Hz */ +#define fx_filterQ fx_conv_Q /* [0,127] -- */ + +static int fx_delay(int val) +{ + return (unsigned short)snd_sf_calc_parm_delay(val); +} + +static int fx_attack(int val) +{ + return (unsigned short)snd_sf_calc_parm_attack(val); +} + +static int fx_hold(int val) +{ + return (unsigned short)snd_sf_calc_parm_hold(val); +} + +static int fx_decay(int val) +{ + return (unsigned short)snd_sf_calc_parm_decay(val); +} + +static int fx_the_value(int val) +{ + return (unsigned short)(val & 0xff); +} + +static int fx_twice_value(int val) +{ + return (unsigned short)((val * 2) & 0xff); +} + +static int fx_conv_pitch(int val) +{ + return (short)(val * 4096 / 1200); +} + +static int fx_conv_Q(int val) +{ + return (unsigned short)((val / 8) & 0xff); +} + + +static nrpn_conv_table awe_effects[] = +{ + { 0, EMUX_FX_LFO1_DELAY, fx_lfo1_delay}, + { 1, EMUX_FX_LFO1_FREQ, fx_lfo1_freq}, + { 2, EMUX_FX_LFO2_DELAY, fx_lfo2_delay}, + { 3, EMUX_FX_LFO2_FREQ, fx_lfo2_freq}, + + { 4, EMUX_FX_ENV1_DELAY, fx_env1_delay}, + { 5, EMUX_FX_ENV1_ATTACK,fx_env1_attack}, + { 6, EMUX_FX_ENV1_HOLD, fx_env1_hold}, + { 7, EMUX_FX_ENV1_DECAY, fx_env1_decay}, + { 8, EMUX_FX_ENV1_SUSTAIN, fx_env1_sustain}, + { 9, EMUX_FX_ENV1_RELEASE, fx_env1_release}, + + {10, EMUX_FX_ENV2_DELAY, fx_env2_delay}, + {11, EMUX_FX_ENV2_ATTACK, fx_env2_attack}, + {12, EMUX_FX_ENV2_HOLD, fx_env2_hold}, + {13, EMUX_FX_ENV2_DECAY, fx_env2_decay}, + {14, EMUX_FX_ENV2_SUSTAIN, fx_env2_sustain}, + {15, EMUX_FX_ENV2_RELEASE, fx_env2_release}, + + {16, EMUX_FX_INIT_PITCH, fx_init_pitch}, + {17, EMUX_FX_LFO1_PITCH, fx_lfo1_pitch}, + {18, EMUX_FX_LFO2_PITCH, fx_lfo2_pitch}, + {19, EMUX_FX_ENV1_PITCH, fx_env1_pitch}, + {20, EMUX_FX_LFO1_VOLUME, fx_lfo1_volume}, + {21, EMUX_FX_CUTOFF, fx_cutoff}, + {22, EMUX_FX_FILTERQ, fx_filterQ}, + {23, EMUX_FX_LFO1_CUTOFF, fx_lfo1_cutoff}, + {24, EMUX_FX_ENV1_CUTOFF, fx_env1_cutoff}, + {25, EMUX_FX_CHORUS, fx_chorus}, + {26, EMUX_FX_REVERB, fx_reverb}, +}; + +static int num_awe_effects = NELEM(awe_effects); + + +/* + * GS(SC88) NRPN effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static int gs_cutoff(int val) +{ + return (val - 64) * gs_sense[FX_CUTOFF] / 50; +} + +/* resonance: 0 to 15(max) */ +static int gs_filterQ(int val) +{ + return (val - 64) * gs_sense[FX_RESONANCE] / 50; +} + +/* attack: */ +static int gs_attack(int val) +{ + return -(val - 64) * gs_sense[FX_ATTACK] / 50; +} + +/* decay: */ +static int gs_decay(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* release: */ +static int gs_release(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* vibrato freq: 0.042Hz step, max=255 */ +static int gs_vib_rate(int val) +{ + return (val - 64) * gs_sense[FX_VIBRATE] / 50; +} + +/* vibrato depth: max=127, 1 octave */ +static int gs_vib_depth(int val) +{ + return (val - 64) * gs_sense[FX_VIBDEPTH] / 50; +} + +/* vibrato delay: -0.725msec step */ +static int gs_vib_delay(int val) +{ + return -(val - 64) * gs_sense[FX_VIBDELAY] / 50; +} + +static nrpn_conv_table gs_effects[] = +{ + {32, EMUX_FX_CUTOFF, gs_cutoff}, + {33, EMUX_FX_FILTERQ, gs_filterQ}, + {99, EMUX_FX_ENV2_ATTACK, gs_attack}, + {100, EMUX_FX_ENV2_DECAY, gs_decay}, + {102, EMUX_FX_ENV2_RELEASE, gs_release}, + {8, EMUX_FX_LFO1_FREQ, gs_vib_rate}, + {9, EMUX_FX_LFO1_VOLUME, gs_vib_depth}, + {10, EMUX_FX_LFO1_DELAY, gs_vib_delay}, +}; + +static int num_gs_effects = NELEM(gs_effects); + + +/* + * NRPN events + */ +void +snd_emux_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset) +{ + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL, return); + snd_assert(chan != NULL, return); + + if (chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 127 && + chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] <= 26) { + int val; + /* Win/DOS AWE32 specific NRPNs */ + /* both MSB/LSB necessary */ + val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) | + chan->control[MIDI_CTL_LSB_DATA_ENTRY]; + val -= 8192; + send_converted_effect + (awe_effects, num_awe_effects, + port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB], + val, EMUX_FX_FLAG_SET); + return; + } + + if (port->chset.midi_mode == SNDRV_MIDI_MODE_GS && + chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 1) { + int val; + /* GS specific NRPNs */ + /* only MSB is valid */ + val = chan->control[MIDI_CTL_MSB_DATA_ENTRY]; + send_converted_effect + (gs_effects, num_gs_effects, + port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB], + val, EMUX_FX_FLAG_ADD); + return; + } +} + + +/* + * XG control effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static int xg_cutoff(int val) +{ + return (val - 64) * xg_sense[FX_CUTOFF] / 64; +} + +/* resonance: 0(open) to 15(most nasal) */ +static int xg_filterQ(int val) +{ + return (val - 64) * xg_sense[FX_RESONANCE] / 64; +} + +/* attack: */ +static int xg_attack(int val) +{ + return -(val - 64) * xg_sense[FX_ATTACK] / 64; +} + +/* release: */ +static int xg_release(int val) +{ + return -(val - 64) * xg_sense[FX_RELEASE] / 64; +} + +static nrpn_conv_table xg_effects[] = +{ + {71, EMUX_FX_CUTOFF, xg_cutoff}, + {74, EMUX_FX_FILTERQ, xg_filterQ}, + {72, EMUX_FX_ENV2_RELEASE, xg_release}, + {73, EMUX_FX_ENV2_ATTACK, xg_attack}, +}; + +static int num_xg_effects = NELEM(xg_effects); + +int +snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param) +{ + return send_converted_effect(xg_effects, num_xg_effects, + port, chan, param, + chan->control[param], + EMUX_FX_FLAG_ADD); +} + +/* + * receive sysex + */ +void +snd_emux_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset) +{ + snd_emux_port_t *port; + snd_emux_t *emu; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL, return); + snd_assert(chset != NULL, return); + emu = port->emu; + + switch (parsed) { + case SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME: + snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME); + break; + default: + if (emu->ops.sysex) + emu->ops.sysex(emu, buf, len, parsed, chset); + break; + } +} + diff -Nru a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/emux/emux_oss.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,496 @@ +/* + * Interface for OSS sequencer emulation + * + * Copyright (C) 1999 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Changes + * 19990227 Steve Ratcliffe Made separate file and merged in latest + * midi emulation. + */ + +#define __NO_VERSION__ +#include + +#ifdef CONFIG_SND_OSSEMUL + +#include +#include +#include "emux_voice.h" +#include + +static int snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure); +static int snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg); +static int snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg); +static int snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char *buf, int offs, int count); +static int snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg); +static int snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); +static void reset_port_mode(snd_emux_port_t *port, int midi_mode); +static void emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop); +static void gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop); +static void fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop); + +/* operators */ +static snd_seq_oss_callback_t oss_callback = { + owner: THIS_MODULE, + open: snd_emux_open_seq_oss, + close: snd_emux_close_seq_oss, + ioctl: snd_emux_ioctl_seq_oss, + load_patch: snd_emux_load_patch_seq_oss, + reset: snd_emux_reset_seq_oss, +}; + + +/* + * register OSS synth + */ + +void +snd_emux_init_seq_oss(snd_emux_t *emu) +{ + snd_seq_oss_reg_t *arg; + snd_seq_device_t *dev; + + if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS, + sizeof(snd_seq_oss_reg_t), &dev) < 0) + return; + + emu->oss_synth = dev; + strcpy(dev->name, emu->name); + arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + arg->type = SYNTH_TYPE_SAMPLE; + arg->subtype = SAMPLE_TYPE_AWE32; + arg->nvoices = emu->max_voices; + arg->oper = oss_callback; + arg->private_data = emu; + + /* register to OSS synth table */ + snd_device_register(emu->card, dev); +} + + +/* + * unregister + */ +void +snd_emux_detach_seq_oss(snd_emux_t *emu) +{ + if (emu->oss_synth) { + snd_device_free(emu->card, emu->oss_synth); + emu->oss_synth = NULL; + } +} + + +/* use port number as a unique soundfont client number */ +#define SF_CLIENT_NO(p) ((p) + 0x1000) + +/* + * open port for OSS sequencer + */ +static int +snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + snd_seq_port_callback_t callback; + char tmpname[64]; + + emu = snd_magic_cast(snd_emux_t, closure, return -EINVAL); + snd_assert(arg != NULL && emu != NULL, return -ENXIO); + + down(&emu->register_mutex); + + if (!snd_emux_inc_count(emu)) { + up(&emu->register_mutex); + return -EFAULT; + } + + memset(&callback, 0, sizeof(callback)); + callback.owner = THIS_MODULE; + callback.event_input = snd_emux_event_oss_input; + + sprintf(tmpname, "%s OSS Port", emu->name); + p = snd_emux_create_port(emu, tmpname, 32, + 1, &callback); + if (p == NULL) { + snd_printk("can't create port\n"); + snd_emux_dec_count(emu); + up(&emu->register_mutex); + return -ENOMEM; + } + + /* fill the argument data */ + arg->private_data = p; + arg->addr.client = p->chset.client; + arg->addr.port = p->chset.port; + p->oss_arg = arg; + + reset_port_mode(p, arg->seq_mode); + + snd_emux_reset_port(p); + + up(&emu->register_mutex); + return 0; +} + + +#define DEFAULT_DRUM_FLAGS ((1<<9) | (1<<25)) + +/* + * reset port mode + */ +static void +reset_port_mode(snd_emux_port_t *port, int midi_mode) +{ + if (midi_mode) { + port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_MIDI; + port->drum_flags = DEFAULT_DRUM_FLAGS; + port->volume_atten = 0; + port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_KEYPRESS; + } else { + port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_SYNTH; + port->drum_flags = 0; + port->volume_atten = 32; + port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS; + } +} + + +/* + * close port + */ +static int +snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + + emu = p->emu; + snd_assert(emu != NULL, return -ENXIO); + + down(&emu->register_mutex); + snd_emux_sounds_off_all(p); + snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port)); + snd_seq_event_port_detach(p->chset.client, p->chset.port); + snd_emux_dec_count(emu); + + up(&emu->register_mutex); + return 0; +} + + +/* + * load patch + */ +static int +snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, + const char *buf, int offs, int count) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + int rc; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + + emu = p->emu; + snd_assert(emu != NULL, return -ENXIO); + + if (format == GUS_PATCH) + rc = snd_soundfont_load_guspatch(emu->sflist, buf, count, + SF_CLIENT_NO(p->chset.port)); + else if (format == SNDRV_OSS_SOUNDFONT_PATCH) { + soundfont_patch_info_t patch; + if (count < sizeof(patch)) + rc = -EINVAL; + if (copy_from_user(&patch, buf, sizeof(patch))) + rc = -EFAULT; + if (patch.type >= SNDRV_SFNT_LOAD_INFO && + patch.type <= SNDRV_SFNT_PROBE_DATA) + rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port)); + else { + if (emu->ops.load_fx) + rc = emu->ops.load_fx(emu, patch.type, patch.optarg, buf, count); + else + rc = -EINVAL; + } + } else + rc = 0; + return rc; +} + + +/* + * ioctl + */ +static int +snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg) +{ + snd_emux_port_t *p; + snd_emux_t *emu; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + + emu = p->emu; + snd_assert(emu != NULL, return -ENXIO); + + switch (cmd) { + case SNDCTL_SEQ_RESETSAMPLES: + snd_soundfont_remove_samples(emu->sflist); + return 0; + + case SNDCTL_SYNTH_MEMAVL: + if (emu->memhdr) + return snd_util_mem_avail(emu->memhdr); + return 0; + } + + return 0; +} + + +/* + * reset device + */ +static int +snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg) +{ + snd_emux_port_t *p; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + snd_emux_reset_port(p); + return 0; +} + + +/* + * receive raw events: only SEQ_PRIVATE is accepted. + */ +static int +snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private_data, + int atomic, int hop) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + unsigned char cmd, *data; + + p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(p != NULL, return -EINVAL); + emu = p->emu; + snd_assert(emu != NULL, return -EINVAL); + if (ev->type != SNDRV_SEQ_EVENT_OSS) + return snd_emux_event_input(ev, direct, private_data, atomic, hop); + + data = ev->data.raw8.d; + /* only SEQ_PRIVATE is accepted */ + if (data[0] != 0xfe) + return 0; + cmd = data[2] & _EMUX_OSS_MODE_VALUE_MASK; + if (data[2] & _EMUX_OSS_MODE_FLAG) + emuspec_control(emu, p, cmd, data, atomic, hop); + else + gusspec_control(emu, p, cmd, data, atomic, hop); + return 0; +} + + +/* + * OSS/AWE driver specific h/w controls + */ +static void +emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, + unsigned char *event, int atomic, int hop) +{ + int voice; + unsigned short p1; + short p2; + int i; + snd_midi_channel_t *chan; + + voice = event[3]; + if (voice < 0 || voice >= port->chset.max_channels) + chan = NULL; + else + chan = &port->chset.channels[voice]; + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + + switch (cmd) { + case _EMUX_OSS_REMOVE_LAST_SAMPLES: + snd_soundfont_remove_unlocked(emu->sflist); + break; + case _EMUX_OSS_SEND_EFFECT: + if (chan) + snd_emux_send_effect_oss(port, chan, p1, p2); + break; + + case _EMUX_OSS_TERMINATE_ALL: + snd_emux_terminate_all(emu); + break; + + case _EMUX_OSS_TERMINATE_CHANNEL: + /*snd_emux_mute_channel(emu, chan);*/ + break; + case _EMUX_OSS_RESET_CHANNEL: + /*snd_emux_channel_init(chset, chan);*/ + break; + + case _EMUX_OSS_RELEASE_ALL: + fake_event(emu, port, voice, MIDI_CTL_ALL_NOTES_OFF, 0, atomic, hop); + break; + case _EMUX_OSS_NOTEOFF_ALL: + fake_event(emu, port, voice, MIDI_CTL_ALL_SOUNDS_OFF, 0, atomic, hop); + break; + + case _EMUX_OSS_INITIAL_VOLUME: + if (p2) { + port->volume_atten = (short)p1; + snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME); + } + break; + + case _EMUX_OSS_CHN_PRESSURE: + if (chan) { + chan->midi_pressure = p1; + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_FMMOD|SNDRV_EMUX_UPDATE_FM2FRQ2); + } + break; + + case _EMUX_OSS_CHANNEL_MODE: + reset_port_mode(port, p1); + snd_emux_reset_port(port); + break; + + case _EMUX_OSS_DRUM_CHANNELS: + port->drum_flags = *(unsigned int*)&event[4]; + for (i = 0; i < port->chset.max_channels; i++) { + chan = &port->chset.channels[i]; + chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; + } + break; + + case _EMUX_OSS_MISC_MODE: + if (p1 < EMUX_MD_END) + port->ctrls[p1] = p2; + break; + case _EMUX_OSS_DEBUG_MODE: + break; + + default: + if (emu->ops.oss_ioctl) + emu->ops.oss_ioctl(emu, cmd, p1, p2); + break; + } +} + +/* + * GUS specific h/w controls + */ + +#include + +static void +gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, + unsigned char *event, int atomic, int hop) +{ + int voice; + unsigned short p1; + short p2; + int plong; + snd_midi_channel_t *chan; + + if (port->port_mode != SNDRV_EMUX_PORT_MODE_OSS_SYNTH) + return; + if (cmd == _GUS_NUMVOICES) + return; + voice = event[3]; + if (voice < 0 || voice >= port->chset.max_channels) + return; + + chan = &port->chset.channels[voice]; + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + plong = *(int*) &event[4]; + + switch (cmd) { + case _GUS_VOICESAMPLE: + chan->midi_program = p1; + return; + + case _GUS_VOICEBALA: + /* 0 to 15 --> 0 to 127 */ + chan->control[MIDI_CTL_MSB_PAN] = (int)p1 << 3; + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN); + return; + + case _GUS_VOICEVOL: + case _GUS_VOICEVOL2: + /* not supported yet */ + return; + + case _GUS_RAMPRANGE: + case _GUS_RAMPRATE: + case _GUS_RAMPMODE: + case _GUS_RAMPON: + case _GUS_RAMPOFF: + /* volume ramping not supported */ + return; + + case _GUS_VOLUME_SCALE: + return; + + case _GUS_VOICE_POS: +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_send_effect(port, chan, EMUX_FX_SAMPLE_START, + (short)(plong & 0x7fff), + EMUX_FX_FLAG_SET); + snd_emux_send_effect(port, chan, EMUX_FX_COARSE_SAMPLE_START, + (plong >> 15) & 0xffff, + EMUX_FX_FLAG_SET); +#endif + return; + } +} + + +/* + * send an event to midi emulation + */ +static void +fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop) +{ + snd_seq_event_t ev; + memset(&ev, 0, sizeof(ev)); + ev.type = SNDRV_SEQ_EVENT_CONTROLLER; + ev.data.control.channel = ch; + ev.data.control.param = param; + ev.data.control.value = val; + snd_emux_event_input(&ev, 0, port, atomic, hop); +} + +#endif /* CONFIG_SND_OSSEMUL */ diff -Nru a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/emux/emux_proc.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Proc interface for Emu8k/Emu10k1 WaveTable synth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include "emux_voice.h" + +#ifdef CONFIG_PROC_FS + +static void +snd_emux_proc_info_read(snd_info_entry_t *entry, + snd_info_buffer_t *buf) +{ + snd_emux_t *emu; + int i; + + emu = snd_magic_cast(snd_emux_t, entry->private_data, return); + down(&emu->register_mutex); + if (emu->name) + snd_iprintf(buf, "Device: %s\n", emu->name); + snd_iprintf(buf, "Ports: %d\n", emu->num_ports); + snd_iprintf(buf, "Addresses:"); + for (i = 0; i < emu->num_ports; i++) + snd_iprintf(buf, " %d:%d", emu->client, emu->ports[i]); + snd_iprintf(buf, "\n"); + snd_iprintf(buf, "Use Counter: %d\n", emu->used); + snd_iprintf(buf, "Max Voices: %d\n", emu->max_voices); + snd_iprintf(buf, "Allocated Voices: %d\n", emu->num_voices); + if (emu->memhdr) { + snd_iprintf(buf, "Memory Size: %d\n", emu->memhdr->size); + snd_iprintf(buf, "Memory Available: %d\n", snd_util_mem_avail(emu->memhdr)); + snd_iprintf(buf, "Allocated Blocks: %d\n", emu->memhdr->nblocks); + } else { + snd_iprintf(buf, "Memory Size: 0\n"); + } + if (emu->sflist) { + down(&emu->sflist->presets_mutex); + snd_iprintf(buf, "SoundFonts: %d\n", emu->sflist->fonts_size); + snd_iprintf(buf, "Instruments: %d\n", emu->sflist->zone_counter); + snd_iprintf(buf, "Samples: %d\n", emu->sflist->sample_counter); + snd_iprintf(buf, "Locked Instruments: %d\n", emu->sflist->zone_locked); + snd_iprintf(buf, "Locked Samples: %d\n", emu->sflist->sample_locked); + up(&emu->sflist->presets_mutex); + } +#if 0 /* debug */ + if (emu->voices[0].state != SNDRV_EMUX_ST_OFF && emu->voices[0].ch >= 0) { + snd_emux_voice_t *vp = &emu->voices[0]; + snd_iprintf(buf, "voice 0: on\n"); + snd_iprintf(buf, "mod delay=%x, atkhld=%x, dcysus=%x, rel=%x\n", + vp->reg.parm.moddelay, + vp->reg.parm.modatkhld, + vp->reg.parm.moddcysus, + vp->reg.parm.modrelease); + snd_iprintf(buf, "vol delay=%x, atkhld=%x, dcysus=%x, rel=%x\n", + vp->reg.parm.voldelay, + vp->reg.parm.volatkhld, + vp->reg.parm.voldcysus, + vp->reg.parm.volrelease); + snd_iprintf(buf, "lfo1 delay=%x, lfo2 delay=%x, pefe=%x\n", + vp->reg.parm.lfo1delay, + vp->reg.parm.lfo2delay, + vp->reg.parm.pefe); + snd_iprintf(buf, "fmmod=%x, tremfrq=%x, fm2frq2=%x\n", + vp->reg.parm.fmmod, + vp->reg.parm.tremfrq, + vp->reg.parm.fm2frq2); + snd_iprintf(buf, "cutoff=%x, filterQ=%x, chorus=%x, reverb=%x\n", + vp->reg.parm.cutoff, + vp->reg.parm.filterQ, + vp->reg.parm.chorus, + vp->reg.parm.reverb); + snd_iprintf(buf, "avol=%x, acutoff=%x, apitch=%x\n", + vp->avol, vp->acutoff, vp->apitch); + snd_iprintf(buf, "apan=%x, aaux=%x, ptarget=%x, vtarget=%x, ftarget=%x\n", + vp->apan, vp->aaux, + vp->ptarget, + vp->vtarget, + vp->ftarget); + snd_iprintf(buf, "start=%x, end=%x, loopstart=%x, loopend=%x\n", + vp->reg.start, vp->reg.end, vp->reg.loopstart, vp->reg.loopend); + snd_iprintf(buf, "sample_mode=%x, rate=%x\n", vp->reg.sample_mode, vp->reg.rate_offset); + } +#endif + up(&emu->register_mutex); +} + + +void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device) +{ + snd_info_entry_t *entry; + char name[64]; + + sprintf(name, "wavetableD%d", device); + entry = snd_info_create_card_entry(card, name, card->proc_root); + if (entry == NULL) + return; + + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = emu; + entry->c.text.read_size = 1024; + entry->c.text.read = snd_emux_proc_info_read; + if (snd_info_register(entry) < 0) + snd_info_free_entry(entry); + else + emu->proc = entry; +} + +void snd_emux_proc_free(snd_emux_t *emu) +{ + if (emu->proc) { + snd_info_unregister(emu->proc); + emu->proc = NULL; + } +} + +#endif /* CONFIG_PROC_FS */ diff -Nru a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/emux/emux_seq.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,437 @@ +/* + * Midi Sequencer interface routines. + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "emux_voice.h" +#include + + +/* Prototypes for static functions */ +static void free_port(void *private); +static void snd_emux_init_port(snd_emux_port_t *p); +static int snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info); +static int snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info); +static int get_client(snd_card_t *card, int index, char *name); + +/* + * MIDI emulation operators + */ +static snd_midi_op_t emux_ops = { + snd_emux_note_on, + snd_emux_note_off, + snd_emux_key_press, + snd_emux_terminate_note, + snd_emux_control, + snd_emux_nrpn, + snd_emux_sysex, +}; + + +/* + * number of MIDI channels + */ +#define MIDI_CHANNELS 16 + +/* + * type flags for MIDI sequencer port + */ +#define DEFAULT_MIDI_TYPE (SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\ + SNDRV_SEQ_PORT_TYPE_MIDI_GM |\ + SNDRV_SEQ_PORT_TYPE_MIDI_GS |\ + SNDRV_SEQ_PORT_TYPE_MIDI_XG |\ + SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE) + +/* + */ + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +/* + * Initialise the EMUX Synth by creating a client and registering + * a series of ports. + * Each of the ports will contain the 16 midi channels. Applications + * can connect to these ports to play midi data. + */ +int +snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index) +{ + int i; + snd_seq_port_callback_t pinfo; + char tmpname[64]; + + sprintf(tmpname, "%s WaveTable", emu->name); + emu->client = get_client(card, index, tmpname); + if (emu->client < 0) { + snd_printk("can't create client\n"); + return -ENODEV; + } + + if (emu->num_ports < 0) { + snd_printk("seqports must be greater than zero\n"); + emu->num_ports = 1; + } else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) { + snd_printk("too many ports." + "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS); + emu->num_ports = SNDRV_EMUX_MAX_PORTS; + } + + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.owner = THIS_MODULE; + pinfo.use = snd_emux_use; + pinfo.unuse = snd_emux_unuse; + pinfo.event_input = snd_emux_event_input; + + for (i = 0; i < emu->num_ports; i++) { + snd_emux_port_t *p; + + sprintf(tmpname, "%s Port %d", emu->name, i); + p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS, + 0, &pinfo); + if (p == NULL) { + snd_printk("can't create port\n"); + return -ENOMEM; + } + + p->port_mode = SNDRV_EMUX_PORT_MODE_MIDI; + snd_emux_init_port(p); + emu->ports[i] = p->chset.port; + } + + return 0; +} + + +/* + * Detach from the ports that were set up for this synthesizer and + * destroy the kernel client. + */ +void +snd_emux_detach_seq(snd_emux_t *emu) +{ + if (emu->voices) + snd_emux_terminate_all(emu); + + down(&emu->register_mutex); + if (emu->client >= 0) { + snd_seq_delete_kernel_client(emu->client); + emu->client = -1; + } + up(&emu->register_mutex); +} + + +/* + * create a sequencer port and channel_set + */ + +snd_emux_port_t * +snd_emux_create_port(snd_emux_t *emu, char *name, + int max_channels, int oss_port, + snd_seq_port_callback_t *callback) +{ + snd_emux_port_t *p; + int i, type, cap; + + /* Allocate structures for this channel */ + if ((p = snd_magic_kcalloc(snd_emux_port_t, 0, GFP_KERNEL)) == NULL) { + snd_printk("no memory\n"); + return NULL; + } + p->chset.channels = snd_kcalloc(max_channels * sizeof(snd_midi_channel_t), GFP_KERNEL); + if (p->chset.channels == NULL) { + snd_printk("no memory\n"); + snd_magic_kfree(p); + return NULL; + } + for (i = 0; i < max_channels; i++) + p->chset.channels[i].number = i; + p->chset.private_data = p; + p->chset.max_channels = max_channels; + p->emu = emu; + p->chset.client = emu->client; +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_create_effect(p); +#endif + callback->private_free = free_port; + callback->private_data = p; + + cap = SNDRV_SEQ_PORT_CAP_WRITE; + if (oss_port) { + type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; + } else { + type = DEFAULT_MIDI_TYPE; + cap |= SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + } + + p->chset.port = snd_seq_event_port_attach(emu->client, callback, + cap, type, name); + + return p; +} + + +/* + * release memory block for port + */ +static void +free_port(void *private_data) +{ + snd_emux_port_t *p; + + p = snd_magic_cast(snd_emux_port_t, private_data, return); + if (p) { +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_delete_effect(p); +#endif + if (p->chset.channels) + kfree(p->chset.channels); + snd_magic_kfree(p); + } +} + + +#define DEFAULT_DRUM_FLAGS (1<<9) + +/* + * initialize the port specific parameters + */ +static void +snd_emux_init_port(snd_emux_port_t *p) +{ + p->drum_flags = DEFAULT_DRUM_FLAGS; + p->volume_atten = 0; + + snd_emux_reset_port(p); +} + + +/* + * reset port + */ +void +snd_emux_reset_port(snd_emux_port_t *port) +{ + int i; + + /* stop all sounds */ + snd_emux_sounds_off_all(port); + + snd_midi_channel_set_clear(&port->chset); + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_clear_effect(port); +#endif + + /* set port specific control parameters */ + port->ctrls[EMUX_MD_DEF_BANK] = 0; + port->ctrls[EMUX_MD_DEF_DRUM] = 0; + port->ctrls[EMUX_MD_REALTIME_PAN] = 1; + + for (i = 0; i < port->chset.max_channels; i++) { + snd_midi_channel_t *chan = port->chset.channels + i; + chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; + } +} + + +/* + * input sequencer event + */ +int +snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private_data, + int atomic, int hop) +{ + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(port != NULL && ev != NULL, return -EINVAL); + + snd_midi_process_event(&emux_ops, ev, &port->chset); + + return 0; +} + + +/* + * increment usage count + */ +int +snd_emux_inc_count(snd_emux_t *emu) +{ + emu->used++; + if (!try_inc_mod_count(emu->ops.owner)) + goto __error; + if (!try_inc_mod_count(emu->card->module)) { + dec_mod_count(emu->ops.owner); + __error: + emu->used--; + return 0; + } + return 1; +} + + +/* + * decrease usage count + */ +void +snd_emux_dec_count(snd_emux_t *emu) +{ + dec_mod_count(emu->ops.owner); + emu->used--; + if (emu->used <= 0) + snd_emux_terminate_all(emu); + dec_mod_count(emu->card->module); +} + + +/* + * Routine that is called upon a first use of a particular port + */ +static int +snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_emux_port_t *p; + snd_emux_t *emu; + + p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(p != NULL, return -EINVAL); + emu = p->emu; + snd_assert(emu != NULL, return -EINVAL); + + down(&emu->register_mutex); + snd_emux_init_port(p); + snd_emux_inc_count(emu); + up(&emu->register_mutex); + return 0; +} + +/* + * Routine that is called upon the last unuse() of a particular port. + */ +static int +snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_emux_port_t *p; + snd_emux_t *emu; + + p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(p != NULL, return -EINVAL); + emu = p->emu; + snd_assert(emu != NULL, return -EINVAL); + + down(&emu->register_mutex); + snd_emux_sounds_off_all(p); + snd_emux_dec_count(emu); + up(&emu->register_mutex); + return 0; +} + + +/* + * Create a sequencer client + */ +static int +get_client(snd_card_t *card, int index, char *name) +{ + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + int client; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = NULL; + callbacks.allow_input = 1; + callbacks.allow_output = 1; + + /* Find a free client, start from 1 as the MPU expects to use 0 */ + client = snd_seq_create_kernel_client(card, index, &callbacks); + if (client < 0) + return client; + + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + strcpy(cinfo.name, name); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + return client; +} + + +/* + * attach virtual rawmidi devices + */ +int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card) +{ + int i; + + emu->vmidi = NULL; + if (emu->midi_ports <= 0) + return 0; + + emu->vmidi = snd_kcalloc(sizeof(snd_rawmidi_t*) * emu->midi_ports, GFP_KERNEL); + if (emu->vmidi == NULL) + return -ENOMEM; + + for (i = 0; i < emu->midi_ports; i++) { + snd_rawmidi_t *rmidi; + snd_virmidi_dev_t *rdev; + if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0) + goto __error; + rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, continue); + sprintf(rmidi->name, "%s Synth MIDI", emu->name); + rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH; + rdev->client = emu->client; + rdev->port = emu->ports[i]; + if (snd_device_register(card, rmidi) < 0) { + snd_device_free(card, rmidi); + goto __error; + } + emu->vmidi[i] = rmidi; + //snd_printk("virmidi %d ok\n", i); + } + return 0; + +__error: + //snd_printk("error init..\n"); + snd_emux_delete_virmidi(emu); + return -ENOMEM; +} + +int snd_emux_delete_virmidi(snd_emux_t *emu) +{ + int i; + + if (emu->vmidi == NULL) + return 0; + + for (i = 0; i < emu->midi_ports; i++) { + if (emu->vmidi[i]) + snd_device_free(emu->card, emu->vmidi[i]); + } + kfree(emu->vmidi); + emu->vmidi = NULL; + return 0; +} diff -Nru a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/emux/emux_synth.c Tue Feb 19 18:09:01 2002 @@ -0,0 +1,944 @@ +/* + * Midi synth routines for the Emu8k/Emu10k1 + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * Contains code based on awe_wave.c by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include "emux_voice.h" +#include + +/* + * Prototypes + */ + +/* + * Ensure a value is between two points + * macro evaluates its args more than once, so changed to upper-case. + */ +#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) +#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) + +static int get_zone(snd_emux_t *emu, snd_emux_port_t *port, int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table); +static int get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan); +static void terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free); +static void exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass); +static void terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free); +static void update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update); +static void setup_voice(snd_emux_voice_t *vp); +static int calc_pan(snd_emux_voice_t *vp); +static int calc_volume(snd_emux_voice_t *vp); +static int calc_pitch(snd_emux_voice_t *vp); + + +/* + * Start a note. + */ +void +snd_emux_note_on(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + snd_emux_t *emu; + int i, key, nvoices; + snd_emux_voice_t *vp; + snd_sf_zone_t *table[SNDRV_EMUX_MAX_MULTI_VOICES]; + unsigned long flags; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.get_voice != NULL, return); + snd_assert(emu->ops.trigger != NULL, return); + + key = note; /* remember the original note */ + nvoices = get_zone(emu, port, ¬e, vel, chan, table); + if (! nvoices) + return; + + /* exclusive note off */ + for (i = 0; i < nvoices; i++) { + snd_sf_zone_t *zp = table[i]; + if (zp && zp->v.exclusiveClass) + exclusive_note_off(emu, port, zp->v.exclusiveClass); + } + +#if 0 // seems not necessary + /* Turn off the same note on the same channel. */ + terminate_note1(emu, key, chan, 0); +#endif + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < nvoices; i++) { + + /* set up each voice parameter */ + /* at this stage, we don't trigger the voice yet. */ + + if (table[i] == NULL) + continue; + + vp = emu->ops.get_voice(emu, port); + if (vp == NULL || vp->ch < 0) + continue; + snd_assert(vp->emu != NULL && vp->hw != NULL, return); + if (STATE_IS_PLAYING(vp->state)) + emu->ops.terminate(vp); + + vp->time = emu->use_time++; + vp->chan = chan; + vp->port = port; + vp->key = key; + vp->note = note; + vp->velocity = vel; + vp->zone = table[i]; + if (vp->zone->sample) + vp->block = vp->zone->sample->block; + else + vp->block = NULL; + + setup_voice(vp); + + vp->state = SNDRV_EMUX_ST_STANDBY; + if (emu->ops.prepare) { + vp->state = SNDRV_EMUX_ST_OFF; + if (emu->ops.prepare(vp) >= 0) + vp->state = SNDRV_EMUX_ST_STANDBY; + } + } + + /* start envelope now */ + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (vp->state == SNDRV_EMUX_ST_STANDBY && + vp->chan == chan) { + emu->ops.trigger(vp); + vp->state = SNDRV_EMUX_ST_ON; + vp->ontime = jiffies; /* remember the trigger timing */ + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) { + /* clear voice position for the next note on this channel */ + snd_emux_effect_table_t *fx = chan->private; + if (fx) { + fx->flag[EMUX_FX_SAMPLE_START] = 0; + fx->flag[EMUX_FX_COARSE_SAMPLE_START] = 0; + } + } +#endif +} + +/* + * Release a note in response to a midi note off. + */ +void +snd_emux_note_off(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + int ch; + snd_emux_t *emu; + snd_emux_voice_t *vp; + unsigned long flags; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.release != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (ch = 0; ch < emu->max_voices; ch++) { + vp = &emu->voices[ch]; + if (STATE_IS_PLAYING(vp->state) && + vp->chan == chan && vp->key == note) { + vp->time = emu->use_time++; + vp->state = SNDRV_EMUX_ST_RELEASED; + if (vp->ontime == jiffies) { + /* if note-off is sent too shortly after + * note-on, emuX engine cannot produce the sound + * correctly. so we'll release this note + * a bit later via timer callback. + */ + vp->state = SNDRV_EMUX_ST_PENDING; + if (! emu->timer_active) { + emu->tlist.expires = jiffies + 1; + add_timer(&emu->tlist); + emu->timer_active = 1; + } + } else + /* ok now release the note */ + emu->ops.release(vp); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + * timer callback + * + * release the pending note-offs + */ +void snd_emux_timer_callback(unsigned long data) +{ + snd_emux_t *emu = snd_magic_cast(snd_emux_t, (void*)data, return); + snd_emux_voice_t *vp; + int ch, do_again = 0; + + spin_lock(&emu->voice_lock); + for (ch = 0; ch < emu->max_voices; ch++) { + vp = &emu->voices[ch]; + if (vp->state == SNDRV_EMUX_ST_PENDING) { + if (vp->ontime == jiffies) + do_again++; /* release this at the next interrupt */ + else { + emu->ops.release(vp); + vp->state = SNDRV_EMUX_ST_RELEASED; + } + } + } + if (do_again) { + emu->tlist.expires = jiffies + 1; + add_timer(&emu->tlist); + emu->timer_active = 1; + } else + emu->timer_active = 0; + spin_unlock(&emu->voice_lock); +} + +/* + * key pressure change + */ +void +snd_emux_key_press(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + int ch; + snd_emux_t *emu; + snd_emux_voice_t *vp; + unsigned long flags; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.update != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (ch = 0; ch < emu->max_voices; ch++) { + vp = &emu->voices[ch]; + if (vp->state == SNDRV_EMUX_ST_ON && + vp->chan == chan && vp->key == note) { + vp->velocity = vel; + update_voice(emu, vp, SNDRV_EMUX_UPDATE_VOLUME); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Modulate the voices which belong to the channel + */ +void +snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update) +{ + snd_emux_t *emu; + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + if (! update) + return; + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.update != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (vp->chan == chan) + update_voice(emu, vp, update); + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + * Modulate all the voices which belong to the port. + */ +void +snd_emux_update_port(snd_emux_port_t *port, int update) +{ + snd_emux_t *emu; + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + if (! update) + return; + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.update != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (vp->port == port) + update_voice(emu, vp, update); + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Deal with a controler type event. This includes all types of + * control events, not just the midi controllers + */ +void +snd_emux_control(void *p, int type, snd_midi_channel_t *chan) +{ + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + switch (type) { + case MIDI_CTL_MSB_MAIN_VOLUME: + case MIDI_CTL_MSB_EXPRESSION: + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_VOLUME); + break; + + case MIDI_CTL_MSB_PAN: + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN); + break; + + case MIDI_CTL_SOFT_PEDAL: +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + /* FIXME: this is an emulation */ + snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160, + EMUX_FX_FLAG_ADD); +#endif + break; + + case MIDI_CTL_PITCHBEND: + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PITCH); + break; + + case MIDI_CTL_MSB_MODWHEEL: + case MIDI_CTL_CHAN_PRESSURE: + snd_emux_update_channel(port, chan, + SNDRV_EMUX_UPDATE_FMMOD | + SNDRV_EMUX_UPDATE_FM2FRQ2); + break; + + } + + if (port->chset.midi_mode == SNDRV_MIDI_MODE_XG) { + snd_emux_xg_control(port, chan, type); + } +} + + +/* + * for Emu10k1 - release at least 1 voice currently using + */ +int +snd_emux_release_voice(snd_emux_t *emu) +{ + return 0; +} + + +/* + * terminate note - if free flag is true, free the terminated voice + */ +static void +terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free) +{ + int i; + snd_emux_voice_t *vp; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state) && vp->chan == chan && + vp->key == note) + terminate_voice(emu, vp, free); + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * terminate note - exported for midi emulation + */ +void +snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan) +{ + snd_emux_t *emu; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.terminate != NULL, return); + + terminate_note1(emu, note, chan, 1); +} + + +/* + * Terminate all the notes + */ +void +snd_emux_terminate_all(snd_emux_t *emu) +{ + int i; + snd_emux_voice_t *vp; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state)) + terminate_voice(emu, vp, 0); + if (vp->state == SNDRV_EMUX_ST_OFF) { + if (emu->ops.free_voice) + emu->ops.free_voice(vp); + if (emu->ops.reset) + emu->ops.reset(emu, i); + } + vp->time = 0; + } + /* initialize allocation time */ + emu->use_time = 0; + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Terminate all voices associated with the given port + */ +void +snd_emux_sounds_off_all(snd_emux_port_t *port) +{ + int i; + snd_emux_t *emu; + snd_emux_voice_t *vp; + unsigned long flags; + + snd_assert(port != NULL, return); + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.terminate != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state) && + vp->port == port) + terminate_voice(emu, vp, 0); + if (vp->state == SNDRV_EMUX_ST_OFF) { + if (emu->ops.free_voice) + emu->ops.free_voice(vp); + if (emu->ops.reset) + emu->ops.reset(emu, i); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Terminate all voices that have the same exclusive class. This + * is mainly for drums. + */ +static void +exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass) +{ + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state) && vp->port == port && + vp->reg.exclusiveClass == exclass) { + terminate_voice(emu, vp, 0); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + * terminate a voice + * if free flag is true, call free_voice after termination + */ +static void +terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free) +{ + emu->ops.terminate(vp); + vp->time = emu->use_time++; + vp->chan = NULL; + vp->port = NULL; + vp->zone = NULL; + vp->block = NULL; + vp->state = SNDRV_EMUX_ST_OFF; + if (free && emu->ops.free_voice) + emu->ops.free_voice(vp); +} + + +/* + * Modulate the voice + */ +static void +update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update) +{ + if (!STATE_IS_PLAYING(vp->state)) + return; + + if (vp->chan == NULL || vp->port == NULL) + return; + if (update & SNDRV_EMUX_UPDATE_VOLUME) + calc_volume(vp); + if (update & SNDRV_EMUX_UPDATE_PITCH) + calc_pitch(vp); + if (update & SNDRV_EMUX_UPDATE_PAN) { + if (! calc_pan(vp) && (update == SNDRV_EMUX_UPDATE_PAN)) + return; + } + emu->ops.update(vp, update); +} + + +#if 0 // not used +/* table for volume target calculation */ +static unsigned short voltarget[16] = { + 0xEAC0, 0xE0C8, 0xD740, 0xCE20, 0xC560, 0xBD08, 0xB500, 0xAD58, + 0xA5F8, 0x9EF0, 0x9830, 0x91C0, 0x8B90, 0x85A8, 0x8000, 0x7A90 +}; +#endif + +#define LO_BYTE(v) ((v) & 0xff) +#define HI_BYTE(v) (((v) >> 8) & 0xff) + +/* + * Sets up the voice structure by calculating some values that + * will be needed later. + */ +static void +setup_voice(snd_emux_voice_t *vp) +{ + soundfont_voice_parm_t *parm; + int pitch; + + /* copy the original register values */ + vp->reg = vp->zone->v; + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_setup_effect(vp); +#endif + + /* reset status */ + vp->apan = -1; + vp->avol = -1; + vp->apitch = -1; + + calc_volume(vp); + calc_pitch(vp); + calc_pan(vp); + + parm = &vp->reg.parm; + + /* compute filter target and correct modulation parameters */ + if (LO_BYTE(parm->modatkhld) >= 0x80 && parm->moddelay >= 0x8000) { + parm->moddelay = 0xbfff; + pitch = (HI_BYTE(parm->pefe) << 4) + vp->apitch; + if (pitch > 0xffff) + pitch = 0xffff; + /* calculate filter target */ + vp->ftarget = parm->cutoff + LO_BYTE(parm->pefe); + LIMITVALUE(vp->ftarget, 0, 255); + vp->ftarget <<= 8; + } else { + vp->ftarget = parm->cutoff; + vp->ftarget <<= 8; + pitch = vp->apitch; + } + + /* compute pitch target */ + if (pitch != 0xffff) { + vp->ptarget = 1 << (pitch >> 12); + if (pitch & 0x800) vp->ptarget += (vp->ptarget*0x102e)/0x2710; + if (pitch & 0x400) vp->ptarget += (vp->ptarget*0x764)/0x2710; + if (pitch & 0x200) vp->ptarget += (vp->ptarget*0x389)/0x2710; + vp->ptarget += (vp->ptarget >> 1); + if (vp->ptarget > 0xffff) vp->ptarget = 0xffff; + } else + vp->ptarget = 0xffff; + + if (LO_BYTE(parm->modatkhld) >= 0x80) { + parm->modatkhld &= ~0xff; + parm->modatkhld |= 0x7f; + } + + /* compute volume target and correct volume parameters */ + vp->vtarget = 0; +#if 0 /* FIXME: this leads to some clicks.. */ + if (LO_BYTE(parm->volatkhld) >= 0x80 && parm->voldelay >= 0x8000) { + parm->voldelay = 0xbfff; + vp->vtarget = voltarget[vp->avol % 0x10] >> (vp->avol >> 4); + } +#endif + + if (LO_BYTE(parm->volatkhld) >= 0x80) { + parm->volatkhld &= ~0xff; + parm->volatkhld |= 0x7f; + } +} + +/* + * calculate pitch parameter + */ +static int +calc_pan(snd_emux_voice_t *vp) +{ + snd_midi_channel_t *chan = vp->chan; + int pan; + + /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */ + if (vp->reg.fixpan > 0) /* 0-127 */ + pan = 255 - (int)vp->reg.fixpan * 2; + else { + pan = chan->control[MIDI_CTL_MSB_PAN] - 64; + if (vp->reg.pan >= 0) /* 0-127 */ + pan += vp->reg.pan - 64; + pan = 127 - (int)pan * 2; + } + LIMITVALUE(pan, 0, 255); + + if (pan != vp->apan) { + vp->apan = pan; + if (pan == 0) + vp->aaux = 0xff; + else + vp->aaux = (-pan) & 0xff; + return 1; + } else + return 0; +} + + +/* + * calculate volume attenuation + * + * Voice volume is controlled by volume attenuation parameter. + * So volume becomes maximum when avol is 0 (no attenuation), and + * minimum when 255 (-96dB or silence). + */ + +/* tables for volume->attenuation calculation */ +static unsigned char voltab1[128] = { + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, + 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, + 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, + 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char voltab2[128] = { + 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a, + 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, + 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15, + 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, + 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, + 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char expressiontab[128] = { + 0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42, + 0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30, + 0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, + 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e, + 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, + 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, + 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, + 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * Magic to calculate the volume (actually attenuation) from all the + * voice and channels parameters. + */ +static int +calc_volume(snd_emux_voice_t *vp) +{ + int vol; + int main_vol, expression_vol, master_vol; + snd_midi_channel_t *chan = vp->chan; + snd_emux_port_t *port = vp->port; + + expression_vol = chan->control[MIDI_CTL_MSB_EXPRESSION]; + LIMITMAX(vp->velocity, 127); + LIMITVALUE(expression_vol, 0, 127); + if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) { + /* 0 - 127 */ + main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME]; + vol = (vp->velocity * main_vol * expression_vol) / (127*127); + vol = vol * vp->reg.amplitude / 127; + + LIMITVALUE(vol, 0, 127); + + /* calc to attenuation */ + vol = snd_sf_vol_table[vol]; + + } else { + main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME] * vp->reg.amplitude / 127; + LIMITVALUE(main_vol, 0, 127); + + vol = voltab1[main_vol] + voltab2[vp->velocity]; + vol = (vol * 8) / 3; + vol += vp->reg.attenuation; + vol += ((0x100 - vol) * expressiontab[expression_vol])/128; + } + + master_vol = port->chset.gs_master_volume; + LIMITVALUE(master_vol, 0, 127); + vol += snd_sf_vol_table[master_vol]; + vol += port->volume_atten; + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + if (chan->private) { + snd_emux_effect_table_t *fx = chan->private; + vol += fx->val[EMUX_FX_ATTEN]; + } +#endif + + LIMITVALUE(vol, 0, 255); + if (vp->avol == vol) + return 0; /* value unchanged */ + + vp->avol = vol; + if (!SF_IS_DRUM_BANK(get_bank(port, chan)) + && LO_BYTE(vp->reg.parm.volatkhld) < 0x7d) { + int atten; + if (vp->velocity < 70) + atten = 70; + else + atten = vp->velocity; + vp->acutoff = (atten * vp->reg.parm.cutoff + 0xa0) >> 7; + } else { + vp->acutoff = vp->reg.parm.cutoff; + } + + return 1; /* value changed */ +} + +/* + * calculate pitch offset + * + * 0xE000 is no pitch offset at 44100Hz sample. + * Every 4096 is one octave. + */ + +static int +calc_pitch(snd_emux_voice_t *vp) +{ + snd_midi_channel_t *chan = vp->chan; + int offset; + + /* calculate offset */ + if (vp->reg.fixkey >= 0) { + offset = (vp->reg.fixkey - vp->reg.root) * 4096 / 12; + } else { + offset = (vp->note - vp->reg.root) * 4096 / 12; + } + offset = (offset * vp->reg.scaleTuning) / 100; + offset += vp->reg.tune * 4096 / 1200; + if (chan->midi_pitchbend != 0) { + /* (128 * 8192: 1 semitone) ==> (4096: 12 semitones) */ + offset += chan->midi_pitchbend * chan->gm_rpn_pitch_bend_range / 3072; + } + + /* tuning via RPN: + * coarse = -8192 to 8192 (100 cent per 128) + * fine = -8192 to 8192 (max=100cent) + */ + /* 4096 = 1200 cents in emu8000 parameter */ + offset += chan->gm_rpn_coarse_tuning * 4096 / (12 * 128); + offset += chan->gm_rpn_fine_tuning / 24; + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + /* add initial pitch correction */ + if (chan->private) { + snd_emux_effect_table_t *fx = chan->private; + if (fx->flag[EMUX_FX_INIT_PITCH]) + offset += fx->val[EMUX_FX_INIT_PITCH]; + } +#endif + + /* 0xe000: root pitch */ + offset += 0xe000 + vp->reg.rate_offset; + offset += vp->emu->pitch_shift; + LIMITVALUE(offset, 0, 0xffff); + if (offset == vp->apitch) + return 0; /* unchanged */ + vp->apitch = offset; + return 1; /* value changed */ +} + +/* + * Get the bank number assigned to the channel + */ +static int +get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan) +{ + int val; + + switch (port->chset.midi_mode) { + case SNDRV_MIDI_MODE_XG: + val = chan->control[MIDI_CTL_MSB_BANK]; + if (val == 127) + return 128; /* return drum bank */ + return chan->control[MIDI_CTL_LSB_BANK]; + + case SNDRV_MIDI_MODE_GS: + if (chan->drum_channel) + return 128; + /* ignore LSB (bank map) */ + return chan->control[MIDI_CTL_MSB_BANK]; + + default: + if (chan->drum_channel) + return 128; + return chan->control[MIDI_CTL_MSB_BANK]; + } +} + + +/* Look for the zones matching with the given note and velocity. + * The resultant zones are stored on table. + */ +static int +get_zone(snd_emux_t *emu, snd_emux_port_t *port, + int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table) +{ + int preset, bank, def_preset, def_bank; + + bank = get_bank(port, chan); + preset = chan->midi_program; + + if (SF_IS_DRUM_BANK(bank)) { + def_preset = port->ctrls[EMUX_MD_DEF_DRUM]; + def_bank = bank; + } else { + def_preset = preset; + def_bank = port->ctrls[EMUX_MD_DEF_BANK]; + } + + return snd_soundfont_search_zone(emu->sflist, notep, vel, preset, bank, + def_preset, def_bank, + table, SNDRV_EMUX_MAX_MULTI_VOICES); +} + +/* + */ +void +snd_emux_init_voices(snd_emux_t *emu) +{ + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + vp->ch = -1; /* not used */ + vp->state = SNDRV_EMUX_ST_OFF; + vp->chan = NULL; + vp->port = NULL; + vp->time = 0; + vp->emu = emu; + vp->hw = emu->hw; + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + */ +void snd_emux_lock_voice(snd_emux_t *emu, int voice) +{ + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF) + emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED; + else + snd_printk("invalid voice for lock %d (state = %x)\n", + voice, emu->voices[voice].state); + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + */ +void snd_emux_unlock_voice(snd_emux_t *emu, int voice) +{ + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED) + emu->voices[voice].state = SNDRV_EMUX_ST_OFF; + else + snd_printk("invalid voice for unlock %d (state = %x)\n", + voice, emu->voices[voice].state); + spin_unlock_irqrestore(&emu->voice_lock, flags); +} diff -Nru a/sound/synth/emux/emux_voice.h b/sound/synth/emux/emux_voice.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/emux/emux_voice.h Tue Feb 19 18:09:00 2002 @@ -0,0 +1,84 @@ +#ifndef __EMUX_VOICE_H +#define __EMUX_VOICE_H + +/* + * A structure to keep track of each hardware voice + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +/* Prototypes for emux_seq.c */ +int snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index); +void snd_emux_detach_seq(snd_emux_t *emu); +snd_emux_port_t *snd_emux_create_port(snd_emux_t *emu, char *name, int max_channels, int type, snd_seq_port_callback_t *callback); +void snd_emux_reset_port(snd_emux_port_t *port); +int snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); +int snd_emux_inc_count(snd_emux_t *emu); +void snd_emux_dec_count(snd_emux_t *emu); +int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card); +int snd_emux_delete_virmidi(snd_emux_t *emu); + +/* Prototypes for emux_synth.c */ +void snd_emux_init_voices(snd_emux_t *emu); + +void snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan); +void snd_emux_control(void *p, int type, struct snd_midi_channel *chan); + +void snd_emux_sounds_off_all(snd_emux_port_t *port); +void snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update); +void snd_emux_update_port(snd_emux_port_t *port, int update); + +void snd_emux_timer_callback(unsigned long data); + +/* emux_effect.c */ +#ifdef SNDRV_EMUX_USE_RAW_EFFECT +void snd_emux_create_effect(snd_emux_port_t *p); +void snd_emux_delete_effect(snd_emux_port_t *p); +void snd_emux_clear_effect(snd_emux_port_t *p); +void snd_emux_setup_effect(snd_emux_voice_t *vp); +void snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val); +void snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode); +#endif + +/* emux_nrpn.c */ +void snd_emux_sysex(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +int snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param); +void snd_emux_nrpn(void *private_data, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); + +/* emux_oss.c */ +void snd_emux_init_seq_oss(snd_emux_t *emu); +void snd_emux_detach_seq_oss(snd_emux_t *emu); + +/* emux_proc.c */ +#ifdef CONFIG_PROC_FS +void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device); +void snd_emux_proc_free(snd_emux_t *emu); +#endif + +#define STATE_IS_PLAYING(s) ((s) & SNDRV_EMUX_ST_ON) + +#endif diff -Nru a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/emux/soundfont.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,1462 @@ +/* + * Soundfont generic routines. + * It is intended that these should be used by any driver that is willing + * to accept soundfont patches. + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Deal with reading in of a soundfont. Code follows the OSS way + * of doing things so that the old sfxload utility can be used. + * Everything may change when there is an alsa way of doing things. + */ +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +/* Prototypes for static functions */ + +static int open_patch(snd_sf_list_t *sflist, const char *data, int count, int client); +static snd_soundfont_t *newsf(snd_sf_list_t *sflist, int type, char *name); +static int is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name); +static int close_patch(snd_sf_list_t *sflist); +static int probe_data(snd_sf_list_t *sflist, int sample_id); +static void set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp); +static snd_sf_zone_t *sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf); +static void set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp); +static snd_sf_sample_t *sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf); +static void sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp); +static int load_map(snd_sf_list_t *sflist, const void *data, int count); +static int load_info(snd_sf_list_t *sflist, const void *data, long count); +static int remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr); +static void init_voice_info(soundfont_voice_info_t *avp); +static void init_voice_parm(soundfont_voice_parm_t *pp); +static snd_sf_sample_t *set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp); +static snd_sf_sample_t *find_sample(snd_soundfont_t *sf, int sample_id); +static int load_data(snd_sf_list_t *sflist, const void *data, long count); +static void rebuild_presets(snd_sf_list_t *sflist); +static void add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur); +static void delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp); +static snd_sf_zone_t *search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key); +static int search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level); +static int get_index(int bank, int instr, int key); +static void snd_sf_init(snd_sf_list_t *sflist); +static void snd_sf_clear(snd_sf_list_t *sflist); + +/* + * lock access to sflist + */ +static int +lock_preset(snd_sf_list_t *sflist, int nonblock) +{ + unsigned long flags; + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->sf_locked && nonblock) { + spin_unlock_irqrestore(&sflist->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&sflist->lock, flags); + down(&sflist->presets_mutex); + sflist->sf_locked = 1; + return 0; +} + + +/* + * remove lock + */ +static void +unlock_preset(snd_sf_list_t *sflist) +{ + up(&sflist->presets_mutex); + sflist->sf_locked = 0; +} + + +/* + * close the patch if the patch was opened by this client. + */ +int +snd_soundfont_close_check(snd_sf_list_t *sflist, int client) +{ + unsigned long flags; + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->open_client == client) { + spin_unlock_irqrestore(&sflist->lock, flags); + return close_patch(sflist); + } + spin_unlock_irqrestore(&sflist->lock, flags); + return 0; +} + + +/* + * Deal with a soundfont patch. Any driver could use these routines + * although it was designed for the AWE64. + * + * The sample_write and callargs pararameters allow a callback into + * the actual driver to write sample data to the board or whatever + * it wants to do with it. + */ +int +snd_soundfont_load(snd_sf_list_t *sflist, const void *data, long count, int client) +{ + soundfont_patch_info_t patch; + unsigned long flags; + int rc; + + if (count < sizeof(patch)) { + snd_printk("patch record too small %ld\n", count); + return -EINVAL; + } + if (copy_from_user(&patch, data, sizeof(patch))) + return -EFAULT; + + count -= sizeof(patch); + data += sizeof(patch); + + if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) { + snd_printk("'The wrong kind of patch' %x\n", patch.key); + return -EINVAL; + } + if (count < patch.len) { + snd_printk("Patch too short %ld, need %d\n", count, patch.len); + return -EINVAL; + } + if (patch.len < 0) { + snd_printk("poor length %d\n", patch.len); + return -EINVAL; + } + + if (patch.type == SNDRV_SFNT_OPEN_PATCH) { + /* grab sflist to open */ + lock_preset(sflist, 0); + rc = open_patch(sflist, data, count, client); + unlock_preset(sflist); + return rc; + } + + /* check if other client already opened patch */ + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->open_client != client) { + spin_unlock_irqrestore(&sflist->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&sflist->lock, flags); + + lock_preset(sflist, 0); + rc = -EINVAL; + switch (patch.type) { + case SNDRV_SFNT_LOAD_INFO: + rc = load_info(sflist, data, count); + break; + case SNDRV_SFNT_LOAD_DATA: + rc = load_data(sflist, data, count); + break; + case SNDRV_SFNT_CLOSE_PATCH: + rc = close_patch(sflist); + break; + case SNDRV_SFNT_REPLACE_DATA: + /*rc = replace_data(&patch, data, count);*/ + break; + case SNDRV_SFNT_MAP_PRESET: + rc = load_map(sflist, data, count); + break; + case SNDRV_SFNT_PROBE_DATA: + rc = probe_data(sflist, patch.optarg); + break; + case SNDRV_SFNT_REMOVE_INFO: + /* patch must be opened */ + if (sflist->currsf) { + snd_printk("soundfont: remove_info: patch not opened\n"); + rc = -EINVAL; + } else { + int bank, instr; + bank = ((unsigned short)patch.optarg >> 8) & 0xff; + instr = (unsigned short)patch.optarg & 0xff; + if (! remove_info(sflist, sflist->currsf, bank, instr)) + rc = -EINVAL; + else + rc = 0; + } + break; + } + unlock_preset(sflist); + + return rc; +} + + +/* check if specified type is special font (GUS or preset-alias) */ +static inline int +is_special_type(int type) +{ + type &= 0x0f; + return (type == SNDRV_SFNT_PAT_TYPE_GUS || + type == SNDRV_SFNT_PAT_TYPE_MAP); +} + + +/* open patch; create sf list */ +static int +open_patch(snd_sf_list_t *sflist, const char *data, int count, int client) +{ + soundfont_open_parm_t parm; + snd_soundfont_t *sf; + unsigned long flags; + + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->open_client >= 0 || sflist->currsf) { + spin_unlock_irqrestore(&sflist->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&sflist->lock, flags); + + if (copy_from_user(&parm, data, sizeof(parm))) + return -EFAULT; + + if (is_special_type(parm.type)) { + parm.type |= SNDRV_SFNT_PAT_SHARED; + sf = newsf(sflist, parm.type, NULL); + } else + sf = newsf(sflist, parm.type, parm.name); + if (sf == NULL) { + return -ENOMEM; + } + + sflist->open_client = client; + sflist->currsf = sf; + + return 0; +} + +/* + * Allocate a new soundfont structure. + */ +static snd_soundfont_t * +newsf(snd_sf_list_t *sflist, int type, char *name) +{ + snd_soundfont_t *sf; + + /* check the shared fonts */ + if (type & SNDRV_SFNT_PAT_SHARED) { + for (sf = sflist->fonts; sf; sf = sf->next) { + if (is_identical_font(sf, type, name)) { + return sf; + } + } + } + + /* not found -- create a new one */ + sf = (snd_soundfont_t*)snd_kcalloc(sizeof(*sf), GFP_KERNEL); + if (sf == NULL) + return NULL; + sf->id = sflist->fonts_size; + sflist->fonts_size++; + + /* prepend this record */ + sf->next = sflist->fonts; + sflist->fonts = sf; + + sf->type = type; + sf->zones = NULL; + sf->samples = NULL; + if (name) + memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN); + + return sf; +} + +/* check if the given name matches to the existing list */ +static int +is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name) +{ + return ((sf->type & SNDRV_SFNT_PAT_SHARED) && + (sf->type & 0x0f) == (type & 0x0f) && + (name == NULL || + memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0)); +} + +/* + * Close the current patch. + */ +static int +close_patch(snd_sf_list_t *sflist) +{ + sflist->currsf = NULL; + sflist->open_client = -1; + + rebuild_presets(sflist); + + return 0; + +} + +/* probe sample in the current list -- nothing to be loaded */ +static int +probe_data(snd_sf_list_t *sflist, int sample_id) +{ + /* patch must be opened */ + if (sflist->currsf) { + /* search the specified sample by optarg */ + if (find_sample(sflist->currsf, sample_id)) + return 0; + } + return -EINVAL; +} + +/* + * increment zone counter + */ +static void +set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp) +{ + zp->counter = sflist->zone_counter++; + if (sf->type & SNDRV_SFNT_PAT_LOCKED) + sflist->zone_locked = sflist->zone_counter; +} + +/* + * allocate a new zone record + */ +static snd_sf_zone_t * +sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf) +{ + snd_sf_zone_t *zp; + + if ((zp = snd_kcalloc(sizeof(*zp), GFP_KERNEL)) == NULL) + return NULL; + zp->next = sf->zones; + sf->zones = zp; + + init_voice_info(&zp->v); + + set_zone_counter(sflist, sf, zp); + return zp; +} + + +/* + * increment sample couter + */ +static void +set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp) +{ + sp->counter = sflist->sample_counter++; + if (sf->type & SNDRV_SFNT_PAT_LOCKED) + sflist->sample_locked = sflist->sample_counter; +} + +/* + * allocate a new sample list record + */ +static snd_sf_sample_t * +sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf) +{ + snd_sf_sample_t *sp; + + if ((sp = snd_kcalloc(sizeof(*sp), GFP_KERNEL)) == NULL) + return NULL; + + sp->next = sf->samples; + sf->samples = sp; + + set_sample_counter(sflist, sf, sp); + return sp; +} + +/* + * delete sample list -- this is an exceptional job. + * only the last allocated sample can be deleted. + */ +static void +sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp) +{ + /* only last sample is accepted */ + if (sp == sf->samples) { + sf->samples = sp->next; + kfree(sp); + } +} + + +/* load voice map */ +static int +load_map(snd_sf_list_t *sflist, const void *data, int count) +{ + snd_sf_zone_t *zp, *prevp; + snd_soundfont_t *sf; + soundfont_voice_map_t map; + + /* get the link info */ + if (count < sizeof(map)) + return -EINVAL; + if (copy_from_user(&map, data, sizeof(map))) + return -EFAULT; + + if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS) + return -EINVAL; + + sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL); + if (sf == NULL) + return -ENOMEM; + + prevp = NULL; + for (zp = sf->zones; zp; prevp = zp, zp = zp->next) { + if (zp->mapped && + zp->instr == map.map_instr && + zp->bank == map.map_bank && + zp->v.low == map.map_key && + zp->v.start == map.src_instr && + zp->v.end == map.src_bank && + zp->v.fixkey == map.src_key) { + /* the same mapping is already present */ + /* relink this record to the link head */ + if (prevp) { + prevp->next = zp->next; + zp->next = sf->zones; + sf->zones = zp; + } + /* update the counter */ + set_zone_counter(sflist, sf, zp); + return 0; + } + } + + /* create a new zone */ + if ((zp = sf_zone_new(sflist, sf)) == NULL) + return -ENOMEM; + + zp->bank = map.map_bank; + zp->instr = map.map_instr; + zp->mapped = 1; + if (map.map_key >= 0) { + zp->v.low = map.map_key; + zp->v.high = map.map_key; + } + zp->v.start = map.src_instr; + zp->v.end = map.src_bank; + zp->v.fixkey = map.src_key; + zp->v.sf_id = sf->id; + + add_preset(sflist, zp); + + return 0; +} + + +/* remove the present instrument layers */ +static int +remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr) +{ + snd_sf_zone_t *prev, *next, *p; + int removed = 0; + + prev = NULL; + for (p = sf->zones; p; p = next) { + next = p->next; + if (! p->mapped && + p->bank == bank && p->instr == instr) { + /* remove this layer */ + if (prev) + prev->next = next; + else + sf->zones = next; + removed++; + kfree(p); + } else + prev = p; + } + if (removed) + rebuild_presets(sflist); + return removed; +} + + +/* + * Read an info record from the user buffer and save it on the current + * open soundfont. + */ +static int +load_info(snd_sf_list_t *sflist, const void *data, long count) +{ + snd_soundfont_t *sf; + snd_sf_zone_t *zone; + soundfont_voice_rec_hdr_t hdr; + int i; + + /* patch must be opened */ + if ((sf = sflist->currsf) == NULL) + return -EINVAL; + + if (is_special_type(sf->type)) + return -EINVAL; + + if (count < sizeof(hdr)) { + printk("Soundfont error: invalid patch zone length\n"); + return -EINVAL; + } + if (copy_from_user((char*)&hdr, data, sizeof(hdr))) + return -EFAULT; + + data += sizeof(hdr); + count -= sizeof(hdr); + + if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { + printk("Soundfont error: Illegal voice number %d\n", hdr.nvoices); + return -EINVAL; + } + + if (count < sizeof(soundfont_voice_info_t)*hdr.nvoices) { + printk("Soundfont Error: patch length(%ld) is smaller than nvoices(%d)\n", + count, hdr.nvoices); + return -EINVAL; + } + + switch (hdr.write_mode) { + case SNDRV_SFNT_WR_EXCLUSIVE: + /* exclusive mode - if the instrument already exists, + return error */ + for (zone = sf->zones; zone; zone = zone->next) { + if (!zone->mapped && + zone->bank == hdr.bank && + zone->instr == hdr.instr) + return -EINVAL; + } + break; + case SNDRV_SFNT_WR_REPLACE: + /* replace mode - remove the instrument if it already exists */ + remove_info(sflist, sf, hdr.bank, hdr.instr); + break; + } + + for (i = 0; i < hdr.nvoices; i++) { + snd_sf_zone_t tmpzone; + + /* copy awe_voice_info parameters */ + if (copy_from_user(&tmpzone.v, data, sizeof(tmpzone.v))) { + return -EFAULT; + } + + data += sizeof(tmpzone.v); + count -= sizeof(tmpzone.v); + + tmpzone.bank = hdr.bank; + tmpzone.instr = hdr.instr; + tmpzone.mapped = 0; + tmpzone.v.sf_id = sf->id; + if (tmpzone.v.mode & SNDRV_SFNT_MODE_INIT_PARM) + init_voice_parm(&tmpzone.v.parm); + + /* create a new zone */ + if ((zone = sf_zone_new(sflist, sf)) == NULL) { + return -ENOMEM; + } + + /* copy the temporary data */ + zone->bank = tmpzone.bank; + zone->instr = tmpzone.instr; + zone->v = tmpzone.v; + + /* look up the sample */ + zone->sample = set_sample(sf, &zone->v); + } + + return 0; +} + + +/* initialize voice_info record */ +static void +init_voice_info(soundfont_voice_info_t *avp) +{ + memset(avp, 0, sizeof(*avp)); + + avp->root = 60; + avp->high = 127; + avp->velhigh = 127; + avp->fixkey = -1; + avp->fixvel = -1; + avp->fixpan = -1; + avp->pan = -1; + avp->amplitude = 127; + avp->scaleTuning = 100; + + init_voice_parm(&avp->parm); +} + +/* initialize voice_parm record: + * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. + * Vibrato and Tremolo effects are zero. + * Cutoff is maximum. + * Chorus and Reverb effects are zero. + */ +static void +init_voice_parm(soundfont_voice_parm_t *pp) +{ + memset(pp, 0, sizeof(*pp)); + + pp->moddelay = 0x8000; + pp->modatkhld = 0x7f7f; + pp->moddcysus = 0x7f7f; + pp->modrelease = 0x807f; + + pp->voldelay = 0x8000; + pp->volatkhld = 0x7f7f; + pp->voldcysus = 0x7f7f; + pp->volrelease = 0x807f; + + pp->lfo1delay = 0x8000; + pp->lfo2delay = 0x8000; + + pp->cutoff = 0xff; +} + +/* search the specified sample */ +static snd_sf_sample_t * +set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp) +{ + snd_sf_sample_t *sample; + + sample = find_sample(sf, avp->sample); + if (sample == NULL) + return NULL; + + /* add in the actual sample offsets: + * The voice_info addresses define only the relative offset + * from sample pointers. Here we calculate the actual DRAM + * offset from sample pointers. + */ + avp->start += sample->v.start; + avp->end += sample->v.end; + avp->loopstart += sample->v.loopstart; + avp->loopend += sample->v.loopend; + + /* copy mode flags */ + avp->sample_mode = sample->v.mode_flags; + + return sample; +} + +/* find the sample pointer with the given id in the soundfont */ +static snd_sf_sample_t * +find_sample(snd_soundfont_t *sf, int sample_id) +{ + snd_sf_sample_t *p; + + if (sf == NULL) + return NULL; + + for (p = sf->samples; p; p = p->next) { + if (p->v.sample == sample_id) + return p; + } + return NULL; +} + + +/* + * Load sample information, this can include data to be loaded onto + * the soundcard. It can also just be a pointer into soundcard ROM. + * If there is data it will be written to the soundcard via the callback + * routine. + */ +static int +load_data(snd_sf_list_t *sflist, const void *data, long count) +{ + snd_soundfont_t *sf; + soundfont_sample_info_t sample_info; + snd_sf_sample_t *sp; + long off; + + /* patch must be opened */ + if ((sf = sflist->currsf) == NULL) + return -EINVAL; + + if (is_special_type(sf->type)) + return -EINVAL; + + if (copy_from_user(&sample_info, data, sizeof(sample_info))) + return -EFAULT; + + off = sizeof(sample_info); + + if (sample_info.size != (count-off)/2) + return -EINVAL; + + /* Check for dup */ + if (find_sample(sf, sample_info.sample)) { + /* if shared sample, skip this data */ + if (sf->type & SNDRV_SFNT_PAT_SHARED) + return 0; + return -EINVAL; + } + + /* Allocate a new sample structure */ + if ((sp = sf_sample_new(sflist, sf)) == NULL) + return -ENOMEM; + + sp->v = sample_info; + sp->v.sf_id = sf->id; + sp->v.dummy = 0; + sp->v.truesize = sp->v.size; + + /* + * If there is wave data then load it. + */ + if (sp->v.size > 0) { + int rc; + rc = sflist->callback.sample_new + (sflist->callback.private_data, sp, sflist->memhdr, + data + off, count - off); + if (rc < 0) { + sf_sample_delete(sflist, sf, sp); + return rc; + } + sflist->mem_used += sp->v.truesize; + } + + return count; +} + + +/* log2_tbl[i] = log2(i+128) * 0x10000 */ +static int log_tbl[129] = { + 0x70000, 0x702df, 0x705b9, 0x7088e, 0x70b5d, 0x70e26, 0x710eb, 0x713aa, + 0x71663, 0x71918, 0x71bc8, 0x71e72, 0x72118, 0x723b9, 0x72655, 0x728ed, + 0x72b80, 0x72e0e, 0x73098, 0x7331d, 0x7359e, 0x7381b, 0x73a93, 0x73d08, + 0x73f78, 0x741e4, 0x7444c, 0x746b0, 0x74910, 0x74b6c, 0x74dc4, 0x75019, + 0x75269, 0x754b6, 0x75700, 0x75946, 0x75b88, 0x75dc7, 0x76002, 0x7623a, + 0x7646e, 0x766a0, 0x768cd, 0x76af8, 0x76d1f, 0x76f43, 0x77164, 0x77382, + 0x7759d, 0x777b4, 0x779c9, 0x77bdb, 0x77dea, 0x77ff5, 0x781fe, 0x78404, + 0x78608, 0x78808, 0x78a06, 0x78c01, 0x78df9, 0x78fef, 0x791e2, 0x793d2, + 0x795c0, 0x797ab, 0x79993, 0x79b79, 0x79d5d, 0x79f3e, 0x7a11d, 0x7a2f9, + 0x7a4d3, 0x7a6ab, 0x7a880, 0x7aa53, 0x7ac24, 0x7adf2, 0x7afbe, 0x7b188, + 0x7b350, 0x7b515, 0x7b6d8, 0x7b899, 0x7ba58, 0x7bc15, 0x7bdd0, 0x7bf89, + 0x7c140, 0x7c2f5, 0x7c4a7, 0x7c658, 0x7c807, 0x7c9b3, 0x7cb5e, 0x7cd07, + 0x7ceae, 0x7d053, 0x7d1f7, 0x7d398, 0x7d538, 0x7d6d6, 0x7d872, 0x7da0c, + 0x7dba4, 0x7dd3b, 0x7ded0, 0x7e063, 0x7e1f4, 0x7e384, 0x7e512, 0x7e69f, + 0x7e829, 0x7e9b3, 0x7eb3a, 0x7ecc0, 0x7ee44, 0x7efc7, 0x7f148, 0x7f2c8, + 0x7f446, 0x7f5c2, 0x7f73d, 0x7f8b7, 0x7fa2f, 0x7fba5, 0x7fd1a, 0x7fe8d, + 0x80000, +}; + +/* convert from linear to log value + * + * conversion: value = log2(amount / base) * ratio + * + * argument: + * amount = linear value (unsigned, 32bit max) + * offset = base offset (:= log2(base) * 0x10000) + * ratio = division ratio + * + */ +int +snd_sf_linear_to_log(unsigned int amount, int offset, int ratio) +{ + int v; + int s, low, bit; + + if (amount < 2) + return 0; + for (bit = 0; ! (amount & 0x80000000L); bit++) + amount <<= 1; + s = (amount >> 24) & 0x7f; + low = (amount >> 16) & 0xff; + /* linear approxmimation by lower 8 bit */ + v = (log_tbl[s + 1] * low + log_tbl[s] * (0x100 - low)) >> 8; + v -= offset; + v = (v * ratio) >> 16; + v += (24 - bit) * ratio; + return v; +} + +#define OFFSET_MSEC 653117 /* base = 1000 */ +#define OFFSET_ABSCENT 851781 /* base = 8176 */ +#define OFFSET_SAMPLERATE 1011119 /* base = 44100 */ + +#define ABSCENT_RATIO 1200 +#define TIMECENT_RATIO 1200 +#define SAMPLERATE_RATIO 4096 + +/* + * mHz to abscent + * conversion: abscent = log2(MHz / 8176) * 1200 + */ +static int +freq_to_note(int mhz) +{ + return snd_sf_linear_to_log(mhz, OFFSET_ABSCENT, ABSCENT_RATIO); +} + +/* convert Hz to AWE32 rate offset: + * sample pitch offset for the specified sample rate + * rate=44100 is no offset, each 4096 is 1 octave (twice). + * eg, when rate is 22050, this offset becomes -4096. + * + * conversion: offset = log2(Hz / 44100) * 4096 + */ +static int +calc_rate_offset(int hz) +{ + return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO); +} + + +/* calculate GUS envelope time */ +static int +calc_gus_envelope_time(int rate, int start, int end) +{ + int r, p, t; + r = (3 - ((rate >> 6) & 3)) * 3; + p = rate & 0x3f; + t = end - start; + if (t < 0) t = -t; + if (13 > r) + t = t << (13 - r); + else + t = t >> (r - 13); + return (t * 10) / (p * 441); +} + +/* convert envelope time parameter to soundfont parameters */ + +/* attack & decay/release time table (msec) */ +static short attack_time_tbl[128] = { +32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816, +707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, +361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, +180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, +90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, +45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, +22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, +11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0, +}; + +static short decay_time_tbl[128] = { +32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082, +2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507, +1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722, +691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361, +345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180, +172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90, +86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45, +43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22, +}; + +/* delay time = 0x8000 - msec/92 */ +int +snd_sf_calc_parm_hold(int msec) +{ + int val = (0x7f * 92 - msec) / 92; + if (val < 1) val = 1; + if (val >= 126) val = 126; + return val; +} + +/* search an index for specified time from given time table */ +static int +calc_parm_search(int msec, short *table) +{ + int left = 1, right = 127, mid; + while (left < right) { + mid = (left + right) / 2; + if (msec < (int)table[mid]) + left = mid + 1; + else + right = mid; + } + return left; +} + +/* attack time: search from time table */ +int +snd_sf_calc_parm_attack(int msec) +{ + return calc_parm_search(msec, attack_time_tbl); +} + +/* decay/release time: search from time table */ +int +snd_sf_calc_parm_decay(int msec) +{ + return calc_parm_search(msec, decay_time_tbl); +} + +int snd_sf_vol_table[128] = { + 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, + 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, + 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, + 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, + 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, + 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, + 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, + 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, +}; + + +#define calc_gus_sustain(val) (0x7f - snd_sf_vol_table[(val)/2]) +#define calc_gus_attenuation(val) snd_sf_vol_table[(val)/2] + +/* load GUS patch */ +static int +load_guspatch(snd_sf_list_t *sflist, const char *data, long count, int client) +{ + struct patch_info patch; + snd_soundfont_t *sf; + snd_sf_zone_t *zone; + snd_sf_sample_t *smp; + int note, sample_id; + int rc; + + if (count < sizeof(patch)) { + snd_printk("patch record too small %ld\n", count); + return -EINVAL; + } + if (copy_from_user(&patch, data, sizeof(patch))) + return -EFAULT; + + count -= sizeof(patch); + data += sizeof(patch); + + sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL); + if (sf == NULL) + return -ENOMEM; + if ((smp = sf_sample_new(sflist, sf)) == NULL) + return -ENOMEM; + sample_id = sflist->sample_counter; + smp->v.sample = sample_id; + smp->v.start = 0; + smp->v.end = patch.len; + smp->v.loopstart = patch.loop_start; + smp->v.loopend = patch.loop_end; + smp->v.size = patch.len; + + /* set up mode flags */ + smp->v.mode_flags = 0; + if (!(patch.mode & WAVE_16_BITS)) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_8BITS; + if (patch.mode & WAVE_UNSIGNED) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_UNSIGNED; + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_NO_BLANK; + if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_SINGLESHOT; + if (patch.mode & WAVE_BIDIR_LOOP) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_BIDIR_LOOP; + if (patch.mode & WAVE_LOOP_BACK) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_REVERSE_LOOP; + + if (patch.mode & WAVE_16_BITS) { + /* convert to word offsets */ + smp->v.size /= 2; + smp->v.end /= 2; + smp->v.loopstart /= 2; + smp->v.loopend /= 2; + } + /*smp->v.loopend++;*/ + + smp->v.dummy = 0; + smp->v.truesize = 0; + smp->v.sf_id = sf->id; + + /* set up voice info */ + if ((zone = sf_zone_new(sflist, sf)) == NULL) { + sf_sample_delete(sflist, sf, smp); + return -ENOMEM; + } + + /* + * load wave data + */ + if (sflist->callback.sample_new) { + rc = sflist->callback.sample_new + (sflist->callback.private_data, smp, sflist->memhdr, data, count); + if (rc < 0) { + sf_sample_delete(sflist, sf, smp); + return rc; + } + /* memory offset is updated after */ + } + + /* update the memory offset here */ + sflist->mem_used += smp->v.truesize; + + zone->v.sample = sample_id; /* the last sample */ + zone->v.rate_offset = calc_rate_offset(patch.base_freq); + note = freq_to_note(patch.base_note); + zone->v.root = note / 100; + zone->v.tune = -(note % 100); + zone->v.low = (freq_to_note(patch.low_note) + 99) / 100; + zone->v.high = freq_to_note(patch.high_note) / 100; + /* panning position; -128 - 127 => 0-127 */ + zone->v.pan = (patch.panning + 128) / 2; +#if 0 + snd_printk("gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n", + (int)patch.base_freq, zone->v.rate_offset, + zone->v.root, zone->v.tune, zone->v.low, zone->v.high); +#endif + + /* detuning is ignored */ + /* 6points volume envelope */ + if (patch.mode & WAVE_ENVELOPES) { + int attack, hold, decay, release; + attack = calc_gus_envelope_time + (patch.env_rate[0], 0, patch.env_offset[0]); + hold = calc_gus_envelope_time + (patch.env_rate[1], patch.env_offset[0], + patch.env_offset[1]); + decay = calc_gus_envelope_time + (patch.env_rate[2], patch.env_offset[1], + patch.env_offset[2]); + release = calc_gus_envelope_time + (patch.env_rate[3], patch.env_offset[1], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[4], patch.env_offset[3], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[5], patch.env_offset[4], + patch.env_offset[5]); + zone->v.parm.volatkhld = + (snd_sf_calc_parm_hold(hold) << 8) | + snd_sf_calc_parm_attack(attack); + zone->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | + snd_sf_calc_parm_decay(decay); + zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release); + zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]); +#if 0 + snd_printk("gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n", + zone->v.parm.volatkhld, + zone->v.parm.voldcysus, + zone->v.parm.volrelease, + zone->v.attenuation); +#endif + } + + /* fast release */ + if (patch.mode & WAVE_FAST_RELEASE) { + zone->v.parm.volrelease = 0x807f; + } + + /* tremolo effect */ + if (patch.mode & WAVE_TREMOLO) { + int rate = (patch.tremolo_rate * 1000 / 38) / 42; + zone->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; + } + /* vibrato effect */ + if (patch.mode & WAVE_VIBRATO) { + int rate = (patch.vibrato_rate * 1000 / 38) / 42; + zone->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; + } + + /* scale_freq, scale_factor, volume, and fractions not implemented */ + + if (!(smp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT)) + zone->v.mode = SNDRV_SFNT_MODE_LOOPING; + else + zone->v.mode = 0; + + /* append to the tail of the list */ + /*zone->bank = ctrls[AWE_MD_GUS_BANK];*/ + zone->bank = 0; + zone->instr = patch.instr_no; + zone->mapped = 0; + zone->v.sf_id = sf->id; + + zone->sample = set_sample(sf, &zone->v); + + /* rebuild preset now */ + add_preset(sflist, zone); + + return 0; +} + +/* load GUS patch */ +int +snd_soundfont_load_guspatch(snd_sf_list_t *sflist, const char *data, + long count, int client) +{ + int rc; + lock_preset(sflist, 0); + rc = load_guspatch(sflist, data, count, client); + unlock_preset(sflist); + return rc; +} + + +/* + * Rebuild the preset table. This is like a hash table in that it allows + * quick access to the zone information. For each preset there are zone + * structures linked by next_instr and by next_zone. Former is the whole + * link for this preset, and latter is the link for zone (i.e. instrument/ + * bank/key combination). + */ +static void +rebuild_presets(snd_sf_list_t *sflist) +{ + snd_soundfont_t *sf; + snd_sf_zone_t *cur; + + /* clear preset table */ + memset(sflist->presets, 0, sizeof(sflist->presets)); + + /* search all fonts and insert each font */ + for (sf = sflist->fonts; sf; sf = sf->next) { + for (cur = sf->zones; cur; cur = cur->next) { + if (! cur->mapped && cur->sample == NULL) { + /* try again to search the corresponding sample */ + cur->sample = set_sample(sf, &cur->v); + if (cur->sample == NULL) + continue; + } + + add_preset(sflist, cur); + } + } +} + + +/* + * add the given zone to preset table + */ +static void +add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur) +{ + snd_sf_zone_t *zone; + int index; + + zone = search_first_zone(sflist, cur->bank, cur->instr, cur->v.low); + if (zone && zone->v.sf_id != cur->v.sf_id) { + /* different instrument was already defined */ + snd_sf_zone_t *p; + /* compare the allocated time */ + for (p = zone; p; p = p->next_zone) { + if (p->counter > cur->counter) + /* the current is older.. skipped */ + return; + } + /* remove old zones */ + delete_preset(sflist, zone); + zone = NULL; /* do not forget to clear this! */ + } + + /* prepend this zone */ + if ((index = get_index(cur->bank, cur->instr, cur->v.low)) < 0) + return; + cur->next_zone = zone; /* zone link */ + cur->next_instr = sflist->presets[index]; /* preset table link */ + sflist->presets[index] = cur; +} + +/* + * delete the given zones from preset_table + */ +static void +delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp) +{ + int index; + snd_sf_zone_t *p; + + if ((index = get_index(zp->bank, zp->instr, zp->v.low)) < 0) + return; + for (p = sflist->presets[index]; p; p = p->next_instr) { + while (p->next_instr == zp) { + p->next_instr = zp->next_instr; + zp = zp->next_zone; + if (zp == NULL) + return; + } + } +} + + +/* + * Search matching zones from preset table. + * The note can be rewritten by preset mapping (alias). + * The found zones are stored on 'table' array. max_layers defines + * the maximum number of elements in this array. + * This function returns the number of found zones. 0 if not found. + */ +int +snd_soundfont_search_zone(snd_sf_list_t *sflist, int *notep, int vel, + int preset, int bank, + int def_preset, int def_bank, + snd_sf_zone_t **table, int max_layers) +{ + int nvoices; + + if (lock_preset(sflist, 1)) + return 0; + + nvoices = search_zones(sflist, notep, vel, preset, bank, table, max_layers, 0); + if (! nvoices) { + if (preset != def_preset || bank != def_bank) + nvoices = search_zones(sflist, notep, vel, def_preset, def_bank, table, max_layers, 0); + } + unlock_preset(sflist); + + return nvoices; +} + + +/* + * search the first matching zone + */ +static snd_sf_zone_t * +search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key) +{ + int index; + snd_sf_zone_t *zp; + + if ((index = get_index(bank, preset, key)) < 0) + return NULL; + for (zp = sflist->presets[index]; zp; zp = zp->next_instr) { + if (zp->instr == preset && zp->bank == bank) + return zp; + } + return NULL; +} + + +/* + * search matching zones from sflist. can be called recursively. + */ +static int +search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level) +{ + snd_sf_zone_t *zp; + int nvoices; + + zp = search_first_zone(sflist, bank, preset, *notep); + nvoices = 0; + for (; zp; zp = zp->next_zone) { + if (*notep >= zp->v.low && *notep <= zp->v.high && + vel >= zp->v.vellow && vel <= zp->v.velhigh) { + if (zp->mapped) { + /* search preset mapping (aliasing) */ + int key = zp->v.fixkey; + preset = zp->v.start; + bank = zp->v.end; + + if (level > 5) /* too deep alias level */ + return 0; + if (key < 0) + key = *notep; + nvoices = search_zones(sflist, &key, vel, + preset, bank, table, + max_layers, level + 1); + if (nvoices > 0) + *notep = key; + break; + } + table[nvoices++] = zp; + if (nvoices >= max_layers) + break; + } + } + + return nvoices; +} + + +/* calculate the index of preset table: + * drums are mapped from 128 to 255 according to its note key. + * other instruments are mapped from 0 to 127. + * if the index is out of range, return -1. + */ +static int +get_index(int bank, int instr, int key) +{ + int index; + if (SF_IS_DRUM_BANK(bank)) + index = key + SF_MAX_INSTRUMENTS; + else + index = instr; + index = index % SF_MAX_PRESETS; + if (index < 0) + return -1; + return index; +} + +/* + * Initialise the sflist structure. + */ +static void +snd_sf_init(snd_sf_list_t *sflist) +{ + memset(sflist->presets, 0, sizeof(sflist->presets)); + + sflist->mem_used = 0; + sflist->currsf = NULL; + sflist->open_client = -1; + sflist->fonts = NULL; + sflist->fonts_size = 0; + sflist->zone_counter = 0; + sflist->sample_counter = 0; + sflist->zone_locked = 0; + sflist->sample_locked = 0; +} + +/* + * Release all list records + */ +static void +snd_sf_clear(snd_sf_list_t *sflist) +{ + snd_soundfont_t *sf, *nextsf; + snd_sf_zone_t *zp, *nextzp; + snd_sf_sample_t *sp, *nextsp; + + for (sf = sflist->fonts; sf; sf = nextsf) { + nextsf = sf->next; + for (zp = sf->zones; zp; zp = nextzp) { + nextzp = zp->next; + kfree(zp); + } + for (sp = sf->samples; sp; sp = nextsp) { + nextsp = sp->next; + if (sflist->callback.sample_free) + sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr); + kfree(sp); + } + kfree(sf); + } + + snd_sf_init(sflist); +} + + +/* + * Create a new sflist structure + */ +snd_sf_list_t * +snd_sf_new(snd_sf_callback_t *callback, snd_util_memhdr_t *hdr) +{ + snd_sf_list_t *sflist; + + if ((sflist = snd_kcalloc(sizeof(snd_sf_list_t), GFP_KERNEL)) == NULL) + return NULL; + + init_MUTEX(&sflist->presets_mutex); + spin_lock_init(&sflist->lock); + sflist->sf_locked = 0; + sflist->memhdr = hdr; + + if (callback) + sflist->callback = *callback; + + snd_sf_init(sflist); + return sflist; +} + + +/* + * Free everything allocated off the sflist structure. + */ +void +snd_sf_free(snd_sf_list_t *sflist) +{ + if (sflist == NULL) + return; + + lock_preset(sflist, 0); + if (sflist->callback.sample_reset) + sflist->callback.sample_reset(sflist->callback.private_data); + snd_sf_clear(sflist); + unlock_preset(sflist); + + kfree(sflist); +} + +/* + * Remove all samples + * The soundcard should be silet before calling this function. + */ +int +snd_soundfont_remove_samples(snd_sf_list_t *sflist) +{ + lock_preset(sflist, 0); + if (sflist->callback.sample_reset) + sflist->callback.sample_reset(sflist->callback.private_data); + snd_sf_clear(sflist); + unlock_preset(sflist); + + return 0; +} + +/* + * Remove unlocked samples. + * The soundcard should be silet before calling this function. + */ +int +snd_soundfont_remove_unlocked(snd_sf_list_t *sflist) +{ + snd_soundfont_t *sf; + snd_sf_zone_t *zp, *nextzp; + snd_sf_sample_t *sp, *nextsp; + + if (lock_preset(sflist, 1)) + return -EBUSY; + + if (sflist->callback.sample_reset) + sflist->callback.sample_reset(sflist->callback.private_data); + + /* to be sure */ + memset(sflist->presets, 0, sizeof(sflist->presets)); + + for (sf = sflist->fonts; sf; sf = sf->next) { + for (zp = sf->zones; zp; zp = nextzp) { + if (zp->counter < sflist->zone_locked) + break; + nextzp = zp->next; + sf->zones = nextzp; + kfree(zp); + } + + for (sp = sf->samples; sp; sp = nextsp) { + if (sp->counter < sflist->sample_locked) + break; + nextsp = sp->next; + sf->samples = nextsp; + sflist->mem_used -= sp->v.truesize; + if (sflist->callback.sample_free) + sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr); + kfree(sp); + } + } + + sflist->zone_counter = sflist->zone_locked; + sflist->sample_counter = sflist->sample_locked; + + rebuild_presets(sflist); + + unlock_preset(sflist); + return 0; +} + +/* + * Return the used memory size (in words) + */ +int +snd_soundfont_mem_used(snd_sf_list_t *sflist) +{ + return sflist->mem_used; +} diff -Nru a/sound/synth/util_mem.c b/sound/synth/util_mem.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/synth/util_mem.c Tue Feb 19 18:09:00 2002 @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Generic memory management routines for soundcard memory allocation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai"); +MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation"); +MODULE_LICENSE("GPL"); + +#define get_memblk(p) list_entry(p, snd_util_memblk_t, list) + +/* + * create a new memory manager + */ +snd_util_memhdr_t * +snd_util_memhdr_new(int memsize) +{ + snd_util_memhdr_t *hdr; + + hdr = snd_kcalloc(sizeof(*hdr), GFP_KERNEL); + if (hdr == NULL) + return NULL; + hdr->size = memsize; + init_MUTEX(&hdr->block_mutex); + INIT_LIST_HEAD(&hdr->block); + + return hdr; +} + +/* + * free a memory manager + */ +void snd_util_memhdr_free(snd_util_memhdr_t *hdr) +{ + struct list_head *p; + + snd_assert(hdr != NULL, return); + /* release all blocks */ + while ((p = hdr->block.next) != &hdr->block) { + list_del(p); + kfree(get_memblk(p)); + } + kfree(hdr); +} + +/* + * allocate a memory block (without mutex) + */ +snd_util_memblk_t * +__snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size) +{ + snd_util_memblk_t *blk; + snd_util_unit_t units, prev_offset; + struct list_head *p; + + snd_assert(hdr != NULL, return NULL); + snd_assert(size > 0, return NULL); + + /* word alignment */ + units = size; + if (units & 1) + units++; + if (units > hdr->size) + return NULL; + + /* look for empty block */ + prev_offset = 0; + list_for_each(p, &hdr->block) { + blk = get_memblk(p); + if (blk->offset - prev_offset >= units) + goto __found; + prev_offset = blk->offset + blk->size; + } + if (hdr->size - prev_offset < units) + return NULL; + +__found: + return __snd_util_memblk_new(hdr, units, p->prev); +} + + +/* + * create a new memory block with the given size + * the block is linked next to prev + */ +snd_util_memblk_t * +__snd_util_memblk_new(snd_util_memhdr_t *hdr, snd_util_unit_t units, + struct list_head *prev) +{ + snd_util_memblk_t *blk; + + blk = kmalloc(sizeof(snd_util_memblk_t) + hdr->block_extra_size, GFP_KERNEL); + if (blk == NULL) + return NULL; + + if (! prev || prev == &hdr->block) + blk->offset = 0; + else { + snd_util_memblk_t *p = get_memblk(prev); + blk->offset = p->offset + p->size; + } + blk->size = units; + list_add(&blk->list, prev); + hdr->nblocks++; + hdr->used += units; + return blk; +} + + +/* + * allocate a memory block (with mutex) + */ +snd_util_memblk_t * +snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size) +{ + snd_util_memblk_t *blk; + down(&hdr->block_mutex); + blk = __snd_util_mem_alloc(hdr, size); + up(&hdr->block_mutex); + return blk; +} + + +/* + * remove the block from linked-list and free resource + * (without mutex) + */ +void +__snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk) +{ + list_del(&blk->list); + hdr->nblocks--; + hdr->used -= blk->size; + kfree(blk); +} + +/* + * free a memory block (with mutex) + */ +int snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk) +{ + snd_assert(hdr && blk, return -EINVAL); + + down(&hdr->block_mutex); + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return 0; +} + +/* + * return available memory size + */ +int snd_util_mem_avail(snd_util_memhdr_t *hdr) +{ + unsigned int size; + down(&hdr->block_mutex); + size = hdr->size - hdr->used; + up(&hdr->block_mutex); + return size; +} + + +EXPORT_SYMBOL(snd_util_memhdr_new); +EXPORT_SYMBOL(snd_util_memhdr_free); +EXPORT_SYMBOL(snd_util_mem_alloc); +EXPORT_SYMBOL(snd_util_mem_free); +EXPORT_SYMBOL(snd_util_mem_avail); +EXPORT_SYMBOL(__snd_util_mem_alloc); +EXPORT_SYMBOL(__snd_util_mem_free); +EXPORT_SYMBOL(__snd_util_memblk_new); + +/* + * INIT part + */ + +static int __init alsa_util_mem_init(void) +{ + return 0; +} + +static void __exit alsa_util_mem_exit(void) +{ +} + +module_init(alsa_util_mem_init) +module_exit(alsa_util_mem_exit)